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
Het Modbus RTU frame byte voor byte, plus CRC
Ontleed een Modbus RTU frame byte voor byte: adres, function code, data en CRC-16. Je ziet ook waarom uitgerekend de CRC als enige low byte eerst gaat.
Wat deze les behandelt
- De vier velden van een RTU-frame, byte voor byte
- Waarom alleen de CRC met het low byte eerst in het frame staat
- Wat er gebeurt als de CRC niet klopt: een timeout, geen foutmelding
Lees eerst: Exception codes lezen en de juiste function code kiezen, Baudrate en pariteit: waarom 8E1 de default is
Een Modbus RTU frame is niet meer dan vier velden achter elkaar: een adresbyte, een function code, de data en twee CRC-bytes. Er zit geen startteken in en geen lengteveld, dus de bytes zelf zijn alles wat je hebt. Na deze les ontleed je elk frame uit een capture, reken je vooraf uit hoe lang het antwoord wordt, en zie je aan de CRC of een frame heel is aangekomen.
Een echt frame, byte voor byte
Zo gaat een compleet leesverzoek over een RS485-bus:
01 03 00 6B 00 03 74 17
Acht bytes, meer niet. Zo lees je ze:
| Byte | Hex | Veld | Wat het betekent |
|---|---|---|---|
| 1 | 01 | serveradres | het verzoek is voor het apparaat op adres 1 |
| 2 | 03 | function code | FC03, Read Holding Registers |
| 3 en 4 | 00 6B | startadres | high byte eerst, dus 0x006B is 107. Dat is het PDU-adres; in datasheettaal register 40108 |
| 5 en 6 | 00 03 | aantal registers | drie registers, dus de PDU-adressen 107, 108 en 109 |
| 7 en 8 | 74 17 | CRC-16 | de CRC-waarde 0x1774, low byte eerst |
Pas nu de regel eronder. Elk RTU-frame heeft dezelfde vier velden in dezelfde volgorde: 1 adresbyte, 1 function code byte, 0 tot 252 databytes en 2 CRC-bytes. Wat die databytes betekenen bepaalt de function code; welke codes bestaan staat in de referentie van de Modbus function codes. Voor Modbus RTU als transport is de uitleg over Modbus RTU de naslagpagina bij deze les.
Het antwoord op datzelfde frame
Het apparaat op adres 1 antwoordt met elf bytes:
01 03 06 02 2B 00 00 00 64 05 7A
| Byte | Hex | Veld | Wat het betekent |
|---|---|---|---|
| 1 | 01 | serveradres | hetzelfde apparaat antwoordt |
| 2 | 03 | function code | weer FC03, dus dit is een normaal antwoord |
| 3 | 06 | byte count | er volgen zes databytes |
| 4 en 5 | 02 2B | eerste register | 0x022B is 555 |
| 6 en 7 | 00 00 | tweede register | 0 |
| 8 en 9 | 00 64 | derde register | 0x0064 is 100 |
| 10 en 11 | 05 7A | CRC-16 | de CRC-waarde 0x7A05, low byte eerst |
Twee dingen lees je hier meteen af. De function code is ongewijzigd 03, dus er ging niets mis. En het derde veld telt bytes, geen registers: bij drie registers staat er 6. Dat veld heet in datasheets en tools byte count.
Daarmee heb je de rekenregel. Een FC03-verzoek is altijd 8 bytes; het antwoord is 5 + 2N bytes, met N het aantal registers: 1 adres, 1 function code, 1 byte count en 2 CRC, plus twee bytes per register.
Nu een voorbeeld dat op precies een punt afwijkt:
01 83 02 C0 F1
Zelfde apparaat, zelfde vraag, maar de function code is 83 in plaats van 03. Dat is 0x03 plus 0x80: het hoogste bit staat aan, en dat betekent exception-antwoord. De byte erna is de exception code, hier exception 02. Wat die codes betekenen behandelt de les over function codes en exceptions.
Waar begint en eindigt een frame?
Een frame begint en eindigt met stilte, want een andere markering is er niet. In beide frames hierboven zit geen startteken en geen lengteveld: de ontvanger weet alleen dat de lijn stil was en dat er daarna karakters kwamen.
Die stilte is vastgelegd in karaktertijden, niet in milliseconden. Een RTU-karakter is altijd 11 bits, dus bij 19200 baud duurt een karakter 0,573 ms. Daaruit volgen de twee grenzen uit de les over baudrate, pariteit en timing, nu met een frame ernaast: t3,5 is 2,005 ms stilte tussen twee frames, en t1,5 is 0,859 ms maximale gaping binnen een frame.
De maximale framegrootte ligt wel vast: 256 bytes voor een compleet RTU-frame, dus 1 adresbyte, 253 bytes PDU en 2 CRC-bytes. Van die PDU gaat 1 byte naar de function code en blijven er 252 databytes over. Trek daar in een FC03-antwoord de byte count af, dan blijven er 251 bytes voor registerwaarden over. Een register is 2 bytes, dus passen er 125 in, en dat is precies de bovengrens van 125 registers die de spec voor FC03 noemt.
Wat de CRC precies is, en wat hij niet vangt
De CRC-16 is een controlegetal dat de zender over alle voorgaande bytes uitrekent en achter het bericht plakt, zodat de ontvanger ziet of er onderweg iets veranderd is. Met de hand hoef je hem nooit uit te rekenen, maar je moet wel weten waar hij over gaat.
Drie afspraken leggen de berekening vast: de startwaarde is 0xFFFF, het polynoom is 0xA001 in de gespiegelde vorm die je in code gebruikt, en er is geen finale XOR. De CRC loopt over het adresbyte, de function code en alle databytes. Niet over de start-, stop- en pariteitsbits, want die horen bij het transport en niet bij het bericht.
Daarmee is ook duidelijk waarom pariteit en CRC naast elkaar bestaan. Pariteit werkt per karakter en ziet alleen een oneven aantal omgeklapte bits in dat ene karakter; de CRC werkt over het hele bericht. Ze vervangen elkaar niet, ze vullen elkaar aan. Het algoritme zelf hoef je alleen uit te schrijven als je een eigen library bouwt.
Waarom de CRC omgekeerd in het frame staat
Omdat de standaard het zo voorschrijft, en omdat dit de enige plek in een RTU-frame is waar dat gebeurt. De berekende CRC-waarde van het verzoek uit de eerste sectie is 0x1774, maar op de lijn staat 74 17: eerst de low byte, dan de high byte.
Alle andere 16-bits velden gaan andersom. Het startadres 0x006B staat als 00 6B in het frame, het aantal registers 3 als 00 03, en de registerwaarde 555 als 02 2B. Allemaal high byte eerst. Alleen de twee CRC-bytes zijn omgedraaid.
Nog een voorbeeld, zodat je het patroon herkent: het verzoek 01 03 00 00 00 0A heeft CRC-waarde 0xCDC5 en gaat als 01 03 00 00 00 0A C5 CD over de bus.
De truc die ontvangers gebruiken
Een ontvanger hoeft de meegestuurde CRC niet met een zelf berekende waarde te vergelijken. Hij rekent de CRC over het complete frame inclusief de twee CRC-bytes, en de uitkomst moet 0x0000 zijn. Dat klopt voor 01 03 00 6B 00 03 74 17 en voor 01 03 00 00 00 0A C5 CD.
Elk frame uit een capture kun je dus zelf nacontroleren. De decoder hieronder doet dat, en noemt de verwachte waarde als de CRC niet klopt.
CRC klopt
- Serveradres
- Function code
- Data
- CRC-16
| Veld | Hex | Decimaal | Betekenis |
|---|---|---|---|
| Serveradres | 01 | 1 | |
| Function code | 03 | 3 | Read Holding Registers |
| Startadres | 00 6B | 107 | |
| Aantal | 00 03 | 3 | |
| CRC-16 | 74 17 | 6004 |
Wat er gebeurt als de CRC niet klopt
Niets. De server voert het bericht niet uit en bouwt geen antwoord, dus de client wacht tot zijn response timeout afloopt. Er komt geen exception, geen foutcode en geen melding.
Dat is de belangrijkste praktijkregel uit deze les, want het bepaalt hoe je een storing leest. Een exception betekent dat de communicatie op orde is en dat er een logisch probleem is: verkeerd registeradres, verkeerde function code, te veel registers tegelijk. Een timeout betekent dat het eerder misgaat: verkeerd serveradres, verkeerde baudrate of pariteit, een onderbroken lijn, of te veel bitfouten. De standaard noemt bij 9600 bps een response timeout van een seconde tot enkele seconden, dus je merkt zo'n stil frame pas na die wachttijd.
Welke oorzaken je in welke volgorde nagaat staat in de les over storingen op een RTU-bus. Wil je eerst weten welke apparaten antwoorden, dan is een scan van je Modbus-netwerk de snelste stap.
Veelgemaakte fouten
De CRC-bytes omdraaien. De CRC-waarde 0x1774 hoort als 74 17 in het frame te staan. Wie hem als 17 74 schrijft krijgt van geen enkel apparaat antwoord. Adressen, aantallen en registerwaarden gaan wel high byte eerst; de CRC is de enige uitzondering.
Byte count en aantal registers verwisselen. In een antwoord staat een byte count, geen registerteller. Bij drie registers staat er 6, en wie die 6 als "zes registers" leest zoekt zes waarden waar er maar drie staan. Deel de byte count altijd door 2 voordat je gaat tellen.
Een timeout lezen als "het apparaat is er niet". Een CRC-fout geeft stilte, en stilte ziet er in een log precies zo uit als een afwezig apparaat. Controleer daarom eerst de fysieke laag en de seriele instellingen voordat je het adres in twijfel trekt.
Denken dat een pauze halverwege het frame niet uitmaakt. Een gaping van meer dan t1,5 tussen twee karakters maakt het frame ongeldig, ook als alle bytes kloppen. Een USB-adapter die zijn buffers in stukjes doorgeeft veroorzaakt precies dat.
Aan de slag
Je ontleedt eerst een frame op papier en controleert daarna je antwoord met de decoder. Stap 1 tot en met 5 hebben geen hardware nodig.
- 1
Schrijf het frame over
Neem
01 03 00 00 00 0A C5 CDen schrijf per byte op wat je denkt dat hij doet, voordat je iets opzoekt. - 2
Benoem de vier velden
Bepaal het serveradres, de function code, het startadres en het aantal registers. Let op dat de twee 16-bits velden high byte eerst staan.
- 3
Reken de lengte van het antwoord uit
Er worden 10 registers gevraagd, dus 20 databytes. Plus 1 adres, 1 function code, 1 byte count en 2 CRC: samen 25 bytes.
- 4
Controleer met de decoder
Plak het frame in de decoder hierboven en vergelijk veld voor veld met je eigen ontleding.
- 5
Sloop een byte
Verander een enkele byte, bijvoorbeeld de
0Ain een0B, en kijk welke CRC-waarde de decoder dan verwacht. Zo zie je dat een verschil van een bit een compleet andere CRC oplevert. - 6
Kijk mee op je eigen bus
Heb je hardware, draai dan mbpoll (gratis te gebruiken, ook zakelijk) met de schakelaar
-v, zodat de verzonden en ontvangen bytes op je scherm komen. Vergelijk die bytes met je ontleding. Of open de Protocol Analyzer in ModbusCloud Diagnostics, die dezelfde bytes per frameonderdeel kleurt en er een korte beschrijving bij zet.
Je bent klaar als je ontleding gelijk is aan wat de decoder toont, en als je bij het gesloopte frame de verwachte CRC-waarde ziet staan.
Samenvatting
- Elk Modbus RTU frame bestaat uit een adresbyte, een function code, 0 tot 252 databytes en 2 CRC-bytes, met een maximum van 256 bytes voor het hele frame.
- Een FC03-verzoek is 8 bytes lang en het antwoord 5 + 2N bytes, want de byte count telt bytes en geen registers.
- Adressen, aantallen en registerwaarden staan high byte eerst, en alleen de CRC staat omgedraaid:
0x1774gaat als74 17over de lijn. - Een ontvanger berekent de CRC over het complete frame inclusief de CRC-bytes en verwacht als uitkomst
0x0000. - Een CRC-fout levert stilte op en geen exception, dus een timeout wijst op de fysieke laag, de seriele instellingen of het serveradres.
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.