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

add Low-Power demo #6989

Merged
merged 69 commits into from
Feb 2, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
0c870ae
add Low-Power demo
Tech-TX Jan 6, 2020
fd1c366
add Low-Power demo
Tech-TX Jan 6, 2020
b912664
add Low-Power demo, remove OTA and add WiFi timeout
Tech-TX Jan 9, 2020
287bc2d
add Low-Power demo
Tech-TX Jan 6, 2020
7ea68d8
add Low-Power demo
Tech-TX Jan 6, 2020
f77a2c5
add Low-Power demo, remove OTA and add WiFi timeout
Tech-TX Jan 9, 2020
387e30d
add Low-Power demo, remove OTA and add WiFi timeout
Tech-TX Jan 9, 2020
0db9254
Merge branch 'master' into add_Low-Power_demo
Tech-TX Jan 9, 2020
3f3ab30
Merge branch 'add_Low-Power_demo' of https://github.com/Tech-TX/Ardui…
Tech-TX Jan 10, 2020
df75200
add Low-Power demo
Tech-TX Jan 10, 2020
ab30950
Merge branch 'master' into add_Low-Power_demo
Tech-TX Jan 10, 2020
11f1931
add Low-Power demo
Tech-TX Jan 10, 2020
7d6a03b
Merge branch 'add_Low-Power_demo' of https://github.com/Tech-TX/Ardui…
Tech-TX Jan 10, 2020
2003ef4
add Low-Power demo
Tech-TX Jan 10, 2020
acf3cec
add Low-Power demo
Tech-TX Jan 10, 2020
2f2c508
add Low-Power demo
Tech-TX Jan 11, 2020
47b73a2
add Low-Power demo
Tech-TX Jan 11, 2020
36a8dbb
add Low-Power demo
Tech-TX Jan 11, 2020
7178362
Merge branch 'master' into add_Low-Power_demo
Tech-TX Jan 11, 2020
5d16fca
add Low-Power demo
Tech-TX Jan 12, 2020
7a74da2
Merge branch 'add_Low-Power_demo' of https://github.com/Tech-TX/Ardui…
Tech-TX Jan 12, 2020
aa4feb2
add Low-Power demo
Tech-TX Jan 12, 2020
8f4448d
add Low-Power demo
Tech-TX Jan 12, 2020
b8e42e4
add Low-Power demo
Tech-TX Jan 12, 2020
068f4bd
add Low-Power demo
Tech-TX Jan 12, 2020
3118de5
add Low-Power demo
Tech-TX Jan 12, 2020
ac40b6e
add Low-Power demo
Tech-TX Jan 12, 2020
662b1f2
add Low-Power demo
Tech-TX Jan 13, 2020
5695626
add Low-Power demo
Tech-TX Jan 16, 2020
7627080
Merge branch 'master' into add_Low-Power_demo
Tech-TX Jan 17, 2020
7b0e515
add Low-Power demo
Tech-TX Jan 17, 2020
a41c381
Merge branch 'add_Low-Power_demo' of https://github.com/Tech-TX/Ardui…
Tech-TX Jan 17, 2020
b93c2e3
add Low-Power demo
Tech-TX Jan 17, 2020
f4d00c6
add Low-Power demo
Tech-TX Jan 18, 2020
841fa1b
add Low-Power demo
Tech-TX Jan 18, 2020
efe55a0
Merge branch 'master' into add_Low-Power_demo
Tech-TX Jan 18, 2020
ebc2b0a
add Low-Power demo
Tech-TX Jan 22, 2020
c9c073a
Merge branch 'add_Low-Power_demo' of https://github.com/Tech-TX/Ardui…
Tech-TX Jan 22, 2020
222b9e8
add Low-Power demo
Tech-TX Jan 25, 2020
9ef40f8
Merge branch 'master' into add_Low-Power_demo
Tech-TX Jan 25, 2020
3ffb8ec
add Low-Power demo
Tech-TX Jan 26, 2020
b902ed2
Merge branch 'add_Low-Power_demo' of https://github.com/Tech-TX/Ardui…
Tech-TX Jan 26, 2020
6ff566d
Merge branch 'master' into add_Low-Power_demo
Tech-TX Jan 26, 2020
c8a5955
add Low-Power demo
Tech-TX Jan 26, 2020
d9ea100
Merge branch 'add_Low-Power_demo' of https://github.com/Tech-TX/Ardui…
Tech-TX Jan 26, 2020
431f5dc
add Low-Power demo
Tech-TX Jan 28, 2020
9597a12
Merge branch 'master' into add_Low-Power_demo
Tech-TX Jan 28, 2020
7d3ee8d
add Low-Power demo
Tech-TX Jan 28, 2020
701634e
Merge branch 'add_Low-Power_demo' of https://github.com/Tech-TX/Ardui…
Tech-TX Jan 28, 2020
b618e6c
add Low-Power demo
Tech-TX Jan 29, 2020
3292f9b
add Low-Power demo
Tech-TX Jan 29, 2020
1e80279
add Low-Power demo
Tech-TX Jan 29, 2020
93a527b
add Low-Power demo
Tech-TX Jan 29, 2020
6018d0b
add Low-Power demo
Tech-TX Jan 30, 2020
55d95e2
Merge branch 'master' into add_Low-Power_demo
Tech-TX Jan 30, 2020
b990e59
add Low-Power demo
Tech-TX Jan 31, 2020
c107c43
add Low-Power demo
Tech-TX Jan 31, 2020
fecacfd
add Low-Power demo
Tech-TX Jan 31, 2020
601a5ae
add Low-Power demo
Tech-TX Jan 31, 2020
a3ddde1
add Low-Power demo
Tech-TX Jan 31, 2020
535d483
add Low-Power demo
Tech-TX Jan 31, 2020
3775b1b
add Low-Power demo
Tech-TX Feb 1, 2020
56cb239
add Low-Power demo
Tech-TX Feb 1, 2020
c26ce4a
add Low-Power demo
Tech-TX Feb 1, 2020
a76ec12
add Low-Power demo
Tech-TX Feb 1, 2020
a79f48c
add Low-Power demo
Tech-TX Feb 1, 2020
ee4c701
add Low-Power demo
Tech-TX Feb 1, 2020
d378102
add Low-Power demo
Tech-TX Feb 1, 2020
df1a494
Merge branch 'master' into add_Low-Power_demo
Tech-TX Feb 2, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
365 changes: 365 additions & 0 deletions libraries/esp8266/examples/LowPowerDemo/LowPowerDemo.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,365 @@
/* This example demonstrates the different low-power modes of the ESP8266

My initial setup was a WeMos D1 Mini with 3.3V connected to the 3V3 pin through a meter
so that it bypassed the on-board voltage regulator and USB chip. There's still about
0.3 mA worth of leakage current due to the unpowered chips, so an ESP-01 will show lower
current readings than what I could achieve. These tests should work with any module.
While the modem is on the current is 67 mA or jumping around with a listed minimum.
To verify the 20 uA Deep Sleep current I removed the voltage regulator and USB chip.

Since I'm now missing the USB chip, I've included OTA upload. You'll need to upload
from USB or a USB-to-TTL converter the first time, then you can disconnect and use OTA
afterwards during any test if the WiFi is connected. Some tests disconnect or sleep WiFi
so OTA won't go through. If you want OTA upload, hit RESET & press the test button once.

This test assumes you have a pushbutton switch connected between D3 and GND to advance
the tests. You'll also need to connect D0/GPIO16 to RST for the Deep Sleep tests.
If you forget to connect D0 to RST it will hang after the first Deep Sleep test.
Connect an LED from any free pin through a 330 ohm resistor to the 3.3V supply, NOT the 3V3
pin on the module or it adds to the measured current. When it blinks you can proceed.
When the LED is lit continuously it's connecting WiFi, when it's off the CPU is asleep.
The LED blinks slowly when the tests are complete.

WiFi connections will be made over twice as fast if you can use a static IP address.

This example code is in the public domain, and was inspired by code from numerous sources */

#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <PolledTimeout.h>

//#define DEBUG // prints WiFi connection info to serial, uncomment if you want WiFi messages
#ifdef DEBUG
#define DEBUG_PRINTLN(x) Serial.println(x)
#define DEBUG_PRINT(x) Serial.print(x)
#else
#define DEBUG_PRINTLN(x)
#define DEBUG_PRINT(x)
#endif

#define WAKE_UP_PIN D3 // GPIO0, can also force a serial flash upload with RESET

// un-comment one of the two lines below for your LED connection
#define LED D1 // external LED for modules with built-in LEDs so it doesn't add to the current
//#define LED D4 // GPIO2 LED for ESP-01,07 modules; D4 is LED_BUILTIN on most other modules

ADC_MODE(ADC_VCC); // allows us to monitor the internal VCC level; it varies with WiFi load
// don't connect anything to the analog input pin(s)!

// enter your WiFi configuration below
const char* AP_SSID = "SSID"; // your router's SSID here
const char* AP_PASS = "password"; // your router's password here
IPAddress staticIP(0, 0, 0, 0); // parameters below are for your static IP address, if used
IPAddress gateway(0, 0, 0, 0);
IPAddress subnet(0, 0, 0, 0);
IPAddress dns1(0, 0, 0, 0);
IPAddress dns2(0, 0, 0, 0);


// CRC function used to ensure data validity of RTC User Memory
uint32_t calculateCRC32(const uint8_t *data, size_t length);

// This structure will be stored in RTC memory to remember the reset loop count.
// First field is CRC32, which is calculated based on the rest of the structure contents.
// Any fields can go after the CRC32. The structure must be 4-byte aligned.
struct {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's encapsulate the functionality related to the rtc and the data you're saving.
First, let's list what I consider as "related functionality":

  1. this rtcData struct
  2. the process of storing the struct into the rtc mem (updateRTC())
  3. the process of loading back the struct from the rtc mem (the snippet of code below the comment //Read struct from RTC memory
  4. obtaining the resetCount value from the struct (included in the code of the previous point)
  5. updating the struct with a new resetCount (the line that is always right before calling updateRTC(): resetCount = someNumber)

Encapsulation in C++ means putting the functionality in an object, but in such a way that using that object hides the ugly details, and simplifies the code around the line where it is being used from.

A C++ object is essentially a group of functions that share a scope of specific variables. The functions are usually referred to as either methods or member functions, and the variables in the internal scope are called member variables or just members (this is a simplification).

There are 2 types of C++ objects: class and struct. They are functionally the same, the only difference between them is the default visibility of the object's internal variables:

  • In a class, any member variable or methods is by default private, so a user of the object can't access the internal variables directly (they are meant to be used privately by the class methods). To make either a member variable or method public, it must be explicitly declared as "public".
  • In a struct, all member variables and methods are by default public, so a user of the object has free access to everything. To hide a variable or method, it must be explicitly declared as "private".

In this example, you're already using a struct. Because this is an example sketch, use of a struct is ok.

In C++, a struct is very much like a normal-C struct, but you can add methods to it, and that is how the functionality listed above will be encapsulated.

Given the above, the following is one possible way of encapsulating:

//definition of the object
struct RTCData //object name, usually upper CamelCase
{
  //member variables are usually marked with a slightly different notation. In our case, we prepend "_" (underscore).
  uint32_t _crc32;
  byte _data[4];  // the last byte stores the reset count

 /* Writes the struct to the rtc mem.
    Returns the value of resetCount: if rtc mem was inited, returns the value found, or if uninited returns 0.
    Called from setup()
  */
  int readFromRTC() {  // object methods, usually lower camelCase
    if (ESP.rtcUserMemoryRead(64, (uint32_t*) this, sizeof(RTCData))) {
      uint32_t crcOfData = crc32((uint8_t*) &_data[0], sizeof(_data));
      if (crcOfData != _crc32) {  // if the CRC is invalid
        return 0;  // set first test run since power on or external reset
      }
      return = _data[3];  // read the previous reset count
  }

  //Replaces updateRTC() to reflect intent
 /* Reads the struct from the rtc mem
    Takes as arg the resetCount value to use
    Returns resetCount for convenience
 */
  int writeToRTC(int resetCount) {
    _data[3] = resetCount;  // save the reset count for the next test run
    // Update CRC32 of data
    _crc32 = crc32((uint8_t*) &_data[0], sizeof(_data));
    if (resetCount == 5) {  // wipe the CRC in RTC memory when we're done with all tests
      _crc32 = 0;
    }
    // Write struct to RTC memory
    ESP.rtcUserMemoryWrite(64, (uint32_t*) this, sizeof(RTCData));

    return resetCount; //return the arg for convenience
  }
 
};

//declaration of the object
RTCData rtcData; 
int resetCount; //use of int is more efficient vs. byte in this 32bit architecture

Now, usage can be as follows:

...
/*  in setup: 
    Replaces the code below the "Read struct from RTC memory" comment
*/
    resetCount = rtcData.readFromRTC();
...

/*  in setup and loop: replaces the two lines:
    resetCount = somenum; 
    updateRTC();
*/
    resetCount = rtcData.writeToRTC(somenum);

In the above you will notice use of this. Within any class method, this is a special member variable: a pointer to the class instance you are in. So, in this case it's the memory address of the start of the global rtcData struct instance.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can sort of follow that part, but I'd rather minimize refactoring the code into C++ as I can't read it (and can't maintain it). About twice as many people know/use C as know C++, so re-writing it into C++ makes it incomprehensible to those of us that don't do C++. An 'example' or 'demo' should be understood by the largest number of users, not just the C++ crowd.

The other refactor (several comments below) makes perfectly good sense though. I'm all for making it more readable. Working on it...

Copy link
Contributor Author

@Tech-TX Tech-TX Jan 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm reading your code correctly, I think the definition in the struct needs to change since resetCount is now an int, so

byte _data[4];  // the last byte stores the reset count

should be something like

int _data[];  // 4 bytes for the int resetCount

with matching revisions to readFrom and writeTo

edit: dunno if that's going to work, as the crc32 looks like it's operating byte-wide

uint32_t crc32;
byte data[4]; // the last byte stores the reset count
} rtcData;

byte resetLoop = 0; // keeps track of the number of Deep Sleep tests / resets
String resetCause = "";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This resetCause isn't needed, the line should be removed. You are declaring it inside setup(), and using it only there, so that local declaration is enough.

Copy link
Contributor Author

@Tech-TX Tech-TX Jan 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, I'd forgotten I moved that whole thing inside of setup(). Removed.

For whatever reason, I got exceptions if I tried to do multiple reads of ESP.getResetReason() like this:

  Serial.println(ESP.getResetReason());
  if ((ESP.getResetReason() == "External System") || (ESP.getResetReason() == "Power on")) {

I didn't try to hunt it down, as it was a very long stack dump. I've never seen a stack dump that long. That's why I read it once into a string.


const unsigned int blinkDelay = 100; // fast blink rate for the LED when waiting for the user
const unsigned int longDelay = 350; // longer delay() for the two AUTOMATIC modes
esp8266::polledTimeout::periodicFastMs blinkLED(blinkDelay);
// use fully qualified type and avoid importing all ::esp8266 namespace to the global namespace

void wakeupCallback() { // unlike ISRs, you can do a print() from a callback function
Serial.println(F("Woke from Forced Light Sleep - this is the callback"));
}

void setup() {
pinMode(LED, OUTPUT); // Activity and Status indicator
digitalWrite(LED, LOW); // turn on the LED
pinMode(WAKE_UP_PIN, INPUT_PULLUP); // polled to advance tests, INTR for Forced Light Sleep
Serial.begin(115200);
Serial.print(F("\nReset reason = "));
String resetCause = ESP.getResetReason();
Serial.println(resetCause);
if (resetCause == "External System") {
Serial.println(F("I'm awake and starting the low power tests"));
resetLoop = 5;
updateRTC(); // if external reset, wipe the RTC memory and start all over
}

// Read struct from RTC memory
if (ESP.rtcUserMemoryRead(64, (uint32_t*) &rtcData, sizeof(rtcData))) {
uint32_t crcOfData = calculateCRC32((uint8_t*) &rtcData.data[0], sizeof(rtcData.data));
if (crcOfData != rtcData.crc32) { // if the CRC is invalid
resetLoop = 0; // set first test loop since power on or external reset
} else {
resetLoop = rtcData.data[3]; // read the previous reset count
}
}
} // end of Setup()

void loop() {
if (resetLoop == 0) {
// 1st test - running with WiFi unconfigured, reads ~67 mA minimum
Serial.println(F("\n1st test - running with WiFi unconfigured"));
float volts = ESP.getVcc();
Serial.printf("The internal VCC reads %1.3f volts\n", volts / 1000 );
Serial.println(F("press the button to continue"));
waitPushbutton(false, blinkDelay);

// 2nd test - Automatic Modem Sleep 7 seconds after WiFi is connected (LED flashes)
Serial.println(F("\n2nd test - Automatic Modem Sleep"));
Serial.println(F("connecting WiFi, please wait until the LED blinks"));
init_WiFi();
init_OTA();
Serial.println(F("The current will drop in 7 seconds."));
volts = ESP.getVcc();
Serial.printf("The internal VCC reads %1.3f volts\n", volts / 1000 );
Serial.println(F("press the button to continue"));
waitPushbutton(true, longDelay);

// 3rd test - Forced Modem Sleep
Serial.println(F("\n3rd test - Forced Modem Sleep"));
WiFi.forceSleepBegin();
delay(10); // it doesn't always go to sleep unless you delay(10)
volts = ESP.getVcc();
Serial.printf("The internal VCC reads %1.3f volts\n", volts / 1000 );
Serial.println(F("press the button to continue"));
waitPushbutton(false, blinkDelay);

// 4th test - Automatic Light Sleep
Serial.println(F("\n4th test - Automatic Light Sleep"));
Serial.println(F("reconnecting WiFi"));
Serial.println(F("it will be in Automatic Light Sleep once WiFi connects (LED blinks)"));
digitalWrite(LED, LOW); // visual cue that we're reconnecting
WiFi.setSleepMode(WIFI_LIGHT_SLEEP, 5); // Automatic Light Sleep
WiFi.forceSleepWake(); // reconnect with previous STA mode and connection settings
while (!WiFi.localIP())
delay(50);
volts = ESP.getVcc();
Serial.printf("The internal VCC reads %1.3f volts\n", volts / 1000 );
Serial.println(F("press the button to continue"));
waitPushbutton(true, longDelay);

// 5th test - Forced Light Sleep using Non-OS SDK calls
Serial.println(F("\n5th test - Forced Light Sleep using Non-OS SDK calls"));
WiFi.mode(WIFI_OFF); // you must turn the modem off; using disconnect won't work
yield();
digitalWrite(LED, HIGH); // turn the LED off so they know the CPU isn't running
volts = ESP.getVcc();
Serial.printf("The internal VCC reads %1.3f volts\n", volts / 1000 );
Serial.println(F("CPU going to sleep, pull WAKE_UP_PIN low to wake it (press the button)"));
delay(100); // needs a brief delay after the print or it may print the whole message
wifi_fpm_set_sleep_type(LIGHT_SLEEP_T);
gpio_pin_wakeup_enable(GPIO_ID_PIN(WAKE_UP_PIN), GPIO_PIN_INTR_LOLEVEL);
// only LOLEVEL or HILEVEL interrupts work, no edge, that's an SDK or CPU limitation
wifi_fpm_set_wakeup_cb(wakeupCallback); // Set wakeup callback (optional)
wifi_fpm_open();
wifi_fpm_do_sleep(0xFFFFFFF); // only 0xFFFFFFF allowed; any other value and it won't sleep
delay(10); // it goes to sleep some time during this delay() and waits for an interrupt
Serial.println(F("Woke up!")); // the interrupt callback hits before this is executed

// 6th test - Deep Sleep for 10 seconds, wake with RF_DEFAULT
Serial.println(F("\n6th test - Deep Sleep for 10 seconds, wake with RF_DEFAULT"));
init_WiFi(); // initialize WiFi since we turned it off in the last test
init_OTA();
resetLoop = 1; // advance to the next Deep Sleep test after the reset
updateRTC(); // save the current test state in RTC memory
volts = ESP.getVcc();
Serial.printf("The internal VCC reads %1.3f volts\n", volts / 1000 );
Serial.println(F("press the button to continue"));
while (!digitalRead(WAKE_UP_PIN)) // wait for them to release the button from the last test
delay(10);
delay(50); // debounce time for the switch, button released
waitPushbutton(false, blinkDelay);
digitalWrite(LED, LOW); // turn the LED on, at least briefly
Serial.println(F("going into Deep Sleep now..."));
delay(10); // sometimes the \n isn't printed without a short delay
ESP.deepSleep(10E6, WAKE_RF_DEFAULT); // good night! D0 fires a reset in 10 seconds...
delay(10);
// if you do ESP.deepSleep(0, mode); it needs a RESET to come out of sleep (RTC is off)
// maximum timed Deep Sleep interval = 71.58 minutes with 0xFFFFFFFF
// the 2 uA GPIO current during Deep Sleep can't drive the LED so it's off now
Serial.println(F("What... I'm not asleep?!?")); // it will never get here
}

// 7th test - Deep Sleep for 10 seconds, wake with RFCAL
if (resetLoop < 4) {
init_WiFi(); // need to reinitialize WiFi & OTA due to Deep Sleep resets
init_OTA(); // since we didn't do it in setup() because of the first test
}
if (resetLoop == 1) { // second reset loop since power on
resetLoop = 2; // advance to the next Deep Sleep test after the reset
updateRTC(); // save the current test state in RTC memory
Serial.println(F("\n7th test - in RF_DEFAULT, Deep Sleep for 10 seconds, wake with RFCAL"));
float volts = ESP.getVcc();
Serial.printf("The internal VCC reads %1.3f volts\n", volts / 1000 );
Serial.println(F("press the button to continue"));
waitPushbutton(false, blinkDelay);
Serial.println(F("going into Deep Sleep now..."));
delay(10); // sometimes the \n isn't printed without a short delay
ESP.deepSleep(10E6, WAKE_RFCAL); // good night! D0 fires a reset in 10 seconds...
delay(10);
Serial.println(F("What... I'm not asleep?!?")); // it will never get here
}

// 8th test - Deep Sleep Instant for 10 seconds, wake with NO_RFCAL
if (resetLoop == 2) { // third reset loop since power on
resetLoop = 3; // advance to the next Deep Sleep test after the reset
updateRTC(); // save the current test state in RTC memory
Serial.println(F("\n8th test - in RFCAL, Deep Sleep Instant for 10 seconds, wake with NO_RFCAL"));
float volts = ESP.getVcc();
Serial.printf("The internal VCC reads %1.3f volts\n", volts / 1000 );
Serial.println(F("press the button to continue"));
waitPushbutton(false, blinkDelay);
Serial.println(F("going into Deep Sleep now..."));
delay(10); // sometimes the \n isn't printed without a short delay
ESP.deepSleepInstant(10E6, WAKE_NO_RFCAL); // good night! D0 fires a reset in 10 seconds...
delay(10);
Serial.println(F("What... I'm not asleep?!?")); // it will never get here
}

// 9th test - Deep Sleep Instant for 10 seconds, wake with RF_DISABLED
if (resetLoop == 3) { // fourth reset loop since power on
resetLoop = 4; // advance to the next Deep Sleep test after the reset
updateRTC(); // save the current test state in RTC memory
Serial.println(F("\n9th test - in NO_RFCAL, Deep Sleep Instant for 10 seconds, wake with RF_DISABLED"));
float volts = ESP.getVcc();
Serial.printf("The internal VCC reads %1.3f volts\n", volts / 1000 );
Serial.println(F("press the button to continue"));
waitPushbutton(false, blinkDelay);
Serial.println(F("going into Deep Sleep now..."));
delay(10); // sometimes the \n isn't printed without a short delay
ESP.deepSleepInstant(10E6, WAKE_RF_DISABLED); // good night! D0 fires a reset in 10 seconds...
delay(10);
Serial.println(F("What... I'm not asleep?!?")); // it will never get here
}

if (resetLoop == 4) {
resetLoop = 5; // start all over
updateRTC(); // save the current test state in RTC memory
float volts = ESP.getVcc();
Serial.printf("The internal VCC reads %1.3f volts\n", volts / 1000 );
Serial.println(F("\nTests completed, in RF_DISABLED, press the button to do an ESP.restart()"));
waitPushbutton(false, 1000);
ESP.restart();
}
}

void waitPushbutton(bool usesDelay, unsigned int delayTime) { // loop until they press the button
// note: 2 different modes, as both of the AUTOMATIC power saving modes need a long delay()
if (!usesDelay) { // quick interception of button press, no delay()
blinkLED.reset(delayTime);
while (digitalRead(WAKE_UP_PIN)) { // wait for a button press
if (blinkLED) {
digitalWrite(LED, !digitalRead(LED)); // toggle the activity LED
if (WiFi.localIP()) // don't check OTA if WiFi isn't connected
ArduinoOTA.handle(); //see if we need to reflash
}
yield();
}
} else { // long delay() for the 2 AUTOMATIC modes, but it misses quick button presses
while (digitalRead(WAKE_UP_PIN)) { // wait for a button press
digitalWrite(LED, !digitalRead(LED)); // toggle the activity LED
if (WiFi.localIP()) // don't check OTA if WiFi isn't connected
ArduinoOTA.handle(); //see if we need to reflash
delay(delayTime);
}
}
delay(50); // debounce time for the switch, button pressed
while (!digitalRead(WAKE_UP_PIN)) // now wait for them to release the button
delay(10);
delay(50); // debounce time for the switch, button released
}

uint32_t calculateCRC32(const uint8_t *data, size_t length) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see we already have this crc32 function in crc32.cpp, the declaration is in cores/esp8266/coredecls.h. Please reuse that one to not reinvent the wheel.

Copy link
Contributor Author

@Tech-TX Tech-TX Jan 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may take me a week to figure that one out, as it's not exactly clear how to use it for a newbie, nor is it listed at readthedocs. The only example I could find that used it is equally unclear (WiFiShutdown.ino). At the moment I'm trying to figure out what this is doing, quite literally symbol-by-symbol.
nv->crc = crc32(&nv->data, sizeof(nv->data));
From what I've figured out so far, that's called "function call by reference" but giving it a name doesn't magically make me understand it. I suspect that's why the person that wrote the RTCUserMemory example coded their own version of a crc32 (that's what I copied).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, replaced with the one linked from coredecls.h. That was less painful than I thought.

uint32_t crc = 0xffffffff;
while (length--) {
uint8_t c = *data++;
for (uint32_t i = 0x80; i > 0; i >>= 1) {
bool bit = crc & 0x80000000;
if (c & i) {
bit = !bit;
}
crc <<= 1;
if (bit) {
crc ^= 0x04c11db7;
}
}
}
return crc;
}

void updateRTC() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would get replaced by the RTCData::writeToRTC() method, declared and defined within the class at the start of the sketch.

rtcData.data[3] = resetLoop; // save the loop count for the next reset
// Update CRC32 of data
rtcData.crc32 = calculateCRC32((uint8_t*) &rtcData.data[0], sizeof(rtcData.data));
if (resetLoop == 5) // wipe the CRC in RTC memory when we're done with all tests
rtcData.crc32 = 0;
// Write struct to RTC memory
ESP.rtcUserMemoryWrite(64, (uint32_t*) &rtcData, sizeof(rtcData));
}

void init_WiFi() {
/* Explicitly set the ESP8266 as a WiFi-client (STAtion mode), otherwise by default it
would try to act as both a client and an access-point and could cause network issues
with other WiFi devices on your network. */
digitalWrite(LED, LOW); // give a visual indication that we're alive but busy
WiFi.persistent(false); // don't store the connection each time to save wear on the flash
WiFi.mode(WIFI_STA);
WiFi.config(staticIP, gateway, subnet); // if using static IP, enter parameters at the top
WiFi.begin(AP_SSID, AP_PASS);
Serial.print(F("connecting to WiFi "));
Serial.println(AP_SSID);
DEBUG_PRINT(F("my MAC: "));
DEBUG_PRINTLN(WiFi.macAddress());
while (WiFi.status() != WL_CONNECTED)
delay(50);
DEBUG_PRINTLN(F("WiFi connected"));
while (!WiFi.localIP())
delay(50);
WiFi.setAutoReconnect(true);
DEBUG_PRINT(F("WiFi Gateway IP: "));
DEBUG_PRINTLN(WiFi.gatewayIP());
DEBUG_PRINT(F("my IP address: "));
DEBUG_PRINTLN(WiFi.localIP());
}

void init_OTA() {
// Port defaults to 8266
// ArduinoOTA.setPort(8266);

// Hostname defaults to esp8266-[ChipID]
// ArduinoOTA.setHostname("myesp8266"));

// No authentication by default
// ArduinoOTA.setPassword((const char *)"123"));

ArduinoOTA.onStart([]() {
Serial.println(F("Start"));
});
ArduinoOTA.onEnd([]() {
Serial.println(F("\nEnd"));
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println(F("Auth Failed"));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if-else bodies on their own lines, please, like so:

if (this)
  doThat();
else if(this2)
  doThat2();
else
...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already fixed, just not committed yet. I saw the error at Travis.

else if (error == OTA_BEGIN_ERROR) Serial.println(F("Begin Failed"));
else if (error == OTA_CONNECT_ERROR) Serial.println(F("Connect Failed"));
else if (error == OTA_RECEIVE_ERROR) Serial.println(F("Receive Failed"));
else if (error == OTA_END_ERROR) Serial.println(F("End Failed"));
});
ArduinoOTA.begin();
yield();
}
Loading