1Pump 1Valves with Blynk IoT

***esp8266 with Blynk:grinning: :grinning: :grinning:

// Fill-in information from your Blynk Template here
#define BLYNK_TEMPLATE_ID ""
#define BLYNK_DEVICE_NAME "
"

#define BLYNK_FIRMWARE_VERSION "0.1.0"

#define BLYNK_PRINT Serial
//#define BLYNK_DEBUG

#define APP_DEBUG

// Uncomment your board, or configure a custom board in Settings.h
//#define USE_SPARKFUN_BLYNK_BOARD
#define USE_NODE_MCU_BOARD
//#define USE_WITTY_CLOUD_BOARD
//#define USE_WEMOS_D1_MINI

#include "BlynkEdgent.h"

void setup()
{

Serial.begin(115200);

 pinMode(D0,OUTPUT); 
 pinMode(D1,OUTPUT);


digitalWrite(D0,HIGH);
digitalWrite(D1,HIGH);

delay(10);

BlynkEdgent.begin();

}

void loop()
{
BlynkEdgent.run();
}

BLYNK_WRITE(V0)
{

if(param.asInt() == 1)
{
digitalWrite(D0,LOW);
}
else
{
digitalWrite(D0,HIGH);
}
}

BLYNK_WRITE(V1)
{
if(param.asInt() == 1)
{
digitalWrite(D1,LOW);
}
else
{
digitalWrite(D1,HIGH);
}
}
}

1. BlynkEdgent.h*
extern "C" {
#include "user_interface.h"

void app_loop();
}

#include "Settings.h"
#include <BlynkSimpleEsp8266_SSL.h>

#ifndef BLYNK_NEW_LIBRARY
#error "Old version of Blynk library is in use. Please replace it with the new one."
#endif

#if !defined(BLYNK_TEMPLATE_ID) || !defined(BLYNK_DEVICE_NAME)
#error "Please specify your BLYNK_TEMPLATE_ID and BLYNK_DEVICE_NAME"
#endif

#include "BlynkState.h"
#include "ConfigStore.h"
#include "ResetButton.h"
#include "ConfigMode.h"
#include "Indicator.h"
#include "OTA.h"
#include "Console.h"

inline
void BlynkState::set(State m) {
if (state != m && m < MODE_MAX_VALUE) {
DEBUG_PRINT(String(StateStr[state]) + " => " + StateStr[m]);
state = m;

// You can put your state handling here,
// i.e. implement custom indication

}
}

void printDeviceBanner()
{
Blynk.printBanner();
DEBUG_PRINT("--------------------------");
DEBUG_PRINT(String("Product: ") + BLYNK_DEVICE_NAME);
DEBUG_PRINT(String("Firmware: ") + BLYNK_FIRMWARE_VERSION " (build " DATE " " TIME ")");
if (configStore.getFlag(CONFIG_FLAG_VALID)) {
DEBUG_PRINT(String("Token: ...") + (configStore.cloudToken+28));
}
DEBUG_PRINT(String("Device: ") + BLYNK_INFO_DEVICE + " @ " + ESP.getCpuFreqMHz() + "MHz");
DEBUG_PRINT(String("MAC: ") + WiFi.macAddress());
DEBUG_PRINT(String("Flash: ") + ESP.getFlashChipRealSize() / 1024 + "K");
String coreVer = ESP.getCoreVersion();
coreVer.replace("_", ".");
DEBUG_PRINT(String("ESP core: ") + coreVer);
DEBUG_PRINT(String("ESP SDK: ") + ESP.getSdkVersion());
DEBUG_PRINT(String("Boot Ver: ") + ESP.getBootVersion());
DEBUG_PRINT(String("Boot Mode:") + ESP.getBootMode());
DEBUG_PRINT(String("FW info: ") + ESP.getSketchSize() + "/" + ESP.getFreeSketchSpace() + ", MD5:" + ESP.getSketchMD5());
DEBUG_PRINT(String("Free mem: ") + ESP.getFreeHeap());
DEBUG_PRINT("--------------------------");
}

void runBlynkWithChecks() {
Blynk.run();
if (BlynkState::get() == MODE_RUNNING) {
if (!Blynk.connected()) {
if (WiFi.status() == WL_CONNECTED) {
BlynkState::set(MODE_CONNECTING_CLOUD);
} else {
BlynkState::set(MODE_CONNECTING_NET);
}
}
}
}

class Edgent {

public:
void begin()
{
indicator_init();
button_init();
config_init();
console_init();

printDeviceBanner();

if (configStore.getFlag(CONFIG_FLAG_VALID)) {
  BlynkState::set(MODE_CONNECTING_NET);
} else if (config_load_blnkopt()) {
  DEBUG_PRINT("Firmware is preprovisioned");
  BlynkState::set(MODE_CONNECTING_NET);
} else {
  BlynkState::set(MODE_WAIT_CONFIG);
}

}

void run() {
app_loop();
switch (BlynkState::get()) {
case MODE_WAIT_CONFIG:
case MODE_CONFIGURING: enterConfigMode(); break;
case MODE_CONNECTING_NET: enterConnectNet(); break;
case MODE_CONNECTING_CLOUD: enterConnectCloud(); break;
case MODE_RUNNING: runBlynkWithChecks(); break;
case MODE_OTA_UPGRADE: enterOTA(); break;
case MODE_SWITCH_TO_STA: enterSwitchToSTA(); break;
case MODE_RESET_CONFIG: enterResetConfig(); break;
default: enterError(); break;
}
}

};

Edgent BlynkEdgent;
BlynkTimer edgentTimer;

void app_loop() {
edgentTimer.run();
edgentConsole.run();
}

  1. BlynkState.h
    enum State {
    MODE_WAIT_CONFIG,
    MODE_CONFIGURING,
    MODE_CONNECTING_NET,
    MODE_CONNECTING_CLOUD,
    MODE_RUNNING,
    MODE_OTA_UPGRADE,
    MODE_SWITCH_TO_STA,
    MODE_RESET_CONFIG,
    MODE_ERROR,

MODE_MAX_VALUE
};

#if defined(APP_DEBUG)
const char* StateStr[MODE_MAX_VALUE+1] = {
"WAIT_CONFIG",
"CONFIGURING",
"CONNECTING_NET",
"CONNECTING_CLOUD",
"RUNNING",
"OTA_UPGRADE",
"SWITCH_TO_STA",
"RESET_CONFIG",
"ERROR",

"INIT"
};
#endif

namespace BlynkState
{
volatile State state = MODE_MAX_VALUE;

State get() { return state; }
bool is (State m) { return (state == m); }
void set(State m);
};

  1. ConfigMode.h
    #include <ESP8266WiFi.h>
    #include <ESP8266WebServer.h>
    #include <ESP8266HTTPUpdateServer.h>
    #include <DNSServer.h>

ESP8266WebServer server(80);
ESP8266HTTPUpdateServer httpUpdater;
DNSServer dnsServer;
const byte DNS_PORT = 53;

#ifdef BLYNK_USE_SPIFFS
#include <FS.h>
#else
const char* config_form = R"html(

WiFi setup body { background-color: #fcfcfc; box-sizing: border-box; } body, input { font-family: Roboto, sans-serif; font-weight: 400; font-size: 16px; } .centered { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
padding: 20px;
background-color: #ccc;
border-radius: 4px;

}
td { padding:0 0 0 5px; }
label { white-space:nowrap; }
input { width: 20em; }
input[name="port"] { width: 5em; }
input[type="submit"], img { margin: auto; display: block; width: 30%; }

WiFi SSID:
Password:
Auth token:
Host:
Port:

)html"; #endif

void restartMCU() {
ESP.restart();
delay(10000);
ESP.reset();
while(1) {};
}

void getWiFiName(char* buff, size_t len, bool withPrefix = true) {
byte mac[6] = { 0, };
WiFi.macAddress(mac);

uint32_t unique = 0;
for (int i=0; i<4; i++) {
unique = BlynkCRC32(&mac, sizeof(mac), unique);
}
unique &= 0xFFFFF;

if (withPrefix) {
snprintf(buff, len, "Blynk %s-%05X", BLYNK_DEVICE_NAME, unique);
} else {
snprintf(buff, len, "%s-%05X", BLYNK_DEVICE_NAME, unique);
}
}

void enterConfigMode()
{
char ssidBuff[64];
getWiFiName(ssidBuff, sizeof(ssidBuff));

WiFi.mode(WIFI_OFF);
delay(100);
WiFi.mode(WIFI_AP_STA);
WiFi.softAPConfig(WIFI_AP_IP, WIFI_AP_IP, WIFI_AP_Subnet);
WiFi.softAP(ssidBuff);
delay(500);

IPAddress myIP = WiFi.softAPIP();
DEBUG_PRINT(String("AP SSID: ") + ssidBuff);
DEBUG_PRINT(String("AP IP: ") + myIP[0] + "." + myIP[1] + "." + myIP[2] + "." + myIP[3]);

if (myIP == (uint32_t)0)
{
config_set_last_error(BLYNK_PROV_ERR_INTERNAL);
BlynkState::set(MODE_ERROR);
return;
}

// Set up DNS Server
dnsServer.setTTL(300); // Time-to-live 300s
dnsServer.setErrorReplyCode(DNSReplyCode::ServerFailure); // Return code for non-accessible domains
#ifdef WIFI_CAPTIVE_PORTAL_ENABLE
dnsServer.start(DNS_PORT, "*", WiFi.softAPIP()); // Point all to our IP
server.onNotFound(handleRoot);
#else
dnsServer.start(DNS_PORT, CONFIG_AP_URL, WiFi.softAPIP());
DEBUG_PRINT(String("AP URL: ") + CONFIG_AP_URL);
#endif

httpUpdater.setup(&server, "/update");

#ifndef BLYNK_USE_SPIFFS
server.on("/", {
server.send(200, "text/html", config_form);
});
#endif
server.on("/config", {
DEBUG_PRINT("Applying configuration...");
String ssid = server.arg("ssid");
String ssidManual = server.arg("ssidManual");
String pass = server.arg("pass");
if (ssidManual != "") {
ssid = ssidManual;
}
String token = server.arg("blynk");
String host = server.arg("host");
String port = server.arg("port_ssl");

String ip   = server.arg("ip");
String mask = server.arg("mask");
String gw   = server.arg("gw");
String dns  = server.arg("dns");
String dns2 = server.arg("dns2");

bool save  = server.arg("save").toInt();

String content;

DEBUG_PRINT(String("WiFi SSID: ") + ssid + " Pass: " + pass);
DEBUG_PRINT(String("Blynk cloud: ") + token + " @ " + host + ":" + port);

if (token.length() == 32 && ssid.length() > 0) {
  configStore.setFlag(CONFIG_FLAG_VALID, false);
  CopyString(ssid, configStore.wifiSSID);
  CopyString(pass, configStore.wifiPass);
  CopyString(token, configStore.cloudToken);
  if (host.length()) {
    CopyString(host,  configStore.cloudHost);
  }
  if (port.length()) {
    configStore.cloudPort = port.toInt();
  }

  IPAddress addr;
  
  if (ip.length() && addr.fromString(ip)) {
    configStore.staticIP = addr;
    configStore.setFlag(CONFIG_FLAG_STATIC_IP, true);
  } else {
    configStore.setFlag(CONFIG_FLAG_STATIC_IP, false);
  }
  if (mask.length() && addr.fromString(mask)) {
    configStore.staticMask = addr;
  }
  if (gw.length() && addr.fromString(gw)) {
    configStore.staticGW = addr;
  }
  if (dns.length() && addr.fromString(dns)) {
    configStore.staticDNS = addr;
  }
  if (dns2.length() && addr.fromString(dns2)) {
    configStore.staticDNS2 = addr;
  }

  if (save) {
    configStore.setFlag(CONFIG_FLAG_VALID, true);
    config_save();

    content = R"json({"status":"ok","msg":"Configuration saved"})json";
  } else {
    content = R"json({"status":"ok","msg":"Trying to connect..."})json";
  }
  server.send(200, "application/json", content);

  BlynkState::set(MODE_SWITCH_TO_STA);
} else {
  DEBUG_PRINT("Configuration invalid");
  content = R"json({"status":"error","msg":"Configuration invalid"})json";
  server.send(500, "application/json", content);
}

});
server.on("/board_info.json", {
DEBUG_PRINT("Sending board info...");
const char* tmpl = BLYNK_TEMPLATE_ID;
char ssidBuff[64];
getWiFiName(ssidBuff, sizeof(ssidBuff));
char buff[512];
snprintf(buff, sizeof(buff),
R"json({"board":"%s","tmpl_id":"%s","fw_type":"%s","fw_ver":"%s","ssid":"%s","bssid":"%s","last_error":%d,"wifi_scan":true,"static_ip":true})json",
BLYNK_DEVICE_NAME,
tmpl ? tmpl : "Unknown",
BLYNK_FIRMWARE_TYPE,
BLYNK_FIRMWARE_VERSION,
ssidBuff,
WiFi.softAPmacAddress().c_str(),
configStore.last_error
);
server.send(200, "application/json", buff);
});
server.on("/wifi_scan.json", {
DEBUG_PRINT("Scanning networks...");
int wifi_nets = WiFi.scanNetworks(true, true);
const uint32_t t = millis();
while (wifi_nets < 0 &&
millis() - t < 20000)
{
delay(20);
wifi_nets = WiFi.scanComplete();
}
DEBUG_PRINT(String("Found networks: ") + wifi_nets);

if (wifi_nets > 0) {
  // Sort networks
  int indices[wifi_nets];
  for (int i = 0; i < wifi_nets; i++) {
    indices[i] = i;
  }
  for (int i = 0; i < wifi_nets; i++) {
    for (int j = i + 1; j < wifi_nets; j++) {
      if (WiFi.RSSI(indices[j]) > WiFi.RSSI(indices[i])) {
        std::swap(indices[i], indices[j]);
      }
    }
  }

  wifi_nets = BlynkMin(15, wifi_nets); // Show top 15 networks

  // TODO: skip empty names
  server.setContentLength(CONTENT_LENGTH_UNKNOWN);
  server.send(200, "application/json", "[\n");


  char buff[256];
  for (int i = 0; i < wifi_nets; i++){
    int id = indices[i];

    const char* sec;
    switch (WiFi.encryptionType(id)) {
    case ENC_TYPE_WEP:  sec = "WEP"; break;
    case ENC_TYPE_TKIP: sec = "WPA/PSK"; break;
    case ENC_TYPE_CCMP: sec = "WPA2/PSK"; break;
    case ENC_TYPE_AUTO: sec = "WPA/WPA2/PSK"; break;
    case ENC_TYPE_NONE: sec = "OPEN"; break;
    default:            sec = "unknown"; break;
    }

    snprintf(buff, sizeof(buff),
      R"json(  {"ssid":"%s","bssid":"%s","rssi":%i,"sec":"%s","ch":%i,"hidden":%d})json",
      WiFi.SSID(id).c_str(),
      WiFi.BSSIDstr(id).c_str(),
      WiFi.RSSI(id),
      sec,
      WiFi.channel(id),
      WiFi.isHidden(id)
    );

    server.sendContent(buff);
    if (i != wifi_nets-1) server.sendContent(",\n");
  }
  server.sendContent("\n]");
} else {
  server.send(200, "application/json", "[]");
}

});
server.on("/reset", {
BlynkState::set(MODE_RESET_CONFIG);
server.send(200, "application/json", R"json({"status":"ok","msg":"Configuration reset"})json");
});
server.on("/reboot", {
restartMCU();
});

#ifdef BLYNK_USE_SPIFFS
if (SPIFFS.begin()) {
server.serveStatic("/img", SPIFFS, "/img");
server.serveStatic("/", SPIFFS, "/index.html");
} else {
DEBUG_PRINT("Webpage: No SPIFFS");
}
#endif

server.begin();

while (BlynkState::is(MODE_WAIT_CONFIG) || BlynkState::is(MODE_CONFIGURING)) {
delay(10);
dnsServer.processNextRequest();
server.handleClient();
app_loop();
if (BlynkState::is(MODE_WAIT_CONFIG) && WiFi.softAPgetStationNum() > 0) {
BlynkState::set(MODE_CONFIGURING);
} else if (BlynkState::is(MODE_CONFIGURING) && WiFi.softAPgetStationNum() == 0) {
BlynkState::set(MODE_WAIT_CONFIG);
}
}

server.stop();

#ifdef BLYNK_USE_SPIFFS
SPIFFS.end();
#endif
}

void enterConnectNet() {
BlynkState::set(MODE_CONNECTING_NET);
DEBUG_PRINT(String("Connecting to WiFi: ") + configStore.wifiSSID);

WiFi.mode(WIFI_STA);

char ssidBuff[64];
getWiFiName(ssidBuff, sizeof(ssidBuff));
String hostname(ssidBuff);
hostname.replace(" ", "-");

WiFi.hostname(hostname.c_str());

if (configStore.getFlag(CONFIG_FLAG_STATIC_IP)) {
if (!WiFi.config(configStore.staticIP,
configStore.staticGW,
configStore.staticMask,
configStore.staticDNS,
configStore.staticDNS2)
) {
DEBUG_PRINT("Failed to configure Static IP");
config_set_last_error(BLYNK_PROV_ERR_CONFIG);
BlynkState::set(MODE_ERROR);
return;
}
}

if (!WiFi.begin(configStore.wifiSSID, configStore.wifiPass)) {
config_set_last_error(BLYNK_PROV_ERR_CONFIG);
BlynkState::set(MODE_ERROR);
return;
}

unsigned long timeoutMs = millis() + WIFI_NET_CONNECT_TIMEOUT;
while ((timeoutMs > millis()) && (WiFi.status() != WL_CONNECTED))
{
delay(10);
app_loop();

if (!BlynkState::is(MODE_CONNECTING_NET)) {
  WiFi.disconnect();
  return;
}

}

if (WiFi.status() == WL_CONNECTED) {
IPAddress localip = WiFi.localIP();
if (configStore.getFlag(CONFIG_FLAG_STATIC_IP)) {
BLYNK_LOG_IP("Using Static IP: ", localip);
} else {
BLYNK_LOG_IP("Using Dynamic IP: ", localip);
}

BlynkState::set(MODE_CONNECTING_CLOUD);

} else {
config_set_last_error(BLYNK_PROV_ERR_NETWORK);
BlynkState::set(MODE_ERROR);
}
}

void enterConnectCloud() {
BlynkState::set(MODE_CONNECTING_CLOUD);

Blynk.config(configStore.cloudToken, configStore.cloudHost, configStore.cloudPort);
Blynk.connect(0);

unsigned long timeoutMs = millis() + WIFI_CLOUD_CONNECT_TIMEOUT;
while ((timeoutMs > millis()) &&
(!Blynk.isTokenInvalid()) &&
(Blynk.connected() == false))
{
delay(10);
Blynk.run();
app_loop();
if (!BlynkState::is(MODE_CONNECTING_CLOUD)) {
Blynk.disconnect();
return;
}
}

if (millis() > timeoutMs) {
DEBUG_PRINT("Timeout");
}

if (Blynk.isTokenInvalid()) {
config_set_last_error(BLYNK_PROV_ERR_TOKEN);
BlynkState::set(MODE_WAIT_CONFIG);
} else if (Blynk.connected()) {
BlynkState::set(MODE_RUNNING);

if (!configStore.getFlag(CONFIG_FLAG_VALID)) {
  configStore.last_error = BLYNK_PROV_ERR_NONE;
  configStore.setFlag(CONFIG_FLAG_VALID, true);
  config_save();
}

} else {
config_set_last_error(BLYNK_PROV_ERR_CLOUD);
BlynkState::set(MODE_ERROR);
}
}

void enterSwitchToSTA() {
BlynkState::set(MODE_SWITCH_TO_STA);

DEBUG_PRINT("Switching to STA...");

delay(1000);
WiFi.mode(WIFI_OFF);
delay(100);
WiFi.mode(WIFI_STA);

BlynkState::set(MODE_CONNECTING_NET);
}

void enterError() {
BlynkState::set(MODE_ERROR);

unsigned long timeoutMs = millis() + 10000;
while (timeoutMs > millis() || g_buttonPressed)
{
delay(10);
app_loop();
if (!BlynkState::is(MODE_ERROR)) {
return;
}
}
DEBUG_PRINT("Restarting after error.");
delay(10);

restartMCU();
}

4.ConfigStore.h
#define CONFIG_FLAG_VALID 0x01
#define CONFIG_FLAG_STATIC_IP 0x02

#define BLYNK_PROV_ERR_NONE 0 // All good
#define BLYNK_PROV_ERR_CONFIG 700 // Invalid config from app (malformed token,etc)
#define BLYNK_PROV_ERR_NETWORK 701 // Could not connect to the router
#define BLYNK_PROV_ERR_CLOUD 702 // Could not connect to the cloud
#define BLYNK_PROV_ERR_TOKEN 703 // Invalid token error (after connection)
#define BLYNK_PROV_ERR_INTERNAL 704 // Other issues (i.e. hardware failure)

struct ConfigStore {
uint32_t magic;
char version[15];
uint8_t flags;

char wifiSSID[34];
char wifiPass[64];

char cloudToken[34];
char cloudHost[34];
uint16_t cloudPort;

uint32_t staticIP;
uint32_t staticMask;
uint32_t staticGW;
uint32_t staticDNS;
uint32_t staticDNS2;

int last_error;

void setFlag(uint8_t mask, bool value) {
if (value) {
flags |= mask;
} else {
flags &= ~mask;
}
}

bool getFlag(uint8_t mask) {
return (flags & mask) == mask;
}
} attribute((packed));

ConfigStore configStore;

const ConfigStore configDefault = {
0x626C6E6B,
BLYNK_FIRMWARE_VERSION,
0x00,

"",
"",

"invalid token",
CONFIG_DEFAULT_SERVER,
CONFIG_DEFAULT_PORT,
0,
BLYNK_PROV_ERR_NONE
};

template<typename T, int size>
void CopyString(const String& s, T(&arr)[size]) {
s.toCharArray(arr, size);
}

static bool config_load_blnkopt()
{
static const char blnkopt[] = "blnkopt\0"
BLYNK_PARAM_KV("ssid" , BLYNK_PARAM_PLACEHOLDER_64
BLYNK_PARAM_PLACEHOLDER_64
BLYNK_PARAM_PLACEHOLDER_64
BLYNK_PARAM_PLACEHOLDER_64)
BLYNK_PARAM_KV("host" , CONFIG_DEFAULT_SERVER)
BLYNK_PARAM_KV("port" , BLYNK_TOSTRING(CONFIG_DEFAULT_PORT))
"\0";

BlynkParam prov(blnkopt+8, sizeof(blnkopt)-8-2);
BlynkParam::iterator ssid = prov["ssid"];
BlynkParam::iterator pass = prov["pass"];
BlynkParam::iterator auth = prov["auth"];
BlynkParam::iterator host = prov["host"];
BlynkParam::iterator port = prov["port"];

if (!(ssid.isValid() && auth.isValid())) {
return false;
}

// reset to defaut before loading values from blnkopt
configStore = configDefault;

if (ssid.isValid()) { CopyString(ssid.asStr(), configStore.wifiSSID); }
if (pass.isValid()) { CopyString(pass.asStr(), configStore.wifiPass); }
if (auth.isValid()) { CopyString(auth.asStr(), configStore.cloudToken); }
if (host.isValid()) { CopyString(host.asStr(), configStore.cloudHost); }
if (port.isValid()) { configStore.cloudPort = port.asInt(); }

return true;
}

#include <EEPROM.h>
#define EEPROM_CONFIG_START 0

void config_load()
{
memset(&configStore, 0, sizeof(configStore));
EEPROM.get(EEPROM_CONFIG_START, configStore);
if (configStore.magic != configDefault.magic) {
DEBUG_PRINT("Using default config.");
configStore = configDefault;
return;
}
}

bool config_save()
{
EEPROM.put(EEPROM_CONFIG_START, configStore);
EEPROM.commit();
DEBUG_PRINT("Configuration stored to flash");
return true;
}

bool config_init()
{
EEPROM.begin(sizeof(ConfigStore));
config_load();
return true;
}

void enterResetConfig()
{
DEBUG_PRINT("Resetting configuration!");
configStore = configDefault;
config_save();
BlynkState::set(MODE_WAIT_CONFIG);
}

void config_set_last_error(int error) {
// Only set error if not provisioned
if (!configStore.getFlag(CONFIG_FLAG_VALID)) {
configStore = configDefault;
configStore.last_error = error;
BLYNK_LOG2("Last error code: ", error);
config_save();
}
}

5.Console.h

#include <Blynk/BlynkConsole.h>

BlynkConsole edgentConsole;

void console_init()
{
edgentConsole.init(BLYNK_PRINT);

edgentConsole.print("\n>");

edgentConsole.addCommand("reboot", {
edgentConsole.print(R"json({"status":"OK","msg":"resetting device"})json" "\n");
delay(100);
restartMCU();
});

edgentConsole.addCommand("config", {
edgentConsole.print(R"json({"status":"OK","msg":"entering configuration mode"})json" "\n");
BlynkState::set(MODE_WAIT_CONFIG);
});

edgentConsole.addCommand("devinfo", {
edgentConsole.printf(
R"json({"board":"%s","tmpl_id":"%s","fw_type":"%s","fw_ver":"%s"})json" "\n",
BLYNK_DEVICE_NAME,
BLYNK_TEMPLATE_ID,
BLYNK_FIRMWARE_TYPE,
BLYNK_FIRMWARE_VERSION
);
});

edgentConsole.addCommand("netinfo", {
char ssidBuff[64];
getWiFiName(ssidBuff, sizeof(ssidBuff));

byte mac[6] = { 0, };
WiFi.macAddress(mac);

edgentConsole.printf(
    R"json({"ssid":"%s","bssid":"%02x:%02x:%02x:%02x:%02x:%02x","rssi":%d})json" "\n",
    ssidBuff,
    mac[5], mac[4], mac[3], mac[2], mac[1], mac[0],
    WiFi.RSSI()
);

});

}

BLYNK_WRITE(InternalPinDBG) {
String cmd = String(param.asStr()) + "\n";
edgentConsole.runCommand((char*)cmd.c_str());
}

6.Indicator.h

#if defined(BOARD_LED_PIN_WS2812)
#include <Adafruit_NeoPixel.h> // Library: GitHub - adafruit/Adafruit_NeoPixel: Arduino library for controlling single-wire LED pixels (NeoPixel, WS2812, etc.)

Adafruit_NeoPixel rgb = Adafruit_NeoPixel(1, BOARD_LED_PIN_WS2812, NEO_GRB + NEO_KHZ800);
#endif

void indicator_run();

#if !defined(BOARD_LED_BRIGHTNESS)
#define BOARD_LED_BRIGHTNESS 255
#endif

#if defined(BOARD_LED_PIN_WS2812) || defined(BOARD_LED_PIN_R)
#define BOARD_LED_IS_RGB
#endif

#define DIMM(x) ((uint32_t)(x)(BOARD_LED_BRIGHTNESS)/255)
#define RGB(r,g,b) (DIMM(r) << 16 | DIMM(g) << 8 | DIMM(b) << 0)
#define TO_PWM(x) ((uint32_t)(x)
(BOARD_PWM_MAX)/255)

class Indicator {
public:

enum Colors {
COLOR_BLACK = RGB(0x00, 0x00, 0x00),
COLOR_WHITE = RGB(0xFF, 0xFF, 0xE7),
COLOR_BLUE = RGB(0x0D, 0x36, 0xFF),
COLOR_BLYNK = RGB(0x2E, 0xFF, 0xB9),
COLOR_RED = RGB(0xFF, 0x10, 0x08),
COLOR_MAGENTA = RGB(0xA7, 0x00, 0xFF),
};

Indicator() {
}

void init() {
m_Counter = 0;
initLED();
}

uint32_t run() {
State currState = BlynkState::get();

// Reset counter if indicator state changes
if (m_PrevState != currState) {
  m_PrevState = currState;
  m_Counter = 0;
}

if (g_buttonPressed) {
  if (millis() - g_buttonPressTime > BUTTON_HOLD_TIME_ACTION)     { return beatLED(COLOR_WHITE,   (int[]){ 100, 100 }); }
  if (millis() - g_buttonPressTime > BUTTON_HOLD_TIME_INDICATION) { return waveLED(COLOR_WHITE,   1000); }
}
switch (currState) {
case MODE_RESET_CONFIG:
case MODE_WAIT_CONFIG:       return beatLED(COLOR_BLUE,    (int[]){ 50, 500 });
case MODE_CONFIGURING:       return beatLED(COLOR_BLUE,    (int[]){ 200, 200 });
case MODE_CONNECTING_NET:    return beatLED(COLOR_BLYNK,   (int[]){ 50, 500 });
case MODE_CONNECTING_CLOUD:  return beatLED(COLOR_BLYNK,   (int[]){ 100, 100 });
case MODE_RUNNING:           return waveLED(COLOR_BLYNK,   5000);
case MODE_OTA_UPGRADE:       return beatLED(COLOR_MAGENTA, (int[]){ 50, 50 });
default:                     return beatLED(COLOR_RED,     (int[]){ 80, 100, 80, 1000 } );
}

}

protected:

/*

  • LED drivers
    */

#if defined(BOARD_LED_PIN_WS2812) // Addressable, NeoPixel RGB LED

void initLED() {
rgb.begin();
setRGB(COLOR_BLACK);
}

void setRGB(uint32_t color) {
rgb.setPixelColor(0, color);
rgb.show();
}

#elif defined(BOARD_LED_PIN_R) // Normal RGB LED (common anode or common cathode)

void initLED() {
pinMode(BOARD_LED_PIN_R, OUTPUT);
pinMode(BOARD_LED_PIN_G, OUTPUT);
pinMode(BOARD_LED_PIN_B, OUTPUT);
}

void setRGB(uint32_t color) {
uint8_t r = (color & 0xFF0000) >> 16;
uint8_t g = (color & 0x00FF00) >> 8;
uint8_t b = (color & 0x0000FF);
#if BOARD_LED_INVERSE
analogWrite(BOARD_LED_PIN_R, TO_PWM(255 - r));
analogWrite(BOARD_LED_PIN_G, TO_PWM(255 - g));
analogWrite(BOARD_LED_PIN_B, TO_PWM(255 - b));
#else
analogWrite(BOARD_LED_PIN_R, TO_PWM(r));
analogWrite(BOARD_LED_PIN_G, TO_PWM(g));
analogWrite(BOARD_LED_PIN_B, TO_PWM(b));
#endif
}

#elif defined(BOARD_LED_PIN) // Single color LED

void initLED() {
pinMode(BOARD_LED_PIN, OUTPUT);
}

void setLED(uint32_t color) {
#if BOARD_LED_INVERSE
analogWrite(BOARD_LED_PIN, TO_PWM(255 - color));
#else
analogWrite(BOARD_LED_PIN, TO_PWM(color));
#endif
}

#else

#warning Invalid LED configuration.

void initLED() {
}

void setLED(uint32_t color) {
}

#endif

/*

  • Animations
    */

uint32_t skipLED() {
return 20;
}

#if defined(BOARD_LED_IS_RGB)

template
uint32_t beatLED(uint32_t onColor, const T& beat) {
const uint8_t cnt = sizeof(beat)/sizeof(beat[0]);
setRGB((m_Counter % 2 == 0) ? onColor : (uint32_t)COLOR_BLACK);
uint32_t next = beat[m_Counter % cnt];
m_Counter = (m_Counter+1) % cnt;
return next;
}

uint32_t waveLED(uint32_t colorMax, unsigned breathePeriod) {
uint8_t redMax = (colorMax & 0xFF0000) >> 16;
uint8_t greenMax = (colorMax & 0x00FF00) >> 8;
uint8_t blueMax = (colorMax & 0x0000FF);

// Brightness will rise from 0 to 128, then fall back to 0
uint8_t brightness = (m_Counter < 128) ? m_Counter : 255 - m_Counter;

// Multiply our three colors by the brightness:
redMax *= ((float)brightness / 128.0);
greenMax *= ((float)brightness / 128.0);
blueMax *= ((float)brightness / 128.0);
// And turn the LED to that color:
setRGB((redMax << 16) | (greenMax << 8) | blueMax);

// This function relies on the 8-bit, unsigned m_Counter rolling over.
m_Counter = (m_Counter+1) % 256;
return breathePeriod / 256;

}

#else

template
uint32_t beatLED(uint32_t, const T& beat) {
const uint8_t cnt = sizeof(beat)/sizeof(beat[0]);
setLED((m_Counter % 2 == 0) ? BOARD_LED_BRIGHTNESS : 0);
uint32_t next = beat[m_Counter % cnt];
m_Counter = (m_Counter+1) % cnt;
return next;
}

uint32_t waveLED(uint32_t, unsigned breathePeriod) {
uint32_t brightness = (m_Counter < 128) ? m_Counter : 255 - m_Counter;

setLED(DIMM(brightness*2));

// This function relies on the 8-bit, unsigned m_Counter rolling over.
m_Counter = (m_Counter+1) % 256;
return breathePeriod / 256;

}

#endif

private:
uint8_t m_Counter;
State m_PrevState;
};

Indicator indicator;

/*

  • Animation timers
    */

#if defined(USE_TICKER)

#include <Ticker.h>

Ticker blinker;

void indicator_run() {
uint32_t returnTime = indicator.run();
if (returnTime) {
blinker.attach_ms(returnTime, indicator_run);
}
}

void indicator_init() {
indicator.init();
blinker.attach_ms(100, indicator_run);
}

#elif defined(USE_PTHREAD)

#include <pthread.h>

pthread_t blinker;

void* indicator_thread(void*) {
while (true) {
uint32_t returnTime = indicator.run();
returnTime = BlynkMathClamp(returnTime, 1, 10000);
vTaskDelay(returnTime);
}
}

void indicator_init() {
indicator.init();
pthread_create(&blinker, NULL, indicator_thread, NULL);
}

#elif defined(USE_TIMER_ONE)

#include <TimerOne.h>

void indicator_run() {
uint32_t returnTime = indicator.run();
if (returnTime) {
Timer1.initialize(returnTime*1000);
}
}

void indicator_init() {
indicator.init();
Timer1.initialize(100*1000);
Timer1.attachInterrupt(indicator_run);
}

#elif defined(USE_TIMER_THREE)

#include <TimerThree.h>

void indicator_run() {
uint32_t returnTime = indicator.run();
if (returnTime) {
Timer3.initialize(returnTime*1000);
}
}

void indicator_init() {
indicator.init();
Timer3.initialize(100*1000);
Timer3.attachInterrupt(indicator_run);
}

#elif defined(USE_TIMER_FIVE)

#include <Timer5.h> // Library: GitHub - michael71/Timer5: Arduino Lib for TC5 (Timer5) - for SAMD processor, Arduino Zero and MKR1000

int indicator_counter = -1;
void indicator_run() {
indicator_counter -= 10;
if (indicator_counter < 0) {
indicator_counter = indicator.run();
}
}

void indicator_init() {
indicator.init();
MyTimer5.begin(1000/10);
MyTimer5.attachInterrupt(indicator_run);
MyTimer5.start();
}

#else

#warning LED indicator needs a functional timer!

void indicator_run() {}
void indicator_init() {}

#endif

7.OTA.h
#define OTA_FATAL(...) { BLYNK_LOG1(VA_ARGS); delay(1000); restartMCU(); }

#define USE_SSL

String overTheAirURL;

extern BlynkTimer edgentTimer;

BLYNK_WRITE(InternalPinOTA) {
overTheAirURL = param.asString();

edgentTimer.setTimeout(2000L, {
// Start OTA
Blynk.logEvent("sys_ota", "OTA started");

// Disconnect, not to interfere with OTA process
Blynk.disconnect();

BlynkState::set(MODE_OTA_UPGRADE);

});
}

#if defined(ESP32)
#include <Update.h>
#include <WiFiClientSecure.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <time.h>
#endif

#if defined(USE_SSL) && defined(ESP8266)

WiFiClient* connectSSL(const String& host, const int port)
{
WiFiUDP::stopAll();
WiFiClient::stopAll();

time_t now = time(nullptr);
if (time(nullptr) < 100000) {
// Synchronize time useing SNTP. This is necessary to verify that
// the TLS certificates offered by the server are currently valid
configTime(0, 0, "pool.ntp.org", "time.nist.gov");

while (now < 100000) {
    delay(100);
    now = time(nullptr);
}

}

// Reuse Secure WIFI Client on ESP8266
//WiFiClientSecure* clientSSL = &_blynkWifiClient;
WiFiClientSecure* clientSSL = new WiFiClientSecure();

clientSSL->setTrustAnchors(&BlynkCert);
if (!clientSSL->connect(host.c_str(), port)) {
OTA_FATAL(F("Connection failed"));
}
return clientSSL;
}

#elif defined(USE_SSL) && defined(ESP32)

WiFiClient* connectSSL(const String& host, const int port)
{
WiFiUDP::stopAll();
WiFiClient::stopAll();

WiFiClientSecure* clientSSL = new WiFiClientSecure();
clientSSL->setCACert(BLYNK_DEFAULT_ROOT_CA);
if (clientSSL->connect(host.c_str(), port)) {
DEBUG_PRINT(F("Certificate OK"));
} else {
OTA_FATAL(F("Secure connection failed"));
}
return clientSSL;
}

#endif

WiFiClient* connectTCP(const String& host, const int port)
{
WiFiUDP::stopAll();
WiFiClient::stopAll();

WiFiClient* clientTCP = new WiFiClient();
if (!clientTCP->connect(host.c_str(), port)) {
OTA_FATAL(F("Client not connected"));
}
return clientTCP;
}

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;
}

void enterOTA() {
BlynkState::set(MODE_OTA_UPGRADE);

// Disconnect, not to interfere with OTA process
Blynk.disconnect();

String protocol, host, url;
int port;

DEBUG_PRINT(String("OTA: ") + overTheAirURL);

if (!parseURL(overTheAirURL, protocol, host, port, url)) {
OTA_FATAL(F("Cannot parse URL"));
}

DEBUG_PRINT(String("Connecting to ") + host + ":" + port);

Client* client = NULL;
if (protocol == "http") {
client = connectTCP(host, port);
#ifdef USE_SSL
} else if (protocol == "https") {
client = connectSSL(host, port);
#endif
} else {
OTA_FATAL(String("Unsupported protocol: ") + protocol);
}

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) {
OTA_FATAL("Response timeout");
}
delay(10);
}

// Collect headers
String md5;
int contentLength = 0;

while (client->available()) {
String line = client->readStringUntil('\n');
line.trim();
//DEBUG_PRINT(line); // Uncomment this to show response headers
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;
}
delay(10);
}

if (contentLength <= 0) {
OTA_FATAL("Content-Length not defined");
}

bool canBegin = Update.begin(contentLength);
if (!canBegin) {
Update.printError(BLYNK_PRINT);
OTA_FATAL("OTA begin failed");
}

if (md5.length()) {
md5.trim();
md5.toLowerCase();
DEBUG_PRINT(String("Expected MD5: ") + md5);
if(!Update.setMD5(md5.c_str())) {
OTA_FATAL("Cannot set MD5");
}
}

DEBUG_PRINT("Flashing...");

// The next loop does approx. the same thing as Update.writeStream(http) or Update.write(http)

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) {
OTA_FATAL("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) {
  BLYNK_PRINT.print(String("\r ") + progress + "%");
  prevProgress = progress;
}

}
BLYNK_PRINT.println();
client->stop();

if (written != contentLength) {
Update.printError(BLYNK_PRINT);
OTA_FATAL(String("Write failed. Written ") + written + " / " + contentLength + " bytes");
}

if (!Update.end()) {
Update.printError(BLYNK_PRINT);
OTA_FATAL(F("Update not ended"));
}

if (!Update.isFinished()) {
OTA_FATAL(F("Update not finished"));
}

DEBUG_PRINT("=== Update successfully completed. Rebooting.");
restartMCU();
}

8.ResetButton.h

volatile bool g_buttonPressed = false;
volatile uint32_t g_buttonPressTime = -1;

void button_action(void)
{
BlynkState::set(MODE_RESET_CONFIG);
}

ICACHE_RAM_ATTR
void button_change(void)
{
#if BOARD_BUTTON_ACTIVE_LOW
bool buttonState = !digitalRead(BOARD_BUTTON_PIN);
#else
bool buttonState = digitalRead(BOARD_BUTTON_PIN);
#endif

if (buttonState && !g_buttonPressed) {
g_buttonPressTime = millis();
g_buttonPressed = true;
DEBUG_PRINT("Hold the button for 10 seconds to reset configuration...");
} else if (!buttonState && g_buttonPressed) {
g_buttonPressed = false;
uint32_t buttonHoldTime = millis() - g_buttonPressTime;
if (buttonHoldTime >= BUTTON_HOLD_TIME_ACTION) {
button_action();
} else {
// User action
}
g_buttonPressTime = -1;
}
}

void button_init()
{
#if BOARD_BUTTON_ACTIVE_LOW
pinMode(BOARD_BUTTON_PIN, INPUT_PULLUP);
#else
pinMode(BOARD_BUTTON_PIN, INPUT);
#endif
attachInterrupt(BOARD_BUTTON_PIN, button_change, CHANGE);
}

9.Settings.h
#if defined(USE_NODE_MCU_BOARD) || defined(USE_WEMOS_D1_MINI)

#define BOARD_BUTTON_PIN 0
#define BOARD_BUTTON_ACTIVE_LOW true

#define BOARD_LED_PIN 2
#define BOARD_LED_INVERSE true
#define BOARD_LED_BRIGHTNESS 255

#elif defined(USE_SPARKFUN_BLYNK_BOARD)

#define BOARD_BUTTON_PIN 0
#define BOARD_BUTTON_ACTIVE_LOW true

#define BOARD_LED_PIN_WS2812 4
#define BOARD_LED_BRIGHTNESS 64

#elif defined(USE_WITTY_CLOUD_BOARD)

#define BOARD_BUTTON_PIN 4
#define BOARD_BUTTON_ACTIVE_LOW true

#define BOARD_LED_PIN_R 15
#define BOARD_LED_PIN_G 12
#define BOARD_LED_PIN_B 13
#define BOARD_LED_INVERSE false
#define BOARD_LED_BRIGHTNESS 64

#else

#warning "Custom board configuration is used"

#define BOARD_BUTTON_PIN 0 // Pin where user button is attached
#define BOARD_BUTTON_ACTIVE_LOW true // true if button is "active-low"

#define BOARD_LED_PIN 4 // Set LED pin - if you have a single-color LED attached
//#define BOARD_LED_PIN_R 15 // Set R,G,B pins - if your LED is PWM RGB
//#define BOARD_LED_PIN_G 12
//#define BOARD_LED_PIN_B 13
//#define BOARD_LED_PIN_WS2812 4 // Set if your LED is WS2812 RGB
#define BOARD_LED_INVERSE false // true if LED is common anode, false if common cathode
#define BOARD_LED_BRIGHTNESS 64 // 0..255 brightness control

#endif

/*

  • Advanced options
    */

#define BUTTON_HOLD_TIME_INDICATION 3000
#define BUTTON_HOLD_TIME_ACTION 10000

#define BOARD_PWM_MAX 1023

#define CONFIG_AP_URL "blynk.setup"
#define CONFIG_DEFAULT_SERVER "blynk.cloud"
#define CONFIG_DEFAULT_PORT 443

#define WIFI_NET_CONNECT_TIMEOUT 30000
#define WIFI_CLOUD_CONNECT_TIMEOUT 60000
#define WIFI_AP_IP IPAddress(192, 168, 4, 1)
#define WIFI_AP_Subnet IPAddress(255, 255, 255, 0)
//#define WIFI_CAPTIVE_PORTAL_ENABLE

#define USE_TICKER
//#define USE_TIMER_ONE
//#define USE_TIMER_THREE
//#define USE_TIMER_FIVE
//#define USE_PTHREAD

#define BLYNK_NO_DEFAULT_BANNER

#if defined(APP_DEBUG)
#define DEBUG_PRINT(...) BLYNK_LOG1(VA_ARGS)
#else
#define DEBUG_PRINT(...)
#endif

Welcome to the forums. Please read this: How to get the best out of this forum - Using Arduino / Installation & Troubleshooting - Arduino Forum

and then go back and edit your post so your code is inside code tags. It will help people help you.
Also, is there a question you are trying to ask? What is/isn't working?

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.