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

シリアル接続を介してサーボ位置のリストを次の形式でarduinoに送信しています

1:90&2:80&3:180 

次のように解析されます:

servoId : Position & servoId : Position & servoId : Position

これらの値をどのように分割し、次のように変換しますか?整数?

コメント

  • スレーブ(arduino uno)がシリアル30; 12.4; 1および1マスター(esp8266)受信文字列を介して文字列を送信しますマスターで3012.4 1のようなデータを分離し、microsdカードに保存したい

回答

逆に他の回答については、次の理由からStringを避けたいと思います。

  • 動的メモリ使用量(すぐに

    ヒープの断片化およびメモリの枯渇

  • 非常に遅い constrオークション/破壊/代入演算子

Arduinoのような組み込み環境(SRAMが多いMegaの場合でも)では、標準のC関数

  • strchr() :C文字列内の文字を検索します(つまり、 char *
  • 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を、コマンドを読み取って実行する関数内のローカル変数として宣言することもできます。関数が返されると、input(スタック内)が占めるサイズが復元されます。

コメント

  • ‘メモリの問題について考えていませんでした。これは素晴らしいことです。
  • すばらしい。私の答えは非常に” arduino “に基づいており、新しいユーザーがより慣れることができる典型的なarduino SDK関数を使用していましたが、この答えは”本番”システムで何をすべきか。一般に、組み込みシステムでの動的メモリ割り当てから逃れるようにしてください。

回答

この関数を使用できます分離文字が何であるかに基づいて文字列を断片に分離します。

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

文字列をintに変換

int xvalue = xvalue.toInt(xval); int yvalue = yvalue.toInt(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 <= 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]) : ""; } 

コメント

  • それは美しい完璧な答えです!どうもありがとう!
  • これを選択する必要があります。 (親指を立てる)
  • エレガント!キーと値のペアを送信するのに最適です

回答

次のようなことができますが、いくつかのことを説明します:

readStringUntil()を使用する場合、文字を受信するかタイムアウトになるまで待機します。したがって、現在の文字列では、待機する必要があるため、最後の位置は少し長く続きます。このタイムアウトを回避するために、末尾に&を追加できます。モニターでこの動作を簡単に確認し、追加の&を使用して、または使用せずに文字列を送信しようとすると、そのようなタイムアウト遅延が表示されます。

実際に実行しますサーボインデックスは必要ありません。位置の文字列を送信するだけで、90&80&180&のように文字列内の値の位置でサーボインデックスを取得できます。サーボインデックスを使用する場合は、それをチェックして(intに変換してから、ループインデックスiと一致させる)、メッセージに問題がないことを確認することをお勧めします。

readStringUntilから返される文字列が空でないことを確認する必要があります。関数がタイムアウトした場合、十分なデータを受信できなかったため、intの値を抽出しようとすると奇妙な結果が生成されます。

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

コメント

  • これは非常に良い解決策のようです。ありがとうございます。この例では、完全に解決されています
  • サーボ入力の数は未定義ですか?私の例では3でした。しかし、それが多かったり少なかったりした場合はどうでしょうか。そのようなシナリオを処理するための提案を提供できますか
  • 確かに:2つの可能性があります。1 。最初にサーボの数を送信します:3:val1 & val2 & val3 &、読み取りループを開始する前のそのような番号。 2.別のターミネーターを使用して、サーボがもうないことを示し、それが見つかるまでループします:val1 & val2 & val3 &#など。
  • このソリューションがお役に立ててうれしいです、@ ValrikRobot、答えが役に立ったかどうかを検証していただけますか?
  • またはあなたforを削除するだけなので、コマンドを送信するたびにコードが機能します。

回答

Stream.readStringUntil(terminator) を使用して、パーツごとに異なるターミネーターを渡すことができます。

パーツごとに次に、 String.toInt

回答

最も簡単な解決策は、 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); 

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

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

乾杯!

コメント

  • serial.read()では機能しません…任意のiなぜですか?次のエラーが発生します:invalid conversion from 'int' to 'char*' [-fpermissive]

回答

次の例を参照してください: 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; } 

回答

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

回答

jfpoilpret は素晴らしい回答を提供しました Arduinoでシリアルコマンドを解析します。ただし、Attiny85には双方向シリアルがありません-SoftwareSerialを使用する必要があります。これは、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回路図 ここに画像の説明を入力

Sketchは次のようにコンパイルされます:

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. 

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

コメント

  • ATtiny85でシリアルから読み取る方法は’実際には質問の一部ではありません。
  • 申し訳ありません。質問とは異なりますが、Attinyで利用できるコミュニティとリソースはArduinoよりもはるかに小さいです。答えを探している私のような人々はArduinoキーワードを使用し、Arduinoコードをに実装するときに非常に難しい状況に陥ることがありますAttinyは必ずしも些細なことではありません。元のコードをAttinyで動作するように変換し、動作をテストして、shaすることにしました。 re it
  • このサイトはQ & A形式です。答えは質問に答える必要があります。 ‘関係のないものを追加するだけです。

回答

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 } 

回答

ここに Arduinoがあります「文字列を部分文字列に分割する方法」という質問への回答として文字列を分割するメソッド現在の質問の複製。

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

関数はStringSplit()解析です。文字列sLine = "1.12345,4.56789,hello"から3つの文字列sParams[0]="1.12345"sParams[1]="4.56789" & sParams[2]="hello"

  1. String 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<iCount;i++) { Serial.print(sParams[i]); } Serial.println(""); } 

回答

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

回答

それはあなたの質問に対する答えではありませんが、誰かにとって役立つかもしれません。文字列に特定のプレフィックスが付いている場合は、単にstartsWithsubstringを使用できます。例

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

次に、10を抽出するものをheight 10送信します。 。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です