どうも、バイト戦士です。
今回は、左手デバイスを自作しました。
やりたいことやるんじゃぁッ
Youtube始めます
今回は動画を作りました。
概要はまとめることができたと思います。
キーボードを自作する理由を考える
何をやるにも理由は大事。
僕の場合は理由を特に決めずに作り始めて「ああ、、なんかうまくいかないな、、、、」となり、目的がないので修正しなければいけない理由も見つからない、、、以後、雲散霧消、、、
といった感じに自然消滅した事例が多いので、理由をちゃんと考えてから始めることにしました。
自作キーボードを作る理由①
なんか作るうえで理由は大事。
そしてワイが左手用のキーボードが欲しい理由、それは、
左手がいてぇ。
ということ。具体的にはCtrl+Zキーを連打するときです。

みての通り、小指と薬指を酷使します。
最近は慣れてきた感もありますが、左手の左半分が腱鞘炎っぽくなったりするのでマジで何とかしたいです。
自作キーボードを作る理由②
理由2つめ。
市販品がたけぇ。
キーボードって安いものだと1000円しないんですが、なぜか左手キーボードになると平均的に1万円くらいします。
左手デバイスってキーの数がノーマルのキーボードより減っているはずなのに高いです。
やっぱり「必要最低限」から外れるデバイスというのは、良くも悪くも「カスタムパーツ」なんだと思います。
もちろん、価値を感じて物品を購入すること、それ自体を否定はしません。
が、自分のように普段の検索や落書き趣味絵を描く程度の人間に、3万円のデバイスはちょっと高いです。
だから安く作ってやろう、というわけです。
自作キーボードを作る理由③
理由三つ目。
お高いデバイスをポテチ食ったり飯食いながら触りたくはない。
(ものがいいだけに、買ったら多分大切にします。)
一般的に既製品が高くなるのは原価よりも広告費や開発費に上乗せして「利益」を出すからだと思います。
その点自作なら「利益」に相当する部分がないので出費した金額と工夫の数だけ自分好みに仕上げることができます。
左手デバイスといえど、普通に使いたい。
でもって、完全に自分好みのデバイスに仕上げたい。
そういうわけです。なので今回は自作。
全体的な仕様を決める
まずは仕様を決めます。
おおざっぱに、どんな機能がほしいかなぁ~っていうのを決めていきます。
キーの種類と数
今回は以下のキーを実装することにします。
①頻繁に使うキー(E/J、W、A、S、D、1、2、3、4)
②Ctrl キーと Z キーの代用スイッチ(腱鞘炎防止)
③リニア入力デバイス_ゲームパッドスイッチ
④リニア入力デバイス_ロータリーエンコーダ(拡大率調整)
⑤リニア入力デバイス_スライド抵抗器(拡大率詳細調整)
こんな感じです。割と数は多め。
PCとの接続
接続に求める条件は以下の通り。
①USBでパソコンと接続する
②バッテリとかはつけない(充電とかめんどくさい)
ここで細かく書いてもいいですが、基本的にプログラムをいじればどうとでもなるのでここでは考えないことにします。
短いですが、以上。これを満たすものを作ります。
全体の構成を決める
全体の構成(回路図)は以下のようになります。

今回はHIDデバイスとしての認識・設定・パソコンとの通信などはArduino Pro Microにすべて任せます。
あとはつけたいスイッチの種類(キー、エンコーダ、スライド抵抗器、etc.)に応じたコードを書きこんでいくだけです。
Arduinoにはマウスやキーボードの操作に対応するためのライブラリが用意されているので、複雑なコードは必要ないです。
必要なパーツを集める
欲しい機能や仕様をだいたい決めたので、次はパーツを購入していきます。
今回はなるべくコスパ良く仕上げたかったので、キーやスイッチなどはAmazonから格安で購入しました。
(お好みのものを使えばモチベーションが上がると思います。スイッチは互換品にこだわらなくてもいいかも?)
マイコンボード
ぶっちゃけこの辺のマイコンはストックしておいても困らないので2個入を購入しました。
単品も売ってますが届くまでに時間かかるね。。。。
それはそうと、なんか昔に比べてだいぶ値上がりしている感があります。
KeyStudioあたりで安いArduino Leonardoだしてくれないかな・・・・・
キースイッチ
キーはこちらを選びました。赤軸です。(多分互換品です)
そういえば「赤軸」って商標なんですかね?
結構いろいろなところで使われてますけど単に押し心地の分類みたいな使われ方をしている気がします。。。
キーキャップ
キーキャップです。
クソ安いですがつくりはしっかりしてました。(バリとかはナシ。ベタベタしていたりとかもない。)
ただカラーリングはもうちょっとましなものを購入すればよかったですね。。。
ジョイスティック
ゲームパッドなどについているジョイススティックと同じものです。
激安中華パーツの部類のなので耐久性はどうかわかりませんが、今んとこ大丈夫。
ロータリーエンコーダ
最後にロータリーエンコーダです。
回転数の制限がなく、いつまでもコリコリ回せる音量調節とかのやつです。
マウスホイール操作を行うために使います。
基板
正直基板は必要ないかもしれないですね。
スイッチの数が少ない場合はマイコンの足に線を直につけてもいいと思います。
個人的にショートが怖いので基板に線を出して間隔を広げて使うことにします。
組み立てをする!
準備が整ったので次は組み立てに入ります。
スイッチ類をはんだ付け
まずスイッチにはんだ付けを行っていきます。
今回は数が少ないので直接線をはやして接続します。

やって驚いたのですが、このスイッチ割と耐熱性あります。
はんだ付けが長引いたらすぐに端子周りが融けてしまう、、、なんてことを創造していたんですが、大丈夫でした。
テストパーツに組付け
ハードがないとテストしにくいのでテストパーツの筐体を作ってしまうことにしました。
何かにスイッチを固定してからテストした方がいいですね。
印刷した筐体は以下のような感じです。

う~ん。
テスト(のちに捨てる)ので超絶適当な設定(レイヤー高さ0.5、サポート材なし)でやったんですが、いくら高品質なプリンタといえど無理がありますね、、、
ていうかフィラメントの押出径が0.4mmなのになんで0.5mmで積層できたんだ?
まぁ、これは後で捨ててしまうのでひとまず良しとします。
部品を取り付けて何となくイメージをつかむ
先ほどはんだ付けしたスイッチなど、おおよその部品を取り付けてみました。

見た目は微妙ですが、大体の完成予想はできました。
割と適当でもいいかと思ったんですけど、結構使いにくさが際立ちました。
ちゃんと考えないとゴミを作ってしまいそう。
自作してみたいひとはこの辺を気を付けた方がいいかもですね。
プログラムを作成する
適当にキースイッチ、ロータリーエンコーダ、ジョイスティックを動かすためのコードを作成します。
プログラムを以下に示します。
#include <Mouse.h>
#include <Keyboard.h>
//エンコーダ設定
const int Encoder_phase_A=2;
const int Encoder_phase_B=3;
volatile int Encoder_pos = 0; //何も操作しなかったとき(エンコーダを操作しなかったとき)にコンパイラに消されることを防ぐ
volatile uint8_t AB_prev = 0;
static int count_now=0; //スクロールの一度の操作量を制限
//Joystick設定
const int Joystick_switch = 9;
const int Joystick_x_Axis = A1;
const int Joystick_y_Axis = A0;
int range = 25; //XY軸動作範囲
int center = range / 2; //Joystick中心位置
int threshold = range / 4; //Joystickデッドゾーン
//Arduino入力ピン割り当て
const int Key1_input = 4;
const int Key2_input = 5;
const int Key3_input = 6;
const int Key4_input = 7;
const int Key5_input = 10;
const int Key6_input = 16;
const int Key7_input = 14;
const int Key8_input = 15;
//入力ピン-入力文字割り当て
const char Key1='w';
const char Key2='a';
const char Key3='s';
const char Key4='d';
const char Key5='1';
const char Key6='2';
const char Key7='3';
const char Key8='4';
//
#define Key1_Joystick_switch KEY_LEFT_CTRL //ショートカット用:2つ入力
const char Key2_Joystick_switch = 'z';
//プロトタイプ宣言
void Encoder();
void readAxis();
void setup() {
//エンコーダ信号入力:A/B
pinMode(Encoder_phase_A,INPUT_PULLUP); //プルアップ抵抗を有効化
pinMode(Encoder_phase_B,INPUT_PULLUP); //プルアップ抵抗を有効化
//Joystick信号入力:X/Y,switch
pinMode(Joystick_switch, INPUT_PULLUP);
pinMode(Joystick_x_Axis, INPUT);
pinMode(Joystick_y_Axis, INPUT);
//キー入力
pinMode(Key1_input,INPUT_PULLUP); //Wキー
pinMode(Key2_input,INPUT_PULLUP); //Aキー
pinMode(Key3_input,INPUT_PULLUP); //Sキー
pinMode(Key4_input,INPUT_PULLUP); //Dキー
pinMode(Key5_input,INPUT_PULLUP); //1キー
pinMode(Key6_input,INPUT_PULLUP); //2キー
pinMode(Key7_input,INPUT_PULLUP); //3キー
pinMode(Key8_input,INPUT_PULLUP); //4キー
//ロータリーエンコーダ入力
attachInterrupt(0,Encoder,CHANGE);
attachInterrupt(1,Encoder,CHANGE);
//その他
Mouse.begin();
Keyboard.begin();
Serial.begin(9600);
}
void loop() {
//Serial.println(Encoder_pos);
//*******エンコーダ設定*******
if(Encoder_pos == 1){
Mouse.move(0,0,1);
}else if(Encoder_pos == -1)
{
Mouse.move(0,0,-1);
}
Encoder_pos=0;
//*******Joystick設定*******
if(digitalRead(Joystick_switch) == LOW){
Keyboard.press(Key1_Joystick_switch);
Keyboard.press(Key2_Joystick_switch);
}else if(digitalRead(Joystick_switch) == HIGH){
Keyboard.release(Key1_Joystick_switch);
Keyboard.release(Key2_Joystick_switch);
}
Mouse.move(readAxis(Joystick_x_Axis), (-1*readAxis(Joystick_y_Axis)),0);
//*******キー設定*******
if (digitalRead(Key1_input) == LOW){
Keyboard.press(Key1);
}else{
Keyboard.release(Key1);
}
if (digitalRead(Key2_input) == LOW){
Keyboard.press(Key2);
}else{
Keyboard.release(Key2);
}
if (digitalRead(Key3_input) == LOW){
Keyboard.press(Key3);
}else{
Keyboard.release(Key3);
}
if (digitalRead(Key4_input) == LOW){
Keyboard.press(Key4);
}else{
Keyboard.release(Key4);
}
if (digitalRead(Key5_input) == LOW){
Keyboard.press(Key5);
}else{
Keyboard.release(Key5);
}
if (digitalRead(Key6_input) == LOW){
Keyboard.press(Key6);
}else{
Keyboard.release(Key6);
}
if (digitalRead(Key7_input) == LOW){
Keyboard.press(Key7);
}else{
Keyboard.release(Key7);
}
if (digitalRead(Key8_input) == LOW){
Keyboard.press(Key8);
}else{
Keyboard.release(Key8);
}
}
void Encoder(){
uint8_t A=digitalRead(2); //A相の信号を読み取る:読み取り値 0 or 1
uint8_t B=digitalRead(3); //B相の信号を読み取る:読み取り値 0 or 1
uint8_t AB = (A << 1) | B; //A相の読み値を1bit上位に、B相の読み値を下位に配置:2bitの2進数表現
uint8_t ABAB = (AB_prev << 2) | AB; //最新の読み値を下位2bitに配置、[4321]の順に配置
if(ABAB == 0b1101 || ABAB == 0b0100 || ABAB == 0b0010 || ABAB == 0b1011 ) //正回転
{
Encoder_pos=1;
}else if(ABAB == 0b1110 || ABAB == 0b0111 || ABAB == 0b0001 || ABAB == 0b1000 ) //逆回転
{
Encoder_pos=-1;
}
AB_prev=AB; //現在のA相とB相の組み合わせ(2進数2bit)を保存しておく
}
int readAxis(int thisAxis){
int reading = analogRead(thisAxis); //値を読み出す
reading = map(reading, 0, 1023, 0, range); //ADCの値を実際の範囲に変換
int distance = reading -center; //正負の値に変換
if(abs(distance) < threshold){
distance = 0; //閾値以下なら、Joystickは中心位置にあるものとする:誤作動防止のデッドゾーン
}
return distance;
}
Mouse.hとKeyboard.hのライブラリは最初から入っていると思っていなかったんですが、いつの間にか標準搭載になってました・・・
(ていうかエディタ自体がCCSみたいな仕様に変更されていてすごく驚きました。。。。)
ライブラリ内の関数の使い方は検索すればだいたい出てくると思います。
いやぁ~、、、、Arduinoめっちゃ便利やな、、、
通信テスト
確認ができたら通信テストを行ってみます。
とりあえず問題はなさそうでした。

ジョイスティック・ロータリーエンコーダ・キースイッチ、三つとも正常でした。
さすがキースイッチ、なんも保護入れてないけど一切チャタリングはないですね。
この辺の技術って普通のスイッチに適用できないんですかね。
(マウスのスイッチとかね。。。。)
ロータリーエンコーダの動作とプログラムの紹介
今回マウスホイールの動作を左手で実現したかったため、ロータリーエンコーダを使用しています。
ロータリーエンコーダの動作原理、、、というか回転方向の認識の仕方は以下の通り。

プログラムの書き方によっては速度も検知しますが、今回は向きだけで充分。
チャタリング防止・誤検知防止のためにいくつか書き方はあるようですが、この書き方が一番正確なようです。
今回購入したロータリーエンコーダは指で回してコリコリとした感触が感じられるくらい分解能が低いです。
エンコーダを回すと感触があるので、今回はこれを一回検知したらマウスホイールを回転させたと認識させることにしました。
ホイール操作の誤作動って結構イライラするので、これだけは割り込み処理で実行することにします。
Arduinoにおける割り込み処理はとても簡単です。
以下のように「attachInterrupt(”割り込み番号”, 実行する関数, 実行モード)」と書くだけです。
attachInterrupt(0,Encoder,CHANGE);
ここで割り込みを使うことにより、ロータリーエンコーダを動かした場合だけは loop() よりも優先的に実行してくれます。
ロータリーエンコーダの操作がもたつかない、ということです。
ジョイスティックの動作とプログラムの紹介
ジョイスティックの動作原理は以下のような感じです。

回転タイプの可変抵抗器がx軸とy軸についているので、この値をよみとってどれだけスティックが傾いているのか検知します。
Arduinoには可変抵抗器で分圧した電圧が入力されます。
Arduino内では5Vの電圧はデジタル値で1023に変換されます。(10bit)
あとは、これをmap関数(Arduino標準搭載)という便利な関数で0~1023→マウス操作量へ変更し、Mouse.move関数へ値を渡せばOKです。
本番用筐体の作成
中身のハード、プログラムは完成したので、最後に筐体を作り直します。
再設計
先ほどテストパーツを作ってみて感じた不満点・違和感は以下のような感じでした。(再掲)

これを踏まえたうえで形状を変更しました。だいぶ使いやすくなったはず!
筐体完成!
最後にテストパーツに組み込んでいた部品を移植しました。
全体として完成図は以下のような感じです。

色が超微妙です・・・・・
なんだか色の組み合わせがあべこべすぎて人体標本みたいな感じがします。(赤と茶色と白と透明。。。。)
せめてプリント用素材の色はもうちょいましなものを選べばよかったですね。
完成!
完成しました。
いやぁ~、、、実際蓋が閉まらないとか紆余曲折あったんですが、個人的には結構上出来だと思います。

ただまぁ、使用感は、極めて普通。
超絶悪くはないし、超絶良いわけでもない。
いわゆる普通のデバイスですね。
個人的に案外こういう並な感想が出た方が長く使える感触があります。(盛り上がるわけでもなく落ち込むわけでもないヤツ)
部品取りは免れそうだぜ☆!
まとめ
今回はマイコンを使用してキーボードを自作する方法を紹介しました。
購入してもいいですが、飯食ったりポテトチップスを食ったり炭酸飲んだりしながら使いたい(神経使わずに使いたい)のでなるべく安く作ってみました。
無論、実用性はちゃんとあります。
キースイッチなどは全部使っていないので、全体としてかかった費用は2500円くらいになるかと思います。
参考になれば幸いです。
それでは。
興味あったらどうぞ
動画もよろしくお願いします!