USB HOST Serial機能が動作したので、HUBを経由して、3個のデバイスからUSBデータを受信してみました。
サンプルプログラムだけで、3個のデバイスからのデータをヘッダ付で時系列にログできました。
USB HOST Serialを使うとUSB出力付デバイスからのデータ収集が超便利になります。
※Teensy備忘録
★Teensyいじるシリーズ
その1(導入とSD速度) その2(USBHOST初め) その3(USBHOST3ポート)
その4(USBHOST事例) その5(ハードウェアシリアル便利)
●実験方法
Teensy4.1のUSBHOST端子からHUBへ中継コネクタを通して接続します。
オスのコネクタ同士を接続するために作りました。
この3個のUSBデバイスをHUBに接続してそれぞれのデータをTeensyUSBHOSTでまとめます。
左からTeensy3.2 50msec周期で”0987654321″とmillis()の結果を出力します。
中は、SimpleRTK2BでNMEA(GGA以外)を出力 100msec周期
右は、SimpleRTK2b liteに秋月にUSB-XbeeでUSBシリアル接続 $GNGGAのみ出力 100msec
●結果
ヘッダ付で出力してくれるので、後で整理しやすいです。
SB2のTeensy3.2は50msec周期ででてくるのですが、遅いSBのNMEA出力を
待っても、順不同ですが50msecのタイムスタンプでわかります。
■結果から
USB HOSTでまとめて1つのファイルでログできるので、RTKデータ以外のセンサデータや
周期がことなるデータの受信もタイムスタンプさえつけておけばデータ落ち無しに受信できることが判ったので、何でもUSBHOSTにつないてログすれば1ファイルにまとまるのでシンプルな計測になります。以前だとマイコンでデータ毎にログ方法をかえてファイルを何種類も作っていたのですが、USBHOST方式なら、1ファイルにまとまっているので、後処理の効率はよくなります。その代わり、データを抽出整理するプログラムが必要です。
●はまった点<SiliconLab系のCP20XXUSBシリアルチップは全部使えない>
当初、M5Atom2個をUSB HUBにまとめてTeensy4.1のUSB HOST接続していたら、1個しか受信できませんでした。おかしいなと思って、いろいろな組み合わせを試してみました。
USBデバイス1 | USBデバイス2 | USBデバイス3 | 結果 |
M5Atom | M5Atom | ー | ✖ どちらか1個しか受信できない |
M5Stack | M5Atom | ー | ✖ どちらか1個しか受信できない |
Teensy3.2 | M5Atom | ー | ✖ Teensy3.2しか受信できない |
Teensy3.2 | M5Stack | ー | ✖ Teensy3.2しか受信できない |
Teensy3.2 | ESP32DevKit | ー | ✖ Teensy3.2しか受信できない |
SimpleRTK2Blite | M5Atom | ー | ✖SimpleRTK2Bliteしか受信できない |
SimpleRTK2Blite | M5Stack | ー | ✖ SimpleRTK2Bliteしか受信できない |
SimpeRTK2Blite | Teensy3.2 | ー | ◎両方受信できる |
SimpleRTK2Blite | Teensy3.2 | SimpleRTK2B | ◎3個全部受信できる |
M5Atomは、CP2104コンパチIC、M5Stackは、CP2104Nを使っていますが、
Teensy4.1のUSB HOSTのライブラリ”USBHost_t36.h”を使ったサンプルプログラムでは、単独では受信できますが
複数個の接続では受信できないことがわかりました。ライブラリとプログラムのバージョンアップでCP210系USBシリアルチップに対応する可能性がありますが。しばらくの間は、CP210系は使えないです。
●USB HOST3個まとめて受信するサンプルプログラム
「Teensyユーザーフォーラムのルールが必ずコードを載せること」となっているので、その場でコードを
コピペして走らせて確認できるので、凄く便利です。
USB HOSTの複数デバイス接続の話題、Teensy ユーザーフォーラムにありました。
Setup USB Host to talk with hub with two FTDI serial devices
この質問に対して、Teensy USB HOSTライブラリとサンプルプログラムの開発者が相談に乗ってくれてます。
■#5の回答に PaulStoffregenというライブラリの開発者様が回答されてます。
でも何が何だかわからない状態でこう言われても困ってしまっていると、更に下のコメントに
サンプルプログラムを作ってくれている開発者おられました。感謝です。■#15Kurltさんのコード 感謝です。
このコードを丸ごとコピペして、3ポート実験をしました。実験用で使ったコードは、GISTに保存してあります。そっくりですが、自分用の名前をかえてあります。
https://gist.github.com/dj1711572002/aabdaf3b9cbd848d28a2bef1e15522d3
赤文字が追加されたUSBインスタンス userialb,userialb2 もともとは、userial で3個のデバイスです。
// Simple test of USB Host Mouse/Keyboard // // This example is in the public domain #include "USBHost_t36.h" #define USBBAUD 115200 uint32_t baud = USBBAUD; uint32_t format = USBHOST_SERIAL_8N1; USBHost myusb; USBHub hub1(myusb); USBHub hub2(myusb); USBHIDParser hid1(myusb); USBHIDParser hid2(myusb); USBHIDParser hid3(myusb); USBSerial userial(myusb); USBSerial_BigBuffer userialb(myusb); USBSerial_BigBuffer userialb2(myusb); USBDriver *drivers[] = {&hub1, &hub2, &hid1, &hid2, &hid3, &userial, &userialb, &userialb2}; #define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0])) const char * driver_names[CNT_DEVICES] = {"Hub1", "Hub2", "HID1", "HID2", "HID3", "USERIAL1", "USERIALB", "USERIALB2" }; bool driver_active[CNT_DEVICES] = {false, false, false, false}; void setup() { pinMode(13, OUTPUT); pinMode(2, OUTPUT); pinMode(3, OUTPUT); for (int i = 0; i < 5; i++) { digitalWrite(2, HIGH); delayMicroseconds(50); digitalWrite(2, LOW); delayMicroseconds(50); } while (!Serial && (millis() < 5000)) ; // wait for Arduino Serial Monitor Serial.println("\n\nUSB Host Testing - Serial"); myusb.begin(); Serial1.begin(115200); // We will echo stuff Through Serial1... } void loop() { digitalWrite(13, !digitalRead(13)); myusb.Task(); updateDeviceList(); if (Serial.available()) { Serial.println("Serial Available"); while (Serial.available()) { int ch = Serial.read(); if (ch == '#') { // Lets see if we have a baud rate specified here... uint32_t new_baud = 0; for (;;) { ch = Serial.read(); if ((ch < '0') || (ch > '9')) break; new_baud = new_baud * 10 + ch - '0'; } // See if the user is specifying a format: 8n1, 7e1, 7e2, 8n2 // Note this is Quick and very dirty code... // if (ch == ',') { char command_line[10]; ch = Serial.read(); while (ch == ' ') Serial.read(); // ignore any spaces. uint8_t cb = 0; while ((ch > ' ') && (cb < sizeof(command_line))) { command_line[cb++] = ch; ch = Serial.read(); } command_line[cb] = '\0'; if (CompareStrings(command_line, "8N1")) format = USBHOST_SERIAL_8N1; else if (CompareStrings(command_line, "8N2")) format = USBHOST_SERIAL_8N2; else if (CompareStrings(command_line, "7E1")) format = USBHOST_SERIAL_7E1; else if (CompareStrings(command_line, "7O1")) format = USBHOST_SERIAL_7O1; } Serial.println("\n*** Set new Baud command ***\n do userial.end()"); digitalWriteFast(2, HIGH); userial.end(); // Do the end statement; digitalWriteFast(2, LOW); if (new_baud) { baud = new_baud; Serial.print(" New Baud: "); Serial.println(baud); Serial.print(" Format: "); Serial.println(format, HEX); digitalWriteFast(3, HIGH); userial.begin(baud, format); digitalWriteFast(3, LOW); Serial.println(" Completed "); Serial1.end(); Serial1.begin(baud, format); } else if (ch == '$') { while (Serial.read() != -1) ; while (Serial.read() == -1) { if (userial)userial.write("USerial"); if (userialb)userialb.write("USerialB"); if (userialb2)userialb2.write("UserialB2"); delay(100); } } else { Serial.println(" New Baud 0 - leave disabled"); } while (Serial.read() != -1); } else { if (userial)userial.write(ch); if (userialb)userialb.write(ch); if (userialb2)userialb2.write(ch); } } } while (Serial1.available()) { // Serial.println("Serial1 Available"); Serial1.write(Serial1.read()); } while (userial.available()) { // Serial.println("USerial Available"); Serial.write(userial.read()); } if (userialb.available()) { Serial.print("SB:"); while (userialb.available()) { // Serial.println("USerialb Available"); Serial.write(userialb.read()); } } if (userialb2.available()) { Serial.print("SB2:"); while (userialb2.available()) { // Serial.println("USerialb Available"); Serial.write(userialb2.read()); } } } void updateDeviceList() { // Print out information about different devices. for (uint8_t i = 0; i < CNT_DEVICES; i++) { if (*drivers[i] != driver_active[i]) { if (driver_active[i]) { Serial.printf("*** Device %s - disconnected ***\n", driver_names[i]); driver_active[i] = false; } else { Serial.printf("*** Device %s %x:%x - connected ***\n", driver_names[i], drivers[i]->idVendor(), drivers[i]->idProduct()); driver_active[i] = true; const uint8_t *psz = drivers[i]->manufacturer(); if (psz && *psz) Serial.printf(" manufacturer: %s\n", psz); psz = drivers[i]->product(); if (psz && *psz) Serial.printf(" product: %s\n", psz); psz = drivers[i]->serialNumber(); if (psz && *psz) Serial.printf(" Serial: %s\n", psz); // If this is a new Serial device. if (drivers[i] == &userial) { // Lets try first outputting something to our USerial to see if it will go out... userial.begin(baud); } if (drivers[i] == &userialb) { // Lets try first outputting something to our USerial to see if it will go out... userialb.begin(baud); } if (drivers[i] == &userialb2) { // Lets try first outputting something to our USerial to see if it will go out... userialb2.begin(baud); } } } } } bool CompareStrings(const char *sz1, const char *sz2) { while (*sz2 != 0) { if (toupper(*sz1) != toupper(*sz2)) return false; sz1++; sz2++; } return true; // end of string so show as match } |
※2022年5月末 USB HOSTシリアル2ポート接続での不具合
GNSS RTK チップF9Pを2枚使ったシステムを制作しましたが、F9Pの送信周期が±20msecばらつきます。
2Chでばらばらのタイミングでポートにはいいてくるので、タイムスタンプが2ポートでずれてくることがります。それぞれのポートで受信しているデータに抜け落ちはないのですが、タイムスタンプおみてリアルタイム処理ができない課題が発生しました。しょうがないので、1枚だけUSB HOST もう一枚はシリアルで通信することで、同期がとれるようになりました。シリアルの場合USBよりバッファが弱いので、データ落ちは多いですが、許容レベル(0.3%)以内なので許容しました。今回は、特殊なチップを2枚同期させるという事例でしたので、USB HOSTが1ポートしか使えなかったというだけです。
以下に顛末が備忘録されてます。