├── ESP32WakeStubs ├── images │ ├── ppk-2-ESP32-wake-stub.png │ ├── ppk-2-esp32s3-wake-stub.png │ └── ppk-2-esp32s3-wake-stub-fast-boot.png └── README.md ├── ESPNowPowerManagement ├── images │ ├── ppk-esp32-pm-off.png │ ├── ppk-esp32s3-pm-off.png │ ├── ppk-esp32-pm-10-500.png │ ├── ppk-esp32-pm-50-500.png │ ├── ppk-esp32-pm-75-200.png │ ├── ppk-esp32-pm-75-300.png │ ├── ppk-esp32s3-pm-10-500.png │ ├── ppk-esp32s3-pm-50-500.png │ ├── ppk-esp32s3-pm-75-200.png │ └── ppk-esp32s3-pm-75-300.png ├── src │ └── main.py └── README.md ├── OptimisingMicropythonBootTime ├── images │ ├── ppk2-gc_init-time.png │ ├── ppk-2-fast-boot-all.png │ ├── ppk-20221117T050247.png │ ├── PPK2-full-boot-to-main.png │ ├── ppk-2-ESP32-fast-boot-1.png │ ├── ppk-2-boot-to-main-py.png │ ├── ppk-2-fast-boot-final.png │ ├── ppk2-fast-boot_preboot.png │ ├── ppk-2-fast-boot-no-spiram.png │ ├── ppk-2-nvs_flash_init_time.png │ ├── ppk-2-fast-boot-no-nvs-init.png │ ├── ppk-2-fast-boot-to-app_main.png │ └── ppk-2-fast-boot_preboot_to_deepsleep.png └── README.md ├── ESPNowvsWifiEnergyUsage ├── images │ ├── ppk-2-fast-boot_preboot-wifi-esp32.png │ ├── ppk-2-fast-boot_preboot-espnow-esp32.png │ ├── ppk-2-fast-boot_preboot-wifi-mqtt-static-ip.png │ ├── ppk-2-fast-boot_preboot-espnow-esp32-not-received.png │ ├── ppk-2-fast-boot_preboot-wifi-esp32-connect-only.png │ ├── ppk-2-fast-boot_preboot-espnow-esp32-not-received-closeup.png │ └── ppk-2-fast-boot_preboot-wifi-esp32-connect-only-static-ip.png └── README.md └── README.md /ESP32WakeStubs/images/ppk-2-ESP32-wake-stub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/ESP32WakeStubs/images/ppk-2-ESP32-wake-stub.png -------------------------------------------------------------------------------- /ESP32WakeStubs/images/ppk-2-esp32s3-wake-stub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/ESP32WakeStubs/images/ppk-2-esp32s3-wake-stub.png -------------------------------------------------------------------------------- /ESPNowPowerManagement/images/ppk-esp32-pm-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/ESPNowPowerManagement/images/ppk-esp32-pm-off.png -------------------------------------------------------------------------------- /ESPNowPowerManagement/images/ppk-esp32s3-pm-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/ESPNowPowerManagement/images/ppk-esp32s3-pm-off.png -------------------------------------------------------------------------------- /ESPNowPowerManagement/images/ppk-esp32-pm-10-500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/ESPNowPowerManagement/images/ppk-esp32-pm-10-500.png -------------------------------------------------------------------------------- /ESPNowPowerManagement/images/ppk-esp32-pm-50-500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/ESPNowPowerManagement/images/ppk-esp32-pm-50-500.png -------------------------------------------------------------------------------- /ESPNowPowerManagement/images/ppk-esp32-pm-75-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/ESPNowPowerManagement/images/ppk-esp32-pm-75-200.png -------------------------------------------------------------------------------- /ESPNowPowerManagement/images/ppk-esp32-pm-75-300.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/ESPNowPowerManagement/images/ppk-esp32-pm-75-300.png -------------------------------------------------------------------------------- /ESPNowPowerManagement/images/ppk-esp32s3-pm-10-500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/ESPNowPowerManagement/images/ppk-esp32s3-pm-10-500.png -------------------------------------------------------------------------------- /ESPNowPowerManagement/images/ppk-esp32s3-pm-50-500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/ESPNowPowerManagement/images/ppk-esp32s3-pm-50-500.png -------------------------------------------------------------------------------- /ESPNowPowerManagement/images/ppk-esp32s3-pm-75-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/ESPNowPowerManagement/images/ppk-esp32s3-pm-75-200.png -------------------------------------------------------------------------------- /ESPNowPowerManagement/images/ppk-esp32s3-pm-75-300.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/ESPNowPowerManagement/images/ppk-esp32s3-pm-75-300.png -------------------------------------------------------------------------------- /ESP32WakeStubs/images/ppk-2-esp32s3-wake-stub-fast-boot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/ESP32WakeStubs/images/ppk-2-esp32s3-wake-stub-fast-boot.png -------------------------------------------------------------------------------- /OptimisingMicropythonBootTime/images/ppk2-gc_init-time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/OptimisingMicropythonBootTime/images/ppk2-gc_init-time.png -------------------------------------------------------------------------------- /OptimisingMicropythonBootTime/images/ppk-2-fast-boot-all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/OptimisingMicropythonBootTime/images/ppk-2-fast-boot-all.png -------------------------------------------------------------------------------- /OptimisingMicropythonBootTime/images/ppk-20221117T050247.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/OptimisingMicropythonBootTime/images/ppk-20221117T050247.png -------------------------------------------------------------------------------- /OptimisingMicropythonBootTime/images/PPK2-full-boot-to-main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/OptimisingMicropythonBootTime/images/PPK2-full-boot-to-main.png -------------------------------------------------------------------------------- /OptimisingMicropythonBootTime/images/ppk-2-ESP32-fast-boot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/OptimisingMicropythonBootTime/images/ppk-2-ESP32-fast-boot-1.png -------------------------------------------------------------------------------- /OptimisingMicropythonBootTime/images/ppk-2-boot-to-main-py.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/OptimisingMicropythonBootTime/images/ppk-2-boot-to-main-py.png -------------------------------------------------------------------------------- /OptimisingMicropythonBootTime/images/ppk-2-fast-boot-final.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/OptimisingMicropythonBootTime/images/ppk-2-fast-boot-final.png -------------------------------------------------------------------------------- /OptimisingMicropythonBootTime/images/ppk2-fast-boot_preboot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/OptimisingMicropythonBootTime/images/ppk2-fast-boot_preboot.png -------------------------------------------------------------------------------- /OptimisingMicropythonBootTime/images/ppk-2-fast-boot-no-spiram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/OptimisingMicropythonBootTime/images/ppk-2-fast-boot-no-spiram.png -------------------------------------------------------------------------------- /OptimisingMicropythonBootTime/images/ppk-2-nvs_flash_init_time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/OptimisingMicropythonBootTime/images/ppk-2-nvs_flash_init_time.png -------------------------------------------------------------------------------- /ESPNowvsWifiEnergyUsage/images/ppk-2-fast-boot_preboot-wifi-esp32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/ESPNowvsWifiEnergyUsage/images/ppk-2-fast-boot_preboot-wifi-esp32.png -------------------------------------------------------------------------------- /OptimisingMicropythonBootTime/images/ppk-2-fast-boot-no-nvs-init.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/OptimisingMicropythonBootTime/images/ppk-2-fast-boot-no-nvs-init.png -------------------------------------------------------------------------------- /OptimisingMicropythonBootTime/images/ppk-2-fast-boot-to-app_main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/OptimisingMicropythonBootTime/images/ppk-2-fast-boot-to-app_main.png -------------------------------------------------------------------------------- /ESPNowvsWifiEnergyUsage/images/ppk-2-fast-boot_preboot-espnow-esp32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/ESPNowvsWifiEnergyUsage/images/ppk-2-fast-boot_preboot-espnow-esp32.png -------------------------------------------------------------------------------- /ESPNowvsWifiEnergyUsage/images/ppk-2-fast-boot_preboot-wifi-mqtt-static-ip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/ESPNowvsWifiEnergyUsage/images/ppk-2-fast-boot_preboot-wifi-mqtt-static-ip.png -------------------------------------------------------------------------------- /OptimisingMicropythonBootTime/images/ppk-2-fast-boot_preboot_to_deepsleep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/OptimisingMicropythonBootTime/images/ppk-2-fast-boot_preboot_to_deepsleep.png -------------------------------------------------------------------------------- /ESPNowvsWifiEnergyUsage/images/ppk-2-fast-boot_preboot-espnow-esp32-not-received.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/ESPNowvsWifiEnergyUsage/images/ppk-2-fast-boot_preboot-espnow-esp32-not-received.png -------------------------------------------------------------------------------- /ESPNowvsWifiEnergyUsage/images/ppk-2-fast-boot_preboot-wifi-esp32-connect-only.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/ESPNowvsWifiEnergyUsage/images/ppk-2-fast-boot_preboot-wifi-esp32-connect-only.png -------------------------------------------------------------------------------- /ESPNowvsWifiEnergyUsage/images/ppk-2-fast-boot_preboot-espnow-esp32-not-received-closeup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/ESPNowvsWifiEnergyUsage/images/ppk-2-fast-boot_preboot-espnow-esp32-not-received-closeup.png -------------------------------------------------------------------------------- /ESPNowvsWifiEnergyUsage/images/ppk-2-fast-boot_preboot-wifi-esp32-connect-only-static-ip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glenn20/upy-esp32-experiments/HEAD/ESPNowvsWifiEnergyUsage/images/ppk-2-fast-boot_preboot-wifi-esp32-connect-only-static-ip.png -------------------------------------------------------------------------------- /ESPNowPowerManagement/src/main.py: -------------------------------------------------------------------------------- 1 | import time, network, espnow 2 | 3 | sta = network.WLAN(0) 4 | e = espnow.ESPNow() 5 | sta.active(1) 6 | e.active(1) 7 | pm_values = ((75, 200), (75, 300), (50, 500), (10, 500), (65535, 65535)) 8 | while True: 9 | for p in pm_values: 10 | e.config(pm=p) 11 | time.sleep(2) 12 | -------------------------------------------------------------------------------- /ESP32WakeStubs/README.md: -------------------------------------------------------------------------------- 1 | # Using ESP32 Wake stubs 2 | 3 | (commit [8e30d80](https://github.com/glenn20/micropython/commit/8e30d80afcd56af38260127fef5c3080b71e8555) 4 | Add wake stub code to modesp32.c) 5 | 6 | (See [Espressif 7 | Docs](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/deep-sleep-stub.html) 8 | and examples at ) 9 | 10 | > ESP32 supports running a “deep sleep wake stub” when coming out of deep sleep. 11 | This function runs immediately as soon as the chip wakes up - before any normal 12 | initialisation, bootloader, or ESP-IDF code has run. After the wake stub runs, 13 | the SoC can go back to sleep or continue to start ESP-IDF normally. 14 | 15 | - Useful for silently counting input pulses or timer based sampling of a GPIO 16 | input and periodically booting to micropython to send updates via ESPNow or 17 | Wifi. 18 | - However, there are some severe limitations on what you can do in the wake_stub 19 | function (see docs). 20 | - In particular all coding must be in C and access only ESP32 ROM functions. 21 | 22 | | | | 23 | |---|---| 24 | **ESP32:** Returns to sleep after 2.8ms (~0.044mC)! | ![_](./images/ppk-2-ESP32-wake-stub.png) 25 | **ESP32-S3:** Returns to deepsleep after 8.7ms (0.18mC) (4x longer than ESP32) (vs 50.2ms/2.0mC). | ![_](./images/ppk-2-esp32s3-wake-stub.png) 26 | **ESP32-S3:** Compared with fast boot from deep sleep of 50ms (2.0mC)). | ![_](./images/ppk-2-esp32s3-wake-stub-fast-boot.png) 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # My Experiments and Measurements with Micropython on ESP32* devices 2 | 3 | Here are some of my experiments and measurements on running micropython on 4 | ESP32* devices, including: 5 | 6 | - [Optimising Micropython Boot Time](./OptimisingMicropythonBootTime/README.md): 7 | Optimising the time to boot Micropython from deepsleep mode and returning to 8 | deepsleep. 9 | - [ESP32 Wake Stubs](./ESP32WakeStubs/README.md): Measuring power consumption 10 | and time to boot using the ESP32 Wake Stubs. 11 | - [ESPNow vs Wifi Energy Consumption](./ESPNowvsWifiEnergyUsage/README.md): 12 | Measuring the energy consumed to boot Micropython from deepsleep and send a 13 | status message to a peer via ESPNow and Wifi. 14 | - [ESPNow Power Management Tests](./ESPNowPowerManagement/README.md): Tests and 15 | measurements of the new ESPNOW power management features available in ESP IDF 16 | v5.0.2. 17 | 18 | Comments, suggestions, criticisms and requests are welcome via the Issues or 19 | Discussions tabs above. 20 | 21 | NOTE: I was a bit time constrained during these initial measurements, so they 22 | are not as systematic, consistent or clearly presented as I would prefer, but 23 | there are still some useful conclusions to be drawn, so I thought I'd share this 24 | early work. 25 | 26 | One big caveat is that most of these initial measurements are made by monitoring 27 | the power consumption on the USB power line which will over-estimate power 28 | consumed when the devices are powered by battery. 29 | 30 | ## Highlights 31 | 32 | Measuring time to wake ESP32-S3 and ESP32 devices from deepsleep, boot micropython and return to deepsleep: 33 | 34 | | Wake from deepsleep, boot and return to deepsleep | Time (ms) | Charge (mC) | Energy (microWh) | 35 | |---|---:|---:|---:| 36 | | Unoptimised (boot to main.py) | 635.1 | 41.9 | 58.3 | 37 | | Optimised (boot to frozen _boot.py) | 48.8 | 1.8 | 2.5 | 38 | | Optimised boot and send ESPNow Mesage | 119.0 | 7.7 | 10.7 | 39 | | Optmised boot and send MQTT message (static IP) | 774.3 | 108.6 | 150.9 | 40 | | Optmised boot and send MQTT message (DHCP) | 1626.0 | 204.3 | 283.8 | 41 | | ESP32 Wake Stub | 2.8 | 0.044 | 0.06 | 42 | 43 | drawing 46 | -------------------------------------------------------------------------------- /ESPNowPowerManagement/README.md: -------------------------------------------------------------------------------- 1 | # ESPNOW power management 2 | 3 | ## Aim 4 | 5 | To check and test the new connectionless power management features in ESP IDF 6 | v5.0.2. 7 | 8 | - Usage: `e.config(pm=(window, interval))`: 9 | 10 | - every `interval` milliseconds the radio will be turned on for `window` 11 | milliseconds to listen for incoming messages (`interval` should be a 12 | multiple of 100ms). Incoming messages will be lost while the radio is off. 13 | Example: 14 | 15 | ```py 16 | e.config(pm=(75, 200)) # equivalent to WLAN.config(pm=WLAN.PM_PERFORMANCE) 17 | e.config(pm=(75, 300)) # equivalent to WLAN.config(pm=WLAN.PM_POWERSAVE) 18 | ``` 19 | 20 | See [Config ESP-NOW Power-saving Parameter]( 21 | https://docs.espressif.com/projects/esp-idf/en/v5.0.2/esp32/api-reference/network/esp_now.html#config-esp-now-power-saving-parameter) 22 | 23 | Based on my micropython 24 | [`espnow-50-pm`](https://github.com/glenn20/micropython/tree/espnow-50-pm) 25 | branch: 26 | 27 | - Pull Request at https://github.com/micropython/micropython/pull/11890. 28 | 29 | ## Summary of Results (see below for full data) 30 | 31 | ### ESP32: (DFRobot Beetle ESP32 module) 32 | 33 | | ESPNOW pm setting | Duty Cycle | Current (mA) | Power (mW) | % of max | 34 | |---|---:|---:|---:|---:| 35 | OFF | 100% | 100 | 330 | 100% | 36 | `pm=(75, 200)` | 38% | 56 | 185 | 56% | 37 | `pm=(75, 300)` | 25% | 47 | 155 | 47% | 38 | `pm=(50, 500)` | 10% | 36 | 119 | 36% | 39 | `pm=(10, 500)` | 2% | 31 | 102 | 31% | 40 | 41 | **Radio OFF:** Current = 28.9mA (95mW) 42 | **Radio ON:** Current = 100mA (330mW) 43 | 44 | ### ESP32S3: (FeatherS3) 45 | 46 | | ESPNOW pm setting | Duty Cycle | Current (mA) | Power (mW) | % of max | 47 | |---|---:|---:|---:|---:| 48 | OFF | 100% | 103 | 340 | 100% | 49 | `pm=(75, 200)` | 38% | 67 | 221 | 65% | 50 | `pm=(75, 300)` | 25% | 57 | 188 | 55% | 51 | `pm=(50, 500)` | 10% | 50 | 165 | 49% | 52 | `pm=(10, 500)` | 2% | 45 | 149 | 44% | 53 | 54 | **Radio OFF:** Current = 43.8mA (145mW) 55 | **Radio ON:** Current = 104mA (343mW) 56 | 57 | ### ESP32S2: (FeatherS2) 58 | 59 | | ESPNOW pm setting | Duty Cycle | Current (mA) | Power (mW) | % of max | 60 | |---|---:|---:|---:|---:| 61 | OFF | 100% | 73 | 241 | 100% | 62 | `pm=(75, 200)` | 38% | 41 | 135 | 56% | 63 | `pm=(75, 300)` | 25% | 35 | 116 | 48% | 64 | `pm=(50, 500)` | 10% | 27 | 89 | 37% | 65 | `pm=(10, 500)` | 2% | 23 | 76 | 32% | 66 | 67 | **Radio OFF:** Current = 22.2mA (73mW) 68 | **Radio ON:** Current = 73.0mA (241mW) 69 | 70 | ## Software 71 | 72 | These measurements are based on my micropython `espnow-50-pm` branch at: 73 | 74 | - 75 | - based on micropython master branch revision: [v1.20.0-246-gc2ea8b2f9] 76 | - Pull Request at https://github.com/micropython/micropython/pull/11890. 77 | 78 | which includes support for setting the ESPNOW wireless power saving parameters. 79 | 80 | ## Hardware 81 | 82 | - DFRobot Beetle ESP32 module 83 | - FeatherS3 device from UnexpectedMaker [feathers3.io](https://feathers3.io) 84 | - FeatherS2 device from UnexpectedMaker [feathers2.io](https://feathers2.io) 85 | - [Power Profiler Kit 86 | II](https://www.nordicsemi.com/Products/Development-hardware/Power-Profiler-Kit-2) 87 | to measure power consumption of device during experiments. 88 | - Also supports visualisation of timing pulses from device pins. 89 | - Cables, computer. 90 | 91 | These measurements are made with the PPK2 operating in PSU mode to power the 92 | ESP32 devices via the GND and 3.3V pins (some other components will also be 93 | powered). 94 | 95 | ## Measurements 96 | 97 | Measurements are made with module running the following `main.py`: 98 | 99 | ```py 100 | import time, network, espnow 101 | 102 | sta = network.WLAN(0) 103 | e = espnow.ESPNow() 104 | sta.active(1); e.active(1) 105 | pm_values = ((75, 200), (75, 300), (50, 500), (10, 500), (1000, 500)) 106 | while True: 107 | for p in pm_values: 108 | e.config(pm=p) 109 | time.sleep(2) 110 | ``` 111 | 112 | ### DFRobot Beetle ESP32 module 113 | 114 | **Radio OFF:** Current = 28.9mA (95mW) 115 | **Radio ON:** Current = 100mA (330mW) 116 | 117 | | ESPNOW pm setting | Average Current (mA) | PPK2 Screenshot | 118 | |---|---:|---| 119 | `pm=(1000, 500)` (disabled) | 100 | ![_](./images/ppk-esp32-pm-off.png) 120 | `pm=(75, 200)` | 56 | ![_](./images/ppk-esp32-pm-75-200.png) 121 | `pm=(75, 300)` | 47 | ![_](./images/ppk-esp32-pm-75-300.png) 122 | `pm=(50, 500)` | 36 | ![_](./images/ppk-esp32-pm-50-500.png) 123 | `pm=(10, 500)` | 31 | ![_](./images/ppk-esp32-pm-10-500.png) 124 | 125 | ### FeatherS3 module 126 | 127 | **Radio OFF:** Current = 43.8mA (145mW) 128 | **Radio ON:** Current = 104mA (343mW) 129 | 130 | | ESPNOW pm setting | Average Current (mA) | PPK2 Screenshot | 131 | |---|---:|---| 132 | `pm=(1000, 500)` (disabled) | 103 | ![_](./images/ppk-esp32s3-pm-off.png) 133 | `pm=(75, 200)` | 67 | ![_](./images/ppk-esp32s3-pm-75-200.png) 134 | `pm=(75, 300)` | 57 | ![_](./images/ppk-esp32s3-pm-75-300.png) 135 | `pm=(50, 500)` | 50 | ![_](./images/ppk-esp32s3-pm-50-500.png) 136 | `pm=(10, 500)` | 45 | ![_](./images/ppk-esp32s3-pm-10-500.png) 137 | -------------------------------------------------------------------------------- /ESPNowvsWifiEnergyUsage/README.md: -------------------------------------------------------------------------------- 1 | # Compare Boot Time and Energy Consumption for sending Messages via ESPNow and Wifi 2 | 3 | We will compare the boot time and energy consumption of ESP32 devices to: 4 | 5 | - Wake from deepsleep, 6 | - Boot to Micropython, 7 | - Execute frozen module to: 8 | - Send a status message via ESPNow or Wifi, 9 | - Return to deepsleep. 10 | 11 | These measurements use the optimised (fast boot) process described in [Optimising Micropython Boot Time](../OptimisingMicropythonBootTime/README.md). 12 | 13 | Commit: [c34ca002](https://github.com/glenn20/micropython/commit/c34ca0023b5c25e9797d7a41302bb77bc33836a5) 14 | Add frozen modules to send status over espnow/wifi/mqtt. 15 | 16 | ## Summary of Results 17 | 18 | **_(Energy (uWh) = 1000 * Charge (mC) * 5V / (60 * 60))_** 19 | 20 | | Test | Time (ms) | Charge (mC) | Energy (microWh) | 21 | |---|---:|---:|---:| 22 | | Fast Boot to `_boot.py` only | 70 | 3.32 | 4.6 | 23 | | Send ESPNow Succeed | 119 | 7.68 | 10.7 | 24 | | Send ESPNow Failed | 145 | 15.4 | 21.4 | 25 | | Connect to wifi (DHCP) and send HTTP Post | 2928 | 341.3 | 474.0 | 26 | | Connect to wifi (DHCP) | 1626 | 204.3 | 283.8 | 27 | | Connect to wifi (Static IP) | 734 | 100.2 | 139.2 | 28 | | Connect to wifi (Static IP) and send MQTT message | 774 | 108.6 | 150.9 | 29 | 30 | ## Send status via ESNow from _boot.py (commit [fc62686](https://github.com/micropython/micropython/commit/fc62686524245f9f1b492eb0c978e00375e44d90)) 31 | 32 | **NOTES:** 33 | 34 | - This commit has `nvs_flash_init()` enabled at boot time as this is required 35 | for `esp_wifi_init()` (additional 7ms of bootup time). 36 | - This app is run as a module frozen into the Micropython image 37 | - Add ~350ms (23.6mC, 32.8uWh) to mount filesystem from flash storage and 38 | execute app. 39 | 40 | `_preboot.py`: 41 | ```python 42 | import machine 43 | 44 | def send_state(broker): 45 | import network 46 | from _espnow import ESPNow 47 | 48 | enow = ESPNow() 49 | enow.active(True) 50 | enow.add_peer(broker) 51 | sta = network.WLAN(network.STA_IF) 52 | sta.active(True) 53 | enow.send(broker, b"wake_up", True) 54 | enow.active(False) 55 | sta.active(False) 56 | 57 | if machine.reset_cause() == machine.DEEPSLEEP_RESET: 58 | send_state(b"\xf4\x12\xfaA\xf7T") 59 | machine.deepsleep(1000) 60 | ``` 61 | 62 | **NOTE:** The ESP32 board has a deepsleep current of 15mA. 63 | 64 | | | | 65 | |---|---| 66 | **ESP32 Boot, Send ESPNow message:** Time to boot and return to deepsleep is 118.8ms (7.68mC). Sending the message generates a current spike of ~550mA over 0.6ms (1 byte) to 2.5ms (255 bytes). | ![_](./images/ppk-2-fast-boot_preboot-espnow-esp32.png) 67 | If the peer is not found, the transmission is retried and more energy is consumed: 144.9ms (15.43mC) (twice the energy consumption). Note: Retransmission does **NOT** occur if sending to the broadcast address. |![_](./images/ppk-2-fast-boot_preboot-espnow-esp32-not-received.png) 68 | Zoom in on retransmit attempts | ![_](./images/ppk-2-fast-boot_preboot-espnow-esp32-not-received-closeup.png) 69 | 70 | ## Send status over wifi from _boot.py (commit [44a1341](https://github.com/glenn20/micropython/commit/44a1341147513e7fbe0ccd9c2025869c09d27845)) 71 | 72 | `_preboot_wifi.py`: 73 | 74 | ```python 75 | import machine 76 | 77 | def send_state(broker): 78 | import network 79 | import urequests 80 | 81 | sta = network.WLAN(network.STA_IF) 82 | sta.active(True) 83 | sta.connect("ssid", "password") 84 | while not sta.isconnected(): 85 | pass 86 | try: 87 | r = urequests.post("http://XXX.XXX.XXX.XXX:5000/status", data="hello") 88 | except OSError: 89 | pass 90 | sta.disconnect() 91 | sta.active(False) 92 | 93 | if machine.reset_cause() == machine.DEEPSLEEP_RESET: 94 | send_state() 95 | machine.deepsleep(1000) 96 | ``` 97 | 98 | NOTES: 99 | 100 | - Boot times showed considerable variability with some transmissions taking up 101 | to 4.9s (490mC). 102 | - Wifi connection and dhcp response times may vary by Access Point. 103 | - You can see the [WIFI_PS_MIN_MODEM power saving mode](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv4N14wifi_ps_type_t17WIFI_PS_MIN_MODEME) at work, where the wifi radio 104 | is turned off periodically while connected to the Access Point. 105 | 106 | | | | 107 | |---|---| 108 | **ESP32 Boot, connect to wifi and DHCP, POST message:** Time to boot and return to deepsleep is 2928ms (341.3mC) . | ![_](./images/ppk-2-fast-boot_preboot-wifi-esp32.png) 109 | **ESP32 Boot, connect to Wifi and DHCP:** Time to connect to wifi and then deepsleep (ie. don't send message) is 1626ms (204.3mC). | ![_](./images/ppk-2-fast-boot_preboot-wifi-esp32-connect-only.png) 110 | **ESP32 Wifi connect - static IP:** Time to boot and return to deepsleep is 734.3ms (100.2mC) | ![_](./images/ppk-2-fast-boot_preboot-wifi-esp32-connect-only-static-ip.png) 111 | **ESP32 Wifi connect - MQTT - Static IP:** Time to boot and post MQTT message is 774ms (108.6mC) | ![_](./images/ppk-2-fast-boot_preboot-wifi-mqtt-static-ip.png) 112 | 113 | `_preboot_mqtt.py`: 114 | 115 | ```python 116 | import machine 117 | 118 | def send_state(broker): 119 | import network 120 | import urequests 121 | from umqtt.simple import MQTTClient, MQTTException 122 | 123 | sta = network.WLAN(network.STA_IF) 124 | sta.active(True) 125 | sta.connect("ssid", "password") 126 | while not sta.isconnected(): 127 | pass 128 | try: 129 | mq = MQTTClient( 130 | "esp32_test", "192.168.10.10", 131 | user="mqtt_user", password="mqtt_password") 132 | mq.connect() 133 | mq.publish("glenn_test/test", "Sensor") 134 | mq.disconnect() 135 | except MQTTException as err: 136 | print(err) 137 | sta.disconnect() 138 | sta.active(False) 139 | 140 | if machine.reset_cause() == machine.DEEPSLEEP_RESET: 141 | send_state() 142 | machine.deepsleep(1000) 143 | ``` 144 | -------------------------------------------------------------------------------- /OptimisingMicropythonBootTime/README.md: -------------------------------------------------------------------------------- 1 | # Optimising Micropython Boot time on ESP32 devices 2 | 3 | ## Aim 4 | 5 | To optimise the operation of micropython on ESP32 devices for operation as 6 | battery operated sensor devices. In particular, to investigate and optimise: 7 | 8 | 1. Time (and power consumption) during boot from deepsleep 9 | 10 | ## Summary of Results (see below for full data) 11 | 12 | **_(Energy (uWh) = 1000 * Charge (mC) * 5V / (60 * 60))_** 13 | 14 | Measuring time to wake FEATHERS3 (ESP32-S3) device from deepsleep, boot 15 | micropython and return to deepsleep: 16 | 17 | | Before Optimisation | Time (ms) | Charge (mC) | Energy (microWh) | 18 | |---|---:|---:|---:| 19 | | Boot to `app_main()` without Validation | 47.0 | 1.7 | 2.4 | 20 | | Validation of Image | 217.0 | 14.2 | 19.7 | 21 | | `nvs_flash_init()` | 7.8 | 0.6 | 0.8 | 22 | | Allocate SPIRAM to GC | 12.5 | 0.7 | 1.0 | 23 | | Micropython `app_main()` to `_boot.py` | 0.8 | 0.1 | 0.8 | 24 | | `_boot.py` to `main.py` and deepsleep | 350.0 | 23.6 | 32.8 | 25 | |**Total:** | **635.1** | **41.9** | **58.3** | 26 | 27 | | After Optimisation | Time (ms) | Charge (mC) | Energy (microWh) | 28 | |---|---:|---:|---:| 29 | | Boot to `app_main()` without Validation | 47.0 | 1.7 | 2.4 | 30 | | Micropython `app_main()` to `_boot.py` | 0.8 | 0.06 | 0.1 | 31 | | `_boot.py` to deepsleep (`_preboot.py`) | 1.0 | 0.06 | 0.1 | 32 | |**Total:** | **48.8** | **1.82** | **2.5** | 33 | 34 | For ESP32 device: 35 | 36 | - the optimised boot process takes **70ms (3.32mC)** compared to 48.8ms (2.5mC) for 37 | the ESP32-S3 38 | 39 | See [ESP32 Wake Stubs](../ESP32WakeStubs/README.md) for an additional power optimisation 40 | method. 41 | 42 | ## Software 43 | 44 | These experiments are based on my micropython experiments branch at: 45 | 46 | - 47 | - based on micropython master branch revision: [v1.19.1-660-gc8913fdbf](https://github.com/micropython/micropython/tree/c8913fdbfadd43c879bba4d6d565be8b644f1feb) 48 | 49 | ## Hardware 50 | 51 | - FeatherS3 device from UnexpectedMaker [feathers3.io](https://feathers3.io) 52 | - Selected for low power consumption and support for my sensors. 53 | - [Power Profiler Kit 54 | II](https://www.nordicsemi.com/Products/Development-hardware/Power-Profiler-Kit-2) 55 | to measure power consumption of device during experiments. 56 | - Also supports visualisation of timing pulses from device pins. 57 | - Cables, computer. 58 | 59 | ## Measuring ESP32 boot times and power consumption 60 | 61 | These measurements are made with the ESP32 device powered by USB (5V) and the 62 | PPK2 operating in ammeter mode to measure the current on the USB 5V line 63 | powering the ESP32 device. This method does not fully reflect the power 64 | efficiency of the device when running off a LiPoly battery, but is convenient 65 | for reprogramming and controlling the device during the test. The average 66 | current draw for the FeatherS3 device while in deepsleep is 1.73mA (at 5V USB). 67 | 68 | See below for final power consumption measurements when the PPK2 is the power 69 | source (simulating a LiPo battery power source). 70 | 71 | ### Full boot to main.py (unoptimised _naive_ implementation) 72 | 73 | `main.py`: 74 | 75 | ```python 76 | import time 77 | import machine 78 | 79 | pin = machine.Pin(18, machine.Pin.OUT) 80 | pin.value(1) 81 | 82 | # If we have just woken from a deep sleep go back to sleep 83 | if machine.reset_cause() == machine.DEEPSLEEP_RESET: 84 | time.sleep_ms(10) 85 | pin.value(0) 86 | machine.deepsleep(1000) 87 | ``` 88 | 89 | _Interpreting the images:_ 90 | 91 | - The greyed numbers show values for current, time and charge within the 92 | selected region. 93 | - The voltage levels for Pin 18 (the boot timing signal pin) are shown in the 94 | trace lablled `0` at the bottom of the image. These are used to calculate 95 | timing between key boot events in micropython. 96 | 97 | | | | 98 | |---|---| 99 | Time to go back to sleep from main.py: 635ms. Power consumption: 41.3mC | ![_](./images/PPK2-full-boot-to-main.png) 100 | Time to start of micropython `app_main()`: 264ms (15.9mC) |![_](./images/ppk-20221117T050247.png) 101 | From `app.main()` to end of boardctl_startup() (nvs_flash_init()): 7.8ms (0.57mC) | ![A](./images/ppk-2-nvs_flash_init_time.png) 102 | Time to execute gc_init() (with 16MB SPIRAM): 12.5ms (0.71mC) | ![_](./images/ppk2-gc_init-time.png) 103 | Time from call to load `_boot.py` to execution of deepsleep() in `main.py`: 350ms (23.6mC) | ![A](./images/ppk-2-boot-to-main-py.png) 104 | 105 | # Optimisation opportunities 106 | 107 | ## A: Reduce time to boot to `app_main()`: (commit [f2a3c66](https://github.com/glenn20/micropython/commit/f2a3c66ad30784cfc82269a491107befbd0bf8a6)) 108 | 109 | Add `CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP=y` to 110 | ports/esp32/boards/sdkconfig.base: 111 | 112 | - Disables validation of the micropython image when the device wakes from 113 | deepsleep (see [Espressif 114 | Docs](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/kconfig.html#config-bootloader-skip-validate-in-deep-sleep))) 115 | 116 | | | | 117 | |---|---| 118 | **Reduces time to boot to `app_main()` from 264ms to 47.0ms** (15.9mC to 1.71mC) | ![_](./images/ppk-2-fast-boot-to-app_main.png) 119 | Total boot time back to sleep: 418ms (26.5mC) | ![_](./images/ppk-2-fast-boot-all.png) 120 | 121 | ## B: Execute app (deepsleep reboot) from frozen `_boot.py` module: (commit [8154c70](https://github.com/glenn20/micropython/commit/8154c70be59106cff7e0ad5c888479b62971852e)) 122 | 123 | Moving the code in `main.py` to `ports/esp32/modules/_boot.py` will eliminate 124 | the need to mount and initialise the filesystem on the flash memory before 125 | executing the app. 126 | 127 | | | | 128 | |---|---| 129 | Time from `_boot.py` back to deepsleep reduced from 350ms (23.6mC) to 1.5ms (0.09mC) | ![A](./images/ppk-2-fast-boot_preboot_to_deepsleep.png) 130 | Total time from boot back to deepsleep: 70.4ms (3.17mC) | ![A](./images/ppk2-fast-boot_preboot.png) 131 | 132 | | | | 133 | |---|---| 134 | ## C: Skip `nvs_flash_init()` on boot if waking from deepsleep: (commit [9bfa464](https://github.com/glenn20/micropython/commit/9bfa4641cfe3f458c183ae88eae787a2cf2de3a7)) 135 | 136 | **NOTE:** `nvs_flash_init()` must be called before initialising wifi (as RF 137 | calibaration data is read from nvs - EVEN if we disable NVS in the wifi config!!!). 138 | 139 | | | | 140 | |---|---| 141 | Time for `nvs_flash_init()` reduced from 7.8ms (0.57mC) to 0ms (0mC). (200microseconds is time to generate pulse on logic pin). | ![_](./images/ppk-2-fast-boot-no-nvs-init.png) 142 | 143 | ## D: Skip initialisation of SPIRAM on boot if waking from deepsleep: (commit [450b5a3](https://github.com/glenn20/micropython/commit/450b5a308df2e17eb2b740a49f65bb080c665612)) 144 | 145 | This reduces the work in gc_init() and is only of benefit on devices with 146 | SPIRAM. 147 | 148 | NOTE: For most battery operated devices it is unlikely that additional SPIRAM 149 | would be required or be desirable, but I include it here as my testing device 150 | was equipped with SPIRAM. 151 | 152 | **NOTE: micropython no longer calls `gc_init()` on all available memory at boot 153 | (including PSRAM) and this change should no longer be necessary for v1.21+. (See 154 | micropython commit 155 | [e465012](https://github.com/micropython/micropython/commit/e4650125b88a35f074097f16d84a8f49bd22ac06) 156 | and this [discussion](https://github.com/orgs/micropython/discussions/12316)).** 157 | 158 | | | | 159 | |---|---| 160 | `gc_init()` time reduced from 12.5ms (0.71mC) to less than 0.1ms. (200 microseconds is time to generate pulse on logic pin.) | ![_](./images/ppk-2-fast-boot-no-spiram.png) 161 | 162 | ## After all optimisations: 163 | 164 | | | | 165 | |---|---| 166 | Time from boot to back to deepsleep: 50.0ms (1.87mC) (reduced from 264ms (15.9mC)). (Includes 6 * 0.2 = 1.2ms overhead for generating boot timing signals). | ![_](./images/ppk-2-fast-boot-final.png) 167 | 168 | # Optimised code on ESP32 module 169 | 170 | **NOTE:** This esp32 module board is not very power efficient (background current = 171 | 15.0mA), so take care in comparing with ESP32-S3 module above. 172 | 173 | | | | 174 | |---|---| 175 | The ESP32 boot from, and return to, deepsleep takes 70ms (3.32mC) compared to 48.8ms (1.82mC) for the ESP32-S3. | ![_](./images/ppk-2-ESP32-fast-boot-1.png) 176 | --------------------------------------------------------------------------------