BlynkEdgent OTA & hardcoded WiFi SSID and pwd for esp8266-01S

My project involves an ESP8266-01S with three sensors: GPIO0 controls a 5V relay, GPIO2 is set as an input for a PIR sensor, and GPIO3 (the RX signal) is configured as an input for a proximity sensor. After consulting notes from this and other forums and overcoming the usual developer challenges, the project is functioning well (tested for ~48 hrs of active use)

I then integrated Blynk, which was initially unfamiliar to me. However, I managed to grasp basics, got the Blynk app working on my mobile device, and configured it to receive notifications from the sensors. Subsequently I implemented dynamic WiFi credentials, thanks to assistance from this forum and Pete’s responses. Following that, I enabled OTA, which also functioned after resolving a few teething issues.

During testing, the project crashed after 3-4 hours and lost the WiFi credentials. Re-entering the credentials and performing a power cycle restored functionality, but this behavior is obviously undesirable.

The issues that plague me are as follows:

[1] Debugging is challenging because GPIO3 is in use, ruling out its RX function. I am using relay on/off morse code for debugging, which is cumbersome.

[2] To ensure that GPIO3 is configured as an input, I included the following code:

Serial.begin(115200, SERIAL_8N1, SERIAL_TX_ONLY);

My question is whether my use of GPIO0, GPIO2, GPIO3, and/or the above Serial setup could interfere with the BlynkEdgent code and cause a crash.

[3] For this project, I want to hardcode the WiFi SSID and password while retaining the OTA feature. Is there a way to use only the OTA feature of BlynkEdgent without employing dynamic WiFi setup? I foresee a conflict because we cannot use both Blynk.begin(auth, ssid, pass); and BlynkEdgent.begin();

I have not included my code here, as it appears to function correctly. If necessary, I can provide it.

Pete.

Thanks for the quick reply, Pete!

Before trying this out, want to know whether any changes are to be made in BlynkEdgent #includes in my code below:

// Blynk Credentials
#define BLYNK_TEMPLATE_ID "...."
#define BLYNK_TEMPLATE_NAME "...."

// token is not needed for updates via OTA
// #define BLYNK_AUTH_TOKEN "..."
// char auth[ ] = BLYNK_AUTH_TOKEN;

//  WiFi credentials.
char ssid[ ] = "...";
char pass[ ] = "...";

//#define BLYNK_PRINT Serial // disabling as it seems to interfere with GPIO3 as input
//#define BLYNK_DEBUG
//#define APP_DEBUG

#define USE_NODE_MCU_BOARD
#define BLYNK_FIRMWARE_VERSION "0.1.3"

#include <BlynkEdgent.h>
#include <ESP8266WiFi.h>
#include <TimeLib.h>
#include <WidgetRTC.h>

// Define GPIOs
const int GPIO0 = 0; // Relay
const int GPIO3 = 3; // PIR Motion Sensor (uses RX pin)
const int GPIO2 = 2; // Proximity Sensor

void setup() {
    Serial.begin(115200,SERIAL_8N1,SERIAL_TX_ONLY);   // This allows you to use RX as normal I/O, while still writing debug messages to Serial
   // Serial.begin(115200);   // blynk.edgent
    delay(100);
    Blynk.begin(auth, ssid, pass);
//    BlynkEdgent.begin();
 
    rtc.begin();  // Synchronize RTC
    timer.setInterval(5000L, clockDisplay);  // Update clock display every 5 seconds

    // Explicitly initialize sensor readings to avoid floating input issues
    activityDetected = 0;
    readMotion = LOW;
    readProxi = HIGH;   // HIGH = sensor LED off. LOW = sensor LED on

    Blynk.syncVirtual(V2);
    Blynk.syncVirtual(V3);
}

void loop() {
// changes for blynk.Edgent
//    Blynk.run();
    BlynkEdgent.run();
    timer.run();
    detectActivity();
}

In your code snippet, I see method BLYNK_WRITE(InternalPinOTA). Does this override method in BlynkEdgent lib? I just have to insert this in my code and Blynk.Air will take care of the rest?

@AVora Please edit your post, using the pencil icon at the bottom, and add triple backticks at the beginning and end of your code so that it displays correctly.
Triple backticks look like this:
```

Copy and paste these if you can’t find the correct symbol on your keyboard.

Pete.

done! my bad. apologies

Also, is Update.h part of blynk lib?

You shouldn’t be starting with the Blynk Edgent example.
You need to start with a regular non-Edgent sketch with hard-coded WiFi credentials and auth token, and add the OTA code shown in the link to that sketch.

Pete.

Hi Pete, added the code snippet and restored to code to be without Blynk Edgent. However, the compiler throws up error that it is unable to find Update.h. Am I missing some PATH or libraries?

Impossible to tell without seeing your code.

Pete.

here it is:

//
// ver 1.4  filename: Blynk_HSS_WiFi_hardcoded_OTA_v1_3
//          under dev.
//          this ver (will) supports:
//          [1] 1 motion sensor (gpio3), 1 proximity sensor (gpio2), 1 relay (gpio0)
//          [2] watchdog for soft restart in case of code crash
//          [3] support mobile blynk app to display ver number defined by BLYNK_FIRMWARE_VERSION
//	    [4] hardcoded ssid and pwd, but still uses Blynk.Air OTA


// Blynk Credentials
#define BLYNK_TEMPLATE_ID "xxx"
#define BLYNK_TEMPLATE_NAME "xxx"
#define BLYNK_AUTH_TOKEN "xxx"
char auth[] = BLYNK_AUTH_TOKEN;

// WiFi credentials.
char ssid[] = "xxx";
char pass[] = "xxx";

#define BLYNK_PRINT Serial
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <TimeLib.h>
#include <WidgetRTC.h>

//--- sourced by Pete Knight, moderator @ Blynk ------
//#define USE_NODE_MCU_BOARD
#define BLYNK_FIRMWARE_VERSION "0.1.4"
//#include <BlynkEdgent.h>
#include <Update.h>
#include <HTTPClient.h>

// Define GPIOs
const int GPIO0 = 0;  // Relay or LED
const int GPIO3 = 3;  // PIR Motion Sensor (uses RX pin)
const int GPIO2 = 2;  // Proximity Sensor

int activityDetected;
bool activateMotionSensor, activateProxiSensor;
int readMotion, readProxi;

// Timer for periodic checks
BlynkTimer timer;
WidgetRTC rtc;

unsigned long previousMillis = 0;
const long checkInterval = 50;  // Check every 50ms

void setup() {
    Serial.begin(115200,SERIAL_8N1,SERIAL_TX_ONLY);   // This allows you to use RX as normal I/O, while still writing debug messages to Serial
    delay(100);
    Blynk.begin(auth, ssid, pass);
//    BlynkEdgent.begin();
 
    rtc.begin();  // Synchronize RTC
    timer.setInterval(5000L, clockDisplay);  // Update clock display every 5 seconds

    pinMode(GPIO0, OUTPUT);
    pinMode(GPIO3, INPUT);
    pinMode(GPIO2, INPUT);
    
    // Explicitly initialize sensor readings to avoid floating input issues
    activityDetected = 0;
    readMotion = LOW;
    readProxi = HIGH;   // HIGH = door closed (sensor LED off). LOW = door shut (sensor LED on)

    Blynk.syncVirtual(V2);
    Blynk.syncVirtual(V3);
}

void loop() {
    Blynk.run();
// changes for blynk.Edgent
//  BlynkEdgent.run();
    timer.run();
    detectActivity();
}

//--- sourced by Pete Knight, moderator @ Blynk ------
//
String overTheAirURL = "";
BLYNK_WRITE(InternalPinOTA) {
  overTheAirURL = param.asString();
  HTTPClient http;

  http.begin(overTheAirURL);
  int httpCode = http.GET();
  if (httpCode != HTTP_CODE_OK) {return;}
  int contentLength = http.getSize();
  if (contentLength <= 0) {return; }
  bool canBegin = Update.begin(contentLength);
  if (!canBegin) { return;}
  Client& client = http.getStream();
  int written = Update.writeStream(client);
  if (written != contentLength) {return;}
  if (!Update.end()) {return;}
  if (!Update.isFinished()) {return;}
reboot();
}

//--- sourced by Pete Knight, moderator @ Blynk ------
//
void reboot()
{
#if defined(ARDUINO_ARCH_MEGAAVR)
  wdt_enable(WDT_PERIOD_8CLK_gc);
#elif defined(__AVR__)
  wdt_enable(WDTO_15MS);
#elif defined(__arm__)
  NVIC_SystemReset();
#elif defined(ESP8266) || defined(ESP32)
  ESP.restart();
#else
  #error "MCU reset procedure not implemented"
#endif
  for (;;) {}
}
//--------------------------------------

If you read through the rest of the posts in the minimal OTA link I provided, you’ll see that if you’re using an ESP8266 you need to change this…

to this…

#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>

you also need to change your BLYNK_WRITE(InternalPinOTA) section of code to be like this…

//--- sourced by Pete Knight, moderator @ Blynk ------
//
String overTheAirURL = "";
BLYNK_WRITE(InternalPinOTA) {
  overTheAirURL = param.asString();
  WiFiClient my_wifi_client;
  HTTPClient http;
  http.begin(my_wifi_client, overTheAirURL);

  int httpCode = http.GET();
  if (httpCode != HTTP_CODE_OK) {return;}
  int contentLength = http.getSize();
  if (contentLength <= 0) {return; }
  bool canBegin = Update.begin(contentLength);
  if (!canBegin) { return;}
  Client& client = http.getStream();
  int written = Update.writeStream(client);
  if (written != contentLength) {return;}
  if (!Update.end()) {return;}
  if (!Update.isFinished()) {return;}
reboot();
}

Your code still won’t compile though, because you have calls in void setup and void run to non-existent functions which need to be commented-out…

//timer.setInterval(5000L, clockDisplay); // Update clock display every 5 seconds

//detectActivity();

Other observations…

Don’t do this…

Do this instead…

Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass);

Don’t do this in void setup…

Add this function instead…;

BLYNK_CONNECTED()
{
  Blynk.syncVirtual(V2);
  Blynk.syncVirtual(V3);
}

and don’t do this…

because the RTC widget was a Blynk Legacy thing. Read this instead…

Pete.

Pete, I believe I’ve finally figured it out! First off, I really appreciate your sharp insights and invaluable guidance.

I went through the “minimal OTA” post multiple times. While I couldn’t use it exactly as-is since it’s designed for ESP32 and I’m working with an ESP8266-01, it gave me an understanding of the key steps needed.

So, I dug into the critical files in the Edgent_ESP8266 folder and incorporated the necessary functions into my own code.

I’m sharing a clean version below—hopefully, it’ll be useful for others with similar requirements.

//
// ver 1.0  filename: Blynk_esp8266_01_OTA_without_BlynkEdgent_v1
//          this code doesn't use Blynk.Edgent, but still supports Blynk.Air OTA feature. Below code mimics key methods of OTA.h to suport OTA without using Edgent.
//          Use of Blynk.Edgent neccessiates to remove hardcode WiFi SSID and password from your code; rightly so as per its design and feature.
//          However the below code can be used by anyone; for reasons best known to them, who wants to use code with hardcode network SSID and 
//          password but still be able to have OTA support. 
//          caution: this code comes with taillight guarantee!

// Blynk Credentials
#define BLYNK_TEMPLATE_ID "xxxx"
#define BLYNK_TEMPLATE_NAME "xxxx"
#define BLYNK_AUTH_TOKEN "xxxx"

// WiFi credentials
char auth[] = BLYNK_AUTH_TOKEN;
char ssid[] = "xxxx";
char pass[] = "xxxx";

#define BLYNK_PRINT Serial
#define BLYNK_FIRMWARE_VERSION "0.1.0"

#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>
#include <WiFiClientSecure.h>

//--------------------------------------------------------------------------------
void setup() {
  Serial.begin(115200);
  delay(100);
  Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass);
}

//--------------------------------------------------------------------------------
void loop() {
  Blynk.run();
}


//--------------------------------------------------------------------------------
// the below snippets are for esp8266-01 OTA support
// all below functions mimics functions in Edgent related files for 8266
//
String overTheAirURL = "";
String protocol, host, url;
int port;

BLYNK_WRITE(InternalPinOTA) {		// mimicking enterOTA() method in Edgent_ESP8266.h
  //Blynk.disconnect();         // Disconnect, not to interfere with OTA process. 
                                // Have commented this out, as using it causes some failure which yet to be investigated 
  
  overTheAirURL = param.asString();       // get the URL string
  if (!parseURL(overTheAirURL, protocol, host, port, url)) {
    return;   // error: Cannot parse URL
  }

  Client* client = NULL;
  client = connectTCP(host, port);
  client->print(String("GET ") + url + " HTTP/1.0\r\n" + "Host: " + host + "\r\n" + "Connection: keep-alive\r\n" + "\r\n");

  uint32_t timeout = millis();
  while (client->connected() && !client->available()) {
    if (millis() - timeout > 10000L) {
      return;   // error: Response timeout
    }
    delay(10);
  }

  String md5;
  int contentLength = 0;
  while (client->available()) {
    String line = client->readStringUntil('\n');
    line.trim();
    line.toLowerCase();

    if (line.startsWith("content-length:")) {
      contentLength = line.substring(line.lastIndexOf(':') + 1).toInt();
    } else if (line.startsWith("x-md5:")) {
        md5 = line.substring(line.lastIndexOf(':') + 1);
      } else if (line.length() == 0) {
          break;  // md5 length = 0
        }
    delay(10);
  }

  if (contentLength <= 0) {
    return;     // error: Content-Length not defined
  }

  bool canBegin = Update.begin(contentLength);
  if (!canBegin) {
    return;     // error: OTA begin failed
  }

  if (md5.length()) {
    md5.trim();
    md5.toLowerCase();
    if(!Update.setMD5(md5.c_str())) { 
      return;     // error: Cannot set MD5
    }
  }

  // flashing starts here...

  int written = 0;
  int prevProgress = 0;
  uint8_t buff[256];
  while (client->connected() && written < contentLength) {
    delay(10);
    timeout = millis();
    while (client->connected() && !client->available()) {
      delay(1);
      if (millis() - timeout > 10000L) {
        return;     // error: timeout
      }
    }

    int len = client->read(buff, sizeof(buff));
    if (len <= 0) continue;

    Update.write(buff, len);
    written += len;
    const int progress = (written*100)/contentLength;

    if (progress - prevProgress >= 10 || progress == 100) {
      prevProgress = progress;
    }
  }

  client->stop();

  if (written != contentLength) {
    return;       // error: Write failed
  }
  
  if (!Update.end()) {
    return;     // error: Update not ended
  }      

  if (!Update.isFinished()) {
    return;     // error: Update not finished
  }
 
  // OTA update successfully completed! Rebooting
  reboot();
}


//--------------------------------------------------------------------------------
// mimicked from restart() in ConfigMode.h
void reboot() {
  ESP.restart();
  ESP.reset();
  while(1) {};
}

//--------------------------------------------------------------------------------
// mimicked from connectTCP() in OTA.h
WiFiClient* connectTCP(const String& host, const int port)
{
  WiFiUDP::stopAll();
  WiFiClient::stopAll();

  WiFiClient* clientTCP = new WiFiClient();
  if (!clientTCP->connect(host.c_str(), port)) {
    return NULL;      // error: Client not connected
  }
  return clientTCP;
}

//--------------------------------------------------------------------------------
// mimicked from parseURL() in OTA.h
bool parseURL(String url, String& protocol, String& host, int& port, String& uri)
{
  int index = url.indexOf(':');
  if(index < 0) {
    return false;
  }

  protocol = url.substring(0, index);
  url.remove(0, (index + 3)); // remove protocol part

  index = url.indexOf('/');
  String server = url.substring(0, index);
  url.remove(0, index);       // remove server part

  index = server.indexOf(':');
  if(index >= 0) {
    host = server.substring(0, index);          // hostname
    port = server.substring(index + 1).toInt(); // port
  } else {
    host = server;
    if (protocol == "http") {
      port = 80;
    } else if (protocol == "https") {
      port = 443;
    }
  }

  if (url.length()) {
    uri = url;
  } else {
    uri = "/";
  }
  return true;
}

Again, many thanks! your knowledge on these topics and your ability to recall past issues and solutions is commendable!

postscript: in my earlier code snippet, some of the functions weren’t void or some loose ends. hey were originally part of a larger program, but I removed them before sharing since I was confident the core functionality was working. I wanted to keep the focus on the main issue rather than adding unnecessary code clutter.

That seems like a rather weird and massively over-complicated solution to a fairly simple solution.

There were several ESP8266 examples in the topic I linked to, which work perfectly well, and I pointed-out the ESP8266 changes that were required to get your code working.

Pete.