無線鍵鼠套裝/有線(鍵盤+鼠標(biāo)) 改 無線藍(lán)牙鍵鼠套裝
這里主要講實(shí)現(xiàn)方法以及需要的硬件或代碼,如果有不明白的請(qǐng)先看手把手教學(xué): 鍵鼠 有線/適配器 改無線(藍(lán)牙/WiFi), 普通鍵鼠改鼠標(biāo)宏 - 嗶哩嗶哩 (bilibili.com)
程序示例:【Esp32有線/無線 鍵鼠轉(zhuǎn)藍(lán)牙連接】

一:用到的硬件
????1:帶藍(lán)牙的ESP32模塊,目前價(jià)格在11塊錢左右
????2:CH9350模塊(兩個(gè)usb母頭版本),或者CH9350芯片用來自己設(shè)計(jì),但是建議剛接觸的小伙伴或者不想花太多時(shí)間的小伙伴直接購買成品模塊,雖然價(jià)格會(huì)翻一倍多,但是省事

這是我自己沒事的時(shí)候隨便畫的圖紙
二:程序方面
注意?。。?字有點(diǎn)多,想深入點(diǎn)了解的同學(xué)可以看看
注意1:原庫文件太過臃腫,重新寫了庫文件,用途不同。
注意2:我這里沒單獨(dú)做小鍵盤按鍵,也就是說按小鍵盤的數(shù)字鍵與字母按鍵上的數(shù)字鍵所觸發(fā)的鍵值一樣,不影響實(shí)際使用。
注意3:小鍵盤num切換鍵沒做,我是打算留著做其他功能用,感興趣的可以自己添加功能,比如開個(gè)燈或者繼電器什么的。
注意4:由于我的鍵盤并沒有鍵盤燈,所以鍵盤上的幾個(gè)燈我并沒有寫控制程序,但不影響鍵盤實(shí)際功能使用,有需要的可以自行添加控制語句,雖然沒啥實(shí)際作用。
注意5:有些機(jī)械鍵盤上面有兩個(gè)win鍵(微標(biāo)鍵,窗口鍵,開始鍵),這個(gè)鍵的叫法很多種,就是 “田” 字鍵,我只做了左邊一個(gè),右邊的很少見就沒添加,有興趣的可以自己添加進(jìn)去。
注意6:CH9350模塊用的是模式2,幾個(gè)模式中也就這個(gè)模式省算力,如果只是把ESP32用來模擬鍵鼠用,不用來聯(lián)WiFi或者控制外圍設(shè)備使用的話倒是不用在乎這些問題。
注意7:程序不支持多媒體按鍵,但是有需要完全可以自己添加多媒體功能上去,比如可以改成按下 num+F1 就是聲音控制等等。
幾個(gè)模式的區(qū)別
模式0/1: 會(huì)一直發(fā)送請(qǐng)求,數(shù)據(jù)量巨大,算力一般的單片機(jī)非常容易死機(jī),優(yōu)點(diǎn)是支持多媒體按鍵.? ?
模式 2 3 4 均不支持多媒體按鍵
模式2: 鼠標(biāo)數(shù)據(jù)為相對(duì)位置,長時(shí)間使用不會(huì)產(chǎn)生數(shù)據(jù)堆積
模式4: 鼠標(biāo)發(fā)送絕對(duì)位置,此模式及其不穩(wěn)定(也可能是模塊及程序問題),長時(shí)間使用數(shù)據(jù)會(huì)堆積
模式3: 沒用過,有興趣的自行研究!
程序開始
1:CH9350_USBKEY.h???文件?這里是需要用到的庫文件,請(qǐng)各位同學(xué)查漏補(bǔ)缺

2:CH9350_USBKEY.cpp 文件







char USB9350_KeyMouse::keypress(char scancode) {? ?//這個(gè)程序是判斷鍵盤鍵盤功能鍵的
3.CH9350_USBKEY.ino 控制主程序

這里是鼠標(biāo)移動(dòng)算法

鼠標(biāo)按鍵算法 ,這里添加了兩個(gè)功能程序,
????1)鼠標(biāo)左鍵與鼠標(biāo)前進(jìn)鍵同時(shí)按下,執(zhí)行 原神五郎機(jī)關(guān)槍程序(比較喜歡玩原神)
????2)鼠標(biāo)左鍵與鼠標(biāo)后退鍵同時(shí)按下,執(zhí)行 鼠標(biāo)連點(diǎn)程序,主要用來跳過原神劇情或者需要頻繁使用鼠標(biāo)左鍵的場(chǎng)景,比如升級(jí)原神人物等級(jí)的時(shí)候需要按幾百下鼠標(biāo)


鍵盤按鍵算法
老規(guī)矩,下面是代碼程序
附帶全功能代碼,如果只需要鍵鼠程序,請(qǐng)自行修改!
/* 模式0/1: 會(huì)一直發(fā)送請(qǐng)求,數(shù)據(jù)量巨大,算力一般的單片機(jī)非常容易死機(jī),優(yōu)點(diǎn)是支持多媒體按鍵.
? ?模式 2 3 4 均不支持多媒體按鍵
? ?模式2: 鼠標(biāo)數(shù)據(jù)為相對(duì)位置,長時(shí)間使用不會(huì)產(chǎn)生數(shù)據(jù)堆積
? ?模式4: 鼠標(biāo)發(fā)送絕對(duì)位置,此模式及其不穩(wěn)定(也可能是模塊及程序問題),長時(shí)間使用數(shù)據(jù)會(huì)堆積
? ?模式3: 沒用過,有興趣的自行研究!
*/
#include "CH9350_USBKEY.h"
//IOs for CH9350 used on ESP32
#define RXKEY 16
#define TXKEY 17
HardwareSerial SerialKey(2);? ?//使用串口2
USB9350_KeyMouse USB9350;? //Create an Instance for Keyboard/ Mouse Decoder
char anxarr[4], qarr[4];
unsigned long StartTime, StartTime1;
unsigned long rrldely; //按鍵延時(shí)
//Globals
int Key_gn;
int ch9350_5V = 255;
//<<< <<< <<< <<< <<< <<< == == == == == 各函數(shù) == == == == == >>> >>> >>> >>> >>> >>> >>
int aak = 0;
void rrl() { //原神五郎機(jī)關(guān)槍程序
? unsigned long sj = millis() - rrldely;
? if ( sj < 60) {
? ? if (aak == 0) {
? ? ? aak++;
? ? ? Mouse.click(MOUSE_LEFT);
? ? ? Keyboard.write('r');
? ? }
? ? else if ((aak == 1) && (sj > 40)) {
? ? ? aak++;
? ? ? Keyboard.write('r');
? ? ? Mouse.move(25, 0);
? ? }
? } else {
? ? aak = 0;
? }
}
void zuoyou() {//射擊游戲?qū)S?,僅做了初級(jí)防檢測(cè)
? Mouse.press(MOUSE_RIGHT);
? if (millis() > StartTime1) {? ? ? ? ? ? ? ? //同時(shí)按下執(zhí)行鼠標(biāo)連點(diǎn)
? ? Mouse.release(MOUSE_LEFT);
? ? StartTime1 = millis() + random(135, 145);//鼠標(biāo)按下時(shí)間
? ? Mouse.press(MOUSE_LEFT);
? ? Mouse.move(0, 5);? ?//鼠標(biāo)下移 壓槍
? }
? //? else if (millis() > ( StartTime1 - 2))? {
? //? ? ? Mouse.release(MOUSE_LEFT);
? //? }
}
//鼠標(biāo)按鈕及移動(dòng)
void sbmov_go() { //優(yōu)先級(jí): 鼠標(biāo)移動(dòng) > 鼠標(biāo)按鍵 > 鍵盤功能鍵 > 鍵盤按鍵
? int x, y, z, Button;
? int Change = USB9350.GetMouseXY(&x, &y, &z, &Button);
? //鼠標(biāo)篇
? {
? ? //鼠標(biāo)移動(dòng)
? ? if (Change) {// CH9350 設(shè)置為模式2
? ? ? int xy = 128;
? ? ? if (x > xy) x -= 2 * xy;
? ? ? if (y > xy) y -= 2 * xy;? ? ??
? ? ? if (z != 0) z = z > 1 ? -1 : 1;
? ? ??
? ? ? Mouse.move(x, y, z);
? ? ? Serial.println(" 鼠標(biāo)按下: x: " + String(x) + ", y: " + String(y) + ", z: " + String(z) + " Button: " + String(Button));
? ? }
? ? //鼠標(biāo)功能鍵
? ? {
? ? ? if (Button == 17) {? ? ? ? ? ? ? ? ? ?//左鍵 前進(jìn)鍵
? ? ? ? if (millis() > StartTime1) {
? ? ? ? ? rrldely = millis();
? ? ? ? ? StartTime1 = millis() + 120; //延時(shí)
? ? ? ? }
? ? ? } else if (Button == 3) {
? ? ? ? zuoyou();
? ? ? } else if (Button == 9) {? ? ? ? ? ? ? ? ? ? ?//左鍵 后退鍵 同時(shí)按下執(zhí)行鼠標(biāo)連點(diǎn)
? ? ? ? if (millis() > StartTime1) {
? ? ? ? ? Mouse.click(MOUSE_LEFT);
? ? ? ? ? StartTime1 = millis() + random(66, 88);//延時(shí)
? ? ? ? }
? ? ? } else {
? ? ? ? for (int i = 16; i > 0; i /= 2)//? i本地鍵值
? ? ? ? {
? ? ? ? ? if (Button >= i ) {
? ? ? ? ? ? if (!Mouse.isPressed(i)) {
? ? ? ? ? ? ? Mouse.press(i);
? ? ? ? ? ? }
? ? ? ? ? ? Button -= i;
? ? ? ? ? } else if (Mouse.isPressed(i)) {
? ? ? ? ? ? Mouse.release(i);
? ? ? ? ? }
? ? ? ? }
? ? ? }
? ? }
? }
? //鍵盤篇
? {
? ? if ((USB9350.anarr[0] + USB9350.anarr[4]) > 0) {
? ? ? //功能鍵 按下 與 釋放 判斷 ,支持功能鍵全鍵無沖
? ? ? int gnj = USB9350.anarr[4]; //Lshift Lctrl Lwin Lalt Ralt Rctrl Rshift
? ? ? Serial.println();
? ? ? Serial.print("鍵盤按下: ");
? ? ? if (USB9350.anarr[4] > 0) {
? ? ? ? Serial.print("功能鍵 " + String((int)USB9350.anarr[4]) + " ");
? ? ? }
? ? ? for (int i = 128, il = 135; i > 0; i /= 2, il--)//? i本地鍵值 il藍(lán)牙發(fā)送值
? ? ? {
? ? ? ? if (gnj >= i ) {
? ? ? ? ? Keyboard.press(il);
? ? ? ? ? gnj -= i;
? ? ? ? } else {
? ? ? ? ? Keyboard.release(il);
? ? ? ? }
? ? ? }
? ? ? USB9350.anarr[4] = 0x00;
? ? ? //普通鍵盤 按下 判斷,支持4鍵無沖
? ? ? {
? ? ? ? for (int i = 0; i < 4; i++) {
? ? ? ? ? int keyc;
? ? ? ? ? keyc = USB9350.ScanToASCII(USB9350.anarr[i]);
? ? ? ? ? if (USB9350.anarr[i] != 0x00) { //anxia
? ? ? ? ? ? Serial.printf(" 0x%02X '%c' ", USB9350.anarr[i], keyc);
? ? ? ? ? ? Keyboard.press(keyc);
? ? ? ? ? ? anxarr[i] = keyc;
? ? ? ? ? ? USB9350.anarr[i] = 0x00;
? ? ? ? ? } else {
? ? ? ? ? ? anxarr[i] = 0x00;
? ? ? ? ? }
? ? ? ? }
? ? ? ? for (int i = 0; i < 4; i++) {? //釋放未按下的鍵
? ? ? ? ? if (qarr[i] != 0x00) {
? ? ? ? ? ? int a = 0;
? ? ? ? ? ? for (int ii = 0; ii < 4; ii++) {
? ? ? ? ? ? ? if (anxarr[ii] != 0x00) {
? ? ? ? ? ? ? ? if (anxarr[ii] == qarr[i]) {
? ? ? ? ? ? ? ? ? a = 1;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? if (a == 0) {
? ? ? ? ? ? ? Keyboard.release(qarr[i]);
? ? ? ? ? ? ? Serial.println();
? ? ? ? ? ? ? Serial.printf("松開 '%c' \n", qarr[i] );
? ? ? ? ? ? }
? ? ? ? ? }
? ? ? ? ? qarr[i] = anxarr[i];
? ? ? ? }
? ? ? }
? ? ? StartTime = millis();
? ? }
? ? else if ((StartTime > 0) && (USB9350._SerArr[1] == 0x01))
? ? {
? ? ? if ((USB9350._SerArr[4] == 0x00) && (USB9350._SerArr[2] == 0x00))
? ? ? {
? ? ? ? Serial.println();
? ? ? ? Serial.printf("釋放鍵盤 %ldms\n", millis() - StartTime);
? ? ? ? StartTime = 0;
? ? ? ? Keyboard.releaseAll();
? ? ? ? qarr[0] = 0x00;
? ? ? }
? ? }
? }
? rrl(); //檢查RRL鍵 原神五郎專用
}
//<<< <<< <<< <<< <<< <<< == == == == == 設(shè)置ESP-NOW數(shù)據(jù)結(jié)構(gòu)體 == == == == == >>> >>> >>> >>> >>> >>> >>
// REPLACE WITH THE MAC Address of your receiver
uint8_t mac_to[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
String jso[3] = { "name", "type", "val" };? ? ? ?//jso應(yīng)當(dāng)包含的值
String my_name = "Esp32_Mouse";
String to_name = "value";
typedef struct test_struct? {? ? ? ? ? ? ? ? ? ? ? ? ?// 設(shè)置ESP-NOW數(shù)據(jù)結(jié)構(gòu)體
? char a[250];
} test_struct ;
test_struct? to_str;
test_struct? myData;
String mydata;
String esp_now_to_ls;
//創(chuàng)建類型變量esp_now_peer_info_t存儲(chǔ)有關(guān)對(duì)等點(diǎn)的信息。
esp_now_peer_info_t peerInfo;
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
? String st = status ? "No!" : "Ok!";
? Serial.println("`發(fā)送回調(diào) :" + st + " `");
}
// 數(shù)據(jù)接收回調(diào)函數(shù)
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
? memcpy(&myData, incomingData, sizeof(myData));
? //打印數(shù)據(jù)發(fā)送方的MAC地址
? char macStr[18];
? Serial.print("`");
? Serial.print("from:");
? snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
? ? ? ? ? ?mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
? Serial.print(macStr);
? String my_data = jm_arr(String(myData.a)); //解密
? if (my_data.endsWith("}") && my_data.startsWith("{")) {
? ? Serial.print("-> " + String(my_data.length()));
? ? Serial.println(" :" + my_data + "`");
? ? mydata = my_data;
? } else {
? ? Serial.println("NO_ESP :" + my_data + "`");
? }
}
void json_to(String a) { //打包數(shù)據(jù)
? String txt = "";
? if (a != "") {
? ? JSONVar jsonRead;
? ? jsonRead[jso[0]] = my_name;
? ? jsonRead[jso[1]] = to_name;
? ? jsonRead[jso[2]] = a;
? ? to_name = "value"; //返回狀
? ? //序列化
? ? txt = JSON.stringify(jsonRead);
? ? txt = jm_arr(txt); //加密
? ? esp_now_to_ls = txt;
? }
}
String jm_arr(String a) {
? char jm = 'z';? //加密
? char lls[245];
? a.toCharArray(lls, sizeof(lls));
? for (int i = 0; i < a.length(); i++) {
? ? if (lls[i] != jm) {
? ? ? lls[i] = (char)(lls[i] ^ jm);
? ? }
? }
? return String(lls);
? //? String(lss).toCharArray(to_str.a, sizeof(to_str.a));
? //? //? String(a).toCharArray(to_str.a, sizeof(to_str.a));
? //? esp_now_send(mac_to, (uint8_t *)&to_str, sizeof(to_str));
}
void mydata_go() {
? mydata.trim();
? int jso_num = sizeof(jso) / sizeof(jso[0]);
? int hefa = jso_num;
? String cw = "未找到: ";
? for (int i = 0; i < jso_num; i++) {
? ? int n_h = mydata.indexOf("\"" + jso[i] + "\":");
? ? if (n_h <= 0) {? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//合法性
? ? ? cw += (jso[i] + " ");
? ? ? hefa--;
? ? }
? }
? if (hefa == jso_num) {
? ? JSONVar myObject = JSON.parse(mydata);? //數(shù)據(jù)為const char* 格式
? ? const char* name = myObject[jso[0]];
? ? const char* type = myObject[jso[1]];
? ? const char* val = myObject[jso[2]];
? ? String my_na = String(name);
? ? String type_ls = String(type);
? ? String msg = String(val);
? ? //? ? if (my_na == "AMD銳龍ITX") {
? ? //? ? ? ESP.restart();
? ? //
? ? //? ? ? //? ? ? if (msg == "255" || msg == "on") {
? ? //? ? ? //? ? ? ? Serial.println("電腦開機(jī),重啟系統(tǒng)");
? ? //? ? ? //? ? ? ? ESP.restart();
? ? //? ? ? //? ? ? }
? ? //? ? }7
? } else {
? ? Serial.println("NO ESP: " + mydata);
? }
? mydata = "";
}
//<<< <<< <<< <<< <<< <<< == == == == == SETUP == == == == == >>> >>> >>> >>> >>> >>> >>
void setup()
{
? WiFi.mode(WIFI_STA);
? Serial.begin(115200);
? Serial.println("Start");
? Serial.println("[OLD] MAC: " + WiFi.macAddress());
? Serial.println("NEW Wi-Fi 通道: " + String(WiFi.channel()));
? analogWrite(13, ch9350_5V); //啟動(dòng)CH
? Keyboard.begin();
? Mouse.begin();
? SerialKey.begin(115200, SERIAL_8N1, RXKEY, TXKEY);
? USB9350.begin(SerialKey);
? Serial1.begin(115200, SERIAL_8N1, 25, 33);//8266模塊
? Serial.printf("CH9350_USBKEY? ? RX:16? TX:17 \n");
? Serial.printf("ESP8266? ? RX:25? TX:33 \n");
? if (esp_now_init() != ESP_OK) return;? //ESP-NOW初始化
? esp_now_register_send_cb(OnDataSent);? //發(fā)送回調(diào)
? esp_now_register_recv_cb(OnDataRecv);? //接收回調(diào)
? memcpy(peerInfo.peer_addr, mac_to, 6); //配對(duì)點(diǎn)
? peerInfo.channel = 0;
? peerInfo.encrypt = false;
? if (esp_now_add_peer(&peerInfo) != ESP_OK) {
? ? Serial.println("Failed to add peer mac1");
? ? return;
? }
}
void loop() {
? if (Keyboard.isConnected()) { //如果藍(lán)牙連接成功
? ? sbmov_go();? //鍵盤鼠標(biāo)篇
? }
? if (esp_now_to_ls != "") { //有待發(fā)送數(shù)據(jù)就發(fā)送
? ? esp_now_to_ls.toCharArray(to_str.a, sizeof(to_str.a));
? ? esp_now_send(mac_to, (uint8_t *)&to_str, sizeof(to_str));
? ? esp_now_to_ls = "";
? } else if (mydata != "") { //有接收數(shù)據(jù)就執(zhí)行接收
? ? mydata_go();
? }
? String va = "";
? if (Serial1.available() > 0) {
? ? while (Serial1.available() > 0) {
? ? ? va += char(Serial1.read());
? ? }
? ? Serial.println(va);
? } else if (Serial.available() > 0) {
? ? while (Serial.available() > 0) {
? ? ? va += char(Serial.read());
? ? }
? ? Serial1.println(va);
? }
}