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

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

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

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

Q&A

解決済

2回答

4885閲覧

Arduinoシリアルモニタで文字列を入力

octopuss

総合スコア7

Arduino

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

0グッド

0クリップ

投稿2020/03/19 08:30

前提・実現したいこと

Arduinoのシリアルモニタから数字の文字列を入力したものを数値に変換してシリアルモニタに表示するといった簡単なテスト用のプログラムを作ろうとしました.もちろん1桁なら問題ないのですが,それ以上になると一つの数値として認識しないので工夫しようと考えました.しかし以下のコードで実行すると,2桁までは問題なく機能しますが,3桁超えると入力した数字からずれて表示されるようになります.変数ansに代入するfor文がおかしいと思われるのですが,どのように直したらいけるでしょうか?

発生している問題・エラーメッセージ

エラーメッセージ

該当のソースコード

C++

1void setup() { 2 Serial.begin(9600); 3 Serial.print("Please enter the number!\n"); 4} 5 6void loop(){ 7 byte data_size; 8 long ans=0; 9 byte num[data_size]; 10 data_size=Serial.available(); 11 if(data_size>0){ 12 delay(20); 13 data_size=Serial.available(); 14 for (byte i=0;i<data_size;i++){ 15 num[i]=Serial.read()-'0'; 16 } 17 for (byte i=0;i<data_size;i++){ 18 ans=ans+num[data_size-1-i]*(int)pow(10,i); 19 } 20 Serial.println(ans); 21 Serial.print("Please enter the number!\n"); 22 } 23}

試したこと

Serial.print()の引数に直接num[data_size-1-0](int)pow(10,0)+num[data_size-1-1](int)pow(10,1)+num[data_size-1-2]*(int)pow(10,2)を入れたところもちろんですが入力した3桁の数字と一致しました.

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

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

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

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

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

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

guest

回答2

0

ベストアンサー

ローカルのauto変数(関数の中で宣言したstatic属性をつけてない変数)の初期値は「不定」です。「不定」というのは、プログラムを実行してみるまでその値が何になるか決まらないこと。0かも、1000かも、-32768かも、やってみるまでわかりません(まぁ、現実にはある傾向を持ったりはしますけれど、その期待をしてはいけないというのが約束)。で。

Arduino

1void loop(){ 2 byte data_size; 3 long ans=0; 4 byte num[data_size];

ここまで、data_size変数の値は不定です。もしかしたら0かも、-100かもしれません。要素数が-100の配列ってなんでしょうね。どんな動作をしても文句は言えない状態です。一言で言えば「やってはいけない」です。

なんでこんなことをしているかと想像すると、
data_size=Serial.available();
でdata_sizeに3が入ったとしたら、その途端にnum[3]の配列が出来たことになる...と期待したんじゃないかと思います。残念ながらそうではありません。C/C++(そしてC++をベースにしたArduino言語を含む以下略)では、各文は「その時の値」で動作します。
byte num[data_size];
はその文を実行したときのdata_sizeの値によって動作が決まり、その後data_sizeをどんな値にしようともその結果が変わることはありません。

ついでにいうと、C++の文法上は配列の宣言に置いて要素数に変数は使えません(Cではオプション規格です)。Arduino IDEが内部的に使っているgccの独自拡張によって使えているだけだ、というのは一応気を付けておいたほうがいいかも知れません。

そして、恐ろしいことに、C/C++では、配列のサイズがめちゃくちゃで、マトモなサイズでなくても配列を生成し、そのめちゃくちゃな配列にデータを書き込むことも、さらには配列としてきちんと確保していない場所にデータを書き込むことも出来てしまいます。そのような場合、素直に「メチャクチャな動き」をしてくれればまだマシなのですが、しばしば「一見ちゃんと動いているように見えるが、関係のなさそうなところで突然破綻する」ことがあります。

以上が言語のレベルでの問題。
次はアプリケーションレベルの問題。

シリアル通信の物理的なデータの単位は1バイトです。それ以上でも以下でもありません(まぁ、UARTの設定で7bitとか9bitとかも出来るかも知れませんが、それはちょっとおいといて)。ナマのシリアル通信には「2桁」とか「3桁」という概念はないのです。送信側が"123"と"45"の数字を一気に送ったとして、受信側はそれをどう区切って受け取るかはシリアル通信のレイヤでは規定出来ないのです。"1"と"2345"、"12"と"345"、あるいは"1"と"2"と"3"と"4"と"5"、かも。"123"と"45"としてやり取りするには、送り側と受け側で「何らかの約束」をしてデータをグループ化する必要があります。

多くの場合は、改行やカンマなど、本来のデータには含まれていない文字を「区切り」(デリミタ)として送ること。送る側は"123"のあとに改行コードを送り、続けて"45"を送って改行コードを送る。受ける側は'1''2''3'を受信したあと改行コードが来たら、'1''2''3'が一組なんだなぁ、と知ることが出来て、じゃあそれを"123"として扱えばいいことがわかるわけです。"45"も同様。

データが必ず3桁だと決まっているのなら、"123456"を送っては"123" "456"と受け取る、と決められるかも知れません。

あるいは、あまり確実でないかも知れませんが、時間で切ってやるという考えもあるかも知れません。ある時間以内にまとめて"123"を送って、ある時間内に受け取ったデータは連続して"123"とする。そこから一定以上の間隔をあけて"45"を送り、受ける、なんていうのも考えられるかもしれません。

いろいろ方法はあります。通信は送信側と受信側が合意していればいいのですから。

Arduinoで、適当な区切り文字を設定する場合であれば、Serial::readStringUntil()関数を使うのが楽かも知れません。調べてみて下さい。
訓練としてそういう便利な関数を使わず地道にやるというのなら、例えば

Arduino

1const int SIZE = 16; 2byte buf[SIZE]; 3int idx = 0; 4 5void setup() { 6 Serial.begin(9600); 7 Serial.print("Please enter the number!\n"); 8} 9 10void loop() { 11 if (Serial.available() > 0) { 12 buf[idx] = Serial.read(); 13 if (buf[idx] == '\n') { //データの区切りを受信した 14 int num=0; 15 int i=0; 16 while( '0'<= buf[i] && buf[i]<='9'){//有効な数字なら変換 17 num*=10; 18 num+=buf[i]-'0'; 19 i++; 20 } 21 Serial.println(num); 22 Serial.print("Please enter the number!\n"); 23 idx = 0; 24 } else { 25 idx = (idx + 1) % SIZE; //受信位置を一つすすめる。範囲外にさせないガードをしておく 26 } 27 } 28}

とか。

投稿2020/03/19 14:28

thkana

総合スコア7703

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

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

0

あらかじめ文字の配列(バッファ)を用意しておいて、1文字受信したら順番にためていき、Enterキーでその配列を文字列として変換するようにすればいいです

投稿2020/03/19 08:34

y_waiwai

総合スコア88042

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問