ruessel
Beiträge: 10499

DIY: DJI Pocket 4 Controller?

Beitrag von ruessel »

Hier ein ESP32-C6 Test-Code um zu versuchen die DJI Pocket 4 zu hacken, an der DJI Action 6 / 360 läuft es, benutzt das DJI R‑SDK‑Protokoll:

Code: Alles auswählen

#include <NimBLEDevice.h>

static NimBLEAdvertisedDevice* djiDevice = nullptr;
static NimBLEClient* client = nullptr;

static NimBLEUUID serviceUUID("0000fee7-0000-1000-8000-00805f9b34fb");
static NimBLEUUID charUUID("000036f5-0000-1000-8000-00805f9b34fb");

NimBLECharacteristic* controlChar = nullptr;

uint8_t recordToggleFrame[] = {
  0x55, 0x0A, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63
};

class AdvertisedDeviceCallbacks : public NimBLEAdvertisedDeviceCallbacks {
  void onResult(NimBLEAdvertisedDevice* dev) override {
    if (dev->getName().find("DJI") != std::string::npos) {
      Serial.println("DJI Gerät gefunden!");
      djiDevice = dev;
      NimBLEDevice::getScan()->stop();
    }
  }
};

void setup() {
  Serial.begin(115200);
  NimBLEDevice::init("ESP32-DJI-Test");

  NimBLEScan* scan = NimBLEDevice::getScan();
  scan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks());
  scan->setActiveScan(true);
  scan->start(10, false);
}

void loop() {
  if (djiDevice && !client) {
    Serial.println("Verbinde zu DJI Gerät...");
    client = NimBLEDevice::createClient();

    if (client->connect(djiDevice)) {
      Serial.println("Verbunden!");

      NimBLEService* service = client->getService(serviceUUID);
      if (!service) {
        Serial.println("Service nicht gefunden!");
        return;
      }

      controlChar = service->getCharacteristic(charUUID);
      if (!controlChar) {
        Serial.println("Characteristic nicht gefunden!");
        return;
      }

      Serial.println("Sende Record Toggle Frame...");
      controlChar->writeValue(recordToggleFrame, sizeof(recordToggleFrame));

      Serial.println("Warten auf Antwort...");
    }
  }

  delay(2000);
}
Ich kann es nicht testen, da die Kamera noch nicht vor Ort ist. Diese Software versucht Kontakt zur Kamera zu bekommen:

- aktiviert BLE

- sucht nach DJI‑Geräten

- verbindet sich automatisch

- sendet einen Record‑Toggle‑Frame (Start/Stop)

- loggt jede Antwort der Pocket 4 im seriellen Monitor

Hinweis: Das ist ein Testprogramm, kein fertiger Controller.
Es prüft nur, ob die Pocket 4 das Protokoll versteht.

Damit könnte man einen kleinen drahtlosen Controller für die Kamera basteln, den es anscheinend nicht auf dem Markt gibt. Muss ja nicht immer nur das Smartphone mit App sein ;-)

Aber: Gilt dieses Protokoll auch für die DJI Pocket 4?
Hier wird es schwierig:

- DJI veröffentlicht kein offizielles SDK für die Pocket‑Serie.
- Es gibt keine Dokumentation, dass die Pocket 4 das gleiche R‑SDK‑Protokoll nutzt wie Osmo Action 6 oder Osmo 360.
- Die Pocket 4 lässt sich zwar per Smartphone steuern, aber DJI kapselt diese Kommunikation in der DJI Mimo App.

Es wird nie langweilig, sobald meine Pocket 4 da ist (zum WE) teste ich natürlich ob es klappt. Aber selbst wenn es klappt, wird die Entwicklung einer sinnvollen Anwendung wie z.B. Radargesteuerter Auslöser etc. noch etwas dauern, es liegen noch andere Projekte hier an, diese Bastelarbeit ist zwar spannend, muss aber trotzdem etwas warten ;-)
Gruss vom Ruessel



ruessel
Beiträge: 10499

Re: DIY: DJI Pocket 4 Controller?

Beitrag von ruessel »

Heute soll meine Pocket 4 geliefert werden und ich musste feststellen, ich habe nur noch freie ESP 32D hier liegen, sind günstige nachbauten des ESP32 WROOM Link: https://www.ebay.de/itm/317665795063. Daher wurde das Sketch nochmal für diesen Chip überarbeitet, kleinere Fehler wurden beseitigt. Ich benutze die "BLE" Bibliothek von 2017 Neil Kolban, nur diese funktioniert hier. Link dazu: https://github.com/espressif/arduino-esp32 zur Zeit die Version 3.3.8

Der Sketch läuft einwandfrei, habe es schon in einen Chip geladen - nur die Kamera zum Testen fehlt. Das Skript ist nicht nur für die Pocket, sondern für alle DJI Produkte die sich verbinden können. Damit hat man(n) einen Anhalt ob sich freie Entwicklungen für das Produkt überhaupt lohnen oder DJI legt ihre Protokolle in Zukunft frei offen. ;-)

Code: Alles auswählen

// DJI Kontaktsoftwaretest Ruesseltechnik V23.04.2026
// fuer ESP32 WROOM
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>

// DJI Pocket 4 BLE UUIDs
static BLEUUID djiServiceUUID("0000fdc0-0000-1000-8000-00805f9b34fb");
static BLEUUID djiControlCharUUID("0000fdc1-0000-1000-8000-00805f9b34fb");

BLEClient* client = nullptr;
BLERemoteCharacteristic* controlChar = nullptr;

BLEAdvertisedDevice* foundDev = nullptr;
bool connected = false;
bool scanning = false;

// Statusanzeige
unsigned long lastStatus = 0;
int scanSeconds = 0;
int dotCount = 0;

// Frame zum Start/Stop der Aufnahme
uint8_t recordToggleFrame[] = {0x03, 0x01, 0x00, 0x00};

class DJIAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
  void onResult(BLEAdvertisedDevice advertisedDevice) override {

    if (advertisedDevice.isAdvertisingService(djiServiceUUID)) {
      Serial.println("DJI Pocket 4 gefunden!");

      foundDev = new BLEAdvertisedDevice(advertisedDevice);
      scanning = false;

      BLEDevice::getScan()->stop();
    }
  }
};

void startScan() {
  Serial.println("Starte Scan...");
  scanning = true;
  scanSeconds = 0;
  dotCount = 0;

  BLEScan* scan = BLEDevice::getScan();
  scan->setAdvertisedDeviceCallbacks(new DJIAdvertisedDeviceCallbacks());
  scan->setActiveScan(true);
  scan->start(3, false);
}

bool connectToPocket() {
  if (!foundDev) return false;

  Serial.println("Verbinde zur DJI Pocket 4...");

  client = BLEDevice::createClient();

  if (!client->connect(foundDev)) {
    Serial.println("Verbindung fehlgeschlagen.");
    return false;
  }

  Serial.println("Verbunden!");

  BLERemoteService* service = client->getService(djiServiceUUID);
  if (!service) {
    Serial.println("Service nicht gefunden!");
    return false;
  }

  controlChar = service->getCharacteristic(djiControlCharUUID);
  if (!controlChar) {
    Serial.println("Control Characteristic nicht gefunden!");
    return false;
  }

  Serial.println("Control Characteristic bereit.");
  connected = true;
  return true;
}

void setup() {
  Serial.begin(115200);
  Serial.println("DJI Pocket 4 Auto-Scan/Auto-Reconnect gestartet...");

  BLEDevice::init("");

  startScan();
}

void loop() {

  // 1) Wenn nicht verbunden → Status anzeigen
  if (!connected) {

    // Jede Sekunde Status ausgeben
    if (millis() - lastStatus > 1000) {
      lastStatus = millis();
      scanSeconds++;

      // Punkt-Animation
      dotCount = (dotCount + 1) % 4;
      String dots = "";
      for (int i = 0; i < dotCount; i++) dots += ".";

      Serial.print("Pocket 4 suchen ");
      Serial.print(dots);
      Serial.print("  ");
      Serial.print(scanSeconds);
      Serial.println("s");
    }

    // Falls Scan fertig, aber kein Gerät → erneut scannen
    if (!scanning && !foundDev) {
      startScan();
      return;
    }

    // Gerät gefunden → verbinden
    if (foundDev && !connected) {
      if (connectToPocket()) {
        Serial.println("Bereit für Befehle.");
      } else {
        Serial.println("Neuer Versuch in 2 Sekunden...");
        delay(2000);
        foundDev = nullptr;
        startScan();
      }
      return;
    }

    return;
  }

  // 2) Wenn verbunden → Toggle senden
  if (connected && controlChar) {
    Serial.println("Sende Aufnahme-Toggle...");

    bool ok = controlChar->writeValue(recordToggleFrame, sizeof(recordToggleFrame), false);

    if (ok) Serial.println("Frame gesendet.");
    else    Serial.println("Fehler beim Senden.");

    delay(2000);

    if (controlChar->canRead()) {
      String resp = controlChar->readValue();
      Serial.print("Antwort: ");
      Serial.println(resp);
    }

    delay(3000);
  }

  // 3) Verbindung prüfen
  if (client && !client->isConnected()) {
    Serial.println("Verbindung verloren! Starte Reconnect...");
    connected = false;
    controlChar = nullptr;
    foundDev = nullptr;
    startScan();
  }
}
Gruss vom Ruessel



ruessel
Beiträge: 10499

Re: DIY: DJI Pocket 4 Controller?

Beitrag von ruessel »

Leider kein sofortiger Erfolg! Wäre ja auch zu einfach.

Wichtigste Erkenntnis: Die Pocket 4 sendet NICHT den DJI‑Service FDC0
Ich habe erwartet:

FDC0 → DJI Remote Control Service

FDC1 → Control Characteristic

Aber die Pocket 4 sendet:

👉Service UUID: FFF0 (Generic BLE Vendor Service)
Das bedeutet:

❗ Die Pocket 4 nutzt NICHT das gleiche BLE‑Protokoll wie die Pocket 2 oder Osmo Action.
❗ Sie sendet nur einen generischen Vendor‑Service (FFF0), kein DJI‑Remote‑Control‑Service.

Ich gebe aber noch nicht auf!

Dahinter bin ich mit diesem Programm gekommen, ein Schnüffler für alle Geräte in der Umgebung!
Die Osmo Pocket 4 meldet sich auch sofort wie folgt:
---- Gerät gefunden ----
Adresse: 4c:43:f6:17:3b:60
Name: OsmoPocket4-3B5F
RSSI: -34
RAW:
Name: OsmoPocket4-3B5F, Address: xx:xx:xx:xx:xx:xx, manufacturer data: aa08210000dxxxxxxxxx000000000, serviceUUID: 0000fff0-0000-1000-8000-00xxxxxxxxxxx, rssi: -34 (genaue Daten mit x hier im Forum anonym gemacht)
------------------------

Hier der Code für meinen SUPER-RAW-BLE-DEBUG-SCANNER ;-)
Die Pocket in der Nähe vom ESP32 legen, die Empfindlichkeit des Scanners habe ich stark gedämpft, ich will ja nicht noch das Bügeleisen der Nachbarn scannen, sind so schon genug Geräte die sich hier im Haus melden.

Code: Alles auswählen

// SUPER-RAW-BLE-DEBUG-SCANNER für ESP32
// Er scannt alles was in der Umgebung ist; Ruesseltechnik 23.04.2026

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>

unsigned long lastStatus = 0;
int scanSeconds = 0;
int packetCounter = 0;

class RawCallbacks : public BLEAdvertisedDeviceCallbacks {
  void onResult(BLEAdvertisedDevice d) override {

    packetCounter++;

    // Option A: Nur jedes 10. Paket anzeigen
    if (packetCounter % 10 != 0) return;

    // Option B: Nur starke Signale anzeigen
    if (d.getRSSI() < -85) return;

    Serial.println("---- Gerät gefunden ----");

    Serial.print("Adresse: ");
    Serial.println(d.getAddress().toString().c_str());

    Serial.print("Name: ");
    Serial.println(d.getName().c_str());

    Serial.print("RSSI: ");
    Serial.println(d.getRSSI());

    Serial.println("RAW:");
    Serial.println(d.toString().c_str());

    Serial.println("------------------------\n");
  }
};

void setup() {
  Serial.begin(115200);
  delay(2000);
  Serial.println("RAW BLE Debug Scanner gestartet!");

  BLEDevice::init("");

  BLEScan* scan = BLEDevice::getScan();
  scan->setAdvertisedDeviceCallbacks(new RawCallbacks());
  scan->setActiveScan(true);   // wichtig: holt auch Scan-Response
  scan->setInterval(100);
  scan->setWindow(99);
}

void loop() {

  // Jede Sekunde Status anzeigen
  if (millis() - lastStatus > 1000) {
    lastStatus = millis();
    scanSeconds++;
    Serial.print("Scanne... ");
    Serial.print(scanSeconds);
    Serial.println("s");
  }

  BLEDevice::getScan()->start(3, false);
  delay(200);
}
Gruss vom Ruessel
Zuletzt geändert von ruessel am Do 23 Apr, 2026 15:39, insgesamt 1-mal geändert.



ruessel
Beiträge: 10499

Re: DIY: DJI Pocket 4 Controller?

Beitrag von ruessel »

Jackpot.

Jetzt geht es ins eingemachte. Ich musste noch eine Software erstellen, die die Pocket 4 veranlasst sich zu melden und verrät was sie an Eingaben erwartet. Software dazu unten.

Erkenntnis:
DJI hat das Protokoll geändert das die Pocket 2, Osmo Action, Ronin etc. nutzen:

FDC0 / FDC1 (DJI Remote Control Service)

Die Pocket 4 nutzt:

FFF0 / FFF3 / FFF4 / FFF5

Das bedeutet:

👉 Der alte DJI‑Remote‑Control‑Code funktioniert nicht.
👉 Wir müssen das Protokoll der Pocket 4 reverse‑engineeren.

Die Pocket 4 zeigt drei Services:
(1) Generic Access (0x1800)
Standard‑BLE‑Service, nichts DJI‑spezifisches.

0x2A00 = Device Name
→ Wert: OsmoPocket4-5879
→ READ + WRITE (hier könnte man den Namen ändern)

(2) Generic Attribute (0x1801)
Standard‑Service für Service‑Änderungen. Firmware?
Nicht relevant für Steuerung.

(3) Vendor‑Service FFF0 (wichtig!)
Das ist der einzige DJI‑spezifische Service, den die Pocket 4 freigibt. Service UUID: FFF0

Nächster Schritt: Wir müssen herausfinden, welche Characteristic reagiert
Dazu brauchen wir einen Command‑Tester, der:

- sich verbindet

- FFF3, FFF4 und FFF5 einzeln testet

- kleine Test‑Frames sendet

- schaut, ob die Pocket 4 antwortet

- schaut, ob NOTIFY‑Events kommen

Damit finden wir heraus:

- welche Characteristic Befehle annimmt

- welches Format DJI erwartet

- ob Start/Stop möglich ist

- ob die Kamera eine Session‑ID oder ein Pairing‑Token braucht

das alles dauert nun ein wenig, auch ist es genug für heute...... wir sind jetzt mal sehr optimistisch und damit wirklich kurz davor, die Pocket 4 vollständig zu kontrollieren!

Code: Alles auswählen

// SERVICES DER POCKET 4 auslesen.
// Ruesseltechnik 23.04.2026

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEClient.h>

static BLEAddress pocketAddr("4c:43:f6:17:3b:60");  // Deine Pocket 4 MAC hier einsetzen, mein SUPER-RAW-BLE-DEBUG-SCANNER Sketch dazu benutzen!!
BLEClient* client;

void printHexFromString(const String &data) {
  Serial.print("    Value (hex): ");
  for (size_t i = 0; i < data.length(); i++) {
    char buf[4];
    sprintf(buf, "%02X ", (uint8_t)data[i]);
    Serial.print(buf);
  }
  Serial.println();
}

void exploreServices(BLEClient* client) {
  auto services = client->getServices();

  Serial.println("\n==============================");
  Serial.println("   SERVICES DER POCKET 4");
  Serial.println("==============================");

  for (auto const& entry : *services) {
    BLERemoteService* service = entry.second;

    Serial.print("\nService UUID: ");
    Serial.println(service->getUUID().toString().c_str());

    auto chars = service->getCharacteristics();

    for (auto const& centry : *chars) {
      BLERemoteCharacteristic* ch = centry.second;

      Serial.print("  Characteristic UUID: ");
      Serial.println(ch->getUUID().toString().c_str());

      Serial.print("    Properties: ");
      if (ch->canRead()) Serial.print("READ ");
      if (ch->canWrite()) Serial.print("WRITE ");
      if (ch->canWriteNoResponse()) Serial.print("WRITE_NR ");
      if (ch->canNotify()) Serial.print("NOTIFY ");
      if (ch->canIndicate()) Serial.print("INDICATE ");
      Serial.println();

      // Wert auslesen, falls möglich
      if (ch->canRead()) {
        String val = ch->readValue();  
        printHexFromString(val);
      }
    }
  }
}

void setup() {
  Serial.begin(115200);
  delay(2000);
  Serial.println("Pocket 4 BLE Service Explorer gestartet!");

  BLEDevice::init("");

  client = BLEDevice::createClient();
}

void loop() {

  if (!client->isConnected()) {
    Serial.println("Verbinde mit Pocket 4...");

    if (client->connect(pocketAddr)) {
      Serial.println("Verbunden!");
      exploreServices(client);
    } else {
      Serial.println("Verbindung fehlgeschlagen. Neuer Versuch in 3s...");
      delay(3000);
      return;
    }
  }

  Serial.println("\nFertig. Explorer läuft nicht erneut.");
  while (true) delay(1000);
}

Gruss vom Ruessel



 Aktuelle Beiträge [alle Foren]
 
» Kann man noch MUTIG Filme produzieren? MARTIN MOSZKOWICZ
von MaxSchreck - Fr 6:01
» DaVinci Resolve 21 Photo - Der Anfang vom Ende für Adobe Lightroom?
von rob - Fr 0:18
» GoPro MISSION 1 Pro - 8K-Actioncams mit 1-Zoll-Sensor und MFT-Mount
von iasi - Do 23:12
» DJI Osmo Pocket 4 kommt noch im April - interner Speicher und neue Pro-Version
von Jörg - Do 23:04
» Was schaust Du gerade?
von Darth Schneider - Do 22:21
» Nikon teasert erste Cine lens (mit AF)
von pillepalle - Do 21:41
» Kinefinity VISTA: Kompakte Cine-Cam erklärt: Sensor, Formate, Verfügbarkeit und Preis
von MarcusG - Do 21:05
» Was hast Du zuletzt gekauft?
von Funless - Do 20:07
» DJI Lite X1 und Lite 1 - Neue Einsteiger-Drohnen mit 360°-Hinderniserkennung
von slashCAM - Do 18:21
» DaVinci Resolve 21 Photo ausführlich im Screencapture Workshop erklärt
von slashCAM - Do 17:09
» Handbrake oder gibt es ne bessere Möglichkeit?
von cantsin - Do 16:27
» Cartoni Hyperroll - modulares Roll-Rig für kreative Kamerafahrten
von Darth Schneider - Do 15:18
» Flusskreuzfahrt Teil 4: Braunschweig
von Riki1979 - Do 14:51
» DIY: DJI Pocket 4 Controller?
von ruessel - Do 13:45
» FILMFÖRDERUNG FÜR ALLE?
von Nigma1313 - Do 12:24
» BlackRAW Visor App - .braw-Dateien von SSD am iPhone sichten, graden und mehr
von slashCAM - Do 11:57
» Sony HXR-NX80 auf v Mount Anschliessen
von rush - Mi 22:30
» 1000€ Richtmikro für Hochzeitsfilme
von johnnycash89 - Mi 22:26
» Musikvideo mit KI und realen Aufnahmen
von Serge - Mi 20:32
» SmallRig: Gut durchdachtes neues Schulterrig und mobiles Effektlicht erklärt
von Darth Schneider - Mi 18:34
» Adobe erklärt die Details zum neuen Premiere Color Mode
von berlin123 - Mi 18:15
» Kinefinity Vista - kompakte 6K-Vollformat Cine-Cam gesichtet
von Darth Schneider - Mi 18:05
» Flusskreuzfahrt Teil 3: Minden
von Riki1979 - Mi 17:51
» Sennheiser HD 480 PRO - neuer geschlossener Referenzkopfhörer für Studio und Live
von Pianist - Mi 16:22
» Insta360 Mic Pro - Funkmikro mit farbigem E-Ink-Display und 32-Bit Audio Float
von ruessel - Mi 12:17
» Flusskreuzfahrt, Teil 2: Bremen
von Bildlauf - Mi 11:19
» Gerald Undone geht in Pension
von Rick SSon - Mi 11:17
» Panasonic LUMIX S 40mm F2 - neues, kompaktes Standardobjektiv
von Rick SSon - Mi 11:11
» Der Combo Stand
von pillepalle - Mi 10:15
» Blackmagic im Interview: Resolve Photo, Fairlight Live und Camera App Update erklärt
von Jott - Mi 9:49
» Warum Netzwerken wichtiger ist als Talent! FILMREIF
von 7River - Mi 9:39
» RØDELink II vorgestellt - 32-Bit-Float, UHF-Übertragung von Lectrosonics
von rush - Mi 7:04
» SD+HD Bearbeitung/Restauration/Digitalisierung mit VD1/2
von Jens65 - Di 23:01
» Wo liegen die Schwachstellen bei meinem System?
von blacktopfieber - Di 21:08
» NAB 2026 ...dies und das
von Jörg - Di 15:25