From 9759c0dbef0e6688d5570a8c2cd267c128821cc8 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Sat, 29 Feb 2020 22:23:34 -0800 Subject: [PATCH 1/4] adding SSD1675 driver --- components/display/SH1106.c | 2 +- components/display/SSD1306.c | 14 +- components/display/SSD132x.c | 8 +- components/display/SSD1675.c | 252 ++++++++++++++++++++++++++ components/display/core/gds.c | 15 +- components/display/core/gds.h | 6 +- components/display/core/gds_private.h | 5 +- components/display/display.c | 13 +- 8 files changed, 287 insertions(+), 28 deletions(-) create mode 100644 components/display/SSD1675.c 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..0cdbe25c 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 { From 0bf2c11fdb1a1edcb3ac4c0a96cf9768f7123d0c Mon Sep 17 00:00:00 2001 From: philippe44 Date: Sun, 1 Mar 2020 00:25:09 -0800 Subject: [PATCH 2/4] Correcting annoying AirPlay bug! - release --- components/display/display.c | 2 +- components/raop/raop.c | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/components/display/display.c b/components/display/display.c index 0cdbe25c..ddc79a0a 100644 --- a/components/display/display.c +++ b/components/display/display.c @@ -192,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 07eab618..4d824f7e 100644 --- a/components/raop/raop.c +++ b/components/raop/raop.c @@ -631,11 +631,9 @@ 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, @@ -651,6 +649,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); } } From 1ed815466a4575cfaf7e9269e665017e15d74048 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Sun, 1 Mar 2020 10:36:15 -0800 Subject: [PATCH 3/4] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ``` From b2d90d36c173465f322c1d2fc1e9ebfd88834473 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Sun, 1 Mar 2020 15:49:52 -0800 Subject: [PATCH 4/4] Ancient AirPlay issue fixed - release --- components/raop/raop.c | 25 ++++++++++++++++--------- components/raop/rtp.c | 41 +++++++++++++++++++++++------------------ 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/components/raop/raop.c b/components/raop/raop.c index 4d824f7e..ffce9b46 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; @@ -640,7 +641,7 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock) 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)) { @@ -686,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; @@ -698,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);