├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── include ├── bt_keyboard.hpp ├── esp32-ps2dev.h ├── globals.hpp ├── scan_codes_set_2.h └── serial_mouse.h ├── main ├── CMakeLists.txt ├── bt_keyboard.cpp ├── esp32-ps2dev.cpp ├── main.cpp └── serial_mouse.cpp ├── sdkconfig ├── sdkconfig.defaults └── sdkconfig.old /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | sdkconfig 3 | sdkconfig.old -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # For more information about build system see 2 | # https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html 3 | # The following five lines of boilerplate have to be in your project's 4 | # CMakeLists in this exact order for cmake to work correctly 5 | cmake_minimum_required(VERSION 3.5) 6 | 7 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 8 | project(esp32-bt2ps2) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2008-2023 Humberto Möckel, Chris J. Kiick, Gene E. Scogin, Tomas Mudrunka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 1/9/24: Serial Mouse support is out for v0.7! Check notes on release 2 | 3 | 24/8/24: Mouse and Multimedia Key support is out for v0.6! Check notes on release 4 | 5 | Tested Bluetooth Keyboards and Mice (Please add your own!): [Excel spreadsheet](https://1drv.ms/x/c/496a36eb8f7bab52/EVKre4_rNmoggEllRQIAAAABdvU2lrtNIQN1tRJOs5V3sw?e=jx4JUH) 6 | 7 | FreeRTOS ticking rate of 1000hz and other configs are critical. Be sure to use the default SDKconfig.defaults file included! 8 | 9 | # ESP32 Bluetooth/BLE to PS/2 keyboard/mouse adapter 10 | 11 | Project to adapt a Bluetooth or BLE keyboard and/or mouse to use on a computer with compatible PS/2 keyboard/mouse connector/s, wirelessly. 12 | Note that big DIN 5 pin connectors ("AT" keyboard) and Mini-DINs (the violet ones) are equally supported. 13 | 14 | YouTube demo: https://youtu.be/2PVjWfAAJFE 15 | 16 |

17 | 18 |

19 | 20 | # Compatibility 21 | 22 | Tested Bluetooth Keyboards and Mice (Please add your own!): https://1drv.ms/x/s!AlKre4_rNmpJiYpl1v4KcbK1Pm77zA?e=0I6QRB 23 | 24 | Working under latest ESP-IDF v5.3, compiled on Visual Studio Code. Multi-device support may not work on lower versions of the SDK. 25 | 26 | **Developed and tested on the ESP32 DevKit rev 1 board, other variants may not work!** 27 | 28 | WARNING: This project is for use in a plain ESP-32 module with BLE and BT Classic support and dual core processor. If you have another variant like C3, you'll have to adapt the code. 29 | 30 | * ESP32 S3/C3 (BLE only boards): Check https://github.com/Hamberthm/esp32-bt2ps2/issues/3 31 | * USB-HID instead of PS/2: Check https://github.com/Hamberthm/esp32-bt2ps2/issues/4 32 | 33 | # Electrical connections 34 | 35 | DIN and Mini-DIN connectors have similar pinout but in different arrangements. 36 | 37 | Please use a multimeter and online info to make your cable. Beware of voltage present on the port, you can short and fry things! 38 | 39 | Connections: 40 | 41 | | ESP32 pin | PS/2 pin | example color | Notes | 42 | | --------- | -------- | ------------- | --------------------------------- | 43 | | 23 or any | DATA | orange | Repeat on other pin for mouse bus | 44 | | 22 or any | CLK | white | Repeat on other pin for mouse bus | 45 | | GND | GND | black | Always connect! | 46 | | Vin | +5v | red | Disconnect if using USB power! | 47 | 48 | You can change the DATA and CLK pins to whatever suits your fancy using these lines in the main.cpp file: 49 | 50 | ```cpp 51 | const int KB_CLK_PIN = 22; 52 | const int KB_DATA_PIN = 23; 53 | 54 | const int MOUSE_CLK_PIN = 26; 55 | const int MOUSE_DATA_PIN = 25; 56 | ``` 57 | Note: Pin 12 & 13 are ideal to solder a connector so you can use Vin, GND, 13 and 12 all in a row (ESP32 DevKit rev 1 boards). BEWARE that pin 12 is a strapping pin on the ESP32 and the module will FAIL to boot due to high signals from the PS/2 port. You can remove the strapping function of pin 12 by blowing an [eFuse](https://docs.espressif.com/projects/esptool/en/latest/esp32s2/espefuse/index.html) on your board. Use the following command: 58 | 59 | ``` 60 | python espefuse.py --port COM4 set_flash_voltage 3.3V 61 | ``` 62 | 63 | Also, applying voltage to pins 1 & 3 (U0 TXD and RXD) could cause the serial communication not to work, thus making serial console output impossible. Be careful on the ones you choose! 64 | 65 | There is no need to connect the 5 volts from the port if you wish to power the board over USB. For debugging I recommend you leave it disconnected, as you can end up back-feeding 5 volts back to the PS/2 port, bad things can happen. Once all is working and you don't want to debug anymore, the 5 volts from the port are enough to power the board over the Vin (regulated) pin, making this a pretty neat standalone device! 66 | 67 | NOTE: Don't leave the GND cable from the PS2 port floating, otherwise communication won't work! Always connect GND cable to the board even if you're using external power. 68 | 69 | Note: ESP32 is **unofficially** 5V tolerant, so you can directly connect PS/2 pins to the board, that's my setup on my rev v1 board and I had no problems. However, you may use a logic level converter. 70 | 71 | # Building and flashing 72 | 73 | *Note there's a binary release available! 74 | 75 | Project works as-is under Visual Studio Code (2024). You need to have the Espressif IDF extension installed and the v5.3 of the ESP-IDF SDK. Workflow is as follows: 76 | 77 | 1- Install Visual Studio Code 78 | 79 | 2- On the left, open the extensions panel 80 | 81 | 3- Search and install the Espressif IDF extension 82 | 83 | 4- Open the command prompt pressing Ctrl+Shift+P 84 | 85 | 5- Execute command "ESP-IDF: Configure ESP-IDF extension" 86 | 87 | 6- Select Express, and under "Select ESP-IDF version" choose v5.3 88 | 89 | 7- After installation, select File > Open Folder and open the ESP32-BT2PS2 project folder 90 | 91 | 8- Start building by pressing Ctrl+E and then B, or using the Build button in the bottom bar 92 | 93 | 9- If succesfully built, connect and flash your ESP32 board (Ctrl+E then F, or the flash button) 94 | 95 | Refer to the following link for more instructions: 96 | 97 | * https://github.com/espressif/vscode-esp-idf-extension/blob/master/docs/tutorial/install.md 98 | 99 | Once succesfully built and flashed, you're ready to rock! (your BLE keyboard on an ancient computer, that is). 100 | 101 | 102 | # Usage and debugging 103 | 104 | Once powered up and first of all, the code will create and init an `esp32_ps2dev::PS2Keyboard` object, so it can start to talk to the computer as soon as possible. This is critical because during boot the BIOS can send different commands to the module to test the presence of the keyboard. 105 | 106 | After PS/2 init, the module scans for nearby Bluetooth and BLE devices. If the last bonded keyboard is in range, it will try to connect to it using the keys stored on the NVS flash, so no pairing is needed for every connection. If it doesn't detect a previously bonded device, it will try to connect to the nearest keyboard in pairing mode. If both processes fail, it will wait one second and scan again until it finds anything. 107 | 108 | IMPORTANT: Is recommended to do the pairing to the keyboard BEFORE connecing the board to a PS/2 port. For this, connect a charger or power bank to the ESP's USB port and pair your device more comfortably. 109 | 110 | For v0.6, you can invoke pairing during execution (blue LED on) at any time. Press the "BOOT" button on the rev v1 board, essentially shorting GPIO_0 to ground. LED will go off and enter pairing mode. To cancel pairing, press and hold for 3 seconds (pairing aborting does not work on startup pairing). 111 | 112 | **LED off: Pairing mode activated. LED on: normal execution, only previously paired devices can connect at any time.** 113 | 114 | ### >> BLE KEYBOARD OR MOUSE PAIRING: 115 | Set keyboard or mouse in pairing mode and power on the board. No code entry required. 116 | 117 | ### >> BLUETOOTH CLASSIC KEYBOARD PAIRING (code pairing): 118 | 119 | 1- Set keyboard in pairing mode and power on the board. 120 | 121 | 2- Wait until you see a short blast of quick flashes, then pay attention: 122 | 123 | 3- For each code digit, the board will flash the LED the number of times equal to the digit. 124 | 125 | 4- Press each digit on the keyboard as you read them (do not wait until it finishes! Some keyboards have a timeout). 126 | 127 | 5- If the digit is 0, a steady light will show for 1.5 seconds instead of the digit. 128 | 129 | 6- When you see the blast of quick flashes again, press ENTER. 130 | 131 | OR you can always look at the code using the Serial monitor console, whatever you find easier, you fancy boy. 132 | 133 | ### >> BLUETOOTH CLASSIC KEYBOARD PAIRING, LEGACY PROCEDURE (legacy code pairing): 134 | 135 | In older keyboards, the user must enter a custom code on the host device and then on the keyboard. Since we can't input it easily on the ESP32, the code is fixed to 1234. 136 | 137 | 1- Set keyboard in pairing mode and power on the board 138 | 139 | 2- Watch the Serial Ouput Console. Wait for the board finishing the scan and for the message "Waiting pairing code entry..." 140 | 141 | 3- Type 1234 on the keyboard then press ENTER. 142 | 143 | Note: For Legacy Mode, no LED output is possible at the moment, so we need to check the serial console for the right time to enter the code. 144 | 145 | --- 146 | 147 | - If paired and working correctly, the board should blink the LED with each key press on the keyboard, or with each click on the mouse 148 | 149 | Once connected you can start using your keyboard and mouse, blue LED should be on. Remove any USB power source and connect the board to the PS/2 compatible system and enjoy. Remember PS/2 is NOT a HOT-SWAP protocol, please only connect the board with the system totally OFF. 150 | 151 | You can hot-disconnect the keyboard and mouse for models that support multiple devices hopping. The module will detect the disconnection and repeatedly try to reconnect while you're using other systems, so it will be back online as soon as the keyboard gets up to the ESP32 again. This is critical for keyboards/mouses that go to sleep and disconnect, or if you swap between computers using a multi-connection keyboard. 152 | 153 | Please note that pairing is only done after power up. If you wish to pair a new device, please reset the module with the reset button or power off and on the computer (not just reset it, because we need a power cycle). Note that if a previously paired device is on and in range, it will always connect to it first. Also invoke pairing after power on (LED on) by using the BOOT button or shorting GPIO_0 to ground. 154 | 155 | In case something doesn't work, you'll need to debug. 156 | 157 | If the blue light on the module lights up and your keyboard connects, but it doesn't work, first of all reset your system. If it still doesn't work, then you'll need to enable debugging in the `esp32-ps2dev.cpp` file using `#DEFINE _ESP32_PS2DEV_DEBUG_`. Check for "PS/2 command received" messages and see where it hangs, or what the BIOS doesn't like. This is advanced so if you need help, make a new issue. 158 | 159 | # TODO 160 | * Test on many keyboards and mouses. 161 | * Improve stability. 162 | * Clean up the code. 163 | * Test against various hosts. 164 | 165 | # Reference 166 | - http://www-ug.eecg.toronto.edu/msl/nios_devices/datasheets/PS2%20Protocol.htm 167 | - http://www-ug.eecg.toronto.edu/msl/nios_devices/datasheets/PS2%20Mouse%20Protocol.htm 168 | - http://www-ug.eecg.toronto.edu/msl/nios_devices/datasheets/PS2%20Keyboard%20Protocol.htm 169 | 170 | # History 171 | Using code and hard work from these folks: 172 | 173 | * https://playground.arduino.cc/ComponentLib/Ps2mouse/ 174 | * https://github.com/grappendorf/arduino-framework/tree/master/ps2dev 175 | * https://github.com/dpavlin/Arduino-projects/tree/master/libraries/ps2dev 176 | * ps2 library Written by Chris J. Kiick, January 2008. https://github.com/ckiick 177 | * modified by Gene E. Scogin, August 2008. 178 | * modified by Tomas 'Harvie' Mudrunka 2019. https://github.com/harvie/ps2dev 179 | * modified for ESP32 by hrko 2022. https://github.com/hrko/esp32-ps2dev 180 | * fixed for PC compatibility and stability by Hambert, 2023. https://github.com/Hamberthm/esp32-ps2dev 181 | * bt-keyboard library by Guy Turcotte, 2022. https://github.com/turgu1/bt-keyboard 182 | * adapted to Arduino IDE and added improvements by Hambert, 2023. https://github.com/Hamberthm/bt-keyboard 183 | -------------------------------------------------------------------------------- /include/bt_keyboard.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Guy Turcotte 2 | // 3 | // MIT License. Look at file licenses.txt for details. 4 | // 5 | // ----- 6 | // 7 | // Original code from the bluetooth/esp_hid_host example of ESP-IDF license: 8 | // 9 | // Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD 10 | // 11 | // Licensed under the Apache License, Version 2.0 (the "License"); 12 | // you may not use this file except in compliance with the License. 13 | // You may obtain a copy of the License at 14 | 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, software 18 | // distributed under the License is distributed on an "AS IS" BASIS, 19 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | // See the License for the specific language governing permissions and 21 | // limitations under the License. 22 | 23 | #pragma once 24 | 25 | #include "freertos/FreeRTOS.h" 26 | #include "freertos/task.h" 27 | #include "freertos/semphr.h" 28 | #include "freertos/event_groups.h" 29 | #include "esp_err.h" 30 | #include "esp_log.h" 31 | #include "esp_system.h" 32 | #include "esp_event.h" 33 | #include "esp_bt.h" 34 | #include "esp_bt_defs.h" 35 | #include "esp_bt_device.h" 36 | #include "esp_bt_main.h" 37 | #include "esp_hidh.h" 38 | #include "esp_hid_common.h" 39 | #include "esp_gap_bt_api.h" 40 | #include "esp_gap_ble_api.h" 41 | #include "esp_gatts_api.h" 42 | #include "esp_gatt_defs.h" 43 | #include "esp_wifi.h" 44 | 45 | #include 46 | #include 47 | 48 | // #include "esp32-hal-bt.h" 49 | 50 | class BTKeyboard 51 | { 52 | public: 53 | typedef void pid_handler(uint32_t code); 54 | 55 | const uint8_t KEY_CAPS_LOCK = 0x39; 56 | 57 | enum class KeyModifier : uint8_t 58 | { 59 | L_CTRL = 0x01, 60 | L_SHIFT = 0x02, 61 | L_ALT = 0x04, 62 | L_META = 0x08, 63 | R_CTRL = 0x10, 64 | R_SHIFT = 0x20, 65 | R_ALT = 0x40, 66 | R_META = 0x80 67 | }; 68 | 69 | const uint8_t CTRL_MASK = ((uint8_t)KeyModifier::L_CTRL) | ((uint8_t)KeyModifier::R_CTRL); 70 | const uint8_t SHIFT_MASK = ((uint8_t)KeyModifier::L_SHIFT) | ((uint8_t)KeyModifier::R_SHIFT); 71 | const uint8_t ALT_MASK = ((uint8_t)KeyModifier::L_ALT) | ((uint8_t)KeyModifier::R_ALT); 72 | const uint8_t META_MASK = ((uint8_t)KeyModifier::L_META) | ((uint8_t)KeyModifier::R_META); 73 | 74 | static const uint8_t MAX_KEY_COUNT = 10; // Adjusted for normal humans with 10 fingers 75 | // static const uint8_t MAX_KEY_COUNT = 11; // Uncomment if also using dick 76 | // static const uint8_t MAX_KEY_COUNT = 5; // Uncomment if you're a known argentinian politician 77 | 78 | struct KeyInfo 79 | { 80 | KeyModifier modifier; 81 | uint8_t keys[MAX_KEY_COUNT]; 82 | }; 83 | 84 | struct KeyInfo_CCONTROL // container for 16-bit CCONTROL Usage Codes 85 | { 86 | uint16_t keys[MAX_KEY_COUNT]; 87 | }; 88 | 89 | struct Mouse_Control 90 | { 91 | int16_t mouse_x = 0; 92 | 93 | int16_t mouse_y = 0; 94 | 95 | int8_t mouse_w = 0; 96 | 97 | uint8_t mouse_buttons = 0; 98 | }; 99 | 100 | private: 101 | static constexpr char const *TAG = "BTKeyboard"; 102 | 103 | static const esp_bt_mode_t HIDH_IDLE_MODE = (esp_bt_mode_t)0x00; 104 | static const esp_bt_mode_t HIDH_BLE_MODE = (esp_bt_mode_t)0x01; 105 | static const esp_bt_mode_t HIDH_BT_MODE = (esp_bt_mode_t)0x02; 106 | static const esp_bt_mode_t HIDH_BTDM_MODE = (esp_bt_mode_t)0x03; 107 | 108 | #if CONFIG_BT_HID_HOST_ENABLED 109 | #if CONFIG_BT_BLE_ENABLED 110 | static const esp_bt_mode_t HID_HOST_MODE = HIDH_BTDM_MODE; 111 | #else 112 | static const esp_bt_mode_t HID_HOST_MODE = HIDH_BT_MODE; 113 | #endif 114 | #elif CONFIG_BT_BLE_ENABLED 115 | static const esp_bt_mode_t HID_HOST_MODE = HIDH_BLE_MODE; 116 | #else 117 | static const esp_bt_mode_t HID_HOST_MODE = HIDH_IDLE_MODE; 118 | #endif 119 | 120 | static SemaphoreHandle_t bt_hidh_cb_semaphore; 121 | static SemaphoreHandle_t ble_hidh_cb_semaphore; 122 | 123 | struct esp_hid_scan_result_t 124 | { 125 | struct esp_hid_scan_result_t *next; 126 | 127 | esp_bd_addr_t bda; 128 | const char *name; 129 | int8_t rssi; 130 | esp_hid_usage_t usage; 131 | esp_hid_transport_t transport; // BT, BLE or USB 132 | 133 | union 134 | { 135 | struct 136 | { 137 | esp_bt_cod_t cod; 138 | esp_bt_uuid_t uuid; 139 | } bt; 140 | struct 141 | { 142 | esp_ble_addr_type_t addr_type; 143 | uint16_t appearance; 144 | } ble; 145 | }; 146 | }; 147 | 148 | typedef struct esp_hidh_dev_report_s // stealed from stack 149 | { 150 | struct esp_hidh_dev_report_s *next; 151 | uint8_t map_index; // the index of the report map 152 | uint8_t report_id; // the id of the report 153 | uint8_t report_type; // input, output or feature 154 | uint8_t protocol_mode; // boot or report 155 | size_t value_len; // maximum len of value by report map 156 | esp_hid_usage_t usage; // generic, keyboard or mouse 157 | // BLE properties 158 | uint16_t handle; // handle to the value 159 | uint16_t ccc_handle; // handle to client config 160 | uint8_t permissions; // report permissions 161 | } esp_hidh_dev_report_t; 162 | 163 | typedef struct 164 | { 165 | uint16_t usage_page = 0; 166 | uint16_t usage = 0; 167 | uint16_t inner_usage_page = 0; 168 | uint16_t inner_usage = 0; 169 | uint8_t report_id = 0; 170 | uint16_t input_len = 0; 171 | uint16_t output_len = 0; 172 | uint16_t feature_len = 0; 173 | uint32_t logical_minimum = 0; 174 | uint32_t logical_maximum = 0; 175 | uint32_t usage_minimum = 0; 176 | uint32_t usage_maximum = 0; 177 | bool contains_array = false; 178 | uint16_t mouse_x_bit_index = 0; 179 | uint16_t mouse_x_lenght = 0; 180 | uint16_t mouse_y_bit_index = 0; 181 | uint16_t mouse_y_lenght = 0; 182 | uint16_t mouse_w_bit_index = 0; 183 | uint16_t mouse_w_lenght = 0; 184 | uint16_t mouse_buttons_bit_index = 0; 185 | uint16_t mouse_buttons_amount = 0; 186 | } hid_report_params_t; 187 | 188 | typedef struct 189 | { 190 | uint8_t report_id = 0; 191 | uint16_t input_len = 0; 192 | uint32_t logical_minimum = 0; 193 | uint32_t logical_maximum = 0; 194 | uint32_t usage_minimum = 0; 195 | uint32_t usage_maximum = 0; 196 | uint16_t report_count = 0; 197 | bool contains_array = false; 198 | std::vector array_usages; 199 | } hid_report_multimedia_control; 200 | 201 | typedef struct 202 | { 203 | uint8_t report_id = 0; 204 | uint16_t input_len = 0; 205 | uint16_t mouse_x_bit_index = 0; 206 | uint16_t mouse_x_bit_lenght = 0; 207 | uint16_t mouse_y_bit_index = 0; 208 | uint16_t mouse_y_bit_lenght = 0; 209 | uint16_t mouse_w_bit_index = 0; 210 | uint16_t mouse_w_bit_lenght = 0; 211 | uint16_t mouse_buttons_bit_index = 0; 212 | uint16_t mouse_buttons_amount = 0; 213 | } hid_report_mouse; 214 | 215 | static std::map, hid_report_multimedia_control> multimedia_reports; 216 | static std::map, hid_report_mouse> mouse_reports; 217 | static KeyInfo infoKey; 218 | 219 | typedef enum 220 | { 221 | PARSE_WAIT_USAGE_PAGE, 222 | PARSE_WAIT_USAGE, 223 | PARSE_WAIT_COLLECTION_APPLICATION, 224 | PARSE_WAIT_END_COLLECTION 225 | } s_parse_step_t; 226 | 227 | static s_parse_step_t s_parse_step; 228 | static uint8_t s_collection_depth; 229 | static hid_report_params_t s_report_params; 230 | static hid_report_params_t s_report_params_empty; 231 | static uint16_t s_report_size; 232 | static uint16_t s_report_count; 233 | static int s_usages_count; 234 | static std::vector temp_usages_array; 235 | 236 | typedef struct 237 | { 238 | uint8_t cmd; 239 | uint8_t len; 240 | union 241 | { 242 | uint32_t value; 243 | uint8_t data[4]; 244 | }; 245 | } hid_report_cmd_t; 246 | 247 | esp_hid_scan_result_t *bt_scan_results; 248 | esp_hid_scan_result_t *ble_scan_results; 249 | static esp_hid_scan_result_t lastConnected; 250 | size_t num_bt_scan_results; 251 | size_t num_ble_scan_results; 252 | 253 | static void hidh_callback(void *handler_args, esp_event_base_t base, int32_t id, void *event_data); 254 | 255 | static void bt_gap_event_handler(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param); 256 | static void ble_gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); 257 | 258 | static const char *ble_addr_type_str(esp_ble_addr_type_t ble_addr_type); 259 | static const char *ble_gap_evt_str(uint8_t event); 260 | static const char *bt_gap_evt_str(uint8_t event); 261 | static const char *ble_key_type_str(esp_ble_key_type_t key_type); 262 | 263 | static const char *gap_bt_prop_type_names[]; 264 | static const char *ble_gap_evt_names[]; 265 | static const char *bt_gap_evt_names[]; 266 | static const char *ble_addr_type_names[]; 267 | 268 | static const char shift_trans_dict[]; 269 | 270 | void handle_bt_device_result(esp_bt_gap_cb_param_t *param); 271 | void handle_ble_device_result(esp_ble_gap_cb_param_t *scan_rst); 272 | 273 | void esp_hid_scan_results_free(esp_hid_scan_result_t *results); 274 | esp_hid_scan_result_t *find_scan_result(esp_bd_addr_t bda, esp_hid_scan_result_t *results); 275 | 276 | void add_bt_scan_result(esp_bd_addr_t bda, 277 | esp_bt_cod_t *cod, 278 | esp_bt_uuid_t *uuid, 279 | uint8_t *name, 280 | uint8_t name_len, 281 | int rssi); 282 | 283 | void add_ble_scan_result(esp_bd_addr_t bda, 284 | esp_ble_addr_type_t addr_type, 285 | uint16_t appearance, 286 | uint8_t *name, 287 | uint8_t name_len, 288 | int rssi); 289 | 290 | void print_uuid(esp_bt_uuid_t *uuid); 291 | 292 | esp_err_t start_ble_scan(uint32_t seconds); 293 | esp_err_t start_bt_scan(uint32_t seconds); 294 | esp_err_t esp_hid_scan(uint32_t seconds, size_t *num_results, esp_hid_scan_result_t **results, bool enable_bt_classic); 295 | 296 | inline void set_battery_level(uint8_t level) { battery_level = level; } 297 | 298 | void push_key(uint8_t *keys, uint8_t size); 299 | void push_key_CCONTROL(uint16_t *keys, uint8_t size); 300 | void mouse_handle(uint8_t *report_data, std::pair *key_pair); 301 | 302 | QueueHandle_t event_queue; 303 | QueueHandle_t event_queue_CCONTROL; // queue for long 16-bit CCONTROL Usage Codes 304 | QueueHandle_t event_queue_MOUSE; // queue for mouse control 305 | int8_t battery_level; 306 | bool key_avail[MAX_KEY_COUNT]; 307 | char last_ch; 308 | TickType_t repeat_period; 309 | pid_handler *pairing_handler; 310 | bool caps_lock; 311 | 312 | static esp_err_t hid_report_parse_multimedia_keys(const uint8_t *hid_rm, size_t hid_rm_len, esp_hidh_dev_t *device); 313 | static int parse_cmd(const uint8_t *data, size_t len, size_t index, hid_report_cmd_t **out); 314 | static int handle_cmd(hid_report_cmd_t *cmd, esp_hidh_dev_t *device); 315 | static int handle_report(hid_report_params_t *report, esp_hidh_dev_t *device); 316 | int16_t getBits(const void *Data, uint16_t StartBit, uint16_t NumBits); 317 | 318 | public: 319 | BTKeyboard() : bt_scan_results(nullptr), 320 | ble_scan_results(nullptr), 321 | num_bt_scan_results(0), 322 | num_ble_scan_results(0), 323 | pairing_handler(nullptr), 324 | caps_lock(false) 325 | { 326 | } 327 | 328 | bool setup(pid_handler *handler = nullptr); 329 | bool devices_scan(int seconds_wait_time = 5); 330 | bool devices_scan_ble_daemon(int seconds_wait_time = 5); 331 | 332 | inline uint8_t get_battery_level() { return battery_level; } 333 | 334 | inline bool wait_for_low_event(KeyInfo &inf, TickType_t duration = portMAX_DELAY) 335 | { 336 | return xQueueReceive(event_queue, &inf, duration); 337 | } 338 | 339 | inline bool wait_for_low_event_CCONTROL(KeyInfo_CCONTROL &inf, TickType_t duration = portMAX_DELAY) 340 | { 341 | return xQueueReceive(event_queue_CCONTROL, &inf, duration); 342 | } 343 | 344 | inline bool wait_for_low_event_MOUSE(Mouse_Control &inf, TickType_t duration = portMAX_DELAY) 345 | { 346 | return xQueueReceive(event_queue_MOUSE, &inf, duration); 347 | } 348 | 349 | char wait_for_ascii_char(bool forever = true); 350 | inline char get_ascii_char() { return wait_for_ascii_char(false); } 351 | 352 | void quick_reconnect(void); 353 | 354 | static bool isConnected; // hidh callback event CLOSE turns this false when kb disconnects 355 | static bool btFound; // found a BT (not BLE) device during scan, let's wait for pairing 356 | }; 357 | -------------------------------------------------------------------------------- /include/esp32-ps2dev.h: -------------------------------------------------------------------------------- 1 | #ifndef __ESP32_PS2DEV_H__ 2 | #define __ESP32_PS2DEV_H__ 3 | 4 | #include 5 | #include 6 | 7 | // #include "Arduino.h" 8 | #include "freertos/FreeRTOS.h" 9 | #include "freertos/task.h" 10 | #include "freertos/semphr.h" 11 | #include "freertos/event_groups.h" 12 | #include "driver/gpio.h" 13 | #include "esp_timer.h" 14 | #include "esp_log.h" 15 | #include 16 | #include "scan_codes_set_2.h" 17 | #include 18 | #include 19 | 20 | namespace esp32_ps2dev 21 | { 22 | // Time per clock should be 60 to 100 microseconds according to PS/2 specifications. 23 | // Thus, half period should be 30 to 50 microseconds. 24 | const uint32_t CLK_HALF_PERIOD_MICROS = 40; 25 | const uint32_t CLK_QUATER_PERIOD_MICROS = CLK_HALF_PERIOD_MICROS / 2; 26 | // I could not find any specification of time between bytes from the PS/2 specification. 27 | // Based on observation of the mouse signal waveform using an oscilloscope, there appears to be an interval of 1 to 2 clock cycles. 28 | // ref. https://youtu.be/UqRDLWGLCEk 29 | const uint32_t BYTE_INTERVAL_MICROS = 500; // in v0.4 was OK: 500, change if not working and you know what you're doing. 30 | const int PACKET_QUEUE_LENGTH = 20; 31 | const UBaseType_t DEFAULT_TASK_PRIORITY = 10; 32 | //const BaseType_t DEFAULT_TASK_CORE = APP_CPU_NUM; 33 | const BaseType_t DEFAULT_TASK_CORE = 0; 34 | const BaseType_t DEFAULT_TASK_CORE_MOUSE = 1; 35 | // The device should check for "HOST_REQUEST_TO_SEND" at a interval not exceeding 10 milliseconds. 36 | const uint32_t INTERVAL_CHECKING_HOST_SEND_REQUEST_MILLIS = 9; 37 | const uint32_t MOUSE_CLICK_PRESSING_DURATION_MILLIS = 100; 38 | 39 | class PS2Packet 40 | { 41 | public: 42 | uint8_t len; 43 | uint8_t data[16]; 44 | }; 45 | 46 | class PS2dev 47 | { 48 | public: 49 | PS2dev(int clk, int data); 50 | 51 | enum class BusState 52 | { 53 | IDLE, 54 | COMMUNICATION_INHIBITED, 55 | HOST_REQUEST_TO_SEND, 56 | }; 57 | 58 | void config(UBaseType_t task_priority, BaseType_t task_core); 59 | void begin(BaseType_t core); 60 | int write(unsigned char data); 61 | int write_wait_idle(uint8_t data, uint64_t timeout_micros = 1500); 62 | int read(unsigned char *data, uint64_t timeout_ms = 0); 63 | virtual int reply_to_host(uint8_t host_cmd) = 0; 64 | BusState get_bus_state(); 65 | SemaphoreHandle_t get_bus_mutex_handle(); 66 | QueueHandle_t get_packet_queue_handle(); 67 | int send_packet(PS2Packet *packet); 68 | 69 | protected: 70 | int _ps2clk; 71 | int _ps2data; 72 | UBaseType_t _config_task_priority = DEFAULT_TASK_PRIORITY; 73 | BaseType_t _config_task_core = DEFAULT_TASK_CORE; 74 | TaskHandle_t _task_process_host_request; 75 | TaskHandle_t _task_send_packet; 76 | QueueHandle_t _queue_packet; 77 | SemaphoreHandle_t _mutex_bus; 78 | void golo(int pin); 79 | void gohi(int pin); 80 | void ack(); 81 | }; 82 | 83 | class PS2Mouse : public PS2dev 84 | { 85 | public: 86 | PS2Mouse(int clk, int data); 87 | enum class ResolutionCode : uint8_t 88 | { 89 | RES_1 = 0x00, 90 | RES_2 = 0x01, 91 | RES_4 = 0x02, 92 | RES_8 = 0x03 93 | }; 94 | enum class Scale : uint8_t 95 | { 96 | ONE_ONE = 0, 97 | TWO_ONE = 1 98 | }; 99 | enum class Mode : uint8_t 100 | { 101 | REMOTE_MODE = 0, 102 | STREAM_MODE = 1, 103 | WRAP_MODE = 2 104 | }; 105 | enum class Command : uint8_t 106 | { 107 | RESET = 0xFF, 108 | RESEND = 0xFE, 109 | ERROR = 0xFC, 110 | ACK = 0xFA, 111 | SET_DEFAULTS = 0xF6, 112 | DISABLE_DATA_REPORTING = 0xF5, 113 | ENABLE_DATA_REPORTING = 0xF4, 114 | SET_SAMPLE_RATE = 0xF3, 115 | GET_DEVICE_ID = 0xF2, 116 | SET_REMOTE_MODE = 0xF0, 117 | SET_WRAP_MODE = 0xEE, 118 | RESET_WRAP_MODE = 0xEC, 119 | READ_DATA = 0xEB, 120 | SET_STREAM_MODE = 0xEA, 121 | STATUS_REQUEST = 0xE9, 122 | SET_RESOLUTION = 0xE8, 123 | SET_SCALING_2_1 = 0xE7, 124 | SET_SCALING_1_1 = 0xE6, 125 | SELF_TEST_PASSED = 0xAA, 126 | }; 127 | enum class Button : uint8_t 128 | { 129 | LEFT, 130 | RIGHT, 131 | MIDDLE, 132 | BUTTON_4, 133 | BUTTON_5, 134 | }; 135 | 136 | void begin(bool restore_internal_state); 137 | int reply_to_host(uint8_t host_cmd); 138 | bool has_wheel(); 139 | bool has_4th_and_5th_buttons(); 140 | bool data_reporting_enabled(); 141 | void reset_counter(); 142 | uint8_t get_sample_rate(); 143 | void move(int16_t x, int16_t y, int8_t wheel); 144 | void press(Button button); 145 | void release(Button button); 146 | void click(Button button); 147 | void _report(); 148 | 149 | protected: 150 | static constexpr char const *TAG = "PS2Mouse"; 151 | void _send_status(); 152 | void _save_internal_state_to_nvs(); 153 | void _load_internal_state_from_nvs(); 154 | TaskHandle_t _task_poll_mouse_count; 155 | nvs_handle _nvs_handle; 156 | bool _has_wheel = false; 157 | bool _has_4th_and_5th_buttons = false; 158 | bool _data_reporting_enabled = false; 159 | ResolutionCode _resolution = ResolutionCode::RES_4; 160 | Scale _scale = Scale::ONE_ONE; 161 | Mode _mode = Mode::STREAM_MODE; 162 | Mode _last_mode = Mode::STREAM_MODE; 163 | uint8_t _last_sample_rate[3] = {0, 0, 0}; 164 | uint8_t _sample_rate = 100; 165 | int16_t _count_x = 0; 166 | uint8_t _count_x_overflow = 0; 167 | int16_t _count_y = 0; 168 | uint8_t _count_y_overflow = 0; 169 | int8_t _count_z = 0; 170 | uint8_t _button_left = 0; 171 | uint8_t _button_right = 0; 172 | uint8_t _button_middle = 0; 173 | uint8_t _button_4th = 0; 174 | uint8_t _button_5th = 0; 175 | }; 176 | 177 | class PS2Keyboard : public PS2dev 178 | { 179 | public: 180 | PS2Keyboard(int clk, int data); 181 | int reply_to_host(uint8_t host_cmd); 182 | enum class Command 183 | { 184 | RESET = 0xFF, 185 | RESEND = 0xFE, 186 | ACK = 0xFA, 187 | SET_DEFAULTS = 0xF6, 188 | DISABLE_DATA_REPORTING = 0xF5, 189 | ENABLE_DATA_REPORTING = 0xF4, 190 | SET_TYPEMATIC_RATE = 0xF3, 191 | GET_DEVICE_ID = 0xF2, 192 | SET_SCAN_CODE_SET = 0xF0, 193 | ECHO = 0xEE, 194 | SET_RESET_LEDS = 0xED, 195 | BAT_SUCCESS = 0xAA, 196 | }; 197 | void begin(); 198 | bool data_reporting_enabled(); 199 | bool is_scroll_lock_led_on(); 200 | bool is_num_lock_led_on(); 201 | bool is_caps_lock_led_on(); 202 | void keydown(scancodes::Key key); 203 | void keyup(scancodes::Key key); 204 | void type(scancodes::Key key); 205 | void type(std::initializer_list keys); 206 | void type(const char *str); 207 | void keyHid_send(uint8_t btkey, bool keyDown); 208 | void keyHid_send_CCONTROL(uint16_t btkey, bool keyDown); 209 | 210 | protected: 211 | bool _data_reporting_enabled = true; 212 | bool _led_scroll_lock = false; 213 | bool _led_num_lock = false; 214 | bool _led_caps_lock = false; 215 | }; 216 | 217 | void _taskfn_process_host_request(void *arg); 218 | void _taskfn_send_packet(void *arg); 219 | void _taskfn_poll_mouse_count(void *arg); 220 | 221 | } // namespace esp32_ps2dev 222 | 223 | #endif // __ESP32_PS2DEV_H__ -------------------------------------------------------------------------------- /include/globals.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Guy Turcotte 2 | // 3 | // MIT License. Look at file licenses.txt for details. 4 | 5 | #pragma once 6 | 7 | #include "esp_err.h" 8 | #include "esp_log.h" 9 | #include "esp_system.h" 10 | -------------------------------------------------------------------------------- /include/scan_codes_set_2.h: -------------------------------------------------------------------------------- 1 | #ifndef DD85C2BD_1EA1_416E_B227_80C3C8C3E40A 2 | #define DD85C2BD_1EA1_416E_B227_80C3C8C3E40A 3 | 4 | //#include "Arduino.h" 5 | 6 | // Source: http://www.computer-engineering.org/ps2keyboard/scancodes2.html 7 | // Archive: https://web.archive.org/web/20100225093757/http://www.computer-engineering.org/ps2keyboard/scancodes2.html 8 | 9 | namespace esp32_ps2dev { 10 | 11 | namespace scancodes { 12 | 13 | typedef enum { 14 | K_A, 15 | K_B, 16 | K_C, 17 | K_D, 18 | K_E, 19 | K_F, 20 | K_G, 21 | K_H, 22 | K_I, 23 | K_J, 24 | K_K, 25 | K_L, 26 | K_M, 27 | K_N, 28 | K_O, 29 | K_P, 30 | K_Q, 31 | K_R, 32 | K_S, 33 | K_T, 34 | K_U, 35 | K_V, 36 | K_W, 37 | K_X, 38 | K_Y, 39 | K_Z, 40 | K_0, 41 | K_1, 42 | K_2, 43 | K_3, 44 | K_4, 45 | K_5, 46 | K_6, 47 | K_7, 48 | K_8, 49 | K_9, 50 | K_BACKQUOTE, 51 | K_MINUS, 52 | K_EQUALS, 53 | K_BACKSLASH, 54 | K_BACKSPACE, 55 | K_SPACE, 56 | K_TAB, 57 | K_CAPSLOCK, 58 | K_LSHIFT, 59 | K_LCTRL, 60 | K_LSUPER, 61 | K_LALT, 62 | K_RSHIFT, 63 | K_RCTRL, 64 | K_RSUPER, 65 | K_RALT, 66 | K_MENU, 67 | K_RETURN, 68 | K_ESCAPE, 69 | K_F1, 70 | K_F2, 71 | K_F3, 72 | K_F4, 73 | K_F5, 74 | K_F6, 75 | K_F7, 76 | K_F8, 77 | K_F9, 78 | K_F10, 79 | K_F11, 80 | K_F12, 81 | K_PRINT, 82 | K_SCROLLOCK, 83 | K_PAUSE, 84 | K_LEFTBRACKET, 85 | K_INSERT, 86 | K_HOME, 87 | K_PAGEUP, 88 | K_DELETE, 89 | K_END, 90 | K_PAGEDOWN, 91 | K_UP, 92 | K_LEFT, 93 | K_DOWN, 94 | K_RIGHT, 95 | K_NUMLOCK, 96 | K_KP_DIVIDE, 97 | K_KP_MULTIPLY, 98 | K_KP_MINUS, 99 | K_KP_PLUS, 100 | K_KP_ENTER, 101 | K_KP_PERIOD, 102 | K_KP0, 103 | K_KP1, 104 | K_KP2, 105 | K_KP3, 106 | K_KP4, 107 | K_KP5, 108 | K_KP6, 109 | K_KP7, 110 | K_KP8, 111 | K_KP9, 112 | K_RIGHTBRACKET, 113 | K_SEMICOLON, 114 | K_QUOTE, 115 | K_COMMA, 116 | K_PERIOD, 117 | K_SLASH, 118 | K_ACPI_POWER, 119 | K_ACPI_SLEEP, 120 | K_ACPI_WAKE, 121 | K_MEDIA_NEXT_TRACK, 122 | K_MEDIA_PREV_TRACK, 123 | K_MEDIA_STOP, 124 | K_MEDIA_PLAY_PAUSE, 125 | K_MEDIA_MUTE, 126 | K_MEDIA_VOLUME_UP, 127 | K_MEDIA_VOLUME_DOWN, 128 | K_MEDIA_MEDIA_SELECT, 129 | K_MEDIA_EMAIL, 130 | K_MEDIA_CALC, 131 | K_MEDIA_MY_COMPUTER, 132 | K_MEDIA_WWW_SEARCH, 133 | K_MEDIA_WWW_HOME, 134 | K_MEDIA_WWW_BACK, 135 | K_MEDIA_WWW_FORWARD, 136 | K_MEDIA_WWW_STOP, 137 | K_MEDIA_WWW_REFRESH, 138 | K_MEDIA_WWW_FAVORITES, 139 | } Key; 140 | 141 | const uint8_t MAKE_K_A[] = {0x1C}; 142 | const uint8_t MAKE_K_B[] = {0x32}; 143 | const uint8_t MAKE_K_C[] = {0x21}; 144 | const uint8_t MAKE_K_D[] = {0x23}; 145 | const uint8_t MAKE_K_E[] = {0x24}; 146 | const uint8_t MAKE_K_F[] = {0x2B}; 147 | const uint8_t MAKE_K_G[] = {0x34}; 148 | const uint8_t MAKE_K_H[] = {0x33}; 149 | const uint8_t MAKE_K_I[] = {0x43}; 150 | const uint8_t MAKE_K_J[] = {0x3B}; 151 | const uint8_t MAKE_K_K[] = {0x42}; 152 | const uint8_t MAKE_K_L[] = {0x4B}; 153 | const uint8_t MAKE_K_M[] = {0x3A}; 154 | const uint8_t MAKE_K_N[] = {0x31}; 155 | const uint8_t MAKE_K_O[] = {0x44}; 156 | const uint8_t MAKE_K_P[] = {0x4D}; 157 | const uint8_t MAKE_K_Q[] = {0x15}; 158 | const uint8_t MAKE_K_R[] = {0x2D}; 159 | const uint8_t MAKE_K_S[] = {0x1B}; 160 | const uint8_t MAKE_K_T[] = {0x2C}; 161 | const uint8_t MAKE_K_U[] = {0x3C}; 162 | const uint8_t MAKE_K_V[] = {0x2A}; 163 | const uint8_t MAKE_K_W[] = {0x1D}; 164 | const uint8_t MAKE_K_X[] = {0x22}; 165 | const uint8_t MAKE_K_Y[] = {0x35}; 166 | const uint8_t MAKE_K_Z[] = {0x1A}; 167 | const uint8_t MAKE_K_0[] = {0x45}; 168 | const uint8_t MAKE_K_1[] = {0x16}; 169 | const uint8_t MAKE_K_2[] = {0x1E}; 170 | const uint8_t MAKE_K_3[] = {0x26}; 171 | const uint8_t MAKE_K_4[] = {0x25}; 172 | const uint8_t MAKE_K_5[] = {0x2E}; 173 | const uint8_t MAKE_K_6[] = {0x36}; 174 | const uint8_t MAKE_K_7[] = {0x3D}; 175 | const uint8_t MAKE_K_8[] = {0x3E}; 176 | const uint8_t MAKE_K_9[] = {0x46}; 177 | const uint8_t MAKE_K_BACKQUOTE[] = {0x0E}; 178 | const uint8_t MAKE_K_MINUS[] = {0x4E}; 179 | const uint8_t MAKE_K_EQUALS[] = {0x55}; 180 | const uint8_t MAKE_K_BACKSLASH[] = {0x5D}; 181 | const uint8_t MAKE_K_BACKSPACE[] = {0x66}; 182 | const uint8_t MAKE_K_SPACE[] = {0x29}; 183 | const uint8_t MAKE_K_TAB[] = {0x0D}; 184 | const uint8_t MAKE_K_CAPSLOCK[] = {0x58}; 185 | const uint8_t MAKE_K_LSHIFT[] = {0x12}; 186 | const uint8_t MAKE_K_LCTRL[] = {0x14}; 187 | const uint8_t MAKE_K_LSUPER[] = {0xE0, 0x1F}; 188 | const uint8_t MAKE_K_LALT[] = {0x11}; 189 | const uint8_t MAKE_K_RSHIFT[] = {0x59}; 190 | const uint8_t MAKE_K_RCTRL[] = {0xE0, 0x14}; 191 | const uint8_t MAKE_K_RSUPER[] = {0xE0, 0x27}; 192 | const uint8_t MAKE_K_RALT[] = {0xE0, 0x11}; 193 | const uint8_t MAKE_K_MENU[] = {0xE0, 0x2F}; 194 | const uint8_t MAKE_K_RETURN[] = {0x5A}; 195 | const uint8_t MAKE_K_ESCAPE[] = {0x76}; 196 | const uint8_t MAKE_K_F1[] = {0x05}; 197 | const uint8_t MAKE_K_F2[] = {0x06}; 198 | const uint8_t MAKE_K_F3[] = {0x04}; 199 | const uint8_t MAKE_K_F4[] = {0x0C}; 200 | const uint8_t MAKE_K_F5[] = {0x03}; 201 | const uint8_t MAKE_K_F6[] = {0x0B}; 202 | const uint8_t MAKE_K_F7[] = {0x83}; 203 | const uint8_t MAKE_K_F8[] = {0x0A}; 204 | const uint8_t MAKE_K_F9[] = {0x01}; 205 | const uint8_t MAKE_K_F10[] = {0x09}; 206 | const uint8_t MAKE_K_F11[] = {0x78}; 207 | const uint8_t MAKE_K_F12[] = {0x07}; 208 | const uint8_t MAKE_K_PRINT[] = {0xE0, 0x12, 0xE0, 0x7C}; 209 | const uint8_t MAKE_K_SCROLLOCK[] = {0x7E}; 210 | const uint8_t MAKE_K_PAUSE[] = {0xE1, 0x14, 0x77, 0xE1, 0xF0, 0x14, 0xF0, 0x77}; 211 | const uint8_t MAKE_K_LEFTBRACKET[] = {0x54}; 212 | const uint8_t MAKE_K_INSERT[] = {0xE0, 0x70}; 213 | const uint8_t MAKE_K_HOME[] = {0xE0, 0x6C}; 214 | const uint8_t MAKE_K_PAGEUP[] = {0xE0, 0x7D}; 215 | const uint8_t MAKE_K_DELETE[] = {0xE0, 0x71}; 216 | const uint8_t MAKE_K_END[] = {0xE0, 0x69}; 217 | const uint8_t MAKE_K_PAGEDOWN[] = {0xE0, 0x7A}; 218 | const uint8_t MAKE_K_UP[] = {0xE0, 0x75}; 219 | const uint8_t MAKE_K_LEFT[] = {0xE0, 0x6B}; 220 | const uint8_t MAKE_K_DOWN[] = {0xE0, 0x72}; 221 | const uint8_t MAKE_K_RIGHT[] = {0xE0, 0x74}; 222 | const uint8_t MAKE_K_NUMLOCK[] = {0x77}; 223 | const uint8_t MAKE_K_KP_DIVIDE[] = {0xE0, 0x4A}; 224 | const uint8_t MAKE_K_KP_MULTIPLY[] = {0x7C}; 225 | const uint8_t MAKE_K_KP_MINUS[] = {0x7B}; 226 | const uint8_t MAKE_K_KP_PLUS[] = {0x79}; 227 | const uint8_t MAKE_K_KP_ENTER[] = {0xE0, 0x5A}; 228 | const uint8_t MAKE_K_KP_PERIOD[] = {0x71}; 229 | const uint8_t MAKE_K_KP0[] = {0x70}; 230 | const uint8_t MAKE_K_KP1[] = {0x69}; 231 | const uint8_t MAKE_K_KP2[] = {0x72}; 232 | const uint8_t MAKE_K_KP3[] = {0x7A}; 233 | const uint8_t MAKE_K_KP4[] = {0x6B}; 234 | const uint8_t MAKE_K_KP5[] = {0x73}; 235 | const uint8_t MAKE_K_KP6[] = {0x74}; 236 | const uint8_t MAKE_K_KP7[] = {0x6C}; 237 | const uint8_t MAKE_K_KP8[] = {0x75}; 238 | const uint8_t MAKE_K_KP9[] = {0x7D}; 239 | const uint8_t MAKE_K_RIGHTBRACKET[] = {0x5B}; 240 | const uint8_t MAKE_K_SEMICOLON[] = {0x4C}; 241 | const uint8_t MAKE_K_QUOTE[] = {0x52}; 242 | const uint8_t MAKE_K_COMMA[] = {0x41}; 243 | const uint8_t MAKE_K_PERIOD[] = {0x49}; 244 | const uint8_t MAKE_K_SLASH[] = {0x4A}; 245 | const uint8_t MAKE_K_ACPI_POWER[] = {0xE0, 0x37}; 246 | const uint8_t MAKE_K_ACPI_SLEEP[] = {0xE0, 0x3F}; 247 | const uint8_t MAKE_K_ACPI_WAKE[] = {0xE0, 0x5E}; 248 | const uint8_t MAKE_K_MEDIA_NEXT_TRACK[] = {0xE0, 0x4D}; 249 | const uint8_t MAKE_K_MEDIA_PREV_TRACK[] = {0xE0, 0x15}; 250 | const uint8_t MAKE_K_MEDIA_STOP[] = {0xE0, 0x3B}; 251 | const uint8_t MAKE_K_MEDIA_PLAY_PAUSE[] = {0xE0, 0x34}; 252 | const uint8_t MAKE_K_MEDIA_MUTE[] = {0xE0, 0x23}; 253 | const uint8_t MAKE_K_MEDIA_VOLUME_UP[] = {0xE0, 0x32}; 254 | const uint8_t MAKE_K_MEDIA_VOLUME_DOWN[] = {0xE0, 0x21}; 255 | const uint8_t MAKE_K_MEDIA_MEDIA_SELECT[] = {0xE0, 0x50}; 256 | const uint8_t MAKE_K_MEDIA_EMAIL[] = {0xE0, 0x48}; 257 | const uint8_t MAKE_K_MEDIA_CALC[] = {0xE0, 0x2B}; 258 | const uint8_t MAKE_K_MEDIA_MY_COMPUTER[] = {0xE0, 0x40}; 259 | const uint8_t MAKE_K_MEDIA_WWW_SEARCH[] = {0xE0, 0x10}; 260 | const uint8_t MAKE_K_MEDIA_WWW_HOME[] = {0xE0, 0x3A}; 261 | const uint8_t MAKE_K_MEDIA_WWW_BACK[] = {0xE0, 0x38}; 262 | const uint8_t MAKE_K_MEDIA_WWW_FORWARD[] = {0xE0, 0x30}; 263 | const uint8_t MAKE_K_MEDIA_WWW_STOP[] = {0xE0, 0x28}; 264 | const uint8_t MAKE_K_MEDIA_WWW_REFRESH[] = {0xE0, 0x20}; 265 | const uint8_t MAKE_K_MEDIA_WWW_FAVORITES[] = {0xE0, 0x18}; 266 | 267 | const uint8_t BREAK_K_A[] = {0xF0, 0x1C}; 268 | const uint8_t BREAK_K_B[] = {0xF0, 0x32}; 269 | const uint8_t BREAK_K_C[] = {0xF0, 0x21}; 270 | const uint8_t BREAK_K_D[] = {0xF0, 0x23}; 271 | const uint8_t BREAK_K_E[] = {0xF0, 0x24}; 272 | const uint8_t BREAK_K_F[] = {0xF0, 0x2B}; 273 | const uint8_t BREAK_K_G[] = {0xF0, 0x34}; 274 | const uint8_t BREAK_K_H[] = {0xF0, 0x33}; 275 | const uint8_t BREAK_K_I[] = {0xF0, 0x43}; 276 | const uint8_t BREAK_K_J[] = {0xF0, 0x3B}; 277 | const uint8_t BREAK_K_K[] = {0xF0, 0x42}; 278 | const uint8_t BREAK_K_L[] = {0xF0, 0x4B}; 279 | const uint8_t BREAK_K_M[] = {0xF0, 0x3A}; 280 | const uint8_t BREAK_K_N[] = {0xF0, 0x31}; 281 | const uint8_t BREAK_K_O[] = {0xF0, 0x44}; 282 | const uint8_t BREAK_K_P[] = {0xF0, 0x4D}; 283 | const uint8_t BREAK_K_Q[] = {0xF0, 0x15}; 284 | const uint8_t BREAK_K_R[] = {0xF0, 0x2D}; 285 | const uint8_t BREAK_K_S[] = {0xF0, 0x1B}; 286 | const uint8_t BREAK_K_T[] = {0xF0, 0x2C}; 287 | const uint8_t BREAK_K_U[] = {0xF0, 0x3C}; 288 | const uint8_t BREAK_K_V[] = {0xF0, 0x2A}; 289 | const uint8_t BREAK_K_W[] = {0xF0, 0x1D}; 290 | const uint8_t BREAK_K_X[] = {0xF0, 0x22}; 291 | const uint8_t BREAK_K_Y[] = {0xF0, 0x35}; 292 | const uint8_t BREAK_K_Z[] = {0xF0, 0x1A}; 293 | const uint8_t BREAK_K_0[] = {0xF0, 0x45}; 294 | const uint8_t BREAK_K_1[] = {0xF0, 0x16}; 295 | const uint8_t BREAK_K_2[] = {0xF0, 0x1E}; 296 | const uint8_t BREAK_K_3[] = {0xF0, 0x26}; 297 | const uint8_t BREAK_K_4[] = {0xF0, 0x25}; 298 | const uint8_t BREAK_K_5[] = {0xF0, 0x2E}; 299 | const uint8_t BREAK_K_6[] = {0xF0, 0x36}; 300 | const uint8_t BREAK_K_7[] = {0xF0, 0x3D}; 301 | const uint8_t BREAK_K_8[] = {0xF0, 0x3E}; 302 | const uint8_t BREAK_K_9[] = {0xF0, 0x46}; 303 | const uint8_t BREAK_K_BACKQUOTE[] = {0xF0, 0x0E}; 304 | const uint8_t BREAK_K_MINUS[] = {0xF0, 0x4E}; 305 | const uint8_t BREAK_K_EQUALS[] = {0xF0, 0x55}; 306 | const uint8_t BREAK_K_BACKSLASH[] = {0xF0, 0x5D}; 307 | const uint8_t BREAK_K_BACKSPACE[] = {0xF0, 0x66}; 308 | const uint8_t BREAK_K_SPACE[] = {0xF0, 0x29}; 309 | const uint8_t BREAK_K_TAB[] = {0xF0, 0x0D}; 310 | const uint8_t BREAK_K_CAPSLOCK[] = {0xF0, 0x58}; 311 | const uint8_t BREAK_K_LSHIFT[] = {0xF0, 0x12}; 312 | const uint8_t BREAK_K_LCTRL[] = {0xF0, 0x14}; 313 | const uint8_t BREAK_K_LSUPER[] = {0xE0, 0xF0, 0x1F}; 314 | const uint8_t BREAK_K_LALT[] = {0xF0, 0x11}; 315 | const uint8_t BREAK_K_RSHIFT[] = {0xF0, 0x59}; 316 | const uint8_t BREAK_K_RCTRL[] = {0xE0, 0xF0, 0x14}; 317 | const uint8_t BREAK_K_RSUPER[] = {0xE0, 0xF0, 0x27}; 318 | const uint8_t BREAK_K_RALT[] = {0xE0, 0xF0, 0x11}; 319 | const uint8_t BREAK_K_MENU[] = {0xE0, 0xF0, 0x2F}; 320 | const uint8_t BREAK_K_RETURN[] = {0xF0, 0x5A}; 321 | const uint8_t BREAK_K_ESCAPE[] = {0xF0, 0x76}; 322 | const uint8_t BREAK_K_F1[] = {0xF0, 0x05}; 323 | const uint8_t BREAK_K_F2[] = {0xF0, 0x06}; 324 | const uint8_t BREAK_K_F3[] = {0xF0, 0x04}; 325 | const uint8_t BREAK_K_F4[] = {0xF0, 0x0C}; 326 | const uint8_t BREAK_K_F5[] = {0xF0, 0x03}; 327 | const uint8_t BREAK_K_F6[] = {0xF0, 0x0B}; 328 | const uint8_t BREAK_K_F7[] = {0xF0, 0x83}; 329 | const uint8_t BREAK_K_F8[] = {0xF0, 0x0A}; 330 | const uint8_t BREAK_K_F9[] = {0xF0, 0x01}; 331 | const uint8_t BREAK_K_F10[] = {0xF0, 0x09}; 332 | const uint8_t BREAK_K_F11[] = {0xF0, 0x78}; 333 | const uint8_t BREAK_K_F12[] = {0xF0, 0x07}; 334 | const uint8_t BREAK_K_PRINT[] = {0xE0, 0xF0, 0x7C, 0xE0, 0xF0, 0x12}; 335 | const uint8_t BREAK_K_SCROLLOCK[] = {0xF0, 0x7E}; 336 | const uint8_t BREAK_K_PAUSE[] = {}; 337 | const uint8_t BREAK_K_LEFTBRACKET[] = {0xF0, 0x54}; 338 | const uint8_t BREAK_K_INSERT[] = {0xE0, 0xF0, 0x70}; 339 | const uint8_t BREAK_K_HOME[] = {0xE0, 0xF0, 0x6C}; 340 | const uint8_t BREAK_K_PAGEUP[] = {0xE0, 0xF0, 0x7D}; 341 | const uint8_t BREAK_K_DELETE[] = {0xE0, 0xF0, 0x71}; 342 | const uint8_t BREAK_K_END[] = {0xE0, 0xF0, 0x69}; 343 | const uint8_t BREAK_K_PAGEDOWN[] = {0xE0, 0xF0, 0x7A}; 344 | const uint8_t BREAK_K_UP[] = {0xE0, 0xF0, 0x75}; 345 | const uint8_t BREAK_K_LEFT[] = {0xE0, 0xF0, 0x6B}; 346 | const uint8_t BREAK_K_DOWN[] = {0xE0, 0xF0, 0x72}; 347 | const uint8_t BREAK_K_RIGHT[] = {0xE0, 0xF0, 0x74}; 348 | const uint8_t BREAK_K_NUMLOCK[] = {0xF0, 0x77}; 349 | const uint8_t BREAK_K_KP_DIVIDE[] = {0xE0, 0xF0, 0x4A}; 350 | const uint8_t BREAK_K_KP_MULTIPLY[] = {0xF0, 0x7C}; 351 | const uint8_t BREAK_K_KP_MINUS[] = {0xF0, 0x7B}; 352 | const uint8_t BREAK_K_KP_PLUS[] = {0xF0, 0x79}; 353 | const uint8_t BREAK_K_KP_ENTER[] = {0xE0, 0xF0, 0x5A}; 354 | const uint8_t BREAK_K_KP_PERIOD[] = {0xF0, 0x71}; 355 | const uint8_t BREAK_K_KP0[] = {0xF0, 0x70}; 356 | const uint8_t BREAK_K_KP1[] = {0xF0, 0x69}; 357 | const uint8_t BREAK_K_KP2[] = {0xF0, 0x72}; 358 | const uint8_t BREAK_K_KP3[] = {0xF0, 0x7A}; 359 | const uint8_t BREAK_K_KP4[] = {0xF0, 0x6B}; 360 | const uint8_t BREAK_K_KP5[] = {0xF0, 0x73}; 361 | const uint8_t BREAK_K_KP6[] = {0xF0, 0x74}; 362 | const uint8_t BREAK_K_KP7[] = {0xF0, 0x6C}; 363 | const uint8_t BREAK_K_KP8[] = {0xF0, 0x75}; 364 | const uint8_t BREAK_K_KP9[] = {0xF0, 0x7D}; 365 | const uint8_t BREAK_K_RIGHTBRACKET[] = {0xF0, 0x5B}; 366 | const uint8_t BREAK_K_SEMICOLON[] = {0xF0, 0x4C}; 367 | const uint8_t BREAK_K_QUOTE[] = {0xF0, 0x52}; 368 | const uint8_t BREAK_K_COMMA[] = {0xF0, 0x41}; 369 | const uint8_t BREAK_K_PERIOD[] = {0xF0, 0x49}; 370 | const uint8_t BREAK_K_SLASH[] = {0xF0, 0x4A}; 371 | const uint8_t BREAK_K_ACPI_POWER[] = {0xE0, 0xF0, 0x37}; 372 | const uint8_t BREAK_K_ACPI_SLEEP[] = {0xE0, 0xF0, 0x3F}; 373 | const uint8_t BREAK_K_ACPI_WAKE[] = {0xE0, 0xF0, 0x5E}; 374 | const uint8_t BREAK_K_MEDIA_NEXT_TRACK[] = {0xE0, 0xF0, 0x4D}; 375 | const uint8_t BREAK_K_MEDIA_PREV_TRACK[] = {0xE0, 0xF0, 0x15}; 376 | const uint8_t BREAK_K_MEDIA_STOP[] = {0xE0, 0xF0, 0x3B}; 377 | const uint8_t BREAK_K_MEDIA_PLAY_PAUSE[] = {0xE0, 0xF0, 0x34}; 378 | const uint8_t BREAK_K_MEDIA_MUTE[] = {0xE0, 0xF0, 0x23}; 379 | const uint8_t BREAK_K_MEDIA_VOLUME_UP[] = {0xE0, 0xF0, 0x32}; 380 | const uint8_t BREAK_K_MEDIA_VOLUME_DOWN[] = {0xE0, 0xF0, 0x21}; 381 | const uint8_t BREAK_K_MEDIA_MEDIA_SELECT[] = {0xE0, 0xF0, 0x50}; 382 | const uint8_t BREAK_K_MEDIA_EMAIL[] = {0xE0, 0xF0, 0x48}; 383 | const uint8_t BREAK_K_MEDIA_CALC[] = {0xE0, 0xF0, 0x2B}; 384 | const uint8_t BREAK_K_MEDIA_MY_COMPUTER[] = {0xE0, 0xF0, 0x40}; 385 | const uint8_t BREAK_K_MEDIA_WWW_SEARCH[] = {0xE0, 0xF0, 0x10}; 386 | const uint8_t BREAK_K_MEDIA_WWW_HOME[] = {0xE0, 0xF0, 0x3A}; 387 | const uint8_t BREAK_K_MEDIA_WWW_BACK[] = {0xE0, 0xF0, 0x38}; 388 | const uint8_t BREAK_K_MEDIA_WWW_FORWARD[] = {0xE0, 0xF0, 0x30}; 389 | const uint8_t BREAK_K_MEDIA_WWW_STOP[] = {0xE0, 0xF0, 0x28}; 390 | const uint8_t BREAK_K_MEDIA_WWW_REFRESH[] = {0xE0, 0xF0, 0x20}; 391 | const uint8_t BREAK_K_MEDIA_WWW_FAVORITES[] = {0xE0, 0xF0, 0x18}; 392 | 393 | const uint8_t* const MAKE_CODES[] = {MAKE_K_A, 394 | MAKE_K_B, 395 | MAKE_K_C, 396 | MAKE_K_D, 397 | MAKE_K_E, 398 | MAKE_K_F, 399 | MAKE_K_G, 400 | MAKE_K_H, 401 | MAKE_K_I, 402 | MAKE_K_J, 403 | MAKE_K_K, 404 | MAKE_K_L, 405 | MAKE_K_M, 406 | MAKE_K_N, 407 | MAKE_K_O, 408 | MAKE_K_P, 409 | MAKE_K_Q, 410 | MAKE_K_R, 411 | MAKE_K_S, 412 | MAKE_K_T, 413 | MAKE_K_U, 414 | MAKE_K_V, 415 | MAKE_K_W, 416 | MAKE_K_X, 417 | MAKE_K_Y, 418 | MAKE_K_Z, 419 | MAKE_K_0, 420 | MAKE_K_1, 421 | MAKE_K_2, 422 | MAKE_K_3, 423 | MAKE_K_4, 424 | MAKE_K_5, 425 | MAKE_K_6, 426 | MAKE_K_7, 427 | MAKE_K_8, 428 | MAKE_K_9, 429 | MAKE_K_BACKQUOTE, 430 | MAKE_K_MINUS, 431 | MAKE_K_EQUALS, 432 | MAKE_K_BACKSLASH, 433 | MAKE_K_BACKSPACE, 434 | MAKE_K_SPACE, 435 | MAKE_K_TAB, 436 | MAKE_K_CAPSLOCK, 437 | MAKE_K_LSHIFT, 438 | MAKE_K_LCTRL, 439 | MAKE_K_LSUPER, 440 | MAKE_K_LALT, 441 | MAKE_K_RSHIFT, 442 | MAKE_K_RCTRL, 443 | MAKE_K_RSUPER, 444 | MAKE_K_RALT, 445 | MAKE_K_MENU, 446 | MAKE_K_RETURN, 447 | MAKE_K_ESCAPE, 448 | MAKE_K_F1, 449 | MAKE_K_F2, 450 | MAKE_K_F3, 451 | MAKE_K_F4, 452 | MAKE_K_F5, 453 | MAKE_K_F6, 454 | MAKE_K_F7, 455 | MAKE_K_F8, 456 | MAKE_K_F9, 457 | MAKE_K_F10, 458 | MAKE_K_F11, 459 | MAKE_K_F12, 460 | MAKE_K_PRINT, 461 | MAKE_K_SCROLLOCK, 462 | MAKE_K_PAUSE, 463 | MAKE_K_LEFTBRACKET, 464 | MAKE_K_INSERT, 465 | MAKE_K_HOME, 466 | MAKE_K_PAGEUP, 467 | MAKE_K_DELETE, 468 | MAKE_K_END, 469 | MAKE_K_PAGEDOWN, 470 | MAKE_K_UP, 471 | MAKE_K_LEFT, 472 | MAKE_K_DOWN, 473 | MAKE_K_RIGHT, 474 | MAKE_K_NUMLOCK, 475 | MAKE_K_KP_DIVIDE, 476 | MAKE_K_KP_MULTIPLY, 477 | MAKE_K_KP_MINUS, 478 | MAKE_K_KP_PLUS, 479 | MAKE_K_KP_ENTER, 480 | MAKE_K_KP_PERIOD, 481 | MAKE_K_KP0, 482 | MAKE_K_KP1, 483 | MAKE_K_KP2, 484 | MAKE_K_KP3, 485 | MAKE_K_KP4, 486 | MAKE_K_KP5, 487 | MAKE_K_KP6, 488 | MAKE_K_KP7, 489 | MAKE_K_KP8, 490 | MAKE_K_KP9, 491 | MAKE_K_RIGHTBRACKET, 492 | MAKE_K_SEMICOLON, 493 | MAKE_K_QUOTE, 494 | MAKE_K_COMMA, 495 | MAKE_K_PERIOD, 496 | MAKE_K_SLASH, 497 | MAKE_K_ACPI_POWER, 498 | MAKE_K_ACPI_SLEEP, 499 | MAKE_K_ACPI_WAKE, 500 | MAKE_K_MEDIA_NEXT_TRACK, 501 | MAKE_K_MEDIA_PREV_TRACK, 502 | MAKE_K_MEDIA_STOP, 503 | MAKE_K_MEDIA_PLAY_PAUSE, 504 | MAKE_K_MEDIA_MUTE, 505 | MAKE_K_MEDIA_VOLUME_UP, 506 | MAKE_K_MEDIA_VOLUME_DOWN, 507 | MAKE_K_MEDIA_MEDIA_SELECT, 508 | MAKE_K_MEDIA_EMAIL, 509 | MAKE_K_MEDIA_CALC, 510 | MAKE_K_MEDIA_MY_COMPUTER, 511 | MAKE_K_MEDIA_WWW_SEARCH, 512 | MAKE_K_MEDIA_WWW_HOME, 513 | MAKE_K_MEDIA_WWW_BACK, 514 | MAKE_K_MEDIA_WWW_FORWARD, 515 | MAKE_K_MEDIA_WWW_STOP, 516 | MAKE_K_MEDIA_WWW_REFRESH, 517 | MAKE_K_MEDIA_WWW_FAVORITES}; 518 | 519 | const uint8_t MAKE_CODES_LEN[] = {sizeof(MAKE_K_A), 520 | sizeof(MAKE_K_B), 521 | sizeof(MAKE_K_C), 522 | sizeof(MAKE_K_D), 523 | sizeof(MAKE_K_E), 524 | sizeof(MAKE_K_F), 525 | sizeof(MAKE_K_G), 526 | sizeof(MAKE_K_H), 527 | sizeof(MAKE_K_I), 528 | sizeof(MAKE_K_J), 529 | sizeof(MAKE_K_K), 530 | sizeof(MAKE_K_L), 531 | sizeof(MAKE_K_M), 532 | sizeof(MAKE_K_N), 533 | sizeof(MAKE_K_O), 534 | sizeof(MAKE_K_P), 535 | sizeof(MAKE_K_Q), 536 | sizeof(MAKE_K_R), 537 | sizeof(MAKE_K_S), 538 | sizeof(MAKE_K_T), 539 | sizeof(MAKE_K_U), 540 | sizeof(MAKE_K_V), 541 | sizeof(MAKE_K_W), 542 | sizeof(MAKE_K_X), 543 | sizeof(MAKE_K_Y), 544 | sizeof(MAKE_K_Z), 545 | sizeof(MAKE_K_0), 546 | sizeof(MAKE_K_1), 547 | sizeof(MAKE_K_2), 548 | sizeof(MAKE_K_3), 549 | sizeof(MAKE_K_4), 550 | sizeof(MAKE_K_5), 551 | sizeof(MAKE_K_6), 552 | sizeof(MAKE_K_7), 553 | sizeof(MAKE_K_8), 554 | sizeof(MAKE_K_9), 555 | sizeof(MAKE_K_BACKQUOTE), 556 | sizeof(MAKE_K_MINUS), 557 | sizeof(MAKE_K_EQUALS), 558 | sizeof(MAKE_K_BACKSLASH), 559 | sizeof(MAKE_K_BACKSPACE), 560 | sizeof(MAKE_K_SPACE), 561 | sizeof(MAKE_K_TAB), 562 | sizeof(MAKE_K_CAPSLOCK), 563 | sizeof(MAKE_K_LSHIFT), 564 | sizeof(MAKE_K_LCTRL), 565 | sizeof(MAKE_K_LSUPER), 566 | sizeof(MAKE_K_LALT), 567 | sizeof(MAKE_K_RSHIFT), 568 | sizeof(MAKE_K_RCTRL), 569 | sizeof(MAKE_K_RSUPER), 570 | sizeof(MAKE_K_RALT), 571 | sizeof(MAKE_K_MENU), 572 | sizeof(MAKE_K_RETURN), 573 | sizeof(MAKE_K_ESCAPE), 574 | sizeof(MAKE_K_F1), 575 | sizeof(MAKE_K_F2), 576 | sizeof(MAKE_K_F3), 577 | sizeof(MAKE_K_F4), 578 | sizeof(MAKE_K_F5), 579 | sizeof(MAKE_K_F6), 580 | sizeof(MAKE_K_F7), 581 | sizeof(MAKE_K_F8), 582 | sizeof(MAKE_K_F9), 583 | sizeof(MAKE_K_F10), 584 | sizeof(MAKE_K_F11), 585 | sizeof(MAKE_K_F12), 586 | sizeof(MAKE_K_PRINT), 587 | sizeof(MAKE_K_SCROLLOCK), 588 | sizeof(MAKE_K_PAUSE), 589 | sizeof(MAKE_K_LEFTBRACKET), 590 | sizeof(MAKE_K_INSERT), 591 | sizeof(MAKE_K_HOME), 592 | sizeof(MAKE_K_PAGEUP), 593 | sizeof(MAKE_K_DELETE), 594 | sizeof(MAKE_K_END), 595 | sizeof(MAKE_K_PAGEDOWN), 596 | sizeof(MAKE_K_UP), 597 | sizeof(MAKE_K_LEFT), 598 | sizeof(MAKE_K_DOWN), 599 | sizeof(MAKE_K_RIGHT), 600 | sizeof(MAKE_K_NUMLOCK), 601 | sizeof(MAKE_K_KP_DIVIDE), 602 | sizeof(MAKE_K_KP_MULTIPLY), 603 | sizeof(MAKE_K_KP_MINUS), 604 | sizeof(MAKE_K_KP_PLUS), 605 | sizeof(MAKE_K_KP_ENTER), 606 | sizeof(MAKE_K_KP_PERIOD), 607 | sizeof(MAKE_K_KP0), 608 | sizeof(MAKE_K_KP1), 609 | sizeof(MAKE_K_KP2), 610 | sizeof(MAKE_K_KP3), 611 | sizeof(MAKE_K_KP4), 612 | sizeof(MAKE_K_KP5), 613 | sizeof(MAKE_K_KP6), 614 | sizeof(MAKE_K_KP7), 615 | sizeof(MAKE_K_KP8), 616 | sizeof(MAKE_K_KP9), 617 | sizeof(MAKE_K_RIGHTBRACKET), 618 | sizeof(MAKE_K_SEMICOLON), 619 | sizeof(MAKE_K_QUOTE), 620 | sizeof(MAKE_K_COMMA), 621 | sizeof(MAKE_K_PERIOD), 622 | sizeof(MAKE_K_SLASH), 623 | sizeof(MAKE_K_ACPI_POWER), 624 | sizeof(MAKE_K_ACPI_SLEEP), 625 | sizeof(MAKE_K_ACPI_WAKE), 626 | sizeof(MAKE_K_MEDIA_NEXT_TRACK), 627 | sizeof(MAKE_K_MEDIA_PREV_TRACK), 628 | sizeof(MAKE_K_MEDIA_STOP), 629 | sizeof(MAKE_K_MEDIA_PLAY_PAUSE), 630 | sizeof(MAKE_K_MEDIA_MUTE), 631 | sizeof(MAKE_K_MEDIA_VOLUME_UP), 632 | sizeof(MAKE_K_MEDIA_VOLUME_DOWN), 633 | sizeof(MAKE_K_MEDIA_MEDIA_SELECT), 634 | sizeof(MAKE_K_MEDIA_EMAIL), 635 | sizeof(MAKE_K_MEDIA_CALC), 636 | sizeof(MAKE_K_MEDIA_MY_COMPUTER), 637 | sizeof(MAKE_K_MEDIA_WWW_SEARCH), 638 | sizeof(MAKE_K_MEDIA_WWW_HOME), 639 | sizeof(MAKE_K_MEDIA_WWW_BACK), 640 | sizeof(MAKE_K_MEDIA_WWW_FORWARD), 641 | sizeof(MAKE_K_MEDIA_WWW_STOP), 642 | sizeof(MAKE_K_MEDIA_WWW_REFRESH), 643 | sizeof(MAKE_K_MEDIA_WWW_FAVORITES)}; 644 | 645 | const uint8_t* const BREAK_CODES[] = {BREAK_K_A, 646 | BREAK_K_B, 647 | BREAK_K_C, 648 | BREAK_K_D, 649 | BREAK_K_E, 650 | BREAK_K_F, 651 | BREAK_K_G, 652 | BREAK_K_H, 653 | BREAK_K_I, 654 | BREAK_K_J, 655 | BREAK_K_K, 656 | BREAK_K_L, 657 | BREAK_K_M, 658 | BREAK_K_N, 659 | BREAK_K_O, 660 | BREAK_K_P, 661 | BREAK_K_Q, 662 | BREAK_K_R, 663 | BREAK_K_S, 664 | BREAK_K_T, 665 | BREAK_K_U, 666 | BREAK_K_V, 667 | BREAK_K_W, 668 | BREAK_K_X, 669 | BREAK_K_Y, 670 | BREAK_K_Z, 671 | BREAK_K_0, 672 | BREAK_K_1, 673 | BREAK_K_2, 674 | BREAK_K_3, 675 | BREAK_K_4, 676 | BREAK_K_5, 677 | BREAK_K_6, 678 | BREAK_K_7, 679 | BREAK_K_8, 680 | BREAK_K_9, 681 | BREAK_K_BACKQUOTE, 682 | BREAK_K_MINUS, 683 | BREAK_K_EQUALS, 684 | BREAK_K_BACKSLASH, 685 | BREAK_K_BACKSPACE, 686 | BREAK_K_SPACE, 687 | BREAK_K_TAB, 688 | BREAK_K_CAPSLOCK, 689 | BREAK_K_LSHIFT, 690 | BREAK_K_LCTRL, 691 | BREAK_K_LSUPER, 692 | BREAK_K_LALT, 693 | BREAK_K_RSHIFT, 694 | BREAK_K_RCTRL, 695 | BREAK_K_RSUPER, 696 | BREAK_K_RALT, 697 | BREAK_K_MENU, 698 | BREAK_K_RETURN, 699 | BREAK_K_ESCAPE, 700 | BREAK_K_F1, 701 | BREAK_K_F2, 702 | BREAK_K_F3, 703 | BREAK_K_F4, 704 | BREAK_K_F5, 705 | BREAK_K_F6, 706 | BREAK_K_F7, 707 | BREAK_K_F8, 708 | BREAK_K_F9, 709 | BREAK_K_F10, 710 | BREAK_K_F11, 711 | BREAK_K_F12, 712 | BREAK_K_PRINT, 713 | BREAK_K_SCROLLOCK, 714 | BREAK_K_PAUSE, 715 | BREAK_K_LEFTBRACKET, 716 | BREAK_K_INSERT, 717 | BREAK_K_HOME, 718 | BREAK_K_PAGEUP, 719 | BREAK_K_DELETE, 720 | BREAK_K_END, 721 | BREAK_K_PAGEDOWN, 722 | BREAK_K_UP, 723 | BREAK_K_LEFT, 724 | BREAK_K_DOWN, 725 | BREAK_K_RIGHT, 726 | BREAK_K_NUMLOCK, 727 | BREAK_K_KP_DIVIDE, 728 | BREAK_K_KP_MULTIPLY, 729 | BREAK_K_KP_MINUS, 730 | BREAK_K_KP_PLUS, 731 | BREAK_K_KP_ENTER, 732 | BREAK_K_KP_PERIOD, 733 | BREAK_K_KP0, 734 | BREAK_K_KP1, 735 | BREAK_K_KP2, 736 | BREAK_K_KP3, 737 | BREAK_K_KP4, 738 | BREAK_K_KP5, 739 | BREAK_K_KP6, 740 | BREAK_K_KP7, 741 | BREAK_K_KP8, 742 | BREAK_K_KP9, 743 | BREAK_K_RIGHTBRACKET, 744 | BREAK_K_SEMICOLON, 745 | BREAK_K_QUOTE, 746 | BREAK_K_COMMA, 747 | BREAK_K_PERIOD, 748 | BREAK_K_SLASH, 749 | BREAK_K_ACPI_POWER, 750 | BREAK_K_ACPI_SLEEP, 751 | BREAK_K_ACPI_WAKE, 752 | BREAK_K_MEDIA_NEXT_TRACK, 753 | BREAK_K_MEDIA_PREV_TRACK, 754 | BREAK_K_MEDIA_STOP, 755 | BREAK_K_MEDIA_PLAY_PAUSE, 756 | BREAK_K_MEDIA_MUTE, 757 | BREAK_K_MEDIA_VOLUME_UP, 758 | BREAK_K_MEDIA_VOLUME_DOWN, 759 | BREAK_K_MEDIA_MEDIA_SELECT, 760 | BREAK_K_MEDIA_EMAIL, 761 | BREAK_K_MEDIA_CALC, 762 | BREAK_K_MEDIA_MY_COMPUTER, 763 | BREAK_K_MEDIA_WWW_SEARCH, 764 | BREAK_K_MEDIA_WWW_HOME, 765 | BREAK_K_MEDIA_WWW_BACK, 766 | BREAK_K_MEDIA_WWW_FORWARD, 767 | BREAK_K_MEDIA_WWW_STOP, 768 | BREAK_K_MEDIA_WWW_REFRESH, 769 | BREAK_K_MEDIA_WWW_FAVORITES}; 770 | 771 | const uint8_t BREAK_CODES_LEN[] = {sizeof(BREAK_K_A), 772 | sizeof(BREAK_K_B), 773 | sizeof(BREAK_K_C), 774 | sizeof(BREAK_K_D), 775 | sizeof(BREAK_K_E), 776 | sizeof(BREAK_K_F), 777 | sizeof(BREAK_K_G), 778 | sizeof(BREAK_K_H), 779 | sizeof(BREAK_K_I), 780 | sizeof(BREAK_K_J), 781 | sizeof(BREAK_K_K), 782 | sizeof(BREAK_K_L), 783 | sizeof(BREAK_K_M), 784 | sizeof(BREAK_K_N), 785 | sizeof(BREAK_K_O), 786 | sizeof(BREAK_K_P), 787 | sizeof(BREAK_K_Q), 788 | sizeof(BREAK_K_R), 789 | sizeof(BREAK_K_S), 790 | sizeof(BREAK_K_T), 791 | sizeof(BREAK_K_U), 792 | sizeof(BREAK_K_V), 793 | sizeof(BREAK_K_W), 794 | sizeof(BREAK_K_X), 795 | sizeof(BREAK_K_Y), 796 | sizeof(BREAK_K_Z), 797 | sizeof(BREAK_K_0), 798 | sizeof(BREAK_K_1), 799 | sizeof(BREAK_K_2), 800 | sizeof(BREAK_K_3), 801 | sizeof(BREAK_K_4), 802 | sizeof(BREAK_K_5), 803 | sizeof(BREAK_K_6), 804 | sizeof(BREAK_K_7), 805 | sizeof(BREAK_K_8), 806 | sizeof(BREAK_K_9), 807 | sizeof(BREAK_K_BACKQUOTE), 808 | sizeof(BREAK_K_MINUS), 809 | sizeof(BREAK_K_EQUALS), 810 | sizeof(BREAK_K_BACKSLASH), 811 | sizeof(BREAK_K_BACKSPACE), 812 | sizeof(BREAK_K_SPACE), 813 | sizeof(BREAK_K_TAB), 814 | sizeof(BREAK_K_CAPSLOCK), 815 | sizeof(BREAK_K_LSHIFT), 816 | sizeof(BREAK_K_LCTRL), 817 | sizeof(BREAK_K_LSUPER), 818 | sizeof(BREAK_K_LALT), 819 | sizeof(BREAK_K_RSHIFT), 820 | sizeof(BREAK_K_RCTRL), 821 | sizeof(BREAK_K_RSUPER), 822 | sizeof(BREAK_K_RALT), 823 | sizeof(BREAK_K_MENU), 824 | sizeof(BREAK_K_RETURN), 825 | sizeof(BREAK_K_ESCAPE), 826 | sizeof(BREAK_K_F1), 827 | sizeof(BREAK_K_F2), 828 | sizeof(BREAK_K_F3), 829 | sizeof(BREAK_K_F4), 830 | sizeof(BREAK_K_F5), 831 | sizeof(BREAK_K_F6), 832 | sizeof(BREAK_K_F7), 833 | sizeof(BREAK_K_F8), 834 | sizeof(BREAK_K_F9), 835 | sizeof(BREAK_K_F10), 836 | sizeof(BREAK_K_F11), 837 | sizeof(BREAK_K_F12), 838 | sizeof(BREAK_K_PRINT), 839 | sizeof(BREAK_K_SCROLLOCK), 840 | sizeof(BREAK_K_PAUSE), 841 | sizeof(BREAK_K_LEFTBRACKET), 842 | sizeof(BREAK_K_INSERT), 843 | sizeof(BREAK_K_HOME), 844 | sizeof(BREAK_K_PAGEUP), 845 | sizeof(BREAK_K_DELETE), 846 | sizeof(BREAK_K_END), 847 | sizeof(BREAK_K_PAGEDOWN), 848 | sizeof(BREAK_K_UP), 849 | sizeof(BREAK_K_LEFT), 850 | sizeof(BREAK_K_DOWN), 851 | sizeof(BREAK_K_RIGHT), 852 | sizeof(BREAK_K_NUMLOCK), 853 | sizeof(BREAK_K_KP_DIVIDE), 854 | sizeof(BREAK_K_KP_MULTIPLY), 855 | sizeof(BREAK_K_KP_MINUS), 856 | sizeof(BREAK_K_KP_PLUS), 857 | sizeof(BREAK_K_KP_ENTER), 858 | sizeof(BREAK_K_KP_PERIOD), 859 | sizeof(BREAK_K_KP0), 860 | sizeof(BREAK_K_KP1), 861 | sizeof(BREAK_K_KP2), 862 | sizeof(BREAK_K_KP3), 863 | sizeof(BREAK_K_KP4), 864 | sizeof(BREAK_K_KP5), 865 | sizeof(BREAK_K_KP6), 866 | sizeof(BREAK_K_KP7), 867 | sizeof(BREAK_K_KP8), 868 | sizeof(BREAK_K_KP9), 869 | sizeof(BREAK_K_RIGHTBRACKET), 870 | sizeof(BREAK_K_SEMICOLON), 871 | sizeof(BREAK_K_QUOTE), 872 | sizeof(BREAK_K_COMMA), 873 | sizeof(BREAK_K_PERIOD), 874 | sizeof(BREAK_K_SLASH), 875 | sizeof(BREAK_K_ACPI_POWER), 876 | sizeof(BREAK_K_ACPI_SLEEP), 877 | sizeof(BREAK_K_ACPI_WAKE), 878 | sizeof(BREAK_K_MEDIA_NEXT_TRACK), 879 | sizeof(BREAK_K_MEDIA_PREV_TRACK), 880 | sizeof(BREAK_K_MEDIA_STOP), 881 | sizeof(BREAK_K_MEDIA_PLAY_PAUSE), 882 | sizeof(BREAK_K_MEDIA_MUTE), 883 | sizeof(BREAK_K_MEDIA_VOLUME_UP), 884 | sizeof(BREAK_K_MEDIA_VOLUME_DOWN), 885 | sizeof(BREAK_K_MEDIA_MEDIA_SELECT), 886 | sizeof(BREAK_K_MEDIA_EMAIL), 887 | sizeof(BREAK_K_MEDIA_CALC), 888 | sizeof(BREAK_K_MEDIA_MY_COMPUTER), 889 | sizeof(BREAK_K_MEDIA_WWW_SEARCH), 890 | sizeof(BREAK_K_MEDIA_WWW_HOME), 891 | sizeof(BREAK_K_MEDIA_WWW_BACK), 892 | sizeof(BREAK_K_MEDIA_WWW_FORWARD), 893 | sizeof(BREAK_K_MEDIA_WWW_STOP), 894 | sizeof(BREAK_K_MEDIA_WWW_REFRESH), 895 | sizeof(BREAK_K_MEDIA_WWW_FAVORITES)}; 896 | 897 | } // namespace scancodes 898 | 899 | } // namespace esp32_ps2dev 900 | 901 | #endif /* DD85C2BD_1EA1_416E_B227_80C3C8C3E40A */ 902 | -------------------------------------------------------------------------------- /include/serial_mouse.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "freertos/FreeRTOS.h" 4 | #include "freertos/task.h" 5 | #include "freertos/semphr.h" 6 | #include "freertos/event_groups.h" 7 | #include "esp_log.h" 8 | #include "driver/gpio.h" 9 | #include "esp_timer.h" 10 | 11 | class serialMouse 12 | { 13 | public: 14 | struct Data 15 | { 16 | uint8_t buttons; 17 | int xMovement; 18 | int yMovement; 19 | } report_data, report_buffer; 20 | 21 | void setup(int rtsPin, int rxPin); 22 | void serialMove(uint8_t buttons, int16_t mouseX, int16_t mouseY); 23 | static void initSerialPort(); 24 | static TaskHandle_t handle_reset_watchdog; 25 | static TaskHandle_t handle_serial_daemon; 26 | static bool resetReceived; // Host system issued a reset command via RTS pin 27 | static int RS232_RTS; 28 | static int RS232_RX; 29 | static void sendToSerial(const serialMouse::Data &data); 30 | 31 | private: 32 | static constexpr char const *TAG = "serial_mouse"; 33 | static unsigned long IRAM_ATTR micros(); 34 | static void IRAM_ATTR delayMicroseconds(uint32_t us); 35 | static void sendSerialBit(int data); 36 | static void sendSerialByte(uint8_t data); 37 | }; -------------------------------------------------------------------------------- /main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "main.cpp" "bt_keyboard.cpp" "esp32-ps2dev.cpp" "${app_sources}" 2 | REQUIRES esp_hid nvs_flash driver) 3 | 4 | component_compile_options(-Wno-error=format= -Wno-format) -------------------------------------------------------------------------------- /main/esp32-ps2dev.cpp: -------------------------------------------------------------------------------- 1 | #include "..\include\esp32-ps2dev.h" 2 | #define NOP() asm volatile("nop") 3 | #define HIGH 0x1 4 | #define LOW 0x0 5 | 6 | // Unomment following line to enable debug messages on the PS2DEV module 7 | //#define _ESP32_PS2DEV_DEBUG_ 8 | 9 | namespace esp32_ps2dev 10 | { 11 | 12 | // THIS SECTION DEFINES THE FUNCTIONS USED BELOW AS IMPLEMENTED IN THE ARDUINO CORE FOR ESP32 13 | // THE CODE ON THIS FILE HAS BEEN PORTED FROM AN ARDUINO-IDE PROJECT SO THIS IS NECESSARY 14 | // I KNOW IT'S NOT IDEAL BUT I DO NOT HAVE TIME TO MODIFY ALL NOR THE PATIENCE 15 | // ALSO THIS IS VERY TIME SENSITIVE CODE SO BETTER LEFT ALONE 16 | 17 | BaseType_t xTaskCreateUniversal(TaskFunction_t pxTaskCode, 18 | const char *const pcName, 19 | const uint32_t usStackDepth, 20 | void *const pvParameters, 21 | UBaseType_t uxPriority, 22 | TaskHandle_t *const pxCreatedTask, 23 | const BaseType_t xCoreID) 24 | { 25 | #ifndef CONFIG_FREERTOS_UNICORE 26 | if (xCoreID >= 0 && xCoreID < 2) 27 | { 28 | return xTaskCreatePinnedToCore(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask, xCoreID); 29 | } 30 | else 31 | { 32 | #endif 33 | return xTaskCreate(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask); 34 | #ifndef CONFIG_FREERTOS_UNICORE 35 | } 36 | #endif 37 | } 38 | 39 | extern void digitalWrite(uint8_t pin, uint8_t val) 40 | { 41 | gpio_set_level((gpio_num_t)pin, val); 42 | } 43 | 44 | extern int digitalRead(uint8_t pin) 45 | { 46 | return gpio_get_level((gpio_num_t)pin); 47 | } 48 | void delay(uint32_t ms) 49 | { 50 | vTaskDelay(ms / portTICK_PERIOD_MS); 51 | } 52 | 53 | unsigned long IRAM_ATTR millis() 54 | { 55 | return (unsigned long)(esp_timer_get_time() / 1000ULL); 56 | } 57 | 58 | unsigned long IRAM_ATTR micros() 59 | { 60 | return (unsigned long)(esp_timer_get_time()); 61 | } 62 | 63 | void IRAM_ATTR delayMicroseconds(uint32_t us) 64 | { 65 | uint32_t m = micros(); 66 | if (us) 67 | { 68 | uint32_t e = (m + us); 69 | if (m > e) 70 | { // overflow 71 | while (micros() > e) 72 | { 73 | NOP(); 74 | } 75 | } 76 | while (micros() < e) 77 | { 78 | NOP(); 79 | } 80 | } 81 | } 82 | 83 | // END OF ARDUINO CORE ADAPTATION 84 | 85 | PS2dev::PS2dev(int clk, int data) 86 | { 87 | _ps2clk = clk; 88 | _ps2data = data; 89 | } 90 | 91 | void PS2dev::config(UBaseType_t task_priority, BaseType_t task_core) 92 | { 93 | if (task_priority < 1) 94 | { 95 | task_priority = 1; 96 | } 97 | else if (task_priority > configMAX_PRIORITIES) 98 | { 99 | task_priority = configMAX_PRIORITIES - 1; 100 | } 101 | _config_task_priority = task_priority; 102 | _config_task_core = task_core; 103 | } 104 | 105 | void PS2dev::begin(BaseType_t core = DEFAULT_TASK_CORE) 106 | { 107 | gohi(_ps2clk); 108 | gohi(_ps2data); 109 | _mutex_bus = xSemaphoreCreateMutex(); 110 | _queue_packet = xQueueCreate(PACKET_QUEUE_LENGTH, sizeof(PS2Packet)); 111 | xTaskCreateUniversal(_taskfn_process_host_request, "process_host_request", 4096, this, _config_task_priority, &_task_process_host_request, core); 112 | xTaskCreateUniversal(_taskfn_send_packet, "send_packet", 4096, this, _config_task_priority - 1, &_task_send_packet, core); 113 | } 114 | 115 | void PS2dev::gohi(int pin) 116 | { 117 | digitalWrite(pin, HIGH); 118 | gpio_set_direction((gpio_num_t)pin, GPIO_MODE_INPUT); 119 | } 120 | void PS2dev::golo(int pin) 121 | { 122 | gpio_set_direction((gpio_num_t)pin, GPIO_MODE_OUTPUT_OD); 123 | digitalWrite(pin, LOW); 124 | } 125 | void PS2dev::ack() 126 | { 127 | delayMicroseconds(BYTE_INTERVAL_MICROS); 128 | write(0xFA); 129 | delayMicroseconds(BYTE_INTERVAL_MICROS); 130 | } 131 | int PS2dev::write(unsigned char data) 132 | { 133 | unsigned char i; 134 | unsigned char parity = 1; 135 | 136 | if (get_bus_state() != BusState::IDLE) 137 | { 138 | return -1; 139 | } 140 | 141 | portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; 142 | taskENTER_CRITICAL(&mux); 143 | 144 | golo(_ps2data); 145 | delayMicroseconds(CLK_QUATER_PERIOD_MICROS); 146 | // device sends on falling clock 147 | golo(_ps2clk); // start bit 148 | delayMicroseconds(CLK_HALF_PERIOD_MICROS); 149 | gohi(_ps2clk); 150 | delayMicroseconds(CLK_QUATER_PERIOD_MICROS); 151 | 152 | for (i = 0; i < 8; i++) 153 | { 154 | if (data & 0x01) 155 | { 156 | gohi(_ps2data); 157 | } 158 | else 159 | { 160 | golo(_ps2data); 161 | } 162 | delayMicroseconds(CLK_QUATER_PERIOD_MICROS); 163 | golo(_ps2clk); 164 | delayMicroseconds(CLK_HALF_PERIOD_MICROS); 165 | gohi(_ps2clk); 166 | delayMicroseconds(CLK_QUATER_PERIOD_MICROS); 167 | 168 | parity = parity ^ (data & 0x01); 169 | data = data >> 1; 170 | } 171 | // parity bit 172 | if (parity) 173 | { 174 | gohi(_ps2data); 175 | } 176 | else 177 | { 178 | golo(_ps2data); 179 | } 180 | delayMicroseconds(CLK_QUATER_PERIOD_MICROS); 181 | golo(_ps2clk); 182 | delayMicroseconds(CLK_HALF_PERIOD_MICROS); 183 | gohi(_ps2clk); 184 | delayMicroseconds(CLK_QUATER_PERIOD_MICROS); 185 | 186 | // stop bit 187 | gohi(_ps2data); 188 | delayMicroseconds(CLK_QUATER_PERIOD_MICROS); 189 | golo(_ps2clk); 190 | delayMicroseconds(CLK_HALF_PERIOD_MICROS); 191 | gohi(_ps2clk); 192 | delayMicroseconds(CLK_QUATER_PERIOD_MICROS); 193 | 194 | taskEXIT_CRITICAL(&mux); 195 | 196 | return 0; 197 | } 198 | int PS2dev::write_wait_idle(uint8_t data, uint64_t timeout_micros) 199 | { 200 | uint64_t start_time = micros(); 201 | while (get_bus_state() != BusState::IDLE) 202 | { 203 | if (micros() - start_time > timeout_micros) 204 | { 205 | return -1; 206 | } 207 | } 208 | return write(data); 209 | } 210 | int PS2dev::read(unsigned char *value, uint64_t timeout_ms) 211 | { 212 | unsigned int data = 0x00; 213 | unsigned int bit = 0x01; 214 | 215 | unsigned char calculated_parity = 1; 216 | unsigned char received_parity = 0; 217 | 218 | // wait for data line to go low and clock line to go high (or timeout) 219 | unsigned long waiting_since = millis(); 220 | while (get_bus_state() != BusState::HOST_REQUEST_TO_SEND) 221 | { 222 | if ((millis() - waiting_since) > timeout_ms) 223 | return -1; 224 | delay(1); 225 | } 226 | 227 | portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; 228 | taskENTER_CRITICAL(&mux); 229 | 230 | delayMicroseconds(CLK_QUATER_PERIOD_MICROS); 231 | golo(_ps2clk); 232 | delayMicroseconds(CLK_HALF_PERIOD_MICROS); 233 | gohi(_ps2clk); 234 | delayMicroseconds(CLK_QUATER_PERIOD_MICROS); 235 | 236 | while (bit < 0x0100) 237 | { 238 | if (digitalRead(_ps2data) == HIGH) 239 | { 240 | data = data | bit; 241 | calculated_parity = calculated_parity ^ 1; 242 | } 243 | else 244 | { 245 | calculated_parity = calculated_parity ^ 0; 246 | } 247 | 248 | bit = bit << 1; 249 | 250 | delayMicroseconds(CLK_QUATER_PERIOD_MICROS); 251 | golo(_ps2clk); 252 | delayMicroseconds(CLK_HALF_PERIOD_MICROS); 253 | gohi(_ps2clk); 254 | delayMicroseconds(CLK_QUATER_PERIOD_MICROS); 255 | } 256 | // we do the delay at the end of the loop, so at this point we have 257 | // already done the delay for the parity bit 258 | 259 | // parity bit 260 | if (digitalRead(_ps2data) == HIGH) 261 | { 262 | received_parity = 1; 263 | } 264 | 265 | // stop bit 266 | delayMicroseconds(CLK_QUATER_PERIOD_MICROS); 267 | golo(_ps2clk); 268 | delayMicroseconds(CLK_HALF_PERIOD_MICROS); 269 | gohi(_ps2clk); 270 | delayMicroseconds(CLK_QUATER_PERIOD_MICROS); 271 | 272 | delayMicroseconds(CLK_QUATER_PERIOD_MICROS); 273 | golo(_ps2data); 274 | golo(_ps2clk); 275 | delayMicroseconds(CLK_HALF_PERIOD_MICROS); 276 | gohi(_ps2clk); 277 | delayMicroseconds(CLK_QUATER_PERIOD_MICROS); 278 | gohi(_ps2data); 279 | 280 | taskEXIT_CRITICAL(&mux); 281 | 282 | *value = data & 0x00FF; 283 | 284 | if (received_parity == calculated_parity) 285 | { 286 | return 0; 287 | } 288 | else 289 | { 290 | return -2; 291 | } 292 | } 293 | PS2dev::BusState PS2dev::get_bus_state() 294 | { 295 | if (digitalRead(_ps2clk) == LOW) 296 | { 297 | return BusState::COMMUNICATION_INHIBITED; 298 | } 299 | else if (digitalRead(_ps2data) == LOW) 300 | { 301 | return BusState::HOST_REQUEST_TO_SEND; 302 | } 303 | else 304 | { 305 | return BusState::IDLE; 306 | } 307 | } 308 | SemaphoreHandle_t PS2dev::get_bus_mutex_handle() { return _mutex_bus; } 309 | QueueHandle_t PS2dev::get_packet_queue_handle() { return _queue_packet; } 310 | int PS2dev::send_packet(PS2Packet *packet) { return (xQueueSend(_queue_packet, packet, 0) == pdTRUE) ? 0 : -1; } 311 | 312 | PS2Mouse::PS2Mouse(int clk, int data) : PS2dev(clk, data) {} 313 | void PS2Mouse::begin(bool restore_internal_state = 1) 314 | { 315 | PS2dev::begin(DEFAULT_TASK_CORE_MOUSE); 316 | 317 | auto ret = nvs_flash_init(); 318 | if (ret != ESP_OK) 319 | { 320 | ESP_LOGE(TAG, "PS2Mouse::begin: nvs_flash_init failed"); 321 | } 322 | const auto nvs_ns = std::string("ps2dev") + std::to_string(_ps2clk) + std::to_string(_ps2data); 323 | ret = nvs_open(nvs_ns.c_str(), NVS_READWRITE, &_nvs_handle); 324 | if (ret != ESP_OK) 325 | { 326 | ESP_LOGE(TAG, "PS2Mouse::begin: nvs_open failed"); 327 | } 328 | 329 | if (!restore_internal_state) 330 | { 331 | xSemaphoreTake(_mutex_bus, portMAX_DELAY); 332 | delay(200); 333 | write(0xAA); 334 | delayMicroseconds(BYTE_INTERVAL_MICROS); 335 | write(0x00); 336 | xSemaphoreGive(_mutex_bus); 337 | } 338 | else if (ret == ESP_OK) 339 | { 340 | _load_internal_state_from_nvs(); 341 | ESP_LOGI(TAG, "Internal state for mouse loaded from NVS"); 342 | xSemaphoreTake(_mutex_bus, portMAX_DELAY); 343 | delay(200); 344 | write(0xAA); 345 | delayMicroseconds(BYTE_INTERVAL_MICROS); 346 | write(0x00); 347 | xSemaphoreGive(_mutex_bus); 348 | } 349 | 350 | xTaskCreatePinnedToCore(_taskfn_poll_mouse_count, "PS2Mouse", 4096, this, _config_task_priority - 1, &_task_poll_mouse_count, DEFAULT_TASK_CORE_MOUSE); 351 | } 352 | int PS2Mouse::reply_to_host(uint8_t host_cmd) 353 | { 354 | uint8_t val; 355 | if (_mode == Mode::WRAP_MODE) 356 | { 357 | switch ((Command)host_cmd) 358 | { 359 | case Command::SET_WRAP_MODE: // set wrap mode 360 | #if defined(_ESP32_PS2DEV_DEBUG_) 361 | printf("PS2Mouse::reply_to_host: (WRAP_MODE) Set wrap mode command received"); 362 | #endif 363 | ack(); 364 | reset_counter(); 365 | break; 366 | case Command::RESET_WRAP_MODE: // reset wrap mode 367 | #if defined(_ESP32_PS2DEV_DEBUG_) 368 | printf("PS2Mouse::reply_to_host: (WRAP_MODE) Reset wrap mode command received"); 369 | #endif 370 | ack(); 371 | reset_counter(); 372 | _mode = _last_mode; 373 | _save_internal_state_to_nvs(); 374 | break; 375 | default: 376 | write(host_cmd); 377 | } 378 | return 0; 379 | } 380 | 381 | switch ((Command)host_cmd) 382 | { 383 | case Command::RESET: // reset 384 | #if defined(_ESP32_PS2DEV_DEBUG_) 385 | printf("PS2Mouse::reply_to_host: Reset command received"); 386 | #endif 387 | ack(); 388 | // the while loop lets us wait for the host to be ready 389 | while (write(0xAA) != 0) 390 | delay(1); 391 | delayMicroseconds(BYTE_INTERVAL_MICROS); 392 | while (write(0x00) != 0) 393 | delay(1); 394 | _has_wheel = false; 395 | _has_4th_and_5th_buttons = false; 396 | _sample_rate = 100; 397 | _resolution = ResolutionCode::RES_4; 398 | _scale = Scale::ONE_ONE; 399 | _data_reporting_enabled = false; 400 | _mode = Mode::STREAM_MODE; 401 | _save_internal_state_to_nvs(); 402 | reset_counter(); 403 | break; 404 | case Command::RESEND: // resend 405 | #if defined(_ESP32_PS2DEV_DEBUG_) 406 | printf("PS2Mouse::reply_to_host: Resend command received"); 407 | #endif 408 | ack(); 409 | break; 410 | case Command::SET_DEFAULTS: // set defaults 411 | #if defined(_ESP32_PS2DEV_DEBUG_) 412 | printf("PS2Mouse::reply_to_host: Set defaults command received"); 413 | #endif 414 | // enter stream mode 415 | ack(); 416 | _sample_rate = 100; 417 | _resolution = ResolutionCode::RES_4; 418 | _scale = Scale::ONE_ONE; 419 | _data_reporting_enabled = false; 420 | _mode = Mode::STREAM_MODE; 421 | _save_internal_state_to_nvs(); 422 | reset_counter(); 423 | break; 424 | case Command::DISABLE_DATA_REPORTING: // disable data reporting 425 | #if defined(_ESP32_PS2DEV_DEBUG_) 426 | printf("PS2Mouse::reply_to_host: Disable data reporting command received"); 427 | #endif 428 | ack(); 429 | _data_reporting_enabled = false; 430 | _save_internal_state_to_nvs(); 431 | reset_counter(); 432 | break; 433 | case Command::ENABLE_DATA_REPORTING: // enable data reporting 434 | #if defined(_ESP32_PS2DEV_DEBUG_) 435 | printf("PS2Mouse::reply_to_host: Enable data reporting command received"); 436 | #endif 437 | ack(); 438 | _data_reporting_enabled = true; 439 | _save_internal_state_to_nvs(); 440 | reset_counter(); 441 | break; 442 | case Command::SET_SAMPLE_RATE: // set sample rate 443 | ack(); 444 | if (read(&val) == 0) 445 | { 446 | switch (val) 447 | { 448 | case 10: 449 | case 20: 450 | case 40: 451 | case 60: 452 | case 80: 453 | case 100: 454 | case 200: 455 | _sample_rate = val; 456 | _last_sample_rate[0] = _last_sample_rate[1]; 457 | _last_sample_rate[1] = _last_sample_rate[2]; 458 | _last_sample_rate[2] = val; 459 | #if defined(_ESP32_PS2DEV_DEBUG_) 460 | printf("Set sample rate command received: %x", val); 461 | //_ESP32_PS2DEV_DEBUG_.println(val); 462 | #endif 463 | ack(); 464 | break; 465 | 466 | default: 467 | break; 468 | } 469 | _save_internal_state_to_nvs(); 470 | // _min_report_interval_us = 1000000 / sample_rate; 471 | reset_counter(); 472 | } 473 | break; 474 | case Command::GET_DEVICE_ID: // get device id 475 | #if defined(_ESP32_PS2DEV_DEBUG_) 476 | printf("PS2Mouse::reply_to_host: Get device id command received"); 477 | #endif 478 | ack(); 479 | if (_last_sample_rate[0] == 200 && _last_sample_rate[1] == 100 && _last_sample_rate[2] == 80) 480 | { 481 | write(0x03); // Intellimouse with wheel 482 | #if defined(_ESP32_PS2DEV_DEBUG_) 483 | printf("PS2Mouse::reply_to_host: Act as Intellimouse with wheel."); 484 | #endif 485 | _has_wheel = true; 486 | _save_internal_state_to_nvs(); 487 | } 488 | else if (_last_sample_rate[0] == 200 && _last_sample_rate[1] == 200 && _last_sample_rate[2] == 80 && _has_wheel == true) 489 | { 490 | write(0x04); // Intellimouse with 4th and 5th buttons 491 | #if defined(_ESP32_PS2DEV_DEBUG_) 492 | printf("PS2Mouse::reply_to_host: Act as Intellimouse with 4th and 5th buttons."); 493 | #endif 494 | _has_4th_and_5th_buttons = true; 495 | _save_internal_state_to_nvs(); 496 | } 497 | else 498 | { 499 | write(0x00); // Standard PS/2 mouse 500 | #if defined(_ESP32_PS2DEV_DEBUG_) 501 | printf("PS2Mouse::reply_to_host: Act as standard PS/2 mouse."); 502 | #endif 503 | _has_wheel = false; 504 | _has_4th_and_5th_buttons = false; 505 | _save_internal_state_to_nvs(); 506 | } 507 | reset_counter(); 508 | break; 509 | case Command::SET_REMOTE_MODE: // set remote mode 510 | #if defined(_ESP32_PS2DEV_DEBUG_) 511 | printf("PS2Mouse::reply_to_host: Set remote mode command received"); 512 | #endif 513 | // ack(); 514 | delayMicroseconds(BYTE_INTERVAL_MICROS); 515 | while (write(0xFA) != 0) 516 | delay(1); 517 | delayMicroseconds(BYTE_INTERVAL_MICROS); 518 | reset_counter(); 519 | _mode = Mode::REMOTE_MODE; 520 | _save_internal_state_to_nvs(); 521 | break; 522 | case Command::SET_WRAP_MODE: // set wrap mode 523 | #if defined(_ESP32_PS2DEV_DEBUG_) 524 | printf("PS2Mouse::reply_to_host: Set wrap mode command received"); 525 | #endif 526 | ack(); 527 | reset_counter(); 528 | _last_mode = _mode; 529 | _mode = Mode::WRAP_MODE; 530 | _save_internal_state_to_nvs(); 531 | break; 532 | case Command::RESET_WRAP_MODE: // reset wrap mode 533 | #if defined(_ESP32_PS2DEV_DEBUG_) 534 | printf("PS2Mouse::reply_to_host: Reset wrap mode command received"); 535 | #endif 536 | ack(); 537 | reset_counter(); 538 | break; 539 | case Command::READ_DATA: // read data 540 | #if defined(_ESP32_PS2DEV_DEBUG_) 541 | // printf("PS2Mouse::reply_to_host: Read data command received"); ////////////////////////////////////////////////////////////// 542 | #endif 543 | ack(); 544 | _report(); 545 | reset_counter(); 546 | break; 547 | case Command::SET_STREAM_MODE: // set stream mode 548 | #if defined(_ESP32_PS2DEV_DEBUG_) 549 | printf("PS2Mouse::reply_to_host: Set stream mode command received"); 550 | #endif 551 | ack(); 552 | reset_counter(); 553 | break; 554 | case Command::STATUS_REQUEST: // status request 555 | #if defined(_ESP32_PS2DEV_DEBUG_) 556 | printf("PS2Mouse::reply_to_host: Status request command received"); 557 | #endif 558 | ack(); 559 | _send_status(); 560 | break; 561 | case Command::SET_RESOLUTION: // set resolution 562 | ack(); 563 | if (read(&val) == 0 && val <= 3) 564 | { 565 | _resolution = (ResolutionCode)val; 566 | #if defined(_ESP32_PS2DEV_DEBUG_) 567 | printf("PS2Mouse::reply_to_host: Set resolution command received: %x", val); 568 | //_ESP32_PS2DEV_DEBUG_.println(val, HEX); 569 | #endif 570 | ack(); 571 | _save_internal_state_to_nvs(); 572 | reset_counter(); 573 | } 574 | break; 575 | case Command::SET_SCALING_2_1: // set scaling 2:1 576 | #if defined(_ESP32_PS2DEV_DEBUG_) 577 | printf("PS2Mouse::reply_to_host: Set scaling 2:1 command received"); 578 | #endif 579 | ack(); 580 | _scale = Scale::TWO_ONE; 581 | _save_internal_state_to_nvs(); 582 | break; 583 | case Command::SET_SCALING_1_1: // set scaling 1:1 584 | #if defined(_ESP32_PS2DEV_DEBUG_) 585 | printf("PS2Mouse::reply_to_host: Set scaling 1:1 command received"); 586 | #endif 587 | ack(); 588 | _scale = Scale::ONE_ONE; 589 | _save_internal_state_to_nvs(); 590 | break; 591 | default: 592 | delayMicroseconds(BYTE_INTERVAL_MICROS); 593 | while ((write(0xFE) != 0)) 594 | delay(1); 595 | #if defined(_ESP32_PS2DEV_DEBUG_) 596 | printf("PS2Mouse::reply_to_host: Unknown command receivedASD: %x", host_cmd); 597 | //_ESP32_PS2DEV_DEBUG_.println(host_cmd, HEX); 598 | #endif 599 | break; 600 | } 601 | return 0; 602 | } 603 | bool PS2Mouse::has_wheel() { return _has_wheel; } 604 | bool PS2Mouse::has_4th_and_5th_buttons() { return _has_4th_and_5th_buttons; } 605 | bool PS2Mouse::data_reporting_enabled() { return _data_reporting_enabled; } 606 | void PS2Mouse::reset_counter() 607 | { 608 | _count_x = 0; 609 | _count_y = 0; 610 | _count_z = 0; 611 | _count_x_overflow = 0; 612 | _count_y_overflow = 0; 613 | } 614 | uint8_t PS2Mouse::get_sample_rate() { return _sample_rate; } 615 | void PS2Mouse::move(int16_t x, int16_t y, int8_t wheel) 616 | { 617 | _count_x += x; 618 | _count_y -= y; // We need to decrement, because USB HID inverts the vertical axis 619 | _count_z -= wheel; 620 | xTaskNotifyGive(_task_poll_mouse_count); 621 | } 622 | void PS2Mouse::press(Button button) 623 | { 624 | switch (button) 625 | { 626 | case Button::LEFT: 627 | _button_left = 1; 628 | break; 629 | case Button::RIGHT: 630 | _button_right = 1; 631 | break; 632 | case Button::MIDDLE: 633 | _button_middle = 1; 634 | break; 635 | case Button::BUTTON_4: 636 | _button_4th = 1; 637 | break; 638 | case Button::BUTTON_5: 639 | _button_5th = 1; 640 | break; 641 | default: 642 | break; 643 | } 644 | xTaskNotifyGive(_task_poll_mouse_count); 645 | } 646 | void PS2Mouse::release(Button button) 647 | { 648 | switch (button) 649 | { 650 | case Button::LEFT: 651 | _button_left = 0; 652 | break; 653 | case Button::RIGHT: 654 | _button_right = 0; 655 | break; 656 | case Button::MIDDLE: 657 | _button_middle = 0; 658 | break; 659 | case Button::BUTTON_4: 660 | _button_4th = 0; 661 | break; 662 | case Button::BUTTON_5: 663 | _button_5th = 0; 664 | break; 665 | default: 666 | break; 667 | } 668 | xTaskNotifyGive(_task_poll_mouse_count); 669 | } 670 | void PS2Mouse::click(Button button) 671 | { 672 | press(button); 673 | delay(MOUSE_CLICK_PRESSING_DURATION_MILLIS); 674 | release(button); 675 | } 676 | void PS2Mouse::_report() 677 | { 678 | PS2Packet packet; 679 | if (_scale == Scale::TWO_ONE) 680 | { 681 | int16_t *p[2] = {&_count_x, &_count_y}; 682 | for (size_t i = 0; i < 2; i++) 683 | { 684 | bool positive = *p[i] >= 0; 685 | uint16_t abs_value = positive ? *p[i] : -*p[i]; 686 | switch (abs_value) 687 | { 688 | case 1: 689 | abs_value = 1; 690 | break; 691 | case 2: 692 | abs_value = 1; 693 | break; 694 | case 3: 695 | abs_value = 3; 696 | break; 697 | case 4: 698 | abs_value = 6; 699 | break; 700 | case 5: 701 | abs_value = 9; 702 | break; 703 | default: 704 | abs_value *= 2; 705 | break; 706 | } 707 | if (!positive) 708 | *p[i] = -abs_value; 709 | } 710 | } 711 | if (_count_x > 255) 712 | { 713 | _count_x_overflow = 1; 714 | _count_x = 255; 715 | } 716 | else if (_count_x < -255) 717 | { 718 | _count_x_overflow = 1; 719 | _count_x = -255; 720 | } 721 | if (_count_y > 255) 722 | { 723 | _count_y_overflow = 1; 724 | _count_y = 255; 725 | } 726 | else if (_count_y < -255) 727 | { 728 | _count_y_overflow = 1; 729 | _count_y = -255; 730 | } 731 | if (_count_z > 7) 732 | { 733 | _count_z = 7; 734 | } 735 | else if (_count_z < -8) 736 | { 737 | _count_z = -8; 738 | } 739 | 740 | packet.len = 3 + _has_wheel; 741 | packet.data[0] = (_button_left) | ((_button_right) << 1) | ((_button_middle) << 2) | (1 << 3) | ((_count_x < 0) << 4) | 742 | ((_count_y < 0) << 5) | (_count_x_overflow << 6) | (_count_y_overflow << 7); 743 | packet.data[1] = _count_x & 0xFF; 744 | packet.data[2] = _count_y & 0xFF; 745 | if (_has_wheel && !_has_4th_and_5th_buttons) 746 | { 747 | packet.data[3] = _count_z & 0xFF; 748 | } 749 | else if (_has_wheel && _has_4th_and_5th_buttons) 750 | { 751 | packet.data[3] = (_count_z & 0x0F) | ((_button_4th) << 4) | ((_button_5th) << 5); 752 | } 753 | 754 | send_packet(&packet); 755 | reset_counter(); 756 | } 757 | void PS2Mouse::_send_status() 758 | { 759 | PS2Packet packet; 760 | packet.len = 3; 761 | bool mode = (_mode == Mode::REMOTE_MODE); 762 | packet.data[0] = (_button_right & 1) & ((_button_middle & 1) << 1) & ((_button_left & 1) << 2) & ((0) << 3) & 763 | (((uint8_t)_scale & 1) << 4) & ((_data_reporting_enabled & 1) << 5) & ((mode & 1) << 6) & ((0) << 7); 764 | packet.data[1] = (uint8_t)_resolution; 765 | packet.data[2] = _sample_rate; 766 | send_packet(&packet); 767 | } 768 | 769 | PS2Keyboard::PS2Keyboard(int clk, int data) : PS2dev(clk, data) {} 770 | void PS2Keyboard::begin() 771 | { 772 | PS2dev::begin(); 773 | 774 | xSemaphoreTake(_mutex_bus, portMAX_DELAY); 775 | delayMicroseconds(BYTE_INTERVAL_MICROS); 776 | delay(200); 777 | write(0xAA); 778 | xSemaphoreGive(_mutex_bus); 779 | } 780 | bool PS2Keyboard::data_reporting_enabled() { return _data_reporting_enabled; } 781 | bool PS2Keyboard::is_scroll_lock_led_on() { return _led_scroll_lock; } 782 | bool PS2Keyboard::is_num_lock_led_on() { return _led_num_lock; } 783 | bool PS2Keyboard::is_caps_lock_led_on() { return _led_caps_lock; } 784 | int PS2Keyboard::reply_to_host(uint8_t host_cmd) 785 | { 786 | uint8_t val; 787 | switch ((Command)host_cmd) 788 | { 789 | case Command::RESET: // reset 790 | #if defined(_ESP32_PS2DEV_DEBUG_) 791 | printf("PS2Keyboard::reply_to_host: Reset command received"); 792 | #endif // _ESP32_PS2DEV_DEBUG_ 793 | // the while loop lets us wait for the host to be ready 794 | _data_reporting_enabled = false; 795 | ack(); // ack() provides delay, some systems need it 796 | while (write((uint8_t)Command::BAT_SUCCESS) != 0) 797 | delay(1); 798 | _data_reporting_enabled = true; // some systems don't enable data reporting after issuing a RESET command, so we do it by default 799 | break; 800 | case Command::RESEND: // resend 801 | #if defined(_ESP32_PS2DEV_DEBUG_) 802 | printf("PS2Keyboard::reply_to_host: Resend command received"); 803 | #endif // _ESP32_PS2DEV_DEBUG_ 804 | ack(); 805 | break; 806 | case Command::SET_DEFAULTS: // set defaults 807 | #if defined(_ESP32_PS2DEV_DEBUG_) 808 | printf("PS2Keyboard::reply_to_host: Set defaults command received"); 809 | #endif // _ESP32_PS2DEV_DEBUG_ 810 | // enter stream mode 811 | ack(); 812 | break; 813 | case Command::DISABLE_DATA_REPORTING: // disable data reporting 814 | #if defined(_ESP32_PS2DEV_DEBUG_) 815 | printf("PS2Keyboard::reply_to_host: Disable data reporting command received"); 816 | #endif // _ESP32_PS2DEV_DEBUG_ 817 | _data_reporting_enabled = false; 818 | ack(); 819 | break; 820 | case Command::ENABLE_DATA_REPORTING: // enable data reporting 821 | #if defined(_ESP32_PS2DEV_DEBUG_) 822 | printf("PS2Keyboard::reply_to_host: Enable data reporting command received"); 823 | #endif // _ESP32_PS2DEV_DEBUG_ 824 | _data_reporting_enabled = true; 825 | ack(); 826 | break; 827 | case Command::SET_TYPEMATIC_RATE: // set typematic rate 828 | #if defined(_ESP32_PS2DEV_DEBUG_) 829 | printf("PS2Keyboard::reply_to_host: Set typematic rate command received"); 830 | #endif // _ESP32_PS2DEV_DEBUG_ 831 | ack(); 832 | if (!read(&val)) 833 | ack(); // do nothing with the rate 834 | break; 835 | case Command::GET_DEVICE_ID: // get device id 836 | #if defined(_ESP32_PS2DEV_DEBUG_) 837 | printf("PS2Keyboard::reply_to_host: Get device id command received"); 838 | #endif // _ESP32_PS2DEV_DEBUG_ 839 | ack(); 840 | while (write(0xAB) != 0) 841 | delay(1); // ensure ID gets writed, some hosts may be sensitive 842 | while (write(0x83) != 0) 843 | delay(1); // this is critical for combined ports (they decide mouse/kb on this) 844 | break; 845 | case Command::SET_SCAN_CODE_SET: // set scan code set 846 | #if defined(_ESP32_PS2DEV_DEBUG_) 847 | printf("PS2Keyboard::reply_to_host: Set scan code set command received"); 848 | #endif // _ESP32_PS2DEV_DEBUG_ 849 | ack(); 850 | if (!read(&val)) 851 | ack(); // do nothing with the rate 852 | break; 853 | case Command::ECHO: // echo 854 | #if defined(_ESP32_PS2DEV_DEBUG_) 855 | printf("PS2Keyboard::reply_to_host: Echo command received"); 856 | #endif // _ESP32_PS2DEV_DEBUG_ 857 | delayMicroseconds(BYTE_INTERVAL_MICROS); 858 | write(0xEE); 859 | delayMicroseconds(BYTE_INTERVAL_MICROS); 860 | break; 861 | case Command::SET_RESET_LEDS: // set/reset LEDs 862 | #if defined(_ESP32_PS2DEV_DEBUG_) 863 | printf("PS2Keyboard::reply_to_host: Set/reset LEDs command received"); 864 | #endif // _ESP32_PS2DEV_DEBUG_ 865 | delayMicroseconds(BYTE_INTERVAL_MICROS); 866 | while (write(0xFA) != 0) 867 | delay(1); 868 | delayMicroseconds(BYTE_INTERVAL_MICROS); 869 | if (!read(&val, 10)) 870 | { 871 | delayMicroseconds(BYTE_INTERVAL_MICROS); 872 | while (write(0xFA) != 0) 873 | delay(1); 874 | delayMicroseconds(BYTE_INTERVAL_MICROS); 875 | _led_scroll_lock = ((val & 1) != 0); 876 | _led_num_lock = ((val & 2) != 0); 877 | _led_caps_lock = ((val & 4) != 0); 878 | } 879 | return 1; 880 | break; 881 | default: 882 | ack(); 883 | #if defined(_ESP32_PS2DEV_DEBUG_) 884 | printf("PS2Keyboard::reply_to_host: Unknown command received: %x", host_cmd); 885 | //_ESP32_PS2DEV_DEBUG_.println(host_cmd, HEX); 886 | #endif // _ESP32_PS2DEV_DEBUG_ 887 | break; 888 | } 889 | 890 | return 0; 891 | } 892 | void PS2Keyboard::keydown(scancodes::Key key) 893 | { 894 | if (!_data_reporting_enabled) 895 | return; 896 | PS2Packet packet; 897 | packet.len = scancodes::MAKE_CODES_LEN[key]; 898 | for (uint8_t i = 0; i < packet.len; i++) 899 | { 900 | packet.data[i] = scancodes::MAKE_CODES[key][i]; 901 | } 902 | send_packet(&packet); 903 | } 904 | void PS2Keyboard::keyup(scancodes::Key key) 905 | { 906 | if (!_data_reporting_enabled) 907 | return; 908 | PS2Packet packet; 909 | packet.len = scancodes::BREAK_CODES_LEN[key]; 910 | for (uint8_t i = 0; i < packet.len; i++) 911 | { 912 | packet.data[i] = scancodes::BREAK_CODES[key][i]; 913 | } 914 | send_packet(&packet); 915 | } 916 | void PS2Keyboard::type(scancodes::Key key) 917 | { 918 | keydown(key); 919 | delay(10); 920 | keyup(key); 921 | } 922 | void PS2Keyboard::type(std::initializer_list keys) 923 | { 924 | std::stack stack; 925 | for (auto key : keys) 926 | { 927 | keydown(key); 928 | stack.push(key); 929 | delay(10); 930 | } 931 | while (!stack.empty()) 932 | { 933 | keyup(stack.top()); 934 | stack.pop(); 935 | delay(10); 936 | } 937 | } 938 | void PS2Keyboard::type(const char *str) 939 | { 940 | size_t i = 0; 941 | while (str[i] != '\0') 942 | { 943 | char c = str[i]; 944 | scancodes::Key key; 945 | bool shift = false; 946 | switch (c) 947 | { 948 | case '\b': 949 | key = scancodes::Key::K_BACKSPACE; 950 | break; 951 | case '\t': 952 | key = scancodes::Key::K_TAB; 953 | break; 954 | case '\r': 955 | case '\n': 956 | key = scancodes::Key::K_RETURN; 957 | break; 958 | case ' ': 959 | key = scancodes::Key::K_SPACE; 960 | break; 961 | case '!': 962 | shift = true; 963 | key = scancodes::Key::K_1; 964 | break; 965 | case '\"': 966 | shift = true; 967 | key = scancodes::Key::K_QUOTE; 968 | break; 969 | case '#': 970 | shift = true; 971 | key = scancodes::Key::K_3; 972 | break; 973 | case '$': 974 | shift = true; 975 | key = scancodes::Key::K_4; 976 | break; 977 | case '&': 978 | shift = true; 979 | key = scancodes::Key::K_7; 980 | break; 981 | case '\'': 982 | key = scancodes::Key::K_QUOTE; 983 | break; 984 | case '(': 985 | shift = true; 986 | key = scancodes::Key::K_9; 987 | break; 988 | case ')': 989 | shift = true; 990 | key = scancodes::Key::K_0; 991 | break; 992 | case '*': 993 | shift = true; 994 | key = scancodes::Key::K_8; 995 | break; 996 | case '+': 997 | shift = true; 998 | key = scancodes::Key::K_EQUALS; 999 | break; 1000 | case ',': 1001 | key = scancodes::Key::K_COMMA; 1002 | break; 1003 | case '-': 1004 | key = scancodes::Key::K_MINUS; 1005 | break; 1006 | case '.': 1007 | key = scancodes::Key::K_PERIOD; 1008 | break; 1009 | case '/': 1010 | key = scancodes::Key::K_SLASH; 1011 | break; 1012 | case '0': 1013 | key = scancodes::Key::K_0; 1014 | break; 1015 | case '1': 1016 | key = scancodes::Key::K_1; 1017 | break; 1018 | case '2': 1019 | key = scancodes::Key::K_2; 1020 | break; 1021 | case '3': 1022 | key = scancodes::Key::K_3; 1023 | break; 1024 | case '4': 1025 | key = scancodes::Key::K_4; 1026 | break; 1027 | case '5': 1028 | key = scancodes::Key::K_5; 1029 | break; 1030 | case '6': 1031 | key = scancodes::Key::K_6; 1032 | break; 1033 | case '7': 1034 | key = scancodes::Key::K_7; 1035 | break; 1036 | case '8': 1037 | key = scancodes::Key::K_8; 1038 | break; 1039 | case '9': 1040 | key = scancodes::Key::K_9; 1041 | break; 1042 | case ':': 1043 | shift = true; 1044 | key = scancodes::Key::K_SEMICOLON; 1045 | break; 1046 | case ';': 1047 | key = scancodes::Key::K_SEMICOLON; 1048 | break; 1049 | case '<': 1050 | shift = true; 1051 | key = scancodes::Key::K_COMMA; 1052 | break; 1053 | case '=': 1054 | key = scancodes::Key::K_EQUALS; 1055 | break; 1056 | case '>': 1057 | shift = true; 1058 | key = scancodes::Key::K_PERIOD; 1059 | break; 1060 | case '\?': 1061 | shift = true; 1062 | key = scancodes::Key::K_SLASH; 1063 | break; 1064 | case '@': 1065 | shift = true; 1066 | key = scancodes::Key::K_2; 1067 | break; 1068 | case '[': 1069 | key = scancodes::Key::K_LEFTBRACKET; 1070 | break; 1071 | case '\\': 1072 | key = scancodes::Key::K_BACKSLASH; 1073 | break; 1074 | case ']': 1075 | key = scancodes::Key::K_RIGHTBRACKET; 1076 | break; 1077 | case '^': 1078 | shift = true; 1079 | key = scancodes::Key::K_6; 1080 | break; 1081 | case '_': 1082 | shift = true; 1083 | key = scancodes::Key::K_MINUS; 1084 | break; 1085 | case '`': 1086 | key = scancodes::Key::K_BACKQUOTE; 1087 | break; 1088 | case 'a': 1089 | key = scancodes::Key::K_A; 1090 | break; 1091 | case 'b': 1092 | key = scancodes::Key::K_B; 1093 | break; 1094 | case 'c': 1095 | key = scancodes::Key::K_C; 1096 | break; 1097 | case 'd': 1098 | key = scancodes::Key::K_D; 1099 | break; 1100 | case 'e': 1101 | key = scancodes::Key::K_E; 1102 | break; 1103 | case 'f': 1104 | key = scancodes::Key::K_F; 1105 | break; 1106 | case 'g': 1107 | key = scancodes::Key::K_G; 1108 | break; 1109 | case 'h': 1110 | key = scancodes::Key::K_H; 1111 | break; 1112 | case 'i': 1113 | key = scancodes::Key::K_I; 1114 | break; 1115 | case 'j': 1116 | key = scancodes::Key::K_J; 1117 | break; 1118 | case 'k': 1119 | key = scancodes::Key::K_K; 1120 | break; 1121 | case 'l': 1122 | key = scancodes::Key::K_L; 1123 | break; 1124 | case 'm': 1125 | key = scancodes::Key::K_M; 1126 | break; 1127 | case 'n': 1128 | key = scancodes::Key::K_N; 1129 | break; 1130 | case 'o': 1131 | key = scancodes::Key::K_O; 1132 | break; 1133 | case 'p': 1134 | key = scancodes::Key::K_P; 1135 | break; 1136 | case 'q': 1137 | key = scancodes::Key::K_Q; 1138 | break; 1139 | case 'r': 1140 | key = scancodes::Key::K_R; 1141 | break; 1142 | case 's': 1143 | key = scancodes::Key::K_S; 1144 | break; 1145 | case 't': 1146 | key = scancodes::Key::K_T; 1147 | break; 1148 | case 'u': 1149 | key = scancodes::Key::K_U; 1150 | break; 1151 | case 'v': 1152 | key = scancodes::Key::K_V; 1153 | break; 1154 | case 'w': 1155 | key = scancodes::Key::K_W; 1156 | break; 1157 | case 'x': 1158 | key = scancodes::Key::K_X; 1159 | break; 1160 | case 'y': 1161 | key = scancodes::Key::K_Y; 1162 | break; 1163 | case 'z': 1164 | key = scancodes::Key::K_Z; 1165 | break; 1166 | case 'A': 1167 | shift = true; 1168 | key = scancodes::Key::K_A; 1169 | break; 1170 | case 'B': 1171 | shift = true; 1172 | key = scancodes::Key::K_B; 1173 | break; 1174 | case 'C': 1175 | shift = true; 1176 | key = scancodes::Key::K_C; 1177 | break; 1178 | case 'D': 1179 | shift = true; 1180 | key = scancodes::Key::K_D; 1181 | break; 1182 | case 'E': 1183 | shift = true; 1184 | key = scancodes::Key::K_E; 1185 | break; 1186 | case 'F': 1187 | shift = true; 1188 | key = scancodes::Key::K_F; 1189 | break; 1190 | case 'G': 1191 | shift = true; 1192 | key = scancodes::Key::K_G; 1193 | break; 1194 | case 'H': 1195 | shift = true; 1196 | key = scancodes::Key::K_H; 1197 | break; 1198 | case 'I': 1199 | shift = true; 1200 | key = scancodes::Key::K_I; 1201 | break; 1202 | case 'J': 1203 | shift = true; 1204 | key = scancodes::Key::K_J; 1205 | break; 1206 | case 'K': 1207 | shift = true; 1208 | key = scancodes::Key::K_K; 1209 | break; 1210 | case 'L': 1211 | shift = true; 1212 | key = scancodes::Key::K_L; 1213 | break; 1214 | case 'M': 1215 | shift = true; 1216 | key = scancodes::Key::K_M; 1217 | break; 1218 | case 'N': 1219 | shift = true; 1220 | key = scancodes::Key::K_N; 1221 | break; 1222 | case 'O': 1223 | shift = true; 1224 | key = scancodes::Key::K_O; 1225 | break; 1226 | case 'P': 1227 | shift = true; 1228 | key = scancodes::Key::K_P; 1229 | break; 1230 | case 'Q': 1231 | shift = true; 1232 | key = scancodes::Key::K_Q; 1233 | break; 1234 | case 'R': 1235 | shift = true; 1236 | key = scancodes::Key::K_R; 1237 | break; 1238 | case 'S': 1239 | shift = true; 1240 | key = scancodes::Key::K_S; 1241 | break; 1242 | case 'T': 1243 | shift = true; 1244 | key = scancodes::Key::K_T; 1245 | break; 1246 | case 'U': 1247 | shift = true; 1248 | key = scancodes::Key::K_U; 1249 | break; 1250 | case 'V': 1251 | shift = true; 1252 | key = scancodes::Key::K_V; 1253 | break; 1254 | case 'W': 1255 | shift = true; 1256 | key = scancodes::Key::K_W; 1257 | break; 1258 | case 'X': 1259 | shift = true; 1260 | key = scancodes::Key::K_X; 1261 | break; 1262 | case 'Y': 1263 | shift = true; 1264 | key = scancodes::Key::K_Y; 1265 | break; 1266 | case 'Z': 1267 | shift = true; 1268 | key = scancodes::Key::K_Z; 1269 | break; 1270 | 1271 | default: 1272 | i++; 1273 | continue; 1274 | break; 1275 | } 1276 | if (shift) 1277 | { 1278 | keydown(scancodes::Key::K_LSHIFT); 1279 | delay(10); 1280 | type(key); 1281 | delay(10); 1282 | keyup(scancodes::Key::K_LSHIFT); 1283 | } 1284 | else 1285 | { 1286 | type(key); 1287 | } 1288 | i++; 1289 | } 1290 | } 1291 | 1292 | void _taskfn_process_host_request(void *arg) 1293 | { 1294 | PS2dev *ps2dev = (PS2dev *)arg; 1295 | while (true) 1296 | { 1297 | xSemaphoreTake(ps2dev->get_bus_mutex_handle(), portMAX_DELAY); 1298 | if (ps2dev->get_bus_state() == PS2dev::BusState::HOST_REQUEST_TO_SEND) 1299 | { 1300 | uint8_t host_cmd; 1301 | if (ps2dev->read(&host_cmd) == 0) 1302 | { 1303 | ps2dev->reply_to_host(host_cmd); 1304 | } 1305 | } 1306 | xSemaphoreGive(ps2dev->get_bus_mutex_handle()); 1307 | delay(INTERVAL_CHECKING_HOST_SEND_REQUEST_MILLIS); 1308 | } 1309 | vTaskDelete(NULL); 1310 | } 1311 | void _taskfn_send_packet(void *arg) 1312 | { 1313 | PS2dev *ps2dev = (PS2dev *)arg; 1314 | while (true) 1315 | { 1316 | PS2Packet packet; 1317 | if (xQueueReceive(ps2dev->get_packet_queue_handle(), &packet, portMAX_DELAY) == pdTRUE) 1318 | { 1319 | xSemaphoreTake(ps2dev->get_bus_mutex_handle(), portMAX_DELAY); 1320 | delayMicroseconds(BYTE_INTERVAL_MICROS); 1321 | for (int i = 0; i < packet.len; i++) 1322 | { 1323 | ps2dev->write_wait_idle(packet.data[i]); 1324 | delayMicroseconds(BYTE_INTERVAL_MICROS); 1325 | } 1326 | xSemaphoreGive(ps2dev->get_bus_mutex_handle()); 1327 | } 1328 | } 1329 | vTaskDelete(NULL); 1330 | } 1331 | void _taskfn_poll_mouse_count(void *arg) 1332 | { 1333 | PS2Mouse *ps2mouse = (PS2Mouse *)arg; 1334 | while (true) 1335 | { 1336 | xTaskNotifyWait(0, 0, NULL, portMAX_DELAY); 1337 | if (ps2mouse->data_reporting_enabled()) 1338 | { 1339 | ps2mouse->_report(); 1340 | } 1341 | delay(1000 / ps2mouse->get_sample_rate()); 1342 | } 1343 | vTaskDelete(NULL); 1344 | } 1345 | 1346 | void PS2Mouse::_save_internal_state_to_nvs() 1347 | { 1348 | auto ret = nvs_set_u8(_nvs_handle, "hasWheel", _has_wheel); 1349 | if (ret != ESP_OK) 1350 | { 1351 | ESP_LOGE(TAG, "PS2Mouse::_save_internal_state_to_nvs: nvs_set_u8 failed to save hasWheel."); 1352 | } 1353 | ret = nvs_set_u8(_nvs_handle, "has4and5Btn", _has_4th_and_5th_buttons); 1354 | if (ret != ESP_OK) 1355 | { 1356 | ESP_LOGE(TAG, "PS2Mouse::_save_internal_state_to_nvs: nvs_set_u8 failed to save has4and5Btn."); 1357 | } 1358 | ret = nvs_set_u8(_nvs_handle, "dataRepEn", _data_reporting_enabled); 1359 | if (ret != ESP_OK) 1360 | { 1361 | ESP_LOGE(TAG, "PS2Mouse::_save_internal_state_to_nvs: nvs_set_u8 failed to save dataRepEn."); 1362 | } 1363 | ret = nvs_set_u8(_nvs_handle, "resolution", (uint8_t)_resolution); 1364 | if (ret != ESP_OK) 1365 | { 1366 | ESP_LOGE(TAG, "PS2Mouse::_save_internal_state_to_nvs: nvs_set_u8 failed to save resolution."); 1367 | } 1368 | ret = nvs_set_u8(_nvs_handle, "scale", (uint8_t)_scale); 1369 | if (ret != ESP_OK) 1370 | { 1371 | ESP_LOGE(TAG, "PS2Mouse::_save_internal_state_to_nvs: nvs_set_u8 failed to save scale."); 1372 | } 1373 | ret = nvs_set_u8(_nvs_handle, "mode", (uint8_t)_mode); 1374 | if (ret != ESP_OK) 1375 | { 1376 | ESP_LOGE(TAG, "PS2Mouse::_save_internal_state_to_nvs: nvs_set_u8 failed to save mode."); 1377 | } 1378 | } 1379 | 1380 | void PS2Mouse::_load_internal_state_from_nvs() 1381 | { 1382 | auto ret = nvs_get_u8(_nvs_handle, "hasWheel", (uint8_t *)&_has_wheel); 1383 | if (ret != ESP_OK) 1384 | { 1385 | ESP_LOGE(TAG, "PS2Mouse::_load_internal_state_from_nvs: nvs_get_u8 failed to load hasWheel."); 1386 | } 1387 | nvs_get_u8(_nvs_handle, "has4and5Btn", (uint8_t *)&_has_4th_and_5th_buttons); 1388 | if (ret != ESP_OK) 1389 | { 1390 | ESP_LOGE(TAG, "PS2Mouse::_load_internal_state_from_nvs: nvs_get_u8 failed to load has4and5Btn."); 1391 | } 1392 | nvs_get_u8(_nvs_handle, "dataRepEn", (uint8_t *)&_data_reporting_enabled); 1393 | if (ret != ESP_OK) 1394 | { 1395 | ESP_LOGE(TAG, "PS2Mouse::_load_internal_state_from_nvs: nvs_get_u8 failed to load dataRepEn."); 1396 | } 1397 | nvs_get_u8(_nvs_handle, "resolution", (uint8_t *)&_resolution); 1398 | if (ret != ESP_OK) 1399 | { 1400 | ESP_LOGE(TAG, "PS2Mouse::_load_internal_state_from_nvs: nvs_get_u8 failed to load resolution."); 1401 | } 1402 | nvs_get_u8(_nvs_handle, "scale", (uint8_t *)&_scale); 1403 | if (ret != ESP_OK) 1404 | { 1405 | ESP_LOGE(TAG, "PS2Mouse::_load_internal_state_from_nvs: nvs_get_u8 failed to load scale."); 1406 | } 1407 | nvs_get_u8(_nvs_handle, "mode", (uint8_t *)&_mode); 1408 | if (ret != ESP_OK) 1409 | { 1410 | ESP_LOGE(TAG, "PS2Mouse::_load_internal_state_from_nvs: nvs_get_u8 failed to load mode."); 1411 | } 1412 | } 1413 | 1414 | void PS2Keyboard::keyHid_send(uint8_t btkey, bool keyDown) 1415 | { 1416 | scancodes::Key key; 1417 | switch (btkey) 1418 | { 1419 | case 0x04: 1420 | key = scancodes::Key::K_A; 1421 | break; 1422 | case 0x05: 1423 | key = scancodes::Key::K_B; 1424 | break; 1425 | case 0x06: 1426 | key = scancodes::Key::K_C; 1427 | break; 1428 | case 0x07: 1429 | key = scancodes::Key::K_D; 1430 | break; 1431 | case 0x08: 1432 | key = scancodes::Key::K_E; 1433 | break; 1434 | case 0x09: 1435 | key = scancodes::Key::K_F; 1436 | break; 1437 | case 0x0A: 1438 | key = scancodes::Key::K_G; 1439 | break; 1440 | case 0x0B: 1441 | key = scancodes::Key::K_H; 1442 | break; 1443 | case 0x0C: 1444 | key = scancodes::Key::K_I; 1445 | break; 1446 | case 0x0D: 1447 | key = scancodes::Key::K_J; 1448 | break; 1449 | case 0x0E: 1450 | key = scancodes::Key::K_K; 1451 | break; 1452 | case 0x0F: 1453 | key = scancodes::Key::K_L; 1454 | break; 1455 | case 0x10: 1456 | key = scancodes::Key::K_M; 1457 | break; 1458 | case 0x11: 1459 | key = scancodes::Key::K_N; 1460 | break; 1461 | case 0x12: 1462 | key = scancodes::Key::K_O; 1463 | break; 1464 | case 0x13: 1465 | key = scancodes::Key::K_P; 1466 | break; 1467 | case 0x14: 1468 | key = scancodes::Key::K_Q; 1469 | break; 1470 | case 0x15: 1471 | key = scancodes::Key::K_R; 1472 | break; 1473 | case 0x16: 1474 | key = scancodes::Key::K_S; 1475 | break; 1476 | case 0x17: 1477 | key = scancodes::Key::K_T; 1478 | break; 1479 | case 0x18: 1480 | key = scancodes::Key::K_U; 1481 | break; 1482 | case 0x19: 1483 | key = scancodes::Key::K_V; 1484 | break; 1485 | case 0x1A: 1486 | key = scancodes::Key::K_W; 1487 | break; 1488 | case 0x1B: 1489 | key = scancodes::Key::K_X; 1490 | break; 1491 | case 0x1C: 1492 | key = scancodes::Key::K_Y; 1493 | break; 1494 | case 0x1D: 1495 | key = scancodes::Key::K_Z; 1496 | break; 1497 | case 0x1E: 1498 | key = scancodes::Key::K_1; 1499 | break; 1500 | case 0x1F: 1501 | key = scancodes::Key::K_2; 1502 | break; 1503 | case 0x20: 1504 | key = scancodes::Key::K_3; 1505 | break; 1506 | case 0x21: 1507 | key = scancodes::Key::K_4; 1508 | break; 1509 | case 0x22: 1510 | key = scancodes::Key::K_5; 1511 | break; 1512 | case 0x23: 1513 | key = scancodes::Key::K_6; 1514 | break; 1515 | case 0x24: 1516 | key = scancodes::Key::K_7; 1517 | break; 1518 | case 0x25: 1519 | key = scancodes::Key::K_8; 1520 | break; 1521 | case 0x26: 1522 | key = scancodes::Key::K_9; 1523 | break; 1524 | case 0x27: 1525 | key = scancodes::Key::K_0; 1526 | break; 1527 | case 0x28: 1528 | key = scancodes::Key::K_RETURN; 1529 | break; 1530 | case 0x29: 1531 | key = scancodes::Key::K_ESCAPE; 1532 | break; 1533 | case 0x2A: 1534 | key = scancodes::Key::K_BACKSPACE; 1535 | break; 1536 | case 0x2B: 1537 | key = scancodes::Key::K_TAB; 1538 | break; 1539 | case 0x2C: 1540 | key = scancodes::Key::K_SPACE; 1541 | break; 1542 | case 0x2D: 1543 | key = scancodes::Key::K_MINUS; 1544 | break; 1545 | case 0x2E: 1546 | key = scancodes::Key::K_EQUALS; 1547 | break; 1548 | case 0x2F: 1549 | key = scancodes::Key::K_LEFTBRACKET; 1550 | break; 1551 | case 0x30: 1552 | key = scancodes::Key::K_RIGHTBRACKET; 1553 | break; 1554 | case 0x31: 1555 | key = scancodes::Key::K_BACKSLASH; 1556 | break; 1557 | case 0x33: 1558 | key = scancodes::Key::K_SEMICOLON; 1559 | break; 1560 | case 0x34: 1561 | key = scancodes::Key::K_QUOTE; 1562 | break; 1563 | case 0x35: 1564 | key = scancodes::Key::K_BACKQUOTE; 1565 | break; 1566 | case 0x36: 1567 | key = scancodes::Key::K_COMMA; 1568 | break; 1569 | case 0x37: 1570 | key = scancodes::Key::K_PERIOD; 1571 | break; 1572 | case 0x38: 1573 | key = scancodes::Key::K_SLASH; 1574 | break; 1575 | case 0x39: 1576 | key = scancodes::Key::K_CAPSLOCK; 1577 | break; 1578 | case 0x3A: 1579 | key = scancodes::Key::K_F1; 1580 | break; 1581 | case 0x3B: 1582 | key = scancodes::Key::K_F2; 1583 | break; 1584 | case 0x3C: 1585 | key = scancodes::Key::K_F3; 1586 | break; 1587 | case 0x3D: 1588 | key = scancodes::Key::K_F4; 1589 | break; 1590 | case 0x3E: 1591 | key = scancodes::Key::K_F5; 1592 | break; 1593 | case 0x3F: 1594 | key = scancodes::Key::K_F6; 1595 | break; 1596 | case 0x40: 1597 | key = scancodes::Key::K_F7; 1598 | break; 1599 | case 0x41: 1600 | key = scancodes::Key::K_F8; 1601 | break; 1602 | case 0x42: 1603 | key = scancodes::Key::K_F9; 1604 | break; 1605 | case 0x43: 1606 | key = scancodes::Key::K_F10; 1607 | break; 1608 | case 0x44: 1609 | key = scancodes::Key::K_F11; 1610 | break; 1611 | case 0x45: 1612 | key = scancodes::Key::K_F12; 1613 | break; 1614 | case 0x46: 1615 | key = scancodes::Key::K_PRINT; 1616 | break; 1617 | case 0x47: 1618 | key = scancodes::Key::K_SCROLLOCK; 1619 | break; 1620 | case 0x48: 1621 | key = scancodes::Key::K_PAUSE; 1622 | break; 1623 | case 0x49: 1624 | key = scancodes::Key::K_INSERT; 1625 | break; 1626 | case 0x4A: 1627 | key = scancodes::Key::K_HOME; 1628 | break; 1629 | case 0x4B: 1630 | key = scancodes::Key::K_PAGEUP; 1631 | break; 1632 | case 0x4C: 1633 | key = scancodes::Key::K_DELETE; 1634 | break; 1635 | case 0x4D: 1636 | key = scancodes::Key::K_END; 1637 | break; 1638 | case 0x4E: 1639 | key = scancodes::Key::K_PAGEDOWN; 1640 | break; 1641 | case 0x4F: 1642 | key = scancodes::Key::K_RIGHT; 1643 | break; 1644 | case 0x50: 1645 | key = scancodes::Key::K_LEFT; 1646 | break; 1647 | case 0x51: 1648 | key = scancodes::Key::K_DOWN; 1649 | break; 1650 | case 0x52: 1651 | key = scancodes::Key::K_UP; 1652 | break; 1653 | case 0x53: 1654 | key = scancodes::Key::K_NUMLOCK; 1655 | break; 1656 | case 0x54: 1657 | key = scancodes::Key::K_KP_DIVIDE; 1658 | break; 1659 | case 0x55: 1660 | key = scancodes::Key::K_KP_MULTIPLY; 1661 | break; 1662 | case 0x56: 1663 | key = scancodes::Key::K_KP_MINUS; 1664 | break; 1665 | case 0x57: 1666 | key = scancodes::Key::K_KP_PLUS; 1667 | break; 1668 | case 0x58: 1669 | key = scancodes::Key::K_KP_ENTER; 1670 | break; 1671 | case 0x59: 1672 | key = scancodes::Key::K_KP1; 1673 | break; 1674 | case 0x5A: 1675 | key = scancodes::Key::K_KP2; 1676 | break; 1677 | case 0x5B: 1678 | key = scancodes::Key::K_KP3; 1679 | break; 1680 | case 0x5C: 1681 | key = scancodes::Key::K_KP4; 1682 | break; 1683 | case 0x5D: 1684 | key = scancodes::Key::K_KP5; 1685 | break; 1686 | case 0x5E: 1687 | key = scancodes::Key::K_KP6; 1688 | break; 1689 | case 0x5F: 1690 | key = scancodes::Key::K_KP7; 1691 | break; 1692 | case 0x60: 1693 | key = scancodes::Key::K_KP8; 1694 | break; 1695 | case 0x61: 1696 | key = scancodes::Key::K_KP9; 1697 | break; 1698 | case 0x62: 1699 | key = scancodes::Key::K_KP0; 1700 | break; 1701 | case 0x63: 1702 | key = scancodes::Key::K_KP_PERIOD; 1703 | break; 1704 | case 0x65: 1705 | key = scancodes::Key::K_MENU; 1706 | break; 1707 | case 0x66: 1708 | key = scancodes::Key::K_ACPI_POWER; 1709 | break; 1710 | case 0x74: 1711 | key = scancodes::Key::K_MEDIA_PLAY_PAUSE; 1712 | break; 1713 | case 0x78: 1714 | key = scancodes::Key::K_MEDIA_STOP; 1715 | break; 1716 | case 0x7F: 1717 | key = scancodes::Key::K_MEDIA_MUTE; 1718 | break; 1719 | case 0x80: 1720 | key = scancodes::Key::K_MEDIA_VOLUME_UP; 1721 | break; 1722 | case 0x81: 1723 | key = scancodes::Key::K_MEDIA_VOLUME_DOWN; 1724 | break; 1725 | case 0xE0: 1726 | key = scancodes::Key::K_LCTRL; 1727 | break; 1728 | case 0xE1: 1729 | key = scancodes::Key::K_LSHIFT; 1730 | break; 1731 | case 0xE2: 1732 | key = scancodes::Key::K_LALT; 1733 | break; 1734 | case 0xE3: 1735 | key = scancodes::Key::K_LSUPER; 1736 | break; 1737 | case 0xE4: 1738 | key = scancodes::Key::K_RCTRL; 1739 | break; 1740 | case 0xE5: 1741 | key = scancodes::Key::K_RSHIFT; 1742 | break; 1743 | case 0xE6: 1744 | key = scancodes::Key::K_RALT; 1745 | break; 1746 | case 0xE7: 1747 | key = scancodes::Key::K_RSUPER; 1748 | break; 1749 | 1750 | default: 1751 | return; 1752 | break; 1753 | } 1754 | 1755 | if (keyDown) 1756 | keydown(key); 1757 | else 1758 | keyup(key); 1759 | } 1760 | 1761 | void PS2Keyboard::keyHid_send_CCONTROL(uint16_t btkey, bool keyDown) 1762 | { 1763 | scancodes::Key key; 1764 | switch (btkey) 1765 | { 1766 | case 0xCD: 1767 | key = scancodes::Key::K_MEDIA_PLAY_PAUSE; 1768 | break; 1769 | case 0xE9: 1770 | key = scancodes::Key::K_MEDIA_VOLUME_UP; 1771 | break; 1772 | case 0xEA: 1773 | key = scancodes::Key::K_MEDIA_VOLUME_DOWN; 1774 | break; 1775 | case 0xB6: 1776 | key = scancodes::Key::K_MEDIA_PREV_TRACK; 1777 | break; 1778 | case 0xB5: 1779 | key = scancodes::Key::K_MEDIA_NEXT_TRACK; 1780 | break; 1781 | case 0x183: 1782 | key = scancodes::Key::K_MEDIA_MEDIA_SELECT; 1783 | break; 1784 | case 0x18A: 1785 | key = scancodes::Key::K_MEDIA_EMAIL; 1786 | break; 1787 | case 0xE2: 1788 | key = scancodes::Key::K_MEDIA_MUTE; 1789 | break; 1790 | case 0x221: 1791 | key = scancodes::Key::K_MEDIA_WWW_SEARCH; 1792 | break; 1793 | case 0x223: 1794 | key = scancodes::Key::K_HOME; 1795 | break; 1796 | case 0x196: 1797 | key = scancodes::Key::K_MEDIA_WWW_HOME; 1798 | break; 1799 | case 0x224: 1800 | key = scancodes::Key::K_MEDIA_WWW_BACK; 1801 | break; 1802 | 1803 | default: 1804 | return; 1805 | break; 1806 | } 1807 | 1808 | if (keyDown) 1809 | keydown(key); 1810 | else 1811 | keyup(key); 1812 | } 1813 | 1814 | } // namespace esp32_ps2dev 1815 | -------------------------------------------------------------------------------- /main/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ESP32-BT2PS2 3 | Software for Espressif ESP32 chipsets for PS/2 emulation and Bluetooth BLE/Classic HID keyboard/mouse interfacing. 4 | Thanks to all the pioneers who worked on the code libraries that made this project possible, check them on the README! 5 | Copyright Humberto Mockel - Hamcode - 2024 6 | hamberthm@gmail.com 7 | Dedicated to all who love me and all who I love. 8 | Never stop dreaming. 9 | */ 10 | 11 | #include "..\include\globals.hpp" 12 | #include "nvs_flash.h" 13 | #include "esp_system.h" 14 | #include "driver\gpio.h" 15 | #include 16 | #include 17 | #include "..\include\bt_keyboard.hpp" // Interface with a BT/BLE peripheral device (Keyboard & Mouse) 18 | #include "..\include\esp32-ps2dev.h" // Emulate a PS/2 device 19 | #include "..\include\serial_mouse.h" // Emulate a serial mouse 20 | 21 | /////////////////////////////// USER ADJUSTABLE VARIABLES ////////////////////////////////////////////////// 22 | 23 | // PS/2 emulation variables 24 | const int KB_CLK_PIN = 22; // VERY IMPORTANT: Not all pins are suitable out-of-the-box. Check README for more info! 25 | const int KB_DATA_PIN = 23; 26 | 27 | const int MOUSE_CLK_PIN = 26; // VERY IMPORTANT: Not all pins are suitable out-of-the-box. Check README for more info! 28 | const int MOUSE_DATA_PIN = 25; 29 | 30 | static const int SERIAL_MOUSE_RS232_RTS = 15; // If you're not using serial, do not connect anything to this pin, otherwise PS/2 may not work if pulled down 31 | static const int SERIAL_MOUSE_RS232_RX = 4; 32 | 33 | const bool pairing_at_startup = true; // set to 'true' if you want BT & BLE pairing at startup (slower startup) 34 | // otherwise, pairing must be requested by pressing BOOT button (or set GPIO 0 to ground) during execution 35 | 36 | /////////////////////////////////////////////////////////////////////////////////////////////////////////// 37 | 38 | static constexpr char const *TAG = "BT2PS2 Main Module"; 39 | static bool pairingRequested = false; // pairing requested flag 40 | static bool pairingAborted = false; // pairing aborted flag 41 | static bool bleNowScanning = false; // BLE scanning daemon in course flag 42 | static bool codeHandlerActive = false; 43 | void mouse_task(void *arg); 44 | void ble_connection_daemon(void *arg); 45 | TaskHandle_t pairing_task_handle; 46 | 47 | esp32_ps2dev::PS2Mouse mouse(MOUSE_CLK_PIN, MOUSE_DATA_PIN); 48 | 49 | esp32_ps2dev::PS2Keyboard keyboard(KB_CLK_PIN, KB_DATA_PIN); 50 | 51 | serialMouse mouse_serial; 52 | 53 | // BTKeyboard section 54 | BTKeyboard bt_keyboard; 55 | 56 | int NumDigits(int x) // Returns number of digits in keyboard code 57 | { 58 | x = abs(x); 59 | return (x < 10 ? 1 : (x < 100 ? 2 : (x < 1000 ? 3 : (x < 10000 ? 4 : (x < 100000 ? 5 : (x < 1000000 ? 6 : (x < 10000000 ? 7 : (x < 100000000 ? 8 : (x < 1000000000 ? 9 : 10))))))))); 60 | } 61 | 62 | void pairing_handler(uint32_t pid) // This handler deals with BT Classic's manual code entry. DEFAULT CODE FOR LEGACY PAIRING IS 1234 (When no signaling occurs) 63 | { 64 | codeHandlerActive = true; // LED control taken from main loop 65 | 66 | int x = (int)pid; 67 | std::cout << "Please enter the following pairing code, " 68 | << std::endl 69 | << "followed by ENTER on your keyboard: " 70 | << pid 71 | << std::endl; 72 | 73 | for (int i = 0; i < 10; i++) // Flash quickly many times to alert user of incoming code display 74 | { 75 | gpio_set_level(GPIO_NUM_2, 1); 76 | vTaskDelay(50 / portTICK_PERIOD_MS); 77 | gpio_set_level(GPIO_NUM_2, 0); 78 | vTaskDelay(50 / portTICK_PERIOD_MS); 79 | } 80 | 81 | int dig = NumDigits(x); // How many digits does our code have? 82 | 83 | for (int i = 1; i <= dig; i++) 84 | { 85 | vTaskDelay(2000 / portTICK_PERIOD_MS); 86 | ESP_LOGW(TAG, "PAIRING CODE (TYPE ON KEYBOARD AND PRESS ENTER): %d ", pid); 87 | int flash = ((int)((pid / pow(10, (dig - i)))) % 10); // This extracts one ditit at a time from our code 88 | ESP_LOGI(TAG, "Flashing %d times", flash); 89 | for (int n = 0; n < flash; n++) // Flash the LED as many times as the digit 90 | { 91 | gpio_set_level(GPIO_NUM_2, 1); 92 | vTaskDelay(200 / portTICK_PERIOD_MS); 93 | gpio_set_level(GPIO_NUM_2, 0); 94 | vTaskDelay(200 / portTICK_PERIOD_MS); 95 | } 96 | 97 | if (flash < 1) // If digit is 0, keep a steady light for 1.5sec 98 | { 99 | gpio_set_level(GPIO_NUM_2, 1); 100 | vTaskDelay(1500 / portTICK_PERIOD_MS); 101 | gpio_set_level(GPIO_NUM_2, 0); 102 | vTaskDelay(200 / portTICK_PERIOD_MS); 103 | } 104 | } 105 | 106 | vTaskDelay(1000 / portTICK_PERIOD_MS); 107 | 108 | for (int i = 0; i < 10; i++) // Quick flashes indicate end of code display 109 | { 110 | 111 | gpio_set_level(GPIO_NUM_2, 1); 112 | vTaskDelay(50 / portTICK_PERIOD_MS); 113 | gpio_set_level(GPIO_NUM_2, 0); 114 | vTaskDelay(50 / portTICK_PERIOD_MS); 115 | } 116 | 117 | codeHandlerActive = false; // LED control returned to main loop 118 | } 119 | 120 | static void IRAM_ATTR pairing_scan(void *arg = NULL) 121 | { 122 | BTKeyboard::isConnected = false; 123 | pairingRequested = true; 124 | 125 | gpio_set_level(GPIO_NUM_2, 0); // Turn off LED for pairing 126 | 127 | while (bleNowScanning) 128 | { 129 | vTaskDelay(500 / portTICK_PERIOD_MS); // shield for BLE daemon 130 | } 131 | 132 | ESP_LOGI(TAG, "/////////////////// PAIRING ACTIVATED ////////////////////////"); 133 | ESP_LOGI(TAG, "Scanning..."); 134 | 135 | while (!BTKeyboard::isConnected && !pairingAborted) 136 | { 137 | bt_keyboard.devices_scan(); 138 | if (BTKeyboard::btFound) 139 | { 140 | BTKeyboard::btFound = false; 141 | for (int i = 0; i < 60; i++) 142 | { 143 | if (BTKeyboard::isConnected) 144 | break; 145 | ESP_LOGI(TAG, "Waiting for BT Classic manual code entry..."); 146 | vTaskDelay(1000 / portTICK_PERIOD_MS); 147 | } 148 | } 149 | else 150 | { 151 | vTaskDelay(1000 / portTICK_PERIOD_MS); 152 | ESP_LOGI(TAG, "Pairing re-scan..."); 153 | } 154 | } // Required to discover new keyboards and for pairing 155 | // Default duration is 5 seconds 156 | 157 | ESP_LOGI(TAG, "/////////////////// PAIRING ENDED ////////////////////////"); 158 | 159 | pairingRequested = false; 160 | pairingAborted = false; 161 | gpio_set_level(GPIO_NUM_2, 1); // success, device found (or not and it was aborted, but we are positive in life) 162 | 163 | vTaskDelete(NULL); 164 | 165 | ESP_LOGE(TAG, "Pairing task could not be ended! You shouldn't be reading this, panic!"); 166 | } 167 | 168 | static void IRAM_ATTR start_pairing_scan(void *arg = NULL) 169 | { 170 | xTaskCreatePinnedToCore(pairing_scan, "pairing_task", 4096, NULL, 0, &pairing_task_handle, 0); 171 | } 172 | 173 | extern "C" 174 | { 175 | 176 | void app_main(void) 177 | { 178 | gpio_config_t io_conf; // PIN CONFIGURARION SECTION: CRITICAL 179 | 180 | io_conf.intr_type = GPIO_INTR_DISABLE; 181 | io_conf.mode = GPIO_MODE_OUTPUT_OD; 182 | io_conf.pin_bit_mask = (1ULL << KB_DATA_PIN); 183 | io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; 184 | io_conf.pull_up_en = GPIO_PULLUP_DISABLE; 185 | gpio_config(&io_conf); 186 | io_conf.pin_bit_mask = (1ULL << KB_CLK_PIN); 187 | gpio_config(&io_conf); 188 | 189 | io_conf.intr_type = GPIO_INTR_DISABLE; 190 | io_conf.mode = GPIO_MODE_OUTPUT_OD; 191 | io_conf.pin_bit_mask = (1ULL << MOUSE_DATA_PIN); 192 | io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; 193 | io_conf.pull_up_en = GPIO_PULLUP_DISABLE; 194 | gpio_config(&io_conf); 195 | io_conf.pin_bit_mask = (1ULL << MOUSE_CLK_PIN); 196 | gpio_config(&io_conf); 197 | 198 | io_conf.intr_type = GPIO_INTR_DISABLE; // PAIRING BUTTON CONFIGURATION (GPIO 0 OR "BOOT BUTTON") 199 | io_conf.mode = GPIO_MODE_INPUT; 200 | io_conf.pin_bit_mask = (1ULL << GPIO_NUM_0); 201 | io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; 202 | io_conf.pull_up_en = GPIO_PULLUP_ENABLE; 203 | gpio_config(&io_conf); 204 | 205 | io_conf.intr_type = GPIO_INTR_NEGEDGE; // interrupt for serial mouse 206 | io_conf.mode = GPIO_MODE_INPUT; 207 | io_conf.pin_bit_mask = (1ULL << SERIAL_MOUSE_RS232_RTS); 208 | io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; 209 | io_conf.pull_up_en = GPIO_PULLUP_ENABLE; 210 | gpio_config(&io_conf); 211 | 212 | io_conf.intr_type = GPIO_INTR_DISABLE; 213 | io_conf.mode = GPIO_MODE_OUTPUT; 214 | io_conf.pin_bit_mask = (1ULL << SERIAL_MOUSE_RS232_RX); 215 | io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; 216 | io_conf.pull_up_en = GPIO_PULLUP_DISABLE; 217 | gpio_config(&io_conf); 218 | 219 | // init PS/2 emulation first 220 | 221 | gpio_reset_pin(GPIO_NUM_2); // using built-in LED for notifications 222 | gpio_set_direction(GPIO_NUM_2, GPIO_MODE_OUTPUT); // Set the GPIO as a push/pull output 223 | gpio_set_level(GPIO_NUM_2, 1); 224 | 225 | mouse.begin(true); // true parameter indicates we want to recover previous mouse state from NVS 226 | keyboard.begin(); 227 | mouse_serial.setup(SERIAL_MOUSE_RS232_RTS, SERIAL_MOUSE_RS232_RX); 228 | 229 | gpio_set_level(GPIO_NUM_2, 0); 230 | 231 | // init BTKeyboard 232 | esp_err_t ret; 233 | 234 | // To test the Pairing code entry, uncomment the following line as pairing info is 235 | // kept in the nvs. Pairing will then be required on every boot. 236 | // ESP_ERROR_CHECK(nvs_flash_erase()); 237 | 238 | ret = nvs_flash_init(); 239 | if ((ret == ESP_ERR_NVS_NO_FREE_PAGES) || (ret == ESP_ERR_NVS_NEW_VERSION_FOUND)) 240 | { 241 | ESP_ERROR_CHECK(nvs_flash_erase()); 242 | ret = nvs_flash_init(); 243 | } 244 | ESP_ERROR_CHECK(ret); 245 | 246 | if (bt_keyboard.setup(pairing_handler)) // Must be called once 247 | { 248 | if (pairing_at_startup) 249 | { 250 | start_pairing_scan(); 251 | } 252 | else 253 | { 254 | gpio_set_level(GPIO_NUM_2, 1); // let the fun begin! 255 | } 256 | } 257 | else 258 | { 259 | ESP_LOGE(TAG, "bt_keyboard.setup() returned false. System initialization ABORTED! Post above log error messages in forum to get help. Restarting..."); 260 | vTaskDelay(5000 / portTICK_PERIOD_MS); 261 | esp_restart(); // fun aborted 262 | } 263 | 264 | // time variables, don't adjust unless you know what you're doing 265 | uint8_t typematicRate = 20; // characters per second in Typematic mode 266 | uint16_t typematicDelay = 500; // ms to become Typematic 267 | 268 | // fixed stuff 269 | uint8_t cycle = 1000 / typematicRate; // keywait timeout in ms. Important so we can check connection and do Typematic 270 | TickType_t repeat_period = pdMS_TO_TICKS(cycle); // keywait timeout in ticks. Important so we can check connection and do Typematic 271 | BTKeyboard::KeyInfo info; // freshly received 272 | BTKeyboard::KeyInfo infoBuf; // currently pressed 273 | BTKeyboard::KeyInfo_CCONTROL infoCCONTROL; // freshly received multimedia (CCONTROL) keys 274 | BTKeyboard::KeyInfo_CCONTROL infoBufCCONTROL; // currently pressed multimedia (CCONTROL) keys 275 | TaskHandle_t mouse_task_handle; // mouse uses it's own task. Mouse is important 276 | TaskHandle_t ble_connection_daemon_handle; // the BLE daemon constantly scans for BLE previously bonded devices 277 | 278 | bool found = false; // just an innocent flasg I mean flag 279 | int typematicLeft = typematicDelay; // timekeeping 280 | 281 | info.modifier = infoBuf.modifier = (BTKeyboard::KeyModifier)0; 282 | 283 | for (int j = 0; j < BTKeyboard::MAX_KEY_COUNT; j++) 284 | { 285 | infoBuf.keys[j] = 0; 286 | info.keys[j] = 0; 287 | infoBufCCONTROL.keys[j] = 0; 288 | infoCCONTROL.keys[j] = 0; 289 | } 290 | 291 | xTaskCreatePinnedToCore(mouse_task, "mouse_task", 4096, NULL, 9, &mouse_task_handle, 0); // this lives in core 0 so not to run alongside serial mouse daemon (and getting paused by it) 292 | xTaskCreatePinnedToCore(ble_connection_daemon, "ble_task", 4096, NULL, 0, &ble_connection_daemon_handle, 0); 293 | 294 | while (true) 295 | { 296 | if (bt_keyboard.wait_for_low_event(info, repeat_period)) 297 | { 298 | // Handle modifier keys 299 | if (info.modifier != infoBuf.modifier) 300 | { 301 | 302 | // MODIFIER SECTION 303 | 304 | // Are you a communist? 305 | if (((uint8_t)info.modifier & 0x0f) != ((uint8_t)infoBuf.modifier & 0x0f)) 306 | { 307 | 308 | // LSHIFT 309 | if (((uint8_t)info.modifier & 0x02) != ((uint8_t)infoBuf.modifier & 0x02)) 310 | { 311 | if ((uint8_t)info.modifier & 0x02) 312 | { 313 | ESP_LOGD(TAG, "Down key: LSHIFT"); 314 | keyboard.keydown(esp32_ps2dev::scancodes::Key::K_LSHIFT); 315 | } 316 | else 317 | { 318 | ESP_LOGD(TAG, "Up key: LSHIFT"); 319 | keyboard.keyup(esp32_ps2dev::scancodes::Key::K_LSHIFT); 320 | } 321 | } 322 | 323 | // LCTRL 324 | if (((uint8_t)info.modifier & 0x01) != ((uint8_t)infoBuf.modifier & 0x01)) 325 | { 326 | if ((uint8_t)info.modifier & 0x01) 327 | { 328 | ESP_LOGD(TAG, "Down key: LCTRL"); 329 | keyboard.keydown(esp32_ps2dev::scancodes::Key::K_LCTRL); 330 | } 331 | else 332 | { 333 | ESP_LOGD(TAG, "Up key: LCTRL"); 334 | keyboard.keyup(esp32_ps2dev::scancodes::Key::K_LCTRL); 335 | } 336 | } 337 | 338 | // LMETA 339 | if (((uint8_t)info.modifier & 0x08) != ((uint8_t)infoBuf.modifier & 0x08)) 340 | { 341 | if ((uint8_t)info.modifier & 0x08) 342 | { 343 | ESP_LOGD(TAG, "Down key: LMETA"); 344 | keyboard.keydown(esp32_ps2dev::scancodes::Key::K_LSUPER); 345 | } 346 | else 347 | { 348 | ESP_LOGD(TAG, "Up key: LMETA"); 349 | keyboard.keyup(esp32_ps2dev::scancodes::Key::K_LSUPER); 350 | } 351 | } 352 | 353 | // LALT 354 | if (((uint8_t)info.modifier & 0x04) != ((uint8_t)infoBuf.modifier & 0x04)) 355 | { 356 | if ((uint8_t)info.modifier & 0x04) 357 | { 358 | ESP_LOGD(TAG, "Down key: LALT"); 359 | keyboard.keydown(esp32_ps2dev::scancodes::Key::K_LALT); 360 | } 361 | else 362 | { 363 | ESP_LOGD(TAG, "Up key: LALT"); 364 | keyboard.keyup(esp32_ps2dev::scancodes::Key::K_LALT); 365 | } 366 | } 367 | } 368 | 369 | // Are you a capitalist? 370 | if (((uint8_t)info.modifier & 0xf0) != ((uint8_t)infoBuf.modifier & 0xf0)) 371 | { 372 | 373 | // RSHIFT 374 | if (((uint8_t)info.modifier & 0x20) != ((uint8_t)infoBuf.modifier & 0x20)) 375 | { 376 | if ((uint8_t)info.modifier & 0x20) 377 | { 378 | ESP_LOGD(TAG, "Down key: RSHIFT"); 379 | keyboard.keydown(esp32_ps2dev::scancodes::Key::K_RSHIFT); 380 | } 381 | else 382 | { 383 | ESP_LOGD(TAG, "Up key: RSHIFT"); 384 | keyboard.keyup(esp32_ps2dev::scancodes::Key::K_RSHIFT); 385 | } 386 | } 387 | 388 | // RCTRL 389 | if (((uint8_t)info.modifier & 0x10) != ((uint8_t)infoBuf.modifier & 0x10)) 390 | { 391 | if ((uint8_t)info.modifier & 0x10) 392 | { 393 | ESP_LOGD(TAG, "Down key: RCTRL"); 394 | keyboard.keydown(esp32_ps2dev::scancodes::Key::K_RCTRL); 395 | } 396 | else 397 | { 398 | ESP_LOGD(TAG, "Up key: RCTRL"); 399 | keyboard.keyup(esp32_ps2dev::scancodes::Key::K_RCTRL); 400 | } 401 | } 402 | 403 | // RMETA 404 | if (((uint8_t)info.modifier & 0x80) != ((uint8_t)infoBuf.modifier & 0x80)) 405 | { 406 | if ((uint8_t)info.modifier & 0x80) 407 | { 408 | ESP_LOGD(TAG, "Down key: RMETA"); 409 | keyboard.keydown(esp32_ps2dev::scancodes::Key::K_RSUPER); 410 | } 411 | else 412 | { 413 | ESP_LOGD(TAG, "Up key: RMETA"); 414 | keyboard.keyup(esp32_ps2dev::scancodes::Key::K_RSUPER); 415 | } 416 | } 417 | 418 | // RALT 419 | if (((uint8_t)info.modifier & 0x40) != ((uint8_t)infoBuf.modifier & 0x40)) 420 | { 421 | if ((uint8_t)info.modifier & 0x40) 422 | { 423 | ESP_LOGD(TAG, "Down key: RALT"); 424 | keyboard.keydown(esp32_ps2dev::scancodes::Key::K_RALT); 425 | } 426 | else 427 | { 428 | ESP_LOGD(TAG, "Up key: RALT"); 429 | keyboard.keyup(esp32_ps2dev::scancodes::Key::K_RALT); 430 | } 431 | } 432 | } 433 | } 434 | 435 | // KEY SECTION (always tested) 436 | // release the keys that have been just released 437 | for (int i = 0; i < BTKeyboard::MAX_KEY_COUNT; i++) 438 | { 439 | if (!infoBuf.keys[i]) 440 | { // Detect END FLAG 441 | break; 442 | } 443 | for (int j = 0; (info.keys[j]) && (j < BTKeyboard::MAX_KEY_COUNT); j++) 444 | { 445 | if (infoBuf.keys[i] == info.keys[j]) 446 | { 447 | found = true; 448 | break; 449 | } 450 | } 451 | if (!found) 452 | { 453 | ESP_LOGD(TAG, "Up key: %x", infoBuf.keys[i]); 454 | keyboard.keyHid_send(infoBuf.keys[i], false); 455 | gpio_set_level(GPIO_NUM_2, 1); 456 | } 457 | else 458 | { 459 | found = false; 460 | } 461 | } 462 | 463 | // press the keys that have been just pressed 464 | for (int i = 0; i < BTKeyboard::MAX_KEY_COUNT; i++) 465 | { 466 | if (!info.keys[i]) 467 | { // Detect END FLAG 468 | break; 469 | } 470 | for (int j = 0; (infoBuf.keys[j]) && (j < BTKeyboard::MAX_KEY_COUNT); j++) 471 | { 472 | if (info.keys[i] == infoBuf.keys[j]) 473 | { 474 | found = true; 475 | break; 476 | } 477 | } 478 | if (!found) 479 | { 480 | ESP_LOGD(TAG, "Down key: %x", info.keys[i]); 481 | keyboard.keyHid_send(info.keys[i], true); 482 | gpio_set_level(GPIO_NUM_2, 0); 483 | } 484 | else 485 | { 486 | found = false; 487 | } 488 | } 489 | 490 | infoBuf = info; // Now all the keys are handled, we save the state 491 | typematicLeft = typematicDelay; // Typematic timer reset 492 | } 493 | 494 | else 495 | { 496 | if (infoBuf.keys[0]) 497 | { // If any key held down, do the Typematic dance 498 | typematicLeft = typematicLeft - cycle; 499 | if (typematicLeft <= 0) 500 | { 501 | for (int i = 1; i < BTKeyboard::MAX_KEY_COUNT; i++) 502 | { 503 | if (infoBuf.keys[i] == 0) 504 | { 505 | if (infoBuf.keys[i - 1] != 0x39) 506 | { // Please don't repeat caps, it's ugly 507 | ESP_LOGD(TAG, "Down key: %x", infoBuf.keys[i - 1]); 508 | keyboard.keyHid_send(infoBuf.keys[i - 1], true); // Resend the last key 509 | } 510 | break; 511 | } 512 | } 513 | } 514 | } 515 | 516 | if (!pairingRequested) 517 | { 518 | gpio_set_level(GPIO_NUM_2, 1); // LED up for every key cycle 519 | 520 | if (!gpio_get_level(GPIO_NUM_0)) // Pairing request via BOOT button (GPIO_0) check 521 | { 522 | pairingRequested = true; 523 | start_pairing_scan(); 524 | } 525 | } 526 | else 527 | { 528 | if (!pairingAborted && !codeHandlerActive) // code handler needs LED control, no touchy while is active! 529 | { 530 | gpio_set_level(GPIO_NUM_2, 0); // LED down while pairing is active 531 | } 532 | else if (!codeHandlerActive) 533 | { 534 | gpio_set_level(GPIO_NUM_2, 1); // LED up while pairin is active but abort is requested 535 | } 536 | if (!gpio_get_level(GPIO_NUM_0)) // Pairing request via BOOT button (GPIO_0) check 537 | { 538 | vTaskDelay(2000 / portTICK_PERIOD_MS); 539 | if (!gpio_get_level(GPIO_NUM_0)) 540 | pairingAborted = true; // User pressed and hold the pairing button for 2sec while it was active. This is an abort request! 541 | } 542 | } 543 | } 544 | 545 | ////////////////////// MULTIMEDIA KEYS (CCONTROL HID USAGE CODES) SECTION 546 | 547 | if (bt_keyboard.wait_for_low_event_CCONTROL(infoCCONTROL, 0)) // return immediately if queue empty 548 | { 549 | // KEY SECTION (always tested) 550 | // release the keys that have been just released 551 | for (int i = 0; i < BTKeyboard::MAX_KEY_COUNT; i++) 552 | { 553 | if (!infoBufCCONTROL.keys[i]) // Detect END FLAG 554 | break; 555 | for (int j = 0; (infoCCONTROL.keys[j]) && (j < BTKeyboard::MAX_KEY_COUNT); j++) 556 | { 557 | if (infoBufCCONTROL.keys[i] == infoCCONTROL.keys[j]) 558 | { 559 | found = true; 560 | break; 561 | } 562 | } 563 | if (!found) 564 | { 565 | ESP_LOGD(TAG, "Up CCONTROL key: %x", infoBufCCONTROL.keys[i]); 566 | keyboard.keyHid_send_CCONTROL(infoBufCCONTROL.keys[i], false); 567 | gpio_set_level(GPIO_NUM_2, 1); 568 | } 569 | else 570 | found = false; 571 | } 572 | 573 | // press the keys that have been just pressed 574 | for (int i = 0; i < BTKeyboard::MAX_KEY_COUNT; i++) 575 | { 576 | if (!infoCCONTROL.keys[i]) // Detect END FLAG 577 | break; 578 | for (int j = 0; (infoBufCCONTROL.keys[j]) && (j < BTKeyboard::MAX_KEY_COUNT); j++) 579 | { 580 | if (infoCCONTROL.keys[i] == infoBufCCONTROL.keys[j]) 581 | { 582 | found = true; 583 | break; 584 | } 585 | } 586 | if (!found) 587 | { 588 | ESP_LOGD(TAG, "Down CCONTROL key: %x", infoCCONTROL.keys[i]); 589 | keyboard.keyHid_send_CCONTROL(infoCCONTROL.keys[i], true); 590 | gpio_set_level(GPIO_NUM_2, 0); 591 | } 592 | else 593 | found = false; 594 | } 595 | 596 | infoBufCCONTROL = infoCCONTROL; // Now all the keys are handled, we save the state 597 | } 598 | } 599 | } 600 | } 601 | 602 | void mouse_task(void *arg) 603 | { 604 | BTKeyboard::Mouse_Control infoMouse; // freshly received mouse report 605 | BTKeyboard::Mouse_Control infoMouseBuf; // currently pressed mouse report 606 | 607 | while (true) 608 | { 609 | if (bt_keyboard.wait_for_low_event_MOUSE(infoMouse)) // return immediately if queue empty 610 | { 611 | if (!gpio_get_level((gpio_num_t)SERIAL_MOUSE_RS232_RTS)) // serial connection auto detection, RTS should be high during serial presence 612 | { 613 | mouse_serial.serialMove(infoMouse.mouse_buttons, infoMouse.mouse_x, -infoMouse.mouse_y); 614 | } 615 | else 616 | { 617 | mouse.move(infoMouse.mouse_x, infoMouse.mouse_y, infoMouse.mouse_w); 618 | 619 | // KEY SECTION (always tested) 620 | if ((infoMouse.mouse_buttons) != (infoMouseBuf.mouse_buttons)) 621 | { 622 | if ((infoMouse.mouse_buttons & 0b1) != (infoMouseBuf.mouse_buttons & 0b1)) // change on first button? 623 | { 624 | if (infoMouse.mouse_buttons & 0b1) 625 | { 626 | ESP_LOGD(TAG, "Down Mouse button 1"); 627 | mouse.press(esp32_ps2dev::PS2Mouse::Button::LEFT); 628 | gpio_set_level(GPIO_NUM_2, 0); 629 | } 630 | else 631 | { 632 | ESP_LOGD(TAG, "Up Mouse button 1"); 633 | mouse.release(esp32_ps2dev::PS2Mouse::Button::LEFT); 634 | gpio_set_level(GPIO_NUM_2, 1); 635 | } 636 | } 637 | 638 | if ((infoMouse.mouse_buttons & 0b10) != (infoMouseBuf.mouse_buttons & 0b10)) // change on second button? 639 | { 640 | if (infoMouse.mouse_buttons & 0b10) 641 | { 642 | ESP_LOGD(TAG, "Down Mouse button 2"); 643 | mouse.press(esp32_ps2dev::PS2Mouse::Button::RIGHT); 644 | gpio_set_level(GPIO_NUM_2, 0); 645 | } 646 | else 647 | { 648 | ESP_LOGD(TAG, "Up Mouse button 2"); 649 | mouse.release(esp32_ps2dev::PS2Mouse::Button::RIGHT); 650 | gpio_set_level(GPIO_NUM_2, 1); 651 | } 652 | } 653 | 654 | if ((infoMouse.mouse_buttons & 0b100) != (infoMouseBuf.mouse_buttons & 0b100)) // change on third button? 655 | { 656 | if (infoMouse.mouse_buttons & 0b100) 657 | { 658 | ESP_LOGD(TAG, "Down Mouse button 3"); 659 | mouse.press(esp32_ps2dev::PS2Mouse::Button::MIDDLE); 660 | gpio_set_level(GPIO_NUM_2, 0); 661 | } 662 | else 663 | { 664 | ESP_LOGD(TAG, "Up Mouse button 3"); 665 | mouse.release(esp32_ps2dev::PS2Mouse::Button::MIDDLE); 666 | gpio_set_level(GPIO_NUM_2, 1); 667 | } 668 | } 669 | } 670 | } 671 | 672 | infoMouseBuf = infoMouse; // Now all the keys are handled, we save the state 673 | } 674 | } 675 | } 676 | 677 | void ble_connection_daemon(void *arg) 678 | { 679 | while (true) 680 | { 681 | if (!pairingRequested) 682 | { 683 | bleNowScanning = true; 684 | bt_keyboard.devices_scan_ble_daemon(); 685 | ESP_LOGD(TAG, "RAM left %d", esp_get_free_heap_size()); 686 | } 687 | else 688 | { 689 | ESP_LOGI(TAG, "Pairing requested! Stopping BLE connection daemon..."); 690 | bleNowScanning = false; 691 | while (pairingRequested) 692 | vTaskDelay(5000 / portTICK_PERIOD_MS); 693 | } 694 | } 695 | } -------------------------------------------------------------------------------- /main/serial_mouse.cpp: -------------------------------------------------------------------------------- 1 | // This module contains code from the PS/2 to Serial mouse project by Necroware 2 | // Adapted and modified by Hambert - HamCode - 2024 3 | 4 | #include "..\include\serial_mouse.h" 5 | #include "..\include\esp32-ps2dev.h" 6 | #define NOP() asm volatile("nop") 7 | 8 | static bool threeButtons = false; 9 | 10 | void reset_watchdog(void *args); 11 | void serial_daemon(void *args); 12 | 13 | bool serialMouse::resetReceived = false; 14 | TaskHandle_t serialMouse::handle_reset_watchdog = NULL; 15 | TaskHandle_t serialMouse::handle_serial_daemon = NULL; 16 | int serialMouse::RS232_RTS; 17 | int serialMouse::RS232_RX; 18 | 19 | unsigned long IRAM_ATTR serialMouse::micros() // define inexistent Arduino IDE functions 20 | { 21 | return (unsigned long)(esp_timer_get_time()); 22 | } 23 | 24 | void IRAM_ATTR serialMouse::delayMicroseconds(uint32_t us) 25 | { 26 | uint32_t m = micros(); 27 | if (us) 28 | { 29 | uint32_t e = (m + us); 30 | if (m > e) 31 | { // overflow 32 | while (micros() > e) 33 | { 34 | NOP(); 35 | } 36 | } 37 | while (micros() < e) 38 | { 39 | NOP(); 40 | } 41 | } 42 | } 43 | 44 | template 45 | const T &constrain(const T &x, const T &a, const T &b) 46 | { 47 | if (x < a) 48 | { 49 | return a; 50 | } 51 | else if (b < x) 52 | { 53 | return b; 54 | } 55 | else 56 | return x; 57 | } 58 | 59 | // Delay between the signals to match 1200 baud 60 | static const auto usDelay = 1000000 / 1200; 61 | 62 | void serialMouse::sendSerialBit(int data) 63 | { 64 | gpio_set_level((gpio_num_t)RS232_RX, data); 65 | delayMicroseconds(usDelay); 66 | } 67 | 68 | void serialMouse::sendSerialByte(uint8_t data) 69 | { 70 | portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; 71 | taskENTER_CRITICAL(&mux); 72 | // Start bit 73 | sendSerialBit(0); 74 | 75 | // Data bits 76 | for (int i = 0; i < 7; i++) 77 | { 78 | sendSerialBit((data >> i) & 0x01); 79 | } 80 | 81 | // Stop bit 82 | sendSerialBit(1); 83 | 84 | // 7+1 bits is normal mouse protocol, but some serial controllers 85 | // expect 8+1 bits format. We send additional stop bit to stay 86 | // compatible to that kind of controllers. 87 | sendSerialBit(1); 88 | taskEXIT_CRITICAL(&mux); 89 | } 90 | 91 | void serialMouse::sendToSerial(const serialMouse::Data &data) 92 | { 93 | auto dx = constrain(data.xMovement, -127, 127); 94 | auto dy = constrain(-data.yMovement, -127, 127); 95 | uint8_t lb = (data.buttons & 0b001) ? 0x20 : 0; 96 | uint8_t rb = (data.buttons & 0b010) ? 0x10 : 0; 97 | sendSerialByte(0x40 | lb | rb | ((dy >> 4) & 0xC) | ((dx >> 6) & 0x3)); 98 | sendSerialByte(dx & 0x3F); 99 | sendSerialByte(dy & 0x3F); 100 | if (threeButtons) 101 | { 102 | uint8_t mb = (data.buttons & 0b100) ? 0x20 : 0; 103 | sendSerialByte(mb); 104 | } 105 | } 106 | 107 | void serialMouse::initSerialPort() 108 | { 109 | gpio_set_level((gpio_num_t)RS232_RX, 1); 110 | delayMicroseconds(10000); 111 | sendSerialByte('M'); 112 | if (threeButtons) 113 | { 114 | sendSerialByte('3'); 115 | // ESP_LOGI(TAG, "Init 3-buttons mode"); 116 | } 117 | delayMicroseconds(10000); 118 | } 119 | 120 | void serialMouse::setup(int rtsPin, int rxPin) 121 | { 122 | RS232_RTS = rtsPin; 123 | RS232_RX = rxPin; 124 | 125 | if (handle_reset_watchdog == NULL) 126 | { 127 | xTaskCreatePinnedToCore(reset_watchdog, "serial reset watchdog", 4096, NULL, 10, &handle_reset_watchdog, esp32_ps2dev::DEFAULT_TASK_CORE_MOUSE); 128 | xTaskCreatePinnedToCore(serial_daemon, "Serial daemon", 4096, this, 9, &handle_serial_daemon, esp32_ps2dev::DEFAULT_TASK_CORE_MOUSE); 129 | } 130 | 131 | initSerialPort(); 132 | 133 | ESP_LOGI(TAG, "Setup done"); 134 | } 135 | 136 | void serialMouse::serialMove(uint8_t buttons, int16_t mouseX, int16_t mouseY) 137 | { 138 | report_data.buttons = report_data.buttons | buttons; 139 | report_data.xMovement += mouseX; 140 | report_data.yMovement += mouseY; 141 | 142 | xTaskNotifyGive(handle_serial_daemon); 143 | } 144 | 145 | static void IRAM_ATTR rtsInterrupt(void *args) 146 | { 147 | 148 | BaseType_t xHigherPriorityTaskWoken; 149 | xHigherPriorityTaskWoken = pdFALSE; 150 | 151 | vTaskNotifyGiveFromISR(serialMouse::handle_reset_watchdog, &xHigherPriorityTaskWoken); 152 | } 153 | 154 | void reset_watchdog(void *args) 155 | { 156 | gpio_install_isr_service(0); 157 | gpio_isr_handler_add((gpio_num_t)serialMouse::RS232_RTS, rtsInterrupt, (void *)(gpio_num_t)serialMouse::RS232_RTS); 158 | 159 | serialMouse::resetReceived = false; 160 | 161 | const TickType_t xBlockTime = portMAX_DELAY; 162 | uint32_t ulNotifiedValue; 163 | 164 | while (true) 165 | { 166 | ulNotifiedValue = ulTaskNotifyTake(pdTRUE, xBlockTime); 167 | if (ulNotifiedValue > 0) 168 | { 169 | serialMouse::resetReceived = true; 170 | serialMouse::initSerialPort(); 171 | serialMouse::resetReceived = false; 172 | ESP_LOGI("Serial mouse reset_watchdog: ", "Mouse reset event has been processed."); 173 | } 174 | } 175 | } 176 | 177 | void serial_daemon(void *arg) 178 | { 179 | serialMouse *mouseSer = (serialMouse *)arg; 180 | while (true) 181 | { 182 | xTaskNotifyWait(0, 0, NULL, portMAX_DELAY); 183 | mouseSer->report_buffer = mouseSer->report_data; 184 | mouseSer->report_data.buttons = 0; 185 | mouseSer->report_data.xMovement = 0; 186 | mouseSer->report_data.yMovement = 0; 187 | if (!serialMouse::resetReceived) 188 | mouseSer->sendToSerial(mouseSer->report_buffer); 189 | } 190 | vTaskDelete(NULL); 191 | } -------------------------------------------------------------------------------- /sdkconfig.defaults: -------------------------------------------------------------------------------- 1 | CONFIG_BT_ENABLED=y 2 | CONFIG_BTDM_CTRL_MODE_BTDM=y 3 | CONFIG_BTDM_CTRL_HCI_MODE_VHCI=y 4 | CONFIG_BT_BLUEDROID_ENABLED=y 5 | CONFIG_BT_CLASSIC_ENABLED=y 6 | CONFIG_BT_BLE_ENABLED=y 7 | CONFIG_BT_HID_ENABLED=y 8 | CONFIG_BT_HID_HOST_ENABLED=y 9 | 10 | CONFIG_BT_ACL_CONNECTIONS=9 11 | # CONFIG_BTDM_CONTROLLER_MODEM_SLEEP is not set 12 | CONFIG_BT_GATTC_NOTIF_REG_MAX=20 13 | 14 | CONFIG_FREERTOS_HZ=1000 15 | 16 | # CONFIG_PARTITION_TABLE_SINGLE_APP is not set 17 | CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y 18 | # CONFIG_PARTITION_TABLE_TWO_OTA is not set 19 | # CONFIG_PARTITION_TABLE_CUSTOM is not set 20 | CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" 21 | CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp_large.csv" 22 | CONFIG_PARTITION_TABLE_OFFSET=0x9000 23 | CONFIG_PARTITION_TABLE_MD5=y --------------------------------------------------------------------------------