[F#]USBマイコンと通信する＜Digispark編＞

## はじめに
引き出しの整理をしていたらDigisparkのストックが出てきました。
前回の購入時（21年1月頃）はクローン品が$1.2程、さらに円高だったので多めに買った残り物です。
最近は円建てで300円以上するので、買う気になりませんが...

キーボード・マウスとしての活用事例が多いようですが、`DigiUSB`でお手軽な汎用USBデバイスになります。

https://github.com/digistump/DigistumpArduino/blob/master/digistump-avr/libraries/DigisparkUSB/Readme.txt

ただし、汎用デバイスとして使うには柔軟なホスト環境も必要です。
GitHubにはデバイスやホストのサンプルプログラムが実行ファイルも併せて公開されています。

https://github.com/digistump/DigisparkExamplePrograms/tree/master

検索するとホストとして上記サンプルプログラム中の`monitor.exe`を使用する例が散見されます。
上記をzipダウンロードして展開し、`Python\DigiUSB\windows\`まで下りて`monitor.exe`を実行すればそのまま動きます。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209885/df6a0e10-b696-f0a5-0b83-6a0d9a41240d.png width=240px>
上は実際に動かした際のスクショです。
使用したDigisparkは**大文字小文字を変換して返す**ようにプログラムされている（詳細は後述）ので正常に機能しています。
GUIかつ文字列での通信ということもあって、動作確認の他には使う場面が限定されそうです。

サンプルプログラムを眺めると、核になるのは次のファイルということがわかります。
このファイルを`import`すればPythonで簡単に通信ができてしまいます。（要`PyUSB`）
+ [Python/DigiUSB/source/arduino/usbdevice.py](https://github.com/digistump/DigisparkExamplePrograms/blob/master/Python/DigiUSB/source/arduino/usbdevice.py)

本家Pythonはもちろんのこと、`PyUSB`をインストールしたIronPython 3.4でも動作します。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209885/ec56f82b-81b5-8208-270d-3314e05c6a75.png width=640px>

「このままPythonで動かせばいいのに、今さら...」の感は否めませんが、今回はホスト側を**F#スクリプト**で記述し、**自由度の高い汎用USBデバイスとホスト**の製作例を記事にします。

サンプルプログラムの焼き直しで特に目新しい内容はありませんので、一つの事例として読み流してください。

具体的にはDigisparkにNeoPixel（8LED スティック）を接続してホストPCで操作してみます。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209885/7586c625-832b-ba34-3a86-ce46d7c6775c.png width=300px>

やることは単純に次の２つです。
+ F#スクリプトでホストPCのUSBから24バイトのデータ(8LED分)をDigisparkに流す。
+ DigisparkはUSBから受け取った24バイトのデータでNeoPixelを点灯させる。

Python同様、F#もREPL環境が使えるので、イルミネーションのパターンをすぐに試作できます。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209885/8a0aa4c3-0bae-b253-1543-1a6bbb158f0e.png width=300px>
## 環境
+ Windows 11 Pro 22H2（ThinkPad L15 Core i5-10210U）
+ .NET SDK 7.0.400<font color=red> ＜要注意！＞</font>
+ VSCode（拡張機能として PlatformIO IDE + Ionide for F#）
（なお、Ionide for F#は、C#拡張機能に依存するので、こちらもインストール）
+ Digispark開発時にはドライバが必要（下記から入手）
https://github.com/digistump/DigistumpArduino/blob/master/tools
Windowsの場合は`micronucleus-2.0a4-win.zip`を展開し、`DPinst64.exe`を実行すればよい。

<font color=red>注意</font>：.NET7 SDKの一部バージョンにはバグがあり、記事中のF#スクリプトが動作しないものがあります。（`#r "nuget: ...."`がエラーになる。）
ただし、**執筆時最新の SDK 7.0.400（Runtime 7.0.10）（2023/8/8リリース）は動作します**。

https://github.com/dotnet/fsharp/issues/15570

VSCodeに拡張機能を追加すると、マイコンのコード編集・アップロード、ホスト側のF#スクリプト編集・実行が同一環境下で完結し、軽量かつ強力な統合環境が実現できます。
ワークスペースによって、マイコンのプロジェクトフォルダとホスト側スクリプトのフォルダを一括管理できるのも大きなメリットです。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209885/9a1bd186-1dcb-f60d-a238-4f544cf3577a.png width=640px>

## まずはループバックテスト
USBからDigisparkにデータを送信し、USBでDigisparkからのデータを受信します。
今回は少し手を加えて、**半角英字コードを受信した場合に大文字小文字を変換して返す**ことにします。
### マイコン側
PlatformIO IDEを使ったDigisparkプロジェクトの作成はとても簡単です。
新規プロジェクトで、ボードのドロップダウンリストから"Digispark USB (Digistump)"を選択するだけです。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209885/40098179-9e14-29ea-2df5-5677c9467efe.png width=640px>

今回はプロジェクト名を`Digispark_ChangeCase`としておきました。
既定のフォルダ（ドキュメント内のPlatformIO\Projects）内に同名のフォルダができます。

Digispark側のコード（スケッチ）は`main.cpp`を書き換えます。
PlatformIOを利用しているので`#include <Arduino.h>`が必要なことを除けば特に補足はありません。
USB周りは`DigiUSB`ライブラリで簡単に済ませます。

```cpp:main.cpp
#include <Arduino.h>
#include <DigiUSB.h>

#define LED 1  // Built-in red LED (Model A)
uint8_t changeCase(uint8_t);

void setup() {
  DigiUSB.begin();
  pinMode(LED, OUTPUT);
}

void loop() {
  while (DigiUSB.available()) {
    digitalWrite(LED, HIGH);
    DigiUSB.write(changeCase((uint8_t)DigiUSB.read()));
  }
  digitalWrite(LED, LOW);
  DigiUSB.refresh();
}

uint8_t changeCase(uint8_t c) {
  if ('A' <= c && c <= 'Z') {
    c += 0x20;
  } else if ('a' <= c && c <= 'z') {
    c -= 0x20;
  }
  return c;
}
```
VSCode + PlatformIO IDEの場合、エディタ上でショートカットキー`Alt + Ctrl + U`によりデバイスへのアップロードを実行します。
（ビルドだけなら`Alt + Ctrl + B`）

Digisparkのお約束で、**アップロードの前にUSBから抜いておきます。**
抜き忘れた場合でも下記メッセージが出てから抜いて大丈夫です。

ターミナルに
```
> Please plug in the device (will time out in 60 seconds) ... 
```
が表示されたら準備が整ったので、Digisparkを挿すと書き込みが始まります。

以下に実際のスクショを載せますが、`DigiUSB`はかなりのリソースを消費します。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209885/35dc7860-34d6-582d-ccd8-d08a5d07cea9.png width=480px>

書き込みが成功すれば、挿し直すことなく新たなUSBデバイスとして認識されます。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209885/e232531e-971d-e666-8583-0e1bba57b0e3.png width=480px>

DigiUSBで作成したUSBデバイスは上図のとおり`libusb-win32`デバイスです。
前述のとおり開発環境はすでにドライバがインストールされていますが、**デバイスを他のWindows PCで使う場合はドライバ（libusb-win32）のインストールが必要です。**

### ホスト側
F#のスクリプトはGitHubで公開されているコードを参考（ほぼ引用）にして書いてみました。
+ [Python/DigiUSB/source/arduino/usbdevice.py](https://github.com/digistump/DigisparkExamplePrograms/blob/079dce4df7ce17fd594118bab439df4aa7f1e1f4/Python/DigiUSB/source/arduino/usbdevice.py)（前述のPythonファイル）
+ [DigiSpark/ArduinoUsbDevice.cs](https://github.com/stlk/DigiSparkDotNet/blob/master/DigiSpark/ArduinoUsbDevice.cs)

`PyUSB`に替わるUSB用のライブラリとして、libusb-win32をサポートしている`LibUsbDotNet`を使います。
こちらもUSBホストは簡潔に記述できます。下は公式ドキュメントへのリンクです。

https://libusbdotnet.sourceforge.net/V2/Index.html

VID，PIDによるデバイスの認識、1バイトの送信・受信も数行のコードで済みます。

モデルにした`usbdevice.py`同様の作りを目指しましたが、使い勝手をよくするために複数バイトの入出力、文字列の入出力なども実装しておきます。

以下、見てのとおり**エラーチェックや例外処理は施していません。終了処理も省略しました。**
（ただし、`LibUsbDotNet.UsbDevice`クラスにアクセスできるように`UsbDevice`プロパティを実装しています。）
1バイトを読み取るための`ReadByte`の結果は`option`型にして成否の判断ができるようにしています。

```Fsharp:DigiUSB.fsx
#r "nuget: LibUsbDotNet, 2.2.29"

open LibUsbDotNet
open LibUsbDotNet.Main
open System.Collections.Generic

type DigiUsbDevice() =
    let USBRQ_HID_GET_REPORT = 0x01uy
    let USBRQ_HID_SET_REPORT = 0x09uy
    let USB_HID_REPORT_TYPE_FEATURE = 0x03

    let REQUEST_TYPE_SEND =
        UsbCtrlFlags.RequestType_Class
        ||| UsbCtrlFlags.Recipient_Device
        ||| UsbCtrlFlags.Direction_Out
        |> byte

    let REQUEST_TYPE_RECEIVE =
        UsbCtrlFlags.RequestType_Class
        ||| UsbCtrlFlags.Recipient_Device
        ||| UsbCtrlFlags.Direction_In
        |> byte

    let Device: UsbDevice =
        (0x16c0, 0x05df) // (VID, PID) of Digispark
        |> UsbDeviceFinder
        |> UsbDevice.OpenUsbDevice

    // 次の３つのプロパティはデバイス情報確認用
    member this.UsbDevice = Device
    member this.Manufacturer = Device.Info.ManufacturerString
    member this.Product = Device.Info.ProductString

    // 送信メソッド（1バイト・バイト列・文字列）
    member this.WriteByte(data: byte) =
        let mutable packet: UsbSetupPacket =
            UsbSetupPacket(REQUEST_TYPE_SEND, USBRQ_HID_SET_REPORT, USB_HID_REPORT_TYPE_FEATURE <<< 8, int data, 0)
        let mutable bytesWritten: int = 0
        Device.ControlTransfer(&packet, null, 0, &bytesWritten)

    member this.WriteBytes(data: seq<byte>) =
        data |> Seq.map this.WriteByte |> Seq.forall id

    member this.WriteString(str: string) =
        str |> Seq.map byte |> this.WriteBytes

    // 受信メソッド（1バイト・バイト列・文字列）
    member this.ReadByte() : byte option =
        let mutable packet: UsbSetupPacket =
            UsbSetupPacket(REQUEST_TYPE_RECEIVE, USBRQ_HID_GET_REPORT, USB_HID_REPORT_TYPE_FEATURE <<< 8, 0, 1)
        let readValue: byte array = [| 0uy |]
        let mutable bytesRead: int = 0
        let result: bool = Device.ControlTransfer(&packet, readValue, 1, &bytesRead)
        if result && (bytesRead = 1) then
            Some(readValue[0])
        else
            None

    member this.ReadBytes() =
        let data: Queue<byte> = Queue<byte>()
        let mutable flag: bool = true
        while flag do
            let result: byte option = this.ReadByte()
            if result.IsSome then
                data.Enqueue(result.Value)
            else
                flag <- false
        data :> seq<byte>

    member this.ReadString() =
        this.ReadBytes()
        |> Seq.map char
        |> System.String.Concat
```
完成したスクリプト`DigiUSB.fsx`は保存して、以降は他のスクリプトから呼び出して使います。
**ここまでで、目的の95%は達成です。**

続いて、ループバックテスト用のスクリプト`DigiUSB_ChangeCase.fsx`を書きます。
こちらは、上記`DigiUSB.fsx`のロードと、送信と受信を１つのメソッドにまとめただけのスクリプトです。
末尾でデバイスのインスタンス`myDigi`を作成します。
```Fsharp:DigiUSB_ChangeCase.fsx
#load "DigiUSB.fsx"
open DigiUSB

type ChangeCaseDevice() =
    let Digi: DigiUsbDevice = DigiUsbDevice()

    member this.sendByte(data: byte) =
        data |> Digi.WriteByte |> ignore
        (Digi.ReadByte()).Value

    member this.sendBytes(data: seq<byte>) =
        data |> Digi.WriteBytes |> ignore
        Digi.ReadBytes()

    member this.sendString str =
        str |> Digi.WriteString |> ignore
        Digi.ReadString()

    member this.Manufacturer = Digi.Manufacturer   // 接続確認用
    member this.Product = Digi.Product             // 接続確認用

let myDigi: ChangeCaseDevice = ChangeCaseDevice()  // インスタンス作成
```
Ionide for F#がインストールされていればVSCodeのエディタ上で上記を全選択し、`Alt + Enter`で選択範囲を実行します。

以降は、Digisparkを「送信データを加工（大文字小文字の変換）して返すUSBデバイス」`myDigi`として扱うことができます。

エディタ上で、上記スクリプトに次のコードを追加します。
```Fsharp:DigiUSB_ChangeCase.fsx（追加分）
myDigi.UsbDevice.IsOpen  // UsbDeviceクラスのテスト
myDigi.Manufacturer      // 接続確認
myDigi.Product           // 接続確認
65uy |> myDigi.sendByte
[ 65uy; 66uy; 97uy; 98uy ] |> myDigi.sendBytes
"aBc123XyZ" |> myDigi.sendString
```
下の3行がループバックテストになります。
1バイト、バイト列、文字列データを送信すると受信結果を返します。

追加分について、エディタ上で1行ごとに`Alt + Enter`で実行します。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209885/b54a7ef8-b3ec-1531-6484-8932e9f03063.png width=400px>

大文字小文字も変換されており、ループバックテストは成功です。
Digisparkがデータを受信するとボード上の赤色LEDが微かに点滅するので、目視でも動作が確認できます。

ちなみに、DigiUSBのバッファは128バイトです。
128バイト以上のデータを送った場合、127バイトを返しました。
## NeoPixelの制御例
最後にNeoPixelを接続してみます。
ポイントは**Digisparkの負担を軽くすること**と**マイコン側の処理時間を考慮すること**です。

### DigisparkとNeoPixelの接続
接続は冒頭の写真のとおりで、Digispark PB0 <---> NeoPixel DIN と GND <---> GND の2本です。
マイコンとNeoPixelの接続に関しては次の記事を一読することをおすすめします。

https://learn.adafruit.com/adafruit-neopixel-uberguide/powering-neopixels

信号線には300～500Ωの抵抗挿入が推奨されていますが今回は省略しています。
また、NeoPixelの電源(+5V)は別電源にしています。
### マイコン側
Digisparkに使われているATtiny85は低スペックの8ビットマイコンです。
ブートローダ(Micronucleus)とDigiUSBでかなりのリソースが消費されています。
NeoPixel用のライブラリには**リソース不足でビルドに失敗するものもあります**が、FastLEDはビルドが通りました。
残りのリソースは少ないので、DigisparkにはNeoPixelを点灯させる仕事だけを担ってもらいます。


https://fastled.io/

PlatformIO IDEからライブラリの検索・追加も容易にできます。
ライブラリを追加する様子です。
「Add to Project」をクリック後、目的のプロジェクトに追加します。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209885/9fea6b0c-b28b-c738-a053-1bdc5b6c03fa.png width=640px>


```cpp:main.cpp
#include <Arduino.h>
#include <DigiUSB.h>
#include <FastLED.h>

#define LED_PIN 0 // To Neopixel DIN
#define NUM_LEDS 8
#define DATA_LEN (NUM_LEDS * 3)

union npdata {
  CRGB leds[NUM_LEDS];
  uint8_t usbdata[DATA_LEN];
} data;

uint8_t count;

void setup() {
  DigiUSB.begin();
  FastLED.addLeds<NEOPIXEL, LED_PIN>(data.leds, NUM_LEDS);
  count = 0;
}

void loop() {
  while (DigiUSB.available()) {
    data.usbdata[count++] = DigiUSB.read();
  }
  if (count >= DATA_LEN) {
    DigiUSB.refresh();  // 外すと不安定になる
    FastLED.show();
    count = 0;
  }
  DigiUSB.refresh();
}
```
24バイト受信したらそのまま点灯（`FastLED.show`）させるだけのスケッチになります。
注意点は、**こまめに**`DigiUSB.refresh`**を呼ばないと接続が切れることです。**
上のスケッチもコメントのとおりで、`FastLED.show`に時間がかかるので直前に一つ挿入しました。


以下はビルド時に表示されたリソース使用率です。
```
RAM:   [========= ]  90.8% (used 465 bytes from 512 bytes)
Flash: [========  ]  82.3% (used 4948 bytes from 6012 bytes)
```
参考までに16 LEDに変更（`#define NUM_LEDS 16`）した場合です。
```
RAM:   [==========]  95.5% (used 489 bytes from 512 bytes)
Flash: [========  ]  82.3% (used 4948 bytes from 6012 bytes)
```
RAMの使用量がきっちり8球分の24バイト増加しています。LEDを24球にするとRAMは100%超えになります。
### ホスト側
ホスト側のスクリプトは、先の`DigiUSB.fsx`を使って24バイトを送信すればいいだけですが、使い勝手を考えて以下の機能を持つ`DigiLED`クラスを作成します。
+ インスタンス作成時の動作確認（1秒間のテスト点灯）
+ RGB各8バイトの配列を用意
+ 上記配列のクリア（配列を0埋めする）
+ 点灯（RGB配列データの送信）
+ 全消灯（0を24バイト送信）

点灯`TurnOn`と全消灯`TurnOff`にはFastLEDの処理時間（実質はNeoPixelへのデータ転送時間）を考慮して`System.Threading.Thread.Sleep(1)`を挿入し、待機時間を設けています。
これがないと連続した点灯指示に対してマイコン側の処理が追いつかなくなります。
```Fsharp:DigiUSB_NeoPixel.fsx
#load "DigiUSB.fsx"
open DigiUSB
open System.Threading

type DigiLED() =
    let Device = DigiUsbDevice()

    do
        Array.create 24 7uy |> Device.WriteBytes |> ignore
        Thread.Sleep(1000)
        Array.zeroCreate<byte> 24 |> Device.WriteBytes |> ignore

    member val R = Array.zeroCreate<byte> 8 with get, set
    member val G = Array.zeroCreate<byte> 8 with get, set
    member val B = Array.zeroCreate<byte> 8 with get, set

    member this.TurnOff() =
        Array.zeroCreate<byte> 24
        |> Device.WriteBytes
        |> ignore
        Thread.Sleep(1)

    member this.TurnOn() =
        Array.zip3 this.R this.G this.B
        |> Seq.collect (fun (r: byte, g: byte, b: byte) -> [ r; g; b ])
        |> Device.WriteBytes
        |> ignore
        Thread.Sleep(1)

    member this.ClearR() = Array.fill this.R 0 8 0uy
    member this.ClearG() = Array.fill this.G 0 8 0uy
    member this.ClearB() = Array.fill this.B 0 8 0uy

    member this.Clear() =
        this.ClearR()
        this.ClearG()
        this.ClearB()
```
スクリプトを追加しましょう。
```Fsharp:DigiUSB_NeoPixel.fsx（追加分）
let myLED: DigiLED = DigiLED()
```
上の行にカーソルを置いて`Alt + Enter`でデバイスのインスタンス`myLED`を作成します。
インスタンス作成時に動作確認を兼ねて、**NeoPixelが1秒間弱く白色点灯**します。
動作確認ができたら`myLED`を使って少し遊んでみます。
#### 点灯例１
```Fsharp:DigiUSB_NeoPixel.fsx（追加分）
// グラデーション表示（以下を全て選択し、Alt + Enter）
myLED.Clear()
myLED.R <- [| 2uy .. 2uy .. 16uy |]
myLED.G <- [| 2uy .. 2uy .. 16uy |] |> Array.rev
myLED.B <- Array.create 8 8uy
myLED.TurnOn()
```
グラデーション表示されました。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209885/d893ac1b-8715-0d0a-29f4-f168c9ab7a46.png width=300px>

`myLED.TurnOff()`で消灯します。
```Fsharp:DigiUSB_NeoPixel.fsx（追加分）
myLED.TurnOff()
```
#### 点灯例２
用途に合わせた関数作成も自在です。
8球についてRGBごとにそれぞれ "0" "1" 文字列で記述し「8色 + 明るさ指定」で配列をセットする関数です。
```Fsharp:DigiUSB_NeoPixel.fsx（追加分）
// 関数化（8色 + 明るさ指定）（以下を選択し、Alt + Enter）
let easy8colors (brightness: byte) ((r: string, g: string, b: string): string * string * string) =
    let setCells (bitstr: string) =
        bitstr
        |> Seq.map (string >> byte >> (*) brightness)
        |> Seq.toArray
    myLED.R <- setCells r
    myLED.G <- setCells g
    myLED.B <- setCells b
```
F#になじみのない方には`Seq.map (string >> byte >> (*) brightness)...`の辺りがわかりにくいかと思います。

抜き出して適当な値を入れて実行した結果を示します。
ここでは文字列を重みのついたバイト配列に変換しています。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209885/85a46ba6-7bc4-3127-e179-8f7560438bee.png width=480px>
点灯させてみましょう。
3要素タプルの左からRGBで、R要素を中央4球、G要素を左4球、B要素を右4球に与えています。
末尾の`10uy`は明るさです。

```Fsharp:DigiUSB_NeoPixel.fsx（追加分）
("00111100", "11110000", "00001111") |> easy8colors 10uy
myLED.TurnOn()
```
上記で点灯させたものが記事冒頭２枚目の写真です。（再掲）
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209885/8a0aa4c3-0bae-b253-1543-1a6bbb158f0e.png width=300px>
#### 点灯例３
最後は「ナイトライダー」っぽく流れる関数です。
```Fsharp:DigiUSB_NeoPixel.fsx（追加分）
// 関数化（Knight Rider 風）（以下を選択し、Alt + Enter）
let knightRider() =
    let data: byte array array =
        "0, 0, 0, 0, 0, 0, 0, 0, 64, 32, 16, 8, 4, 2, 1, 0"
            .Split(',')
        |> Array.map byte
        |> Array.windowed 8
    myLED.Clear()
    for _ in 0..4 do
        for cells: byte array in data do
            myLED.R <- Array.copy cells
            myLED.G <- [| for i: byte in cells -> i / 2uy |]
            myLED.TurnOn()
            Thread.Sleep(5)
        myLED.TurnOff()
        Thread.Sleep(500)
```
関数の冒頭がわかりにくいのですが、配列の配列を作っています。
冒頭部分を抜き出して実行した結果を示します。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209885/0d88069a-ccb7-30a6-5cad-f1ff50605bf8.png width=400px>
これを順番に表示させて、LEDが尾を引きながら流れる感じにしています。
このデータを赤と緑（値を1/2）にセットするとオレンジで光ります。

関数`knightRider`を実行します。
```Fsharp:DigiUSB_NeoPixel.fsx（追加分）
knightRider()
```
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209885/84ad9055-5567-694c-08ba-09548202bfc0.gif width=240px>

（左端LEDが点灯していませんが、動画をgifに変換した際にコマ落ちしたようです。実際は左端まで流れています。）

### おわりに
蒸し返しになりますが、単に24バイトを送信するだけなら`usbdevice.py`を利用し、Pythonでホストを書いても十分です。
**F#で書いてみたのはコレクション操作のバリエーションが豊富で、NeoPixelのパターンデータを作成しやすかった**点にあります。
用途に応じて使い分けできればいいかと思います。

F#でもPythonでもREPLでマイコン操作（というよりはマイコンを介した周辺機器操作）ができると何かと便利です。
ただし、Digisparkは別にして、Arduinoの場合、**通常はUSBシリアルで通信ができるので実務的にはそちらを選択する方が楽でしょう。**

さて、タイトルに**＜Digispark編＞**と付けましたが、机の引き出しからはATmega32U4を使用したUSB直挿しマイコン（"**Pro Micro Beetle**" とか "**Arduino Leonardo Beetle**" とかで検索すると引っかかるヤツ）も出てきました。

こちらは、 **USB CDC（シリアル）** に加えて、**USB Raw HID** でも通信できるので、あらためて記事にする予定です。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209885/91b35e0d-b1b4-cefb-bdb8-0d808766d338.png width=300px>
左：Digispark クローン品　　右：ATmega32U4 "Pro Micro Beetle"（共に製造元不明）