Merge remote-tracking branch 'origin/master-v4.3' into master-v4.3

This commit is contained in:
Sebastien L
2022-01-20 13:45:36 -05:00
77 changed files with 823 additions and 320 deletions

View File

@@ -281,6 +281,19 @@ CONFIG_LED_GREEN_GPIO=-1
CONFIG_LED_RED_GPIO=-1
# end of LED configuration
#
# Audio controls
#
CONFIG_AUDIO_CONTROLS=""
# end of Audio Contorls configuration
#
# AMP configuration
#
CONFIG_AMP_GPIO=-1
# end of AMP configuration
#
# Audio JACK
#
@@ -296,7 +309,7 @@ CONFIG_SPKFAULT_GPIO=-1
#
# Battery measure
#
CONFIG_BAT_CHANNEL=-1
CONFIG_BAT_CONFIG=""
# end of Battery measure
CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info -C 30 -W"
@@ -533,7 +546,8 @@ CONFIG_ESP_TLS_USING_MBEDTLS=y
# CONFIG_ESP_TLS_USE_SECURE_ELEMENT is not set
# CONFIG_ESP_TLS_SERVER is not set
# CONFIG_ESP_TLS_PSK_VERIFICATION is not set
# CONFIG_ESP_TLS_INSECURE is not set
CONFIG_ESP_TLS_INSECURE=y
CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y
# end of ESP-TLS
#
@@ -727,7 +741,7 @@ CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y
#
# ESP HTTP client
#
# CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS is not set
CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y
# CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH is not set
# end of ESP HTTP client

View File

@@ -269,11 +269,22 @@ CONFIG_JACK_GPIO=34
CONFIG_JACK_GPIO_LEVEL=0
CONFIG_SPKFAULT_GPIO=2
CONFIG_SPKFAULT_GPIO_LEVEL=0
CONFIG_BAT_CHANNEL=7
CONFIG_BAT_SCALE="20.24"
CONFIG_BAT_CONFIG="channel=7,scale=20.24,atten=0"
CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info -C 30 -W"
# end of Squeezelite-ESP32
#
# Audio controls
#
CONFIG_AUDIO_CONTROLS=""
# end of Audio Contorls configuration
#
# AMP configuration
#
CONFIG_AMP_GPIO=-1
# end of AMP configuration
#
# Compiler options
#
@@ -505,7 +516,8 @@ CONFIG_ESP_TLS_USING_MBEDTLS=y
# CONFIG_ESP_TLS_USE_SECURE_ELEMENT is not set
# CONFIG_ESP_TLS_SERVER is not set
# CONFIG_ESP_TLS_PSK_VERIFICATION is not set
# CONFIG_ESP_TLS_INSECURE is not set
CONFIG_ESP_TLS_INSECURE=y
CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y
# end of ESP-TLS
#
@@ -699,7 +711,7 @@ CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y
#
# ESP HTTP client
#
# CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS is not set
CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y
# CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH is not set
# end of ESP HTTP client

View File

@@ -2,6 +2,7 @@ idf_component_register(SRC_DIRS . core core/ifaces fonts
INCLUDE_DIRS . fonts core
REQUIRES platform_config tools esp_common
PRIV_REQUIRES services freertos driver
EMBED_FILES note.jpg
)
set_source_files_properties(display.c

View File

@@ -86,7 +86,7 @@ static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
Device->WriteCommand( Device, Contrast );
}
static void SPIParams(int Speed, uint8_t *mode, uint8_t *CS_pre, uint8_t *CS_post) {
static void SPIParams(int Speed, uint8_t *mode, uint16_t *CS_pre, uint8_t *CS_post) {
*CS_post = Speed / (8*1000*1000);
}

View File

@@ -235,12 +235,13 @@ void GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
ledc_update_duty( LEDC_HIGH_SPEED_MODE, Device->Backlight.Channel );
}
}
void GDS_SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate ) { if (Device->SetLayout) Device->SetLayout( Device, HFlip, VFlip, Rotate ); }
void GDS_SetDirty( struct GDS_Device* Device ) { Device->Dirty = true; }
int GDS_GetWidth( struct GDS_Device* Device ) { return Device ? Device->Width : 0; }
int GDS_GetHeight( struct GDS_Device* Device ) { return Device ? Device->Height : 0; }
int GDS_GetDepth( struct GDS_Device* Device ) { return Device ? Device->Depth : 0; }
int GDS_GetMode( struct GDS_Device* Device ) { return Device ? Device->Mode : 0; }
int GDS_GetWidth( struct GDS_Device* Device ) { return Device ? Device->Width : 0; }
void GDS_SetTextWidth( struct GDS_Device* Device, int TextWidth ) { Device->TextWidth = Device && TextWidth && TextWidth < Device->Width ? TextWidth : Device->Width; }
int GDS_GetHeight( struct GDS_Device* Device ) { return Device ? Device->Height : 0; }
int GDS_GetDepth( struct GDS_Device* Device ) { return Device ? Device->Depth : 0; }
int GDS_GetMode( struct GDS_Device* Device ) { return Device ? Device->Mode : 0; }
void GDS_DisplayOn( struct GDS_Device* Device ) { if (Device->DisplayOn) Device->DisplayOn( Device ); }
void GDS_DisplayOff( struct GDS_Device* Device ) { if (Device->DisplayOff) Device->DisplayOff( Device ); }

View File

@@ -38,6 +38,7 @@ void GDS_Update( struct GDS_Device* Device );
void GDS_SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate );
void GDS_SetDirty( struct GDS_Device* Device );
int GDS_GetWidth( struct GDS_Device* Device );
void GDS_SetTextWidth( struct GDS_Device* Device, int TextWidth );
int GDS_GetHeight( struct GDS_Device* Device );
int GDS_GetDepth( struct GDS_Device* Device );
int GDS_GetMode( struct GDS_Device* Device );

View File

@@ -73,13 +73,13 @@ void GDS_FontDrawChar( struct GDS_Device* Device, char Character, int x, int y,
CharStartY+= OffsetY;
/* Do not attempt to draw if this character is entirely offscreen */
if ( CharEndX < 0 || CharStartX >= Device->Width || CharEndY < 0 || CharStartY >= Device->Height ) {
if ( CharEndX < 0 || CharStartX >= Device->TextWidth || CharEndY < 0 || CharStartY >= Device->Height ) {
ClipDebug( x, y );
return;
}
/* Do not attempt to draw past the end of the screen */
CharEndX = ( CharEndX >= Device->Width ) ? Device->Width - 1 : CharEndX;
CharEndX = ( CharEndX >= Device->TextWidth ) ? Device->TextWidth - 1 : CharEndX;
CharEndY = ( CharEndY >= Device->Height ) ? Device->Height - 1 : CharEndY;
Device->Dirty = true;
@@ -146,7 +146,7 @@ int GDS_FontGetCharWidth( struct GDS_Device* Display, char Character ) {
}
int GDS_FontGetMaxCharsPerRow( struct GDS_Device* Display ) {
return Display->Width / Display->Font->Width;
return Display->TextWidth / Display->Font->Width;
}
int GDS_FontGetMaxCharsPerColumn( struct GDS_Device* Display ) {
@@ -210,7 +210,7 @@ void GDS_FontGetAnchoredStringCoords( struct GDS_Device* Display, int* OutX, int
switch ( Anchor ) {
case TextAnchor_East: {
*OutY = ( Display->Height / 2 ) - ( StringHeight / 2 );
*OutX = ( Display->Width - StringWidth );
*OutX = ( Display->TextWidth - StringWidth );
break;
}
@@ -221,19 +221,19 @@ void GDS_FontGetAnchoredStringCoords( struct GDS_Device* Display, int* OutX, int
break;
}
case TextAnchor_North: {
*OutX = ( Display->Width / 2 ) - ( StringWidth / 2 );
*OutX = ( Display->TextWidth / 2 ) - ( StringWidth / 2 );
*OutY = 0;
break;
}
case TextAnchor_South: {
*OutX = ( Display->Width / 2 ) - ( StringWidth / 2 );
*OutX = ( Display->TextWidth / 2 ) - ( StringWidth / 2 );
*OutY = ( Display->Height - StringHeight );
break;
}
case TextAnchor_NorthEast: {
*OutX = ( Display->Width - StringWidth );
*OutX = ( Display->TextWidth - StringWidth );
*OutY = 0;
break;
@@ -246,7 +246,7 @@ void GDS_FontGetAnchoredStringCoords( struct GDS_Device* Display, int* OutX, int
}
case TextAnchor_SouthEast: {
*OutY = ( Display->Height - StringHeight );
*OutX = ( Display->Width - StringWidth );
*OutX = ( Display->TextWidth - StringWidth );
break;
}
@@ -258,7 +258,7 @@ void GDS_FontGetAnchoredStringCoords( struct GDS_Device* Display, int* OutX, int
}
case TextAnchor_Center: {
*OutY = ( Display->Height / 2 ) - ( StringHeight / 2 );
*OutX = ( Display->Width / 2 ) - ( StringWidth / 2 );
*OutX = ( Display->TextWidth / 2 ) - ( StringWidth / 2 );
break;
}

View File

@@ -142,7 +142,7 @@ static unsigned OutHandlerDirect(JDEC *Decoder, void *Bitmap, JRECT *Frame) {
JpegCtx *Context = (JpegCtx*) Decoder->device;
uint8_t *Pixels = (uint8_t*) Bitmap;
int Shift = 8 - Context->Depth;
// decoded image is RGB888, shift only make sense for grayscale
if (Context->Mode == GDS_RGB888) {
OUTHANDLERDIRECT(Scaler888, 0);
@@ -167,7 +167,7 @@ static unsigned OutHandlerDirect(JDEC *Decoder, void *Bitmap, JRECT *Frame) {
static void* DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scale, bool SizeOnly, int RGB_Mode) {
JDEC Decoder;
JpegCtx Context;
char *Scratch = calloc(SCRATCH_SIZE, 1);
char *Scratch = malloc(SCRATCH_SIZE);
if (!Scratch) {
ESP_LOGE(TAG, "Cannot allocate workspace");
@@ -372,7 +372,7 @@ bool GDS_DrawJPEG(struct GDS_Device* Device, uint8_t *Source, int x, int y, int
JDEC Decoder;
JpegCtx Context;
bool Ret = false;
char *Scratch = calloc(SCRATCH_SIZE, 1);
char *Scratch = malloc(SCRATCH_SIZE);
if (!Scratch) {
ESP_LOGE(TAG, "Cannot allocate workspace");

View File

@@ -95,7 +95,7 @@ struct GDS_Device {
const struct GDS_FontDef* Font;
} Lines[MAX_LINES];
uint16_t Width;
uint16_t Width, TextWidth;
uint16_t Height;
uint8_t Depth, Mode;
@@ -125,7 +125,7 @@ struct GDS_Device {
void (*DrawRGB)( struct GDS_Device* Device, uint8_t *Image,int x, int y, int Width, int Height, int RGB_Mode );
void (*ClearWindow)( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color );
// may provide for tweaking
void (*SPIParams)(int Speed, uint8_t *mode, uint8_t *CS_pre, uint8_t *CS_post);
void (*SPIParams)(int Speed, uint8_t *mode, uint16_t *CS_pre, uint8_t *CS_post);
// interface-specific methods
WriteCommandProc WriteCommand;

View File

@@ -100,13 +100,13 @@ bool GDS_TextLine(struct GDS_Device* Device, int N, int Pos, int Attr, char *Tex
Width = GDS_FontMeasureString( Device, Text );
// adjusting position, erase only EoL for rigth-justified
if (Pos == GDS_TEXT_RIGHT) X = Device->Width - Width - 1;
else if (Pos == GDS_TEXT_CENTER) X = (Device->Width - Width) / 2;
if (Pos == GDS_TEXT_RIGHT) X = Device->TextWidth - Width - 1;
else if (Pos == GDS_TEXT_CENTER) X = (Device->TextWidth - Width) / 2;
// erase if requested
if (Attr & GDS_TEXT_CLEAR) {
int Y_min = max(0, Device->Lines[N].Y), Y_max = max(0, Device->Lines[N].Y + Device->Lines[N].Font->Height);
for (int c = (Attr & GDS_TEXT_CLEAR_EOL) ? X : 0; c < Device->Width; c++)
for (int c = (Attr & GDS_TEXT_CLEAR_EOL) ? X : 0; c < Device->TextWidth; c++)
for (int y = Y_min; y < Y_max; y++)
DrawPixelFast( Device, c, y, GDS_COLOR_BLACK );
}
@@ -119,7 +119,7 @@ bool GDS_TextLine(struct GDS_Device* Device, int N, int Pos, int Attr, char *Tex
Device->Dirty = true;
if (Attr & GDS_TEXT_UPDATE) GDS_Update( Device );
return Width + X < Device->Width;
return Width + X < Device->TextWidth;
}
/****************************************************************************************
@@ -146,7 +146,7 @@ int GDS_TextStretch(struct GDS_Device* Device, int N, char *String, int Max) {
// we might already fit
GDS_SetFont( Device, Device->Lines[N].Font );
if (GDS_FontMeasureString( Device, String ) <= Device->Width) return 0;
if (GDS_FontMeasureString( Device, String ) <= Device->TextWidth) return 0;
// add some space for better visual
strncat(String, Space, Max-Len);
@@ -157,7 +157,7 @@ int GDS_TextStretch(struct GDS_Device* Device, int N, char *String, int Max) {
Boundary = GDS_FontMeasureString( Device, String );
// add a full display width
while (Len < Max && GDS_FontMeasureString( Device, String ) - Boundary < Device->Width) {
while (Len < Max && GDS_FontMeasureString( Device, String ) - Boundary < Device->TextWidth) {
String[Len++] = String[Extra++];
String[Len] = '\0';
}

View File

@@ -75,7 +75,7 @@ bool GDS_I2CAttachDevice( struct GDS_Device* Device, int Width, int Height, int
Device->RSTPin = RSTPin;
Device->Backlight.Pin = BacklightPin;
Device->IF = GDS_IF_I2C;
Device->Width = Width;
Device->Width = Device->TextWidth = Width;
Device->Height = Height;
if ( RSTPin >= 0 ) {

View File

@@ -35,7 +35,7 @@ bool GDS_SPIInit( int SPI, int DC ) {
}
bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int CSPin, int RSTPin, int BackLightPin, int Speed ) {
spi_device_interface_config_t SPIDeviceConfig;
spi_device_interface_config_t SPIDeviceConfig = { };
spi_device_handle_t SPIDevice;
NullCheck( Device, return false );
@@ -45,8 +45,6 @@ bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( CSPin, 0 ), return false );
}
memset( &SPIDeviceConfig, 0, sizeof( spi_device_interface_config_t ) );
SPIDeviceConfig.clock_speed_hz = Speed > 0 ? Speed : SPI_MASTER_FREQ_8M;
SPIDeviceConfig.spics_io_num = CSPin;
SPIDeviceConfig.queue_size = 1;
@@ -63,7 +61,7 @@ bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int
Device->CSPin = CSPin;
Device->Backlight.Pin = BackLightPin;
Device->IF = GDS_IF_SPI;
Device->Width = Width;
Device->Width = Device->TextWidth = Width;
Device->Height = Height;
if ( RSTPin >= 0 ) {

View File

@@ -20,6 +20,7 @@
#include "gds_draw.h"
#include "gds_text.h"
#include "gds_font.h"
#include "gds_image.h"
static const char *TAG = "display";
@@ -30,6 +31,9 @@ static const char *TAG = "display";
#define SCROLLABLE_SIZE 384
#define HEADER_SIZE 64
#define DEFAULT_SLEEP 3600
#define ARTWORK_BORDER 1
extern const uint8_t default_artwork[] asm("_binary_note_jpg_start");
static EXT_RAM_ATTR struct {
TaskHandle_t task;
@@ -47,6 +51,13 @@ static EXT_RAM_ATTR struct {
char string[8]; // H:MM:SS
bool visible;
} duration;
struct {
bool enable, active;
bool fit;
bool updated;
int tick;
int offset;
} artwork;
TickType_t tick;
} displayer;
@@ -147,6 +158,14 @@ void display_init(char *welcome) {
GDS_TextSetFontAuto(display, 2, GDS_FONT_LINE_2, -3);
displayer.metadata_config = config_alloc_get(NVS_TYPE_STR, "metadata_config");
// leave room for artwork is display is horizontal-style
if (strcasestr(displayer.metadata_config, "artwork")) {
displayer.artwork.enable = true;
displayer.artwork.fit = true;
if (height <= 64 && width > height * 2) displayer.artwork.offset = width - height - ARTWORK_BORDER;
PARSE_PARAM(displayer.metadata_config, "artwork", ':', displayer.artwork.fit);
}
}
free(config);
@@ -225,7 +244,12 @@ static void displayer_task(void *args) {
// just re-write the whole line it's easier
GDS_TextLine(display, 1, GDS_TEXT_LEFT, GDS_TEXT_CLEAR, displayer.header);
GDS_TextLine(display, 1, GDS_TEXT_RIGHT, GDS_TEXT_UPDATE, _line);
// if we have not received artwork after 5s, display a default icon
if (displayer.artwork.active && !displayer.artwork.updated && tick - displayer.artwork.tick > pdMS_TO_TICKS(5000)) {
ESP_LOGI(TAG, "no artwork received, setting default");
displayer_artwork((uint8_t*) default_artwork);
}
timer_sleep = 1000;
} else timer_sleep = max(1000 - elapsed, 0);
} else timer_sleep = DEFAULT_SLEEP;
@@ -238,6 +262,32 @@ static void displayer_task(void *args) {
}
}
/****************************************************************************************
*
*/
void displayer_artwork(uint8_t *data) {
if (!displayer.artwork.active) return;
int x = displayer.artwork.offset ? displayer.artwork.offset + ARTWORK_BORDER : 0;
int y = x ? 0 : 32;
GDS_ClearWindow(display, x, y, -1, -1, GDS_COLOR_BLACK);
if (data) {
displayer.artwork.updated = true;
GDS_DrawJPEG(display, data, x, y, GDS_IMAGE_CENTER | (displayer.artwork.fit ? GDS_IMAGE_FIT : 0));
} else {
displayer.artwork.updated = false;
displayer.artwork.tick = xTaskGetTickCount();
}
}
/****************************************************************************************
*
*/
bool displayer_can_artwork(void) {
return displayer.artwork.active;
}
/****************************************************************************************
*
*/
@@ -378,6 +428,7 @@ void displayer_control(enum displayer_cmd_e cmd, ...) {
switch(cmd) {
case DISPLAYER_ACTIVATE: {
char *header = va_arg(args, char*);
displayer.artwork.active = displayer.artwork.enable && va_arg(args, int);
strncpy(displayer.header, header, HEADER_SIZE);
displayer.header[HEADER_SIZE] = '\0';
displayer.state = DISPLAYER_ACTIVE;
@@ -388,16 +439,20 @@ void displayer_control(enum displayer_cmd_e cmd, ...) {
displayer.duration.visible = false;
displayer.offset = displayer.boundary = 0;
display_bus(&displayer, DISPLAY_BUS_TAKE);
if (displayer.artwork.active) GDS_SetTextWidth(display, displayer.artwork.offset);
vTaskResume(displayer.task);
break;
}
case DISPLAYER_SUSPEND:
// task will display the line 2 from beginning and suspend
displayer.state = DISPLAYER_IDLE;
displayer_artwork(NULL);
display_bus(&displayer, DISPLAY_BUS_GIVE);
break;
case DISPLAYER_SHUTDOWN:
// let the task self-suspend (we might be doing i2c_write)
GDS_SetTextWidth(display, 0);
displayer_artwork(NULL);
displayer.state = DISPLAYER_DOWN;
display_bus(&displayer, DISPLAY_BUS_GIVE);
break;

View File

@@ -38,5 +38,7 @@ bool display_is_valid_driver(const char * driver);
void displayer_scroll(char *string, int speed, int pause);
void displayer_control(enum displayer_cmd_e cmd, ...);
void displayer_metadata(char *artist, char *album, char *title);
void displayer_artwork(uint8_t *data);
void displayer_timer(enum displayer_time_e mode, int elapsed, int duration);
bool displayer_can_artwork(void);
char * display_get_supported_drivers(void);

BIN
components/display/note.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -172,7 +172,7 @@ static bool cmd_handler(bt_sink_cmd_t cmd, ...) {
// now handle events for display
switch(cmd) {
case BT_SINK_AUDIO_STARTED:
displayer_control(DISPLAYER_ACTIVATE, "BLUETOOTH");
displayer_control(DISPLAYER_ACTIVATE, "BLUETOOTH", false);
break;
case BT_SINK_AUDIO_STOPPED:
displayer_control(DISPLAYER_SUSPEND);

View File

@@ -14,6 +14,11 @@ extern "C" {
if ((__p = strcasestr(S, P)) && (__p = strchr(__p, C))) V = atoi(__p+1); \
} while (0)
#define PARSE_PARAM_FLOAT(S,P,C,V) do { \
char *__p; \
if ((__p = strcasestr(S, P)) && (__p = strchr(__p, C))) V = atof(__p+1); \
} while (0)
#define PARSE_PARAM_STR(S,P,C,V,I) do { \
char *__p; \
if ((__p = strstr(S, P)) && (__p = strchr(__p, C))) { \

View File

@@ -11,3 +11,9 @@ idf_component_register( SRCS
PRIV_REQUIRES console app_update tools services spi_flash platform_config vfs pthread wifi-manager platform_config newlib telnet display squeezelite tools)
target_link_libraries(${COMPONENT_LIB} "-Wl,--undefined=GDS_DrawPixelFast")
target_link_libraries(${COMPONENT_LIB} ${build_dir}/esp-idf/$<TARGET_PROPERTY:RECOVERY_PREFIX>/lib$<TARGET_PROPERTY:RECOVERY_PREFIX>.a )
set_source_files_properties(cmd_config.c
PROPERTIES COMPILE_FLAGS
-Wno-unused-function
)

View File

@@ -283,17 +283,17 @@ static int stdin_dummy(const char * path, int flags, int mode) { return 0; }
void initialize_console() {
/* Minicom, screen, idf_monitor send CR when ENTER key is pressed (unused if we redirect stdin) */
esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
esp_vfs_dev_uart_port_set_rx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CR);
/* Move the caret to the beginning of the next line on '\n' */
esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
esp_vfs_dev_uart_port_set_tx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CRLF);
/* Configure UART. Note that REF_TICK is used so that the baud rate remains
* correct while APB frequency is changing in light sleep mode.
*/
const uart_config_t uart_config = { .baud_rate =
CONFIG_ESP_CONSOLE_UART_BAUDRATE, .data_bits = UART_DATA_8_BITS,
const uart_config_t uart_config = { .baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1,
.use_ref_tick = true };
};
ESP_ERROR_CHECK(uart_param_config(CONFIG_ESP_CONSOLE_UART_NUM, &uart_config));
/* Install UART driver for interrupt-driven reads and writes */

View File

@@ -628,6 +628,9 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
LOG_INFO("[%p]: received metadata", ctx);
settings.ctx = &metadata;
memset(&metadata, 0, sizeof(struct metadata_s));
if (!dmap_parse(&settings, body, len)) {
LOG_INFO("[%p]: received metadata\n\tartist: %s\n\talbum: %s\n\ttitle: %s",
ctx, metadata.artist, metadata.album, metadata.title);
success = ctx->cmd_cb(RAOP_METADATA, metadata.artist, metadata.album, metadata.title);
free_metadata(&metadata);
}

View File

@@ -112,7 +112,7 @@ static bool cmd_handler(raop_event_t event, ...) {
switch(event) {
case RAOP_SETUP:
actrls_set(controls, false, NULL, actrls_ir_action);
displayer_control(DISPLAYER_ACTIVATE, "AIRPLAY");
displayer_control(DISPLAYER_ACTIVATE, "AIRPLAY", true);
break;
case RAOP_PLAY:
displayer_control(DISPLAYER_TIMER_RUN);
@@ -127,8 +127,14 @@ static bool cmd_handler(raop_event_t event, ...) {
case RAOP_METADATA: {
char *artist = va_arg(args, char*), *album = va_arg(args, char*), *title = va_arg(args, char*);
displayer_metadata(artist, album, title);
displayer_artwork(NULL);
break;
}
case RAOP_ARTWORK: {
uint8_t *data = va_arg(args, uint8_t*);
displayer_artwork(data);
break;
}
case RAOP_PROGRESS: {
int elapsed = va_arg(args, int), duration = va_arg(args, int);
displayer_timer(DISPLAYER_ELAPSED, elapsed, duration);

View File

@@ -14,7 +14,7 @@
#define RAOP_SAMPLE_RATE 44100
typedef enum { RAOP_SETUP, RAOP_STREAM, RAOP_PLAY, RAOP_FLUSH, RAOP_METADATA, RAOP_PROGRESS, RAOP_PAUSE, RAOP_STOP,
typedef enum { RAOP_SETUP, RAOP_STREAM, RAOP_PLAY, RAOP_FLUSH, RAOP_METADATA, RAOP_ARTWORK, RAOP_PROGRESS, RAOP_PAUSE, RAOP_STOP,
RAOP_VOLUME, RAOP_TIMING, RAOP_PREV, RAOP_NEXT, RAOP_REW, RAOP_FWD,
RAOP_VOLUME_UP, RAOP_VOLUME_DOWN, RAOP_RESUME, RAOP_TOGGLE } raop_event_t ;

View File

@@ -1109,7 +1109,6 @@ cJSON * get_gpio_list(bool refresh) {
}
gpio_list= cJSON_CreateArray();
#ifndef CONFIG_BAT_LOCKED
char *bat_config = config_alloc_get_default(NVS_TYPE_STR, "bat_config", NULL, 0);
if (bat_config) {
int channel = -1;
@@ -1121,11 +1120,6 @@ cJSON * get_gpio_list(bool refresh) {
}
free(bat_config);
}
#else
if(adc1_pad_get_io_num(CONFIG_BAT_CHANNEL,&gpio_num )==ESP_OK){
cJSON_AddItemToArray(gpio_list,get_gpio_entry("bat","other",gpio_num,true));
}
#endif
gpio_list=get_GPIO_nvs_list(gpio_list);
gpio_list=get_SPDIF_GPIO(gpio_list,is_spdif_config_locked());
gpio_list=get_Rotary_GPIO(gpio_list);

View File

@@ -35,12 +35,13 @@ static struct {
int count;
int cells, attenuation;
TimerHandle_t timer;
} battery = {
.channel = CONFIG_BAT_CHANNEL,
} battery = {
.channel = -1,
.cells = 2,
.attenuation = ADC_ATTEN_DB_0,
};
void (*battery_handler_svc)(float value);
/****************************************************************************************
*
*/
@@ -65,6 +66,7 @@ static void battery_callback(TimerHandle_t xTimer) {
if (++battery.count == 30) {
battery.avg = battery.sum / battery.count;
battery.sum = battery.count = 0;
if (battery_handler_svc) (battery_handler_svc)(battery.avg);
ESP_LOGI(TAG, "Voltage %.2fV", battery.avg);
}
}
@@ -73,17 +75,18 @@ static void battery_callback(TimerHandle_t xTimer) {
*
*/
void battery_svc_init(void) {
#ifdef CONFIG_BAT_SCALE
battery.scale = atof(CONFIG_BAT_SCALE);
#endif
char *nvs_item = config_alloc_get_default(NVS_TYPE_STR, "bat_config", "n", 0);
if (nvs_item) {
#ifndef CONFIG_BAT_LOCKED
PARSE_PARAM(nvs_item, "channel", '=', battery.channel);
PARSE_PARAM(nvs_item, "scale", '=', battery.scale);
PARSE_PARAM(nvs_item, "atten", '=', battery.attenuation);
char *nvs_item = config_alloc_get_default(NVS_TYPE_STR, "bat_config", "", 0);
#ifdef CONFIG_BAT_LOCKED
char *p = nvs_item;
asprintf(&nvs_item, CONFIG_BAT_CONFIG ",%s", p);
free(p);
#endif
if (nvs_item) {
PARSE_PARAM(nvs_item, "channel", '=', battery.channel);
PARSE_PARAM_FLOAT(nvs_item, "scale", '=', battery.scale);
PARSE_PARAM(nvs_item, "atten", '=', battery.attenuation);
PARSE_PARAM(nvs_item, "cells", '=', battery.cells);
free(nvs_item);
}
@@ -96,7 +99,7 @@ void battery_svc_init(void) {
battery.timer = xTimerCreate("battery", BATTERY_TIMER / portTICK_RATE_MS, pdTRUE, NULL, battery_callback);
xTimerStart(battery.timer, portMAX_DELAY);
ESP_LOGI(TAG, "Battery measure channel: %u, scale %f, cells %u, avg %.2fV", battery.channel, battery.scale, battery.cells, battery.avg);
ESP_LOGI(TAG, "Battery measure channel: %u, scale %f, atten %d, cells %u, avg %.2fV", battery.channel, battery.scale, battery.attenuation, battery.cells, battery.avg);
} else {
ESP_LOGI(TAG, "No battery");
}

View File

@@ -20,6 +20,7 @@ extern bool jack_inserted_svc(void);
extern void (*spkfault_handler_svc)(bool inserted);
extern bool spkfault_svc(void);
extern void (*battery_handler_svc)(float value);
extern float battery_value_svc(void);
extern uint16_t battery_level_svc(void);

View File

@@ -119,8 +119,8 @@ static void cspotTask(void *pvParameters) {
switch (event.eventType) {
case CSpotEventType::TRACK_INFO: {
TrackInfo track = std::get<TrackInfo>(event.data);
// duration is in chunks of 0.5 ms
cspot.cHandler(CSPOT_TRACK, 44100, track.duration / 2, track.artist.c_str(), track.album.c_str(), track.name.c_str());
cspot.cHandler(CSPOT_TRACK, 44100, track.duration, track.artist.c_str(),
track.album.c_str(), track.name.c_str(), track.imageUrl.c_str());
break;
}
case CSpotEventType::PLAY_PAUSE: {
@@ -192,7 +192,7 @@ struct cspot_s* cspot_create(const char *name, cspot_cmd_cb_t cmd_cb, cspot_data
cspot.cHandler = cmd_cb;
cspot.dHandler = data_cb;
strncpy(cspot.name, name, sizeof(cspot.name) - 1);
cspot.TaskHandle = xTaskCreateStatic(&cspotTask, "cspot", CSPOT_STACK_SIZE, NULL, CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT, xStack, &xTaskBuffer);
cspot.TaskHandle = xTaskCreateStatic(&cspotTask, "cspot", CSPOT_STACK_SIZE, NULL, CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT - 2, xStack, &xTaskBuffer);
return &cspot;
}

View File

@@ -45,5 +45,6 @@ endif()
add_library(cspot STATIC ${SOURCES} ${PROTO_SRCS})
# PUBLIC to propagate includes from bell to cspot dependents
target_compile_definitions(bell PUBLIC PB_ENABLE_MALLOC)
target_compile_definitions(bell PUBLIC PB_FIELD_32BIT)
target_link_libraries(cspot PUBLIC ${EXTRA_LIBS})
target_include_directories(cspot PUBLIC "include" ${GENERATED_INCLUDES} ${NANOPB_INCLUDE_DIRS})

View File

@@ -7,8 +7,6 @@ option(BELL_DISABLE_CODECS "Disable libhelix AAC and MP3 codecs" OFF)
#set(BELL_EXTERNAL_CJSON "" CACHE STRING "External cJSON library target name, optional")
#set(BELL_EXTERNAL_TREMOR "" CACHE STRING "External tremor library target name, optional")
add_definitions(-DPB_ENABLE_MALLOC)
# Include nanoPB library
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/nanopb/extra)
find_package(Nanopb REQUIRED)
@@ -98,3 +96,4 @@ message(${NANOPB_INCLUDE_DIRS})
# PUBLIC to propagate esp-idf includes to bell dependents
target_link_libraries(bell PUBLIC ${EXTRA_LIBS})
target_include_directories(bell PUBLIC "include" ${EXTRA_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR})
target_compile_definitions(bell PUBLIC PB_ENABLE_MALLOC)

View File

@@ -56,7 +56,7 @@ public:
std::vector<uint8_t> sha1HMAC(const std::vector<uint8_t>& inputKey, const std::vector<uint8_t>& message);
// AES CTR
void aesCTRXcrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& iv, std::vector<uint8_t>& data);
void aesCTRXcrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& iv, uint8_t* data, size_t nbytes);
// AES ECB
void aesECBdecrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& data);

View File

@@ -63,7 +63,7 @@ public:
std::vector<uint8_t> sha1HMAC(const std::vector<uint8_t>& inputKey, const std::vector<uint8_t>& message);
// AES CTR
void aesCTRXcrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& iv, std::vector<uint8_t>& data);
void aesCTRXcrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& iv, uint8_t* buffer, size_t nbytes);
// AES ECB
void aesECBdecrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& data);

View File

@@ -42,6 +42,10 @@ void pbDecode(T &result, const pb_msgdesc_t *fields, std::vector<uint8_t> &data)
}
}
void pbPutString(const std::string &stringToPack, char* dst);
void pbPutCharArray(const char * stringToPack, char* dst);
void pbPutBytes(const std::vector<uint8_t> &data, pb_bytes_array_t &dst);
const char* pb_encode_to_string(const pb_msgdesc_t *fields, const void *data);
pb_istream_t pb_istream_from_http(bell::HTTPClient::HTTPResponse *response, size_t length = 0);

View File

@@ -29,7 +29,7 @@ namespace bell
#ifdef ESP_PLATFORM
this->xStack = NULL;
this->priority = CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + priority;
if (this->priority < 0) this->priority = ESP_TASK_PRIO_MIN;
if (this->priority <= ESP_TASK_PRIO_MIN) this->priority = ESP_TASK_PRIO_MIN + 1;
if (runOnPSRAM) {
this->xStack = (StackType_t*) heap_caps_malloc(this->stackSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
}

View File

@@ -88,7 +88,7 @@ std::vector<uint8_t> CryptoMbedTLS::sha1HMAC(const std::vector<uint8_t> &inputKe
}
// AES CTR
void CryptoMbedTLS::aesCTRXcrypt(const std::vector<uint8_t> &key, std::vector<uint8_t> &iv, std::vector<uint8_t> &data)
void CryptoMbedTLS::aesCTRXcrypt(const std::vector<uint8_t> &key, std::vector<uint8_t> &iv, uint8_t* buffer, size_t nbytes)
{
// needed for internal cache
size_t off = 0;
@@ -99,12 +99,12 @@ void CryptoMbedTLS::aesCTRXcrypt(const std::vector<uint8_t> &key, std::vector<ui
// Perform decrypt
mbedtls_aes_crypt_ctr(&aesCtx,
data.size(),
nbytes,
&off,
iv.data(),
streamBlock,
data.data(),
data.data());
buffer,
buffer);
}
void CryptoMbedTLS::aesECBdecrypt(const std::vector<uint8_t> &key, std::vector<uint8_t> &data)
@@ -115,7 +115,7 @@ void CryptoMbedTLS::aesECBdecrypt(const std::vector<uint8_t> &key, std::vector<u
// Mbedtls's decrypt only works on 16 byte blocks
for (unsigned int x = 0; x < data.size() / 16; x++)
{
// Perform decrypt
// Perform finalize
mbedtls_aes_crypt_ecb(&aesCtx,
MBEDTLS_AES_DECRYPT,
data.data() + (x * 16),

View File

@@ -99,7 +99,7 @@ std::vector<uint8_t> CryptoOpenSSL::sha1HMAC(const std::vector<uint8_t>& inputKe
}
// AES CTR
void CryptoOpenSSL::aesCTRXcrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& iv, std::vector<uint8_t> &data)
void CryptoOpenSSL::aesCTRXcrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& iv, uint8_t* buffer, size_t nbytes)
{
// Prepare AES_KEY
auto cryptoKey = AES_KEY();
@@ -110,9 +110,9 @@ void CryptoOpenSSL::aesCTRXcrypt(const std::vector<uint8_t>& key, std::vector<ui
unsigned int offsetInBlock = 0;
CRYPTO_ctr128_encrypt(
data.data(),
data.data(),
data.size(),
buffer,
buffer,
nbytes,
&cryptoKey,
iv.data(),
ecountBuf,

View File

@@ -287,7 +287,7 @@ std::string HTTPClient::HTTPResponse::readToString() {
return result;
}
std::string result;
char buffer[BUF_SIZE];
char buffer[BUF_SIZE+1]; // make space for null-terminator
size_t len;
do {
len = this->read(buffer, BUF_SIZE);

View File

@@ -47,6 +47,22 @@ pb_bytes_array_t* vectorToPbArray(const std::vector<uint8_t>& vectorToPack)
return result;
}
void pbPutString(const std::string &stringToPack, char* dst) {
stringToPack.copy(dst, stringToPack.size());
dst[stringToPack.size()] = '\0';
}
void pbPutCharArray(const char * stringToPack, char* dst) {
// copy stringToPack into dst
strcpy(dst, stringToPack);
//dst[sizeof(stringToPack)-1] = '\0';
}
void pbPutBytes(const std::vector<uint8_t> &data, pb_bytes_array_t &dst) {
dst.size = data.size();
std::copy(data.begin(), data.end(), dst.bytes);
}
std::vector<uint8_t> pbArrayToVector(pb_bytes_array_t* pbArray) {
return std::vector<uint8_t>(pbArray->bytes, pbArray->bytes + pbArray->size);
}

View File

@@ -14,12 +14,15 @@ class AudioChunk {
private:
/**
* @brief Calculates a correct IV by performing bignum addition.
*
*
* @param num Number to add to IV.
* @return std::vector<uint8_t>
* @return std::vector<uint8_t>
*/
std::vector<uint8_t> getIVSum(uint32_t num);
size_t decryptedCount = 0;
size_t oldStartPos;
public:
std::unique_ptr<Crypto> crypto;
std::vector<uint8_t> decryptedData;
@@ -41,14 +44,21 @@ public:
std::unique_ptr<WrappedSemaphore> isLoadedSemaphore;
/**
* @brief
* @brief
*/
std::unique_ptr<WrappedSemaphore> isHeaderFileSizeLoadedSemaphore;
/**
* Decrypts data and writes it to the target buffer
* @param target data buffer to write to
* @param offset data offset
* @param nbytes number of bytes to read
*/
void readData(uint8_t *target, size_t offset, size_t nbytes);
/**
* @brief AudioChunk handles all audiochunk related operations.
*
*
* @param seqId Sequence id of requested chunk
* @param audioKey Audio key used for decryption of audio data
* @param startPosition Start position of current chunk in audio file
@@ -59,16 +69,16 @@ public:
/**
* @brief Appends incoming chunked data to local cache.
*
*
* @param data encrypted binary audio data.
*/
void appendData(const std::vector<uint8_t> &data);
/**
* @brief Performs AES CTR decryption of received data.
*
* @brief Sets loaded status on the chunk
*
*/
void decrypt();
void finalize();
};
#endif

View File

@@ -9,7 +9,7 @@ class Packet
private:
public:
Packet(uint8_t command, std::vector<uint8_t> &data);
Packet(uint8_t command, const std::vector<uint8_t> &data);
uint8_t command;
std::vector<uint8_t> data;
};

View File

@@ -13,7 +13,7 @@ public:
void stream(std::vector<uint8_t> &buf); /* stream cipher */
void maconly(std::vector<uint8_t> &buf); /* accumulate MAC */
void encrypt(std::vector<uint8_t> &buf); /* encrypt + MAC */
void decrypt(std::vector<uint8_t> &buf); /* decrypt + MAC */
void decrypt(std::vector<uint8_t> &buf); /* finalize + MAC */
void finish(std::vector<uint8_t> &buf); /* finalise MAC */
private:

View File

@@ -37,6 +37,7 @@ class SpircController {
private:
std::shared_ptr<MercuryManager> manager;
std::string username;
bool firstFrame = true;
std::unique_ptr<Player> player;
std::unique_ptr<PlayerState> state;
std::shared_ptr<AudioSink> audioSink;

View File

@@ -1,6 +1,5 @@
LoginCredentials.username type:FT_POINTER
LoginCredentials.auth_data type:FT_POINTER
LoginCredentials.auth_data type:FT_POINTER
SystemInfo.system_information_string type:FT_POINTER
SystemInfo.device_id type:FT_POINTER
ClientResponseEncrypted.version_string type:FT_POINTER
LoginCredentials.username max_size:30, fixed_length:false
LoginCredentials.auth_data max_size:512, fixed_length:false
SystemInfo.system_information_string max_size:16, fixed_length:false
SystemInfo.device_id max_size:50, fixed_length:false
ClientResponseEncrypted.version_string max_size:32, fixed_length:false

View File

@@ -9,7 +9,7 @@
PB_BIND(SystemInfo, SystemInfo, 2)
PB_BIND(LoginCredentials, LoginCredentials, AUTO)
PB_BIND(LoginCredentials, LoginCredentials, 2)
PB_BIND(ClientResponseEncrypted, ClientResponseEncrypted, 2)

View File

@@ -58,23 +58,25 @@ typedef enum _AuthenticationType {
} AuthenticationType;
/* Struct definitions */
typedef PB_BYTES_ARRAY_T(512) LoginCredentials_auth_data_t;
typedef struct _LoginCredentials {
char *username;
char username[30];
AuthenticationType typ;
pb_bytes_array_t *auth_data;
LoginCredentials_auth_data_t auth_data;
} LoginCredentials;
typedef struct _SystemInfo {
CpuFamily cpu_family;
Os os;
char *system_information_string;
char *device_id;
char system_information_string[16];
char device_id[50];
} SystemInfo;
typedef struct _ClientResponseEncrypted {
LoginCredentials login_credentials;
SystemInfo system_info;
char *version_string;
bool has_version_string;
char version_string[32];
} ClientResponseEncrypted;
@@ -97,12 +99,12 @@ extern "C" {
#endif
/* Initializer values for message structs */
#define SystemInfo_init_default {_CpuFamily_MIN, _Os_MIN, NULL, NULL}
#define LoginCredentials_init_default {NULL, _AuthenticationType_MIN, NULL}
#define ClientResponseEncrypted_init_default {LoginCredentials_init_default, SystemInfo_init_default, NULL}
#define SystemInfo_init_zero {_CpuFamily_MIN, _Os_MIN, NULL, NULL}
#define LoginCredentials_init_zero {NULL, _AuthenticationType_MIN, NULL}
#define ClientResponseEncrypted_init_zero {LoginCredentials_init_zero, SystemInfo_init_zero, NULL}
#define SystemInfo_init_default {_CpuFamily_MIN, _Os_MIN, "", ""}
#define LoginCredentials_init_default {"", _AuthenticationType_MIN, {0, {0}}}
#define ClientResponseEncrypted_init_default {LoginCredentials_init_default, SystemInfo_init_default, false, ""}
#define SystemInfo_init_zero {_CpuFamily_MIN, _Os_MIN, "", ""}
#define LoginCredentials_init_zero {"", _AuthenticationType_MIN, {0, {0}}}
#define ClientResponseEncrypted_init_zero {LoginCredentials_init_zero, SystemInfo_init_zero, false, ""}
/* Field tags (for use in manual encoding/decoding) */
#define LoginCredentials_username_tag 10
@@ -120,22 +122,22 @@ extern "C" {
#define SystemInfo_FIELDLIST(X, a) \
X(a, STATIC, REQUIRED, UENUM, cpu_family, 10) \
X(a, STATIC, REQUIRED, UENUM, os, 60) \
X(a, POINTER, OPTIONAL, STRING, system_information_string, 90) \
X(a, POINTER, OPTIONAL, STRING, device_id, 100)
X(a, STATIC, REQUIRED, STRING, system_information_string, 90) \
X(a, STATIC, REQUIRED, STRING, device_id, 100)
#define SystemInfo_CALLBACK NULL
#define SystemInfo_DEFAULT NULL
#define LoginCredentials_FIELDLIST(X, a) \
X(a, POINTER, OPTIONAL, STRING, username, 10) \
X(a, STATIC, REQUIRED, STRING, username, 10) \
X(a, STATIC, REQUIRED, UENUM, typ, 20) \
X(a, POINTER, OPTIONAL, BYTES, auth_data, 30)
X(a, STATIC, REQUIRED, BYTES, auth_data, 30)
#define LoginCredentials_CALLBACK NULL
#define LoginCredentials_DEFAULT NULL
#define ClientResponseEncrypted_FIELDLIST(X, a) \
X(a, STATIC, REQUIRED, MESSAGE, login_credentials, 10) \
X(a, STATIC, REQUIRED, MESSAGE, system_info, 50) \
X(a, POINTER, OPTIONAL, STRING, version_string, 70)
X(a, STATIC, OPTIONAL, STRING, version_string, 70)
#define ClientResponseEncrypted_CALLBACK NULL
#define ClientResponseEncrypted_DEFAULT NULL
#define ClientResponseEncrypted_login_credentials_MSGTYPE LoginCredentials
@@ -151,9 +153,9 @@ extern const pb_msgdesc_t ClientResponseEncrypted_msg;
#define ClientResponseEncrypted_fields &ClientResponseEncrypted_msg
/* Maximum encoded size of messages (where known) */
/* SystemInfo_size depends on runtime parameters */
/* LoginCredentials_size depends on runtime parameters */
/* ClientResponseEncrypted_size depends on runtime parameters */
#define ClientResponseEncrypted_size 665
#define LoginCredentials_size 550
#define SystemInfo_size 75
#ifdef __cplusplus
} /* extern "C" */

View File

@@ -48,14 +48,14 @@ enum AuthenticationType {
message SystemInfo {
required CpuFamily cpu_family = 0xa;
required Os os = 0x3c;
optional string system_information_string = 0x5a;
optional string device_id = 0x64;
required string system_information_string = 0x5a;
required string device_id = 0x64;
}
message LoginCredentials {
optional string username = 0xa;
required string username = 0xa;
required AuthenticationType typ = 0x14;
optional bytes auth_data = 0x1e;
required bytes auth_data = 0x1e;
}
message ClientResponseEncrypted {

View File

@@ -1,2 +1,2 @@
Header.uri type:FT_POINTER
Header.method type:FT_POINTER
Header.uri max_size:64, fixed_length:false
Header.method max_size:32, fixed_length:false

View File

@@ -11,8 +11,10 @@
/* Struct definitions */
typedef struct _Header {
char *uri;
char *method;
bool has_uri;
char uri[64];
bool has_method;
char method[32];
} Header;
@@ -21,8 +23,8 @@ extern "C" {
#endif
/* Initializer values for message structs */
#define Header_init_default {NULL, NULL}
#define Header_init_zero {NULL, NULL}
#define Header_init_default {false, "", false, ""}
#define Header_init_zero {false, "", false, ""}
/* Field tags (for use in manual encoding/decoding) */
#define Header_uri_tag 1
@@ -30,8 +32,8 @@ extern "C" {
/* Struct field encoding specification for nanopb */
#define Header_FIELDLIST(X, a) \
X(a, POINTER, OPTIONAL, STRING, uri, 1) \
X(a, POINTER, OPTIONAL, STRING, method, 3)
X(a, STATIC, OPTIONAL, STRING, uri, 1) \
X(a, STATIC, OPTIONAL, STRING, method, 3)
#define Header_CALLBACK NULL
#define Header_DEFAULT NULL
@@ -41,7 +43,7 @@ extern const pb_msgdesc_t Header_msg;
#define Header_fields &Header_msg
/* Maximum encoded size of messages (where known) */
/* Header_size depends on runtime parameters */
#define Header_size 98
#ifdef __cplusplus
} /* extern "C" */

View File

@@ -1,18 +1,18 @@
Track.name type:FT_POINTER
Track.gid type:FT_POINTER
Track.restriction type:FT_POINTER
Track.alternative type:FT_POINTER
Track.file type:FT_POINTER
Track.artist type:FT_POINTER
AudioFile.file_id type:FT_POINTER
Image.file_id type:FT_POINTER
Artist.gid type:FT_POINTER
Artist.name type:FT_POINTER
Album.name type:FT_POINTER
Episode.gid type:FT_POINTER
Episode.name type:FT_POINTER
ImageGroup.image type:FT_POINTER
Episode.audio type:FT_POINTER
Episode.covers type:FT_POINTER
Restriction.countries_allowed type:FT_POINTER
Restriction.countries_forbidden type:FT_POINTER
Track.name type: FT_POINTER
Track.gid type: FT_POINTER
Track.file type: FT_POINTER
Track.restriction type: FT_POINTER
Track.alternative type: FT_POINTER
Track.artist type: FT_POINTER
AudioFile.file_id type: FT_POINTER
Image.file_id type: FT_POINTER
Artist.gid type: FT_POINTER
Artist.name type: FT_POINTER
Album.name type: FT_POINTER
Episode.gid type: FT_POINTER
Episode.name type: FT_POINTER
ImageGroup.image type: FT_POINTER
Episode.audio type: FT_POINTER
Episode.covers type: FT_POINTER
Restriction.countries_allowed type: FT_POINTER
Restriction.countries_forbidden type: FT_POINTER

View File

@@ -7,7 +7,7 @@ DeviceState.sw_version type:FT_POINTER
DeviceState.name type:FT_POINTER
DeviceState.capabilities max_count:17, fixed_count:false
State.context_uri type:FT_POINTER
State.track max_count:100, fixed_count:false
State.track type:FT_POINTER
TrackRef.gid type:FT_POINTER
TrackRef.uri type:FT_POINTER
TrackRef.context type:FT_POINTER

View File

@@ -9,7 +9,7 @@
PB_BIND(TrackRef, TrackRef, AUTO)
PB_BIND(State, State, 4)
PB_BIND(State, State, AUTO)
PB_BIND(Capability, Capability, 2)

View File

@@ -62,6 +62,27 @@ typedef struct _Capability {
char stringValue[50][50];
} Capability;
typedef struct _State {
char *context_uri;
bool has_index;
uint32_t index;
bool has_position_ms;
uint32_t position_ms;
bool has_status;
PlayStatus status;
bool has_position_measured_at;
uint64_t position_measured_at;
pb_callback_t context_description;
bool has_shuffle;
bool shuffle;
bool has_repeat;
bool repeat;
bool has_playing_track_index;
uint32_t playing_track_index;
pb_size_t track_count;
struct _TrackRef *track;
} State;
typedef struct _TrackRef {
pb_bytes_array_t *gid;
char *uri;
@@ -89,27 +110,6 @@ typedef struct _DeviceState {
pb_callback_t local_uris;
} DeviceState;
typedef struct _State {
char *context_uri;
bool has_index;
uint32_t index;
bool has_position_ms;
uint32_t position_ms;
bool has_status;
PlayStatus status;
bool has_position_measured_at;
uint64_t position_measured_at;
pb_callback_t context_description;
bool has_shuffle;
bool shuffle;
bool has_repeat;
bool repeat;
bool has_playing_track_index;
uint32_t playing_track_index;
pb_size_t track_count;
TrackRef track[100];
} State;
typedef struct _Frame {
bool has_version;
uint32_t version;
@@ -154,12 +154,12 @@ extern "C" {
/* Initializer values for message structs */
#define TrackRef_init_default {NULL, NULL, false, 0, NULL}
#define State_init_default {NULL, false, 0, false, 0, false, _PlayStatus_MIN, false, 0, {{NULL}, NULL}, false, 0, false, 0, false, 0, 0, {TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default}}
#define State_init_default {NULL, false, 0, false, 0, false, _PlayStatus_MIN, false, 0, {{NULL}, NULL}, false, 0, false, 0, false, 0, 0, NULL}
#define Capability_init_default {false, _CapabilityType_MIN, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}}
#define DeviceState_init_default {NULL, false, 0, false, 0, false, 0, NULL, false, 0, false, 0, {{NULL}, NULL}, 0, {Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default}, {{NULL}, NULL}}
#define Frame_init_default {false, 0, NULL, NULL, false, 0, false, _MessageType_MIN, false, DeviceState_init_default, false, State_init_default, false, 0, false, 0, false, 0, 0, NULL}
#define TrackRef_init_zero {NULL, NULL, false, 0, NULL}
#define State_init_zero {NULL, false, 0, false, 0, false, _PlayStatus_MIN, false, 0, {{NULL}, NULL}, false, 0, false, 0, false, 0, 0, {TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero}}
#define State_init_zero {NULL, false, 0, false, 0, false, _PlayStatus_MIN, false, 0, {{NULL}, NULL}, false, 0, false, 0, false, 0, 0, NULL}
#define Capability_init_zero {false, _CapabilityType_MIN, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}}
#define DeviceState_init_zero {NULL, false, 0, false, 0, false, 0, NULL, false, 0, false, 0, {{NULL}, NULL}, 0, {Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero}, {{NULL}, NULL}}
#define Frame_init_zero {false, 0, NULL, NULL, false, 0, false, _MessageType_MIN, false, DeviceState_init_zero, false, State_init_zero, false, 0, false, 0, false, 0, 0, NULL}
@@ -168,6 +168,16 @@ extern "C" {
#define Capability_typ_tag 1
#define Capability_intValue_tag 2
#define Capability_stringValue_tag 3
#define State_context_uri_tag 2
#define State_index_tag 3
#define State_position_ms_tag 4
#define State_status_tag 5
#define State_position_measured_at_tag 7
#define State_context_description_tag 8
#define State_shuffle_tag 13
#define State_repeat_tag 14
#define State_playing_track_index_tag 26
#define State_track_tag 27
#define TrackRef_gid_tag 1
#define TrackRef_uri_tag 2
#define TrackRef_queued_tag 3
@@ -182,16 +192,6 @@ extern "C" {
#define DeviceState_error_message_tag 16
#define DeviceState_capabilities_tag 17
#define DeviceState_local_uris_tag 18
#define State_context_uri_tag 2
#define State_index_tag 3
#define State_position_ms_tag 4
#define State_status_tag 5
#define State_position_measured_at_tag 7
#define State_context_description_tag 8
#define State_shuffle_tag 13
#define State_repeat_tag 14
#define State_playing_track_index_tag 26
#define State_track_tag 27
#define Frame_version_tag 1
#define Frame_ident_tag 2
#define Frame_protocol_version_tag 3
@@ -223,7 +223,7 @@ X(a, CALLBACK, OPTIONAL, STRING, context_description, 8) \
X(a, STATIC, OPTIONAL, BOOL, shuffle, 13) \
X(a, STATIC, OPTIONAL, BOOL, repeat, 14) \
X(a, STATIC, OPTIONAL, UINT32, playing_track_index, 26) \
X(a, STATIC, REPEATED, MESSAGE, track, 27)
X(a, POINTER, REPEATED, MESSAGE, track, 27)
#define State_CALLBACK pb_default_field_callback
#define State_DEFAULT NULL
#define State_track_MSGTYPE TrackRef

View File

@@ -24,13 +24,28 @@ void AudioChunk::appendData(const std::vector<uint8_t> &data)
this->decryptedData.insert(this->decryptedData.end(), data.begin(), data.end());
}
void AudioChunk::decrypt()
void AudioChunk::readData(uint8_t *target, size_t offset, size_t nbytes) {
auto readPos = offset + nbytes;
auto modulo = (readPos % 16);
auto ivReadPos = readPos;
if (modulo != 0) {
ivReadPos += (16 - modulo);
}
if (ivReadPos > decryptedCount) {
// calculate the IV for right position
auto calculatedIV = this->getIVSum((oldStartPos + decryptedCount) / 16);
crypto->aesCTRXcrypt(this->audioKey, calculatedIV, decryptedData.data() + decryptedCount, ivReadPos - decryptedCount);
decryptedCount = ivReadPos;
}
memcpy(target, this->decryptedData.data() + offset, nbytes);
}
void AudioChunk::finalize()
{
// calculate the IV for right position
auto calculatedIV = this->getIVSum(startPosition / 16);
crypto->aesCTRXcrypt(this->audioKey, calculatedIV, decryptedData);
this->oldStartPos = this->startPosition;
this->startPosition = this->endPosition - this->decryptedData.size();
this->isLoaded = true;
}

View File

@@ -3,7 +3,7 @@
#include "Logger.h"
AudioChunkManager::AudioChunkManager()
: bell::Task("AudioChunkManager", 4 * 1024, 2, 0) {
: bell::Task("AudioChunkManager", 4 * 1024, -1, 0) {
this->chunks = std::vector<std::shared_ptr<AudioChunk>>();
startTask();
}
@@ -80,7 +80,7 @@ void AudioChunkManager::runTask() {
switch (data.size()) {
case DATA_SIZE_HEADER: {
CSPOT_LOG(debug, "ID: %d: header decrypt!", seqId);
CSPOT_LOG(debug, "ID: %d: header finalize!", seqId);
auto headerSize = ntohs(extract<uint16_t>(data, 2));
// Got file size!
chunk->headerFileSize =
@@ -92,9 +92,9 @@ void AudioChunkManager::runTask() {
if (chunk->endPosition > chunk->headerFileSize) {
chunk->endPosition = chunk->headerFileSize;
}
CSPOT_LOG(debug, "ID: %d: Starting decrypt!",
CSPOT_LOG(debug, "ID: %d: finalize chunk!",
seqId);
chunk->decrypt();
chunk->finalize();
chunk->isLoadedSemaphore->give();
break;

View File

@@ -156,7 +156,6 @@ void ChunkedAudioStream::startPlaybackLoop()
void ChunkedAudioStream::seek(size_t dpos, Whence whence)
{
BELL_LOG(info, "cspot", "%d", dpos);
auto seekPos = 0;
switch (whence)
{

View File

@@ -31,6 +31,7 @@ void ChunkedByteStream::fetchFileInformation() {
endChunk->keepInMemory = true;
chunks.push_back(endChunk);
requestChunk(0);
}
std::shared_ptr<AudioChunk> ChunkedByteStream::getChunkForPosition(size_t position) {
@@ -57,11 +58,14 @@ size_t ChunkedByteStream::read(uint8_t *buf, size_t nbytes) {
std::scoped_lock lock(this->readMutex);
auto chunk = getChunkForPosition(pos);
uint16_t chunkIndex = this->pos / AUDIO_CHUNK_SIZE;
for (auto it = chunks.begin(); it != chunks.end();) {
if (((*it)->endPosition<pos || (*it)->startPosition>(pos + 2 * AUDIO_CHUNK_SIZE)) && !(*it)->keepInMemory) {
it = chunks.erase(it);
} else {
it++;
if (loadAheadEnabled) {
for (auto it = chunks.begin(); it != chunks.end();) {
if (((*it)->endPosition<pos || (*it)->startPosition>(pos + 2 * AUDIO_CHUNK_SIZE)) && !(*it)->keepInMemory) {
it = chunks.erase(it);
} else {
it++;
}
}
}
@@ -85,7 +89,7 @@ size_t ChunkedByteStream::read(uint8_t *buf, size_t nbytes) {
pos += read;
auto nextChunkPos = ((chunkIndex + 1) * AUDIO_CHUNK_SIZE) + 1;
if (loadAheadEnabled && nextChunkPos + AUDIO_CHUNK_SIZE < fileSize) {
if (loadAheadEnabled && nextChunkPos < fileSize) {
auto nextChunk = getChunkForPosition(nextChunkPos);
if (nextChunk == nullptr) {
@@ -109,15 +113,12 @@ size_t ChunkedByteStream::attemptRead(uint8_t *buffer, size_t bytes, std::shared
toRead = chunk->decryptedData.size() - offset;
}
// Copy data
memcpy(buffer, chunk->decryptedData.data() + offset, toRead);
chunk->readData(buffer, offset, toRead);
return toRead;
}
void ChunkedByteStream::seek(size_t nbytes) {
std::scoped_lock lock(this->readMutex);
BELL_LOG(info, "cspot", "seeking to %d", nbytes);
pos = nbytes;

View File

@@ -35,7 +35,7 @@ std::vector<uint8_t> LoginBlob::decodeBlob(const std::vector<uint8_t> &blob, con
}
encryptionKey = std::vector<uint8_t>(encryptionKey.begin(), encryptionKey.begin() + 16);
crypto->aesCTRXcrypt(encryptionKey, iv, encrypted);
crypto->aesCTRXcrypt(encryptionKey, iv, encrypted.data(), encrypted.size());
return encrypted;
}

View File

@@ -9,9 +9,9 @@ std::map<MercuryType, std::string> MercuryTypeMap({
{MercuryType::UNSUB, "UNSUB"},
});
MercuryManager::MercuryManager(std::unique_ptr<Session> session): bell::Task("mercuryManager", 6 * 1024, 2, 1)
MercuryManager::MercuryManager(std::unique_ptr<Session> session): bell::Task("mercuryManager", 6 * 1024, 0, 1)
{
tempMercuryHeader = Header_init_default;
tempMercuryHeader = {};
this->timeProvider = std::make_shared<TimeProvider>();
this->callbacks = std::map<uint64_t, mercuryCallback>();
this->subscriptions = std::map<std::string, mercuryCallback>();
@@ -294,8 +294,11 @@ uint64_t MercuryManager::execute(MercuryType method, std::string uri, mercuryCal
// Construct mercury header
CSPOT_LOG(debug, "executing MercuryType %s", MercuryTypeMap[method].c_str());
tempMercuryHeader.uri = (char *)(uri.c_str());
tempMercuryHeader.method = (char *)(MercuryTypeMap[method].c_str());
pbPutString(uri, tempMercuryHeader.uri);
pbPutString(MercuryTypeMap[method], tempMercuryHeader.method);
tempMercuryHeader.has_method = true;
tempMercuryHeader.has_uri = true;
// GET and SEND are actually the same. Therefore the override
// The difference between them is only in header's method

View File

@@ -3,13 +3,12 @@
MercuryResponse::MercuryResponse(std::vector<uint8_t> &data)
{
// this->mercuryHeader = std::make_unique<Header>();
this->mercuryHeader = Header_init_default;
this->mercuryHeader = {};
this->parts = mercuryParts(0);
this->parseResponse(data);
}
MercuryResponse::~MercuryResponse() {
pb_release(Header_fields, &mercuryHeader);
}
void MercuryResponse::parseResponse(std::vector<uint8_t> &data)

View File

@@ -1,6 +1,6 @@
#include "Packet.h"
Packet::Packet(uint8_t command, std::vector<uint8_t> &data) {
Packet::Packet(uint8_t command, const std::vector<uint8_t> &data) {
this->command = command;
this->data = data;
};

View File

@@ -3,7 +3,7 @@
// #include <valgrind/memcheck.h>
Player::Player(std::shared_ptr<MercuryManager> manager, std::shared_ptr<AudioSink> audioSink): bell::Task("player", 10 * 1024, +0, 1)
Player::Player(std::shared_ptr<MercuryManager> manager, std::shared_ptr<AudioSink> audioSink): bell::Task("player", 10 * 1024, -2, 1)
{
this->audioSink = audioSink;
this->manager = manager;

View File

@@ -24,18 +24,18 @@ PlayerState::PlayerState(std::shared_ptr<TimeProvider> timeProvider)
innerFrame.state.repeat = false;
innerFrame.state.has_repeat = true;
innerFrame.device_state.sw_version = (char*) swVersion;
innerFrame.device_state.sw_version = strdup(swVersion);
innerFrame.device_state.is_active = false;
innerFrame.device_state.has_is_active = true;
innerFrame.device_state.can_play = true;
innerFrame.device_state.has_can_play = true;
innerFrame.device_state.volume = configMan->volume;
innerFrame.device_state.has_volume = true;
innerFrame.device_state.name = (char*) configMan->deviceName.c_str();
innerFrame.device_state.name = strdup(configMan->deviceName.c_str());
// Prepare player's capabilities
addCapability(CapabilityType_kCanBePlayer, 1);
@@ -53,33 +53,32 @@ PlayerState::PlayerState(std::shared_ptr<TimeProvider> timeProvider)
}
PlayerState::~PlayerState() {
pb_release(Frame_fields, &innerFrame);
pb_release(Frame_fields, &remoteFrame);
// do not destruct inner frame as it is never allocated
// pb_release(Frame_fields, &innerFrame);
}
void PlayerState::setPlaybackState(const PlaybackState state)
{
switch (state)
{
case PlaybackState::Loading:
// Prepare the playback at position 0
innerFrame.state.status = PlayStatus_kPlayStatusPause;
innerFrame.state.position_ms = 0;
innerFrame.state.position_measured_at = timeProvider->getSyncedTimestamp();
break;
case PlaybackState::Playing:
innerFrame.state.status = PlayStatus_kPlayStatusPlay;
innerFrame.state.position_measured_at = timeProvider->getSyncedTimestamp();
break;
case PlaybackState::Stopped:
break;
case PlaybackState::Paused:
// Update state and recalculate current song position
innerFrame.state.status = PlayStatus_kPlayStatusPause;
uint32_t diff = timeProvider->getSyncedTimestamp() - innerFrame.state.position_measured_at;
this->updatePositionMs(innerFrame.state.position_ms + diff);
break;
case PlaybackState::Loading:
// Prepare the playback at position 0
innerFrame.state.status = PlayStatus_kPlayStatusPause;
innerFrame.state.position_ms = 0;
innerFrame.state.position_measured_at = timeProvider->getSyncedTimestamp();
break;
case PlaybackState::Playing:
innerFrame.state.status = PlayStatus_kPlayStatusPlay;
innerFrame.state.position_measured_at = timeProvider->getSyncedTimestamp();
break;
case PlaybackState::Stopped:
break;
case PlaybackState::Paused:
// Update state and recalculate current song position
innerFrame.state.status = PlayStatus_kPlayStatusPause;
uint32_t diff = timeProvider->getSyncedTimestamp() - innerFrame.state.position_measured_at;
this->updatePositionMs(innerFrame.state.position_ms + diff);
break;
}
}
@@ -137,8 +136,8 @@ void PlayerState::updatePositionMs(uint32_t position)
void PlayerState::updateTracks()
{
CSPOT_LOG(info, "---- Track count %d", remoteFrame.state.track_count);
//innerFrame.state.context_uri = remoteFrame.state.context_uri == nullptr ? nullptr : strdup(remoteFrame.state.context_uri);
std::copy(std::begin(remoteFrame.state.track), std::end(remoteFrame.state.track), std::begin(innerFrame.state.track));
std::swap(innerFrame.state.context_uri, remoteFrame.state.context_uri);
std::swap(innerFrame.state.track, remoteFrame.state.track);
innerFrame.state.track_count = remoteFrame.state.track_count;
innerFrame.state.has_playing_track_index = true;
innerFrame.state.playing_track_index = remoteFrame.state.playing_track_index;
@@ -167,17 +166,13 @@ void PlayerState::setShuffle(bool shuffle)
if (shuffle)
{
// Put current song at the begining
auto tmp = innerFrame.state.track[0];
innerFrame.state.track[0] = innerFrame.state.track[innerFrame.state.playing_track_index];
innerFrame.state.track[innerFrame.state.playing_track_index] = tmp;
std::swap(innerFrame.state.track[0], innerFrame.state.track[innerFrame.state.playing_track_index]);
// Shuffle current tracks
for (int x = 1; x < innerFrame.state.track_count - 1; x++)
{
auto j = x + (std::rand() % (innerFrame.state.track_count - x));
tmp = innerFrame.state.track[j];
innerFrame.state.track[j] = innerFrame.state.track[x];
innerFrame.state.track[x] = tmp;
std::swap(innerFrame.state.track[j], innerFrame.state.track[x]);
}
innerFrame.state.playing_track_index = 0;
}
@@ -196,11 +191,14 @@ std::shared_ptr<TrackReference> PlayerState::getCurrentTrack()
std::vector<uint8_t> PlayerState::encodeCurrentFrame(MessageType typ)
{
free(innerFrame.ident);
free(innerFrame.protocol_version);
// Prepare current frame info
innerFrame.version = 1;
innerFrame.ident = (char *) deviceId;
innerFrame.ident = strdup(deviceId);
innerFrame.seq_nr = this->seqNum;
innerFrame.protocol_version = (char*) protocolVersion;
innerFrame.protocol_version = strdup(protocolVersion);
innerFrame.typ = typ;
innerFrame.state_update_id = timeProvider->getSyncedTimestamp();
innerFrame.has_version = true;
@@ -233,10 +231,9 @@ void PlayerState::addCapability(CapabilityType typ, int intValue, std::vector<st
for (int x = 0; x < stringValue.size(); x++)
{
stringValue[x].copy(this->innerFrame.device_state.capabilities[capabilityIndex].stringValue[x], stringValue[x].size());
this->innerFrame.device_state.capabilities[capabilityIndex].stringValue[x][stringValue[x].size()] = '\0';
pbPutString(stringValue[x], this->innerFrame.device_state.capabilities[capabilityIndex].stringValue[x]);
}
this->innerFrame.device_state.capabilities[capabilityIndex].stringValue_count = stringValue.size();
this->capabilityIndex += 1;
}
}

View File

@@ -49,17 +49,26 @@ std::vector<uint8_t> Session::authenticate(std::shared_ptr<LoginBlob> blob)
authBlob = blob;
// prepare authentication request proto
authRequest.login_credentials.username = (char *)(blob->username.c_str());
authRequest.login_credentials.auth_data = vectorToPbArray(blob->authData);
pbPutString(blob->username, authRequest.login_credentials.username);
std::copy(blob->authData.begin(), blob->authData.end(), authRequest.login_credentials.auth_data.bytes);
authRequest.login_credentials.auth_data.size = blob->authData.size();
authRequest.login_credentials.typ = (AuthenticationType) blob->authType;
authRequest.system_info.cpu_family = CpuFamily_CPU_UNKNOWN;
authRequest.system_info.os = Os_OS_UNKNOWN;
authRequest.system_info.system_information_string = (char *)informationString;
authRequest.system_info.device_id = (char *)deviceId;
authRequest.version_string = (char *)versionString;
auto infoStr = std::string(informationString);
pbPutString(infoStr, authRequest.system_info.system_information_string);
auto deviceIdStr = std::string(deviceId);
pbPutString(deviceId, authRequest.system_info.device_id);
auto versionStr = std::string(versionString);
pbPutString(versionStr, authRequest.version_string);
authRequest.has_version_string = true;
auto data = pbEncode(ClientResponseEncrypted_fields, &authRequest);
free(authRequest.login_credentials.auth_data);
// Send login request
this->shanConn->sendPacket(LOGIN_REQUEST_COMMAND, data);

View File

@@ -56,7 +56,7 @@ void SpircController::disconnect(void) {
state->setActive(false);
notify();
// Send the event at the end at it might be a last gasp
sendEvent(CSpotEventType::DISC);
sendEvent(CSpotEventType::DISC);
}
void SpircController::playToggle() {
@@ -103,7 +103,7 @@ void SpircController::prevSong() {
}
void SpircController::handleFrame(std::vector<uint8_t> &data) {
//pb_release(Frame_fields, &state->remoteFrame);
pb_release(Frame_fields, &state->remoteFrame);
pbDecode(state->remoteFrame, Frame_fields, data);
switch (state->remoteFrame.typ) {

View File

@@ -10,8 +10,8 @@ SpotifyTrack::SpotifyTrack(std::shared_ptr<MercuryManager> manager, std::shared_
{
this->manager = manager;
this->fileId = std::vector<uint8_t>();
episodeInfo = Episode_init_default;
trackInfo = Track_init_default;
episodeInfo = {};
trackInfo = {};
mercuryCallback trackResponseLambda = [=](std::unique_ptr<MercuryResponse> res) {
this->trackInformationCallback(std::move(res), position_ms, isPaused);
@@ -84,13 +84,15 @@ void SpotifyTrack::trackInformationCallback(std::unique_ptr<MercuryResponse> res
int altIndex = 0;
while (!canPlayTrack())
{
trackInfo.restriction = trackInfo.alternative[altIndex].restriction;
trackInfo.restriction_count = trackInfo.alternative[altIndex].restriction_count;
trackInfo.gid = trackInfo.alternative[altIndex].gid;
trackInfo.file = trackInfo.alternative[altIndex].file;
altIndex++;
std::swap(trackInfo.restriction, trackInfo.alternative[altIndex].restriction);
std::swap(trackInfo.restriction_count, trackInfo.alternative[altIndex].restriction_count);
std::swap(trackInfo.file, trackInfo.alternative[altIndex].file);
std::swap(trackInfo.file_count, trackInfo.alternative[altIndex].file_count);
std::swap(trackInfo.gid, trackInfo.alternative[altIndex].gid);
CSPOT_LOG(info, "Trying alternative %d", altIndex);
}
auto trackId = pbArrayToVector(trackInfo.gid);
this->fileId = std::vector<uint8_t>();

View File

@@ -13,6 +13,7 @@
#include "display.h"
#include "accessors.h"
#include "network_services.h"
#include "tools.h"
#include "cspot_private.h"
#include "cspot_sink.h"
@@ -87,6 +88,19 @@ const static actrls_t controls = {
cspot_volume_down, cspot_volume_up, cspot_toggle// knob left, knob_right, knob push
};
/****************************************************************************************
* Download callback
*/
void got_artwork(uint8_t* data, size_t len, void *context) {
if (data) {
ESP_LOGI(TAG, "got artwork of %zu bytes", len);
displayer_artwork(data);
free(data);
} else {
ESP_LOGW(TAG, "artwork error or too large %zu", len);
}
}
/****************************************************************************************
* Command handler
*/
@@ -106,7 +120,7 @@ static bool cmd_handler(cspot_event_t event, ...) {
switch(event) {
case CSPOT_SETUP:
actrls_set(controls, false, NULL, actrls_ir_action);
displayer_control(DISPLAYER_ACTIVATE, "SPOTIFY");
displayer_control(DISPLAYER_ACTIVATE, "SPOTIFY", true);
break;
case CSPOT_PLAY:
displayer_control(DISPLAYER_TIMER_RUN);
@@ -129,6 +143,11 @@ static bool cmd_handler(cspot_event_t event, ...) {
uint32_t sample_rate = va_arg(args, uint32_t);
int duration = va_arg(args, int);
char *artist = va_arg(args, char*), *album = va_arg(args, char*), *title = va_arg(args, char*);
char *artwork = va_arg(args, char*);
if (artwork && displayer_can_artwork()) {
ESP_LOGI(TAG, "requesting artwork %s", artwork);
http_download(artwork, 128*1024, got_artwork, NULL);
}
displayer_metadata(artist, album, title);
displayer_timer(DISPLAYER_ELAPSED, loaded ? -1 : 0, duration);
loaded = false;

View File

@@ -21,6 +21,11 @@ set_source_files_properties(mad.c pcm.c flac.c alac.c helix-aac.c vorbis.c opus.
-Wno-maybe-uninitialized
)
set_source_files_properties(wm8978/wm8978.c
PROPERTIES COMPILE_FLAGS
-Wno-unused-function
)
add_definitions(-DLINKALL -DLOOPBACK -DNO_FAAD -DEMBEDDED -DTREMOR_ONLY -DCUSTOM_VERSION=${BUILD_NUMBER})
if (${DEPTH} EQUAL "32")

View File

@@ -89,16 +89,14 @@ void equalizer_close(void) {
*/
void equalizer_update(s8_t *gain) {
char config[EQ_BANDS * 4 + 1] = { };
char *p = config;
int n = 0;
for (int i = 0; i < EQ_BANDS; i++) {
equalizer.gain[i] = gain[i];
if (gain[i] < 0) *p++ = '-';
*p++ = (gain[i] / 10) + 0x30;
*p++ = (gain[i] % 10) + 0x30;
if (i < EQ_BANDS - 1) *p++ = ',';
n += sprintf(config + n, "%d,", gain[i]);
}
config[n-1] = '\0';
config_set_value(NVS_TYPE_STR, "equalizer", config);
equalizer.update = true;
}

View File

@@ -75,6 +75,10 @@ sure that using rate_delay would fix that
#define STATS_PERIOD_MS 5000
#define STAT_STACK_SIZE (3*1024)
#ifndef CONFIG_AMP_GPIO_LEVEL
#define CONFIG_AMP_GPIO_LEVEL 1
#endif
extern struct outputstate output;
extern struct buffer *streambuf;
extern struct buffer *outputbuf;
@@ -101,7 +105,7 @@ static TaskHandle_t stats_task, output_i2s_task;
static bool stats;
static struct {
int gpio, active;
} amp_control = { -1, 1 },
} amp_control = { CONFIG_AMP_GPIO, CONFIG_AMP_GPIO_LEVEL },
mute_control = { CONFIG_MUTE_GPIO, CONFIG_MUTE_GPIO_LEVEL };
DECLARE_ALL_MIN_MAX;
@@ -171,20 +175,16 @@ static void jack_handler(bool inserted) {
/****************************************************************************************
* amp GPIO
*/
#ifndef AMP_GPIO_LOCKED
static void set_amp_gpio(int gpio, char *value) {
char *p;
if (strcasestr(value, "amp")) {
amp_control.gpio = gpio;
if ((p = strchr(value, ':')) != NULL) amp_control.active = atoi(p + 1);
gpio_pad_select_gpio_x(amp_control.gpio);
gpio_set_direction_x(amp_control.gpio, GPIO_MODE_OUTPUT);
gpio_set_level_x(amp_control.gpio, !amp_control.active);
LOG_INFO("setting amplifier GPIO %d (active:%d)", amp_control.gpio, amp_control.active);
}
}
#endif
/****************************************************************************************
* Set pin from config string
@@ -247,7 +247,7 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
// common I2S initialization
i2s_config.mode = I2S_MODE_MASTER | I2S_MODE_TX;
i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT;
i2s_config.communication_format = I2S_COMM_FORMAT_I2S| I2S_COMM_FORMAT_I2S_MSB;
i2s_config.communication_format = I2S_COMM_FORMAT_STAND_I2S;
// in case of overflow, do not replay old buffer
i2s_config.tx_desc_auto_clear = true;
i2s_config.use_apll = true;
@@ -347,13 +347,21 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
jack_handler_chain = jack_handler_svc;
jack_handler_svc = jack_handler;
#ifndef AMP_GPIO_LOCKED
parse_set_GPIO(set_amp_gpio);
#endif
if (amp_control.gpio != -1) {
gpio_pad_select_gpio_x(amp_control.gpio);
gpio_set_direction_x(amp_control.gpio, GPIO_MODE_OUTPUT);
gpio_set_level_x(amp_control.gpio, !amp_control.active);
LOG_INFO("setting amplifier GPIO %d (active:%d)", amp_control.gpio, amp_control.active);
}
if (jack_mutes_amp && jack_inserted_svc()) adac->speaker(false);
else adac->speaker(true);
adac->headset(jack_inserted_svc());
adac->headset(jack_inserted_svc());
// create task as a FreeRTOS task but uses stack in internal RAM
{
@@ -672,7 +680,7 @@ static const u16_t spdif_bmclookup[256] = { //biphase mark encoded values (least
audio is transmitted first (not the MSB) and that ESP32 libray sends R then L,
contrary to what seems to be usually done, so (dst) order had to be changed
*/
void spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst, size_t *count) {
static void IRAM_ATTR spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst, size_t *count) {
register u16_t hi, lo, aux;
size_t cnt = *count;

View File

@@ -0,0 +1,13 @@
# This should be made a pure CMake component but as CMake is even
# more shitty under Windows, backslash in path screws it all
if(CONFIG_MUSE)
message("Compiling for MUSE")
set(src_dirs "muse")
else()
set(src_dirs ".")
endif()
idf_component_register( SRC_DIRS ${src_dirs}
PRIV_REQUIRES services
)

View File

@@ -0,0 +1,3 @@
// weak should do the job but it does not...
__attribute__((weak)) void target_init(void) {
}

View File

@@ -0,0 +1,138 @@
/*
YOUR LICENSE
*/
#include <string.h>
#include <esp_log.h>
#include <esp_types.h>
#include <esp_system.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
//#include <driver/adc.h>
#include "driver/rmt.h"
#include "monitor.h"
/////////////////////////////////////////////////////////////////
//*********************** NeoPixels ***************************
////////////////////////////////////////////////////////////////
#define NUM_LEDS 1
#define LED_RMT_TX_CHANNEL 0
#define LED_RMT_TX_GPIO 22
#define BITS_PER_LED_CMD 24
#define LED_BUFFER_ITEMS ((NUM_LEDS * BITS_PER_LED_CMD))
// These values are determined by measuring pulse timing with logic analyzer and adjusting to match datasheet.
#define T0H 14 // 0 bit high time
#define T1H 52 // 1 bit high time
#define TL 52 // low time for either bit
#define GREEN 0xFF0000
#define RED 0x00FF00
#define BLUE 0x0000FF
#define WHITE 0xFFFFFF
#define YELLOW 0xE0F060
struct led_state {
uint32_t leds[NUM_LEDS];
};
void ws2812_control_init(void);
void ws2812_write_leds(struct led_state new_state);
///////////////////////////////////////////////////////////////////
static const char TAG[] = "muse";
static void (*battery_handler_chain)(float value);
static void battery_svc(float value);
void target_init(void) {
battery_handler_chain = battery_handler_svc;
battery_handler_svc = battery_svc;
ESP_LOGI(TAG, "Initializing for Muse");
}
static void battery_svc(float value) {
ESP_LOGI(TAG, "Called for battery service with %f", value);
// put here your code for LED according to value
if (battery_handler_chain) battery_handler_chain(value);
}
// Battery monitoring
/*
static void battery(void *data)
{
#define VGREEN 2300
#define VRED 2000
#define NM 10
static int val;
static int V[NM];
static int I=0;
int S;
for(int i=0;i<NM;i++)V[i]=VGREEN;
vTaskDelay(1000 / portTICK_PERIOD_MS);
struct led_state new_state;
ws2812_control_init();
// init ADC interface for battery survey
adc1_config_width(ADC_WIDTH_BIT_12);
adc1_config_channel_atten(ADC1_GPIO33_CHANNEL, ADC_ATTEN_DB_11);
while(true)
{
vTaskDelay(1000 / portTICK_PERIOD_MS);
V[I++] = adc1_get_raw(ADC1_GPIO33_CHANNEL);
if(I >= NM)I = 0;
S = 0;
for(int i=0;i<NM;i++)S = S + V[i];
val = S / NM;
new_state.leds[0] = YELLOW;
if(val > VGREEN) new_state.leds[0] = GREEN;
if(val < VRED) new_state.leds[0] = RED;
printf("====> %d %6x\n", val, new_state.leds[0]);
ws2812_write_leds(new_state);
}
}
*/
// This is the buffer which the hw peripheral will access while pulsing the output pin
rmt_item32_t led_data_buffer[LED_BUFFER_ITEMS];
void setup_rmt_data_buffer(struct led_state new_state);
void ws2812_control_init(void)
{
rmt_config_t config;
config.rmt_mode = RMT_MODE_TX;
config.channel = LED_RMT_TX_CHANNEL;
config.gpio_num = LED_RMT_TX_GPIO;
config.mem_block_num = 3;
config.tx_config.loop_en = false;
config.tx_config.carrier_en = false;
config.tx_config.idle_output_en = true;
config.tx_config.idle_level = 0;
config.clk_div = 2;
ESP_ERROR_CHECK(rmt_config(&config));
ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0));
}
void ws2812_write_leds(struct led_state new_state) {
setup_rmt_data_buffer(new_state);
ESP_ERROR_CHECK(rmt_write_items(LED_RMT_TX_CHANNEL, led_data_buffer, LED_BUFFER_ITEMS, false));
ESP_ERROR_CHECK(rmt_wait_tx_done(LED_RMT_TX_CHANNEL, portMAX_DELAY));
}
void setup_rmt_data_buffer(struct led_state new_state)
{
for (uint32_t led = 0; led < NUM_LEDS; led++) {
uint32_t bits_to_send = new_state.leds[led];
uint32_t mask = 1 << (BITS_PER_LED_CMD - 1);
for (uint32_t bit = 0; bit < BITS_PER_LED_CMD; bit++) {
uint32_t bit_is_set = bits_to_send & mask;
led_data_buffer[led * BITS_PER_LED_CMD + bit] = bit_is_set ?
(rmt_item32_t){{{T1H, 1, TL, 0}}} :
(rmt_item32_t){{{T0H, 1, TL, 0}}};
mask >>= 1;
}
}
}

View File

@@ -1,5 +1,6 @@
idf_component_register( SRCS operator.cpp tools.c trace.c
REQUIRES _override esp_common pthread
PRIV_REQUIRES esp_http_client esp-tls
INCLUDE_DIRS .
)

View File

@@ -11,9 +11,14 @@
#include <stdint.h>
#include <string.h>
#include <ctype.h>
#include "tools.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_task.h"
#include "esp_tls.h"
#include "esp_http_client.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "tools.h"
const static char TAG[] = "tools";
@@ -171,3 +176,100 @@ char * strdup_psram(const char * source){
}
return ptr;
}
/****************************************************************************************
* URL download
*/
typedef struct {
void *user_context;
http_download_cb_t callback;
size_t max, bytes;
bool abort;
uint8_t *data;
esp_http_client_handle_t client;
} http_context_t;
static void http_downloader(void *arg);
static esp_err_t http_event_handler(esp_http_client_event_t *evt);
void http_download(char *url, size_t max, http_download_cb_t callback, void *context) {
http_context_t *http_context = (http_context_t*) heap_caps_calloc(sizeof(http_context_t), 1, MALLOC_CAP_SPIRAM);
esp_http_client_config_t config = {
.url = url,
.event_handler = http_event_handler,
.user_data = http_context,
};
http_context->callback = callback;
http_context->user_context = context;
http_context->max = max;
http_context->client = esp_http_client_init(&config);
xTaskCreate(http_downloader, "downloader", 4*1024, http_context, ESP_TASK_PRIO_MIN + 1, NULL);
}
static void http_downloader(void *arg) {
http_context_t *http_context = (http_context_t*) arg;
esp_http_client_perform(http_context->client);
esp_http_client_cleanup(http_context->client);
free(http_context);
vTaskDelete(NULL);
}
static esp_err_t http_event_handler(esp_http_client_event_t *evt) {
http_context_t *http_context = (http_context_t*) evt->user_data;
if (http_context->abort) return ESP_FAIL;
switch(evt->event_id) {
case HTTP_EVENT_ERROR:
http_context->callback(NULL, 0, http_context->user_context);
http_context->abort = true;
break;
case HTTP_EVENT_ON_HEADER:
if (!strcasecmp(evt->header_key, "Content-Length")) {
size_t len = atoi(evt->header_value);
if (!len || len > http_context->max) {
ESP_LOGI(TAG, "content-length null or too large %zu / %zu", len, http_context->max);
http_context->abort = true;
}
}
break;
case HTTP_EVENT_ON_DATA: {
size_t len = esp_http_client_get_content_length(evt->client);
if (!http_context->data) {
if ((http_context->data = (uint8_t*) malloc(len)) == NULL) {
http_context->abort = true;
ESP_LOGE(TAG, "gailed to allocate memory for output buffer %zu", len);
return ESP_FAIL;
}
}
memcpy(http_context->data + http_context->bytes, evt->data, evt->data_len);
http_context->bytes += evt->data_len;
break;
}
case HTTP_EVENT_ON_FINISH:
http_context->callback(http_context->data, http_context->bytes, http_context->user_context);
break;
case HTTP_EVENT_DISCONNECTED: {
int mbedtls_err = 0;
esp_err_t err = esp_tls_get_and_clear_last_error(evt->data, &mbedtls_err, NULL);
if (err != ESP_OK) {
ESP_LOGE(TAG, "HTTP download disconnect %d", err);
if (http_context->data) free(http_context->data);
http_context->callback(NULL, 0, http_context->user_context);
return ESP_FAIL;
}
break;
default:
break;
}
}
return ESP_OK;
}

View File

@@ -53,6 +53,9 @@ char* strdup_psram(const char * source);
const char* str_or_unknown(const char * str);
const char* str_or_null(const char * str);
typedef void (*http_download_cb_t)(uint8_t* data, size_t len, void *context);
void http_download(char *url, size_t max, http_download_cb_t callback, void *context);
extern const char unknown_string_placeholder[];
#ifdef __cplusplus

View File

@@ -440,6 +440,7 @@
GPL
License.</li>
<li>tarablessd1306, &copy; 2017-2018, Tara Keeling. Licensed under the MIT license.</li>
<li>CSpot, &copy; 2020 feelfreelinux & alufers. Licensed under the GPL License</li>
</ul>
</div>
</div>

View File

@@ -1,5 +1,5 @@
idf_component_register(SRC_DIRS .
PRIV_REQUIRES esp_common wifi-manager pthread squeezelite-ota platform_console telnet display
PRIV_REQUIRES esp_common wifi-manager pthread squeezelite-ota platform_console telnet display targets
EMBED_FILES ../server_certs/github.pem
LDFRAGMENTS "linker.lf"
)

View File

@@ -26,6 +26,8 @@ menu "Squeezelite-ESP32"
help
Set logging level info|debug|sdebug
endmenu
config AMP_LOCKED
bool
config JACK_LOCKED
bool
config BAT_LOCKED
@@ -41,6 +43,9 @@ menu "Squeezelite-ESP32"
config MUTE_GPIO_LEVEL
int
default 0
config WELL_KNOWN
bool
default n
menu "Target"
choice OUTPUT_TYPE
prompt "Main system"
@@ -54,12 +59,21 @@ menu "Squeezelite-ESP32"
select I2C_LOCKED
select LED_LOCKED
select SPKFAULT_LOCKED
config BASIC_I2C_BT
bool "Generic I2S & Bluetooth"
config TWATCH2020
bool "T-WATCH2020 by LilyGo"
select WELL_KNOWN
config MUSE
bool "Muse"
select JACK_LOCKED
select BAT_LOCKED
select I2C_LOCKED
endchoice
select AMP_LOCKED
select WELL_KNOWN
config BASIC_I2C_BT
bool "Generic I2S & Bluetooth"
config TWATCH2020
bool "T-WATCH2020 by LilyGo"
select I2C_LOCKED
select WELL_KNOWN
endchoice
config RELEASE_API
string "Software update URL"
default "https://api.github.com/repos/sle118/squeezelite-esp32/releases"
@@ -76,11 +90,13 @@ menu "Squeezelite-ESP32"
string
default "SqueezeAMP" if SQUEEZEAMP
default "Squeezelite-TWATCH" if TWATCH2020
default "Muse" if MUSE
default "Squeezelite-ESP32"
config FW_PLATFORM_NAME
string
default "SqueezeAmp" if SQUEEZEAMP
default "TWATCH" if TWATCH2020
default "Muse" if MUSE
default "ESP32"
# AGGREGATES - begin
# these parameters are "aggregates" that take precedence. They must have a default value
@@ -88,6 +104,7 @@ menu "Squeezelite-ESP32"
string
default "model=TAS57xx,bck=33,ws=25,do=32,sda=27,scl=26,mute=14:0" if SQUEEZEAMP
default "model=I2S,bck=26,ws=25,do=33,i2c=53,sda=21,scl=22" if TWATCH2020
default "model=I2S,bck=5,ws=25,do=26,di=35,i2c=16,sda=18,scl=23,mck" if MUSE
default ""
config SPDIF_CONFIG
string
@@ -98,7 +115,8 @@ menu "Squeezelite-ESP32"
default ""
config SPI_CONFIG
string
default "dc=27,data=19,clk=18" if TWATCH2020
default "dc=27,data=19,clk=18" if TWATCH2020
default "mosi=15,miso=2,clk=14" if MUSE
default ""
config DISPLAY_CONFIG
string
@@ -107,17 +125,28 @@ menu "Squeezelite-ESP32"
config ETH_CONFIG
string
default ""
# AGGREGATES - end
config DAC_CONTROLSET
string
default '{ "init": [ {"reg":41, "val":128}, {"reg":18, "val":255} ], "poweron": [ {"reg":18, "val":64, "mode":"or"} ], "poweroff": [ {"reg":18, "val":191, "mode":"and" } ] }' if TWATCH2020
default "{ \"init\": [ {\"reg\":41, \"val\":128}, {\"reg\":18, \"val\":255} ], \"poweron\": [ {\"reg\":18, \"val\":64, \"mode\":\"or\"} ], \"poweroff\": [ {\"reg\":18, \"val\":191, \"mode\":\"and\"} ] }" if TWATCH2020
default "{\"init\":[ {\"reg\":0,\"val\":128}, {\"reg\":0,\"val\":0}, {\"reg\":25,\"val\":4}, {\"reg\":1,\"val\":80}, {\"reg\":2,\"val\":0}, {\"reg\":8,\"val\":0}, {\"reg\":4,\"val\":192}, {\"reg\":0,\"val\":18}, {\"reg\":1,\"val\":0}, {\"reg\":23,\"val\":24}, {\"reg\":24,\"val\":2}, {\"reg\":38,\"val\":9}, {\"reg\":39,\"val\":144}, {\"reg\":42,\"val\":144}, {\"reg\":43,\"val\":128}, {\"reg\":45,\"val\":128}, {\"reg\":27,\"val\":0}, {\"reg\":26,\"val\":0}, {\"reg\":2,\"val\":240}, {\"reg\":2,\"val\":0}, {\"reg\":29,\"val\":28}, {\"reg\":4,\"val\":48}, {\"reg\":25,\"val\":0} ]}" if MUSE
default ""
config AUDIO_CONTROLS
string
default "[{\"gpio\":32, \"pull\":true, \"debounce\":10, \"normal\":{\"pressed\":\"ACTRLS_VOLDOWN\"}}, {\"gpio\":19, \"pull\":true, \"debounce\":40, \"normal\":{\"pressed\":\"ACTRLS_VOLUP\"}}, {\"gpio\":12, \"pull\":true, \"debounce\":40, \"longpress\":1000, \"normal\":{\"pressed\":\"ACTRLS_TOGGLE\"},\"longpress\":{\"pressed\":\"ACTRLS_POWER\"}}]" if MUSE
default ""
# AGGREGATES - end
# VARs that must be reset when changign target
config BAT_CONFIG
default "channel=7,scale=20.24,atten=0" if SQUEEZEAMP
default "channel=5,scale=1,atten=3,cells=1" if MUSE
default ""
config AMP_GPIO
int
default 21 if MUSE
default -1
config JACK_GPIO
int
default 34 if SQUEEZEAMP
default -1
default 34 if SQUEEZEAMP || MUSE
default -1
config SPKFAULT_GPIO
int
default 2 if SQUEEZEAMP
@@ -129,6 +158,7 @@ menu "Squeezelite-ESP32"
config LED_GREEN_GPIO
int
default 12 if SQUEEZEAMP
default 22 if MUSE
default -1
config LED_RED_GPIO
int
@@ -274,6 +304,14 @@ menu "Squeezelite-ESP32"
help
Enable Spotify connect using CSpot
endmenu
menu "Controls"
depends on !MUSE
config AUDIO_CONTROLS
string "Audio buttons set (JSON)"
help
Configuration of buttons (see README for syntax)
endmenu
menu "Display Screen"
depends on !TWATCH2020
@@ -320,8 +358,9 @@ menu "Squeezelite-ESP32"
Set parameters of GPIO extender
model=<model>[,addr=<addr>][,base=<100..N>][,count=<0..32>][,intr=<gpio>][,port=dac|system]
endmenu
menu "LED configuration"
visible if !SQUEEZEAMP && !TWATCH2020
visible if !SQUEEZEAMP && !TWATCH2020 && !MUSE
config LED_GREEN_GPIO
int "Green led GPIO"
help
@@ -329,7 +368,7 @@ menu "Squeezelite-ESP32"
config LED_GREEN_GPIO_LEVEL
int "Green led ON level"
depends on LED_GREEN_GPIO != -1
config LED_RED_GPIO
config LED_RED_GPIO
int "Red led GPIO"
help
Set to -1 for no LED
@@ -339,9 +378,10 @@ menu "Squeezelite-ESP32"
default 0 if SQUEEZEAMP
default 1
endmenu
menu "Audio JACK"
visible if !SQUEEZEAMP && !TWATCH2020
config JACK_GPIO
menu "Audio JACK"
visible if !WELL_KNOWN
config JACK_GPIO
int "Jack insertion GPIO"
help
GPIO to detect speaker jack insertion. Set to -1 for no detection.
@@ -349,10 +389,23 @@ menu "Squeezelite-ESP32"
depends on JACK_GPIO != -1
int "Level when inserted (0/1)"
default 0
endmenu
menu "Speaker Fault"
visible if !SQUEEZEAMP && !TWATCH2020
config SPKFAULT_GPIO
endmenu
menu "Amplifier"
visible if !WELL_KNOWN
config AMP_GPIO
int "Amplifier GPIO"
help
GPIO to switch on/off amplifier. Set to -1 for no amplifier.
config AMP_GPIO_LEVEL
depends on AMP_GPIO != -1
int "Active level(0/1)"
default 1
endmenu
menu "Speaker Fault"
visible if !WELL_KNOWN
config SPKFAULT_GPIO
int "Speaker fault GPIO"
help
GPIO to detect speaker fault condition. Set to -1 for no detection.
@@ -361,20 +414,16 @@ menu "Squeezelite-ESP32"
int "Level when fault (0/1)"
default 0
endmenu
menu "Battery measure"
visible if !SQUEEZEAMP && !TWATCH2020
config BAT_CHANNEL
int "Set channel (0..7)"
visible if !WELL_KNOWN
config BAT_CONFIG
string "Battery acquisition configuration"
help
Read a value every 10s on ADC1 on set Channel
config BAT_SCALE
string "Set scaling factor"
depends on BAT_CHANNEL != -1
default "20.24" if SQUEEZEAMP
default ""
help
Set the scaling factor for this 12 bits ADC
Sets parameters for battery voltage measure
channel=<0..7>,scale=<ratio_to_4096>,atten=<adc_atten>,cells=<1..3>
endmenu
config DEFAULT_COMMAND_LINE
string "Default command line to execute"
default "squeezelite -o I2S -b 500:2000 -d all=info -C 30"

View File

@@ -71,6 +71,7 @@ extern const uint8_t server_cert_pem_end[] asm("_binary_github_pem_end");
// as an exception _init function don't need include
extern void services_init(void);
extern void display_init(char *welcome);
extern void target_init(void);
const char * str_or_unknown(const char * str) { return (str?str:unknown_string_placeholder); }
const char * str_or_null(const char * str) { return (str?str:null_string_placeholder); }
bool is_recovery_running;
@@ -446,6 +447,7 @@ void app_main()
ESP_LOGI(TAG,"Initializing display");
display_init("SqueezeESP32");
MEMTRACE_PRINT_DELTA();
target_init();
if(is_recovery_running && display){
GDS_ClearExt(display, true);
GDS_SetFont(display, &Font_line_2 );