Open gavalierm opened 7 months ago
Hi @gavalierm - it sounds like an interesting approach to working with the cameras - being able to separate the data and send it out instead of interpreting and processing it on the ESP32 sounds possible.
I think the Bluetooth connection would need to be maintained between the camera and the ESP32. I'm not familiar with passing connection handshakes, you probably know more about this than me.
I have limited time to work on my own project, unfortunately, as I'm working on another project. However, I am happy to try and help.
Working from my code base would be my preference (e.g. forked).
Are you planning on making the new project a public repository?
Hi @marklysze i m glad to hear that.
Yes it can be public, why not. But in fact, i tried to find this approach on github but i found nothing. So it looks like nobody needs this or nobody got this idea :) and how nobody use this public issue to create own bussines :)
Like i said i m not C programmer so i do not know how pointers work, why and how implement good and stable C code, if something works i do not know why really.
Because i use Arduino when it came out i m stuck on Arduino IDE and "arduino thinking" attitude. But i see that do not using Arduino wrappers build above the ESP IDF API is better idea, but i do not know how.
My project has simple idea (i sent to you a invitation to my private repo but i no think is still working.. 1yr old..) but need some better programmer..
The scenario is easy: Phase A
Phase B:
Phase C: Subscribe all bluetooth Services (ingoing, outgoing, camera state and so on..) and simply forward all payloads to the mqtt an vice versa like MQTT topic like "topic/espid/tx" for sending and MQTT topic like "topic/espid/rx" fo receiving
All payloads are now forwarded and managed MQTT client and you can parse payloads waymore easy via javascript/python/whatever without compiling and flashing ESP again and again when logic needs update.
And ofcourse you can build own PWA with better looking UI, you are not limited to BT range, ESP display/touch capability, because your ESP is now transef all data to the mqtt topics and you can sit on you laptop and control cameras over PWA in your browser using mqtt client.
PWA mqtt client <-> mqtt broker server <-> mqtt client on ESP <-> bridge logic on esp <-> BT client on ESP <-> Camera
Because ESP32-C3 and ESP32-S3 have BT/WIFI coexitence it can be done - but like i said i cant write clean ESP code which make sense and you it looks like you can. So i share this idea with you and hope you can do this (if you like the idea)
Appreciate your detailed response and thoughts on an approach.
I have just watched a short video on MQTT to try and understand it :). Sounds like it could work.
I'm sure I'll have more questions to help establish feasibility. I'll ask a few, though I'll be honest that I'm a bit concerned that we can't both do the C++ programming, as there will be a bit involved and my time is, unfortunately, limited.
However, let me ask a few more questions to see if we can make something:
If the goal is to take all of the camera specific code into a front end application that could work but from my experience to date, camera setting changes have to be very specific and having access to a number of different models would help. The physical cameras are needed for development. For example, I tried coding for on URSA Mini Pro G2 but the resolution changing wasn't working because I needed to knov whicm resolutions were full sensor and which were windowed - it wasn't as simple as just sending a resolution value to the camera. The issue with the BMD protocol is there is no reply to a packet sent so in doesn't tell you if something wrong. I just want to give this example because replicating what is being done in C++ in the front end will be required if it is handling all of the configuration.
In fact MQTT is not the case (which i tried to use this time), it can be Websocket (which i use a lot), ESP-NOW (i never used because need another ESP-NOW - TCP/IP bridge to comunicate with other non-ESP devices).
The BMD protocol you have mentioned in your readme.md https://documents.blackmagicdesign.com/DeveloperManuals/BlackmagicCameraControl.pdf https://github.com/coral/blackmagic-camera-protocol/blob/main/PROTOCOL.json
Bluetooth messages is the same as SDI messages so example trigger autofocus on camera 4
4 4 0 0 0 1 0 0
In fact Bluetooth protocol ignore the "camera id" bit, because you communicate directly with the single one camera. So camera accept via Bluetooth every camera id (so im no worry about) and i want to keep as much logic as possible in javascript not in ESP, so i want to send data info ESP_ID topic and decide what data this ESP needs.
SDI protocol is "broadcast" so every single device catch the data and parse "device bit" and if match with their own use data, but this means that every ESP need to know what device id represents and this means more logic into ESP and more handshaking between ESP and Node.js to declare device id.
So easy way is do not wory about device_id in ESP - during MQTT handshaking determine the ESP_ID (this is constatn based on ESP mac addr, or cpu_id...) and use dynamic mapping in Node.js like "this cpu id = this SDI device ID".
And this can be managed fawlessly in some PWA.
So when you receive payload from BT is uint8_t *pData, size_t length, message which can be directly forward to the MQTT as "string"
I send data from MQTT to BT with this part of code: Message was like [4,6,0,0,4,2,128,1,0x33,0x01,0,0]
if(BT.isConnected()){
//
char* token = strtok(payload + 1, delimiter); // + 1 Skip the first '[' character
size_t i = 0;
uint8_t* pData = new uint8_t[length];
while (token != NULL && i < length - 1) { // -1 Skip the last ']' character
uint8_t value = 0;
if (strncmp(token, "0x", 2) == 0) { // Check if token starts with "0x"
value = static_cast<uint8_t>(strtoul(token + 2, NULL, 16)); // Convert token to hex value
} else {
value = static_cast<uint8_t>(atoi(token)); // Convert token to decimal value
}
if(LOGGING) Serial.println(value);
pData[i] = value;
i++;
token = strtok(NULL, delimiter);
//value = =round(((range + 2047)/2048) * (2048 * P2))
}
BT.send(pData,i);
// Free the dynamically allocated memory
delete[] pData;
}
and sendig message from BT to MQTT
static void receiveNotify(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify)
{
if (length < 4) {
// Invalid packet, ignore it
return;
}
//skip group 9 is not documented and i do not know what it is
if(pData[4] == 9){
return;
}
//
if(LOGGING) Serial.println("CONTROL: Publish change");
char buffer[length * 4];
int pos = 0;
buffer[pos++] = '[';
for (int i = 0; i < length; i++) {
if (pData[i] == 0) {
pos += sprintf(&buffer[pos], "0,");
} else {
pos += sprintf(&buffer[pos], "%d,", pData[i]);
}
}
if (pos > 0) {
buffer[pos - 1] = ']'; // Replace the last comma with null terminator
}
buffer[pos++] = '\0';
//const char* str = reinterpret_cast<const char*>(pData);
MQTT.publish(("btbm/" + tally_id + "/ccu/change").c_str(), 0, true, buffer);
}
And i plan write SDI protocol parser in javascript to use with for human needs... so when operator want to change isto to "400" i run
mqtt.send('topic',encodeDataToSDIProtocol('iso',400)); //return payload message for BT
and vice versa
mqtt.reveive('topic',decodeDataToSDIProtocol(payload_from_bt)); //return human readable data like "iso/400"
In recap:
And maybe i need to say that BT/WIFI coexistence was something that i can not trully tested.
It works like expected but sometimes i just randomly get ESP to reboot. And because i wasnt 100 percent sure that code is OK and Healthy i can not say that RF coexistence is bad idea OR just my code is rubbish with memory leaks...
Okay, I understand better what you're thinking.
In the code base there's this class that handles incoming packets of data: https://github.com/marklysze/Magic-Pocket-Control-ESP32/blob/main/src/CCU/CCUDecodingFunctions.cpp
So the ESP32 could send the bytes to the Websocket instead.
Similarly, there's a function in: https://github.com/marklysze/Magic-Pocket-Control-ESP32/blob/main/src/Camera/BMDCameraConnection.cpp
Called sendBytesToOutgoing that will send bytes to the camera, so this could be called when receiving data from the Websocket.
It sounds like an interesting solution. If you can provide some sample code (whether it's yours or other code on GitHub) for ESP32 WiFi connect/maintain/disconnect, and ESP32 WebSocket then I can see how feasible it is to integrate into the code base. Then perhaps your repository could be the front end that accepts data and sends data back?
Wifi on ESP32 in arudino wrapper is very straightforward.
Still i will try to use MQTT not Websocket directly, and if this idea fails i rewrite it back to the websocket (which is more reasonable for bi-direct messaging)
If i use websocket i need to extend "SDI" protocol to some new/custom payloads like "bluetooth handshaking" and "heartbeats messaging" or even "tally signaling with custom colors" i m not familiar how SDI protocol separate the CCU and TALLY or others messages.
Here is my ino sketch for working ESP32 D1 mini tally light (based on websocket messages) - this is from my private repo
/*
*
* Created on: 24.05.2015
*
*/
#include <Arduino.h>
#include <EEPROM.h>
#include <ESP8266WiFi.h>
//#include <ArduinoJson.h>
#include <WebSocketsClient.h>
#include <Hash.h>
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif
// define the number of bytes you want to access
#define EEPROM_SIZE 1
#define SSID ""
#define PASS ""
#define HOST "atemrpi.local" //bridge service
#define PORT 9240 // Enter server port
#define USE_SERIAL Serial1
#define SW_1 16
#define SW_2 14
#define SW_3 12
#define SW_4 13
#define LED_PIN 0 //14;
#define NUMPIXELS 7 // Popular NeoPixel ring size
//ESP8266WiFiMulti WiFiMulti;
WebSocketsClient webSocket;
Adafruit_NeoPixel pixels(NUMPIXELS, LED_PIN, NEO_GRB + NEO_KHZ800);
bool tally_locked = false;
int luminance = 255; //at full
uint32_t black = pixels.Color(0, 0, 0);
uint32_t gray = pixels.Color(100, 80, 60);
uint32_t red = pixels.Color(255, 0, 0);
uint32_t green = pixels.Color(0, 255, 0);
uint32_t blue = pixels.Color(0, 0, 255);
uint32_t orange = pixels.Color(255, 100, 0);
uint32_t white = pixels.Color(255, 255, 255);
uint32_t violet = pixels.Color(255, 0, 250);
uint32_t pink = pixels.Color(255,190,200);
uint32_t yellow = pixels.Color(255, 200, 0);
int tally_id = 0;
bool io_status = false;
bool wifi_status = false;
bool atem_status = true; //not handled for now
int pgm = 0;
int pvw = 0;
int trs = 0;
uint32_t signal_color = black;
uint32_t tally_color = gray;
//
unsigned long interval=100;
// Tracks the time since last event fired
unsigned long previousMillis=0;
//
bool signal_state = false;
//
String hostname = "ESP Tally";
void doSignal(uint32_t color, int start_led = 0, int end_led = NUMPIXELS, float koef = 1){
if( color == signal_color ){
return;
}
USE_SERIAL.println("SIGNAL");
//fill strip with same color from start to end, others pins stay untouched
pixels.fill(black, 0, NUMPIXELS); //reset tp full black
pixels.fill(color, start_led, end_led);
//set luminance of whole strip // WARNING CAN BE LOSS by using lower value without reseting the color to the full bright
pixels.setBrightness(luminance);
//
int lumi = round(luminance * koef);
if( lumi < 50 ){
lumi = 50;
}else if( lumi > 255 ){
lumi = 255;
}
//
pixels.setBrightness(lumi);
//send defined pattern to the led strip
pixels.show();
signal_color = color; //set last
}
void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_ERROR:
USE_SERIAL.printf("[WSc] Error!!!\n");
break;
case WStype_DISCONNECTED:
USE_SERIAL.printf("[WSc] Disconnected!\n");
io_status = false;
break;
case WStype_CONNECTED: {
USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
io_status = true;
doSignal(white,0,1);
// send message to server when Connected
String message = "JOIN " + hostname;
webSocket.sendTXT(message);
}
break;
case WStype_TEXT:
USE_SERIAL.printf("[WSc] get text: %s\n", payload);
//String message = (char*)payload;
parseMessage(payload);
break;
case WStype_BIN:
USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
//hexdump(payload, length);
break;
case WStype_PING:
// pong will be send automatically
//USE_SERIAL.printf("[WSc] get ping\n");
break;
case WStype_PONG:
// answer to a ping we send
//USE_SERIAL.printf("[WSc] get pong\n");
break;
default:
USE_SERIAL.printf("[WSc] Default event\n");
break;
}
}
String converter(uint8_t *str){
String message = String((char *)str);
message.trim();
return message;
}
void parseMessage( uint8_t * payload ){
String message = converter(payload);
int cmd_delimiter_index = message.indexOf(' ');
//USE_SERIAL.println(cmd_delimiter_index);
String cmd = "Test";
if( cmd_delimiter_index > 1){
cmd = message.substring(0,cmd_delimiter_index);
}else{
cmd = message;
}
//USE_SERIAL.println(cmd);
if(cmd.equals("TALLY-LOCK")){
String id = message.substring(cmd.length());
if(id.toInt() == 1){
tally_locked = true;
tally_color = yellow;
}else{
tally_locked = false;
tally_color = gray;
}
}else if(cmd.equals("TALLY-PGM") || cmd.equals("TALLY-PVW")){
String id = message.substring(cmd.length());
//id = id.substring(0,id.indexOf(' '));
USE_SERIAL.println(id);
if(cmd.equals("TALLY-PGM")){
trs = pgm;
pgm = id.toInt();
}
if(cmd.equals("TALLY-PVW")){
pvw = id.toInt();
}
if( pgm == tally_id ){
tally_color = red;
}else if( pvw == tally_id ){
tally_color = green;
}else{
tally_color = gray;
}
}else if(cmd.equals("TALLY-TRS")){
if( pgm == tally_id || pvw == tally_id || trs == tally_id){
tally_color = red;
}
}else if(cmd.equals("SET-LUMINANCE")){
String data = message.substring(cmd.length());
luminance = data.toInt();
if( luminance < 50 ){
luminance = 50;
}else if( luminance > 255 ){
luminance = 255;
}
USE_SERIAL.println(luminance);
pixels.setBrightness(luminance);
pixels.show(); // light up
EEPROM.write(0, luminance);
EEPROM.commit();
}
}
void setup() {
pixels.begin(); // INITIALIZE NeoPixel pixel object (REQUIRED)
USE_SERIAL.begin(115200); delay(500); //Waiting for serial
// initialize EEPROM with predefined size
EEPROM.begin(EEPROM_SIZE);
// read the last LED state from flash memory
if( EEPROM.read(0) > 5 ){
luminance = EEPROM.read(0);
}
doSignal(white,0,1); //power on
USE_SERIAL.setDebugOutput(true);
USE_SERIAL.println();
USE_SERIAL.println();
USE_SERIAL.println();
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH); //Powerpack keepalive internal led //this is not needed, becayse i dont use whole off (my black is single gray)
pinMode(SW_1, INPUT_PULLUP); //NO INPUT_PULLUP on wemos D1 //PULLED hardway by 10k resistor
pinMode(SW_2, INPUT_PULLUP);
pinMode(SW_3, INPUT_PULLUP);
pinMode(SW_4, INPUT_PULLUP);
delay(1000); //human reaction to power on
if( digitalRead(SW_1) == LOW ){
tally_id += 1;
}
if( digitalRead(SW_2) == LOW ){
tally_id += 2;
}
if( digitalRead(SW_3) == LOW ){
tally_id += 3;
}
if( digitalRead(SW_4) == LOW ){
tally_id += 4;
}
for (int i = 0; i < tally_id; ++i)
{
doSignal(red,0,1);
delay(100);
doSignal(black);
delay(400);
}
USE_SERIAL.print( "TALLY ID: " ); USE_SERIAL.println( tally_id );
//strcpy(watch_id, id.c_str());
WiFi.mode(WIFI_STA); //SET STATION MODE // wifi simple client to avoid switching between modes
//Get Default Hostname
USE_SERIAL.printf("Default hostname: %s\n", WiFi.hostname().c_str());
//Set new hostname
hostname = WiFi.hostname() + "-" + tally_id;
//String hostname = WiFi.hostname() + "-" + tally_id;
WiFi.hostname(hostname);
//Get Current Hostname
USE_SERIAL.printf("New hostname: %s\n", WiFi.hostname().c_str());
WiFi.begin(SSID, PASS);
//WiFi.disconnect();
while(WiFi.status() != WL_CONNECTED) {
doSignal(blue,0,1);
delay(100);
doSignal(black);
delay(400);
}
// server address, port and URL
webSocket.begin(HOST, PORT, "/");
// event handler
webSocket.onEvent(webSocketEvent);
// use HTTP Basic Authorization this is optional remove if not needed
//webSocket.setAuthorization("user", "Password");
// try ever 5000 again if connection has failed
webSocket.setReconnectInterval(5000);
// start heartbeat (optional)
// ping server every 15000 ms
// expect pong from server within 3000 ms
// consider connection disconnected if pong is not received 2 times
webSocket.enableHeartbeat(15000, 3000, 2);
}
void loop() {
unsigned long currentMillis = millis();
if(WiFi.status() == WL_CONNECTED) {
webSocket.loop();
wifi_status = true;
} else {
webSocket.disconnect();
wifi_status = false;
}
if( wifi_status == false || io_status == false || atem_status == false ){
uint32_t color = black;
if( wifi_status == false ){
color = blue;
}else if( io_status == false ){
color = orange;
}else if( atem_status == false ){
color = violet;
}
// How much time has passed, accounting for rollover with subtraction!
if ((unsigned long)(currentMillis - previousMillis) >= interval) {
// It's time to do something!
//USE_SERIAL.println("Signal");
if(signal_state){
interval = 100;
doSignal(black);
}else{
interval = 900;
doSignal(color,0,1); //on
}
signal_state = !signal_state;
// Use the snapshot to set track time until next event
previousMillis = currentMillis;
}
//
}else{
// Get snapshot of time
interval = 0;
if( tally_color == gray ){
interval = 50;
}
if(tally_locked){
tally_color = yellow;
}
// How much time has passed, accounting for rollover with subtraction!
if ((unsigned long)(currentMillis - previousMillis) >= interval) {
USE_SERIAL.println("SIGNALING...");
if( tally_color == gray ){
doSignal(tally_color,0,1); //o
}else if( tally_color == green ){
doSignal(tally_color,1); //o
}else{
doSignal(tally_color); //o
}
previousMillis = currentMillis;
}
}
}
Thanks for sharing that. Due to my time limitations, I'd suggest you start on your side of things and when you have some working code I'll try and tie in with it by sending camera commands through to you (the byte data) and taking command data I receive from you and send to the camera.
I can write some working code and add some notes like:
//TODO - send this data to camera here
//TODO - receive data from camera here
There is some limitations what i m thinikg about:
onDataCallback(mqtt, buffer_from_bt) {
//called on BT onData event
//sone validating stuff here
//like inject destination byte, source byte
//and send data
mqtt.publish("topic", buffer_from_bt)
}
sendDataCallback(bt, buffer_from_mqtt) {
//called on MQTT onData event
switch buffer_from_mqtt[command_type_byte] {
case: ENUMS.BT_PASSCODE_EVENT
bt.setPasscode(buffer_from_mqtt)
break;
case: ENUMS.OTHER_NON_CCU_EVENT
//do something differnet like store something in memory...
//reboot
//shatever
break;
return;
}
//sone validating stuff here
//like inject destination byte, source byte
//all other data are CCU and send data
bt.writeCcuData(buffer_from_mqtt)
}
onPasscodeCallback(bt, buffer_from_mqtt) {
//called on BT security/passcode event
//sone validating studd here
//like inject destination byte, source byte
//notify that camera need passcode
mqtt.publish("topic/passcode", null)
}
Maybe the good way is add fake BOARD like "ESP_mqtt" to your code context and implement for this board only callbacks without display stuff, if your code have this scalability.
And in main.c users define this board and will have access to the "mqtt bridge".
Data Block have data set to the max value
I m not sure thats 100 correct but i can update this when i found some mistakes.
This is auto generated from the data sheet.
https://github.com/gavalierm/blackmagic-camera-control-protocol
0 0 Lens Focus
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 06 00 00 00 00 80 00 00 08 00 00
0 1 Lens Instantaneous autofocus
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 00 01 00 00
0 2 Lens Aperture (f-stop)
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 06 00 00 00 02 80 00 FF 7F 00 00
0 3 Lens Aperture (normalised)
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 06 00 00 00 03 80 00 00 08 00 00
0 4 Lens Aperture (ordinal)
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 06 00 00 00 04 02 00 00 01 00 00
0 5 Lens Instantaneous auto aperture
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 00 05 00 00
0 6 Lens Optical image stabilisation
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 00 06 00 00
0 7 Lens Set absolute zoom (mm)
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 06 00 00 00 07 02 00 A0 0F 00 00
0 8 Lens Set absolute zoom (normalised)
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 06 00 00 00 08 80 00 00 08 00 00
0 9 Lens Set continuous zoom (speed)
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 06 00 00 00 09 80 00 00 08 00 00
1 0 Video Video mode
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 01 00 01 00
1 1 Video Gain (up to Camera 4.9)
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 05 00 00 01 01 01 00 7F 00 00 00
1 2 Video Manual White Balance
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 08 00 00 01 02 02 00 32 32 00 00
1 3 Video Set auto WB
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 01 03 00 00
1 4 Video Restore auto WB
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 01 04 00 00
1 5 Video Exposure (us)
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 08 00 00 01 05 03 00 10 A4 00 00
1 6 Video Exposure (ordinal)
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 01 06 02 00
1 7 Video Dynamic Range Mode
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 05 00 00 01 07 01 00 02 00 00 00
1 8 Video Video sharpening level
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 05 00 00 01 08 01 00 03 00 00 00
1 9 Video Recording format
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 01 09 02 00
1 10 Video Set auto exposure mode
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 05 00 00 01 0A 01 00 04 00 00 00
1 11 Video Shutter angle
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 08 00 00 01 0B 03 00 A0 8C 00 00
1 12 Video Shutter speed
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 08 00 00 01 0C 03 00 88 13 00 00
1 13 Video Gain
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 05 00 00 01 0D 01 00 7F 00 00 00
1 14 Video ISO
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 08 00 00 01 0E 03 00 FF FF FF 7F
1 15 Video Display LUT
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 01 0F 01 00
1 16 Video ND Filter
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 06 00 00 01 10 80 00 FF 7F 00 00
2 0 Audio Mic level
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 06 00 00 02 00 80 00 00 08 00 00
2 1 Audio Headphone level
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 06 00 00 02 01 80 00 00 08 00 00
2 2 Audio Headphone program mix
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 06 00 00 02 02 80 00 00 08 00 00
2 3 Audio Speaker level
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 06 00 00 02 03 80 00 00 08 00 00
2 4 Audio Input type
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 05 00 00 02 04 01 00 03 00 00 00
2 5 Audio Input levels
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 08 00 00 02 05 80 00 00 00 08 00
2 6 Audio Phantom power
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 02 06 00 00
3 0 Output Overlay enables
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 03 00 00 00
3 1 Output Frame guides style (Camera 3.x)
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 05 00 00 03 01 01 00 08 00 00 00
3 2 Output Frame guides opacity (Camera 3.x)
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 06 00 00 03 02 80 00 00 08 00 00
3 3 Output Overlays (replaces .1 and .2 above from Cameras 4.0)
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 08 00 00 03 03 01 00 64 64 64 64
4 0 Display Brightness
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 06 00 00 04 00 80 00 00 08 00 00
4 1 Display Exposure and focus tools
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 04 01 00 00
4 2 Display Zebra level
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 06 00 00 04 02 80 00 00 08 00 00
4 3 Display Peaking level
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 06 00 00 04 03 80 00 00 08 00 00
4 4 Display Color bar enable
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 05 00 00 04 04 01 00 1E 00 00 00
4 5 Display Focus Assist
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 04 05 01 00
4 6 Display Program return feed enable
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 05 00 00 04 06 01 00 1E 00 00 00
5 0 Tally Tally brightness
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 06 00 00 05 00 80 00 00 08 00 00
5 1 Tally Front tally brightness
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 06 00 00 05 01 80 00 00 08 00 00
5 2 Tally Rear tally brightness
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 06 00 00 05 02 80 00 00 08 00 00
6 0 Reference Source
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 05 00 00 06 00 01 00 02 00 00 00
6 1 Reference Offset
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 06 01 03 00
7 0 Configuration Real Time Clock
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 07 00 03 00
7 1 Configuration System language
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 07 01 05 00
7 2 Configuration Timezone
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 07 02 03 00
7 3 Configuration Location
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 07 03 04 00
8 0 Color Correction Lift Adjust
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 0C 00 00 08 00 80 00 00 00 00 00 10 00 00 00
8 1 Color Correction Gamma Adjust
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 0C 00 00 08 01 80 00 00 00 00 00 20 00 00 00
8 2 Color Correction Gain Adjust
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 0C 00 00 08 02 80 00 FF FF FF FF 7F 00 00 00
8 3 Color Correction Offset Adjust
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 0C 00 00 08 03 80 00 00 00 00 00 40 00 00 00
8 4 Color Correction Contrast Adjust
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 08 00 00 08 04 80 00 00 00 10 00
8 5 Color Correction Luma mix
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 06 00 00 08 05 80 00 00 08 00 00
8 6 Color Correction Color Adjust
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 08 00 00 08 06 80 00 00 00 10 00
8 7 Color Correction Correction Reset Default
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 08 07 00 00
10 0 Media Codec
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 0A 00 01 00
10 1 Media Transport mode
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 0A 01 01 00
10 2 Media Playback Control
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 0A 02 01 00
10 3 Media Still Capture
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 0A 03 00 00
11 0 PTZ Control Pan/Tilt Velocity
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 08 00 00 0B 00 80 00 00 00 08 00
11 1 PTZ Control Memory Preset
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 06 00 00 0B 01 01 00 05 05 00 00
12 0 Metadata Reel
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 06 00 00 0C 00 02 00 E7 03 00 00
12 1 Metadata Scene Tags
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 0C 01 01 00
12 2 Metadata Scene
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 0C 02 05 00
12 3 Metadata Take
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 06 00 00 0C 03 01 00 63 63 00 00
12 4 Metadata Good Take
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 0C 04 00 00
12 5 Metadata Camera ID
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 0C 05 05 00
12 6 Metadata Camera Operator
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 0C 06 05 00
12 7 Metadata Director
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 0C 07 05 00
12 8 Metadata Project Name
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 0C 08 05 00
12 9 Metadata Lens Type
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 0C 09 05 00
12 10 Metadata Lens Iris
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 0C 0A 05 00
12 11 Metadata Lens Focal Length
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 0C 0B 05 00
12 12 Metadata Lens Distance
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 0C 0C 05 00
12 13 Metadata Lens Filter
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 0C 0D 05 00
12 14 Metadata Slate Mode
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 0C 0E 01 00
128 0 RGB Tally PGM
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 07 00 00 80 00 01 00 02 02 02 00
128 1 RGB Tally Set tally color
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 0C 00 00 80 01 02 00 FF FF FF FF 00 00 00 00
129 0 Bluetooth Disconnect
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 81 00 00 00
129 1 Bluetooth Connect
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 81 01 02 00
129 4 Bluetooth Passcode
De Le Cm __ Ca Pa Ty Op 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_
FF 04 00 00 81 04 03 00
Maybe the good way is add fake BOARD like "ESP_mqtt" to your code context and implement for this board only callbacks without display stuff, if your code have this scalability.
And in main.c users define this board and will have access to the "mqtt bridge".
Yep, that's an idea - could add an MQTT specific board for platformio and in that main file could set parameters so that it uses MQTT instead of UI.
When your side is somewhat functional and I have time I'll run it and see if I can do my side to send and receive commands.
@marklysze
So i have some work done.
Disclaimer: Because i do not know if i m doing it right i do my best :)
What i choose:
What i have done:
Interesting part:
Whan i m missing and what frustrate me
I think i was done BUT i realize that i can get any notification from CCU. I do not know WHY! because my subscribe is all the same as Timecode or CameraStatus.
I tried to write to the camera but without results (but this is not the case now, it was only one attempt)
I really missing something and i do not know what it is.
You've done a bit! Would you be able to point me to the code you've written?
I think i m fall in rabbit hole with Nimble ESP IDF. I think with basic Arduino BLE lib i will be done already....... I m struggling with basic i think in Nimble.
https://github.com/espressif/esp-idf/issues/12989
My code is here https://github.com/gavalierm/btbm-tally and you have contributing rights, but because i rewrite whole code again and again becase testing and trying to get it done... maybe fork or branch will be good idea.
It is one big file main.c with included gatt_client.c to split thing. Because i do not get things done i do not need to keep code clean for now.
Hey @gavalierm, gotcha - this does sometimes take us down paths that we weren't expecting!
I'll be honest that I'll find it hard to contribute to the repository because it's not in an environment I normally work in (VSCode and PlatformIO). If I do get some time to download it and load it I can see if I can get it going in VSCode/PlatformIO.
If there's any way you can run VSCode and PlatformIO with it I'd be able to help more...
I tried VScode many times, and still fallback to the my Sublime Text :)
Maybe sometime that PlatformIO will e standalone app like aruduino IDE. Or even better, somebody integrate the platform io to the Sublime text.
But meanwhile, ma code is done (all my headache) was described in the ESP-IDF issue.
If you wan to try my code you can, maybe it will stay public forever maybe not. But you will still have access as contributor.
Maybe you can help me check the syntex and resolve some "unprofessional" syntax? Like pointers, declarations or comparing and merging the data?
You can try my code, it is working now.
Cool, what device are you using?
Hello, in past i wrote private lib based on BlueMagic32 (but used only the connection part) to create bridge between BLE and Websocket.
My target was very simple, pass all data (including connection handshake) to the Websocket (or MQTT) and all app logic build on websocket server (node.js).
The main reason for this was better scaleability and future proof desing, becase no need to update ESP module every time when BMD protocol was changed, or something like that.
I was succesfull but the project died because lack of my time to optimize the workflow. (And i m not good on C++ or ESP).
I found this project and i like that is life and still receive new commits.
Goals:
Questions: Do you like the idea? It is possible to derivate your LIB to do all BLE stuff and coexist with Wifi stuff?
Thank you!
Underline: Major problem was BLE/Wifi coexistence. ESP C3 was ok but not perfect, ESP S3 i bought but never tried, because no time. If BLE/WIFI can not be stable second idea was put two ESPs one for BLE, one for WIFI and share messages via Serial, or something like that.