V1.0.0 Release Candidate

Implémentation OTA
Web serveur sur le système de fichier SPIFFS
Interface Utilisateur pour la Configuration
This commit is contained in:
Charles 2015-12-23 01:51:12 +01:00
parent 0f73e251b0
commit c11caa31a1
18 changed files with 2094 additions and 1255 deletions

4
.gitignore vendored
View File

@ -7,6 +7,10 @@ Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
node_modules/
bootstrap-3.3.5-dist/
font-awesome-4.5.0/
webdev/
# Windows Installer files
*.cab

View File

@ -1,585 +0,0 @@
// **********************************************************************************
// ESP8266 Teleinfo WEB Server
// **********************************************************************************
// Creative Commons Attrib Share-Alike License
// You are free to use/extend this library but please abide with the CC-BY-SA license:
// Attribution-NonCommercial-ShareAlike 4.0 International License
// http://creativecommons.org/licenses/by-nc-sa/4.0/
//
// For any explanation about teleinfo ou use , see my blog
// http://hallard.me/category/tinfo
//
// This program works with the Wifinfo board
// see schematic here https://github.com/hallard/teleinfo/tree/master/Wifinfo
//
// Written by Charles-Henri Hallard (http://hallard.me)
//
// History : V1.00 2015-06-14 - First release
//
// All text above must be included in any redistribution.
//
// **********************************************************************************
// Include Arduino header
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <WiFiUDP.h>
#include <EEPROM.h>
#include <Ticker.h>
//#include <WebSocketsServer.h>
//#include <Hash.h>
#include <NeoPixelBus.h>
#include <LibTeleinfo.h>
// Global project file
#include "ESP8266_WifInfo.h"
//WiFiManager wifi(0);
ESP8266WebServer server(80);
// Udp listener and telnet server
WiFiUDP OTA;
// Teleinfo
TInfo tinfo;
// RGB Loed
NeoPixelBus rgb_led = NeoPixelBus(1, RGB_LED_PIN, NEO_RGB | NEO_KHZ800);
// define whole brigtness level for RGBLED
uint8_t rgb_brightness = 127;
// LED Blink timers
Ticker rgb_ticker;
Ticker blu_ticker;
Ticker red_ticker;
Ticker Every_1_Sec;
volatile boolean task_1_sec = false;
unsigned long seconds = 0;
// sysinfo data
_sysinfo sysinfo;
/* ======================================================================
Function: UpdateSysinfo
Purpose : update sysinfo variables
Input : true if first call
true if needed to print on serial debug
Output : -
Comments: -
====================================================================== */
void UpdateSysinfo(boolean first_call, boolean show_debug)
{
char buff[64];
int32_t adc;
int sec = seconds;
int min = sec / 60;
int hr = min / 60;
sprintf( buff, "%02d:%02d:%02d", hr, min % 60, sec % 60);
sysinfo.sys_uptime = buff;
sprintf( buff, "%d KB", ESP.getFreeHeap()/1024 );
sysinfo.sys_free_ram = buff;
adc = ( (1000 * analogRead(A0)) / 1024);
sprintf( buff, "%d mV", adc);
sysinfo.sys_analog = buff;
// Values not subject to change during running sketch
if (first_call) {
sprintf( buff, "%d KB", ESP.getFlashChipRealSize()/1024 );
sysinfo.sys_flash_real_size = buff;
sprintf( buff, "%d KB", ESP.getSketchSize()/1024 );
sysinfo.sys_firmware_size = buff;
sprintf( buff, "%d KB", ESP.getFreeSketchSpace()/1024 );
sysinfo.sys_firmware_free = buff;
sprintf( buff, "%d MHZ", ESP.getFlashChipSpeed()/1000 );
sysinfo.sys_flash_speed = buff;
}
if (show_debug) {
Debug(F("Firmware : ")); Debugln(__DATE__ " " __TIME__);
Debug(F("Flash real id: ")); Serial1.printf("0x%08X\r\n", ESP.getFlashChipId());
Debug(F("Flash RSize : ")); Debugln(sysinfo.sys_flash_real_size);
Debug(F("CPU Speed : ")); Debugln(sysinfo.sys_flash_speed);
Debug(F("Sketch size : ")); Debugln(sysinfo.sys_firmware_size);
Debug(F("Free size : ")); Debugln(sysinfo.sys_firmware_free);
Debug(F("Free RAM : ")); Debugln(sysinfo.sys_free_ram);
Debug(F("OTA port : ")); Debugln(config.ota_port);
Debug(F("Analog Read : ")); Debugln(sysinfo.sys_analog);
Debug(F("Saved Config : ")); Debugln(sysinfo.sys_eep_config);
}
}
/* ======================================================================
Function: Task_1_Sec
Purpose : update our second ticker
Input : -
Output : -
Comments: -
====================================================================== */
void Task_1_Sec()
{
task_1_sec = true;
seconds++;
}
/* ======================================================================
Function: LedOff
Purpose : callback called after led blink delay
Input : led (defined in term of PIN)
Output : -
Comments: -
====================================================================== */
void LedOff(int led)
{
#ifdef BLU_LED_PIN
if (led==BLU_LED_PIN)
LedBluOFF();
#endif
if (led==RED_LED_PIN)
LedRedOFF();
if (led==RGB_LED_PIN)
LedRGBOFF();
}
/* ======================================================================
Function: ADPSCallback
Purpose : called by library when we detected a ADPS on any phased
Input : phase number
0 for ADPS (monophase)
1 for ADIR1 triphase
2 for ADIR2 triphase
3 for ADIR3 triphase
Output : -
Comments: should have been initialised in the main sketch with a
tinfo.attachADPSCallback(ADPSCallback())
====================================================================== */
void ADPSCallback(uint8_t phase)
{
// Monophasé
if (phase == 0 ) {
Debugln(F("ADPS"));
} else {
Debug(F("ADPS Phase "));
Debugln('0' + phase);
}
}
/* ======================================================================
Function: DataCallback
Purpose : callback when we detected new or modified data received
Input : linked list pointer on the concerned data
value current state being TINFO_VALUE_ADDED/TINFO_VALUE_UPDATED
Output : -
Comments: -
====================================================================== */
void DataCallback(ValueList * me, uint8_t flags)
{
// This is for simulating ADPS during my tests
// ===========================================
/*
static uint8_t test = 0;
// Each new/updated values
if (++test >= 20) {
test=0;
uint8_t anotherflag = TINFO_FLAGS_NONE;
ValueList * anotherme = tinfo.addCustomValue("ADPS", "46", &anotherflag);
// Do our job (mainly debug)
DataCallback(anotherme, anotherflag);
}
Debugf("%02d:",test);
*/
// ===========================================
// Do whatever you want there
Debug(me->name);
Debug('=');
Debug(me->value);
if ( flags & TINFO_FLAGS_NOTHING ) Debug(F(" Nothing"));
if ( flags & TINFO_FLAGS_ADDED ) Debug(F(" Added"));
if ( flags & TINFO_FLAGS_UPDATED ) Debug(F(" Updated"));
if ( flags & TINFO_FLAGS_EXIST ) Debug(F(" Exist"));
if ( flags & TINFO_FLAGS_ALERT ) Debug(F(" Alert"));
Debugln();
}
/* ======================================================================
Function: NewFrame
Purpose : callback when we received a complete teleinfo frame
Input : linked list pointer on the concerned data
Output : -
Comments: -
====================================================================== */
void NewFrame(ValueList * me)
{
char buff[32];
// Light the RGB LED
LedRGBON(COLOR_GREEN);
sprintf( buff, "New Frame (%ld Bytes free)", ESP.getFreeHeap() );
Debugln(buff);
// led off after delay
rgb_ticker.once_ms( (uint32_t) BLINK_LED_MS, LedOff, (int) RGB_LED_PIN);
}
/* ======================================================================
Function: NewFrame
Purpose : callback when we received a complete teleinfo frame
Input : linked list pointer on the concerned data
Output : -
Comments: it's called only if one data in the frame is different than
the previous frame
====================================================================== */
void UpdatedFrame(ValueList * me)
{
char buff[32];
// Light the RGB LED (purple)
LedRGBON(COLOR_MAGENTA);
sprintf( buff, "Updated Frame (%ld Bytes free)", ESP.getFreeHeap() );
Debugln(buff);
// led off after delay
rgb_ticker.once_ms(BLINK_LED_MS, LedOff, RGB_LED_PIN);
}
/* ======================================================================
Function: CheckOTAUpdate
Purpose : Check if we need to update the firmware over the Air
Input : -
Output : -
Comments: If upgraded, no return, perform update and reboot ESP
====================================================================== */
void CheckOTAUpdate(void)
{
bool spiffs = false;
//OTA detection
if (OTA.parsePacket()) {
IPAddress remote = OTA.remoteIP();
int cmd = OTA.parseInt();
int port = OTA.parseInt();
int size = OTA.parseInt();
LedRGBON(COLOR_MAGENTA);
DebugF("OTA received command ");
Debugln(cmd);
if (cmd == U_SPIFFS) {
spiffs = true;
DebugF("Get SPIFFS image");
}
DebugF("Update Start: ip:");
Debug(remote);
Debugf(", port:%d, size:%dKB\n", port, size/1024);
uint32_t startTime = millis();
WiFiUDP::stopAll();
if(!Update.begin(size, cmd)) {
DebugF("Update Begin Error");
return;
}
WiFiClient ota_client;
if (ota_client.connect(remote, port)) {
uint32_t written;
while(!Update.isFinished()) {
written = Update.write(ota_client);
if(written > 0)
{
LedRGBOFF();
ota_client.print(written, DEC);
LedRGBON(COLOR_MAGENTA);
}
}
LedRGBOFF();
Serial.setDebugOutput(false);
if(Update.end()) {
ota_client.println("OK");
Debugf("Update Success: %u\nRebooting...\n", millis() - startTime);
ESP.restart();
} else {
Update.printError(ota_client);
Update.printError(Serial);
}
} else {
Debugf("Connect Failed: %u\n", millis() - startTime);
}
// Be sure to re enable it
OTA.begin(DEFAULT_OTA_PORT);
}
}
/* ======================================================================
Function: WifiSoftAP
Purpose : Change Wifi mode to Soft AP
Input : -
Output : state of the wifi status
Comments: -
====================================================================== */
int WifiSoftAP()
{
}
/* ======================================================================
Function: WifiHandleConn
Purpose : Handle Wifi connection / reconnection and OTA updates
Input : setup true if we're called 1st Time from setup
Output : state of the wifi status
Comments: -
====================================================================== */
int WifiHandleConn(boolean setup = false)
{
int ret ;
if (setup)
{
// Check WiFi connection mode, at startup
// try to connect to AP
if (WiFi.getMode()!=WIFI_STA) {
WiFi.mode(WIFI_STA);
delay(10);
}
// Get Wifi Status
ret = WiFi.status();
// Try to get 1st connexion
if ( ret != WL_CONNECTED ) {
// Orange we're not connected anymore
LedRGBON(COLOR_ORANGE);
DebugF("Connecting to: ");
Debug(DEFAULT_WIFI_SSID);
Debug(F("..."));
WiFi.begin(DEFAULT_WIFI_SSID, DEFAULT_WIFI_PASS);
ret = WiFi.waitForConnectResult();
if ( ret != WL_CONNECTED) {
LedRGBON(COLOR_RED);
DebuglnF("Connection failed!");
} else {
LedRGBON(COLOR_GREEN);
DebuglnF("Connected");
DebugF("IP address : "); Debugln(WiFi.localIP());
DebugF("MAC address : "); Debugln(WiFi.macAddress());
// just in case your sketch sucks, keep update OTA Available
// Trust me, when coding and testing it happens, this could save
// the need to connect FTDI to reflash
// Usefull just after 1st connexion when called from setup() before
// launching potentially bugging main()
for (uint8_t i=0; i<= 10; i++) {
LedRGBON(COLOR_MAGENTA);
delay(100);
LedRGBOFF();
delay(200);
CheckOTAUpdate();
}
}
}
// We did not succeded to connect ?
if ( ret != WL_CONNECTED ) {
uint8_t mac[WL_MAC_ADDR_LENGTH];
char ssid[32];
// start Soft AP
DebuglnF("Starting Soft AP mode");
WiFi.mode(WIFI_AP);
// Add the last two bytes of the MAC address to AP name
WiFi.softAPmacAddress(mac);
sprintf_P(ssid, PSTR("%s_%02X%02X"), DEFAULT_WIFI_AP_SSID, mac[4], mac[5] );
DebugF("SSID : "); Debugln(ssid);
//WiFi.softAP(ssid, DEFAULT_WIFI_AP_PSK);
//DebuglnF("PSK : " DEFAULT_WIFI_AP_PSK);
// No password
WiFi.softAP(ssid);
DebugF("IP : ");
Debugln(WiFi.softAPIP());
}
// Advertise US for Arduino IDE
// not very usefull on Windows (IDE does not always sees us)
MDNS.begin(DEFAULT_HOSTNAME);
MDNS.addService("arduino", "tcp", DEFAULT_OTA_PORT);
// Setup OTA feature
OTA.begin(DEFAULT_OTA_PORT);
}
// Handle OTA
CheckOTAUpdate();
return ret;
}
/* ======================================================================
Function: setup
Purpose : Setup I/O and other one time startup stuff
Input : -
Output : -
Comments: -
====================================================================== */
void setup()
{
boolean reset_config = true;
// Set WiFi to station mode and disconnect from an AP if it was previously connected
//WiFi.mode(WIFI_STA);
//WiFi.disconnect();
//delay(100);
// Init the RGB Led, and set it off
rgb_led.Begin();
LedRGBOFF();
// Init the serial 1
// Teleinfo is connected to RXD2 (GPIO13) to
// avoid conflict when flashing, this is why
// we swap RXD1/RXD1 to RXD2/TXD2
// Note that TXD2 is not used teleinfo is receive only
Serial.begin(1200, SERIAL_7E1);
Serial.swap();
// Our Debug Serial TXD0
// note this serial can only transmit, just
// enough for debugging purpose
Serial1.begin(115200);
Debugln(F("=============="));
Debugln(F("Wifinfo V1.0.1"));
Debugln();
Debugflush();
// Clear our global flags
config.config = 0;
// Our configuration is stored into EEPROM
EEPROM.begin(sizeof(_Config));
// Read Configuration from EEP
if (readConfig()) {
// in case of saved default config it's not good
if (config.ssid[0]!='*' && config.pass[0]!='*' ) {
sysinfo.sys_eep_config = PSTR("Good CRC, config OK");
reset_config = false;
} else {
sysinfo.sys_eep_config = PSTR("Good CRC, not set!");
}
}
if (reset_config ) {
// Error, enable default configuration
strcpy_P(config.ssid, PSTR(DEFAULT_WIFI_SSID));
strcpy_P(config.pass, PSTR(DEFAULT_WIFI_PASS));
strcpy_P(config.host, PSTR(DEFAULT_HOSTNAME));
config.ota_port = DEFAULT_OTA_PORT ;
// Indicate the error in global flags
config.config |= CFG_BAD_CRC;
sysinfo.sys_eep_config = PSTR("Reset to default");
// save back
saveConfig();
}
Debugln(sysinfo.sys_eep_config);
// Init teleinfo
tinfo.init();
// Attach the callback we need
// set all as an example
tinfo.attachADPS(ADPSCallback);
tinfo.attachData(DataCallback);
tinfo.attachNewFrame(NewFrame);
tinfo.attachUpdatedFrame(UpdatedFrame);
// We'll drive our onboard LED
// old TXD1, not used anymore, has been swapped
pinMode(RED_LED_PIN, OUTPUT);
LedRedOFF();
// start Wifi connect or soft AP
WifiHandleConn(true);
// Update sysinfor variable and print them
UpdateSysinfo(true, true);
server.on("/", handleRoot);
server.on("/json", sendJSON);
server.on("/tinfojsontbl", tinfoJSONTable);
server.on("/sysjsontbl", sysJSONTable);
server.onNotFound(handleNotFound);
server.begin();
Debugln(F("HTTP server started"));
//webSocket.begin();
//webSocket.onEvent(webSocketEvent);
// Light off the RGB LED
LedRGBOFF();
// control watchdog
//ESP.wdtEnable();
//ESP.wdtDisable()
//ESP.wdtFeed();
// Update sysinfo every second
Every_1_Sec.attach(1, Task_1_Sec);
}
/* ======================================================================
Function: loop
Purpose : infinite loop main code
Input : -
Output : -
Comments: -
====================================================================== */
void loop()
{
char c;
// Do all related network stuff
server.handleClient();
//webSocket.loop();
// 1 second task job ?
if (task_1_sec) {
UpdateSysinfo(false, false);
task_1_sec = false;
}
// Handle teleinfo serial
if ( Serial.available() ) {
// Read Serial and process to tinfo
c = Serial.read();
//Serial1.print(c);
tinfo.process(c);
}
delay(10);
}

View File

@ -1,75 +0,0 @@
// **********************************************************************************
// ESP8266 Teleinfo WEB Server configuration Include file
// **********************************************************************************
// Creative Commons Attrib Share-Alike License
// You are free to use/extend this library but please abide with the CC-BY-SA license:
// Attribution-NonCommercial-ShareAlike 4.0 International License
// http://creativecommons.org/licenses/by-nc-sa/4.0/
//
// For any explanation about teleinfo ou use , see my blog
// http://hallard.me/category/tinfo
//
// This program works with the Wifinfo board
// see schematic here https://github.com/hallard/teleinfo/tree/master/Wifinfo
//
// Written by Charles-Henri Hallard (http://hallard.me)
//
// History : V1.00 2015-06-14 - First release
//
// All text above must be included in any redistribution.
//
// **********************************************************************************
#ifndef __CONFIG_H__
#define __CONFIG_H__
// Include main project include file
#include "ESP8266_WifInfo.h"
#define CFG_MAX_SSID_SIZE 32
#define CFG_MAX_PASS_SIZE 32
#define CFG_MAX_HOSTNAME 16
// Mettez ici vos identifiant de connexion à
// votre réseau WIFI
#define DEFAULT_WIFI_SSID "************"
#define DEFAULT_WIFI_PASS "************"
#define DEFAULT_HOSTNAME "WifInfo-esp01"
// En mode acces point
#define DEFAULT_WIFI_AP_SSID "WifInfo"
#define DEFAULT_WIFI_AP_PSK "WifInfoPSK"
// Port pour l'OTA
#define DEFAULT_OTA_PORT 8266
// Bit definition for different configuration modes
#define CFG_LCD 0x0001 // Enable display
#define CFG_DEBUG 0x0002 // Enable serial debug
#define CFG_BAD_CRC 0x8000 // Bad CRC when reading configuration
// Config saved into eeprom
// 128 bytes total including CRC
typedef struct
{
char ssid[CFG_MAX_SSID_SIZE]; /* SSID */
char pass[CFG_MAX_PASS_SIZE]; /* Password */
char host[CFG_MAX_HOSTNAME]; /* Password */
uint32_t config; /* Bit field register */
uint16_t ota_port; /* OTA port */
uint8_t filler[40]; /* in case adding data in config avoiding loosing current conf by bad crc*/
uint16_t crc;
} _Config;
// Exported variables/object instancied in main sketch
// ===================================================
extern _Config config;
// Declared exported function from route.cpp
// ===================================================
bool readConfig (void);
bool saveConfig (void);
#endif

View File

@ -1,562 +0,0 @@
// **********************************************************************************
// ESP8266 Teleinfo WEB Server, route web function
// **********************************************************************************
// Creative Commons Attrib Share-Alike License
// You are free to use/extend this library but please abide with the CC-BY-SA license:
// Attribution-NonCommercial-ShareAlike 4.0 International License
// http://creativecommons.org/licenses/by-nc-sa/4.0/
//
// For any explanation about teleinfo ou use, see my blog
// http://hallard.me/category/tinfo
//
// This program works with the Wifinfo board
// see schematic here https://github.com/hallard/teleinfo/tree/master/Wifinfo
//
// Written by Charles-Henri Hallard (http://hallard.me)
//
// History : V1.00 2015-06-14 - First release
//
// All text above must be included in any redistribution.
//
// **********************************************************************************
// Include Arduino header
#include "route.h"
/* ======================================================================
Function: handleRoot
Purpose : handle main page /, display information
Input : -
Output : -
Comments: -
====================================================================== */
void handleRoot(void)
{
String response="";
// Just to debug where we are
Debug(F("Serving / page..."));
LedBluON();
// start HTML Code
response += F("<html><head>"
"<meta name='viewport' content='width=device-width, initial-scale=1'>"
"<meta charset='UTF-8'>"
"<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css'>"
"<link rel='stylesheet' href='//cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.8.1/bootstrap-table.min.css'>"
// Our custom style
"<style type='text/css'>"
".nav-tabs {margin-top:4px;}"
".fixed-table-body {height:auto;}"
".progress {position:relative;}"
".progress span {position:absolute;display:block;width:100%;color:black;}"
"</style>"
"<script src='http://code.jquery.com/jquery-2.1.3.min.js'></script>"
"<script src='http://cdn.rawgit.com/Foliotek/AjaxQ/master/ajaxq.js'></script>"
"<title>Wifinfo</title>"
"</head><body>");
response += F("<div class='container'>"
// Onglets
"<ul class='nav nav-tabs' id='myTab'>"
"<li class='active'>"
"<a href='#tab_tinfo' data-toggle='tab'>"
"Téléinformation <span class='badge' id='scharge'></span>"
"</a>"
"</li>"
"<li><a href='#tab_sys' data-toggle='tab'>Système</a></li>"
"<li><a href='#tab_cfg' data-toggle='tab'>Configuration</a></li>"
"</ul>");
// Contenu des onglets
response += F("<div class='tab-content'>");
// tab teleinfo
response += F( "<div class='tab-pane fade in active' id='tab_tinfo'>"
"<h4>Données de Téléinformation</h4>"
"<div><span style='float:left;width:18ex;text-align:center;'>Charge courante : </span>"
"<div class='progress'>"
"<div class='progress-bar progress-bar-success' style='width:0' id='pbar'>"
"<span class=show id='pcharge'>Attente des données</span>"
"</div></div></div>"
"<div id='toolbar'>"
"</div>"
"<table data-toggle='table' "
"data-toolbar='#toolbar'"
"data-url='/tinfojsontbl' "
"class='table table-striped' "
"data-show-refresh='true' "
"data-show-toggle='true' "
"data-show-columns='true' "
"data-search='true' "
"data-row-style='rowStyle' "
"id='tblinfo' "
"data-select-item-name='toolbar1'>"
"<thead>"
"<tr>"
"<th data-field='na' data-align='left' data-sortable='true' data-formatter='labelFormatter'>Etiquette</th>"
"<th data-field='va' data-align='left' data-sortable='true' data-formatter='valueFormatter'>Valeur</th>"
"<th data-field='ck' data-align='center'>Checksum</th>"
"<th data-field='fl' data-align='center' data-visible='false'>Flags</th>"
"</tr>"
"</thead>"
"</table>"
"</div>"); // tab pane
// tab Systeme
response += F( "<div class='tab-pane fade' id='tab_sys'>"
"<h4>Données du système</h4>"
"<table data-toggle='table' "
"data-url='/sysjsontbl' "
"class='table table-striped' "
"data-show-refresh='true' "
"data-show-toggle='true' "
"data-search='true' "
"id='tblsys' "
"data-select-item-name='toolbar2'>"
"<thead>"
"<tr>"
"<th data-field='na' data-align='left'>Donnée</th>"
"<th data-field='va' data-align='left'>Valeur</th>"
"</tr>"
"</thead>"
"</table>"
"</div>"); // tab pane
// tab Configuration
response += F( "<div class='tab-pane fade' id='tab_cfg'>"
"<h4>Configuration du module WifInfo</h4>"
"<p>Cette partie reste à faire, des volontaires motivés ?</p>"
"</div>"); // tab pane
response += F("</div>"); // tab content
/*
response += F("<script>$( document ).ready(function() {"));
response += F("$.getq('queue','/"));
response += F("Uptime"));
response += F("', function(data) { $('#"));
response += F("Uptime"));
response += F("').html(data."));
response += F("Uptime"));
response += F("); });"));
response += F("});</script>"));
*/
response += F("</div>\r\n"); // Container
response += F("<script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js'></script>\r\n");
response += F("<script src='//cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.8.1/bootstrap-table.min.js'></script>\r\n");
response += F("<script src='//cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.8.1/locale/bootstrap-table-fr-FR.min.js'></script>\r\n");
response += F("<script>" "\r\n"
"var counters={};"
"var isousc, iinst;"
"function rowStyle(row, index){" "\r\n"
"var classes=['active','success','info','warning','danger'];" "\r\n"
"var flags=parseInt(row.fl,10);" "\r\n"
"if (flags&0x80){return{classes:classes[4]};}""\r\n"
"if (flags&0x02){return{classes:classes[3]};}""\r\n"
"if (flags&0x08){return{classes:classes[1]};}""\r\n"
"return {};""\r\n"
"}""\r\n"
"function labelFormatter(value, row){" "\r\n"
"var flags=parseInt(row.fl,10);" "\r\n"
"if (typeof counters[value]==='undefined') counters[value]=1;" "\r\n"
"if (flags&0x88) counters[value]++;" "\r\n"
"return value + ' <span class=\"badge\">'+counters[value]+'</span>';" "\r\n"
"}" "\r\n"
"function valueFormatter(value, row){" "\r\n"
"if (row.na==\"ISOUSC\")" "\r\n"
"isousc=parseInt(value);" "\r\n"
"else if (row.na==\"IINST\"){" "\r\n"
"var pb, pe, cl;" "\r\n"
"iinst=parseInt(value);" "\r\n"
"pe=parseInt(iinst*100/isousc);" "\r\n"
"if (isNaN(pe)) pe=0;" "\r\n"
"cl='success';" "\r\n"
"if (pe>70) cl ='info';" "\r\n"
"if (pe>80) cl ='warning';" "\r\n"
"if (pe>90) cl ='danger';" "\r\n"
"cl = 'progress-bar-' + cl;" "\r\n"
"if (pe>0) $('#scharge').text(pe+'%');" "\r\n"
"if (typeof isousc!='undefined')" "\r\n"
" $('#pcharge').text(iinst+'A / '+isousc+'A');" "\r\n"
"$('#pbar').attr('class','progress-bar '+cl);" "\r\n"
"$('#pbar').css('width', pe+'%');"" \r\n"
"}" "\r\n"
"return value;" "\r\n"
"}" "\r\n"
"</script>" "\r\n");
response += F("<script>" "\r\n"
"var myTimer;" "\r\n"
"function myRefresh(){" "\r\n"
"var id=$('.nav-tabs .active > a').attr('href');" "\r\n"
"if (id=='#tab_tinfo') id='#tblinfo';" "\r\n"
"if (id=='#tab_sys') id='#tblsys';" "\r\n"
#ifdef DEBUG
"console.log('Refreshing : '+id);" "\r\n"
#endif
"clearInterval(myTimer);" "\r\n"
"$('#tblinfo').bootstrapTable('refresh',{silent: true});" "\r\n"
"if (id=='#tblsys')" "\r\n"
"$(id).bootstrapTable('refresh',{silent: true});" "\r\n"
"}""\r\n"
"$(function () {" "\r\n"
"$('#tblinfo').on('load-success.bs.table', function (e, data) {" "\r\n"
#ifdef DEBUG
"console.log('Event: load-success.bs.table');" "\r\n"
#endif
"myTimer=setInterval(function(){myRefresh()},1000);" "\r\n"
"})" "\r\n"
".on('load-error.bs.table', function (e, status) {" "\r\n"
#ifdef DEBUG
"console.log('Event: load-error.bs.table');" "\r\n"
#endif
"myTimer=setInterval(function(){myRefresh()},5000);" "\r\n"
"})" "\r\n"
"});" "\r\n"
"</script>");
response += F("</body></html>\r\n");
// Just to debug buffer free size
Debug(F("sending "));
Debug(response.length());
Debug(F(" bytes..."));
// Send response to client
server.send ( 200, "text/html", response );
Debug(F("OK!"));
LedBluOFF();
}
/* ======================================================================
Function: formatNumberJSON
Purpose : check if data value is full number and send correct JSON format
Input : String where to add response
char * value to check
Output : -
Comments: 00150 => 150
ADCO => "ADCO"
1 => 1
====================================================================== */
void formatNumberJSON( String &response, char * value)
{
// we have at least something ?
if (value && strlen(value))
{
boolean isNumber = true;
uint8_t c;
char * p = value;
// just to be sure
if (strlen(p)<=16) {
// check if value is number
while (*p && isNumber) {
if ( *p < '0' || *p > '9' )
isNumber = false;
p++;
}
// this will add "" on not number values
if (!isNumber) {
response += '\"' ;
response += value ;
response += F("\":") ;
} else {
// this will remove leading zero on numbers
p = value;
while (*p=='0' && *(p+1) )
p++;
response += p ;
}
} else {
Debugln(F("formatNumberJSON error!"));
}
}
}
/* ======================================================================
Function: tinfoJSONTable
Purpose : dump all teleinfo values in JSON table format for browser
Input : linked list pointer on the concerned data
true to dump all values, false for only modified ones
Output : -
Comments: -
====================================================================== */
void tinfoJSONTable(void)
{
ValueList * me = tinfo.getList();
String response = "";
// Just to debug where we are
Debug(F("Serving /tinfoJSONTable page...\r\n"));
// Got at least one ?
if (me) {
uint8_t index=0;
boolean first_item = true;
// Json start
response += F("[\r\n");
// Loop thru the node
while (me->next) {
// we're there
ESP.wdtFeed();
// go to next node
me = me->next;
// First item do not add , separator
if (first_item)
first_item = false;
else
response += F(",\r\n");
Debug(F("(")) ;
Debug(++index) ;
Debug(F(") ")) ;
if (me->name) Debug(me->name) ;
else Debug(F("NULL")) ;
Debug(F("=")) ;
if (me->value) Debug(me->value) ;
else Debug(F("NULL")) ;
Debug(F(" '")) ;
Debug(me->checksum) ;
Debug(F("' "));
// Flags management
if ( me->flags) {
Debug(F("Flags:0x"));
Debugf("%02X => ", me->flags);
if ( me->flags & TINFO_FLAGS_EXIST)
Debug(F("Exist ")) ;
if ( me->flags & TINFO_FLAGS_UPDATED)
Debug(F("Updated ")) ;
if ( me->flags & TINFO_FLAGS_ADDED)
Debug(F("New ")) ;
}
Debugln() ;
response += F("{\"na\":\"");
response += me->name ;
response += F("\", \"va\":\"") ;
response += me->value;
response += F("\", \"ck\":\"") ;
if (me->checksum == '"' || me->checksum == '\\' || me->checksum == '/')
response += '\\';
response += (char) me->checksum;
response += F("\", \"fl\":");
response += me->flags ;
response += '}' ;
}
// Json end
response += F("\r\n]");
} else {
Debugln(F("sending 404..."));
server.send ( 404, "text/plain", "No data" );
}
Debug(F("sending..."));
server.send ( 200, "text/json", response );
Debugln(F("OK!"));
}
/* ======================================================================
Function: sysJSONTable
Purpose : dump all sysinfo values in JSON table format for browser
Input : linked list pointer on the concerned data
true to dump all values, false for only modified ones
Output : -
Comments: -
====================================================================== */
void sysJSONTable()
{
String response = "";
// Just to debug where we are
Debug(F("Serving /sysJSONTable page..."));
// Json start
response += F("[\r\n");
response += "{\"na\":\"Uptime\",\"va\":\"";
response += sysinfo.sys_uptime;
response += "\"},\r\n";
response += "{\"na\":\"Compile le\",\"va\":\"" __DATE__ " " __TIME__ "\"},\r\n";
response += "{\"na\":\"Free Ram\",\"va\":\"";
response += sysinfo.sys_free_ram;
response += "\"},\r\n";
response += "{\"na\":\"Flash Real Size\",\"va\":\"";
response += sysinfo.sys_flash_real_size ;
response += "\"},\r\n";
response += "{\"na\":\"Firmware Size\",\"va\":\"";
response += sysinfo.sys_firmware_size;
response += "\"},\r\n";
response += "{\"na\":\"Free Size\",\"va\":\"";
response += sysinfo.sys_firmware_free;
response += "\"},\r\n";
response += "{\"na\":\"Wifi SSID\",\"va\":\"";
response += config.ssid;
response += "\"},\r\n";
response += "{\"na\":\"OTA Network Port\",\"va\":";
response += config.ota_port ;
response += "},\r\n";
response += "{\"na\":\"Wifi RSSI\",\"va\":\"";
response += WiFi.RSSI() ;
response += " dB\"}\r\n";
response += "{\"na\":\"Analog\",\"va\":\"";
response += sysinfo.sys_analog;
response += " dB\"}\r\n";
// Json end
response += F("]\r\n");
Debug(F("sending..."));
server.send ( 200, "text/json", response );
Debugln(F("Ok!"));
}
/* ======================================================================
Function: sendJSON
Purpose : dump all values in JSON
Input : linked list pointer on the concerned data
true to dump all values, false for only modified ones
Output : -
Comments: -
====================================================================== */
void sendJSON(void)
{
ValueList * me = tinfo.getList();
String response = "";
// Got at least one ?
if (me) {
// Json start
response += F("{\"_UPTIME\":");
response += seconds;
// Loop thru the node
while (me->next) {
// we're there
ESP.wdtFeed();
// go to next node
me = me->next;
response += F(",\"") ;
response += me->name ;
response += F("\":") ;
formatNumberJSON(response, me->value);
}
// Json end
response += F("}\r\n") ;
} else {
server.send ( 404, "text/plain", "No data" );
}
server.send ( 200, "text/json", response );
}
/* ======================================================================
Function: handleNotFound
Purpose : default WEB routing when URI is not found
Input : linked list pointer on the concerned data
true to dump all values, false for only modified ones
Output : -
Comments: We search is we have a name that match to this URI, if one we
return it's pair name/value in json
====================================================================== */
void handleNotFound(void)
{
String response = "";
// We check for an known label
ValueList * me = tinfo.getList();
const char * uri;
boolean found = false;
// Led on
LedBluON();
// convert uri to char * for compare
uri = server.uri().c_str();
Debugf("handleNotFound(%s)\r\n", uri);
// Got at least one and consistent URI ?
if (me && uri && *uri=='/' && *++uri ) {
// Loop thru the linked list of values
while (me->next && !found) {
// we're there
ESP.wdtFeed();
// go to next node
me = me->next;
//Debugf("compare to '%s' ", me->name);
// Do we have this one ?
if (stricmp (me->name, uri) == 0 )
{
// no need to continue
found = true;
// Add to respone
response += F("{\"") ;
response += me->name ;
response += F("\":") ;
formatNumberJSON(response, me->value);
response += F("}\r\n");
}
}
}
// Got it, send json
if (found) {
server.send ( 200, "text/json", response );
} else {
// send error message in plain text
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += ( server.method() == HTTP_GET ) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for ( uint8_t i = 0; i < server.args(); i++ ) {
message += " " + server.argName ( i ) + ": " + server.arg ( i ) + "\n";
}
server.send ( 404, "text/plain", message );
}
// Led off
LedBluOFF();
}

View File

@ -26,6 +26,7 @@
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266mDNS.h>
#include <WiFiUDP.h>
#include <EEPROM.h>
@ -34,23 +35,33 @@
//#include <Hash.h>
#include <NeoPixelBus.h>
#include <LibTeleinfo.h>
#include <fs.h>
#include "route.h"
extern "C" {
#include "user_interface.h"
}
#include "webserver.h"
#include "webclient.h"
#include "config.h"
#define DEBUG
#define DEBUG_SERIAL Serial1
#define DEBUG_SERIAL1
#define WIFINFO_VERSION "1.0.0"
// I prefix debug macro to be sure to use specific for THIS library
// debugging, this should not interfere with main sketch or other
// libraries
#ifdef DEBUG
#define Debug(x) Serial1.print(x)
#define Debugln(x) Serial1.println(x)
#define DebugF(x) Serial1.print(F(x))
#define DebuglnF(x) Serial1.println(F(x))
#define Debugf(...) Serial1.printf(__VA_ARGS__)
#define Debugflush Serial1.flush
#define Debug(x) DEBUG_SERIAL.print(x)
#define Debugln(x) DEBUG_SERIAL.println(x)
#define DebugF(x) DEBUG_SERIAL.print(F(x))
#define DebuglnF(x) DEBUG_SERIAL.println(F(x))
#define Debugf(...) DEBUG_SERIAL.printf(__VA_ARGS__)
#define Debugflush DEBUG_SERIAL.flush
#else
#define Debug(x) {}
#define Debugln(x) {}
@ -92,13 +103,6 @@
typedef struct
{
String sys_uptime;
String sys_free_ram;
String sys_flash_real_size;
String sys_flash_speed;
String sys_firmware_size;
String sys_firmware_free;
String sys_analog;
String sys_eep_config;
} _sysinfo;
// Exported variables/object instancied in main sketch
@ -110,7 +114,15 @@ extern NeoPixelBus rgb_led ;
extern uint8_t rgb_brightness;
extern unsigned long seconds;
extern _sysinfo sysinfo;
extern Ticker Tick_emoncms;
extern Ticker Tick_jeedom;
// Exported function located in main sketch
// ===================================================
void ResetConfig(void);
void Task_emoncms();
void Task_jeedom();
#endif

View File

@ -0,0 +1,766 @@
// **********************************************************************************
// ESP8266 Teleinfo WEB Server
// **********************************************************************************
// Creative Commons Attrib Share-Alike License
// You are free to use/extend this library but please abide with the CC-BY-SA license:
// Attribution-NonCommercial-ShareAlike 4.0 International License
// http://creativecommons.org/licenses/by-nc-sa/4.0/
//
// For any explanation about teleinfo ou use , see my blog
// http://hallard.me/category/tinfo
//
// This program works with the Wifinfo board
// see schematic here https://github.com/hallard/teleinfo/tree/master/Wifinfo
//
// Written by Charles-Henri Hallard (http://hallard.me)
//
// History : V1.00 2015-06-14 - First release
//
// All text above must be included in any redistribution.
//
// **********************************************************************************
// Include Arduino header
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <EEPROM.h>
#include <Ticker.h>
//#include <WebSocketsServer.h>
//#include <Hash.h>
#include <NeoPixelBus.h>
#include <LibTeleinfo.h>
#include <fs.h>
// Global project file
#include "Wifinfo.h"
//WiFiManager wifi(0);
ESP8266WebServer server(80);
bool ota_blink;
// Teleinfo
TInfo tinfo;
// RGB Loed
NeoPixelBus rgb_led = NeoPixelBus(1, RGB_LED_PIN, NEO_RGB | NEO_KHZ800);
// define whole brigtness level for RGBLED
uint8_t rgb_brightness = 127;
// LED Blink timers
Ticker rgb_ticker;
Ticker blu_ticker;
Ticker red_ticker;
Ticker Every_1_Sec;
Ticker Tick_emoncms;
Ticker Tick_jeedom;
volatile boolean task_1_sec = false;
volatile boolean task_emoncms = false;
volatile boolean task_jeedom = false;
unsigned long seconds = 0;
// sysinfo data
_sysinfo sysinfo;
/* ======================================================================
Function: UpdateSysinfo
Purpose : update sysinfo variables
Input : true if first call
true if needed to print on serial debug
Output : -
Comments: -
====================================================================== */
void UpdateSysinfo(boolean first_call, boolean show_debug)
{
char buff[64];
int32_t adc;
int sec = seconds;
int min = sec / 60;
int hr = min / 60;
sprintf_P( buff, PSTR("%02d:%02d:%02d"), hr, min % 60, sec % 60);
sysinfo.sys_uptime = buff;
}
/* ======================================================================
Function: Task_1_Sec
Purpose : update our second ticker
Input : -
Output : -
Comments: -
====================================================================== */
void Task_1_Sec()
{
task_1_sec = true;
seconds++;
}
/* ======================================================================
Function: Task_emoncms
Purpose : callback of emoncms ticker
Input :
Output : -
Comments: Like an Interrupt, need to be short, we set flag for main loop
====================================================================== */
void Task_emoncms()
{
task_emoncms = true;
}
/* ======================================================================
Function: Task_jeedom
Purpose : callback of jeedom ticker
Input :
Output : -
Comments: Like an Interrupt, need to be short, we set flag for main loop
====================================================================== */
void Task_jeedom()
{
task_jeedom = true;
}
/* ======================================================================
Function: LedOff
Purpose : callback called after led blink delay
Input : led (defined in term of PIN)
Output : -
Comments: -
====================================================================== */
void LedOff(int led)
{
#ifdef BLU_LED_PIN
if (led==BLU_LED_PIN)
LedBluOFF();
#endif
if (led==RED_LED_PIN)
LedRedOFF();
if (led==RGB_LED_PIN)
LedRGBOFF();
}
/* ======================================================================
Function: ADPSCallback
Purpose : called by library when we detected a ADPS