tweak BT + start to add AirPlay

This commit is contained in:
philippe44
2019-08-16 23:22:46 -07:00
parent c3543bcf25
commit 2a770483a1
12 changed files with 155 additions and 15 deletions

View File

@@ -20,6 +20,11 @@ nvs_set autoexec2 str -v "squeezelite -o I2S -b 500:2000 -d all=info -m ESP32"
nvs_set autoexec u8 -v 1 nvs_set autoexec u8 -v 1
4/ set bluetooth & airplaysink name (if not set in menuconfig)
nvs_set bt_sink_name str -v "<name>"
nvs_set airplay_sink_name str -v "<name>"
The "join" and "squeezelite" commands can also be typed at the prompt to start manually. Use "help" to see the list. The "join" and "squeezelite" commands can also be typed at the prompt to start manually. Use "help" to see the list.
The squeezelite options are very similar to the regular Linux ones. Differences are : The squeezelite options are very similar to the regular Linux ones. Differences are :

View File

@@ -0,0 +1,65 @@
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include "mdns.h"
#include "nvs.h"
#include "tcpip_adapter.h"
#include "esp_log.h"
#include "esp_console.h"
#include "esp_pthread.h"
#include "esp_system.h"
#include "freertos/timers.h"
#include "airplay_sink.h"
#include "trace.h"
static const char * TAG = "platform";
extern char current_namespace[];
void airplay_sink_init(void) {
const char *hostname;
char *airplay_name, sink_name[32] = CONFIG_AIRPLAY_NAME;
nvs_handle nvs;
tcpip_adapter_get_hostname(TCPIP_ADAPTER_IF_STA, &hostname);
//initialize mDNS
ESP_ERROR_CHECK( mdns_init() );
ESP_ERROR_CHECK( mdns_hostname_set(hostname) );
//structure with TXT records
mdns_txt_item_t serviceTxtData[] = {
{"am", "esp32"},
{"tp", "UDP"},
{"sm","false"},
{"sv","false"},
{"ek","1"},
{"et","0,1"},
{"md","0,1,2"},
{"cn","0,1"},
{"ch","2"},
{"ss","16"},
{"sr","44100"},
{"vn","3"},
{"txtvers","1"},
};
if (nvs_open(current_namespace, NVS_READONLY, &nvs) == ESP_OK) {
size_t len = 31;
nvs_get_str(nvs, "airplay_sink_name", sink_name, &len);
nvs_close(nvs);
}
// AirPlay wants mDNS name to be MAC@name
uint8_t mac[6];
esp_read_mac(mac, ESP_MAC_WIFI_STA);
asprintf(&airplay_name, "%02X%02X%02X%02X%02X%02X@%s", mac[3], mac[4], mac[5], mac[3], mac[4], mac[5], sink_name);
ESP_LOGI(TAG, "mdns hostname set to: [%s] with servicename %s", hostname, sink_name);
//initialize service
ESP_ERROR_CHECK( mdns_service_add(airplay_name, "_raop", "_tcp", 6000, serviceTxtData, sizeof(serviceTxtData) / sizeof(mdns_txt_item_t)) );
free(airplay_name);
}

View File

@@ -0,0 +1,22 @@
/*
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.
*/
#ifndef __AIRPLAY_SINK_H__
#define __AIRPLAY_SINK_H__
#include <stdint.h>
//typedef enum { BT_SINK_CONNECTED, BT_SINK_DISCONNECTED, BT_SINK_PLAY, BT_SINK_STOP, BT_SINK_PAUSE,
//BT_SINK_RATE, BT_SINK_VOLUME, } bt_sink_cmd_t;
/**
* @brief init sink mode (need to be provided)
*/
void airplay_sink_init(void);
#endif /* __AIRPLAY_SINK_H__*/

View File

@@ -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.
#
CFLAGS += -I$(COMPONENT_PATH)/../tools

View File

@@ -21,6 +21,7 @@
#include "esp_gap_bt_api.h" #include "esp_gap_bt_api.h"
#include "esp_a2dp_api.h" #include "esp_a2dp_api.h"
#include "esp_avrc_api.h" #include "esp_avrc_api.h"
#include "nvs.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
@@ -39,9 +40,11 @@
#define BT_RC_CT_TAG "RCCT" #define BT_RC_CT_TAG "RCCT"
#ifndef CONFIG_BT_SINK_NAME #ifndef CONFIG_BT_SINK_NAME
#define CONFIG_BT_SINK_NAME "unavailable" #define CONFIG_BT_SINK_NAME "default"
#endif #endif
extern char current_namespace[];
/* event for handler "bt_av_hdl_stack_up */ /* event for handler "bt_av_hdl_stack_up */
enum { enum {
BT_APP_EVT_STACK_UP = 0, BT_APP_EVT_STACK_UP = 0,
@@ -449,8 +452,16 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param)
switch (event) { switch (event) {
case BT_APP_EVT_STACK_UP: { case BT_APP_EVT_STACK_UP: {
/* set up device name */ /* set up device name */
char *dev_name = CONFIG_BT_SINK_NAME; nvs_handle nvs;
esp_bt_dev_set_device_name(dev_name); char dev_name[32] = CONFIG_BT_SINK_NAME;
if (nvs_open(current_namespace, NVS_READONLY, &nvs) == ESP_OK) {
size_t len = 31;
nvs_get_str(nvs, "bt_sink_name", dev_name, &len);
nvs_close(nvs);
}
esp_bt_dev_set_device_name(dev_name);
esp_bt_gap_register_callback(bt_app_gap_cb); esp_bt_gap_register_callback(bt_app_gap_cb);

View File

@@ -7,5 +7,6 @@
# please read the SDK documents if you need to do this. # please read the SDK documents if you need to do this.
# #
CFLAGS += -I$(COMPONENT_PATH)/../tools CFLAGS += -I$(COMPONENT_PATH)/../tools
#CFLAGS += -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG #CFLAGS += -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG

View File

@@ -13,7 +13,8 @@ CFLAGS += -O3 -DLINKALL -DLOOPBACK -DNO_FAAD -DRESAMPLE16 -DEMBEDDED -DTREMOR_ON
-I$(COMPONENT_PATH)/../tools \ -I$(COMPONENT_PATH)/../tools \
-I$(COMPONENT_PATH)/../codecs/inc/opus \ -I$(COMPONENT_PATH)/../codecs/inc/opus \
-I$(COMPONENT_PATH)/../codecs/inc/opusfile \ -I$(COMPONENT_PATH)/../codecs/inc/opusfile \
-I$(COMPONENT_PATH)/../driver_bt -I$(COMPONENT_PATH)/../driver_bt \
-I$(COMPONENT_PATH)/../airplay
# -I$(COMPONENT_PATH)/../codecs/inc/faad2 # -I$(COMPONENT_PATH)/../codecs/inc/faad2

View File

@@ -21,6 +21,7 @@
#include "squeezelite.h" #include "squeezelite.h"
#include "bt_app_sink.h" #include "bt_app_sink.h"
#include "airplay_sink.h"
#define LOCK_O mutex_lock(outputbuf->mutex) #define LOCK_O mutex_lock(outputbuf->mutex)
#define UNLOCK_O mutex_unlock(outputbuf->mutex) #define UNLOCK_O mutex_unlock(outputbuf->mutex)
@@ -92,15 +93,17 @@ static void bt_sink_cmd_handler(bt_sink_cmd_t cmd, ...)
switch(cmd) { switch(cmd) {
case BT_SINK_CONNECTED: case BT_SINK_CONNECTED:
output.external = true;
output.state = OUTPUT_STOPPED; output.state = OUTPUT_STOPPED;
LOG_INFO("BT sink started"); LOG_INFO("BT sink started");
break; break;
case BT_SINK_DISCONNECTED: case BT_SINK_DISCONNECTED:
output.external = false;
output.state = OUTPUT_OFF; output.state = OUTPUT_OFF;
LOG_INFO("BT sink stopped"); LOG_INFO("BT sink stopped");
break; break;
case BT_SINK_PLAY: case BT_SINK_PLAY:
output.state = OUTPUT_EXTERNAL; output.state = OUTPUT_RUNNING;
LOG_INFO("BT sink playing"); LOG_INFO("BT sink playing");
break; break;
case BT_SINK_PAUSE: case BT_SINK_PAUSE:
@@ -138,4 +141,12 @@ void register_other(void) {
LOG_WARN("Cannot be a BT sink and source"); LOG_WARN("Cannot be a BT sink and source");
} }
#endif #endif
#ifdef CONFIG_AIRPLAY_SINK
if (!strcasestr(output.device, "BT ")) {
airplay_sink_init();
LOG_INFO("Initializing AirPlay sink");
} else {
LOG_WARN("Cannot be an AirPlay sink and BT source");
}
#endif
} }

View File

@@ -417,7 +417,7 @@ static void *output_thread_i2s() {
LOG_INFO("Output state is %d", output.state); LOG_INFO("Output state is %d", output.state);
if (output.state == OUTPUT_OFF) led_blink(LED_GREEN, 100, 2500); if (output.state == OUTPUT_OFF) led_blink(LED_GREEN, 100, 2500);
else if (output.state == OUTPUT_STOPPED) led_blink(LED_GREEN, 200, 1000); else if (output.state == OUTPUT_STOPPED) led_blink(LED_GREEN, 200, 1000);
else if (output.state >= OUTPUT_RUNNING) led_on(LED_GREEN); else if (output.state == OUTPUT_RUNNING) led_on(LED_GREEN);
} }
state = output.state; state = output.state;

View File

@@ -371,6 +371,7 @@ static void process_strm(u8_t *pkt, int len) {
sendSTAT("STMc", 0); sendSTAT("STMc", 0);
sentSTMu = sentSTMo = sentSTMl = false; sentSTMu = sentSTMo = sentSTMl = false;
LOCK_O; LOCK_O;
output.external = false;
output.threshold = strm->output_threshold; output.threshold = strm->output_threshold;
output.next_replay_gain = unpackN(&strm->replay_gain); output.next_replay_gain = unpackN(&strm->replay_gain);
output.fade_mode = strm->transition_type - '0'; output.fade_mode = strm->transition_type - '0';
@@ -703,7 +704,7 @@ static void slimproto_run() {
if (_start_output && (output.state == OUTPUT_STOPPED || output.state == OUTPUT_OFF)) { if (_start_output && (output.state == OUTPUT_STOPPED || output.state == OUTPUT_OFF)) {
output.state = OUTPUT_BUFFER; output.state = OUTPUT_BUFFER;
} }
if (output.state == OUTPUT_RUNNING && !sentSTMu && status.output_full == 0 && status.stream_state <= DISCONNECT && if (!output.external && output.state == OUTPUT_RUNNING && !sentSTMu && status.output_full == 0 && status.stream_state <= DISCONNECT &&
_decode_state == DECODE_STOPPED) { _decode_state == DECODE_STOPPED) {
_sendSTMu = true; _sendSTMu = true;
@@ -721,7 +722,7 @@ static void slimproto_run() {
output.state = OUTPUT_OFF; output.state = OUTPUT_OFF;
LOG_DEBUG("output timeout"); LOG_DEBUG("output timeout");
} }
if (output.state == OUTPUT_RUNNING && now - status.last > 1000) { if (!output.external && output.state == OUTPUT_RUNNING && now - status.last > 1000) {
_sendSTMt = true; _sendSTMt = true;
status.last = now; status.last = now;
} }

View File

@@ -634,7 +634,7 @@ bool resample_init(char *opt);
// output.c output_alsa.c output_pa.c output_pack.c // output.c output_alsa.c output_pa.c output_pack.c
typedef enum { OUTPUT_OFF = -1, OUTPUT_STOPPED = 0, OUTPUT_BUFFER, OUTPUT_RUNNING, typedef enum { OUTPUT_OFF = -1, OUTPUT_STOPPED = 0, OUTPUT_BUFFER, OUTPUT_RUNNING,
OUTPUT_PAUSE_FRAMES, OUTPUT_SKIP_FRAMES, OUTPUT_START_AT, OUTPUT_EXTERNAL } output_state; OUTPUT_PAUSE_FRAMES, OUTPUT_SKIP_FRAMES, OUTPUT_START_AT } output_state;
#if DSD #if DSD
typedef enum { PCM, DOP, DSD_U8, DSD_U16_LE, DSD_U32_LE, DSD_U16_BE, DSD_U32_BE, DOP_S24_LE, DOP_S24_3LE } dsd_format; typedef enum { PCM, DOP, DSD_U8, DSD_U16_LE, DSD_U32_LE, DSD_U16_BE, DSD_U32_BE, DOP_S24_LE, DOP_S24_3LE } dsd_format;
@@ -654,6 +654,7 @@ struct outputstate {
output_state state; output_state state;
output_format format; output_format format;
const char *device; const char *device;
bool external;
#if ALSA #if ALSA
unsigned buffer; unsigned buffer;
unsigned period; unsigned period;

View File

@@ -229,7 +229,7 @@ menu "Squeezelite-ESP32"
config BT_SINK_NAME config BT_SINK_NAME
depends on BT_SINK depends on BT_SINK
string "Name of Bluetooth A2DP device" string "Name of Bluetooth A2DP device"
default "ESP32" default "ESP32-BT"
help help
This is the name of the bluetooth speaker that will be broadcasted This is the name of the bluetooth speaker that will be broadcasted
config BT_SINK_PIN config BT_SINK_PIN
@@ -237,8 +237,20 @@ menu "Squeezelite-ESP32"
int "Bluetooth PIN code" int "Bluetooth PIN code"
default 1234 default 1234
config AIRPLAY_SINK config AIRPLAY_SINK
bool "AirPlay receiver (not availabe now)" bool "AirPlay receiver"
default n default y
config AIRPLAY_NAME
depends on AIRPLAY_SINK
string "Name of AirPlay device"
default "ESP32-AirPlay"
help
This is the name of the AirPlay speaker that will be broadcasted
config AIRPLAY_PORT
depends on AIRPLAY_SINK
string "AirPlay listening port"
default 5000
help
AirPlay service listening port
endmenu endmenu
endmenu endmenu