-
Notifications
You must be signed in to change notification settings - Fork 32
/
espRFLinkMQTT.h
181 lines (149 loc) · 13.6 KB
/
espRFLinkMQTT.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// Include Arduino header
#include <PubSubClient.h>
#include <ESP8266WiFi.h>
#include <ArduinoOTA.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPUpdateServer.h>
#include <EEPROM.h>
#include "Rflink.h"
#define CFG_SSID_SIZE 32
#define CFG_PSK_SIZE 64
#define CFG_HOSTNAME_SIZE 32
#define CFG_MQTT_SERVER_SIZE 32
#define CFG_MQTT_USER_SIZE 32
#define CFG_MQTT_PASSWORD_SIZE 32
#ifdef LOAD_TEST
#define FILTERED_ID_SIZE 64
#else
#ifndef FILTERED_ID_SIZE
#define FILTERED_ID_SIZE 32
// Tested and works with 64 max (free mem left is ~6KB). Above it will exceed eeprom size and web interface may crash because of memory shortage
#endif
#endif
//********************************************************************************
// Structures definition
//********************************************************************************
// Structure for ID filtering
typedef struct {
char id[MAX_ID_LEN+1]; // ID
char id_applied[MAX_ID_LEN+1]; // ID applied -> used for MQTT topic
unsigned long publish_interval; // interval to publish (ms) if data did not change
char description[MAX_DATA_LEN+1]; // Device description
} _filtered_IDs;
// Import user configuration
#include "config.h"
// Structure for MQTT configuration
typedef struct
{
char server[CFG_MQTT_SERVER_SIZE+1]; // Broker
char user[CFG_MQTT_USER_SIZE+1]; // User
char password[CFG_MQTT_PASSWORD_SIZE+1]; // Pass
uint16_t port; // Protocol port
} _mqtt;
// Structure for configuration
typedef struct { // structure to store configuration in EEPROM memory
char ssid[CFG_SSID_SIZE+1]; // SSID
char psk[CFG_PSK_SIZE+1]; // Pre shared key
char hostname[CFG_HOSTNAME_SIZE+1]; // Hostname
_mqtt mqtt; // MQTT configuration
int mega_reset_pin; // Pin to reset MEGA
unsigned long resetMegaInterval; // Auto reset MEGA interval
bool id_filtering; // ID filtering enabled
_filtered_IDs filtered_id[FILTERED_ID_SIZE];
bool settings_locked; // Lock settings and block access point
unsigned int version;
} _eepromConfig;
// Structure for matrix
typedef struct { // structure for additionnal data when IP filtering is used
char json[3*MAX_DATA_LEN]; // store last json message
unsigned long last_published; // store last published time (millis)
unsigned long last_received; // store last received time (millis)
} _matrix;
//********************************************************************************
// Constants and global variables
//********************************************************************************
const int filtered_id_number = min(FILTERED_ID_SIZE,(int) (sizeof(filtered_IDs) / sizeof(filtered_IDs[0])));
bool MQTT_DEBUG = 0; // debug variable to publish received data on MQTT debug topic ; default is disabled, can be enabled from web interface
int mqttConnectionAttempts = 0;
#define MQTT_CHECK_INTERVAL 10000L // check MQTT connection every 10s ; not too low to leave time for other functions (web interface, ...)
unsigned long lastMqttConnect = 0; // timer to check MQTT connection ; negative value forces it at startup
unsigned long lastReceived = 0; // store last received data time from RFLink
#define UPTIME_INTERVAL 300000L // publish uptime every 5 min
unsigned long lastUptime = 0; // timer to publish uptime on MQTT server ; negative value forces update at startup
unsigned long now = 0;
int resetMegaCounter = 0; // number of times Mega is reset
_eepromConfig eepromConfig;
const int eepromAddress = 0; // Start address for eeprom config
const int eepromConfigMaxSize = max(3840, (int) sizeof(eepromConfig)); // Length for eeprom config - leaves some space
_matrix matrix[FILTERED_ID_SIZE];
// main input / output buffers
char BUFFER [BUFFER_SIZE];
char JSON [BUFFER_SIZE];
// message builder buffers
char MQTT_NAME[MAX_DATA_LEN];
char MQTT_ID [MAX_ID_LEN+1];
char MQTT_TOPIC[MAX_TOPIC_LEN];
char FIELD_BUF[MAX_DATA_LEN];
// Serial iterator counter
int CPT;
#ifdef EXPERIMENTAL
// TEST lost packets
char LINE_NUMBER[3];
byte line_number = 0;
int lost_packets = -1;
#endif
//********************************************************************************
// CSS and html in PROGMEM
//********************************************************************************
static const char cssDatasheet[] PROGMEM = "" // CSS
"" // from ESP Easy Mega release 20180914 - first part till @media-screen
"* {font-family: sans-serif; font-size: 12pt; margin: 0px; padding: 0px; box-sizing: border-box; }h1 {font-size: 16pt; color: #07D; margin: 8px 0; font-weight: bold; }h2 {font-size: 12pt; margin: 0 -4px; padding: 6px; background-color: #444; color: #FFF; font-weight: bold; }h3 {font-size: 12pt; margin: 16px -4px 0 -4px; padding: 4px; background-color: #EEE; color: #444; font-weight: bold; }h6 {font-size: 10pt; color: #07D; }pre, xmp, code, kbd, samp, tt{ font-family:monospace,monospace; font-size:1em; }.button {margin: 4px; padding: 4px 16px; background-color: #07D; color: #FFF; text-decoration: none; border-radius: 4px; border: none;}.button.link { }.button.link.wide {display: inline-block; width: 100%; text-align: center;}.button.link.red {background-color: red;}.button.help {padding: 2px 4px; border-style: solid; border-width: 1px; border-color: gray; border-radius: 50%; }.button:hover {background: #369; }input, select, textarea {margin: 4px; padding: 4px 8px; border-radius: 4px; background-color: #eee; border-style: solid; border-width: 1px; border-color: gray;}input:hover {background-color: #ccc; }input.wide {max-width: 500px; width:80%; }input.widenumber {max-width: 500px; width:100px; }#selectwidth {max-width: 500px; width:80%; padding: 4px 8px;}select:hover {background-color: #ccc; }.container {display: block; padding-left: 35px; margin-left: 4px; margin-top: 0px; position: relative; cursor: pointer; font-size: 12pt; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; }.container input {position: absolute; opacity: 0; cursor: pointer; }.checkmark {position: absolute; top: 0; left: 0; height: 25px; width: 25px; background-color: #eee; border-style: solid; border-width: 1px; border-color: gray; border-radius: 4px;}.container:hover input ~ .checkmark {background-color: #ccc; }.container input:checked ~ .checkmark { background-color: #07D; }.checkmark:after {content: ''; position: absolute; display: none; }.container input:checked ~ .checkmark:after {display: block; }.container .checkmark:after {left: 7px; top: 3px; width: 5px; height: 10px; border: solid white; border-width: 0 3px 3px 0; -webkit-transform: rotate(45deg); -ms-transform: rotate(45deg); transform: rotate(45deg); }.container2 {display: block; padding-left: 35px; margin-left: 9px; margin-bottom: 20px; position: relative; cursor: pointer; font-size: 12pt; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; }.container2 input {position: absolute; opacity: 0; cursor: pointer; }.dotmark {position: absolute; top: 0; left: 0; height: 26px; width: 26px; background-color: #eee; border-style: solid; border-width: 1px; border-color: gray; border-radius: 50%;}.container2:hover input ~ .dotmark {background-color: #ccc; }.container2 input:checked ~ .dotmark { background-color: #07D;}.dotmark:after {content: ''; position: absolute; display: none; }.container2 input:checked ~ .dotmark:after {display: block; }.container2 .dotmark:after {top: 8px; left: 8px; width: 8px; height: 8px; border-radius: 50%; background: white; }#toastmessage {visibility: hidden; min-width: 250px; margin-left: -125px; background-color: #07D;color: #fff; text-align: center; border-radius: 4px; padding: 16px; position: fixed;z-index: 1; left: 282px; bottom: 30%; font-size: 17px; border-style: solid; border-width: 1px; border-color: gray;}#toastmessage.show {visibility: visible; -webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s; animation: fadein 0.5s, fadeout 0.5s 2.5s; }@-webkit-keyframes fadein {from {bottom: 20%; opacity: 0;} to {bottom: 30%; opacity: 0.9;} }@keyframes fadein {from {bottom: 20%; opacity: 0;} to {bottom: 30%; opacity: 0.9;} }@-webkit-keyframes fadeout {from {bottom: 30%; opacity: 0.9;} to {bottom: 0; opacity: 0;} }@keyframes fadeout {from {bottom: 30%; opacity: 0.9;} to {bottom: 0; opacity: 0;} }.level_0 { color: #F1F1F1; }.level_1 { color: #FCFF95; }.level_2 { color: #9DCEFE; }.level_3 { color: #A4FC79; }.level_4 { color: #F2AB39; }.level_9 { color: #FF5500; }.logviewer { color: #F1F1F1; background-color: #272727; font-family: 'Lucida Console', Monaco, monospace; height: 530px; max-width: 1000px; width: 80%; padding: 4px 8px; overflow: auto; border-style: solid; border-color: gray; }textarea {max-width: 1000px; width:80%; padding: 4px 8px; font-family: 'Lucida Console', Monaco, monospace; }textarea:hover {background-color: #ccc; }table.normal th {padding: 6px; background-color: #444; color: #FFF; border-color: #888; font-weight: bold; }table.normal td {padding: 4px; height: 30px;}table.normal tr {padding: 4px; }table.normal {color: #000; width: 100%; min-width: 420px; border-collapse: collapse; }table.multirow th {padding: 6px; background-color: #444; color: #FFF; border-color: #888; font-weight: bold; }table.multirow td {padding: 4px; text-align: center; height: 30px;}table.multirow tr {padding: 4px; }table.multirow tr:nth-child(even){background-color: #DEE6FF; }table.multirow {color: #000; width: 100%; min-width: 420px; border-collapse: collapse; }tr.highlight td { background-color: #dbff0075; }.note {color: #444; font-style: italic; }.headermenu {position: fixed; top: 0; left: 0; right: 0; height: 90px; padding: 8px 12px; background-color: #F8F8F8; border-bottom: 1px solid #DDD; z-index: 1;}.apheader {padding: 8px 12px; background-color: #F8F8F8;}.bodymenu {margin-top: 96px;}.menubar {position: inherit; top: 55px; }.menu {float: left; padding: 4px 16px 8px 16px; color: #444; white-space: nowrap; border: solid transparent; border-width: 4px 1px 1px; border-radius: 4px 4px 0 0; text-decoration: none; }.menu.active {color: #000; background-color: #FFF; border-color: #07D #DDD #FFF; }.menu:hover {color: #000; background: #DEF; }.menu_button {display: none;}.on {color: green; }.off {color: red; }.div_l {float: left; }.div_r {float: right; margin: 2px; padding: 1px 10px; border-radius: 4px; background-color: #080; color: white; }.div_br {clear: both; }.alert {padding: 20px; background-color: #f44336; color: white; margin-bottom: 15px; }.warning {padding: 20px; background-color: #ffca17; color: white; margin-bottom: 15px; }.closebtn {margin-left: 15px; color: white; font-weight: bold; float: right; font-size: 22px; line-height: 20px; cursor: pointer; transition: 0.3s; }.closebtn:hover {color: black; }section{overflow-x: auto; width: 100%; }\r\n"
"" // force some changes in CSS compared to ESP Easy Mega
"table.multirow td {text-align: center; font-size:0.9em;} table.multirow input {font-size:0.9em} h3 {margin: 16px 0px 0px 0px;}\r\n"
"table.condensed td {padding: 0px 20px 0px 5px; height: 1em;}table.condensed tr {padding: 0px; }table.condensed {padding: 0px;border-left:1px solid #EEE;} .button.link {font-weight:normal;} #menunotification {margin-left: 2em;border-left: 1px solid #EEE;padding-left: 10px;padding-top:5px;} #menunotification td {min-width: 250px;} #configuration_table input[type=number] {width: 8em;} table.high td {height: 40px;} #cmds a {float:left;}\r\n"
"table.multirow-left td {text-align: left;} table.multirow td.t-left, th.t-left {text-align: left;}\r\n"
"" // from ESP Easy Mega release 20180914 - second part
"@media screen and (max-width: 420) {span.showmenulabel { display: none; }.menu { max-width: 11vw; max-width: 48px; }\r\n";
static const char htmlMenu[] PROGMEM = "" // Menu
"<header class='headermenu'>\r\n"
"<script>\r\n"
"function fetchAndNotify(url) {\r\n"
" scroll(0,0);"
" fetch(url)\r\n"
" .then(function(response) {\r\n"
" return response.text();\r\n"
" })\r\n"
" .then(function(text) {\r\n"
" console.log('Request successful: ', text);\r\n"
" document.getElementById('menunotification').innerHTML = text;\r\n"
" })\r\n"
" .catch(function(error) {\r\n"
" log('Request failed', error);\r\n"
" });\r\n"
" };\r\n"
"</script>\r\n"
"<h1 id='menutitle'>espRFLinkMQTT</h1>\r\n"
"<div class='menubar'>"
"<a id='menuhome' class='menu' href='.'>⌂<span class='showmenulabel'>Home</span></a>\r\n"
"<a id='menuid' class='menu' href='/idfiltering'>⧴<span class='showmenulabel'>ID Filtering</span></a>\r\n"
"<a id='menulivedata' class='menu' href='/livedata'>💬<span class='showmenulabel'>Live Data</span></a>\r\n"
"<a id='menusystem' class='menu' href='/system'>🔧<span class='showmenulabel'>System</span></a>\r\n"
"</div>\r\n"
"</header>\r\n"
"<div id='menunotification' style='margin-left:2em;'></div>\r\n"
"";
static const char htmlStart[] PROGMEM = "" // Html page start part
"<!DOCTYPE html>\r\n<html>\r\n"
"<head><title>espRFLinkMQTT</title>\r\n"
"<meta name='viewport' content='width=device-width, initial-scale=1.0'>\r\n"
"<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />\r\n"
"<link rel=\"stylesheet\" type=\"text/css\" href=\"/esp.css\">\r\n"
"</head>\r\n"
"<body class='bodymenu'>\r\n";
static const char htmlEnd[] PROGMEM = "" // Html page end part
"<br><footer><h6>Powered by <a href='https://github.com/seb821' style='font-size: 0.9em; text-decoration: none'>github.com/seb821</a></h6></footer>\r\n"
"</body>\r\n</html>\r\n";
#ifdef RFLINK_WIFI_BOARD
#include <Wire.h> // RFLInk WiFi board
unsigned long lastSecond = 0;
#endif