6 changed files with 380 additions and 369 deletions
@ -0,0 +1,368 @@
|
||||
#include "MqtriggerApp.h" |
||||
#include "HttpHandlers.h" |
||||
|
||||
#include "nx/software/SystemSettings.h" |
||||
#include "nx/software/SystemSettingsApi.h" |
||||
#include "nx/software/AppSettings.h" |
||||
#include "nx/software/AppSettingsApi.h" |
||||
#include "nx/software/CommandParser.h" |
||||
#include "nx/firmware/Storage.h" |
||||
#include "nx/firmware/Wifi.h" |
||||
#include "nx/firmware/MqTriggerHttpServer.h" |
||||
#include "nx/firmware/MqttClient.h" |
||||
#include "nx/firmware/SntpClient.h" |
||||
#include "nx/firmware/StatusIndicator.h" |
||||
#include "nx/firmware/Restarter.h" |
||||
#include "nx/firmware/GpioTrigger.h" |
||||
#include "nx/firmware/Buttons.h" |
||||
|
||||
#include <freertos/FreeRTOS.h> |
||||
#include <freertos/task.h> |
||||
#include <esp_system.h> |
||||
#include <esp_log.h> |
||||
|
||||
#include <string.h> |
||||
|
||||
#define TAG "APP" |
||||
|
||||
#define SNTP_RETRIES 8 |
||||
#define API_QOS 2 |
||||
#define HB_QOS 1 |
||||
|
||||
#define BUTTON_SERVICE_GPIO_NUM 15 |
||||
#define BUTTON_SERVICE_ID 1 |
||||
#define SERVICE_BUTTON_DEFAULT_RESET_HOLD_SEC 1500 |
||||
|
||||
static const char* DEVICE_NAME_PREFIX = "mqt-"; |
||||
|
||||
static const uint8_t TRIGGER_GPIO[] = {27, 26, 25, 33}; |
||||
|
||||
// ------
|
||||
|
||||
static void restoreDefaultsOnStartupButtonHold(void); |
||||
static void startWifi(void); |
||||
static void onCommandRequest(const uint8_t* content, size_t ctLen, |
||||
const char** response, size_t* respLen); |
||||
static void onWifiConnected(void); |
||||
static void onWifiError(void); |
||||
static void onAppSettingsUpdate(void); |
||||
static void onMqttConnected(); |
||||
static void onMqttDisconnected(); |
||||
static void onMqttError(); |
||||
static void onMqttMessage(const char* msg); |
||||
static void hbTask(void*); |
||||
static void connWatchdogTask(void*); |
||||
|
||||
static SystemSettings* systemSettings = NULL; |
||||
static AppSettings* appSettings = NULL; |
||||
static WifiSettings wifiSettings; |
||||
static MqttSettings mqttSettings; |
||||
|
||||
static bool wifiStarted = false; |
||||
static bool mqttStarted = false; |
||||
static bool serviceMode = false; |
||||
|
||||
static const MqTriggerHttpCallbacks httpCallbacks = { |
||||
.getRoot = rootHandler, |
||||
.getJs = jsHandler, |
||||
.getCss = cssHandler, |
||||
.getSysSetForm = sysFormHandler, |
||||
.getAppSetForm = appFormHandler, |
||||
.getSysSet = nxApiGetSystemSettings, |
||||
.postSysSet = nxApiUpdateSystemSettings, |
||||
.getAppSet = nxApiGetAppSettings, |
||||
.postAppSet = nxApiUpdateAppSettings, |
||||
.postCmd = onCommandRequest |
||||
}; |
||||
|
||||
static Button buttons[] = { |
||||
{ |
||||
.id = BUTTON_SERVICE_ID, |
||||
.btnGpio = BUTTON_SERVICE_GPIO_NUM, |
||||
.pullGpio = 0, |
||||
.inverted = false, |
||||
.isPressed = false, |
||||
.isBouncing = false, |
||||
.debounceMs = 50 |
||||
} |
||||
}; |
||||
|
||||
|
||||
static void restoreDefaultsOnStartupButtonHold(void) |
||||
{ |
||||
vTaskDelay(1000 / portTICK_PERIOD_MS); // possible debouncing capacitor wait
|
||||
|
||||
if (nxIsButtonPressed(BUTTON_SERVICE_ID)) { |
||||
ESP_LOGI(TAG, "SERVICE BUTTON IS PRESSED"); |
||||
} |
||||
else { |
||||
ESP_LOGI(TAG, "SERVICE BUTTON IS NOT PRESSED"); |
||||
} |
||||
|
||||
const uint16_t stepMs = 500; |
||||
uint16_t steps = (SERVICE_BUTTON_DEFAULT_RESET_HOLD_SEC * 1000) / stepMs; |
||||
serviceMode = nxIsButtonPressed(BUTTON_SERVICE_ID); |
||||
|
||||
while (nxIsButtonPressed(BUTTON_SERVICE_ID)) { |
||||
if (steps == 0) { |
||||
ESP_LOGW(TAG, "RESTORING DEFAULT SYSTEM AND DISPLAY SETTINGS"); |
||||
nxRestoreAppDefaultSettings(); |
||||
nxRestoreSystemDefaultSettings(); |
||||
vTaskDelay(1000 / portTICK_PERIOD_MS); |
||||
esp_restart(); |
||||
} |
||||
ESP_LOGW(TAG, "HOLDING SERVICE BUTTON [COUNTDOWN: %i]", steps); |
||||
vTaskDelay(stepMs / portTICK_PERIOD_MS); |
||||
steps -= 1; |
||||
} |
||||
} |
||||
|
||||
static void startWifi(void) |
||||
{ |
||||
wifiSettings = (struct WifiSettings){ |
||||
.wname = systemSettings->wifiSsid, |
||||
.wpass = systemSettings->wifiPassword, |
||||
.devicePrefix = DEVICE_NAME_PREFIX, |
||||
.usePowerSave = &(systemSettings->wifiPowerSave), |
||||
.useStaticAddr = &(systemSettings->useStaticAddr), |
||||
.ip4addr = systemSettings->ip4addr, |
||||
.ip4gw = systemSettings->ip4gw, |
||||
.ip4mask = systemSettings->ip4mask, |
||||
.dns = systemSettings->dnsAddr |
||||
}; |
||||
|
||||
wifiSettings.mode = serviceMode ? NX_WIFI_MODE_AP : NX_WIFI_MODE_STA; |
||||
|
||||
ESP_LOGI(TAG, "Initializing WiFi"); |
||||
if (!nxInitWifi(&wifiSettings)) { |
||||
nxUpdateStatus(STATUS_SYSTEM_ERROR); |
||||
} |
||||
|
||||
nxEnablePowerSavingStatus(systemSettings->wifiPowerSave); |
||||
} |
||||
|
||||
static void handleCommand(const char* cmd, const char* args[], uint8_t argc) |
||||
{ |
||||
ESP_LOGI(TAG, "Handling command %s with %i args", cmd, argc); |
||||
if (strcmp(cmd, "trigger") == 0 && argc >= 2) { |
||||
uint32_t durationMs = atoi(args[1]); |
||||
nxTriggerGpio(atoi(args[0]), durationMs); |
||||
} |
||||
else if (strcmp(cmd, "on") == 0 && argc >= 1) { |
||||
nxUpdateTriggerGpio(atoi(args[0]), 1); |
||||
} |
||||
else if (strcmp(cmd, "off") == 0 && argc >= 1) { |
||||
nxUpdateTriggerGpio(atoi(args[0]), 0); |
||||
} |
||||
else if (strcmp(cmd, "setsys") == 0 && argc >= 1) { |
||||
nxApiUpdateSystemSettings((uint8_t*)args[0], strlen(args[0]), NULL, NULL); |
||||
} |
||||
else if (strcmp(cmd, "setapp") == 0 && argc >= 1) { |
||||
nxApiUpdateAppSettings((uint8_t*)args[0], strlen(args[0]), NULL, NULL); |
||||
} |
||||
else if (strcmp(cmd, "reboot") == 0) { |
||||
esp_restart(); |
||||
} |
||||
else { |
||||
ESP_LOGW(TAG, "Unknown command or missing args: %s", cmd); |
||||
} |
||||
} |
||||
|
||||
static void onCommandRequest(const uint8_t* content, size_t ctLen, |
||||
const char** response, size_t* respLen) |
||||
{ |
||||
nxUpdateStatus(STATUS_OK_WORKING); |
||||
nxParseCommandString((const char*)content, handleCommand); |
||||
} |
||||
|
||||
static void onWifiConnected(void) |
||||
{ |
||||
nxUpdateStatus(STATUS_OK); |
||||
|
||||
if (wifiStarted) { |
||||
return; |
||||
} |
||||
|
||||
wifiStarted = true; |
||||
systemSettings->deviceName = nxGetWifiDeviceName(); |
||||
|
||||
nxInitSntpClient(SNTP_RETRIES, systemSettings->sntpAddr, systemSettings->tzEnv); |
||||
|
||||
strcpy(mqttSettings.brokerAddr, appSettings->mqttHost); |
||||
strcpy(mqttSettings.apiTopic, appSettings->mqttApiUri); |
||||
strcpy(mqttSettings.hbTopic, appSettings->mqttHbUri); |
||||
strcpy(mqttSettings.user, appSettings->mqttUser); |
||||
|
||||
strcpy(mqttSettings.password, appSettings->mqttPassword); |
||||
strcpy(mqttSettings.clientId, appSettings->overrideDevName |
||||
? appSettings->customDevName |
||||
: systemSettings->deviceName); |
||||
|
||||
mqttSettings.hbIntervalSec = appSettings->mqttHbIntervalSec; |
||||
mqttSettings.caCrt = appSettings->mqttUseTls ? appSettings->caCert : NULL; |
||||
mqttSettings.messageCb = onMqttMessage; |
||||
mqttSettings.connectedCb = onMqttConnected; |
||||
mqttSettings.disconnectedCb = onMqttDisconnected; |
||||
mqttSettings.errorCb = onMqttError; |
||||
|
||||
if (!nxStartMqttClient(&mqttSettings)) { |
||||
onMqttError(); |
||||
} |
||||
|
||||
nxSetMqTriggerHttpCallbacks(&httpCallbacks); |
||||
nxStartMqTriggerHttpServer(); |
||||
} |
||||
|
||||
static void onAppSettingsUpdate(void) |
||||
{ |
||||
ESP_LOGI(TAG, "App settings updated"); |
||||
nxUpdateStatus(STATUS_OK_WORKING); |
||||
} |
||||
|
||||
static void onMqttMessage(const char* msg) |
||||
{ |
||||
ESP_LOGI(TAG, "MQTT MESSAGE RECEIVED: %s", msg); |
||||
nxUpdateStatus(STATUS_OK_WORKING); |
||||
nxParseCommandString(msg, handleCommand); |
||||
} |
||||
|
||||
static void onWifiError() |
||||
{ |
||||
ESP_LOGE(TAG, "WiFi ERROR"); |
||||
nxUpdateStatus(STATUS_SYSTEM_ERROR); |
||||
} |
||||
|
||||
static void onMqttConnected() |
||||
{ |
||||
ESP_LOGI(TAG, "MQTT CONNECTED"); |
||||
|
||||
if (!mqttStarted) { |
||||
xTaskCreate(&hbTask, "hb_task", 2048, NULL, 1, NULL); |
||||
mqttStarted = true; |
||||
} |
||||
|
||||
// (re)subscribe
|
||||
if (nxMqttSubscribe(appSettings->mqttApiUri, API_QOS)) { |
||||
nxUpdateStatus(STATUS_OK); |
||||
} |
||||
} |
||||
|
||||
static void onMqttDisconnected() |
||||
{ |
||||
ESP_LOGW(TAG, "MQTT DISCONNECTED"); |
||||
nxUpdateStatus(STATUS_APP_ERROR); |
||||
} |
||||
|
||||
static void onMqttError() |
||||
{ |
||||
ESP_LOGE(TAG, "MQTT ERROR"); |
||||
nxUpdateStatus(STATUS_APP_ERROR); |
||||
} |
||||
|
||||
static void hbTask(void* param) |
||||
{ |
||||
// UNUSED(param);
|
||||
ESP_LOGI(TAG, "Starting MQTT heartbeat task"); |
||||
|
||||
ESP_LOGI(TAG, "hbIntervalSec: %d", appSettings->mqttHbIntervalSec); |
||||
|
||||
if (appSettings->mqttHbIntervalSec < 1) { |
||||
ESP_LOGI(TAG, "Heartbeat interval < 1 sec, skipping"); |
||||
vTaskDelete(NULL); |
||||
return; |
||||
} |
||||
|
||||
while (1) { |
||||
if (nxMqttIsConnected()) { |
||||
ESP_LOGI(TAG, "Sending MQTT heartbeat"); |
||||
|
||||
char timeStr[DT_FORMAT_LEN]; |
||||
nxGetTimeStr(timeStr); |
||||
char hbMessage[DT_FORMAT_LEN + strlen(nxGetWifiDeviceName()) + 2]; |
||||
strcpy(hbMessage, nxGetWifiDeviceName()); |
||||
strcat(hbMessage, " "); |
||||
strcat(hbMessage, timeStr); |
||||
|
||||
if(nxMqttPublish(appSettings->mqttHbUri, HB_QOS, hbMessage, strlen(hbMessage), 1) < 0) { |
||||
ESP_LOGE(TAG, "Cannot publish heartbeat message"); |
||||
} |
||||
} |
||||
else { |
||||
ESP_LOGW(TAG, "Skipping MQTT heartbeat due to the disconnected client"); |
||||
} |
||||
|
||||
vTaskDelay(appSettings->mqttHbIntervalSec*1000 / portTICK_PERIOD_MS); |
||||
} |
||||
} |
||||
|
||||
static void connWatchdogTask(void* param) |
||||
{ |
||||
if (appSettings->wdogMaxSec == 0) { |
||||
ESP_LOGI(TAG, "Connection watchdog disabled"); |
||||
vTaskDelete(NULL); |
||||
return; |
||||
} |
||||
|
||||
ESP_LOGI(TAG, "Starting connection watchdog task"); |
||||
vTaskDelay(1000 * 20 / portTICK_PERIOD_MS); // wait for initialization
|
||||
|
||||
uint32_t fails = 0; |
||||
const uint32_t WD_DELAY_SEC = 10; |
||||
const uint32_t MAX_FAILS = appSettings->wdogMaxSec/WD_DELAY_SEC; |
||||
|
||||
while (1) { |
||||
if (!nxMqttIsConnected() || !nxIsWifiStaConnected()) { |
||||
ESP_LOGI(TAG, "Connection watchdog: DEVICE NOT CONNECTED TO WIFI/MQTT BROKER"); |
||||
fails += 1; |
||||
if (fails >= MAX_FAILS) { |
||||
ESP_LOGW(TAG, "MAX CONNECTION FAILED CHECKS REACHED, REBOOTING"); |
||||
esp_restart(); |
||||
} |
||||
} |
||||
else if (fails > 0){ |
||||
fails = 0; |
||||
} |
||||
vTaskDelay(1000 / portTICK_PERIOD_MS); |
||||
} |
||||
} |
||||
|
||||
// -----
|
||||
|
||||
void nxStartMqtriggerApp(void) |
||||
{ |
||||
nxInitStatusIndicator(); |
||||
nxUpdateStatus(STATUS_BOOT); |
||||
xTaskCreate(&nxStatusIndicatorTask, "status_task", 2048, NULL, 1, NULL); |
||||
|
||||
nxInitButtons(buttons, sizeof(buttons)/sizeof(buttons[0])); |
||||
nxInitStorage(); |
||||
|
||||
// uncomment to force default NVS initialization for development
|
||||
// uint8_t zero = 0;
|
||||
// nxStorageWrite("ledinit", &zero, 1);
|
||||
// nxStorageWrite("sysinit", &zero, 1);
|
||||
|
||||
nxInitSystemSettings(nxStorageWrite, nxStorageRead); |
||||
nxInitAppSettings(nxStorageWrite, nxStorageRead, onAppSettingsUpdate); |
||||
|
||||
systemSettings = nxGetSystemSettings(); |
||||
appSettings = nxGetAppSettings(); |
||||
|
||||
restoreDefaultsOnStartupButtonHold(); |
||||
|
||||
nxInitGpioTrigger(TRIGGER_GPIO, sizeof(TRIGGER_GPIO)); |
||||
|
||||
// uncomment to force WiFi settings for development
|
||||
// strcpy(systemSettings->wifiSsid, "myssid");
|
||||
// strcpy(systemSettings->wifiPassword, "password");
|
||||
// systemSettings->useStaticAddr = false;
|
||||
|
||||
nxStartRestarter(systemSettings->rsSchedule, systemSettings->tzEnv); |
||||
xTaskCreate(&connWatchdogTask, "conn_watchdog_task", 2048, NULL, 1, NULL); |
||||
|
||||
nxSetWifiConnectedCallback(onWifiConnected); |
||||
nxSetWifiErrorCallback(onWifiError); |
||||
startWifi(); |
||||
|
||||
} |
||||
|
||||
@ -0,0 +1,6 @@
|
||||
#ifndef MAIN_MQTRIGGERAPP_H_ |
||||
#define MAIN_MQTRIGGERAPP_H_ |
||||
|
||||
void nxStartMqtriggerApp(void); |
||||
|
||||
#endif /* MAIN_MQTRIGGERAPP_H_ */ |
||||
@ -1 +1 @@
|
||||
<meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon type=image/png href=data:image/png,%89PNG%0D%0A%1A%0A><link rel=stylesheet href=index.css><script src=index.js></script><div class=.float-left>MqTrigger v1.1</div><header class="sticky row"><div class="container nav-bar"><ul><li><a id=app-btn href=# role=button class=nav-btn data-dst=/app/form>Application</a><li><a href=# role=button class=nav-btn data-dst=/sys/form>System</a></ul></div></header><div class=container><div class=col-md-offset-1 id=content><h1>Loading</h1><p>Loading content... Please make sure that JavaScript is enabled.</div></div><hr><footer><div class=container><p>Copyright© nixlab.in 2022</div></footer><script>initNavButtons();document.getElementById('app-btn').click();</script> |
||||
<meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon type=image/png href=data:image/png,%89PNG%0D%0A%1A%0A><link rel=stylesheet href=index.css><script src=index.js></script><title>MqTrigger web panel</title><header class="sticky row"><div class="container nav-bar"><ul><li><a id=app-btn href=# role=button class=nav-btn data-dst=/app/form>Application</a><li><a href=# role=button class=nav-btn data-dst=/sys/form>System</a></ul></div></header><div class=container><div class=col-md-offset-1 id=content><h1>Loading</h1><p>Loading content... Please make sure that JavaScript is enabled.</div></div><hr><footer><div class=container><p>MqTrigger 2022</div></footer><script>initNavButtons();document.getElementById('app-btn').click();</script> |
||||
Loading…
Reference in new issue