Consoleアプリでシリアル受信を本格的に組むのは初めてなので、少してこずりました。
●C#コンソールアプリでシリアル受信をシングルタスクで実現しました。
今までは。フォームアプリだったので、ボタンなどいじりながら受信するので、マルチタスクで、スレッドの管理をするために、Invokeとdelegateを組まないといけなかったので、サンプルプログラムをコピペして使いまわしてきました。
3年たっても、Invokeとdelegateの意味が解らないので、一生自前で、マルチスレッドのプログラムは組めないと諦めてます。しかし、コンソールアプリなら、シングルタスクでもシリアル受信高速で十分使えます。
36MBのバイナリが、マルチスレッドのSerialDataRecived_Handler()で受信した場合で17Mbpsでしたが、シングルタスクで、受信部分を書いたプログラムでも36MBのバイナリ17Mbpsでてますので、マルチスレッドと同等の速度が実現できてました。
●やり方の説明
Teensy4.1のUSBシリアル(Serial.print) baudrate設定は無視されて数十MpbsでるのSDカードのファPCへアップロードする経路とします。
Serial2をUSBシリアルアダプタ 115200bpsでデバッグ用のモニターとします。
(秋月のFT234X 超小型USBシリアル変換モジュールで接続)でモニタしながらデバッグします。
https://akizukidenshi.com/catalog/g/g108461/
マイクロUSBコネクタを使ってないのは、スキーに装着するBOXが狭いのでコネクタが入るすきまがないからです。
コネクタではなく、TeensyのUSBパッドにUSBケーブルを直付してます。直付の結線が要注意なので、こちらの記事に注意点メモしてあります。
【STA24】USBケーブルばらしてD+D-の緑白バラバラ<WEB解説もバラバラ>
●デバッグ環境
下図、左がTeraTermでSerial2をモニターしています。C#プログラムから送ったファイル名が正しいかとかチェックします。
真ん中がVisualStudioでC#コンソール画面です。SDカードのDIRのリストをマイコンから読み込んで、データとして使えるようにファイル名とファイルサイズを配列として整理してあります。
右のバイナリーエディタの元ファイルと比較しながらデバッグを進めます。
DIRリストを整理して表示して、アップロードしたいファイル番号をキー入力してマイコンに送信するとマイコン側がSDカードから読み取って、USB経由で送信してきます。それを受信して、受信サイズと時間を測定して速度を計算します。全部プリントすると何分もかかるので、表示は、確認用に、データの最初100バイトと最後の100バイトさけプリントして、実際のファイルのバイナリデータを比較してデータがきちんと送受信されているか、
36MByteのファイルを16秒で受信できました。計算すると17Mbpsでてました。画面に表示すると遅くなるので、表示せずにメモリー上の配列に保存してから、後でプリントアウトします。プリントをすると時間は5-10分かかってしまいます。
C#のコードはこちら
https://gist.github.com/dj1711572002/968d5ac0da8bf4bf195100ed6ab2a431
●シングルタスク用のシリアル受信部
こちらの記事をそのままコピペさせていただきました。感謝です。
C#のシリアルで、マルチスレッドない事例はここだけしか見当たらなかったので、貴重な記事です。
https://jp-seemore.com/sys/17629/
下表に書かれている送受信部これだけでできます。マルチスレッドでないので、これをmainにコピペして
シリアルの前準備でOPENさえしておけば、送受信できます。準備も解説の最初に書かれてます。
受信のポイント1:bytesToRead(整数)でバッファ内に残っているデータのバイト数が判ります。
受信のポイント2:receivedData[]バイト配列をbytesToRead個分だけ宣言します。
受信のポイント3;serialPort.read(配列、開始、全数)でバッファのデータを配列へ読み込みます。
受信のポイント4:byte配列をstringに変換します。ASCIIで送られてきたという前提の行なので、プリント用変換です
// データ送信
string dataToSend = “Hello Serial”; serialPort.Write(dataToSend); // データ受信 int bytesToRead = serialPort.BytesToRead; byte[] receivedData = new byte[bytesToRead]; serialPort.Read(receivedData, 0, bytesToRead); string receivedString = Encoding.ASCII.GetString(receivedData); Console.WriteLine(“Received data: “ + receivedString); |
●私の改良版 binary配列に追記する改良
巨大なデータなので、いちいちプリントアウトしないで、バイナリのまま
配列に詰め込んでしまって後から処理します。
工夫点1:バイナリファイルは、どこが終わりかわからないのが欠点ですが、このアップローダーの場合は
事前に、ファイルDIRリスト作成してあって各ファイルの容量が判っているので、ファイル名を指定して、ファイルの容量分だけ受信ループを回して受信するという受信方法をして、ファイルを安全に間違いなくアップロードさせてます。
工夫点2:上記を実現するために、ファイルDIRの解析部分に手間がかかってますファイル番号fileNとfname[]とfseize[]の2種類の配列で、ファイルDIRの管理をしてます。
1:バイナリーデータ受信部
シリアル受信方法で、最もシンプルな方式です。
int fs = Convert.ToInt32(fsize[no]); //シングルタスク シリアル受信部 byte配列格納 while (sum < fs) { bytesToRead = mySerialPort.BytesToRead;//バッファに残っているバイト数 byte[] receivedData = new byte[bytesToRead];//配列宣言 mySerialPort.Read(datab, sum, bytesToRead);//Readして配列格納、スタート位置sum sum = sum + bytesToRead;//位置インクリメント } sw.Stop(); long sa = sw.ElapsedMilliseconds; Console.WriteLine(); double speed = sum / sa *8/1000; Console.WriteLine(“dlen=” + sum.ToString() + “Time=” + sa.ToString() + “msec”+”,speed=”+speed.ToString()+”Mbps”); for (int m = 0; m <100; m++) { Console.Write(datab[m].ToString(“X2”) + “,”);} Console.WriteLine(“——————–“); for (int m = sum-100; m < sum; m++) { Console.Write(datab[m].ToString(“X2”) + “,”);} |
2:ファイルDIR受信解析部
これは、結構ややこしいです。
ポイント1:最後の”Done!”という単語をDIRリストの最後を教えてます。
これがでてきたら受信ループが終了できるので、マイコン側にプログラムで仕込んでおきます。
ポイント2:splitを2回かけて、ファイル名とサイズを切り出します。ケースバイケースなので
マイコン側のプログラムを同時に調整しながらつくります。
ポイント3:USBシリアルとシリアルポート2か所でPCと通信さUSBがふさがっているので、デバッグ用の
モニターができなくなるので、シリアル2は必須です。
while (s.IndexOf(“Done!”) < 0) { bytesToRead = mySerialPort.BytesToRead; //Console.WriteLine(“BTR=”, bytesToRead.ToString()); byte[] receivedData0 = new byte[bytesToRead]; mySerialPort.Read(receivedData0, 0, bytesToRead);// //sum = sum + bytesToRead; s = System.Text.Encoding.ASCII.GetString(receivedData0); liststr = liststr + s; Console.Write(s);} //receivedString = Encoding.ASCII.GetString(listb); Console.Write(“CHECK AGAIN:”+liststr); //Console.WriteLine(“Received data: ” + receivedString); //if (liststr.IndexOf(“Done!”) > 0)//Doneが含まれていたらLIST終了 /// { //string[] del = { “\r\n” }; Console.WriteLine(“Recived Done! THEN analysys”); string[] delimiters = new string[] { “\r\n”, ” ” }; string[] arr = liststr.Split(delimiters, StringSplitOptions.None); listsum = “”; for (int i = 1; i < arr.Length; i++) { if (arr[i – 1].Length == 0 && arr[i].Length > 0) { fsize[filecnt] = arr[i]; //Console.WriteLine(i.ToString() + “,” + “:size[“+filecnt.ToString() +”]=”+fsize[filecnt]);} else if (arr[i].Length == 0) { // Console.WriteLine(i.ToString() + “:0”); } else if (arr[i].IndexOf(“ubx”) > 0 || arr[i].IndexOf(“bin”) > 0) { fname[filecnt] = arr[i]; Console.WriteLine(i.ToString() + “:fname[” + filecnt.ToString() + “]=” + fname[filecnt] + “:” + fsize[filecnt]);filecnt++; } else { // Console.WriteLine(i.ToString() + “:” + arr[i]); }}Console.WriteLine(“Select file No=”); |
③マイコン側のプログラム
ArudinoIDEです。動作検証用なので、SETUP内で回してます。
ポイント1:マイコンプログラムのデバッグは、Teratermで行います。キー入力で、動作確認します。
ポイント2:PCからの入力文字がきちんと届いてない場合が多いので、シリアル2をTeraTermで監視しながら
デバッグしないといけません。
//———-SD UPDATE TEST—————————–
String s; //sdlist(); while(true) delay(100); Serial2.println(“============================================================================”); if(s==’a’) Serial2.println(“============================================================================”); } |