RS-485 modbus library


#41

@beck, Sorry for the delay.

I too have been using the library for a few days and noticed the same offset/mapping issues that you speak of. However to be honest, it will still work fine for my application as I will be transferring 16 words at max. I will be bit-packing my coils into holding registers, so the 4x registers is honestly all I need.

I have tested it to my Panasonic FPX PLC outfitted with an RS485 communication cassette and a MAX485 chip on the Photon side. I let it run overnight with the master (my PLC) reading/writing data to two slaves every 100ms. That is WAY faster than I will need in real life, but I wanted to test it. So far no errors on either device, so I am happy with that.

I have used some other Modbus RS485 libraries with Arduino before that resolve some of the addressing issues. Now that I know this works, maybe I will port it in the future if I have some time.


#42

5 posts were merged into an existing topic: Photon and Modbus 485 RTU - Connections & interface requirements


Photon and Modbus 485 RTU - Connections & interface requirements
#43

Hello @peekay123 ,
I tried to import the the library ModBusMaster in the WEB IDE but does not compile.
Also I tried with manual input files but without success.
I try and ModBus_RS485_Slave library and compile without problem.It requires a small modification of code to support multiple serial ports for Electron, but I’m still not done testing.

#include "ModbusRtu.h"
#include "Serial2/Serial2.h"
#include "Serial4/Serial4.h"
#include "Serial5/Serial5.h"
#include "application.h"
/**
 * @brief
 * Initialize class object.
 *
 * Sets up the serial port using specified baud rate.
 * Call once class has been instantiated, typically within setup().
 *
 * @see http://arduino.cc/en/Serial/Begin#.Uy4CJ6aKlHY
 * @param speed   baud rate, in standard increments (300..115200)
 * @param config  data frame settings (data length, parity and stop bits)
 * @ingroup setup
 */
void Modbus::begin(long u32speed) {

  switch( u8serno ) {
  case 1:
    port = &Serial1;
    break;

  case 2:
    port = &Serial2;
    break;
    
    case 4:
    port = &Serial4;
    break;
    
    case 5:
    port = &Serial5;
    break;

  case 0:
  default:
    port = &Serial1;
    break;
  }

I want to know which library is better for Modbus Master?


#44

@developer_bt, neither library has been updated in a while and they are not Libraries v2.0 compatible. I’ll try and spend some time today to review them both.


#45

@developer_bt, there is already a ModBusMaster library on the IDE. Have you tried it?


#46

@peekay123 Yes I tried to import the the library ModBusMaster in the WEB IDE but does not compile. The same happens with manual input C++/h files.

Processing  /workspace/modbus.ino
make -C ../modules/electron/user-part all
make[1]: Entering directory '/firmware/modules/electron/user-part'
make -C ../../../user 
make[2]: Entering directory '/firmware/user'
Building cpp file: /workspace/modbus.cpp
Invoking: ARM GCC CPP Compiler
mkdir -p ../build/target/user/platform-10-m/workspace/
arm-none-eabi-gcc -DSTM32_DEVICE -DSTM32F2XX -DPLATFORM_THREADING=1 -DPLATFORM_ID=10 -DPLATFORM_NAME=electron -DUSBD_VID_SPARK=0x2B04 -DUSBD_PID_DFU=0xD00A -DUSBD_PID_CDC=0xC00A -DSPARK_PLATFORM -g3 -gdwarf-2 -Os -mcpu=cortex-m3 -mthumb -DINCLUDE_PLATFORM=1 -DPRODUCT_ID=10 -DPRODUCT_FIRMWARE_VERSION=65535 -DUSE_STDPERIPH_DRIVER -DDFU_BUILD_ENABLE -DPARTICLE_NO_ARDUINO_COMPATIBILITY=0 -DSYSTEM_VERSION_STRING=0.6.1 -DRELEASE_BUILD -I./inc -I../wiring/inc -I../system/inc -I../services/inc -I../communication/src -I../hal/inc -I../hal/shared -I/rtos/FreeRTOSv8.2.2/FreeRTOS/Source/include -I/rtos/FreeRTOSv8.2.2/FreeRTOS/Source/portable/GCC/ARM_CM3 -I../hal/src/electron -I../hal/src/stm32f2xx -I../hal/src/stm32 -I../platform/shared/inc -I../platform/MCU/STM32F2xx/STM32_USB_Host_Driver/inc -I../platform/MCU/STM32F2xx/STM32_StdPeriph_Driver/inc -I../platform/MCU/STM32F2xx/STM32_USB_OTG_Driver/inc -I../platform/MCU/STM32F2xx/STM32_USB_Device_Driver/inc -I../platform/MCU/STM32F2xx/SPARK_Firmware_Driver/inc -I../platform/MCU/shared/STM32/inc -I../platform/MCU/STM32F2xx/CMSIS/Include -I../platform/MCU/STM32F2xx/CMSIS/Device/ST/Include -I../dynalib/inc -I/workspace/ -I./libraries -I/workspace/ -I/workspace/ -I/workspace/ -I/workspace/ -I. -MD -MP -MF ../build/target/user/platform-10-m/workspace/modbus.o.d -ffunction-sections -fdata-sections -Wall -Wno-switch -Wno-error=deprecated-declarations -fmessage-length=0 -fno-strict-aliasing -DSPARK=1 -DPARTICLE=1 -DSTART_DFU_FLASHER_SERIAL_SPEED=14400 -DSTART_YMODEM_FLASHER_SERIAL_SPEED=28800 -DSPARK_PLATFORM_NET=UBLOXSARA -fno-builtin-malloc -fno-builtin-free -fno-builtin-realloc  -DLOG_INCLUDE_SOURCE_INFO=1 -DPARTICLE_USER_MODULE -DUSE_THREADING=0 -DUSE_SPI=SPI -DUSE_CS=A2 -DUSE_SPI=SPI -DUSE_CS=A2 -DUSE_THREADING=0 -DUSER_FIRMWARE_IMAGE_SIZE=0x20000 -DUSER_FIRMWARE_IMAGE_LOCATION=0x8080000 -DMODULAR_FIRMWARE=1 -DMODULE_VERSION=4 -DMODULE_FUNCTION=5 -DMODULE_INDEX=1 -DMODULE_DEPENDENCY=4,2,105 -D_WINSOCK_H -D_GNU_SOURCE -DLOG_MODULE_CATEGORY="\"app\""  -fno-exceptions -fno-rtti -fcheck-new -std=gnu++11 -c -o ../build/target/user/platform-10-m/workspace/modbus.o /workspace/modbus.cpp
In file included from /workspace/modbus.cpp:1:0:
/workspace/ModBusMaster.h:80:0: warning: "lowByte" redefined [enabled by default]
 #define lowByte(w)                     ((w) & 0xFF)
 ^
In file included from ../wiring/inc/spark_wiring_arduino.h:36:0,
                 from ./inc/application.h:92,
                 from /workspace/modbus.cpp:1:
../wiring/inc/spark_wiring_arduino_constants.h:100:0: note: this is the location of the previous definition
 #define lowByte(w)   ((uint8_t) ((w) & 0xff))
 ^
In file included from /workspace/modbus.cpp:1:0:
/workspace/ModBusMaster.h:81:0: warning: "highByte" redefined [enabled by default]
 #define highByte(w)                    (((w) >> 8) & 0xFF)
 ^
In file included from ../wiring/inc/spark_wiring_arduino.h:36:0,
                 from ./inc/application.h:92,
                 from /workspace/modbus.cpp:1:
../wiring/inc/spark_wiring_arduino_constants.h:104:0: note: this is the location of the previous definition
 #define highByte(w)  ((uint8_t) ((w) >> 8))
 ^
/workspace/modbus.cpp: In function 'void loop()':
/workspace/modbus.cpp:18:12: warning: variable 'data' set but not used [-Wunused-but-set-variable]
 
            ^

Building cpp file: /workspace/ModBusMaster.cpp
Invoking: ARM GCC CPP Compiler
mkdir -p ../build/target/user/platform-10-m/workspace/
arm-none-eabi-gcc -DSTM32_DEVICE -DSTM32F2XX -DPLATFORM_THREADING=1 -DPLATFORM_ID=10 -DPLATFORM_NAME=electron -DUSBD_VID_SPARK=0x2B04 -DUSBD_PID_DFU=0xD00A -DUSBD_PID_CDC=0xC00A -DSPARK_PLATFORM -g3 -gdwarf-2 -Os -mcpu=cortex-m3 -mthumb -DINCLUDE_PLATFORM=1 -DPRODUCT_ID=10 -DPRODUCT_FIRMWARE_VERSION=65535 -DUSE_STDPERIPH_DRIVER -DDFU_BUILD_ENABLE -DPARTICLE_NO_ARDUINO_COMPATIBILITY=0 -DSYSTEM_VERSION_STRING=0.6.1 -DRELEASE_BUILD -I./inc -I../wiring/inc -I../system/inc -I../services/inc -I../communication/src -I../hal/inc -I../hal/shared -I/rtos/FreeRTOSv8.2.2/FreeRTOS/Source/include -I/rtos/FreeRTOSv8.2.2/FreeRTOS/Source/portable/GCC/ARM_CM3 -I../hal/src/electron -I../hal/src/stm32f2xx -I../hal/src/stm32 -I../platform/shared/inc -I../platform/MCU/STM32F2xx/STM32_USB_Host_Driver/inc -I../platform/MCU/STM32F2xx/STM32_StdPeriph_Driver/inc -I../platform/MCU/STM32F2xx/STM32_USB_OTG_Driver/inc -I../platform/MCU/STM32F2xx/STM32_USB_Device_Driver/inc -I../platform/MCU/STM32F2xx/SPARK_Firmware_Driver/inc -I../platform/MCU/shared/STM32/inc -I../platform/MCU/STM32F2xx/CMSIS/Include -I../platform/MCU/STM32F2xx/CMSIS/Device/ST/Include -I../dynalib/inc -I/workspace/ -I./libraries -I/workspace/ -I/workspace/ -I/workspace/ -I/workspace/ -I. -MD -MP -MF ../build/target/user/platform-10-m/workspace/ModBusMaster.o.d -ffunction-sections -fdata-sections -Wall -Wno-switch -Wno-error=deprecated-declarations -fmessage-length=0 -fno-strict-aliasing -DSPARK=1 -DPARTICLE=1 -DSTART_DFU_FLASHER_SERIAL_SPEED=14400 -DSTART_YMODEM_FLASHER_SERIAL_SPEED=28800 -DSPARK_PLATFORM_NET=UBLOXSARA -fno-builtin-malloc -fno-builtin-free -fno-builtin-realloc  -DLOG_INCLUDE_SOURCE_INFO=1 -DPARTICLE_USER_MODULE -DUSE_THREADING=0 -DUSE_SPI=SPI -DUSE_CS=A2 -DUSE_SPI=SPI -DUSE_CS=A2 -DUSE_THREADING=0 -DUSER_FIRMWARE_IMAGE_SIZE=0x20000 -DUSER_FIRMWARE_IMAGE_LOCATION=0x8080000 -DMODULAR_FIRMWARE=1 -DMODULE_VERSION=4 -DMODULE_FUNCTION=5 -DMODULE_INDEX=1 -DMODULE_DEPENDENCY=4,2,105 -D_WINSOCK_H -D_GNU_SOURCE -DLOG_MODULE_CATEGORY="\"app\""  -fno-exceptions -fno-rtti -fcheck-new -std=gnu++11 -c -o ../build/target/user/platform-10-m/workspace/ModBusMaster.o /workspace/ModBusMaster.cpp
In file included from /workspace/ModBusMaster.cpp:32:0:
/workspace/ModBusMaster.h:80:0: warning: "lowByte" redefined [enabled by default]
 #define lowByte(w)                     ((w) & 0xFF)
 ^
In file included from ../wiring/inc/spark_wiring_arduino.h:36:0,
                 from ./inc/application.h:92,
                 from /workspace/ModBusMaster.h:39,
                 from /workspace/ModBusMaster.cpp:32:
../wiring/inc/spark_wiring_arduino_constants.h:100:0: note: this is the location of the previous definition
 #define lowByte(w)   ((uint8_t) ((w) & 0xff))
 ^
In file included from /workspace/ModBusMaster.cpp:32:0:
/workspace/ModBusMaster.h:81:0: warning: "highByte" redefined [enabled by default]
 #define highByte(w)                    (((w) >> 8) & 0xFF)
 ^
In file included from ../wiring/inc/spark_wiring_arduino.h:36:0,
                 from ./inc/application.h:92,
                 from /workspace/ModBusMaster.h:39,
                 from /workspace/ModBusMaster.cpp:32:
../wiring/inc/spark_wiring_arduino_constants.h:104:0: note: this is the location of the previous definition
 #define highByte(w)  ((uint8_t) ((w) >> 8))
 ^
In file included from ./inc/application.h:92:0,
                 from /workspace/ModBusMaster.h:39,
                 from /workspace/ModBusMaster.cpp:32:
/workspace/ModBusMaster.cpp: In function 'uint16_t makeWord(uint8_t, uint8_t)':
../wiring/inc/spark_wiring_arduino.h:53:19: error: redefinition of 'uint16_t makeWord(uint8_t, uint8_t)'
 #define word(...) makeWord(__VA_ARGS__)
                   ^

/workspace/ModBusMaster.cpp:42:10: note: in expansion of macro 'word'
 uint16_t word(uint8_t high, uint8_t low) {
          ^
../wiring/inc/spark_wiring_arduino.h:47:17: error: 'uint16_t makeWord(uint8_t, uint8_t)' previously defined here
 inline uint16_t makeWord(uint8_t h, uint8_t l) {
                 ^

../build/module.mk:267: recipe for target '../build/target/user/platform-10-m/workspace/ModBusMaster.o' failed
make[2]: Leaving directory '/firmware/user'
make[2]: *** [../build/target/user/platform-10-m/workspace/ModBusMaster.o] Error 1
../../../build/recurse.mk:11: recipe for target 'user' failed
make[1]: Leaving directory '/firmware/modules/electron/user-part'
make[1]: *** [user] Error 2
../build/recurse.mk:11: recipe for target 'modules/electron/user-part' failed
make: *** [modules/electron/user-part] Error 2

ModBus_RS485_Slave library compile without problem.


#47

@developer_bt, the errors are caused by the Arduino pre-processor problems. If you compile for system firmware v 0.6.0, it will work.


#48

@peekay123 Thanks whether you will soon update to work with 0.6.1?
And generally which a better library for modbus master?


#49

@developer_bt, Particle will be releasing 0.6.2 soon, which fixes the Arduino compatibility issues. To be honest, I ported the modbus library for some members and I don’t personally use it. The existing IDE modbus master library is the same as mine and seems to be the one most commonly used.


#50

Thank you @peekay123 ,
I’ll wait for the new version to not return to 0.6.1
In any case I will test both libraries.


#51

Hello Folks,
I will share some of my experiences with mod-bus libraries test that I have done so far, kindly ask suggestion for a problem that I having right now with particle photon.

My intention is pulling some data from holding register on VFD Control techniques.
For some reason i din’t have success using this library,Smarmengol-Modbus Library, the problem I have no idea how to address a holding register like this one 401811, if i try to set 1811 register the arduino or photon
hangs and do nothing.

With this library “4-20ma” work fine with arduino… I use software serial to connect the MAX485, and that work perfect, I was able to read any register the VFD… I tried that same library with the ESP8266 and works too… the problem… if one disconnect the max485 from the ESP8266 or simple disconnect the cable from slave, the ESP8266 crash after a few intents of read the registers…timeouts…
I tried to determine the problem… but I am not so advanced programmer to dig into the library … so I gave up … I read somewhere thi is because the library Software Serial + mod-bus disable the GPIO pin interrupts very often . Now I move to try with Particle photon. initially I was having a problem very often got timeout while trying to read 10 holding registers… mysteriously solved
for my test I just used the “4-20ma” library without any modification.
this is the sample code:

#include <ModbusMaster.h>
/* 
  We're using a MAX485-compatible RS485 Transceiver.
  Rx/Tx is hooked up to the hardware serial port at 'Serial'.
  The Data Enable and Receiver Enable pins are hooked up as follows:
*/
#define MAX485_DE      A3
#define MAX485_RE_NEG  A2
#define CT_VFD(m,n)  (m *100+ n-1)  // formula to acces VFD registers

// instantiate ModbusMaster object
ModbusMaster node;
#define reg_qty 11
void preTransmission()
{
  digitalWrite(MAX485_RE_NEG, 1);
  digitalWrite(MAX485_DE, 1);
}

void postTransmission()
{
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);
}

void setup()
{
  pinMode(MAX485_RE_NEG, OUTPUT);
  pinMode(MAX485_DE, OUTPUT);
  // Init in receive mode
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);

  // Modbus communication runs at 115200 baud
  Serial.begin(19200);
  Serial1.begin(19200);
  // Modbus slave ID 1, Serial1 on the Particle Photon
  node.begin(1, Serial1);
  // Callbacks allow us to configure the RS485 transceiver correctly
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
}

bool state = true;

void loop()
{
  uint8_t result;
  uint16_t data[6];

  // Toggle the coil at address 0x0002 (Manual Load Control)
  //result = node.writeSingleCoil(0x0002, state);
  //state = !state;

  // Read 16 registers starting at 0x3100)
  //result = node.readInputRegisters(0x3100, 16);

  // read   holding register VFD Control_techniques  starting at Pr 1811  ( modbus 401811- 401814)
   result=node.readHoldingRegisters(CT_VFD(18,11), reg_qty);
  if (result == node.ku8MBSuccess)
  {
    Serial.println("Data: ");
   // Serial.println(node.getResponseBuffer(1));
    for (uint8_t j = 0; j < reg_qty; j++)  Serial.println(node.getResponseBuffer(j),DEC);

                  Serial1.flush();
                 // node.clearTransmitBuffer();
  }
  else {
    Serial.println("NO- RESPONSE"); // i got not response  very oftem
    //mySerial.flush();

  }
 //node.clearResponseBuffer();
//  node.clearTransmitBuffer();
  delay(5000);
 Serial.println("running...");
}

Questions that I have so maybe some one can provide some advise
-The modbus master library ported by @peekay123 do not work for me . i got some errors… I see this library was ported 3 years ago… so maybe that is the reason… Not clear if 4-20ma library specially ported for spark has any advantage over the un-ported 4-20ma master library.
-this is another diferent library Modbus_RS485 that seems pretty interesting… but as is it right now I can’t get it to read any modbus registers on the VFD using photon not sure if i did right to addressee the register using this line -->telegram[1].u16RegAdd = 1811; // start address in slave


#52

A few questions:

  1. The RS-485 device you are using, is it 1-based or 0-based? If it is 0-based. It should be:
#define CT_VFD(m,n) (m *100+ n)
  1. Is the reg_qty size actually 11? Meaning the data package you are looking for has a size of 2*11= 22 bytes? According to your comment, it seems your reg_qty for 1811 should be 4 but reg_qty is defined as 11.
  2. Does your MODBUS device default to a “sleep” state if nothing is communicating with it? If so you need to ping the device with a read register command, wait a few milliseconds, and try again.

Here is some code I wrote up for you to try out. It handles some of the conditions I stated above.

// Turn serial output on or off
#define DEBUGON 1

// Transmission delay between modbus calls
#define XMITDELAY 100

// 0 for 0-based, 1 for 1-based numbering
#define BASED_NUMBERING 1

/*********************************************************************************
* Retrieves holding register data while handling device sleeping conditions
**********************************************************************************/

bool getHoldingRegisterData(uint16_t registerAddress, uint16_t regSize, uint16_t* data){

    uint8_t j, result;

    if(DEBUGON){
      Serial.print(F("Reading register: "));
      Serial.print(registerAddress);
      Serial.print(F(" regSize: "));
      Serial.print(regSize);
      Serial.print(F(" sizeof(data): "));
      Serial.print(sizeof(&data));
      Serial.print(F(" XMITDELAY: "));
      Serial.println(XMITDELAY);
    }
	
	// Delay and get register data.
    
    result = node.readHoldingRegisters(registerAddress-BASED_NUMBERING, regSize);
	delay(XMITDELAY);
	
	// Device may be sleeping, ping it a couple more times.  
	
	if(result ==node.ku8MBResponseTimedOut){

            if(DEBUGON){
              Serial.println(F("Response timed out. Trying again. "));
            }	
			
			int i =0;
			
			while(i < 2){

				result = node.readHoldingRegisters(registerAddress-BASED_NUMBERING, regSize);
				delay(XMITDELAY);
				
				if(DEBUGON) Serial.printlnf("Timeout iteration# %d. ", i);
				
				if(result == node.ku8MBResponseTimedOut){
					if(DEBUGON) Serial.println(F("Failed. Response timed out. Adjust the XMITDELAY and/or ModbusMaster::ku8MBResponseTimeout? "));
				}
				else break;
				
				i++;
				
			}

    }

    if (result == node.ku8MBSuccess) {
      if(DEBUGON) Serial.print(F("Success, Received data: "));

      for (j = 0; j < regSize; j++) {

        data[j] = node.getResponseBuffer(j);
        if(DEBUGON){
          Serial.print(data[j], HEX);
          Serial.print(F(" "));
        }
      }
	  
	  if(DEBUGON){
		  Serial.println("");
	  }

	  node.clearResponseBuffer();
	  node.clearTransmitBuffer();
          return true;

    }
    else{
	  if(DEBUGON){
		Serial.print(F("Failed, Response Code: "));
		Serial.println(result, HEX);
	  }
    }
	
	node.clearResponseBuffer();
	node.clearTransmitBuffer();
    return false;
    }

#53

Thanks @rama appreciate your support i will try that.


VFD has 16 bits register and 32 bit, I am trying to access to a 16bit ones, application register are contiguous, So i am using the function that read multiple registers, where i set the first register to read and then the qty.
I am using this RS485 breakoout board RS485
my idea is to try a reliable Modbus library that can be used with photon, so i will design a PCB board to place the photon and all the required components to connect it to CT(EMERSON ) ac/dc drives that has mod-bus rtu as standard communication protocol, see the picture of how register can be addressed ( I am using modbus standard protocol on the VFD)


#54

Ok so for a 16 bit value, reg_qty= 1. 32 bit -> reg_qty = 2.

I think there are problems in the way you are implementing the Modbus libraries and you need to write simple unit tests to confirm functionality. It’s difficult to debug.

If you can make a USB-Serial connection with your device, my suggestion is to first download and use this program to make sure your command structure is correct.

Is your MODBUS device configured for a baud rate of 19200 @ 8N1? (this is default for Serial1 I believe). If for example its 8E1, then you need to change to port configure:

Serial1.begin(19200, SERIAL_8E1);

You will know your port is configured properly if you ping the device repeatedly and get at least an error response from your device (qmodmaster can confirm this).

Next, write a simple program testing the variations of the following (hard coded values).

result = node.readHoldingRegisters(1800,1);
result = node.readHoldingRegisters(1801,1);
result = node.readHoldingRegisters(1800,2);

#55

I tested your piece of code and it work as well, your is far better organized that mine… once again thanks.


#56

@luisgcu no problem… it took me a month to figure it all out :blush:


#57

Great job guys :smile:
Whether you can put the full code from the example that you have done for reading holding registers?
Or all the code it is seen in the picture?


#58

hello @developer_bt as soon as I get a bit of free time i will share what I have.

here is picture of my test set with Emerson Control-Techniques VFD.


#59

The port @peekay123 was a great start for me on my modbus project. I did see some other ModbusMaster versions out there too.

Some mods I did to my copy were:

  • Added directives so the library could compile with Particle and Arduino
  • Abstracted (is this the right word?) the MBSerial object to work with CustomSoftwareSerial (Arduino) and for initializing SerialX objects with various baud_rate and parity configs ie. SERIAL_8E1, SERIAL_8N1
  • Added half duplex functionality. ie. using a TX enable pin.

I did see in another ModbusMaster copy that preTransmission and postTransmission callbacks were added in the ModbusMasterTransaction.

I definitely don’t mind uploading the mods, I just don’t know the best way to do it. There’s a few different versions of this library and I don’t want to make another on the internet lol.


#60

I have only one problem, and is when register on the device get a negative value for example -1 , then mod-bus reading display 65535.