|
| 1 | +.. currentmodule:: esp32-lowpower |
| 2 | + |
| 3 | +.. _esp32-lowpower: |
| 4 | + |
| 5 | +:mod:`esp32-lowpower` --- low power options for the ESP32 |
| 6 | +========================================================= |
| 7 | + |
| 8 | +The ``machine`` and ``network`` modules have a number of options that enable |
| 9 | +low power operation on the ESP32. Overall, these options offer approximately a 2x reduction in |
| 10 | +operating power while being connected to a WiFi access point by slowing the CPU when |
| 11 | +it is idle and by listening to the access point's beacons less frequently. These low |
| 12 | +power options are not without drawbacks, in particular, the ESP32 will respond |
| 13 | +with more delay to incoming packets or connections as well as potentially to I/O |
| 14 | +events. |
| 15 | + |
| 16 | +Note that in addition to the options described below the ESP32 offers light-sleep |
| 17 | +and deep-sleep modes |
| 18 | +as part of the ``machine`` module: both modes consume less power than the |
| 19 | +options presented here but they suspend normal operation of WiFi and I/O. |
| 20 | + |
| 21 | +.. module:: machine |
| 22 | + :synopsis: functions related to the hardware |
| 23 | + |
| 24 | +ESP32-specific low-power options in ``machine`` |
| 25 | +----------------------------------------------- |
| 26 | + |
| 27 | +.. function:: freq(max_freq, [key=None, \*, ...]) |
| 28 | + |
| 29 | + The ``machine.freq`` function may be used to set the frequency of the esp32's processor |
| 30 | + cores. *max_freq* sets the maximum frequency in Hz and accepts the values |
| 31 | + 20000000, 40000000, 80000000, 160000000, and 240000000. The optional keyword parameter |
| 32 | + *min_freq* sets the minimum frequency, which causes FreeRTOS to reduce the |
| 33 | + clock rate when the processor is idle. It accepts the value 10000000 in addition |
| 34 | + to those accepted for *max_freq*. |
| 35 | + |
| 36 | + Note that allowing FreeRTOS to change the processor frequency dynamically by setting different |
| 37 | + max/min frequencies can affect some I/O peripherals: |
| 38 | + - UART, LEDC (``machine.PWM``): not affected |
| 39 | + - RMT: frequency varies, need to be fixed (TODO item) |
| 40 | + - SPI, I2C, I2S, SDMMC: not affected, they lock the frequency while active |
| 41 | + Please consult the ESP-IDF |
| 42 | + section on `Dynamic Frequency Scaling |
| 43 | + <https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/system/power_management.html#dynamic-frequency-scaling-and-peripheral-drivers>` for more details. |
| 44 | + |
| 45 | + The optional and highly experimental *auto_light_sleep* keyword parameter allows automatic |
| 46 | + light sleep to be enabled in which case the system enters light-sleep mode |
| 47 | + automatically when idle (see the power management section of the ESP-IDF documentation). |
| 48 | + This setting is currently difficult to use because it causes most |
| 49 | + I/O peripherals to stop functioning, including the console UART and many GPIO |
| 50 | + pins. It is only provided for completeness and to enable further experimentation |
| 51 | + with low-power modes. |
| 52 | + |
| 53 | +.. module:: network |
| 54 | + :synopsis: network configuration |
| 55 | + |
| 56 | +ESP32-specific low-power options in ``network`` |
| 57 | +----------------------------------------------- |
| 58 | + |
| 59 | +.. function:: AbstractNIC.connect([service_id, key=None, \*, ...]) |
| 60 | + |
| 61 | + For the WLAN ``STA_IF`` the *connect* function supports an optional |
| 62 | + *listen_interval* keyword parameter which causes the WiFi driver to |
| 63 | + use the 802.11 power-save-mode (PSM) with the specified beacon-skip interval. |
| 64 | + |
| 65 | + The effect of the listen interval is that the ESP32 tells its access point to queue packets |
| 66 | + that are destined for it and to flag the presence of such packets in the standard |
| 67 | + WiFi beacon (typ. every 100ms). The ESP32 then enables the radio just in time to receive a |
| 68 | + beacon, check the flag, explicitly retrieve queued packets if there are any, and then |
| 69 | + it turns the radio off again. |
| 70 | + |
| 71 | + A *listen_interval* value of N >0 causes the ESP32 to wake up and listen every |
| 72 | + N beacons (e.g. a value of 5 can cause packets to be queued for up to about 500ms |
| 73 | + assuming the standard beacon interval of 100ms). |
| 74 | + A value of 0 enables PSM and uses the DTIM value broadcast by the access point as |
| 75 | + listen interval. A DTIM setting is available in many access points, but not all. |
| 76 | + A value of -1 disables PSM and causes the ESP32 to keep the radio on at all times. |
| 77 | + The default value is 1. |
| 78 | + |
| 79 | +Low-power examples |
| 80 | +------------------ |
| 81 | + |
| 82 | +The following scope capture shows the power consumption in default mode while connected |
| 83 | +to Wifi that has DTIM=1 and being pinged once a second. This mode is equivalent to calling |
| 84 | +``machine.freq(160000000)`` and ``connect(..., listen_interval=0)``. |
| 85 | +(Because the AP's DTIM setting is 1 the same behavior could be observed by setting |
| 86 | +*listen_interval=1*.) |
| 87 | + |
| 88 | +.. image:: img/ESP32-micropython-160Mhz.png |
| 89 | + :alt: Scope capture of power consumption in default mode |
| 90 | + :width: 638px |
| 91 | + |
| 92 | +The blue trace at the bottom shows power consumption in mA at 50mA per vertical division |
| 93 | +and a time resolution of 100ms per horizontal division. |
| 94 | +For the majority of the time the consumption hovers around 35mA to 60mA but every 100ms |
| 95 | +it spikes up to about 120-140mA when the WiFi radio is turned on to receive the |
| 96 | +access point's beacon. At the trigger point (500ms into the trace) the beacons indicates |
| 97 | +that a packet is queued (presumably due to the pings) and the ESP32 picks-up the packet |
| 98 | +and responds to the ping (the first thicker spike up to about 190mA), delays for approx |
| 99 | +60ms, and then transmits to the AP that it is re-entering power-save-mode (the thinner |
| 100 | +spike to ~190mA). |
| 101 | + |
| 102 | +Sample output from the ping (running on a Linux box on the same network) shows delays |
| 103 | +up to about 100ms:: |
| 104 | + 64 bytes from 192.168.0.124: icmp_seq=1 ttl=255 time=49.2 ms |
| 105 | + 64 bytes from 192.168.0.124: icmp_seq=2 ttl=255 time=67.5 ms |
| 106 | + 64 bytes from 192.168.0.124: icmp_seq=3 ttl=255 time=95.1 ms |
| 107 | + 64 bytes from 192.168.0.124: icmp_seq=4 ttl=255 time=114 ms |
| 108 | + 64 bytes from 192.168.0.124: icmp_seq=5 ttl=255 time=35.4 ms |
| 109 | + 64 bytes from 192.168.0.124: icmp_seq=6 ttl=255 time=57.5 ms |
| 110 | + |
| 111 | +The cyan trace at the top shows when the micropython interpreter sleeps, i.e. yields the |
| 112 | +application processor core: while high the processor is yielded and while low micropython |
| 113 | +runs. In this capture the interpreter is idle and just wakes up every 400ms to check |
| 114 | +events and yield again. |
| 115 | + |
| 116 | +The next scope capture shows the same situation but with |
| 117 | +``machine.freq(80000000, min_freq=10000000)`` and ``connect(..., listen_interval=5)```. |
| 118 | + |
| 119 | +.. image:: img/ESP32-micropython-10-80Mhz-li5.png |
| 120 | + :alt: Scope capture of power consumption in low-power mode |
| 121 | + :width: 637px |
| 122 | + |
| 123 | +In this capture the scope settings are identical to above. The idle power consumption is now reduced |
| 124 | +to approx 12mA and when the radio is on the consumption is around 115mA. The trigger point |
| 125 | +again shows an incoming ping and response with about the same timing as previously. |
| 126 | +However the frequency at which the ESP32 listens to the access point's beacons is now |
| 127 | +500ms as requested with the ``listen_interval`` parameter. One such listen period can be seen |
| 128 | +20ms before the end of the trace. |
| 129 | + |
| 130 | +Sample output from the ping shows irregular and sometimes long delays:: |
| 131 | + 64 bytes from 192.168.0.124: icmp_seq=44 ttl=255 time=86.9 ms |
| 132 | + 64 bytes from 192.168.0.124: icmp_seq=45 ttl=255 time=110 ms |
| 133 | + 64 bytes from 192.168.0.124: icmp_seq=46 ttl=255 time=136 ms |
| 134 | + 64 bytes from 192.168.0.124: icmp_seq=47 ttl=255 time=399 ms |
| 135 | + 64 bytes from 192.168.0.124: icmp_seq=48 ttl=255 time=76.5 ms |
| 136 | + 64 bytes from 192.168.0.124: icmp_seq=49 ttl=255 time=97.8 ms |
| 137 | + |
| 138 | +Averaged out these scope traces show a reduction of power consumption from around 63mA to |
| 139 | +around 25mA, but this should be taken as a rough estimate only because the processor utilitzation |
| 140 | +and WiFi traffic have a big impact on the average consumption. |
| 141 | + |
| 142 | +For completeness, the following cropped capture shows power consumption with |
| 143 | +PSM turned off, i.e., ``machine.freq(160000000)`` and ``connect(..., listen_interval=-1)``. |
| 144 | + |
| 145 | +.. image:: img/ESP32-micropython-160Mhz-noli.png |
| 146 | + :alt: Scope capture of power consumption with power-save off |
| 147 | + :width: 636px |
| 148 | + |
| 149 | +While the average power consumption is around 125mA the ping response times are better than with |
| 150 | +power-save enabled:: |
| 151 | + 64 bytes from 192.168.0.124: icmp_seq=64 ttl=255 time=2.59 ms |
| 152 | + 64 bytes from 192.168.0.124: icmp_seq=65 ttl=255 time=2.42 ms |
| 153 | + 64 bytes from 192.168.0.124: icmp_seq=66 ttl=255 time=1.68 ms |
| 154 | + 64 bytes from 192.168.0.124: icmp_seq=67 ttl=255 time=1.36 ms |
| 155 | + 64 bytes from 192.168.0.124: icmp_seq=68 ttl=255 time=1.62 ms |
| 156 | + 64 bytes from 192.168.0.124: icmp_seq=69 ttl=255 time=1.30 ms |
| 157 | + |
0 commit comments