aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSlendi <slendi@socopon.com>2024-02-04 20:54:38 +0200
committerSlendi <slendi@socopon.com>2024-02-04 20:54:38 +0200
commit9664aa441fcce8629dd00bc3b458641e514c150f (patch)
treeeaa3e6996bfc5b792a73e1451148c590f6add511
Initial commitHEADmaster
Signed-off-by: Slendi <slendi@socopon.com>
-rw-r--r--A2DPSwitcher.ino90
-rw-r--r--LICENSE.txt14
-rw-r--r--README.md40
-rw-r--r--config.h20
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