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))'
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/).
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.
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!
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.
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.
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.
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.
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
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;
}
}
}
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.