From 8e95bd3dd28f6cded0feec261993f508c1c220f6 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Sat, 7 Mar 2020 14:01:53 -0800 Subject: [PATCH] AirPlay fix & misc - Spectrum scale fix - Initialize more display parameters - Reboot after 30s of no connection - Reboot after IP address change --- components/display/SSD1306.c | 13 ++++--- components/display/SSD132x.c | 20 +++++++++-- components/display/display.c | 4 +-- components/raop/raop.c | 7 ++-- components/raop/rtp.c | 58 ++++++++++++++++++------------ components/raop/rtp.h | 3 +- components/raop/util.c | 2 +- components/services/monitor.c | 8 +++-- components/squeezelite/display.c | 6 ++-- components/squeezelite/embedded.h | 17 +++++---- components/squeezelite/slimproto.c | 20 +++++++---- main/cmd_squeezelite.c | 19 +++++----- main/esp_app_main.c | 12 +++++++ 13 files changed, 127 insertions(+), 62 deletions(-) diff --git a/components/display/SSD1306.c b/components/display/SSD1306.c index fec88996..487dc84e 100644 --- a/components/display/SSD1306.c +++ b/components/display/SSD1306.c @@ -74,7 +74,7 @@ static void Update( struct GDS_Device* Device ) { CurrentPage = p + 1; // actual write - Device->WriteData( Device, Private->Shadowbuffer + p*width + first, last - first + 1); + Device->WriteData( Device, Private->Shadowbuffer + p*width + first, last - first + 1 ); } } #else @@ -114,6 +114,10 @@ static bool Init( struct GDS_Device* Device ) { // charge pump regulator, do direct init Device->WriteCommand( Device, 0x8D ); Device->WriteCommand( Device, 0x14 ); + + // set Clocks + Device->WriteCommand( Device, 0xD5 ); + Device->WriteCommand( Device, ( 0x08 << 4 ) | 0x00 ); // COM pins HW config (alternative:EN if 64, DIS if 32, remap:DIS) - some display might need something different Device->WriteCommand( Device, 0xDA ); @@ -122,6 +126,10 @@ static bool Init( struct GDS_Device* Device ) { // MUX Ratio Device->WriteCommand( Device, 0xA8 ); Device->WriteCommand( Device, Device->Height - 1); + // Page & GDDRAM Start Column High/Low + Device->WriteCommand( Device, 0x00 ); + Device->WriteCommand( Device, 0x10 ); + Device->WriteCommand( Device, 0xB0 ); // Display Offset Device->WriteCommand( Device, 0xD3 ); Device->WriteCommand( Device, 0 ); @@ -133,9 +141,6 @@ static bool Init( struct GDS_Device* Device ) { Device->SetHFlip( Device, false ); // no Display Inversion Device->WriteCommand( Device, 0xA6 ); - // set Clocks - Device->WriteCommand( Device, 0xD5 ); - Device->WriteCommand( Device, ( 0x08 << 4 ) | 0x00 ); // set Adressing Mode Horizontal Device->WriteCommand( Device, 0x20 ); Device->WriteCommand( Device, 0 ); diff --git a/components/display/SSD132x.c b/components/display/SSD132x.c index f7ed2b3b..e68a4d74 100644 --- a/components/display/SSD132x.c +++ b/components/display/SSD132x.c @@ -126,6 +126,7 @@ static void Update1( struct GDS_Device* Device ) { // not sure the compiler does not have to redo all calculation in for loops, so local it is int width = Device->Width / 8, rows = Device->Height; uint8_t *optr = Private->Shadowbuffer, *iptr = Device->Framebuffer; + int CurrentRow = -1, FirstCol = -1, LastCol = -1; // by row, find first and last columns that have been updated for (int r = 0; r < rows; r++) { @@ -140,9 +141,22 @@ static void Update1( struct GDS_Device* Device ) { // now update the display by "byte rows" if (first--) { - SetColumnAddress( Device, first, last ); - SetRowAddress( Device, r, r); - Device->WriteData( Device, Private->Shadowbuffer + r*width + first, last - first + 1); + // only set column when useful, saves a fair bit of CPU + if (first > FirstCol && first <= FirstCol + 4 && last < LastCol && last >= LastCol - 4) { + first = FirstCol; + last = LastCol; + } else { + SetColumnAddress( Device, first, last ); + FirstCol = first; + LastCol = last; + } + + // Set row only when needed, otherwise let auto-increment work + if (r != CurrentRow) SetRowAddress( Device, r, Device->Height - 1 ); + CurrentRow = r + 1; + + // actual write + Device->WriteData( Device, Private->Shadowbuffer + r*width + first, last - first + 1 ); } } #else diff --git a/components/display/display.c b/components/display/display.c index ddc79a0a..889a91c1 100644 --- a/components/display/display.c +++ b/components/display/display.c @@ -201,8 +201,8 @@ static void displayer_task(void *args) { displayer.tick = tick; displayer.elapsed += elapsed / 1000; xSemaphoreGive(displayer.mutex); - if (displayer.elapsed < 3600) sprintf(counter, "%5u:%02u", displayer.elapsed / 60, displayer.elapsed % 60); - else sprintf(counter, "%2u:%02u:%02u", displayer.elapsed / 3600, (displayer.elapsed % 3600) / 60, displayer.elapsed % 60); + if (displayer.elapsed < 3600) snprintf(counter, 16, "%5u:%02u", displayer.elapsed / 60, displayer.elapsed % 60); + else snprintf(counter, 16, "%2u:%02u:%02u", displayer.elapsed / 3600, (displayer.elapsed % 3600) / 60, displayer.elapsed % 60); GDS_TextLine(display, 1, GDS_TEXT_RIGHT, (GDS_TEXT_CLEAR | GDS_TEXT_CLEAR_EOL) | GDS_TEXT_UPDATE, counter); timer_sleep = 1000; } else timer_sleep = max(1000 - elapsed, 0); diff --git a/components/raop/raop.c b/components/raop/raop.c index ceed0aa0..a01303e1 100644 --- a/components/raop/raop.c +++ b/components/raop/raop.c @@ -508,7 +508,7 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock) pthread_create(&ctx->active_remote.thread, NULL, &search_remote, ctx); #else ctx->active_remote.running = true; - ctx->active_remote.destroy_mutex = xSemaphoreCreateMutex(); + ctx->active_remote.destroy_mutex = xSemaphoreCreateBinary(); ctx->active_remote.xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); ctx->active_remote.thread = xTaskCreateStatic( (TaskFunction_t) search_remote, "search_remote", SEARCH_STACK_SIZE, ctx, ESP_TASK_PRIO_MIN + 1, ctx->active_remote.xStack, ctx->active_remote.xTaskBuffer); #endif @@ -570,7 +570,10 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock) if ((p = strcasestr(buf, "rtptime")) != NULL) sscanf(p, "%*[^=]=%u", &rtptime); // only send FLUSH if useful (discards frames above buffer head and top) - if (ctx->rtp && rtp_flush(ctx->rtp, seqno, rtptime)) success = ctx->cmd_cb(RAOP_FLUSH); + if (ctx->rtp && rtp_flush(ctx->rtp, seqno, rtptime, true)) { + success = ctx->cmd_cb(RAOP_FLUSH); + rtp_flush_release(ctx->rtp); + } } else if (!strcmp(method, "TEARDOWN")) { diff --git a/components/raop/rtp.c b/components/raop/rtp.c index 0a3b6b3f..ff831884 100644 --- a/components/raop/rtp.c +++ b/components/raop/rtp.c @@ -267,25 +267,25 @@ rtp_resp_t rtp_init(struct in_addr host, int latency, char *aeskey, char *aesiv, resp.cport = ctx->rtp_sockets[CONTROL].lport; resp.tport = ctx->rtp_sockets[TIMING].lport; resp.aport = ctx->rtp_sockets[DATA].lport; - - if (rc) { - ctx->running = true; + + ctx->running = true; + #ifdef WIN32 - pthread_create(&ctx->thread, NULL, rtp_thread_func, (void *) ctx); + pthread_create(&ctx->thread, NULL, rtp_thread_func, (void *) ctx); #else - // xTaskCreate((TaskFunction_t) rtp_thread_func, "RTP_thread", RTP_TASK_SIZE, ctx, CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + 1 , &ctx->thread); - ctx->xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); - ctx->thread = xTaskCreateStatic( (TaskFunction_t) rtp_thread_func, "RTP_thread", RTP_STACK_SIZE, ctx, - CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + 1, ctx->xStack, ctx->xTaskBuffer ); + ctx->xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + ctx->thread = xTaskCreateStatic( (TaskFunction_t) rtp_thread_func, "RTP_thread", RTP_STACK_SIZE, ctx, + CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + 1, ctx->xStack, ctx->xTaskBuffer ); #endif - } else { + + // cleanup everything if we failed + if (!rc) { LOG_ERROR("[%p]: cannot start RTP", ctx); rtp_end(ctx); ctx = NULL; - } - - resp.ctx = ctx; - + } + + resp.ctx = ctx; return resp; } @@ -327,7 +327,7 @@ void rtp_end(rtp_t *ctx) } /*---------------------------------------------------------------------------*/ -bool rtp_flush(rtp_t *ctx, unsigned short seqno, unsigned int rtptime) +bool rtp_flush(rtp_t *ctx, unsigned short seqno, unsigned int rtptime, bool exit_locked) { bool rc = true; u32_t now = gettime_ms(); @@ -340,7 +340,7 @@ bool rtp_flush(rtp_t *ctx, unsigned short seqno, unsigned int rtptime) buffer_reset(ctx->audio_buffer); ctx->playing = false; ctx->flush_seqno = seqno; - pthread_mutex_unlock(&ctx->ab_mutex); + if (!exit_locked) pthread_mutex_unlock(&ctx->ab_mutex); } LOG_INFO("[%p]: flush %hu %u", ctx, seqno, rtptime); @@ -349,8 +349,13 @@ bool rtp_flush(rtp_t *ctx, unsigned short seqno, unsigned int rtptime) } /*---------------------------------------------------------------------------*/ -void rtp_record(rtp_t *ctx, unsigned short seqno, unsigned rtptime) -{ +void rtp_flush_release(rtp_t *ctx) { + pthread_mutex_unlock(&ctx->ab_mutex); +} + + +/*---------------------------------------------------------------------------*/ +void rtp_record(rtp_t *ctx, unsigned short seqno, unsigned rtptime) { ctx->record.seqno = seqno; ctx->record.rtptime = rtptime; ctx->record.time = gettime_ms(); @@ -576,7 +581,7 @@ static void *rtp_thread_func(void *arg) { while (ctx->running) { ssize_t plen; char type; - socklen_t rtp_client_len = sizeof(struct sockaddr_storage); + socklen_t rtp_client_len = sizeof(struct sockaddr_in); int idx = 0; char *pktp = packet; struct timeval timeout = {0, 100*1000}; @@ -589,14 +594,18 @@ static void *rtp_thread_func(void *arg) { for (i = 0; i < 3; i++) if (FD_ISSET(ctx->rtp_sockets[i].sock, &fds)) idx = i; - plen = recvfrom(ctx->rtp_sockets[idx].sock, packet, MAX_PACKET, 0, (struct sockaddr*) &ctx->rtp_host, &rtp_client_len); + plen = recvfrom(ctx->rtp_sockets[idx].sock, packet, MAX_PACKET, MSG_DONTWAIT, (struct sockaddr*) &ctx->rtp_host, &rtp_client_len); if (!ntp_sent) { LOG_WARN("[%p]: NTP request not send yet", ctx); ntp_sent = rtp_request_timing(ctx); } - if (plen < 0) continue; + if (plen <= 0) { + LOG_WARN("Nothing received on a readable socket %d", plen); + continue; + } + assert(plen <= MAX_PACKET); type = packet[1] & ~0x80; @@ -715,6 +724,11 @@ static void *rtp_thread_func(void *arg) { break; } + + default: { + LOG_WARN("Unknown packet received %x", (int) type); + break; + } } } @@ -756,7 +770,7 @@ static bool rtp_request_timing(rtp_t *ctx) { host.sin_port = htons(ctx->rtp_sockets[TIMING].rport); - if (sizeof(req) != sendto(ctx->rtp_sockets[TIMING].sock, req, sizeof(req), 0, (struct sockaddr*) &host, sizeof(host))) { + if (sizeof(req) != sendto(ctx->rtp_sockets[TIMING].sock, req, sizeof(req), MSG_DONTWAIT, (struct sockaddr*) &host, sizeof(host))) { LOG_WARN("[%p]: SENDTO failed (%s)", ctx, strerror(errno)); } @@ -782,7 +796,7 @@ static bool rtp_request_resend(rtp_t *ctx, seq_t first, seq_t last) { ctx->rtp_host.sin_port = htons(ctx->rtp_sockets[CONTROL].rport); - if (sizeof(req) != sendto(ctx->rtp_sockets[CONTROL].sock, req, sizeof(req), 0, (struct sockaddr*) &ctx->rtp_host, sizeof(ctx->rtp_host))) { + if (sizeof(req) != sendto(ctx->rtp_sockets[CONTROL].sock, req, sizeof(req), MSG_DONTWAIT, (struct sockaddr*) &ctx->rtp_host, sizeof(ctx->rtp_host))) { LOG_WARN("[%p]: SENDTO failed (%s)", ctx, strerror(errno)); } diff --git a/components/raop/rtp.h b/components/raop/rtp.h index 554690ad..ebf734f5 100644 --- a/components/raop/rtp.h +++ b/components/raop/rtp.h @@ -14,7 +14,8 @@ rtp_resp_t rtp_init(struct in_addr host, int latency, short unsigned pCtrlPort, short unsigned pTimingPort, raop_cmd_cb_t cmd_cb, raop_data_cb_t data_cb); void rtp_end(struct rtp_s *ctx); -bool rtp_flush(struct rtp_s *ctx, unsigned short seqno, unsigned rtptime); +bool rtp_flush(struct rtp_s *ctx, unsigned short seqno, unsigned rtptime, bool exit_locked); +void rtp_flush_release(struct rtp_s *ctx); void rtp_record(struct rtp_s *ctx, unsigned short seqno, unsigned rtptime); void rtp_metadata(struct rtp_s *ctx, struct metadata_s *metadata); diff --git a/components/raop/util.c b/components/raop/util.c index 2b7f3a17..13744409 100644 --- a/components/raop/util.c +++ b/components/raop/util.c @@ -401,7 +401,7 @@ bool http_parse(int sock, char *method, key_data_t *rkd, char **body, int *len) } if (*len) { - int size = 0; + int size = 0; *body = malloc(*len + 1); while (*body && size < *len) { diff --git a/components/services/monitor.c b/components/services/monitor.c index 4b56dbe5..1fb246f1 100644 --- a/components/services/monitor.c +++ b/components/services/monitor.c @@ -23,6 +23,7 @@ #include "accessors.h" #define MONITOR_TIMER (10*1000) +#define SCRATCH_SIZE 256 static const char *TAG = "monitor"; @@ -54,7 +55,7 @@ static void task_stats( void ) { current.tasks = malloc( current.n * sizeof( TaskStatus_t ) ); current.n = uxTaskGetSystemState( current.tasks, current.n, ¤t.total ); - static EXT_RAM_ATTR char scratch[128+1]; + static EXT_RAM_ATTR char scratch[SCRATCH_SIZE]; *scratch = '\0'; #ifdef CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS @@ -63,7 +64,8 @@ static void task_stats( void ) { for(int i = 0, n = 0; i < current.n; i++ ) { for (int j = 0; j < previous.n; j++) { if (current.tasks[i].xTaskNumber == previous.tasks[j].xTaskNumber) { - n += sprintf(scratch + n, "%16s %2u%% s:%5u", current.tasks[i].pcTaskName, + n += snprintf(scratch + n, SCRATCH_SIZE - n, "%16s (%u) %2u%% s:%5u", current.tasks[i].pcTaskName, + current.tasks[i].eCurrentState, 100 * (current.tasks[i].ulRunTimeCounter - previous.tasks[j].ulRunTimeCounter) / elapsed, current.tasks[i].usStackHighWaterMark); if (i % 3 == 2 || i == current.n - 1) { @@ -72,7 +74,7 @@ static void task_stats( void ) { } break; } - } + } } #else for (int i = 0, n = 0; i < current.n; i ++) { diff --git a/components/squeezelite/display.c b/components/squeezelite/display.c index 3923467f..544bdf13 100644 --- a/components/squeezelite/display.c +++ b/components/squeezelite/display.c @@ -100,7 +100,7 @@ static struct { #define SB_HEIGHT 32 // lenght are number of frames, i.e. 2 channels of 16 bits -#define FFT_LEN_BIT 6 +#define FFT_LEN_BIT 7 #define FFT_LEN (1 << FFT_LEN_BIT) #define RMS_LEN_BIT 6 #define RMS_LEN (1 << RMS_LEN_BIT) @@ -739,10 +739,10 @@ static void visu_update(void) { */ void spectrum_limits(int min, int n, int pos) { if (n / 2) { - int step = ((DISPLAY_BW - min) * visu.spectrum_scale * 2) / n; + int step = ((DISPLAY_BW - min) * visu.spectrum_scale) / (n/2); visu.bars[pos].limit = min + step; for (int i = 1; i < n/2; i++) visu.bars[pos+i].limit = visu.bars[pos+i-1].limit + step; - spectrum_limits(visu.bars[pos + n/2 - 1].limit, n/2, pos + n/2); + spectrum_limits(visu.bars[pos + n/2 - 1].limit, n - n/2, pos + n/2); } else { visu.bars[pos].limit = DISPLAY_BW; } diff --git a/components/squeezelite/embedded.h b/components/squeezelite/embedded.h index 90926dca..14769aef 100644 --- a/components/squeezelite/embedded.h +++ b/components/squeezelite/embedded.h @@ -6,6 +6,7 @@ /* must provide - mutex_create_p - pthread_create_name + - register_xxx (see below) - stack size - s16_t, s32_t, s64_t and u64_t - PLAYER_ID / custom_player_id @@ -16,6 +17,11 @@ - EXT_BSS recommended to add platform specific include(s) here */ + +typedef int16_t s16_t; +typedef int32_t s32_t; +typedef int64_t s64_t; +typedef unsigned long long u64_t; #ifndef PTHREAD_STACK_MIN #define PTHREAD_STACK_MIN 256 @@ -26,18 +32,17 @@ #define OUTPUT_THREAD_STACK_SIZE 6 * 1024 #define IR_THREAD_STACK_SIZE 6 * 1024 +// number of 5s times search for a server will happen beforee slimproto exits (0 = no limit) +#define MAX_SERVER_RETRIES 5 + // or can be as simple as #define PLAYER_ID 100 -#define PLAYER_ID custom_player_id; +#define PLAYER_ID custom_player_id extern u8_t custom_player_id; #define BASE_CAP "Model=squeezeesp32,AccuratePlayPoints=1,HasDigitalOut=1,HasPolarityInversion=1,Firmware=" VERSION +// to force some special buffer attribute #define EXT_BSS __attribute__((section(".ext_ram.bss"))) -typedef int16_t s16_t; -typedef int32_t s32_t; -typedef int64_t s64_t; -typedef unsigned long long u64_t; - // all exit() calls are made from main thread (or a function called in main thread) #define exit(code) { int ret = code; pthread_exit(&ret); } #define gettime_ms _gettime_ms_ diff --git a/components/squeezelite/slimproto.c b/components/squeezelite/slimproto.c index af7f7bde..3c55ec43 100644 --- a/components/squeezelite/slimproto.c +++ b/components/squeezelite/slimproto.c @@ -773,7 +773,7 @@ void wake_controller(void) { wake_signal(wake_e); } -in_addr_t discover_server(char *default_server) { +in_addr_t discover_server(char *default_server, int max) { struct sockaddr_in d; struct sockaddr_in s; char buf[32], port_d[] = "JSON", clip_d[] = "CLIP"; @@ -827,7 +827,7 @@ in_addr_t discover_server(char *default_server) { server_addr(default_server, &s.sin_addr.s_addr, &port); } - } while (s.sin_addr.s_addr == 0 && running); + } while (s.sin_addr.s_addr == 0 && running && (!max || --max)); closesocket(disc_sock); @@ -858,7 +858,7 @@ void slimproto(log_level level, char *server, u8_t mac[6], const char *name, con } if (!slimproto_ip) { - slimproto_ip = discover_server(server); + slimproto_ip = discover_server(server, 0); } if (!slimproto_port) { @@ -937,10 +937,18 @@ void slimproto(log_level level, char *server, u8_t mac[6], const char *name, con sleep(5); } - // rediscover server if it was not set at startup +#if EMBEDDED + // in embedded we give up after a while no matter what + if (++failed_connect > 5 && !server) { + slimproto_ip = serv_addr.sin_addr.s_addr = discover_server(NULL, MAX_SERVER_RETRIES); + if (!slimproto_ip) return; + } else if (MAX_SERVER_RETRIES && failed_connect > 5 * MAX_SERVER_RETRIES) return; +#else + // rediscover server if it was not set at startup or exit if (!server && ++failed_connect > 5) { - slimproto_ip = serv_addr.sin_addr.s_addr = discover_server(NULL); - } + slimproto_ip = serv_addr.sin_addr.s_addr = discover_server(NULL, 0); + } +#endif } else { diff --git a/main/cmd_squeezelite.c b/main/cmd_squeezelite.c index ae958166..0399ae2f 100644 --- a/main/cmd_squeezelite.c +++ b/main/cmd_squeezelite.c @@ -13,9 +13,8 @@ #include "freertos/event_groups.h" #include "pthread.h" #include "platform_esp32.h" -#include "nvs.h" -#include "nvs_flash.h" -//extern char current_namespace[]; +#include "config.h" + static const char * TAG = "squeezelite_cmd"; #define SQUEEZELITE_THREAD_STACK_SIZE (6*1024) extern int main(int argc, char **argv); @@ -36,7 +35,7 @@ static void * squeezelite_runner_thread(){ main(thread_parms.argc,thread_parms.argv); return NULL; } -#define ADDITIONAL_SQUEEZELILTE_ARGS 5 +#define ADDITIONAL_SQUEEZELITE_ARGS 5 static void * squeezelite_thread(){ int * exit_code; static bool isRunning=false; @@ -45,9 +44,6 @@ static void * squeezelite_thread(){ return NULL; } isRunning=true; -// Let's not wait on WiFi to allow squeezelite to run in bluetooth mode -// ESP_LOGI(TAG,"Waiting for WiFi."); -// while(!wait_for_wifi()){usleep(100000);}; ESP_LOGV(TAG ,"Number of args received: %u",thread_parms.argc ); ESP_LOGV(TAG ,"Values:"); for(int i = 0;i