Sondenwerte über Modbus RTU abfragen

Hallo Leute!

ich war bisher immer nur ein begeisterter stiller Teilnehmer diese Forums, doch jetzt habe ich auch mal eine Frage, da ich mit den bisher gefunden Threads leider nix anfangen kann bzw. einfach nicht weiterkommen.

Ich habe eine Pegelmesssonde um die Wasserstandswerte meiner Aquarien zu dokumentieren und kontrollieren. (sehr große Aquarien!). Das hat bisher mit Arduino über 4-20mA und dem Adafruit 16bit ADC super geklappt! Jedoch bietet mir die Sonde auch die Möglichkeit die Werte noch genauer über RS485 abzufragen. Das habe ich bereits über nen USB-RS485 Adapter am PC gemacht (Simple Modbus Software) und die Werte kommen einwandfrei!!

Jetzt möchte ich aber die Daten über den Arduino abrufen (ich nutze den MEGA).
Für die RS485 Kommunikation nutze ich ein RS485->TTL Modul: http://www.amazon.de/MAX485-Modul-RS-485-TTL-auf/dp/B00L7IETRE

Das Modbus Kommunikationsprotokoll meiner Sonde: http://www.keller-druck.com/picts/pdf/engl/comm_protocol_e.pdf

Connections auf dem MEGA:
DI -> TX1
RO -> RX1
DE + RE -> PIN 23
VCC -> 5V
GND->GND
A + B entprechend der Sonde.
Sonde läuft mit 12V

Ich habe bereits alle Modbus Master Librarys (SimpleModbusMAster, ModbusRTU, etc) ausprobiert bekomme aber keine Werte, bzw habe keine Ahnung wie ich an die Werte komme. Ich bin ein Arduino-Beginner und habe die Example-Sketches der Librays probiert und die entspr. Modbus befehle für mich abgeändert ....

Der Modbus-Befehl ist klar und wurde erfolgreich am PC getestet:
Slave: 1
Funktion: 3 (0x003) (Read Holding Register)
Start Register: 12 bzw. 0x0011
Register to Read: 2

Am PC bekomme ich eine Response mit den korrekten Werten.

Da die Sonde keine Signallampe o.ä. hat, weiß ich nicht mal ob die Verbindung zur Sonde über den Arduino steht? Hat irgendjemand einen Rat? Oder kann mir mit seinem bereits vorhandenen ModbusRTU Protokoll weiterhelfen??

Ich habe schon die Vermutung dass das RS485 Modul defekt ist und habe mir bereits einen MAX485-Chip bestellt um das mal direkt zu testen....

Jedoch habe ich trotzdem keine Ahnung in welcher Variable der Librarys die Werte versteckt sein könnten....

Für mich ist Arduino und Coding nur ein fantastisches Hobby, also bitte seid nicht zu hart zu mir.... Arbeite zum größten Teil mit fertigen Code-Schnipsel.... Würde aber so gerne die genaueren Werte über den Arduino und RS485 abfragen. Das kann theoretisch doch nicht so ein Akt sein ..... ?

Ich freue mich über jede Hilfe!!!!

Gute Zeit für Alle!!
Manu

Hi Manu
Bitte hänge mal deinen Code in den Thread (Code tags </> nicht vergessen damit es leserlich wird) den du schon mal versucht hast. Ich würde den gerne mal sehen. Wenn die Kommunikation mit dem PC klappt so sollzte sie auch mit dem Arduino funktionieren (soweit der RS485 Wandler läuft) .
Gruß
DerDani

Hi,

zunächst Danke für rasche Antwort.

Wie bereits erwähnt habe ich bisher nur die Example-Sketches getestet. Hier mal der, mit der SimpleModbusMaster Library: https://drive.google.com/folderview?id=0B0B286tJkafVYnBhNGo4N3poQ2c&usp=drive_web&tid=0B0B286tJkafVSENVcU1RQVBfSzg

#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 500 // the scan rate
#define retry_count 10

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

#define LED 9

// 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()
{
  Serial.begin(9600);
  // Initialize each packet
  modbus_construct(&packets[PACKET1], 1, READ_HOLDING_REGISTERS, 12, 2, 0);

  
  // Initialize the Modbus Finite State Machine
  modbus_configure(&Serial1, baud, SERIAL_8N1, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS, regs);
  
  pinMode(LED, OUTPUT);
}

void loop()
{
  modbus_update();

  Serial.println(regs[0]);
  Serial.println(regs[1]);
  Serial.println(regs[2]);
  Serial.println(regs[3]);
  Serial.println(regs[4]);
  Serial.println(regs[5]);
  Serial.println(regs[6]);
  Serial.println(regs[7]);
  Serial.println(regs[8]);
  Serial.println(regs[9]);
  Serial.println(regs[10]);
  Serial.println(regs[11]);
  Serial.println(regs[12]);
  Serial.println(regs[13]);
  Serial.println(regs[14]);
  Serial.println(regs[15]);
  Serial.println(regs[16]);
  Serial.println(regs[17]);
  Serial.println(regs[18]);
  Serial.println(regs[19]);
  Serial.println(regs[20]);
  Serial.println(regs[21]);
  Serial.println(regs[22]);
  Serial.println(regs[23]);
  delay(1000);
  
  //regs[0] = analogRead(0); // update data to be written to arduino slave
  
  //analogWrite(LED, regs[0]>>2); // constrain adc value from the arduino slave to 255
}

Ich ging davon aus dass in den regs der 16Bit-Int Wert geschrieben wurde, deshalb die Ausgabe all dieser (das war meine Abänderung des Codes) ... Jedoch kommen hier wahrlos- meist gleichbleibende Werte raus....
Desweiteren habe ich mal ein Screenshot der Erfolgreichen PC-Übertragung angehängt mit dem entspr. Modbus-Befehl und dessen Response... Der Rückgabewert ist auf der rechten Seite zu sehen -> 16BitInt, 40020, -4 (-4 ist der Wert den ich will.... = -0,04 mBar)
Ich kann auch gerne noch die anderen Sketches der anderen Librarys posten, jedoch ähneln die sich alle sehr...
Danke vorab!!
LG
Manu

In deiner Anfrage fehlen die beiden CRC Bytes die die Sonde erwartet. Die bekommst du dann auch zurück die du dann aus den Daten wieder rausbringen muss. So schreibst du nur die reinen Daten ohne CRC
Gruß
DerDAni

Ich dachte den CRC-Check übernimmt die Library und diese erwartet hier keine Eingabe von mir ... ? Wenn nicht, hört sich das sehr kompliziert zum Umsetzen für mich an... ?
Lg
Manu

Die Software SimplyModbus macht es selber. Aber im Arduino musst du dich darum kümmern. Da es auch verschiedene CRC Brechenungen gibt. Schau mal hier >Klick<
Gruß
DerDani

Danke nochmal für Deine Aufmerksamkeit bzgl meines Problems...
Hier ein Auszug aus der Library (.cpp).... wird hier nicht der CRC Wert errechnet...?

void constructPacket()
{	 
  packet->requests++;
  frame[0] = packet->id;
  frame[1] = packet->function;
  frame[2] = packet->address >> 8; // address Hi
  frame[3] = packet->address & 0xFF; // address Lo
	// For functions 1 & 2 data is the number of points
  // For functions 3, 4 & 16 data is the number of registers
  // For function 15 data is the number of coils
  frame[4] = packet->data >> 8; // MSB
  frame[5] = packet->data & 0xFF; // LSB
  
	
	unsigned char frameSize;    
	
  // construct the frame according to the modbus function  
  if (packet->function == PRESET_MULTIPLE_REGISTERS) 
		frameSize = construct_F16();
	else if (packet->function == FORCE_MULTIPLE_COILS)
		frameSize = construct_F15();
	else // else functions 1,2,3 & 4 is assumed. They all share the exact same request format.
    frameSize = 8; // the request is always 8 bytes in size for the above mentioned functions.
		
	unsigned int crc16 = calculateCRC(frameSize - 2);	
  frame[frameSize - 2] = crc16 >> 8; // split crc into 2 bytes
  frame[frameSize - 1] = crc16 & 0xFF;
  sendPacket(frameSize);

	state = WAITING_FOR_REPLY; // state change
	
	// if broadcast is requested (id == 0) for function 15 or 16 then override 
  // the previous state and force a success since the slave wont respond
	if (packet->id == 0)
			processSuccess();
}

oder hier ...:

unsigned int calculateCRC(unsigned char bufferSize) 
{
  unsigned int temp, temp2, flag;
  temp = 0xFFFF;
  for (unsigned char i = 0; i < bufferSize; i++)
  {
    temp = temp ^ frame[i];
    for (unsigned char j = 1; j <= 8; j++)
    {
      flag = temp & 0x0001;
      temp >>= 1;
      if (flag)
        temp ^= 0xA001;
    }
  }
  // Reverse byte order. 
  temp2 = temp >> 8;
  temp = (temp << 8) | temp2;
  temp &= 0xFFFF;
  // the returned value is already swapped
  // crcLo byte is first & crcHi byte is last
  return temp; 
}

?
LG
Manu

In Mathe gibt es sowas wie eine Formel.
Um diese Formel umzusetzen gibt es Zahlen.
Die Zahlen stellt man an die richtige Position und schon hat man das Ergebnis.
So würde ich es erklären.

Hi,

skorpi ich hoffe nicht dass Du beruflich auf pädagogische Art etwas vermitteln musst... bitte nimm es nicht persönlich: ... Programmierer halt... :slight_smile: Nicht jeder, der programmiertechnisch nicht ganz soviel auf dem Kasten hat, ist auch in Mathe oder logischem Denken eine Niete. Ich respektiere Eure Arbeit als Programmierer und weiß dass Eurer Arbeit und Intension in Euren Codes liegt, und dass man diese schwer erarbeiteten Codes und Fragmente und demnach sein Wissen an Unwissende nicht einfach weitergibt. Trotzdem hat mir Deine "Erklärung" überhaupt nicht geholfen....

Nichts desto trotz kann ich Erfolge vermelden, und möchte dies hier kurz teilen ...

Ich habe nun die 'alte' SimpleModbusMaster Library installiert:

https://code.google.com/p/simple-modbus/

Die CRC-Prüfung übernimmt die Library. Ferner ist der CRC-Check über Modbus standardisiert.

Hier der funktionierende Code (Arduino MEGA):

#include <SimpleModbusMaster.h>



//////////////////// Port information ///////////////////
#define baud 9600
#define timeout 1000
#define polling 50 // the scan rate

// If the packets internal retry register matches
// the set retry count then communication is stopped
// on that packet. To re-enable the packet you must
// set the "connection" variable to true.
#define retry_count 10

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

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

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

// Create a packetPointer to access each packet
// individually. This is not required you can access
// the array explicitly. E.g. packets[PACKET1].id = 2;
// This does become tedious though...
packetPointer packet1 = &packets[PACKET1];

// Data read from the arduino slave will be stored in this array
// if the array is initialized to the packet.
unsigned int readRegs[2];


void setup()
   Serial.begin(9600);
  // read 2 register starting at address 0x0012  
  modbus_construct(packet1, 1, READ_HOLDING_REGISTERS, 0x0012, 2, readRegs);
  modbus_configure(&Serial1, baud, SERIAL_8N1, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS);
  
}

void loop()
{
  modbus_update();
  Serial.print("Wert in mBar:");
  Serial.println((readRegs[1])*0.01);
  delay(500);
}

Ich steure den MAX485 Chip direkt auf dem Breadboard an, und habe am MAX485 zwischen A+B einen 120Ohm Widerstand..

Läuft tadellos!

Danke für Eure Hilfe !!

LG
Manu