Leistungsmesser DDS661 Modbus per Arduino auslesen

Ich möchte einen EARU DDS 661 Leistungsmesser von Ali per Modbus RTU mittels Arduino auslesen. Es ist eher als Spaßprojekt zu verstehen, um etwas zu lernen.

Vorhanden sind:

DDS 661
RS485-USB Dongle
RS485-TTL Adapterplatine ( JZK 5 Stück TTL auf RS485 Modul, 485 auf serielle Schnittstelle, UART-Ebene gegenseitige Konvertierung, automatische Hardware-Durchflusskontrolle, TTL Turn RS485 3,3 V 5 V: Amazon.de: Computer & Zubehör)
Arduino Uno R3

Das Datenblatt des DDS 661 wurde mir recht schnell zugesandt (wenn ich dürfte würde ich es hier hochladen), sodass ich es bisher geschafft habe, den Leistungsmesser mittels RS485-USB Dongle und dem Windows Programm Gmodbus (war in der Bedienung für mich am einfachsten verständlich) auslesen zu können.

Auf

01 04 00 00 00 02 71 CB
(01=Adresse, 04="Befehl", 00 00 = Start Lesen Register 0, 00 02= 2 Worte und 4 Bytes an Daten, 71 CB = Checksumme)

kommt dann zurück

01 04 04 43 68 66 66 C4 56
(01=Adresse, 04="Befehl", 04=4 Bytes an Daten, 43 68 66 66= Daten nach IEEE754

entspricht am Ende des Tages einer Spannung von 232,4 Volt. Passt.

Mit dem Arduino habe ich es bisher nur geschafft, Daten zu senden (überprüft mit dem RS485-USB Dongle) und Modbus Slave als "Listener".
Der Befehl 01 04 00 00 00 02 71 CB wird korrekt auf den Bus gesendet, eine Antwort vom Leistungsmesser kommt aber nicht; wie als wäre der Bus überlastet.
Dann habe ich mit sendendem Arduino und Gmodbus den Befehl gesendet, und bekomme ganz normal etwas vom Leistungsmesser zurück.
Hierbei war auffällig, dass die RX LED des RS485-TTL Moduls kurz leuchtet.

Code anbei - mein RS485-TTL Modul von Amazon hat leider den DE/RE Pin nicht, soll selbst umschalten zwischen senden und empfangen.



#include<ModbusMaster.h>
#include <SoftwareSerial.h>



#define RX_PIN 11
#define TX_PIN 10
//#define MAX485_REDE_PIN 12
SoftwareSerial modbusSerial(RX_PIN, TX_PIN);

ModbusMaster node; //object node for class ModbusMaster

void setup()
{
  Serial.begin(9600); //Baud Rate as 9600
   
  modbusSerial.begin(9600); //Baud Rate as 9600
  node.begin(1, modbusSerial); //Slave ID as 1 on SoftwareSerial
 
}

void loop()
{
  uint8_t result = node.readInputRegisters(0,2);

  if (result == node.ku8MBSuccess)
  {
    Serial.print("data : ");
    Serial.print(node.getResponseBuffer(0));
  }
  Serial.print("\n");

  delay(3000);
}

Vielen Dank im Voraus für Eure Hilfe!

Ihr Thema wurde in den deutschen Bereich des Forums verschoben. Bitte verwenden Sie die englische Sprache in den englischen Kategorien des Forums.

Willkommen im Forum
Ich wurde sagen da fehlt was, schau dir den Beispiel RS485_HalfDuplex aus der Modbus Lib an.
Da zu im Forum gibt es sehr viele Themen über Modbus.

Selbst nutze nur die, Problemlose Module.

gib dir mal den Fehlercode aus:

void loop()
{
  uint8_t result = node.readInputRegisters(0,2);

  if (result == node.ku8MBSuccess)  {
    Serial.print("data : ");
    Serial.print(node.getResponseBuffer(0)); Serial.print(' ');
    Serial.print(node.getResponseBuffer(1)); Serial.println();
  }
  else {
    Serial.print(F(" result=0x")); Serial.println(result, HEX);
  }

  delay(3000);
}

und dann schau nach in der Lib wass der Fehlercode (typische Anfangsfehler sind E0, E1 oder E2) bedeutet.

Wenn du nicht weiterkommst, poste

  • den Serial Output.
  • Bilder von der Verkabelung aus dem man deutlich ALLE Verbindungen zwischen Arduino, Adapter und Leistungsmesser sieht.

DDS661 RS485 English Manual.pdf (54,9 KB)

Danke für die Antworten.
Für alle die suchen habe ich das Modbus-Handbuch des DDS661 hochgeladen, da man dieses nirgendwo findet.

Bild des Aufbaus...zwei Leitungen gehen noch zu dem RS485-USB Dongle.

Fehlercode ist 0xE2.

    /**
    ModbusMaster response timed out exception.
    
    The entire response was not received within the timeout period, 
    ModbusMaster::ku8MBResponseTimeout. 
    
    @ingroup constant
    */
    static const uint8_t ku8MBResponseTimedOut           = 0xE2;

dein Node meldet sich nicht.

  • Ich sehe nicht ob RX /TX richtig angeschlossen sind
  • Ich sehe nicht ob A / B anständig verlötet ist (oder nur in die Lötaugen reingesteckt wurde ...)
  • A - Weiß geht auf ?!? und dann auf ?!?
  • B- Grau geht auf ?!? und dann auf ?!?

RX -> grün -> Pin 11
TX -> rot -> Pin 10

A -> weiss -> wago -> rot -> 24 DDS661
B -> grau -> wago -> schwarz - 23 DDS661

A und B haben Pins eingelötet bekommen, anständig gelötet, trotzdem schief geworden :wink:

Arduino hängt am Rechner, das RS485->USB Dongle am Laptop.
Dieser empfängt folgende, (richtige) Telegramme.

Sende ich mit GModbus das Telegram, so empfange ich immer noch korrekte Daten des Leistungsmessers:

/Edit: Ein Kollege auf der Arbeit meint, dass vielleicht etwas mit den "overhead" Daten nicht passt - bekomme ich mit meinem USB Dongle Rohdaten ausgelesen, oder sind diese schon "beschnitten"?
Was bedeutet Parität?

Es lag tatsächlich an der Parität.
GModbus ist standardmäßig auf "even" gestellt, das Powermeter auch, daher funktionierte die Kommunikation über GModbus.

Arduino SoftwareSerial kann ja lediglich 8N1, also ohne Parität. Daher funktionierte hier die Kommunikation nicht. Augenscheinlich sieht der Befehl von Arduino und GModbus gleich aus, in der Realität aber nicht, da das Paritätsbit bei GModbus mitkommt, beim Arduino Befehl nicht.
Das Powermeter weiß also garnicht, was es tun soll, interpretiert den Befehl nicht richtig.

Nach einigem Probieren gelang es mir, die Parität beim Powermeter zu entfernen, da es beim Arduino über SoftwareSerial ja nicht möglich ist, an der Parität etwas zu ändern. Nun bekomme ich auf einen Tx Befehl sofort ein Rx auf meinem RS485 Wandler.

Ich hoffe, diese Lösung hilft noch einigen, von den Ex02 oder 126er Fehlern liest man ja genug :wink:

bedeutet das, dass du nun den Sensor und in GModbus auf 8N1 umgestellt hast? Wenn ja - kannst du bitte noch abschließend einen Screenshot von den korrekten GModbus Einstellungen posten?

Ja, genau, Sensor und GModbus ist auf 8N1 umgestellt.
Bei GModbus kann man die Einstellung das Feld "Parity" ändern:

(alter Screenshot, hier steht nun "none")

Nun habe ich noch eine Frage, da ich sehr unbewandert beim programmieren bin.

Das Leistungsmessgerät gibt ja Daten im 32-bit 4 byte single-precision floating point aus. Das sieht dann so aus:

Reg 0: 17256

Reg 1: 32768

Wie bekomme ich diese Daten als normale Dezimalzahl umgewandelt?

Danke und viele Grüße
Constantin

void loop() {
uint16_t data[2];
float volt;
float result;
   
        {  
        result = node.readInputRegisters(0,2); //adress 00, 02 words of data
     
        if (result == node.ku8MBSuccess) 
        {
          Serial.println("Success, Received data: ");

           (data[0]=node.getResponseBuffer(0x00));
           (data[1]=node.getResponseBuffer(0x01));
           
           Serial.println(data[0]);
           Serial.println(data[1]);

           //Serial.print("Voltage: ");
           //Serial.print(volt);
           //Serial.println(" Volts");      
        } 
          else {
          Serial.print("Failed, Response Code: ");
          Serial.print(result, HEX);
          Serial.println("");
          delay(5000); 
        }

        delay (2000);
        }   
}

Nimm die HEX raus dann sollte klappen, Versuch kostet nichts

`Serial.print(result, 2);`

Danke für die Antwort.

Was Du ansprichst wird nur im Fehlerfall ausgegeben, der aber hoffentlich nicht mehr eintritt :slight_smile:

so lang du nichts anderes findest, probier mal:

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

  // das hast:
  data[0] = 17256;  // 0x4368
  data[1] = 32768;  // 0x8000

  // so soll es befüllt sein:
  //byte in[4] {0x00, 0x80, 0x68, 0x43 };  // 232.50
  byte in[4];
  in [0] = data[1] & 0xFF;
  in [1] = data[1] >> 8;
  in [2] = data[0] & 0xFF;
  in [3] = data[0] >> 8;

  float out;
  memcpy (&out, in, 4);
  Serial.println(out);
}

void loop() {
}
1 Like

Hallo,
erstmal vielen Dank für die Infos hier. Die kommen für mich genau zur richtigen Zeit.
Weil ich aber Probleme hatte den DDS661 auf Parity None umzustellen und das dann aber doch mit GModbus hin bekommen hab, hier die Lösung auch dafür:


Zur Umstellung braucht es folgende Bytes: 01 10 00 02 00 02 04 40 00 00 00 67 B6
Darin enthalten die Floatpoint Darstellung der Zahl 2. Das ist 4000 0000 und HEX 4000 ist nunmal DEC 16384.

Also gaaaanz einfach.

Beste Grüße aus dem Bayerischen Wald,
Klaus

Ich erlaube mir einfach mal das oben Dargestellte in Codeform zusammen zu fassen. Hier am Beispiel eines Arduino Nano mit dem in post #3 gezeigten MAX485 Modul, bei dem die Anschlüsse WriteEnable und NotReadEnable zu verbinden sind.

#include <ModbusMaster.h>
#include <SoftwareSerial.h>

#define RX_PIN     A1
#define TX_PIN     A0
#define WE_NRE_PIN 4

SoftwareSerial modbusSerial( RX_PIN, TX_PIN );
ModbusMaster   node; //object node for class ModbusMaster

void preTransmission()
{
  digitalWrite( WE_NRE_PIN, 1 );
}

void postTransmission()
{
  digitalWrite( WE_NRE_PIN, 0 );
}

bool readDDS661( int address, float *value, int *error  )
{
  *value = 0.0;
  *error = 0;
  
  uint8_t result = node.readInputRegisters( address, 2 );

  if ( result == node.ku8MBSuccess )
  {
    int16_t data[2];
    byte    bytes[4];
    char    buf[5];
    
    data[0] = node.getResponseBuffer( 0 );
    data[1] = node.getResponseBuffer( 1 );

    sprintf( buf, "%04x", data[0] );
    Serial.print( "data : 0x" ); Serial.print( buf );
    sprintf( buf, "%04x", data[1] );
    Serial.print( " 0x" ); Serial.print( buf ); Serial.println();

    bytes[0] = data[1] & 0xFF;
    bytes[1] = data[1] >> 8;
    bytes[2] = data[0] & 0xFF;
    bytes[3] = data[0] >> 8;
  
    memcpy( value, bytes, 4 );
    Serial.print( "value: " ); Serial.println( *value );

    return true;
  }
  else
  {
    Serial.print( "error: 0x" ); Serial.println( result, HEX );
    *error = result;
    return false;
  }
}

void setup()
{
  pinMode( WE_NRE_PIN, OUTPUT ) ;
  postTransmission();
  
  Serial.begin( 9600 ); //Baud Rate as 9600
   
  modbusSerial.begin( 9600 );     //Baud Rate as 9600
  node.begin( 1, modbusSerial );  //Slave ID as 1 on SoftwareSerial
  
  node.preTransmission( preTransmission );
  node.postTransmission( postTransmission );
}

void loop()
{
  float value;
  int   error;
  
  Serial.println( "_____ Voltage _____" );
  readDDS661( 0x0000, &value, &error );
  Serial.println( "____ Frequency ____" );
  readDDS661( 0x0036, &value, &error );
  Serial.println( "______ Power ______" );
  readDDS661( 0x0012, &value, &error );
  Serial.println( "_____ Energy ______" );
  readDDS661( 0x0100, &value, &error );
  
  delay( 3000 );
}

Nochmal ein Großes Danke an Euch :grinning_face: