diff --git a/README.md b/README.md index ed645d8..a33a1d4 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,13 @@ furble is developed as a PlatformIO project for the M5StickC and M5StickC Plus ## Supported Cameras The following devices have actually been tested and confirmed to work: +- Fujifilm GFX100S ([@adrianuseless](https://github.com/adrianuseless)) - Fujifilm X-S10 ([@dimitrij2k](https://github.com/dimitrij2k)) - Fujifilm X-T200 ([@Cronkan](https://github.com/Cronkan)) - Fujifilm X-T30 - Fujifilm X100V -- Canon EOS M6 +- Canon EOS M6 ([@tardisx](https://github.com/tardisx)) +- Canon EOS RP ([@wolcano](https://github.com/wolcano)) ## Motivation @@ -49,6 +51,7 @@ Reports of further confirmed working Fujifilm cameras are welcome. With access to a Canon EOS M6, I was able to implement support for it. Other Canon cameras might work, but I suspect the shutter control protocol will be different. +@wolcano kindly implemented support for the Canon EOS RP. #### Protocol Reverse Engineering diff --git a/lib/furble/CanonEOS.cpp b/lib/furble/CanonEOS.cpp new file mode 100644 index 0000000..9e95a97 --- /dev/null +++ b/lib/furble/CanonEOS.cpp @@ -0,0 +1,185 @@ +#include +#include +#include +#include +#include + +#include "Furble.h" + +namespace Furble { + +CanonEOS::CanonEOS(const void *data, size_t len) { + if (len != sizeof(eos_t)) + throw; + + const eos_t *eos = (eos_t *)data; + m_Name = std::string(eos->name); + m_Address = NimBLEAddress(eos->address, eos->type); + memcpy(&m_Uuid, &eos->uuid, sizeof(uuid128_t)); +} + +CanonEOS::CanonEOS(NimBLEAdvertisedDevice *pDevice) { + m_Name = pDevice->getName(); + m_Address = pDevice->getAddress(); + Serial.println("Name = " + String(m_Name.c_str())); + Serial.println("Address = " + String(m_Address.toString().c_str())); + Device::getUUID128(&m_Uuid); +} + +CanonEOS::~CanonEOS(void) { + NimBLEDevice::deleteClient(m_Client); + m_Client = nullptr; +} + +bool CanonEOS::write_value(NimBLEClient *pClient, + const char *serviceUUID, + const char *characteristicUUID, + uint8_t *data, + size_t length) { + NimBLERemoteService *pSvc = pClient->getService(serviceUUID); + if (pSvc) { + NimBLERemoteCharacteristic *pChr = pSvc->getCharacteristic(characteristicUUID); + return ((pChr != nullptr) && pChr->canWrite() && pChr->writeValue(data, length, true)); + } + + return false; +} + +bool CanonEOS::write_prefix(NimBLEClient *pClient, + const char *serviceUUID, + const char *characteristicUUID, + uint8_t prefix, + uint8_t *data, + size_t length) { + uint8_t buffer[length + 1] = {0}; + buffer[0] = prefix; + memcpy(&buffer[1], data, length); + return write_value(pClient, serviceUUID, characteristicUUID, buffer, length + 1); +} + +/** + * Connect to a Canon EOS. + * + * The EOS uses the 'just works' BLE bonding to pair, all bond management is + * handled by the underlying NimBLE and ESP32 libraries. + */ +bool CanonEOS::connect(NimBLEClient *pClient, ezProgressBar &progress_bar, uint32_t pair_delay) { + m_Client = pClient; + + Serial.println("Connecting"); + if (!m_Client->connect(m_Address)) { + Serial.println("Connection failed!!!"); + return false; + } + + Serial.println("Connected"); + progress_bar.value(10.0f); + + Serial.println("Securing"); + if (!m_Client->secureConnection()) { + return false; + } + Serial.println("Secured!"); + progress_bar.value(20.0f); + + Serial.println("Identifying 1!"); + if (!write_prefix(m_Client, CANON_EOS_SVC_IDEN_UUID, CANON_EOS_CHR_NAME_UUID, 0x01, + (uint8_t *)FURBLE_STR, strlen(FURBLE_STR))) + return false; + + progress_bar.value(30.0f); + + Serial.println("Identifying 2!"); + if (!write_prefix(m_Client, CANON_EOS_SVC_IDEN_UUID, CANON_EOS_CHR_IDEN_UUID, 0x03, m_Uuid.uint8, + UUID128_LEN)) + return false; + + progress_bar.value(40.0f); + + Serial.println("Identifying 3!"); + if (!write_prefix(m_Client, CANON_EOS_SVC_IDEN_UUID, CANON_EOS_CHR_IDEN_UUID, 0x04, + (uint8_t *)FURBLE_STR, strlen(FURBLE_STR))) + return false; + + progress_bar.value(50.0f); + + Serial.println("Identifying 4!"); + + uint8_t x = 0x02; + if (!write_prefix(m_Client, CANON_EOS_SVC_IDEN_UUID, CANON_EOS_CHR_IDEN_UUID, 0x05, &x, 1)) + return false; + + progress_bar.value(60.0f); + + Serial.println("Identifying 5!"); + + /* write to 0xf204 */ + x = 0x0a; + if (!write_value(m_Client, CANON_EOS_SVC_UNK0_UUID, CANON_EOS_CHR_UNK0_UUID, &x, 1)) + return false; + + progress_bar.value(70.0f); + delay(pair_delay); // give camera user time to confirm pairing + + /* write to 0xf104 */ + x = 0x01; + if (!write_value(m_Client, CANON_EOS_SVC_IDEN_UUID, CANON_EOS_CHR_IDEN_UUID, &x, 1)) + return false; + + progress_bar.value(80.0f); + + Serial.println("Identifying 6!"); + + /* write to 0xf307 */ + x = 0x03; + if (!write_value(m_Client, CANON_EOS_SVC_UNK1_UUID, CANON_EOS_CHR_UNK1_UUID, &x, 1)) + return false; + + Serial.println("Paired!"); + progress_bar.value(100.0f); + + return true; +} + +void CanonEOS::shutterPress(void) { + uint8_t x[2] = {0x00, 0x01}; + write_value(m_Client, CANON_EOS_SVC_SHUTTER_UUID, CANON_EOS_CHR_SHUTTER_UUID, &x[0], 2); +} + +void CanonEOS::shutterRelease(void) { + uint8_t x[2] = {0x00, 0x02}; + write_value(m_Client, CANON_EOS_SVC_SHUTTER_UUID, CANON_EOS_CHR_SHUTTER_UUID, &x[0], 2); +} + +void CanonEOS::focusPress(void) { + shutterPress(); + return; +} + +void CanonEOS::focusRelease(void) { + // do nothing + return; +} + +void CanonEOS::disconnect(void) { + m_Client->disconnect(); +} + +size_t CanonEOS::getSerialisedBytes(void) { + return sizeof(eos_t); +} + +bool CanonEOS::serialise(void *buffer, size_t bytes) { + if (bytes != sizeof(eos_t)) { + return false; + } + eos_t *x = (eos_t *)buffer; + strncpy(x->name, m_Name.c_str(), MAX_NAME); + x->address = (uint64_t)m_Address; + x->type = m_Address.getType(); + memcpy(&x->uuid, &m_Uuid, sizeof(uuid128_t)); + + return true; +} + +} // namespace Furble diff --git a/lib/furble/CanonEOS.h b/lib/furble/CanonEOS.h new file mode 100644 index 0000000..1a8e50e --- /dev/null +++ b/lib/furble/CanonEOS.h @@ -0,0 +1,68 @@ +#ifndef CANONEOS_H +#define CANONEOS_H + +#include "Device.h" + +namespace Furble { +/** + * Canon EOS Partial Abstract Base. + */ +class CanonEOS: public Device { + public: + CanonEOS(const void *data, size_t len); + CanonEOS(NimBLEAdvertisedDevice *pDevice); + ~CanonEOS(void); + + protected: + typedef struct _eos_t { + char name[MAX_NAME]; /** Human readable device name. */ + uint64_t address; /** Device MAC address. */ + uint8_t type; /** Address type. */ + uuid128_t uuid; /** Our UUID. */ + } eos_t; + + const char *CANON_EOS_SVC_IDEN_UUID = "00010000-0000-1000-0000-d8492fffa821"; + /** 0xf108 */ + const char *CANON_EOS_CHR_NAME_UUID = "00010006-0000-1000-0000-d8492fffa821"; + /** 0xf104 */ + const char *CANON_EOS_CHR_IDEN_UUID = "0001000a-0000-1000-0000-d8492fffa821"; + + const char *CANON_EOS_SVC_UNK0_UUID = "00020000-0000-1000-0000-d8492fffa821"; + /** 0xf204 */ + const char *CANON_EOS_CHR_UNK0_UUID = "00020002-0000-1000-0000-d8492fffa821"; + + const char *CANON_EOS_SVC_UNK1_UUID = "00030000-0000-1000-0000-d8492fffa821"; + /** 0xf307 */ + const char *CANON_EOS_CHR_UNK1_UUID = "00030010-0000-1000-0000-d8492fffa821"; + + const char *CANON_EOS_SVC_SHUTTER_UUID = "00030000-0000-1000-0000-d8492fffa821"; + /** 0xf311 */ + const char *CANON_EOS_CHR_SHUTTER_UUID = "00030030-0000-1000-0000-d8492fffa821"; + + bool write_value(NimBLEClient *pClient, + const char *serviceUUID, + const char *characteristicUUID, + uint8_t *data, + size_t length); + + bool write_prefix(NimBLEClient *pClient, + const char *serviceUUID, + const char *characteristicUUID, + uint8_t prefix, + uint8_t *data, + size_t length); + + bool connect(NimBLEClient *pClient, ezProgressBar &progress_bar, uint32_t pair_delay = 0); + void shutterPress(void); + void shutterRelease(void); + void focusPress(void); + void focusRelease(void); + void disconnect(void); + size_t getSerialisedBytes(void); + bool serialise(void *buffer, size_t bytes); + + uuid128_t m_Uuid; +}; + +} // namespace Furble +#endif diff --git a/lib/furble/CanonEOSM6.cpp b/lib/furble/CanonEOSM6.cpp index 915b705..2f45e4d 100644 --- a/lib/furble/CanonEOSM6.cpp +++ b/lib/furble/CanonEOSM6.cpp @@ -6,55 +6,13 @@ #include "Furble.h" -typedef struct _eosm6_t { - char name[MAX_NAME]; /** Human readable device name. */ - uint64_t address; /** Device MAC address. */ - uint8_t type; /** Address type. */ - uuid128_t uuid; /** Our UUID. */ -} eosm6_t; - -static const char *CANON_EOSM6_SVC_IDEN_UUID = "00010000-0000-1000-0000-d8492fffa821"; -/** 0xf108 */ -static const char *CANON_EOSM6_CHR_NAME_UUID = "00010006-0000-1000-0000-d8492fffa821"; -/** 0xf104 */ -static const char *CANON_EOSM6_CHR_IDEN_UUID = "0001000a-0000-1000-0000-d8492fffa821"; - -static const char *CANON_EOSM6_SVC_UNK0_UUID = "00020000-0000-1000-0000-d8492fffa821"; -/** 0xf204 */ -static const char *CANON_EOSM6_CHR_UNK0_UUID = "00020002-0000-1000-0000-d8492fffa821"; - -static const char *CANON_EOSM6_SVC_UNK1_UUID = "00030000-0000-1000-0000-d8492fffa821"; -/** 0xf307 */ -static const char *CANON_EOSM6_CHR_UNK1_UUID = "00030010-0000-1000-0000-d8492fffa821"; - -static const char *CANON_EOSM6_SVC_SHUTTER_UUID = "00030000-0000-1000-0000-d8492fffa821"; -/** 0xf311 */ -static const char *CANON_EOSM6_CHR_SHUTTER_UUID = "00030030-0000-1000-0000-d8492fffa821"; - namespace Furble { -CanonEOSM6::CanonEOSM6(const void *data, size_t len) { - if (len != sizeof(eosm6_t)) - throw; - - const eosm6_t *eosm6 = (eosm6_t *)data; - m_Name = std::string(eosm6->name); - m_Address = NimBLEAddress(eosm6->address, eosm6->type); - memcpy(&m_Uuid, &eosm6->uuid, sizeof(uuid128_t)); -} +CanonEOSM6::CanonEOSM6(const void *data, size_t len) : CanonEOS(data, len) {} -CanonEOSM6::CanonEOSM6(NimBLEAdvertisedDevice *pDevice) { - m_Name = pDevice->getName(); - m_Address = pDevice->getAddress(); - Serial.println("Name = " + String(m_Name.c_str())); - Serial.println("Address = " + String(m_Address.toString().c_str())); - Device::getUUID128(&m_Uuid); -} +CanonEOSM6::CanonEOSM6(NimBLEAdvertisedDevice *pDevice) : CanonEOS(pDevice) {} -CanonEOSM6::~CanonEOSM6(void) { - NimBLEDevice::deleteClient(m_Client); - m_Client = nullptr; -} +CanonEOSM6::~CanonEOSM6(void) {} const size_t CANON_EOS_M6_ADV_DATA_LEN = 21; const uint8_t CANON_EOS_M6_ID_0 = 0xa9; @@ -80,32 +38,6 @@ bool CanonEOSM6::matches(NimBLEAdvertisedDevice *pDevice) { return false; } -static bool write_value(NimBLEClient *pClient, - const char *serviceUUID, - const char *characteristicUUID, - uint8_t *data, - size_t length) { - NimBLERemoteService *pSvc = pClient->getService(serviceUUID); - if (pSvc) { - NimBLERemoteCharacteristic *pChr = pSvc->getCharacteristic(characteristicUUID); - return ((pChr != nullptr) && pChr->canWrite() && pChr->writeValue(data, length, true)); - } - - return false; -} - -static bool write_prefix(NimBLEClient *pClient, - const char *serviceUUID, - const char *characteristicUUID, - uint8_t prefix, - uint8_t *data, - size_t length) { - uint8_t buffer[length + 1] = {0}; - buffer[0] = prefix; - memcpy(&buffer[1], data, length); - return write_value(pClient, serviceUUID, characteristicUUID, buffer, length + 1); -} - /** * Connect to a Canon EOS M6. * @@ -113,120 +45,11 @@ static bool write_prefix(NimBLEClient *pClient, * handled by the underlying NimBLE and ESP32 libraries. */ bool CanonEOSM6::connect(NimBLEClient *pClient, ezProgressBar &progress_bar) { - m_Client = pClient; - - Serial.println("Connecting"); - if (!m_Client->connect(m_Address)) { - Serial.println("Connection failed!!!"); - return false; - } - - Serial.println("Connected"); - progress_bar.value(10.0f); - - Serial.println("Securing"); - if (!m_Client->secureConnection()) { - return false; - } - Serial.println("Secured!"); - progress_bar.value(20.0f); - - Serial.println("Identifying 1!"); - if (!write_prefix(m_Client, CANON_EOSM6_SVC_IDEN_UUID, CANON_EOSM6_CHR_NAME_UUID, 0x01, - (uint8_t *)FURBLE_STR, strlen(FURBLE_STR))) - return false; - - progress_bar.value(30.0f); - - Serial.println("Identifying 2!"); - if (!write_prefix(m_Client, CANON_EOSM6_SVC_IDEN_UUID, CANON_EOSM6_CHR_IDEN_UUID, 0x03, - m_Uuid.uint8, UUID128_LEN)) - return false; - - progress_bar.value(40.0f); - - Serial.println("Identifying 3!"); - if (!write_prefix(m_Client, CANON_EOSM6_SVC_IDEN_UUID, CANON_EOSM6_CHR_IDEN_UUID, 0x04, - (uint8_t *)FURBLE_STR, strlen(FURBLE_STR))) - return false; - - progress_bar.value(50.0f); - - Serial.println("Identifying 4!"); - - uint8_t x = 0x02; - if (!write_prefix(m_Client, CANON_EOSM6_SVC_IDEN_UUID, CANON_EOSM6_CHR_IDEN_UUID, 0x05, &x, 1)) - return false; - - progress_bar.value(60.0f); - - Serial.println("Identifying 5!"); - - /* write to 0xf204 */ - x = 0x0a; - if (!write_value(m_Client, CANON_EOSM6_SVC_UNK0_UUID, CANON_EOSM6_CHR_UNK0_UUID, &x, 1)) - return false; - - progress_bar.value(70.0f); - - /* write to 0xf104 */ - x = 0x01; - if (!write_value(m_Client, CANON_EOSM6_SVC_IDEN_UUID, CANON_EOSM6_CHR_IDEN_UUID, &x, 1)) - return false; - - progress_bar.value(80.0f); - - Serial.println("Identifying 6!"); - - /* write to 0xf307 */ - x = 0x03; - if (!write_value(m_Client, CANON_EOSM6_SVC_UNK1_UUID, CANON_EOSM6_CHR_UNK1_UUID, &x, 1)) - return false; - - Serial.println("Paired!"); - progress_bar.value(100.0f); - - return true; -} - -void CanonEOSM6::shutterPress(void) { - uint8_t x[2] = {0x00, 0x01}; - write_value(m_Client, CANON_EOSM6_SVC_SHUTTER_UUID, CANON_EOSM6_CHR_SHUTTER_UUID, &x[0], 2); -} - -void CanonEOSM6::shutterRelease(void) { - uint8_t x[2] = {0x00, 0x02}; - write_value(m_Client, CANON_EOSM6_SVC_SHUTTER_UUID, CANON_EOSM6_CHR_SHUTTER_UUID, &x[0], 2); -} - -void CanonEOSM6::shutterFocus(void) { - // Unimplemented - return; -} - -void CanonEOSM6::disconnect(void) { - m_Client->disconnect(); + return CanonEOS::connect(pClient, progress_bar); } device_type_t CanonEOSM6::getDeviceType(void) { return FURBLE_CANON_EOS_M6; } -size_t CanonEOSM6::getSerialisedBytes(void) { - return sizeof(eosm6_t); -} - -bool CanonEOSM6::serialise(void *buffer, size_t bytes) { - if (bytes != sizeof(eosm6_t)) { - return false; - } - eosm6_t *x = (eosm6_t *)buffer; - strncpy(x->name, m_Name.c_str(), MAX_NAME); - x->address = (uint64_t)m_Address; - x->type = m_Address.getType(); - memcpy(&x->uuid, &m_Uuid, sizeof(uuid128_t)); - - return true; -} - } // namespace Furble diff --git a/lib/furble/CanonEOSM6.h b/lib/furble/CanonEOSM6.h new file mode 100644 index 0000000..22aeea8 --- /dev/null +++ b/lib/furble/CanonEOSM6.h @@ -0,0 +1,28 @@ +#ifndef CANONEOSM6_H +#define CANONEOSM6_H + +#include "CanonEOS.h" + +namespace Furble { +/** + * Canon EOS M6. + */ +class CanonEOSM6: public CanonEOS { + public: + CanonEOSM6(const void *data, size_t len); + CanonEOSM6(NimBLEAdvertisedDevice *pDevice); + ~CanonEOSM6(void); + + /** + * Determine if the advertised BLE device is a Canon EOS M6. + */ + static bool matches(NimBLEAdvertisedDevice *pDevice); + + bool connect(NimBLEClient *pClient, ezProgressBar &progress_bar); + + private: + device_type_t getDeviceType(void); +}; + +} // namespace Furble +#endif diff --git a/lib/furble/CanonEOSRP.cpp b/lib/furble/CanonEOSRP.cpp index fee2ecc..8ef3305 100644 --- a/lib/furble/CanonEOSRP.cpp +++ b/lib/furble/CanonEOSRP.cpp @@ -6,55 +6,13 @@ #include "Furble.h" -typedef struct _eosrp_t { - char name[MAX_NAME]; /** Human readable device name. */ - uint64_t address; /** Device MAC address. */ - uint8_t type; /** Address type. */ - uuid128_t uuid; /** Our UUID. */ -} eosrp_t; - -static const char *CANON_EOSRP_SVC_IDEN_UUID = "00010000-0000-1000-0000-d8492fffa821"; -/** 0xf108 */ -static const char *CANON_EOSRP_CHR_NAME_UUID = "00010006-0000-1000-0000-d8492fffa821"; -/** 0xf104 */ -static const char *CANON_EOSRP_CHR_IDEN_UUID = "0001000a-0000-1000-0000-d8492fffa821"; - -static const char *CANON_EOSRP_SVC_UNK0_UUID = "00020000-0000-1000-0000-d8492fffa821"; -/** 0xf204 */ -static const char *CANON_EOSRP_CHR_UNK0_UUID = "00020002-0000-1000-0000-d8492fffa821"; - -static const char *CANON_EOSRP_SVC_UNK1_UUID = "00030000-0000-1000-0000-d8492fffa821"; -/** 0xf307 */ -static const char *CANON_EOSRP_CHR_UNK1_UUID = "00030010-0000-1000-0000-d8492fffa821"; - -static const char *CANON_EOSRP_SVC_SHUTTER_UUID = "00030000-0000-1000-0000-d8492fffa821"; -/** 0xf311 */ -static const char *CANON_EOSRP_CHR_SHUTTER_UUID = "00030030-0000-1000-0000-d8492fffa821"; - namespace Furble { -CanonEOSRP::CanonEOSRP(const void *data, size_t len) { - if (len != sizeof(eosrp_t)) - throw; - - const eosrp_t *eosrp = (eosrp_t *)data; - m_Name = std::string(eosrp->name); - m_Address = NimBLEAddress(eosrp->address, eosrp->type); - memcpy(&m_Uuid, &eosrp->uuid, sizeof(uuid128_t)); -} +CanonEOSRP::CanonEOSRP(const void *data, size_t len) : CanonEOS(data, len) {} -CanonEOSRP::CanonEOSRP(NimBLEAdvertisedDevice *pDevice) { - m_Name = pDevice->getName(); - m_Address = pDevice->getAddress(); - Serial.println("Name = " + String(m_Name.c_str())); - Serial.println("Address = " + String(m_Address.toString().c_str())); - Device::getUUID128(&m_Uuid); -} +CanonEOSRP::CanonEOSRP(NimBLEAdvertisedDevice *pDevice) : CanonEOS(pDevice) {} -CanonEOSRP::~CanonEOSRP(void) { - NimBLEDevice::deleteClient(m_Client); - m_Client = nullptr; -} +CanonEOSRP::~CanonEOSRP(void) {} const size_t CANON_EOS_RP_ADV_DATA_LEN = 8; const uint8_t CANON_EOS_RP_ID_0 = 0xa9; @@ -80,32 +38,6 @@ bool CanonEOSRP::matches(NimBLEAdvertisedDevice *pDevice) { return false; } -static bool write_value(NimBLEClient *pClient, - const char *serviceUUID, - const char *characteristicUUID, - uint8_t *data, - size_t length) { - NimBLERemoteService *pSvc = pClient->getService(serviceUUID); - if (pSvc) { - NimBLERemoteCharacteristic *pChr = pSvc->getCharacteristic(characteristicUUID); - return ((pChr != nullptr) && pChr->canWrite() && pChr->writeValue(data, length, true)); - } - - return false; -} - -static bool write_prefix(NimBLEClient *pClient, - const char *serviceUUID, - const char *characteristicUUID, - uint8_t prefix, - uint8_t *data, - size_t length) { - uint8_t buffer[length + 1] = {0}; - buffer[0] = prefix; - memcpy(&buffer[1], data, length); - return write_value(pClient, serviceUUID, characteristicUUID, buffer, length + 1); -} - /** * Connect to a Canon EOS RP. * @@ -113,123 +45,11 @@ static bool write_prefix(NimBLEClient *pClient, * handled by the underlying NimBLE and ESP32 libraries. */ bool CanonEOSRP::connect(NimBLEClient *pClient, ezProgressBar &progress_bar) { - m_Client = pClient; - - Serial.println("Connecting"); - if (!m_Client->connect(m_Address)) { - Serial.println("Connection failed!!!"); - return false; - } - - Serial.println("Connected"); - progress_bar.value(10.0f); - - Serial.println("Securing"); - if (!m_Client->secureConnection()) { - return false; - } - Serial.println("Secured!"); - progress_bar.value(20.0f); - - Serial.println("Identifying 1!"); - if (!write_prefix(m_Client, CANON_EOSRP_SVC_IDEN_UUID, CANON_EOSRP_CHR_NAME_UUID, 0x01, - (uint8_t *)FURBLE_STR, strlen(FURBLE_STR))) - return false; - - progress_bar.value(30.0f); - - Serial.println("Identifying 2!"); - if (!write_prefix(m_Client, CANON_EOSRP_SVC_IDEN_UUID, CANON_EOSRP_CHR_IDEN_UUID, 0x03, - m_Uuid.uint8, UUID128_LEN)) - return false; - - progress_bar.value(40.0f); - - Serial.println("Identifying 3!"); - if (!write_prefix(m_Client, CANON_EOSRP_SVC_IDEN_UUID, CANON_EOSRP_CHR_IDEN_UUID, 0x04, - (uint8_t *)FURBLE_STR, strlen(FURBLE_STR))) - return false; - - progress_bar.value(50.0f); - - Serial.println("Identifying 4!"); - - uint8_t x = 0x02; - if (!write_prefix(m_Client, CANON_EOSRP_SVC_IDEN_UUID, CANON_EOSRP_CHR_IDEN_UUID, 0x05, &x, 1)) - return false; - - progress_bar.value(60.0f); - - Serial.println("Identifying 5!"); - - /* write to 0xf204 */ - x = 0x0a; - if (!write_value(m_Client, CANON_EOSRP_SVC_UNK0_UUID, CANON_EOSRP_CHR_UNK0_UUID, &x, 1)) - return false; - - progress_bar.value(70.0f); - delay(5000); // give camera user time to confirm pairing - - Serial.println("Identifying 5b!"); - - /* write to 0xf104 */ - x = 0x01; - if (!write_value(m_Client, CANON_EOSRP_SVC_IDEN_UUID, CANON_EOSRP_CHR_IDEN_UUID, &x, 1)) - return false; - - progress_bar.value(80.0f); - - Serial.println("Identifying 6!"); - - /* write to 0xf307 */ - x = 0x03; - if (!write_value(m_Client, CANON_EOSRP_SVC_UNK1_UUID, CANON_EOSRP_CHR_UNK1_UUID, &x, 1)) - return false; - - Serial.println("Paired!"); - progress_bar.value(100.0f); - - return true; -} - -void CanonEOSRP::shutterPress(void) { - uint8_t x[2] = {0x00, 0x01}; - write_value(m_Client, CANON_EOSRP_SVC_SHUTTER_UUID, CANON_EOSRP_CHR_SHUTTER_UUID, &x[0], 2); -} - -void CanonEOSRP::shutterRelease(void) { - uint8_t x[2] = {0x00, 0x02}; - write_value(m_Client, CANON_EOSRP_SVC_SHUTTER_UUID, CANON_EOSRP_CHR_SHUTTER_UUID, &x[0], 2); -} - -void CanonEOSRP::shutterFocus(void) { - // Unimplemented - return; -} - -void CanonEOSRP::disconnect(void) { - m_Client->disconnect(); + return CanonEOS::connect(pClient, progress_bar, 5000); } device_type_t CanonEOSRP::getDeviceType(void) { return FURBLE_CANON_EOS_RP; } -size_t CanonEOSRP::getSerialisedBytes(void) { - return sizeof(eosrp_t); -} - -bool CanonEOSRP::serialise(void *buffer, size_t bytes) { - if (bytes != sizeof(eosrp_t)) { - return false; - } - eosrp_t *x = (eosrp_t *)buffer; - strncpy(x->name, m_Name.c_str(), MAX_NAME); - x->address = (uint64_t)m_Address; - x->type = m_Address.getType(); - memcpy(&x->uuid, &m_Uuid, sizeof(uuid128_t)); - - return true; -} - } // namespace Furble diff --git a/lib/furble/CanonEOSRP.h b/lib/furble/CanonEOSRP.h new file mode 100644 index 0000000..0b08075 --- /dev/null +++ b/lib/furble/CanonEOSRP.h @@ -0,0 +1,28 @@ +#ifndef CANONEOSRP_H +#define CANONEOSRP_H + +#include "CanonEOS.h" + +namespace Furble { +/** + * Canon EOS RP. + */ +class CanonEOSRP: public CanonEOS { + public: + CanonEOSRP(const void *data, size_t len); + CanonEOSRP(NimBLEAdvertisedDevice *pDevice); + ~CanonEOSRP(void); + + /** + * Determine if the advertised BLE device is a Canon EOS RP. + */ + static bool matches(NimBLEAdvertisedDevice *pDevice); + + bool connect(NimBLEClient *pClient, ezProgressBar &progress_bar); + + private: + device_type_t getDeviceType(void); +}; + +} // namespace Furble +#endif diff --git a/lib/furble/Device.cpp b/lib/furble/Device.cpp index 13751b1..c0bceab 100644 --- a/lib/furble/Device.cpp +++ b/lib/furble/Device.cpp @@ -133,8 +133,8 @@ void Device::loadDevices(std::vector &device_list) { m_Prefs.getBytes(index[i].name, dbuffer, dbytes); switch (index[i].type) { - case FURBLE_FUJIFILM_XT30: - device_list.push_back(new FujifilmXT30(dbuffer, dbytes)); + case FURBLE_FUJIFILM: + device_list.push_back(new Fujifilm(dbuffer, dbytes)); break; case FURBLE_CANON_EOS_M6: device_list.push_back(new CanonEOSM6(dbuffer, dbytes)); @@ -152,8 +152,8 @@ void Device::fillSaveName(char *name) { } void Device::match(NimBLEAdvertisedDevice *pDevice, std::vector &list) { - if (FujifilmXT30::matches(pDevice)) { - list.push_back(new Furble::FujifilmXT30(pDevice)); + if (Fujifilm::matches(pDevice)) { + list.push_back(new Furble::Fujifilm(pDevice)); } else if (CanonEOSM6::matches(pDevice)) { list.push_back(new Furble::CanonEOSM6(pDevice)); } else if (CanonEOSRP::matches(pDevice)) { diff --git a/lib/furble/Device.h b/lib/furble/Device.h new file mode 100644 index 0000000..36c1323 --- /dev/null +++ b/lib/furble/Device.h @@ -0,0 +1,90 @@ +#ifndef DEVICE_H +#define DEVICE_H + +#define MAX_NAME (64) + +#define UUID128_LEN (16) +#define UUID128_AS_32_LEN (UUID128_LEN / sizeof(uint32_t)) + +namespace Furble { +/** + * Represents a single target camera. + */ +class Device { + public: + /** + * UUID type + */ + typedef struct _uuid128_t { + union { + uint32_t uint32[UUID128_AS_32_LEN]; + uint8_t uint8[UUID128_LEN]; + }; + } uuid128_t; + + /** + * Connect to the target camera such that it is ready for shutter control. + * + * This should include connection and pairing as needed for the target + * device. + * + * @return true if the client is now ready for shutter control + */ + virtual bool connect(NimBLEClient *pClient, ezProgressBar &progress_bar) = 0; + + /** + * Disconnect from the target. + */ + virtual void disconnect(void) = 0; + + /** + * Send a shutter button press command. + */ + virtual void shutterPress(void) = 0; + + /** + * Send a shutter button release command. + */ + virtual void shutterRelease(void) = 0; + + /** + * Send a focus button press command. + */ + virtual void focusPress(void) = 0; + + /** + * Send a focus button release command. + */ + virtual void focusRelease(void) = 0; + + const char *getName(void); + void save(void); + void remove(void); + + static void loadDevices(std::vector &device_list); + + /** + * Add matching devices to the list. + */ + static void match(NimBLEAdvertisedDevice *pDevice, std::vector &list); + + /** + * Generate a device consistent 128-bit UUID. + */ + static void getUUID128(uuid128_t *uuid); + + protected: + NimBLEAddress m_Address = NimBLEAddress(""); + NimBLEClient *m_Client; + std::string m_Name; + + private: + virtual device_type_t getDeviceType(void) = 0; + virtual size_t getSerialisedBytes(void) = 0; + virtual bool serialise(void *buffer, size_t bytes) = 0; + + void fillSaveName(char *name); +}; +} // namespace Furble + +#endif diff --git a/lib/furble/Fujifilm.cpp b/lib/furble/Fujifilm.cpp new file mode 100644 index 0000000..eae7d1e --- /dev/null +++ b/lib/furble/Fujifilm.cpp @@ -0,0 +1,224 @@ +#include +#include +#include +#include +#include + +#include "Furble.h" + +typedef struct _fujifilm_t { + char name[MAX_NAME]; /** Human readable device name. */ + uint64_t address; /** Device MAC address. */ + uint8_t type; /** Address type. */ + uint8_t token[FUJIFILM_TOKEN_LEN]; /** Pairing token. */ +} fujifilm_t; + +/** 0x4001 */ +static const char *FUJIFILM_SVC_PAIR_UUID = "91f1de68-dff6-466e-8b65-ff13b0f16fb8"; +/** 0x4042 */ +static const char *FUJIFILM_CHR_PAIR_UUID = "aba356eb-9633-4e60-b73f-f52516dbd671"; +static const char *FUJIFILM_CHR_IDEN_UUID = "85b9163e-62d1-49ff-a6f5-054b4630d4a1"; + +// Currently unused +// static const char *FUJIFILM_SVC_READ_UUID = +// "4e941240-d01d-46b9-a5ea-67636806830b"; static const char +// *FUJIFILM_CHR_READ_UUID = "bf6dc9cf-3606-4ec9-a4c8-d77576e93ea4"; + +static const char *FUJIFILM_SVC_CONF_UUID = "4c0020fe-f3b6-40de-acc9-77d129067b14"; +static const char *FUJIFILM_CHR_IND1_UUID = "a68e3f66-0fcc-4395-8d4c-aa980b5877fa"; +static const char *FUJIFILM_CHR_IND2_UUID = "bd17ba04-b76b-4892-a545-b73ba1f74dae"; +static const char *FUJIFILM_CHR_NOT1_UUID = "f9150137-5d40-4801-a8dc-f7fc5b01da50"; +static const char *FUJIFILM_CHR_NOT2_UUID = "ad06c7b7-f41a-46f4-a29a-712055319122"; +static const char *FUJIFILM_CHR_NOT3_UUID = "049ec406-ef75-4205-a390-08fe209c51f0"; + +static const char *FUJIFILM_SVC_SHUTTER_UUID = "6514eb81-4e8f-458d-aa2a-e691336cdfac"; +static const char *FUJIFILM_CHR_SHUTTER_UUID = "7fcf49c6-4ff0-4777-a03d-1a79166af7a8"; + +static const uint8_t FUJIFILM_SHUTTER_CMD[2] = {0x01, 0x00}; +static const uint8_t FUJIFILM_SHUTTER_PRESS[2] = {0x02, 0x00}; +static const uint8_t FUJIFILM_SHUTTER_RELEASE[2] = {0x00, 0x00}; +static const uint8_t FUJIFILM_SHUTTER_FOCUS[2] = {0x03, 0x00}; + +namespace Furble { + +static void print_token(const uint8_t *token) { + Serial.println("Token = " + String(token[0], HEX) + String(token[1], HEX) + String(token[2], HEX) + + String(token[3], HEX)); +} + +Fujifilm::Fujifilm(const void *data, size_t len) { + if (len != sizeof(fujifilm_t)) + throw; + + const fujifilm_t *fujifilm = (fujifilm_t *)data; + m_Name = std::string(fujifilm->name); + m_Address = NimBLEAddress(fujifilm->address, fujifilm->type); + memcpy(m_Token, fujifilm->token, FUJIFILM_TOKEN_LEN); +} + +Fujifilm::Fujifilm(NimBLEAdvertisedDevice *pDevice) { + const char *data = pDevice->getManufacturerData().data(); + m_Name = pDevice->getName(); + m_Address = pDevice->getAddress(); + m_Token[0] = data[3]; + m_Token[1] = data[4]; + m_Token[2] = data[5]; + m_Token[3] = data[6]; + Serial.println("Name = " + String(m_Name.c_str())); + Serial.println("Address = " + String(m_Address.toString().c_str())); + print_token(m_Token); +} + +Fujifilm::~Fujifilm(void) { + NimBLEDevice::deleteClient(m_Client); + m_Client = nullptr; +} + +const size_t FUJIFILM_ADV_TOKEN_LEN = 7; +const uint8_t FUJIFILM_ID_0 = 0xd8; +const uint8_t FUJIFILM_ID_1 = 0x04; +const uint8_t FUJIFILM_TYPE_TOKEN = 0x02; + +/** + * Determine if the advertised BLE device is a Fujifilm X-T30. + */ +bool Fujifilm::matches(NimBLEAdvertisedDevice *pDevice) { + if (pDevice->haveManufacturerData() + && pDevice->getManufacturerData().length() == FUJIFILM_ADV_TOKEN_LEN) { + const char *data = pDevice->getManufacturerData().data(); + if (data[0] == FUJIFILM_ID_0 && data[1] == FUJIFILM_ID_1 && data[2] == FUJIFILM_TYPE_TOKEN) { + return true; + } + } + return false; +} + +/** + * Connect to a Fujifilm X-T30. + * + * During initial pairing the X-T30 advertisement includes a pairing token, this + * token is what we use to identify ourselves upfront and during subsequent + * re-pairing. + */ +bool Fujifilm::connect(NimBLEClient *pClient, ezProgressBar &progress_bar) { + m_Client = pClient; + + progress_bar.value(10.0f); + + NimBLERemoteService *pSvc = nullptr; + NimBLERemoteCharacteristic *pChr = nullptr; + + Serial.println("Connecting"); + if (!m_Client->connect(m_Address)) + return false; + + Serial.println("Connected"); + progress_bar.value(20.0f); + pSvc = m_Client->getService(FUJIFILM_SVC_PAIR_UUID); + if (pSvc == nullptr) + return false; + + Serial.println("Pairing"); + pChr = pSvc->getCharacteristic(FUJIFILM_CHR_PAIR_UUID); + if (pChr == nullptr) + return false; + + if (!pChr->canWrite()) + return false; + print_token(m_Token); + if (!pChr->writeValue(m_Token, FUJIFILM_TOKEN_LEN)) + return false; + Serial.println("Paired!"); + progress_bar.value(30.0f); + + Serial.println("Identifying"); + pChr = pSvc->getCharacteristic(FUJIFILM_CHR_IDEN_UUID); + if (!pChr->canWrite()) + return false; + if (!pChr->writeValue(FURBLE_STR)) + return false; + Serial.println("Identified!"); + progress_bar.value(40.0f); + + Serial.println("Configuring"); + pSvc = m_Client->getService(FUJIFILM_SVC_CONF_UUID); + // indications + pSvc->getCharacteristic(FUJIFILM_CHR_IND1_UUID)->subscribe(false); + progress_bar.value(50.0f); + pSvc->getCharacteristic(FUJIFILM_CHR_IND2_UUID)->subscribe(false); + progress_bar.value(60.0f); + // notifications + pSvc->getCharacteristic(FUJIFILM_CHR_NOT1_UUID)->subscribe(true); + progress_bar.value(70.0f); + pSvc->getCharacteristic(FUJIFILM_CHR_NOT2_UUID)->subscribe(true); + progress_bar.value(80.0f); + pSvc->getCharacteristic(FUJIFILM_CHR_NOT3_UUID)->subscribe(true); + progress_bar.value(90.0f); + + Serial.println("Configured"); + + progress_bar.value(100.0f); + + return true; +} + +void Fujifilm::shutterPress(void) { + NimBLERemoteService *pSvc = m_Client->getService(FUJIFILM_SVC_SHUTTER_UUID); + NimBLERemoteCharacteristic *pChr = pSvc->getCharacteristic(FUJIFILM_CHR_SHUTTER_UUID); + pChr->writeValue(&FUJIFILM_SHUTTER_CMD[0], sizeof(FUJIFILM_SHUTTER_CMD), true); + pChr->writeValue(&FUJIFILM_SHUTTER_PRESS[0], sizeof(FUJIFILM_SHUTTER_PRESS), true); +} + +void Fujifilm::shutterRelease(void) { + NimBLERemoteService *pSvc = m_Client->getService(FUJIFILM_SVC_SHUTTER_UUID); + NimBLERemoteCharacteristic *pChr = pSvc->getCharacteristic(FUJIFILM_CHR_SHUTTER_UUID); + pChr->writeValue(&FUJIFILM_SHUTTER_CMD[0], sizeof(FUJIFILM_SHUTTER_CMD), true); + pChr->writeValue(&FUJIFILM_SHUTTER_RELEASE[0], sizeof(FUJIFILM_SHUTTER_RELEASE), true); +} + +void Fujifilm::focusPress(void) { + NimBLERemoteService *pSvc = m_Client->getService(FUJIFILM_SVC_SHUTTER_UUID); + NimBLERemoteCharacteristic *pChr = pSvc->getCharacteristic(FUJIFILM_CHR_SHUTTER_UUID); + pChr->writeValue(&FUJIFILM_SHUTTER_CMD[0], sizeof(FUJIFILM_SHUTTER_CMD), true); + pChr->writeValue(&FUJIFILM_SHUTTER_FOCUS[0], sizeof(FUJIFILM_SHUTTER_FOCUS), true); +} + +void Fujifilm::focusRelease(void) { + shutterRelease(); +} + +void Fujifilm::print(void) { + Serial.print("Name: "); + Serial.println(m_Name.c_str()); + Serial.print("Address: "); + Serial.println(m_Address.toString().c_str()); + Serial.print("Type: "); + Serial.println(m_Address.getType()); +} + +void Fujifilm::disconnect(void) { + m_Client->disconnect(); +} + +device_type_t Fujifilm::getDeviceType(void) { + return FURBLE_FUJIFILM; +} + +size_t Fujifilm::getSerialisedBytes(void) { + return sizeof(fujifilm_t); +} + +bool Fujifilm::serialise(void *buffer, size_t bytes) { + if (bytes != sizeof(fujifilm_t)) { + return false; + } + fujifilm_t *x = (fujifilm_t *)buffer; + strncpy(x->name, m_Name.c_str(), 64); + x->address = (uint64_t)m_Address; + x->type = m_Address.getType(); + memcpy(x->token, m_Token, FUJIFILM_TOKEN_LEN); + + return true; +} + +} // namespace Furble diff --git a/lib/furble/Fujifilm.h b/lib/furble/Fujifilm.h new file mode 100644 index 0000000..6c33512 --- /dev/null +++ b/lib/furble/Fujifilm.h @@ -0,0 +1,40 @@ +#ifndef FUJIFILM_H +#define FUJIFILM_H + +#include "Device.h" + +#define FUJIFILM_TOKEN_LEN (4) + +namespace Furble { +/** + * Fujifilm X. + */ +class Fujifilm: public Device { + public: + Fujifilm(const void *data, size_t len); + Fujifilm(NimBLEAdvertisedDevice *pDevice); + ~Fujifilm(void); + + /** + * Determine if the advertised BLE device is a Fujifilm X-T30. + */ + static bool matches(NimBLEAdvertisedDevice *pDevice); + + bool connect(NimBLEClient *pClient, ezProgressBar &progress_bar); + void shutterPress(void); + void shutterRelease(void); + void focusPress(void); + void focusRelease(void); + void disconnect(void); + void print(void); + + private: + device_type_t getDeviceType(void); + size_t getSerialisedBytes(void); + bool serialise(void *buffer, size_t bytes); + + uint8_t m_Token[FUJIFILM_TOKEN_LEN] = {0}; +}; + +} // namespace Furble +#endif diff --git a/lib/furble/FujifilmXT30.cpp b/lib/furble/FujifilmXT30.cpp deleted file mode 100644 index fc7312b..0000000 --- a/lib/furble/FujifilmXT30.cpp +++ /dev/null @@ -1,220 +0,0 @@ -#include -#include -#include -#include -#include - -#include "Furble.h" - -typedef struct _xt30_t { - char name[MAX_NAME]; /** Human readable device name. */ - uint64_t address; /** Device MAC address. */ - uint8_t type; /** Address type. */ - uint8_t token[XT30_TOKEN_LEN]; /** Pairing token. */ -} xt30_t; - -/** 0x4001 */ -static const char *FUJI_XT30_SVC_PAIR_UUID = "91f1de68-dff6-466e-8b65-ff13b0f16fb8"; -/** 0x4042 */ -static const char *FUJI_XT30_CHR_PAIR_UUID = "aba356eb-9633-4e60-b73f-f52516dbd671"; -static const char *FUJI_XT30_CHR_IDEN_UUID = "85b9163e-62d1-49ff-a6f5-054b4630d4a1"; - -// Currently unused -// static const char *FUJI_XT30_SVC_READ_UUID = -// "4e941240-d01d-46b9-a5ea-67636806830b"; static const char -// *FUJI_XT30_CHR_READ_UUID = "bf6dc9cf-3606-4ec9-a4c8-d77576e93ea4"; - -static const char *FUJI_XT30_SVC_CONF_UUID = "4c0020fe-f3b6-40de-acc9-77d129067b14"; -static const char *FUJI_XT30_CHR_IND1_UUID = "a68e3f66-0fcc-4395-8d4c-aa980b5877fa"; -static const char *FUJI_XT30_CHR_IND2_UUID = "bd17ba04-b76b-4892-a545-b73ba1f74dae"; -static const char *FUJI_XT30_CHR_NOT1_UUID = "f9150137-5d40-4801-a8dc-f7fc5b01da50"; -static const char *FUJI_XT30_CHR_NOT2_UUID = "ad06c7b7-f41a-46f4-a29a-712055319122"; -static const char *FUJI_XT30_CHR_NOT3_UUID = "049ec406-ef75-4205-a390-08fe209c51f0"; - -static const char *FUJI_XT30_SVC_SHUTTER_UUID = "6514eb81-4e8f-458d-aa2a-e691336cdfac"; -static const char *FUJI_XT30_CHR_SHUTTER_UUID = "7fcf49c6-4ff0-4777-a03d-1a79166af7a8"; - -static const uint8_t FUJI_XT30_SHUTTER_CMD[2] = {0x01, 0x00}; -static const uint8_t FUJI_XT30_SHUTTER_PRESS[2] = {0x02, 0x00}; -static const uint8_t FUJI_XT30_SHUTTER_RELEASE[2] = {0x00, 0x00}; -static const uint8_t FUJI_XT30_SHUTTER_FOCUS[2] = {0x03, 0x00}; - -namespace Furble { - -static void print_token(const uint8_t *token) { - Serial.println("Token = " + String(token[0], HEX) + String(token[1], HEX) + String(token[2], HEX) - + String(token[3], HEX)); -} - -FujifilmXT30::FujifilmXT30(const void *data, size_t len) { - if (len != sizeof(xt30_t)) - throw; - - const xt30_t *xt30 = (xt30_t *)data; - m_Name = std::string(xt30->name); - m_Address = NimBLEAddress(xt30->address, xt30->type); - memcpy(m_Token, xt30->token, XT30_TOKEN_LEN); -} - -FujifilmXT30::FujifilmXT30(NimBLEAdvertisedDevice *pDevice) { - const char *data = pDevice->getManufacturerData().data(); - m_Name = pDevice->getName(); - m_Address = pDevice->getAddress(); - m_Token[0] = data[3]; - m_Token[1] = data[4]; - m_Token[2] = data[5]; - m_Token[3] = data[6]; - Serial.println("Name = " + String(m_Name.c_str())); - Serial.println("Address = " + String(m_Address.toString().c_str())); - print_token(m_Token); -} - -FujifilmXT30::~FujifilmXT30(void) { - NimBLEDevice::deleteClient(m_Client); - m_Client = nullptr; -} - -const size_t FUJI_XT30_ADV_TOKEN_LEN = 7; -const uint8_t FUJI_XT30_ID_0 = 0xd8; -const uint8_t FUJI_XT30_ID_1 = 0x04; -const uint8_t FUJI_XT30_TYPE_TOKEN = 0x02; - -/** - * Determine if the advertised BLE device is a Fujifilm X-T30. - */ -bool FujifilmXT30::matches(NimBLEAdvertisedDevice *pDevice) { - if (pDevice->haveManufacturerData() - && pDevice->getManufacturerData().length() == FUJI_XT30_ADV_TOKEN_LEN) { - const char *data = pDevice->getManufacturerData().data(); - if (data[0] == FUJI_XT30_ID_0 && data[1] == FUJI_XT30_ID_1 && data[2] == FUJI_XT30_TYPE_TOKEN) { - return true; - } - } - return false; -} - -/** - * Connect to a Fujifilm X-T30. - * - * During initial pairing the X-T30 advertisement includes a pairing token, this - * token is what we use to identify ourselves upfront and during subsequent - * re-pairing. - */ -bool FujifilmXT30::connect(NimBLEClient *pClient, ezProgressBar &progress_bar) { - m_Client = pClient; - - progress_bar.value(10.0f); - - NimBLERemoteService *pSvc = nullptr; - NimBLERemoteCharacteristic *pChr = nullptr; - - Serial.println("Connecting"); - if (!m_Client->connect(m_Address)) - return false; - - Serial.println("Connected"); - progress_bar.value(20.0f); - pSvc = m_Client->getService(FUJI_XT30_SVC_PAIR_UUID); - if (pSvc == nullptr) - return false; - - Serial.println("Pairing"); - pChr = pSvc->getCharacteristic(FUJI_XT30_CHR_PAIR_UUID); - if (pChr == nullptr) - return false; - - if (!pChr->canWrite()) - return false; - print_token(m_Token); - if (!pChr->writeValue(m_Token, XT30_TOKEN_LEN)) - return false; - Serial.println("Paired!"); - progress_bar.value(30.0f); - - Serial.println("Identifying"); - pChr = pSvc->getCharacteristic(FUJI_XT30_CHR_IDEN_UUID); - if (!pChr->canWrite()) - return false; - if (!pChr->writeValue(FURBLE_STR)) - return false; - Serial.println("Identified!"); - progress_bar.value(40.0f); - - Serial.println("Configuring"); - pSvc = m_Client->getService(FUJI_XT30_SVC_CONF_UUID); - // indications - pSvc->getCharacteristic(FUJI_XT30_CHR_IND1_UUID)->subscribe(false); - progress_bar.value(50.0f); - pSvc->getCharacteristic(FUJI_XT30_CHR_IND2_UUID)->subscribe(false); - progress_bar.value(60.0f); - // notifications - pSvc->getCharacteristic(FUJI_XT30_CHR_NOT1_UUID)->subscribe(true); - progress_bar.value(70.0f); - pSvc->getCharacteristic(FUJI_XT30_CHR_NOT2_UUID)->subscribe(true); - progress_bar.value(80.0f); - pSvc->getCharacteristic(FUJI_XT30_CHR_NOT3_UUID)->subscribe(true); - progress_bar.value(90.0f); - - Serial.println("Configured"); - - progress_bar.value(100.0f); - - return true; -} - -void FujifilmXT30::shutterPress(void) { - NimBLERemoteService *pSvc = m_Client->getService(FUJI_XT30_SVC_SHUTTER_UUID); - NimBLERemoteCharacteristic *pChr = pSvc->getCharacteristic(FUJI_XT30_CHR_SHUTTER_UUID); - pChr->writeValue(&FUJI_XT30_SHUTTER_CMD[0], sizeof(FUJI_XT30_SHUTTER_CMD), true); - pChr->writeValue(&FUJI_XT30_SHUTTER_PRESS[0], sizeof(FUJI_XT30_SHUTTER_PRESS), true); -} - -void FujifilmXT30::shutterRelease(void) { - NimBLERemoteService *pSvc = m_Client->getService(FUJI_XT30_SVC_SHUTTER_UUID); - NimBLERemoteCharacteristic *pChr = pSvc->getCharacteristic(FUJI_XT30_CHR_SHUTTER_UUID); - pChr->writeValue(&FUJI_XT30_SHUTTER_CMD[0], sizeof(FUJI_XT30_SHUTTER_CMD), true); - pChr->writeValue(&FUJI_XT30_SHUTTER_RELEASE[0], sizeof(FUJI_XT30_SHUTTER_RELEASE), true); -} - -void FujifilmXT30::shutterFocus(void) { - NimBLERemoteService *pSvc = m_Client->getService(FUJI_XT30_SVC_SHUTTER_UUID); - NimBLERemoteCharacteristic *pChr = pSvc->getCharacteristic(FUJI_XT30_CHR_SHUTTER_UUID); - pChr->writeValue(&FUJI_XT30_SHUTTER_CMD[0], sizeof(FUJI_XT30_SHUTTER_CMD), true); - pChr->writeValue(&FUJI_XT30_SHUTTER_FOCUS[0], sizeof(FUJI_XT30_SHUTTER_FOCUS), true); -} - -void FujifilmXT30::print(void) { - Serial.print("Name: "); - Serial.println(m_Name.c_str()); - Serial.print("Address: "); - Serial.println(m_Address.toString().c_str()); - Serial.print("Type: "); - Serial.println(m_Address.getType()); -} - -void FujifilmXT30::disconnect(void) { - m_Client->disconnect(); -} - -device_type_t FujifilmXT30::getDeviceType(void) { - return FURBLE_FUJIFILM_XT30; -} - -size_t FujifilmXT30::getSerialisedBytes(void) { - return sizeof(xt30_t); -} - -bool FujifilmXT30::serialise(void *buffer, size_t bytes) { - if (bytes != sizeof(xt30_t)) { - return false; - } - xt30_t *x = (xt30_t *)buffer; - strncpy(x->name, m_Name.c_str(), 64); - x->address = (uint64_t)m_Address; - x->type = m_Address.getType(); - memcpy(x->token, m_Token, XT30_TOKEN_LEN); - - return true; -} - -} // namespace Furble diff --git a/lib/furble/Furble.h b/lib/furble/Furble.h index 34a118a..6cc82c5 100644 --- a/lib/furble/Furble.h +++ b/lib/furble/Furble.h @@ -10,172 +10,17 @@ #define FURBLE_STR "furble" #ifndef FURBLE_VERSION -#define FURBLE_VERSION = "unknown" +#define FURBLE_VERSION "unknown" #endif -#define MAX_NAME (64) - -#define XT30_TOKEN_LEN (4) -#define UUID128_LEN (16) -#define UUID128_AS_32_LEN (UUID128_LEN / sizeof(uint32_t)) - typedef enum { - FURBLE_FUJIFILM_XT30 = 1, + FURBLE_FUJIFILM = 1, FURBLE_CANON_EOS_M6 = 2, FURBLE_CANON_EOS_RP = 3, } device_type_t; -typedef struct _uuid128_t { - union { - uint32_t uint32[UUID128_AS_32_LEN]; - uint8_t uint8[UUID128_LEN]; - }; -} uuid128_t; - -namespace Furble { - -/** - * Represents a single target camera. - */ -class Device { - public: - /** - * Connect to the target camera such that it is ready for shutter control. - * - * This should include connection and pairing as needed for the target - * device. - * - * @return true if the client is now ready for shutter control - */ - virtual bool connect(NimBLEClient *pClient, ezProgressBar &progress_bar) = 0; - - /** - * Disconnect from the target. - */ - virtual void disconnect(void) = 0; - - /** - * Send a shutter button press command. - */ - virtual void shutterPress(void) = 0; - - /** - * Send a shutter button release command. - */ - virtual void shutterRelease(void) = 0; - - /** - * Send a shutter button focus command. - */ - virtual void shutterFocus(void) = 0; - - const char *getName(void); - void save(void); - void remove(void); - - static void loadDevices(std::vector &device_list); - - /** - * Add matching devices to the list. - */ - static void match(NimBLEAdvertisedDevice *pDevice, std::vector &list); - - /** - * Generate a device consistent 128-bit UUID. - */ - static void getUUID128(uuid128_t *uuid); - - protected: - NimBLEAddress m_Address = NimBLEAddress(""); - NimBLEClient *m_Client; - std::string m_Name; - - private: - virtual device_type_t getDeviceType(void) = 0; - virtual size_t getSerialisedBytes(void) = 0; - virtual bool serialise(void *buffer, size_t bytes) = 0; - - void fillSaveName(char *name); -}; - -class FujifilmXT30: public Device { - public: - FujifilmXT30(const void *data, size_t len); - FujifilmXT30(NimBLEAdvertisedDevice *pDevice); - ~FujifilmXT30(void); - - /** - * Determine if the advertised BLE device is a Fujifilm X-T30. - */ - static bool matches(NimBLEAdvertisedDevice *pDevice); - - bool connect(NimBLEClient *pClient, ezProgressBar &progress_bar); - void shutterPress(void); - void shutterRelease(void); - void shutterFocus(void); - void disconnect(void); - void print(void); - - private: - device_type_t getDeviceType(void); - size_t getSerialisedBytes(void); - bool serialise(void *buffer, size_t bytes); - - uint8_t m_Token[XT30_TOKEN_LEN] = {0}; -}; - -class CanonEOSM6: public Device { - public: - CanonEOSM6(const void *data, size_t len); - CanonEOSM6(NimBLEAdvertisedDevice *pDevice); - ~CanonEOSM6(void); - - /** - * Determine if the advertised BLE device is a Canon EOS M6. - */ - static bool matches(NimBLEAdvertisedDevice *pDevice); - - const char *getName(void); - bool connect(NimBLEClient *pClient, ezProgressBar &progress_bar); - void shutterPress(void); - void shutterRelease(void); - void shutterFocus(void); - void disconnect(void); - - private: - device_type_t getDeviceType(void); - size_t getSerialisedBytes(void); - bool serialise(void *buffer, size_t bytes); - - uuid128_t m_Uuid; -}; - -class CanonEOSRP: public Device { - public: - CanonEOSRP(const void *data, size_t len); - CanonEOSRP(NimBLEAdvertisedDevice *pDevice); - ~CanonEOSRP(void); - - /** - * Determine if the advertised BLE device is a Canon EOS RP. - */ - static bool matches(NimBLEAdvertisedDevice *pDevice); - - const char *getName(void); - bool connect(NimBLEClient *pClient, ezProgressBar &progress_bar); - void shutterPress(void); - void shutterRelease(void); - void shutterFocus(void); - void disconnect(void); - - private: - device_type_t getDeviceType(void); - size_t getSerialisedBytes(void); - bool serialise(void *buffer, size_t bytes); - - uuid128_t m_Uuid; -}; - -} // namespace Furble +#include "CanonEOSM6.h" +#include "CanonEOSRP.h" +#include "Fujifilm.h" #endif diff --git a/src/furble.ino b/src/furble.ino index 3dba52a..b344552 100644 --- a/src/furble.ino +++ b/src/furble.ino @@ -55,11 +55,11 @@ static void remote_control(Furble::Device *device) { } if (m5.BtnB.wasPressed()) { - device->shutterFocus(); + device->focusPress(); } if (m5.BtnB.wasReleased()) { - device->shutterRelease(); + device->focusRelease(); } delay(50);