mirror of
https://github.com/HASwitchPlate/openHASP.git
synced 2025-04-19 12:57:19 +00:00
Add source files
This commit is contained in:
parent
1100360eb3
commit
025ac7b35f
1371
src/hasp.cpp
Normal file
1371
src/hasp.cpp
Normal file
File diff suppressed because it is too large
Load Diff
145
src/hasp_config.cpp
Normal file
145
src/hasp_config.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
#include "Arduino.h"
|
||||
#include "ArduinoJson.h"
|
||||
|
||||
#ifdef ESP32
|
||||
#include "SPIFFS.h"
|
||||
#endif
|
||||
#include <FS.h> // Include the SPIFFS library
|
||||
|
||||
#include "hasp_config.h"
|
||||
#include "hasp_log.h"
|
||||
#include "hasp_debug.h"
|
||||
#include "hasp_http.h"
|
||||
#include "hasp_mqtt.h"
|
||||
#include "hasp_wifi.h"
|
||||
#include "hasp_mdns.h"
|
||||
#include "hasp_gui.h"
|
||||
#include "hasp_tft.h"
|
||||
#include "hasp_ota.h"
|
||||
#include "hasp.h"
|
||||
|
||||
#define HASP_CONFIG_FILE F("/config.json")
|
||||
|
||||
void spiffsList()
|
||||
{
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
debugPrintln(F("FILE: Listing files on the internal flash:"));
|
||||
File root = SPIFFS.open("/");
|
||||
File file = root.openNextFile();
|
||||
while(file) {
|
||||
char msg[64];
|
||||
sprintf_P(msg, PSTR("FILE: * %s (%u bytes)"), file.name(), (uint32_t)file.size());
|
||||
debugPrintln(msg);
|
||||
file = root.openNextFile();
|
||||
}
|
||||
#endif
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
debugPrintln(F("FILE: Listing files on the internal flash:"));
|
||||
Dir dir = SPIFFS.openDir("/");
|
||||
while(dir.next()) {
|
||||
char msg[64];
|
||||
sprintf_P(msg, PSTR("FILE: * %s (%u bytes)"), dir.fileName().c_str(), (uint32_t)dir.fileSize());
|
||||
debugPrintln(msg);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool configChanged()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void configLoop()
|
||||
{
|
||||
if(configChanged()) {
|
||||
// configSetConfig();
|
||||
}
|
||||
}
|
||||
|
||||
void configGetConfig(JsonDocument & settings, bool setupdebug = false)
|
||||
{
|
||||
File file = SPIFFS.open(HASP_CONFIG_FILE, "r");
|
||||
|
||||
if(file) {
|
||||
size_t size = file.size();
|
||||
if(size > 1024) {
|
||||
errorPrintln(F("CONF: %sConfig file size is too large"));
|
||||
return;
|
||||
}
|
||||
|
||||
DeserializationError error = deserializeJson(settings, file);
|
||||
if(!error) {
|
||||
file.close();
|
||||
|
||||
if(setupdebug) {
|
||||
debugSetup(); // Debug started, now we can use it; HASP header sent
|
||||
debugPrintln(F("FILE: [SUCCESS] SPI flash FS mounted"));
|
||||
spiffsList();
|
||||
}
|
||||
debugPrintln(String(F("CONF: Loading ")) + String(HASP_CONFIG_FILE));
|
||||
|
||||
// show settings in log
|
||||
String output;
|
||||
serializeJson(settings, output);
|
||||
debugPrintln(String(F("CONF: ")) + output);
|
||||
|
||||
debugPrintln(String(F("CONF: [SUCCESS] Loaded ")) + String(HASP_CONFIG_FILE));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(setupdebug) {
|
||||
// setup debugging defaults
|
||||
debugSetup(); // Debug started, now we can use it; HASP header sent
|
||||
debugPrintln(F("FILE: [SUCCESS] SPI flash FS mounted"));
|
||||
spiffsList();
|
||||
}
|
||||
debugPrintln(String(F("CONF: Loading ")) + String(HASP_CONFIG_FILE));
|
||||
errorPrintln(String(F("CONF: %sFailed to load ")) + String(HASP_CONFIG_FILE));
|
||||
}
|
||||
|
||||
void configWriteConfig()
|
||||
{
|
||||
/* Read Config File */
|
||||
DynamicJsonDocument settings(1024);
|
||||
debugPrintln(String(F("CONF: Config LOADING first")) + String(HASP_CONFIG_FILE));
|
||||
configGetConfig(settings, false);
|
||||
debugPrintln(String(F("CONF: Config LOADED first")) + String(HASP_CONFIG_FILE));
|
||||
|
||||
bool changed = true;
|
||||
// changed |= debugGetConfig(settings[F("debug")].to<JsonObject>());
|
||||
// changed |= guiGetConfig(settings[F("gui")].to<JsonObject>());
|
||||
changed |= haspGetConfig(settings[F("hasp")].to<JsonObject>());
|
||||
// changed |= httpGetConfig(settings[F("http")].to<JsonObject>());
|
||||
// changed |= mdnsGetConfig(settings[F("mdns")].to<JsonObject>());
|
||||
// changed |= mqttGetConfig(settings[F("mqtt")].to<JsonObject>());
|
||||
// changed |= otaGetConfig(settings[F("ota")].to<JsonObject>());
|
||||
// changed |= tftGetConfig(settings[F("tft")].to<JsonObject>());
|
||||
// changed |= wifiGetConfig(settings[F("wifi")].to<JsonObject>());
|
||||
|
||||
if(changed) {
|
||||
File file = SPIFFS.open(HASP_CONFIG_FILE, "w");
|
||||
if(file) {
|
||||
debugPrintln(F("CONF: Writing /config.json"));
|
||||
size_t size = serializeJson(settings, file);
|
||||
file.close();
|
||||
if(size > 0) {
|
||||
debugPrintln(F("CONF: [SUCCESS] /config.json saved"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
errorPrintln(F("CONF: %sFailed to write /config.json"));
|
||||
} else {
|
||||
debugPrintln(F("CONF: Configuration was not changed"));
|
||||
}
|
||||
}
|
||||
|
||||
void configSetup(JsonDocument & settings)
|
||||
{
|
||||
if(!SPIFFS.begin()) {
|
||||
errorPrintln(F("FILE: %sSPI flash init failed. Unable to mount FS."));
|
||||
} else {
|
||||
configGetConfig(settings, true);
|
||||
}
|
||||
}
|
74
src/hasp_debug.cpp
Normal file
74
src/hasp_debug.cpp
Normal file
@ -0,0 +1,74 @@
|
||||
#include <Arduino.h>
|
||||
#include "ArduinoJson.h"
|
||||
|
||||
#ifdef ESP8266
|
||||
#include <SoftwareSerial.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#else
|
||||
#include <Wifi.h>
|
||||
#endif
|
||||
#include <WiFiUdp.h>
|
||||
#include <Syslog.h>
|
||||
|
||||
#include "hasp_debug.h"
|
||||
#include "hasp_config.h"
|
||||
|
||||
#include "user_config_override.h"
|
||||
|
||||
#ifndef SYSLOG_SERVER
|
||||
#define SYSLOG_SERVER ""
|
||||
#endif
|
||||
#ifndef SYSLOG_PORT
|
||||
#define SYSLOG_PORT 514
|
||||
#endif
|
||||
#ifndef APP_NAME
|
||||
#define APP_NAME "HASP"
|
||||
#endif
|
||||
|
||||
std::string debugAppName = APP_NAME;
|
||||
std::string debugSyslogHost = SYSLOG_SERVER;
|
||||
uint16_t debugSyslogPort = SYSLOG_PORT;
|
||||
|
||||
// A UDP instance to let us send and receive packets over UDP
|
||||
WiFiUDP syslogClient;
|
||||
|
||||
// Create a new syslog instance with LOG_KERN facility
|
||||
// Syslog syslog(syslogClient, SYSLOG_SERVER, SYSLOG_PORT, MQTT_CLIENT, APP_NAME, LOG_KERN);
|
||||
// Create a new empty syslog instance
|
||||
Syslog syslog(syslogClient, debugSyslogHost.c_str(), debugSyslogPort, debugAppName.c_str(), debugAppName.c_str(),
|
||||
LOG_LOCAL0);
|
||||
|
||||
void debugSetup()
|
||||
{
|
||||
Serial.begin(115200); /* prepare for possible serial debug */
|
||||
Serial.flush();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println(F("\n _____ _____ _____ _____\n | | | _ | __| _ |\n"
|
||||
" | | |__ | __|\n |__|__|__|__|_____|__|\n"
|
||||
" Home Automation Switch Plate\n Open Hardware edition\n\n"));
|
||||
Serial.flush();
|
||||
|
||||
// prepare syslog configuration here (can be anywhere before first call of
|
||||
// log/logf method)
|
||||
|
||||
syslog.server(debugSyslogHost.c_str(), debugSyslogPort);
|
||||
syslog.deviceHostname(debugAppName.c_str());
|
||||
syslog.appName(debugAppName.c_str());
|
||||
syslog.defaultPriority(LOG_LOCAL0);
|
||||
}
|
||||
|
||||
void debugLoop()
|
||||
{}
|
||||
|
||||
void serialPrintln(String debugText)
|
||||
{
|
||||
String debugTimeText =
|
||||
"[+" + String(float(millis()) / 1000, 3) + "s] " + String(ESP.getFreeHeap()) + " " + debugText;
|
||||
Serial.println(debugTimeText);
|
||||
}
|
||||
|
||||
void debugStop()
|
||||
{
|
||||
Serial.flush();
|
||||
}
|
50
src/hasp_eeprom.cpp
Normal file
50
src/hasp_eeprom.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#include <EEPROM.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "hasp_debug.h"
|
||||
|
||||
void eepromWrite(char addr, std::string & data);
|
||||
std::string eepromRead(char addr);
|
||||
|
||||
void eepromSetup()
|
||||
{
|
||||
EEPROM.begin(1024);
|
||||
// debugPrintln("EEPROM: Started Eeprom");
|
||||
}
|
||||
|
||||
void eepromLoop()
|
||||
{}
|
||||
|
||||
void eepromUpdate(uint16_t addr, char ch)
|
||||
{
|
||||
if(EEPROM.read(addr) != ch) {
|
||||
EEPROM.write(addr, ch);
|
||||
}
|
||||
}
|
||||
|
||||
void eepromWrite(uint16_t addr, std::string & data)
|
||||
{
|
||||
int count = data.length();
|
||||
for(int i = 0; i < count; i++) {
|
||||
eepromUpdate(addr + i, data[i]);
|
||||
}
|
||||
eepromUpdate(addr + count, '\0');
|
||||
EEPROM.commit();
|
||||
}
|
||||
|
||||
std::string eepromRead(uint16_t addr)
|
||||
{
|
||||
int i;
|
||||
char data[1024]; // Max 1024 Bytes
|
||||
int len = 0;
|
||||
unsigned char k;
|
||||
k = EEPROM.read(addr);
|
||||
while(k != '\0' && len < 1023) // Read until null character
|
||||
{
|
||||
k = EEPROM.read(addr + len);
|
||||
if((uint8_t(k) < 32) || (uint8_t(k) > 127)) break; // check for printable ascii, includes '\0'
|
||||
data[len] = k;
|
||||
len++;
|
||||
}
|
||||
return std::string(data);
|
||||
}
|
233
src/hasp_gui.cpp
Normal file
233
src/hasp_gui.cpp
Normal file
@ -0,0 +1,233 @@
|
||||
#include <Ticker.h>
|
||||
|
||||
#include "lvgl.h"
|
||||
#include "lv_conf.h"
|
||||
|
||||
#include "TFT_eSPI.h"
|
||||
|
||||
#ifdef ESP32
|
||||
#include "png_decoder.h"
|
||||
#endif
|
||||
#include "lv_zifont.h"
|
||||
|
||||
#include "hasp_log.h"
|
||||
#include "hasp_debug.h"
|
||||
#include "hasp_config.h"
|
||||
#include "hasp_gui.h"
|
||||
|
||||
#define LVGL_TICK_PERIOD 30 // 30
|
||||
|
||||
uint16_t guiSleepTime = 150; // 0.1 second resolution
|
||||
bool guiSleeping = false;
|
||||
uint8_t guiTickPeriod = 50;
|
||||
Ticker tick; /* timer for interrupt handler */
|
||||
TFT_eSPI tft = TFT_eSPI(); /* TFT instance */
|
||||
|
||||
bool IRAM_ATTR guiCheckSleep()
|
||||
{
|
||||
bool shouldSleep = lv_disp_get_inactive_time(NULL) > guiSleepTime * 100;
|
||||
if(shouldSleep && !guiSleeping) {
|
||||
debugPrintln(F("GUI: Going to sleep now..."));
|
||||
guiSleeping = true;
|
||||
}
|
||||
return shouldSleep;
|
||||
}
|
||||
|
||||
#if LV_USE_LOG != 0
|
||||
/* Serial debugging */
|
||||
void debugLvgl(lv_log_level_t level, const char * file, uint32_t line, const char * dsc)
|
||||
{
|
||||
char msg[128];
|
||||
sprintf(msg, PSTR("LVGL: %s@%d->%s"), file, line, dsc);
|
||||
debugPrintln(msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Display flushing */
|
||||
void tft_espi_flush(lv_disp_drv_t * disp, const lv_area_t * area, lv_color_t * color_p)
|
||||
{
|
||||
uint16_t c;
|
||||
|
||||
tft.startWrite(); /* Start new TFT transaction */
|
||||
tft.setAddrWindow(area->x1, area->y1, (area->x2 - area->x1 + 1),
|
||||
(area->y2 - area->y1 + 1)); /* set the working window */
|
||||
for(int y = area->y1; y <= area->y2; y++) {
|
||||
for(int x = area->x1; x <= area->x2; x++) {
|
||||
c = color_p->full;
|
||||
tft.writeColor(c, 1);
|
||||
color_p++;
|
||||
}
|
||||
}
|
||||
tft.endWrite(); /* terminate TFT transaction */
|
||||
lv_disp_flush_ready(disp); /* tell lvgl that flushing is done */
|
||||
}
|
||||
|
||||
/* Interrupt driven periodic handler */
|
||||
static void IRAM_ATTR lv_tick_handler(void)
|
||||
{
|
||||
lv_tick_inc(guiTickPeriod);
|
||||
}
|
||||
|
||||
/* Reading input device (simulated encoder here) */
|
||||
bool read_encoder(lv_indev_drv_t * indev, lv_indev_data_t * data)
|
||||
{
|
||||
static int32_t last_diff = 0;
|
||||
int32_t diff = 0; /* Dummy - no movement */
|
||||
int btn_state = LV_INDEV_STATE_REL; /* Dummy - no press */
|
||||
|
||||
data->enc_diff = diff - last_diff;
|
||||
data->state = btn_state;
|
||||
last_diff = diff;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool my_touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data)
|
||||
{
|
||||
uint16_t touchX, touchY;
|
||||
|
||||
bool touched = tft.getTouch(&touchX, &touchY, 600);
|
||||
if(!touched) return false;
|
||||
|
||||
bool shouldSleep = guiCheckSleep();
|
||||
if(!shouldSleep && guiSleeping) {
|
||||
debugPrintln(F("GUI: Waking up!"));
|
||||
guiSleeping = false;
|
||||
}
|
||||
|
||||
// Ignore first press?
|
||||
|
||||
if(touchX > tft.width() || touchY > tft.height()) {
|
||||
Serial.print(F("Y or y outside of expected parameters.. x: "));
|
||||
Serial.print(touchX);
|
||||
Serial.print(F(" / y: "));
|
||||
Serial.println(touchY);
|
||||
} else {
|
||||
/*Save the state and save the pressed coordinate*/
|
||||
data->state = touched ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
|
||||
data->point.x = touchX;
|
||||
data->point.y = touchY;
|
||||
/*
|
||||
Serial.print("Data x");
|
||||
Serial.println(touchX);
|
||||
|
||||
Serial.print("Data y");
|
||||
Serial.println(touchY);*/
|
||||
}
|
||||
|
||||
return false; /*Return `false` because we are not buffering and no more data to read*/
|
||||
}
|
||||
|
||||
void guiSetup(TFT_eSPI & screen, JsonObject settings)
|
||||
{
|
||||
size_t buffer_size;
|
||||
tft = screen;
|
||||
lv_init();
|
||||
|
||||
#if ESP32
|
||||
/* allocate on iram (or psram ?) */
|
||||
buffer_size = 1024 * 8;
|
||||
static lv_color_t * guiVdbBuffer = (lv_color_t *)malloc(sizeof(lv_color_t) * buffer_size);
|
||||
static lv_disp_buf_t disp_buf;
|
||||
lv_disp_buf_init(&disp_buf, guiVdbBuffer, NULL, buffer_size);
|
||||
#else
|
||||
/* allocate on heap */
|
||||
static lv_color_t guiVdbBuffer[1024 * 4];
|
||||
buffer_size = sizeof(guiVdbBuffer) / sizeof(guiVdbBuffer[0]);
|
||||
static lv_disp_buf_t disp_buf;
|
||||
lv_disp_buf_init(&disp_buf, guiVdbBuffer, NULL, buffer_size);
|
||||
#endif
|
||||
debugPrintln(String(F("LVGL: VDB size : ")) + String(buffer_size));
|
||||
|
||||
#if LV_USE_LOG != 0
|
||||
debugPrintln(F("LVGL: Registering lvgl logging handler"));
|
||||
lv_log_register_print_cb(debugLvgl); /* register print function for debugging */
|
||||
#endif
|
||||
|
||||
/* Initialize PNG decoder */
|
||||
// png_decoder_init();
|
||||
|
||||
/* Initialize the display driver */
|
||||
lv_disp_drv_t disp_drv;
|
||||
lv_disp_drv_init(&disp_drv);
|
||||
disp_drv.flush_cb = tft_espi_flush;
|
||||
disp_drv.buffer = &disp_buf;
|
||||
#if(TFT_ROTATION == 0 || TFT_ROTATION == 2 || TFT_ROTATION == 4 || TFT_ROTATION == 6)
|
||||
/* 1/3=Landscape or 0/2=Portrait orientation */
|
||||
// Normal width & height
|
||||
disp_drv.hor_res = TFT_WIDTH; // From User_Setup.h
|
||||
disp_drv.ver_res = TFT_HEIGHT; // From User_Setup.h
|
||||
#else
|
||||
// Swapped width & height
|
||||
disp_drv.hor_res = TFT_HEIGHT; // From User_Setup.h
|
||||
disp_drv.ver_res = TFT_WIDTH; // From User_Setup.h
|
||||
#endif
|
||||
lv_disp_drv_register(&disp_drv);
|
||||
|
||||
/*Initialize the touch pad*/
|
||||
lv_indev_drv_t indev_drv;
|
||||
lv_indev_drv_init(&indev_drv);
|
||||
// indev_drv.type = LV_INDEV_TYPE_ENCODER;
|
||||
// indev_drv.read_cb = read_encoder;
|
||||
indev_drv.type = LV_INDEV_TYPE_POINTER;
|
||||
indev_drv.read_cb = my_touchpad_read;
|
||||
lv_indev_t * mouse_indev = lv_indev_drv_register(&indev_drv);
|
||||
|
||||
lv_obj_t * label = lv_label_create(lv_layer_sys(), NULL);
|
||||
lv_label_set_text(label, "<");
|
||||
lv_indev_set_cursor(mouse_indev, label); // connect the object to the driver
|
||||
|
||||
/*
|
||||
lv_obj_t * cursor = lv_obj_create(lv_layer_sys(), NULL); // show on every page
|
||||
lv_obj_set_size(cursor, 9, 9);
|
||||
static lv_style_t style_cursor;
|
||||
lv_style_copy(&style_cursor, &lv_style_pretty);
|
||||
style_cursor.body.radius = LV_RADIUS_CIRCLE;
|
||||
style_cursor.body.main_color = LV_COLOR_RED;
|
||||
style_cursor.body.opa = LV_OPA_COVER;
|
||||
lv_obj_set_style(cursor, &style_cursor);
|
||||
// lv_obj_set_click(cursor, false);
|
||||
lv_indev_set_cursor(mouse_indev, cursor); // connect the object to the driver
|
||||
*/
|
||||
/* Initialize mouse pointer */
|
||||
/*// if(true) {
|
||||
debugPrintln(PSTR("LVGL: Initialize Cursor"));
|
||||
lv_obj_t * cursor;
|
||||
lv_obj_t * mouse_layer = lv_disp_get_layer_sys(NULL); // default display
|
||||
// cursor = lv_obj_create(lv_scr_act(), NULL);
|
||||
cursor = lv_obj_create(mouse_layer, NULL); // show on every page
|
||||
lv_obj_set_size(cursor, 9, 9);
|
||||
static lv_style_t style_round;
|
||||
lv_style_copy(&style_round, &lv_style_plain);
|
||||
style_round.body.radius = LV_RADIUS_CIRCLE;
|
||||
style_round.body.main_color = LV_COLOR_RED;
|
||||
style_round.body.opa = LV_OPA_COVER;
|
||||
lv_obj_set_style(cursor, &style_round);
|
||||
lv_obj_set_click(cursor, false); // don't click on the cursor
|
||||
lv_indev_set_cursor(mouse_indev, cursor);
|
||||
// }*/
|
||||
|
||||
/*Initialize the graphics library's tick*/
|
||||
tick.attach_ms(guiTickPeriod, lv_tick_handler);
|
||||
|
||||
// guiLoop();
|
||||
}
|
||||
|
||||
void IRAM_ATTR guiLoop()
|
||||
{
|
||||
lv_task_handler(); /* let the GUI do its work */
|
||||
guiCheckSleep();
|
||||
}
|
||||
void guiStop()
|
||||
{}
|
||||
|
||||
bool guiGetConfig(const JsonObject & settings)
|
||||
{
|
||||
if(!settings.isNull() && settings[F_GUI_TICKPERIOD] == guiTickPeriod) return false;
|
||||
|
||||
settings[F_GUI_TICKPERIOD] = guiTickPeriod;
|
||||
|
||||
size_t size = serializeJson(settings, Serial);
|
||||
Serial.println();
|
||||
|
||||
return true;
|
||||
}
|
1066
src/hasp_http.cpp
Normal file
1066
src/hasp_http.cpp
Normal file
File diff suppressed because it is too large
Load Diff
863
src/hasp_http.old
Normal file
863
src/hasp_http.old
Normal file
@ -0,0 +1,863 @@
|
||||
//#include "webServer.h"
|
||||
#include <Arduino.h>
|
||||
#include "ArduinoJson.h"
|
||||
|
||||
#include "hasp_log.h"
|
||||
#include "hasp_debug.h"
|
||||
#include "hasp_http.h"
|
||||
#include "hasp_mqtt.h"
|
||||
#include "hasp_wifi.h"
|
||||
#include "hasp_config.h"
|
||||
#include "hasp.h"
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
#include "SPIFFS.h"
|
||||
#endif
|
||||
#include <FS.h>
|
||||
#include <ESP.h>
|
||||
|
||||
bool httpEnable = true;
|
||||
bool webServerStarted = false;
|
||||
uint16_t httpPort = 80;
|
||||
FS * filesystem = &SPIFFS;
|
||||
File fsUploadFile;
|
||||
String httpUser = "admin";
|
||||
String httpPassword = "";
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
#include <ESP8266WebServer.h>
|
||||
ESP8266WebServer webServer(80);
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
#include <rom/rtc.h> // needed to get the ResetInfo
|
||||
#include <WebServer.h>
|
||||
WebServer webServer(80);
|
||||
|
||||
// Compatibility function for ESP8266 getRestInfo
|
||||
String esp32ResetReason(uint8_t cpuid)
|
||||
{
|
||||
if(cpuid > 1) {
|
||||
return F("Invalid CPU id");
|
||||
}
|
||||
RESET_REASON reason = rtc_get_reset_reason(cpuid);
|
||||
|
||||
switch(reason) {
|
||||
case 1:
|
||||
return F("POWERON_RESET");
|
||||
break; /**<1, Vbat power on reset*/
|
||||
case 3:
|
||||
return F("SW_RESET");
|
||||
break; /**<3, Software reset digital core*/
|
||||
case 4:
|
||||
return F("OWDT_RESET");
|
||||
break; /**<4, Legacy watch dog reset digital core*/
|
||||
case 5:
|
||||
return F("DEEPSLEEP_RESET");
|
||||
break; /**<5, Deep Sleep reset digital core*/
|
||||
case 6:
|
||||
return F("SDIO_RESET");
|
||||
break; /**<6, Reset by SLC module, reset digital core*/
|
||||
case 7:
|
||||
return F("TG0WDT_SYS_RESET");
|
||||
break; /**<7, Timer Group0 Watch dog reset digital core*/
|
||||
case 8:
|
||||
return F("TG1WDT_SYS_RESET");
|
||||
break; /**<8, Timer Group1 Watch dog reset digital core*/
|
||||
case 9:
|
||||
return F("RTCWDT_SYS_RESET");
|
||||
break; /**<9, RTC Watch dog Reset digital core*/
|
||||
case 10:
|
||||
return F("INTRUSION_RESET");
|
||||
break; /**<10, Instrusion tested to reset CPU*/
|
||||
case 11:
|
||||
return F("TGWDT_CPU_RESET");
|
||||
break; /**<11, Time Group reset CPU*/
|
||||
case 12:
|
||||
return F("SW_CPU_RESET");
|
||||
break; /**<12, Software reset CPU*/
|
||||
case 13:
|
||||
return F("RTCWDT_CPU_RESET");
|
||||
break; /**<13, RTC Watch dog Reset CPU*/
|
||||
case 14:
|
||||
return F("EXT_CPU_RESET");
|
||||
break; /**<14, for APP CPU, reseted by PRO CPU*/
|
||||
case 15:
|
||||
return F("RTCWDT_BROWN_OUT_RESET");
|
||||
break; /**<15, Reset when the vdd voltage is not stable*/
|
||||
case 16:
|
||||
return F("RTCWDT_RTC_RESET");
|
||||
break; /**<16, RTC Watch dog reset digital core and rtc module*/
|
||||
default:
|
||||
return F("NO_MEAN");
|
||||
}
|
||||
}
|
||||
|
||||
// these need to be removed
|
||||
const uint8_t D0 = 0;
|
||||
const uint8_t D1 = 1;
|
||||
const uint8_t D2 = 2;
|
||||
#endif // ESP32
|
||||
|
||||
static const char HTTP_DOCTYPE[] PROGMEM =
|
||||
"<!DOCTYPE html><html lang=\"en\"><head><meta name=\"viewport\" content=\"width=device-width,initial-scale=1,"
|
||||
"user-scalable=no\"/>";
|
||||
static const char HTTP_META_GO_BACK[] PROGMEM = "<meta http-equiv='refresh' content='10;url=/'/>";
|
||||
static const char HTTP_HEADER[] PROGMEM = "<title>%s</title>";
|
||||
static const char HTTP_STYLE[] PROGMEM =
|
||||
"<style>.c{text-align:center;}"
|
||||
"div,input{padding:5px;font-size:1em;}"
|
||||
"input{width:90%;}"
|
||||
"body{text-align:center;font-family:verdana;}"
|
||||
"button{border:0;border-radius:0.6rem;background-color:#1fb3ec;color:#eee;line-height:2.4rem;font-size:1.2rem;"
|
||||
"width:100%;}"
|
||||
".q{float:right;width:64px;text-align:right;}"
|
||||
".red{background-color:#f33;}"
|
||||
".button3{background-color:#f44336;}"
|
||||
".button4{background-color:#e7e7e7;color:black;}"
|
||||
".button5{background-color:#555555;}"
|
||||
".button6{background-color:#4CAF50;}</style>";
|
||||
static const char HTTP_SCRIPT[] PROGMEM = "<script>function "
|
||||
"c(l){document.getElementById('s').value=l.innerText||l.textContent;document."
|
||||
"getElementById('p').focus();}</script>";
|
||||
static const char HTTP_HEADER_END[] PROGMEM =
|
||||
"</head><body><div style='text-align:left;display:inline-block;min-width:260px;'>";
|
||||
static const char HTTP_END[] PROGMEM = "<div style='text-align:right;font-size:11px;'><hr/><a href='/about' "
|
||||
"style='color:#aaa;'>HASP 0.0.0 by Francis Van Roie</div></body></html>";
|
||||
// Additional CSS style to match Hass theme
|
||||
static const char HASP_STYLE[] PROGMEM =
|
||||
"<style>button{background-color:#03A9F4;}body{width:60%;margin:auto;}input:invalid{border:"
|
||||
"1px solid red;}input[type=checkbox]{width:20px;}</style>";
|
||||
|
||||
// these need to be removed
|
||||
uint8_t motionPin = 0; // GPIO input pin for motion sensor if connected and enabled
|
||||
bool debugSerialEnabled = true; // Enable USB serial debug output
|
||||
bool debugTelnetEnabled = false; // Enable telnet debug output
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// These defaults may be overwritten with values saved by the web interface
|
||||
char motionPinConfig[3] = "0";
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// URL for auto-update "version.json"
|
||||
const char UPDATE_URL[] = "http://haswitchplate.com/update/version.json";
|
||||
// Default link to compiled Arduino firmware image
|
||||
String espFirmwareUrl = "http://haswitchplate.com/update/HASwitchPlate.ino.d1_mini.bin";
|
||||
// Default link to compiled Nextion firmware images
|
||||
String lcdFirmwareUrl = "http://haswitchplate.com/update/HASwitchPlate.tft";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
String formatBytes(size_t bytes);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
bool httpIsAuthenticated(const String & page)
|
||||
{
|
||||
if(httpPassword[0] != '\0') { // Request HTTP auth if httpPassword is set
|
||||
if(!webServer.authenticate(httpUser.c_str(), httpPassword.c_str())) {
|
||||
webServer.requestAuthentication();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
char buffer[128];
|
||||
sprintf(buffer, PSTR("HTTP: Sending %s page to client connected from: %s"), page.c_str(),
|
||||
webServer.client().remoteIP().toString().c_str());
|
||||
debugPrintln(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
void webSendPage(String & nodename, uint32_t httpdatalength, bool gohome = false)
|
||||
{
|
||||
char buffer[64];
|
||||
|
||||
/* Calculate Content Length upfront */
|
||||
uint16_t contentLength = 0;
|
||||
contentLength += sizeof(HTTP_DOCTYPE) - 1;
|
||||
contentLength += sizeof(HTTP_HEADER) - 1 - 2 + nodename.length();
|
||||
contentLength += sizeof(HTTP_SCRIPT) - 1;
|
||||
contentLength += sizeof(HTTP_STYLE) - 1;
|
||||
contentLength += sizeof(HASP_STYLE) - 1;
|
||||
if(gohome) contentLength += sizeof(HTTP_META_GO_BACK) - 1;
|
||||
contentLength += sizeof(HTTP_HEADER_END) - 1;
|
||||
contentLength += sizeof(HTTP_END) - 1;
|
||||
|
||||
webServer.setContentLength(contentLength + httpdatalength);
|
||||
|
||||
webServer.send_P(200, PSTR("text/html"), HTTP_DOCTYPE); // 122
|
||||
sprintf_P(buffer, HTTP_HEADER, nodename.c_str());
|
||||
webServer.sendContent(buffer); // 17-2+len
|
||||
webServer.sendContent_P(HTTP_SCRIPT); // 131
|
||||
webServer.sendContent_P(HTTP_STYLE); // 487
|
||||
webServer.sendContent_P(HASP_STYLE); // 145
|
||||
if(gohome) webServer.sendContent_P(HTTP_META_GO_BACK); // 47
|
||||
webServer.sendContent_P(HTTP_HEADER_END); // 80
|
||||
}
|
||||
|
||||
void webHandleRoot()
|
||||
{
|
||||
if(!httpIsAuthenticated(F("root"))) return;
|
||||
|
||||
char buffer[64];
|
||||
String nodename = haspGetNodename();
|
||||
String httpMessage((char *)0);
|
||||
httpMessage.reserve(1024);
|
||||
|
||||
httpMessage += String(F("<h1>"));
|
||||
httpMessage += String(nodename);
|
||||
httpMessage += String(F("</h1>"));
|
||||
|
||||
httpMessage += F("<p><form method='get' action='info'><button type='submit'>Information</button></form></p>");
|
||||
httpMessage += F("<p><form method='get' action='config'><button type='submit'>Configuration</button></form></p>");
|
||||
|
||||
httpMessage +=
|
||||
F("<p><form method='get' action='firmware'><button type='submit'>Firmware Upgrade</button></form></p>");
|
||||
httpMessage +=
|
||||
F("<p><form method='get' action='reboot'><button class='red' type='submit'>Restart</button></form></p>");
|
||||
|
||||
webSendPage(nodename, httpMessage.length(), false);
|
||||
webServer.sendContent(httpMessage); // len
|
||||
webServer.sendContent_P(HTTP_END); // 20
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void webHandleAbout()
|
||||
{ // http://plate01/about
|
||||
if(!httpIsAuthenticated(F("/about"))) return;
|
||||
|
||||
String nodename = haspGetNodename();
|
||||
String httpMessage((char *)0);
|
||||
httpMessage.reserve(1250);
|
||||
|
||||
httpMessage += F("<p><h3>HASP OpenHardware edition</h3>Copyright© 2020 Francis Van Roie "
|
||||
"</br>MIT License</p>");
|
||||
httpMessage += F("<p>Based on the previous work of the following open source developers.</p><hr>");
|
||||
httpMessage +=
|
||||
F("<p><h3>HASwitchPlate</h3>Copyright© 2019 Allen Derusha allen@derusha.org</b></br>MIT License</p>");
|
||||
httpMessage +=
|
||||
F("<p><h3>LittlevGL</h3>Copyright© 2016 Gábor Kiss-Vámosi</br>Copyright© 2019 "
|
||||
"LittlevGL</br>MIT License</p>");
|
||||
httpMessage += F("<p><h3>Lvgl ziFont Font Engine</h3>Copyright© 2020 Francis Van Roie</br>MIT License</p>");
|
||||
httpMessage += F("<p><h3>TFT_eSPI Library</h3>Copyright© 2017 Bodmer (https://github.com/Bodmer) All "
|
||||
"rights reserved.</br>FreeBSD License</br>");
|
||||
httpMessage +=
|
||||
F("<i>includes parts from the Adafruit_GFX library - Copyright© 2012 Adafruit Industries. All rights "
|
||||
"reserved. BSD License</i></p>");
|
||||
httpMessage += F("<p><h3>ArduinoJson</h3>Copyright© 2014-2019 Benoit BLANCHON</br>MIT License</p>");
|
||||
httpMessage += F("<p><h3>PubSubClient</h3>Copyright© 2008-2015 Nicholas O'Leary</br>MIT License</p>");
|
||||
httpMessage += F("<p><h3>Syslog</h3>Copyright© 2016 Martin Sloup</br>MIT License</p>");
|
||||
|
||||
httpMessage += F("</p><p><form method='get' action='/'><button type='submit'>Main Menu</button></form>");
|
||||
|
||||
webSendPage(nodename, httpMessage.length(), false);
|
||||
webServer.sendContent(httpMessage); // len
|
||||
webServer.sendContent_P(HTTP_END); // 20
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void webHandleInfo()
|
||||
{ // http://plate01/
|
||||
if(!httpIsAuthenticated(F("/info"))) return;
|
||||
|
||||
char buffer[64];
|
||||
String nodename = haspGetNodename();
|
||||
String httpMessage((char *)0);
|
||||
httpMessage.reserve(1024);
|
||||
|
||||
httpMessage += F("<hr><b>MQTT Status: </b>");
|
||||
if(mqttIsConnected()) { // Check MQTT connection
|
||||
httpMessage += String(F("Connected"));
|
||||
} else {
|
||||
httpMessage += String(F("<font color='red'><b>Disconnected</b></font>, return code: "));
|
||||
// +String(mqttClient.returnCode());
|
||||
}
|
||||
httpMessage += String(F("<br/><b>MQTT ClientID: </b>"));
|
||||
// +String(mqttClientId);
|
||||
httpMessage += F("<br/><b>HASP Version: </b>");
|
||||
httpMessage += String(haspGetVersion());
|
||||
httpMessage += F("<br/><b>Uptime: </b>");
|
||||
httpMessage += String(long(millis() / 1000));
|
||||
|
||||
// httpMessage += String(F("<br/><b>LCD Model: </b>")) + String(LV_HASP_HOR_RES_MAX) + " x " +
|
||||
// String(LV_HASP_VER_RES_MAX); httpMessage += String(F("<br/><b>LCD Version: </b>")) + String(lcdVersion);
|
||||
httpMessage += F("</p/><p><b>LCD Active Page: </b>");
|
||||
httpMessage += String(haspGetPage());
|
||||
httpMessage += F("<br/><b>CPU Frequency: </b>");
|
||||
httpMessage += String(ESP.getCpuFreqMHz());
|
||||
|
||||
httpMessage += F("MHz</p/><p><b>SSID: </b>");
|
||||
httpMessage += String(WiFi.SSID());
|
||||
httpMessage += F("</br><b>Signal Strength: </b>");
|
||||
httpMessage += String(WiFi.RSSI());
|
||||
httpMessage += F("</br><b>IP Address: </b>");
|
||||
httpMessage += String(WiFi.localIP().toString());
|
||||
httpMessage += F("</br><b>Gateway: </b>");
|
||||
httpMessage += String(WiFi.gatewayIP().toString());
|
||||
httpMessage += F("</br><b>DNS Server: </b>");
|
||||
httpMessage += String(WiFi.dnsIP().toString());
|
||||
httpMessage += F("</br><b>MAC Aress: </b>");
|
||||
httpMessage += String(WiFi.macAddress());
|
||||
|
||||
httpMessage += F("</p/><p><b>ESP Chip Id: </b>");
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
httpMessage += String(ESP.getChipRevision());
|
||||
#else
|
||||
httpMessage += String(ESP.getChipId());
|
||||
#endif
|
||||
httpMessage += F("<br/><b>Flash Chip Size: </b>");
|
||||
httpMessage += formatBytes(ESP.getFlashChipSize());
|
||||
httpMessage += F("</br><b>Program Size: </b>");
|
||||
httpMessage += formatBytes(ESP.getSketchSize());
|
||||
httpMessage += F(" bytes<br/><b>Free Program Space: </b>");
|
||||
httpMessage += formatBytes(ESP.getFreeSketchSpace());
|
||||
httpMessage += F(" bytes<br/><b>Free Memory: </b>");
|
||||
httpMessage += formatBytes(ESP.getFreeHeap());
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
// httpMessage += F("<br/><b>Heap Max Alloc: </b>");
|
||||
// httpMessage += String(ESP.getMaxAllocHeap());
|
||||
httpMessage += F("<br/><b>Memory Fragmentation: </b>");
|
||||
httpMessage += String((int16_t)(100.00f - (float)ESP.getMaxAllocHeap() / (float)ESP.getFreeHeap() * 100.00f));
|
||||
httpMessage += F("<br/><b>ESP SDK version: </b>");
|
||||
httpMessage += String(ESP.getSdkVersion());
|
||||
httpMessage += F("<br/><b>Last Reset: </b> CPU0: ");
|
||||
httpMessage += String(esp32ResetReason(0));
|
||||
httpMessage += F(" / CPU1: ");
|
||||
httpMessage += String(esp32ResetReason(1));
|
||||
#else
|
||||
httpMessage += F("<br/><b>Memory Fragmentation: </b>");
|
||||
httpMessage += String(ESP.getHeapFragmentation());
|
||||
httpMessage += F("<br/><b>ESP Core version: </b>");
|
||||
httpMessage += String(ESP.getCoreVersion());
|
||||
httpMessage += F("<br/><b>Last Reset: </b>");
|
||||
httpMessage += String(ESP.getResetInfo());
|
||||
#endif
|
||||
|
||||
httpMessage += F("</p><p><form method='get' action='/'><button type='submit'>Main Menu</button></form>");
|
||||
|
||||
webSendPage(nodename, httpMessage.length(), false);
|
||||
webServer.sendContent(httpMessage); // len
|
||||
webServer.sendContent_P(HTTP_END); // 20
|
||||
}
|
||||
|
||||
String formatBytes(size_t bytes)
|
||||
{
|
||||
if(bytes < 1024) {
|
||||
return String(bytes) + "B";
|
||||
} else if(bytes < (1024 * 1024)) {
|
||||
return String(bytes / 1024.0) + "KB";
|
||||
} else if(bytes < (1024 * 1024 * 1024)) {
|
||||
return String(bytes / 1024.0 / 1024.0) + "MB";
|
||||
} else {
|
||||
return String(bytes / 1024.0 / 1024.0 / 1024.0) + "GB";
|
||||
}
|
||||
}
|
||||
|
||||
String getContentType(String filename)
|
||||
{
|
||||
if(webServer.hasArg(F("download"))) {
|
||||
return F("application/octet-stream");
|
||||
} else if(filename.endsWith(F(".htm"))) {
|
||||
return F("text/html");
|
||||
} else if(filename.endsWith(F(".html"))) {
|
||||
return F("text/html");
|
||||
} else if(filename.endsWith(F(".css"))) {
|
||||
return F("text/css");
|
||||
} else if(filename.endsWith(F(".js"))) {
|
||||
return F("application/javascript");
|
||||
} else if(filename.endsWith(F(".png"))) {
|
||||
return F("image/png");
|
||||
} else if(filename.endsWith(F(".gif"))) {
|
||||
return F("image/gif");
|
||||
} else if(filename.endsWith(F(".jpg"))) {
|
||||
return F("image/jpeg");
|
||||
} else if(filename.endsWith(F(".ico"))) {
|
||||
return F("image/x-icon");
|
||||
} else if(filename.endsWith(F(".xml"))) {
|
||||
return F("text/xml");
|
||||
} else if(filename.endsWith(F(".pdf"))) {
|
||||
return F("application/x-pdf");
|
||||
} else if(filename.endsWith(F(".zip"))) {
|
||||
return F("application/x-zip");
|
||||
} else if(filename.endsWith(F(".gz"))) {
|
||||
return F("application/x-gzip");
|
||||
}
|
||||
return F("text/plain");
|
||||
}
|
||||
|
||||
String urldecode(String str)
|
||||
{
|
||||
String encodedString = "";
|
||||
char c;
|
||||
char code0;
|
||||
char code1;
|
||||
for(int i = 0; i < str.length(); i++) {
|
||||
c = str.charAt(i);
|
||||
if(c == '+') {
|
||||
encodedString += ' ';
|
||||
} else if(c == '%') {
|
||||
char buffer[3];
|
||||
i++;
|
||||
buffer[0] = str.charAt(i);
|
||||
i++;
|
||||
buffer[1] = str.charAt(i);
|
||||
buffer[2] = '\0';
|
||||
c = (char)strtol((const char *)&buffer, NULL, 16);
|
||||
encodedString += c;
|
||||
} else {
|
||||
encodedString += c;
|
||||
}
|
||||
yield();
|
||||
}
|
||||
return encodedString;
|
||||
}
|
||||
|
||||
bool handleFileRead(String path)
|
||||
{
|
||||
path = urldecode(path).substring(0, 31);
|
||||
if(!httpIsAuthenticated(path)) return false;
|
||||
|
||||
if(path.endsWith("/")) {
|
||||
path += F("index.htm");
|
||||
}
|
||||
String pathWithGz = path + F(".gz");
|
||||
if(filesystem->exists(pathWithGz) || filesystem->exists(path)) {
|
||||
if(filesystem->exists(pathWithGz)) path += F(".gz");
|
||||
|
||||
File file = filesystem->open(path, "r");
|
||||
String contentType = getContentType(path);
|
||||
if(path == F("/edit.htm.gz")) {
|
||||
contentType = F("text/html");
|
||||
}
|
||||
webServer.streamFile(file, contentType);
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void handleFileUpload()
|
||||
{
|
||||
if(webServer.uri() != "/edit") {
|
||||
return;
|
||||
}
|
||||
HTTPUpload & upload = webServer.upload();
|
||||
if(upload.status == UPLOAD_FILE_START) {
|
||||
String filename = upload.filename;
|
||||
if(!filename.startsWith("/")) {
|
||||
filename = "/" + filename;
|
||||
}
|
||||
debugPrintln(String(F("handleFileUpload Name: ")) + filename);
|
||||
fsUploadFile = filesystem->open(filename, "w");
|
||||
filename.clear();
|
||||
} else if(upload.status == UPLOAD_FILE_WRITE) {
|
||||
// DBG_OUTPUT_PORT.print("handleFileUpload Data: "); debugPrintln(upload.currentSize);
|
||||
if(fsUploadFile) {
|
||||
fsUploadFile.write(upload.buf, upload.currentSize);
|
||||
}
|
||||
} else if(upload.status == UPLOAD_FILE_END) {
|
||||
if(fsUploadFile) {
|
||||
fsUploadFile.close();
|
||||
}
|
||||
debugPrintln(String(F("handleFileUpload Size: ")) + String(upload.totalSize));
|
||||
}
|
||||
}
|
||||
|
||||
void handleFileDelete()
|
||||
{
|
||||
if(webServer.args() == 0) {
|
||||
return webServer.send(500, PSTR("text/plain"), PSTR("BAD ARGS"));
|
||||
}
|
||||
String path = webServer.arg(0);
|
||||
debugPrintln(String(F("handleFileDelete: ")) + path);
|
||||
if(path == "/") {
|
||||
return webServer.send(500, PSTR("text/plain"), PSTR("BAD PATH"));
|
||||
}
|
||||
if(!filesystem->exists(path)) {
|
||||
return webServer.send(404, PSTR("text/plain"), PSTR("FileNotFound"));
|
||||
}
|
||||
filesystem->remove(path);
|
||||
webServer.send(200, PSTR("text/plain"), "");
|
||||
path.clear();
|
||||
}
|
||||
|
||||
void handleFileCreate()
|
||||
{
|
||||
if(webServer.args() == 0) {
|
||||
return webServer.send(500, PSTR("text/plain"), PSTR("BAD ARGS"));
|
||||
}
|
||||
String path = webServer.arg(0);
|
||||
debugPrintln(String(F("handleFileCreate: ")) + path);
|
||||
if(path == "/") {
|
||||
return webServer.send(500, PSTR("text/plain"), PSTR("BAD PATH"));
|
||||
}
|
||||
if(filesystem->exists(path)) {
|
||||
return webServer.send(500, PSTR("text/plain"), PSTR("FILE EXISTS"));
|
||||
}
|
||||
File file = filesystem->open(path, "w");
|
||||
if(file) {
|
||||
file.close();
|
||||
} else {
|
||||
return webServer.send(500, PSTR("text/plain"), PSTR("CREATE FAILED"));
|
||||
}
|
||||
webServer.send(200, PSTR("text/plain"), "");
|
||||
path.clear();
|
||||
}
|
||||
|
||||
void handleFileList()
|
||||
{
|
||||
if(!webServer.hasArg(F("dir"))) {
|
||||
webServer.send(500, PSTR("text/plain"), PSTR("BAD ARGS"));
|
||||
return;
|
||||
}
|
||||
|
||||
String path = webServer.arg(F("dir"));
|
||||
debugPrintln(String(F("handleFileList: ")) + path);
|
||||
path.clear();
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
debugPrintln(PSTR("HTTP: Listing files on the internal flash:"));
|
||||
File root = SPIFFS.open("/");
|
||||
File file = root.openNextFile();
|
||||
String output = "[";
|
||||
|
||||
while(file) {
|
||||
if(output != "[") {
|
||||
output += ',';
|
||||
}
|
||||
bool isDir = false;
|
||||
output += F("{\"type\":\"");
|
||||
output += (isDir) ? F("dir") : F("file");
|
||||
output += F("\",\"name\":\"");
|
||||
if(file.name()[0] == '/') {
|
||||
output += &(file.name()[1]);
|
||||
} else {
|
||||
output += file.name();
|
||||
}
|
||||
output += F("\"}");
|
||||
|
||||
char msg[64];
|
||||
sprintf(msg, PSTR("HTTP: * %s (%u bytes)"), file.name(), (uint32_t)file.size());
|
||||
debugPrintln(msg);
|
||||
|
||||
// file.close();
|
||||
file = root.openNextFile();
|
||||
}
|
||||
output += "]";
|
||||
#else
|
||||
Dir dir = filesystem->openDir(path);
|
||||
String output = "[";
|
||||
while(dir.next()) {
|
||||
File entry = dir.openFile("r");
|
||||
if(output != "[") {
|
||||
output += ',';
|
||||
}
|
||||
bool isDir = false;
|
||||
output += F("{\"type\":\"");
|
||||
output += (isDir) ? F("dir") : F("file");
|
||||
output += F("\",\"name\":\"");
|
||||
if(entry.name()[0] == '/') {
|
||||
output += &(entry.name()[1]);
|
||||
} else {
|
||||
output += entry.name();
|
||||
}
|
||||
output += F("\"}");
|
||||
entry.close();
|
||||
}
|
||||
output += "]";
|
||||
#endif
|
||||
webServer.send(200, PSTR("text/json"), output);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void webHandleConfig()
|
||||
{ // http://plate01/
|
||||
if(!httpIsAuthenticated(F("/config"))) return;
|
||||
|
||||
char buffer[64];
|
||||
String nodename = haspGetNodename();
|
||||
String httpMessage((char *)0);
|
||||
httpMessage.reserve(1024);
|
||||
|
||||
httpMessage += String(F("<form method='POST' action='saveConfig'>"));
|
||||
httpMessage += String(F("<b>WiFi SSID</b> <i><small>(required)</small></i><input id='wifiSSID' required "
|
||||
"name='wifiSSID' maxlength=32 placeholder='WiFi SSID' value='")) +
|
||||
String(WiFi.SSID()) + "'>";
|
||||
httpMessage += String(F("<br/><b>WiFi Password</b> <i><small>(required)</small></i><input id='wifiPass' required "
|
||||
"name='wifiPass' type='password' maxlength=64 placeholder='WiFi Password' value='")) +
|
||||
String("********") + "'>";
|
||||
httpMessage +=
|
||||
F("<br/><br/><b>HASP Node Name</b> <i><small>(required. lowercase letters, numbers, and _ only)</small>"
|
||||
"</i><input id='haspGetNodename()' required name='haspGetNodename()' maxlength=15 "
|
||||
"placeholder='HASP Node Name' pattern='[a-z0-9_]*' value='");
|
||||
httpMessage += nodename + "'>";
|
||||
httpMessage += F("<br/><br/><b>Group Name</b> <i><small>(required)</small></i><input id='groupName' required "
|
||||
"name='groupName' maxlength=15 placeholder='Group Name' value='");
|
||||
httpMessage += mqttGetGroup() + "'>";
|
||||
httpMessage += F("<br/><br/><b>MQTT Broker</b> <i><small>(required)</small></i><input id='mqttServer' required "
|
||||
"name='mqttServer' maxlength=63 placeholder='mqttServer' value='");
|
||||
httpMessage += mqttGetServer() + "'>";
|
||||
httpMessage += F("<br/><b>MQTT Port</b> <i><small>(required)</small></i><input id='mqttPort' required "
|
||||
"name='mqttPort' type='number' maxlength=5 placeholder='mqttPort' value='");
|
||||
httpMessage += String(mqttGetPort()) + "'>";
|
||||
httpMessage += F("<br/><b>MQTT User</b> <i><small>(optional)</small></i><input id='mqttUser' name='mqttUser' "
|
||||
"maxlength=31 placeholder='mqttUser' value='");
|
||||
httpMessage += mqttGetUser() + "'>";
|
||||
httpMessage += F("<br/><b>MQTT Password</b> <i><small>(optional)</small></i><input id='mqttPassword' "
|
||||
"name='mqttPassword' type='password' maxlength=31 placeholder='mqttPassword' value='");
|
||||
if(mqttGetPassword() != "") httpMessage += String("********");
|
||||
|
||||
httpMessage += String(F("'><br/><br/><b>HASP Admin Username</b> <i><small>(optional)</small></i><input "
|
||||
"id='httpUser' name='httpUser' maxlength=31 placeholder='Admin User' value='")) +
|
||||
String(httpUser) + "'>";
|
||||
httpMessage +=
|
||||
String(F("<br/><b>HASP Admin Password</b> <i><small>(optional)</small></i><input id='httpPassword' "
|
||||
"name='httpPassword' type='password' maxlength=31 placeholder='Admin User Password' value='"));
|
||||
if(httpPassword.length() != 0) {
|
||||
httpMessage += String("********");
|
||||
}
|
||||
httpMessage +=
|
||||
String(F("'><br/><hr><b>Motion Sensor Pin: </b><select id='motionPinConfig' name='motionPinConfig'>"));
|
||||
httpMessage += String(F("<option value='0'"));
|
||||
if(!motionPin) {
|
||||
httpMessage += String(F(" selected"));
|
||||
}
|
||||
httpMessage += String(F(">disabled/not installed</option><option value='D0'"));
|
||||
if(motionPin == D0) {
|
||||
httpMessage += String(F(" selected"));
|
||||
}
|
||||
httpMessage += String(F(">D0</option><option value='D1'"));
|
||||
if(motionPin == D1) {
|
||||
httpMessage += String(F(" selected"));
|
||||
}
|
||||
httpMessage += String(F(">D1</option><option value='D2'"));
|
||||
if(motionPin == D2) {
|
||||
httpMessage += String(F(" selected"));
|
||||
}
|
||||
httpMessage += String(F(">D2</option></select>"));
|
||||
|
||||
httpMessage += String(F("<br/><b>Serial debug output enabled:</b><input id='debugSerialEnabled' "
|
||||
"name='debugSerialEnabled' type='checkbox'"));
|
||||
if(debugSerialEnabled) {
|
||||
httpMessage += String(F(" checked='checked'"));
|
||||
}
|
||||
httpMessage += String(F("><br/><b>Telnet debug output enabled:</b><input id='debugTelnetEnabled' "
|
||||
"name='debugTelnetEnabled' type='checkbox'"));
|
||||
if(debugTelnetEnabled) {
|
||||
httpMessage += String(F(" checked='checked'"));
|
||||
}
|
||||
httpMessage += String(F("><br/><b>mDNS enabled:</b><input id='mdnsEnabled' name='mdnsEnabled' type='checkbox'"));
|
||||
/*if (mdnsEnabled)
|
||||
{
|
||||
httpMessage += String(F(" checked='checked'"));
|
||||
}
|
||||
httpMessage += String(F("><br/><hr><button type='submit'>save settings</button></form>"));
|
||||
|
||||
if (updateEspAvailable)
|
||||
{
|
||||
httpMessage += String(F("<br/><hr><font color='green'><center><h3>HASP Update
|
||||
available!</h3></center></font>")); httpMessage += String(F("<form method='get' action='espfirmware'>"));
|
||||
httpMessage += String(F("<input id='espFirmwareURL' type='hidden' name='espFirmware' value='")) +
|
||||
espFirmwareUrl
|
||||
+ "'>"; httpMessage += String(F("<button type='submit'>update HASP to v")) + String(updateEspAvailableVersion) +
|
||||
String(F("</button></form>"));
|
||||
}*/
|
||||
|
||||
httpMessage += F("<hr><button type='submit'>Save Configuration</button></form>");
|
||||
|
||||
httpMessage += F("<p><form method='get' action='resetConfig'><button class='red' type='submit'>Factory Reset "
|
||||
"All Settings</button></form></p>");
|
||||
|
||||
httpMessage += F("<hr><form method='get' action='/'><button type='submit'>Main Menu</button></form>");
|
||||
|
||||
webSendPage(nodename, httpMessage.length(), false);
|
||||
webServer.sendContent(httpMessage); // len
|
||||
webServer.sendContent_P(HTTP_END); // 20
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void httpHandleNotFound()
|
||||
{ // webServer 404
|
||||
if(handleFileRead(webServer.uri())) return;
|
||||
|
||||
debugPrintln(String(F("HTTP: Sending 404 to client connected from: ")) + webServer.client().remoteIP().toString());
|
||||
|
||||
String httpMessage((char *)0);
|
||||
httpMessage.reserve(128);
|
||||
httpMessage += F("File Not Found\n\nURI: ");
|
||||
httpMessage += webServer.uri();
|
||||
httpMessage += F("\nMethod: ");
|
||||
httpMessage += (webServer.method() == HTTP_GET) ? F("GET") : F("POST");
|
||||
httpMessage += F("\nArguments: ");
|
||||
httpMessage += webServer.args();
|
||||
httpMessage += "\n";
|
||||
for(uint8_t i = 0; i < webServer.args(); i++) {
|
||||
httpMessage += " " + webServer.argName(i) + ": " + webServer.arg(i) + "\n";
|
||||
}
|
||||
webServer.send(404, PSTR("text/plain"), httpMessage.c_str());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void webHandleSaveConfig()
|
||||
{
|
||||
if(!httpIsAuthenticated(F("/saveconfig"))) return;
|
||||
|
||||
configWriteConfig();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void webHandleFirmware()
|
||||
{
|
||||
if(!httpIsAuthenticated(F("/firmware"))) return;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void httpHandleEspFirmware()
|
||||
{ // http://plate01/espfirmware
|
||||
if(!httpIsAuthenticated(F("/espfirmware"))) return;
|
||||
|
||||
String nodename = haspGetNodename();
|
||||
char buffer[64];
|
||||
String httpMessage((char *)0);
|
||||
httpMessage.reserve(128);
|
||||
httpMessage += String(F("<h1>"));
|
||||
httpMessage += String(haspGetNodename());
|
||||
httpMessage += String(F(" ESP update</h1><br/>Updating ESP firmware from: "));
|
||||
httpMessage += String(webServer.arg("espFirmware"));
|
||||
|
||||
webSendPage(nodename, httpMessage.length(), true);
|
||||
webServer.sendContent(httpMessage); // len
|
||||
webServer.sendContent_P(HTTP_END); // 20
|
||||
|
||||
debugPrintln(String(F("HTTP: Attempting ESP firmware update from: ")) + String(webServer.arg("espFirmware")));
|
||||
// espStartOta(webServer.arg("espFirmware"));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void httpHandleReboot()
|
||||
{ // http://plate01/reboot
|
||||
if(!httpIsAuthenticated(F("/reboot"))) return;
|
||||
|
||||
String nodename = haspGetNodename();
|
||||
String httpMessage = F("Rebooting Device");
|
||||
webSendPage(nodename, httpMessage.length(), true);
|
||||
webServer.sendContent(httpMessage); // len
|
||||
webServer.sendContent_P(HTTP_END); // 20
|
||||
delay(500);
|
||||
|
||||
debugPrintln(PSTR("HTTP: Reboot device"));
|
||||
haspSetPage(0);
|
||||
haspSetAttr(F("p[0].b[1].txt"), F("\"Rebooting...\""));
|
||||
|
||||
delay(500);
|
||||
haspReset();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void httpHandleResetConfig()
|
||||
{ // http://plate01/resetConfig
|
||||
if(!httpIsAuthenticated(F("/espfirmware"))) return;
|
||||
|
||||
bool resetConfirmed = webServer.arg(F("confirm")) == F("yes");
|
||||
String nodename = haspGetNodename();
|
||||
char buffer[64];
|
||||
String httpMessage((char *)0);
|
||||
httpMessage.reserve(128);
|
||||
|
||||
if(resetConfirmed) { // User has confirmed, so reset everything
|
||||
httpMessage += F("<h1>");
|
||||
httpMessage += haspGetNodename();
|
||||
httpMessage += F("</h1><b>Resetting all saved settings and restarting device into WiFi AP mode</b>");
|
||||
} else {
|
||||
httpMessage += F("<h1>Warning</h1><b>This process will reset all settings to the default values and "
|
||||
"restart the device. You may need to connect to the WiFi AP displayed on the panel to "
|
||||
"re-configure the device before accessing it again."
|
||||
"<br/><hr><br/><form method='get' action='resetConfig'>"
|
||||
"<br/><br/><button type='submit' name='confirm' value='yes'>reset all settings</button></form>"
|
||||
"<br/><hr><br/><form method='get' action='/'>"
|
||||
"<button type='submit'>return home</button></form>");
|
||||
}
|
||||
|
||||
webSendPage(nodename, httpMessage.length(), resetConfirmed);
|
||||
webServer.sendContent(httpMessage); // len
|
||||
webServer.sendContent_P(HTTP_END); // 20
|
||||
|
||||
if(resetConfirmed) {
|
||||
delay(1000);
|
||||
// configClearSaved();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void httpSetup(const JsonObject & settings)
|
||||
{
|
||||
webServer.on(F("/page/"), []() {
|
||||
String pageid = webServer.arg(F("page"));
|
||||
webServer.send(200, PSTR("text/plain"), "Page: '" + pageid + "'");
|
||||
haspSetPage(pageid.toInt());
|
||||
});
|
||||
|
||||
webServer.on("/list", HTTP_GET, handleFileList);
|
||||
// load editor
|
||||
webServer.on("/edit", HTTP_GET, []() {
|
||||
if(!handleFileRead("/edit.htm")) {
|
||||
webServer.send(404, "text/plain", "FileNotFound");
|
||||
}
|
||||
});
|
||||
webServer.on("/edit", HTTP_PUT, handleFileCreate);
|
||||
webServer.on("/edit", HTTP_DELETE, handleFileDelete);
|
||||
// first callback is called after the request has ended with all parsed arguments
|
||||
// second callback handles file uploads at that location
|
||||
webServer.on("/edit", HTTP_POST, []() { webServer.send(200, "text/plain", ""); }, handleFileUpload);
|
||||
// get heap status, analog input value and all GPIO statuses in one json call
|
||||
webServer.on("/all", HTTP_GET, []() {
|
||||
String json('{');
|
||||
json += "\"heap\":" + String(ESP.getFreeHeap());
|
||||
json += ", \"analog\":" + String(analogRead(A0));
|
||||
json += "}";
|
||||
webServer.send(200, "text/json", json);
|
||||
json.clear();
|
||||
});
|
||||
|
||||
webServer.on(F("/"), webHandleRoot);
|
||||
webServer.on(F("/about"), webHandleAbout);
|
||||
webServer.on(F("/info"), webHandleInfo);
|
||||
webServer.on(F("/config"), webHandleConfig);
|
||||
webServer.on(F("/saveConfig"), webHandleSaveConfig);
|
||||
webServer.on(F("/resetConfig"), httpHandleResetConfig);
|
||||
webServer.on(F("/firmware"), webHandleFirmware);
|
||||
webServer.on(F("/espfirmware"), httpHandleEspFirmware);
|
||||
webServer.on(F("/reboot"), httpHandleReboot);
|
||||
webServer.onNotFound(httpHandleNotFound);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void httpReconnect()
|
||||
{
|
||||
if(!httpEnable) return;
|
||||
|
||||
webServer.stop();
|
||||
webServer.begin();
|
||||
debugPrintln(String(F("HTTP: Server started @ http://")) + WiFi.localIP().toString());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void httpLoop(bool wifiIsConnected)
|
||||
{
|
||||
if(httpEnable) webServer.handleClient();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
bool httpGetConfig(const JsonObject & settings)
|
||||
{
|
||||
if(!settings.isNull() && settings[F_CONFIG_ENABLE] == httpEnable && settings[F_CONFIG_PORT] == httpPort &&
|
||||
settings[F_CONFIG_USER] == httpUser && settings[F_CONFIG_PASS] == httpPassword)
|
||||
return false;
|
||||
|
||||
settings[F_CONFIG_ENABLE] = httpEnable;
|
||||
settings[F_CONFIG_PORT] = httpPort;
|
||||
settings[F_CONFIG_USER] = httpUser;
|
||||
settings[F_CONFIG_PASS] = httpPassword;
|
||||
|
||||
size_t size = serializeJson(settings, Serial);
|
||||
Serial.println();
|
||||
|
||||
return true;
|
||||
}
|
43
src/hasp_log.cpp
Normal file
43
src/hasp_log.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef ESP8266
|
||||
#include <SoftwareSerial.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#else
|
||||
#include <Wifi.h>
|
||||
#endif
|
||||
#include <WiFiUdp.h>
|
||||
#include <Syslog.h>
|
||||
|
||||
#include "hasp_log.h"
|
||||
#include "hasp_debug.h"
|
||||
|
||||
void debugPrintln(String debugText)
|
||||
{
|
||||
serialPrintln(debugText);
|
||||
// if(WiFi.isConnected()) syslog.log(LOG_INFO, debugText);
|
||||
}
|
||||
|
||||
void errorPrintln(String debugText)
|
||||
{
|
||||
char buffer[256];
|
||||
sprintf_P(buffer, debugText.c_str(), PSTR("[ERROR] "));
|
||||
serialPrintln(buffer);
|
||||
if(WiFi.isConnected()) {
|
||||
char buffer[256];
|
||||
sprintf_P(buffer, debugText.c_str(), "");
|
||||
// syslog.log(LOG_ERR, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void warningPrintln(String debugText)
|
||||
{
|
||||
char buffer[256];
|
||||
sprintf_P(buffer, debugText.c_str(), PSTR("[WARNING] "));
|
||||
serialPrintln(buffer);
|
||||
if(WiFi.isConnected()) {
|
||||
char buffer[256];
|
||||
sprintf_P(buffer, debugText.c_str(), "");
|
||||
// syslog.log(LOG_WARNING, buffer);
|
||||
}
|
||||
}
|
55
src/hasp_mdns.cpp
Normal file
55
src/hasp_mdns.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
#include "Arduino.h"
|
||||
#include "ArduinoJson.h"
|
||||
|
||||
#ifdef ESP32
|
||||
#include <ESPmDNS.h>
|
||||
#else
|
||||
#include <ESP8266mDNS.h>
|
||||
MDNSResponder::hMDNSService hMDNSService;
|
||||
#endif
|
||||
|
||||
#include "hasp_mdns.h"
|
||||
|
||||
const char F_CONFIG_ENABLE[] PROGMEM = "enable";
|
||||
|
||||
bool mdnsEnabled = true;
|
||||
String hasp2Node = "plate01";
|
||||
const float haspVersion = 0.38;
|
||||
|
||||
void mdnsSetup(const JsonObject & settings)
|
||||
{
|
||||
if(mdnsEnabled) {
|
||||
// Setup mDNS service discovery if enabled
|
||||
// MDNS.addService(String(hasp2Node), String("tcp"), 80);
|
||||
/*if(debugTelnetEnabled) {
|
||||
MDNS.addService(haspNode, "telnet", "tcp", 23);
|
||||
}*/
|
||||
// MDNS.addServiceTxt(hasp2Node, "tcp", "app_name", "HASwitchPlate");
|
||||
// MDNS.addServiceTxt(hasp2Node, "tcp", "app_version", String(haspVersion).c_str());
|
||||
MDNS.begin(hasp2Node.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void mdnsLoop(bool wifiIsConnected)
|
||||
{
|
||||
// if(mdnsEnabled) {
|
||||
// MDNS();
|
||||
// }s
|
||||
}
|
||||
|
||||
void mdnsStop()
|
||||
{
|
||||
MDNS.end();
|
||||
}
|
||||
|
||||
bool mdnsGetConfig(const JsonObject & settings)
|
||||
{
|
||||
if(!settings.isNull() && settings[F_CONFIG_ENABLE] == mdnsEnabled) return false;
|
||||
|
||||
settings[F_CONFIG_ENABLE] = mdnsEnabled;
|
||||
|
||||
size_t size = serializeJson(settings, Serial);
|
||||
Serial.println();
|
||||
|
||||
return true;
|
||||
}
|
422
src/hasp_mqtt.cpp
Normal file
422
src/hasp_mqtt.cpp
Normal file
@ -0,0 +1,422 @@
|
||||
#include <Arduino.h>
|
||||
#include "ArduinoJson.h"
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
#include <Wifi.h>
|
||||
#else
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <EEPROM.h>
|
||||
#include <ESP.h>
|
||||
#include <DNSServer.h>
|
||||
#endif
|
||||
#include <PubSubClient.h>
|
||||
|
||||
#include "hasp_log.h"
|
||||
#include "hasp_debug.h"
|
||||
#include "hasp_config.h"
|
||||
#include "hasp_mqtt.h"
|
||||
#include "hasp_wifi.h"
|
||||
#include "hasp.h"
|
||||
|
||||
#include "user_config_override.h"
|
||||
|
||||
// Size of buffer for incoming MQTT message
|
||||
#define mqttMaxPacketSize 2u * 1024u
|
||||
|
||||
String mqttClientId; // Auto-generated MQTT ClientID
|
||||
/*
|
||||
String mqttGetSubtopic; // MQTT subtopic for incoming commands requesting .val
|
||||
String mqttGetSubtopicJSON; // MQTT object buffer for JSON status when requesting .val
|
||||
String mqttStateTopic; // MQTT topic for outgoing panel interactions
|
||||
String mqttStateJSONTopic; // MQTT topic for outgoing panel interactions in JSON format
|
||||
String mqttCommandTopic; // MQTT topic for incoming panel commands
|
||||
String mqttGroupCommandTopic; // MQTT topic for incoming group panel commands
|
||||
String mqttStatusTopic; // MQTT topic for publishing device connectivity state
|
||||
String mqttSensorTopic; // MQTT topic for publishing device information in JSON format
|
||||
*/
|
||||
String mqttLightCommandTopic; // MQTT topic for incoming panel backlight on/off commands
|
||||
String mqttLightStateTopic; // MQTT topic for outgoing panel backlight on/off state
|
||||
String mqttLightBrightCommandTopic; // MQTT topic for incoming panel backlight dimmer commands
|
||||
String mqttLightBrightStateTopic; // MQTT topic for outgoing panel backlight dimmer state
|
||||
// String mqttMotionStateTopic; // MQTT topic for outgoing motion sensor state
|
||||
|
||||
String mqttNodeTopic;
|
||||
String mqttGroupTopic;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// These defaults may be overwritten with values saved by the web interface
|
||||
char mqttServer[64] = MQTT_HOST;
|
||||
uint16_t mqttPort = MQTT_PORT;
|
||||
char mqttUser[32] = MQTT_USER;
|
||||
char mqttPassword[32] = MQTT_PASSW;
|
||||
// char haspNode[16] = "plate01";
|
||||
String mqttGroupName = "plates";
|
||||
|
||||
/*
|
||||
const String mqttCommandSubscription = mqttCommandTopic + "/#";
|
||||
const String mqttGroupCommandSubscription = mqttGroupCommandTopic + "/#";
|
||||
const String mqttLightSubscription = "hasp/" + String(haspGetNodename()) + "/light/#";
|
||||
const String mqttLightBrightSubscription = "hasp/" + String(haspGetNodename()) + "/brightness/#";
|
||||
*/
|
||||
|
||||
WiFiClient wifiClient;
|
||||
PubSubClient mqttClient(wifiClient);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Send changed values OUT
|
||||
void mqttSendNewEvent(uint8_t pageid, uint8_t btnid, int32_t val)
|
||||
{
|
||||
char topic[72];
|
||||
sprintf_P(topic, PSTR("hasp/%s/state/p[%u].b[%u].event"), haspGetNodename().c_str(), pageid, btnid);
|
||||
char value[32];
|
||||
itoa(val, value, 10);
|
||||
mqttClient.publish(topic, value);
|
||||
debugPrintln(String(F("MQTT OUT: ")) + String(topic) + " = " + String(value));
|
||||
|
||||
// as json
|
||||
sprintf_P(topic, PSTR("hasp/%s/state/json"), haspGetNodename().c_str(), pageid, btnid);
|
||||
sprintf_P(value, PSTR("{\"event\":\"p[%u]].b[%u].event\", \"value\":%u}"), pageid, btnid, val);
|
||||
mqttClient.publish(topic, value);
|
||||
debugPrintln(String(F("MQTT OUT: ")) + String(topic) + " = " + String(value));
|
||||
}
|
||||
|
||||
void mqttSendNewValue(uint8_t pageid, uint8_t btnid, int32_t val)
|
||||
{
|
||||
char topic[72];
|
||||
sprintf_P(topic, PSTR("hasp/%s/state/p[%u].b[%u].val"), haspGetNodename().c_str(), pageid, btnid);
|
||||
char value[32];
|
||||
itoa(val, value, 10);
|
||||
mqttClient.publish(topic, value);
|
||||
debugPrintln(String(F("MQTT OUT: ")) + String(topic) + " = " + String(value));
|
||||
|
||||
// as json
|
||||
sprintf_P(topic, PSTR("hasp/%s/state/json"), haspGetNodename().c_str(), pageid, btnid);
|
||||
sprintf_P(value, PSTR("{\"event\":\"p[%u]].b[%u].val\", \"value\":%u}"), pageid, btnid, val);
|
||||
mqttClient.publish(topic, value);
|
||||
debugPrintln(String(F("MQTT OUT: ")) + String(topic) + " = " + String(value));
|
||||
}
|
||||
|
||||
void mqttSendNewValue(uint8_t pageid, uint8_t btnid, String txt)
|
||||
{
|
||||
char topic[72];
|
||||
sprintf_P(topic, PSTR("hasp/%s/state/p[%u].b[%u].txt"), haspGetNodename().c_str(), pageid, btnid);
|
||||
mqttClient.publish(topic, txt.c_str());
|
||||
debugPrintln(String(F("MQTT OUT: ")) + String(topic) + " = " + txt);
|
||||
|
||||
// as json
|
||||
char value[64];
|
||||
sprintf_P(topic, PSTR("hasp/%s/state/json"), haspGetNodename().c_str(), pageid, btnid);
|
||||
sprintf_P(value, PSTR("{\"event\":\"p[%u]].b[%u].txt\", \"value\":\"%s\"}"), pageid, btnid, txt.c_str());
|
||||
mqttClient.publish(topic, value);
|
||||
debugPrintln(String(F("MQTT OUT: ")) + String(topic) + " = " + String(value));
|
||||
}
|
||||
|
||||
void mqttHandlePage(String strPageid)
|
||||
{
|
||||
if(strPageid.length() == 0) {
|
||||
String strPayload = String(haspGetPage());
|
||||
String topic = mqttNodeTopic + F("state/page");
|
||||
char buffer[64];
|
||||
sprintf_P(buffer, PSTR("MQTT OUT: %s = %s"), topic.c_str(), strPayload.c_str());
|
||||
debugPrintln(buffer);
|
||||
mqttClient.publish(topic.c_str(), strPayload.c_str());
|
||||
} else {
|
||||
if(strPageid.toInt() <= 250) haspSetPage(strPageid.toInt());
|
||||
}
|
||||
}
|
||||
|
||||
void mqttHandleJson(String & strPayload)
|
||||
{ // Parse an incoming JSON array into individual Nextion commands
|
||||
if(strPayload.endsWith(
|
||||
",]")) { // Trailing null array elements are an artifact of older Home Assistant automations and need to
|
||||
// be removed before parsing by ArduinoJSON 6+
|
||||
strPayload.remove(strPayload.length() - 2, 2);
|
||||
strPayload.concat("]");
|
||||
}
|
||||
DynamicJsonDocument nextionCommands(mqttMaxPacketSize + 1024);
|
||||
DeserializationError jsonError = deserializeJson(nextionCommands, strPayload);
|
||||
if(jsonError) { // Couldn't parse incoming JSON command
|
||||
debugPrintln(String(F("MQTT: [ERROR] Failed to parse incoming JSON command with error: ")) +
|
||||
String(jsonError.c_str()));
|
||||
return;
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < nextionCommands.size(); i++) {
|
||||
debugPrintln(nextionCommands[i]);
|
||||
// nextionSendCmd(nextionCommands[i]);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Receive incoming messages
|
||||
void mqttCallback(char * topic, byte * payload, unsigned int length)
|
||||
{ // Handle incoming commands from MQTT
|
||||
payload[length] = '\0';
|
||||
String strTopic = topic;
|
||||
String strPayload = (char *)payload;
|
||||
|
||||
// strTopic: homeassistant/haswitchplate/devicename/command/p[1].b[4].txt
|
||||
// strPayload: "Lights On"
|
||||
// subTopic: p[1].b[4].txt
|
||||
|
||||
// Incoming Namespace (replace /device/ with /group/ for group commands)
|
||||
// '[...]/device/command' -m '' = No command requested, respond with mqttStatusUpdate()
|
||||
// '[...]/device/command' -m 'dim=50' = nextionSendCmd("dim=50")
|
||||
// '[...]/device/command/json' -m '["dim=5", "page 1"]' = nextionSendCmd("dim=50"), nextionSendCmd("page 1")
|
||||
// '[...]/device/command/page' -m '1' = nextionSendCmd("page 1")
|
||||
// '[...]/device/command/statusupdate' -m '' = mqttStatusUpdate()
|
||||
// '[...]/device/command/lcdupdate' -m 'http://192.168.0.10/local/HASwitchPlate.tft' =
|
||||
// nextionStartOtaDownload("http://192.168.0.10/local/HASwitchPlate.tft")
|
||||
// '[...]/device/command/lcdupdate' -m '' = nextionStartOtaDownload("lcdFirmwareUrl")
|
||||
// '[...]/device/command/espupdate' -m 'http://192.168.0.10/local/HASwitchPlate.ino.d1_mini.bin' =
|
||||
// espStartOta("http://192.168.0.10/local/HASwitchPlate.ino.d1_mini.bin")
|
||||
// '[...]/device/command/espupdate' -m '' = espStartOta("espFirmwareUrl")
|
||||
// '[...]/device/command/p[1].b[4].txt' -m '' = nextionGetAttr("p[1].b[4].txt")
|
||||
// '[...]/device/command/p[1].b[4].txt' -m '"Lights On"' = nextionSetAttr("p[1].b[4].txt", "\"Lights On\"")
|
||||
|
||||
debugPrintln(String(F("MQTT IN: '")) + strTopic + "' : '" + strPayload + "'");
|
||||
|
||||
if(strTopic.startsWith(mqttNodeTopic)) {
|
||||
strTopic = strTopic.substring(mqttNodeTopic.length(), strTopic.length());
|
||||
} else if(strTopic.startsWith(mqttGroupTopic)) {
|
||||
strTopic = strTopic.substring(mqttGroupTopic.length(), strTopic.length());
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
// debugPrintln(String(F("MQTT Short Topic : '")) + strTopic + "'");
|
||||
|
||||
if(strTopic == F("command")) {
|
||||
if(strPayload == "") { // '[...]/device/command' -m '' = No command requested, respond with mqttStatusUpdate()
|
||||
// mqttStatusUpdate(); // return status JSON via MQTT
|
||||
} else { // '[...]/device/command' -m 'dim=50' == nextionSendCmd("dim=50")
|
||||
// nextionSendCmd(strPayload);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(strTopic.startsWith(F("command/"))) {
|
||||
strTopic = strTopic.substring(8u, strTopic.length());
|
||||
// debugPrintln(String(F("MQTT Shorter Command Topic : '")) + strTopic + "'");
|
||||
|
||||
if(strTopic == F("page")) { // '[...]/device/command/page' -m '1' == nextionSendCmd("page 1")
|
||||
mqttHandlePage(strPayload);
|
||||
} else if(strTopic == F("dim")) { // '[...]/device/command/page' -m '1' == nextionSendCmd("page 1")
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
ledcWrite(0, map(strPayload.toInt(), 0, 100, 0, 1023)); // ledChannel and value
|
||||
#else
|
||||
analogWrite(D1, map(strPayload.toInt(), 0, 100, 0, 1023));
|
||||
#endif
|
||||
|
||||
} else if(strTopic == F("json")) { // '[...]/device/command/json' -m '["dim=5", "page 1"]' =
|
||||
// nextionSendCmd("dim=50"), nextionSendCmd("page 1")
|
||||
mqttHandleJson(strPayload); // Send to nextionParseJson()
|
||||
} else if(strTopic == F("statusupdate")) { // '[...]/device/command/statusupdate' == mqttStatusUpdate()
|
||||
// mqttStatusUpdate(); // return status JSON via MQTT
|
||||
} else if(strTopic == F("espupdate")) { // '[...]/device/command/espupdate' -m
|
||||
// 'http://192.168.0.10/local/HASwitchPlate.ino.d1_mini.bin' ==
|
||||
// espStartOta("http://192.168.0.10/local/HASwitchPlate.ino.d1_mini.bin")
|
||||
if(strPayload == "") {
|
||||
// espStartOta(espFirmwareUrl);
|
||||
} else {
|
||||
// espStartOta(strPayload);
|
||||
}
|
||||
} else if(strTopic == F("reboot")) { // '[...]/device/command/reboot' == reboot microcontroller)
|
||||
debugPrintln(F("MQTT: Rebooting device"));
|
||||
haspReset();
|
||||
} else if(strTopic == F("lcdreboot")) { // '[...]/device/command/lcdreboot' == reboot LCD panel)
|
||||
debugPrintln(F("MQTT: Rebooting LCD"));
|
||||
haspReset();
|
||||
} else if(strTopic == F("factoryreset")) { // '[...]/device/command/factoryreset' == clear all saved settings)
|
||||
// configClearSaved();
|
||||
} else if(strPayload == "") { // '[...]/device/command/p[1].b[4].txt' -m '' == nextionGetAttr("p[1].b[4].txt")
|
||||
haspProcessAttribute(strTopic, "");
|
||||
} else { // '[...]/device/command/p[1].b[4].txt' -m '"Lights On"' ==
|
||||
// nextionSetAttr("p[1].b[4].txt", "\"Lights On\"")
|
||||
haspProcessAttribute(strTopic, strPayload);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(strTopic == mqttLightBrightCommandTopic) { // change the brightness from the light topic
|
||||
int panelDim = map(strPayload.toInt(), 0, 255, 0, 100);
|
||||
// nextionSetAttr("dim", String(panelDim));
|
||||
// nextionSendCmd("dims=dim");
|
||||
// mqttClient.publish(mqttLightBrightStateTopic, strPayload);
|
||||
} else if(strTopic == mqttLightCommandTopic &&
|
||||
strPayload == F("OFF")) { // set the panel dim OFF from the light topic, saving current dim level first
|
||||
// nextionSendCmd("dims=dim");
|
||||
// nextionSetAttr("dim", "0");
|
||||
mqttClient.publish(mqttLightStateTopic.c_str(), PSTR("OFF"));
|
||||
} else if(strTopic == mqttLightCommandTopic &&
|
||||
strPayload == F("ON")) { // set the panel dim ON from the light topic, restoring saved dim level
|
||||
// nextionSendCmd("dim=dims");
|
||||
mqttClient.publish(mqttLightStateTopic.c_str(), PSTR("ON"));
|
||||
}
|
||||
|
||||
if(strTopic == F("status") &&
|
||||
strPayload == F("OFF")) { // catch a dangling LWT from a previous connection if it appears
|
||||
char topicBuffer[64];
|
||||
sprintf_P(topicBuffer, PSTR("%sstatus"), mqttNodeTopic.c_str());
|
||||
debugPrintln(String(F("MQTT: binary_sensor state: [")) + topicBuffer + "] : ON");
|
||||
mqttClient.publish(topicBuffer, "ON", true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void mqttReconnect()
|
||||
{
|
||||
static uint8_t mqttReconnectCount = 0;
|
||||
bool mqttFirstConnect = true;
|
||||
String nodeName = haspGetNodename();
|
||||
// Generate an MQTT client ID as haspNode + our MAC address
|
||||
mqttClientId = nodeName + "-" + WiFi.macAddress();
|
||||
|
||||
char topicBuffer[64];
|
||||
sprintf_P(topicBuffer, PSTR("hasp/%s/"), nodeName.c_str());
|
||||
mqttNodeTopic = topicBuffer;
|
||||
sprintf_P(topicBuffer, PSTR("hasp/%s/"), mqttGroupName.c_str());
|
||||
mqttGroupTopic = topicBuffer;
|
||||
|
||||
// haspSetPage(0);
|
||||
debugPrintln(String(F("MQTT: Attempting connection to broker ")) + String(mqttServer) + String(F(" as clientID ")) +
|
||||
mqttClientId);
|
||||
|
||||
// Attempt to connect and set LWT and Clean Session
|
||||
sprintf_P(topicBuffer, PSTR("%sstatus"), mqttNodeTopic.c_str());
|
||||
if(!mqttClient.connect(mqttClientId.c_str(), mqttUser, mqttPassword, topicBuffer, 0, false, "OFF", true)) {
|
||||
// Retry until we give up and restart after connectTimeout seconds
|
||||
mqttReconnectCount++;
|
||||
|
||||
Serial.print(String(F("failed, rc=")));
|
||||
Serial.print(mqttClient.state());
|
||||
// Wait 5 seconds before retrying
|
||||
// delay(50);
|
||||
return;
|
||||
}
|
||||
|
||||
debugPrintln(F("MQTT: [SUCCESS] MQTT Client is Connected"));
|
||||
haspReconnect();
|
||||
|
||||
/*
|
||||
// MQTT topic string definitions
|
||||
mqttStateTopic = prefix + F("/state");
|
||||
mqttStateJSONTopic = prefix + F("/state/json");
|
||||
mqttCommandTopic = prefix + F("/page");
|
||||
mqttGroupCommandTopic = "hasp/" + mqttGroupName + "/page";
|
||||
mqttCommandTopic = prefix + F("/command");
|
||||
mqttGroupCommandTopic = "hasp/" + mqttGroupName + "/command";
|
||||
mqttSensorTopic = prefix + F("/sensor");
|
||||
mqttLightCommandTopic = prefix + F("/light/switch");
|
||||
mqttLightStateTopic = prefix + F("/light/state");
|
||||
mqttLightBrightCommandTopic = prefix + F("/brightness/set");
|
||||
mqttLightBrightStateTopic = prefix + F("/brightness/state");
|
||||
mqttMotionStateTopic = prefix + F("/motion/state");
|
||||
*/
|
||||
// Set keepAlive, cleanSession, timeout
|
||||
// mqttClient.setOptions(30, true, 5000);
|
||||
|
||||
// declare LWT
|
||||
// mqttClient.setWill(mqttStatusTopic.c_str(), "OFF");
|
||||
|
||||
// Attempt to connect to broker, setting last will and testament
|
||||
// Subscribe to our incoming topics
|
||||
sprintf_P(topicBuffer, PSTR("%scommand/#"), mqttGroupTopic.c_str());
|
||||
if(mqttClient.subscribe(topicBuffer)) {
|
||||
debugPrintln(String(F("MQTT: * Subscribed to ")) + topicBuffer);
|
||||
}
|
||||
sprintf_P(topicBuffer, PSTR("%scommand/#"), mqttNodeTopic.c_str());
|
||||
if(mqttClient.subscribe(topicBuffer)) {
|
||||
debugPrintln(String(F("MQTT: * Subscribed to ")) + topicBuffer);
|
||||
}
|
||||
sprintf_P(topicBuffer, PSTR("%slight/#"), mqttNodeTopic.c_str());
|
||||
if(mqttClient.subscribe(topicBuffer)) {
|
||||
debugPrintln(String(F("MQTT: * Subscribed to ")) + topicBuffer);
|
||||
}
|
||||
sprintf_P(topicBuffer, PSTR("%sbrightness/#"), mqttNodeTopic.c_str());
|
||||
if(mqttClient.subscribe(topicBuffer)) {
|
||||
debugPrintln(String(F("MQTT: * Subscribed to ")) + topicBuffer);
|
||||
}
|
||||
|
||||
sprintf_P(topicBuffer, PSTR("%sstatus"), mqttNodeTopic.c_str());
|
||||
if(mqttClient.subscribe(topicBuffer)) {
|
||||
debugPrintln(String(F("MQTT: * Subscribed to ")) + topicBuffer);
|
||||
}
|
||||
// Force any subscribed clients to toggle OFF/ON when we first connect to
|
||||
// make sure we get a full panel refresh at power on. Sending OFF,
|
||||
// "ON" will be sent by the mqttStatusTopic subscription action.
|
||||
debugPrintln(String(F("MQTT: binary_sensor state: [")) + topicBuffer + "] : " + (mqttFirstConnect ? "OFF" : "ON"));
|
||||
mqttClient.publish(topicBuffer, mqttFirstConnect ? "OFF" : "ON", true); //, 1);
|
||||
|
||||
mqttFirstConnect = false;
|
||||
mqttReconnectCount = 0;
|
||||
}
|
||||
|
||||
void mqttSetup(const JsonObject & settings)
|
||||
{
|
||||
if(!settings[F_CONFIG_HOST].isNull()) {
|
||||
strcpy(mqttServer, settings[F_CONFIG_HOST]);
|
||||
}
|
||||
if(!settings[F_CONFIG_PORT].isNull()) {
|
||||
mqttPort = settings[F_CONFIG_PORT];
|
||||
}
|
||||
if(!settings[F_CONFIG_USER].isNull()) {
|
||||
strcpy(mqttUser, settings[F_CONFIG_USER]);
|
||||
}
|
||||
if(!settings[F_CONFIG_PASS].isNull()) {
|
||||
strcpy(mqttPassword, settings[F_CONFIG_PASS]);
|
||||
}
|
||||
if(!settings[F_CONFIG_GROUP].isNull()) {
|
||||
mqttGroupName = settings[F_CONFIG_GROUP].as<String>();
|
||||
}
|
||||
|
||||
mqttClient.setServer(mqttServer, 1883);
|
||||
mqttClient.setCallback(mqttCallback);
|
||||
}
|
||||
|
||||
void mqttLoop(bool wifiIsConnected)
|
||||
{
|
||||
if(wifiIsConnected && !mqttClient.connected())
|
||||
mqttReconnect();
|
||||
else
|
||||
mqttClient.loop();
|
||||
}
|
||||
|
||||
bool mqttIsConnected()
|
||||
{
|
||||
return mqttClient.connected();
|
||||
}
|
||||
|
||||
void mqttStop()
|
||||
{
|
||||
if(mqttClient.connected()) {
|
||||
char topicBuffer[64];
|
||||
|
||||
sprintf_P(topicBuffer, PSTR("%sstatus"), mqttNodeTopic.c_str());
|
||||
mqttClient.publish(topicBuffer, "OFF");
|
||||
|
||||
sprintf_P(topicBuffer, PSTR("%ssensor"), mqttNodeTopic.c_str());
|
||||
mqttClient.publish(topicBuffer, "{\"status\": \"unavailable\"}");
|
||||
|
||||
mqttClient.disconnect();
|
||||
debugPrintln(String(F("MQTT: Disconnected from broker")));
|
||||
}
|
||||
}
|
||||
|
||||
bool mqttGetConfig(const JsonObject & settings)
|
||||
{
|
||||
if(!settings.isNull() && settings[F_CONFIG_HOST] == mqttServer && settings[F_CONFIG_PORT] == mqttPort &&
|
||||
settings[F_CONFIG_USER] == mqttUser && settings[F_CONFIG_PASS] == mqttPassword &&
|
||||
settings[F_CONFIG_GROUP] == mqttGroupName)
|
||||
return false;
|
||||
|
||||
settings[F_CONFIG_GROUP] = mqttGroupName;
|
||||
settings[F_CONFIG_HOST] = mqttServer;
|
||||
settings[F_CONFIG_PORT] = mqttPort;
|
||||
settings[F_CONFIG_USER] = mqttUser;
|
||||
settings[F_CONFIG_PASS] = mqttPassword;
|
||||
|
||||
size_t size = serializeJson(settings, Serial);
|
||||
Serial.println();
|
||||
|
||||
return true;
|
||||
}
|
64
src/hasp_ota.cpp
Normal file
64
src/hasp_ota.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include <Arduino.h>
|
||||
#include "ArduinoJson.h"
|
||||
#include <ArduinoOTA.h>
|
||||
|
||||
#include "hasp_log.h"
|
||||
#include "hasp_debug.h"
|
||||
#include "hasp_ota.h"
|
||||
#include "hasp.h"
|
||||
|
||||
#define F_OTA_URL F("otaurl")
|
||||
|
||||
std::string otaUrl = "http://ota.local";
|
||||
|
||||
void otaSetup(JsonObject settings)
|
||||
{
|
||||
char buffer[256];
|
||||
|
||||
if(!settings[F_OTA_URL].isNull()) {
|
||||
otaUrl = settings[F_OTA_URL].as<String>().c_str();
|
||||
sprintf_P(buffer, PSTR("ORA url: %s"), otaUrl.c_str());
|
||||
debugPrintln(buffer);
|
||||
}
|
||||
|
||||
ArduinoOTA.setHostname(String(haspGetNodename()).c_str());
|
||||
// ArduinoOTA.setPassword(configPassword);
|
||||
|
||||
ArduinoOTA.onStart([]() {
|
||||
debugPrintln(F("OTA: update start"));
|
||||
haspSendCmd("page 0");
|
||||
haspSetAttr("p[0].b[1].txt", "\"ESP OTA Update\"");
|
||||
});
|
||||
ArduinoOTA.onEnd([]() {
|
||||
haspSendCmd("page 0");
|
||||
debugPrintln(F("OTA: update complete"));
|
||||
haspSetAttr("p[0].b[1].txt", "\"ESP OTA Update\\rComplete!\"");
|
||||
haspReset();
|
||||
});
|
||||
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
|
||||
haspSetAttr("p[0].b[1].txt", "\"ESP OTA Update\\rProgress: " + String(progress / (total / 100)) + "%\"");
|
||||
});
|
||||
ArduinoOTA.onError([](ota_error_t error) {
|
||||
debugPrintln(String(F("OTA: ERROR code ")) + String(error));
|
||||
if(error == OTA_AUTH_ERROR)
|
||||
debugPrintln(F("OTA: ERROR - Auth Failed"));
|
||||
else if(error == OTA_BEGIN_ERROR)
|
||||
debugPrintln(F("OTA: ERROR - Begin Failed"));
|
||||
else if(error == OTA_CONNECT_ERROR)
|
||||
debugPrintln(F("OTA: ERROR - Connect Failed"));
|
||||
else if(error == OTA_RECEIVE_ERROR)
|
||||
debugPrintln(F("OTA: ERROR - Receive Failed"));
|
||||
else if(error == OTA_END_ERROR)
|
||||
debugPrintln(F("OTA: ERROR - End Failed"));
|
||||
haspSetAttr("p[0].b[1].txt", "\"ESP OTA FAILED\"");
|
||||
delay(5000);
|
||||
// haspSendCmd("page " + String(nextionActivePage));
|
||||
});
|
||||
ArduinoOTA.begin();
|
||||
debugPrintln(F("OTA: Over the Air firmware update ready"));
|
||||
}
|
||||
|
||||
void otaLoop()
|
||||
{
|
||||
ArduinoOTA.handle(); // Arduino OTA loop
|
||||
}
|
57
src/hasp_spiffs.cpp
Normal file
57
src/hasp_spiffs.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
#include <Arduino.h>
|
||||
#include "ArduinoJson.h"
|
||||
|
||||
#include "hasp_conf.h"
|
||||
#include "hasp_log.h"
|
||||
#include "hasp_spiffs.h"
|
||||
|
||||
#if LV_USE_HASP_SPIFFS
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
#include "SPIFFS.h"
|
||||
#endif
|
||||
#include <FS.h> // Include the SPIFFS library
|
||||
#endif
|
||||
/*
|
||||
void spiffsList()
|
||||
{
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
debugPrintln(PSTR("FILE: Listing files on the internal flash:"));
|
||||
File root = SPIFFS.open("/");
|
||||
File file = root.openNextFile();
|
||||
while(file) {
|
||||
char msg[64];
|
||||
sprintf(msg, PSTR("FILE: * %s (%u bytes)"), file.name(), (uint32_t)file.size());
|
||||
debugPrintln(msg);
|
||||
file = root.openNextFile();
|
||||
}
|
||||
#endif
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
debugPrintln(PSTR("FILE: Listing files on the internal flash:"));
|
||||
Dir dir = SPIFFS.openDir("/");
|
||||
while(dir.next()) {
|
||||
char msg[64];
|
||||
sprintf(msg, PSTR("FILE: * %s (%u bytes)"), dir.fileName().c_str(), (uint32_t)dir.fileSize());
|
||||
debugPrintln(msg);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
*/
|
||||
void spiffsSetup()
|
||||
{
|
||||
// no spiffs settings
|
||||
|
||||
#if LV_USE_HASP_SPIFFS
|
||||
char msg[64];
|
||||
if(!SPIFFS.begin()) {
|
||||
sprintf(msg, PSTR("FILE: %sSPI flash init failed. Unable to mount FS."));
|
||||
errorPrintln(msg);
|
||||
} else {
|
||||
sprintf(msg, PSTR("FILE: [SUCCESS] SPI flash FS mounted"));
|
||||
debugPrintln(msg);
|
||||
// spiffsList();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void spiffsLoop()
|
||||
{}
|
229
src/hasp_tft.cpp
Normal file
229
src/hasp_tft.cpp
Normal file
@ -0,0 +1,229 @@
|
||||
#include "TFT_eSPI.h" // Graphics and font library for ST7735 driver chip
|
||||
#include "ArduinoJson.h"
|
||||
|
||||
#ifdef ESP8266
|
||||
ADC_MODE(ADC_VCC); // tftShowConfig measures the voltage on the pin
|
||||
#endif
|
||||
|
||||
#include "hasp_log.h"
|
||||
#include "hasp_tft.h"
|
||||
|
||||
#define F_TFT_ROTATION F("rotation")
|
||||
#define F_TFT_FREQUENCY F("frequency")
|
||||
|
||||
int8_t getPinName(int8_t pin);
|
||||
|
||||
void tftSetup(TFT_eSPI & tft, JsonObject settings)
|
||||
{
|
||||
uint8_t rotation = TFT_ROTATION;
|
||||
if(settings[F_TFT_ROTATION]) rotation = settings[F_TFT_ROTATION];
|
||||
uint32_t frequency = SPI_FREQUENCY;
|
||||
if(settings[F_TFT_FREQUENCY]) frequency = settings[F_TFT_FREQUENCY];
|
||||
|
||||
char buffer[64];
|
||||
sprintf_P(buffer, PSTR("TFT: %d rotation / %d frequency"), rotation, frequency);
|
||||
debugPrintln(buffer);
|
||||
|
||||
tft.begin(); /* TFT init */
|
||||
tft.setRotation(rotation); /* 1/3=Landscape or 0/2=Portrait orientation */
|
||||
|
||||
tftShowConfig(tft);
|
||||
/* Load Calibration data */
|
||||
|
||||
#ifdef ESP8266
|
||||
uint16_t calData[5] = {238, 3529, 369, 3532, 6};
|
||||
#else
|
||||
uint16_t calData[5] = {294, 3410, 383, 3491, 4};
|
||||
#endif
|
||||
// uint16_t calData[5] = {0, 0, 0, 0, 0};
|
||||
uint8_t calDataOK = 0;
|
||||
|
||||
// Calibrate
|
||||
if(0) {
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
tft.setCursor(20, 0);
|
||||
// tft.setTextFont(2);
|
||||
tft.setTextSize(1);
|
||||
tft.setTextColor(TFT_WHITE, TFT_BLACK);
|
||||
|
||||
tft.println(PSTR("Touch corners as indicated"));
|
||||
|
||||
tft.setTextFont(1);
|
||||
tft.calibrateTouch(calData, TFT_MAGENTA, TFT_BLACK, 15);
|
||||
|
||||
for(uint8_t i = 0; i < 5; i++) {
|
||||
Serial.print(calData[i]);
|
||||
if(i < 4) Serial.print(", ");
|
||||
}
|
||||
}
|
||||
|
||||
tft.setTouch(calData);
|
||||
}
|
||||
|
||||
void tftLoop()
|
||||
{
|
||||
// Nothing to do here
|
||||
}
|
||||
|
||||
void tftStop()
|
||||
{}
|
||||
|
||||
void tftOffsetInfo(uint8_t pin, uint8_t x_offset, uint8_t y_offset)
|
||||
{
|
||||
char buffer[64];
|
||||
if(x_offset != 0) {
|
||||
sprintf_P(buffer, PSTR("TFT: R%u x offset = %i"), pin, x_offset);
|
||||
debugPrintln(buffer);
|
||||
}
|
||||
if(y_offset != 0) {
|
||||
sprintf_P(buffer, PSTR("TFT: R%u y offset = %i"), pin, y_offset);
|
||||
debugPrintln(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void tftPinInfo(String pinfunction, int8_t pin)
|
||||
{
|
||||
if(pin != -1) {
|
||||
char buffer[64];
|
||||
sprintf_P(buffer, PSTR("TFT: %s = D%i (GPIO %i)"), pinfunction.c_str(), getPinName(pin), pin);
|
||||
debugPrintln(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void tftCalibrate()
|
||||
{}
|
||||
|
||||
void tftShowConfig(TFT_eSPI & tft)
|
||||
{
|
||||
setup_t tftSetup;
|
||||
char buffer[128];
|
||||
tft.getSetup(tftSetup);
|
||||
|
||||
sprintf_P(buffer, PSTR("TFT: TFT_eSPI ver = %s"), tftSetup.version.c_str());
|
||||
debugPrintln(buffer);
|
||||
sprintf_P(buffer, PSTR("TFT: Processor = ESP%i"), tftSetup.esp, HEX);
|
||||
debugPrintln(buffer);
|
||||
sprintf_P(buffer, PSTR("TFT: Frequency = %i MHz"), ESP.getCpuFreqMHz());
|
||||
debugPrintln(buffer);
|
||||
|
||||
#ifdef ESP8266
|
||||
sprintf_P(buffer, PSTR("TFT: Voltage = %2.2f V"), ESP.getVcc() / 918.0);
|
||||
debugPrintln(buffer); // 918 empirically determined
|
||||
#endif
|
||||
sprintf_P(buffer, PSTR("TFT: Transactions = %s"), (tftSetup.trans == 1) ? PSTR("Yes") : PSTR("No"));
|
||||
debugPrintln(buffer);
|
||||
sprintf_P(buffer, PSTR("TFT: Interface = %s"), (tftSetup.serial == 1) ? PSTR("SPI") : PSTR("Parallel"));
|
||||
debugPrintln(buffer);
|
||||
#ifdef ESP8266
|
||||
sprintf_P(buffer, PSTR("TFT: SPI overlap = %s"), (tftSetup.overlap == 1) ? PSTR("Yes") : PSTR("No"));
|
||||
debugPrintln(buffer);
|
||||
#endif
|
||||
if(tftSetup.tft_driver != 0xE9D) // For ePaper displays the size is defined in the sketch
|
||||
{
|
||||
sprintf_P(buffer, PSTR("TFT: Display driver = %i"), tftSetup.tft_driver);
|
||||
debugPrintln(buffer);
|
||||
sprintf_P(buffer, PSTR("TFT: Display width = %i"), tftSetup.tft_width);
|
||||
debugPrintln(buffer);
|
||||
sprintf_P(buffer, PSTR("TFT: Display height = %i"), tftSetup.tft_height);
|
||||
debugPrintln(buffer);
|
||||
} else if(tftSetup.tft_driver == 0xE9D)
|
||||
debugPrintln(F("Display driver = ePaper"));
|
||||
|
||||
// Offsets, not all used yet
|
||||
tftOffsetInfo(0, tftSetup.r0_x_offset, tftSetup.r0_y_offset);
|
||||
tftOffsetInfo(1, tftSetup.r1_x_offset, tftSetup.r1_y_offset);
|
||||
tftOffsetInfo(2, tftSetup.r2_x_offset, tftSetup.r2_y_offset);
|
||||
tftOffsetInfo(3, tftSetup.r3_x_offset, tftSetup.r3_y_offset);
|
||||
/* replaced by tftOffsetInfo
|
||||
if(tftSetup.r1_x_offset != 0) Serial.printf("R1 x offset = %i \n", tftSetup.r1_x_offset);
|
||||
if(tftSetup.r1_y_offset != 0) Serial.printf("R1 y offset = %i \n", tftSetup.r1_y_offset);
|
||||
if(tftSetup.r2_x_offset != 0) Serial.printf("R2 x offset = %i \n", tftSetup.r2_x_offset);
|
||||
if(tftSetup.r2_y_offset != 0) Serial.printf("R2 y offset = %i \n", tftSetup.r2_y_offset);
|
||||
if(tftSetup.r3_x_offset != 0) Serial.printf("R3 x offset = %i \n", tftSetup.r3_x_offset);
|
||||
if(tftSetup.r3_y_offset != 0) Serial.printf("R3 y offset = %i \n", tftSetup.r3_y_offset);
|
||||
*/
|
||||
|
||||
tftPinInfo(F("MOSI "), tftSetup.pin_tft_mosi);
|
||||
tftPinInfo(F("MISO "), tftSetup.pin_tft_miso);
|
||||
tftPinInfo(F("SCLK "), tftSetup.pin_tft_clk);
|
||||
|
||||
#ifdef ESP8266
|
||||
if(tftSetup.overlap == true) {
|
||||
debugPrintln(F("Overlap selected, following pins MUST be used:\n"));
|
||||
|
||||
debugPrintln(F("MOSI = SD1 (GPIO 8)\n"));
|
||||
debugPrintln(F("MISO = SD0 (GPIO 7)\n"));
|
||||
debugPrintln(F("SCK = CLK (GPIO 6)\n"));
|
||||
debugPrintln(F("TFT_CS = D3 (GPIO 0)\n\n"));
|
||||
|
||||
debugPrintln(F("TFT_DC and TFT_RST pins can be tftSetup defined\n"));
|
||||
}
|
||||
#endif
|
||||
|
||||
tftPinInfo(F("TFT_CS "), tftSetup.pin_tft_cs);
|
||||
tftPinInfo(F("TFT_DC "), tftSetup.pin_tft_dc);
|
||||
tftPinInfo(F("TFT_RST"), tftSetup.pin_tft_rst);
|
||||
|
||||
tftPinInfo(F("TOUCH_RST"), tftSetup.pin_tch_cs);
|
||||
|
||||
tftPinInfo(F("TFT_WR "), tftSetup.pin_tft_wr);
|
||||
tftPinInfo(F("TFT_RD "), tftSetup.pin_tft_rd);
|
||||
|
||||
tftPinInfo(F("TFT_D0 "), tftSetup.pin_tft_d0);
|
||||
tftPinInfo(F("TFT_D1 "), tftSetup.pin_tft_d1);
|
||||
tftPinInfo(F("TFT_D2 "), tftSetup.pin_tft_d2);
|
||||
tftPinInfo(F("TFT_D3 "), tftSetup.pin_tft_d3);
|
||||
tftPinInfo(F("TFT_D4 "), tftSetup.pin_tft_d4);
|
||||
tftPinInfo(F("TFT_D5 "), tftSetup.pin_tft_d5);
|
||||
tftPinInfo(F("TFT_D6 "), tftSetup.pin_tft_d6);
|
||||
tftPinInfo(F("TFT_D7 "), tftSetup.pin_tft_d7);
|
||||
|
||||
uint16_t fonts = tft.fontsLoaded();
|
||||
if(fonts & (1 << 1)) debugPrintln(F("Font GLCD loaded\n"));
|
||||
if(fonts & (1 << 2)) debugPrintln(F("Font 2 loaded\n"));
|
||||
if(fonts & (1 << 4)) debugPrintln(F("Font 4 loaded\n"));
|
||||
if(fonts & (1 << 6)) debugPrintln(F("Font 6 loaded\n"));
|
||||
if(fonts & (1 << 7)) debugPrintln(F("Font 7 loaded\n"));
|
||||
if(fonts & (1 << 9))
|
||||
debugPrintln(F("Font 8N loaded\n"));
|
||||
else if(fonts & (1 << 8))
|
||||
debugPrintln(F("Font 8 loaded\n"));
|
||||
if(fonts & (1 << 15)) debugPrintln(F("Smooth font enabled\n"));
|
||||
|
||||
if(tftSetup.serial == 1) {
|
||||
sprintf_P(buffer, PSTR("TFT: Display SPI frequency = %2.1f MHz"), tftSetup.tft_spi_freq / 10.0);
|
||||
debugPrintln(buffer);
|
||||
}
|
||||
if(tftSetup.pin_tch_cs != -1) {
|
||||
sprintf_P(buffer, PSTR("TFT: Touch SPI frequency = %2.1f MHz"), tftSetup.tch_spi_freq / 10.0);
|
||||
debugPrintln(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
// Get pin name for ESP8266
|
||||
int8_t getPinName(int8_t pin)
|
||||
{
|
||||
// For ESP32 pin labels on boards use the GPIO number
|
||||
#ifdef ESP32
|
||||
return pin;
|
||||
#endif
|
||||
|
||||
// For ESP8266 the pin labels are not the same as the GPIO number
|
||||
// These are for the NodeMCU pin definitions:
|
||||
// GPIO Dxx
|
||||
if(pin == 16) return 0;
|
||||
if(pin == 5) return 1;
|
||||
if(pin == 4) return 2;
|
||||
if(pin == 0) return 3;
|
||||
if(pin == 2) return 4;
|
||||
if(pin == 14) return 5;
|
||||
if(pin == 12) return 6;
|
||||
if(pin == 13) return 7;
|
||||
if(pin == 15) return 8;
|
||||
if(pin == 3) return 9;
|
||||
if(pin == 1) return 10;
|
||||
if(pin == 9) return 11;
|
||||
if(pin == 10) return 12;
|
||||
|
||||
return -1; // Invalid pin
|
||||
}
|
179
src/hasp_wifi.cpp
Normal file
179
src/hasp_wifi.cpp
Normal file
@ -0,0 +1,179 @@
|
||||
#include <Arduino.h>
|
||||
#include "ArduinoJson.h"
|
||||
|
||||
#include "hasp_conf.h"
|
||||
|
||||
#include "hasp_wifi.h"
|
||||
#include "hasp_mqtt.h"
|
||||
#include "hasp_http.h"
|
||||
#include "hasp_log.h"
|
||||
#include "hasp_debug.h"
|
||||
#include "hasp_config.h"
|
||||
#include "hasp_gui.h"
|
||||
#include "hasp.h"
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
#include <Wifi.h>
|
||||
#else
|
||||
#include <ESP8266WiFi.h>
|
||||
|
||||
static WiFiEventHandler wifiEventHandler[3];
|
||||
|
||||
#endif
|
||||
|
||||
#include "user_config_override.h"
|
||||
|
||||
std::string wifiSsid = WIFI_SSID;
|
||||
std::string wifiPassword = WIFI_PASSW;
|
||||
|
||||
// long wifiPrevMillis = 0;
|
||||
// bool wifiWasConnected = false;
|
||||
// int8_t wifiReconnectAttempt = -20;
|
||||
|
||||
void wifiConnected(IPAddress ipaddress)
|
||||
{
|
||||
char buffer[64];
|
||||
sprintf_P(buffer, PSTR("WIFI: Received IP address %s"), ipaddress.toString().c_str());
|
||||
debugPrintln(buffer);
|
||||
sprintf_P(buffer, PSTR("WIFI: Connected = %s"), WiFi.status() == WL_CONNECTED ? PSTR("yes") : PSTR("no"));
|
||||
debugPrintln(buffer);
|
||||
|
||||
httpReconnect();
|
||||
// mqttReconnect();
|
||||
haspReconnect();
|
||||
}
|
||||
|
||||
void wifiDisconnected(const char * ssid, uint8_t reason)
|
||||
{
|
||||
char buffer[64];
|
||||
sprintf_P(buffer, PSTR("WIFI: Disconnected from %s (Reason: %d)"), ssid, reason);
|
||||
debugPrintln(buffer);
|
||||
WiFi.reconnect();
|
||||
}
|
||||
|
||||
void wifiSsidConnected(const char * ssid)
|
||||
{
|
||||
char buffer[64];
|
||||
sprintf_P(buffer, PSTR("WIFI: Connected to SSID %s. Requesting IP..."), ssid);
|
||||
debugPrintln(buffer);
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
void wifi_callback(system_event_id_t event, system_event_info_t info)
|
||||
{
|
||||
switch(event) {
|
||||
case SYSTEM_EVENT_STA_CONNECTED:
|
||||
wifiSsidConnected((const char *)info.connected.ssid);
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_GOT_IP:
|
||||
wifiConnected(IPAddress(info.got_ip.ip_info.ip.addr));
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_DISCONNECTED:
|
||||
wifiDisconnected((const char *)info.disconnected.ssid, info.disconnected.reason);
|
||||
// NTP.stop(); // NTP sync can be disabled to avoid sync errors
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
void wifiSTAConnected(WiFiEventStationModeConnected info)
|
||||
{
|
||||
wifiSsidConnected(info.ssid.c_str());
|
||||
}
|
||||
|
||||
// Start NTP only after IP network is connected
|
||||
void wifiSTAGotIP(WiFiEventStationModeGotIP info)
|
||||
{
|
||||
wifiConnected(IPAddress(info.ip));
|
||||
}
|
||||
|
||||
// Manage network disconnection
|
||||
void wifiSTADisconnected(WiFiEventStationModeDisconnected info)
|
||||
{
|
||||
wifiDisconnected(info.ssid.c_str(), info.reason);
|
||||
}
|
||||
#endif
|
||||
|
||||
void wifiSetup(JsonObject settings)
|
||||
{
|
||||
char buffer[64];
|
||||
|
||||
if(!settings[F_CONFIG_SSID].isNull()) {
|
||||
wifiSsid = settings[F_CONFIG_SSID].as<String>().c_str();
|
||||
|
||||
// sprintf_P(buffer, PSTR("Wifi Ssid: %s"), wifiSsid.c_str());
|
||||
// debugPrintln(buffer);
|
||||
}
|
||||
if(!settings[F_CONFIG_PASS].isNull()) {
|
||||
wifiPassword = settings[F_CONFIG_PASS].as<String>().c_str();
|
||||
|
||||
// sprintf_P(buffer, PSTR("Wifi Password: %s"), wifiPassword.c_str());
|
||||
// debugPrintln(buffer);
|
||||
}
|
||||
|
||||
sprintf_P(buffer, PSTR("WIFI: Connecting to : %s"), wifiSsid.c_str());
|
||||
debugPrintln(buffer);
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(wifiSsid.c_str(), wifiPassword.c_str());
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
WiFi.onEvent(wifi_callback);
|
||||
#endif
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
wifiEventHandler[0] = WiFi.onStationModeGotIP(wifiSTAGotIP); // As soon WiFi is connected, start NTP Client
|
||||
wifiEventHandler[1] = WiFi.onStationModeDisconnected(wifiSTADisconnected);
|
||||
wifiEventHandler[2] = WiFi.onStationModeConnected(wifiSTAConnected);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool wifiLoop()
|
||||
{
|
||||
return WiFi.status() == WL_CONNECTED;
|
||||
|
||||
/*
|
||||
if(WiFi.status() == WL_CONNECTED) {
|
||||
if(wifiWasConnected) return true;
|
||||
|
||||
debugPrintln(F("WIFI: Reconnected"));
|
||||
wifiWasConnected = true;
|
||||
wifiReconnectAttempt = 1;
|
||||
wifiPrevMillis = millis();
|
||||
haspOnline();
|
||||
return true;
|
||||
|
||||
} else if(millis() - wifiPrevMillis > 1000) {
|
||||
if(wifiReconnectAttempt < 20) {
|
||||
if(wifiReconnectAttempt == 1) { // <0 means we were never connected yet
|
||||
// haspOffline();
|
||||
warningPrintln(String(F("WIFI: %sConnection lost. Reconnecting... #")) +
|
||||
String(wifiReconnectAttempt)); WiFi.reconnect(); } else { debugPrintln(F("WIFI: Waiting for connection..."));
|
||||
}
|
||||
} else {
|
||||
// haspOffline();
|
||||
debugPrintln(F("WIFI: Connection lost. Reconnecting..."));
|
||||
WiFi.reconnect();
|
||||
}
|
||||
wifiReconnectAttempt++;
|
||||
wifiPrevMillis = millis();
|
||||
}
|
||||
return false;*/
|
||||
}
|
||||
|
||||
bool wifiGetConfig(const JsonObject & settings)
|
||||
{
|
||||
if(!settings.isNull() && settings[F_CONFIG_SSID] == String(wifiSsid.c_str()) &&
|
||||
settings[F_CONFIG_PASS] == String(wifiPassword.c_str()))
|
||||
return false;
|
||||
|
||||
settings[F_CONFIG_SSID] = String(wifiSsid.c_str());
|
||||
settings[F_CONFIG_PASS] = String(wifiPassword.c_str());
|
||||
|
||||
size_t size = serializeJson(settings, Serial);
|
||||
Serial.println();
|
||||
|
||||
return true;
|
||||
}
|
519
src/lv_theme_hasp.c
Normal file
519
src/lv_theme_hasp.c
Normal file
@ -0,0 +1,519 @@
|
||||
/**
|
||||
* @file lv_theme_hasp.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../lib/lvgl/src/lv_themes/lv_theme.h"
|
||||
|
||||
#if LV_USE_THEME_HASP
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
static lv_theme_t theme;
|
||||
static lv_style_t def;
|
||||
static lv_style_t scr;
|
||||
|
||||
/*Static style definitions*/
|
||||
static lv_style_t sb;
|
||||
static lv_style_t plain_bordered;
|
||||
static lv_style_t label_prim;
|
||||
static lv_style_t label_sec;
|
||||
static lv_style_t label_hint;
|
||||
|
||||
static lv_style_t btn_rel, btn_pr, btn_trel, btn_tpr, btn_ina;
|
||||
|
||||
/*Saved input parameters*/
|
||||
static uint16_t _hue;
|
||||
static lv_font_t * _font;
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static void basic_init(void)
|
||||
{
|
||||
lv_style_plain.text.font = _font;
|
||||
lv_style_pretty.text.font = _font;
|
||||
lv_style_pretty_color.text.font = _font;
|
||||
|
||||
lv_style_btn_rel.text.font = _font;
|
||||
lv_style_btn_pr.text.font = _font;
|
||||
lv_style_btn_tgl_rel.text.font = _font;
|
||||
lv_style_btn_tgl_pr.text.font = _font;
|
||||
lv_style_btn_ina.text.font = _font;
|
||||
|
||||
if(_hue <= 360) {
|
||||
lv_style_pretty_color.body.main_color = lv_color_hsv_to_rgb(_hue, 10, 70);
|
||||
lv_style_pretty_color.body.grad_color = lv_color_hsv_to_rgb(_hue, 80, 80);
|
||||
|
||||
lv_style_plain_color.body.main_color = lv_color_hsv_to_rgb(_hue, 50, 75);
|
||||
lv_style_plain_color.body.grad_color = lv_style_plain_color.body.main_color;
|
||||
|
||||
lv_style_btn_rel.body.main_color = lv_color_hsv_to_rgb(_hue, 10, 70);
|
||||
lv_style_btn_rel.body.grad_color = lv_color_hsv_to_rgb(_hue, 80, 80);
|
||||
|
||||
lv_style_btn_pr.body.main_color = lv_color_hsv_to_rgb(_hue, 80, 70);
|
||||
lv_style_btn_pr.body.grad_color = lv_color_hsv_to_rgb(_hue, 10, 80);
|
||||
|
||||
lv_style_btn_tgl_rel.body.main_color = lv_color_hsv_to_rgb(_hue, 10, 70);
|
||||
lv_style_btn_tgl_rel.body.grad_color = lv_color_hsv_to_rgb(_hue, 80, 80);
|
||||
|
||||
lv_style_btn_tgl_pr.body.main_color = lv_color_hsv_to_rgb(_hue, 80, 70);
|
||||
lv_style_btn_tgl_pr.body.grad_color = lv_color_hsv_to_rgb(_hue, 10, 80);
|
||||
}
|
||||
|
||||
lv_style_copy(&def, &lv_style_pretty); /*Initialize the default style*/
|
||||
def.text.font = _font;
|
||||
|
||||
lv_style_copy(&scr, &def);
|
||||
scr.body.padding.bottom = 0;
|
||||
scr.body.padding.top = 0;
|
||||
scr.body.padding.left = 0;
|
||||
scr.body.padding.right = 0;
|
||||
|
||||
lv_style_copy(&sb, &lv_style_pretty_color);
|
||||
sb.body.grad_color = sb.body.main_color;
|
||||
sb.body.padding.right = sb.body.padding.right / 2; /*Make closer to the edges*/
|
||||
sb.body.padding.bottom = sb.body.padding.bottom / 2;
|
||||
|
||||
lv_style_copy(&plain_bordered, &lv_style_plain);
|
||||
plain_bordered.body.border.width = 2;
|
||||
plain_bordered.body.border.color = lv_color_hex3(0xbbb);
|
||||
|
||||
theme.style.bg = &lv_style_plain;
|
||||
theme.style.scr = &scr;
|
||||
theme.style.panel = &lv_style_pretty;
|
||||
}
|
||||
|
||||
static void btn_init(void)
|
||||
{
|
||||
#if LV_USE_BTN != 0
|
||||
theme.style.btn.rel = &lv_style_btn_rel;
|
||||
theme.style.btn.pr = &lv_style_btn_pr;
|
||||
theme.style.btn.tgl_rel = &lv_style_btn_tgl_rel;
|
||||
theme.style.btn.tgl_pr = &lv_style_btn_tgl_pr;
|
||||
theme.style.btn.ina = &lv_style_btn_ina;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void label_init(void)
|
||||
{
|
||||
#if LV_USE_LABEL != 0
|
||||
|
||||
lv_style_copy(&label_prim, &lv_style_plain);
|
||||
lv_style_copy(&label_sec, &lv_style_plain);
|
||||
lv_style_copy(&label_hint, &lv_style_plain);
|
||||
|
||||
label_prim.text.color = lv_color_hex3(0x111);
|
||||
label_sec.text.color = lv_color_hex3(0x888);
|
||||
label_hint.text.color = lv_color_hex3(0xaaa);
|
||||
|
||||
theme.style.label.prim = &label_prim;
|
||||
theme.style.label.sec = &label_sec;
|
||||
theme.style.label.hint = &label_hint;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void img_init(void)
|
||||
{
|
||||
#if LV_USE_IMG != 0
|
||||
|
||||
theme.style.img.light = &def;
|
||||
theme.style.img.dark = &def;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void line_init(void)
|
||||
{
|
||||
#if LV_USE_LINE != 0
|
||||
|
||||
theme.style.line.decor = &def;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void led_init(void)
|
||||
{
|
||||
#if LV_USE_LED != 0
|
||||
static lv_style_t led;
|
||||
|
||||
lv_style_copy(&led, &lv_style_pretty_color);
|
||||
led.body.shadow.width = LV_DPI / 10;
|
||||
led.body.radius = LV_RADIUS_CIRCLE;
|
||||
led.body.border.width = LV_DPI / 30;
|
||||
led.body.border.opa = LV_OPA_30;
|
||||
led.body.shadow.color = led.body.main_color;
|
||||
|
||||
theme.style.led = &led;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void bar_init(void)
|
||||
{
|
||||
#if LV_USE_BAR
|
||||
|
||||
theme.style.bar.bg = &lv_style_pretty;
|
||||
theme.style.bar.indic = &lv_style_pretty_color;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void slider_init(void)
|
||||
{
|
||||
#if LV_USE_SLIDER != 0
|
||||
static lv_style_t slider_bg;
|
||||
lv_style_copy(&slider_bg, &lv_style_pretty);
|
||||
slider_bg.body.padding.left = LV_DPI / 20;
|
||||
slider_bg.body.padding.right = LV_DPI / 20;
|
||||
slider_bg.body.padding.top = LV_DPI / 20;
|
||||
slider_bg.body.padding.bottom = LV_DPI / 20;
|
||||
|
||||
theme.style.slider.bg = &slider_bg;
|
||||
theme.style.slider.indic = &lv_style_pretty_color;
|
||||
theme.style.slider.knob = &lv_style_pretty;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void sw_init(void)
|
||||
{
|
||||
#if LV_USE_SW != 0
|
||||
static lv_style_t sw_indic, sw_bg;
|
||||
lv_style_copy(&sw_indic, &lv_style_pretty_color);
|
||||
sw_indic.body.padding.left = -0;
|
||||
sw_indic.body.padding.right = -0;
|
||||
sw_indic.body.padding.top = -0;
|
||||
sw_indic.body.padding.bottom = -0;
|
||||
sw_indic.body.padding.inner = -0;
|
||||
|
||||
lv_style_copy(&sw_bg, &lv_style_pretty);
|
||||
sw_bg.body.padding.left = -0;
|
||||
sw_bg.body.padding.right = -0;
|
||||
sw_bg.body.padding.top = -0;
|
||||
sw_bg.body.padding.bottom = -0;
|
||||
sw_bg.body.padding.inner = -0;
|
||||
|
||||
theme.style.sw.bg = &sw_bg;
|
||||
theme.style.sw.indic = &sw_indic;
|
||||
theme.style.sw.knob_off = &lv_style_pretty;
|
||||
theme.style.sw.knob_on = &lv_style_pretty;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void lmeter_init(void)
|
||||
{
|
||||
#if LV_USE_LMETER != 0
|
||||
static lv_style_t lmeter;
|
||||
lv_style_copy(&lmeter, &lv_style_pretty_color);
|
||||
lmeter.line.color = lv_color_hex3(0xddd);
|
||||
lmeter.line.width = 2;
|
||||
lmeter.body.main_color = lv_color_mix(lmeter.body.main_color, LV_COLOR_WHITE, LV_OPA_50);
|
||||
lmeter.body.grad_color = lv_color_mix(lmeter.body.grad_color, LV_COLOR_BLACK, LV_OPA_50);
|
||||
|
||||
theme.style.lmeter = &lmeter;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void gauge_init(void)
|
||||
{
|
||||
#if LV_USE_GAUGE != 0
|
||||
static lv_style_t gauge;
|
||||
lv_style_copy(&gauge, theme.style.lmeter);
|
||||
gauge.line.color = theme.style.lmeter->body.grad_color;
|
||||
gauge.line.width = 2;
|
||||
gauge.body.main_color = lv_color_hex3(0x888);
|
||||
gauge.body.grad_color = theme.style.lmeter->body.main_color;
|
||||
gauge.text.color = lv_color_hex3(0x888);
|
||||
gauge.text.font = _font;
|
||||
|
||||
theme.style.gauge = &gauge;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void chart_init(void)
|
||||
{
|
||||
#if LV_USE_CHART
|
||||
|
||||
theme.style.chart = &lv_style_pretty;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void cb_init(void)
|
||||
{
|
||||
#if LV_USE_CB != 0
|
||||
|
||||
theme.style.cb.bg = &lv_style_transp;
|
||||
theme.style.cb.box.rel = &lv_style_pretty;
|
||||
theme.style.cb.box.pr = &lv_style_btn_pr;
|
||||
theme.style.cb.box.tgl_rel = &lv_style_btn_tgl_rel;
|
||||
theme.style.cb.box.tgl_pr = &lv_style_btn_tgl_pr;
|
||||
theme.style.cb.box.ina = &lv_style_btn_ina;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void btnm_init(void)
|
||||
{
|
||||
#if LV_USE_BTNM
|
||||
|
||||
theme.style.btnm.bg = &lv_style_pretty;
|
||||
theme.style.btnm.btn.rel = &lv_style_btn_rel;
|
||||
theme.style.btnm.btn.pr = &lv_style_btn_pr;
|
||||
theme.style.btnm.btn.tgl_rel = &lv_style_btn_tgl_rel;
|
||||
theme.style.btnm.btn.tgl_pr = &lv_style_btn_tgl_pr;
|
||||
theme.style.btnm.btn.ina = &lv_style_btn_ina;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void kb_init(void)
|
||||
{
|
||||
#if LV_USE_KB
|
||||
|
||||
theme.style.kb.bg = &lv_style_pretty;
|
||||
theme.style.kb.btn.rel = &lv_style_btn_rel;
|
||||
theme.style.kb.btn.pr = &lv_style_btn_pr;
|
||||
theme.style.kb.btn.tgl_rel = &lv_style_btn_tgl_rel;
|
||||
theme.style.kb.btn.tgl_pr = &lv_style_btn_tgl_pr;
|
||||
theme.style.kb.btn.ina = &lv_style_btn_ina;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void mbox_init(void)
|
||||
{
|
||||
#if LV_USE_MBOX
|
||||
|
||||
theme.style.mbox.bg = &lv_style_pretty;
|
||||
theme.style.mbox.btn.bg = &lv_style_transp;
|
||||
theme.style.mbox.btn.rel = &lv_style_btn_rel;
|
||||
theme.style.mbox.btn.pr = &lv_style_btn_tgl_pr;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void page_init(void)
|
||||
{
|
||||
#if LV_USE_PAGE
|
||||
|
||||
theme.style.page.bg = &lv_style_pretty;
|
||||
theme.style.page.scrl = &lv_style_transp_tight;
|
||||
theme.style.page.sb = &sb;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ta_init(void)
|
||||
{
|
||||
#if LV_USE_TA
|
||||
|
||||
theme.style.ta.area = &lv_style_pretty;
|
||||
theme.style.ta.oneline = &lv_style_pretty;
|
||||
theme.style.ta.cursor = NULL;
|
||||
theme.style.ta.sb = &sb;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void list_init(void)
|
||||
{
|
||||
#if LV_USE_LIST != 0
|
||||
|
||||
theme.style.list.bg = &lv_style_pretty;
|
||||
theme.style.list.scrl = &lv_style_transp_fit;
|
||||
theme.style.list.sb = &sb;
|
||||
theme.style.list.btn.rel = &lv_style_btn_rel;
|
||||
theme.style.list.btn.pr = &lv_style_btn_pr;
|
||||
theme.style.list.btn.tgl_rel = &lv_style_btn_tgl_rel;
|
||||
theme.style.list.btn.tgl_pr = &lv_style_btn_tgl_pr;
|
||||
theme.style.list.btn.ina = &lv_style_btn_ina;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ddlist_init(void)
|
||||
{
|
||||
#if LV_USE_DDLIST != 0
|
||||
|
||||
theme.style.ddlist.bg = &lv_style_pretty;
|
||||
theme.style.ddlist.sel = &lv_style_plain_color;
|
||||
theme.style.ddlist.sb = &sb;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void roller_init(void)
|
||||
{
|
||||
#if LV_USE_ROLLER != 0
|
||||
|
||||
theme.style.roller.bg = &lv_style_pretty;
|
||||
theme.style.roller.sel = &lv_style_plain_color;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void tabview_init(void)
|
||||
{
|
||||
#if LV_USE_TABVIEW != 0
|
||||
|
||||
theme.style.tabview.bg = &plain_bordered;
|
||||
theme.style.tabview.indic = &lv_style_plain_color;
|
||||
theme.style.tabview.btn.bg = &lv_style_transp;
|
||||
theme.style.tabview.btn.rel = &lv_style_btn_rel;
|
||||
theme.style.tabview.btn.pr = &lv_style_btn_pr;
|
||||
theme.style.tabview.btn.tgl_rel = &lv_style_btn_tgl_rel;
|
||||
theme.style.tabview.btn.tgl_pr = &lv_style_btn_tgl_pr;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void table_init(void)
|
||||
{
|
||||
#if LV_USE_TABLE != 0
|
||||
theme.style.table.bg = &lv_style_transp_tight;
|
||||
theme.style.table.cell = &lv_style_plain;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void win_init(void)
|
||||
{
|
||||
#if LV_USE_WIN != 0
|
||||
|
||||
theme.style.win.bg = &plain_bordered;
|
||||
theme.style.win.sb = &sb;
|
||||
theme.style.win.header = &lv_style_plain_color;
|
||||
theme.style.win.content = &lv_style_transp;
|
||||
theme.style.win.btn.rel = &lv_style_btn_rel;
|
||||
theme.style.win.btn.pr = &lv_style_btn_pr;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if LV_USE_GROUP
|
||||
|
||||
static void style_mod(lv_group_t * group, lv_style_t * style)
|
||||
{
|
||||
(void)group; /*Unused*/
|
||||
#if LV_COLOR_DEPTH != 1
|
||||
/*Make the style to be a little bit orange*/
|
||||
style->body.border.opa = LV_OPA_COVER;
|
||||
style->body.border.color = LV_COLOR_ORANGE;
|
||||
|
||||
/*If not empty or has border then emphasis the border*/
|
||||
if(style->body.opa != LV_OPA_TRANSP || style->body.border.width != 0) style->body.border.width = LV_DPI / 20;
|
||||
|
||||
style->body.main_color = lv_color_mix(style->body.main_color, LV_COLOR_ORANGE, LV_OPA_70);
|
||||
style->body.grad_color = lv_color_mix(style->body.grad_color, LV_COLOR_ORANGE, LV_OPA_70);
|
||||
style->body.shadow.color = lv_color_mix(style->body.shadow.color, LV_COLOR_ORANGE, LV_OPA_60);
|
||||
|
||||
style->text.color = lv_color_mix(style->text.color, LV_COLOR_ORANGE, LV_OPA_70);
|
||||
#else
|
||||
style->body.border.opa = LV_OPA_COVER;
|
||||
style->body.border.color = LV_COLOR_BLACK;
|
||||
style->body.border.width = 2;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void style_mod_edit(lv_group_t * group, lv_style_t * style)
|
||||
{
|
||||
(void)group; /*Unused*/
|
||||
#if LV_COLOR_DEPTH != 1
|
||||
/*Make the style to be a little bit orange*/
|
||||
style->body.border.opa = LV_OPA_COVER;
|
||||
style->body.border.color = LV_COLOR_GREEN;
|
||||
|
||||
/*If not empty or has border then emphasis the border*/
|
||||
if(style->body.opa != LV_OPA_TRANSP || style->body.border.width != 0) style->body.border.width = LV_DPI / 20;
|
||||
|
||||
style->body.main_color = lv_color_mix(style->body.main_color, LV_COLOR_GREEN, LV_OPA_70);
|
||||
style->body.grad_color = lv_color_mix(style->body.grad_color, LV_COLOR_GREEN, LV_OPA_70);
|
||||
style->body.shadow.color = lv_color_mix(style->body.shadow.color, LV_COLOR_GREEN, LV_OPA_60);
|
||||
|
||||
style->text.color = lv_color_mix(style->text.color, LV_COLOR_GREEN, LV_OPA_70);
|
||||
#else
|
||||
style->body.border.opa = LV_OPA_COVER;
|
||||
style->body.border.color = LV_COLOR_BLACK;
|
||||
style->body.border.width = 3;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /*LV_USE_GROUP*/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Initialize the default theme
|
||||
* @param hue [0..360] hue value from HSV color space to define the theme's base color
|
||||
* @param font pointer to a font (NULL to use the default)
|
||||
* @return pointer to the initialized theme
|
||||
*/
|
||||
lv_theme_t * lv_theme_hasp_init(uint16_t hue, lv_font_t * font)
|
||||
{
|
||||
if(font == NULL) font = LV_FONT_DEFAULT;
|
||||
|
||||
_hue = hue;
|
||||
_font = font;
|
||||
|
||||
/*For backward compatibility initialize all theme elements with a default style */
|
||||
uint16_t i;
|
||||
lv_style_t ** style_p = (lv_style_t **)&theme.style;
|
||||
for(i = 0; i < LV_THEME_STYLE_COUNT; i++) {
|
||||
*style_p = &def;
|
||||
style_p++;
|
||||
}
|
||||
|
||||
basic_init();
|
||||
btn_init();
|
||||
label_init();
|
||||
img_init();
|
||||
line_init();
|
||||
led_init();
|
||||
bar_init();
|
||||
slider_init();
|
||||
sw_init();
|
||||
lmeter_init();
|
||||
gauge_init();
|
||||
chart_init();
|
||||
cb_init();
|
||||
btnm_init();
|
||||
kb_init();
|
||||
mbox_init();
|
||||
page_init();
|
||||
ta_init();
|
||||
list_init();
|
||||
ddlist_init();
|
||||
roller_init();
|
||||
tabview_init();
|
||||
table_init();
|
||||
win_init();
|
||||
|
||||
#if LV_USE_GROUP
|
||||
theme.group.style_mod_xcb = style_mod;
|
||||
theme.group.style_mod_edit_xcb = style_mod_edit;
|
||||
#endif
|
||||
|
||||
return &theme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pointer to the theme
|
||||
* @return pointer to the theme
|
||||
*/
|
||||
lv_theme_t * lv_theme_get_hasp(void)
|
||||
{
|
||||
return &theme;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
#endif
|
57
src/lv_theme_hasp.h
Normal file
57
src/lv_theme_hasp.h
Normal file
@ -0,0 +1,57 @@
|
||||
/**
|
||||
* @file lv_theme_default.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_THEME_HASP_H
|
||||
#define LV_THEME_HASP_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lvgl.h"
|
||||
#include "../lib/lvgl/src/lv_conf_internal.h"
|
||||
|
||||
#if LV_USE_THEME_HASP
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Initialize the default theme
|
||||
* @param hue [0..360] hue value from HSV color space to define the theme's base color
|
||||
* @param font pointer to a font (NULL to use the default)
|
||||
* @return pointer to the initialized theme
|
||||
*/
|
||||
lv_theme_t * lv_theme_hasp_init(uint16_t hue, lv_font_t * font);
|
||||
|
||||
/**
|
||||
* Get a pointer to the theme
|
||||
* @return pointer to the theme
|
||||
*/
|
||||
lv_theme_t * lv_theme_get_hasp(void);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /*LV_THEME_TEMPL_H*/
|
134
src/main.cpp
Normal file
134
src/main.cpp
Normal file
@ -0,0 +1,134 @@
|
||||
|
||||
#include "ArduinoJson.h"
|
||||
#include "TFT_eSPI.h"
|
||||
|
||||
#include "hasp_conf.h"
|
||||
|
||||
#include "hasp_debug.h"
|
||||
#include "hasp_eeprom.h"
|
||||
#include "hasp_spiffs.h"
|
||||
#include "hasp_config.h"
|
||||
#include "hasp_tft.h"
|
||||
#include "hasp_gui.h"
|
||||
//#include "hasp_ota.h"
|
||||
#include "hasp.h"
|
||||
|
||||
#if LV_USE_HASP_SPIFFS
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
#include "SPIFFS.h"
|
||||
#endif
|
||||
#include <FS.h> // Include the SPIFFS library
|
||||
#endif
|
||||
|
||||
#if LV_USE_HASP_WIFI
|
||||
#include "hasp_wifi.h"
|
||||
#endif
|
||||
|
||||
#if LV_USE_HASP_MQTT
|
||||
#include "hasp_mqtt.h"
|
||||
#endif
|
||||
|
||||
#if LV_USE_HASP_HTTP
|
||||
#include "hasp_http.h"
|
||||
#endif
|
||||
|
||||
bool isConnected;
|
||||
|
||||
void setup()
|
||||
{
|
||||
/* Init Storage */
|
||||
eepromSetup();
|
||||
#if LV_USE_HASP_SPIFFS
|
||||
// spiffsSetup();
|
||||
#endif
|
||||
|
||||
/* Read Config File */
|
||||
DynamicJsonDocument settings(1024);
|
||||
// configGetConfig(doc);
|
||||
// JsonObject settings = doc.as<JsonObject>();
|
||||
configSetup(settings);
|
||||
|
||||
if(!settings[F("pins")][F("TFT_BCKL")].isNull()) {
|
||||
int8_t pin = settings[F("pins")][F("TFT_BCKL")].as<int8_t>();
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
if(pin >= 0)
|
||||
// configure LED PWM functionalitites
|
||||
ledcSetup(0, 5000, 10);
|
||||
// attach the channel to the GPIO to be controlled
|
||||
ledcAttachPin(pin, 0);
|
||||
#else
|
||||
pinMode(pin, OUTPUT);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if LV_USE_HASP_SDCARD
|
||||
sdcardSetup();
|
||||
#endif
|
||||
|
||||
/* Init Graphics */
|
||||
TFT_eSPI screen = TFT_eSPI();
|
||||
tftSetup(screen, settings[F("tft")]);
|
||||
guiSetup(screen, settings[F("gui")]);
|
||||
|
||||
/* Init GUI Application */
|
||||
haspSetup(settings[F("hasp")]);
|
||||
|
||||
/* Init Network Services */
|
||||
#if LV_USE_HASP_WIFI
|
||||
wifiSetup(settings[F("wifi")]);
|
||||
|
||||
#if LV_USE_HASP_MQTT
|
||||
mqttSetup(settings[F("mqtt")]);
|
||||
#endif
|
||||
|
||||
#if LV_USE_HASP_MDNS
|
||||
mdnsSetup(settings[F("mdns")]);
|
||||
#endif
|
||||
|
||||
#if LV_USE_HASP_HTTP
|
||||
httpSetup(settings[F("http")]);
|
||||
#endif
|
||||
|
||||
// otaSetup(settings[F("ota")]);
|
||||
#endif
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
/* Storage Loops */
|
||||
// eepromLoop();
|
||||
// spiffsLoop();
|
||||
#if LV_USE_HASP_SDCARD
|
||||
// sdcardLoop();
|
||||
#endif
|
||||
|
||||
configLoop();
|
||||
|
||||
/* Graphics Loops */
|
||||
// tftLoop();
|
||||
guiLoop();
|
||||
|
||||
/* Application Loops */
|
||||
// haspLoop();
|
||||
|
||||
/* Network Services Loops */
|
||||
#if LV_USE_HASP_WIFI > 0
|
||||
isConnected = wifiLoop();
|
||||
|
||||
#if LV_USE_HASP_MQTT > 0
|
||||
mqttLoop(isConnected);
|
||||
#endif
|
||||
|
||||
#if LV_USE_HASP_HTTP > 0
|
||||
httpLoop(isConnected);
|
||||
#endif
|
||||
|
||||
#if LV_USE_HASP_MDNS > 0
|
||||
mdnsLoop(wifiIsConnected);
|
||||
#endif
|
||||
|
||||
// otaLoop();
|
||||
#endif
|
||||
|
||||
delay(5);
|
||||
}
|
33
src/user_config_override-template.h
Normal file
33
src/user_config_override-template.h
Normal file
@ -0,0 +1,33 @@
|
||||
/***************************************************
|
||||
WiFi Settings
|
||||
**************************************************/
|
||||
#define WIFI_SSID ""
|
||||
#define WIFI_PASSW ""
|
||||
|
||||
/***************************************************
|
||||
MQTT Settings
|
||||
**************************************************/
|
||||
#define MQTT_HOST ""
|
||||
#define MQTT_PORT 1883
|
||||
#define MQTT_USER ""
|
||||
#define MQTT_PASSW ""
|
||||
#define MQTT_TOPIC "plates"
|
||||
#define MQTT_CLIENTID "plate01"
|
||||
|
||||
#define MQTT_TELEPERIOD 60000
|
||||
#define MQTT_STATEPERIOD 300000
|
||||
|
||||
/***************************************************
|
||||
* Server Settings
|
||||
**************************************************/
|
||||
#define OTA_HOSTNAME ""
|
||||
#define OTA_SERVER ""
|
||||
#define OTA_PORT 80
|
||||
#define OTA_URL ""
|
||||
|
||||
/***************************************************
|
||||
* Syslog Settings
|
||||
**************************************************/
|
||||
#define SYSLOG_SERVER ""
|
||||
#define SYSLOG_PORT 514
|
||||
#define APP_NAME "HASP"
|
Loading…
x
Reference in New Issue
Block a user