Estou enviando uma lista de posições de servo por meio da conexão serial para o arduino no seguinte formato
1:90&2:80&3:180
Que seria analisado como:
servoId : Position & servoId : Position & servoId : Position
Como dividir esses valores e convertê-los em um inteiro?
Comentários
- eu tenho escravo (arduino uno) enviar string via serial 30; 12.4; 1 e 1 master (esp8266) string recive eu quero no master ter dados separados como 30 12.4 1 e salvá-los no cartão micro SD
Resposta
Contrariamente para outras respostas, prefiro ficar longe de String
pelos seguintes motivos:
- uso de memória dinâmica (que pode levar rapidamente a fragmentação de heap e esgotamento de memória )
- bastante lento devido a constr Operadores de ação / destruição / atribuição
Em um ambiente embarcado como o Arduino (mesmo para um Mega que tenha mais SRAM), prefiro usar funções C padrão :
-
strchr()
: pesquisa por um caractere em uma string C (ou sejachar *
) -
strtok()
: divisões uma string C em substrings, com base em um caractere separador -
atoi()
: converte um C string para umaint
Isso levaria ao seguinte exemplo de código:
// 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, "&"); }
A vantagem aqui é que nenhuma alocação dinâmica de memória ocorre; você pode até declarar input
como uma variável local dentro de uma função que leria os comandos e os executaria; quando a função é retornada, o tamanho ocupado por input
(na pilha) é recuperado.
Comentários
- Hadn ‘ t pensou no problema de memória. isso é ótimo.
- Excelente. Minha resposta foi muito ” arduino ” baseada e usando funções do SDK Arduino típicas com as quais um novo usuário poderia estar mais acostumado, mas esta resposta é o que deve ser feito para sistemas ” produção “. Em geral, tente escapar da alocação de memória dinâmica em sistemas embarcados.
Resposta
Esta função pode ser usada para separar uma string em pedaços com base no caractere de separação.
String xval = getValue(myString, ":", 0); String yval = getValue(myString, ":", 1); Serial.println("Y:" + yval); Serial.print("X:" + xval);
Converter String em int
int xvalue = xvalue.toInt(xval); int yvalue = yvalue.toInt(yval);
Este pedaço de código recebe uma string e separa com base em um determinado caractere e retorna O item entre o caractere de separação
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]) : ""; }
Comentários
- essa é uma resposta linda e perfeita! muito obrigado!
- Você deve escolher este. (Polegar para cima)
- elegante! funciona perfeitamente para enviar pares de valores-chave
Resposta
Você poderia fazer algo como o seguinte, mas leve em consideração conta várias coisas:
Se você usar readStringUntil()
, ele irá esperar até receber o caractere ou tempo limite. Assim, com sua string atual, a última posição vai durar um pouco mais, pois tem que esperar. Você pode adicionar um &
final para evitar esse tempo limite. Você pode facilmente verificar esse comportamento em seu monitor, tente enviar a string com e sem o &
extra e você verá um atraso de tempo limite.
Você realmente faz não precisa do índice do servo, você pode apenas enviar sua string de posições e obter o índice do servo pela posição do valor na string, algo como: 90&80&180&
. Se você usar o índice servo, talvez queira verificá-lo (converter para int
e, em seguida, combinar o índice de loop i) para garantir que nada deu errado com sua mensagem.
Você deve verificar se a string de retorno de readStringUntil
não está vazia. Se a função expirar, você não recebeu dados suficientes e, portanto, qualquer tentativa de extrair seus int
valores produzirá resultados estranhos.
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); } } }
Comentários
- Esta parece uma solução muito boa, obrigado. O exemplo esclarece perfeitamente
- E se tivéssemos um número indefinido de entradas do servo? no meu exemplo havia 3. Mas e se às vezes fosse mais ou menos. Você pode oferecer alguma sugestão para lidar com tal cenário
- Claro: há duas possibilidades. 1 .Envie primeiro o número de servos: 3: val1 & val2 & val3 &, leia tal número antes de iniciar o loop. 2. Use um terminador diferente para indicar que você não tem mais servos, faça um loop até encontrá-lo: val1 & val2 & val3 & #, por exemplo.
- Que bom que esta solução ajudou você, @ValrikRobot, você poderia validar a resposta se ela foi útil?
- ou você pode simplesmente remover o para, e assim o código funcionará sempre que você enviar um comando.
Resposta
Você pode usar Stream.readStringUntil(terminator)
passando um terminador diferente para cada parte.
Em cada parte você então liga para String.toInt
Resposta
A solução mais simples é usar 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);
Isso dá a seguinte saída:
n=6 id1=1, pos1=90 id2=2, pos2=80 id3=3, pos3=180
Saudações!
Comentários
- Não está funcionando para serial.read () … any i dea porque? Recebo o seguinte erro:
invalid conversion from 'int' to 'char*' [-fpermissive]
Resposta
Veja o exemplo em: 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; }
Resposta
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; } } }
Resposta
jfpoilpret forneceu uma ótima resposta para analisar o comando serial no Arduino. No entanto Attiny85 não tem serial bidirecional – SoftwareSerial deve ser usado. É assim que você transfere o mesmo código para 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, "&"); } } }
esquemas Attiny85 para números de pinos
O Sketch é compilado em:
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.
Portanto, há bastante espaço e memória para o resto do código
Comentários
- Como ler em série em um ATtiny85 não ‘ realmente faz parte da pergunta.
- Desculpe por divergindo da pergunta, mas a comunidade e os recursos disponíveis para Attiny são bem menores do que para Arduino. Pessoas como eu em busca de respostas usam a palavra-chave
Arduino
e às vezes se metem em situações complicadas ao implementar o código do Arduino Attiny nem sempre é trivial. Tive que converter o código original para funcionar no Attiny, testei e decidi re it - Este site está no formato Q & A. As respostas devem responder à pergunta. O seu apenas adiciona algo que ‘ não tem relação com ele.
Resposta
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 }
Resposta
Aqui está Arduino método para dividir uma string como resposta à pergunta “Como dividir uma string em uma substring?” declarada como uma duplicata da questão presente.
O objetivo da solução é analisar uma série de GPS posições registradas em um arquivo cartão SD . Em vez de receber uma string de Serial
, a string é lida do arquivo.
A função é StringSplit()
parse uma String sLine = "1.12345,4.56789,hello"
a 3 Strings sParams[0]="1.12345"
, sParams[1]="4.56789"
& sParams[2]="hello"
.
-
String sInput
: as linhas de entrada a serem analisadas, -
char cDelim
: o caractere delimitador entre os parâmetros, -
String sParams[]
: a matriz de saída de parâmetros, -
int iMaxParams
: o número máximo de parâmetros, - Saída
int
: o número de parâmetros analisados parâmetros,
A função é baseada em String::indexOf()
e 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); }
E o uso é muito simples:
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(""); }
Resposta
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(){}
Resposta
Não é uma resposta à sua pergunta, mas pode ser útil para alguém. Se sua string tiver um prefixo específico, você pode usar simplesmente startsWith
e substring
. Por exemplo,
void loop () if(Serial.available()){ command = Serial.readStringUntil("\n"); Serial.println("Received command: " + command); if(command.startsWith("height")) { Serial.println(command.substring(7)); } } }
E, em seguida, envie height 10
o que irá extrair 10
.