The use of the ESP8266 in the world of IoT

User avatar
By ArjenV
#92680 Dear Forum.

A while ago I researched the battery-powered options for ESP8266. Found some interesting articles and managed to program an ESP8266 with a temp sensor that updates every 5 mins to Thingspeak. It runs on a 3.7V Li-ion 200mAh for ca 20 days.

I will not go into detail, but one of the 'tricks' involved is putting the deepsleep in the RF disabled mode, so that when it returns from deepsleep the RF is initially not enabled.

ESP.deepSleep( SLEEPTIME, WAKE_RF_DISABLED );

Further I use static IP and the rtcMemory to store the MAC address and WIFI channel as to invoke a quick WIFI connection. (And this is a huge time saver!)

WiFi.config( ip,dns, gateway, subnet );
WiFi.begin( WLAN_SSID, WLAN_PASSWD, rtcData.channel, rtcData.ap_mac, true );

Here's a sample code I used and adapted for my needs:
https://gitlab.com/diy_bloke/verydeepsl ... pSleep.ino

Sofar so good. My setup reads a temp-sensor every 5 minutes (along with battery Voltage and the time the ESP is awake) and sends it to Thingspeak. It is only 'on' for some 1.3 secs every measurement.

However, there is more to gain. What if I store -say- 12 measurements (conveniently 1 hour of data) in the rtcMemory and upload it only once an hour? The 'on' time per measurement would drastically go down (I measured ca 250msec) and the powerhungry RF radio would be on for only 1.3 secs once an hour.

So I stayed up one night and programmed a new setup. Using a NodeMCU for convenience. And it seems to work.

However, and here is the issue, -sorry for the long introduction- the quick connection 'trick' is not working anymore. It needs to get the ,already known, info again and that takes about 10 secs to complete.

I tried several things, put in delays after some RF commands, tried WIFI_off and other goto sleep commands but nothing worked sofar.

And the strange thing is that when I switch on the WIFI every measurement it will quick connect. Once I cycle throught the code without switching on the WIFI it won't quick connect anymore.

So I went back to a bare-bone setup. Again this 'bug' appears! So when coming from deepsleep and start the WIFI every time, a quick connection is made. But when I come from deepsleep and cycle one or more times without switching on the WIFI it looses the ability to quick connect.

Maybe someone can shed a light on this issue....

(Code may have some redundant lines, I copied them from my main program)
Where I put the arrow <----------------------in the code try putting 0 and then e.g 2. The nr of times it will cycle through deepsleep before turning on WIFI.


Code: Select all
//What happens if radio is switched on every xx times?
// No reading of sensors, only radio on/off
long SLEEPTIME=30e6; // 30secs

#include <ESP8266WiFi.h>
#include "secrets.h"

float tempSensor1, V_adjust=0.97, VBat;
unsigned long myChannelNumber = SECRET_CH_ID;
const char * myWriteAPIKey = SECRET_WRITE_APIKEY;
char* WLAN_SSID = SECRET_SSID;   // your network SSID (name)
char* WLAN_PASSWD = SECRET_PASS;   // your network password
int httpCode=0;
unsigned long previousTime = millis();
const unsigned long interval = 1000;
int OTA_Timeout = 60;

WiFiClient clientWiFi;
// Temperature wire is plugged into port D5 on the ESP8266 (GPIO 14)

// We make a structure to store connection information
// The ESP8266 RTC memory is arranged into blocks of 4 bytes. The access methods read and write 4 bytes at a time,
// so the RTC data structure should be padded to a 4-byte multiple.
struct {
  uint32_t crc32;   // 4 bytes
  uint8_t channel;  // 1 byte,   5 in total
  uint8_t ap_mac[6];// 6 bytes, 11 in total
  uint8_t index_count;  // 1 byte,  12 in total
} rtcData;

//We will use static ip
IPAddress ip( 192, 168, 1, 12 );// pick your own suitable static IP address
IPAddress gateway( 192, 168, 1, 254 ); // may be different for your network
IPAddress subnet( 255, 255, 255, 0 ); // may be different for your network (but this one is pretty standard)
IPAddress dns(192,168,1,254);

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH); //That means LED off..

  Serial.begin(115200);
  // we disable WiFi, coming from DeepSleep, as we do not need it right away
  WiFi.mode( WIFI_OFF ); //required? we start with rf disabled ???
  WiFi.forceSleepBegin();
  delay( 1 );

  // Try to read WiFi settings from RTC memory
  bool rtcValid = false;
  if( ESP.rtcUserMemoryRead( 0, (uint32_t*)&rtcData, sizeof( rtcData ) ) ) {
    // Calculate the CRC of what we just read from RTC memory, but skip the first 4 bytes as that's the checksum itself.
    uint32_t crc = calculateCRC32( ((uint8_t*)&rtcData) + 4, sizeof( rtcData ) - 4 );
    if( crc == rtcData.crc32 ) {
      rtcValid = true;
    }
    else {
      rtcData.index_count = 0;
    }
  }

 
  //-----------------
  // Now is the time to do stuff like reading sensors while the radio is still off
  // Start the sensors but to give it time the actual reading is done later
  //-----------------

  Serial.println();
  Serial.println();
  Serial.printf("millis: %i, msecs\n\r", millis());
 
  //Read the voltage
  int  vdd = analogRead(A0);
  Serial.printf("Vdd = %d -> %.2fV\n\r",vdd, vdd*5.02/1024*V_adjust);

  //Switch Radio back On
  Serial.printf("millis: %i, msecs\n\r", millis());
  Serial.printf("index count: %i\n\r", rtcData.index_count);
  if (rtcData.index_count >= 2) {  // set to 0 for every cycle, set to eg 4 for every 4 cycles.  <----------------------
    Serial.println(" Starting Radio");
 
    WiFi.forceSleepWake();
    delay(1);

    // Disable the WiFi persistence.  The ESP8266 will not load and save WiFi settings unnecessarily in the flash memory.
    WiFi.persistent( false );

    // Bring up the WiFi connection
    WiFi.mode( WIFI_STA );
    WiFi.config( ip,dns, gateway, subnet );

    //-----------Now we replace the normally used "WiFi.begin();" with a procedure using connection data stored by us
    if( rtcValid ) {
      // The RTC data was good, make a quick connection
      Serial.println("RTC OK, Quick connection!");
      WiFi.begin( WLAN_SSID, WLAN_PASSWD, rtcData.channel, rtcData.ap_mac, true );
    }
    else {
      // The RTC data was not valid, so make a regular connection
      Serial.println("RTC BAD, Regular connection!");
      WiFi.begin( WLAN_SSID, WLAN_PASSWD );
    }

    //------now wait for connection
    int retries = 0;
    int wifiStatus = WiFi.status();
    while( wifiStatus != WL_CONNECTED ) {
      retries++;
      if( retries == 100 ) {
        Serial.println("Retries WIFI = 100");
   
        // Quick connect is not working, reset WiFi and try regular connection
        WiFi.disconnect();
        delay(10);
        WiFi.forceSleepBegin();
        delay(10);
        WiFi.forceSleepWake();
        delay(10);
        WiFi.begin( WLAN_SSID, WLAN_PASSWD );
      }
      if( retries == 600 ) {
        Serial.println("Retries WIFI = 600");
   
        // Giving up after 30 seconds and going back to sleep
        WiFi.disconnect( true );
        delay(1);
        WiFi.mode( WIFI_OFF );
        Serial.printf("millis: %i, msecs\n\r", millis());
        ESP.deepSleep( SLEEPTIME, WAKE_RF_DISABLED );
      }
      delay(50);
      wifiStatus = WiFi.status();
    }
    //---------

    Serial.println(" WiFi connected");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
 
    // Write current connection info back to RTC
    rtcData.channel = WiFi.channel();
    memcpy( rtcData.ap_mac, WiFi.BSSID(), 6 ); // Copy 6 bytes of BSSID (AP's MAC address)
    rtcData.index_count = 0;  // reset count
    rtcData.crc32 = calculateCRC32( ((uint8_t*)&rtcData) + 4, sizeof( rtcData ) - 4 );
    ESP.rtcUserMemoryWrite( 0, (uint32_t*)&rtcData, sizeof( rtcData ) );

    //-------Now it's time to use your connection, e.g. by sending data

    // Do nothing just test deepsleep cycles....
 

    WiFi.disconnect( true );
    delay( 1 );
   
  }
  rtcData.index_count++;
  rtcData.crc32 = calculateCRC32( ((uint8_t*)&rtcData) + 4, sizeof( rtcData ) - 4 );
  ESP.rtcUserMemoryWrite( 0, (uint32_t*)&rtcData, sizeof( rtcData ) );
  // WAKE_RF_DISABLED to keep the WiFi radio disabled when we wake up
  Serial.printf("millis: %i, msecs\n\r", millis());
  Serial.println(" Going to Deepsleep zzzzz");

  ESP.deepSleep( SLEEPTIME, WAKE_RF_DISABLED );
}

void loop() {
}

// the CRC routine
uint32_t calculateCRC32( const uint8_t *data, size_t length ) {
  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;
}
User avatar
By ArjenV
#92689 I deleted all the redundant code and added some delays to see wether that would help. Alas
For the reading of the analog input at A0 I connected a potmeter from 3V3 to Gnd and with the middle sleeve to the analog input. Not required though. Nothing is done with the measurement in this program.

Code: Select all// from https://gitlab.com/diy_bloke/verydeepsleep/blob/master/VeryDeepSleep.ino

//What happens if radio is switched on every xx times?
// No reading of sensors, only radio on/off
// Connected a 10k potmeter between 3V3 and GND. Middle sleeve to Analog input.
// Just to measure the analog input. No processing further..


long SLEEPTIME=10e6; // 10secs
int i;

#include <ESP8266WiFi.h>
#include "secrets.h"  // For WIFI settings

char* WLAN_SSID = SECRET_SSID;   // your network SSID (name)
char* WLAN_PASSWD = SECRET_PASS;   // your network password

WiFiClient clientWiFi;

// We make a structure to store connection information
// The ESP8266 RTC memory is arranged into blocks of 4 bytes. The access methods read and write 4 bytes at a time,
// so the RTC data structure should be a multiple of 4-byte.
struct {
  uint32_t crc32;   // 4 bytes
  uint8_t channel;  // 1 byte,   5 in total
  uint8_t ap_mac[6];// 6 bytes, 11 in total
  uint8_t index_count;  // 1 byte,  12 in total
} rtcData;

//We will use static ip
IPAddress ip( 192, 168, 1, 12 );// pick your own suitable static IP address
IPAddress gateway( 192, 168, 1, 254 ); // may be different for your network
IPAddress subnet( 255, 255, 255, 0 );
IPAddress dns(192,168,1,254);

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH); //That means LED off..

  Serial.begin(115200);
  // we disable WiFi, coming from DeepSleep, as we do not need it right away
  WiFi.mode( WIFI_OFF ); //required? we start with rf disabled ???
  delay(1);
  WiFi.forceSleepBegin();
  delay(1);

  // Try to read WiFi settings from RTC memory
  bool rtcValid = false;
  if( ESP.rtcUserMemoryRead( 0, (uint32_t*)&rtcData, sizeof( rtcData ) ) ) {
    // Calculate the CRC of what we just read from RTC memory, but skip the first 4 bytes as that's the checksum itself.
    uint32_t crc = calculateCRC32( ((uint8_t*)&rtcData) + 4, sizeof( rtcData ) - 4 );
    if( crc == rtcData.crc32 ) {
      rtcValid = true;
    }
    else {
      rtcData.index_count = 0;
    }
  }

  Serial.println();
  Serial.println();
  Serial.printf("millis: %i, msecs\n\r", millis());
 
  //Read the voltage
  int  vdd = analogRead(A0);
  Serial.printf("Vdd = %d\n\r",vdd);
  Serial.printf("millis: %i, msecs\n\r", millis());
  Serial.printf("index count: %i\n\r", rtcData.index_count);
  if (rtcData.index_count >= 2) {  // set to 0 for every cycle, set to eg 4 for every 4 cycles. <----------------
    Serial.println(" Starting Radio");    //Switch Radio back On
    WiFi.forceSleepWake();
    delay(1);
    // Disable the WiFi persistence.  The ESP8266 will not load and save WiFi settings unnecessarily in the flash memory.
    WiFi.persistent( false );
    delay(1);

    // Bring up the WiFi connection
    WiFi.mode( WIFI_STA );
    delay(1);
    WiFi.config( ip,dns, gateway, subnet );
    delay(1);

    //-----------Now we replace the normally used "WiFi.begin();" with a procedure using connection data stored by us
    if( rtcValid ) {
      // The RTC data was good, make a quick connection
      Serial.println("RTC OK, Quick connection!");
      Serial.printf("Channel: %i ", rtcData.channel);
      for (i=0; i<6; i++) {
        Serial.printf("%x ", rtcData.ap_mac[i]);
      }
      Serial.println();
     
      WiFi.begin( WLAN_SSID, WLAN_PASSWD, rtcData.channel, rtcData.ap_mac, true );
      delay(1);
    }
    else {
      // The RTC data was not valid, so make a regular connection
      Serial.println("RTC BAD, Regular connection!");
      WiFi.begin( WLAN_SSID, WLAN_PASSWD );
    }

    //------now wait for connection
    int retries = 0;
    int wifiStatus = WiFi.status();
    while( wifiStatus != WL_CONNECTED ) {
      retries++;
      if( retries == 100 ) {
        Serial.println("Retries WIFI = 100");
   
        // Quick connect is not working, reset WiFi and try regular connection
        WiFi.disconnect();
        delay(10);
        WiFi.forceSleepBegin();
        delay(10);
        WiFi.forceSleepWake();
        delay(10);
        WiFi.begin( WLAN_SSID, WLAN_PASSWD );
      }
      if( retries == 600 ) {
        Serial.println("Retries WIFI = 600");
   
        // Giving up after 30 seconds and going back to sleep
        WiFi.disconnect( true );
        delay(1);
        WiFi.mode( WIFI_OFF );
        Serial.printf("millis: %i, msecs\n\r", millis());
        ESP.deepSleep( SLEEPTIME, WAKE_RF_DISABLED );
      }
      delay(50);
      wifiStatus = WiFi.status();
    }
    //---------

    Serial.println(" WiFi connected");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
    Serial.printf("Channel: %i ", WiFi.channel());
    Serial.println(WiFi.BSSIDstr());
   
    // Write current connection info back to RTC
    rtcData.channel = WiFi.channel();
    memcpy( rtcData.ap_mac, WiFi.BSSID(), 6 ); // Copy 6 bytes of BSSID (AP's MAC address)
    rtcData.index_count = 0;  // reset count
    rtcData.crc32 = calculateCRC32( ((uint8_t*)&rtcData) + 4, sizeof( rtcData ) - 4 );
    ESP.rtcUserMemoryWrite( 0, (uint32_t*)&rtcData, sizeof( rtcData ) );

    // Do nothing just test deepsleep cycles....
    WiFi.disconnect( true );
    delay( 1 );
   
  }
  rtcData.index_count++;
  rtcData.crc32 = calculateCRC32( ((uint8_t*)&rtcData) + 4, sizeof( rtcData ) - 4 );
  ESP.rtcUserMemoryWrite( 0, (uint32_t*)&rtcData, sizeof( rtcData ) );
  Serial.printf("millis: %i, msecs\n\r", millis());
  Serial.println(" Going to Deepsleep zzzzz");

  // WAKE_RF_DISABLED to keep the WiFi radio disabled when we wake up
  ESP.deepSleep( SLEEPTIME, WAKE_RF_DISABLED );
}

void loop() {
  // no loop .....
}

// the CRC routine
uint32_t calculateCRC32( const uint8_t *data, size_t length ) {
  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;
}
User avatar
By btidey
#92693 This is just a theory but I suspect this may be to do with the part where you

// Write current connection info back to RTC

In my experience the calls to get the Wifi.Channel and Wifi.BSSID do not return the actual information after a connection is made. They just return the data from the persistent memory location. So when you have persistent false and have cycles where the connection is not made then that data will be invalid.

I did a mod to the WifiManager library which is in the feature_fastConnect branch of that code. I found the way round the problem was to retrieve the channel and BSSID during a channel scan where you do get reliable channel and BSSID information. In this library persistent true is used so that once a successful connection had been made then a aimple Wifi.begin() would use previously stored channel and BSSID values. If they had changed for some reason at the router then the failure triggers a scan to obtain the correct values and make them persistent again. So there is only a delay if router values change.

So I think maybe you could change to using persistent true and Wifi.begin during those cycles where you make a wifi connection. You then need the scan routine to get correct values for the first initialisation or when things change. Alternatively you could have persistent false and explicitly add the rtc values in but then you should not always store the values back to rtc but instead only do a rescan and then refresh the rtc values if you detect a connect failure.

The scan routine is at
https://github.com/tzapu/WiFiManager/bl ... anager.cpp

in getFastConfig(String ssid) at the end of the code
User avatar
By ArjenV
#92704 Thanks for your reaction. Had to read it several times to grasp what you were saying. I am not a noob, but certainly not a pro.

So I changed my code: 1st I just commented out the WiFi.persistent( false ); Alas Did not work. Then I set it to 'true'
WiFi.persistent( true );
Again no success.

I checked with my modem/router at home. The MAC addres and channel are exactly what the ESP finds and stores in rtc memory. It does not change within a few minutes or even days. So the call to

WiFi.begin( WLAN_SSID, WLAN_PASSWD, rtcData.channel, rtcData.ap_mac, true );

is valid in my opinion. It is the same call when I put the if statement in a zero state --> i.e call the RF every time. It then works.

What else can I try?