USS Enterprise in Loxone einbinden

Einklappen
X
 
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge
  • MarkusCosi
    LoxBus Spammer
    • 28.09.2023
    • 245

    #1

    USS Enterprise in Loxone einbinden

    ..vllt. hat der ein oder andere ja auch die USS Enterprise von Playmobil zuhause.... bekanntlich lässt sich diese per Android oder iOS App steuern, z.B. im "Kommando"-Modus, um Licht und Sound-Effekte abzuspielen. Mit einem ESP32 Modul und den Infos zu den Bluetooth-Kennungen kann man die Licht & Sound-Effekte der Enterprise auch über http-Befehle in Loxone einbinden.

    Ich habe dazu folgendes ESP32 Modul benutzt: ESP32-D0WD-V3 (revision v3.1) / diymore ESP32 NodeMCU ESP32 WROOM 32D - development board CH340.
    Zusätzlich benötigt ihr nur noch die MAC Adresse eurer Enterprise, z.B. herauszufinden über Wireshark (ggf. auch über BLE Scanner App?), und den Code unten für den ESP32. Danach in Loxone http-get Befehle triggern, z.B. mit http://192.168.1.XXX/M1 (XXX=vergebene lokale IP, herausfinden am Router!).

    Ich habe die BLE-Befehle der iPhone App an die Enterprise über iPhone Bluetooth Logging in Erfahrung gebracht (nach Aktivierung des Loggings die App benutzt und die Befehle der Reihe nach ausgeführt), und zwar so wie hier beschrieben per Wireshark und dem Filter: bluetooth.dst == ac:XX:XX:26:c5:22. Hier muss man die passende MAC Adresse für euere Enterprise in Wireshark herausfinden (suche nach: "TexasInstrum_26:c5:22", oder nach Timestamp die entsprechenden Befehle im BT Protokoll wiederfinden) um übersichtlich alle Befehle (=Values) an die Enterprise gelistet zu bekommen. Man bekommt so die einzelnen Befehle heraus um die entsprechenden Aktionen zu triggern. Das habe ich bereits gemacht und findet sich unter im Code im byte-Array PmUSSECMDs wieder!

    Schnell konnte ich so (geht auch über BLE scanner Apps am Smartphone) den entsprechenden BLE Service UUID und die Charcteristic UUID herausfinden:
    [Destination Device Name: Pm_USSE_6C522] ← hierunter ist die Enterprise in BLE Scanner Apps schnell zu finden.
    [Service UUID: bc2f4cc6aaef43519034d66268e328f0]
    [UUID: 06d1e5e779ad4a718faa373789f7d93c]

    über eine iPhone App wie z.B. "BLE Scanner" (dort: connect to "Pm_USSE_6C522" → Custom Service → Custom Characteristic → click "W" symbol → Hex → e.g.: aa0701ffff) oder aber am Mac über eine App wie "LightBlue" (dort: connect to "Pm_USSE_6C522" → USSE Command (="Characteristic user description") → "Write new value") kann man die Befehle (=Values) auch vorher mal testen.

    Der nächste Schritt war die Verbindung zur Enterprise analog zu meinem Vorgehen hier: link, d.h. unter Nutzung der "BLEDevice.h"-Library. (siehe code anbei)
    Außerdem läuft dann auf dem ESP32 ein Server über die Bibliotheken: "WiFi.h" / "WiFiClient.h" / "HTTPClient.h", sodass das Modul die passenden BLE Befehle auf http-get Befehl hin sendet...

    Anwendungsfälle:
    • Kopplung des "Roter Alarm"-Sounds an die Alarm-Anlage
    • Lichter der Enterprise an BWM Koppeln...

    Viel Spaß damit!

    Code:
    // Playmobile USS Enterprise
    #include <WiFi.h> //Wifi support
    #include <WiFiClient.h> //Wifi support
    #include <HTTPClient.h> //Wifi server support
    #include "BLEDevice.h" //BLE support
    
    //On-chip LED GPIO (depends on used ESP32 board)
    int LED_BUILTIN = 2;
    
    //Wifi SSID and password
    const char* ssid = "YourSSID";
    const char* password = "YourPW";
    unsigned long previousMillis = 0;
    unsigned long timeoutTime = 30000; //timeout for Wifi-Connection
    unsigned long previousMillisS = 0;
    const long timeoutTimeS = 1000; // Define timeout for Server connection
    WiFiServer server(80);
    
    //BLE UUIDs: The pattern is 8-4-4-4-12
    #define EnterpriseBLEAddress "ac:XX:XX:26:c5:22"
    // The remote service we wish to connect to: Pm_USSE_6C522 (Playmobil USS Enterprise)
    static BLEUUID serviceUUID("bc2f4cc6-aaef-4351-9034-d66268e328f0");
    // The characteristic of the remote service we are interested in: "USSE Command"
    static BLEUUID charUUID("06d1e5e7-79ad-4a71-8faa-373789f7d93c");
    static BLERemoteCharacteristic* pRemoteCharacteristic;
    //variable holding connection status
    //BLEClient* pClient;
    bool BLEConnectionStatus;
    
    //define commands ("Values") sniffed from Playmobil Enterprise iPhone APP
    static byte PmUSSECMDs[19][5] = {
    {0xaa, 0x07, 0x01, 0xff, 0xff}, // CMD 1 / Warp: aa0701ffff
    {0xaa, 0x07, 0x02, 0x00, 0xff}, // CMD 2 / Alarm: aa070200ff
    {0xaa, 0x07, 0x03, 0x00, 0xff}, // CMD 3 / Torpedo: aa070300ff
    {0xaa, 0x08, 0x07, 0xc8, 0xff}, // CMD 4 / sound Alarm: aa0807c8ff
    {0xaa, 0x08, 0x05, 0xc8, 0xff}, // CMD 5 / sound Torpedo: aa0805c8ff
    {0xaa, 0x08, 0x02, 0xc8, 0xff}, // CMD 6 / sound Pfeife: aa0802c8ff
    {0xaa, 0x08, 0x08, 0xc8, 0xff}, // CMD 7 / sound Live long and Prosper: aa0808c8ff
    {0xaa, 0x08, 0x06, 0xc8, 0xff}, // CMD 8 / sound Scotty James to Enterprise: aa0806c8ff
    {0xaa, 0x08, 0x0b, 0xbe, 0xff}, // CMD 9 / sound Brücken-Ambiente: aa080bbeff
    {0xaa, 0x08, 0x01, 0xbe, 0xff}, // CMD 10 / sound Dematerialisieren: aa0801beff
    {0xaa, 0x04, 0x07, 0xc8, 0xff}, // CMD 11 / lights: Warp Gondeln: aa0407c8ff
    {0xaa, 0x05, 0x07, 0xc8, 0xff}, // CMD 12 / lights: Go to Warp: aa0507c8ff
    {0xaa, 0x04, 0x03, 0xc8, 0xff}, // CMD 13 / lights: Torpedo: aa0403c8ff
    {0xaa, 0x05, 0x01, 0xc8, 0xff}, // CMD 14 / lights: Torpedo Twice: aa0501c8ff
    {0xaa, 0x03, 0xff, 0x00, 0xff}, // CMD 15 / Vol 100: aa03ff00ff
    {0xaa, 0x03, 0x83, 0x00, 0xff}, // CMD 16 / Vol 50: aa038300ff
    {0xaa, 0x03, 0x80, 0x00, 0xff}, // CMD 17 / Vol 30: aa038000ff
    {0xaa, 0x03, 0x00, 0x00, 0xff}, // CMD 18 / Vol 0: aa030000ff
    {0xaa, 0x07, 0x04, 0x00, 0xff} // CMD 19 / Alles AUS: aa070400ff
    };
    
    WiFiClient espClient;
    
    // Standard Arduino function which is called once when the device first starts up
    void setup() {
    // Setup for Serial Monitor (when connected to Arduino IDE: Tools -> Serial Monitor)
    Serial.begin(9600);
    while(!Serial) {
    delay(10);
    }
    // Initialize digital pin LED_BUILTIN as an output.
    pinMode(LED_BUILTIN, OUTPUT);
    //connect to Pm_USSE_6C522 (Playmobil USS Enterprise)
    BLEDevice::init("");
    BLEClient* pClient = BLEDevice::createClient();
    Serial.println("\nTrying to connect to Pm_USSE_6C522");
    //BLE address of Pm_USSE_6C522 (Playmobil USS Enterprise)
    if (pClient->connect(BLEAddress(EnterpriseBLEAddress))) {
    Serial.println("Connected!");
    BlinkLED();BlinkLED();
    Serial.println("\nTrying to connect to BLE-Service");
    BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
    if (pRemoteService == nullptr) {
    Serial.println("Failed to get service");
    return;
    } else Serial.println("Got service");
    Serial.println("\nTrying to connect to BLE-Characteristic of Service");
    pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
    if (pRemoteCharacteristic == nullptr) {
    Serial.println("Failed to get characteristic");
    return;
    } else Serial.println("Got characteristic");
    } else Serial.println("Failed to connect");
    Serial.println("\nPrint Status:");
    BLEConnectionStatus = pClient->isConnected();
    Serial.println(BLEConnectionStatus);
    // Connect to WiFi
    Serial.println("\nConnecting to WiFi");
    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid, password);
    while(WiFi.status() != WL_CONNECTED){
    Serial.print(".");
    delay(500);
    }
    Serial.print("\nConnected to the WiFi network: ");
    Serial.print(String(ssid));
    Serial.print(" | local ESP32 IP: ");
    Serial.print(WiFi.localIP());
    Serial.print(" | RSSI: ");
    Serial.println(WiFi.RSSI());
    //start server to allow control via http commands
    server.begin();
    // Blink LED on Chip once on Startup
    BlinkLED();BlinkLED();BlinkLED();
    }
    
    // Blink LED on Chip
    void BlinkLED() {
    digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
    delay(100); // LED on for 100msec
    digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
    delay(100); // wait / off for 100msec
    }
    
    // Standard Arduino function that is called in an endless loop after setup
    void loop() {
    unsigned long currentMillis = millis(); //works for ca. 50 days before reset to 0
    unsigned long currentMillisS = millis(); //works for ca. 50 days before reset to 0
    // if WiFi is down, try reconnecting every timeoutTime seconds
    if ((WiFi.status() != WL_CONNECTED) && (currentMillis - previousMillis >= timeoutTime)) {
    Serial.print(millis());
    Serial.println("Reconnecting to WiFi...");
    WiFi.disconnect();
    WiFi.reconnect();
    Serial.print("\nConnected to the WiFi network: ");
    Serial.print(String(ssid));
    Serial.print(" | local ESP32 IP: ");
    Serial.print(WiFi.localIP());
    Serial.print(" | RSSI: ");
    Serial.println(WiFi.RSSI());
    previousMillis = currentMillis;
    }
    if (currentMillis - previousMillis < 0) {
    previousMillis = currentMillis;
    }
    //get restart command from external http command to server
    WiFiClient client = server.available();
    if (client) {
    currentMillisS = millis();
    previousMillisS = currentMillisS;
    Serial.println("New Client");
    String currentLine = "";
    while (client.connected() && (currentMillisS - previousMillisS <= timeoutTimeS)) {
    currentMillisS = millis();
    if (client.available()) {
    char c = client.read();
    Serial.write(c);
    if (c == '\n') {
    if (currentLine.length() == 0) {
    // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
    // and a content-type so the client knows what's coming, then a blank line:
    client.println("HTTP/1.1 200 OK");
    client.println("Content-type:text/html");
    //client.println("Connection: close");
    client.println();
    //create the buttons
    client.print("Effekte: <a href=\"/M1\">Warp</a><br>");
    client.print("Effekte: <a href=\"/M2\">Alarm</a><br>");
    client.print("Effekte: <a href=\"/M3\">Torpedo</a><br><br>");
    client.print("Sound: <a href=\"/M4\">Alarm</a><br>");
    client.print("Sound: <a href=\"/M5\">Torpedo</a><br>");
    client.print("Sound: <a href=\"/M6\">Pfeife</a><br>");
    client.print("Sound: <a href=\"/M7\">Live Long and Prosper</a><br>");
    client.print("Sound: <a href=\"/M8\">Scotty James to Enterprise</a><br>");
    client.print("Sound: <a href=\"/M9\">Brückenambiente</a><br>");
    client.print("Sound: <a href=\"/M10\">Dematerialisieren</a><br><br>");
    client.print("Lights: <a href=\"/M11\">Warp Gondeln</a><br>");
    client.print("Lights: <a href=\"/M12\">Go to Warp</a><br>");
    client.print("Lights: <a href=\"/M13\">Torpedo Full</a><br>");
    client.print("Lights: <a href=\"/M14\">Torpedo Twice</a><br><br>");
    client.print("Volume: <a href=\"/v1\">100%</a><br>");
    client.print("Volume: <a href=\"/v2\">50%</a><br>");
    client.print("Volume: <a href=\"/v3\">30%</a><br>");
    client.print("Volume: <a href=\"/v4\">0%</a><br><br>");
    client.print("Enterprise: <a href=\"/x\">AUS</a><br><br>");
    client.print("ESP32 Modul: <a href=\"/T\">Blink!</a><br><br>");
    // The HTTP response ends with another blank line:
    client.println();
    // break out of the while loop:
    break;
    } else {
    currentLine = "";
    }
    } else if (c != '\r') {
    currentLine += c;
    }
    // Execute commands
    if (currentLine.endsWith("GET /T")) {
    Serial.println(" command received to Blink ESP32 Module");
    BlinkLED();BlinkLED();BlinkLED();
    }
    if (currentLine.endsWith("GET /R")) {
    Serial.println(" command received to restart ESP32 Module");
    BlinkLED();BlinkLED();BlinkLED();
    ESP.restart();
    }
    if (currentLine.endsWith("GET /M1")) {
    Serial.println(" command received to send BLE-command 1 to Enterprise");
    BlinkLED();
    pRemoteCharacteristic->writeValue(PmUSSECMDs[0],sizeof(PmUSSECMDs[0]));
    }
    if (currentLine.endsWith("GET /M2")) {
    Serial.println(" command received to send BLE-command 2 to Enterprise");
    BlinkLED();
    pRemoteCharacteristic->writeValue(PmUSSECMDs[1],sizeof(PmUSSECMDs[1]));
    }
    if (currentLine.endsWith("GET /M3")) {
    Serial.println(" command received to send BLE-command 3 to Enterprise");
    BlinkLED();
    pRemoteCharacteristic->writeValue(PmUSSECMDs[2],sizeof(PmUSSECMDs[2]));
    }
    if (currentLine.endsWith("GET /M4")) {
    Serial.println(" command received to send BLE-command 4 to Enterprise");
    BlinkLED();
    pRemoteCharacteristic->writeValue(PmUSSECMDs[3],sizeof(PmUSSECMDs[3]));
    }
    if (currentLine.endsWith("GET /M5")) {
    Serial.println(" command received to send BLE-command 5 to Enterprise");
    BlinkLED();
    pRemoteCharacteristic->writeValue(PmUSSECMDs[4],sizeof(PmUSSECMDs[4]));
    }
    if (currentLine.endsWith("GET /M6")) {
    Serial.println(" command received to send BLE-command 6 to Enterprise");
    BlinkLED();
    pRemoteCharacteristic->writeValue(PmUSSECMDs[5],sizeof(PmUSSECMDs[5]));
    }
    if (currentLine.endsWith("GET /M7")) {
    Serial.println(" command received to send BLE-command 7 to Enterprise");
    BlinkLED();
    pRemoteCharacteristic->writeValue(PmUSSECMDs[6],sizeof(PmUSSECMDs[6]));
    }
    if (currentLine.endsWith("GET /M8")) {
    Serial.println(" command received to send BLE-command 8 to Enterprise");
    BlinkLED();
    pRemoteCharacteristic->writeValue(PmUSSECMDs[7],sizeof(PmUSSECMDs[7]));
    }
    if (currentLine.endsWith("GET /M9")) {
    Serial.println(" command received to send BLE-command 9 to Enterprise");
    BlinkLED();
    pRemoteCharacteristic->writeValue(PmUSSECMDs[8],sizeof(PmUSSECMDs[8]));
    }
    if (currentLine.endsWith("GET /M10")) {
    Serial.println(" command received to send BLE-command 10 to Enterprise");
    BlinkLED();
    pRemoteCharacteristic->writeValue(PmUSSECMDs[9],sizeof(PmUSSECMDs[9]));
    }
    if (currentLine.endsWith("GET /M11")) {
    Serial.println(" command received to send BLE-command 11 to Enterprise");
    BlinkLED();
    pRemoteCharacteristic->writeValue(PmUSSECMDs[10],sizeof(PmUSSECMDs[10]));
    }
    if (currentLine.endsWith("GET /M12")) {
    Serial.println(" command received to send BLE-command 12 to Enterprise");
    BlinkLED();
    pRemoteCharacteristic->writeValue(PmUSSECMDs[11],sizeof(PmUSSECMDs[11]));
    }
    if (currentLine.endsWith("GET /M13")) {
    Serial.println(" command received to send BLE-command 13 to Enterprise");
    BlinkLED();
    pRemoteCharacteristic->writeValue(PmUSSECMDs[12],sizeof(PmUSSECMDs[12]));
    }
    if (currentLine.endsWith("GET /M14")) {
    Serial.println(" command received to send BLE-command 14 to Enterprise");
    BlinkLED();
    pRemoteCharacteristic->writeValue(PmUSSECMDs[13],sizeof(PmUSSECMDs[13]));
    }
    if (currentLine.endsWith("GET /v1")) {
    Serial.println(" command received to send BLE-command 15 = v1 = Vol 100% to Enterprise");
    BlinkLED();
    pRemoteCharacteristic->writeValue(PmUSSECMDs[14],sizeof(PmUSSECMDs[14]));
    }
    if (currentLine.endsWith("GET /v2")) {
    Serial.println(" command received to send BLE-command 16 = v2 = Vol 50% to Enterprise");
    BlinkLED();
    pRemoteCharacteristic->writeValue(PmUSSECMDs[15],sizeof(PmUSSECMDs[15]));
    }
    if (currentLine.endsWith("GET /v3")) {
    Serial.println(" command received to send BLE-command 17 = v3 = Vol 30% to Enterprise");
    BlinkLED();
    pRemoteCharacteristic->writeValue(PmUSSECMDs[16],sizeof(PmUSSECMDs[16]));
    }
    if (currentLine.endsWith("GET /v4")) {
    Serial.println(" command received to send BLE-command 18 = v4 = Vol 0% to Enterprise");
    BlinkLED();
    pRemoteCharacteristic->writeValue(PmUSSECMDs[17],sizeof(PmUSSECMDs[17]));
    }
    if (currentLine.endsWith("GET /x")) {
    Serial.println(" command received to send BLE-command 19 = x = AUS to Enterprise");
    BlinkLED();
    pRemoteCharacteristic->writeValue(PmUSSECMDs[18],sizeof(PmUSSECMDs[18]));
    }
    }
    }
    client.stop();
    Serial.println("Client disconnected");
    }
    }
    P.S.: hier noch ein paar Befehle, die ich nicht im obigen Code eingebaut habe, aber leicht ergänzt werden können: aa0804c8ff (Sound: Dilithiumkern entfernt); aa0406c8ff (Lichter: Dilithiumkern); aa0401c8ff (Lichter: Konsole); aa03be00ff → Vol = 75%​;
    Zuletzt geändert von MarkusCosi; vor 21 Stunden.
  • MarkusCosi
    LoxBus Spammer
    • 28.09.2023
    • 245

    #2
    kurzes Update: ich habe noch ein paar Befehle oben ergänzt. Das Setup funktioniert prinzipiell. Leider ist der Webserver-part der ganzen Geschichte am ESP32 sehr unzuverlässig.
    Über Hilfe würde ich mich sehr freuen: Link zum Post!
    Update: es funktioniert nun mit einem TimeOut im loop().
    Zuletzt geändert von MarkusCosi; In den letzten 2 Wochen.

    Kommentar

    • Noschvie
      MS Profi
      • 24.09.2018
      • 542

      #3
      Na echt beeindruckend!

      Kommentar

      • Labmaster
        Lox Guru
        • 20.01.2017
        • 2604

        #4
        Ich hab schon mehrfach den Webserver im ESP32 verwendet incl. dem ganzen Captive Portal mit der Wifi/AP/STA configuration.
        Hatted a bisher noch keine Probleme bezüglich erreichbarkeit oder das man Anfragen mehrfach senden muss.
        Ich verwende hierfür bisher die ESp32 Asyc Webserver Library von ESP32Async" und dort als Basis die Examples. ( siehe auch https://github.com/ESP32Async/ESPAsyncWebServer )

        Einfach in der Arduino IDE bei den Library nach "ESP32Async" filtern und die Library "ESP Async WebServer" installieren.
        Dann halt unter File->Examples->in Liste unter "Examples from Custom Libraries" -> "Captive Portal" und "Simple Server" anschauen, verwenden, modifizieren.



        Kommentar


        • MarkusCosi
          MarkusCosi kommentierte
          Kommentar bearbeiten
          Danke für den Tipp! Ich werde das wohl nochmal bei Gelegenheit probieren. Ich hatte tatsächlich, bevor ich den Trick mit der TimeOut gefunden hatte, schonmal mit der Library angefangen; aber dann kamen eine Reihe von Fehlermeldungen beim Kompilieren zu denen ich nichts gefunden hatte.. Aber die von dir erwähnten Beispiele hatte ich mir da noch nicht angeschaut, vllt. hatte ich irgendwo einfach einen blöden Fehler...
      • Grestorn
        LoxBus Spammer
        • 11.07.2022
        • 338

        #5
        Mach mal ein Video von der NCC-1701 und wie Du sie steuerst! Das würde mich echt interessieren... Da könnte ich glatt schwach werden.

        Kommentar

        • MarkusCosi
          LoxBus Spammer
          • 28.09.2023
          • 245

          #6
          Hallo!

          Anbei mal ein Foto der Installation. Aktuell läuft noch alles nach wie vor problemlos und zuverlässig mit dem angepassten Code wie in Post Nummer 1 oben.
          Ich nutze einen virtuellen Ausgang um aktuell einen Torpedo-Sound zu triggern bei Bewegung im Flur (kann ich aber abstellen mit einem Schalter). Für die Licht-Steuerung will ich erst die 3 Batterien im Modell durch einen 4,5V-Batterie-Netzadapter (mit Dummy-Batterien) ersetzen um die Enterprise dauerhaft mit Strom zu versorgen (ginge auch per USB-C-Kabel, wäre aber etwas unschöner IMHO)...

          Außerdem habe ich mir den Torpedo-Effekt (Licht + Sound) und ein paar weitere Effekte auf einen Shelly BLU RC 4 Button gelegt ...
          Schon eine schöne Spielerei . Ab und an gibt es die Enterprise für 199 € im Angebot...


          Klicke auf die Grafik für eine vergrößerte Ansicht

Name: IMG_6959.png
Ansichten: 159
Größe: 5,97 MB
ID: 460788 Klicke auf die Grafik für eine vergrößerte Ansicht

Name: IMG_6964.jpg
Ansichten: 144
Größe: 2,80 MB
ID: 460789Klicke auf die Grafik für eine vergrößerte Ansicht

Name: Enterprise.png
Ansichten: 114
Größe: 74,1 KB
ID: 460790

          Kommentar

          • MarkusCosi
            LoxBus Spammer
            • 28.09.2023
            • 245

            #7
            kurzes Update:
            Bei Batterie-Stromversorgung sind die Batterien (bei mir waren es Akkus) nach ca. 1-2 Wochen leer. Dabei habe ich die Sound-Effekte nicht durchgängig benutzt, und auch die anderen Effekte nicht besonders oft. Es liegt vermutlich am stetigen BLE Betrieb. Mit einem 4,5V Dummy-Batterie-Netzadapter funktioniert es auch (Kabel kann vom Maschinenraum in die Untertassensektion unauffällig hochgeleitet werden). Hierbei habe ich einen maximalen Stromfluss von ca. 126mA gemessen. Das deckte sich mit den vom Shelly Plug angezeigten 0-1W bei Betrieb der Effekte.. Mit einem 0,14mm^2 zwei-adrigen Kabel werde ich als nächstes versuchen die Stromversorgung dauerhaft hinzubekommen...

            Kommentar

            Lädt...