受信した文字列を分割するにはどうすればよいですか?

サーボの位置のリストをシリアル接続でarduinoに以下のフォーマットで送信しています。

1:90&2:80&3:180

というようにパースされることになります。

サーボ ID : 位置 & サーボ ID : 位置 & サーボ ID : 位置`。

これらの値をどのように分割し、整数に変換すればよいのでしょうか?

質問へのコメント (1)
ソリューション

他の回答とは逆に、私は以下の理由で String を使用しないようにします。

  • 動的なメモリ使用(すぐにヒープの断片化メモリの枯渇につながる可能性がある)。
  • 構築/破壊/代入の演算子により、かなり遅い。

Arduinoのような組み込み環境では(SRAMに余裕のあるMegaでも)、むしろ標準C関数を使いたいところです。

  • strtok(): C 言語文字列を区切り文字に基づいて部分文字列に分割する。
  • atoi(): C の文字列を int に変換する。

そうすると、次のようなコードサンプルになります。

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

この利点は、動的なメモリ割り当てが行われないことです。コマンドを読み込んで実行する関数の内部で、 input をローカル変数として宣言することもできます。

解説 (2)

この関数を使用して、分離文字を基にして文字列を分割できます。

String xval = getValue(myString, ':', 0);
String yval = getValue(myString, ':', 1);

Serial.println("Y:" + yval);
Serial.print("X:" + xval);

文字列をintに変換します。

int xvalue = stringToNumber(xval);
int yvalue = stringToNumber(yval);

このコードのチャンクは文字列を取り、特定の文字に基づいてそれを分離して返します。 分離文字間のアイテム。


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 
解説 (1)

のようにすることもできますが、いくつかの点に注意してください。

もし readStringUntil() を使用した場合、文字を受け取るまで、またはタイムアウトするまで待機することになります。したがって、現在の文字列では、最後の位置は待つ必要があるため、少し長くなります。このタイムアウトを回避するために、末尾に & を追加することができます。この動作はモニターで簡単に確認できます。& を追加した場合と追加しない場合で文字列を送信してみると、タイムアウトの遅延が確認できます。

サーボインデックスがなくても、文字列を送信し、その文字列の値の位置でサーボインデックスを取得することができます。90&80&180&.サーボのインデックスを使用する場合は、それをチェックする(int に変換し、ループのインデックス i と一致させる)ことで、メッセージに何も問題がないことを確認することができるかもしれません。

readStringUntil`` からの戻り文字列が空でないことを確認する必要があります。もし関数がタイムアウトした場合は、十分なデータを受信していないことになります。


void setup() {
    Serial.begin(9600);
}

void loop() {
    for(int i=1; i
解説 (5)

各パートに異なるターミネータを渡す Stream.readStringUntil(terminator) を使うことができます。

各パートで、次に String.toInt を呼び出します。

解説 (0)

最も簡単な解決策は、scanf()を使用することです。

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

これにより、次の出力が得られます。

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

乾杯。!

解説 (1)

例:を参照してください。 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; iindex) {
            //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;
}
解説 (0)

String getValue(String data, char separator, int index)
{
    int maxIndex = data.length() - 1;
    int j = 0;
    String chunkVal = "";

    for (int i = 0; i 
解説 (0)
  • jfpoilpret *は、Arduinoでシリアルコマンドを解析するための優れた回答を提供しました。 ただし、Attiny85には双方向シリアルがありません-SoftwareSerialを使用する必要があります。 これは、Attiny85に同じコードを移植する方法です。
#include 

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

PIN番号のAttiny85回路図。 。![ここに画像の説明を入力してください](https://i.stack.imgur.com/IxwmX.png。)

スケッチコンパイル:

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.

したがって、残りのコードには十分なスペースとメモリがあります。

解説 (3)
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
}
解説 (0)
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(){}
解説 (0)

それはあなたの質問への答えではありませんが、それは誰かに役立つかもしれません。 文字列に特定の接頭辞がある場合は、単に「startsWith」と「substring」を使用できます。 例えば。

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

次に、「高さ10」を送信します。

解説 (0)

"文字列をサブストリングに分割する方法?"は、現在の質問の複製として宣言されています。

このソリューションの目的は、 SDカードファイルにログインした一連の GPS ポジションを解析することです。 「シリアル」から文字列を受信する代わりに、文字列はファイルから読み取られます。

関数は、 StringSplit() parse a String sLine = "1.12345,4.56789、hello" to 3 Strings sParams [0] = "1.12345"sParams [1] = "4.56789" ` 'sParams [llo ` ` '.

1。 文字列sInput:解析される入力行。 2。 char cDelim:パラメーター間の区切り文字。 3。 string sParams []:パラメータの出力配列。 4。 int iMaxParams:パラメータの最大数。 5。 出力「int」:解析されたパラメーターの数。

この関数は、 String :: indexOf()および 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);
}

そして、使用法は本当に簡単です:


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
解説 (0)