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

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

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

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

Q&A

解決済

3回答

6755閲覧

ArduinoでPCから受信したSerial信号を型変換したい

HiroPokeHero

総合スコア45

Arduino

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

0グッド

0クリップ

投稿2019/08/15 09:06

編集2019/08/16 06:59

前提・実現したいこと

ArduinoでPCからのSerial信号を受信したい。
受信したSerial信号の文字列を分割して、
CAN通信のサービスID、データ長、データとしてCAN通信したい。

ここで、知りたいのは分割した文字をキャストする方法です。

tx:130h,8,FE,3F,2F,88,23,23,44,FF
※ tx:までは固定で、130hは0x0h~0x7FFhまでの”0x”を省略したデータで、
8はデータ長で1~8となっています。
以降はデータで1byte×データ長のデータで、すべてコンマ区切りになています。

文字を分割することはできそうですが、その際に文字をキャストする部分でエラーになります。

この場合だと、(tx:130h,8,FE,3F,2F,88,23,23,44,FFはString)
以下のようになっていればいいようと思っています。
CANtxId = 0x130;
len = 8;
txBuf[8] = {0xFE,0x3F,0x2F,0x88,0x23,0x23,0x44,0xFF}

やったこと

以下のようにスクリプトを作成しました。

定義部

long unsigned int CANrxId, CANtxId; unsigned char len = 0; unsigned char rxBuf[8],txBuf[8]; String txData, charID, charData; char msgString[128]; // Array to store serial string int i = 0, i1 = 0, i2 = 0;

文字の分割処理部抜粋

i1 = txData.indexOf(","); charID = "0x"; charID = charID.concat(txData.substring(3,i1-2)); CANtxId = (long unsigned int)charID; i2 = txData.indexOf(",", i1+1); len = (unsigned char*)txData(i2-1); i1 = i2; while (i2 < 0){ i2 = txData.indexOf(",", i1+1); charData = "0x"; charData = charData.concat(txData.substring(i1+1, i2-1)); txBuf[i] = (byte)charData; i++; i1 = i2; }

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

D:\Arduino\CAN_Box3\CAN_Box3.ino: In function 'void loop()': D:\Arduino\CAN_Box3\CAN_Box3.ino:39:39: warning: invalid conversion from 'const char*' to 'char' [-fpermissive] txData = Serial.readStringUntil("\n"); ^ In file included from C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/HardwareSerial.h:29:0, from C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/Arduino.h:232, from C:\Users\�_�a\AppData\Local\Temp\arduino_build_334818\sketch\CAN_Box3.ino.cpp:1: C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/Stream.h:108:10: note: initializing argument 1 of 'String Stream::readStringUntil(char)' String readStringUntil(char terminator); ^ CAN_Box3:45:32: error: invalid cast from type 'String' to type 'long unsigned int' CANtxId = (long unsigned int)charID; ^ CAN_Box3:47:36: error: no match for call to '(String) (int)' len = (unsigned char*)txData(i2-1); ^ CAN_Box3:53:22: error: invalid cast from type 'String' to type 'byte {aka unsigned char}' txBuf[i] = (byte)charData; ^ 次のフォルダのライブラリMCP_CAN_lib-masterを使用中:D:\Arduino\libraries\MCP_CAN_lib-master (legacy) 次のフォルダのライブラリSPIバージョン1.0を使用中:C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\SPI exit status 1 invalid cast from type 'String' to type 'long unsigned int'

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

Windows10、
Arduino1.8.9

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

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

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

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

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

guest

回答3

0

ベストアンサー

キャストっていうのは値は変えずに型だけを変えるものですから、この場合はキャストではなくデータ変換でしょう。

とりあえず上から...

D:\Arduino\CAN_Box3\CAN_Box3.ino:39:39: warning: invalid conversion from 'const char*' to 'char' [-fpermissive] txData = Serial.readStringUntil("\n");

警告ではありますが、このままじゃきっと動きませんね。serial::readStringUntil()の引数は文字列ではなく文字です。
txData = Serial.readStringUntil('\n');

以下、PCからの文字列がどんなものが送られてくるのか記載がないので内容についてはわかりませんが、

CAN_Box3:45:32: error: invalid cast from type 'String' to type 'long unsigned int' CANtxId = (long unsigned int)charID;

String型をいくらキャストしたところで、文字列の内容を解釈してくれることはありません。というか、そもそも「文字列」を「整数」として解釈は出来ないのでキャストが不可能で、エラーになっています。
変換としては、10進数であればString::toInt()ってのが使えるのですがこれは16進を解釈してくれません。仕方ないのでCの関数でstrtoul(文字列型変数.c_str(),NULL,16)とすれば先頭に0xがあってもなくても(つまりわざわざ"0x"を付加する必要はない)16進数として文字列を解釈しますから、その辺を使って下さい。

CAN_Box3:47:36: error: no match for call to '(String) (int)' len = (unsigned char*)txData(i2-1);

なにをやりたいのか私には全くわかりません。文字列型の変数にカッコをつけるという謎。そしてそのなにかをunsigned char*にキャストしてunsigned char型の変数に代入しようという謎。
2個めのカンマの前一文字がデータ長を表しているということなら、統一的にstrtol()/strtoul()で処理してもいいですけれど、一文字取り出したのを文字列に再構成とかちょっと面倒です。
かならず一桁の値だということが確かなら
len = txData[i2-1]-'0';
でも十分、とも言えます。
(「数字」と「数値」の関係は把握していますか?)

CAN_Box3:53:22: error: invalid cast from type 'String' to type 'byte {aka unsigned char}' txBuf[i] = (byte)charData;

これも先程と同様。(byte)をつけても文字列の内容を解釈してくれるわけではありません。先程と同様にstrtoulあたりで「変換」した後、charにキャストするのが適切かと思います。

追記

通信を行う際には、原則として「エラー」の発生を無視してはいけません。ノイズであったり接続断であったり、いろいろな状況が考えられるのであらゆる場合に完全に対応というのは難しいですが、それでも容易に考えられる破滅的な状況は考慮すべきです。

今回、データ本体(データ長のあとの部分)をwhileループで取得しようとしています。ノイズか何かで改行コードを拾いそこなったらどうなりますか。次の行とデータが繋がって','が多いデータ列となり、容易にデータ数が8を超えてtxBuf[8]に対してバッファオーバーフローを起こし、プログラムの異常動作を引き起こしかねません。ここは、先に取得してあるlenを最大値としてループを回すべきでしょう。
一方、','をロストした場合にはデータ数が足りなくなります。多いにしろ少ないにしろ、取得したデータ数とlenの値を比較すればエラーの検出は出来ますが、エラーが検出された場合そのパケットをどうするか(単に無視する/捨てるか、再送リクエストの仕組みを作るか等)はシステム全体の動作に鑑みてあなたが決めて下さい。
それに関連して、lenは1~8という範囲が仕様に示されているのなら、その範囲にあることをチェックすべきです。

投稿2019/08/15 14:26

編集2019/08/17 00:30
thkana

総合スコア7629

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

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

HiroPokeHero

2019/08/16 07:15

ありがとうございます。 まず、txDataには文字列で今回編集した「実現したいこと」にしめした 文字列が入っています。 strtoulを使ってみます。 ちなみに文字列型変数.c_str()の.c_str()は何を意味するでしょうか? >文字列型の変数にカッコをつけるという謎 txData(i2-1)←これのことですよね? 正しくはtxData[i2-1]でしょうか? お恥ずかしながらいろんな言語を扱っていると言語間のルールが 頭の中で混同してしまうときがあります。
HiroPokeHero

2019/08/16 07:23

ほとんどのエラーがなくなりました。 .c_str()をつけることで改善が見込めました。 ※ 上記の改修にて、すべてのエラーがなくなりましたが、   lenに入っている値が狙いと異なっていたので、   ようやくご指摘の後半がわかりました。   この場合もstrtoulを使用しました。
thkana

2019/08/16 23:50

> 変数.c_str()は何を意味するでしょうか? Serial.print()とか普通に使ってますよね? 同じです。というか、これが何かわからないのは非常にマズイです。他人の使用例を見ただけで理解するのは難しいでしょうから、C++の入門書をパラパラとでいいので眺めておくことを強くオススメします(もちろん、必要だと思った部分は熟読して下さい)。 c_str()そのものについてはググればいくらでも情報があるでしょうから、まずはそちらを御覧ください。それでわからないという質問であれば出来る範囲でお答えします。 lenについては回答を編集します。
HiroPokeHero

2019/08/17 02:28

thkanaさん> いろいろありがとうございます。 ググってみます。 C++の入門書はかなり昔ですが、2,3冊一通りは読んで、 実際にトライもしていたのですが、 当時の参考書にはなかったのかもしれません。もしくは、私が失念しているほうが可能性は高いですかね。 また、追記の部分も今後パワーアップしていこうと思います。 まずは、やりたいことを実現して、その後想定される課題の潰しこみをしていくようなステップで考えています。 その際は相談させてください。
HiroPokeHero

2019/08/17 02:31

全然話が変わりますが、 本文ではなく、質問や解答のところでコードとして表示するのはどうすればいいでしょうか? 別のスレッドで、めっちゃ長くなってしまいました。
thkana

2019/08/17 08:07

> 当時の参考書にはなかったのかも あり得ません。クラスのメンバー関数の呼び出し方を書いていないC++の解説なんて。 > 本文ではなく、質問や回答のところでコードとして 質問や回答のところでなく「追記・修正依頼」や「コメント」のところで、ということですか? であれば私は知りません。内容によっては、追加検討したこと等として質問や回答を編集してそちらに含めてしまうのも手かと思いますが。
guest

0

文字列から整数に変換するのは、strtol 関数がありますね
まあ、2文字とか4文字のHex文字列なら、自作してしまうのもいいかと思いますよ

投稿2019/08/15 09:15

y_waiwai

総合スコア87747

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

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

HiroPokeHero

2019/08/16 07:24

ご連絡ありがとうございます。 strtolを検索してみました。 thkanaさんもおっしゃられているので、strtolとstrtoulを適宜使い分け用と思います。
guest

0

文字列の分割には関数indexOfと関数substringを使用しました。
また、文字列の分割にはうまくいきましたが、
このままでは文字列を数値として扱うことができないので、
関数strtoulを使用することで文字を変換することができました。

投稿2019/08/16 07:59

HiroPokeHero

総合スコア45

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.49%

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

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

質問する

関連した質問