Jag skickar en lista med servopositioner via seriell anslutning till arduino i följande format
1:90&2:80&3:180
Vilket skulle analyseras som:
servoId : Position & servoId : Position & servoId : Position
Hur skulle jag dela upp dessa värden och konvertera dem till ett heltal?
Kommentarer
- jag har slav (arduino uno) skicka sträng via seriell 30; 12.4; 1 och 1 master (esp8266) recive sträng jag vill ha i master har separata data som 30 12.4 1 och spara den på micro sd-kort
Svar
I motsats till andra svar, skulle jag hellre hålla mig borta från String
av följande skäl:
- dynamisk minnesanvändning (som snabbt kan leda till högfragmentering och minnesutmattning )
- ganska långsam på grund av konst funktion / förstörelse / tilldelningsoperatörer
I en inbäddad miljö som Arduino (även för en Mega som har mer SRAM) skulle jag hellre använda standard C-funktioner :
-
strchr()
: sök efter ett tecken i en C-sträng (dvs.char *
) -
strtok()
: delar en C-sträng till understrängar, baserat på ett avskiljartecken -
atoi()
: konverterar en C sträng till enint
Det skulle leda till följande kodexempel:
// 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, "&"); }
Fördelen här är att ingen dynamisk minnestilldelning sker; du kan till och med deklarera input
som en lokal variabel i en funktion som läser kommandona och utför dem; när funktionen returneras återställs storleken av input
(i stacken).
Kommentarer
- Hade ’ tänkte inte på minnesfrågan. det här är fantastiskt.
- Utmärkt. Mitt svar var väldigt ” arduino ” baserat och använde typiska arduino SDK-funktioner som en ny användare kan vara mer van vid, men det här svaret är vad ska göras för ” produktion ” -system. Försök i allmänhet att fly från dynamisk minnesallokering i inbäddade system.
Svar
Denna funktion kan användas att separera en sträng i bitar baserat på vad som skiljer karaktären.
String xval = getValue(myString, ":", 0); String yval = getValue(myString, ":", 1); Serial.println("Y:" + yval); Serial.print("X:" + xval);
Konvertera sträng till int
int xvalue = xvalue.toInt(xval); int yvalue = yvalue.toInt(yval);
Denna bit av kod tar en sträng och separerar det baserat på ett visst tecken och returnerar objektet mellan det separerande tecknet
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 är ett vackert perfekt svar! tack så mycket!
- Du borde välja den här. (Tummen upp)
- elegant! fungerar perfekt för att skicka nyckelvärde par
Svar
Du kan göra något av följande, men ta gärna med konto flera saker:
Om du använder readStringUntil()
väntar den tills den får karaktären eller timeouts. Således, med din nuvarande sträng, kommer den sista positionen att vara lite längre, eftersom den måste vänta. Du kan lägga till en efterföljande &
för att undvika denna tidsgräns. Du kan enkelt kontrollera detta beteende på din bildskärm, försök att skicka strängen med och utan extra &
så ser du en sådan tidsavbrottfördröjning.
Du gör faktiskt behöver inte servoindexet, du kan bara skicka din sträng av positioner och få servoindexet efter värdepositionen i strängen, något som: 90&80&180&
. Om du använder servoindexet kanske du vill kontrollera det (konvertera till int
och sedan matcha loopindexet i) för att säkerställa att inget gick fel med ditt meddelande.
Du måste kontrollera att retursträngen från readStringUntil
inte är tom. Om funktionens tidsavbrott inte fick tillräckligt med data och alltså försök att extrahera dina int
-värden kommer att ge konstiga resultat.
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
- Detta verkar som en mycket bra lösning tack. Exemplet rensar det perfekt
- Vad händer om vi hade ett odefinierat antal servoingångar? i mitt exempel fanns det 3. Men tänk om det ibland var mer eller mindre. Kan du ge något förslag för att hantera ett sådant scenario
- Visst: Det finns två möjligheter. 1 .Skicka först antalet servon: 3: val1 & val2 & val3 &, läs ett sådant antal innan du startar slingan. 2. Använd en annan terminator för att ange att du inte har fler servor, slinga tills du hittar den: val1 & val2 & val3 & #, till exempel.
- Glad att den här lösningen hjälpte dig, @ValrikRobot, kan du snälla validera svaret om det var användbart?
- eller du kan bara ta bort for, så koden fungerar bara när du skickar ett kommando.
Svar
Du kan använda Stream.readStringUntil(terminator)
skicka en annan terminator för varje del.
På varje del du ringer sedan till String.toInt
Svar
Enklaste lösningen är att använda 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);
Detta ger följande utdata:
n=6 id1=1, pos1=90 id2=2, pos2=80 id3=3, pos3=180
Skål!
Kommentarer
- Det fungerar inte för serial.read () … något jag dea varför? Jag får följande fel:
invalid conversion from 'int' to 'char*' [-fpermissive]
Svar
Se exempel 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 bra svar för att analysera seriekommandon på Arduino. Attiny85 har dock inte dubbelriktad serie – SoftwareSerial måste användas. Så här portar du samma kod för 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-scheman för pin-nummer
Skiss kompilerar 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å det finns gott om utrymme och minne för resten av koden
Kommentarer
- Hur man läser från serie på en ATtiny85 är inte ’ t egentligen en del av frågan.
- Ledsen för avviker från frågan, men gemenskapen och de tillgängliga resurserna för Attiny är mycket mindre än för Arduino. Människor som jag som letar efter svar använder
Arduino
nyckelord och ibland hamnar i mycket knepiga situationer när de implementerar Arduino-kod på Attiny är inte alltid trivialt. Var tvungen att konvertera originalkoden till att fungera på Attiny, testade att den fungerade och beslutade att sha re it - Denna webbplats har Q & A-format. Svaren ska svara på frågan. Din lägger bara till något som ’ inte är relaterat till 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
Här är Arduino metod för att dela en sträng som svar på frågan ”Hur man delar en sträng i substring?” förklaras som en kopia av den aktuella frågan.
Målet med lösningen är att analysera en serie av GPS positioner inloggade i en SD-kort -fil. Istället för att ha en sträng mottagen från Serial
läses strängen från filen.
Funktionen är StringSplit()
analys a Sträng sLine = "1.12345,4.56789,hello"
till 3 strängar sParams[0]="1.12345"
, sParams[1]="4.56789"
& sParams[2]="hello"
.
-
String sInput
: ingångsraderna som ska analyseras, -
char cDelim
: avgränsningstecken mellan parametrar, -
String sParams[]
: utmatningsmatrisen med parametrar, -
int iMaxParams
: maximalt antal parametrar, - Output
int
: antalet analyserade parametrar,
Funktionen är baserad på String::indexOf()
och 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); }
Och användningen är väldigt 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 är inte ett svar på din fråga men det kan vara användbart för någon. Om din sträng har ett specifikt prefix kan du helt enkelt använda startsWith
och substring
. Till exempel
void loop () if(Serial.available()){ command = Serial.readStringUntil("\n"); Serial.println("Received command: " + command); if(command.startsWith("height")) { Serial.println(command.substring(7)); } } }
Och skicka sedan height 10
vad som extraherar 10
.