Hi @jabred
hier mein kompletter Code (alle die es besser können, werft die Steine JETZT 
Ich geh schon mal in Deckung.
Ich habe ein 4-zeiliges LiquidCrystal Display verwendet und einen Temperatur-/Luftfeuchtigkeitssensor, weil ich das auch noch auf dem Display haben wollte
Außerdem gibt es zwei Regler: Links zum Sender blättern (Drehen) und Wählen (Drücken).
Rechts zum Lautstärke regeln (Drehen) und Anzeigemodus ändern (Drücken):
Der entscheidende Part (Umschalten zwischen webradio und bluethooth audio) läuft über die Variable "conn_mode".
Da das hier kein Vorzeigeprojekt sollten werde, hab ich den Code jetzt nicht sonderlich gut aufgeräumt, kann durchaus sein, dass das noch bissl durcheinander ist und die ein oder andere Variable gar nicht (mehr) in Verwendung ist.
Das Radio funktioniert für mich so aber ideal.
#include <Preferences.h>
#include "Arduino.h"
#include "WiFi.h"
#include <WebServer.h>
#include "Audio.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "AiEsp32RotaryEncoder.h"
#include <SHT21.h>
#include <time.h>
#include "BluetoothA2DPSink.h"
#include "Button2.h"
// ++++++++++++++++++++++++++++ Verdrahtung:
// ############# LCD-Display + GY-21 Sensor (SHT21):
// SDA: 21
// SCL: 22
// ############# Button zum Umschalten zwischen webradio und bluetooth audio
#define BUTTON_PIN 19
// ############# Drehgeber 1 (EC11) - Sender umschalten (Drehen) und Sender wählen (Drücken)
// Button-Pins: Ein Pin direkt auf GND, der andere auf GPIO 33 und über einen 10k zusätzlich auf 5V
// PotiPins: Von Oben gesehen: Links auf GPIO34, Mitte auf GND, Rechts auf GPIO 35
#define ROTARY_ENCODER_1_BUTTON_PIN 33
#define ROTARY_ENCODER_1_A_PIN 34
#define ROTARY_ENCODER_1_B_PIN 35
#define ROTARY_ENCODER_1_VCC_PIN -1
#define ROTARY_ENCODER_1_STEPS 4
// ############# Drehgeber 2 (EC11) - Lautstärke wählen (Drehen) und Display-Modus umschalten (Drücken)
// Button-Pins: Ein Pin direkt auf GND, der andere auf GPIO 14 und über einen 10k zusätzlich auf 5V
// PotiPins: Von Oben gesehen: Links auf GPIO18, Mitte auf GND, Rechts auf GPIO 4
#define ROTARY_ENCODER_2_BUTTON_PIN 14
#define ROTARY_ENCODER_2_A_PIN 18
#define ROTARY_ENCODER_2_B_PIN 4
#define ROTARY_ENCODER_2_VCC_PIN -1
#define ROTARY_ENCODER_2_STEPS 4
// Digital I/O used
#define I2S_DOUT 25
#define I2S_BCLK 27
#define I2S_LRC 26
// ++++++++++++++++++++++++++++ Initialisieren
BluetoothA2DPSink a2dp_sink;
WebServer server(80); // Server der App
LiquidCrystal_I2C lcd(0x27,20,4);
Preferences pref;
AiEsp32RotaryEncoder rotaryEncoder = AiEsp32RotaryEncoder(ROTARY_ENCODER_1_A_PIN, ROTARY_ENCODER_1_B_PIN, ROTARY_ENCODER_1_BUTTON_PIN, ROTARY_ENCODER_1_VCC_PIN, ROTARY_ENCODER_1_STEPS);
AiEsp32RotaryEncoder rotaryEncoder2 = AiEsp32RotaryEncoder(ROTARY_ENCODER_2_A_PIN, ROTARY_ENCODER_2_B_PIN, ROTARY_ENCODER_2_BUTTON_PIN, ROTARY_ENCODER_2_VCC_PIN, ROTARY_ENCODER_2_STEPS);
Audio audio;
SHT21 sht;
Button2 button = Button2(BUTTON_PIN);
tm timeinfo;
time_t now;
// +++++++++++++++++++++++++++++++++++++++++++ Variablen
#define WIFI_TIMEOUT_MS 20000 // So lange wird versucht, neu mit Wifi zu verbinden, wenn die Verbindung mal verloren geht (in keepWiFiAlive())
const char* NTP_SERVER = "de.pool.ntp.org";
const char* TZ_INFO = "CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00"; // Hier kann man die Zeitzonen nachschauen (https://remotemonitoringsystems.ca/time-zone-abbreviations.php)
// ############################################## Senderliste
//structure for station list
typedef struct {
char * url; //stream url
char * name; //stations name
} Station;
#define STATIONS 13
Station stationlist[STATIONS] PROGMEM = {
{"http://streams.radiobob.de/bob-live/mp3-192/mediaplayer","Bob Livestream"},
{"http://streams.radiobob.de/bob-classicrock/mp3-192/mediaplayer ","Bob Classic Rock"},
{"http://streams.radiobob.de/bob-alternative/mp3-192/mediaplayer","Bob Alternative"},
{"http://streams.radiobob.de/bob-hartesaite/mp3-192/mediaplayer","Bob Harte Saite"},
{"http://streams.radiobob.de/bob-bestofrock/mp3-192/mediaplayer","Bob Best of Rock"},
{"https://streams.radiobob.de/70errock/mp3-192/mediaplayer/","Bob 70er"},
{"http://streams.radiobob.de/bob-80srock/mp3-192/mediaplayer","Bob 80er"},
{"http://streams.radiobob.de/bob-90srock/mp3-192/mediaplayer","Bob 90er"},
{"http://streams.radiobob.de/bob-metal/mp3-192/mediaplayer","Bob Metal"},
{"http://streams.radiobob.de/bob-grunge/mp3-192/mediaplayer","Bob Grunge"},
{"http://streams.radiobob.de/bob-livemusic/mp3-192/mediaplayer","Bob Festival"},
{"http://streams.radiobob.de/southernrock/mp3-192/mediaplayer","Bob Southern Rock"},
{"http://streams.radiobob.de/progrock/mp3-192/mediaplayer/","Bob ProgRock"}};
// Über die Index-Seite kann man initial seine WLAN-Zugangsdaten eingeben, wenn man sich über den AP / 192.168.4.1 verbindet
#include "index.h"
// Anzeige
uint8_t displayMode = 1;
// displayMode 1 = Sendername, Songtitel, Datum und Uhrzeit werden angezeigt
// displayMode 0 = Songtitel, Luftfeuchtigkeit, Temperatur und Datum/Uhrzeit werden angezeigt
uint8_t setMode = 0;
uint32_t lastdispchange = 0;
uint32_t lastvolchange = 0; // Zeitpunkt der letzten Lautstärkeänderung
uint32_t lasttimecheck = 0;
String titleinfo = "";
String titleinfo_old = "";
uint16_t currentHour = 0;
uint16_t currentMinute = 0;
uint16_t currentSecond = 0;
uint16_t monthDay = 0;
uint16_t currentMonth = 0;
uint16_t currentYear = 0;
char currentDate[20];
unsigned long connTimer;
bool wifi_conn_success=false;
uint8_t conn_mode=0; // 0 = Webradio, 1 = Bluetooth-Audio
uint8_t volume = 5;
uint8_t curStation = 0;
uint8_t actStation = 0; //index for current station in station list used for streaming
uint32_t lastchange = 0; // Zeitpunkt der letzten Sender- oder Volume-Änderung
// Luftfeuchtigkeit / Temperatur
float temp;
float humidity;
char ssid[50]; // WLAN-SSID, wird vom Benutzer über die App gefüllt
char password[50]; // WLAN-Passwort, wird vom Benutzer über die App gefüllt
// Interrupt für Sender-Regler
void IRAM_ATTR readEncoderISR()
{
rotaryEncoder.readEncoder_ISR();
}
//interrupt für Volume-Regler / Display-Umschalter
void IRAM_ATTR readEncoder2ISR()
{
rotaryEncoder2.readEncoder_ISR();
}
// ++++++++++++++++++++++++++++++++++++++++ WiFi-Verbindung aufrechterhalten
void keepWiFiAlive(void * parameters) {
for (;;) {
if (wifi_conn_success) {
// Nur wenn die manuell gestartete, erste Verbindung geklappt hat, wieder neu verbinden
// Denn wenn diese schon nicht geklappt hat, hat es auch keinen Wert, nochmal zu versuchen.
if (WiFi.status() == WL_CONNECTED) {
//Wifi ist immer noch verbunden
vTaskDelay(10000 / portTICK_PERIOD_MS);
continue;
}
Serial.println("WiFi-Verbindungsversuch....!");
WiFi.begin(ssid, password);
unsigned long startAttemptTime=millis();
while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < WIFI_TIMEOUT_MS) {
vTaskDelay(500 / portTICK_PERIOD_MS);
}
if (WiFi.status() != WL_CONNECTED) {
vTaskDelay(300000 / portTICK_PERIOD_MS);
continue;
} else {
continue;
}
} else {
vTaskDelay(10000 / portTICK_PERIOD_MS);
continue;
}
}
}
void setup() {
Wire.begin(); // begin Wire(I2C)
Serial.begin(115200);
// WiFi-Verbindung aufrechterhalten
xTaskCreatePinnedToCore(
keepWiFiAlive,
"Keep Wifi Alive",
5000,
NULL,
2,
NULL,
0
);
pinMode(BUTTON_PIN, INPUT_PULLUP);
button.setPressedHandler(pressed);
i2s_pin_config_t my_pin_config = {
.bck_io_num = 27,
.ws_io_num = 26,
.data_out_num = 25,
.data_in_num = I2S_PIN_NO_CHANGE
};
a2dp_sink.set_pin_config(my_pin_config);
loadEinstellungen();
// +++++++++++++++++++++++++++++++++ Audio-Objekt aufbauen
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
// NTP-Server und Zeitzone setzen
configTime(0, 0, NTP_SERVER);
setenv("TZ", TZ_INFO, 1);
pinMode(34, INPUT);
pinMode(35, INPUT);
pinMode(14, INPUT_PULLUP);
pinMode(18, INPUT_PULLUP);
pinMode(4, INPUT_PULLUP);
// ++++++++++++++++++++++++++++++++++ Display initialisieren
lcd.init();
// Print a message to the LCD.
lcd.backlight();
lcd.clear();
if (conn_mode == 0) {
// Webradio-Modus
// +++++++++++++++++++++++++++++++++ WiFi verbinden
lcd.setCursor(0,0);
lcd.print("Webradio");
WiFi_conn();
// +++++++++++++++++++++++++++++++++ Drehgeber initialisieren
//start rotary encoder instance
rotaryEncoder.begin();
rotaryEncoder.setup(readEncoderISR);
rotaryEncoder.setBoundaries(0, STATIONS, true);
rotaryEncoder.disableAcceleration();
server.on("/", display_root);
server.on("/set", [](){
strcpy(ssid, server.arg("ssid").c_str());
strcpy(password, server.arg("pass").c_str());
pref.begin("wr_settings", false);
pref.putString("ssid", server.arg("ssid"));
pref.putString("password", server.arg("pass"));
pref.end();
WiFi_conn();
server.send(200, "text/html", "<html><body>Die Zugangsdaten wurden gespeichert. Verbindungsaufbau wird versucht...</body></html>");
});
server.begin();
senderEinstellen(curStation);
} else {
// Bluetooth-Audio-Modus
showTemp_bluetooth();
// Pins für Bluetooth Audio konfigurieren
i2s_pin_config_t my_pin_config = {
.bck_io_num = 27,
.ws_io_num = 26,
.data_out_num = 25,
.data_in_num = I2S_PIN_NO_CHANGE
};
a2dp_sink.set_pin_config(my_pin_config);
a2dp_sink.start("MyMusic");
}
rotaryEncoder2.begin();
rotaryEncoder2.setup(readEncoder2ISR);
rotaryEncoder2.setBoundaries(0, 12, false);
rotaryEncoder2.disableAcceleration();
set_volume();
}
void loop()
{
if (conn_mode==0) {
String sendername = String(stationlist[curStation].name);
server.handleClient();
// Prüfen, ob an Regler eins geschraubt/gedrückt wird
rotary_loop();
}
if (millis()-lasttimecheck > 1000) {
get_rtc_time();
lasttimecheck = millis();
refresh_time();
}
button.loop();
audio.loop();
rotary_loop2();
if (millis()-lastvolchange > 3000) {
setMode = 0;
}
if (lastdispchange==0 || (millis()-lastdispchange > 5000)) {
Serial.println("Drin in Display-Schleife!");
if (displayMode==0) {
// Immer wenn sich der Titel ändert, wird die Temperatur refreshed...
showTemp();
} else {
if (conn_mode==0) {
if (titleinfo !="" && titleinfo!="www.radiobob.de") {
showTitle();
} else {
showStation();
}
} else {
showTemp_bluetooth();
}
}
lastdispchange = millis();
}
}
// RotaryEncoder 1:
// Sender durchblättern (Drehen) und wählen (Drücken)
void rotary_loop()
{
//dont do anything unless value changed
if (rotaryEncoder.encoderChanged())
{
uint16_t v = rotaryEncoder.readEncoder();
Serial.printf("Station: %i\n",v);
//set new currtent station and show its name
if (v < STATIONS) {
curStation = v;
showStation();
lastchange = millis();
}
}
//if no change happened within 10s set active station as current station
if ((lastchange > 0) && ((millis()-lastchange) > 10000) && displayMode==1){
curStation = actStation;
lastchange = 0;
//showStation();
lcd.clear();
showTitle();
}
//react on rotary encoder switch
if (rotaryEncoder.isEncoderButtonClicked())
{
//set current station as active station and start streaming
actStation = curStation;
Serial.printf("Active station %s\n",stationlist[actStation].name);
senderEinstellen(actStation);
//startUrl();
//call show station to display the speaker symbol
//showStation();
showTitle();
}
}
// RotaryEncoder 2
// Standard beim Drehen: Volume einstellen
// Drücken = Umschalten des Anzeigemodus
void rotary_loop2()
{
//dont do anything unless value changed
if (rotaryEncoder2.encoderChanged())
{
lastdispchange=millis();
set_volume();
}
//react on rotary encoder switch
if (rotaryEncoder2.isEncoderButtonClicked())
{
if (displayMode == 0) {
displayMode = 1;
} else {
displayMode = 0;
}
// Damit sofort umgeschaltet wird....
lastdispchange=0;
}
}
void WiFi_conn() {
wifi_conn_success=true;
WiFi.disconnect();
WiFi.mode(WIFI_AP_STA);
WiFi.softAP("webradio", "123456789");
Serial.println("Verbinden mit folgenden Zugangsdaten:");
Serial.print("SSID:");Serial.println(ssid);
Serial.print("Passwort:");Serial.println(password);
WiFi.begin(ssid, password);
int i = 0;
// Nur wenn wir noch nicht verbunden sind, wird jetzt neu verbunden
unsigned long pause = 10000;
connTimer = millis();
while (WiFi.status() != WL_CONNECTED && (millis() - connTimer < pause)) {
//while (WiFi.status() != WL_CONNECTED && connTimer < connCancel){
Serial.print(".");
i++;
delay(500);
}
if (WiFi.status() != WL_CONNECTED) {
WiFi.disconnect();
WiFi.softAP("webradio", "123456789");
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Kein WLAN");
wifi_conn_success=false;
}
}
void display_root() {
const char * httpType PROGMEM = "text/html";
//server.send(200, "text/html", "");
server.send_P(200, httpType, indexPage);
}
void loadEinstellungen() {
// Zuletzt eingestellten Sendern ermitteln und dann direkt laden
pref.begin("wr_settings", false);
curStation = pref.getUInt("curStation", 0);
conn_mode = pref.getUInt("conn_mode", 0);
strcpy(ssid, pref.getString("ssid").c_str());
strcpy(password, pref.getString("password").c_str());
pref.end();
}
void senderEinstellen(int selectedStation) {
Serial.println("Sender wird eingestellt am Display");
audio.connecttohost(stationlist[selectedStation].url);
//lcd.setCursor(0,0);
//lcd.print(stationlist[selectedStation].name);
// Sender speichern, dass dieser beim neuen Einschalten des Radios wieder gewählt wird
pref.begin("wr_settings", false);
pref.putUInt("curStation", selectedStation);
pref.end();
}
void set_dispmode() {
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Anzeigemodus");
if (displayMode == 0) {
displayMode = 1;
lcd.setCursor(0,1);
lcd.print("Sender / Titel");
} else {
displayMode = 0;
lcd.setCursor(0,1);
lcd.print("Datum / Temp / Luft");
}
}
void set_volume() {
uint16_t v = rotaryEncoder2.readEncoder();
Serial.print("Encoder-Position:");Serial.println(v);
volume = map(v, 0, 12, 0, 12);
Serial.print("Volume:");Serial.println(volume);
audio.setVolume(volume);
show_volume();
lastchange = millis();
}
void show_volume() {
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Volume: ");
lcd.print(volume);
}
void showStation() {
// Display aktualisieren
lcd.clear();
if (curStation == actStation) {
lcd.home();
lcd.print(char(1));
}
lcd.setCursor(2,0);
String name = String(stationlist[curStation].name);
if (name.length() < 20)
lcd.print(name);
else {
uint8_t p = name.lastIndexOf(" ",20); //if name does not fit, split line on space
lcd.print(name.substring(0,p));
lcd.setCursor(0,1);
lcd.print(name.substring(p+1,p+21));
}
}
void showTitle() {
// Display aktualisieren
if (titleinfo_old != titleinfo) lcd.clear();
// Sender anzeigen
lcd.setCursor(0,0);
// Sender anzeigen
String name = String(stationlist[curStation].name);
lcd.print(name);
lcd.setCursor(0,1);
if (titleinfo.length() < 21)
lcd.print(titleinfo);
else {
uint8_t p = titleinfo.lastIndexOf(" ",20); //if name does not fit, split line on space
lcd.print(titleinfo.substring(0,p));
lcd.setCursor(0,2);
lcd.print(titleinfo.substring(p+1,p+21));
}
titleinfo_old=titleinfo;
}
void refresh_time() {
lcd.setCursor(0,3);
lcd.print(currentDate);
}
void showTemp() {
// Temperatur / Luftfeuchtigkeit oben, Uhrzeit unten
temp = sht.getTemperature(); // get temp from SHT
humidity = sht.getHumidity(); // get temp from SHT
if (titleinfo_old != titleinfo) lcd.clear();
lcd.setCursor(0,0);
if (titleinfo_old != titleinfo) lcd.clear();
if (titleinfo.length() < 21)
lcd.print(titleinfo);
else {
uint8_t p = titleinfo.lastIndexOf(" ",20); //if name does not fit, split line on space
lcd.print(titleinfo.substring(0,p));
lcd.setCursor(0,1);
lcd.print(titleinfo.substring(p+1,p+21));
}
titleinfo_old=titleinfo;
lcd.setCursor(0,2);
lcd.print(temp);
lcd.print(" C ");
lcd.print(humidity);
lcd.print(" % ");
//lcd.setCursor(0,1);
//lcd.print(currentDate);
}
void showTemp_bluetooth() {
// Temperatur / Luftfeuchtigkeit oben, Uhrzeit unten
temp = sht.getTemperature(); // get temp from SHT
humidity = sht.getHumidity(); // get temp from SHT
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Bluetooth Audio");
lcd.setCursor(0,2);
lcd.print(temp);
lcd.print(" C ");
lcd.print(humidity);
lcd.print(" % ");
//lcd.setCursor(0,1);
//lcd.print(currentDate);
}
void audio_showstreamtitle(const char *info){
Serial.print("streamtitle ");Serial.println(info);
titleinfo = info;
}
bool get_rtc_time() {
time(&now);
localtime_r(&now, &timeinfo);
currentHour = timeinfo.tm_hour;
currentMinute = timeinfo.tm_min;
currentSecond = timeinfo.tm_sec;
monthDay = timeinfo.tm_mday;
currentMonth = timeinfo.tm_mon+1;
currentYear = timeinfo.tm_year+1900;
sprintf(currentDate, "%02d.%02d.%d %02d:%02d:%02d", monthDay, currentMonth, currentYear , currentHour, currentMinute, currentSecond);
return true;
}
void scrollText(int row, String message, int delayTime, int lcdColumns) {
for (int i=0; i < lcdColumns; i++) {
message = " " + message;
}
message = message + " ";
for (int pos = 0; pos < message.length(); pos++) {
lcd.setCursor(0, row);
lcd.print(message.substring(pos, pos + lcdColumns));
delay(delayTime);
}
}
void pressed(Button2& btn) {
if (conn_mode == 0) {
Serial.println("Umschalten auf Bluetooth");
pref.begin("wr_settings", false);
pref.putUInt("conn_mode", 1);
pref.end();
} else {
Serial.println("Umschalten auf webradio");
pref.begin("wr_settings", false);
pref.putUInt("conn_mode", 0);
pref.end();
}
ESP.restart();
}
In der letzten Funktion void pressed() wird der Modus gesetzt und dann ein Neustart angestoßen.
Wenn du noch Fragen hast, meld dich gerne.
Sorry für das bescheidene Handy-Foto 
Hier ne Ansicht des Displays:
