Arduino RS485 keine Antwort vom Meter

Hallo,

ich benötige einen Tip oder ähnliches.

Ziel:
Der Uno R3 soll mittels RS485 die Daten eines Kostal Smart Meters auslesen.

Hardware:

  • Arduino Uno R3
  • JZK TTL auf RS485 Modul (Amazon)
  • Kostal Smart Energy Meter (Als slave)

Ich habe zusätzlich einen USB zu RS458 und die SimpleModbus Software.

Wenn ich den Uno (Master) mit dem PC (Slave) verbinde kann ich die
Daten normal abfragen.
Das gleiche mit dem PC (Master) und dem Smart Meter (Slave).

Nur wenn ich den Uno (Master) mit dem Meter (Slave) verbinde ..
passiert nichts.
Ich habe parallel den USB angeschlossen und sehe wie der Uno sendet. Er
bekommt aber keine Antwort.

(In dem Bild sind noch weitere Bauteile für späteres. Habe später alles entfernt.)

#include <SoftwareSerial.h>
 
// Create a SoftwareSerial object to communicate with the MAX485 module
SoftwareSerial mySerial(11, 10); // RX, TX
 
// Define Modbus parameters
const byte slaveAddress = 0x01;          // Address of the Modbus slave device
const byte functionCode = 0x03;          // Function code to read holding registers
const byte startAddressHigh = 0x00;      // High byte of the starting address
const byte startAddressLow = 0x28; //2A;       // Low byte of the starting address
const byte registerCountHigh = 0x00;     // High byte of the number of registers to read
const byte registerCountLow = 0x01;      // Low byte of the number of registers to read

uint32_t data;

void setup() {
  // Initialize serial communication for debugging
  Serial.begin(9600);
  // Initialize SoftwareSerial for Modbus communication
  mySerial.begin(9600);
  // Allow some time for initialization
  delay(1000);
}
 
void loop() { Serial.println("- - - - -  Start - - - - - ");
  // Create a request frame for Modbus communication
  byte requestFrame[8];
  constructModbusRequest(requestFrame, slaveAddress, functionCode, startAddressHigh, startAddressLow, registerCountHigh, registerCountLow);
 
  // Send the Modbus request frame
  sendModbusRequest(requestFrame, 8);
 
  // Read and process the Modbus response frame
  if (mySerial.available()) {
    // Create a buffer to store the response frame
    byte responseFrame[9];
    // Read the response frame from the slave device
    readModbusResponse(responseFrame, 9);
    // Process the response frame to extract data
    processModbusResponse(responseFrame);
  } /*else { just for testing
    // Print an error message if no response is received
    Serial.println("No response from slave.");
  }*/

  Serial.print("Daten - ");
  Serial.println(data);
  Serial.println(data, HEX);
 
  // Wait for 2 seconds before the next request
  delay(10000);
}
 

// Function to construct a Modbus request frame
void constructModbusRequest(byte *frame, byte address, byte function, byte startHigh, byte startLow, byte countHigh, byte countLow) {
  frame[0] = address;          // Address of the slave device
  frame[1] = function;         // Function code
  frame[2] = startHigh;        // High byte of the starting address
  frame[3] = startLow;         // Low byte of the starting address
  frame[4] = countHigh;        // High byte of the number of registers to read
  frame[5] = countLow;         // Low byte of the number of registers to read
 
  // Calculate and append the CRC to the request frame
  uint16_t crc = calculateCRC(frame, 6);
  frame[6] = crc & 0xFF;         // CRC low byte
  frame[7] = (crc >> 8) & 0xFF;  // CRC high byte
}
 
// Function to send a Modbus request frame
void sendModbusRequest(byte *frame, byte length) {
  for (byte i = 0; i < length; i++) {
    mySerial.write(frame[i]); // Send each byte of the frame
  }
}
 
// Function to read a Modbus response frame
void readModbusResponse(byte *frame, byte length) {
  for (byte i = 0; i < length; i++) {
    if (mySerial.available()) {
      frame[i] = mySerial.read(); // Read each byte of the frame
      Serial.println(frame[i]);
    }
  }
}
 
// Function to process the Modbus response frame and extract data
void processModbusResponse(byte *frame) {
  uint32_t data1 = (frame[3] << 8) | frame[4];   //Translate the int16 to int32. Otherwise it deletes the bits (when shifting with ">> 24")
  uint16_t data2 = (frame[5] << 8) | frame[6];
  data = (data1 << 16) | data2; //int32
  //Serial.print("Daten - ");
  //Serial.println(data);
  //Serial.println(data, HEX);
}

// Function to calculate the CRC of a Modbus frame
uint16_t calculateCRC(byte *frame, byte length) {
  uint16_t crc = 0xFFFF; // Initialize CRC to 0xFFFF
  for (byte i = 0; i < length; i++) {
    crc ^= frame[i]; // XOR the frame byte with the CRC
    for (byte j = 0; j < 8; j++) {
      if (crc & 0x0001) { // Check if the LSB of the CRC is 1
        crc >>= 1;        // Right shift the CRC
        crc ^= 0xA001;    // XOR the CRC with the polynomial 0xA001
      } else {
        crc >>= 1;        // Right shift the CRC
      }
    }
  }
  return crc; // Return the calculated CRC
}

Dein Empfangscode ist "eigenartig". Aber wenn du schon keine Antwort via PC sniffen kannst, wird das senden schon nicht korrekt sein.

An deiner Stelle würde ich es mal mit einer Library versuchen.
Ich mag die Modbus Master von DocWalker aus dem Library Manager.

Verstehe.
Per PC bekomme ich Antworten. Nur Uno und Meter wollen nicht zusammen.

Dann untersuchst du, was der PC anderes sendet, als der Arduino!
Der klassische Vergleichstest, bis der Unterschied gefunden ist.

Gute Idee. Das hatte ich mir angeschaut, er sendet den gleichen HEX code wie der PC.

Ich habe mit dem Uno Daten vom PC abgefragt, was passt. Den PC habe ich mit den gleichen settings wie das Meter.

noch mal konkret bitte:

Wenn du vom UNO als Master zu deinem Meter/Slave sendest

  • siehst du eine Bytegleiche Anfrage vom UNO am PC?
  • siehst du eine taugliche Antwort vom Slave?

Als ich meinen PC (als slave) parallel angeklemmt hatte, konnte ich die request vom Uno an das Meter sehen. Der HEX code war wie er sein sollte. Nur die antwort blieb aus
Im Bild sieht man die requests vom Uno. (Sollte sein was du meinst?)
grafik

Schick doch mal was vom PC und schneide die acht byte mit.
Genau die selben acht byte schickst Du dann vom Arduino.

Nicht, dass da irgendwas mit der CRC schief geht.

Beim letzten vergleich war alles gleich, aber kann ich machen. Ist in einem anderen Gebäude.
Schicke morgen bildern rein

ja.

Ich würde mit einer Library weitermachen.

Ich habe noch einmal sceenshots vom ganzen gemacht.

1 PC <-> Meter

Auch wenn es sich etwas komisch verhält bezüglich des CRCs bekomme ich den wert vom Meter.

2 Arduino <-> Meter

Obwohl ich genau das gleiche sende bekomme ich keine Antwort.
Links habe ich den PC parallel angeklemmt um den Bus zu sehen.

Ein auszug aus dem Manual. Die 42 (2A) ist das Register welches ich benötige.

aaah, zwei stop bits.

probier mal:

mySerial.begin(9600, SERIAL_8E2);

Gute idee, den kannte ich nocht nicht.
Funktioniert dies auch mit SoftwareSerial ?
Wirft mir einen compiler Fehler.

Warum hältst Du diesen vor uns geheim?

Gruß Tommy

Klar, entschuldige. Hatte noch rumprobiert.

A:\Projects\Heizstab\TestingModbusAnd\TestingModbusAnd.ino: In function 'void setup()':
A:\Projects\Heizstab\TestingModbusAnd\TestingModbusAnd.ino:20:34: error: no matching function for call to 'SoftwareSerial::begin(int, int)'
   mySerial.begin(9600, SERIAL_8E2);
                                  ^
In file included from A:\Projects\Heizstab\TestingModbusAnd\TestingModbusAnd.ino:1:0:
C:\Users\Inori\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\libraries\SoftwareSerial\src/SoftwareSerial.h:94:8: note: candidate: void SoftwareSerial::begin(long int)
   void begin(long speed);
        ^~~~~
C:\Users\Inori\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\libraries\SoftwareSerial\src/SoftwareSerial.h:94:8: note:   candidate expects 1 argument, 2 provided
exit status 1

Compilation error: no matching function for call to 'SoftwareSerial::begin(int, int)'

Dann machen wir das so

Die Fehlermeldung ist ein Text, den man mit Markieren, Strg+C kopieren kann und in Codetags hier einfügen kann. Dann muss sich keiner die Augen verbiegen.

Gruß Tommy

dann nimm eine andere Serial Library oder stell dein Target um.

Habe es kurz auf die Hardware Serial umgesteckt und myserial zu Serial geändert.
Mit dem "SERIAL_8E2" bekomme ich warhaftig passende zahlen.

Besten dank noiasca!

Kennt dann jemand eine SoftwareSerial wo ich die 2-Stop-Bits einstellen kann?
Brauche den Hardware für debugging und zum aufspielen.

Nimm einen controller mit mehreren Hardware Serial.
Ansonsten wäre Debugging über Soft Serial ja eigentlich auch möglich.

ja, irgendwie so.
Hatte auch eine CustomSoftwareSerial library gesehen. werde mal herum probieren.
thx.