Browse Source

Refactored MQTT client

master
chodak166 4 years ago
parent
commit
113dc05239
  1. 9
      components/firmware/include/nx/firmware/MqttClient.h
  2. 1
      components/firmware/include/nx/firmware/Wifi.h
  3. 2
      components/firmware/src/HttpHelpers.h
  4. 1
      components/firmware/src/MqTriggerHttpServer.c
  5. 96
      components/firmware/src/MqttClient.c
  6. 8
      components/firmware/src/SntpClient.c
  7. 14
      components/firmware/src/Wifi.c
  8. 6
      components/software/include/nx/software/AppSettings.h
  9. 1
      components/software/include/nx/software/SystemSettings.h
  10. 69
      components/software/src/AppSettings.c
  11. 42
      components/software/src/AppSettingsApi.c
  12. 7
      components/software/src/KeyValueParser.c
  13. 42
      components/software/src/SystemSettings.c
  14. 31
      components/software/src/SystemSettingsApi.c
  15. 1
      main/CMakeLists.txt
  16. 55
      main/Main.c
  17. 22
      main/static/app.html
  18. 28
      main/static/index.js
  19. 6
      main/static/min/app.html
  20. 7
      main/static/min/index.js
  21. 3
      main/static/min/sys.html
  22. 6
      main/static/sys.html

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

@ -2,10 +2,12 @@
#define COMPONENTS_FIRMWARE_INCLUDE_NX_FIRMWARE_MQTTCLIENT_H_
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#define MQTT_BROKER_URI_MAX_LEN 128
#define MQTT_TOPIC_MAX_LEN 128
#define MQTT_USER_MAX_LEN 16
#define MQTT_USER_MAX_LEN 32
typedef void (*MqttConnectedCallback)();
typedef void (*MqttDisconnectedCallback)();
@ -17,6 +19,7 @@ typedef struct MqttSettings
char brokerAddr[MQTT_BROKER_URI_MAX_LEN];
char apiTopic[MQTT_TOPIC_MAX_LEN];
char hbTopic[MQTT_TOPIC_MAX_LEN];
char clientId[MQTT_USER_MAX_LEN];
char user[MQTT_USER_MAX_LEN];
char password[MQTT_USER_MAX_LEN];
uint16_t hbIntervalSec;
@ -30,6 +33,8 @@ typedef struct MqttSettings
void nxStartMqttClient(const MqttSettings* settings);
void nxMqttHeartbeatTask(void*);
bool nxMqttSubscribe(const char* topic, uint8_t qos);
uint32_t nxMqttPublish(const char* topic, uint8_t qos, const char* data, size_t size, uint8_t retain);
#endif /* COMPONENTS_FIRMWARE_INCLUDE_NX_FIRMWARE_MQTTCLIENT_H_ */

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

@ -31,6 +31,7 @@ bool nxInitWifi(WifiSettings* wifiSettings);
void nxSetWifiConnectedCallback(WifiCallback callback);
void nxSetWifiErrorCallback(WifiCallback callback);
const char* nxGetWifiDeviceName(void);
#endif // COMPONENTS_FIRMWARE_WIFI_H

2
components/firmware/src/HttpHelpers.h

@ -6,7 +6,7 @@
typedef void(*RequestCallback)(const uint8_t* content, size_t ctLen,
const char** response, size_t* respLen);
#define REQ_CONTENT_MAX_SIZE 1024
#define REQ_CONTENT_MAX_SIZE 2048
// Request handler helper macro
#define ADD_HTTP_HANDLER(_URI, _NAME, _METHOD)\

1
components/firmware/src/MqTriggerHttpServer.c

@ -82,6 +82,7 @@ bool nxStartMqTriggerHttpServer(void)
config.server_port = 80;
config.max_uri_handlers = 16;
config.uri_match_fn = httpd_uri_match_wildcard;
config.stack_size = 8192;
// ...
if (server != NULL) {

96
components/firmware/src/MqttClient.c

@ -17,12 +17,6 @@
#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;
@ -32,8 +26,6 @@ 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");
@ -42,15 +34,6 @@ static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event)
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:
@ -78,8 +61,6 @@ static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event)
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");
@ -116,59 +97,42 @@ void nxStartMqttClient(const MqttSettings* mqttSettings)
esp_mqtt_client_config_t mqtt_cfg = {};
mqtt_cfg.uri = settings->brokerAddr;
mqtt_cfg.uri = settings->brokerAddr;
mqtt_cfg.client_id = settings->clientId;
mqtt_cfg.cert_pem = settings->caCrt;
mqtt_cfg.username = settings->user;
mqtt_cfg.password = settings->password;
mqtt_cfg.client_id = CLIENT_ID;
mqtt_cfg.cert_pem = settings->caCrt;
mqtt_cfg.username = settings->user;
mqtt_cfg.password = settings->password;
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);
}
if (mqtt_cfg.host) {
ESP_LOGI(TAG, "host: %s", mqtt_cfg.host);
bool nxMqttSubscribe(const char* topic, uint8_t qos)
{
if (!clientHandle) {
ESP_LOGE(TAG, "MQTT client not initialized, aborting subscription of topic %s", topic);
return false;
}
if (mqtt_cfg.uri) {
ESP_LOGI(TAG, "uri: %s", mqtt_cfg.uri);
int msg_id = esp_mqtt_client_subscribe(clientHandle, settings->apiTopic, qos);
if (msg_id == -1) {
ESP_LOGE(TAG, "Subscribtion of topic %s failed", topic);
if (settings->errorCb) {
settings->errorCb();
}
return false;
}
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);
return true;
}
void nxMqttHeartbeatTask(void* param)
uint32_t nxMqttPublish(const char* topic, uint8_t qos, const char* data, size_t size, uint8_t retain)
{
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);
// }
if (!clientHandle) {
ESP_LOGE(TAG, "MQTT client not initialized, aborting data publishing in topic %s", topic);
return -1;
}
return esp_mqtt_client_publish(clientHandle, topic, data, size, qos, retain);
}

8
components/firmware/src/SntpClient.c

@ -8,7 +8,7 @@
static const char* TAG = "SNTP_CLIENT";
static const char* tzString = "CET-1CEST,M3.5.0,M10.5.0/3";
static const char* tzString = NULL;
bool nxInitSntpClient(int retries, const char* serverAddr, const char* timezoneString)
{
@ -35,8 +35,10 @@ void nxGetTimeStr(char buf[DT_FORMAT_LEN])
struct tm timeinfo;
time(&now);
setenv("TZ", tzString, 1);
tzset();
if (tzString) {
setenv("TZ", tzString, 1);
tzset();
}
localtime_r(&now, &timeinfo);
strftime(buf, DT_FORMAT_LEN, "%Y-%m-%d %H:%M:%S", &timeinfo);
}

14
components/firmware/src/Wifi.c

@ -43,7 +43,7 @@ static WifiSettings* settings = NULL;
static WifiCallback errorCallback = NULL;
static WifiCallback connectedCallback = NULL;
static char deviceName[DEVICE_NAME_MAX_LEN];
static char deviceName[DEVICE_NAME_MAX_LEN] = {0};
static bool initWifiStation(void);
@ -342,3 +342,15 @@ static bool initWifiStation(void)
return waitForStaConnection();
}
const char* nxGetWifiDeviceName(void)
{
if (settings && settings->devicePrefix && strlen(deviceName) == 0) {
generateDeviceName();
return deviceName;
}
else if (strlen(deviceName)) {
return deviceName;
}
return "unknown";
}

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

@ -8,6 +8,8 @@
#define WIFI_STRINGS_MAX_LEN 32
#define URI_MAX_SIZE 64
#define USER_DATA_MAX_SIZE 16
#define CA_CERT_MAX_SIZE 1500
#define DEVICE_NAME_MAX_LEN 64
typedef bool (*StorageReadFn)(const char* key, void* data, size_t size);
@ -23,7 +25,9 @@ typedef struct AppSettings {
char mqttPassword[USER_DATA_MAX_SIZE];
uint16_t mqttHbIntervalSec;
uint8_t mqttUseTls;
char caCert[CA_CERT_MAX_SIZE];
bool overrideDevName;
char customDevName[DEVICE_NAME_MAX_LEN];
} AppSettings;

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

@ -26,6 +26,7 @@ typedef struct SystemSettings {
char sntpAddr[URI_MAX_LEN];
char tzEnv[TZ_ENV_LEN];
char rsSchedule[RESTART_SCHEDULE_SIZE];
const char* deviceName; // set in the main component
} SystemSettings;

69
components/software/src/AppSettings.c

@ -10,6 +10,9 @@
#define KEY_MQTT_TLS "mqtls"
#define KEY_MQTT_USER "mquser"
#define KEY_MQTT_PASS "mqpass"
#define KEY_OVR_DEVNAME "ovdn"
#define KEY_CUSTOM_DEVNAME "cdn"
#define KEY_CA_CERT "cacert"
#define STORAGE_READ(_KEY, _NAME)\
storageRead(_KEY, &settings._NAME, sizeof(settings._NAME));
@ -19,13 +22,16 @@
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 = 60 * 15;
static const uint8_t DEFAULT_MQTT_TLS = 0;
static const char* DEFAULT_MQTT_USER = "";
static const char* DEFAULT_MQTT_PASS = "";
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 = 60 * 15;
static const uint8_t DEFAULT_MQTT_TLS = 0;
static const char* DEFAULT_MQTT_USER = "";
static const char* DEFAULT_MQTT_PASS = "";
static const bool DEFAULT_OV_DEVNAME = false;
static const char* DEFAULT_CUSTOM_DEVNAME = "esp-dev";
static const char* DEFAULT_CA_CERT = "-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----\n";
static StorageWriteFn storageWrite = NULL;
@ -46,13 +52,16 @@ static bool firstRun(void)
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 );
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 );
STORAGE_READ(KEY_OVR_DEVNAME , overrideDevName );
STORAGE_READ(KEY_CUSTOM_DEVNAME , customDevName );
STORAGE_READ(KEY_CA_CERT , caCert );
}
@ -87,11 +96,15 @@ void nxRestoreAppDefaultSettings(void)
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 );
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 );
strcpy(settings.caCert , DEFAULT_CA_CERT );
strcpy(settings.customDevName , DEFAULT_CUSTOM_DEVNAME);
settings.overrideDevName = DEFAULT_OV_DEVNAME;
nxWriteAppSettings();
}
@ -102,14 +115,16 @@ 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 );
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 );
STORAGE_WRITE(KEY_CA_CERT , caCert );
STORAGE_WRITE(KEY_CUSTOM_DEVNAME , customDevName );
STORAGE_WRITE(KEY_OVR_DEVNAME , overrideDevName );
settingsUpdatedCb();
}

42
components/software/src/AppSettingsApi.c

@ -15,23 +15,26 @@
#define API_KEY_MQTT_TLS "mqtls"
#define API_KEY_MQTT_USER "mquser"
#define API_KEY_MQTT_PASS "mqpass"
#define API_KEY_CA_CERT "cacert"
#define API_KEY_OV_DEVNAME "ovdn"
#define API_KEY_CUSTOM_DEVNAME "cdn"
#define UNUSED(x) (void)(x)
#define RESP_BUF_SIZE 1024
#define RESP_BUF_SIZE 2048
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);
printf("App 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);
strcpy(settings->mqttHost, value);
}
else if (strcmp(key, API_KEY_MQTT_API_URI) == 0) {
strcpy(settings->mqttApiUri, value);
@ -46,10 +49,19 @@ static void handleKvPair(const char* key, const char* value, const void* userDat
strcpy(settings->mqttPassword, value);
}
else if (strcmp(key, API_KEY_MQTT_HB_SEC) == 0) {
settings->mqttHbIntervalSec = atoi(value);
settings->mqttHbIntervalSec = atoi(value);
}
else if (strcmp(key, API_KEY_MQTT_TLS) == 0) {
settings->mqttUseTls = atoi(value);
settings->mqttUseTls = atoi(value);
}
else if (strcmp(key, API_KEY_CA_CERT) == 0) {
strcpy(settings->caCert, value);
}
else if (strcmp(key, API_KEY_CUSTOM_DEVNAME) == 0) {
strcpy(settings->customDevName, value);
}
else if (strcmp(key, API_KEY_OV_DEVNAME) == 0) {
settings->overrideDevName = atoi(value);
}
else {
fprintf(stderr, "Unknown key: %s\n", key);
@ -59,7 +71,7 @@ static void handleKvPair(const char* key, const char* value, const void* userDat
// --------- Public API --------- //
void nxApiGetAppSettings(const uint8_t* msg, size_t msgLen,
const char** response, size_t* respLen)
const char** response, size_t* respLen)
{
UNUSED(msg); UNUSED(msgLen);
memset(responseBuffer, 0, RESP_BUF_SIZE);
@ -72,14 +84,18 @@ void nxApiGetAppSettings(const uint8_t* msg, size_t msgLen,
"\"%s\":\"%s\", "
"\"%s\":%i, " // hb interval
"\"%s\":%i, "
"\"%s\":\"%s\" " // mqtt user
"\"%s\":\"%s\", " // mqtt user
"\"%s\":%i," // ovdn
"\"%s\":\"%s\"" // cdn
"}",
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
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 ,
API_KEY_OV_DEVNAME , settings->overrideDevName ,
API_KEY_CUSTOM_DEVNAME, settings->customDevName
);
*response = responseBuffer;
*respLen = strlen(responseBuffer);

7
components/software/src/KeyValueParser.c

@ -1,24 +1,25 @@
#include "KeyValueParser.h"
#include <stdio.h>
#include <stdlib.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];
char* value = calloc(1, strlen(string));
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) {
while (fscanf(stream, "%32[^=]=%2048[^&]%*c", key, value) == 2 && maxPairs > 0 && !done) {
handler(key, value, userData, &done);
maxPairs -= 1;
}
fclose(stream);
free(value);
}

42
components/software/src/SystemSettings.c

@ -54,15 +54,15 @@ static bool firstRun(void)
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_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);
storageRead(KEY_RS_SCHEDULE , settings.rsSchedule , RESTART_SCHEDULE_SIZE);
storageRead(KEY_WIFI_SSID , settings.wifiSsid , WIFI_STRINGS_MAX_LEN);
storageRead(KEY_WIFI_PASS , settings.wifiPassword , WIFI_STRINGS_MAX_LEN);
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);
storageRead(KEY_RS_SCHEDULE , settings.rsSchedule , RESTART_SCHEDULE_SIZE);
STORAGE_READ(KEY_WIFI_POWER_SAVE, wifiPowerSave);
STORAGE_READ(KEY_WIFI_USE_STATIC, useStaticAddr);
@ -115,18 +115,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_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 );
storageWrite(KEY_RS_SCHEDULE , settings.rsSchedule , RESTART_SCHEDULE_SIZE );
STORAGE_WRITE(KEY_WIFI_POWER_SAVE, wifiPowerSave);
STORAGE_WRITE(KEY_WIFI_USE_STATIC, useStaticAddr);
storageWrite(KEY_WIFI_SSID , settings.wifiSsid , WIFI_STRINGS_MAX_LEN );
storageWrite(KEY_WIFI_PASS , settings.wifiPassword , WIFI_STRINGS_MAX_LEN );
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 );
storageWrite(KEY_RS_SCHEDULE , settings.rsSchedule , RESTART_SCHEDULE_SIZE );
STORAGE_WRITE(KEY_WIFI_POWER_SAVE, wifiPowerSave );
STORAGE_WRITE(KEY_WIFI_USE_STATIC, useStaticAddr );
}
SystemSettings* nxGetSystemSettings(void)

31
components/software/src/SystemSettingsApi.c

@ -14,18 +14,19 @@
ssid=myssid&wpass=mypassword&wpwsave=1&wstatic=1&ip=3232237657&gw=3232237569&mask=4294967040
*/
#define API_KEY_SSID "ssid"
#define API_KEY_WPASS "wpass"
#define API_KEY_WPWSAVE "wpwsave"
#define API_KEY_USE_STATIC "wstatic"
#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_RS_SCHEDULE "rsch"
#define API_KEY_RESTORE "restore_default"
#define API_KEY_SSID "ssid"
#define API_KEY_WPASS "wpass"
#define API_KEY_WPWSAVE "wpwsave"
#define API_KEY_USE_STATIC "wstatic"
#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_RS_SCHEDULE "rsch"
#define API_KEY_DEVNAME "dn"
#define API_KEY_RESTORE "restore_default"
#define UNUSED(x) (void)(x)
@ -111,7 +112,8 @@ void nxApiGetSystemSettings(const uint8_t* msg, size_t msgLen,
"\"%s\":\"%i.%i.%i.%i\"," // dns
"\"%s\":\"%s\"," // sntp
"\"%s\":\"%s\"," // tz
"\"%s\":\"%s\"" // rsch
"\"%s\":\"%s\"," // rsch
"\"%s\":\"%s\"" // dn
"}",
API_KEY_SSID, settings->wifiSsid,
API_KEY_WPWSAVE, settings->wifiPowerSave ? "true" : "false",
@ -122,7 +124,8 @@ void nxApiGetSystemSettings(const uint8_t* msg, size_t msgLen,
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,
API_KEY_RS_SCHEDULE, settings->rsSchedule
API_KEY_RS_SCHEDULE, settings->rsSchedule,
API_KEY_DEVNAME, settings->deviceName
);
*response = responseBuffer;
*respLen = strlen(responseBuffer);

1
main/CMakeLists.txt

@ -12,4 +12,3 @@ 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)

55
main/Main.c

@ -24,10 +24,10 @@
#define TAG "MAIN"
#define SNTP_RETRIES 8
#define API_QOS 2
#define HB_QOS 1
static const char* DEVICE_NAME_PREFIX = "mqtrigger-";
extern const char ca_crt_start[] asm("_binary_ca_crt_start");
static const char* DEVICE_NAME_PREFIX = "mqt-";
static void startWifi(void);
static void onWifiConnected(void);
@ -38,6 +38,8 @@ static void onMqttDisconnected();
static void onMqttError();
static void onMqttMessage(const char* msg);
static void hbTask(void*);
static SystemSettings* systemSettings = NULL;
static AppSettings* appSettings = NULL;
static WifiSettings wifiSettings;
@ -53,7 +55,7 @@ static const MqTriggerHttpCallbacks httpCallbacks = {
.postSysSet = nxApiUpdateSystemSettings,
.getAppSet = nxApiGetAppSettings,
.postAppSet = nxApiUpdateAppSettings
// .postCmd = nxApiHandleLedCmd
// .postCmd = nxApiHandleCmd
};
void app_main(void)
@ -81,8 +83,6 @@ void app_main(void)
static void startWifi(void)
{
strcpy(systemSettings->wifiSsid, "cintra");
strcpy(systemSettings->wifiPassword, "fffefdfcfb");
systemSettings->useStaticAddr = true;
systemSettings->ip4addr[3] = 91;
@ -106,15 +106,22 @@ static void startWifi(void)
static void onWifiConnected(void)
{
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 ? ca_crt_start : NULL;
mqttSettings.caCrt = appSettings->mqttUseTls ? appSettings->caCert : NULL;
mqttSettings.messageCb = onMqttMessage;
mqttSettings.connectedCb = onMqttConnected;
mqttSettings.disconnectedCb = onMqttDisconnected;
@ -147,7 +154,10 @@ static void onWifiError()
static void onMqttConnected()
{
ESP_LOGI(TAG, "MQTT CONNECTED");
nxUpdateStatus(STATUS_OK);
if (nxMqttSubscribe(appSettings->mqttApiUri, API_QOS)) {
xTaskCreate(&hbTask, "hb_task", 2048, NULL, 1, NULL);
nxUpdateStatus(STATUS_OK);
}
}
static void onMqttDisconnected()
@ -161,3 +171,32 @@ 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) {
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);
nxMqttPublish(appSettings->mqttHbUri, HB_QOS, hbMessage, strlen(hbMessage), 1);
vTaskDelay(appSettings->mqttHbIntervalSec*1000 / portTICK_PERIOD_MS);
}
}

22
main/static/app.html

@ -48,6 +48,27 @@
</label>
</p>
<p>
<label class="label">Override device name:
<select class="select" name="ovdn">
<option value="0">No</option>
<option value="1">Yes</option>
</select>
</label>
</p>
<p>
<label>Custom device name:
<input type="text" name="cdn">
</label>
</p>
<p>
<label>Certificate data (begin to end, tags included):
<p><textarea name="cacert"></textarea></p>
</label>
</p>
<p>
<button type="button" class="send">SEND</button>
</p>
@ -61,7 +82,6 @@
</div>
<h3>Restore defaults</h3>
<form id="restore-form" action="/app">
<input type="hidden" name="restore_default" value="1" />

28
main/static/index.js

@ -1,3 +1,5 @@
var valuesCache = {};
function ip2int(ip) {
return ip.split('.').reduce(function(ipInt, octet) { return (ipInt<<8) + parseInt(octet, 10)}, 0) >>> 0;
}
@ -70,16 +72,37 @@ function loadContent(url) {
}
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;
}
if (valuesCache[srcUrl]) {
console.log("Source values already cached: " + valuesCache[srcUrl]);
//TODO: dry
let obj = JSON.parse(valuesCache[srcUrl]);
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];
if (input.hasAttribute('data-onset')) {
eval(input.getAttribute("data-onset"));
}
}
}
}
continue;
}
console.log("Getting values from " + srcUrl);
let http = new XMLHttpRequest();
http.open('GET', srcUrl, true);
http.open('GET', srcUrl, false);
//Send the proper header information along with the request
//http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
@ -87,6 +110,9 @@ function loadValues(element) {
http.onreadystatechange = function () {//Call a function when the state changes.
if (http.readyState == 4 && http.status == 200) {
console.log("Values received: " + http.responseText);
valuesCache[srcUrl] = http.responseText;
//TODO: dry
let obj = JSON.parse(http.responseText);
if (obj) {
for (var key of Object.keys(obj)) {

6
main/static/min/app.html

@ -7,4 +7,8 @@
<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>
<option value=1>Yes</select></label><p><label class=label>Override device name:
<select class=select name=ovdn>
<option value=0>No
<option value=1>Yes</select></label><p><label>Custom device name:
<input name=cdn></label><p><label>Certificate data (begin to end, tags included):<p><textarea name=cacert></textarea></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>

7
main/static/min/index.js

@ -1,4 +1,4 @@
function ip2int(ip){return ip.split('.').reduce(function(ipInt,octet){return(ipInt<<8)+parseInt(octet,10)},0)>>>0;}
var valuesCache={};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){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;}}
else{console.log("Ignoring "+element.name);}}});console.log("params: "+params);postParams(form.getAttribute('action'),params);console.log("Form sent");}
@ -6,7 +6,10 @@ function initFormSendButtons(form){const btns=form.querySelectorAll('.send');for
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])
if(valuesCache[srcUrl]){console.log("Source values already cached: "+valuesCache[srcUrl]);let obj=JSON.parse(valuesCache[srcUrl]);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];if(input.hasAttribute('data-onset')){eval(input.getAttribute("data-onset"));}}}}
continue;}
console.log("Getting values from "+srcUrl);let http=new XMLHttpRequest();http.open('GET',srcUrl,false);http.onreadystatechange=function(){if(http.readyState==4&&http.status==200){console.log("Values received: "+http.responseText);valuesCache[srcUrl]=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];if(input.hasAttribute('data-onset')){eval(input.getAttribute("data-onset"));}}}}}}
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);}}

3
main/static/min/sys.html

@ -1,4 +1,5 @@
<h1>Device system management</h1><form id=net-form action=/sys data-values-src=/sys><h3>Network</h3><p><label>WiFi SSID:
<h1>Device system management</h1><form id=net-form action=/sys data-values-src=/sys><p><label>Device name:
<input name=dn readonly data-ignore></label><h3>Network</h3><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>

6
main/static/sys.html

@ -1,6 +1,12 @@
<h1>Device system management</h1>
<form id="net-form" action="/sys" data-values-src="/sys">
<p>
<label>Device name:
<input type="text" name="dn" readonly="readonly" data-ignore>
</label>
</p>
<h3>Network</h3>
<p>
<label>WiFi SSID:

Loading…
Cancel
Save