Browse Source

Added status indicator; added system restarter

master
chodak166 4 years ago
parent
commit
e2fc3806f7
  1. 2
      components/firmware/CMakeLists.txt
  2. 22
      components/firmware/include/nx/firmware/HwConfig.h
  3. 6
      components/firmware/include/nx/firmware/Restarter.h
  4. 20
      components/firmware/include/nx/firmware/StatusIndicator.h
  5. 3
      components/firmware/src/MqTriggerHttpServer.c
  6. 95
      components/firmware/src/Restarter.c
  7. 212
      components/firmware/src/StatusIndicator.c
  8. 2
      components/software/include/nx/software/SystemSettings.h
  9. 2
      components/software/src/AppSettings.c
  10. 5
      components/software/src/SystemSettings.c
  11. 10
      components/software/src/SystemSettingsApi.c
  12. 49
      main/Main.c
  13. 43
      main/static/index.js
  14. 15
      main/static/min/index.js
  15. 21
      main/static/min/sys.html
  16. 79
      main/static/sys.html

2
components/firmware/CMakeLists.txt

@ -7,6 +7,8 @@ idf_component_register(
src/MqttClient.c src/MqttClient.c
src/Buttons.c src/Buttons.c
src/SntpClient.c src/SntpClient.c
src/StatusIndicator.c
src/Restarter.c
INCLUDE_DIRS ./include INCLUDE_DIRS ./include
PRIV_INCLUDE_DIRS ./src PRIV_INCLUDE_DIRS ./src

22
components/firmware/include/nx/firmware/HwConfig.h

@ -0,0 +1,22 @@
#ifndef COMPONENTS_FIRMWARE_INCLUDE_NX_FIRMWARE_HWCONFIG_H_
#define COMPONENTS_FIRMWARE_INCLUDE_NX_FIRMWARE_HWCONFIG_H_
#define DEVICE_ID "dev01"
#define GPIO_SERVICE_BTN 15
#define GPIO_TRIGGER_OUT_1 27
#define GPIO_TRIGGER_OUT_2 26
#define GPIO_TRIGGER_OUT_3 25
#define GPIO_TRIGGER_OUT_4 33
#define USED_OUTPUTS_COUNT 4
#define GPIO_LED_RED 14
#define GPIO_LED_GREEN 12
#define GPIO_LED_BLUE 13
#define LED_LEVEL_ON 1
#define LED_LEVEL_OFF 0
#endif /* COMPONENTS_FIRMWARE_INCLUDE_NX_FIRMWARE_HWCONFIG_H_ */

6
components/firmware/include/nx/firmware/Restarter.h

@ -0,0 +1,6 @@
#ifndef COMPONENTS_FIRMWARE_INCLUDE_NX_FIRMWARE_RESTARTER_H_
#define COMPONENTS_FIRMWARE_INCLUDE_NX_FIRMWARE_RESTARTER_H_
void nxStartRestarter(const char* schedule, const char* timezone);
#endif /* COMPONENTS_FIRMWARE_INCLUDE_NX_FIRMWARE_RESTARTER_H_ */

20
components/firmware/include/nx/firmware/StatusIndicator.h

@ -0,0 +1,20 @@
#ifndef COMPONENTS_FIRMWARE_INCLUDE_NX_FIRMWARE_STATUSINDICATOR_H_
#define COMPONENTS_FIRMWARE_INCLUDE_NX_FIRMWARE_STATUSINDICATOR_H_
#include <stdbool.h>
typedef enum {
STATUS_BOOT = 0, // cont. red
STATUS_OK, // cont. green
STATUS_OK_WORKING, // 10Hz green
STATUS_OK_ALT, // 1Hz blue (e.g. service mode)
STATUS_SYSTEM_ERROR, // 1Hz red (e.g. wifi not connected)
STATUS_APP_ERROR // 2 x 2Hz + 1s off (e.g. mqtt not connected)
} HwStatus;
void nxInitStatusIndicator();
void nxUpdateStatus(HwStatus status);
void nxStatusIndicatorTask(void*);
void nxEnablePowerSavingStatus(bool enable);
#endif /* COMPONENTS_FIRMWARE_INCLUDE_NX_FIRMWARE_STATUSINDICATOR_H_ */

3
components/firmware/src/MqTriggerHttpServer.c

@ -54,6 +54,7 @@ static httpd_uri_t uriReboot = {
.handler = uriRebootHandler .handler = uriRebootHandler
}; };
void registerMqTriggerHttpHandlers(void) void registerMqTriggerHttpHandlers(void)
{ {
httpd_register_uri_handler(server, &uriOptions); httpd_register_uri_handler(server, &uriOptions);
@ -79,7 +80,7 @@ bool nxStartMqTriggerHttpServer(void)
// configure server here: // configure server here:
config.server_port = 80; config.server_port = 80;
config.max_uri_handlers = 8; config.max_uri_handlers = 16;
config.uri_match_fn = httpd_uri_match_wildcard; config.uri_match_fn = httpd_uri_match_wildcard;
// ... // ...

95
components/firmware/src/Restarter.c

@ -0,0 +1,95 @@
#include "nx/firmware/Restarter.h"
#include "esp_log.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <time.h>
#include <string.h>
#define TAG "RESTARTER"
#define RESTARTER_CHECK_INTERVAL_SEC 10
#define RESTART_SCHEDULE_SIZE 12
#define RESTART_SCHEDULE_MIN_LEN 7 // 'x x x x'
const char* schedule = "";
const char* tz = "";
// day - days from sunday (0 is sunday), schedule - binary schedule from sunday (1 is sunday, 64 is saturday)
int isDayInSchedule(int day, int schedule)
{
int dayMatch = 1 << day;
int match = (dayMatch & schedule);
return match != 0;
}
void restarterTask(void* param)
{
//UNUSED(param);
ESP_LOGI(TAG, "Starting restarter task");
if (strlen(schedule) < RESTART_SCHEDULE_MIN_LEN) {
ESP_LOGW(TAG, "Closing restarter task due to the incomplete schedule: [%s]", schedule);
vTaskDelete( NULL );
return;
}
long int intervalMs = 1000 * RESTARTER_CHECK_INTERVAL_SEC;
char scheduleLine[RESTART_SCHEDULE_SIZE];
strcpy(scheduleLine, schedule);
int daySchedule = atoi( strtok(scheduleLine, " ") );
int hour = atoi( strtok(NULL, " ") );
int minute = atoi( strtok(NULL, " ") );
int maxUptime = atoi( strtok(NULL, " ") );
ESP_LOGI(TAG, "Restart schedule: %i %i %i %i", daySchedule, hour, minute, maxUptime);
if (daySchedule == 0) {
ESP_LOGI(TAG, "Closing restarter task due to zero days in schedule");
vTaskDelete( NULL );
return;
}
while (1) {
vTaskDelay(intervalMs / portTICK_PERIOD_MS);
int uptimeMin = portTICK_PERIOD_MS * xTaskGetTickCount() / 1000 / 60;
time_t now;
struct tm timeinfo;
time(&now);
// Set timezone to Eastern Standard Time and print local time
setenv("TZ", tz, 1);
tzset();
localtime_r(&now, &timeinfo);
if (uptimeMin < maxUptime) {
continue;
}
if (!isDayInSchedule(timeinfo.tm_wday, daySchedule)) {
continue;
}
if (hour != timeinfo.tm_hour || hour == -1) {
continue;
}
if (minute != timeinfo.tm_min) {
continue;
}
ESP_LOGI(TAG, "all conditions met, restarting...");
esp_restart();
}
}
void nxStartRestarter(const char* scheduleString, const char* timezone)
{
schedule = scheduleString;
tz = timezone;
xTaskCreate(restarterTask, "restarter_task", 2048,
(void*)1, tskIDLE_PRIORITY, NULL);
}

212
components/firmware/src/StatusIndicator.c

@ -0,0 +1,212 @@
#include "nx/firmware/StatusIndicator.h"
#include "nx/firmware/HwConfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include <stdio.h>
#define UNUSED(x) (void)(x)
#define BLINK_GPIO GPIO_LED_GREEN
#define RED_ON gpio_set_level(GPIO_LED_RED, LED_LEVEL_ON)
#define RED_OFF gpio_set_level(GPIO_LED_RED, LED_LEVEL_OFF)
#define GREEN_ON gpio_set_level(GPIO_LED_GREEN, LED_LEVEL_ON)
#define GREEN_OFF gpio_set_level(GPIO_LED_GREEN, LED_LEVEL_OFF)
#define BLUE_ON gpio_set_level(GPIO_LED_BLUE, LED_LEVEL_ON)
#define BLUE_OFF gpio_set_level(GPIO_LED_BLUE, LED_LEVEL_OFF)
#define BLINK_INTERVAL 500/portTICK_PERIOD_MS
static const char* TAG = "STATUS";
static HwStatus prevStatus = STATUS_BOOT;
static HwStatus currentStatus = STATUS_BOOT;
static bool powerSaving = false;
static void statusCycleBoot()
{
GREEN_OFF;
BLUE_OFF;
RED_ON;
vTaskDelay(BLINK_INTERVAL);
}
static void statusCycleOk()
{
GREEN_ON;
BLUE_OFF;
RED_OFF;
vTaskDelay(BLINK_INTERVAL);
}
static void statusCycleOkPowerSave()
{
GREEN_OFF;
BLUE_OFF;
RED_OFF;
const int factor = 5;
int cycles = 20 * factor;
while (--cycles >= 0 && currentStatus == STATUS_OK) {
vTaskDelay(BLINK_INTERVAL / factor);
}
if (currentStatus == STATUS_OK) {
GREEN_ON;
vTaskDelay(BLINK_INTERVAL / 5);
}
}
static void statusCycleOkWorking()
{
int cycles = 3;
while (--cycles >= 0) {
GREEN_ON;
BLUE_OFF;
RED_OFF;
vTaskDelay(BLINK_INTERVAL/10);
GREEN_OFF;
vTaskDelay(BLINK_INTERVAL/10);
}
currentStatus = prevStatus;
}
static void statusCycleBtEnabled()
{
GREEN_OFF;
BLUE_ON;
RED_OFF;
vTaskDelay(BLINK_INTERVAL);
BLUE_OFF;
vTaskDelay(BLINK_INTERVAL);
}
static void statusCycleWifiError()
{
GREEN_OFF;
BLUE_OFF;
RED_ON;
vTaskDelay(BLINK_INTERVAL);
RED_OFF;
vTaskDelay(BLINK_INTERVAL);
}
static void statusCycleMqttError()
{
GREEN_OFF;
BLUE_OFF;
RED_ON;
vTaskDelay(BLINK_INTERVAL/2);
RED_OFF;
vTaskDelay(BLINK_INTERVAL/2);
RED_ON;
vTaskDelay(BLINK_INTERVAL/2);
RED_OFF;
vTaskDelay(BLINK_INTERVAL*2);
}
static void printStatus()
{
ets_printf("TRIGGER STATUS: ");
switch (currentStatus) {
case STATUS_BOOT:
ets_printf("BOOT");
break;
case STATUS_OK:
ets_printf("OK");
break;
case STATUS_OK_WORKING:
ets_printf("OK WORKING");
break;
case STATUS_OK_ALT:
ets_printf("BT ENABLED");
break;
case STATUS_SYSTEM_ERROR:
ets_printf("WIFI ERROR");
break;
case STATUS_APP_ERROR:
ets_printf("MQTT ERROR");
break;
}
ets_printf("\n");
}
void nxEnablePowerSavingStatus(bool enable)
{
powerSaving = enable;
}
void nxStatusIndicatorTask(void* params)
{
UNUSED(params);
ESP_LOGI(TAG, "STATUS INIT");
while(1) {
switch (currentStatus) {
case STATUS_BOOT:
statusCycleBoot();
break;
case STATUS_OK:
if (powerSaving == true) {
statusCycleOkPowerSave();
}
else {
statusCycleOk();
}
break;
case STATUS_OK_WORKING:
statusCycleOkWorking();
break;
case STATUS_OK_ALT:
statusCycleBtEnabled();
break;
case STATUS_SYSTEM_ERROR:
statusCycleWifiError();
break;
case STATUS_APP_ERROR:
statusCycleMqttError();
break;
}
}
}
void nxInitStatusIndicator()
{
gpio_reset_pin(GPIO_LED_RED);
gpio_reset_pin(GPIO_LED_GREEN);
gpio_reset_pin(GPIO_LED_BLUE);
gpio_set_direction(GPIO_LED_RED, GPIO_MODE_OUTPUT);
gpio_set_direction(GPIO_LED_GREEN, GPIO_MODE_OUTPUT);
gpio_set_direction(GPIO_LED_BLUE, GPIO_MODE_OUTPUT);
GREEN_OFF;
BLUE_OFF;
RED_ON;
}
void nxUpdateStatus(HwStatus newStatus)
{
// priority filters:
if (currentStatus == STATUS_SYSTEM_ERROR
&& newStatus == STATUS_APP_ERROR) {
return;
}
prevStatus = currentStatus;
currentStatus = newStatus;
printStatus();
}

2
components/software/include/nx/software/SystemSettings.h

@ -8,6 +8,7 @@
#define WIFI_STRINGS_MAX_LEN 32 #define WIFI_STRINGS_MAX_LEN 32
#define URI_MAX_LEN 64 #define URI_MAX_LEN 64
#define TZ_ENV_LEN 32 #define TZ_ENV_LEN 32
#define RESTART_SCHEDULE_SIZE 12
typedef bool (*StorageReadFn)(const char* key, void* data, size_t size); typedef bool (*StorageReadFn)(const char* key, void* data, size_t size);
@ -24,6 +25,7 @@ typedef struct SystemSettings {
uint8_t dnsAddr[4]; uint8_t dnsAddr[4];
char sntpAddr[URI_MAX_LEN]; char sntpAddr[URI_MAX_LEN];
char tzEnv[TZ_ENV_LEN]; char tzEnv[TZ_ENV_LEN];
char rsSchedule[RESTART_SCHEDULE_SIZE];
} SystemSettings; } SystemSettings;

2
components/software/src/AppSettings.c

@ -22,7 +22,7 @@ static const uint8_t INIT_FLAG_VALUE = 1;
static const char* DEFAULT_MQTT_HOST = ""; static const char* DEFAULT_MQTT_HOST = "";
static const char* DEFAULT_MQTT_API_URI = "dev/api"; static const char* DEFAULT_MQTT_API_URI = "dev/api";
static const char* DEFAULT_MQTT_HB_URI = "dev/hb"; static const char* DEFAULT_MQTT_HB_URI = "dev/hb";
static const uint16_t DEFAULT_MQTT_HB_SEC = 0; static const uint16_t DEFAULT_MQTT_HB_SEC = 60 * 15;
static const uint8_t DEFAULT_MQTT_TLS = 0; static const uint8_t DEFAULT_MQTT_TLS = 0;
static const char* DEFAULT_MQTT_USER = ""; static const char* DEFAULT_MQTT_USER = "";
static const char* DEFAULT_MQTT_PASS = ""; static const char* DEFAULT_MQTT_PASS = "";

5
components/software/src/SystemSettings.c

@ -14,6 +14,7 @@
#define KEY_DNS_ADDR "dns" #define KEY_DNS_ADDR "dns"
#define KEY_SNTP_ADDR "sntp" #define KEY_SNTP_ADDR "sntp"
#define KEY_TZ_ENV "tz" #define KEY_TZ_ENV "tz"
#define KEY_RS_SCHEDULE "rsch"
#define IPV4_ADDR_SIZE 4 #define IPV4_ADDR_SIZE 4
@ -34,6 +35,7 @@ static const char DEFAULT_WIFI_MASK[IPV4_ADDR_SIZE] = {255,255,255,0};
static const char DEFAULT_DNS_ADDR[IPV4_ADDR_SIZE] = {8,8,8,8}; static const char DEFAULT_DNS_ADDR[IPV4_ADDR_SIZE] = {8,8,8,8};
static const char* DEFAULT_SNTP_ADDR = "pool.ntp.org"; static const char* DEFAULT_SNTP_ADDR = "pool.ntp.org";
static const char* DEFAULT_TZ_ENV = "CET-1CEST,M3.5.0,M10.5.0/3"; static const char* DEFAULT_TZ_ENV = "CET-1CEST,M3.5.0,M10.5.0/3";
static const char* DEFAULT_RS_SCHEDULE = "72 23 59 60"; // days (bin), hour, minute, required uptime (min)
static StorageWriteFn storageWrite = NULL; static StorageWriteFn storageWrite = NULL;
static StorageReadFn storageRead = NULL; static StorageReadFn storageRead = NULL;
@ -60,6 +62,7 @@ static void loadSystemSettings(void)
storageRead(KEY_DNS_ADDR , settings.dnsAddr , IPV4_ADDR_SIZE); storageRead(KEY_DNS_ADDR , settings.dnsAddr , IPV4_ADDR_SIZE);
storageRead(KEY_SNTP_ADDR , settings.sntpAddr , URI_MAX_LEN); storageRead(KEY_SNTP_ADDR , settings.sntpAddr , URI_MAX_LEN);
storageRead(KEY_TZ_ENV , settings.tzEnv , TZ_ENV_LEN); storageRead(KEY_TZ_ENV , settings.tzEnv , TZ_ENV_LEN);
storageRead(KEY_RS_SCHEDULE , settings.rsSchedule , RESTART_SCHEDULE_SIZE);
STORAGE_READ(KEY_WIFI_POWER_SAVE, wifiPowerSave); STORAGE_READ(KEY_WIFI_POWER_SAVE, wifiPowerSave);
STORAGE_READ(KEY_WIFI_USE_STATIC, useStaticAddr); STORAGE_READ(KEY_WIFI_USE_STATIC, useStaticAddr);
@ -96,6 +99,7 @@ void nxRestoreSystemDefaultSettings(void)
memcpy(settings.dnsAddr, DEFAULT_DNS_ADDR, IPV4_ADDR_SIZE); memcpy(settings.dnsAddr, DEFAULT_DNS_ADDR, IPV4_ADDR_SIZE);
strcpy(settings.sntpAddr, DEFAULT_SNTP_ADDR); strcpy(settings.sntpAddr, DEFAULT_SNTP_ADDR);
strcpy(settings.tzEnv, DEFAULT_TZ_ENV); strcpy(settings.tzEnv, DEFAULT_TZ_ENV);
strcpy(settings.rsSchedule, DEFAULT_RS_SCHEDULE);
nxWriteSystemSettings(); nxWriteSystemSettings();
} }
@ -119,6 +123,7 @@ void nxWriteSystemSettings(void)
storageWrite(KEY_DNS_ADDR , settings.dnsAddr , IPV4_ADDR_SIZE ); storageWrite(KEY_DNS_ADDR , settings.dnsAddr , IPV4_ADDR_SIZE );
storageWrite(KEY_SNTP_ADDR , settings.sntpAddr , URI_MAX_LEN ); storageWrite(KEY_SNTP_ADDR , settings.sntpAddr , URI_MAX_LEN );
storageWrite(KEY_TZ_ENV , settings.tzEnv , TZ_ENV_LEN ); storageWrite(KEY_TZ_ENV , settings.tzEnv , TZ_ENV_LEN );
storageWrite(KEY_RS_SCHEDULE , settings.rsSchedule , RESTART_SCHEDULE_SIZE );
STORAGE_WRITE(KEY_WIFI_POWER_SAVE, wifiPowerSave); STORAGE_WRITE(KEY_WIFI_POWER_SAVE, wifiPowerSave);
STORAGE_WRITE(KEY_WIFI_USE_STATIC, useStaticAddr); STORAGE_WRITE(KEY_WIFI_USE_STATIC, useStaticAddr);

10
components/software/src/SystemSettingsApi.c

@ -24,6 +24,7 @@
#define API_KEY_DNS_ADDR "dns" #define API_KEY_DNS_ADDR "dns"
#define API_KEY_SNTP_ADDR "sntp" #define API_KEY_SNTP_ADDR "sntp"
#define API_KEY_TZ_ENV "tz" #define API_KEY_TZ_ENV "tz"
#define API_KEY_RS_SCHEDULE "rsch"
#define API_KEY_RESTORE "restore_default" #define API_KEY_RESTORE "restore_default"
#define UNUSED(x) (void)(x) #define UNUSED(x) (void)(x)
@ -82,6 +83,9 @@ void handleKvPair(const char* key, const char* value, const void* userData, bool
else if (strcmp(key, API_KEY_TZ_ENV) == 0) { else if (strcmp(key, API_KEY_TZ_ENV) == 0) {
strcpy(settings->tzEnv, value); strcpy(settings->tzEnv, value);
} }
else if (strcmp(key, API_KEY_RS_SCHEDULE) == 0) {
strcpy(settings->rsSchedule, value);
}
else { else {
fprintf(stderr, "Unknown key: %s\n", key); fprintf(stderr, "Unknown key: %s\n", key);
} }
@ -106,7 +110,8 @@ void nxApiGetSystemSettings(const uint8_t* msg, size_t msgLen,
"\"%s\":\"%i.%i.%i.%i\"," // mask "\"%s\":\"%i.%i.%i.%i\"," // mask
"\"%s\":\"%i.%i.%i.%i\"," // dns "\"%s\":\"%i.%i.%i.%i\"," // dns
"\"%s\":\"%s\"," // sntp "\"%s\":\"%s\"," // sntp
"\"%s\":\"%s\"" // tz "\"%s\":\"%s\"," // tz
"\"%s\":\"%s\"" // rsch
"}", "}",
API_KEY_SSID, settings->wifiSsid, API_KEY_SSID, settings->wifiSsid,
API_KEY_WPWSAVE, settings->wifiPowerSave ? "true" : "false", API_KEY_WPWSAVE, settings->wifiPowerSave ? "true" : "false",
@ -116,7 +121,8 @@ void nxApiGetSystemSettings(const uint8_t* msg, size_t msgLen,
API_KEY_IPV4MASK, settings->ip4mask[0], settings->ip4mask[1], settings->ip4mask[2], settings->ip4mask[3], API_KEY_IPV4MASK, settings->ip4mask[0], settings->ip4mask[1], settings->ip4mask[2], settings->ip4mask[3],
API_KEY_DNS_ADDR, settings->dnsAddr[0], settings->dnsAddr[1], settings->dnsAddr[2], settings->dnsAddr[3], API_KEY_DNS_ADDR, settings->dnsAddr[0], settings->dnsAddr[1], settings->dnsAddr[2], settings->dnsAddr[3],
API_KEY_SNTP_ADDR, settings->sntpAddr, API_KEY_SNTP_ADDR, settings->sntpAddr,
API_KEY_TZ_ENV, settings->tzEnv API_KEY_TZ_ENV, settings->tzEnv,
API_KEY_RS_SCHEDULE, settings->rsSchedule
); );
*response = responseBuffer; *response = responseBuffer;
*respLen = strlen(responseBuffer); *respLen = strlen(responseBuffer);

49
main/Main.c

@ -4,6 +4,7 @@
#include "nx/software/AppSettingsApi.h" #include "nx/software/AppSettingsApi.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h" #include "esp_system.h"
#include "nx/firmware/Storage.h" #include "nx/firmware/Storage.h"
@ -11,6 +12,8 @@
#include "nx/firmware/MqTriggerHttpServer.h" #include "nx/firmware/MqTriggerHttpServer.h"
#include "nx/firmware/MqttClient.h" #include "nx/firmware/MqttClient.h"
#include "nx/firmware/SntpClient.h" #include "nx/firmware/SntpClient.h"
#include "nx/firmware/StatusIndicator.h"
#include "nx/firmware/Restarter.h"
#include "HttpHandlers.h" #include "HttpHandlers.h"
@ -20,7 +23,7 @@
#define TAG "MAIN" #define TAG "MAIN"
#define SNTP_RETRIES 5 #define SNTP_RETRIES 8
static const char* DEVICE_NAME_PREFIX = "mqtrigger-"; static const char* DEVICE_NAME_PREFIX = "mqtrigger-";
@ -28,7 +31,11 @@ extern const char ca_crt_start[] asm("_binary_ca_crt_start");
static void startWifi(void); static void startWifi(void);
static void onWifiConnected(void); static void onWifiConnected(void);
static void onWifiError(void);
static void onAppSettingsUpdate(void); static void onAppSettingsUpdate(void);
static void onMqttConnected();
static void onMqttDisconnected();
static void onMqttError();
static void onMqttMessage(const char* msg); static void onMqttMessage(const char* msg);
static SystemSettings* systemSettings = NULL; static SystemSettings* systemSettings = NULL;
@ -51,6 +58,10 @@ static const MqTriggerHttpCallbacks httpCallbacks = {
void app_main(void) void app_main(void)
{ {
nxInitStatusIndicator();
nxUpdateStatus(STATUS_BOOT);
xTaskCreate(&nxStatusIndicatorTask, "status_task", 2048, NULL, 1, NULL);
nxInitStorage(); nxInitStorage();
nxInitSystemSettings(nxStorageWrite, nxStorageRead); nxInitSystemSettings(nxStorageWrite, nxStorageRead);
@ -59,7 +70,10 @@ void app_main(void)
systemSettings = nxGetSystemSettings(); systemSettings = nxGetSystemSettings();
appSettings = nxGetAppSettings(); appSettings = nxGetAppSettings();
nxStartRestarter(systemSettings->rsSchedule, systemSettings->tzEnv);
nxSetWifiConnectedCallback(onWifiConnected); nxSetWifiConnectedCallback(onWifiConnected);
nxSetWifiErrorCallback(onWifiError);
startWifi(); startWifi();
} }
@ -85,7 +99,9 @@ static void startWifi(void)
}; };
ESP_LOGI(TAG, "Initializing WiFi"); ESP_LOGI(TAG, "Initializing WiFi");
nxInitWifi(&wifiSettings); if (!nxInitWifi(&wifiSettings)) {
nxUpdateStatus(STATUS_SYSTEM_ERROR);
}
} }
static void onWifiConnected(void) static void onWifiConnected(void)
@ -100,6 +116,9 @@ static void onWifiConnected(void)
mqttSettings.hbIntervalSec = appSettings->mqttHbIntervalSec; mqttSettings.hbIntervalSec = appSettings->mqttHbIntervalSec;
mqttSettings.caCrt = appSettings->mqttUseTls ? ca_crt_start : NULL; mqttSettings.caCrt = appSettings->mqttUseTls ? ca_crt_start : NULL;
mqttSettings.messageCb = onMqttMessage; mqttSettings.messageCb = onMqttMessage;
mqttSettings.connectedCb = onMqttConnected;
mqttSettings.disconnectedCb = onMqttDisconnected;
mqttSettings.errorCb = onMqttError;
nxStartMqttClient(&mqttSettings); nxStartMqttClient(&mqttSettings);
@ -110,9 +129,35 @@ static void onWifiConnected(void)
static void onAppSettingsUpdate(void) static void onAppSettingsUpdate(void)
{ {
ESP_LOGI(TAG, "App settings updated"); ESP_LOGI(TAG, "App settings updated");
nxUpdateStatus(STATUS_OK_WORKING);
} }
static void onMqttMessage(const char* msg) static void onMqttMessage(const char* msg)
{ {
ESP_LOGI(TAG, "MQTT MESSAGE RECEIVED: %s", msg); ESP_LOGI(TAG, "MQTT MESSAGE RECEIVED: %s", msg);
nxUpdateStatus(STATUS_OK_WORKING);
}
static void onWifiError()
{
ESP_LOGE(TAG, "WiFi ERROR");
nxUpdateStatus(STATUS_SYSTEM_ERROR);
}
static void onMqttConnected()
{
ESP_LOGI(TAG, "MQTT CONNECTED");
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);
} }

43
main/static/index.js

@ -15,8 +15,9 @@ function sendClickHandler(event) {
console.log(element.value); console.log(element.value);
if (element.value) { //TODO: checkbox/radio if (element.value) { //TODO: checkbox/radio
params += params ? '&' : '';
if (!element.hasAttribute('data-ignore')) {
params += params ? '&' : '';
if (element.hasAttribute('data-ip32')) { if (element.hasAttribute('data-ip32')) {
console.log("IPv4 32"); console.log("IPv4 32");
params += element.name + '=' + ip2int(element.value); params += element.name + '=' + ip2int(element.value);
@ -25,6 +26,11 @@ function sendClickHandler(event) {
params += element.name + '=' + element.value; params += element.name + '=' + element.value;
} }
} }
else {
console.log("Ignoring " + element.name);
}
}
}); });
@ -88,6 +94,9 @@ function loadValues(element) {
let input = element.querySelector("[name='" + key + "'"); let input = element.querySelector("[name='" + key + "'");
if (input) { //TODO: checkbox/radio if (input) { //TODO: checkbox/radio
input.value = obj[key]; input.value = obj[key];
if (input.hasAttribute('data-onset')) {
eval(input.getAttribute("data-onset"));
}
} }
} }
} }
@ -124,3 +133,35 @@ function initNavButtons() {
btns[i].addEventListener('click', navClickHandler); btns[i].addEventListener('click', navClickHandler);
} }
} }
function getRschDays() {
let daysBin = 0;
for (let i = 0; i < 7; ++i) {
let day = document.getElementById("rsd_" + i.toString());
if (day && day.checked === true) {
daysBin |= (1 << i);
}
}
return daysBin;
}
function rschUpdate() {
let rschElem = document.getElementById("rsch");
rschElem.value = getRschDays()
+ " "
+ document.getElementById("rsch_time").value.replace(":", " ")
+ " "
+ document.getElementById("rsch_rup").value;
}
function rschSet() {
let data = document.getElementById("rsch").value.split(" ");
document.getElementById("rsch_time").value = data[1] + ":" + data[2];
document.getElementById("rsch_rup").value = data[3];
for (let i = 0; i < 7; ++i) {
if (data[0] & (1 << i)) {
document.getElementById("rsd_" + i.toString()).checked = true;
}
}
}

15
main/static/min/index.js

@ -1,14 +1,23 @@
function ip2int(ip){return ip.split('.').reduce(function(ipInt,octet){return(ipInt<<8)+parseInt(octet,10)},0)>>>0;} function ip2int(ip){return ip.split('.').reduce(function(ipInt,octet){return(ipInt<<8)+parseInt(octet,10)},0)>>>0;}
function sendClickHandler(event){console.log("Sending form...");let form=event.srcElement.form;console.debug(form);let params='';Array.from(form.elements).forEach(element=>{console.log(element);console.log(element.name);console.log(element.value);if(element.value){params+=params?'&':'';if(element.hasAttribute('data-ip32')){console.log("IPv4 32");params+=element.name+'='+ip2int(element.value);} function sendClickHandler(event){console.log("Sending form...");let form=event.srcElement.form;console.debug(form);let params='';Array.from(form.elements).forEach(element=>{console.log(element);console.log(element.name);console.log(element.value);if(element.value){if(!element.hasAttribute('data-ignore')){params+=params?'&':'';if(element.hasAttribute('data-ip32')){console.log("IPv4 32");params+=element.name+'='+ip2int(element.value);}
else{params+=element.name+'='+element.value;}}});console.log("params: "+params);postParams(form.getAttribute('action'),params);console.log("Form sent");} else{params+=element.name+'='+element.value;}}
else{console.log("Ignoring "+element.name);}}});console.log("params: "+params);postParams(form.getAttribute('action'),params);console.log("Form sent");}
function initFormSendButtons(form){const btns=form.querySelectorAll('.send');for(i=0;i<btns.length;++i){btns[i].addEventListener('click',sendClickHandler);}} function initFormSendButtons(form){const btns=form.querySelectorAll('.send');for(i=0;i<btns.length;++i){btns[i].addEventListener('click',sendClickHandler);}}
function loadContent(url){let http=new XMLHttpRequest();http.open('GET',url,true);let element=document.getElementById('content');element.innerHTML="LOADING...";http.onreadystatechange=function(){if(http.readyState==4&&http.status==200){console.log("Content received");element.innerHTML=http.responseText;loadValues(element);initFormSendButtons(element);}} function loadContent(url){let http=new XMLHttpRequest();http.open('GET',url,true);let element=document.getElementById('content');element.innerHTML="LOADING...";http.onreadystatechange=function(){if(http.readyState==4&&http.status==200){console.log("Content received");element.innerHTML=http.responseText;loadValues(element);initFormSendButtons(element);}}
http.send();} http.send();}
function loadValues(element){const forms=element.querySelectorAll('form');for(i=0;i<forms.length;++i){const srcUrl=forms[i].getAttribute('data-values-src');if(!srcUrl){continue;} function loadValues(element){const forms=element.querySelectorAll('form');for(i=0;i<forms.length;++i){const srcUrl=forms[i].getAttribute('data-values-src');if(!srcUrl){continue;}
console.log("Getting values from "+srcUrl);let http=new XMLHttpRequest();http.open('GET',srcUrl,true);http.onreadystatechange=function(){if(http.readyState==4&&http.status==200){console.log("Values received: "+http.responseText);let obj=JSON.parse(http.responseText);if(obj){for(var key of Object.keys(obj)){console.log(key+" -> "+obj[key]) console.log("Getting values from "+srcUrl);let http=new XMLHttpRequest();http.open('GET',srcUrl,true);http.onreadystatechange=function(){if(http.readyState==4&&http.status==200){console.log("Values received: "+http.responseText);let obj=JSON.parse(http.responseText);if(obj){for(var key of Object.keys(obj)){console.log(key+" -> "+obj[key])
let input=element.querySelector("[name='"+key+"'");if(input){input.value=obj[key];}}}}} let input=element.querySelector("[name='"+key+"'");if(input){input.value=obj[key];if(input.hasAttribute('data-onset')){eval(input.getAttribute("data-onset"));}}}}}}
http.send();}} http.send();}}
function postParams(url,params){var http=new XMLHttpRequest();http.open('POST',url,true);http.setRequestHeader('Content-type','application/x-www-form-urlencoded');http.onreadystatechange=function(){if(http.readyState==4&&http.status==200){alert(http.responseText);}} function postParams(url,params){var http=new XMLHttpRequest();http.open('POST',url,true);http.setRequestHeader('Content-type','application/x-www-form-urlencoded');http.onreadystatechange=function(){if(http.readyState==4&&http.status==200){alert(http.responseText);}}
http.send(params);} http.send(params);}
function navClickHandler(event){console.log('Button Clicked');console.log('Destination: '+this.getAttribute('data-dst'));loadContent(this.getAttribute('data-dst'));} function navClickHandler(event){console.log('Button Clicked');console.log('Destination: '+this.getAttribute('data-dst'));loadContent(this.getAttribute('data-dst'));}
function initNavButtons(){const btns=document.querySelectorAll('.nav-btn');for(i=0;i<btns.length;++i){btns[i].addEventListener('click',navClickHandler);}} function initNavButtons(){const btns=document.querySelectorAll('.nav-btn');for(i=0;i<btns.length;++i){btns[i].addEventListener('click',navClickHandler);}}
function getRschDays(){let daysBin=0;for(let i=0;i<7;++i){let day=document.getElementById("rsd_"+i.toString());if(day&&day.checked===true){daysBin|=(1<<i);}}
return daysBin;}
function rschUpdate(){let rschElem=document.getElementById("rsch");rschElem.value=getRschDays()
+" "
+document.getElementById("rsch_time").value.replace(":"," ")
+" "
+document.getElementById("rsch_rup").value;}
function rschSet(){let data=document.getElementById("rsch").value.split(" ");document.getElementById("rsch_time").value=data[1]+":"+data[2];document.getElementById("rsch_rup").value=data[3];for(let i=0;i<7;++i){if(data[0]&(1<<i)){document.getElementById("rsd_"+i.toString()).checked=true;}}}

21
main/static/min/sys.html

@ -1,4 +1,4 @@
<h1>Device system management</h1><form id=sys-form action=/sys data-values-src=/sys><p><label>WiFi SSID: <h1>Device system management</h1><form id=net-form action=/sys data-values-src=/sys><h3>Network</h3><p><label>WiFi SSID:
<input name=ssid></label><p><label>WiFi password: <input name=ssid></label><p><label>WiFi password:
<input type=password name=wpass></label><p><label class=label>WiFi power saving: <input type=password name=wpass></label><p><label class=label>WiFi power saving:
<select class=select name=wpwsave> <select class=select name=wpwsave>
@ -10,6 +10,21 @@
<input name=ip data-ip32></label><p><label>Gateway (x.x.x.x): <input name=ip data-ip32></label><p><label>Gateway (x.x.x.x):
<input name=gw data-ip32></label><p><label>Mask (x.x.x.x): <input name=gw data-ip32></label><p><label>Mask (x.x.x.x):
<input name=mask data-ip32></label><p><label>DNS server (x.x.x.x): <input name=mask data-ip32></label><p><label>DNS server (x.x.x.x):
<input name=dns data-ip32></label><p><label>SNTP server: <input name=dns data-ip32></label></p><button type=button class=send>SEND</button></form><div class=col-md-offset-1><hr></div><form id=time-form action=/sys data-values-src=/sys><h3>Time</h3><p><label>SNTP server:
<input name=sntp></label><p><label>Time zone (GNU TZ variable format) : <input name=sntp></label><p><label>Time zone (GNU TZ variable format) :
<input name=tz></label></p><button type=button class=send>SEND</button></form><div class=col-md-offset-1><hr></div><h3>Restore defaults</h3><form id=restore-form action=/sys><input type=hidden name=restore_default value=1><p><button type=button class=send>RESTORE DEFAULT SETTINGS</button></form><div class=col-md-offset-1><hr></div><h3>Reboot</h3><form id=reboot-form action=/sys/reboot><input type=hidden name=reboot value=1><p><button type=button class=send>REBOOT</button></form> <input name=tz></label></p><button type=button class=send>SEND</button></form><div class=col-md-offset-1><hr></div><form id=rch-form action=/sys data-values-src=/sys><h3>Restarter schedule</h3><input type=hidden id=rsch name=rsch data-onset=rschSet();><p><label>Monday
<input type=checkbox name=rsd_1 id=rsd_1 onchange=rschUpdate(); data-ignore></label>
<label>Tuesday
<input type=checkbox name=rsd_2 id=rsd_2 onchange=rschUpdate(); data-ignore></label>
<label>Wednesday
<input type=checkbox name=rsd_3 id=rsd_3 onchange=rschUpdate(); data-ignore></label>
<label>Thursday
<input type=checkbox name=rsd_4 id=rsd_4 onchange=rschUpdate(); data-ignore></label>
<label>Friday
<input type=checkbox name=rsd_5 id=rsd_5 onchange=rschUpdate(); data-ignore></label>
<label>Saturday
<input type=checkbox name=rsd_6 id=rsd_6 onchange=rschUpdate(); data-ignore></label>
<label>Sunday
<input type=checkbox name=rsd_0 id=rsd_0 onchange=rschUpdate(); data-ignore></label><p><label>Time:
<input type=time id=rsch_time name=rsch_time onchange=rschUpdate() data-ignore></label><p><label>Minimal required uptime (min):
<input type=number id=rsch_rup name=rsch_rup onchange=rschUpdate() data-ignore></label></p><button type=button class=send>SEND</button></form><div class=col-md-offset-1><hr></div><h3>Restore defaults</h3><form id=restore-form action=/sys><input type=hidden name=restore_default value=1><p><button type=button class=send>RESTORE DEFAULT SETTINGS</button></form><div class=col-md-offset-1><hr></div><h3>Reboot</h3><form id=reboot-form action=/sys/reboot><input type=hidden name=reboot value=1><p><button type=button class=send>REBOOT</button></form>

79
main/static/sys.html

@ -1,14 +1,15 @@
<h1>Device system management</h1> <h1>Device system management</h1>
<form id="sys-form" action="/sys" data-values-src="/sys"> <form id="net-form" action="/sys" data-values-src="/sys">
<h3>Network</h3>
<p> <p>
<label>WiFi SSID: <label>WiFi SSID:
<input type="text" name="ssid"> <input type="text" name="ssid" />
</label> </label>
</p> </p>
<p> <p>
<label>WiFi password: <label>WiFi password:
<input type="password" name="wpass"> <input type="password" name="wpass" />
</label> </label>
</p> </p>
@ -32,34 +33,44 @@
<p> <p>
<label>Address (x.x.x.x): <label>Address (x.x.x.x):
<input type="text" name="ip" data-ip32> <input type="text" name="ip" data-ip32 />
</label> </label>
</p> </p>
<p> <p>
<label>Gateway (x.x.x.x): <label>Gateway (x.x.x.x):
<input type="text" name="gw" data-ip32> <input type="text" name="gw" data-ip32 />
</label> </label>
</p> </p>
<p> <p>
<label>Mask (x.x.x.x): <label>Mask (x.x.x.x):
<input type="text" name="mask" data-ip32> <input type="text" name="mask" data-ip32 />
</label> </label>
</p> </p>
<p> <p>
<label>DNS server (x.x.x.x): <label>DNS server (x.x.x.x):
<input type="text" name="dns" data-ip32> <input type="text" name="dns" data-ip32 />
</label> </label>
</p> </p>
<button type="button" class="send">SEND</button>
</form>
<div class="col-md-offset-1">
<hr />
</div>
<form id="time-form" action="/sys" data-values-src="/sys">
<h3>Time</h3>
<p> <p>
<label>SNTP server: <label>SNTP server:
<input type="text" name="sntp"> <input type="text" name="sntp"/>
</label> </label>
</p> </p>
<p> <p>
<label>Time zone (GNU TZ variable format) : <label>Time zone (GNU TZ variable format) :
<input type="text" name="tz"> <input type="text" name="tz"/>
</label> </label>
</p> </p>
@ -70,6 +81,56 @@
<hr /> <hr />
</div> </div>
<form id="rch-form" action="/sys" data-values-src="/sys">
<h3>Restarter schedule</h3>
<input type="hidden" id="rsch" name="rsch" data-onset="rschSet();"/>
<p>
<label>Monday
<input type="checkbox" name="rsd_1" id="rsd_1" onchange="rschUpdate();" data-ignore />
</label>
<label>Tuesday
<input type="checkbox" name="rsd_2" id="rsd_2" onchange="rschUpdate();" data-ignore />
</label>
<label>Wednesday
<input type="checkbox" name="rsd_3" id="rsd_3" onchange="rschUpdate();" data-ignore />
</label>
<label>Thursday
<input type="checkbox" name="rsd_4" id="rsd_4" onchange="rschUpdate();" data-ignore />
</label>
<label>Friday
<input type="checkbox" name="rsd_5" id="rsd_5" onchange="rschUpdate();" data-ignore />
</label>
<label>Saturday
<input type="checkbox" name="rsd_6" id="rsd_6" onchange="rschUpdate();" data-ignore />
</label>
<label>Sunday
<input type="checkbox" name="rsd_0" id="rsd_0" onchange="rschUpdate();" data-ignore />
</label>
</p>
<p>
<label>Time:
<input type="time" id="rsch_time" name="rsch_time" onchange="rschUpdate()" data-ignore />
</label>
</p>
<p>
<label>Minimal required uptime (min):
<input type="number" id="rsch_rup" name="rsch_rup" onchange="rschUpdate()" data-ignore />
</label>
</p>
<button type="button" class="send">SEND</button>
</form>
<div class="col-md-offset-1">
<hr />
</div>
<h3>Restore defaults</h3> <h3>Restore defaults</h3>
<form id="restore-form" action="/sys"> <form id="restore-form" action="/sys">
<input type="hidden" name="restore_default" value="1" /> <input type="hidden" name="restore_default" value="1" />

Loading…
Cancel
Save