Jak rozdělím příchozí řetězec?

Posílám seznam pozic serva přes sériové připojení k arduinu v následujícím formátu

1:90&2:80&3:180 

Které by byly analyzovány jako:

servoId : Position & servoId : Position & servoId : Position

Jak bych rozdělil tyto hodnoty a převedl je na celé číslo?

Komentáře

  • Mám slave (arduino uno) posílá řetězec přes sériové 30; 12.4; 1 a 1 hlavní (esp8266) přijímací řetězec chci, aby pán měl oddělená data jako 30 12,4 1 a uložil je na kartu micro sd

odpověď

naopak k dalším odpovědím se raději držím dál od String z následujících důvodů:

  • využití dynamické paměti (to může rychle vést k fragmentace haldy a vyčerpání paměti )
  • poměrně pomalý kvůli konst operátoři uction / destrukce / přiřazení

Ve vestavěném prostředí, jako je Arduino (i pro Mega, které má více SRAM), bych raději použil standardní funkce C :

  • strchr() : vyhledejte znak v řetězci C (tj char *)
  • strtok() : rozdělení řetězec C na podřetězce na základě oddělovacího znaku
  • atoi() : převede C řetězec do int

To by vedlo k následující ukázce kódu:

 // 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, "&"); }  

Výhodou je, že nedochází k žádné dynamické alokaci paměti; můžete dokonce deklarovat input jako lokální proměnnou uvnitř funkce, která bude číst příkazy a spouštět je; jakmile je funkce vrácena, obnoví se velikost obsazená input (v zásobníku).

Komentáře

  • Had ‚ nemyslel na problém s pamětí. to je skvělé.
  • Skvělé. Moje odpověď byla velmi “ arduino “ založená a používající typické funkce SDK arduino, na které by nový uživatel mohl být více zvyklý, ale tato odpověď je co je třeba udělat pro “ produkční “ systémy. Obecně se pokuste uniknout z dynamického přidělování paměti ve vestavěných systémech.

Odpovědět

Tuto funkci lze použít oddělit řetězec na kousky podle toho, co je oddělovací znak.

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

Převést řetězec na int

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

Tento blok kódu má řetězec a odděluje ji na základě daného znaku a vrací Položku mezi oddělovacím znakem

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]) : ""; } 

Komentáře

  • to je krásná dokonalá odpověď! moc děkuji!
  • Měli byste si vybrat tohle. (Palec nahoru)
  • elegantní! funguje perfektně k odesílání párů klíčových hodnot

Odpověď

Mohli byste udělat něco podobného, ale vezměte prosím do účet několik věcí:

Pokud používáte readStringUntil(), počká to, dokud nedostane znak nebo vyprší časový limit. S aktuálním řetězcem tedy poslední pozice vydrží o něco déle, protože musí čekat. Chcete-li se tomuto časovému limitu vyhnout, můžete přidat koncový &. Toto chování můžete snadno zkontrolovat na svém monitoru, zkuste odeslat řetězec s dalšími & a uvidíte takové zpoždění.

Ve skutečnosti ano nepotřebujete servo index, stačí poslat řetězec pozic a získat servo index podle hodnoty pozice v řetězci, něco jako: 90&80&180&. Pokud používáte servo index, možná ho budete chtít zkontrolovat (převést na int a poté porovnat index smyčky i), abyste se ujistili, že se ve vaší zprávě nic nepokazilo.

Musíte zkontrolovat, zda vracející se řetězec z readStringUntil není prázdný. Pokud vyprší časový limit funkce, neobdrželi jste dostatek dat, a jakýkoli pokus o extrakci hodnot int přinese podivné výsledky.

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); } } } 

Komentáře

  • Zdá se to jako velmi dobré řešení, děkuji. Příklad to dokonale vyčistí
  • Co kdybychom měli nedefinovaný počet vstupů serva? v mém příkladu bylo 3. Ale co když to někdy bylo více nebo méně. Můžete nabídnout nějaký návrh na řešení takového scénáře?
  • Jistě: Existují dvě možnosti. 1 .Nejprve odešlete počet serv: 3: val1 & val2 & val3 &, přečtěte si takové číslo před zahájením smyčky. 2. Použijte jiný terminátor k označení, že již nemáte žádná serva, dokud jej nenajdete: val1 & val2 & val3 & # například.
  • Jsem rád, že vám toto řešení pomohlo, @ValrikRobot, mohl byste prosím ověřit odpověď, pokud byla užitečná?
  • nebo vy stačí odstranit for, a tak bude kód fungovat, kdykoli odešlete příkaz.

Odpovědět

Můžete použít Stream.readStringUntil(terminator) předat pro každou část jiného zakončení.

Na každou část poté zavoláte String.toInt

odpověď

Nejjednodušším řešením je použít 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); 

Tím získáte následující výstup:

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

Na zdraví!

Komentáře

  • Pro serial.read () … nefunguje … i ano proč? Zobrazuje se následující chyba: invalid conversion from 'int' to 'char*' [-fpermissive]

Odpovědět

Viz příklad na adrese: 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; } 

Odpověď

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; } } } 

Odpověď

jfpoilpret poskytla skvělou odpověď pro analýzu sériového příkazu na Arduinu. Attiny85 však nemá obousměrné sériové číslo – je třeba použít SoftwareSerial. Takto můžete přenést stejný kód pro 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, "&"); } } }  

Schémata Attiny85 pro čísla pinů zde zadejte popis obrázku

Skica se zkompiluje do:

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. 

Takže pro zbytek kódu je spousta místa a paměti

Komentáře

  • Jak číst ze seriálu na ATtiny85 není ‚ opravdu součástí otázky.
  • Omlouváme se za Odchyluje se od otázky, ale komunita a zdroje dostupné pro Attiny jsou mnohem menší než pro Arduino. Lidé jako já, kteří hledají odpovědi, používají klíčové slovo Arduino a někdy se dostanou do velmi složitých situací, jako je implementace kódu Arduino Attiny není vždy triviální. Musel převést původní kód, aby fungoval na Attiny, otestoval jeho fungování a rozhodl se sha znovu
  • Tento web je ve formátu Q & A. Odpovědi by měly odpovídat na otázku. Vaše právě přidává něco, co ‚ s tím nesouvisí.

Odpovědět

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 } 

Odpověď

Zde je Arduino metoda rozdělení řetězce jako odpověď na otázku „Jak rozdělit řetězec v řetězci?“ deklarován jako duplikát této otázky.

Cílem řešení je analyzovat řadu GPS pozice zaznamenané do SD karty souboru. Místo toho, aby byl řetězec přijat od Serial, je řetězec načítán ze souboru.

Funkce je StringSplit() analyzována a Řetězec sLine = "1.12345,4.56789,hello" na 3 řetězce sParams[0]="1.12345", sParams[1]="4.56789" & sParams[2]="hello".

  1. String sInput: vstupní řádky k analýze,
  2. char cDelim: oddělovací znak mezi parametry,
  3. String sParams[]: výstupní pole parametrů,
  4. int iMaxParams: maximální počet parametrů,
  5. výstup int: počet analyzovaných parametry,

Funkce je založena na String::indexOf() a 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); } 

A použití je opravdu jednoduché:

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(""); } 

Odpovědět

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(){} 

Odpovědět

Není to odpověď na vaši otázku, ale pro někoho může být užitečná. Pokud má váš řetězec konkrétní předponu, můžete použít jednoduše startsWith a substring. Např.

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

A poté odešlete height 10 co extrahuje 10 .

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *