Seeeduino XIAOでPCと爆速シリアル通信

# Seeeduino XIAO

Seeed Studioから出ているArduino互換の小型マイコンボード。ARM Cortex-M0+のCPU(SAMD21G18)でFlash256KB, SRAM32KBついてお値段なんと600円以下！ いい時代になったものですね。

![Seeeduino XIAO](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70022/90d9a27d-cc41-bfb3-3a31-2dfbeb76effd.jpeg)

# Seeeduino XIAOのUSBについて

さてこのXIAOですが、マイコンチップがネイティブでUSBに対応しているので、ボードのUSB TypeCポートがマイコンチップに直接接続されています。
そのため、PCとシリアル通信をする際には「シリアル通信」といいながらUSBを仮装シリアル(CDC)として認識させて通信しているので、シリアル通信といってもUSBの高速通信を使うことができ、通常のUARTとは比べ物にならないほど高速にシリアル通信ができます。
Arduino UNOなどは、ボードのUSBポートはUSBシリアル変換チップからメインのマイコン（UNOの場合はATMEGA328）にTXとRXのUARTで接続されているため、どうしてもボトルネックとなり**115200bps(14kbyte/s)**程度が限界になります。
SAMD21G18はUSB2.0 Fullspeedに対応しているので、**12Mbps(1.5Mbyte/s)**のポテンシャルはあります。もちろんプロトコルのオーバーヘッドがあるのでそれ以下にはなりますが。

図にするとこんな感じです。

![Arduino UNOとSeeeduino XIAOのシリアル通信](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70022/17c1f3c2-f441-f054-0a38-afb926906d6a.png)


# 実験

Arduino環境でシリアル通信の速度を計測してみます。
PC側の通信はProcessingを使いました。
今回はボード側からPC側の方向の速度を計測しています。

## Arduinoソースコード

Arduino側ではひたすらデータを送信します。データチェックがしやすいように0,1,2,...255というデータを送ります。
また、PC側から送信開始の指示ができるようにしてあります。
Arduino UNO, Seeeduino XIAO両方で共通のコードが動きました。

```c++
boolean sending = false;
const int BUF_SIZE = 256;
byte buf[BUF_SIZE];

void setup() {
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB
  }
  // バッファ初期化 0,1,2,3,...,255
  for (int i = 0; i < BUF_SIZE; i++) {
    buf[i] = i;
  }
}

void loop() {
  int c = Serial.read();
  switch (c) {
    case '1':
      sending = true; // 送信開始
      break;
    case '0':
      sending = false; // 送信停止
      break;
  }
  if (sending) {
    // 送信中ならバッファを送信する
    Serial.write(buf, BUF_SIZE);
  }
}

```

## Processingソースコード

Processingではシリアルでデータを受信して受信速度を計算します。
送った通り0,1,2,...255,0,1,...とデータが来ているかどうかもチェックしています。

```java
import processing.serial.*;

Serial serial;
long startms = 0;
long totalbytes = 0;
byte[] buf = new byte[32 * 1024]; // 32kbのバッファ

void setup() {
  printArray(Serial.list());
  serial = new Serial(this, Serial.list()[0], 115200); // [0]の部分はポートによって変える
}

void draw() {
  if(serial.available() > 0) {
    // データ読み込み
    int bytes = serial.readBytes(buf);
    // バッファの中身チェック
    for(int i = 0; i < bytes; i++) {
      if(buf[i] != (byte)(totalbytes + i) % 256) { // 0,1,2,...,255,0,1,...となっているのを確認
        println("DATA ERROR", buf[i], totalbytes, (byte)(totalbytes + i) % 256);
        break;
      }
    }
    // 総受信バイト数に加算
    totalbytes += bytes;
    // 開始からの経過時間計算
    long t = millis() - startms;
    // 経過時間, 今回の受信バイト数, 総受信バイト数, 平均速度[bytes/s] を出力
    println(t, bytes, totalbytes, totalbytes * 1000 / t);
  }
}

void keyPressed() {
  switch(key) {
  case '1':
    // 1キーを押すと受信開始
    startms = millis(); // 開始時刻リセット
    totalbytes = 0; // 総受信バイト数リセット
    serial.write('1'); // Arduino側の送信を開始させる
    break;
  case '0':
    // 0キーを押すと受信終了
    serial.write('0'); // Arduino側の送信を停止させる
    break;
  }
}

```

## 実験結果

上記プログラムで測定した結果は以下です。
**Seeeduino XIAOがArduino UNOの10倍以上の速度が出ています！！**

| ボード         | ボーレート設定 | 結果(転送速度)                            |
| -------------- | -------------- | ------------------------------- |
| Arduino UNO    | 115200         | 11753[byte/s] = 11.5[kbyte/s]   |
| Arduino UNO    | 234000         | 22207[byte/s] = 21.7[kbyte/s]   |
| Arduino UNO    | 500000         | 49976[byte/s] = 48.8[kbyte/s]   |
| Arduino UNO    | 1000000        | 測定不能（データエラー）        |
| **Seeeduino XIAO** | 115200         | **705002[byte/s] = 688.5[kbyte/s]** |

Arduino UNOではボーレートの設定を変えながら実験しました。ボーレートの設定を1000000にしたところ、Processing側で仕込んだデータチェックに引っ掛かり、計測不能となりました。Arduino UNOではボーレートは500000が限界といったところでしょうか。
Seeeduino XIAOではボーレートの設定にかかわらず転送速度は変わりませんでした。なんとProcessing側とArduino側でボーレートの設定が違っていても通信ができました。仮装シリアルポートだからですかね。USB2.0 Fullspeedの1.5M[byte/s]の理論値と比べるとそれなりに出ていることもわかります。

# まとめ

ネイティブUSBをつかえば高速にシリアル通信ができることがわかりました。画像や音を転送したり、高速なセンサーデータの転送に使えるのではないでしょうか。
Arduino DUEのようなネイティブUSBポートを持つArduinoでも同じように高速に通信ができると思います。もちろんSeeedのWio Terminalも！
Seeeduino XIAOは速い／安い／小さいの三拍子が揃っていてすばらしいですね。