Arduino – ModBus RS485 RTU Slave
Het is mogelijk een arduino is te gebruiken als ModBus slave, 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:
Ps. Op de ModBus A/B pinnen kan je een RS485 USB stick aansluiten en deze via een Raspberry Pi aansturen (ModBus master)
Scripts
Wat heb je nodig?
1) Arduino Modbus bibliotheek (smarmengol), ik heb na lang zoeken en uitproberen eindelijk een bibliotheek gevonden die goed werkt
Met een RS485 USB stick op de Raspberry Pi als Modbus master en een python script kan je de waarden uitlezen.
2) Python pymodbus bibliotheek
Gebruik Modbus Functie 4 (Read Input Registers) om de data uit te lezen
Testscript met ModBus data array
Dit testscript stelt een slave in op adres 12Â met 9600 baud en vult register 0 t/m 15 met een getallenreeks:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include <ModbusRtu.h> #define TXEN 2 // Arduino pin aangesloten op de RE-DE pinnen van de RS485 transceiver (XEN >2 dan RS-485) #define SLAVEID 12 // ModBus Slave adres. #define BAUDRATE 9600 // ModBus Slave baudrate. // Modbus register data array (start register = 0) uint16_t au16data[16] = {3, 14, 9265, 4, 2, 7182, 28182, 8, 0, 0, 0, 0, 0, 0, 1, 28}; // 16 registers /** * 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 slave(SLAVEID, 0, TXEN); // Stel slave ID en protocol in void setup() { slave.begin(BAUDRATE, SERIAL_8N1); // Begin slave met baudrate en poortinstellingen } void loop() { slave.poll(au16data, 16); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#!/usr/bin/env python from pymodbus.client.sync import ModbusSerialClient client = ModbusSerialClient(method="rtu", port="/dev/ttyUSB0", stopbits=1, bytesize=8, parity="N", baudrate=9600, timeout=1, retries=2) data = client.read_input_registers(0, 4, unit=12) # FUNCTIE 04 - Lees register (start=0, lengte=4, unitid=12) client.close() print data print "waarde_0:", data.registers[0] print "waarde_1:", data.registers[1] print "waarde_2:", data.registers[2] print "waarde_3:", data.registers[3] |
Het resultaat:
1 2 3 4 5 |
ReadRegisterResponse (4) waarde_0: 3 waarde_1: 14 waarde_2: 9265 waarde_3: 4 |
Script Analoge ingang uitlezen
Hieronder een voorbeeld om 2 analoge ingangen van een arduino in de modbus data array te stoppen.
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 |
#include <ModbusRtu.h> #define TXEN 2 // Arduino pin aangeloten op de RE-DE pinnen van de RS485 transceiver (XEN >2 dan RS-485) #define SLAVEID 12 // ModBus Slave adres. #define BAUDRATE 9600 // ModBus Slave baudrate. // Modbus register data array (start register = 0) uint16_t au16data[2] = {0, 0}; // 2 registers /** * 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 slave(SLAVEID, 0, TXEN); // Stel slave ID en protocol in void setup() { slave.begin(BAUDRATE, SERIAL_8N1); // Begin slave met baudrate en poortinstellingen } void loop() { au16data[0] = analogRead(0); au16data[1] = analogRead(1); slave.poll(au16data, 2); delay(100); } |
1 2 3 4 5 6 7 8 9 10 11 |
#!/usr/bin/env python from pymodbus.client.sync import ModbusSerialClient client = ModbusSerialClient(method="rtu", port="/dev/ttyUSB0", stopbits=1, bytesize=8, parity="N", baudrate=9600, timeout=1, retries=2) data = client.read_input_registers(0, 2, unit=12) # FUNCTIE 04 - Lees register (start=0, lengte=2, unitid=12) client.close() print data print "waarde_0:", data.registers[0] print "waarde_1:", data.registers[1] |
Het resultaat (A0 is hoog gemaakt):
1 2 3 |
ReadRegisterResponse (2) waarde_0: 1023 waarde_1: 223 |
Script Arduino LED schakelen
Met onderstaand voorbeeld kan je met modbus functie 6 (Write Register) data wegschrijven naar de ModBus array op de arduino, daarmee kan je bijvoorbeeld een LED schakelen.
Schema:
Schema met de MAX485 chip:
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 |
#include <ModbusRtu.h> #define TXEN 2 // Arduino pin aangeloten op de RE-DE pinnen van de RS485 transceiver (XEN >2 dan RS-485) #define SLAVEID 12 // ModBus Slave adres. #define BAUDRATE 9600 // ModBus Slave baudrate. #define LED 8 // Arduino pin waar de LED op aangesloten is. // Modbus register data array (start register = 0) uint16_t au16data[2] = {0, 0}; // 2 registers /** * 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 slave(SLAVEID, 0, TXEN); // Stel slave ID en protocol in void setup() { pinMode(LED, OUTPUT); slave.begin(BAUDRATE, SERIAL_8N1); // Begin slave met baudrate en poortinstellingen } void loop() { digitalWrite(LED, au16data[1]); slave.poll(au16data, 2); delay(100); } |
Script met “andresarmento” library:
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 |
#include <Modbus.h> #include <ModbusSerial.h> // Modbus Registers Offsets (0-9999) const int LAMP_ADRES = 1; // Used Pins const int ledPin = 13; ModbusSerial mb; // ModbusSerial object void setup() { mb.config(&Serial, 9600, SERIAL_8N1, 2); // Config Modbus Serial (port, speed, byte format) mb.setSlaveId(12); // Set the Slave ID (1-247) // Set ledPin mode pinMode(ledPin, OUTPUT); // Add LAMP_ADRES register mb.addHreg(LAMP_ADRES); } void loop() { // Call once inside loop() - all magic here mb.task(); // Attach ledPin to LAMP_ADRES register digitalWrite(ledPin, mb.Hreg(LAMP_ADRES)); } |
1 2 3 4 5 6 7 8 9 10 |
#!/usr/bin/env python from pymodbus.client.sync import ModbusSerialClient client = ModbusSerialClient(method="rtu", port="/dev/ttyUSB0", stopbits=1, bytesize=8, parity="N", baudrate=9600, timeout=1, retries=2) data = client.write_register(1, 1, unit=12) # LED AAN (FUNCTIE 06 - Schrijven (enkele register)) #data = client.write_register(1, 0, unit=12) # LED UIT (FUNCTIE 06 - Schrijven (enkele register)) client.close() print data |
Resultaat console (LED aan):
WriteRegisterResponse 1 => 1