Spørsmål:
Kommunikasjonsprotokoll beste praksis og mønstre
Jeremy Gillick
2014-09-17 04:52:52 UTC
view on stackexchange narkive permalink

Hver gang jeg designer en serieprotokoll som skal brukes mellom to arduinos, føler jeg meg litt som om jeg finner på et hjul på nytt. Jeg lurer på om det er noen gode fremgangsmåter eller mønstre folk følger. Dette spørsmålet handler mindre om selve koden, men mer om formatet på meldingene.

Hvis jeg for eksempel vil fortelle en arduino å blinke, er den første LED tre ganger, kan jeg sende:

  ^ L1, F3 \ n  
  • '^': starter en ny kommando
  • 'L': Definerer kommandoen, (L: mål denne kommandoen mot en LED)
  • '1 ': Target the first LED
  • ', ': Command line separator, new value in this message to follow
  • ' F ': Flash sub-command
  • '3': 3 ganger (blink LED-lampen tre ganger)
  • '\ n': Avslutt kommandoen

Tanker? Hvordan nærmer du deg vanligvis å skrive en ny serieprotokoll? Hva med om jeg ville sende et spørsmål fra arduino 1 til arduino 2 og så motta svar?

Fire svar:
geometrikal
2014-09-17 10:14:36 UTC
view on stackexchange narkive permalink

Det er mange måter å skrive en seriell protokoll avhengig av hvilken funksjonalitet du måtte ønske deg og hvor mye feilkontroll du trenger.

Noen av de vanligste tingene du ser i punkt-til-punkt-protokoller er:

Slutt på melding

De enkleste ASCII-protokollene har bare slutten på meldingstegnets sekvens, ofte \ r eller \ n da dette er det som blir skrevet ut når enter-tasten trykkes. Binære protokoller kan bruke 0x03 eller annen vanlig byte.

Start av melding

Problemet med å bare ha slutten på meldingen er at du ikke vet hvilke andre byte som allerede er mottatt når du sender meldingen. Disse bytene vil da bli prefikset til meldingen og føre til at den tolkes feil. For eksempel, hvis Arduino nettopp våknet av søvn, kan det være noe søppel i den serielle bufferen. For å komme deg rundt dette har du en start på meldingssekvensen. I eksempelet ditt, ^ , i binære protokoller ofte 0x02

Feilkontroll

Hvis meldingen kan bli ødelagt, vi trenger feilkontroll. Dette kan være en kontrollsum eller en CRC-feil eller noe annet.

Escape Characters

Det kan være at kontrollsummen legger til et kontrolltegn, som f.eks. begynnelsen på meldingen eller slutten på meldingen, eller meldingen inneholder en verdi lik et kontrolltegn. Løsningen er å introdusere en rømningskarakter. Flukttegnet plasseres foran et modifisert kontrolltegn slik at det faktiske kontrolltegnet ikke er til stede. F.eks. hvis et starttegn er 0x02, kan vi bruke rømningstegnet 0x10 ved å sende verdien 0x02 i meldingen som byteparet 0x10 0x12 (byte XOR-kontrolltegn)

Pakke nummer

Hvis en melding er ødelagt, kan vi be om å sende den på nytt med en nack- eller prøv-melding, men hvis flere meldinger er sendt, kan bare den siste meldingen sendes på nytt. I stedet kan pakken gis et nummer som ruller over etter et visst antall meldinger. For eksempel, hvis dette nummeret er 16, kan den sendende enheten lagre de siste 16 sendte meldingene, og hvis noen ble ødelagt, kan den mottakende enheten be om å sende på nytt ved hjelp av pakkenummer.

Lengde◄

Ofte ser du i binære protokoller en lengdebyte som forteller mottakerenheten hvor mange tegn som er i meldingen. Dette legger til et annet nivå av feilkontroll som om riktig antall byte ikke ble mottatt, så var det en feil.

Arduino spesifikk

Når du kommer med en protokoll for Arduino, er den første vurderingen hvor pålitelig kommunikasjonskanalen er. Hvis du sender over de fleste trådløse medier, XBee, WiFi, etc, er det allerede innebygd feilkontroll og prøver på nytt, og dermed ikke noe poeng i å sette disse i protokollen din. Hvis du sender over RS422 et par kilometer, vil det være nødvendig. De tingene jeg vil inkludere er starten på meldingen og slutten på meldingstegnene, slik du har gjort. Min typiske implementering ser ut som:

>messageType, data1, data2,…, dataN \ n

Å avgrense datadelene med et komma gjør det enkelt å analysere , og meldingen sendes med ASCII. ASCII-protokoller er gode fordi du kan skrive meldinger i den serielle skjermen.

Hvis du vil ha en binær protokoll, kanskje for å forkorte meldingsstørrelsene, må du implementere escaping hvis en databyte kan være den samme som en kontrollbyte. Binære kontrolltegn er bedre for systemer der hele spekteret av feilkontroll og prøvinger er ønsket. Nyttelasten kan fremdeles være ASCII hvis ønskelig.

Er det ikke mulig at søppelet før den virkelige starten på meldingskoden kan inneholde en start på meldingskontrollkoden? Hvordan vil du takle dette?
@CMCDragonkai Ja dette er en mulighet, spesielt for kontrollkoder for enkeltbyte. Men hvis du støter på en startkontrollkode halvveis gjennom parsing av en melding, blir meldingen forkastet og parsing starter på nytt.
BrettAM
2014-09-17 07:13:56 UTC
view on stackexchange narkive permalink

Jeg har ikke noen formell ekspertise på serielle protokoller, men jeg har brukt dem ganske mange ganger, og mer eller mindre avgjort på dette skjemaet:

(Pakkeoverskrift) (ID-byte) ( data) (fletcher16 checksum) (Packet Footer)

Jeg lager vanligvis toppteksten til 2 byte og Footer 1 byte. Min parser vil dumpe alt når den ser en ny pakkehode, og prøve å analysere meldingen hvis den ser en bunntekst. Hvis sjekksummen mislykkes, vil den ikke fjerne meldingen, men fortsette å legge til bunnteksttegnet er funnet og en sjekksum lykkes. På den måten trenger bunnteksten bare å være en byte, siden kollisjoner ikke forstyrrer meldingen.

ID-en er vilkårlig, noen ganger med lengden på dataseksjonen som den nederste nissen (4 biter). En annen lengdebit kan brukes, men jeg bryr meg vanligvis ikke siden lengden ikke trenger å være kjent for å analysere riktig, så det å se riktig lengde komme gjennom for en gitt ID er bare mer bekreftelse på at meldingen var riktig. p>

Kontrollsummen fletcher16 er en kontrollsum på 2 byte med nesten samme kvalitet som CRC, men er mye lettere å implementere. noen detaljer her. Koden kan være så enkel som denne:

  for (int i = 0; i < bufSize; i ++) {sum1 = (sum1 + buffer [i])% 255; sum2 = (sum2 + sum1)% 255;} uint16_t sjekksum = (((uint16_t) sum1) <<8) | sum2;  

Jeg har også brukt et anrops- og responsystem for kritiske meldinger, hvor PCen vil sende en melding hver 500ms eller så til den får en OK-melding med en kontrollsum av hele original melding som data (inkludert den originale sjekksummen).

Denne ordningen er selvfølgelig ikke godt egnet for å bli skrevet inn i en terminal som eksempelet ditt ville være. Protokollen din virker ganske bra for å være begrenset til ASCII, og jeg er sikker på at det er lettere for et raskt prosjekt som du vil kunne lese og sende meldinger direkte. For større prosjekter er det hyggelig å ha tettheten til en binær protokoll og sikkerheten til en sjekksum.

Siden "[din] parser vil dumpe alt når den ser en ny pakkehode", lurer jeg på om dette ikke skaper problemer hvis overskriften tilfeldigvis oppstår inne i data?
@humanityANDpeace Årsaken til at du slipper den, er at når en pakke blir kuttet av, vil den aldri analysere riktig, så når bestemmer du søpla og fortsetter? Den enkleste, og etter min erfaring god nok, er løsningen å slippe en dårlig pakke så snart neste topptekst kommer inn. Jeg har brukt en 16-bits topptekst uten problemer, men du kan gjøre det lenger hvis sikkerhet er viktigere båndbredde.
Så det du refererer til som header er noe av en Magic 16bit kombinasjon. dvs. 010101001 10101010, ikke sant? Jeg er enig i at det bare er 1/256 * 256 endring å treffe, men det deaktiverer også å bruke denne 16-biten i dataene dine, ellers blir det feiltolket som en header, og du forkaster meldingen, ikke sant?
@humanityANDpeace Jeg vet at det er et år senere, men du må introdusere en rømningssekvens. Før sendingen kontrollerer serveren nyttelasten for spesielle byte, og slipper dem deretter med en annen spesiell byte. Kundesiden, du må sette den originale nyttelasten sammen igjen. Dette betyr at du ikke kan sende pakker med fast lengde og kompliserer implementeringen. Det er mange serielle protokollstandarder å velge mellom som alle adresserer dette. [Her er en veldig god lesing om emnet] (https://www.embeddedrelated.com/showarticle/113.php).
Maarten Bodewes
2016-08-12 15:26:05 UTC
view on stackexchange narkive permalink

Hvis du er interessert i standarder, kan du ta en titt på ASN.1 / BER TLV-koding. ASN.1 er et språk som brukes til å beskrive datastrukturer, laget spesielt for kommunikasjon. BER er en TLV-metode for koding av data strukturert ved bruk av ASN.1. Problemet er at ASN.1-koding i beste fall kan være vanskelig. Å lage en fullverdig ASN.1 kompilator er et prosjekt i seg selv (og spesielt vanskelig på det, tenk måneder ).


Det er sannsynligvis bedre å beholde bare TLV-struktur. TLV består i utgangspunktet av tre elementer: en tag, en lengde og et verdifelt. Taggen definerer datatypen (tekststreng, oktettstreng, heltall osv.) Og lengden lengden av verdien .

I BER betegner T også om verdi er et sett med TLV-strukturer i seg selv (en konstruert node) eller direkte en verdi (en primitiv node). På den måten kan du lage et tre i binær, omtrent som XML (men uten XML-overhead).

Eksempel:

  TT LL VV02 01 FF  

er et heltall (tag 02 ) med en lengde på verdien 1 (lengde 01 ) og verdi -1 (verdi FF ). I ASN.1 / BER er heltallene tegn på store endian tall, men du kan selvfølgelig bruke ditt eget format.

  TT LL (TT LL VV, TT LL VV VV) 30 07 02 01 FF 02 02 00 FF  

er en sekvens (en liste) med lengde 7 som inneholder to heltall, en med verdi -1 og en med verdi 255. De to heltall kodinger utgjør sammen verdien av sekvensen.

Du kan ganske enkelt kaste dette inn i en online dekoder også, er det ikke så fint?


Du kan også bruke ubestemt lengde i BER som lar deg streame data. I så fall må du analysere treet ditt riktig. Jeg vil betrakte det som et avansert emne. Du må vite om bredde først og dybde første analyse, for en.


Ved å bruke en TLV-ordning kan du i utgangspunktet tenke på hvilken som helst datastruktur og kode den. ASN.1 går mye lenger enn det, og gir deg unike identifikatorer (OID-er), valg (mye som C-fagforeninger), inkluderer andre ASN.1-strukturer etc. etc., men det kan være for mye for prosjektet ditt. Sannsynligvis er de mest kjente ASN.1-definerte strukturene i dag sertifikatene som brukes av nettleseren din.

JRobert
2016-08-12 18:26:01 UTC
view on stackexchange narkive permalink

Hvis ikke, har du det grunnleggende dekket. Kommandoene dine kan opprettes og leses av både mennesker og maskiner, noe som er et stort pluss. Du kan legge til et kontrollsum for å oppdage en feil dannet kommando eller en som er skadet under transport, spesielt hvis kanalen din inneholder lang kabel eller en radiokobling.

Hvis du trenger industriell styrke (enheten din må ikke forårsake eller la noen bli skadet eller dø; du trenger høye datahastigheter, feilgjenoppretting, manglende pakkeoppdagelse osv.) så se på noen av standardprotokollene og designpraksis.



Denne spørsmålet ble automatisk oversatt fra engelsk.Det opprinnelige innholdet er tilgjengelig på stackexchange, som vi takker for cc by-sa 3.0-lisensen den distribueres under.
Loading...