====== Arduino - Modbus TCP/RTU ====== ===== Modbus kommunikáció ===== A Modbus protokoll kialakulása szorosan összefonódott az első PLC születésével. Az első PLC, a "Modicon 084" 1969-ben kezdte meg első ciklusait, és rá egy évre, létrehozták a Modbus-t. Nyilván azóta jelentős változásokon esett át a protokoll, de napjainkig megmaradt egy **De-facto-standard**-nak, azaz, igen széles körben alkalmazott kommunikációs rendszernek. A Modbus két hordozóközegen bukkan fel a leggyakrabban, az RS485-ön (RTU) és az etherneten (TCP). A Modbus magasabb szintű műveletei mindkét esetben megegyeznek, azaz lehet vegyes hálózatokat képezni a protokollal, ahol az átvitel RTU-bval és TCP-vel is megvalósul. {{ wiki:comm:modbus1.png |Modbus communication}} A Modbus egy monomaster hálózatot vagy pont-pont kapcsolatot feltételez. Mindkét esetben egy master és legalább egy slave szükséges a kommunikációhoz. A Modbus RTU csak egy master egység jelenlétét teszi lehetővé. ===== Modbus master / slave ===== Fontos megjegyezni, hogy sok más kommunikációs technológiával szemben az adatokat a slave egységek tárolják (jellemzően regiszteres formában) és a master jogosult ezeket az adattartalmakat lekérdezni. Más közelítésben azt mondhatjuk, hogy **a Modbus esetén a slave a szerver**. Az Arduino-k ebben a hálózatban mint slave, mint master szerepet is betölthetnek: {{ :hu:arduino:modbus_master_slave.png |Modbus master / slave}} ===== Multimaster ===== A Modbus hálózaton egyszerre több master is jelen lehet (multimaster), de ez esetben ún. multimaster-gateway-t kell beiktatnunk a kummunikációs hálózatba, és a masterek csak Modbus TCP-re lehetnek felfűzve: {{ :hu:arduino:modbus_multimaster.png |Modbus Multimaster}}  A TCP esetén az RTU-t telepítették a TCP-re, így az ethernetes hálózatok összes előnyét sikerült beágyazni a Modbusba. Mivel az etherneten az állomások címe nagyságrendekkel magasabb, mint az RTU/ASCII-nél, így ennek a korlátját a Modbus-ban definiált 1 bájtos címe jelenti (itt is marad az 1..247 korlát). ===== Modbus funkciókódok ===== ^Function Code^Register Type| |1|Read Coil| |2|Read Discrete Input| |3|Read Holding Registers| |4|Read Input Registers| |5|Write Single Coil| |6|Write Single Holding Register| |15|Write Multiple Coils| |16|Write Multiple Holding Registers| ====== Modbus RTU ====== Az RS-485 technikai jellemzői: ^ ^RS-485| ^Működési mód|[[hu:comm:start#szinkron|szinkron átvitel]]| ^Meghajtók és vevők\\ száma egy vonalon|32 állomás szegmensenként| ^Adatátvitel módja|[[hu:comm:start#half_duplex|félduplex]] / [[hu:comm:start#duplex|duplex]]| ^Adatátvitel|[[hu:comm:start#multipoint|multipoint]]| ^Max. kábelhosszúság|1200 m| ^Max. adatátvitel\\ 12 m\\ 1200 m|\\ 35 Mbps\\ 100 kbps| ^Max. jelváltozási\\ sebesség (slew rate)|n.a.| ^Vevő bemeneti\\ ellenállás|≧ 12 kΩ| ^Meghajtó terhelés-\\ impedancia|54 Ω| ^Vevő "holtsáv"|±200 mV| ^Vevő feszültségszint|--7..12 V| ^Meghajtó kimenő\\ feszültség max.|--7..12 V| ^Meghajtó kimenő\\ feszültség min. (terheléssel)|±1.5 V| ^Meghajtó kimeneti\\ rövidzárási áram limit|150 mA tól Test felé\\ 250 mA Vcc felé| ^Vevő hiszterézis|50 mV| Az RS-485 egy szimmetrikus átviteli mód. Az EIA-485 megnevezés azonos az RS-485 standarddal. A 32 egység / szegmens elvi határon belül az adó és vevő egységek száma szabadon variálható (multipoint). A maximum 32 egység / szegmens határ az előre definiált meghajtó terhelés (Unit Load [**UL**]) mellett érvényes, ami az RS-485 esetében 12 kΩ. Az egységek száma emelhető, ha a meghajtó terhelés csökken. Jellemzően ezt - az UL-t - a negyedére (48 kΩ) vagy nyolcadára (96 kΩ) szokás csökkenteni, így az állomások száma rendre 128-ra, vagy 256-ra emelhető. Hálózati erősítővel (repeaterrel) az állomások száma szintén emelhető. Az RS-485 120 Ω vonalimpedanciát tételez fel a vezetéktől. A szegmens két végét 680 Ω-os 120W-os (10%, 1/2 watt) véglezárókkal szükséges zárni. === RTU/ASCII === A slave-ek száma nem haladhatja meg a 246-ot, címzésük az 1..247-es tartományban történhet. A gyakorlat szerint egy szegmensben 32 állomás lehet, és csak repeater-ekkel bővíthető a hálózat. A 0. címmel broadcast üzeneteket lehet küldeni, amennyiben ez a művelet logikai szinten nincs korlátozva. A master jellemzően az 1. címet szokta megkapni, Modbus RRU egy hálózat-szegmensben csak egy master-t enged meg. === RS-485 jelráta === Az RS-485-nek nincs definiálva maximális hossz, de jellemzően a jeleket 1200 méter távolságig tudja továbbítani, és kb. 50 méterig lehet biztosítani a 10 Mbps átvitelt. Az átviteli ráta / távolság hányadosa jelentősen függ az alkalmazott vezeték minőségétől és a vonali erősítők (repeaterek) számától. {{wiki:comm:rs_signal_rate.png?562x383|RS-232, RS-422, RS-485 compare signal rates }} \\ === RS-485 jelszintek === {{wiki:comm:rs485_jelszint.png?346x327|RS-485 signal levels}} \\ A meghajtó kimenő feszültsége +12V..-7V tartományban kell, hogy maradjon. A +0.2V..-0.2V a holtsáv. A +0.2V..+6V tartomány a vevő oldalon a logikai "0" értéknek felel meg, a -0.2V..-6V tartomány pedig a logikai "1"-nek. === RS-485 half duplex kapcsolás === {{wiki:comm:rs485hd.png?442x328|RS-485 half duplex plate}} \\ === RS-485 full duplex kapcsolás === {{wiki:comm:rs485fd.png?442x328|RS-485 full duplex plate}} \\ === RS-485 / Modbus RTU kapcsolás === A Modbus RTU-hoz jellemzően a félduplex vezetékezést szokás használni, így három vezetéket tartalmaz az átvitel D+, D- és GND. A vezetékek feszültségszintjeit ellenállásokkal szokás stabilizálni (a puulup, pulldown esetén a lenti ellenállás a minimális érték, ez akár pár kΩ is lehet): {{:hu:arduino:modbus_pull_down.png|RS-485 / Modbus RTU kapcsolás}} {{ :hu:arduino:arduino_modbus_panel.png?150|MAX485 modul}} === Arduino Modbus RTU könyvtárak === Modbus RTU slave: https://www.arduino.cc/reference/en/libraries/modbusrtu_slave/ \\ Modbus RTU slave RS-485: https://www.arduino.cc/reference/en/libraries/modbusrtu_slave_rs485/ \\ Modbus RTU master: https://www.arduino.cc/reference/en/libraries/modbusmaster/ \\ Modbus RTU ESP8266: https://www.arduino.cc/reference/en/libraries/modbus-esp8266/ \\ ModbusConfig ESP8266/ESP32/Arduino: https://www.arduino.cc/reference/en/libraries/modbusconfig/ \\ ===== MAX485 modul ===== A MAX 485 modul egy egyszerű csatlakozási lehetőséget kínál az Arduino oldalról a Modbus RTU hálózathoz. A modul viszonylag egyszerűen csatolható az Arduinokhoz: {{:hu:arduino:example-rs485-mqtt.png?400|MAX485 modul}} == Vezetékezés == {{:hu:comm:c25b.png?400|MAX485 modul}} |Pin|Leírás| |Vcc|5V DC| |A|A+ Modbus| |B|B- Modbus| |GND|GND| |RO|receiver output (Rx)| |RE|Receiver enable| |DE|driver enable| |DI|driver input (Tx)| {{ :hu:comm:rs485_shield_1.png?200|RS-485 shield}} ===== RS-485 shield ===== Az RS485-Shield kibővítheti az Arduinót egy RS-485 interfésszel. Az Arduinók többsége csak egy UART soros porttal van ellátva, ennek a funkcióit bővíti ki (cseréli le) ez a shield RS485-re. A bővítmény az Arduino D0-D7 közötti portjait tudja az RS-485-re "átirányítani", így mér az Arduino Mega boardon is csak a normál Serial portot tudja felhasználni. Ez azért probléma, mert az Arduinókra a program letöltésére is a soros, normál "serial" kerül felhasználásra. A shield esetén a megoldás erre a problémára, hogy a program letöltése idejére mindkét jumpert el kell távolítani a címkijelölő portról. Kényelmetlen, de ez van. A jumpereket egyébként a serial-hoz kell igazítani, azaz a D0 az RX, a D1 a TX. **A modbus shield részei:** {{:hu:comm:rs485_shield_2.png|RS-485 shield részei}} * 1: címkijelölő port, a serialhoz D0 az RX, a D1 a TX * 2: kommunikációs jellemző, érdemes TX_CTRL-en hagyni * 3: a kommunikáció jelzése: ha felváltva néha felvillan, nyertünk * 4: a Modbus csatlakozók, elég csak egyet bekötni * 5: tápfeszültség kiválasztása, Uno esetén pl. 5V A board mind Modbus Slave mind Master funkcióval is felruházható. Ebben az esetben javaslom ezeknek a libeknek az alkalmazását: * ArduinoRS485.h : https://www.arduino.cc/en/Reference/ArduinoRS485 * ArduinoModbus.h : https://www.arduino.cc/en/ArduinoModbus/ArduinoModbus ==== Modbus slave példaprogram ==== * a D8 portra kötöttem egy LEDet a 2.regiszter állapotjelzésére (ha nem nulla, világít a LED) * a D4 portra egy DS18B20 hőmérséklet szenzor onewire kommunikációval, ennek eredménye az első regiszterben található. /* OB121 Modbus slave example, 2021. Vamos Sandor * reg 0: life register reg 1: temperature (*100) reg 2: led on/off reg 3: copy to reg 4 reg 4: copy from reg 3 */ #include #include #include // ArduinoModbus depends on the ArduinoRS485 library #include // Data wire is plugged into port 4 #define ONE_WIRE_BUS 4 // Setup a oneWire instance to communicate with any OneWire devices OneWire oneWire(ONE_WIRE_BUS); // Pass our oneWire reference to Dallas Temperature. DallasTemperature sensors(&oneWire); uint8_t sensor1[8] = { 0x28, 0x20, 0x54, 0x29, 0x05, 0x00, 0x00, 0xF4 }; //copied from the address scanner float tempSensor1; uint16_t reg0, reg1, reg2, reg3, reg4; bool ledsta; const int ledPin = LED_BUILTIN; void setup() { sensors.begin(); Serial.begin(9600); // start the Modbus RTU server, with (slave) id 1 if (!ModbusRTUServer.begin(1, 9600)) { while (1); } // configure the LED pinMode(8, OUTPUT); digitalWrite(8, LOW); // configure Holding Registers from address 0x00 ModbusRTUServer.configureHoldingRegisters(0x00, 20); } void loop() { sensors.requestTemperatures(); tempSensor1 = sensors.getTempC(sensor1); // Gets the values of the temperature // poll for Modbus RTU requests ModbusRTUServer.poll(); reg1 = (word)(tempSensor1*100); reg0++; ModbusRTUServer.holdingRegisterWrite(0, reg0); ModbusRTUServer.holdingRegisterWrite(1, reg1); reg2 = ModbusRTUServer.holdingRegisterRead(2); reg3 = ModbusRTUServer.holdingRegisterRead(3); ModbusRTUServer.holdingRegisterWrite(4, reg3); // read the current value of the coil //int coilValue = ModbusRTUServer.coilRead(0x00); if (reg2 == 0) { digitalWrite(8, LOW); } else { digitalWrite(8, HIGH); } }