Browse Source

Complete update

fastled
Xose Pérez 8 years ago
parent
commit
88de7f51d0
31 changed files with 746 additions and 1017 deletions
  1. +7
    -1
      .gitignore
  2. +3
    -0
      .gitmodules
  3. +5
    -4
      code/deploy
  4. +61
    -12
      code/gulpfile.js
  5. +2
    -1
      code/html/custom.css
  6. +64
    -42
      code/html/custom.js
  7. +1
    -1
      code/html/fsversion
  8. +13
    -58
      code/html/index.html
  9. +1
    -0
      code/lib/JustWifi
  10. +0
    -214
      code/lib/JustWifi/JustWifi.cpp
  11. +0
    -99
      code/lib/JustWifi/JustWifi.h
  12. +1
    -0
      code/main
  13. +4
    -7
      code/package.json
  14. +28
    -0
      code/pio_hooks.py
  15. +35
    -80
      code/platformio.ini
  16. +1
    -1
      code/src/button.ino
  17. +5
    -0
      code/src/debug.h
  18. +50
    -23
      code/src/defaults.h
  19. +36
    -42
      code/src/dht.ino
  20. +8
    -6
      code/src/emon.ino
  21. +53
    -90
      code/src/main.ino
  22. +51
    -66
      code/src/mqtt.ino
  23. +44
    -0
      code/src/ntp.ino
  24. +11
    -24
      code/src/ota.ino
  25. +4
    -6
      code/src/relay.ino
  26. +28
    -18
      code/src/rf.ino
  27. +2
    -6
      code/src/settings.ino
  28. +1
    -1
      code/src/version.h
  29. +17
    -100
      code/src/webserver.ino
  30. +107
    -0
      code/src/websockets.ino
  31. +103
    -115
      code/src/wifi.ino

+ 7
- 1
.gitignore View File

@ -2,6 +2,12 @@
*.b#? *.b#?
.modgit .modgit
firmware* firmware*
.pioenvs
*.gch
.pio*
.clang_complete
.gcc-flags.json
.sconsign.dblite .sconsign.dblite
credentials.h
.build
node_modules node_modules
code/data

+ 3
- 0
.gitmodules View File

@ -10,3 +10,6 @@
[submodule "code/vendor/RemoteSwitch-arduino-library"] [submodule "code/vendor/RemoteSwitch-arduino-library"]
path = code/vendor/RemoteSwitch-arduino-library path = code/vendor/RemoteSwitch-arduino-library
url = https://github.com/jccprj/RemoteSwitch-arduino-library url = https://github.com/jccprj/RemoteSwitch-arduino-library
[submodule "code/lib/JustWifi"]
path = code/lib/JustWifi
url = https://bitbucket.org/xoseperez/justwifi

+ 5
- 4
code/deploy View File

@ -21,7 +21,7 @@
MQTT_HOST=192.168.1.10 MQTT_HOST=192.168.1.10
function help() { function help() {
echo "Syntax: $0 <device>"
echo "Syntax: $0 <device> <target>"
devices devices
} }
@ -40,14 +40,15 @@ function valid_ip() {
} }
# Check arguments # Check arguments
if [ "$#" -ne 1 ]; then
if [ "$#" -ne 2 ]; then
help help
exit 1 exit 1
fi fi
device=$1-device device=$1-device
target=$2
# Get IP # Get IP
topic=`cat platformio.ini | grep $device -A 10 | grep "topic" | cut -d' ' -f3`
topic=`cat platformio.ini | grep $device -A 2 | grep "topic" | cut -d' ' -f3`
if [ "$topic" == "" ]; then if [ "$topic" == "" ]; then
echo "Unknown device $device or topic not defined" echo "Unknown device $device or topic not defined"
devices devices
@ -60,4 +61,4 @@ if valid_ip $ip; then
exit 3 exit 3
fi fi
platformio run -vv -e $device --target upload --upload-port $ip
platformio run -vv -e $device --target $target --upload-port $ip

+ 61
- 12
code/gulpfile.js View File

@ -1,16 +1,43 @@
var gulp = require('gulp');
var plumber = require('gulp-plumber');
var htmlmin = require('gulp-htmlmin');
var cleancss = require('gulp-clean-css');
var uglify = require('gulp-uglify');
var gzip = require('gulp-gzip');
var del = require('del');
var useref = require('gulp-useref');
var gulpif = require('gulp-if');
/*
ESP8266 file system builder
Copyright (C) 2016 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/>.
*/
// -----------------------------------------------------------------------------
// File system builder
// -----------------------------------------------------------------------------
const gulp = require('gulp');
const plumber = require('gulp-plumber');
const htmlmin = require('gulp-htmlmin');
const cleancss = require('gulp-clean-css');
const uglify = require('gulp-uglify');
const gzip = require('gulp-gzip');
const del = require('del');
const useref = require('gulp-useref');
const gulpif = require('gulp-if');
const inline = require('gulp-inline');
/* Clean destination folder */ /* Clean destination folder */
gulp.task('clean', function() { gulp.task('clean', function() {
return del(['data/*']);
del(['data/*']);
return true;
}); });
/* Copy static files */ /* Copy static files */
@ -22,6 +49,26 @@ gulp.task('files', function() {
.pipe(gulp.dest('data/')); .pipe(gulp.dest('data/'));
}); });
/* Process HTML, CSS, JS --- INLINE --- */
gulp.task('inline', function() {
return gulp.src('html/*.html')
.pipe(inline({
base: 'html/',
js: uglify,
css: cleancss,
disabledTypes: ['svg', 'img']
}))
.pipe(htmlmin({
collapseWhitespace: true,
removeComments: true,
minifyCSS: true,
minifyJS: true
}))
.pipe(gzip())
.pipe(gulp.dest('data'));
})
/* Process HTML, CSS, JS */ /* Process HTML, CSS, JS */
gulp.task('html', function() { gulp.task('html', function() {
return gulp.src('html/*.html') return gulp.src('html/*.html')
@ -39,5 +86,7 @@ gulp.task('html', function() {
.pipe(gulp.dest('data')); .pipe(gulp.dest('data'));
}); });
/* Default Task */
gulp.task('default', ['clean', 'files', 'html']);
/* Build file system */
gulp.task('buildfs', ['clean', 'files', 'html']);
gulp.task('buildfs2', ['clean', 'files', 'inline']);
gulp.task('default', ['buildfs']);

+ 2
- 1
code/html/custom.css View File

@ -51,9 +51,10 @@ div.hint {
margin-top: -5px; margin-top: -5px;
} }
.iPhoneCheckContainer { .iPhoneCheckContainer {
margin-top: -10px;
letter-spacing: 0em; letter-spacing: 0em;
height: 36px; height: 36px;
} }
.iPhoneCheckHandle { .iPhoneCheckHandle {
top: 8px;
top: 9px;
} }

+ 64
- 42
code/html/custom.js View File

@ -1,12 +1,11 @@
var update_timer = null;
var relaySlider;
var websock;
function doUpdate() { function doUpdate() {
var self = $(this); var self = $(this);
self.addClass("loading"); self.addClass("loading");
$.ajax({ $.ajax({
'method': 'POST', 'method': 'POST',
'url': '/post',
'url': '/save',
'dataType': 'json', 'dataType': 'json',
'data': $("#formSave").serializeArray() 'data': $("#formSave").serializeArray()
}).done(function(data) { }).done(function(data) {
@ -28,16 +27,31 @@ function toggleMenu() {
$("#menuLink").toggleClass('active'); $("#menuLink").toggleClass('active');
} }
function parseResponse(data) {
function processData(data) {
// pre-process // pre-process
if ("network" in data) data.network = data.network.toUpperCase();
if ("mqttStatus" in data) data.mqttStatus = data.mqttStatus ? "CONNECTED" : "NOT CONNECTED";
if ("network" in data) {
data.network = data.network.toUpperCase();
}
if ("mqttStatus" in data) {
data.mqttStatus = data.mqttStatus ? "CONNECTED" : "NOT CONNECTED";
}
// relay // relay
if ("relayStatus" in data) { if ("relayStatus" in data) {
$("input[name='relayStatus']") $("input[name='relayStatus']")
.prop("checked", data.relayStatus) .prop("checked", data.relayStatus)
.iphoneStyle({
checkedLabel: 'ON',
uncheckedLabel: 'OFF',
onChange: function(elem, value) {
$.ajax({
'method': 'GET',
'url': value ? '/relay/on' : '/relay/off',
'dataType': 'json'
});
}
})
.iphoneStyle("refresh"); .iphoneStyle("refresh");
} }
@ -49,8 +63,30 @@ function parseResponse(data) {
// automatic assign // automatic assign
Object.keys(data).forEach(function(key) { Object.keys(data).forEach(function(key) {
var id = "input[name=" + key + "]";
if ($(id).length) $(id).val(data[key]);
// Look for INPUTs
var element = $("input[name=" + key + "]");
if (element.length > 0) {
if (element.attr('type') == 'checkbox') {
element.prop("checked", data[key] == 1)
.iphoneStyle({
resizeContainer: false,
resizeHandle: false,
checkedLabel: 'ON',
uncheckedLabel: 'OFF'
})
.iphoneStyle("refresh");
} else {
element.val(data[key]);
}
}
// Look for SELECTs
var element = $("select[name=" + key + "]");
if (element.length > 0) {
element.val(data[key]);
}
}); });
// WIFI // WIFI
@ -63,46 +99,32 @@ function parseResponse(data) {
}); });
}; };
if ("updateInterval" in data) {
if (update_timer) clearInterval(update_timer);
if (data.updateInterval > 0) {
update_timer = setInterval(update, data.updateInterval);
}
}
} }
function update() {
$.ajax({
'method': 'GET',
'url': '/status',
'dataType': 'json'
}).done(parseResponse);
function getJson(str) {
try {
return JSON.parse(str);
} catch (e) {
return false;
}
} }
function init() { function init() {
$.ajax({
'method': 'GET',
'url': '/get',
'dataType': 'json'
}).done(parseResponse);
}
$(function() {
$("#menuLink").on('click', toggleMenu); $("#menuLink").on('click', toggleMenu);
$(".button-update").on('click', doUpdate); $(".button-update").on('click', doUpdate);
$(".pure-menu-link").on('click', showPanel); $(".pure-menu-link").on('click', showPanel);
relaySlider = $('#relayStatus').iphoneStyle({
checkedLabel: 'ON',
uncheckedLabel: 'OFF',
onChange: function(elem, value) {
$.ajax({
'method': 'GET',
'url': value ? '/relay/on' : '/relay/off',
'dataType': 'json'
});
setTimeout(update, 200);
}
});
init();
});
var host = window.location.hostname;
websock = new WebSocket('ws://' + host + ':81/');
websock.onopen = function(evt) {};
websock.onclose = function(evt) {};
websock.onerror = function(evt) {};
websock.onmessage = function(evt) {
var data = getJson(evt.data);
if (data) processData(data);
};
}
$(init);

+ 1
- 1
code/html/fsversion View File

@ -1 +1 @@
0.9.7
0.9.8

+ 13
- 58
code/html/index.html View File

@ -44,10 +44,6 @@
<a href="#" class="pure-menu-link" data="panel-mqtt">MQTT</a> <a href="#" class="pure-menu-link" data="panel-mqtt">MQTT</a>
</li> </li>
<li class="pure-menu-item">
<a href="#" class="pure-menu-link" data="panel-rf">RF</a>
</li>
<li class="pure-menu-item"> <li class="pure-menu-item">
<a href="#" class="pure-menu-link" data="panel-power">POWER</a> <a href="#" class="pure-menu-link" data="panel-power">POWER</a>
</li> </li>
@ -87,42 +83,42 @@
<div class="pure-g"> <div class="pure-g">
<label class="pure-u-1 pure-u-sm-1-4" for="hostname">Hostname</label> <label class="pure-u-1 pure-u-sm-1-4" for="hostname">Hostname</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="hostname" disabled>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="hostname" readonly />
</div> </div>
<div class="pure-g"> <div class="pure-g">
<label class="pure-u-1 pure-u-sm-1-4" for="network">Network</label> <label class="pure-u-1 pure-u-sm-1-4" for="network">Network</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="network" disabled>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="network" readonly />
</div> </div>
<div class="pure-g"> <div class="pure-g">
<label class="pure-u-1 pure-u-sm-1-4" for="ip">IP</label> <label class="pure-u-1 pure-u-sm-1-4" for="ip">IP</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="ip" disabled>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="ip" readonly />
</div> </div>
<div class="pure-g"> <div class="pure-g">
<label class="pure-u-1 pure-u-sm-1-4" for="mqtt">MQTT Status</label> <label class="pure-u-1 pure-u-sm-1-4" for="mqtt">MQTT Status</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="mqttStatus" disabled>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="mqttStatus" readonly />
</div> </div>
<div class="pure-g"> <div class="pure-g">
<label class="pure-u-1 pure-u-sm-1-4" for="temperature">Temperature</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="temperature" disabled>
<label class="pure-u-1 pure-u-sm-1-4" for="temperature">Temperature (ºC)</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="temperature" readonly />
</div> </div>
<div class="pure-g"> <div class="pure-g">
<label class="pure-u-1 pure-u-sm-1-4" for="humidity">Humidity</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="humidity" disabled>
<label class="pure-u-1 pure-u-sm-1-4" for="humidity">Humidity (%)</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="humidity" readonly />
</div> </div>
<div class="pure-g"> <div class="pure-g">
<label class="pure-u-1 pure-u-sm-1-4" for="power">Power</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="power" disabled>
<label class="pure-u-1 pure-u-sm-1-4" for="power">Power (W)</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="power" readonly />
</div> </div>
<div class="pure-g"> <div class="pure-g">
<label class="pure-u-1 pure-u-sm-1-4" for="relayStatus">Relay Status</label>
<input class="pure-u-1 pure-u-sm-3-4" type="checkbox" checked="checked" name="relayStatus" id="relayStatus"/>
<div class="pure-u-1 pure-u-sm-1-4"><label for="relayStatus">Relay Status</label></div>
<div class="pure-u-1 pure-u-sm-1-4"><input type="checkbox" name="relayStatus" /></div>
</div> </div>
</fieldset> </fieldset>
@ -137,7 +133,7 @@
<div class="header"> <div class="header">
<h1>WIFI</h1> <h1>WIFI</h1>
<h2>You can configure up to 3 different WiFi networks. The device will try to connect to any of them starting with the first one.</h2>
<h2>You can configure up to 3 different WiFi networks. The device will try to connect in order of signal strength.</h2>
</div> </div>
<div class="page"> <div class="page">
@ -233,46 +229,6 @@
</div> </div>
<div class="panel" id="panel-rf">
<div class="header">
<h1>RADIO</h1>
<h2>
Configure your radio channel and device ID.
</h2>
</div>
<div class="page">
<fieldset>
<div class="pure-g">
<div class="pure-u-1">
<label class="form-label" for="rfChannel">Channel</label>
<div class="hint">This is the 5 bits code (0-31) you define on your remote, usually from a DIP switch.</div>
<input name="rfChannel" type="number" class="pure-u-1-4" min="0" max="30" step="1" tabindex="13"/>
</div>
<div class="pure-u-1">
<label class="form-label" for="rfDevice">Device</label>
<div class="hint">This is the button in your remote that will trigger the switch</div>
<select name="rfDevice" class="pure-u-1-4" tabindex="31">
<option value="0">A</a>
<option value="1">B</a>
<option value="2">C</a>
<option value="3">D</a>
<option value="4">E</a>
</select>
</div>
</div>
</fieldset>
</div>
</div>
<div class="panel" id="panel-power"> <div class="panel" id="panel-power">
<div class="header"> <div class="header">
@ -311,7 +267,6 @@
</div> </div>
</div> </div>
</form> </form>
</div> <!-- content --> </div> <!-- content -->


+ 1
- 0
code/lib/JustWifi

@ -0,0 +1 @@
Subproject commit 012b9773add97ba5675b62e70f14164a38024010

+ 0
- 214
code/lib/JustWifi/JustWifi.cpp View File

@ -1,214 +0,0 @@
/*
JustWifi
Wifi Manager for ESP8266
Copyright (C) 2016 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 "JustWifi.h"
#include <functional>
bool JustWifi::connected() {
return (WiFi.status() == WL_CONNECTED);
}
bool JustWifi::autoConnect() {
unsigned long timeout;
// Return if already connected
if (connected()) return true;
// Try to connect to last successful network
if (WiFi.SSID().length() > 0) {
ETS_UART_INTR_DISABLE();
wifi_station_disconnect();
ETS_UART_INTR_ENABLE();
_doCallback(MESSAGE_AUTO_CONNECTING, (char *) WiFi.SSID().c_str());
WiFi.mode(WIFI_STA);
WiFi.begin();
// Check connection with timeout
timeout = millis();
while (millis() - timeout < _connect_timeout) {
_doCallback(MESSAGE_CONNECT_WAITING);
if (WiFi.status() == WL_CONNECTED) break;
delay(100);
}
if (WiFi.status() == WL_CONNECTED) {
_mode = MODE_STATION;
_doCallback(MESSAGE_CONNECTED);
return true;
}
_doCallback(MESSAGE_AUTO_FAILED);
} else {
_doCallback(MESSAGE_AUTO_NOSSID);
}
_mode = MODE_NONE;
return false;
}
bool JustWifi::connect() {
unsigned long timeout;
// Return if already connected
if (connected()) return true;
// Loop through configured networks
for (unsigned char i=0; i<_network_count; i++) {
// Skip if no SSID defined
if (_network[i].ssid.length() == 0) continue;
// TODO: Static IP options here
// Connect
_doCallback(MESSAGE_CONNECTING, (char *) _network[i].ssid.c_str());
WiFi.begin(_network[i].ssid.c_str(), _network[i].pass.c_str());
// Check connection with timeout
timeout = millis();
while (millis() - timeout < _connect_timeout) {
_doCallback(MESSAGE_CONNECT_WAITING);
if (WiFi.status() == WL_CONNECTED) break;
delay(100);
}
// Get out of the i loop if connected
if (WiFi.status() == WL_CONNECTED) {
break;
} else {
_mode = MODE_NONE;
_doCallback(MESSAGE_CONNECT_FAILED, (char *) _network[i].ssid.c_str());
}
}
if (WiFi.status() == WL_CONNECTED) {
//WiFi.setAutoConnect(true);
//WiFi.setAutoReconnect(true);
_mode = MODE_STATION;
_doCallback(MESSAGE_CONNECTED);
return true;
}
return false;
}
bool JustWifi::startAP(char * ssid, char * pass) {
// Return if already connected
// if (connected()) return true;
// TODO: Static IP options here
_doCallback(MESSAGE_ACCESSPOINT_CREATING);
WiFi.mode(WIFI_AP);
if (strlen(pass) > 0) {
if (strlen(pass) < 8 || strlen(pass) > 63) {
_mode = MODE_NONE;
_doCallback(MESSAGE_ACCESSPOINT_FAILED);
return false;
}
WiFi.softAP(ssid, pass);
} else {
WiFi.softAP(ssid);
}
// TODO: Setup the DNS server redirecting all the queries to this IP
_ssid = String(ssid);
_mode = MODE_ACCESS_POINT;
_doCallback(MESSAGE_ACCESSPOINT_CREATED);
return true;
}
bool JustWifi::disconnect() {
WiFi.disconnect(true);
_mode = MODE_NONE;
_doCallback(MESSAGE_DISCONNECTED);
}
bool JustWifi::cleanNetworks() {
_network_count = 0;
}
bool JustWifi::addNetwork(char * ssid, char * pass) {
if (_network_count == MAX_NETWORKS) return false;
_network[_network_count].ssid = String(ssid);
_network[_network_count].pass = String(pass);
_network_count++;
return true;
}
justwifi_mode_t JustWifi::getMode() {
return _mode;
}
String JustWifi::getIP() {
if (_mode == MODE_STATION) {
return WiFi.localIP().toString();
} else if (_mode == MODE_ACCESS_POINT) {
return WiFi.softAPIP().toString();
}
return String("");
}
String JustWifi::getNetwork() {
if (_mode == MODE_STATION) {
return WiFi.SSID();
}
return _ssid;
}
void JustWifi::setConnectTimeout(unsigned long ms) {
_connect_timeout = ms;
}
void JustWifi::onMessage(TMessageFunction fn) {
_callback = fn;
}
void JustWifi::loop() {
if ((WiFi.status() != WL_CONNECTED) && (_mode == MODE_STATION)) {
_mode = MODE_NONE;
_doCallback(MESSAGE_DISCONNECTED);
}
if ((WiFi.status() == WL_CONNECTED) && (_mode != MODE_STATION)) {
_mode = MODE_STATION;
_doCallback(MESSAGE_CONNECTED);
}
}
void JustWifi::_doCallback(justwifi_messages_t message, char * parameter) {
if (_callback != NULL) _callback(message, parameter);
}

+ 0
- 99
code/lib/JustWifi/JustWifi.h View File

@ -1,99 +0,0 @@
/*
JustWifi
Wifi Manager for ESP8266
Copyright (C) 2016 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 JustWifi_h
#define JustWifi_h
#include <functional>
#include <ESP8266WiFi.h>
extern "C" {
#include "user_interface.h"
}
#define MAX_NETWORKS 3
#define WIFI_CONNECT_TIMEOUT 10000
struct network_t {
String ssid;
String pass;
};
typedef enum {
MODE_NONE,
MODE_STATION,
MODE_ACCESS_POINT
} justwifi_mode_t;
typedef enum {
MESSAGE_AUTO_NOSSID,
MESSAGE_AUTO_CONNECTING,
MESSAGE_AUTO_FAILED,
MESSAGE_CONNECTING,
MESSAGE_CONNECT_WAITING,
MESSAGE_CONNECT_FAILED,
MESSAGE_CONNECTED,
MESSAGE_ACCESSPOINT_CREATING,
MESSAGE_ACCESSPOINT_FAILED,
MESSAGE_ACCESSPOINT_CREATED,
MESSAGE_DISCONNECTED
} justwifi_messages_t;
class JustWifi {
public:
typedef std::function<void(justwifi_messages_t, char *)> TMessageFunction;
bool autoConnect();
bool connect();
bool startAP(char * ssid, char * pass);
bool disconnect();
bool connected();
bool cleanNetworks();
bool addNetwork(char * ssid, char * pass);
void setConnectTimeout(unsigned long ms);
justwifi_mode_t getMode();
String getIP();
String getNetwork();
void onMessage(TMessageFunction fn);
void loop();
private:
network_t _network[MAX_NETWORKS];
String _ssid;
justwifi_mode_t _mode = MODE_NONE;
unsigned char _network_count = 0;
unsigned long _connect_timeout = WIFI_CONNECT_TIMEOUT;
TMessageFunction _callback = NULL;
void _doCallback(justwifi_messages_t message, char * parameter = NULL);
};
#endif

+ 1
- 0
code/main View File

@ -0,0 +1 @@
src

+ 4
- 7
code/package.json View File

@ -3,13 +3,8 @@
"version": "0.1.0", "version": "0.1.0",
"description": "Gulp based build system for ESP8266 file system files", "description": "Gulp based build system for ESP8266 file system files",
"main": "gulpfile.js", "main": "gulpfile.js",
"repository": {
"type": "git",
"url": ""
},
"author": "Xose Pérez <xose.perez@gmail.com>", "author": "Xose Pérez <xose.perez@gmail.com>",
"license": "MIT",
"homepage": "",
"license": "GPL-3.0",
"devDependencies": { "devDependencies": {
"del": "^2.2.1", "del": "^2.2.1",
"gulp": "^3.9.1", "gulp": "^3.9.1",
@ -17,9 +12,11 @@
"gulp-gzip": "^1.4.0", "gulp-gzip": "^1.4.0",
"gulp-htmlmin": "^2.0.0", "gulp-htmlmin": "^2.0.0",
"gulp-if": "^2.0.1", "gulp-if": "^2.0.1",
"gulp-inline": "^0.1.1",
"gulp-plumber": "^1.1.0", "gulp-plumber": "^1.1.0",
"gulp-uglify": "^1.5.3", "gulp-uglify": "^1.5.3",
"gulp-useref": "^3.1.2"
"gulp-useref": "^3.1.2",
"yargs": "^5.0.0"
}, },
"dependencies": {} "dependencies": {}
} }

+ 28
- 0
code/pio_hooks.py View File

@ -0,0 +1,28 @@
#!/bin/python
import subprocess
import socket
from SCons.Script import DefaultEnvironment
env = DefaultEnvironment()
def is_valid_ip(ip):
try:
socket.inet_aton(ip)
return True
except socket.error:
return False
def before_build_spiffs(source, target, env):
env.Execute("gulp buildfs2")
def before_upload(source, target, env):
upload_port = env.get('UPLOAD_PORT', False)
if upload_port and upload_port[0] == '/':
cmd = ["mosquitto_sub", "-t", upload_port, "-h", "192.168.1.10", "-N", "-C", "1"]
ip = subprocess.check_output(cmd)
if is_valid_ip(ip):
env['UPLOAD_PORT'] = '"' + ip + '"'
#env.AddPreAction("uploadfs", before_upload)
#env.AddPreAction("upload", before_upload)
env.AddPreAction(".pioenvs/%s/spiffs.bin" % env['PIOENV'], before_build_spiffs)

+ 35
- 80
code/platformio.ini View File

@ -1,111 +1,66 @@
[env:sonoff-debug]
platform = espressif
framework = arduino
board = esp01_1m
lib_install = 89,64,19
build_flags = -Wl,-Tesp8266.flash.1m256.ld
build_flags = -D SONOFF -D DEBUG
[platformio]
env_default = node-debug
[env:sonoff-debug-ota]
platform = espressif
[common]
platform = espressif8266
framework = arduino framework = arduino
board = esp01_1m
lib_install = 89,64,19
build_flags = -Wl,-Tesp8266.flash.1m256.ld
build_flags = -D SONOFF -D DEBUG
lib_install = 19,44,64,89,549,727
extra_script = pio_hooks.py
[ota]
upload_speed = 115200 upload_speed = 115200
upload_port = "192.168.4.1" upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266 upload_flags = --auth=fibonacci --port 8266
[env:sonoff-debug]
include = common
board = esp01_1m
build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSONOFF
[env:sonoff-debug-ota]
include = env:sonoff-debug,ota
[env:slampher-debug] [env:slampher-debug]
platform = espressif
framework = arduino
include = common
board = esp01_1m board = esp01_1m
lib_install = 89,64,19
build_flags = -Wl,-Tesp8266.flash.1m256.ld
build_flags = -D SLAMPHER -D DEBUG
build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSLAMPHER
[env:slampher-debug-ota] [env:slampher-debug-ota]
platform = espressif
framework = arduino
board = esp01_1m
lib_install = 89,64,19
build_flags = -Wl,-Tesp8266.flash.1m256.ld
build_flags = -D SLAMPHER -D DEBUG
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
include = env:slampher-debug,ota
[env:s20-debug] [env:s20-debug]
platform = espressif
framework = arduino
include = common
board = esp01_1m board = esp01_1m
lib_install = 89,64,19
build_flags = -Wl,-Tesp8266.flash.1m256.ld
build_flags = -D S20 -D DEBUG
build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DS20
[env:s20-debug-ota] [env:s20-debug-ota]
platform = espressif
framework = arduino
board = esp01_1m
lib_install = 89,64,19
build_flags = -Wl,-Tesp8266.flash.1m256.ld
build_flags = -D S20 -D DEBUG
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
include = env:s20-debug,ota
[env:node-debug] [env:node-debug]
platform = espressif
framework = arduino
include = common
board = nodemcuv2 board = nodemcuv2
lib_install = 89,64,19
build_flags = -D NODEMCUV2 -D DEBUG
build_flags = -DNODEMCUV2 -DDEBUG_PORT=Serial
[env:node-debug-ota] [env:node-debug-ota]
platform = espressif
framework = arduino
board = nodemcuv2
lib_install = 89,64,19
build_flags = -D NODEMCUV2 -D DEBUG
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
include = env:node-debug,ota
[env:ac-device]
topic = /home/cellar/airconditioner/ip
include = env:s20-debug-ota
[env:washer-device] [env:washer-device]
platform = espressif
framework = arduino
board = esp01_1m
lib_install = 89,64,19
topic = /home/cellar/washer/ip topic = /home/cellar/washer/ip
build_flags = -Wl,-Tesp8266.flash.1m256.ld
build_flags = -D SONOFF -D DEBUG -D ENABLE_EMON -D ENABLE_DHT
upload_speed = 115200
upload_port = "192.168.1.114"
upload_flags = --auth=fibonacci --port 8266
include = env:sonoff-debug-ota
build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSONOFF -DENABLE_EMON=1 -DENABLE_DHT=1
[env:studio-lamp-device] [env:studio-lamp-device]
platform = espressif
framework = arduino
board = esp01_1m
lib_install = 89,64,19
topic = /home/studio/lamp/ip topic = /home/studio/lamp/ip
build_flags = -Wl,-Tesp8266.flash.1m256.ld
build_flags = -D SONOFF -D DEBUG
upload_speed = 115200
upload_port = "192.168.1.114"
upload_flags = --auth=fibonacci --port 8266
include = env:sonoff-debug-ota
[env:living-lamp-device] [env:living-lamp-device]
platform = espressif
framework = arduino
board = esp01_1m
lib_install = 89,64,19
topic = /home/living/lamp/ip topic = /home/living/lamp/ip
build_flags = -Wl,-Tesp8266.flash.1m256.ld
build_flags = -D SONOFF -D DEBUG -D RF
upload_speed = 115200
upload_port = "192.168.1.114"
upload_flags = --auth=fibonacci --port 8266
include = env:sonoff-debug-ota

+ 1
- 1
code/src/button.ino View File

@ -22,7 +22,7 @@ void buttonSetup() {
void buttonLoop() { void buttonLoop() {
if (button1.loop()) { if (button1.loop()) {
if (button1.getEvent() == EVENT_SINGLE_CLICK) toggleRelay(); if (button1.getEvent() == EVENT_SINGLE_CLICK) toggleRelay();
if (button1.getEvent() == EVENT_LONG_CLICK) wifiAP();
if (button1.getEvent() == EVENT_LONG_CLICK) createAP();
if (button1.getEvent() == EVENT_DOUBLE_CLICK) ESP.reset(); if (button1.getEvent() == EVENT_DOUBLE_CLICK) ESP.reset();
} }
} }

+ 5
- 0
code/src/debug.h View File

@ -0,0 +1,5 @@
#ifdef DEBUG_PORT
#define DEBUG_MSG(...) DEBUG_PORT.printf( __VA_ARGS__ )
#else
#define DEBUG_MSG(...)
#endif

+ 50
- 23
code/src/defaults.h View File

@ -1,5 +1,9 @@
// Managed from platformio.ini
//#define DEBUG
//------------------------------------------------------------------------------
// SET BY PLATFORMIO
//------------------------------------------------------------------------------
//#define DEBUG_PORT Serial
//#define ESPURNA //#define ESPURNA
//#define SONOFF //#define SONOFF
//#define SLAMPHER //#define SLAMPHER
@ -8,9 +12,14 @@
//#define ENABLE_NOFUSS 1 //#define ENABLE_NOFUSS 1
//#define ENABLE_EMON 1 //#define ENABLE_EMON 1
//#define ENABLE_RF 1
//#define ENABLE_DHT 1 //#define ENABLE_DHT 1
//#define ENABLE_RF 1
// -----------------------------------------------------------------------------
// HARDWARE
// -----------------------------------------------------------------------------
#define SERIAL_BAUDRATE 115200
#define BUTTON_PIN 0 #define BUTTON_PIN 0
#define RELAY_PIN 12 #define RELAY_PIN 12
@ -41,28 +50,30 @@
#ifdef NODEMCUV2 #ifdef NODEMCUV2
#define MANUFACTURER "NODEMCU" #define MANUFACTURER "NODEMCU"
#define DEVICE "LOLIN" #define DEVICE "LOLIN"
#define LED_PIN 4
#define LED_PIN 2
#endif #endif
#define AP_PASS "fibonacci"
#define OTA_PASS "fibonacci"
#define OTA_PORT 8266
#define HOSTNAME DEVICE
#define BUFFER_SIZE 1024 #define BUFFER_SIZE 1024
#define STATUS_UPDATE_INTERVAL 10000
#define HEARTBEAT_INTERVAL 60000
#define HEARTBEAT_INTERVAL 300000
#define FS_VERSION_FILE "/fsversion" #define FS_VERSION_FILE "/fsversion"
#define WIFI_MAX_NETWORKS 3
#define WIFI_RECONNECT_INTERVAL 300000
#define RF_PIN 14
// -----------------------------------------------------------------------------
// WIFI
// -----------------------------------------------------------------------------
#define DHT_PIN 14
#define DHT_UPDATE_INTERVAL 300000
#define DHT_TYPE DHT22
#define DHT_TIMING 11
#define WIFI_RECONNECT_INTERVAL 300000
#define WIFI_MAX_NETWORKS 3
#define AP_PASS "fibonacci"
#define OTA_PASS "fibonacci"
#define OTA_PORT 8266
#define NOFUSS_SERVER "http://192.168.1.100"
#define NOFUSS_INTERVAL 3600000
// -----------------------------------------------------------------------------
// MQTT
// -----------------------------------------------------------------------------
#define MQTT_SERVER "192.168.1.100" #define MQTT_SERVER "192.168.1.100"
#define MQTT_PORT 1883 #define MQTT_PORT 1883
@ -74,16 +85,31 @@
#define MQTT_VERSION_TOPIC "/version" #define MQTT_VERSION_TOPIC "/version"
#define MQTT_FSVERSION_TOPIC "/fsversion" #define MQTT_FSVERSION_TOPIC "/fsversion"
#define MQTT_HEARTBEAT_TOPIC "/heartbeat" #define MQTT_HEARTBEAT_TOPIC "/heartbeat"
#define MQTT_POWER_TOPIC "/power"
#define MQTT_TEMPERATURE_TOPIC "/temperature"
#define MQTT_HUMIDITY_TOPIC "/humidity"
#define NOFUSS_SERVER "http://192.168.1.100"
#define NOFUSS_INTERVAL 600000
// -----------------------------------------------------------------------------
// NTP
// -----------------------------------------------------------------------------
#define NTP_SERVER "pool.ntp.org"
#define NTP_TIME_OFFSET 1
#define NTP_DAY_LIGHT true
#define NTP_UPDATE_INTERVAL 1800
//--------------------------------------------------------------------------------
// DRIVERS
//--------------------------------------------------------------------------------
#define RF_PIN 14
#define RF_CHANNEL 31 #define RF_CHANNEL 31
#define RF_DEVICE 1 #define RF_DEVICE 1
#define DHT_PIN 14
#define DHT_UPDATE_INTERVAL 300000
#define DHT_TYPE DHT22
#define DHT_TIMING 11
#define DHT_TEMPERATURE_TOPIC "/temperature"
#define DHT_HUMIDITY_TOPIC "/humidity"
#define EMON_CURRENT_PIN 0 #define EMON_CURRENT_PIN 0
#define EMON_SAMPLES 1000 #define EMON_SAMPLES 1000
#define EMON_INTERVAL 10000 #define EMON_INTERVAL 10000
@ -94,3 +120,4 @@
#define EMON_CURRENT_OFFSET 0.25 #define EMON_CURRENT_OFFSET 0.25
#define EMON_MAINS_VOLTAGE 230 #define EMON_MAINS_VOLTAGE 230
#define EMON_CURRENT_RATIO 180 #define EMON_CURRENT_RATIO 180
#define EMON_POWER_TOPIC "/power"

+ 36
- 42
code/src/dht.ino View File

@ -9,63 +9,57 @@ Copyright (C) 2016 by Xose Pérez <xose dot perez at gmail dot com>
#if ENABLE_DHT #if ENABLE_DHT
#include "DHT.h"
#include <DHT.h>
DHT dht(DHT_PIN, DHT_TYPE, DHT_TIMING); DHT dht(DHT_PIN, DHT_TYPE, DHT_TIMING);
double temperature;
double humidity;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// DHT // DHT
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
double getTemperature() {
return temperature;
}
double getHumidity() {
return humidity;
}
void dhtSetup() { void dhtSetup() {
dht.begin(); dht.begin();
} }
void dhtLoop() { void dhtLoop() {
static unsigned long last_check = 0;
if (!mqttConnected()) return; if (!mqttConnected()) return;
if ((last_check > 0) && ((millis() - last_check) < DHT_UPDATE_INTERVAL)) return;
last_check = millis();
char buffer[10];
temperature = dht.readTemperature();
if (isnan(temperature)) {
#ifdef DEBUG
Serial.println(F("[DHT] Error reading temperature"));
#endif
} else {
dtostrf(temperature, 4, 1, buffer);
mqttSend((char *) MQTT_TEMPERATURE_TOPIC, buffer);
#ifdef DEBUG
Serial.print(F("[DHT] Temperature: "));
Serial.println(temperature);
#endif
}
humidity = dht.readHumidity();
if (isnan(humidity)) {
#ifdef DEBUG
Serial.println(F("[DHT] Error reading humidity"));
#endif
} else {
dtostrf(humidity, 4, 1, buffer);
mqttSend((char *) MQTT_HUMIDITY_TOPIC, buffer);
#ifdef DEBUG
Serial.print(F("[DHT] Humidity: "));
Serial.println(humidity);
#endif
// Check if we should read new data
static unsigned long last_update = 0;
if ((millis() - last_update > DHT_UPDATE_INTERVAL) || (last_update == 0)) {
last_update = millis();
// Read sensor data
double h = dht.readHumidity();
double t = dht.readTemperature();
// Check if readings are valid
if (isnan(h) || isnan(t)) {
DEBUG_MSG("[DHT] Error reading sensor\n");
} else {
char temperature[6];
char humidity[6];
dtostrf(t, 4, 1, temperature);
itoa((int) h, humidity, 10);
DEBUG_MSG("[DHT] Temperature: %s\n", temperature);
DEBUG_MSG("[DHT] Humidity: %s\n", humidity);
// Send MQTT messages
mqttSend((char *) getSetting("dhtTemperatureTopic", DHT_TEMPERATURE_TOPIC).c_str(), temperature);
mqttSend((char *) getSetting("dhtHumidityTopic", DHT_HUMIDITY_TOPIC).c_str(), humidity);
// Update websocket clients
char buffer[20];
sprintf_P(buffer, PSTR("{\"temperature\": %s, \"humidity\": %s}"), temperature, humidity);
webSocketSend(buffer);
}
} }
} }


+ 8
- 6
code/src/emon.ino View File

@ -71,17 +71,19 @@ Copyright (C) 2016 by Xose Pérez <xose dot perez at gmail dot com>
float mainsVoltage = getSetting("pwMainsVoltage", String(EMON_MAINS_VOLTAGE)).toFloat(); float mainsVoltage = getSetting("pwMainsVoltage", String(EMON_MAINS_VOLTAGE)).toFloat();
#ifdef DEBUG
Serial.print(F("[ENERGY] Power now: "));
Serial.print(int(current * mainsVoltage));
Serial.println(F("W"));
#endif
//DEBUG_MSG("[ENERGY] Power now: %dW\n", int(current * mainsVoltage));
// Update websocket clients
char text[20];
sprintf_P(text, PSTR("{\"power\": %d}"), int(current * mainsVoltage));
webSocketSend(text);
// Send MQTT messages averaged every EMON_MEASUREMENTS
if (measurements == EMON_MEASUREMENTS) { if (measurements == EMON_MEASUREMENTS) {
char buffer[8]; char buffer[8];
double power = (sum - max - min) * mainsVoltage / (measurements - 2); double power = (sum - max - min) * mainsVoltage / (measurements - 2);
sprintf(buffer, "%d", int(power)); sprintf(buffer, "%d", int(power));
mqttSend((char *) MQTT_POWER_TOPIC, buffer);
mqttSend((char *) getSetting("emonPowerTopic", EMON_POWER_TOPIC).c_str(), buffer);
sum = 0; sum = 0;
measurements = 0; measurements = 0;
} }


+ 53
- 90
code/src/main.ino View File

@ -19,48 +19,22 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <Arduino.h> #include <Arduino.h>
#include "version.h"
#include "defaults.h" #include "defaults.h"
#include "FS.h"
String getSetting(const String& key, String defaultValue = "");
#include "version.h"
#include "debug.h"
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Methods
// PROTOTYPES
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void getCompileTime(char * buffer) {
int day, month, year, hour, minute, second;
// parse date
String tmp = String(__DATE__);
day = tmp.substring(4,6).toInt();
year = tmp.substring(7).toInt();
tmp = tmp.substring(0,3);
if (tmp.equals("Jan")) month = 1;
if (tmp.equals("Feb")) month = 2;
if (tmp.equals("Mar")) month = 3;
if (tmp.equals("Apr")) month = 4;
if (tmp.equals("May")) month = 5;
if (tmp.equals("Jun")) month = 6;
if (tmp.equals("Jul")) month = 7;
if (tmp.equals("Aug")) month = 8;
if (tmp.equals("Sep")) month = 9;
if (tmp.equals("Oct")) month = 10;
if (tmp.equals("Nov")) month = 11;
if (tmp.equals("Dec")) month = 12;
// parse time
tmp = String(__TIME__);
hour = tmp.substring(0,2).toInt();
minute = tmp.substring(3,5).toInt();
second = tmp.substring(6,8).toInt();
sprintf(buffer, "%d%02d%02d%02d%02d%02d", year, month, day, hour, minute, second);
buffer[14] = 0;
#include <NtpClientLib.h>
#include <WebSocketsServer.h>
#include "FS.h"
String getSetting(const String& key, String defaultValue = "");
}
// -----------------------------------------------------------------------------
// METHODS
// -----------------------------------------------------------------------------
String getIdentifier() { String getIdentifier() {
char identifier[20]; char identifier[20];
@ -95,9 +69,7 @@ void hardwareSetup() {
void getFSVersion(char * buffer) { void getFSVersion(char * buffer) {
File h = SPIFFS.open(FS_VERSION_FILE, "r"); File h = SPIFFS.open(FS_VERSION_FILE, "r");
if (!h) { if (!h) {
#ifdef DEBUG
Serial.println(F("[SPIFFS] Could not open file system version file."));
#endif
DEBUG_MSG("[SPIFFS] Could not open file system version file.\n");
strcpy(buffer, APP_VERSION); strcpy(buffer, APP_VERSION);
return; return;
} }
@ -112,83 +84,73 @@ void hardwareLoop() {
// Heartbeat // Heartbeat
static unsigned long last_heartbeat = 0; static unsigned long last_heartbeat = 0;
if (millis() - last_heartbeat > HEARTBEAT_INTERVAL) {
last_heartbeat = millis();
mqttSend((char *) MQTT_HEARTBEAT_TOPIC, (char *) "1");
#ifdef DEBUG
Serial.print(F("[BEAT] Free heap: "));
Serial.println(ESP.getFreeHeap());
#endif
if (mqttConnected()) {
if ((millis() - last_heartbeat > HEARTBEAT_INTERVAL) || (last_heartbeat == 0)) {
last_heartbeat = millis();
mqttSend((char *) MQTT_HEARTBEAT_TOPIC, (char *) "1");
DEBUG_MSG("[BEAT] Free heap: %d\n", ESP.getFreeHeap());
DEBUG_MSG("[NTP] Time: %s\n", (char *) NTP.getTimeDateString().c_str());
}
} }
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Booting
// BOOTING
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void welcome() { void welcome() {
char buffer[BUFFER_SIZE];
getCompileTime(buffer);
Serial.println();
Serial.println();
Serial.print(APP_NAME);
Serial.print(F(" "));
Serial.print(APP_VERSION);
Serial.print(F(" built "));
Serial.println(buffer);
Serial.println(APP_AUTHOR);
Serial.println(APP_WEBSITE);
Serial.println();
Serial.print(F("Device: "));
Serial.println(getIdentifier());
Serial.print(F("Last reset reason: "));
Serial.println(ESP.getResetReason());
Serial.print(F("Memory size: "));
Serial.print(ESP.getFlashChipSize());
Serial.println(F(" bytes"));
Serial.print(F("Free heap: "));
Serial.print(ESP.getFreeHeap());
Serial.println(F(" bytes"));
delay(2000);
Serial.printf("%s %s\n", (char *) APP_NAME, (char *) APP_VERSION);
Serial.printf("%s\n%s\n\n", (char *) APP_AUTHOR, (char *) APP_WEBSITE);
//Serial.printf("Device: %s\n", (char *) getIdentifier().c_str());
Serial.printf("ChipID: %06X\n", ESP.getChipId());
Serial.printf("Last reset reason: %s\n", (char *) ESP.getResetReason().c_str());
Serial.printf("Memory size: %d bytes\n", ESP.getFlashChipSize());
Serial.printf("Free heap: %d bytes\n", ESP.getFreeHeap());
FSInfo fs_info; FSInfo fs_info;
if (SPIFFS.info(fs_info)) { if (SPIFFS.info(fs_info)) {
Serial.print(F("File system total size: "));
Serial.print(fs_info.totalBytes);
Serial.println(F(" bytes"));
Serial.print(F("File system used size : "));
Serial.print(fs_info.usedBytes);
Serial.println(F(" bytes"));
Serial.printf("File system total size: %d bytes\n", fs_info.totalBytes);
Serial.printf(" used size : %d bytes\n", fs_info.usedBytes);
Serial.printf(" block size: %d bytes\n", fs_info.blockSize);
Serial.printf(" page size : %d bytes\n", fs_info.pageSize);
Serial.printf(" max files : %d\n", fs_info.maxOpenFiles);
Serial.printf(" max length: %d\n", fs_info.maxPathLength);
} }
Serial.println(); Serial.println();
Serial.println();
} }
void setup() { void setup() {
hardwareSetup(); hardwareSetup();
buttonSetup();
welcome();
settingsSetup(); settingsSetup();
setSetting("hostname", String() + getIdentifier()); setSetting("hostname", String() + getIdentifier());
saveSettings(); saveSettings();
relaySetup();
delay(2000);
welcome();
buttonSetup();
relaySetup();
wifiSetup(); wifiSetup();
otaSetup(); otaSetup();
mqttSetup(); mqttSetup();
webServerSetup(); webServerSetup();
webSocketSetup();
ntpSetup();
#if ENABLE_NOFUSS #if ENABLE_NOFUSS
nofussSetup(); nofussSetup();
#endif #endif
#if ENABLE_RF
rfSetup();
#endif
#if ENABLE_DHT #if ENABLE_DHT
dhtSetup(); dhtSetup();
#endif #endif
#if ENABLE_RF
rfSetup();
#endif
#if ENABLE_EMON #if ENABLE_EMON
powerMonitorSetup(); powerMonitorSetup();
#endif #endif
@ -197,28 +159,29 @@ void setup() {
void loop() { void loop() {
wifiLoop();
hardwareLoop(); hardwareLoop();
buttonLoop(); buttonLoop();
settingsLoop();
wifiLoop();
otaLoop(); otaLoop();
mqttLoop(); mqttLoop();
webServerLoop(); webServerLoop();
webSocketLoop();
ntpLoop();
#if ENABLE_NOFUSS #if ENABLE_NOFUSS
nofussLoop(); nofussLoop();
#endif #endif
#if ENABLE_RF
rfLoop();
#endif
#if ENABLE_DHT #if ENABLE_DHT
dhtLoop(); dhtLoop();
#endif #endif
#if ENABLE_RF
rfLoop();
#endif
#if ENABLE_EMON #if ENABLE_EMON
powerMonitorLoop(); powerMonitorLoop();
#endif #endif
settingsLoop();
delay(1); delay(1);
} }

+ 51
- 66
code/src/mqtt.ino View File

@ -7,11 +7,12 @@ Copyright (C) 2016 by Xose Pérez <xose dot perez at gmail dot com>
*/ */
#include <WiFiClient.h>
#include <PubSubClient.h> #include <PubSubClient.h>
#include <ESP8266WiFi.h>
WiFiClient client; WiFiClient client;
PubSubClient mqtt(client); PubSubClient mqtt(client);
boolean mqttStatus = false;
String mqttTopic; String mqttTopic;
bool isCallbackMessage = false; bool isCallbackMessage = false;
@ -34,34 +35,20 @@ void buildTopics() {
} }
void mqttSend(char * topic, char * message) { void mqttSend(char * topic, char * message) {
if (!mqtt.connected()) return; if (!mqtt.connected()) return;
if (isCallbackMessage) return; if (isCallbackMessage) return;
String path = mqttTopic + String(topic); String path = mqttTopic + String(topic);
#ifdef DEBUG
Serial.print(F("[MQTT] Sending "));
Serial.print(path);
Serial.print(F(" "));
Serial.println(message);
#endif
DEBUG_MSG("[MQTT] Sending %s %s\n", (char *) path.c_str(), message);
mqtt.publish(path.c_str(), message, MQTT_RETAIN); mqtt.publish(path.c_str(), message, MQTT_RETAIN);
} }
void mqttCallback(char* topic, byte* payload, unsigned int length) { void mqttCallback(char* topic, byte* payload, unsigned int length) {
#ifdef DEBUG
Serial.print(F("[MQTT] Received "));
Serial.print(topic);
Serial.print(F(" "));
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
#endif
char buffer[length+1];
memcpy(buffer, payload, length);
buffer[length] = 0;
DEBUG_MSG("[MQTT] Received %s %s\n", topic, buffer);
// Action to perform // Action to perform
if ((char)payload[0] == '0') { if ((char)payload[0] == '0') {
@ -75,52 +62,42 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) {
if ((char)payload[0] == '2') { if ((char)payload[0] == '2') {
toggleRelay(); toggleRelay();
} }
isCallbackMessage = false;
isCallbackMessage = false;
} }
void mqttConnect() { void mqttConnect() {
String mqttServer = getSetting("mqttServer", MQTT_SERVER);
int mqttPort = getSetting("mqttPort", String(MQTT_PORT)).toInt();
String mqttUser = getSetting("mqttUser");
String mqttPassword = getSetting("mqttPassword");
if (!mqtt.connected() && (mqttServer.length()>0)) {
mqtt.setServer((const char *) mqttServer.c_str(), mqttPort);
#ifdef DEBUG
Serial.print(F("[MQTT] Connecting to broker at "));
Serial.print(mqttServer);
#endif
if (mqttUser.length() > 0) {
#ifdef DEBUG
Serial.print(F(" as user "));
Serial.print(mqttUser);
Serial.print(F(": "));
#endif
mqtt.connect(
getSetting("hostname").c_str(),
(const char *) mqttUser.c_str(),
(const char *) mqttPassword.c_str()
);
if (!mqtt.connected()) {
String host = getSetting("mqttServer", MQTT_SERVER);
String port = getSetting("mqttPort", String(MQTT_PORT));
String user = getSetting("mqttUser");
String pass = getSetting("mqttPassword");
if (host.length() == 0) return;
DEBUG_MSG("[MQTT] Connecting to broker at %s", (char *) host.c_str());
mqtt.setServer(host.c_str(), port.toInt());
if ((user != "") & (pass != "")) {
DEBUG_MSG(" as user %s: ", (char *) user.c_str());
mqtt.connect(getSetting("hostname", HOSTNAME).c_str(), user.c_str(), pass.c_str());
} else { } else {
#ifdef DEBUG
Serial.print(F(" anonymously: "));
#endif
mqtt.connect(getSetting("hostname").c_str());
DEBUG_MSG(" anonymously: ");
mqtt.connect(getSetting("hostname", HOSTNAME).c_str());
} }
if (mqtt.connected()) { if (mqtt.connected()) {
#ifdef DEBUG
Serial.println(F("connected!"));
#endif
DEBUG_MSG("connected!\n");
buildTopics(); buildTopics();
mqttStatus = true;
// Send status via webSocket
webSocketSend((char *) "{\"mqttStatus\": true}");
// Say hello and report our IP and VERSION // Say hello and report our IP and VERSION
mqttSend((char *) MQTT_IP_TOPIC, (char *) getIP().c_str()); mqttSend((char *) MQTT_IP_TOPIC, (char *) getIP().c_str());
@ -133,19 +110,13 @@ void mqttConnect() {
mqttSend((char *) MQTT_STATUS_TOPIC, (char *) (digitalRead(RELAY_PIN) ? "1" : "0")); mqttSend((char *) MQTT_STATUS_TOPIC, (char *) (digitalRead(RELAY_PIN) ? "1" : "0"));
// Subscribe to topic // Subscribe to topic
#ifdef DEBUG
Serial.print(F("[MQTT] Subscribing to "));
Serial.println(mqttTopic);
#endif
DEBUG_MSG("[MQTT] Subscribing to %s\n", (char *) mqttTopic.c_str());
mqtt.subscribe(mqttTopic.c_str()); mqtt.subscribe(mqttTopic.c_str());
} else { } else {
#ifdef DEBUG
Serial.print(F("failed, rc="));
Serial.println(mqtt.state());
#endif
DEBUG_MSG("failed (rc=%d)\n", mqtt.state());
} }
} }
@ -157,14 +128,28 @@ void mqttSetup() {
} }
void mqttLoop() { void mqttLoop() {
static unsigned long timeout = millis();
if (wifiConnected()) {
static unsigned long lastPeriod = 0;
if (WiFi.status() == WL_CONNECTED) {
if (!mqtt.connected()) { if (!mqtt.connected()) {
if (timeout < millis()) {
if (mqttStatus) {
webSocketSend((char *) "{\"mqttStatus\": false}");
mqttStatus = false;
}
unsigned long currPeriod = millis() / MQTT_RECONNECT_DELAY;
if (currPeriod != lastPeriod) {
lastPeriod = currPeriod;
mqttConnect(); mqttConnect();
timeout = millis() + MQTT_RECONNECT_DELAY;
} }
} }
if (mqtt.connected()) mqtt.loop(); if (mqtt.connected()) mqtt.loop();
} }
} }

+ 44
- 0
code/src/ntp.ino View File

@ -0,0 +1,44 @@
/*
RENTALITO
NTP MODULE
Copyright (C) 2016 by Xose Pérez <xose dot perez at gmail dot com>
*/
#include <TimeLib.h>
#include <NtpClientLib.h>
#include <WiFiClient.h>
// -----------------------------------------------------------------------------
// NTP
// -----------------------------------------------------------------------------
void ntpConnect(WiFiEventStationModeGotIP ipInfo) {
NTP.begin(NTP_SERVER, NTP_TIME_OFFSET, NTP_DAY_LIGHT);
NTP.setInterval(NTP_UPDATE_INTERVAL);
}
void ntpSetup() {
NTP.onNTPSyncEvent([](NTPSyncEvent_t error) {
if (error) {
if (error == noResponse) {
DEBUG_MSG("[NTP] Error: NTP server not reachable\n");
} else if (error == invalidAddress) {
DEBUG_MSG("[NTP] Error: Invalid NTP server address\n");
}
} else {
DEBUG_MSG("[NTP] Time: %s\n", (char *) NTP.getTimeDateString(NTP.getLastNTPSync()).c_str());
}
});
static WiFiEventHandler e;
e = WiFi.onStationModeGotIP(ntpConnect);
}
void ntpLoop() {
now();
}

+ 11
- 24
code/src/ota.ino View File

@ -7,7 +7,6 @@ Copyright (C) 2016 by Xose Pérez <xose dot perez at gmail dot com>
*/ */
#include <ArduinoJson.h>
#include "ArduinoOTA.h" #include "ArduinoOTA.h"
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -17,41 +16,29 @@ Copyright (C) 2016 by Xose Pérez <xose dot perez at gmail dot com>
void otaSetup() { void otaSetup() {
ArduinoOTA.setPort(OTA_PORT); ArduinoOTA.setPort(OTA_PORT);
ArduinoOTA.setHostname(getSetting("hostname").c_str());
ArduinoOTA.setHostname(getSetting("hostname", HOSTNAME).c_str());
ArduinoOTA.setPassword((const char *) OTA_PASS); ArduinoOTA.setPassword((const char *) OTA_PASS);
ArduinoOTA.onStart([]() { ArduinoOTA.onStart([]() {
#if ENABLE_RF
rfEnable(false);
#endif
#if DEBUG
Serial.println(F("[OTA] Start"));
#endif
DEBUG_MSG("[OTA] Start\n");
}); });
ArduinoOTA.onEnd([]() { ArduinoOTA.onEnd([]() {
#if DEBUG
Serial.println(F("[OTA] End"));
#endif
#if ENABLE_RF
rfEnable(true);
#endif
DEBUG_MSG("\n[OTA] End\n");
}); });
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
#if DEBUG
Serial.printf("[OTA] Progress: %u%%\r", (progress / (total / 100)));
#endif
DEBUG_MSG("[OTA] Progress: %u%%\r", (progress / (total / 100)));
}); });
ArduinoOTA.onError([](ota_error_t error) { ArduinoOTA.onError([](ota_error_t error) {
#if DEBUG
Serial.printf("[OTA] Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println(F("[OTA] Auth Failed"));
else if (error == OTA_BEGIN_ERROR) Serial.println(F("[OTA] Begin Failed"));
else if (error == OTA_CONNECT_ERROR) Serial.println(F("[OTA] Connect Failed"));
else if (error == OTA_RECEIVE_ERROR) Serial.println(F("[OTA] Receive Failed"));
else if (error == OTA_END_ERROR) Serial.println(F("[OTA] End Failed"));
#if DEBUG_PORT
DEBUG_MSG("\n[OTA] Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) DEBUG_MSG("Auth Failed\n");
else if (error == OTA_BEGIN_ERROR) DEBUG_MSG("Begin Failed\n");
else if (error == OTA_CONNECT_ERROR) DEBUG_MSG("Connect Failed\n");
else if (error == OTA_RECEIVE_ERROR) DEBUG_MSG("Receive Failed\n");
else if (error == OTA_END_ERROR) DEBUG_MSG("End Failed\n");
#endif #endif
}); });


+ 4
- 6
code/src/relay.ino View File

@ -17,14 +17,13 @@ void switchRelayOn() {
if (!digitalRead(RELAY_PIN)) { if (!digitalRead(RELAY_PIN)) {
#ifdef DEBUG
Serial.println(F("[RELAY] ON"));
#endif
DEBUG_MSG("[RELAY] ON\n");
digitalWrite(RELAY_PIN, HIGH); digitalWrite(RELAY_PIN, HIGH);
EEPROM.write(0, 1); EEPROM.write(0, 1);
EEPROM.commit(); EEPROM.commit();
mqttSend((char *) MQTT_STATUS_TOPIC, (char *) "1"); mqttSend((char *) MQTT_STATUS_TOPIC, (char *) "1");
webSocketSend((char *) "{\"relayStatus\": true}");
} }
@ -34,14 +33,13 @@ void switchRelayOff() {
if (digitalRead(RELAY_PIN)) { if (digitalRead(RELAY_PIN)) {
#ifdef DEBUG
Serial.println(F("[RELAY] OFF"));
#endif
DEBUG_MSG("[RELAY] OFF\n");
digitalWrite(RELAY_PIN, LOW); digitalWrite(RELAY_PIN, LOW);
EEPROM.write(0, 0); EEPROM.write(0, 0);
EEPROM.commit(); EEPROM.commit();
mqttSend((char *) MQTT_STATUS_TOPIC, (char *) "0"); mqttSend((char *) MQTT_STATUS_TOPIC, (char *) "0");
webSocketSend((char *) "{\"relayStatus\": false}");
} }


+ 28
- 18
code/src/rf.ino View File

@ -19,20 +19,10 @@ Copyright (C) 2016 by Xose Pérez <xose dot perez at gmail dot com>
// RF // RF
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void rfEnable(bool enable) {
if (enable) {
RemoteReceiver::enable();
} else {
RemoteReceiver::disable();
}
}
void rfLoop() { void rfLoop() {
return;
if (rfCode == 0) return; if (rfCode == 0) return;
#ifdef DEBUG
Serial.print(F("[RF] Received code: "));
Serial.println(rfCode);
#endif
DEBUG_MSG("[RF] Received code: %lu\n", rfCode);
if (rfCode == rfCodeON) switchRelayOn(); if (rfCode == rfCodeON) switchRelayOn();
if (rfCode == rfCodeOFF) switchRelayOff(); if (rfCode == rfCodeOFF) switchRelayOff();
rfCode = 0; rfCode = 0;
@ -62,12 +52,8 @@ Copyright (C) 2016 by Xose Pérez <xose dot perez at gmail dot com>
rfCodeOFF = code + 2; rfCodeOFF = code + 2;
rfCodeON = code + 6; rfCodeON = code + 6;
#ifdef DEBUG
Serial.print(F("[RF] Code ON: "));
Serial.println(rfCodeON);
Serial.print(F("[RF] Code OFF: "));
Serial.println(rfCodeOFF);
#endif
DEBUG_MSG("[RF] Code ON : %lu\n", rfCodeON);
DEBUG_MSG("[RF] Code OFF: %lu\n", rfCodeOFF);
} }
@ -76,9 +62,33 @@ Copyright (C) 2016 by Xose Pérez <xose dot perez at gmail dot com>
} }
void rfSetup() { void rfSetup() {
pinMode(RF_PIN, INPUT_PULLUP); pinMode(RF_PIN, INPUT_PULLUP);
rfBuildCodes(); rfBuildCodes();
RemoteReceiver::init(RF_PIN, 3, rfCallback); RemoteReceiver::init(RF_PIN, 3, rfCallback);
RemoteReceiver::disable();
DEBUG_MSG("[RF] Disabled\n");
static WiFiEventHandler e1 = WiFi.onStationModeDisconnected([](const WiFiEventStationModeDisconnected& event) {
RemoteReceiver::disable();
DEBUG_MSG("[RF] Disabled\n");
});
static WiFiEventHandler e2 = WiFi.onSoftAPModeStationDisconnected([](const WiFiEventSoftAPModeStationDisconnected& event) {
RemoteReceiver::disable();
DEBUG_MSG("[RF] Disabled\n");
});
static WiFiEventHandler e3 = WiFi.onStationModeConnected([](const WiFiEventStationModeConnected& event) {
RemoteReceiver::enable();
DEBUG_MSG("[RF] Enabled\n");
});
static WiFiEventHandler e4 = WiFi.onSoftAPModeStationConnected([](const WiFiEventSoftAPModeStationConnected& event) {
RemoteReceiver::enable();
DEBUG_MSG("[RF] Enabled\n");
});
} }
#endif #endif

+ 2
- 6
code/src/settings.ino View File

@ -31,9 +31,7 @@ void settingsSetup() {
[]() {} []() {}
#endif #endif
); );
#if DEBUG
Serial.println("[SETTINGS] Initialized");
#endif
DEBUG_MSG("[SETTINGS] Initialized\n");
} }
void settingsLoop() { void settingsLoop() {
@ -55,9 +53,7 @@ bool delSetting(const String& key) {
} }
void saveSettings() { void saveSettings() {
#if DEBUG
Serial.println("[SETTINGS] Saving");
#endif
DEBUG_MSG("[SETTINGS] Saving\n");
#if not AUTO_SAVE #if not AUTO_SAVE
EEPROM.commit(); EEPROM.commit();
#endif #endif


+ 1
- 1
code/src/version.h View File

@ -1,4 +1,4 @@
#define APP_NAME "Espurna" #define APP_NAME "Espurna"
#define APP_VERSION "0.9.7"
#define APP_VERSION "0.9.8"
#define APP_AUTHOR "xose.perez@gmail.com" #define APP_AUTHOR "xose.perez@gmail.com"
#define APP_WEBSITE "http://tinkerman.cat" #define APP_WEBSITE "http://tinkerman.cat"

+ 17
- 100
code/src/webserver.ino View File

@ -35,27 +35,20 @@ String getContentType(String filename) {
} }
void handleRelayOn() { void handleRelayOn() {
#ifdef DEBUG
Serial.println(F("[WEBSERVER] Request: /relay/on"));
#endif
DEBUG_MSG("[WEBSERVER] Request: /relay/on\n");
switchRelayOn(); switchRelayOn();
server.send(200, "text/plain", "ON"); server.send(200, "text/plain", "ON");
} }
void handleRelayOff() { void handleRelayOff() {
#ifdef DEBUG
Serial.println(F("[WEBSERVER] Request: /relay/off"));
#endif
DEBUG_MSG("[WEBSERVER] Request: /relay/off\n");
switchRelayOff(); switchRelayOff();
server.send(200, "text/plain", "OFF"); server.send(200, "text/plain", "OFF");
} }
bool handleFileRead(String path) { bool handleFileRead(String path) {
#ifdef DEBUG
Serial.print(F("[WEBSERVER] Request: "));
Serial.println(path);
#endif
DEBUG_MSG("[WEBSERVER] Request: %s\n", (char *) path.c_str());
if (path.endsWith("/")) path += "index.html"; if (path.endsWith("/")) path += "index.html";
String contentType = getContentType(path); String contentType = getContentType(path);
@ -74,86 +67,9 @@ bool handleFileRead(String path) {
} }
void handleGet() {
#ifdef DEBUG
Serial.println("[WEBSERVER] Request: /get");
#endif
char buffer[64];
sprintf(buffer, "%s %s", APP_NAME, APP_VERSION);
StaticJsonBuffer<1024> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
root["app"] = buffer;
root["manufacturer"] = String(MANUFACTURER);
root["device"] = String(DEVICE);
root["hostname"] = getSetting("hostname");
root["network"] = getNetwork();
root["ip"] = getIP();
root["updateInterval"] = STATUS_UPDATE_INTERVAL;
root["mqttServer"] = getSetting("mqttServer", MQTT_SERVER);
root["mqttPort"] = getSetting("mqttPort", String(MQTT_PORT));
root["mqttUser"] = getSetting("mqttUser");
root["mqttPassword"] = getSetting("mqttPassword");
root["mqttTopic"] = getSetting("mqttTopic", MQTT_TOPIC);
JsonArray& wifi = root.createNestedArray("wifi");
for (byte i=0; i<3; i++) {
JsonObject& network = wifi.createNestedObject();
network["ssid"] = getSetting("ssid" + String(i));
network["pass"] = getSetting("pass" + String(i));
}
#if ENABLE_RF
root["rfChannel"] = getSetting("rfChannel", String(RF_CHANNEL));
root["rfDevice"] = getSetting("rfDevice", String(RF_DEVICE));
#endif
#if ENABLE_EMON
root["pwMainsVoltage"] = getSetting("pwMainsVoltage", String(EMON_MAINS_VOLTAGE));
root["pwCurrentRatio"] = getSetting("pwCurrentRatio", String(EMON_CURRENT_RATIO));
#endif
String output;
root.printTo(output);
server.send(200, "text/json", output);
}
void handleSave() {
void handleStatus() {
// Update reconnection timeout to avoid disconnecting the web client
resetConnectionTimeout();
#ifdef DEBUG
//Serial.println("[WEBSERVER] Request: /status");
#endif
StaticJsonBuffer<256> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
root["relayStatus"] = (digitalRead(RELAY_PIN) == HIGH);
root["mqttStatus"] = mqttConnected();
#if ENABLE_EMON
root["power"] = getCurrent() * getSetting("pwMainsVoltage", String(EMON_MAINS_VOLTAGE)).toFloat();
#endif
#if ENABLE_DHT
root["temperature"] = getTemperature();
root["humidity"] = getHumidity();
#endif
String output;
root.printTo(output);
server.send(200, "text/json", output);
}
void handlePost() {
#ifdef DEBUG
Serial.println(F("[WEBSERVER] Request: /post"));
#endif
DEBUG_MSG("[WEBSERVER] Request: /save\n");
bool dirty = false; bool dirty = false;
bool dirtyMQTT = false; bool dirtyMQTT = false;
@ -182,7 +98,9 @@ void handlePost() {
server.send(202, "text/json", "{}"); server.send(202, "text/json", "{}");
if (dirty) saveSettings();
if (dirty) {
saveSettings();
}
#if ENABLE_RF #if ENABLE_RF
rfBuildCodes(); rfBuildCodes();
@ -192,13 +110,12 @@ void handlePost() {
setCurrentRatio(getSetting("pwCurrentRatio").toFloat()); setCurrentRatio(getSetting("pwCurrentRatio").toFloat());
#endif #endif
// Disconnect from current WIFI network if it's not the first on the list
// wifiLoop will take care of the reconnection
if (getNetwork() != getSetting("ssid0")) {
wifiDisconnect();
// Reconfigure networks
wifiConfigure();
wifiDisconnect();
// else check if we should reconigure MQTT connection
} else if (dirtyMQTT) {
// Check if we should reconigure MQTT connection
if (dirtyMQTT) {
mqttDisconnect(); mqttDisconnect();
} }
@ -206,20 +123,20 @@ void handlePost() {
void webServerSetup() { void webServerSetup() {
//SPIFFS.begin();
// Relay control // Relay control
server.on("/relay/on", HTTP_GET, handleRelayOn); server.on("/relay/on", HTTP_GET, handleRelayOn);
server.on("/relay/off", HTTP_GET, handleRelayOff); server.on("/relay/off", HTTP_GET, handleRelayOff);
// Configuration page // Configuration page
server.on("/get", HTTP_GET, handleGet);
server.on("/post", HTTP_POST, handlePost);
server.on("/status", HTTP_GET, handleStatus);
server.on("/save", HTTP_POST, handleSave);
// Anything else // Anything else
server.onNotFound([]() { server.onNotFound([]() {
// Hidden files // Hidden files
#ifndef DEBUG
#ifndef DEBUG_PORT
if (server.uri().startsWith("/.")) { if (server.uri().startsWith("/.")) {
server.send(403, "text/plain", "Forbidden"); server.send(403, "text/plain", "Forbidden");
return; return;


+ 107
- 0
code/src/websockets.ino View File

@ -0,0 +1,107 @@
/*
RENTALITO
WEBSERVER MODULE
Copyright (C) 2016 by Xose Pérez <xose dot perez at gmail dot com>
*/
#include <WebSocketsServer.h>
#include <Hash.h>
#include <ArduinoJson.h>
WebSocketsServer webSocket = WebSocketsServer(81);
// -----------------------------------------------------------------------------
// WEBSOCKETS
// -----------------------------------------------------------------------------
bool webSocketSend(char * payload) {
//DEBUG_MSG("[WEBSOCKET] Broadcasting '%s'\n", payload);
webSocket.broadcastTXT(payload);
}
bool webSocketSend(uint8_t num, char * payload) {
//DEBUG_MSG("[WEBSOCKET] Sending '%s' to #%d\n", payload, num);
webSocket.sendTXT(num, payload);
}
void webSocketStart(uint8_t num) {
char buffer[64];
sprintf(buffer, "%s %s", APP_NAME, APP_VERSION);
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
root["app"] = buffer;
root["manufacturer"] = String(MANUFACTURER);
root["device"] = String(DEVICE);
root["hostname"] = getSetting("hostname", HOSTNAME);
root["network"] = getNetwork();
root["ip"] = getIP();
root["mqttStatus"] = mqttConnected() ? "1" : "0";
root["mqttServer"] = getSetting("mqttServer", MQTT_SERVER);
root["mqttPort"] = getSetting("mqttPort", String(MQTT_PORT));
root["mqttUser"] = getSetting("mqttUser");
root["mqttPassword"] = getSetting("mqttPassword");
root["mqttTopic"] = getSetting("mqttTopic", MQTT_TOPIC);
root["relayStatus"] = digitalRead(RELAY_PIN) == HIGH;
#if ENABLE_RF
root["rfChannel"] = getSetting("rfChannel", String(RF_CHANNEL));
root["rfDevice"] = getSetting("rfDevice", String(RF_DEVICE));
#endif
#if ENABLE_EMON
root["pwMainsVoltage"] = getSetting("pwMainsVoltage", String(EMON_MAINS_VOLTAGE));
root["pwCurrentRatio"] = getSetting("pwCurrentRatio", String(EMON_CURRENT_RATIO));
#endif
JsonArray& wifi = root.createNestedArray("wifi");
for (byte i=0; i<3; i++) {
JsonObject& network = wifi.createNestedObject();
network["ssid"] = getSetting("ssid" + String(i));
network["pass"] = getSetting("pass" + String(i));
}
String output;
root.printTo(output);
webSocket.sendTXT(num, (char *) output.c_str());
}
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
DEBUG_MSG("[WEBSOCKET] #%u disconnected\n", num);
break;
case WStype_CONNECTED:
#if DEBUG_PORT
{
IPAddress ip = webSocket.remoteIP(num);
DEBUG_MSG("[WEBSOCKET] #%u connected, ip: %d.%d.%d.%d, url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
}
#endif
webSocketStart(num);
break;
case WStype_TEXT:
DEBUG_MSG("[WEBSOCKET] #%u sent: %s\n", num, payload);
break;
case WStype_BIN:
DEBUG_MSG("[WEBSOCKET] #%u sent binary length: %u\n", num, length);
break;
}
}
void webSocketSetup() {
webSocket.begin();
webSocket.onEvent(webSocketEvent);
}
void webSocketLoop() {
webSocket.loop();
}

+ 103
- 115
code/src/wifi.ino View File

@ -9,23 +9,22 @@ Copyright (C) 2016 by Xose Pérez <xose dot perez at gmail dot com>
#include "JustWifi.h" #include "JustWifi.h"
extern "C" {
#include "user_interface.h"
}
JustWifi jw;
unsigned long wifiLastConnectionTime = 0;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// WIFI // WIFI
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
String getIP() { String getIP() {
return jw.getIP();
if (WiFi.getMode() == WIFI_AP) {
return WiFi.softAPIP().toString();
}
return WiFi.localIP().toString();
} }
String getNetwork() { String getNetwork() {
return jw.getNetwork();
if (WiFi.getMode() == WIFI_AP) {
return jw.getAPSSID();
}
return WiFi.SSID();
} }
void wifiDisconnect() { void wifiDisconnect() {
@ -33,136 +32,125 @@ void wifiDisconnect() {
} }
void resetConnectionTimeout() { void resetConnectionTimeout() {
wifiLastConnectionTime = millis();
jw.resetReconnectTimeout();
} }
bool wifiConnected() { bool wifiConnected() {
return jw.connected(); return jw.connected();
} }
bool createAP() {
return jw.createAP();
}
void wifiConfigure() {
jw.cleanNetworks();
if (getSetting("ssid0").length() > 0) jw.addNetwork((char *) getSetting("ssid0").c_str(), (char *) getSetting("pass0").c_str());
if (getSetting("ssid1").length() > 0) jw.addNetwork((char *) getSetting("ssid1").c_str(), (char *) getSetting("pass1").c_str());
if (getSetting("ssid2").length() > 0) jw.addNetwork((char *) getSetting("ssid2").c_str(), (char *) getSetting("pass2").c_str());
jw.disconnect();
}
void wifiSetup() { void wifiSetup() {
wifi_station_set_hostname((char *) getSetting("hostname").c_str());
jw.setHostname((char *) getSetting("hostname", HOSTNAME).c_str());
jw.scanNetworks(true);
jw.setAPMode(AP_MODE_ALONE);
jw.setSoftAP((char *) getIdentifier().c_str(), (char *) AP_PASS);
wifiConfigure();
// Message callbacks // Message callbacks
jw.onMessage([](justwifi_messages_t code, char * parameter) { jw.onMessage([](justwifi_messages_t code, char * parameter) {
#ifdef DEBUG_PORT
if (code == MESSAGE_SCANNING) {
DEBUG_MSG("[WIFI] Scanning\n");
}
if (code == MESSAGE_SCAN_FAILED) {
DEBUG_MSG("[WIFI] Scan failed\n");
}
if (code == MESSAGE_NO_NETWORKS) {
DEBUG_MSG("[WIFI] No networks found\n");
}
if (code == MESSAGE_NO_KNOWN_NETWORKS) {
DEBUG_MSG("[WIFI] No known networks found\n");
}
if (code == MESSAGE_FOUND_NETWORK) {
DEBUG_MSG("[WIFI] %s\n", parameter);
}
if (code == MESSAGE_CONNECTING) {
DEBUG_MSG("[WIFI] Connecting to %s\n", parameter);
}
if (code == MESSAGE_CONNECT_WAITING) {
// too much noise
}
if (code == MESSAGE_CONNECT_FAILED) {
DEBUG_MSG("[WIFI] Could not connect to %s\n", parameter);
}
if (code == MESSAGE_CONNECTED) {
DEBUG_MSG("[WIFI] MODE STA -------------------------------------\n");
DEBUG_MSG("[WIFI] SSID %s\n", WiFi.SSID().c_str());
DEBUG_MSG("[WIFI] IP %s\n", WiFi.localIP().toString().c_str());
DEBUG_MSG("[WIFI] MAC %s\n", WiFi.macAddress().c_str());
DEBUG_MSG("[WIFI] GW %s\n", WiFi.gatewayIP().toString().c_str());
DEBUG_MSG("[WIFI] MASK %s\n", WiFi.subnetMask().toString().c_str());
DEBUG_MSG("[WIFI] DNS %s\n", WiFi.dnsIP().toString().c_str());
DEBUG_MSG("[WIFI] HOST %s\n", WiFi.hostname().c_str());
DEBUG_MSG("[WIFI] ----------------------------------------------\n");
}
if (code == MESSAGE_ACCESSPOINT_CREATED) {
DEBUG_MSG("[WIFI] MODE AP --------------------------------------\n");
DEBUG_MSG("[WIFI] SSID %s\n", jw.getAPSSID().c_str());
DEBUG_MSG("[WIFI] IP %s\n", WiFi.softAPIP().toString().c_str());
DEBUG_MSG("[WIFI] MAC %s\n", WiFi.softAPmacAddress().c_str());
DEBUG_MSG("[WIFI] ----------------------------------------------\n");
}
if (code == MESSAGE_DISCONNECTED) {
DEBUG_MSG("[WIFI] Disconnected\n");
}
if (code == MESSAGE_ACCESSPOINT_CREATING) {
DEBUG_MSG("[WIFI] Creating access point\n");
}
if (code == MESSAGE_ACCESSPOINT_FAILED) {
DEBUG_MSG("[WIFI] Could not create access point\n");
}
#endif
// Disconnect from MQTT server if no WIFI // Disconnect from MQTT server if no WIFI
if (code != MESSAGE_CONNECTED) { if (code != MESSAGE_CONNECTED) {
if (mqttConnected()) mqttDisconnect(); if (mqttConnected()) mqttDisconnect();
} }
#if DEBUG
if (code == MESSAGE_AUTO_NOSSID) {
Serial.println("[WIFI] No information about the last successful network");
}
if (code == MESSAGE_AUTO_CONNECTING) {
Serial.print("[WIFI] Connecting to last successful network: ");
Serial.println(parameter);
}
if (code == MESSAGE_AUTO_FAILED) {
Serial.println("[WIFI] Could not connect to last successful network");
}
if (code == MESSAGE_CONNECTING) {
Serial.print("[WIFI] Connecting to ");
Serial.println(parameter);
}
if (code == MESSAGE_CONNECT_WAITING) {
//
}
if (code == MESSAGE_CONNECT_FAILED) {
Serial.print("[WIFI] Could not connect to ");
Serial.println(parameter);
}
if (code == MESSAGE_CONNECTED) {
Serial.print("[WIFI] Connected to ");
Serial.print(jw.getNetwork());
Serial.print(" with IP ");
Serial.println(jw.getIP());
}
// Configure mDNS
if (code == MESSAGE_CONNECTED) {
if (code == MESSAGE_DISCONNECTED) {
Serial.println("[WIFI] Disconnected");
}
if (MDNS.begin((char *) WiFi.hostname().c_str())) {
MDNS.addService("http", "tcp", 80);
DEBUG_MSG("[MDNS] OK\n");
} else {
DEBUG_MSG("[MDNS] FAIL\n");
}
if (code == MESSAGE_ACCESSPOINT_CREATING) {
Serial.println("[WIFI] Creating access point");
}
if (code == MESSAGE_ACCESSPOINT_CREATED) {
Serial.print("[WIFI] Access point created with SSID ");
Serial.print(jw.getNetwork());
Serial.print(" and IP ");
Serial.println(jw.getIP());
}
if (code == MESSAGE_ACCESSPOINT_FAILED) {
Serial.println("[WIFI] Could not create access point");
}
#endif
}
}); });
} }
bool wifiAP() {
//jw.disconnect();
return jw.startAP((char *) getSetting("hostname").c_str(), (char *) AP_PASS);
}
void wifiConnect() {
resetConnectionTimeout();
//WiFi.printDiag(Serial);
// Set networks
jw.cleanNetworks();
jw.addNetwork((char *) getSetting("ssid0").c_str(), (char *) getSetting("pass0").c_str());
jw.addNetwork((char *) getSetting("ssid1").c_str(), (char *) getSetting("pass1").c_str());
jw.addNetwork((char *) getSetting("ssid2").c_str(), (char *) getSetting("pass2").c_str());
// Connecting
if (!jw.autoConnect()) {
if (!jw.connect()) {
if (!wifiAP()) {
#if DEBUG
Serial.println("[WIFI] Could not start any wifi interface!");
#endif
}
}
}
}
void wifiLoop() { void wifiLoop() {
jw.loop(); jw.loop();
// Check disconnection
if (!jw.connected()) {
// If we are in AP mode try to reconnect every WIFI_RECONNECT_INTERVAL
// wifiLastConnectionTime gets updated upon every connect try or when
// the webserver is hit by a request to avoid web clients to be
// disconnected while configuring the board
if (jw.getMode() == MODE_ACCESS_POINT) {
if (millis() - wifiLastConnectionTime > WIFI_RECONNECT_INTERVAL) {
wifiConnect();
}
// else reconnect right away
} else {
wifiConnect();
}
}
} }

Loading…
Cancel
Save