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; }
Add Comment
You must be logged in to post a comment.