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 (tjchar *
) -
strtok()
: rozdělení řetězec C na podřetězce na základě oddělovacího znaku -
atoi()
: převede C řetězec doint
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ů
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"
.
-
String sInput
: vstupní řádky k analýze, -
char cDelim
: oddělovací znak mezi parametry, -
String sParams[]
: výstupní pole parametrů, -
int iMaxParams
: maximální počet parametrů, - 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
.