初始化提交
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
/**************************************************************
|
||||
* 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 "Settings.h"
|
||||
#include <SPI.h>
|
||||
#include <WiFi101.h>
|
||||
#include <BlynkSimpleMKR1000.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;
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
/**************************************************************
|
||||
* 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 <WiFiClient.h>
|
||||
#include <WiFiMDNSResponder.h>
|
||||
|
||||
WiFiServer server(WIFI_AP_CONFIG_PORT);
|
||||
WiFiMDNSResponder mdnsResponder;
|
||||
|
||||
String urlDecode(const String& text);
|
||||
String urlFindArg(const String& url, const String& arg);
|
||||
|
||||
enum Request {
|
||||
REQ_BOARD_INFO,
|
||||
REQ_ROOT,
|
||||
REQ_CONFIG,
|
||||
REQ_RESET,
|
||||
REQ_REBOOT
|
||||
};
|
||||
|
||||
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() {
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
void enterConfigMode()
|
||||
{
|
||||
byte mac[6];
|
||||
memset(mac, 0, sizeof(mac));
|
||||
WiFi.macAddress(mac);
|
||||
uint32_t chipId = *(uint32_t*)(mac+2) & 0xFFFFFF;
|
||||
|
||||
randomSeed(chipId);
|
||||
const uint32_t unique = random(0xFFFFF);
|
||||
char ssidBuff[64];
|
||||
snprintf(ssidBuff, sizeof(ssidBuff), "%s-%05X", PRODUCT_WIFI_SSID, unique);
|
||||
|
||||
WiFi.beginAP(ssidBuff);
|
||||
mdnsResponder.begin(BOARD_CONFIG_AP_URL);
|
||||
|
||||
delay(500);
|
||||
IPAddress myIP = WiFi.localIP();
|
||||
DEBUG_PRINT(String("AP SSID: ") + ssidBuff);
|
||||
DEBUG_PRINT(String("AP IP: ") + myIP[0] + "." + myIP[1] + "." + myIP[2] + "." + myIP[3]);
|
||||
DEBUG_PRINT(String("AP URL: ") + BOARD_CONFIG_AP_URL + ".local");
|
||||
|
||||
server.begin();
|
||||
|
||||
while(BlynkState::is(MODE_WAIT_CONFIG)) {
|
||||
mdnsResponder.poll();
|
||||
WiFiClient client = server.available(); // listen for incoming clients
|
||||
|
||||
if (client) { // if you get a client,
|
||||
String currentLine = ""; // make a String to hold incoming data from the client
|
||||
String config_line = "";
|
||||
Request req = REQ_ROOT;
|
||||
while (client.connected()) { // loop while the client's connected
|
||||
if (client.available()) { // if there's bytes to read from the client,
|
||||
char c = client.read(); // read a byte, then
|
||||
Serial.write(c); // print it out the serial monitor
|
||||
if (c == '\n') { // if the byte is a newline character
|
||||
|
||||
// if the current line is blank, you got two newline characters in a row.
|
||||
// that's the end of the client HTTP request, so send a response:
|
||||
if (currentLine.length() == 0) {
|
||||
String responce = "200 OK";
|
||||
String content = "";
|
||||
String content_type = "text/html";
|
||||
|
||||
switch(req) {
|
||||
case REQ_ROOT: {
|
||||
content = config_form;
|
||||
} break;
|
||||
case REQ_CONFIG: {
|
||||
String ssid = urlFindArg(config_line, "ssid");
|
||||
String ssidManual = urlFindArg(config_line, "ssidManual");
|
||||
String pass = urlFindArg(config_line, "pass");
|
||||
if (ssidManual != "") {
|
||||
ssid = ssidManual;
|
||||
}
|
||||
String token = urlFindArg(config_line, "blynk");
|
||||
String host = urlFindArg(config_line, "host");
|
||||
String port = urlFindArg(config_line, "port");
|
||||
|
||||
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";
|
||||
|
||||
BlynkState::set(MODE_SWITCH_TO_STA);
|
||||
} else {
|
||||
DEBUG_PRINT("Configuration invalid");
|
||||
content = R"json({"status":"error","msg":"Configuration invalid"})json";
|
||||
}
|
||||
content_type = "application/json";
|
||||
} break;
|
||||
case REQ_BOARD_INFO: {
|
||||
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
|
||||
);
|
||||
content = buff;
|
||||
content_type = "application/json";
|
||||
} break;
|
||||
case REQ_RESET: {
|
||||
BlynkState::set(MODE_RESET_CONFIG);
|
||||
content = R"json({"status":"ok","msg":"Configuration reset"})json";
|
||||
content_type = "application/json";
|
||||
} break;
|
||||
case REQ_REBOOT: {
|
||||
restartMCU();
|
||||
} break;
|
||||
}
|
||||
|
||||
client.println("HTTP/1.1 " + responce);
|
||||
client.println("Content-type:" + content_type);
|
||||
client.println();
|
||||
client.println(content);
|
||||
// break out of the while loop:
|
||||
break;
|
||||
} else { // if you got a newline, then clear currentLine:
|
||||
currentLine = "";
|
||||
}
|
||||
} else if (c != '\r') { // if you got anything else but a carriage return character,
|
||||
currentLine += c; // add it to the end of the currentLine
|
||||
}
|
||||
|
||||
if (currentLine.indexOf("GET /board_info.json") >= 0) {
|
||||
req = REQ_BOARD_INFO;
|
||||
} else if (currentLine.indexOf(" /config") >= 0) {
|
||||
req = REQ_CONFIG;
|
||||
int idx = currentLine.indexOf("?");
|
||||
config_line = "&" + currentLine.substring(idx+1, currentLine.lastIndexOf(' ')) + "&";
|
||||
} else if (currentLine.indexOf(" /reset") >= 0) {
|
||||
req = REQ_RESET;
|
||||
} else if (currentLine.indexOf(" /reboot") >= 0) {
|
||||
req = REQ_REBOOT;
|
||||
}
|
||||
}
|
||||
}
|
||||
client.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String urlDecode(const String& text)
|
||||
{
|
||||
String decoded = "";
|
||||
char temp[] = "0x00";
|
||||
unsigned int len = text.length();
|
||||
unsigned int i = 0;
|
||||
while (i < len) {
|
||||
char decodedChar;
|
||||
char encodedChar = text.charAt(i++);
|
||||
if ((encodedChar == '%') && (i + 1 < len)) {
|
||||
temp[2] = text.charAt(i++);
|
||||
temp[3] = text.charAt(i++);
|
||||
|
||||
decodedChar = strtol(temp, NULL, 16);
|
||||
} else {
|
||||
if (encodedChar == '+') {
|
||||
decodedChar = ' ';
|
||||
} else {
|
||||
decodedChar = encodedChar;
|
||||
}
|
||||
}
|
||||
decoded += decodedChar;
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
|
||||
String urlFindArg(const String& url, const String& arg)
|
||||
{
|
||||
int s = url.indexOf("&" + arg + "=");
|
||||
if (s < 0)
|
||||
return "";
|
||||
int s_len = arg.length() + 2;
|
||||
int e = url.indexOf('&', s + s_len);
|
||||
return urlDecode(url.substring(s + s_len, e));
|
||||
}
|
||||
|
||||
void enterConnectNet() {
|
||||
BlynkState::set(MODE_CONNECTING_NET);
|
||||
DEBUG_PRINT(String("Connecting to WiFi: ") + configStore.wifiSSID);
|
||||
|
||||
WiFi.end();
|
||||
|
||||
unsigned long timeoutMs = millis() + WIFI_NET_CONNECT_TIMEOUT;
|
||||
while ((timeoutMs > millis()) && (WiFi.status() != WL_CONNECTED))
|
||||
{
|
||||
WiFi.begin(configStore.wifiSSID, configStore.wifiPass);
|
||||
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.end();
|
||||
delay(1000);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/**************************************************************
|
||||
* 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 <FlashStorage.h>
|
||||
FlashStorage(flash, ConfigStore);
|
||||
|
||||
void config_load()
|
||||
{
|
||||
memset(&configStore, 0, sizeof(configStore));
|
||||
configStore = flash.read();
|
||||
if (configStore.magic != configDefault.magic) {
|
||||
DEBUG_PRINT("Using default config.");
|
||||
configStore = configDefault;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool config_save()
|
||||
{
|
||||
flash.write(configStore);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool config_init()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
/**************************************************************
|
||||
* 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);
|
||||
}
|
||||
|
||||
#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() {
|
||||
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
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* OTA is not ready for this board yet.
|
||||
* It could be derived from:
|
||||
* https://www.hackster.io/flower-platform/program-mkr-over-the-air-goodies-voice-control-etc-562e9a
|
||||
*/
|
||||
|
||||
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_ERROR);
|
||||
}
|
||||
|
||||
@@ -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_PULLDOWN);
|
||||
#endif
|
||||
attachInterrupt(BOARD_BUTTON_PIN, button_change, CHANGE);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* General options
|
||||
*/
|
||||
|
||||
#define BOARD_FIRMWARE_VERSION "1.0.1"
|
||||
#define BOARD_HARDWARE_VERSION "1.0.0"
|
||||
|
||||
#define BOARD_NAME "Product Name" // Name of your product. Should match App Export request info.
|
||||
#define BOARD_VENDOR "Company Name" // Name of your company. Should match App Export request info.
|
||||
#define BOARD_TEMPLATE_ID "TMPL0000" // ID of the Tile Template. Can be found in Tile Template Settings
|
||||
|
||||
#define PRODUCT_WIFI_SSID "Our Product" // Name of the device, to be displayed during configuration. Should match export request info.
|
||||
#define BOARD_CONFIG_AP_URL "our-product" // Config page will be available in a browser at 'http://our-product.local/'
|
||||
|
||||
/*
|
||||
* 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 6 // Set LED pin - if you have a simple 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 you need to inverse LED signal
|
||||
#define BOARD_LED_BRIGHTNESS 64 // 0..255 brightness control
|
||||
|
||||
#elif defined(USE_MKR1000_BOARD)
|
||||
|
||||
// Example configuration for MKR1000 Board
|
||||
#define BOARD_BUTTON_PIN 0
|
||||
#define BOARD_BUTTON_ACTIVE_LOW true
|
||||
|
||||
#define BOARD_LED_PIN 6
|
||||
#define BOARD_LED_INVERSE false
|
||||
|
||||
#else
|
||||
#error "No board selected"
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Advanced options
|
||||
*/
|
||||
|
||||
#define BUTTON_HOLD_TIME_INDICATION 3000
|
||||
#define BUTTON_HOLD_TIME_ACTION 10000
|
||||
|
||||
#define BOARD_PWM_MAX 255
|
||||
|
||||
#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
|
||||
#define USE_TIMER_FIVE
|
||||
|
||||
#if defined(APP_DEBUG)
|
||||
#define DEBUG_PRINT(...) BLYNK_LOG1(__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_PRINT(...)
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*************************************************************
|
||||
This is a DEMO. You can use it only for development and testing.
|
||||
You should open Setting.h and modify General options.
|
||||
|
||||
If you would like to add these features to your product,
|
||||
please contact Blynk for Businesses:
|
||||
|
||||
http://www.blynk.io/
|
||||
|
||||
This example requires some additional libraries:
|
||||
https://github.com/cmaglie/FlashStorage
|
||||
https://github.com/michael71/Timer5
|
||||
|
||||
*************************************************************/
|
||||
|
||||
#define USE_MKR1000_BOARD
|
||||
|
||||
#define APP_DEBUG // Comment this out to disable debug prints
|
||||
|
||||
#define BLYNK_PRINT Serial
|
||||
|
||||
#include "BlynkProvisioning.h"
|
||||
|
||||
void setup() {
|
||||
delay(500);
|
||||
Serial.begin(115200);
|
||||
|
||||
BlynkProvisioning.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// This handles the network and cloud connection
|
||||
BlynkProvisioning.run();
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user