diff --git a/build-scripts/I2S-4MFlash-sdkconfig.defaults b/build-scripts/I2S-4MFlash-sdkconfig.defaults index 40110fd2..3b5a281b 100644 --- a/build-scripts/I2S-4MFlash-sdkconfig.defaults +++ b/build-scripts/I2S-4MFlash-sdkconfig.defaults @@ -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 # diff --git a/build-scripts/SqueezeAmp-sdkconfig.defaults b/build-scripts/SqueezeAmp-sdkconfig.defaults index fb63230a..f3fc23f1 100644 --- a/build-scripts/SqueezeAmp-sdkconfig.defaults +++ b/build-scripts/SqueezeAmp-sdkconfig.defaults @@ -274,6 +274,18 @@ CONFIG_BAT_SCALE="20.24" 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 # diff --git a/components/services/battery.c b/components/services/battery.c index a0134c8c..2b356106 100644 --- a/components/services/battery.c +++ b/components/services/battery.c @@ -41,6 +41,8 @@ static struct { .attenuation = ADC_ATTEN_DB_0, }; +void (*battery_handler_svc)(float value); + /**************************************************************************************** * */ @@ -65,6 +67,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); } } diff --git a/components/services/monitor.h b/components/services/monitor.h index 3f441ff9..b0710153 100644 --- a/components/services/monitor.h +++ b/components/services/monitor.h @@ -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); diff --git a/components/squeezelite/output_i2s.c b/components/squeezelite/output_i2s.c index 76ce69cb..5f3a4a2c 100644 --- a/components/squeezelite/output_i2s.c +++ b/components/squeezelite/output_i2s.c @@ -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 @@ -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 { diff --git a/components/targets/CMakeLists.txt b/components/targets/CMakeLists.txt new file mode 100644 index 00000000..f8a5b188 --- /dev/null +++ b/components/targets/CMakeLists.txt @@ -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 +) diff --git a/components/targets/init.c b/components/targets/init.c new file mode 100644 index 00000000..f6e42cb8 --- /dev/null +++ b/components/targets/init.c @@ -0,0 +1,3 @@ +// weak should do the job but it does not... + __attribute__((weak)) void target_init(void) { +} diff --git a/components/targets/muse/muse.c b/components/targets/muse/muse.c new file mode 100644 index 00000000..d09fa402 --- /dev/null +++ b/components/targets/muse/muse.c @@ -0,0 +1,137 @@ +/* + YOUR LICENSE + */ +#include +#include +#include +#include +#include +#include +#include +#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(void *data); +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); + 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 = 0; + S = 0; + for(int i=0;i 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; + } + } + } + diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index ed29407b..d2947f14 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -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 @@ -55,11 +57,17 @@ menu "Squeezelite-ESP32" select LED_LOCKED select SPKFAULT_LOCKED config BASIC_I2C_BT - bool "Generic I2S & Bluetooth" - config TWATCH2020 - bool "T-WATCH2020 by LilyGo" + bool "Generic I2S & Bluetooth" + config TWATCH2020 + bool "T-WATCH2020 by LilyGo" + select I2C_LOCKED + config MUSE + bool "Muse" + select JACK_LOCKED + select BAT_LOCKED select I2C_LOCKED - endchoice + select AMP_LOCKED + endchoice config RELEASE_API string "Software update URL" default "https://api.github.com/repos/sle118/squeezelite-esp32/releases" @@ -76,11 +84,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 +98,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 +109,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 +119,25 @@ 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 + default -1 + 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 +149,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 +295,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 +349,9 @@ menu "Squeezelite-ESP32" Set parameters of GPIO extender model=[,addr=][,base=<100..N>][,count=<0..32>][,intr=][,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 +359,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 +369,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 !SQUEEZEAMP && !TWATCH2020 && !MUSE + config JACK_GPIO int "Jack insertion GPIO" help GPIO to detect speaker jack insertion. Set to -1 for no detection. @@ -349,10 +380,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 !SQUEEZEAMP && !TWATCH2020 && !MUSE + 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 !SQUEEZEAMP && !TWATCH2020 && !MUSE + config SPKFAULT_GPIO int "Speaker fault GPIO" help GPIO to detect speaker fault condition. Set to -1 for no detection. diff --git a/main/esp_app_main.c b/main/esp_app_main.c index b2e9178f..b70c6099 100644 --- a/main/esp_app_main.c +++ b/main/esp_app_main.c @@ -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 );