diff --git a/README.md b/README.md index b04d6c4..0d09307 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ ## Feature list - FLAC, OPUS and PCM decoding currently supported -- Wifi setup from menuconfig or through [ImprovWifi via Serial](https://www.improv-wifi.com/) +- Wifi setup from menuconfig +- WiFi provisioning via [ImprovWifi via Serial](https://www.improv-wifi.com/)
+ Ensure your browser supports this, Chrome or Edge will handle serial communication just fine. - Auto connect to snapcast server on network - Buffers up to 758ms on Wroom modules (tested with 44100:16:2) - Buffers more then enough on Wrover modules @@ -12,10 +14,10 @@ - DSP / EQ functionality configurable through menuconfig and partly controllable through HTTP server running on ESP client (work in progress) ## Description -I have continued the work from @badaix, @bridadan and @jorgenkraghjakobsen towards a ESP32 Snapcast -client. Currently it support basic features like multiroom sync, network -controlled volume and mute. For now it supports FLAC, OPUS, PCM 16bit -audio streams with sample rates up to 48Khz maybe more, I didn't test. +I have picked up the work from [bridadan](https://github.com/bridadan/libsnapcast) and [jorgenkraghjakobsen](https://github.com/jorgenkraghjakobsen/snapclient) +towards a ESP32 Snapcast client. It is a full featured snapcast client which +supports the codecs FLAC, OPUS and PCM 16bit audio streams with sample rates +up to 48Khz maybe more, I didn't test. Please check out the task list and feel free to fill in. @@ -29,7 +31,7 @@ The codebase is split into components and build on ESP-IDF v5.1.5. I stil have some refactoring on the todo list as the concept has started to settle and allow for new features can be added in a structured manner. In the code you will find parts that are only partly related features and still not on the task -list. Also there is a lot of code clean up needed. +list. Also there is a lot of code clean up needed, as there is quite some dead code too. Components - audio-board : taken from ADF, stripped down to strictly necessary parts for playback @@ -40,24 +42,26 @@ Components - esp-dsp : Submodule to the ESP-ADF done by David Douard - esp-peripherals : taken from ADF, stripped down to strictly necessary parts for usage with Lyrat v4.3 - flac : flac audio encoder/decoder full submodule - - libmedian: Median Filter implementation. Many thanks to @accabog https://github.com/accabog/MedianFilter + - opus : Opus audio coder/decoder full submodule + - libmedian: Median Filter implementation. Many thanks to [accabog](https://github.com/accabog/MedianFilter) - libbuffer : Generic buffer abstraction - lightsnapcast : * snapcast module, port of @bridadan scapcast packages decode library * player module, which is responsible for sync and low level I2S control - net_functions : - - opus : Opus audio coder/decoder full submodule - ota_server : - protocol : - rtprx : Alternative RTP audio client UDP low latency also opus based - websocket : - websocket_if : - - wifi_interface : wifi provisoning and init code for wifi module and AP connection + - improv_wifi : WiFi provisioning via [ImprovWifi via Serial](https://www.improv-wifi.com/) + - network_interface : init code for wifi module and AP connection and ethernet init code + - ui_http_server : work in progress control interface for DSP functions The snapclient functionanlity are implemented in a task included in main - but should be refactored to a component at some point. -I did my own syncing implementation which is different than @jorgenkraghjakobsen's +I did my own syncing implementation which is different than jorgenkraghjakobsen's approach in the original repository, at least regarding syncing itself. I tried to replicate the behaivior of how badaix did it for his original snapclients. @@ -83,26 +87,12 @@ end of a freeRTOS queue. Now the front end just needs to pass on the decoded aud data to the queue with the server timestamp and chunk size. The backend reads timestamps and waits until the audio chunk has the correct playback-delay to be written to the DAC amplifer speaker through i2s DMA. When the backend pipeline -is in sync, any offset get rolled in by micro tuning the APLL on the ESP. No -sample manipulation needed. +is in sync, any offset gets corrected by inserting a single sample every chunk_ms, +which is determined by the server. ### Hardware -You will need an ESP32 or ESP32-S2 and an I2S DAC. We recommend using a Lyrat board. For pinout see the config options. - - - ESP pinout MA12070P - ------------------------------------------------------ - -> I2S_BCK Audio Clock 3.072 MHz - -> I2S_WS Frame Word Select or L/R - -> GND Ground - -> I2S_DI Audio data 24bits LSB first - -> MCLK Master clk connect to I2S_BCK - -> I2C_SCL I2C clock - -> I2C_SDA I2C Data - -> GND Ground - -> NENABLE Amplifier Enable active low - -> NMUTE Amplifier Mute active low - +You will need an ESP32 or ESP32-S2 and an I2S DAC. For pinout see the default config options in menuconfig (Audio Board). ## Installation @@ -148,19 +138,22 @@ idf.py menuconfig Configure to match your setup - Audio HAL : Choose your audio board - Lyrat (4.3, 4.2) - - Lyrat TD (2.2, 2.1) --> not supported yet + - Lyrat TD (2.2, 2.1) - Lyrat Mini (1.1) - - KORVO DU1906 --> not supported yet - - ESP32-S2 Kaluga (1.2) --> not supported yet + - KORVO DU1906 + - ESP32-S2 Kaluga (1.2) + - ESP-AI-Thinker-ES8388 (2.2) - Or a custom board - Custom Audio Board : Configure your DAC and GPIO - DAC Chip : - TI PCM51XX/TAS57XX DAC (PCM51XX are stereo DAC in TSSOP package and TAS57XX are class-D amp in HTSSOP package. Both have i2s input and i2c control) - TI PCM5102A DAC (Very basic stereo DAC WITHOUT i2c control) + - TI TAS5805M DAC - Infineon MA120X0 (High power class-D amp in QFN package) - Analog Devices ADAU1961 (Stereo DAC with multiple analog inputs in LFCSP package) - Analog Devices MAX98357 (Very popular basic mono AMP without i2c control) - - DAC I2C control interface : Choose GPIO pin of your I2C line and address of the DAC. If your DAC doesn't support I2C (PCM5102A or equivalent), put unused GPIO values. + - Princton Technology PT8211 + - DAC I2C control interface : Choose GPIO pin of your I2C line and address of the DAC. If your DAC doesn't support I2C (PCM5102A or equivalent), set them to -1. - I2C master interface : GPIO pin of your DAC I2S bus. - DAC interface configuration : Configure specific GPIO for your DAC functionnalities. Use `?` to have more info. - ESP32 DSP processor config : @@ -175,7 +168,7 @@ Configure to match your setup - Snapclient configuration : - Use mDNS : The client will search on the network for the snapserver automatically. Your network must support mDNS. - Snapserver host : IP or URL of the server if mDNS is disabled or the mDNS resolution fail. - - Snapserver port : Port of your snapserver, default is 1704. + - Snapserver port : Port of your snapserver, default is 1704. - Snapclient name : The name under wich your ESP will appear on the Snapserver. - HTTP Server Setting : The ESP create a basic webpage. You can configure the port to view this page and configure the DSP. @@ -217,7 +210,7 @@ On a linux box: ``` cd snapclient -idf.py build +idf.py build curl snapclient.local:8032 --data-binary @- < build/snapclient.bin ``` Replace `snapclient.local` with your clients IP address. If you have multiple clients you could use the Android or Web App to find out your clients IPs. diff --git a/components/improv_wifi/CMakeLists.txt b/components/improv_wifi/CMakeLists.txt index aca4905..c42e753 100644 --- a/components/improv_wifi/CMakeLists.txt +++ b/components/improv_wifi/CMakeLists.txt @@ -5,7 +5,7 @@ cmake_minimum_required(VERSION 3.15) SRCS "improvWifi.cpp" "improv_wrapper.cpp" "wifi_provisioning.c" PRIV_INCLUDE_DIRS "Improv-WiFi-Library/src" "priv_include" INCLUDE_DIRS "include" - PRIV_REQUIRES driver network_interface + PRIV_REQUIRES driver esp_wifi network_interface #SRCS "Improv-WiFi-Library/src/ImprovWiFiLibrary.cpp" "wifi_provisioning.c" #INCLUDE_DIRS "Improv-WiFi-Library/src" "include" ) diff --git a/components/improv_wifi/wifi_provisioning.c b/components/improv_wifi/wifi_provisioning.c index 1519b51..c0d6eea 100644 --- a/components/improv_wifi/wifi_provisioning.c +++ b/components/improv_wifi/wifi_provisioning.c @@ -13,8 +13,10 @@ #include "esp_wifi.h" #include "freertos/FreeRTOS.h" #include "freertos/event_groups.h" +#include "freertos/projdefs.h" #include "freertos/task.h" #include "improv_wrapper.h" +#include "network_interface.h" #include "wifi_interface.h" #define TAG "IMPROV" @@ -105,13 +107,10 @@ void improv_wifi_scan(unsigned char *scanResponse, int bufLen, esp_wifi_scan_start(NULL, true); } - // ESP_LOGI(TAG, "Max AP number ap_info can hold = %u", number); + // ESP_LOGI(TAG, "Max AP number ap_info can hold = %u", number); ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&number, ap_info)); - ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(count)); - // ESP_LOGI(TAG, "Total APs scanned = %u, actual AP number ap_info holds = - // %u", - // *count, number); + *count = 0; scanResponse[0] = 0; for (int i = 0; i < number; i++) { char rssiStr[8] = { @@ -120,7 +119,7 @@ void improv_wifi_scan(unsigned char *scanResponse, int bufLen, char cipherStr[8] = { 0, }; - uint16_t neededLen; + int16_t neededLen; itoa(ap_info[i].rssi, rssiStr, 10); if (ap_info[i].authmode != WIFI_AUTH_OPEN) { @@ -140,19 +139,20 @@ void improv_wifi_scan(unsigned char *scanResponse, int bufLen, strcat((char *)scanResponse, (char *)"\n"); bufLen -= neededLen; + + (*count)++; } } - // ESP_LOGI(TAG, "APs \t\t%s", scanResponse); + // ESP_LOGI(TAG, "APs \n%s", scanResponse); } bool improv_wifi_connect(const char *ssid, const char *password) { uint8_t count = 0; - wifi_ap_record_t apRec; - esp_err_t err; + esp_netif_ip_info_t ip; - while ((err = esp_wifi_sta_get_ap_info(&apRec)) != ESP_ERR_WIFI_NOT_CONNECT) { - esp_wifi_disconnect(); + esp_wifi_disconnect(); + while (wifi_get_ip(&ip) == true) { vTaskDelay(pdMS_TO_TICKS(100)); } @@ -163,7 +163,7 @@ bool improv_wifi_connect(const char *ssid, const char *password) { ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); esp_wifi_connect(); - while (esp_wifi_sta_get_ap_info(&apRec) != ESP_OK) { + while (wifi_get_ip(&ip) == false) { vTaskDelay(pdMS_TO_TICKS(500)); if (count > 20) { esp_wifi_disconnect(); @@ -175,43 +175,42 @@ bool improv_wifi_connect(const char *ssid, const char *password) { return true; } +/** + */ bool improv_wifi_is_connected(void) { - wifi_ap_record_t apRec; + bool ret = network_if_get_ip(NULL); - if (esp_wifi_sta_get_ap_info(&apRec) == ESP_OK) { - // printf("connected\n"); - - return true; - } - - // printf("NOT connected\n"); - - return false; + return ret; } +/** + */ void improv_wifi_get_local_ip(uint8_t *address) { esp_netif_ip_info_t ip_info; - // TODO: find a better way to do this - do { - esp_netif_get_ip_info(get_current_netif(), &ip_info); - vTaskDelay(pdMS_TO_TICKS(200)); - } while (ip_info.ip.addr == 0); + if (network_if_get_ip(&ip_info) == false) { + ESP_LOGE(TAG, "%s: no valid IP available", __func__); + + return; + } address[0] = ip_info.ip.addr >> 0; address[1] = ip_info.ip.addr >> 8; address[2] = ip_info.ip.addr >> 16; address[3] = ip_info.ip.addr >> 24; - // ESP_LOGI(TAG, "%d.%d.%d.%d", address[0], address[1], address[2], - // address[3]); + ESP_LOGI(TAG, "%d.%d.%d.%d", address[0], address[1], address[2], address[3]); } +/** + */ void improv_init(void) { uint8_t webPortStr[6] = {0}; uint16_t webPort = CONFIG_WEB_PORT; uint8_t urlStr[26] = "http://{LOCAL_IPV4}:"; + ESP_LOGI(TAG, "start IPROV WiFi provisioning service"); + utoa(webPort, (char *)webPortStr, 10); strcat((char *)urlStr, (char *)webPortStr); @@ -244,4 +243,5 @@ void improv_deinit(void) { t_improv_task = NULL; } improv_wifi_destroy(); + ESP_ERROR_CHECK(uart_driver_delete(UART_NUM_0)); } diff --git a/components/lightsnapcast/player.c b/components/lightsnapcast/player.c index 812faf6..2398d24 100644 --- a/components/lightsnapcast/player.c +++ b/components/lightsnapcast/player.c @@ -6,6 +6,7 @@ #include #include +#include "driver/i2s_common.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "freertos/task.h" @@ -319,6 +320,12 @@ int deinit_player(void) { playerTaskHandle = NULL; } + my_i2s_channel_disable(tx_chan); + if (tx_chan) { + i2s_del_channel(tx_chan); + tx_chan = NULL; + } + if (snapcastSettingsMux != NULL) { vSemaphoreDelete(snapcastSettingsMux); snapcastSettingsMux = NULL; diff --git a/components/network_interface/CMakeLists.txt b/components/network_interface/CMakeLists.txt index 1d51b81..cd5560c 100644 --- a/components/network_interface/CMakeLists.txt +++ b/components/network_interface/CMakeLists.txt @@ -1,4 +1,3 @@ idf_component_register(SRCS "network_interface.c" "eth_interface.c" "wifi_interface.c" INCLUDE_DIRS "include" - PRIV_REQUIRES driver esp_eth esp_netif esp_timer nvs_flash improv_wifi - REQUIRES esp_wifi) + PRIV_REQUIRES driver esp_wifi esp_eth esp_netif esp_timer nvs_flash improv_wifi) diff --git a/components/network_interface/eth_interface.c b/components/network_interface/eth_interface.c index 6fc4843..c99a2ee 100644 --- a/components/network_interface/eth_interface.c +++ b/components/network_interface/eth_interface.c @@ -27,6 +27,9 @@ static const char *TAG = "ETH_IF"; static uint8_t eth_port_cnt = 0; +static esp_netif_ip_info_t ip_info = {{0}, {0}, {0}}; +static bool connected = false; + #if CONFIG_SNAPCLIENT_SPI_ETHERNETS_NUM #define SPI_ETHERNETS_NUM CONFIG_SNAPCLIENT_SPI_ETHERNETS_NUM #else @@ -374,6 +377,33 @@ static void eth_event_handler(void *arg, esp_event_base_t event_base, } } +/** Event handler for IP_EVENT_ETH_GOT_IP */ +static void lost_ip_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) { + ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; + + for (int i = 0; i < eth_port_cnt; i++) { + char if_desc_str[10]; + char num_str[3]; + + itoa(i, num_str, 10); + strcat(strcpy(if_desc_str, NETWORK_INTERFACE_DESC_ETH), num_str); + + if (network_is_our_netif(if_desc_str, event->esp_netif)) { + // const esp_netif_ip_info_t *ip_info = &event->ip_info; + + memcpy((void *)&ip_info, (const void *)&event->ip_info, + sizeof(esp_netif_ip_info_t)); + + ESP_LOGI(TAG, "Ethernet Lost IP Address"); + + connected = false; + + break; + } + } +} + /** Event handler for IP_EVENT_ETH_GOT_IP */ static void got_ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { @@ -387,20 +417,35 @@ static void got_ip_event_handler(void *arg, esp_event_base_t event_base, strcat(strcpy(if_desc_str, NETWORK_INTERFACE_DESC_ETH), num_str); if (network_is_our_netif(if_desc_str, event->esp_netif)) { - const esp_netif_ip_info_t *ip_info = &event->ip_info; + // const esp_netif_ip_info_t *ip_info = &event->ip_info; + + memcpy((void *)&ip_info, (const void *)&event->ip_info, + sizeof(esp_netif_ip_info_t)); ESP_LOGI(TAG, "Ethernet Got IP Address"); ESP_LOGI(TAG, "~~~~~~~~~~~"); - ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip)); - ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask)); - ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw)); + ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info.ip)); + ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info.netmask)); + ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info.gw)); ESP_LOGI(TAG, "~~~~~~~~~~~"); + connected = true; + break; } } } +/** + */ +bool eth_get_ip(esp_netif_ip_info_t *ip) { + if (ip) { + memcpy((void *)ip, (const void *)&ip_info, sizeof(esp_netif_ip_info_t)); + } + + return connected; +} + static void eth_on_got_ipv6(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data; @@ -465,6 +510,8 @@ void eth_start(void) { ð_event_handler, eth_netif)); ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_LOST_IP, + &lost_ip_event_handler, NULL)); ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, ð_on_got_ipv6, NULL)); diff --git a/components/network_interface/include/eth_interface.h b/components/network_interface/include/eth_interface.h index 6c52b5e..37f5ef5 100644 --- a/components/network_interface/include/eth_interface.h +++ b/components/network_interface/include/eth_interface.h @@ -6,6 +6,7 @@ extern "C" { #endif +bool eth_get_ip(esp_netif_ip_info_t *ip); void eth_start(void); #ifdef __cplusplus diff --git a/components/network_interface/include/network_interface.h b/components/network_interface/include/network_interface.h index 4af30c7..660b31e 100644 --- a/components/network_interface/include/network_interface.h +++ b/components/network_interface/include/network_interface.h @@ -22,8 +22,9 @@ extern char *ipv6_addr_types_to_str[6]; esp_netif_t *network_get_netif_from_desc(const char *desc); const char *network_get_ifkey(esp_netif_t *esp_netif); +bool network_if_get_ip(esp_netif_ip_info_t *ip); bool network_is_netif_up(esp_netif_t *esp_netif); bool network_is_our_netif(const char *prefix, esp_netif_t *netif); -void network_init(void); +void network_if_init(void); #endif /* COMPONENTS_NETWORK_INTERFACE_INCLUDE_NETWORK_INTERFACE_H_ */ diff --git a/components/network_interface/include/wifi_interface.h b/components/network_interface/include/wifi_interface.h index 5db002d..65fa142 100644 --- a/components/network_interface/include/wifi_interface.h +++ b/components/network_interface/include/wifi_interface.h @@ -1,11 +1,6 @@ #ifndef _WIFI_INTERFACE_H_ #define _WIFI_INTERFACE_H_ -#include "esp_netif_types.h" -#include "freertos/FreeRTOS.h" -#include "freertos/event_groups.h" -#include "freertos/task.h" - // use wifi provisioning #define ENABLE_WIFI_PROVISIONING CONFIG_ENABLE_WIFI_PROVISIONING @@ -19,14 +14,7 @@ #define WIFI_MAXIMUM_RETRY CONFIG_WIFI_MAXIMUM_RETRY -/* The event group allows multiple bits for each event, but we only care about - * two events: - * - we are connected to the AP with an IP - * - we failed to connect after the maximum amount of retries */ -#define WIFI_CONNECTED_BIT BIT0 -#define WIFI_FAIL_BIT BIT1 - +bool wifi_get_ip(esp_netif_ip_info_t *ip); void wifi_start(void); -esp_netif_t *get_current_netif(void); #endif /* _WIFI_INTERFACE_H_ */ diff --git a/components/network_interface/network_interface.c b/components/network_interface/network_interface.c index 81a1994..6aa8066 100644 --- a/components/network_interface/network_interface.c +++ b/components/network_interface/network_interface.c @@ -67,7 +67,22 @@ bool network_is_netif_up(esp_netif_t *esp_netif) { return esp_netif_is_netif_up(esp_netif); } -void network_init(void) { +bool network_if_get_ip(esp_netif_ip_info_t *ip) { +#if CONFIG_SNAPCLIENT_USE_INTERNAL_ETHERNET || \ + CONFIG_SNAPCLIENT_USE_SPI_ETHERNET + if (eth_get_ip(ip) == true) { + return true; + } +#endif + + if (wifi_get_ip(ip) == true) { + return true; + } + + return false; +} + +void network_if_init(void) { esp_netif_init(); ESP_ERROR_CHECK(esp_event_loop_create_default()); diff --git a/components/network_interface/wifi_interface.c b/components/network_interface/wifi_interface.c index e95f30f..a3f5207 100644 --- a/components/network_interface/wifi_interface.c +++ b/components/network_interface/wifi_interface.c @@ -22,18 +22,19 @@ #include "wifi_provisioning.h" #endif -static const char *TAG = "WIFI"; +static const char *TAG = "WIFI_IF"; static void reset_reason_timer_counter_cb(void *); static char mac_address[18]; -EventGroupHandle_t s_wifi_event_group; - static int s_retry_num = 0; static esp_netif_t *esp_wifi_netif = NULL; +static esp_netif_ip_info_t ip_info = {{0}, {0}, {0}}; +static bool connected = false; + #if ENABLE_WIFI_PROVISIONING static esp_timer_handle_t resetReasonTimerHandle = NULL; static const esp_timer_create_args_t resetReasonTimerArgs = { @@ -95,18 +96,50 @@ static void got_ip_event_handler(void *arg, esp_event_base_t event_base, return; } - const esp_netif_ip_info_t *ip_info = &event->ip_info; + // const esp_netif_ip_info_t *ip_info = &event->ip_info; + + memcpy((void *)&ip_info, (const void *)&event->ip_info, + sizeof(esp_netif_ip_info_t)); ESP_LOGI(TAG, "Wifi Got IP Address"); ESP_LOGI(TAG, "~~~~~~~~~~~"); - ESP_LOGI(TAG, "WIFIIP:" IPSTR, IP2STR(&ip_info->ip)); - ESP_LOGI(TAG, "WIFIMASK:" IPSTR, IP2STR(&ip_info->netmask)); - ESP_LOGI(TAG, "WIFIGW:" IPSTR, IP2STR(&ip_info->gw)); + ESP_LOGI(TAG, "WIFIIP:" IPSTR, IP2STR(&ip_info.ip)); + ESP_LOGI(TAG, "WIFIMASK:" IPSTR, IP2STR(&ip_info.netmask)); + ESP_LOGI(TAG, "WIFIGW:" IPSTR, IP2STR(&ip_info.gw)); ESP_LOGI(TAG, "~~~~~~~~~~~"); + connected = true; + s_retry_num = 0; } +static void lost_ip_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) { + ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; + if (!network_is_our_netif(NETWORK_INTERFACE_DESC_STA, event->esp_netif)) { + return; + } + + // const esp_netif_ip_info_t *ip_info = &event->ip_info; + + memcpy((void *)&ip_info, (const void *)&event->ip_info, + sizeof(esp_netif_ip_info_t)); + + connected = false; + + ESP_LOGI(TAG, "Wifi Lost IP Address"); +} + +/** + */ +bool wifi_get_ip(esp_netif_ip_info_t *ip) { + if (ip) { + memcpy((void *)ip, (const void *)&ip_info, sizeof(esp_netif_ip_info_t)); + } + + return connected; +} + /** */ void wifi_start(void) { @@ -122,11 +155,6 @@ void wifi_start(void) { esp_wifi_netif = esp_netif_create_wifi(WIFI_IF_STA, &esp_netif_config); esp_wifi_set_default_wifi_sta_handlers(); - ESP_ERROR_CHECK(esp_wifi_set_bandwidth(WIFI_IF_STA, WIFI_BW_HT40)); - - ESP_ERROR_CHECK(esp_wifi_set_protocol( - WIFI_IF_STA, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N)); - // esp_wifi_set_ps(WIFI_PS_MIN_MODEM); // esp_wifi_set_ps(WIFI_PS_NONE); @@ -176,11 +204,23 @@ void wifi_start(void) { /* Start Wi-Fi station */ ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_protocol( + WIFI_IF_STA, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N)); + + ESP_ERROR_CHECK(esp_wifi_set_bandwidth(WIFI_IF_STA, WIFI_BW_HT40)); + wifi_config_t wifi_config; ESP_ERROR_CHECK(esp_wifi_get_config(WIFI_IF_STA, &wifi_config)); wifi_config.sta.sort_method = WIFI_CONNECT_AP_BY_SIGNAL; ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); + ESP_ERROR_CHECK(esp_event_handler_register( + WIFI_EVENT, ESP_EVENT_ANY_ID, (esp_event_handler_t)&event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, + &got_ip_event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_LOST_IP, + &lost_ip_event_handler, NULL)); + ESP_ERROR_CHECK(esp_wifi_start()); ESP_LOGI(TAG, "Starting provisioning"); @@ -206,6 +246,8 @@ void wifi_start(void) { WIFI_EVENT, ESP_EVENT_ANY_ID, (esp_event_handler_t)&event_handler, NULL)); ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &got_ip_event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_LOST_IP, + &lost_ip_event_handler, NULL)); ESP_ERROR_CHECK(esp_wifi_start()); @@ -213,5 +255,3 @@ void wifi_start(void) { wifi_config.sta.ssid); #endif } - -esp_netif_t *get_current_netif(void) { return esp_wifi_netif; } diff --git a/components/ui_http_server/CMakeLists.txt b/components/ui_http_server/CMakeLists.txt index 6068edb..0a05458 100644 --- a/components/ui_http_server/CMakeLists.txt +++ b/components/ui_http_server/CMakeLists.txt @@ -1,6 +1,6 @@ idf_component_register(SRCS "ui_http_server.c" INCLUDE_DIRS "include" - REQUIRES spiffs esp_http_server mbedtls dsp_processor vfs esp_wifi) + REQUIRES spiffs esp_http_server dsp_processor vfs) # Create a SPIFFS image from the contents of the 'html' directory # that fits the partition named 'storage'. FLASH_IN_PROJECT indicates that diff --git a/components/ui_http_server/include/ui_http_server.h b/components/ui_http_server/include/ui_http_server.h index b25f927..b590e0a 100644 --- a/components/ui_http_server/include/ui_http_server.h +++ b/components/ui_http_server/include/ui_http_server.h @@ -5,7 +5,7 @@ extern "C" { #endif -void init_http_server_task(char *key); +void init_http_server_task(void); typedef struct { char str_value[8]; diff --git a/components/ui_http_server/ui_http_server.c b/components/ui_http_server/ui_http_server.c index dde9521..b9ba0a4 100644 --- a/components/ui_http_server/ui_http_server.c +++ b/components/ui_http_server/ui_http_server.c @@ -8,31 +8,26 @@ CONDITIONS OF ANY KIND, either express or implied. */ -#include "ui_http_server.h" - -#include -#include -#include -#include #include -#include -#include "dsp_processor.h" #include "esp_err.h" #include "esp_http_server.h" #include "esp_log.h" #include "esp_spiffs.h" #include "esp_vfs.h" -#include "esp_wifi.h" +// #include "esp_wifi.h" + +#include "dsp_processor.h" #include "freertos/FreeRTOS.h" #include "freertos/queue.h" #include "freertos/task.h" +#include "ui_http_server.h" -static const char *TAG = "HTTP"; +static const char *TAG = "UI_HTTP"; -static QueueHandle_t xQueueHttp; - -static esp_netif_t *netInterface = NULL; +static QueueHandle_t xQueueHttp = NULL; +static TaskHandle_t taskHandle = NULL; +static httpd_handle_t server = NULL; /** * @@ -53,28 +48,32 @@ static void SPIFFS_Directory(char *path) { * */ static esp_err_t SPIFFS_Mount(char *path, char *label, int max_files) { - esp_vfs_spiffs_conf_t conf = {.base_path = path, - .partition_label = label, - .max_files = max_files, - .format_if_mount_failed = true}; + esp_err_t ret; - // Use settings defined above to initialize and mount SPIFFS file system. - // Note: esp_vfs_spiffs_register is an all-in-one convenience function. - esp_err_t ret = esp_vfs_spiffs_register(&conf); + if (!esp_spiffs_mounted(label)) { + esp_vfs_spiffs_conf_t conf = {.base_path = path, + .partition_label = label, + .max_files = max_files, + .format_if_mount_failed = true}; - if (ret != ESP_OK) { - if (ret == ESP_FAIL) { - ESP_LOGE(TAG, "Failed to mount or format filesystem"); - } else if (ret == ESP_ERR_NOT_FOUND) { - ESP_LOGE(TAG, "Failed to find SPIFFS partition"); - } else { - ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret)); + // Use settings defined above to initialize and mount SPIFFS file system. + // Note: esp_vfs_spiffs_register is an all-in-one convenience function. + ret = esp_vfs_spiffs_register(&conf); + + if (ret != ESP_OK) { + if (ret == ESP_FAIL) { + ESP_LOGE(TAG, "Failed to mount or format filesystem"); + } else if (ret == ESP_ERR_NOT_FOUND) { + ESP_LOGE(TAG, "Failed to find SPIFFS partition"); + } else { + ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret)); + } + return ret; } - return ret; } size_t total = 0, used = 0; - ret = esp_spiffs_info(conf.partition_label, &total, &used); + ret = esp_spiffs_info(label, &total, &used); if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret)); @@ -258,9 +257,7 @@ static esp_err_t root_post_handler(httpd_req_t *req) { /* Redirect onto root to see the updated file list */ httpd_resp_set_status(req, "303 See Other"); httpd_resp_set_hdr(req, "Location", "/"); -#ifdef CONFIG_EXAMPLE_HTTPD_CONN_CLOSE_HEADER - httpd_resp_set_hdr(req, "Connection", "close"); -#endif + // httpd_resp_set_hdr(req, "Connection", "close"); httpd_resp_sendstr(req, "post successfully"); return ESP_OK; } @@ -273,11 +270,21 @@ static esp_err_t favicon_get_handler(httpd_req_t *req) { return ESP_OK; } +/** + */ +esp_err_t stop_server(void) { + if (server) { + httpd_stop(server); + server = NULL; + } + + return ESP_OK; +} + /* * Function to start the web server */ esp_err_t start_server(const char *base_path, int port) { - httpd_handle_t server = NULL; httpd_config_t config = HTTPD_DEFAULT_CONFIG(); config.server_port = port; config.max_open_sockets = 2; @@ -317,86 +324,15 @@ esp_err_t start_server(const char *base_path, int port) { return ESP_OK; } -//// LEDC Stuff -//#define LEDC_TIMER LEDC_TIMER_0 -//#define LEDC_MODE LEDC_LOW_SPEED_MODE -////#define LEDC_OUTPUT_IO (5) // Define the output GPIO -//#define LEDC_OUTPUT_IO CONFIG_BLINK_GPIO // Define the output -// GPIO #define LEDC_CHANNEL LEDC_CHANNEL_0 #define LEDC_DUTY_RES -// LEDC_TIMER_13_BIT // Set duty resolution to 13 bits #define LEDC_DUTY -//(4095) // Set duty to 50%. ((2 ** 13) - 1) * 50% = 4095 #define LEDC_FREQUENCY -//(5000) // Frequency in Hertz. Set frequency at 5 kHz -// -// static void ledc_init(void) -//{ -// // Prepare and then apply the LEDC PWM timer configuration -// ledc_timer_config_t ledc_timer = { -// .speed_mode = LEDC_MODE, -// .timer_num = LEDC_TIMER, -// .duty_resolution = LEDC_DUTY_RES, -// .freq_hz = LEDC_FREQUENCY, // Set output -// frequency at 5 kHz .clk_cfg = LEDC_AUTO_CLK -// }; -// ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); -// -// // Prepare and then apply the LEDC PWM channel configuration -// ledc_channel_config_t ledc_channel = { -// .speed_mode = LEDC_MODE, -// .channel = LEDC_CHANNEL, -// .timer_sel = LEDC_TIMER, -// .intr_type = LEDC_INTR_DISABLE, -// .gpio_num = LEDC_OUTPUT_IO, -// .duty = 0, // Set duty to 0% -// .hpoint = 0 -// }; -// ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel)); -//} - /** * */ static void http_server_task(void *pvParameters) { - /* Get the local IP address */ - esp_netif_ip_info_t ip_info; - - ESP_ERROR_CHECK(esp_netif_get_ip_info(netInterface, &ip_info)); - - char ipString[64]; - sprintf(ipString, IPSTR, IP2STR(&ip_info.ip)); - - ESP_LOGI(TAG, "Start http task=%s", ipString); - - char portString[6]; - sprintf(portString, "%d", CONFIG_WEB_PORT); - - char url[strlen("http://") + strlen(ipString) + strlen(":") + - strlen(portString) + 1]; - memset(url, 0, sizeof(url)); - strcat(url, ipString); - strcat(url, ":"); - strcat(url, portString); - - // Set the LEDC peripheral configuration - // ledc_init(); - - // Set duty to 50% - // double maxduty = pow(2, 13) - 1; - // float percent = 0.5; - // uint32_t duty = maxduty * percent; - // ESP_LOGI(TAG, "duty=%"PRIu32, duty); - // ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, LEDC_DUTY)); - // ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, duty)); - // Update duty to apply the new value - // ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL)); - // Start Server - ESP_LOGI(TAG, "Starting server on %s", url); ESP_ERROR_CHECK(start_server("/html", CONFIG_WEB_PORT)); URL_t urlBuf; while (1) { - // ESP_LOGW (TAG, "stack free: %d", uxTaskGetStackHighWaterMark(NULL)); - // Waiting for post if (xQueueReceive(xQueueHttp, &urlBuf, portMAX_DELAY) == pdTRUE) { filterParams_t filterParams; @@ -413,16 +349,6 @@ static void http_server_task(void *pvParameters) { #if CONFIG_USE_DSP_PROCESSOR dsp_processor_update_filter_params(&filterParams); #endif - - // Set duty value - // percent = urlBuf.long_value / 100.0; - // duty = maxduty * percent; - // ESP_LOGI(TAG, "percent=%f duty=%"PRIu32, - // percent, duty); - // ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, duty)); - // Update duty to apply the new value - // ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, - // LEDC_CHANNEL)); } } @@ -434,19 +360,7 @@ static void http_server_task(void *pvParameters) { /** * */ -void init_http_server_task(char *key) { - if (!key) { - ESP_LOGE(TAG, - "key should be \"WIFI_STA_DEF\", \"WIFI_AP_DEF\" or \"ETH_DEF\""); - return; - } - - netInterface = esp_netif_get_handle_from_ifkey(key); - if (!netInterface) { - ESP_LOGE(TAG, "can't get net interface for %s", key); - return; - } - +void init_http_server_task(void) { // Initialize SPIFFS ESP_LOGI(TAG, "Initializing SPIFFS"); if (SPIFFS_Mount("/html", "storage", 6) != ESP_OK) { @@ -455,9 +369,17 @@ void init_http_server_task(char *key) { } // Create Queue - xQueueHttp = xQueueCreate(10, sizeof(URL_t)); - configASSERT(xQueueHttp); + if (!xQueueHttp) { + xQueueHttp = xQueueCreate(10, sizeof(URL_t)); + configASSERT(xQueueHttp); + } - xTaskCreatePinnedToCore(http_server_task, "HTTP", 512 * 5, NULL, 2, NULL, - tskNO_AFFINITY); + if (taskHandle) { + stop_server(); + vTaskDelete(taskHandle); + taskHandle = NULL; + } + + xTaskCreatePinnedToCore(http_server_task, "HTTP", 512 * 5, NULL, 2, + &taskHandle, tskNO_AFFINITY); } diff --git a/main/main.c b/main/main.c index 67be30f..424b983 100644 --- a/main/main.c +++ b/main/main.c @@ -550,33 +550,35 @@ static void http_get_task(void *pvParameters) { } } + ESP_LOGI(TAG, "Wait for network connection"); #if CONFIG_SNAPCLIENT_USE_INTERNAL_ETHERNET || \ CONFIG_SNAPCLIENT_USE_SPI_ETHERNET esp_netif_t *eth_netif = network_get_netif_from_desc(NETWORK_INTERFACE_DESC_ETH); +#endif esp_netif_t *sta_netif = network_get_netif_from_desc(NETWORK_INTERFACE_DESC_STA); while (1) { +#if CONFIG_SNAPCLIENT_USE_INTERNAL_ETHERNET || \ + CONFIG_SNAPCLIENT_USE_SPI_ETHERNET bool ethUp = network_is_netif_up(eth_netif); - bool staUp = network_is_netif_up(sta_netif); if (ethUp) { netif = eth_netif; break; } +#endif + bool staUp = network_is_netif_up(sta_netif); if (staUp) { netif = sta_netif; break; } - ESP_LOGI(TAG, "Wait for WiFi or Eth"); - vTaskDelay(pdMS_TO_TICKS(1000)); } -#endif #if SNAPCAST_SERVER_USE_MDNS // Find snapcast server @@ -2841,7 +2843,9 @@ void app_main(void) { init_http_server_task("WIFI_STA_DEF"); #endif */ - network_init(); + network_if_init(); + + init_http_server_task(); // Enable websocket server // ESP_LOGI(TAG, "Setup ws server");