33 changed files with 13658 additions and 9 deletions
@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's |
||||
# CMakeLists in this exact order for cmake to work correctly |
||||
cmake_minimum_required(VERSION 3.5) |
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake) |
||||
project(mqtrigger) |
||||
@ -1,8 +1,5 @@
|
||||
MIT License |
||||
Copyright (c) <year> <copyright holders> |
||||
Code in this repository is in the Public Domain (or CC0 licensed, at your option.) |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
||||
Unless required by applicable law or agreed to in writing, this |
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR |
||||
CONDITIONS OF ANY KIND, either express or implied. |
||||
|
||||
@ -1,3 +1,11 @@
|
||||
# mqtrigger |
||||
ESP-IDF template app |
||||
==================== |
||||
|
||||
Remote GPIO trigger |
||||
This is a template application to be used with [Espressif IoT Development Framework](https://github.com/espressif/esp-idf). |
||||
|
||||
Please check [ESP-IDF docs](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for getting started instructions. |
||||
|
||||
*Code in this repository is in the Public Domain (or CC0 licensed, at your option.) |
||||
Unless required by applicable law or agreed to in writing, this |
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR |
||||
CONDITIONS OF ANY KIND, either express or implied.* |
||||
|
||||
@ -0,0 +1,15 @@
|
||||
idf_component_register( |
||||
SRCS |
||||
src/Storage.c |
||||
src/Wifi.c |
||||
src/MqTriggerHttpServer.c |
||||
src/HttpHelpers.c |
||||
# src/Buttons.c |
||||
|
||||
INCLUDE_DIRS ./include |
||||
PRIV_INCLUDE_DIRS ./src |
||||
|
||||
REQUIRES |
||||
nvs_flash |
||||
esp_http_server |
||||
) |
||||
@ -0,0 +1,10 @@
|
||||
#
|
||||
# Component Makefile
|
||||
#
|
||||
# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default,
|
||||
# this will take the sources in the src/ directory, compile them and link them into
|
||||
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
|
||||
# please read the SDK documents if you need to do this.
|
||||
#
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS := .
|
||||
@ -0,0 +1,20 @@
|
||||
#ifndef COMPONENTS_FIRMWARE_NXBUTTONS_H_ |
||||
#define COMPONENTS_FIRMWARE_NXBUTTONS_H_ |
||||
|
||||
#include <stdint.h> |
||||
#include <stdbool.h> |
||||
|
||||
#define BUTTON_SERVICE_ID 1 |
||||
#define BUTTON_SERVICE_GPIO_NUM 23 |
||||
|
||||
typedef void (*ButtonEventHandler)(uint8_t btn); |
||||
|
||||
void nxInitButtons(void); |
||||
|
||||
void nxSetOnPressHandler(ButtonEventHandler handler); |
||||
|
||||
void nxSetOnReleaseHandler(ButtonEventHandler handler); |
||||
|
||||
bool nxIsButtonPressed(uint8_t btn); |
||||
|
||||
#endif /* COMPONENTS_FIRMWARE_NXBUTTONS_H_ */ |
||||
@ -0,0 +1,23 @@
|
||||
#ifndef COMPONENTS_FIRMWARE_INCLUDE_FIRMWARE_MQTRIGGERHTTPSERVER_H_ |
||||
#define COMPONENTS_FIRMWARE_INCLUDE_FIRMWARE_MQTRIGGERHTTPSERVER_H_ |
||||
|
||||
#include <stdbool.h> |
||||
#include <stddef.h> |
||||
#include <inttypes.h> |
||||
|
||||
typedef void(*RequestCallback)(const uint8_t* content, size_t ctLen, |
||||
const char** response, size_t* respLen); |
||||
|
||||
typedef struct MqTriggerHttpCallbacks { |
||||
RequestCallback getRoot; |
||||
RequestCallback getSysSet; |
||||
RequestCallback getSysSetForm; |
||||
RequestCallback postSysSet; |
||||
RequestCallback reboot; |
||||
} MqTriggerHttpCallbacks; |
||||
|
||||
bool nxStartMqTriggerHttpServer(void); |
||||
void nxStopMqTriggerHttpServer(); |
||||
void nxSetMqTriggerHttpCallbacks(MqTriggerHttpCallbacks* cbs); |
||||
|
||||
#endif /* COMPONENTS_FIRMWARE_INCLUDE_FIRMWARE_MQTRIGGERHTTPSERVER_H_ */ |
||||
@ -0,0 +1,13 @@
|
||||
#ifndef COMPONENTS_FIRMWARE_STORAGE_H_ |
||||
#define COMPONENTS_FIRMWARE_STORAGE_H_ |
||||
|
||||
#include <stddef.h> |
||||
#include <stdbool.h> |
||||
|
||||
void nxInitStorage(void); |
||||
|
||||
void nxStorageWrite(const char* key, const void* data, size_t size); |
||||
|
||||
bool nxStorageRead(const char* key, void* data, size_t size); |
||||
|
||||
#endif /* COMPONENTS_FIRMWARE_STORAGE_H_ */ |
||||
@ -0,0 +1,35 @@
|
||||
#ifndef COMPONENTS_FIRMWARE_WIFI_H |
||||
#define COMPONENTS_FIRMWARE_WIFI_H |
||||
|
||||
#include <stdint.h> |
||||
#include <stddef.h> |
||||
#include <stdbool.h> |
||||
|
||||
typedef enum WifiMode { |
||||
NX_WIFI_MODE_STA = 0, |
||||
NX_WIFI_MODE_AP = 1 |
||||
} WifiMode; |
||||
|
||||
typedef struct WifiSettings { |
||||
char* wname; //ssid
|
||||
char* wpass; |
||||
const char* devicePrefix; |
||||
bool* usePowerSave; |
||||
bool* useStaticAddr; |
||||
uint8_t* ip4addr; |
||||
uint8_t* ip4gw; |
||||
uint8_t* ip4mask; |
||||
WifiMode mode; |
||||
} WifiSettings; |
||||
|
||||
typedef void (*WifiCallback)(); |
||||
|
||||
typedef bool (*StorageReadFn)(const char* key, void* data, size_t size); |
||||
|
||||
bool nxInitWifi(WifiSettings* wifiSettings); |
||||
|
||||
void nxSetWifiConnectedCallback(WifiCallback callback); |
||||
void nxSetWifiErrorCallback(WifiCallback callback); |
||||
|
||||
|
||||
#endif // COMPONENTS_FIRMWARE_WIFI_H
|
||||
@ -0,0 +1,142 @@
|
||||
#include "nx/firmware/Buttons.h" |
||||
|
||||
#include <esp_log.h> |
||||
#include <driver/gpio.h> |
||||
|
||||
#include <freertos/FreeRTOS.h> |
||||
#include <freertos/task.h> |
||||
#include <freertos/queue.h> |
||||
#include <stdlib.h> |
||||
#include <stdbool.h> |
||||
|
||||
#define TAG "NX_BTN" |
||||
|
||||
#define EVENT_QUEUE_SIZE 5 |
||||
#define ESP_INTR_FLAG_DEFAULT 0 |
||||
|
||||
#define RTOS_TASK_NAME "button_queue_task" |
||||
#define RTOS_TASK_STACK_SIZE 4096 // stack overflow when less
|
||||
#define RTOS_TASK_PRIORITY 10 |
||||
|
||||
typedef struct Button |
||||
{ |
||||
uint8_t id; |
||||
uint8_t btnGpio; |
||||
uint8_t pullGpio; // disabled when 0
|
||||
bool inverted; // triggered by gnd by default
|
||||
bool isPressed; |
||||
bool isBouncing; |
||||
uint32_t debounceMs; |
||||
} Button; |
||||
|
||||
static Button buttons[] = { |
||||
{ |
||||
.id = BUTTON_SERVICE_ID, |
||||
.btnGpio = BUTTON_SERVICE_GPIO_NUM, |
||||
.pullGpio = 0, |
||||
.inverted = false, |
||||
.isPressed = false, |
||||
.isBouncing = false, |
||||
.debounceMs = 50 |
||||
} |
||||
}; |
||||
|
||||
const size_t buttonCount = sizeof(buttons)/sizeof(buttons[0]); |
||||
|
||||
static ButtonEventHandler pressHandler = NULL; |
||||
static ButtonEventHandler releaseHandler = NULL; |
||||
|
||||
static xQueueHandle gpioEventQueue = NULL; |
||||
|
||||
static void IRAM_ATTR gpioInterruptHandler(void* arg) |
||||
{ |
||||
Button* button = (Button*)arg; |
||||
if (!button->isBouncing) { |
||||
xQueueSendFromISR(gpioEventQueue, &button, NULL); |
||||
} |
||||
} |
||||
|
||||
static void buttonQueueTask(void* arg) |
||||
{ |
||||
Button* button = NULL; |
||||
for(;;) { |
||||
if(xQueueReceive(gpioEventQueue, &button, portMAX_DELAY)) { |
||||
ESP_LOGI(TAG, "Interrupt by GPIO %i", button->btnGpio); |
||||
|
||||
if (button->debounceMs) { |
||||
button->isBouncing = true; |
||||
vTaskDelay(button->debounceMs / portTICK_RATE_MS); |
||||
} |
||||
button->isBouncing = false; |
||||
|
||||
uint8_t level = gpio_get_level(button->btnGpio); |
||||
bool wasPressed = button->isPressed; |
||||
button->isPressed = button->inverted ? level : !level; |
||||
|
||||
if (pressHandler && button->isPressed && !wasPressed) { |
||||
pressHandler(button->id); |
||||
} |
||||
else if (releaseHandler && !button->isPressed && wasPressed) { |
||||
releaseHandler(button->id); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// --------- Public API --------- //
|
||||
|
||||
void nxInitButtons(void) |
||||
{ |
||||
gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT); |
||||
|
||||
for (size_t i = 0; i < buttonCount; ++i) { |
||||
Button* button = &buttons[i]; |
||||
gpio_reset_pin(button->btnGpio); |
||||
gpio_set_direction(button->btnGpio, GPIO_MODE_INPUT); |
||||
if (button->pullGpio) { |
||||
gpio_reset_pin(button->pullGpio); |
||||
gpio_set_direction(button->pullGpio, GPIO_MODE_OUTPUT); |
||||
gpio_set_level(button->pullGpio, button->inverted ? 0 : 1); |
||||
} |
||||
else if (button->inverted){ |
||||
gpio_pullup_en(button->btnGpio); |
||||
gpio_set_pull_mode(button->btnGpio, GPIO_PULLDOWN_ONLY); |
||||
button->isPressed = gpio_get_level(button->btnGpio); |
||||
} |
||||
else { |
||||
gpio_pullup_en(button->btnGpio); |
||||
gpio_set_pull_mode(button->btnGpio, GPIO_PULLUP_ONLY); |
||||
button->isPressed = !gpio_get_level(button->btnGpio); |
||||
} |
||||
|
||||
gpio_set_intr_type(button->btnGpio, GPIO_INTR_ANYEDGE); |
||||
gpio_isr_handler_add(button->btnGpio, gpioInterruptHandler, button); |
||||
} |
||||
|
||||
gpioEventQueue = xQueueCreate(EVENT_QUEUE_SIZE, sizeof(Button*)); |
||||
xTaskCreate(buttonQueueTask, RTOS_TASK_NAME, RTOS_TASK_STACK_SIZE, |
||||
NULL, RTOS_TASK_PRIORITY, NULL); |
||||
} |
||||
|
||||
void nxSetOnPressHandler(ButtonEventHandler handler) |
||||
{ |
||||
pressHandler = handler; |
||||
} |
||||
|
||||
void nxSetOnReleaseHandler(ButtonEventHandler handler) |
||||
{ |
||||
releaseHandler = handler; |
||||
} |
||||
|
||||
bool nxIsButtonPressed(uint8_t btn) |
||||
{ |
||||
for(size_t i = 0; i < buttonCount; ++i) { |
||||
if (btn == buttons[i].id) { |
||||
return buttons[i].isPressed; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
|
||||
|
||||
@ -0,0 +1,49 @@
|
||||
#include "HttpHelpers.h" |
||||
|
||||
#include <esp_log.h> |
||||
#include <math.h> |
||||
|
||||
#define TAG "NX_HTTP_H" |
||||
|
||||
void nxAddCorsHeaders(httpd_req_t* req) |
||||
{ |
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); |
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Headers", "*"); |
||||
} |
||||
|
||||
esp_err_t nxExecRequestCallback(httpd_req_t*req, RequestCallback callback) |
||||
{ |
||||
if (callback) { |
||||
uint8_t content[REQ_CONTENT_MAX_SIZE]; |
||||
memset(content, 0x00, REQ_CONTENT_MAX_SIZE); |
||||
size_t cntSize = fmin(req->content_len, sizeof(content)); |
||||
|
||||
ESP_LOGI(TAG, "Request content size: %i", cntSize); |
||||
|
||||
if (cntSize > 0) { |
||||
int ret = httpd_req_recv(req, (char*)content, cntSize); |
||||
if (ret <= 0) { /* 0 return value indicates connection closed */ |
||||
ESP_LOGE(TAG, "httpd_req_recv error %i", ret); |
||||
if (ret == HTTPD_SOCK_ERR_TIMEOUT) { |
||||
/* In case of timeout one can choose to retry calling
|
||||
* httpd_req_recv(), but to keep it simple, here we |
||||
* respond with an HTTP 408 (Request Timeout) error */ |
||||
httpd_resp_send_408(req); |
||||
} |
||||
/* In case of error, returning ESP_FAIL will
|
||||
* ensure that the underlying socket is closed */ |
||||
return ESP_FAIL; |
||||
} |
||||
} |
||||
|
||||
const char* respBody = NULL; |
||||
size_t respBodySize = 0; |
||||
callback(content, cntSize, &respBody, &respBodySize); |
||||
httpd_resp_send(req, respBody, respBodySize); |
||||
} |
||||
else { |
||||
const char resp[] = "No callback set\n"; |
||||
httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN); |
||||
} |
||||
return ESP_OK; |
||||
} |
||||
@ -0,0 +1,29 @@
|
||||
#ifndef COMPONENTS_FIRMWARE_SRC_HTTPHELPERS_H_ |
||||
#define COMPONENTS_FIRMWARE_SRC_HTTPHELPERS_H_ |
||||
|
||||
#include <esp_http_server.h> |
||||
|
||||
typedef void(*RequestCallback)(const uint8_t* content, size_t ctLen, |
||||
const char** response, size_t* respLen); |
||||
|
||||
#define REQ_CONTENT_MAX_SIZE 1024 |
||||
|
||||
// Request handler helper macro
|
||||
#define ADD_HTTP_HANDLER(_URI, _NAME, _METHOD)\ |
||||
static esp_err_t uri_handler_##_NAME(httpd_req_t* req)\
|
||||
{\
|
||||
nxAddCorsHeaders(req);\
|
||||
return nxExecRequestCallback(req, callbacks && callbacks->_NAME \
|
||||
? callbacks->_NAME : NULL);\
|
||||
}\
|
||||
static httpd_uri_t uri_##_NAME = {\
|
||||
.uri = _URI, .method = _METHOD,\
|
||||
.handler = uri_handler_##_NAME\
|
||||
}; |
||||
|
||||
void nxAddCorsHeaders(httpd_req_t* req); |
||||
|
||||
esp_err_t nxExecRequestCallback(httpd_req_t*req, |
||||
RequestCallback callback); |
||||
|
||||
#endif /* COMPONENTS_FIRMWARE_SRC_HTTPHELPERS_H_ */ |
||||
@ -0,0 +1,80 @@
|
||||
#include "nx/firmware/MqTriggerHttpServer.h" |
||||
#include "HttpHelpers.h" |
||||
|
||||
#include <esp_log.h> |
||||
|
||||
#define TAG "NX_HTTP" |
||||
|
||||
static httpd_handle_t server = NULL; |
||||
static MqTriggerHttpCallbacks* callbacks = NULL; |
||||
|
||||
// -- URI handlers --
|
||||
|
||||
ADD_HTTP_HANDLER("/", getRoot, 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) |
||||
|
||||
static esp_err_t uriOptionsHandler(httpd_req_t* req) |
||||
{ |
||||
nxAddCorsHeaders(req); |
||||
const char resp[] = ""; |
||||
httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN); |
||||
return ESP_OK; |
||||
} |
||||
|
||||
static httpd_uri_t uriOptions = { |
||||
.uri = "*", |
||||
.method = HTTP_OPTIONS, |
||||
.handler = uriOptionsHandler |
||||
}; |
||||
|
||||
void registerMqTriggerHttpHandlers(void) |
||||
{ |
||||
httpd_register_uri_handler(server, &uriOptions); |
||||
|
||||
httpd_register_uri_handler(server, &uri_getRoot); |
||||
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); |
||||
} |
||||
|
||||
|
||||
// --------- Public API --------- //
|
||||
|
||||
bool nxStartMqTriggerHttpServer(void) |
||||
{ |
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG(); |
||||
|
||||
// configure server here:
|
||||
config.server_port = 80; |
||||
config.max_uri_handlers = 8; |
||||
config.uri_match_fn = httpd_uri_match_wildcard; |
||||
// ...
|
||||
|
||||
if (server != NULL) { |
||||
nxStopMqTriggerHttpServer(); |
||||
} |
||||
|
||||
if (httpd_start(&server, &config) == ESP_OK) { |
||||
registerMqTriggerHttpHandlers(); |
||||
} |
||||
return server != NULL; |
||||
} |
||||
|
||||
void nxStopMqTriggerHttpServer() |
||||
{ |
||||
if (server) { |
||||
httpd_stop(server); |
||||
server = NULL; |
||||
} |
||||
} |
||||
|
||||
void nxSetMqTriggerHttpCallbacks(MqTriggerHttpCallbacks* cbs) |
||||
{ |
||||
callbacks = cbs; |
||||
} |
||||
|
||||
|
||||
@ -0,0 +1,87 @@
|
||||
#include "nx/firmware/Storage.h" |
||||
|
||||
#include <esp_log.h> |
||||
#include <esp_spi_flash.h> |
||||
#include <nvs_flash.h> |
||||
#include <nvs.h> |
||||
|
||||
#define TAG "NX_STORAGE" |
||||
|
||||
#define NX_STORAGE_NAMESPACE "NX_STORAGE" |
||||
|
||||
static void initializeNvs() |
||||
{ |
||||
ESP_LOGI(TAG, "Initializing NVS"); |
||||
esp_err_t ret = nvs_flash_init(); |
||||
|
||||
if (ret == ESP_OK) { |
||||
ESP_LOGI(TAG, "NVS initialized"); |
||||
} |
||||
else if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { |
||||
ESP_LOGI(TAG, "NVS full, erasing"); |
||||
ESP_ERROR_CHECK(nvs_flash_erase()); |
||||
ret = nvs_flash_init(); |
||||
} |
||||
ESP_ERROR_CHECK(ret); |
||||
} |
||||
|
||||
|
||||
// --------- Public API --------- //
|
||||
|
||||
void nxInitStorage(void) |
||||
{ |
||||
initializeNvs(); |
||||
} |
||||
|
||||
void nxStorageWrite(const char* key, const void* data, size_t size) |
||||
{ |
||||
ESP_LOGI(TAG, "Writing %i bytes as %s", size, key); |
||||
|
||||
nvs_handle_t nvsHandle; |
||||
esp_err_t err; |
||||
|
||||
err = nvs_open(NX_STORAGE_NAMESPACE, NVS_READWRITE, &nvsHandle); |
||||
if (err != ESP_OK) { |
||||
ESP_LOGE(TAG, "Cannot open NVS"); |
||||
return; |
||||
} |
||||
// Write
|
||||
err = nvs_set_blob(nvsHandle, key, data, size); |
||||
if (err != ESP_OK) { |
||||
ESP_LOGE(TAG, "Cannot write to NVS"); |
||||
} |
||||
else { |
||||
ESP_LOGI(TAG, "Write OK"); |
||||
} |
||||
// Commit
|
||||
err = nvs_commit(nvsHandle); |
||||
if (err != ESP_OK) { |
||||
ESP_LOGE(TAG, "Cannot commit to NVS"); |
||||
} |
||||
// Close
|
||||
nvs_close(nvsHandle); |
||||
} |
||||
|
||||
bool nxStorageRead(const char* key, void* data, size_t size) |
||||
{ |
||||
ESP_LOGI(TAG, "Reading %i bytes as %s", size, key); |
||||
|
||||
nvs_handle_t my_handle; |
||||
esp_err_t err; |
||||
|
||||
err = nvs_open(NX_STORAGE_NAMESPACE, NVS_READWRITE, &my_handle); |
||||
if (err != ESP_OK) { |
||||
ESP_LOGE(TAG, "Cannot open NVS"); |
||||
return false; |
||||
} |
||||
|
||||
err = nvs_get_blob(my_handle, key, data, &size); |
||||
if (err != ESP_OK) { |
||||
ESP_LOGE(TAG, "Cannot get NVS data"); |
||||
return false; |
||||
} |
||||
|
||||
// Close
|
||||
nvs_close(my_handle); |
||||
return true; |
||||
} |
||||
@ -0,0 +1,332 @@
|
||||
#include "nx/firmware/Wifi.h" |
||||
|
||||
#include "freertos/FreeRTOS.h" |
||||
#include "freertos/task.h" |
||||
#include "freertos/event_groups.h" |
||||
#include "esp_system.h" |
||||
#include "esp_wifi.h" |
||||
#include "esp_event.h" |
||||
#include "esp_log.h" |
||||
#include "nvs_flash.h" |
||||
|
||||
#include "lwip/err.h" |
||||
#include "lwip/sys.h" |
||||
#include "lwip/dns.h" |
||||
#include "lwip/inet.h" |
||||
#include "lwip/sockets.h" |
||||
|
||||
#define TAG "WIFI" |
||||
|
||||
#define WIFI_MAXIMUM_RETRY -1 |
||||
#define WIFI_RECONNECT_DELAY_SEC 15 |
||||
#define WIFI_AP_CHANNEL 11 |
||||
#define WIFI_MAX_CONNECTION 3 |
||||
|
||||
/* The event group allows multiple bits for each event, but we only care about two events:
|
||||
* - we are connected to the AP with an IP |
||||
* - we failed to connect after the maximum amount of retries */ |
||||
#define WIFI_CONNECTED_BIT BIT0 |
||||
#define WIFI_FAIL_BIT BIT1 |
||||
|
||||
#define WIFI_AP_PASSWORD "" |
||||
|
||||
#define DEVICE_NAME_MAX_LEN 32 |
||||
|
||||
static const int POWER_SAVE_AP_BEACON_INTERVAL = 10; |
||||
|
||||
static EventGroupHandle_t wifiEventGroup; |
||||
|
||||
static int retryCount = 0; |
||||
|
||||
static WifiSettings* settings = NULL; |
||||
|
||||
static WifiCallback errorCallback = NULL; |
||||
static WifiCallback connectedCallback = NULL; |
||||
|
||||
static char deviceName[DEVICE_NAME_MAX_LEN]; |
||||
|
||||
static bool initWifiStation(void); |
||||
|
||||
static bool initWifiAp(void); |
||||
|
||||
// --------- Public API --------- //
|
||||
|
||||
bool nxInitWifi(WifiSettings* wifiSettings) |
||||
{ |
||||
settings = wifiSettings; |
||||
if (settings->mode == NX_WIFI_MODE_AP) { |
||||
return initWifiAp(); |
||||
} |
||||
return initWifiStation(); |
||||
} |
||||
|
||||
void nxSetWifiConnectedCallback(WifiCallback callback) |
||||
{ |
||||
connectedCallback = callback; |
||||
} |
||||
|
||||
void nxSetWifiErrorCallback(WifiCallback callback) |
||||
{ |
||||
errorCallback = callback; |
||||
} |
||||
|
||||
|
||||
// ------------------------------ //
|
||||
|
||||
|
||||
static void generateDeviceName() |
||||
{ |
||||
ESP_LOGI(TAG, "Generating device name with prefix %s", settings->devicePrefix); |
||||
|
||||
uint8_t mac[6]; |
||||
esp_read_mac(mac, 0); // 0 means WiFi base MAC address
|
||||
sprintf(deviceName, "%s%x%x%x%x%x%x", settings->devicePrefix, |
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); |
||||
ESP_LOGI(TAG, "Device name generated: %s", deviceName); |
||||
} |
||||
|
||||
static void handleStaEvents(esp_event_base_t event_base, int32_t event_id, void* event_data) |
||||
{ |
||||
ESP_LOGI(TAG, "WiFi STA event received; event ID: %i", event_id); |
||||
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { |
||||
esp_wifi_connect(); |
||||
} |
||||
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { |
||||
ESP_LOGE(TAG, "Cannot connect to the AP"); |
||||
if (errorCallback) { |
||||
errorCallback(); |
||||
} |
||||
|
||||
if (WIFI_MAXIMUM_RETRY < 0 || retryCount < WIFI_MAXIMUM_RETRY) { |
||||
ESP_LOGI(TAG, "Sleeping %i seconds before AP reconnection attempt", WIFI_RECONNECT_DELAY_SEC); |
||||
vTaskDelay(WIFI_RECONNECT_DELAY_SEC * 1000 / portTICK_PERIOD_MS); |
||||
|
||||
ESP_LOGI(TAG, "AP reconnection..."); |
||||
esp_wifi_connect(); |
||||
retryCount++; |
||||
} |
||||
else { |
||||
xEventGroupSetBits(wifiEventGroup, WIFI_FAIL_BIT); |
||||
} |
||||
} |
||||
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { |
||||
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; |
||||
ESP_LOGI(TAG, "IP address acquired: " IPSTR, IP2STR(&event->ip_info.ip)); |
||||
retryCount = 0; |
||||
if (connectedCallback) { |
||||
connectedCallback(); |
||||
} |
||||
xEventGroupSetBits(wifiEventGroup, WIFI_CONNECTED_BIT); |
||||
} |
||||
} |
||||
|
||||
static void handleApEvents(esp_event_base_t event_base, int32_t event_id, void* event_data) |
||||
{ |
||||
ESP_LOGI(TAG, "WiFi AP event received; event ID: %i", event_id); |
||||
|
||||
if (event_id == WIFI_EVENT_AP_STACONNECTED) { |
||||
wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data; |
||||
ESP_LOGI(TAG, "Station "MACSTR" joined, AID=%d", |
||||
MAC2STR(event->mac), event->aid); |
||||
} else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) { |
||||
wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data; |
||||
ESP_LOGI(TAG, "Station "MACSTR" left, AID=%d", |
||||
MAC2STR(event->mac), event->aid); |
||||
} |
||||
} |
||||
|
||||
static void eventHandler(void* arg, esp_event_base_t event_base, |
||||
int32_t event_id, void* event_data) |
||||
{ |
||||
ESP_LOGI(TAG, "Handling WiFi event in mode %i", settings->mode); |
||||
|
||||
if (settings->mode == NX_WIFI_MODE_STA) { |
||||
handleStaEvents(event_base, event_id, event_data); |
||||
} |
||||
else { |
||||
handleApEvents(event_base, event_id, event_data); |
||||
} |
||||
} |
||||
|
||||
|
||||
static bool initWifiAp(void) |
||||
{ |
||||
generateDeviceName(NX_WIFI_MODE_AP); |
||||
ESP_LOGI(TAG, "Initializing WiFi AP"); |
||||
|
||||
ESP_ERROR_CHECK(esp_netif_init()); |
||||
ESP_ERROR_CHECK(esp_event_loop_create_default()); |
||||
esp_netif_create_default_wifi_ap(); |
||||
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); |
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg)); |
||||
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, |
||||
ESP_EVENT_ANY_ID, |
||||
&eventHandler, |
||||
NULL, |
||||
NULL)); |
||||
|
||||
wifi_config_t wifiConfig = { |
||||
.ap = { |
||||
.ssid_len = strlen(deviceName), |
||||
.channel = WIFI_AP_CHANNEL, |
||||
.password = WIFI_AP_PASSWORD, |
||||
.max_connection = WIFI_MAX_CONNECTION, |
||||
.authmode = WIFI_AUTH_WPA_WPA2_PSK |
||||
}, |
||||
}; |
||||
strcpy((char*)(wifiConfig.ap.ssid), deviceName); |
||||
|
||||
if (strlen((const char*)wifiConfig.ap.password) == 0) { |
||||
wifiConfig.ap.authmode = WIFI_AUTH_OPEN; |
||||
} |
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); |
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifiConfig)); |
||||
ESP_ERROR_CHECK(esp_wifi_start()); |
||||
|
||||
ESP_LOGI(TAG, "WiFi AP initialization finished. SSID: %s password: %s channel: %d", |
||||
wifiConfig.ap.ssid, wifiConfig.ap.password, wifiConfig.ap.channel); |
||||
return true; |
||||
} |
||||
|
||||
static void configStaticApAddress(esp_netif_t* sta) |
||||
{ |
||||
ESP_LOGI(TAG, "Setting static IP address"); |
||||
esp_netif_dhcpc_stop(sta); |
||||
esp_netif_ip_info_t ip_info; |
||||
|
||||
IP4_ADDR(&ip_info.ip, |
||||
settings->ip4addr[0], |
||||
settings->ip4addr[1], |
||||
settings->ip4addr[2], |
||||
settings->ip4addr[3] |
||||
); |
||||
IP4_ADDR(&ip_info.gw, |
||||
settings->ip4gw[0], |
||||
settings->ip4gw[1], |
||||
settings->ip4gw[2], |
||||
settings->ip4gw[3] |
||||
); |
||||
IP4_ADDR(&ip_info.netmask, |
||||
settings->ip4mask[0], |
||||
settings->ip4mask[1], |
||||
settings->ip4mask[2], |
||||
settings->ip4mask[3] |
||||
); |
||||
ESP_LOGI(TAG, "Calling esp_netif_set_ip_info"); |
||||
esp_netif_set_ip_info(sta, &ip_info); |
||||
} |
||||
|
||||
static void registerWifiStaHandlers(void) |
||||
{ |
||||
ESP_LOGI(TAG, "Registering WiFi station handlers"); |
||||
|
||||
esp_event_handler_instance_t instance_any_id; |
||||
esp_event_handler_instance_t instance_got_ip; |
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, |
||||
ESP_EVENT_ANY_ID, |
||||
&eventHandler, |
||||
NULL, |
||||
&instance_any_id)); |
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, |
||||
IP_EVENT_STA_GOT_IP, |
||||
&eventHandler, |
||||
NULL, |
||||
&instance_got_ip)); |
||||
} |
||||
|
||||
static void configureWifiSta(void) |
||||
{ |
||||
ESP_LOGI(TAG, "Configuring WiFi station"); |
||||
|
||||
wifi_config_t wifi_config = { |
||||
.sta = { |
||||
.threshold.authmode = WIFI_AUTH_WPA2_PSK, |
||||
.listen_interval = POWER_SAVE_AP_BEACON_INTERVAL, |
||||
.pmf_cfg = { |
||||
.capable = true, |
||||
.required = false |
||||
}, |
||||
}, |
||||
}; |
||||
|
||||
strcpy((char*)wifi_config.sta.ssid, settings->wname); |
||||
strcpy((char*)wifi_config.sta.password, settings->wpass); |
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); |
||||
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); |
||||
ESP_LOGI(TAG, "WiFi station configured"); |
||||
} |
||||
|
||||
static bool waitForStaConnection(void) |
||||
{ |
||||
ESP_LOGI(TAG, "Waiting for the WiFi station to connect"); |
||||
|
||||
bool result = false; |
||||
|
||||
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
|
||||
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ |
||||
EventBits_t bits = xEventGroupWaitBits(wifiEventGroup, |
||||
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, |
||||
pdFALSE, |
||||
pdFALSE, |
||||
portMAX_DELAY); |
||||
|
||||
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
|
||||
* happened. */ |
||||
if (bits & WIFI_CONNECTED_BIT) { |
||||
ESP_LOGI(TAG, "Connected to the AP; SSID: %s", settings->wname); |
||||
|
||||
//resolveBrokerAddr();
|
||||
|
||||
if (*settings->usePowerSave) { |
||||
esp_wifi_set_ps(WIFI_PS_MAX_MODEM); |
||||
} |
||||
|
||||
result = true; |
||||
} else if (bits & WIFI_FAIL_BIT) { |
||||
ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s", |
||||
settings->wname, settings->wpass); |
||||
if (errorCallback) { |
||||
errorCallback(); |
||||
} |
||||
} else { |
||||
ESP_LOGE(TAG, "UNEXPECTED EVENT"); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
static bool initWifiStation(void) |
||||
{ |
||||
generateDeviceName(NX_WIFI_MODE_STA); |
||||
ESP_LOGI(TAG, "Initializing WiFi station"); |
||||
|
||||
wifiEventGroup = xEventGroupCreate(); |
||||
|
||||
ESP_ERROR_CHECK(esp_netif_init()); |
||||
ESP_ERROR_CHECK(esp_event_loop_create_default()); |
||||
|
||||
esp_netif_t* sta = esp_netif_create_default_wifi_sta(); |
||||
|
||||
if (*settings->useStaticAddr) { |
||||
configStaticApAddress(sta); |
||||
} |
||||
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); |
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg)); |
||||
|
||||
registerWifiStaHandlers(); |
||||
|
||||
configureWifiSta(); |
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_start() ); |
||||
|
||||
ESP_LOGI(TAG, "Updating the host name"); |
||||
ESP_ERROR_CHECK(esp_netif_set_hostname(sta, deviceName)); |
||||
|
||||
return waitForStaConnection(); |
||||
} |
||||
|
||||
@ -0,0 +1,11 @@
|
||||
idf_component_register( |
||||
SRCS |
||||
src/nxSystemSettings.c |
||||
src/nxSystemSettingsApi.c |
||||
|
||||
INCLUDE_DIRS ./include |
||||
PRIV_INCLUDE_DIRS ./src |
||||
|
||||
# REQUIRES |
||||
# hal |
||||
) |
||||
@ -0,0 +1,10 @@
|
||||
#
|
||||
# Component Makefile
|
||||
#
|
||||
# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default,
|
||||
# this will take the sources in the src/ directory, compile them and link them into
|
||||
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
|
||||
# please read the SDK documents if you need to do this.
|
||||
#
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS := .
|
||||
@ -0,0 +1,40 @@
|
||||
#ifndef NX_SOFTWARE_NXSYSTEMSETTINGS_H_ |
||||
#define NX_SOFTWARE_NXSYSTEMSETTINGS_H_ |
||||
|
||||
#include <stdbool.h> |
||||
#include <stddef.h> |
||||
#include <stdint.h> |
||||
|
||||
#define WIFI_STRINGS_MAX_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]; |
||||
bool wifiPowerSave; |
||||
bool useStaticAddr; |
||||
uint8_t ip4addr[4]; |
||||
uint8_t ip4gw[4]; |
||||
uint8_t ip4mask[4]; |
||||
} SystemSettings; |
||||
|
||||
|
||||
void nxInitSystemSettings(StorageWriteFn writeFn, |
||||
StorageReadFn readFn, RebootFn rebootFn); |
||||
|
||||
void nxUpdateSystemSettings(SystemSettings* newSettings); |
||||
|
||||
void nxRestoreSystemDefaultSettings(void); |
||||
|
||||
void nxWriteSystemSettings(void); |
||||
|
||||
void nxRebootSystem(void); |
||||
|
||||
SystemSettings* nxGetSystemSettings(void); |
||||
|
||||
#endif /* NX_SOFTWARE_NXSYSTEMSETTINGS_H_ */ |
||||
@ -0,0 +1,22 @@
|
||||
#ifndef NX_SOFTWARE_NXSYSTEMSETTINGSAPI_H_ |
||||
#define NX_SOFTWARE_NXSYSTEMSETTINGSAPI_H_ |
||||
|
||||
#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_ */ |
||||
@ -0,0 +1,114 @@
|
||||
#include <nx/software/nxSystemSettings.h> |
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
|
||||
#define KEY_INIT_FLAG "set" |
||||
#define KEY_WIFI_SSID "ssid" |
||||
#define KEY_WIFI_PASS "wpass" |
||||
#define KEY_WIFI_POWER_SAVE "wpsave" |
||||
#define KEY_WIFI_USE_STATIC "wstat" |
||||
#define KEY_WIFI_ADDR "waddr" |
||||
#define KEY_WIFI_GW "wgw" |
||||
#define KEY_WIFI_MASK "wmask" |
||||
|
||||
#define IPV4_ADDR_SIZE 4 |
||||
|
||||
static const uint8_t INIT_FLAG_VALUE = 1; |
||||
static const char* DEFAULT_WIFI_SSID = "service_wifi"; |
||||
static const char* DEFAULT_WIFI_PASS = "service_wifi"; |
||||
static const bool DEFAULT_WIFI_POWER_SAVE = false; |
||||
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 StorageWriteFn storageWrite = NULL; |
||||
static StorageReadFn storageRead = NULL; |
||||
RebootFn execReboot = NULL; |
||||
|
||||
static SystemSettings 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 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); |
||||
} |
||||
|
||||
|
||||
// --------- Public API --------- //
|
||||
|
||||
void nxInitSystemSettings(StorageWriteFn writeFn, |
||||
StorageReadFn readFn, |
||||
RebootFn rebootFn) |
||||
{ |
||||
storageWrite = writeFn; |
||||
storageRead = readFn; |
||||
execReboot = rebootFn; |
||||
|
||||
if (firstRun()) { |
||||
printf("FIRST RUN\n"); |
||||
nxRestoreSystemDefaultSettings(); |
||||
} |
||||
else { |
||||
loadSystemSettings(); |
||||
} |
||||
} |
||||
|
||||
void nxRestoreSystemDefaultSettings(void) |
||||
{ |
||||
printf("Restoring DEFAULT system settings\n"); |
||||
strcpy(settings.wifiSsid, DEFAULT_WIFI_SSID); |
||||
strcpy(settings.wifiPassword, DEFAULT_WIFI_PASS); |
||||
settings.wifiPowerSave = DEFAULT_WIFI_POWER_SAVE; |
||||
settings.useStaticAddr = DEFAULT_WIFI_USE_STATIC; |
||||
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); |
||||
nxWriteSystemSettings(); |
||||
} |
||||
|
||||
void nxUpdateSystemSettings(SystemSettings* newSettings) |
||||
{ |
||||
memcpy(&settings, newSettings, sizeof(SystemSettings)); |
||||
nxWriteSystemSettings(); |
||||
} |
||||
|
||||
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); |
||||
} |
||||
|
||||
SystemSettings* nxGetSystemSettings(void) |
||||
{ |
||||
return &settings; |
||||
} |
||||
|
||||
void nxRebootSystem(void) |
||||
{ |
||||
execReboot(); |
||||
} |
||||
|
||||
|
||||
@ -0,0 +1,127 @@
|
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <inttypes.h> |
||||
#include <nx/software/nxSystemSettings.h> |
||||
#include <nx/software/nxSystemSettingsApi.h> |
||||
#include <string.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 |
||||
*/ |
||||
|
||||
#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 UNUSED(x) (void)(x) |
||||
|
||||
#define RESP_BUF_SIZE 1024 |
||||
|
||||
char responseBuffer[RESP_BUF_SIZE]; |
||||
|
||||
void int2ip(uint8_t array[4], uint32_t addr) |
||||
{ |
||||
array[0] = (addr & 0xFF000000) >> 24; |
||||
array[1] = (addr & 0x00FF0000) >> 16; |
||||
array[2] = (addr & 0x0000FF00) >> 8; |
||||
array[3] = (addr & 0x000000FF); |
||||
} |
||||
|
||||
void handleKvPair(const char* key, const char* value) |
||||
{ |
||||
printf("System settings update; Key: %s, Value: %s\n", key, value); |
||||
|
||||
SystemSettings* settings = nxGetSystemSettings(); |
||||
if (strcmp(key, API_KEY_SSID) == 0) { |
||||
strcpy(settings->wifiSsid, value); |
||||
} |
||||
else if (strcmp(key, API_KEY_WPASS) == 0) { |
||||
strcpy(settings->wifiPassword, value); |
||||
} |
||||
else if (strcmp(key, API_KEY_WPWSAVE) == 0) { |
||||
settings->wifiPowerSave = atoi(value); |
||||
} |
||||
else if (strcmp(key, API_KEY_USE_STATIC) == 0) { |
||||
settings->useStaticAddr = atoi(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); |
||||
int2ip(settings->ip4gw, addr); |
||||
} |
||||
else if (strcmp(key, API_KEY_IPV4MASK) == 0) { |
||||
uint32_t addr = strtoul(value, NULL, 10); |
||||
int2ip(settings->ip4mask, addr); |
||||
} |
||||
else { |
||||
fprintf(stderr, "Unknown key: %s\n", key); |
||||
} |
||||
} |
||||
|
||||
// --------- Public API --------- //
|
||||
|
||||
void nxApiGetSystemSettings(const uint8_t* msg, size_t msgLen, |
||||
const char** response, size_t* respLen) |
||||
{ |
||||
UNUSED(msg); UNUSED(msgLen); |
||||
memset(responseBuffer, 0, RESP_BUF_SIZE); |
||||
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\"}", |
||||
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] |
||||
); |
||||
*response = responseBuffer; |
||||
*respLen = strlen(responseBuffer); |
||||
} |
||||
|
||||
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; |
||||
} |
||||
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(); |
||||
} |
||||
@ -0,0 +1,27 @@
|
||||
EESchema-DOCLIB Version 2.0 |
||||
# |
||||
$CMP AP1509 |
||||
D 150KHz, 2A PWM BUCK DC/DC CONVERTER |
||||
K 3.3V 2A 150KHz PWM Buck DC/DC |
||||
F https://www.tme.eu/Document/7fdb23bb64ab89e0668906be83b8b7e3/AP1509x-DTE.pdf |
||||
$ENDCMP |
||||
# |
||||
$CMP APE1707M-12-HF_copy |
||||
D 2A, 150KHz PWM Buck DC/DC Converter, fixed 12.0V output voltage, SO-8 |
||||
K 12V 2A 150KHz PWM Buck DC/DC |
||||
F http://files.remont-aud.net/baza/dc_dc/data/APE1707.pdf |
||||
$ENDCMP |
||||
# |
||||
$CMP APE1707M-50-HF_copy |
||||
D 2A, 150KHz PWM Buck DC/DC Converter, fixed 5.0V output voltage, SO-8 |
||||
K 5V 2A 150KHz PWM Buck DC/DC |
||||
F http://files.remont-aud.net/baza/dc_dc/data/APE1707.pdf |
||||
$ENDCMP |
||||
# |
||||
$CMP APE1707M-HF_copy |
||||
D 2A, 150KHz PWM Buck DC/DC Converter, adjustable output voltage, SO-8 |
||||
K Adjustable 2A 150KHz PWM Buck DC/DC |
||||
F http://files.remont-aud.net/baza/dc_dc/data/APE1707.pdf |
||||
$ENDCMP |
||||
# |
||||
#End Doc Library |
||||
@ -0,0 +1,28 @@
|
||||
EESchema-LIBRARY Version 2.4 |
||||
#encoding utf-8 |
||||
# |
||||
# AP1509 |
||||
# |
||||
DEF AP1509 U 0 20 Y Y 1 F N |
||||
F0 "U" -300 225 50 H V L CNN |
||||
F1 "AP1509" 0 225 50 H V L CNN |
||||
F2 "Package_SO:SOP-8_3.9x4.9mm_P1.27mm" 25 -250 50 H I L CIN |
||||
F3 "" 0 0 50 H I C CNN |
||||
ALIAS APE1707M-50-HF_copy APE1707M-12-HF_copy APE1707M-HF_copy |
||||
$FPLIST |
||||
SOIC*3.9x4.9mm*P1.27mm* |
||||
$ENDFPLIST |
||||
DRAW |
||||
S -300 175 300 -200 0 1 10 f |
||||
X VIN 1 -400 100 100 R 50 50 1 1 W |
||||
X OUT 2 400 -100 100 L 50 50 1 1 w |
||||
X FB 3 400 100 100 L 50 50 1 1 W |
||||
X SD 4 -400 -100 100 R 50 50 1 1 I |
||||
X GND 5 0 -300 100 U 50 50 1 1 I |
||||
X GND 6 0 -300 100 U 50 50 1 1 I N |
||||
X GND 7 0 -300 100 U 50 50 1 1 I N |
||||
X GND 8 0 -300 100 U 50 50 1 1 I N |
||||
ENDDRAW |
||||
ENDDEF |
||||
# |
||||
#End Library |
||||
@ -0,0 +1,252 @@
|
||||
update=nie, 17 kwi 2022, 19:14:29 |
||||
version=1 |
||||
last_client=kicad |
||||
[general] |
||||
version=1 |
||||
RootSch= |
||||
BoardNm= |
||||
[cvpcb] |
||||
version=1 |
||||
NetIExt=net |
||||
[eeschema] |
||||
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= |
||||
LastNetListRead= |
||||
CopperLayerCount=2 |
||||
BoardThickness=1.6 |
||||
AllowMicroVias=0 |
||||
AllowBlindVias=0 |
||||
RequireCourtyardDefinitions=0 |
||||
ProhibitOverlappingCourtyards=1 |
||||
MinTrackWidth=0.5 |
||||
MinViaDiameter=0.5 |
||||
MinViaDrill=0.3 |
||||
MinMicroViaDiameter=0.2 |
||||
MinMicroViaDrill=0.09999999999999999 |
||||
MinHoleToHole=0.25 |
||||
TrackWidth1=0.5 |
||||
TrackWidth2=0.5 |
||||
TrackWidth3=0.7 |
||||
ViaDiameter1=1.3 |
||||
ViaDrill1=0.5 |
||||
ViaDiameter2=1.3 |
||||
ViaDrill2=0.5 |
||||
dPairWidth1=0.5 |
||||
dPairGap1=0.25 |
||||
dPairViaGap1=0.25 |
||||
SilkLineWidth=0.12 |
||||
SilkTextSizeV=1 |
||||
SilkTextSizeH=1 |
||||
SilkTextSizeThickness=0.15 |
||||
SilkTextItalic=0 |
||||
SilkTextUpright=1 |
||||
CopperLineWidth=0.2 |
||||
CopperTextSizeV=1.5 |
||||
CopperTextSizeH=1.5 |
||||
CopperTextThickness=0.3 |
||||
CopperTextItalic=0 |
||||
CopperTextUpright=1 |
||||
EdgeCutLineWidth=0.05 |
||||
CourtyardLineWidth=0.05 |
||||
OthersLineWidth=0.15 |
||||
OthersTextSizeV=1 |
||||
OthersTextSizeH=1 |
||||
OthersTextSizeThickness=0.15 |
||||
OthersTextItalic=0 |
||||
OthersTextUpright=1 |
||||
SolderMaskClearance=0 |
||||
SolderMaskMinWidth=0 |
||||
SolderPasteClearance=0 |
||||
SolderPasteRatio=-0 |
||||
[pcbnew/Layer.F.Cu] |
||||
Name=F.Cu |
||||
Type=0 |
||||
Enabled=1 |
||||
[pcbnew/Layer.In1.Cu] |
||||
Name=In1.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In2.Cu] |
||||
Name=In2.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In3.Cu] |
||||
Name=In3.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In4.Cu] |
||||
Name=In4.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In5.Cu] |
||||
Name=In5.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In6.Cu] |
||||
Name=In6.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In7.Cu] |
||||
Name=In7.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In8.Cu] |
||||
Name=In8.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In9.Cu] |
||||
Name=In9.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In10.Cu] |
||||
Name=In10.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In11.Cu] |
||||
Name=In11.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In12.Cu] |
||||
Name=In12.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In13.Cu] |
||||
Name=In13.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In14.Cu] |
||||
Name=In14.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In15.Cu] |
||||
Name=In15.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In16.Cu] |
||||
Name=In16.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In17.Cu] |
||||
Name=In17.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In18.Cu] |
||||
Name=In18.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In19.Cu] |
||||
Name=In19.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In20.Cu] |
||||
Name=In20.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In21.Cu] |
||||
Name=In21.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In22.Cu] |
||||
Name=In22.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In23.Cu] |
||||
Name=In23.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In24.Cu] |
||||
Name=In24.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In25.Cu] |
||||
Name=In25.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In26.Cu] |
||||
Name=In26.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In27.Cu] |
||||
Name=In27.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In28.Cu] |
||||
Name=In28.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In29.Cu] |
||||
Name=In29.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.In30.Cu] |
||||
Name=In30.Cu |
||||
Type=0 |
||||
Enabled=0 |
||||
[pcbnew/Layer.B.Cu] |
||||
Name=B.Cu |
||||
Type=0 |
||||
Enabled=1 |
||||
[pcbnew/Layer.B.Adhes] |
||||
Enabled=1 |
||||
[pcbnew/Layer.F.Adhes] |
||||
Enabled=1 |
||||
[pcbnew/Layer.B.Paste] |
||||
Enabled=1 |
||||
[pcbnew/Layer.F.Paste] |
||||
Enabled=1 |
||||
[pcbnew/Layer.B.SilkS] |
||||
Enabled=1 |
||||
[pcbnew/Layer.F.SilkS] |
||||
Enabled=1 |
||||
[pcbnew/Layer.B.Mask] |
||||
Enabled=1 |
||||
[pcbnew/Layer.F.Mask] |
||||
Enabled=1 |
||||
[pcbnew/Layer.Dwgs.User] |
||||
Enabled=1 |
||||
[pcbnew/Layer.Cmts.User] |
||||
Enabled=1 |
||||
[pcbnew/Layer.Eco1.User] |
||||
Enabled=1 |
||||
[pcbnew/Layer.Eco2.User] |
||||
Enabled=1 |
||||
[pcbnew/Layer.Edge.Cuts] |
||||
Enabled=1 |
||||
[pcbnew/Layer.Margin] |
||||
Enabled=1 |
||||
[pcbnew/Layer.B.CrtYd] |
||||
Enabled=1 |
||||
[pcbnew/Layer.F.CrtYd] |
||||
Enabled=1 |
||||
[pcbnew/Layer.B.Fab] |
||||
Enabled=1 |
||||
[pcbnew/Layer.F.Fab] |
||||
Enabled=1 |
||||
[pcbnew/Layer.Rescue] |
||||
Enabled=0 |
||||
[pcbnew/Netclasses] |
||||
[pcbnew/Netclasses/Default] |
||||
Name=Default |
||||
Clearance=0.3 |
||||
TrackWidth=0.5 |
||||
ViaDiameter=1.3 |
||||
ViaDrill=0.5 |
||||
uViaDiameter=0.3 |
||||
uViaDrill=0.1 |
||||
dPairWidth=0.5 |
||||
dPairGap=0.25 |
||||
dPairViaGap=0.25 |
||||
@ -0,0 +1,8 @@
|
||||
# Edit following two lines to set component requirements (see docs) |
||||
set(COMPONENT_REQUIRES firmware software) |
||||
set(COMPONENT_PRIV_REQUIRES ) |
||||
|
||||
set(COMPONENT_SRCS "main.c") |
||||
set(COMPONENT_ADD_INCLUDEDIRS "") |
||||
|
||||
register_component() |
||||
@ -0,0 +1,14 @@
|
||||
# put here your custom config value |
||||
menu "Example Configuration" |
||||
config ESP_WIFI_SSID |
||||
string "WiFi SSID" |
||||
default "myssid" |
||||
help |
||||
SSID (network name) for the example to connect to. |
||||
|
||||
config ESP_WIFI_PASSWORD |
||||
string "WiFi Password" |
||||
default "mypassword" |
||||
help |
||||
WiFi password (WPA or WPA2) for the example to use. |
||||
endmenu |
||||
@ -0,0 +1,14 @@
|
||||
<html> |
||||
<head> |
||||
<style> |
||||
body { |
||||
background-color: rgb(105, 195, 224); |
||||
} |
||||
</style> |
||||
</head> |
||||
|
||||
<body> |
||||
<h1>PB LED MATRIX</h1> |
||||
</body> |
||||
|
||||
</html> |
||||
@ -0,0 +1,48 @@
|
||||
#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(); |
||||
} |
||||
|
||||
Loading…
Reference in new issue