Fehlermeldung mit Modbus Library

Hallo,

habe mir die Modbus Library esp32ModbusRTU heruntergeladen/installiert und das Demo-Sketch geladen. Lässt sich übersetzen wie gewüscht. Beim Versuch die CallBack Funktion onData als separate Funktion einzufügen erhalte ich die Fehlermeldung: no matching function for call to 'esp32ModbusRTU::onData(void (&)(uint8_t, esp32Modbus::FunctionCode, uint8_t*, size_t))'

#define VARIANTE 1 --> funktioniert
#define VARIANTE 2 --> Fehlermeldung

gemäß Beschreibung Homepage sollte dies jedoch funktionieren? Wo liegt der Fehler?

Gruß Arduino4Fun

Code

/*

Copyright 2018 Bert Melis

This example reads 2 words (4 bytes) from address 52 of a server with id 1.
address 52 = register 30053 (Eastron SDM630 Total system power)
The ESP is connected to a max3485 with pins 17 (RX), 4 (TX) and 16 as RTS.

*/

#include <Arduino.h>
#include <esp32ModbusRTU.h>
#include <algorithm>  // for std::reverse

esp32ModbusRTU modbus(&Serial1, 16);  // use Serial1 and pin 16 as RTS

#define VARIANTE  2

#if( VARIANTE == 2 )
  void handleError( esp32Modbus::Error error )
  {
    Serial.printf("error: 0x%02x\n\n", static_cast<uint8_t>(error));
  }
  
  void handleData(uint8_t serverAddress, esp32Modbus::FunctionCode fc, uint8_t* data, size_t length) 
  {
    Serial.printf("received data: id: %u, fc %u\ndata: 0x", serverAddress, fc);
    for (uint8_t i = 0; i < length; ++i) {
      Serial.printf("%02x", data[i]);
    }
    Serial.print("\n");
  }
#endif

void setup() {
  Serial.begin(115200);  // Serial output
  Serial1.begin(9600, SERIAL_8N1, 17, 4, true);  // Modbus connection

  #if( VARIANTE == 1 )
    modbus.onData([](uint8_t serverAddress, esp32Modbus::FunctionCode fc, uint16_t address, uint8_t* data, size_t length) 
    {
      Serial.printf("id 0x%02x fc 0x%02x len %u: 0x", serverAddress, fc, length);
      for (size_t i = 0; i < length; ++i) {
        Serial.printf("%02x", data[i]);
      }
      std::reverse(data, data + 4);  // fix endianness
      Serial.printf("\nval: %.2f", *reinterpret_cast<float*>(data));
      Serial.print("\n\n");
    });
    modbus.onError([](esp32Modbus::Error error) 
    {
      Serial.printf("error: 0x%02x\n\n", static_cast<uint8_t>(error));
    });
  #endif

  #if( VARIANTE == 2 )
    modbus.onData( handleData );
    modbus.onError( handleError );
  #endif

  modbus.begin();
}

void loop() 
{
  static uint32_t lastMillis = 0;
  if (millis() - lastMillis > 30000) {
    lastMillis = millis();
    Serial.print("sending Modbus request...\n");
    modbus.readInputRegisters(0x01, 52, 2);
  }
}

Frage mal auf Github nach (issue eröffnen), Bert antwortet meist schnell. Oder steige auf seine (und meine... ;)) Nachfolgelibrary eModbus um - er hat die verlinkt oder du gehst gleich auf [Github] (https://emodbus.github.io/).

Mit dem Issue scheint es wohl nicht zu gehen

This repository has been archived by the owner. It is now read-only.

Dann nimm eModbus - da kann ich dir eher helfen.

Danke für die Antwort. Gibt es bereits einen Sketch zum Auslesen des recht verbreiteten Stromzähler "EASTRON SDM630 Modbus-V2 MID Zweirichtungs-Multifunktionsstromzähler" auf Basis eModbus?

Nicht, das ich wüsste, aber nach einem schnellen Blick auf das PDF mit dem Protokoll ist das ziemlich geradeaus. Du kannst alle Werte inklusive der floats direkt auslesen. Ich habe einen Schneider-Zähler, der sehr ähnlich aufgebaut ist.

Hallo Miq,

Kannst du mir einen "einfachen" Sketch mit der Methodik Auslesen zu deinem Schneider Zähler bereitstellen, so dass ich den für den SDM portieren kann. Wäre klasse!

Danke und Gruß Arduino4Fun

Möchte die Daten einfach mit dem ESP32 auslesen und per MQTT an ioBroker zu senden

Ich würde tatsächlich empfehlen, dass du dir einen der existierenden Client-Beispielsketche hernimmst und anhand der Registerliste deines Zählers modifizierst. Die Werte gibst du erstmal nur auf Serial aus.

Damit erwirbst du dir ein Verständnis des Protokolls und kannst dann im nächsten Schritt überlegen, in was für einer Datenstruktur du die gelesenen Daten ablegen willst. Als letztes besorgst du dir eine MQTT-Library und schickst deine Daten auf die Reise.

Hi Miq1,

danke für den Hinweis. Werde mir die Client-Beispielsketche anschauen. MQTT ist bereits mit anderen ESP32 Applikationen am laufen und sollte kein Problem darstellen.

Danke und Gruß Arduino4Fun

es gibt eine eigene SDM lib, die nutze ich um die Daten per MQTT zum iobroker zu schicken.

sdm.readVal(SDM_PHASE_2_CURRENT),3,2,L2_Current

https://github.com/reaper7/SDM_Energy_Meter
so als Alternative ....
Grüsse
Christian

Hallo Miq,

habe mit der eModbus Library ein wenig experimentiert - jedoch ohne Erfolg. Ich kann meinem SDM630 keine Daten entlocken.

Kannst du mir ein Minimal-Sketch zum Auslesen deines Schneider-Zähler mit Hilfe der eModbus Library bereitstellen?

Habe auch nochmals mit der esp32ModbusRTU Library experimentiert. Hier kann ich Daten mit Hilfe der Demo empfangen ... jedoch nicht stabil. ESP32 runtertakten geht besser, zwei Aufrufe hintereinander gar nicht ... also auch keine Alternative.

Gruß Arduino4Fun

Ich schaue morgen mal danach. PLS SBY!

Ach so:

  • welche Server-ID hat Dein Zähler?
  • was für RS485-Adapter benutzt du - Autohalbduplex oder mit RE/DE-Pins?
  • Baudrate?
  • welche Serial benutzt du?
  • RS485 ist an einer Seite terminiert? Oder hast du ganz kurze Drähte?

Hallo Miq1,

der Zähler hat per Default die ID 01. Der Adapter nutzt RE/DE über Pin 4. Die Baudrate beträgt 9600 und ich verwende die Pins RX 16 und TX 17 somit Serial2. Termininierung Senderseitig über den den Adapter und Engerät über den SDM630, siehe Bild. Test-Verbindung über ein CAT Kabel.

Gruß Arduino4Fun

Das sieht erstmal alles vernünftig aus. Ich hacke morgen mal was zusammen.

Hallo Miq1,

wie oben geschrieben ... mit der esp32ModbusRTU Library konnte ich dem SDM630 Werte entlocken. Mit Einstellung 240MHz CPU Takt so gut wie gar nicht, mit 10MHz etwas stabiler. Sollte nur ein Test sein und aus Verzweiflung alles mögliche verändert. Der Code dazu war

// Läuft nur einigermaßen stabil mit CPU Frequenz  10 MHz (40 MHz XTAL), 4..5 Zyklen falsch, dann Werte
// Läuft nur einigermaßen stabil mit CPU Frequenz  20 MHz (40 MHz XTAL), 4..5 Zyklen falsch, dann Werte
// Läuft NICHT            stabil mit CPU Frequenz  40 MHz (40 MHz XTAL), 1x kam Wert nach 4..5x falsch, dann falsch
// Läuft NICHT            stabil mit CPU Frequenz  80 MHz (40 MHz XTAL), 1x kam Wert nach 4..5x falsch, dann falsch
// Läuft NICHT            stabil mit CPU Frequenz 160 MHz (40 MHz XTAL), 1x kam Wert nach 4..5x falsch, dann falsch
// Läuft NICHT            stabil mit CPU Frequenz 240 MHz (40 MHz XTAL), 1x kam Wert nach 4..5x falsch, dann falsch

// UART    RX IO   TX IO   CTS   RTS
// UART0   GPIO3   GPIO1   N/A   N/A
// UART1   GPIO9   GPIO10  GPIO6   GPIO11
// UART2   GPIO16  GPIO17  GPIO8   GPIO7

#include <Arduino.h>
#include <esp32ModbusRTU.h>
#include <algorithm>  // for std::reverse

esp32ModbusRTU modbus( &Serial2,4 ); 

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

  Serial.print("Serial Init Debug...\n");
  
  Serial2.begin( 9600,SERIAL_8N1,16,17 );  // Modbus connection

  Serial.print("Serial Init ModBus...\n");

  modbus.onData([](uint8_t serverAddress, esp32Modbus::FunctionCode fc, uint16_t address, uint8_t* data, size_t length) 
  {
    Serial.printf("id 0x%02x fc 0x%02x len %u: 0x", serverAddress, fc, length);
    for (size_t i = 0; i < length; ++i) {
      Serial.printf("%02x", data[i]);
    }
    std::reverse(data, data + 4);  // fix endianness
    Serial.printf("\nval: %.2f", *reinterpret_cast<float*>(data));
    Serial.print("\n\n");
  });
  modbus.onError([](esp32Modbus::Error error) 
  {
    Serial.printf("error: 0x%02x\n\n", static_cast<uint8_t>(error));
  });
  modbus.begin();

  Serial.print("ModBus started...\n");
}

void loop() 
{
  static uint32_t lastMillis = 0;
  
  if( millis() - lastMillis > 3000) 
  {
    lastMillis = millis();
    Serial.print("sending Modbus request...\n");
    modbus.readInputRegisters(0x01, 0, 2);
  }
}

und im Seriel Monitor erscheint dann auch

sending Modbus request...
id 0x01 fc 0x04 len 4: 0x4367afb4
val: 231.69

Im Ziel möchte ich die eModbus Library verwenden

Gruß Arduino4Fun

Der Systemtakt sollte überhaupt keinen Einfluss haben. Da ist irgendwas anderes faul, aber, wie geschrieben, nicht meine Baustelle :wink:

Noch etwas Geduld, bitte.

Okay, hier ist was. Da ich das Gerät nicht habe, habe ich mir ein paar von der Beschreibung sinnvolle Register herausgepickt, ab 0x002A. Die sollten alle 10 Sekunden abgefragt und auf Serial ausgegeben werden.

// =================================================================================================
// eModbus: Copyright 2020 by Michael Harwerth, Bert Melis and the contributors to ModbusClient
//               MIT license - see license.md for details
// =================================================================================================

// Example code to show the usage of the eModbus library. 
// Please refer to root/Readme.md for a full description.

// Note: this is an example for the "EASTRON SDM630 Modbus-V2" power meter!

// Includes: <Arduino.h> for Serial etc.
#include <Arduino.h>

// Include the header for the ModbusClient RTU style
#include "ModbusClientRTU.h"
#include "Logging.h"

// Definitions for this special case
#define RXPIN GPIO_NUM_16
#define TXPIN GPIO_NUM_17
#define REDEPIN GPIO_NUM_4
#define BAUDRATE 9600
#define FIRST_REGISTER 0x002A
#define NUM_VALUES 21
#define READ_INTERVAL 10000

bool data_ready = false;
float values[NUM_VALUES];
uint32_t request_time;

// Create a ModbusRTU client instance
// The RS485 module has no halfduplex, so the second parameter with the DE/RE pin is required!
ModbusClientRTU MB(Serial2, REDEPIN);

// Define an onData handler function to receive the regular responses
// Arguments are received response message and the request's token
void handleData(ModbusMessage response, uint32_t token) 
{
  // First value is on pos 3, after server ID, function code and length byte
  uint16_t offs = 3;
  // The device has values all as IEEE754 float32 in two consecutive registers
  // Read the requested in a loop
  for (uint8_t i = 0; i < NUM_VALUES; ++i) {
    offs = response.get(offs, values[i]);
  }
  // Signal "data is complete"
  request_time = token;
  data_ready = true;
}

// Define an onError handler function to receive error responses
// Arguments are the error code returned and a user-supplied token to identify the causing request
void handleError(Error error, uint32_t token) 
{
  // ModbusError wraps the error code and provides a readable error message for it
  ModbusError me(error);
  LOG_E("Error response: %02X - %s\n", (int)me, (const char *)me);
}

// Setup() - initialization happens here
void setup() {
// Init Serial monitor
  Serial.begin(115200);
  while (!Serial) {}
  Serial.println("__ OK __");

// Set up Serial2 connected to Modbus RTU
  Serial2.begin(BAUDRATE, SERIAL_8N1, RXPIN, TXPIN);

// Set up ModbusRTU client.
// - provide onData handler function
  MB.onDataHandler(&handleData);
// - provide onError handler function
  MB.onErrorHandler(&handleError);
// Set message timeout to 2000ms
  MB.setTimeout(2000);
// Start ModbusRTU background task
  MB.begin();
}

// loop() - cyclically request the data
void loop() {
  static uint32_t next_request = millis();

  // Shall we do another request?
  if (millis() - next_request > READ_INTERVAL) {
    // Yes.
    data_ready = false;
    // Issue the request
    Error err = MB.addRequest(millis(), 1, READ_INPUT_REGISTER, FIRST_REGISTER, NUM_VALUES * 2);
    if (err!=SUCCESS) {
      ModbusError e(err);
      LOG_E("Error creating request: %02X - %s\n", (int)e, (const char *)e);
    }
    // Save current time to check for next cycle
    next_request = millis();
  } else {
    // No, but we may have another response
    if (data_ready) {
      // We do. Print out the data
      Serial.printf("Requested at %8.3fs:\n", request_time / 1000.0);
      for (uint8_t i = 0; i < NUM_VALUES; ++i) {
        Serial.printf("   %04X: %8.3f\n", i * 2 + FIRST_REGISTER, values[i]);
      }
      Serial.printf("----------\n\n");
      data_ready = false;
    }
  }
}

Hallo Miq1,

danke für den Sketch. Habe ihn ausprobiert. Bekomme jedoch die Fehlermeldung

e[0me[1;33m[E] 24774| SDM630_004.ino       [  57] handleError: Error response: E5 - Packet length error

Um die HW auszuschließen, habe ich den Aufbau dupliziert - fliegend - jedoch gleiches Ergebnis. Hoffe nicht, dass es am SDM630 liegt.

Arduino4Fun

E5 ist bei RTU in der Regel ein Timingproblem.

Benutzt du die Arduino-IDE? Dann müsstest Du leider manuell in der Datei Logging.h in der eModbus-Library mal das #define LOG_LEVEL auf LOG_LEVEL_VERBOSE setzen, neu übersetzen, laufen lassen und hier den Output posten. In anderen Entwicklungsumgebungen reicht das Compilerflag -DLOG_LEVEL=6.