Jeg sender en liste over servopositioner via den serielle forbindelse til arduinoen i følgende format
1:90&2:80&3:180
Hvilket ville blive analyseret som:
servoId : Position & servoId : Position & servoId : Position
Hvordan vil jeg opdele disse værdier og konvertere dem til et heltal?
Kommentarer
- Jeg har slave (arduino uno) sende streng via seriel 30; 12.4; 1 og 1 master (esp8266) recive streng jeg vil i master have adskilte data som 30 12.4 1 og gemme dem i micro sd-kort
Svar
I modsætning til andre svar, vil jeg hellere holde mig væk fra String
af følgende årsager:
- dynamisk hukommelsesforbrug (det kan hurtigt føre til bunkefragmentering og hukommelsesudmattelse )
- ret langsom på grund af constr funktion / ødelæggelse / tildelingsoperatører
I et indlejret miljø som Arduino (selv for en Mega, der har mere SRAM), vil jeg hellere bruge standard C-funktioner :
-
strchr()
: søg efter et tegn i en C-streng (dvs.char *
) -
strtok()
: opdeler en C-streng i understrenge, baseret på et skilletegn -
atoi()
: konverterer en C streng til enint
Det ville 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 hukommelsestildeling finder sted; du kan endda erklære input
som en lokal variabel inde i en funktion, der læser kommandoerne og udfører dem; når funktionen er returneret, gendannes størrelsen optaget af input
(i stakken).
Kommentarer
- Havde ‘ ikke tænkt på hukommelsesproblemet. dette er fantastisk.
- Fremragende. Mit svar var meget ” arduino ” og brugte typiske arduino SDK-funktioner, som en ny bruger kunne være mere vant til, men dette svar er hvad der skal gøres for ” produktion ” -systemer. Generelt skal du prøve at flygte fra dynamisk hukommelsesallokering i integrerede systemer.
Svar
Denne funktion kan bruges at adskille en streng i stykker baseret på hvad den adskilte karakter 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);
Dette stykke kode tager en streng og adskiller det baseret på et givet tegn og returnerer Elementet mellem det adskilte tegn
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 smukt perfekt svar! mange tak!
- Du skal vælge denne. (Tommelfinger op)
- elegant! fungerer perfekt til at sende nøgleværdipar
Svar
Du kan gøre noget som det følgende, men vær opmærksom på konto flere ting:
Hvis du bruger readStringUntil()
, venter den, indtil den modtager tegnet eller timeouts. Således vil den sidste position med din nuværende streng vare lidt længere, da den skal vente. Du kan tilføje et efterfølgende &
for at undgå denne timeout. Du kan nemt kontrollere denne adfærd på din skærm, prøv at sende strengen med og uden den ekstra &
, og du vil se en sådan timeout-forsinkelse.
Det gør du faktisk ikke har brug for servoindekset, kan du bare sende din streng med positioner og få servoindekset efter værdipositionen i strengen, noget som: 90&80&180&
. Hvis du bruger servoindekset, vil du måske kontrollere det (konverter til int
og derefter matche loop-indekset i) for at sikre, at intet gik galt med din besked.
Du skal kontrollere, at den returnerende streng fra readStringUntil
ikke er tom. Hvis funktionens timeouts ikke modtog nok data, og dermed vil ethvert forsøg på at udtrække dine int
-værdier give mærkelige 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 meget god løsning tak. Eksemplet rydder det perfekt op
- Hvad hvis vi havde et udefineret antal servoindgange? i mit eksempel var der 3. Men hvad nu hvis det undertiden var mere eller mindre. Kan du give noget forslag til håndtering af et sådant scenario
- Sikker på: Der er to muligheder. 1 .Send først antallet af servoer: 3: val1 & val2 & val3 &, læs et sådant antal inden sløjfen startes. 2. Brug en anden terminator til at indikere, at du ikke har flere servoer, løb, indtil du finder den: val1 & val2 & val3 & #, for eksempel.
- Glad for at denne løsning hjalp dig, @ValrikRobot, kan du venligst validere svaret, hvis det var nyttigt?
- eller dig kan bare fjerne for, og så fungerer koden bare når som helst du sender en kommando.
Svar
Du kan bruge Stream.readStringUntil(terminator)
ved at sende en anden terminator til hver del.
På hver del du ringer derefter til String.toInt
Svar
Den enkleste løsning er at bruge 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 giver følgende output:
n=6 id1=1, pos1=90 id2=2, pos2=80 id3=3, pos3=180
Skål!
Kommentarer
- Det fungerer ikke for serial.read () … nogen i dea hvorfor? Jeg får følgende fejl:
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 gav et godt svar til parsering af seriel kommando på Arduino. Attiny85 har dog ikke tovejs seriel – SoftwareSerial skal bruges. Sådan portes den samme kode til 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-skemaer til pin-numre
Skits kompileres i:
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å der er masser af plads og hukommelse til resten af koden
Kommentarer
- Sådan læses fra serie på en ATtiny85 er ikke ‘ ikke en del af spørgsmålet.
- Undskyld for afviger fra spørgsmål, men samfund og ressourcer, der er tilgængelige for Attiny, er langt mindre end for Arduino. Folk som mig på udkig efter svar bruger
Arduino
søgeord og kommer nogle gange i meget vanskelige situationer, når de implementerer Arduino-kode Attiny er ikke altid trivielt. Var nødt til at konvertere original kode til at arbejde på Attiny, testede den fungerer og besluttede at sha re it - Dette websted er i Q & A-format. Svarene skal besvare spørgsmålet. Din tilføjer bare noget, som ‘ ikke er relateret til det.
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 til at opdele en streng som svar på spørgsmålet “Sådan opdeles en streng i substring?” erklæret som et duplikat af dette spørgsmål.
Formålet med løsningen er at analysere en serie af GPS positioner logget ind i en SD-kort -fil. I stedet for at have en streng modtaget fra Serial
læses strengen fra filen.
Funktionen er StringSplit()
parse en streng sLine = "1.12345,4.56789,hello"
til 3 strenge sParams[0]="1.12345"
, sParams[1]="4.56789"
& sParams[2]="hello"
.
-
String sInput
: de inputlinjer, der skal parses, -
char cDelim
: skilletegn mellem parametre, -
String sParams[]
: output array af parametre, -
int iMaxParams
: det maksimale antal parametre, - Output
int
: antallet af parsede parametre,
Funktionen er baseret 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 brugen er virkelig 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å dit spørgsmål, men det kan være nyttigt for nogen. Hvis din streng har et specifikt præfiks, kan du blot bruge 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 derefter height 10
hvad der udtrækker 10
.