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
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 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_a2dp_api.h"
#include "esp_avrc_api.h"
#include "nvs.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
@@ -39,9 +40,11 @@
#define BT_RC_CT_TAG "RCCT"
#ifndef CONFIG_BT_SINK_NAME
#define CONFIG_BT_SINK_NAME "unavailable"
#define CONFIG_BT_SINK_NAME "default"
#endif
extern char current_namespace[];
/* event for handler "bt_av_hdl_stack_up */
enum {
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) {
case BT_APP_EVT_STACK_UP: {
/* set up device name */
char *dev_name = CONFIG_BT_SINK_NAME;
esp_bt_dev_set_device_name(dev_name);
nvs_handle nvs;
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);

View File

@@ -7,5 +7,6 @@
# 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

View File

@@ -13,7 +13,8 @@ CFLAGS += -O3 -DLINKALL -DLOOPBACK -DNO_FAAD -DRESAMPLE16 -DEMBEDDED -DTREMOR_ON
-I$(COMPONENT_PATH)/../tools \
-I$(COMPONENT_PATH)/../codecs/inc/opus \
-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

View File

@@ -21,6 +21,7 @@
#include "squeezelite.h"
#include "bt_app_sink.h"
#include "airplay_sink.h"
#define LOCK_O mutex_lock(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) {
case BT_SINK_CONNECTED:
output.external = true;
output.state = OUTPUT_STOPPED;
LOG_INFO("BT sink started");
break;
case BT_SINK_DISCONNECTED:
output.external = false;
output.state = OUTPUT_OFF;
LOG_INFO("BT sink stopped");
break;
case BT_SINK_PLAY:
output.state = OUTPUT_EXTERNAL;
output.state = OUTPUT_RUNNING;
LOG_INFO("BT sink playing");
break;
case BT_SINK_PAUSE:
@@ -138,4 +141,12 @@ void register_other(void) {
LOG_WARN("Cannot be a BT sink and source");
}
#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);
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_RUNNING) led_on(LED_GREEN);
else if (output.state == OUTPUT_RUNNING) led_on(LED_GREEN);
}
state = output.state;

View File

@@ -371,6 +371,7 @@ static void process_strm(u8_t *pkt, int len) {
sendSTAT("STMc", 0);
sentSTMu = sentSTMo = sentSTMl = false;
LOCK_O;
output.external = false;
output.threshold = strm->output_threshold;
output.next_replay_gain = unpackN(&strm->replay_gain);
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)) {
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) {
_sendSTMu = true;
@@ -721,7 +722,7 @@ static void slimproto_run() {
output.state = OUTPUT_OFF;
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;
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
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
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_format format;
const char *device;
bool external;
#if ALSA
unsigned buffer;
unsigned period;

View File

@@ -229,7 +229,7 @@ menu "Squeezelite-ESP32"
config BT_SINK_NAME
depends on BT_SINK
string "Name of Bluetooth A2DP device"
default "ESP32"
default "ESP32-BT"
help
This is the name of the bluetooth speaker that will be broadcasted
config BT_SINK_PIN
@@ -237,8 +237,20 @@ menu "Squeezelite-ESP32"
int "Bluetooth PIN code"
default 1234
config AIRPLAY_SINK
bool "AirPlay receiver (not availabe now)"
default n
bool "AirPlay receiver"
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