8000 add Low-Power demo · esp8266/Arduino@0c870ae · GitHub
[go: up one dir, main page]

Skip to content

Commit 0c870ae

Browse files
committed
add Low-Power demo
1 parent 8242d72 commit 0c870ae

File tree

2 files changed

+471
-0
lines changed

2 files changed

+471
-0
lines changed
Lines changed: 365 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,365 @@
1+
/* This example demonstrates the different low-power modes of the ESP8266
2+
3+
My initial setup was a WeMos D1 Mini with 3.3V connected to the 3V3 pin through a meter
4+
so that it bypassed the on-board voltage regulator and USB chip. There's still about
5+
0.3 mA worth of leakage current due to the unpowered chips, so an ESP-01 will show lower
6+
current readings than what I could achieve. These tests should work with any module.
7+
While the modem is on the current is 67 mA or jumping around with a listed minimum.
8+
To verify the 20 uA Deep Sleep current I removed the voltage regulator and USB chip.
9+
10+
Since I'm now missing the USB chip, I've included OTA upload. You'll need to upload
11+
from USB or a USB-to-TTL converter the first time, then you can disconnect and use OTA
12+
afterwards during any test if the WiFi is connected. Some tests disconnect or sleep WiFi
13+
so OTA won't go through. If you want OTA upload, hit RESET & press the test button once.
14+
15+
This test assumes you have a pushbutton switch connected between D3 and GND to advance
16+
the tests. You'll also need to connect D0/GPIO16 to RST for the Deep Sleep tests.
17+
If you forget to connect D0 to RST it will hang after the first Deep Sleep test.
18+
Connect an LED from any free pin through a 330 ohm resistor to the 3.3V supply, NOT the 3V3
19+
pin on the module or it adds to the measured current. When it blinks you can proceed.
20+
When the LED is lit continuously it's connecting WiFi, when it's off the CPU is asleep.
21+
The LED blinks slowly when the tests are complete.
22+
23+
WiFi connections will be made over twice as fast if you can use a static IP address.
24+
25+
This example code is in the public domain, and was inspired by code from numerous sources */
26+
27+
#include <ESP8266WiFi.h>
28+
#include <ESP8266mDNS.h>
29+
#include <WiFiUdp.h>
30+
#include <ArduinoOTA.h>
31+
#include <PolledTimeout.h>
32+
33+
//#define DEBUG // prints WiFi connection info to serial, uncomment if you want WiFi messages
34+
#ifdef DEBUG
35+
#define DEBUG_PRINTLN(x) Serial.println(x)
36+
#define DEBUG_PRINT(x) Serial.print(x)
37+
#else
38+
#define DEBUG_PRINTLN(x)
39+
#define DEBUG_PRINT(x)
40+
#endif
41+
42+
#define WAKE_UP_PIN D3 // GPIO0, can also force a serial flash upload with RESET
43+
44+
// un-comment one of the two lines below for your LED connection
45+
#define LED D1 // external LED for modules with built-in LEDs so it doesn't add to the current
46+
//#define LED D4 // GPIO2 LED for ESP-01,07 modules; D4 is LED_BUILTIN on most other modules
47+
48+
ADC_MODE(ADC_VCC); // allows us to monitor the internal VCC level; it varies with WiFi load
49+
// don't connect anything to the analog input pin(s)!
50+
51+
// enter your WiFi configuration below
52+
const char* AP_SSID = "SSID"; // your router's SSID here
53+
const char* AP_PASS = "password"; // your router's password here
54+
IPAddress staticIP(0, 0, 0, 0); // parameters below are for your static IP address, if used
55+
IPAddress gateway(0, 0, 0, 0);
56+
IPAddress subnet(0, 0, 0, 0);
57+
IPAddress dns1(0, 0, 0, 0);
58+
IPAddress dns2(0, 0, 0, 0);
59+
60+
61+
// CRC function used to ensure data validity of RTC User Memory
62+
uint32_t calculateCRC32(const uint8_t *data, size_t length);
63+
64+
// This structure will be stored in RTC memory to remember the reset loop count.
65+
// First field is CRC32, which is calculated based on the rest of the structure contents.
66+
// Any fields can go after the CRC32. The structure must be 4-byte aligned.
67+
struct {
68+
uint32_t crc32;
69+
byte data[4]; // the last byte stores the reset count
70+
} rtcData;
71+
72+
byte resetLoop = 0; // keeps track of the number of Deep Sleep tests / resets
73+
String resetCause = "";
74+
75+
const unsigned int blinkDelay = 100; // fast blink rate for the LED when waiting for the user
76+
const unsigned int longDelay = 350; // longer delay() for the two AUTOMATIC modes
77+
esp8266::polledTimeout::periodicFastMs blinkLED(blinkDelay);
78+
// use fully qualified type and avoid importing all ::esp8266 namespace to the global namespace
79+
80+
void wakeupCallback() { // unlike ISRs, you can do a print() from a callback function
81+
Serial.println(F("Woke from Forced Light Sleep - this is the callback"));
82+
}
83+
84+
void setup() {
85+
pinMode(LED, OUTPUT); // Activity and Status indicator
86+
digitalWrite(LED, LOW); // turn on the LED
87+
pinMode(WAKE_UP_PIN, INPUT_PULLUP); // polled to advance tests, INTR for Forced Light Sleep
88+
Serial.begin(115200);
89+
Serial.print(F("\nReset reason = "));
90+
String resetCause = ESP.getResetReason();
91+
Serial.println(resetCause);
92+
if (resetCause == "External System") {
93+
Serial.println(F("I'm awake and starting the low power tests"));
94+
resetLoop = 5;
95+
updateRTC(); // if external reset, wipe the RTC memory and start all over
96+
}
97+
98+
// Read struct from RTC memory
99+
if (ESP.rtcUserMemoryRead(64, (uint32_t*) &rtcData, sizeof(rtcData))) {
100+
uint32_t crcOfData = calculateCRC32((uint8_t*) &rtcData.data[0], sizeof(rtcData.data));
101+
if (crcOfData != rtcData.crc32) { // if the CRC is invalid
102+
resetLoop = 0; // set first test loop since power on or external reset
103+
} else {
104+
resetLoop = rtcData.data[3]; // read the previous reset count
105+
}
106+
}
107+
} // end of Setup()
108+
109+
void loop() {
110+
if (resetLoop == 0) {
111+
// 1st test - running with WiFi unconfigured, reads ~67 mA minimum
112+
Serial.println(F("\n1st test - running with WiFi unconfigured"));
113+
float volts = ESP.getVcc();
114+
Serial.printf("The internal VCC reads %1.3f volts\n", volts / 1000 );
115+
Serial.println(F("press the button to continue"));
116+
waitPushbutton(false, blinkDelay);
117+
118+
// 2nd test - Automatic Modem Sleep 7 seconds after WiFi is connected (LED flashes)
119+
Serial.println(F("\n2nd test - Automatic Modem Sleep"));
120+
Serial.println(F("connecting WiFi, please wait until the LED blinks"));
121+
init_WiFi();
122+
init_OTA();
123+
Serial.println(F("The current will drop in 7 seconds."));
124+
volts = ESP.getVcc();
125+
Serial.printf("The internal VCC reads %1.3f volts\n", volts / 1000 );
126+
Serial.println(F("press the button to continue"));
127+
waitPushbutton(true, longDelay);
128+
129+
// 3rd test - Forced Modem Sleep
130+
Serial.println(F("\n3rd test - Forced Modem Sleep"));
131+
WiFi.forceSleepBegin();
132+
delay(10); // it doesn't always go to sleep unless you delay(10)
133+
volts = ESP.getVcc();
134+
Serial.printf("The internal VCC reads %1.3f volts\n", volts / 1000 );
135+
Serial.println(F("press the button to continue"));
136+
waitPushbutton(false, blinkDelay);
137+
138+
// 4th test - Automatic Light Sleep
139+
Serial.println(F("\n4th test - Automatic Light Sleep"));
140+
Serial.println(F("reconnecting WiFi"));
141+
Serial.println(F("it will be in Automatic Light Sleep once WiFi connects (LED blinks)"));
142+
digitalWrite(LED, LOW); // visual cue that we're reconnecting
143+
WiFi.setSleepMode(WIFI_LIGHT_SLEEP, 5); // Automatic Light Sleep
144+
WiFi.forceSleepWake(); // reconnect with previous STA mode and connection settings
145+
while (!WiFi.localIP())
146+
delay(50);
147+
volts = ESP.getVcc();
148+
Serial.printf("The internal VCC reads %1.3f volts\n", volts / 1000 );
149+
Serial.println(F("press the button to continue"));
150+
waitPushbutton(true, longDelay);
151+
152+
// 5th test - Forced Light Sleep using Non-OS SDK calls
153+
Serial.println(F("\n5th test - Forced Light Sleep using Non-OS SDK calls"));
154+
WiFi.mode(WIFI_OFF); // you must turn the modem off; using disconnect won't work
155+
yield();
156+
digitalWrite(LED, HIGH); // turn the LED off so they know the CPU isn't running
157+
volts = ESP.getVcc();
158+
Serial.printf("The internal VCC reads %1.3f volts\n", volts / 1000 );
159+
Serial.println(F("CPU going to sleep, pull WAKE_UP_PIN low to wake it (press the button)"));
160+
delay(100); // needs a brief delay after the print or it may print the whole message
161+
wifi_fpm_set_sleep_type(LIGHT_SLEEP_T);
162+
gpio_pin_wakeup_enable(GPIO_ID_PIN(WAKE_UP_PIN), GPIO_PIN_INTR_LOLEVEL);
163+
// only LOLEVEL or HILEVEL interrupts work, no edge, that's an SDK or CPU limitation
164+
wifi_fpm_set_wakeup_cb(wakeupCallback); // Set wakeup callback (optional)
165+
wifi_fpm_open();
166+
wifi_fpm_do_sleep(0xFFFFFFF); // only 0xFFFFFFF allowed; any other value and it won't sleep
167+
delay(10); // it goes to sleep some time during this delay() and waits for an interrupt
168+
Serial.println(F("Woke up!")); // the interrupt callback hits before this is executed
169+
170+
// 6th test - Deep Sleep for 10 seconds, wake with RF_DEFAULT
171+
Serial.println(F("\n6th test - Deep Sleep for 10 seconds, wake with RF_DEFAULT"));
172+
init_WiFi(); // initialize WiFi since we turned it off in the last test
173+
init_OTA();
174+
resetLoop = 1; // advance to the next Deep Sleep test after the reset
175+
updateRTC(); // save the current test state in RTC memory
176+
volts = ESP.getVcc();
177+
Serial.printf("The internal VCC reads %1.3f volts\n", volts / 1000 );
178+
Serial.println(F("press the button to continue"));
179+
while (!digitalRead(WAKE_UP_PIN)) // wait for them to release the button from the last test
180+
delay(10);
181+
delay(50); // debounce time for the switch, button released
182+
waitPushbutton(false, blinkDelay);
183+
digitalWrite(LED, LOW); // turn the LED on, at least briefly
184+
Serial.println(F("going into Deep Sleep now..."));
185+
delay(10); // sometimes the \n isn't printed without a short delay
186+
ESP.deepSleep(10E6, WAKE_RF_DEFAULT); // good night! D0 fires a reset in 10 seconds...
187+
delay(10);
188+
// if you do ESP.deepSleep(0, mode); it needs a RESET to come out of sleep (RTC is off)
189+
// maximum timed Deep Sleep interval = 71.58 minutes with 0xFFFFFFFF
190+
// the 2 uA GPIO current during Deep Sleep can't drive the LED so it's off now
191+
Serial.println(F("What... I'm not asleep?!?")); // it will never get here
192+
}
193+
194+
// 7th test - Deep Sleep for 10 seconds, wake with RFCAL
195+
if (resetLoop < 4) {
196+
init_WiFi(); // need to reinitialize WiFi & OTA due to Deep Sleep resets
197+
init_OTA(); // since we didn't do it in setup() because of the first test
198+
}
199+
if (resetLoop == 1) { // second reset loop since power on
200+
resetLoop = 2; // advance to the next Deep Sleep test after the reset
201+
updateRTC(); // save the current test state in RTC memory
202+
Serial.println(F("\n7th test - in RF_DEFAULT, Deep Sleep for 10 seconds, wake with RFCAL"));
203+
float volts = ESP.getVcc();
204+
Serial.printf("The internal VCC reads %1.3f volts\n", volts / 1000 );
205+
Serial.println(F("press the button to continue"));
206+
waitPushbutton(false, blinkDelay);
207+
Serial.println(F("going into Deep Sleep now..."));
208+
delay(10); // sometimes the \n isn't printed without a short delay
209+
ESP.deepSleep(10E6, WAKE_RFCAL); // good night! D0 fires a reset in 10 seconds...
210+
delay(10);
211+
Serial.println(F("What... I'm not asleep?!?")); // it will never get here
212+
}
213+
214+
// 8th test - Deep Sleep Instant for 10 seconds, wake with NO_RFCAL
215+
if (resetLoop == 2) { // third reset loop since power on
216+
resetLoop = 3; // advance to the next Deep Sleep test after the reset
217+
updateRTC(); // save the current test state in RTC memory
218+
Serial.println(F("\n8th test - in RFCAL, Deep Sleep Instant for 10 seconds, wake with NO_RFCAL"));
219+
float volts = ESP.getVcc();
220+
Serial.printf("The internal VCC reads %1.3f volts\n", volts / 1000 );
221+
Serial.println(F("press the button to continue"));
222+
waitPushbutton(false, blinkDelay);
223+
Serial.println(F("going into Deep Sleep now..."));
224+
delay(10); // sometimes the \n isn't printed without a short delay
225+
ESP.deepSleepInstant(10E6, WAKE_NO_RFCAL); // good night! D0 fires a reset in 10 seconds...
226+
delay(10);
227+
Serial.println(F("What... I'm not asleep?!?")); // it will never get here
228+
}
229+
230+
// 9th test - Deep Sleep Instant for 10 seconds, wake with RF_DISABLED
231+
if (resetLoop == 3) { // fourth reset loop since power on
232+
resetLoop = 4; // advance to the next Deep Sleep test after the reset
233+
updateRTC(); // save the current test state in RTC memory
234+
Serial.println(F("\n9th test - in NO_RFCAL, Deep Sleep Instant for 10 seconds, wake with RF_DISABLED"));
235+
float volts = ESP.getVcc();
236+
Serial.printf("The internal VCC reads %1.3f volts\n", volts / 1000 );
237+
Serial.println(F("press the button to continue"));
238+
waitPushbutton(false, blinkDelay);
239+
Serial.println(F("going into Deep Sleep now..."));
240+
delay(10); // sometimes the \n isn't printed without a short delay
241+
ESP.deepSleepInstant(10E6, WAKE_RF_DISABLED); // good night! D0 fires a reset in 10 seconds...
242+
delay(10);
243+
Serial.println(F("What... I'm not asleep?!?")); // it will never get here
244+
}
245+
246+
if (resetLoop == 4) {
247+
resetLoop = 5; // start all over
248+
updateRTC(); // save the current test state in RTC memory
249+
float volts = ESP.getVcc();
250+
Serial.printf("The internal VCC reads %1.3f volts\n", volts / 1000 );
251+
Serial.println(F("\nTests completed, in RF_DISABLED, press the button to do an ESP.restart()"));
252+
waitPushbutton(false, 1000);
253+
ESP.restart();
254+
}
255+
}
256+
257+
void waitPushbutton(bool usesDelay, unsigned int delayTime) { // loop until they press the button
258+
// note: 2 different modes, as both of the AUTOMATIC power saving modes need a long delay()
259+
if (!usesDelay) { // quick interception of button press, no delay()
260+
blinkLED.reset(delayTime);
261+
while (digitalRead(WAKE_UP_PIN)) { // wait for a button press
262+
if (blinkLED) {
263+
digitalWrite(LED, !digitalRead(LED)); // toggle the activity LED
264+
if (WiFi.localIP()) // don't check OTA if WiFi isn't connected
265+
ArduinoOTA.handle(); //see if we need to reflash
266+
}
267+
yield();
268+
}
269+
} else { // long delay() for the 2 AUTOMATIC modes, but it misses quick button presses
270+
while (digitalRead(WAKE_UP_PIN)) { // wait for a button press
271+
digitalWrite(LED, !digitalRead(LED)); // toggle the activity LED
272+
if (WiFi.localIP()) // don't check OTA if WiFi isn't connected
273+
ArduinoOTA.handle(); //see if we need to reflash
274+
delay(delayTime);
275+
}
276+
}
277+
delay(50); // debounce time for the switch, button pressed
278+
while (!digitalRead(WAKE_UP_PIN)) // now wait for them to release the button
279+
delay(10);
280+
delay(50); // debounce time for the switch, button released
281+
}
282+
283+
uint32_t calculateCRC32(const uint8_t *data, size_t length) {
284+
uint32_t crc = 0xffffffff;
285+
while (length--) {
286+
uint8_t c = *data++;
287+
for (uint32_t i = 0x80; i > 0; i >>= 1) {
288+
bool bit = crc & 0x80000000;
289+
if (c & i) {
290+
bit = !bit;
291+
}
292+
crc <<= 1;
293+
if (bit) {
294+
crc ^= 0x04c11db7;
295+
}
296+
}
297+
}
298+
return crc;
299+
}
300+
301+
void updateRTC() {
302+
rtcData.data[3] = resetLoop; // save the loop count for the next reset
303+
// Update CRC32 of data
304+
rtcData.crc32 = calculateCRC32((uint8_t*) &rtcData.data[0], sizeof(rtcData.data));
305+
if (resetLoop == 5) // wipe the CRC in RTC memory when we're done with all tests
306+
rtcData.crc32 = 0;
307+
// Write struct to RTC memory
308+
ESP.rtcUserMemoryWrite(64, (uint32_t*) &rtcData, sizeof(rtcData));
309+
}
310+
311+
void init_WiFi() {
312+
/* Explicitly set the ESP8266 as a WiFi-client (STAtion mode), otherwise by default it
313+
would try to act as both a client and an access-point and could cause network issues
314+
with other WiFi devices on your network. */
315+
digitalWrite(LED, LOW); // give a visual indication that we're alive but busy
316+
WiFi.persistent(false); // don't store the connection each time to save wear on the flash
317+
WiFi.mode(WIFI_STA);
318+
WiFi.config(staticIP, gateway, subnet); // if using static IP, enter parameters at the top
319+
WiFi.begin(AP_SSID, AP_PASS);
320+
Serial.print(F("connecting to WiFi "));
321+
Serial.println(AP_SSID);
322+
DEBUG_PRINT(F("my MAC: "));
323+
DEBUG_PRINTLN(WiFi.macAddress());
324+
while (WiFi.status() != WL_CONNECTED)
325+
delay(50);
326+
DEBUG_PRINTLN(F("WiFi connected"));
327+
while (!WiFi.localIP())
328+
delay(50);
329+
WiFi.setAutoReconnect(true);
330+
DEBUG_PRINT(F("WiFi Gateway IP: "));
331+
DEBUG_PRINTLN(WiFi.gatewayIP());
332+
DEBUG_PRINT(F("my IP address: "));
333+
DEBUG_PRINTLN(WiFi.localIP());
334+
}
335+
336+
void init_OTA() {
337+
// Port defaults to 8266
338+
// ArduinoOTA.setPort(8266);
339+
340+
// Hostname defaults to esp8266-[ChipID]
341+
// ArduinoOTA.setHostname("myesp8266"));
342+
343+
// No authentication by default
344+
// ArduinoOTA.setPassword((const char *)"123"));
345+
346+
ArduinoOTA.onStart([]() {
347+
Serial.println(F("Start"));
348+
});
349+
ArduinoOTA.onEnd([]() {
350+
Serial.println(F("\nEnd"));
351+
});
352+
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
353+
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
354+
});
355+
ArduinoOTA.onError([](ota_error_t error) {
356+
Serial.printf("Error[%u]: ", error);
357+
if (error == OTA_AUTH_ERROR) Serial.println(F("Auth Failed"));
358+
else if (error == OTA_BEGIN_ERROR) Serial.println(F("Begin Failed"));
359+
else if (error == OTA_CONNECT_ERROR) Serial.println(F("Connect Failed"));
360+
else if (error == OTA_RECEIVE_ERROR) Serial.println(F("Receive Failed"));
361+
else if (error == OTA_END_ERROR) Serial.println(F("End Failed"));
362+
});
363+
ArduinoOTA.begin();
364+
yield();
365+
}

0 commit comments

Comments
 (0)
0