diff --git a/README.md b/README.md index 08383ae2..c2785f9b 100644 --- a/README.md +++ b/README.md @@ -46,8 +46,10 @@ nvs_set autoexec2 str -v "squeezelite -o \"BT -n 'MySpeaker'\" -b 500:2000 -R -u #pragma GCC optimize ("O0") #pragma GCC pop_options - opus & opusfile + - for opus, the ESP-provided library seems to work, but opusfile is still needed - per mad & few others, edit configure and change $ac_link to add -c (faking link) - change ac_files to remove '' + - add DEPS_CFLAGS and DEPS_LIBS to avoid pkg-config to be required - better use helixaac - set IDF_PATH=/home/esp-idf - set ESPPORT=COM9 diff --git a/components/io/component.mk b/components/io/component.mk new file mode 100644 index 00000000..e0e9f4c1 --- /dev/null +++ b/components/io/component.mk @@ -0,0 +1,10 @@ +# +# Component Makefile +# +# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default, +# this will take the sources in the src/ directory, compile them and link them into +# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, +# please read the SDK documents if you need to do this. +# + +COMPONENT_ADD_INCLUDEDIRS := . diff --git a/components/io/led.c b/components/io/led.c new file mode 100644 index 00000000..164eaa08 --- /dev/null +++ b/components/io/led.c @@ -0,0 +1,101 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/timers.h" +#include "esp_system.h" +#include "esp_log.h" +#include "driver/gpio.h" +#include "led.h" + +#define MAX_LED 8 +#define BLOCKTIME 10 // up to portMAX_DELAY + +static struct led_s { + gpio_num_t gpio; + bool on; + int onstate; + int ontime, offtime; + int waiton, waitoff; + TimerHandle_t timer; +} leds[MAX_LED]; + +static void vCallbackFunction( TimerHandle_t xTimer ) { + struct led_s *led = (struct led_s*) pvTimerGetTimerID (xTimer); + + if (!led->timer) return; + + led->on = !led->on; + gpio_set_level(led->gpio, led->on ? led->onstate : !led->onstate); + + // was just on for a while + if (!led->on && led->offtime == -1) return; + + // regular blinking + xTimerChangePeriod(xTimer, (led->on ? led->ontime : led->offtime) / portTICK_RATE_MS, BLOCKTIME); +} + +bool led_blink_core(int idx, int ontime, int offtime, bool wait) { + if (!leds[idx].gpio) return false; + + if (leds[idx].timer) { + // low priority timers will wait + if (wait && xTimerIsTimerActive(leds[idx].timer)) { + leds[idx].waiton = ontime; + leds[idx].waitoff = offtime; + return true; + } + xTimerStop(leds[idx].timer, BLOCKTIME); + } + + leds[idx].ontime = ontime; + leds[idx].offtime = offtime; + + if (ontime == 0) { + gpio_set_level(leds[idx].gpio, !leds[idx].onstate); + } else if (offtime == 0) { + gpio_set_level(leds[idx].gpio, leds[idx].onstate); + } else { + if (!leds[idx].timer) leds[idx].timer = xTimerCreate("ledTimer", ontime / portTICK_RATE_MS, pdFALSE, (void *)&leds[idx], vCallbackFunction); + leds[idx].on = true; + gpio_set_level(leds[idx].gpio, leds[idx].onstate); + if (xTimerStart(leds[idx].timer, BLOCKTIME) == pdFAIL) return false; + } + + return true; +} + +bool led_release(int idx) { + if (!leds[idx].gpio) return false; + + if (leds[idx].waiton) { + led_blink_core(idx, leds[idx].waiton, leds[idx].waitoff, false); + leds[idx].waiton = 0; + } else { + gpio_set_level(leds[idx].gpio, !leds[idx].onstate); + } + + return true; +} + +bool led_config(int idx, gpio_num_t gpio, int onstate) { + if (idx >= MAX_LED) return false; + leds[idx].gpio = gpio; + leds[idx].onstate = onstate; + + gpio_pad_select_gpio(gpio); + gpio_set_direction(gpio, GPIO_MODE_OUTPUT); + gpio_set_level(gpio, !onstate); + + return true; +} diff --git a/components/io/led.h b/components/io/led.h new file mode 100644 index 00000000..91283cd5 --- /dev/null +++ b/components/io/led.h @@ -0,0 +1,37 @@ +/* + * Squeezelite for esp32 + * + * (c) Sebastien 2019 + * Philippe G. 2019, philippe_44@outlook.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef LED_H + +#include "driver/gpio.h" + +enum { LED_GREEN = 0, LED_RED }; + +#define led_on(idx) led_blink_core(idx, 1, 0, false) +#define led_off(idx) led_blink_core(idx, 0, 0, false) +#define led_blink(idx, on, off) led_blink_core(idx, on, off, false) +#define led_blink_wait(idx, on, off) led_blink_core(idx, on, off, true) + +bool led_blink_core(int idx, int ontime, int offtime, bool wait); +bool led_release(int idx); +bool led_config(int idx, gpio_num_t gpio, int onstate); + +#endif \ No newline at end of file diff --git a/components/squeezelite/component.mk b/components/squeezelite/component.mk index 9281a039..7bc7786f 100644 --- a/components/squeezelite/component.mk +++ b/components/squeezelite/component.mk @@ -2,7 +2,7 @@ # "main" pseudo-component makefile. # # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) -CFLAGS += -O3 -DLINKALL -DLOOPBACK -DTAS575x -DNO_FAAD -DRESAMPLE16 -DEMBEDDED -DTREMOR_ONLY -DBYTES_PER_FRAME=4 \ +CFLAGS += -O3 -DLINKALL -DLOOPBACK -DNO_FAAD -DRESAMPLE16 -DEMBEDDED -DTREMOR_ONLY -DBYTES_PER_FRAME=4 \ -I$(COMPONENT_PATH)/../codecs/inc \ -I$(COMPONENT_PATH)/../codecs/inc/mad \ -I$(COMPONENT_PATH)/../codecs/inc/alac \ diff --git a/components/squeezelite/output_i2s.c b/components/squeezelite/output_i2s.c index 63c4b9dd..169e88fc 100644 --- a/components/squeezelite/output_i2s.c +++ b/components/squeezelite/output_i2s.c @@ -43,9 +43,11 @@ sure that using rate_delay would fix that #include "squeezelite.h" #include "driver/i2s.h" #include "driver/i2c.h" +#include "driver/gpio.h" #include "perf_trace.h" #include #include "time.h" +#include "led.h" #define LOCK mutex_lock(outputbuf->mutex) #define UNLOCK mutex_unlock(outputbuf->mutex) @@ -109,7 +111,18 @@ static void *output_thread_i2s(); static void *output_thread_i2s_stats(); static void dac_cmd(dac_cmd_e cmd, ...); -#ifdef TAS575x +#ifdef CONFIG_SQUEEZEAMP + +#define TAS575x + +#undef CONFIG_I2S_BCK_IO +#define CONFIG_I2S_BCK_IO 33 +#undef CONFIG_I2S_WS_IO +#define CONFIG_I2S_WS_IO 25 +#undef CONFIG_I2S_DO_IO +#define CONFIG_I2S_DO_IO 32 +#undef CONFIG_I2S_NUM +#define CONFIG_I2S_NUM 0 #define I2C_PORT 0 #define I2C_ADDR 0x4c @@ -151,7 +164,13 @@ static const struct tas575x_cmd_s tas575x_cmd[] = { void output_init_i2s(log_level level, char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle) { loglevel = level; -#ifdef TAS575x +#ifdef TAS575x + gpio_pad_select_gpio(39); + gpio_set_direction(39, GPIO_MODE_INPUT); + + adc1_config_width(ADC_WIDTH_BIT_12); + adc1_config_channel_atten(ADC1_CHANNEL_0,ADC_ATTEN_DB_0); + // init volume & mute gpio_pad_select_gpio(VOLUME_GPIO); gpio_set_direction(VOLUME_GPIO, GPIO_MODE_OUTPUT); @@ -333,6 +352,9 @@ static void *output_thread_i2s() { int discard = 0; uint32_t fullness = gettime_ms(); bool synced; +#ifdef TAS575x + output_state state = OUTPUT_OFF; +#endif while (running) { @@ -340,6 +362,16 @@ static void *output_thread_i2s() { LOCK; +#ifdef TAS575x + // manage led display + if (state != output.state) { + if (output.state == OUTPUT_OFF) led_blink(LED_GREEN, 100, 2500); + else if (output.state == OUTPUT_STOPPED) led_blink_wait(LED_GREEN, 200, 1000); + else if (output.state == OUTPUT_RUNNING) led_on(LED_GREEN); + } + state = output.state; +#endif + if (output.state == OUTPUT_OFF) { UNLOCK; LOG_INFO("Output state is off."); @@ -348,6 +380,7 @@ static void *output_thread_i2s() { i2s_stop(CONFIG_I2S_NUM); dac_cmd(DAC_OFF); } + LOG_ERROR("Jack %d Voltage %.2fV", !gpio_get_level(39), adc1_get_raw(ADC1_CHANNEL_0) / 4095. * (10+169)/10. * 1.1); usleep(200000); continue; } else if (output.state == OUTPUT_STOPPED) { @@ -389,7 +422,7 @@ static void *output_thread_i2s() { LOG_INFO("Restarting I2S."); i2s_zero_dma_buffer(CONFIG_I2S_NUM); i2s_start(CONFIG_I2S_NUM); - dac_cmd(DAC_ON); + dac_cmd(DAC_ON); } // this does not work well as set_sample_rates resets the fifos (and it's too early) diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index abff1554..65a39899 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -123,26 +123,17 @@ menu "Squeezelite-ESP32" menu "Audio Output" choice OUTPUT_TYPE prompt "Output Type" - default DACAUDIO + default BASIC_I2C_BT help - Type of output for squeezelite to send audio to - config DACAUDIO - bool "DAC over I2S" - config BTAUDIO - bool "Bluetooth A2DP" + Type of hardware platform + config SQUEEZEAMP + bool "SqueezeAMP (TAS575x & Bluetooth)" + config BASIC_I2C_BT + bool "Generic I2S & Bluetooth" endchoice - config OUTPUT_NAME - string - default "" - default "BT" if BTAUDIO - default "DAC" if DACAUDIO - - config OUTPUT_RATES - string "Output rates" - default "44100" - help - [:] Sample rates supported, allows output to be off when squeezelite is started; rates = |-|,,; delay = optional delay switching rates in ms - menu "DAC I2S settings" + + menu "DAC I2S settings" + depends on BASIC_I2C_BT config I2S_NUM int "I2S channel (0 or 1). " default 0 @@ -180,6 +171,7 @@ menu "Squeezelite-ESP32" default 24 if I2S_BITS_PER_CHANNEL_24 default 8 if I2S_BITS_PER_CHANNEL_8 endmenu + menu "A2DP settings" config A2DP_SINK_NAME string "Name of Bluetooth A2DP device" diff --git a/main/cmd_wifi.c b/main/cmd_wifi.c index 29fc943a..e5004704 100644 --- a/main/cmd_wifi.c +++ b/main/cmd_wifi.c @@ -21,6 +21,7 @@ #include "esp_wifi.h" #include "tcpip_adapter.h" #include "esp_event.h" +#include "led.h" #define JOIN_TIMEOUT_MS (10000) @@ -46,8 +47,10 @@ static void event_handler(void* arg, esp_event_base_t event_base, { if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { esp_wifi_connect(); + led_blink(LED_GREEN, 250, 250); xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + led_release(LED_GREEN); xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); } } @@ -89,6 +92,7 @@ static void initialise_wifi(void) ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_NULL) ); ESP_ERROR_CHECK( esp_wifi_start() ); initialized = true; + led_blink(LED_GREEN, 250, 250); } static bool wifi_join(const char *ssid, const char *pass, int timeout_ms) diff --git a/main/esp_app_main.c b/main/esp_app_main.c index 8e05230e..511d7fa4 100644 --- a/main/esp_app_main.c +++ b/main/esp_app_main.c @@ -18,10 +18,21 @@ * along with this program. If not, see . * */ - #include "platform_esp32.h" +#include "led.h" + +#ifdef CONFIG_SQUEEZEAMP +#define LED_GREEN_GPIO 12 +#define LED_RED_GPIO 13 +#else +#define LED_GREEN_GPIO 0 +#define LED_RED_GPIO 0 +#endif void app_main() { + led_config(LED_GREEN, LED_GREEN_GPIO, 0); + led_config(LED_RED, LED_RED_GPIO, 0); + console_start(); }