I’m having issues with connectivity. My Blynk connection is lost after random periods longer than several hours, requiring me to restart my Arduino almost every day. (The code is for an automated watering system that sense dry soil and actives relays to water the respective plant.)
On restart connections to Blynk are faultless, but something is causing Blynk to lose connection. I believe I am not flooding the Blynk server as I send a just a few data points every 15 seconds and the restarts are always happening during while the short 30 second watering periods kick in with more frequent updates.
In my network monitoring of my Wifi, I do not see any indications that the WiFi network itself might be dropping the Arduino. I have stable Wifi and don’t see issues with any other devices.
Any suggestions or ideas what might be causing these regular connection losses would be appreciated!
• Hardware model + communication type: Arduino NANO 33 IoT, WiFi communication. I have a Relay board attached that is being controlled by the digital outputs.
• Smartphone OS (iOS or Android) + version: Apple iPhone 11, iOS version 14.4.2
• Blynk server or local server. Using Blynk server
• Blynk Library version: 0.6.1
//#include <MemoryFree.h>
#define BLYNK_PRINT Serial
#define BLYNK_ON // Should blynk be updated with data?
#include <SPI.h>
#include <WiFiNINA.h>
#include <BlynkSimpleWiFiNINA.h>
#include <TimeLib.h>
#include <WidgetRTC.h>
// Define moisture sensor pins.
const int sensorNum=8; // How many analog pins exist
int SensorPins[sensorNum]={14,15,0,0,0,0,0,0}; // 0 = no sensor
int plantStartWateringLevel[sensorNum]={0,0,0,0,0,0,0,0}; // Level at which if sensor is below this level, plant should start to be watered.
int plantStopWateringLevel[sensorNum]={70,70,70,70,70,70,70,70}; // Level at which if sensor is above this level, plant should stop being watered.
String plantName[sensorNum]={"Bonsai", "Bonsai","","","","","",""};
int valvePin[sensorNum]={3,3,0,0,0,0,0,0}; // Which Arduino digital pin to trigger when sensor i is above / below trigger level.
float moistureScore[sensorNum];
float pumpDuration; // How many minutes have passed since water storage container was refilled.
// Defining digital and analog pins
#define PUMP_RELAY 2
#define VALVE_RELAY 3
#define BLYNK_FAST_UPDATE 3000
#define BLYNK_SLOW_UPDATE 15000
// Define the extreme values for the sensor output.
int waterValue=340; // Measured raw reading when sensor is in water
int dryValue=900; // Measured raw reading when sensor is in air
const int dryBuffer=5; // dryValue is dryBuffer % above the value of dryValue
const int wetBuffer=3; // waterValue is wetBuffer % below the value of waterValue
const int maxMoistureScore=100;
float wetValue=waterValue; // Assumed raw value to equal 100% water
float zeroValue=dryValue; // Assumed raw value to equal 0% water
int sensorValueOrig = 0;
int countValvesOpen=0; // How many plant valves are open. Only run pump if at least 1 valve is open.
double valveStartTime=0; // To hold millis when start watering.
double valveDuration=0; // To hold watering duration in millis.
// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "MyAuthToken";
// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "MYSSID";
char pass[] = "MyPassword";
// Attach virtual serial terminal to Virtual Pin V1
WidgetTerminal terminal(V0);
BlynkTimer timer;
WidgetRTC rtc;
float lastMoistureAverage[sensorNum]={maxMoistureScore/2, maxMoistureScore/2, maxMoistureScore/2,maxMoistureScore/2,maxMoistureScore/2,maxMoistureScore/2,maxMoistureScore/2,maxMoistureScore/2};
float trimValue=0.2;
double lastBlynkUpdate=0;
double blynkUpdateGap=BLYNK_SLOW_UPDATE; // How many millis should pass before sensor values are sent to Blynk.
// Digital clock display of the time
void clockDisplay()
{
String my_h = MyPrintDigits(hour());
String my_m = MyPrintDigits(minute());
String my_s = MyPrintDigits(second());
String currentTime = my_h+ ":"+my_m +":"+ my_s;
String currentDate = String(day()) + "." + month() + "." + year();
Serial.print("Current time: ");
Serial.print(currentDate);
Serial.print(" ");
Serial.print(currentTime);
Serial.println();
// Send time to the App
Blynk.virtualWrite(V9, currentTime);
// Send date to the App
Blynk.virtualWrite(V8, currentDate);
}
String timeNow() {
String my_h = MyPrintDigits(hour());
String my_m = MyPrintDigits(minute());
String my_s = MyPrintDigits(second());
String currentTime = my_h+ ":"+my_m +":"+ my_s;
return currentTime;
}
String dateNow() {
String currentDate = String(day()) + "." + month() + "." + year();
return currentDate;
}
BLYNK_CONNECTED() {
// Synchronize time on connection
rtc.begin();
Blynk.syncVirtual(V1, V5, V6);
}
void setup()
{
Serial.begin(9600);
Blynk.begin(auth, ssid, pass);
pinMode(PUMP_RELAY, OUTPUT);
pinMode(VALVE_RELAY, OUTPUT);
zeroValue=computeZeroValue(dryValue, waterValue, dryBuffer, wetBuffer);
wetValue=computeWetValue(dryValue, waterValue, dryBuffer, wetBuffer);
// Clear the terminal content
printDateTime();
terminal.println("Blynk restarted.");
terminal.flush();
}
void printDateTime() {
terminal.print(dateNow());
terminal.print(" ");
terminal.println(timeNow());
}
BLYNK_WRITE(V1)
// Bonsai plant - start watering
{
int pinValue=param.asInt(); // assigning incoming value from pin V1 to a variable
plantStartWateringLevel[0]= pinValue;
plantStartWateringLevel[1]= pinValue; // assigning incoming value from pin V1 to a variable
// Setting 2 sensors as Bonsai has 2 sensors.
Serial.print("Bonsai starts watering below ");
Serial.println(plantStartWateringLevel[0]);
printDateTime();
terminal.print("Start watering bonsai if below: ");
terminal.println(plantStartWateringLevel[0]);
terminal.flush();
}
BLYNK_WRITE(V6)
// Bonsai plant - stop watering trigger level
{
int pinValue=param.asInt(); // assigning incoming value from pin V1 to a variable
plantStopWateringLevel[0]= pinValue; // assigning incoming value from pin V6 to a variable
plantStopWateringLevel[1]= pinValue; // assigning incoming value from pin V6 to a variable
Serial.print("Bonsai stops watering above ");
Serial.println(plantStopWateringLevel[0]);
printDateTime();
terminal.print("Stop watering bonsai if above: ");
terminal.println(plantStopWateringLevel[0]);
terminal.flush();
}
BLYNK_WRITE(V2) // Manually start watering Bonsai
{
int pinValue = param.asInt(); // assigning incoming value from pin V2 to a variable
if(pinValue==1) {
startWatering(0);
countValvesOpen=1;
}
}
BLYNK_WRITE(V12) // Emergency stop
{
int pinValue = param.asInt(); // assigning incoming value from pin V12 to a variable
if(pinValue==1 && countValvesOpen==1) { // Only stop if valve actually open.
stopWatering(0);
countValvesOpen=0;
}
}
BLYNK_WRITE(V7) // Button to signal that water container has been refilled.
{
int pinValue = param.asInt(); // assigning incoming value from pin V7 to a variable
if(pinValue==1) {
Serial.println("*** Water container refilled ***");
printDateTime();
terminal.println("Water container refilled. Resetting pumpDuration.");
terminal.flush();
pumpDuration=0;
}
}
void loop()
{
Blynk.run();
timer.run();
// Step through each sensorPin...
for (int i=0; i<(sensorNum); i++) {
// If sensor pin present
if (SensorPins[i]>0) {
// Read pin and return moisture level.
sensorValueOrig = analogRead(SensorPins[i]);
moistureScore[i]=computeMoistureScore(sensorValueOrig);
float currentMoistureAverage = filter(moistureScore[i], trimValue, lastMoistureAverage[i]);
lastMoistureAverage[i] = currentMoistureAverage;
}
else {
// No sensor on this pin
}
}
if (millis()>lastBlynkUpdate+blynkUpdateGap) {
// Enough time has passed. Send the sensor values to blynk.
lastBlynkUpdate=millis();
clockDisplay();
#ifdef BLYNK_ON
Blynk.virtualWrite(V5, pumpDuration);
for (int i=0; i<(sensorNum); i++) {
switch (i) {
case 0:
Blynk.virtualWrite(V10, (int) moistureScore[i]);
break;
case 1:
Blynk.virtualWrite(V11, (int) moistureScore[i]);
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 7:
break;
}
}
#endif
for (int i=0; i<(sensorNum); i++) {
if (SensorPins[i]>0) { // Check if this pin has a sensor.
if ((int) moistureScore[i]<plantStartWateringLevel[i] && countValvesOpen == 0) {
startWatering(i);
}
if ((int) moistureScore[i]>plantStopWateringLevel[i] && countValvesOpen==1) {
// stopWatering only if you are currently watering.
stopWatering(i);
}
}
}
}
}
void startWatering(int i) {
Serial.println("*** Valve Relay ON ***");
digitalWrite(valvePin[i], HIGH);
Serial.println("*** Pump Relay ON ***");
digitalWrite(PUMP_RELAY, HIGH);
valveStartTime=millis(); // Store millis when start watering.
printDateTime();
terminal.print("Watering ");
terminal.print(plantName[i]);
terminal.println("...");
terminal.flush();
blynkUpdateGap=BLYNK_FAST_UPDATE;
countValvesOpen=1;
}
void stopWatering(int i) {
Serial.println("*** Valve Relay OFF ***");
digitalWrite(valvePin[i], LOW);
Serial.println("*** Pump Relay OFF ***");
digitalWrite(PUMP_RELAY, LOW);
valveDuration=millis()-valveStartTime; // Store watering duration in millis.
terminal.print("Stopping watering of ");
terminal.print(plantName[i]);
terminal.print(". ");
terminal.print((valveDuration/1000));
terminal.println(" secs.");
pumpDuration+=(float) (valveDuration/60000);
terminal.print("Watering mins since last refil: ");
terminal.println(pumpDuration);
terminal.flush();
valveDuration=0;
blynkUpdateGap=BLYNK_SLOW_UPDATE;
}
// filter the current result using a weighted average filter:
float filter(float rawValue, float weight, float lastValue) {
float result = weight * rawValue + (1.0-weight)*lastValue;
return result;
}
float computeZeroValue(int dryValue, int waterValue, int dryBuffer, int wetBuffer) {
float zeroValue=dryValue+((dryBuffer*(dryValue-waterValue))/(100-dryBuffer-wetBuffer));
return zeroValue;
}
int computeWetValue(int dryValue, int waterValue, int dryBuffer, int wetBuffer) {
int wetValue=waterValue-((wetBuffer*(dryValue-waterValue))/(100-dryBuffer-wetBuffer));
return wetValue;
}
float computeMoistureScore(int reading) {
float b=(float)zeroValue-(float)wetValue;
float a=((float)reading-(float)wetValue)/b;
float moistureScore=(1-a)*(float)maxMoistureScore;
return moistureScore;
}
String MyPrintDigits(int digits) { // -- Utility function for digital clock display: prints preceding colon and leading 0
String new_digits="";
if (digits < 10) new_digits += "0";
new_digits += String(digits);
return new_digits;
}