Arduino Uno reading Modbus holding registers

I've been at this project for months and have decided I need help. Using an Arduino Uno to read specific holding registers on my generator's controller, but keep getting a timeout error. 485MAX chip is in use. I think the code is fine, compiles and runs, but I am wondering if i need to convert the modbus registers in my code to hexadecimal format for it to work properly. Would anyone like to guide me? I've scoured online resources for awhile with no progress to be had. Using Pin 2 of the Uno to control DE/RE on the 485MAX, Uno TX->DI 485MAX, Uno RX->RO 485MAX

    #include <ModbusMaster.h>
    #include <SoftwareSerial.h> // If using SoftwareSerial for Modbus communication
    

//Define Pins and Objects.
    #define RS485_DE_RE_PIN 2 // Example pin for DE/RE control
    #define MODBUS_RX_PIN 8 // Example pin for Modbus RX
    #define MODBUS_TX_PIN 7 // Example pin for Modbus TX

    SoftwareSerial modbusSerial(MODBUS_RX_PIN, MODBUS_TX_PIN); // If using SoftwareSerial
    ModbusMaster node; // Modbus Master object
//•	//Pre/Post Transmission Callbacks: Define functions to control the RS485 transceiver's data direction (DE/RE pin):
    void preTransmission() {
      digitalWrite(RS485_DE_RE_PIN, HIGH); // Enable transmit
    }

    void postTransmission() {
      digitalWrite(RS485_DE_RE_PIN, LOW); // Enable receive
    }
//Setup Function.
    void setup() {
      Serial.begin(19200); // Initialize Serial for debugging output
      pinMode(RS485_DE_RE_PIN, OUTPUT);
      digitalWrite(RS485_DE_RE_PIN, LOW); // Start in receive mode

      modbusSerial.begin(19200); // Initialize SoftwareSerial for Modbus communication (match slave's baud rate)
      node.begin(1, modbusSerial); // Initialize ModbusMaster with slave ID (e.g., 1) and serial object(may have to change to just "Serial")
      node.preTransmission(preTransmission);
      node.postTransmission(postTransmission);
    }
//Loop Function (Reading and Printing).
    void loop() {
      uint8_t result;
      uint16_t data[5];//array to store 5 register values

      // Example: Read a holding register (function code 0x03)
      // Replace 0x01 with the actual register address and 1 with the number of registers to read
      result = node.readHoldingRegisters(401102, 1);//holding register for oil pressure
      

      if (result == node.ku8MBSuccess) {
        data[0] = node.getResponseBuffer(0); // Get the data from the response buffer (index 0 for the first register)
        

        Serial.print("Engine Oil Pressure: ");
        Serial.println(data[0]);
        
      } else {
        Serial.print("Modbus read error: ");
        Serial.println(result, HEX); // Print error code in hexadecimal
      }

      delay(500); // Delay between readings
      
      result = node.readHoldingRegisters(401107, 1);//holding register for battery voltage
      

      if (result == node.ku8MBSuccess) {
        data[1] = node.getResponseBuffer(1); // Get the data from the response buffer (index 1 for the 2nd register)
        

        Serial.print("Battery Voltage: ");
        Serial.println(data[1]);
        
      } else {
        Serial.print("Modbus read error: ");
        Serial.println(result, HEX); // Print error code in hexadecimal
      }
      delay(500);//delay between readings
      
      result = node.readHoldingRegisters(401340, 1);//holding register for L-L avg
      

      if (result == node.ku8MBSuccess) {
        data[2] = node.getResponseBuffer(2); // Get the data from the response buffer (index 2 for the 3rd register)
        

        Serial.print("AVG L-L Voltage: ");
        Serial.println(data[2]);
        
      } else {
        Serial.print("Modbus read error: ");
        Serial.println(result, HEX); // Print error code in hexadecimal
      }
      delay(500);//delay between readings

      result = node.readHoldingRegisters(401103, 1);//holding register for coolant temp
      

      if (result == node.ku8MBSuccess) {
        data[3] = node.getResponseBuffer(3); // Get the data from the response buffer (index 3 for the 4th register)
        

        Serial.print("Coolant Temp: ");
        Serial.println(data[3]);
        
      } else {
        Serial.print("Modbus read error: ");
        Serial.println(result, HEX); // Print error code in hexadecimal
      }
      delay(500);//delay between readings

      result = node.readHoldingRegisters(401506, 1);//holding register for run time
      

      if (result == node.ku8MBSuccess) {
        data[4] = node.getResponseBuffer(4); // Get the data from the response buffer (index 4 for the 5th register)
        

        Serial.print("Run Time: ");
        Serial.println(data[4]);
        
      } else {
        Serial.print("Modbus read error: ");
        Serial.println(result, HEX); // Print error code in hexadecimal
      }
      delay(500);//delay between readings

    }

type or paste code here
type or paste code here

Welcome to the forums. Take a moment and read this: How to get the best out of this forum - Using Arduino / Installation & Troubleshooting - Arduino Forum

Then Post your code (using code tags) and how you have your 485Max chip connected and it will greatly improve your chances of someone being able to help you.

Thanks, I'm stumbling through this first post, aren't I? lol

Please give a link to the document where You found that?

Hmm.

From ModbusMaster.h:

uint8_t  readHoldingRegisters(uint16_t, uint16_t);

The range of a uint16_t is [0..65535].

401340 doesn't fit in that range.

So that's obviously not going to work.

Begin by dealing with all the warnings that crop up when you turn the compiler's warning level to ALL.

arduino-cli compile -b arduino:avr:uno --warnings all --output-dir ~/tmp --no-color (in directory: /home/me/Documents/sketchbook/Uno_R3/test)

/home/me/Documents/sketchbook/Uno_R3/test/test.ino: In function 'void loop()':
/home/me/Documents/sketchbook/Uno_R3/test/test.ino:38:51: warning: large integer implicitly truncated to unsigned type [-Woverflow]
       result = node.readHoldingRegisters(401102, 1);//holding register for oil pressure
                                                   ^
/home/me/Documents/sketchbook/Uno_R3/test/test.ino:55:51: warning: large integer implicitly truncated to unsigned type [-Woverflow]
       result = node.readHoldingRegisters(401107, 1);//holding register for battery voltage
                                                   ^
/home/me/Documents/sketchbook/Uno_R3/test/test.ino:71:51: warning: large integer implicitly truncated to unsigned type [-Woverflow]
       result = node.readHoldingRegisters(401340, 1);//holding register for L-L avg
                                                   ^
/home/me/Documents/sketchbook/Uno_R3/test/test.ino:87:51: warning: large integer implicitly truncated to unsigned type [-Woverflow]
       result = node.readHoldingRegisters(401103, 1);//holding register for coolant temp
                                                   ^
/home/me/Documents/sketchbook/Uno_R3/test/test.ino:103:51: warning: large integer implicitly truncated to unsigned type [-Woverflow]
       result = node.readHoldingRegisters(401506, 1);//holding register for run time
                                                   ^
In file included from /home/me/Documents/sketchbook/libraries/ModbusMaster/src/ModbusMaster.h:58:0,
                 from /home/me/Documents/sketchbook/Uno_R3/test/test.ino:1:
/home/me/Documents/sketchbook/libraries/ModbusMaster/src/util/crc16.h: At global scope:
/home/me/Documents/sketchbook/libraries/ModbusMaster/src/util/crc16.h:71:17: warning: 'uint16_t crc16_update(uint16_t, uint8_t)' defined but not used [-Wunused-function]
 static uint16_t crc16_update(uint16_t crc, uint8_t a)
                 ^~~~~~~~~~~~
/home/me/Documents/sketchbook/libraries/ModbusMaster/src/ModbusMaster.cpp: In member function 'uint8_t ModbusMaster::requestFrom(uint16_t, uint16_t)':
/home/me/Documents/sketchbook/libraries/ModbusMaster/src/ModbusMaster.cpp:84:44: warning: unused parameter 'address' [-Wunused-parameter]
 uint8_t ModbusMaster::requestFrom(uint16_t address, uint16_t quantity)
                                            ^~~~~~~
/home/me/Documents/sketchbook/libraries/ModbusMaster/src/ModbusMaster.cpp:94:27: warning: 'read' is used uninitialized in this function [-Wuninitialized]
   _u8ResponseBufferLength = read;
   ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
Sketch uses 7996 bytes (24%) of program storage space. Maximum is 32256 bytes.
Global variables use 691 bytes (33%) of dynamic memory, leaving 1357 bytes for local variables. Maximum is 2048 bytes.
Compilation finished successfully.

That's the holding registers provided by the manufacturer, can't link the document because its proprietary.

Okey, thanks. What does the table say at the head? As You said in the post, are they hexadecimal numbers? In Your code they appear as integers but they are too big to fit in a standard integer, -32768 to + 32767 for a 16 bit.

Signed, or unsigned long could be important as well as adding the hex suffix.


Reckon my code should say 1339 instead of 401340? That's what I'm gathering from the manual description anyway. It appears they're saying its 0 based, so -1 from what it's displayed as and omit the 40 prefix as that designates its a holding register and nothing more, am I getting warmer here or not so much?

I'm learning new things already, lol. Thanks. I don't know how to turn on the "ALL" function on my compiler, just using the Arduino IDE. Might try it through Eclipse later when I'm home. Appreciate the help.

so I changed the registers in my code, omitted the 40 and my theory proved incorrect. still no communication between devices. I'm open to any ideas at this point. I didn't think this would be a difficult project....

That sounds wrong to me. Adding “UL” makes the compiler handle the large number correctly.

Can you give me an example of what you're referring to? I'm so new to this that I'm surprised I can even make a basic loop function properly.

Arduino/reference is a very good place to visit. Look for “variables”.

Learn what a “bit”, a “byte”, an “integer”, “long integer” stands for. It’s a common mistake to use the wrong format.

File -> Preferences -> Compiler Warnings = ALL

When you get timeout, it's quite surely not related to wrong address. Correctly made slave should give an other exception if address is not valid.
Also, 401506 can only translate to 0x05E1 or 0x05E2. Try them both.

Post more info about your wiring, length, soldered or not, powering, termination resistors used etc...

There are several different ModbusMaster libraries. Which one are you using? The one referenced by @van_der_decken allows you to use SoftwareSerial as your Stream. Others do not. They only allow hardware Serial ports.

Another thing you may try is this. Since your proprietary documentation mentions that the leading digit (or digits) still represent the type of Modbus register, 04 = an Input Register, not a Holding Register.

Replace this:

result = node.readHoldingRegisters(401102, 1);

with

result = node.readInputRegisters(1102, 1);

Good Luck!

I believe the one I'm using allows for SoftwareSerial. I did try to use 1102 as a holding register with no change in status. Wiring does not use a terminating resistor, wiring is about 1 foot, I'm operating at the back of the controller. I removed the remote serial annunciator currently in use before hooking up to the generator controller. The annunciator works fine, but it is programmed to be a Modbus master and you can't have 2 masters on the same line.


changed to a long and enabled all warnings on the compiler, this is what i get. What does this mean? I understand its maybe expecting a larger integer than what the register is, but this shouldn't affect how the program runs, right?


manual says they're holding registers, idk

So I should be writing my holding registers in HEX format in the program? So instead of 401506, I should be writing (0x05E1,1)?