diff --git a/README.md b/README.md index f4ffbccee..150ebaaa1 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ Please note that when sending to a Bluetooth speaker (source), only 44.1 kHz can Most DAC will work out-of-the-box with simply an I2S connection, but some require specific commands to be sent using I2C. See DAC option below to understand how to send these dedicated commands. There is build-in support for TAS575x, TAS5780, TAS5713 and AC101 DAC. ### Raw WROOM esp32-s3 module -The esp32-s3 based modules like [this]@(https://www.espressif.com/sites/default/files/documentation/esp32-s3-wroom-1_wroom-1u_datasheet_en.pdf) are also supported but requires esp-idf 4.4. It is not yet part of official releases, but it compiles & runs. The s3 does not have bluetooth audio. Note that CPU performances are greatly enhanced. +The esp32-s3 based modules like [this](https://www.espressif.com/sites/default/files/documentation/esp32-s3-wroom-1_wroom-1u_datasheet_en.pdf) are also supported but requires esp-idf 4.4. It is not yet part of official releases, but it compiles & runs. The s3 does not have bluetooth audio. Note that CPU performances are greatly enhanced. ### SqueezeAMP This is the main hardware companion of Squeezelite-esp32 and has been developped together. Details on capabilities can be found [here](https://forums.slimdevices.com/showthread.php?110926-pre-ANNOUNCE-SqueezeAMP-and-SqueezeliteESP32) and [here](https://github.com/philippe44/SqueezeAMP). @@ -389,11 +389,12 @@ Where (all parameters are optionals except gpio) Where `` is either the name of another configuration to load (remap) or one amongst ``` -ACTRLS_NONE, ACTRLS_SLEEP, ACTRLS_POWER, ACTRLS_VOLUP, ACTRLS_VOLDOWN, ACTRLS_TOGGLE, ACTRLS_PLAY, +ACTRLS_NONE, ACTRLS_POWER, ACTRLS_VOLUP, ACTRLS_VOLDOWN, ACTRLS_TOGGLE, ACTRLS_PLAY, ACTRLS_PAUSE, ACTRLS_STOP, ACTRLS_REW, ACTRLS_FWD, ACTRLS_PREV, ACTRLS_NEXT, BCTRLS_UP, BCTRLS_DOWN, BCTRLS_LEFT, BCTRLS_RIGHT, BCTRLS_PS1, BCTRLS_PS2, BCTRLS_PS3, BCTRLS_PS4, BCTRLS_PS5, BCTRLS_PS6, BCTRLS_PS7, BCTRLS_PS8, BCTRLS_PS9, BCTRLS_PS10, -KNOB_LEFT, KNOB_RIGHT, KNOB_PUSH, +KNOB_LEFT, KNOB_RIGHT, KNOB_PUSH, +ACTRLS_SLEEP, ``` Note that ACTRLS_SLEEP is not an actual button that can be sent to LMS, but it's a hook to activate deep sleep mode (see [Sleeping](#sleeping)). @@ -508,11 +509,11 @@ NB: Set parameter to empty to disable battery reading. For named configurations ### Sleeping The esp32 can be put in deep sleep mode to save some power. How much really depends on the connected periperals, so best is to do your own measures. Waking-up from deep sleep is the equivalent of a reboot, but as the chip takes a few seconds to connect, it's still an efficient process. -The esp32 can enter deep sleep after an audio inactivity timeout, after a button has been pressed or after a GPIO is set to a given level. It wakes up only on GPIO events +The esp32 can enter deep sleep after an audio inactivity timeout, after a button has been pressed or after a GPIO is set to a given level. It wakes up only on some GPIO events. Note that *all* GPIO are isolated when sleeping (unless they are set with the `rtc`option) so you can not assume anything about their value, except that they will not drain current. The `rtc` option allows to keep some GPIO (from the RTC domain only) either pulled up or down. This can be useful if you want to keep some periperal active, for example a GPIO expander whose interrupt will be used to wake-up the system. The NVS parameter `sleep_config` is mostly used for setting sleep conditions ``` -[delay=][,sleep=[:0|1]][,wake=[:0|1][|[:0|1]...] +[delay=][,sleep=[:0|1]][,wake=[:0|1][|[:0|1]...][,rtc=[:0|1][|[:0|1]...] ``` - delay is in **minutes** - sleep is the GPIO that will put the system into sleep and it can be a level 0 or 1 @@ -524,10 +525,12 @@ Please see [buttons](#buttons) for detailed syntax. The option to use multiple GPIOs is very limited on esp32 and the esp-idf 4.3.x we are using: it is only possible to wake-up when **any** of the defined GPIO is set to 1. The fact that you can specify different levels in the wake list is irrelevant for now, it's just a provision for future upgrades to more recent versions of esp-idf. -**Note that not all GPIOs can be used to wake-up the esp32** +**Only the following GPIOs can be used to wake-up the esp32** - ESP32: 0, 2, 4, 12-15, 25-27, 32-39; - ESP32-S3: 0-21. +Some have asked for a soft power on/off option. Although this is not built-in, it's easy to create yours as long as the regulator/power supply of the board can be controlled by Vcc or GND. Depending on how it is active, add a pull-up/down resistor to the regulator's control and connect it also to one GPIO of the esp32. Then using set_GPIO, set that GPIO to Vcc or GND. Use a hardware button that forces the regulator on with a pull- up/down and once the esp32 has booted, it will force the GPIO to the desired value maintaining the board on by software. To power it off by software, just use the deep sleep option which will suspend all GPIO hence switching off the regulator. + # Configuration ## Setup WiFi diff --git a/components/services/audio_controls.c b/components/services/audio_controls.c index 60792ccf2..b071bc082 100644 --- a/components/services/audio_controls.c +++ b/components/services/audio_controls.c @@ -58,11 +58,11 @@ static const actrls_config_map_t actrls_config_map[] = // BEWARE: the actions below need to stay aligned with the corresponding enum to properly support json parsing // along with the actrls_t controls in LMS_controls, bt_sink and raop_sink #define EP(x) [x] = #x /* ENUM PRINT */ -static const char * actrls_action_s[ ] = { EP(ACTRLS_SLEEP),EP(ACTRLS_POWER),EP(ACTRLS_VOLUP),EP(ACTRLS_VOLDOWN),EP(ACTRLS_TOGGLE),EP(ACTRLS_PLAY), +static const char * actrls_action_s[ ] = { EP(ACTRLS_POWER),EP(ACTRLS_VOLUP),EP(ACTRLS_VOLDOWN),EP(ACTRLS_TOGGLE),EP(ACTRLS_PLAY), EP(ACTRLS_PAUSE),EP(ACTRLS_STOP),EP(ACTRLS_REW),EP(ACTRLS_FWD),EP(ACTRLS_PREV),EP(ACTRLS_NEXT), EP(BCTRLS_UP),EP(BCTRLS_DOWN),EP(BCTRLS_LEFT),EP(BCTRLS_RIGHT), EP(BCTRLS_PS0),EP(BCTRLS_PS1),EP(BCTRLS_PS2),EP(BCTRLS_PS3),EP(BCTRLS_PS4),EP(BCTRLS_PS5),EP(BCTRLS_PS6),EP(BCTRLS_PS7),EP(BCTRLS_PS8),EP(BCTRLS_PS9), - EP(KNOB_LEFT),EP(KNOB_RIGHT),EP(KNOB_PUSH), + EP(KNOB_LEFT),EP(KNOB_RIGHT),EP(KNOB_PUSH), EP(ACTRLS_SLEEP), ""} ; static const char * TAG = "audio controls"; diff --git a/components/services/audio_controls.h b/components/services/audio_controls.h index 137bda7e6..0c5f5fd26 100644 --- a/components/services/audio_controls.h +++ b/components/services/audio_controls.h @@ -11,12 +11,12 @@ #include "buttons.h" // BEWARE: this is the index of the array of action below (change actrls_action_s as well!) -typedef enum { ACTRLS_NONE = -1, ACTRLS_SLEEP, ACTRLS_POWER, ACTRLS_VOLUP, ACTRLS_VOLDOWN, ACTRLS_TOGGLE, ACTRLS_PLAY, +typedef enum { ACTRLS_NONE = -1, ACTRLS_POWER, ACTRLS_VOLUP, ACTRLS_VOLDOWN, ACTRLS_TOGGLE, ACTRLS_PLAY, ACTRLS_PAUSE, ACTRLS_STOP, ACTRLS_REW, ACTRLS_FWD, ACTRLS_PREV, ACTRLS_NEXT, BCTRLS_UP, BCTRLS_DOWN, BCTRLS_LEFT, BCTRLS_RIGHT, BCTRLS_PS0,BCTRLS_PS1,BCTRLS_PS2,BCTRLS_PS3,BCTRLS_PS4,BCTRLS_PS5,BCTRLS_PS6,BCTRLS_PS7,BCTRLS_PS8,BCTRLS_PS9, KNOB_LEFT, KNOB_RIGHT, KNOB_PUSH, - ACTRLS_REMAP, ACTRLS_MAX + ACTRLS_REMAP, ACTRLS_SLEEP, ACTRLS_MAX } actrls_action_e; typedef void (*actrls_handler)(bool pressed); diff --git a/components/services/led.c b/components/services/led.c index decf2f8f4..0c9d42a6f 100644 --- a/components/services/led.c +++ b/components/services/led.c @@ -283,7 +283,8 @@ void set_led_gpio(int gpio, char *value) { struct led_config_s *config; if (strcasestr(value, "green")) config = &green; - else config = &red; + else if (strcasestr(value, "red"))config = &red; + else return; config->gpio = gpio; char *p = value; diff --git a/components/services/services.c b/components/services/services.c index 6ace648b6..b301d8d3f 100644 --- a/components/services/services.c +++ b/components/services/services.c @@ -45,6 +45,7 @@ pwm_system_t pwm_system = { static EXT_RAM_ATTR struct { uint64_t wake_gpio, wake_level; + uint64_t rtc_gpio, rtc_level; uint32_t delay; } sleep_config; @@ -136,6 +137,28 @@ static void sleep_init(void) { ESP_LOGI(TAG, "Sleep wake-up gpio bitmap 0x%llx (active 0x%llx)", sleep_config.wake_gpio, sleep_config.wake_level); } } + + // get the rtc-pull criteria + if ((p = strcasestr(config, "rtc"))) { + char list[32] = "", item[8]; + sscanf(p, "%*[^=]=%31[^,]", list); + p = list - 1; + while (p++ && sscanf(p, "%7[^|]", item)) { + int level = 0, gpio = atoi(item); + if (!rtc_gpio_is_valid_gpio(gpio)) { + ESP_LOGE(TAG, "invalid rtc GPIO %d", gpio); + } else { + sleep_config.rtc_gpio |= 1LL << gpio; + } + if (sscanf(item, "%*[^:]:%d", &level)) sleep_config.rtc_level |= level << gpio; + p = strchr(p, '|'); + } + + // when moving to esp-idf more recent than 4.4.x, multiple gpio wake-up with level specific can be done + if (sleep_config.rtc_gpio) { + ESP_LOGI(TAG, "RTC forced gpio bitmap 0x%llx (active 0x%llx)", sleep_config.rtc_gpio, sleep_config.rtc_level); + } + } // then get the gpio that activate sleep (we could check that we have a valid wake) if ((p = strcasestr(config, "sleep"))) { @@ -165,22 +188,37 @@ void services_sleep_activate(sleep_cause_e cause) { // call all sleep hooks that might want to do something for (void (**hook)(void) = sleep_hooks; *hook; hook++) (*hook)(); - // isolate all possible GPIOs, except the wake-up ones + // isolate all possible GPIOs, except the wake-up and RTC-maintaines ones esp_sleep_config_gpio_isolate(); + + // keep RTC domain up if we need to maintain pull-up/down of some GPIO from RTC + if (sleep_config.rtc_gpio) esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); + for (int i = 0; i < GPIO_NUM_MAX; i++) { - if (!rtc_gpio_is_valid_gpio(i) || ((1LL << i) & sleep_config.wake_gpio)) continue; - rtc_gpio_isolate(i); + // must be a RTC GPIO + if (!rtc_gpio_is_valid_gpio(i)) continue; + + // do we need to maintain a pull-up or down of that GPIO + if ((1LL << i) & sleep_config.rtc_gpio) { + if ((sleep_config.rtc_level >> i) & 0x01) rtc_gpio_pullup_en(i); + else rtc_gpio_pulldown_en(i); + // or is this not wake-up GPIO, just isolate it + } else if (!((1LL << i) & sleep_config.wake_gpio)) { + rtc_gpio_isolate(i); + } } // is there just one GPIO if (sleep_config.wake_gpio & (sleep_config.wake_gpio - 1)) { ESP_LOGI(TAG, "going to sleep cause %d, wake-up on multiple GPIO, any '1' wakes up 0x%llx", cause, sleep_config.wake_gpio); esp_sleep_enable_ext1_wakeup(sleep_config.wake_gpio, ESP_EXT1_WAKEUP_ANY_HIGH); - } else { + } else if (sleep_config.wake_gpio) { int gpio = __builtin_ctz(sleep_config.wake_gpio); int level = (sleep_config.wake_level >> gpio) & 0x01; ESP_LOGI(TAG, "going to sleep cause %d, wake-up on GPIO %d level %d", cause, gpio, level); esp_sleep_enable_ext0_wakeup(gpio, level); + } else { + ESP_LOGW(TAG, "going to sleep cause %d, no wake-up option", cause); } // we need to use a timer in case the same button is used for sleep and wake-up and it's "pressed" vs "released" selected