Browse Source

Added OTA and other small changes

fastled
Xose Pérez 8 years ago
parent
commit
e1af27ad7b
11 changed files with 825 additions and 56 deletions
  1. +16
    -13
      README.md
  2. +325
    -0
      lib/ArduinoOTA/ArduinoOTA.cpp
  3. +69
    -0
      lib/ArduinoOTA/ArduinoOTA.h
  4. +54
    -0
      lib/ArduinoOTA/examples/BasicOTA/BasicOTA.ino
  5. +68
    -0
      lib/ArduinoOTA/examples/OTALeds/OTALeds.ino
  6. +26
    -0
      lib/ArduinoOTA/keywords.txt
  7. +9
    -0
      lib/ArduinoOTA/library.properties
  8. +78
    -0
      lib/DebounceEvent/DebounceEvent.cpp
  9. +49
    -0
      lib/DebounceEvent/DebounceEvent.h
  10. +14
    -2
      platformio.ini
  11. +117
    -41
      src/code.ino

+ 16
- 13
README.md View File

@ -1,14 +1,14 @@
# ITead Sonoff Custom Firmware
# Espurna
This is a custom C firmware for [ITead Sonoff][1] Smart WiFi Switch. This device
has an ESP8266 on board with a 8Mbit flash memory chip, a mains to 3V3 transformer
and a relay (GPIO12). It also features a button (GPIO0), a LED (GPIO13) and
an unpopulated header you can use to reprogram it.
Espurna ("spark" in Catalan) is a custom C firmware for [ITead Sonoff][1] Smart WiFi Switch.
This device has an ESP8266 on board with a 8Mbit flash memory chip, a mains to 3V3 transformer
and a relay (GPIO12). It also features a button (GPIO0), a LED (GPIO13) and an unpopulated header you can use to reprogram it.
## Features
* WebServer for configuration and simple relay toggle
* You can configure up to 3 WIFI networks
* Flashing firmware Over-The-Air (OTA)
* Up to 3 configurable WIFI networks
* MQTT support with configurable host and topic
* Manual switch ON/OFF with button
* Visual status of the connection via the LED
@ -22,24 +22,27 @@ in-line with the button. They are (from the button outwards):
* RX
* TX
* GND
* MTNS
* GPIO14
Last one is not necessary. Mind it's a **3V3 device**, if connected to 5V you will
probably fry it. Button is connected to GPIO0 on the ESP8266 chip, so to enter
flash mode you have to hold the button pressed while powering on the board, then
you can realease it again.
## Firmware
The project is ready to be build using [PlatformIO][2].
Please refer to their web page for instructions on how to install the builder.
Once installed:
```bash
> platformio init -b esp01_1m
> platformio run
> platformio run --target upload
> platformio run --target uploadfs
> platformio run --target upload -e wire
> platformio run --target uploadfs -e wire
```
Once you have flashed it you can flash it again over-the-air using the ```ota``` environment:
```bash
> platformio run --target upload -e ota
> platformio run --target uploadfs -e ota
```
Library dependencies are automatically managed via PlatformIO Library Manager.


+ 325
- 0
lib/ArduinoOTA/ArduinoOTA.cpp View File

@ -0,0 +1,325 @@
#ifndef LWIP_OPEN_SRC
#define LWIP_OPEN_SRC
#endif
#include <functional>
#include <WiFiUdp.h>
#include "ArduinoOTA.h"
#include "MD5Builder.h"
extern "C" {
#include "osapi.h"
#include "ets_sys.h"
#include "user_interface.h"
}
#include "lwip/opt.h"
#include "lwip/udp.h"
#include "lwip/inet.h"
#include "lwip/igmp.h"
#include "lwip/mem.h"
#include "include/UdpContext.h"
#include <ESP8266mDNS.h>
#ifdef DEBUG_ESP_OTA
#ifdef DEBUG_ESP_PORT
#define OTA_DEBUG DEBUG_ESP_PORT
#endif
#endif
ArduinoOTAClass::ArduinoOTAClass()
: _port(0)
, _udp_ota(0)
, _initialized(false)
, _state(OTA_IDLE)
, _size(0)
, _cmd(0)
, _ota_port(0)
, _start_callback(NULL)
, _end_callback(NULL)
, _error_callback(NULL)
, _progress_callback(NULL)
{
}
ArduinoOTAClass::~ArduinoOTAClass(){
if(_udp_ota){
_udp_ota->unref();
_udp_ota = 0;
}
}
void ArduinoOTAClass::onStart(OTA_CALLBACK(fn)) {
_start_callback = fn;
}
void ArduinoOTAClass::onEnd(OTA_CALLBACK(fn)) {
_end_callback = fn;
}
void ArduinoOTAClass::onProgress(OTA_CALLBACK_PROGRESS(fn)) {
_progress_callback = fn;
}
void ArduinoOTAClass::onError(OTA_CALLBACK_ERROR(fn)) {
_error_callback = fn;
}
void ArduinoOTAClass::setPort(uint16_t port) {
if (!_initialized && !_port && port) {
_port = port;
}
}
void ArduinoOTAClass::setHostname(const char * hostname) {
if (!_initialized && !_hostname.length() && hostname) {
_hostname = hostname;
}
}
void ArduinoOTAClass::setPassword(const char * password) {
if (!_initialized && !_password.length() && password) {
_password = password;
}
}
void ArduinoOTAClass::begin() {
if (_initialized)
return;
if (!_hostname.length()) {
char tmp[15];
sprintf(tmp, "esp8266-%06x", ESP.getChipId());
_hostname = tmp;
}
if (!_port) {
_port = 8266;
}
if(_udp_ota){
_udp_ota->unref();
_udp_ota = 0;
}
_udp_ota = new UdpContext;
_udp_ota->ref();
if(!_udp_ota->listen(*IP_ADDR_ANY, _port))
return;
_udp_ota->onRx(std::bind(&ArduinoOTAClass::_onRx, this));
MDNS.begin(_hostname.c_str());
if (_password.length()) {
MDNS.enableArduino(_port, true);
} else {
MDNS.enableArduino(_port);
}
_initialized = true;
_state = OTA_IDLE;
#ifdef OTA_DEBUG
OTA_DEBUG.printf("OTA server at: %s.local:%u\n", _hostname.c_str(), _port);
#endif
}
int ArduinoOTAClass::parseInt(){
char data[16];
uint8_t index = 0;
char value;
while(_udp_ota->peek() == ' ') _udp_ota->read();
while(true){
value = _udp_ota->peek();
if(value < '0' || value > '9'){
data[index++] = '\0';
return atoi(data);
}
data[index++] = _udp_ota->read();
}
return 0;
}
String ArduinoOTAClass::readStringUntil(char end){
String res = "";
char value;
while(true){
value = _udp_ota->read();
if(value == '\0' || value == end){
return res;
}
res += value;
}
return res;
}
void ArduinoOTAClass::_onRx(){
if(!_udp_ota->next()) return;
ip_addr_t ota_ip;
if (_state == OTA_IDLE) {
int cmd = parseInt();
if (cmd != U_FLASH && cmd != U_SPIFFS)
return;
_ota_ip = _udp_ota->getRemoteAddress();
_cmd = cmd;
_ota_port = parseInt();
_size = parseInt();
_udp_ota->read();
_md5 = readStringUntil('\n');
_md5.trim();
if(_md5.length() != 32)
return;
ota_ip.addr = (uint32_t)_ota_ip;
if (_password.length()){
MD5Builder nonce_md5;
nonce_md5.begin();
nonce_md5.add(String(micros()));
nonce_md5.calculate();
_nonce = nonce_md5.toString();
char auth_req[38];
sprintf(auth_req, "AUTH %s", _nonce.c_str());
_udp_ota->append((const char *)auth_req, strlen(auth_req));
_udp_ota->send(&ota_ip, _udp_ota->getRemotePort());
_state = OTA_WAITAUTH;
return;
} else {
_udp_ota->append("OK", 2);
_udp_ota->send(&ota_ip, _udp_ota->getRemotePort());
_state = OTA_RUNUPDATE;
}
} else if (_state == OTA_WAITAUTH) {
int cmd = parseInt();
if (cmd != U_AUTH) {
_state = OTA_IDLE;
return;
}
_udp_ota->read();
String cnonce = readStringUntil(' ');
String response = readStringUntil('\n');
if (cnonce.length() != 32 || response.length() != 32) {
_state = OTA_IDLE;
return;
}
MD5Builder _passmd5;
_passmd5.begin();
_passmd5.add(_password);
_passmd5.calculate();
String passmd5 = _passmd5.toString();
String challenge = passmd5 + ":" + String(_nonce) + ":" + cnonce;
MD5Builder _challengemd5;
_challengemd5.begin();
_challengemd5.add(challenge);
_challengemd5.calculate();
String result = _challengemd5.toString();
ota_ip.addr = (uint32_t)_ota_ip;
if(result.equals(response)){
_udp_ota->append("OK", 2);
_udp_ota->send(&ota_ip, _udp_ota->getRemotePort());
_state = OTA_RUNUPDATE;
} else {
_udp_ota->append("Authentication Failed", 21);
_udp_ota->send(&ota_ip, _udp_ota->getRemotePort());
if (_error_callback) _error_callback(OTA_AUTH_ERROR);
_state = OTA_IDLE;
}
}
while(_udp_ota->next()) _udp_ota->flush();
}
void ArduinoOTAClass::_runUpdate() {
if (!Update.begin(_size, _cmd)) {
#ifdef OTA_DEBUG
OTA_DEBUG.println("Update Begin Error");
#endif
if (_error_callback) {
_error_callback(OTA_BEGIN_ERROR);
}
_udp_ota->listen(*IP_ADDR_ANY, _port);
_state = OTA_IDLE;
return;
}
Update.setMD5(_md5.c_str());
WiFiUDP::stopAll();
WiFiClient::stopAll();
if (_start_callback) {
_start_callback();
}
if (_progress_callback) {
_progress_callback(0, _size);
}
WiFiClient client;
if (!client.connect(_ota_ip, _ota_port)) {
#ifdef OTA_DEBUG
OTA_DEBUG.printf("Connect Failed\n");
#endif
_udp_ota->listen(*IP_ADDR_ANY, _port);
if (_error_callback) {
_error_callback(OTA_CONNECT_ERROR);
}
_state = OTA_IDLE;
}
uint32_t written, total = 0;
while (!Update.isFinished() && client.connected()) {
int waited = 1000;
while (!client.available() && waited--)
delay(1);
if (!waited){
#ifdef OTA_DEBUG
OTA_DEBUG.printf("Receive Failed\n");
#endif
_udp_ota->listen(*IP_ADDR_ANY, _port);
if (_error_callback) {
_error_callback(OTA_RECEIVE_ERROR);
}
_state = OTA_IDLE;
}
written = Update.write(client);
if (written > 0) {
client.print(written, DEC);
total += written;
if(_progress_callback) {
_progress_callback(total, _size);
}
}
}
if (Update.end()) {
client.print("OK");
client.stop();
delay(10);
#ifdef OTA_DEBUG
OTA_DEBUG.printf("Update Success\nRebooting...\n");
#endif
if (_end_callback) {
_end_callback();
}
ESP.restart();
} else {
_udp_ota->listen(*IP_ADDR_ANY, _port);
if (_error_callback) {
_error_callback(OTA_END_ERROR);
}
Update.printError(client);
#ifdef OTA_DEBUG
Update.printError(OTA_DEBUG);
#endif
_state = OTA_IDLE;
}
}
void ArduinoOTAClass::handle() {
if (_state == OTA_RUNUPDATE) {
_runUpdate();
_state = OTA_IDLE;
}
}
ArduinoOTAClass ArduinoOTA;

+ 69
- 0
lib/ArduinoOTA/ArduinoOTA.h View File

@ -0,0 +1,69 @@
#ifndef __ARDUINO_OTA_H
#define __ARDUINO_OTA_H
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
class UdpContext;
#define OTA_CALLBACK(callback) void (*callback)()
#define OTA_CALLBACK_PROGRESS(callback) void (*callback)(unsigned int, unsigned int)
#define OTA_CALLBACK_ERROR(callback) void (*callback)(ota_error_t)
typedef enum {
OTA_IDLE,
OTA_WAITAUTH,
OTA_RUNUPDATE
} ota_state_t;
typedef enum {
OTA_AUTH_ERROR,
OTA_BEGIN_ERROR,
OTA_CONNECT_ERROR,
OTA_RECEIVE_ERROR,
OTA_END_ERROR
} ota_error_t;
class ArduinoOTAClass
{
public:
ArduinoOTAClass();
~ArduinoOTAClass();
void setPort(uint16_t port);
void setHostname(const char *hostname);
void setPassword(const char *password);
void onStart(OTA_CALLBACK(fn));
void onEnd(OTA_CALLBACK(fn));
void onProgress(OTA_CALLBACK_PROGRESS(fn));
void onError(OTA_CALLBACK_ERROR (fn));
void begin();
void handle();
private:
int _port;
String _password;
String _hostname;
String _nonce;
UdpContext *_udp_ota;
bool _initialized;
ota_state_t _state;
int _size;
int _cmd;
int _ota_port;
IPAddress _ota_ip;
String _md5;
OTA_CALLBACK(_start_callback);
OTA_CALLBACK(_end_callback);
OTA_CALLBACK_ERROR(_error_callback);
OTA_CALLBACK_PROGRESS(_progress_callback);
void _runUpdate(void);
void _onRx(void);
int parseInt(void);
String readStringUntil(char end);
};
extern ArduinoOTAClass ArduinoOTA;
#endif /* __ARDUINO_OTA_H */

+ 54
- 0
lib/ArduinoOTA/examples/BasicOTA/BasicOTA.ino View File

@ -0,0 +1,54 @@
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
const char* ssid = "..........";
const char* password = "..........";
void setup() {
Serial.begin(115200);
Serial.println("Booting");
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("Connection Failed! Rebooting...");
delay(5000);
ESP.restart();
}
// Port defaults to 8266
// ArduinoOTA.setPort(8266);
// Hostname defaults to esp8266-[ChipID]
// ArduinoOTA.setHostname("myesp8266");
// No authentication by default
// ArduinoOTA.setPassword((const char *)"123");
ArduinoOTA.onStart([]() {
Serial.println("Start");
});
ArduinoOTA.onEnd([]() {
Serial.println("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
ArduinoOTA.begin();
Serial.println("Ready");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void loop() {
ArduinoOTA.handle();
}

+ 68
- 0
lib/ArduinoOTA/examples/OTALeds/OTALeds.ino View File

@ -0,0 +1,68 @@
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
const char* ssid = "...";
const char* password = "...";
const char* host = "OTA-LEDS";
int led_pin = 13;
#define N_DIMMERS 3
int dimmer_pin[] = {14, 5, 15};
void setup() {
Serial.begin(115200);
/* switch on led */
pinMode(led_pin, OUTPUT);
digitalWrite(led_pin, LOW);
Serial.println("Booting");
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.waitForConnectResult() != WL_CONNECTED){
WiFi.begin(ssid, password);
Serial.println("Retrying connection...");
}
/* switch off led */
digitalWrite(led_pin, HIGH);
/* configure dimmers, and OTA server events */
analogWriteRange(1000);
analogWrite(led_pin,990);
for (int i=0; i<N_DIMMERS; i++)
{
pinMode(dimmer_pin[i], OUTPUT);
analogWrite(dimmer_pin[i],50);
}
ArduinoOTA.setHostname(host);
ArduinoOTA.onStart([]() { // switch off all the PWMs during upgrade
for(int i=0; i<N_DIMMERS;i++)
analogWrite(dimmer_pin[i], 0);
analogWrite(led_pin,0);
});
ArduinoOTA.onEnd([]() { // do a fancy thing with our board led at end
for (int i=0;i<30;i++)
{
analogWrite(led_pin,(i*100) % 1001);
delay(50);
}
});
ArduinoOTA.onError([](ota_error_t error) { ESP.restart(); });
/* setup the OTA server */
ArduinoOTA.begin();
Serial.println("Ready");
}
void loop() {
ArduinoOTA.handle();
}

+ 26
- 0
lib/ArduinoOTA/keywords.txt View File

@ -0,0 +1,26 @@
#######################################
# Syntax Coloring Map For Ultrasound
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
ArduinoOTA KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
begin KEYWORD2
setup KEYWORD2
handle KEYWORD2
onStart KEYWORD2
onEnd KEYWORD2
onError KEYWORD2
onProgress KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################

+ 9
- 0
lib/ArduinoOTA/library.properties View File

@ -0,0 +1,9 @@
name=ArduinoOTA
version=1.0
author=Ivan Grokhotkov and Miguel Angel Ajo
maintainer=Ivan Grokhtkov <ivan@esp8266.com>
sentence=Enables Over The Air upgrades, via wifi and espota.py UDP request/TCP download.
paragraph=With this library you can enable your sketch to be upgraded over network. Includes mdns anounces to get discovered by the arduino IDE.
category=Communication
url=
architectures=esp8266

+ 78
- 0
lib/DebounceEvent/DebounceEvent.cpp View File

@ -0,0 +1,78 @@
/*
Debounce buttons and trigger events
Copyright (C) 2015 by Xose Pérez <xose dot perez at gmail dot com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Arduino.h>
#include "DebounceEvent.h"
DebounceEvent::DebounceEvent(uint8_t pin, callback_t callback, uint8_t defaultStatus, unsigned long delay) {
// store configuration
_pin = pin;
_status = _defaultStatus = defaultStatus;
_delay = delay;
_callback = callback;
// set up button
if (_defaultStatus == LOW) {
pinMode(_pin, INPUT);
} else {
pinMode(_pin, INPUT_PULLUP);
}
}
bool DebounceEvent::loop() {
// holds whether status has changed or not
bool changed = false;
if (digitalRead(_pin) != _status) {
delay(_delay);
uint8_t newStatus = digitalRead(_pin);
if (newStatus != _status) {
changed = true;
_status = newStatus;
// raise events if callback defined
if (_callback) {
// raise change event
_callback(_pin, EVENT_CHANGED);
if (_status == _defaultStatus) {
// raise released event
_callback(_pin, EVENT_RELEASED);
} else {
// raise pressed event
_callback(_pin, EVENT_PRESSED);
}
}
}
}
return changed;
}
bool DebounceEvent::pressed() {
return (_status != _defaultStatus);
}

+ 49
- 0
lib/DebounceEvent/DebounceEvent.h View File

@ -0,0 +1,49 @@
/*
Debounce buttons and trigger events
Copyright (C) 2015 by Xose Pérez <xose dot perez at gmail dot com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _DEBOUNCE_EVENT_h
#define _DEBOUNCE_EVENT_h
#define DEBOUNCE_DELAY 100
#define EVENT_CHANGED 0
#define EVENT_PRESSED 1
#define EVENT_RELEASED 2
typedef void(*callback_t)(uint8_t pin, uint8_t event);
class DebounceEvent {
private:
uint8_t _pin;
uint8_t _status;
uint8_t _defaultStatus;
unsigned long _delay;
callback_t _callback;
public:
DebounceEvent(uint8_t pin, callback_t callback = false, uint8_t defaultStatus = HIGH, unsigned long delay = DEBOUNCE_DELAY);
bool pressed();
bool loop();
};
#endif

+ 14
- 2
platformio.ini View File

@ -17,8 +17,20 @@
# Automatic targets - enable auto-uploading
# targets = upload
[env:nodemcuv2]
[platformio]
#env_default = wire
[env:wire]
platform = espressif
framework = arduino
board = esp01_1m
lib_install = 89
[env:ota]
platform = espressif
framework = arduino
board = esp01_1m
libs=89
lib_install = 89
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266

+ 117
- 41
src/code.ino View File

@ -23,6 +23,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <PubSubClient.h>
#include <DebounceEvent.h>
#include <ArduinoOTA.h>
#include "FS.h"
// -----------------------------------------------------------------------------
@ -31,20 +33,24 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define DEBUG
#define APP_NAME "Espurna"
#define MAX_VERSION 0
#define MIN_VERSION 9
#define APP_AUTHOR "xose.perez@gmail.com"
#define APP_WEBSITE "http://tinkerman.cat"
#define MODEL "SONOFF"
#define BUTTON_PIN 0
#define RELAY_PIN 12
#define LED_PIN 13
#define DEBOUNCE_COUNTER_START 150
#define AP_PASS "fibonacci"
#define OTA_PASS "fibonacci"
#define BUFFER_SIZE 1024
#define CONFIG_PATH "/.config"
#define WIFI_CONNECT_TIMEOUT 5000
#define WIFI_RECONNECT_DELAY 30000
#define MQTT_RECONNECT_DELAY 30000
#define WIFI_RECONNECT_DELAY 5000
#define MQTT_RECONNECT_DELAY 10000
#define NETWORK_BUFFER 3
// -----------------------------------------------------------------------------
@ -55,9 +61,7 @@ ESP8266WebServer server(80);
WiFiClient client;
PubSubClient mqtt(client);
bool relayOn = false;
char identifier[] = "SONOFF_0000";
bool identifierSet = false;
char identifier[20] = {0};
byte network = 0;
String config_ssid[NETWORK_BUFFER];
@ -69,6 +73,8 @@ String mqtt_port = "1883";
char mqtt_subscribe_to[30];
char mqtt_publish_to[30];
DebounceEvent button1 = false;
// -----------------------------------------------------------------------------
// Relay
// -----------------------------------------------------------------------------
@ -81,8 +87,6 @@ void switchRelayOn() {
mqtt.publish(mqtt_publish_to, "1");
}
digitalWrite(RELAY_PIN, HIGH);
digitalWrite(LED_PIN, HIGH);
relayOn = true;
}
void switchRelayOff() {
@ -93,12 +97,10 @@ void switchRelayOff() {
mqtt.publish(mqtt_publish_to, "0");
}
digitalWrite(RELAY_PIN, LOW);
digitalWrite(LED_PIN, LOW);
relayOn = false;
}
void toggleRelay() {
if (relayOn) {
if (digitalRead(RELAY_PIN)) {
switchRelayOff();
} else {
switchRelayOn();
@ -292,15 +294,13 @@ void webServerLoop() {
// -----------------------------------------------------------------------------
char * getIdentifier() {
if (!identifierSet) {
if (identifier[0] == 0) {
uint8_t mac[WL_MAC_ADDR_LENGTH];
WiFi.softAPmacAddress(mac);
String macID = String(mac[WL_MAC_ADDR_LENGTH - 2], HEX) + String(mac[WL_MAC_ADDR_LENGTH - 1], HEX);
macID.toUpperCase();
for (byte i=0; i<4; i++) {
identifier[7+i] = macID.charAt(i);
}
identifierSet = true;
String name = MODEL + String("_") + String(mac[WL_MAC_ADDR_LENGTH - 2], HEX) + String(mac[WL_MAC_ADDR_LENGTH - 1], HEX);
name.toUpperCase();
byte length = std::min(20, (int) name.length() + 1);
name.toCharArray(identifier, length);
}
return identifier;
}
@ -327,6 +327,7 @@ void wifiSetup() {
// Wait
unsigned long timeout = millis() + WIFI_CONNECT_TIMEOUT;
while (timeout > millis()) {
showStatus();
if (WiFi.status() == WL_CONNECTED) break;
delay(100);
}
@ -439,6 +440,7 @@ void mqttConnect() {
Serial.println(mqtt_subscribe_to);
#endif
mqtt.publish(mqtt_publish_to, "HOLA");
mqtt.subscribe(mqtt_subscribe_to);
@ -538,45 +540,118 @@ bool loadConfig() {
}
// -----------------------------------------------------------------------------
// Generic methods
// OTA
// -----------------------------------------------------------------------------
void OTASetup() {
// Port defaults to 8266
ArduinoOTA.setPort(8266);
// Hostname defaults to esp8266-[ChipID]
ArduinoOTA.setHostname(getIdentifier());
// No authentication by default
ArduinoOTA.setPassword((const char *) OTA_PASS);
ArduinoOTA.onStart([]() {
#ifdef DEBUG
Serial.println("OTA - Start");
#endif
});
ArduinoOTA.onEnd([]() {
#ifdef DEBUG
Serial.println("OTA - End");
#endif
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
#ifdef DEBUG
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
#endif
});
ArduinoOTA.onError([](ota_error_t error) {
#ifdef DEBUG
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
#endif
});
ArduinoOTA.begin();
}
void OTALoop() {
ArduinoOTA.handle();
}
// -----------------------------------------------------------------------------
// Hardware (buttons, LEDs,...)
// -----------------------------------------------------------------------------
void hardwareSetup() {
Serial.begin(115200);
pinMode(RELAY_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(LED_PIN, OUTPUT);
SPIFFS.begin();
button1 = DebounceEvent(BUTTON_PIN);
}
void buttonLoop() {
static int lastButtonState = HIGH;
static int debounceCounter = 0;
if (debounceCounter > 0) {
if (debounceCounter == 1) {
int newButtonState = lastButtonState == HIGH ? LOW : HIGH;
if (newButtonState == LOW) {
toggleRelay();
}
lastButtonState = newButtonState;
}
debounceCounter--;
void blink(unsigned long delayOff, unsigned long delayOn) {
static unsigned long next = millis();
static bool status = HIGH;
if (next < millis()) {
status = !status;
digitalWrite(LED_PIN, status);
next += ((status) ? delayOff : delayOn);
}
}
} else if (lastButtonState != digitalRead(BUTTON_PIN)) {
debounceCounter = DEBOUNCE_COUNTER_START;
void showStatus() {
if (WiFi.status() == WL_CONNECTED) {
blink(5000, 500);
} else {
blink(500, 500);
}
}
void hardwareLoop() {
if (button1.loop()) {
if (!button1.pressed()) toggleRelay();
}
showStatus();
}
// -----------------------------------------------------------------------------
// Booting
// -----------------------------------------------------------------------------
void welcome() {
Serial.println();
Serial.print(APP_NAME);
Serial.print(" ");
Serial.print(MAX_VERSION);
Serial.print(".");
Serial.println(MIN_VERSION);
Serial.println(APP_WEBSITE);
Serial.println(APP_AUTHOR);
Serial.println();
Serial.print("Device: ");
Serial.println(getIdentifier());
Serial.println();
}
void setup() {
hardwareSetup();
SPIFFS.begin();
delay(5000);
welcome();
OTASetup();
switchRelayOff();
loadConfig();
wifiSetup();
@ -585,9 +660,10 @@ void setup() {
}
void loop() {
OTALoop();
wifiLoop();
webServerLoop();
mqttLoop();
buttonLoop();
hardwareLoop();
delay(1);
}

Loading…
Cancel
Save