mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-06 03:27:01 +03:00
improv-wifi initial commit
This commit is contained in:
10
.gitignore
vendored
10
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
162
components/platform_console/improv_console.c
Normal file
162
components/platform_console/improv_console.c
Normal 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);
|
||||
}
|
||||
23
components/platform_console/improv_console.h
Normal file
23
components/platform_console/improv_console.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#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();
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
431
components/wifi-manager/improv.c
Normal file
431
components/wifi-manager/improv.c
Normal 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;
|
||||
}
|
||||
279
components/wifi-manager/improv.h
Normal file
279
components/wifi-manager/improv.h
Normal 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.
|
||||
@@ -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]) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -130,7 +130,6 @@ void register_regular_handlers(httpd_handle_t server){
|
||||
|
||||
}
|
||||
|
||||
|
||||
esp_err_t http_server_start()
|
||||
{
|
||||
|
||||
|
||||
@@ -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
otadata.bin
Normal file
BIN
otadata.bin
Normal file
Binary file not shown.
@@ -143,6 +143,7 @@ add_custom_command(
|
||||
COMMAND xtensa-esp32-elf-objcopy --globalize-symbol find_command_by_name ${build_dir}/esp-idf/console/libconsole.a
|
||||
VERBATIM
|
||||
)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/otadata.bin ${build_dir}/ota_data_initial.bin COPYONLY)
|
||||
add_custom_command(
|
||||
TARGET squeezelite.elf
|
||||
PRE_LINK
|
||||
|
||||
Reference in New Issue
Block a user