first commit
This commit is contained in:
153
libraries/Blynk/examples/Blynk.Edgent/Edgent_ESP32/BlynkEdgent.h
Normal file
153
libraries/Blynk/examples/Blynk.Edgent/Edgent_ESP32/BlynkEdgent.h
Normal file
@@ -0,0 +1,153 @@
|
||||
|
||||
extern "C" {
|
||||
void app_loop();
|
||||
void restartMCU();
|
||||
}
|
||||
|
||||
#include "Settings.h"
|
||||
#include <BlynkSimpleEsp32_SSL.h>
|
||||
|
||||
#if defined(BLYNK_USE_LITTLEFS)
|
||||
#include <LittleFS.h>
|
||||
#define BLYNK_FS LittleFS
|
||||
#elif defined(BLYNK_USE_SPIFFS)
|
||||
#if defined(ESP32)
|
||||
#include <SPIFFS.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <FS.h>
|
||||
#endif
|
||||
#define BLYNK_FS SPIFFS
|
||||
#endif
|
||||
|
||||
#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_NAME) && defined(BLYNK_DEVICE_NAME)
|
||||
#define BLYNK_TEMPLATE_NAME BLYNK_DEVICE_NAME
|
||||
#endif
|
||||
|
||||
#if !defined(BLYNK_TEMPLATE_ID) || !defined(BLYNK_TEMPLATE_NAME)
|
||||
#error "Please specify your BLYNK_TEMPLATE_ID and BLYNK_TEMPLATE_NAME"
|
||||
#endif
|
||||
|
||||
#if defined(BLYNK_AUTH_TOKEN)
|
||||
#error "BLYNK_AUTH_TOKEN is assigned automatically when using Blynk.Edgent, please remove it from the configuration"
|
||||
#endif
|
||||
|
||||
BlynkTimer edgentTimer;
|
||||
|
||||
#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()
|
||||
{
|
||||
#ifdef BLYNK_PRINT
|
||||
Blynk.printBanner();
|
||||
BLYNK_PRINT.println("----------------------------------------------------");
|
||||
BLYNK_PRINT.print(" Device: "); BLYNK_PRINT.println(getWiFiName());
|
||||
BLYNK_PRINT.print(" Firmware: "); BLYNK_PRINT.println(BLYNK_FIRMWARE_VERSION " (build " __DATE__ " " __TIME__ ")");
|
||||
if (configStore.getFlag(CONFIG_FLAG_VALID)) {
|
||||
BLYNK_PRINT.print(" Token: ");
|
||||
BLYNK_PRINT.println(String(configStore.cloudToken).substring(0,4) +
|
||||
" - •••• - •••• - ••••");
|
||||
}
|
||||
BLYNK_PRINT.print(" Platform: "); BLYNK_PRINT.println(String(BLYNK_INFO_DEVICE) + " @ " + ESP.getCpuFreqMHz() + "MHz");
|
||||
BLYNK_PRINT.print(" Chip rev: "); BLYNK_PRINT.println(ESP.getChipRevision());
|
||||
BLYNK_PRINT.print(" SDK: "); BLYNK_PRINT.println(ESP.getSdkVersion());
|
||||
BLYNK_PRINT.print(" Flash: "); BLYNK_PRINT.println(String(ESP.getFlashChipSize() / 1024) + "K");
|
||||
BLYNK_PRINT.print(" Free mem: "); BLYNK_PRINT.println(ESP.getFreeHeap());
|
||||
BLYNK_PRINT.println("----------------------------------------------------");
|
||||
#endif
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
WiFi.persistent(false);
|
||||
WiFi.enableSTA(true); // Needed to get MAC
|
||||
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0))
|
||||
WiFi.setMinSecurity(WIFI_AUTH_WEP);
|
||||
#endif
|
||||
|
||||
#ifdef BLYNK_FS
|
||||
BLYNK_FS.begin(true);
|
||||
#endif
|
||||
|
||||
indicator_init();
|
||||
button_init();
|
||||
config_init();
|
||||
printDeviceBanner();
|
||||
console_init();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (!String(BLYNK_TEMPLATE_ID).startsWith("TMPL") ||
|
||||
!strlen(BLYNK_TEMPLATE_NAME)
|
||||
) {
|
||||
DEBUG_PRINT("Invalid configuration of TEMPLATE_ID / TEMPLATE_NAME");
|
||||
while (true) { delay(100); }
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
} BlynkEdgent;
|
||||
|
||||
void app_loop() {
|
||||
edgentTimer.run();
|
||||
edgentConsole.run();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
544
libraries/Blynk/examples/Blynk.Edgent/Edgent_ESP32/ConfigMode.h
Normal file
544
libraries/Blynk/examples/Blynk.Edgent/Edgent_ESP32/ConfigMode.h
Normal file
@@ -0,0 +1,544 @@
|
||||
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServer.h>
|
||||
#include <DNSServer.h>
|
||||
#include <Update.h>
|
||||
|
||||
#ifndef BLYNK_FS
|
||||
|
||||
const char* config_form = R"html(
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>WiFi setup</title>
|
||||
<style>
|
||||
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%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="centered">
|
||||
<form method="get" action="config">
|
||||
<table>
|
||||
<tr><td><label for="ssid">WiFi SSID:</label></td> <td><input type="text" name="ssid" length=64 required="required"></td></tr>
|
||||
<tr><td><label for="pass">Password:</label></td> <td><input type="text" name="pass" length=64></td></tr>
|
||||
<tr><td><label for="blynk">Auth token:</label></td><td><input type="text" name="blynk" placeholder="a0b1c2d..." pattern="[-_a-zA-Z0-9]{32}" maxlength="32" required="required"></td></tr>
|
||||
<tr><td><label for="host">Host:</label></td> <td><input type="text" name="host" value="blynk.cloud" length=64></td></tr>
|
||||
<tr><td><label for="port_ssl">Port:</label></td> <td><input type="number" name="port_ssl" value="443" min="1" max="65535"></td></tr>
|
||||
</table><br/>
|
||||
<input type="submit" value="Apply">
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
)html";
|
||||
|
||||
#endif
|
||||
|
||||
WebServer server(80);
|
||||
DNSServer dnsServer;
|
||||
const byte DNS_PORT = 53;
|
||||
|
||||
static int connectNetRetries = WIFI_CLOUD_MAX_RETRIES;
|
||||
static int connectBlynkRetries = WIFI_CLOUD_MAX_RETRIES;
|
||||
|
||||
static const char serverUpdateForm[] PROGMEM =
|
||||
R"(<html><body>
|
||||
<form method='POST' action='' enctype='multipart/form-data'>
|
||||
<input type='file' name='update'>
|
||||
<input type='submit' value='Update'>
|
||||
</form>
|
||||
</body></html>)";
|
||||
|
||||
void restartMCU() {
|
||||
ESP.restart();
|
||||
while(1) {};
|
||||
}
|
||||
|
||||
static
|
||||
String encodeUniquePart(uint32_t n, unsigned len)
|
||||
{
|
||||
static constexpr char alphabet[] = { "0W8N4Y1HP5DF9K6JM3C2UA7R" };
|
||||
static constexpr int base = sizeof(alphabet)-1;
|
||||
|
||||
char buf[16] = { 0, };
|
||||
char prev = 0;
|
||||
for (unsigned i = 0; i < len; n /= base) {
|
||||
char c = alphabet[n % base];
|
||||
if (c == prev) {
|
||||
c = alphabet[(n+1) % base];
|
||||
}
|
||||
prev = buf[i++] = c;
|
||||
}
|
||||
return String(buf);
|
||||
}
|
||||
|
||||
static
|
||||
String getWiFiName(bool withPrefix = true)
|
||||
{
|
||||
const uint64_t chipId = ESP.getEfuseMac();
|
||||
|
||||
uint32_t unique = 0;
|
||||
for (int i=0; i<4; i++) {
|
||||
unique = BlynkCRC32(&chipId, sizeof(chipId), unique);
|
||||
}
|
||||
String devUnique = encodeUniquePart(unique, 4);
|
||||
|
||||
String devPrefix = CONFIG_DEVICE_PREFIX;
|
||||
String devName = String(BLYNK_TEMPLATE_NAME).substring(0, 31-6-devPrefix.length());
|
||||
|
||||
if (withPrefix) {
|
||||
return devPrefix + " " + devName + "-" + devUnique;
|
||||
} else {
|
||||
return devName + "-" + devUnique;
|
||||
}
|
||||
}
|
||||
|
||||
static inline
|
||||
String macToString(byte mac[6]) {
|
||||
char buff[20];
|
||||
snprintf(buff, sizeof(buff), "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
return String(buff);
|
||||
}
|
||||
|
||||
static inline
|
||||
const char* wifiSecToStr(wifi_auth_mode_t t) {
|
||||
switch (t) {
|
||||
case WIFI_AUTH_OPEN: return "OPEN";
|
||||
case WIFI_AUTH_WEP: return "WEP";
|
||||
case WIFI_AUTH_WPA_PSK: return "WPA";
|
||||
case WIFI_AUTH_WPA2_PSK: return "WPA2";
|
||||
case WIFI_AUTH_WPA_WPA2_PSK: return "WPA+WPA2";
|
||||
case WIFI_AUTH_WPA2_ENTERPRISE: return "WPA2-EAP";
|
||||
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0))
|
||||
case WIFI_AUTH_WPA3_PSK: return "WPA3";
|
||||
case WIFI_AUTH_WPA2_WPA3_PSK: return "WPA2+WPA3";
|
||||
case WIFI_AUTH_WAPI_PSK: return "WAPI";
|
||||
#endif
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
String getWiFiMacAddress() {
|
||||
return WiFi.macAddress();
|
||||
}
|
||||
|
||||
static
|
||||
String getWiFiApBSSID() {
|
||||
return WiFi.softAPmacAddress();
|
||||
}
|
||||
|
||||
static
|
||||
String getWiFiNetworkSSID() {
|
||||
return WiFi.SSID();
|
||||
}
|
||||
|
||||
static
|
||||
String getWiFiNetworkBSSID() {
|
||||
return WiFi.BSSIDstr();
|
||||
}
|
||||
|
||||
void enterConfigMode()
|
||||
{
|
||||
WiFi.mode(WIFI_OFF);
|
||||
delay(100);
|
||||
WiFi.mode(WIFI_AP);
|
||||
delay(2000);
|
||||
WiFi.softAPConfig(WIFI_AP_IP, WIFI_AP_IP, WIFI_AP_Subnet);
|
||||
WiFi.softAP(getWiFiName().c_str());
|
||||
delay(500);
|
||||
|
||||
// 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
|
||||
|
||||
server.on("/update", HTTP_GET, []() {
|
||||
server.sendHeader("Connection", "close");
|
||||
server.send(200, "text/html", serverUpdateForm);
|
||||
});
|
||||
server.on("/update", HTTP_POST, []() {
|
||||
server.sendHeader("Connection", "close");
|
||||
if (!Update.hasError()) {
|
||||
server.send(200, "text/plain", "OK");
|
||||
} else {
|
||||
server.send(500, "text/plain", "FAIL");
|
||||
}
|
||||
delay(1000);
|
||||
restartMCU();
|
||||
}, []() {
|
||||
HTTPUpload& upload = server.upload();
|
||||
if (upload.status == UPLOAD_FILE_START) {
|
||||
DEBUG_PRINT(String("Update: ") + upload.filename);
|
||||
//WiFiUDP::stop();
|
||||
|
||||
if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { //start with max available size
|
||||
DEBUG_PRINT(Update.errorString());
|
||||
}
|
||||
} else if (upload.status == UPLOAD_FILE_WRITE) {
|
||||
/* flashing firmware to ESP*/
|
||||
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
|
||||
DEBUG_PRINT(Update.errorString());
|
||||
}
|
||||
#ifdef BLYNK_PRINT
|
||||
BLYNK_PRINT.print(".");
|
||||
#endif
|
||||
} else if (upload.status == UPLOAD_FILE_END) {
|
||||
#ifdef BLYNK_PRINT
|
||||
BLYNK_PRINT.println();
|
||||
#endif
|
||||
DEBUG_PRINT("Finishing...");
|
||||
if (Update.end(true)) { //true to set the size to the current progress
|
||||
DEBUG_PRINT("Update Success. Rebooting");
|
||||
} else {
|
||||
DEBUG_PRINT(Update.errorString());
|
||||
}
|
||||
}
|
||||
});
|
||||
#ifndef BLYNK_FS
|
||||
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 forceSave = 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 = configDefault;
|
||||
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 (forceSave) {
|
||||
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);
|
||||
|
||||
connectNetRetries = connectBlynkRetries = 1;
|
||||
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", []() {
|
||||
// Configuring starts with board info request (may impact indication)
|
||||
BlynkState::set(MODE_CONFIGURING);
|
||||
|
||||
DEBUG_PRINT("Sending board info...");
|
||||
const char* tmpl = BLYNK_TEMPLATE_ID;
|
||||
|
||||
char buff[512];
|
||||
snprintf(buff, sizeof(buff),
|
||||
R"json({"board":"%s","tmpl_id":"%s","fw_type":"%s","fw_ver":"%s","ssid":"%s","bssid":"%s","mac":"%s","last_error":%d,"wifi_scan":true,"static_ip":true})json",
|
||||
BLYNK_TEMPLATE_NAME,
|
||||
tmpl ? tmpl : "Unknown",
|
||||
BLYNK_FIRMWARE_TYPE,
|
||||
BLYNK_FIRMWARE_VERSION,
|
||||
getWiFiName().c_str(),
|
||||
getWiFiApBSSID().c_str(),
|
||||
getWiFiMacAddress().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
|
||||
String result = "[\n";
|
||||
|
||||
char buff[256];
|
||||
for (int i = 0; i < wifi_nets; i++){
|
||||
int id = indices[i];
|
||||
|
||||
snprintf(buff, sizeof(buff),
|
||||
R"json( {"ssid":"%s","bssid":"%s","rssi":%i,"sec":"%s","ch":%i})json",
|
||||
WiFi.SSID(id).c_str(),
|
||||
WiFi.BSSIDstr(id).c_str(),
|
||||
WiFi.RSSI(id),
|
||||
wifiSecToStr(WiFi.encryptionType(id)),
|
||||
WiFi.channel(id)
|
||||
);
|
||||
|
||||
result += buff;
|
||||
if (i != wifi_nets-1) result += ",\n";
|
||||
}
|
||||
WiFi.scanDelete();
|
||||
server.send(200, "application/json", result + "\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_FS
|
||||
server.serveStatic("/img/favicon.png", BLYNK_FS, "/img/favicon.png");
|
||||
server.serveStatic("/img/logo.png", BLYNK_FS, "/img/logo.png");
|
||||
server.serveStatic("/", BLYNK_FS, "/index.html");
|
||||
#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_CONFIGURING) && WiFi.softAPgetStationNum() == 0) {
|
||||
BlynkState::set(MODE_WAIT_CONFIG);
|
||||
}
|
||||
}
|
||||
|
||||
server.stop();
|
||||
}
|
||||
|
||||
void enterConnectNet() {
|
||||
BlynkState::set(MODE_CONNECTING_NET);
|
||||
DEBUG_PRINT(String("Connecting to WiFi: ") + configStore.wifiSSID);
|
||||
|
||||
// Needed for setHostname to work
|
||||
WiFi.enableSTA(false);
|
||||
|
||||
String hostname = getWiFiName();
|
||||
hostname.replace(" ", "-");
|
||||
WiFi.setHostname(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;
|
||||
}
|
||||
}
|
||||
|
||||
WiFi.begin(configStore.wifiSSID, configStore.wifiPass);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
connectNetRetries = WIFI_CLOUD_MAX_RETRIES;
|
||||
BlynkState::set(MODE_CONNECTING_CLOUD);
|
||||
} else if (--connectNetRetries <= 0) {
|
||||
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()) &&
|
||||
(WiFi.status() == WL_CONNECTED) &&
|
||||
(!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); // TODO: retry after timeout
|
||||
} else if (WiFi.status() != WL_CONNECTED) {
|
||||
BlynkState::set(MODE_CONNECTING_NET);
|
||||
} else if (Blynk.connected()) {
|
||||
BlynkState::set(MODE_RUNNING);
|
||||
connectBlynkRetries = WIFI_CLOUD_MAX_RETRIES;
|
||||
|
||||
if (!configStore.getFlag(CONFIG_FLAG_VALID)) {
|
||||
configStore.last_error = BLYNK_PROV_ERR_NONE;
|
||||
configStore.setFlag(CONFIG_FLAG_VALID, true);
|
||||
config_save();
|
||||
|
||||
Blynk.sendInternal("meta", "set", "Hotspot Name", getWiFiName());
|
||||
}
|
||||
} else if (--connectBlynkRetries <= 0) {
|
||||
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();
|
||||
}
|
||||
|
||||
154
libraries/Blynk/examples/Blynk.Edgent/Edgent_ESP32/ConfigStore.h
Normal file
154
libraries/Blynk/examples/Blynk.Edgent/Edgent_ESP32/ConfigStore.h
Normal file
@@ -0,0 +1,154 @@
|
||||
|
||||
#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 <Preferences.h>
|
||||
|
||||
void config_load()
|
||||
{
|
||||
Preferences prefs;
|
||||
if (prefs.begin("blynk", true)) { // read-only
|
||||
memset(&configStore, 0, sizeof(configStore));
|
||||
prefs.getBytes("config", &configStore, sizeof(configStore));
|
||||
if (configStore.magic != configDefault.magic) {
|
||||
DEBUG_PRINT("Using default config.");
|
||||
configStore = configDefault;
|
||||
}
|
||||
} else {
|
||||
DEBUG_PRINT("Config read failed");
|
||||
}
|
||||
}
|
||||
|
||||
bool config_save()
|
||||
{
|
||||
Preferences prefs;
|
||||
if (prefs.begin("blynk", false)) { // writeable
|
||||
prefs.putBytes("config", &configStore, sizeof(configStore));
|
||||
DEBUG_PRINT("Configuration stored to flash");
|
||||
return true;
|
||||
} else {
|
||||
DEBUG_PRINT("Config write failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool config_init()
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
225
libraries/Blynk/examples/Blynk.Edgent/Edgent_ESP32/Console.h
Normal file
225
libraries/Blynk/examples/Blynk.Edgent/Edgent_ESP32/Console.h
Normal file
@@ -0,0 +1,225 @@
|
||||
|
||||
#include <Blynk/BlynkConsole.h>
|
||||
|
||||
extern "C" {
|
||||
#include "esp_partition.h"
|
||||
#include "esp_ota_ops.h"
|
||||
}
|
||||
|
||||
BlynkConsole edgentConsole;
|
||||
|
||||
void console_init()
|
||||
{
|
||||
#ifdef BLYNK_PRINT
|
||||
edgentConsole.begin(BLYNK_PRINT);
|
||||
#endif
|
||||
|
||||
edgentConsole.print("\n>");
|
||||
|
||||
edgentConsole.addCommand("reboot", []() {
|
||||
edgentConsole.print(R"json({"status":"OK","msg":"rebooting wifi module"})json" "\n");
|
||||
delay(100);
|
||||
restartMCU();
|
||||
});
|
||||
|
||||
edgentConsole.addCommand("config", [](int argc, const char** argv) {
|
||||
if (argc < 1 || 0 == strcmp(argv[0], "start")) {
|
||||
BlynkState::set(MODE_WAIT_CONFIG);
|
||||
} else if (0 == strcmp(argv[0], "erase")) {
|
||||
BlynkState::set(MODE_RESET_CONFIG);
|
||||
}
|
||||
});
|
||||
|
||||
edgentConsole.addCommand("devinfo", []() {
|
||||
edgentConsole.printf(
|
||||
R"json({"name":"%s","board":"%s","tmpl_id":"%s","fw_type":"%s","fw_ver":"%s"})json" "\n",
|
||||
getWiFiName().c_str(),
|
||||
BLYNK_TEMPLATE_NAME,
|
||||
BLYNK_TEMPLATE_ID,
|
||||
BLYNK_FIRMWARE_TYPE,
|
||||
BLYNK_FIRMWARE_VERSION
|
||||
);
|
||||
});
|
||||
|
||||
edgentConsole.addCommand("connect", [](int argc, const char** argv) {
|
||||
if (argc < 2) {
|
||||
edgentConsole.print(R"json({"status":"error","msg":"invalid arguments. expected: <auth> <ssid> <pass>"})json" "\n");
|
||||
return;
|
||||
}
|
||||
String auth = argv[0];
|
||||
String ssid = argv[1];
|
||||
String pass = (argc >= 3) ? argv[2] : "";
|
||||
|
||||
if (auth.length() != 32) {
|
||||
edgentConsole.print(R"json({"status":"error","msg":"invalid token size"})json" "\n");
|
||||
return;
|
||||
}
|
||||
|
||||
edgentConsole.print(R"json({"status":"OK","msg":"trying to connect..."})json" "\n");
|
||||
|
||||
configStore = configDefault;
|
||||
CopyString(ssid, configStore.wifiSSID);
|
||||
CopyString(pass, configStore.wifiPass);
|
||||
CopyString(auth, configStore.cloudToken);
|
||||
|
||||
BlynkState::set(MODE_SWITCH_TO_STA);
|
||||
});
|
||||
|
||||
edgentConsole.addCommand("wifi", [](int argc, const char* argv[]) {
|
||||
if (argc < 1 || 0 == strcmp(argv[0], "show")) {
|
||||
edgentConsole.printf(
|
||||
"mac:%s ip:%s (%s [%s] %ddBm)\n",
|
||||
getWiFiMacAddress().c_str(),
|
||||
WiFi.localIP().toString().c_str(),
|
||||
getWiFiNetworkSSID().c_str(),
|
||||
getWiFiNetworkBSSID().c_str(),
|
||||
WiFi.RSSI()
|
||||
);
|
||||
} else if (0 == strcmp(argv[0], "scan")) {
|
||||
int found = WiFi.scanNetworks();
|
||||
for (int i = 0; i < found; i++) {
|
||||
bool current = (WiFi.SSID(i) == WiFi.SSID());
|
||||
edgentConsole.printf(
|
||||
"%s %s [%s] %s ch:%d rssi:%d\n",
|
||||
(current ? "*" : " "), WiFi.SSID(i).c_str(),
|
||||
macToString(WiFi.BSSID(i)).c_str(),
|
||||
wifiSecToStr(WiFi.encryptionType(i)),
|
||||
WiFi.channel(i), WiFi.RSSI(i)
|
||||
);
|
||||
}
|
||||
WiFi.scanDelete();
|
||||
}
|
||||
});
|
||||
|
||||
edgentConsole.addCommand("firmware", [](int argc, const char** argv) {
|
||||
if (argc < 1 || 0 == strcmp(argv[0], "info")) {
|
||||
unsigned sketchSize = ESP.getSketchSize();
|
||||
|
||||
edgentConsole.printf(" Version: %s (build %s)\n", BLYNK_FIRMWARE_VERSION, __DATE__ " " __TIME__);
|
||||
edgentConsole.printf(" Type: %s\n", BLYNK_FIRMWARE_TYPE);
|
||||
edgentConsole.printf(" Platform: %s\n", BLYNK_INFO_DEVICE);
|
||||
edgentConsole.printf(" SDK: %s\n", ESP.getSdkVersion());
|
||||
|
||||
if (const esp_partition_t* running = esp_ota_get_running_partition()) {
|
||||
edgentConsole.printf(" Partition: %s (%dK)\n", running->label, running->size / 1024);
|
||||
edgentConsole.printf(" App size: %dK (%d%%)\n", sketchSize/1024, (sketchSize*100)/(running->size));
|
||||
edgentConsole.printf(" App MD5: %s\n", ESP.getSketchMD5().c_str());
|
||||
}
|
||||
|
||||
} else if (0 == strcmp(argv[0], "rollback")) {
|
||||
if (Update.rollBack()) {
|
||||
edgentConsole.print(R"json({"status":"ok"})json" "\n");
|
||||
edgentTimer.setTimeout(50, restartMCU);
|
||||
} else {
|
||||
edgentConsole.print(R"json({"status":"error"})json" "\n");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
edgentConsole.addCommand("status", [](int argc, const char** argv) {
|
||||
const int64_t t = esp_timer_get_time() / 1000000;
|
||||
unsigned secs = t % BLYNK_SECS_PER_MIN;
|
||||
unsigned mins = (t / BLYNK_SECS_PER_MIN) % BLYNK_SECS_PER_MIN;
|
||||
unsigned hrs = (t % BLYNK_SECS_PER_DAY) / BLYNK_SECS_PER_HOUR;
|
||||
unsigned days = t / BLYNK_SECS_PER_DAY;
|
||||
|
||||
edgentConsole.printf(" Uptime: %dd %dh %dm %ds\n", days, hrs, mins, secs);
|
||||
edgentConsole.printf(" Chip: %s rev %d\n", ESP.getChipModel(), ESP.getChipRevision());
|
||||
edgentConsole.printf(" Flash: %dK\n", ESP.getFlashChipSize() / 1024);
|
||||
edgentConsole.printf(" Stack unused: %d\n", uxTaskGetStackHighWaterMark(NULL));
|
||||
edgentConsole.printf(" Heap free: %d / %d\n", ESP.getFreeHeap(), ESP.getHeapSize());
|
||||
edgentConsole.printf(" max alloc: %d\n", ESP.getMaxAllocHeap());
|
||||
edgentConsole.printf(" min free: %d\n", ESP.getMinFreeHeap());
|
||||
if (ESP.getPsramSize()) {
|
||||
edgentConsole.printf(" PSRAM free: %d / %d\n", ESP.getFreePsram(), ESP.getPsramSize());
|
||||
}
|
||||
#ifdef BLYNK_FS
|
||||
uint32_t fs_total = BLYNK_FS.totalBytes();
|
||||
edgentConsole.printf(" FS free: %d / %d\n", (fs_total-BLYNK_FS.usedBytes()), fs_total);
|
||||
#endif
|
||||
});
|
||||
|
||||
#ifdef BLYNK_FS
|
||||
|
||||
edgentConsole.addCommand("ls", [](int argc, const char** argv) {
|
||||
const char* path = (argc < 1) ? "/" : argv[0];
|
||||
File rootDir = BLYNK_FS.open(path);
|
||||
while (File f = rootDir.openNextFile()) {
|
||||
#if defined(BLYNK_USE_SPIFFS) && (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 0, 0))
|
||||
String fn = f.name();
|
||||
#else
|
||||
String fn = f.path();
|
||||
#endif
|
||||
|
||||
MD5Builder md5;
|
||||
md5.begin();
|
||||
md5.addStream(f, f.size());
|
||||
md5.calculate();
|
||||
String md5str = md5.toString();
|
||||
|
||||
edgentConsole.printf("%8d %-24s %s\n",
|
||||
f.size(), fn.c_str(),
|
||||
md5str.substring(0,8).c_str());
|
||||
}
|
||||
});
|
||||
|
||||
edgentConsole.addCommand("rm", [](int argc, const char** argv) {
|
||||
if (argc < 1) return;
|
||||
|
||||
for (int i=0; i<argc; i++) {
|
||||
const char* fn = argv[i];
|
||||
if (BLYNK_FS.remove(fn)) {
|
||||
edgentConsole.printf("Removed %s\n", fn);
|
||||
} else {
|
||||
edgentConsole.printf("Removing %s failed\n", fn);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
edgentConsole.addCommand("mv", [](int argc, const char** argv) {
|
||||
if (argc != 2) return;
|
||||
|
||||
if (!BLYNK_FS.rename(argv[0], argv[1])) {
|
||||
edgentConsole.print("Rename failed\n");
|
||||
}
|
||||
});
|
||||
|
||||
edgentConsole.addCommand("cat", [](int argc, const char** argv) {
|
||||
if (argc != 1) return;
|
||||
|
||||
if (!BLYNK_FS.exists(argv[0])) {
|
||||
edgentConsole.print("File not found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (File f = BLYNK_FS.open(argv[0], FILE_READ)) {
|
||||
while (f.available()) {
|
||||
edgentConsole.print((char)f.read());
|
||||
}
|
||||
edgentConsole.print("\n");
|
||||
} else {
|
||||
edgentConsole.print("Cannot open file\n");
|
||||
}
|
||||
});
|
||||
|
||||
edgentConsole.addCommand("echo", [](int argc, const char** argv) {
|
||||
if (argc != 2) return;
|
||||
|
||||
if (File f = BLYNK_FS.open(argv[1], FILE_WRITE)) {
|
||||
if (!f.print(argv[0])) {
|
||||
edgentConsole.print("Cannot write file\n");
|
||||
}
|
||||
} else {
|
||||
edgentConsole.print("Cannot open file\n");
|
||||
}
|
||||
});
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
BLYNK_WRITE(InternalPinDBG) {
|
||||
String cmd = String(param.asStr()) + "\n";
|
||||
edgentConsole.runCommand((char*)cmd.c_str());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*************************************************************
|
||||
Blynk is a platform with iOS and Android apps to control
|
||||
ESP32, Arduino, Raspberry Pi and the likes over the Internet.
|
||||
You can easily build mobile and web interfaces for any
|
||||
projects by simply dragging and dropping widgets.
|
||||
|
||||
Downloads, docs, tutorials: https://www.blynk.io
|
||||
Sketch generator: https://examples.blynk.cc
|
||||
Blynk community: https://community.blynk.cc
|
||||
Follow us: https://www.fb.com/blynkapp
|
||||
https://twitter.com/blynk_app
|
||||
|
||||
Blynk library is licensed under MIT license
|
||||
*************************************************************
|
||||
Blynk.Edgent implements:
|
||||
- Blynk.Inject - Dynamic WiFi credentials provisioning
|
||||
- Blynk.Air - Over The Air firmware updates
|
||||
- Device state indication using a physical LED
|
||||
- Credentials reset using a physical Button
|
||||
*************************************************************/
|
||||
|
||||
/* Fill in information from your Blynk Template here */
|
||||
/* Read more: https://bit.ly/BlynkInject */
|
||||
//#define BLYNK_TEMPLATE_ID "TMPxxxxxx"
|
||||
//#define BLYNK_TEMPLATE_NAME "Device"
|
||||
|
||||
#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_ESP32_DEV_MODULE
|
||||
//#define USE_ESP32C3_DEV_MODULE
|
||||
//#define USE_ESP32S2_DEV_KIT
|
||||
//#define USE_WROVER_BOARD
|
||||
//#define USE_TTGO_T7
|
||||
//#define USE_TTGO_T_OI
|
||||
|
||||
#include "BlynkEdgent.h"
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
delay(100);
|
||||
|
||||
BlynkEdgent.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
BlynkEdgent.run();
|
||||
}
|
||||
|
||||
311
libraries/Blynk/examples/Blynk.Edgent/Edgent_ESP32/Indicator.h
Normal file
311
libraries/Blynk/examples/Blynk.Edgent/Edgent_ESP32/Indicator.h
Normal file
@@ -0,0 +1,311 @@
|
||||
|
||||
#if defined(BOARD_LED_PIN_WS2812)
|
||||
#include <Adafruit_NeoPixel.h> // Library: https://github.com/adafruit/Adafruit_NeoPixel
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const long t = millis();
|
||||
if (g_buttonPressed) {
|
||||
if (t - g_buttonPressTime > BUTTON_HOLD_TIME_ACTION) { return beatLED(COLOR_WHITE, (int[]){ 100, 100 }); }
|
||||
if (t - 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() {
|
||||
ledcAttachPin(BOARD_LED_PIN_R, BOARD_LEDC_CHANNEL_1);
|
||||
ledcAttachPin(BOARD_LED_PIN_G, BOARD_LEDC_CHANNEL_2);
|
||||
ledcAttachPin(BOARD_LED_PIN_B, BOARD_LEDC_CHANNEL_3);
|
||||
|
||||
ledcSetup(BOARD_LEDC_CHANNEL_1, BOARD_LEDC_BASE_FREQ, BOARD_LEDC_TIMER_BITS);
|
||||
ledcSetup(BOARD_LEDC_CHANNEL_2, BOARD_LEDC_BASE_FREQ, BOARD_LEDC_TIMER_BITS);
|
||||
ledcSetup(BOARD_LEDC_CHANNEL_3, BOARD_LEDC_BASE_FREQ, BOARD_LEDC_TIMER_BITS);
|
||||
}
|
||||
|
||||
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
|
||||
ledcWrite(BOARD_LEDC_CHANNEL_1, TO_PWM(255 - r));
|
||||
ledcWrite(BOARD_LEDC_CHANNEL_2, TO_PWM(255 - g));
|
||||
ledcWrite(BOARD_LEDC_CHANNEL_3, TO_PWM(255 - b));
|
||||
#else
|
||||
ledcWrite(BOARD_LEDC_CHANNEL_1, TO_PWM(r));
|
||||
ledcWrite(BOARD_LEDC_CHANNEL_2, TO_PWM(g));
|
||||
ledcWrite(BOARD_LEDC_CHANNEL_3, TO_PWM(b));
|
||||
#endif
|
||||
}
|
||||
|
||||
#elif defined(BOARD_LED_PIN) // Single color LED
|
||||
|
||||
void initLED() {
|
||||
ledcSetup(BOARD_LEDC_CHANNEL_1, BOARD_LEDC_BASE_FREQ, BOARD_LEDC_TIMER_BITS);
|
||||
ledcAttachPin(BOARD_LED_PIN, BOARD_LEDC_CHANNEL_1);
|
||||
}
|
||||
|
||||
void setLED(uint32_t color) {
|
||||
#if BOARD_LED_INVERSE
|
||||
ledcWrite(BOARD_LEDC_CHANNEL_1, TO_PWM(255 - color));
|
||||
#else
|
||||
ledcWrite(BOARD_LEDC_CHANNEL_1, 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<typename T>
|
||||
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<typename T>
|
||||
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: https://github.com/michael71/Timer5
|
||||
|
||||
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
|
||||
|
||||
91
libraries/Blynk/examples/Blynk.Edgent/Edgent_ESP32/OTA.h
Normal file
91
libraries/Blynk/examples/Blynk.Edgent/Edgent_ESP32/OTA.h
Normal file
@@ -0,0 +1,91 @@
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <Update.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
void enterOTA() {
|
||||
BlynkState::set(MODE_OTA_UPGRADE);
|
||||
|
||||
DEBUG_PRINT(String("Firmware update URL: ") + overTheAirURL);
|
||||
|
||||
HTTPClient http;
|
||||
http.begin(overTheAirURL);
|
||||
|
||||
const char* headerkeys[] = { "x-MD5" };
|
||||
http.collectHeaders(headerkeys, sizeof(headerkeys)/sizeof(char*));
|
||||
|
||||
int httpCode = http.GET();
|
||||
if (httpCode != HTTP_CODE_OK) {
|
||||
DEBUG_PRINT("HTTP response should be 200");
|
||||
BlynkState::set(MODE_ERROR);
|
||||
return;
|
||||
}
|
||||
int contentLength = http.getSize();
|
||||
if (contentLength <= 0) {
|
||||
DEBUG_PRINT("Content-Length not defined");
|
||||
BlynkState::set(MODE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
bool canBegin = Update.begin(contentLength);
|
||||
if (!canBegin) {
|
||||
DEBUG_PRINT("Not enough space to begin OTA");
|
||||
BlynkState::set(MODE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (http.hasHeader("x-MD5")) {
|
||||
String md5 = http.header("x-MD5");
|
||||
if (md5.length() == 32) {
|
||||
md5.toLowerCase();
|
||||
DEBUG_PRINT("Expected MD5: " + md5);
|
||||
Update.setMD5(md5.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef BLYNK_FS
|
||||
BLYNK_FS.end();
|
||||
#endif
|
||||
|
||||
Client& client = http.getStream();
|
||||
int written = Update.writeStream(client);
|
||||
if (written != contentLength) {
|
||||
DEBUG_PRINT(String("OTA written ") + written + " / " + contentLength + " bytes");
|
||||
BlynkState::set(MODE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Update.end()) {
|
||||
DEBUG_PRINT("Error #" + String(Update.getError()));
|
||||
BlynkState::set(MODE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Update.isFinished()) {
|
||||
DEBUG_PRINT("Update failed.");
|
||||
BlynkState::set(MODE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_PRINT("=== Update successfully completed. Rebooting.");
|
||||
restartMCU();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
#ifdef BOARD_BUTTON_PIN
|
||||
|
||||
volatile bool g_buttonPressed = false;
|
||||
volatile uint32_t g_buttonPressTime = -1;
|
||||
|
||||
void button_action(void)
|
||||
{
|
||||
BlynkState::set(MODE_RESET_CONFIG);
|
||||
}
|
||||
|
||||
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 if (buttonHoldTime >= BUTTON_PRESS_TIME_ACTION) {
|
||||
// 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_PULLDOWN);
|
||||
#endif
|
||||
attachInterrupt(BOARD_BUTTON_PIN, button_change, CHANGE);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define g_buttonPressed false
|
||||
#define g_buttonPressTime 0
|
||||
|
||||
void button_init() {}
|
||||
|
||||
#endif
|
||||
133
libraries/Blynk/examples/Blynk.Edgent/Edgent_ESP32/Settings.h
Normal file
133
libraries/Blynk/examples/Blynk.Edgent/Edgent_ESP32/Settings.h
Normal file
@@ -0,0 +1,133 @@
|
||||
|
||||
/*
|
||||
* Board configuration (see examples below).
|
||||
*/
|
||||
|
||||
#if defined(USE_WROVER_BOARD)
|
||||
|
||||
#define BOARD_BUTTON_PIN 15
|
||||
#define BOARD_BUTTON_ACTIVE_LOW true
|
||||
|
||||
#define BOARD_LED_PIN_R 0
|
||||
#define BOARD_LED_PIN_G 2
|
||||
#define BOARD_LED_PIN_B 4
|
||||
#define BOARD_LED_INVERSE false
|
||||
#define BOARD_LED_BRIGHTNESS 128
|
||||
|
||||
#elif defined(USE_TTGO_T7)
|
||||
|
||||
#warning "This board does not have a button. Connect a button to gpio0 <> GND"
|
||||
|
||||
#define BOARD_BUTTON_PIN 0
|
||||
#define BOARD_BUTTON_ACTIVE_LOW true
|
||||
|
||||
#define BOARD_LED_PIN 19
|
||||
#define BOARD_LED_INVERSE false
|
||||
#define BOARD_LED_BRIGHTNESS 64
|
||||
|
||||
#elif defined(USE_TTGO_T_OI)
|
||||
|
||||
#warning "This board does not have a button. Connect a button to gpio0 <> GND"
|
||||
|
||||
#define BOARD_BUTTON_PIN 0
|
||||
#define BOARD_BUTTON_ACTIVE_LOW true
|
||||
|
||||
#define BOARD_LED_PIN 3
|
||||
#define BOARD_LED_INVERSE false
|
||||
#define BOARD_LED_BRIGHTNESS 64
|
||||
|
||||
#elif defined(USE_ESP32_DEV_MODULE)
|
||||
|
||||
#warning "The LED of this board is not configured"
|
||||
|
||||
#define BOARD_BUTTON_PIN 0
|
||||
#define BOARD_BUTTON_ACTIVE_LOW true
|
||||
|
||||
#elif defined(USE_ESP32C3_DEV_MODULE)
|
||||
|
||||
#define BOARD_BUTTON_PIN 9
|
||||
#define BOARD_BUTTON_ACTIVE_LOW true
|
||||
|
||||
#define BOARD_LED_PIN_WS2812 8
|
||||
#define BOARD_LED_INVERSE false
|
||||
#define BOARD_LED_BRIGHTNESS 32
|
||||
|
||||
#elif defined(USE_ESP32S2_DEV_KIT)
|
||||
|
||||
#define BOARD_BUTTON_PIN 0
|
||||
#define BOARD_BUTTON_ACTIVE_LOW true
|
||||
|
||||
#define BOARD_LED_PIN 19
|
||||
#define BOARD_LED_INVERSE false
|
||||
#define BOARD_LED_BRIGHTNESS 128
|
||||
|
||||
#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 BUTTON_PRESS_TIME_ACTION 50
|
||||
|
||||
#define BOARD_PWM_MAX 1023
|
||||
|
||||
#define BOARD_LEDC_CHANNEL_1 1
|
||||
#define BOARD_LEDC_CHANNEL_2 2
|
||||
#define BOARD_LEDC_CHANNEL_3 3
|
||||
#define BOARD_LEDC_TIMER_BITS 10
|
||||
#define BOARD_LEDC_BASE_FREQ 12000
|
||||
|
||||
#if !defined(CONFIG_DEVICE_PREFIX)
|
||||
#define CONFIG_DEVICE_PREFIX "Blynk"
|
||||
#endif
|
||||
#if !defined(CONFIG_AP_URL)
|
||||
#define CONFIG_AP_URL "blynk.setup"
|
||||
#endif
|
||||
#if !defined(CONFIG_DEFAULT_SERVER)
|
||||
#define CONFIG_DEFAULT_SERVER "blynk.cloud"
|
||||
#endif
|
||||
#if !defined(CONFIG_DEFAULT_PORT)
|
||||
#define CONFIG_DEFAULT_PORT 443
|
||||
#endif
|
||||
|
||||
#define WIFI_CLOUD_MAX_RETRIES 500
|
||||
#define WIFI_NET_CONNECT_TIMEOUT 50000
|
||||
#define WIFI_CLOUD_CONNECT_TIMEOUT 50000
|
||||
#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__)
|
||||
#define DEBUG_PRINTF(...) BLYNK_LOG(__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_PRINT(...)
|
||||
#define DEBUG_PRINTF(...)
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
|
||||
extern "C" {
|
||||
#include "user_interface.h"
|
||||
|
||||
void app_loop();
|
||||
void restartMCU();
|
||||
}
|
||||
|
||||
#include "Settings.h"
|
||||
#include <BlynkSimpleEsp8266_SSL.h>
|
||||
|
||||
#if defined(BLYNK_USE_LITTLEFS)
|
||||
#include <LittleFS.h>
|
||||
#define BLYNK_FS LittleFS
|
||||
#elif defined(BLYNK_USE_SPIFFS)
|
||||
#if defined(ESP32)
|
||||
#include <SPIFFS.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <FS.h>
|
||||
#endif
|
||||
#define BLYNK_FS SPIFFS
|
||||
#endif
|
||||
#if defined(BLYNK_FS) && defined(ESP8266)
|
||||
#define BLYNK_FILE_READ "r"
|
||||
#define BLYNK_FILE_WRITE "w"
|
||||
#endif
|
||||
|
||||
#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_NAME) && defined(BLYNK_DEVICE_NAME)
|
||||
#define BLYNK_TEMPLATE_NAME BLYNK_DEVICE_NAME
|
||||
#endif
|
||||
|
||||
#if !defined(BLYNK_TEMPLATE_ID) || !defined(BLYNK_TEMPLATE_NAME)
|
||||
#error "Please specify your BLYNK_TEMPLATE_ID and BLYNK_TEMPLATE_NAME"
|
||||
#endif
|
||||
|
||||
#if defined(BLYNK_AUTH_TOKEN)
|
||||
#error "BLYNK_AUTH_TOKEN is assigned automatically when using Blynk.Edgent, please remove it from the configuration"
|
||||
#endif
|
||||
|
||||
BlynkTimer edgentTimer;
|
||||
|
||||
#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()
|
||||
{
|
||||
#ifdef BLYNK_PRINT
|
||||
Blynk.printBanner();
|
||||
BLYNK_PRINT.println("----------------------------------------------------");
|
||||
BLYNK_PRINT.print(" Device: "); BLYNK_PRINT.println(getWiFiName());
|
||||
BLYNK_PRINT.print(" Firmware: "); BLYNK_PRINT.println(BLYNK_FIRMWARE_VERSION " (build " __DATE__ " " __TIME__ ")");
|
||||
if (configStore.getFlag(CONFIG_FLAG_VALID)) {
|
||||
BLYNK_PRINT.print(" Token: ");
|
||||
BLYNK_PRINT.println(String(configStore.cloudToken).substring(0,4) +
|
||||
" - •••• - •••• - ••••");
|
||||
}
|
||||
BLYNK_PRINT.print(" Platform: "); BLYNK_PRINT.println(String(BLYNK_INFO_DEVICE) + " @ " + ESP.getCpuFreqMHz() + "MHz");
|
||||
BLYNK_PRINT.print(" Boot ver: "); BLYNK_PRINT.println(ESP.getBootVersion());
|
||||
BLYNK_PRINT.print(" SDK: "); BLYNK_PRINT.println(ESP.getSdkVersion());
|
||||
BLYNK_PRINT.print(" ESP Core: "); BLYNK_PRINT.println(ESP.getCoreVersion());
|
||||
BLYNK_PRINT.print(" Flash: "); BLYNK_PRINT.println(String(ESP.getFlashChipSize() / 1024) + "K");
|
||||
BLYNK_PRINT.print(" Free mem: "); BLYNK_PRINT.println(ESP.getFreeHeap());
|
||||
BLYNK_PRINT.println("----------------------------------------------------");
|
||||
#endif
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
|
||||
#ifdef BLYNK_FS
|
||||
BLYNK_FS.begin();
|
||||
#endif
|
||||
|
||||
indicator_init();
|
||||
button_init();
|
||||
config_init();
|
||||
printDeviceBanner();
|
||||
console_init();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (!String(BLYNK_TEMPLATE_ID).startsWith("TMPL") ||
|
||||
!strlen(BLYNK_TEMPLATE_NAME)
|
||||
) {
|
||||
DEBUG_PRINT("Invalid configuration of TEMPLATE_ID / TEMPLATE_NAME");
|
||||
while (true) { delay(100); }
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
} BlynkEdgent;
|
||||
|
||||
void app_loop() {
|
||||
edgentTimer.run();
|
||||
edgentConsole.run();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,504 @@
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <ESP8266HTTPUpdateServer.h>
|
||||
#include <DNSServer.h>
|
||||
|
||||
#ifndef BLYNK_FS
|
||||
|
||||
const char* config_form = R"html(
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>WiFi setup</title>
|
||||
<style>
|
||||
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%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="centered">
|
||||
<form method="get" action="config">
|
||||
<table>
|
||||
<tr><td><label for="ssid">WiFi SSID:</label></td> <td><input type="text" name="ssid" length=64 required="required"></td></tr>
|
||||
<tr><td><label for="pass">Password:</label></td> <td><input type="text" name="pass" length=64></td></tr>
|
||||
<tr><td><label for="blynk">Auth token:</label></td><td><input type="text" name="blynk" placeholder="a0b1c2d..." pattern="[-_a-zA-Z0-9]{32}" maxlength="32" required="required"></td></tr>
|
||||
<tr><td><label for="host">Host:</label></td> <td><input type="text" name="host" value="blynk.cloud" length=64></td></tr>
|
||||
<tr><td><label for="port_ssl">Port:</label></td> <td><input type="number" name="port_ssl" value="443" min="1" max="65535"></td></tr>
|
||||
</table><br/>
|
||||
<input type="submit" value="Apply">
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
)html";
|
||||
|
||||
#endif
|
||||
|
||||
ESP8266WebServer server(80);
|
||||
ESP8266HTTPUpdateServer httpUpdater;
|
||||
DNSServer dnsServer;
|
||||
const byte DNS_PORT = 53;
|
||||
|
||||
static int connectNetRetries = WIFI_CLOUD_MAX_RETRIES;
|
||||
static int connectBlynkRetries = WIFI_CLOUD_MAX_RETRIES;
|
||||
|
||||
void restartMCU() {
|
||||
ESP.restart();
|
||||
ESP.reset();
|
||||
while(1) {};
|
||||
}
|
||||
|
||||
static
|
||||
String encodeUniquePart(uint32_t n, unsigned len)
|
||||
{
|
||||
static constexpr char alphabet[] = { "0W8N4Y1HP5DF9K6JM3C2UA7R" };
|
||||
static constexpr int base = sizeof(alphabet)-1;
|
||||
|
||||
char buf[16] = { 0, };
|
||||
char prev = 0;
|
||||
for (unsigned i = 0; i < len; n /= base) {
|
||||
char c = alphabet[n % base];
|
||||
if (c == prev) {
|
||||
c = alphabet[(n+1) % base];
|
||||
}
|
||||
prev = buf[i++] = c;
|
||||
}
|
||||
return String(buf);
|
||||
}
|
||||
|
||||
static
|
||||
String getWiFiName(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);
|
||||
}
|
||||
String devUnique = encodeUniquePart(unique, 4);
|
||||
|
||||
String devPrefix = CONFIG_DEVICE_PREFIX;
|
||||
String devName = String(BLYNK_TEMPLATE_NAME).substring(0, 31-6-devPrefix.length());
|
||||
|
||||
if (withPrefix) {
|
||||
return devPrefix + " " + devName + "-" + devUnique;
|
||||
} else {
|
||||
return devName + "-" + devUnique;
|
||||
}
|
||||
}
|
||||
|
||||
static inline
|
||||
String macToString(byte mac[6]) {
|
||||
char buff[20];
|
||||
snprintf(buff, sizeof(buff), "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
return String(buff);
|
||||
}
|
||||
|
||||
static inline
|
||||
const char* wifiSecToStr(uint8_t t) {
|
||||
switch (t) {
|
||||
case ENC_TYPE_NONE: return "OPEN";
|
||||
case ENC_TYPE_WEP: return "WEP";
|
||||
case ENC_TYPE_TKIP: return "WPA";
|
||||
case ENC_TYPE_CCMP: return "WPA2";
|
||||
case ENC_TYPE_AUTO: return "WPA+WPA2";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
String getWiFiMacAddress() {
|
||||
return WiFi.macAddress();
|
||||
}
|
||||
|
||||
static
|
||||
String getWiFiApBSSID() {
|
||||
return WiFi.softAPmacAddress();
|
||||
}
|
||||
|
||||
static
|
||||
String getWiFiNetworkSSID() {
|
||||
return WiFi.SSID();
|
||||
}
|
||||
|
||||
static
|
||||
String getWiFiNetworkBSSID() {
|
||||
return WiFi.BSSIDstr();
|
||||
}
|
||||
|
||||
void enterConfigMode()
|
||||
{
|
||||
WiFi.mode(WIFI_OFF);
|
||||
delay(100);
|
||||
WiFi.mode(WIFI_AP_STA);
|
||||
WiFi.softAPConfig(WIFI_AP_IP, WIFI_AP_IP, WIFI_AP_Subnet);
|
||||
WiFi.softAP(getWiFiName().c_str());
|
||||
delay(500);
|
||||
|
||||
IPAddress myIP = WiFi.softAPIP();
|
||||
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_FS
|
||||
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 forceSave = 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 = configDefault;
|
||||
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 (forceSave) {
|
||||
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);
|
||||
|
||||
connectNetRetries = connectBlynkRetries = 1;
|
||||
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", []() {
|
||||
// Configuring starts with board info request (may impact indication)
|
||||
BlynkState::set(MODE_CONFIGURING);
|
||||
|
||||
DEBUG_PRINT("Sending board info...");
|
||||
const char* tmpl = BLYNK_TEMPLATE_ID;
|
||||
|
||||
char buff[512];
|
||||
snprintf(buff, sizeof(buff),
|
||||
R"json({"board":"%s","tmpl_id":"%s","fw_type":"%s","fw_ver":"%s","ssid":"%s","bssid":"%s","mac":"%s","last_error":%d,"wifi_scan":true,"static_ip":true})json",
|
||||
BLYNK_TEMPLATE_NAME,
|
||||
tmpl ? tmpl : "Unknown",
|
||||
BLYNK_FIRMWARE_TYPE,
|
||||
BLYNK_FIRMWARE_VERSION,
|
||||
getWiFiName().c_str(),
|
||||
getWiFiApBSSID().c_str(),
|
||||
getWiFiMacAddress().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];
|
||||
|
||||
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),
|
||||
wifiSecToStr(WiFi.encryptionType(id)),
|
||||
WiFi.channel(id),
|
||||
WiFi.isHidden(id)
|
||||
);
|
||||
|
||||
server.sendContent(buff);
|
||||
if (i != wifi_nets-1) server.sendContent(",\n");
|
||||
}
|
||||
WiFi.scanDelete();
|
||||
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_FS
|
||||
server.serveStatic("/img", BLYNK_FS, "/img");
|
||||
server.serveStatic("/", BLYNK_FS, "/index.html");
|
||||
#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_CONFIGURING) && WiFi.softAPgetStationNum() == 0) {
|
||||
BlynkState::set(MODE_WAIT_CONFIG);
|
||||
}
|
||||
}
|
||||
|
||||
server.stop();
|
||||
}
|
||||
|
||||
void enterConnectNet() {
|
||||
BlynkState::set(MODE_CONNECTING_NET);
|
||||
DEBUG_PRINT(String("Connecting to WiFi: ") + configStore.wifiSSID);
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
|
||||
String hostname = getWiFiName();
|
||||
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);
|
||||
}
|
||||
|
||||
connectNetRetries = WIFI_CLOUD_MAX_RETRIES;
|
||||
BlynkState::set(MODE_CONNECTING_CLOUD);
|
||||
} else if (--connectNetRetries <= 0) {
|
||||
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()) &&
|
||||
(WiFi.status() == WL_CONNECTED) &&
|
||||
(!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); // TODO: retry after timeout
|
||||
} else if (WiFi.status() != WL_CONNECTED) {
|
||||
BlynkState::set(MODE_CONNECTING_NET);
|
||||
} else if (Blynk.connected()) {
|
||||
BlynkState::set(MODE_RUNNING);
|
||||
connectBlynkRetries = WIFI_CLOUD_MAX_RETRIES;
|
||||
|
||||
if (!configStore.getFlag(CONFIG_FLAG_VALID)) {
|
||||
configStore.last_error = BLYNK_PROV_ERR_NONE;
|
||||
configStore.setFlag(CONFIG_FLAG_VALID, true);
|
||||
config_save();
|
||||
|
||||
Blynk.sendInternal("meta", "set", "Hotspot Name", getWiFiName());
|
||||
}
|
||||
} else if (--connectBlynkRetries <= 0) {
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
|
||||
#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();
|
||||
}
|
||||
}
|
||||
|
||||
209
libraries/Blynk/examples/Blynk.Edgent/Edgent_ESP8266/Console.h
Normal file
209
libraries/Blynk/examples/Blynk.Edgent/Edgent_ESP8266/Console.h
Normal file
@@ -0,0 +1,209 @@
|
||||
|
||||
#include <Blynk/BlynkConsole.h>
|
||||
|
||||
BlynkConsole edgentConsole;
|
||||
|
||||
void console_init()
|
||||
{
|
||||
#ifdef BLYNK_PRINT
|
||||
edgentConsole.begin(BLYNK_PRINT);
|
||||
#endif
|
||||
|
||||
edgentConsole.print("\n>");
|
||||
|
||||
edgentConsole.addCommand("reboot", []() {
|
||||
edgentConsole.print(R"json({"status":"OK","msg":"rebooting wifi module"})json" "\n");
|
||||
delay(100);
|
||||
restartMCU();
|
||||
});
|
||||
|
||||
edgentConsole.addCommand("config", [](int argc, const char** argv) {
|
||||
if (argc < 1 || 0 == strcmp(argv[0], "start")) {
|
||||
BlynkState::set(MODE_WAIT_CONFIG);
|
||||
} else if (0 == strcmp(argv[0], "erase")) {
|
||||
BlynkState::set(MODE_RESET_CONFIG);
|
||||
}
|
||||
});
|
||||
|
||||
edgentConsole.addCommand("devinfo", []() {
|
||||
edgentConsole.printf(
|
||||
R"json({"name":"%s","board":"%s","tmpl_id":"%s","fw_type":"%s","fw_ver":"%s"})json" "\n",
|
||||
getWiFiName().c_str(),
|
||||
BLYNK_TEMPLATE_NAME,
|
||||
BLYNK_TEMPLATE_ID,
|
||||
BLYNK_FIRMWARE_TYPE,
|
||||
BLYNK_FIRMWARE_VERSION
|
||||
);
|
||||
});
|
||||
|
||||
edgentConsole.addCommand("connect", [](int argc, const char** argv) {
|
||||
if (argc < 2) {
|
||||
edgentConsole.print(R"json({"status":"error","msg":"invalid arguments. expected: <auth> <ssid> <pass>"})json" "\n");
|
||||
return;
|
||||
}
|
||||
String auth = argv[0];
|
||||
String ssid = argv[1];
|
||||
String pass = (argc >= 3) ? argv[2] : "";
|
||||
|
||||
if (auth.length() != 32) {
|
||||
edgentConsole.print(R"json({"status":"error","msg":"invalid token size"})json" "\n");
|
||||
return;
|
||||
}
|
||||
|
||||
edgentConsole.print(R"json({"status":"OK","msg":"trying to connect..."})json" "\n");
|
||||
|
||||
configStore = configDefault;
|
||||
CopyString(ssid, configStore.wifiSSID);
|
||||
CopyString(pass, configStore.wifiPass);
|
||||
CopyString(auth, configStore.cloudToken);
|
||||
|
||||
BlynkState::set(MODE_SWITCH_TO_STA);
|
||||
});
|
||||
|
||||
edgentConsole.addCommand("wifi", [](int argc, const char* argv[]) {
|
||||
if (argc < 1 || 0 == strcmp(argv[0], "show")) {
|
||||
edgentConsole.printf(
|
||||
"mac:%s ip:%s (%s [%s] %ddBm)\n",
|
||||
getWiFiMacAddress().c_str(),
|
||||
WiFi.localIP().toString().c_str(),
|
||||
getWiFiNetworkSSID().c_str(),
|
||||
getWiFiNetworkBSSID().c_str(),
|
||||
WiFi.RSSI()
|
||||
);
|
||||
} else if (0 == strcmp(argv[0], "scan")) {
|
||||
int found = WiFi.scanNetworks();
|
||||
for (int i = 0; i < found; i++) {
|
||||
bool current = (WiFi.SSID(i) == WiFi.SSID());
|
||||
edgentConsole.printf(
|
||||
"%s %s [%s] %s ch:%d rssi:%d\n",
|
||||
(current ? "*" : " "), WiFi.SSID(i).c_str(),
|
||||
macToString(WiFi.BSSID(i)).c_str(),
|
||||
wifiSecToStr(WiFi.encryptionType(i)),
|
||||
WiFi.channel(i), WiFi.RSSI(i)
|
||||
);
|
||||
}
|
||||
WiFi.scanDelete();
|
||||
}
|
||||
});
|
||||
|
||||
edgentConsole.addCommand("firmware", [](int argc, const char** argv) {
|
||||
if (argc < 1 || 0 == strcmp(argv[0], "info")) {
|
||||
unsigned sketchSize = ESP.getSketchSize();
|
||||
unsigned partSize = sketchSize + ESP.getFreeSketchSpace();
|
||||
|
||||
edgentConsole.printf(" Version: %s (build %s)\n", BLYNK_FIRMWARE_VERSION, __DATE__ " " __TIME__);
|
||||
edgentConsole.printf(" Type: %s\n", BLYNK_FIRMWARE_TYPE);
|
||||
edgentConsole.printf(" Platform: %s\n", BLYNK_INFO_DEVICE);
|
||||
edgentConsole.printf(" SDK: %s\n", ESP.getSdkVersion());
|
||||
edgentConsole.printf(" ESP Core: %s\n", ESP.getCoreVersion().c_str());
|
||||
|
||||
edgentConsole.printf(" App size: %dK (%d%%)\n", sketchSize/1024, (sketchSize*100)/partSize);
|
||||
edgentConsole.printf(" App MD5: %s\n", ESP.getSketchMD5().c_str());
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
edgentConsole.addCommand("status", [](int argc, const char** argv) {
|
||||
const uint64_t t = micros64() / 1000000;
|
||||
unsigned secs = t % BLYNK_SECS_PER_MIN;
|
||||
unsigned mins = (t / BLYNK_SECS_PER_MIN) % BLYNK_SECS_PER_MIN;
|
||||
unsigned hrs = (t % BLYNK_SECS_PER_DAY) / BLYNK_SECS_PER_HOUR;
|
||||
unsigned days = t / BLYNK_SECS_PER_DAY;
|
||||
|
||||
uint32_t heap_free; uint16_t heap_max;
|
||||
uint8_t heap_frag;
|
||||
ESP.getHeapStats(&heap_free, &heap_max, &heap_frag);
|
||||
edgentConsole.printf(" Uptime: %dd %dh %dm %ds\n", days, hrs, mins, secs);
|
||||
edgentConsole.printf(" Reset reason: %s\n", ESP.getResetReason().c_str());
|
||||
edgentConsole.printf(" Flash: %dK\n", ESP.getFlashChipSize() / 1024);
|
||||
edgentConsole.printf(" Stack unused: %d\n", ESP.getFreeContStack());
|
||||
edgentConsole.printf(" Heap free: %d / %d\n", heap_free, heap_max);
|
||||
edgentConsole.printf(" fragment: %d\n", heap_frag);
|
||||
edgentConsole.printf(" max alloc: %d\n", ESP.getMaxFreeBlockSize());
|
||||
#ifdef BLYNK_FS
|
||||
FSInfo fs_info;
|
||||
BLYNK_FS.info(fs_info);
|
||||
edgentConsole.printf(" FS free: %d / %d\n", (fs_info.totalBytes-fs_info.usedBytes), fs_info.totalBytes);
|
||||
#endif
|
||||
});
|
||||
|
||||
#ifdef BLYNK_FS
|
||||
|
||||
edgentConsole.addCommand("ls", [](int argc, const char** argv) {
|
||||
const char* path = (argc < 1) ? "/" : argv[0];
|
||||
Dir dir = BLYNK_FS.openDir(path);
|
||||
while (dir.next()) {
|
||||
File f = dir.openFile(BLYNK_FILE_READ);
|
||||
|
||||
MD5Builder md5;
|
||||
md5.begin();
|
||||
md5.addStream(f, f.size());
|
||||
md5.calculate();
|
||||
String md5str = md5.toString();
|
||||
|
||||
edgentConsole.printf("%8d %-24s %s\n",
|
||||
f.size(), dir.fileName().c_str(),
|
||||
md5str.substring(0,8).c_str());
|
||||
}
|
||||
});
|
||||
|
||||
edgentConsole.addCommand("rm", [](int argc, const char** argv) {
|
||||
if (argc < 1) return;
|
||||
|
||||
for (int i=0; i<argc; i++) {
|
||||
const char* fn = argv[i];
|
||||
if (BLYNK_FS.remove(fn)) {
|
||||
edgentConsole.printf("Removed %s\n", fn);
|
||||
} else {
|
||||
edgentConsole.printf("Removing %s failed\n", fn);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
edgentConsole.addCommand("mv", [](int argc, const char** argv) {
|
||||
if (argc != 2) return;
|
||||
|
||||
if (!BLYNK_FS.rename(argv[0], argv[1])) {
|
||||
edgentConsole.print("Rename failed\n");
|
||||
}
|
||||
});
|
||||
|
||||
edgentConsole.addCommand("cat", [](int argc, const char** argv) {
|
||||
if (argc != 1) return;
|
||||
|
||||
if (!BLYNK_FS.exists(argv[0])) {
|
||||
edgentConsole.print("File not found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (File f = BLYNK_FS.open(argv[0], BLYNK_FILE_READ)) {
|
||||
while (f.available()) {
|
||||
edgentConsole.print((char)f.read());
|
||||
}
|
||||
edgentConsole.print("\n");
|
||||
} else {
|
||||
edgentConsole.print("Cannot open file\n");
|
||||
}
|
||||
});
|
||||
|
||||
edgentConsole.addCommand("echo", [](int argc, const char** argv) {
|
||||
if (argc != 2) return;
|
||||
|
||||
if (File f = BLYNK_FS.open(argv[1], BLYNK_FILE_WRITE)) {
|
||||
if (!f.print(argv[0])) {
|
||||
edgentConsole.print("Cannot write file\n");
|
||||
}
|
||||
} else {
|
||||
edgentConsole.print("Cannot open file\n");
|
||||
}
|
||||
});
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
BLYNK_WRITE(InternalPinDBG) {
|
||||
String cmd = String(param.asStr()) + "\n";
|
||||
edgentConsole.runCommand((char*)cmd.c_str());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*************************************************************
|
||||
Blynk is a platform with iOS and Android apps to control
|
||||
ESP32, Arduino, Raspberry Pi and the likes over the Internet.
|
||||
You can easily build mobile and web interfaces for any
|
||||
projects by simply dragging and dropping widgets.
|
||||
|
||||
Downloads, docs, tutorials: https://www.blynk.io
|
||||
Sketch generator: https://examples.blynk.cc
|
||||
Blynk community: https://community.blynk.cc
|
||||
Follow us: https://www.fb.com/blynkapp
|
||||
https://twitter.com/blynk_app
|
||||
|
||||
Blynk library is licensed under MIT license
|
||||
*************************************************************
|
||||
Blynk.Edgent implements:
|
||||
- Blynk.Inject - Dynamic WiFi credentials provisioning
|
||||
- Blynk.Air - Over The Air firmware updates
|
||||
- Device state indication using a physical LED
|
||||
- Credentials reset using a physical Button
|
||||
*************************************************************/
|
||||
|
||||
/* Fill in information from your Blynk Template here */
|
||||
/* Read more: https://bit.ly/BlynkInject */
|
||||
//#define BLYNK_TEMPLATE_ID "TMPxxxxxx"
|
||||
//#define BLYNK_TEMPLATE_NAME "Device"
|
||||
|
||||
#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);
|
||||
delay(100);
|
||||
|
||||
BlynkEdgent.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
BlynkEdgent.run();
|
||||
}
|
||||
|
||||
306
libraries/Blynk/examples/Blynk.Edgent/Edgent_ESP8266/Indicator.h
Normal file
306
libraries/Blynk/examples/Blynk.Edgent/Edgent_ESP8266/Indicator.h
Normal file
@@ -0,0 +1,306 @@
|
||||
|
||||
#if defined(BOARD_LED_PIN_WS2812)
|
||||
#include <Adafruit_NeoPixel.h> // Library: https://github.com/adafruit/Adafruit_NeoPixel
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const long t = millis();
|
||||
if (g_buttonPressed) {
|
||||
if (t - g_buttonPressTime > BUTTON_HOLD_TIME_ACTION) { return beatLED(COLOR_WHITE, (int[]){ 100, 100 }); }
|
||||
if (t - 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<typename T>
|
||||
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<typename T>
|
||||
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: https://github.com/michael71/Timer5
|
||||
|
||||
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
|
||||
|
||||
266
libraries/Blynk/examples/Blynk.Edgent/Edgent_ESP8266/OTA.h
Normal file
266
libraries/Blynk/examples/Blynk.Edgent/Edgent_ESP8266/OTA.h
Normal file
@@ -0,0 +1,266 @@
|
||||
|
||||
#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) {
|
||||
#ifdef BLYNK_PRINT
|
||||
Update.printError(BLYNK_PRINT);
|
||||
#endif
|
||||
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) {
|
||||
#ifdef BLYNK_PRINT
|
||||
BLYNK_PRINT.print(String("\r ") + progress + "%");
|
||||
#endif
|
||||
prevProgress = progress;
|
||||
}
|
||||
}
|
||||
#ifdef BLYNK_PRINT
|
||||
BLYNK_PRINT.println();
|
||||
#endif
|
||||
client->stop();
|
||||
|
||||
if (written != contentLength) {
|
||||
#ifdef BLYNK_PRINT
|
||||
Update.printError(BLYNK_PRINT);
|
||||
#endif
|
||||
OTA_FATAL(String("Write failed. Written ") + written + " / " + contentLength + " bytes");
|
||||
}
|
||||
|
||||
if (!Update.end()) {
|
||||
#ifdef BLYNK_PRINT
|
||||
Update.printError(BLYNK_PRINT);
|
||||
#endif
|
||||
OTA_FATAL(F("Update not ended"));
|
||||
}
|
||||
|
||||
if (!Update.isFinished()) {
|
||||
OTA_FATAL(F("Update not finished"));
|
||||
}
|
||||
|
||||
DEBUG_PRINT("=== Update successfully completed. Rebooting.");
|
||||
restartMCU();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
|
||||
#ifdef BOARD_BUTTON_PIN
|
||||
|
||||
volatile bool g_buttonPressed = false;
|
||||
volatile uint32_t g_buttonPressTime = -1;
|
||||
|
||||
void button_action(void)
|
||||
{
|
||||
BlynkState::set(MODE_RESET_CONFIG);
|
||||
}
|
||||
|
||||
IRAM_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 if (buttonHoldTime >= BUTTON_PRESS_TIME_ACTION) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define g_buttonPressed false
|
||||
#define g_buttonPressTime 0
|
||||
|
||||
void button_init() {}
|
||||
|
||||
#endif
|
||||
101
libraries/Blynk/examples/Blynk.Edgent/Edgent_ESP8266/Settings.h
Normal file
101
libraries/Blynk/examples/Blynk.Edgent/Edgent_ESP8266/Settings.h
Normal file
@@ -0,0 +1,101 @@
|
||||
|
||||
/*
|
||||
* Board configuration (see examples below).
|
||||
*/
|
||||
|
||||
#if defined(USE_NODE_MCU_BOARD) || defined(USE_WEMOS_D1_MINI)
|
||||
|
||||
#if defined(USE_WEMOS_D1_MINI)
|
||||
#warning "This board does not have a button. Connect a button to gpio0 <> GND"
|
||||
#endif
|
||||
|
||||
#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 BUTTON_PRESS_TIME_ACTION 50
|
||||
|
||||
#define BOARD_PWM_MAX 1023
|
||||
|
||||
#if !defined(CONFIG_DEVICE_PREFIX)
|
||||
#define CONFIG_DEVICE_PREFIX "Blynk"
|
||||
#endif
|
||||
#if !defined(CONFIG_AP_URL)
|
||||
#define CONFIG_AP_URL "blynk.setup"
|
||||
#endif
|
||||
#if !defined(CONFIG_DEFAULT_SERVER)
|
||||
#define CONFIG_DEFAULT_SERVER "blynk.cloud"
|
||||
#endif
|
||||
#if !defined(CONFIG_DEFAULT_PORT)
|
||||
#define CONFIG_DEFAULT_PORT 443
|
||||
#endif
|
||||
|
||||
#define WIFI_CLOUD_MAX_RETRIES 500
|
||||
#define WIFI_NET_CONNECT_TIMEOUT 50000
|
||||
#define WIFI_CLOUD_CONNECT_TIMEOUT 50000
|
||||
#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__)
|
||||
#define DEBUG_PRINTF(...) BLYNK_LOG(__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_PRINT(...)
|
||||
#define DEBUG_PRINTF(...)
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
* Arduino 33 IoT and Arduino MKR1010 are no longer supported by Blynk.Edgent.
|
||||
* Blynk now offers a better solution for Dual-MCU boards. Please use Blynk.NCP:
|
||||
* https://github.com/blynkkk/BlynkNcpExample
|
||||
*
|
||||
*/
|
||||
@@ -0,0 +1,97 @@
|
||||
/*************************************************************
|
||||
Blynk is a platform with iOS and Android apps to control
|
||||
ESP32, Arduino, Raspberry Pi and the likes over the Internet.
|
||||
You can easily build mobile and web interfaces for any
|
||||
projects by simply dragging and dropping widgets.
|
||||
|
||||
Downloads, docs, tutorials: https://www.blynk.io
|
||||
Sketch generator: https://examples.blynk.cc
|
||||
Blynk community: https://community.blynk.cc
|
||||
Follow us: https://www.fb.com/blynkapp
|
||||
https://twitter.com/blynk_app
|
||||
|
||||
Blynk library is licensed under MIT license
|
||||
*************************************************************
|
||||
|
||||
NOTE: This example requires the connectivity module on your board to be
|
||||
flashed using Blynk.NCP firmware.
|
||||
|
||||
The easiest way to install the NCP firmware is using this PlatformIO project:
|
||||
https://github.com/blynkkk/BlynkNcpExample
|
||||
|
||||
*************************************************************/
|
||||
|
||||
/* Fill in information from your Blynk Template here */
|
||||
/* Read more: https://bit.ly/BlynkInject */
|
||||
//#define BLYNK_TEMPLATE_ID "TMPxxxxxx"
|
||||
//#define BLYNK_TEMPLATE_NAME "Device"
|
||||
|
||||
/* The firmware version of the Primary MCU (used for OTA updates) */
|
||||
#define BLYNK_FIRMWARE_VERSION "0.1.0"
|
||||
|
||||
// Debug output
|
||||
#define BLYNK_PRINT Serial
|
||||
|
||||
// Redefine NCP connection port settings, if needed
|
||||
//#define BLYNK_NCP_SERIAL Serial1
|
||||
//#define BLYNK_NCP_BAUD 2000000
|
||||
|
||||
#include <BlynkEdgentNCP.h>
|
||||
|
||||
BlynkTimer timer;
|
||||
|
||||
BLYNK_CONNECTED() {
|
||||
BLYNK_LOG("Connected to Blynk 🙌");
|
||||
}
|
||||
|
||||
BLYNK_DISCONNECTED() {
|
||||
BLYNK_LOG("Blynk disconnected");
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
|
||||
// Give Serial Monitor some time to connect
|
||||
delay(3000);
|
||||
|
||||
BLYNK_LOG("Main firmware: %s", BLYNK_FIRMWARE_VERSION);
|
||||
BLYNK_LOG("Build: %s", __DATE__ " " __TIME__);
|
||||
|
||||
// Initialize the Blynk.NCP hardware
|
||||
if (Blynk.initNCP()) {
|
||||
String ver = Blynk.getNcpVersion();
|
||||
BLYNK_LOG("Blynk.NCP firmware: %s", ver.c_str());
|
||||
} else {
|
||||
BLYNK_LOG("Cannot communicate to Blynk.NCP");
|
||||
BLYNK_LOG(" Please ensure you have flashed your board with the Blynk.NCP firmware, before running this example.");
|
||||
BLYNK_LOG(" See: https://github.com/blynkkk/BlynkNcpExample");
|
||||
return;
|
||||
}
|
||||
|
||||
// Print state changes
|
||||
Blynk.onStateChange([]() {
|
||||
BLYNK_LOG("State: %s", Blynk.getStateString());
|
||||
});
|
||||
|
||||
// Set config mode timeout to 30 minutes, for testing purposes
|
||||
Blynk.setConfigTimeout(30*60);
|
||||
|
||||
// White labeling (use this ONLY if you have a branded Blynk App)
|
||||
//Blynk.setVendorPrefix("MyCompany");
|
||||
//Blynk.setVendorServer("dashboard.mycompany.com");
|
||||
|
||||
// Product setup
|
||||
Blynk.begin(BLYNK_TEMPLATE_ID, BLYNK_TEMPLATE_NAME);
|
||||
|
||||
// Publish some data periodically
|
||||
timer.setInterval(1000, []() {
|
||||
Blynk.virtualWrite(V0, millis() / 1000);
|
||||
});
|
||||
}
|
||||
|
||||
void loop() {
|
||||
timer.run();
|
||||
Blynk.run();
|
||||
delay(1);
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
|
||||
extern "C" {
|
||||
void app_loop();
|
||||
void restartMCU();
|
||||
}
|
||||
|
||||
#include "Settings.h"
|
||||
#include <BlynkSimpleWioTerminal_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_NAME) && defined(BLYNK_DEVICE_NAME)
|
||||
#define BLYNK_TEMPLATE_NAME BLYNK_DEVICE_NAME
|
||||
#endif
|
||||
|
||||
#if !defined(BLYNK_TEMPLATE_ID) || !defined(BLYNK_TEMPLATE_NAME)
|
||||
#error "Please specify your BLYNK_TEMPLATE_ID and BLYNK_TEMPLATE_NAME"
|
||||
#endif
|
||||
|
||||
#if defined(BLYNK_AUTH_TOKEN)
|
||||
#error "BLYNK_AUTH_TOKEN is assigned automatically when using Blynk.Edgent, please remove it from the configuration"
|
||||
#endif
|
||||
|
||||
BlynkTimer edgentTimer;
|
||||
|
||||
#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()
|
||||
{
|
||||
#ifdef BLYNK_PRINT
|
||||
Blynk.printBanner();
|
||||
BLYNK_PRINT.println("----------------------------------------------------");
|
||||
BLYNK_PRINT.print(" Device: "); BLYNK_PRINT.println(getWiFiName());
|
||||
BLYNK_PRINT.print(" Firmware: "); BLYNK_PRINT.println(BLYNK_FIRMWARE_VERSION " (build " __DATE__ " " __TIME__ ")");
|
||||
if (configStore.getFlag(CONFIG_FLAG_VALID)) {
|
||||
BLYNK_PRINT.print(" Token: ");
|
||||
BLYNK_PRINT.println(String(configStore.cloudToken).substring(0,4) +
|
||||
" - •••• - •••• - ••••");
|
||||
}
|
||||
BLYNK_PRINT.print(" Platform: "); BLYNK_PRINT.println(String(BLYNK_INFO_DEVICE) + " @ " + (F_CPU/1000000) + "MHz");
|
||||
BLYNK_PRINT.print(" WiFi FW: "); BLYNK_PRINT.println(rpc_system_version());
|
||||
BLYNK_PRINT.println("----------------------------------------------------");
|
||||
#endif
|
||||
}
|
||||
|
||||
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();
|
||||
printDeviceBanner();
|
||||
console_init();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (!String(BLYNK_TEMPLATE_ID).startsWith("TMPL") ||
|
||||
!strlen(BLYNK_TEMPLATE_NAME)
|
||||
) {
|
||||
DEBUG_PRINT("Invalid configuration of TEMPLATE_ID / TEMPLATE_NAME");
|
||||
while (true) { delay(100); }
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
} BlynkEdgent;
|
||||
|
||||
void app_loop() {
|
||||
edgentTimer.run();
|
||||
edgentConsole.run();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,480 @@
|
||||
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServer.h>
|
||||
#include <DNSServer.h>
|
||||
|
||||
const char* config_form = R"html(
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>WiFi setup</title>
|
||||
<style>
|
||||
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%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="centered">
|
||||
<form method="get" action="config">
|
||||
<table>
|
||||
<tr><td><label for="ssid">WiFi SSID:</label></td> <td><input type="text" name="ssid" length=64 required="required"></td></tr>
|
||||
<tr><td><label for="pass">Password:</label></td> <td><input type="text" name="pass" length=64></td></tr>
|
||||
<tr><td><label for="blynk">Auth token:</label></td><td><input type="text" name="blynk" placeholder="a0b1c2d..." pattern="[-_a-zA-Z0-9]{32}" maxlength="32" required="required"></td></tr>
|
||||
<tr><td><label for="host">Host:</label></td> <td><input type="text" name="host" value="blynk.cloud" length=64></td></tr>
|
||||
<tr><td><label for="port_ssl">Port:</label></td> <td><input type="number" name="port_ssl" value="443" min="1" max="65535"></td></tr>
|
||||
</table><br/>
|
||||
<input type="submit" value="Apply">
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
)html";
|
||||
|
||||
WebServer server(80);
|
||||
DNSServer dnsServer;
|
||||
const byte DNS_PORT = 53;
|
||||
|
||||
static int connectNetRetries = WIFI_CLOUD_MAX_RETRIES;
|
||||
static int connectBlynkRetries = WIFI_CLOUD_MAX_RETRIES;
|
||||
|
||||
void restartMCU() {
|
||||
NVIC_SystemReset();
|
||||
while(1) {};
|
||||
}
|
||||
|
||||
static
|
||||
String encodeUniquePart(uint32_t n, unsigned len)
|
||||
{
|
||||
static constexpr char alphabet[] = { "0W8N4Y1HP5DF9K6JM3C2UA7R" };
|
||||
static constexpr int base = sizeof(alphabet)-1;
|
||||
|
||||
char buf[16] = { 0, };
|
||||
char prev = 0;
|
||||
for (unsigned i = 0; i < len; n /= base) {
|
||||
char c = alphabet[n % base];
|
||||
if (c == prev) {
|
||||
c = alphabet[(n+1) % base];
|
||||
}
|
||||
prev = buf[i++] = c;
|
||||
}
|
||||
return String(buf);
|
||||
}
|
||||
|
||||
static
|
||||
String getWiFiName(bool withPrefix = true)
|
||||
{
|
||||
static byte mac[6] = { 0, };
|
||||
static bool needMac = true;
|
||||
if (needMac) {
|
||||
WiFi.macAddress(mac);
|
||||
needMac = false;
|
||||
}
|
||||
|
||||
uint32_t unique = 0;
|
||||
for (int i=0; i<4; i++) {
|
||||
unique = BlynkCRC32(&mac, sizeof(mac), unique);
|
||||
}
|
||||
String devUnique = encodeUniquePart(unique, 4);
|
||||
|
||||
String devPrefix = CONFIG_DEVICE_PREFIX;
|
||||
String devName = String(BLYNK_TEMPLATE_NAME).substring(0, 31-6-devPrefix.length());
|
||||
|
||||
if (withPrefix) {
|
||||
return devPrefix + " " + devName + "-" + devUnique;
|
||||
} else {
|
||||
return devName + "-" + devUnique;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
String getWiFiMacAddress() {
|
||||
return WiFi.macAddress();
|
||||
}
|
||||
|
||||
static
|
||||
String getWiFiApBSSID() {
|
||||
return WiFi.softAPmacAddress();
|
||||
}
|
||||
|
||||
static
|
||||
String getWiFiNetworkSSID() {
|
||||
return WiFi.SSID();
|
||||
}
|
||||
|
||||
static
|
||||
String getWiFiNetworkBSSID() {
|
||||
return WiFi.BSSIDstr();
|
||||
}
|
||||
|
||||
String scanNetworks()
|
||||
{
|
||||
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
|
||||
String result = "[\n";
|
||||
|
||||
char buff[256];
|
||||
for (int i = 0; i < wifi_nets; i++){
|
||||
int id = indices[i];
|
||||
|
||||
const char* sec;
|
||||
switch (WiFi.encryptionType(id)) {
|
||||
case WIFI_AUTH_WEP: sec = "WEP"; break;
|
||||
case WIFI_AUTH_WPA_PSK: sec = "WPA/PSK"; break;
|
||||
case WIFI_AUTH_WPA2_PSK: sec = "WPA2/PSK"; break;
|
||||
case WIFI_AUTH_WPA_WPA2_PSK: sec = "WPA/WPA2/PSK"; break;
|
||||
case WIFI_AUTH_OPEN: sec = "OPEN"; break;
|
||||
default: sec = "unknown"; break;
|
||||
}
|
||||
|
||||
snprintf(buff, sizeof(buff),
|
||||
R"json( {"ssid":"%s","bssid":"%s","rssi":%i,"sec":"%s","ch":%i})json",
|
||||
WiFi.SSID(id).c_str(),
|
||||
WiFi.BSSIDstr(id).c_str(),
|
||||
WiFi.RSSI(id),
|
||||
sec,
|
||||
WiFi.channel(id)
|
||||
);
|
||||
|
||||
result += buff;
|
||||
if (i != wifi_nets-1) result += ",\n";
|
||||
}
|
||||
return result + "\n]";
|
||||
} else {
|
||||
return "[]";
|
||||
}
|
||||
}
|
||||
|
||||
void handleRoot() {
|
||||
server.send(200, "text/html", config_form);
|
||||
}
|
||||
|
||||
String networks = "[]";
|
||||
|
||||
void enterConfigMode()
|
||||
{
|
||||
networks = scanNetworks();
|
||||
|
||||
WiFi.mode(WIFI_OFF);
|
||||
delay(100);
|
||||
WiFi.mode(WIFI_AP);
|
||||
delay(2000);
|
||||
WiFi.softAPConfig(WIFI_AP_IP, WIFI_AP_IP, WIFI_AP_Subnet);
|
||||
WiFi.softAP(getWiFiName().c_str());
|
||||
delay(500);
|
||||
|
||||
// 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
|
||||
|
||||
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 forceSave = 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 = configDefault;
|
||||
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 (forceSave) {
|
||||
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);
|
||||
|
||||
connectNetRetries = connectBlynkRetries = 1;
|
||||
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", []() {
|
||||
// Configuring starts with board info request (may impact indication)
|
||||
BlynkState::set(MODE_CONFIGURING);
|
||||
|
||||
DEBUG_PRINT("Sending board info...");
|
||||
const char* tmpl = BLYNK_TEMPLATE_ID;
|
||||
|
||||
char buff[512];
|
||||
snprintf(buff, sizeof(buff),
|
||||
R"json({"board":"%s","tmpl_id":"%s","fw_type":"%s","fw_ver":"%s","ssid":"%s","bssid":"%s","mac":"%s","last_error":%d,"wifi_scan":true,"static_ip":true,"5ghz":true})json",
|
||||
BLYNK_TEMPLATE_NAME,
|
||||
tmpl ? tmpl : "Unknown",
|
||||
BLYNK_FIRMWARE_TYPE,
|
||||
BLYNK_FIRMWARE_VERSION,
|
||||
getWiFiName().c_str(),
|
||||
getWiFiApBSSID().c_str(),
|
||||
getWiFiMacAddress().c_str(),
|
||||
configStore.last_error
|
||||
);
|
||||
server.send(200, "application/json", buff);
|
||||
});
|
||||
server.on("/wifi_scan.json", []() {
|
||||
server.send(200, "application/json", networks);
|
||||
});
|
||||
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();
|
||||
});
|
||||
|
||||
server.begin();
|
||||
|
||||
while (BlynkState::is(MODE_WAIT_CONFIG) || BlynkState::is(MODE_CONFIGURING)) {
|
||||
delay(10);
|
||||
dnsServer.processNextRequest();
|
||||
server.handleClient();
|
||||
app_loop();
|
||||
if (BlynkState::is(MODE_CONFIGURING) && WiFi.softAPgetStationNum() == 0) {
|
||||
BlynkState::set(MODE_WAIT_CONFIG);
|
||||
}
|
||||
}
|
||||
|
||||
server.stop();
|
||||
}
|
||||
|
||||
void enterConnectNet() {
|
||||
BlynkState::set(MODE_CONNECTING_NET);
|
||||
DEBUG_PRINT(String("Connecting to WiFi: ") + configStore.wifiSSID);
|
||||
|
||||
String hostname = getWiFiName();
|
||||
hostname.replace(" ", "-");
|
||||
WiFi.setHostname(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 (strlen(configStore.wifiPass)) {
|
||||
WiFi.begin(configStore.wifiSSID, configStore.wifiPass);
|
||||
} else {
|
||||
WiFi.begin(configStore.wifiSSID);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
connectNetRetries = WIFI_CLOUD_MAX_RETRIES;
|
||||
BlynkState::set(MODE_CONNECTING_CLOUD);
|
||||
} else if (--connectNetRetries <= 0) {
|
||||
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()) &&
|
||||
(WiFi.status() == WL_CONNECTED) &&
|
||||
(!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); // TODO: retry after timeout
|
||||
} else if (WiFi.status() != WL_CONNECTED) {
|
||||
BlynkState::set(MODE_CONNECTING_NET);
|
||||
} else if (Blynk.connected()) {
|
||||
BlynkState::set(MODE_RUNNING);
|
||||
connectBlynkRetries = WIFI_CLOUD_MAX_RETRIES;
|
||||
|
||||
if (!configStore.getFlag(CONFIG_FLAG_VALID)) {
|
||||
configStore.last_error = BLYNK_PROV_ERR_NONE;
|
||||
configStore.setFlag(CONFIG_FLAG_VALID, true);
|
||||
config_save();
|
||||
|
||||
Blynk.sendInternal("meta", "set", "Hotspot Name", getWiFiName());
|
||||
}
|
||||
} else if (--connectBlynkRetries <= 0) {
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
|
||||
#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 <sfud.h>
|
||||
const sfud_flash *_flash = sfud_get_device_table() + 0;
|
||||
|
||||
void config_load()
|
||||
{
|
||||
memset(&configStore, 0, sizeof(configStore));
|
||||
sfud_err result = sfud_read(_flash, 0, sizeof(configStore), (uint8_t*)&configStore);
|
||||
if (result != SFUD_SUCCESS || configStore.magic != 0x626C6E6B)
|
||||
{
|
||||
DEBUG_PRINT("Using default config.");
|
||||
configStore = configDefault;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool config_save()
|
||||
{
|
||||
|
||||
sfud_err result = sfud_erase(_flash, 0, sizeof(configStore));
|
||||
delay(100);
|
||||
if (!result == SFUD_SUCCESS) { DEBUG_PRINT("Erase flash data failed"); return false; }
|
||||
|
||||
result = sfud_write(_flash, 0, sizeof(configStore), (uint8_t*)&configStore);
|
||||
delay(50);
|
||||
|
||||
if (!result == SFUD_SUCCESS) { DEBUG_PRINT("Write the flash data failed"); return false; }
|
||||
|
||||
DEBUG_PRINT("Configuration stored to flash");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool config_init()
|
||||
{
|
||||
if (sfud_init() != SFUD_SUCCESS) { DEBUG_PRINT("SFUD init failed"); return false; }
|
||||
|
||||
sfud_qspi_fast_read_enable(sfud_get_device(SFUD_W25Q32_DEVICE_INDEX), 2);
|
||||
|
||||
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;
|
||||
|
||||
sfud_err result = sfud_erase(_flash, 0, 1);
|
||||
|
||||
configStore.last_error = error;
|
||||
BLYNK_LOG2("Last error code: ", error);
|
||||
config_save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
|
||||
#include <Blynk/BlynkConsole.h>
|
||||
|
||||
BlynkConsole edgentConsole;
|
||||
|
||||
void console_init()
|
||||
{
|
||||
#ifdef BLYNK_PRINT
|
||||
edgentConsole.begin(BLYNK_PRINT);
|
||||
#endif
|
||||
|
||||
edgentConsole.print("\n>");
|
||||
|
||||
edgentConsole.addCommand("reboot", []() {
|
||||
edgentConsole.print(R"json({"status":"OK","msg":"rebooting wifi module"})json" "\n");
|
||||
delay(100);
|
||||
restartMCU();
|
||||
});
|
||||
|
||||
edgentConsole.addCommand("config", [](int argc, const char** argv) {
|
||||
if (argc < 1 || 0 == strcmp(argv[0], "start")) {
|
||||
BlynkState::set(MODE_WAIT_CONFIG);
|
||||
} else if (0 == strcmp(argv[0], "erase")) {
|
||||
BlynkState::set(MODE_RESET_CONFIG);
|
||||
}
|
||||
});
|
||||
|
||||
edgentConsole.addCommand("devinfo", []() {
|
||||
edgentConsole.printf(
|
||||
R"json({"name":"%s","board":"%s","tmpl_id":"%s","fw_type":"%s","fw_ver":"%s"})json" "\n",
|
||||
getWiFiName().c_str(),
|
||||
BLYNK_TEMPLATE_NAME,
|
||||
BLYNK_TEMPLATE_ID,
|
||||
BLYNK_FIRMWARE_TYPE,
|
||||
BLYNK_FIRMWARE_VERSION
|
||||
);
|
||||
});
|
||||
|
||||
edgentConsole.addCommand("connect", [](int argc, const char** argv) {
|
||||
if (argc < 2) {
|
||||
edgentConsole.print(R"json({"status":"error","msg":"invalid arguments. expected: <auth> <ssid> <pass>"})json" "\n");
|
||||
return;
|
||||
}
|
||||
String auth = argv[0];
|
||||
String ssid = argv[1];
|
||||
String pass = (argc >= 3) ? argv[2] : "";
|
||||
|
||||
if (auth.length() != 32) {
|
||||
edgentConsole.print(R"json({"status":"error","msg":"invalid token size"})json" "\n");
|
||||
return;
|
||||
}
|
||||
|
||||
edgentConsole.print(R"json({"status":"OK","msg":"trying to connect..."})json" "\n");
|
||||
|
||||
configStore = configDefault;
|
||||
CopyString(ssid, configStore.wifiSSID);
|
||||
CopyString(pass, configStore.wifiPass);
|
||||
CopyString(auth, configStore.cloudToken);
|
||||
|
||||
BlynkState::set(MODE_SWITCH_TO_STA);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
BLYNK_WRITE(InternalPinDBG) {
|
||||
String cmd = String(param.asStr()) + "\n";
|
||||
edgentConsole.runCommand((char*)cmd.c_str());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*************************************************************
|
||||
Blynk is a platform with iOS and Android apps to control
|
||||
ESP32, Arduino, Raspberry Pi and the likes over the Internet.
|
||||
You can easily build mobile and web interfaces for any
|
||||
projects by simply dragging and dropping widgets.
|
||||
|
||||
Downloads, docs, tutorials: https://www.blynk.io
|
||||
Sketch generator: https://examples.blynk.cc
|
||||
Blynk community: https://community.blynk.cc
|
||||
Follow us: https://www.fb.com/blynkapp
|
||||
https://twitter.com/blynk_app
|
||||
|
||||
Blynk library is licensed under MIT license
|
||||
*************************************************************
|
||||
Blynk.Edgent implements:
|
||||
- Blynk.Inject - Dynamic WiFi credentials provisioning
|
||||
- Blynk.Air - Over The Air firmware updates
|
||||
- Device state indication using a physical LED
|
||||
- Credentials reset using a physical Button
|
||||
|
||||
Required libraries:
|
||||
- Seeed Arduino rpcUnified
|
||||
- Seeed Arduino rpcWiFi
|
||||
- Seeed Arduino SFUD
|
||||
- Seeed Arduino FS
|
||||
- Seeed Arduino mbedtls
|
||||
- Seeed Arduino FreeRTOS
|
||||
- ArduinoOTA
|
||||
- ArduinoHttpClient
|
||||
|
||||
NOTE: Please also update the WiFi module firmware:
|
||||
https://wiki.seeedstudio.com/Wio-Terminal-Network-Overview
|
||||
*************************************************************/
|
||||
|
||||
/* Fill in information from your Blynk Template here */
|
||||
/* Read more: https://bit.ly/BlynkInject */
|
||||
//#define BLYNK_TEMPLATE_ID "TMPxxxxxx"
|
||||
//#define BLYNK_TEMPLATE_NAME "Device"
|
||||
|
||||
#define BLYNK_FIRMWARE_VERSION "0.1.0"
|
||||
|
||||
#define BLYNK_PRINT Serial
|
||||
//#define BLYNK_DEBUG
|
||||
|
||||
#define APP_DEBUG
|
||||
|
||||
#include "BlynkEdgent.h"
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
delay(100);
|
||||
|
||||
BlynkEdgent.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
BlynkEdgent.run();
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
|
||||
#if defined(BOARD_LED_PIN_WS2812)
|
||||
#include <Adafruit_NeoPixel.h> // Library: https://github.com/adafruit/Adafruit_NeoPixel
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const long t = millis();
|
||||
if (g_buttonPressed) {
|
||||
if (t - g_buttonPressTime > BUTTON_HOLD_TIME_ACTION) { return beatLED(COLOR_WHITE, (int[]){ 100, 100 }); }
|
||||
if (t - 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<typename T>
|
||||
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<typename T>
|
||||
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_TC3)
|
||||
|
||||
#include <TimerTC3.h>
|
||||
|
||||
void indicator_run() {
|
||||
uint32_t returnTime = indicator.run();
|
||||
if (returnTime) {
|
||||
TimerTc3.initialize(returnTime*1000);
|
||||
}
|
||||
}
|
||||
|
||||
void indicator_init() {
|
||||
indicator.init();
|
||||
TimerTc3.initialize(100*1000);
|
||||
TimerTc3.attachInterrupt(indicator_run);
|
||||
}
|
||||
|
||||
#elif defined(USE_TCC0)
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#warning LED indicator needs a functional timer!
|
||||
|
||||
void indicator_run() {}
|
||||
void indicator_init() {}
|
||||
|
||||
#endif
|
||||
|
||||
151
libraries/Blynk/examples/Blynk.Edgent/Edgent_Wio_Terminal/OTA.h
Normal file
151
libraries/Blynk/examples/Blynk.Edgent/Edgent_Wio_Terminal/OTA.h
Normal file
@@ -0,0 +1,151 @@
|
||||
|
||||
#include <ArduinoOTA.h> // only for InternalStorage
|
||||
#include <ArduinoHttpClient.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();
|
||||
|
||||
// Force HTTP update
|
||||
overTheAirURL.replace("https://", "http://");
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
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 = new WiFiClient();
|
||||
#ifdef USE_SSL
|
||||
} else if (protocol == "https") {
|
||||
client = &_blynkWifiClient;
|
||||
//client = new WiFiClientSecure();
|
||||
#endif
|
||||
} else {
|
||||
OTA_FATAL(String("Unsupported protocol: ") + protocol);
|
||||
}
|
||||
HttpClient http(*client, host, port);
|
||||
http.get(url);
|
||||
|
||||
int statusCode = http.responseStatusCode();
|
||||
if (statusCode != 200) {
|
||||
http.stop();
|
||||
OTA_FATAL(String("HTTP status code: ") + statusCode);
|
||||
}
|
||||
|
||||
|
||||
int contentLength = http.contentLength();
|
||||
if (contentLength == HttpClient::kNoContentLengthHeader) {
|
||||
http.stop();
|
||||
OTA_FATAL("Content-Length not defined");
|
||||
}
|
||||
|
||||
if (!InternalStorage.open(contentLength)) {
|
||||
http.stop();
|
||||
OTA_FATAL("Not enough space to store the update");
|
||||
}
|
||||
//InternalStorage.debugPrint();
|
||||
|
||||
DEBUG_PRINT("Flashing...");
|
||||
|
||||
int written = 0;
|
||||
int prevProgress = 0;
|
||||
uint8_t buff[256];
|
||||
while (client->connected() && written < contentLength) {
|
||||
|
||||
int len = http.readBytes(buff, sizeof(buff));
|
||||
if (len <= 0) continue;
|
||||
|
||||
for (int i = 0; i<len; i++) {
|
||||
InternalStorage.write(buff[i]);
|
||||
}
|
||||
written += len;
|
||||
|
||||
const int progress = (written*100)/contentLength;
|
||||
if (progress - prevProgress >= 10 || progress == 100) {
|
||||
#ifdef BLYNK_PRINT
|
||||
BLYNK_PRINT.print(String("\r ") + progress + "%");
|
||||
#endif
|
||||
prevProgress = progress;
|
||||
}
|
||||
}
|
||||
#ifdef BLYNK_PRINT
|
||||
BLYNK_PRINT.println();
|
||||
#endif
|
||||
InternalStorage.close();
|
||||
http.stop();
|
||||
|
||||
if (written != contentLength) {
|
||||
OTA_FATAL(String("Interrupted at ") + written + " / " + contentLength + " bytes");
|
||||
}
|
||||
|
||||
DEBUG_PRINT("=== Update successfully completed. Rebooting.");
|
||||
InternalStorage.apply();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
#ifdef BOARD_BUTTON_PIN
|
||||
|
||||
volatile bool g_buttonPressed = false;
|
||||
volatile uint32_t g_buttonPressTime = -1;
|
||||
|
||||
void button_action(void)
|
||||
{
|
||||
BlynkState::set(MODE_RESET_CONFIG);
|
||||
}
|
||||
|
||||
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 if (buttonHoldTime >= BUTTON_PRESS_TIME_ACTION) {
|
||||
// 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_PULLDOWN);
|
||||
#endif
|
||||
attachInterrupt(BOARD_BUTTON_PIN, button_change, CHANGE);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define g_buttonPressed false
|
||||
#define g_buttonPressTime 0
|
||||
|
||||
void button_init() {}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,61 @@
|
||||
|
||||
/*
|
||||
* Board configuration (see examples below).
|
||||
*/
|
||||
|
||||
// Example configuration for Wio Terminal Board
|
||||
#define BOARD_BUTTON_PIN WIO_KEY_A // Pin where user button is attached
|
||||
#define BOARD_BUTTON_ACTIVE_LOW true // true if button is "active-low"
|
||||
|
||||
#define BOARD_LED_PIN LED_BUILTIN // Set LED pin - if you have a single-color LED attached
|
||||
//#define BOARD_LED_PIN_R 27 // Set R,G,B pins - if your LED is PWM RGB
|
||||
//#define BOARD_LED_PIN_G 26
|
||||
//#define BOARD_LED_PIN_B 25
|
||||
//#define BOARD_LED_PIN_WS2812 33 // 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 255 // 0..255 brightness control
|
||||
|
||||
|
||||
/*
|
||||
* Advanced options
|
||||
*/
|
||||
|
||||
#define BUTTON_HOLD_TIME_INDICATION 3000
|
||||
#define BUTTON_HOLD_TIME_ACTION 10000
|
||||
#define BUTTON_PRESS_TIME_ACTION 50
|
||||
|
||||
#define BOARD_PWM_MAX 1023
|
||||
|
||||
#if !defined(CONFIG_DEVICE_PREFIX)
|
||||
#define CONFIG_DEVICE_PREFIX "Blynk"
|
||||
#endif
|
||||
#if !defined(CONFIG_AP_URL)
|
||||
#define CONFIG_AP_URL "blynk.setup"
|
||||
#endif
|
||||
#if !defined(CONFIG_DEFAULT_SERVER)
|
||||
#define CONFIG_DEFAULT_SERVER "blynk.cloud"
|
||||
#endif
|
||||
#if !defined(CONFIG_DEFAULT_PORT)
|
||||
#define CONFIG_DEFAULT_PORT 443
|
||||
#endif
|
||||
|
||||
#define WIFI_CLOUD_MAX_RETRIES 500
|
||||
#define WIFI_NET_CONNECT_TIMEOUT 50000
|
||||
#define WIFI_CLOUD_CONNECT_TIMEOUT 50000
|
||||
#define WIFI_AP_IP IPAddress(192, 168, 4, 1)
|
||||
#define WIFI_AP_Subnet IPAddress(255, 255, 255, 0)
|
||||
//#define WIFI_CAPTIVE_PORTAL_ENABLE 1
|
||||
|
||||
//#define USE_TC3
|
||||
//#define USE_TCC0
|
||||
|
||||
#define BLYNK_NO_DEFAULT_BANNER
|
||||
|
||||
#if defined(APP_DEBUG)
|
||||
#define DEBUG_PRINT(...) BLYNK_LOG1(__VA_ARGS__)
|
||||
#define DEBUG_PRINTF(...) BLYNK_LOG(__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_PRINT(...)
|
||||
#define DEBUG_PRINTF(...)
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user