diff --git a/Makefile b/Makefile index 929f00d9..bfbb0778 100644 --- a/Makefile +++ b/Makefile @@ -13,8 +13,7 @@ #recovery: EXTRA_CPPFLAGS+=-DRECOVERY_APPLICATION=1 PROJECT_NAME?=squeezelite -EXTRA_CPPFLAGS+= -I$(PROJECT_PATH)/main -EXTRA_COMPONENT_DIRS := esp-dsp +EXTRA_CPPFLAGS+= -I$(PROJECT_PATH)/main -I$(IDF_PATH)/components/esp_http_server/src -I$(IDF_PATH)/components/esp_http_server/src/port/esp32 -I$(IDF_PATH)/components/esp_http_server/src/util #/-Wno-error=maybe-uninitialized include $(IDF_PATH)/make/project.mk diff --git a/README.md b/README.md index bb701d3b..531128de 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ SPI,width=,height=,cs=[,speed=][,HFlip][,VFlip][dri - VFlip and HFlip are optional can be used to change display orientation - Default speed is 8000000 (8MHz) but SPI can work up to 26MHz or even 40MHz -Currently 128x32/64 I2C display like [this](https://www.buydisplay.com/i2c-blue-0-91-inch-oled-display-module-128x32-arduino-raspberry-pi) and [this](https://www.waveshare.com/wiki/1.3inch_OLED_HAT) are supported +Currently 128x32/64 I2C and SPI display like [this](https://www.buydisplay.com/i2c-blue-0-91-inch-oled-display-module-128x32-arduino-raspberry-pi) and [this](https://www.waveshare.com/wiki/1.3inch_OLED_HAT) are supported The NVS parameter "metadata_config" sets how metadata is displayed for AirPlay and Bluetooth. Syntax is ``` diff --git a/components/cmd_system/cmd_system.c b/components/cmd_system/cmd_system.c index dd452e41..7e9dc015 100644 --- a/components/cmd_system/cmd_system.c +++ b/components/cmd_system/cmd_system.c @@ -106,8 +106,7 @@ esp_err_t guided_boot(esp_partition_subtype_t partition_subtype) if(!wait_for_commit()){ ESP_LOGW(TAG,"Unable to commit configuration. "); } - ESP_LOGW(TAG, "Restarting after tx complete"); - uart_wait_tx_done(UART_NUM_1, 500 / portTICK_RATE_MS); + vTaskDelay(750/ portTICK_PERIOD_MS); esp_restart(); return ESP_OK; } @@ -117,8 +116,7 @@ esp_err_t guided_boot(esp_partition_subtype_t partition_subtype) if(!wait_for_commit()){ ESP_LOGW(TAG,"Unable to commit configuration. "); } - ESP_LOGW(TAG, "Restarting after tx complete"); - uart_wait_tx_done(UART_NUM_1, 500 / portTICK_RATE_MS); + vTaskDelay(750/ portTICK_PERIOD_MS); esp_restart(); return ESP_OK; } @@ -166,8 +164,7 @@ esp_err_t guided_boot(esp_partition_subtype_t partition_subtype) if(!wait_for_commit()){ ESP_LOGW(TAG,"Unable to commit configuration. "); } - ESP_LOGW(TAG, "Restarting after tx complete"); - uart_wait_tx_done(UART_NUM_1, 500 / portTICK_RATE_MS); + vTaskDelay(750/ portTICK_PERIOD_MS); esp_restart(); } } @@ -181,8 +178,7 @@ static int restart(int argc, char **argv) if(!wait_for_commit()){ ESP_LOGW(TAG,"Unable to commit configuration. "); } - ESP_LOGW(TAG, "Restarting after tx complete"); - uart_wait_tx_done(UART_NUM_1, 500 / portTICK_RATE_MS); + vTaskDelay(750/ portTICK_PERIOD_MS); esp_restart(); return 0; } @@ -193,9 +189,7 @@ void simple_restart() if(!wait_for_commit()){ ESP_LOGW(TAG,"Unable to commit configuration. "); } - - ESP_LOGW(TAG, "Restarting after tx complete"); - uart_wait_tx_done(UART_NUM_1, 500 / portTICK_RATE_MS); + vTaskDelay(750/ portTICK_PERIOD_MS); esp_restart(); } diff --git a/components/display/SH1106.c b/components/display/SH1106.c index ccdc36b8..91e33a54 100644 --- a/components/display/SH1106.c +++ b/components/display/SH1106.c @@ -143,7 +143,7 @@ struct GDS_Device* SH1106_Detect(char *Driver, struct GDS_Device* Device) { if (!strcasestr(Driver, "SH1106")) return NULL; if (!Device) Device = calloc(1, sizeof(struct GDS_Device)); - *Device = SH1106; + *Device = SH1106; Device->Depth = 1; #if !defined SHADOW_BUFFER && defined USE_IRAM Device->Alloc = GDS_ALLOC_IRAM_SPI; diff --git a/components/display/SSD1306.c b/components/display/SSD1306.c index faaacea2..fec88996 100644 --- a/components/display/SSD1306.c +++ b/components/display/SSD1306.c @@ -42,12 +42,12 @@ static void Update( struct GDS_Device* Device ) { #ifdef SHADOW_BUFFER struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private; // not sure the compiler does not have to redo all calculation in for loops, so local it is - int width = Device->Width, rows = Device->Height / 8; + int width = Device->Width, pages = Device->Height / 8; uint8_t *optr = Private->Shadowbuffer, *iptr = Device->Framebuffer; - int CurrentRow = -1, FirstCol = -1, LastCol = -1; + int CurrentPage = -1, FirstCol = -1, LastCol = -1; // by row, find first and last columns that have been updated - for (int r = 0; r < rows; r++) { + for (int p = 0; p < pages; p++) { uint8_t first = 0, last; for (int c = 0; c < width; c++) { if (*iptr != *optr) { @@ -70,15 +70,15 @@ static void Update( struct GDS_Device* Device ) { } // Set row only when needed, otherwise let auto-increment work - if (r != CurrentRow) SetPageAddress( Device, r, Device->Height / 8 - 1 ); - CurrentRow = r + 1; + if (p != CurrentPage) SetPageAddress( Device, p, Device->Height / 8 - 1 ); + CurrentPage = p + 1; // actual write - Device->WriteData( Device, Private->Shadowbuffer + r*width + first, last - first + 1); + Device->WriteData( Device, Private->Shadowbuffer + p*width + first, last - first + 1); } } #else - // automatic counter and end Page/Column + // automatic counter and end Page/Column (we assume Height % 8 == 0) SetColumnAddress( Device, 0, Device->Width - 1); SetPageAddress( Device, 0, Device->Height / 8 - 1); Device->WriteData( Device, Device->Framebuffer, Device->FramebufferSize ); diff --git a/components/display/SSD132x.c b/components/display/SSD132x.c index 217c51c9..6088d187 100644 --- a/components/display/SSD132x.c +++ b/components/display/SSD132x.c @@ -162,7 +162,7 @@ static void IRAM_ATTR DrawPixel1Fast( struct GDS_Device* Device, int X, int Y, i *FBOffset ^= BIT( 7 - XBit ); } else { // we might be able to save the 7-Xbit using BitRemap (A0 bit 2) - *FBOffset = ( Color == GDS_COLOR_WHITE ) ? *FBOffset | BIT( 7 - XBit ) : *FBOffset & ~BIT( 7 - XBit ); + *FBOffset = ( Color == GDS_COLOR_BLACK ) ? *FBOffset & ~BIT( 7 - XBit ) : *FBOffset | BIT( 7 - XBit ); } } @@ -181,8 +181,6 @@ static void ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int c += chunk * 8; while (c <= x2) DrawPixel1Fast( Device, c++, r, Color ); } - - Device->Dirty = true; } static void DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color ) { @@ -193,8 +191,6 @@ static void DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, i // just do bitreverse and if BitRemap works, there will be even nothing to do for (int i = Height * Width >> 3; --i >= 0;) *optr++ = BitReverseTable256[*Data++]; - - // Dirty is set for us } static void SetHFlip( struct GDS_Device* Device, bool On ) { @@ -307,7 +303,7 @@ struct GDS_Device* SSD132x_Detect(char *Driver, struct GDS_Device* Device) { *Device = SSD132x; ((struct PrivateSpace*) Device->Private)->Model = Model; - + sscanf(Driver, "%*[^:]:%c", &Device->Depth); if (Model == SSD1326 && Device->Depth == 1) { diff --git a/components/display/SSD1675.c b/components/display/SSD1675.c new file mode 100644 index 00000000..390c8986 --- /dev/null +++ b/components/display/SSD1675.c @@ -0,0 +1,252 @@ +/** + * Copyright (c) 2017-2018 Tara Keeling + * 2020 Philippe G. + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include + +#include "gds.h" +#include "gds_private.h" + +static char TAG[] = "SSD1675"; + +const unsigned char EPD_lut_full_update[] = { + 0x80,0x60,0x40,0x00,0x00,0x00,0x00, //LUT0: BB: VS 0 ~7 + 0x10,0x60,0x20,0x00,0x00,0x00,0x00, //LUT1: BW: VS 0 ~7 + 0x80,0x60,0x40,0x00,0x00,0x00,0x00, //LUT2: WB: VS 0 ~7 + 0x10,0x60,0x20,0x00,0x00,0x00,0x00, //LUT3: WW: VS 0 ~7 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00, //LUT4: VCOM: VS 0 ~7 + 0x03,0x03,0x00,0x00,0x02, // TP0 A~D RP0 + 0x09,0x09,0x00,0x00,0x02, // TP1 A~D RP1 + 0x03,0x03,0x00,0x00,0x02, // TP2 A~D RP2 + 0x00,0x00,0x00,0x00,0x00, // TP3 A~D RP3 + 0x00,0x00,0x00,0x00,0x00, // TP4 A~D RP4 + 0x00,0x00,0x00,0x00,0x00, // TP5 A~D RP5 + 0x00,0x00,0x00,0x00,0x00, // TP6 A~D RP6 + 0x15,0x41,0xA8,0x32,0x30,0x0A, +}; + +const unsigned char EPD_lut_partial_update[]= { //20 bytes + 0x00,0x00,0x00,0x00,0x00,0x00,0x00, //LUT0: BB: VS 0 ~7 + 0x80,0x00,0x00,0x00,0x00,0x00,0x00, //LUT1: BW: VS 0 ~7 + 0x40,0x00,0x00,0x00,0x00,0x00,0x00, //LUT2: WB: VS 0 ~7 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00, //LUT3: WW: VS 0 ~7 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00, //LUT4: VCOM: VS 0 ~7 + 0x0A,0x00,0x00,0x00,0x00, // TP0 A~D RP0 + 0x00,0x00,0x00,0x00,0x00, // TP1 A~D RP1 + 0x00,0x00,0x00,0x00,0x00, // TP2 A~D RP2 + 0x00,0x00,0x00,0x00,0x00, // TP3 A~D RP3 + 0x00,0x00,0x00,0x00,0x00, // TP4 A~D RP4 + 0x00,0x00,0x00,0x00,0x00, // TP5 A~D RP5 + 0x00,0x00,0x00,0x00,0x00, // TP6 A~D RP6 + 0x15,0x41,0xA8,0x32,0x30,0x0A, +}; + +struct PrivateSpace { + int ReadyPin; + uint16_t Height; +}; + +// Functions are not declared to minimize # of lines + +void WaitReady( struct GDS_Device* Device) { + struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private; + if (Private->ReadyPin >= 0) { + int count = 4*1000; + while (gpio_get_level( Private->ReadyPin ) && count) { + vTaskDelay( pdMS_TO_TICKS(100) ); + count -= 100; + } + } else { + vTaskDelay( pdMS_TO_TICKS(2000) ); + } +} + +static void WriteByte( struct GDS_Device* Device, uint8_t Data ) { + Device->WriteData( Device, &Data, 1 ); +} + +static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) { + // start might be greater than end if we decrement + Device->WriteCommand( Device, 0x44 ); + Device->WriteData( Device, &Start, 1 ); + Device->WriteData( Device, &End, 1 ); + + // we obviously want to start ... from the start + Device->WriteCommand( Device, 0x4e ); + WriteByte( Device, Start ); +} +static void SetRowAddress( struct GDS_Device* Device, uint16_t Start, uint16_t End ) { + // start might be greater than end if we decrement + Device->WriteCommand( Device, 0x45 ); + WriteByte( Device, Start ); + WriteByte( Device, Start >> 8 ); + WriteByte( Device, End ); + WriteByte( Device, End >> 8 ); + + // we obviously want to start ... from the start + Device->WriteCommand( Device, 0x4f ); + WriteByte( Device, Start ); + WriteByte( Device, Start >> 8 ); +} + +static void Update( struct GDS_Device* Device ) { + uint8_t *iptr = Device->Framebuffer; + + Device->WriteCommand( Device, 0x24 ); + + // this is awfully slow, but e-ink are slow anyway ... + for (int i = Device->FramebufferSize; --i >= 0;) { + WriteByte( Device, ~*iptr++ ); + } + + Device->WriteCommand( Device, 0x22 ); + WriteByte( Device, 0xC7); + Device->WriteCommand( Device, 0X20 ); + + WaitReady( Device ); +} + +// remember that for these ELD drivers W and H are "inverted" +static void IRAM_ATTR DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color ) { + uint32_t YBit = ( Y & 0x07 ); + Y>>= 3; + + uint8_t* FBOffset = Device->Framebuffer + ( ( Y * Device->Width ) + X ); + *FBOffset = ( Color == GDS_COLOR_BLACK ) ? *FBOffset & ~BIT( 7-YBit ) : *FBOffset | BIT( 7-YBit ); +} + +static void ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color ) { + for (int r = y1; r <= y2; r++) { + for (int c = x1; c <= x2; c++) { + DrawPixelFast( Device, c, r, Color ); + } + } +} + +static void DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color ) { + if (!Height) Height = Device->Height; + if (!Width) Width = Device->Width; + + // just do row/column swap + for (int r = 0; r < Height; r++) { + uint8_t *optr = Device->Framebuffer + r*Device->Width, *iptr = Data + r; + for (int c = Width; --c >= 0;) { + *optr++ = *iptr; + iptr += Height; + } + } +} + +static bool Init( struct GDS_Device* Device ) { + struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private; + + // need to re-adjust framebuffer because these are non % 8 + Private->Height = Device->Height; + if (Device->Height & 0x07) Device->Height = ((Device->Height >> 3) + 1) << 3; + + Device->FramebufferSize = Device->Width * Device->Height / 8; + Device->Framebuffer = calloc(1, Device->FramebufferSize); + NullCheck( Device->Framebuffer, return false ); + + if (Private->ReadyPin >= 0) { + gpio_pad_select_gpio( Private->ReadyPin ); + gpio_set_pull_mode( Private->ReadyPin, GPIO_PULLUP_ONLY); + gpio_set_direction( Private->ReadyPin, GPIO_MODE_INPUT ); + } + + // soft reset + vTaskDelay(pdMS_TO_TICKS( 2000 )); + Device->WriteCommand( Device, 0x12 ); + WaitReady( Device ); + + Device->WriteCommand( Device, 0x74 ); + WriteByte( Device, 0x54 ); + Device->WriteCommand( Device, 0x7e ); + WriteByte( Device, 0x3B ); + + Device->WriteCommand( Device, 0x3c ); + WriteByte( Device, 0x03 ); + + Device->WriteCommand( Device, 0x2c ); + WriteByte( Device, 0x55 ); + + Device->WriteCommand( Device, 0x03 ); + WriteByte( Device, EPD_lut_full_update[70] ); + + Device->WriteCommand( Device, 0x04 ); + WriteByte( Device, EPD_lut_full_update[71] ); + WriteByte( Device, EPD_lut_full_update[72] ); + WriteByte( Device, EPD_lut_full_update[73] ); + + Device->WriteCommand( Device, 0x3a ); + WriteByte( Device, EPD_lut_full_update[74] ); + Device->WriteCommand( Device, 0x3b ); + WriteByte( Device, EPD_lut_full_update[75] ); + + Device->WriteCommand( Device, 0X32 ); + for (int i = 0; i < 70; i++) { + WriteByte( Device, EPD_lut_full_update[i] ); + } + + // now deal with funny X/Y layout (W and H are "inverted") + Device->WriteCommand( Device, 0x01 ); + WriteByte( Device, Device->Width - 1 ); + WriteByte( Device, (Device->Width - 1) >> 8 ); + WriteByte( Device, (0 << 0) ); + + /* + Start from 0, Ymax, incX, decY. Starting from X=Height would be difficult + as we would hit the extra bits added because height % 8 != 0 and they are + not written by the DrawPixel. By starting from X=0 we are aligned and + doing incY is like a clockwise 90° rotation (vs otherwise we would virtually + do a counter-clockwise rotation but again have the "border" issue. + */ + Device->WriteCommand( Device, 0x11 ); + WriteByte( Device, (1 << 2) | (0 << 1) | (1 << 0)); + + // must be in order with increment/decrement i.e start might be > end if we decrement + SetColumnAddress( Device, 0x0, (Device->Height >> 3) - 1 ); + SetRowAddress ( Device, Device->Width - 1, 0 ); + + WaitReady( Device ); + + Update( Device ); + + return true; +} + +static const struct GDS_Device SSD1675 = { + .DrawBitmapCBR = DrawBitmapCBR, .ClearWindow = ClearWindow, + .DrawPixelFast = DrawPixelFast, + .Update = Update, .Init = Init, +}; + +struct GDS_Device* SSD1675_Detect(char *Driver, struct GDS_Device* Device) { + if (!strcasestr(Driver, "SSD1675")) return NULL; + + if (!Device) Device = calloc(1, sizeof(struct GDS_Device)); + *Device = SSD1675; + + Device->Depth = 1; + Device->Alloc = GDS_ALLOC_NONE; + + char *p; + struct PrivateSpace* Private = (struct PrivateSpace*) Device->Private; + Private->ReadyPin = -1; + if ((p = strcasestr(Driver, "ready")) != NULL) Private->ReadyPin = atoi(strchr(p, '=') + 1); + + ESP_LOGI(TAG, "SSD1675 driver with ready GPIO %d", Private->ReadyPin); + + return Device; +} \ No newline at end of file diff --git a/components/display/core/gds.c b/components/display/core/gds.c index 72567ba4..499a931b 100644 --- a/components/display/core/gds.c +++ b/components/display/core/gds.c @@ -133,11 +133,18 @@ bool GDS_Reset( struct GDS_Device* Device ) { } bool GDS_Init( struct GDS_Device* Device ) { - Device->FramebufferSize = ( Device->Width * Device->Height ) / (8 / Device->Depth); - if ((Device->Alloc && GDS_ALLOC_IRAM) || ((Device->Alloc & GDS_ALLOC_IRAM_SPI) && Device->IF == GDS_IF_SPI)) heap_caps_calloc( 1, Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); - else Device->Framebuffer = calloc( 1, Device->FramebufferSize ); - NullCheck( Device->Framebuffer, return false ); + Device->FramebufferSize = (Device->Width * Device->Height) / (8 / Device->Depth); + + // allocate FB unless explicitely asked not to + if (!(Device->Alloc & GDS_ALLOC_NONE)) { + if ((Device->Alloc & GDS_ALLOC_IRAM) || ((Device->Alloc & GDS_ALLOC_IRAM_SPI) && Device->IF == GDS_IF_SPI)) { + heap_caps_calloc( 1, Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); + } else { + Device->Framebuffer = calloc( 1, Device->FramebufferSize ); + } + NullCheck( Device->Framebuffer, return false ); + } bool Res = Device->Init( Device ); if (!Res) free(Device->Framebuffer); diff --git a/components/display/core/gds.h b/components/display/core/gds.h index d0ec7ef9..49cd7c6c 100644 --- a/components/display/core/gds.h +++ b/components/display/core/gds.h @@ -13,13 +13,13 @@ monochrome mode is not such type of screen, SH1106 and SSD1306 are */ -enum { GDS_COLOR_L0 = 0, GDS_COLOR_L1 = 1, GDS_COLOR_L2, GDS_COLOR_L3, GDS_COLOR_L4, GDS_COLOR_L5, GDS_COLOR_L6, GDS_COLOR_L7, +enum { GDS_COLOR_L0 = 0, GDS_COLOR_L1, GDS_COLOR_L2, GDS_COLOR_L3, GDS_COLOR_L4, GDS_COLOR_L5, GDS_COLOR_L6, GDS_COLOR_L7, GDS_COLOR_L8, GDS_COLOR_L9, GDS_COLOR_L10, GDS_COLOR_L11, GDS_COLOR_L12, GDS_COLOR_L13, GDS_COLOR_L14, GDS_COLOR_L15, GDS_COLOR_MAX }; -#define GDS_COLOR_BLACK GDS_COLOR_L0 -#define GDS_COLOR_WHITE (GDS_COLOR_MAX - 1) +#define GDS_COLOR_BLACK (0) +#define GDS_COLOR_WHITE (-1) #define GDS_COLOR_XOR (GDS_COLOR_MAX + 1) struct GDS_Device; diff --git a/components/display/core/gds_private.h b/components/display/core/gds_private.h index 74fd8630..6d3f46f1 100644 --- a/components/display/core/gds_private.h +++ b/components/display/core/gds_private.h @@ -7,6 +7,7 @@ #include "gds.h" #include "gds_err.h" +#define GDS_ALLOC_NONE 0x80 #define GDS_ALLOC_IRAM 0x01 #define GDS_ALLOC_IRAM_SPI 0x02 @@ -158,7 +159,7 @@ inline void IRAM_ATTR GDS_DrawPixel1Fast( struct GDS_Device* Device, int X, int if ( Color == GDS_COLOR_XOR ) { *FBOffset ^= BIT( YBit ); } else { - *FBOffset = ( Color >= GDS_COLOR_WHITE / 2 ) ? *FBOffset | BIT( YBit ) : *FBOffset & ~BIT( YBit ); + *FBOffset = ( Color == GDS_COLOR_BLACK ) ? *FBOffset & ~BIT( YBit ) : *FBOffset | BIT( YBit ); } } @@ -166,7 +167,7 @@ inline void IRAM_ATTR GDS_DrawPixel4Fast( struct GDS_Device* Device, int X, int uint8_t* FBOffset; FBOffset = Device->Framebuffer + ( (Y * Device->Width >> 1) + (X >> 1)); - *FBOffset = X & 0x01 ? (*FBOffset & 0x0f) | (Color << 4) : ((*FBOffset & 0xf0) | Color); + *FBOffset = X & 0x01 ? (*FBOffset & 0x0f) | ((Color & 0x0f) << 4) : ((*FBOffset & 0xf0) | (Color & 0x0f)); } inline void IRAM_ATTR GDS_DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color ) { diff --git a/components/display/display.c b/components/display/display.c index 8741cd7b..ddc79a0a 100644 --- a/components/display/display.c +++ b/components/display/display.c @@ -59,8 +59,8 @@ static EXT_RAM_ATTR struct { static void displayer_task(void *args); struct GDS_Device *display; -extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect; -GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, NULL }; +extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect, SSD1675_Detect; +GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, SSD1675_Detect, NULL }; /**************************************************************************************** * @@ -86,15 +86,18 @@ void display_init(char *welcome) { // so far so good if (display && width > 0 && height > 0) { + int RST_pin = -1; + if ((p = strcasestr(config, "reset")) != NULL) RST_pin = atoi(strchr(p, '=') + 1); + // Detect driver interface if (strstr(config, "I2C") && i2c_system_port != -1) { int address = 0x3C; if ((p = strcasestr(config, "address")) != NULL) address = atoi(strchr(p, '=') + 1); - + init = true; GDS_I2CInit( i2c_system_port, -1, -1, i2c_system_speed ) ; - GDS_I2CAttachDevice( display, width, height, address, -1 ); + GDS_I2CAttachDevice( display, width, height, address, RST_pin ); ESP_LOGI(TAG, "Display is I2C on port %u", address); } else if (strstr(config, "SPI") && spi_system_host != -1) { @@ -105,7 +108,7 @@ void display_init(char *welcome) { init = true; GDS_SPIInit( spi_system_host, spi_system_dc_gpio ); - GDS_SPIAttachDevice( display, width, height, CS_pin, -1, speed ); + GDS_SPIAttachDevice( display, width, height, CS_pin, RST_pin, speed ); ESP_LOGI(TAG, "Display is SPI host %u with cs:%d", spi_system_host, CS_pin); } else { @@ -189,7 +192,7 @@ static void displayer_task(void *args) { // handler elapsed track time if (displayer.timer && displayer.state == DISPLAYER_ACTIVE) { - char counter[12]; + char counter[16]; TickType_t tick = xTaskGetTickCount(); uint32_t elapsed = (tick - displayer.tick) * portTICK_PERIOD_MS; diff --git a/components/raop/raop.c b/components/raop/raop.c index ff887557..1417243b 100644 --- a/components/raop/raop.c +++ b/components/raop/raop.c @@ -57,9 +57,9 @@ typedef struct raop_ctx_s { struct in_addr peer; // IP of the iDevice (airplay sender) bool running; #ifdef WIN32 - pthread_t thread, search_thread; + pthread_t thread; #else - TaskHandle_t thread, search_thread, joiner; + TaskHandle_t thread, joiner; StaticTask_t *xTaskBuffer; StackType_t xStack[RTSP_STACK_SIZE] __attribute__ ((aligned (4))); #endif @@ -81,10 +81,11 @@ typedef struct raop_ctx_s { char DACPid[32], id[32]; struct in_addr host; u16_t port; + bool running; #ifdef WIN32 struct mDNShandle_s *handle; + pthread_t thread; #else - bool running; TaskHandle_t thread, joiner; StaticTask_t *xTaskBuffer; StackType_t xStack[SEARCH_STACK_SIZE] __attribute__ ((aligned (4)));; @@ -218,7 +219,7 @@ void raop_delete(struct raop_ctx_s *ctx) { struct sockaddr addr; socklen_t nlen = sizeof(struct sockaddr); #endif - + if (!ctx) return; #ifdef WIN32 @@ -240,7 +241,7 @@ void raop_delete(struct raop_ctx_s *ctx) { // terminate search, but do not reclaim memory of pthread if never launched if (ctx->active_remote.handle) { close_mDNS(ctx->active_remote.handle); - pthread_join(ctx->search_thread, NULL); + pthread_join(ctx->active_remote.thread, NULL); } // stop broadcasting devices @@ -515,7 +516,7 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock) #ifdef WIN32 ctx->active_remote.handle = init_mDNS(false, ctx->host); - pthread_create(&ctx->search_thread, NULL, &search_remote, ctx); + pthread_create(&ctx->active_remote.thread, NULL, &search_remote, ctx); #else ctx->active_remote.running = true; ctx->active_remote.destroy_mutex = xSemaphoreCreateMutex(); @@ -592,7 +593,7 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock) // need to make sure no search is on-going and reclaim pthread memory #ifdef WIN32 if (ctx->active_remote.handle) close_mDNS(ctx->active_remote.handle); - pthread_join(ctx->search_thread, NULL); + pthread_join(ctx->active_remote.thread, NULL); #else ctx->active_remote.joiner = xTaskGetCurrentTaskHandle(); ctx->active_remote.running = false; @@ -631,18 +632,16 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock) current = ((current - start) / 44100) * 1000; if (stop) stop = ((stop - start) / 44100) * 1000; else stop = -1; - LOG_INFO("[%p]: SET PARAMETER progress %u/%u %s", ctx, current, stop, p); - success = ctx->cmd_cb(RAOP_PROGRESS, current, stop); - } - - if (body && ((p = kd_lookup(headers, "Content-Type")) != NULL) && !strcasecmp(p, "application/x-dmap-tagged")) { + LOG_INFO("[%p]: SET PARAMETER progress %d/%u %s", ctx, current, stop, p); + success = ctx->cmd_cb(RAOP_PROGRESS, max(current, 0), stop); + } else if (body && ((p = kd_lookup(headers, "Content-Type")) != NULL) && !strcasecmp(p, "application/x-dmap-tagged")) { struct metadata_s metadata; dmap_settings settings = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, on_dmap_string, NULL, NULL }; - LOG_INFO("[%p]: received metadata"); + LOG_INFO("[%p]: received metadata", ctx); settings.ctx = &metadata; memset(&metadata, 0, sizeof(struct metadata_s)); if (!dmap_parse(&settings, body, len)) { @@ -651,6 +650,10 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock) success = ctx->cmd_cb(RAOP_METADATA, metadata.artist, metadata.album, metadata.title); free_metadata(&metadata); } + } else { + char *dump = kd_dump(headers); + LOG_INFO("Unhandled SET PARAMETER\n%s", dump); + free(dump); } } @@ -684,9 +687,13 @@ void abort_rtsp(raop_ctx_t *ctx) { rtp_end(ctx->rtp); ctx->rtp = NULL; LOG_INFO("[%p]: RTP thread aborted", ctx); - } + } if (ctx->active_remote.running) { +#ifdef WIN32 + pthread_join(ctx->active_remote.thread, NULL); + close_mDNS(ctx->active_remote.handle); +#else // need to make sure no search is on-going and reclaim task memory ctx->active_remote.joiner = xTaskGetCurrentTaskHandle(); ctx->active_remote.running = false; @@ -696,8 +703,10 @@ void abort_rtsp(raop_ctx_t *ctx) { vSemaphoreDelete(ctx->active_remote.thread); heap_caps_free(ctx->active_remote.xTaskBuffer); +#endif memset(&ctx->active_remote, 0, sizeof(ctx->active_remote)); + LOG_INFO("[%p]: Remote search thread aborted", ctx); } diff --git a/components/raop/rtp.c b/components/raop/rtp.c index beccdda9..0a3b6b3f 100644 --- a/components/raop/rtp.c +++ b/components/raop/rtp.c @@ -443,25 +443,23 @@ static void buffer_put_packet(rtp_t *ctx, seq_t seqno, unsigned rtptime, bool fi LOG_SDEBUG("packet expected seqno:%hu rtptime:%u (W:%hu R:%hu)", seqno, rtptime, ctx->ab_write, ctx->ab_read); } else if (seq_order(ctx->ab_write, seqno)) { + seq_t i; + u32_t now; + // newer than expected if (ctx->latency && seq_order(ctx->latency / ctx->frame_size, seqno - ctx->ab_write - 1)) { // only get rtp latency-1 frames back (last one is seqno) LOG_WARN("[%p] too many missing frames %hu seq: %hu, (W:%hu R:%hu)", ctx, seqno - ctx->ab_write - 1, seqno, ctx->ab_write, ctx->ab_read); ctx->ab_write = seqno - ctx->latency / ctx->frame_size; } - if (ctx->latency && seq_order(ctx->latency / ctx->frame_size, seqno - ctx->ab_read)) { - // if ab_read is lagging more than http latency, advance it - LOG_WARN("[%p] on hold for too long %hu (W:%hu R:%hu)", ctx, seqno - ctx->ab_read + 1, ctx->ab_write, ctx->ab_read); - ctx->ab_read = seqno - ctx->latency / ctx->frame_size + 1; - } - if (rtp_request_resend(ctx, ctx->ab_write + 1, seqno-1)) { - seq_t i; - u32_t now = gettime_ms(); - for (i = ctx->ab_write + 1; seq_order(i, seqno); i++) { - ctx->audio_buffer[BUFIDX(i)].rtptime = rtptime - (seqno-i)*ctx->frame_size; - ctx->audio_buffer[BUFIDX(i)].last_resend = now; - } + + // need to request re-send and adjust timing of gaps + rtp_request_resend(ctx, ctx->ab_write + 1, seqno-1); + for (now = gettime_ms(), i = ctx->ab_write + 1; seq_order(i, seqno); i++) { + ctx->audio_buffer[BUFIDX(i)].rtptime = rtptime - (seqno-i)*ctx->frame_size; + ctx->audio_buffer[BUFIDX(i)].last_resend = now; } + LOG_DEBUG("[%p]: packet newer seqno:%hu rtptime:%u (W:%hu R:%hu)", ctx, seqno, rtptime, ctx->ab_write, ctx->ab_read); abuf = ctx->audio_buffer + BUFIDX(seqno); ctx->ab_write = seqno; @@ -519,14 +517,21 @@ static void buffer_push_packet(rtp_t *ctx) { LOG_DEBUG("[%p]: discarded frame now:%u missed by:%d (W:%hu R:%hu)", ctx, now, now - playtime, ctx->ab_write, ctx->ab_read); ctx->discarded++; curframe->ready = 0; + } else if (playtime - now <= hold) { + if (curframe->ready) { + ctx->data_cb((const u8_t*) curframe->data, curframe->len, playtime); + curframe->ready = 0; + } else { + LOG_DEBUG("[%p]: created zero frame (W:%hu R:%hu)", ctx, ctx->ab_write, ctx->ab_read); + ctx->data_cb(silence_frame, ctx->frame_size * 4, playtime); + ctx->silent_frames++; + } } else if (curframe->ready) { ctx->data_cb((const u8_t*) curframe->data, curframe->len, playtime); curframe->ready = 0; - } else if (playtime - now <= hold) { - LOG_DEBUG("[%p]: created zero frame (W:%hu R:%hu)", ctx, ctx->ab_write, ctx->ab_read); - ctx->data_cb(silence_frame, ctx->frame_size * 4, playtime); - ctx->silent_frames++; - } else break; + } else { + break; + } ctx->ab_read++; ctx->out_frames++; @@ -637,7 +642,7 @@ static void *rtp_thread_func(void *arg) { u32_t rtp_now = ntohl(*(u32_t*)(pktp+16)); u16_t flags = ntohs(*(u16_t*)(pktp+2)); u32_t remote_gap = NTP2MS(remote - ctx->timing.remote); - + // something is wrong and if we are supposed to be NTP synced, better ask for re-sync if (remote_gap > 10000) { if (ctx->synchro.status & NTP_SYNC) rtp_request_timing(ctx); diff --git a/components/services/component.mk b/components/services/component.mk index ace79fdb..0bf7bd7a 100644 --- a/components/services/component.mk +++ b/components/services/component.mk @@ -9,3 +9,4 @@ COMPONENT_SRCDIRS := . COMPONENT_ADD_INCLUDEDIRS := . +CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_DEBUG \ No newline at end of file diff --git a/components/services/messaging.c b/components/services/messaging.c index f8841baa..065de8d3 100644 --- a/components/services/messaging.c +++ b/components/services/messaging.c @@ -27,7 +27,7 @@ typedef struct { RingbufHandle_t buf_handle; } messaging_list_t; static messaging_list_t top; - +#define MSG_LENGTH_AVG 1024 messaging_list_t * get_struct_ptr(messaging_handle_t handle){ return (messaging_list_t *)handle; @@ -35,12 +35,14 @@ messaging_list_t * get_struct_ptr(messaging_handle_t handle){ messaging_handle_t get_handle_ptr(messaging_list_t * handle){ return (messaging_handle_t )handle; } + RingbufHandle_t messaging_create_ring_buffer(uint8_t max_count){ RingbufHandle_t buf_handle = NULL; StaticRingbuffer_t *buffer_struct = malloc(sizeof(StaticRingbuffer_t)); if (buffer_struct != NULL) { - size_t buf_size = (size_t )(sizeof(single_message_t)+8)*(size_t )(max_count>0?max_count:5); // no-split buffer requires an additional 8 bytes - uint8_t *buffer_storage = (uint8_t *)heap_caps_malloc(buf_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + size_t buf_size = (size_t )(sizeof(single_message_t)+8+MSG_LENGTH_AVG)*(size_t )(max_count>0?max_count:5); // no-split buffer requires an additional 8 bytes + buf_size = buf_size - (buf_size % 4); + uint8_t *buffer_storage = (uint8_t *)heap_caps_malloc(buf_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_32BIT); if (buffer_storage== NULL) { ESP_LOGE(tag,"buff alloc failed"); } @@ -62,9 +64,9 @@ void messaging_fill_messages(messaging_list_t * target_subscriber){ message= messaging_retrieve_message(top.buf_handle); if(message){ //re-post to original queue so it is available to future subscribers - messaging_post_to_queue(get_handle_ptr(&top), message, sizeof(single_message_t)); + messaging_post_to_queue(get_handle_ptr(&top), message, message->msg_size); // post to new subscriber - messaging_post_to_queue(get_handle_ptr(target_subscriber) , message, sizeof(single_message_t)); + messaging_post_to_queue(get_handle_ptr(target_subscriber) , message, message->msg_size); FREE_AND_NULL(message); } } @@ -116,6 +118,7 @@ const char * messaging_get_class_desc(messaging_classes msg_class){ switch (msg_class) { CASE_TO_STR(MESSAGING_CLASS_OTA); CASE_TO_STR(MESSAGING_CLASS_SYSTEM); + CASE_TO_STR(MESSAGING_CLASS_STATS); default: return "Unknown"; break; @@ -156,14 +159,9 @@ single_message_t * messaging_retrieve_message(RingbufHandle_t buf_handle){ vRingbufferGetInfo(buf_handle, NULL, NULL, NULL, NULL, &uxItemsWaiting); if(uxItemsWaiting>0){ message = (single_message_t *)xRingbufferReceive(buf_handle, &item_size, pdMS_TO_TICKS(50)); - if(item_size!=sizeof(single_message_t)){ - ESP_LOGE(tag,"Invalid message length!"); - } - else { - message_copy = heap_caps_malloc(item_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); - if(message_copy){ - memcpy(message_copy,message,item_size); - } + message_copy = heap_caps_malloc(item_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if(message_copy){ + memcpy(message_copy,message,item_size); } vRingbufferReturnItem(buf_handle, (void *)message); } @@ -171,25 +169,34 @@ single_message_t * messaging_retrieve_message(RingbufHandle_t buf_handle){ } esp_err_t messaging_post_to_queue(messaging_handle_t subscriber_handle, single_message_t * message, size_t message_size){ - UBaseType_t uxItemsWaiting=0; size_t item_size=0; messaging_list_t * subscriber=get_struct_ptr(subscriber_handle); if(!subscriber->buf_handle){ ESP_LOGE(tag,"post failed: null buffer for %s", str_or_unknown(subscriber->subscriber_name)); return ESP_FAIL; } - vRingbufferGetInfo(subscriber->buf_handle,NULL,NULL,NULL,NULL,&uxItemsWaiting); - if(uxItemsWaiting>=subscriber->max_count){ - ESP_LOGD(tag,"messaged dropped for %s",str_or_unknown(subscriber->subscriber_name)); + void * pItem=NULL; + UBaseType_t res=pdFALSE; + while(1){ + ESP_LOGD(tag,"Attempting to reserve %d bytes for %s",message_size, str_or_unknown(subscriber->subscriber_name)); + res = xRingbufferSendAcquire(subscriber->buf_handle, &pItem, message_size, pdMS_TO_TICKS(50)); + if(res == pdTRUE && pItem){ + ESP_LOGD(tag,"Reserving complete for %s", str_or_unknown(subscriber->subscriber_name)); + memcpy(pItem,message,message_size); + xRingbufferSendComplete(subscriber->buf_handle, pItem); + break; + } + ESP_LOGD(tag,"Dropping for %s",str_or_unknown(subscriber->subscriber_name)); single_message_t * dummy = (single_message_t *)xRingbufferReceive(subscriber->buf_handle, &item_size, pdMS_TO_TICKS(50)); if (dummy== NULL) { - ESP_LOGE(tag,"receive from buffer failed"); + ESP_LOGE(tag,"Dropping message failed"); + break; } else { + ESP_LOGD(tag,"Dropping message of %d bytes for %s",item_size, str_or_unknown(subscriber->subscriber_name)); vRingbufferReturnItem(subscriber->buf_handle, (void *)dummy); } } - UBaseType_t res = xRingbufferSend(subscriber->buf_handle, message, message_size, pdMS_TO_TICKS(1000)); if (res != pdTRUE) { ESP_LOGE(tag,"post to %s failed",str_or_unknown(subscriber->subscriber_name)); return ESP_FAIL; @@ -197,19 +204,27 @@ esp_err_t messaging_post_to_queue(messaging_handle_t subscriber_handle, single_m return ESP_OK; } void messaging_post_message(messaging_types type,messaging_classes msg_class, char *fmt, ...){ - single_message_t message={}; + single_message_t * message=NULL; + size_t msg_size=0; + size_t ln =0; messaging_list_t * cur=⊤ va_list va; va_start(va, fmt); - vsnprintf(message.message, sizeof(message.message), fmt, va); + ln = vsnprintf(NULL, 0, fmt, va)+1; + msg_size = sizeof(single_message_t)+ln; + message = (single_message_t *)heap_caps_malloc(msg_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + vsprintf(message->message, fmt, va); va_end(va); - message.type = type; - message.msg_class = msg_class; - message.sent_time = esp_timer_get_time() / 1000; + message->msg_size = msg_size; + message->type = type; + message->msg_class = msg_class; + message->sent_time = esp_timer_get_time() / 1000; + ESP_LOGD(tag,"Post: %s",message->message); while(cur){ - messaging_post_to_queue(get_handle_ptr(cur), &message, sizeof(single_message_t)); + messaging_post_to_queue(get_handle_ptr(cur), message, msg_size); cur = get_struct_ptr(cur->next); } + FREE_AND_NULL(message); return; } diff --git a/components/services/messaging.h b/components/services/messaging.h index fb0ca0a0..f9ecbca6 100644 --- a/components/services/messaging.h +++ b/components/services/messaging.h @@ -9,7 +9,8 @@ typedef enum { } messaging_types; typedef enum { MESSAGING_CLASS_OTA, - MESSAGING_CLASS_SYSTEM + MESSAGING_CLASS_SYSTEM, + MESSAGING_CLASS_STATS } messaging_classes; typedef struct messaging_list_t *messaging_handle_t; @@ -18,7 +19,8 @@ typedef struct { time_t sent_time; messaging_types type; messaging_classes msg_class; - char message[151]; + size_t msg_size; + char message[]; } single_message_t; cJSON * messaging_retrieve_messages(RingbufHandle_t buf_handle); diff --git a/components/services/monitor.c b/components/services/monitor.c index 4b56dbe5..15e861ad 100644 --- a/components/services/monitor.c +++ b/components/services/monitor.c @@ -21,6 +21,9 @@ #include "globdefs.h" #include "config.h" #include "accessors.h" +#include "messaging.h" +#include "cJSON.h" +#include "trace.h" #define MONITOR_TIMER (10*1000) @@ -43,16 +46,17 @@ bool spkfault_svc(void); /**************************************************************************************** * */ -static void task_stats( void ) { +static void task_stats( cJSON* top ) { #ifdef CONFIG_FREERTOS_USE_TRACE_FACILITY static struct { TaskStatus_t *tasks; uint32_t total, n; } current, previous; - + cJSON * tlist=cJSON_CreateArray(); current.n = uxTaskGetNumberOfTasks(); current.tasks = malloc( current.n * sizeof( TaskStatus_t ) ); current.n = uxTaskGetSystemState( current.tasks, current.n, ¤t.total ); + cJSON_AddNumberToObject(top,"ntasks",current.n); static EXT_RAM_ATTR char scratch[128+1]; *scratch = '\0'; @@ -66,6 +70,15 @@ static void task_stats( void ) { n += sprintf(scratch + n, "%16s %2u%% s:%5u", current.tasks[i].pcTaskName, 100 * (current.tasks[i].ulRunTimeCounter - previous.tasks[j].ulRunTimeCounter) / elapsed, current.tasks[i].usStackHighWaterMark); + cJSON * t=cJSON_CreateObject(); + cJSON_AddNumberToObject(t,"cpu",100 * (current.tasks[i].ulRunTimeCounter - previous.tasks[j].ulRunTimeCounter) / elapsed); + cJSON_AddNumberToObject(t,"minstk",current.tasks[i].usStackHighWaterMark); + cJSON_AddNumberToObject(t,"bprio",current.tasks[i].uxBasePriority); + cJSON_AddNumberToObject(t,"cprio",current.tasks[i].uxCurrentPriority); + cJSON_AddStringToObject(t,"nme",current.tasks[i].pcTaskName); + cJSON_AddNumberToObject(t,"st",current.tasks[i].eCurrentState); + cJSON_AddNumberToObject(t,"num",current.tasks[i].xTaskNumber); + cJSON_AddItemToArray(tlist,t); if (i % 3 == 2 || i == current.n - 1) { ESP_LOGI(TAG, "%s", scratch); n = 0; @@ -77,13 +90,21 @@ static void task_stats( void ) { #else for (int i = 0, n = 0; i < current.n; i ++) { n += sprintf(scratch + n, "%16s s:%5u\t", current.tasks[i].pcTaskName, current.tasks[i].usStackHighWaterMark); + cJSON * t=cJSON_CreateObject(); + cJSON_AddNumberToObject(t,"minstk",current.tasks[i].usStackHighWaterMark); + cJSON_AddNumberToObject(t,"bprio",current.tasks[i].uxBasePriority); + cJSON_AddNumberToObject(t,"cprio",current.tasks[i].uxCurrentPriority); + cJSON_AddStringToObject(t,"nme",current.tasks[i].pcTaskName); + cJSON_AddStringToObject(t,"st",current.tasks[i].eCurrentState); + cJSON_AddNumberToObject(t,"num",current.tasks[i].xTaskNumber); + cJSON_AddItemToArray(tlist,t); if (i % 3 == 2 || i == current.n - 1) { ESP_LOGI(TAG, "%s", scratch); n = 0; } } #endif - + cJSON_AddItemToObject(top,"tasks",tlist); if (previous.tasks) free(previous.tasks); previous = current; #endif @@ -93,13 +114,26 @@ static void task_stats( void ) { * */ static void monitor_callback(TimerHandle_t xTimer) { + cJSON * top=cJSON_CreateObject(); + cJSON_AddNumberToObject(top,"free_iram",heap_caps_get_free_size(MALLOC_CAP_INTERNAL)); + cJSON_AddNumberToObject(top,"min_free_iram",heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL)); + cJSON_AddNumberToObject(top,"free_spiram",heap_caps_get_free_size(MALLOC_CAP_SPIRAM)); + cJSON_AddNumberToObject(top,"min_free_spiram",heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM)); + ESP_LOGI(TAG, "Heap internal:%zu (min:%zu) external:%zu (min:%zu)", heap_caps_get_free_size(MALLOC_CAP_INTERNAL), heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL), heap_caps_get_free_size(MALLOC_CAP_SPIRAM), heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM)); - task_stats(); + task_stats(top); + char * top_a= cJSON_PrintUnformatted(top); + if(top_a){ + messaging_post_message(MESSAGING_INFO, MESSAGING_CLASS_STATS,top_a); + FREE_AND_NULL(top_a); + } + cJSON_free(top); + } /**************************************************************************************** @@ -107,6 +141,7 @@ static void monitor_callback(TimerHandle_t xTimer) { */ static void jack_handler_default(void *id, button_event_e event, button_press_e mode, bool long_press) { ESP_LOGD(TAG, "Jack %s", event == BUTTON_PRESSED ? "inserted" : "removed"); + messaging_post_message(MESSAGING_INFO, MESSAGING_CLASS_SYSTEM,"jack is %s",BUTTON_PRESSED ? "inserted" : "removed"); if (jack_handler_svc) (*jack_handler_svc)(event == BUTTON_PRESSED); } diff --git a/components/squeezelite-ota/squeezelite-ota.c b/components/squeezelite-ota/squeezelite-ota.c index 6202c051..ea7bd719 100644 --- a/components/squeezelite-ota/squeezelite-ota.c +++ b/components/squeezelite-ota/squeezelite-ota.c @@ -616,7 +616,7 @@ void ota_task(void *pvParameter) { esp_err_t err = ESP_OK; int data_read = 0; - GDS_TextSetFont(display,2,&Font_droid_sans_fallback_15x17,-2); + GDS_TextSetFont(display,2,GDS_GetHeight(display)>32?&Font_droid_sans_fallback_15x17:&Font_droid_sans_fallback_11x13,-2); GDS_ClearExt(display, true); GDS_TextLine(display, 1, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "Firmware update"); GDS_TextLine(display, 2, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "Initializing"); diff --git a/components/telnet/telnet.c b/components/telnet/telnet.c index 0f603ae4..03bab437 100644 --- a/components/telnet/telnet.c +++ b/components/telnet/telnet.c @@ -38,8 +38,10 @@ #include "config.h" #include "nvs_utilities.h" #include "platform_esp32.h" +#include "messaging.h" #include "trace.h" + /************************************ * Globals */ @@ -119,6 +121,8 @@ void init_telnet(){ buf_handle = xRingbufferCreateStatic(log_buf_size, RINGBUF_TYPE_BYTEBUF, buffer_storage, buffer_struct); if (buf_handle == NULL) { ESP_LOGE(TAG,"Failed to create ring buffer for telnet!"); + messaging_post_message(MESSAGING_ERROR,MESSAGING_CLASS_SYSTEM,"Failed to allocate memory for telnet buffer"); + return; } @@ -144,7 +148,7 @@ void init_telnet(){ void start_telnet(void * pvParameter){ static bool isStarted=false; StaticTask_t *xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)); - StackType_t *xStack = heap_caps_malloc(TELNET_STACK_SIZE,(MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)); + StackType_t *xStack = heap_caps_malloc(TELNET_STACK_SIZE,(MALLOC_CAP_SPIRAM|MALLOC_CAP_8BIT)); if(!isStarted && bIsEnabled) { xTaskCreateStatic( (TaskFunction_t) &telnet_task, "telnet", TELNET_STACK_SIZE, NULL, ESP_TASK_MAIN_PRIO , xStack, xTaskBuffer); diff --git a/components/wifi-manager/_esp_http_server.h b/components/wifi-manager/_esp_http_server.h new file mode 100644 index 00000000..bde57c65 --- /dev/null +++ b/components/wifi-manager/_esp_http_server.h @@ -0,0 +1,93 @@ +// Copyright 2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef __ESP_HTTP_SERVER_H_ +#define __ESP_HTTP_SERVER_H_ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + +/** + * @brief Starts the web server + * + * Create an instance of HTTP server and allocate memory/resources for it + * depending upon the specified configuration. + * + * Example usage: + * @code{c} + * + * //Function for starting the webserver + * httpd_handle_t start_webserver(void) + * { + * // Generate default configuration + * httpd_config_t config = HTTPD_DEFAULT_CONFIG(); + * + * // Empty handle to http_server + * httpd_handle_t server = NULL; + * + * // Start the httpd server + * if (httpd_start(&server, &config) == ESP_OK) { + * // Register URI handlers + * httpd_register_uri_handler(server, &uri_get); + * httpd_register_uri_handler(server, &uri_post); + * } + * // If server failed to start, handle will be NULL + * return server; + * } + * + * @endcode + * + * @param[in] config Configuration for new instance of the server + * @param[out] handle Handle to newly created instance of the server. NULL on error + * @return + * - ESP_OK : Instance created successfully + * - ESP_ERR_INVALID_ARG : Null argument(s) + * - ESP_ERR_HTTPD_ALLOC_MEM : Failed to allocate memory for instance + * - ESP_ERR_HTTPD_TASK : Failed to launch server task + */ +esp_err_t __httpd_start(httpd_handle_t *handle, const httpd_config_t *config); +static inline int __httpd_os_thread_create_static(TaskHandle_t *thread, + const char *name, uint16_t stacksize, int prio, + void (*thread_routine)(void *arg), void *arg, + BaseType_t core_id) +{ + + + StaticTask_t *xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)); + StackType_t *xStack = heap_caps_malloc(stacksize,(MALLOC_CAP_SPIRAM|MALLOC_CAP_8BIT)); + + + // + *thread = xTaskCreateStaticPinnedToCore(thread_routine, name, stacksize, arg, prio, xStack,xTaskBuffer,core_id); + if (*thread) { + return ESP_OK; + } + return ESP_FAIL; +} +#ifdef __cplusplus +} +#endif + +#endif /* ! _ESP_HTTP_SERVER_H_ */ diff --git a/components/wifi-manager/_esp_httpd_main.c b/components/wifi-manager/_esp_httpd_main.c new file mode 100644 index 00000000..af6faefa --- /dev/null +++ b/components/wifi-manager/_esp_httpd_main.c @@ -0,0 +1,373 @@ +// Copyright 2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include + +#include +#include <_esp_http_server.h> +#include "../src/esp_httpd_priv.h" +#include "../src/util/ctrl_sock.h" + +static const char *TAG = "_httpd"; + + +struct httpd_ctrl_data { + enum httpd_ctrl_msg { + HTTPD_CTRL_SHUTDOWN, + HTTPD_CTRL_WORK, + } hc_msg; + httpd_work_fn_t hc_work; + void *hc_work_arg; +}; + + +static esp_err_t _httpd_server_init(struct httpd_data *hd) +{ + int fd = socket(PF_INET6, SOCK_STREAM, 0); + if (fd < 0) { + ESP_LOGE(TAG, LOG_FMT("error in socket (%d)"), errno); + return ESP_FAIL; + } + + struct in6_addr inaddr_any = IN6ADDR_ANY_INIT; + struct sockaddr_in6 serv_addr = { + .sin6_family = PF_INET6, + .sin6_addr = inaddr_any, + .sin6_port = htons(hd->config.server_port) + }; + + /* Enable SO_REUSEADDR to allow binding to the same + * address and port when restarting the server */ + int enable = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) { + /* This will fail if CONFIG_LWIP_SO_REUSE is not enabled. But + * it does not affect the normal working of the HTTP Server */ + ESP_LOGW(TAG, LOG_FMT("error enabling SO_REUSEADDR (%d)"), errno); + } + + int ret = bind(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); + if (ret < 0) { + ESP_LOGE(TAG, LOG_FMT("error in bind (%d)"), errno); + close(fd); + return ESP_FAIL; + } + + ret = listen(fd, hd->config.backlog_conn); + if (ret < 0) { + ESP_LOGE(TAG, LOG_FMT("error in listen (%d)"), errno); + close(fd); + return ESP_FAIL; + } + + int ctrl_fd = cs_create_ctrl_sock(hd->config.ctrl_port); + if (ctrl_fd < 0) { + ESP_LOGE(TAG, LOG_FMT("error in creating ctrl socket (%d)"), errno); + close(fd); + return ESP_FAIL; + } + + int msg_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (msg_fd < 0) { + ESP_LOGE(TAG, LOG_FMT("error in creating msg socket (%d)"), errno); + close(fd); + close(ctrl_fd); + return ESP_FAIL; + } + + hd->listen_fd = fd; + hd->ctrl_fd = ctrl_fd; + hd->msg_fd = msg_fd; + return ESP_OK; +} + +static void _httpd_process_ctrl_msg(struct httpd_data *hd) +{ + struct httpd_ctrl_data msg; + int ret = recv(hd->ctrl_fd, &msg, sizeof(msg), 0); + if (ret <= 0) { + ESP_LOGW(TAG, LOG_FMT("error in recv (%d)"), errno); + return; + } + if (ret != sizeof(msg)) { + ESP_LOGW(TAG, LOG_FMT("incomplete msg")); + return; + } + + switch (msg.hc_msg) { + case HTTPD_CTRL_WORK: + if (msg.hc_work) { + ESP_LOGD(TAG, LOG_FMT("work")); + (*msg.hc_work)(msg.hc_work_arg); + } + break; + case HTTPD_CTRL_SHUTDOWN: + ESP_LOGD(TAG, LOG_FMT("shutdown")); + hd->hd_td.status = THREAD_STOPPING; + break; + default: + break; + } +} + +static esp_err_t _httpd_accept_conn(struct httpd_data *hd, int listen_fd) +{ + /* If no space is available for new session, close the least recently used one */ + if (hd->config.lru_purge_enable == true) { + if (!httpd_is_sess_available(hd)) { + /* Queue asynchronous closure of the least recently used session */ + return httpd_sess_close_lru(hd); + /* Returning from this allowes the main server thread to process + * the queued asynchronous control message for closing LRU session. + * Since connection request hasn't been addressed yet using accept() + * therefore _httpd_accept_conn() will be called again, but this time + * with space available for one session + */ + } + } + + struct sockaddr_in addr_from; + socklen_t addr_from_len = sizeof(addr_from); + int new_fd = accept(listen_fd, (struct sockaddr *)&addr_from, &addr_from_len); + if (new_fd < 0) { + ESP_LOGW(TAG, LOG_FMT("error in accept (%d)"), errno); + return ESP_FAIL; + } + ESP_LOGD(TAG, LOG_FMT("newfd = %d"), new_fd); + + struct timeval tv; + /* Set recv timeout of this fd as per config */ + tv.tv_sec = hd->config.recv_wait_timeout; + tv.tv_usec = 0; + setsockopt(new_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)); + + /* Set send timeout of this fd as per config */ + tv.tv_sec = hd->config.send_wait_timeout; + tv.tv_usec = 0; + setsockopt(new_fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv)); + + if (ESP_OK != httpd_sess_new(hd, new_fd)) { + ESP_LOGW(TAG, LOG_FMT("session creation failed")); + close(new_fd); + return ESP_FAIL; + } + ESP_LOGD(TAG, LOG_FMT("complete")); + return ESP_OK; +} +/* Manage in-coming connection or data requests */ +static esp_err_t _httpd_server(struct httpd_data *hd) +{ + fd_set read_set; + FD_ZERO(&read_set); + if (hd->config.lru_purge_enable || httpd_is_sess_available(hd)) { + /* Only listen for new connections if server has capacity to + * handle more (or when LRU purge is enabled, in which case + * older connections will be closed) */ + FD_SET(hd->listen_fd, &read_set); + } + FD_SET(hd->ctrl_fd, &read_set); + + int tmp_max_fd; + httpd_sess_set_descriptors(hd, &read_set, &tmp_max_fd); + int maxfd = MAX(hd->listen_fd, tmp_max_fd); + tmp_max_fd = maxfd; + maxfd = MAX(hd->ctrl_fd, tmp_max_fd); + + ESP_LOGD(TAG, LOG_FMT("doing select maxfd+1 = %d"), maxfd + 1); + int active_cnt = select(maxfd + 1, &read_set, NULL, NULL, NULL); + if (active_cnt < 0) { + ESP_LOGE(TAG, LOG_FMT("error in select (%d)"), errno); + httpd_sess_delete_invalid(hd); + return ESP_OK; + } + + /* Case0: Do we have a control message? */ + if (FD_ISSET(hd->ctrl_fd, &read_set)) { + ESP_LOGD(TAG, LOG_FMT("processing ctrl message")); + _httpd_process_ctrl_msg(hd); + if (hd->hd_td.status == THREAD_STOPPING) { + ESP_LOGD(TAG, LOG_FMT("stopping thread")); + return ESP_FAIL; + } + } + + /* Case1: Do we have any activity on the current data + * sessions? */ + int fd = -1; + while ((fd = httpd_sess_iterate(hd, fd)) != -1) { + if (FD_ISSET(fd, &read_set) || (httpd_sess_pending(hd, fd))) { + ESP_LOGD(TAG, LOG_FMT("processing socket %d"), fd); + if (httpd_sess_process(hd, fd) != ESP_OK) { + ESP_LOGD(TAG, LOG_FMT("closing socket %d"), fd); + close(fd); + /* Delete session and update fd to that + * preceding the one being deleted */ + fd = httpd_sess_delete(hd, fd); + } + } + } + + /* Case2: Do we have any incoming connection requests to + * process? */ + if (FD_ISSET(hd->listen_fd, &read_set)) { + ESP_LOGD(TAG, LOG_FMT("processing listen socket %d"), hd->listen_fd); + if (_httpd_accept_conn(hd, hd->listen_fd) != ESP_OK) { + ESP_LOGW(TAG, LOG_FMT("error accepting new connection")); + } + } + return ESP_OK; +} + +static void _httpd_close_all_sessions(struct httpd_data *hd) +{ + int fd = -1; + while ((fd = httpd_sess_iterate(hd, fd)) != -1) { + ESP_LOGD(TAG, LOG_FMT("cleaning up socket %d"), fd); + httpd_sess_delete(hd, fd); + close(fd); + } +} +/* The main HTTPD thread */ +static void _httpd_thread(void *arg) +{ + int ret; + struct httpd_data *hd = (struct httpd_data *) arg; + hd->hd_td.status = THREAD_RUNNING; + + ESP_LOGD(TAG, LOG_FMT("web server started")); + while (1) { + ret = _httpd_server(hd); + if (ret != ESP_OK) { + break; + } + } + + ESP_LOGD(TAG, LOG_FMT("web server exiting")); + close(hd->msg_fd); + cs_free_ctrl_sock(hd->ctrl_fd); + _httpd_close_all_sessions(hd); + close(hd->listen_fd); + hd->hd_td.status = THREAD_STOPPED; + httpd_os_thread_delete(); +} +static struct httpd_data *__httpd_create(const httpd_config_t *config) +{ + /* Allocate memory for httpd instance data */ + struct httpd_data *hd = calloc(1, sizeof(struct httpd_data)); + if (!hd) { + ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP server instance")); + return NULL; + } + hd->hd_calls = calloc(config->max_uri_handlers, sizeof(httpd_uri_t *)); + if (!hd->hd_calls) { + ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP URI handlers")); + free(hd); + return NULL; + } + hd->hd_sd = calloc(config->max_open_sockets, sizeof(struct sock_db)); + if (!hd->hd_sd) { + ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP session data")); + free(hd->hd_calls); + free(hd); + return NULL; + } + struct httpd_req_aux *ra = &hd->hd_req_aux; + ra->resp_hdrs = calloc(config->max_resp_headers, sizeof(struct resp_hdr)); + if (!ra->resp_hdrs) { + ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP response headers")); + free(hd->hd_sd); + free(hd->hd_calls); + free(hd); + return NULL; + } + hd->err_handler_fns = calloc(HTTPD_ERR_CODE_MAX, sizeof(httpd_err_handler_func_t)); + if (!hd->err_handler_fns) { + ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP error handlers")); + free(ra->resp_hdrs); + free(hd->hd_sd); + free(hd->hd_calls); + free(hd); + return NULL; + } + /* Save the configuration for this instance */ + hd->config = *config; + return hd; +} +static void _httpd_delete(struct httpd_data *hd) +{ + struct httpd_req_aux *ra = &hd->hd_req_aux; + /* Free memory of httpd instance data */ + free(hd->err_handler_fns); + free(ra->resp_hdrs); + free(hd->hd_sd); + + /* Free registered URI handlers */ + httpd_unregister_all_uri_handlers(hd); + free(hd->hd_calls); + free(hd); +} +esp_err_t __httpd_start(httpd_handle_t *handle, const httpd_config_t *config) +{ + if (handle == NULL || config == NULL) { + return ESP_ERR_INVALID_ARG; + } + + /* Sanity check about whether LWIP is configured for providing the + * maximum number of open sockets sufficient for the server. Though, + * this check doesn't guarantee that many sockets will actually be + * available at runtime as other processes may use up some sockets. + * Note that server also uses 3 sockets for its internal use : + * 1) listening for new TCP connections + * 2) for sending control messages over UDP + * 3) for receiving control messages over UDP + * So the total number of required sockets is max_open_sockets + 3 + */ + if (CONFIG_LWIP_MAX_SOCKETS < config->max_open_sockets + 3) { + ESP_LOGE(TAG, "Configuration option max_open_sockets is too large (max allowed %d)\n\t" + "Either decrease this or configure LWIP_MAX_SOCKETS to a larger value", + CONFIG_LWIP_MAX_SOCKETS - 3); + return ESP_ERR_INVALID_ARG; + } + + struct httpd_data *hd = __httpd_create(config); + if (hd == NULL) { + /* Failed to allocate memory */ + return ESP_ERR_HTTPD_ALLOC_MEM; + } + + if (_httpd_server_init(hd) != ESP_OK) { + _httpd_delete(hd); + return ESP_FAIL; + } + + httpd_sess_init(hd); + if (__httpd_os_thread_create_static(&hd->hd_td.handle, "httpd", + hd->config.stack_size, + hd->config.task_priority, + _httpd_thread, hd, + hd->config.core_id) != ESP_OK) { + /* Failed to launch task */ + _httpd_delete(hd); + return ESP_ERR_HTTPD_TASK; + } + + *handle = (httpd_handle_t *)hd; + return ESP_OK; +} + diff --git a/components/wifi-manager/code.js b/components/wifi-manager/code.js index 55fcb8e0..6a6f12f5 100644 --- a/components/wifi-manager/code.js +++ b/components/wifi-manager/code.js @@ -24,6 +24,14 @@ var nvs_type_t = { NVS_TYPE_ANY : 0xff /*!< Must be last */ } ; +var task_state_t = { + 0 : "eRunning", /*!< A task is querying the state of itself, so must be running. */ + 1 : "eReady", /*!< The task being queried is in a read or pending ready list. */ + 2 : "eBlocked", /*!< The task being queried is in the Blocked state. */ + 3 : "eSuspended", /*!< The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */ + 4 : "eDeleted" + +} var releaseURL = 'https://api.github.com/repos/sle118/squeezelite-esp32/releases'; var recovery = false; var enableAPTimer = true; @@ -649,7 +657,9 @@ function refreshAPHTML(data){ function getMessages() { $.getJSON("/messages.json", function(data) { data.forEach(function(msg) { - var msg_age = msg["current_time"] - msg["sent_time"]; + var msg_age = msg["current_time"] - msg["sent_time"]; + var msg_time = new Date( ); + msg_time.setTime( msg_time .getTime() - msg_age ); switch (msg["class"]) { case "MESSAGING_CLASS_OTA": //message: "{"ota_dsc":"Erasing flash complete","ota_pct":0}" @@ -668,6 +678,16 @@ function getMessages() { } } break; + case "MESSAGING_CLASS_STATS": + // for task states, check structure : task_state_t + var stats_data = JSON.parse(msg["message"]); + console.log(msg_time + " - Number of tasks on the ESP32: " + stats_data["ntasks"]); + var stats_tasks = stats_data["tasks"]; + console.log(msg_time + '\tname' + '\tcpu' + '\tstate'+ '\tminstk'+ '\tbprio'+ '\tcprio'+ '\tnum' ); + stats_tasks.forEach(function(task) { + console.log(msg_time + '\t' + task["nme"] + '\t'+ task["cpu"] + '\t'+ task_state_t[task["st"]]+ '\t'+ task["minstk"]+ '\t'+ task["bprio"]+ '\t'+ task["cprio"]+ '\t'+ task["num"]); + }); + break; case "MESSAGING_CLASS_SYSTEM": showMessage(msg["message"], msg["type"],msg_age); lastMsg = msg; diff --git a/components/wifi-manager/component.mk b/components/wifi-manager/component.mk index 1800f23c..4a5d5006 100644 --- a/components/wifi-manager/component.mk +++ b/components/wifi-manager/component.mk @@ -7,7 +7,7 @@ # please read the SDK documents if you need to do this. # COMPONENT_EMBED_FILES := style.css code.js index.html bootstrap.min.css.gz jquery.min.js.gz popper.min.js.gz bootstrap.min.js.gz -COMPONENT_ADD_INCLUDEDIRS := . +COMPONENT_ADD_INCLUDEDIRS := . $(IDF_PATH)/components/esp_http_server/src $(IDF_PATH)/components/esp_http_server/src/port/esp32 $(IDF_PATH)/components/esp_http_server/src/util CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_INFO diff --git a/components/wifi-manager/wifi_manager.c b/components/wifi-manager/wifi_manager.c index 667d01d6..430ffd6f 100644 --- a/components/wifi-manager/wifi_manager.c +++ b/components/wifi-manager/wifi_manager.c @@ -361,6 +361,7 @@ esp_err_t wifi_manager_save_sta_config(){ esp_err = nvs_commit(handle); if (esp_err != ESP_OK) { ESP_LOGE(TAG, "Unable to commit changes. Error %s", esp_err_to_name(esp_err)); + messaging_post_message(MESSAGING_ERROR,MESSAGING_CLASS_SYSTEM,"Unable to save wifi credentials. %s",esp_err_to_name(esp_err)); return esp_err; } nvs_close(handle); @@ -1335,6 +1336,8 @@ void wifi_manager( void * pvParameters ){ else{ /* lost connection ? */ ESP_LOGE(TAG, "WiFi Connection lost."); + messaging_post_message(MESSAGING_WARNING,MESSAGING_CLASS_SYSTEM,"WiFi Connection lost"); + if(wifi_manager_lock_json_buffer( portMAX_DELAY )){ wifi_manager_generate_ip_info_json( UPDATE_LOST_CONNECTION ); wifi_manager_unlock_json_buffer(); diff --git a/components/wifi-manager/wifi_manager_http_server.c b/components/wifi-manager/wifi_manager_http_server.c index 373cad3f..deb91352 100644 --- a/components/wifi-manager/wifi_manager_http_server.c +++ b/components/wifi-manager/wifi_manager_http_server.c @@ -22,6 +22,7 @@ #include "http_server_handlers.h" #include "esp_log.h" #include "esp_http_server.h" +#include "_esp_http_server.h" #include #include #include @@ -112,6 +113,7 @@ void register_regular_handlers(httpd_handle_t server){ } + esp_err_t http_server_start() { ESP_LOGI(TAG, "Initializing HTTP Server"); @@ -131,7 +133,7 @@ esp_err_t http_server_start() // config.open_fn ESP_LOGI(TAG, "Starting HTTP Server"); - esp_err_t err= httpd_start(&_server, &config); + esp_err_t err= __httpd_start(&_server, &config); if(err != ESP_OK){ ESP_LOGE_LOC(TAG,"Start server failed"); } diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 71b1bcdd..6cf0111e 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,4 +1,4 @@ -set(COMPONENT_ADD_INCLUDEDIRS . ) +set(COMPONENT_ADD_INCLUDEDIRS . ${IDF_PATH}/components/esp_http_server/src ${IDF_PATH}/components/esp_http_server/src/port/esp32 ${IDF_PATH}/components/esp_http_server/src/util) set(COMPONENT_SRCS "esp_app_main.c" "platform_esp32.c" "cmd_wifi.c" "console.c" "nvs_utilities.c" "cmd_squeezelite.c" "config.c") set(REQUIRES esp_common) diff --git a/main/component.mk b/main/component.mk index 8edcf630..175f015e 100644 --- a/main/component.mk +++ b/main/component.mk @@ -9,4 +9,4 @@ CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_INFO -DMODEL_NAME=SqueezeESP32 LDFLAGS += -s COMPONENT_EMBED_TXTFILES := ${PROJECT_PATH}/server_certs/github.pem -COMPONENT_ADD_INCLUDEDIRS := . \ No newline at end of file +COMPONENT_ADD_INCLUDEDIRS := . \ No newline at end of file diff --git a/main/esp_app_main.c b/main/esp_app_main.c index 10234c45..8ccb6018 100644 --- a/main/esp_app_main.c +++ b/main/esp_app_main.c @@ -47,6 +47,7 @@ #include "config.h" #include "audio_controls.h" #include "telnet.h" +#include "messaging.h" static const char certs_namespace[] = "certificates"; static const char certs_key[] = "blob"; @@ -73,12 +74,14 @@ const char * str_or_unknown(const char * str) { return (str?str:unknown_string_p /* brief this is an exemple of a callback that you can setup in your own app to get notified of wifi manager event */ void cb_connection_got_ip(void *pvParameter){ ESP_LOGI(TAG, "I have a connection!"); + messaging_post_message(MESSAGING_INFO,MESSAGING_CLASS_SYSTEM,"Wifi connected"); xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); bWifiConnected=true; led_unpush(LED_GREEN); } void cb_connection_sta_disconnected(void *pvParameter){ led_blink_pushed(LED_GREEN, 250, 250); + messaging_post_message(MESSAGING_WARNING,MESSAGING_CLASS_SYSTEM,"Wifi disconnected"); bWifiConnected=false; xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); } @@ -427,4 +430,5 @@ void app_main() #endif free(fwurl); } + messaging_post_message(MESSAGING_INFO,MESSAGING_CLASS_SYSTEM,"System started"); } diff --git a/main/platform_esp32.h b/main/platform_esp32.h index 3443e069..f0fff28a 100644 --- a/main/platform_esp32.h +++ b/main/platform_esp32.h @@ -20,7 +20,6 @@ */ #pragma once - #include "esp_pthread.h" #ifndef SQUEEZELITE_ESP32_RELEASE_URL #define SQUEEZELITE_ESP32_RELEASE_URL "https://github.com/sle118/squeezelite-esp32/releases" diff --git a/plugin/SqueezeESP32.zip b/plugin/SqueezeESP32.zip index f9688f9a..44b663b5 100644 Binary files a/plugin/SqueezeESP32.zip and b/plugin/SqueezeESP32.zip differ diff --git a/plugin/SqueezeESP32/Player.pm b/plugin/SqueezeESP32/Player.pm index fe6b208c..a1dc3e33 100644 --- a/plugin/SqueezeESP32/Player.pm +++ b/plugin/SqueezeESP32/Player.pm @@ -20,10 +20,10 @@ sub playerSettingsFrame { my $value; my $id = unpack('C', $$data_ref); - + # New SETD command 0xfe for display width if ($id == 0xfe) { - $value = (unpack('CC', $$data_ref))[1]; + $value = (unpack('Cn', $$data_ref))[1]; if ($value > 100 && $value < 400) { $client->display->widthOverride(1, $value); $client->update; diff --git a/plugin/SqueezeESP32/install.xml b/plugin/SqueezeESP32/install.xml index c8f6edc7..405670f3 100644 --- a/plugin/SqueezeESP32/install.xml +++ b/plugin/SqueezeESP32/install.xml @@ -10,6 +10,6 @@ PLUGIN_SQUEEZEESP32 PLUGIN_SQUEEZEESP32_DESC Plugins::SqueezeESP32::Plugin - 0.12 + 0.20 Philippe diff --git a/plugin/repo.xml b/plugin/repo.xml index 51d66d5c..838bd4d0 100644 --- a/plugin/repo.xml +++ b/plugin/repo.xml @@ -1,10 +1,10 @@ - + https://github.com/sle118/squeezelite-esp32 Philippe - 3e60650efdff28cd0da9a7e9d0ddccf3a68d350d + c17cce48b8fa14f995ca93f51fdab52b4bdcb422 philippe_44@outlook.com SqueezeESP32 additional player id (100) http://github.com/sle118/squeezelite-esp32/raw/master/plugin/SqueezeESP32.zip