From 6033c7fc145b70b20626c6a079ae7a01ec445be6 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Sat, 30 Oct 2021 18:25:22 -0700 Subject: [PATCH 01/12] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a570c0a4..e626fb6e 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ But squeezelite-esp32 is highly extensible and you can add - Buttons and Rotary Encoder and map/combine them to various functions (play, pause, volume, next ...) - IR receiver (no pullup resistor or capacitor needed, just the 38kHz receiver) - Monochrome, GrayScale or Color displays using SPI or I2C (supported drivers are SH1106, SSD1306, SSD1322, SSD1326/7, SSD1351, ST7735, ST7789 and ILI9341). -- Ethernet using a LAN6270 with RMII interface +- Ethernet using a LAN8270 with RMII interface Other features include From f4615462c71b76df4f028431ae3d40b8afb92e9b Mon Sep 17 00:00:00 2001 From: philippe44 Date: Sat, 30 Oct 2021 18:34:56 -0700 Subject: [PATCH 02/12] Update README.md --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e626fb6e..b25deac6 100644 --- a/README.md +++ b/README.md @@ -387,8 +387,13 @@ Wired ethernet is supported by esp32 with various options but squeezelite is onl - SMI (Serial Management Interface) wiring is not fixed and you can change it either in the configuration or using "ethernet_config" parameter with the following syntax: ``` -MDC=,MDIO=[,RST=gpio>] +mdc=,mdio=[,rst=gpio>] ``` +Default value are mdc=23,mdio=18,rst=4. Note that connecting a reset pin for the LAN8270 is optional but recommended to avoid GPIO0 to be stuck in download mode at boot time. +- Clock + +The APLL of the esp32 is required for the audio codec, so we **need** a LAN8270 that provides a 50MHz clock. That clock **must** be connected to GPIO0, there is no alternative. This means that if your DAC requires an MCLK, then you are out of luck. It is not possible to have both to work together. There might be some workaround using CLK_OUT2 and GPIO3, but I don't have time for this. + ** THIS IS NOT AVAILABLE YET, SO MORE TO COME ON HOW TO USE WIRED ETHERNET*** ### Battery / ADC The NVS parameter "bat_config" sets the ADC1 channel used to measure battery/DC voltage. The "atten" value attenuates the input voltage to the ADC input (the read value maintains a 0-1V rage) where: 0=no attenuation(0..800mV), 1=2.5dB attenuation(0..1.1V), 2=6dB attenuation(0..1.35V), 3=11dB attenuation(0..2.6V). Scale is a float ratio applied to every sample of the 12 bits ADC. A measure is taken every 10s and an average is made every 5 minutes (not a sliding window). Syntax is From 4f6dcc2cc760e3fb246ce7741b6904d3d9f4c0ff Mon Sep 17 00:00:00 2001 From: Philippe G Date: Sun, 31 Oct 2021 14:45:35 -0700 Subject: [PATCH 03/12] add SPI ethernet --- components/raop/raop_sink.c | 15 ++++---- components/services/accessors.c | 40 +++++++++++++++------ components/services/accessors.h | 8 +++-- main/Kconfig.projbuild | 64 +++++++++++++++++++++++++-------- 4 files changed, 91 insertions(+), 36 deletions(-) diff --git a/components/raop/raop_sink.c b/components/raop/raop_sink.c index e654f112..51356d12 100644 --- a/components/raop/raop_sink.c +++ b/components/raop/raop_sink.c @@ -25,10 +25,10 @@ #define CONFIG_AIRPLAY_NAME "ESP32-AirPlay" #endif -typedef struct { +static EXT_RAM_ATTR struct raop_cb_s { raop_cmd_vcb_t cmd; raop_data_cb_t data; -} raop_cb_t; +} raop_cbs; log_level raop_loglevel = lINFO; log_level util_loglevel; @@ -204,10 +204,8 @@ static bool raop_sink_start(raop_cmd_vcb_t cmd_cb, raop_data_cb_t data_cb) { * Airplay sink timer handler */ static void raop_start_handler( TimerHandle_t xTimer ) { - raop_cb_t *cbs = (raop_cb_t*) pvTimerGetTimerID (xTimer); - if (raop_sink_start(cbs->cmd, cbs->data)) { + if (raop_sink_start(raop_cbs.cmd, raop_cbs.data)) { xTimerDelete(xTimer, portMAX_DELAY); - free(cbs); } } @@ -216,10 +214,9 @@ static void raop_start_handler( TimerHandle_t xTimer ) { */ void raop_sink_init(raop_cmd_vcb_t cmd_cb, raop_data_cb_t data_cb) { if (!raop_sink_start(cmd_cb, data_cb)) { - raop_cb_t *cbs = (raop_cb_t*) malloc(sizeof(raop_cb_t)); - cbs->cmd = cmd_cb; - cbs->data = data_cb; - TimerHandle_t timer = xTimerCreate("raopStart", 1000 / portTICK_RATE_MS, pdTRUE, cbs, raop_start_handler); + raop_cbs.cmd = cmd_cb; + raop_cbs.data = data_cb; + TimerHandle_t timer = xTimerCreate("raopStart", 5000 / portTICK_RATE_MS, pdTRUE, NULL, raop_start_handler); xTimerStart(timer, portMAX_DELAY); LOG_INFO( "delaying AirPlay start"); } diff --git a/components/services/accessors.c b/components/services/accessors.c index ac7ab5d2..63cbad8a 100644 --- a/components/services/accessors.c +++ b/components/services/accessors.c @@ -118,7 +118,7 @@ static void set_i2s_pin(char *config, i2s_pin_config_t *pin_config) { * Get i2s config structure from config string */ const i2s_platform_config_t * config_get_i2s_from_str(char * dac_config ){ - static i2s_platform_config_t i2s_dac_pin = { + static EXT_RAM_ATTR i2s_platform_config_t i2s_dac_pin = { .i2c_addr = -1, .sda= -1, .scl = -1, @@ -146,16 +146,26 @@ const i2s_platform_config_t * config_get_i2s_from_str(char * dac_config ){ * Get eth config structure from config string */ const eth_config_t * config_get_eth_from_str(char * eth_config ){ - static eth_config_t eth_pin = { - .mdc = -1, - .mdio = -1, - .rst = -1, + static EXT_RAM_ATTR eth_config_t eth_pin = { + .rmii = false, + .model = "", }; char * p=NULL; + if ((p = strcasestr(eth_config, "model")) != NULL) sscanf(p, "%*[^=]=%15[^,]", eth_pin.model); if ((p = strcasestr(eth_config, "mdc")) != NULL) eth_pin.mdc = atoi(strchr(p, '=') + 1); if ((p = strcasestr(eth_config, "mdio")) != NULL) eth_pin.mdio = atoi(strchr(p, '=') + 1); if ((p = strcasestr(eth_config, "rst")) != NULL) eth_pin.rst = atoi(strchr(p, '=') + 1); + if ((p = strcasestr(eth_config, "mosi")) != NULL) eth_pin.mosi = atoi(strchr(p, '=') + 1); + if ((p = strcasestr(eth_config, "miso")) != NULL) eth_pin.miso = atoi(strchr(p, '=') + 1); + if ((p = strcasestr(eth_config, "intr")) != NULL) eth_pin.intr = atoi(strchr(p, '=') + 1); + if ((p = strcasestr(eth_config, "cs")) != NULL) eth_pin.cs = atoi(strchr(p, '=') + 1); + if ((p = strcasestr(eth_config, "speed")) != NULL) eth_pin.speed = atoi(strchr(p, '=') + 1); + if ((p = strcasestr(eth_config, "clk")) != NULL) eth_pin.clk = atoi(strchr(p, '=') + 1); + if ((p = strcasestr(eth_config, "host")) != NULL) eth_pin.host = atoi(strchr(p, '=') + 1); + + if (strcasestr(eth_pin.model, "lan8720")) eth_pin.rmii = true; + return ð_pin; } @@ -164,7 +174,7 @@ const eth_config_t * config_get_eth_from_str(char * eth_config ){ */ const i2s_platform_config_t * config_spdif_get( ){ char * spdif_config = config_spdif_get_string(); - static i2s_platform_config_t i2s_dac_config; + static EXT_RAM_ATTR i2s_platform_config_t i2s_dac_config; memcpy(&i2s_dac_config, config_get_i2s_from_str(spdif_config), sizeof(i2s_dac_config)); free(spdif_config); return &i2s_dac_config; @@ -175,7 +185,7 @@ const i2s_platform_config_t * config_spdif_get( ){ */ const i2s_platform_config_t * config_dac_get(){ char * spdif_config = get_dac_config_string(); - static i2s_platform_config_t i2s_dac_config; + static EXT_RAM_ATTR i2s_platform_config_t i2s_dac_config; memcpy(&i2s_dac_config, config_get_i2s_from_str(spdif_config), sizeof(i2s_dac_config)); free(spdif_config); return &i2s_dac_config; @@ -185,9 +195,19 @@ const i2s_platform_config_t * config_dac_get(){ * Get ethernet config structure */ const eth_config_t * config_eth_get( ){ - char * config = config_alloc_get_str("eth_config", CONFIG_ETH_CONFIG, "mdc=" STR(CONFIG_MDC_IO) - ",mdio=" STR(CONFIG_MDIO_IO) ",do=" STR(CONFIG_PHY_RST_IO)); - static eth_config_t eth_config; + char * config = config_alloc_get_str("eth_config", CONFIG_ETH_CONFIG, "rst=" STR(CONFIG_ETH_PHY_RST_IO) +#if defined(CONFIG_ETH_LAN8720) + ",model=lan8720" +#elif defined(CONFIG_ETH_DM9051) + ",model=dm9051" +#endif + ",mdc=" STR(CONFIG_ETH_MDC_IO) ",mdio=" STR(CONFIG_ETH_MDIO_IO) + ",host=" STR(CONFIG_ETH_SPI_HOST) ",cs=" STR(CONFIG_ETH_SPI_CS_IO) + ",mosi=" STR(CONFIG_ETH_SPI_MOSI_IO) ",miso=" STR(CONFIG_ETH_SPI_MISO_IO) + ",intr=" STR(CONFIG_ETH_SPI_INTR_IO) + ",clk=" STR(CONFIG_ETH_SPI_CLK_IO) ",speed=" STR(CONFIG_ETH_SPI_SPEED) ); + static EXT_RAM_ATTR eth_config_t eth_config; + ESP_LOGD(TAG, "Ethernet config string %s", config); memcpy(ð_config, config_get_eth_from_str(config), sizeof(eth_config)); free(config); return ð_config; diff --git a/components/services/accessors.h b/components/services/accessors.h index 6cf59a2c..614d39cf 100644 --- a/components/services/accessors.h +++ b/components/services/accessors.h @@ -31,9 +31,13 @@ typedef struct { } display_config_t; typedef struct { - int mdc; - int mdio; + bool rmii; + char model[16]; int rst; + int mdc, mdio; + int host; + int cs, mosi, miso, intr, clk; + int speed; } eth_config_t; typedef struct { diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index f408318d..b5b84113 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -98,7 +98,7 @@ menu "Squeezelite-ESP32" default "" config ETH_CONFIG string - default "" + default "" config DAC_CONTROLSET string default "{ \"init\": [ {\"reg\":41, \"val\":128}, {\"reg\":18, \"val\":255} ], \"poweron\": [ {\"reg\":18, \"val\":64, \"mode\":\"or\"} ], \"poweroff\": [ {\"reg\":18, \"val\":191, \"mode\":\"and\"} ] }" if TWATCH2020 @@ -107,23 +107,57 @@ menu "Squeezelite-ESP32" endmenu menu "Ethernet Options" - visible if BASIC_I2C_BT && ETH_USE_ESP32_EMAC - config ETH_MDC_IO - int "SMI MDC GPIO number" - default 23 - help - Set the GPIO number used by SMI MDC. - config ETH_MDIO_IO - int "SMI MDIO GPIO number" - default 18 - help - Set the GPIO number used by SMI MDIO. + visible if BASIC_I2C_BT && (ETH_USE_ESP32_EMAC || ETH_USE_SPI_ETHERNET) + choice + prompt "Ethernet Chipset" + default ETH_NODRIVER + config ETH_NODRIVER + bool "Defined in NVS" + config ETH_LAN8720 + bool "Microchip LAN8720 (RMII)" + config ETH_DM9051 + bool "Davicom 9051 (SPI)" + endchoice config ETH_PHY_RST_IO - int "PHY Reset GPIO number" - default 4 + int "PHY Reset GPIO number" if !ETH_NODRIVER + default -1 help Set the GPIO number used to reset PHY chip. - Set to -1 to disable PHY chip hardware reset. + Set to -1 to disable PHY chip hardware reset. + config ETH_MDC_IO + int "SMI MDC GPIO number" if ETH_LAN8720 + default -1 + help + Set the GPIO number used by SMI MDC. + config ETH_MDIO_IO + int "SMI MDIO GPIO number" if ETH_LAN8720 + default -1 + help + Set the GPIO number used by SMI MDIO. + config ETH_SPI_HOST + int "SPI host number (-1,1 or 2)" if ETH_DM9051 + default -1 + help + Set to -1 to use system's SPI config (see Various I/O) + Set to 2 or 3 to use a dedicated bus + config ETH_SPI_INTR_IO + int "interrupt" if ETH_DM9051 + default -1 + config ETH_SPI_CS_IO + int "Chip Select" if ETH_DM9051 + default -1 + config ETH_SPI_CLK_IO + int "SPI clock" if ETH_SPI_HOST != -1 && ETH_DM9051 + default -1 + config ETH_SPI_MOSI_IO + int "Data Out" if ETH_SPI_HOST != -1 && ETH_DM9051 + default -1 + config ETH_SPI_MISO_IO + int "Data In" if ETH_SPI_HOST != -1 && ETH_DM9051 + default -1 + config ETH_SPI_SPEED + int "SPI speed (Hz)" if ETH_SPI_HOST != -1 && ETH_DM9051 + default 20000000 endmenu menu "Audio settings" From 71911914cbc5043dd32e346fdb2c65ea7968a2d7 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Sun, 31 Oct 2021 15:59:00 -0700 Subject: [PATCH 04/12] Update README.md --- README.md | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b25deac6..6d71dae8 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ But squeezelite-esp32 is highly extensible and you can add - Buttons and Rotary Encoder and map/combine them to various functions (play, pause, volume, next ...) - IR receiver (no pullup resistor or capacitor needed, just the 38kHz receiver) - Monochrome, GrayScale or Color displays using SPI or I2C (supported drivers are SH1106, SSD1306, SSD1322, SSD1326/7, SSD1351, ST7735, ST7789 and ILI9341). -- Ethernet using a LAN8270 with RMII interface +- Ethernet using a Microchip LAN8720 with RMII interface or Davicom DM9051 over SPI. Other features include @@ -138,8 +138,9 @@ sda=,scl=[,port=0|1][,speed=] ### SPI The NVS parameter "spi_config" set the spi's gpio used for generic purpose (e.g. display). Leave it blank to disable SPI usage. The DC parameter is needed for displays. Syntax is ``` -data=,clk=[,dc=][,host=1|2] +data|mosi=,clk=[,dc=][,host=1|2][miso=] ``` +The "miso" parameter is only used when SPI bus is to be shared with other peripheral (e.g. ethernet, see below), otherwise it can be omitted. Note that "data" can also be named "mosi". ### DAC/I2S The NVS parameter "dac_config" set the gpio used for i2s communication with your DAC. You can define the defaults at compile time but nvs parameter takes precedence except for SqueezeAMP and A1S where these are forced at runtime. Syntax is ``` @@ -372,8 +373,9 @@ There is no good or bad option, it's your choice. Use the NVS parameter "lms_ctr **Note that gpio 36 and 39 are input only and cannot use interrupt. When using them for a button, a 100ms polling is started which is expensive. Long press is also likely to not work very well** ### Ethernet (coming soon) -Wired ethernet is supported by esp32 with various options but squeezelite is only supporting a LAN8270 with a RMII interface like [this](https://www.aliexpress.com/item/32858432526.html). The esp32 has a strict set of wires required to use the RMII interface. +Wired ethernet is supported by esp32 with various options but squeezelite is only supporting a LAN8270 with a RMII interface like [this](https://www.aliexpress.com/item/32858432526.html) or DM9051 over SPI like [that](https://www.amazon.com/dp/B08JLFWX9Z). +#### RMII (LAN8720) - RMII PHY wiring is fixed and can not be changed | GPIO | RMII Signal | Notes | @@ -385,14 +387,29 @@ Wired ethernet is supported by esp32 with various options but squeezelite is onl | GPIO26 | RX1 | EMAC_RXD1 | | GPIO27 | CRS_DV | EMAC_RX_DRV | -- SMI (Serial Management Interface) wiring is not fixed and you can change it either in the configuration or using "ethernet_config" parameter with the following syntax: +- SMI (Serial Management Interface) wiring is not fixed and you can change it either in the configuration or using "eth_config" parameter with the following syntax: ``` -mdc=,mdio=[,rst=gpio>] +model=lan8720,mdc=,mdio=[,rst=] ``` Default value are mdc=23,mdio=18,rst=4. Note that connecting a reset pin for the LAN8270 is optional but recommended to avoid GPIO0 to be stuck in download mode at boot time. - Clock The APLL of the esp32 is required for the audio codec, so we **need** a LAN8270 that provides a 50MHz clock. That clock **must** be connected to GPIO0, there is no alternative. This means that if your DAC requires an MCLK, then you are out of luck. It is not possible to have both to work together. There might be some workaround using CLK_OUT2 and GPIO3, but I don't have time for this. +#### SPI (DM9051) +Ethernet over SPI is supported as well and requires less GPIOs but is obvsiously slower. Another benefit is that SPI bus can be shared with the display, but it's also possible to have a dedicated SPI interface (the esp32 has 3 different SPI but SPI0 is reserved for external Flash/RAM). The "eth_config" parameter syntax becomes: +``` +model=dm9051,host=<-1|1|2>[,rst=],cs=,speed=,intr=[mosi=,miso=,clk=] +``` +- To use the system SPI, shared with display (see spi_config) "host" must be set to -1. Any other value will reserve the SPI interface (careful of conflict with spi_config) +- When not using system SPI, bi-directional transfer requires "mosi" for data out, "miso" for data in and "clk" +- The esp32 has a special I/O multiplexer for faster speed (up to 80 MHz) but that requires using specific GPIOs, depending on SPI bus (See [here](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/spi_master.html)) + +| Pin Name | SPI2 | SPI3 | +| -------- | ---- | ---- | +| CS0* | 15 | 5 | +| SCLK | 14 | 18 | +| MISO | 12 | 19 | +| MOSI | 13 | 23 | ** THIS IS NOT AVAILABLE YET, SO MORE TO COME ON HOW TO USE WIRED ETHERNET*** ### Battery / ADC From 5f5466fcb08bf1d6c24b40383c777eede4cded84 Mon Sep 17 00:00:00 2001 From: Philippe G Date: Sun, 31 Oct 2021 16:13:42 -0700 Subject: [PATCH 05/12] add "MISO" to system's spi_config --- components/platform_console/cmd_i2ctools.c | 5 ++++- components/services/accessors.c | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/components/platform_console/cmd_i2ctools.c b/components/platform_console/cmd_i2ctools.c index fd73b41a..7537a03a 100644 --- a/components/platform_console/cmd_i2ctools.c +++ b/components/platform_console/cmd_i2ctools.c @@ -76,6 +76,7 @@ static struct { static struct { struct arg_int *data; + struct arg_int *miso; struct arg_int *clk; struct arg_int *dc; struct arg_int *host; @@ -453,6 +454,7 @@ static int do_spiconfig_cmd(int argc, char **argv){ /* Check "--clk" option */ nerrors+=is_output_gpio(spiconfig_args.clk, f, &spi_config.sclk_io_num, true); nerrors+=is_output_gpio(spiconfig_args.data, f, &spi_config.mosi_io_num, true); + nerrors+=is_output_gpio(spiconfig_args.miso, f, &spi_config.miso_io_num, true); nerrors+=is_output_gpio(spiconfig_args.dc, f, &dc, true); nerrors+=is_output_gpio(spiconfig_args.host, f, &host, true); @@ -1030,7 +1032,8 @@ static void register_spiconfig(void) { spiconfig_args.clear = arg_lit0(NULL, "clear", "Clear configuration"); spiconfig_args.clk = arg_int0("k", "clk", "", "Clock GPIO"); - spiconfig_args.data = arg_int0("d","data", "","Data GPIO"); + spiconfig_args.data = arg_int0("d","data", "","Data OUT GPIO"); + spiconfig_args.miso = arg_int0("d","miso", "","Data IN GPIO"); spiconfig_args.dc = arg_int0("c","dc", "", "DC GPIO"); spiconfig_args.host= arg_int0("h", "host", "1|2", "SPI Host Number"); spiconfig_args.end = arg_end(4); diff --git a/components/services/accessors.c b/components/services/accessors.c index 63cbad8a..0601312b 100644 --- a/components/services/accessors.c +++ b/components/services/accessors.c @@ -391,7 +391,7 @@ esp_err_t config_spi_set(const spi_bus_config_t * config, int host, int dc){ esp_err_t err = ESP_OK; char * config_buffer=calloc(buffer_size,1); if(config_buffer) { - snprintf(config_buffer,buffer_size,"data=%u,clk=%u,dc=%u,host=%u",config->mosi_io_num,config->sclk_io_num,dc,host); + snprintf(config_buffer,buffer_size,"data=%u,clk=%u,dc=%u,host=%u,miso=%d",config->mosi_io_num,config->sclk_io_num,dc,host,config->miso_io_num); log_send_messaging(MESSAGING_INFO,"Updating SPI configuration to %s",config_buffer); err = config_set_value(NVS_TYPE_STR, "spi_config", config_buffer); if(err!=ESP_OK){ @@ -561,7 +561,7 @@ const set_GPIO_struct_t * get_gpio_struct(){ */ const spi_bus_config_t * config_spi_get(spi_host_device_t * spi_host) { char *nvs_item, *p; - static spi_bus_config_t spi = { + static EXT_RAM_ATTR spi_bus_config_t spi = { .mosi_io_num = -1, .sclk_io_num = -1, .miso_io_num = -1, @@ -572,6 +572,8 @@ const spi_bus_config_t * config_spi_get(spi_host_device_t * spi_host) { nvs_item = config_alloc_get_str("spi_config", CONFIG_SPI_CONFIG, NULL); if (nvs_item) { if ((p = strcasestr(nvs_item, "data")) != NULL) spi.mosi_io_num = atoi(strchr(p, '=') + 1); + if ((p = strcasestr(nvs_item, "mosi")) != NULL) spi.mosi_io_num = atoi(strchr(p, '=') + 1); + if ((p = strcasestr(nvs_item, "miso")) != NULL) spi.miso_io_num = atoi(strchr(p, '=') + 1); if ((p = strcasestr(nvs_item, "clk")) != NULL) spi.sclk_io_num = atoi(strchr(p, '=') + 1); if ((p = strcasestr(nvs_item, "dc")) != NULL) spi_system_dc_gpio = atoi(strchr(p, '=') + 1); if ((p = strcasestr(nvs_item, "host")) != NULL) spi_system_host = atoi(strchr(p, '=') + 1); From 7d409668c8e15527bfae5568166a65b82e1e7c0c Mon Sep 17 00:00:00 2001 From: philippe44 Date: Sun, 31 Oct 2021 18:24:23 -0700 Subject: [PATCH 06/12] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6d71dae8..d8371876 100644 --- a/README.md +++ b/README.md @@ -398,7 +398,7 @@ The APLL of the esp32 is required for the audio codec, so we **need** a LAN8270 #### SPI (DM9051) Ethernet over SPI is supported as well and requires less GPIOs but is obvsiously slower. Another benefit is that SPI bus can be shared with the display, but it's also possible to have a dedicated SPI interface (the esp32 has 3 different SPI but SPI0 is reserved for external Flash/RAM). The "eth_config" parameter syntax becomes: ``` -model=dm9051,host=<-1|1|2>[,rst=],cs=,speed=,intr=[mosi=,miso=,clk=] +model=dm9051,host=<-1|1|2>,cs=,speed=,intr=[,rst=][,mosi=,miso=,clk=] ``` - To use the system SPI, shared with display (see spi_config) "host" must be set to -1. Any other value will reserve the SPI interface (careful of conflict with spi_config) - When not using system SPI, bi-directional transfer requires "mosi" for data out, "miso" for data in and "clk" From 2146014f0406b12b797716ab3d9107d9860be171 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Sun, 31 Oct 2021 18:26:43 -0700 Subject: [PATCH 07/12] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d8371876..609092c6 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,7 @@ sda=,scl=[,port=0|1][,speed=] ### SPI The NVS parameter "spi_config" set the spi's gpio used for generic purpose (e.g. display). Leave it blank to disable SPI usage. The DC parameter is needed for displays. Syntax is ``` -data|mosi=,clk=[,dc=][,host=1|2][miso=] +data|mosi=,clk=[,dc=][,host=1|2][,miso=] ``` The "miso" parameter is only used when SPI bus is to be shared with other peripheral (e.g. ethernet, see below), otherwise it can be omitted. Note that "data" can also be named "mosi". ### DAC/I2S @@ -415,7 +415,7 @@ model=dm9051,host=<-1|1|2>,cs=,speed=,intr=[,rst=][ ### Battery / ADC The NVS parameter "bat_config" sets the ADC1 channel used to measure battery/DC voltage. The "atten" value attenuates the input voltage to the ADC input (the read value maintains a 0-1V rage) where: 0=no attenuation(0..800mV), 1=2.5dB attenuation(0..1.1V), 2=6dB attenuation(0..1.35V), 3=11dB attenuation(0..2.6V). Scale is a float ratio applied to every sample of the 12 bits ADC. A measure is taken every 10s and an average is made every 5 minutes (not a sliding window). Syntax is ``` -channel=0..7,scale=,cells=<2|3>,[atten=<0|1|2|3>] +channel=0..7,scale=,cells=<2|3>[,atten=<0|1|2|3>] ``` NB: Set parameter to empty to disable battery reading. For well-known configuration, this is ignored (except for SqueezeAMP where number of cells is required) # Configuration From 0fa41e5ef76d9323c3ac68724597ac0f9331fc2f Mon Sep 17 00:00:00 2001 From: philippe44 Date: Sun, 31 Oct 2021 20:03:06 -0700 Subject: [PATCH 08/12] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 609092c6..c9d6f952 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ The NVS parameter "dac_config" set the gpio used for i2s communication with your ``` bck=,ws=,do=[,mck][,mute=[:0|1][,model=TAS57xx|TAS5713|AC101|I2S][,sda=,scl=gpio[,i2c=]] ``` -if "model" is not set or is not recognized, then default "I2S" is used. The option "mck" is used for some codecs that require a master clock (although they should not). Only GPIO0 can be used as MCLK. I2C parameters are optional an only needed if your dac requires an I2C control (See 'dac_controlset' below). Note that "i2c" parameters are decimal, hex notation is not allowed. +if "model" is not set or is not recognized, then default "I2S" is used. The option "mck" is used for some codecs that require a master clock (although they should not). Only GPIO0 can be used as MCLK and be aware that this cannot coexit with RMII Ethernet (see ethernet section below). I2C parameters are optional and only needed if your DAC requires an I2C control (See 'dac_controlset' below). Note that "i2c" parameters are decimal, hex notation is not allowed. So far, TAS75xx, TAS5714, AC101 and ES8388 are recognized models where the proper init sequence/volume/power controls are sent. For other codecs that might require an I2C commands, please use the parameter "dac_controlset" that allows definition of simple commands to be sent over i2c for init, power on and off using a JSON syntax: ``` From d27a4306645dc64abfd872dc6f707a235b54f5af Mon Sep 17 00:00:00 2001 From: philippe44 Date: Sun, 31 Oct 2021 20:05:23 -0700 Subject: [PATCH 09/12] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c9d6f952..de418ef8 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ bck=,ws=,do=[,mck][,mute=[:0|1][,model=TAS57xx|TAS5713|A ``` if "model" is not set or is not recognized, then default "I2S" is used. The option "mck" is used for some codecs that require a master clock (although they should not). Only GPIO0 can be used as MCLK and be aware that this cannot coexit with RMII Ethernet (see ethernet section below). I2C parameters are optional and only needed if your DAC requires an I2C control (See 'dac_controlset' below). Note that "i2c" parameters are decimal, hex notation is not allowed. -So far, TAS75xx, TAS5714, AC101 and ES8388 are recognized models where the proper init sequence/volume/power controls are sent. For other codecs that might require an I2C commands, please use the parameter "dac_controlset" that allows definition of simple commands to be sent over i2c for init, power on and off using a JSON syntax: +So far, TAS57xx, TAS5713, AC101, WM8978 and ES8388 are recognized models where the proper init sequence/volume/power controls are sent. For other codecs that might require an I2C commands, please use the parameter "dac_controlset" that allows definition of simple commands to be sent over i2c for init, power on and off using a JSON syntax: ``` { init: [ {"reg":,"val":,"mode":|"or"|"and"}, ... {{"reg":,"val":,"mode":|"or"|"and"} ], poweron: [ {"reg":,"val":,"mode":|"or"|"and"}, ... {{"reg":,"val":,"mode":|"or"|"and"} ], From 4aa54537e5bed0d58067b52b7d1e49db99644366 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Sun, 31 Oct 2021 20:10:31 -0700 Subject: [PATCH 10/12] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index de418ef8..2b4d90f6 100644 --- a/README.md +++ b/README.md @@ -391,10 +391,10 @@ Wired ethernet is supported by esp32 with various options but squeezelite is onl ``` model=lan8720,mdc=,mdio=[,rst=] ``` -Default value are mdc=23,mdio=18,rst=4. Note that connecting a reset pin for the LAN8270 is optional but recommended to avoid GPIO0 to be stuck in download mode at boot time. +Connecting a reset pin for the LAN8720 is optional but recommended to avoid that GPIO0 (50MHz input clock) locks the esp32 in download mode at boot time. - Clock -The APLL of the esp32 is required for the audio codec, so we **need** a LAN8270 that provides a 50MHz clock. That clock **must** be connected to GPIO0, there is no alternative. This means that if your DAC requires an MCLK, then you are out of luck. It is not possible to have both to work together. There might be some workaround using CLK_OUT2 and GPIO3, but I don't have time for this. +The APLL of the esp32 is required for the audio codec, so we **need** a LAN8720 that provides a 50MHz clock. That clock **must** be connected to GPIO0, there is no alternative. This means that if your DAC requires an MCLK, then you are out of luck. It is not possible to have both to work together. There might be some workaround using CLK_OUT2 and GPIO3, but I don't have time for this. #### SPI (DM9051) Ethernet over SPI is supported as well and requires less GPIOs but is obvsiously slower. Another benefit is that SPI bus can be shared with the display, but it's also possible to have a dedicated SPI interface (the esp32 has 3 different SPI but SPI0 is reserved for external Flash/RAM). The "eth_config" parameter syntax becomes: ``` From 4dce4b307b460d4eecec59dfffa0c46244b62ead Mon Sep 17 00:00:00 2001 From: philippe44 Date: Sun, 31 Oct 2021 20:27:30 -0700 Subject: [PATCH 11/12] Update README.md --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 2b4d90f6..7f03f092 100644 --- a/README.md +++ b/README.md @@ -136,11 +136,11 @@ sda=,scl=[,port=0|1][,speed=] ``` Please note that you can not use the same GPIO or port as the DAC ### SPI -The NVS parameter "spi_config" set the spi's gpio used for generic purpose (e.g. display). Leave it blank to disable SPI usage. The DC parameter is needed for displays. Syntax is +The esp32 has 4 SPI sub-systems, one is unaccessible so numbering is 0..2 and SPI0 is reserved for Flash/PSRAM. The NVS parameter "spi_config" set the spi's gpio used for generic purpose (e.g. display). Leave it blank to disable SPI usage. The DC parameter is needed for displays. Syntax is ``` data|mosi=,clk=[,dc=][,host=1|2][,miso=] ``` -The "miso" parameter is only used when SPI bus is to be shared with other peripheral (e.g. ethernet, see below), otherwise it can be omitted. Note that "data" can also be named "mosi". +Default "host" is 1. The "miso" parameter is only used when SPI bus is to be shared with other peripheral (e.g. ethernet, see below), otherwise it can be omitted. Note that "data" can also be named "mosi". ### DAC/I2S The NVS parameter "dac_config" set the gpio used for i2s communication with your DAC. You can define the defaults at compile time but nvs parameter takes precedence except for SqueezeAMP and A1S where these are forced at runtime. Syntax is ``` @@ -373,7 +373,7 @@ There is no good or bad option, it's your choice. Use the NVS parameter "lms_ctr **Note that gpio 36 and 39 are input only and cannot use interrupt. When using them for a button, a 100ms polling is started which is expensive. Long press is also likely to not work very well** ### Ethernet (coming soon) -Wired ethernet is supported by esp32 with various options but squeezelite is only supporting a LAN8270 with a RMII interface like [this](https://www.aliexpress.com/item/32858432526.html) or DM9051 over SPI like [that](https://www.amazon.com/dp/B08JLFWX9Z). +Wired ethernet is supported by esp32 with various options but squeezelite is only supporting a Microchip LAN8720 with a RMII interface like [this](https://www.aliexpress.com/item/32858432526.html) or Davicom DM9051 over SPI like [that](https://www.amazon.com/dp/B08JLFWX9Z). #### RMII (LAN8720) - RMII PHY wiring is fixed and can not be changed @@ -396,13 +396,13 @@ Connecting a reset pin for the LAN8720 is optional but recommended to avoid that The APLL of the esp32 is required for the audio codec, so we **need** a LAN8720 that provides a 50MHz clock. That clock **must** be connected to GPIO0, there is no alternative. This means that if your DAC requires an MCLK, then you are out of luck. It is not possible to have both to work together. There might be some workaround using CLK_OUT2 and GPIO3, but I don't have time for this. #### SPI (DM9051) -Ethernet over SPI is supported as well and requires less GPIOs but is obvsiously slower. Another benefit is that SPI bus can be shared with the display, but it's also possible to have a dedicated SPI interface (the esp32 has 3 different SPI but SPI0 is reserved for external Flash/RAM). The "eth_config" parameter syntax becomes: +Ethernet over SPI is supported as well and requires less GPIOs but is obvsiously slower. Another benefit is that the SPI bus can be shared with the display, but it's also possible to have a dedicated SPI interface. The esp32 has 4 SPI sub-systems, one is unaccessible so numbering is 0..2 and SPI0 is reserved for Flash/PSRAM. The "eth_config" parameter syntax becomes: ``` -model=dm9051,host=<-1|1|2>,cs=,speed=,intr=[,rst=][,mosi=,miso=,clk=] +model=dm9051,cs=,speed=,intr=[,host=<-1|1|2>][,rst=][,mosi=,miso=,clk=] ``` -- To use the system SPI, shared with display (see spi_config) "host" must be set to -1. Any other value will reserve the SPI interface (careful of conflict with spi_config) -- When not using system SPI, bi-directional transfer requires "mosi" for data out, "miso" for data in and "clk" -- The esp32 has a special I/O multiplexer for faster speed (up to 80 MHz) but that requires using specific GPIOs, depending on SPI bus (See [here](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/spi_master.html)) +- To use the system SPI, shared with display (see spi_config) "host" must be set to -1. Any other value will reserve the SPI interface (careful of conflict with spi_config). The default "host" is 2 to avoid conflicting wiht default "spi_config" settings. +- When not using system SPI, "mosi" for data out, "miso" for data in and "clk" **must** be set +- The esp32 has a special I/O multiplexer for faster speed (up to 80 MHz) but that requires using specific GPIOs, which depends on SPI bus (See [here](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/spi_master.html) for more details) | Pin Name | SPI2 | SPI3 | | -------- | ---- | ---- | From 974ff5fa68da1c3e9897b837aba097321e0206d6 Mon Sep 17 00:00:00 2001 From: Philippe G Date: Wed, 3 Nov 2021 22:00:07 -0700 Subject: [PATCH 12/12] strncpy is not safe + memory optimization --- components/platform_console/cmd_config.c | 1 + components/platform_console/cmd_wifi.c | 2 ++ components/services/buttons.c | 6 +++--- components/services/led.c | 4 ++-- components/squeezelite/displayer.c | 6 +++--- components/wifi-manager/wifi_manager.c | 1 + 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/components/platform_console/cmd_config.c b/components/platform_console/cmd_config.c index e138041a..d7fca3b3 100644 --- a/components/platform_console/cmd_config.c +++ b/components/platform_console/cmd_config.c @@ -611,6 +611,7 @@ static int do_i2s_cmd(int argc, char **argv) } if(i2s_args.model_name->count>0 && strlen(i2s_args.model_name->sval[0])>0){ strncpy(i2s_dac_pin.model,i2s_args.model_name->sval[0],sizeof(i2s_dac_pin.model)); + i2s_dac_pin.model[sizeof(i2s_dac_pin.model) - 1] = '\0'; } if(!nerrors ){ fprintf(f,"Storing i2s parameters.\n"); diff --git a/components/platform_console/cmd_wifi.c b/components/platform_console/cmd_wifi.c index eeaad2f0..558c3c9f 100644 --- a/components/platform_console/cmd_wifi.c +++ b/components/platform_console/cmd_wifi.c @@ -117,8 +117,10 @@ static bool wifi_join(const char *ssid, const char *pass, int timeout_ms) initialise_wifi(); wifi_config_t wifi_config = { 0 }; strncpy((char *) wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid)); + wifi_config.sta.ssid[sizeof(wifi_config.sta.ssid) - 1] = '\0'; if (pass) { strncpy((char *) wifi_config.sta.password, pass, sizeof(wifi_config.sta.password)); + wifi_config.sta.password[sizeof(wifi_config.sta.password) - 1] = '\0'; } ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); diff --git a/components/services/buttons.c b/components/services/buttons.c index 8289eed5..d734dec7 100644 --- a/components/services/buttons.c +++ b/components/services/buttons.c @@ -47,14 +47,14 @@ static EXT_RAM_ATTR struct button_s { TimerHandle_t timer; } buttons[MAX_BUTTONS]; -static struct { +static EXT_RAM_ATTR struct { int gpio, level; struct button_s *button; } polled_gpio[] = { {36, -1, NULL}, {39, -1, NULL}, {-1, -1, NULL} }; static TimerHandle_t polled_timer; -static struct { +static EXT_RAM_ATTR struct { QueueHandle_t queue; void *client; rotary_encoder_info_t info; @@ -62,7 +62,7 @@ static struct { rotary_handler handler; } rotary; -static struct { +static EXT_RAM_ATTR struct { RingbufHandle_t rb; infrared_handler handler; } infrared; diff --git a/components/services/led.c b/components/services/led.c index 8432457a..1f2fedbe 100644 --- a/components/services/led.c +++ b/components/services/led.c @@ -28,7 +28,7 @@ static const char *TAG = "led"; -static struct led_s { +static EXT_RAM_ATTR struct led_s { gpio_num_t gpio; bool on; int onstate; @@ -40,7 +40,7 @@ static struct led_s { TimerHandle_t timer; } leds[MAX_LED]; -static struct { +static EXT_RAM_ATTR struct { int gpio; int active; int pwm; diff --git a/components/squeezelite/displayer.c b/components/squeezelite/displayer.c index 03941001..36d6c650 100644 --- a/components/squeezelite/displayer.c +++ b/components/squeezelite/displayer.c @@ -244,8 +244,7 @@ static void visu_handler(u8_t *data, int len); static void dmxt_handler(u8_t *data, int len); static void displayer_task(void* arg); -// PLACEHOLDER -void *led_display = 0x1000; +void *led_display; /* scrolling undocumented information grfs @@ -537,7 +536,8 @@ static void show_display_buffer(char *ddram) { char *line2; memset(line1, 0, LINELEN+1); - strncpy(line1, ddram, LINELEN); + strncpy(line1, ddram, LINELEN+1); + line1[LINELEN] = '\0'; line2 = &(ddram[LINELEN]); line2[LINELEN] = '\0'; diff --git a/components/wifi-manager/wifi_manager.c b/components/wifi-manager/wifi_manager.c index d44014f3..0be48ca8 100644 --- a/components/wifi-manager/wifi_manager.c +++ b/components/wifi-manager/wifi_manager.c @@ -292,6 +292,7 @@ void wifi_manager_init_wifi(){ 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'; ESP_LOGI(TAG,"LMS IP: %s, hport: %d, cport: %d",lms_server_ip, hport, cport); lms_server_port = hport; lms_server_cport = cport;