LoginSignup
36
28

More than 5 years have passed since last update.

Arduino Uno R3 で作るESC専用USBキーボード

Posted at

mixiグループアドベントカレンダー 9日目を担当させていただきます。
普段はSNSミクシィのAndroidアプリを開発しています。Android全く触れない内容ですが、どうぞよろしくお願いします。

概要

新しいMacBookProが発表され、ファンクションキーの代わりにTouchBarになりました。
Vimを使っている私としてはESCは残してほしかったので完全に\(^o^)/状態でした。
多分、設定でESCは残せるだろうし、Ctrl-[ を使えばいいのですが、ESCをまず数回叩かないと気持ち悪いレベルになっている自分としては非常に寂しい。
とにかくESCの打鍵感が欲しいので、ESC専用キーボードは売っているのだろうかと軽くググったらなかった。なら作ってみようという話です。

正直なところ、ATmega32u4チップが搭載されたArduino LeonardoかArudiono Microを使うとUSBデバイスとして認識してくれるので、ライブラリを使えば簡単にできると思います。
しかし、ここでは多くの人がもっているであろうであるArudino Uno R3(以下R3) をターゲットにして、DFUによるもう一つのチップを書き込むなどしてR3をキーボードデバイスにします。

R3のチップ構成について

R3はざっくり2つのAtmelチップがついています。

チップ 概要 仕様
ATmega 16u2 usb serialとして使われ328pに書き込むために使われ、USBポート付近にあるやつ。R2からAtmelチップになったのでDFUができるようになった。ちなみにR2は ATmega 8u2 link
ATmeaga 328p sketchなどで書き込まれるMCU。一般的にはこちらがメイン link

Arudinoを開発する場合、作成したSketchを公式IDEなどからR3に書き込みエルチカなどします。IDEを使って書き込んでいるチップは ATmeaga 328p です。今回はR3をUSBキーボードデバイスとして認識させるために ATmega 16u2 も書き換えます。

やること

  • R3をUSBキーボードデバイスとして認識させてみる (DFUで ATmega 16u2 に書き込み)
  • R3をスケッチから書き込みできるようにUSBシリアルに戻す (DFUで ATmega 16u2 に書き込み)
  • ESCキーコードを送るプログラム作成 (sketch)
  • sketchを ATmeaga 328p に書き込み
  • 再度R3をUSBキーボードデバイスとして認識させる (DFUで ATmega 16u2 に書き込み)

やってみる

R3をUSBキーボードとして認識させてみる

R3をUSBキーボードとして認識させてみます。
そのためには、DFUで ATmega 16u2 に対してキーボードHexファイルを書き込みます。
DFU(Device Firmware Update)とは、ざっくりとUSBプロトコルを使用してプログラム(hexファイル)をチップに書き込むことです。

hexファイルの取得

Hexファイルは、Atmelチップ開発環境を構築すればビルドして作成できます。macならCrossPack、linxuならtoolchainでクロスコンパイルするための avr-gcc を導入する必要があります。
LUFAライブラリを使うと様々なUSBデバイスになるHexファイルを作成することができます。2つのHexファイルもLUFAライブラリを使っています。

今回必要なHexファイルは2つです。

UNO-dfu_and_usbserial_combined.hex

最初から書き込まれているデフォルトのHexファイル。ATmeaga 328p にsketchプログラムを書き込めたりします。GitHubから取得できます。公式IDEをインストール済みなら /Applications/Arduino.app/Contents/Java/hardware/arduino/avr/firmwares/atmegaxxu2/ にあります。

Arduino-keyboard-0.3.hex

USBkeyboardデバイスとして認識するためのHexファイル。ここから入手できますが、現在リンク切れなので今回のプロジェクトリポジトリに追加ておきました。

dfu-programmerのインストール

dfu-programmer という便利なコマンドラインプログラマがあります。
これはISP対応のUSBブートローダを備えたAtmelチップに対してコマンドラインで書き込むことができます。
brew を使ってインストールします。(UbuntuとかのDebian系なら apt でインストール)

$ brew update
$ brew install dfu-programmer

# versionを確認してみる
$ dfu-programmer --version
    dfu-programmer 0.7.2

DFUプログラムをする

dfuプログラムは以下の手順で行います。

  • dfuモードにする
  • dfu-programmer による書き込み(削除・書き込み・リセット)

dfuモードにする

以下のようなアナログ操作です。

  • USBから抜く
  • リセットスイッチの前のピン2つをジャンパーピンで接続
  • USB接続する
  • ジャンパーピンを抜く

dfuモードになったかどうかはシステムレポートのUSBの状態を見て判断できます。

  • 左上のアップルマーク -> このMacについて -> システムレポート -> USB で 複合装置 になっていればOKです。
normal dfu mode
dfu_off.png dfu_on.png

dfu-progmrammer による書き込み

削除・書き込み・リセットを一連の手順とします。

# 削除する
$ dfu-programmer atmega16u2 erase
    Checking memory from 0x0 to 0x2FFF...  Not blank at 0x1.
    Erasing flash...  Success

# 書き込む (ここではUSBKeyboard Hexファイル)
$ dfu-programmer atmega16u2 flash ./hex/Arduino-keyboard-0.3.hex
    Checking memory from 0x0 to 0xFFF...  Empty.
    0%                            100%  Programming 0x1000 bytes...
    [>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]  Success
    0%                            100%  Reading 0x3000 bytes...
    [>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]  Success
    Validating...  Success
    0x1000 bytes written into 0x3000 bytes memory (33.33%).

# reset
$ dfu-programmer atmega16u2 reset

USBを抜き差しするとキーボードとして認識されるはずです。システムレポートで確認してみます。

スクリーンショット 2016-12-06 23.52.24.png

R3をUSBシリアルとして認識させてみる (元に戻す)

USBキーボードとして認識できましたが、 GPIOからのインプットをESCキーとして送信するためのプログラムは本丸である ATmeaga 328p に書き込む必要があります。
このままでは通常のSketchを書き込むことができないので、先程の手順でHexファイルを書き込み、もとに戻してみます。

  • 前述の方法で、DFUモードにしてください
  • dfu-programmer でUSBシリアルHexファイルを書き込みます
# 削除
$ dfu-programmer atmega16u2 erase
    :
# 書き込み
$ dfu-programmer atmega16u2 flash /Applications/Arduino.app/Contents/Java/hardware/arduino/avr/firmwares/atmegaxxu2/UNO-dfu_and_usbserial_combined.hex
    :
# リセット
$ dfu-programmer atmega16u2 reset
    :

次はESCキーコードを送るSketchプログラムを作成します。

ESCキーコードを送るプログラム作成 (sketch)

ESCキーコードを送るプログラムを作成します。このプログラムの書き込み先は ATmeaga 328p なのでArduinoの一般的な開発と同じです。

USBプロトコルと送信するキーコードについて

  • uno R3 はKeyboardライブラリが使えないのでSerialで書き込みます。
  • Serialで送信するのは基本的には8バイトのデータで、プロトコルは以下の通りとなります。
  • USB仕様HID編8.3 Report Format for Array Items に詳細が記載されています。
Byte Contents
0 : Bit0 Left CTRL
0 : Bit1 Left SHIFT
0 : Bit2 Left ALT
0 : Bit3 Left GUI
0 : Bit4 Right CTRL
0 : Bit5 Right SHIFT
0 : Bit6 Right ALT
0 : Bit7 Right GUI
1 今回使わない
2~7 HID active key codes

キーコードについては 仕様10 Keyboard/Keypad Page (0x07) にあるリストから探してください。
送信したいのは ESC キーでなのでリストから 0x29 となります。

回路作成

誤操作を防ぐためにプルダウン回路にします。

switch.png

ビルド環境作成

コマンドラインで開発したいので、platformIOを使います。IDEでSketchをプログラムする場合は飛ばしてください。

  • virtualenv上で、platformIOをインストールします
# workon virtualenv
$ mkvirtualenv --no-site-package arduino

# install platformio
$ pip install platformio
$ pip install --egg scons
  • プロジェクト作成
$ mkdir esc_keyboard; cd ./esc_keyboard;
$ platformio init --board=uno
$ echo ".pioenvs" > .gitignore
$ git init; git add -A; git commit -a -m "init";
  • R3のデバイスを調べてコンフィグに設定します (下のスクリプトだと peco が必要)
$ uno=`platformio serialports list |grep usb | peco`; echo "upload_port="$uno >> platformio.ini
$ git commit -a -m "add upload port"
  • makefileも作成
Makefile
all: build install monitor

build:
    platformio run

# ターゲットボードにアップロードします
install:
    platformio run --target=upload

# シリアルをモニタします
# デバイス指定したい場合はオプションを追加してください (-p /dev/xxx)
# モニタを止めたい場合 ctrl+[
monitor:
    platformio serialports monitor

clean:
    platformio run --target=clean

.PHONY: all build install clean monitor

プログラム作成して書き込む

  • プログラムは愚直なコードですが以下のとおりです。
./src/main.ino
#define LED_PIN 13

#define PIN_ESC  2

#define KEY_RESET       0x00
#define KEY_ESC         0x29

uint8_t keyData[8] = { 0, 0, 0, 0, 0, 0, 0 };

void handler(int pin, uint8_t keycode) {
    if (digitalRead(pin) == 1) {
        digitalWrite(LED_PIN, HIGH);

        keyData[2] = keycode;
        Serial.write(keyData, 8);   // send keypress
        keyData[2] = KEY_RESET;
        Serial.write(keyData, 8);   // release key

        while( digitalRead(pin) == 1) {
            delay(100);
        }
        digitalWrite(LED_PIN, LOW);
    }
}

void setup() {
    pinMode(LED_PIN, OUTPUT);
    pinMode(PIN_ESC,  INPUT);
    Serial.begin(9600);
    delay(2000);
}

void loop() {
    handler(PIN_ESC,  KEY_ESC);
}

  • buildして書き込む
# build (platformio run と同じ)
$ make build

# 書き込み (platformio run --target=upload と同じ)
$ make install

# シリアル出力を確認 (platformio serialports monitor と同じ)
$ make monitor

スイッチ押下でR3に組み込まれているLEDが点灯し、よくわからない文字列がシリアル出力されていればプログラムはOKです。
USBプロトコルに従ったシリアルデータを送るプログラムの書き込みができました。最後に再度USBキーボードとして認識させるために ATmega 16u2 にHexファイルを書き込みます。

R3をUSBキーボードとして認識させる

USBキーボードとして認識させるために、再度DFUモードにしてHexファイルを書き換えます。

  • 前述の方法で、DFUモードにしてください
  • dfu-programmer でUSBシリアルHexファイルを書き込みます
# 削除
$ dfu-programmer atmega16u2 erase
    :
# 書き込み
$ dfu-programmer atmega16u2 flash ./hex/Arduino-keyboard-0.3.hex
    :
# リセット
$ dfu-programmer atmega16u2 reset
    :

どうでしょうか、R3がキーボードとして認識して、かつスイッチ押下すると、、、ESCキー押したことになっていると思います。ESCキーボードの完成!無理やりDashButtonの箱にR3入れてみました。

スクリーンショット 2016-12-07 1.00.25.png

まとめ

DFUも含めた開発の流れとしては面倒ですが以下のような感じです。

  1. DFUモードでusbシリアルHexファイルを ATmega 16u2 に書き込む
  2. sketchプログラム作成
  3. IDEなどで ATmeaga 328p に書き込む
  4. DFUモードでKeyboardデバイスHexファイルを ATmega 16u2 に書き込む
  5. 確認してsketchプログラムを修正したいのならば 最初へ戻る

今回は単純な構造なのでシンプルなプログラムで実装しましたが、フルスペックキーボードの場合、もっと効率的なプログラムが必要かと思います。
正直 Arudiono Micro の方が小さくて簡単にキーボード認識させることができるので実用的だと思いますが、DFUというもう少し踏み込んだ開発は楽しいですね。
USBキーボードだけでなく、Hexファイルを変えればマウスにも、MIDI(Moco For LUFA)にもなります。
USB仕様を把握してLUFAライブラリを使って自分で好きなデバイスにもできると思います。サンプルも充実しているので是非チェックしてみてください。

今回のソースはGithubにあります。

36
28
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
36
28