ESP8266 using HTTP Mode, successfully updates data to Blynk. But shows offline [SOLVED]

Hi all,
Very new to Blynk but so far getting great results. I have an existing Arduino project on ESP8266 that is already running a webserver with websocket interface for remote control. It has a good captive portal and has been very reliable to date.

Taking code from the Arduino Example:
Blynk → Boards_With_HTTP_Api → ESP8266_ESP32

I was able to merge the httpRequest function into my code and it runs successfully. Not much to note in the Serial output other than the connection to the Blynk Server was successful.

The value I am sending “Hello” updates to the Blynk. I can also replace my “Hello” string with a int counter which updates every second to blynk. The one annoyance is that even during successful communication Blynk shows my device is “Offline”.

Is there a way to alert the Blynk Server that I am actually online? For instance a seperate sync command, or sending data faster?

My simplified code is below:

#include <ESP8266WiFi.h>                
#include <ESPAsyncWebServer.h>
#include <ESPAsyncWiFiManager.h>         
#include <ESPAsyncTCP.h>
#include "index.h"

/* Fill in information from Blynk Device Info here */
#define BLYNK_TEMPLATE_ID           "*********"
#define BLYNK_TEMPLATE_NAME         "*********"
#define BLYNK_AUTH_TOKEN            "*********"

// Blynk cloud server
const char* host = "blynk.cloud";
unsigned int port = 80;


WiFiClient client;


AsyncWebServer server(80);
DNSServer dns;
AsyncWebSocket ws("/ws");   



void notifyClients(String textMsg) {
  ws.textAll(textMsg);
}

void handleWebSocketMessage(void *arg, uint8_t *data, size_t len) {
  AwsFrameInfo *info = (AwsFrameInfo*)arg;
  if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
    data[len] = 0;
    if (strcmp((char*)data, "toggle") == 0) {
      ledState = !ledState;
    }
  }
}

void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
    switch (type) {
      case WS_EVT_CONNECT:
        Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
        break;
      case WS_EVT_DISCONNECT:
        Serial.printf("WebSocket client #%u disconnected\n", client->id());
        break;
      case WS_EVT_DATA:
        handleWebSocketMessage(arg, data, len);
        break;
      case WS_EVT_PONG:
      case WS_EVT_ERROR:
        break;
  }
}

void initWebSocket() {
  ws.onEvent(onEvent);
  server.addHandler(&ws);
}


void setup(){
  Serial.begin(9600);
  AsyncWiFiManager wifiManager(&server,&dns);
  wifiManager.autoConnect("********");
  Serial.println("Connected!");
  Serial.println(WiFi.localIP());
  initWebSocket();

  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html);
  });
 
  // Start server
  server.begin();
}


void loop() {
  String message = "Hello";
  ws.cleanupClients();
  delay(1000);
  httpRequest("GET", String("/external/api/update?token=") + BLYNK_AUTH_TOKEN + "&pin=V0&value=" + message, "", response);
}






//////////////////////////////////////////////////BLYNK FUNCTION FROM HTTP API EXAMPLE
bool httpRequest(const String& method,
                 const String& url,
                 const String& request,
                 String&       response)
{
  Serial.print(F("Connecting to "));
  Serial.print(host);
  Serial.print(":");
  Serial.print(port);
  Serial.print("... ");
  if (client.connect(host, port)) {
    Serial.println("OK");
  } else {
    Serial.println("failed");
    return false;
  }

  Serial.print(method); Serial.print(" "); Serial.println(url);

  client.print(method); client.print(" ");
  client.print(url); client.println(F(" HTTP/1.1"));
  client.print(F("Host: ")); client.println(host);
  client.println(F("Connection: close"));
  if (request.length()) {
    client.println(F("Content-Type: application/json"));
    client.print(F("Content-Length: ")); client.println(request.length());
    client.println();
    client.print(request);
  } else {
    client.println();
  }

  //Serial.println("Waiting response");
  int timeout = millis() + 5000;
  while (client.available() == 0) {
    if (timeout - millis() < 0) {
      Serial.println(">>> Client Timeout !");
      client.stop();
      return false;
    }
  }

  //Serial.println("Reading response");
  int contentLength = -1;
  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.length() == 0) {
      break;
    }
  }

  //Serial.println("Reading response body");
  response = "";
  response.reserve(contentLength + 1);
  while (response.length() < contentLength) {
    if (client.available()) {
      char c = client.read();
      response += c;
    } else if (!client.connected()) {
      break;
    }
  }
  client.stop();
  return true;
}

Your device isn’t online as far as Blynk is concerned.
A device is online if the device has negotiated an “always on” connection to the Blynk server, and responds to heartbeat handshake messages.
The HTTP(S) protocol doesn’t create this type of connection, it simply creates an “as required” connection which is then terminated once the data exchange has taken place.

Pete.

Thank you for the quick reply Pete!
Your answer makes total sense. Would you be able to suggest a different route instead of the HTTP api were I can still maintain my existing webserver and captive portal functionality… Such as integrating Edgent or other Blynk modules into my code where a WIFI connection is already established?

Thanks again.

Difficult to say without understanding what your web server and captive portal do.

You certainly wouldn’t want to go down the Edgent route if you were doing this stuff.

Pete.

Ok guys, I solved this problem if anyone wants a webserver and Blynk running simultaneously on an ESP8266.
In this code, the ESP8266 will try to connect to the wifi network with credentials stored in eeprom. If the credentials do not exist or the conenction attempt fails it will switch to AP mode and serve up a SSID that you can connect to (portal) and configure your WIFI credentials. It will then save these to eeprom and connect to your network.

It will then become a webserver with websocket connection so that sensor values can be displayed on a webpage. This functionality is fully documented here:

Simply adding the BlynkMultiClient.h to the sketch was enough to get the Blink connection. I can update values at 1hz and Blynk shows me as Online.

#include <ESPAsyncWebServer.h>
#include <ESPAsyncWiFiManager.h>         //https://github.com/tzapu/WiFiManager
#include <ESPAsyncTCP.h>

#include "index.h"

/* Fill in information from Blynk Device Info here */
#define BLYNK_TEMPLATE_ID           "************"
#define BLYNK_TEMPLATE_NAME         "************"
#define BLYNK_AUTH_TOKEN            "************"
#define BLYNK_PRINT Serial
#include <BlynkMultiClient.h>


// Blynk cloud server
const char* host = "blynk.cloud";
unsigned int port = 80;
int value = 0;


WiFiClient blynkWiFiClient;

AsyncWebServer server(80);
DNSServer dns;
AsyncWebSocket ws("/ws");   // Create AsyncWebServer object on port 80



void notifyClients(String textMsg) {
  ws.textAll(textMsg);
}

void handleWebSocketMessage(void *arg, uint8_t *data, size_t len) {
  AwsFrameInfo *info = (AwsFrameInfo*)arg;
  if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
    data[len] = 0;
    if (strcmp((char*)data, "toggle") == 0) {
      ledState = !ledState;
    }
  }
}

void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
    switch (type) {
      case WS_EVT_CONNECT:
        Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
        break;
      case WS_EVT_DISCONNECT:
        Serial.printf("WebSocket client #%u disconnected\n", client->id());
        break;
      case WS_EVT_DATA:
        handleWebSocketMessage(arg, data, len);
        break;
      case WS_EVT_PONG:
      case WS_EVT_ERROR:
        break;
  }
}

void initWebSocket() {
  ws.onEvent(onEvent);
  server.addHandler(&ws);
}


void setup(){
  Serial.begin(9600);
  AsyncWiFiManager wifiManager(&server,&dns);
  
  // Connect to Wi-Fi
  wifiManager.autoConnect("TempSSID");    //THE SSID OF THE CONFIG PORTAL

  // Print ESP Local IP Address
  Serial.println("Connected!");
  Serial.println(WiFi.localIP());
  initWebSocket();

  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html);
  });
 
  // Start server
  server.begin();

  // Setup Blynk
  Blynk.addClient("WiFi", blynkWiFiClient, 80);
  Blynk.config(BLYNK_AUTH_TOKEN);
}


void loop() {
  ws.cleanupClients();    //WEBSOCKET HOUSEKEEPING
  String inData = "";
  String response;

  value++;
  delay(1000);
  

Blynk.virtualWrite(V1, value);    //VIRTUAL WRITE TO BLYNK
Blynk.run();
}```