diff options
author | Slendi <slendi@socopon.com> | 2024-02-04 20:54:38 +0200 |
---|---|---|
committer | Slendi <slendi@socopon.com> | 2024-02-04 20:54:38 +0200 |
commit | 9664aa441fcce8629dd00bc3b458641e514c150f (patch) | |
tree | eaa3e6996bfc5b792a73e1451148c590f6add511 |
Signed-off-by: Slendi <slendi@socopon.com>
-rw-r--r-- | A2DPSwitcher.ino | 90 | ||||
-rw-r--r-- | LICENSE.txt | 14 | ||||
-rw-r--r-- | README.md | 40 | ||||
-rw-r--r-- | config.h | 20 |
4 files changed, 164 insertions, 0 deletions
diff --git a/A2DPSwitcher.ino b/A2DPSwitcher.ino new file mode 100644 index 0000000..f782c36 --- /dev/null +++ b/A2DPSwitcher.ino @@ -0,0 +1,90 @@ +#include <BluetoothA2DPSink.h> + +#include "config.h" + +class A2DPSink : public BluetoothA2DPSink { +protected: + void audio_data_callback(const uint8_t *data, uint32_t len) { + // FIXME: Figure out why the fuck the data received from Android starts being at the same level after like + // 40% and correct for that. + + // Could use this for knowing when to override bluetooth. If no audio data is coming through, override. + // The media approach seems nicer though most of the time because if your mum calls and you're blasing music, + // the other people wouldn't have to hear her yell at you for not taking the trash out. So, + // FIXME: Add a switch for toggling between those two modes and implement the method described above. + BluetoothA2DPSink::audio_data_callback(data, len); + } +}; + +A2DPSink sink; + +bool music_playing = false; + +void callback_avrc_state(esp_avrc_playback_stat_t state) { + music_playing = state == ESP_AVRC_PLAYBACK_PLAYING; +} + +void setup() { + Serial.begin(9600); + + pinMode(PIN_SWITCH, OUTPUT); + pinMode(PIN_BUTTON, INPUT); + pinMode(LED_BUILTIN, OUTPUT); + + static i2s_config_t const i2s_config { + .mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_TX), + .sample_rate = 44100, // Corrected by info from bluetooth. + .bits_per_sample = (i2s_bits_per_sample_t) 32, // The DAC module will only take the 8bits from MSB. + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, + .communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_MSB, + .intr_alloc_flags = 0, // Default interrupt priority. + .dma_buf_count = 8, + .dma_buf_len = 64, + .use_apll = false, + .tx_desc_auto_clear = true, + }; + sink.set_i2s_config(i2s_config); + + static i2s_pin_config_t const i2s_pin_config { + .bck_io_num = I2S_BCLK_SCK, + .ws_io_num = I2S_WS, + .data_out_num = I2S_SD, + .data_in_num = -1, + }; + sink.set_pin_config(i2s_pin_config); + + sink.set_avrc_rn_playstatus_callback(&callback_avrc_state); + sink.set_discoverability(ESP_BT_NON_DISCOVERABLE); + + // Start the thingy! + sink.start(NAME, false); +} + +// If 0, then device is not discoverable. +unsigned long time_discover = 0; +void disconnect_peer() { + if (!sink.is_connected()) { + sink.set_discoverability(ESP_BT_GENERAL_DISCOVERABLE); + time_discover = millis(); + return; + } + + sink.set_connected(false); +} + +void loop() { + if (digitalRead(PIN_BUTTON)) + sink.debounce(disconnect_peer, 500); + + if (sink.is_connected() && music_playing) + digitalWrite(PIN_SWITCH, 1); + else + digitalWrite(PIN_SWITCH, 0); + + if (time_discover != 0 && millis() - time_discover >= TIME_DISCOVERABLE) { + time_discover = 0; + sink.set_discoverability(ESP_BT_NON_DISCOVERABLE); + } + + digitalWrite(LED_BUILTIN, time_discover > 0); +} diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..04acc06 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,14 @@ +A2DPSwitcher - An audio source switcher with Bluetooth +Copyright (C) 2024 Slendi <slendi@socopon.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, version 3. + +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 <https://www.gnu.org/licenses/>. diff --git a/README.md b/README.md new file mode 100644 index 0000000..2dc53eb --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +# A2DP Switcher + +This project allows you to connect via a Bluetooth Classic A2DP connection to +your speakers. It does this by using a multiplexer which switches the output +from a DAC automatically when media is playing on the Bluetooth, and back to +whatever device you would normally have your speakers connected to, for +example, a computer. + +## Installation + +### Prerequisites + +- Some sort of DAC, such as a PCM5102A module; +- An ESP32; +- Some Arduino IDE knowledge. +- Optionally, if you do not want a wiring mess, a PCB which you can find here. + +### Configuration + +If you want to change pin configurations or bluetooth settings, edit the +"config.h" file. + +### Building + +Go to Tools -> Boards -> Boards Manager. Find the `esp32` package by Espressif +Systems and install that. + +Get the `ESP32-A2DP` library at https://github.com/pschatzmann/ESP32-A2DP. +Go to Code -> Download ZIP. In Arduino IDE, go to Sketch -> Load library -> Add +ZIP library and select the downloaded ZIP. + +### Flashing + +Go to Tools and select your ESP32 board, and on Port, select the port your board +is on. Hit Ctrl-U or press the Upload button. + +## License + +This project is licensed under the GNU GPLv3 license. For more information go to +[LICENSE.txt](./LICENSE.txt) at the root of the repository. diff --git a/config.h b/config.h new file mode 100644 index 0000000..373fab2 --- /dev/null +++ b/config.h @@ -0,0 +1,20 @@ +#ifndef CONFIG_H +#define CONFIG_H + +// The pin which connects to the multiplexer. +static constexpr int const PIN_SWITCH = GPIO_NUM_12; +// The pin for disconnecting peers or turning on discovery. +static constexpr int const PIN_BUTTON = GPIO_NUM_15; +// The Bluetooth name of the device. +static constexpr char const *NAME = "A2DP Junction"; +// The amount of time the device should remain discoverable for after pressing the button. +static constexpr unsigned long TIME_DISCOVERABLE = 20 * 1000; + +// I2S Bit Clock/Continuous Serial Clock pin +static constexpr int I2S_BCLK_SCK = GPIO_NUM_18; +// I2S Word Select pin +static constexpr int I2S_WS = GPIO_NUM_19; +// I2S Serial Data pin +static constexpr int I2S_SD = GPIO_NUM_5; + +#endif // CONFIG_H |