Membangun Bluetooth Macro Keyboard Programmable ESP32 Klasik Berdasarkan Permasalahan
Pendahuluan
Macro keyboard telah menjadi alat yang sangat populer di kalangan content creator, programmer, dan gamer. Dengan menggunakan ESP32, kita dapat membuat macro keyboard Bluetooth yang dapat diprogram sesuai kebutuhan. Artikel ini akan membahas secara lengkap proses pembuatan dari nol hingga troubleshooting masalah yang mungkin muncul.
Apa itu Macro Keyboard?
Macro keyboard adalah perangkat input yang memungkinkan pengguna untuk menjalankan serangkaian perintah kompleks hanya dengan menekan satu tombol. Misalnya, dengan satu tombol, Anda dapat membuka aplikasi, mengetik teks panjang, atau menjalankan shortcut keyboard yang rumit.
Komponen yang Diperlukan
Hardware
- ESP32 Development Board (NodeMCU ESP32 atau ESP32 DevKit).
- Push button switches (1 buah, sesuai kebutuhan).
- Resistor 1kΩ (untuk pull-up).
- Breadboard.
- Kabel jumper.
Software
- VSCode + PlatformIO Atau Arduino IDE.
- Liblary :
Pemasalah Pertama Bagaimana Membuat Bluetooth Keyboard dengan ESP32 ?
- Patikan kalian sudah menginstal Liblary : BLE Keyboard.
- Lalu kalian buka example dari liblary esp32 keyboard bluetooth.
- Disana kita di beri 1 contoh untuk esp32 mengirim data ke bluetooth secara otomatis tidak kita menggunakan button di klik baru program baru jalan.
- Berikut programnya dengan penjelasan fungsional program.C++
/** * This example turns the ESP32 into a Bluetooth LE keyboard that writes the words, presses Enter, presses a media key and then Ctrl+Alt+Delete */ #include <BleKeyboard.h> // Include the library for Bluetooth Low Energy (BLE) keyboard functionality BleKeyboard bleKeyboard; // Create an instance of the BleKeyboard object void setup() { Serial.begin(115200); // Initialize serial communication at a baud rate of 115200 Serial.println("Starting BLE work!"); // Print a message to the serial monitor bleKeyboard.begin(); // Initialize the BLE keyboard. This makes the ESP32 discoverable as a BLE device. } void loop() { if(bleKeyboard.isConnected()) { // Check if the ESP32 is connected to a BLE host (e.g., a computer or phone) Serial.println("Sending 'Hello world'..."); // Print a message to the serial monitor bleKeyboard.print("Hello world"); // Type and send the string "Hello world" to the connected device delay(1000); // Wait for 1 second Serial.println("Sending Enter key..."); // Print a message to the serial monitor bleKeyboard.write(KEY_RETURN); // Send the Enter key press delay(1000); // Wait for 1 second Serial.println("Sending Play/Pause media key..."); // Print a message to the serial monitor bleKeyboard.write(KEY_MEDIA_PLAY_PAUSE); // Send the Play/Pause media key press delay(1000); // Wait for 1 second // // Below is an example of pressing multiple keyboard modifiers // which by default is commented out. /* Serial.println("Sending Ctrl+Alt+Delete..."); // Print a message to the serial monitor bleKeyboard.press(KEY_LEFT_CTRL); // Press and hold the Left Control key bleKeyboard.press(KEY_LEFT_ALT); // Press and hold the Left Alt key bleKeyboard.press(KEY_DELETE); // Press and hold the Delete key delay(100); // Wait for 100 milliseconds bleKeyboard.releaseAll(); // Release all currently pressed keys */ } Serial.println("Waiting 5 seconds..."); // Print a message to the serial monitor delay(5000); // Wait for 5 seconds before the loop repeats }
Permasalahan selanjutnya misalkan saya ingin membuat fungsi tombol di luar contoh diatas ?
Kalian bisa buka file liblary dari BLE Keyboard disana kalian bisa mendapatkan database dari keyboard atau kalian bisa menggunakan database yang saya miliki di bawah ini.
ASCII Characters (32-127)
| Decimal | Hex | Character |
|---|---|---|
| 32 | 0x20 | (Space) |
| 33 | 0x21 | ! |
| 34 | 0x22 | " |
| 35 | 0x23 | # |
| 36 | 0x24 | $ |
| 37 | 0x25 | % |
| 38 | 0x26 | & |
| 39 | 0x27 | ' |
| 40 | 0x28 | ( |
| 41 | 0x29 | ) |
| 42 | 0x2A | * |
| 43 | 0x2B | + |
| 44 | 0x2C | , |
| 45 | 0x2D | - |
| 46 | 0x2E | . |
| 47 | 0x2F | / |
| 48 | 0x30 | 0 |
| 49 | 0x31 | 1 |
| 50 | 0x32 | 2 |
| 51 | 0x33 | 3 |
| 52 | 0x34 | 4 |
| 53 | 0x35 | 5 |
| 54 | 0x36 | 6 |
| 55 | 0x37 | 7 |
| 56 | 0x38 | 8 |
| 57 | 0x39 | 9 |
| 58 | 0x3A | : |
| 59 | 0x3B | ; |
| 60 | 0x3C | < |
| 61 | 0x3D | = |
| 62 | 0x3E | > |
| 63 | 0x3F | ? |
| 64 | 0x40 | @ |
| 65 | 0x41 | A |
| 66 | 0x42 | B |
| 67 | 0x43 | C |
| 68 | 0x44 | D |
| 69 | 0x45 | E |
| 70 | 0x46 | F |
| 71 | 0x47 | G |
| 72 | 0x48 | H |
| 73 | 0x49 | I |
| 74 | 0x4A | J |
| 75 | 0x4B | K |
| 76 | 0x4C | L |
| 77 | 0x4D | M |
| 78 | 0x4E | N |
| 79 | 0x4F | O |
| 80 | 0x50 | P |
| 81 | 0x51 | Q |
| 82 | 0x52 | R |
| 83 | 0x53 | S |
| 84 | 0x54 | T |
| 85 | 0x55 | U |
| 86 | 0x56 | V |
| 87 | 0x57 | W |
| 88 | 0x58 | X |
| 89 | 0x59 | Y |
| 90 | 0x5A | Z |
| 91 | 0x5B | [ |
| 92 | 0x5C | \ |
| 93 | 0x5D | ] |
| 94 | 0x5E | ^ |
| 95 | 0x5F | _ |
| 96 | 0x60 | ` |
| 97 | 0x61 | a |
| 98 | 0x62 | b |
| 99 | 0x63 | c |
| 100 | 0x64 | d |
| 101 | 0x65 | e |
| 102 | 0x66 | f |
| 103 | 0x67 | g |
| 104 | 0x68 | h |
| 105 | 0x69 | i |
| 106 | 0x6A | j |
| 107 | 0x6B | k |
| 108 | 0x6C | l |
| 109 | 0x6D | m |
| 110 | 0x6E | n |
| 111 | 0x6F | o |
| 112 | 0x70 | p |
| 113 | 0x71 | q |
| 114 | 0x72 | r |
| 115 | 0x73 | s |
| 116 | 0x74 | t |
| 117 | 0x75 | u |
| 118 | 0x76 | v |
| 119 | 0x77 | w |
| 120 | 0x78 | x |
| 121 | 0x79 | y |
| 122 | 0x7A | z |
| 123 | 0x7B | { |
| 124 | 0x7C | | |
| 125 | 0x7D | } |
| 126 | 0x7E | ~ |
| 127 | 0x7F | DEL |
Special Key Codes
| Constant Name | Hex Value | Decimal Value |
|---|---|---|
| KEY_LEFT_CTRL | 0x80 | 128 |
| KEY_LEFT_SHIFT | 0x81 | 129 |
| KEY_LEFT_ALT | 0x82 | 130 |
| KEY_LEFT_GUI | 0x83 | 131 |
| KEY_RIGHT_CTRL | 0x84 | 132 |
| KEY_RIGHT_SHIFT | 0x85 | 133 |
| KEY_RIGHT_ALT | 0x86 | 134 |
| KEY_RIGHT_GUI | 0x87 | 135 |
| KEY_UP_ARROW | 0xDA | 218 |
| KEY_DOWN_ARROW | 0xD9 | 217 |
| KEY_LEFT_ARROW | 0xD8 | 216 |
| KEY_RIGHT_ARROW | 0xD7 | 215 |
| KEY_BACKSPACE | 0xB2 | 178 |
| KEY_TAB | 0xB3 | 179 |
| KEY_RETURN | 0xB0 | 176 |
| KEY_ESC | 0xB1 | 177 |
| KEY_INSERT | 0xD1 | 209 |
| KEY_PRTSC | 0xCE | 206 |
| KEY_DELETE | 0xD4 | 212 |
| KEY_PAGE_UP | 0xD3 | 211 |
| KEY_PAGE_DOWN | 0xD6 | 214 |
| KEY_HOME | 0xD2 | 210 |
| KEY_END | 0xD5 | 213 |
| KEY_CAPS_LOCK | 0xC1 | 193 |
| KEY_F1 | 0xC2 | 194 |
| KEY_F2 | 0xC3 | 195 |
| KEY_F3 | 0xC4 | 196 |
| KEY_F4 | 0xC5 | 197 |
| KEY_F5 | 0xC6 | 198 |
| KEY_F6 | 0xC7 | 199 |
| KEY_F7 | 0xC8 | 200 |
| KEY_F8 | 0xC9 | 201 |
| KEY_F9 | 0xCA | 202 |
| KEY_F10 | 0xCB | 203 |
| KEY_F11 | 0xCC | 204 |
| KEY_F12 | 0xCD | 205 |
| KEY_F13 | 0xF0 | 240 |
| KEY_F14 | 0xF1 | 241 |
| KEY_F15 | 0xF2 | 242 |
| KEY_F16 | 0xF3 | 243 |
| KEY_F17 | 0xF4 | 244 |
| KEY_F18 | 0xF5 | 245 |
| KEY_F19 | 0xF6 | 246 |
| KEY_F20 | 0xF7 | 247 |
| KEY_F21 | 0xF8 | 248 |
| KEY_F22 | 0xF9 | 249 |
| KEY_F23 | 0xFA | 250 |
| KEY_F24 | 0xFB | 251 |
| KEY_NUM_0 | 0xEA | 234 |
| KEY_NUM_1 | 0xE1 | 225 |
| KEY_NUM_2 | 0xE2 | 226 |
| KEY_NUM_3 | 0xE3 | 227 |
| KEY_NUM_4 | 0xE4 | 228 |
| KEY_NUM_5 | 0xE5 | 229 |
| KEY_NUM_6 | 0xE6 | 230 |
| KEY_NUM_7 | 0xE7 | 231 |
| KEY_NUM_8 | 0xE8 | 232 |
| KEY_NUM_9 | 0xE9 | 233 |
| KEY_NUM_SLASH | 0xDC | 220 |
| KEY_NUM_ASTERISK | 0xDD | 221 |
| KEY_NUM_MINUS | 0xDE | 222 |
| KEY_NUM_PLUS | 0xDF | 223 |
| KEY_NUM_ENTER | 0xE0 | 224 |
| KEY_NUM_PERIOD | 0xEB | 235 |
Media Key Codes
| Constant Name | Hex Value | Decimal Value |
|---|---|---|
| KEY_MEDIA_NEXT_TRACK | {1, 0} | 1, 0 |
| KEY_MEDIA_PREVIOUS_TRACK | {2, 0} | 2, 0 |
| KEY_MEDIA_STOP | {4, 0} | 4, 0 |
| KEY_MEDIA_PLAY_PAUSE | {8, 0} | 8, 0 |
| KEY_MEDIA_MUTE | {16, 0} | 16, 0 |
| KEY_MEDIA_VOLUME_UP | {32, 0} | 32, 0 |
| KEY_MEDIA_VOLUME_DOWN | {64, 0} | 64, 0 |
| KEY_MEDIA_WWW_HOME | {128, 0} | 128, 0 |
| KEY_MEDIA_LOCAL_MACHINE_BROWSER | {0, 1} | 0, 1 |
| KEY_MEDIA_CALCULATOR | {0, 2} | 0, 2 |
| KEY_MEDIA_WWW_BOOKMARKS | {0, 4} | 0, 4 |
| KEY_MEDIA_WWW_SEARCH | {0, 8} | 0, 8 |
| KEY_MEDIA_WWW_STOP | {0, 16} | 0, 16 |
| KEY_MEDIA_WWW_BACK | {0, 32} | 0, 32 |
| KEY_MEDIA_CONSUMER_CONTROL_CONFIGURATION | {0, 64} | 0, 64 |
| KEY_MEDIA_EMAIL_READER | {0, 128} | 0, 128 |
Selanjutnya pertanyaan tambahan kalian mungkin bingung bagaimana cara pakai data base diatas?
kalian pasti melihat di data base ada angka desimal nomor 33 sampai 127 dan ada key tambahan di nomor 127++.
contoh penggunaannya seperti program di bawah ini :
C++
bleKeyboard.press(128); // Press and hold the Left Control key bleKeyboard.press(97); // Press and hold the 'a'
Khusus untuk key media kalian harus membuat variable seperti kode di bawah ini
C++
MediaKeyReport keyMedia = {0, 0}; // ubah nilainya sesai key yang mau di ambil contoh "MediaKeyReport keyMedia = {1, 0};"
bleKeyboard.write(keyMedia);
Selanjutnya pertanyaan tambahan kalian mungkin bingung apa perbedaan antara keyboard.press vs keyboard.write ?
| Fungsi | Aksi |
|---|---|
press(KEY_X) |
Menekan tombol (dan tetap ditekan) sampai program release di aktifkan |
release(KEY_X) |
Melepaskan tombol tertentu |
releaseAll() |
Melepaskan semua tombol |
write(KEY_X) |
Menekan lalu melepaskan tombol secara otomatis |
Permasalahan selanjutnya bagaimana membuat program di example bisa dikontrol dengan tombol ?
- Patikan kalian sudah menginstal Liblary No Delay Button
- Lalu kalian pilih tombol apa saya yang akan di gunakan ?
- Contoh saja kalian ingin mengirim fungsi "CTRL + A"
- Maka programnya akan menjadi seperti iniC++
#include <BleKeyboard.h> // Include the library for Bluetooth Low Energy (BLE) keyboard functionality BleKeyboard bleKeyboard; // Create an instance of the BleKeyboard object #include <NoDelayButton.h> // Include the library for debounced button input without delay uint8_t pins[] = { 4 }; // Define an array of pins for the buttons. Here, pin 4 is used. char buttonChars[] = { 'A' }; // Define an array of characters associated with each button. 'A' is associated with the button on pin 4. const uint8_t expectedButtonCount = 1; // Define the number of buttons being used. Here, it's 1. NoDelayButton button(pins, buttonChars, 1, expectedButtonCount); // Create an instance of the NoDelayButton object, initializing it with the pin, character, and count. void setup() { Serial.begin(115200); // Initialize serial communication at a baud rate of 115200. Serial.println("Starting BLE work!"); // Print a message to the serial monitor. bleKeyboard.begin(); // Initialize the BLE keyboard. This makes the ESP32 discoverable as a BLE device. } void loop() { if (bleKeyboard.isConnected()) { // Check if the ESP32 is connected to a BLE host (e.g., a computer or phone). if (button.scanValue() == 1) { // Check if the button is pressed. 'scanValue() == 1' means the button on the first pin (index 0) has been pressed. Serial.println("Sending Ctrl+a"); // Print a message to the serial monitor. bleKeyboard.press(128); // Press and hold the Left Control key. bleKeyboard.press(97); // Press and hold the 'a' key. delay(100); // Wait for 100 milliseconds to ensure the key presses are registered. bleKeyboard.releaseAll(); // Release all currently pressed keys (Ctrl and 'a'). } } }
Permasalahan selanjutnya bagaimana tombol keyboard bisa di ubah tanpa harus flashing ?
Kita bisa menggunakan banyak metode yang penting bisa kirim data ke microkontroler, contoh saya lebih prever menggunakan web server karena tidak perlu bikin aplikasi walaupun bikin aplikasi berbasis web tapi ini mudah.
Selanjutnya ada permasalahan tambahan menggunakan wifi dan bluetooth bersamaan mengakibatkan beberapa masalah !
- Dari sisi daya sudah pasti menaikan penggunaan listrik, ini bermasalah jika kalian menggunakan baterai.
- Dari sisi program akan ada banyak program security karena bisa saja wifinya di kontrol orang tidak lain mengakibatkan isi fungsi key kita berubah. Selain itu juga ada masalah memory dan gangguan stack.
- Dari sisi sinyal akan menurun drastis karena bekerja gantian, tenntu mengakibatkan kinerjanya menurun dan mengakibatkan masalah baru, tidak saya bahas karena saya tidak ada pengalaman ngoprek sinyal.
Jadi saya lebih prefrer menggunakan salah satunya di kontrol saat pertama kali boot.
Selanjutnya bagaimana cara mengaktifkan wifi saat configurasi dan menyalakan bluetooth saat ingin memakai tombolnya ?
Seperti yang sudah di jelakan kita tinggal membuat program seperti ini, jika tombol yang tersambung dengan pin 4 di tekan saat boot maka wifi akan aktif
C++
#include <Arduino.h>
#include <WiFi.h>
#include <BleKeyboard.h>
BleKeyboard bleKeyboard;
void setup()
{
Serial.begin(115200);
if (!digitalRead(4/*PIN CONTROL MODE*/))
{
WiFi.softAP(ssid);
//Lanjutkan Program loop WiFi
while (1)
{
}
}
else
{
WiFi.mode(WIFI_OFF);
bleKeyboard.begin();
//Lanjutkan Program Loop Bluetooth
while (1)
{
}
}
}
void loop()
{
}Selanjutnya bagaimana membuat web server pada ESP32 ?
Dari kode di atas kalian ubah beberapa bagian sampai berubah seperti kode di bawah ini, kode di bawah ini sudah ditambahkan program untuk esp32 bisa menghasilkan web server di port 80.
C++
#include <Arduino.h>
#include <WiFi.h>
#include <BleKeyboard.h>
#include <ESPAsyncWebServer.h>
#include <AsyncTCP.h>
const char *ssid = "[ CONFIG KEYBOARD ]"; // Ganti dengan SSID WiFi Anda
AsyncWebServer server(80);
BleKeyboard bleKeyboard;
void setup() {
Serial.begin(115200);
if (!digitalRead(4 /*PIN CONTROL MODE*/)) {
WiFi.softAP(ssid);
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send(200, "text/html", "WEB Server Berhasil dibuat!");
});
server.begin();
while (1) {
}
} else {
WiFi.mode(WIFI_OFF);
bleKeyboard.begin();
//Lanjutkan Program Loop Bluetooth
while (1) {
}
}
}
void loop() {
}
- Klik tombol konfiguras tekan [ jangan di lepas ]
- Klik reset dan menunggu 5 detik sehingga masuk boot configurasi keyboard
- Baru lepas tombol konfigurasi
| Menyambukan wifi ke soft Akses point dari esp32 |
| Hasil dari Web Server esp32 yang kita buat |
Jika sudah seperti diatas artinya kalian sudah bisa membuat Web Sederhana
Pertanyaan selanjutnya bang itu cuma server sederhana saya ingin full program bluetooth macro keyboard programmable ?
Ini dia program untuk
C++
#include <Arduino.h> #include <WiFi.h> #include <ESPAsyncWebServer.h> #include <AsyncTCP.h> #include <BleKeyboard.h> #include <NoDelayButton.h> #include <SPIFFS.h> #include <DataParser.h> const char index_html[] PROGMEM = R"rawliteral( <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Macro Keyboard Config</title> <style> body { background: linear-gradient(135deg, #0f2027, #203a43, #2c5364); color: white; font-family: 'Segoe UI', sans-serif; margin: 0; padding: 0; } .glass { background: rgba(0, 0, 0, 0.08); border-radius: 16px; backdrop-filter: blur(12px); box-shadow: 0 4px 30px rgba(0, 0, 0, 0.5); padding: 24px; margin: 40px auto; width: 90%; max-width: 500px; } h2 { text-align: center; margin-bottom: 20px; } label { display: block; margin-top: 10px; font-weight: bold; margin-bottom: 10px; } button { background-color: #4CAF50; border: none; padding: 10px 16px; color: white; font-size: 16px; border-radius: 8px; cursor: pointer; margin-top: 50px; width: 100%; } button:hover { background-color: #3e8e41; } select, input[type="number"], input[type="text"] { width: 100%; padding: 10px; font-size: 16px; border-radius: 8px; border: none; box-sizing: border-box; appearance: none; -webkit-appearance: none; -moz-appearance: none; height: 40px; } .info-box { background: rgba(255, 255, 255, 0.1); border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 12px; padding: 20px; margin-bottom: 25px; backdrop-filter: blur(8px); } .info-box h3 { margin-top: 0; color: #4CAF50; font-size: 18px; } .info-box p { margin: 10px 0; line-height: 1.6; } .info-box ol { margin: 10px 0; padding-left: 20px; } .info-box li { margin: 8px 0; line-height: 1.5; } </style> </head> <body> <div class="glass"> <h2>ESP32 Macro Config</h2> <div class="info-box"> <h3>Cara Penggunaan</h3> <p><strong>Prioritas Eksekusi:</strong></p> <ol> <li><strong>Key Write</strong> - Prioritas tertinggi, menulis karakter/tombol khusus</li> <li><strong>Key Media</strong> - Kontrol multimedia (jika Key Write kosong)</li> <li><strong>3 Key Press</strong> - Kombinasi 3 tombol (Ctrl+Shift+C)</li> <li><strong>2 Key Press</strong> - Kombinasi 2 tombol (Ctrl+C)</li> <li><strong>1 Key Press</strong> - Satu tombol saja</li> </ol> <p><strong>Catatan:</strong> Sistem akan mengeksekusi hanya satu yang teratas dari daftar yang terisi. </p> </div> <form action="/save_macro" method="GET"> <label for="key1">Key Press 1</label> <select name="key1" id="key1"></select> <label for="key2">Key Press 2</label> <select name="key2" id="key2"></select> <label for="key3">Key Press 3</label> <select name="key3" id="key3"></select> <label for="key4">Key Write</label> <select name="key4" id="key4"></select> <label for="delay">Delay (ms):</label> <input type="number" name="delay" id="delay" placeholder="Contoh: 100" /> <label for="slot">Program Ulang Tombol Nomor :</label> <input type="number" name="slot" id="slot" min="1" max="1" value="1" /> <button type="submit">Simpan Macro</button> </form> </div> <script> // Define all key options const keyOptions = { basic: [ { value: "", text: "--None--" }, { value: "128", text: "Ctrl" }, { value: "129", text: "Shift" }, { value: "130", text: "Alt" }, { value: "131", text: "GUI" } ], letters: [ { value: "65", text: "A" }, { value: "66", text: "B" }, { value: "67", text: "C" }, { value: "68", text: "D" }, { value: "69", text: "E" }, { value: "70", text: "F" }, { value: "71", text: "G" }, { value: "72", text: "H" }, { value: "73", text: "I" }, { value: "74", text: "J" }, { value: "75", text: "K" }, { value: "76", text: "L" }, { value: "77", text: "M" }, { value: "78", text: "N" }, { value: "79", text: "O" }, { value: "80", text: "P" }, { value: "81", text: "Q" }, { value: "82", text: "R" }, { value: "83", text: "S" }, { value: "84", text: "T" }, { value: "85", text: "U" }, { value: "86", text: "V" }, { value: "87", text: "W" }, { value: "88", text: "X" }, { value: "89", text: "Y" }, { value: "90", text: "Z" } ], numbers: [ { value: "48", text: "0" }, { value: "49", text: "1" }, { value: "50", text: "2" }, { value: "51", text: "3" }, { value: "52", text: "4" }, { value: "53", text: "5" }, { value: "54", text: "6" }, { value: "55", text: "7" }, { value: "56", text: "8" }, { value: "57", text: "9" } ], functionKeys: [ { value: "194", text: "F1" }, { value: "195", text: "F2" }, { value: "196", text: "F3" }, { value: "197", text: "F4" }, { value: "198", text: "F5" }, { value: "199", text: "F6" }, { value: "200", text: "F7" }, { value: "201", text: "F8" }, { value: "202", text: "F9" }, { value: "203", text: "F10" }, { value: "204", text: "F11" }, { value: "205", text: "F12" } ], arrows: [ { value: "218", text: "Up Arrow" }, { value: "217", text: "Down Arrow" }, { value: "216", text: "Left Arrow" }, { value: "215", text: "Right Arrow" } ], special: [ { value: "178", text: "Backspace" }, { value: "179", text: "Tab" }, { value: "176", text: "Enter" }, { value: "177", text: "Escape" }, { value: "32", text: "Space" }, { value: "212", text: "Delete" }, { value: "209", text: "Insert" }, { value: "210", text: "Home" }, { value: "213", text: "End" }, { value: "211", text: "Page Up" }, { value: "214", text: "Page Down" }, { value: "193", text: "Caps Lock" } ], multimedia: [ { value: "1'0", text: "Next Track" }, { value: "4'0", text: "Previous Track" }, { value: "8'0", text: "Play/Pause" }, { value: "16'0", text: "Mute" }, { value: "32'0", text: "Volume Up" }, { value: "64'0", text: "Volume Down" }, { value: "128'0", text: "Home" }, { value: "0'1", text: "My Computer" }, { value: "0'2", text: "Calculator" }, { value: "0'4", text: "Bookmarks" }, { value: "0'8", text: "Search" }, { value: "0'32", text: "Back" }, { value: "0'128", text: "Email Reader" } ] }; // Function to populate select elements function populateSelect(selectId, includeAll = false) { const select = document.getElementById(selectId); select.innerHTML = ''; // Clear existing options // Add default option const defaultOption = document.createElement('option'); defaultOption.value = ''; defaultOption.textContent = '--None--'; select.appendChild(defaultOption); // For key write select, add all options organized by groups const groups = [ { name: 'Basic Keys', options: keyOptions.basic.slice(1) }, { name: 'Letters', options: keyOptions.letters }, { name: 'Numbers', options: keyOptions.numbers }, { name: 'Function Keys', options: keyOptions.functionKeys }, { name: 'Arrow Keys', options: keyOptions.arrows }, { name: 'Special Keys', options: keyOptions.special }, { name: 'Multimedia', options: keyOptions.multimedia } ]; groups.forEach(group => { const optgroup = document.createElement('optgroup'); optgroup.label = group.name; group.options.forEach(option => { const optionElement = document.createElement('option'); optionElement.value = option.value; optionElement.textContent = option.text; optgroup.appendChild(optionElement); }); select.appendChild(optgroup); }); } // Initialize all select elements when page loads document.addEventListener('DOMContentLoaded', function () { populateSelect('key1'); populateSelect('key2'); populateSelect('key3'); populateSelect('key4'); }); </script> </body> </html> )rawliteral"; // Definisikan pin dan karakter untuk tombol uint8_t pins[] = { 4 }; char buttonChars[] = { 'A' }; const uint8_t expectedButtonCount = 1; NoDelayButton button(pins, buttonChars, 1, expectedButtonCount); BleKeyboard bleKeyboard; uint8_t keyPress1Button1 = 0; uint8_t keyPress2Button1 = 0; uint8_t keyPress3Button1 = 0; uint8_t keyWriteButton1 = 0; uint8_t delayButton1 = 100; MediaKeyReport keyWriteMediaButton1 = { 0, 0 }; void loadMacroSlot1(); const char *ssid = "[ CONFIG KEYBOARD ]"; // Ganti dengan SSID WiFi Anda AsyncWebServer server(80); void handleSaveMacro(AsyncWebServerRequest *request); void setup() { Serial.begin(115200); if (!SPIFFS.begin(true)) { Serial.println("SPIFFS Gagal"); while (1) { delay(1000); } } if (button.scanValue() == 1) { WiFi.softAP(ssid); Serial.println("WiFi AP Created."); server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(200, "text/html", index_html); }); server.on("/save_macro", HTTP_GET, handleSaveMacro); server.begin(); while (1) { delay(1000); } } else { WiFi.mode(WIFI_OFF); Serial.println("WiFi is OFF."); bleKeyboard.begin(); loadMacroSlot1(); while (1) { if (bleKeyboard.isConnected()) { if (button.scanValue() == 1) { if (delayButton1 > 0) { if (keyWriteButton1 != 0) { bleKeyboard.write(keyWriteButton1); Serial.println("Key Write: " + String(keyWriteButton1)); } else if (keyWriteMediaButton1[0] != 0 || keyWriteMediaButton1[1] != 0) { bleKeyboard.write(keyWriteMediaButton1); Serial.println("Media Key: " + String(keyWriteMediaButton1[0]) + "," + String(keyWriteMediaButton1[1])); } else if (keyPress1Button1 != 0 && keyPress2Button1 != 0 && keyPress3Button1 != 0) { bleKeyboard.press(keyPress1Button1); delay(1); bleKeyboard.press(keyPress2Button1); delay(1); bleKeyboard.press(keyPress3Button1); bleKeyboard.releaseAll(); } else if (keyPress1Button1 != 0 && keyPress2Button1 != 0) { bleKeyboard.press(keyPress1Button1); delay(1); bleKeyboard.press(keyPress2Button1); bleKeyboard.releaseAll(); } else if (keyPress1Button1 != 0) { bleKeyboard.press(keyPress1Button1); bleKeyboard.releaseAll(); } delay(delayButton1); } } } } } } void loop() { } void handleSaveMacro(AsyncWebServerRequest *request) { String keyPress1 = request->hasParam("key1") ? request->getParam("key1")->value() : "0"; String keyPress2 = request->hasParam("key2") ? request->getParam("key2")->value() : "0"; String keyPress3 = request->hasParam("key3") ? request->getParam("key3")->value() : "0"; String keyWrite = request->hasParam("key4") ? request->getParam("key4")->value() : "0"; String delay = request->hasParam("delay") ? request->getParam("delay")->value() : "0"; String slot = request->hasParam("slot") ? request->getParam("slot")->value() : "1"; String filename = "/macro" + slot + ".txt"; String content = keyPress1 + "," + keyPress2 + "," + keyPress3 + "," + keyWrite + "," + delay; File file = SPIFFS.open(filename, FILE_WRITE); if (file) { file.print(content); file.close(); request->send(200, "text/html", "Macro slot " + slot + " saved! <br>" + content + "<script>" "setTimeout(function(){ window.location.href = '/'; }, 2000);" "</script>"); } else { request->send(500, "text/plain", "Gagal menyimpan ke SPIFFS"); } } void loadMacroSlot1() { String filename = "/macro1.txt"; if (!SPIFFS.exists(filename)) { Serial.println("File tidak ada."); return; } File file = SPIFFS.open(filename, FILE_READ); if (!file) { Serial.println("Gagal membuka file."); return; } String line = file.readStringUntil('\n'); file.close(); String output[10]; int jumlahString = 0; Serial.println("=== Loading Macro Slot 1 ==="); Serial.println("Line: " + line); DataParser::parse(line, output, ',', jumlahString); // Substring setiap field keyPress1Button1 = output[0].toInt(); keyPress2Button1 = output[1].toInt(); keyPress3Button1 = output[2].toInt(); // Cek apakah keyWriteButton1 adalah angka atau string // Jika tidak ada karakter pemisah, berarti itu adalah angka if (output[3].indexOf("'") == -1) { keyWriteButton1 = output[3].toInt(); } else if (output[3].indexOf("'") != -1) { String output3[2]; DataParser::parse(output[3], output3, '\'', jumlahString); keyWriteMediaButton1[0] = output3[0].toInt(); // Inisialisasi dengan nilai default keyWriteMediaButton1[1] = output3[1].toInt(); // Inisialisasi dengan nilai default } delayButton1 = output[4].toInt() > 100 ? output[4].toInt() : 100; // Set minimum delay to 100ms // Debug print Serial.println("=== Macro Slot 1 Loaded ==="); Serial.println("Key1: " + (String)keyPress1Button1); Serial.println("Key2: " + (String)keyPress2Button1); Serial.println("Key3: " + (String)keyPress3Button1); Serial.println("Key4: " + (String)keyWriteButton1); Serial.println("Key4: " + String(keyWriteMediaButton1[0]) + "," + String(keyWriteMediaButton1[1])); Serial.println("Delay: " + (String)delayButton1); }
Komentar
Posting Komentar