├── .gitignore ├── LICENSE ├── README.md ├── firmware ├── CMakeLists.txt ├── main │ ├── CMakeLists.txt │ ├── battery.c │ ├── battery.h │ ├── color_predictor.c │ ├── color_predictor.h │ ├── controller.c │ ├── controller.h │ ├── gap.c │ ├── gap.h │ ├── gatt_svr.c │ ├── gatt_svr.h │ ├── gpio_interrupt.c │ ├── gpio_interrupt.h │ ├── i2c_config.c │ ├── i2c_config.h │ ├── led.c │ ├── led.h │ ├── main.c │ ├── motor.c │ ├── motor.h │ ├── opt4060.c │ └── opt4060.h ├── sdkconfig └── sdkconfig.ci ├── hardware ├── .gitignore ├── racer-sensors │ ├── fp-info-cache │ ├── racer-sensors-v0.1.zip │ ├── sensors-v0.1-BOM.csv │ ├── sensors.kicad_pcb │ ├── sensors.kicad_prl │ ├── sensors.kicad_pro │ └── sensors.kicad_sch ├── racer │ ├── fp-info-cache │ ├── racer-v0.2-BOM.csv │ ├── racer-v0.2.zip │ ├── racer.kicad_pcb │ ├── racer.kicad_prl │ ├── racer.kicad_pro │ └── racer.kicad_sch └── thumbtroller │ ├── controller-bottom-pos.csv │ ├── controller-top-pos.csv │ ├── controller.kicad_pcb │ ├── controller.kicad_prl │ ├── controller.kicad_pro │ ├── controller.kicad_sch │ └── thumbtroller-v0.2.zip ├── mechanical ├── Racer-entire-car.step ├── main-enclosure.step ├── top-enclosure.step └── wheel.step ├── project_pictures ├── 242A0548.png └── 242A1274.png └── scripts ├── README.md ├── ble_device_config.json ├── color_data.txt ├── controller.py ├── nn_model.bin └── trainer.py /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | dependencies.lock 3 | .idea/ 4 | cmake-build-debug/ 5 | sdkconfig.old 6 | venv/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Micro Racer Car - StuckAtPrototype 2 | 3 | *thank you for the overwhelmingly positve support!! such an inspiration to keep making fun open source content!* 4 | 5 | ## Youtube Video 6 | A bit of a back-story of how this project came to be. 7 | 8 | [![My Kickstarter failed, so I open sourced it](https://img.youtube.com/vi/6jzG-BMannc/0.jpg)](https://www.youtube.com/watch?v=6jzG-BMannc) 9 | 10 | Sub if you like what you see. 11 | 12 | 13 | *Some pictures of the project* 14 | 15 | ![Picture of PCB car](project_pictures/242A0548.png) 16 | 17 | ![Picture of PCB car](project_pictures/242A1274.png) 18 | 19 | ## Hardware Remote for this car 20 | 21 | https://github.com/StuckAtPrototype/Thumbtroller 22 | 23 | ## Project Structure 24 | The project consists of 25 | 1. Firmware 26 | 2. Hardware 27 | 3. Mechanical 28 | 4. Scripts 29 | 30 | These are structured into their own files. I could have used submodules, but decided against it. 31 | 32 | ### 1. Firmware 33 | Code for the little car. This lives on the ESP32 34 | 35 | #### Requirements 36 | - ESP32 IDF version 5.3.1 37 | - USB to Serial dongle 38 | - Target set to ESP32-H2 39 | 40 | ### 2. Hardware 41 | 42 | #### Schematic 43 | PDF schematic included for your viewing pleasure. 44 | #### PCBs 45 | All the gerber files you'd need to send to a fab house. 46 | #### Kicad 47 | All the files you'd need to expand and work on this further. If you'd like. 48 | 49 | ### 3. Mechanical 50 | 51 | #### Enclosure 52 | All the step files you need to make one of these. Extrusion printer works well for this part. 53 | 54 | #### Wheels 55 | A bit of caution on this one.. you'll need an SLA printer. 56 | 57 | ### 4. Scripts 58 | Did anyone say neural networks? 59 | 60 | This folder has all the python code you'd need to train up your own neural network for the car. It also consists scripts that let you drive it using a keyboard -- just in case you dont want to make a physical controller. 61 | 62 | #### Requirements 63 | - Python 3 64 | - You'll need to install a bunch of pip modules 65 | 66 | #### Training the neural network 67 | Training the neural network is as simple as running the training script with the data in the `color_data.txt` file. For data format see the sample data in the file. You need to stick to the formatting. 68 | 69 | To train run `python trainer.py` 70 | 71 | #### Keyboard controller 72 | To run the script `python controller.py` 73 | 74 | Use `w` `s` `a` `d` for control. Modify the script for different speeds, etc 75 | 76 | *Protocol for motor control* 77 | 78 | 60,1,60,1,5 -- translates to: 79 | 80 | motor side A: speed 60, direction forward 81 | 82 | motor side B: speed 60, direction forward 83 | 84 | 500 miliseconds run time 85 | 86 | See firmware file `motor.c` if you need more details 87 | 88 | ## What the project could use 89 | 1. Cleanup, but thats true for almost anything out there 90 | 2. Some fun code that makes the little car drive using the color sensor -- think very fancy line follower 91 | 3. LLM integration -- ChatGPT driving a physical little robot? anyone? :) 92 | 93 | ## If you take it further 94 | Let me know if you ever make one of these, I'd love to see it. Seriously, that'd be exciting and inspiring to keep making my projects open source! 95 | 96 | --- 97 | ## License 98 | ### Apache 2.0 -- i.e. use as you'd like 99 | http://www.apache.org/licenses/LICENSE-2.0 100 | 101 | --- 102 | ## Special Thanks 103 | Thanks to Michael Angerer for his open sourced `esp32_ble_ota` project. I used it to get BLE running in this project. His blog post and github repo are a great resource. Check it out. https://github.com/michael-angerer/esp32_ble_ota 104 | -------------------------------------------------------------------------------- /firmware/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following lines of boilerplate have to be in your project's 2 | # CMakeLists in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.16) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(hello_world) 7 | -------------------------------------------------------------------------------- /firmware/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "main.c" "gap.c" "gatt_svr.c" "motor.c" 2 | "controller.c" "led.c" "battery.c" "gpio_interrupt.c" "i2c_config.c" 3 | "opt4060.c" "color_predictor.c" 4 | INCLUDE_DIRS ".") 5 | -------------------------------------------------------------------------------- /firmware/main/battery.c: -------------------------------------------------------------------------------- 1 | #include "battery.h" 2 | #include "esp_adc/adc_oneshot.h" 3 | #include "esp_log.h" 4 | 5 | static const char *TAG = "BATTERY"; 6 | 7 | #define ADC_UNIT ADC_UNIT_1 8 | #define ADC_CHANNEL ADC_CHANNEL_2 // GPIO2 is ADC1_CHANNEL_2 9 | #define ADC_ATTEN ADC_ATTEN_DB_12 10 | #define ADC_BITWIDTH ADC_BITWIDTH_DEFAULT 11 | 12 | static adc_oneshot_unit_handle_t adc1_handle; 13 | 14 | esp_err_t battery_init(void) { 15 | adc_oneshot_unit_init_cfg_t init_config1 = { 16 | .unit_id = ADC_UNIT, 17 | .ulp_mode = ADC_ULP_MODE_DISABLE, 18 | }; 19 | ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config1, &adc1_handle)); 20 | 21 | adc_oneshot_chan_cfg_t config = { 22 | .atten = ADC_ATTEN, 23 | .bitwidth = ADC_BITWIDTH, 24 | }; 25 | ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, ADC_CHANNEL, &config)); 26 | 27 | return ESP_OK; 28 | } 29 | 30 | esp_err_t battery_read_voltage(float *voltage) { 31 | int raw; 32 | esp_err_t ret = adc_oneshot_read(adc1_handle, ADC_CHANNEL, &raw); 33 | if (ret != ESP_OK) { 34 | ESP_LOGE(TAG, "Failed to read ADC value"); 35 | return ret; 36 | } 37 | 38 | // Convert raw ADC value to voltage 39 | // *voltage = (float)raw / (float)(1 << ADC_BITWIDTH) * 3.3f; // Assuming 0-3.3V range 40 | *voltage = (float)raw; 41 | 42 | // 3300 == 2556 43 | // 3700 == 2865 44 | // 4000 == 3025 45 | 46 | // correct the voltage readings 47 | // this is due to us reading through a 10k resistor instead of direct 48 | // this in turn creates a small voltage divider with the resistors inside the esp body 49 | *voltage = (1.4925f * raw - 511.83f) / 1000.0f; 50 | 51 | return ESP_OK; 52 | } 53 | -------------------------------------------------------------------------------- /firmware/main/battery.h: -------------------------------------------------------------------------------- 1 | #ifndef BATTERY_H 2 | #define BATTERY_H 3 | 4 | #include "esp_err.h" 5 | 6 | // Initializes the ADC for battery voltage measurement 7 | esp_err_t battery_init(void); 8 | 9 | // Reads the battery voltage 10 | esp_err_t battery_read_voltage(float *voltage); 11 | 12 | #endif // BATTERY_H 13 | -------------------------------------------------------------------------------- /firmware/main/color_predictor.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "esp_log.h" 4 | #include "color_predictor.h" 5 | 6 | static const char *TAG = "ColorPredictor"; 7 | 8 | static float relu(float x) { 9 | return (x > 0) ? x : 0; 10 | } 11 | 12 | static void softmax(float* input, int size) { 13 | float max = input[0]; 14 | for (int i = 1; i < size; i++) { 15 | if (input[i] > max) { 16 | max = input[i]; 17 | } 18 | } 19 | 20 | float sum = 0; 21 | for (int i = 0; i < size; i++) { 22 | input[i] = expf(input[i] - max); 23 | sum += input[i]; 24 | } 25 | 26 | for (int i = 0; i < size; i++) { 27 | input[i] /= sum; 28 | } 29 | } 30 | 31 | static void forward(NeuralNetwork* nn, float* input, float* output) { 32 | float hidden1[HIDDEN_SIZE1]; 33 | float hidden2[HIDDEN_SIZE2]; 34 | 35 | // Input to first hidden layer 36 | for (int i = 0; i < HIDDEN_SIZE1; i++) { 37 | hidden1[i] = 0; 38 | for (int j = 0; j < INPUT_SIZE; j++) { 39 | hidden1[i] += input[j] * nn->input_weights[j][i]; 40 | } 41 | hidden1[i] = relu(hidden1[i] + nn->hidden_bias1[i]); 42 | } 43 | 44 | // First hidden layer to second hidden layer 45 | for (int i = 0; i < HIDDEN_SIZE2; i++) { 46 | hidden2[i] = 0; 47 | for (int j = 0; j < HIDDEN_SIZE1; j++) { 48 | hidden2[i] += hidden1[j] * nn->hidden_weights1[j][i]; 49 | } 50 | hidden2[i] = relu(hidden2[i] + nn->hidden_bias2[i]); 51 | } 52 | 53 | // Second hidden layer to output 54 | for (int i = 0; i < OUTPUT_SIZE; i++) { 55 | output[i] = 0; 56 | for (int j = 0; j < HIDDEN_SIZE2; j++) { 57 | output[i] += hidden2[j] * nn->hidden_weights2[j][i]; 58 | } 59 | output[i] += nn->output_bias[i]; 60 | } 61 | 62 | // Apply softmax to output 63 | softmax(output, OUTPUT_SIZE); 64 | } 65 | 66 | uint32_t predict_color(NeuralNetwork* nn, float red, float green, float blue, float clear) { 67 | float input[INPUT_SIZE] = {red / 2048.0f, green / 2048.0f, blue / 2048.0f, clear / 2048.0f}; 68 | float output[OUTPUT_SIZE]; 69 | 70 | forward(nn, input, output); 71 | 72 | ESP_LOGD(TAG, "Predicted color probabilities:"); 73 | ESP_LOGD(TAG, "Red: %.2f", output[0]); 74 | ESP_LOGD(TAG, "Black: %.2f", output[1]); 75 | ESP_LOGD(TAG, "Green: %.2f", output[2]); 76 | ESP_LOGD(TAG, "White: %.2f", output[3]); 77 | 78 | const char* color_names[] = {"Red", "Black", "Green", "White"}; 79 | uint32_t max_index = 0; 80 | for (int i = 1; i < OUTPUT_SIZE; i++) { 81 | if (output[i] > output[max_index]) { 82 | max_index = i; 83 | } 84 | } 85 | ESP_LOGW(TAG, "Predicted color: %s", color_names[max_index]); 86 | 87 | return max_index; 88 | } 89 | 90 | void initialize_neural_network(NeuralNetwork* nn) { 91 | // Replace these placeholder values with the actual values generated by the Python script 92 | uint32_t input_weights[INPUT_SIZE][HIDDEN_SIZE1] = { 93 | {0x3dce7ff6, 0x401e01c2, 0x3f459587, 0x400acd7e, 0xbf93b293, 0x3f2cf3d9, 0x3dbe0fee, 0xbe966725, 0xbe3eca2c, 0xbfb2791a, 0xbd73893e, 0xc00ff0f9, 0x3e6a0ceb, 0x3f3c1ffe, 0x3f152aea, 0xbe1e4345}, 94 | {0xbe871da1, 0xbfa83525, 0x3ff85ca0, 0x3ee6bebb, 0x3e3af972, 0xbe95efb6, 0x3f06985c, 0x3fd70757, 0xbf87f079, 0x3f106880, 0x3fa1f1f0, 0x3f40f90e, 0xbfc2e236, 0x3bf3808c, 0xc00b8537, 0xbfc3463b}, 95 | {0x3e4727eb, 0x3fb70b61, 0x3f187bcc, 0x3f89ce30, 0xbf77adbf, 0x3fd85ae7, 0xbc9ef28e, 0x3f915603, 0xbedd5458, 0xbd7e8b58, 0xbf76fde8, 0xbf8105a0, 0xbfa62a86, 0xbd80ff74, 0xbfbf5b8a, 0xbf8c73c0}, 96 | {0xbeb4bb83, 0x3e755932, 0xbda6a6a8, 0xbca989d1, 0xbdcd8bf7, 0x3e90b015, 0xbf4f5c9b, 0x3f2422c6, 0x3f038e7b, 0x3faccd13, 0x3e0d272d, 0x3ff347d2, 0xbf0afc03, 0xbf162bd6, 0x3f8f54f9, 0xbee48cae}, 97 | }; 98 | 99 | 100 | uint32_t hidden_weights1[HIDDEN_SIZE1][HIDDEN_SIZE2] = { 101 | {0xbeddbd91, 0x3f6c2506, 0xbd911552, 0xbe76f383, 0x3ed556cd, 0x3da8e90b, 0x3e49ea95, 0xbeb2d561}, 102 | {0xbf1a278d, 0x3ca261d7, 0x3fedee4f, 0xbfb821c0, 0xbe097b23, 0xbe7e2793, 0x3fab0160, 0xbee746cd}, 103 | {0x3f8c4ec1, 0xbede6a3f, 0x3fc49ba2, 0x3e48b249, 0x3ebb0e0d, 0x3f37e7e8, 0xbf485926, 0x3e7d986a}, 104 | {0xbecc1e5e, 0x3cb84720, 0x3fd03b59, 0xbf795731, 0x3f3fdb72, 0x3edd316c, 0xbe94472d, 0xbe02ab59}, 105 | {0xbe5c034e, 0x3f404120, 0x3f1605c2, 0x3e5eb4f7, 0xbe289aa5, 0x3c2ddfd5, 0xbe87485e, 0xbf22705f}, 106 | {0xbed082a6, 0x3ec899c8, 0x3f5cc247, 0xbdefa97d, 0x3f34ec47, 0xbec36f74, 0xbed7b3cc, 0x3c916d35}, 107 | {0xbdf4806f, 0xbecec7c0, 0xbc73454b, 0x3dfdec3e, 0x3ead244d, 0x3dbaa0f1, 0x3f4ed45e, 0xbdfef530}, 108 | {0x3fd68018, 0xbcb75ceb, 0x3f8dee75, 0xbae6561d, 0xbd807fb9, 0x3df4d7c4, 0xbee11a0a, 0xbdaf9fda}, 109 | {0xbf336719, 0x3c49d37b, 0xbf2c21c1, 0x3e81ad0b, 0x3ddc0c19, 0xbec2aa5b, 0x3f29ba8d, 0xbda87acc}, 110 | {0x3f3f73a8, 0xbf4b89b5, 0xbd185663, 0x3f686f07, 0xbe27f74f, 0xbe28d1fd, 0x3cb4593c, 0x3e810361}, 111 | {0x3f3eef19, 0x3f34aac4, 0x3de40d11, 0xbd210d3d, 0x3c3d2d8a, 0x3e374f9b, 0xbe94b745, 0x3c7a17ab}, 112 | {0x3fc60b9a, 0xbe8e5dd0, 0xbf5e80ec, 0x3f803072, 0xbde40889, 0xbd405f1b, 0x3e839d7f, 0xbe986ca2}, 113 | {0x3e5a3297, 0xbeebd1e2, 0xbd293f51, 0x3e872d1e, 0x3f46442f, 0x3f5b6e08, 0x3f0ba50d, 0x3e823e3c}, 114 | {0x3ee984dd, 0x3db408e0, 0x3e809863, 0xbe9ccc61, 0xbea47da9, 0x3e63df62, 0x3f27a76d, 0xba9d4194}, 115 | {0xbfd16413, 0xbd3cc8e7, 0xbe818b79, 0xbe8174c0, 0xbe35a6aa, 0x3e5ae9db, 0x4006262e, 0xbdb2febc}, 116 | {0x3df66350, 0xbe836eca, 0xbe5d5139, 0xbd7b160e, 0xbbf3ebbc, 0xbf4f0ecc, 0x3e8ccde4, 0x3d26607e}, 117 | }; 118 | 119 | uint32_t hidden_weights2[HIDDEN_SIZE2][OUTPUT_SIZE] = { 120 | {0xbec535c5, 0xc0175ba4, 0x400e5d6e, 0xbf832f19}, 121 | {0x3eda3cf3, 0xbd8a0d44, 0xbf0ae5c1, 0x3ef72346}, 122 | {0x3fa4f9a5, 0xc00bb0ce, 0xbfdd5d0a, 0x3ff993c4}, 123 | {0xbfb11a1a, 0x3e412ab1, 0x3fc9744c, 0xbe8ab1d8}, 124 | {0xbdfd7557, 0xbf07ad4e, 0xbf8cc4c5, 0x3f319475}, 125 | {0x3f255c30, 0xbf4fbc1d, 0x3ed62111, 0x3e0a3a56}, 126 | {0x3f405f36, 0x401f25bd, 0xbf9aabb0, 0xbf78920d}, 127 | {0x3ef9266c, 0xbf11d1bb, 0x3f25a439, 0xbe24bc17}, 128 | }; 129 | 130 | 131 | uint32_t hidden_bias1[HIDDEN_SIZE1] = {0x0, 0x3d26afd5, 0xbee89e2a, 0xbec19fae, 0x0, 0xbe98e479, 0x0, 0xbe172c28, 132 | 0x3ea6bccb, 0x3d61e7b9, 0xbdb4ef70, 0x3ebe1c6d, 0x0, 0xbd7032e0, 0x3f8e17ec, 0x0}; 133 | 134 | uint32_t hidden_bias2[HIDDEN_SIZE2] = {0x3e2b2fc8, 0x3c82fd64, 0x3f4c8c0c, 0xbf009713, 0xbd5baff6, 0x3dc2def8, 0x3f7d654f, 0x0}; 135 | 136 | uint32_t output_bias[OUTPUT_SIZE] = {0x3f1e3c58, 0xbd665b7d, 0x3def0633, 0xbf2db793}; 137 | 138 | // Copy the values to the neural network structure 139 | for (int i = 0; i < INPUT_SIZE; i++) { 140 | for (int j = 0; j < HIDDEN_SIZE1; j++) { 141 | nn->input_weights[i][j] = *(float*)&input_weights[i][j]; 142 | } 143 | } 144 | 145 | for (int i = 0; i < HIDDEN_SIZE1; i++) { 146 | for (int j = 0; j < HIDDEN_SIZE2; j++) { 147 | nn->hidden_weights1[i][j] = *(float*)&hidden_weights1[i][j]; 148 | } 149 | } 150 | 151 | for (int i = 0; i < HIDDEN_SIZE2; i++) { 152 | for (int j = 0; j < OUTPUT_SIZE; j++) { 153 | nn->hidden_weights2[i][j] = *(float*)&hidden_weights2[i][j]; 154 | } 155 | } 156 | 157 | for (int i = 0; i < HIDDEN_SIZE1; i++) { 158 | nn->hidden_bias1[i] = *(float*)&hidden_bias1[i]; 159 | } 160 | 161 | for (int i = 0; i < HIDDEN_SIZE2; i++) { 162 | nn->hidden_bias2[i] = *(float*)&hidden_bias2[i]; 163 | } 164 | 165 | for (int i = 0; i < OUTPUT_SIZE; i++) { 166 | nn->output_bias[i] = *(float*)&output_bias[i]; 167 | } 168 | } 169 | 170 | -------------------------------------------------------------------------------- /firmware/main/color_predictor.h: -------------------------------------------------------------------------------- 1 | #ifndef COLOR_PREDICTOR_H 2 | #define COLOR_PREDICTOR_H 3 | 4 | #include 5 | 6 | #define INPUT_SIZE 4 7 | #define HIDDEN_SIZE1 16 8 | #define HIDDEN_SIZE2 8 9 | #define OUTPUT_SIZE 4 10 | 11 | typedef struct { 12 | float input_weights[INPUT_SIZE][HIDDEN_SIZE1]; 13 | float hidden_weights1[HIDDEN_SIZE1][HIDDEN_SIZE2]; 14 | float hidden_weights2[HIDDEN_SIZE2][OUTPUT_SIZE]; 15 | float hidden_bias1[HIDDEN_SIZE1]; 16 | float hidden_bias2[HIDDEN_SIZE2]; 17 | float output_bias[OUTPUT_SIZE]; 18 | } NeuralNetwork; 19 | 20 | uint32_t predict_color(NeuralNetwork* nn, float red, float green, float blue, float clear); 21 | void initialize_neural_network(NeuralNetwork* nn); 22 | 23 | #endif // COLOR_PREDICTOR_H 24 | -------------------------------------------------------------------------------- /firmware/main/controller.c: -------------------------------------------------------------------------------- 1 | #include "controller.h" 2 | #include "motor.h" 3 | #include 4 | #include "esp_log.h" 5 | 6 | static MotorCommand current_command; 7 | static SemaphoreHandle_t command_mutex; 8 | static TaskHandle_t controller_task_handle = NULL; 9 | static TimerHandle_t command_timer; 10 | static TimerHandle_t command_game_timer; 11 | 12 | // game state 13 | game_status state; 14 | 15 | // we need to make sure to give immunity for 10 seconds after we have a hit an obstacle 16 | static TickType_t cooldown_end = 0; 17 | 18 | void command_set_game_status(uint32_t status){ 19 | 20 | 21 | if(state == GAME_OFF){ 22 | // set the game state 23 | state = status; 24 | uint32_t period = 0; 25 | 26 | TickType_t current_time = xTaskGetTickCount(); 27 | 28 | // set the timer period 29 | switch (status) { 30 | case GAME_GREEN: 31 | if(current_time < cooldown_end){ 32 | ESP_LOGW("controller", "Cooldown. Ignoring."); 33 | period = 0; 34 | // turn off the game effect 35 | state = GAME_OFF; 36 | 37 | } else{ 38 | ESP_LOGW("controller", "Set GREEN"); 39 | led_set_flash_mode(LED_FLASH_ALL); 40 | period = 1000; // 1 second 41 | cooldown_end = current_time + pdMS_TO_TICKS(5000); // 5 second cool down 42 | } 43 | break; 44 | case GAME_RED: 45 | period = 1000; // 1 second 46 | ESP_LOGW("controller", "Set RED"); 47 | break; 48 | case GAME_WHITE: 49 | period = 10000; // 10 seconds 50 | ESP_LOGW("controller", "Set WHITE"); 51 | led_set_flash_mode(LED_FLASH_FRONT_ALTERNATE); 52 | break; 53 | case GAME_BLACK: 54 | period = 10000; // 10 seconds 55 | ESP_LOGW("controller", "Set BLACK"); 56 | led_set_flash_mode(LED_FLASH_BACK); 57 | break; 58 | case GAME_YELLOW: 59 | period = 1000; // 1 second 60 | ESP_LOGW("controller", "Set YELLOW"); 61 | break; 62 | default: 63 | period = 0; 64 | break; 65 | } 66 | 67 | // start the timer 68 | if (xTimerIsTimerActive(command_game_timer) == pdFALSE && period != 0) { 69 | ESP_LOGW("controller", "Starting game timer"); 70 | xTimerChangePeriod(command_game_timer, pdMS_TO_TICKS(period), 0); 71 | xTimerStart(command_game_timer, 0); 72 | 73 | } 74 | } 75 | } 76 | 77 | void command_timer_callback(TimerHandle_t xTimer) 78 | { 79 | ESP_LOGI("controller", "Timer expired, stopping motors"); 80 | 81 | // Set motor speeds to 0 when the timer expires 82 | MotorUpdate update_a = {0, 0, 0}; 83 | MotorUpdate update_b = {1, 0, 0}; 84 | 85 | BaseType_t xStatus; 86 | xStatus = xQueueSend(motor_queue[0], &update_a, 0); 87 | if (xStatus != pdPASS) { 88 | ESP_LOGE("controller","Failed to send stop update for Motor A"); 89 | } 90 | xStatus = xQueueSend(motor_queue[1], &update_b, 0); 91 | if (xStatus != pdPASS) { 92 | ESP_LOGE("controller","Failed to send stop update for Motor B"); 93 | } 94 | 95 | } 96 | 97 | void command_game_timer_callback(TimerHandle_t xTimer) 98 | { 99 | ESP_LOGW("controller", "Game timer expired"); 100 | state = GAME_OFF; 101 | led_set_flash_mode(LED_CONST); 102 | } 103 | 104 | #define SPEEDUP 10 105 | #define SLOWDOWN 10 106 | #define SPINOUT_SPEED 60 107 | 108 | void set_motor_command(MotorCommand command) 109 | { 110 | // modify the command based on the game state 111 | switch (state) { 112 | case GAME_GREEN: // spin out for now 113 | // trying to detect if we are moving forward or not 114 | command.MotorASpeed = SPINOUT_SPEED; 115 | command.MotorADirection = 1; 116 | command.MotorBSpeed = SPINOUT_SPEED; 117 | command.MotorBDirection = 0; 118 | command.seconds = 10; 119 | break; 120 | case GAME_RED: // speed up 121 | // trying to detect if we are moving forward or not 122 | if(command.MotorASpeed < SPEEDUP && command.MotorBSpeed > SPEEDUP){ 123 | command.MotorASpeed += SPEEDUP; 124 | command.MotorBSpeed += SPEEDUP; 125 | } 126 | break; 127 | case GAME_WHITE: // speed up 128 | // trying to detect if we are moving forward or not 129 | if(command.MotorASpeed > SPEEDUP && command.MotorBSpeed > SPEEDUP){ 130 | ESP_LOGW("controller", "speeding up!"); 131 | command.MotorASpeed += SPEEDUP; 132 | command.MotorBSpeed += SPEEDUP; 133 | } 134 | 135 | break; 136 | case GAME_BLACK: // slow down 137 | if(command.MotorASpeed > SLOWDOWN && command.MotorBSpeed > SLOWDOWN){ 138 | command.MotorASpeed -= SLOWDOWN; 139 | command.MotorBSpeed -= SLOWDOWN; 140 | } 141 | break; 142 | case GAME_YELLOW: // spin out 143 | break; 144 | default: 145 | break; 146 | 147 | } 148 | 149 | if (xTimerIsTimerActive(command_timer) == pdFALSE) { 150 | ESP_LOGI("controller", "Starting timer"); 151 | xTimerChangePeriod(command_timer, pdMS_TO_TICKS(command.seconds * 100), 0); 152 | xTimerStart(command_timer, 0); 153 | 154 | } else 155 | { 156 | ESP_LOGI("controller", "Resetting timer"); 157 | xTimerReset(command_timer, 0); 158 | xTimerStart(command_timer, 0); 159 | } 160 | 161 | 162 | current_command = command; 163 | 164 | if (controller_task_handle != NULL) { 165 | // Notify the controller task to process the new command 166 | xTaskNotifyGive(controller_task_handle); 167 | } 168 | 169 | ESP_LOGI("controller"," set_motor_command: Timer set for %lu S", command.seconds); 170 | } 171 | 172 | 173 | 174 | void controller_task(void *pvParameters) 175 | { 176 | while (1) { 177 | // Wait for a notification to process the command 178 | ulTaskNotifyTake(pdTRUE, portMAX_DELAY); 179 | 180 | MotorCommand command = current_command; 181 | 182 | // Send motor updates to the queue 183 | MotorUpdate update_a = {0, command.MotorASpeed, command.MotorADirection}; 184 | MotorUpdate update_b = {1, command.MotorBSpeed, command.MotorBDirection}; 185 | 186 | BaseType_t xStatus; 187 | xStatus = xQueueSend(motor_queue[0], &update_a, 0); 188 | if (xStatus != pdPASS) { 189 | ESP_LOGE("controller", "Failed to send update for Motor A"); 190 | } 191 | xStatus = xQueueSend(motor_queue[1], &update_b, 0); 192 | if (xStatus != pdPASS) { 193 | ESP_LOGE("controller","Failed to send update for Motor B"); 194 | } 195 | 196 | ESP_LOGI("controller","Motor 0 set to speed %d%%, direction %s", command.MotorASpeed, 197 | command.MotorADirection == 0 ? "FORWARD" : "BACKWARD"); 198 | ESP_LOGI("controller","Motor 1 set to speed %d%% , direction %s", command.MotorBSpeed, 199 | command.MotorBDirection == 0 ? "FORWARD" : "BACKWARD"); 200 | 201 | ESP_LOGI("controller","set motor speeds via controller"); 202 | } 203 | } 204 | 205 | void controller_init() 206 | { 207 | command_mutex = xSemaphoreCreateMutex(); 208 | if (command_mutex == NULL) { 209 | ESP_LOGE("controller","Failed to create command mutex"); 210 | return; 211 | } 212 | 213 | command_timer = xTimerCreate("CommandTimer", pdMS_TO_TICKS(1000), pdFALSE, (void *)0, command_timer_callback); 214 | if (command_timer == NULL) { 215 | ESP_LOGE("controller","Failed to create command timer"); 216 | return; 217 | } 218 | 219 | 220 | command_game_timer = xTimerCreate("CommandGameTimer", pdMS_TO_TICKS(1000), 221 | pdFALSE, (void *)0, command_game_timer_callback); 222 | 223 | if (command_game_timer == NULL) { 224 | ESP_LOGE("controller","Failed to create command timer"); 225 | return; 226 | } 227 | 228 | xTaskCreate(controller_task, "controller_task", 2048, NULL, 5, &controller_task_handle); 229 | 230 | // set the game state to GAME_OFF 231 | state = GAME_OFF; 232 | } 233 | -------------------------------------------------------------------------------- /firmware/main/controller.h: -------------------------------------------------------------------------------- 1 | // controller.h 2 | 3 | #ifndef CONTROLLER_H 4 | #define CONTROLLER_H 5 | 6 | #include "freertos/FreeRTOS.h" 7 | #include "freertos/semphr.h" 8 | #include "freertos/task.h" 9 | #include "freertos/timers.h" 10 | 11 | #include "led.h" 12 | 13 | // Define the MotorCommand structure 14 | typedef struct { 15 | int MotorASpeed; 16 | int MotorADirection; 17 | int MotorBSpeed; 18 | int MotorBDirection; 19 | uint32_t seconds; 20 | } MotorCommand; 21 | 22 | // game states 23 | typedef enum { 24 | GAME_RED = 0, 25 | GAME_BLACK, 26 | GAME_GREEN, 27 | GAME_WHITE, 28 | GAME_YELLOW, 29 | GAME_OFF 30 | } game_status; 31 | 32 | void command_set_game_status(uint32_t status); 33 | void set_motor_command(MotorCommand command); 34 | void controller_init(void); 35 | 36 | #endif // CONTROLLER_H 37 | -------------------------------------------------------------------------------- /firmware/main/gap.c: -------------------------------------------------------------------------------- 1 | #include "gap.h" 2 | #include "led.h" 3 | 4 | uint8_t addr_type; 5 | 6 | int gap_event_handler(struct ble_gap_event *event, void *arg); 7 | 8 | void advertise() { 9 | struct ble_gap_adv_params adv_params; 10 | struct ble_hs_adv_fields fields; 11 | int rc; 12 | 13 | memset(&fields, 0, sizeof(fields)); 14 | 15 | // flags: discoverability + BLE only 16 | fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP; 17 | 18 | // include power levels 19 | fields.tx_pwr_lvl_is_present = 1; 20 | fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; 21 | 22 | // include device name 23 | fields.name = (uint8_t *)device_name; 24 | fields.name_len = strlen(device_name); 25 | fields.name_is_complete = 1; 26 | 27 | rc = ble_gap_adv_set_fields(&fields); 28 | if (rc != 0) { 29 | ESP_LOGE(LOG_TAG_GAP, "Error setting advertisement data: rc=%d", rc); 30 | return; 31 | } 32 | 33 | // start advertising 34 | memset(&adv_params, 0, sizeof(adv_params)); 35 | adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; 36 | adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; 37 | rc = ble_gap_adv_start(addr_type, NULL, BLE_HS_FOREVER, &adv_params, 38 | gap_event_handler, NULL); 39 | if (rc != 0) { 40 | ESP_LOGE(LOG_TAG_GAP, "Error enabling advertisement data: rc=%d", rc); 41 | return; 42 | } 43 | } 44 | 45 | void reset_cb(int reason) { 46 | ESP_LOGE(LOG_TAG_GAP, "BLE reset: reason = %d", reason); 47 | } 48 | 49 | void sync_cb(void) { 50 | // determine best adress type 51 | ble_hs_id_infer_auto(0, &addr_type); 52 | 53 | // start avertising 54 | advertise(); 55 | } 56 | 57 | int gap_event_handler(struct ble_gap_event *event, void *arg) { 58 | switch (event->type) { 59 | case BLE_GAP_EVENT_CONNECT: 60 | // A new connection was established or a connection attempt failed 61 | ESP_LOGI(LOG_TAG_GAP, "GAP: Connection %s: status=%d", 62 | event->connect.status == 0 ? "established" : "failed", 63 | event->connect.status); 64 | 65 | // turn on the front LEDS 66 | set_led(3,true); 67 | set_led(2,true); 68 | 69 | break; 70 | 71 | case BLE_GAP_EVENT_DISCONNECT: 72 | ESP_LOGD(LOG_TAG_GAP, "GAP: Disconnect: reason=%d\n", 73 | event->disconnect.reason); 74 | 75 | // turn off the front LEDS 76 | set_led(3,false); 77 | set_led(2,false); 78 | set_led(1,true); 79 | set_led(0,true); 80 | 81 | // Connection terminated; resume advertising 82 | advertise(); 83 | break; 84 | 85 | case BLE_GAP_EVENT_ADV_COMPLETE: 86 | ESP_LOGI(LOG_TAG_GAP, "GAP: adv complete"); 87 | advertise(); 88 | break; 89 | 90 | case BLE_GAP_EVENT_SUBSCRIBE: 91 | ESP_LOGI(LOG_TAG_GAP, "GAP: Subscribe: conn_handle=%d", 92 | event->connect.conn_handle); 93 | 94 | // turn on the front headlines 95 | set_led(0,true); 96 | set_led(1,true); 97 | set_led(2,true); 98 | set_led(3,true); 99 | 100 | break; 101 | 102 | case BLE_GAP_EVENT_MTU: 103 | ESP_LOGI(LOG_TAG_GAP, "GAP: MTU update: conn_handle=%d, mtu=%d", 104 | event->mtu.conn_handle, event->mtu.value); 105 | break; 106 | } 107 | 108 | return 0; 109 | } 110 | 111 | void host_task(void *param) { 112 | // returns only when nimble_port_stop() is executed 113 | nimble_port_run(); 114 | nimble_port_freertos_deinit(); 115 | } 116 | -------------------------------------------------------------------------------- /firmware/main/gap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "esp_log.h" 4 | #include "host/ble_hs.h" 5 | #include "nimble/ble.h" 6 | #include "nimble/nimble_port.h" 7 | #include "nimble/nimble_port_freertos.h" 8 | 9 | #define LOG_TAG_GAP "gap" 10 | 11 | static const char device_name[] = "Racer3"; 12 | 13 | void advertise(); 14 | void reset_cb(int reason); 15 | void sync_cb(void); 16 | void host_task(void *param); -------------------------------------------------------------------------------- /firmware/main/gatt_svr.c: -------------------------------------------------------------------------------- 1 | #include "gatt_svr.h" 2 | #include "mbedtls/aes.h" 3 | #include 4 | #include "controller.h" 5 | 6 | 7 | uint8_t gatt_svr_chr_ota_control_val; 8 | uint8_t gatt_svr_chr_ota_data_val[128]; 9 | 10 | uint16_t ota_control_val_handle; 11 | uint16_t ota_data_val_handle; 12 | 13 | uint16_t num_pkgs_received = 0; 14 | uint16_t packet_size = 0; 15 | 16 | static const char *manuf_name = "DreamNight LLC"; 17 | static const char *model_num = "Racer3"; 18 | 19 | static int gatt_svr_chr_write(struct os_mbuf *om, uint16_t min_len, 20 | uint16_t max_len, void *dst, uint16_t *len); 21 | 22 | static int gatt_svr_chr_ota_control_cb(uint16_t conn_handle, 23 | uint16_t attr_handle, 24 | struct ble_gatt_access_ctxt *ctxt, 25 | void *arg); 26 | 27 | static int gatt_svr_chr_ota_data_cb(uint16_t conn_handle, uint16_t attr_handle, 28 | struct ble_gatt_access_ctxt *ctxt, 29 | void *arg); 30 | 31 | static int gatt_svr_chr_access_device_info(uint16_t conn_handle, 32 | uint16_t attr_handle, 33 | struct ble_gatt_access_ctxt *ctxt, 34 | void *arg); 35 | 36 | static const struct ble_gatt_svc_def gatt_svr_svcs[] = { 37 | {// Service: Device Information 38 | .type = BLE_GATT_SVC_TYPE_PRIMARY, 39 | .uuid = BLE_UUID16_DECLARE(GATT_DEVICE_INFO_UUID), 40 | .characteristics = 41 | (struct ble_gatt_chr_def[]) { 42 | { 43 | // Characteristic: Manufacturer Name 44 | .uuid = BLE_UUID16_DECLARE(GATT_MANUFACTURER_NAME_UUID), 45 | .access_cb = gatt_svr_chr_access_device_info, 46 | .flags = BLE_GATT_CHR_F_READ, 47 | }, 48 | { 49 | // Characteristic: Model Number 50 | .uuid = BLE_UUID16_DECLARE(GATT_MODEL_NUMBER_UUID), 51 | .access_cb = gatt_svr_chr_access_device_info, 52 | .flags = BLE_GATT_CHR_F_READ, 53 | }, 54 | { 55 | 0, 56 | }, 57 | }}, 58 | 59 | { 60 | // service: OTA Service 61 | .type = BLE_GATT_SVC_TYPE_PRIMARY, 62 | .uuid = &gatt_svr_svc_ota_uuid.u, 63 | .characteristics = 64 | (struct ble_gatt_chr_def[]) { 65 | { 66 | // characteristic: OTA control 67 | .uuid = &gatt_svr_chr_ota_control_uuid.u, 68 | .access_cb = gatt_svr_chr_ota_control_cb, 69 | .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE | 70 | BLE_GATT_CHR_F_NOTIFY, 71 | .val_handle = &ota_control_val_handle, 72 | }, 73 | { 74 | // characteristic: OTA data 75 | .uuid = &gatt_svr_chr_ota_data_uuid.u, 76 | .access_cb = gatt_svr_chr_ota_data_cb, 77 | .flags = BLE_GATT_CHR_F_WRITE, 78 | .val_handle = &ota_data_val_handle, 79 | }, 80 | { 81 | 0, 82 | }}, 83 | }, 84 | 85 | { 86 | 0, 87 | }, 88 | }; 89 | 90 | static int gatt_svr_chr_access_device_info(uint16_t conn_handle, 91 | uint16_t attr_handle, 92 | struct ble_gatt_access_ctxt *ctxt, 93 | void *arg) { 94 | uint16_t uuid; 95 | int rc; 96 | 97 | uuid = ble_uuid_u16(ctxt->chr->uuid); 98 | 99 | if (uuid == GATT_MODEL_NUMBER_UUID) { 100 | rc = os_mbuf_append(ctxt->om, model_num, strlen(model_num)); 101 | return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; 102 | } 103 | 104 | if (uuid == GATT_MANUFACTURER_NAME_UUID) { 105 | rc = os_mbuf_append(ctxt->om, manuf_name, strlen(manuf_name)); 106 | return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; 107 | } 108 | 109 | assert(0); 110 | return BLE_ATT_ERR_UNLIKELY; 111 | } 112 | 113 | static int gatt_svr_chr_write(struct os_mbuf *om, uint16_t min_len, 114 | uint16_t max_len, void *dst, uint16_t *len) { 115 | uint16_t om_len; 116 | int rc; 117 | 118 | om_len = OS_MBUF_PKTLEN(om); 119 | if (om_len < min_len || om_len > max_len) { 120 | return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; 121 | } 122 | 123 | rc = ble_hs_mbuf_to_flat(om, dst, max_len, len); 124 | if (rc != 0) { 125 | return BLE_ATT_ERR_UNLIKELY; 126 | } 127 | 128 | return 0; 129 | } 130 | 131 | static void update_ota_control(uint16_t conn_handle) { 132 | struct os_mbuf *om; 133 | esp_err_t err; 134 | 135 | // check which value has been received 136 | switch (gatt_svr_chr_ota_control_val) { 137 | case SVR_CHR_OTA_CONTROL_REQUEST: 138 | // OTA request 139 | ESP_LOGD(LOG_TAG_GATT_SVR, "OTA has been requested via BLE."); 140 | 141 | // is this only the initial request, or does this happen on every transaction? 142 | 143 | // this is saying back to the other device that we are good to go 144 | gatt_svr_chr_ota_control_val = SVR_CHR_OTA_CONTROL_REQUEST_ACK; 145 | 146 | // this would say that we are not good to go 147 | // gatt_svr_chr_ota_control_val = SVR_CHR_OTA_CONTROL_REQUEST_NAK; 148 | 149 | // retrieve the packet size from OTA data 150 | packet_size = 151 | (gatt_svr_chr_ota_data_val[1] << 8) + gatt_svr_chr_ota_data_val[0]; 152 | ESP_LOGI(LOG_TAG_GATT_SVR, "Packet size is: %d", packet_size); 153 | 154 | // clear the var 155 | num_pkgs_received = 0; 156 | 157 | // notify the client via BLE that the OTA has been acknowledged (or not) 158 | om = ble_hs_mbuf_from_flat(&gatt_svr_chr_ota_control_val, 159 | sizeof(gatt_svr_chr_ota_control_val)); 160 | ble_gattc_notify_custom(conn_handle, ota_control_val_handle, om); 161 | ESP_LOGD(LOG_TAG_GATT_SVR, "OTA request acknowledgement has been sent."); 162 | 163 | break; 164 | 165 | // this case is saying we are done with the comms and are closing the channel 166 | case SVR_CHR_OTA_CONTROL_DONE: 167 | 168 | // if needed, we can run checks of any kind here before telling the 169 | // other device if we are ok to terminate the channel 170 | 171 | // i.e. turn off all motors before closing connection 172 | gatt_svr_chr_ota_control_val = SVR_CHR_OTA_CONTROL_DONE_ACK; 173 | 174 | // or 175 | // gatt_svr_chr_ota_control_val = SVR_CHR_OTA_CONTROL_DONE_NAK; 176 | 177 | 178 | // notify the client via BLE that DONE has been acknowledged 179 | om = ble_hs_mbuf_from_flat(&gatt_svr_chr_ota_control_val, 180 | sizeof(gatt_svr_chr_ota_control_val)); 181 | 182 | ble_gattc_notify_custom(conn_handle, ota_control_val_handle, om); 183 | ESP_LOGD(LOG_TAG_GATT_SVR, "OTA DONE acknowledgement has been sent."); 184 | 185 | break; 186 | 187 | default: 188 | break; 189 | } 190 | 191 | 192 | } 193 | 194 | static int gatt_svr_chr_ota_control_cb(uint16_t conn_handle, 195 | uint16_t attr_handle, 196 | struct ble_gatt_access_ctxt *ctxt, 197 | void *arg) { 198 | int rc; 199 | uint8_t length = sizeof(gatt_svr_chr_ota_control_val); 200 | 201 | switch (ctxt->op) { 202 | case BLE_GATT_ACCESS_OP_READ_CHR: 203 | // a client is reading the current value of ota control 204 | rc = os_mbuf_append(ctxt->om, &gatt_svr_chr_ota_control_val, length); 205 | return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; 206 | break; 207 | 208 | case BLE_GATT_ACCESS_OP_WRITE_CHR: 209 | // a client is writing a value to ota control 210 | rc = gatt_svr_chr_write(ctxt->om, 1, length, 211 | &gatt_svr_chr_ota_control_val, NULL); 212 | 213 | // write the new value control: 214 | update_ota_control(conn_handle); 215 | return rc; 216 | break; 217 | 218 | default: 219 | break; 220 | } 221 | 222 | // this shouldn't happen 223 | assert(0); 224 | return BLE_ATT_ERR_UNLIKELY; 225 | } 226 | 227 | 228 | // this is where we will recieve the actual data over characteristic OTA Data 229 | static int gatt_svr_chr_ota_data_cb(uint16_t conn_handle, uint16_t attr_handle, 230 | struct ble_gatt_access_ctxt *ctxt, 231 | void *arg) { 232 | int rc; 233 | esp_err_t err; 234 | 235 | // store the received data into gatt_svr_chr_ota_data_val 236 | rc = gatt_svr_chr_write(ctxt->om, 1, sizeof(gatt_svr_chr_ota_data_val), 237 | gatt_svr_chr_ota_data_val, NULL); 238 | 239 | ESP_LOGI(LOG_TAG_GATT_SVR, "Received packet data:%i, %i, %i, %i, %i", gatt_svr_chr_ota_data_val[0], 240 | gatt_svr_chr_ota_data_val[1], gatt_svr_chr_ota_data_val[2], gatt_svr_chr_ota_data_val[3], 241 | gatt_svr_chr_ota_data_val[4]); 242 | 243 | // Example command to set motor speeds and direction for 10 seconds 244 | MotorCommand command = {gatt_svr_chr_ota_data_val[0], gatt_svr_chr_ota_data_val[1], 245 | gatt_svr_chr_ota_data_val[2], gatt_svr_chr_ota_data_val[3], 246 | gatt_svr_chr_ota_data_val[4]}; 247 | set_motor_command(command); 248 | 249 | num_pkgs_received++; 250 | ESP_LOGI(LOG_TAG_GATT_SVR, "Received packet %d", num_pkgs_received); 251 | 252 | return rc; 253 | } 254 | 255 | void gatt_svr_init() { 256 | ble_svc_gap_init(); 257 | ble_svc_gatt_init(); 258 | ble_gatts_count_cfg(gatt_svr_svcs); 259 | ble_gatts_add_svcs(gatt_svr_svcs); 260 | } 261 | -------------------------------------------------------------------------------- /firmware/main/gatt_svr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "esp_ota_ops.h" 4 | #include "host/ble_hs.h" 5 | #include "host/ble_uuid.h" 6 | #include "services/gap/ble_svc_gap.h" 7 | #include "services/gatt/ble_svc_gatt.h" 8 | 9 | #define LOG_TAG_GATT_SVR "gatt_svr" 10 | #define REBOOT_DEEP_SLEEP_TIMEOUT 500 11 | #define GATT_DEVICE_INFO_UUID 0x180A 12 | #define GATT_MANUFACTURER_NAME_UUID 0x2A29 13 | #define GATT_MODEL_NUMBER_UUID 0x2A24 14 | 15 | typedef enum { 16 | SVR_CHR_OTA_CONTROL_NOP, 17 | SVR_CHR_OTA_CONTROL_REQUEST, 18 | SVR_CHR_OTA_CONTROL_REQUEST_ACK, 19 | SVR_CHR_OTA_CONTROL_REQUEST_NAK, 20 | SVR_CHR_OTA_CONTROL_DONE, 21 | SVR_CHR_OTA_CONTROL_DONE_ACK, 22 | SVR_CHR_OTA_CONTROL_DONE_NAK, 23 | } svr_chr_ota_control_val_t; 24 | 25 | 26 | // service: OTA Service 27 | // d6f1d96d-594c-4c53-b1c6-244a1dfde6d8 28 | static const ble_uuid128_t gatt_svr_svc_ota_uuid = 29 | BLE_UUID128_INIT(0xd8, 0xe6, 0xfd, 0x1d, 0x4a, 024, 0xc6, 0xb1, 0x53, 0x4c, 30 | 0x4c, 0x59, 0x6d, 0xd9, 0xf1, 0xd6); 31 | 32 | // characteristic: OTA Control 33 | // 7ad671aa-21c0-46a4-b722-270e3ae3d830 34 | static const ble_uuid128_t gatt_svr_chr_ota_control_uuid = 35 | BLE_UUID128_INIT(0x30, 0xd8, 0xe3, 0x3a, 0x0e, 0x27, 0x22, 0xb7, 0xa4, 0x46, 36 | 0xc0, 0x21, 0xaa, 0x71, 0xd6, 0x7a); 37 | 38 | // characteristic: OTA Data 39 | // 23408888-1f40-4cd8-9b89-ca8d45f8a5b0 40 | static const ble_uuid128_t gatt_svr_chr_ota_data_uuid = 41 | BLE_UUID128_INIT(0xb0, 0xa5, 0xf8, 0x45, 0x8d, 0xca, 0x89, 0x9b, 0xd8, 0x4c, 42 | 0x40, 0x1f, 0x88, 0x88, 0x40, 0x23); 43 | 44 | 45 | 46 | void gatt_svr_init(); -------------------------------------------------------------------------------- /firmware/main/gpio_interrupt.c: -------------------------------------------------------------------------------- 1 | #include "gpio_interrupt.h" 2 | #include 3 | #include "freertos/task.h" 4 | #include "driver/gpio.h" 5 | 6 | #define GPIO_QUEUE_LENGTH 10 7 | #define GPIO_QUEUE_ITEM_SIZE sizeof(uint32_t) 8 | #define DEBOUNCE_TIME_MS 200 9 | 10 | QueueHandle_t gpio_evt_queue = NULL; 11 | 12 | QueueHandle_t gpio_interrupt_get_evt_queue(void){ 13 | return gpio_evt_queue; 14 | } 15 | 16 | static void IRAM_ATTR gpio_isr_handler(void* arg) 17 | { 18 | static uint32_t last_interrupt_time = 0; 19 | uint32_t interrupt_time = xTaskGetTickCountFromISR(); 20 | uint32_t gpio_num = (uint32_t) arg; 21 | 22 | // Check if it's a falling edge 23 | if (gpio_get_level(gpio_num) == 0) { 24 | // Debounce mechanism 25 | if (interrupt_time - last_interrupt_time > pdMS_TO_TICKS(DEBOUNCE_TIME_MS)) { 26 | xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL); 27 | last_interrupt_time = interrupt_time; 28 | } 29 | } 30 | } 31 | 32 | void configure_gpio_interrupt(void) 33 | { 34 | // Create a queue to handle GPIO event from ISR 35 | gpio_evt_queue = xQueueCreate(GPIO_QUEUE_LENGTH, GPIO_QUEUE_ITEM_SIZE); 36 | 37 | // Configure the GPIO pin 38 | gpio_config_t io_conf = {}; 39 | io_conf.intr_type = GPIO_INTR_NEGEDGE; // Interrupt on falling edge only 40 | io_conf.pin_bit_mask = (1ULL << INTERRUPT_PIN); // Bit mask of the pin 41 | io_conf.mode = GPIO_MODE_INPUT; // Set as input 42 | io_conf.pull_up_en = GPIO_PULLUP_ENABLE; // Enable pull-up resistor 43 | io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; // Disable pull-down resistor 44 | gpio_config(&io_conf); 45 | 46 | // Install GPIO ISR service 47 | gpio_install_isr_service(0); 48 | 49 | // Attach the interrupt service routine 50 | gpio_isr_handler_add(INTERRUPT_PIN, gpio_isr_handler, (void*) INTERRUPT_PIN); 51 | 52 | printf("GPIO %d configured for interrupt on falling edge only with pull-up enabled and %d ms debounce\n", INTERRUPT_PIN, DEBOUNCE_TIME_MS); 53 | } 54 | 55 | -------------------------------------------------------------------------------- /firmware/main/gpio_interrupt.h: -------------------------------------------------------------------------------- 1 | #ifndef GPIO_INTERRUPT_H 2 | #define GPIO_INTERRUPT_H 3 | 4 | #include 5 | #include "freertos/FreeRTOS.h" 6 | #include "freertos/queue.h" 7 | 8 | // Define the interrupt pin 9 | #define INTERRUPT_PIN 10 10 | 11 | // Function prototypes 12 | QueueHandle_t gpio_interrupt_get_evt_queue(void); 13 | void configure_gpio_interrupt(void); 14 | void start_gpio_interrupt_task(void); 15 | 16 | // Declare the queue handle as extern so it can be accessed from main.c 17 | extern QueueHandle_t gpio_evt_queue; 18 | 19 | #endif // GPIO_INTERRUPT_H 20 | -------------------------------------------------------------------------------- /firmware/main/i2c_config.c: -------------------------------------------------------------------------------- 1 | #include "i2c_config.h" 2 | #include "esp_log.h" 3 | 4 | static const char *TAG = "i2c_config"; 5 | 6 | esp_err_t i2c_master_init(void) 7 | { 8 | int i2c_master_port = I2C_MASTER_NUM; 9 | i2c_config_t conf = { 10 | .mode = I2C_MODE_MASTER, 11 | .sda_io_num = I2C_MASTER_SDA_IO, 12 | .sda_pullup_en = GPIO_PULLUP_ENABLE, 13 | .scl_io_num = I2C_MASTER_SCL_IO, 14 | .scl_pullup_en = GPIO_PULLUP_ENABLE, 15 | .master.clk_speed = I2C_MASTER_FREQ_HZ, 16 | }; 17 | 18 | esp_err_t err = i2c_param_config(i2c_master_port, &conf); 19 | if (err != ESP_OK) { 20 | ESP_LOGE(TAG, "I2C parameter configuration failed. Error: %s", esp_err_to_name(err)); 21 | return err; 22 | } 23 | 24 | err = i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0); 25 | if (err != ESP_OK) { 26 | ESP_LOGE(TAG, "I2C driver installation failed. Error: %s", esp_err_to_name(err)); 27 | return err; 28 | } 29 | 30 | ESP_LOGI(TAG, "I2C master initialized successfully"); 31 | return ESP_OK; 32 | } 33 | -------------------------------------------------------------------------------- /firmware/main/i2c_config.h: -------------------------------------------------------------------------------- 1 | #ifndef I2C_CONFIG_H 2 | #define I2C_CONFIG_H 3 | 4 | #include "driver/i2c.h" 5 | 6 | // I2C configuration 7 | #define I2C_MASTER_SCL_IO 0 // GPIO number for I2C master clock (IO0) 8 | #define I2C_MASTER_SDA_IO 1 // GPIO number for I2C master data (IO1) 9 | #define I2C_MASTER_NUM 0 // I2C master i2c port number 10 | #define I2C_MASTER_FREQ_HZ 400000 // I2C master clock frequency (400 kHz) 11 | #define I2C_MASTER_TX_BUF_DISABLE 0 // I2C master doesn't need buffer 12 | #define I2C_MASTER_RX_BUF_DISABLE 0 // I2C master doesn't need buffer 13 | 14 | // Function prototypes 15 | esp_err_t i2c_master_init(void); 16 | 17 | #endif // I2C_CONFIG_H 18 | -------------------------------------------------------------------------------- /firmware/main/led.c: -------------------------------------------------------------------------------- 1 | // led.c 2 | 3 | #include "led.h" 4 | #include "driver/gpio.h" 5 | #include 6 | 7 | // Shared configuration structure 8 | led_config_t led_config = { 9 | .mode = LED_CONST, 10 | .flash_period = pdMS_TO_TICKS(500), // Default 500ms 11 | .led_state = {false, false, false, false} 12 | }; 13 | 14 | static const int led_gpios[NUM_LEDS] = {LED_1_GPIO, LED_2_GPIO, LED_3_GPIO, LED_4_GPIO}; 15 | 16 | static int get_gpio_num(int led_index) 17 | { 18 | if (led_index >= 0 && led_index < NUM_LEDS) { 19 | return led_gpios[led_index]; 20 | } 21 | return -1; 22 | } 23 | 24 | static void led_task(void *pvParameters) 25 | { 26 | TickType_t xLastWakeTime = xTaskGetTickCount(); 27 | bool toggle = false; 28 | 29 | while (1) { 30 | switch (led_config.mode) { 31 | case LED_CONST: 32 | // Apply current LED states 33 | for (int i = 0; i < NUM_LEDS; i++) { 34 | gpio_set_level(led_gpios[i], led_config.led_state[i] ? 1 : 0); 35 | } 36 | vTaskDelay(led_config.flash_period); 37 | break; 38 | case LED_FLASH_ALL: 39 | for (int i = 0; i < NUM_LEDS; i++) { 40 | gpio_set_level(led_gpios[i], toggle ? 1 : 0); 41 | } 42 | toggle = !toggle; 43 | vTaskDelayUntil(&xLastWakeTime, led_config.flash_period); 44 | break; 45 | case LED_FLASH_BACK: 46 | gpio_set_level(led_gpios[0], toggle ? 1 : 0); 47 | gpio_set_level(led_gpios[1], toggle ? 1 : 0); 48 | toggle = !toggle; 49 | vTaskDelayUntil(&xLastWakeTime, led_config.flash_period); 50 | break; 51 | case LED_FLASH_FRONT: 52 | gpio_set_level(led_gpios[2], toggle ? 1 : 0); 53 | gpio_set_level(led_gpios[3], toggle ? 1 : 0); 54 | toggle = !toggle; 55 | vTaskDelayUntil(&xLastWakeTime, led_config.flash_period); 56 | break; 57 | case LED_FLASH_FRONT_ALTERNATE: 58 | gpio_set_level(led_gpios[2], toggle ? 1 : 0); 59 | gpio_set_level(led_gpios[3], toggle ? 0 : 1); 60 | toggle = !toggle; 61 | vTaskDelayUntil(&xLastWakeTime, led_config.flash_period); 62 | break; 63 | } 64 | } 65 | } 66 | 67 | static void configure_led(int gpio) 68 | { 69 | gpio_config_t io_conf = { 70 | .intr_type = GPIO_INTR_DISABLE, 71 | .mode = GPIO_MODE_OUTPUT, 72 | .pin_bit_mask = (1ULL << gpio), 73 | .pull_down_en = 0, 74 | .pull_up_en = 0 75 | }; 76 | gpio_config(&io_conf); 77 | } 78 | 79 | void led_init(void) 80 | { 81 | for (int i = 0; i < NUM_LEDS; i++) { 82 | configure_led(led_gpios[i]); 83 | } 84 | 85 | xTaskCreate(led_task, "led_task", 2048, NULL, 10, NULL); 86 | } 87 | 88 | void set_led(int led_index, bool state) 89 | { 90 | if (led_index < 0 || led_index >= NUM_LEDS) { 91 | printf("Invalid LED index\n"); 92 | return; 93 | } 94 | 95 | led_config.led_state[led_index] = state; 96 | if (led_config.mode == LED_CONST) { 97 | gpio_set_level(led_gpios[led_index], state ? 1 : 0); 98 | } 99 | printf("LED %d (GPIO %d) set to %s\n", led_index, led_gpios[led_index], state ? "ON" : "OFF"); 100 | } 101 | 102 | void led_set_flash_mode(led_flash mode) 103 | { 104 | led_config.mode = mode; 105 | printf("LED flash mode set to %d\n", mode); 106 | } 107 | 108 | void led_set_flash_period(TickType_t period) 109 | { 110 | led_config.flash_period = period; 111 | printf("LED flash period set to %u ticks\n", (unsigned int)period); 112 | } 113 | 114 | void led_all_on(void) 115 | { 116 | for (int i = 0; i < NUM_LEDS; i++) { 117 | set_led(i, true); 118 | } 119 | led_set_flash_mode(LED_CONST); 120 | } 121 | 122 | void led_all_off(void) 123 | { 124 | for (int i = 0; i < NUM_LEDS; i++) { 125 | set_led(i, false); 126 | } 127 | led_set_flash_mode(LED_CONST); 128 | } 129 | 130 | void led_front_on(void) 131 | { 132 | set_led(0, true); 133 | set_led(1, true); 134 | led_set_flash_mode(LED_CONST); 135 | } 136 | 137 | void led_back_on(void) 138 | { 139 | set_led(2, true); 140 | set_led(3, true); 141 | led_set_flash_mode(LED_CONST); 142 | } 143 | -------------------------------------------------------------------------------- /firmware/main/led.h: -------------------------------------------------------------------------------- 1 | // led.h 2 | 3 | #ifndef LED_H 4 | #define LED_H 5 | 6 | #include 7 | #include "freertos/FreeRTOS.h" 8 | #include "freertos/task.h" 9 | 10 | #define NUM_LEDS 4 11 | 12 | #define LED_1_GPIO 27 13 | #define LED_2_GPIO 26 14 | #define LED_3_GPIO 25 15 | #define LED_4_GPIO 22 16 | 17 | // LED states 18 | typedef enum { 19 | LED_CONST = 0, 20 | LED_FLASH_ALL, 21 | LED_FLASH_BACK, 22 | LED_FLASH_FRONT, 23 | LED_FLASH_FRONT_ALTERNATE 24 | } led_flash; 25 | 26 | typedef struct { 27 | led_flash mode; 28 | TickType_t flash_period; 29 | bool led_state[NUM_LEDS]; 30 | } led_config_t; 31 | 32 | // Extern declaration of the shared configuration 33 | extern led_config_t led_config; 34 | 35 | // Original function prototypes 36 | void led_init(void); 37 | void set_led(int led_index, bool state); 38 | 39 | // New function prototypes 40 | void led_set_flash_mode(led_flash mode); 41 | void led_set_flash_period(TickType_t period); 42 | void led_all_on(void); 43 | void led_all_off(void); 44 | void led_front_on(void); 45 | void led_back_on(void); 46 | 47 | #endif // LED_H 48 | -------------------------------------------------------------------------------- /firmware/main/main.c: -------------------------------------------------------------------------------- 1 | // main.c 2 | 3 | #include 4 | #include 5 | #include "sdkconfig.h" 6 | #include "freertos/FreeRTOS.h" 7 | #include "freertos/task.h" 8 | #include "freertos/queue.h" 9 | #include "freertos/semphr.h" 10 | #include "esp_chip_info.h" 11 | #include "esp_flash.h" 12 | #include "esp_system.h" 13 | #include "esp_log.h" 14 | #include "driver/ledc.h" 15 | #include "driver/gpio.h" 16 | #include "motor.h" 17 | 18 | // BLE 19 | #include "gap.h" 20 | #include "gatt_svr.h" 21 | #include "nvs_flash.h" 22 | #include "esp_bt.h" 23 | 24 | #include "controller.h" 25 | #include "led.h" 26 | 27 | // battery 28 | #include "battery.h" 29 | 30 | // interrupt on gpio 31 | #include "gpio_interrupt.h" 32 | 33 | // i2c config for the color sensor 34 | #include "i2c_config.h" 35 | 36 | #include "opt4060.h" 37 | #include "color_predictor.h" 38 | 39 | 40 | #define LEDC_TIMER LEDC_TIMER_0 41 | #define LEDC_MODE LEDC_LOW_SPEED_MODE 42 | #define LEDC_DUTY_RES LEDC_TIMER_10_BIT // Set duty resolution to 13 bits 43 | #define LEDC_FREQUENCY (15000) // Frequency in Hertz. Set frequency at 15 kHz 44 | 45 | #define MOTOR_A_FWD_GPIO 13 46 | #define MOTOR_A_BWD_GPIO 14 47 | #define MOTOR_B_FWD_GPIO 4 48 | #define MOTOR_B_BWD_GPIO 5 49 | 50 | // Global variables 51 | static bool motor_direction[NUM_MOTORS] = {true, true}; // true for forward, false for backward 52 | 53 | QueueHandle_t gpio_intr_evt_queue = NULL; 54 | NeuralNetwork nn; 55 | 56 | void gpio_interrupt_task(void *pvParameters) 57 | { 58 | uint32_t io_num; 59 | for(;;) { 60 | if(xQueueReceive(gpio_intr_evt_queue, &io_num, portMAX_DELAY)) { 61 | printf("GPIO[%lu] interrupt occurred (falling edge)!\n", io_num); 62 | 63 | uint16_t red, green, blue, clear; 64 | opt4060_read_color(&red, &green, &blue, &clear); 65 | ESP_LOGD("main", "Color values - Red: %d, Green: %d, Blue: %d, Clear: %d, Color: White", red, green, blue, clear); 66 | 67 | if (gpio_get_level(INTERRUPT_PIN) == 0) { 68 | uint32_t color = predict_color(&nn, red, green, blue, clear); 69 | command_set_game_status(color); 70 | } 71 | } 72 | } 73 | } 74 | 75 | void app_main(void) 76 | { 77 | ledc_timer_config_t ledc_timer = { 78 | .speed_mode = LEDC_MODE, 79 | .timer_num = LEDC_TIMER, 80 | .duty_resolution = LEDC_DUTY_RES, 81 | .freq_hz = LEDC_FREQUENCY, 82 | .clk_cfg = LEDC_AUTO_CLK 83 | }; 84 | esp_err_t err = ledc_timer_config(&ledc_timer); 85 | if (err != ESP_OK) { 86 | printf("LEDC Timer Config failed: %s\n", esp_err_to_name(err)); 87 | return; 88 | } 89 | 90 | // Initialize LEDs 91 | led_init(); 92 | 93 | // set the blink rate to 100ms 94 | led_set_flash_period(pdMS_TO_TICKS(100)); 95 | 96 | // Configure Motors 97 | configure_motor_pwm(MOTOR_A_FWD_GPIO, LEDC_CHANNEL_0); 98 | configure_motor_pwm(MOTOR_A_BWD_GPIO, LEDC_CHANNEL_1); 99 | configure_motor_pwm(MOTOR_B_FWD_GPIO, LEDC_CHANNEL_2); 100 | configure_motor_pwm(MOTOR_B_BWD_GPIO, LEDC_CHANNEL_3); 101 | 102 | // Create motor queue 103 | motor_queue[0] = xQueueCreate(MOTOR_QUEUE_SIZE, sizeof(MotorUpdate)); 104 | if (motor_queue[0] == NULL) { 105 | printf("Failed to create motor queue\n"); 106 | return; 107 | } 108 | motor_queue[1] = xQueueCreate(MOTOR_QUEUE_SIZE, sizeof(MotorUpdate)); 109 | if (motor_queue[1] == NULL) { 110 | printf("Failed to create motor queue\n"); 111 | return; 112 | } 113 | 114 | // Create semaphore for synchronizing motor start 115 | motor_start_semaphore = xSemaphoreCreateCounting(4,0); 116 | if (motor_start_semaphore == NULL) { 117 | printf("Failed to create motor start semaphore\n"); 118 | return; 119 | } 120 | 121 | // Create motor tasks for Motor A and Motor B 122 | xTaskCreate(motor_task, "motor_task_A", 2048, (void *)0, 1, NULL); 123 | xTaskCreate(motor_task, "motor_task_B", 2048, (void *)1, 1, NULL); 124 | 125 | // Turn on all LEDs 126 | for (int i = 0; i < NUM_LEDS; i++) { 127 | set_led(i, true); 128 | } 129 | 130 | set_led(3,false); 131 | set_led(2,false); 132 | set_led(1,true); 133 | set_led(0,true); 134 | 135 | 136 | // BLE Setup ------------------- 137 | nimble_port_init(); 138 | ble_hs_cfg.sync_cb = sync_cb; 139 | ble_hs_cfg.reset_cb = reset_cb; 140 | gatt_svr_init(); 141 | ble_svc_gap_device_name_set(device_name); 142 | nimble_port_freertos_init(host_task); 143 | 144 | // Initialize the motor controller 145 | controller_init(); 146 | 147 | esp_err_t ret = battery_init(); 148 | if (ret != ESP_OK) { 149 | ESP_LOGE("main", "Battery initialization failed"); 150 | return; 151 | } 152 | 153 | 154 | initialize_neural_network(&nn); 155 | 156 | // initilize interrupt for HAL 157 | configure_gpio_interrupt(); 158 | gpio_intr_evt_queue = gpio_interrupt_get_evt_queue(); 159 | xTaskCreate(gpio_interrupt_task, "gpio_interrupt_task", 2048, NULL, 10, NULL); 160 | 161 | // color sensor 162 | opt4060_init(); 163 | 164 | 165 | while (1) { 166 | 167 | // // uncomment this for training data collection 168 | // uint16_t red, green, blue, clear; 169 | // opt4060_read_color(&red, &green, &blue, &clear); 170 | // // NOTE "Color: White" is hardcoded -- rename this to the color you are training for 171 | // ESP_LOGI("main", "Color values - Red: %d, Green: %d, Blue: %d, Clear: %d, Color: Black", red, green, blue, clear); 172 | 173 | vTaskDelay(100 / portTICK_PERIOD_MS); // Delay 174 | 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /firmware/main/motor.c: -------------------------------------------------------------------------------- 1 | #include "motor.h" 2 | #include 3 | #include "esp_log.h" 4 | 5 | 6 | QueueHandle_t motor_queue[2]; 7 | SemaphoreHandle_t motor_start_semaphore; 8 | 9 | void configure_motor_pwm(int gpio, ledc_channel_t channel) 10 | { 11 | ledc_channel_config_t ledc_channel = { 12 | .speed_mode = LEDC_LOW_SPEED_MODE, 13 | .channel = channel, 14 | .timer_sel = LEDC_TIMER_0, 15 | .intr_type = LEDC_INTR_DISABLE, 16 | .gpio_num = gpio, 17 | .duty = 0, 18 | .hpoint = 0 19 | }; 20 | esp_err_t err = ledc_channel_config(&ledc_channel); 21 | if (err != ESP_OK) { 22 | ESP_LOGE("motor","LEDC Channel Config failed for GPIO %d: %s", gpio, esp_err_to_name(err)); 23 | } 24 | } 25 | 26 | void set_motor_speed(int motor_index, int speed_percent, bool direction) 27 | { 28 | if (motor_index < 0 || motor_index >= NUM_MOTORS) { 29 | ESP_LOGE("motor","Invalid motor index"); 30 | return; 31 | } 32 | 33 | // Ensure speed_percent is between MIN_SPEED_PERCENT and 100 34 | speed_percent = (speed_percent < MIN_SPEED_PERCENT) ? 0 : (speed_percent > 100) ? 100 : speed_percent; 35 | 36 | // Map MIN_SPEED_PERCENT-100 to MIN_DUTY-MAX_DUTY 37 | int duty; 38 | if (speed_percent == 0) { 39 | duty = 0; 40 | } else { 41 | int min_duty = (MIN_SPEED_PERCENT * MAX_DUTY) / 100; 42 | duty = min_duty + ((speed_percent - MIN_SPEED_PERCENT) * (MAX_DUTY - min_duty)) / (100 - MIN_SPEED_PERCENT); 43 | } 44 | 45 | ledc_channel_t fwd_channel = (ledc_channel_t)(motor_index * 2); 46 | ledc_channel_t bwd_channel = (ledc_channel_t)(motor_index * 2 + 1); 47 | 48 | ledc_set_duty(LEDC_LOW_SPEED_MODE, fwd_channel, direction ? duty : 0); 49 | ledc_set_duty(LEDC_LOW_SPEED_MODE, bwd_channel, direction ? 0 : duty); 50 | ledc_update_duty(LEDC_LOW_SPEED_MODE, fwd_channel); 51 | ledc_update_duty(LEDC_LOW_SPEED_MODE, bwd_channel); 52 | 53 | ESP_LOGD("motor","Motor %d set to speed %d%% (duty %d), direction %s", 54 | motor_index, speed_percent, duty, direction ? "FORWARD" : "BACKWARD"); 55 | } 56 | 57 | void soft_start_motor(int motor_index, int target_speed, bool target_direction) 58 | { 59 | static int current_speed[NUM_MOTORS] = {0}; 60 | static bool current_direction[NUM_MOTORS] = {true, true}; 61 | 62 | int start_speed = current_speed[motor_index]; 63 | bool start_direction = current_direction[motor_index]; 64 | 65 | // If direction is changing, first slow down to 0 66 | if (start_direction != target_direction && start_speed > 0) { 67 | ESP_LOGD("motor","slow stopping motor %i", motor_index); 68 | for (int speed = start_speed; speed >= MIN_SPEED_PERCENT; speed--) { 69 | set_motor_speed(motor_index, speed, start_direction); 70 | vTaskDelay(SOFT_START_DELAY_MS / portTICK_PERIOD_MS); 71 | } 72 | set_motor_speed(motor_index, 0, start_direction); 73 | start_speed = 0; 74 | } 75 | 76 | // Now ramp up to target speed in the correct direction 77 | if (target_speed > 0) { 78 | ESP_LOGD("motor","ramping up motor %i", motor_index); 79 | if (start_speed == 0) { 80 | ESP_LOGD("motor","initial speed was 0 %i", motor_index); 81 | set_motor_speed(motor_index, MIN_SPEED_PERCENT, target_direction); 82 | vTaskDelay(SOFT_START_DELAY_MS * 2 / portTICK_PERIOD_MS); // Longer delay for initial start 83 | start_speed = MIN_SPEED_PERCENT; 84 | } 85 | 86 | int step = (target_speed > start_speed) ? 1 : -1; 87 | for (int speed = start_speed; speed != target_speed; speed += step) { 88 | set_motor_speed(motor_index, speed, target_direction); 89 | vTaskDelay(SOFT_START_DELAY_MS / portTICK_PERIOD_MS); 90 | } 91 | } else { 92 | ESP_LOGD("motor","direct set on motor %i", motor_index); 93 | set_motor_speed(motor_index, 0, target_direction); 94 | } 95 | 96 | // Ensure we reach exactly the target speed 97 | set_motor_speed(motor_index, target_speed, target_direction); 98 | 99 | // Update current speed and direction 100 | current_speed[motor_index] = target_speed; 101 | current_direction[motor_index] = target_direction; 102 | ESP_LOGD("motor","done with motor %i", motor_index); 103 | 104 | } 105 | 106 | void motor_task(void *pvParameters) 107 | { 108 | int motor_index = (int)pvParameters; 109 | MotorUpdate update; 110 | while (1) { 111 | if (xQueueReceive(motor_queue[0], &update, pdMS_TO_TICKS(10)) == pdTRUE) { 112 | ESP_LOGI("Motor", "motor index %i speed %i direction %i", update.motor_index, update.speed_percent, 113 | update.direction); 114 | set_motor_speed(update.motor_index, update.speed_percent, update.direction); 115 | } 116 | if (xQueueReceive(motor_queue[1], &update, pdMS_TO_TICKS(10)) == pdTRUE) { 117 | ESP_LOGI("Motor", "motor index %i speed %i direction %i", update.motor_index, update.speed_percent, 118 | update.direction); 119 | set_motor_speed(update.motor_index, update.speed_percent, update.direction); 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /firmware/main/motor.h: -------------------------------------------------------------------------------- 1 | #ifndef MOTOR_H 2 | #define MOTOR_H 3 | 4 | #include "freertos/FreeRTOS.h" 5 | #include "freertos/task.h" 6 | #include "freertos/queue.h" 7 | #include "freertos/semphr.h" 8 | #include "driver/ledc.h" 9 | 10 | #define NUM_MOTORS 2 11 | #define MIN_SPEED_PERCENT 15 // Minimum speed percentage 12 | #define MAX_DUTY ((1 << LEDC_TIMER_10_BIT) - 1) // Max duty cycle for 10-bit resolution 13 | #define MOTOR_QUEUE_SIZE 10 14 | #define SOFT_START_DELAY_MS 30 // Adjust this value to change the softness of the start 15 | 16 | // Motor speed update structure 17 | typedef struct { 18 | int motor_index; 19 | int speed_percent; 20 | bool direction; 21 | } MotorUpdate; 22 | 23 | void configure_motor_pwm(int gpio, ledc_channel_t channel); 24 | void set_motor_speed(int motor_index, int speed_percent, bool direction); 25 | void soft_start_motor(int motor_index, int target_speed, bool target_direction); 26 | void motor_task(void *pvParameters); 27 | 28 | extern QueueHandle_t motor_queue[2]; 29 | extern SemaphoreHandle_t motor_start_semaphore; 30 | 31 | #endif // MOTOR_H 32 | -------------------------------------------------------------------------------- /firmware/main/opt4060.c: -------------------------------------------------------------------------------- 1 | #include "opt4060.h" 2 | #include "driver/i2c.h" 3 | #include "esp_log.h" 4 | 5 | // math 6 | #include 7 | #include 8 | 9 | static const char *TAG = "OPT4060"; 10 | 11 | static esp_err_t i2c_master_init(void) 12 | { 13 | i2c_config_t conf = { 14 | .mode = I2C_MODE_MASTER, 15 | .sda_io_num = I2C_MASTER_SDA_IO, 16 | .scl_io_num = I2C_MASTER_SCL_IO, 17 | .sda_pullup_en = GPIO_PULLUP_ENABLE, 18 | .scl_pullup_en = GPIO_PULLUP_ENABLE, 19 | .master.clk_speed = I2C_MASTER_FREQ_HZ, 20 | }; 21 | 22 | esp_err_t err = i2c_param_config(I2C_MASTER_NUM, &conf); 23 | if (err != ESP_OK) { 24 | ESP_LOGE(TAG, "I2C parameter configuration failed: %s", esp_err_to_name(err)); 25 | return err; 26 | } 27 | 28 | err = i2c_driver_install(I2C_MASTER_NUM, conf.mode, 0, 0, 0); 29 | if (err != ESP_OK) { 30 | if (err == ESP_ERR_INVALID_STATE) { 31 | ESP_LOGW(TAG, "I2C driver already installed, attempting to use existing installation"); 32 | return ESP_OK; 33 | } 34 | ESP_LOGE(TAG, "I2C driver installation failed: %s", esp_err_to_name(err)); 35 | return err; 36 | } 37 | 38 | return ESP_OK; 39 | } 40 | 41 | esp_err_t opt4060_init(void) 42 | { 43 | esp_err_t ret = i2c_master_init(); 44 | if (ret != ESP_OK) { 45 | ESP_LOGE(TAG, "Failed to initialize I2C: %s", esp_err_to_name(ret)); 46 | return ret; 47 | } 48 | 49 | // set the chip to continuous conversion at 1ms 50 | // 0x30, 0x78 - write to 0x0A 51 | uint8_t write_data[3] = {0x0A, 0x30, 0x78}; 52 | ret = i2c_master_write_to_device(I2C_MASTER_NUM, OPT4060_SENSOR_ADDR, write_data, 53 | 3, pdMS_TO_TICKS(1000)); 54 | 55 | if (ret == ESP_OK) 56 | ESP_LOGI(TAG, "OPT4060 initialized successfully"); 57 | else 58 | ESP_LOGE(TAG, "failed to init OPT4060"); 59 | 60 | return ESP_OK; 61 | } 62 | 63 | void determineColor(uint16_t red, uint16_t green, uint16_t blue, uint16_t clear) { 64 | // // Normalize the values 65 | // float r = (float)red / clear; 66 | // float g = (float)green / clear; 67 | // float b = (float)blue / clear; 68 | // 69 | // // Define color thresholds 70 | // float threshold = 0.2; 71 | // float white_threshold = 0.9; 72 | // float black_threshold = 0.1; 73 | // 74 | // // Determine the color 75 | // if (r < black_threshold && g < black_threshold && b < black_threshold) { 76 | // printf("Black\n"); 77 | // } else if (r > white_threshold && g > white_threshold && b > white_threshold) { 78 | // printf("White\n"); 79 | // } else if (r > threshold && r > g && r > b) { 80 | // printf("Red\n"); 81 | // } else if (g > threshold && g > r && g > b) { 82 | // printf("Green\n"); 83 | // } else if (b > threshold && b > r && b > g) { 84 | // printf("Blue\n"); 85 | // } else if (r > threshold && g > threshold && r > b && g > b) { 86 | // printf("Yellow\n"); 87 | // } else if (r > threshold && b > threshold && r > g && b > g) { 88 | // printf("Magenta\n"); 89 | // } else if (g > threshold && b > threshold && g > r && b > r) { 90 | // printf("Cyan\n"); 91 | // } else { 92 | // printf("Unknown\n"); 93 | // } 94 | 95 | // Define thresholds based on the provided examples 96 | const uint16_t GREEN_THRESHOLD = 1000; 97 | const uint16_t BLACK_THRESHOLD = 300; 98 | const uint16_t WHITE_THRESHOLD = 1000; 99 | const uint16_t CLEAR_THRESHOLD = 3000; 100 | 101 | // Calculate ratios for more accurate color detection 102 | float red_ratio = (float)red / (red + green + blue); 103 | float green_ratio = (float)green / (red + green + blue); 104 | float blue_ratio = (float)blue / (red + green + blue); 105 | 106 | // Check for green 107 | if (green > GREEN_THRESHOLD && green_ratio > 0.4) { 108 | ESP_LOGW("opt4060", "Green"); 109 | } 110 | // Check for black 111 | else if (red < BLACK_THRESHOLD && green < BLACK_THRESHOLD && blue < BLACK_THRESHOLD) { 112 | ESP_LOGW("opt4060", "Black"); 113 | } 114 | // Check for white 115 | else if (red > WHITE_THRESHOLD && green > WHITE_THRESHOLD && blue > WHITE_THRESHOLD && clear > CLEAR_THRESHOLD) { 116 | ESP_LOGW("opt4060", "White"); 117 | } 118 | // Check for red 119 | else if (red_ratio > 0.35 && red > green && red > blue) { 120 | ESP_LOGW("opt4060", "Red"); 121 | } 122 | // Check for blue 123 | else if (blue_ratio > 0.35 && blue > red && blue > green) { 124 | ESP_LOGW("opt4060", "Blue"); 125 | } 126 | // If no specific color is detected 127 | else { 128 | ESP_LOGW("opt4060", "Unknown"); 129 | } 130 | 131 | } 132 | 133 | void classify_color(double X, double Y, double Z, double LUX) { 134 | // Convert XYZ to RGB 135 | double R = 3.2404542 * X - 1.5371385 * Y - 0.4985314 * Z; 136 | double G = -0.9692660 * X + 1.8760108 * Y + 0.0415560 * Z; 137 | double B = 0.0556434 * X - 0.2040259 * Y + 1.0572252 * Z; 138 | 139 | // Normalize RGB values 140 | R = fmax(0, fmin(1, R)); 141 | G = fmax(0, fmin(1, G)); 142 | B = fmax(0, fmin(1, B)); 143 | 144 | // Define thresholds 145 | const double COLOR_THRESHOLD = 0.5; 146 | const double BLACK_THRESHOLD = 0.1; 147 | const double WHITE_THRESHOLD = 0.9; 148 | 149 | // Check for black and white first 150 | if (LUX < BLACK_THRESHOLD) { 151 | ESP_LOGW(TAG, "Classified color: Black"); 152 | } else if (LUX > WHITE_THRESHOLD && R > WHITE_THRESHOLD && G > WHITE_THRESHOLD && B > WHITE_THRESHOLD) { 153 | ESP_LOGW(TAG, "Classified color: White"); 154 | } 155 | // Classify based on dominant color 156 | else if (R > COLOR_THRESHOLD && R > G && R > B) { 157 | ESP_LOGW(TAG, "Classified color: Red"); 158 | } else if (G > COLOR_THRESHOLD && G > R && G > B) { 159 | ESP_LOGW(TAG, "Classified color: Green"); 160 | } else if (B > COLOR_THRESHOLD && B > R && B > G) { 161 | ESP_LOGW(TAG, "Classified color: Blue"); 162 | } 163 | // Default case 164 | else { 165 | ESP_LOGW(TAG, "Classified color: Unclassified"); 166 | } 167 | } 168 | 169 | esp_err_t opt4060_read_color(uint16_t *red, uint16_t *green, uint16_t *blue, uint16_t *clear) 170 | { 171 | 172 | uint8_t data[16]; 173 | esp_err_t ret; 174 | 175 | uint8_t address[1] = {0}; 176 | 177 | ret = i2c_master_write_read_device(I2C_MASTER_NUM, OPT4060_SENSOR_ADDR, address, 1, data, 178 | 16, pdMS_TO_TICKS(1000)); 179 | if (ret != ESP_OK) { 180 | ESP_LOGE(TAG, "Failed to read color data: %s", esp_err_to_name(ret)); 181 | return ret; 182 | } 183 | 184 | // Convert the read data to color values 185 | // *red = (data[1] << 8) | data[0]; 186 | // *green = (data[3] << 8) | data[2]; 187 | // *blue = (data[5] << 8) | data[4]; 188 | // *clear = (data[7] << 8) | data[6]; 189 | 190 | *red = ((data[0] & 0xF) << 8) | data[1]; 191 | *green = ((data[4] & 0xF) << 8) | data[5]; 192 | *blue = ((data[8] & 0xF) << 8) | data[9]; 193 | *clear = ((data[12] & 0xF) << 8) | data[13]; 194 | 195 | 196 | 197 | 198 | 199 | return ESP_OK; 200 | } 201 | -------------------------------------------------------------------------------- /firmware/main/opt4060.h: -------------------------------------------------------------------------------- 1 | #ifndef OPT4060_H 2 | #define OPT4060_H 3 | 4 | #include 5 | #include "esp_err.h" 6 | 7 | #define I2C_MASTER_SCL_IO 0 // GPIO number for I2C master clock 8 | #define I2C_MASTER_SDA_IO 1 // GPIO number for I2C master data 9 | #define I2C_MASTER_NUM 0 // I2C master i2c port number 10 | #define I2C_MASTER_FREQ_HZ 100000 // I2C master clock frequency 11 | #define OPT4060_SENSOR_ADDR 0x44 // OPT4060 I2C address (1000100 in binary) 12 | 13 | #define OPT4060_REG_COLOR 0x00 // Register address for color data 14 | 15 | esp_err_t opt4060_init(void); 16 | void determineColor(uint16_t red, uint16_t green, uint16_t blue, uint16_t clear); 17 | void classify_color(double X, double Y, double Z, double LUX); 18 | esp_err_t opt4060_read_color(uint16_t *red, uint16_t *green, uint16_t *blue, uint16_t *clear); 19 | 20 | #endif // OPT4060_H 21 | -------------------------------------------------------------------------------- /firmware/sdkconfig.ci: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuckAtPrototype/Racer/f43d1263155c1e04efdd052b35089c74f59d61d8/firmware/sdkconfig.ci -------------------------------------------------------------------------------- /hardware/.gitignore: -------------------------------------------------------------------------------- 1 | */*-backups/ 2 | */*.kicad_pcb.lck 3 | */*.kicad_sch.lck 4 | -------------------------------------------------------------------------------- /hardware/racer-sensors/racer-sensors-v0.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuckAtPrototype/Racer/f43d1263155c1e04efdd052b35089c74f59d61d8/hardware/racer-sensors/racer-sensors-v0.1.zip -------------------------------------------------------------------------------- /hardware/racer-sensors/sensors-v0.1-BOM.csv: -------------------------------------------------------------------------------- 1 | Id,Designator,Footprint,Quantity,Designation,Supplier and ref,Notes 2 | 1,"R4,R5,R6,R3",R_0603_1608Metric,4,10K,WR06X103 JTL, 3 | 2,"D1,D2",LED_0603_1608Metric,2,LED,IN-S63AT5UW, 4 | 3,"R2,R1",R_0603_1608Metric,2,R,ESR03EZPF4700,"Note: I used a 470ohm resistor, but this might need to be adjusted to vary brightness " 5 | 4,C3,C_0603_1608Metric,1,4.7uF,GRM188R61A106ME69D,Replaced with 10uF 6 | 5,"C1,C2",C_0603_1608Metric,2,0.1uF,CC0603KRX7R7BB104, 7 | 6,U2,SOT-23_TO-236_TDK,1,HAL1501SU-A,HAL1501SU-A, 8 | 7,U1,FCSOT8_DTS_TEX,1,OPT4060DTSR,OPT4060DTSR, 9 | 8,J1,PinHeader_2x03_P1.27mm_Vertical_SMD,1,Conn_02x03_SMT,20021321-00006C4LF, 10 | -------------------------------------------------------------------------------- /hardware/racer-sensors/sensors.kicad_prl: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "active_layer": 36, 4 | "active_layer_preset": "", 5 | "auto_track_width": true, 6 | "hidden_netclasses": [], 7 | "hidden_nets": [], 8 | "high_contrast_mode": 0, 9 | "net_color_mode": 1, 10 | "opacity": { 11 | "images": 0.6, 12 | "pads": 1.0, 13 | "tracks": 1.0, 14 | "vias": 1.0, 15 | "zones": 0.6 16 | }, 17 | "selection_filter": { 18 | "dimensions": true, 19 | "footprints": true, 20 | "graphics": true, 21 | "keepouts": true, 22 | "lockedItems": false, 23 | "otherItems": true, 24 | "pads": true, 25 | "text": true, 26 | "tracks": true, 27 | "vias": true, 28 | "zones": true 29 | }, 30 | "visible_items": [ 31 | 0, 32 | 1, 33 | 2, 34 | 3, 35 | 4, 36 | 5, 37 | 8, 38 | 9, 39 | 10, 40 | 11, 41 | 12, 42 | 13, 43 | 15, 44 | 16, 45 | 17, 46 | 18, 47 | 19, 48 | 20, 49 | 21, 50 | 22, 51 | 23, 52 | 24, 53 | 25, 54 | 26, 55 | 27, 56 | 28, 57 | 29, 58 | 30, 59 | 32, 60 | 33, 61 | 34, 62 | 35, 63 | 36, 64 | 39, 65 | 40 66 | ], 67 | "visible_layers": "ffcffff_ffffffff", 68 | "zone_display_mode": 0 69 | }, 70 | "git": { 71 | "repo_password": "", 72 | "repo_type": "", 73 | "repo_username": "", 74 | "ssh_key": "" 75 | }, 76 | "meta": { 77 | "filename": "sensors.kicad_prl", 78 | "version": 3 79 | }, 80 | "project": { 81 | "files": [] 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /hardware/racer-sensors/sensors.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "3dviewports": [], 4 | "design_settings": { 5 | "defaults": { 6 | "apply_defaults_to_fp_fields": false, 7 | "apply_defaults_to_fp_shapes": false, 8 | "apply_defaults_to_fp_text": false, 9 | "board_outline_line_width": 0.05, 10 | "copper_line_width": 0.2, 11 | "copper_text_italic": false, 12 | "copper_text_size_h": 1.5, 13 | "copper_text_size_v": 1.5, 14 | "copper_text_thickness": 0.3, 15 | "copper_text_upright": false, 16 | "courtyard_line_width": 0.05, 17 | "dimension_precision": 4, 18 | "dimension_units": 3, 19 | "dimensions": { 20 | "arrow_length": 1270000, 21 | "extension_offset": 500000, 22 | "keep_text_aligned": true, 23 | "suppress_zeroes": false, 24 | "text_position": 0, 25 | "units_format": 1 26 | }, 27 | "fab_line_width": 0.1, 28 | "fab_text_italic": false, 29 | "fab_text_size_h": 1.0, 30 | "fab_text_size_v": 1.0, 31 | "fab_text_thickness": 0.15, 32 | "fab_text_upright": false, 33 | "other_line_width": 0.1, 34 | "other_text_italic": false, 35 | "other_text_size_h": 1.0, 36 | "other_text_size_v": 1.0, 37 | "other_text_thickness": 0.15, 38 | "other_text_upright": false, 39 | "pads": { 40 | "drill": 0.762, 41 | "height": 1.524, 42 | "width": 1.524 43 | }, 44 | "silk_line_width": 0.1, 45 | "silk_text_italic": false, 46 | "silk_text_size_h": 1.0, 47 | "silk_text_size_v": 1.0, 48 | "silk_text_thickness": 0.1, 49 | "silk_text_upright": false, 50 | "zones": { 51 | "min_clearance": 0.2032 52 | } 53 | }, 54 | "diff_pair_dimensions": [ 55 | { 56 | "gap": 0.0, 57 | "via_gap": 0.0, 58 | "width": 0.0 59 | } 60 | ], 61 | "drc_exclusions": [], 62 | "meta": { 63 | "version": 2 64 | }, 65 | "rule_severities": { 66 | "annular_width": "error", 67 | "clearance": "error", 68 | "connection_width": "warning", 69 | "copper_edge_clearance": "error", 70 | "copper_sliver": "warning", 71 | "courtyards_overlap": "error", 72 | "diff_pair_gap_out_of_range": "error", 73 | "diff_pair_uncoupled_length_too_long": "error", 74 | "drill_out_of_range": "error", 75 | "duplicate_footprints": "warning", 76 | "extra_footprint": "warning", 77 | "footprint": "error", 78 | "footprint_symbol_mismatch": "warning", 79 | "footprint_type_mismatch": "ignore", 80 | "hole_clearance": "error", 81 | "hole_near_hole": "error", 82 | "invalid_outline": "error", 83 | "isolated_copper": "warning", 84 | "item_on_disabled_layer": "error", 85 | "items_not_allowed": "error", 86 | "length_out_of_range": "error", 87 | "lib_footprint_issues": "warning", 88 | "lib_footprint_mismatch": "warning", 89 | "malformed_courtyard": "error", 90 | "microvia_drill_out_of_range": "error", 91 | "missing_courtyard": "ignore", 92 | "missing_footprint": "warning", 93 | "net_conflict": "warning", 94 | "npth_inside_courtyard": "ignore", 95 | "padstack": "warning", 96 | "pth_inside_courtyard": "ignore", 97 | "shorting_items": "error", 98 | "silk_edge_clearance": "warning", 99 | "silk_over_copper": "warning", 100 | "silk_overlap": "warning", 101 | "skew_out_of_range": "error", 102 | "solder_mask_bridge": "error", 103 | "starved_thermal": "error", 104 | "text_height": "warning", 105 | "text_thickness": "warning", 106 | "through_hole_pad_without_hole": "error", 107 | "too_many_vias": "error", 108 | "track_dangling": "warning", 109 | "track_width": "error", 110 | "tracks_crossing": "error", 111 | "unconnected_items": "error", 112 | "unresolved_variable": "error", 113 | "via_dangling": "warning", 114 | "zones_intersect": "error" 115 | }, 116 | "rules": { 117 | "max_error": 0.005, 118 | "min_clearance": 0.1524, 119 | "min_connection": 0.0, 120 | "min_copper_edge_clearance": 0.0, 121 | "min_hole_clearance": 0.1524, 122 | "min_hole_to_hole": 0.127, 123 | "min_microvia_diameter": 0.2, 124 | "min_microvia_drill": 0.1, 125 | "min_resolved_spokes": 1, 126 | "min_silk_clearance": 0.0, 127 | "min_text_height": 0.508, 128 | "min_text_thickness": 0.127, 129 | "min_through_hole_diameter": 0.254, 130 | "min_track_width": 0.1524, 131 | "min_via_annular_width": 0.127, 132 | "min_via_diameter": 0.508, 133 | "solder_mask_to_copper_clearance": 0.0, 134 | "use_height_for_length_calcs": true 135 | }, 136 | "teardrop_options": [ 137 | { 138 | "td_onpadsmd": true, 139 | "td_onroundshapesonly": false, 140 | "td_ontrackend": false, 141 | "td_onviapad": true 142 | } 143 | ], 144 | "teardrop_parameters": [ 145 | { 146 | "td_allow_use_two_tracks": true, 147 | "td_curve_segcount": 0, 148 | "td_height_ratio": 1.0, 149 | "td_length_ratio": 0.5, 150 | "td_maxheight": 2.0, 151 | "td_maxlen": 1.0, 152 | "td_on_pad_in_zone": false, 153 | "td_target_name": "td_round_shape", 154 | "td_width_to_size_filter_ratio": 0.9 155 | }, 156 | { 157 | "td_allow_use_two_tracks": true, 158 | "td_curve_segcount": 0, 159 | "td_height_ratio": 1.0, 160 | "td_length_ratio": 0.5, 161 | "td_maxheight": 2.0, 162 | "td_maxlen": 1.0, 163 | "td_on_pad_in_zone": false, 164 | "td_target_name": "td_rect_shape", 165 | "td_width_to_size_filter_ratio": 0.9 166 | }, 167 | { 168 | "td_allow_use_two_tracks": true, 169 | "td_curve_segcount": 0, 170 | "td_height_ratio": 1.0, 171 | "td_length_ratio": 0.5, 172 | "td_maxheight": 2.0, 173 | "td_maxlen": 1.0, 174 | "td_on_pad_in_zone": false, 175 | "td_target_name": "td_track_end", 176 | "td_width_to_size_filter_ratio": 0.9 177 | } 178 | ], 179 | "track_widths": [ 180 | 0.0, 181 | 0.1524, 182 | 0.2032, 183 | 0.3048 184 | ], 185 | "tuning_pattern_settings": { 186 | "diff_pair_defaults": { 187 | "corner_radius_percentage": 80, 188 | "corner_style": 1, 189 | "max_amplitude": 1.0, 190 | "min_amplitude": 0.2, 191 | "single_sided": false, 192 | "spacing": 1.0 193 | }, 194 | "diff_pair_skew_defaults": { 195 | "corner_radius_percentage": 80, 196 | "corner_style": 1, 197 | "max_amplitude": 1.0, 198 | "min_amplitude": 0.2, 199 | "single_sided": false, 200 | "spacing": 0.6 201 | }, 202 | "single_track_defaults": { 203 | "corner_radius_percentage": 80, 204 | "corner_style": 1, 205 | "max_amplitude": 1.0, 206 | "min_amplitude": 0.2, 207 | "single_sided": false, 208 | "spacing": 0.6 209 | } 210 | }, 211 | "via_dimensions": [ 212 | { 213 | "diameter": 0.0, 214 | "drill": 0.0 215 | }, 216 | { 217 | "diameter": 0.508, 218 | "drill": 0.254 219 | } 220 | ], 221 | "zones_allow_external_fillets": false 222 | }, 223 | "ipc2581": { 224 | "dist": "", 225 | "distpn": "", 226 | "internal_id": "", 227 | "mfg": "", 228 | "mpn": "" 229 | }, 230 | "layer_presets": [], 231 | "viewports": [] 232 | }, 233 | "boards": [], 234 | "cvpcb": { 235 | "equivalence_files": [] 236 | }, 237 | "erc": { 238 | "erc_exclusions": [], 239 | "meta": { 240 | "version": 0 241 | }, 242 | "pin_map": [ 243 | [ 244 | 0, 245 | 0, 246 | 0, 247 | 0, 248 | 0, 249 | 0, 250 | 1, 251 | 0, 252 | 0, 253 | 0, 254 | 0, 255 | 2 256 | ], 257 | [ 258 | 0, 259 | 2, 260 | 0, 261 | 1, 262 | 0, 263 | 0, 264 | 1, 265 | 0, 266 | 2, 267 | 2, 268 | 2, 269 | 2 270 | ], 271 | [ 272 | 0, 273 | 0, 274 | 0, 275 | 0, 276 | 0, 277 | 0, 278 | 1, 279 | 0, 280 | 1, 281 | 0, 282 | 1, 283 | 2 284 | ], 285 | [ 286 | 0, 287 | 1, 288 | 0, 289 | 0, 290 | 0, 291 | 0, 292 | 1, 293 | 1, 294 | 2, 295 | 1, 296 | 1, 297 | 2 298 | ], 299 | [ 300 | 0, 301 | 0, 302 | 0, 303 | 0, 304 | 0, 305 | 0, 306 | 1, 307 | 0, 308 | 0, 309 | 0, 310 | 0, 311 | 2 312 | ], 313 | [ 314 | 0, 315 | 0, 316 | 0, 317 | 0, 318 | 0, 319 | 0, 320 | 0, 321 | 0, 322 | 0, 323 | 0, 324 | 0, 325 | 2 326 | ], 327 | [ 328 | 1, 329 | 1, 330 | 1, 331 | 1, 332 | 1, 333 | 0, 334 | 1, 335 | 1, 336 | 1, 337 | 1, 338 | 1, 339 | 2 340 | ], 341 | [ 342 | 0, 343 | 0, 344 | 0, 345 | 1, 346 | 0, 347 | 0, 348 | 1, 349 | 0, 350 | 0, 351 | 0, 352 | 0, 353 | 2 354 | ], 355 | [ 356 | 0, 357 | 2, 358 | 1, 359 | 2, 360 | 0, 361 | 0, 362 | 1, 363 | 0, 364 | 2, 365 | 2, 366 | 2, 367 | 2 368 | ], 369 | [ 370 | 0, 371 | 2, 372 | 0, 373 | 1, 374 | 0, 375 | 0, 376 | 1, 377 | 0, 378 | 2, 379 | 0, 380 | 0, 381 | 2 382 | ], 383 | [ 384 | 0, 385 | 2, 386 | 1, 387 | 1, 388 | 0, 389 | 0, 390 | 1, 391 | 0, 392 | 2, 393 | 0, 394 | 0, 395 | 2 396 | ], 397 | [ 398 | 2, 399 | 2, 400 | 2, 401 | 2, 402 | 2, 403 | 2, 404 | 2, 405 | 2, 406 | 2, 407 | 2, 408 | 2, 409 | 2 410 | ] 411 | ], 412 | "rule_severities": { 413 | "bus_definition_conflict": "error", 414 | "bus_entry_needed": "error", 415 | "bus_to_bus_conflict": "error", 416 | "bus_to_net_conflict": "error", 417 | "conflicting_netclasses": "error", 418 | "different_unit_footprint": "error", 419 | "different_unit_net": "error", 420 | "duplicate_reference": "error", 421 | "duplicate_sheet_names": "error", 422 | "endpoint_off_grid": "warning", 423 | "extra_units": "error", 424 | "global_label_dangling": "warning", 425 | "hier_label_mismatch": "error", 426 | "label_dangling": "error", 427 | "lib_symbol_issues": "warning", 428 | "missing_bidi_pin": "warning", 429 | "missing_input_pin": "warning", 430 | "missing_power_pin": "error", 431 | "missing_unit": "warning", 432 | "multiple_net_names": "warning", 433 | "net_not_bus_member": "warning", 434 | "no_connect_connected": "warning", 435 | "no_connect_dangling": "warning", 436 | "pin_not_connected": "error", 437 | "pin_not_driven": "error", 438 | "pin_to_pin": "warning", 439 | "power_pin_not_driven": "error", 440 | "similar_labels": "warning", 441 | "simulation_model_issue": "ignore", 442 | "unannotated": "error", 443 | "unit_value_mismatch": "error", 444 | "unresolved_variable": "error", 445 | "wire_dangling": "error" 446 | } 447 | }, 448 | "libraries": { 449 | "pinned_footprint_libs": [], 450 | "pinned_symbol_libs": [] 451 | }, 452 | "meta": { 453 | "filename": "sensors.kicad_pro", 454 | "version": 1 455 | }, 456 | "net_settings": { 457 | "classes": [ 458 | { 459 | "bus_width": 12, 460 | "clearance": 0.2, 461 | "diff_pair_gap": 0.25, 462 | "diff_pair_via_gap": 0.25, 463 | "diff_pair_width": 0.2, 464 | "line_style": 0, 465 | "microvia_diameter": 0.3, 466 | "microvia_drill": 0.1, 467 | "name": "Default", 468 | "pcb_color": "rgba(0, 0, 0, 0.000)", 469 | "schematic_color": "rgba(0, 0, 0, 0.000)", 470 | "track_width": 0.2, 471 | "via_diameter": 0.6, 472 | "via_drill": 0.3, 473 | "wire_width": 6 474 | } 475 | ], 476 | "meta": { 477 | "version": 3 478 | }, 479 | "net_colors": null, 480 | "netclass_assignments": null, 481 | "netclass_patterns": [] 482 | }, 483 | "pcbnew": { 484 | "last_paths": { 485 | "gencad": "", 486 | "idf": "", 487 | "netlist": "", 488 | "plot": "gerbers/", 489 | "pos_files": "", 490 | "specctra_dsn": "", 491 | "step": "", 492 | "svg": "", 493 | "vrml": "" 494 | }, 495 | "page_layout_descr_file": "" 496 | }, 497 | "schematic": { 498 | "annotate_start_num": 0, 499 | "bom_fmt_presets": [], 500 | "bom_fmt_settings": { 501 | "field_delimiter": ",", 502 | "keep_line_breaks": false, 503 | "keep_tabs": false, 504 | "name": "CSV", 505 | "ref_delimiter": ",", 506 | "ref_range_delimiter": "", 507 | "string_delimiter": "\"" 508 | }, 509 | "bom_presets": [], 510 | "bom_settings": { 511 | "exclude_dnp": false, 512 | "fields_ordered": [ 513 | { 514 | "group_by": false, 515 | "label": "Reference", 516 | "name": "Reference", 517 | "show": true 518 | }, 519 | { 520 | "group_by": true, 521 | "label": "Value", 522 | "name": "Value", 523 | "show": true 524 | }, 525 | { 526 | "group_by": false, 527 | "label": "Datasheet", 528 | "name": "Datasheet", 529 | "show": true 530 | }, 531 | { 532 | "group_by": false, 533 | "label": "Footprint", 534 | "name": "Footprint", 535 | "show": true 536 | }, 537 | { 538 | "group_by": false, 539 | "label": "Qty", 540 | "name": "${QUANTITY}", 541 | "show": true 542 | }, 543 | { 544 | "group_by": true, 545 | "label": "DNP", 546 | "name": "${DNP}", 547 | "show": true 548 | } 549 | ], 550 | "filter_string": "", 551 | "group_symbols": true, 552 | "name": "Grouped By Value", 553 | "sort_asc": true, 554 | "sort_field": "Reference" 555 | }, 556 | "connection_grid_size": 50.0, 557 | "drawing": { 558 | "dashed_lines_dash_length_ratio": 12.0, 559 | "dashed_lines_gap_length_ratio": 3.0, 560 | "default_line_thickness": 6.0, 561 | "default_text_size": 50.0, 562 | "field_names": [], 563 | "intersheets_ref_own_page": false, 564 | "intersheets_ref_prefix": "", 565 | "intersheets_ref_short": false, 566 | "intersheets_ref_show": false, 567 | "intersheets_ref_suffix": "", 568 | "junction_size_choice": 3, 569 | "label_size_ratio": 0.375, 570 | "operating_point_overlay_i_precision": 3, 571 | "operating_point_overlay_i_range": "~A", 572 | "operating_point_overlay_v_precision": 3, 573 | "operating_point_overlay_v_range": "~V", 574 | "overbar_offset_ratio": 1.23, 575 | "pin_symbol_size": 25.0, 576 | "text_offset_ratio": 0.15 577 | }, 578 | "legacy_lib_dir": "", 579 | "legacy_lib_list": [], 580 | "meta": { 581 | "version": 1 582 | }, 583 | "net_format_name": "", 584 | "page_layout_descr_file": "", 585 | "plot_directory": "", 586 | "spice_current_sheet_as_root": false, 587 | "spice_external_command": "spice \"%I\"", 588 | "spice_model_current_sheet_as_root": true, 589 | "spice_save_all_currents": false, 590 | "spice_save_all_dissipations": false, 591 | "spice_save_all_voltages": false, 592 | "subpart_first_id": 65, 593 | "subpart_id_separator": 0 594 | }, 595 | "sheets": [ 596 | [ 597 | "ec97f746-e716-4c2a-b33e-ac66d59ccd45", 598 | "Root" 599 | ] 600 | ], 601 | "text_variables": {} 602 | } 603 | -------------------------------------------------------------------------------- /hardware/racer/racer-v0.2-BOM.csv: -------------------------------------------------------------------------------- 1 | Id,Designator,Footprint,Quantity,Designation,Supplier and ref,Notes 2 | 1,D8,C_0603_1608Metric,1,LED,150060GS75000,Indicates charging complete. I made this led Green 3 | 2,D9,C_0603_1608Metric,1,LED,150060RS75000,Indicates charging in progress. I made this LED red 4 | 3,"D1, D2",C_0603_1608Metric,2,LED,150060RS75000,Tail lights. I made this red 5 | 4,"D3, D4",C_0603_1608Metric,2,LED,IN-S63AT5UW,Headlights. I made this white 6 | 5,"C2,C16,C15,C5,C6,C1",C_0603_1608Metric,6,10uF,GRM188R61A106ME69D, 7 | 6,"C7,C12,C13,C3,C8,C4,C11",C_0603_1608Metric,7,0.1uF,CC0603KRX7R7BB104, 8 | 7,"R6,R4,R2,R8",R_0603_1608Metric,4,30k,ERA-3AED303V, 9 | 8,R22,R_0603_1608Metric,1,5k1,RMCF0603JT5R10,Replaced with 5K1 10 | 9,R23,R_0603_1608Metric,1,1.43k,RT0603BRE071K43L,Needs to be high precision. Its responsible for thermistor function on the charger. 11 | 10,"R14,R25,R12,R13,R15,R21,R20",R_0603_1608Metric,7,200,ESR03EZPF2000, 12 | 11,C14,C_0603_1608Metric,1,2.2uF,CL10A225KO8NNNC, 13 | 12,"R7,R5,R10,R9,R30,R3,R11,R1",R_0603_1608Metric,8,10k,WR06X103 JTL, 14 | 13,"R26,R28,R29,R27",R_0603_1608Metric,4,100k,WR06X1003FTL, 15 | 14,U6,AP2204,1,AP2204,AP2205-33W5-7,Replaced with AP2205 as AP2204 is now deprecated 16 | 15,"R17,R18",R_0603_1608Metric,2,5k1,RMCF0603JT5R10, 17 | 16,"U4,U1,U2,U3",A3908,4,A3908,A3908EEETR-T, 18 | 17,"S2,S3",SW_PTS810_SJM_250_SMTR_LFS,2,TL4100AF240QG_,PTS810 SJM 250 SMTR LFS,Replaced with PTS810 SJM 250 SMTR LFS 19 | 18,J12,JST_PH_B2B-PH-K_1x02_P2.00mm_Vertical,1,JST 2.0 BATT,B2B-PH-K-S, 20 | 19,P1,CUI_UJC-VP-3-SMT-TR,1,USB_C_Plug_USB2.0,UJC-VP-3-SMT-TR, 21 | 20,TH1,R_0603_1608Metric,1,TH 10k,NTCG163JF103HT1, 22 | 21,D6,D_0603_1608Metric,1,PWLED,150060RS75000,"NOTE: red LED repeats throughout the BOM, consolidate for better pricing…" 23 | 22,J11,PinHeader_1x04_P2.54mm_Vertical,1,UART,N/A,Buy any 2.54 header on ebay or amazon and cut it to 4 positions 24 | 23,U5,MODULE_ESP32-H2-MINI-1-H2,1,ESP32-H2-MINI-1,ESP32-H2-MINI-1-N4, 25 | 24,C10,C_0603_1608Metric,1,1uF 25V,CC0603KRX5R8BB105, 26 | 25,D5,D_SOD-523,1,TPD1E10B06DPYR,TPD1E10B06DYAR, 27 | 26,S1,SW_JS102011SAQN,1,JS102011SAQN,JS102011SAQN, 28 | 27,R24,R_0603_1608Metric,1,75K,ERJ-PB3B7502V, 29 | 28,U7,SON50P300X300X100-11N,1,MCP73833,MCP73833T-AMI/MF, 30 | 29,C17,C_0603_1608Metric,1,22uF,GRM188R60J226MEA0J, 31 | 30,R16,R_0603_1608Metric,1,1M,RMCF0603FT1M00, 32 | 31,C9,C_0603_1608Metric,1,47uF,GRM188C80E476ME05D, 33 | 32,J9,PinHeader_2x03_P1.27mm_Vertical_SMD,1,Conn_02x03_SMT,GRPB032VWQS-RC, 34 | ,,,,,, 35 | ,,,,,, 36 | Mehanical,,,,,, 37 | Id,Part name,Technical description,Link,Note,, 38 | 1,Battery,PKCELL LP552530,https://www.adafruit.com/product/4237,"If you are willing to wait, there are alternatives sold by YDL. Always check polarity of the connector before plugging it in! ",, 39 | 2,Motors,6mm Planetary DC 3V 1200RPM motors,https://www.aliexpress.us/item/3256801986204077.html,"I cannot recall where I got my motors, its been years. The linked store looks ok, but I cannot guarantee it. The specs of the motors match",, 40 | -------------------------------------------------------------------------------- /hardware/racer/racer-v0.2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuckAtPrototype/Racer/f43d1263155c1e04efdd052b35089c74f59d61d8/hardware/racer/racer-v0.2.zip -------------------------------------------------------------------------------- /hardware/racer/racer.kicad_prl: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "active_layer": 40, 4 | "active_layer_preset": "", 5 | "auto_track_width": true, 6 | "hidden_netclasses": [], 7 | "hidden_nets": [], 8 | "high_contrast_mode": 0, 9 | "net_color_mode": 1, 10 | "opacity": { 11 | "images": 0.6, 12 | "pads": 1.0, 13 | "tracks": 1.0, 14 | "vias": 1.0, 15 | "zones": 0.6 16 | }, 17 | "selection_filter": { 18 | "dimensions": true, 19 | "footprints": true, 20 | "graphics": true, 21 | "keepouts": true, 22 | "lockedItems": false, 23 | "otherItems": true, 24 | "pads": true, 25 | "text": true, 26 | "tracks": true, 27 | "vias": true, 28 | "zones": true 29 | }, 30 | "visible_items": [ 31 | 0, 32 | 1, 33 | 2, 34 | 3, 35 | 4, 36 | 5, 37 | 8, 38 | 9, 39 | 10, 40 | 11, 41 | 12, 42 | 13, 43 | 15, 44 | 16, 45 | 17, 46 | 18, 47 | 19, 48 | 20, 49 | 21, 50 | 22, 51 | 23, 52 | 24, 53 | 25, 54 | 26, 55 | 27, 56 | 28, 57 | 29, 58 | 30, 59 | 32, 60 | 33, 61 | 34, 62 | 35, 63 | 36, 64 | 39, 65 | 40 66 | ], 67 | "visible_layers": "ff4ffff_ffffffff", 68 | "zone_display_mode": 0 69 | }, 70 | "git": { 71 | "repo_password": "", 72 | "repo_type": "", 73 | "repo_username": "", 74 | "ssh_key": "" 75 | }, 76 | "meta": { 77 | "filename": "tank.kicad_prl", 78 | "version": 3 79 | }, 80 | "project": { 81 | "files": [] 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /hardware/racer/racer.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "3dviewports": [], 4 | "design_settings": { 5 | "defaults": { 6 | "apply_defaults_to_fp_fields": false, 7 | "apply_defaults_to_fp_shapes": false, 8 | "apply_defaults_to_fp_text": false, 9 | "board_outline_line_width": 0.1, 10 | "copper_line_width": 0.1016, 11 | "copper_text_italic": false, 12 | "copper_text_size_h": 0.508, 13 | "copper_text_size_v": 0.508, 14 | "copper_text_thickness": 0.1016, 15 | "copper_text_upright": false, 16 | "courtyard_line_width": 0.05, 17 | "dimension_precision": 4, 18 | "dimension_units": 3, 19 | "dimensions": { 20 | "arrow_length": 1270000, 21 | "extension_offset": 500000, 22 | "keep_text_aligned": true, 23 | "suppress_zeroes": false, 24 | "text_position": 0, 25 | "units_format": 1 26 | }, 27 | "fab_line_width": 0.1016, 28 | "fab_text_italic": false, 29 | "fab_text_size_h": 0.508, 30 | "fab_text_size_v": 0.508, 31 | "fab_text_thickness": 0.1016, 32 | "fab_text_upright": false, 33 | "other_line_width": 0.1016, 34 | "other_text_italic": false, 35 | "other_text_size_h": 0.508, 36 | "other_text_size_v": 0.508, 37 | "other_text_thickness": 0.1016, 38 | "other_text_upright": false, 39 | "pads": { 40 | "drill": 0.762, 41 | "height": 1.524, 42 | "width": 1.524 43 | }, 44 | "silk_line_width": 0.1016, 45 | "silk_text_italic": false, 46 | "silk_text_size_h": 0.508, 47 | "silk_text_size_v": 0.508, 48 | "silk_text_thickness": 0.1016, 49 | "silk_text_upright": false, 50 | "zones": { 51 | "min_clearance": 0.5 52 | } 53 | }, 54 | "diff_pair_dimensions": [ 55 | { 56 | "gap": 0.0, 57 | "via_gap": 0.0, 58 | "width": 0.0 59 | } 60 | ], 61 | "drc_exclusions": [ 62 | "footprint_type_mismatch|175732800|109995000|a0accbaf-4c88-4d9b-a33b-483c8d6cf345|00000000-0000-0000-0000-000000000000", 63 | "footprint_type_mismatch|176670200|131877200|b4d11d8a-6823-4555-afb5-e281dbb73ef6|00000000-0000-0000-0000-000000000000", 64 | "footprint_type_mismatch|177258000|92155200|893b6f4d-ca29-4cfc-8e28-ec17db2b8cb3|00000000-0000-0000-0000-000000000000", 65 | "footprint_type_mismatch|184912000|124750600|85739941-68b4-452d-b206-1e220b27ffbb|00000000-0000-0000-0000-000000000000", 66 | "footprint_type_mismatch|192337400|92063000|901525e1-5d10-4132-adb7-46ad328f0aa2|00000000-0000-0000-0000-000000000000", 67 | "footprint_type_mismatch|193090800|131914200|6309729c-b0eb-45b6-9068-ab82c3edf66f|00000000-0000-0000-0000-000000000000", 68 | "footprint_type_mismatch|198720200|102158800|3b30881e-2389-484b-b281-97faee83cbc6|00000000-0000-0000-0000-000000000000", 69 | "lib_footprint_mismatch|184886600|133985000|d29dc366-5f2a-4c5e-ae46-a25d45e2eb0e|00000000-0000-0000-0000-000000000000", 70 | "lib_footprint_mismatch|184886600|89382600|3494d978-8306-4ce5-b4ee-dea6fb601383|00000000-0000-0000-0000-000000000000" 71 | ], 72 | "meta": { 73 | "version": 2 74 | }, 75 | "rule_severities": { 76 | "annular_width": "error", 77 | "clearance": "error", 78 | "connection_width": "warning", 79 | "copper_edge_clearance": "error", 80 | "copper_sliver": "warning", 81 | "courtyards_overlap": "ignore", 82 | "diff_pair_gap_out_of_range": "error", 83 | "diff_pair_uncoupled_length_too_long": "error", 84 | "drill_out_of_range": "error", 85 | "duplicate_footprints": "warning", 86 | "extra_footprint": "warning", 87 | "footprint": "error", 88 | "footprint_symbol_mismatch": "warning", 89 | "footprint_type_mismatch": "error", 90 | "hole_clearance": "error", 91 | "hole_near_hole": "error", 92 | "holes_co_located": "warning", 93 | "invalid_outline": "error", 94 | "isolated_copper": "warning", 95 | "item_on_disabled_layer": "error", 96 | "items_not_allowed": "error", 97 | "length_out_of_range": "error", 98 | "lib_footprint_issues": "warning", 99 | "lib_footprint_mismatch": "warning", 100 | "malformed_courtyard": "error", 101 | "microvia_drill_out_of_range": "error", 102 | "missing_courtyard": "ignore", 103 | "missing_footprint": "warning", 104 | "net_conflict": "warning", 105 | "npth_inside_courtyard": "ignore", 106 | "padstack": "error", 107 | "pth_inside_courtyard": "ignore", 108 | "shorting_items": "error", 109 | "silk_edge_clearance": "warning", 110 | "silk_over_copper": "warning", 111 | "silk_overlap": "warning", 112 | "skew_out_of_range": "error", 113 | "solder_mask_bridge": "error", 114 | "starved_thermal": "error", 115 | "text_height": "warning", 116 | "text_thickness": "warning", 117 | "through_hole_pad_without_hole": "error", 118 | "too_many_vias": "error", 119 | "track_dangling": "warning", 120 | "track_width": "error", 121 | "tracks_crossing": "error", 122 | "unconnected_items": "error", 123 | "unresolved_variable": "error", 124 | "via_dangling": "warning", 125 | "zones_intersect": "error" 126 | }, 127 | "rules": { 128 | "max_error": 0.005, 129 | "min_clearance": 0.14986, 130 | "min_connection": 0.0, 131 | "min_copper_edge_clearance": 0.0, 132 | "min_hole_clearance": 0.1524, 133 | "min_hole_to_hole": 0.127, 134 | "min_microvia_diameter": 0.2, 135 | "min_microvia_drill": 0.1, 136 | "min_resolved_spokes": 1, 137 | "min_silk_clearance": 0.0, 138 | "min_text_height": 0.508, 139 | "min_text_thickness": 0.0762, 140 | "min_through_hole_diameter": 0.254, 141 | "min_track_width": 0.1524, 142 | "min_via_annular_width": 0.127, 143 | "min_via_diameter": 0.4572, 144 | "solder_mask_to_copper_clearance": 0.0, 145 | "use_height_for_length_calcs": true 146 | }, 147 | "teardrop_options": [ 148 | { 149 | "td_onpadsmd": true, 150 | "td_onroundshapesonly": false, 151 | "td_ontrackend": false, 152 | "td_onviapad": true 153 | } 154 | ], 155 | "teardrop_parameters": [ 156 | { 157 | "td_allow_use_two_tracks": true, 158 | "td_curve_segcount": 0, 159 | "td_height_ratio": 1.0, 160 | "td_length_ratio": 0.5, 161 | "td_maxheight": 2.0, 162 | "td_maxlen": 1.0, 163 | "td_on_pad_in_zone": false, 164 | "td_target_name": "td_round_shape", 165 | "td_width_to_size_filter_ratio": 0.9 166 | }, 167 | { 168 | "td_allow_use_two_tracks": true, 169 | "td_curve_segcount": 0, 170 | "td_height_ratio": 1.0, 171 | "td_length_ratio": 0.5, 172 | "td_maxheight": 2.0, 173 | "td_maxlen": 1.0, 174 | "td_on_pad_in_zone": false, 175 | "td_target_name": "td_rect_shape", 176 | "td_width_to_size_filter_ratio": 0.9 177 | }, 178 | { 179 | "td_allow_use_two_tracks": true, 180 | "td_curve_segcount": 0, 181 | "td_height_ratio": 1.0, 182 | "td_length_ratio": 0.5, 183 | "td_maxheight": 2.0, 184 | "td_maxlen": 1.0, 185 | "td_on_pad_in_zone": false, 186 | "td_target_name": "td_track_end", 187 | "td_width_to_size_filter_ratio": 0.9 188 | } 189 | ], 190 | "track_widths": [ 191 | 0.0, 192 | 0.1524, 193 | 0.2032, 194 | 0.254, 195 | 0.3048, 196 | 0.4572, 197 | 0.762, 198 | 1.016, 199 | 1.27 200 | ], 201 | "tuning_pattern_settings": { 202 | "diff_pair_defaults": { 203 | "corner_radius_percentage": 80, 204 | "corner_style": 1, 205 | "max_amplitude": 1.0, 206 | "min_amplitude": 0.2, 207 | "single_sided": false, 208 | "spacing": 1.0 209 | }, 210 | "diff_pair_skew_defaults": { 211 | "corner_radius_percentage": 80, 212 | "corner_style": 1, 213 | "max_amplitude": 1.0, 214 | "min_amplitude": 0.2, 215 | "single_sided": false, 216 | "spacing": 0.6 217 | }, 218 | "single_track_defaults": { 219 | "corner_radius_percentage": 80, 220 | "corner_style": 1, 221 | "max_amplitude": 1.0, 222 | "min_amplitude": 0.2, 223 | "single_sided": false, 224 | "spacing": 0.6 225 | } 226 | }, 227 | "via_dimensions": [ 228 | { 229 | "diameter": 0.0, 230 | "drill": 0.0 231 | }, 232 | { 233 | "diameter": 0.508, 234 | "drill": 0.254 235 | } 236 | ], 237 | "zones_allow_external_fillets": false 238 | }, 239 | "ipc2581": { 240 | "dist": "", 241 | "distpn": "", 242 | "internal_id": "", 243 | "mfg": "", 244 | "mpn": "" 245 | }, 246 | "layer_presets": [], 247 | "viewports": [] 248 | }, 249 | "boards": [], 250 | "cvpcb": { 251 | "equivalence_files": [] 252 | }, 253 | "erc": { 254 | "erc_exclusions": [], 255 | "meta": { 256 | "version": 0 257 | }, 258 | "pin_map": [ 259 | [ 260 | 0, 261 | 0, 262 | 0, 263 | 0, 264 | 0, 265 | 0, 266 | 1, 267 | 0, 268 | 0, 269 | 0, 270 | 0, 271 | 2 272 | ], 273 | [ 274 | 0, 275 | 2, 276 | 0, 277 | 1, 278 | 0, 279 | 0, 280 | 1, 281 | 0, 282 | 2, 283 | 2, 284 | 2, 285 | 2 286 | ], 287 | [ 288 | 0, 289 | 0, 290 | 0, 291 | 0, 292 | 0, 293 | 0, 294 | 1, 295 | 0, 296 | 1, 297 | 0, 298 | 1, 299 | 2 300 | ], 301 | [ 302 | 0, 303 | 1, 304 | 0, 305 | 0, 306 | 0, 307 | 0, 308 | 1, 309 | 1, 310 | 2, 311 | 1, 312 | 1, 313 | 2 314 | ], 315 | [ 316 | 0, 317 | 0, 318 | 0, 319 | 0, 320 | 0, 321 | 0, 322 | 1, 323 | 0, 324 | 0, 325 | 0, 326 | 0, 327 | 2 328 | ], 329 | [ 330 | 0, 331 | 0, 332 | 0, 333 | 0, 334 | 0, 335 | 0, 336 | 0, 337 | 0, 338 | 0, 339 | 0, 340 | 0, 341 | 2 342 | ], 343 | [ 344 | 1, 345 | 1, 346 | 1, 347 | 1, 348 | 1, 349 | 0, 350 | 1, 351 | 1, 352 | 1, 353 | 1, 354 | 1, 355 | 2 356 | ], 357 | [ 358 | 0, 359 | 0, 360 | 0, 361 | 1, 362 | 0, 363 | 0, 364 | 1, 365 | 0, 366 | 0, 367 | 0, 368 | 0, 369 | 2 370 | ], 371 | [ 372 | 0, 373 | 2, 374 | 1, 375 | 2, 376 | 0, 377 | 0, 378 | 1, 379 | 0, 380 | 2, 381 | 2, 382 | 2, 383 | 2 384 | ], 385 | [ 386 | 0, 387 | 2, 388 | 0, 389 | 1, 390 | 0, 391 | 0, 392 | 1, 393 | 0, 394 | 2, 395 | 0, 396 | 0, 397 | 2 398 | ], 399 | [ 400 | 0, 401 | 2, 402 | 1, 403 | 1, 404 | 0, 405 | 0, 406 | 1, 407 | 0, 408 | 2, 409 | 0, 410 | 0, 411 | 2 412 | ], 413 | [ 414 | 2, 415 | 2, 416 | 2, 417 | 2, 418 | 2, 419 | 2, 420 | 2, 421 | 2, 422 | 2, 423 | 2, 424 | 2, 425 | 2 426 | ] 427 | ], 428 | "rule_severities": { 429 | "bus_definition_conflict": "error", 430 | "bus_entry_needed": "error", 431 | "bus_to_bus_conflict": "error", 432 | "bus_to_net_conflict": "error", 433 | "conflicting_netclasses": "error", 434 | "different_unit_footprint": "error", 435 | "different_unit_net": "error", 436 | "duplicate_reference": "error", 437 | "duplicate_sheet_names": "error", 438 | "endpoint_off_grid": "warning", 439 | "extra_units": "error", 440 | "global_label_dangling": "warning", 441 | "hier_label_mismatch": "error", 442 | "label_dangling": "error", 443 | "lib_symbol_issues": "warning", 444 | "missing_bidi_pin": "warning", 445 | "missing_input_pin": "warning", 446 | "missing_power_pin": "error", 447 | "missing_unit": "warning", 448 | "multiple_net_names": "warning", 449 | "net_not_bus_member": "warning", 450 | "no_connect_connected": "warning", 451 | "no_connect_dangling": "warning", 452 | "pin_not_connected": "error", 453 | "pin_not_driven": "error", 454 | "pin_to_pin": "warning", 455 | "power_pin_not_driven": "error", 456 | "similar_labels": "warning", 457 | "simulation_model_issue": "ignore", 458 | "unannotated": "error", 459 | "unit_value_mismatch": "error", 460 | "unresolved_variable": "error", 461 | "wire_dangling": "error" 462 | } 463 | }, 464 | "libraries": { 465 | "pinned_footprint_libs": [], 466 | "pinned_symbol_libs": [] 467 | }, 468 | "meta": { 469 | "filename": "racer.kicad_pro", 470 | "version": 1 471 | }, 472 | "net_settings": { 473 | "classes": [ 474 | { 475 | "bus_width": 12, 476 | "clearance": 0.1524, 477 | "diff_pair_gap": 0.25, 478 | "diff_pair_via_gap": 0.25, 479 | "diff_pair_width": 0.2, 480 | "line_style": 0, 481 | "microvia_diameter": 0.3, 482 | "microvia_drill": 0.1, 483 | "name": "Default", 484 | "pcb_color": "rgba(0, 0, 0, 0.000)", 485 | "schematic_color": "rgba(0, 0, 0, 0.000)", 486 | "track_width": 0.2032, 487 | "via_diameter": 0.508, 488 | "via_drill": 0.254, 489 | "wire_width": 6 490 | } 491 | ], 492 | "meta": { 493 | "version": 3 494 | }, 495 | "net_colors": null, 496 | "netclass_assignments": null, 497 | "netclass_patterns": [] 498 | }, 499 | "pcbnew": { 500 | "last_paths": { 501 | "gencad": "", 502 | "idf": "", 503 | "netlist": "", 504 | "plot": "gerbers", 505 | "pos_files": "./", 506 | "specctra_dsn": "", 507 | "step": "tank.step", 508 | "svg": "", 509 | "vrml": "" 510 | }, 511 | "page_layout_descr_file": "" 512 | }, 513 | "schematic": { 514 | "annotate_start_num": 0, 515 | "bom_export_filename": "", 516 | "bom_fmt_presets": [], 517 | "bom_fmt_settings": { 518 | "field_delimiter": ",", 519 | "keep_line_breaks": false, 520 | "keep_tabs": false, 521 | "name": "CSV", 522 | "ref_delimiter": ",", 523 | "ref_range_delimiter": "", 524 | "string_delimiter": "\"" 525 | }, 526 | "bom_presets": [], 527 | "bom_settings": { 528 | "exclude_dnp": false, 529 | "fields_ordered": [ 530 | { 531 | "group_by": false, 532 | "label": "Reference", 533 | "name": "Reference", 534 | "show": true 535 | }, 536 | { 537 | "group_by": true, 538 | "label": "Value", 539 | "name": "Value", 540 | "show": true 541 | }, 542 | { 543 | "group_by": false, 544 | "label": "Datasheet", 545 | "name": "Datasheet", 546 | "show": true 547 | }, 548 | { 549 | "group_by": false, 550 | "label": "Footprint", 551 | "name": "Footprint", 552 | "show": true 553 | }, 554 | { 555 | "group_by": false, 556 | "label": "Qty", 557 | "name": "${QUANTITY}", 558 | "show": true 559 | }, 560 | { 561 | "group_by": true, 562 | "label": "DNP", 563 | "name": "${DNP}", 564 | "show": true 565 | }, 566 | { 567 | "group_by": false, 568 | "label": "#", 569 | "name": "${ITEM_NUMBER}", 570 | "show": false 571 | }, 572 | { 573 | "group_by": false, 574 | "label": "MANUFACTURER", 575 | "name": "MANUFACTURER", 576 | "show": false 577 | }, 578 | { 579 | "group_by": false, 580 | "label": "Description", 581 | "name": "Description", 582 | "show": false 583 | } 584 | ], 585 | "filter_string": "", 586 | "group_symbols": true, 587 | "name": "", 588 | "sort_asc": true, 589 | "sort_field": "Reference" 590 | }, 591 | "connection_grid_size": 50.0, 592 | "drawing": { 593 | "dashed_lines_dash_length_ratio": 12.0, 594 | "dashed_lines_gap_length_ratio": 3.0, 595 | "default_line_thickness": 6.0, 596 | "default_text_size": 50.0, 597 | "field_names": [], 598 | "intersheets_ref_own_page": false, 599 | "intersheets_ref_prefix": "", 600 | "intersheets_ref_short": false, 601 | "intersheets_ref_show": false, 602 | "intersheets_ref_suffix": "", 603 | "junction_size_choice": 3, 604 | "label_size_ratio": 0.375, 605 | "operating_point_overlay_i_precision": 3, 606 | "operating_point_overlay_i_range": "~A", 607 | "operating_point_overlay_v_precision": 3, 608 | "operating_point_overlay_v_range": "~V", 609 | "overbar_offset_ratio": 1.23, 610 | "pin_symbol_size": 25.0, 611 | "text_offset_ratio": 0.15 612 | }, 613 | "legacy_lib_dir": "", 614 | "legacy_lib_list": [], 615 | "meta": { 616 | "version": 1 617 | }, 618 | "net_format_name": "", 619 | "page_layout_descr_file": "", 620 | "plot_directory": "", 621 | "spice_current_sheet_as_root": false, 622 | "spice_external_command": "spice \"%I\"", 623 | "spice_model_current_sheet_as_root": true, 624 | "spice_save_all_currents": false, 625 | "spice_save_all_dissipations": false, 626 | "spice_save_all_voltages": false, 627 | "subpart_first_id": 65, 628 | "subpart_id_separator": 0 629 | }, 630 | "sheets": [ 631 | [ 632 | "11c7bd96-786c-4c10-b71b-7b198cc052d5", 633 | "Root" 634 | ] 635 | ], 636 | "text_variables": {} 637 | } 638 | -------------------------------------------------------------------------------- /hardware/thumbtroller/controller-bottom-pos.csv: -------------------------------------------------------------------------------- 1 | Ref,Val,Package,PosX,PosY,Rot,Side 2 | "D1","LED","C_0603_1608Metric",4.267200,42.062400,180.000000,bottom 3 | "D2","LED","C_0603_1608Metric",4.267200,38.252400,180.000000,bottom 4 | "D3","LED","C_0603_1608Metric",4.267200,34.442400,180.000000,bottom 5 | "S2","TL4100AF240QG_","SW_PTS810_SJM_250_SMTR_LFS",15.029000,20.421600,180.000000,bottom 6 | "S3","TL4100AF240QG_","SW_PTS810_SJM_250_SMTR_LFS",15.036800,10.718800,180.000000,bottom 7 | "S5","TL4100AF240QG_","SW_PTS810_SJM_250_SMTR_LFS",20.599400,15.560900,-90.000000,bottom 8 | "S6","TL4100AF240QG_","SW_PTS810_SJM_250_SMTR_LFS",9.440600,15.552600,90.000000,bottom 9 | -------------------------------------------------------------------------------- /hardware/thumbtroller/controller-top-pos.csv: -------------------------------------------------------------------------------- 1 | Ref,Val,Package,PosX,PosY,Rot,Side, 2 | C1,1uF 25V,C_0603_1608Metric,2.93,37.62,180,top,270 3 | C2,1uF 25V,C_0603_1608Metric,2.93,36.07,180,top,270 4 | C3,1uF 25V,C_0603_1608Metric,2.96,32.97,180,top,270 5 | C4,1uF 25V,C_0603_1608Metric,2.96,29.87,180,top,270 6 | C10,1uF 25V,C_0603_1608Metric,3.00,48.67,180,top,270 7 | C11,0.1uF,C_0603_1608Metric,19.99,46.89,0,top,90 8 | C12,0.1uF,C_0603_1608Metric,6.33,48.62,0,top,90 9 | C13,0.1uF,C_0603_1608Metric,9.96,41.71,90,top,180 10 | C14,2.2uF,C_0603_1608Metric,19.29,16.02,0,top,90 11 | C15,10uF,C_0603_1608Metric,19.54,22.52,0,top,90 12 | C16,10uF,C_0603_1608Metric,3.54,16.02,180,top,270 13 | C17,22uF,C_0603_1608Metric,3.43,7.95,-90,top,0 14 | R1,200,R_0603_1608Metric,2.74,42.57,0,top,90 15 | R2,10k,R_0603_1608Metric,6.06,37.62,0,top,90 16 | R3,100k,R_0603_1608Metric,2.93,39.18,180,top,270 17 | R4,10k,R_0603_1608Metric,6.07,34.52,0,top,90 18 | R5,100k,R_0603_1608Metric,2.96,34.52,180,top,270 19 | R6,10k,R_0603_1608Metric,6.03,31.42,0,top,90 20 | R7,100k,R_0603_1608Metric,2.95,31.42,180,top,270 21 | R8,10k,R_0603_1608Metric,6.07,28.32,0,top,90 22 | R9,10k,R_0603_1608Metric,6.30,46.89,180,top,270 23 | R10,10k,R_0603_1608Metric,4.52,26.06,180,top,270 24 | R11,10k,R_0603_1608Metric,9.80,35.00,180,top,270 25 | R12,200,R_0603_1608Metric,2.74,46.13,0,top,90 26 | R13,200,R_0603_1608Metric,2.74,44.35,0,top,90 27 | R14,100k,R_0603_1608Metric,2.98,28.32,180,top,270 28 | R16,1M,R_0603_1608Metric,16.71,10.03,90,top,180 29 | R17,5k1,R_0603_1608Metric,25.58,12.19,90,top,180 30 | R18,5k1,R_0603_1608Metric,23.93,12.19,90,top,180 31 | R20,470,R_0603_1608Metric,11.68,17.55,180,top,270 32 | R21,470,R_0603_1608Metric,11.66,15.42,180,top,270 33 | R22,1.43K,R_0603_1608Metric,1.73,7.95,-90,top,0 34 | R23,1.43K,R_0603_1608Metric,8.54,9.52,180,top,270 35 | R24,75K,R_0603_1608Metric,11.54,11.02,180,top,270 36 | R25,200,R_0603_1608Metric,15.57,5.49,90,top,180 37 | -------------------------------------------------------------------------------- /hardware/thumbtroller/controller.kicad_prl: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "active_layer": 0, 4 | "active_layer_preset": "", 5 | "auto_track_width": true, 6 | "hidden_netclasses": [], 7 | "hidden_nets": [], 8 | "high_contrast_mode": 0, 9 | "net_color_mode": 1, 10 | "opacity": { 11 | "images": 0.6, 12 | "pads": 1.0, 13 | "tracks": 1.0, 14 | "vias": 1.0, 15 | "zones": 0.6 16 | }, 17 | "selection_filter": { 18 | "dimensions": true, 19 | "footprints": true, 20 | "graphics": true, 21 | "keepouts": true, 22 | "lockedItems": false, 23 | "otherItems": true, 24 | "pads": true, 25 | "text": true, 26 | "tracks": true, 27 | "vias": true, 28 | "zones": true 29 | }, 30 | "visible_items": [ 31 | 0, 32 | 1, 33 | 2, 34 | 3, 35 | 4, 36 | 5, 37 | 8, 38 | 9, 39 | 10, 40 | 11, 41 | 12, 42 | 13, 43 | 15, 44 | 16, 45 | 17, 46 | 18, 47 | 19, 48 | 20, 49 | 21, 50 | 22, 51 | 23, 52 | 24, 53 | 25, 54 | 26, 55 | 27, 56 | 28, 57 | 29, 58 | 30, 59 | 32, 60 | 33, 61 | 34, 62 | 35, 63 | 36, 64 | 39, 65 | 40 66 | ], 67 | "visible_layers": "ff4ffff_ffffffff", 68 | "zone_display_mode": 0 69 | }, 70 | "git": { 71 | "repo_password": "", 72 | "repo_type": "", 73 | "repo_username": "", 74 | "ssh_key": "" 75 | }, 76 | "meta": { 77 | "filename": "controller.kicad_prl", 78 | "version": 3 79 | }, 80 | "project": { 81 | "files": [] 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /hardware/thumbtroller/controller.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "3dviewports": [], 4 | "design_settings": { 5 | "defaults": { 6 | "apply_defaults_to_fp_fields": false, 7 | "apply_defaults_to_fp_shapes": false, 8 | "apply_defaults_to_fp_text": false, 9 | "board_outline_line_width": 0.1, 10 | "copper_line_width": 0.1016, 11 | "copper_text_italic": false, 12 | "copper_text_size_h": 0.508, 13 | "copper_text_size_v": 0.508, 14 | "copper_text_thickness": 0.1016, 15 | "copper_text_upright": false, 16 | "courtyard_line_width": 0.05, 17 | "dimension_precision": 4, 18 | "dimension_units": 3, 19 | "dimensions": { 20 | "arrow_length": 1270000, 21 | "extension_offset": 500000, 22 | "keep_text_aligned": true, 23 | "suppress_zeroes": false, 24 | "text_position": 0, 25 | "units_format": 1 26 | }, 27 | "fab_line_width": 0.1016, 28 | "fab_text_italic": false, 29 | "fab_text_size_h": 0.508, 30 | "fab_text_size_v": 0.508, 31 | "fab_text_thickness": 0.1016, 32 | "fab_text_upright": false, 33 | "other_line_width": 0.1016, 34 | "other_text_italic": false, 35 | "other_text_size_h": 0.508, 36 | "other_text_size_v": 0.508, 37 | "other_text_thickness": 0.1016, 38 | "other_text_upright": false, 39 | "pads": { 40 | "drill": 0.762, 41 | "height": 1.524, 42 | "width": 1.524 43 | }, 44 | "silk_line_width": 0.1016, 45 | "silk_text_italic": false, 46 | "silk_text_size_h": 0.508, 47 | "silk_text_size_v": 0.508, 48 | "silk_text_thickness": 0.1016, 49 | "silk_text_upright": false, 50 | "zones": { 51 | "min_clearance": 0.0 52 | } 53 | }, 54 | "diff_pair_dimensions": [ 55 | { 56 | "gap": 0.0, 57 | "via_gap": 0.0, 58 | "width": 0.0 59 | } 60 | ], 61 | "drc_exclusions": [], 62 | "meta": { 63 | "version": 2 64 | }, 65 | "rule_severities": { 66 | "annular_width": "error", 67 | "clearance": "error", 68 | "connection_width": "warning", 69 | "copper_edge_clearance": "error", 70 | "copper_sliver": "warning", 71 | "courtyards_overlap": "ignore", 72 | "diff_pair_gap_out_of_range": "error", 73 | "diff_pair_uncoupled_length_too_long": "error", 74 | "drill_out_of_range": "error", 75 | "duplicate_footprints": "warning", 76 | "extra_footprint": "warning", 77 | "footprint": "error", 78 | "footprint_symbol_mismatch": "warning", 79 | "footprint_type_mismatch": "warning", 80 | "hole_clearance": "error", 81 | "hole_near_hole": "error", 82 | "invalid_outline": "error", 83 | "isolated_copper": "warning", 84 | "item_on_disabled_layer": "error", 85 | "items_not_allowed": "error", 86 | "length_out_of_range": "error", 87 | "lib_footprint_issues": "warning", 88 | "lib_footprint_mismatch": "warning", 89 | "malformed_courtyard": "error", 90 | "microvia_drill_out_of_range": "error", 91 | "missing_courtyard": "ignore", 92 | "missing_footprint": "warning", 93 | "net_conflict": "warning", 94 | "npth_inside_courtyard": "ignore", 95 | "padstack": "error", 96 | "pth_inside_courtyard": "ignore", 97 | "shorting_items": "error", 98 | "silk_edge_clearance": "warning", 99 | "silk_over_copper": "warning", 100 | "silk_overlap": "warning", 101 | "skew_out_of_range": "error", 102 | "solder_mask_bridge": "error", 103 | "starved_thermal": "error", 104 | "text_height": "warning", 105 | "text_thickness": "warning", 106 | "through_hole_pad_without_hole": "error", 107 | "too_many_vias": "error", 108 | "track_dangling": "warning", 109 | "track_width": "error", 110 | "tracks_crossing": "error", 111 | "unconnected_items": "error", 112 | "unresolved_variable": "error", 113 | "via_dangling": "warning", 114 | "zones_intersect": "error" 115 | }, 116 | "rules": { 117 | "max_error": 0.005, 118 | "min_clearance": 0.14986, 119 | "min_connection": 0.0, 120 | "min_copper_edge_clearance": 0.0, 121 | "min_hole_clearance": 0.1524, 122 | "min_hole_to_hole": 0.127, 123 | "min_microvia_diameter": 0.2, 124 | "min_microvia_drill": 0.1, 125 | "min_resolved_spokes": 1, 126 | "min_silk_clearance": 0.0, 127 | "min_text_height": 0.508, 128 | "min_text_thickness": 0.127, 129 | "min_through_hole_diameter": 0.254, 130 | "min_track_width": 0.1524, 131 | "min_via_annular_width": 0.127, 132 | "min_via_diameter": 0.4572, 133 | "solder_mask_to_copper_clearance": 0.0, 134 | "use_height_for_length_calcs": true 135 | }, 136 | "teardrop_options": [ 137 | { 138 | "td_onpadsmd": true, 139 | "td_onroundshapesonly": false, 140 | "td_ontrackend": false, 141 | "td_onviapad": true 142 | } 143 | ], 144 | "teardrop_parameters": [ 145 | { 146 | "td_allow_use_two_tracks": true, 147 | "td_curve_segcount": 0, 148 | "td_height_ratio": 1.0, 149 | "td_length_ratio": 0.5, 150 | "td_maxheight": 2.0, 151 | "td_maxlen": 1.0, 152 | "td_on_pad_in_zone": false, 153 | "td_target_name": "td_round_shape", 154 | "td_width_to_size_filter_ratio": 0.9 155 | }, 156 | { 157 | "td_allow_use_two_tracks": true, 158 | "td_curve_segcount": 0, 159 | "td_height_ratio": 1.0, 160 | "td_length_ratio": 0.5, 161 | "td_maxheight": 2.0, 162 | "td_maxlen": 1.0, 163 | "td_on_pad_in_zone": false, 164 | "td_target_name": "td_rect_shape", 165 | "td_width_to_size_filter_ratio": 0.9 166 | }, 167 | { 168 | "td_allow_use_two_tracks": true, 169 | "td_curve_segcount": 0, 170 | "td_height_ratio": 1.0, 171 | "td_length_ratio": 0.5, 172 | "td_maxheight": 2.0, 173 | "td_maxlen": 1.0, 174 | "td_on_pad_in_zone": false, 175 | "td_target_name": "td_track_end", 176 | "td_width_to_size_filter_ratio": 0.9 177 | } 178 | ], 179 | "track_widths": [ 180 | 0.0, 181 | 0.1524, 182 | 0.2032, 183 | 0.254, 184 | 0.3048, 185 | 0.4572, 186 | 0.762, 187 | 1.016, 188 | 1.27 189 | ], 190 | "tuning_pattern_settings": { 191 | "diff_pair_defaults": { 192 | "corner_radius_percentage": 80, 193 | "corner_style": 1, 194 | "max_amplitude": 1.0, 195 | "min_amplitude": 0.2, 196 | "single_sided": false, 197 | "spacing": 1.0 198 | }, 199 | "diff_pair_skew_defaults": { 200 | "corner_radius_percentage": 80, 201 | "corner_style": 1, 202 | "max_amplitude": 1.0, 203 | "min_amplitude": 0.2, 204 | "single_sided": false, 205 | "spacing": 0.6 206 | }, 207 | "single_track_defaults": { 208 | "corner_radius_percentage": 80, 209 | "corner_style": 1, 210 | "max_amplitude": 1.0, 211 | "min_amplitude": 0.2, 212 | "single_sided": false, 213 | "spacing": 0.6 214 | } 215 | }, 216 | "via_dimensions": [ 217 | { 218 | "diameter": 0.0, 219 | "drill": 0.0 220 | }, 221 | { 222 | "diameter": 0.508, 223 | "drill": 0.254 224 | } 225 | ], 226 | "zones_allow_external_fillets": false 227 | }, 228 | "ipc2581": { 229 | "dist": "", 230 | "distpn": "", 231 | "internal_id": "", 232 | "mfg": "", 233 | "mpn": "" 234 | }, 235 | "layer_presets": [], 236 | "viewports": [] 237 | }, 238 | "boards": [], 239 | "cvpcb": { 240 | "equivalence_files": [] 241 | }, 242 | "erc": { 243 | "erc_exclusions": [], 244 | "meta": { 245 | "version": 0 246 | }, 247 | "pin_map": [ 248 | [ 249 | 0, 250 | 0, 251 | 0, 252 | 0, 253 | 0, 254 | 0, 255 | 1, 256 | 0, 257 | 0, 258 | 0, 259 | 0, 260 | 2 261 | ], 262 | [ 263 | 0, 264 | 2, 265 | 0, 266 | 1, 267 | 0, 268 | 0, 269 | 1, 270 | 0, 271 | 2, 272 | 2, 273 | 2, 274 | 2 275 | ], 276 | [ 277 | 0, 278 | 0, 279 | 0, 280 | 0, 281 | 0, 282 | 0, 283 | 1, 284 | 0, 285 | 1, 286 | 0, 287 | 1, 288 | 2 289 | ], 290 | [ 291 | 0, 292 | 1, 293 | 0, 294 | 0, 295 | 0, 296 | 0, 297 | 1, 298 | 1, 299 | 2, 300 | 1, 301 | 1, 302 | 2 303 | ], 304 | [ 305 | 0, 306 | 0, 307 | 0, 308 | 0, 309 | 0, 310 | 0, 311 | 1, 312 | 0, 313 | 0, 314 | 0, 315 | 0, 316 | 2 317 | ], 318 | [ 319 | 0, 320 | 0, 321 | 0, 322 | 0, 323 | 0, 324 | 0, 325 | 0, 326 | 0, 327 | 0, 328 | 0, 329 | 0, 330 | 2 331 | ], 332 | [ 333 | 1, 334 | 1, 335 | 1, 336 | 1, 337 | 1, 338 | 0, 339 | 1, 340 | 1, 341 | 1, 342 | 1, 343 | 1, 344 | 2 345 | ], 346 | [ 347 | 0, 348 | 0, 349 | 0, 350 | 1, 351 | 0, 352 | 0, 353 | 1, 354 | 0, 355 | 0, 356 | 0, 357 | 0, 358 | 2 359 | ], 360 | [ 361 | 0, 362 | 2, 363 | 1, 364 | 2, 365 | 0, 366 | 0, 367 | 1, 368 | 0, 369 | 2, 370 | 2, 371 | 2, 372 | 2 373 | ], 374 | [ 375 | 0, 376 | 2, 377 | 0, 378 | 1, 379 | 0, 380 | 0, 381 | 1, 382 | 0, 383 | 2, 384 | 0, 385 | 0, 386 | 2 387 | ], 388 | [ 389 | 0, 390 | 2, 391 | 1, 392 | 1, 393 | 0, 394 | 0, 395 | 1, 396 | 0, 397 | 2, 398 | 0, 399 | 0, 400 | 2 401 | ], 402 | [ 403 | 2, 404 | 2, 405 | 2, 406 | 2, 407 | 2, 408 | 2, 409 | 2, 410 | 2, 411 | 2, 412 | 2, 413 | 2, 414 | 2 415 | ] 416 | ], 417 | "rule_severities": { 418 | "bus_definition_conflict": "error", 419 | "bus_entry_needed": "error", 420 | "bus_to_bus_conflict": "error", 421 | "bus_to_net_conflict": "error", 422 | "conflicting_netclasses": "error", 423 | "different_unit_footprint": "error", 424 | "different_unit_net": "error", 425 | "duplicate_reference": "error", 426 | "duplicate_sheet_names": "error", 427 | "endpoint_off_grid": "warning", 428 | "extra_units": "error", 429 | "global_label_dangling": "warning", 430 | "hier_label_mismatch": "error", 431 | "label_dangling": "error", 432 | "lib_symbol_issues": "warning", 433 | "missing_bidi_pin": "warning", 434 | "missing_input_pin": "warning", 435 | "missing_power_pin": "error", 436 | "missing_unit": "warning", 437 | "multiple_net_names": "warning", 438 | "net_not_bus_member": "warning", 439 | "no_connect_connected": "warning", 440 | "no_connect_dangling": "warning", 441 | "pin_not_connected": "error", 442 | "pin_not_driven": "error", 443 | "pin_to_pin": "warning", 444 | "power_pin_not_driven": "error", 445 | "similar_labels": "warning", 446 | "simulation_model_issue": "ignore", 447 | "unannotated": "error", 448 | "unit_value_mismatch": "error", 449 | "unresolved_variable": "error", 450 | "wire_dangling": "error" 451 | } 452 | }, 453 | "libraries": { 454 | "pinned_footprint_libs": [], 455 | "pinned_symbol_libs": [] 456 | }, 457 | "meta": { 458 | "filename": "controller.kicad_pro", 459 | "version": 1 460 | }, 461 | "net_settings": { 462 | "classes": [ 463 | { 464 | "bus_width": 12, 465 | "clearance": 0.1524, 466 | "diff_pair_gap": 0.25, 467 | "diff_pair_via_gap": 0.25, 468 | "diff_pair_width": 0.2, 469 | "line_style": 0, 470 | "microvia_diameter": 0.3, 471 | "microvia_drill": 0.1, 472 | "name": "Default", 473 | "pcb_color": "rgba(0, 0, 0, 0.000)", 474 | "schematic_color": "rgba(0, 0, 0, 0.000)", 475 | "track_width": 0.2032, 476 | "via_diameter": 0.508, 477 | "via_drill": 0.254, 478 | "wire_width": 6 479 | } 480 | ], 481 | "meta": { 482 | "version": 3 483 | }, 484 | "net_colors": null, 485 | "netclass_assignments": null, 486 | "netclass_patterns": [] 487 | }, 488 | "pcbnew": { 489 | "last_paths": { 490 | "gencad": "", 491 | "idf": "", 492 | "netlist": "", 493 | "plot": "gerbers/", 494 | "pos_files": "./", 495 | "specctra_dsn": "", 496 | "step": "controller-pcb.step", 497 | "svg": "", 498 | "vrml": "" 499 | }, 500 | "page_layout_descr_file": "" 501 | }, 502 | "schematic": { 503 | "annotate_start_num": 0, 504 | "bom_fmt_presets": [], 505 | "bom_fmt_settings": { 506 | "field_delimiter": ",", 507 | "keep_line_breaks": false, 508 | "keep_tabs": false, 509 | "name": "CSV", 510 | "ref_delimiter": ",", 511 | "ref_range_delimiter": "", 512 | "string_delimiter": "\"" 513 | }, 514 | "bom_presets": [], 515 | "bom_settings": { 516 | "exclude_dnp": false, 517 | "fields_ordered": [ 518 | { 519 | "group_by": false, 520 | "label": "Reference", 521 | "name": "Reference", 522 | "show": true 523 | }, 524 | { 525 | "group_by": true, 526 | "label": "Value", 527 | "name": "Value", 528 | "show": true 529 | }, 530 | { 531 | "group_by": false, 532 | "label": "Datasheet", 533 | "name": "Datasheet", 534 | "show": true 535 | }, 536 | { 537 | "group_by": false, 538 | "label": "Footprint", 539 | "name": "Footprint", 540 | "show": true 541 | }, 542 | { 543 | "group_by": false, 544 | "label": "Qty", 545 | "name": "${QUANTITY}", 546 | "show": true 547 | }, 548 | { 549 | "group_by": true, 550 | "label": "DNP", 551 | "name": "${DNP}", 552 | "show": true 553 | }, 554 | { 555 | "group_by": false, 556 | "label": "#", 557 | "name": "${ITEM_NUMBER}", 558 | "show": false 559 | }, 560 | { 561 | "group_by": false, 562 | "label": "MANUFACTURER", 563 | "name": "MANUFACTURER", 564 | "show": false 565 | }, 566 | { 567 | "group_by": false, 568 | "label": "Description", 569 | "name": "Description", 570 | "show": false 571 | } 572 | ], 573 | "filter_string": "", 574 | "group_symbols": true, 575 | "name": "", 576 | "sort_asc": true, 577 | "sort_field": "Reference" 578 | }, 579 | "connection_grid_size": 50.0, 580 | "drawing": { 581 | "dashed_lines_dash_length_ratio": 12.0, 582 | "dashed_lines_gap_length_ratio": 3.0, 583 | "default_line_thickness": 6.0, 584 | "default_text_size": 50.0, 585 | "field_names": [], 586 | "intersheets_ref_own_page": false, 587 | "intersheets_ref_prefix": "", 588 | "intersheets_ref_short": false, 589 | "intersheets_ref_show": false, 590 | "intersheets_ref_suffix": "", 591 | "junction_size_choice": 3, 592 | "label_size_ratio": 0.375, 593 | "operating_point_overlay_i_precision": 3, 594 | "operating_point_overlay_i_range": "~A", 595 | "operating_point_overlay_v_precision": 3, 596 | "operating_point_overlay_v_range": "~V", 597 | "overbar_offset_ratio": 1.23, 598 | "pin_symbol_size": 25.0, 599 | "text_offset_ratio": 0.15 600 | }, 601 | "legacy_lib_dir": "", 602 | "legacy_lib_list": [], 603 | "meta": { 604 | "version": 1 605 | }, 606 | "net_format_name": "", 607 | "page_layout_descr_file": "", 608 | "plot_directory": "", 609 | "spice_current_sheet_as_root": false, 610 | "spice_external_command": "spice \"%I\"", 611 | "spice_model_current_sheet_as_root": true, 612 | "spice_save_all_currents": false, 613 | "spice_save_all_dissipations": false, 614 | "spice_save_all_voltages": false, 615 | "subpart_first_id": 65, 616 | "subpart_id_separator": 0 617 | }, 618 | "sheets": [ 619 | [ 620 | "11c7bd96-786c-4c10-b71b-7b198cc052d5", 621 | "Root" 622 | ] 623 | ], 624 | "text_variables": {} 625 | } 626 | -------------------------------------------------------------------------------- /hardware/thumbtroller/thumbtroller-v0.2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuckAtPrototype/Racer/f43d1263155c1e04efdd052b35089c74f59d61d8/hardware/thumbtroller/thumbtroller-v0.2.zip -------------------------------------------------------------------------------- /project_pictures/242A0548.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuckAtPrototype/Racer/f43d1263155c1e04efdd052b35089c74f59d61d8/project_pictures/242A0548.png -------------------------------------------------------------------------------- /project_pictures/242A1274.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuckAtPrototype/Racer/f43d1263155c1e04efdd052b35089c74f59d61d8/project_pictures/242A1274.png -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | ## trainer.py 2 | 3 | #### to run, simply call `python trainer.py` 4 | 5 | trainer.py is the training algorithm for the neural network embedded within the Racer. 6 | 7 | To use, `trainer.py` ensure you have classified data in `color_data.txt` 8 | 9 | Here is the format: 10 | 11 | ``` 12 | W (17447) main: Color values - Red: 1822, Green: 2184, Blue: 1762, Clear: 2008, Color: White 13 | W (9847) main: Color values - Red: 220, Green: 472, Blue: 124, Clear: 1780, Color: Black 14 | W (16447) main: Color values - Red: 488, Green: 748, Blue: 196, Clear: 1620, Color: Red 15 | W (19947) main: Color values - Red: 428, Green: 1368, Blue: 336, Clear: 2148, Color: Green 16 | ``` 17 | 18 | it requires all colors to be present to train. 200 samples of each color seems to be ok. 19 | 20 | 21 | ## controller.py 22 | 23 | controller.py is a simple BLE script that accepts keyboard input and relays it to the Racer. Its great for debugging. 24 | 25 | #### to run, simply call `python controller.py` -------------------------------------------------------------------------------- /scripts/ble_device_config.json: -------------------------------------------------------------------------------- 1 | {"ble_address": "74:4D:BD:61:A7:B3"} -------------------------------------------------------------------------------- /scripts/color_data.txt: -------------------------------------------------------------------------------- 1 | W (647) main: Color values - Red: 440, Green: 1378, Blue: 328, Clear: 1060, Color: Green 2 | W (747) main: Color values - Red: 428, Green: 1376, Blue: 324, Clear: 1060, Color: Green 3 | W (847) main: Color values - Red: 432, Green: 1384, Blue: 332, Clear: 1064, Color: Green 4 | W (947) main: Color values - Red: 436, Green: 1380, Blue: 336, Clear: 1060, Color: Green 5 | W (1047) main: Color values - Red: 432, Green: 1380, Blue: 336, Clear: 1060, Color: Green 6 | W (1147) main: Color values - Red: 432, Green: 1380, Blue: 336, Clear: 1064, Color: Green 7 | W (1247) main: Color values - Red: 428, Green: 1376, Blue: 332, Clear: 1064, Color: Green 8 | W (1347) main: Color values - Red: 436, Green: 1376, Blue: 328, Clear: 1068, Color: Green 9 | W (1447) main: Color values - Red: 436, Green: 1376, Blue: 332, Clear: 2156, Color: Green 10 | W (1547) main: Color values - Red: 440, Green: 1388, Blue: 328, Clear: 2156, Color: Green 11 | W (1647) main: Color values - Red: 436, Green: 1384, Blue: 328, Clear: 2144, Color: Green 12 | W (1747) main: Color values - Red: 440, Green: 1360, Blue: 332, Clear: 2144, Color: Green 13 | W (1847) main: Color values - Red: 436, Green: 1380, Blue: 330, Clear: 2152, Color: Green 14 | W (1947) main: Color values - Red: 436, Green: 1372, Blue: 332, Clear: 2160, Color: Green 15 | W (2047) main: Color values - Red: 432, Green: 1368, Blue: 332, Clear: 2156, Color: Green 16 | W (2147) main: Color values - Red: 428, Green: 1368, Blue: 320, Clear: 2144, Color: Green 17 | W (2247) main: Color values - Red: 428, Green: 1364, Blue: 320, Clear: 2136, Color: Green 18 | W (2347) main: Color values - Red: 436, Green: 1356, Blue: 324, Clear: 2132, Color: Green 19 | W (2447) main: Color values - Red: 432, Green: 1352, Blue: 316, Clear: 2160, Color: Green 20 | W (2547) main: Color values - Red: 432, Green: 1352, Blue: 328, Clear: 2140, Color: Green 21 | W (2647) main: Color values - Red: 440, Green: 1384, Blue: 336, Clear: 2180, Color: Green 22 | W (2747) main: Color values - Red: 440, Green: 1404, Blue: 348, Clear: 2216, Color: Green 23 | W (2847) main: Color values - Red: 440, Green: 1384, Blue: 328, Clear: 2156, Color: Green 24 | W (2947) main: Color values - Red: 440, Green: 1400, Blue: 282, Clear: 2192, Color: Green 25 | W (3047) main: Color values - Red: 436, Green: 1408, Blue: 332, Clear: 2180, Color: Green 26 | W (3147) main: Color values - Red: 440, Green: 1376, Blue: 328, Clear: 2184, Color: Green 27 | W (3247) main: Color values - Red: 448, Green: 1408, Blue: 344, Clear: 2212, Color: Green 28 | W (3347) main: Color values - Red: 440, Green: 1404, Blue: 336, Clear: 2180, Color: Green 29 | W (3447) main: Color values - Red: 440, Green: 1396, Blue: 332, Clear: 2152, Color: Green 30 | W (3547) main: Color values - Red: 444, Green: 1404, Blue: 336, Clear: 2172, Color: Green 31 | W (3647) main: Color values - Red: 444, Green: 1404, Blue: 332, Clear: 2184, Color: Green 32 | W (3747) main: Color values - Red: 448, Green: 1378, Blue: 328, Clear: 2188, Color: Green 33 | W (3847) main: Color values - Red: 428, Green: 1368, Blue: 328, Clear: 2140, Color: Green 34 | W (3947) main: Color values - Red: 428, Green: 1368, Blue: 320, Clear: 2132, Color: Green 35 | W (4047) main: Color values - Red: 398, Green: 1264, Blue: 320, Clear: 2112, Color: Green 36 | W (4147) main: Color values - Red: 432, Green: 1380, Blue: 328, Clear: 2128, Color: Green 37 | W (4247) main: Color values - Red: 428, Green: 1368, Blue: 320, Clear: 2136, Color: Green 38 | W (4347) main: Color values - Red: 440, Green: 1352, Blue: 324, Clear: 2152, Color: Green 39 | W (4447) main: Color values - Red: 424, Green: 1372, Blue: 324, Clear: 2156, Color: Green 40 | W (4547) main: Color values - Red: 432, Green: 1376, Blue: 328, Clear: 2152, Color: Green 41 | W (4647) main: Color values - Red: 432, Green: 1384, Blue: 320, Clear: 2100, Color: Green 42 | W (4747) main: Color values - Red: 440, Green: 1388, Blue: 328, Clear: 2164, Color: Green 43 | W (4847) main: Color values - Red: 436, Green: 1388, Blue: 324, Clear: 2164, Color: Green 44 | W (4947) main: Color values - Red: 436, Green: 1368, Blue: 328, Clear: 2172, Color: Green 45 | W (5047) main: Color values - Red: 448, Green: 1412, Blue: 336, Clear: 2216, Color: Green 46 | W (5147) main: Color values - Red: 448, Green: 1392, Blue: 336, Clear: 2176, Color: Green 47 | W (5247) main: Color values - Red: 448, Green: 1420, Blue: 340, Clear: 2192, Color: Green 48 | W (5347) main: Color values - Red: 440, Green: 1384, Blue: 326, Clear: 2184, Color: Green 49 | W (5447) main: Color values - Red: 432, Green: 1380, Blue: 340, Clear: 2180, Color: Green 50 | W (5547) main: Color values - Red: 444, Green: 1396, Blue: 334, Clear: 2192, Color: Green 51 | W (5647) main: Color values - Red: 440, Green: 1388, Blue: 340, Clear: 2184, Color: Green 52 | W (5747) main: Color values - Red: 432, Green: 1380, Blue: 332, Clear: 2144, Color: Green 53 | W (5847) main: Color values - Red: 436, Green: 1364, Blue: 324, Clear: 2128, Color: Green 54 | W (5947) main: Color values - Red: 424, Green: 1352, Blue: 260, Clear: 2132, Color: Green 55 | W (6047) main: Color values - Red: 428, Green: 1360, Blue: 336, Clear: 2144, Color: Green 56 | W (6147) main: Color values - Red: 440, Green: 1388, Blue: 328, Clear: 2164, Color: Green 57 | W (6247) main: Color values - Red: 432, Green: 1368, Blue: 328, Clear: 2152, Color: Green 58 | W (6347) main: Color values - Red: 436, Green: 1382, Blue: 328, Clear: 2152, Color: Green 59 | W (6447) main: Color values - Red: 448, Green: 1384, Blue: 332, Clear: 2156, Color: Green 60 | W (6547) main: Color values - Red: 432, Green: 1380, Blue: 324, Clear: 2160, Color: Green 61 | W (6647) main: Color values - Red: 432, Green: 1360, Blue: 324, Clear: 2140, Color: Green 62 | W (6747) main: Color values - Red: 436, Green: 1376, Blue: 324, Clear: 2152, Color: Green 63 | W (6847) main: Color values - Red: 436, Green: 1376, Blue: 328, Clear: 2156, Color: Green 64 | W (6947) main: Color values - Red: 432, Green: 1368, Blue: 324, Clear: 2124, Color: Green 65 | W (7047) main: Color values - Red: 436, Green: 1364, Blue: 332, Clear: 1898, Color: Green 66 | W (7147) main: Color values - Red: 348, Green: 1100, Blue: 260, Clear: 1740, Color: Green 67 | W (7247) main: Color values - Red: 396, Green: 1240, Blue: 308, Clear: 1968, Color: Green 68 | W (7347) main: Color values - Red: 344, Green: 1064, Blue: 260, Clear: 1700, Color: Green 69 | W (7447) main: Color values - Red: 304, Green: 916, Blue: 224, Clear: 1464, Color: Green 70 | W (7547) main: Color values - Red: 256, Green: 760, Blue: 184, Clear: 1236, Color: Green 71 | W (7647) main: Color values - Red: 232, Green: 652, Blue: 148, Clear: 1072, Color: Green 72 | W (7747) main: Color values - Red: 220, Green: 556, Blue: 132, Clear: 1952, Color: Green 73 | W (7847) main: Color values - Red: 264, Green: 776, Blue: 188, Clear: 2560, Color: Green 74 | W (7947) main: Color values - Red: 332, Green: 1048, Blue: 256, Clear: 1664, Color: Green 75 | W (8047) main: Color values - Red: 408, Green: 1280, Blue: 312, Clear: 2020, Color: Green 76 | W (8147) main: Color values - Red: 440, Green: 1384, Blue: 324, Clear: 2164, Color: Green 77 | W (8247) main: Color values - Red: 436, Green: 1368, Blue: 336, Clear: 2164, Color: Green 78 | W (8347) main: Color values - Red: 440, Green: 1380, Blue: 332, Clear: 2164, Color: Green 79 | W (8447) main: Color values - Red: 432, Green: 1364, Blue: 332, Clear: 2152, Color: Green 80 | W (8547) main: Color values - Red: 436, Green: 1372, Blue: 328, Clear: 2140, Color: Green 81 | W (8647) main: Color values - Red: 432, Green: 1364, Blue: 336, Clear: 2140, Color: Green 82 | W (8747) main: Color values - Red: 432, Green: 1388, Blue: 332, Clear: 2176, Color: Green 83 | W (8847) main: Color values - Red: 436, Green: 1390, Blue: 340, Clear: 2168, Color: Green 84 | W (8947) main: Color values - Red: 444, Green: 1376, Blue: 310, Clear: 2044, Color: Green 85 | W (9047) main: Color values - Red: 428, Green: 1368, Blue: 328, Clear: 2140, Color: Green 86 | W (9147) main: Color values - Red: 432, Green: 1348, Blue: 320, Clear: 2120, Color: Green 87 | W (9247) main: Color values - Red: 432, Green: 1376, Blue: 324, Clear: 2156, Color: Green 88 | W (9347) main: Color values - Red: 388, Green: 1232, Blue: 288, Clear: 1916, Color: Green 89 | W (9447) main: Color values - Red: 432, Green: 1380, Blue: 332, Clear: 2156, Color: Green 90 | W (9547) main: Color values - Red: 436, Green: 1360, Blue: 328, Clear: 2140, Color: Green 91 | W (9647) main: Color values - Red: 360, Green: 1252, Blue: 300, Clear: 1908, Color: Green 92 | W (9747) main: Color values - Red: 432, Green: 1396, Blue: 332, Clear: 2168, Color: Green 93 | W (9847) main: Color values - Red: 432, Green: 1376, Blue: 332, Clear: 2144, Color: Green 94 | W (9947) main: Color values - Red: 432, Green: 1384, Blue: 324, Clear: 2168, Color: Green 95 | W (10047) main: Color values - Red: 432, Green: 1364, Blue: 320, Clear: 2136, Color: Green 96 | W (10147) main: Color values - Red: 436, Green: 1376, Blue: 336, Clear: 2160, Color: Green 97 | W (10247) main: Color values - Red: 432, Green: 1372, Blue: 328, Clear: 2136, Color: Green 98 | W (10347) main: Color values - Red: 384, Green: 1216, Blue: 278, Clear: 1788, Color: Green 99 | W (10447) main: Color values - Red: 428, Green: 1348, Blue: 324, Clear: 2088, Color: Green 100 | W (10547) main: Color values - Red: 428, Green: 1364, Blue: 318, Clear: 1956, Color: Green 101 | W (10647) main: Color values - Red: 424, Green: 1352, Blue: 328, Clear: 2132, Color: Green 102 | W (10747) main: Color values - Red: 440, Green: 1462, Blue: 344, Clear: 2188, Color: Green 103 | W (10847) main: Color values - Red: 428, Green: 1376, Blue: 320, Clear: 2168, Color: Green 104 | W (10947) main: Color values - Red: 436, Green: 1228, Blue: 324, Clear: 2156, Color: Green 105 | W (11047) main: Color values - Red: 440, Green: 1376, Blue: 324, Clear: 2116, Color: Green 106 | W (11147) main: Color values - Red: 424, Green: 1372, Blue: 318, Clear: 2148, Color: Green 107 | W (11247) main: Color values - Red: 424, Green: 1364, Blue: 312, Clear: 2148, Color: Green 108 | W (11347) main: Color values - Red: 428, Green: 1352, Blue: 324, Clear: 2156, Color: Green 109 | W (11447) main: Color values - Red: 436, Green: 1376, Blue: 332, Clear: 2172, Color: Green 110 | W (11547) main: Color values - Red: 440, Green: 1380, Blue: 332, Clear: 2188, Color: Green 111 | W (11647) main: Color values - Red: 448, Green: 1410, Blue: 344, Clear: 2196, Color: Green 112 | W (11747) main: Color values - Red: 456, Green: 1432, Blue: 336, Clear: 2220, Color: Green 113 | W (11847) main: Color values - Red: 444, Green: 1392, Blue: 332, Clear: 2212, Color: Green 114 | W (11947) main: Color values - Red: 456, Green: 1420, Blue: 340, Clear: 2236, Color: Green 115 | W (12047) main: Color values - Red: 448, Green: 1432, Blue: 344, Clear: 2232, Color: Green 116 | W (12147) main: Color values - Red: 456, Green: 1448, Blue: 348, Clear: 2236, Color: Green 117 | W (12247) main: Color values - Red: 460, Green: 1460, Blue: 344, Clear: 2264, Color: Green 118 | W (12347) main: Color values - Red: 452, Green: 1424, Blue: 340, Clear: 2244, Color: Green 119 | W (12447) main: Color values - Red: 448, Green: 1424, Blue: 340, Clear: 2196, Color: Green 120 | W (12547) main: Color values - Red: 436, Green: 1372, Blue: 328, Clear: 2140, Color: Green 121 | W (12647) main: Color values - Red: 372, Green: 1132, Blue: 264, Clear: 1716, Color: Green 122 | W (12747) main: Color values - Red: 392, Green: 1188, Blue: 292, Clear: 1836, Color: Green 123 | W (12847) main: Color values - Red: 420, Green: 1304, Blue: 320, Clear: 2028, Color: Green 124 | W (12947) main: Color values - Red: 428, Green: 1368, Blue: 332, Clear: 2140, Color: Green 125 | W (13047) main: Color values - Red: 436, Green: 1364, Blue: 328, Clear: 2152, Color: Green 126 | W (13147) main: Color values - Red: 428, Green: 1376, Blue: 332, Clear: 2144, Color: Green 127 | W (13247) main: Color values - Red: 440, Green: 1392, Blue: 328, Clear: 2156, Color: Green 128 | W (13347) main: Color values - Red: 436, Green: 1376, Blue: 340, Clear: 2168, Color: Green 129 | W (13447) main: Color values - Red: 432, Green: 1368, Blue: 320, Clear: 2160, Color: Green 130 | W (13547) main: Color values - Red: 428, Green: 1360, Blue: 324, Clear: 2140, Color: Green 131 | W (13647) main: Color values - Red: 432, Green: 1368, Blue: 328, Clear: 2132, Color: Green 132 | W (13747) main: Color values - Red: 428, Green: 1376, Blue: 320, Clear: 2132, Color: Green 133 | W (13847) main: Color values - Red: 440, Green: 1400, Blue: 336, Clear: 2184, Color: Green 134 | W (13947) main: Color values - Red: 440, Green: 1388, Blue: 340, Clear: 2204, Color: Green 135 | W (14047) main: Color values - Red: 440, Green: 1400, Blue: 332, Clear: 2188, Color: Green 136 | W (14147) main: Color values - Red: 444, Green: 1408, Blue: 336, Clear: 2208, Color: Green 137 | W (14247) main: Color values - Red: 452, Green: 1420, Blue: 336, Clear: 2228, Color: Green 138 | W (14347) main: Color values - Red: 456, Green: 1436, Blue: 340, Clear: 2240, Color: Green 139 | W (14447) main: Color values - Red: 448, Green: 1404, Blue: 340, Clear: 2184, Color: Green 140 | W (14547) main: Color values - Red: 432, Green: 1360, Blue: 324, Clear: 2124, Color: Green 141 | W (14647) main: Color values - Red: 344, Green: 1020, Blue: 240, Clear: 1604, Color: Green 142 | W (14747) main: Color values - Red: 336, Green: 912, Blue: 208, Clear: 1424, Color: Green 143 | W (14847) main: Color values - Red: 328, Green: 880, Blue: 216, Clear: 1440, Color: Green 144 | W (14947) main: Color values - Red: 328, Green: 864, Blue: 196, Clear: 1352, Color: Green 145 | W (15047) main: Color values - Red: 342, Green: 1000, Blue: 236, Clear: 1548, Color: Green 146 | W (15147) main: Color values - Red: 440, Green: 1372, Blue: 328, Clear: 2148, Color: Green 147 | W (15247) main: Color values - Red: 444, Green: 1416, Blue: 340, Clear: 2200, Color: Green 148 | W (15347) main: Color values - Red: 448, Green: 1410, Blue: 344, Clear: 2192, Color: Green 149 | W (15447) main: Color values - Red: 368, Green: 1152, Blue: 280, Clear: 1864, Color: Green 150 | W (15547) main: Color values - Red: 336, Green: 1036, Blue: 244, Clear: 1644, Color: Green 151 | W (15647) main: Color values - Red: 360, Green: 1140, Blue: 272, Clear: 1764, Color: Green 152 | W (15747) main: Color values - Red: 440, Green: 1384, Blue: 320, Clear: 2164, Color: Green 153 | W (15847) main: Color values - Red: 448, Green: 1416, Blue: 340, Clear: 2204, Color: Green 154 | W (15947) main: Color values - Red: 440, Green: 1396, Blue: 332, Clear: 2204, Color: Green 155 | W (16047) main: Color values - Red: 444, Green: 1392, Blue: 336, Clear: 2192, Color: Green 156 | W (16147) main: Color values - Red: 440, Green: 1384, Blue: 324, Clear: 2168, Color: Green 157 | W (16247) main: Color values - Red: 444, Green: 1376, Blue: 332, Clear: 2164, Color: Green 158 | W (16347) main: Color values - Red: 436, Green: 1376, Blue: 324, Clear: 2160, Color: Green 159 | W (16447) main: Color values - Red: 428, Green: 1364, Blue: 324, Clear: 2120, Color: Green 160 | W (16547) main: Color values - Red: 436, Green: 1372, Blue: 336, Clear: 2156, Color: Green 161 | W (16647) main: Color values - Red: 436, Green: 1376, Blue: 324, Clear: 2144, Color: Green 162 | W (16747) main: Color values - Red: 432, Green: 1352, Blue: 320, Clear: 2140, Color: Green 163 | W (16847) main: Color values - Red: 424, Green: 1356, Blue: 322, Clear: 2104, Color: Green 164 | W (16947) main: Color values - Red: 432, Green: 1368, Blue: 278, Clear: 2188, Color: Green 165 | W (17047) main: Color values - Red: 424, Green: 1344, Blue: 316, Clear: 2084, Color: Green 166 | W (17147) main: Color values - Red: 432, Green: 1372, Blue: 328, Clear: 2104, Color: Green 167 | W (17247) main: Color values - Red: 424, Green: 1340, Blue: 316, Clear: 2100, Color: Green 168 | W (17347) main: Color values - Red: 432, Green: 1372, Blue: 328, Clear: 2180, Color: Green 169 | W (17447) main: Color values - Red: 436, Green: 1312, Blue: 324, Clear: 2160, Color: Green 170 | W (17547) main: Color values - Red: 416, Green: 1340, Blue: 316, Clear: 2096, Color: Green 171 | W (17647) main: Color values - Red: 416, Green: 1316, Blue: 304, Clear: 2060, Color: Green 172 | W (17747) main: Color values - Red: 428, Green: 1368, Blue: 328, Clear: 2072, Color: Green 173 | W (17847) main: Color values - Red: 412, Green: 1320, Blue: 312, Clear: 2076, Color: Green 174 | W (17947) main: Color values - Red: 416, Green: 1316, Blue: 302, Clear: 2068, Color: Green 175 | W (18047) main: Color values - Red: 424, Green: 1352, Blue: 284, Clear: 2120, Color: Green 176 | W (18147) main: Color values - Red: 416, Green: 1336, Blue: 320, Clear: 2088, Color: Green 177 | W (18247) main: Color values - Red: 404, Green: 1316, Blue: 312, Clear: 1772, Color: Green 178 | W (18347) main: Color values - Red: 416, Green: 1320, Blue: 320, Clear: 2072, Color: Green 179 | W (18447) main: Color values - Red: 436, Green: 1380, Blue: 328, Clear: 2136, Color: Green 180 | W (18547) main: Color values - Red: 432, Green: 1372, Blue: 322, Clear: 2152, Color: Green 181 | W (18647) main: Color values - Red: 440, Green: 1372, Blue: 328, Clear: 2152, Color: Green 182 | W (18747) main: Color values - Red: 436, Green: 1364, Blue: 332, Clear: 2152, Color: Green 183 | W (18847) main: Color values - Red: 432, Green: 1376, Blue: 328, Clear: 2148, Color: Green 184 | W (18947) main: Color values - Red: 432, Green: 1372, Blue: 332, Clear: 2144, Color: Green 185 | W (19047) main: Color values - Red: 432, Green: 1372, Blue: 336, Clear: 2148, Color: Green 186 | W (19147) main: Color values - Red: 436, Green: 1332, Blue: 324, Clear: 2148, Color: Green 187 | W (19247) main: Color values - Red: 428, Green: 1368, Blue: 332, Clear: 2140, Color: Green 188 | W (19347) main: Color values - Red: 436, Green: 1364, Blue: 328, Clear: 2148, Color: Green 189 | W (19447) main: Color values - Red: 432, Green: 1372, Blue: 332, Clear: 2148, Color: Green 190 | W (19547) main: Color values - Red: 428, Green: 1376, Blue: 332, Clear: 2132, Color: Green 191 | W (19647) main: Color values - Red: 432, Green: 1372, Blue: 324, Clear: 2148, Color: Green 192 | W (19747) main: Color values - Red: 440, Green: 1368, Blue: 332, Clear: 2140, Color: Green 193 | W (19847) main: Color values - Red: 432, Green: 1372, Blue: 324, Clear: 2156, Color: Green 194 | W (19947) main: Color values - Red: 428, Green: 1368, Blue: 336, Clear: 2148, Color: Green 195 | W (647) main: Color values - Red: 484, Green: 720, Blue: 188, Clear: 1596, Color: Red 196 | W (747) main: Color values - Red: 480, Green: 716, Blue: 192, Clear: 1600, Color: Red 197 | W (847) main: Color values - Red: 488, Green: 728, Blue: 192, Clear: 1604, Color: Red 198 | W (947) main: Color values - Red: 484, Green: 716, Blue: 188, Clear: 1608, Color: Red 199 | W (1047) main: Color values - Red: 488, Green: 724, Blue: 184, Clear: 1608, Color: Red 200 | W (1147) main: Color values - Red: 488, Green: 728, Blue: 188, Clear: 1600, Color: Red 201 | W (1247) main: Color values - Red: 488, Green: 732, Blue: 188, Clear: 1604, Color: Red 202 | W (1347) main: Color values - Red: 488, Green: 720, Blue: 192, Clear: 1608, Color: Red 203 | W (1447) main: Color values - Red: 488, Green: 724, Blue: 192, Clear: 1604, Color: Red 204 | W (1547) main: Color values - Red: 488, Green: 724, Blue: 184, Clear: 1600, Color: Red 205 | W (1647) main: Color values - Red: 484, Green: 716, Blue: 188, Clear: 1604, Color: Red 206 | W (1747) main: Color values - Red: 480, Green: 728, Blue: 182, Clear: 1596, Color: Red 207 | W (1847) main: Color values - Red: 484, Green: 732, Blue: 188, Clear: 1608, Color: Red 208 | W (1947) main: Color values - Red: 480, Green: 728, Blue: 184, Clear: 1596, Color: Red 209 | W (2047) main: Color values - Red: 488, Green: 724, Blue: 184, Clear: 1596, Color: Red 210 | W (2147) main: Color values - Red: 488, Green: 724, Blue: 192, Clear: 1600, Color: Red 211 | W (2247) main: Color values - Red: 484, Green: 720, Blue: 188, Clear: 1604, Color: Red 212 | W (2347) main: Color values - Red: 484, Green: 724, Blue: 188, Clear: 1600, Color: Red 213 | W (2447) main: Color values - Red: 480, Green: 724, Blue: 196, Clear: 1608, Color: Red 214 | W (2547) main: Color values - Red: 484, Green: 716, Blue: 188, Clear: 1600, Color: Red 215 | W (2647) main: Color values - Red: 488, Green: 724, Blue: 188, Clear: 1604, Color: Red 216 | W (2747) main: Color values - Red: 488, Green: 720, Blue: 192, Clear: 1596, Color: Red 217 | W (2847) main: Color values - Red: 488, Green: 720, Blue: 188, Clear: 1600, Color: Red 218 | W (2947) main: Color values - Red: 488, Green: 716, Blue: 188, Clear: 1604, Color: Red 219 | W (3047) main: Color values - Red: 488, Green: 716, Blue: 192, Clear: 1600, Color: Red 220 | W (3147) main: Color values - Red: 488, Green: 724, Blue: 184, Clear: 1604, Color: Red 221 | W (3247) main: Color values - Red: 484, Green: 724, Blue: 188, Clear: 1600, Color: Red 222 | W (3347) main: Color values - Red: 488, Green: 724, Blue: 184, Clear: 1604, Color: Red 223 | W (3447) main: Color values - Red: 488, Green: 724, Blue: 184, Clear: 1608, Color: Red 224 | W (3547) main: Color values - Red: 484, Green: 724, Blue: 196, Clear: 1612, Color: Red 225 | W (3647) main: Color values - Red: 488, Green: 712, Blue: 192, Clear: 1596, Color: Red 226 | W (3747) main: Color values - Red: 480, Green: 716, Blue: 192, Clear: 1604, Color: Red 227 | W (3847) main: Color values - Red: 480, Green: 716, Blue: 188, Clear: 1604, Color: Red 228 | W (3947) main: Color values - Red: 484, Green: 720, Blue: 184, Clear: 1600, Color: Red 229 | W (4047) main: Color values - Red: 490, Green: 728, Blue: 192, Clear: 1592, Color: Red 230 | W (4147) main: Color values - Red: 492, Green: 724, Blue: 192, Clear: 1604, Color: Red 231 | W (4247) main: Color values - Red: 484, Green: 720, Blue: 188, Clear: 1600, Color: Red 232 | W (4347) main: Color values - Red: 480, Green: 720, Blue: 188, Clear: 1596, Color: Red 233 | W (4447) main: Color values - Red: 480, Green: 720, Blue: 192, Clear: 1604, Color: Red 234 | W (4547) main: Color values - Red: 488, Green: 728, Blue: 188, Clear: 1604, Color: Red 235 | W (4647) main: Color values - Red: 488, Green: 724, Blue: 180, Clear: 1600, Color: Red 236 | W (4747) main: Color values - Red: 484, Green: 720, Blue: 184, Clear: 1600, Color: Red 237 | W (4847) main: Color values - Red: 488, Green: 720, Blue: 188, Clear: 1604, Color: Red 238 | W (4947) main: Color values - Red: 488, Green: 724, Blue: 182, Clear: 1604, Color: Red 239 | W (5047) main: Color values - Red: 480, Green: 720, Blue: 180, Clear: 1580, Color: Red 240 | W (5147) main: Color values - Red: 492, Green: 764, Blue: 200, Clear: 1616, Color: Red 241 | W (5247) main: Color values - Red: 452, Green: 684, Blue: 168, Clear: 1432, Color: Red 242 | W (5347) main: Color values - Red: 480, Green: 724, Blue: 180, Clear: 1592, Color: Red 243 | W (5447) main: Color values - Red: 476, Green: 708, Blue: 188, Clear: 1548, Color: Red 244 | W (5547) main: Color values - Red: 472, Green: 716, Blue: 192, Clear: 1576, Color: Red 245 | W (5647) main: Color values - Red: 476, Green: 716, Blue: 184, Clear: 1580, Color: Red 246 | W (5747) main: Color values - Red: 480, Green: 716, Blue: 184, Clear: 1564, Color: Red 247 | W (5847) main: Color values - Red: 488, Green: 728, Blue: 188, Clear: 1612, Color: Red 248 | W (5947) main: Color values - Red: 488, Green: 740, Blue: 192, Clear: 1604, Color: Red 249 | W (6047) main: Color values - Red: 492, Green: 732, Blue: 196, Clear: 1632, Color: Red 250 | W (6147) main: Color values - Red: 484, Green: 740, Blue: 188, Clear: 1588, Color: Red 251 | W (6247) main: Color values - Red: 488, Green: 736, Blue: 192, Clear: 1628, Color: Red 252 | W (6347) main: Color values - Red: 496, Green: 732, Blue: 192, Clear: 1592, Color: Red 253 | W (6447) main: Color values - Red: 488, Green: 732, Blue: 196, Clear: 1604, Color: Red 254 | W (6547) main: Color values - Red: 460, Green: 688, Blue: 180, Clear: 1516, Color: Red 255 | W (6647) main: Color values - Red: 484, Green: 716, Blue: 200, Clear: 1596, Color: Red 256 | W (6747) main: Color values - Red: 452, Green: 668, Blue: 180, Clear: 1472, Color: Red 257 | W (6847) main: Color values - Red: 484, Green: 724, Blue: 188, Clear: 1584, Color: Red 258 | W (6947) main: Color values - Red: 476, Green: 712, Blue: 180, Clear: 1588, Color: Red 259 | W (7047) main: Color values - Red: 492, Green: 732, Blue: 188, Clear: 1588, Color: Red 260 | W (7147) main: Color values - Red: 488, Green: 728, Blue: 188, Clear: 1600, Color: Red 261 | W (7247) main: Color values - Red: 488, Green: 728, Blue: 188, Clear: 1596, Color: Red 262 | W (7347) main: Color values - Red: 488, Green: 740, Blue: 188, Clear: 1612, Color: Red 263 | W (7447) main: Color values - Red: 480, Green: 724, Blue: 184, Clear: 1604, Color: Red 264 | W (7547) main: Color values - Red: 484, Green: 736, Blue: 192, Clear: 1612, Color: Red 265 | W (7647) main: Color values - Red: 488, Green: 728, Blue: 184, Clear: 1612, Color: Red 266 | W (7747) main: Color values - Red: 484, Green: 724, Blue: 188, Clear: 1612, Color: Red 267 | W (7847) main: Color values - Red: 480, Green: 716, Blue: 184, Clear: 1556, Color: Red 268 | W (7947) main: Color values - Red: 464, Green: 696, Blue: 180, Clear: 1508, Color: Red 269 | W (8047) main: Color values - Red: 460, Green: 668, Blue: 164, Clear: 1516, Color: Red 270 | W (8147) main: Color values - Red: 488, Green: 726, Blue: 188, Clear: 1620, Color: Red 271 | W (8247) main: Color values - Red: 484, Green: 732, Blue: 188, Clear: 1600, Color: Red 272 | W (8347) main: Color values - Red: 480, Green: 728, Blue: 184, Clear: 1604, Color: Red 273 | W (8447) main: Color values - Red: 488, Green: 736, Blue: 188, Clear: 1612, Color: Red 274 | W (8547) main: Color values - Red: 476, Green: 728, Blue: 200, Clear: 1576, Color: Red 275 | W (8647) main: Color values - Red: 480, Green: 732, Blue: 196, Clear: 1584, Color: Red 276 | W (8747) main: Color values - Red: 480, Green: 736, Blue: 200, Clear: 1604, Color: Red 277 | W (8847) main: Color values - Red: 488, Green: 736, Blue: 200, Clear: 1624, Color: Red 278 | W (8947) main: Color values - Red: 488, Green: 728, Blue: 188, Clear: 1600, Color: Red 279 | W (9047) main: Color values - Red: 488, Green: 736, Blue: 188, Clear: 1608, Color: Red 280 | W (9147) main: Color values - Red: 488, Green: 732, Blue: 184, Clear: 1596, Color: Red 281 | W (9247) main: Color values - Red: 480, Green: 732, Blue: 188, Clear: 1604, Color: Red 282 | W (9347) main: Color values - Red: 484, Green: 724, Blue: 196, Clear: 1612, Color: Red 283 | W (9447) main: Color values - Red: 484, Green: 732, Blue: 192, Clear: 1600, Color: Red 284 | W (9547) main: Color values - Red: 492, Green: 732, Blue: 192, Clear: 1604, Color: Red 285 | W (9647) main: Color values - Red: 486, Green: 740, Blue: 188, Clear: 1628, Color: Red 286 | W (9747) main: Color values - Red: 488, Green: 732, Blue: 188, Clear: 1616, Color: Red 287 | W (9847) main: Color values - Red: 492, Green: 732, Blue: 188, Clear: 1620, Color: Red 288 | W (9947) main: Color values - Red: 488, Green: 732, Blue: 192, Clear: 1620, Color: Red 289 | W (10047) main: Color values - Red: 480, Green: 732, Blue: 192, Clear: 1608, Color: Red 290 | W (10147) main: Color values - Red: 488, Green: 728, Blue: 192, Clear: 1612, Color: Red 291 | W (10247) main: Color values - Red: 492, Green: 728, Blue: 188, Clear: 1616, Color: Red 292 | W (10347) main: Color values - Red: 488, Green: 724, Blue: 192, Clear: 1616, Color: Red 293 | W (10447) main: Color values - Red: 488, Green: 732, Blue: 188, Clear: 1616, Color: Red 294 | W (10547) main: Color values - Red: 484, Green: 728, Blue: 192, Clear: 1608, Color: Red 295 | W (10647) main: Color values - Red: 476, Green: 716, Blue: 188, Clear: 1600, Color: Red 296 | W (10747) main: Color values - Red: 432, Green: 636, Blue: 168, Clear: 1404, Color: Red 297 | W (10847) main: Color values - Red: 476, Green: 708, Blue: 180, Clear: 1540, Color: Red 298 | W (10947) main: Color values - Red: 484, Green: 720, Blue: 188, Clear: 1584, Color: Red 299 | W (11047) main: Color values - Red: 488, Green: 724, Blue: 180, Clear: 1596, Color: Red 300 | W (11147) main: Color values - Red: 456, Green: 688, Blue: 184, Clear: 1484, Color: Red 301 | W (11247) main: Color values - Red: 480, Green: 712, Blue: 184, Clear: 1572, Color: Red 302 | W (11347) main: Color values - Red: 480, Green: 724, Blue: 188, Clear: 1596, Color: Red 303 | W (11447) main: Color values - Red: 484, Green: 728, Blue: 196, Clear: 1608, Color: Red 304 | W (11547) main: Color values - Red: 484, Green: 720, Blue: 196, Clear: 1588, Color: Red 305 | W (11647) main: Color values - Red: 484, Green: 716, Blue: 180, Clear: 1564, Color: Red 306 | W (11747) main: Color values - Red: 484, Green: 736, Blue: 196, Clear: 1604, Color: Red 307 | W (11847) main: Color values - Red: 488, Green: 744, Blue: 196, Clear: 1616, Color: Red 308 | W (11947) main: Color values - Red: 484, Green: 720, Blue: 188, Clear: 1600, Color: Red 309 | W (12047) main: Color values - Red: 492, Green: 730, Blue: 192, Clear: 1620, Color: Red 310 | W (12147) main: Color values - Red: 496, Green: 728, Blue: 196, Clear: 1608, Color: Red 311 | W (12247) main: Color values - Red: 484, Green: 740, Blue: 200, Clear: 1612, Color: Red 312 | W (12347) main: Color values - Red: 484, Green: 736, Blue: 192, Clear: 1608, Color: Red 313 | W (12447) main: Color values - Red: 488, Green: 728, Blue: 192, Clear: 1612, Color: Red 314 | W (12547) main: Color values - Red: 480, Green: 728, Blue: 200, Clear: 1584, Color: Red 315 | W (12647) main: Color values - Red: 472, Green: 712, Blue: 188, Clear: 1548, Color: Red 316 | W (12747) main: Color values - Red: 476, Green: 716, Blue: 192, Clear: 1572, Color: Red 317 | W (12847) main: Color values - Red: 488, Green: 690, Blue: 160, Clear: 1592, Color: Red 318 | W (12947) main: Color values - Red: 500, Green: 748, Blue: 212, Clear: 1704, Color: Red 319 | W (13047) main: Color values - Red: 472, Green: 656, Blue: 156, Clear: 1536, Color: Red 320 | W (13147) main: Color values - Red: 464, Green: 696, Blue: 172, Clear: 1544, Color: Red 321 | W (13247) main: Color values - Red: 472, Green: 692, Blue: 184, Clear: 1552, Color: Red 322 | W (13347) main: Color values - Red: 464, Green: 692, Blue: 176, Clear: 1520, Color: Red 323 | W (13447) main: Color values - Red: 480, Green: 720, Blue: 184, Clear: 1564, Color: Red 324 | W (13547) main: Color values - Red: 484, Green: 736, Blue: 196, Clear: 1612, Color: Red 325 | W (13647) main: Color values - Red: 486, Green: 732, Blue: 192, Clear: 1616, Color: Red 326 | W (13747) main: Color values - Red: 488, Green: 736, Blue: 188, Clear: 1628, Color: Red 327 | W (13847) main: Color values - Red: 492, Green: 732, Blue: 196, Clear: 1632, Color: Red 328 | W (13947) main: Color values - Red: 492, Green: 736, Blue: 188, Clear: 1624, Color: Red 329 | W (14047) main: Color values - Red: 496, Green: 740, Blue: 192, Clear: 1620, Color: Red 330 | W (14147) main: Color values - Red: 492, Green: 740, Blue: 200, Clear: 1628, Color: Red 331 | W (14247) main: Color values - Red: 496, Green: 740, Blue: 196, Clear: 1620, Color: Red 332 | W (14347) main: Color values - Red: 488, Green: 740, Blue: 196, Clear: 1624, Color: Red 333 | W (14447) main: Color values - Red: 496, Green: 740, Blue: 180, Clear: 1628, Color: Red 334 | W (14547) main: Color values - Red: 492, Green: 744, Blue: 196, Clear: 1628, Color: Red 335 | W (14647) main: Color values - Red: 492, Green: 740, Blue: 192, Clear: 1628, Color: Red 336 | W (14747) main: Color values - Red: 496, Green: 740, Blue: 192, Clear: 1632, Color: Red 337 | W (14847) main: Color values - Red: 492, Green: 744, Blue: 192, Clear: 1624, Color: Red 338 | W (14947) main: Color values - Red: 496, Green: 736, Blue: 188, Clear: 1620, Color: Red 339 | W (15047) main: Color values - Red: 492, Green: 740, Blue: 188, Clear: 1620, Color: Red 340 | W (15147) main: Color values - Red: 492, Green: 740, Blue: 200, Clear: 1620, Color: Red 341 | W (15247) main: Color values - Red: 484, Green: 744, Blue: 196, Clear: 1616, Color: Red 342 | W (15347) main: Color values - Red: 496, Green: 744, Blue: 192, Clear: 1620, Color: Red 343 | W (15447) main: Color values - Red: 484, Green: 744, Blue: 200, Clear: 1628, Color: Red 344 | W (15547) main: Color values - Red: 496, Green: 736, Blue: 192, Clear: 1620, Color: Red 345 | W (15647) main: Color values - Red: 492, Green: 744, Blue: 192, Clear: 1624, Color: Red 346 | W (15747) main: Color values - Red: 488, Green: 732, Blue: 196, Clear: 1632, Color: Red 347 | W (15847) main: Color values - Red: 492, Green: 740, Blue: 188, Clear: 1628, Color: Red 348 | W (15947) main: Color values - Red: 500, Green: 732, Blue: 192, Clear: 1620, Color: Red 349 | W (16047) main: Color values - Red: 496, Green: 736, Blue: 192, Clear: 1632, Color: Red 350 | W (16147) main: Color values - Red: 492, Green: 740, Blue: 192, Clear: 1632, Color: Red 351 | W (16247) main: Color values - Red: 500, Green: 744, Blue: 188, Clear: 1624, Color: Red 352 | W (16347) main: Color values - Red: 488, Green: 744, Blue: 196, Clear: 1624, Color: Red 353 | W (16447) main: Color values - Red: 488, Green: 748, Blue: 196, Clear: 1620, Color: Red 354 | W (647) main: Color values - Red: 224, Green: 448, Blue: 132, Clear: 1780, Color: Black 355 | W (747) main: Color values - Red: 224, Green: 456, Blue: 132, Clear: 1780, Color: Black 356 | W (847) main: Color values - Red: 220, Green: 456, Blue: 124, Clear: 1772, Color: Black 357 | W (947) main: Color values - Red: 216, Green: 460, Blue: 120, Clear: 1788, Color: Black 358 | W (1047) main: Color values - Red: 212, Green: 456, Blue: 128, Clear: 1748, Color: Black 359 | W (1147) main: Color values - Red: 220, Green: 450, Blue: 120, Clear: 1784, Color: Black 360 | W (1247) main: Color values - Red: 216, Green: 456, Blue: 132, Clear: 1772, Color: Black 361 | W (1347) main: Color values - Red: 216, Green: 452, Blue: 132, Clear: 1776, Color: Black 362 | W (1447) main: Color values - Red: 224, Green: 456, Blue: 128, Clear: 1780, Color: Black 363 | W (1547) main: Color values - Red: 216, Green: 460, Blue: 128, Clear: 1772, Color: Black 364 | W (1647) main: Color values - Red: 224, Green: 456, Blue: 136, Clear: 1780, Color: Black 365 | W (1747) main: Color values - Red: 212, Green: 456, Blue: 128, Clear: 1776, Color: Black 366 | W (1847) main: Color values - Red: 224, Green: 456, Blue: 124, Clear: 1760, Color: Black 367 | W (1947) main: Color values - Red: 224, Green: 460, Blue: 124, Clear: 1764, Color: Black 368 | W (2047) main: Color values - Red: 216, Green: 452, Blue: 128, Clear: 1780, Color: Black 369 | W (2147) main: Color values - Red: 220, Green: 456, Blue: 132, Clear: 1772, Color: Black 370 | W (2247) main: Color values - Red: 216, Green: 456, Blue: 124, Clear: 1772, Color: Black 371 | W (2347) main: Color values - Red: 220, Green: 456, Blue: 124, Clear: 1764, Color: Black 372 | W (2447) main: Color values - Red: 224, Green: 464, Blue: 124, Clear: 1766, Color: Black 373 | W (2547) main: Color values - Red: 220, Green: 460, Blue: 132, Clear: 1776, Color: Black 374 | W (2647) main: Color values - Red: 216, Green: 456, Blue: 136, Clear: 1760, Color: Black 375 | W (2747) main: Color values - Red: 224, Green: 464, Blue: 128, Clear: 1780, Color: Black 376 | W (2847) main: Color values - Red: 224, Green: 464, Blue: 132, Clear: 1784, Color: Black 377 | W (2947) main: Color values - Red: 224, Green: 456, Blue: 128, Clear: 1752, Color: Black 378 | W (3047) main: Color values - Red: 220, Green: 460, Blue: 128, Clear: 1796, Color: Black 379 | W (3147) main: Color values - Red: 216, Green: 464, Blue: 136, Clear: 1776, Color: Black 380 | W (3247) main: Color values - Red: 220, Green: 472, Blue: 132, Clear: 1828, Color: Black 381 | W (3347) main: Color values - Red: 224, Green: 460, Blue: 128, Clear: 1784, Color: Black 382 | W (3447) main: Color values - Red: 220, Green: 464, Blue: 124, Clear: 1776, Color: Black 383 | W (3547) main: Color values - Red: 220, Green: 456, Blue: 130, Clear: 1762, Color: Black 384 | W (3647) main: Color values - Red: 216, Green: 456, Blue: 124, Clear: 1768, Color: Black 385 | W (3747) main: Color values - Red: 216, Green: 460, Blue: 120, Clear: 1752, Color: Black 386 | W (3847) main: Color values - Red: 214, Green: 456, Blue: 124, Clear: 1764, Color: Black 387 | W (3947) main: Color values - Red: 228, Green: 464, Blue: 132, Clear: 1796, Color: Black 388 | W (4047) main: Color values - Red: 220, Green: 468, Blue: 132, Clear: 1796, Color: Black 389 | W (4147) main: Color values - Red: 220, Green: 456, Blue: 128, Clear: 1760, Color: Black 390 | W (4247) main: Color values - Red: 224, Green: 452, Blue: 124, Clear: 1756, Color: Black 391 | W (4347) main: Color values - Red: 224, Green: 464, Blue: 128, Clear: 1780, Color: Black 392 | W (4447) main: Color values - Red: 216, Green: 460, Blue: 132, Clear: 1772, Color: Black 393 | W (4547) main: Color values - Red: 212, Green: 460, Blue: 124, Clear: 1756, Color: Black 394 | W (4647) main: Color values - Red: 224, Green: 460, Blue: 124, Clear: 1776, Color: Black 395 | W (4747) main: Color values - Red: 216, Green: 464, Blue: 136, Clear: 1792, Color: Black 396 | W (4847) main: Color values - Red: 228, Green: 464, Blue: 132, Clear: 1808, Color: Black 397 | W (4947) main: Color values - Red: 228, Green: 472, Blue: 140, Clear: 1828, Color: Black 398 | W (5047) main: Color values - Red: 228, Green: 476, Blue: 124, Clear: 1788, Color: Black 399 | W (5147) main: Color values - Red: 224, Green: 452, Blue: 128, Clear: 1772, Color: Black 400 | W (5247) main: Color values - Red: 224, Green: 456, Blue: 128, Clear: 1768, Color: Black 401 | W (5347) main: Color values - Red: 224, Green: 464, Blue: 136, Clear: 1794, Color: Black 402 | W (5447) main: Color values - Red: 216, Green: 460, Blue: 128, Clear: 1776, Color: Black 403 | W (5547) main: Color values - Red: 228, Green: 472, Blue: 132, Clear: 1792, Color: Black 404 | W (5647) main: Color values - Red: 216, Green: 464, Blue: 124, Clear: 1784, Color: Black 405 | W (5747) main: Color values - Red: 232, Green: 464, Blue: 132, Clear: 1784, Color: Black 406 | W (5847) main: Color values - Red: 214, Green: 460, Blue: 128, Clear: 1768, Color: Black 407 | W (5947) main: Color values - Red: 220, Green: 464, Blue: 128, Clear: 1804, Color: Black 408 | W (6047) main: Color values - Red: 224, Green: 460, Blue: 128, Clear: 1772, Color: Black 409 | W (6147) main: Color values - Red: 220, Green: 468, Blue: 128, Clear: 1788, Color: Black 410 | W (6247) main: Color values - Red: 220, Green: 460, Blue: 132, Clear: 1792, Color: Black 411 | W (6347) main: Color values - Red: 216, Green: 456, Blue: 124, Clear: 1742, Color: Black 412 | W (6447) main: Color values - Red: 216, Green: 456, Blue: 120, Clear: 1768, Color: Black 413 | W (6547) main: Color values - Red: 220, Green: 448, Blue: 124, Clear: 1776, Color: Black 414 | W (6647) main: Color values - Red: 212, Green: 448, Blue: 124, Clear: 1756, Color: Black 415 | W (6747) main: Color values - Red: 216, Green: 456, Blue: 124, Clear: 1748, Color: Black 416 | W (6847) main: Color values - Red: 220, Green: 476, Blue: 136, Clear: 1784, Color: Black 417 | W (6947) main: Color values - Red: 220, Green: 460, Blue: 128, Clear: 1792, Color: Black 418 | W (7047) main: Color values - Red: 228, Green: 460, Blue: 124, Clear: 1792, Color: Black 419 | W (7147) main: Color values - Red: 220, Green: 468, Blue: 132, Clear: 1804, Color: Black 420 | W (7247) main: Color values - Red: 224, Green: 464, Blue: 136, Clear: 1820, Color: Black 421 | W (7347) main: Color values - Red: 228, Green: 472, Blue: 132, Clear: 1804, Color: Black 422 | W (7447) main: Color values - Red: 228, Green: 472, Blue: 132, Clear: 1804, Color: Black 423 | W (7547) main: Color values - Red: 224, Green: 472, Blue: 132, Clear: 1796, Color: Black 424 | W (7647) main: Color values - Red: 220, Green: 472, Blue: 128, Clear: 1794, Color: Black 425 | W (7747) main: Color values - Red: 216, Green: 468, Blue: 132, Clear: 1804, Color: Black 426 | W (7847) main: Color values - Red: 228, Green: 468, Blue: 132, Clear: 1804, Color: Black 427 | W (7947) main: Color values - Red: 228, Green: 468, Blue: 132, Clear: 1808, Color: Black 428 | W (8047) main: Color values - Red: 220, Green: 468, Blue: 136, Clear: 1812, Color: Black 429 | W (8147) main: Color values - Red: 220, Green: 472, Blue: 132, Clear: 1800, Color: Black 430 | W (8247) main: Color values - Red: 224, Green: 472, Blue: 124, Clear: 1792, Color: Black 431 | W (8347) main: Color values - Red: 220, Green: 468, Blue: 124, Clear: 1804, Color: Black 432 | W (8447) main: Color values - Red: 220, Green: 464, Blue: 132, Clear: 1796, Color: Black 433 | W (8547) main: Color values - Red: 224, Green: 464, Blue: 128, Clear: 1796, Color: Black 434 | W (8647) main: Color values - Red: 224, Green: 468, Blue: 136, Clear: 1800, Color: Black 435 | W (8747) main: Color values - Red: 220, Green: 468, Blue: 132, Clear: 1808, Color: Black 436 | W (8847) main: Color values - Red: 216, Green: 464, Blue: 128, Clear: 1804, Color: Black 437 | W (8947) main: Color values - Red: 220, Green: 464, Blue: 132, Clear: 1800, Color: Black 438 | W (9047) main: Color values - Red: 224, Green: 468, Blue: 132, Clear: 1804, Color: Black 439 | W (9147) main: Color values - Red: 224, Green: 464, Blue: 128, Clear: 1768, Color: Black 440 | W (9247) main: Color values - Red: 228, Green: 464, Blue: 128, Clear: 1804, Color: Black 441 | W (9347) main: Color values - Red: 228, Green: 476, Blue: 132, Clear: 1812, Color: Black 442 | W (9447) main: Color values - Red: 224, Green: 476, Blue: 136, Clear: 1804, Color: Black 443 | W (9547) main: Color values - Red: 220, Green: 464, Blue: 136, Clear: 1800, Color: Black 444 | W (9647) main: Color values - Red: 228, Green: 468, Blue: 128, Clear: 1808, Color: Black 445 | W (9747) main: Color values - Red: 232, Green: 472, Blue: 124, Clear: 1804, Color: Black 446 | W (9847) main: Color values - Red: 220, Green: 472, Blue: 124, Clear: 1780, Color: Black 447 | W (647) main: Color values - Red: 1842, Green: 1100, Blue: 1782, Clear: 1016, Color: White 448 | W (747) main: Color values - Red: 1838, Green: 1088, Blue: 1762, Clear: 2008, Color: White 449 | W (847) main: Color values - Red: 1836, Green: 1088, Blue: 1758, Clear: 2012, Color: White 450 | W (947) main: Color values - Red: 1832, Green: 1088, Blue: 1772, Clear: 2016, Color: White 451 | W (1047) main: Color values - Red: 1828, Green: 1084, Blue: 1766, Clear: 2020, Color: White 452 | W (1147) main: Color values - Red: 1828, Green: 1088, Blue: 1766, Clear: 2020, Color: White 453 | W (1247) main: Color values - Red: 1832, Green: 1084, Blue: 1770, Clear: 2012, Color: White 454 | W (1347) main: Color values - Red: 1834, Green: 1088, Blue: 1736, Clear: 2016, Color: White 455 | W (1447) main: Color values - Red: 1834, Green: 1084, Blue: 1776, Clear: 2012, Color: White 456 | W (1547) main: Color values - Red: 1832, Green: 1084, Blue: 1762, Clear: 2020, Color: White 457 | W (1647) main: Color values - Red: 1836, Green: 1092, Blue: 1762, Clear: 2016, Color: White 458 | W (1747) main: Color values - Red: 1838, Green: 1084, Blue: 1768, Clear: 2020, Color: White 459 | W (1847) main: Color values - Red: 1830, Green: 1088, Blue: 1760, Clear: 2016, Color: White 460 | W (1947) main: Color values - Red: 1832, Green: 1088, Blue: 1760, Clear: 2016, Color: White 461 | W (2047) main: Color values - Red: 1832, Green: 1088, Blue: 1772, Clear: 2016, Color: White 462 | W (2147) main: Color values - Red: 1830, Green: 1080, Blue: 1736, Clear: 2016, Color: White 463 | W (2247) main: Color values - Red: 1828, Green: 1084, Blue: 1756, Clear: 2016, Color: White 464 | W (2347) main: Color values - Red: 1836, Green: 1088, Blue: 1764, Clear: 2016, Color: White 465 | W (2447) main: Color values - Red: 1830, Green: 1084, Blue: 1776, Clear: 2016, Color: White 466 | W (2547) main: Color values - Red: 1834, Green: 1084, Blue: 1758, Clear: 2016, Color: White 467 | W (2647) main: Color values - Red: 1826, Green: 1084, Blue: 1766, Clear: 2012, Color: White 468 | W (2747) main: Color values - Red: 1834, Green: 1088, Blue: 1756, Clear: 2016, Color: White 469 | W (2847) main: Color values - Red: 1834, Green: 1088, Blue: 1772, Clear: 2016, Color: White 470 | W (2947) main: Color values - Red: 1838, Green: 1084, Blue: 1746, Clear: 2016, Color: White 471 | W (3047) main: Color values - Red: 1824, Green: 1088, Blue: 1768, Clear: 2016, Color: White 472 | W (3147) main: Color values - Red: 1838, Green: 1084, Blue: 1762, Clear: 2020, Color: White 473 | W (3247) main: Color values - Red: 1824, Green: 1092, Blue: 1772, Clear: 2016, Color: White 474 | W (3347) main: Color values - Red: 1832, Green: 1088, Blue: 1728, Clear: 2020, Color: White 475 | W (3447) main: Color values - Red: 1834, Green: 1084, Blue: 1770, Clear: 2016, Color: White 476 | W (3547) main: Color values - Red: 1826, Green: 1084, Blue: 1760, Clear: 2012, Color: White 477 | W (3647) main: Color values - Red: 1830, Green: 1080, Blue: 1736, Clear: 2012, Color: White 478 | W (3747) main: Color values - Red: 1806, Green: 1084, Blue: 1754, Clear: 2000, Color: White 479 | W (3847) main: Color values - Red: 1826, Green: 1088, Blue: 1760, Clear: 2012, Color: White 480 | W (3947) main: Color values - Red: 1834, Green: 1088, Blue: 1758, Clear: 2016, Color: White 481 | W (4047) main: Color values - Red: 1826, Green: 1084, Blue: 1760, Clear: 2020, Color: White 482 | W (4147) main: Color values - Red: 1842, Green: 1092, Blue: 1760, Clear: 2016, Color: White 483 | W (4247) main: Color values - Red: 1826, Green: 1088, Blue: 1756, Clear: 2012, Color: White 484 | W (4347) main: Color values - Red: 1828, Green: 1088, Blue: 1770, Clear: 2016, Color: White 485 | W (4447) main: Color values - Red: 1842, Green: 1088, Blue: 1774, Clear: 2012, Color: White 486 | W (4547) main: Color values - Red: 1820, Green: 1088, Blue: 1764, Clear: 2016, Color: White 487 | W (4647) main: Color values - Red: 1834, Green: 1084, Blue: 1764, Clear: 2016, Color: White 488 | W (4747) main: Color values - Red: 1826, Green: 1088, Blue: 1770, Clear: 2016, Color: White 489 | W (4847) main: Color values - Red: 1830, Green: 1084, Blue: 1768, Clear: 2012, Color: White 490 | W (4947) main: Color values - Red: 1840, Green: 1088, Blue: 1756, Clear: 2020, Color: White 491 | W (5047) main: Color values - Red: 1830, Green: 1088, Blue: 1760, Clear: 2012, Color: White 492 | W (5147) main: Color values - Red: 1828, Green: 1092, Blue: 1760, Clear: 2016, Color: White 493 | W (5247) main: Color values - Red: 1844, Green: 1084, Blue: 1770, Clear: 2016, Color: White 494 | W (5347) main: Color values - Red: 1830, Green: 1084, Blue: 1770, Clear: 2016, Color: White 495 | W (5447) main: Color values - Red: 1830, Green: 1076, Blue: 1734, Clear: 2016, Color: White 496 | W (5547) main: Color values - Red: 1828, Green: 1084, Blue: 1762, Clear: 2016, Color: White 497 | W (5647) main: Color values - Red: 1834, Green: 1088, Blue: 1756, Clear: 2016, Color: White 498 | W (5747) main: Color values - Red: 1714, Green: 2044, Blue: 1650, Clear: 1856, Color: White 499 | W (5847) main: Color values - Red: 1826, Green: 2196, Blue: 1766, Clear: 2016, Color: White 500 | W (5947) main: Color values - Red: 1864, Green: 2224, Blue: 1788, Clear: 2044, Color: White 501 | W (6047) main: Color values - Red: 1868, Green: 2252, Blue: 1806, Clear: 2064, Color: White 502 | W (6147) main: Color values - Red: 1906, Green: 2288, Blue: 1838, Clear: 2100, Color: White 503 | W (6247) main: Color values - Red: 1844, Green: 2208, Blue: 1778, Clear: 2032, Color: White 504 | W (6347) main: Color values - Red: 1852, Green: 2220, Blue: 1782, Clear: 2040, Color: White 505 | W (6447) main: Color values - Red: 1932, Green: 2324, Blue: 1846, Clear: 2132, Color: White 506 | W (6547) main: Color values - Red: 1986, Green: 2384, Blue: 1896, Clear: 2192, Color: White 507 | W (6647) main: Color values - Red: 2076, Green: 2476, Blue: 1932, Clear: 2276, Color: White 508 | W (6747) main: Color values - Red: 2070, Green: 2468, Blue: 1964, Clear: 2272, Color: White 509 | W (6847) main: Color values - Red: 2046, Green: 2432, Blue: 1938, Clear: 2244, Color: White 510 | W (6947) main: Color values - Red: 2006, Green: 2412, Blue: 1884, Clear: 2212, Color: White 511 | W (7047) main: Color values - Red: 1978, Green: 2350, Blue: 1860, Clear: 2172, Color: White 512 | W (7147) main: Color values - Red: 1948, Green: 2332, Blue: 1852, Clear: 2144, Color: White 513 | W (7247) main: Color values - Red: 1914, Green: 2296, Blue: 1830, Clear: 2112, Color: White 514 | W (7347) main: Color values - Red: 1966, Green: 2356, Blue: 1858, Clear: 2156, Color: White 515 | W (7447) main: Color values - Red: 2094, Green: 2492, Blue: 1936, Clear: 2280, Color: White 516 | W (7547) main: Color values - Red: 2246, Green: 2668, Blue: 2070, Clear: 2456, Color: White 517 | W (7647) main: Color values - Red: 2270, Green: 2700, Blue: 2102, Clear: 2496, Color: White 518 | W (7747) main: Color values - Red: 2302, Green: 2728, Blue: 2128, Clear: 2524, Color: White 519 | W (7847) main: Color values - Red: 2288, Green: 2712, Blue: 2118, Clear: 2520, Color: White 520 | W (7947) main: Color values - Red: 2288, Green: 2710, Blue: 2120, Clear: 2516, Color: White 521 | W (8047) main: Color values - Red: 2300, Green: 2712, Blue: 2102, Clear: 2516, Color: White 522 | W (8147) main: Color values - Red: 2250, Green: 2648, Blue: 2060, Clear: 2444, Color: White 523 | W (8247) main: Color values - Red: 2084, Green: 2468, Blue: 1932, Clear: 2260, Color: White 524 | W (8347) main: Color values - Red: 1918, Green: 2292, Blue: 1822, Clear: 2092, Color: White 525 | W (8447) main: Color values - Red: 1836, Green: 2188, Blue: 1754, Clear: 2008, Color: White 526 | W (8547) main: Color values - Red: 1826, Green: 2192, Blue: 1766, Clear: 2012, Color: White 527 | W (8647) main: Color values - Red: 1972, Green: 2404, Blue: 1894, Clear: 2204, Color: White 528 | W (8747) main: Color values - Red: 2136, Green: 2548, Blue: 1992, Clear: 2356, Color: White 529 | W (8847) main: Color values - Red: 2114, Green: 2548, Blue: 1970, Clear: 2352, Color: White 530 | W (8947) main: Color values - Red: 2170, Green: 2596, Blue: 2036, Clear: 2388, Color: White 531 | W (9047) main: Color values - Red: 2210, Green: 2620, Blue: 2072, Clear: 2420, Color: White 532 | W (9147) main: Color values - Red: 2154, Green: 2572, Blue: 2032, Clear: 2384, Color: White 533 | W (9247) main: Color values - Red: 2186, Green: 2604, Blue: 2062, Clear: 2412, Color: White 534 | W (9347) main: Color values - Red: 2252, Green: 2664, Blue: 2086, Clear: 2480, Color: White 535 | W (9447) main: Color values - Red: 2138, Green: 2556, Blue: 2016, Clear: 2356, Color: White 536 | W (9547) main: Color values - Red: 1910, Green: 2272, Blue: 1822, Clear: 2096, Color: White 537 | W (9647) main: Color values - Red: 1886, Green: 2252, Blue: 1802, Clear: 2068, Color: White 538 | W (9747) main: Color values - Red: 1924, Green: 2304, Blue: 1826, Clear: 2104, Color: White 539 | W (9847) main: Color values - Red: 2038, Green: 2440, Blue: 1912, Clear: 2232, Color: White 540 | W (9947) main: Color values - Red: 2076, Green: 2468, Blue: 1900, Clear: 2264, Color: White 541 | W (10047) main: Color values - Red: 2058, Green: 2452, Blue: 1892, Clear: 2244, Color: White 542 | W (10147) main: Color values - Red: 1998, Green: 2376, Blue: 1878, Clear: 2176, Color: White 543 | W (10247) main: Color values - Red: 1914, Green: 2296, Blue: 1828, Clear: 2100, Color: White 544 | W (10347) main: Color values - Red: 1818, Green: 2196, Blue: 1758, Clear: 2008, Color: White 545 | W (10447) main: Color values - Red: 1838, Green: 2224, Blue: 1774, Clear: 2036, Color: White 546 | W (10547) main: Color values - Red: 1926, Green: 2316, Blue: 1830, Clear: 2120, Color: White 547 | W (10647) main: Color values - Red: 2148, Green: 2568, Blue: 2000, Clear: 2352, Color: White 548 | W (10747) main: Color values - Red: 2202, Green: 2620, Blue: 2042, Clear: 2416, Color: White 549 | W (10847) main: Color values - Red: 2044, Green: 2456, Blue: 1912, Clear: 2236, Color: White 550 | W (10947) main: Color values - Red: 1732, Green: 2092, Blue: 1694, Clear: 1920, Color: White 551 | W (11047) main: Color values - Red: 1790, Green: 2148, Blue: 1726, Clear: 1972, Color: White 552 | W (11147) main: Color values - Red: 1894, Green: 2256, Blue: 1816, Clear: 2080, Color: White 553 | W (11247) main: Color values - Red: 1924, Green: 2304, Blue: 1812, Clear: 2112, Color: White 554 | W (11347) main: Color values - Red: 1904, Green: 2296, Blue: 1830, Clear: 2108, Color: White 555 | W (11447) main: Color values - Red: 1918, Green: 2300, Blue: 1832, Clear: 2100, Color: White 556 | W (11547) main: Color values - Red: 1886, Green: 2252, Blue: 1808, Clear: 2072, Color: White 557 | W (11647) main: Color values - Red: 1866, Green: 2240, Blue: 1802, Clear: 2064, Color: White 558 | W (11747) main: Color values - Red: 1856, Green: 2220, Blue: 1792, Clear: 2040, Color: White 559 | W (11847) main: Color values - Red: 1766, Green: 2136, Blue: 1732, Clear: 1964, Color: White 560 | W (11947) main: Color values - Red: 1770, Green: 2120, Blue: 1718, Clear: 1944, Color: White 561 | W (12047) main: Color values - Red: 1908, Green: 2288, Blue: 1822, Clear: 2100, Color: White 562 | W (12147) main: Color values - Red: 2042, Green: 2434, Blue: 1924, Clear: 2240, Color: White 563 | W (12247) main: Color values - Red: 2022, Green: 2444, Blue: 1922, Clear: 2244, Color: White 564 | W (12347) main: Color values - Red: 2170, Green: 2584, Blue: 2036, Clear: 2388, Color: White 565 | W (12447) main: Color values - Red: 2244, Green: 2660, Blue: 2072, Clear: 2460, Color: White 566 | W (12547) main: Color values - Red: 2300, Green: 2712, Blue: 2112, Clear: 2516, Color: White 567 | W (12647) main: Color values - Red: 2372, Green: 2792, Blue: 2156, Clear: 2592, Color: White 568 | W (12747) main: Color values - Red: 2388, Green: 2824, Blue: 2176, Clear: 2620, Color: White 569 | W (12847) main: Color values - Red: 2362, Green: 2792, Blue: 2170, Clear: 2588, Color: White 570 | W (12947) main: Color values - Red: 2220, Green: 2632, Blue: 2070, Clear: 2432, Color: White 571 | W (13047) main: Color values - Red: 2156, Green: 2576, Blue: 2012, Clear: 2356, Color: White 572 | W (13147) main: Color values - Red: 2028, Green: 2412, Blue: 1892, Clear: 2212, Color: White 573 | W (13247) main: Color values - Red: 1896, Green: 2280, Blue: 1792, Clear: 2080, Color: White 574 | W (13347) main: Color values - Red: 1876, Green: 2264, Blue: 1810, Clear: 2068, Color: White 575 | W (13447) main: Color values - Red: 2054, Green: 2448, Blue: 1940, Clear: 2264, Color: White 576 | W (13547) main: Color values - Red: 2080, Green: 2478, Blue: 1972, Clear: 2280, Color: White 577 | W (13647) main: Color values - Red: 2086, Green: 2488, Blue: 1976, Clear: 2292, Color: White 578 | W (13747) main: Color values - Red: 2054, Green: 2460, Blue: 1948, Clear: 2268, Color: White 579 | W (13847) main: Color values - Red: 1862, Green: 2232, Blue: 1790, Clear: 2052, Color: White 580 | W (13947) main: Color values - Red: 1874, Green: 2256, Blue: 1828, Clear: 2076, Color: White 581 | W (14047) main: Color values - Red: 1938, Green: 2344, Blue: 1874, Clear: 2152, Color: White 582 | W (14147) main: Color values - Red: 2018, Green: 2416, Blue: 1912, Clear: 2220, Color: White 583 | W (14247) main: Color values - Red: 1872, Green: 2240, Blue: 1816, Clear: 2076, Color: White 584 | W (14347) main: Color values - Red: 1722, Green: 2056, Blue: 1678, Clear: 1896, Color: White 585 | W (14447) main: Color values - Red: 1708, Green: 2044, Blue: 1668, Clear: 1880, Color: White 586 | W (14547) main: Color values - Red: 1724, Green: 2060, Blue: 1656, Clear: 1892, Color: White 587 | W (14647) main: Color values - Red: 1712, Green: 2068, Blue: 1664, Clear: 1892, Color: White 588 | W (14747) main: Color values - Red: 1732, Green: 2084, Blue: 1672, Clear: 1916, Color: White 589 | W (14847) main: Color values - Red: 1898, Green: 2270, Blue: 1834, Clear: 2100, Color: White 590 | W (14947) main: Color values - Red: 2048, Green: 2444, Blue: 1936, Clear: 2252, Color: White 591 | W (15047) main: Color values - Red: 2014, Green: 2416, Blue: 1908, Clear: 2216, Color: White 592 | W (15147) main: Color values - Red: 1890, Green: 2256, Blue: 1808, Clear: 2080, Color: White 593 | W (15247) main: Color values - Red: 1888, Green: 2264, Blue: 1818, Clear: 2084, Color: White 594 | W (15347) main: Color values - Red: 1878, Green: 2248, Blue: 1786, Clear: 2068, Color: White 595 | W (15447) main: Color values - Red: 1870, Green: 2236, Blue: 1788, Clear: 2060, Color: White 596 | W (15547) main: Color values - Red: 1852, Green: 2236, Blue: 1798, Clear: 2048, Color: White 597 | W (15647) main: Color values - Red: 1856, Green: 2228, Blue: 1798, Clear: 2044, Color: White 598 | W (15747) main: Color values - Red: 1854, Green: 2220, Blue: 1776, Clear: 2040, Color: White 599 | W (15847) main: Color values - Red: 1852, Green: 2208, Blue: 1784, Clear: 2040, Color: White 600 | W (15947) main: Color values - Red: 1838, Green: 2208, Blue: 1748, Clear: 2036, Color: White 601 | W (16047) main: Color values - Red: 1838, Green: 2216, Blue: 1778, Clear: 2032, Color: White 602 | W (16147) main: Color values - Red: 1844, Green: 2192, Blue: 1774, Clear: 2028, Color: White 603 | W (16247) main: Color values - Red: 1844, Green: 2204, Blue: 1762, Clear: 2024, Color: White 604 | W (16347) main: Color values - Red: 1832, Green: 2204, Blue: 1764, Clear: 2028, Color: White 605 | W (16447) main: Color values - Red: 1840, Green: 2204, Blue: 1766, Clear: 2024, Color: White 606 | W (16547) main: Color values - Red: 1838, Green: 2204, Blue: 1764, Clear: 2020, Color: White 607 | W (16647) main: Color values - Red: 1826, Green: 2186, Blue: 1736, Clear: 2020, Color: White 608 | W (16747) main: Color values - Red: 1830, Green: 2204, Blue: 1762, Clear: 2016, Color: White 609 | W (16847) main: Color values - Red: 1834, Green: 2200, Blue: 1762, Clear: 2016, Color: White 610 | W (16947) main: Color values - Red: 1838, Green: 2192, Blue: 1778, Clear: 2016, Color: White 611 | W (17047) main: Color values - Red: 1822, Green: 2192, Blue: 1762, Clear: 2012, Color: White 612 | W (17147) main: Color values - Red: 1822, Green: 2192, Blue: 1752, Clear: 2012, Color: White 613 | W (17247) main: Color values - Red: 1816, Green: 2188, Blue: 1756, Clear: 2008, Color: White 614 | W (17347) main: Color values - Red: 1824, Green: 2180, Blue: 1758, Clear: 2008, Color: White 615 | W (17447) main: Color values - Red: 1822, Green: 2184, Blue: 1762, Clear: 2008, Color: White -------------------------------------------------------------------------------- /scripts/controller.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import os 3 | import json 4 | from bleak import BleakClient, BleakScanner 5 | import keyboard 6 | 7 | CONFIG_FILE = "ble_device_config.json" 8 | CHARACTERISTIC_UUID = "23408888-1f40-4cd8-9b89-ca8d45f8a5b0" # Replace with your characteristic UUID 9 | 10 | 11 | async def send_command(client, speedA, speedB, directionA, directionB, duration): 12 | command = bytearray([speedA, speedB, directionA, directionB, duration]) 13 | await client.write_gatt_char(CHARACTERISTIC_UUID, command) 14 | 15 | async def select_device(): 16 | devices = await BleakScanner.discover() 17 | if not devices: 18 | print("No BLE devices found.") 19 | return None 20 | 21 | print("Available BLE devices:") 22 | for i, device in enumerate(devices): 23 | print(f"{i}: {device.name} - {device.address}") 24 | 25 | while True: 26 | try: 27 | selection = int(input("Select device by number: ")) 28 | if 0 <= selection < len(devices): 29 | return devices[selection].address 30 | else: 31 | print("Invalid selection. Please try again.") 32 | except ValueError: 33 | print("Please enter a valid number.") 34 | 35 | def load_config(): 36 | if os.path.exists(CONFIG_FILE): 37 | with open(CONFIG_FILE, "r") as f: 38 | return json.load(f) 39 | return {} 40 | 41 | def save_config(config): 42 | with open(CONFIG_FILE, "w") as f: 43 | json.dump(config, f) 44 | 45 | async def get_ble_address(): 46 | config = load_config() 47 | saved_address = config.get("ble_address") 48 | 49 | if saved_address: 50 | print(f"Previously connected to device with address: {saved_address}") 51 | choice = input("Do you want to connect to the same device? (y/n): ").lower() 52 | if choice == 'y': 53 | return saved_address 54 | 55 | new_address = await select_device() 56 | if new_address: 57 | config["ble_address"] = new_address 58 | save_config(config) 59 | return new_address 60 | 61 | async def handle_key_press(client): 62 | 63 | # motor A speed, motor A dir, motor B speed, motor B dir, time 50 == 5.0 seconds 64 | if keyboard.is_pressed('w'): 65 | await send_command(client, 60,1,60,1,5) 66 | print(f"Forward") 67 | elif keyboard.is_pressed('s'): 68 | await send_command(client, 50,0,50,0,5) 69 | print(f"Backwards") 70 | elif keyboard.is_pressed('d'): 71 | await send_command(client, 40,1,40,0,3) 72 | print(f"Right") 73 | elif keyboard.is_pressed('a'): 74 | await send_command(client, 40,0,40,1,3) 75 | print(f"Left") 76 | 77 | 78 | 79 | async def main(): 80 | global motor_speed 81 | 82 | ble_address = await get_ble_address() 83 | if not ble_address: 84 | print("No device selected. Exiting.") 85 | return 86 | 87 | print(f"Connecting to device: {ble_address}") 88 | async with BleakClient(ble_address) as client: 89 | print(f"Connected: {await client.is_connected()}") 90 | print("Press 'w' to speed up, 's' to slow down, or 'q' to quit.") 91 | 92 | while True: 93 | await handle_key_press(client) 94 | if keyboard.is_pressed('q'): 95 | print("Quitting...") 96 | break 97 | 98 | if __name__ == "__main__": 99 | asyncio.run(main()) 100 | -------------------------------------------------------------------------------- /scripts/nn_model.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuckAtPrototype/Racer/f43d1263155c1e04efdd052b35089c74f59d61d8/scripts/nn_model.bin -------------------------------------------------------------------------------- /scripts/trainer.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import re 3 | import struct 4 | from sklearn.utils import shuffle 5 | from sklearn.model_selection import train_test_split 6 | 7 | class NeuralNetwork: 8 | def __init__(self, input_size, hidden_size1, hidden_size2, output_size): 9 | self.input_size = input_size 10 | self.hidden_size1 = hidden_size1 11 | self.hidden_size2 = hidden_size2 12 | self.output_size = output_size 13 | 14 | self.input_weights = np.random.randn(input_size, hidden_size1).astype(np.float32) * np.sqrt(2.0/input_size) 15 | self.hidden_weights1 = np.random.randn(hidden_size1, hidden_size2).astype(np.float32) * np.sqrt(2.0/hidden_size1) 16 | self.hidden_weights2 = np.random.randn(hidden_size2, output_size).astype(np.float32) * np.sqrt(2.0/hidden_size2) 17 | self.hidden_bias1 = np.zeros((1, hidden_size1), dtype=np.float32) 18 | self.hidden_bias2 = np.zeros((1, hidden_size2), dtype=np.float32) 19 | self.output_bias = np.zeros((1, output_size), dtype=np.float32) 20 | 21 | def relu(self, x): 22 | return np.maximum(0, x) 23 | 24 | def relu_derivative(self, x): 25 | return np.where(x > 0, 1, 0) 26 | 27 | def softmax(self, x): 28 | exp_x = np.exp(x - np.max(x, axis=1, keepdims=True)) 29 | return exp_x / np.sum(exp_x, axis=1, keepdims=True) 30 | 31 | def forward(self, X): 32 | self.hidden1 = self.relu(np.dot(X, self.input_weights) + self.hidden_bias1) 33 | self.hidden2 = self.relu(np.dot(self.hidden1, self.hidden_weights1) + self.hidden_bias2) 34 | self.output = self.softmax(np.dot(self.hidden2, self.hidden_weights2) + self.output_bias) 35 | return self.output 36 | 37 | def backward(self, X, y, output, learning_rate): 38 | output_error = y - output 39 | hidden2_error = np.dot(output_error, self.hidden_weights2.T) 40 | hidden1_error = np.dot(hidden2_error, self.hidden_weights1.T) 41 | 42 | hidden2_delta = hidden2_error * self.relu_derivative(self.hidden2) 43 | hidden1_delta = hidden1_error * self.relu_derivative(self.hidden1) 44 | 45 | self.hidden_weights2 += learning_rate * np.dot(self.hidden2.T, output_error) 46 | self.hidden_weights1 += learning_rate * np.dot(self.hidden1.T, hidden2_delta) 47 | self.input_weights += learning_rate * np.dot(X.T, hidden1_delta) 48 | 49 | self.output_bias += learning_rate * np.sum(output_error, axis=0, keepdims=True) 50 | self.hidden_bias2 += learning_rate * np.sum(hidden2_delta, axis=0, keepdims=True) 51 | self.hidden_bias1 += learning_rate * np.sum(hidden1_delta, axis=0, keepdims=True) 52 | 53 | def train(self, X, y, X_val, y_val, epochs, learning_rate, batch_size=32, patience=50): 54 | best_val_loss = float('inf') 55 | patience_counter = 0 56 | for epoch in range(epochs): 57 | epoch_loss = 0 58 | for i in range(0, len(X), batch_size): 59 | batch_X = X[i:i+batch_size] 60 | batch_y = y[i:i+batch_size] 61 | output = self.forward(batch_X) 62 | self.backward(batch_X, batch_y, output, learning_rate) 63 | epoch_loss += np.mean(np.square(batch_y - output)) 64 | 65 | val_output = self.forward(X_val) 66 | val_loss = np.mean(np.square(y_val - val_output)) 67 | 68 | if epoch % 100 == 0: 69 | print(f"Epoch {epoch}, Loss: {epoch_loss/len(X)}, Val Loss: {val_loss}") 70 | 71 | if val_loss < best_val_loss: 72 | best_val_loss = val_loss 73 | patience_counter = 0 74 | else: 75 | patience_counter += 1 76 | if patience_counter >= patience: 77 | print(f"Early stopping at epoch {epoch}") 78 | break 79 | 80 | def parse_input(file_path): 81 | data = [] 82 | labels = [] 83 | with open(file_path, 'r') as file: 84 | for line in file: 85 | match = re.search(r'Red: (\d+), Green: (\d+), Blue: (\d+), Clear: (\d+), Color: (\w+)', line) 86 | if match: 87 | r, g, b, c, color = match.groups() 88 | data.append([int(r), int(g), int(b), int(c)]) 89 | labels.append(color) 90 | return np.array(data), np.array(labels) 91 | 92 | def normalize_data(data): 93 | return data / np.array([2048, 2048, 2048, 2048]) 94 | 95 | def one_hot_encode(labels): 96 | unique_labels = np.array(['Red', 'Black', 'Green', 'White']) 97 | label_dict = {label: i for i, label in enumerate(unique_labels)} 98 | encoded = np.zeros((len(labels), len(unique_labels))) 99 | for i, label in enumerate(labels): 100 | encoded[i, label_dict[label]] = 1 101 | return encoded, unique_labels 102 | 103 | def float_to_hex(f): 104 | return hex(struct.unpack('