🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

Q&A

解決済

2回答

2215閲覧

M5Stack(ESP32)のBluetoothserialで文字列を送信し条件分岐させる方法

nullsan

総合スコア20

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

0グッド

0クリップ

投稿2019/12/29 02:41

ESP32の派生ボードのM5StackのBluetoothserialに関する質問です。
tera termでrebootと文字列を送信した後、M5Stackがリブートするコードを記述したいのですが、
受信した文字列をtrueと判別する方法がネットにあまりなく悩んでいます。

試行錯誤したコードこちらです

//初期化 BluetoothSerial BT; char c[]; char reboot[] = "reboot"; //loop if (Serial.available() > 0) { c = BT.read(); if(c == reboot) { Serial.println("Reboot now "); ESP.restart(); }

数字などであればなんとかなりそうなのですが文字列の取り扱いについてご教示いただけると幸いです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

Arduinoの類は比較的プログラミングの詳細には踏み込まない使い方をすることが多いでしょうけれど、それでもコピペ以上のことを自分の思い通りにやりたいと思ったら知らなきゃいけないことはいろいろあります。

  1. C/C++言語の基礎ぐらいは知っておかないとどうにもなりません。

Cには文字列という型はなく、文字の配列で終端が'\0'であるものを文字列として扱うということになっているだけです。で、Cでは配列はしばしば配列データの先頭の在り処を以て表すことになっています。

C

1char str1[6]="Hello"; 2char str2[6]="Hello";

これは、

  • srt1としてメモリ6バイト分の領域を確保し、そこにHello+\0をコピーします。
  • str2としてメモリ6バイト分の領域を確保し、そこにHello+\0をコピーします。

このとき、str1とかstr2は確保した領域の場所を指しています。決して"Hello"というデータの中身を表してはいないのです。str1==str2は確保した場所の比較ということになり、それぞれ別に確保した領域なのでこれは偽になります。文字列の比較は、それぞれの場所から順次データを比較していく、という手順を踏まなければいけません。それをやってくれるstrcmp()等の関数はあるのでそれを使うのが一つの手段。

あるいは、Arduinoでは文字列を(charの配列よりは)楽に扱うためにStringという型(クラス)が用意されています。その機能で文字列を比較するものがありますから

Arduino

1void func(){ 2 String str1="Hello"; 3 String str2="Hello"; 4 if(!str1.compareTo(str2)){//compareToはstrcmpと同様一致で0を返すので修正しました 5 Serial.println("same"); 6 } 7}

などとするのも一つの手です(C++標準ライブラリのstringとはちょっと違うので注意が必要)


もう一つ。シリアル通信の基礎も。

(書いてるうちに話は進んじゃってるけど)
BluetoothSerial::read()関数は、送られた文字を「1文字だけ」受け取る関数です。送り側が"reboot"を送ったなら、read()では'r'だけが取得されます(他は溜め込まれています。あまり溜まっているのが多くなるといろいろ支障がでますが、数文字分は問題ないです)。なので、"reboot"と比較したいのなら、順次'r''e''b''o''o''t'と比較するか、あるいは受信文字を溜め込んで"reboot"と比較するか、そういうことが必要になります。

で。read()がそういう働きをする根源は、シリアル通信の通信単位が「バイト」であるということです。一文字分。通信のレベルでは、二文字以上のグループを形成する決まりはありません。送る側で"12345"というデータを送ったとして、受信する側ではバイト単位で受信するから"123"と"45"と受け取るか、"12345"と受け取るか、あるいは"1""2""3""4""5"と受け取るか、場合によってはその前後のデータとくっついてしまったり...なので、複数バイトで意味を持つデータをやりとりするときには、何らかの方法でその「塊」がわかるようにしてやる必要があります。そういう規則のことを「プロトコル」といいますが、送り手と受け手でなにかの規則を共有します。例えば「データは常にxバイトの固定長」とか、「さきにデータの大きさをxバイトで送って、以降そのデータサイズを受け取る」とか、「データ本体に出現しないはずのデータが出てくるまではひとかたまり」とか。

Arduinoでは、多分比較的簡単なのがテキストベースで通信し、改行を区切りにして改行までをひとかたまりのデータとみなす、という手法でしょう。readStringUntil()という関数を使います。
'Arduino readStringUntil'あたりで検索してみれば、Serialでの使用例が見つかると思いますので参考にしてみて下さい(いや、ちょっと力尽きてきたので手抜き:-)

投稿2019/12/29 04:06

編集2019/12/29 09:20
thkana

総合スコア7703

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nullsan

2019/12/29 09:04

ものすごくよく分かった上に実行できました!ありがとうございます。 ちなみにarduino(ESP32,M5Stack)でswitch文で文字列は扱えないのでしょうか? javaでは扱えるようです。 //略 BT.println("----Setup menu----"); code = BT.readStringUntil('\n'); switch (code) { case "Exit": M5.Lcd.print("exit"); BT.print("exit"); delay(1500); break;} 上のように使おうとするとコンパイル時に以下のエラーが返ります。 switch quantity not an integer
thkana

2019/12/29 09:15

C/C++(をベースにしたArduino言語)では、caseのラベルは整数の定数でなければいけません。文字列は、回答本文に書いたように、データの在り処を示す値(ポインタ/アドレス)となり、整数ではありません。
nullsan

2019/12/29 09:18

まだ理解が足りず申し訳ありません。 if文で対応するか、数値に置き換えるなどで対応できるかやってみます!
guest

0

strcmp関数について調べてみてはどうでしょうか

投稿2019/12/29 02:46

y_waiwai

総合スコア88038

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nullsan

2019/12/29 02:59 編集

とても便利な関数ですね!活用すれば解決できそうなきがしてさっそくコンパイルしたところ以下のようなメッセージでエラーがでました。 invalid conversion from 'int' to 'const char*' [-fpermissive] BluetoothSerial.read();ではint以外の型を使うことができないのでしょうか?
y_waiwai

2019/12/29 03:08

readってのは1文字しか読んでこないので、 "reboot"って読むなら6文字分読むまでためておかないとダメですねー
nullsan

2019/12/29 03:12

なるほど!一文字ずつ読む仕組みが必要なのですね。 6文字分貯めるというのはどのようなコードになるのでしょうか? readは一文字読んだら読みこんだ文字は消えて次の文字がセットされるという処理をしているという 解釈ですが間違いはないでしょうか?
y_waiwai

2019/12/29 03:16

> readは一文字読んだら読みこんだ文字は消えて次の文字がセットされるという処理 をしているので、別に6+1文字(それ以上)の文字が貯めておけるように、文字配列の変数を定義しておいてそれを使う必要があります。 C言語における文字列の扱いというのがまだよくわかってらっしゃらないようなので、そこらへん学ばれてはどうでしょう
nullsan

2019/12/29 03:50

ご指摘ありがとうございます! 早速文字列について再学習しながら実行しています。 char buf[80];と宣言して以下のように読み込んだ文字を代入しようとしました。 char buf[80]; buf = BT.read(); これですとコンパイル時に以下のエラーがでます。 incompatible types in assignment of 'int' to 'char [80]' たびたびですがBluetoothSerial.read();で文字列を扱う型は利用できないのでしょうか?
y_waiwai

2019/12/29 04:26

繰り返しますがreadでは一文字しか読みません 文字列にするには文字を並べて最後に0を入れます まだまだそこらへんの知識が足りません。 がんばって学んでください
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問