├── README.md ├── .gitignore ├── .vscode └── extensions.json ├── test └── README ├── platformio.ini ├── lib └── README ├── include └── README └── src └── main.cpp /README.md: -------------------------------------------------------------------------------- 1 | # Web Bluetooth Remote Control 2 | 3 | This is the code for the ESP32. 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .vscode/.browse.c_cpp.db* 3 | .vscode/c_cpp_properties.json 4 | .vscode/launch.json 5 | .vscode/ipch 6 | .vscode/settings.json 7 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ], 7 | "unwantedRecommendations": [ 8 | "ms-vscode.cpptools-extension-pack" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /test/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for PlatformIO Test Runner and project tests. 3 | 4 | Unit Testing is a software testing method by which individual units of 5 | source code, sets of one or more MCU program modules together with associated 6 | control data, usage procedures, and operating procedures, are tested to 7 | determine whether they are fit for use. Unit testing finds problems early 8 | in the development cycle. 9 | 10 | More information about PlatformIO Unit Testing: 11 | - https://docs.platformio.org/en/latest/advanced/unit-testing/index.html 12 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [env:wemos_d1_mini32] 12 | platform = espressif32 13 | board = wemos_d1_mini32 14 | framework = arduino 15 | upload_speed = 921600 16 | monitor_speed = 115200 17 | lib_deps = https://github.com/w00000dy/ESP32-BLE-Combo.git 18 | ;build_flags = -DCORE_DEBUG_LEVEL=3 -------------------------------------------------------------------------------- /lib/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project specific (private) libraries. 3 | PlatformIO will compile them to static libraries and link into executable file. 4 | 5 | The source code of each library should be placed in an own separate directory 6 | ("lib/your_library_name/[here are source files]"). 7 | 8 | For example, see a structure of the following two libraries `Foo` and `Bar`: 9 | 10 | |--lib 11 | | | 12 | | |--Bar 13 | | | |--docs 14 | | | |--examples 15 | | | |--src 16 | | | |- Bar.c 17 | | | |- Bar.h 18 | | | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html 19 | | | 20 | | |--Foo 21 | | | |- Foo.c 22 | | | |- Foo.h 23 | | | 24 | | |- README --> THIS FILE 25 | | 26 | |- platformio.ini 27 | |--src 28 | |- main.c 29 | 30 | and a contents of `src/main.c`: 31 | ``` 32 | #include 33 | #include 34 | 35 | int main (void) 36 | { 37 | ... 38 | } 39 | 40 | ``` 41 | 42 | PlatformIO Library Dependency Finder will find automatically dependent 43 | libraries scanning project source files. 44 | 45 | More information about PlatformIO Library Dependency Finder 46 | - https://docs.platformio.org/page/librarymanager/ldf.html 47 | -------------------------------------------------------------------------------- /include/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project header files. 3 | 4 | A header file is a file containing C declarations and macro definitions 5 | to be shared between several project source files. You request the use of a 6 | header file in your project source file (C, C++, etc) located in `src` folder 7 | by including it, with the C preprocessing directive `#include'. 8 | 9 | ```src/main.c 10 | 11 | #include "header.h" 12 | 13 | int main (void) 14 | { 15 | ... 16 | } 17 | ``` 18 | 19 | Including a header file produces the same results as copying the header file 20 | into each source file that needs it. Such copying would be time-consuming 21 | and error-prone. With a header file, the related declarations appear 22 | in only one place. If they need to be changed, they can be changed in one 23 | place, and programs that include the header file will automatically use the 24 | new version when next recompiled. The header file eliminates the labor of 25 | finding and changing all the copies as well as the risk that a failure to 26 | find one copy will result in inconsistencies within a program. 27 | 28 | In C, the usual convention is to give header files names that end with `.h'. 29 | It is most portable to use only letters, digits, dashes, and underscores in 30 | header file names, and at most one dot. 31 | 32 | Read more about using header files in official GCC documentation: 33 | 34 | * Include Syntax 35 | * Include Operation 36 | * Once-Only Headers 37 | * Computed Includes 38 | 39 | https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html 40 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define LOG_TAG_SC "Serial Controller" 5 | #define TASK_STACK_SIZE 4096 6 | 7 | bool connected = false; 8 | TaskHandle_t serialController; 9 | 10 | void serialControl(void* parameter) { 11 | String command = ""; 12 | for (;;) { 13 | if (Serial.available()) { 14 | int c = Serial.read(); 15 | #if CORE_DEBUG_LEVEL >= 3 16 | Serial.write(c); 17 | #endif 18 | command += (char)c; 19 | if (c == '\x1B') { // ESC 20 | Serial.println("Restarting..."); 21 | ESP.restart(); 22 | } else if (c == ';') { 23 | ESP_LOGI(LOG_TAG_SC, "Core: %d", xPortGetCoreID()); 24 | ESP_LOGI(LOG_TAG_SC, "Stack: %d / %d (%d%%)", uxTaskGetStackHighWaterMark(NULL), TASK_STACK_SIZE, uxTaskGetStackHighWaterMark(NULL) * 100 / TASK_STACK_SIZE); 25 | ESP_LOGI(LOG_TAG_SC, "portTICK_PERIOD_MS: %d", portTICK_PERIOD_MS); 26 | } 27 | if (command[0] == '`') { 28 | if (c == '\n') { 29 | // count the number of commas in the command 30 | size_t commaCount = 0; 31 | for (size_t i = 0; i < command.length(); i++) { 32 | if (command[i] == ',') { 33 | commaCount++; 34 | } 35 | } 36 | 37 | command = command.substring(1); 38 | command.replace("\r", ""); 39 | command.replace("\n", ""); 40 | command.trim(); 41 | ESP_LOGI(LOG_TAG_SC, "Command: %s", command.c_str()); 42 | 43 | const int commaIndex = command.indexOf(','); 44 | if (commaCount == 0) { 45 | if (command == "KEY_LEFT_SHIFT_DOWN") { 46 | Keyboard.press(KEY_LEFT_SHIFT); 47 | } else if (command == "KEY_LEFT_SHIFT_UP") { 48 | Keyboard.release(KEY_LEFT_SHIFT); 49 | } else if (command == "KEY_LEFT_CTRL_DOWN") { 50 | Keyboard.press(KEY_LEFT_CTRL); 51 | } else if (command == "KEY_LEFT_CTRL_UP") { 52 | Keyboard.release(KEY_LEFT_CTRL); 53 | } else if (command == "KEY_LEFT_ALT_DOWN") { 54 | Keyboard.press(KEY_LEFT_ALT); 55 | } else if (command == "KEY_LEFT_ALT_UP") { 56 | Keyboard.release(KEY_LEFT_ALT); 57 | } else if (command == "KEY_RIGHT_ALT_DOWN") { 58 | Keyboard.press(KEY_RIGHT_ALT); 59 | } else if (command == "KEY_RIGHT_ALT_UP") { 60 | Keyboard.release(KEY_RIGHT_ALT); 61 | } else if (command == "KEY_DELETE") { 62 | Keyboard.write(KEY_DELETE); 63 | } else if (command == "KEY_END") { 64 | Keyboard.write(KEY_END); 65 | } else if (command == "KEY_UP_ARROW") { 66 | Keyboard.write(KEY_UP_ARROW); 67 | } else if (command == "KEY_DOWN_ARROW") { 68 | Keyboard.write(KEY_DOWN_ARROW); 69 | } else if (command == "KEY_LEFT_ARROW") { 70 | Keyboard.write(KEY_LEFT_ARROW); 71 | } else if (command == "KEY_RIGHT_ARROW") { 72 | Keyboard.write(KEY_RIGHT_ARROW); 73 | } else if (command == "MOUSE_LEFT_DOWN") { 74 | Mouse.press(MOUSE_LEFT); 75 | } else if (command == "MOUSE_LEFT_UP") { 76 | Mouse.release(MOUSE_LEFT); 77 | } else if (command == "MOUSE_RIGHT_DOWN") { 78 | Mouse.press(MOUSE_RIGHT); 79 | } else if (command == "MOUSE_RIGHT_UP") { 80 | Mouse.release(MOUSE_RIGHT); 81 | } else { 82 | ESP_LOGE(LOG_TAG_SC, "Invalid Command"); 83 | } 84 | } else if (commaCount == 1) { 85 | String xStr = command.substring(0, commaIndex); 86 | String yStr = command.substring(commaIndex + 1); 87 | int x = 0; 88 | int y = 0; 89 | ESP_LOGD(LOG_TAG_SC, "X: '%s', Y: '%s'", xStr.c_str(), yStr.c_str()); 90 | if (xStr != "0") { 91 | x = xStr.toInt(); 92 | if (!x) { 93 | ESP_LOGE(LOG_TAG_SC, "%s (X) is not an integer", xStr.c_str()); 94 | } 95 | } 96 | if (yStr != "0") { 97 | y = yStr.toInt(); 98 | if (!y) { 99 | ESP_LOGE(LOG_TAG_SC, "%s (Y) is not an integer", yStr.c_str()); 100 | } 101 | } 102 | 103 | if (x >= -128 && x <= 127 && y >= -128 && y <= 127) { 104 | ESP_LOGI(LOG_TAG_SC, "Moving mouse to (%d, %d)", x, y); 105 | Mouse.move(x, y); 106 | } else { 107 | Serial.println("One of the mouse values is out of range. Received command: " + command); 108 | } 109 | } else if (commaCount == 2) { 110 | const int comma2Index = command.indexOf(',', commaIndex + 1); 111 | String xStr = command.substring(0, commaIndex); 112 | String yStr = command.substring(commaIndex + 1, comma2Index); 113 | String wheelStr = command.substring(comma2Index + 1); 114 | int x = 0; 115 | int y = 0; 116 | int wheel = 0; 117 | ESP_LOGD(LOG_TAG_SC, "X: '%s', Y: '%s', Wheel: '%s'", xStr.c_str(), yStr.c_str(), wheelStr.c_str()); 118 | if (xStr != "0") { 119 | x = xStr.toInt(); 120 | if (!x) { 121 | ESP_LOGE(LOG_TAG_SC, "%s (X) is not an integer", xStr.c_str()); 122 | } 123 | } 124 | if (yStr != "0") { 125 | y = yStr.toInt(); 126 | if (!y) { 127 | ESP_LOGE(LOG_TAG_SC, "%s (Y) is not an integer", yStr.c_str()); 128 | } 129 | } 130 | if (wheelStr != "0") { 131 | wheel = wheelStr.toInt(); 132 | if (!wheel) { 133 | ESP_LOGE(LOG_TAG_SC, "[Wheel] is not an integer"); 134 | } 135 | } 136 | 137 | if (x >= -128 && x <= 127 && y >= -128 && y <= 127 && wheel >= -128 && wheel <= 127) { 138 | ESP_LOGI(LOG_TAG_SC, "Moving mouse to (%d, %d, %d)", x, y, wheel); 139 | Mouse.move(x, y, wheel); 140 | } else { 141 | Serial.println("One of the mouse values is out of range. Received command: " + command); 142 | } 143 | } else { 144 | ESP_LOGE(LOG_TAG_SC, "Invalid Mouse Command"); 145 | } 146 | 147 | command = ""; 148 | } 149 | } else { 150 | Keyboard.write(c); 151 | command = ""; 152 | } 153 | } 154 | vTaskDelay(1); 155 | } 156 | } 157 | 158 | void initializeBluetoothPairing() { 159 | Keyboard.begin(); 160 | Mouse.begin(); 161 | 162 | Serial.println("The Bluetooth Device Is Ready To Pair"); 163 | } 164 | 165 | void setup() { 166 | Serial.begin(115200); 167 | 168 | ESP_LOGD("Setup", "Core: %d", xPortGetCoreID()); 169 | 170 | xTaskCreate(serialControl, "Serial Contr.", TASK_STACK_SIZE, NULL, 1, &serialController); 171 | 172 | initializeBluetoothPairing(); 173 | } 174 | 175 | void loop() { 176 | if (Keyboard.isConnected() && !connected) { 177 | connected = true; 178 | Serial.println("Connected"); 179 | } else if (!Keyboard.isConnected() && connected) { 180 | connected = false; 181 | Serial.println("Disconnected"); 182 | Keyboard.end(); 183 | Mouse.end(); 184 | initializeBluetoothPairing(); 185 | } 186 | 187 | delay(1); 188 | } --------------------------------------------------------------------------------