Bekijk alle lessen
Deel 1
Basis
Deel 2
Modbus RTU
- Les 5RS485 uitgelegd: A, B, common en terminatie20 min
- Les 6Baudrate en pariteit: waarom 8E1 de default is16 min
- Les 7Het Modbus RTU frame byte voor byte, plus CRC18 min
- Les 8Meerdere Modbus apparaten op een bus zetten16 min
- Les 9Je eerste Modbus apparaat uitlezen met mbpoll22 min
- Les 10Modbus RTU storing zoeken: symptoom en oorzaak20 min
Deel 3
Modbus TCP
Deel 4
Gevorderd
- Les 16Veilig naar een Modbus apparaat schrijven18 min
- Les 17Word order en float: dezelfde bytes, andere waarde22 min
- Les 18Pollinterval en busbelasting zelf berekenen18 min
- Les 19Modbus beveiligen: het protocol doet het niet16 min
- Les 20Modbus integreren: PLC, Home Assistant of cloud20 min
- Les 21Modbus checklist voor oplevering en overdracht18 min
Poort 502, de MBAP header en de unit id
De Modbus TCP MBAP header is zeven bytes en vervangt adres en CRC. Leer wat elk veld doet, waarom poort 502 vaststaat en wanneer de unit id echt ertoe doet.
Wat deze les behandelt
- De zeven bytes van de MBAP-header en wie elk veld invult
- De Length-byte narekenen, en waarom er geen CRC in zit
- Welke unit id bij een native apparaat hoort en welke achter een gateway
Lees eerst: Exception codes lezen en de juiste function code kiezen, Het Modbus RTU frame byte voor byte, plus CRC
De MBAP header is de envelop van zeven bytes waarin Modbus TCP precies dezelfde PDU stopt die op RS485 tussen een adresbyte en een CRC zit. Vier velden, meer is het niet: transaction id, protocol id, length en unit id. Na deze les ontleed je elk Modbus TCP frame byte voor byte, reken je de Length zelf na, en weet je welke unit id je invult bij een native apparaat en welke achter een gateway.
Hetzelfde verzoek, nu over ethernet
Dit is een leesverzoek over Modbus TCP, precies zoals het over de kabel gaat:
00 01 00 00 00 06 FF 03 00 6B 00 03
Twaalf bytes. Zo lees je ze:
| Bytes | Veld | Waarde | Wat het betekent |
|---|---|---|---|
00 01 | transaction id | 1 | volgnummer dat de client kiest |
00 00 | protocol id | 0 | altijd nul, dit is Modbus |
00 06 | length | 6 | er volgen nog zes bytes |
FF | unit id | 255 | native TCP-apparaat, geen gateway |
03 | function code | FC03 | Read Holding Registers |
00 6B | startadres | 107 | PDU-adres 107, in datasheettaal register 40108 |
00 03 | aantal registers | 3 | drie registers achter elkaar |
Het RTU-frame voor dezelfde vraag, uit de les over het RTU-frame en de CRC, telt acht bytes:
01 03 00 6B 00 03 74 17
De laatste vijf bytes zijn in beide frames identiek: 03 00 6B 00 03. Dat is de PDU, en die verandert niet. Weg zijn het serveradres 01 en de CRC 74 17, en daarvoor in de plaats staat een header van zeven bytes. Modbus TCP als transport staat compleet in de uitleg over Modbus TCP; deze les ontleedt die zeven bytes.
De zeven bytes van de MBAP-header
De header heeft vier velden, en alle vier staan ze big-endian op de lijn, high byte eerst.
| Offset | Veld | Bytes | Wie vult hem in |
|---|---|---|---|
| 0 | transaction identifier | 2 | de client kiest, de server kopieert |
| 2 | protocol identifier | 2 | de client zet 0, de server kopieert |
| 4 | length | 2 | allebei zelf berekend |
| 6 | unit identifier | 1 | de client kiest, de server kopieert |
Daar zit een geheugensteun in: van de zeven headerbytes zijn er zes identiek in vraag en antwoord. Alleen Length verandert, want dat is het enige veld dat de server zelf uitrekent in plaats van kopieert.
Geen CRC: Ethernet en TCP doen de integriteitscontrole al.
Het protocol id gedraagt zich anders dan je verwacht. Staat er iets anders dan 0x0000 in, dan stuurt de server geen exception-antwoord terug maar gooit hij het bericht stilzwijgend weg. Je ziet dan niets terugkomen en loopt in een timeout.
Waarom er een transaction id is
Omdat op TCP meerdere verzoeken tegelijk onderweg mogen zijn. Een client mag een tweede vraag versturen voordat het antwoord op de eerste binnen is, en die antwoorden hoeven niet in dezelfde volgorde terug te komen. De transaction id lost dat op: de client geeft elk lopend verzoek op die verbinding een eigen nummer, en de server kopieert dat nummer ongewijzigd naar het antwoord. Vraag en antwoord horen bij elkaar zodra de nummers matchen.
Op RS485 bestaat dat probleem niet, want daar praat er per bus een apparaat tegelijk en wacht de client op antwoord of timeout voordat hij verdergaat. Hoe je het nummer kiest is aan de client: een teller die per verzoek met 1 omhoog gaat is genoeg. De enige eis is dat het nummer uniek is zolang het verzoek loopt.
De Length-byte narekenen
Length telt precies twee dingen: de ene unit id byte plus alle bytes van de PDU. In het verzoek hierboven is de PDU 03 00 6B 00 03, dus vijf bytes, en 1 plus 5 is 6. Dat is de 00 06 in de header. Het complete frame is 12 bytes, altijd zes meer dan Length.
Het antwoord op datzelfde verzoek:
00 01 00 00 00 09 FF 03 06 02 2B 00 00 00 64
| Bytes | Veld | Waarde | Wat het betekent |
|---|---|---|---|
00 01 | transaction id | 1 | gekopieerd uit het verzoek |
00 00 | protocol id | 0 | gekopieerd uit het verzoek |
00 09 | length | 9 | 1 unit id plus 8 PDU-bytes, zelf berekend |
FF | unit id | 255 | gekopieerd uit het verzoek |
03 | function code | FC03 | dezelfde code, dus geen fout |
06 | byte count | 6 | drie registers maal twee bytes |
02 2B | register 1 | 555 | |
00 00 | register 2 | 0 | |
00 64 | register 3 | 100 |
De PDU is hier 8 bytes (1 function code, 1 byte count en 6 databytes), dus Length is 9 en het frame 15 bytes. Nu een voorbeeld dat op precies een punt afwijkt:
00 01 00 00 00 03 FF 83 02
Zelfde header, maar Length is 3: 1 unit id plus een PDU van 2 bytes. De function code is 0x83, dat is 0x03 plus 0x80, en de byte erna is exception 02. Een exception-antwoord herken je dus aan twee dingen tegelijk. Wat die codes betekenen staat in de les over function codes en exception codes.
Volledig frame (ADU)
MBAP header (7 bytes)
PDU (function code en data)
Length telt 1 byte unit id plus 5 bytes PDU, samen 6.
| Veld | Bytes | Waarde | Betekenis |
|---|---|---|---|
| Transaction id | 00 01 | 1 | Volgnummer dat de client kiest. De server stuurt het ongewijzigd terug, zodat je vraag en antwoord kunt koppelen. |
| Protocol id | 00 00 | 0 | Altijd 0 voor Modbus. Een andere waarde gooit de server stilzwijgend weg. |
| Length | 00 06 | 6 | Aantal bytes dat hierna nog volgt, de unit id meegerekend. |
| Unit id | 01 | 1 | Achter een gateway het echte serveradres op de RS485-bus. Bij een native TCP-apparaat 255 of 0. |
| Function code | 03 | 3 (Read Holding Registers) | Welke bewerking je vraagt. |
| Startadres | 00 00 | 0 | Eerste register, 0-gebaseerd zoals het over de draad gaat. |
| Aantal | 00 0A | 10 | Aantal registers of coils in dit ene verzoek. |
Waarom er geen CRC in zit
Omdat de lagen eronder het werk al doen. Ethernet zet achter elk frame een CRC-32 en gooit beschadigde frames weg voordat ze boven komen. TCP heeft daarnaast een eigen checksum en stuurt gesneuvelde segmenten opnieuw. Een extra controle in de Modbus-laag kost alleen bandbreedte en rekentijd.
Er zit een tweede reden onder. Op RS485 markeert 3,5 karaktertijden stilte het einde van een frame. TCP kent die stilte niet, want het is een bytestroom zonder berichtgrenzen: een bericht kan over twee segmenten verdeeld raken of samen met het volgende in een segment zitten. Het Length-veld vertelt de ontvanger waar het bericht ophoudt, en is daarmee de directe tegenhanger van die stilte.
Wat je in het veld unit id invult
Bij een apparaat dat rechtstreeks aan het netwerk hangt vul je 0xFF in, dus 255. De waarde 0x00 mag ook. Het IP-adres wijst het apparaat immers al aan, dus het byte heeft geen adresfunctie meer. Achter een gateway is het juist wel het echte serveradres op de RS485-bus, tussen 1 en 247. De gateway leest dat byte en stuurt je verzoek naar die ene server door.
Waarom uitgerekend 255? Dat is bescherming. Wordt een IP-adres later opnieuw uitgedeeld en zit er ineens een gateway op, dan routeert die een betekenisvol serveradres door naar een apparaat dat je nooit bedoeld hebt. Bij 255 gooit de gateway het verzoek gewoon weg.
Fabrikanten volgen die aanbeveling lang niet altijd: het SunSpec-profiel van SMA vraagt unit id 126, een Fronius GEN24-omvormer wil 1 en de energiemeter erachter 200, en SolarEdge over ethernet gebruikt 1. Vertrouw hier dus niet op de standaard maar op het datasheet.
De inrichting aan gateway-kant staat in de handleiding voor het installeren van een Modbus TCP gateway, de bredere afweging in de vergelijking van Modbus RTU en Modbus TCP.
Waarom poort 502 vaststaat
Omdat de poort al sinds 1996 bij IANA geregistreerd staat, met de servicenaam mbap op naam van modbus.org. De standaard is er streng over: een apparaat moet standaard op poort 502 luisteren. Een tweede, instelbare poort mag ernaast, maar 502 hoort beschikbaar te blijven.
In de praktijk houdt niet iedereen zich daaraan. Het bekendste voorbeeld is SolarEdge: op toestellen met SetApp staat Modbus TCP standaard op poort 1502. Kom je op 502 niet binnen, controleer dan eerst de poort in het datasheet voordat je de firewall verdenkt.
Veelgemaakte fouten
De unit id verwarren met het RS485-serveradres. Bij een native TCP-apparaat is dat byte betekenisloos en vul je 255 of 0 in. Achter een gateway is het het echte serveradres, en dan is 255 gegarandeerd fout. Kijk eerst waar je tegen praat.
Length lezen als de totale framelengte. Length telt alleen wat er na dat veld komt: 1 byte unit id plus de PDU. Het complete frame is altijd zes bytes langer. Wie die twee verwart, knipt bij het parsen van een berichtenstroom steeds op de verkeerde plek.
Een CRC verwachten. Laat je een RTU-decoder los op een TCP-frame, dan leest die de laatste twee databytes als CRC en meldt een fout die er niet is. In het antwoord hierboven zou 00 64, de meetwaarde 100, ineens een kapotte checksum heten.
Aannemen dat het altijd poort 502 is. Een afwijkende poort mag en komt voor. Controleer hem in het datasheet voordat je in de firewall gaat graven.
Aan de slag
Je bouwt zelf een frame en controleert of de Length klopt. Dit werkt zonder hardware, want de builder in deze les rekent in de pagina.
- 1
Zet de builder op het voorbeeld
Kies in de MbapBuilder hierboven function code FC03, unit id 255, startadres 107 en aantal 3.
- 2
Vergelijk met het referentieframe
Leg het resultaat naast
00 01 00 00 00 06 FF 03 00 6B 00 03. Alleen de transaction id mag afwijken, want dat nummer kiest de client zelf. - 3
Zet het aantal registers op 10
Kijk wat er met de Length-byte gebeurt, en vooral wat er niet mee gebeurt. Length blijft 6, want een verzoek is even lang of je nu 3 of 10 registers vraagt. Length gaat over dit frame, niet over het antwoord.
- 4
Wissel naar FC16
Zet de function code op FC16 met drie registers. De PDU groeit van 5 naar 12 bytes, want er komen een byte count en zes databytes bij. Length wordt 13 en het frame 19 bytes.
- 5
Reken het antwoord op papier na
Hoe lang wordt het antwoord op stap 2? 7 bytes MBAP, plus 1 function code, plus 1 byte count, plus 6 databytes is 15 bytes. Controleer dat tegen het antwoordframe hierboven.
- 6
Wil je het echt over de lijn zien
Lees later in de cursus met een simulator een register uit en vang het verkeer af. Je ziet dan dezelfde twaalf bytes voorbijkomen, met een eigen transaction id.
Verwacht resultaat: een frame dat op de transaction id na identiek is aan het referentieframe, en een uitgerekende antwoordlengte van 15 bytes.
Samenvatting
- De MBAP-header is zeven bytes lang: transaction id (2), protocol id (2), length (2) en unit id (1), allemaal high byte eerst.
- Length telt 1 byte unit id plus de lengte van de PDU, dus het complete frame is altijd zes bytes langer dan die waarde.
- Van de zeven headerbytes zijn er zes identiek in vraag en antwoord; alleen Length berekent de server zelf opnieuw.
- Modbus TCP heeft geen CRC omdat ethernet en TCP de integriteit al bewaken, terwijl het Length-veld de berichtgrens markeert.
- Bij een native TCP-apparaat vul je unit id 255 of 0 in, achter een gateway het echte serveradres tussen 1 en 247.
Controleer jezelf
Vier vragen over deze les. Je krijgt bij elk antwoord uitleg.
Vraag 1 van 4
Zelf zien hoe het werkt?
De ModbusCloud Gateway leest de apparaten uit deze cursus uit zonder dat je registers hoeft te programmeren.