Serious issue: Digitalwrite fails after 15 minutes

@Dmitriy
I have 5 relays that have worked for months without issue and lately (not sure last week maybe 2?) the digitalwrite fails after 15 minutes or so. Point is, it took me an age to figure out what was going on, but now I’ve finally figured it out…

If I reset the unit and run digitalwrite low/high it switches off/on correctly but after ~15 minutes it just stops working. Has anything recently changed that can have an influence on this and do you know either a fix or a workaround?

I can give you the relevant code but its not really exciting:

      if( heatOn[0] && !maintenanceOn_V[0] || 
          heatOn[1] && !maintenanceOn_V[1] ||
          heatOn[2] && !maintenanceOn_V[2] ||
          heatOn[3] && !maintenanceOn_V[3] ||
          heatOn[4] && !maintenanceOn_V[4] ||
          heatOn[5] && !maintenanceOn_V[5] ||
          heatOn[6] && !maintenanceOn_V[6] )      // run pump IF a valve is open aka heatOn = true
      {                 
        Serial.println("START PUMP");
        digitalWrite(HEATER_RLY_PIN_PUMP, LOW);   // activate = LOW, deactivate = HIGH
        resetMaintenanceTimerPump();              // reset maint. timer for pump as its now running.
      }
      else
      {
        Serial.println("STOP PUMP");
        digitalWrite(HEATER_RLY_PIN_PUMP, HIGH); 
      }

the serial log show me START PUMP and STOP PUMP but nothing happens.
I do have

pinMode(HEATER_RLY_PIN_PUMP, OUTPUT);   

in the setup().

Give that this has worked for months and recently it stopped working and I didn’t change any (relevant) code, just added extra checks and vpin writes to figure out what the hell was going on I get the feeling that this might have to do with a Blynk update.

any clues?

Its a really serious issue as this concerns the central heating system for my house!!

Android Galaxy S8
app: 2.26.2
Server 0.38
library: 0.5.3
units: wemos d1 mini

edit: this also explains another enigma I’ve encountered. I have this routine on a 4 seconds timer:

void blinkLED(){
  digitalWrite(2, LOW);                           // turn the LED on/off (HIGH is the voltage level)
  delay(100);
  digitalWrite(2, HIGH);                          // turn the LED on/off (HIGH is the voltage level)
}

for some units this keeps working for days, for other units the blinking led stop after a couple of hours. Apparently if you keep constantly setting that digital port it keeps working for a longer time but also fails after a while. This too has worked for months and recently started to show issues (led stopped blinking but unit was still connected)

Not easy to be sure with just your code snippets…

But there was that change to BlynkTimer that I haven’t quite understood the mechanics of.

Could it be that your code had some overrunning timers that are are being discarded?

its all possible but the simple fact remains that IF my log shows this:

START PUMP

then the second line:

digitalWrite(HEATER_RLY_PIN_PUMP, LOW);

MUST be executed. Moreover the ‘resetmantenancetimer’ function also pushes stuff to the log that also shows up. So the bottomline is: the line BEFORE and AFTER are BOTH executed, hence the line in the middle must have been executed as well. And it does work the first 15 minutes, after that…the relay does no longer respond. So it has to be an issue with digitalwrite there is no other logical explanation (yes, yes, failing hardware…all 15 !! units all of a sudden have the same failure… occam’s razor!).

Well, aside form the App & library having option to directly control/read analog and digital pins, I don’t see how Blynk could be interfering?

If you use ONLY virtual pins then you can add in this command and see if that makes a difference…

#define BLYNK_NO_BUILTIN  // Disable built-in analog & digital pin operations

I’m not sure what you mean with that, what else is there beyond virtual pins?

Interesting point though, I haven’t tried setting a relay with a blynk app button. Ill check if that does work.

image

Without that command I showed above… the internal library function that controls this is still active

ok interesting. I’ve added a button to directly set the relay. The following happens:

  1. If I raise the target temp above the current temp the relays SHOULD go on: it does NOT
  2. If I use the button on the same port it DOES turn on!!
  3. If I THEN raise the target temp again above the current temp: it works again!!!

so…clueless actually, except that it strongly points in a blyn related issue with digitalwrite (over time).

edit:
I will try your suggestion with turning the blynk hardware off and see how that works but if that were the case that only strenghtens the point that there is a blynk related issue…and given that a lot of iot works with relays, its a really serious issue!

edit2:
ok I have two experiments running:
One relay with: #define BLYNK_NO_BUILTIN
and
One relay with: pinMode(HEATER_RLY_PIN[n], OUTPUT);
in front of EACH digitalwrite.

I’ll report in the morning what the results are (its close to midnight here now)

ok checked this morning and both methods work, which clearly indicates a Blynk bug @Dmitriy

I can keep my system working, so thats good, but for blynk this is pretty serious as apparently ANY hardware input port can start to fail over time using ‘default’ blynk.

I think we will still need more details, and full code… before any true clear declaration can be made.

I use lots of Digital & Analog reads, writes and so on in my projects, all running up to date libraries and local Server, and they keep running 24/7 without any glitches.

Perhaps you are running into other timing issues, library issues, power, etc. If there is a bug, can you provide a simple sketch that shows repeatable issue?

Also, what if any error messages or other indicators (DEBUG or serial.print() ) tracking have you got that you can show to indicate a Blynk bug?

Here are direct links to my code:
This is the central unit that operates the central heating (and failed after 15m):
https://www.dropbox.com/s/x5mv1yanbdxxxh7/HEATING_Central.ino?dl=1
(note that it now works due to placing pinMode(HEATER_RELAY_PIN_CV, OUTPUT); in front of every pin operation)

This is the code for the 4 units that operate the valve and pump (and also fails after 15m):
https://www.dropbox.com/s/d72c6nck4nxeqn4/Heating_Relay.ino?dl=1
(note that this one now works due to this line:
#define BLYNK_NO_BUILTIN // Disable built-in analog & digital pin operations)

And in case its required, here the code of the thermostats that tell the relays units to either turn on or off:
https://www.dropbox.com/s/mxikne06y38qyoq/Heating_Thermostat.ino?dl=1

and finally my header file, which I’m not gonna share as that contains all the sensitive stuff, but here’s a copy:

#include <BlynkSimpleEsp8266.h>     // Blynk
#include <ArduinoOTA.h>             // OTA
#include <U8g2lib.h>                // OLED
#include <BME280I2C.h>              // Sensor
#include <ESP8266httpUpdate.h>		// Http OTA update
#include <Wire.h>                   // I2C communication (Sensor)
#include <WidgetRTC.h>              // RTC Widget

//#include <ESP8266WiFi.h>            // 
//#include <ESP8266mDNS.h>            // 
//#include <ESP8266HTTPClient.h>      // 
//#include <WiFiUdp.h>                // 
//#include <TimeLib.h>			      // 

#define BLYNK_PRINT Serial			// redirect blynk messages like "Connecting to..." to Serial.print()
//#define BLYNK_HEARTBEAT 20			// defaults to 10s. 
//#define BLYNK_TIMEOUT_MS 5000UL		// defaults to 2000UL

//when going to production comment out blynk_print and uncomment the following lines:
//#define BLYNK_NO_BUILTIN   			// Disable built-in analog & digital pin operations
//#define BLYNK_NO_FLOAT     			// Disable float operations

// #define BLYNK_USE_128_VPINS		// no longer required

#define BLYNK_GREEN     "#23C48E"	// RR GG BB
#define BLYNK_BLUE      "#04C0F8"
#define BLYNK_YELLOW    "#ED9D00"
#define BLYNK_RED       "#D3435C"
#define BLYNK_DARK_BLUE "#5F7CD8"
#define BLYNK_ORANGE	"#FFA500"

#define RED 			"#FF0000"
#define WHITE 			"#FFFFFF"  
#define BLACK 			"#000000"  
#define YELLOW 			"#FFFF00" 
#define PURPLE 			"#9400D3" 
#define INDIGO 			"#4B0082" 
#define BLUE 			"#0000FF"  
#define GREEN 			"#00FF00"  
#define GRAY 			"#919191"  
#define GREY 			"#C0C0C0"  
#define ORANGE	 		"#FFA500"

#define CCENTRAL 		"#000000" // BLACK
#define CBCKCENTRAL 	"#919191" // GRAY
#define CLIVING 		"#23C48E" // BLYNK_GREEN
#define CSTUDY 			"#9400D3" // PURPLE
#define CBATH 			"#d68a00" // DARK_ORANGE
#define CBED 			"#d68a00" // DARK_ORANGE
#define CLAUNDRY 		"#d68a00" // DARK_ORANGE
#define CARTHUR 		"#d68a00" // DARK_ORANGE
#define CCASPER 		"#04C0F8" // BLYNK_BLUE

const char* ssid		= "";
const char* password	= "";
const char* fwUrlBase	= "";   // used for http OTAbool HTTP_OTA = false;
IPAddress server(192,168,1,90); 
const int port			= 8080;

#define auth_CENTRAL   ""

#define auth_T_LIVING  ""
#define auth_T_STUDY   ""
#define auth_T_BATH    ""
#define auth_T_BED     ""
#define auth_T_LAUNDRY ""
#define auth_T_ARTHUR  ""
#define auth_T_CASPER  ""

#define auth_R_FLOOR_1 ""
#define auth_R_FLOOR_2 ""
#define auth_R_FLOOR_3 ""
#define auth_R_FLOOR_4 ""

// pins used by therm and relay
#define channel0_PIN		V0
#define channel1_PIN		V1
#define channel2_PIN		V2
#define channel3_PIN		V3
#define channel4_PIN		V4

// pins used by thermometers
#define currentT_PIN		V10
#define currentH_PIN		V11
#define currentP_PIN		V12

#define heatOn_PIN			V13
#define maintOn_PIN			V14

#define error_PIN			V19 // sensor error
#define OLED_PIN			V20 // slider for oled contrast
#define timer1_PIN			V21	// timer input widget
#define timer2_PIN			V22	// timer input widget
#define timer3_PIN			V23	// timer input widget
#define timer4_PIN			V24	// timer input widget

// pins used by CENTRAL and THM
#define targetTType_PIN   	V31 // pin where the current targetTtype is stored (home, away, vacation)

const int setTargetT_PIN[3]		= {V32, V33, V34}; // 0=@home; 1=@night; 2=@vacation
const String setTargetTCOLOR[3]	= {BLYNK_GREEN, BLYNK_DARK_BLUE, BLYNK_ORANGE}; // 0=@home; 1=@night; 2=@vacation
#define homeTargetT_PIN		V32 // only available at THM can't use the array in a case statement, hence the seperate definition as well. 
#define nightTargetT_PIN	V33 // only available at THM
#define vacationTargetT_PIN	V34 // THM and CENRTAL

#define showTargetT_PIN		V35 // only available at THM //value varies depending on targetTType (so either home/night/vacation
#define targetTMIN_PIN		V36 // only available at THM
#define targetTPLUS_PIN		V37 // only available at THM

// pins used by CENTRAL
#define CV_PIN				V10 // turn CV on/off
#define SHOW_TIME_PIN		V11 // show a clock

// pins used by ALL
#define TERMINAL_PIN		V30 // used to show the terminal outputs
#define version_BCK_PIN		V38 // shows current version of firmware. this pin is exclusively used by ESP_BCK_CENTRAL
#define OTA_BCK_update_PIN	V39 // on=check for update on server this pin is exclusively used by ESP_BCK_CENTRAL
#define OTA_update_PIN		V40 // on=check for update on server
#define offline_PIN			V41 // Every unit uses the pin to check offline status of that unit
#define version_PIN			V42 // shows current version of firmware
#define wifiSignal_PIN		V43 // button to turn on wifi signal checker (in terminal)
#define TERMINALBCK_PIN		V44 // used to show the BCK terminal outputs

int relayOn_PIN[7] = {V51,V52,V53,V54,V55,V56,V57}; // additional check whether the pin is actually set

// Note that there is no mem difference between const and define, however define is more bug prone!
const int SCL_PIN = D5;
const int SDA_PIN = D6;
const int RES_PIN = D1;
const int DC_PIN = D2; 
const int BUTTON_UP_PIN = D7;   // the pin that the pushbutton is attached to
const int BUTTON_DO_PIN = D8;   // the pin that the pushbutton is attached to
// const int T_SENSOR_PIN = A0; // ADC !! (only this pin = (A0)
const int TIME_RUN_FUNCTIONS			= 1000 * 60; // Run all 1 minute functions
String displaycurrenttimepluswifi;

///////////////////////////////////VARIABLES//////////////////////////////////////////////// 

///FUNCTIONS // Usually I would move this to a cpp file, but I get numerous errors so I gave up and placed them here. 
/////////////////STRING///////////////////////////////////////////////
String floatToString(float val, int decimalplaces){
  String value(val, decimalplaces);
  return value;
}

/*String getMAC(){//return mac address of unit
  uint8_t mac[6];
  char result[14];
  snprintf( result, sizeof( result ), "%02x%02x%02x%02x%02x%02x", mac[ 0 ], mac[ 1 ], mac[ 2 ], mac[ 3 ], mac[ 4 ], mac[ 5 ] );
  return String( result );
}*/
/////////////////STRING///////////////////////////////////////////////
/////////////////OTA////////////////////////////////////////////////// 
void initOTA(char ESP_NAME[]){
  Serial.print("EPS Name: ");
  Serial.println(ESP_NAME);
  ArduinoOTA.setHostname(ESP_NAME);
  // ArduinoOTA.setPassword("admin");
  ArduinoOTA.onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH)
      type = "sketch";
    else // U_SPIFFS
      type = "filesystem";
    // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
    Serial.println("Start updating " + type);
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });
  ArduinoOTA.begin();
  Serial.println("OTA Ready");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}
/////////////////OTA////////////////////////////////////////////////// 
//////////////////////// FOTA//////////////////////////////////////// 
String checkForUpdates(char ESP_NAME[]) {
  String ESP = String(ESP_NAME);
  String fwURL = String( fwUrlBase ) + ESP + ".bin";          // e.g. ESP_CENTRAL.bin
  String oneLine = "";                                         // used for returntxt and serial line
  String returnTxt = "HTTP FIRMWARE UPDATE FOR: " + ESP + "\n"; // in case of error return string which then can be ported to terminal
  
  Serial.println( returnTxt );
  t_httpUpdate_return ret = ESPhttpUpdate.update( fwURL );    // IF successful, ESP resets after this line
  //t_httpUpdate_return ret = ESPhttpUpdate.update("https://username.github.io/firmware/blink.bin", "", "fingerprint_goes_here");
  switch(ret) {                                               // In case of failure....
	case HTTP_UPDATE_FAILED:
	  oneLine = "HTTP_UPDATE_FAILED Error "+ String(ESPhttpUpdate.getLastError()) + ": " + ESPhttpUpdate.getLastErrorString().c_str() + "\n";
	  Serial.print(oneLine);
	  returnTxt += oneLine;
	  break;
	case HTTP_UPDATE_NO_UPDATES:
	  oneLine = "HTTP_UPDATE_NO_UPDATES\n";
	  Serial.print(oneLine);
	  returnTxt += oneLine;
	  break;
  }
  return returnTxt;
}
//////////////////////// FOTA//////////////////////////////////////// 

other timing issues

No timing isues, although Blynk does start to fail in updating digital reads also ‘after a while’, but I haven’t fully investigated this issue so haven’t reported anything about it yet. In short: use ‘value display’ widget; monitor GP12 which after ‘a while’ remains stuck on either HIGH or LOW.

library issues

well given that my units work if I use this line: #define BLYNK_NO_BUILTIN it indeed appears to be a Blynk library issue

power

given that all 12 units have have been working flawlessly the passed 3-4 months and only the relays units started to fail recently AND given my other posts I really doubt that. That and I’ve tested them all the last week: 5v each.

I’ve given it all in the earlier posts, but the short of it:

if it works WITH this line:
#define BLYNK_NO_BUILTIN // Disable built-in analog & digital pin operations)
and it does NOT work without that line, what other conclusion then that its a blynk related issue can you think of?

Well, you DO require pinMode() in setup for each and every digital pin you use… that is normal Arduino coding practice… and while the Blynk library does sorta take care of that for “code free” use from the App, it is recommended to have them manually entered in your void setup() anyhow when using code routines.

@wolph42 how many digital/analog pins do you use within the app?

@Dmitriy
the short version: 2.

The long version:
The relays-wemos units are connected to 2 relays on pins D1 and D2 (one for valve and one for pump). Next to 5V and Ground those are the only connections to the units.

The Central unit is a bit more complex as I use a redundancy scheme with two wemos units where the main unit is connected to 1 relay (the central heating) and a backup unit is connected to 2 relays (one is the central heating and the other is connected to the main unit, allowing the backup to hard-reset the main unit).

edit: misintepreted your question. from within the app…ALL of them? Guestimating here:
7 thermostats:
-5 pins for channels
-3 for heat/humid/pressure
-2 for heat/maintenance
-3 error/oled/temptype
-3 temptypes
-2 to operate temp setting +/-
-4 timers
-8 other stuff
-2 button up/down
----+
32 pins per thermostat

4 floor relays:

  • 5 channels
  • 8 other stuff
  • 1 or 4 relayOn but in an array (see header file)
    —+
    14 or 17 pins per relay

1 Central unit:

  • 4 for target temp
  • 8 for other stuff
    —+
    12

then additionally (for debug purposes) in the app I’ve added extra monitors for the phsysical state (HIGH/LOW) of ALL the relay pins; so for the relays 4(pumps)+7(valves)=11 and central I’ve added a digital read for all available pins which is 8(?). These read-outs also start to fail ‘after a while’ but i haven’t really looked into that yet.
edit2: Here the project:

@Gunner

from my first post:

you don’t however need it in front of every.single.line.you.update.a.pin!

Yes, I was just agreeing with you… but I did miss the initial reference… just a case of TLDR :blush:

I meant every discreet pin, not every instance you process a pin command :stuck_out_tongue_winking_eye:

Since you do not appear to use actual pin references in your App, thus the addition of the #define BLYNK_NO_BUILTIN seems to solve the issue, then is the case closed, or is there still issues? Sorry if I am confused and missing something.

actually I am using pin references in my app (mainly to check if everythings is working), but to test this I added the line.
And I don’t agree with: ‘if you turn off the buggy part of Blynk and then it works again: then its case closed’. I think this community also exists to help the devs debugging there software. Actually a ‘workaround’ is not a proper solution unless its for a really obscure unit (I’m using mainstream wemos d1 units) or for a really obscure purpose (I’m setting pins hi/lo, which is basically core iot), which clearly is NOT the case in my case.

And there are more issues (which look related) but those I’ll need to investigate further and I’ll post those in a new topic in due time.

But again… the “built in” pin control is meant for the basic “code free” applications using just the App and direct pin manipulation in the widgets.

So disabling that when not needed (AKA writing you own coded functions, etc) s not a case of skipping a bug so much as proper coding practice and eliminating possible pinmode() redundancies that are no longer necessary.

I have been doing that since day one (OK, day two once I found out about the command) and as stated, no problems… thus no “bug” that I can see that needs developer distraction.

But ultimately, if the developers read all this and determine there is a bug, then it is a bonus :smiley:

hmm, is it ONLY required for pin manipulation or also for reading pins? I do like to keep tracking them in the app. Funny thing though: I wan’t aware that you could direcly operate pins from the app. This is actually really useful when stuff goes wrong and I’m not (physically) around to fix stuff. So I’d like to have the option to do this (now that I know of this).

Manipulation in this sense means reading/writing… but I have never tried simply reading a pin state with Built-In disabled still works. EDIT - Nope :smiley:

I do use both methods in my test bench, but I still recommend disabling it for long term projects and sticking with vPins.

yeah, you’re probably right.

I’m wondering IF you are not breaking the things this way

Question: Are you reading pin state directly in APP, with setting the read time interval and requesting pin state DIRECTLY by referencing to (for example) D4?? Because If you do so, then my thinking is, that in this “mode” you are “forcing” Blynk to READ pin state by direct pin manipulation, which PROBABLY sets this pin to INPUT MODE. I wouldn’t count it then as a BLYNK ISSUE, rather as an improper usage scenario. That would also explain, WHY placing pinMode before setting the pin output WORKS…

EDIT: Check the BlynkApiArduino.h file. You will find an answer to your issue. But in short: You need to set pin direction every time you need to write to this pin. If you put it only in setup() it will be overwritten by direct pin manipulations (READING sets it as INPUT, WRITING as OUTPUT)

1 Like