使用 Arduino Pro Micro 製作 USB鍵盤

最近有人向在下查詢,有否外接的 USB數字鍵盤 是 電話按鈕部局
在下在 Google 上嘗試搜尋 usb telephone layout numpad ,大部分都是 電腦按鈕部局 的 USB數字鍵盤
而 電話按鈕部局 都是工業用途,不鏽鋼外殼、防水、12個按扭 已經需要大約 50美元
50美元 只使用 12個按鈕 又有點貴,在下又要想想方法有否平價方案

Arduino Pro Micro

Arduino Pro Micro 使用 ATmega32U4晶片 ,是一種具備 抽像控制模型 (Abstract Control Model (ACM)) 的晶片
能讓電子訊號轉模擬成 USB訊號

外觀
見下文
見下文
Arduino Pro Micro 的正面

見下文
Arduino Pro Micro 的背面

引腳
引腳編號編號引腳
D1124RAW
D0223GND
GND322RST
GND4215V
D2520A3
D3~619A2
D4718A1
D5~817A0
D6~916D15
D71015D14
D81114D16
D9~1213D10~

編號引腳方向功能
1D1輸入/輸出第1數碼引腳, UART TX引腳
2D0輸入/輸出第0數碼引腳, UART RX引腳
3GND接地
4GND接地
5D2輸入/輸出第2數碼引腳, I 2 C SDA引腳
6D3~輸入/輸出第3數碼引腳, I 2 C SCL引腳,有 PWM 功能
7D4輸入/輸出第4數碼引腳
8D5~輸入/輸出第5數碼引腳,有 PWM 功能
9D6~輸入/輸出第6數碼引腳,有 PWM 功能
10D7輸入/輸出第7數碼引腳
11D8輸入/輸出第8數碼引腳
12D9~輸入/輸出第9數碼引腳,有 PWM 功能
13D10~輸入/輸出第10數碼引腳,有 PWM 功能
14D16輸入/輸出第16數碼引腳,SPI MOSI引腳
15D14輸入/輸出第14數碼引腳,SPI MISO引腳
16D15輸入/輸出第15數碼引腳,SPI SCK引腳
17A0輸入/輸出第0類比引腳
18A1輸入/輸出第1類比引腳
19A2輸入/輸出第2類比引腳
20A3輸入/輸出第3類比引腳
215V輸出 5V 電源
22RST接地時重新啟動
23GND接地
24RAW接受 4.8V 至 12V 電源
(沒有穩電器,要注意電壓變化)

線路原型
見下文
其實只是接駁一個按鈕作測試

實際線路
見下文
因為線路簡單,實際線路與線路原型接近相同

見下文
編寫測試的 Sketch 並上載到 Arduino Pro Micro
在下使用 Arduino 預設的 Keyboard.h
#include <Keyboard.h>

const byte SIG = A3;

void setup() {
	Keyboard.begin();
	pinMode(SIG, INPUT);
	pinMode(LED_BUILTIN_TX, OUTPUT);
	pinMode(LED_BUILTIN_RX, OUTPUT);
}

void loop() {
	if (digitalRead(SIG) == HIGH) {
		digitalWrite(LED_BUILTIN_TX, HIGH);
		digitalWrite(LED_BUILTIN_RX, LOW);
		Keyboard.press('A');
	} else {
		digitalWrite(LED_BUILTIN_TX, LOW);
		digitalWrite(LED_BUILTIN_RX, HIGH);
		Keyboard.release('A');
	}
}

Keyboard.h 非常容易使用
語法功能
Keyboard.begin();
啟動鍵盤模擬
Keyboard.end();
結束鍵盤模擬
Keyboard.print(char);
Keyboard.print(char[]);
Keyboard.print(String);
模擬鍵盤輸入 字元、字元陣列、字串
Keyboard.println();
Keyboard.println(char);
Keyboard.println(char[]);
Keyboard.println(String);
模擬鍵盤輸入 新行、字元、字元陣列、字串
Keyboard.write(char);
模擬鍵盤輸入 字元
Keyboard.press(char);
模擬鍵盤按著 字元
Keyboard.release(char);
模擬鍵盤釋放 字元
Keyboard.releaseAll();
模擬鍵盤釋放 所有字元

自製鍵盤

在下為了能方便安裝及卸除 Arduino Pro Micro 及用盡 16支引腳,設計一塊簡單的電路板測試

見下文
電路板設計圖

見下文
16支引腳 其中 8支引腳 控制列,另外 8支引腳 控制欄

見下文
在下使用 試算表儲存格的命名方式 配置按鈕的名稱

見下文
見下文
連接到 Arduino Pro Micro 的引腳亦配置 欄列名稱

見下文
所有線都配置在電路板背面

見下文
見下文
將 100KΩ電阻器 及 引腳座 焊接到 電路板
下拉電阻可以讓訊號保低電壓,避免訊號不穩定

見下文
由於不需要使用 Arduino Pro Micro 第1引腳 至 第4引腳 ,因此在下「偷工減料」一點,焊少4支引腳

見下文
見下文
見下文
將 Arduino Pro Micro 安裝到 引腳座

見下文
焊接 64個按鈕 到 電路板

見下文
除了 Keyboard.h ,在下還使用 Keypad.h 測試按鍵效果

#include <Keypad.h>
#include <Keyboard.h>
const byte ROW_COUNT = 8;
const byte COL_COUNT = 8;
const byte ROW_PINS[ROW_COUNT] = {A3, A2, A1, A0, 15, 14, 16, 10};
const byte COL_PINS[COL_COUNT] = {2, 3, 4, 5, 6, 7, 8, 9};
const byte KEYS[ROW_COUNT][COL_COUNT] = {
	{0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18},
	{0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28},
	{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38},
	{0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48},
	{0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58},
	{0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68},
	{0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78},
	{0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88}
};
Keypad keypad = Keypad(makeKeymap(KEYS), ROW_PINS, COL_PINS, ROW_COUNT, COL_COUNT);
void setup() {
	pinMode(LED_BUILTIN_RX, OUTPUT);
	pinMode(LED_BUILTIN_TX, OUTPUT);
	Keyboard.begin();
}
void loop() {
	digitalWrite(LED_BUILTIN_RX, HIGH);
	digitalWrite(LED_BUILTIN_TX, HIGH);
	const byte KEY = keypad.getKey();
	if (KEY > 0) {
		if (KEY == 0x11) {
			// select all
			Keyboard.press(0x84);
			Keyboard.write(0x8C);
			Keyboard.release(0x84);
		} else if (KEY == 0x12) {
			// cut
			Keyboard.press(0x84);
			Keyboard.write(0xA3);
			Keyboard.release(0x84);
		} else if (KEY == 0x13) {
			// copy
			Keyboard.press(0x84);
			Keyboard.write(0x8E);
			Keyboard.release(0x84);
		} else if (KEY == 0x14) {
			// paste
			Keyboard.press(0x84);
			Keyboard.write(0xA1);
			Keyboard.release(0x84);
		} else if (KEY == 0x15) {
		} else if (KEY == 0x16) {
		} else if (KEY == 0x17) {
		} else if (KEY == 0x18) {
		} else if (KEY == 0x21) {
		} else if (KEY == 0x22) {
		} else if (KEY == 0x23) {
		} else if (KEY == 0x24) {
		} else if (KEY == 0x25) {
		} else if (KEY == 0x26) {
		} else if (KEY == 0x27) {
		} else if (KEY == 0x28) {
		} else if (KEY == 0x31) {
		} else if (KEY == 0x32) {
		} else if (KEY == 0x33) {
		} else if (KEY == 0x34) {
		} else if (KEY == 0x35) {
		} else if (KEY == 0x36) {
		} else if (KEY == 0x37) {
		} else if (KEY == 0x38) {
		} else if (KEY == 0x41) {
		} else if (KEY == 0x42) {
		} else if (KEY == 0x43) {
		} else if (KEY == 0x44) {
		} else if (KEY == 0x45) {
		} else if (KEY == 0x46) {
		} else if (KEY == 0x47) {
		} else if (KEY == 0x48) {
		} else if (KEY == 0x51) {
		} else if (KEY == 0x52) {
		} else if (KEY == 0x53) {
		} else if (KEY == 0x54) {
		} else if (KEY == 0x55) {
		} else if (KEY == 0x56) {
		} else if (KEY == 0x57) {
		} else if (KEY == 0x58) {
		} else if (KEY == 0x61) {
		} else if (KEY == 0x62) {
		} else if (KEY == 0x63) {
		} else if (KEY == 0x64) {
		} else if (KEY == 0x65) {
		} else if (KEY == 0x66) {
		} else if (KEY == 0x67) {
		} else if (KEY == 0x68) {
		} else if (KEY == 0x61) {
		} else if (KEY == 0x62) {
		} else if (KEY == 0x63) {
		} else if (KEY == 0x64) {
		} else if (KEY == 0x65) {
		} else if (KEY == 0x66) {
		} else if (KEY == 0x67) {
		} else if (KEY == 0x68) {
		} else if (KEY == 0x71) {
		} else if (KEY == 0x72) {
		} else if (KEY == 0x73) {
		} else if (KEY == 0x74) {
		} else if (KEY == 0x75) {
		} else if (KEY == 0x76) {
		} else if (KEY == 0x77) {
		} else if (KEY == 0x78) {
		} else if (KEY == 0x81) {
		} else if (KEY == 0x82) {
		} else if (KEY == 0x83) {
		} else if (KEY == 0x84) {
		} else if (KEY == 0x85) {
		} else if (KEY == 0x86) {
		} else if (KEY == 0x87) {
		} else if (KEY == 0x88) {
		}
		digitalWrite(LED_BUILTIN_TX, LOW);
	}
	delay(10);
}

示範中在下使用了
  • 向左選取 (Shift + 左)
  • 向右選取 (Shift + 右)
  • 向上選取 (Shift + 上)
  • 向下選取 (Shift + 下)
  • 全選 (Ctrl + A)
  • 剪下 (Ctrl + X)
  • 複製 (Ctrl + C)
  • 貼上 (Ctrl + V)
讓 Arduino Pro Micro 模擬這些組合鍵輸入,變成單鍵操作

8位元 ASCII表

Arduino 的 Keyboard.h 定義了 8位元 ASCII表 ,即是 0x00 至 0xFF 的字元

十進制十六進制二進制用途
ASCII標準鍵盤數字鍵盤
00x00B00000000Null
10x01B00000001Start of Heading
20x02B00000010Start of Text
30x03B00000011End of Text
40x04B00000100End of Transmission
50x05B00000101Enquiry
60x06B00000110Acknowledge
70x07B00000111Bell
80x08B00001000Backspace
90x09B00001001Horizontal Tabulation
100xAB00001010Line Feed
110xBB00001011Vertical Tabulation
120xCB00001100Form Feed
130xDB00001101Carriage Return
140xEB00001110Shift Out
150xFB00001111Shift In
160x10B00010000Data Link Escape
170x11B00010001Device Control 1
180x12B00010010Device Control 2
190x13B00010011Device Control 3
200x14B00010100Device Control 4
210x15B00010101Negative Acknowledgement
220x16B00010110Synchronous Idle
230x17B00010111End of Transmission Block
240x18B00011000Cancel
250x19B00011001End of Medium
260x1AB00011010Substitute
270x1BB00011011
280x1CB00011100File Separator
290x1DB00011101Group Separator
300x1EB00011110Record Separator
310x1FB00011111Unit Separator
320x20B00100000空格 ( )
330x21B00100001嘆號 (!)
340x22B00100010雙引號 (")
350x23B00100011井號 (#)
360x24B00100100錢幣號 ($)
370x25B00100101百分號 (%)
380x26B00100110及號 (&)
390x27B00100111單引號 (')
400x28B00101000開圓括號 (
410x29B00101001關圓括號 )
420x2AB00101010星號 (*)
430x2BB00101011加號 (+)
440x2CB00101100逗號 (,)
450x2DB00101101減號 (-)
460x2EB00101110點 (.)
470x2FB00101111斜線 (/)
480x30B00110000數字0
490x31B00110001數字1
500x32B00110010數字2
510x33B00110011數字3
520x34B00110100數字4
530x35B00110101數字5
540x36B00110110數字6
550x37B00110111數字7
560x38B00111000數字8
570x39B00111001數字9
580x3AB00111010冒號 (:)
590x3BB00111011分號 (;)
600x3CB00111100小於號 (<)
610x3DB00111101等於號 (=)
620x3EB00111110大於號 (>)
630x3FB00111111問號 (?)
640x40B01000000在號 (@)
650x41B01000001大寫字母A
660x42B01000010大寫字母B
670x43B01000011大寫字母C
680x44B01000100大寫字母D
690x45B01000101大寫字母E
700x46B01000110大寫字母F
710x47B01000111大寫字母G
720x48B01001000大寫字母H
730x49B01001001大寫字母I
740x4AB01001010大寫字母J
750x4BB01001011大寫字母K
760x4CB01001100大寫字母L
770x4DB01001101大寫字母M
780x4EB01001110大寫字母N
790x4FB01001111大寫字母O
800x50B01010000大寫字母P
810x51B01010001大寫字母Q
820x52B01010010大寫字母R
830x53B01010011大寫字母S
840x54B01010100大寫字母T
850x55B01010101大寫字母U
860x56B01010110大寫字母V
870x57B01010111大寫字母W
880x58B01011000大寫字母X
890x59B01011001大寫字母Y
900x5AB01011010大寫字母Z
910x5BB01011011開方括號 ([)
920x5CB01011100反斜線 (\)
930x5DB01011101關方括號 (])
940x5EB01011110上標號 (^)
950x5FB01011111底線 (_)
960x60B01100000反引號 (`)
970x61B01100001小寫字母a
980x62B01100010小寫字母b
990x63B01100011小寫字母c
1000x64B01100100小寫字母d
1010x65B01100101小寫字母e
1020x66B01100110小寫字母f
1030x67B01100111小寫字母g
1040x68B01101000小寫字母h
1050x69B01101001小寫字母i
1060x6AB01101010小寫字母j
1070x6BB01101011小寫字母k
1080x6CB01101100小寫字母l
1090x6DB01101101小寫字母m
1100x6EB01101110小寫字母n
1110x6FB01101111小寫字母o
1120x70B01110000小寫字母p
1130x71B01110001小寫字母q
1140x72B01110010小寫字母r
1150x73B01110011小寫字母s
1160x74B01110100小寫字母t
1170x75B01110101小寫字母u
1180x76B01110110小寫字母v
1190x77B01110111小寫字母w
1200x78B01111000小寫字母x
1210x79B01111001小寫字母y
1220x7AB01111010小寫字母z
1230x7BB01111011開曲括號 ({)
1240x7CB01111100管線 (|)
1250x7DB01111101關曲括號 (})
1260x7EB01111110波紋號 (~)
1270x7FB01111111Delete
1280x80B10000000左Ctrl鍵
1290x81B10000001左Shift鍵
1300x82B10000010左Alt鍵
1310x83B10000011Meta鍵
1320x84B10000100右Ctrl鍵
1330x85B10000101右Shift鍵
1340x86B10000110右Alt鍵
1350x87B10000111OS鍵
1360x88B10001000
1370x89B10001001
1380x8AB10001010
1390x8BB10001011
1400x8CB10001100小寫字母a鍵
1410x8DB10001101小寫字母b鍵
1420x8EB10001110小寫字母c鍵
1430x8FB10001111小寫字母d鍵
1440x90B10010000小寫字母e鍵
1450x91B10010001小寫字母f鍵
1460x92B10010010小寫字母g鍵
1470x93B10010011小寫字母h鍵
1480x94B10010100小寫字母i鍵
1490x95B10010101小寫字母j鍵
1500x96B10010110小寫字母k鍵
1510x97B10010111小寫字母l鍵
1520x98B10011000小寫字母m鍵
1530x99B10011001小寫字母n鍵
1540x9AB10011010小寫字母o鍵
1550x9BB10011011小寫字母p鍵
1560x9CB10011100小寫字母q鍵
1570x9DB10011101小寫字母r鍵
1580x9EB10011110小寫字母s鍵
1590x9FB10011111小寫字母t鍵
1600xA0B10100000小寫字母u鍵
1610xA1B10100001小寫字母v鍵
1620xA2B10100010小寫字母w鍵
1630xA3B10100011小寫字母x鍵
1640xA4B10100100小寫字母y鍵
1650xA5B10100101小寫字母z鍵
1660xA6B10100110數字1
1670xA7B10100111數字2
1680xA8B10101000數字3
1690xA9B10101001數字4
1700xAAB10101010數字5
1710xABB10101011數字6
1720xACB10101100數字7
1730xADB10101101數字8
1740xAEB10101110數字9
1750xAFB10101111數字0
1760xB0B10110000Enter鍵
1770xB1B10110001Escape鍵
1780xB2B10110010Backspace鍵
1790xB3B10110011Tab鍵
1800xB4B10110100Space鍵
1810xB5B10110101減號鍵
1820xB6B10110110等於號鍵
1830xB7B10110111開方括號鍵
1840xB8B10111000關方括號鍵
1850xB9B10111001反斜線鍵
1860xBAB10111010反斜線鍵
1870xBBB10111011分號鍵
1880xBCB10111100單引號鍵
1890xBDB10111101反引號鍵
1900xBEB10111110逗號鍵
1910xBFB10111111點鍵
1920xC0B11000000斜線鍵
1930xC1B11000001大小寫鎖定鍵
1940xC2B11000010F1鍵
1950xC3B11000011F2鍵
1960xC4B11000100F3鍵
1970xC5B11000101F4鍵
1980xC6B11000110F5鍵
1990xC7B11000111F6鍵
2000xC8B11001000F7鍵
2010xC9B11001001F8鍵
2020xCAB11001010F9鍵
2030xCBB11001011F10鍵
2040xCCB11001100F11鍵
2050xCDB11001101F12鍵
2060xCEB11001110Print Screen鍵
2070xCFB11001111Scroll Lock鍵
2080xD0B11010000Pause鍵
2090xD1B11010001Insert鍵
2100xD2B11010010Home鍵
2110xD3B11010011Page Up鍵
2120xD4B11010100Delete鍵
2130xD5B11010101End鍵
2140xD6B11010110Page Down鍵
2150xD7B11010111右箭咀鍵
2160xD8B11011000左箭咀鍵
2170xD9B11011001下箭咀鍵
2180xDAB11011010上箭咀鍵
2190xDBB11011011Num Lock鍵
2200xDCB11011100斜線鍵
2210xDDB11011101星號鍵
2220xDEB11011110減號鍵
2230xDFB11011111加號鍵
2240xE0B11100000Enter鍵
2250xE1B11100001數字1鍵
2260xE2B11100010數字2鍵
2270xE3B11100011數字3鍵
2280xE4B11100100數字4鍵
2290xE5B11100101數字5鍵
2300xE6B11100110數字6鍵
2310xE7B11100111數字7鍵
2320xE8B11101000數字8鍵
2330xE9B11101001數字9鍵
2340xEAB11101010數字0鍵
2350xEBB11101011點鍵
2360xECB11101100小於號鍵
2370xEDB11101101Context Menu鍵
2380xEEB11101110Halt鍵
2390xEFB11101111等號鍵
2400xF0B11110000F13鍵
2410xF1B11110001F14鍵
2420xF2B11110010F15鍵
2430xF3B11110011F16鍵
2440xF4B11110100F17鍵
2450xF5B11110101F18鍵
2460xF6B11110110F19鍵
2470xF7B11110111F20鍵
2480xF8B11111000F21鍵
2490xF9B11111001F22鍵
2500xFAB11111010F23鍵
2510xFBB11111011F24鍵
2520xFCB11111100
2530xFDB11111101
2540xFEB11111110
2550xFFB11111111

在下將輸入字元分為 ASCII字元 、 標準鍵盤 、 數字鍵盤
Keyboard.h 由 0x00 至 0x7F 模擬輸入的內容與 ISO646 的 ASCII字元 相同
但 0x80 至 0xFF 則是由 Keyboard.h 定義,讓 Arduino 能模擬出更真實的鍵盤操作
例如 0x80 至 0x87 的 修飾鍵 ,可以模擬如示範影片的 選取、複製、貼上 等複合鍵的功能

模擬鍵盤時,當使用 Keyboard.press 指定的操作後,必須使用 Keyboard.release 指定相對的操作
或使用 Keyboard.releaseAll 所有操作,避免操作被鎖定,即使拔除 USB 仍然生效
需要按下真實鍵盤上相同操作的按鈕或關機才能取消操作

Keyboard.h 提供模擬實體鍵盤的操作
Keyboard.write('1');
Keyboard.write(0xA6);
Keyboard.write(0xE1);

輸出結果都是 數字1
但實際上 Keyboard.write('1') 只是輸出 數字1
Keyboard.write(0xA6) 是模擬按下 105鍵盤 (105 Keyboard) 的 數字1鍵
而 Keyboard.write(0xE1) 是模擬按下 數字鍵盤 (Number Pad) 的 數字1鍵
細節上 Keyboard.write(0xA6) 及 Keyboard.write(0xE1) 是不同
例如很多電腦遊戲的快捷鍵操作,當指明需要按 數字鍵盤 的 數字鍵 時,按 105鍵盤 的 數字鍵 則無法生效,反之亦然

總結

由於向在下查詢此問題的人,並不太熟識科技知識,記憶力亦不太好
因此在下將經常使用的組合鍵,製作成單鍵操,方便該人士使用

這個自製鍵盤,除左解決到一些對鍵盤操作有困難的人的問題
還可以簡化在下使用一些重覆工序的電腦操作,及方便遊玩的電腦遊戲時快速輸入特定按鍵操作
Arduino 除了 Keyboard.h ,還有 Mouse.h 及 Joystick.h 可以自行製作類似 Marco 的操作
希望在下稍後有時間製作

參考資料