做一把2021年Stack Overflow的愚人节键盘

背景

刷空间的时候,发现有人说今天Stack Overflow的愚人节彩蛋是限制用户复制次数,然后配上了一个键盘的推销图片。
觉得这个键盘还挺有意思的,小巧精致,思考了一下觉得实现起来还挺简单的,基本上把就是把之前做的原神自动弹琴装置稍微改改就成了,脑测代码百行以内

然后打开KiCad直接开干,导出渲染一气呵成。材料已经就绪,键帽先不买,用我现成的。

渲染效果

特性

  • 3%配列机械键盘
  • 支持热插拔,可更换任意轴体
  • 自定义按键,不仅仅是Ctrl+C/V
  • 支持连发模式
  • 易学的源码

材料

序号 名称 数量 备注
1 ATMega32u4 1 主控芯片 QFN44封装
2 AP2112K-3.3 1 5V转3.3V芯片 SOT-23-5
3 16Mhz晶振 1 2.0*1.6
4 1uF电容 1 0603
5 22pf电容 2 0603
6 10uf电容 2 0603
7 1K电阻 5 0805
8 10K电阻 1 0805
9 22R电阻 2 0805
10 凯华热插拔轴座 3 1511系列
11 轴体 3 任意轴体
12 2位拨码开关 1 1.27mm
13 Type-C接口 1 16p
14 肖特基二极管 1 BAT60JFILM SOD-323
15 微动开关 1 3x4x2.5 四脚贴片

焊接

PS:板子的颜色并不代表好坏,我选绿色只是因为他免费24小时加急。

涂上锡膏,风枪对着吹吹就好了。这个QFN44并不是特别难焊,也没有必要为了一块小板子做个钢网。

组装

简单的画了个方盒子作为外壳,然后从我的键盘上扣下来三个键帽装上去。

https://github.com/chaosgoo/HairsPP,求Star.

附:键位编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181

#define KEY_LEFT_CTRL 0x80
#define KEY_LEFT_SHIFT 0x81
#define KEY_LEFT_ALT 0x82
#define KEY_LEFT_GUI 0x83
#define KEY_RIGHT_CTRL 0x84
#define KEY_RIGHT_SHIFT 0x85
#define KEY_RIGHT_ALT 0x86
#define KEY_RIGHT_GUI 0x87

#define KEY_UP_ARROW 0xDA
#define KEY_DOWN_ARROW 0xD9
#define KEY_LEFT_ARROW 0xD8
#define KEY_RIGHT_ARROW 0xD7
#define KEY_BACKSPACE 0xB2
#define KEY_TAB 0xB3
#define KEY_RETURN 0xB0
#define KEY_ESC 0xB1
#define KEY_INSERT 0xD1
#define KEY_DELETE 0xD4
#define KEY_PAGE_UP 0xD3
#define KEY_PAGE_DOWN 0xD6
#define KEY_HOME 0xD2
#define KEY_END 0xD5
#define KEY_CAPS_LOCK 0xC1
#define KEY_F1 0xC2
#define KEY_F2 0xC3
#define KEY_F3 0xC4
#define KEY_F4 0xC5
#define KEY_F5 0xC6
#define KEY_F6 0xC7
#define KEY_F7 0xC8
#define KEY_F8 0xC9
#define KEY_F9 0xCA
#define KEY_F10 0xCB
#define KEY_F11 0xCC
#define KEY_F12 0xCD
#define KEY_F13 0xF0
#define KEY_F14 0xF1
#define KEY_F15 0xF2
#define KEY_F16 0xF3
#define KEY_F17 0xF4
#define KEY_F18 0xF5
#define KEY_F19 0xF6
#define KEY_F20 0xF7
#define KEY_F21 0xF8
#define KEY_F22 0xF9
#define KEY_F23 0xFA
#define KEY_F24 0xFB

{
0x00, // NUL
0x00, // SOH
0x00, // STX
0x00, // ETX
0x00, // EOT
0x00, // ENQ
0x00, // ACK
0x00, // BEL
0x2a, // BS Backspace
0x2b, // TAB Tab
0x28, // LF Enter
0x00, // VT
0x00, // FF
0x00, // CR
0x00, // SO
0x00, // SI
0x00, // DEL
0x00, // DC1
0x00, // DC2
0x00, // DC3
0x00, // DC4
0x00, // NAK
0x00, // SYN
0x00, // ETB
0x00, // CAN
0x00, // EM
0x00, // SUB
0x00, // ESC
0x00, // FS
0x00, // GS
0x00, // RS
0x00, // US

0x2c, // ' '
0x1e|SHIFT, // !
0x34|SHIFT, // "
0x20|SHIFT, // #
0x21|SHIFT, // $
0x22|SHIFT, // %
0x24|SHIFT, // &
0x34, // '
0x26|SHIFT, // (
0x27|SHIFT, // )
0x25|SHIFT, // *
0x2e|SHIFT, // +
0x36, // ,
0x2d, // -
0x37, // .
0x38, // /
0x27, // 0
0x1e, // 1
0x1f, // 2
0x20, // 3
0x21, // 4
0x22, // 5
0x23, // 6
0x24, // 7
0x25, // 8
0x26, // 9
0x33|SHIFT, // :
0x33, // ;
0x36|SHIFT, // <
0x2e, // =
0x37|SHIFT, // >
0x38|SHIFT, // ?
0x1f|SHIFT, // @
0x04|SHIFT, // A
0x05|SHIFT, // B
0x06|SHIFT, // C
0x07|SHIFT, // D
0x08|SHIFT, // E
0x09|SHIFT, // F
0x0a|SHIFT, // G
0x0b|SHIFT, // H
0x0c|SHIFT, // I
0x0d|SHIFT, // J
0x0e|SHIFT, // K
0x0f|SHIFT, // L
0x10|SHIFT, // M
0x11|SHIFT, // N
0x12|SHIFT, // O
0x13|SHIFT, // P
0x14|SHIFT, // Q
0x15|SHIFT, // R
0x16|SHIFT, // S
0x17|SHIFT, // T
0x18|SHIFT, // U
0x19|SHIFT, // V
0x1a|SHIFT, // W
0x1b|SHIFT, // X
0x1c|SHIFT, // Y
0x1d|SHIFT, // Z
0x2f, // [
0x31, // bslash
0x30, // ]
0x23|SHIFT, // ^
0x2d|SHIFT, // _
0x35, // `
0x04, // a
0x05, // b
0x06, // c
0x07, // d
0x08, // e
0x09, // f
0x0a, // g
0x0b, // h
0x0c, // i
0x0d, // j
0x0e, // k
0x0f, // l
0x10, // m
0x11, // n
0x12, // o
0x13, // p
0x14, // q
0x15, // r
0x16, // s
0x17, // t
0x18, // u
0x19, // v
0x1a, // w
0x1b, // x
0x1c, // y
0x1d, // z
0x2f|SHIFT, // {
0x31|SHIFT, // |
0x30|SHIFT, // }
0x35|SHIFT, // ~
0 // DEL
}

风物之诗琴自动弹奏

闲言碎语

一觉醒来,发现风物之诗琴居然可以兑换了,自己弹是不可能弹的,交给脚本吧。

键盘就让一块Pro Micro扮演吧,Python 解析midi文件,然后将需要按下的按键发送给Pro Micro。

这里我测试用的midi文件为MidiShow上随便找的一个千本樱。因为原神的风物之诗琴没有长按效果,所以实现起来少了很多工作量。

开干

Pro Micro上运行的代码非常简单,只有20行,需要注意的是并非所有的板子都支持Keyboard.h这个库

genshin.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

#include "Keyboard.h"

void setup() {
// 初始化键盘
Keyboard.begin();
// 初始化串口
Serial.begin(115200);
}

void loop() {
// 当串口可用时候,读取串口输入
while (Serial.available()) {
char key = Serial.read();
// 模拟键盘点击执行串口读取的内容
Keyboard.print(key);
// 延迟
// delay(20);
}
}

python记得安装serial这个库

serial_communication.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

import serial
class SerialCommunication:
# 初始化串口通信类
# @param port_x 端口号 str
# @param bit_rates 波特率 int
# @param timeout 超时时间(s)
def __init__(self, port_x, bit_rates=9600, timeout=1):
self.port_x = port_x
self.bit_rates = bit_rates
self.timeout = timeout
self.serial = serial.Serial(self.port_x, self.bit_rates, timeout=self.timeout)
self.execute_count = 0
print("Serial work on {} at {}bps".format(self.port_x, self.bit_rates))

# 执行一条命令
# @param key_event 待执行的命令
def execute(self, key_event):
self.execute_count += 1
try:
self.serial.write(key_event.encode())
except Exception as execute:
print('---Error on execute---', execute)

# 日志
def log(self):
print("Write %d commands" % self.execute_count)

python需要安装mido这个库才可以解析midi文件

main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109

import time

import mido
from mido import MidiFile

from serial_communication import SerialCommunication

# 由于我不懂音乐,所以下面的表格一定是错误的,如果您知道如何修改出正确的关系,请留言。
# 虽然这个是错的表,但是还是能播放的。

key_dict = {

24: 'Z',
# 25: 'X', #
26: 'X',
# 27: 'C', #
28: 'C',
29: 'V',
# 30: 'B', #
31: 'B',
# 32: 'B',
33: 'M',
# 34: 'N', #
35: 'N',
36: 'Z',
# 37: 'X', #
38: 'X',
# 39: 'C', #
40: 'C',
41: 'V',
# 42: 'B', #
43: 'B',
# 44: 'M', #
45: 'M',
# 46: 'N', #
47: 'N',
48: 'A',
# 49: 'S', #
50: 'S',
# 51: 'D', #
52: 'D',
53: 'F',
# 54: 'G', #
55: 'G',
# 56: 'H', #
57: 'H',
# 58: 'J', #
59: 'J',
60: 'Q',
# 61: 'W', #
62: 'W',
# 63: 'E', #
64: 'E',
65: 'R',
# 66: 'T', #
67: 'T',
# 68: 'Y', #
69: 'Y',
# 70: 'U', #
71: 'U',
72: 'Q',
# 73: 'W', #
74: 'W',
# 75: 'E', #
76: 'E',
77: 'R',
# 78: 'T', #
79: 'T',
# 80: 'Y', #
81: 'Y',
# 82: 'U', #
83: 'U',
84: 'A',
# 85: 'S', #
86: 'S',
88: 'D',
89: 'F',
# 90: 'G', #
91: 'G',
93: 'H',
94: 'J',
# 95: 'J',
}

if __name__ == '__main__':
virtual_key_board = SerialCommunication("COM17", 115200, 1)
mid = MidiFile("input_mid.mid")
tempo = mido.bpm2tempo(131)
time.sleep(3)
unsupport = set()
for i, track in enumerate(mid.tracks):
print('Track {}: {}'.format(i, track.name))
passed_time = 0
for msg in track:
ab_time = mido.tick2second(msg.time, mid.ticks_per_beat, tempo)
real_time = ab_time + passed_time
passed_time += ab_time

if msg.type == "note_on":
if msg.note in key_dict.keys():
virtual_key_board.execute(key_dict[msg.note])
pass
else:
unsupport.add(str(msg.note))
print("skip note:{}".format(msg.note))
time.sleep(msg.time*0.001)
print(unsupport)

其名为Firefly

萤火虫

名字由来

一直觉得TinyMonitor这个名称有点怪怪的,因为它功能已经超出了”小显示器”这个范畴了,所以就开始思考正式发布前换个名字吧。
几日前,翻自己的iCloud的时候,看到了前年在学校门口拍的萤火虫照片,好家伙,小小的会发光,这不就是我想要的名称吗,于是决定起名为Firefly了(好随便)。
随即打开KiCad加上Firefly丝印。

电路渲染图

底视图
顶视图

坐等到货焊接,然后开启下一阶段设计,比如外壳…或者底座什么的,毕竟我留了一个Dout用来外接像素屏幕。

2021-03-19

到货焊接了,目前没有太大问题,这个天线信号强度远超预期啊,tql。
有钱了可得整个紫色沉金啊。
背面,背光那里走了飞线,其实不用走,刮开旁边的阻焊接正极也一样…
有屏幕正面,该画个壳子的设计图

© 2025 Do U Find IT? All Rights Reserved.
Theme by hiero