Compare commits

...

5 Commits

Author SHA1 Message Date
github-actions
aa8b5ec2d4 Update prebuilt objects [skip actions] 2022-12-02 18:34:52 +00:00
Sebastien L
3c94f63876 Fix offline icons 2022-12-02 12:00:57 -05:00
Sebastien L
383a2fd7ce Fix include file name 2022-11-29 11:54:43 -05:00
Sebastien L
f74ecf5e60 improv-wifi initial commit 2022-11-29 11:53:20 -05:00
Sebastien L
1390e258db improv-wifi initial commit 2022-11-29 11:04:27 -05:00
48 changed files with 1328 additions and 195 deletions

10
.gitignore vendored
View File

@@ -18,3 +18,13 @@ components/wifi-manager/UML-State-Machine-in-C
envfile.txt
artifacts
web-installer
squeezelite-esp32.code-workspace
esp-idf-vscode-generated.gdb
debug.log
components/wifi-manager/esp32_improv.cpp.txt
components/wifi-manager/esp32_improv.h.txt

View File

@@ -5,6 +5,7 @@ idf_component_register( SRCS
cmd_system.c
cmd_wifi.c
platform_console.c
improv_console.c
cmd_config.c
INCLUDE_DIRS .
REQUIRES nvs_flash

View File

@@ -33,6 +33,7 @@
#include "esp_netif.h"
#include "esp_event.h"
#include "led.h"
#include "improv.h"
extern bool bypass_network_manager;
#define JOIN_TIMEOUT_MS (10000)
#include "platform_console.h"
@@ -48,6 +49,15 @@ static struct {
struct arg_str *password;
struct arg_end *end;
} join_args;
static struct {
struct arg_lit * conect;
struct arg_lit * state;
struct arg_lit * info;
struct arg_lit * list;
struct arg_str *ssid;
struct arg_str *password;
struct arg_end *end;
} improv_args;
@@ -185,6 +195,44 @@ static int connect(int argc, char **argv)
return 0;
}
extern bool on_improv_command(ImprovCommandStruct_t *command); // command callback
static int do_improv(int argc, char **argv)
{
ImprovCommandStruct_t command;
int nerrors = arg_parse_msg(argc, argv,(struct arg_hdr **)&improv_args);
if (nerrors != 0) {
return 1;
}
if(improv_args.info->count>0){
memset(&command,0x00,sizeof(command));
command.command = IMPROV_CMD_GET_DEVICE_INFO;
on_improv_command(&command);
}
if(improv_args.conect->count>0){
if(improv_args.ssid->count == 0){
ESP_LOGE(__func__,"Parameter ssid is required to connect");
return 1;
}
command.ssid = improv_args.ssid->sval[0];
if(improv_args.password->count == 0){
command.password= improv_args.password->sval[0];
}
command.command = IMPROV_CMD_WIFI_SETTINGS;
on_improv_command(&command);
}
if(improv_args.state->count>0){
memset(&command,0x00,sizeof(command));
command.command = IMPROV_CMD_GET_CURRENT_STATE;
on_improv_command(&command);
}
if(improv_args.list->count>0){
memset(&command,0x00,sizeof(command));
command.command = IMPROV_CMD_GET_WIFI_NETWORKS;
on_improv_command(&command);
}
return 0;
}
void register_wifi_join()
{
join_args.timeout = arg_int0(NULL, "timeout", "<t>", "Connection timeout, ms");
@@ -201,10 +249,28 @@ void register_wifi_join()
};
ESP_ERROR_CHECK( esp_console_cmd_register(&join_cmd) );
}
static void register_improv_debug(){
improv_args.conect = arg_lit0(NULL,"connect","Connects to the specified wifi ssid and password");
improv_args.ssid = arg_str0(NULL, NULL, "<ssid>", "SSID of AP");
improv_args.password = arg_str0(NULL, NULL, "<pass>", "Password of AP");
improv_args.info = arg_lit0(NULL,"info","Request the info packet");
improv_args.list = arg_lit0(NULL,"list","Request the wifi list packet");
improv_args.state = arg_lit0(NULL,"state","Requests the state packet");
improv_args.end = arg_end(2);
const esp_console_cmd_t improv_cmd = {
.command = "improv",
.help = "Send an improv-wifi serial command to the system",
.hint = NULL,
.func = &do_improv,
.argtable = &improv_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&improv_cmd) );
}
void register_wifi()
{
register_wifi_join();
register_improv_debug();
if(bypass_network_manager){
initialise_wifi();
}

View File

@@ -0,0 +1,162 @@
#include "platform_console.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "esp_log.h"
#include "esp_console.h"
#include "esp_vfs_dev.h"
#include "driver/uart.h"
#include "linenoise/linenoise.h"
#include "argtable3/argtable3.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "pthread.h"
#include "platform_esp32.h"
#include "cmd_decl.h"
#include "trace.h"
#include "platform_config.h"
#include "telnet.h"
#include "tools.h"
#include "improv.h"
#include "messaging.h"
#include "config.h"
#include "improv_console.h"
#include "network_status.h"
static const char * TAG ="improv_console";
const time_t improv_timeout_ms = 50;
TickType_t improv_timeout_tick = pdMS_TO_TICKS(improv_timeout_ms);
ImprovState_t improv_state = IMPROV_STATE_READY_AUTHORIZED;
const size_t improv_buffer_size = 121;
size_t improv_buffer_len = 0;
uint8_t * improv_buffer_data = NULL;
TickType_t improv_delay = portMAX_DELAY;
void cb_improv_got_ip(nm_state_t new_state, int sub_state){
if(improv_state == IMPROV_STATE_PROVISIONING){
char * url = network_status_alloc_get_system_url();
ESP_LOGI(TAG,"Signaling improv state connected state with url: %s",STR_OR_BLANK(url));
improv_send_device_url(IMPROV_CMD_WIFI_SETTINGS,url);
FREE_AND_NULL(url);
}
}
void cb_improv_disconnected(nm_state_t new_state, int sub_state){
if(improv_state == IMPROV_STATE_PROVISIONING){
ESP_LOGI(TAG,"Signalling improv connect failure ");
improv_state = IMPROV_STATE_READY_AUTHORIZED;
improv_send_error(IMPROV_ERROR_UNABLE_TO_CONNECT);
}
}
bool on_improv_command(ImprovCommandStruct_t *command){
esp_err_t err = ESP_OK;
wifi_connect_state_t wifi_state = network_wifi_get_connect_state();
const esp_app_desc_t* desc = esp_ota_get_app_description();
improv_buffer_len = 0;
char * url=NULL;
char * host_name = NULL;
ESP_LOGI(TAG, "Processing improv command %s",improv_get_command_desc(command->command));
if(!command){
return false;
}
switch (command->command)
{
case IMPROV_CMD_WIFI_SETTINGS:
// attempt to connect to the provided SSID+password
improv_state = IMPROV_STATE_PROVISIONING;
ESP_LOGI(TAG,"Improv connect to %s",command->ssid );
network_async_connect(command->ssid, command->password);
FREE_AND_NULL(command->ssid);
FREE_AND_NULL(command->password);
break;
case IMPROV_CMD_GET_CURRENT_STATE:
if(wifi_state !=NETWORK_WIFI_STATE_CONNECTING){
network_async_scan();
}
switch (wifi_state)
{
case NETWORK_WIFI_STATE_CONNECTING:
ESP_LOGI(TAG,"Signaling improv state " );
return improv_send_current_state(improv_state);
break;
case NETWORK_WIFI_STATE_INVALID_CONFIG:
improv_state = IMPROV_STATE_READY_AUTHORIZED;
ESP_LOGW(TAG,"Signaling improv state IMPROV_ERROR_UNABLE_TO_CONNECT" );
return improv_send_error(IMPROV_ERROR_UNABLE_TO_CONNECT);
break;
case NETWORK_WIFI_STATE_FAILED:
ESP_LOGW(TAG,"Signaling improv state IMPROV_ERROR_NOT_AUTHORIZED" );
network_async_scan();
improv_state = IMPROV_STATE_READY_AUTHORIZED;
return improv_send_error(IMPROV_ERROR_NOT_AUTHORIZED);
break;
case NETWORK_WIFI_STATE_CONNECTED:
network_async_scan();
url = network_status_alloc_get_system_url();
ESP_LOGI(TAG,"Signaling improv state connected state with url: %s",STR_OR_BLANK(url));
improv_state = IMPROV_STATE_PROVISIONED;
improv_send_current_state(improv_state);
// also send url
improv_send_device_url(IMPROV_CMD_GET_CURRENT_STATE,url);
FREE_AND_NULL(url);
break;
default:
ESP_LOGI(TAG,"Signaling improv state " );
return improv_send_current_state(improv_state);
break;
}
break;
case IMPROV_CMD_GET_DEVICE_INFO:
ESP_LOGI(TAG,"Signaling improv with device info. Firmware Name: %s, Version: %s ",desc->project_name,desc->version );
host_name = config_alloc_get_str("host_name",NULL,"Squeezelite");
improv_send_device_info(desc->project_name,desc->version,"ESP32",host_name);
FREE_AND_NULL(host_name);
break;
case IMPROV_CMD_GET_WIFI_NETWORKS:
ESP_LOGI(TAG,"Signaling improv with list of wifi networks " );
improv_wifi_list_send();
break;
default:
ESP_LOGE(TAG,"Signaling improv with invalid RPC call received" );
improv_send_error(IMPROV_ERROR_INVALID_RPC);
break;
}
return false;
}
void on_improv_error(ImprovError_t error){
improv_send_error(error);
ESP_LOGE(TAG,"Error processing improv-wifi packet : %s", improv_get_error_desc(error));
}
#if BUFFER_DEBUG
void dump_buffer(const char * prefix, const char * buff, size_t len){
printf("\n%s (%d): ",prefix, len);
for(int i=0;i<len;i++){
printf(" %c ",isprint(buff[i])?buff[i]:'.');
}
printf("\n%s (%d): ",prefix, len);
for(int i=0;i<len;i++){
printf("0x%03x ",buff[i]);
}
printf("\n");
}
#else
#define dump_buffer(prefix,buff,size)
#endif
bool improv_send_callback(uint8_t * buffer, size_t length){
dump_buffer("send", (const char *) buffer, length);
uart_write_bytes(CONFIG_ESP_CONSOLE_UART_NUM,buffer,length );
return true;
}
void improv_console_init(){
ESP_LOGI(TAG,"Initializing improv callbacks");
network_register_state_callback(NETWORK_WIFI_ACTIVE_STATE,WIFI_CONNECTED_STATE, "improv_got_ip", &cb_improv_got_ip);
network_register_state_callback(NETWORK_WIFI_ACTIVE_STATE,WIFI_CONNECTING_NEW_FAILED_STATE, "improv_disconnect", &cb_improv_disconnected);
network_register_state_callback(NETWORK_WIFI_CONFIGURING_ACTIVE_STATE,WIFI_CONFIGURING_CONNECT_FAILED_STATE, "improv_disconnect", &cb_improv_disconnected);
}

View File

@@ -0,0 +1,24 @@
extern TickType_t improv_timeout_tick;
#pragma once
#include "network_manager.h"
#include "improv.h"
#include "freertos/FreeRTOS.h"
#include <stdio.h>
#include <stdlib.h>
#define BUFFER_DEBUG 0
extern TickType_t improv_timeout_tick;
extern const size_t improv_buffer_size;
extern size_t improv_buffer_len;
extern uint8_t * improv_buffer_data;
extern const time_t improv_timeout_ms;
extern TickType_t improv_delay;
void cb_improv_got_ip(nm_state_t new_state, int sub_state);
bool on_improv_command(ImprovCommandStruct_t *command);
void on_improv_error(ImprovError_t error);
void dump_buffer(const char * prefix, const char * buff, size_t len);
bool improv_send_callback(uint8_t * buffer, size_t length);
void improv_console_init();

View File

@@ -27,17 +27,18 @@
#include "platform_config.h"
#include "telnet.h"
#include "tools.h"
#include "improv.h"
#include "messaging.h"
#include "network_manager.h"
#include "config.h"
#include "improv_console.h"
static pthread_t thread_console;
static void * console_thread();
void console_start();
static const char * TAG = "console";
extern bool bypass_network_manager;
extern void register_squeezelite();
bool improv=false;
static EXT_RAM_ATTR QueueHandle_t uart_queue;
static EXT_RAM_ATTR struct {
uint8_t _buf[512];
@@ -249,31 +250,77 @@ void process_autoexec(){
}
}
#define BUFFERDEBUG 0
static ssize_t stdin_read(int fd, void* data, size_t size) {
size_t bytes = -1;
uint32_t improv_next_timeout = 0;
if(!improv_buffer_data){
improv_buffer_data = (uint8_t * )malloc_init_external(improv_buffer_size);
memset(improv_buffer_data,0x00,improv_buffer_size);
improv_set_send_callback(improv_send_callback);
}
size_t read_size = 0;
while (1) {
QueueSetMemberHandle_t activated = xQueueSelectFromSet(stdin_redir.queue_set, portMAX_DELAY);
if (activated == uart_queue) {
uart_event_t event;
xQueueReceive(uart_queue, &event, 0);
if (event.type == UART_DATA) {
bytes = uart_read_bytes(CONFIG_ESP_CONSOLE_UART_NUM, data, size < event.size ? size : event.size, 0);
// we have to do our own line ending translation here
for (int i = 0; i < bytes; i++) if (((char*)data)[i] == '\r') ((char*)data)[i] = '\n';
break;
}
} else if (xRingbufferCanRead(stdin_redir.handle, activated)) {
QueueSetMemberHandle_t activated = xQueueSelectFromSet(stdin_redir.queue_set, improv_delay);
uint32_t now = esp_timer_get_time() / 1000; uart_event_t event;
xQueueReceive(uart_queue, &event, 0);
//esp_rom_printf(".");
//esp_rom_printf("\n********Activated: 0x%6X, type: %d\n",(unsigned int)activated, event.type);
if (event.type == UART_DATA) {
//esp_rom_printf("uart.");
#if BUFFERDEBUG
printf("\n********event: %d, read: %d\n", event.size,bytes);
#endif
bytes = uart_read_bytes(CONFIG_ESP_CONSOLE_UART_NUM, improv_buffer_data+improv_buffer_len,1, 0);
while(bytes>0){
//esp_rom_printf("rb[%c]\n",*(bufferdata+buffer_len));
improv_buffer_len++;
if(!improv_parse_serial_byte(improv_buffer_len-1,improv_buffer_data[improv_buffer_len-1],improv_buffer_data,on_improv_command,on_improv_error)){
#if BUFFERDEBUG
//dump_buffer("improv invalid",(const char *)bufferdata,buffer_len);
#endif
if(improv_buffer_len>0){
//esp_rom_printf("not improv\n");
xRingbufferSend(stdin_redir.handle, improv_buffer_data,improv_buffer_len, pdMS_TO_TICKS(100));
}
improv_buffer_len=0;
}
bytes = uart_read_bytes(CONFIG_ESP_CONSOLE_UART_NUM, improv_buffer_data+improv_buffer_len,1, 0);
}
improv_next_timeout = esp_timer_get_time() / 1000+improv_timeout_ms;
#if BUFFERDEBUG
//dump_buffer("after event",(const char *)bufferdata,buffer_len);
#endif
}
if ( xRingbufferCanRead(stdin_redir.handle, activated)) {
//esp_rom_printf("\n********rbr!\n");
char *p = xRingbufferReceiveUpTo(stdin_redir.handle, &bytes, 0, size);
// we might receive strings, replace null by \n
#if BUFFERDEBUG
//dump_buffer("Ringbuf read",p,bytes);
#endif
for (int i = 0; i < bytes; i++) if (p[i] == '\0' || p[i] == '\r') p[i] = '\n';
memcpy(data, p, bytes);
vRingbufferReturnItem(stdin_redir.handle, p);
break;
}
if(improv_buffer_len>0){
improv_delay = improv_timeout_tick;
}
else {
improv_delay = portMAX_DELAY;
}
if (now > improv_next_timeout && improv_buffer_len > 0) {
#if BUFFERDEBUG
//dump_buffer("improv timeout",(const char *)bufferdata,buffer_len);
#endif
//esp_rom_printf("\n********QueueSent\n");
xRingbufferSendFromISR(stdin_redir.handle, improv_buffer_data, improv_buffer_len, pdMS_TO_TICKS(100));
improv_buffer_len = 0;
}
}
return bytes;
@@ -304,6 +351,9 @@ void initialize_console() {
/* re-direct stdin to our own driver so we can gather data from various sources */
stdin_redir.queue_set = xQueueCreateSet(2);
if(!stdin_redir.queue_set){
ESP_LOGE(TAG,"Serial event queue set could not be created");
}
stdin_redir.handle = xRingbufferCreateStatic(sizeof(stdin_redir._buf), RINGBUF_TYPE_BYTEBUF, stdin_redir._buf, &stdin_redir._ringbuf);
xRingbufferAddToQueueSetRead(stdin_redir.handle, stdin_redir.queue_set);
xQueueAddToSet(uart_queue, stdin_redir.queue_set);
@@ -312,7 +362,6 @@ void initialize_console() {
vfs.flags = ESP_VFS_FLAG_DEFAULT;
vfs.open = stdin_dummy;
vfs.read = stdin_read;
ESP_ERROR_CHECK(esp_vfs_register("/dev/console", &vfs, NULL));
freopen("/dev/console", "r", stdin);
@@ -352,6 +401,7 @@ bool console_push(const char *data, size_t size) {
void console_start() {
/* we always run console b/c telnet sends commands to stdin */
initialize_console();
improv_console_init();
/* Register commands */
MEMTRACE_PRINT_DELTA_MESSAGE("Registering help command");
@@ -449,6 +499,7 @@ static esp_err_t run_command(char * line){
}
static void * console_thread() {
if(!is_recovery_running){
MEMTRACE_PRINT_DELTA_MESSAGE("Running autoexec");
process_autoexec();

View File

@@ -3,8 +3,8 @@ set( WEBPACK_DIR webapp/webpack/dist )
idf_component_register( SRC_DIRS . webapp UML-State-Machine-in-C/src
INCLUDE_DIRS . webapp UML-State-Machine-in-C/src
REQUIRES squeezelite-ota json mdns
PRIV_REQUIRES tools services platform_config esp_common json newlib freertos spi_flash nvs_flash mdns pthread wpa_supplicant platform_console esp_http_server console driver_bt
REQUIRES squeezelite-ota json mdns bt
PRIV_REQUIRES tools services platform_config esp_common json newlib freertos spi_flash nvs_flash mdns pthread wpa_supplicant platform_console esp_http_server console driver_bt
)
include(webapp/webapp.cmake)

View File

@@ -0,0 +1,431 @@
#include "esp_system.h"
#include "esp_log.h"
#include "improv.h"
#include "tools.h"
#include "string.h"
static ImprovCommandStruct_t last_command;
static callback_table_t callbacks[] = {
{IMPROV_CMD_UNKNOWN, NULL},
{IMPROV_CMD_WIFI_SETTINGS, NULL},
{IMPROV_CMD_GET_CURRENT_STATE, NULL},
{IMPROV_CMD_GET_DEVICE_INFO, NULL},
{IMPROV_CMD_GET_WIFI_NETWORKS, NULL},
{IMPROV_CMD_BAD_CHECKSUM, NULL},
{-1, NULL}};
const char *improv_get_error_desc(ImprovError_t error)
{
switch (error)
{
ENUM_TO_STRING(IMPROV_ERROR_NONE)
ENUM_TO_STRING(IMPROV_ERROR_INVALID_RPC)
ENUM_TO_STRING(IMPROV_ERROR_UNKNOWN_RPC)
ENUM_TO_STRING(IMPROV_ERROR_UNABLE_TO_CONNECT)
ENUM_TO_STRING(IMPROV_ERROR_NOT_AUTHORIZED)
ENUM_TO_STRING(IMPROV_ERROR_UNKNOWN)
}
return "";
}
const char *improv_get_command_desc(ImprovCommand_t command)
{
switch (command)
{
ENUM_TO_STRING(IMPROV_CMD_UNKNOWN)
ENUM_TO_STRING(IMPROV_CMD_WIFI_SETTINGS)
ENUM_TO_STRING(IMPROV_CMD_GET_CURRENT_STATE)
ENUM_TO_STRING(IMPROV_CMD_GET_DEVICE_INFO)
ENUM_TO_STRING(IMPROV_CMD_GET_WIFI_NETWORKS)
ENUM_TO_STRING(IMPROV_CMD_BAD_CHECKSUM)
}
return "";
};
static improv_send_callback_t send_callback = NULL;
const uint8_t improv_prefix[] = {'I', 'M', 'P', 'R', 'O', 'V', IMPROV_SERIAL_VERSION};
typedef struct __attribute__((__packed__))
{
uint8_t prefix[6];
uint8_t version;
uint8_t packet_type;
uint8_t data_len;
} improv_packet_t;
#define PACKET_CHECKSUM_SIZE sizeof(uint8_t)
#define PACKET_PAYLOAD(packet) ((uint8_t *)packet) + sizeof(improv_packet_t)
static ImprovAPListStruct_t *ap_list = NULL;
static size_t ap_list_size = 0;
static size_t ap_list_actual = 0;
void improv_wifi_list_free()
{
ap_list_actual = 0;
ImprovAPListStruct_t *current = ap_list;
for (int i = 0; i < ap_list_actual; i++)
{
if (!current)
{
break;
}
FREE_AND_NULL(current->rssi);
FREE_AND_NULL(current->ssid);
FREE_AND_NULL(current->auth_req);
current++;
}
FREE_AND_NULL(ap_list);
}
bool improv_wifi_list_allocate(size_t num_entries)
{
improv_wifi_list_free();
ap_list = malloc_init_external(num_entries * sizeof(ImprovAPListStruct_t) + 1); // last byte will always be null
ap_list_size = num_entries;
ap_list_actual = 0;
return ap_list != NULL;
}
bool improv_wifi_list_add(const char *ssid, int8_t rssi, bool auth_req)
{
const size_t yes_no_length = 4;
ImprovAPListStruct_t *current = ap_list + ap_list_actual;
if (ap_list_actual > ap_list_size || !current)
{
return false;
}
current->ssid = strdup_psram(ssid);
size_t length = snprintf(NULL, 0, "%02d", rssi) + 1;
current->auth_req = malloc_init_external(yes_no_length); // enough for YES/NO to fit
current->rssi = (char *)malloc_init_external(length);
if (!current->rssi || !current->auth_req)
{
return false;
}
snprintf(current->rssi, length, "%02d", rssi);
snprintf(current->auth_req, yes_no_length, "%s", auth_req ? "YES" : "NO");
ap_list_actual++;
return true;
}
void improv_parse_data(ImprovCommandStruct_t *improv_command, const uint8_t *data, size_t length, bool check_checksum)
{
ImprovCommand_t command = (ImprovCommand_t)data[0];
uint8_t data_length = data[1];
if (data_length != length - 2 - check_checksum)
{
improv_command->command = IMPROV_CMD_UNKNOWN;
return;
}
if (check_checksum)
{
uint8_t checksum = data[length - 1];
uint32_t calculated_checksum = 0;
for (uint8_t i = 0; i < length - 1; i++)
{
calculated_checksum += data[i];
}
if ((uint8_t)calculated_checksum != checksum)
{
improv_command->command = IMPROV_CMD_BAD_CHECKSUM;
return;
}
}
if (command == IMPROV_CMD_WIFI_SETTINGS)
{
uint8_t ssid_length = data[2];
uint8_t ssid_start = 3;
size_t ssid_end = ssid_start + ssid_length;
uint8_t pass_length = data[ssid_end];
size_t pass_start = ssid_end + 1;
size_t pass_end = pass_start + pass_length;
improv_command->ssid = malloc(ssid_length + 1);
memset(improv_command->ssid, 0x00, ssid_length + 1);
memcpy(improv_command->ssid, &data[ssid_start], ssid_length);
improv_command->password = NULL;
if (pass_length > 0)
{
improv_command->password = malloc(pass_length + 1);
memset(improv_command->password, 0x00, pass_length + 1);
memcpy(improv_command->password, &data[pass_start], pass_length);
}
}
improv_command->command = command;
}
bool improv_parse_serial_byte(size_t position, uint8_t byte, const uint8_t *buffer,
improv_command_callback_t callback, on_error_callback_t on_error)
{
ImprovCommandStruct_t command = {0};
if (position < 7)
return byte == improv_prefix[position];
if (position <= 8)
return true;
uint8_t command_type = buffer[7];
uint8_t data_len = buffer[8];
if (position <= 8 + data_len)
return true;
if (position == 8 + data_len + 1)
{
uint8_t checksum = 0x00;
for (size_t i = 0; i < position; i++)
checksum += buffer[i];
if (checksum != byte)
{
on_error(IMPROV_ERROR_INVALID_RPC);
return false;
}
if (command_type == IMPROV_PACKET_TYPE_RPC)
{
improv_parse_data(&command, &buffer[9], data_len, false);
callback(&command);
}
}
return false;
}
void improv_set_send_callback(improv_send_callback_t callback)
{
send_callback = callback;
}
bool improv_set_callback(ImprovCommand_t command, improv_command_callback_t callback)
{
callback_table_t *pCt = &callbacks;
while (pCt->index > -1)
{
if (pCt->index == command)
{
pCt->callback = callback;
return true;
}
}
return false;
}
bool improv_handle_callback(ImprovCommandStruct_t *command)
{
const callback_table_t *pCt = &callbacks;
while (pCt->index > -1)
{
if (pCt->index == command->command)
{
return pCt->callback && pCt->callback(command);
}
}
return false;
}
bool improv_send_packet(uint8_t *packet, size_t msg_len)
{
bool result = false;
if (send_callback && packet && msg_len > 0)
{
result = send_callback(packet, msg_len);
}
return result;
}
bool improv_send_byte(ImprovSerialType_t packet_type, uint8_t data)
{
size_t msg_len;
uint8_t *packet = improv_build_response(packet_type, (const char *)&data, 1, &msg_len);
bool result = improv_send_packet(packet, msg_len);
FREE_AND_NULL(packet);
return result;
}
bool improv_send_current_state(ImprovState_t state)
{
return improv_send_byte(IMPROV_PACKET_TYPE_CURRENT_STATE, (uint8_t)state);
}
bool improv_send_error(ImprovError_t error)
{
return improv_send_byte(IMPROV_PACKET_TYPE_ERROR_STATE, (uint8_t)error);
}
size_t improv_wifi_get_wifi_list_count(){
return ap_list_actual;
}
bool improv_wifi_list_send()
{
size_t msglen = 0;
bool result = true;
if (ap_list_actual == 0)
{
return false;
}
for (int i = 0; i < ap_list_actual && result; i++)
{
uint8_t *packet = improv_build_rpc_response(IMPROV_CMD_GET_WIFI_NETWORKS,(const char **) &ap_list[i], IMPROV_AP_STRUCT_NUM_STR, &msglen);
result = improv_send_packet(packet, msglen);
FREE_AND_NULL(packet);
}
uint8_t *packet = improv_build_rpc_response(IMPROV_CMD_GET_WIFI_NETWORKS, NULL, 0, &msglen);
result = improv_send_packet(packet, msglen);
FREE_AND_NULL(packet);
return result;
}
bool improv_send_device_url(ImprovCommand_t from_command, const char *url)
{
size_t msglen = 0;
uint8_t *packet = NULL;
bool result = false;
if (url && strlen(url))
{
packet = improv_build_rpc_response(from_command, &url, 1, &msglen);
if (!packet)
return false;
result = improv_send_packet(packet, msglen);
FREE_AND_NULL(packet);
}
packet = improv_build_rpc_response(from_command, "", 0, &msglen);
if (!packet)
return false;
result = improv_send_packet(packet, msglen);
return result;
}
bool improv_send_device_info(const char *firmware_name, const char *firmware_version, const char *hardware_chip_variant, const char *device_name)
{
ImprovDeviceInfoStruct_t device_info;
size_t msglen = 0;
device_info.device_name = device_name;
device_info.firmware_name = firmware_name;
device_info.firmware_version = firmware_version;
device_info.hardware_chip_variant = hardware_chip_variant;
device_info.nullptr = NULL;
uint8_t *packet = improv_build_rpc_response(IMPROV_CMD_GET_DEVICE_INFO, &device_info, IMPROV_DEVICE_INFO_NUM_STRINGS, &msglen);
if (!packet)
return false;
bool result = improv_send_packet(packet, msglen);
FREE_AND_NULL(packet);
return true;
}
bool parse_improv_serial_line(const uint8_t *buffer)
{
const uint8_t *b = buffer;
const uint8_t *p = improv_prefix;
const uint8_t *data = NULL;
uint8_t checksum = 0x00;
uint8_t rec_checksum = 0x00;
while (*p != '\0' && *b != '\0')
{
// check if line prefix matches the standard
if (*p++ != *b++)
{
return false;
}
}
uint8_t command_type = *p++;
if (command_type == 0)
return false;
uint8_t data_len = *p++;
data = p;
rec_checksum = buffer[sizeof(improv_prefix) + data_len];
for (size_t i = 0; i < sizeof(improv_prefix) + data_len; i++)
{
checksum += buffer[i];
}
if (checksum != rec_checksum)
{
improv_send_error(IMPROV_ERROR_INVALID_RPC);
return false;
}
if (command_type == IMPROV_PACKET_TYPE_RPC)
{
improv_parse_data(&last_command, &data, data_len, false);
return improv_handle_callback(&last_command);
}
return false;
}
// Improv packet format
// 1-6 Header will equal IMPROV
// 7 Version CURRENT VERSION = 1
// 8 Type (see below)
// 9 Length
// 10...X Data
// X + 10 Checksum
improv_packet_t *improv_alloc_prefix(size_t data_len, ImprovSerialType_t packet_type, size_t *out_len)
{
size_t buffer_len = sizeof(improv_packet_t) + data_len + 1; // one byte for checksum
if (out_len)
{
*out_len = buffer_len;
}
improv_packet_t *out = (improv_packet_t *)malloc_init_external(buffer_len + 1);
memcpy(out, improv_prefix, sizeof(improv_prefix));
out->packet_type = (uint8_t)packet_type;
out->data_len = (uint8_t)data_len;
return out;
}
uint8_t improv_set_checksum(improv_packet_t *data, size_t buffer_len)
{
uint32_t calculated_checksum = 0;
for (int b = 0; b < buffer_len - 1; b++)
{
calculated_checksum += ((uint8_t *)data)[b];
}
calculated_checksum = calculated_checksum & 0xFF;
((uint8_t *)data)[buffer_len - 1] = (uint8_t)calculated_checksum;
return calculated_checksum;
}
uint8_t *improv_build_response(ImprovSerialType_t packet_type, const char *datum, size_t len, size_t *out_len)
{
size_t buffer_len = 0;
improv_packet_t *improv_packet = improv_alloc_prefix(len, packet_type, &buffer_len);
if (out_len)
{
*out_len = buffer_len;
}
uint8_t *p = PACKET_PAYLOAD(improv_packet);
for (int i = 0; i < len; i++)
{
*p++ = datum[i]; // string 1
}
improv_set_checksum(improv_packet, buffer_len);
return (uint8_t *)improv_packet;
}
uint8_t *improv_build_rpc_response(ImprovCommand_t command, const char **results, size_t num_strings, size_t *out_len)
{
size_t buffer_len = 0;
size_t total_string_len = 0;
size_t string_buffer_len = 0;
for (int i = 0; i < num_strings && (results[i] && (results[i])[0] != '\0'); i++)
{
size_t l = strlen(results[i]);
total_string_len += l;
string_buffer_len += l + 1; // length of the string plus byte for length
}
improv_packet_t *improv_packet = improv_alloc_prefix(string_buffer_len + 2, IMPROV_PACKET_TYPE_RPC_RESPONSE, &buffer_len); // 2 bytes for command and length of all strings
if (out_len)
{
*out_len = buffer_len;
}
uint8_t *p = PACKET_PAYLOAD(improv_packet);
*p++ = (uint8_t)command; // command being responded to
*p++ = (uint8_t)string_buffer_len; //
for (int i = 0; i < num_strings && results[i]; i++)
{
uint8_t curlel = (uint8_t)strlen(results[i]);
*p++ = curlel;
memcpy(p, results[i], curlel);
p += curlel;
}
improv_set_checksum(improv_packet, buffer_len);
return (uint8_t *)improv_packet;
}

View File

@@ -0,0 +1,279 @@
#pragma once
// This is the description of the Improv Wi-Fi protocol using a serial port.
// The device needs to be connected to the computer via a USB/UART serial port.
// The protocol has two actors: the Improv service running on the gadget and the Improv client.
// The Improv service will receive Wi-Fi credentials from the client via the serial connection.
// The Improv client asks for the current state and sends the Wi-Fi credentials.
// =========================================================================================
// Packet format
// ======================================
// Byte Purpose
// ---- -------------------------------
// 1-6 Header will equal IMPROV
// 7 Version CURRENT VERSION = 1
// 8 Type (see below)
// 9 Length
// 10...X Data
// X + 10 Checksum
// =========================================================================================
// Packet types
// ======================================
// Type Description Direction
// ---- ------------ -----------------
// 0x01 Current state Device to Client
// 0x02 Error state Device to Client
// 0x03 RPC Command Device to Client
// 0x04 RPC Result Client to Device
typedef enum {
IMPROV_PACKET_TYPE_CURRENT_STATE = 0x01,
IMPROV_PACKET_TYPE_ERROR_STATE = 0x02,
IMPROV_PACKET_TYPE_RPC = 0x03,
IMPROV_PACKET_TYPE_RPC_RESPONSE = 0x04
} ImprovSerialType_t;
// =========================================================================================
// Packet: Current State
// ======================================
// Type: 0x01
// Direction: Device to Client
// --------------------------------------
// The data of this packet is a single byte and contains the current status of the provisioning
// service. It is to be written to any listening clients for instant feedback.
// Byte Description
// 1 current state
// The current state can be the following values:
// Value State Purpose
// ----- ------------------ -----------------------------------------
// 0x02 Ready (Authorized) Ready to accept credentials.
// 0x03 Provisioning Credentials received, attempt to connect.
// 0x04 Provisioned Connection successful.
typedef enum {
IMPROV_STATE_READY_AUTHORIZED = 0x02,
IMPROV_STATE_PROVISIONING = 0x03,
IMPROV_STATE_PROVISIONED = 0x04,
} ImprovState_t;
// =========================================================================================
// Packet: Error state
// ======================================
// Type: 0x02
// Direction: Device to client
// --------------------------------------
// The data of this packet is a single byte and contains the current status of the
// provisioning service. Whenever it changes the device needs to sent it to any listening
// clients for instant feedback.
// Byte Description
// 1 error state
// Error state can be the following values:
// Value State Purpose
// ----- ------------------ -----------------------------------------
// 0x00 No error This shows there is no current error state.
// 0x01 Invalid RPC packet RPC packet was malformed/invalid.
// 0x02 Unknown RPC command The command sent is unknown.
// 0x03 Unable to connect The credentials have been received and an attempt to connect
// to the network has failed.
// 0xFF Unknown Error
typedef enum {
IMPROV_ERROR_NONE = 0x00,
IMPROV_ERROR_INVALID_RPC = 0x01,
IMPROV_ERROR_UNKNOWN_RPC = 0x02,
IMPROV_ERROR_UNABLE_TO_CONNECT = 0x03,
IMPROV_ERROR_NOT_AUTHORIZED = 0x04,
IMPROV_ERROR_UNKNOWN = 0xFF,
} ImprovError_t;
// =========================================================================================
// Packet: RPC Command
// Type: 0x03
// Direction: Client to device
// --------------------------------------
// This packet type is used for the client to send commands to the device. When an RPC
// command is sent, the device should sent an update to the client to set the error state to
// 0 (no error). The response will either be an RPC result packet or an error state update.
// Byte Description
// ----- ---------------------
// 1 Command (see below)
// 2 Data length
// 3...X Data
typedef enum {
IMPROV_CMD_UNKNOWN = 0x00,
IMPROV_CMD_WIFI_SETTINGS = 0x01,
IMPROV_CMD_GET_CURRENT_STATE = 0x02,
IMPROV_CMD_GET_DEVICE_INFO = 0x03,
IMPROV_CMD_GET_WIFI_NETWORKS = 0x04,
IMPROV_CMD_BAD_CHECKSUM = 0xFF,
} ImprovCommand_t;
// ======================================
// RPC Command: Send Wi-Fi settings
// Submit Wi-Fi credentials to the Improv Service to attempt to connect to.
// Type: 0x03
// Command ID: 0x01
// Byte Description
// ----- ----------------
// 1 command (0x01)
// 2 data length
// 3 ssid length
// 4...X ssid bytes
// X password length
// X...Y password bytes
// Example: SSID = MyWirelessAP, Password = mysecurepassword
// 01 1E 0C {MyWirelessAP} 10 {mysecurepassword}
// This command will generate an RPC result. The first entry in the list is an URL to
// redirect the user to.
// If there is no URL, omit the entry or add an empty string.
// ======================================
// RPC Command: Request current state
// Sends a request for the device to send the current state of improv to the client.
// Type: 0x03
// Command ID: 0x02
// Byte Description
// ----- ----------------
// 1 command (0x02)
// 2 data length (0)
// This command will trigger at least one packet, the Current State (see above) and if
// already provisioned,
// the same response you would get if device provisioning was successful (see below).
// ======================================
// RPC Command: Request device information
// Sends a request for the device to send information about itself.
// Type: 0x03
// Command ID: 0x03
// Byte Description
// ----- ----------------
// 1 command (0x02)
// 2 data length (0)
// This command will trigger one packet, the Device Information formatted as a RPC result.
// This result will contain at least 4 strings.
// Order of strings: Firmware name, firmware version, hardware chip/variant, device name.
// Example: ESPHome, 2021.11.0, ESP32-C3, Temperature Monitor.
// ======================================
// RPC Command: Request scanned Wi-Fi networks
// Sends a request for the device to send the Wi-Fi networks it sees.
// Type: 0x03
// Command ID: 0x04
// Byte Description
// ----- ----------------
// 1 command (0x02)
// 2 data length (0)
// This command will trigger at least one RPC Response. Each response will contain at
// least 3 strings.
// Order of strings: Wi-Fi SSID, RSSI, Auth required.
// Example: MyWirelessNetwork, -60, YES.
// The final response (or the first if no networks are found) will have 0 strings in the body.
// =========================================================================================
// Packet: RPC Result
// ======================================
// Type: 0x04
// Direction: Device to client
// --------------------------------------
// This packet type contains the response to an RPC command. Results are returned as a list
// of strings. An empty list is allowed.
// Byte Description
// ----- ----------------
// 1 Command being responded to (see above)
// 2 Data length
// 3 Length of string 1
// 4...X String 1
// X Length of string 2
// X...Y String 2
// ... etc
static const uint8_t CAPABILITY_IDENTIFY = 0x01;
static const uint8_t IMPROV_SERIAL_VERSION = 1;
#ifndef FREE_AND_NULL
#define FREE_AND_NULL(x) if(x) { free(x); x=NULL; }
#endif
#ifndef ENUM_TO_STRING
#define ENUM_TO_STRING(g) \
case g: \
return STR(g); \
break;
#endif
typedef struct {
ImprovCommand_t command;
char * ssid;
char * password;
} ImprovCommandStruct_t;
typedef struct {
char * ssid;
char * rssi;
char * auth_req; // YES/NO
} ImprovAPListStruct_t;
#define IMPROV_AP_STRUCT_NUM_STR 3
typedef struct {
char * firmware_name;
char * firmware_version;
char * hardware_chip_variant;
char * device_name;
char * nullptr;
} ImprovDeviceInfoStruct_t;
#define IMPROV_DEVICE_INFO_NUM_STRINGS 4
typedef bool (*improv_command_callback_t)(ImprovCommandStruct_t *cmd);
typedef void (*on_error_callback_t)(ImprovError_t error);
typedef bool (*improv_send_callback_t)(uint8_t * buffer, size_t length);
typedef struct {
int index ;
improv_command_callback_t callback ;
} callback_table_t;
void improv_parse_data(ImprovCommandStruct_t * improv_command, const uint8_t *data, size_t length, bool check_checksum) ;
bool improv_parse_serial_byte(size_t position, uint8_t byte, const uint8_t *buffer,improv_command_callback_t callback, on_error_callback_t on_error);
bool parse_improv_serial_line( const uint8_t *buffer);
void improv_set_send_callback(improv_send_callback_t callback );
bool improv_set_callback(ImprovCommand_t command, improv_command_callback_t callback );
bool improv_wifi_list_allocate(size_t num_entries);
bool improv_wifi_list_add(const char * ssid, int8_t rssi, bool auth_req );
bool improv_wifi_list_send( );
size_t improv_wifi_get_wifi_list_count();
bool improv_send_device_info( const char * firmware_name, const char * firmware_version, const char * hardware_chip_variant, const char * device_name);
uint8_t * improv_build_response(ImprovSerialType_t command, const char * datum, size_t len, size_t * out_len);
uint8_t * improv_build_rpc_response(ImprovCommand_t command, const char ** results, size_t num_strings, size_t * out_len);
bool improv_send_current_state(ImprovState_t state);
bool improv_send_error(ImprovError_t error);
const char * improv_get_error_desc(ImprovError_t error);
const char * improv_get_command_desc(ImprovCommand_t command);
bool improv_send_device_url( ImprovCommand_t from_command, const char * url);
// Improv Wi-Fi Contact GitHub
// Improv is an initiative by ESPHome & Home Assistant.
// Development funded by Nabu Casa.

View File

@@ -62,20 +62,23 @@ typedef struct network_callback {
SLIST_ENTRY(network_callback)
next; //!< next callback
} network_callback_t;
static wifi_connect_state_t wifi_connect_state = NETWORK_WIFI_STATE_INIT;
/** linked list of command structures */
static SLIST_HEAD(cb_list, network_callback) s_cb_list;
network_t NM;
//! Create and initialize the array of state machines.
state_machine_t* const SM[] = {(state_machine_t*)&NM};
static void network_timer_cb(void* timer_id);
int get_root_id(const state_t * state);
const state_t* get_root( const state_t* const state);
static void network_task(void* pvParameters);
void network_wifi_set_connect_state(wifi_connect_state_t state){
wifi_connect_state = state;
}
wifi_connect_state_t network_wifi_get_connect_state(){
return wifi_connect_state;
}
void network_start_stop_dhcp_client(esp_netif_t* netif, bool start) {
tcpip_adapter_dhcp_status_t status;
esp_err_t err = ESP_OK;
@@ -195,17 +198,21 @@ void network_start_stop_dhcps(esp_netif_t* netif, bool start) {
#define ADD_LEAF(name,...) CASE_TO_STR(name);
#define ADD_EVENT(name) CASE_TO_STR(name);
#define ADD_FIRST_EVENT(name) CASE_TO_STR(name);
static const char* state_to_string(const state_t * state) {
if(!state) {
return "";
}
switch (state->Parent?state->Parent->Id:state->Id) {
static const char* nm_state_to_string(nm_state_t state) {
switch (state) {
ALL_NM_STATE
default:
break;
}
return "Unknown";
}
static const char* state_to_string(const state_t * state) {
if(!state) {
return "";
}
return nm_state_to_string(state->Parent?state->Parent->Id:state->Id);
}
static const char* wifi_state_to_string(mn_wifi_active_state_t state) {
switch (state) {
ALL_WIFI_STATE(,)
@@ -230,25 +237,27 @@ static const char* wifi_configuring_state_to_string(mn_wifi_configuring_state_t
}
return "Unknown";
}
static const char* sub_state_to_string(const state_t * state) {
if(!state) {
return "N/A";
}
int root_id = get_root_id(state);
switch (root_id)
static const char* sub_state_id_to_string(nm_state_t state, int substate) {
switch (state)
{
case NETWORK_ETH_ACTIVE_STATE:
return eth_state_to_string(state->Id);
return eth_state_to_string(substate);
break;
case NETWORK_WIFI_ACTIVE_STATE:
return wifi_state_to_string(state->Id);
return wifi_state_to_string(substate);
case NETWORK_WIFI_CONFIGURING_ACTIVE_STATE:
return wifi_configuring_state_to_string(state->Id);
return wifi_configuring_state_to_string(substate);
default:
break;
}
return "*";
}
static const char* sub_state_to_string(const state_t * state) {
if(!state) {
return "N/A";
}
return sub_state_id_to_string(get_root_id(state), state->Id);
}
static const char* event_to_string(network_event_t state) {
switch (state) {
@@ -274,8 +283,7 @@ static const max_sub_states_t state_max[] = {
{ .parent_state = NETWORK_INSTANTIATED_STATE, .sub_state_last = 0 },
{.parent_state = NETWORK_ETH_ACTIVE_STATE, .sub_state_last = TOTAL_ETH_ACTIVE_STATE-1 },
{.parent_state = NETWORK_WIFI_ACTIVE_STATE, .sub_state_last = TOTAL_WIFI_ACTIVE_STATE-1 },
{.parent_state = WIFI_CONFIGURING_STATE, .sub_state_last = TOTAL_WIFI_CONFIGURING_STATE-1 },
{.parent_state = WIFI_CONFIGURING_STATE, .sub_state_last = TOTAL_WIFI_CONFIGURING_STATE-1 },
{.parent_state = NETWORK_WIFI_CONFIGURING_ACTIVE_STATE, .sub_state_last = TOTAL_WIFI_CONFIGURING_STATE-1 },
{.parent_state =-1}
};
@@ -345,23 +353,27 @@ static void network_task(void* pvParameters) {
return -1;
}
esp_err_t network_register_state_callback(nm_state_t state,int sub_state, const char* from, network_status_reached_cb cb) {
const char * error_prefix = "Error registering callback for State" ;
network_callback_t* item = NULL;
if (!cb) {
return ESP_ERR_INVALID_ARG;
}
item = calloc(1, sizeof(*item));
if (item == NULL) {
ESP_LOGE(TAG,"%s %s. Memory allocation failed",error_prefix, nm_state_to_string(state));
return ESP_ERR_NO_MEM;
}
if(sub_state != -1 && sub_state>get_max_substate(state)){
// sub state has to be valid
ESP_LOGE(TAG,"%s %s. Substate %d/%d %s",error_prefix, nm_state_to_string(state), sub_state,get_max_substate(state), sub_state>get_max_substate(state)?"out of boundaries":"invalid");
return ESP_ERR_INVALID_ARG;
}
ESP_LOGI(TAG,"Registering callback for State %s, substate %s: %s", nm_state_to_string(state), sub_state_id_to_string(state,sub_state), from);
item->state = state;
item->cb = cb;
item->from = from;
item->sub_state=sub_state;
network_callback_t* last = SLIST_FIRST(&s_cb_list);
if (last == NULL) {
SLIST_INSERT_HEAD(&s_cb_list, item, next);
@@ -389,19 +401,26 @@ static bool is_root_state(const state_t * state){
static bool is_current_state(const state_t* state, nm_state_t state_id, int sub_state_id){
return get_root(state)->Id == state_id && (sub_state_id==-1 || (!is_root_state(state) && state->Id == sub_state_id) );
}
void network_execute_cb(state_machine_t* const state_machine, const char * caller) {
network_callback_t* it;
bool found=false;
ESP_LOGI(TAG,"Checking if we need to invoke callbacks. ");
SLIST_FOREACH(it, &s_cb_list, next) {
if (is_current_state(state_machine->State,it->state, it->sub_state)) {
char * cb_prefix= messaging_alloc_format_string("BEGIN Executing Callback %s", it->from) ;
NETWORK_DEBUG_STATE_MACHINE(true,STR_OR_BLANK(cb_prefix),state_machine,false, STR_OR_BLANK(caller));
FREE_AND_NULL(cb_prefix);
it->cb((nm_state_t)get_root(state_machine->State)->Id, is_root_state(state_machine->State)?-1:state_machine->State->Id);
found = true;
cb_prefix= messaging_alloc_format_string("END Executing Callback %s", it->from) ;
NETWORK_DEBUG_STATE_MACHINE(false,STR_OR_BLANK(cb_prefix),state_machine,false, STR_OR_BLANK(caller));
FREE_AND_NULL(cb_prefix);
}
}
if(!found){
NETWORK_DEBUG_STATE_MACHINE(true,"No Callback found ",state_machine,false, STR_OR_BLANK(caller));
}
}
bool network_is_wifi_prioritized() {
@@ -465,7 +484,7 @@ void network_manager_format_from_to_states(esp_log_level_t level, const char* pr
source_sub_state = sub_state_to_string(from_state);
}
if (show_source) {
ESP_LOG_LEVEL(level, TAG, "%s %s %s(%s)->%s(%s) [%s]",
ESP_LOG_LEVEL(level, TAG, "%s %s %s.%s->%s.%s [evt:%s]",
STR_OR_BLANK(caller),
prefix,
source_state,
@@ -724,19 +743,23 @@ void network_ip_event_handler(void* arg, esp_event_base_t event_base, int32_t ev
break;
}
}
void network_set_hostname(esp_netif_t* interface) {
esp_err_t err;
char * alloc_get_host_name(){
ESP_LOGD(TAG, "Retrieving host name from nvs");
char* host_name = (char*)config_alloc_get(NVS_TYPE_STR, "host_name");
if (host_name == NULL) {
ESP_LOGE(TAG, "Could not retrieve host name from nvs");
} else {
ESP_LOGD(TAG, "Setting host name to : %s", host_name);
if ((err = esp_netif_set_hostname(interface, host_name)) != ESP_OK) {
ESP_LOGE(TAG, "Unable to set host name. Error: %s", esp_err_to_name(err));
}
free(host_name);
}
return host_name;
}
void network_set_hostname(esp_netif_t* interface) {
esp_err_t err;
char * host_name = alloc_get_host_name();
if(!host_name) return;
ESP_LOGD(TAG, "Setting host name to : %s", host_name);
if ((err = esp_netif_set_hostname(interface, host_name)) != ESP_OK) {
ESP_LOGE(TAG, "Unable to set host name. Error: %s", esp_err_to_name(err));
}
free(host_name);
}
#define LOCAL_MAC_SIZE 20
char* network_manager_alloc_get_mac_string(uint8_t mac[6]) {

View File

@@ -10,6 +10,7 @@
#include "hsm.h"
#include "esp_log.h"
#include "network_services.h"
#include "improv.h"
#ifdef __cplusplus
extern "C" {
@@ -257,10 +258,20 @@ typedef enum update_reason_code_t {
UPDATE_LOST_CONNECTION = 3,
UPDATE_FAILED_ATTEMPT_AND_RESTORE = 4,
UPDATE_ETHERNET_CONNECTED = 5
}update_reason_code_t;
typedef enum {
NETWORK_WIFI_STATE_INIT,
NETWORK_WIFI_STATE_CONNECTING,
NETWORK_WIFI_STATE_DOWN,
NETWORK_WIFI_STATE_INVALID_CONFIG,
NETWORK_WIFI_STATE_FAILED,
NETWORK_WIFI_STATE_CONNECTED
} wifi_connect_state_t;
void network_wifi_set_connect_state(wifi_connect_state_t state);
wifi_connect_state_t network_wifi_get_connect_state();
@@ -312,6 +323,7 @@ void network_manager_initialise_mdns();
bool network_is_wifi_prioritized();
void network_set_timer(uint16_t duration, const char * tag);
void network_set_hostname(esp_netif_t * netif);
char * alloc_get_host_name();
esp_err_t network_get_ip_info_for_netif(esp_netif_t* netif, tcpip_adapter_ip_info_t* ipInfo);
void network_start_stop_dhcp_client(esp_netif_t* netif, bool start);
void network_start_stop_dhcps(esp_netif_t* netif, bool start);

View File

@@ -1,7 +1,10 @@
#ifdef NETWORK_HANDLERS_LOG_LEVEL
#define LOG_LOCAL_LEVEL NETWORK_HANDLERS_LOG_LEVEL
#pragma message("Log Level overwritten to " LOG_LOCAL_LEVEL)
#else
#pragma message("Log Level set to " LOG_LOCAL_LEVEL)
#endif
#include "network_manager.h"
#include "network_manager.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@@ -42,6 +45,7 @@
#include "tools.h"
#include "http_server_handlers.h"
#include "network_manager.h"
#include "improv.h"
static const char TAG[]="network_handlers";
@@ -387,6 +391,7 @@ static state_machine_result_t NETWORK_ETH_ACTIVE_STATE_handler(state_machine_t*
case EN_SCAN:
ESP_LOGW(TAG,"Wifi scan cannot be executed in this state");
network_wifi_built_known_ap_list();
result = EVENT_HANDLED;
break;
case EN_DELETE: {
@@ -418,7 +423,9 @@ static state_machine_result_t ETH_CONNECTING_NEW_STATE_entry_handler(state_machi
network_t* const nm = (network_t *)State_Machine;
network_handler_entry_print(State_Machine,true);
network_start_stop_dhcp_client(nm->wifi_netif, true);
network_wifi_connect(nm->event_parameters->ssid,nm->event_parameters->password);
if(network_wifi_connect(nm->event_parameters->ssid,nm->event_parameters->password) == ESP_ERR_INVALID_ARG){
network_async_fail();
}
FREE_AND_NULL(nm->event_parameters->ssid);
FREE_AND_NULL(nm->event_parameters->password);
NETWORK_EXECUTE_CB(State_Machine);
@@ -430,6 +437,11 @@ static state_machine_result_t ETH_CONNECTING_NEW_STATE_handler(state_machine_t*
network_handler_print(State_Machine,true);
state_machine_result_t result = EVENT_HANDLED;
switch (State_Machine->Event) {
case EN_FAIL:
ESP_LOGW(TAG,"Error connecting to access point");
network_status_update_ip_info(UPDATE_FAILED_ATTEMPT);
result = local_traverse_state(State_Machine, &Wifi_Configuring_State[WIFI_CONFIGURING_STATE],__FUNCTION__);
break;
case EN_GOT_IP:
result= local_traverse_state(State_Machine, &network_states[WIFI_CONNECTED_STATE],__FUNCTION__);
break;
@@ -621,6 +633,7 @@ static state_machine_result_t NETWORK_WIFI_CONFIGURING_ACTIVE_STATE_entry_handle
nm->wifi_ap_netif = network_wifi_config_ap();
dns_server_start(nm->wifi_ap_netif);
network_wifi_start_scan();
NETWORK_EXECUTE_CB(State_Machine);
network_handler_entry_print(State_Machine,false);
return EVENT_HANDLED;
}
@@ -649,6 +662,9 @@ static state_machine_result_t NETWORK_WIFI_CONFIGURING_ACTIVE_STATE_handler(stat
case EN_ETH_GOT_IP:
network_interface_coexistence(State_Machine);
break;
case EN_TIMER:
result= EVENT_HANDLED;
break;
default:
result =EVENT_UN_HANDLED;
}
@@ -696,7 +712,9 @@ static state_machine_result_t WIFI_CONFIGURING_CONNECT_STATE_entry_handler(state
network_t* const nm = (network_t *)State_Machine;
network_handler_entry_print(State_Machine,true);
network_start_stop_dhcp_client(nm->wifi_netif, true);
network_wifi_connect(nm->event_parameters->ssid,nm->event_parameters->password);
if(network_wifi_connect(nm->event_parameters->ssid,nm->event_parameters->password) == ESP_ERR_INVALID_ARG){
network_async_fail();
}
FREE_AND_NULL(nm->event_parameters->ssid);
FREE_AND_NULL(nm->event_parameters->password);
NETWORK_EXECUTE_CB(State_Machine);
@@ -718,13 +736,16 @@ static state_machine_result_t WIFI_CONFIGURING_CONNECT_STATE_handler(state_machi
network_status_update_ip_info(UPDATE_CONNECTION_OK);
result= local_traverse_state(State_Machine, &Wifi_Configuring_State[WIFI_CONFIGURING_CONNECT_SUCCESS_STATE],__FUNCTION__);
break;
case EN_FAIL:
ESP_LOGW(TAG,"Error connecting to access point");
result = local_traverse_state(State_Machine, &Wifi_Configuring_State[WIFI_CONFIGURING_CONNECT_FAILED_STATE],__FUNCTION__);
break;
case EN_LOST_CONNECTION:
if(nm->event_parameters->disconnected_event->reason == WIFI_REASON_ASSOC_LEAVE) {
ESP_LOGI(TAG,"Wifi was disconnected from previous access point. Waiting to connect.");
}
else {
network_status_update_ip_info(UPDATE_FAILED_ATTEMPT);
result = local_traverse_state(State_Machine, &Wifi_Configuring_State[WIFI_CONFIGURING_STATE],__FUNCTION__);
result = local_traverse_state(State_Machine, &Wifi_Configuring_State[WIFI_CONFIGURING_CONNECT_FAILED_STATE],__FUNCTION__);
}
break;
case EN_TIMER:
@@ -747,6 +768,43 @@ static state_machine_result_t WIFI_CONFIGURING_CONNECT_STATE_exit_handler(state_
return EVENT_HANDLED;
}
/*********************************************************************************************
* WIFI_CONFIGURING_CONNECT_FAILED_STATE
*/
static state_machine_result_t WIFI_CONFIGURING_CONNECT_FAILED_STATE_entry_handler(state_machine_t* const State_Machine) {
network_handler_entry_print(State_Machine,true);
network_status_update_ip_info(UPDATE_FAILED_ATTEMPT);
ESP_LOGE(TAG, "Connecting Failed.");
NETWORK_EXECUTE_CB(State_Machine);
network_async_fail();
network_handler_entry_print(State_Machine,false);
return EVENT_HANDLED;
}
static state_machine_result_t WIFI_CONFIGURING_CONNECT_FAILED_STATE_handler(state_machine_t* const State_Machine) {
network_handler_print(State_Machine,true);
state_machine_result_t result = EVENT_HANDLED;
network_t* const nm = (network_t *)State_Machine;
switch (State_Machine->Event) {
case EN_FAIL:
result = local_traverse_state(State_Machine, &Wifi_Configuring_State[WIFI_CONFIGURING_STATE],__FUNCTION__);
break;
default:
result= EVENT_HANDLED;
}
// Process global handler at the end, since we want to overwrite
// UPDATE_STATUS with our own logic above
HANDLE_GLOBAL_EVENT(State_Machine);
network_handler_print(State_Machine,false);
return result;
}
static state_machine_result_t WIFI_CONFIGURING_CONNECT_FAILED_STATE_exit_handler(state_machine_t* const State_Machine) {
network_exit_handler_print(State_Machine,true);
network_set_timer(0,NULL);
network_exit_handler_print(State_Machine,false);
return EVENT_HANDLED;
}
/*********************************************************************************************
* WIFI_CONFIGURING_CONNECT_SUCCESS_STATE
*/
@@ -827,6 +885,7 @@ static state_machine_result_t WIFI_CONNECTING_STATE_handler(state_machine_t* con
}
else if(nm->event_parameters->disconnected_event->reason != WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT) {
network_status_update_ip_info(UPDATE_FAILED_ATTEMPT);
network_wifi_set_connect_state(NETWORK_WIFI_STATE_FAILED);
result = local_traverse_state(State_Machine, &Wifi_Configuring_State[WIFI_CONFIGURING_STATE],__FUNCTION__);
}
break;
@@ -838,6 +897,7 @@ static state_machine_result_t WIFI_CONNECTING_STATE_handler(state_machine_t* con
}
static state_machine_result_t WIFI_CONNECTING_STATE_exit_handler(state_machine_t* const State_Machine) {
network_exit_handler_print(State_Machine,true);
network_set_timer(0,NULL);
network_exit_handler_print(State_Machine,false);
return EVENT_HANDLED;
}
@@ -849,7 +909,9 @@ static state_machine_result_t WIFI_CONNECTING_NEW_STATE_entry_handler(state_mach
network_t* const nm = (network_t *)State_Machine;
network_handler_entry_print(State_Machine,true);
network_start_stop_dhcp_client(nm->wifi_netif, true);
network_wifi_connect(nm->event_parameters->ssid,nm->event_parameters->password);
if(network_wifi_connect(nm->event_parameters->ssid,nm->event_parameters->password) == ESP_ERR_INVALID_ARG){
network_async_fail();
}
FREE_AND_NULL(nm->event_parameters->ssid);
FREE_AND_NULL(nm->event_parameters->password);
NETWORK_EXECUTE_CB(State_Machine);
@@ -859,8 +921,13 @@ static state_machine_result_t WIFI_CONNECTING_NEW_STATE_entry_handler(state_mach
static state_machine_result_t WIFI_CONNECTING_NEW_STATE_handler(state_machine_t* const State_Machine) {
HANDLE_GLOBAL_EVENT(State_Machine);
network_handler_print(State_Machine,true);
network_t* const nm = (network_t *)State_Machine;
state_machine_result_t result = EVENT_HANDLED;
switch (State_Machine->Event) {
case EN_FAIL:
ESP_LOGW(TAG,"Error connecting to access point");
result = local_traverse_state(State_Machine, &Wifi_Configuring_State[WIFI_CONNECTING_NEW_FAILED_STATE],__FUNCTION__);
break;
case EN_GOT_IP:
network_status_update_ip_info(UPDATE_CONNECTION_OK);
result= local_traverse_state(State_Machine, &Wifi_Active_State[WIFI_CONNECTED_STATE],__FUNCTION__);
@@ -901,6 +968,7 @@ static state_machine_result_t WIFI_CONNECTING_NEW_STATE_exit_handler(state_machi
static state_machine_result_t WIFI_CONNECTING_NEW_FAILED_STATE_entry_handler(state_machine_t* const State_Machine) {
network_t* const nm = (network_t *)State_Machine;
network_handler_entry_print(State_Machine,true);
network_wifi_set_connect_state(NETWORK_WIFI_STATE_FAILED);
if (nm->wifi_connected ) {
// Wifi was already connected to an existing access point. Restore connection
network_connect_active_ssid(State_Machine);
@@ -1140,18 +1208,18 @@ static state_machine_result_t ETH_ACTIVE_CONNECTED_STATE_exit_handler(state_mach
static state_machine_result_t local_switch_state(state_machine_t* state_machine,
const state_t* const target_state, const char * caller) {
const state_t* source = state_machine->State;
NETWORK_PRINT_TRANSITION(true, "BEGIN SWITCH", ((network_t *)state_machine)->source_state, target_state, state_machine->Event, true,caller);
NETWORK_PRINT_TRANSITION(true, "switch.begin", ((network_t *)state_machine)->source_state, target_state, state_machine->Event, true,caller);
state_machine_result_t result = switch_state(state_machine, target_state);
NETWORK_PRINT_TRANSITION( false,"BEGIN SWITCH", ((network_t *)state_machine)->source_state, target_state, state_machine->Event, true,caller);
NETWORK_PRINT_TRANSITION( false,"switch.end", ((network_t *)state_machine)->source_state, target_state, state_machine->Event, true,caller);
((network_t *)state_machine)->source_state = source;
return result;
}
static state_machine_result_t local_traverse_state(state_machine_t* const state_machine,
const state_t* const target_state, const char * caller) {
const state_t * source = state_machine->State;
NETWORK_PRINT_TRANSITION( true,"BEGIN TRAVERSE", ((network_t *)state_machine)->source_state, target_state, state_machine->Event, true, caller);
NETWORK_PRINT_TRANSITION( true,"traverse.begin", ((network_t *)state_machine)->source_state, target_state, state_machine->Event, true, caller);
state_machine_result_t result = traverse_state(state_machine, target_state);
NETWORK_PRINT_TRANSITION( false,"END TRAVERSE", ((network_t *)state_machine)->source_state, target_state, state_machine->Event, true,caller);
NETWORK_PRINT_TRANSITION( false,"traverse.end", ((network_t *)state_machine)->source_state, target_state, state_machine->Event, true,caller);
((network_t *)state_machine)->source_state = source;
return result;
}

View File

@@ -36,20 +36,23 @@ extern "C" {
#define ALL_WIFI_CONFIGURING_STATE(PARENT, LEVEL)\
ADD_LEAF(WIFI_CONFIGURING_STATE,PARENT,LEVEL)\
ADD_LEAF(WIFI_CONFIGURING_CONNECT_STATE,PARENT,LEVEL)\
ADD_LEAF(WIFI_CONFIGURING_CONNECT_FAILED_STATE,PARENT,LEVEL)\
ADD_LEAF(WIFI_CONFIGURING_CONNECT_SUCCESS_STATE,PARENT,LEVEL)
typedef enum {
ALL_NM_STATE
TOTAL_NM_STATE
} nm_state_t;
typedef enum {
ALL_WIFI_STATE(,)
TOTAL_WIFI_ACTIVE_STATE
} mn_wifi_active_state_t;
typedef enum {
ALL_ETH_STATE(,)
TOTAL_ETH_ACTIVE_STATE
} mn_eth_active_state_t;
typedef enum {
ALL_WIFI_STATE(,)
TOTAL_WIFI_ACTIVE_STATE
} mn_wifi_active_state_t;
typedef enum {
ALL_WIFI_CONFIGURING_STATE(,)
TOTAL_WIFI_CONFIGURING_STATE

View File

@@ -14,6 +14,7 @@
#include "platform_esp32.h"
#include "tools.h"
#include "trace.h"
#include "messaging.h"
#ifndef CONFIG_SQUEEZELITE_ESP32_RELEASE_URL
#pragma message "Defaulting release url"
#define CONFIG_SQUEEZELITE_ESP32_RELEASE_URL "https://github.com/sle118/squeezelite-esp32/releases"
@@ -33,7 +34,11 @@ static uint16_t lms_server_cport = 0;
static void (*chained_notify)(in_addr_t, u16_t, u16_t);
static void connect_notify(in_addr_t ip, u16_t hport, u16_t cport);
#define STA_IP_LEN sizeof(char) * IP4ADDR_STRLEN_MAX
static update_reason_code_t last_reason_code = -1;
void * get_http_server(int *port);
update_reason_code_t get_last_reason_code(){
return last_reason_code;
}
void init_network_status() {
chained_notify = server_notify;
server_notify = connect_notify;
@@ -169,6 +174,11 @@ void network_status_safe_reset_sta_ip_string() {
char* network_status_get_sta_ip_string() {
return network_status_ip_address;
}
char * network_status_alloc_get_system_url(){
int port=0;
void * server = get_http_server(&port);
return messaging_alloc_format_string("http://%s:%d/",network_status_ip_address,port);
}
void set_lms_server_details(in_addr_t ip, u16_t hport, u16_t cport) {
strncpy(lms_server_ip, inet_ntoa(ip), sizeof(lms_server_ip));
lms_server_ip[sizeof(lms_server_ip) - 1] = '\0';
@@ -297,11 +307,13 @@ void network_status_update_address(cJSON* root, esp_netif_ip_info_t* ip_info) {
ESP_LOGE(TAG, "Cannor update IP address. JSON structure or ip_info is null");
return;
}
network_status_safe_update_sta_ip_string(&ip_info->ip);
network_update_cjson_string(&root, "ip", ip4addr_ntoa((ip4_addr_t*)&ip_info->ip));
network_update_cjson_string(&root, "netmask", ip4addr_ntoa((ip4_addr_t*)&ip_info->netmask));
network_update_cjson_string(&root, "gw", ip4addr_ntoa((ip4_addr_t*)&ip_info->gw));
}
void network_status_update_ip_info(update_reason_code_t update_reason_code) {
last_reason_code = update_reason_code;
ESP_LOGV(TAG, "network_status_update_ip_info called");
esp_netif_ip_info_t ip_info;
if (network_status_lock_json_buffer(portMAX_DELAY)) {

View File

@@ -54,6 +54,8 @@ cJSON* network_status_get_basic_info(cJSON** old);
void network_status_update_basic_info();
void network_status_clear_ip();
void network_status_safe_reset_sta_ip_string();
update_reason_code_t get_last_reason_code();
char * network_status_alloc_get_system_url();
#ifdef __cplusplus
}
#endif

View File

@@ -739,6 +739,7 @@ static void network_wifi_event_handler(void* arg, esp_event_base_t event_base, i
wifi_event_sta_connected_t* s = (wifi_event_sta_connected_t*)event_data;
char* bssid = network_manager_alloc_get_mac_string(s->bssid);
char* ssid = strdup_psram((char*)s->ssid);
network_wifi_set_connect_state(NETWORK_WIFI_STATE_CONNECTED);
if (bssid && ssid) {
ESP_LOGD(TAG, "WIFI_EVENT_STA_CONNECTED. Channel: %d, Access point: %s, BSSID: %s ", s->channel, STR_OR_BLANK(ssid), (bssid));
}
@@ -773,6 +774,7 @@ static void network_wifi_event_handler(void* arg, esp_event_base_t event_base, i
ESP_LOGI(TAG, "WiFi Roaming to new access point");
} else {
network_async_lost_connection((wifi_event_sta_disconnected_t*)event_data);
network_wifi_set_connect_state(NETWORK_WIFI_STATE_FAILED);
}
} break;
@@ -841,8 +843,10 @@ void network_wifi_generate_access_points_json(cJSON** ap_list) {
known_access_point_t* it;
if (*ap_list == NULL)
return;
improv_wifi_list_allocate(ap_num);
for (int i = 0; i < ap_num; i++) {
network_wifi_add_access_point_json(*ap_list, &accessp_records[i]);
improv_wifi_list_add(ap_ssid_string(&accessp_records[i]),accessp_records[i].rssi, accessp_records[i].authmode!=WIFI_AUTH_OPEN);
}
SLIST_FOREACH(it, &s_ap_list, next) {
if (!network_wifi_was_ssid_seen(it->ssid)) {
@@ -1126,10 +1130,12 @@ esp_err_t network_wifi_connect(const char* ssid, const char* password) {
ESP_LOGD(TAG, "network_wifi_connect");
if (!is_wifi_up()) {
messaging_post_message(MESSAGING_WARNING, MESSAGING_CLASS_SYSTEM, "Wifi not started. Cannot connect");
network_wifi_set_connect_state(NETWORK_WIFI_STATE_DOWN);
return ESP_FAIL;
}
if (!ssid || !password || strlen(ssid) == 0) {
ESP_LOGE(TAG, "Cannot connect wifi. wifi config is null!");
network_wifi_set_connect_state(NETWORK_WIFI_STATE_INVALID_CONFIG);
return ESP_ERR_INVALID_ARG;
}
@@ -1159,12 +1165,17 @@ esp_err_t network_wifi_connect(const char* ssid, const char* password) {
config.sta.scan_method = WIFI_ALL_CHANNEL_SCAN;
if ((err = esp_wifi_set_config(WIFI_IF_STA, &config)) != ESP_OK) {
network_wifi_set_connect_state(NETWORK_WIFI_STATE_FAILED);
ESP_LOGE(TAG, "Failed to set STA configuration. Error %s", esp_err_to_name(err));
}
if (err == ESP_OK) {
ESP_LOGI(TAG, "Wifi Connecting to %s...", ssid);
if ((err = esp_wifi_connect()) != ESP_OK) {
ESP_LOGE(TAG, "Failed to initiate wifi connection. Error %s", esp_err_to_name(err));
network_wifi_set_connect_state(NETWORK_WIFI_STATE_FAILED);
}
else{
network_wifi_set_connect_state(NETWORK_WIFI_STATE_CONNECTING);
}
}
return err;

View File

@@ -70,6 +70,7 @@ size_t network_wifi_get_known_count_in_range();
esp_err_t network_wifi_built_known_ap_list();
esp_err_t network_wifi_connect_next_in_range();
const wifi_sta_config_t* network_wifi_load_active_config();
#ifdef __cplusplus
}
#endif

View File

@@ -1,5 +1,11 @@
{
"configurations": [
{
"command": "npm run dev",
"name": "Run npm dev",
"request": "launch",
"type": "node-terminal"
},
{
"type": "chrome",
"request": "launch",

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,68 +1,5 @@
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare let sd: {};
declare let rf: boolean;

View File

@@ -30,6 +30,6 @@ export namespace devServer {
const progress: boolean;
}
export function onListening(devServer: any): void;
export function onBeforeSetupMiddleware(devServer: any): void;
export function setupMiddlewares(middlewares: any, devServer: any): any;
}
export const plugins: HtmlWebPackPlugin[];

View File

@@ -44,19 +44,19 @@
</div>
<div class="info navbar-right" style="display: inline-flex;">
<span class="recovery_element material-icons " style="color:orange; display: none">system_update_alt</span>
<span id="battery" class="material-icons" style="fill:white; display: none">battery_full</span>
<span id="o_jack" class="material-icons" style="fill:white; display: none">headphones</span>
<span id="s_airplay" class="material-icons" style="fill:white; display: none">airplay</span>
<span class="recovery_element material-icons " style="color:orange; display: none" aria-label="🛑" >system_update_alt</span>
<span id="battery" class="material-icons" style="fill:white; display: none" aria-label="🔋" >battery_full</span>
<span id="o_jack" class="material-icons" style="fill:white; display: none" aria-label="🎧">headphones</span>
<span id="s_airplay" class="material-icons" style="fill:white; display: none" aria-label="🍎">airplay</span>
<em id="s_cspot" class="fab fa-spotify" style="fill:white; display: inline"></em>
<span data-bs-toggle="tooltip" id="o_type" data-bs-placement="top" title="">
<span id="o_bt" class="material-icons" style="fill:white; display: none">bluetooth</span>
<span id="o_spdif" class="material-icons" style="fill:white; display: none">graphic_eq</span>
<span id="o_i2s" class="material-icons" style="fill:white; display: none">speaker</span>
<span id="o_bt" class="material-icons" style="fill:white; display: none" aria-label="">bluetooth</span>
<span id="o_spdif" class="material-icons" style="fill:white; display: none" aria-label="">graphic_eq</span>
<span id="o_i2s" class="material-icons" style="fill:white; display: none" aria-label="🔈">speaker</span>
</span>
<span id="ethernet" class="material-icons if_eth" style="fill:white; display: none">cable</span>
<span id="ethernet" class="material-icons if_eth" style="fill:white; display: none" aria-label="ETH">cable</span>
<span id="wifiStsIcon" class="material-icons if_wifi"
style="fill:white; display: none">signal_wifi_statusbar_4_bar</span>
style="fill:white; display: none" aria-label=""></span>
</div>
</header>
@@ -374,7 +374,7 @@
</div>
</div>
</div>
<div class="card-body if_wifi" style="display: none">
<!-- <div class="card-body if_wifi" style="display: none">
<table class="table table-hover">
<thead>
<tr>
@@ -388,7 +388,7 @@
</table>
<button type="button" id="updateAP" class="btn btn-info btn-sm">Scan</button>
</div>
</div> -->

View File

@@ -77,22 +77,22 @@ const nvsTypes = {
NVS_TYPE_ANY: 0xff /*! < Must be last */,
};
const btIcons = {
bt_playing: 'media_bluetooth_on',
bt_disconnected: 'media_bluetooth_off',
bt_neutral: 'bluetooth',
bt_connecting: 'bluetooth_searching',
bt_connected: 'bluetooth_connected',
bt_disabled: 'bluetooth_disabled',
play_arrow: 'play_circle_filled',
pause: 'pause_circle',
stop: 'stop_circle',
'': '',
bt_playing: {'label':'','icon': 'media_bluetooth_on'},
bt_disconnected: {'label':'','icon': 'media_bluetooth_off'},
bt_neutral: {'label':'','icon': 'bluetooth'},
bt_connecting: {'label':'','icon': 'bluetooth_searching'},
bt_connected: {'label':'','icon': 'bluetooth_connected'},
bt_disabled: {'label':'','icon': 'bluetooth_disabled'},
play_arrow: {'label':'','icon': 'play_circle_filled'},
pause: {'label':'','icon': 'pause_circle'},
stop: {'label':'','icon': 'stop_circle'},
'': {'label':'','icon':''}
};
const batIcons = [
{ icon: "battery_0_bar", ranges: [{ f: 5.8, t: 6.8 }, { f: 8.8, t: 10.2 }] },
{ icon: "battery_2_bar", ranges: [{ f: 6.8, t: 7.4 }, { f: 10.2, t: 11.1 }] },
{ icon: "battery_3_bar", ranges: [{ f: 7.4, t: 7.5 }, { f: 11.1, t: 11.25 }] },
{ icon: "battery_4_bar", ranges: [{ f: 7.5, t: 7.8 }, { f: 11.25, t: 11.7 }] }
{ icon: "battery_0_bar", label:'▪', ranges: [{ f: 5.8, t: 6.8 }, { f: 8.8, t: 10.2 }] },
{ icon: "battery_2_bar", label:'▪▪', ranges: [{ f: 6.8, t: 7.4 }, { f: 10.2, t: 11.1 }] },
{ icon: "battery_3_bar", label:'▪▪▪', ranges: [{ f: 7.4, t: 7.5 }, { f: 11.1, t: 11.25 }] },
{ icon: "battery_4_bar", label:'▪▪▪▪', ranges: [{ f: 7.5, t: 7.8 }, { f: 11.25, t: 11.7 }] }
];
const btStateIcons = [
{ desc: 'Idle', sub: ['bt_neutral'] },
@@ -464,13 +464,19 @@ window.handleReboot = function (link) {
$('#reboot_nav').removeClass('active'); delayReboot(500, '', link);
}
}
function isConnected(){
return ConnectedTo.ip && ConnectedTo.ip!='0.0.0.0';
}
function getIcon(icons){
return isConnected()?icons.icon:icons.label;
}
function handlebtstate(data) {
let icon = '';
let tt = '';
if (data.bt_status !== undefined && data.bt_sub_status !== undefined) {
const iconindex = btStateIcons[data.bt_status].sub[data.bt_sub_status];
if (iconindex) {
icon = btIcons[iconindex];
icon = btIcons[iconindex];
tt = btStateIcons[data.bt_status].desc;
} else {
icon = btIcons.bt_connected;
@@ -479,7 +485,7 @@ function handlebtstate(data) {
}
$('#o_type').attr('title', tt);
$('#o_bt').html(icon);
$('#o_bt').html(isConnected()?icon.label:icon.text);
}
function handleTemplateTypeRadio(outtype) {
$('#o_type').children('span').css({ display: 'none' });
@@ -857,7 +863,6 @@ window.handleConnect = function () {
$("*[class*='connecting']").hide();
$('#ssid-wait').text(ConnectingToSSID.ssid);
$('.connecting').show();
$.ajax({
url: '/connect.json',
dataType: 'text',
@@ -876,6 +881,10 @@ window.handleConnect = function () {
}
$(document).ready(function () {
$('.material-icons').each(function (_index, entry) {
entry.attributes['icon']=entry.textContent;
});
setIcons(true);
handleNVSVisible();
flashState.init();
$('#fw-url-input').on('input', function () {
@@ -1294,20 +1303,23 @@ window.setURL = function (button) {
function rssiToIcon(rssi) {
if (rssi >= -55) {
return `signal_wifi_statusbar_4_bar`;
return {'label':'****','icon':`signal_wifi_statusbar_4_bar`};
} else if (rssi >= -60) {
return `network_wifi_3_bar`;
return {'label':'***','icon':`network_wifi_3_bar`};
} else if (rssi >= -65) {
return `network_wifi_2_bar`;
return {'label':'**','icon':`network_wifi_2_bar`};
} else if (rssi >= -70) {
return `network_wifi_1_bar`;
return {'label':'*','icon':`network_wifi_1_bar`};
} else {
return `signal_wifi_statusbar_null`;
return {'label':'.','icon':`signal_wifi_statusbar_null`};
}
}
function refreshAP() {
if (ConnectedTo?.urc === connectReturnCode.ETH) return;
$.ajaxSetup({
timeout: 3000 //Time in milliseconds
});
$.getJSON('/scan.json', async function () {
await sleep(2000);
$.getJSON('/ap.json', function (data) {
@@ -1327,10 +1339,13 @@ function refreshAP() {
});
}
function formatAP(ssid, rssi, auth) {
const rssi_icon=rssiToIcon(rssi);
const auth_icon={label:auth == 0 ? '🔓' : '🔒',icon:auth == 0 ? 'no_encryption' : 'lock'};
return `<tr data-bs-toggle="modal" data-bs-target="#WifiConnectDialog"><td></td><td>${ssid}</td><td>
<span class="material-icons" style="fill:white; display: inline" >${rssiToIcon(rssi)}</span>
<span class="material-icons" style="fill:white; display: inline" aria-label="${rssi_icon.label}" icon="${rssi_icon.icon}" >${getIcon(rssi_icon)}</span>
</td><td>
<span class="material-icons">${(auth == 0 ? 'no_encryption' : 'lock')}</span>
<span class="material-icons" aria-label="${auth_icon.label}" icon="${auth_icon.icon}">${getIcon(auth_icon)}</span>
</td></tr>`;
}
function refreshAPHTML2(data) {
@@ -1411,6 +1426,9 @@ function getBTSinkOpt(name) {
return $(`${btSinkNamesOptSel} option:contains('${name}')`);
}
function getMessages() {
$.ajaxSetup({
timeout: messageInterval //Time in milliseconds
});
$.getJSON('/messages.json', async function (data) {
for (const msg of data) {
const msgAge = msg.current_time - msg.sent_time;
@@ -1627,7 +1645,13 @@ function handleWifiDialog(data) {
}
}
function setIcons(offline){
$('.material-icons').each(function (_index, entry) {
entry.textContent = entry.attributes[offline?'aria-label':'icon'].value;
});
}
function handleNetworkStatus(data) {
setIcons(data.ssid==='');
if (hasConnectionChanged(data) || !data.urc) {
ConnectedTo = data;
$(".if_eth").hide();
@@ -1658,16 +1682,18 @@ function batteryToIcon(voltage) {
for (const iconEntry of batIcons) {
for (const entryRanges of iconEntry.ranges) {
if (inRange(voltage, entryRanges.f, entryRanges.t)) {
return iconEntry.icon;
return { label: iconEntry.label, icon:iconEntry.icon};
}
}
}
return "battery_full";
return {label:'▪▪▪▪',icon:"battery_full"};
}
function checkStatus() {
$.ajaxSetup({
timeout: statusInterval //Time in milliseconds
});
$.getJSON('/status.json', function (data) {
handleRecoveryMode(data);
handleNVSVisible();
@@ -1691,7 +1717,10 @@ function checkStatus() {
$('span#flash-status').html('');
}
if (data.Voltage) {
$('#battery').html(`${batteryToIcon(data.Voltage)}`);
const bat_icon=batteryToIcon(data.Voltage);
$('#battery').html(`${getIcon(bat_icon)}`);
$('#battery').attr("aria-label",bat_icon.label);
$('#battery').attr("icon",bat_icon.icon);
$('#battery').show();
} else {
$('#battery').hide();
@@ -1835,6 +1864,9 @@ function getLongOps(data, name, longopts) {
return data.values[name] !== undefined ? data.values[name][longopts] : "";
}
function getCommands() {
$.ajaxSetup({
timeout: 7000 //Time in milliseconds
});
$.getJSON('/commands.json', function (data) {
console.log(data);
$('.orec').show();
@@ -1956,6 +1988,9 @@ function getCommands() {
}
function getConfig() {
$.ajaxSetup({
timeout: 7000 //Time in milliseconds
});
$.getJSON('/config.json', function (entries) {
$('#nvsTable tr').remove();
const data = (entries.config ? entries.config : entries);

View File

@@ -1,5 +1,5 @@
target_add_binary_data( __idf_wifi-manager webapp/dist/css/index.7964a13ec910c36040b8.css.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/favicon-32x32.png BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/index.html.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/index.d78ba9.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/node_vendors.d78ba9.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/index.d3a3e6.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/node_vendors.d3a3e6.bundle.js.gz BINARY)

View File

@@ -6,29 +6,29 @@ extern const uint8_t _favicon_32x32_png_start[] asm("_binary_favicon_32x32_png_s
extern const uint8_t _favicon_32x32_png_end[] asm("_binary_favicon_32x32_png_end");
extern const uint8_t _index_html_gz_start[] asm("_binary_index_html_gz_start");
extern const uint8_t _index_html_gz_end[] asm("_binary_index_html_gz_end");
extern const uint8_t _index_d78ba9_bundle_js_gz_start[] asm("_binary_index_d78ba9_bundle_js_gz_start");
extern const uint8_t _index_d78ba9_bundle_js_gz_end[] asm("_binary_index_d78ba9_bundle_js_gz_end");
extern const uint8_t _node_vendors_d78ba9_bundle_js_gz_start[] asm("_binary_node_vendors_d78ba9_bundle_js_gz_start");
extern const uint8_t _node_vendors_d78ba9_bundle_js_gz_end[] asm("_binary_node_vendors_d78ba9_bundle_js_gz_end");
extern const uint8_t _index_d3a3e6_bundle_js_gz_start[] asm("_binary_index_d3a3e6_bundle_js_gz_start");
extern const uint8_t _index_d3a3e6_bundle_js_gz_end[] asm("_binary_index_d3a3e6_bundle_js_gz_end");
extern const uint8_t _node_vendors_d3a3e6_bundle_js_gz_start[] asm("_binary_node_vendors_d3a3e6_bundle_js_gz_start");
extern const uint8_t _node_vendors_d3a3e6_bundle_js_gz_end[] asm("_binary_node_vendors_d3a3e6_bundle_js_gz_end");
const char * resource_lookups[] = {
"/css/index.7964a13ec910c36040b8.css.gz",
"/favicon-32x32.png",
"/index.html.gz",
"/js/index.d78ba9.bundle.js.gz",
"/js/node_vendors.d78ba9.bundle.js.gz",
"/js/index.d3a3e6.bundle.js.gz",
"/js/node_vendors.d3a3e6.bundle.js.gz",
""
};
const uint8_t * resource_map_start[] = {
_index_7964a13ec910c36040b8_css_gz_start,
_favicon_32x32_png_start,
_index_html_gz_start,
_index_d78ba9_bundle_js_gz_start,
_node_vendors_d78ba9_bundle_js_gz_start
_index_d3a3e6_bundle_js_gz_start,
_node_vendors_d3a3e6_bundle_js_gz_start
};
const uint8_t * resource_map_end[] = {
_index_7964a13ec910c36040b8_css_gz_end,
_favicon_32x32_png_end,
_index_html_gz_end,
_index_d78ba9_bundle_js_gz_end,
_node_vendors_d78ba9_bundle_js_gz_end
_index_d3a3e6_bundle_js_gz_end,
_node_vendors_d3a3e6_bundle_js_gz_end
};

View File

@@ -1,6 +1,6 @@
/***********************************
webpack_headers
dist/css/index.7964a13ec910c36040b8.css.gz,dist/favicon-32x32.png,dist/index.html.gz,dist/js/index.d78ba9.bundle.js.gz,dist/js/node_vendors.d78ba9.bundle.js.gz
dist/css/index.7964a13ec910c36040b8.css.gz,dist/favicon-32x32.png,dist/index.html.gz,dist/js/index.d3a3e6.bundle.js.gz,dist/js/node_vendors.d3a3e6.bundle.js.gz
***********************************/
#pragma once
#include <inttypes.h>

View File

@@ -136,8 +136,6 @@ module.exports ={
test: './src/test.ts',
},
devServer: {
static: {
directory: path.resolve(__dirname, './dist'),
staticOptions: {},
@@ -154,7 +152,6 @@ module.exports ={
},
devMiddleware: {
publicPath: "/",
},
open: true,
compress: true,
@@ -183,7 +180,7 @@ module.exports ={
console.log('Listening on port:', port);
},
onBeforeSetupMiddleware: function (devServer) {
setupMiddlewares: function (middlewares, devServer) {
data.devServer=devServer;
devServer.app.use(bodyParser.json()) // for parsing application/json
devServer.app.use(bodyParser.urlencoded({ extended: true })) // for parsing application/x-www-form-urlencoded
@@ -438,6 +435,7 @@ module.exports ={
devServer.app.get('/reboot', function(req, res) {
waitForReboot();
res.json({"result" : "OK" }); });
return middlewares;
},
},
plugins: [

View File

@@ -130,7 +130,6 @@ void register_regular_handlers(httpd_handle_t server){
}
esp_err_t http_server_start()
{

View File

@@ -1,5 +1,5 @@
idf_component_register(SRC_DIRS .
PRIV_REQUIRES _override esp_common wifi-manager pthread squeezelite-ota platform_console telnet display targets
PRIV_REQUIRES _override esp_common wifi-manager pthread squeezelite-ota platform_console telnet display targets driver_bt
EMBED_FILES ../server_certs/github.pem
LDFRAGMENTS "linker.lf"
)

BIN
ota_data_initial.bin Normal file

Binary file not shown.

Binary file not shown.

BIN
server_certs/rootca1.cer.23 Normal file

Binary file not shown.

View File

@@ -143,6 +143,8 @@ add_custom_command(
COMMAND xtensa-esp32-elf-objcopy --globalize-symbol find_command_by_name ${build_dir}/esp-idf/console/libconsole.a
VERBATIM
)
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/ota_data_initial.bin DESTINATION ${build_dir} )
add_custom_command(
TARGET squeezelite.elf
PRE_LINK