Browse Source

Option to build without web server support

fastled
Xose Pérez 7 years ago
parent
commit
fb60201cf1
16 changed files with 374 additions and 219 deletions
  1. +11
    -7
      code/espurna/analog.ino
  2. +3
    -2
      code/espurna/config/arduino.h
  3. +23
    -6
      code/espurna/config/general.h
  4. +4
    -0
      code/espurna/config/prototypes.h
  5. +16
    -9
      code/espurna/dht.ino
  6. +12
    -6
      code/espurna/ds18b20.ino
  7. +20
    -16
      code/espurna/emon.ino
  8. +4
    -1
      code/espurna/espurna.ino
  9. +28
    -24
      code/espurna/hlw8012.ino
  10. +58
    -52
      code/espurna/light.ino
  11. +6
    -2
      code/espurna/ntp.ino
  12. +6
    -2
      code/espurna/ota.ino
  13. +17
    -5
      code/espurna/relay.ino
  14. +18
    -10
      code/espurna/rfbridge.ino
  15. +103
    -77
      code/espurna/web.ino
  16. +45
    -0
      code/espurna/wifi.ino

+ 11
- 7
code/espurna/analog.ino View File

@ -19,10 +19,12 @@ unsigned int getAnalog() {
void analogSetup() { void analogSetup() {
pinMode(ANALOG_PIN, INPUT); pinMode(ANALOG_PIN, INPUT);
apiRegister(ANALOG_TOPIC, ANALOG_TOPIC, [](char * buffer, size_t len) {
snprintf_P(buffer, len, PSTR("%d"), getAnalog());
});
#if WEB_SUPPORT
apiRegister(ANALOG_TOPIC, ANALOG_TOPIC, [](char * buffer, size_t len) {
snprintf_P(buffer, len, PSTR("%d"), getAnalog());
});
#endif
} }
@ -51,9 +53,11 @@ void analogLoop() {
#endif #endif
// Update websocket clients // Update websocket clients
char buffer[100];
sprintf_P(buffer, PSTR("{\"analogVisible\": 1, \"analogValue\": %d}"), analog);
wsSend(buffer);
#if WEB_SUPPORT
char buffer[100];
sprintf_P(buffer, PSTR("{\"analogVisible\": 1, \"analogValue\": %d}"), analog);
wsSend(buffer);
#endif
} }


+ 3
- 2
code/espurna/config/arduino.h View File

@ -46,6 +46,8 @@
//#define ALEXA_SUPPORT 0 //#define ALEXA_SUPPORT 0
//#define ANALOG_SUPPORT 1 //#define ANALOG_SUPPORT 1
//#define DEBUG_SERIAL_SUPPORT 0
//#define DEBUG_UDP_SUPPORT 1
//#define DHT_SUPPORT 1 //#define DHT_SUPPORT 1
//#define DOMOTICZ_SUPPORT 0 //#define DOMOTICZ_SUPPORT 0
//#define DS18B20_SUPPORT 1 //#define DS18B20_SUPPORT 1
@ -55,7 +57,6 @@
//#define MDNS_SUPPORT 0 //#define MDNS_SUPPORT 0
//#define NOFUSS_SUPPORT 1 //#define NOFUSS_SUPPORT 1
//#define RF_SUPPORT 1 //#define RF_SUPPORT 1
//#define DEBUG_SERIAL_SUPPORT 0
//#define SPIFFS_SUPPORT 1 //#define SPIFFS_SUPPORT 1
//#define TERMINAL_SUPPORT 0 //#define TERMINAL_SUPPORT 0
//#define DEBUG_UDP_SUPPORT 1
//#define WEB_SUPPORT 0

+ 23
- 6
code/espurna/config/general.h View File

@ -227,10 +227,28 @@ PROGMEM const char* const custom_reset_string[] = {
#define WIFI_MAX_NETWORKS 5 // Max number of WIFI connection configurations #define WIFI_MAX_NETWORKS 5 // Max number of WIFI connection configurations
#define WIFI_AP_MODE AP_MODE_ALONE #define WIFI_AP_MODE AP_MODE_ALONE
// Optional hardcoded configuration (up to 2 different networks)
//#define WIFI1_SSID "..."
//#define WIFI1_PASS "..."
//#define WIFI1_IP "192.168.1.201"
//#define WIFI1_GW "192.168.1.1"
//#define WIFI1_MASK "255.255.255.0"
//#define WIFI1_DNS "8.8.8.8"
//#define WIFI2_SSID "..."
//#define WIFI2_PASS "..."
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// WEB // WEB
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#ifndef WEB_SUPPORT
#define WEB_SUPPORT 1 // This enables web support (http, api)
#endif
#ifndef WEB_EMBEDDED
#define WEB_EMBEDDED 1 // This option builds the firmware with the web interface embedded.
#endif
#define WEB_MODE_NORMAL 0 #define WEB_MODE_NORMAL 0
#define WEB_MODE_PASSWORD 1 #define WEB_MODE_PASSWORD 1
@ -238,15 +256,12 @@ PROGMEM const char* const custom_reset_string[] = {
#define WEB_FORCE_PASS_CHANGE 1 // Force the user to change the password if default one #define WEB_FORCE_PASS_CHANGE 1 // Force the user to change the password if default one
#define WEB_PORT 80 // HTTP port #define WEB_PORT 80 // HTTP port
// This option builds the firmware with the web interface embedded.
#ifndef WEB_EMBEDDED
#define WEB_EMBEDDED 1
#endif
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// WEBSOCKETS // WEBSOCKETS
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// This will only be enabled if WEB_SUPPORT is 1 (this is the default value)
#define WS_BUFFER_SIZE 5 // Max number of secured websocket connections #define WS_BUFFER_SIZE 5 // Max number of secured websocket connections
#define WS_TIMEOUT 1800000 // Timeout for secured websocket #define WS_TIMEOUT 1800000 // Timeout for secured websocket
@ -254,7 +269,9 @@ PROGMEM const char* const custom_reset_string[] = {
// API // API
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#define API_ENABLED 0 // Do not enable API by default
// This will only be enabled if WEB_SUPPORT is 1 (this is the default value)
#define API_ENABLED 0 // Do not enable API by default
#define API_BUFFER_SIZE 10 // Size of the buffer for HTTP GET API responses #define API_BUFFER_SIZE 10 // Size of the buffer for HTTP GET API responses
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------


+ 4
- 0
code/espurna/config/prototypes.h View File

@ -8,13 +8,17 @@
typedef std::function<void(char *, size_t)> apiGetCallbackFunction; typedef std::function<void(char *, size_t)> apiGetCallbackFunction;
typedef std::function<void(const char *)> apiPutCallbackFunction; typedef std::function<void(const char *)> apiPutCallbackFunction;
void apiRegister(const char * url, const char * key, apiGetCallbackFunction getFn, apiPutCallbackFunction putFn = NULL); void apiRegister(const char * url, const char * key, apiGetCallbackFunction getFn, apiPutCallbackFunction putFn = NULL);
void mqttRegister(void (*callback)(unsigned int, const char *, const char *)); void mqttRegister(void (*callback)(unsigned int, const char *, const char *));
String mqttSubtopic(char * topic); String mqttSubtopic(char * topic);
template<typename T> bool setSetting(const String& key, T value); template<typename T> bool setSetting(const String& key, T value);
template<typename T> bool setSetting(const String& key, unsigned int index, T value); template<typename T> bool setSetting(const String& key, unsigned int index, T value);
template<typename T> String getSetting(const String& key, T defaultValue); template<typename T> String getSetting(const String& key, T defaultValue);
template<typename T> String getSetting(const String& key, unsigned int index, T defaultValue); template<typename T> String getSetting(const String& key, unsigned int index, T defaultValue);
template<typename T> void domoticzSend(const char * key, T value); template<typename T> void domoticzSend(const char * key, T value);
template<typename T> void domoticzSend(const char * key, T nvalue, const char * svalue); template<typename T> void domoticzSend(const char * key, T nvalue, const char * svalue);
template<typename T> bool influxDBSend(const char * topic, T payload); template<typename T> bool influxDBSend(const char * topic, T payload);
char * ltrim(char * s); char * ltrim(char * s);

+ 16
- 9
code/espurna/dht.ino View File

@ -29,13 +29,18 @@ unsigned int getDHTHumidity() {
} }
void dhtSetup() { void dhtSetup() {
dht.begin(); dht.begin();
apiRegister(DHT_TEMPERATURE_TOPIC, DHT_TEMPERATURE_TOPIC, [](char * buffer, size_t len) {
dtostrf(_dhtTemperature, len-1, 1, buffer);
});
apiRegister(DHT_HUMIDITY_TOPIC, DHT_HUMIDITY_TOPIC, [](char * buffer, size_t len) {
snprintf_P(buffer, len, PSTR("%d"), _dhtHumidity);
});
#if WEB_SUPPORT
apiRegister(DHT_TEMPERATURE_TOPIC, DHT_TEMPERATURE_TOPIC, [](char * buffer, size_t len) {
dtostrf(_dhtTemperature, len-1, 1, buffer);
});
apiRegister(DHT_HUMIDITY_TOPIC, DHT_HUMIDITY_TOPIC, [](char * buffer, size_t len) {
snprintf_P(buffer, len, PSTR("%d"), _dhtHumidity);
});
#endif
} }
void dhtLoop() { void dhtLoop() {
@ -99,9 +104,11 @@ void dhtLoop() {
#endif #endif
// Update websocket clients // Update websocket clients
char buffer[100];
sprintf_P(buffer, PSTR("{\"dhtVisible\": 1, \"dhtTmp\": %s, \"dhtHum\": %s, \"tmpUnits\": %d}"), temperature, humidity, tmpUnits);
wsSend(buffer);
#if WEB_SUPPORT
char buffer[100];
sprintf_P(buffer, PSTR("{\"dhtVisible\": 1, \"dhtTmp\": %s, \"dhtHum\": %s, \"tmpUnits\": %d}"), temperature, humidity, tmpUnits);
wsSend(buffer);
#endif
} }


+ 12
- 6
code/espurna/ds18b20.ino View File

@ -34,12 +34,16 @@ const char* getDSTemperatureStr() {
} }
void dsSetup() { void dsSetup() {
ds18b20.begin(); ds18b20.begin();
ds18b20.setWaitForConversion(false); ds18b20.setWaitForConversion(false);
apiRegister(DS18B20_TEMPERATURE_TOPIC, DS18B20_TEMPERATURE_TOPIC, [](char * buffer, size_t len) {
dtostrf(_dsTemperature, len-1, 1, buffer);
});
#if WEB_SUPPORT
apiRegister(DS18B20_TEMPERATURE_TOPIC, DS18B20_TEMPERATURE_TOPIC, [](char * buffer, size_t len) {
dtostrf(_dsTemperature, len-1, 1, buffer);
});
#endif
} }
void dsLoop() { void dsLoop() {
@ -104,9 +108,11 @@ void dsLoop() {
#endif #endif
// Update websocket clients // Update websocket clients
char buffer[100];
sprintf_P(buffer, PSTR("{\"dsVisible\": 1, \"dsTmp\": %s, \"tmpUnits\": %d}"), getDSTemperatureStr(), tmpUnits);
wsSend(buffer);
#if WEB_SUPPORT
char buffer[100];
sprintf_P(buffer, PSTR("{\"dsVisible\": 1, \"dsTmp\": %s, \"tmpUnits\": %d}"), getDSTemperatureStr(), tmpUnits);
wsSend(buffer);
#endif
} }


+ 20
- 16
code/espurna/emon.ino View File

@ -111,21 +111,25 @@ void powerMonitorSetup() {
brzo_i2c_end_transaction(); brzo_i2c_end_transaction();
#endif #endif
apiRegister(EMON_APOWER_TOPIC, EMON_APOWER_TOPIC, [](char * buffer, size_t len) {
if (_emonReady) {
snprintf_P(buffer, len, PSTR("%d"), _emonPower);
} else {
buffer = NULL;
}
});
#if WEB_SUPPORT
apiRegister(EMON_CURRENT_TOPIC, EMON_CURRENT_TOPIC, [](char * buffer, size_t len) {
if (_emonReady) {
dtostrf(_emonCurrent, len-1, 3, buffer);
} else {
buffer = NULL;
}
});
apiRegister(EMON_APOWER_TOPIC, EMON_APOWER_TOPIC, [](char * buffer, size_t len) {
if (_emonReady) {
snprintf_P(buffer, len, PSTR("%d"), _emonPower);
} else {
buffer = NULL;
}
});
apiRegister(EMON_CURRENT_TOPIC, EMON_CURRENT_TOPIC, [](char * buffer, size_t len) {
if (_emonReady) {
dtostrf(_emonCurrent, len-1, 3, buffer);
} else {
buffer = NULL;
}
});
#endif // WEB_SUPPORT
} }
@ -163,11 +167,11 @@ void powerMonitorLoop() {
DEBUG_MSG_P(PSTR("[ENERGY] Power: %dW\n"), int(current * voltage)); DEBUG_MSG_P(PSTR("[ENERGY] Power: %dW\n"), int(current * voltage));
// Update websocket clients // Update websocket clients
if (wsConnected()) {
#if WEB_SUPPORT
char text[100]; char text[100];
sprintf_P(text, PSTR("{\"emonVisible\": 1, \"emonApparentPower\": %d, \"emonCurrent\": %s}"), int(current * voltage), String(current, 3).c_str()); sprintf_P(text, PSTR("{\"emonVisible\": 1, \"emonApparentPower\": %d, \"emonCurrent\": %s}"), int(current * voltage), String(current, 3).c_str());
wsSend(text); wsSend(text);
}
#endif
} }


+ 4
- 1
code/espurna/espurna.ino View File

@ -226,7 +226,10 @@ void setup() {
saveSettings(); saveSettings();
} }
webSetup();
#if WEB_SUPPORT
webSetup();
#endif
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE #if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
lightSetup(); lightSetup();
#endif #endif


+ 28
- 24
code/espurna/hlw8012.ino View File

@ -158,28 +158,32 @@ void hlw8012Setup() {
hlw8012RetrieveCalibration(); hlw8012RetrieveCalibration();
// API definitions // API definitions
apiRegister(HLW8012_POWER_TOPIC, HLW8012_POWER_TOPIC, [](char * buffer, size_t len) {
if (_hlwReady) {
snprintf_P(buffer, len, PSTR("%d"), _hlwPower);
} else {
buffer = NULL;
}
});
apiRegister(HLW8012_CURRENT_TOPIC, HLW8012_CURRENT_TOPIC, [](char * buffer, size_t len) {
if (_hlwReady) {
dtostrf(_hlwCurrent, len-1, 3, buffer);
} else {
buffer = NULL;
}
});
apiRegister(HLW8012_VOLTAGE_TOPIC, HLW8012_VOLTAGE_TOPIC, [](char * buffer, size_t len) {
if (_hlwReady) {
snprintf_P(buffer, len, PSTR("%d"), _hlwVoltage);
} else {
buffer = NULL;
}
});
#if WEB_SUPPORT
apiRegister(HLW8012_POWER_TOPIC, HLW8012_POWER_TOPIC, [](char * buffer, size_t len) {
if (_hlwReady) {
snprintf_P(buffer, len, PSTR("%d"), _hlwPower);
} else {
buffer = NULL;
}
});
apiRegister(HLW8012_CURRENT_TOPIC, HLW8012_CURRENT_TOPIC, [](char * buffer, size_t len) {
if (_hlwReady) {
dtostrf(_hlwCurrent, len-1, 3, buffer);
} else {
buffer = NULL;
}
});
apiRegister(HLW8012_VOLTAGE_TOPIC, HLW8012_VOLTAGE_TOPIC, [](char * buffer, size_t len) {
if (_hlwReady) {
snprintf_P(buffer, len, PSTR("%d"), _hlwVoltage);
} else {
buffer = NULL;
}
});
#endif // WEB_SUPPORT
} }
void hlw8012Loop() { void hlw8012Loop() {
@ -244,8 +248,8 @@ void hlw8012Loop() {
} }
voltage_previous = voltage; voltage_previous = voltage;
if (wsConnected()) {
#if WEB_SUPPORT
{
unsigned int apparent = getApparentPower(); unsigned int apparent = getApparentPower();
double factor = getPowerFactor(); double factor = getPowerFactor();
unsigned int reactive = getReactivePower(); unsigned int reactive = getReactivePower();
@ -264,8 +268,8 @@ void hlw8012Loop() {
String output; String output;
root.printTo(output); root.printTo(output);
wsSend(output.c_str()); wsSend(output.c_str());
} }
#endif
if (--report_count == 0) { if (--report_count == 0) {


+ 58
- 52
code/espurna/light.ino View File

@ -391,6 +391,7 @@ void lightUpdate(bool save, bool forward) {
if (forward) lightMQTT(); if (forward) lightMQTT();
// Report color to WS clients (using current brightness setting) // Report color to WS clients (using current brightness setting)
#if WEB_SUPPORT
{ {
DynamicJsonBuffer jsonBuffer; DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject(); JsonObject& root = jsonBuffer.createObject();
@ -410,6 +411,7 @@ void lightUpdate(bool save, bool forward) {
root.printTo(output); root.printTo(output);
wsSend(output.c_str()); wsSend(output.c_str());
} }
#endif
// Delay saving to EEPROM 5 seconds to avoid wearing it out unnecessarily // Delay saving to EEPROM 5 seconds to avoid wearing it out unnecessarily
if (save) colorTicker.once(LIGHT_SAVE_DELAY, _lightColorSave); if (save) colorTicker.once(LIGHT_SAVE_DELAY, _lightColorSave);
@ -461,67 +463,71 @@ void lightBrightness(unsigned int b) {
void _lightAPISetup() { void _lightAPISetup() {
// API entry points (protected with apikey)
if (lightHasColor()) {
#if WEB_SUPPORT
apiRegister(MQTT_TOPIC_COLOR, MQTT_TOPIC_COLOR,
[](char * buffer, size_t len) {
_toRGB(buffer, len, false);
},
[](const char * payload) {
lightColor(payload);
lightUpdate(true, true);
}
);
apiRegister(MQTT_TOPIC_BRIGHTNESS, MQTT_TOPIC_BRIGHTNESS,
[](char * buffer, size_t len) {
snprintf_P(buffer, len, PSTR("%d"), _brightness);
},
[](const char * payload) {
lightBrightness(atoi(payload));
lightUpdate(true, true);
}
);
// API entry points (protected with apikey)
if (lightHasColor()) {
apiRegister(MQTT_TOPIC_KELVIN, MQTT_TOPIC_KELVIN,
[](char * buffer, size_t len) {},
[](const char * payload) {
_fromKelvin(atol(payload));
lightUpdate(true, true);
}
);
apiRegister(MQTT_TOPIC_COLOR, MQTT_TOPIC_COLOR,
[](char * buffer, size_t len) {
_toRGB(buffer, len, false);
},
[](const char * payload) {
lightColor(payload);
lightUpdate(true, true);
}
);
apiRegister(MQTT_TOPIC_BRIGHTNESS, MQTT_TOPIC_BRIGHTNESS,
[](char * buffer, size_t len) {
snprintf_P(buffer, len, PSTR("%d"), _brightness);
},
[](const char * payload) {
lightBrightness(atoi(payload));
lightUpdate(true, true);
}
);
apiRegister(MQTT_TOPIC_KELVIN, MQTT_TOPIC_KELVIN,
[](char * buffer, size_t len) {},
[](const char * payload) {
_fromKelvin(atol(payload));
lightUpdate(true, true);
}
);
apiRegister(MQTT_TOPIC_MIRED, MQTT_TOPIC_MIRED,
[](char * buffer, size_t len) {},
[](const char * payload) {
_fromMireds(atol(payload));
lightUpdate(true, true);
}
);
apiRegister(MQTT_TOPIC_MIRED, MQTT_TOPIC_MIRED,
[](char * buffer, size_t len) {},
[](const char * payload) {
_fromMireds(atol(payload));
lightUpdate(true, true);
}
);
}
}
for (unsigned int id=0; id<lightChannels(); id++) {
for (unsigned int id=0; id<lightChannels(); id++) {
char url[15];
sprintf_P(url, PSTR("%s/%d"), MQTT_TOPIC_CHANNEL, id);
char url[15];
sprintf_P(url, PSTR("%s/%d"), MQTT_TOPIC_CHANNEL, id);
char key[10];
sprintf_P(key, PSTR("%s%d"), MQTT_TOPIC_CHANNEL, id);
char key[10];
sprintf_P(key, PSTR("%s%d"), MQTT_TOPIC_CHANNEL, id);
apiRegister(url, key,
[id](char * buffer, size_t len) {
snprintf_P(buffer, len, PSTR("%d"), lightChannel(id));
},
[id](const char * payload) {
lightChannel(id, atoi(payload));
lightUpdate(true, true);
}
);
apiRegister(url, key,
[id](char * buffer, size_t len) {
snprintf_P(buffer, len, PSTR("%d"), lightChannel(id));
},
[id](const char * payload) {
lightChannel(id, atoi(payload));
lightUpdate(true, true);
}
);
}
}
#endif // WEB_SUPPORT
} }
void lightSetup() { void lightSetup() {


+ 6
- 2
code/espurna/ntp.ino View File

@ -51,10 +51,14 @@ void ntpSetup() {
} else if (error == invalidAddress) { } else if (error == invalidAddress) {
DEBUG_MSG_P(PSTR("[NTP] Error: Invalid NTP server address\n")); DEBUG_MSG_P(PSTR("[NTP] Error: Invalid NTP server address\n"));
} }
wsSend("{\"ntpStatus\": false}");
#if WEB_SUPPORT
wsSend("{\"ntpStatus\": false}");
#endif
} else { } else {
DEBUG_MSG_P(PSTR("[NTP] Time: %s\n"), (char *) ntpDateTime().c_str()); DEBUG_MSG_P(PSTR("[NTP] Time: %s\n"), (char *) ntpDateTime().c_str());
wsSend("{\"ntpStatus\": true}");
#if WEB_SUPPORT
wsSend("{\"ntpStatus\": true}");
#endif
} }
}); });
} }


+ 6
- 2
code/espurna/ota.ino View File

@ -24,13 +24,17 @@ void otaSetup() {
ArduinoOTA.onStart([]() { ArduinoOTA.onStart([]() {
DEBUG_MSG_P(PSTR("[OTA] Start\n")); DEBUG_MSG_P(PSTR("[OTA] Start\n"));
wsSend("{\"message\": \"OTA update started\"}");
#if WEB_SUPPORT
wsSend("{\"message\": \"OTA update started\"}");
#endif
}); });
ArduinoOTA.onEnd([]() { ArduinoOTA.onEnd([]() {
customReset(CUSTOM_RESET_OTA); customReset(CUSTOM_RESET_OTA);
DEBUG_MSG_P(PSTR("\n[OTA] End\n")); DEBUG_MSG_P(PSTR("\n[OTA] End\n"));
wsSend("{\"action\": \"reload\"}");
#if WEB_SUPPORT
wsSend("{\"action\": \"reload\"}");
#endif
delay(100); delay(100);
}); });


+ 17
- 5
code/espurna/relay.ino View File

@ -151,9 +151,11 @@ void relayPulseMode(unsigned int value, bool report) {
} }
*/ */
char message[20];
sprintf_P(message, PSTR("{\"relayPulseMode\": %d}"), value);
wsSend(message);
#if WEB_SUPPORT
char message[20];
sprintf_P(message, PSTR("{\"relayPulseMode\": %d}"), value);
wsSend(message);
#endif
} }
@ -314,6 +316,8 @@ unsigned char relayCount() {
// REST API // REST API
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#if WEB_SUPPORT
void relaySetupAPI() { void relaySetupAPI() {
// API entry points (protected with apikey) // API entry points (protected with apikey)
@ -343,10 +347,13 @@ void relaySetupAPI() {
} }
#endif // WEB_SUPPORT
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// WebSockets // WebSockets
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#if WEB_SUPPORT
void relayWS() { void relayWS() {
DynamicJsonBuffer jsonBuffer; DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject(); JsonObject& root = jsonBuffer.createObject();
@ -358,6 +365,7 @@ void relayWS() {
root.printTo(output); root.printTo(output);
wsSend(output.c_str()); wsSend(output.c_str());
} }
#endif
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// MQTT // MQTT
@ -480,7 +488,9 @@ void relaySetup() {
if (relayMode == RELAY_MODE_TOOGLE) relayRetrieve(true); if (relayMode == RELAY_MODE_TOOGLE) relayRetrieve(true);
relayLoop(); relayLoop();
relaySetupAPI();
#if WEB_SUPPORT
relaySetupAPI();
#endif
relaySetupMQTT(); relaySetupMQTT();
DEBUG_MSG_P(PSTR("[RELAY] Number of relays: %d\n"), _relays.size()); DEBUG_MSG_P(PSTR("[RELAY] Number of relays: %d\n"), _relays.size());
@ -517,7 +527,9 @@ void relayLoop(void) {
relayPulse(id); relayPulse(id);
relaySync(id); relaySync(id);
relaySave(); relaySave();
relayWS();
#if WEB_SUPPORT
relayWS();
#endif
} }
#if DOMOTICZ_SUPPORT #if DOMOTICZ_SUPPORT


+ 18
- 10
code/espurna/rfbridge.ino View File

@ -56,9 +56,11 @@ void _rfbLearn() {
Serial.flush(); Serial.flush();
Serial.println(); Serial.println();
char wsb[100];
sprintf_P(wsb, PSTR("{\"action\": \"rfbLearn\", \"data\":{\"id\": %d, \"status\": %d}}"), _learnId, _learnStatus ? 1 : 0);
wsSend(wsb);
#if WEB_SUPPORT
char buffer[100];
snprintf_P(wsb, strlen(buffer), PSTR("{\"action\": \"rfbLearn\", \"data\":{\"id\": %d, \"status\": %d}}"), _learnId, _learnStatus ? 1 : 0);
wsSend(buffer);
#endif
} }
@ -99,7 +101,9 @@ void _rfbDecode() {
if (action == RF_CODE_LEARN_KO) { if (action == RF_CODE_LEARN_KO) {
_rfbAck(); _rfbAck();
DEBUG_MSG_P(PSTR("[RFBRIDGE] Learn timeout\n")); DEBUG_MSG_P(PSTR("[RFBRIDGE] Learn timeout\n"));
wsSend("{\"action\": \"rfbTimeout\"}");
#if WEB_SUPPORT
wsSend("{\"action\": \"rfbTimeout\"}");
#endif
} }
if (action == RF_CODE_LEARN_OK || action == RF_CODE_RFIN) { if (action == RF_CODE_LEARN_OK || action == RF_CODE_RFIN) {
@ -114,9 +118,11 @@ void _rfbDecode() {
rfbStore(_learnId, _learnStatus, buffer); rfbStore(_learnId, _learnStatus, buffer);
// Websocket update // Websocket update
char wsb[100];
sprintf_P(wsb, PSTR("{\"rfb\":[{\"id\": %d, \"status\": %d, \"data\": \"%s\"}]}"), _learnId, _learnStatus ? 1 : 0, buffer);
wsSend(wsb);
#if WEB_SUPPORT
char wsb[100];
sprintf_P(wsb, strlen(wsb), PSTR("{\"rfb\":[{\"id\": %d, \"status\": %d, \"data\": \"%s\"}]}"), _learnId, _learnStatus ? 1 : 0, buffer);
wsSend(wsb);
#endif
} }
@ -286,9 +292,11 @@ void rfbForget(unsigned char id, bool status) {
delSetting(key); delSetting(key);
// Websocket update // Websocket update
char wsb[100];
sprintf_P(wsb, PSTR("{\"rfb\":[{\"id\": %d, \"status\": %d, \"data\": \"\"}]}"), id, status ? 1 : 0);
wsSend(wsb);
#if WEB_SUPPORT
char wsb[100];
snprintf_P(wsb, strlen(wsb), PSTR("{\"rfb\":[{\"id\": %d, \"status\": %d, \"data\": \"\"}]}"), id, status ? 1 : 0);
wsSend(wsb);
#endif
} }


+ 103
- 77
code/espurna/web.ino View File

@ -6,11 +6,11 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
*/ */
#if WEB_SUPPORT
#include <ESPAsyncTCP.h> #include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <ESP8266mDNS.h>
#include <FS.h> #include <FS.h>
#include <Hash.h>
#include <AsyncJson.h> #include <AsyncJson.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <Ticker.h> #include <Ticker.h>
@ -20,16 +20,23 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
#include "static/index.html.gz.h" #include "static/index.html.gz.h"
#endif #endif
// -----------------------------------------------------------------------------
AsyncWebServer * _server; AsyncWebServer * _server;
AsyncWebSocket ws("/ws");
Ticker deferred;
char _last_modified[50];
Ticker _web_defer;
// -----------------------------------------------------------------------------
AsyncWebSocket _ws("/ws");
typedef struct { typedef struct {
IPAddress ip; IPAddress ip;
unsigned long timestamp = 0; unsigned long timestamp = 0;
} ws_ticket_t; } ws_ticket_t;
ws_ticket_t _ticket[WS_BUFFER_SIZE]; ws_ticket_t _ticket[WS_BUFFER_SIZE];
// -----------------------------------------------------------------------------
typedef struct { typedef struct {
char * url; char * url;
char * key; char * key;
@ -37,27 +44,12 @@ typedef struct {
apiPutCallbackFunction putFn = NULL; apiPutCallbackFunction putFn = NULL;
} web_api_t; } web_api_t;
std::vector<web_api_t> _apis; std::vector<web_api_t> _apis;
char _last_modified[50];
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// WEBSOCKETS // WEBSOCKETS
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
bool wsConnected() {
return (ws.count() > 0);
}
bool wsSend(const char * payload) {
if (ws.count() > 0) {
ws.textAll(payload);
}
}
bool wsSend(uint32_t client_id, const char * payload) {
ws.text(client_id, payload);
}
void wsMQTTCallback(unsigned int type, const char * topic, const char * payload) {
void _wsMQTTCallback(unsigned int type, const char * topic, const char * payload) {
if (type == MQTT_CONNECT_EVENT) { if (type == MQTT_CONNECT_EVENT) {
wsSend("{\"mqttStatus\": true}"); wsSend("{\"mqttStatus\": true}");
@ -76,7 +68,7 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
JsonObject& root = jsonBuffer.parseObject((char *) payload); JsonObject& root = jsonBuffer.parseObject((char *) payload);
if (!root.success()) { if (!root.success()) {
DEBUG_MSG_P(PSTR("[WEBSOCKET] Error parsing data\n")); DEBUG_MSG_P(PSTR("[WEBSOCKET] Error parsing data\n"));
ws.text(client_id, "{\"message\": \"Error parsing data!\"}");
wsSend(client_id, "{\"message\": \"Error parsing data!\"}");
return; return;
} }
@ -111,7 +103,7 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
JsonObject& data = root["data"]; JsonObject& data = root["data"];
if (!data.containsKey("app") || (data["app"] != APP_NAME)) { if (!data.containsKey("app") || (data["app"] != APP_NAME)) {
ws.text(client_id, "{\"message\": \"The file does not look like a valid configuration backup.\"}");
wsSend(client_id, "{\"message\": \"The file does not look like a valid configuration backup.\"}");
return; return;
} }
@ -127,14 +119,14 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
saveSettings(); saveSettings();
ws.text(client_id, "{\"message\": \"Changes saved. You should reboot your board now.\"}");
wsSend(client_id, "{\"message\": \"Changes saved. You should reboot your board now.\"}");
} }
if (action.equals("reconnect")) { if (action.equals("reconnect")) {
// Let the HTTP request return and disconnect after 100ms // Let the HTTP request return and disconnect after 100ms
deferred.once_ms(100, wifiDisconnect);
_web_defer.once_ms(100, wifiDisconnect);
} }
@ -272,11 +264,11 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
} }
if (key == "adminPass2") { if (key == "adminPass2") {
if (!value.equals(adminPass)) { if (!value.equals(adminPass)) {
ws.text(client_id, "{\"message\": \"Passwords do not match!\"}");
wsSend(client_id, "{\"message\": \"Passwords do not match!\"}");
return; return;
} }
if (value.length() == 0) continue; if (value.length() == 0) continue;
ws.text(client_id, "{\"action\": \"reload\"}");
wsSend(client_id, "{\"action\": \"reload\"}");
key = String("adminPass"); key = String("adminPass");
} }
@ -379,9 +371,9 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
} }
if (changed) { if (changed) {
ws.text(client_id, "{\"message\": \"Changes saved\"}");
wsSend(client_id, "{\"message\": \"Changes saved\"}");
} else { } else {
ws.text(client_id, "{\"message\": \"No changes detected\"}");
wsSend(client_id, "{\"message\": \"No changes detected\"}");
} }
} }
@ -397,7 +389,7 @@ void _wsStart(uint32_t client_id) {
JsonObject& root = jsonBuffer.createObject(); JsonObject& root = jsonBuffer.createObject();
bool changePassword = false; bool changePassword = false;
#if WEB_PASS_CHANGE == 1
#if WEB_FORCE_PASS_CHANGE
String adminPass = getSetting("adminPass", ADMIN_PASS); String adminPass = getSetting("adminPass", ADMIN_PASS);
if (adminPass.equals(ADMIN_PASS)) changePassword = true; if (adminPass.equals(ADMIN_PASS)) changePassword = true;
#endif #endif
@ -600,7 +592,7 @@ void _wsStart(uint32_t client_id) {
String output; String output;
root.printTo(output); root.printTo(output);
ws.text(client_id, (char *) output.c_str());
wsSend(client_id, (char *) output.c_str());
} }
@ -616,7 +608,7 @@ bool _wsAuth(AsyncWebSocketClient * client) {
if (index == WS_BUFFER_SIZE) { if (index == WS_BUFFER_SIZE) {
DEBUG_MSG_P(PSTR("[WEBSOCKET] Validation check failed\n")); DEBUG_MSG_P(PSTR("[WEBSOCKET] Validation check failed\n"));
ws.text(client->id(), "{\"message\": \"Session expired, please reload page...\"}");
wsSend(client->id(), "{\"message\": \"Session expired, please reload page...\"}");
return false; return false;
} }
@ -665,21 +657,32 @@ void _wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTy
} }
// -----------------------------------------------------------------------------
// WEBSERVER
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void webLogRequest(AsyncWebServerRequest *request) {
DEBUG_MSG_P(PSTR("[WEBSERVER] Request: %s %s\n"), request->methodToString(), request->url().c_str());
bool wsConnected() {
return (_ws.count() > 0);
} }
bool _authenticate(AsyncWebServerRequest *request) {
String password = getSetting("adminPass", ADMIN_PASS);
char httpPassword[password.length() + 1];
password.toCharArray(httpPassword, password.length() + 1);
return request->authenticate(WEB_USERNAME, httpPassword);
bool wsSend(const char * payload) {
if (_ws.count() > 0) {
_ws.textAll(payload);
}
}
bool wsSend(uint32_t client_id, const char * payload) {
_ws.text(client_id, payload);
}
void wsSetup() {
_ws.onEvent(_wsEvent);
mqttRegister(_wsMQTTCallback);
_server->addHandler(&_ws);
} }
// -----------------------------------------------------------------------------
// API
// -----------------------------------------------------------------------------
bool _authAPI(AsyncWebServerRequest *request) { bool _authAPI(AsyncWebServerRequest *request) {
if (getSetting("apiEnabled", API_ENABLED).toInt() == 0) { if (getSetting("apiEnabled", API_ENABLED).toInt() == 0) {
@ -718,7 +721,7 @@ ArRequestHandlerFunction _bindAPI(unsigned int apiID) {
return [apiID](AsyncWebServerRequest *request) { return [apiID](AsyncWebServerRequest *request) {
webLogRequest(request);
_webLog(request);
if (!_authAPI(request)) return; if (!_authAPI(request)) return;
web_api_t api = _apis[apiID]; web_api_t api = _apis[apiID];
@ -757,28 +760,9 @@ ArRequestHandlerFunction _bindAPI(unsigned int apiID) {
} }
void apiRegister(const char * url, const char * key, apiGetCallbackFunction getFn, apiPutCallbackFunction putFn) {
// Store it
web_api_t api;
char buffer[40];
snprintf_P(buffer, strlen(buffer), PSTR("/api/%s"), url);
api.url = strdup(buffer);
api.key = strdup(key);
api.getFn = getFn;
api.putFn = putFn;
_apis.push_back(api);
// Bind call
unsigned int methods = HTTP_GET;
if (putFn != NULL) methods += HTTP_PUT;
_server->on(buffer, methods, _bindAPI(_apis.size() - 1));
}
void _onAPIs(AsyncWebServerRequest *request) { void _onAPIs(AsyncWebServerRequest *request) {
webLogRequest(request);
_webLog(request);
if (!_authAPI(request)) return; if (!_authAPI(request)) return;
@ -805,7 +789,7 @@ void _onAPIs(AsyncWebServerRequest *request) {
void _onRPC(AsyncWebServerRequest *request) { void _onRPC(AsyncWebServerRequest *request) {
webLogRequest(request);
_webLog(request);
if (!_authAPI(request)) return; if (!_authAPI(request)) return;
@ -820,7 +804,7 @@ void _onRPC(AsyncWebServerRequest *request) {
if (action.equals("reset")) { if (action.equals("reset")) {
response = 200; response = 200;
deferred.once_ms(100, []() {
_web_defer.once_ms(100, []() {
customReset(CUSTOM_RESET_RPC); customReset(CUSTOM_RESET_RPC);
ESP.restart(); ESP.restart();
}); });
@ -832,9 +816,50 @@ void _onRPC(AsyncWebServerRequest *request) {
} }
// -----------------------------------------------------------------------------
void apiRegister(const char * url, const char * key, apiGetCallbackFunction getFn, apiPutCallbackFunction putFn) {
// Store it
web_api_t api;
char buffer[40];
snprintf_P(buffer, strlen(buffer), PSTR("/api/%s"), url);
api.url = strdup(buffer);
api.key = strdup(key);
api.getFn = getFn;
api.putFn = putFn;
_apis.push_back(api);
// Bind call
unsigned int methods = HTTP_GET;
if (putFn != NULL) methods += HTTP_PUT;
_server->on(buffer, methods, _bindAPI(_apis.size() - 1));
}
void apiSetup() {
_server->on("/apis", HTTP_GET, _onAPIs);
_server->on("/rpc", HTTP_GET, _onRPC);
}
// -----------------------------------------------------------------------------
// WEBSERVER
// -----------------------------------------------------------------------------
void _webLog(AsyncWebServerRequest *request) {
DEBUG_MSG_P(PSTR("[WEBSERVER] Request: %s %s\n"), request->methodToString(), request->url().c_str());
}
bool _authenticate(AsyncWebServerRequest *request) {
String password = getSetting("adminPass", ADMIN_PASS);
char httpPassword[password.length() + 1];
password.toCharArray(httpPassword, password.length() + 1);
return request->authenticate(WEB_USERNAME, httpPassword);
}
void _onAuth(AsyncWebServerRequest *request) { void _onAuth(AsyncWebServerRequest *request) {
webLogRequest(request);
_webLog(request);
if (!_authenticate(request)) return request->requestAuthentication(); if (!_authenticate(request)) return request->requestAuthentication();
IPAddress ip = request->client()->remoteIP(); IPAddress ip = request->client()->remoteIP();
@ -857,7 +882,7 @@ void _onAuth(AsyncWebServerRequest *request) {
void _onGetConfig(AsyncWebServerRequest *request) { void _onGetConfig(AsyncWebServerRequest *request) {
webLogRequest(request);
_webLog(request);
if (!_authenticate(request)) return request->requestAuthentication(); if (!_authenticate(request)) return request->requestAuthentication();
AsyncJsonResponse * response = new AsyncJsonResponse(); AsyncJsonResponse * response = new AsyncJsonResponse();
@ -884,7 +909,7 @@ void _onGetConfig(AsyncWebServerRequest *request) {
#if WEB_EMBEDDED #if WEB_EMBEDDED
void _onHome(AsyncWebServerRequest *request) { void _onHome(AsyncWebServerRequest *request) {
webLogRequest(request);
_webLog(request);
if (request->header("If-Modified-Since").equals(_last_modified)) { if (request->header("If-Modified-Since").equals(_last_modified)) {
@ -906,7 +931,7 @@ void _onUpgrade(AsyncWebServerRequest *request) {
AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", Update.hasError() ? "FAIL" : "OK"); AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", Update.hasError() ? "FAIL" : "OK");
response->addHeader("Connection", "close"); response->addHeader("Connection", "close");
if (!Update.hasError()) { if (!Update.hasError()) {
deferred.once_ms(100, []() {
_web_defer.once_ms(100, []() {
customReset(CUSTOM_RESET_UPGRADE); customReset(CUSTOM_RESET_UPGRADE);
ESP.restart(); ESP.restart();
}); });
@ -944,20 +969,21 @@ void _onUpgradeData(AsyncWebServerRequest *request, String filename, size_t inde
} }
} }
// -----------------------------------------------------------------------------
void webSetup() { void webSetup() {
// Cache the Last-Modifier header value
sprintf_P(_last_modified, PSTR("%s %s GMT"), __DATE__, __TIME__);
// Create server // Create server
_server = new AsyncWebServer(getSetting("webPort", WEB_PORT).toInt()); _server = new AsyncWebServer(getSetting("webPort", WEB_PORT).toInt());
// Setup websocket // Setup websocket
ws.onEvent(_wsEvent);
mqttRegister(wsMQTTCallback);
wsSetup();
// Cache the Last-Modifier header value
sprintf_P(_last_modified, PSTR("%s %s GMT"), __DATE__, __TIME__);
// Setup webserver
_server->addHandler(&ws);
// API setup
apiSetup();
// Rewrites // Rewrites
_server->rewrite("/", "/index.html"); _server->rewrite("/", "/index.html");
@ -968,8 +994,6 @@ void webSetup() {
#endif #endif
_server->on("/config", HTTP_GET, _onGetConfig); _server->on("/config", HTTP_GET, _onGetConfig);
_server->on("/auth", HTTP_GET, _onAuth); _server->on("/auth", HTTP_GET, _onAuth);
_server->on("/apis", HTTP_GET, _onAPIs);
_server->on("/rpc", HTTP_GET, _onRPC);
_server->on("/upgrade", HTTP_POST, _onUpgrade, _onUpgradeData); _server->on("/upgrade", HTTP_POST, _onUpgrade, _onUpgradeData);
// Serve static files // Serve static files
@ -977,7 +1001,7 @@ void webSetup() {
_server->serveStatic("/", SPIFFS, "/") _server->serveStatic("/", SPIFFS, "/")
.setLastModified(_last_modified) .setLastModified(_last_modified)
.setFilter([](AsyncWebServerRequest *request) -> bool { .setFilter([](AsyncWebServerRequest *request) -> bool {
webLogRequest(request);
_webLog(request);
return true; return true;
}); });
#endif #endif
@ -992,3 +1016,5 @@ void webSetup() {
DEBUG_MSG_P(PSTR("[WEBSERVER] Webserver running on port %d\n"), getSetting("webPort", WEB_PORT).toInt()); DEBUG_MSG_P(PSTR("[WEBSERVER] Webserver running on port %d\n"), getSetting("webPort", WEB_PORT).toInt());
} }
#endif // WEB_SUPPORT

+ 45
- 0
code/espurna/wifi.ino View File

@ -7,6 +7,7 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
*/ */
#include "JustWifi.h" #include "JustWifi.h"
#include <ESP8266mDNS.h>
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// WIFI // WIFI
@ -115,8 +116,52 @@ void wifiStatus() {
} }
// Inject hardcoded networks
void wifiInject() {
#ifdef WIFI1_SSID
if (getSetting("ssid", 0, "").length() == 0) setSetting("ssid", 0, WIFI1_SSID);
#endif
#ifdef WIFI1_PASS
if (getSetting("pass", 0, "").length() == 0) setSetting("pass", 0, WIFI1_PASS);
#endif
#ifdef WIFI1_IP
if (getSetting("ip", 0, "").length() == 0) setSetting("ip", 0, WIFI1_IP);
#endif
#ifdef WIFI1_GW
if (getSetting("gw", 0, "").length() == 0) setSetting("gw", 0, WIFI1_GW);
#endif
#ifdef WIFI1_MASK
if (getSetting("mask", 0, "").length() == 0) setSetting("mask", 0, WIFI1_MASK);
#endif
#ifdef WIFI1_DNS
if (getSetting("dns", 0, "").length() == 0) setSetting("dns", 0, WIFI1_DNS);
#endif
#ifdef WIFI2_SSID
if (getSetting("ssid", 1, "").length() == 0) setSetting("ssid", 1, WIFI2_SSID);
#endif
#ifdef WIFI2_PASS
if (getSetting("pass", 1, "").length() == 0) setSetting("pass", 1, WIFI2_PASS);
#endif
#ifdef WIFI2_IP
if (getSetting("ip", 1, "").length() == 0) setSetting("ip", 1, WIFI2_IP);
#endif
#ifdef WIFI2_GW
if (getSetting("gw", 1, "").length() == 0) setSetting("gw", 1, WIFI2_GW);
#endif
#ifdef WIFI2_MASK
if (getSetting("mask", 1, "").length() == 0) setSetting("mask", 1, WIFI2_MASK);
#endif
#ifdef WIFI2_DNS
if (getSetting("dns", 1, "").length() == 0) setSetting("dns", 1, WIFI2_DNS);
#endif
}
void wifiSetup() { void wifiSetup() {
wifiInject();
wifiConfigure(); wifiConfigure();
// Message callbacks // Message callbacks


Loading…
Cancel
Save