Browse Source

Added MQTT client; Added SNTP client; added http server static files

master
chodak166 4 years ago
parent
commit
9c0094c033
  1. 5
      components/firmware/CMakeLists.txt
  2. 9
      components/firmware/include/nx/firmware/MqTriggerHttpServer.h
  3. 35
      components/firmware/include/nx/firmware/MqttClient.h
  4. 11
      components/firmware/include/nx/firmware/SntpClient.h
  5. 1
      components/firmware/include/nx/firmware/Wifi.h
  6. 39
      components/firmware/src/MqTriggerHttpServer.c
  7. 174
      components/firmware/src/MqttClient.c
  8. 42
      components/firmware/src/SntpClient.c
  9. 12
      components/firmware/src/Wifi.c
  10. 7
      components/software/CMakeLists.txt
  11. 40
      components/software/include/nx/software/AppSettings.h
  12. 13
      components/software/include/nx/software/AppSettingsApi.h
  13. 12
      components/software/include/nx/software/SystemSettings.h
  14. 9
      components/software/include/nx/software/SystemSettingsApi.h
  15. 119
      components/software/src/AppSettings.c
  16. 95
      components/software/src/AppSettingsApi.c
  17. 24
      components/software/src/KeyValueParser.c
  18. 11
      components/software/src/KeyValueParser.h
  19. 49
      components/software/src/SystemSettings.c
  20. 76
      components/software/src/SystemSettingsApi.c
  21. 3786
      kicad/mqtrigger.kicad_pcb
  22. 22
      kicad/mqtrigger.pro
  23. 9
      main/CMakeLists.txt
  24. 50
      main/HttpHandlers.h
  25. 118
      main/Main.c
  26. 48
      main/main.c
  27. 83
      main/static/app.html
  28. 22
      main/static/ca.crt
  29. 2169
      main/static/index.css
  30. 48
      main/static/index.html
  31. 126
      main/static/index.js
  32. 10
      main/static/min/app.html
  33. 1
      main/static/min/index.css
  34. 2
      main/static/min/index.html
  35. 14
      main/static/min/index.js
  36. 15
      main/static/min/sys.html
  37. 7
      main/static/minify.sh
  38. 91
      main/static/sys.html

5
components/firmware/CMakeLists.txt

@ -4,7 +4,9 @@ idf_component_register(
src/Wifi.c
src/MqTriggerHttpServer.c
src/HttpHelpers.c
# src/Buttons.c
src/MqttClient.c
src/Buttons.c
src/SntpClient.c
INCLUDE_DIRS ./include
PRIV_INCLUDE_DIRS ./src
@ -12,4 +14,5 @@ idf_component_register(
REQUIRES
nvs_flash
esp_http_server
mqtt
)

9
components/firmware/include/nx/firmware/MqTriggerHttpServer.h

@ -10,14 +10,19 @@ typedef void(*RequestCallback)(const uint8_t* content, size_t ctLen,
typedef struct MqTriggerHttpCallbacks {
RequestCallback getRoot;
RequestCallback getJs;
RequestCallback getCss;
RequestCallback getSysSet;
RequestCallback getSysSetForm;
RequestCallback postSysSet;
RequestCallback reboot;
RequestCallback getAppSet;
RequestCallback getAppSetForm;
RequestCallback postAppSet;
RequestCallback postCmd;
} MqTriggerHttpCallbacks;
bool nxStartMqTriggerHttpServer(void);
void nxStopMqTriggerHttpServer();
void nxSetMqTriggerHttpCallbacks(MqTriggerHttpCallbacks* cbs);
void nxSetMqTriggerHttpCallbacks(const MqTriggerHttpCallbacks* cbs);
#endif /* COMPONENTS_FIRMWARE_INCLUDE_FIRMWARE_MQTRIGGERHTTPSERVER_H_ */

35
components/firmware/include/nx/firmware/MqttClient.h

@ -0,0 +1,35 @@
#ifndef COMPONENTS_FIRMWARE_INCLUDE_NX_FIRMWARE_MQTTCLIENT_H_
#define COMPONENTS_FIRMWARE_INCLUDE_NX_FIRMWARE_MQTTCLIENT_H_
#include <stdint.h>
#define MQTT_BROKER_URI_MAX_LEN 128
#define MQTT_TOPIC_MAX_LEN 128
#define MQTT_USER_MAX_LEN 16
typedef void (*MqttConnectedCallback)();
typedef void (*MqttDisconnectedCallback)();
typedef void (*MqttErrorCallback)();
typedef void (*MqttMessageCallback)(const char* msg);
typedef struct MqttSettings
{
char brokerAddr[MQTT_BROKER_URI_MAX_LEN];
char apiTopic[MQTT_TOPIC_MAX_LEN];
char hbTopic[MQTT_TOPIC_MAX_LEN];
char user[MQTT_USER_MAX_LEN];
char password[MQTT_USER_MAX_LEN];
uint16_t hbIntervalSec;
const char* caCrt;
MqttMessageCallback messageCb;
MqttConnectedCallback connectedCb;
MqttDisconnectedCallback disconnectedCb;
MqttErrorCallback errorCb;
} MqttSettings;
void nxStartMqttClient(const MqttSettings* settings);
void nxMqttHeartbeatTask(void*);
#endif /* COMPONENTS_FIRMWARE_INCLUDE_NX_FIRMWARE_MQTTCLIENT_H_ */

11
components/firmware/include/nx/firmware/SntpClient.h

@ -0,0 +1,11 @@
#ifndef COMPONENTS_FIRMWARE_INCLUDE_NX_FIRMWARE_SNTPCLIENT_H_
#define COMPONENTS_FIRMWARE_INCLUDE_NX_FIRMWARE_SNTPCLIENT_H_
#include <stdbool.h>
#define DT_FORMAT_LEN 20
bool nxInitSntpClient(int retries, const char* serverAddr, const char* timezoneString);
void nxGetTimeStr(char buf[DT_FORMAT_LEN]);
#endif /* COMPONENTS_FIRMWARE_INCLUDE_NX_FIRMWARE_SNTPCLIENT_H_ */

1
components/firmware/include/nx/firmware/Wifi.h

@ -19,6 +19,7 @@ typedef struct WifiSettings {
uint8_t* ip4addr;
uint8_t* ip4gw;
uint8_t* ip4mask;
uint8_t* dns;
WifiMode mode;
} WifiSettings;

39
components/firmware/src/MqTriggerHttpServer.c

@ -5,16 +5,23 @@
#define TAG "NX_HTTP"
#define REBOOT_DELAY_MS 3000
static httpd_handle_t server = NULL;
static MqTriggerHttpCallbacks* callbacks = NULL;
static const MqTriggerHttpCallbacks* callbacks = NULL;
// -- URI handlers --
ADD_HTTP_HANDLER("/", getRoot, HTTP_GET)
ADD_HTTP_HANDLER("/index.js", getJs, HTTP_GET)
ADD_HTTP_HANDLER("/index.css", getCss, HTTP_GET)
ADD_HTTP_HANDLER("/sys", getSysSet, HTTP_GET)
ADD_HTTP_HANDLER("/sys/form", getSysSetForm, HTTP_GET)
ADD_HTTP_HANDLER("/sys", postSysSet, HTTP_POST)
ADD_HTTP_HANDLER("/sys/reboot", reboot, HTTP_POST)
ADD_HTTP_HANDLER("/app", getAppSet, HTTP_GET)
ADD_HTTP_HANDLER("/app/form", getAppSetForm, HTTP_GET)
ADD_HTTP_HANDLER("/app", postAppSet, HTTP_POST)
ADD_HTTP_HANDLER("/app/cmd", postCmd, HTTP_POST)
static esp_err_t uriOptionsHandler(httpd_req_t* req)
{
@ -30,18 +37,40 @@ static httpd_uri_t uriOptions = {
.handler = uriOptionsHandler
};
static esp_err_t uriRebootHandler(httpd_req_t* req)
{
nxAddCorsHeaders(req);
const char resp[] = "";
httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN);
vTaskDelay(REBOOT_DELAY_MS / portTICK_PERIOD_MS);
esp_restart();
return ESP_OK;
}
static httpd_uri_t uriReboot = {
.uri = "/sys/reboot",
.method = HTTP_POST,
.handler = uriRebootHandler
};
void registerMqTriggerHttpHandlers(void)
{
httpd_register_uri_handler(server, &uriOptions);
httpd_register_uri_handler(server, &uri_getRoot);
httpd_register_uri_handler(server, &uri_getJs);
httpd_register_uri_handler(server, &uri_getCss);
httpd_register_uri_handler(server, &uri_getSysSet);
httpd_register_uri_handler(server, &uri_getSysSetForm);
httpd_register_uri_handler(server, &uri_postSysSet);
httpd_register_uri_handler(server, &uri_reboot);
httpd_register_uri_handler(server, &uri_getAppSet);
httpd_register_uri_handler(server, &uri_getAppSetForm);
httpd_register_uri_handler(server, &uri_postAppSet);
httpd_register_uri_handler(server, &uri_postCmd);
httpd_register_uri_handler(server, &uriReboot);
}
// --------- Public API --------- //
bool nxStartMqTriggerHttpServer(void)
@ -72,7 +101,7 @@ void nxStopMqTriggerHttpServer()
}
}
void nxSetMqTriggerHttpCallbacks(MqTriggerHttpCallbacks* cbs)
void nxSetMqTriggerHttpCallbacks(const MqTriggerHttpCallbacks* cbs)
{
callbacks = cbs;
}

174
components/firmware/src/MqttClient.c

@ -0,0 +1,174 @@
#include "nx/firmware/MqttClient.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "lwip/sockets.h"
#include "lwip/dns.h"
#include "lwip/netdb.h"
#include "esp_log.h"
#include "mqtt_client.h"
#include <stdint.h>
#define UNUSED(x) (void)(x)
static const char* TAG = "MQTT";
static const int CMD_QOS = 2;
//static const int HB_QOS = 1;
//static const long int MINIMAL_HB_INTERVAL_MS = 1000 * 60;
static const char* CLIENT_ID = "p10-dev";
static const MqttSettings* settings = NULL;
esp_mqtt_client_handle_t clientHandle = NULL;
static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event)
{
esp_mqtt_client_handle_t client = event->client;
int msg_id;
// your_context_t *context = event->context;
switch (event->event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
clientHandle = client;
if (settings->connectedCb) {
settings->connectedCb();
}
msg_id = esp_mqtt_client_subscribe(client, settings->apiTopic, CMD_QOS);
if (msg_id == -1) {
ESP_LOGE(TAG, "Subscribtion failed");
if (settings->errorCb) {
settings->errorCb();
}
}
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
clientHandle = NULL;
if (settings->disconnectedCb) {
settings->disconnectedCb();
}
break;
case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_UNSUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_PUBLISHED:
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_DATA:
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
if (settings->messageCb) {
event->data[event->data_len] = '\0';
settings->messageCb(event->data);
}
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
printf("DATA=%.*s\r\n", event->data_len, event->data);
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
if (settings->errorCb) {
settings->errorCb();
}
break;
default:
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
break;
}
return ESP_OK;
}
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) {
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id);
mqtt_event_handler_cb(event_data);
}
// --------- Public API --------- //
void nxStartMqttClient(const MqttSettings* mqttSettings)
{
ESP_LOGI(TAG, "Initializing MQTT");
if (strlen(mqttSettings->brokerAddr) == 0) {
ESP_LOGW(TAG, "Empty broker address, skipping MQTT initialization");
return;
}
settings = mqttSettings;
esp_mqtt_client_config_t mqtt_cfg = {};
mqtt_cfg.uri = settings->brokerAddr;
mqtt_cfg.client_id = CLIENT_ID;
mqtt_cfg.cert_pem = settings->caCrt;
mqtt_cfg.username = settings->user;
mqtt_cfg.password = settings->password;
if (mqtt_cfg.host) {
ESP_LOGI(TAG, "host: %s", mqtt_cfg.host);
}
if (mqtt_cfg.uri) {
ESP_LOGI(TAG, "uri: %s", mqtt_cfg.uri);
}
ESP_LOGI(TAG, "client: %s", mqtt_cfg.client_id);
ESP_LOGI(TAG, "user: %s", mqtt_cfg.username);
ESP_LOGI(TAG, "pass: %s", mqtt_cfg.password);
ESP_LOGI(TAG, "cert: %s", mqtt_cfg.cert_pem);
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, client);
esp_mqtt_client_start(client);
}
void nxMqttHeartbeatTask(void* param)
{
UNUSED(param);
// ESP_LOGI(TAG, "Starting MQTT heartbeat task");
//
// ESP_LOGI(TAG, "hbIntervalMin: %d", settings->hbIntervalMin);
//
// long int hbIntervalMs = 1000 * 60 * settings->hbIntervalMin;
//
// ESP_LOGI(TAG, "hbIntervalMs: %li", hbIntervalMs);
//
//
// if (hbIntervalMs < MINIMAL_HB_INTERVAL_MS) {
// hbIntervalMs = MINIMAL_HB_INTERVAL_MS;
// }
//
// while (1) {
// if (clientHandle) {
// ESP_LOGI(TAG, "Sending MQTT heartbeat");
//
// char timeStr[DT_FORMAT_LEN];
// getTimeStr(timeStr);
// char hbMessage[DT_FORMAT_LEN + strlen(DEVICE_ID) + 2];
// strcpy(hbMessage, DEVICE_ID);
// strcat(hbMessage, " ");
// strcat(hbMessage, timeStr);
//
// esp_mqtt_client_publish(clientHandle, HEARTBEAT_TOPIC,
// hbMessage, strlen(hbMessage), HB_QOS, 1);
// }
// vTaskDelay(hbIntervalMs / portTICK_PERIOD_MS);
// }
}

42
components/firmware/src/SntpClient.c

@ -0,0 +1,42 @@
#include "nx/firmware/SntpClient.h"
#include <time.h>
#include <sys/time.h>
#include "esp_sntp.h"
#include "esp_log.h"
#define RETRY_DELAY_MS 2000
static const char* TAG = "SNTP_CLIENT";
static const char* tzString = "CET-1CEST,M3.5.0,M10.5.0/3";
bool nxInitSntpClient(int retries, const char* serverAddr, const char* timezoneString)
{
tzString = timezoneString;
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, serverAddr);
sntp_init();
int retry = 0;
while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET && ++retry < retries) {
ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, retries);
vTaskDelay(RETRY_DELAY_MS / portTICK_PERIOD_MS);
}
char buf[DT_FORMAT_LEN];
nxGetTimeStr(buf);
ESP_LOGI(TAG, "Initial system time: %s", buf);
return retry < retries;
}
void nxGetTimeStr(char buf[DT_FORMAT_LEN])
{
time_t now;
struct tm timeinfo;
time(&now);
setenv("TZ", tzString, 1);
tzset();
localtime_r(&now, &timeinfo);
strftime(buf, DT_FORMAT_LEN, "%Y-%m-%d %H:%M:%S", &timeinfo);
}

12
components/firmware/src/Wifi.c

@ -327,6 +327,18 @@ static bool initWifiStation(void)
ESP_LOGI(TAG, "Updating the host name");
ESP_ERROR_CHECK(esp_netif_set_hostname(sta, deviceName));
ESP_LOGI(TAG, "Updating DNS server IPv4");
esp_netif_dns_info_t dns;
dns.ip.u_addr.ip4.addr = PP_HTONL(LWIP_MAKEU32(
settings->dns[0],
settings->dns[1],
settings->dns[2],
settings->dns[3]));
dns.ip.type = IPADDR_TYPE_V4;
ESP_ERROR_CHECK(esp_netif_set_dns_info(sta, ESP_NETIF_DNS_MAIN, &dns));
return waitForStaConnection();
}

7
components/software/CMakeLists.txt

@ -1,7 +1,10 @@
idf_component_register(
SRCS
src/nxSystemSettings.c
src/nxSystemSettingsApi.c
src/SystemSettings.c
src/SystemSettingsApi.c
src/KeyValueParser.c
src/AppSettings.c
src/AppSettingsApi.c
INCLUDE_DIRS ./include
PRIV_INCLUDE_DIRS ./src

40
components/software/include/nx/software/AppSettings.h

@ -0,0 +1,40 @@
#ifndef NX_SOFTWARE_APPSETTINGS_H_
#define NX_SOFTWARE_APPSETTINGS_H_
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#define WIFI_STRINGS_MAX_LEN 32
#define URI_MAX_SIZE 64
#define USER_DATA_MAX_SIZE 16
typedef bool (*StorageReadFn)(const char* key, void* data, size_t size);
typedef void (*StorageWriteFn)(const char* key, const void* data, size_t size);
typedef void (*SettingsUpdatedCb)(void);
typedef struct AppSettings {
char mqttHost[URI_MAX_SIZE];
char mqttApiUri[URI_MAX_SIZE];
char mqttHbUri[URI_MAX_SIZE];
char mqttUser[USER_DATA_MAX_SIZE];
char mqttPassword[USER_DATA_MAX_SIZE];
uint16_t mqttHbIntervalSec;
uint8_t mqttUseTls;
} AppSettings;
void nxInitAppSettings(StorageWriteFn writeFn, StorageReadFn readFn, SettingsUpdatedCb updateCb);
void nxUpdateAppSettings(AppSettings* newSettings);
void nxRestoreAppDefaultSettings(void);
void nxWriteAppSettings(void);
AppSettings* nxGetAppSettings(void);
#endif /* NX_SOFTWARE_APPSETTINGS_H_ */

13
components/software/include/nx/software/AppSettingsApi.h

@ -0,0 +1,13 @@
#ifndef NX_SOFTWARE_APPSETTINGSAPI_H_
#define NX_SOFTWARE_APPSETTINGSAPI_H_
#include <stdint.h>
#include <stddef.h>
void nxApiUpdateAppSettings(const uint8_t* msg, size_t msgLen,
const char** response, size_t* respLen);
void nxApiGetAppSettings(const uint8_t* msg, size_t msgLen,
const char** response, size_t* respLen);
#endif /* NX_SOFTWARE_APPSETTINGSAPI_H_ */

12
components/software/include/nx/software/nxSystemSettings.h → components/software/include/nx/software/SystemSettings.h

@ -6,13 +6,13 @@
#include <stdint.h>
#define WIFI_STRINGS_MAX_LEN 32
#define URI_MAX_LEN 64
#define TZ_ENV_LEN 32
typedef bool (*StorageReadFn)(const char* key, void* data, size_t size);
typedef void (*StorageWriteFn)(const char* key, const void* data, size_t size);
typedef void(*RebootFn)(void);
typedef struct SystemSettings {
char wifiSsid[WIFI_STRINGS_MAX_LEN];
char wifiPassword[WIFI_STRINGS_MAX_LEN];
@ -21,11 +21,13 @@ typedef struct SystemSettings {
uint8_t ip4addr[4];
uint8_t ip4gw[4];
uint8_t ip4mask[4];
uint8_t dnsAddr[4];
char sntpAddr[URI_MAX_LEN];
char tzEnv[TZ_ENV_LEN];
} SystemSettings;
void nxInitSystemSettings(StorageWriteFn writeFn,
StorageReadFn readFn, RebootFn rebootFn);
void nxInitSystemSettings(StorageWriteFn writeFn, StorageReadFn readFn);
void nxUpdateSystemSettings(SystemSettings* newSettings);
@ -33,8 +35,6 @@ void nxRestoreSystemDefaultSettings(void);
void nxWriteSystemSettings(void);
void nxRebootSystem(void);
SystemSettings* nxGetSystemSettings(void);
#endif /* NX_SOFTWARE_NXSYSTEMSETTINGS_H_ */

9
components/software/include/nx/software/nxSystemSettingsApi.h → components/software/include/nx/software/SystemSettingsApi.h

@ -4,19 +4,10 @@
#include <stdint.h>
#include <stddef.h>
void nxApiUpdateSystemSettings(const uint8_t* msg, size_t msgLen,
const char** response, size_t* respLen);
void nxApiGetSystemSettings(const uint8_t* msg, size_t msgLen,
const char** response, size_t* respLen);
//TODO: handle as settings value?
void nxApiRestoreSystemSettings(const uint8_t* msg, size_t msgLen,
const char** response, size_t* respLen);
void nxApiRebootSystem(const uint8_t* msg, size_t msgLen,
const char** response, size_t* respLen);
#endif /* NX_SOFTWARE_NXSYSTEMSETTINGSAPI_H_ */

119
components/software/src/AppSettings.c

@ -0,0 +1,119 @@
#include "nx/software/AppSettings.h"
#include <stdio.h>
#include <string.h>
#define KEY_INIT_FLAG "appinit"
#define KEY_MQTT_HOST "mqhost"
#define KEY_MQTT_API_URI "mqapi"
#define KEY_MQTT_HB_URI "mqhb"
#define KEY_MQTT_HB_SEC "mqhbs"
#define KEY_MQTT_TLS "mqtls"
#define KEY_MQTT_USER "mquser"
#define KEY_MQTT_PASS "mqpass"
#define STORAGE_READ(_KEY, _NAME)\
storageRead(_KEY, &settings._NAME, sizeof(settings._NAME));
#define STORAGE_WRITE(_KEY, _NAME)\
storageWrite(_KEY, &settings._NAME, sizeof(settings._NAME));
static const uint8_t INIT_FLAG_VALUE = 1;
static const char* DEFAULT_MQTT_HOST = "";
static const char* DEFAULT_MQTT_API_URI = "dev/api";
static const char* DEFAULT_MQTT_HB_URI = "dev/hb";
static const uint16_t DEFAULT_MQTT_HB_SEC = 0;
static const uint8_t DEFAULT_MQTT_TLS = 0;
static const char* DEFAULT_MQTT_USER = "";
static const char* DEFAULT_MQTT_PASS = "";
static StorageWriteFn storageWrite = NULL;
static StorageReadFn storageRead = NULL;
static SettingsUpdatedCb settingsUpdatedCb = NULL;
static AppSettings settings;
static bool firstRun(void)
{
uint8_t initFlag = 0;
if (!storageRead(KEY_INIT_FLAG, &initFlag, sizeof(initFlag))) {
return true;
}
return initFlag != INIT_FLAG_VALUE;
}
static void loadSettings(void)
{
printf("Loading application settings\n");
STORAGE_READ(KEY_MQTT_HOST , mqttHost );
STORAGE_READ(KEY_MQTT_API_URI, mqttApiUri );
STORAGE_READ(KEY_MQTT_HB_URI , mqttHbUri );
STORAGE_READ(KEY_MQTT_HB_SEC , mqttHbIntervalSec);
STORAGE_READ(KEY_MQTT_TLS , mqttUseTls );
STORAGE_READ(KEY_MQTT_USER , mqttUser );
STORAGE_READ(KEY_MQTT_PASS , mqttPassword );
}
// --------- Public API --------- //
void nxInitAppSettings(StorageWriteFn writeFn,
StorageReadFn readFn, SettingsUpdatedCb updateCb)
{
storageWrite = writeFn;
storageRead = readFn;
settingsUpdatedCb = updateCb;
if (firstRun()) {
nxRestoreAppDefaultSettings();
}
else {
loadSettings();
}
}
void nxUpdateAppSettings(AppSettings* newSettings)
{
memcpy(&settings, newSettings, sizeof(AppSettings));
nxWriteAppSettings();
}
void nxRestoreAppDefaultSettings(void)
{
printf("Restoring DEFAULT application settings\n");
settings.mqttHbIntervalSec = DEFAULT_MQTT_HB_SEC;
settings.mqttUseTls = DEFAULT_MQTT_TLS;
strcpy(settings.mqttHbUri , DEFAULT_MQTT_HB_URI );
strcpy(settings.mqttApiUri , DEFAULT_MQTT_API_URI);
strcpy(settings.mqttHost , DEFAULT_MQTT_HOST );
strcpy(settings.mqttUser , DEFAULT_MQTT_USER );
strcpy(settings.mqttPassword, DEFAULT_MQTT_PASS );
nxWriteAppSettings();
}
void nxWriteAppSettings(void)
{
printf("WRITING APPLICATION SETTINGS\n");
storageWrite(KEY_INIT_FLAG , &INIT_FLAG_VALUE , 1);
STORAGE_WRITE(KEY_MQTT_HOST , mqttHost );
STORAGE_WRITE(KEY_MQTT_API_URI, mqttApiUri );
STORAGE_WRITE(KEY_MQTT_HB_URI , mqttHbUri );
STORAGE_WRITE(KEY_MQTT_HB_SEC , mqttHbIntervalSec);
STORAGE_WRITE(KEY_MQTT_TLS , mqttUseTls );
STORAGE_WRITE(KEY_MQTT_USER , mqttUser );
STORAGE_WRITE(KEY_MQTT_PASS , mqttPassword );
settingsUpdatedCb();
}
AppSettings* nxGetAppSettings(void)
{
return &settings;
}

95
components/software/src/AppSettingsApi.c

@ -0,0 +1,95 @@
#include "nx/software/AppSettingsApi.h"
#include "nx/software/AppSettings.h"
#include "KeyValueParser.h"
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#define API_KEY_RESTORE "restore_default"
#define API_KEY_MQTT_HOST "mqhost"
#define API_KEY_MQTT_API_URI "mqapi"
#define API_KEY_MQTT_HB_URI "mqhb"
#define API_KEY_MQTT_HB_SEC "mqhbs"
#define API_KEY_MQTT_TLS "mqtls"
#define API_KEY_MQTT_USER "mquser"
#define API_KEY_MQTT_PASS "mqpass"
#define UNUSED(x) (void)(x)
#define RESP_BUF_SIZE 1024
char responseBuffer[RESP_BUF_SIZE];
static void handleKvPair(const char* key, const char* value, const void* userData, bool* done)
{
printf("LED display settings update; Key: %s, Value: %s\n", key, value);
AppSettings* settings = nxGetAppSettings();
if (strcmp(key, API_KEY_RESTORE) == 0) {
nxRestoreAppDefaultSettings();
}
else if (strcmp(key, API_KEY_MQTT_HOST) == 0) {
strcpy(settings->mqttHost, value);
}
else if (strcmp(key, API_KEY_MQTT_API_URI) == 0) {
strcpy(settings->mqttApiUri, value);
}
else if (strcmp(key, API_KEY_MQTT_HB_URI) == 0) {
strcpy(settings->mqttHbUri, value);
}
else if (strcmp(key, API_KEY_MQTT_USER) == 0) {
strcpy(settings->mqttUser, value);
}
else if (strcmp(key, API_KEY_MQTT_PASS) == 0) {
strcpy(settings->mqttPassword, value);
}
else if (strcmp(key, API_KEY_MQTT_HB_SEC) == 0) {
settings->mqttHbIntervalSec = atoi(value);
}
else if (strcmp(key, API_KEY_MQTT_TLS) == 0) {
settings->mqttUseTls = atoi(value);
}
else {
fprintf(stderr, "Unknown key: %s\n", key);
}
}
// --------- Public API --------- //
void nxApiGetAppSettings(const uint8_t* msg, size_t msgLen,
const char** response, size_t* respLen)
{
UNUSED(msg); UNUSED(msgLen);
memset(responseBuffer, 0, RESP_BUF_SIZE);
AppSettings* settings = nxGetAppSettings();
sprintf(responseBuffer,
"{"
"\"%s\":\"%s\", " // mqtt host
"\"%s\":\"%s\", "
"\"%s\":\"%s\", "
"\"%s\":%i, " // hb interval
"\"%s\":%i, "
"\"%s\":\"%s\" " // mqtt user
"}",
API_KEY_MQTT_HOST , settings->mqttHost ,
API_KEY_MQTT_API_URI, settings->mqttApiUri ,
API_KEY_MQTT_HB_URI , settings->mqttHbUri ,
API_KEY_MQTT_HB_SEC , settings->mqttHbIntervalSec,
API_KEY_MQTT_TLS , settings->mqttUseTls ,
API_KEY_MQTT_USER , settings->mqttUser
);
*response = responseBuffer;
*respLen = strlen(responseBuffer);
}
void nxApiUpdateAppSettings(const uint8_t* msg, size_t msgLen,
const char** response, size_t* respLen)
{
memset(responseBuffer, 0, RESP_BUF_SIZE);
nxParseKeyValueString((const char*)msg, handleKvPair, NULL);
nxWriteAppSettings();
}

24
components/software/src/KeyValueParser.c

@ -0,0 +1,24 @@
#include "KeyValueParser.h"
#include <stdio.h>
#include <string.h>
#define MAX_KEY_SIZE 32
#define MAX_VALUE_SIZE 32
#define MAX_PAIRS 16
void nxParseKeyValueString(const char* string, KeyValueHandler handler, const void* userData)
{
bool done = false;
char key[MAX_KEY_SIZE];
char value[MAX_VALUE_SIZE];
FILE *stream;
stream = fmemopen((void*)string, strlen(string), "r");
int maxPairs = MAX_PAIRS;
while (fscanf(stream, "%127[^=]=%127[^&]%*c", key, value) == 2 && maxPairs > 0 && !done) {
handler(key, value, userData, &done);
maxPairs -= 1;
}
fclose(stream);
}

11
components/software/src/KeyValueParser.h

@ -0,0 +1,11 @@
#ifndef COMPONENTS_SOFTWARE_KEYVALUEPARSER_H_
#define COMPONENTS_SOFTWARE_KEYVALUEPARSER_H_
#include <stdbool.h>
typedef void (*KeyValueHandler)(const char* key, const char* value, const void* userData, bool* done);
void nxParseKeyValueString(const char* string, KeyValueHandler handler, const void* userData);
#endif /* COMPONENTS_SOFTWARE_KEYVALUEPARSER_H_ */

49
components/software/src/nxSystemSettings.c → components/software/src/SystemSettings.c

@ -1,8 +1,9 @@
#include <nx/software/nxSystemSettings.h>
#include "../include/nx/software/SystemSettings.h"
#include <stdio.h>
#include <string.h>
#define KEY_INIT_FLAG "set"
#define KEY_INIT_FLAG "sysinit"
#define KEY_WIFI_SSID "ssid"
#define KEY_WIFI_PASS "wpass"
#define KEY_WIFI_POWER_SAVE "wpsave"
@ -10,9 +11,18 @@
#define KEY_WIFI_ADDR "waddr"
#define KEY_WIFI_GW "wgw"
#define KEY_WIFI_MASK "wmask"
#define KEY_DNS_ADDR "dns"
#define KEY_SNTP_ADDR "sntp"
#define KEY_TZ_ENV "tz"
#define IPV4_ADDR_SIZE 4
#define STORAGE_READ(_KEY, _NAME)\
storageRead(_KEY, &settings._NAME, sizeof(settings._NAME));
#define STORAGE_WRITE(_KEY, _NAME)\
storageWrite(_KEY, &settings._NAME, sizeof(settings._NAME));
static const uint8_t INIT_FLAG_VALUE = 1;
static const char* DEFAULT_WIFI_SSID = "service_wifi";
static const char* DEFAULT_WIFI_PASS = "service_wifi";
@ -21,10 +31,12 @@ static const bool DEFAULT_WIFI_USE_STATIC = false;
static const char DEFAULT_WIFI_ADDR[IPV4_ADDR_SIZE] = {192,168,8,101};
static const char DEFAULT_WIFI_GW[IPV4_ADDR_SIZE] = {192,168,8,1};
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_SNTP_ADDR = "pool.ntp.org";
static const char* DEFAULT_TZ_ENV = "CET-1CEST,M3.5.0,M10.5.0/3";
static StorageWriteFn storageWrite = NULL;
static StorageReadFn storageRead = NULL;
RebootFn execReboot = NULL;
static SystemSettings settings;
@ -42,23 +54,25 @@ static void loadSystemSettings(void)
printf("Loading system settings\n");
storageRead(KEY_WIFI_SSID , settings.wifiSsid , WIFI_STRINGS_MAX_LEN);
storageRead(KEY_WIFI_PASS , settings.wifiPassword, WIFI_STRINGS_MAX_LEN);
storageRead(KEY_WIFI_POWER_SAVE, &settings.wifiPowerSave, sizeof(settings.wifiPowerSave));
storageRead(KEY_WIFI_USE_STATIC, &settings.useStaticAddr, sizeof(settings.useStaticAddr));
storageRead(KEY_WIFI_ADDR , settings.ip4addr , IPV4_ADDR_SIZE);
storageRead(KEY_WIFI_GW , settings.ip4gw , IPV4_ADDR_SIZE);
storageRead(KEY_WIFI_MASK , settings.ip4mask , IPV4_ADDR_SIZE);
storageRead(KEY_DNS_ADDR , settings.dnsAddr , IPV4_ADDR_SIZE);
storageRead(KEY_SNTP_ADDR , settings.sntpAddr , URI_MAX_LEN);
storageRead(KEY_TZ_ENV , settings.tzEnv , TZ_ENV_LEN);
STORAGE_READ(KEY_WIFI_POWER_SAVE, wifiPowerSave);
STORAGE_READ(KEY_WIFI_USE_STATIC, useStaticAddr);
}
// --------- Public API --------- //
void nxInitSystemSettings(StorageWriteFn writeFn,
StorageReadFn readFn,
RebootFn rebootFn)
StorageReadFn readFn)
{
storageWrite = writeFn;
storageRead = readFn;
execReboot = rebootFn;
if (firstRun()) {
printf("FIRST RUN\n");
@ -79,6 +93,10 @@ void nxRestoreSystemDefaultSettings(void)
memcpy(settings.ip4addr, DEFAULT_WIFI_ADDR, IPV4_ADDR_SIZE);
memcpy(settings.ip4gw, DEFAULT_WIFI_GW, IPV4_ADDR_SIZE);
memcpy(settings.ip4mask, DEFAULT_WIFI_MASK, IPV4_ADDR_SIZE);
memcpy(settings.dnsAddr, DEFAULT_DNS_ADDR, IPV4_ADDR_SIZE);
strcpy(settings.sntpAddr, DEFAULT_SNTP_ADDR);
strcpy(settings.tzEnv, DEFAULT_TZ_ENV);
nxWriteSystemSettings();
}
@ -92,13 +110,18 @@ void nxWriteSystemSettings(void)
{
printf("WRITING SYSTEM SETTINGS\n");
storageWrite(KEY_INIT_FLAG, &INIT_FLAG_VALUE, 1);
storageWrite(KEY_WIFI_SSID, settings.wifiSsid , WIFI_STRINGS_MAX_LEN);
storageWrite(KEY_WIFI_PASS, settings.wifiPassword, WIFI_STRINGS_MAX_LEN);
storageWrite(KEY_WIFI_POWER_SAVE, &settings.wifiPowerSave, sizeof(settings.wifiPowerSave));
storageWrite(KEY_WIFI_USE_STATIC, &settings.useStaticAddr, sizeof(settings.useStaticAddr));
storageWrite(KEY_WIFI_ADDR, settings.ip4addr , IPV4_ADDR_SIZE );
storageWrite(KEY_WIFI_GW , settings.ip4gw , IPV4_ADDR_SIZE );
storageWrite(KEY_WIFI_MASK, settings.ip4mask , IPV4_ADDR_SIZE );
storageWrite(KEY_DNS_ADDR , settings.dnsAddr , IPV4_ADDR_SIZE );
storageWrite(KEY_SNTP_ADDR, settings.sntpAddr , URI_MAX_LEN );
storageWrite(KEY_TZ_ENV , settings.tzEnv , TZ_ENV_LEN );
STORAGE_WRITE(KEY_WIFI_POWER_SAVE, wifiPowerSave);
STORAGE_WRITE(KEY_WIFI_USE_STATIC, useStaticAddr);
}
SystemSettings* nxGetSystemSettings(void)
@ -106,9 +129,3 @@ SystemSettings* nxGetSystemSettings(void)
return &settings;
}
void nxRebootSystem(void)
{
execReboot();
}

76
components/software/src/nxSystemSettingsApi.c → components/software/src/SystemSettingsApi.c

@ -1,15 +1,17 @@
#include "../include/nx/software/SystemSettingsApi.h"
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <nx/software/nxSystemSettings.h>
#include <nx/software/nxSystemSettingsApi.h>
#include "KeyValueParser.h"
#include <string.h>
#include "../include/nx/software/SystemSettings.h"
#define IPV4_ADDR_SIZE 4
/* curl example:
curl -v -X POST http://192.168.4.1/sys -d \
ssid=myssid,wpass=mypassword,wpwsave=1,wstatic=1,ip=3232237657,gw=3232237569,mask=4294967040
ssid=myssid&wpass=mypassword&wpwsave=1&wstatic=1&ip=3232237657&gw=3232237569&mask=4294967040
*/
#define API_KEY_SSID "ssid"
@ -19,6 +21,10 @@
#define API_KEY_IPV4ADDR "ip"
#define API_KEY_IPV4GW "gw"
#define API_KEY_IPV4MASK "mask"
#define API_KEY_DNS_ADDR "dns"
#define API_KEY_SNTP_ADDR "sntp"
#define API_KEY_TZ_ENV "tz"
#define API_KEY_RESTORE "restore_default"
#define UNUSED(x) (void)(x)
@ -34,12 +40,15 @@ void int2ip(uint8_t array[4], uint32_t addr)
array[3] = (addr & 0x000000FF);
}
void handleKvPair(const char* key, const char* value)
void handleKvPair(const char* key, const char* value, const void* userData, bool* done)
{
printf("System settings update; Key: %s, Value: %s\n", key, value);
SystemSettings* settings = nxGetSystemSettings();
if (strcmp(key, API_KEY_SSID) == 0) {
if (strcmp(key, API_KEY_RESTORE) == 0) {
nxRestoreSystemDefaultSettings();
}
else if (strcmp(key, API_KEY_SSID) == 0) {
strcpy(settings->wifiSsid, value);
}
else if (strcmp(key, API_KEY_WPASS) == 0) {
@ -54,12 +63,6 @@ void handleKvPair(const char* key, const char* value)
else if (strcmp(key, API_KEY_IPV4ADDR) == 0) {
uint32_t addr = strtoul(value, NULL, 10);
int2ip(settings->ip4addr, addr);
// debug
printf("new ip: %i.%i.%i.%i\n",
settings->ip4addr[0],
settings->ip4addr[1],
settings->ip4addr[2],
settings->ip4addr[3]);
}
else if (strcmp(key, API_KEY_IPV4GW) == 0) {
uint32_t addr = strtoul(value, NULL, 10);
@ -69,6 +72,16 @@ void handleKvPair(const char* key, const char* value)
uint32_t addr = strtoul(value, NULL, 10);
int2ip(settings->ip4mask, addr);
}
else if (strcmp(key, API_KEY_DNS_ADDR) == 0) {
uint32_t addr = strtoul(value, NULL, 10);
int2ip(settings->dnsAddr, addr);
}
else if (strcmp(key, API_KEY_SNTP_ADDR) == 0) {
strcpy(settings->sntpAddr, value);
}
else if (strcmp(key, API_KEY_TZ_ENV) == 0) {
strcpy(settings->tzEnv, value);
}
else {
fprintf(stderr, "Unknown key: %s\n", key);
}
@ -84,14 +97,26 @@ void nxApiGetSystemSettings(const uint8_t* msg, size_t msgLen,
SystemSettings* settings = nxGetSystemSettings();
sprintf(responseBuffer,
"{\"%s\"=\"%s\", \"%s\"=%s, \"%s\"=%s, "
"\"%s\"=\"%i.%i.%i.%i\", \"%s\"=\"%i.%i.%i.%i\", \"%s\"=\"%i.%i.%i.%i\"}",
"{"
"\"%s\":\"%s\", " // ssid
"\"%s\":%s, " // power save
"\"%s\":%s, " // use static ip
"\"%s\":\"%i.%i.%i.%i\", " // ip
"\"%s\":\"%i.%i.%i.%i\", " // gw
"\"%s\":\"%i.%i.%i.%i\"," // mask
"\"%s\":\"%i.%i.%i.%i\"," // dns
"\"%s\":\"%s\"," // sntp
"\"%s\":\"%s\"" // tz
"}",
API_KEY_SSID, settings->wifiSsid,
API_KEY_WPWSAVE, settings->wifiPowerSave ? "true" : "false",
API_KEY_USE_STATIC, settings->useStaticAddr ? "true" : "false",
API_KEY_IPV4ADDR, settings->ip4addr[0], settings->ip4addr[1], settings->ip4addr[2], settings->ip4addr[3],
API_KEY_IPV4GW, settings->ip4gw[0], settings->ip4gw[1], settings->ip4gw[2], settings->ip4gw[3],
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_SNTP_ADDR, settings->sntpAddr,
API_KEY_TZ_ENV, settings->tzEnv
);
*response = responseBuffer;
*respLen = strlen(responseBuffer);
@ -100,28 +125,7 @@ void nxApiGetSystemSettings(const uint8_t* msg, size_t msgLen,
void nxApiUpdateSystemSettings(const uint8_t* msg, size_t msgLen,
const char** response, size_t* respLen)
{
char key[32];
char value[32];
FILE *stream;
stream = fmemopen((void*)msg, strlen((const char*)msg), "r");
int maxPairs = 10;
while (fscanf(stream, "%127[^=]=%127[^,]%*c", key, value) == 2 && maxPairs > 0) {
handleKvPair(key, value);
maxPairs -= 1;
}
nxParseKeyValueString((const char*)msg, handleKvPair, NULL);
nxWriteSystemSettings();
}
void nxApiRestoreSystemSettings(const uint8_t* msg, size_t msgLen,
const char** response, size_t* respLen)
{
UNUSED(msg); UNUSED(msgLen); UNUSED(response); UNUSED(respLen);
nxRestoreSystemDefaultSettings();
}
void nxApiRebootSystem(const uint8_t* msg, size_t msgLen,
const char** response, size_t* respLen)
{
UNUSED(msg); UNUSED(msgLen); UNUSED(response); UNUSED(respLen);
nxRebootSystem();
}

3786
kicad/mqtrigger.kicad_pcb

File diff suppressed because it is too large Load Diff

22
kicad/mqtrigger.pro

@ -1,4 +1,4 @@
update=nie, 17 kwi 2022, 19:14:29
update=wto, 17 maj 2022, 17:53:07
version=1
last_client=kicad
[general]
@ -12,16 +12,6 @@ NetIExt=net
version=1
LibDir=
[eeschema/libraries]
[schematic_editor]
version=1
PageLayoutDescrFile=
PlotDirectoryName=
SubpartIdSeparator=0
SubpartFirstId=65
NetFmtName=CadStar
SpiceAjustPassiveValues=0
LabSize=50
ERC_TestSimilarLabels=1
[pcbnew]
version=1
PageLayoutDescrFile=
@ -250,3 +240,13 @@ uViaDrill=0.1
dPairWidth=0.5
dPairGap=0.25
dPairViaGap=0.25
[schematic_editor]
version=1
PageLayoutDescrFile=
PlotDirectoryName=
SubpartIdSeparator=0
SubpartFirstId=65
NetFmtName=CadStar
SpiceAjustPassiveValues=0
LabSize=50
ERC_TestSimilarLabels=1

9
main/CMakeLists.txt

@ -2,7 +2,14 @@
set(COMPONENT_REQUIRES firmware software)
set(COMPONENT_PRIV_REQUIRES )
set(COMPONENT_SRCS "main.c")
set(COMPONENT_SRCS "Main.c")
set(COMPONENT_ADD_INCLUDEDIRS "")
register_component()
target_add_binary_data(${COMPONENT_TARGET} "static/min/index.html" TEXT)
target_add_binary_data(${COMPONENT_TARGET} "static/min/index.css" TEXT)
target_add_binary_data(${COMPONENT_TARGET} "static/min/index.js" TEXT)
target_add_binary_data(${COMPONENT_TARGET} "static/min/sys.html" TEXT)
target_add_binary_data(${COMPONENT_TARGET} "static/min/app.html" TEXT)
target_add_binary_data(${COMPONENT_TARGET} "static/ca.crt" TEXT)

50
main/HttpHandlers.h

@ -0,0 +1,50 @@
#ifndef MAIN_HTTPHANDLERS_H_
#define MAIN_HTTPHANDLERS_H_
#include <stdint.h>
#include <stddef.h>
#include <string.h>
extern const char index_html_start[] asm("_binary_index_html_start");
extern const char index_css_start[] asm("_binary_index_css_start");
extern const char index_js_start[] asm("_binary_index_js_start");
extern const char sys_html_start[] asm("_binary_sys_html_start");
extern const char app_html_start[] asm("_binary_app_html_start");
static void rootHandler(const uint8_t* content, size_t ctLen,
const char** response, size_t* respLen)
{
*response = index_html_start;
*respLen = strlen(index_html_start);
}
static void jsHandler(const uint8_t* content, size_t ctLen,
const char** response, size_t* respLen)
{
*response = index_js_start;
*respLen = strlen(index_js_start);
}
static void cssHandler(const uint8_t* content, size_t ctLen,
const char** response, size_t* respLen)
{
*response = index_css_start;
*respLen = strlen(index_css_start);
}
static void sysFormHandler(const uint8_t* content, size_t ctLen,
const char** response, size_t* respLen)
{
*response = sys_html_start;
*respLen = strlen(sys_html_start);
}
static void appFormHandler(const uint8_t* content, size_t ctLen,
const char** response, size_t* respLen)
{
*response = app_html_start;
*respLen = strlen(app_html_start);
}
#endif /* MAIN_HTTPHANDLERS_H_ */

118
main/Main.c

@ -0,0 +1,118 @@
#include "nx/software/SystemSettings.h"
#include "nx/software/SystemSettingsApi.h"
#include "nx/software/AppSettings.h"
#include "nx/software/AppSettingsApi.h"
#include "freertos/FreeRTOS.h"
#include "esp_system.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 "HttpHandlers.h"
#include "esp_log.h"
#include <string.h>
#define TAG "MAIN"
#define SNTP_RETRIES 5
static const char* DEVICE_NAME_PREFIX = "mqtrigger-";
extern const char ca_crt_start[] asm("_binary_ca_crt_start");
static void startWifi(void);
static void onWifiConnected(void);
static void onAppSettingsUpdate(void);
static void onMqttMessage(const char* msg);
static SystemSettings* systemSettings = NULL;
static AppSettings* appSettings = NULL;
static WifiSettings wifiSettings;
static MqttSettings mqttSettings;
static const MqTriggerHttpCallbacks httpCallbacks = {
.getRoot = rootHandler,
.getJs = jsHandler,
.getCss = cssHandler,
.getSysSetForm = sysFormHandler,
.getAppSetForm = appFormHandler,
.getSysSet = nxApiGetSystemSettings,
.postSysSet = nxApiUpdateSystemSettings,
.getAppSet = nxApiGetAppSettings,
.postAppSet = nxApiUpdateAppSettings
// .postCmd = nxApiHandleLedCmd
};
void app_main(void)
{
nxInitStorage();
nxInitSystemSettings(nxStorageWrite, nxStorageRead);
nxInitAppSettings(nxStorageWrite, nxStorageRead, onAppSettingsUpdate);
systemSettings = nxGetSystemSettings();
appSettings = nxGetAppSettings();
nxSetWifiConnectedCallback(onWifiConnected);
startWifi();
}
// -----
static void startWifi(void)
{
strcpy(systemSettings->wifiSsid, "cintra");
strcpy(systemSettings->wifiPassword, "fffefdfcfb");
systemSettings->useStaticAddr = true;
systemSettings->ip4addr[3] = 91;
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
};
ESP_LOGI(TAG, "Initializing WiFi");
nxInitWifi(&wifiSettings);
}
static void onWifiConnected(void)
{
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);
mqttSettings.hbIntervalSec = appSettings->mqttHbIntervalSec;
mqttSettings.caCrt = appSettings->mqttUseTls ? ca_crt_start : NULL;
mqttSettings.messageCb = onMqttMessage;
nxStartMqttClient(&mqttSettings);
nxSetMqTriggerHttpCallbacks(&httpCallbacks);
nxStartMqTriggerHttpServer();
}
static void onAppSettingsUpdate(void)
{
ESP_LOGI(TAG, "App settings updated");
}
static void onMqttMessage(const char* msg)
{
ESP_LOGI(TAG, "MQTT MESSAGE RECEIVED: %s", msg);
}

48
main/main.c

@ -1,48 +0,0 @@
#include "freertos/FreeRTOS.h"
#include "esp_system.h"
#include "nx/firmware/Storage.h"
#include "nx/firmware/Wifi.h"
#include "nx/firmware/MqTriggerHttpServer.h"
#include "nx/software/nxSystemSettings.h"
#include "esp_log.h"
#include <string.h>
#define TAG "MAIN"
static const char* DEVICE_NAME_PREFIX = "mqtrigger-";
static SystemSettings* systemSettings = NULL;
static WifiSettings wifiSettings;
void app_main(void)
{
nxInitStorage();
nxInitSystemSettings(nxStorageWrite, nxStorageRead, esp_restart);
ESP_LOGI(TAG, "System settings initialized");
systemSettings = nxGetSystemSettings();
strcpy(systemSettings->wifiSsid, "cintra");
strcpy(systemSettings->wifiPassword, "fffefdfcfb");
systemSettings->useStaticAddr = true;
systemSettings->ip4addr[3] = 91;
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
};
ESP_LOGI(TAG, "Initializing WiFi");
nxInitWifi(&wifiSettings);
nxStartMqTriggerHttpServer();
}

83
main/static/app.html

@ -0,0 +1,83 @@
<h1>Application settings</h1>
<h3>MQTT settings</h3>
<form id="app-settings" action="/app" data-values-src="/app">
<p>
<label>MQTT host address:
<input type="text" name="mqhost">
</label>
</p>
<p>
<label>API topic:
<input type="text" name="mqapi">
</label>
</p>
<p>
<label>Heartbeat topic [TODO]:
<input type="text" name="mqhb">
</label>
</p>
<p>
<label>Heartbeat interval (sec) [TODO]
<input type="number" name="mqhbs">
</label>
</p>
<p>
<label>User name:
<input type="text" name="mquser">
</label>
</p>
<p>
<label>Password:
<input type="password" name="mqpass">
</label>
</p>
<p>
<label class="label">Use TLS:
<select class="select" name="mqtls">
<option value="0">No</option>
<option value="1">Yes</option>
</select>
</label>
</p>
<p>
<button type="button" class="send">SEND</button>
</p>
NOTE: device reboot is required
</form>
<div class="col-md-offset-1">
<hr />
</div>
<h3>Restore defaults</h3>
<form id="restore-form" action="/app">
<input type="hidden" name="restore_default" value="1" />
<p>
<button type="button" class="send">RESTORE DEFAULT SETTINGS</button>
</p>
</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>
</p>
</form>

22
main/static/ca.crt

@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDlzCCAn+gAwIBAgIUWURIxc42eG2pExKTzmZbH61aNK0wDQYJKoZIhvcNAQEL
BQAwWzELMAkGA1UEBhMCUEwxFDASBgNVBAgMC01hem93aWVja2llMREwDwYDVQQH
DAhXYXJzemF3YTEPMA0GA1UECgwGbml4bGFiMRIwEAYDVQQDDAluaXhsYWIuaW4w
HhcNMjIwNTE4MDkxNTM4WhcNNDIwNTEzMDkxNTM4WjBbMQswCQYDVQQGEwJQTDEU
MBIGA1UECAwLTWF6b3dpZWNraWUxETAPBgNVBAcMCFdhcnN6YXdhMQ8wDQYDVQQK
DAZuaXhsYWIxEjAQBgNVBAMMCW5peGxhYi5pbjCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAM1PrRlwzAi5Z6627dhCIIxrRf3f6m8QYRnfNfme1HRYyCYP
cZFDQDRIRkBjvBCeIOpU4caFtzJok/k8C523/pqMUzpBtFSCu4ASxRxy22xUR2pW
KVd76NTrm6PbkuKL6mtk5hOKqqAZSbZhwflmOKDhqb2GMDCvCFv/klSBe6IuX6Pk
oHFIwPntYRX58nwFMoTc65DGn9XKo5jik1F+g8pH9aNDRLW0mBRw77mAQuqvMLvG
8DNkKEREBvmuGJzU0uy91hN1EpKpGZ5Mn3OuuCMC6wucmyykCSDA12efBXJfKWGM
EHiG7SyJVJeLl6mHTk3aaIVloNk1yuvtzTfUwBsCAwEAAaNTMFEwHQYDVR0OBBYE
FGnHRrCf0gUfF5X5uFjKppU0sDkzMB8GA1UdIwQYMBaAFGnHRrCf0gUfF5X5uFjK
ppU0sDkzMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADnQEVM2
ovaGc3BEI2tnIurUSzEiG86gebS6OsK8w/uPhEHr/PCUpIDNEq0jFvenYi0WyuTG
pdQjdHe8cynqcprfxA+/gqzSQN17Ok8xKJSDcJ0ZV0awIMWRAoG18+HYcACQvHFN
56Ovkkvc0YVY8x6tLYTTTL2re7C73gT7Nsc7Kdvpmjj763DuoYlxLLaf1RCfEdtc
s+d9zKENZWOgj4xekPETZUFfxwPS3p39x1zUrJtld1L1cIhcBz8HawphrPyAl9Wl
K77J3blOxKmsxKJo95kpEiDss0Git066gqHFZjFSriLCTRds7oPK1URm0/5nbLG8
l1xpAT+JDQIiU90=
-----END CERTIFICATE-----

2169
main/static/index.css

File diff suppressed because it is too large Load Diff

48
main/static/index.html

@ -0,0 +1,48 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=">
<link rel="stylesheet" href="index.css" />
<script type="text/javascript" src="index.js"></script>
</head>
<body>
<header class="sticky row">
<div class="col-sm col-md-10 col-md-offset-1">
<a id="app-btn" href="#" role="button" class="nav-btn" data-dst="/app/form">App</a>
<a href="#" role="button" class="nav-btn" data-dst="/sys/form">System</a>
<!-- <a href="#" role="button" class="nav-btn" data-dst="/test-form.html">TEST FORM</a> -->
</div>
</header>
<div class="container">
<div class="row cols-sm-12 cols-md-10">
<div class="col-md-offset-1" id="content">
<h1>Loading</h1>
<p>Loading content... Please make sure that JavaScript is enabled.</p>
</div>
<div class="col-md-offset-1"> <hr/> </div>
</div>
</div>
<footer>
<div class="col-sm col-md-10 col-md-offset-1">
<p>
Copyright &copy; Łukasz Chodyła 2022
</p>
</div>
</footer>
<script>
initNavButtons();
document.getElementById('app-btn').click();
</script>
</body>
</html>

126
main/static/index.js

@ -0,0 +1,126 @@
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...");
//console.debug(event);
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) { //TODO: checkbox/radio
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");
}
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..."; //TODO: add spinner
//Send the proper header information along with the request
//http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
http.onreadystatechange = function () {//Call a function when the state changes.
if (http.readyState == 4 && http.status == 200) {
console.log("Content received");
element.innerHTML = http.responseText;
loadValues(element);
initFormSendButtons(element);
}
}
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;
}
console.log("Getting values from " + srcUrl);
let http = new XMLHttpRequest();
http.open('GET', srcUrl, true);
//Send the proper header information along with the request
//http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
http.onreadystatechange = function () {//Call a function when the state changes.
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) { //TODO: checkbox/radio
input.value = obj[key];
}
}
}
}
}
http.send();
}
}
function postParams(url, params) {
var http = new XMLHttpRequest();
http.open('POST', url, true);
//Send the proper header information along with the request
http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
http.onreadystatechange = function () {//Call a function when the state changes.
if (http.readyState == 4 && http.status == 200) {
alert(http.responseText);
}
}
http.send(params);
}
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);
}
}

10
main/static/min/app.html

@ -0,0 +1,10 @@
<h1>Application settings</h1><h3>MQTT settings</h3><form id=app-settings action=/app data-values-src=/app><p><label>MQTT host address:
<input name=mqhost></label><p><label>API topic:
<input name=mqapi></label><p><label>Heartbeat topic [TODO]:
<input name=mqhb></label><p><label>Heartbeat interval (sec) [TODO]
<input type=number name=mqhbs></label><p><label>User name:
<input name=mquser></label><p><label>Password:
<input type=password name=mqpass></label><p><label class=label>Use TLS:
<select class=select name=mqtls>
<option value=0>No
<option value=1>Yes</select></label><p><button type=button class=send>SEND</button></p>NOTE: device reboot is required</form><div class=col-md-offset-1><hr></div><h3>Restore defaults</h3><form id=restore-form action=/app><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>

1
main/static/min/index.css

File diff suppressed because one or more lines are too long

2
main/static/min/index.html

@ -0,0 +1,2 @@
<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><header class="sticky row"><div class="col-sm col-md-10 col-md-offset-1"><a id=app-btn href=# role=button class=nav-btn data-dst=/app/form>App</a>
<a href=# role=button class=nav-btn data-dst=/sys/form>System</a></div></header><div class=container><div class="row cols-sm-12 cols-md-10"><div class=col-md-offset-1 id=content><h1>Loading</h1><p>Loading content... Please make sure that JavaScript is enabled.</div><div class=col-md-offset-1><hr></div></div></div><footer><div class="col-sm col-md-10 col-md-offset-1"><p>Copyright &copy; Łukasz Chodyła 2022</div></footer><script>initNavButtons();document.getElementById('app-btn').click();</script>

14
main/static/min/index.js

@ -0,0 +1,14 @@
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);}
else{params+=element.name+'='+element.value;}}});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 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();}
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])
let input=element.querySelector("[name='"+key+"'");if(input){input.value=obj[key];}}}}}
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);}}
http.send(params);}
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);}}

15
main/static/min/sys.html

@ -0,0 +1,15 @@
<h1>Device system management</h1><form id=sys-form action=/sys data-values-src=/sys><p><label>WiFi SSID:
<input name=ssid></label><p><label>WiFi password:
<input type=password name=wpass></label><p><label class=label>WiFi power saving:
<select class=select name=wpwsave>
<option value=0>No
<option value=1>Yes</select></label><p><label class=label>Use static IPv4 address:
<select class=select name=wstatic>
<option value=0>No
<option value=1>Yes</select></label><p><label>Address (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=mask data-ip32></label><p><label>DNS server (x.x.x.x):
<input name=dns data-ip32></label><p><label>SNTP server:
<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>

7
main/static/minify.sh

@ -0,0 +1,7 @@
#!/bin/bash
minify index.html > min/index.html
minify index.js > min/index.js
minify index.css > min/index.css
minify sys.html > min/sys.html
minify app.html > min/app.html

91
main/static/sys.html

@ -0,0 +1,91 @@
<h1>Device system management</h1>
<form id="sys-form" action="/sys" data-values-src="/sys">
<p>
<label>WiFi SSID:
<input type="text" name="ssid">
</label>
</p>
<p>
<label>WiFi password:
<input type="password" name="wpass">
</label>
</p>
<p>
<label class="label">WiFi power saving:
<select class="select" name="wpwsave">
<option value="0">No</option>
<option value="1">Yes</option>
</select>
</label>
</p>
<p>
<label class="label">Use static IPv4 address:
<select class="select" name="wstatic">
<option value="0">No</option>
<option value="1">Yes</option>
</select>
</label>
</p>
<p>
<label>Address (x.x.x.x):
<input type="text" name="ip" data-ip32>
</label>
</p>
<p>
<label>Gateway (x.x.x.x):
<input type="text" name="gw" data-ip32>
</label>
</p>
<p>
<label>Mask (x.x.x.x):
<input type="text" name="mask" data-ip32>
</label>
</p>
<p>
<label>DNS server (x.x.x.x):
<input type="text" name="dns" data-ip32>
</label>
</p>
<p>
<label>SNTP server:
<input type="text" name="sntp">
</label>
</p>
<p>
<label>Time zone (GNU TZ variable format) :
<input type="text" 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>
</p>
</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>
</p>
</form>
Loading…
Cancel
Save