Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deep Sleep WIP #11

Open
wants to merge 1,547 commits into
base: platform/libretuya
Choose a base branch
from
Open

Conversation

Xmister
Copy link

@Xmister Xmister commented Jun 15, 2023

This is an initial try on Deep Sleep for the Beken platform.
Most of the code are tailored from OpenBeken.
Platform Changes
BDK Changes

NOTE: I'm still having linking issues, and can't figure out why it can't find a definition for bk_enter_deep_sleep_mode

Maybe someone can help me figure out the last steps. @kuba2k2 maybe?

@kuba2k2
Copy link
Member

kuba2k2 commented Jun 15, 2023

Let's keep the discussion in the main PR (the platform one). I'll reply there tomorrow with a little review.

@samhunt
Copy link

samhunt commented Jul 18, 2023

Just wanted to leave my 2c here of testing that I have done with the deep-sleep functionality on my Door Sensor. FWIW, the device is going to sleep, but it is constantly waking up every 30 seconds (without changing the wakeup pin) as is shown in the logs below. I have also attached my device yaml if there is something that I have done wrong in the config.

I'll keep playing around to see if there's anything I can see as to why it keeps waking

INFO Successfully connected to 192.168.0.97
[01:07:36][I][deep_sleep:136]: Beginning Deep Sleep
[01:07:36][D][lt.preferences:104]: Saving 1 preferences to flash...
[01:07:36][D][lt.preferences:132]: Saving 1 preferences to flash: 0 cached, 1 written, 0 failed
INFO Processing expected disconnect from ESPHome API for 192.168.0.97
WARNING Disconnected from API
INFO Successfully connected to 192.168.0.97
[01:08:10][I][deep_sleep:136]: Beginning Deep Sleep
[01:08:10][D][lt.preferences:104]: Saving 1 preferences to flash...
[01:08:11][D][lt.preferences:132]: Saving 1 preferences to flash: 0 cached, 1 written, 0 failed
INFO Processing expected disconnect from ESPHome API for 192.168.0.97
WARNING Disconnected from API
INFO Successfully connected to 192.168.0.97
[01:08:45][I][deep_sleep:136]: Beginning Deep Sleep
[01:08:45][D][lt.preferences:104]: Saving 1 preferences to flash...
[01:08:45][D][lt.preferences:132]: Saving 1 preferences to flash: 0 cached, 1 written, 0 failed
INFO Processing expected disconnect from ESPHome API for 192.168.0.97
WARNING Disconnected from API
INFO Successfully connected to 192.168.0.97
[01:09:20][I][deep_sleep:136]: Beginning Deep Sleep
[01:09:20][D][lt.preferences:104]: Saving 1 preferences to flash...
[01:09:20][D][lt.preferences:132]: Saving 1 preferences to flash: 0 cached, 1 written, 0 failed
INFO Processing expected disconnect from ESPHome API for 192.168.0.97
WARNING Disconnected from API
INFO Successfully connected to 192.168.0.97
[01:09:55][I][deep_sleep:136]: Beginning Deep Sleep
[01:09:55][D][lt.preferences:104]: Saving 1 preferences to flash...
[01:09:55][D][lt.preferences:132]: Saving 1 preferences to flash: 0 cached, 1 written, 0 failed
INFO Processing expected disconnect from ESPHome API for 192.168.0.97
WARNING Disconnected from API
INFO Successfully connected to 192.168.0.97
[01:10:29][I][deep_sleep:136]: Beginning Deep Sleep
[01:10:29][D][lt.preferences:104]: Saving 1 preferences to flash...
[01:10:29][D][lt.preferences:132]: Saving 1 preferences to flash: 0 cached, 1 written, 0 failed
INFO Processing expected disconnect from ESPHome API for 192.168.0.97
WARNING Disconnected from API
INFO Successfully connected to 192.168.0.97
[01:11:04][I][deep_sleep:136]: Beginning Deep Sleep
[01:11:04][D][lt.preferences:104]: Saving 1 preferences to flash...
[01:11:04][D][lt.preferences:132]: Saving 1 preferences to flash: 0 cached, 1 written, 0 failed
INFO Processing expected disconnect from ESPHome API for 192.168.0.97
WARNING Disconnected from API
INFO Successfully connected to 192.168.0.97
[01:11:39][I][deep_sleep:136]: Beginning Deep Sleep
[01:11:39][D][lt.preferences:104]: Saving 1 preferences to flash...
[01:11:39][D][lt.preferences:132]: Saving 1 preferences to flash: 0 cached, 1 written, 0 failed
INFO Processing expected disconnect from ESPHome API for 192.168.0.97
WARNING Disconnected from API
INFO Successfully connected to 192.168.0.97
substitutions:
  name: "door-sensor-laundry-02"
  wifi_ssid: !secret wifi_ssid
  wifi_password: !secret wifi_password
  static_ip: 192.168.0.97
  gateway: 192.168.0.1
  subnet: 255.255.255.0
  sensor_pin: P16

esphome:
  name: $name

bk72xx:
  board: generic-bk7231n-qfn32-tuya
  framework:
    version: dev

api:
 encryption:
   key: !secret api_password

ota:
  password: !secret ota_password

wifi:
  ssid: $wifi_ssid
  password: $wifi_password
  fast_connect: true
  manual_ip:
    static_ip: $static_ip
    gateway: $gateway
    subnet: $subnet

captive_portal:

mdns:

logger:

web_server:
  local: true
      
text_sensor:
  - platform: wifi_info
    ip_address:
      id: ip_address
      name: IP Address

binary_sensor:
  - platform: gpio
    pin: 
      number: $sensor_pin
      mode:
        input: true
        pullup: true
    name: $name Sensor
    device_class: door
    id: door

deep_sleep:
  wakeup_pin: $sensor_pin
  id: deep_sleep_1
  run_duration: 30s

@Xmister
Copy link
Author

Xmister commented Jul 18, 2023

@samhunt In my case multiple pins were reacting to a magnet, and I had to find the proper one for wake source by trial and error.
You can also try if using wakeup_pins (plural) makes any difference.

@samhunt
Copy link

samhunt commented Jul 28, 2023

@samhunt In my case multiple pins were reacting to a magnet, and I had to find the proper one for wake source by trial and error. You can also try if using wakeup_pins (plural) makes any difference.

Thanks, I'll give that a try

@alessiocappa
Copy link

@samhunt, did you manage to make it works?
I'm trying to configure deep sleep on a CBU board, linked to a PIR sensor, but it doesn't seem to work.

I've tried several combination of the deep_sleep parameters, but I always encounter the same problem.
Basically the device goes into deep sleep, but then after something like 5 seconds it wakes up without any changes on the PIN status.

Here is my current setup for deep_sleep and binary_sensor

deep_sleep:
  id: deep_sleep_pir
  run_duration: 15s
  wakeup_pin: P16
  wakeup_pin_mode: INVERT_WAKEUP

binary_sensor:
  - platform: gpio
    pin:
      number: P16
      mode:
        input: true
        pulldown: true
    name: "PIR Sensor"
    device_class: motion

What I noticed is that if I use the INVERT_WAKEUP option, the device goes to sleep and then it wakes up after 5 seconds even if the sensor value is ON or OFF.
If I remove it and keep the default IGNORE option, it does the same while the sensor state is OFF, and it stays in deep sleep if the state is ON until it goes back to OFF.

I tried also with both pullup and pulldown, but the result is the same.

I don't know if there is something wrong with my configuration or with the INVERT_WAKEUP option.
My goal is to put the device in sleep mode as soon as possible and wake it up only if there is a status change, which is what that option should do as far as I understood.

@samhunt
Copy link

samhunt commented Aug 22, 2023

@samhunt, did you manage to make it works? I'm trying to configure deep sleep on a CBU board, linked to a PIR sensor, but it doesn't seem to work.

I've tried several combination of the deep_sleep parameters, but I always encounter the same problem. Basically the device goes into deep sleep, but then after something like 5 seconds it wakes up without any changes on the PIN status.

Here is my current setup for deep_sleep and binary_sensor

deep_sleep:
  id: deep_sleep_pir
  run_duration: 15s
  wakeup_pin: P16
  wakeup_pin_mode: INVERT_WAKEUP

binary_sensor:
  - platform: gpio
    pin:
      number: P16
      mode:
        input: true
        pulldown: true
    name: "PIR Sensor"
    device_class: motion

What I noticed is that if I use the INVERT_WAKEUP option, the device goes to sleep and then it wakes up after 5 seconds even if the sensor value is ON or OFF. If I remove it and keep the default IGNORE option, it does the same while the sensor state is OFF, and it stays in deep sleep if the state is ON until it goes back to OFF.

I tried also with both pullup and pulldown, but the result is the same.

I don't know if there is something wrong with my configuration or with the INVERT_WAKEUP option. My goal is to put the device in sleep mode as soon as possible and wake it up only if there is a status change, which is what that option should do as far as I understood.

No I wasn't able to get libretiny to work. OpenBreken worked fine with the same device, and the batteries were able to last over a month instead of a day or so with this code.
I've ended up replacing my device with one that uses zigbee since the ~8 second deep_sleep start up time didn't work with my use case

@alessiocappa
Copy link

@samhunt, did you manage to make it works? I'm trying to configure deep sleep on a CBU board, linked to a PIR sensor, but it doesn't seem to work.
I've tried several combination of the deep_sleep parameters, but I always encounter the same problem. Basically the device goes into deep sleep, but then after something like 5 seconds it wakes up without any changes on the PIN status.
Here is my current setup for deep_sleep and binary_sensor

deep_sleep:
  id: deep_sleep_pir
  run_duration: 15s
  wakeup_pin: P16
  wakeup_pin_mode: INVERT_WAKEUP

binary_sensor:
  - platform: gpio
    pin:
      number: P16
      mode:
        input: true
        pulldown: true
    name: "PIR Sensor"
    device_class: motion

What I noticed is that if I use the INVERT_WAKEUP option, the device goes to sleep and then it wakes up after 5 seconds even if the sensor value is ON or OFF. If I remove it and keep the default IGNORE option, it does the same while the sensor state is OFF, and it stays in deep sleep if the state is ON until it goes back to OFF.
I tried also with both pullup and pulldown, but the result is the same.
I don't know if there is something wrong with my configuration or with the INVERT_WAKEUP option. My goal is to put the device in sleep mode as soon as possible and wake it up only if there is a status change, which is what that option should do as far as I understood.

No I wasn't able to get libretiny to work. OpenBreken worked fine with the same device, and the batteries were able to last over a month instead of a day or so with this code. I've ended up replacing my device with one that uses zigbee since the ~8 second deep_sleep start up time didn't work with my use case

I'm in the exact same situation... I tried with OpenBeken, same device and same pin settings, and deep sleep seems to be working fine.
However, as you said, the device has a really slow response with that firmware (~8 second also in my case, while it was ~4 with the original firmware), so it's not ideal for me neither. I don't need a super fast response, that's why I'm waiting a bit before moving to a ZigBee device.

With libretiny the response seems to be faster, at least when it reboot immediately after going into deep sleep based on what I was able to test.
If I use a timer instead of a wake up pin, it also works and the connection time seems acceptable for me, but I would need to use a pin to wake up only when needed.

I'm not an expert of C/C++, so it's a bit tricky for me to change the code and do some tests.
However, I can try to compare what is done in the OpenBeken firmware and see if there is any difference with the deep_sleep implementation in libretiny, in order to understand what could be the root cause.

Any help would me more than appreciated. 😃

@alessiocappa
Copy link

I think I found where the problem is.

The condition in lt_deep_sleep_config_gpio is wrong compared to OpenBeken (but also to ESP framework).
Here is the condition in the ESP framework when the wake up GPIO is defined in esp_deep_sleep_enable_gpio_wakeup:

if (mode == ESP_GPIO_WAKEUP_GPIO_HIGH) {
    s_config.gpio_trigger_mode |= (mode << gpio_idx);
} else {
    s_config.gpio_trigger_mode &= ~(mode << gpio_idx);
}

while in libretiny when the deep sleep GPIO is configured in lt_deep_sleep_config_gpio the if statement is reversed:

if (on_high) {
    deep_sleep_param.gpio_edge_map &= (~gpio_index_map);
} else {
    deep_sleep_param.gpio_edge_map |= gpio_index_map;
}

That's why as soon as the device went to sleep using the INVERT_WAKEUP option, it was immediately turned on again.

I didn't want to recompile all the platform code (and honestly I don't even know how to do it 😃), but I tried to apply a quick change in the esp-home deep_sleep component, and it seems to work!
Basically I changed in deep_sleep_component.cpp the initialization of the level variable, by removing the NOT operator:

bool level = this->wakeup_pin_->is_inverted();

In this way I can invert the condition in the platform code, even if it's not correct logically speaking.
I see the level variable is initialized in the same way when multiple wake up pins are defined, so maybe that's the reason why it was working for some people.

I will do some more test, but so far it seems to work as expected. Hope that helps! 🙂

@Xmister
Copy link
Author

Xmister commented Aug 22, 2023

@alessiocappa This what openbeken has to say about it:

gpio_edge_map:The gpio edge bitmap for wakeup gpios,
 *              gpio_edge_map is hex and every bits is map to gpio0-gpio31.
 *              0:rising,1:falling.

Also in their code:

if (falling) {
		if (index >= 32)
			g_gpio_edge_map[1] |= (1 << (index - 32));
		else
			g_gpio_edge_map[0] |= (1 << index);
	}

So it looks like the same implementation as mine. (On rising (high), zero out the pin, on falling, set it to 1).
My INVERT_WAKEUP config also works with it, so I'm not sure what's causing the difference for you

@alessiocappa
Copy link

@Xmister are you using single or multiple wake up pins? I see in your code that the level variable is initialized differently in case multiple pins are defined, which is the same way that works for me. That might be the reason why it works in your case, idk.

I flashed OpenBeken and deep sleep was working on the same device, so I don't think it's an hardware problem.
This is how they set the falling variable:

value = HAL_PIN_ReadDigitalInput(i);
if (value) {
	// on falling edge wake up
	falling = 1;
}
else {
	// on rising edge wake up
	falling = 0;
}

As far as I understand, if the pin is HIGH falling is 1, while if the pin is LOW falling is 0.
I think the naming is a bit confusing; in other words (and based on my understanding), falling is true when the wake up pin is HIGH and we are expecting to exit from deep sleep as soon as it goes LOW and viceversa.

@Xmister
Copy link
Author

Xmister commented Aug 23, 2023

@alessiocappa This is the code for handling the invertion, i.e. if it's currently high, wake up on low.
The platform code you copied from libretiny was just setting a bitmask based on what esphome decided should be the edge. So it's one step later in the process.

@samhunt
Copy link

samhunt commented Aug 23, 2023

I did test out changing the code in my cached libretiny which didn't seem to make any difference. I then reverted, and made the change to libretiny-esphome which also didn't seem to make a difference.
In both cases, my Door Sensor with deep_sleep enabled would randomly wake up after a minute or so.

FWIW my device only has one pin available to attach to deep_sleep

@alessiocappa
Copy link

@alessiocappa This is the code for handling the invertion, i.e. if it's currently high, wake up on low. The platform code you copied from libretiny was just setting a bitmask based on what esphome decided should be the edge. So it's one step later in the process.

I see, it was just to provide the full context. 🙂
Let me try to simulate the same scenario, considering the deep_sleep setting that I'm using:

deep_sleep:
  id: deep_sleep_pir
  run_duration: 15s
  wakeup_pin: P16
  wakeup_pin_mode: INVERT_WAKEUP

And the following code in libretiny-esphome:

bool level = !this->wakeup_pin_->is_inverted();
if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) {
  level = !level;
}

Let's assume P16 is LOW.
P16 is not defined as inverted, so level will be initialized as "1". Then, wakeup_pin_mode is set to INVERT_WAKEUP and digital_read should return 0 for P16, so level will not be inverted and it will stay "1", which is correct considering that we are expecting to wake up the device when the pin goes HIGH.

The function lt_deep_sleep_config_gpio is then called, passing level as second parameter (which is "1"), so when the condition on on_high is evaluated, the pin is set to 0:

if (on_high) {
    deep_sleep_param.gpio_edge_map &= (~gpio_index_map);
} else {
    deep_sleep_param.gpio_edge_map |= gpio_index_map;
}

So the device is woken up immediately, considering that the pin is already in a LOW state.

I double checked the OpenBeken code and indeed the logic seems to be the same, but I think the main difference is that OpenBeken was providing an inverted value by default to me, so I guess that why it was working with OB and it works with libretiny if I modify the definition of level. Not sure why the value is read inverted in OB though, in ESP home I get the right value (ON when HIGH, OFF when LOW), unless I explicitly declare it as inverted.

Also, it's not clear to me why level is set differently in libretiny-esphome in case a single or multiple wake up pins are defined:

if (this->wakeup_pin_ != nullptr) {
    bool level = !this->wakeup_pin_->is_inverted();
    if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) {
      level = !level;
    }
if (wakeup_pins_.size() > 0) {
    bool level;
    for (WakeUpPinItem item : this->wakeup_pins_) {
      level = item.wakeup_pin->is_inverted();
      if (item.wakeup_pin_mode == WAKEUP_PIN_MODE_INVERT_WAKEUP && item.wakeup_pin->digital_read()) {
        level = !level;
      }
      lt_deep_sleep_config_gpio(1 << item.wakeup_pin->get_pin(), level);
    }
  }

Thanks for your help! 👍

@Xmister
Copy link
Author

Xmister commented Aug 23, 2023

@alessiocappa I see, if the openbeken logic is based on an inverted value all the time, then yes, our logic is wrong. I indeed use the wakeup_pins definition which has the "bug" of wrongly initializing the level variable, and that's why it works for me.
And indeed, the translated manufacturer docs agree that "1" should be the rising edge: https://docs-bekencorp-com.translate.goog/sdk_3.0.x/bk7238/html/developer-guide/power_save/sleep_test.html?_x_tr_sl=auto&_x_tr_tl=en&_x_tr_hl=hu&_x_tr_pto=wapp

@Xmister
Copy link
Author

Xmister commented Aug 23, 2023

Created libretiny-eu/libretiny#159
And also github://Xmister/libretuya-esphome@deep-sleep-compat should work with the current libretiny version.

@samhunt
Copy link

samhunt commented Aug 24, 2023

I don't know what I am doing wrong, but I cannot get that change in libretiny (which i have in my esphome cache) and this branch to work.
Even with many different combinations of the binary_sensor and deep_sleep mode (INPUT/INPUT_PINUP) and wakeup_pin_mode (IGNORE/INVERT_WAKEUP) this door sensor wakes up within 30-60 seconds.

external_components:
  - source: github://Xmister/libretuya-esphome@deep-sleep
    components: [ esp32, deep_sleep ]

binary_sensor:
  - platform: gpio
    pin: 
      number: $sensor_pin
      mode: INPUT
    name: $name Sensor
    device_class: door
    id: door

deep_sleep:
  wakeup_pin: 
    number: $sensor_pin    
   # mode: INPUT_PULLUP
  wakeup_pin_mode: INVERT_WAKEUP
  id: deep_sleep_1
  run_duration: 60s
  sleep_duration: 1440min

@Xmister
Copy link
Author

Xmister commented Sep 6, 2023

@samhunt Is it also happening when you leave out sleep_duration?

@samhunt
Copy link

samhunt commented Sep 26, 2023

@samhunt Is it also happening when you leave out sleep_duration?

Sorry for the late reply. I haven't been able to give that a long test, but that seems to have done the trick

@Xmister Xmister requested a review from kuba2k2 as a code owner October 27, 2023 13:28
@alessiocappa
Copy link

Hi @Xmister, first of all, thanks a lot for having updated the deep sleep component in order to work with the official ESPHome release! I hope it will be implemented in the official firmware as well, but for now I can use it as an external component.

However, I would like to check with you how does it work in your case from a performance point of view.
I was initially testing it on a motion sensor, but it was too slow for my use case and I decided to use another device; however, I had a few Wi-Fi door sensors laying around and I wanted to do some tests on them as well.
On one of them I have installed ESPHome, enabled deep_sleep + wakeup_pin and connected the device to my MQTT broker, while the other sensor is still using the Tuya firmware.

What I noticed is that the response time on the one with ESPHome is way more slower; indeed, the one with the stock firmware seems to be pretty fast, with an average response time of about 3s, while the device with ESPHome installed can take up to 8/9, sometimes 10 seconds to report the state.
I've already tried several options, but I can't achieve a good response time. At the moment I have in my configuration a static IP set, as well as the fast_connect option enabled, and based on what I found on the internet, the step to connect to the network seems to be the main bottleneck.

What is the response time in your case? Do you know if there is a way to improve it?
I don't know how the stock firmware works, but I assume that if the device can report its status in about 3s with the Tuya firmware, which is also communicating to the Tuya cloud platform, I would expect at least the same response time using ESPHome (if not better considering that everything is local).
Maybe the stock firmware is using a different power saving approach, instead of the deep sleep?

@Xmister
Copy link
Author

Xmister commented Nov 1, 2023

@alessiocappa Hi,
8-10s is what I get as well. What I can think of is neither esphome or libretiny was written for fast connection times. They probably miss some tricks that can speed up WiFi negotiation.

In my use case (door sensor as gas meter) I can just update the internal state on wake within 1s, and only report back to HA every 10-100 wakes.

@Liionboy
Copy link

How do you put deep_sleep on same pin because at me tell me always duplicate pin use

@Liionboy
Copy link

deep_sleep:
id: deep_sleep_pir
run_duration: 15s
wakeup_pin: P16
wakeup_pin_mode: INVERT_WAKEUP

how you use 2 PIN in yaml? bacuse always i have :
Pin 6 is used in multiple places.

And can't compile

@samhunt
Copy link

samhunt commented Jan 30, 2024

deep_sleep:
id: deep_sleep_pir
run_duration: 15s
wakeup_pin: P16
wakeup_pin_mode: INVERT_WAKEUP

how you use 2 PIN in yaml? bacuse always i have : Pin 6 is used in multiple places.

And can't compile

Is this your yaml that is not compiling?

jesserockz and others added 30 commits May 9, 2024 23:23
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jesse Hills <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jesse Hills <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.