Squeezebox über RFID-Karten steuern
Da unser Sohn noch nicht lesen kann, aber trotzdem gerne die Squeezebox benutzt um seine Hörspiele, Musik und Streams zu hören, musste ich mir eine Möglichkeit überlegen wie man die Squeezebox kindgerecht bedienen kann.
Da die Squeezebox ans openHAB angebunden ist liegt nichts näher als einen ESP mit RFID-Leser und MQTT dafür zu verwenden. Die Daten fließen dann folgendermaßen:
RFID-Reader -> ESP8266-12/D1Mini -> MQTT -> openHAB -> Squeezebox (Touch in meinem Fall)
Nebenbei schalte ich den Verstärker (da der schon ein paar Jahre auf dem Buckel hat per Funk-Steckdose) an und aus wenn die Squeezebox nicht verwendet wird. Da gehe ich hier aber nicht drauf ein (ist auch nicht so schwer ;))
Damit Junior auch eine Rückmeldung bekommt, dass die Karte angenommen wurde (und er ein bisschen Geduld haben muss bis die Squeezebox reagiert hat) missbrauche ich die LED auf dem Chip als Rückmeldung. Die Karten beklebe ich mit Etiketten mit den entsprechenden Bildern.
Material
ESP8266-12F * Affiliatelink
Jumper-Kabel * Affiliatelink
RFID Kit RC522 13,56MHz * Affiliatelink
13,56MHz RFID-Karten * Affiliatelink
HERMA 5028 Universal Etiketten DIN A4 * Affiliatelink
Anschlüsse
RC522 | ESP8226-12F |
---|---|
3.3V | 3V3 |
RST | D3 |
GND | G |
IRQ | |
MISO | D6 |
MOSI | D7 |
SCK | D5 |
SDA | D8 |
Code (ESP)
#include <SPI.h>
#include <MFRC522.h>
#include <MFRC522Extended.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <ESP8266WebServer.h>
#include <time.h>
// #############################################
// WLAN-Zugang
// #############################################
#define wifi_ssid "WLAN-SSID"
#define wifi_password "WLAN-PW"
// #############################################
// MQTT-Server (Openhab)
// #############################################
#define mqtt_server "192.168.2.15" // Openhabian-MQTT-IP
#define mqtt_user "openhabian"
#define mqtt_password ""
// #############################################
// NTP-Client
// #############################################
#define MY_NTP_SERVER "192.168.2.1"
#define MY_TZ "CET-1CEST,M3.5.0/02,M10.5.0/03"
// #############################################
// RFID-Reader
// #############################################
#define SS_PIN 15
#define RST_PIN 0
MFRC522 rfid(SS_PIN, RST_PIN);
MFRC522::MIFARE_Key key;
String kartennummer;
time_t now;
tm tm;
String TimeStamp;
ESP8266WebServer server(80);
String htmltext;
WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;
void setup ()
{
pinMode(LED_BUILTIN, OUTPUT); // Initialize the LED_BUILTIN pin as an output
SPI.begin();
rfid.PCD_Init();
Serial.begin(115200); // Serielle Ausgabe mit 115200 bps
setup_wifi();
client.setServer(mqtt_server, 1883);
configTime(MY_TZ, MY_NTP_SERVER);
server.on("/", handle_OnData);
server.on("/f.css", handleCss);
server.onNotFound(handle_NotFound);
server.begin();
Serial.println("HTTP server started");
Serial.println("Boot RFID-Reader...");
}
void loop ()
{
if (!client.connected()) {
reconnect();
}
server.handleClient();
time(&now);
localtime_r(&now, &tm);
TimeStamp = String(tm.tm_mday) + "." + String(tm.tm_mon + 1) + "." + String(tm.tm_year + 1900) + " " + String(tm.tm_hour) + ":" + String(tm.tm_min) + ":" + String(tm.tm_sec);
handleRFID();
if (kartennummer != "") {
client.publish("rfid/kartennummer", String(kartennummer).c_str(), true);
digitalWrite(LED_BUILTIN, LOW);
delay(100);
digitalWrite(LED_BUILTIN, HIGH);
htmltext = "";
htmltext += "<h2>Uhrzeit</h2>";
htmltext += "<p><b>" + String(TimeStamp) + "</b></p>";
htmltext += "<h2>Werte</h2>";
htmltext += "<p>letzte Karte: <b>" + String(kartennummer) + "</b></p>";
delay(1000);
kartennummer = "";
client.publish("rfid/kartennummer", String("0").c_str(), true);
}
}
void handleRFID() {
if (!rfid.PICC_IsNewCardPresent()) return;
if (!rfid.PICC_ReadCardSerial()) return;
Serial.println(printHex(rfid.uid.uidByte, rfid.uid.size));
kartennummer = printHex(rfid.uid.uidByte, rfid.uid.size);
rfid.PICC_HaltA();
rfid.PCD_StopCrypto1();
}
String printHex(byte *buffer, byte bufferSize) {
String id = "";
for (byte i = 0; i < bufferSize; i++) {
id += buffer[i] < 0x10 ? "0" : "";
id += String(buffer[i], HEX);
}
return id;
}
void setup_wifi() {
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(wifi_ssid);
WiFi.begin(wifi_ssid, wifi_password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.print(WiFi.localIP());
}
bool checkBound(float newValue, float prevValue, float maxDiff) {
return !isnan(newValue) &&
(newValue < prevValue - maxDiff || newValue > prevValue + maxDiff);
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Create a random client ID
String clientId = "ESP8266-";
clientId += String(random(0xffff), HEX);
// Attempt to connect
if (client.connect(clientId.c_str(), mqtt_user, mqtt_password)) {
Serial.println("connected");
// Once connected, publish an announcement...
client.publish("outTopic", "ESP01 alive");
// ... and resubscribe
client.subscribe("inTopic");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void handle_OnData() {
server.send(200, "text/html", SendHTML(htmltext));
}
void handle_OnConnect() {
server.send(200, "text/html", SendHTML("Boot"));
}
void handle_NotFound(){
server.send(404, "text/plain", "Not found");
}
String SendHTML(String addtext){
String ptr = "";
ptr += "<!DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n";
ptr += "<html lang='de'>\n";
ptr += "<head>\n";
ptr += "<title>RFID-Leser Webserver</title>\n";
ptr += "<link rel='stylesheet' type='text/css' href='/f.css'>\n";
ptr += "<meta content='text/html; charset=UTF-8' http-equiv='content-type'>\n";
ptr += "</head>\n";
ptr += "<body>\n";
ptr += "<h1>RFID-Leser Webserver</h1>\n";
ptr += addtext;
ptr += "</body>\n";
ptr += "</html>";
return ptr;
}
void handleCss()
{
String message = "";
message += "*{font-family:sans-serif}\n";
message += "body{margin:10px;width:300px}\n";
message += "h1, h2{color:white;background:#8FCE00;text-align:center}\n";
message += "h1{font-size:1.2em;margin:1px;padding:5px}\n";
message += "h2{font-size:1.0em}\n";
message += "h3{font-size:0.9em}\n";
message += "a{text-decoration:none;color:dimgray;text-align:center}\n";
message += ".small{font-size:0.6em}\n";
message += ".value{font-size:1.8em;text-align:center;line-height:50%}\n";
message += "footer p, .infodaten p{font-size:0.7em;color:dimgray;background:silver;";
message += "text-align:center;margin-bottom:5px}\n";
message += "nav{background-color:silver;margin:1px;padding:5px;font-size:0.8em}\n";
message += "nav a{color:white;padding:10px;text-decoration:none}\n";
message += "nav a:hover{text-decoration:underline}\n";
message += "nav p{margin:0px;padding:0px}\n";
message += ".btn{background-color:#C0C0C0;color:dimgray;text-decoration:none;";
message += "border-style:solid;border-color:dimgray}\n";
message += ".on, .off{margin-top:0;margin-bottom:0.2em;margin-left:3em;";
message += "font-size:1.4em;background-color:#C0C0C0;";
message += "border-style:solid;width:5em;height:1.5em;text-decoration:none;text-align:center}\n";
message += ".on{border-color:green}\n)";
server.send(200, "text/css", message);
}
Die Kartennummer wird gelesen und per MQTT verschickt. Ebenfalls wird die LED kurz aktiviert (0,5s) als Rückmeldung. Nach dem senden der Kartennummer schicke ich die Kartennummer 0 hinterher um auch eine zweite benutzung der Karte detektieren zu können. Ich wähle damit z.B. eine weitere Playlist an falls die aktuelle nicht gefällt oder schon gehört wurde.
Im Webserver (oder einem MQTT-Client) kann man sich die letzte Kartennummer (rfid/kartennummer) ansehen um die Programmierung im openHAB zu erstellen.
Vorbereitung Squeezebox
In der Squeezebox habe ich die Wiedergabelisten erstellt auf die ich dann zukünftig zugreifen will. Damit nicht immer mit dem gleichen Titel begonnen wird (z.B. bei Hörspielen) habe ich diese in verschiedene aufgeteilt die dann zufällig in Blöcken ausgewählt wird. (Wie ich das meine kann man im Code vermutlich ganz gut erkennen)
Vorbereitung openHAB
Die MQTT-Werte und die entsprechenden Squeezebox-Player müssen im Openhab natürlich auch als Items angelegt werden. Mindestens benötigt man folgende:
String MQTT_RFID_Leser_Kartennummer "Kartennummer" {channel="mqtt:topic:7c69bd7575:rfid_reader:MQTT_RFIDReader_Kartennummer"}
String SB_Wohnzimmer_Stream "Stream url" {channel="squeezebox:squeezeboxplayer:bc95bf4504:0004202375f1:stream"}
Player SB_Wohnzimmer_Control "" {channel="squeezebox:squeezeboxplayer:bc95bf4504:0004202375f1:control"}
(Ja, den Control braucht man natürlich nicht zwingend, macht aber Sinn ;))
Die Channel müsst ihr natürlich anpassen auf euer Setting und eure IDs. Im VisualStudio geht das ja ganz gut.
Code (openHAB)
// Squeezebox ansteuern
rule "Squeezekarte empfangen"
when
Item MQTT_RFID_Leser_Kartennummer changed
then
SB_Wohnzimmer_Power.sendCommand("ON")
if (MQTT_RFID_Leser_Kartennummer.state == "778e0f32") {
// Die drei Fragezeichen
// 8 Playlists => Nummer zwischen 0 und 7
var Nummer = (Math::random * 7).intValue
SB_Wohnzimmer_Stream.sendCommand("/listen/Die_drei_Fragezeichen_Kids_" + Nummer)
// SB_Wohnzimmer_Stream.sendCommand("/listen/Die_drei_Fragezeichen_Kids")
SB_Wohnzimmer_Control.sendCommand("PLAY")
}
else if (MQTT_RFID_Leser_Kartennummer.state == "f4253660") {
// Absolut Relax
SB_Wohnzimmer_Stream.sendCommand("https://stream.absolutradio.de/relax/mp3-128/logitech.m3u")
SB_Wohnzimmer_Control.sendCommand("PLAY")
}
else if (MQTT_RFID_Leser_Kartennummer.state == "23796909") {
// Antenne Bayern Weihnachtshits
SB_Wohnzimmer_Stream.sendCommand("http://opml.radiotime.com/Tune.ashx?id=s244472&formats=aac,ogg,mp3,wmpro,wma,wmvoice&partnerId=16&serial=01af601506296fc0c85f467f7a7746b0")
SB_Wohnzimmer_Control.sendCommand("PLAY")
}
Die m3u liegen bei mir im Ordner „listen“ den ihr im Squeezebox-Server als Wiedergabelisten-Ordner angebt. Ihr gebt hier die Dateinamen ohne „.m3u“ an. Die drei Fragezeichen Kids kann man übrigens sehr gut als 3er-Boxen für ca. 10€ als CD kaufen (z.B. bei Amazon * Affiliatelink, wir haben unsere hauptsächlich aus Kleinanzeigen und von Freunden deren Kinder doch keine so großen Fans waren wie die Eltern ;)).
Beim Eintrag für die drei Fragezeichen kann man sehen, dass ich 8 Playlists (0…7) angelegt habe von der zufällig eine verwendet wird.
Aufbau
Ich war erst mal faul und hab die Komponenten in eine Lego-Kiste verbaut und mit einem kleinen Schalter versehen.
Um das Feedback sichtbar zu machen habe ich das Innenleben so gebaut, dass der ESP direkt am vorderen Rand liegt und die LED direkt am transparenten Stein liegt. Damit ist das lesen der Karte gut sichbar.
Viel Spaß beim nachbauen oder verbessern. Gerne Feedback wenn ihr noch etwas zum verbessern findet.
Schreibe einen Kommentar