シリアル書き込みスケッチのユーザー関数化その1


VS-RC003をArduinoのシリアル経由で操作するプロジェクトがまだ続いています。
一気に最後まで作ってしまうべきかもしれませんが、Arduinoの練習がてら完成まで少しずつ前進中です。

今回新たに試したことは、
・ユーザー定義関数を使う。
・ストリング関数を使う。
・配列、文字列、16進数などの相互変換を行う。
のあたりです。



今回定義したユーザー関数は、
VSRC_WnV(x,y)
です。
xには変数番号(0〜255)
yには数値データ(0〜65535)

をそれぞれ入れて使います。
変数番号(たとえばコントローラの接続設定は240)と数値データ(たとえばゲームパッド未接続なら0)を整数で指定して実行すると、アドレスと16進数リトルエンディアンを自動的に作成して書き込んでくれるという、VS-RC使いにとってはなかなか便利な関数です。
書き込みに成功すればtrue、失敗ならfalseを返してくれるようにしてみました。



今回はまだテストで、シリアルモニタで結果を見られるようになっています。
この後、このユーザー関数はarduino nanoなどでも使えるようにハードウェアシリアル(0番1番ピン)用に作り直し、モニタリング機能も排除する予定です。
また、コントロール関連専用のユーザー関数VSRC_CONTROL()や、読み込み専用のVSRC_READ()、ベリファイなしの書き込みVSRC_WRITE()なども同時に作成予定です。
自分的に難しいところは超えたので、あとは作業をしていくだけです。
一通り完成し、実際にロボットを安定的に動かすテストまでできれば、自分的にはVS-RCのシリアルは攻略完了となります。



以下、スケッチです。

// VS-RC003のシリアルを書き込み、ベリファイする関数のモニタリング付きスケッチ。
// シリアルでデータ書き込みコマンドを実行した後、該当箇所のデータを読み込んで、
// 書き込みが成功しているかを確認する。
//
// VSRC_WnV([変数番号(0~255)],[数値データ(0~65535)]) という形式の関数。
// WnVはWrite and Verifyの略。
// ユーザー変数は変数番号64~127。
// 変数番号0~63、128~155、238,239,248~250,253は読み込み専用のため書き込みはエラーとなる。
// 書き込んだ文と読み込んだ文が合致して入ればtrue、合致していなければfalseを返す。
//
// VS-RC003へはArduinoのシリアル1のRX,TXを接続。(MEGA,Leonardo,MICROに対応。UNO系は非対応。)

void setup() {
Serial.begin(115200); // 115200bpsでPCモニタ用ポートを開く
Serial1.begin(115200);// 115200bpsでVS-RC003通信用ポートを開く
}

void loop() {
bool w = VSRC_WnV(67, 3563); //ユーザー関数VSRC_WnV()を用いて、VSRC-003の変数番号64番に、3563を書き込む。
if (w == true) {//書き込みの成否を判定して、結果を表示。
Serial.println("Veriy OK!");
} else {
Serial.println("Write ERROR!");
}
Serial.println();
//while (1);
}


// ■ ■ ■ ■ 以下ユーザー関数 VSRC_WnV() の定義 ■ ■ ■ ■

bool VSRC_WnV(long vsrc_address, int vsrc_value) {

//■■■■変数の定義など■■■■
bool Loading = false; //必要データ読取中フラグ
char vsrc_value_buf[4];//書き込む数値の配列
vsrc_address = vsrc_address * 2 + 2099200;//変数番号のアドレスへの変換
String vsrc_address2 = String(vsrc_address, HEX);//アドレスの文字列への変換
String vsrc_value2 = String(vsrc_value, HEX);//代入数値を16進数表記の文字列に変換

//■■■■書き込み用の変数を配列に入れる■■■■
int i = 0;//代入数値(16進数表記4桁)を配列に入れる際のケタ番号
int i3 = 3 - (vsrc_value2.length());// 16進数4桁未満の時に、配列に0を何回入れるか
while (i <= i3) {//16進数4桁の冒頭が0で始まる場合、配列に0を入れて埋める処理
vsrc_value_buf[i] = 0x30;//配列に0を入れる
i++;//配列の番号を足す
}
int i2 = 0;//配列に入れる元の文字列の配列番号
while (i <= 3) {
vsrc_value_buf[i] = vsrc_value2.charAt(i2);//16進数4桁の文字列を、配列に入れる。
i++;//配列の番号を足す
i2++;//文字列の番号を足す
}
vsrc_value_buf[4] == 0X00;//ヌル終端を追加

//■■■■書き込み用のシリアル文をリトルエンディアンで組み立てる■■■■
String vsrc_write_conposit = String ("w " + vsrc_address2 + " " + vsrc_value_buf[2] + vsrc_value_buf[3] + " " + vsrc_value_buf[0] + vsrc_value_buf[1]);

//■■■■実際の書き込み■■■■
Serial.println("Serial1_write_command:");//モニタ用。
Serial.println(vsrc_write_conposit);//モニタ用。書き込んだ文字列を表示する。
Serial1.println(vsrc_write_conposit);//実際に読み込み用のライト文を送信。

//■■■■書き込み完了確認■■■■
delay(38);//18以下だと失敗する。すこし余裕をもたせるべき。
if (Serial1.available() > 0 ) {//受信シリアルのプロンプト「#」を待つ。
for (int i = 0; i <= 64; i++) {//「#」を受け取るまで64文字まで待つ。普通は1発目で見つける。
char data = Serial1.read();
if (data == 0x23)//「#」を受け取り次第抜ける。
{
i = 64;
}
}

//■■■■ベリファイ用のシリアル文を送信する■■■■
String vsrc_read_conposit = String ("r " + vsrc_address2 + " 02");//書き込んだアドレスの数値を読み込むための文章組み立て
Serial.println("Serial1 vsrc_read_command:");//モニタ用。
Serial.println(vsrc_read_conposit);//モニタ用。ベリファイ用のシリアル文
Serial1.println(vsrc_read_conposit);//ベリファイ用のシリアル文を送信する。
delay(30);//このディレイが小さすぎると失敗しやすい。

//■■■■ベリファイ用の読み込み文を組み立てる■■■■
char VSmsgs[64];// 読み取ったシリアルを入れる配列を宣言。
if (Serial1.available() > 0 ) {
int i = 0; //文字配列カウンタ
while (1) {
char data = Serial1.read();
VSmsgs[i] = data; //文字配列に文字を一個入れる。
if (VSmsgs[i - 1] == 0x0a && VSmsgs[i] == 0x23)// 「[LF]」と「#」が連続している文字列を探す。
{
Loading = true; //必要データ読取中のフラグを立てる。
//取得するデータの冒頭を「#」から「w 」に変換して書式を合わせておき、あとで比較できるようにする。
VSmsgs[0] = 0x77; //配列の1文字目にwを入れ直しておく。
VSmsgs[1] = 0x20; //配列の2文字目にスペースを入れ直しておく。
i = 1;
}
if (VSmsgs[i] == 0x0D && Loading == true)//必要データ読取中かつ「CR」が来たら読取終了。
{
Loading = false;
break;
}
i ++;
}
VSmsgs[i] = 0x00;//データにヌル終端を追加

Serial.println("serial1 read data:");
String VSmsgssrg = String(VSmsgs);//読み込んだ配列を文字列に変換
VSmsgssrg.trim();//変換時に発生した空白文字をトリム
Serial.println(VSmsgssrg);//モニタ用。読み込んだデータ(#をw に書き換えたもの)を表示する。

//■■■■書き込んだデータを読み込んだデータを文字列で比較する■■■■
bool Verify = vsrc_write_conposit.equalsIgnoreCase(VSmsgssrg);//大文字小文字を無視して文字列を比較する。合致ならtrue
return Verify;
}
}
}




実行したシリアルモニタは下記のようになります。


Serial1_write_command:
w 200886 eb 0d
Serial1 vsrc_read_command:
r 200886 02
serial1 read data:
w 200886 eb 0d
Veriy OK!
スポンサーサイト

テーマ : 自然科学
ジャンル : 学問・文化・芸術

シリアル読取&書き込みスケッチのリファイン


見えるぞ!私にもシリアルが見える!!




VS-RC003→Arduinoのシリアル解読」でVS-RC003のデータ表示に挑戦した後、
シリアル検証スケッチ」でシリアルの厳密な配列が判明してきました。
前回やや当てずっぽうだった必要データの切り出し方法を厳密なものに改め、スケッチを書き直してみました。
さらに、書き込んでベリファイするものも書いてみました。
だいぶスッキリしました。




PCでモニタリングするプログラムのため、
Arduinoはハードウェアシリアルが使えるMEGAがおすすめです。
Serial1が使えるMICROやLeonardoでも動くと思います。
VS-RC003の設定で好きな方をシリアルに設定し、また、
ArduinoのRXピン、TXピン、5V、GNDを、VS-RC003のTXピン、RXピン、5V,GNDにそれぞれ接続します。




補足ですが、VS-RC003の拡張コネクタは、
CN6:ゲームパッド使える/IXBUS使えない/シリアル使える
CN7:ゲームパッド使えない/IXBUS使える/シリアル使える

となっているようです。
IXBUS機器を複数使う場合CN7に数珠つなぎにします。
CN6にはコントロールパッドの代わりにシリアルをつなぐことが可能です。




まず読み取りです。
電圧の取得を例にします。
指定したアドレスのデータを読み込み、データを表示。
さらにエンディアンを交換し、整数データとして表示するところまでを行います。

// VS-RC003のシリアルを読み取るプログラム作例
// rコマンドで読み出し命令を出し、結果を変数VSmsgsに入れて表示する。
// 例では、r 2009dc 04で電圧を読み取る。
// 2009dc 02 00 xx yy が読み出されるが、xx yy の部分が、該当の電圧データとなる。
// また、リトルエンディアンからビッグエンティアンへ、
// さらにアスキーコードの16進数を数値に変換し、10進数に換算する。
// データの切り出し方を厳密化。

int vcc1, vcc2, vcc3, vcc4;// 換算用の変数を宣言。
bool ERR = false;// 読み取りエラーのフラグ用変数を宣言。
bool Loading = false; //必要データ読取中フラグ

void setup() {
 Serial.begin(115200); // 115200bpsでPCモニタ用ポートを開く
 Serial1.begin(115200);// 115200bpsでVS-RC003通信用ポートを開く
}

void loop() {
 char VSmsgs[64];// 読み取ったシリアルを入れる配列を宣言。
 Serial1.println("r 2009dc 04");//電圧を読み込む命令
 delay(30);//このディレイが小さすぎると失敗しやすい。
 if (Serial1.available() > 0 ) {
   int i = 0; //文字配列カウンタ
   while (1) {
     char data = Serial1.read();
     VSmsgs[i] = data; //文字配列に文字を一個入れる。
     if (VSmsgs[i - 1] == 0x0a && VSmsgs[i] == 0x23)// 「[LF]」と「#」が連続している文字列を探す。
     {
       Loading = true; //必要データ読取中のフラグを立てる。
       VSmsgs[0] = 0x23; //配列の1文字目にプロンプト記号#を入れ直しておく。
       i = 0;
     }
     if (VSmsgs[i] == 0x0D && Loading == true)//必要データ読取中なら「CR」が来たら読取終了。
     {
       Loading = false;
       break;
     }
     i ++;
   }

   VSmsgs[i] = 0x00;//データにヌル終端を追加
   Serial.println(VSmsgs);//取得データを表示。
   Serial.print("Big endian : ");//以下、該当の電圧データをエンディアンを交換して表示する。
   Serial.print(VSmsgs[17]);
   Serial.print(VSmsgs[18]);
   Serial.print(" ");
   Serial.print(VSmsgs[14]);
   Serial.println(VSmsgs[15]);
   Serial.print("ASCII to HEX to DEC : ");

   //以下、取得したASCIIコードを16進数に換算していく。
   //その際、範囲にない文字であれば、エラーフラグを立てる。
   if (VSmsgs[17] <= 0x2f) {//取得した文字が0よりも前の文字であればエラー。
     ERR = true;
   } else if (VSmsgs[17] >= 0x67) {//取得した文字がfよりも後の文字であればエラー。
     ERR = true;
   } else if (VSmsgs[17] >= 0x41) {
     vcc1 = VSmsgs[17] - 0x57;//取得した文字がa以上であれば10からの数字に換算。
   } else {
     vcc1 = VSmsgs[17] - 0x30;//それ以外であれば、0から9の数字に換算。
   }

   if (VSmsgs[18] <= 0x2f) {
     ERR = true;
   } else if (VSmsgs[18] >= 0x67) {
     ERR = true;
   } else if (VSmsgs[18] >= 0x41) {
     vcc2 = VSmsgs[18] - 0x57;
   } else {
     vcc2 = VSmsgs[18] - 0x30;
   }

   if (VSmsgs[14] <= 0x2f) {
     ERR = true;
   } else if (VSmsgs[14] >= 0x67) {
     ERR = true;
   } else if (VSmsgs[14] >= 0x41) {
     vcc3 = VSmsgs[14] - 0x57;
   } else {
     vcc3 = VSmsgs[14] - 0x30;
   }

   if (VSmsgs[15] <= 0x2f) {
     ERR = true;
   } else if (VSmsgs[15] >= 0x67) {
     ERR = true;
   } else if (VSmsgs[15] >= 0x41) {
     vcc4 = VSmsgs[15] - 0x57;
   } else {
     vcc4 = VSmsgs[15] - 0x30;
   }

   Serial.print(vcc1, DEC);//ビッグエンディアンに変更後の16進数を並べる。
   Serial.print(" ");
   Serial.print(vcc2, DEC);
   Serial.print(" ");
   Serial.print(vcc3, DEC);
   Serial.print(" ");
   Serial.println(vcc4, DEC);
   Serial.print("VCC : ");
   int vcc = vcc1 * 4096 + vcc2 * 256 + vcc3 * 16 + vcc4;//16進数を10進数に換算する。
   Serial.print(vcc, DEC);//結果の数値を表示する。
   Serial.println();
   //Serial1.flush();
   if (ERR == true) {//取得データにエラーが出ていればエラーと表示する。
     Serial.println("*** READ ERROR!! ***");
     ERR = false;
   }
     Serial.println();
   delay (1);
 }
}




実行結果は、

#2009dc 02 00 98 0f
Big endian : 0f 98
ASCII to HEX to DEC : 0 15 9 8
VCC : 3992

#2009dc 02 00 8f 0f
Big endian : 0f 8f
ASCII to HEX to DEC : 0 15 8 15
VCC : 3983
(以降くりかえし)


のように表示されます。




次に書き込みです。
VS-RC003にシリアルで指定アドレスに変数を書き込みます。
同じアドレスを読み取って比較することで書き込みの成否までを確認します。

// VS-RC003のシリアルを書き込み、ベリファイするスケッチ例
// シリアルでデータ書き込みコマンドを実行した後、該当箇所のデータを読み込んで、
// 書き込みが成功しているかを確認する。
// WRITE_MSG1で書き込み用のw文を指定し、
// WRITE_MSG2で書き込み検証用にデータを読み込むr文を指定する。
// 読み込みデータは#200...で始まるが、w 20..になるように書き換え、比較しやすくしておく。
// 書き込んだ文と読み込んだ文が合致して入れば、Verify OK!を表示する。

bool Loading = false; //必要データ読取中フラグ

String WRITE_MSG1 = "w 2009f6 01 00"; //書き込み用のライト文
String WRITE_MSG2 = "r 2009f6 02"; //検証用のリード文。該当アドレスから2バイト読み込む指示。

void setup() {
 Serial.begin(115200); // 115200bpsでPCモニタ用ポートを開く
 Serial1.begin(115200);// 115200bpsでVS-RC003通信用ポートを開く
}

void loop() {
 char VSmsgs[64];// 読み取ったシリアルを入れる配列を宣言。
 Serial1.println(WRITE_MSG1);//書き込み用のライト文を送信。
 Serial.print("wrote:");//モニタ用。
 Serial.println(WRITE_MSG1);//モニタ用書き込んだ文字列を表示する。
 delay(38);//18以下だと失敗する。すこし余裕をもたせるべき。
 if (Serial1.available() > 0 ) {
   for (int i = 0; i <= 64; i++) {//「#」を受け取るまで64文字まで待つ。普通は1発目で見つける。
     char data = Serial1.read();
     if (data == 0x23)//「#」を受け取り次第抜ける。
     {
       i = 64;
     }
   }
   Serial1.println(WRITE_MSG2);//検証用のリード文
   delay(30);//このディレイが小さすぎると失敗しやすい。
   if (Serial1.available() > 0 ) {
     int i = 0; //文字配列カウンタ
     while (1) {
       char data = Serial1.read();
       VSmsgs[i] = data; //文字配列に文字を一個入れる。
       if (VSmsgs[i - 1] == 0x0a && VSmsgs[i] == 0x23)// 「[LF]」と「#」が連続している文字列を探す。
       {
         Loading = true; //必要データ読取中のフラグを立てる。
         //取得するデータの冒頭を「#」から「w 」に変換して書式を合わせておき、あとで比較できるようにする。
         VSmsgs[0] = 0x77; //配列の1文字目にwを入れ直しておく。
         VSmsgs[1] = 0x20; //配列の2文字目にスペースを入れ直しておく。
         i = 1;
       }
       if (VSmsgs[i] == 0x0D && Loading == true)//必要データ読取中かつ「CR」が来たら読取終了。
       {
         Loading = false;
         break;
       }
       i ++;
     }

     VSmsgs[i] = 0x00;//データにヌル終端を追加
     Serial.print("read :");
     Serial.println(VSmsgs);//モニタ用。読み込んだデータ(#をw に書き換えたもの)を表示する。
     String VSmsgssrg = VSmsgs;
     if (WRITE_MSG1.compareTo(VSmsgssrg)) {//書き込んだ文字列と読み込んだ文字列を比較する。
       Serial.println("Veriy OK!");
     } else {
       Serial.println("Write ERROR!");
     }
     Serial.println();
     delay (1);
   }
 }
}




こちらの実行結果は、


wrote:w 2009f6 01 00
read :w 2009f6 01 00
Veriy OK!

wrote:w 2009f6 01 00
read :w 2009f6 01 00
Veriy OK!
(以降くりかえし)


のように表示されます。




実際にロボットと接続してみて安定的に動くようであれば、ベリファイの部分を省略してしまうのもArduinoへの負担が減ってよいかと思います。
ついでにユーザー関数化やライブラリ化ができれば運用しやすいのですが、そのやり方はまだいまいちわかっていません><




これでついにVS-RC003&Arduinoのシリアル制御を完全攻略!
・・・ができたような気がします^^
ここまでできれば、SBDBTのファームを書き換えてVS-RC003に直付けすることもできてしまいそうです。
(これはライト級的に欲しい機能です。)

テーマ : 自然科学
ジャンル : 学問・文化・芸術

VS-RC003変数番地計算機


VS-RC003の変数番号を、VS-RC003がシリアル受信で理解できる番地に変換するプログラムのarduino版です。
使いたい変数を設定したスケッチをArduinoにアップすると、シリアルモニタに答えを表示します。
あとで必要になりそうな単機能のスケッチです。




//使いたい変数番号を設定すると、PCのシリアルモニタに番地を
//教えてくれるスケッチ
//変数番号のアドレスへの換算方法は、
//16進数で、「200800 + 変数番号の2倍」

long val_num = 240;//いじりたい変数番号を設定。

void setup() {
 Serial.begin(115200);
}

void loop() {
 //変数番号を2倍して、200800を10進数換算をしたものを足す。
 val_num = val_num * 2 + 2099200;
 Serial.print(val_num, HEX);  //16進数で表示する。
 while (1);
}

テーマ : 自然科学
ジャンル : 学問・文化・芸術

シリアル検証スケッチ


VS-RC003のシリアル関連についてですが、前回のスケッチでは動くもののデータの切り出し方にちょっと無駄がありました。
シリアルの情報は公式に開示されているのですが、細かな点が素人にはちょっとわかりづらいです。
コールバック、#プロンプト、[CR]、[LR]の受信順について厳密に理解しておくため、改めて専用のスケッチを作って検証しておきたいと思います。いそがばまわれ。




使うのは例によってハードウェアシリアルの安定性も高いMEGAです。
Serial1のTX,RXピンをVS-RC003のRX,TXとそれぞれ繋ぎます。
LeonardoやMICROでもたぶん大丈夫だと思います。


//VS-RC003等から送られてくるデータの順番を可視化するプログラム。
//特例として、復帰記号[CR]は「C」、改行記号[LF]は「L」として可視化する。

int LEN = 128;//読み込むシリアル文字列の長さ。仮に決める。
String WRITE_MSG ="r 2009dc 04";//送信したいシリアル文字列はここで指定する。

void setup() {
 Serial.begin(115200); // 115200bpsでPCモニタ用ポートを開く
 Serial1.begin(115200);// 115200bpsでVS-RC003通信用ポートを開く
}

void loop() {
 char VSmsgs[LEN];// 読み取ったシリアルを入れる配列を宣言。
 Serial1.println(WRITE_MSG);//こちらから送信するシリアル命令。
 delay(30);//このディレイが小さすぎると失敗しやすい。
 //if (Serial1.available() > 0 ) {
   for (int i = 0; i < LEN; i++) {
     char data = Serial1.read();
     if (data == 0x0d) {//受信した文字が復帰記号[CR]だったら配列に「C」を代入して可視化する。
       VSmsgs[i] = 0x43;
     }
     else if (data == 0x0a) {//受信した文字が改行記号[LF]だったら配列「L」を代入して可視化する。
       VSmsgs[i] = 0x4c;
     }
     else {
       VSmsgs[i] = data;//受信した文字が上記以外なら、そのまま配列に入れる。
     }
   }
 //}
 Serial.println("Arduino read...");
 Serial.println(VSmsgs);//取得データを表示。
 Serial.println("from Serial1.");
 Serial.println();
 delay (1000);
}




まず、読み込みのテストです。

r 2009dc 04[CR][LR]を送信すると、帰ってくるのは、

Arduino read...
r 2009dc 04C#L
from Serial1.

Arduino read...
r 2009dc 04C#L#2009dc 02 00 d2 0f CL
from Serial1.

Arduino read...
r 2009dc 04C#L#2009dc 02 00 d2 0f CL
from Serial1.
(以降繰り返し)

という結果になりました。
Cは[CR]、Lは[LF]を示します。
1発目はVS-RC003からの返信がこないことが多いので扱いに注意が必要ということがわかります。
最初の#は、VS-RC003からのコマンド受領合図です。[CR]を受け取るとまずこの#を返してきます。
また、2回目の#よりも前の文字列は、送信したコマンドのコールバック(受領したデータを一文字ずつ返してくる仕様)になりますので、データとしては不要なものです。
[LF]#と2バイト連なる文字列を受け取ってから、次の[CR]が来るまでの文字列を読み取るようにプログラムすれば、欲しいデータを切り出せそうです。



次に、書き込みのテストです。

w 2009f6 01 00を送信すると、帰ってくるのは、

Arduino read...
w 2009f6 01 00C#L
from Serial1.

Arduino read...
w 2009f6 01 00C#L
from Serial1.

Arduino read...
w 2009f6 01 00C#L
from Serial1.
(以降繰り返し)

という結果になりました。
書き込みコマンドを受領したVS-RC003は、[CR]を受け取ってすぐに#を返してくれることがわかります。
書き込みが成功しているかのチェックは、もう一度該当箇所を読み込んで比較するのが良さそうです。



そこで、書き込んだ後に読み込み実行するスケッチも簡易的に書いてみました。
こちらを実行すると、ずらずらと改行なしで結果を出力します。
どういう順番でシリアルが帰ってくるのでしょうか。


//VS-RC003等から送られてくるデータの順番を可視化するプログラム。
//特例として、復帰記号[CR]は「C」、改行記号[LF]は「L」として可視化する。

int LEN = 128;//読み込むシリアル文字列の長さ。仮に決める。
String WRITE_MSG1 = "w 2009f6 00 00"; //送信したいシリアル文字列その1を指定。
String WRITE_MSG2 = "r 2009f6 02"; //送信したいシリアル文字列その2を指定。
void setup() {
 Serial.begin(115200); // 115200bpsでPCモニタ用ポートを開く
 Serial1.begin(115200);// 115200bpsでVS-RC003通信用ポートを開く
}

void loop() {
 char VSmsgs1[LEN];// 読み取ったシリアルを入れる配列を宣言。
 Serial1.println(WRITE_MSG1);//こちらから送信するシリアル命令。
 delay(30);//このディレイが小さすぎると失敗しやすい。
 for (int i = 0; i < LEN; i++) {
   char data = Serial1.read();
   if (data == 0x0d) {//受信した文字が復帰記号[CR]だったら配列に「C」を代入して可視化する。
     VSmsgs1[i] = 0x43;
   }
   else if (data == 0x0a) {//受信した文字が改行記号[LF]だったら配列「L」を代入して可視化する。
     VSmsgs1[i] = 0x4c;
   }
   else {
     VSmsgs1[i] = data;//受信した文字が上記以外なら、そのまま配列に入れる。
   }
 }

 char VSmsgs2[LEN];// 読み取ったシリアルを入れる配列を宣言。
 Serial1.println(WRITE_MSG2);//こちらから送信するシリアル命令。
 delay(30);//このディレイが小さすぎると失敗しやすい。
 for (int i = 0; i < LEN; i++) {
   char data = Serial1.read();
   if (data == 0x0d) {//受信した文字が復帰記号[CR]だったら配列に「C」を代入して可視化する。
     VSmsgs2[i] = 0x43;
   }
   else if (data == 0x0a) {//受信した文字が改行記号[LF]だったら配列「L」を代入して可視化する。
     VSmsgs2[i] = 0x4c;
   }
   else {
     VSmsgs2[i] = data;//受信した文字が上記以外なら、そのまま配列に入れる。
   }
 }
 Serial.print(VSmsgs1);//取得データを改行なしで表示。
 Serial.print(VSmsgs2);//取得データを改行なしで表示。
 delay (100);
}



実行結果は、
w 2009f6 00 00C#Lr 2009f6 02C#L#2009f6 00 00 CLw 2009f6 00 00C#Lr 2009f6 02C#L#2009f6 00 00 CLw 2009f6 00 00C#Lr 2009f6 02C#L#2009f6 00 00 CL (以下繰り返し)
となります。

ここから上手に該当箇所を切り出すことができれば、ArduinoとVS-RC003は晴れて通信開通となる・・・はずです。
パズルゲームを遊んでいるようで、面倒ながらも楽しいです^^

テーマ : 自然科学
ジャンル : 学問・文化・芸術

VS-RC003→Arduinoのシリアル解読


以前挫折してほったからしになっていた案件。
ArduinoとVS-RC003の安定したシリアル通信について、突然思い立ってリベンジ中です。

前回の挫折からもう3年も経っているのが驚きです。
ブログではあまり書いてきませんでしたが言語の勉強はチクチクと進めており、C言語、RASPBERRY PI、ROS、LINUX、PYTHONなどの入門書を2冊ぐらいずつ読んだところです。入門書ばかりで何も身にはついていませんが、少しは理解力が向上していると信じたいです。




今回のリベンジでは純粋にソフトウェアのことに集中できるよう、万全を期してArduino MEGAを購入しました。
Arduino MEGAはやや高いのですがハードウェアシリアルを複数扱うことができます。
ハードウェアシリアルが複数あることで、115200のスピードでVS-RC003とのシリアル通信の実験をしながら、
同時にUSB経由でPCでモニンタリングもできるというわけです!
ハードウェアの限界に悩まされなければ、あとはソフトの話なのでパズルゲームのように楽しめるはず・・・です。




ArduinoからVS-RC003に命令を書き込む場合には、115200のスピードのシリアルで文字列を一方的に送りつければ済む話です。あとは安定性をアップさせるための仕掛けをいれれば大丈夫でしょう。

一方、読み取りの方はグッと難易度があがります。
今回はシリアルの読み取りから攻略してみます。




読み取りにはクリアすべきいろいろな難題があります。

・VS-RC003は送った命令をエコーバックしてくるが、データとの区別はどうするか?
・Arduinoが扱えるシリアルデータは1バイトずつだが、文字列のシリアルパケットをどう受け取るか?
・リトルエンティアンのビックエンディアンへの入れ替えは?
・ASCIIコードから16進数への変換は?
・16進数から10進数への変換は?

などなど。絡まったテグスを解きほぐすように、順番に解決していく必要があります。




さっそく、VS-RC003の信号をArduinoで読み取るためのスケッチを書いて見ました。
シリアル仕様マニュアルの例にもある電圧の読み取りに挑戦です。
作業の途中経過がわかるよう、

・読み取った信号
・該当箇所のデータをビッグエンディアンに変更したもの
・それを10進数に置き換えたもの
・さらに換算し、電圧の数値として読み取れるように直したもの

のそれぞれを表示するようにしてみました。




実際のコードです。


// VS-RC003のシリアルを読み取るプログラム作例
// rコマンドで読み出し命令を出し、結果を変数VSmsgsに入れて表示する。
// 例では、r 2009dc 04で電圧を読み取る。
// 2009dc 02 00 xx yy が読み出されるが、xx yy の部分が、該当の電圧データとなる。
// また、リトルエンディアンからビッグエンティアンへ、
// さらにアスキーコードの16進数を数値に変換し、10進数に換算する。
// ArduinoのTX,RX(MEGAなら18,19番ピン、LeonardoやMICROなら1,0番ピン)を
// VS-RC003のRX,TXにそれぞれ配線する。5VとGNDもそれぞれ接続する。
// ※ UNO系等ではハードウェアシリアル(Serial1.begin())が使えないので注意。

int vcc1, vcc2, vcc3, vcc4;// 換算用の変数を宣言。
bool ERR;// 読み取りエラーのフラグ用変数を宣言。

void setup() {
Serial.begin(115200); // 115200bpsでPCモニタ用ポートを開く
Serial1.begin(115200);// 115200bpsでVS-RC003通信用ポートを開く
}

void loop() {
char VSmsgs[64];// 読み取ったシリアルを入れる配列を宣言。
Serial1.println("r 2009dc 04");//電圧を読み込む命令
delay(30);//このディレイが小さすぎると失敗しやすい。
if (Serial1.available() > 0 ) {
int i = 0; //文字配列カウンタ
int CRcounter = 0; //CR記号カウンタ。これが2回出たら1パケット終了。
int SHcounter = 0; //#記号カウンタ。これの2回目から本データ取得。(それまではエコーバックの文字列ということ。)
while (1) {
char data = Serial1.read();
VSmsgs[i] = data; //文字配列に文字を一個入れる。
i ++;
if (data == 0x23) { //#文字が出てきたら#カウンタを加算。
SHcounter ++;
if (SHcounter >= 2) {
i = 0; //#カウンタが2になったら、コールバック終了なので改めてデータを取る。
}
}
if (data == 0x0D) { //改行文字が出てきたらCRカウンタを加算。
CRcounter ++;
if (CRcounter >= 2) { //2回目の改行文字が出てきたら、データ取得完了し、換算と表示処理へ。
VSmsgs[i] = 0x00;//データにヌル終端を追加
Serial.println(VSmsgs);//取得データを表示。
Serial.print("Big endian : ");//以下、該当の電圧データをエンディアンを交換して表示する。
Serial.print(VSmsgs[16]);
Serial.print(VSmsgs[17]);
Serial.print(" ");
Serial.print(VSmsgs[13]);
Serial.println(VSmsgs[14]);
Serial.print("ASCII to HEX to DEC : ");

//以下、取得したASCIIコードを16進数に換算していく。
//その際、範囲にない文字であれば、エラーフラグを立てる。
if (VSmsgs[16] <= 0x2f) {//取得した文字が0よりも前の文字であればエラー。
ERR = 1;
}
else if (VSmsgs[16] >= 0x67) {//取得した文字がfよりも後の文字であればエラー。
ERR = 1;
}
else if (VSmsgs[16] >= 0x41) {
vcc1 = VSmsgs[16] - 0x57;//取得した文字がa以上であれば10からの数字に換算。
} else
{
vcc1 = VSmsgs[16] - 0x30;//それ以外であれば、0から9の数字に換算。
}

if (VSmsgs[17] <= 0x2f) {
ERR = 1;
}
else if (VSmsgs[17] >= 0x67) {
ERR = 1;
}
else if (VSmsgs[17] >= 0x41) {
vcc2 = VSmsgs[17] - 0x57;
} else
{
vcc2 = VSmsgs[17] - 0x30;
}

if (VSmsgs[13] <= 0x2f) {
ERR = 1;
}
else if (VSmsgs[13] >= 0x67) {
ERR = 1;
}
else if (VSmsgs[13] >= 0x41) {
vcc3 = VSmsgs[13] - 0x57;
} else
{
vcc3 = VSmsgs[13] - 0x30;
}

if (VSmsgs[14] <= 0x2f) {
ERR = 1;
}
else if (VSmsgs[14] >= 0x67) {
ERR = 1;
}
else if (VSmsgs[14] >= 0x41) {
vcc4 = VSmsgs[14] - 0x57;
} else
{
vcc4 = VSmsgs[14] - 0x30;
}

Serial.print(vcc1, DEC);//ビッグエンディアンに変更後の16進数を並べる。
Serial.print(" ");
Serial.print(vcc2, DEC);
Serial.print(" ");
Serial.print(vcc3, DEC);
Serial.print(" ");
Serial.println(vcc4, DEC);
Serial.print("VCC : ");
int vcc = vcc1 * 4096 + vcc2 * 256 + vcc3 * 16 + vcc4;//16進数を10進数に換算する。
Serial.print(vcc, DEC);//結果の数値を表示する。
Serial.println();
Serial1.flush();
break;
if (ERR = 1) {//取得データにエラーが出ていればエラーと表示する。
Serial.println("*** READ ERROR!! ***");
ERR = 0;
delay (100);
}
}
}
}
}
Serial.println();
delay (1);
}


実行結果は、PCシリアルモニタで見ると


2009dc 02 00 3c 0e
Big endian : 0e 3c
ASCII to HEX to DEC : 0 14 3 12
VCC : 3644

2009dc 02 00 33 0e
Big endian : 0e 33
ASCII to HEX to DEC : 0 14 3 3
VCC : 3635

2009dc 02 00 29 0e
Big endian : 0e 29
ASCII to HEX to DEC : 0 14 2 9
VCC : 3625
・・・


のような感じになります。
たぶん、結果の換算もうまくいっていると思います。

スケッチ文はかなり雑で下手くそですが、とりあえず基本ができてきたと思います。
これをベースに、使える関数にしていきたいと思います。

テーマ : 自然科学
ジャンル : 学問・文化・芸術

プロフィール

二名川(ニナガワ)

Author:二名川(ニナガワ)
ホビーロボットをレトロゲームが発展したものと捉えて楽しく遊び倒します。
子供が夢を見ている時間帯に稼働します。

宣伝:電子出版しました。
「コンソロイド ガイドブック」
46107_CONSOLOID_GUID_FACE_200.jpg





■作成中の機体
汎用ヒト型決戦遊具 ~RX計画~
RX-7.5 ゼロタンク
RRf-0.6 ゼニィ
RXM-7.9 ゼムネス
RX-7.5R 量産型ゼロタンク
RX-7.5Fp ファミタンク仮設1号
RX-7.7 ゼロキャノン
RX-7.8 ゼログレイ
SMS-0.1 ゼロライナー
以下続く

ブログ内検索
最近の記事
最近のコメント
カテゴリ
月別アーカイブ
リンク