fix: use sleep routine from the original firmware (#1298)
## Summary Fixes #1263 I spent half of my day(-off) reverse engineering the stock english firmware V3.1.1, it's more or less like solving a sudoku with some known pieces (like debug strings, known static addresses, known compiled function, etc) and then the task is to guess the rest. Long story short, this is the sleep routine that they use: <img width="674" height="604" alt="image" src="https://github.com/user-attachments/assets/6d53ce44-7bae-40c7-b4fb-24f898dbcc05" /> From the code above: - They pull down GPIO13 (value = 0xd) before sleep - They verify that power button is released by doing a delay loop of 50ms, similar to what we're doing - `esp_sleep_config_gpio_isolate` is called but I'm not 100% sure why - Pull up power button, note that it's likely redundant because power button should already pulled up by `InputManager` - `param1` and `param2` means enabling front/side buttons for wake up, but it doesn't used in the code in reality. But I think it's physically impossible, see the explanation below - `param3` means "wake up from power button" - `esp_sleep_start` is used; there is a logic to handle if it fails to sleep, then retry recursively (no idea why!) My observation is that they use GPIO13 so that it will be on HIGH state when the chip is powered on, without any user space code to keep it on that state. And once going to deep sleep, it goes into FLOATING by default. That may explain why it need to be in LOW state before going to sleep. (Nice trick btw) Looking again at the circuit diagram provided [here](https://github.com/sunwoods/Xteink-X4/blob/main/readme-img/sch.jpg) (note: it's not official): <img width="705" height="384" alt="image" src="https://github.com/user-attachments/assets/b98d59fd-47ca-4d3d-a24a-94bf999e957b" /> It kinda make sense as the GPIO13 and VBUS (USB VCC) have the same role, they are part of a simple "battery protection" cirtuit Now, we may wonder, how the device wake up when there is no battery at all? <img width="440" height="323" alt="image" src="https://github.com/user-attachments/assets/2981c411-239b-49a7-b9f7-9a75b6c1b6d3" /> It seems like power button is not just a simple switch between GPIO3 and ground, but it also linked the POWER_CTRL, which leads to nowhere on the diagram, but I suppose it connects the battery back for a short amount of time, just enough for the MCU to wake up, and GPIO13 goes HIGH again. It may also explain why power button becomes non-responsive for ~1 second after power on, as it's being pulled up by the current from battery (remind: high = not pressed, low = pressed) To test the theory above, I simply **comment out** the `esp_deep_sleep_enable_gpio_wakeup`: - On battery, power button works as nothing happen - On USB, it doesn't wake up, I need to press RST --- Important things about my analysis: 1. I had to name every function on the code above **manually**, but I'm 99% confident about it. The only function that I'm not sure is `esp_wifi_bt_power_domain_off` ; Edit: it was indeed mislabeled, see https://github.com/crosspoint-reader/crosspoint-reader/pull/1298#discussion_r2879670852 2. Some logic inside the stock firmware looks very strange, there is almost no mention to "arduino" in the hardware, suggesting that they may just call esp-idf functions directly, bypassing the arduino abstraction. --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? **NO** --------- Co-authored-by: Zach Nelson <zach@zdnelson.com>
This commit is contained in:
@@ -58,7 +58,20 @@ void HalPowerManager::startDeepSleep(HalGPIO& gpio) const {
|
|||||||
delay(50);
|
delay(50);
|
||||||
gpio.update();
|
gpio.update();
|
||||||
}
|
}
|
||||||
|
// Pre-sleep routines from the original firmware
|
||||||
|
// GPIO13 is connected to battery latch MOSFET, we need to make sure it's low during sleep
|
||||||
|
// Note that this means the MCU will be completely powered off during sleep, including RTC
|
||||||
|
constexpr gpio_num_t GPIO_SPIWP = GPIO_NUM_13;
|
||||||
|
gpio_set_direction(GPIO_SPIWP, GPIO_MODE_OUTPUT);
|
||||||
|
gpio_set_level(GPIO_SPIWP, 0);
|
||||||
|
esp_sleep_config_gpio_isolate();
|
||||||
|
gpio_deep_sleep_hold_en();
|
||||||
|
gpio_hold_en(GPIO_SPIWP);
|
||||||
|
pinMode(InputManager::POWER_BUTTON_PIN, INPUT_PULLUP);
|
||||||
// Arm the wakeup trigger *after* the button is released
|
// Arm the wakeup trigger *after* the button is released
|
||||||
|
// Note: this is only useful for waking up on USB power. On battery, the MCU will be completely powered off, so the
|
||||||
|
// power button is hard-wired to briefly provide power to the MCU, waking it up regardless of the wakeup source
|
||||||
|
// configuration
|
||||||
esp_deep_sleep_enable_gpio_wakeup(1ULL << InputManager::POWER_BUTTON_PIN, ESP_GPIO_WAKEUP_GPIO_LOW);
|
esp_deep_sleep_enable_gpio_wakeup(1ULL << InputManager::POWER_BUTTON_PIN, ESP_GPIO_WAKEUP_GPIO_LOW);
|
||||||
// Enter Deep Sleep
|
// Enter Deep Sleep
|
||||||
esp_deep_sleep_start();
|
esp_deep_sleep_start();
|
||||||
|
|||||||
Reference in New Issue
Block a user