Modbus Differenzdruck Sensor auslesen

Hallo alle zusammen,

ich habe durch eine Firmenauflösung einen Differenzdrucksensor der Firma Fischer bekommen, mit diesem würde ich gerne Windgeschwindigkeiten in einem Zuluftkanal bei mir regeln. Wenn das ganze so mal funktioniert würde ich gerne ein Instructable zum Reglerentwurf + eigener Visual Studio GUI mit Arduino machen. Leider ist dieser Sensor nur MODBUS-RTU-RS485 fähig. Ihr ahnt damit hatte ich noch nie zu tun :).

Ich verwende einen Arduino UNO mit einem breakout board max485
http://www.dx.com/de/p/ttl-to-rs485-module-for-arduino-green-163849?tc=EUR&gclid=CLW7q_3EjdMCFScz0wodDuEAow#.WOUA1E1MQkg

Die Verkabelung hierzu:
Arduino <-> max485
0RX <-> RO
1TX <-> DI
2 <-> DE+RE
5V <-> VCC
GND<-> GND

Sensor <-> max485
D1 <-> A (Hier meine erste Unsicherheit, ob A auf D1 gehört hab den Hersteller mal angefragt)
D0 <-> B
Bus-R <-> mit allen GND Potentialen verbunden
Bus-R ist so eine Art 3. Busanschluss um alles auf ein gemeinsames Potential zu legen. (Hoffe das ist so in Ordnung)

Der Sensor ist bezüglich der Baudrate, Adresse und Paritäts und Stopbits mit einem DIP-Schalter zu konfigurieren

Hier habe folgendes eingestellt:
Baudrate:9600
Adresse : 24
Parität : keine
Stopbits : 1 Stopbit

Das Modbus Telegramm setzt sich dabei aus
1 Byte SlaveAdresse + 1 Byte Funktionscode + 0...252 Byte Daten + 2 Byte CRC Prüfwert zusammen

Als Libary würde ich gerne die SimpleModbusMaster.h benutzen (bin für anderes jedoch offen)

Ich habe hier mal den Beispielcode angefangen habe aber Probleme damit wie ich das Telegramm zusammenbaue. Nach Anleitung Seite 6 Kapitel 5.3.5 (siehe ganz unten Link zur Anleitung) enthält der Funktionscode "0x04 Read Input Registers" meine Druckwerte.

Jetzt frage ich mal die Modbus-Spezialisiten unter euch.
Nach Modbus Function Code 04 | Read Input Registers | Simply Modbus Software würde sich mein Telegramm um den ersten Druckmesswert Kanal 1 auszulesen doch folgendermaßen zusammensetzen : 18 04 0000 0001 CRC

Mein Gedankengang hierzu:
18 HEX entspricht als Dezimal 24 (meine Slave Adresse)
04 Funktionscode 0x04
0000 Adresse für Druckmesswert Kanal 1 ( Hier verstehe ich das Beispiel nicht ganz, also unsicher)
0001 Register 1 für Druckmesswert Kanal 1 ( Sollte stimmen oder? will ja nur ein Register auslesen)
CRC wird hoffentlich von der Libary automatisch berechnet weis hierzu jemand was?

So und zu meinem bisherigen Code der mir nur Schwachsinn liefert:

Wäre Cool wenn wer helfen könnte. Ich weis Modbusprobleme sind nix allzu neues hier aber die Topics hier beziehen sich meist auf Modbus Kommunikation Arduino zu Arduino.
Ich weis das ist viel was ich hier schreibe, aber ich hoffe es findet sich doch der ein oder andere der so etwas schon einmal gemacht hat und helfen kann, da es ja nicht ganz eigennützig sein soll.

Gruß und vielen Dank schon mal

Zaragesh

#include <SimpleModbusMaster.h>

/*
   The example will use packet1 to read a register from address 0 (the adc ch0 value)
   from the arduino slave (id=1). It will then use this value to adjust the brightness
   of an led on pin 9 using PWM.
   It will then use packet2 to write a register (its own adc ch0 value) to address 1 
   on the arduino slave (id=1) adjusting the brightness of an led on pin 9 using PWM.
*/

//////////////////// Port information ///////////////////
#define baud 9600
#define timeout 1000
#define polling 200 // the scan rate
#define retry_count 10

// used to toggle the receive/transmit pin on the driver
#define TxEnablePin 2 


// The total amount of available memory on the master to store data
#define TOTAL_NO_OF_REGISTERS 1

// This is the easiest way to create new packets
// Add as many as you want. TOTAL_NO_OF_PACKETS
// is automatically updated.
enum
{
  PACKET1,
 // PACKET2,
  TOTAL_NO_OF_PACKETS // leave this last entry
};

// Create an array of Packets to be configured
Packet packets[TOTAL_NO_OF_PACKETS];

// Masters register array
unsigned int regs[TOTAL_NO_OF_REGISTERS];

void setup()
{
  // Initialize each packet 
  // erste 1 Steht für mein Register nach Anleitung 1 
  modbus_construct(&packets[PACKET1], 1, READ_INPUT_REGISTERS, 3, 1,18); // 3= Read Input Resgisters // 1 = ehrlich gesagt keine Ahnung hier //18 = Meine Slave Adresse in HEX 

  
  // Initialize the Modbus Finite State Machine
  modbus_configure(&Serial, baud, SERIAL_8N1, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS, regs); // SERIAL_8N1 = Keine parität und 1 Stopbit
  
 
  Serial.begin(9600);
}

void loop()
{
  modbus_update();
  // Soll mir die antwort ausgeben welche in regs geschrieben wird denke ich mal 
 Serial.print(regs[0]);
 Serial.print(regs[1]);
 Serial.print(regs[2]);
 Serial.print(regs[3]);
 Serial.print(regs[4]);
 Serial.print(regs[5]);
 Serial.print(regs[6]);
 Serial.print(regs[7]);
 Serial.print(regs[8]);
 Serial.println(regs[9]);
 
}

Bedienungsanleitung Sensor mit BUS Beschreibung: http://www.fischermesstechnik.de/sites/default/files/BA_DE_DE43.pdf
Beschreibung der Simple Master Bibliothek: https://drive.google.com/drive/folders/0B0B286tJkafVYnBhNGo4N3poQ2c

Zaragesh:
Die Verkabelung hierzu:
Arduino <-> max485
0RX <-> RO
1TX <-> DI
2 <-> DE+RE
5V <-> VCC
GND<-> GND

Da habe ich eine schlechte Nachricht für Dich: Der UNO hat genau eine serielle Schnittstelle (I2C und SPI helfen hier nicht), die auch noch von USB belegt ist. Wenn Du also den seriellen Monitor benutzen möchtest, sind D0 und D1 tabu. Ein Mega2560 mit vier von den Schnittstellen wäre die bessere Wahl.

Wenn Du mit dem UNO weitermachen willst, könntest Du die Programmbibliothek SoftwareSerial probieren.

Hey,

danke da hatte ich wohl einen Denkfehler. Vorallem benutzt meine GUI ja auch die Schnittstelle. Also ich werde um einen Mega nicht herumkommen.

Was auch noch ganz wichtig ist schaue mal hier.
Wenn du von Input Register sprichst muss du die Register Adresse 30001 (1stes Input Register) nicht die 1
Schaue mal hier HIER
Und dieses Modbus funktionier besser
Hier diese habe ich schon mehrfach im Einsatz mit einer Kommuniktaion mit HMI Systeme von Siemens und Kinco.
Gruß
DerDANI

vor allem im zweiten Link ist der Code ziemlich selbst erklärend

Zaragesh:
Also ich werde um einen Mega nicht herumkommen.

Nunja, es gibt Alternativen: Der Arduino MICRO hat D0 und D1 frei, ist eine Spezialität sozusagen. Wenn der Arduino nur Daten durchzureichen braucht, könnte daher ein Micro reichen.

Schonmal vielen Dank euch beide das ihr euch dafür die Zeit nehmt gelle :slight_smile:

@volvodani

Hey danke schon mal für den Code, das sieht echt ziemlich selbst erklärend aus, wo ich was reinschreiben muss. Hab das jetzt mal so übernommen das Beispiel, bekomme aber immer noch keine Antwort so wirklich :-/
Was mir jetzt noch nicht ganz klar ist, wo sage ich dem Beispiel wie mein Modbus aussieht bezüglich Parität und Stopbits? Ich hatte ja keine Parität und 1 Stopbit. au16data ist ja ein Array was meine Werte enthält korrekt? Ich habe die Telegrammbildung unten mal mitgepostet, eventuell habe ich da einen Fehler gemacht.
Ich hab jetzt mal ein Success und ein Fail print eingebaut um zu sehen in welchem Case ich mich befinde. Bis jetzt immer nur im Case 2, daher nehme ich mal an das ich irgendwo nen Wurm im Telegramm drinnen hab.

Ich hab in den Anhang mal die Angabe zum Funktionscode gesteckt.

@agmue

Ja das wäre auch eine Lösung, die ich dann auch mal probieren werde. Ich sehe gerade das der Arduino Micro dem Arduino bezüglich Flashspeicher und SRAM in nichts nachsteht. Naja wollte schon immer mal den Mega ausprobieren ... :slight_smile:

#include <ModbusRtu.h>

 // data array for modbus network sharing 
 uint16_t au16data[16]; 
 uint8_t u8state; 

 Modbus master(0,0,0); // this is master and RS-232 or USB-FTDI 
 
 modbus_t telegram; 

 
 unsigned long u32wait; 

 
 void setup() { 
   master.begin( 9600); // baud-rate at 19200 
   master.setTimeOut( 2000 ); // if there is no answer in 2000 ms, roll over 
   u32wait = millis() + 1000; 
   u8state = 0;  
   Serial.begin(9600);
 } 
 
 
 void loop() { 
   switch( u8state ) { 
   case 0:  
     if (millis() > u32wait) u8state++; // wait state 
     break; 
   case 1:  
     telegram.u8id = 18; // slave address eigentlich 24 aber in HEX 18
     telegram.u8fct = 30001; // function code (this one is registers read)  so wie in deinem Link gezeigt
     telegram.u16RegAdd = 0000; // start address in slave  Adresse = 0 für Kanal 1 siehe Bild anhang
     telegram.u16CoilsNo = 0001; // number of elements (coils or registers) to read = 0001 weil ich ja nur einen Wert auslesen will
    telegram.au16reg = au16data; // pointer to a memory array in the Arduino 
 
 
     master.query( telegram ); // send query (only once) 
    u8state++; 
     break; 
Serial.print(au16data[0]);
Serial.print(au16data[1]);
Serial.print(au16data[2]);
Serial.print(au16data[3]);
Serial.print(au16data[4]);
Serial.print(au16data[5]);
Serial.print(au16data[6]);
Serial.println(au16data[7]);
Serial.print("Success");

   case 2: 
    master.poll(); // check incoming messages 
     if (master.getState() == COM_IDLE) { 
       u8state = 0; 
       u32wait = millis() + 100;  
       Serial.print("FAIL");  
     } 
    break; 
   } 
 }

Hier habe ich mal meine Modbus Konfig mit "Radzio! Modbus Master Simulator" und einem Modbus - Serial Wandler getestet. Also hier funktioniert der Sensor...
Jedoch bekomme ich mit meinem momentanen Code nichts brauchbares und der Sensor reagiert auch nicht auf die Arduino Master Anfrage mit dem momentanen Code (Kein Blinken der Sensor Status LED).

Aber bin mir ziemlich sicher das ich Mist mit den Adressen gebaut habe :-/

Wie hast du das getestet, das es mit Arduino nicht funktionier?
Kannst du mal deinen Arduino-Code zeigen?
Du kannst nicht gleichzeitig Modbus via UART und Serial Monitor machen. Und per Software Serial kannst du vergessen weil das Timing im Modbus sehr wichtig ist.
Gruß
DerDani

Ich schaue mal nach dem Code ob ich was "finde"

So ich habe mal das Beispiel "anpasst"

/**
 *  Modbus master example 1:
 *  The purpose of this example is to query an array of data
 *  from an external Modbus slave device. 
 *  The link media can be USB or RS232.
 *
 *  Recommended Modbus slave: 
 *  diagslave http://www.modbusdriver.com/diagslave.html
 *
 *  In a Linux box, run 
 *  "./diagslave /dev/ttyUSB0 -b 19200 -d 8 -s 1 -p none -m rtu -a 1"
 *   This is:
 *    serial port /dev/ttyUSB0 at 19200 baud 8N1
 *    RTU mode and address @1
 */

#include <ModbusRtu.h>
#define RXTXPin 7 // Hier der Pin vom RX485 zur Umschaltnung Senden Empfangen

// data array for modbus network sharing
uint16_t au16data[16];
uint8_t u8state;


/**
 *  Modbus object declaration
 *  u8id : node id = 0 for master, = 1..247 for slave
 *  u8serno : serial port (use 0 for Serial)
 *  u8txenpin : 0 for RS-232 and USB-FTDI 
 *               or any pin number > 1 for RS-485
 */
Modbus master(0,0,RXTXPin); // this is master and RS-485 und Pin zum Umschalten

/**
 * This is an structe which contains a query to an slave device
 */
modbus_t telegram;

unsigned long u32wait;

void setup() {
  master.begin( 9600 ); // baud-rate at 9600
  master.setTimeOut( 2000 ); // if there is no answer in 2000 ms, roll over
  u32wait = millis() + 1000;
  u8state = 0; 
}

void loop() {
  switch( u8state ) {
  case 0: 
    if (millis() > u32wait) u8state++; // wait state
    break;
  case 1: 
    telegram.u8id = 24; // slave address
    telegram.u8fct = 4; // function code (this one is registers read)
    telegram.u16RegAdd = 1; // start address in slave
    telegram.u16CoilsNo = 3; // number of elements (coils or registers) to read
    telegram.au16reg = au16data; // pointer to a memory array in the Arduino

    master.query( telegram ); // send query (only once)
    u8state++;
    break;
  case 2:
    master.poll(); // check incoming messages
    if (master.getState() == COM_IDLE) {
      u8state = 0;
      u32wait = millis() + 100; 
    }
    break;
  }
}

Ich konnte aus deinem Code nicht entnehmen welchen Pin die Umschaltung am RS485 "Wandler"macht .

Am Ende sollte in dem Array
au16data[0]= Register 1 stehen
au16data[1]= Register 2 stehen
au16data[2]= Register 3 stehen.

Hoffe geholfen zu haben. Keine Garantie ob das so funktioniert.
In den Beispielen habe ich gesehen das es auch ein Software Seriell vorhanden ist aber ich habe keine Ahnng ob es läuft. Bei meinen HMI Panels war es auch mit 9600baud kaum Möglich was stabiles hinzubekommen erst nach Manipulation von Timingwerten am HMI.

Gruß
DerDani

Also das ist mein momentaner Code, mein alter hatte keine Umschaltung für RX da hast du völlig recht.
Der Code funktioniert an und für sich schon mal zur Hälfte sage ich mal. Der Arduino sendet eine Anfrage und der Sensor antwortet, das entnehme ich zumindest dem Blinken der TX LED am Arduino und der Status LED am Sensor.
Zwecks der angesprochenen Baudrate von dir, da bin ich flexibel sowohl was die GUI betrifft als auch der Sensor selbst. Also von 1200 - 57600 ist alles drin und lässt sich durchprobieren.
Das Umschalten scheint immer noch nicht zu funktionieren, da die RX LED am Arduino dunkel bleibt :sweat_smile: ...
Naja werd mal bissl rumprobieren. Hab jetzt PIN 7 auf DE+RE am max485 gelegt.

/**
 *  Modbus master example 1:
 *  The purpose of this example is to query an array of data
 *  from an external Modbus slave device. 
 *  The link media can be USB or RS232.
 *
 *  Recommended Modbus slave: 
 *  diagslave http://www.modbusdriver.com/diagslave.html
 *
 *  In a Linux box, run 
 *  "./diagslave /dev/ttyUSB0 -b 19200 -d 8 -s 1 -p none -m rtu -a 1"
 *   This is:
 *    serial port /dev/ttyUSB0 at 19200 baud 8N1
 *    RTU mode and address @1
 */

#include <ModbusRtu.h>
#define RXTXPin 7 // Hier der Pin vom RX485 zur Umschaltnung Senden Empfangen

// data array for modbus network sharing
uint16_t au16data[16];
uint8_t u8state;


/**
 *  Modbus object declaration
 *  u8id : node id = 0 for master, = 1..247 for slave
 *  u8serno : serial port (use 0 for Serial)
 *  u8txenpin : 0 for RS-232 and USB-FTDI 
 *               or any pin number > 1 for RS-485
 */
Modbus master(0,0,RXTXPin); // this is master and RS-485 und Pin zum Umschalten

/**
 * This is an structe which contains a query to an slave device
 */
modbus_t telegram;

unsigned long u32wait;

void setup() {
  master.begin( 9600 ); // baud-rate at 9600
  master.setTimeOut( 2000 ); // if there is no answer in 2000 ms, roll over
  u32wait = millis() + 1000;
  u8state = 0; 
}

void loop() {
  switch( u8state ) {
  case 0: 
    if (millis() > u32wait) u8state++; // wait state
    break;
  case 1: 
    telegram.u8id = 1; // slave address
    telegram.u8fct = 4; // function code (this one is registers read)
    telegram.u16RegAdd = 30001; // start address in slave
    telegram.u16CoilsNo = 3; // number of elements (coils or registers) to read
    telegram.au16reg = au16data; // pointer to a memory array in the Arduino

    master.query( telegram ); // send query (only once)
    u8state++;
    break;
  case 2:
    master.poll(); // check incoming messages
    if (master.getState() == COM_IDLE) {
      u8state = 0;
      u32wait = millis() + 100; 
    }
    break;
  }
}

Du musst die Slave Adresse noch anpassen. da steht noch die 1 drin
Gruß
DerDani

Ja das passt, ich habe die Slave Adresse von 24 auf 1 geändert. Keine Ahnung wieso, aber das hat irgendwie sonst Mucken gemacht. Hast du ne Idee zwecks der dunkel bleibenden RX LED? Ich hätte jetzt als nächstes auf meinen Mega gewartet und mir dort mal die Werte von
au16data[0]= Register 1 stehen
au16data[1]= Register 2 stehen
au16data[2]= Register 3 stehen
auf den Seriellen Monitor anzeigen lassen, ich vermute mal ohne leuchtendes RX wird das aber wenig Veränderung nach sich ziehen, oder was meinst du?.

Ich hab mal in

Modbus master(0,0,RXTXPin); // this is master and RS-485 und Pin zum Umschalten

versucht Änderungen vorzunehmen, da mir hier die doppelte Belegung der 0 etwas seltsam vorkam. Dachte das die erste 0 für die Zuweisung von RX wäre und die zweite 0 für die Zuweisung von TX.
Aber auch hier ohne Ergebnis. Ich hab mit dem Oszi jetzt mal diesen PIN 4 überprüft. An und für sich habe ich hier ein Signal. Warum der Arduino also nicht empfängt weis ich so noch nicht. Ich denke ich kann es aber schon mal gut auf den Code eingrenzen (Mit USB-RS485 funktioniert das ganze am Computer ja wunderbar).

Zaragesh:
Ich hab mal in

Modbus master(0,0,RXTXPin); // this is master and RS-485 und Pin zum Umschalten

versucht Änderungen vorzunehmen, da mir hier die doppelte Belegung der 0 etwas seltsam vorkam. Dachte das die erste 0 für die Zuweisung von RX wäre und die zweite 0 für die Zuweisung von TX.

Die Anleitung steht in Deinem Code (ich habe eine Zeile ergänzt):

/**
 *  Modbus object declaration
 *  u8id : node id = 0 for master, = 1..247 for slave
 *  u8serno : serial port (use 0 for Serial)
 *  u8txenpin : 0 for RS-232 and USB-FTDI 
 *               or any pin number > 1 for RS-485
 * Modbus master(u8id,u8serno,u8txenpin);
 */
Modbus master(0,0,RXTXPin); // this is master and RS-485 und Pin zum Umschalten