Raspberry Pi Picoを使ってパスコード自動入力機を作る【前編】

# はじめに (作ろうと思ったきっかけ)

大学のWebサービスのログインパスコードをブラウザで自動生成した結果、非常に長く覚えらづらいパスコードが出来てしまいました。(それ自体は良いこと。)

ところが、自分の端末で入力するときはブラウザによる自動入力があるのでいいものの、学内のパソコンでログインするとき、**いちいち手打ちする必要がありました**。
これがめちゃくちゃ面倒くさい。

てなわけで、**自作キーボードの要領でパスコード自動入力機を作れないか**構想していました。

# Raspberry Pi Pico を選んだ理由

最初は自作キーボードといえばの、**`Pro Micro`** を使おうかとも思いましたが、主にマイコンの値段が高いことなどを理由にやめました。
(それのためだけに使うのはコスパ的に見合わないという意味)

そこで目をつけたのが **`Raspberry Pi Pico`** (以下、`ラズパイピコ`) です。
ラズパイピコ自体は発売直後に手に入れていたのですが、使用用途が思いつかず、ずっと寝かしていました。

最近になり、ラズパイピコが**HIDデバイス**(ヒューマンインターフェースデバイス)として認識させることが出来ることを知りました。
すなわち、ラズパイピコを**キーボードとしてパソコンに認識させることが出来る**のです。

これは使うしか無い!!

と、思い製作を始めました。

# 実行環境の選定

ラズパイピコのプログラミングにどのファームウェア(言語)を用いるかについてです。
今回の候補となったファームウェアについてまとめたいと思います。

| ファームウェア | 言語系 | メリット | デメリット |
|:-:|:-:|:-:|:-:|
| C/C++ | C/C++ | 高速かつ安定 | プログラミングが超面倒 |
| MicroPython | Python | お手軽かつ公式で安心 | **HIDのライブラリが無い** |
| CircuitPython | Python | お手軽でありながら発展的 | 公式環境ではない |
| Arduino | C系 | Arduinoライブラリの資産を活かせる | 初期設定がちょい面倒 |

## C/C++

まず、`C/C++`について、プログラム記述方法が非常に複雑かつ面倒で、挫折してしまいました。
コンパイルするので高速かつ安定して動作するので良いのですが、今回は断念。

## MicroPython

続いて`MicroPython`について、当初は公式サポートのあるこの環境を用いようとしていました。
ところが、調べをすすめるうちに**HIDのライブラリが無い**ことがわかり、やむなく使用を断念。
ただ、調査という意味でLチカやシリアル通信、ディスプレイ描画などのチュートリアルは行いました。

## CircuitPython

そこで代替手段として発見したのが、`CircuitPython`です。
軽く調べたところ、この`CircuitPython`というのはAdafruit社が`MicroPython`を発展させたものとのことです。
そして、こちらの`CircuitPython`環境下であれば、**HIDのライブラリがあります！**
よって、今回はこの環境を採用です。

### 実環境調査

ラズパイピコが2台手元にあったので、以下の写真のようにブレッドボード上にならべて、`MicroPython`と`CircuitPython`の両方を試してみました。

**MicroPythonでSSD1306のチュートリアル**
![IMG_4934.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/ac70efdd-79e7-c4e5-7b0a-7355093e2957.jpeg)

**CircuitPythonでHIDデバイスの実装**
![IMG_4935.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/dc3977bf-3aa0-2388-36ef-dab3b580c143.jpeg)

## Arduino

最後に`Arduino`環境についてですが、未だ情報が少ないことや、初期設定が少し手間だということから今回はパスしました。
それ以外にも、この間まで**M5Stackシリーズの製品をArduinoを使ってプログラミングしまくっていた**ため、ちょっと飽きたというのもあります(笑)。

これとか↓

https://qiita.com/koushiro/items/7cfa3895461171dfc541

これとかも↓

https://qiita.com/koushiro/items/bc6cf855a97fc678f55c

# コード

では実際にコードを書いていきます。
パスコードは仮のものとして`ABCDEfghij01234`としています。
まずは全体像から。

```python:code.py

import usb_hid
from time import sleep
import digitalio
from board import *

from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode

keyboard = Keyboard(usb_hid.devices)

led = digitalio.DigitalInOut(GP25)
led.direction = digitalio.Direction.OUTPUT

#パスコード(例) : ABCDEfghij01234

pass_list = [4,5,6,7,8,9,10,11,12,13,39,30,31,32,33]
big_or_small = [1,1,1,1,1,0,0,0,0,0,0,0,0,0,0]

led.value = True
sleep(2.5)
led.value = False

for i in range(len(pass_list)):
    if big_or_small[i]==1:
        keyboard.press(225)
        sleep(0.01)
    keyboard.send(pass_list[i])
    sleep(0.01)
    keyboard.release(225)

led.value = True
sleep(5)
led.value = False

```

それでは、コードの説明をしていきます。

## ライブラリ

導入したライブラリは全部で6つありますが、前半の4つと、後半の2つに分けられます。

### 前半の4つ

```python
import usb_hid
from time import sleep
import digitalio
from board import *
```

これらは`CircuitPython`の標準ライブラリです。上から順番に、

- USB経由でHID認識させるもの
- スリープ(待機)させるもの
- GPIOを制御するもの
- ボードの定義を参照するもの

です。特に設定等なく、記述するだけで利用できます。

### 後半の2つ

```python
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode
```

問題はこれらのライブラリについてです。
これらのライブラリは、ラズパイピコ内のライブラリ格納フォルダ(`lib`という名前のフォルダ)に、ライブラリファイルをコピーしておかなければなりません。

CircuitPythonで利用可能なライブラリは、以下のサイトよりダウンロード出来ます。
[CircuitPython Libraries](https://circuitpython.org/libraries)

![スクリーンショット 2022-03-04 223511.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/a9042687-81ef-713b-0fb2-c20388bf45c8.png)

上の画像のように、`adafruit-circuitpython-bundle-7.x-mpy-20220304`というzipファイルをダウンロードします。
(バージョンは問いません。)

フォルダを展開すると、まずは`requirements`フォルダを開いて、インポートしたいライブラリを探してください。
ライブラリのフォルダを開くと、`requirements.txt`というテキストファイルがあります。
これを参考に、インポートすべきライブラリを確認します。

ひとつ上の階層に戻り、`lib`フォルダを開き、インポートしたいライブラリを探します。
探したら、それをコピーした後、ラズパイピコ本体の`lib`フォルダにペーストします。

今回の場合は、`adafruit_hid`フォルダをそのままコピーすればオッケーです。

これで、ライブラリを利用できます。

## 定義

```python
keyboard = Keyboard(usb_hid.devices)

led = digitalio.DigitalInOut(GP25)
led.direction = digitalio.Direction.OUTPUT

#パスコード(例) : ABCDEfghij01234

pass_list = [4,5,6,7,8,9,10,11,12,13,39,30,31,32,33]
big_or_small = [1,1,1,1,1,0,0,0,0,0,0,0,0,0,0]
```

### キーボードとLEDの定義

1行目はキーボードの定義
3行目はLEDピンのピン番号の定義
4行目はLEDピンをアウトプットに定義
という流れです。

### パスコードをキーコードに変換して変数に保存

**adafruit_hid.keyboard.Keyboard Reference**

https://docs.circuitpython.org/projects/hid/en/latest/api.html#adafruit-hid-keycode-keycode

↑のサイトにアクセスし、打ちたいパスコードの文字を1字ずつキーコードに変換していきます。

例えば最初の文字の`A`を打ちたいとするならば、[`A = 4`](https://docs.circuitpython.org/projects/hid/en/latest/api.html#adafruit_hid.keycode.Keycode.A) の記述より、`pass_list`の最初に`4`を入れます。
ただし、このままだと通常のキーボードと同様に**小文字が入力**されます。
すなわち、**Shiftキーを押して大文字を入力する必要があります。**
そこで、`big_or_small`という、大文字・小文字を判別するためのリストを用意し、大文字なら`1`を、小文字なら`0`を入れることとします。

## メインタスク

```python
led.value = True
sleep(2.5)
led.value = False

for i in range(len(pass_list)):
    if big_or_small[i]==1:
        keyboard.press(225)
        sleep(0.01)
    keyboard.send(pass_list[i])
    sleep(0.01)
    keyboard.release(225)

led.value = True
sleep(5)
led.value = False
```

プログラムの流れとしては、

1. LEDを点灯する (キー操作準備体制の表示)
2. 2.5秒待機 (ホスト側に認識してもらうための待機)
3. LEDを消灯
4. for文で全文字を入力する
5. 大文字か小文字かの判定
6. 大文字ならShiftキー押下(`keyboard.press(225)`)
7. リストよりキーコードを取り出し、キー送信
8. Shiftキー押下を解除
9. LEDを点灯し、実行終了を知らせる
10. 5秒待機した後、消灯

といった感じです。

# 実行風景

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ラズパイピコをHIDデバイス(キーボード)と認識させて、任意の文字列を打ち込むテスト <a href="https://t.co/YbUPXdT0Yr">pic.twitter.com/YbUPXdT0Yr</a></p>&mdash; Koushiro (@Koushiro_A) <a href="https://twitter.com/Koushiro_A/status/1500283436812046336?ref_src=twsrc%5Etfw">March 6, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# 課題点

## セキュリティ面が弱い

例えば誰かに無断で使われてしまった場合、モロにパスワードが流出します。
またCircuitPythonの場合、パソコンにストレージとしても認識されるため、ソースコードが丸見えです。

これらの点から、セキュリティ的にはあまりよろしくない状況です。

## スタイリッシュじゃない

このパスワード打ち込みのためだけにラズパイピコをパソコンとMicroUSBケーブルを使ってつなげるというのは、正直スマートでは無いと感じます。
そもそも、**今回のプロダクトにおいてGPIOピンは一切必要ないので、もっと小さくて良いのです**。

## 解決策

そこで、こんな物を発見しました。

![5056-00.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/f7b3075e-2444-5baf-fd00-fe8f0fa25349.jpeg)

![5056-03.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/2d297aa5-36c3-5895-16db-904611543871.jpeg)


こちらは、[Adafruit Trinkey QT2040 - RP2040 USB Key with Stemma QT](https://www.adafruit.com/product/5056) というもので、Adafruit社が製作している`RP2040`搭載ボードです。

すなわち、今回のラズパイピコでの開発を、そのまま移植出来る(はず)なのです。

これなら、小型化でき、なおかつパソコンとの接続に必要なケーブルも要りません!

セキュリティ面についても解決策が出来たので、

**次回、こちらのボードを使ってデバイスを完成させたいと思います。**

[**後編の記事はこちら**](https://qiita.com/koushiro/items/8886f68568e982c4e5f9)

# 参考サイト

**Adafruit CircuitPython API Reference**

https://docs.circuitpython.org/en/latest/docs/index.html

**Adafruit HID Library**

https://docs.circuitpython.org/projects/hid/en/latest/index.html