ORANGE pico USB I/O UNIT のプロトコルを観察する

[ORANGE pico](http://www.picosoft.co.jp/orange/) には、USBキーボードとUSBメモリを使用可能にするという [ORANGE pico USB I/O UNIT](http://www.picosoft.co.jp/orange/USBIO/index.html) が存在する。
これは、ORANGE pico 本体と115200bpsのUARTで接続して使用するモジュールである。
今回は、このモジュールが用いているプロトコルを観察する。

# 起動時

起動時に通信をしている様子はみられなかった。

# USBキーボード

USBキーボードのキーを押すと、キーに対応する文字の文字コードが本体へ送信された。
キーを離した際は、通信は行われないようだった。
そのため、このモジュールは**簡易版のシリアルコンソール**として働き、本体にキーボードを接続した場合とは主に以下の動作が異なるようである。

* `kbstatus()` でキーの状態を取得することはできない。(本体に接続した場合は取得できる)
* Esc キーでプログラムの動作を止めるには2回連続で押す必要がある。(本体に接続した場合は1回で止まる)
* Ctrl+C の入力でプログラムの動作を止めることができる。(本体に接続した場合は止まらない)
* 「カタカナ ひらがな ローマ字」キーを押しても反応がみられない。(本体に接続した場合は、カタカナ入力モードのオン/オフが切り替わる)
* Delete キーを押しても文字が消えない。(本体に接続した場合は、消える)
* キーを押しっぱなしにしても1個しか文字が入力できない。(本体に接続した場合は、連続で入力できる)
* テンキーが数字入力として扱われる。(本体に接続した場合は、矢印などの機能が発動する)
* テンキーの `.` (Del) と Enter が効かない。(本体に接続した場合は、いずれも効く)

特殊キーについては、以下のキーを押すと以下のデータ (十六進数) が本体に送信された。
送信されたデータはいずれも1バイトだった。

|キー|データ|
|---|:-:|
|BackSpace|08|
|Tab|09|
|Enter|0D|
|Esc|1B|
|←|1C|
|→|1D|
|↑|1E|
|↓|1F|

以下のキーは、押してもデータの送信は確認できなかった。
ただし、CapsLock キーは、USB I/O UNIT 側で送信する文字の切り替えには有効であった。

* Insert
* Home
* End
* PageUp
* PageDown
* PrintScreen
* CapsLock
* ScrollLock
* NumLock
* Pause
* 半角/全角 漢字
* Shift
* Ctrl
* Alt
* 無変換
* 変換
* カタカナ ひらがな ローマ字
* Windows
* F1～F12
* アプリケーションキー
* テンキーの `.`
* テンキーの Enter

# USBメモリ

以下が、ORANGE pico の USBメモリにアクセスするコマンドである。

|コマンド|動作|
|---|---|
|`xfiles [ファイルマスク]`|指定したファイルマスクに合致するファイルを一覧表示する。<br>ファイルマスクを省略した場合はすべてのファイルを表示する。|
|`xload ファイル名`|指定したファイル名のファイルからプログラムを読み込む。|
|`xsave ファイル名`|指定したファイル名のファイルにプログラムを保存する。|
|`xdel ファイルマスク`|指定したファイルマスクに合致するファイルをすべて削除する。|

## USBメモリを接続していないときの動作

それぞれのコマンドを実行すると、本体から USB I/O UNIT へ以下のデータが送信された。

|コマンド|データ|
|---|---|
|`xfiles`|`1B 70 66 04` (Esc p f \x04)|
|`xload "A"`|`1B 70 72 41 04` (Esc p r A \x04)|
|`xsave "A"`|`1B 70 77 41 04` (Esc p w A \x04)|
|`xdel "A"`|`1B 70 64 41 04` (Esc p d A \x04)|

共通して、

1. コマンドを表す3バイトのデータ
2. 指定されたファイルマスク/ファイル名
3. 0x04

を順に送信するようである。

共通して、本体から3バイト目が送信されるとすぐ、USB I/O UNIT からデータ `15 19 04` が送信された。
これはエラー「Drive not ready」に対応するようである。

![コマンドと応答の様子](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/ce169d61-e851-548d-b2b3-da8f1ea1c18e.png)

## USBメモリを接続したときの動作

### xsave の動作

`new` を実行し、プログラムが空の状態で `xsave "hoge"` を実行すると、以下のデータのやり取りが行われた。

1. 本体 → UNIT : `1B 70 77 68 6F 67 65 04`
2. UNIT → 本体 : `06 04`
3. 本体 → UNIT : `30 04`
4. UNIT → 本体 : `06 04`

`"*"` などの不正なファイル名や、`"012345678.abc"` などの8.3形式に沿わないファイル名を指定すると、USB I/O UNIT 側で弾かれ、エラー「Syntax error」が表示されるようである。
たとえば、`xsave "*"` を実行すると、以下のデータのやり取りが行われた。

1. 本体 → UNIT : `1B 70 77 2A 04`
2. UNIT → 本体 : `15 1A 04 06 04`
3. UNIT → 本体 : `15 D0 04`

2番目のUNITからのデータの送信は、最初のデータの送信の約2秒後に行われた。
タイムアウト的なエラーに相当する可能性があるが、これのためか本体に「ミ」が入力された。

以下の手順で本体から USB I/O UNIT へのプログラムの送信が1行ずつ行われるらしいことがわかった。
プログラムは、入力したプログラムとは必ずしも一致せず、`list` で出力される内容で送信されるようである。
文字コードは SJIS のようである。

1. UNIT から `06 04` を受け取る。
2. UNIT へ、送信するバイト数をASCIIの十進数字で送信し、続いて `04` の1バイトを送信する。
3. UNIT から `06 04` を受け取る。
4. UNIT へ、プログラムの1行をテキストで送信する。改行コード CRLF を最後に入れる。
5. UNIT から `06 04` を受け取る。
6. まだ送信する行がある場合は、次の行について2以降を繰り返す。そうでない場合は、`30 04` を送信する。

最後の `30 04` の送信後、しばらくして UNIT から `06 04` が送られてくる。これが書き込みの完了を表すと推測できる。

たとえば、

```
10 print 1
20 print 2+3
xsave "a.b"
```

を入力すると、以下のような通信が行われた。

![短いプログラムを送信する様子](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/c09a9705-b36d-9e2b-4453-04be9647c293.png)

また、消されない最大となる255文字を1行に入れても、この255文字 (CRLFを足して257バイト) を一気に1個のかたまりで送信するようだった。

保存されたファイルをパソコンで確認すると、プログラムの内容がテキストで書かれていた。
作成日時・更新日時は空欄だった。

また、あらかじめパソコンで

* ディレクトリ
* 読み込み専用属性をつけたファイル
* 隠し属性をつけたファイル

を作成し、`xsave` でこれらの名前を指定して保存をおこなったところ、いずれも無視して上書きが行われた。

### xload の動作

指定したファイル名に対応するファイルが見つからなかったときは、USB I/O UNIT から本体にデータ `15 16 04` が送信され、エラー「File not found」が表示された。

先ほど保存したファイルを `xload "a.b"` で読み込むと、以下のデータのやり取りが行われた。

1. 本体 → UNIT : `1B 70 72 61 2E 62 04`
2. UNIT → 本体 : `32 36 04`
3. 本体 → UNIT : `32 36 04`
4. UNIT → 本体 : `31 30 20 70 72 69 6E 74 20 31 0D 0A 32 30 20 70 72 69 6E 74 20 32 2B 33 0D 0A 06 04`
5. 本体 → UNIT : `30 04`

UNIT から本体へ最後に送られた `06 04` の受信と、本体からの `30 04` の送信は、同時だった。

![データを受信する様子](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/c23626f2-5b2b-f7df-6c5a-59f69a0b790a.png)

すなわち、ファイルの読み込みは以下の手順で行われるようである。

1. UNIT へ `xload` コマンドの情報を送信する。
2. UNIT からファイルサイズがASCIIの十進数字で送られ、続いて `04` の1バイトが送られる。
3. UNIT へ受信するサイズをASCIIの十進数字で送り、続いて `04` の1バイトを送る。
4. UNIT から指定したサイズのデータが送られ、続いて `06 04` の2バイトが送られる。
5. UNIT へ読み込みの終わりを示す `30 04` の2バイトが送られる。

6,546 バイトのファイルも同様に一括での読み込みが行われた。

`xload "*"` を実行すると、まず UNIT から `15 1A 04 30 04` が帰ってきた。
このとき、本体ではエラー「Disk I/O error」が表示され、続いて `0` が入力された。
さらに、約2秒後、UNIT から `FF` の1バイトが送られてきた。
これにより、本体ではキャラクターコード `&HFF` の文字が入力された。(デフォルトでは透明だが、`cpoke` でパターンを設定すると確認できる)

また、あらかじめパソコンで

* ディレクトリ
* 読み込み専用属性をつけたファイル
* 隠し属性をつけたファイル

を作成し、`xload` でこれらの名前を指定して読み込みを試みたところ、いずれも `15 16 04` が返り、エラー「File not found」が出た。

プログラム

```
10 print "ｶﾝﾅ"
```

を `xsave` で保存し、続いて同じファイルを `xload` で読み込むと、

```
10 print ""
```

となり、カタカナが消えてしまった。
ファイルには正しく記録されているので、UARTで流し込むことなどで救出は可能である。
ただし、破壊された後のプログラムをさらにファイルに上書きしてしまうと、救出は難しくなるだろう。

### xfiles の動作

指定したファイルマスクに対応するファイルが見つからなかったときは、USB I/O UNIT から本体にデータ `15 16 04` が送信され、エラー「File not found」が表示された。

ファイルが存在する場合は、それぞれのファイル名が `[` と `]` に挟まれて連続して (長さ、区切りなどの他のデータを挟まずに) UNIT から送られ、最後に `04` の1バイトが送られてきた。

あらかじめパソコンで作成しておいた

* ディレクトリ
* 読み込み専用属性をつけたファイル
* 隠し属性をつけたファイル

は、返される結果に含まれないようだった。

`xfiles "*"` を実行すると、拡張子が無いファイルのみが列挙された。
`xfiles "*.*"` を実行すると、拡張子があるファイルも含めて列挙された。

### xdel の動作

指定したファイルマスクに対応するファイルが見つからなかったときは、USB I/O UNIT から本体にデータ `15 16 04` が送信され、エラー「File not found」が表示された。

ファイルが存在し、削除が完了すると、UNIT から `06 04` が送られてきた。

あらかじめパソコンで作成しておいた

* ディレクトリ
* 読み込み専用属性をつけたファイル
* 隠し属性をつけたファイル

は、削除対象に含まれず、存在しても「File not found」になりうるようだった。

# まとめ

ORANGE pico USB I/O UNIT は、USBキーボードを接続すると、送信のみのシリアルコンソールとして動作する。
キーボード入力にフル対応するわけではなく、本体に接続したキーボードとは `kbstatus()` でキーの状態を取得できない、Delete キーが効かない、などの違いが生じる。

USBメモリの操作を行なう際は、最初に本体から `1B` の1バイトに続いて、以下のデータを送信する。
続いて、ファイルマスク (省略時は空文字列) を送信し、その後で `04` の1バイトを送信する。

|コマンド|データ|
|---|---|
|`xfiles`|`70 66` (pf)|
|`xload "A"`|`70 72 41` (pr)|
|`xsave "A"`|`70 77 41` (pw)|
|`xdel "A"`|`70 64 41` (pd)|

その後の各コマンドの実行に関するやり取りは、本編を参照してほしい。
正常に処理が進んでいる場合は `06 04` が返り、エラーが発生した際は以下のデータが返るようである。

|データ|エラー|
|---|---|
|`15 16 04`|File not found|
|`15 19 04`|Drive not ready|
|`15 1A 04`|Syntax error / Disk I/O error|

読み取り専用属性や隠し属性をつけたファイル、およびディレクトリは、`xload`、`xfiles`、`xdel` コマンドからは見えないが、`xsave` コマンドでは**上書きされる。**
これは、大事なファイルがあっても気付かずに上書きをしてしまう可能性がある**罠**であると言えるだろう。
ORANGE pico USB I/O UNIT だけで扱う分にはこれらの属性やディレクトリは現れないはずなので、このモジュールで使用するUSBメモリはこれ専用にするのがよさそうだ。

カタカナを含むプログラムを `xsave` で保存し、`xload` で読み込むと、カタカナが消えてしまった。
`xload` では、なぜかカタカナが読み込めず、そのためプログラムを完全な形では読み込めないという**罠**もあるようだ。
UARTを塞いでモジュールを接続する手間もあるし、ORANGE pico においてUSBメモリに保存したファイルは直接完全な形では読み込めないことがあることがわかったので、あまり実用的ではなさそうだ。