想不想自己做一个无线键盘接收器?市面上那些几十块的无线键盘,其实核心就是 NRF24L01+ 模块。今天我们来拆解这个黑盒子,用不到 50 元的成本,做一个支持双模切换的无线键盘接收器——既能当普通键盘用,也能当宏键盘使。
这个项目的灵感来自于我工作室里那堆乱七八糟的线缆。每次切换设备都要拔插 USB,烦得要死。索性自己做一个,还能自定义按键映射,简直不要太爽。
需要准备什么?
| 物品 | 型号/规格 | 价格 |
|---|---|---|
| 主控板 | Arduino Pro Micro (ATmega32U4) | ¥25 |
| 无线模块 | NRF24L01+ 2.4GHz | ¥8 |
| 接收端模块 | NRF24L01+ (带 PCB 天线) | ¥10 |
| 按键开关 | 机械键盘轴体×6 | ¥15 |
| PCB 洞洞板 | 5×7cm | ¥5 |
| USB 数据线 | Micro USB | ¥5 |
| 杜邦线 | 公对公/母对母 | ¥5 |
| *总计*\ | ¥73 |
注意: 接收端建议买带 PCB 天线的版本,信号更稳定。发射端用普通天线就行,反正离得近。
步骤 1:硬件连接
先来看发射端(键盘侧)的接线。ATmega32U4 的 SPI 引脚是固定的,别接错了:
NRF24L01+ → Arduino Pro Micro
---------------------------------------
VCC → 3.3V (千万别接 5V,会烧!)
GND → GND
CE → D9
CSN → D10
SCK → D15
MOSI → D16
MISO → D14
IRQ → 不接(可选)
⚠️ 踩坑提醒: NRF24L01+ 是 3.3V 器件,Pro Micro 虽然有 3.3V 输出,但电流有限。如果供电不稳,模块会反复重启。建议加个 10μF 电容在 VCC 和 GND 之间。
接收端接线类似,只是 CE 和 CSN 引脚可以换:
NRF24L01+ → Arduino Pro Micro (接收端)
--------------------------------------------
VCC → 3.3V
GND → GND
CE → D8
CSN → D9
SCK → D15
MOSI → D16
MISO → D14
步骤 2:安装依赖库
打开 Arduino IDE,安装以下库:
# 通过库管理器搜索安装,或用 CLI
arduino-cli lib install "RF24"
arduino-cli lib install "HID-Project"
或者在 IDE 里:
- 工具 → 管理库
- 搜索 "RF24",安装 by TMRh20
- 搜索 "HID-Project",安装 by NicoHood
原理解析: RF24 库负责 NRF24L01+ 的无线通信,HID-Project 让 Arduino 模拟成 USB 键盘。ATmega32U4 原生支持 USB HID,这是它比 Uno/Nano 更适合这个项目的原因。
步骤 3:发射端代码(键盘侧)
#include
#include
#include
#include
// 引脚定义
#define CE_PIN 9
#define CSN_PIN 10
RF24 radio(CE_PIN, CSN_PIN);
// 通信管道地址
const uint64_t pipes[2] = {0xF0F0F0F0E7LL, 0xF0F0F0F0D2LL};
// 按键映射(自定义)
struct KeyMapping {
uint8_t pin;
HIDKeys key;
bool lastState;
};
KeyMapping keys[] = {
{2, KEY_F1, HIGH}, // F1 宏键
{3, KEY_F2, HIGH}, // F2 宏键
{4, KEY_F3, HIGH}, // F3 宏键
{5, KEY_F4, HIGH}, // F4 宏键
{6, KEY_F5, HIGH}, // F5 宏键
{7, KEY_F6, HIGH}, // F6 宏键
};
void setup() {
Serial.begin(115200);
// 初始化按键引脚
for (int i = 0; i < 6; i++) {
pinMode(keys[i].pin, INPUT_PULLUP);
}
// 初始化 NRF24L01+
radio.begin();
radio.setPALevel(RF24_PA_HIGH);
radio.setDataRate(RF24_2MBPS);
radio.openWritingPipe(pipes[0]);
radio.openReadingPipe(1, pipes[1]);
radio.startListening();
// 发送握手信号
delay(100);
radio.stopListening();
uint8_t handshake = 0xAA;
radio.write(&handshake, sizeof(handshake));
radio.startListening();
Serial.println("发射端就绪");
}
void loop() {
// 扫描按键
for (int i = 0; i < 6; i++) {
bool currentState = digitalRead(keys[i].pin);
if (currentState != keys[i].lastState) {
delay(20); // 消抖
currentState = digitalRead(keys[i].pin);
if (currentState == LOW) {
// 按键按下,发送键值
radio.stopListening();
uint8_t packet[2] = {0x01, (uint8_t)keys[i].key};
radio.write(&packet, sizeof(packet));
radio.startListening();
Serial.print("发送按键:");
Serial.println(keys[i].key);
}
keys[i].lastState = currentState;
}
}
delay(10);
}
步骤 4:接收端代码(USB 侧)
#include
#include
#include
#include
// 引脚定义
#define CE_PIN 8
#define CSN_PIN 9
RF24 radio(CE_PIN, CSN_PIN);
const uint64_t pipes[2] = {0xF0F0F0F0E7LL, 0xF0F0F0F0D2LL};
// 模式切换:0=普通键盘,1=宏键盘
uint8_t mode = 0;
#define MODE_SWITCH_PIN A0
void setup() {
Serial.begin(115200);
pinMode(MODE_SWITCH_PIN, INPUT_PULLUP);
// 初始化 NRF24L01+
radio.begin();
radio.setPALevel(RF24_PA_HIGH);
radio.setDataRate(RF24_2MBPS);
radio.openWritingPipe(pipes[1]);
radio.openReadingPipe(1, pipes[0]);
radio.startListening();
// 初始化 USB HID
BootKeyboard.begin();
Serial.println("接收端就绪,等待连接...");
}
void loop() {
// 检查模式切换
if (digitalRead(MODE_SWITCH_PIN) == LOW) {
mode = 1 - mode;
delay(300); // 防抖
Serial.print("切换到模式:");
Serial.println(mode ? "宏键盘" : "普通键盘");
}
// 接收无线数据
if (radio.available()) {
uint8_t packet[2];
radio.read(&packet, sizeof(packet));
if (packet[0] == 0x01) {
// 按键事件
HIDKeys key = (HIDKeys)packet[1];
if (mode == 0) {
// 普通模式:直接转发
BootKeyboard.press(key);
delay(50);
BootKeyboard.release(key);
} else {
// 宏模式:执行预设宏
executeMacro(key);
}
Serial.print("收到按键:");
Serial.println(key);
}
}
}
// 宏定义示例
void executeMacro(HIDKeys key) {
switch (key) {
case KEY_F1:
// Ctrl+C 复制
BootKeyboard.press(KEY_LEFT_CTRL);
BootKeyboard.press('c');
delay(50);
BootKeyboard.releaseAll();
break;
case KEY_F2:
// Ctrl+V 粘贴
BootKeyboard.press(KEY_LEFT_CTRL);
BootKeyboard.press('v');
delay(50);
BootKeyboard.releaseAll();
break;
case KEY_F3:
// Alt+Tab 切换窗口
BootKeyboard.press(KEY_LEFT_ALT);
BootKeyboard.press(KEY_TAB);
delay(50);
BootKeyboard.releaseAll();
break;
case KEY_F4:
// Win+D 显示桌面
BootKeyboard.press(KEY_LEFT_GUI);
BootKeyboard.press('d');
delay(50);
BootKeyboard.releaseAll();
break;
default:
BootKeyboard.press(key);
delay(50);
BootKeyboard.release(key);
}
}
步骤 5:测试验证
烧录代码后,按以下步骤测试:
-
配对测试: 同时给发射端和接收端上电,观察串口输出
发射端:发射端就绪 接收端:接收端就绪,等待连接... 接收端:收到按键:KEY_F1 -
距离测试: 在空旷环境下,NRF24L01+ 的有效距离约 50-100 米(带 PCB 天线)。室内隔墙约 10-20 米。
-
延迟测试: 用示波器或逻辑分析仪测量按键到 USB 输出的延迟,通常在 5-10ms,完全满足打字需求。
效果展示: 我在工作室测试,发射端放在工作台上,接收端插在客厅的 HTPC 上,隔了两堵墙还能稳定使用。
常见问题排查
问题 1: 接收端收不到数据
- 原因: 管道地址不匹配或供电不足
- 解决: 检查
pipes[]数组在两端是否一致;用万用表测 VCC 是否稳定在 3.3V;加 10μF 电容
问题 2: 电脑识别不到 USB 键盘
- 原因: ATmega32U4 固件问题或 USB 线质量问题
- 解决: 换一根能传数据的 USB 线(有些只能充电);重新烧录 Bootloader
问题 3: 按键延迟明显
- 原因: 无线速率设置过低或消抖时间太长
- 解决: 将
setDataRate改为RF24_2MBPS;消抖时间从 50ms 降到 20ms
问题 4: 宏按键执行多次
- 原因: 按键消抖逻辑有问题
- 解决: 确保
lastState状态机正确更新;在宏执行后加 100ms 延时
进阶玩法
这个项目只是个开始,你可以:
- 加 OLED 屏幕: 显示当前模式、电量、信号强度
- 加锂电池: 做成真正无线的,加 TP4056 充电模块
- 多设备切换: 用拨码开关切换不同接收端,一套键盘控制多台电脑
- RGB 灯效: 每个按键加 WS2812B,自定义灯效
- 摇杆支持: 加 analog stick,模拟游戏手柄
总结
用 70 块钱的成本,我们做了一个支持双模切换的无线键盘接收器。核心就是 NRF24L01+ 模块 + ATmega32U4 的组合。这个项目最大的价值不在于省钱,而在于完全可控——你想加什么功能就加什么功能,不用看厂商脸色。
我已经在考虑下一代设计了:用 ESP32-C3 做主控,支持 WiFi 和蓝牙双模,还能通过 OTA 更新固件。到时候再和大家分享。
希望这篇博客文章对您有所帮助!
相关资源:



