After finishing the replacement for the Digital Multi Control screen functions I wanted to add the Modbus communication and test the connection, but it was not as easy as I expected from the Java experience.

The information about Modbus TCP for Arduino is a total mess on the web. Lots of pages for serial communication but not much on TCP, and the stuff you find is dated, unfinished, too big for an Uno and none of the three library attempts were working with the CCGX. I just could not get a proper response. The CCGX received but I always got an error response back. I logged into the CCGX with SSH and looked at the error logs for Modbus communication and saw that not a single message was received as sent. I tested the Arduino Ethernet shield with all kinds of functions to see if I could replicate the problem, but they all worked. Telnet, HTTP and other functions between Arduinos and Arduino and PC were all working. I tried different Ethernet shields, I have a W5100,W5200 and W5500 shield, but it made no difference. I took the laptop down to the van to try without the Ethernet bridge, I tried different Ethernet speeds but nothing wanted to work.

I reduced the smallest library I had found to the bare minimum, put test outputs everywhere, but nothing wanted to work. I went through the code with a fine comb and one thing they all had in common was that they did send the data with a single byte write command. I found this not very elegant and I am a friend of as little overhead as possible so I changed the write command to a block write and all of a sudden I got proper responses from the CCGX after adding a wait loop. None of the library’s did have proper error handling or considered timing, very poor efforts.

Here is the code for the test program in case somebody finds this and wants to have a bare minimum to try the connection. This sample reads from the BMV at index 0 (Unit Id 245). I read the register 259 (Battery voltage) in a loop with a delay of 2 seconds. I use fixed ip addresses, which need to be adjusted to the local situation. ┬áThere is really nothing to it, but it really bugged me. There must be something different with the CCGX Ethernet interface since it is so time critical. Anyway, I don’t really care about it, I am just happy to got it working.

#include <SPI.h>
#include <Ethernet.h>

#define MB_PORT 502

EthernetServer ModbusSlave(MB_PORT);
EthernetClient ModbusMaster;

// Ethernet settings (depending on MAC and Local network)
byte mac[] = {0x90, 0xA2, 0xEA, 0x0E, 0x54, 0xB5 };
IPAddress ip(192, 168, 0, 171);
byte SlaveIp[] = {192, 168, 0, 8};

uint8_t modbusRequest[26];                  // send and receive buffer

int transactionCount = 1;
// #define DEBUG

void setup() {
  // serial setup
  Serial.begin(115200);

  // initialize the ethernet device
  Ethernet.begin(mac, ip);
  delay(1000);
#ifdef DEBUG
  // print our local IP address as a test
  Serial.print("My IP address: ");
  for (byte thisByte = 0; thisByte < 4; thisByte++) {
    // print the value of each byte of the IP address:
    Serial.print(Ethernet.localIP()[thisByte], DEC);
    Serial.print(".");
  }
  Serial.println();
#endif
}

void loop(){

  Serial.println(readRegister(transactionCount++, 245, 259));
  delay(2000);
}

// ------------ Read the register  from Uinit Id and Register
// 0 - 6  MBAP Header 7-11 Modbus PDU
word  readRegister(word tId, byte uId, word registerNumber) {
  modbusRequest[0] = highByte(tId);             // Transaction ID high byte
  modbusRequest[1] = lowByte(tId);              // Transaction ID low byte
  modbusRequest[2] = 0;                         // protocol high byte FIXED
  modbusRequest[3] = 0;                         // protocol low byte
  modbusRequest[4] = 0;                         // Lenght high byte
  modbusRequest[5] = 6;                         // Lenght low byte FIXED
  modbusRequest[6] = uId;                       // unit ID
  modbusRequest[7] = 4;                         // function code FIXED
  modbusRequest[8] = highByte(registerNumber);  // Register to read
  modbusRequest[9] = lowByte(registerNumber);
  modbusRequest[10] = 0;
  modbusRequest[11] = 1;                         // Read count FIXED

  if (ModbusMaster.connect(SlaveIp, 502)) {
    // single char write did not work with the CCGX, worked with everything else
    //
    ModbusMaster.write(modbusRequest, 12);            // send the request
    int byteCounter = 0;
    modbusRequest[7] = 0;                             // response function code
    while (!ModbusMaster.available()) {
      // This needs timeout handling                  // the wait is essential
      // can do other things inbetween
    }
    while (ModbusMaster.available()) {                 // if data available 
      modbusRequest[byteCounter++] = ModbusMaster.read();   // read the response
      if (byteCounter > 10)  {                         // We expect 10 bytes
        ModbusMaster.stop();
        if (modbusRequest[7] == 4) {                   // Errors have function codes >= 0x80
          word result = (modbusRequest[9] * 0x100) + modbusRequest[10];   // Get the result vlue
          return result;
        } else
          return -1;                         // needs better error handling
      }
    }
  } else {
#ifdef DEBUG
    Serial.println("connection with modbus slave failed");
#endif
    ModbusMaster.stop();
  }
  return -1;
}