質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.35%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

3回答

1759閲覧

Arduino  LEDの点灯消灯をシリアル通信(文字列)でやりたい

koh_cafe

総合スコア3

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2020/07/21 20:03

編集2020/07/24 00:05

Arduino初心者です。
LEDの点灯消灯をシリアル通信(文字列)でやりたいのですがうまくいきません。

以下がスケッチなのですが、 Serial.println(dat); で目的のon01がdatに入力されているのは確認出来るのですが
if(dat == "on01"){ が動作していないようです。
データ型の問題の様な気がするのですが、あれこれ試しても上手くいきませんでした。
御教授のほどお願い致します。

追記
マルチポストを推奨をしていない旨を知らずに下記のリンクに同じ質問をしてしまいました。
https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q12228794132
まだ問題は解決はしていないのですが関連した質問などを参考にしてやってみます。

Aruduino

1//IOのピン番号 2const int DIN_PIN = 7; 3const int LED_PIN = 6; 4 5//入力された文字を入れる変数 6char dat[32]; // 格納用文字列 7int count = 0; // 文字数のカウンタ 8 9void setup(){ 10 pinMode( LED_PIN, OUTPUT); 11 pinMode( DIN_PIN, INPUT_PULLUP ); 12 Serial.begin(9600); 13} 14void loop(){ 15 //文字列を読み込む 16 Serial.flush(); 17 if (Serial.available()) { 18 dat[count] = Serial.read(); 19 if (count > 30 || dat[count] == '=') { // 文字数が既定の個数を超えた場合、又は終了文字を受信した場合 20 dat[count] = '\0'; // 末尾に終端文字を入れる 21 count = 0; // 文字カウンタをリセット 22 Serial.println(dat); 23 } else { 24 count++; // 文字カウンタに 1 加算 25 } 26 } 27 //出力 28 //読み込んだ文字が「on01」のときシリアルモニタに「ON」をさせ、LEDを点灯させる 29 if(dat == 'on01'){ 30 digitalWrite(LED_PIN, HIGH); 31 //LEDを点灯させた後okを返信 32 Serial.print("ok\n"); 33 Serial.flush(); 34 } 35 36 //読み込んだ文字が「off01」のときシリアルモニタに「OFF」をさせ、LEDを消灯する 37 if(dat == 'off01'){ 38 digitalWrite(LED_PIN, LOW); 39 //LEDを消灯させた後okを返信 40 Serial.print("ok\n"); 41 Serial.flush(); 42 } 43}

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

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

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

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

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

koh_cafe

2020/07/21 21:41

dodox86 様 ご指摘頂きありがとうございます。
dodox86

2020/07/21 23:18

Arduino側に送られてくるデータは、"on01=", "off01="のような、コマンド文字列の末端が'='であると言うことでしょうか。
koh_cafe

2020/07/22 13:48

thkana 様 ご指摘頂きありがとうございます。 内容理解しました。 無作法ですみません。
thkana

2020/07/23 23:05

ルールに「質問内容にマルチポストをする理由を書き、他のサイトの投稿へのリンクを貼ってください。また、解決した際には必ずteratail及びすべての投稿に解決した旨と、どのように解決したかを記載してください。」と書いてあるのは理解したのですね。 理解したのなら実行して下さい。あちらで解決したようですし。(知恵袋で解決したので知恵袋に追記する必要はないでしょう)
koh_cafe

2020/07/24 00:03

thkana 様 何度もすみません。
guest

回答3

0

ベストアンサー

最初の間違いは「文字リテラル(定数)」と「文字列リテラル」の区別が出来ていないこと。文字リテラル'A'は単に数値65の意味(ASCIIコード環境下。なお、''の中に複数の文字を入れてもエラーにはならないことが多いですが、その使い方は通常しない、と思っておいていいです。それでも本気で知りたければその旨聞いて下さい)。一方、文字列リテラル"A"はどこかにデータ'A''\0'を置いたそのアドレス(ポインタ)の意味になります。今回は文字列として比較するのですから、比較対象は文字列リテラル。

で、文字列の中身の比較をするのならstrcmp()関数を使うのがまず第一案。
strcmp()関数は、第1/第2パラメータの文字列が等しければ0を返すので
if(strcmp(dat,"on01")==0){ //datの中身が"on01"に等しければ
とします。
なお、dat=="on01"は文字列の在り処の比較なので、意図した動きにはなりません。

でも、せっかく文字列型が用意されているのだから使わなきゃもったいないし、String型を使うのならもっと使い込まなきゃもったいない。

まず。
String型に対する==演算子はArduinoでは文字列の中身の比較をちゃんと(?)やってくれます。
https://www.arduino.cc/en/Tutorial/BuiltInExamples#strings を読めば、

The operator == and the method equals() perform identically.

と書いてあります。つまり
if(String(dat)=="on01"){で先のstrcmp版と同様の結果が得られます。String::equals()で書きたいのなら別に止めませんが...

で、Stringを使うと、文字列の格納スペースとか、最後に'\0'をつけることとかは基本的には気にする必要がなくなります。なので、datをそもそもString型にして
String dat="";
として宣言すれば、countの管理は不要。さらにcがchar型の値とすれば
dat+=c;
で文字列を成長させることが出来ます。また、やりたければ行頭や行末のホワイトスペース(スペース、改行、水平タブ)を除去するtrim()を使ってdat.trim()なんてことも出来ます。
先程の比較も、わざわざString型のオブジェクトを生成する必要もなく、
if(dat=="on01"){でOK。

速度やメモリ容量に厳しいプログラム(あるいはC言語の基礎修行)でないのなら、String型を使っちゃえばよいのでは。

つぎに。
全体の構成の話ですが、現状は

1文字受信して 文字列を成長させて  コマンド検出/実行

という構成になっています。でも、コマンド検出/実行は、文字列が確定してからやるものではないでしょうか?

1文字受信して  コマンド文字列確定? ならば    コマンド検出/実行  でなければ    文字列成長

成長中の文字列を覗くから、途中で実行しちゃうみたいな現象が起こるのです。

話は変わって、
Serial.flush();。多分、OS環境下のCでprintf()がデータをバッファに貯めることがあるという話を聞きかじってそれを真似たものだと思いますが、それはあくまでその処理系でのprintf()関数の話。ArduinoのSerial::flush()は全く別の意味、用途を持っています。その意味、用途を以てここに使っているのですか? まぁ、この場合使っても害はなさそうですが...

もう一点、Serial.print("文字列\n"):としているのはなぜでしょう? 特別な意図が無いのなら、Serail.println("文字列");とした方がよろしいかと思います。これも大きな害はないですけれど。

そんなこんなを盛り込むと、loop()関数の一例としてはこんなところでは。

Arduino

1//入力された文字を入れる変数 2String dat = ""; 3void loop() { 4 //文字列を読み込む 5 if (Serial.available()) { 6 char c = Serial.read(); 7 if ( c == '=') { // 終了文字を受信した場合 8 //dat.toLowerCase();//必要に応じて。全部小文字に変換 9 Serial.println(dat); 10 //出力 11 //読み込んだ文字が「on01」のときシリアルモニタに「ON」を送り、LEDを点灯させる 12 if (dat == "on01") { 13 digitalWrite(LED_PIN, HIGH); 14 //LEDを点灯させた後okを返信 15 Serial.println("ok"); 16 } else 17 //読み込んだ文字が「off01」のときシリアルモニタに「OFF」を送り、LEDを消灯する 18 if (dat == "off01") { 19 digitalWrite(LED_PIN, LOW); 20 //LEDを消灯させた後okを返信 21 Serial.println("ok"); 22 } else 23 //入力確認 24 //読み込んだ文字が「in01」のとき 25 if (dat == "in01") { 26 //DIN_PINの状態をon/offで返信 27 int value; 28 value = digitalRead( DIN_PIN ); 29 if ( value == HIGH ) { //HIGHがOFF,LOWがON 30 Serial.println("off"); 31 } else { 32 Serial.println("on"); 33 } 34 }else{ 35 Serial.println("Command ERROR."); 36 } 37 dat = ""; 38 } else { 39 if (dat.length() < 5) {//これ以下でないとコマンドに絶対に一致しないのでデータを保存する意味がない。 40 dat += c; 41 //dat.trim();//必要に応じて。前後のホワイトスペース文字を除く 42 } 43 } 44 } 45}

投稿2020/07/24 02:20

thkana

総合スコア7703

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

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

koh_cafe

2020/07/24 16:04

thkana 様 ご丁寧な説明を頂きありがとうございます。 只今、全て意図した動作をすることが確認できました。 例えばSerial::flush()やSerial.print("文字列\n"):を使用した意図の質問がありましたが、 そもそもウェブにあるサンプルコードで動きを確認し、 問題が無ければそのまま、動作しなければ調べる、 といった作業をしていて、ひとつひとつのコードの意味をきちんと理解していないため、 AのサンプルコードにBのサンプルコードの一部を追記するなどといった作業に対応出来ませんでした。 意図した動作にはなりましが、まずはそれを理解して次のモノに進みたいと考えています。 ありがとうございました。 因みにSerial::flush()はArduino1.0以降は「データ送信完了するまで待つ」という機能かと思いますが、 受信バッファをクリアする命令は無いのでしょうか。
thkana

2020/07/24 21:43

> 受信バッファクリア 関数としてまとまったものがあるかは知りません(Arduinoのライブラリを全部把握しているわけでもないので...)。少なくとも地道に while(Serial.available()){ Serial.read(); } とすればバッファクリアされます。
koh_cafe

2020/07/26 08:46

thkana 様 ありがとうございました。
guest

0

文字列の比較はString.equalsを使います。

cpp

1String(dat).equals("in01");

この辺りを読んでください
https://www.arduino.cc/en/Tutorial/BuiltInExamples#strings
https://www.arduino.cc/reference/en/language/variables/data-types/string/functions/equals/


追記:コメント欄はフォーマットが効かないので、ここに書きます。
2020/07/23 03:55の返信です。

原因はよく分からなかったのですが、とりあえず以下の問題があり、これらをクリアしたら動きました。

  • バッファオーバーフローが発生しています
  • 見えない文字(改行など)を想定していません
  • 読み込みし終わる(想定?)前に1.2.3.が走ります
バッファオーバーフローが発生しています
char dat[4]; // 格納用文字列

読み込む文字は of01 等、終端文字を入れて5バイトなので、4バイトでは足りません。なので、count 等が破壊される可能性があります。
(質問文では32バイトなのに…)

見えない文字(改行など)を想定していません

シリアルモニターの設定で回避出来ますが、改行文字'\n'等を読み取ってしまいます。
in01=\n という文字列が来たとき、次にin01=\n を受けても、=の解釈時にdatは\nin01=となっているので、どれにもequalsになりません。

cpp

1 if (Serial.available()) { 2 int chr = Serial.read(); 3 if (chr >= 31) { // 可視文字または半角スペースを受け取ったとき 4 dat[count] = chr;
読み込みし終わる(想定?)前に1.2.3.が走ります

多分、on01xxxxxx な入力をしても反応するのでは?
読み込みが完了したかどうかのフラグを入れると良いと思います

cpp

1 2void loop(){ 3 bool fire = false; 4 5 // 略 6 7 dat[count] = Serial.read(); 8 if (count > 30 || dat[count] == '=') { 9 fire = true; 10 11 // 略 12 13 if (fire == true) { 14 15 //1.読み込んだ文字が「on01」のときLEDを点灯させる 16 if(String(dat).equals("on01") == true){ 17 // ... 18 } 19}

投稿2020/07/21 20:23

編集2020/07/23 08:53
maai

総合スコア463

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

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

koh_cafe

2020/07/21 22:00

maai 様 早々のご回答頂きありがとうございます。 if(dat == 'on01'){ となっている箇所を if(String(dat).equals("on01") == true){ としてみたのですが、 その場合それ以下に記述のある digitalWrite(LED_PIN, HIGH); と Serial.print("ok\n"); を永遠に繰り返しました。 if(String(dat).equals("on01") == false){ としても同様に繰り返しました。 char dat[32]; をstring型に変換するなどの記述が必要なのでしょうか。 Aruduinoやその他の言語についても全くの素人で、 ネットにあるものを参考に寄せ集めたスケッチなので何かが根本的に違っている様な気がします。
ozwk

2020/07/22 01:30 編集

if (Serial.available()) {}のブロックの中に点灯処理を入れて下さい そして、datをクリアしてください
maai

2020/07/22 00:54

「永遠に繰り返しました」がどんな挙動を指しているのかよくわからないですが、推測で書きます。 loopという名前の通り、loop関数はずっと繰り返します。 コードを読むと、datはSerialから読み込まれた後、クリアされず、ずっとそのままです。 if(String(dat).equals("on01") == true){とすると永遠に繰り返すのは、datの中に"on01"が残っているからです。 if(String(dat).equals("on01") == false){ としても同様に繰り返すのは、datが空の場合にも条件式が成り立つからです。 これを解決するには、例えば、処理をした後、datを空にすると出来そうです。 { digitalWrite(LED_PIN, HIGH); Serial.print("ok\n"); Serial.flush(); dat[0] = '\0'; }
koh_cafe

2020/07/22 18:55

maai 様 ozwk 様 アドバイス頂きありがとうございます。 dat[0] = '\0'; により一部は動作するようになりました。 文字を読込んだ後の処理が下記のように1, 2, 3とあるのですが、 3をスケッチから削除すると1と2は正常に動作し、2を削除すると1と3は正常に動作します。 しかしながら、1~3全てが同時にスケッチにあると、Serial.println(dat); のみが動作し、 1~3は何も動作しなくなります。 if文に何らかの制限などがあるのでしょうか。 御教示頂けると幸いです。 //IOのピン番号 const int DIN_PIN = 7; const int LED_PIN = 6; //入力された文字を入れる変数 char dat[4]; // 格納用文字列 int count = 0; // 文字数のカウンタ void setup(){ pinMode( LED_PIN, OUTPUT); pinMode( DIN_PIN, INPUT_PULLUP ); Serial.begin(9600); } void loop(){ //文字列を読み込む if (Serial.available()) { dat[count] = Serial.read(); if (count > 4 || dat[count] == '=') { // 文字数が既定の個数を超えた場合、又は終了文字を受信した場合 dat[count] = '\0'; // 末尾に終端文字を入れる count = 0; // 文字カウンタをリセット Serial.println(dat); Serial.flush(); } else { count++; // 文字カウンタに 1 加算 } //1.読み込んだ文字が「on01」のときLEDを点灯させる if(String(dat).equals("on01") == true){ digitalWrite(LED_PIN, HIGH); //LEDを点灯させた後okを返信 Serial.print("ok\n"); Serial.flush(); dat[0] = '\0'; } //2.読み込んだ文字が「of01」のときLEDを消灯する if(String(dat).equals("of01") == true){ digitalWrite(LED_PIN, LOW); //LEDを消灯させた後okを返信 Serial.print("ok\n"); Serial.flush(); dat[0] = '\0'; } //3.読み込んだ文字が「in01」のとき if(String(dat).equals("in01") == true){ //DIN_PINの状態をon/offで返信 if ( DIN_PIN == HIGH ){ //HIGHがOFF,LOWがON Serial.print("off\n"); dat[0] = '\0'; }else{ Serial.print("on\n"); Serial.flush(); dat[0] = '\0'; } } } }
maai

2020/07/23 08:56

回答に追記しました。実際にarduinoで走らせないと挙動がよく分からなかった。。。 「3をスケッチから削除すると1と2は正常に動作し、2を削除すると1と3は正常に動作します。」は確かにそうだったのですが、なぜ正常動作するのかは分かりませんでした
koh_cafe

2020/07/23 22:24

maai 様 何度もご回答を頂きございます。 結果として未だに意図した動作が出来ていないのですが、 (スケッチをアップロード後の最初のon01のみ動作しその後は動作しない) 「バッファオーバーフローが発生しています」と 「読み込みし終わる(想定?)前に1.2.3.が走ります」のふたつについては その意味と対処の方法を理解したつもりです。 32バイトを4バイトに変えてしまったのはあれこれ試した後に戻し忘れてしまいました。 「見えない文字(改行など)を想定していません」の内容について、 受信した文字がズレていく現象は確認出来るのですが、 対処方法を正しく理解しないままご教示頂いたものを挿入しているのが原因と思われます。 もう少しやってみます。
guest

0

  • Serial.flush();はこれまで受信した文字を捨ててしまうのでこれを実行してはダメです。この行はコメントアウトしときましょう
  • 文字列の比較はstrcmp関数を使います

でもひとつ、
文字列の比較をループ回るたびに行ってますが、これを、
1行受信完了したときだけ、にするように、
また、
文字列の処理が終わったら、再度処理しないように(例えば受信バッファをクリアするなど)
するようにしましょう。

投稿2020/07/21 22:14

編集2020/07/21 23:54
y_waiwai

総合スコア88042

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

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

dodox86

2020/07/21 23:02

> Serial.flush();はこれまで受信した文字を捨ててしまうのでこれを実行してはダメです。この行はコメントアウトしときましょう について、あれ?と思ったのですが、指摘事項は、Arduino1.0より前の過去の仕様のようです。(機能仕様バグ?)今は、データ送信完了するまで待つそうです。 Serial.flush https://www.arduino.cc/reference/tr/language/functions/communication/serial/flush/ Waits for the transmission of outgoing serial data to complete. (Prior to Arduino 1.0, this instead removed any buffered incoming serial data.)
y_waiwai

2020/07/21 23:10

ご指摘ありがとうございます たしかにそうなってますね。 #ましかし、いずれにしろ不要なものなので削除しとくがいいかと
dodox86

2020/07/21 23:12

> #ましかし、いずれにしろ不要なものなので削除しとくがいいかと なるほど、loop関数冒頭のSerial.flush()の部分ですね。確かに不要(意図不明)に見えますね。
koh_cafe

2020/07/22 18:24

dodox86 様 y_waiwai 様 アドバイス頂きありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問