NodeMCU 8266 over WiFi, Blynk on iOS using Blynk Server. Ver. 0.6.7
I have followed the recommendation that I could find and have only Blynk.run and Time.run in the loop function. However after pushing value to Blynk server with an interval >5sec before going to deep sleep, I do have random updates?
// cyclic event logics
void cyclicEvent()
{
fanCycle++;
readSensor();
if (Blynk.connected())
{
Blynk.virtualWrite(V2, t);
Blynk.virtualWrite(V3, h);
Blynk.run();
}
if (!fanState)
{
Blynk.run();
// Store the fan cycle in RTC memory before deep sleep
ESP.rtcUserMemoryWrite(0, (uint32_t *)&fanCycle, sizeof(fanCycle));
// Sleep for 5 mins
ESP.deepSleep(30000000);
}
}
#include <Arduino.h>
#include <EEPROM.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>
#include <WiFiUdp.h>
#include <BlynkSimpleEsp8266.h>
#include "DHT.h"
#define DEBUG_INFO
// Define the digital pin connected to the DHT sensor and type
#define DHTPIN D2
#define DHTTYPE DHT22
// Firmware revision, firmware server url and project unique identifier
constexpr char projectID[] = "FanControl_B01"; // ESP8266 on NodeLCU simulating
constexpr int32_t FW_VERSION = 113; // 0.01.13
constexpr char fwWebServer[] = "MASTERYODA"; //"192.168.1.47";
constexpr char fwLocation[] = "/iothome/firmwares/";
// Blynk token
constexpr char auth[] = "xxxx";
BlynkTimer timer;
// retry attempt to connect to WiFi
const int retryConnectWiFi = 25;
// Initialize DHT sensor.
DHT dht(DHTPIN, DHTTYPE);
// inititate global WiFi client
WiFiClient wifiClient;
// Global sesnsor data
float t = 0.0;
float h = 0.0;
// Humidity threshold
static float humThreshold;
// timers counter
millis_time_t tSensorCycle = 0;
// fan state 0=off, 1=on
volatile uint8_t fanState = 0;
// fan cycle & fan threshold flag
volatile uint32_t fanCycle = 0;
constexpr uint8_t fanMaxCycle = 11;
volatile uint8_t humAboveThreshold = 0;
// Read the data from the DHT22 sensor
bool readSensor()
{
// Reading temperature or humidity takes about 250 milliseconds!
h = dht.readHumidity();
// Read temperature as Celsius (the default)
t = dht.readTemperature();
// Check if any reads failed and exit early (to try again).
if (isnan(h) || isnan(t))
{
#ifdef DEBUG_INFO //Debug info
Serial.println(F("Failed to read from DHT sensor!"));
#endif
return false;
}
else
{
#ifdef DEBUG_INFO //Debug info
Serial.print(F("Humidity: "));
Serial.print(h);
Serial.print(F("% Temperature: "));
Serial.print(t);
Serial.print(F("°C \n"));
#endif
return true;
}
}
// Turn the fan on and off
void FanControl(byte fanOnOff)
{
if (fanOnOff)
{
fanState = 1;
digitalWrite(D1, HIGH);
}
else
{
fanState = 0;
digitalWrite(D1, LOW);
}
if (Blynk.connected())
{
Blynk.virtualWrite(V1, fanState);
Blynk.run();
}
}
// cyclic event logics
void cyclicEvent()
{
fanCycle++;
#ifdef DEBUG_INFO //Debug info
Serial.println("Reading sensor.");
#endif
readSensor();
#ifdef DEBUG_INFO //Debug info
Serial.printf("Fan cycle value %d.\n", fanCycle);
Serial.printf("Humididty Threshold = %f.\n", humThreshold);
#endif
if (Blynk.connected())
{
#ifdef DEBUG_INFO //Debug info
Serial.println("Sending telemetry to Blynk.");
#endif
Blynk.virtualWrite(V2, t);
Blynk.virtualWrite(V3, h);
Blynk.run();
}
// humidity above threshold
if (h >= humThreshold)
{
if (!fanState)
{
FanControl(1);
}
fanCycle = 0;
humAboveThreshold = true;
#ifdef DEBUG_INFO //Debug info
Serial.println("Humidity above threshold.");
#endif
}
else
{
// transition from above to below humidity threshold
if ((fanCycle < fanMaxCycle) && humAboveThreshold)
{
humAboveThreshold = false;
FanControl(0);
}
// reached time cycle
if (fanCycle == fanMaxCycle)
{
FanControl(1);
#ifdef DEBUG_INFO //Debug info
Serial.println("Fan cycle reached.");
#endif
}
// after time cycle
if (fanCycle > fanMaxCycle)
{
FanControl(0);
fanCycle = 0;
#ifdef DEBUG_INFO //Debug info
Serial.printf("Fan cycle reset %d.\n", fanCycle);
#endif
}
}
if (!fanState)
{
#ifdef DEBUG_INFO //Debug info
Serial.println("Going to sleep.");
#endif
Blynk.run();
// Store the fan cycle in RTC memory before deep sleep
ESP.rtcUserMemoryWrite(0, (uint32_t *)&fanCycle, sizeof(fanCycle));
// Sleep for 5 mins
ESP.deepSleep(30000000);
}
}
// the setup function runs once when you press reset or power the board
void setup()
{
#ifdef DEBUG_INFO //Debug info
// Get the serial output started */
Serial.begin(115200);
Serial.printf("\nBooting chip id %d with SDK %s and firmware %d\n", ESP.getChipId(), ESP.getSdkVersion(), FW_VERSION);
Serial.println(ESP.getFullVersion());
#endif
// inititate the DHT22 sensor
dht.begin();
// Set the pin that control the relay
pinMode(D1, OUTPUT);
// read the fan cycle valur from the RTC memory
ESP.rtcUserMemoryRead(0, (uint32_t *)&fanCycle, sizeof(fanCycle));
// locate the humidity threshold from the flask memory (0)
EEPROM.begin(4);
// Setup a function to be called every second
timer.setInterval(30000L, cyclicEvent);
// local variables
uint8_t counter = 0;
// set WiFi mode to Station only
WiFi.mode(WIFI_STA);
if (WiFi.SSID().length() == 0)
{
#ifdef DEBUG_INFO //Debug info
Serial.print("Please use WPS on your router to connect this device.\n");
#endif
WiFi.beginWPSConfig();
}
// attempt to connect to the access point (AP), retry and if unsuccesfull print diagnostics
if (WiFi.SSID().length() != 0)
{
WiFi.begin();
#ifdef DEBUG_INFO //Debug info
Serial.print("Connecting to Wifi.");
#endif
while (WiFi.status() != WL_CONNECTED && counter <= retryConnectWiFi)
{
#ifdef DEBUG_INFO //Debug info
Serial.print(".");
#endif
delay(1000);
counter++;
}
#ifdef DEBUG_INFO //Debug info
Serial.println();
#endif
}
else
{
#ifdef DEBUG_INFO //Debug info
Serial.println("Authentication timeout.");
#endif
}
if (WiFi.status() == WL_CONNECTED)
{
// get the WiFi info
#ifdef DEBUG_INFO
WiFi.printDiag(Serial);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
#endif
// connect to Blynk
Blynk.config(auth);
Blynk.connect(10000);
Blynk.syncVirtual(V1);
Blynk.syncVirtual(V4);
// WiFi.disconnect();
// Build the url for the version file
String fwVersionUrl = "http://";
fwVersionUrl = fwVersionUrl + fwWebServer + fwLocation + projectID + ".version";
// access the version file and get the string inside
HTTPClient httpClient;
httpClient.begin(wifiClient, fwVersionUrl);
int httpCode = httpClient.GET();
#ifdef DEBUG_INFO //Debug info
Serial.print(fwVersionUrl);
Serial.printf("\nhttp return %d\n", httpCode);
#endif
if (httpCode == 200)
{
String versionStr = httpClient.getString();
#ifdef DEBUG_INFO //Debug info
Serial.print("Available firmware version: ");
Serial.print(versionStr);
Serial.println();
#endif
long srvFwVersion = versionStr.toInt();
// if the server firware version number is bigger than the one of the current firmwre, update the current firmware
if (srvFwVersion > FW_VERSION)
{
String fwBinUrl = fwLocation;
fwBinUrl = fwBinUrl + projectID + '_' + srvFwVersion + ".bin";
#ifdef DEBUG_INFO //Debug info
Serial.printf("Updating to firmware %ld from ", srvFwVersion);
Serial.print(fwBinUrl);
Serial.println();
#endif
t_httpUpdate_return ret = ESPhttpUpdate.update(wifiClient, fwWebServer, 80, fwBinUrl);
#ifdef DEBUG_INFO //Debug info
Serial.print("HTTP return code ");
Serial.print(ret);
Serial.println();
#endif
}
}
httpClient.end();
}
}
// Action on Virtual Pin 1 - On/Off
BLYNK_WRITE(V1)
{
int vpValue1 = param.asInt();
FanControl(vpValue1);
if (vpValue1)
{
fanCycle = 0;
}
}
// Humidity Threshold adjustment
BLYNK_WRITE(V4)
{
int vpValue1 = param.asInt();
humThreshold = (float)vpValue1;
EEPROM.write(0, vpValue1);
EEPROM.commit();
}
void loop()
{
Blynk.run();
timer.run();
}
Because most of the time the system will be in not operational mode and hence do not need to to be powered. The deepsleep is in microsecond, so actually 300sec = 5 mins. The project works just fine except for the update to blynk before deepsleep.
Comparing my code strategy with the one from Christophe, I could only spot a delay of 100ms after the Blynk.run(). So I did the same although I thought that the delay being a blocking function would not benefits the update however, since the addition of the delay before going to sleep seems to have remove the issue of random update. Feel free to comment if you think that this makes any sense.
The delay seems to allow the send process to complete effectively.
In a similar way, with the ESP8266, you often see a delay after the deep sleep command. This ensures that the device has time to go to sleep before starting the code loop again - which somehow stops deep sleep happening.
Interestingly, I’m doing some deep sleep with an ESP32 TTGO T-Call using GSM connection at the moment. It’s behaving differently to anything I’ve observed with an ESP8266 connected via WiFi, which I think is down to the longer ping time increasing the latency and producing some odd results with Blynk.
I’ve come-up with a solution by structuring my code totally differently and I’ll share the results here soon.
Okay, as a follow-up to my previous post I was going to include my code, but I think it might be easier if I simply explained what my project is supposed to do, and how I’ve structured my code (and why).
The project is designed to wake-up every few hours, establish a GSM connection to the Blynk cloud server then check the status of an app button connected to pin V1.
If the button is on then an action is taken (a relay is toggled to reboot a device), and the app button is set to off again. A table widget is updated with either a “no action” or “rebooted” message and a timestamp.
The device then goes back to sleep for another few hours.
I ended-up structuring my code as follows…
void setup
Connect to Blynk via GSM, using Blynk.config/Blynk.connect so it’s non-blocking
void loop
contains Blynk.run
increments a counter to count how many times the void loop is executed
performs an if check against the counter which will make the device deepsleep if the counter reaches a present limit
BLYNK_CONNECTED
performs an rtc.begin to get the current time from the server (for the timestamp in the table)
performs a Blynk.syncVirtual(V1) to get the value of the switch widget
BLYNK_WRITE(V1)
Contains the main code which:
Gets the value of the switch widget attached to V1
Toggles the relay if necessary
Sets the switch widget attached to V1 to Off if necessary
Writes the results out to a table widget along with a timestamp
loops through a number of Blynk.run and delay(100) iterations
Initiates deepsleep again
This is what happens when the device boots up, or wakes from deepsleep (assuming that all goes according to plan)…
void.setup executes and establishes a GSM connection and connects to the Blynk server.
The BLYNK_CONNECTED function is then called and processed.
You’d expect that the BLYNK_WRITE(V1) would then be called almost immediately, or possibly after one or two Blynk.runs being executed. However, that’s not what happens in practice - this maybe because of the slow ping times (550 - 60ms in testing) associated with the GSM connection. Instead, the void loop begins to execute, and goes through over 1000 iterations before the BLYNK_WRITE(V1) function actually executes.
The main code contained within BLYNK_WRITE(V1) is executed. The relay is toggled if necessary then the V1 button widget is set to off (if necessary) and the table widget is updated. A number of Blynk.run commands, interspersed with 100ms delays are then needed to ensure that the V1 widget and tale are updated before going back to sleep. I use 6 Blynk.runs/delays in a for loop to achieve the desired results then initiate deepsleep.
Total wake time (assuming the relay doesn’t need to be toggled) is around 27 seconds, but most of this time is associated with establishing the GSM connection. The actual GSM connected time is around 9 seconds.
The counter in the void loop, and the associated if test is to deal with a situation where a connection is established with the GSM network, but the Blynk.connect is unsuccessful and times-out - because the Blynk server is unreachable.
In this situation, after the Blynk.connect times-out the void loop will start to execute. If no connection is established by the time the loop limit is reached then the device will go to sleep until next time. I use a shorter deepsleep time this situation, so I don’t have to wait as long before the device tries again.