Arduino – ModBus RS485 RTU Master
Het is mogelijk een arduino is te gebruiken als ModBus master, met ModBus RS485 RTU kan je datalijnen gebruiken tot ca. 1200m, dat is erg interessant!
Wat je nodig hebt is een RS485 naar TTL converter om het differentiele signaal van de RS485 bus om te zetten naar TTL signalen voor de Arduino (en andersom).
Hardware RS485 Module Shield
Schema
Sluit de RS485 Module aan zoals hieronder aangegeven.
Of met een losse RS485 chip:
Scripts
Wat heb je nodig?
1) Arduino Modbus bibliotheek (smarmengol), ik heb na lang zoeken en uitproberen eindelijk een bibliotheek gevonden die goed werkt.
Script
Hieronder een script om een modbus slave uit te lezen, de baudrate, register adres, aantal bytes, modbus functie etc. kunnen in de code aangepast worden.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
#include <ModbusRtu.h> // 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, 2); // this is master and RS-232 or USB-FTDI // This is an structe which contains a query to an slave device modbus_t telegram; unsigned long u32wait; void setup() { Serial.begin(9600); master.begin(9600); // baud-rate at 9600 master.setTimeOut(5000); // if there is no answer in 5000 ms, roll over u32wait = millis() + 2000; 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 = 1; // start address in slave telegram.u16CoilsNo = 1; // 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() + 1000; Serial.print("ontvangen data: "); Serial.println(au16data[0]); } break; } delay(200); } |
Console:
Script met I2C LCD display
Om de bus niet te veel te vervuilen met onnodige data, kan men ook de uitgelezen waarde op een display zetten, hieronder is een voorbeeld i.c.m. een I2C LCD display 16×2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
#include <Wire.h> #include <LiquidCrystal_I2C.h> #include <ModbusRtu.h> // 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, 2); // this is master and RS-232 or USB-FTDI // This is an structe which contains a query to an slave device modbus_t telegram; unsigned long u32wait; // Display object aanmaken, eventueel i2c adres aanpassen! LiquidCrystal_I2C lcd(0x27, 16, 2); // set the LCD address to 0x27 for a 16 chars and 2 line display void setup() { master.begin(9600); // baud-rate at 9600 master.setTimeOut(5000); // if there is no answer in 5000 ms, roll over u32wait = millis() + 2000; u8state = 0; lcd.init(); // initialiseer display lcd.backlight(); // zet verlichting aan } void loop() { switch( u8state ) { case 0: lcd.setCursor(0,0); lcd.print("Status: wacht"); if (millis() > u32wait) u8state++; // wait state break; case 1: lcd.setCursor(0,0); lcd.print("Status: zend"); telegram.u8id = 1; // slave address telegram.u8fct = 4; // function code (this one is registers read) telegram.u16RegAdd = 1; // start address in slave telegram.u16CoilsNo = 1; // 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: lcd.setCursor(0,0); lcd.print("Status: ontvang"); master.poll(); // check incoming messages if (master.getState() == COM_IDLE) { u8state = 0; u32wait = millis() + 1000; lcd.setCursor(0,1); // positie 0, regel 1 lcd.print(au16data[0]); } break; } delay(200); } |
Script met softwareserial
Het script hieronder gebruikt de softwareserial om te communiceren met een modbus apparaat en zet de uitslag neer in de console (hardware serial)
(dit is uit het voorbeeldscript genomen, maar het werkt (nog) niet, als iemand weet waar het aan ligt laat het aub weten)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
#include <ModbusRtu.h> #include <SoftwareSerial.h> // data array for modbus network sharing uint16_t au16data[16]; uint8_t u8state; SoftwareSerial mySerial(4, 3); // RX, TX /** * 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, 2); // this is master and RS-232 or USB-FTDI // This is an structe which contains a query to an slave device modbus_t telegram; unsigned long u32wait; void setup() { Serial.begin(9600); master.begin(&mySerial, 9600); // baud-rate at 9600 master.setTimeOut(5000); // if there is no answer in 5000 ms, roll over u32wait = millis() + 2000; 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 = 2; // start address in slave telegram.u16CoilsNo = 1; // 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() + 1000; Serial.print("ontvangen data: "); Serial.println(au16data[0]); } break; } delay(200); } |