Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial rewrite to better support Canon EOS R. #172

Merged
merged 3 commits into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ The following devices have been tested and confirmed to work:
- Canon
- Canon EOS M6 ([@tardisx](https://github.com/tardisx))
- Canon EOS RP ([@wolcano](https://github.com/wolcano))
- Canon EOS R6 Mark II ([@hijae](https://github.com/hijae))
- Mobile Devices (beta)
- Android
- iOS
Expand Down Expand Up @@ -64,11 +65,11 @@ Currently supported features in `furble`:

### Table of Features

|   | Fujifilm X & GFX | Canon EOS M6 | Canon EOS RP | Android & iOS |
|   | Fujifilm X & GFX | Canon EOS M6 | Canon EOS R | Android & iOS |
| --- | :---: | :---: | :---: | :---: |
| Scanning & Pairing | ✔️ | ✔️ | ✔️ | ✔️ |
| Shutter Release | ✔️ | ✔️ | ✔️ | ✔️ |
| Focus | ✔️ (see [#99](https://github.com/gkoh/furble/discussions/99)) | :x: | :x: (see [#29](https://github.com/gkoh/furble/issues/29)) | :x: |
| Focus | ✔️ (see [#99](https://github.com/gkoh/furble/discussions/99)) | :x: | ✔️ | :x: |
| GPS location tagging | ✔️ | :x: (WiFi only) | :x: | :x: |

## Installation
Expand Down Expand Up @@ -305,7 +306,8 @@ 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.
@wolcano kindly implemented initial support for the Canon EOS RP.
@hijae kindly helped with better Canon EOS R support.

#### Protocol Reverse Engineering

Expand Down
2 changes: 1 addition & 1 deletion lib/furble/Camera.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class Camera {
enum class Type : uint32_t {
FUJIFILM = 1,
CANON_EOS_M6 = 2,
CANON_EOS_RP = 3,
CANON_EOS_R = 3,
MOBILE_DEVICE = 4,
FAUXNY = 5,
};
Expand Down
10 changes: 5 additions & 5 deletions lib/furble/CameraList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#include <Preferences.h>

#include "CanonEOSM6.h"
#include "CanonEOSRP.h"
#include "CanonEOSR.h"
#include "FauxNY.h"
#include "Fujifilm.h"
#include "MobileDevice.h"
Expand Down Expand Up @@ -155,8 +155,8 @@ void CameraList::load(void) {
case Camera::Type::CANON_EOS_M6:
m_ConnectList.push_back(std::unique_ptr<Furble::Camera>(new CanonEOSM6(dbuffer, dbytes)));
break;
case Camera::Type::CANON_EOS_RP:
m_ConnectList.push_back(std::unique_ptr<Furble::Camera>(new CanonEOSRP(dbuffer, dbytes)));
case Camera::Type::CANON_EOS_R:
m_ConnectList.push_back(std::unique_ptr<Furble::Camera>(new CanonEOSR(dbuffer, dbytes)));
break;
case Camera::Type::MOBILE_DEVICE:
m_ConnectList.push_back(std::unique_ptr<Furble::Camera>(new MobileDevice(dbuffer, dbytes)));
Expand Down Expand Up @@ -200,8 +200,8 @@ bool CameraList::match(const NimBLEAdvertisedDevice *pDevice) {
} else if (CanonEOSM6::matches(pDevice)) {
m_ConnectList.push_back(std::unique_ptr<Furble::Camera>(new Furble::CanonEOSM6(pDevice)));
return true;
} else if (CanonEOSRP::matches(pDevice)) {
m_ConnectList.push_back(std::unique_ptr<Furble::Camera>(new Furble::CanonEOSRP(pDevice)));
} else if (CanonEOSR::matches(pDevice)) {
m_ConnectList.push_back(std::unique_ptr<Furble::Camera>(new Furble::CanonEOSR(pDevice)));
return true;
}

Expand Down
179 changes: 8 additions & 171 deletions lib/furble/CanonEOS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@

namespace Furble {

constexpr uint8_t CanonEOS::MODE_SHOOT;

CanonEOS::CanonEOS(Type type, const void *data, size_t len) : Camera(type) {
if (len != sizeof(eos_t))
abort();
Expand All @@ -29,181 +27,20 @@ CanonEOS::CanonEOS(Type type, const NimBLEAdvertisedDevice *pDevice) : Camera(ty
Device::getUUID128(&m_Uuid);
}

// Handle pairing notification
void CanonEOS::pairCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic,
uint8_t *pData,
size_t length,
bool isNotify) {
if (!isNotify && (length > 0)) {
ESP_LOGI(LOG_TAG, "Got pairing callback: 0x%02x", pData[0]);
m_PairResult = pData[0];
}
void CanonEOS::_disconnect(void) {
m_Client->disconnect();
m_Connected = false;
}

bool CanonEOS::write_value(NimBLEClient *pClient,
const NimBLEUUID &serviceUUID,
bool CanonEOS::writePrefix(const NimBLEUUID &serviceUUID,
const NimBLEUUID &characteristicUUID,
const 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 NimBLEUUID &serviceUUID,
const NimBLEUUID &characteristicUUID,
const uint8_t prefix,
const uint8_t *data,
size_t length) {
const uint8_t prefix,
const void *data,
uint16_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(void) {
if (NimBLEDevice::isBonded(m_Address)) {
// Already bonded? Assume pair acceptance!
m_PairResult = PAIR_ACCEPT;
} else {
m_PairResult = 0x00;
}

ESP_LOGI(LOG_TAG, "Connecting");
if (!m_Client->connect(m_Address)) {
ESP_LOGI(LOG_TAG, "Connection failed!!!");
return false;
}

ESP_LOGI(LOG_TAG, "Connected");
m_Progress = 10;

ESP_LOGI(LOG_TAG, "Securing");
if (!m_Client->secureConnection()) {
return false;
}
ESP_LOGI(LOG_TAG, "Secured!");
m_Progress = 20;

NimBLERemoteService *pSvc = m_Client->getService(SVC_IDEN_UUID);
if (pSvc) {
NimBLERemoteCharacteristic *pChr = pSvc->getCharacteristic(CHR_NAME_UUID);
if ((pChr != nullptr) && pChr->canIndicate()) {
ESP_LOGI(LOG_TAG, "Subscribed for pairing indication");
pChr->subscribe(false,
[this](BLERemoteCharacteristic *pChr, uint8_t *pData, size_t length,
bool isNotify) { this->pairCallback(pChr, pData, length, isNotify); });
}
}

ESP_LOGI(LOG_TAG, "Identifying 1!");
const auto name = Device::getStringID();
if (!write_prefix(m_Client, SVC_IDEN_UUID, CHR_NAME_UUID, 0x01, (uint8_t *)name.c_str(),
name.length()))
return false;

m_Progress = 30;

ESP_LOGI(LOG_TAG, "Identifying 2!");
if (!write_prefix(m_Client, SVC_IDEN_UUID, CHR_IDEN_UUID, 0x03, m_Uuid.uint8,
Device::UUID128_LEN))
return false;

m_Progress = 40;

ESP_LOGI(LOG_TAG, "Identifying 3!");
if (!write_prefix(m_Client, SVC_IDEN_UUID, CHR_IDEN_UUID, 0x04, (uint8_t *)name.c_str(),
name.length()))
return false;

m_Progress = 50;

ESP_LOGI(LOG_TAG, "Identifying 4!");

uint8_t x = 0x02;
if (!write_prefix(m_Client, SVC_IDEN_UUID, CHR_IDEN_UUID, 0x05, &x, 1))
return false;

m_Progress = 60;

ESP_LOGI(LOG_TAG, "Identifying 5!");

// Give the user 60s to confirm/deny pairing
ESP_LOGI(LOG_TAG, "Waiting for user to confirm/deny pairing.");
for (unsigned int i = 0; i < 60; i++) {
m_Progress = m_Progress.load() + (i % 2);
if (m_PairResult != 0x00) {
break;
}
vTaskDelay(pdMS_TO_TICKS(1000));
}

if (m_PairResult != PAIR_ACCEPT) {
bool deleted = NimBLEDevice::deleteBond(m_Address);
ESP_LOGW(LOG_TAG, "Rejected, delete pairing: %d", deleted);
return false;
}

ESP_LOGI(LOG_TAG, "Paired!");

/* write to 0xf104 */
x = 0x01;
if (!write_value(m_Client, SVC_IDEN_UUID, CHR_IDEN_UUID, &x, 1))
return false;

m_Progress = 90;

ESP_LOGI(LOG_TAG, "Switching mode!");

/* write to 0xf307 */
if (!write_value(m_Client, SVC_MODE_UUID, CHR_MODE_UUID, &MODE_SHOOT, sizeof(MODE_SHOOT)))
return false;

ESP_LOGI(LOG_TAG, "Done!");
m_Progress = 100;

return true;
}

void CanonEOS::shutterPress(void) {
uint8_t x[2] = {0x00, 0x01};
write_value(m_Client, SVC_SHUTTER_UUID, CHR_SHUTTER_UUID, &x[0], 2);
}

void CanonEOS::shutterRelease(void) {
uint8_t x[2] = {0x00, 0x02};
write_value(m_Client, SVC_SHUTTER_UUID, CHR_SHUTTER_UUID, &x[0], 2);
}

void CanonEOS::focusPress(void) {
// do nothing
return;
}

void CanonEOS::focusRelease(void) {
// do nothing
return;
}

void CanonEOS::updateGeoData(const gps_t &gps, const timesync_t &timesync) {
// do nothing
return;
}

void CanonEOS::_disconnect(void) {
m_Client->disconnect();
m_Connected = false;
return m_Client->setValue(serviceUUID, characteristicUUID, {&buffer[0], (uint16_t)(length + 1)});
}

size_t CanonEOS::getSerialisedBytes(void) const {
Expand Down
52 changes: 8 additions & 44 deletions lib/furble/CanonEOS.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,57 +23,21 @@ class CanonEOS: public Camera {
CanonEOS(Type type, const void *data, size_t len);
CanonEOS(Type type, const NimBLEAdvertisedDevice *pDevice);

const NimBLEUUID SVC_IDEN_UUID {0x00010000, 0x0000, 0x1000, 0x0000d8492fffa821};
/** 0xf108 */
const NimBLEUUID CHR_NAME_UUID {0x00010006, 0x0000, 0x1000, 0x0000d8492fffa821};
/** 0xf104 */
const NimBLEUUID CHR_IDEN_UUID {0x0001000a, 0x0000, 0x1000, 0x0000d8492fffa821};

const NimBLEUUID SVC_MODE_UUID {0x00030000, 0x0000, 0x1000, 0x0000d8492fffa821};
/** 0xf307 */
const NimBLEUUID CHR_MODE_UUID {0x00030010, 0x0000, 0x1000, 0x0000d8492fffa821};

const NimBLEUUID SVC_SHUTTER_UUID {0x00030000, 0x0000, 0x1000, 0x0000d8492fffa821};
/** 0xf311 */
const NimBLEUUID CHR_SHUTTER_UUID {0x00030030, 0x0000, 0x1000, 0x0000d8492fffa821};

static constexpr uint8_t PAIR_ACCEPT = 0x02;
static constexpr uint8_t PAIR_REJECT = 0x03;
static constexpr uint8_t MODE_PLAYBACK = 0x01;
static constexpr uint8_t MODE_SHOOT = 0x02;
static constexpr uint8_t MODE_WAKE = 0x03;

bool write_value(NimBLEClient *pClient,
const NimBLEUUID &serviceUUID,
const NimBLEUUID &characteristicUUID,
const uint8_t *data,
size_t length);

bool write_prefix(NimBLEClient *pClient,
const NimBLEUUID &serviceUUID,
const NimBLEUUID &characteristicUUID,
const uint8_t prefix,
const uint8_t *data,
size_t length);
Device::uuid128_t m_Uuid;

void shutterPress(void) override;
void shutterRelease(void) override;
void focusPress(void) override;
void focusRelease(void) override;
void updateGeoData(const gps_t &gps, const timesync_t &timesync) override;
size_t getSerialisedBytes(void) const override;
bool serialise(void *buffer, size_t bytes) const override;

Device::uuid128_t m_Uuid;

protected:
bool _connect(void) override;
void _disconnect(void) override;
void _disconnect(void) override final;

private:
volatile uint8_t m_PairResult = 0x00;
bool writePrefix(const NimBLEUUID &serviceUUID,
const NimBLEUUID &characteristicUUID,
const uint8_t prefix,
const void *data,
uint16_t length);

void pairCallback(NimBLERemoteCharacteristic *, uint8_t *, size_t, bool);
private:
};

} // namespace Furble
Expand Down
Loading