Hvordan deler jeg en innkommende streng?

Jeg sender en liste over servoposisjoner via seriell tilkobling til arduino i følgende format

1:90&2:80&3:180 

Som vil bli analysert som:

servoId : Position & servoId : Position & servoId : Position

Hvordan vil jeg dele disse verdiene opp, og konvertere dem til et heltall?

Kommentarer

  • jeg har slave (arduino uno) sende streng via seriell 30; 12.4; 1 og 1 master (esp8266) motta streng jeg vil i master ha separerte data som 30 12.4 1 og lagre det på micro sd-kort

Svar

I motsetning til andre svar, vil jeg heller holde meg borte fra String av følgende grunner:

  • dynamisk minnebruk (som raskt kan føre til haugfragmentering og minneutmattelse )
  • ganske sakte på grunn av constr funksjon / ødeleggelse / oppdragsoperatører

I et innebygd miljø som Arduino (selv for en Mega som har mer SRAM), vil jeg heller bruke standard C-funksjoner :

  • strchr() : søk etter et tegn i en C-streng (dvs. char *)
  • strtok() : deler en C-streng til understreng, basert på et skilletegn
  • atoi() : konverterer en C streng til en int

Det vil føre til følgende kodeeksempel:

 // Calculate based on max input size expected for one command #define INPUT_SIZE 30 ... // Get next command from Serial (add 1 for final 0) char input[INPUT_SIZE + 1]; byte size = Serial.readBytes(input, INPUT_SIZE); // Add the final 0 to end the C string input[size] = 0; // Read each command pair char* command = strtok(input, "&"); while (command != 0) { // Split the command in two values char* separator = strchr(command, ":"); if (separator != 0) { // Actually split the string in 2: replace ":" with 0 *separator = 0; int servoId = atoi(command); ++separator; int position = atoi(separator); // Do something with servoId and position } // Find the next command in input string command = strtok(0, "&"); }  

Fordelen her er at ingen dynamisk minnetildeling finner sted; du kan til og med erklære input som en lokal variabel i en funksjon som vil lese kommandoene og utføre dem; når funksjonen er returnert gjenopprettes størrelsen opptatt av input (i bunken).

Kommentarer

  • Hadde ikke ‘ ikke tenkt på minneproblemet. dette er flott.
  • Utmerket. Svaret mitt var veldig » arduino » og brukte typiske arduino SDK-funksjoner som en ny bruker kan være mer vant til, men dette svaret er hva skal gjøres for » produksjon » -systemer. Generelt sett, prøv å flykte fra dynamisk minnetildeling i innebygde systemer.

Svar

Denne funksjonen kan brukes å skille en streng i stykker basert på hva skilletegnet er.

String xval = getValue(myString, ":", 0); String yval = getValue(myString, ":", 1); Serial.println("Y:" + yval); Serial.print("X:" + xval); 

Konverter streng til int

int xvalue = xvalue.toInt(xval); int yvalue = yvalue.toInt(yval); 

Denne delen av koden tar en streng og skiller det basert på et gitt tegn og returnerer Elementet mellom skilletegnet

String getValue(String data, char separator, int index) { int found = 0; int strIndex[] = { 0, -1 }; int maxIndex = data.length() - 1; for (int i = 0; i <= maxIndex && found <= index; i++) { if (data.charAt(i) == separator || i == maxIndex) { found++; strIndex[0] = strIndex[1] + 1; strIndex[1] = (i == maxIndex) ? i+1 : i; } } return found > index ? data.substring(strIndex[0], strIndex[1]) : ""; } 

Kommentarer

  • det er et vakkert perfekt svar! tusen takk!
  • Du bør velge denne. (Tommelen opp)
  • elegant! fungerer perfekt for å sende nøkkelverdipar

Svar

Du kan gjøre noe av det følgende, men vær så snill å ta i betraktning konto flere ting:

Hvis du bruker readStringUntil(), vil den vente til den mottar tegnet eller tidsavbrudd. Dermed, med din nåværende streng, vil den siste posisjonen vare litt lenger, siden den må vente. Du kan legge til en etterfølgende & for å unngå denne tidsavbruddet. Du kan enkelt sjekke denne oppførselen på skjermen, prøve å sende strengen med og uten den ekstra &, og du vil se en slik tidsavbruddsforsinkelse.

Du gjør faktisk ikke trenger servoindeksen, kan du bare sende strengen med posisjoner, og få servoindeksen etter verdiposisjonen i strengen, noe sånt som: 90&80&180&. Hvis du bruker servoindeksen, vil du kanskje sjekke den (konvertere til int, og deretter matche løkkeindeksen i) for å sikre at ingenting gikk galt med meldingen din.

Du må kontrollere at returstrengen fra readStringUntil ikke er tom. Hvis tidsavbrudd for funksjonen mottok ikke nok data, og dermed vil ethvert forsøk på å trekke ut int verdiene gi rare resultater.

void setup() { Serial.begin(9600); } void loop() { for(int i=1; i<=3; i++) { String servo = Serial.readStringUntil(":"); if(servo != ""){ //here you could check the servo number String pos = Serial.readStringUntil("&"); int int_pos=pos.toInt(); Serial.println("Pos"); Serial.println(int_pos); } } } 

Kommentarer

  • Dette virker som en veldig god løsning takk. Eksemplet rydder det perfekt
  • Hva om vi hadde et udefinert antall servoinnganger? i mitt eksempel var det 3. Men hva om det noen ganger var mer eller mindre. Kan du gi noen forslag til å håndtere et slikt scenario
  • Jada: Det er to muligheter. 1 .Send først antall servoer: 3: val1 & val2 & val3 &, les et slikt nummer før du starter løkken. 2. Bruk en annen terminator for å indikere at du ikke har flere servoer, sløyfe til du finner den: val1 & val2 & val3 & #, for eksempel.
  • Glad for at denne løsningen hjalp deg, @ValrikRobot, kan du bekrefte svaret hvis det var nyttig?
  • eller du kan bare fjerne for, og så fungerer koden når som helst når du sender en kommando.

Svar

Du kan bruke Stream.readStringUntil(terminator) ved å sende en annen terminator for hver del.

På hver del så ringer du String.toInt

Svar

Enkleste løsning er å bruke sscanf () .

 int id1, id2, id3; int pos1, pos2, pos3; char* buf = "1:90&2:80&3:180"; int n = sscanf(buf, "%d:%d&%d:%d&%d:%d", &id1, &pos1, &id2, &pos2, &id3, &pos3); Serial.print(F("n=")); Serial.println(n); Serial.print(F("id1=")); Serial.print(id1); Serial.print(F(", pos1=")); Serial.println(pos1); Serial.print(F("id2=")); Serial.print(id2); Serial.print(F(", pos2=")); Serial.println(pos2); Serial.print(F("id3=")); Serial.print(id3); Serial.print(F(", pos3=")); Serial.println(pos3); 

Dette gir følgende utdata:

n=6 id1=1, pos1=90 id2=2, pos2=80 id3=3, pos3=180 

Skål!

Kommentarer

  • Det fungerer ikke for serial.read () … noen i dea hvorfor? Jeg får følgende feil: invalid conversion from 'int' to 'char*' [-fpermissive]

Svar

Se eksempel på: https://github.com/BenTommyE/Arduino_getStringPartByNr

// splitting a string and return the part nr index split by separator String getStringPartByNr(String data, char separator, int index) { int stringData = 0; //variable to count data part nr String dataPart = ""; //variable to hole the return text for(int i = 0; i<data.length()-1; i++) { //Walk through the text one letter at a time if(data[i]==separator) { //Count the number of times separator character appears in the text stringData++; } else if(stringData==index) { //get the text when separator is the rignt one dataPart.concat(data[i]); } else if(stringData>index) { //return text and stop if the next separator appears - to save CPU-time return dataPart; break; } } //return text if this is the last part return dataPart; } 

Svar

String getValue(String data, char separator, int index) { int maxIndex = data.length() - 1; int j = 0; String chunkVal = ""; for (int i = 0; i <= maxIndex && j <= index; i++) { chunkVal.concat(data[i]); if (data[i] == separator) { j++; if (j > index) { chunkVal.trim(); return chunkVal; } chunkVal = ""; } else if ((i == maxIndex) && (j < index)) { chunkVal = ""; return chunkVal; } } } 

Svar

jfpoilpret ga flott svar for å analysere seriell kommando på Arduino. Imidlertid har Attiny85 ikke toveis seriell – SoftwareSerial må brukes. Slik porter du den samme koden for Attiny85

 #include <SoftwareSerial.h> // Calculate based on max input size expected for one command #define INPUT_SIZE 30 // Initialize SoftwareSerial SoftwareSerial mySerial(3, 4); // RX=PB3, TX=PB4 // Parameter for receiving Serial command (add 1 for final 0) char input[INPUT_SIZE + 1]; void setup() { mySerial.begin(9600); } void loop() { // We need this counter to simulate Serial.readBytes which SoftwareSerial lacks int key = 0; // Start receiving command from Serial while (mySerial.available()) { delay(3); // Delay to allow buffer to fill, code gets unstable on Attiny85 without this for some reason // Don"t read more characters than defined if (key < INPUT_SIZE && mySerial.available()) { input[key] = mySerial.read(); key += 1; } } if (key > 0) { // Add the final 0 to end the C string input[key] = 0; // Read each command pair char* command = strtok(input, "&"); while (command != 0) { // Split the command in two values char* separator = strchr(command, ":"); if (separator != 0) { // Actually split the string in 2: replace ":" with 0 *separator = 0; int servoId = atoi(command); ++separator; int position = atoi(separator); } // Find the next command in input string command = strtok(0, "&"); } } }  

Attiny85-skjemaer for pin-tall skriv inn bildebeskrivelse her

Skisse kompileres til:

Sketch uses 2244 bytes (27%) of program storage space. Maximum is 8192 bytes. Global variables use 161 bytes (31%) of dynamic memory, leaving 351 bytes for local variables. Maximum is 512 bytes. 

Så det er god plass og minne for resten av koden

Kommentarer

  • Hvordan lese fra serie på en ATtiny85 er ikke ‘ t egentlig en del av spørsmålet.
  • Beklager avviker fra spørsmål, men fellesskap og ressurser som er tilgjengelige for Attiny er mye mindre enn for Arduino. Folk som meg som leter etter svar, bruker Arduino søkeord og noen ganger kommer i veldig vanskelige situasjoner når de implementerer Arduino-kode Attiny er ikke alltid trivielt. Måtte konvertere original kode til å fungere på Attiny, testet den fungerer og bestemte seg for å sha re it
  • Dette nettstedet er i Q & A-format. Svarene skal svare på spørsmålet. Din legger bare til noe som ‘ ikke har noe med det å gjøre.

Svar

char str[] = "1:90&2:80&3:180"; // test sample serial input from servo int servoId; int position; char* p = str; while (sscanf(p, "%d:%d", &servoId, &position) == 2) { // process servoId, position here // while (*p && *p++ != "&"); // to next id/pos pair } 

Svar

Her er Arduino metode for å dele en streng som svar på spørsmålet «Hvordan dele en streng i substring?» erklært som et duplikat av dette spørsmålet.

Målet med løsningen er å analysere en serie med GPS posisjoner logget inn i en SD-kort -fil. I stedet for å ha en streng mottatt fra Serial, leses strengen fra filen.

Funksjonen er StringSplit() en streng sLine = "1.12345,4.56789,hello" til 3 strenger sParams[0]="1.12345", sParams[1]="4.56789" & sParams[2]="hello".

  1. String sInput: inngangslinjene som skal analyseres,
  2. char cDelim: skilletegnet mellom parametere,
  3. String sParams[]: utgangssamlingen med parametere,
  4. int iMaxParams: maksimalt antall parametere,
  5. Output int: antall analyserte parametere,

Funksjonen er basert på String::indexOf() og String::substring():

int StringSplit(String sInput, char cDelim, String sParams[], int iMaxParams) { int iParamCount = 0; int iPosDelim, iPosStart = 0; do { // Searching the delimiter using indexOf() iPosDelim = sInput.indexOf(cDelim,iPosStart); if (iPosDelim > (iPosStart+1)) { // Adding a new parameter using substring() sParams[iParamCount] = sInput.substring(iPosStart,iPosDelim-1); iParamCount++; // Checking the number of parameters if (iParamCount >= iMaxParams) { return (iParamCount); } iPosStart = iPosDelim + 1; } } while (iPosDelim >= 0); if (iParamCount < iMaxParams) { // Adding the last parameter as the end of the line sParams[iParamCount] = sInput.substring(iPosStart); iParamCount++; } return (iParamCount); } 

Og bruken er veldig enkel:

String sParams[3]; int iCount, i; String sLine; // reading the line from file sLine = readLine(); // parse only if exists if (sLine.length() > 0) { // parse the line iCount = StringSplit(sLine,",",sParams,3); // print the extracted paramters for(i=0;i<iCount;i++) { Serial.print(sParams[i]); } Serial.println(""); } 

Svar

void setup() { Serial.begin(9600); char str[] ="1:90&2:80"; char * pch; pch = strtok(str,"&"); printf ("%s\n",pch); pch = strtok(NULL,"&"); //pch=next value printf ("%s\n",pch); } void loop(){} 

Svar

Det er ikke et svar på spørsmålet ditt, men det kan være nyttig for noen. Hvis strengen din har et bestemt prefiks, kan du bare bruke startsWith og substring. F.eks.

void loop () if(Serial.available()){ command = Serial.readStringUntil("\n"); Serial.println("Received command: " + command); if(command.startsWith("height")) { Serial.println(command.substring(7)); } } } 

Og send deretter height 10 hva som vil trekke ut 10 .

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *