├── .gitignore ├── .vscode └── extensions.json ├── README.md ├── examples ├── ESP32 │ └── default │ │ └── default.ino └── Pro-Micro │ ├── default-ctrl │ └── default-ctrl.ino │ ├── default-noshift │ └── default-noshift.ino │ └── default │ └── default.ino ├── images ├── addzip.png ├── board.png ├── front-annotated2.jpg ├── key_settings.jpg ├── keypad.png ├── libman.png ├── libman2.png ├── main_menu.jpg ├── nums.png ├── port.png └── sparkpad.jpg ├── library.properties ├── platformio.ini └── src ├── sparkpad_eeprom.h ├── sparkpad_keypad.h ├── sparkpad_knob.h ├── sparkpad_leds.h ├── sparkpad_obs.h ├── sparkpad_oled.h ├── sparkpad_wifi.h └── test /.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .vscode/.browse.c_cpp.db* 3 | .vscode/c_cpp_properties.json 4 | .vscode/launch.json 5 | .vscode/ipch 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ], 7 | "unwantedRecommendations": [ 8 | "ms-vscode.cpptools-extension-pack" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | The Sparkpad is a reconfigurable control surface, primarily aimed at Streamers. The V1 Sparkpad is designed to communicate with streaming software - such as OBS - via HID commands sent over USB. However, due to it's modular hardware design and open source Arduino firmware, there is scope to do much more. 4 | 5 | We are hoping to foster a development community, and we will continue to develop improvements for the Sparkpad as and when we can. 6 | 7 | ![](/images/sparkpad.jpg) 8 | 9 | 10 | 11 | # Getting started 12 | 13 | To get your Sparkpad up and running, use a USB Micro B cable to connect it to one of your PC/laptop's USB ports. Once Sparkpad is plugged into your computer, it will be recognised automatically as a HID device, so there is no need to install additional drivers. 14 | 15 | The Sparkpad's 12 keys are mapped to F13 - F24 (the bottom left key is mapped to shift) by default. In Windows 10, these function keys do not serve any function (typically), so you are free to use them as hotkeys within your chosen software. 16 | 17 | ![](/images/front-annotated2.jpg) 18 | 19 | 20 | 21 | ## Lighting controls 22 | 23 | To adjust the lighting of your Sparkpad, you can use the OLED and 3 menu buttons located in the bottom left. The middle menu button is the "select" button, and the left and right buttons let you cycle through the available options. The OLED menu is hidden by default to prevent "burn in"; to show it simply press the "select" button. The menu consists of two levels, and these are described below. 24 | 25 | 26 | 27 | ### Main menu 28 | 29 | ![](/images/main_menu.jpg) 30 | 31 | **Colour** : The global lighting colour. Unfortunately due to memory limitations the menu displays numbers instead of actual colours. Listed below are the colour assignments: 32 | 33 | 34 | 35 | 0 : Off 36 | 37 | 1 : Red 38 | 39 | 2 : Blue 40 | 41 | 3 : Pink 42 | 43 | 4 : Green 44 | 45 | 5 : Yellow 46 | 47 | 6 : Cyan 48 | 49 | 7 : White 50 | 51 | 52 | 53 | **Brightness** : The global lighting brightness, with higher numbers being brighter. 54 | 55 | 56 | 57 | **Selected key** : Use this option to tailor lighting for individual keys. For example, to adjust lighting for the top right key, choose "2" for this option and proceed to the "Key settings" menu below. Here is how the numbers and keys are related: 58 | 59 | ![](/images/nums.png) 60 | 61 | 62 | 63 | **Key settings** : Selecting this option takes you to the second menu level, which lists lighting options for the selected key. 64 | 65 | 66 | 67 | **Hide** : Hide the menu. We recommend hiding the menu once you have configured your Sparkpad to prevent "burn in". 68 | 69 | 70 | 71 | ### Key settings 72 | 73 | ![](/images/key_settings.jpg) 74 | 75 | **Active colour** : The key lighting colour in the active state. This setting will only take effect if the key lighting mode is "unique". 76 | 77 | 78 | 79 | **Inactive colour** : The key lighting colour in the inactive state. This setting will only take effect if the key lighting mode is "unique". 80 | 81 | 82 | 83 | **Lighting mode** : The key lighting mode. In "global" mode, the key colour will be the same as the global colour selected in the main menu. In "unique" mode, you can set a unique colour for this key. The assignments are as follows: 84 | 85 | 86 | 87 | 0 : Global 88 | 89 | 1 : Unique 90 | 91 | 92 | 93 | **Switch mode** : The key switch mode. In "static" mode, the key will remain in a fixed state - active or inactive - and pressing the key will not affect this state. In "toggle" mode, pressing the key will toggle it between the active and inactive states. In "delay" mode, whenever the key is pressed it will go into the active state for a predetermined time set by the delay option, before returning to the inactive state. All keys which have their switch mode set to "group toggle" will be linked together such that only one of them can be in the active state at any time. Pressing a key in the group will cause it become active, while the other grouped keys will become inactive. The assignments are as follows: 94 | 95 | 96 | 97 | 0 : Static 98 | 99 | 1 : Toggle 100 | 101 | 2 : Delay 102 | 103 | 3 : Group toggle 104 | 105 | 106 | 107 | **Default state** : The state the key will be in when the Sparkpad starts up, or when it is reset using the reset button. The assignments are as follows: 108 | 109 | 110 | 111 | 0 : Inactive 112 | 113 | 1 : Active 114 | 115 | 116 | 117 | **Delay** : The key delay setting. This option is only relevant if this key is in "delay" mode. You can choose any delay between 1 and 255 seconds. 118 | 119 | 120 | 121 | ## Reset button 122 | 123 | Just below the 12th key on your Sparkpad - in the bottom right hand corner - you will find another button which can be used to reset the Sparkpad. In normal operation you shouldn't have to use this button at all, but if you want to return keys to their default state without unplugging the Sparkpad this can be useful. 124 | 125 | 126 | 127 | ## Volume control knob 128 | 129 | The knob and LED bar above the Sparkpad's keys can be used to control the master volume of your PC. As with all Sparkpad functions, the knob and LED bar can be repurposed if you don't mind doing some coding! Keep reading this guide to learn how. 130 | 131 | 132 | 133 | ## Cleaning your Sparkpad 134 | 135 | To clean the surfaces of your Sparkpad case, you can use soapy water and a non abrasive scrubber such as a sponge or toothbrush. You can also use Isopropyl Alcohol (IPA), and this tends to a better job cleaning the engraved parts. If using soapy water, you should first remove the PCB from the case by undoing the 4 corner screws. With Isopropyl Alcohol you can leave the PCB in the case. You may find that white marks are left behind after cleaning; to remove these, rub the surfaces with clean white paper. 136 | 137 | **Do not use Acetone or other strong solvents, as these will dissolve the black paint on the top panel.** 138 | 139 | 140 | 141 | # Firmware 142 | 143 | To develop custom firmware for your Sparkpad (or to update to newer firmware), there are 3 options available to you. 144 | 145 | 146 | 147 | ## The easy way - Arduino IDE 148 | 149 | ### Installation 150 | 151 | You will first need to download and install the [Arduino IDE](https://www.arduino.cc/en/software), and import the Sparkpad library using the Arduino Library Manager: 152 | 153 | ![](/images/libman.png) 154 | 155 | ![](/images/libman2.png) 156 | 157 | You may get prompted to install some additional libraries, ensure you do else the code will not work. 158 | 159 | 160 | 161 | ### Uploading 162 | 163 | When plugged in via USB, your Sparkpad will be visible to the Arduino IDE as a serial device on a particular port, and the port name will depend on which OS you are using (COM for Windows, dev/tty for Mac and Linux). The board type is Arduino Leonardo: 164 | 165 | ![](/images/port.png) 166 | 167 | ![](/images/board.png) 168 | 169 | To upload the latest firmware version to your Sparkpad, simply choose Sparkpad -> Pro-Micro -> default from the examples menu and hit upload. If you are looking to customise the firmware, then keep reading! 170 | 171 | 172 | 173 | ### Customisation 174 | 175 | The Arduino firmware is split into two components - the .ino file, and .h (or header) files. 176 | 177 | #### .ino file 178 | 179 | This file - or 'sketch' in Arduino parlance - contains the core code which runs on the Sparkpad. You can break down this file into 3 parts. At the top are **definitions** and **includes**: definitions specify global parameters (such as mappings for the Sparkpad's 12 keys), and includes pull in resources from external files (such as the code for the OLED). Below these is the **setup** function which runs once when the Sparkpad boots up and initialises all the components. Finally the **loop** function is the code which runs continuously on the Sparkpad, and this is where all the good stuff happens! The .ino file also contains a few utility functions, which are just there for convenience and to avoid duplicate code. 180 | 181 | If you are just looking to adjust key mappings or other global parameters on your Sparkpad, then you only have to make changes to the necessary definitions in the .ino file. 182 | 183 | #### .h files 184 | 185 | These files contain code specific to a particular Sparkpad component. These files have their own **definitions** and **includes**, and all the functions which define their behaviour. If you are looking to add bespoke functionality to your Sparkpad, then you will probably have to tweak one or more of these files. 186 | 187 | 188 | 189 | ## The lightweight way - avrdude 190 | 191 | If you don't want to look at the code and just want to upload firmware, you can use [this tool] (https://github.com/vanbwodonk/leonardoUploader). Once installed, you can download the .hex file you want from the "releases" section of this repo, and upload it straight to your Sparkpad using the command line. 192 | 193 | 194 | 195 | ## The pro way - platformio 196 | 197 | If you intend to make serious modifications to your Sparkpad firmware, then we recommend downloading VScode and installing the platformio plugin. This repo contains a platformio.ini file which allows you to open it inside VScode as a platformio project. Coding inside platformio lets you to take advantage of tools such as code completion and syntax checking, and gives you easy access to the .h files which are more hidden inside the Arduino IDE. 198 | 199 | The only snag with using platformio is it does not recognise .ino files as source files, so you will have to change the .ino suffix to .cpp on the files you are working on. If you intend to push changes to the main Sparkpad branch, please remember to change these back to .ino before you push to maintain backwards compatibility with the Arduino IDE. 200 | 201 | To further ensure backwards compatibility, when writing new and experimental code please create a new example within the examples folder, and add the example as an environment inside the platformio.ini file. **Don't add your code to one of the default examples**. If you feel the code you are writing deserves to be part of the default firmware, then please raise an issue in Github first. 202 | 203 | 204 | 205 | # Contributing 206 | 207 | We are excited to see what people can make their Sparkpad do! If you want to share your new functionality with the community then please fork this repository and submit a pull request. 208 | 209 | 210 | 211 | # Support 212 | 213 | If you would like to join The Sparkpad Discord Community where we also have a wider community dedicated to streamers please feel free to [join](https://discord.gg/uvYdVn9TBU) or for more information please visit the [Sparkpad Website](https://sparkpad.co.uk) 214 | -------------------------------------------------------------------------------- /examples/ESP32/default/default.ino: -------------------------------------------------------------------------------- 1 | // 2 | // *** N.B. This code is currently under construction! *** 3 | // 4 | 5 | /* 6 | * default firmware for the ESP32 7 | */ 8 | 9 | #define PROTOTYPE_PCB 10 | 11 | /* 12 | * Files to include 13 | */ 14 | 15 | // Non-core files 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "SPIFFS.h" 22 | #include 23 | 24 | // Core files 25 | #include "sparkpad_eeprom.h" 26 | #include "sparkpad_leds.h" 27 | #include "sparkpad_oled.h" 28 | #include "sparkpad_knob.h" 29 | #include "sparkpad_keypad.h" 30 | #include "sparkpad_wifi.h" 31 | // #include "sparkpad_obs.h" 32 | 33 | /* 34 | * Definitions 35 | */ 36 | 37 | // Choose your OS here (Comment out incorrect OS) 38 | #define OS_WINDOWS10 39 | //#define OS_UBUNTU 40 | 41 | #ifdef OS_WINDOWS10 42 | #define VOLUME_RANGE 50 43 | #endif 44 | 45 | #ifdef OS_UBUNTU 46 | #define VOLUME_RANGE 18 47 | #endif 48 | 49 | /* 50 | * Knob + Bargraph behaviour 51 | */ 52 | 53 | Timer<1, millis> save_timer; 54 | 55 | void knob_increase() { 56 | 57 | if (!muted) { 58 | 59 | knob_value = min(max(0, knob_value + 1), VOLUME_RANGE); 60 | update_bar(round(knob_value * (10.0 / VOLUME_RANGE))); 61 | } 62 | } 63 | 64 | void knob_decrease() { 65 | 66 | if (!muted) { 67 | 68 | knob_value = min(max(0, knob_value - 1), VOLUME_RANGE); 69 | update_bar(round(knob_value * (10.0 / VOLUME_RANGE))); 70 | } 71 | } 72 | 73 | void knob_button() { 74 | 75 | if (muted) { 76 | 77 | update_bar(round(knob_value * (10.0 / VOLUME_RANGE))); 78 | muted = false; 79 | } 80 | 81 | else { 82 | 83 | update_bar(0); 84 | muted = true; 85 | } 86 | } 87 | 88 | /* 89 | * Keypad behaviour 90 | */ 91 | 92 | Timer<1, millis, byte> timer_array[12]; 93 | 94 | bool timer_callback(byte index) { 95 | 96 | switchActive[index] = 0; 97 | return true; 98 | } 99 | 100 | void key_pressed(byte index) { 101 | 102 | byte my_mode = localSwitchMode[index]; 103 | byte my_state = switchActive[index]; 104 | byte my_delay = localDelay[index]; 105 | 106 | switch (my_mode) { 107 | 108 | // static 109 | case 0: 110 | break; 111 | 112 | // toggle 113 | case 1: 114 | 115 | if (my_state == 0) switchActive[index] = 1; 116 | else switchActive[index] = 0; 117 | break; 118 | 119 | // delay 120 | case 2: 121 | 122 | // stop active timer 123 | timer_array[index].cancel(); 124 | 125 | // start timer 126 | timer_array[index].in(my_delay*1000, timer_callback, index); 127 | 128 | switchActive[index] = 1; 129 | break; 130 | 131 | // group toggle 132 | case 3: 133 | 134 | // turn off all buttons in a group 135 | for (int i = 0; i < 12; i++) { 136 | 137 | if (localSwitchMode[i] == 3) switchActive[i] = 0; 138 | } 139 | 140 | switchActive[index] = 1; 141 | break; 142 | } 143 | } 144 | 145 | void key_released(byte index) { 146 | 147 | } 148 | 149 | /* 150 | * Arduino code 151 | */ 152 | 153 | void setup(){ 154 | 155 | // EEPROM settings 156 | EEPROM_setup(); 157 | 158 | // Serial 159 | Serial.begin(9600); 160 | 161 | // OLED 162 | Wire.begin(); 163 | oled.begin(&Adafruit128x64, 0x3C); 164 | oled.setFont(menuFont); 165 | oled.clear(); 166 | 167 | // If commented out, screen initialises blank 168 | // nav.doOutput(); 169 | 170 | // LEDs 171 | LEDS_setup(); 172 | 173 | // Bar graph 174 | update_bar(round(knob_value * (10.0 / VOLUME_RANGE))); 175 | save_timer.every(1000, save_knob_value); 176 | 177 | // Knob 178 | knob_setup(); 179 | 180 | // Wifi 181 | WIFI_setup(); 182 | 183 | // Obs 184 | // Serial.print(getOBSConfig()); 185 | } 186 | 187 | /* 188 | * Main program loop - runs continously once setup() is complete 189 | */ 190 | 191 | void loop() { 192 | 193 | // check keypad 194 | keypad_loop(); 195 | 196 | // check knob 197 | knob_loop(); 198 | 199 | // update EEPROM 200 | EEPROM_loop(); 201 | 202 | // update LEDs 203 | LEDs_loop(); 204 | 205 | // tick timers 206 | for (int i = 0; i < 12; i++) { 207 | 208 | timer_array[i].tick(); 209 | } 210 | 211 | save_timer.tick(); 212 | 213 | // do WIFI stuff 214 | WIFI_loop(); 215 | } 216 | -------------------------------------------------------------------------------- /examples/Pro-Micro/default-ctrl/default-ctrl.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * default firmware for the Pro Micro board (aka Arduino Leonardo) 3 | */ 4 | 5 | // #define PROTOTYPE_PCB 6 | // #define FACTORY_RESET 7 | 8 | /* 9 | * Files to include 10 | */ 11 | 12 | // Non-core files 13 | #include 14 | #include 15 | #include 16 | 17 | // Core files 18 | #include "sparkpad_eeprom.h" 19 | #include "sparkpad_oled.h" 20 | #include "sparkpad_knob.h" 21 | #include "sparkpad_leds.h" 22 | #include "sparkpad_keypad.h" 23 | 24 | /* 25 | * Definitions 26 | */ 27 | 28 | // Map keys for the buttons and knob here. For a list of available keys, check out: 29 | // https://github.com/NicoHood/HID/blob/master/src/KeyboardLayouts/ImprovedKeylayouts.h 30 | 31 | KeyboardKeycode key_mappings[12] = { 32 | 33 | KEY_F13, // Key 1 34 | KEY_F14, // Key 2 35 | KEY_F15, // Key 3 36 | KEY_F16, // Key 4 37 | KEY_F17, // Key 5 38 | KEY_F18, // Key 6 39 | KEY_F19, // Key 7 40 | KEY_F20, // Key 8 41 | KEY_F21, // Key 9 42 | KEY_LEFT_CTRL, // Key 10 43 | KEY_F23, // Key 11 44 | KEY_F24 // Key 12 45 | }; 46 | 47 | #define KNOB_INCREASE MEDIA_VOLUME_UP 48 | #define KNOB_DECREASE MEDIA_VOLUME_DOWN 49 | #define KNOB_BUTTON MEDIA_VOLUME_MUTE 50 | 51 | // Choose your OS here (Comment out incorrect OS) 52 | #define OS_WINDOWS10 53 | // #define OS_UBUNTU 54 | 55 | #ifdef OS_WINDOWS10 56 | #define VOLUME_RANGE 50 57 | #endif 58 | 59 | #ifdef OS_UBUNTU 60 | #define VOLUME_RANGE 18 61 | #endif 62 | 63 | /* 64 | * Knob + Bargraph behaviour 65 | */ 66 | 67 | Timer<1, millis> save_timer; 68 | 69 | void knob_increase() { 70 | 71 | Consumer.write(KNOB_INCREASE); 72 | 73 | if (!muted) { 74 | 75 | knob_value = min(max(0, knob_value + 1), VOLUME_RANGE); 76 | update_bar(round(knob_value * (10.0 / VOLUME_RANGE))); 77 | } 78 | } 79 | 80 | void knob_decrease() { 81 | 82 | Consumer.write(KNOB_DECREASE); 83 | 84 | if (!muted) { 85 | 86 | knob_value = min(max(0, knob_value - 1), VOLUME_RANGE); 87 | update_bar(round(knob_value * (10.0 / VOLUME_RANGE))); 88 | } 89 | } 90 | 91 | void knob_button() { 92 | 93 | Consumer.write(KNOB_BUTTON); 94 | 95 | if (muted) { 96 | 97 | update_bar(round(knob_value * (10.0 / VOLUME_RANGE))); 98 | muted = false; 99 | } 100 | 101 | else { 102 | 103 | update_bar(0); 104 | muted = true; 105 | } 106 | } 107 | 108 | /* 109 | * Keypad behaviour 110 | */ 111 | 112 | Timer<1, millis, byte> timer_array[12]; 113 | 114 | bool timer_callback(byte index) { 115 | 116 | switchActive[index] = 0; 117 | return true; 118 | } 119 | 120 | void key_pressed(byte index) { 121 | 122 | Keyboard.press(key_mappings[index]); 123 | 124 | byte my_mode = localSwitchMode[index]; 125 | byte my_state = switchActive[index]; 126 | byte my_delay = localDelay[index]; 127 | 128 | switch (my_mode) { 129 | 130 | // static 131 | case 0: 132 | break; 133 | 134 | // toggle 135 | case 1: 136 | 137 | if (my_state == 0) switchActive[index] = 1; 138 | else switchActive[index] = 0; 139 | break; 140 | 141 | // delay 142 | case 2: 143 | 144 | // stop active timer 145 | timer_array[index].cancel(); 146 | 147 | // start timer 148 | timer_array[index].in(my_delay*1000, timer_callback, index); 149 | 150 | switchActive[index] = 1; 151 | break; 152 | 153 | // group toggle 154 | case 3: 155 | 156 | // turn off all buttons in a group 157 | for (int i = 0; i < 12; i++) { 158 | 159 | if (localSwitchMode[i] == 3) switchActive[i] = 0; 160 | } 161 | 162 | switchActive[index] = 1; 163 | break; 164 | } 165 | } 166 | 167 | void key_released(byte index) { 168 | 169 | Keyboard.release(key_mappings[index]); 170 | } 171 | 172 | /* 173 | * Arduino code 174 | */ 175 | 176 | void setup(){ 177 | 178 | // EEPROM settings 179 | EEPROM_setup(); 180 | 181 | // Keyboard 182 | Keyboard.begin(); 183 | Consumer.begin(); 184 | 185 | // Serial must be disabled for HID to work properly 186 | // Serial.begin(9600); 187 | // while(!Serial); 188 | 189 | // OLED 190 | Wire.begin(); 191 | oled.begin(&Adafruit128x64, 0x3C); 192 | oled.setFont(menuFont); 193 | oled.clear(); 194 | 195 | // If commented out, screen initialises blank 196 | // nav.doOutput(); 197 | 198 | // LEDs 199 | LEDS_setup(); 200 | 201 | // Bar graph 202 | update_bar(round(knob_value * (10.0 / VOLUME_RANGE))); 203 | save_timer.every(1000, save_knob_value); 204 | 205 | // Knob 206 | knob_setup(); 207 | } 208 | 209 | void loop() { 210 | 211 | // check keypad 212 | keypad_loop(); 213 | 214 | // check knob 215 | knob_loop(); 216 | 217 | // update EEPROM 218 | EEPROM_loop(); 219 | 220 | // update LEDs 221 | LEDs_loop(); 222 | 223 | // tick timers 224 | for (int i = 0; i < 12; i++) { 225 | 226 | timer_array[i].tick(); 227 | } 228 | 229 | save_timer.tick(); 230 | } -------------------------------------------------------------------------------- /examples/Pro-Micro/default-noshift/default-noshift.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * default firmware for the Pro Micro board (aka Arduino Leonardo) 3 | */ 4 | 5 | // #define PROTOTYPE_PCB 6 | // #define FACTORY_RESET 7 | 8 | /* 9 | * Files to include 10 | */ 11 | 12 | // Non-core files 13 | #include 14 | #include 15 | #include 16 | 17 | // Core files 18 | #include "sparkpad_eeprom.h" 19 | #include "sparkpad_oled.h" 20 | #include "sparkpad_knob.h" 21 | #include "sparkpad_leds.h" 22 | #include "sparkpad_keypad.h" 23 | 24 | /* 25 | * Definitions 26 | */ 27 | 28 | // Map keys for the buttons and knob here. For a list of available keys, check out: 29 | // https://github.com/NicoHood/HID/blob/master/src/KeyboardLayouts/ImprovedKeylayouts.h 30 | 31 | KeyboardKeycode key_mappings[12] = { 32 | 33 | KEY_F13, // Key 1 34 | KEY_F14, // Key 2 35 | KEY_F15, // Key 3 36 | KEY_F16, // Key 4 37 | KEY_F17, // Key 5 38 | KEY_F18, // Key 6 39 | KEY_F19, // Key 7 40 | KEY_F20, // Key 8 41 | KEY_F21, // Key 9 42 | KEY_F22, // Key 10 43 | KEY_F23, // Key 11 44 | KEY_F24 // Key 12 45 | }; 46 | 47 | #define KNOB_INCREASE MEDIA_VOLUME_UP 48 | #define KNOB_DECREASE MEDIA_VOLUME_DOWN 49 | #define KNOB_BUTTON MEDIA_VOLUME_MUTE 50 | 51 | // Choose your OS here (Comment out incorrect OS) 52 | #define OS_WINDOWS10 53 | // #define OS_UBUNTU 54 | 55 | #ifdef OS_WINDOWS10 56 | #define VOLUME_RANGE 50 57 | #endif 58 | 59 | #ifdef OS_UBUNTU 60 | #define VOLUME_RANGE 18 61 | #endif 62 | 63 | /* 64 | * Knob + Bargraph behaviour 65 | */ 66 | 67 | Timer<1, millis> save_timer; 68 | 69 | void knob_increase() { 70 | 71 | Consumer.write(KNOB_INCREASE); 72 | 73 | if (!muted) { 74 | 75 | knob_value = min(max(0, knob_value + 1), VOLUME_RANGE); 76 | update_bar(round(knob_value * (10.0 / VOLUME_RANGE))); 77 | } 78 | } 79 | 80 | void knob_decrease() { 81 | 82 | Consumer.write(KNOB_DECREASE); 83 | 84 | if (!muted) { 85 | 86 | knob_value = min(max(0, knob_value - 1), VOLUME_RANGE); 87 | update_bar(round(knob_value * (10.0 / VOLUME_RANGE))); 88 | } 89 | } 90 | 91 | void knob_button() { 92 | 93 | Consumer.write(KNOB_BUTTON); 94 | 95 | if (muted) { 96 | 97 | update_bar(round(knob_value * (10.0 / VOLUME_RANGE))); 98 | muted = false; 99 | } 100 | 101 | else { 102 | 103 | update_bar(0); 104 | muted = true; 105 | } 106 | } 107 | 108 | /* 109 | * Keypad behaviour 110 | */ 111 | 112 | Timer<1, millis, byte> timer_array[12]; 113 | 114 | bool timer_callback(byte index) { 115 | 116 | switchActive[index] = 0; 117 | return true; 118 | } 119 | 120 | void key_pressed(byte index) { 121 | 122 | Keyboard.press(key_mappings[index]); 123 | 124 | byte my_mode = localSwitchMode[index]; 125 | byte my_state = switchActive[index]; 126 | byte my_delay = localDelay[index]; 127 | 128 | switch (my_mode) { 129 | 130 | // static 131 | case 0: 132 | break; 133 | 134 | // toggle 135 | case 1: 136 | 137 | if (my_state == 0) switchActive[index] = 1; 138 | else switchActive[index] = 0; 139 | break; 140 | 141 | // delay 142 | case 2: 143 | 144 | // stop active timer 145 | timer_array[index].cancel(); 146 | 147 | // start timer 148 | timer_array[index].in(my_delay*1000, timer_callback, index); 149 | 150 | switchActive[index] = 1; 151 | break; 152 | 153 | // group toggle 154 | case 3: 155 | 156 | // turn off all buttons in a group 157 | for (int i = 0; i < 12; i++) { 158 | 159 | if (localSwitchMode[i] == 3) switchActive[i] = 0; 160 | } 161 | 162 | switchActive[index] = 1; 163 | break; 164 | } 165 | } 166 | 167 | void key_released(byte index) { 168 | 169 | Keyboard.release(key_mappings[index]); 170 | } 171 | 172 | /* 173 | * Arduino code 174 | */ 175 | 176 | void setup(){ 177 | 178 | // EEPROM settings 179 | EEPROM_setup(); 180 | 181 | // Keyboard 182 | Keyboard.begin(); 183 | Consumer.begin(); 184 | 185 | // Serial must be disabled for HID to work properly 186 | // Serial.begin(9600); 187 | // while(!Serial); 188 | 189 | // OLED 190 | Wire.begin(); 191 | oled.begin(&Adafruit128x64, 0x3C); 192 | oled.setFont(menuFont); 193 | oled.clear(); 194 | 195 | // If commented out, screen initialises blank 196 | // nav.doOutput(); 197 | 198 | // LEDs 199 | LEDS_setup(); 200 | 201 | // Bar graph 202 | update_bar(round(knob_value * (10.0 / VOLUME_RANGE))); 203 | save_timer.every(1000, save_knob_value); 204 | 205 | // Knob 206 | knob_setup(); 207 | } 208 | 209 | void loop() { 210 | 211 | // check keypad 212 | keypad_loop(); 213 | 214 | // check knob 215 | knob_loop(); 216 | 217 | // update EEPROM 218 | EEPROM_loop(); 219 | 220 | // update LEDs 221 | LEDs_loop(); 222 | 223 | // tick timers 224 | for (int i = 0; i < 12; i++) { 225 | 226 | timer_array[i].tick(); 227 | } 228 | 229 | save_timer.tick(); 230 | } -------------------------------------------------------------------------------- /examples/Pro-Micro/default/default.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * default firmware for the Pro Micro board (aka Arduino Leonardo) 3 | */ 4 | 5 | // #define PROTOTYPE_PCB 6 | // #define FACTORY_RESET 7 | 8 | /* 9 | * Files to include 10 | */ 11 | 12 | // Non-core files 13 | #include 14 | #include 15 | #include 16 | 17 | // Core files 18 | #include "sparkpad_eeprom.h" 19 | #include "sparkpad_oled.h" 20 | #include "sparkpad_knob.h" 21 | #include "sparkpad_leds.h" 22 | #include "sparkpad_keypad.h" 23 | 24 | /* 25 | * Definitions 26 | */ 27 | 28 | // Map keys for the buttons and knob here. For a list of available keys, check out: 29 | // https://github.com/NicoHood/HID/blob/master/src/KeyboardLayouts/ImprovedKeylayouts.h 30 | 31 | KeyboardKeycode key_mappings[12] = { 32 | 33 | KEY_F13, // Key 1 34 | KEY_F14, // Key 2 35 | KEY_F15, // Key 3 36 | KEY_F16, // Key 4 37 | KEY_F17, // Key 5 38 | KEY_F18, // Key 6 39 | KEY_F19, // Key 7 40 | KEY_F20, // Key 8 41 | KEY_F21, // Key 9 42 | KEY_RIGHT_SHIFT, // Key 10 43 | KEY_F23, // Key 11 44 | KEY_F24 // Key 12 45 | }; 46 | 47 | #define KNOB_INCREASE MEDIA_VOLUME_UP 48 | #define KNOB_DECREASE MEDIA_VOLUME_DOWN 49 | #define KNOB_BUTTON MEDIA_VOLUME_MUTE 50 | 51 | // Choose your OS here (Comment out incorrect OS) 52 | #define OS_WINDOWS10 53 | // #define OS_UBUNTU 54 | 55 | #ifdef OS_WINDOWS10 56 | #define VOLUME_RANGE 50 57 | #endif 58 | 59 | #ifdef OS_UBUNTU 60 | #define VOLUME_RANGE 18 61 | #endif 62 | 63 | /* 64 | * Knob + Bargraph behaviour 65 | */ 66 | 67 | Timer<1, millis> save_timer; 68 | 69 | void knob_increase() { 70 | 71 | Consumer.write(KNOB_INCREASE); 72 | 73 | if (!muted) { 74 | 75 | knob_value = min(max(0, knob_value + 1), VOLUME_RANGE); 76 | update_bar(round(knob_value * (10.0 / VOLUME_RANGE))); 77 | } 78 | } 79 | 80 | void knob_decrease() { 81 | 82 | Consumer.write(KNOB_DECREASE); 83 | 84 | if (!muted) { 85 | 86 | knob_value = min(max(0, knob_value - 1), VOLUME_RANGE); 87 | update_bar(round(knob_value * (10.0 / VOLUME_RANGE))); 88 | } 89 | } 90 | 91 | void knob_button() { 92 | 93 | Consumer.write(KNOB_BUTTON); 94 | 95 | if (muted) { 96 | 97 | update_bar(round(knob_value * (10.0 / VOLUME_RANGE))); 98 | muted = false; 99 | } 100 | 101 | else { 102 | 103 | update_bar(0); 104 | muted = true; 105 | } 106 | } 107 | 108 | /* 109 | * Keypad behaviour 110 | */ 111 | 112 | Timer<1, millis, byte> timer_array[12]; 113 | 114 | bool timer_callback(byte index) { 115 | 116 | switchActive[index] = 0; 117 | return true; 118 | } 119 | 120 | void key_pressed(byte index) { 121 | 122 | Keyboard.press(key_mappings[index]); 123 | 124 | byte my_mode = localSwitchMode[index]; 125 | byte my_state = switchActive[index]; 126 | byte my_delay = localDelay[index]; 127 | 128 | switch (my_mode) { 129 | 130 | // static 131 | case 0: 132 | break; 133 | 134 | // toggle 135 | case 1: 136 | 137 | if (my_state == 0) switchActive[index] = 1; 138 | else switchActive[index] = 0; 139 | break; 140 | 141 | // delay 142 | case 2: 143 | 144 | // stop active timer 145 | timer_array[index].cancel(); 146 | 147 | // start timer 148 | timer_array[index].in(my_delay*1000, timer_callback, index); 149 | 150 | switchActive[index] = 1; 151 | break; 152 | 153 | // group toggle 154 | case 3: 155 | 156 | // turn off all buttons in a group 157 | for (int i = 0; i < 12; i++) { 158 | 159 | if (localSwitchMode[i] == 3) switchActive[i] = 0; 160 | } 161 | 162 | switchActive[index] = 1; 163 | break; 164 | } 165 | } 166 | 167 | void key_released(byte index) { 168 | 169 | Keyboard.release(key_mappings[index]); 170 | } 171 | 172 | /* 173 | * Arduino code 174 | */ 175 | 176 | void setup(){ 177 | 178 | // EEPROM settings 179 | EEPROM_setup(); 180 | 181 | // Keyboard 182 | Keyboard.begin(); 183 | Consumer.begin(); 184 | 185 | // Serial must be disabled for HID to work properly 186 | // Serial.begin(9600); 187 | // while(!Serial); 188 | 189 | // OLED 190 | Wire.begin(); 191 | oled.begin(&Adafruit128x64, 0x3C); 192 | oled.setFont(menuFont); 193 | oled.clear(); 194 | 195 | // If commented out, screen initialises blank 196 | // nav.doOutput(); 197 | 198 | // LEDs 199 | LEDS_setup(); 200 | 201 | // Bar graph 202 | update_bar(round(knob_value * (10.0 / VOLUME_RANGE))); 203 | save_timer.every(1000, save_knob_value); 204 | 205 | // Knob 206 | knob_setup(); 207 | } 208 | 209 | void loop() { 210 | 211 | // check keypad 212 | keypad_loop(); 213 | 214 | // check knob 215 | knob_loop(); 216 | 217 | // update EEPROM 218 | EEPROM_loop(); 219 | 220 | // update LEDs 221 | LEDs_loop(); 222 | 223 | // tick timers 224 | for (int i = 0; i < 12; i++) { 225 | 226 | timer_array[i].tick(); 227 | } 228 | 229 | save_timer.tick(); 230 | } -------------------------------------------------------------------------------- /images/addzip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Patrick-Thomas/Sparkpad-Arduino/998a5d0545909c54bad159f7635fc2f5b5d65142/images/addzip.png -------------------------------------------------------------------------------- /images/board.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Patrick-Thomas/Sparkpad-Arduino/998a5d0545909c54bad159f7635fc2f5b5d65142/images/board.png -------------------------------------------------------------------------------- /images/front-annotated2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Patrick-Thomas/Sparkpad-Arduino/998a5d0545909c54bad159f7635fc2f5b5d65142/images/front-annotated2.jpg -------------------------------------------------------------------------------- /images/key_settings.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Patrick-Thomas/Sparkpad-Arduino/998a5d0545909c54bad159f7635fc2f5b5d65142/images/key_settings.jpg -------------------------------------------------------------------------------- /images/keypad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Patrick-Thomas/Sparkpad-Arduino/998a5d0545909c54bad159f7635fc2f5b5d65142/images/keypad.png -------------------------------------------------------------------------------- /images/libman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Patrick-Thomas/Sparkpad-Arduino/998a5d0545909c54bad159f7635fc2f5b5d65142/images/libman.png -------------------------------------------------------------------------------- /images/libman2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Patrick-Thomas/Sparkpad-Arduino/998a5d0545909c54bad159f7635fc2f5b5d65142/images/libman2.png -------------------------------------------------------------------------------- /images/main_menu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Patrick-Thomas/Sparkpad-Arduino/998a5d0545909c54bad159f7635fc2f5b5d65142/images/main_menu.jpg -------------------------------------------------------------------------------- /images/nums.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Patrick-Thomas/Sparkpad-Arduino/998a5d0545909c54bad159f7635fc2f5b5d65142/images/nums.png -------------------------------------------------------------------------------- /images/port.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Patrick-Thomas/Sparkpad-Arduino/998a5d0545909c54bad159f7635fc2f5b5d65142/images/port.png -------------------------------------------------------------------------------- /images/sparkpad.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Patrick-Thomas/Sparkpad-Arduino/998a5d0545909c54bad159f7635fc2f5b5d65142/images/sparkpad.jpg -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Sparkpad 2 | version=0.2.1 3 | author=Patrick Thomas 4 | maintainer=Patrick Thomas 5 | sentence=Arduino firmware for the Sparkpad 6 | paragraph=Upload and customise firmware for your Sparkpad using this library 7 | category=Device Control 8 | url=https://github.com/Patrick-Thomas/Sparkpad-Arduino 9 | architectures=* 10 | depends=HID-Project,ArduinoMenu library,SSD1306Ascii,arduino-timer,ArduinoJson,WebSockets 11 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [platformio] 12 | src_dir = examples 13 | monitor_speed = 9600 14 | 15 | [env] 16 | framework = arduino 17 | 18 | [env:pro-micro-default] 19 | platform = atmelavr 20 | board = leonardo 21 | build_flags = 22 | -I src 23 | -D ARDUINO_AVR_LEONARDO 24 | src_filter = + 25 | lib_deps = 26 | Wire 27 | EEPROM 28 | greiman/SSD1306Ascii@^1.3.0 29 | neu-rah/ArduinoMenu library@^4.21.3 30 | nicohood/HID-Project@^2.6.2 31 | contrem/arduino-timer@^2.3.0 32 | 33 | [env:pro-micro-default-ctrl] 34 | platform = atmelavr 35 | board = leonardo 36 | build_flags = 37 | -I src 38 | -D ARDUINO_AVR_LEONARDO 39 | src_filter = + 40 | lib_deps = 41 | Wire 42 | EEPROM 43 | greiman/SSD1306Ascii@^1.3.0 44 | neu-rah/ArduinoMenu library@^4.21.3 45 | nicohood/HID-Project@^2.6.2 46 | contrem/arduino-timer@^2.3.0 47 | 48 | [env:pro-micro-default-noshift] 49 | platform = atmelavr 50 | board = leonardo 51 | build_flags = 52 | -I src 53 | -D ARDUINO_AVR_LEONARDO 54 | src_filter = + 55 | lib_deps = 56 | Wire 57 | EEPROM 58 | greiman/SSD1306Ascii@^1.3.0 59 | neu-rah/ArduinoMenu library@^4.21.3 60 | nicohood/HID-Project@^2.6.2 61 | contrem/arduino-timer@^2.3.0 62 | 63 | [env:esp32-default] 64 | platform = espressif32 65 | board = esp32dev 66 | build_flags = 67 | -I src 68 | -D ESP32 69 | src_filter = + 70 | lib_deps = 71 | Wire 72 | EEPROM 73 | greiman/SSD1306Ascii@^1.3.0 74 | neu-rah/ArduinoMenu library@^4.21.3 75 | contrem/arduino-timer @ ^2.3.0 76 | bblanchon/ArduinoJson @ ^6.17.3 77 | ; links2004/WebSockets @ ^2.3.6 78 | -------------------------------------------------------------------------------- /src/sparkpad_eeprom.h: -------------------------------------------------------------------------------- 1 | #ifndef SP_EEPROM 2 | #define SP_EEPROM 3 | 4 | #include 5 | #include 6 | 7 | // EEPROM memory addresses 8 | byte globalLedColourAddress = 0; 9 | byte globalLedBrightnessAddress = 1; 10 | byte knob_address = 2; 11 | byte localActiveLedColourAddress = 3; 12 | byte localLightingModeAddress = 15; 13 | byte localSwitchModeAddress = 27; 14 | byte localDelayAddress = 39; 15 | byte selectedIndexAddress = 51; 16 | byte freshByteAddress = 52; 17 | byte localDefaultToggleStateAddress = 53; 18 | byte localInactiveLedColourAddress = 65; 19 | 20 | // Values to update 21 | byte globalLedColour = 0; 22 | byte globalLedBrightness = 0; 23 | byte localActiveLedColour[12] = {0}; 24 | byte localInactiveLedColour[12] = {0}; 25 | byte localLightingMode[12] = {0}; 26 | byte localSwitchMode[12] = {0}; 27 | byte localDelay[12] = {0}; 28 | byte localDefaultToggleState[12] = {0}; 29 | byte switchActive[12] = {0}; 30 | byte selectedIndex = 0; 31 | byte selectedActiveLedColour = 0; 32 | byte selectedInactiveLedColour = 0; 33 | byte selectedLightingMode = 0; 34 | byte selectedSwitchMode = 0; 35 | byte selectedDefaultToggleState = 0; 36 | byte selectedDelay = 0; 37 | byte knob_value = 0; 38 | 39 | // To detect value changes 40 | byte global_led_brightness_current = 0; 41 | byte selected_index_current = 0; 42 | bool muted = false; 43 | 44 | bool save_knob_value(void *) { 45 | 46 | EEPROM.write(knob_address, knob_value); 47 | #ifdef ESP32 48 | EEPROM.commit(); 49 | #endif 50 | 51 | return true; 52 | } 53 | 54 | void EEPROM_save() { 55 | 56 | EEPROM.write(globalLedColourAddress, globalLedColour); 57 | #ifdef ESP32 58 | EEPROM.commit(); 59 | #endif 60 | 61 | EEPROM.write(globalLedBrightnessAddress, globalLedBrightness); 62 | #ifdef ESP32 63 | EEPROM.commit(); 64 | #endif 65 | 66 | EEPROM.write(localActiveLedColourAddress + selectedIndex, selectedActiveLedColour); 67 | #ifdef ESP32 68 | EEPROM.commit(); 69 | #endif 70 | 71 | EEPROM.write(localInactiveLedColourAddress + selectedIndex, selectedInactiveLedColour); 72 | #ifdef ESP32 73 | EEPROM.commit(); 74 | #endif 75 | 76 | EEPROM.write(localLightingModeAddress + selectedIndex, selectedLightingMode); 77 | #ifdef ESP32 78 | EEPROM.commit(); 79 | #endif 80 | 81 | EEPROM.write(localSwitchModeAddress + selectedIndex, selectedSwitchMode); 82 | #ifdef ESP32 83 | EEPROM.commit(); 84 | #endif 85 | 86 | EEPROM.write(localDefaultToggleStateAddress + selectedIndex, selectedDefaultToggleState); 87 | #ifdef ESP32 88 | EEPROM.commit(); 89 | #endif 90 | 91 | EEPROM.write(localDelayAddress + selectedIndex, selectedDelay); 92 | #ifdef ESP32 93 | EEPROM.commit(); 94 | #endif 95 | 96 | EEPROM.write(selectedIndexAddress, selectedIndex); 97 | #ifdef ESP32 98 | EEPROM.commit(); 99 | #endif 100 | } 101 | 102 | void EEPROM_load() { 103 | 104 | selectedActiveLedColour = localActiveLedColour[selectedIndex]; 105 | selectedInactiveLedColour = localInactiveLedColour[selectedIndex]; 106 | selectedLightingMode = localLightingMode[selectedIndex]; 107 | selectedSwitchMode = localSwitchMode[selectedIndex]; 108 | selectedDefaultToggleState = localDefaultToggleState[selectedIndex]; 109 | selectedDelay = localDelay[selectedIndex]; 110 | 111 | EEPROM.write(selectedIndexAddress, selectedIndex); 112 | #ifdef ESP32 113 | EEPROM.commit(); 114 | #endif 115 | } 116 | 117 | void EEPROM_setup() { 118 | 119 | #ifdef ESP32 120 | EEPROM.begin(77); 121 | #endif 122 | 123 | bool factory_reset = false; 124 | 125 | #ifdef FACTORY_RESET 126 | factory_reset = true; 127 | #endif 128 | 129 | // This should only run for a fresh Sparkpad 130 | if (EEPROM.read(freshByteAddress) == 255 || factory_reset) { 131 | 132 | EEPROM.write(globalLedColourAddress, 0); 133 | #ifdef ESP32 134 | EEPROM.commit(); 135 | #endif 136 | 137 | EEPROM.write(globalLedBrightnessAddress, 0); 138 | #ifdef ESP32 139 | EEPROM.commit(); 140 | #endif 141 | 142 | EEPROM.write(selectedIndexAddress, 0); 143 | #ifdef ESP32 144 | EEPROM.commit(); 145 | #endif 146 | 147 | for (int i = 0; i < 12; i++) { 148 | 149 | EEPROM.write(localActiveLedColourAddress + i, 0); 150 | #ifdef ESP32 151 | EEPROM.commit(); 152 | #endif 153 | 154 | EEPROM.write(localInactiveLedColourAddress + i, 0); 155 | #ifdef ESP32 156 | EEPROM.commit(); 157 | #endif 158 | 159 | EEPROM.write(localLightingModeAddress + i, 0); 160 | #ifdef ESP32 161 | EEPROM.commit(); 162 | #endif 163 | 164 | EEPROM.write(localSwitchModeAddress + i, 0); 165 | #ifdef ESP32 166 | EEPROM.commit(); 167 | #endif 168 | 169 | EEPROM.write(localDefaultToggleStateAddress + i, 0); 170 | #ifdef ESP32 171 | EEPROM.commit(); 172 | #endif 173 | 174 | EEPROM.write(localDelayAddress + i, 0); 175 | #ifdef ESP32 176 | EEPROM.commit(); 177 | #endif 178 | } 179 | 180 | EEPROM.write(knob_address, 0); 181 | #ifdef ESP32 182 | EEPROM.commit(); 183 | #endif 184 | 185 | EEPROM.write(freshByteAddress, 0); 186 | #ifdef ESP32 187 | EEPROM.commit(); 188 | #endif 189 | } 190 | 191 | globalLedColour = EEPROM.read(globalLedColourAddress); 192 | globalLedBrightness = EEPROM.read(globalLedBrightnessAddress); 193 | global_led_brightness_current = globalLedBrightness; 194 | 195 | selectedIndex = EEPROM.read(selectedIndexAddress); 196 | selected_index_current = selectedIndex; 197 | 198 | for (int i = 0; i < 12; i++) { 199 | 200 | localActiveLedColour[i] = EEPROM.read(localActiveLedColourAddress + i); 201 | localInactiveLedColour[i] = EEPROM.read(localInactiveLedColourAddress + i); 202 | localLightingMode[i] = EEPROM.read(localLightingModeAddress + i); 203 | localSwitchMode[i] = EEPROM.read(localSwitchModeAddress + i); 204 | localDefaultToggleState[i] = EEPROM.read(localDefaultToggleStateAddress + i); 205 | localDelay[i] = EEPROM.read(localDelayAddress + i); 206 | } 207 | 208 | selectedActiveLedColour = localActiveLedColour[selectedIndex]; 209 | selectedInactiveLedColour = localInactiveLedColour[selectedIndex]; 210 | selectedLightingMode = localLightingMode[selectedIndex]; 211 | selectedSwitchMode = localSwitchMode[selectedIndex]; 212 | selectedDefaultToggleState = localDefaultToggleState[selectedIndex]; 213 | selectedDelay = localDelay[selectedIndex]; 214 | 215 | knob_value = EEPROM.read(knob_address); 216 | 217 | // populate switchActive array 218 | for (int i = 0; i < 12; i++) { 219 | 220 | // toggle or group toggle 221 | if ((localSwitchMode[i] == 1) || (localSwitchMode[i] == 3)) switchActive[i] = localDefaultToggleState[i]; 222 | } 223 | 224 | } 225 | 226 | void EEPROM_loop() { 227 | 228 | // save modified values to the local arrays 229 | if (selected_index_current != selectedIndex) { 230 | 231 | selected_index_current = selectedIndex; 232 | EEPROM_load(); 233 | } 234 | 235 | localActiveLedColour[selectedIndex] = selectedActiveLedColour; 236 | localInactiveLedColour[selectedIndex] = selectedInactiveLedColour; 237 | localLightingMode[selectedIndex] = selectedLightingMode; 238 | localSwitchMode[selectedIndex] = selectedSwitchMode; 239 | localDefaultToggleState[selectedIndex] = selectedDefaultToggleState; 240 | localDelay[selectedIndex] = selectedDelay; 241 | } 242 | 243 | #endif -------------------------------------------------------------------------------- /src/sparkpad_keypad.h: -------------------------------------------------------------------------------- 1 | #ifndef SP_KEYPAD 2 | #define SP_KEYPAD 3 | 4 | #define KEYPAD_DEBOUNCE_MS 10 5 | 6 | #include 7 | #include "sparkpad_oled.h" 8 | 9 | #ifdef ARDUINO_AVR_LEONARDO 10 | 11 | #ifdef PROTOTYPE_PCB 12 | byte rowPins[4] = {20, 16, 10, 14}; 13 | byte colPins[4] = {19, 18, 15, 21}; 14 | #else 15 | byte rowPins[4] = {5, 15, 9, 8}; 16 | byte colPins[4] = {6, 7, 18, 4}; 17 | #endif 18 | 19 | #elif ESP32 20 | 21 | #ifdef PROTOTYPE_PCB 22 | byte rowPins[4] = {23, 33, 32, 25}; 23 | byte colPins[4] = {14, 27, 26, 13}; 24 | #else 25 | byte rowPins[4] = {16, 26, 23, 19}; 26 | byte colPins[4] = {17, 18, 27, 4}; 27 | #endif 28 | 29 | #else 30 | #error this board is not supported! Must be Arduino Leonardo or ESP32 31 | #endif 32 | 33 | // Prototypes 34 | void key_pressed(byte); 35 | void key_released(byte); 36 | void knob_button(); 37 | 38 | byte bitMap[4]; 39 | byte bitMapNew[4]; 40 | 41 | unsigned long startTime = 0; 42 | 43 | void scanKeys() { 44 | 45 | // Re-intialize the column pins. Allows sharing these pins with other hardware. 46 | for (byte c = 0; c < 4; c++) { 47 | 48 | pinMode(colPins[c], INPUT_PULLUP); 49 | } 50 | 51 | // bitMap stores ALL the keys that are being pressed. 52 | for (byte r = 0; r < 4; r++) { 53 | 54 | pinMode(rowPins[r], OUTPUT); 55 | digitalWrite(rowPins[r], LOW); // Begin row pulse output 56 | 57 | for (byte c = 0; c < 4; c++) { 58 | 59 | bitWrite(bitMapNew[c], r, !digitalRead(colPins[c])); // keypress is active low so invert to high. 60 | } 61 | 62 | // Set pin to high impedance input. Effectively ends row pulse. 63 | digitalWrite(rowPins[r], HIGH); 64 | pinMode(rowPins[r], INPUT); 65 | } 66 | } 67 | 68 | void updateKeys() { 69 | 70 | for (byte c = 0; c < 4; c++) { 71 | 72 | for (byte r = 0; r < 4; r++) { 73 | 74 | // Compare old and current values 75 | bool button_last = bitRead(bitMap[c], r); 76 | bool button_current = bitRead(bitMapNew[c], r); 77 | 78 | // button pressed 79 | if (button_last == false && button_current == true) { 80 | 81 | if (c < 3) key_pressed(r * 3 + c); 82 | 83 | else { 84 | 85 | switch (r) { 86 | 87 | case 0: 88 | knob_button(); 89 | break; 90 | case 1: 91 | nav.doNav(downCmd); 92 | nav.doOutput(); 93 | break; 94 | case 2: 95 | nav.doNav(enterCmd); 96 | nav.doOutput(); 97 | break; 98 | case 3: 99 | nav.doNav(upCmd); 100 | nav.doOutput(); 101 | break; 102 | } 103 | } 104 | } 105 | 106 | // button released 107 | else if (button_last == true && button_current == false) { 108 | 109 | if (c < 3) key_released(r * 3 + c); 110 | } 111 | 112 | bitWrite(bitMap[c], r, button_current); 113 | } 114 | } 115 | } 116 | 117 | void keypad_loop() { 118 | 119 | // Limit how often the keypad is scanned. This makes the loop() run 10 times as fast. 120 | if ( (millis() - startTime) > KEYPAD_DEBOUNCE_MS ) { 121 | 122 | scanKeys(); 123 | updateKeys(); 124 | startTime = millis(); 125 | } 126 | } 127 | 128 | #endif -------------------------------------------------------------------------------- /src/sparkpad_knob.h: -------------------------------------------------------------------------------- 1 | #ifndef SP_KNOB 2 | #define SP_KNOB 3 | 4 | #define KNOB_DEBOUNCE_MS 5 5 | 6 | #include 7 | #include 8 | 9 | #ifdef ARDUINO_AVR_LEONARDO 10 | 11 | #ifdef PROTOTYPE_PCB 12 | #define pinA 1 13 | #define pinB 0 14 | #else 15 | #define pinA 0 16 | #define pinB 1 17 | #endif 18 | 19 | #elif ESP32 20 | 21 | #ifdef PROTOTYPE_PCB 22 | #define pinA 17 23 | #define pinB 16 24 | #else 25 | #define pinA 13 26 | #define pinB 14 27 | #endif 28 | 29 | #else 30 | #error this board is not supported! Must be Arduino Leonardo or ESP32 31 | #endif 32 | 33 | // Prototypes 34 | void knob_increase(); 35 | void knob_decrease(); 36 | 37 | #ifdef PROTOTYPE_PCB 38 | 39 | Timer<1, millis> knob_timer; 40 | 41 | uint8_t state = 0; 42 | 43 | bool knob_callback(void *) { 44 | 45 | uint8_t s = state & 3; 46 | 47 | if (digitalRead(pinA)) s |= 4; 48 | if (digitalRead(pinB)) s |= 8; 49 | 50 | switch (s) { 51 | 52 | case 0: case 5: case 10: case 15: 53 | break; 54 | 55 | case 1: case 7: case 8: case 14: 56 | knob_increase(); 57 | break; 58 | 59 | case 2: case 4: case 11: case 13: 60 | knob_decrease(); 61 | break; 62 | 63 | case 3: case 12: 64 | knob_increase(); 65 | break; 66 | 67 | default: 68 | knob_decrease(); 69 | break; 70 | } 71 | 72 | state = (s >> 2); 73 | 74 | return true; 75 | } 76 | 77 | #else 78 | 79 | void pinA_ISR() { 80 | 81 | if (digitalRead(pinB)) knob_increase(); 82 | else knob_decrease(); 83 | } 84 | 85 | #endif 86 | 87 | void knob_setup() { 88 | 89 | #ifdef PROTOTYPE_PCB 90 | 91 | pinMode(pinA, INPUT); 92 | pinMode(pinB, INPUT); 93 | if (digitalRead(pinA)) state |= 1; 94 | if (digitalRead(pinB)) state |= 2; 95 | 96 | knob_timer.every(KNOB_DEBOUNCE_MS, knob_callback); 97 | 98 | #else 99 | 100 | attachInterrupt(digitalPinToInterrupt(pinA), pinA_ISR, RISING); 101 | 102 | #endif 103 | } 104 | 105 | void knob_loop() { 106 | 107 | #ifdef PROTOTYPE_PCB 108 | knob_timer.tick(); 109 | #endif 110 | } 111 | 112 | #endif -------------------------------------------------------------------------------- /src/sparkpad_leds.h: -------------------------------------------------------------------------------- 1 | #ifndef SP_LED 2 | #define SP_LED 3 | 4 | #include 5 | #include "sparkpad_eeprom.h" 6 | 7 | #define TM16XX_CMD_DATA_AUTO 0x40 8 | #define TM16XX_CMD_DATA_READ 0x42 9 | #define TM16XX_CMD_DATA_FIXED 0x44 10 | #define TM16XX_CMD_DISPLAY 0x80 11 | #define TM16XX_CMD_ADDRESS 0xC0 12 | 13 | #ifdef ARDUINO_AVR_LEONARDO 14 | 15 | #ifdef PROTOTYPE_PCB 16 | #define dataPin 8 17 | #define clockPin 7 18 | #define strobePin 6 19 | #else 20 | #define dataPin 10 21 | #define clockPin 16 22 | #define strobePin 14 23 | #endif 24 | 25 | #elif ESP32 26 | 27 | #ifdef PROTOTYPE_PCB 28 | #define dataPin 19 29 | #define clockPin 18 30 | #define strobePin 5 31 | #else 32 | #define dataPin 32 33 | #define clockPin 33 34 | #define strobePin 25 35 | #endif 36 | 37 | #else 38 | #error this board is not supported! Must be Arduino Leonardo or ESP32 39 | #endif 40 | 41 | byte grid_array[16] = {}; 42 | 43 | void send(byte data) 44 | { 45 | for (int i = 0; i < 8; i++) { 46 | digitalWrite(clockPin, LOW); 47 | digitalWrite(dataPin, data & 1 ? HIGH : LOW); 48 | data >>= 1; 49 | digitalWrite(clockPin, HIGH); 50 | } 51 | } 52 | 53 | void sendCommand(byte cmd) 54 | { 55 | digitalWrite(strobePin, LOW); 56 | send(cmd); 57 | digitalWrite(strobePin, HIGH); 58 | } 59 | 60 | void update_tm1638() { 61 | 62 | sendCommand(TM16XX_CMD_DATA_AUTO); 63 | digitalWrite(strobePin, LOW); 64 | send(TM16XX_CMD_ADDRESS); 65 | 66 | for (int i = 0; i < 16; i++) { 67 | 68 | byte data = grid_array[i]; 69 | send(data); 70 | } 71 | 72 | digitalWrite(strobePin, HIGH); 73 | } 74 | 75 | void update_led(byte number, byte RGB) { 76 | 77 | byte seg = number % 6; 78 | 79 | // red LED 80 | byte grid = number < 6 ? 4 : 10; 81 | 82 | if (RGB & 0x1) grid_array[grid] |= 1 << seg; 83 | else grid_array[grid] &= ~(1 << seg); 84 | 85 | // green LED 86 | grid = number < 6 ? 0 : 6; 87 | 88 | if (RGB & 0x2) grid_array[grid] |= 1 << seg; 89 | else grid_array[grid] &= ~(1 << seg); 90 | 91 | // blue LED 92 | grid = number < 6 ? 2 : 8; 93 | 94 | if (RGB & 0x4) grid_array[grid] |= 1 << seg; 95 | else grid_array[grid] &= ~(1 << seg); 96 | 97 | update_tm1638(); 98 | } 99 | 100 | void update_all_leds(byte RGB) { 101 | 102 | for (int i = 0; i < 12; i++) { 103 | 104 | update_led(i, RGB); 105 | } 106 | } 107 | 108 | void update_leds() { 109 | 110 | for (byte i = 0; i < 12; i++) { 111 | 112 | byte my_active_colour = localActiveLedColour[i]; 113 | byte my_inactive_colour = localInactiveLedColour[i]; 114 | byte my_lighting_mode = localLightingMode[i]; 115 | byte my_switch_mode = localSwitchMode[i]; 116 | byte my_state = switchActive[i]; 117 | 118 | // static 119 | if (my_switch_mode == 0) { 120 | 121 | if (my_lighting_mode == 1) update_led(i, my_active_colour); 122 | else update_led(i, globalLedColour); 123 | } 124 | 125 | // toggle, delay, hold 126 | else { 127 | 128 | // active 129 | if (my_state == 1) { 130 | 131 | if (my_lighting_mode == 1) update_led(i, my_active_colour); 132 | else update_led(i, globalLedColour); 133 | } 134 | 135 | // inactive 136 | else update_led(i, my_inactive_colour); 137 | } 138 | } 139 | } 140 | 141 | void update_bar(byte value) { 142 | 143 | int mask = (1 << value) - 1; 144 | 145 | grid_array[12] = mask & 0xFF; 146 | grid_array[13] = (mask >> 8) & 0xFF; 147 | } 148 | 149 | void setupDisplay(boolean active, byte intensity) 150 | { 151 | #ifdef ARDUINO_AVR_LEONARDO 152 | sendCommand(TM16XX_CMD_DISPLAY | (active ? 8 : 0) | min(7, intensity)); 153 | #elif ESP32 154 | sendCommand(TM16XX_CMD_DISPLAY | (active ? 8 : 0) | _min(7, intensity)); 155 | #endif 156 | } 157 | 158 | void LEDS_setup() { 159 | 160 | pinMode(dataPin, OUTPUT); 161 | pinMode(clockPin, OUTPUT); 162 | pinMode(strobePin, OUTPUT); 163 | 164 | digitalWrite(strobePin, HIGH); 165 | digitalWrite(clockPin, HIGH); 166 | 167 | setupDisplay(true, globalLedBrightness); 168 | update_leds(); 169 | } 170 | 171 | void LEDs_loop() { 172 | 173 | // update the brightness 174 | if (global_led_brightness_current != globalLedBrightness) { 175 | 176 | global_led_brightness_current = globalLedBrightness; 177 | setupDisplay(true, globalLedBrightness); 178 | } 179 | 180 | // refresh the LEDs 181 | update_leds(); 182 | } 183 | 184 | #endif -------------------------------------------------------------------------------- /src/sparkpad_obs.h: -------------------------------------------------------------------------------- 1 | #ifndef SP_OBS 2 | #define SP_OBS 3 | 4 | #include 5 | #include 6 | #include 7 | #include "SPIFFS.h" 8 | #include 9 | 10 | #include 11 | String ObsconfigPath = "/ObsSettings.json"; 12 | 13 | void getOBSConfig() { 14 | if(SPIFFS.exists(ObsconfigPath)){ 15 | StaticJsonDocument<200> doc; 16 | 17 | File config = SPIFFS.open(configPath, "r"); 18 | DeserializationError error = deserializeJson(doc, config); 19 | if (error){ 20 | Serial.println(F("Failed to read file")); 21 | } 22 | 23 | String obs_ip = doc["ip"]; 24 | String obs_port = doc["port"]; 25 | String obs_password = doc["pass"]; 26 | 27 | Serial.println(obs_ip); 28 | Serial.println(obs_port); 29 | Serial.println(obs_password); 30 | } else { 31 | Serial.println("obs is not configured, please configure first"); 32 | } 33 | } 34 | 35 | #endif -------------------------------------------------------------------------------- /src/sparkpad_oled.h: -------------------------------------------------------------------------------- 1 | #ifndef SP_OLED 2 | #define SP_OLED 3 | 4 | #include 5 | #include 6 | #include "SSD1306Ascii.h" 7 | #include "SSD1306AsciiWire.h" 8 | #include 9 | #include 10 | // #include 11 | 12 | #include "sparkpad_eeprom.h" 13 | 14 | using namespace Menu; 15 | 16 | SSD1306AsciiWire oled; 17 | 18 | // Should be 1 for bicolour OLEDs 19 | #define VERTICAL_OFFSET 0 20 | 21 | #define MAX_DEPTH 2 22 | 23 | #define FIRST_LINE 0 //text position for first line 24 | #define SECOND_LINE 1 //text position for second line 25 | 26 | //#define LARGE_FONT Verdana12 27 | 28 | /*Do not change the values(recomended)*/ 29 | #ifdef LARGE_FONT 30 | #define menuFont LARGE_FONT 31 | #define fontW 8 32 | #define fontH 16 33 | #else 34 | // #define menuFont System5x7 35 | #define menuFont lcd5x7 36 | #define fontW 5 37 | #define fontH 8 38 | #endif 39 | 40 | result saver() { 41 | 42 | EEPROM_save(); 43 | return proceed; 44 | } 45 | 46 | result loader() { 47 | 48 | EEPROM_load(); 49 | return proceed; 50 | } 51 | 52 | MENU(keyMenu,"Key settings",doNothing,noEvent,wrapStyle 53 | ,FIELD(selectedActiveLedColour,"Active colour","",0,7,1,0,saver,exitEvent,noStyle) 54 | ,FIELD(selectedInactiveLedColour,"Inactive colour","",0,7,1,0,saver,exitEvent,noStyle) 55 | ,FIELD(selectedLightingMode,"Lighting mode","",0,1,1,0,saver,exitEvent,noStyle) 56 | ,FIELD(selectedSwitchMode,"Switch mode","",0,3,1,0,saver,exitEvent,noStyle) 57 | ,FIELD(selectedDefaultToggleState,"Default state","",0,1,1,0,saver,exitEvent,noStyle) 58 | ,FIELD(selectedDelay,"Delay","s",0,255,1,0,saver,exitEvent,noStyle) 59 | ,EXIT(" strIn;//buffer size: 2^5 = 32 bytes, eventually use 0 for a single byte 86 | // serialIn in(Serial); 87 | chainStream<0> in(NULL); 88 | 89 | NAVROOT(nav,mainMenu,MAX_DEPTH,in,out); 90 | 91 | #endif -------------------------------------------------------------------------------- /src/sparkpad_wifi.h: -------------------------------------------------------------------------------- 1 | #ifndef SP_WIFI 2 | #define SP_WIFI 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "SPIFFS.h" 10 | #define FORMAT_SPIFFS_IF_FAILED true 11 | 12 | const char* ssid = "sparkpad-test"; 13 | const char* pass = "123"; 14 | 15 | IPAddress localIP(192,168,1,1); 16 | IPAddress gateway(192,168,1,1); 17 | IPAddress subnet(255,255,255,0); 18 | 19 | WebServer server(80); 20 | 21 | String configPath = "/WiFiSettings.json"; 22 | String obsConfig = "/ObsSettings.json"; 23 | 24 | String sendHtml() 25 | { 26 | String ptr = " \n"; 27 | ptr += "\n"; 28 | ptr += "LED Control\n"; 29 | ptr += "\n"; 38 | ptr += "\n"; 39 | ptr += "\n"; 40 | ptr += "

Sparkpad Web Server

\n"; 41 | ptr += "

Please enter your Wifi Network name and password

\n"; 42 | ptr += "
"; 43 | ptr += ""; 44 | ptr += ""; 45 | ptr += ""; 46 | ptr += "
"; 47 | ptr += "\n"; 48 | ptr += "\n"; 49 | return ptr; 50 | } 51 | 52 | String sendConfigHtml(String name, String pass) 53 | { 54 | String ptr = " \n"; 55 | ptr += "\n"; 56 | ptr += "Sparkpad Config\n"; 57 | ptr += "\n"; 66 | ptr += "\n"; 67 | ptr += "\n"; 68 | ptr += "

Sparkpad Config Edditor

\n"; 69 | ptr += "

Update your settings from here

\n"; 70 | ptr += "

Home

\n"; 71 | ptr += "
"; 72 | ptr += ""; 73 | ptr += ""; 74 | ptr += ""; 75 | ptr += "
"; 76 | ptr += "

"; 77 | ptr += "Reset esp32 config"; 78 | ptr += "\n"; 79 | ptr += "\n"; 80 | return ptr; 81 | } 82 | 83 | String sendObsConfigHtml(String ip, String port, String pass) 84 | { 85 | String ptr = " \n"; 86 | ptr += "\n"; 87 | ptr += "Sparkpad OBS Config\n"; 88 | ptr += "\n"; 97 | ptr += "\n"; 98 | ptr += "\n"; 99 | ptr += "

Sparkpad OBS Config Edditor

\n"; 100 | ptr += "

Update your OBS settings from here

\n"; 101 | ptr += "

Home

\n"; 102 | ptr += "
"; 103 | ptr += "
"; 104 | ptr += "
"; 105 | ptr += "
"; 106 | ptr += ""; 107 | ptr += "
"; 108 | ptr += "

"; 109 | ptr += "Reset Obs config"; 110 | ptr += "\n"; 111 | ptr += "\n"; 112 | return ptr; 113 | } 114 | 115 | String sendHomePage() 116 | { 117 | String ptr = " \n"; 118 | ptr += "\n"; 119 | ptr += "Sparkpad Config\n"; 120 | ptr += "\n"; 129 | ptr += "\n"; 130 | ptr += "\n"; 131 | ptr += "

Sparkpad

\n"; 132 | ptr += "

Select a place to go below

\n"; 133 | ptr += "

"; 134 | ptr += "Reset config "; 135 | ptr += "Edit config"; 136 | ptr += "Configure Obs"; 137 | ptr += "\n"; 138 | ptr += "\n"; 139 | return ptr; 140 | } 141 | 142 | void handleGet() 143 | { 144 | server.send(200, "text/plain", "Thanks, please bear with us"); 145 | // store the name and pass in strings to be used 146 | String name = server.arg(0); 147 | String pass = server.arg(1); 148 | 149 | DynamicJsonDocument doc(150); 150 | File configFile = SPIFFS.open(configPath, "w"); 151 | 152 | doc["name"] = name; 153 | doc["pass"] = pass; 154 | 155 | serializeJson(doc, configFile); 156 | serializeJson(doc, Serial); 157 | configFile.close(); 158 | Serial.println("\nSaved Config settings... Rebooting in 3 seconds"); 159 | delay(3000); 160 | ESP.restart(); 161 | } 162 | 163 | void handleNotFound() 164 | { 165 | server.send(404, "text/plain", "You are in the wrong place buddy"); 166 | } 167 | 168 | void handleOnConnect() 169 | { 170 | server.send(200, "text/html", sendHtml()); 171 | Serial.println("Client Connected"); 172 | } 173 | 174 | void handleOnConnectSSID() 175 | { 176 | server.send(200, "text/html", sendHomePage()); 177 | } 178 | 179 | void handleReset() 180 | { 181 | server.send(200, "text/plain", "Resetting Config, please bear with us."); 182 | Serial.println("removing config....."); 183 | SPIFFS.remove(configPath); 184 | Serial.println("Config removed rebooting"); 185 | delay(3000); 186 | ESP.restart(); 187 | } 188 | 189 | void handleconfigGet() 190 | { 191 | File configFile = SPIFFS.open(configPath, "r"); 192 | 193 | StaticJsonDocument<150> doc; 194 | 195 | DeserializationError error = deserializeJson(doc, configFile); 196 | if (error) 197 | Serial.println(F("Failed to read file, using default configuration")); 198 | 199 | String name = doc["name"]; 200 | String pass = doc["pass"]; 201 | 202 | server.send(200, "text/html", sendConfigHtml(name, pass)); 203 | } 204 | 205 | void handleConfigSet() 206 | { 207 | SPIFFS.remove(configPath); 208 | 209 | String name = server.arg(0); 210 | String pass = server.arg(1); 211 | 212 | DynamicJsonDocument doc(150); 213 | File configFile = SPIFFS.open(configPath, "w"); 214 | 215 | doc["name"] = name; 216 | doc["pass"] = pass; 217 | 218 | serializeJson(doc, configFile); 219 | serializeJson(doc, Serial); 220 | configFile.close(); 221 | Serial.println("\nSaved Config settings... Rebooting in 3 seconds"); 222 | delay(3000); 223 | ESP.restart(); 224 | } 225 | 226 | void handleobsConfigGet() 227 | { 228 | if(SPIFFS.exists(obsConfig)) 229 | { 230 | StaticJsonDocument<500> doc; 231 | 232 | File config = SPIFFS.open(obsConfig, FILE_READ); 233 | DeserializationError error = deserializeJson(doc, config); 234 | if (error){ 235 | Serial.println(F("Failed to read file")); 236 | Serial.println(error.c_str()); 237 | } 238 | 239 | String ip = doc["ip"]; 240 | String port = doc["port"]; 241 | String password = doc["pass"]; 242 | config.close(); 243 | server.send(200, "text/html", sendObsConfigHtml(ip, port, password)); 244 | } else { 245 | String ip = "Ip of computer obs is running on."; 246 | String port = "4444"; 247 | String password = ""; 248 | server.send(200, "text/html", sendObsConfigHtml(ip, port, password)); 249 | } 250 | } 251 | 252 | void handleobsConfigSet() 253 | { 254 | server.send(200, "text/plain", "Obs config saved rebooting..."); 255 | String ip = server.arg(0); 256 | String port = server.arg(1); 257 | String pass = server.arg(2); 258 | 259 | StaticJsonDocument<500> doc; 260 | File configFile = SPIFFS.open(obsConfig, FILE_WRITE); 261 | 262 | doc["ip"] = ip; 263 | doc["port"] = port; 264 | doc["pass"] = pass; 265 | 266 | serializeJson(doc, configFile); 267 | serializeJson(doc, Serial); 268 | configFile.close(); 269 | Serial.println("\nSaved Config settings... Rebooting in 3 seconds"); 270 | delay(3000); 271 | ESP.restart(); 272 | } 273 | 274 | void handleobsConfigReset() 275 | { 276 | server.send(200, "text/plain", "Resetting Obs Config, please bear with us."); 277 | Serial.println("removing config....."); 278 | SPIFFS.remove(obsConfig); 279 | Serial.println("Config removed rebooting"); 280 | delay(3000); 281 | ESP.restart(); 282 | } 283 | 284 | bool readConfig(String fileName) 285 | { 286 | if(SPIFFS.exists(fileName)) { 287 | File file = SPIFFS.open(fileName, "r"); 288 | DynamicJsonDocument doc(150); 289 | deserializeJson(doc, file); 290 | serializeJson(doc, Serial); 291 | file.close(); 292 | return true; 293 | } else { 294 | Serial.println("File not Found"); 295 | return false; 296 | } 297 | } 298 | 299 | void setupEndpoints(String type) 300 | { 301 | if (type == "AP") 302 | { 303 | server.on("/", handleOnConnect); 304 | server.on("/store", handleGet); 305 | server.onNotFound(handleNotFound); 306 | } 307 | else if (type == "ST") 308 | { 309 | server.on("/", handleOnConnectSSID); 310 | server.on("/reset", handleReset); 311 | server.on("/config", handleconfigGet); 312 | server.on("/configSet", handleConfigSet); 313 | server.on("/obsConfig", handleobsConfigGet); 314 | server.on("/obsConfigSet", handleobsConfigSet); 315 | server.on("/resetObs", handleobsConfigReset); 316 | server.onNotFound(handleNotFound); 317 | } 318 | } 319 | 320 | void bootAP(){ 321 | // WIFI 322 | Serial.println("Starting Wifi Server (AP)"); 323 | WiFi.softAP(ssid); 324 | delay(100); 325 | 326 | IPAddress ip = WiFi.softAPIP(); 327 | Serial.print("AP IP Address: "); 328 | Serial.println(ip); 329 | 330 | setupEndpoints("AP"); 331 | 332 | server.begin(); 333 | Serial.println("Server started!"); 334 | } 335 | 336 | void bootSSID(){ 337 | Serial.println("Connecting to WiFi using credentials from settings"); 338 | 339 | File configFile = SPIFFS.open(configPath, "r"); 340 | 341 | StaticJsonDocument<150> doc; 342 | 343 | DeserializationError error = deserializeJson(doc, configFile); 344 | if (error) 345 | Serial.println(F("Failed to read file, using default configuration")); 346 | 347 | const char* name = doc["name"]; 348 | const char* pass = doc["pass"]; 349 | 350 | WiFi.begin(name, pass); 351 | 352 | int i = 0; 353 | 354 | while (WiFi.status() != WL_CONNECTED) { 355 | delay(1000); 356 | Serial.println("Establishing connection to WiFi.."); 357 | i++; 358 | if(i > 12) { 359 | Serial.println("Cannot connect to network try again!"); 360 | SPIFFS.remove(configPath); 361 | ESP.restart(); 362 | } 363 | } 364 | 365 | Serial.println("Connected to network"); 366 | 367 | Serial.println(WiFi.macAddress()); 368 | Serial.println(WiFi.localIP()); 369 | 370 | setupEndpoints("ST"); 371 | 372 | server.begin(); 373 | Serial.println("Server started!"); 374 | } 375 | 376 | void WIFI_setup() { 377 | 378 | // SPIFFS 379 | Serial.println("Starting Spiffs"); 380 | if(!SPIFFS.begin(true)) { 381 | Serial.println("Error mounting filesystem"); 382 | return; 383 | } 384 | 385 | //SPIFFS.remove(configPath); 386 | 387 | if(readConfig(configPath)) { 388 | bootSSID(); 389 | } else { 390 | bootAP(); 391 | } 392 | } 393 | 394 | void WIFI_loop() { 395 | 396 | server.handleClient(); 397 | 398 | // delay(10000); 399 | // Serial.println("WiFi Status: " + WiFi.status()); 400 | } 401 | 402 | #endif -------------------------------------------------------------------------------- /src/test: -------------------------------------------------------------------------------- 1 | File configFile = SPIFFS.open(configPath, "w"); 2 | doc["ip"] = ""; 3 | doc["port"] = ""; 4 | doc["password"] = ""; 5 | serializeJson(doc, configFile); 6 | serializeJson(doc, Serial); 7 | configFile.close(); 8 | Serial.println("File created, rebooting"); 9 | delay(3000); 10 | ESP.restart(); --------------------------------------------------------------------------------