初始化提交

This commit is contained in:
王立帮
2024-07-20 22:09:06 +08:00
commit c247dd07a6
6876 changed files with 2743096 additions and 0 deletions

View File

@@ -0,0 +1,69 @@
/**************************************************************
* This is a DEMO. You can use it only for development and testing.
*
* If you would like to add these features to your product,
* please contact Blynk for Business:
*
* http://www.blynk.io/
*
**************************************************************/
extern "C" {
#include "user_interface.h"
}
#include "Settings.h"
#include <BlynkSimpleEsp8266.h>
#include "BlynkState.h"
#include "ConfigStore.h"
#include "ResetButton.h"
#include "ConfigMode.h"
#include "Indicator.h"
#include "OTA.h"
inline
void BlynkState::set(State m) {
if (state != m) {
DEBUG_PRINT(String(StateStr[state]) + " => " + StateStr[m]);
state = m;
}
}
class Provisioning {
public:
void begin()
{
DEBUG_PRINT("");
DEBUG_PRINT("Hardware v" + String(BOARD_HARDWARE_VERSION));
DEBUG_PRINT("Firmware v" + String(BOARD_FIRMWARE_VERSION));
indicator_init();
button_init();
config_init();
if (configStore.flagConfig) {
BlynkState::set(MODE_CONNECTING_NET);
} else {
BlynkState::set(MODE_WAIT_CONFIG);
}
}
void run() {
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: Blynk.run(); break;
case MODE_OTA_UPGRADE: enterOTA(); break;
case MODE_SWITCH_TO_STA: enterSwitchToSTA(); break;
case MODE_RESET_CONFIG: enterResetConfig(); break;
default: enterError(); break;
}
}
};
Provisioning BlynkProvisioning;

View File

@@ -0,0 +1,47 @@
/**************************************************************
* This is a DEMO. You can use it only for development and testing.
*
* If you would like to add these features to your product,
* please contact Blynk for Business:
*
* http://www.blynk.io/
*
**************************************************************/
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] = {
"WAIT_CONFIG",
"CONFIGURING",
"CONNECTING_NET",
"CONNECTING_CLOUD",
"RUNNING",
"OTA_UPGRADE",
"SWITCH_TO_STA",
"RESET_CONFIG",
"ERROR"
};
#endif
namespace BlynkState
{
volatile State state;
State get() { return state; }
bool is (State m) { return (state == m); }
void set(State m);
};

View File

@@ -0,0 +1,274 @@
/**************************************************************
* This is a DEMO. You can use it only for development and testing.
*
* If you would like to add these features to your product,
* please contact Blynk for Business:
*
* http://www.blynk.io/
*
**************************************************************/
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPUpdateServer.h>
#include <DNSServer.h>
ESP8266WebServer server(WIFI_AP_CONFIG_PORT);
ESP8266HTTPUpdateServer httpUpdater;
DNSServer dnsServer;
const byte DNS_PORT = 53;
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" length=64></td></tr>
<tr><td><label for="port">Port:</label></td> <td><input type="number" name="port" value="80" min="1" max="65535"></td></tr>
</table><br/>
<input type="submit" value="Apply">
</form>
</div>
</body>
</html>
)html";
void restartMCU() {
ESP.restart();
}
void enterConfigMode()
{
randomSeed(ESP.getChipId());
const uint32_t unique = random(0xFFFFF);
char ssidBuff[64];
snprintf(ssidBuff, sizeof(ssidBuff), "%s-%05X", PRODUCT_WIFI_SSID, unique);
WiFi.mode(WIFI_OFF);
delay(100);
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(WIFI_AP_IP, WIFI_AP_IP, WIFI_AP_Subnet);
WiFi.softAP(ssidBuff);
delay(500);
IPAddress myIP = WiFi.softAPIP();
DEBUG_PRINT(String("AP SSID: ") + ssidBuff);
DEBUG_PRINT(String("AP IP: ") + myIP[0] + "." + myIP[1] + "." + myIP[2] + "." + myIP[3]);
if (myIP == (uint32_t)0)
{
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, BOARD_CONFIG_AP_URL, WiFi.softAPIP());
DEBUG_PRINT(String("AP URL: ") + BOARD_CONFIG_AP_URL);
#endif
httpUpdater.setup(&server);
server.on("/", []() {
server.send(200, "text/html", config_form);
});
server.on("/config", []() {
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");
String content;
unsigned statusCode;
DEBUG_PRINT(String("WiFi SSID: ") + ssid + " Pass: " + pass);
DEBUG_PRINT(String("Blynk cloud: ") + token + " @ " + host + ":" + port);
if (token.length() == 32 && ssid.length() > 0) {
configStore.flagConfig = false;
CopyString(ssid, configStore.wifiSSID);
CopyString(pass, configStore.wifiPass);
CopyString(token, configStore.cloudToken);
if (host.length()) {
CopyString(host, configStore.cloudHost);
}
if (port.length()) {
configStore.cloudPort = port.toInt();
}
content = R"json({"status":"ok","msg":"Configuration saved"})json";
statusCode = 200;
server.send(statusCode, "application/json", content);
BlynkState::set(MODE_SWITCH_TO_STA);
} else {
DEBUG_PRINT("Configuration invalid");
content = R"json({"status":"error","msg":"Configuration invalid"})json";
statusCode = 404;
server.send(statusCode, "application/json", content);
}
});
server.on("/board_info.json", []() {
char buff[256];
snprintf(buff, sizeof(buff),
R"json({"board":"%s","vendor":"%s","tmpl_id":"%s","fw_ver":"%s","hw_ver":"%s"})json",
BOARD_NAME,
BOARD_VENDOR,
BOARD_TEMPLATE_ID,
BOARD_FIRMWARE_VERSION,
BOARD_HARDWARE_VERSION
);
server.send(200, "application/json", buff);
});
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)) {
dnsServer.processNextRequest();
server.handleClient();
if (BlynkState::is(MODE_WAIT_CONFIG) && WiFi.softAPgetStationNum() > 0) {
BlynkState::set(MODE_CONFIGURING);
} else if (BlynkState::is(MODE_CONFIGURING) && WiFi.softAPgetStationNum() == 0) {
BlynkState::set(MODE_WAIT_CONFIG);
}
}
server.stop();
}
void enterConnectNet() {
BlynkState::set(MODE_CONNECTING_NET);
DEBUG_PRINT(String("Connecting to WiFi: ") + configStore.wifiSSID);
WiFi.mode(WIFI_STA);
if (!WiFi.begin(configStore.wifiSSID, configStore.wifiPass)) {
return;
}
unsigned long timeoutMs = millis() + WIFI_NET_CONNECT_TIMEOUT;
while ((timeoutMs > millis()) && (WiFi.status() != WL_CONNECTED))
{
delay(100);
if (!BlynkState::is(MODE_CONNECTING_NET)) {
WiFi.disconnect();
return;
}
}
if (WiFi.status() == WL_CONNECTED) {
BlynkState::set(MODE_CONNECTING_CLOUD);
} else {
BlynkState::set(MODE_ERROR);
}
}
void enterConnectCloud() {
BlynkState::set(MODE_CONNECTING_CLOUD);
Blynk.disconnect();
Blynk.config(configStore.cloudToken, configStore.cloudHost, configStore.cloudPort);
Blynk.connect(0);
unsigned long timeoutMs = millis() + WIFI_CLOUD_CONNECT_TIMEOUT;
while ((timeoutMs > millis()) &&
(Blynk.connected() == false))
{
Blynk.run();
if (!BlynkState::is(MODE_CONNECTING_CLOUD)) {
Blynk.disconnect();
return;
}
}
if (Blynk.connected()) {
BlynkState::set(MODE_RUNNING);
if (!configStore.flagConfig) {
configStore.flagConfig = true;
config_save();
DEBUG_PRINT("Configuration stored to flash");
}
} else {
BlynkState::set(MODE_ERROR);
}
}
void enterSwitchToSTA() {
BlynkState::set(MODE_SWITCH_TO_STA);
DEBUG_PRINT("Switching to STA...");
WiFi.mode(WIFI_OFF);
delay(1000);
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);
if (!BlynkState::is(MODE_ERROR)) {
return;
}
}
DEBUG_PRINT("Restarting after error.");
delay(10);
restartMCU();
}

View File

@@ -0,0 +1,83 @@
/**************************************************************
* This is a DEMO. You can use it only for development and testing.
*
* If you would like to add these features to your product,
* please contact Blynk for Business:
*
* http://www.blynk.io/
*
**************************************************************/
struct ConfigStore {
uint32_t magic;
char version[9];
uint8_t flagConfig:1;
uint8_t flagApFail:1;
uint8_t flagSelfTest:1;
char wifiSSID[34];
char wifiPass[64];
char cloudToken[34];
char cloudHost[34];
uint16_t cloudPort;
uint16_t checksum;
} __attribute__((packed));
ConfigStore configStore;
const ConfigStore configDefault = {
0x626C6E6B,
BOARD_FIRMWARE_VERSION,
0, 0, 0,
"",
"",
"invalid token",
"blynk-cloud.com", 80,
0
};
#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();
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);
}
template<typename T, int size>
void CopyString(const String& s, T(&arr)[size]) {
s.toCharArray(arr, size);
}

View File

@@ -0,0 +1,263 @@
/**************************************************************
* This is a DEMO. You can use it only for development and testing.
*
* If you would like to add these features to your product,
* please contact Blynk for Business:
*
* http://www.blynk.io/
*
**************************************************************/
#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) ((x)*(BOARD_LED_BRIGHTNESS)/255)
#define RGB(r,g,b) (DIMM(r) << 16 | DIMM(g) << 8 | DIMM(b) << 0)
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() {
m_Counter = 0;
initLED();
}
uint32_t run() {
State currState = BlynkState::get();
// Reset counter if indicator state changes
if (m_PrevState != currState) {
m_PrevState = currState;
m_Counter = 0;
}
if (g_buttonPressed) {
if (millis() - g_buttonPressTime > BUTTON_HOLD_TIME_ACTION) { return beatLED(COLOR_WHITE, (int[]){ 100, 100 }); }
if (millis() - g_buttonPressTime > BUTTON_HOLD_TIME_INDICATION) { return waveLED(COLOR_WHITE, 1000); }
}
switch (currState) {
case MODE_RESET_CONFIG:
case MODE_WAIT_CONFIG: return beatLED(COLOR_BLUE, (int[]){ 50, 500 });
case MODE_CONFIGURING: return beatLED(COLOR_BLUE, (int[]){ 200, 200 });
case MODE_CONNECTING_NET: return beatLED(COLOR_BLYNK, (int[]){ 50, 500 });
case MODE_CONNECTING_CLOUD: return beatLED(COLOR_BLYNK, (int[]){ 100, 100 });
case MODE_RUNNING: return waveLED(COLOR_BLYNK, 5000);
case MODE_OTA_UPGRADE: return beatLED(COLOR_MAGENTA, (int[]){ 50, 50 });
default: return beatLED(COLOR_RED, (int[]){ 80, 100, 80, 1000 } );
}
}
protected:
/*
* LED drivers
*/
#if defined(BOARD_LED_PIN_WS2812) // Addressable, NeoPixel RGB LED
void initLED() {
rgb.begin();
setRGB(COLOR_BLACK);
}
void setRGB(uint32_t color) {
rgb.setPixelColor(0, color);
rgb.show();
}
#elif defined(BOARD_LED_PIN_R) // Normal RGB LED (common anode or common cathode)
void initLED() {
pinMode(BOARD_LED_PIN_R, OUTPUT);
pinMode(BOARD_LED_PIN_G, OUTPUT);
pinMode(BOARD_LED_PIN_B, OUTPUT);
}
void setRGB(uint32_t color) {
uint8_t r = (color & 0xFF0000) >> 16;
uint8_t g = (color & 0x00FF00) >> 8;
uint8_t b = (color & 0x0000FF);
#if BOARD_LED_INVERSE
analogWrite(BOARD_LED_PIN_R, BOARD_PWM_MAX - r);
analogWrite(BOARD_LED_PIN_G, BOARD_PWM_MAX - g);
analogWrite(BOARD_LED_PIN_B, BOARD_PWM_MAX - b);
#else
analogWrite(BOARD_LED_PIN_R, r);
analogWrite(BOARD_LED_PIN_G, g);
analogWrite(BOARD_LED_PIN_B, 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, BOARD_PWM_MAX - color);
#else
analogWrite(BOARD_LED_PIN, color);
#endif
}
#else
#warning Invalid LED configuration.
#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_PWM_MAX : 0);
uint32_t next = beat[m_Counter % cnt];
m_Counter = (m_Counter+1) % cnt;
return next;
}
uint32_t waveLED(uint32_t, unsigned breathePeriod) {
uint8_t brightness = (m_Counter < 128) ? m_Counter : 255 - m_Counter;
setLED(BOARD_PWM_MAX * ((float)brightness / (BOARD_PWM_MAX/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() {
blinker.attach_ms(100, indicator_run);
}
#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() {
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() {
Timer3.initialize(100*1000);
Timer3.attachInterrupt(indicator_run);
}
#else
#warning LED indicator needs a functional timer!
void indicator_run() {}
void indicator_init() {}
#endif

View File

@@ -0,0 +1,62 @@
/**************************************************************
* This is a DEMO. You can use it only for development and testing.
*
* If you would like to add these features to your product,
* please contact Blynk for Business:
*
* http://www.blynk.io/
*
**************************************************************
*
* How to trigger an OTA update?
* 1. In Arduino IDE menu: Sketch -> Export compiled Binary
* 2. Open console, navigate to the sketch directory
* 3.a Trigger update using HTTPS API on local server for specific hardware:
* curl -v -F file=@Template_ESP8266.ino.nodemcu.bin --insecure -u admin@blynk.cc:admin https://localhost:9443/admin/ota/start?token=123
* 3.b Trigger update using HTTPS API on local server for all hardware:
* curl -v -F file=@Template_ESP8266.ino.nodemcu.bin --insecure -u admin@blynk.cc:admin https://localhost:9443/admin/ota/start
* 3.c Trigger update using HTTPS API on local server for single user:
* curl -v -F file=@Template_ESP8266.ino.nodemcu.bin --insecure -u admin@blynk.cc:admin https://localhost:9443/admin/ota/start?user=pupkin@gmail.com
* 3.d Trigger update using HTTPS API on local server for single user and specific project:
* curl -v -F file=@Template_ESP8266.ino.nodemcu.bin --insecure -u admin@blynk.cc:admin https://localhost:9443/admin/ota/start?user=pupkin@gmail.com&project=123
* More about ESP8266 OTA updates:
* https://github.com/esp8266/Arduino/blob/master/doc/ota_updates/readme.md
*/
#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>
String overTheAirURL;
BLYNK_WRITE(InternalPinOTA) {
overTheAirURL = param.asString();
// Disconnect, not to interfere with OTA process
Blynk.disconnect();
// Start OTA
BlynkState::set(MODE_OTA_UPGRADE);
delay(500);
}
void enterOTA() {
BlynkState::set(MODE_OTA_UPGRADE);
DEBUG_PRINT(String("Firmware update URL: ") + overTheAirURL);
switch (ESPhttpUpdate.update(overTheAirURL, BOARD_FIRMWARE_VERSION)) {
case HTTP_UPDATE_FAILED:
DEBUG_PRINT(String("Firmware update failed (error ") + ESPhttpUpdate.getLastError() + "): " + ESPhttpUpdate.getLastErrorString());
BlynkState::set(MODE_ERROR);
break;
case HTTP_UPDATE_NO_UPDATES:
DEBUG_PRINT("No firmware updates available.");
BlynkState::set(MODE_CONNECTING_CLOUD);
break;
case HTTP_UPDATE_OK:
DEBUG_PRINT("Firmware update: OK.");
delay(10);
restartMCU();
break;
}
}

View File

@@ -0,0 +1,3 @@
# Instructions for myPlant (Blynk Demo App):
**->** http://help.blynk.cc/hardware-and-libraries/esp8266/myplant-demo **<-**

View File

@@ -0,0 +1,50 @@
/**************************************************************
* This is a DEMO. You can use it only for development and testing.
*
* If you would like to add these features to your product,
* please contact Blynk for Business:
*
* http://www.blynk.io/
*
**************************************************************/
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 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();
}
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);
}

View File

@@ -0,0 +1,102 @@
/*
* General options
*/
#define BOARD_FIRMWARE_VERSION "1.0.1"
#define BOARD_HARDWARE_VERSION "1.0.0"
#define BOARD_NAME "My Plant" // Name of your product. Should match App Export request info.
#define BOARD_VENDOR "Blynk" // Name of your company. Should match App Export request info.
#define BOARD_TEMPLATE_ID "TMPL0000"
#define PRODUCT_WIFI_SSID "Blynk myPlant" // Name of the device, to be displayed during configuration. Should match export request info.
#define BOARD_CONFIG_AP_URL "my-plant.cc" // Config page will be available in a browser at 'http://my-plant.cc/'
/*
* Board configuration (see examples below).
*/
#if defined(USE_CUSTOM_BOARD)
// Custom board configuration
#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 2 // 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
#elif defined(USE_NODE_MCU_BOARD)
#warning "NodeMCU board selected"
// Example configuration for NodeMCU v1.0 Board
#define BOARD_BUTTON_PIN 0
#define BOARD_BUTTON_ACTIVE_LOW true
#define BOARD_LED_PIN_R D8
#define BOARD_LED_PIN_G D7
#define BOARD_LED_PIN_B D6
#define BOARD_LED_INVERSE false
#define BOARD_LED_BRIGHTNESS 64
#elif defined(USE_SPARKFUN_BLYNK_BOARD)
#warning "Sparkfun Blynk board selected"
// Example configuration for 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)
#warning "Witty Cloud board selected"
// Example configuration for 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
#error "No board selected"
#endif
/*
* Advanced options
*/
#define BUTTON_HOLD_TIME_INDICATION 3000
#define BUTTON_HOLD_TIME_ACTION 10000
#define BOARD_PWM_MAX 1023
#define WIFI_NET_CONNECT_TIMEOUT 30000
#define WIFI_CLOUD_CONNECT_TIMEOUT 15000
#define WIFI_AP_CONFIG_PORT 80
#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
#if defined(APP_DEBUG)
#define DEBUG_PRINT(...) BLYNK_LOG1(__VA_ARGS__)
#else
#define DEBUG_PRINT(...)
#endif

View File

@@ -0,0 +1,195 @@
/*************************************************************
This is a DEMO sketch which works with Blynk myPlant app and
showcases how your app made with Blynk can work
You can download free app here:
iOS: https://itunes.apple.com/us/app/blynk-myplant/id1163620518?mt=8
Android: https://play.google.com/store/apps/details?id=cc.blynk.appexport.demo
If you would like to add these features to your product,
please contact Blynk for Businesses:
http://www.blynk.io/
*************************************************************/
#define USE_SPARKFUN_BLYNK_BOARD // Uncomment the board you are using
//#define USE_NODE_MCU_BOARD // Comment out the boards you are not using
//#define USE_WITTY_CLOUD_BOARD
//#define USE_CUSTOM_BOARD // For all other ESP8266-based boards -
// see "Custom board configuration" in Settings.h
#define APP_DEBUG // Comment this out to disable debug prints
#define BLYNK_PRINT Serial
#include "BlynkProvisioning.h"
void setup() {
delay(500);
Serial.begin(115200);
/**************************************************************
*
* Workflow to connect the device to WiFi network.
* Here is how it works:
* 1. At the first start hardware acts as an Access Point and
* broadcasts it's own WiFi.
* 2. myPlant smartphone app connects to this Access Point
* 3. myPlant smartphone app request new Auth Token and passes
* it together with user's WiFi SSID and password
* 4. Hardware saves this information to EEPROM
* 5. Hardware reboots and now connects to user's WiFi Network
* 6. Hardware connects to Blynk Cloud and is ready to work with app
*
* Next time the hardware reboots, it will use the same configuration
* to connect. User can RESET the board and re-initiate provisioning
*
* Explore the Settings.h for parameters
*
**************************************************************/
BlynkProvisioning.begin();
example_init(); // Initialize this example
}
void loop() {
// This handles the network and cloud connection
BlynkProvisioning.run();
// Run this example periodic actions
example_run();
}
/**************************************************************
*
* myPlant example App code
*
* The following code simulates plant watering system
*
**************************************************************/
BlynkTimer timer; // Initiating timer to perform repeating event
static int sensorSoilMoisture = 60;
static int sensorAirHumidity = 50;
static int wateringAmount = 5;
static int wateringTimer = -1;
static bool isNotificationSent = false;
// Getting data from "Set watering amount" slider
BLYNK_WRITE(V5) {
wateringAmount = param.asInt();
DEBUG_PRINT(String("Watering amount: ") + wateringAmount);
}
// Getting data from "Start Watering" button
BLYNK_WRITE(V6) {
if (param.asInt() == 1) {
// If watering started -> start simulating watering
timer.enable(wateringTimer);
DEBUG_PRINT("Watering started by user");
} else {
// If watering stopped -> stop simulating watering
timer.disable(wateringTimer);
DEBUG_PRINT("Watering stopped by user");
}
}
// When device starts ->
// sync watering switch button status
// and watering amount level from the cloud (last App value)
BLYNK_CONNECTED() {
Blynk.syncVirtual(V5, V6);
}
// This is a sinusoidal function used for simulations
float sinusoidal(float minv, float maxv, float period) {
float amp = (maxv - minv) / 2.0;
float med = minv + amp;
return med + amp * sin((M_PI * 2 * millis()) / period);
}
// Simulating values jittering
float randomize(float minv, float maxv) {
return float(random(minv * 1000, maxv * 1000)) / 1000;
}
void example_init() {
// Update sensors each 3 seconds
timer.setInterval(3000L, []() {
// Soil moisture
if (sensorSoilMoisture < 33) {
Blynk.virtualWrite(V1, "DRY");
} else if (sensorSoilMoisture > 33) {
Blynk.virtualWrite(V1, "MOIST");
} else {
Blynk.virtualWrite(V1, "WET");
}
float dayPeriod = 3.0 * 60 * 1000;
// Light level
int light = sinusoidal(5, 95, dayPeriod);
if (light < 33) {
Blynk.virtualWrite(V2, "LOW");
} else if (light > 33) {
Blynk.virtualWrite(V2, "GOOD");
} else {
Blynk.virtualWrite(V2, "MED");
}
// Temperature
Blynk.virtualWrite(V3, sinusoidal(18, 23, dayPeriod) + randomize(-1.0, 1.0));
});
// Humidity updates at a different rate (5s)
timer.setInterval(5000L, []() {
sensorAirHumidity += random (-5, +5);
sensorAirHumidity = constrain(sensorAirHumidity, 30, 90);
Blynk.virtualWrite(V4, sensorAirHumidity);
});
// Soil Moisture decreases 3% every second
timer.setInterval(1000L, []() {
sensorSoilMoisture -= 3;
sensorSoilMoisture = constrain(sensorSoilMoisture, 7, 85);
if (sensorSoilMoisture < 20) {
if (isNotificationSent == false) {
Blynk.email("myPlant notification", "Your plant is thirsty!");
isNotificationSent = true;
DEBUG_PRINT("Email notification sent");
}
}
});
// Simulate watering process
wateringTimer = timer.setInterval(1000L, []() {
sensorSoilMoisture += wateringAmount;
sensorSoilMoisture = constrain(sensorSoilMoisture, 7, 85);
if (sensorSoilMoisture > 30) {
isNotificationSent = false;
}
if (sensorSoilMoisture >= 85) {
// Stop watering
timer.disable(wateringTimer);
// Update "Start Watering" button widget state
Blynk.virtualWrite(V6, 0);
DEBUG_PRINT("Watering stopped automatically");
}
});
timer.disable(wateringTimer);
}
void example_run() {
timer.run();
}