Skip to content

Commit

Permalink
25 refactor canon eos support (#26)
Browse files Browse the repository at this point in the history
* Refactor Canon EOS support.

Implement a Canon EOS base class and derive the M6 and RP from it.
Whilst here, refactor the focus button handling such that there is are
separate 'focusPress' and 'focusRelease' methods.

* Attempt to implement focus support for Canon EOS.

* Update readme for newly supported cameras.

* fix typo in #define (#27)

Co-authored-by: wolcano <martin.karas@gmail.com>
  • Loading branch information
gkoh and wolcano authored Nov 15, 2022
1 parent 26ec382 commit f220019
Show file tree
Hide file tree
Showing 14 changed files with 686 additions and 752 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
185 changes: 185 additions & 0 deletions lib/furble/CanonEOS.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
#include <NimBLEAddress.h>
#include <NimBLEAdvertisedDevice.h>
#include <NimBLEDevice.h>
#include <NimBLERemoteCharacteristic.h>
#include <NimBLERemoteService.h>

#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
68 changes: 68 additions & 0 deletions lib/furble/CanonEOS.h
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit f220019

Please sign in to comment.