Fråga:
Skicka stora mängder seriell data
Steven10172
2014-02-14 13:58:55 UTC
view on stackexchange narkive permalink

Så inom områdena robotik behöver du ibland flera kort och eller datorer länkade ihop för att dela information eller spara statistisk data. För närvarande måste jag skicka några olika variabler över en seriell anslutning och undrade vad som var det bästa sättet att göra så?

Hittills har jag bestämt mig för att sändningsstrukturer förmodligen skulle vara det enklaste skicka data. Känner någon till något annat sätt som kan vara effektivare?

Tänk på att jag i huvudsak kommer att behöva skicka data för 4 motorer, kompressor, olika temperaturer, slumpmässiga saker och de tre sektionerna av armen.

Sex svar:
#1
+9
Steven10172
2014-02-14 13:58:55 UTC
view on stackexchange narkive permalink

Med mina personliga tankar om Structs som det mest effektiva sättet att skicka många olika variabler har jag konstruerat ett bibliotek för att göra det lättare att skicka strucker och variabler över serie. Källkod

I det här biblioteket gör det enkelt att skicka via serier. Jag har använt med med maskin- och programserie. Vanligtvis används detta i kombination med xbee så att jag kan skicka data trådlöst till och från roboten.

När du skickar data blir det enkelt eftersom det låter dig antingen skicka en variabel eller en struktur (den gör inte bryr dig inte).

Här är ett exempel på att skicka en enkel char över serien:

  // Skicka variabeln charVariable över serial.// För att skicka variabeln måste du skicka en instans av Serien som ska användas, // en referens till variabeln som ska skickas och storleken på variabeln som skickas.// Om du vill kan du ange 2 extra argument i slutet som ändrar // standardprefixet och suffixet som används när man försöker rekonstruera variabeln // i den mottagande änden. Om prefix och suffix karaktär anges måste de // matcha i mottagningsänden, annars skickas inte data korrekt övercharcharVariable = 'c'; // Definiera variabeln som ska skickas över serialStreamSend :: sendObject (Serial, &charVariable, sizeof (charVariable)); // Ange ett prefix och suffix characterStreamSend :: sendObject (Serial, &charVariable, sizeof (charVariable), 'a', 'z');  

Exempel på att skicka en enkel int över serien:

  int intVariable = 13496; // Definiera intet som ska skickas över serialStreamSend :: sendObject (xbeeSerial, &intVariable, sizeof (intVariable)); // Ange ett prefix och suffix characterStreamSend :: sendObject (xbeeSerial, &intVariable, sizeof (intVariable), ' 'p');  

Exempel på att skicka en struktur över serie:

  // Definiera strukturen som ska skickas över seriestrukturen SIMPLE_STRUCT {
char charVariable; int intVariable [7]; boolean boolVariable;}; SIMPLE_STRUCT simpleStruct; simpleStruct.charVariable = 'z'; // Ställ charVariable i struct till z // Fyll intVariable array i struct med siffrorna 0 till 6for (int i = 0; i<7; i ++) {simpleStruct.intVariable [i] = i;} // Skicka struct till objektet xbeeSerial som är en mjukvaruserie som // definierades. Istället för att använda xbeeSerial kan du använda Serial som kommer att innebära // hårdvaruserien, och på en Mega kan du ange Serial, Serial1, Serial2, Serial3.StreamSend :: sendObject (xbeeSerial, &simpleStruct, sizeof (simpleStruct)); // Send samma som ovan med ett annat prefix och suffix från standardvärdena // definierade i StreamSend. När du anger när prefix och suffix-tecken som ska skickas // måste du se till att de mottagande ändarna matchar annars kommer data // inte att kunna läsas i andra änden.StreamSend :: sendObject (xbeeSerial, &simpleStruct, sizeof (simpleStruct), '3', 'u');  

Mottagande exempel:

Ta emot en char som var skicka via Streamsend:

  char charVariable; // Definiera variabeln där data ska placeras // Läs data från det seriella objektet och spara dem i charVariable en gång // data har mottagitsbyte packetResults = StreamSend :: mottagObject (Serial, &charVariable, sizeof (charVariable)) ; // Rekonstruera char som kommer från Serien till charVariable som har ett anpassat // suffix av a och ett prefix av zbyte packetResults = StreamSend :: mottagObject (Serial, &charVariable, sizeof (charVariable), 'a', 'z') ;  

Ta emot en int som skickades via StreamSend:

  int intVariable; // Definiera variabeln där data ska placeras // Rekonstruera int från xbeeSerial till variabeln intVariablebyte packetResults = StreamSend :: mottagObject (xbeeSerial, &intVariable, sizeof (intVariable));
// Rekonstruera data till intVariable som skickades med ett anpassat prefix // av j och ett suffix av pbyte packetResults = StreamSend :: receiveObject (xbeeSerial, &intVariable, sizeof (intVariable), 'j', 'p');  kod> 

Ta emot en struktur som skickades via StreamSend:

  // Definiera strukturen som data ska läggas till SIMPLE_STRUCT {char charVariable ; int intVariable [7]; boolean boolVariable;}; SIMPLE_STRUCT simpleStruct; // Skapa en struktur för att lagra data i // Rekonstruera data från xbeeSerial till objektet simpleStructbyte packetResults = StreamSend :: receiveObject (xbeeSerial, &simpleStruct, sizeof (simpleStruct)); // Rekonstruera data från xbeeSerial till objektet förenkla det har // ett prefix av 3 och ett suffix av pbyte packetResults = StreamSend :: receiveObject (xbeeSerial, &simpleStruct, sizeof (simpleStruct), '3', 'p');  

När du har läs data med StreamSend :: mottagObject () du behöver veta om data var BRA, Hittades inte eller DÅLIG.

Bra = Lyckad

Ej hittad = Inget prefixtecken hittades i den angivna oströmmen

Dåligt = På något sätt hittades ett prefixtecken, men uppgifterna är inte intakta. Vanligtvis betyder det att inget suffikstecken hittades eller att uppgifterna inte var i rätt storlek.

Testningens giltighet:

  // När du ringer StreamSend :: mottagObject () returnerar den en byte med statusen // hur saker gick. Om du kör det även om några av testfunktionerna kommer det // att informera dig om hur transaktionen gick (StreamSend :: isPacketGood (packetResults)) {// Packet var bra} annat {// Packet var dåligt} om ( StreamSend :: isPacketCorrupt (packetResults)) {// Packet var korrupt} annars {// Packet hittades inte eller det var bra} om (StreamSend :: isPacketNotFound (packetResults)) {// Packet hittades inte efter Max antal försök} annat {
// Paketet hittades, men kan vara korrupt}  

SteamSend Class:

  #include "Arduino.h" #ifndef STREAMSEND_H # definiera STREAMSEND_H # definiera PACKET_NOT_FOUND 0 # definiera BAD_PACKET 1 # definiera GOOD_PACKET 2 // Ställ in den maximala storleken på den seriella bufferten eller mängden data du vill skicka + 2 // Du måste lägga till 2 för att tillåta prefixet och suffix karaktärutrymme att skicka. # define MAX_SIZE 64class StreamSend {private: static int getWrapperSize () {return sizeof (char) * 2; } statisk byte mottagObject (Stream &ostream, ogiltig * ptr, osignerad int objSize, osignerad int loopSize); statisk byte mottaObject (Stream &ostream, void * ptr, unsigned int objSize, unsigned int loopSize, char prefixChar, char suffixChar); statisk char _prefixChar; // Standardvärdet är s statisk char _suffixChar; // Standardvärdet är e static int _maxLoopsToWait; public: static void sendObject (Stream &ostream, void * ptr, unsigned int objSize); static void sendObject (Stream &ostream, void * ptr, unsigned int objSize, char prefixChar, char suffixChar); statisk byte mottaObject (Stream &ostream, void * ptr, osignerad int objSize); statisk byte mottaObject (Stream &ostream, void * ptr, osignerad int objSize, char prefixChar, char suffixChar); statisk boolesk isPacketNotFound (const byte packetStatus); statisk boolesk isPacketCorrupt (const byte packetStatus); statisk boolesk isPacketGood (const byte packetStatus); statiskt tomrum setPrefixChar (const char värde) {_prefixChar = värde; } static void setSuffixChar (const char value) {_suffixChar = value; } statisk ogiltig uppsättningMaxLoopsToWait (const int-värde) {_maxLoopsToWait = värde; } statisk const char getPrefixChar () {return _prefixChar; } statisk const char getSuffixChar () {return _suffixChar; } static const int getMaxLoopsToWait () {return _maxLoopsToWait; }}; // Förinställning Vissa standardvariabler // Kan ändras när det är lämpligt
char StreamSend :: _ prefixChar = 's'; // Startar karaktär innan data skickas över Serialchar StreamSend :: _ suffixChar = 'e'; // Avslutande karaktär efter all data skickas StreamSend :: _ maxLoopsToWait = -1; // Ställ till -1 för storleken på det aktuella objektet och omslaget / ** * sendObject * * Konverterar objektet till byte och skickar det till strömmen * * @param Stream för att skicka data till * @param ptr till struct för att fylla * @ param storlek på struct * @param karaktär att skicka före dataströmmen (valfritt) * @param karaktär att skicka efter dataströmmen (valfritt) * / void StreamSend :: sendObject (Stream &ostream, void * ptr, osignerad int objSize) { sendObject (ostream, ptr, objSize, _prefixChar, _suffixChar);} void StreamSend :: sendObject (Stream &ostream, void * ptr, unsigned int objSize, char prefixChar, char suffixChar) {if (MAX_SIZE > = objSize +) // se till att objektet inte är för stort byte * b = (byte *) ptr; // Skapa en ptr-uppsättning av byte för att skicka ostream.write ((byte) prefixChar); // Skriv suffikstecknet för att beteckna början på en ström // Gå igenom alla byte som skickas och skriv dem till strömmen för (osignerad int i = 0; i<objSize; i ++) {ostream.write (b [i]) ; // Skriv varje byte till strömmen} ostream.write ((byte) suffixChar); // Skriv prefixtecknet för att beteckna slutet på en ström}} / ** * mottagningsobjekt * * Hämtar data från strömmen och lagrar det medföljande objektet * * @param Stream för att läsa data från * @param ptr till strukturen för att fylla * @paramstorlek på strukturen * @paramtecken som ska skickas före dataströmmen (valfritt) * @paramtecken som ska skickas efter dataströmmen (valfritt) * / byte StreamSend :: receiveObject (Stream &ostream, void * ptr, osignerad int obj ) {return receiveObject (ostream, ptr, objSize, _prefixChar, _suffixChar);}
byte StreamSend :: receiveObject (Stream &ostream, void * ptr, unsigned int objSize, char prefixChar, char suffixChar) {return receiveObject (ostream, ptr, objSize, 0, prefixChar, suffixChar);} byte StreamSend :: ReceiveObject (Stream &ostream, ogiltig * ptr, osignerad int objSize, osignerad int loopSize, char prefixChar, char suffixChar) {int maxLoops = (_maxLoopsToWait == -1)? (objSize + getWrapperSize ()): _maxLoopsToWait; if (loopSize > = maxLoops) {return PACKET_NOT_FOUND; } om (ostream.available () > = (objSize + getWrapperSize ())) {// Packet uppfyller minimikravet om (ostream.read ()! = (byte) prefixChar) {// Prefix-tecken inte hittas // Loop igenom koden igen och läs nästa char return returnObject (ostream, ptr, objSize, loopSize + 1, prefixChar, suffixChar); } char data [objSize]; // Skapa en tmp-char-matris av data från Stream ostream.readBytes (data, objSize); // Läs # bytes memcpy (ptr, data, objSize); // Kopiera byten till strukturen om (ostream.read ()! = (Byte) suffixChar) {// Suffix-tecknet hittades inte returnera BAD_PACKET; } returnera GOOD_PACKET; } returnera PACKET_NOT_FOUND; // Prefix-tecken hittades inte så inget paket upptäcktes} boolean StreamSend :: isPacketNotFound (const byte packetStatus) {return (packetStatus == PACKET_NOT_FOUND);} boolean StreamSend :: isPacketCorrupt (const byte packetStatus) {return (packetStatUS == BAD_ );} boolean StreamSend :: isPacketGood (const byte packetStatus) {return (packetStatus == GOOD_PACKET);} # endif  
Allkodssvar, som svar på alla länkar är avskräckta. såvida inte din kod har _ ton_ kommentarer, skulle jag rekommendera att du förklarade vad som händer
@TheDoctor, Jag har uppdaterat koden. Det borde finnas fler kommentarer nu
#2
+1
TheDoctor
2014-02-14 20:20:15 UTC
view on stackexchange narkive permalink

Om du verkligen vill skicka det snabbt rekommenderar jag Full Duplex Serial (FDX). Det är samma protokoll som USB och Ethernet använder, och det är mycket snabbare än UART. Nackdelen är att det vanligtvis kräver extern hårdvara för att underlätta de höga datahastigheterna. Jag har hört att den nya softwareSreial stöder FDX, men det här kan vara långsammare än UART för hårdvara. Mer information om kommunikationsprotokoll finns i Hur man ansluter två Arduino utan skärmar?

Detta låter intressant. Jag måste titta närmare på det.
Hur kan "[full duplexserie] (http://wcscnet.com/Tutorials/SerialComm/Page1.htm)" vara "mycket snabbare än UART" när det faktiskt är standard UART-kommunikation?
UART är en kommunikation med fast ränta. FDX skickar data så snabbt som möjligt och skickar igen data som inte lyckades.
Jag skulle gärna vilja veta mer om detta protokoll. Kan du lägga till en länk till ditt svar som beskriver ett protokoll som är snabbare än UART? Pratar du om den allmänna idén om [automatisk upprepad begäran] (http: //en.wikipedia .org / wiki / automatic_repeat_request) med [ACK-NAK] (http://en.wikibooks.org/wiki/Serial_Programming/Error_Correction_Methods), eller finns det något specifikt protokoll du tänker på? Ingen av mina Google söker efter "FDX "eller" full duplex serie "tycks matcha din beskrivning.
#3
+1
80HD
2014-02-15 11:09:46 UTC
view on stackexchange narkive permalink

Att skicka en struktur är ganska enkelt.

Du kan deklarera strukturen som vanligt och sedan använda memcpy (@ myStruct, @ myArray) för att kopiera data till en ny plats och sedan använda något som liknar koden nedan för att skriva data som en dataström.

  osignerad char myArraySender [##]; // gör ## tillräckligt stort för att passa structmemcpy (&myStruct, &myArraySender); // kopiera rådata från struct till temp arraydigitalWrite (frameStartPin, High); // ange mottagaren att data är comingserial.write (storlek på myStruct); // berätta för mottagaren hur många byte som rxSerial.write (&myArraySender, storlek på myStruct); // skriv bytesdigitalWrite) frameStartPin, Low); // gjort indikerar överföring 

Sedan kan du koppla en avbrottsrutin till stiftet på den andra enheten som gör följande:

  flyktiga osignerade char len, tempBuff [##]; // flyktigt eftersom avbrottet inte kommer att ske med förutsägbara intervall.attachInterrupt (0, readSerial, Rising); 

// be MCU att ringa fxn när det är högt. Detta kommer att hända under nästan alla ögonblick. om det inte är önskvärt, ta bort avbrottet och se bara efter nya karaktärer i din huvudsakliga chefslinga (aka, UART-polling).

  void readSerial (osignerad karaktär * myArrayReceiver) {osignerad char tempbuff [ storlek på myArrayReceiver]; medan (i< (storlek på myArrayReceiver)) tempBuff [i] = Serial.read (); memcpy (&tempbuff, &myArrayReceiver); Serial.flush ();}  

Syntax och användning av pekare behöver granskas. Jag drog en all-nighter så jag är säker på att ovanstående kod inte ens kompilerar, men tanken är där. Fyll din struktur, kopiera den, använd signal utanför bandet för att undvika inramningsfel, skriv data. I den andra änden, ta emot data, kopiera den till en struktur och sedan blir informationen tillgänglig via vanliga medlemsåtkomstmetoder.

Användningen av bitfält fungerar också, var bara medveten om att nibblarna verkar vara bakåt. Till exempel kan försök att skriva 0011 1101 leda till att 1101 0011 visas i andra änden om maskinerna skiljer sig åt i byteordning.

Om dataintegriteten är viktig kan du också lägga till en kontrollsumma för att se till att du inte kopierar feljusterad skräpdata. Detta är en snabb och effektiv kontroll som jag rekommenderar.

#4
+1
JRobert
2014-03-03 04:57:17 UTC
view on stackexchange narkive permalink

Om du tolererar datamängden är felsökning av kommunikation mycket enklare när du skickar strängar än när du skickar binär; sprintf () / sscanf () och deras varianter är dina vänner här. Lägg in kommunikationen i dedikerade funktioner i sin egen modul (.cpp-fil); om du behöver optimera kanalen senare - efter du har ett fungerande system - kan du ersätta den strängbaserade modulen med en kodad för mindre meddelanden.

Du gör din livet är mycket enklare om du håller fast vid protokollspecifikationer vid överföring och tolkar dem mer löst vid mottagning, med hänsyn till fältbredder, avgränsare, radslut, obetydliga nollor, närvaro av + -skyltar etc.

Ursprungligen skrevs koden för att skicka tillbaka data i en stabiliserande slinga av en Quadcopter så den måste gå ganska snabbt.
#5
  0
Newbie97
2014-02-15 10:23:43 UTC
view on stackexchange narkive permalink

Jag har inga officiella referenser här, men enligt min erfarenhet har det gått ganska effektivt när jag väljer en viss karaktärsposition (er) för att innehålla tillståndet för en variabel, så att du kan ange de tre första tecknen som temperatur, och de tre nästa som vinkeln på en servo och så vidare. I sändningsänden skulle jag spara variablerna individuellt och sedan kombinera dem i en sträng för att skicka seriellt. I den mottagande änden skulle strängen plockas isär, få de första tre tecknen och förvandla dem till vilken variabeltyp jag behöver och sedan göra det igen för att få nästa variabelvärde. Detta system fungerar bäst när du vet med säkerhet hur mycket tecken varje variabel kommer att ta upp, och du letar alltid efter samma variabler (som jag hoppas är en given) varje gång seriell data slingrar igenom.

Du kan välja en variabel för att lägga sist av obestämd längd och få sedan variabeln från det första tecknet till slutet av strängen. Beviljas, den seriella datasträngen kan bli riktigt lång beroende på variabeltyperna och den stora mängden av dem, men det här är det system jag använder och hittills är det enda bakslag jag har drabbat seriell längd, så det är den enda nackdelen jag känner till.

Vilken typ av funktioner använder du för att spara x antal tecken i en int / float / char?
Du kanske inte inser detta, men vad du beskriver är * exakt * hur en `struct 'är organiserad i minnet (bortser från vaddering) och jag föreställer mig att dataöverföringsfunktionerna som du använder kommer att likna de som diskuteras i [Stevens svar] ( http://arduino.stackexchange.com/questions/203/sending-large-amounts-of-serial-data#204).
@AsheeshR Jag hade faktiskt en känsla av structs kan vara så, men jag personligen tenderar att träffa en vägg när jag försöker formatera structs och sedan läsa dem igen på andra sidan. Det var därför jag tänkte att jag bara skulle göra den här strängen, så att jag lätt kunde felsöka om saker misslästes, och så att jag till och med kunde läsa seriell data själv om jag betecknade det som "MOTORa023 MOTORb563" och så vidare, utan utrymmena.
@Steven10172 ja jag medger att jag inte håller koll på de specifika funktionerna, snarare googlar jag den specifika funktionen varje gång. [String to int,] (http://www.cplusplus.com/forum/general/13135/) [String to float,] (http://stackoverflow.com/questions/1012571/stdstring-to-float-or -double) och [String to char] (http://stackoverflow.com/questions/13294067/how-to-convert-string-to-char-array-in-c). Tänk på att jag använder dessa metoder i vanlig c ++ och inte har provat dem i Arduino IDE själv.
#6
  0
Guru Subramani
2017-05-24 21:03:58 UTC
view on stackexchange narkive permalink

Skicka strukturdata över serier

Inget snyggt. Skickar en struktur. Den använder ett escape-tecken '^' för att avgränsa data.

Arduino-kod

  typedef struct {float ax1; float ay1; flyta az1; float gx1; float gy1; flyta gz1; float ax2; float ay2; flyta az2; float gx2; float gy2; float gz2;} __attribute __ ((__ packad __)) data_packet_t; data_packet_t dp; mall <typename T> void sendData (T data) {unsigned long uBufSize = sizeof (data); char pBuffer [uBufSize]; memcpy (pBuffer, &dp, uBufSize); Serial.write ('^'); för (int i = 0; i<uBufSize; i ++) {if (pBuffer [i] == '^') {Serial.write ('^'); } Serial.write (pBuffer [i]); }} ogiltig installation () {Serial.begin (57600);} ogiltig loop () {dp.ax1 = 0,03; // Observera att jag inte fyllde i de andra. För mycket arbete. ; psendData<data_packet_t> (dp);}  

Python-kod:

  importera seriefrån kopia importera kopia från strukturimport * ser = seriell serie (# port = '/ dev / cu.usbmodem1412 ', port =' / dev / ttyUSB0 ', # port =' / dev / cu.usbserial-AL034MCJ ', baudrate = 57600) def get_next_data_block (next_f): om inte hasattr (get_next_data_block, "data_block") : get_next_data_block.data_block = [] medan (1): försök: current_item = next_f () if current_item == '^': next_item = next_f () if next_item == '^': get_next_data_block.data_block.append (next_item) annat: out = copy (get_next_data_block.data_block) get_next_data_block.data_block = [] get_next_data_block.data_block.append (next_item) return out else: get_next_data_block.data_block.append (current_item) utom: b reakfor i in range (1000): # bara så att programmet slutar - kan vara på en stund loop data_ = get_next_data_block (ser.read) försök: skriv ut packa upp ('= ffffffffffff', '' .join (data_))
utom: fortsätt  


Denna fråga och svar översattes automatiskt från det engelska språket.Det ursprungliga innehållet finns tillgängligt på stackexchange, vilket vi tackar för cc by-sa 3.0-licensen som det distribueras under.
Loading...