8000 add Low-Power demo by Tech-TX · Pull Request #6989 · esp8266/Arduino · GitHub
[go: up one dir, main page]

Skip to content

add Low-Power demo #6989

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

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
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
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
Prev Previous commit
Next Next commit
add Low-Power demo
  • Loading branch information
Tech-TX committed Jan 18, 2020
commit 841fa1b9e89bb206990323e007921f0a17bf77db
48 changes: 31 additions & 17 deletions libraries/esp8266/examples/LowPowerDemo/LowPowerDemo.ino
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,18 @@ ADC_MODE(ADC_VCC); // allows you to monitor the internal VCC level; it varies w
// don't connect anything to the analog input pin(s)!

// enter your WiFi configuration below
const char* AP_SSID = "tryagain"; // your router's SSID here
const char* AP_PASS = "093774c242ccb21a3485"; // your router's password here
const char* AP_SSID = "your_SSID"; // your router's SSID here
const char* AP_PASS = "your_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);
uint32_t wifiTimeout = 25E3; // 25 second timeout on the WiFi connection
uint32_t wifiTimeout = 30E3; // 30 second timeout on the WiFi connection

#define testPoint 4 // D2/GPIO4 used to track the timing of several test cycles, entirely optional
//#define testPoint 4 // D2/GPIO4 used to track the timing of several test cycles, optional

// This structure will be stored in RTC memory to remember the reset count (number of Deep Sleeps).
// This structure is stored in RTC memory to remember the reset count (number of Deep Sleeps).
// 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

Expand All @@ -89,6 +89,8 @@ void wakeupCallback() { // unlike ISRs, you can do a print() from a callback fu
#ifdef testPoint
digitalWrite(testPoint, LOW); // testPoint tracks latency from WAKE_UP_PIN LOW to testPoint LOW
#endif
Serial.print(F("millis() = ")); // show that RTC / millis() is slowed in Forced Light Sleep
Serial.println(millis());
Serial.println(F("Woke from Forced Light Sleep - this is the callback"));
}

Expand Down Expand Up @@ -119,6 +121,10 @@ void setup() {
resetCount = rtcData.data[3]; // read the previous reset count
}
}
if (resetCount == 1) { // show that millis() is cleared across Deep Sleep reset
Serial.print(F("millis() = "));
Serial.println(millis());
}
} // end of setup()

void loop() {
Expand All @@ -128,7 +134,7 @@ void loop() {
runTest3();
runTest4();
runTest5();
runTest6(); // first Deep Sleep test, all these end with a RESET
runTest6(); // first Deep Sleep test, all of these end with a RESET
}
if (resetCount < 4) {
initWiFi();
Expand Down Expand Up @@ -200,7 +206,7 @@ void runTest4() {
WiFi.forceSleepWake(); // reconnect with previous STA mode and connection settings
uint32_t wifiStart = millis();
while ((!WiFi.localIP()) && (millis() - wifiStart < wifiTimeout)) {
delay(50);
yield();
}
if (WiFi.localIP()) { // won't go into Automatic Sleep without an active WiFi connection
float volts = ESP.getVcc();
Expand All @@ -221,12 +227,14 @@ void runTest5() {
yield();
digitalWrite(LED, HIGH); // turn the LED off so they know the CPU isn't running
#ifdef testPoint
digitalWrite(testPoint, HIGH);
digitalWrite(testPoint, HIGH);
// testPoint LOW in callback tracks latency from WAKE_UP_PIN LOW to testPoint LOW
#endif
float 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)"));
Serial.print(F("millis() = "));
Serial.println(millis());
Serial.flush(); // needs a delay(100) or Serial.flush() else it doesn't 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);
Expand Down Expand Up @@ -256,6 +264,8 @@ void runTest6() {
//WiFi.mode(WIFI_SHUTDOWN); // Forced Modem Sleep for a more Instant Deep Sleep, and no long
// RFCAL as it goes into Deep Sleep
Serial.println(F("going into Deep Sleep now..."));
Serial.print(F("millis() = "));
Serial.println(millis());
Serial.flush(); // needs a delay(10) or Serial.flush() else it doesn't print the whole message
#ifdef testPoint
digitalWrite(testPoint, HIGH); // testPoint set HIGH to track Deep Sleep period, cleared at startup()
Expand Down Expand Up @@ -329,7 +339,7 @@ void runTest9() {

void resetTests() {
resetCount = 5; // start all over: do ESP.restart to insure a clean starting state
//
//
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);
Expand All @@ -338,7 +348,6 @@ void resetTests() {
ESP.restart();
}


void waitPushbutton(bool usesDelay, unsigned int delayTime) { // loop until they press the button
// note: 2 different modes, as 3 of the power saving modes need a delay() to activate fully
if (!usesDelay) { // quick interception of button press, no delay() used
Expand Down Expand Up @@ -393,19 +402,24 @@ void initWiFi() {
DEBUG_PRINTLN(WiFi.macAddress());
uint32_t wifiStart = millis();
while ((WiFi.status() != WL_CONNECTED) && (millis() - wifiStart < wifiTimeout)) {
delay(50);
yield();
}
if (WiFi.status() == WL_CONNECTED) {
DEBUG_PRINTLN(F("WiFi connected"));
} else {
Serial.println(F("WiFi timed out and didn't connect"));
}
while (!WiFi.localIP() && (WiFi.status() == WL_CONNECTED)) {
delay(50);
wifiStart = millis();
while ((!WiFi.localIP() && (WiFi.status() == WL_CONNECTED)) && (millis() - wifiStart < wifiTimeout)) {
yield();
}
WiFi.setAutoReconnect(true);
DEBUG_PRINT(F("WiFi Gateway IP: "));
DEBUG_PRINTLN(WiFi.gatewayIP());
DEBUG_PRINT(F("my IP address: "));
DEBUG_PRINTLN(WiFi.localIP());
if (WiFi.localIP()) {
DEBUG_PRINT(F("WiFi Gateway IP: "));
DEBUG_PRINTLN(WiFi.gatewayIP());
DEBUG_PRINT(F("my IP address: "));
DEBUG_PRINTLN(WiFi.localIP());
} else {
DEBUG_PRINTLN(F("IP addresses not acquired"));
}
}
4 changes: 2 additions & 2 deletions libraries/esp8266/examples/LowPowerDemo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ Turns off the modem (losing the connection), and reducing the amperage by > 50 m

### Test 4 - Automatic Light Sleep

Like Automatic Modem Sleep, with similar restrictions. Once configured it's immediately active when a connection is established. During periods of long delay() the amperage can drop to ~ 3 mA average. In a network with sparse traffic you might get something near 2-5 mA amperage. The LED blinks more slowly during this test as it's doing delay(350) to get the modem to sleep. With delay() times shorter than the DTIM beacon interval (100 mS beacons for these tests) the modem only goes into Automatic Modem Sleep, and with a longer delay() it will go more fully into Automatic Light Sleep.
Like Automatic Modem Sleep, with similar restrictions. Once configured it's immediately active when a connection is established. During periods of long delay() the amperage can drop to ~ 3 mA average. In a network with sparse traffic you might get something near 2-5 mA amperage. The LED blinks more slowly during this test as it's doing delay(350) to get the modem to sleep. With delay() times shorter than the DTIM beacon interval (100 mS beacons for these tests) the modem only goes into Automatic Modem Sleep, and with a longer delay() it will go more fully into Automatic Light Sleep. Although the CPU clock is cycling on and off to achieve low power, the RTC is still running so millis() and micros() are correct.

### Test 5 - Forced Light Sleep

Similar to Deep Sleep, but without the timer. The chip sleeps at 0.4 mA amperage until it is woken by an external interrupt. The only allowed interrupts are high level and low level; edge interrupts won't work. If you have a design that needs to be woken frequently (more often than every 2 seconds) then you should consider using Forced Light Sleep. For sleep periods longer than 2 seconds, Deep Sleep will be more energy efficient. The chip wakes after an interrupt in about 5 to 5.5 mS (regardless of CPU speed), but WiFi was turned off to enter Forced Light Sleep so you will need to re-initialize it if you are using WiFi.
Similar to Deep Sleep, but without the timer. The chip sleeps at 0.4 mA amperage until it is woken by an external interrupt. The only allowed interrupts are high level and low level; edge interrupts won't work. If you have a design that needs to be woken frequently (more often than every 2 seconds) then you should consider using Forced Light Sleep. For sleep periods longer than 2 seconds, Deep Sleep will be more energy efficient. The chip wakes after an interrupt in about 5 to 5.5 mS (regardless of CPU speed), but WiFi was turned off to enter Forced Light Sleep so you will need to re-initialize it if you are using WiFi. The RTC is slowed so millis() and micros() are also slowed during Forced Light Sleep.

### Test 6 - Deep Sleep, wake with RF_DEFAULT

Expand Down
0