improv-wifi initial commit

This commit is contained in:
Sebastien L
2022-11-29 11:04:27 -05:00
parent b20c8306fa
commit f74ecf5e60
21 changed files with 1223 additions and 68 deletions

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,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();

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();