Canon ミラーレスカメラのシャッターコントローラーをATOM S3 で作ってみた

>

ShutterController
<キャノンのミラーレスカメラでタイムラプス動画を作ったりするのに、定期的にシャッターがきれるようにコントローラーを作ってみました。
僕はCanon M200 で使っていますが、基本的にはキャノンのワイヤレスリモートコントローラー BR-E1 が使えるものはどれでも使えるはずです。が、実際に確認したわけではないので、この辺りは、自己責任で…。
それと、この記事を見て同じように作成される方がいるかどうかわかりませんが、高価なカメラを扱うので、あくまでも自己責任でお願いします。
なにか、やばいことが起こっても一切責任はとれませんので、そこんとこ、よろしく。

M200のレリーズは、ワイヤレスで接続するBR-E1か、アプリを使ってスマホかPCでコントロールするようになっていて、RS60-E3のような直接接続するリモコンやレリーズのようにカメラに直接接続してシャッターを切る方法がありません。 ですので、自分でコントローラーを作成するためには、ワイヤレスの信号のやり取りがわからなければお手上げで、もちろん、キャノンさんも公開はしてくれていません。
が、BR-E1のBluetooth通信を解析してライブラリを作成してくれている方がありましたので、こちらを利用させていただいて、M5 ATOM S3 を使ってインターバルタイマーを作ってみました
ライブラリの詳細は、こちらを参照ください。
ESP32 Canon BLE Remote Library
github.com

回路

ATOM S3 には、タクトスイッチ3つとロータリーエンコーダーを接続して、以下のような回路を組みました。

Shutter Controller

押しボタンは、左からスタートスイッチ(ピン8)、セットスイッチ(ピン7)、ストップスイッチ(ピン39)と、します。

プログラム

ATOM S3のプログラムは、以下のようなもの
あらかじめ、上記のライブラリをインストールしておきます。

#include <M5Unified.h>
#include "CanonBLERemote.h"

#define lcd M5.Lcd
#define A_pin  5    // 割り込みピン エンコーダー用
#define B_pin  6    // 割り込みピン エンコーダー用
#define set_button 7
#define start_button 8
#define stop_button 39
#define EXPIN 38    //外部出力用

String name_remote = "ESP32 Remote";  //ペアリングしたときカメラに表示される名前
CanonBLERemote canon_ble(name_remote);

hw_timer_t * timer = NULL;
volatile boolean led_f = false;
volatile long count = 0;
volatile long TM_counter=0;
volatile long WT_counter=0;

int pre_count = 0;
int my_Mode =21;
int s_Mode = 11;
int ps_Mode;
int set_mode = 21;
int pre_Mode = 11;
int pre_set_mode =11;
unsigned long tm;
unsigned long freq_counter=0;
boolean start = false;

//電源を入れたときの初期値
  int set_md = 21;      //撮影モード
  int set_int = 30;     //撮影間隔
  int set_wait = 0;     //撮影間の待ち時間
  int set_freq = 0;     //撮影回数
  int set_brt = 128;    //Displayの明るさ

void setup() {
    auto cfg = M5.config();
    cfg.clear_display = true;     
        
    M5.begin();
    USBSerial.begin(115200);

    lcd.setRotation(3);
    lcd.setBrightness(set_brt);

    pinMode(A_pin, INPUT);
    pinMode(B_pin, INPUT);
    pinMode(set_button, INPUT_PULLUP);
    pinMode(start_button, INPUT_PULLUP);
    pinMode(stop_button, INPUT_PULLUP);
    pinMode(EXPIN, INPUT_PULLUP);

    canon_ble.init();
    delay(1000);
    // Pairing  Setボタンが押されていたらペアリング開始
    if(digitalRead(set_button) == LOW ){

        // pair() function should be called only when you want to pair with the new camera. 
        // After paired, the pair() function should not be called.
        do{
            USBSerial.println("Pairing...");
        }
        while(!canon_ble.pair(10));
        Serial.println(canon_ble.getPairedAddressString());
        
    }
    delay(1000);
    USBSerial.println("Setup Done");

    attachInterrupt(A_pin, pulse_counter, FALLING);

 // タイマ作成
    timer = timerBegin(0, 80, true);

  // タイマ割り込みサービス・ルーチン onTimer を登録
    timerAttachInterrupt(timer, &onTimer, true);
  
  // 割り込みタイミング(ms)の設定
    timerAlarmWrite(timer, 1000000, true);

  // タイマ有効化
    timerAlarmEnable(timer);
    
    lcd.setTextColor(YELLOW,BLACK);
}

//カメラのシャッターを切る命令
void shoot(){
        USBSerial.println("Shutter pressed");
          if(!canon_ble.focus()){
            USBSerial.println("Focus Failed");
          }
          else
          {
            USBSerial.println("Focus");
          }

          if(!canon_ble.trigger()){
            USBSerial.println("Trigger Failed");
          }
          else {
            USBSerial.println("Shutter trigger");
          }
}

void loop() {
  
  // 撮影モード (Single Mode)
  while(my_Mode == 21){
    ps_Mode = 0;

    //Setボタンが押されたら設定モードに移行
    if(digitalRead(set_button) == LOW){
      while(digitalRead(set_button) == LOW){}
        s_Mode = 11;
        my_Mode = 11;
    }     
    
    //Startボタンが押されたら撮影開始(カウントダウン)
    if(digitalRead(start_button) == LOW) {
      while(digitalRead(start_button) == LOW){}
      start = true;
      canon_ble.focus();
      shoot();
      TM_counter = set_int;
      WT_counter = set_wait;
      freq_counter = 0;
    }

    //Stopボタンが押されたら撮影終了(カウントストップ、カウンターリセット)
    if(digitalRead(stop_button) == LOW) {
    start = false;
    }

    //External Trigger 外部入力のスイッチが入ったらシャッターを切る 
    if(digitalRead(EXPIN) == LOW){
      shoot();
      USBSerial.println("EXPIN Triggered");
    }
         
   //Singleモードで撮影 (通常のタイマー撮影)
    if (pre_Mode != my_Mode ) disp_Single();
    if(start == true) {
      lcd.setTextColor(YELLOW,BLACK);
      lcd.setTextSize(2);
      lcd.setCursor(0,25);
      lcd.print("Int.");
      lcd.setCursor(65,25);
      lcd.printf("%04d",freq_counter+1);     
      lcd.setTextSize(4);
      lcd.setCursor(18,50);
      lcd.printf("%04d",TM_counter);

      //カウントダウン ゼロでシャッターを切る
      if(TM_counter <= 0) {
        shoot();
        freq_counter++;
        // Wait中 (次の撮影までの待ち時間)
        WT_counter=set_wait;
        while(WT_counter > 0) {
          lcd.setTextSize(2);
          lcd.setCursor(0,25);
          lcd.print("Wait");
          lcd.setTextSize(4);
          lcd.setCursor(18,50);
          lcd.printf("%04d",WT_counter);
        }
        TM_counter = set_int;
      }
      //撮影回数が設定値に達したら、撮影終了
      if ((freq_counter+1 >= set_freq) and (set_freq != 0)) {
        start = false;
        freq_counter = 0;
      }
    }
       
    
    pre_Mode = my_Mode;

    // ATOM のボタンを押してシャッターを切る
    M5.update();
    if (M5.BtnA.wasPressed()){
      shoot();
      USBSerial.println("ButtonA Pressed");
    }

  }

// Bulb Mode  バルブモードで撮影−一回の撮影の前後2回シャッターを切る
  while(my_Mode == 22) {
    ps_Mode = 0;
    //Setボタンが押されたら設定モードに移行
    if(digitalRead(set_button) == LOW){
      while(digitalRead(set_button) == LOW){}
        s_Mode = 11;
        my_Mode = 11;
    } 

    //Startボタンが押されたら撮影開始(カウントダウン)
    if(digitalRead(start_button) == LOW) {
      start = true;
      shoot();
      TM_counter = set_int;
      WT_counter = set_wait;
      freq_counter = 0;
    }

    //Stopボタンが押されたら撮影終了(カウントストップ、カウンターリセット)
    if(digitalRead(stop_button) == LOW) {
      //counter = 0;
      start = false;
    }

    //External Trigger 外部入力のスイッチが入ったらシャッターを切る 
    if(digitalRead(EXPIN) == LOW){
      shoot();
      USBSerial.println("EXPIN Triggered");
    }
         
   //Bulbモードで撮影 (シャッター速度をBulbにしたときのモード)   
    if (pre_Mode != my_Mode ) {
      disp_Single();
      lcd.fillRect(0,8,128,16,RED);
      lcd.setTextColor(YELLOW,RED);
      lcd.setTextSize(2);
      lcd.setCursor(28,8);
      lcd.print("Bulb");
    }
    if(start == true) {
      lcd.setTextColor(YELLOW,BLACK);
      lcd.setTextSize(2);
      lcd.setCursor(0,25);
      lcd.print("Int.");
      lcd.setCursor(65,25);
      lcd.printf("%04d",freq_counter+1);     
      lcd.setTextSize(4);
      lcd.setCursor(18,50);
      lcd.printf("%04d",TM_counter);
      
      //カウントダウン ゼロでシャッターを切る
      if(TM_counter <= 0) {
        shoot();
        freq_counter++;
        //Wait中 (次の撮影までの待ち時間)
        WT_counter=set_wait;
        while(WT_counter > 0) {
          lcd.setTextSize(2);
          lcd.setCursor(0,25);
          lcd.print("Wait");
          lcd.setTextSize(4);
          lcd.setCursor(18,50);
          lcd.printf("%04d",WT_counter);
        }
        shoot();
        TM_counter = set_int;
      }
      //撮影回数が設定値に達したら、撮影終了
      if ((freq_counter+1 >= set_freq) and (set_freq != 0)) {
        start = false;
        freq_counter = 0;
      }
    }
       
    
    pre_Mode = my_Mode;

    // ATOM のボタンを押してシャッターを切る
    M5.update();
    if (M5.BtnA.wasPressed()){
      shoot();
      USBSerial.println("ButtonA Pressed");
    }
  }

  //設定モード
  while(my_Mode == 11) {
    //モード設定
    while ((my_Mode == 11) and (s_Mode == 11)) {  
      if (ps_Mode != s_Mode) disp_runMode();      
      if(digitalRead(stop_button) == LOW){            //StopボタンでSingleモード            
        while(digitalRead(stop_button) == LOW){}
        set_md = 22;
      }
      if(digitalRead(start_button) == LOW){           //StartボタンでBulbモード
      while(digitalRead(start_button) == LOW){}
        set_md = 21;
      }

      if (set_md == 22) {
        lcd.setTextColor(BLUE,GREEN);
        lcd.setTextSize(2);
        lcd.setCursor(12,82);
        lcd.print("Bulb");
        lcd.setTextColor(YELLOW,BLACK);
        lcd.setCursor(12,45);
        lcd.print("Single");
        
      }
      if (set_md ==21) {
        lcd.setTextColor(BLUE,GREEN);
        lcd.setTextSize(2);
        lcd.setCursor(12,45);
        lcd.print("Single");
        lcd.setTextColor(YELLOW,BLACK);
        lcd.setCursor(12,82);
        lcd.print("Bulb");
        
      }
      ps_Mode = s_Mode;
      
      if(digitalRead(set_button) == LOW){         //Setボタンで撮影間隔設定
        tm = millis();
        while(digitalRead(set_button) == LOW){
          //USBSerial.println(millis()-tm);
          if ( millis()-tm > 2000 ) break;        
        }
        if (millis()-tm > 2000 ) {                //Setボタンの長押しで撮影モード
          my_Mode = set_md;
        }
        else{
          s_Mode = 12;
        }
      }
    }
  

    while ((my_Mode == 11) and (s_Mode == 12)) {
      count = set_int;
      if (ps_Mode != s_Mode) disp_Interval();

      lcd.setTextColor(YELLOW,BLACK);
      lcd.setTextSize(4);
      lcd.setCursor(18,50);
      lcd.printf("%04d",count);
      set_int = count;
        
      ps_Mode = s_Mode;
      
      if(digitalRead(set_button) == LOW){         //SetボタンでWait時間設定
        tm = millis();
        while(digitalRead(set_button) == LOW){
          if ( millis()-tm > 2000 ) break;
        }
        if (millis()-tm > 2000 ) {
          my_Mode = set_md;
        }
        else{
          s_Mode = 13;
        }
      }
    }
  
    while ((my_Mode == 11) and (s_Mode == 13)) {
      count = set_wait;
      if (ps_Mode != s_Mode) disp_wait();

      lcd.setTextColor(YELLOW,BLACK);
      lcd.setTextSize(4);
      lcd.setCursor(18,50);
      lcd.printf("%02d",count);
      set_wait = count;
        
      ps_Mode = s_Mode;
      
      if(digitalRead(set_button) == LOW){         //Setボタンで撮影回数設定
        tm = millis();
        while(digitalRead(set_button) == LOW){
          if ( millis()-tm > 2000 ) break;
        }
        if (millis()-tm > 2000 ) {
          my_Mode = set_md;
        }
        else{
          s_Mode = 14;
        }
      }
    }
  
    while ((my_Mode == 11) and (s_Mode == 14)) {
      count = set_freq;
      if (ps_Mode != s_Mode) disp_Frequency();

      lcd.setTextColor(YELLOW,BLACK);
      lcd.setTextSize(4);
      lcd.setCursor(18,50);
      lcd.printf("%04d",count);
      set_freq = count;
        
      ps_Mode = s_Mode;
      
      if(digitalRead(set_button) == LOW){         //SetボタンでDisplayの明るさ設定
        tm = millis();
        while(digitalRead(set_button) == LOW){
          if ( millis()-tm > 2000 ) break;
        }
        if (millis()-tm > 2000 ) {
          my_Mode = set_md;
        }
        else{
          s_Mode = 15;
        }
      }
    }
  
    while ((my_Mode == 11) and (s_Mode == 15)) {
      count = set_brt;
      if (ps_Mode != s_Mode) disp_Brightness();

      lcd.setTextColor(YELLOW,BLACK);
      lcd.setTextSize(4);
      lcd.setCursor(18,50);
      lcd.printf("%03d",count);
      set_brt = count;
      lcd.setBrightness(set_brt);
        
      ps_Mode = s_Mode;
      
      if(digitalRead(set_button) == LOW){
        tm = millis();
        while(digitalRead(set_button) == LOW){
          //USBSerial.println(millis()-tm);
          if ( millis()-tm > 2000 ) break;
        }
        if (millis()-tm > 2000 ) {
          my_Mode = set_md;
        }
        else{
          s_Mode = 11;
        }
      }
    }
  

    while(digitalRead(set_button) == LOW){lcd.clear();}  
    pre_Mode = 11;

    }
   }
  
//ロータリーエンコーダのパルスのカウント
void pulse_counter() {
  if(digitalRead(A_pin) ^ digitalRead(B_pin)) {
    count++;
  } else {
    count--;
  }
}

//タイマーのカウントダウン
void onTimer() {
  TM_counter--;
  WT_counter--;
}

使い方

カメラの設定

まず、カメラ側の設定をリモートコントローラー(BR-E1)で撮影できる状態に設定しておきます。
機内モードを切に設定
✔Bluetooyh設定を使うに設定→ワイヤレスリモコンと接続→コントローラーとペアリング(ESP32 Remote と表示されればOK)
✔ドライブモードを[セルフ:10秒/リモコン] に設定

コントローラーの設定

ペアリング
コントローラーはSETボタンを押したままの状態で電源をONするか、SETボタンを押したままリセットボタンを押して再起動すれば、ペアリングのモードになります。
カメラに"ESP32 Remote"と表示されてペアリングが完了したら、SETボタンを押さずにリセットボタンを押すとリモコンが通常モードで起動します。
このままスタートボタンを押せばタイマーがスタートします。 ここで、セットボタンを押すと、セットモードになってパラメーターの設定ができます。
パラメーターの設定
パラメーターの設定は、
Modeの設定
撮影モードの選択でスタートスイッチとストップスイッチで選択し、セットスイッチで決定、次の設定に進みます。
シングルショット  通常の撮影をインターバルタイマーで継続撮影を行います
バルブモード    バルブで撮影を行います。 星野写真を取るときなどに使います。
Intervalの設定
インターバルタイマーの設定で、シャッター間隔を設定します。 ロータリーエンコーダを回して、0〜9999秒まで設定できます。セットスイッチで次の設定に進みます。
Waitの設定
撮影が終わって、次の撮影がスタートするまでの待ち時間です。バルブ撮影で長時間撮影すると、データを書き込んだり、撮影画像を表示している間次の撮影に入れないので、その間の時間を調整するように設定することができます。
ロータリーエンコーダを回して、0〜99秒まで設定できます。
Frequencyの設定
撮影回数の設定です。 設定した回数撮影すると撮影が終了します。 ゼロに設定しておくとずっと撮影を続けます。
Brightnessの設定
ATOM S3 のディスプレイの輝度の設定です。夜に星の撮影をしていると結構眩しかったりするので設定できるようにしてみました。

パラメータの設定が終了したら、セットボタンを長押しすると、一瞬画面が消えて、撮影モードに移行します。撮影モードへのきりかえは、どの設定場面でも長押しで撮影モードに変わります。

撮影モード

撮影モードでStartボタンを押すとシャッターが切られ、カウントダウンが開始します。 設定した秒数経過するとシャッターが切られ、以後、継続的に設定秒数経過する毎にシャッターが切られます。
シャッターを切ったあと、設定したWait時間経過後、次のカウントダウンが始まります。 カメラの設定によっては、このWait時間をうまく設定しないとシャッターが切れないことがあるので、調整が必要です。
また、ATOMからのトリガー信号からカメラの反応するまでのタイムラグがあるので、秒数もあまり正確ではなく、シャッターのタイミング精度を要求する撮影には、向いていないかもしれません。 設定値をいろいろ変えて、カットアンドトライして見てください。
バルブモードのときには、カウントダウン開始時にシャッターが切られ、カウントダウン終了時にもう一度シャッターが切られて、バルブ撮影が終了します。
撮影モードのときにATOM本体のスイッチを押すとマニュアルでシャッターを切ることができます。
また、外部入力からのトリガーでシャッターを切ることができるので、ポタ赤と連動させたり、センサーで撮影したりすることができます。
説明動画をアップしました
youtu.be

最後に

自分の用途には、必要十分なものができたと思っているのですが、実際に使う際には、時間やタイミングの調整などいろいろと調整が必要だと思います。
タイムラプス撮影だけなら、カメラにもその機能があるし、スマホとかと連動させればできることですが、細かいことをするには、便利だと思っています。
特に、フィールドで使用するには、PCやバッテリーやスマホなどといろんなものを持参するのも大変なのでできるだけシンプルにしたいです。また、BluetoothWifiを使うと通信の設定も大変だし、電源も通信に容量を食われてしまいます。そういう意味では、以前の接点スイッチでシャッターが切れるコネクタを残しておいて欲しかったなぁ〜と思っているこの頃です。