Forum: Mikrocontroller und Digitale Elektronik Finder Zähler mit ModBus auslesen


von Stefan B. (stefanb79)


Angehängte Dateien:

Lesenswert?

Hallo, hab ein Problem mit Modbus, zugegeben Modbus ist nicht mein 
ding.. ich bin eher in der Leistungselektronik zuhause..
Vorhaben: im Endausbau sollen 3 Zähler Type Finder 7M.38.8.400.0212 per 
Modbus mit einem µC ausgelesen werden.
Aktuell spiele ich mit einem Zähler und einem Arduino UNO als test.

Zähler ist eingestellt auf ID 33, 19200Baud, 8N1;

lt. Anleitung sollte in Adresse 30111 und 30112 die Spannung an L3 
auszulesen sein.
Ich habe die Schaltung aufgebaut, lt. Oszi werden Daten auf dem Bus 
gesendet, und der Zähler sollte auch antworten, da ich immer 2 
Datenpakete sehen. Wenn ich die Zähleradresse ändere, ist nur 1 Paket 
sichtbar, ich gehe also davon aus dass der Bus an sich OK sein sollte, 
(Buslänge 30cm).

Ich bekomme jedoch die Spannung nicht angezeigt,

ich verwende folgenden Code:

#include <ModbusMaster.h>    // Modbus Master Library
#include <SoftwareSerial.h>  // SoftwareSerial Library

SoftwareSerial modbusSerial(4, 5);  // RX, TX for Modbus
ModbusMaster node;                  // ModbusMaster Objekt

void setup() {
  Serial.begin(9600);          // Serielle Kommunikation mit der 
Baudrate 9600 für Debugging
  modbusSerial.begin(19200);   // Serielle Kommunikation mit der 
Baudrate 19200 für Modbus
  node.begin(33, modbusSerial);  // ModbusSlave ID und Serielle Objekt 
initialisieren
}

void loop() {
  uint16_t registerValue;
  uint8_t result;

  // Senden einer Modbus-Anfrage an Slave-Adresse 33, Funktion 04, 
Register 30111 (2 Register lesen)
  result = node.readInputRegisters(30111, 2);  // Lesen von 2 
Input-Registern ab Adresse 30111

  if (result == node.ku8MBSuccess) {  // Überprüfen, ob das Lesen 
erfolgreich war
    registerValue = node.getResponseBuffer(0);  // Wert des ersten 
Registers in der Antwort
    Serial.print("Register 30111: ");
    Serial.println(registerValue);
    registerValue = node.getResponseBuffer(1);  // Wert des zweiten 
Registers in der Antwort
    Serial.print("Register 30112: ");
    Serial.println(registerValue);
  } else {
    Serial.println("Fehler beim Lesen der Register!");
  }

  delay(1000);  // Wartezeit zwischen den Anfragen
}


evtl. kann mir jemand helfen, mit Bussystemen bin ich nicht wirklich 
vertraut, wo liegt mein Fehler?

Vielen Dank!

von Rüdiger B. (rbruns)


Lesenswert?

Beide enden mit 120 Ohm Terminiert ?
RS485 Adapter auf Seriell oder USb Vorhanden ?

von Harald K. (kirnbichler)


Lesenswert?

Irgendein Schwachkopf ist mal auf die Idee gekommen, beim 
Modbus-Protokoll in den Registernummern zusätzlich die Art des Zugriffs 
(Input Register, Holding Register, Coil, Input State) zu codieren, und 
dazu zur eigentlichen Registernummer einen willkürlichen Offset von 
entweder 10000, 30000 oder 40000 zu addieren.

Das ist komplett bescheuert, weil es einseits nichts mit dem zu tun hat, 
was der Modbus tatsächlich macht, und weil es andererseits völlig 
sinnlos die Anzahl der verfügbaren Register reduziert.

Tatsächlich aber werden auf dem Modbus je Typ 65536 Register 
unterstützt, die von 0 an aufsteigend durchnumeriert werden.

Betrachtet man Dein Beispiel (findermodbus.png), so werden dort zwei 
Register ausgelesen, beginnend bei 0x6b (107).

Der erläuternde Text in Deinem Bild nennt dieses Register 30107.

Also ändere Dein Programm dahingehend, daß der schwachsinnige 
Pseudo-Offset weggelassen wird:
1
  result = node.readInputRegisters(111, 2);  // Lesen von 2

von Stefan B. (stefanb79)


Lesenswert?

Hallo, danke für die Antworten,

habs getestet mit 111,2 , gleiches Problem..

USB-Adapter hab ich aktuell keinen da,
werd ich vml bestellen müssen..

von Timo N. (tnn85)


Lesenswert?

Du hast aber schon einen RS485-Treiber IC irgendwo in deiner Schaltung?

von Stefan B. (stefanb79)


Lesenswert?

Ja, Treiber IC MAX485 ist vorhanden, Abschlusswiderstände sind 
vorhanden,
Der Bus sollte funktionieren, wenn ich mit den Oszi schauen kommt wenn 
ich die
Adresse 33 anspreche nach dem senden sofort eine Antwort, wenn ich eine 
andere Adresse anspreche, welche es nicht gibt, kommt keine Antwort.

Kann mich leider immer nur abends hinsetzen, denk evtl. liegt es am 
Code..

von Mi N. (msx)


Lesenswert?

Stefan B. schrieb:
> Der Bus sollte funktionieren, wenn ich mit den Oszi schauen kommt wenn
> ich die
> Adresse 33 anspreche nach dem senden sofort eine Antwort,

Man kann sich die Antwort auch selber dekodieren. Was steht denn drin?

von Stefan B. (stefanb79)


Lesenswert?

Ich mache abends ein Bild von den Daten welche auf dem Bus unterwegs 
sind.

von Anselm 6. (anselm68)


Lesenswert?

Frage & Antwort auf dem Bus wären wirklich hilfreich.
senden solltest du: [Modbus_ID][Funktionscode][Registernummer][CRC]

@Harald Dieser Offset hat mich, als ich mich in das Thema eingearbeitet 
habe, wahnsinnig gemacht.

Gruß
Anselm

von Harald K. (kirnbichler)


Lesenswert?

Anselm 6. schrieb:
> Frage & Antwort auf dem Bus wären wirklich hilfreich.

Als Hexdump. Kann man auch ohne Logikanalysator angezeigt bekommen, 
z.B.mit Portmon 
https://learn.microsoft.com/en-us/sysinternals/downloads/portmon

von Stefan B. (stefanb79)


Lesenswert?

Ich habe gerade etwas rumgemessen, dabei viel mir auf:

die Umschaltung lesen/schreiben läuft nicht, DE/RE sind im High, damit 
sollte schreiben möglich sein, jedoch kein lesen.

Mit dem Oszi kann ich auf TX daten sehen welche auch nach dem Max485 
vorhanden sind, auf der Busleitung kommt auch eine Antwort, diese wird 
jedoch nicht am RX weiter zum Arduino gegeben, da DE/RE immer auf high 
sind.

beide Pins sind an Pin2 des Arduinos angeschlossen, jedoch werden diese 
im im Programm nirgends deklariert/initiert,
Ich kenne mich mit Modbus nicht aus, macht die ModBusmaster - LIB die 
Umschaltung schreiben/lesen nicht automatisch im Hintergrund mit?

wie kann ich die Pins zuordnen?

Danke!

von Anselm 6. (anselm68)


Lesenswert?

Welchen Pin du für Rx/Tx nimmst kannst du frei entscheiden.
Du musst schon vor den senden den Port setzen und danach zurück. (Je 
nach Polarität des RE/TE deines RS485 Transceivers.

Anselm

von Stefan B. (stefanb79)


Lesenswert?

Danke für die Antworten,
ich hab jetzt einen Code der läuft und die Spannung aus den beiden 
Registern ausliest,
Fehler waren:
DE/RE-Pin des Max485 funktionierten nicht und auch die Registeradressen
stimmen so nicht wie im Datenblatt angegeben,
Spannung L3 (REG 30111 und 30112) müssen wie oben von Harald K. 
geschrieben um den Wert von jeweils 10000/20000/30000 reduziert werden, 
dann kommen die Wert..

Ich muss mich jetzt noch mit den zusammensetzten der beiden Register zu 
einem Spannungswert beschäftigen, aber soweit läuft der Code aktuell..
1
#include <ModbusMaster.h>    // Modbus Master Library
2
#include <SoftwareSerial.h>  // SoftwareSerial Library
3
4
SoftwareSerial modbusSerial(4, 5);  // RX, TX for Modbus
5
ModbusMaster node;                  // ModbusMaster Objekt
6
7
constexpr uint8_t modbusEnablePin = 2;           // The GPIO used to control the MAX485 TX pin. Set to 255 if you are not using RS485 or a selfsensing adapter
8
9
10
void setup() {
11
12
  pinMode (modbusEnablePin, OUTPUT); //Max485 Steuerpin als Ausgang.
13
14
  Serial.begin(9600);          // Serielle Kommunikation mit der Baudrate 9600 für Debugging
15
  modbusSerial.begin(19200);   // Serielle Kommunikation mit der Baudrate 19200 für Modbus
16
  node.begin(33, modbusSerial);  // ModbusSlave ID und Serielle Objekt initialisieren
17
18
//für die ModbusLib, Steuerung des ModbusEnable Pins
19
  node.preTransmission(preTransmission);
20
  node.postTransmission(postTransmission);
21
}
22
23
// this function will be called before the client transmits data
24
void preTransmission() {
25
  digitalWrite(modbusEnablePin, HIGH);
26
}
27
28
// this function will be called after the transmission
29
void postTransmission()  {
30
  digitalWrite(modbusEnablePin, LOW);
31
}
32
33
34
void loop() {
35
  uint16_t registerValue0;
36
  uint16_t registerValue1;
37
  uint8_t result;
38
39
40
  // Senden einer Modbus-Anfrage an Slave-Adresse 33, Funktion 04, Register 30111 (2 Register lesen)
41
42
43
  result = node.readInputRegisters(111, 2);  // Lesen von 2 Input-Registern ab Adresse 30111
44
45
46
  if (result == node.ku8MBSuccess) {  // Überprüfen, ob das Lesen erfolgreich war
47
    registerValue0 = node.getResponseBuffer(0);  // Wert des ersten Registers in der Antwort
48
    Serial.print("Register 30111: ");
49
    Serial.println(registerValue0);
50
    registerValue1 = node.getResponseBuffer(1);  // Wert des zweiten Registers in der Antwort
51
    Serial.print("Register 30112: ");
52
    Serial.println(registerValue1);
53
54
55
  } else {
56
    Serial.println("Fehler beim Lesen der Register!");
57
  }
58
59
  delay(1000);  // Wartezeit zwischen den Anfragen
60
}

von Harald K. (kirnbichler)


Lesenswert?

Stefan B. schrieb:
> Ich muss mich jetzt noch mit den zusammensetzten der beiden Register zu
> einem Spannungswert beschäftigen,

Welche Werte kommen denn?

von Stefan B. (stefanb79)


Lesenswert?

Reg 30111 : 65280
Reg 30112 : 2205  , diese ändert sich immer etwas da die Netzspannung 
nicht konstant ist. 2205 ergibt 220,5V

von Anselm 6. (anselm68)


Lesenswert?

Register 30111 ist Hex 0xFF00
So einen Wert finde ich immer verdächtig.

Anselm
btw.: 220V ist schon lange her, bist du sicher dass der Wert stimmt?

von Stefan B. (stefanb79)


Lesenswert?

Ja, die 220 Stimmen schon, Zähler ist aktuell an einem Regeltrenntrafo 
angeschlossen, im Display wird auch der gleiche Wert angezeigt , wie im 
Datenstrom, Werte ändern sich auch beide wenn ich die Spannung ändere

von Harald K. (kirnbichler)


Lesenswert?

Mal das Bild im Eröffnungsposting ansehen.

Da steht was von "Datatype T5", was eine Finder-spezifische Erweiterung 
sein dürfte und in den obersten acht Bit codiert ist.

Gibt es mehr Unterlagen zum Zähler?

von Stefan B. (stefanb79)


Lesenswert?

Hallo,

https://cdn.findernet.com/app/uploads/Benutzerhandbuch_Typ_7M38_DE.pdf

hier ist alles was es über den Zähler in Bezug auf ModBus gibt,
Seite 33 Modbus Kommunikation..
Seite 34 ff, alle Register

Seite 53 Entschlüsselung der Datentypen

Anruf bei Finder bleibt erfolglos, zum Thema Modbus kann mir keiner 
etwas
sagen..

evtl. sind ja für jeden Parameter der verarbeitet wird generell 2 
Register reserviert und das höhere Register bleibt dann leer?

von Harald K. (kirnbichler)


Lesenswert?

Na, für T5 steht da das hier:

> Unsigned Measurement (32 bit)
> Decade Exponent(Signed 8 bit)
> Binary Unsigned Value (24 bit)
> Example: 123456*10 –3 stored as FD01 E240 (16)

Reg 30111 : 65280
Reg 30112 : 2205

das ist FF00 089D

FF ist der "decade exponent", das ist -1

00 08 9D ist der 24-Bit Wert (2205), also 2205 * 10E-1 = 220.5

Passt doch alles.

Stefan B. schrieb:
> evtl. sind ja für jeden Parameter der verarbeitet wird generell 2
> Register reserviert und das höhere Register bleibt dann leer?

Nein. In der Tabelle ab S. 34 steht das alles fein säuberlich drin, in 
der Spalte "Data" steht, wie der Wert zu interpretieren ist (für R.30111 
steht da T5), und in den ersten beiden Spalten steht, ob ein oder zwei 
Register gelesen werden müssen.

Bei 30111 steht in der zweiten Spalte 30112, also ist logisch, daß die 
beiden Werte zu kombinieren und dann nach der Typbeschreibung T5 zu 
interpretieren sind.

Ich habe schon deutlich schlechtere Modbus-Dokumentationen gesehen.

von Stefan B. (stefanb79)


Lesenswert?

Ok ich glaub jetzt hab ich verstanden.
REG 111   REG 112
FF00      2205
FF = Exponent-1    00 2205 Messwert
FE = Exponent-2
FD = Exponent -3

Um den eigentlichen Wert zu ermitteln darf ich nicht direkt beide 
Register kpl. verknüpfen, da ich dann ja das FF (beiden höchsten 
stellen) mit verrechne.

von Stefan B. (stefanb79)


Lesenswert?

Dann könnte der Code unten evtl. funktionieren,
kann es aber erst abends testen,
1
float convertRegistersToDecimal(uint16_t reg111, uint16_t reg112) {
2
  // beiden Registerwerte zu einer 32-Bit Ganzzahl kombinieren
3
  uint32_t regValue = ((uint32_t)reg111 << 16) | reg112;
4
  
5
  // Exponenten aus den höheren Bits (Bit 31 bis 24) extrahieren
6
  uint8_t exponent = (regValue >> 24) & 0xFF;
7
  
8
  // Wert aus den niedrigeren Bits (Bit 23 bis 0) auslesen
9
  uint32_t value = regValue & 0xFFFFFF;
10
  
11
  // Exponenten in eine vorzeichenbehaftete Ganzzahl wandeln
12
  int8_t signedExponent = (int8_t)exponent;
13
  
14
  // 24-Bit- Ganzzahl in eine Dezimalzahl wandeln
15
  float decimalValue = (float)value * pow(10, -signedExponent);
16
  
17
  return decimalValue;
18
}

von Rainer W. (rawi)


Lesenswert?


: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.