sle118 / squeezelite-esp32

ESP32 Music streaming based on Squeezelite, with support for multi-room sync, AirPlay, Bluetooth, Hardware buttons, display and more
1.2k stars 108 forks source link

Deep sleep and external wakeup #300

Open greggailly opened 1 year ago

greggailly commented 1 year ago

Hi, Is there a way to put the esp32 in deep_sleep after a certain time without "activity" and then wake up from the deep_sleep with a GPIO pin ? While looking into the code, I've found the deep_sleep console command with the --io option which works perfectly well in the console but I can't seem to find a way to execute this either after a certain time without "activity" or (maybe easier at first) with an external button.

Any hints appreciated.

Thanks

philippe44 commented 1 year ago

I have not made anything to maintain properly clocks and sync and all that jazz (and there is a lot), so I have no idea of what happens really when you do that. Why not switching it off?

Renber78 commented 1 year ago

If your goal is to turn off the device in order to save battery power, it is possible to do this with a small hardware modification to the power supply.

You would then have a push button on the device to start the power. The LMS server will be able to cut off power during standby but will no longer be able to turn it back on since the ESP will no longer be powered (this button must be pressed). As for automatic shutdown after a certain time, there is an LMS plugin (PowerSave) that does this very well.

The idea being to add a transistor between your power supply and your hardware whose trigger will be controlled by the button and then maintained by a GPIO when the ESP has started. (The LED power output will probably be perfect for this, but I haven't checked)

If this solution interests you, I can describe it to you in more detail.

philippe44 commented 1 year ago

I also assume that if we do that, there will be requests for using a long-press of an existing button or a shift-press to enter sleep mode. So that means it should be a feature of "buttons". But then, if you want to use "lms_ctrls_raw" option to simply send raw information to LMS, that will not work. In addition, it means that you must define a button set just to have a GPIO that enters in to sleep, even if you don't want anything else.

I now have typically contradictory requirements. You want "buttons" but you don't want "buttons" and doing both will be a nightmare... sigh. I'll have to think about it to not implement something quick and dirty that I'll regret immediately after.

greggailly commented 1 year ago

Thank you to you both ! I'll look more into a hardware solution that controls power all together and the PowerSave plugin @Renber78 suggested. I was looking into deep sleep to make it more flexible for the wakeup part (using a gpio pin or timer based) but for now I will probably go with switching off as the main goal is indeed to save power.

philippe44 commented 1 year ago

I've added a sleep option, see https://github.com/sle118/squeezelite-esp32#sleeping. Let me know if it works for you

Renber78 commented 1 year ago

Several quick observations:

But I think that if the project must be used with batteries, it is better to implement a hardware solution because deep sleep will only cut off the ESP. The dac, display or other accessory will not be cut off and will still drain the battery over time. The hardware solution that I proposed above is identical to a power button that you have on any commercial battery-powered Bluetooth speaker.

If you wish, I can take care of making a test and an electronic diagram for users.

philippe44 commented 1 year ago

Thanks for testing out. The infinite loop when no wake is solved now. The wake gpio must be in the rtc domain and 19 is not. Now, it still can work with an expander if the interrupt of that expander is in the rtc domain. That's one use case I tested. On the backlight topic, how is it connected on your board? I would think that if it is using a GPIO, it will automatically switch off. Now, on my test device, it is not connected so it remains up indeed.

Schematics to be added to README would always be a nice addition. I also added a comment on soft on/off and that can be elaborated by a lot

Renber78 commented 1 year ago

Now wake only works, I also tested the interruption of my extender and it's good. On the other hand, when I put my IR sensor on I have a loop (may be linked to the set_GPIO bug that I reported in another post) I will pass the scope over it during the day to ensure that the IR sensor does not send any noise. For my LCD, in fact it did not have a lighting pin... I have several... and I got confused. I tested another LCD and it's ok too. Good day !

Renber78 commented 1 year ago

Also, in the Readme, this sentence misled me:

Note that not all GPIOs can be used to wake-up the esp32

ESP32: 0, 2, 4, 12-15, 25-27, 32-39; ESP32-S3: 0-21.

I understood that these are the pins that I cannot use... The sentence could be turned differently for greater clarity.

philippe44 commented 1 year ago

Agreed, I've changed it. The backlight is also difficult to handle as there is no option to turn it off by SW. On ST7789, the register 53 does not seem to work as expected. So basically, it's left to the fact that there is a pull-down on that line and if it is connected to the esp32, when it goes on sleep, then the pulldown switches it off.

Renber78 commented 1 year ago

After playing with it a bit, I noticed this:

My test config: delay=1,wake=13:0|33:0

sle118 commented 1 year ago

I'm happy to see this made some progress. It will never be as efficient as a real power cutting option, but it will also make a difference on battery operated devices as cutting radios will reduce power consumption by a lot. It's enough to extend the charge if one forgot to turn the power off manually with a switch. My little Bose Bluetooth speakers died because the battery drained over time, so consuming power during sleep is common I would say

philippe44 commented 1 year ago

For the 3rd point, unfortunately that a limit of esp32. If you look at the README, when using multiple wakes, it wakes-up if at least one of them is 1 and this is the only criteria, sadly. For the sleep after playing, I'll look and for the activity detection, it's true that it's only based on audio.

sle118 commented 1 year ago

you might want to add an option to set RTC pin levels before going to sleep: low=x|y|z,high=a|b|c for the pins that support it. this would provide a path for controlling hardware during sleep

philippe44 commented 1 year ago

you might want to add an option to set RTC pin levels before going to sleep: low=x|y|z,high=a|b|c for the pins that support it. this would provide a path for controlling hardware during sleep

It's already there as the "rtc" option

sle118 commented 1 year ago

I have to admit I had not read the documentation before posting my previous comment, so thank you for educating me. Reading your explanation, I'm thinking that the SqueezeAmp might be a good candidate for the control of the power regulator in deep sleep. I am referring to the tiny header that was made available for a switch. Am I interpreting your documentation correctly?

philippe44 commented 1 year ago

I have to admit I had not read the documentation before posting my previous comment, so thank you for educating me. Reading your explanation, I'm thinking that the SqueezeAmp might be a good candidate for the control of the power regulator in deep sleep. I am referring to the tiny header that was made available for a switch. Am I interpreting your documentation correctly?

Yes absolutely. It requires some flying wires obviously

Renber78 commented 1 year ago

In order to keep the batteries in good condition, it would also be interesting to automatically go to sleep when the battery voltage reaches a critical threshold. This could be added to the battery configuration line. A little luxury: The display shows the transition to sleep mode and low battery. ;-)

sle118 commented 1 year ago

@Renber78 now this is what I can scope creep 🤣

Renber78 commented 1 year ago

Yes absolutely. It requires some flying wires obviously it is not so simple...

Don't forget that when the power is cut there will no longer be 3V3 on the ESP, nor pull-up, nor RTC. On the SqueezeAMP, J7 must be held at GND to shut down, but without power to the ESP the holding GPIO will be unable to pull to ground.

VCC also cannot be used directly as a voltage source for a button pull-up, it would burn the GPIO.

It will necessarily be necessary to go through transistors...

Renber78 commented 1 year ago

Well, I spoke too quickly... by doing the opposite logic, it goes without much quibble.

I haven't tested it because I don't have this hardware but on paper this solution seems to be the right one for the SqueezeAMP.

Also, by adding a P-MOS between VCC and J7 as well as a pull-up resistor on its trigger, you could use an already existing button to wake up the system.

If I have a little more time, I will make different possible diagrams tomorrow.

philippe44 commented 1 year ago

ok - I've added buttons/rotary/ir in criteria for not sleeping as well as BT when it's a sink. I've also added a battery option. End of feature creep, let's see how it works.

NB: I've also activated LED when BT is a sink (@sle118 - don't be mad 😄)

Renber78 commented 1 year ago

Good job, the points I pointed out are well corrected. I haven't tested sleep vs battery voltage yet.

Renber78 commented 1 year ago

squeeze_sleep

Here is a first draft for the various diagrams of a hardware solution. Nothing has been tested yet but it should work. Make your comments.

philippe44 commented 1 year ago

Silly question: on 3/ can't we just have something like that or is this an issue with transient before the zener limits the voltage Screenshot 2023-09-17 18 30 17

Renber78 commented 1 year ago

Yes, you can do like this if your existing button is to Vbatt. I assumed that it was to GND (which is generally the most used method) Your diagram still has a big advantage, you don't have R3 which pumps a slight current from the battery!

So I will add this scenario.

For transitions, nothing prevents us from replacing the zener with a TVS or even a resistor (which should be sized according to V_batt).

philippe44 commented 1 year ago

A TVS is probably the better choice then because I was thinking of the case where that soft on/off is after the vcc/vbatt switch so it can have a large swing of values, so making sure the gpio button voltage on the esp32 is imposed is important, no?

Renber78 commented 1 year ago

Yes I will place a TVS it is indeed safer.

Something else: I saw your last note regarding using the IR sensor as an wakes up I re-tested this and it doesn't work for me, the system wakes up alone. (my config: wake=34:0) I checked my IR signal which is at 1 and does not move. Have you tested this? Is there a problem with the GPIO34?

philippe44 commented 1 year ago

Yes I've tested it on a SqueezeAMP. Why wouldn't it move? By definition, the IR signal changes when receiving something. So it will transition to the wake-up value of 0.

Renber78 commented 1 year ago

When I say that it doesn't move, it's when there is no reception of course (which means that I don't have any disturbing noise that will wake up the ESP). But I have the same problem with the central knob (GPIO33) which is also on pull-up. On the other hand, with the interrupt of the extender (GPIO13) it works (yet it is exactly the same principle as the others, with pull-up, active at 0)

As a reminder: The problem is that it wakes up directly after being put on standby

I think there is really a problem with GPIO3x

Here is the wake-up log:

I (91938) sleep: Configure to isolate all GPIO pins in sleep state
I (91938) services: going to sleep cause 0, wake-up on GPIO -1 level 0

GPIO -1 is not normal

edit: I confirm that all GPIOs greater than 27 do not work for wake-up (v1558)

philippe44 commented 1 year ago

Indeed, no GPIO over 31 would work because I forgot that the built-in leading zeros count function by default is 32 bits 😞. I've now changed it for the 64 bits version and tested that GPIO 34 is taken into account.

sle118 commented 1 year ago

Indeed, no GPIO over 31 would work because I forgot that the built-in leading zeros count function by default is 32 bits 😞. I've now changed it for the 64 bits version and tested that GPIO 34 is taken into account. ....and there was one bit per gpio so 32 gpios max. optimization is never easy 😄

philippe44 commented 1 year ago

BTW I've added something after discussing with @sle118 to handle IR spurious wakes as much as possible.

If you want to wake up on IR, as soon as the gpio toggles, it will wake up the esp32 whether it's a valid command or not and it's impossible to know if it is, you need to be booted up.

So the system will, if it woken up due to ir, enter a period of "spurious" minutes after which if there is no activity, it will go back to sleep.

It happens whether there is an inactivity delay or not.

Renber78 commented 1 year ago

GOOD. I don't see any more problems now.

Two small details without too much importance:

Good day !

philippe44 commented 1 year ago

The delay is by chunks of 30s, so yes it's not accurate but I did not think it matters. For the display, there is a DisplayOff before sleeping, but there is scheduling and I2S involved and it might happen that there is a freeze because the command has not been executed.

I'm always trying to reduce cpu and memory while keeping things basics so the sleep system is the possibility for any software components to register a callback that tells how long its has been inactive. All callbacks are called every 30s to evaluate if sleep is possible. Another set of callbacks can be registered and are called when the system has decided to sleep. It's not really a democracy, you can't refuse to go to sleep, but you can always cheat and say you're busy although you have not done anything ... sounds familiar suddenly 😃

Renber78 commented 1 year ago

squeeze_sleep

I reworked the diagrams. You will notice that I did not place a TVS... I solved the problem the old-fashioned way ;-)

I also changed my approach on diagrams 3 and 4 by reversing the logic of the output to the key GPIO. There is no longer any battery consumption when stopping the system on all diagrams now.

wizmo2 commented 1 year ago

A little off topic, but could you look at implementing the set_GPIO `power' output in recovery mode? I set the output using the :1 option, but peripherals are currently only powered in while in ota.

philippe44 commented 1 year ago

I'm very reluctant to do that because I really don't like to do config reading in recovery. I already think that we do way too much of it now and it should be removed. Recovery should be recovery, and fully minimalistic

wizmo2 commented 1 year ago

No worries, I think vcc is what I actually need, and it looks like you initialize that.

sle118 commented 1 year ago

And this doesn't even take into account the lack of room in recovery. We're counting bytes here.

@philippe44 I think we should remove some "set default" from the recovery of you haven't done that already. Perhaps having a weak function in the main entry point could be helpful to segregate that at linking.

philippe44 commented 1 year ago

Don't you think that removing the register_xxx as I started to do is a better choice?

wizmo2 commented 1 year ago

I've been working on this while trying to build a targeted install for the s3 project I'm working on.
Was starting to prepare a a PR to clean up the UI, along with the some of the Konfig.build settings.

My current build hides everything that was pre-configured, but there are settings that include system parameters and user preferences (for example, rotary encoder settings and display orientation),

IMO, I think you should consider reviewing this and separate pre-defined hardware vrs optional settings. I believe you already have the basics for this, with nvs defaults and overrides.

If you need some help, more than willing,