Compare commits

...

14 Commits

Author SHA1 Message Date
Philippe G
3df6568b6a dual vertical VU - release 2020-08-19 00:25:51 -07:00
Philippe G
ca97b8045e GPIO 7 might be used by error - release 2020-08-18 18:11:33 -07:00
Philippe G
5e8a3fd755 better synchronize bass/treble & equalizer
remove large fonts - release

finalize equalizer/tone interaction
2020-08-17 21:01:57 -07:00
philippe44
92fffb7635 Update README.md 2020-08-15 23:44:58 -07:00
philippe44
5e372860c8 Update README.md 2020-08-15 23:43:27 -07:00
philippe44
36c3f0eb49 Update README.md 2020-08-15 23:38:45 -07:00
Philippe G
4ecc9da6d3 take prefs from the correct namespace 2020-08-15 19:25:27 -07:00
Philippe G
45e0dd38df update this $%*!§* plugin for good, this time 2020-08-15 19:09:23 -07:00
Philippe G
3922851129 update plugin! 2020-08-15 19:07:47 -07:00
Philippe G
f08bde8d48 headset, bass/treble, battery from LMS - release 2020-08-15 18:45:59 -07:00
philippe44
a6b57604d3 Update README.md 2020-08-15 16:05:16 -07:00
Philippe G
ca33ff4ba9 fix DAC defaults in KConfig 2020-08-14 10:56:39 -07:00
Philippe G
8b55fa3986 DAC I2S default were wrong - release 2020-08-14 09:23:53 -07:00
Philippe G
e04a631665 typo in config - release 2020-08-13 14:28:11 -07:00
21 changed files with 368 additions and 113 deletions

134
README.md
View File

@@ -86,6 +86,7 @@ The parameter "dac_controlset" allows definition of simple commands to be sent o
```
This is standard JSON notation, so if you are not familiar with it, Google is your best friend. Be aware that the '...' means you can have as many entries as you want, it's not part of the syntax. Every section is optional, but it does not make sense to set i2c in the 'dac_config' parameter and not setting anything here. The parameter 'mode' allows to *or* the register with the value or to *and* it. Don't set 'mode' if you simply want to write. **Note that all values must be decimal**
NB: For well-known configuration, this is ignored
### SPDIF
The NVS parameter "spdif_config" sets the i2s's gpio needed for SPDIF.
@@ -97,6 +98,7 @@ Leave it blank to disable SPDIF usage, you can also define them at compile time
```
bck=<gpio>,ws=<gpio>,do=<gpio>
```
NB: For well-known configuration, this is ignored
### Display
The NVS parameter "display_config" sets the parameters for an optional display. Syntax is
```
@@ -156,12 +158,13 @@ Syntax is:
```
<gpio>=Vcc|GND|amp[:1|0]|ir|jack[:0|1]|green[:0|1]|red[:0|1]|spkfault[:0|1][,<repeated sequence for next GPIO>]
```
You can define the defaults for jack, spkfault leds at compile time but nvs parameter takes precedence except for SqueezeAMP where these are forced at runtime.
You can define the defaults for jack, spkfault leds at compile time but nvs parameter takes precedence except for well-known configurations where these are forced at runtime.
### LED
See §**set_GPIO** for how to set the green and red LEDs. In addition, their brightness can be controlled using the "led_brigthness" parameter. The syntax is
```
[green=0..100][,red=0..100]
```
NB: For well-known configuration, this is ignored
### Rotary Encoder
One rotary encoder is supported, quadrature shift with press. Such encoders usually have 2 pins for encoders (A and B), and common C that must be set to ground and an optional SW pin for press. A, B and SW must be pulled up, so automatic pull-up is provided by ESP32, but you can add your own resistors. A bit of filtering on A and B (~470nF) helps for debouncing which is not made by software.
@@ -278,12 +281,67 @@ There is no good or bad option, it's your choice. Use the NVS parameter "lms_ctr
### Battery / ADC
The NVS parameter "bat_config" sets the ADC1 channel used to measure battery/DC voltage. Scale is a float ratio applied to every sample of the 12 bits ADC. A measure is taken every 10s and an average is made every 5 minutes (not a sliding window). Syntax is
```
channel=0..7,scale=<scale>
channel=0..7,scale=<scale>,cells=<2|3>
```
NB: Set parameter to empty to disable battery reading
NB: Set parameter to empty to disable battery reading. For well-known configuration, this is ignored (except for SqueezeAMP where number of cells is required)
# Configuration
## Setup WiFi
- Boot the esp, look for a new wifi access point showing up and connect to it. Default build ssid and passwords are "squeezelite"/"squeezelite".
- Once connected, navigate to 192.168.4.1
- Wait for the list of access points visible from the device to populate in the web page.
- Choose an access point and enter any credential as needed
- Once connection is established, note down the address the device received; this is the address you will use to configure it going forward
## Setup squeezelite command line (optional)
At this point, the device should have disabled its built-in access point and should be connected to a known WiFi network.
- navigate to the address that was noted in step #1
- Using the list of predefined options, choose the mode in which you want squeezelite to start
- Generate the command
- Add or change any additional command line option (for example player name, etc)
- Activate squeezelite execution: this tells the device to automatiaclly run the command at start
- Update the configuration
- click on the "start toggle" button. This will force a reboot.
- The toggle switch should be set to 'ON' to ensure that squeezelite is active after booting (you might have to fiddle with it a few times)
- You can enable accessto NVS parameters under 'credits'
## Monitor
In addition of the esp-idf serial link monitor option, you can also enable a telnet server (see NVS parameters) where you'll have access to a ton of logs of what's happening inside the WROVER.
## Update Squeezelite
- From the firmware tab, click on "Check for Updates"
- Look for updated binaries
- Select a line
- Click on "Flash!"
- The system will reboot into recovery mode (if not already in that mode), wipe the squeezelite partition and download/flash the selected version
- You can choose a local file or have a local webserver
## Recovery
- From the firmware tab, click on the "Recovery" button. This will reboot the ESP32 into recovery, where additional configuration options are available from the NVS editor
## Additional configuration notes (from the Web UI)
The squeezelite options are very similar to the regular Linux ones. Differences are :
- the output is -o ["BT -n '<sinkname>' "] | [I2S]
- if you've compiled with RESAMPLE option, normal soxr options are available using -R [-u <options>]. Note that anything above LQ or MQ will overload the CPU
- if you've used RESAMPLE16, <options> are (b|l|m)[:i], with b = basic linear interpolation, l = 13 taps, m = 21 taps, i = interpolate filter coefficients
For example, so use a BT speaker named MySpeaker, accept audio up to 192kHz and resample everything to 44100 and use 16 bits resample with medium quality, the command line is:
squeezelite -o "BT -n 'BT <sinkname>'" -b 500:2000 -R -u m -Z 192000 -r "44100-44100"
See squeezlite command line, but keys options are
- Z <rate> : tell LMS what is the max sample rate supported before LMS resamples
- R (see above)
- r "<minrate>-<maxrate>"
- C <sec> : set timeout to switch off amp gpio
- W : activate WAV and AIFF header parsing
# Building everything yourself
## Setting up ESP-IDF
### Docker
You can use docker to build squeezelite-esp32
You can use docker to build squeezelite-esp32 (optional)
First you need to build the Docker container:
```
docker build -t esp-idf .
@@ -301,9 +359,11 @@ If you want to use a more recent version of gcc and IDF (4.0 stable), move to cm
You can install IDF manually on Linux or Windows (using the Subsystem for Linux) following the instructions at: https://www.instructables.com/id/ESP32-Development-on-Windows-Subsystem-for-Linux/
And then copying the i2s.c patch file from this repo over to the esp-idf folder
You also need to use esp-dsp recent version or at least make sure you have this patch https://github.com/espressif/esp-dsp/pull/12/commits/8b082c1071497d49346ee6ed55351470c1cb4264
You also need to use esp-dsp recent version or at least make sure you have this patch https://github.com/espressif/esp-dsp/pull/12/commits/8b082c1071497d49346ee6ed55351470c1cb4264. As of this writing (08.2020), espressif has patched esp-dsp so this is no more needed
## Building Squeezelite-esp32
Don't forget the to choose one of the config files in build_scripts/ and rename it sdkconfig.defaults or sdkconfig as many important WiFi/BT options are set there. The codecs libraries will not be rebuilt by these scripts (it's a tedious process - see below)
### Usng make (deprecated)
MOST IMPORTANT: create the right default config file
- make defconfig
(Note: You can also copy over config files from the build-scripts folder to ./sdkconfig)
@@ -334,55 +394,13 @@ You can also manually download the recovery & initial boot
```
python ${IDF_PATH}/components/esptool_py/esptool/esptool.py --chip esp32 --port ${ESPPORT} --baud ${ESPBAUD} --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect 0xd000 ./build/ota_data_initial.bin 0x1000 ./build/bootloader/bootloader.bin 0x10000 ./build/recovery.bin 0x8000 ./build/partitions.bin
```
# Configuration
1/ setup WiFi
- Boot the esp, look for a new wifi access point showing up and connect to it. Default build ssid and passwords are "squeezelite"/"squeezelite".
- Once connected, navigate to 192.168.4.1
- Wait for the list of access points visible from the device to populate in the web page.
- Choose an access point and enter any credential as needed
- Once connection is established, note down the address the device received; this is the address you will use to configure it going forward
2/ setup squeezelite command line (optional)
At this point, the device should have disabled its built-in access point and should be connected to a known WiFi network.
- navigate to the address that was noted in step #1
- Using the list of predefined options, hoose the mode in which you want squeezelite to start
- Generate the command
- Add or change any additional command line option (for example player name, etc)
- Activate squeezelite execution: this tells the device to automatiaclly run the command at start
- Update the configuration
- click on the "start toggle" button. This will force a reboot.
- The toggle switch should be set to 'ON' to ensure that squeezelite is active after booting
3/ Updating Squeezelite
- From the firmware tab, click on "Check for Updates"
- Look for updated binaries
- Select a line
- Click on "Flash!"
- The system will reboot into recovery mode (if not already in that mode), wipe the squeezelite partition and download/flash the selected version
3/ Recovery
- From the firmware tab, click on the "Recovery" button. This will reboot the ESP32 into recovery, where additional configuration options are available from the NVS editor
# Additional command line notes, configured from the http configuration
The squeezelite options are very similar to the regular Linux ones. Differences are :
- the output is -o ["BT -n '<sinkname>' "] | [I2S]
- if you've compiled with RESAMPLE option, normal soxr options are available using -R [-u <options>]. Note that anything above LQ or MQ will overload the CPU
- if you've used RESAMPLE16, <options> are (b|l|m)[:i], with b = basic linear interpolation, l = 13 taps, m = 21 taps, i = interpolate filter coefficients
For example, so use a BT speaker named MySpeaker, accept audio up to 192kHz and resample everything to 44100 and use 16 bits resample with medium quality, the command line is:
squeezelite -o "BT -n 'BT <sinkname>'" -b 500:2000 -R -u m -Z 192000 -r "44100-44100"
See squeezlite command line, but keys options are
- Z <rate> : tell LMS what is the max sample rate supported before LMS resamples
- R (see above)
- r "<minrate>-<maxrate>"
## Additional misc notes to do you build
### Using cmake
Create you config using 'idf.py menuconfig' then build binaries using 'idf.py all'. It will build the recovery and the application (squeezelite) itself. See the recommended command to upload everything. Otherwise, if you just want to download squeezelite, do
```
python.exe <idf_path>\components\esptool_py\esptool\esptool.py -p COM<n> -b 921600 --before default_reset --after hard_reset write_flash --flash_mode dio --flash_size detect --flash_freq 80m 0x150000 build\squeezelite.bin
```
Use 'idf monitor' to monitor the application (see esp-idf documentation)
## Additional misc notes to do you build (kitchen sink)
- as of this writing, ESP-IDF has a bug int he way the PLL values are calculated for i2s, so you *must* use the i2s.c file in the patch directory
- for codecs libraries, add -mlongcalls if you want to rebuild them, but you should not (use the provided ones in codecs/lib). if you really want to rebuild them, open an issue
- libmad, libflac (no esp's version), libvorbis (tremor - not esp's version), alac work
@@ -393,16 +411,14 @@ See squeezlite command line, but keys options are
#pragma GCC push_options
#pragma GCC optimize ("O0")
#pragma GCC pop_options
- better use helixaac
- 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
- stack consumption can be very high with some codec variants, so set NONTHREADSAFE_PSEUDOSTACK and GLOBAL_STACK_SIZE=32000 and unset VAR_ARRAYS in config.h
- better use helixaac
- set IDF_PATH=/home/esp-idf
- set ESPPORT=COM9
- update flash partition size
- other compiler #define
- use no resampling or set RESAMPLE (soxr) or set RESAMPLE16 for fast fixed 16 bits resampling
- use LOOPBACK (mandatory)
@@ -410,10 +426,8 @@ See squeezlite command line, but keys options are
- LINKALL (mandatory)
- NO_FAAD unless you want to us faad, which currently overloads the CPU
- TREMOR_ONLY (mandatory)
- better use helixaac
- When initially cloning the repo, make sure you do it recursively. For example:
- git clone --recursive https://github.com/sle118/squeezelite-esp32.git
- If you have already cloned the repository and you are getting compile errors on one of the submodules (e.g. telnet), run the following git command in the root of the repository location
- git submodule update --init --recursive

View File

@@ -26,7 +26,7 @@ CONFIG_SPKFAULT_GPIO=-1
CONFIG_SPKFAULT_GPIO_LEVEL=0
CONFIG_BAT_CHANNEL=-1
CONFIG_BAT_SCALE="0"
CONFIG_SDIF_NUM=0
CONFIG_SPDIF_NUM=0
CONFIG_SPDIF_BCK_IO=27
CONFIG_SPDIF_WS_IO=26
CONFIG_SPDIF_DO_IO=-1
@@ -116,10 +116,6 @@ CONFIG_INCLUDE_VORBIS=y
CONFIG_INCLUDE_ALAC=y
CONFIG_A1S=y
CONFIG_SDIF_NUM=0
CONFIG_SPDIF_BCK_IO=-1
CONFIG_SPDIF_WS_IO=-1
CONFIG_SPDIF_DO_IO=-1
CONFIG_A2DP_SINK_NAME="SMSL BT4.2"
CONFIG_A2DP_DEV_NAME="Squeezelite"

View File

@@ -25,13 +25,13 @@ CONFIG_SPKFAULT_GPIO_LEVEL=0
CONFIG_BAT_CHANNEL=-1
CONFIG_BAT_SCALE="0"
CONFIG_I2S_NUM=0
CONFIG_I2S_BCK_IO=33
CONFIG_I2S_WS_IO=25
CONFIG_I2S_DO_IO=32
CONFIG_I2S_BCK_IO=-1
CONFIG_I2S_WS_IO=-1
CONFIG_I2S_DO_IO=-1
CONFIG_I2S_DI_IO=-1
CONFIG_SDIF_NUM=0
CONFIG_SPDIF_BCK_IO=33
CONFIG_SPDIF_WS_IO=25
CONFIG_SPDIF_NUM=0
CONFIG_SPDIF_BCK_IO=-1
CONFIG_SPDIF_WS_IO=-1
CONFIG_SPDIF_DO_IO=-1
CONFIG_MUTE_GPIO=-1
CONFIG_MUTE_GPIO_LEVEL=-1
@@ -121,11 +121,6 @@ CONFIG_INCLUDE_ALAC=y
CONFIG_BASIC_I2C_BT=y
CONFIG_SDIF_NUM=0
CONFIG_SPDIF_BCK_IO=-1
CONFIG_SPDIF_WS_IO=-1
CONFIG_SPDIF_DO_IO=-1
CONFIG_A2DP_SINK_NAME="SMSL BT4.2"
CONFIG_A2DP_DEV_NAME="Squeezelite"
CONFIG_A2DP_CONTROL_DELAY_MS=500

View File

@@ -29,7 +29,7 @@ CONFIG_SPKFAULT_GPIO_LEVEL=0
CONFIG_BAT_CHANNEL=7
CONFIG_BAT_SCALE="20.24"
CONFIG_I2S_NUM=0
CONFIG_SDIF_NUM=0
CONFIG_SPDIF_NUM=0
CONFIG_SPDIF_CONFIG="bck=33,ws=25,do=15"
CONFIG_DAC_CONFIG="model=TAS57xx,bck=33,ws=25,do=32,sda=27,scl=26,mute=14:0"
CONFIG_IDF_TARGET_ESP32=y

View File

@@ -29,7 +29,7 @@ CONFIG_SPKFAULT_GPIO_LEVEL=0
CONFIG_BAT_CHANNEL=7
CONFIG_BAT_SCALE="20.24"
CONFIG_I2S_NUM=0
CONFIG_SDIF_NUM=0
CONFIG_SPDIF_NUM=0
CONFIG_SPDIF_CONFIG="bck=33,ws=25,do=15"
CONFIG_DAC_CONFIG="model=TAS57xx,bck=33,ws=25,do=32,sda=27,scl=26,mute=14"
CONFIG_MUTE_GPIO_LEVEL=-1

View File

@@ -35,12 +35,20 @@ static const struct GDS_FontDef *GuessFont( struct GDS_Device *Device, int FontT
case GDS_FONT_MEDIUM:
default:
return &Font_droid_sans_fallback_15x17;
#ifdef USE_LARGE_FONTS
case GDS_FONT_LARGE:
return &Font_droid_sans_fallback_24x28;
break;
case GDS_FONT_SEGMENT:
if (Device->Height == 32) return &Font_Tarable7Seg_16x32;
else return &Font_Tarable7Seg_32x64;
#else
case GDS_FONT_LARGE:
case GDS_FONT_SEGMENT:
ESP_LOGW(TAG, "large fonts disabled");
return &Font_droid_sans_fallback_15x17;
break;
#endif
}
}

View File

@@ -33,15 +33,27 @@ static struct {
int channel;
float sum, avg, scale;
int count;
int cells;
TimerHandle_t timer;
} battery;
} battery = {
.channel = CONFIG_BAT_CHANNEL,
.cells = 2,
};
/****************************************************************************************
*
*/
int battery_value_svc(void) {
return battery.avg;
return battery.avg;
}
/****************************************************************************************
*
*/
uint8_t battery_level_svc(void) {
// TODO: this is totally incorrect
return battery.avg ? (battery.avg - (3.0 * battery.cells)) / ((4.2 - 3.0) * battery.cells) * 100 : 0;
}
/****************************************************************************************
*
@@ -59,29 +71,30 @@ static void battery_callback(TimerHandle_t xTimer) {
*
*/
void battery_svc_init(void) {
battery.channel = CONFIG_BAT_CHANNEL;
#ifdef CONFIG_BAT_SCALE
battery.scale = atof(CONFIG_BAT_SCALE);
#endif
#ifndef CONFIG_BAT_LOCKED
char *nvs_item = config_alloc_get_default(NVS_TYPE_STR, "bat_config", "n", 0);
if (nvs_item) {
char *p;
char *p;
#ifndef CONFIG_BAT_LOCKED
if ((p = strcasestr(nvs_item, "channel")) != NULL) battery.channel = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(nvs_item, "scale")) != NULL) battery.scale = atof(strchr(p, '=') + 1);
#endif
if ((p = strcasestr(nvs_item, "cells")) != NULL) battery.cells = atof(strchr(p, '=') + 1);
free(nvs_item);
}
#endif
if (battery.channel != -1) {
ESP_LOGI(TAG, "Battery measure channel: %u, scale %f", battery.channel, battery.scale);
adc1_config_width(ADC_WIDTH_BIT_12);
adc1_config_channel_atten(battery.channel, ADC_ATTEN_DB_0);
battery.avg = adc1_get_raw(battery.channel) * battery.scale / 4095.0;
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);
} else {
ESP_LOGI(TAG, "No battery");
}

View File

@@ -17,4 +17,5 @@ extern void (*spkfault_handler_svc)(bool inserted);
extern bool spkfault_svc(void);
extern int battery_value_svc(void);
extern uint8_t battery_level_svc(void);

View File

@@ -585,7 +585,7 @@ void draw_VU(struct GDS_Device * display, const uint8_t *data, int level, int x,
int scale = 8 - GDS_GetDepth(display);
// use "fast" version as we are not beyond screen boundaries
if (visu.rotate) {
if (rotate) {
for (int r = 0; r < width; r++) {
for (int c = VU_HEIGHT; --c >= 0;) {
GDS_DrawPixelFast(display, c + x, r + y, *data++ >> scale);
@@ -594,13 +594,13 @@ void draw_VU(struct GDS_Device * display, const uint8_t *data, int level, int x,
} else {
for (int r = 0; r < width; r++) {
for (int c = 0; c < VU_HEIGHT; c++) {
GDS_DrawPixelFast(display, r + x, c + y, *data++ >> scale);
GDS_DrawPixelFast(display, r + x, c + y, *data++ >> scale);
}
}
}
} else {
// use "fast" version as we are not beyond screen boundaries
if (visu.rotate) {
if (rotate) {
for (int r = 0; r < width; r++) {
for (int c = VU_HEIGHT; --c >= 0;) {
GDS_DrawPixelFast(display, c + x, r + y, grayMap[*data++]);
@@ -609,11 +609,10 @@ void draw_VU(struct GDS_Device * display, const uint8_t *data, int level, int x,
} else {
for (int r = 0; r < width; r++) {
for (int c = 0; c < VU_HEIGHT; c++) {
GDS_DrawPixelFast(display, r + x, c + y, grayMap[*data++]);
GDS_DrawPixelFast(display, r + x, c + y, grayMap[*data++]);
}
}
}
}
// need to manually set dirty flag as DrawPixel does not do it
@@ -966,9 +965,13 @@ static void visu_update(void) {
}
}
} else if (displayer.width / 2 > 3 * VU_WIDTH / 4) {
int width = visu.rotate ? visu.height : visu.width;
draw_VU(display, vu_bitmap, visu.bars[0].current, 0, visu.row, width / 2, visu.rotate);
draw_VU(display, vu_bitmap, visu.bars[1].current, width / 2, visu.row, width / 2, visu.rotate);
if (visu.rotate) {
draw_VU(display, vu_bitmap, visu.bars[0].current, 0, visu.row, visu.height / 2, visu.rotate);
draw_VU(display, vu_bitmap, visu.bars[1].current, 0, visu.row + visu.height / 2, visu.height / 2, visu.rotate);
} else {
draw_VU(display, vu_bitmap, visu.bars[0].current, 0, visu.row, visu.width / 2, visu.rotate);
draw_VU(display, vu_bitmap, visu.bars[1].current, visu.width / 2, visu.row, visu.width / 2, visu.rotate);
}
} else {
int level = (visu.bars[0].current + visu.bars[1].current) / 2;
draw_VU(display, vu_bitmap, level, 0, visu.row, visu.rotate ? visu.height : visu.width, visu.rotate);

View File

@@ -14,6 +14,7 @@
#include "esp_system.h"
#include "esp_timer.h"
#include "esp_wifi.h"
#include "monitor.h"
mutex_type slimp_mutex;
@@ -60,3 +61,11 @@ u16_t get_RSSI(void) {
if (wifidata.primary != 0) return 100 + wifidata.rssi + 30;
else return 0xffff;
}
u16_t get_plugged(void) {
return jack_inserted_svc() ? PLUG_HEADPHONE : 0;
}
u8_t get_battery(void) {
return (battery_level_svc() * 16) / 100;
}

View File

@@ -63,8 +63,13 @@ extern mutex_type slimp_mutex;
#define LOCK_P mutex_lock(slimp_mutex)
#define UNLOCK_P mutex_unlock(slimp_mutex)
// must provide or define as 0xffff
u16_t get_RSSI(void);
// bitmap of plugs status
#define PLUG_LINE_IN 0x01
#define PLUG_LINE_OUT 0x02
#define PLUG_HEADPHONE 0x04
u16_t get_RSSI(void); // must provide or define as 0xffff
u16_t get_plugged(void); // must provide or define as 0x0
u8_t get_battery(void); // must provide 0..15 or define as 0x0
// to be defined to nothing if you don't want to support these
extern struct visu_export_s {

View File

@@ -31,6 +31,7 @@ sure that using rate_delay would fix that
*/
#include "squeezelite.h"
#include "slimproto.h"
#include "esp_pthread.h"
#include "driver/i2s.h"
#include "driver/i2c.h"
@@ -82,6 +83,7 @@ const struct adac_s *adac = &dac_external;
static log_level loglevel;
static bool (*slimp_handler_chain)(u8_t *data, int len);
static bool jack_mutes_amp;
static bool running, isI2SStarted;
static i2s_config_t i2s_config;
@@ -108,6 +110,36 @@ static void (*jack_handler_chain)(bool inserted);
#define I2C_PORT 0
/****************************************************************************************
* AUDO packet handler
*/
static bool handler(u8_t *data, int len){
bool res = true;
if (!strncmp((char*) data, "audo", 4)) {
struct audo_packet *pkt = (struct audo_packet*) data;
// 0 = headphone (internal speakers off), 1 = sub out,
// 2 = always on (internal speakers on), 3 = always off
if (jack_mutes_amp != (pkt->config == 0)) {
jack_mutes_amp = pkt->config == 0;
config_set_value(NVS_TYPE_STR, "jack_mutes_amp", jack_mutes_amp ? "y" : "n");
if (jack_mutes_amp && jack_inserted_svc()) adac->speaker(false);
else adac->speaker(true);
}
LOG_INFO("got AUDO %02x", pkt->config);
} else {
res = false;
}
// chain protocol handlers (bitwise or is fine)
if (*slimp_handler_chain) res |= (*slimp_handler_chain)(data, len);
return res;
}
/****************************************************************************************
* jack insertion handler
*/
@@ -164,6 +196,10 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
int silent_do = -1;
char *p;
esp_err_t res;
// chain SLIMP handlers
slimp_handler_chain = slimp_handler;
slimp_handler = handler;
p = config_alloc_get_default(NVS_TYPE_STR, "jack_mutes_amp", "n", 0);
jack_mutes_amp = (strcmp(p,"1") == 0 ||strcasecmp(p,"y") == 0);
@@ -192,7 +228,7 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
char *dac_config = config_alloc_get_str("dac_config", CONFIG_DAC_CONFIG, "model=i2s,bck=" STR(CONFIG_I2S_BCK_IO)
",ws=" STR(CONFIG_I2S_WS_IO) ",do=" STR(CONFIG_I2S_DO_IO)
",sda=" STR(CONFIG_I2C_SDA) ",scl=" STR(CONFIG_I2C_SCL)
",mute" STR(CONFIG_MUTE_GPIO));
",mute=" STR(CONFIG_MUTE_GPIO));
i2s_pin_config_t i2s_dac_pin, i2s_spdif_pin;
set_i2s_pin(spdif_config, &i2s_spdif_pin);
@@ -248,7 +284,7 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
char model[32] = "i2s";
if ((p = strcasestr(dac_config, "model")) != NULL) sscanf(p, "%*[^=]=%31[^,]", model);
if ((p = strcasestr(dac_config, "mute")) != NULL) {
char mute[8];
char mute[8] = "";
sscanf(p, "%*[^=]=%7[^,]", mute);
mute_control.gpio = atoi(mute);
if ((p = strchr(mute, ':')) != NULL) mute_control.active = atoi(p + 1);
@@ -256,7 +292,7 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
for (int i = 0; adac == &dac_external && dac_set[i]; i++) if (strcasestr(dac_set[i]->model, model)) adac = dac_set[i];
res = adac->init(dac_config, I2C_PORT, &i2s_config) ? ESP_OK : ESP_FAIL;
res |= i2s_driver_install(CONFIG_I2S_NUM, &i2s_config, 0, NULL);
res |= i2s_set_pin(CONFIG_I2S_NUM, &i2s_dac_pin);

View File

@@ -190,6 +190,7 @@ static void sendSTAT(const char *event, u32_t server_timestamp) {
packN(&pkt.bytes_received_L, (u64_t)status.stream_bytes & 0xffffffff);
#if EMBEDDED
packn(&pkt.signal_strength, get_RSSI());
packn(&pkt.voltage, (get_battery() << 4) | get_plugged());
#else
pkt.signal_strength = 0xffff;
#endif
@@ -197,7 +198,6 @@ static void sendSTAT(const char *event, u32_t server_timestamp) {
packN(&pkt.output_buffer_size, status.output_size);
packN(&pkt.output_buffer_fullness, status.output_full);
packN(&pkt.elapsed_seconds, ms_played / 1000);
// voltage;
packN(&pkt.elapsed_milliseconds, ms_played);
pkt.server_timestamp = server_timestamp; // keep this is server format - don't unpack/pack
// error_code;

View File

@@ -178,6 +178,12 @@ struct codc_packet {
u8_t pcm_endianness;
};
// initially Boom
struct audo_packet {
char opcode[4];
u8_t config;
};
#ifndef SUN
#pragma pack(pop)
#else

View File

@@ -97,17 +97,17 @@ menu "Squeezelite-ESP32"
I2S dma channel to use.
config I2S_BCK_IO
int "I2S Bit clock GPIO number. "
default 33
default -1
help
I2S Bit Clock gpio pin to use.
config I2S_WS_IO
int "I2S Word Select GPIO number. "
default 25
default -1
help
I2S Word Select gpio pin to use.
config I2S_DO_IO
int "I2S Data Output GPIO number. "
default 32
default -1
help
I2S data output gpio pin to use.
config I2S_DI_IO

View File

@@ -333,6 +333,9 @@ void register_default_nvs(){
ESP_LOGD(TAG,"Registering default value for key %s", "dac_controlset");
config_set_default(NVS_TYPE_STR, "dac_controlset", "", 0);
ESP_LOGD(TAG,"Registering default value for key %s", "jack_mutes_amp");
config_set_default(NVS_TYPE_STR, "jack_mutes_amp", "n", 0);
ESP_LOGD(TAG,"Registering default value for key %s", "bat_config");
config_set_default(NVS_TYPE_STR, "bat_config", "", 0);

Binary file not shown.

View File

@@ -9,17 +9,95 @@ use List::Util qw(min);
use Slim::Utils::Log;
use Slim::Utils::Prefs;
my $sprefs = preferences('server');
my $prefs = preferences('plugin.squeezeesp32');
my $log = logger('plugin.squeezeesp32');
{
__PACKAGE__->mk_accessor('rw', 'tone_update');
}
sub new {
my $class = shift;
my $client = $class->SUPER::new(@_);
$client->init_accessor(
tone_update => 0,
);
return $client;
}
our $defaultPrefs = {
'analogOutMode' => 0,
'bass' => 0,
'treble' => 0,
'lineInAlwaysOn' => 0,
'lineInLevel' => 50,
'menuItem' => [qw(
NOW_PLAYING
BROWSE_MUSIC
RADIO
PLUGIN_MY_APPS_MODULE_NAME
PLUGIN_APP_GALLERY_MODULE_NAME
FAVORITES
GLOBAL_SEARCH
PLUGIN_LINE_IN
PLUGINS
SETTINGS
SQUEEZENETWORK_CONNECT
)],
};
my $handlersAdded;
sub model { 'squeezeesp32' }
sub modelName { 'SqueezeESP32' }
sub hasIR { 0 }
sub hasScrolling { 1 }
sub hasIR { 1 }
# TODO: add in settings when ready
sub hasLineIn { 0 }
sub hasHeadSubOut { 1 }
sub maxTreble { 20 }
sub minTreble { -13 }
sub maxBass { 20 }
sub minBass { -13 }
sub init {
my $client = shift;
if (!$handlersAdded) {
# Add a handler for line-in/out status changes
Slim::Networking::Slimproto::addHandler( LIOS => \&lineInOutStatus );
# Create a new event for sending LIOS updates
Slim::Control::Request::addDispatch(
['lios', '_state'],
[1, 0, 0, undef],
);
Slim::Control::Request::addDispatch(
['lios', 'linein', '_state'],
[1, 0, 0, undef],
);
Slim::Control::Request::addDispatch(
['lios', 'lineout', '_state'],
[1, 0, 0, undef],
);
$handlersAdded = 1;
}
$client->SUPER::init(@_);
$client->config_artwork();
$client->config_artwork;
}
sub initPrefs {
my $client = shift;
$sprefs->client($client)->init($defaultPrefs);
$client->SUPER::initPrefs;
}
# Allow the player to define it's display width (and probably more)
@@ -50,12 +128,42 @@ sub playerSettingsFrame {
$client->SUPER::playerSettingsFrame($data_ref);
}
sub hasScrolling {
return 1;
sub bass {
my ($client, $new) = @_;
my $value = $client->SUPER::bass($new);
$client->update_equalizer($value, [2, 1, 3]) if defined $new;
return $value;
}
sub hasIR {
return 1;
sub treble {
my ($client, $new) = @_;
my $value = $client->SUPER::treble($new);
$client->update_equalizer($value, [8, 9, 7]) if defined $new;
return $value;
}
sub update_equalizer {
my ($client, $value, $index) = @_;
return if $client->tone_update;
my $equalizer = $prefs->client($client)->get('equalizer');
$equalizer->[$index->[0]] = $value;
$equalizer->[$index->[1]] = int($value / 2 + 0.5);
$equalizer->[$index->[2]] = int($value / 4 + 0.5);
$prefs->client($client)->set('equalizer', $equalizer);
}
sub update_tones {
my ($client, $equalizer) = @_;
$client->tone_update(1);
$sprefs->client($client)->set('bass', int(($equalizer->[1] * 2 + $equalizer->[2] + $equalizer->[3] * 4) / 7 + 0.5));
$sprefs->client($client)->set('treble', int(($equalizer->[7] * 4 + $equalizer->[8] + $equalizer->[9] * 2) / 7 + 0.5));
$client->tone_update(0);
}
sub update_artwork {
@@ -131,4 +239,61 @@ sub reconnect {
$client->SUPER::reconnect(@_);
}
# Change the analog output mode between headphone and sub-woofer
# If no mode is specified, the value of the client's analogOutMode preference is used.
# Otherwise the mode is temporarily changed to the given value without altering the preference.
sub setAnalogOutMode {
my $client = shift;
# 0 = headphone (internal speakers off), 1 = sub out,
# 2 = always on (internal speakers on), 3 = always off
my $mode = shift;
if (! defined $mode) {
$mode = $sprefs->client($client)->get('analogOutMode');
}
my $data = pack('C', $mode);
$client->sendFrame('audo', \$data);
}
# LINE_IN 0x01
# LINE_OUT 0x02
# HEADPHONE 0x04
sub lineInConnected {
my $state = Slim::Networking::Slimproto::voltage(shift) || return 0;
return $state & 0x01 || 0;
}
sub lineOutConnected {
my $state = Slim::Networking::Slimproto::voltage(shift) || return 0;
return $state & 0x02 || 0;
}
sub lineInOutStatus {
my ( $client, $data_ref ) = @_;
my $state = unpack 'n', $$data_ref;
my $oldState = {
in => $client->lineInConnected(),
out => $client->lineOutConnected(),
};
Slim::Networking::Slimproto::voltage( $client, $state );
Slim::Control::Request::notifyFromArray( $client, [ 'lios', $state ] );
if ($oldState->{in} != $client->lineInConnected()) {
Slim::Control::Request::notifyFromArray( $client, [ 'lios', 'linein', $client->lineInConnected() ] );
if ( Slim::Utils::PluginManager->isEnabled('Slim::Plugin::LineIn::Plugin')) {
Slim::Plugin::LineIn::Plugin::lineInItem($client, 1);
}
}
if ($oldState->{out} != $client->lineOutConnected()) {
Slim::Control::Request::notifyFromArray( $client, [ 'lios', 'lineout', $client->lineOutConnected() ] );
}
}
1;

View File

@@ -73,6 +73,7 @@ sub handler {
$equalizer->[$i] = $paramRef->{"pref_equalizer.$i"} || 0;
}
$cprefs->set('equalizer', $equalizer);
$client->update_tones($equalizer);
}
if ($client->displayWidth) {

View File

@@ -10,6 +10,6 @@
<name>PLUGIN_SQUEEZEESP32</name>
<description>PLUGIN_SQUEEZEESP32_DESC</description>
<module>Plugins::SqueezeESP32::Plugin</module>
<version>0.94</version>
<version>0.103</version>
<creator>Philippe</creator>
</extensions>

View File

@@ -1,10 +1,10 @@
<?xml version='1.0' standalone='yes'?>
<extensions>
<plugins>
<plugin version="0.94" name="SqueezeESP32" minTarget="7.9" maxTarget="*">
<plugin version="0.103" name="SqueezeESP32" minTarget="7.9" maxTarget="*">
<link>https://github.com/sle118/squeezelite-esp32</link>
<creator>Philippe</creator>
<sha>a9bf10b47d13508ba051e4067cdabc2c283f4824</sha>
<sha>d07bb3b0a283fbde50e5533dca695a4505971f03</sha>
<email>philippe_44@outlook.com</email>
<desc lang="EN">SqueezeESP32 additional player id (100)</desc>
<url>http://github.com/sle118/squeezelite-esp32/raw/master/plugin/SqueezeESP32.zip</url>