Arduino Leonardでテンキー(のプロトタイピング)を作った話
実は、@catalyst_yuki_kと時々電子工作的なことをするというのを去年くらいからすこしずつやっている。 一時期出張とかコロナでしばらく中断していたけど、最近になって再開してみたらこれが意外と進捗して楽しかったので、やったことを忘れないうちに書いておく。1
ソフトウェア系の blog とかだとやってみた的な記事ってたいていみつかるのだけど、ことハードとなるとこれが意外と見つからなくて、配線図とコードの全文が まるっと見れるところがなかったりして、色々調べたりハマったこともあるので自分たちがやったことを忘れないようにメモしておく。
もし何かやろうという人の参考になれば非常に幸いです。
今回はおいおい自分達でキーボードとか作って行きたいということで、テンキーを作成した。
作ったもの
ちょっと動画では見にくいかもしれないが、こんな感じのテンキーを作った。
ゆくゆくは実際のキーボードにしたいけど、今回はプロトタイピングということで、ブレッドボードとタクトスイッチで代用している。
夏休みの宿題よろしくテンキー作った pic.twitter.com/x4Qa4FZg0R
— nobuhikosawai (@nobuhikosawai) October 10, 2020
写真はこちら

使ったもの
- Arduino Leaonard
- ブレッドボード
- ダイオード
- 10kΩ 抵抗
- タクトスイッチ
もともとはGrove Beginner Kitにある Arduino 互換のボードで作ろうと思っていたのだけど、 搭載されている Arduino が UNO 互換で どうやら UNO だと HID 機能に対応していないようだったので、Arduino Lenoard に変更した。
やったこと
キーマトリックスを実装した。
キーマトリックスは少ないマイコンでキーボードみたいな入出力の多いデバイスを実現するための方法である。
キーボードをナイーブに 1 つの入出力に対してマイコン 1 つみたいな組み方をしてしまうと、キーの数だけマイコンが必要になってしまう。 それではキー増えるたびにどんどんマイコンが必要になってしまうので、こちらの図にあるように、キーごとに入出力に使うピンの組み合わせを変えておくことで、少ないマイコンで多くのキーの入力を読み取っていくという方式になっている。
キーマトリックスの注意点として、同時押しをした場合に意図しないキーの入力が検出されることがある。それを解決するために、意図しない方向へ電流が流れるのを防ぐため、 整流ダイオードをつける必要がある。整流ダイオードには電流を一方向にしか流さない性質がある。
このあたりの説明についてはこちらの記事がわかりやすい。
配線
実際に作成した配線はこちら。
配線図はfritzingで作成した。OSS なので公式のページの他、Githubからも入手が可能。

プログラム
今回作成した Arduino で動作させるコードは以下のとおり。
#include "Keyboard.h"
const int rowNum = 5;
const int colNum = 4;
const int rowPin[rowNum] = { 3,4,5,6,7 };
const int colPin[colNum] = { 8,9,10,11 };
const byte keyMap[rowNum][colNum] = {
{ 83+136, 84+136, 85+136, 86+136},//num / * -
{ 95+136, 96+136, 97+136, 87+136},//7 8 9 +
{ 92+136, 93+136, 94+136, 0x00},//4 5 6 N/A
{ 89+136, 90+136, 91+136, 88+136},//1 2 3 enter
{ 98+136, 0x00, 99+136, 0x00},//0 N/A . N/A
};
bool currentState[rowNum][colNum];
bool beforeState[rowNum][colNum];
int i,j;
void setup() {
for( i = 0; i < rowNum; i++){
pinMode(rowPin[i],OUTPUT);
}
for( i = 0; i < colNum; i++){
pinMode(colPin[i],INPUT);
}
for( i = 0; i < rowNum; i++){
for( j = 0; j < colNum; j++){
currentState[i][j] = LOW;
beforeState[i][j] = LOW;
}
digitalWrite(rowPin[i],LOW);
}
Serial.begin(9600);
Keyboard.begin();
}
void loop() {
for( i = 0; i < rowNum; i++){
digitalWrite( rowPin[i], HIGH );
for( j = 0; j < colNum; j++){
currentState[i][j] = digitalRead(colPin[j]);
if ( currentState[i][j] != beforeState[i][j] ){
Serial.print("key(");
Serial.print(i);
Serial.print(",");
Serial.print(j);
Serial.print(")");
if ( currentState[i][j] == HIGH){
Serial.println(" Push!");
Keyboard.press( keyMap[i][j] );
} else {
Serial.println(" Release!");
Keyboard.release( keyMap[i][j] );
}
beforeState[i][j] = currentState[i][j];
}
}
digitalWrite( rowPin[i], LOW);
}
}
苦労したこと
チャタリング
いくつかのサイトを見て組んでいったのだけど、最初に組んだときは入力していないのに勝手にキーボードからキーが入力されるという現象が発生した。
実際に作った作例がまるっとのっているものがなかったのと、特に抵抗まわりを詳しく書いてるのがなくて、きちんと抵抗を付けていなかったせいで チャタリングを起こしていたのが原因だった。
正しくプルダウン抵抗を接続した結果、チャタリングが起きなくなった。
あとでこちらで知ったのだが、Arduino は内蔵のプルアップ抵抗があるらしく、最初からそれをつかうと自分たちで抵抗を設置する必要がなくもっとシンプルにできたみたいだった。 (プルダウン抵抗はないみたいです。)
USB HID Usage ID
プログラムに使うキーコードだが、ASCII と書いてあるところもあったのだが、実際に試して見ると意図したとおりに入力できない文字があった。
また、テンキーだと NumLock みたいな文字に存在しないコードや、NumLock による挙動の切り替えみたいなものもあって、それらを取り扱える必要がある。
実際には、USB HID Usage IDで入力する必要があるようで、それに修正すると正しく動いた。
注意として Arduino で USB HID Usage ID で取り扱うには 136 を足す必要がある。
参考:
最後に
後日談としてここで作成したテンキーをもとに Arduino 用のシールドの基盤を書いて、Fusion PCBに発注してみた。
続編でそのあたりの話も書いてみたいと思う。
参考にしたサイト
Footnotes
-
一緒にやってると言いつつハードは周りは友人のほうが詳しくて僕は初心者なので、おんぶにだっこで教えてもらってるみたいな感じだったりもします。 ↩