First Sketch Blues (NodeMCU, DS18B20 & sleep mode)

Hello Everyone,

Total noob here, trying to learn by my many mistakes. But this one has me flummoxed. A very simple project - NodeMCU with two DS18B20 sensors.Programmed with Arduino IDE. Got the sensors working, now trying to introduce sleep mode. Blynk on Android 8. No local server (yet).

I originally kept getting sensor errors, so tried lighting the onboard LED for 1s every cycle, just to make sure the device was waking regularly (turns out my sensor error was a loose wire!).

But - although the device is now sleeping for 1 minute each cycle and correctly reporting temperatures to Blynk, the damned LED stays on for a whole ten seconds every cycle. And this has me doubting my sanity. I originally used BlynkTimer but in desperation turned to the dreaded delay() as you can see. But that made no difference.

I’m sure it’s something dumb I’m doing, perhaps someone can {gently} show me my error, so I can move on with my life…

Thanks in anticipation, code follows:



/*  Got sick of trying to make BlynkTimer work, so trying using the evil delays insteaad.
 *  Shame on me.
 *  Well - that doesn't work, same results. Time to Ask the Community.
 */


//#define BLYNK_PRINT Serial    // Comment this out to disable prints and save space


// Include the Libraries we need
#include <BlynkSimpleEsp8266.h>
#include <OneWire.h>
#include <DallasTemperature.h>


//Set up the sensors
#define ONE_WIRE_BUS 5        // This is the ESP8266 pin to which the sensors are connected
OneWire oneWire(ONE_WIRE_BUS);        // Setup a oneWire instance to communicate with DS18B20
DallasTemperature sensors(&oneWire);        // Pass our oneWire reference to Dallas Temperature

DeviceAddress tempSensor1 = { 0x28, 0xF4, 0x11, 0xBD, 0x04, 0x00, 0x00, 0x93 };        // Hard-coded address of Temperature Probe #1
DeviceAddress tempSensor2 = { 0x28, 0x4D, 0xA3, 0xBC, 0x04, 0x00, 0x00, 0x41 };        // Hard-coded address of Temperature Probe #2


// Set up the Blynk parameters
char auth[] = "xxx461";
char ssid[] = "xxxGM";
char pass[] = "xxx984";


// Initialise the timer
// BlynkTimer timer;


// Declare variables
float temperature1, temperature2;         // Variables for storing temperatures
const int UpdateInterval = 1 * 60 * 1000000;        // for a 1 minute sleep time (1 mins x 60secs * 1000000uS) CHANGE THIS TO 30min AFTER TESTING



void setup() {
  //Serial.begin(9600);      
  Blynk.begin(auth, ssid, pass);

  pinMode(2, OUTPUT);     // Initialize the built-in LED pin as an output

  while (Blynk.connect() == false) {
    // Wait until connected
  }
  
  sensors.begin();
  sensors.setResolution(tempSensor1, 10);
  sensors.setResolution(tempSensor2, 10);

  // timer.setTimeout(100, ledOn);        // Wait 0.1 second then run ledOn function
  ledOn();
  delay(1000);
  
  // timer.setTimeout(1000, ledOff);        // Wait 1 second then run ledOff function
  ledOff();

  // timer.setTimeout(100, sendSensor1);        // Wait 0.1 second then run sendSensor1 function
  sendSensor1();
  delay(2000);
    
  // timer.setTimeout(2000, sendSensor2);        // Wait 2 seconds then run sendSensor2 function
  sendSensor2();
  delay(5000);
  
  // timer.setTimeout(5000, gotoSleep);        // Wait 5 seconds then run gotoSleep function
  gotoSleep();
  
}



void loop() {
  Blynk.run();
  // timer.run();
  

}



void ledOn() {
  digitalWrite(2, LOW);        // Turn the LED on
}

void ledOff() {
  digitalWrite(2, HIGH);        // Turn the LED off
}

void sendSensor1() {
  sensors.requestTemperatures();
  temperature1 = sensors.getTempC(tempSensor1);
  Blynk.virtualWrite(1, temperature1);
}

void sendSensor2() {
  sensors.requestTemperatures();
  temperature2 = sensors.getTempC(tempSensor2);
  Blynk.virtualWrite(2, temperature2);
}

void gotoSleep() {
  ESP.deepSleep(UpdateInterval, WAKE_RF_DEFAULT);         // Sleep for the time set by 'UpdateInterval'
  delay(500);        // wait for deep sleep to happen
}

```cpp

It is reverse logic

When you pull the pin low it turns the led on.

1 Like

When you’re writing code for deep sleep, where you want your device to wake up, do something once (take temperature readings from two sensors in this case), upload the results to Blynk then go back to sleep, the program structure needs to be very different.

There are no processes that you want to repeat over and over again, so you can have an empty void loop.
You don’t want to initialise a Blynk Timer and wait for it to run just the once, as this wastes valuable wake-time.

If you use Blynk.begin, which is a blocking function, then if your device can’t connect to Wi-Fi or to Blynk then the device will stay awake and keep trying to connect until either it’s successful, or the battery goes flat.

The program flow should be something like this:

Try to connect to Wi-Fi a set number of times, with a short delay between each.
If not connected to Wi-Fi then go to sleep.
If connected to Wi-Fi then try to connect to Blynk.
If not connected to Blynk then go to sleep.
If connected to Blynk then take temperature readings.
Upload temperature readings to Blynk.
Go to sleep.

You could refine the logic so that if the device can’t connect to Wi-Fi or Blynk then it sleeps for a different (probably longer) amount of time before trying again - based on the principal that a Wi-Fi/Internet error exists then it will probably require some manual intervention before it works again, so there’s little point in keeping on trying every minute.

As waking from sleep reboots the device and resets any variables then each wale cycle is a one-shot affair. If you need to store/retrieve values to do some sort of comparison, then these values can be stored on the Blynk server against a virtual pin and retrieved using a Blynk.syncVirtual(VPin) after the device has connected to Blynk.

I have some code somewhere that needs a bit of tidying-up. If I get chance then I’ll dig it out and modify it, then post it later today.

Pete.

2 Likes

Thanks, Pete - I’d really appreciate that. In the interests of economical code I might just test for Blynk connection (which would mean wifi must be okay).

The end project will be five of these things monitoring ten fridge/freezer temperatures. A later stage will be to install a local server on a Rasp Pi which would include checks on the health of the five temperature stations.

I’m still perplexed as to why the light stays on for so long. The ledOn() function only gets called after the two potential blockers (wait for wifi and connect to Blynk) have already been successful. And the only thing between ledOn() and ledOff() is a 1000ms delay (or timer).

It’s almost as if the timer is using tens of milliseconds instead of singles!

I appreciate your help.

Adam

Okay, here’s the code that I’ve been experimenting with recently, modified to use your sensors and virtual pins.

The code expects a third display widget attached to pin V3, which will show how long (in seconds) your ESP device was awake for.

Previously, the shortest wake time I was getting was around the the 5 second mark, when using Blynk.begin and an IP address assigned by DHCP.
With this code I’m consistently getting wake times around the 1.2 second mark. This is without the two Dallas temperature sensors connected (I don’t have any to try it with), but the code is attempting to take a reading and returning a value of -127 for each nonexistent sensor, so I don’t imagine that the times would be too much longer with sensors connected.
This is using the Blynk cloud server, with ping times averaging around 80ms.

#define BLYNK_PRINT Serial
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <OneWire.h>
#include <DallasTemperature.h>

//Set up the sensors
#define ONE_WIRE_BUS 5                // This is the ESP8266 pin to which the sensors are connected
OneWire oneWire(ONE_WIRE_BUS);        // Setup a oneWire instance to communicate with DS18B20
DallasTemperature sensors(&oneWire);  // Pass our oneWire reference to Dallas Temperature

DeviceAddress tempSensor1 = { 0x28, 0xF4, 0x11, 0xBD, 0x04, 0x00, 0x00, 0x93 };        // Hard-coded address of Temperature Probe #1
DeviceAddress tempSensor2 = { 0x28, 0x4D, 0xA3, 0xBC, 0x04, 0x00, 0x00, 0x41 };        // Hard-coded address of Temperature Probe #2

// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature DS18B20(&oneWire);

// Set up the WiFi credentials
const char *ssid =                "*****";      
const char *pass =                "*****";

// Set up the  network details - Only needed if you want to have a static IP address. If not used then delete the 'WiFi.config(device_ip, dns, gateway, subnet);' line from void WiFi_Connect()
IPAddress device_ip               (192,168,1,199);                      // Static IP Address for the device
IPAddress dns                     (192,168,1,1);                        // Normally the IP address of your router
IPAddress gateway                 (192,168,1,1);                        // The IP address of your router
IPAddress subnet                  (255,255,255,0);                      // The subnet used by your network

// Set up the Blynk parameters
const char auth[] =            "*****";   // Your Blynk auth code 
const char blynk_server [] =   "blynk-cloud.com"; // new variable to hold the name of the Blynk server
const int blynk_port =         8080;              // new variable to hold the port used by the Blynk server

// Declare variables
float temperature1;
float temperature2;
int wifi_connect_count = 0;          // Variable to keep track of how manty times we've tried to connect to the Wi-Fi
int wifi_connect_max_retries = 10;   // Variable to specify how many attempts we will have at connecting to the Wi-Fi

float sleep_time_minutes =      5;   // Variable - how long (in minutes) we sleep for. As this is a float variable then it can be say 0.5 for a 30 second test


void sendSensors()
{
  sensors.requestTemperatures();
  temperature1 = sensors.getTempC(tempSensor1);
  temperature2 = sensors.getTempC(tempSensor2);
  
  Blynk.virtualWrite(V1, temperature1);  
  Blynk.virtualWrite(V2, temperature2);

  Serial.print("Temp1: "); 
  Serial.println(temperature1); 
  Serial.print("Temp2: "); 
  Serial.println(temperature2); 
}


void WiFi_Connect() // Handle the connection to the Wi-Fi network
{
  Serial.println(F("Connecting to Wi-Fi"));
  WiFi.config(device_ip, dns, gateway, subnet); // Not needed if you just want to have a DHCP assigned IP address. If you don't use this then delete the device_ip, dns, gateway & subnet declarations
    
  if (WiFi.status() != WL_CONNECTED)
  {
      WiFi.begin(ssid, pass); // connect to the network
  }
  while (WiFi.status() != WL_CONNECTED  && wifi_connect_count < wifi_connect_max_retries) // Loop until we've connected, or reached the maximum number of attempts allowed
  {
    delay(500);
    wifi_connect_count++;   
    Serial.print(F("Wi-Fi connection - attempt number "));
    Serial.print(wifi_connect_count);
    Serial.print(" of ");
    Serial.println(wifi_connect_max_retries);  
  }
  
  if (WiFi.status() == WL_CONNECTED)
  {
    WiFi.mode(WIFI_STA);
    Serial.println(F("Wi-Fi CONNECTED"));
    Serial.println();
  }
} // End of void WiFi_Connect



void setup()
{
    // Choose the serial baud rate that allows you to see the ESP boot messages correctly...  
    //Serial.begin(115200);
    Serial.begin(74880);

  // 1) Attempt to connect to Wi-Fi a few times (how many times we try is specified by the 'wifi_connect_max_retries' variable)
  // 2) If we successfully connected to Wi-Fi then attempt to connect to Blynk in a non-blocking way. If we aren't connected to Wi-Fi then go to sleep
  // 3) If we connected to Blynk then run the rest of the code as normal. If we aren't connected to Blynk then go to sleep
  
  //  Blynk.begin(auth, ssid, pass);//starts wifi and Blynk - Not used in the new code as it's a blocking function
  WiFi_Connect(); // Attempt to connect to Wi-Fi

  if (WiFi.status() == WL_CONNECTED)               // If we managed to connect to Wi-Fi then try to connect to Blynk, else go to sleep
  {
    Blynk.config(auth, blynk_server, blynk_port);  // Initialise the Blynk connection settings
    Blynk.connect();                               // Attempt to connect to Blynk
  }
  else
  {
    Serial.println ("Wi-Fi connection failed - going to sleep");
    //sleep_time_minutes = sleep_time_minutes * 2; // If you enable this line of code the it will make the device go to sleep for twice as long before trying again. Changing to 0.5 would make it try again sooner than normal
    Deep_Sleep_Now();
  }
  
  if (Blynk.connected())                          // If we managed to connect to Blynk then carry-on as normal, else go to sleep
  {  
    Serial.println ("Connected to Blynk");
    // If you wanted to retrieve data stored against a virtual pin from the Blynk server the you'd call Blynk.syncVirtual(VPin) here. This would trigger the corresponding BLYNK_WRIRE(VPin) callback
  }
  else
  {  
    // If you enable the following line of code the it will make the device go to sleep for twice as long before trying again. Changing to 0.5 would make it try again sooner than normal, you can adjust to suit your needs
    //sleep_time_minutes = sleep_time_minutes * 2; 
    Serial.println("Blynk connection failed - going to sleep");
    Deep_Sleep_Now();
  }

  sensors.begin();
  sensors.setResolution(tempSensor1, 10);
  sensors.setResolution(tempSensor2, 10);

  Blynk.run(); // Give Blynk some processor time 

  sendSensors();  // Take the temperature readings and upload to Blynk

  float wake_time = (float)millis()/float(1000); // Find out how long since the ESP rebooted
  Blynk.virtualWrite(V3, wake_time);
  Serial.print("Wake Time = ");
  Serial.print(wake_time);
  Serial.println(" seconds");
  Blynk.run(); // Needed to ensure that the Wake Time value is always uploaded to Blynk before going to sleep
  delay(100);  // Give Blynk time to do its thing before going to sleep
  Deep_Sleep_Now();
}


void Deep_Sleep_Now()
{
  Serial.print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Going to sleep for ");
  Serial.print(sleep_time_minutes);  
  Serial.println(" minutes");   
  Serial.println();
  ESP.deepSleep(sleep_time_minutes * 60000000);  // Deep Sleep time is specified in micros
  delay(2000);
}

void loop()
{
  // voud loop is empty, but can't be deleted
}

Read the comments about the network settings, as these will need to be changed to meet your own network settings, or commented-out if you want to use DHCP assigned network settings.

This is the sort of serial output I’m getting:

 ets Jan  8 2013,rst cause:2, boot mode:(3,6)

load 0x4010f000, len 1384, room 16 
tail 8
chksum 0x2d
csum 0x2d
vbb28d4a3
~ld
Connecting to Wi-Fi
Wi-Fi connection - attempt number 1 of 10
Wi-Fi CONNECTED

[701] 
    ___  __          __
   / _ )/ /_ _____  / /__
  / _  / / // / _ \/  '_/
 /____/_/\_, /_//_/_/\_\
        /___/ v0.6.1 on ESP8266

[712] Connecting to blynk-cloud.com:8080
[934] Ready (ping: 91ms).
Connected to Blynk
Temp1: -127.00
Temp2: -127.00
Wake Time = 1.17 seconds
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Going to sleep for 5.00 minutes

One other thing to note, I’ve been having some issues with version 2.5.0 of the Arduino core, so these tests are done using version 2.4.2 of the core. If you’re having Wi-Fi connection issues or long wake times then maybe try downgrading your Core version (in Tools/Board/Boards Manager then scroll down to esp8266 by ESP8266 Community).

Pete.

1 Like

You’ll see in the code I’ve posted that it tries 10 times to connect to Wi-Fi (the number of attempts is defined in a variable) then goes to sleep it cant’t connect. It won’t try connecting to Blynk if it’s not connected to Wi-Fi.

Don’t forget that if you use my code with static IP addressed (faster connection to your router) then each device will need its own unique IP address.

The LED is actually being turned on by the pinMode statement, so is staying on for however long it takes to connect to Blynk then get to then get to the ledOff() function. Adding digitalWrite(2, HIGH); immediately after your pinMode statement cures the problem.

I worked this out by enabling serial prints and adding some Serial.print statements in the ledOn() and ledOff() functions and it was obvious that the LED was being turnhed on before the ledOn() function was being called.

Serial debug messages are invaluable when you’re developing code, they are the only way to monitor variable values and program flow.

Pete.

1 Like

Thanks, a valuable lesson about debugging.

The sketch is working well, I increased the delay in the WiFi_connect function to 2s as it was always taking 4 attempts to connect to my wifi and it’s now flawless. Without the debugging prints I’d never have known.

I’m sticking with DHCP for the moment but will probably go static for the finished project.
Uptime is usually around 3s, so I should get good battery life once I’m reading at 30 minute intervals.

One more question - in the WiFi_Connect() function you’ve used an F() in the line
"Serial.print(F("Wi-Fi connection - attempt number “));”

I see from the documentation that: “You can pass flash-memory based strings to Serial.print() by wrapping them with F()” but I don’t really understand the distinction between that and a normal print. Can you elaborate?

Many thanks again for your help

Adam

1 Like

Good, glad it was of some use to you.

When you’re serial printing simple text strings (no variables) to the serial monitor then it saves memory if you use what’s known as the F() Macro (weird name in my opinion).
It’s not really an issue when working with the the more modern boards that have more memory available, unless you’re trying to keep memory usage down to a minimum.

Pete.

1 Like

Thanks for the project. Finally, I found a working disconnect in the absence of wi-fi. Well, the Serial.print is excellent! I have a few questions. I redid ds18b20 for one sensor, commented out the lines // DeviceAddress tempSensor corrected the line sensors.setResolution (10); but after that the temperature is not transmitted. Transmitted with these lines. And I don’t understand why such a line in the code: DallasTemperature DS18B20 (& oneWire); if present: DallasTemperature sensors (& oneWire) ;? Can I remove it?

In order to communicate with DS18B20 sensor, we need to create object of DallasTemperature library and pass reference of one-wire object as a parameter.

Doesn’t this parameter do: DallasTemperature sensors(&oneWire);?
DallasTemperature DS18B20 (& oneWire); It seems superfluous to me.

This detailed article will help you to understand

I had some code and it was working fine.
Then I removed some bits that weren’t needed and now it doesn’t work anymore.
What did I do wrong?

:thinking: :thinking: :thinking:

Pete.

Yes, I appreciate your joke. But everything that I deleted everything is correct. The mistake was that I made one sensor in the code and removed the addresses of the sensors. So the “sensors.getTempC” command should change to “sensors.getTempCByIndex”, after such a change everything worked fine. Thanks for the code!