38 changed files with 5390 additions and 2046 deletions
@ -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_ */ |
||||
@ -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_ */ |
||||
@ -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);
|
||||
// }
|
||||
} |
||||
@ -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); |
||||
} |
||||
@ -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_ */ |
||||
@ -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_ */ |
||||
@ -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; |
||||
} |
||||
@ -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(); |
||||
} |
||||
|
||||
@ -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); |
||||
} |
||||
@ -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_ */ |
||||
@ -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_ */ |
||||
@ -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); |
||||
} |
||||
@ -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(); |
||||
} |
||||
|
||||
@ -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> |
||||
@ -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----- |
||||
@ -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 © Łukasz Chodyła 2022 |
||||
</p> |
||||
</div> |
||||
</footer> |
||||
|
||||
<script> |
||||
initNavButtons(); |
||||
document.getElementById('app-btn').click(); |
||||
</script> |
||||
|
||||
</body> |
||||
|
||||
</html> |
||||
@ -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); |
||||
} |
||||
} |
||||
@ -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> |
||||
File diff suppressed because one or more lines are too long
@ -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 © Łukasz Chodyła 2022</div></footer><script>initNavButtons();document.getElementById('app-btn').click();</script> |
||||
@ -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);}} |
||||
@ -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> |
||||
@ -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 |
||||
@ -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…
Reference in new issue