You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

368 lines
10 KiB

#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();
}