├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── README.md ├── case └── Sonos Button Case.stl ├── images ├── all.jpg ├── breakout-back.jpg ├── breakout-front.jpg ├── case-bottom.jpg └── case-top.jpg ├── main ├── CMakeLists.txt ├── component.mk ├── config.h ├── sonos.cpp ├── sonos.h ├── sonos_buttons.cpp └── ulp │ └── button_wakeup.S └── sdkconfig /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | .*.swp 35 | build/ 36 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "components/arduino"] 2 | path = components/arduino 3 | url = https://github.com/espressif/arduino-esp32.git 4 | -------------------------------------------------------------------------------- /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.5) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(sonos-buttons) 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Matt Sullivan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # This is a project Makefile. It is assumed the directory this Makefile resides in is a 3 | # project subdirectory. 4 | # 5 | CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_DEBUG -DF_CPU=240000000L -DARDUINO_ESP32_THING -DARDUINO_ARCH_ESP32 -DARDUINO_BOARD=\"ESP32_THING\" -DARDUINO_VARIANT=\"esp32thing\" -DESP32 6 | PROJECT_NAME := sonos-buttons 7 | 8 | include $(IDF_PATH)/make/project.mk 9 | 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | This is a hobby project for controlling a sonos player using an ESP32 board and a button pad. I got the idea for the 3 | project while working in my garage and having gloves on or dirty hands and not wanting to mess with my phone to control the 4 | sonos player in there. Obviously the lazy thing would have been to get a voice assistant in there, but what fun is that? 5 | 6 | I wanted something that I could place anywhere in the garage. Being able to run from battery (low power) and WiFi were 7 | important considerations. I was clearly looking for a fun project here, otherwise I probably would have gone with some 8 | AWS button concoction, or perhaps gone with one of the commercially available sonos remote control options. 9 | 10 | So I wound up going with a handful of stuff from Sparkfun: 11 | - [ESP32 Thing](https://www.sparkfun.com/products/13907) 12 | - [2x2 Button Pad](https://www.sparkfun.com/products/9277) 13 | - [Buttons](https://www.sparkfun.com/products/7836) 14 | - [Buttons Bezel](https://www.sparkfun.com/products/8746) 15 | - [Diodes](https://www.sparkfun.com/products/8588) 16 | 17 | The ESP32 fit the bill for the project since it has a ridiculously low power draw when it's in sleep, has WiFi 18 | built in and the sparkfun board comes with a LIPO battery charger on it. 19 | 20 | ## Software 21 | I had initially used the Sonos public control API, which goes through Sonos' servers to control your local sonos 22 | player. After getting this all working I found the latency period to be less than ideal. Sonos also 23 | had a multi day service outage while I was wrapping up work on that version. I settled on using the local network UPnP based 24 | API that the official sonos applications use. Much of the detail on this came from [SoCo](https://github.com/SoCo/SoCo), so 25 | thanks for that! 26 | 27 | Since my hardware design has four buttons, the software supports four opertations: 28 | - Play/Pause 29 | - Next track 30 | - Volume up 31 | - Volume Down 32 | 33 | If you want to use this project yourself, you'll need to first find the UID of the sonos player that you want to control. 34 | For this I recommend using the [SoCo](https://github.com/SoCo/SoCo) library. Once you've got it installed locally: 35 | 36 | ``` 37 | >>> import soco 38 | >>> [zone.uid for zone in soco.discover() if zone.player_name == 'Garage'] 39 | ['RINCON_XXXXXXXXXXXX'] 40 | ``` 41 | 42 | Set the SONOS_UID constant in `sonos_buttons.cpp` to the sonos player UID you want to control and while you're there set the 43 | SSID and PASSWORD macros to your WiFi credentials.. 44 | 45 | ### Building 46 | 47 | You'll need an ESP32 development environment setup: see [these instructions](https://docs.espressif.com/projects/esp-idf/en/v3.2.3/get-started/index.html) and you'll need the [ULP Toolchain](https://docs.espressif.com/projects/esp-idf/en/v3.2.3/api-guides/ulp.html#installing-the-toolchain) too. 48 | Because the esp-arduino module currently requires the v3.2 branch of esp-IDF, you'll need to stick with that version for compiling. 49 | It looks like the 4.0 branch completely changed how builds work so this project would likely need some updates too. 50 | 51 | Once you have the environment setup, run `make menuconfig` and set the default serial port your ESP32 board appears as. This is under the "Serial flasher config" menu item. There's a ton of other options, but that's the only one you need to change. 52 | 53 | To flash run `make flash` and then to see serial output run `make monitor`. This will build all of the FreeRTOS stuff too, which is a lot, so make's `-j` argument could be helpful here to use multiple processors. 54 | 55 | ### Implementation 56 | 57 | Since power is a concern here, I wanted to make use of the deep sleep feature of the ESP32 SOC. This allows it to go into a 58 | state that draws *micro*amps of current while still being able to respond to external stimuli. See [the ESP32 sleep docs](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/system/sleep_modes.html) for details on that. 59 | 60 | After waking up from a deep sleep the ESP32 has to rejoin wifi, which usually takes a couple of seconds, so one of the hardest things 61 | was to get it to remember what button was pressed to wake it up from deep sleep. I didn't have a lot of luck with the built-in 62 | touchpad, or GPIO (ext1) based wakeup solutions. The ext1 stuff kept going into sleep/wake loops, and the touchpad wakeups 63 | wouldn't report the correct GPIO input for the wakeup source. 64 | 65 | The ESP32 supports wakeup events from a Ultra Low Power (ULP) processor, which is a minimal extra processor that can run some limited code and can wakeup the main processor while 66 | sharing memory with it. When this ULP processor is running and the main processor is in deep sleep, the board supposedly draws current in the 100 microamp 67 | range, so it should still last a very long time on a 1000mah battery. An important point is that during deep sleep, most of main 68 | memory is powered off. When it wakes up it is effectively like a reboot but the so-called RTC memory is still active. The 69 | main program can store stuff in this memory (which is 16K I think) and the ULP processor can use an 8K subset of this 70 | which is shared with the main program. 71 | There isn't a C compiler for the ULP processor, so that part has to be written in assembly with a rather limited 72 | [instruction set](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/ulp_instruction_set.html). The ULP program 73 | lifecycle is a little strange. It's intended to run periodically, woken up by a configurable timer internal to the SOC. In my 74 | case I have it running every 100 milliseconds and it will check to see if any buttons are pressed. If it detects a button press 75 | it will write the button that was pressed into the shared RTC memory and wakeup the main processor(s). If not, it will halt and be restarted by the timer. 76 | 77 | On boot the main application does the following: 78 | - Initialize the GPIO pins for controlling the button LEDs and button readers 79 | - Start a FreeRTOS task for controlling the LEDs based on a global variable. This way it can handle keeping the LEDS operational while the CPU is blocked in IO. How cool is it that a tiny computer like this supports real multitasking? 80 | - If we're not waking from sleep, do a cute blinky dance to show off 81 | - If we are waking from sleep, check if the ULP program stashed a button press to be acted on in the first button input loop 82 | - Join wifi and if that fails, do some angry blinking. 83 | - Look in the [Preferences](https://github.com/espressif/arduino-esp32/tree/master/libraries/Preferences) stored on the SOC's flash for the Sonos player's IP address 84 | - If we don't know the IP address, perform Sonos discovery to find it 85 | - Enter a loop to check for button inputs. 86 | - If a button input occurs, light up the button LED for the duration of the association operation for user feedback and perform that operation. 87 | - After roughly 30 seconds of no button presses (didn't want to introduce a clock, so just based on loop counting hueristics), prepare for deep sleep 88 | 89 | Deep Sleep preparation entails: 90 | - Turn off wifi 91 | - Setup RTC IO for the button GPIO inputs and outputs used for the buttons. 92 | - Configure wakeup from ULP sources 93 | - Set the ULP processor timer to run the ULP program 100ms after it halts 94 | - Start the ULP Program 95 | - Enter deep sleep 96 | 97 | The ULP Program uses essentially the same mechanism as the main processor code for detecting button proesses so the following mostly applies to both. There is one pin for each button plus a single "reader" pin, details of which are described in the hardware section. 98 | - Configure the reader pin for input only, with the pullup resistor enabled. 99 | - Set the four button pins to HIGH 100 | - Loop over each button pin 101 | - Set the button pin to LOW 102 | - Wait a short time 103 | - Read from the reader pin 104 | - If the reader pin reads as LOW, then we increment a bounce counter for the button pin, if it's HIGH then we set the bounce counter to 0 105 | - Set the button pin back to HIGH 106 | - If the bounce counter for a button reaches 3, consider it a button press. 107 | - If a button is pressed: 108 | - Write the pressed button into a variable in memory shared between the ULP and the main processors. 109 | - Wakeup the main processor 110 | - Disable the ULP wakeup timer so it doesn't keep running with the main processor is running 111 | - Halt the ULP program 112 | 113 | ## Hardware 114 | 115 | The hardware bits involved here beyond the ESP32 board are a 2x2 button breakout board from sparkfun, along with the associated 116 | silicon buttons and some little plastic bits to hold everything together. The breakout board has places for full RGB LEDs to 117 | go under each button but I went with simple blue LEDS. These LEDS are meant to provide feedback when the button is pressed. 118 | After the button press is registered, the LED is lit until the operation associated with it is completed. 119 | These LEDS are also used all together during startup with a brief flash to indicate successfully joining the wifi network, or 120 | a series of angry flashes if joining the wifi network failed. 121 | 122 | The button breakout board also has holes for diodes to prevent signals bleeding between rows when doing matrix scanning of the 123 | button array. I'm not great with electronics stuff, but I don't think this is neccessary with a 2x2 grid where each button has it's own input anyway and is only needed for 4x4 grids. I dutifully soldered them on anyway. 124 | 125 | The button side of things is wired up with the switch ground terminals on the breakout board, wired to pins 12, 14, 27 and 26. 126 | The reader pin on the buttons is wired to the switch terminal on the breakout board and GPIO 33 on the ESP32. 127 | You can change this around in the code, but for the ULP bit to work the GPIO pins for the buttons (not LEDS) MUST be 128 | available from the RTC controller. See section 4.11, RTC_MUX Pin List, on page 57 of the [ESP 32 Technical Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf). 129 | 130 | The LED side of things is wired up to GPIO pins 25, 4, 5, and 18 in the same order with the buttons listed above. These are wired 131 | to the LED GND terminals on the breakout baord. GPIO 32 is used to power these in the `ledLoop()` function and is wired to the 132 | Blue LED terminal on the breakout board. The LED pins were chosen to also be compatible with the Sparkfun WROOM dev board as well. 133 | 134 | ## Results 135 | 136 | I eventually wound up rebuilding this with the [SparkFun Thing Plus](https://www.sparkfun.com/products/15663) and designed a 3d printed case for it. 137 | The case design is in `case/Sonos Button Case.stl`. It's designed to fit the Thing Plus and SparkFun's 2000mah LIPO battery and has places for M2.5 nuts and hex standoffs to hold everything together. 138 | After using this for a while, I can report that it works pretty well. The battery life seems to be about three to four weeks and it is indeed useful when working on projects in the garage. 139 | 140 | There are a few issues. Sometimes it fails to join the wifi, or takes a while to do so. Based on experiences with other projects, I think this might be remedied 141 | by upgrading to the latest ESP IDF, but that will require re-writing it to use the standard IDF APIs rather than the arduino wrapper. 142 | 143 | ## Pictures 144 | 145 | ### Rebuild with case 146 | ![](images/case-top.jpg) 147 | ![](images/case-bottom.jpg) 148 | 149 | ### Full Project 150 | ![](images/all.jpg) 151 | 152 | ### Breakout board front 153 | ![](images/breakout-front.jpg) 154 | 155 | ### Breakout board back 156 | ![](images/breakout-back.jpg) 157 | 158 | -------------------------------------------------------------------------------- /case/Sonos Button Case.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piis3/sonos-buttons/dd1bd13b0f16436da3f75e0207f17038c0ed243e/case/Sonos Button Case.stl -------------------------------------------------------------------------------- /images/all.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piis3/sonos-buttons/dd1bd13b0f16436da3f75e0207f17038c0ed243e/images/all.jpg -------------------------------------------------------------------------------- /images/breakout-back.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piis3/sonos-buttons/dd1bd13b0f16436da3f75e0207f17038c0ed243e/images/breakout-back.jpg -------------------------------------------------------------------------------- /images/breakout-front.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piis3/sonos-buttons/dd1bd13b0f16436da3f75e0207f17038c0ed243e/images/breakout-front.jpg -------------------------------------------------------------------------------- /images/case-bottom.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piis3/sonos-buttons/dd1bd13b0f16436da3f75e0207f17038c0ed243e/images/case-bottom.jpg -------------------------------------------------------------------------------- /images/case-top.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piis3/sonos-buttons/dd1bd13b0f16436da3f75e0207f17038c0ed243e/images/case-top.jpg -------------------------------------------------------------------------------- /main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(COMPONENT_SRCS "sonos_buttons.cpp") 2 | set(COMPONENT_ADD_INCLUDEDIRS "") 3 | 4 | register_component() 5 | -------------------------------------------------------------------------------- /main/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # "main" pseudo-component makefile. 3 | # 4 | # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) 5 | 6 | ULP_APP_NAME ?= ulp_$(COMPONENT_NAME) 7 | ULP_S_SOURCES = $(COMPONENT_PATH)/ulp/button_wakeup.S 8 | ULP_EXP_DEP_OBJECTS := sonos_buttons.o 9 | include $(IDF_PATH)/components/ulp/component_ulp_common.mk 10 | -------------------------------------------------------------------------------- /main/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Put your wifi and sonos config here 3 | */ 4 | 5 | #define SONOS_UID "YOUR_SONOS_ID" 6 | #define SSID "YOUR_SSID" 7 | #define PASSWORD "YOUR_PASSWORD" 8 | 9 | -------------------------------------------------------------------------------- /main/sonos.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "sonos.h" 9 | 10 | static const char* PLAYER_SEARCH = "M-SEARCH * HTTP/1.1\r\n" 11 | "HOST: 239.255.255.250:1900\r\n" 12 | "MAN: \"ssdp:discover\"\r\n" 13 | "MX: 1\r\n" 14 | "ST: urn:schemas-upnp-org:device:ZonePlayer:1\r\n"; 15 | 16 | static std::string soapCall(std::string operation) { 17 | return "" 18 | "" 19 | "" 20 | "" 21 | "0" 22 | "1" 23 | "" 24 | "" 25 | ""; 26 | } 27 | 28 | static const std::string GET_VOLUME_CALL = "" 29 | "" 30 | "" 31 | "" 32 | "0" 33 | "Master" 34 | "" 35 | "" 36 | ""; 37 | 38 | static std::string changeVolumeCall(int volume) { 39 | return "" 40 | "" 41 | "" 42 | "" 43 | "0" 44 | "Master" 45 | "" + std::string(String(volume).c_str()) + "" 46 | "" 47 | "" 48 | ""; 49 | } 50 | 51 | std::string tagValue(std::string xmlData, std::string tagName) { 52 | 53 | typedef struct state { 54 | std::string captured; 55 | boolean capturing; 56 | std::string tagName; 57 | } ParseState; 58 | 59 | std::string captured; 60 | 61 | ParseState state = { 62 | captured, 63 | false, 64 | tagName 65 | }; 66 | 67 | XML_StartElementHandler start = [](void *myState, const char *el, const char **attr) { 68 | ParseState *d = (ParseState*) myState; 69 | if (strncmp(el, d->tagName.c_str(), d->tagName.length()) == 0) { 70 | d->capturing = true; 71 | } 72 | }; 73 | XML_EndElementHandler end = [](void *myState, const char *el) { 74 | ParseState *d = (ParseState*) myState; 75 | d->capturing = false; 76 | }; 77 | XML_CharacterDataHandler charData = [](void *myState, const char *s, int len) { 78 | ParseState *d = (ParseState*) myState; 79 | if (d->capturing) { 80 | d->captured += std::string(s, len); 81 | } 82 | }; 83 | XML_Parser p = XML_ParserCreate(NULL); 84 | if (! p) { 85 | Serial.println("Couldn't allocate parser"); 86 | } else { 87 | XML_SetUserData(p, &state); 88 | XML_SetElementHandler(p, start, end); 89 | XML_SetCharacterDataHandler(p, charData); 90 | XML_Parse(p, xmlData.c_str(), xmlData.length(), true); 91 | XML_ParserFree(p); 92 | } 93 | return state.captured; 94 | 95 | } 96 | 97 | // Find the Location attribute for the player specified by uid 98 | std::string filterDeviceLocation(std::string xmlData, std::string targetUid) { 99 | typedef struct state { 100 | std::string location; 101 | std::string targetUid; 102 | } ParseState; 103 | 104 | std::string location; 105 | 106 | ParseState state = { 107 | location, 108 | targetUid 109 | }; 110 | 111 | XML_StartElementHandler start = [](void *myState, const char *el, const char **attr) { 112 | ParseState *state = (ParseState *) myState; 113 | // 15 is ZoneGroupMember length 114 | if (strncmp(el, "ZoneGroupMember", 15) == 0) { 115 | std::string loc; 116 | std::string uid; 117 | 118 | for (int i = 0;; i += 2) { 119 | const char *key = attr[i]; 120 | if (key == NULL) { 121 | break; 122 | } 123 | const char *value = attr[i + 1]; 124 | if (value == NULL) { 125 | break; 126 | } 127 | 128 | if (strncmp(key, "UUID", 4) == 0) { 129 | uid = std::string(value); 130 | } else if (strncmp(key, "Location", 8) == 0) { 131 | loc = std::string(value); 132 | } 133 | } 134 | 135 | if (loc.length() > 0 && (uid == state->targetUid)) { 136 | state->location += loc; 137 | } 138 | } 139 | }; 140 | 141 | XML_Parser p = XML_ParserCreate(NULL); 142 | if (! p) { 143 | Serial.println("Couldn't allocate parser"); 144 | } else { 145 | XML_SetUserData(p, &state); 146 | XML_SetElementHandler(p, start, NULL); 147 | XML_Parse(p, xmlData.c_str(), xmlData.length(), true); 148 | XML_ParserFree(p); 149 | } 150 | 151 | return state.location; 152 | } 153 | 154 | 155 | /** 156 | * Given any sonos' address, ask it for the zone topology to find the right sonos 157 | */ 158 | IPAddress zoneTopology(HTTPClient *http, std::string host, std::string targetUid) { 159 | auto postBody = soapCall("GetZoneGroupState"); 160 | 161 | IPAddress ipaddr; 162 | 163 | if (http->begin(host.c_str(), SONOS_PORT, "/ZoneGroupTopology/Control")) { 164 | http->addHeader("Content-Type", "text/xml; charset=\"utf-8\""); 165 | http->addHeader("SOAPACTION", "urn:schemas-upnp-org:service:ZoneGroupTopology:1#GetZoneGroupState"); 166 | int httpCode = http->POST(postBody.c_str()); 167 | if (httpCode == 200) { 168 | // The body here is an XML doc embedded in the body of another, so just pull out the first one, then run it through the next parser 169 | std::string innerXml = tagValue(std::string(http->getString().c_str()), "ZoneGroupState"); 170 | std::string locationUrl = filterDeviceLocation(innerXml, targetUid); 171 | if (locationUrl.length() > 0 && locationUrl.length() > 7) { 172 | Serial.printf("Found location: %s\n", locationUrl.c_str()); 173 | 174 | int pos = locationUrl.find(":", 7); 175 | if (pos > 0) { 176 | ipaddr.fromString(locationUrl.substr(7, pos - 7).c_str()); 177 | } 178 | } 179 | if (!ipaddr) { 180 | Serial.println("Couldn't find location descriptor for sonos"); 181 | } 182 | } else { 183 | Serial.printf("Got bad status code getting zone topology %d: %s\n", httpCode, http->getString().c_str()); 184 | } 185 | } 186 | return ipaddr; 187 | } 188 | 189 | IPAddress discoverSonos(std::string uid) { 190 | 191 | AsyncUDP udp; 192 | IPAddress foundAddr; 193 | IPAddress targetSonos; 194 | if (udp.listenMulticast(IPAddress(239, 255, 255, 250), 1900)) { 195 | Serial.println("UDP connected"); 196 | udp.onPacket([&foundAddr](AsyncUDPPacket packet) { 197 | if (!foundAddr) { 198 | auto s = std::string((char*) packet.data()); 199 | // All we care about here is finding a sonos, any sonos. 200 | if (s.find("Sonos") > 0) { 201 | foundAddr = packet.remoteIP(); 202 | } 203 | } else { 204 | Serial.printf("Got duplicate announcement from %s\n", packet.remoteIP().toString().c_str()); 205 | } 206 | }); 207 | for (uint8_t i = 0; i < 4 && !foundAddr; i++) { 208 | udp.broadcast(PLAYER_SEARCH); 209 | delay(250); 210 | } 211 | if (foundAddr) { 212 | Serial.printf("Found a sonos address %s\n", foundAddr.toString().c_str()); 213 | 214 | // Now we need to ask whatever sonos we found about the topology to find what we care about 215 | HTTPClient http; 216 | http.setConnectTimeout(HTTP_TIMEOUT); 217 | http.setTimeout(HTTP_TIMEOUT); 218 | 219 | IPAddress ourSonos = zoneTopology(&http, std::string(foundAddr.toString().c_str()), uid); 220 | if (ourSonos) { 221 | Serial.printf("FOUND OUR SONOS at %s\n", ourSonos.toString().c_str()); 222 | targetSonos = ourSonos; 223 | // Save our findings in flash across boots 224 | Preferences prefs; 225 | prefs.begin("sonos"); 226 | prefs.putString("playerAddress", targetSonos.toString()); 227 | prefs.putString("playerUid", String(uid.c_str())); 228 | prefs.end(); 229 | } 230 | http.end(); 231 | } else { 232 | Serial.println("Nope, didn't find anything"); 233 | } 234 | udp.close(); 235 | } 236 | return targetSonos; 237 | } 238 | 239 | int sonosOperation(int (*operation)(HTTPClient *http, IPAddress target), IPAddress targetSonos) { 240 | HTTPClient http; 241 | http.setReuse(false); 242 | http.setConnectTimeout(HTTP_TIMEOUT); 243 | http.setTimeout(HTTP_TIMEOUT); 244 | 245 | int errorCode = operation(&http, targetSonos); 246 | if (errorCode) { 247 | // I think we just care about noticing that we might have to rediscover the sonos but punt for now 248 | Serial.printf("Got error from sonos operation %d\n", errorCode); 249 | } 250 | http.end(); 251 | return errorCode; 252 | } 253 | 254 | std::string playState(HTTPClient *http, IPAddress targetSonos) { 255 | auto postBody = soapCall("GetTransportInfo"); 256 | 257 | if (http->begin(targetSonos.toString(), SONOS_PORT, "/MediaRenderer/AVTransport/Control")) { 258 | Serial.printf("POST: BODY %s\n", postBody.c_str()); 259 | http->addHeader("Content-Type", "text/xml; charset=\"utf-8\""); 260 | http->addHeader("SOAPACTION", "urn:schemas-upnp-org:service:AVTransport:1#GetTransportInfo"); 261 | 262 | int httpCode = http->POST(postBody.c_str()); 263 | if (httpCode == 200) { 264 | /* We're going to get back a soap response like this: 265 | 266 | 267 | 268 | 269 | PLAYING 270 | OK 271 | 1 272 | 273 | 274 | 275 | */ 276 | return tagValue(std::string(http->getString().c_str()), "CurrentTransportState"); 277 | } else { 278 | Serial.printf("Got http error code %d body: %s\n", httpCode, http->getString().c_str()); 279 | } 280 | } else { 281 | Serial.printf("Couldn't connect to %s\n", targetSonos.toString().c_str()); 282 | } 283 | return ""; 284 | } 285 | 286 | int sonosPlay(HTTPClient *http, IPAddress targetSonos) { 287 | std::string currentState = playState(http, targetSonos); 288 | Serial.printf("Current play state is %s\n", currentState.c_str()); 289 | 290 | std::string requestState = currentState == "PLAYING" ? "Pause" : "Play"; 291 | auto postBody = soapCall(requestState); 292 | 293 | if (http->begin(targetSonos.toString(), SONOS_PORT, "/MediaRenderer/AVTransport/Control")) { 294 | Serial.printf("POST: BODY %s\n", postBody.c_str()); 295 | http->addHeader("Content-Type", "text/xml; charset=\"utf-8\""); 296 | http->addHeader("SOAPACTION", ("urn:schemas-upnp-org:service:AVTransport:1#" + requestState).c_str()); 297 | 298 | int httpCode = http->POST(postBody.c_str()); 299 | if (httpCode != 200) { 300 | Serial.printf("Got bad status code from sonos play operation %d\n", httpCode); 301 | Serial.printf("BODY: %s\n", http->getString().c_str()); 302 | return httpCode; 303 | } else { 304 | return 0; 305 | } 306 | } else { 307 | Serial.println("Couldn't connect to sonos, maybe need to re-discover"); 308 | return ENO_CANTCONNECT; 309 | } 310 | } 311 | 312 | int sonosNext(HTTPClient *http, IPAddress targetSonos) { 313 | auto postBody = soapCall("Next"); 314 | 315 | if (http->begin(targetSonos.toString(), SONOS_PORT, "/MediaRenderer/AVTransport/Control")) { 316 | Serial.printf("POST: BODY %s\n", postBody.c_str()); 317 | http->addHeader("Content-Type", "text/xml; charset=\"utf-8\""); 318 | http->addHeader("SOAPACTION", "urn:schemas-upnp-org:service:AVTransport:1#Next"); 319 | 320 | int httpCode = http->POST(postBody.c_str()); 321 | if (httpCode != 200) { 322 | Serial.printf("Got bad status code from sonos next operation %d\n", httpCode); 323 | Serial.printf("BODY: %s\n", http->getString().c_str()); 324 | return httpCode; 325 | } else { 326 | return 0; 327 | } 328 | } else { 329 | Serial.println("Couldn't connect to sonos, maybe need to re-discover"); 330 | return ENO_CANTCONNECT; 331 | } 332 | 333 | } 334 | 335 | int getVolume(HTTPClient *http, IPAddress targetSonos) { 336 | if (http->begin(targetSonos.toString(), SONOS_PORT, "/MediaRenderer/RenderingControl/Control")) { 337 | http->addHeader("Content-Type", "text/xml; charset=\"utf-8\""); 338 | http->addHeader("SOAPACTION", "urn:schemas-upnp-org:service:RenderingControl:1#GetVolume"); 339 | 340 | int httpCode = http->POST(GET_VOLUME_CALL.c_str()); 341 | if (httpCode != 200) { 342 | Serial.printf("Got bad status code from sonos next operation %d\n", httpCode); 343 | Serial.printf("BODY: %s\n", http->getString().c_str()); 344 | return -1; 345 | } else { 346 | auto volStr = tagValue(std::string(http->getString().c_str()), "CurrentVolume"); 347 | return String(volStr.c_str()).toInt(); 348 | } 349 | 350 | } else { 351 | Serial.println("Couldn't connect to sonos, maybe need to re-discover"); 352 | return -1; 353 | } 354 | } 355 | 356 | int changeVolume(HTTPClient *http, IPAddress targetSonos, int amount) { 357 | int currentVolume = getVolume(http, targetSonos); 358 | if (currentVolume < 0) { 359 | Serial.println("Couldn't get the current volume"); 360 | return ENO_CANTCONNECT; 361 | } 362 | 363 | int nextVolume = currentVolume + amount; 364 | if (nextVolume < 0) { 365 | nextVolume = 0; 366 | } else if (nextVolume > 100) { 367 | nextVolume = 100; 368 | } 369 | 370 | if (http->begin(targetSonos.toString(), SONOS_PORT, "/MediaRenderer/RenderingControl/Control")) { 371 | auto call = changeVolumeCall(nextVolume); 372 | Serial.printf("POST: BODY %s\n", call.c_str()); 373 | http->addHeader("Content-Type", "text/xml; charset=\"utf-8\""); 374 | http->addHeader("SOAPACTION", "urn:schemas-upnp-org:service:RenderingControl:1#SetVolume"); 375 | 376 | int httpCode = http->POST(call.c_str()); 377 | if (httpCode != 200) { 378 | Serial.printf("Got bad status code from sonos next operation %d\n", httpCode); 379 | Serial.printf("BODY: %s\n", http->getString().c_str()); 380 | return httpCode; 381 | } else { 382 | return 0; 383 | } 384 | 385 | } else { 386 | Serial.println("Couldn't connect to sonos, maybe need to re-discover"); 387 | return -1; 388 | } 389 | } 390 | 391 | int volumeUp(HTTPClient *http, IPAddress targetSonos) { 392 | return changeVolume(http, targetSonos, 7); 393 | } 394 | 395 | int volumeDown(HTTPClient *http, IPAddress targetSonos) { 396 | return changeVolume(http, targetSonos, -7); 397 | } 398 | 399 | -------------------------------------------------------------------------------- /main/sonos.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define HTTP_TIMEOUT 2000 5 | #define SONOS_PORT 1400 6 | 7 | #define ENO_CANTCONNECT 11; 8 | 9 | int sonosOperation(int (*operation)(HTTPClient *http, IPAddress target), IPAddress targetSonos); 10 | 11 | int volumeUp(HTTPClient *http, IPAddress targetSonos); 12 | int volumeDown(HTTPClient *http, IPAddress targetSonos); 13 | int sonosNext(HTTPClient *http, IPAddress targetSonos); 14 | int sonosPlay(HTTPClient *http, IPAddress targetSonos); 15 | 16 | IPAddress discoverSonos(std::string uid); 17 | -------------------------------------------------------------------------------- /main/sonos_buttons.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Sonos button control. 3 | * ESP32 project to control a sonos player with some buttons 4 | ******************************************************************************/ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "ulp_main.h" 17 | #include "sonos.h" 18 | #include 19 | #include "config.h" 20 | 21 | #define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE 22 | #include "esp_log.h" 23 | 24 | //config variables 25 | #define NUM_BTN_COLUMNS (4) 26 | #define NUM_BTN_ROWS (1) 27 | #define NUM_LED_COLUMNS (4) 28 | #define NUM_LED_ROWS (1) 29 | 30 | #define MAX_DEBOUNCE (3) 31 | 32 | // This is roughly 30 seconds with the various delays + scanning time 33 | #define IDLE_LOOPS_SLEEPY 4500 34 | 35 | static const gpio_num_t btncolumnpins[NUM_BTN_COLUMNS] = {GPIO_NUM_12, GPIO_NUM_14, GPIO_NUM_27, GPIO_NUM_26}; 36 | static const gpio_num_t btnrowpins[NUM_BTN_ROWS] = {GPIO_NUM_33}; 37 | 38 | static const gpio_num_t ledcolumnpins[NUM_LED_COLUMNS] = {GPIO_NUM_25, GPIO_NUM_4, GPIO_NUM_5, GPIO_NUM_18}; 39 | static const gpio_num_t colorpins[NUM_LED_ROWS] = {GPIO_NUM_32}; 40 | 41 | extern const uint8_t bin_start[] asm("_binary_ulp_main_bin_start"); 42 | extern const uint8_t bin_end[] asm("_binary_ulp_main_bin_end"); 43 | 44 | static int8_t debounce_count[NUM_BTN_COLUMNS][NUM_BTN_ROWS]; 45 | static const char* TAG = "SonosButtons"; 46 | 47 | // bit field representing the buttons activated on the last scan loop 48 | static uint8_t buttons_released = 0; 49 | // bit field representing the buttons activated that caused us to wakeup 50 | uint8_t sleep_buttons = 0; 51 | 52 | static uint8_t LEDS_lit = 0; 53 | static IPAddress targetSonos; 54 | 55 | // Store the base station mac address and channel in RTC memory so we can re-connect more quickly 56 | static RTC_DATA_ATTR struct { 57 | uint8_t bssid [6]; 58 | uint16_t channel; // Make this 16 bites to align on the 32 bit boundary 59 | } wifi_cache; 60 | 61 | static RTC_DATA_ATTR uint32_t wifi_cache_checksum; 62 | 63 | void clearWifiCache() { 64 | wifi_cache_checksum = 1; 65 | for (uint32_t i = 0; i < sizeof(wifi_cache.bssid); i++) { 66 | wifi_cache.bssid[i] = 0xFF; 67 | } 68 | wifi_cache.channel = 0; 69 | } 70 | 71 | bool checkWifiCache() { 72 | uint32_t readSum = 0; 73 | uint32_t *p = (uint32_t *) wifi_cache.bssid; 74 | for (uint32_t i = 0; i < sizeof(wifi_cache) / 4; i++) { 75 | readSum += p[i]; 76 | } 77 | 78 | bool hasRealData = false; 79 | for (uint32_t i = 0; i < sizeof(wifi_cache.bssid); i++) { 80 | hasRealData |= (wifi_cache.bssid[i] != 0 && wifi_cache.bssid[i] != 0xFF); 81 | } 82 | if (readSum == wifi_cache_checksum && hasRealData) { 83 | ESP_LOGD(TAG, "Good Wifi cache checksum %d, %X:%X:%X:%X:%X:%X, channel %d\n", 84 | readSum, 85 | wifi_cache.bssid[0], 86 | wifi_cache.bssid[1], 87 | wifi_cache.bssid[2], 88 | wifi_cache.bssid[3], 89 | wifi_cache.bssid[4], 90 | wifi_cache.bssid[5], 91 | wifi_cache.channel 92 | ); 93 | return true; 94 | } else { 95 | ESP_LOGI(TAG, "Bad wifi cache checksum, clearing storage"); 96 | clearWifiCache(); 97 | return false; 98 | } 99 | } 100 | 101 | void storeWifiCache() { 102 | uint8_t *bssid = WiFi.BSSID(); 103 | memcpy(wifi_cache.bssid, bssid, sizeof(wifi_cache.bssid)); 104 | wifi_cache.channel = WiFi.channel(); 105 | 106 | uint32_t checkSum = 0; 107 | uint32_t *p = (uint32_t *) wifi_cache.bssid; 108 | 109 | for (uint32_t i = 0; i < sizeof(wifi_cache) / 4; i++) { 110 | checkSum += p[i]; 111 | } 112 | 113 | wifi_cache_checksum = checkSum; 114 | } 115 | 116 | 117 | static void setuppins() { 118 | uint8_t i, j; 119 | 120 | // button columns 121 | for (i = 0; i < NUM_BTN_COLUMNS; i++) { 122 | rtc_gpio_deinit(btncolumnpins[i]); 123 | pinMode(btncolumnpins[i], OUTPUT); 124 | digitalWrite(btncolumnpins[i], HIGH); 125 | } 126 | 127 | for (i = 0; i < NUM_LED_COLUMNS; i++) { 128 | pinMode(ledcolumnpins[i], OUTPUT); 129 | digitalWrite(ledcolumnpins[i], HIGH); 130 | } 131 | 132 | // button row input lines 133 | for (i = 0; i < NUM_BTN_ROWS; i++) { 134 | rtc_gpio_deinit(btnrowpins[i]); 135 | pinMode(btnrowpins[i], INPUT_PULLUP); 136 | } 137 | 138 | // LED drive lines 139 | for (i = 0; i < NUM_LED_ROWS; i++) { 140 | pinMode(colorpins[i], OUTPUT); 141 | digitalWrite(colorpins[i], LOW); 142 | } 143 | 144 | // Initialize the debounce counter array 145 | for (i = 0; i < NUM_BTN_COLUMNS; i++) { 146 | for (j = 0; j < NUM_BTN_ROWS; j++) { 147 | debounce_count[i][j] = 0; 148 | } 149 | } 150 | 151 | pinMode(5, OUTPUT); 152 | digitalWrite(5, LOW); 153 | LEDS_lit = 0; 154 | } 155 | 156 | // Button detection, adapted from the sparkfun hookup guide at https://learn.sparkfun.com/tutorials/button-pad-hookup-guide 157 | static void scan() { 158 | buttons_released = 0; 159 | static uint8_t current = 0; 160 | uint8_t val; 161 | uint8_t j; 162 | 163 | // Select current columns 164 | digitalWrite(btncolumnpins[current], LOW); 165 | // pause a moment 166 | delay(1); 167 | 168 | // Read the button inputs 169 | for (j = 0; j < NUM_BTN_ROWS; j++) { 170 | val = digitalRead(btnrowpins[j]); 171 | 172 | if (val == LOW) { 173 | // active low: val is low when btn is pressed 174 | if (debounce_count[current][j] < MAX_DEBOUNCE) { 175 | debounce_count[current][j]++; 176 | } 177 | } 178 | else { 179 | // otherwise, button is released 180 | if (debounce_count[current][j] > 0) { 181 | debounce_count[current][j]--; 182 | 183 | if (debounce_count[current][j] == 0 ) { 184 | uint8_t released = (current * NUM_BTN_ROWS) + j; 185 | bitSet(buttons_released, released); 186 | // Turn on the LED while we're working 187 | bitSet(LEDS_lit, released); 188 | } 189 | } 190 | } 191 | }// for j = 0 to 3; 192 | 193 | delay(1); 194 | digitalWrite(btncolumnpins[current], HIGH); 195 | 196 | current++; 197 | if (current >= NUM_BTN_COLUMNS) { 198 | current = 0; 199 | } 200 | } 201 | 202 | void ledLoop( void * args ) { 203 | 204 | uint8_t i; 205 | static uint8_t current = 0; 206 | 207 | for ( ;; ) { 208 | for (current = 0; current < NUM_BTN_COLUMNS; current++) { 209 | delay(1); 210 | // output LED row values 211 | for (i = 0; i < NUM_LED_ROWS; i++) { 212 | digitalWrite(colorpins[i], LOW); 213 | if (bitRead(LEDS_lit, (current * NUM_BTN_ROWS) + i)) { 214 | digitalWrite(ledcolumnpins[current], HIGH); 215 | } 216 | } 217 | } 218 | delay(10); 219 | for (current = 0; current < NUM_BTN_COLUMNS; current++) { 220 | digitalWrite(ledcolumnpins[current], LOW); 221 | for (i = 0; i < NUM_LED_ROWS; i++) { 222 | digitalWrite(colorpins[i], HIGH); 223 | } 224 | } 225 | if (LEDS_lit == 0) { 226 | delay(25); 227 | } 228 | } 229 | } 230 | 231 | void blinkAll(uint8_t times, int waitTime) { 232 | uint8_t startVal = LEDS_lit; 233 | for (uint8_t i = 0; i < times; i++) { 234 | delay(waitTime); 235 | LEDS_lit = 255; 236 | delay(waitTime); 237 | LEDS_lit = startVal; 238 | } 239 | } 240 | 241 | boolean didJustWake() { 242 | esp_sleep_wakeup_cause_t wakeup_reason; 243 | wakeup_reason = esp_sleep_get_wakeup_cause(); 244 | 245 | boolean wokeUp = false; 246 | if (wakeup_reason == ESP_SLEEP_WAKEUP_ULP) { 247 | ESP_LOGI(TAG, "Woke up from sleep (ULP)"); 248 | sleep_buttons = ulp_wake_gpio_bit & 0xFF; 249 | ESP_LOGD(TAG, "GPIO pressed was %d\n", ulp_wake_gpio_bit & 0xFF); 250 | ulp_wake_gpio_bit = 0; 251 | LEDS_lit = sleep_buttons; 252 | wokeUp = true; 253 | } else { 254 | ESP_LOGW(TAG, "Wakeup was not caused by deep sleep: %d\n",wakeup_reason); 255 | sleep_buttons = 0; 256 | } 257 | 258 | return wokeUp; 259 | } 260 | 261 | boolean connectWifi() { 262 | WiFi.mode(WIFI_STA); 263 | bool wasCached = checkWifiCache(); 264 | if (wasCached) { 265 | // Connect using a cached bssid and channel 266 | ESP_LOGD(TAG, "Connecting using cached wifi config"); 267 | WiFi.begin(SSID, PASSWORD, wifi_cache.channel, wifi_cache.bssid, true); 268 | } else { 269 | ESP_LOGD(TAG, "Connecting to without cached wifi config"); 270 | WiFi.begin(SSID, PASSWORD); 271 | } 272 | 273 | ESP_LOGD(TAG, "Waiting for WiFi to connect..."); 274 | uint8_t wifiTries = 0; 275 | wl_status_t status = WiFi.status(); 276 | while (status != WL_CONNECTED && status != WL_CONNECT_FAILED && wifiTries < 50) { 277 | delay(50); 278 | status = WiFi.status(); 279 | wifiTries += 1; 280 | } 281 | 282 | if (! WiFi.isConnected() ) { 283 | if (wasCached) { 284 | clearWifiCache(); 285 | return connectWifi(); 286 | } else { 287 | ESP_LOGE(TAG, "WiFi connect failed, restarting"); 288 | delay(1000); 289 | blinkAll(10, 100); 290 | ESP.restart(); 291 | } 292 | } 293 | ESP_LOGI(TAG, "WiFi connect succeeded"); 294 | return true; 295 | } 296 | 297 | void setup() { 298 | // setup hardware 299 | Serial.begin(115200); 300 | setuppins(); 301 | 302 | xTaskCreatePinnedToCore( 303 | ledLoop, 304 | "LEDLoop", 305 | 1024, 306 | NULL, 307 | 2, 308 | NULL, 309 | 1 310 | ); 311 | boolean woke = didJustWake(); 312 | 313 | if (!woke) { 314 | LEDS_lit = 1; 315 | delay(250); 316 | LEDS_lit = 3; 317 | delay(250); 318 | LEDS_lit = 7; 319 | delay(250); 320 | LEDS_lit = 15; 321 | delay(250); 322 | LEDS_lit = 0; 323 | } 324 | connectWifi(); 325 | 326 | LEDS_lit = 255; 327 | delay(100); 328 | LEDS_lit = 0; 329 | 330 | Preferences prefs; 331 | prefs.begin("sonos", true); 332 | String addr = prefs.getString("playerAddress", ""); 333 | String prefUid = prefs.getString("playerUid", ""); 334 | prefs.end(); 335 | // If the configured sonos UID is different than what we stored, we need to forget our cached IP 336 | if (prefUid != String(SONOS_UID)) { 337 | prefs.begin("sonos", false); 338 | prefs.remove("playerAddress"); 339 | prefs.end(); 340 | addr = String(""); 341 | } 342 | if (addr.length() > 0) { 343 | IPAddress ip; 344 | ip.fromString(addr.c_str()); 345 | if (ip) { 346 | targetSonos = ip; 347 | ESP_LOGI(TAG, "Using cached sonos IP %s", targetSonos.toString().c_str()); 348 | } 349 | } 350 | if (!targetSonos) { 351 | targetSonos = discoverSonos(std::string(SONOS_UID)); 352 | } 353 | } 354 | 355 | void napTime() { 356 | if (WiFi.isConnected()) { 357 | storeWifiCache(); 358 | } 359 | esp_wifi_stop(); 360 | ESP_LOGD(TAG, "Starting ULP processor"); 361 | 362 | for (uint8_t i = 0; i < NUM_BTN_COLUMNS; i++) { 363 | rtc_gpio_init(btncolumnpins[i]); 364 | rtc_gpio_set_direction(btncolumnpins[i], RTC_GPIO_MODE_OUTPUT_ONLY); 365 | rtc_gpio_pulldown_dis(btncolumnpins[i]); 366 | rtc_gpio_pullup_dis(btncolumnpins[i]); 367 | rtc_gpio_hold_dis(btncolumnpins[i]); 368 | } 369 | 370 | for (uint8_t i = 0; i < NUM_BTN_ROWS; i++) { 371 | rtc_gpio_init(btnrowpins[i]); 372 | rtc_gpio_set_direction(btnrowpins[i], RTC_GPIO_MODE_INPUT_ONLY); 373 | rtc_gpio_pulldown_dis(btnrowpins[i]); 374 | rtc_gpio_pullup_en(btnrowpins[i]); 375 | rtc_gpio_hold_en(btnrowpins[i]); 376 | } 377 | 378 | esp_sleep_enable_ulp_wakeup(); 379 | ESP_ERROR_CHECK( ulp_load_binary( 380 | 0 /* load address, set to 0 when using default linker scripts */, 381 | bin_start, 382 | (bin_end - bin_start) / sizeof(uint32_t)) 383 | ); 384 | // Reset the ULP wake bit to zero in case an intervening run set it to a button and it wasn't cleaned up 385 | ulp_wake_gpio_bit = 0; 386 | ESP_LOGI(TAG, "Going to sleep now"); 387 | ESP_ERROR_CHECK( ulp_run(&ulp_scan_btns - RTC_SLOW_MEM) ); 388 | // Wakeup the ULP processor every 100 ms to check for button presses 389 | ESP_ERROR_CHECK( ulp_set_wakeup_period(0, 100000) ); 390 | esp_deep_sleep_start(); 391 | } 392 | 393 | // Second layer of sonos operation wrapper to handle the rediscovery logic 394 | void doSonos(int (*operation)(HTTPClient *http, IPAddress targetSonos)) { 395 | if (!targetSonos) { 396 | targetSonos = discoverSonos(std::string(SONOS_UID)); 397 | } 398 | if (!targetSonos) { 399 | ESP_LOGE(TAG, "Couldn't find the right sonos, bailing"); 400 | return; 401 | } 402 | 403 | int error = sonosOperation(operation, targetSonos); 404 | if (error) { 405 | // try rediscovering 406 | targetSonos = discoverSonos(std::string(SONOS_UID)); 407 | } 408 | } 409 | 410 | void loop() { 411 | static int idleLoopCount = 0; 412 | 413 | int handle_buttons = buttons_released | sleep_buttons; 414 | // Clear the sleep_buttons variable so we only handle it once 415 | sleep_buttons = 0; 416 | scan(); 417 | if (handle_buttons != 0) { 418 | idleLoopCount = 0; 419 | if (bitRead(handle_buttons, 0)) { 420 | ESP_LOGI(TAG, "Sending play/pause"); 421 | doSonos(sonosPlay); 422 | bitClear(LEDS_lit, 0); 423 | } else if (bitRead(handle_buttons, 1)) { 424 | ESP_LOGI(TAG, "Sending next"); 425 | doSonos(sonosNext); 426 | bitClear(LEDS_lit, 1); 427 | } else if (bitRead(handle_buttons, 2)) { 428 | ESP_LOGI(TAG, "Sending volume up"); 429 | doSonos(volumeUp); 430 | bitClear(LEDS_lit, 2); 431 | } else if (bitRead(handle_buttons, 3)) { 432 | ESP_LOGI(TAG, "Sending volume down"); 433 | doSonos(volumeDown); 434 | bitClear(LEDS_lit, 3); 435 | } else { 436 | ESP_LOGE(TAG, "Not implemented"); 437 | delay(1000); 438 | LEDS_lit = 0; 439 | } 440 | } else { 441 | delay(5); 442 | idleLoopCount += 1; 443 | if (idleLoopCount >= IDLE_LOOPS_SLEEPY) { 444 | idleLoopCount = 0; 445 | napTime(); 446 | } 447 | } 448 | } 449 | -------------------------------------------------------------------------------- /main/ulp/button_wakeup.S: -------------------------------------------------------------------------------- 1 | #include "soc/rtc_cntl_reg.h" 2 | #include "soc/rtc_io_reg.h" 3 | #include "soc/rtc_gpio_channel.h" 4 | #include "soc/soc_ulp.h" 5 | 6 | #define BOUNCE_COUNT 3 7 | // number of cycles to wait between button scan steps 8 | #define STEP_WAIT 1000 9 | 10 | .bss 11 | 12 | .text 13 | 14 | // Bitmask index of the button that we woke up for. 15 | // The index of the GPIO pins are GPIO_12, GPIO_14, GPIO_27, GPIO_26 16 | .global wake_gpio_bit 17 | wake_gpio_bit: 18 | .long 0 19 | 20 | btnbounce_12: 21 | .long BOUNCE_COUNT 22 | btnbounce_14: 23 | .long BOUNCE_COUNT 24 | btnbounce_27: 25 | .long BOUNCE_COUNT 26 | btnbounce_26: 27 | .long BOUNCE_COUNT 28 | 29 | // Gets assigned to wake_gpio_bit on wakeup 30 | current_button: 31 | .long 0 32 | 33 | .global scan_btns 34 | scan_btns: 35 | // Clear the wake_gpio_bit variable 36 | move r3, wake_gpio_bit 37 | move r2, 0x0000 38 | st r2, r3, 0 39 | // Clear the bounce counters 40 | move r3, btnbounce_12 41 | move r2, BOUNCE_COUNT 42 | st r2, r3, 0 43 | st r2, r3, 4 44 | st r2, r3, 8 45 | st r2, r3, 12 46 | 47 | scan_btn_init: 48 | /* Start with all of the COL gpio pins in HIGH */ 49 | WRITE_RTC_REG(RTC_GPIO_OUT_W1TS_REG, RTC_GPIO_OUT_DATA_W1TS_S + RTCIO_GPIO12_CHANNEL, 1, 1) 50 | WRITE_RTC_REG(RTC_GPIO_OUT_W1TS_REG, RTC_GPIO_OUT_DATA_W1TS_S + RTCIO_GPIO14_CHANNEL, 1, 1) 51 | WRITE_RTC_REG(RTC_GPIO_OUT_W1TS_REG, RTC_GPIO_OUT_DATA_W1TS_S + RTCIO_GPIO27_CHANNEL, 1, 1) 52 | WRITE_RTC_REG(RTC_GPIO_OUT_W1TS_REG, RTC_GPIO_OUT_DATA_W1TS_S + RTCIO_GPIO26_CHANNEL, 1, 1) 53 | 54 | scan_btn_check: 55 | 56 | handle_gpio_12: 57 | /* Track that we're working on button 12 */ 58 | move r3, current_button 59 | move r2, 1 << 0 60 | st r2, r3, 0 61 | /* Set GPIO 12 to LOW */ 62 | WRITE_RTC_REG(RTC_GPIO_OUT_W1TC_REG, RTC_GPIO_OUT_DATA_W1TC_S + RTCIO_GPIO12_CHANNEL, 1, 1) 63 | wait STEP_WAIT 64 | /* Read gpio 33 state into R0 */ 65 | READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + RTCIO_GPIO33_CHANNEL, 1) 66 | jumpr notpressed_12, 1, GE /* if we read high, then button wasn't pressed, move past the incrementing */ 67 | /* Button was pressed ... */ 68 | move r3, btnbounce_12 /* decrement the bounce counter and check if we need to wake_up */ 69 | ld r2, r3, 0 70 | sub r0, r2, 1 71 | jump wake_up, EQ /* if the subtraction result was 0, then jump to wake_up */ 72 | st r0, r3, 0 73 | wait STEP_WAIT /* we got a button press, so wait a bit and restart the loop */ 74 | jump scan_btn_init 75 | 76 | notpressed_12: 77 | /* Button was not pressed reset the bounce counter */ 78 | move r1, btnbounce_12 79 | move r0, BOUNCE_COUNT 80 | st r0, r1, 0 81 | /* Reset gpio to high */ 82 | WRITE_RTC_REG(RTC_GPIO_OUT_W1TS_REG, RTC_GPIO_OUT_DATA_W1TS_S + RTCIO_GPIO12_CHANNEL, 1, 1) 83 | 84 | wait 8000 85 | 86 | handle_gpio_14: 87 | /* Track that we're working on button 14 */ 88 | move r3, current_button 89 | move r2, 1 << 1 90 | st r2, r3, 0 91 | /* Set GPIO 14 to LOW */ 92 | WRITE_RTC_REG(RTC_GPIO_OUT_W1TC_REG, RTC_GPIO_OUT_DATA_W1TC_S + RTCIO_GPIO14_CHANNEL, 1, 1) 93 | wait STEP_WAIT 94 | /* Read gpio 33 state into R0 */ 95 | READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + RTCIO_GPIO33_CHANNEL, 1) 96 | jumpr notpressed_14, 1, GE /* if we read high, then button wasn't pressed, move past the incrementing */ 97 | /* Button was pressed ... */ 98 | move r3, btnbounce_14 /* decrement the bounce counter and check if we need to wake_up */ 99 | ld r2, r3, 0 100 | sub r0, r2, 1 101 | jump wake_up, EQ /* if the subtraction result was 0, then jump to wake_up */ 102 | st r0, r3, 0 103 | wait STEP_WAIT /* we got a button press, so wait a bit and restart the loop */ 104 | jump scan_btn_init 105 | 106 | notpressed_14: 107 | /* Button was not pressed reset the bounce counter */ 108 | move r1, btnbounce_14 109 | move r0, BOUNCE_COUNT 110 | st r0, r1, 0 111 | /* Reset gpio to high */ 112 | WRITE_RTC_REG(RTC_GPIO_OUT_W1TS_REG, RTC_GPIO_OUT_DATA_W1TS_S + RTCIO_GPIO14_CHANNEL, 1, 1) 113 | 114 | wait 8000 115 | 116 | handle_gpio_27: 117 | /* Track that we're working on button 27 */ 118 | move r3, current_button 119 | move r2, 1 << 2 120 | st r2, r3, 0 121 | /* Set GPIO 27 to LOW */ 122 | WRITE_RTC_REG(RTC_GPIO_OUT_W1TC_REG, RTC_GPIO_OUT_DATA_W1TC_S + RTCIO_GPIO27_CHANNEL, 1, 1) 123 | wait STEP_WAIT 124 | /* Read gpio 33 state into R0 */ 125 | READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + RTCIO_GPIO33_CHANNEL, 1) 126 | jumpr notpressed_27, 1, GE /* if we read high, then button wasn't pressed, move past the incrementing */ 127 | /* Button was pressed ... */ 128 | move r3, btnbounce_27 /* decrement the bounce counter and check if we need to wake_up */ 129 | ld r2, r3, 0 130 | sub r0, r2, 1 131 | jump wake_up, EQ /* if the subtraction result was 0, then jump to wake_up */ 132 | st r0, r3, 0 133 | wait STEP_WAIT /* we got a button press, so wait a bit and restart the loop */ 134 | jump scan_btn_init 135 | 136 | notpressed_27: 137 | /* Button was not pressed reset the bounce counter */ 138 | move r1, btnbounce_27 139 | move r0, BOUNCE_COUNT 140 | st r0, r1, 0 141 | /* Reset gpio to high */ 142 | WRITE_RTC_REG(RTC_GPIO_OUT_W1TS_REG, RTC_GPIO_OUT_DATA_W1TS_S + RTCIO_GPIO27_CHANNEL, 1, 1) 143 | 144 | wait 8000 145 | 146 | handle_gpio_26: 147 | /* Track that we're working on button 26 */ 148 | move r3, current_button 149 | move r2, 1 << 3 150 | st r2, r3, 0 151 | /* Set GPIO 26 to LOW */ 152 | WRITE_RTC_REG(RTC_GPIO_OUT_W1TC_REG, RTC_GPIO_OUT_DATA_W1TC_S + RTCIO_GPIO26_CHANNEL, 1, 1) 153 | wait STEP_WAIT 154 | /* Read gpio 33 state into R0 */ 155 | READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + RTCIO_GPIO33_CHANNEL, 1) 156 | jumpr notpressed_26, 1, GE /* if we read high, then button wasn't pressed, move past the incrementing */ 157 | /* Button was pressed ... */ 158 | move r3, btnbounce_26 /* decrement the bounce counter and check if we need to wake_up */ 159 | ld r2, r3, 0 160 | sub r0, r2, 1 161 | jump wake_up, EQ /* if the subtraction result was 0, then jump to wake_up */ 162 | st r0, r3, 0 163 | wait STEP_WAIT /* we got a button press, so wait a bit and restart the loop */ 164 | jump scan_btn_init 165 | 166 | notpressed_26: 167 | /* Button was not pressed reset the bounce counter */ 168 | move r1, btnbounce_26 169 | move r0, BOUNCE_COUNT 170 | st r0, r1, 0 171 | /* Reset gpio to high */ 172 | WRITE_RTC_REG(RTC_GPIO_OUT_W1TS_REG, RTC_GPIO_OUT_DATA_W1TS_S + RTCIO_GPIO26_CHANNEL, 1, 1) 173 | 174 | /* we didn't find any buttons pressed. HALT and let the timer wake us up again. */ 175 | halt 176 | 177 | /* reset all of the bounce counters, set the wake_gpio_bit variable, wake_up and halt */ 178 | .global wake_up 179 | wake_up: 180 | move r1, btnbounce_12 181 | move r0, BOUNCE_COUNT 182 | st r0, r1, 0 183 | st r0, r1, 4 184 | st r0, r1, 8 185 | st r0, r1, 12 186 | move r1, current_button 187 | move r2, wake_gpio_bit 188 | ld r0, r1, 0 189 | st r0, r2, 0 /* store the current button value into wake gpio */ 190 | wake 191 | /* Disable the sleep wake_up timer so we don't start running again until the main CPU starts us */ 192 | WRITE_RTC_FIELD(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN, 0) 193 | halt 194 | -------------------------------------------------------------------------------- /sdkconfig: -------------------------------------------------------------------------------- 1 | # 2 | # Automatically generated file; DO NOT EDIT. 3 | # Espressif IoT Development Framework Configuration 4 | # 5 | 6 | # 7 | # SDK tool configuration 8 | # 9 | CONFIG_TOOLPREFIX="xtensa-esp32-elf-" 10 | CONFIG_PYTHON="python" 11 | CONFIG_MAKE_WARN_UNDEFINED_VARIABLES=y 12 | 13 | # 14 | # Arduino Configuration 15 | # 16 | CONFIG_ENABLE_ARDUINO_DEPENDS=y 17 | CONFIG_AUTOSTART_ARDUINO=y 18 | CONFIG_ARDUINO_RUN_CORE0= 19 | CONFIG_ARDUINO_RUN_CORE1=y 20 | CONFIG_ARDUINO_RUN_NO_AFFINITY= 21 | CONFIG_ARDUINO_RUNNING_CORE=1 22 | CONFIG_ARDUINO_EVENT_RUN_CORE0= 23 | CONFIG_ARDUINO_EVENT_RUN_CORE1=y 24 | CONFIG_ARDUINO_EVENT_RUN_NO_AFFINITY= 25 | CONFIG_ARDUINO_EVENT_RUNNING_CORE=1 26 | CONFIG_ARDUINO_UDP_RUN_CORE0= 27 | CONFIG_ARDUINO_UDP_RUN_CORE1=y 28 | CONFIG_ARDUINO_UDP_RUN_NO_AFFINITY= 29 | CONFIG_ARDUINO_UDP_RUNNING_CORE=1 30 | CONFIG_DISABLE_HAL_LOCKS= 31 | 32 | # 33 | # Debug Log Configuration 34 | # 35 | CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_NONE= 36 | CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_ERROR= 37 | CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_WARN= 38 | CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_INFO=y 39 | CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_DEBUG= 40 | CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_VERBOSE= 41 | CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL=3 42 | CONFIG_ARDUHAL_LOG_COLORS=y 43 | CONFIG_ARDUHAL_ESP_LOG=y 44 | CONFIG_ARDUHAL_PARTITION_SCHEME_DEFAULT=y 45 | CONFIG_ARDUHAL_PARTITION_SCHEME_MINIMAL= 46 | CONFIG_ARDUHAL_PARTITION_SCHEME_NO_OTA= 47 | CONFIG_ARDUHAL_PARTITION_SCHEME_HUGE_APP= 48 | CONFIG_ARDUHAL_PARTITION_SCHEME_MIN_SPIFFS= 49 | CONFIG_ARDUHAL_PARTITION_SCHEME="default" 50 | CONFIG_AUTOCONNECT_WIFI= 51 | CONFIG_ARDUINO_SELECTIVE_COMPILATION=y 52 | CONFIG_ARDUINO_SELECTIVE_ArduinoOTA= 53 | CONFIG_ARDUINO_SELECTIVE_AsyncUDP=y 54 | CONFIG_ARDUINO_SELECTIVE_AzureIoT= 55 | CONFIG_ARDUINO_SELECTIVE_BLE= 56 | CONFIG_ARDUINO_SELECTIVE_BluetoothSerial= 57 | CONFIG_ARDUINO_SELECTIVE_DNSServer= 58 | CONFIG_ARDUINO_SELECTIVE_EEPROM=y 59 | CONFIG_ARDUINO_SELECTIVE_ESP32=y 60 | CONFIG_ARDUINO_SELECTIVE_ESPmDNS= 61 | CONFIG_ARDUINO_SELECTIVE_FFat= 62 | CONFIG_ARDUINO_SELECTIVE_FS=y 63 | CONFIG_ARDUINO_SELECTIVE_HTTPClient=y 64 | CONFIG_ARDUINO_SELECTIVE_NetBIOS= 65 | CONFIG_ARDUINO_SELECTIVE_Preferences=y 66 | CONFIG_ARDUINO_SELECTIVE_SD=y 67 | CONFIG_ARDUINO_SELECTIVE_SD_MMC=y 68 | CONFIG_ARDUINO_SELECTIVE_SimpleBLE= 69 | CONFIG_ARDUINO_SELECTIVE_SPI=y 70 | CONFIG_ARDUINO_SELECTIVE_SPIFFS=y 71 | CONFIG_ARDUINO_SELECTIVE_Ticker= 72 | CONFIG_ARDUINO_SELECTIVE_Update= 73 | CONFIG_ARDUINO_SELECTIVE_WebServer= 74 | CONFIG_ARDUINO_SELECTIVE_WiFi=y 75 | CONFIG_ARDUINO_SELECTIVE_WiFiClientSecure=y 76 | CONFIG_ARDUINO_SELECTIVE_Wire=y 77 | 78 | # 79 | # Bootloader config 80 | # 81 | CONFIG_LOG_BOOTLOADER_LEVEL_NONE=y 82 | CONFIG_LOG_BOOTLOADER_LEVEL_ERROR= 83 | CONFIG_LOG_BOOTLOADER_LEVEL_WARN= 84 | CONFIG_LOG_BOOTLOADER_LEVEL_INFO= 85 | CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG= 86 | CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE= 87 | CONFIG_LOG_BOOTLOADER_LEVEL=0 88 | CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y 89 | CONFIG_BOOTLOADER_FACTORY_RESET= 90 | CONFIG_BOOTLOADER_APP_TEST= 91 | CONFIG_BOOTLOADER_WDT_ENABLE=y 92 | CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE= 93 | CONFIG_BOOTLOADER_WDT_TIME_MS=9000 94 | 95 | # 96 | # Security features 97 | # 98 | CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT= 99 | CONFIG_SECURE_BOOT_ENABLED= 100 | CONFIG_FLASH_ENCRYPTION_ENABLED= 101 | 102 | # 103 | # Serial flasher config 104 | # 105 | CONFIG_ESPTOOLPY_PORT="/dev/ttyUSB0" 106 | CONFIG_ESPTOOLPY_BAUD_115200B= 107 | CONFIG_ESPTOOLPY_BAUD_230400B= 108 | CONFIG_ESPTOOLPY_BAUD_921600B=y 109 | CONFIG_ESPTOOLPY_BAUD_2MB= 110 | CONFIG_ESPTOOLPY_BAUD_OTHER= 111 | CONFIG_ESPTOOLPY_BAUD_OTHER_VAL=115200 112 | CONFIG_ESPTOOLPY_BAUD=921600 113 | CONFIG_ESPTOOLPY_COMPRESSED=y 114 | CONFIG_FLASHMODE_QIO= 115 | CONFIG_FLASHMODE_QOUT= 116 | CONFIG_FLASHMODE_DIO=y 117 | CONFIG_FLASHMODE_DOUT= 118 | CONFIG_ESPTOOLPY_FLASHMODE="dio" 119 | CONFIG_ESPTOOLPY_FLASHFREQ_80M=y 120 | CONFIG_ESPTOOLPY_FLASHFREQ_40M= 121 | CONFIG_ESPTOOLPY_FLASHFREQ_26M= 122 | CONFIG_ESPTOOLPY_FLASHFREQ_20M= 123 | CONFIG_ESPTOOLPY_FLASHFREQ="80m" 124 | CONFIG_ESPTOOLPY_FLASHSIZE_1MB= 125 | CONFIG_ESPTOOLPY_FLASHSIZE_2MB= 126 | CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y 127 | CONFIG_ESPTOOLPY_FLASHSIZE_8MB= 128 | CONFIG_ESPTOOLPY_FLASHSIZE_16MB= 129 | CONFIG_ESPTOOLPY_FLASHSIZE="4MB" 130 | CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y 131 | CONFIG_ESPTOOLPY_BEFORE_RESET=y 132 | CONFIG_ESPTOOLPY_BEFORE_NORESET= 133 | CONFIG_ESPTOOLPY_BEFORE="default_reset" 134 | CONFIG_ESPTOOLPY_AFTER_RESET=y 135 | CONFIG_ESPTOOLPY_AFTER_NORESET= 136 | CONFIG_ESPTOOLPY_AFTER="hard_reset" 137 | CONFIG_MONITOR_BAUD_9600B= 138 | CONFIG_MONITOR_BAUD_57600B= 139 | CONFIG_MONITOR_BAUD_115200B=y 140 | CONFIG_MONITOR_BAUD_230400B= 141 | CONFIG_MONITOR_BAUD_921600B= 142 | CONFIG_MONITOR_BAUD_2MB= 143 | CONFIG_MONITOR_BAUD_OTHER= 144 | CONFIG_MONITOR_BAUD_OTHER_VAL=115200 145 | CONFIG_MONITOR_BAUD=115200 146 | 147 | # 148 | # Partition Table 149 | # 150 | CONFIG_PARTITION_TABLE_SINGLE_APP=y 151 | CONFIG_PARTITION_TABLE_TWO_OTA= 152 | CONFIG_PARTITION_TABLE_CUSTOM= 153 | CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" 154 | CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp_coredump.csv" 155 | CONFIG_PARTITION_TABLE_OFFSET=0x8000 156 | CONFIG_PARTITION_TABLE_MD5=y 157 | 158 | # 159 | # Compiler options 160 | # 161 | CONFIG_OPTIMIZATION_LEVEL_DEBUG=y 162 | CONFIG_OPTIMIZATION_LEVEL_RELEASE= 163 | CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y 164 | CONFIG_OPTIMIZATION_ASSERTIONS_SILENT= 165 | CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED= 166 | CONFIG_CXX_EXCEPTIONS=y 167 | CONFIG_CXX_EXCEPTIONS_EMG_POOL_SIZE=0 168 | CONFIG_STACK_CHECK_NONE= 169 | CONFIG_STACK_CHECK_NORM=y 170 | CONFIG_STACK_CHECK_STRONG= 171 | CONFIG_STACK_CHECK_ALL= 172 | CONFIG_STACK_CHECK=y 173 | CONFIG_WARN_WRITE_STRINGS=y 174 | CONFIG_DISABLE_GCC8_WARNINGS= 175 | 176 | # 177 | # Component config 178 | # 179 | 180 | # 181 | # Application Level Tracing 182 | # 183 | CONFIG_ESP32_APPTRACE_DEST_TRAX= 184 | CONFIG_ESP32_APPTRACE_DEST_NONE=y 185 | CONFIG_ESP32_APPTRACE_ENABLE= 186 | CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y 187 | CONFIG_AWS_IOT_SDK= 188 | 189 | # 190 | # Bluetooth 191 | # 192 | CONFIG_BT_ENABLED= 193 | CONFIG_BTDM_CONTROLLER_BR_EDR_SCO_DATA_PATH_EFF=0 194 | CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN_EFF=0 195 | CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_ACL_CONN_EFF=0 196 | CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_SYNC_CONN_EFF=0 197 | CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE=0 198 | CONFIG_BT_RESERVE_DRAM=0 199 | 200 | # 201 | # Driver configurations 202 | # 203 | 204 | # 205 | # ADC configuration 206 | # 207 | CONFIG_ADC_FORCE_XPD_FSM= 208 | CONFIG_ADC2_DISABLE_DAC=y 209 | 210 | # 211 | # SPI configuration 212 | # 213 | CONFIG_SPI_MASTER_IN_IRAM= 214 | CONFIG_SPI_MASTER_ISR_IN_IRAM=y 215 | CONFIG_SPI_SLAVE_IN_IRAM= 216 | CONFIG_SPI_SLAVE_ISR_IN_IRAM=y 217 | 218 | # 219 | # ESP32-specific 220 | # 221 | CONFIG_ESP32_DEFAULT_CPU_FREQ_80=y 222 | CONFIG_ESP32_DEFAULT_CPU_FREQ_160= 223 | CONFIG_ESP32_DEFAULT_CPU_FREQ_240= 224 | CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=80 225 | CONFIG_SPIRAM_SUPPORT= 226 | CONFIG_MEMMAP_TRACEMEM= 227 | CONFIG_MEMMAP_TRACEMEM_TWOBANKS= 228 | CONFIG_ESP32_TRAX= 229 | CONFIG_TRACEMEM_RESERVE_DRAM=0x0 230 | CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH=y 231 | CONFIG_ESP32_ENABLE_COREDUMP_TO_UART= 232 | CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE= 233 | CONFIG_ESP32_ENABLE_COREDUMP=y 234 | CONFIG_ESP32_CORE_DUMP_LOG_LEVEL=1 235 | CONFIG_TWO_UNIVERSAL_MAC_ADDRESS= 236 | CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS=y 237 | CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS=4 238 | CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 239 | CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2048 240 | CONFIG_MAIN_TASK_STACK_SIZE=4096 241 | CONFIG_IPC_TASK_STACK_SIZE=1024 242 | CONFIG_TIMER_TASK_STACK_SIZE=4096 243 | CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y 244 | CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF= 245 | CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR= 246 | CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF= 247 | CONFIG_NEWLIB_STDIN_LINE_ENDING_LF= 248 | CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y 249 | CONFIG_NEWLIB_NANO_FORMAT= 250 | CONFIG_CONSOLE_UART_DEFAULT=y 251 | CONFIG_CONSOLE_UART_CUSTOM= 252 | CONFIG_CONSOLE_UART_NONE= 253 | CONFIG_CONSOLE_UART_NUM=0 254 | CONFIG_CONSOLE_UART_BAUDRATE=115200 255 | CONFIG_ULP_COPROC_ENABLED=y 256 | CONFIG_ULP_COPROC_RESERVE_MEM=512 257 | CONFIG_ESP32_PANIC_PRINT_HALT= 258 | CONFIG_ESP32_PANIC_PRINT_REBOOT=y 259 | CONFIG_ESP32_PANIC_SILENT_REBOOT= 260 | CONFIG_ESP32_PANIC_GDBSTUB= 261 | CONFIG_ESP32_DEBUG_OCDAWARE=y 262 | CONFIG_ESP32_DEBUG_STUBS_ENABLE=y 263 | CONFIG_INT_WDT=y 264 | CONFIG_INT_WDT_TIMEOUT_MS=300 265 | CONFIG_INT_WDT_CHECK_CPU1=y 266 | CONFIG_TASK_WDT=y 267 | CONFIG_TASK_WDT_PANIC=y 268 | CONFIG_TASK_WDT_TIMEOUT_S=5 269 | CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y 270 | CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y 271 | CONFIG_BROWNOUT_DET=y 272 | CONFIG_BROWNOUT_DET_LVL_SEL_0=y 273 | CONFIG_BROWNOUT_DET_LVL_SEL_1= 274 | CONFIG_BROWNOUT_DET_LVL_SEL_2= 275 | CONFIG_BROWNOUT_DET_LVL_SEL_3= 276 | CONFIG_BROWNOUT_DET_LVL_SEL_4= 277 | CONFIG_BROWNOUT_DET_LVL_SEL_5= 278 | CONFIG_BROWNOUT_DET_LVL_SEL_6= 279 | CONFIG_BROWNOUT_DET_LVL_SEL_7= 280 | CONFIG_BROWNOUT_DET_LVL=0 281 | CONFIG_REDUCE_PHY_TX_POWER=y 282 | CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y 283 | CONFIG_ESP32_TIME_SYSCALL_USE_RTC= 284 | CONFIG_ESP32_TIME_SYSCALL_USE_FRC1= 285 | CONFIG_ESP32_TIME_SYSCALL_USE_NONE= 286 | CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y 287 | CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL= 288 | CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_OSC= 289 | CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_8MD256= 290 | CONFIG_ESP32_RTC_CLK_CAL_CYCLES=1024 291 | CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=2000 292 | CONFIG_ESP32_XTAL_FREQ_40= 293 | CONFIG_ESP32_XTAL_FREQ_26= 294 | CONFIG_ESP32_XTAL_FREQ_AUTO=y 295 | CONFIG_ESP32_XTAL_FREQ=0 296 | CONFIG_DISABLE_BASIC_ROM_CONSOLE= 297 | CONFIG_NO_BLOBS= 298 | CONFIG_ESP_TIMER_PROFILING= 299 | CONFIG_COMPATIBLE_PRE_V2_1_BOOTLOADERS= 300 | CONFIG_ESP_ERR_TO_NAME_LOOKUP=y 301 | 302 | # 303 | # Wi-Fi 304 | # 305 | CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=8 306 | CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=16 307 | CONFIG_ESP32_WIFI_STATIC_TX_BUFFER= 308 | CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER=y 309 | CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=1 310 | CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=32 311 | CONFIG_ESP32_WIFI_CSI_ENABLED=y 312 | CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y 313 | CONFIG_ESP32_WIFI_TX_BA_WIN=6 314 | CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y 315 | CONFIG_ESP32_WIFI_RX_BA_WIN=16 316 | CONFIG_ESP32_WIFI_NVS_ENABLED=y 317 | CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0= 318 | CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1=y 319 | CONFIG_ESP32_WIFI_SOFTAP_BEACON_MAX_LEN=752 320 | CONFIG_ESP32_WIFI_IRAM_OPT=y 321 | CONFIG_ESP32_WIFI_MGMT_SBUF_NUM=32 322 | 323 | # 324 | # PHY 325 | # 326 | CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y 327 | CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION= 328 | CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20 329 | CONFIG_ESP32_PHY_MAX_TX_POWER=20 330 | 331 | # 332 | # Power Management 333 | # 334 | CONFIG_PM_ENABLE= 335 | 336 | # 337 | # ADC-Calibration 338 | # 339 | CONFIG_ADC_CAL_EFUSE_TP_ENABLE=y 340 | CONFIG_ADC_CAL_EFUSE_VREF_ENABLE=y 341 | CONFIG_ADC_CAL_LUT_ENABLE=y 342 | 343 | # 344 | # Event Loop Library 345 | # 346 | CONFIG_EVENT_LOOP_PROFILING= 347 | 348 | # 349 | # ESP HTTP client 350 | # 351 | CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y 352 | 353 | # 354 | # HTTP Server 355 | # 356 | CONFIG_HTTPD_MAX_REQ_HDR_LEN=512 357 | CONFIG_HTTPD_MAX_URI_LEN=512 358 | CONFIG_HTTPD_PURGE_BUF_LEN=32 359 | CONFIG_HTTPD_LOG_PURGE_DATA= 360 | 361 | # 362 | # Ethernet 363 | # 364 | CONFIG_DMA_RX_BUF_NUM=10 365 | CONFIG_DMA_TX_BUF_NUM=10 366 | CONFIG_EMAC_L2_TO_L3_RX_BUF_MODE=y 367 | CONFIG_EMAC_CHECK_LINK_PERIOD_MS=2000 368 | CONFIG_EMAC_TASK_PRIORITY=20 369 | CONFIG_EMAC_TASK_STACK_SIZE=3072 370 | 371 | # 372 | # FAT Filesystem support 373 | # 374 | CONFIG_FATFS_CODEPAGE_DYNAMIC= 375 | CONFIG_FATFS_CODEPAGE_437= 376 | CONFIG_FATFS_CODEPAGE_720= 377 | CONFIG_FATFS_CODEPAGE_737= 378 | CONFIG_FATFS_CODEPAGE_771= 379 | CONFIG_FATFS_CODEPAGE_775= 380 | CONFIG_FATFS_CODEPAGE_850=y 381 | CONFIG_FATFS_CODEPAGE_852= 382 | CONFIG_FATFS_CODEPAGE_855= 383 | CONFIG_FATFS_CODEPAGE_857= 384 | CONFIG_FATFS_CODEPAGE_860= 385 | CONFIG_FATFS_CODEPAGE_861= 386 | CONFIG_FATFS_CODEPAGE_862= 387 | CONFIG_FATFS_CODEPAGE_863= 388 | CONFIG_FATFS_CODEPAGE_864= 389 | CONFIG_FATFS_CODEPAGE_865= 390 | CONFIG_FATFS_CODEPAGE_866= 391 | CONFIG_FATFS_CODEPAGE_869= 392 | CONFIG_FATFS_CODEPAGE_932= 393 | CONFIG_FATFS_CODEPAGE_936= 394 | CONFIG_FATFS_CODEPAGE_949= 395 | CONFIG_FATFS_CODEPAGE_950= 396 | CONFIG_FATFS_CODEPAGE=850 397 | CONFIG_FATFS_LFN_NONE= 398 | CONFIG_FATFS_LFN_HEAP= 399 | CONFIG_FATFS_LFN_STACK=y 400 | CONFIG_FATFS_MAX_LFN=255 401 | CONFIG_FATFS_API_ENCODING_ANSI_OEM=y 402 | CONFIG_FATFS_API_ENCODING_UTF_16= 403 | CONFIG_FATFS_API_ENCODING_UTF_8= 404 | CONFIG_FATFS_FS_LOCK=0 405 | CONFIG_FATFS_TIMEOUT_MS=10000 406 | CONFIG_FATFS_PER_FILE_CACHE=y 407 | 408 | # 409 | # Modbus configuration 410 | # 411 | CONFIG_MB_QUEUE_LENGTH=20 412 | CONFIG_MB_SERIAL_TASK_STACK_SIZE=2048 413 | CONFIG_MB_SERIAL_BUF_SIZE=256 414 | CONFIG_MB_SERIAL_TASK_PRIO=10 415 | CONFIG_MB_CONTROLLER_SLAVE_ID_SUPPORT=y 416 | CONFIG_MB_CONTROLLER_SLAVE_ID=0x00112233 417 | CONFIG_MB_CONTROLLER_NOTIFY_TIMEOUT=20 418 | CONFIG_MB_CONTROLLER_NOTIFY_QUEUE_SIZE=20 419 | CONFIG_MB_CONTROLLER_STACK_SIZE=4096 420 | CONFIG_MB_EVENT_QUEUE_TIMEOUT=20 421 | CONFIG_MB_TIMER_PORT_ENABLED=y 422 | CONFIG_MB_TIMER_GROUP=0 423 | CONFIG_MB_TIMER_INDEX=0 424 | 425 | # 426 | # FreeRTOS 427 | # 428 | CONFIG_FREERTOS_UNICORE= 429 | CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF 430 | CONFIG_FREERTOS_CORETIMER_0=y 431 | CONFIG_FREERTOS_CORETIMER_1= 432 | CONFIG_FREERTOS_HZ=1000 433 | CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y 434 | CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE= 435 | CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL= 436 | CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y 437 | CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y 438 | CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y 439 | CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=1 440 | CONFIG_FREERTOS_ASSERT_FAIL_ABORT=y 441 | CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE= 442 | CONFIG_FREERTOS_ASSERT_DISABLE= 443 | CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1024 444 | CONFIG_FREERTOS_ISR_STACKSIZE=2096 445 | CONFIG_FREERTOS_LEGACY_HOOKS= 446 | CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 447 | CONFIG_SUPPORT_STATIC_ALLOCATION= 448 | CONFIG_TIMER_TASK_PRIORITY=1 449 | CONFIG_TIMER_TASK_STACK_DEPTH=2048 450 | CONFIG_TIMER_QUEUE_LENGTH=10 451 | CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 452 | CONFIG_FREERTOS_USE_TRACE_FACILITY= 453 | CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS= 454 | CONFIG_FREERTOS_DEBUG_INTERNALS= 455 | CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y 456 | 457 | # 458 | # Heap memory debugging 459 | # 460 | CONFIG_HEAP_POISONING_DISABLED= 461 | CONFIG_HEAP_POISONING_LIGHT=y 462 | CONFIG_HEAP_POISONING_COMPREHENSIVE= 463 | CONFIG_HEAP_TRACING= 464 | CONFIG_HEAP_TASK_TRACKING= 465 | 466 | # 467 | # libsodium 468 | # 469 | 470 | # 471 | # Log output 472 | # 473 | CONFIG_LOG_DEFAULT_LEVEL_NONE= 474 | CONFIG_LOG_DEFAULT_LEVEL_ERROR= 475 | CONFIG_LOG_DEFAULT_LEVEL_WARN= 476 | CONFIG_LOG_DEFAULT_LEVEL_INFO=y 477 | CONFIG_LOG_DEFAULT_LEVEL_DEBUG= 478 | CONFIG_LOG_DEFAULT_LEVEL_VERBOSE= 479 | CONFIG_LOG_DEFAULT_LEVEL=3 480 | CONFIG_LOG_COLORS=y 481 | 482 | # 483 | # LWIP 484 | # 485 | CONFIG_L2_TO_L3_COPY= 486 | CONFIG_LWIP_IRAM_OPTIMIZATION= 487 | CONFIG_LWIP_MAX_SOCKETS=10 488 | CONFIG_USE_ONLY_LWIP_SELECT= 489 | CONFIG_LWIP_SO_REUSE=y 490 | CONFIG_LWIP_SO_REUSE_RXTOALL=y 491 | CONFIG_LWIP_SO_RCVBUF=y 492 | CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=1 493 | CONFIG_LWIP_IP_FRAG=y 494 | CONFIG_LWIP_IP_REASSEMBLY= 495 | CONFIG_LWIP_STATS= 496 | CONFIG_LWIP_ETHARP_TRUST_IP_MAC=y 497 | CONFIG_ESP_GRATUITOUS_ARP=y 498 | CONFIG_GARP_TMR_INTERVAL=60 499 | CONFIG_TCPIP_RECVMBOX_SIZE=32 500 | CONFIG_LWIP_DHCP_DOES_ARP_CHECK=y 501 | CONFIG_LWIP_DHCP_RESTORE_LAST_IP= 502 | 503 | # 504 | # DHCP server 505 | # 506 | CONFIG_LWIP_DHCPS_LEASE_UNIT=60 507 | CONFIG_LWIP_DHCPS_MAX_STATION_NUM=8 508 | CONFIG_LWIP_AUTOIP= 509 | CONFIG_LWIP_NETIF_LOOPBACK=y 510 | CONFIG_LWIP_LOOPBACK_MAX_PBUFS=8 511 | 512 | # 513 | # TCP 514 | # 515 | CONFIG_LWIP_MAX_ACTIVE_TCP=16 516 | CONFIG_LWIP_MAX_LISTENING_TCP=16 517 | CONFIG_TCP_MAXRTX=12 518 | CONFIG_TCP_SYNMAXRTX=6 519 | CONFIG_TCP_MSS=1436 520 | CONFIG_TCP_MSL=60000 521 | CONFIG_TCP_SND_BUF_DEFAULT=5744 522 | CONFIG_TCP_WND_DEFAULT=5744 523 | CONFIG_TCP_RECVMBOX_SIZE=6 524 | CONFIG_TCP_QUEUE_OOSEQ=y 525 | CONFIG_ESP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES= 526 | CONFIG_TCP_OVERSIZE_MSS=y 527 | CONFIG_TCP_OVERSIZE_QUARTER_MSS= 528 | CONFIG_TCP_OVERSIZE_DISABLE= 529 | 530 | # 531 | # UDP 532 | # 533 | CONFIG_LWIP_MAX_UDP_PCBS=16 534 | CONFIG_UDP_RECVMBOX_SIZE=6 535 | CONFIG_TCPIP_TASK_STACK_SIZE=2560 536 | CONFIG_TCPIP_TASK_AFFINITY_NO_AFFINITY=y 537 | CONFIG_TCPIP_TASK_AFFINITY_CPU0= 538 | CONFIG_TCPIP_TASK_AFFINITY_CPU1= 539 | CONFIG_TCPIP_TASK_AFFINITY=0x7FFFFFFF 540 | CONFIG_PPP_SUPPORT= 541 | 542 | # 543 | # ICMP 544 | # 545 | CONFIG_LWIP_MULTICAST_PING= 546 | CONFIG_LWIP_BROADCAST_PING= 547 | 548 | # 549 | # LWIP RAW API 550 | # 551 | CONFIG_LWIP_MAX_RAW_PCBS=16 552 | 553 | # 554 | # mbedTLS 555 | # 556 | CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y 557 | CONFIG_MBEDTLS_DEFAULT_MEM_ALLOC= 558 | CONFIG_MBEDTLS_CUSTOM_MEM_ALLOC= 559 | CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN=y 560 | CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384 561 | CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 562 | CONFIG_MBEDTLS_DEBUG= 563 | CONFIG_MBEDTLS_HARDWARE_AES=y 564 | CONFIG_MBEDTLS_HARDWARE_MPI=y 565 | CONFIG_MBEDTLS_MPI_USE_INTERRUPT= 566 | CONFIG_MBEDTLS_HARDWARE_SHA=y 567 | CONFIG_MBEDTLS_HAVE_TIME=y 568 | CONFIG_MBEDTLS_HAVE_TIME_DATE= 569 | CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y 570 | CONFIG_MBEDTLS_TLS_SERVER_ONLY= 571 | CONFIG_MBEDTLS_TLS_CLIENT_ONLY= 572 | CONFIG_MBEDTLS_TLS_DISABLED= 573 | CONFIG_MBEDTLS_TLS_SERVER=y 574 | CONFIG_MBEDTLS_TLS_CLIENT=y 575 | CONFIG_MBEDTLS_TLS_ENABLED=y 576 | 577 | # 578 | # TLS Key Exchange Methods 579 | # 580 | CONFIG_MBEDTLS_PSK_MODES=y 581 | CONFIG_MBEDTLS_KEY_EXCHANGE_PSK=y 582 | CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_PSK=y 583 | CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_PSK=y 584 | CONFIG_MBEDTLS_KEY_EXCHANGE_RSA_PSK=y 585 | CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y 586 | CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA=y 587 | CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y 588 | CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y 589 | CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y 590 | CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=y 591 | CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=y 592 | CONFIG_MBEDTLS_SSL_RENEGOTIATION=y 593 | CONFIG_MBEDTLS_SSL_PROTO_SSL3= 594 | CONFIG_MBEDTLS_SSL_PROTO_TLS1=y 595 | CONFIG_MBEDTLS_SSL_PROTO_TLS1_1=y 596 | CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y 597 | CONFIG_MBEDTLS_SSL_PROTO_DTLS= 598 | CONFIG_MBEDTLS_SSL_ALPN=y 599 | CONFIG_MBEDTLS_SSL_SESSION_TICKETS=y 600 | 601 | # 602 | # Symmetric Ciphers 603 | # 604 | CONFIG_MBEDTLS_AES_C=y 605 | CONFIG_MBEDTLS_CAMELLIA_C= 606 | CONFIG_MBEDTLS_DES_C= 607 | CONFIG_MBEDTLS_RC4_DISABLED=y 608 | CONFIG_MBEDTLS_RC4_ENABLED_NO_DEFAULT= 609 | CONFIG_MBEDTLS_RC4_ENABLED= 610 | CONFIG_MBEDTLS_BLOWFISH_C= 611 | CONFIG_MBEDTLS_XTEA_C= 612 | CONFIG_MBEDTLS_CCM_C=y 613 | CONFIG_MBEDTLS_GCM_C=y 614 | CONFIG_MBEDTLS_RIPEMD160_C= 615 | 616 | # 617 | # Certificates 618 | # 619 | CONFIG_MBEDTLS_PEM_PARSE_C=y 620 | CONFIG_MBEDTLS_PEM_WRITE_C=y 621 | CONFIG_MBEDTLS_X509_CRL_PARSE_C=y 622 | CONFIG_MBEDTLS_X509_CSR_PARSE_C=y 623 | CONFIG_MBEDTLS_ECP_C=y 624 | CONFIG_MBEDTLS_ECDH_C=y 625 | CONFIG_MBEDTLS_ECDSA_C=y 626 | CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=y 627 | CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=y 628 | CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y 629 | CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y 630 | CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=y 631 | CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=y 632 | CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=y 633 | CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=y 634 | CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=y 635 | CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y 636 | CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y 637 | CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y 638 | CONFIG_MBEDTLS_ECP_NIST_OPTIM=y 639 | 640 | # 641 | # mDNS 642 | # 643 | CONFIG_MDNS_MAX_SERVICES=10 644 | 645 | # 646 | # ESP-MQTT Configurations 647 | # 648 | CONFIG_MQTT_PROTOCOL_311=y 649 | CONFIG_MQTT_TRANSPORT_SSL=y 650 | CONFIG_MQTT_TRANSPORT_WEBSOCKET=y 651 | CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y 652 | CONFIG_MQTT_USE_CUSTOM_CONFIG= 653 | CONFIG_MQTT_TASK_CORE_SELECTION_ENABLED= 654 | CONFIG_MQTT_CUSTOM_OUTBOX= 655 | 656 | # 657 | # NVS 658 | # 659 | 660 | # 661 | # OpenSSL 662 | # 663 | CONFIG_OPENSSL_DEBUG= 664 | CONFIG_OPENSSL_ASSERT_DO_NOTHING=y 665 | CONFIG_OPENSSL_ASSERT_EXIT= 666 | 667 | # 668 | # PThreads 669 | # 670 | CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5 671 | CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=2048 672 | CONFIG_PTHREAD_STACK_MIN=768 673 | 674 | # 675 | # SPI Flash driver 676 | # 677 | CONFIG_SPI_FLASH_VERIFY_WRITE= 678 | CONFIG_SPI_FLASH_ENABLE_COUNTERS= 679 | CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y 680 | CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y 681 | CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS= 682 | CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED= 683 | CONFIG_SPI_FLASH_YIELD_DURING_ERASE=y 684 | CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS=20 685 | CONFIG_SPI_FLASH_ERASE_YIELD_TICKS=1 686 | 687 | # 688 | # SPIFFS Configuration 689 | # 690 | CONFIG_SPIFFS_MAX_PARTITIONS=3 691 | 692 | # 693 | # SPIFFS Cache Configuration 694 | # 695 | CONFIG_SPIFFS_CACHE=y 696 | CONFIG_SPIFFS_CACHE_WR=y 697 | CONFIG_SPIFFS_CACHE_STATS= 698 | CONFIG_SPIFFS_PAGE_CHECK=y 699 | CONFIG_SPIFFS_GC_MAX_RUNS=10 700 | CONFIG_SPIFFS_GC_STATS= 701 | CONFIG_SPIFFS_PAGE_SIZE=256 702 | CONFIG_SPIFFS_OBJ_NAME_LEN=32 703 | CONFIG_SPIFFS_USE_MAGIC=y 704 | CONFIG_SPIFFS_USE_MAGIC_LENGTH=y 705 | CONFIG_SPIFFS_META_LENGTH=4 706 | CONFIG_SPIFFS_USE_MTIME=y 707 | 708 | # 709 | # Debug Configuration 710 | # 711 | CONFIG_SPIFFS_DBG= 712 | CONFIG_SPIFFS_API_DBG= 713 | CONFIG_SPIFFS_GC_DBG= 714 | CONFIG_SPIFFS_CACHE_DBG= 715 | CONFIG_SPIFFS_CHECK_DBG= 716 | CONFIG_SPIFFS_TEST_VISUALISATION= 717 | 718 | # 719 | # TCP/IP Adapter 720 | # 721 | CONFIG_IP_LOST_TIMER_INTERVAL=120 722 | CONFIG_TCPIP_LWIP=y 723 | 724 | # 725 | # Virtual file system 726 | # 727 | CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT=y 728 | CONFIG_SUPPORT_TERMIOS=y 729 | 730 | # 731 | # Wear Levelling 732 | # 733 | CONFIG_WL_SECTOR_SIZE_512= 734 | CONFIG_WL_SECTOR_SIZE_4096=y 735 | CONFIG_WL_SECTOR_SIZE=4096 736 | --------------------------------------------------------------------------------