Release 2017.7.4

Arduinoでカメラのシャッターを切る

全てではないんですが、デジタル一眼レフカメラには赤外線受信機能があり、それでシャッターが切れます。Arduinoを使い、それをやってみました。自作無線レリーズのプロトタイピングです。

ただし、ここではNikonデジタル一眼レフ用の話です(Nikonしか持っていないので…)。

使用電子パーツ

  • Arduino(今回はProMini 3.3Vを使用)
  • タクトスイッチ
  • 赤外線LED
  • 抵抗
  • コンデンサ
  • (あれば)通常LED&抵抗

抵抗の値は、使うArduinoと赤外線LEDによって変わってくるので、自分で調べて計算してください。

マルツ電波 – LEDの基本

コンデンサも電源安定用として繋げているので(つもり)、容量は適当です…。

トランジスタ

赤外線の飛距離を稼ぐにはトランジスタを使って、電流量を別に操作するといいみたいです。一応、無くても動作はします。

自分は試しに以下のトランジスタを使ってみました。

NPNトランジスタ(2SC1815-GR

こちらのトランジスタはこんなピン仕様になっています。

Emitter : 入力電源, Collector : 出力電流, Base : トリガー(Arduino)

回路図

基本的にタクトスイッチでLEDをつけるのと一緒で単純です。

Arduinoピンから、直接赤外線LEDの場合

別で、D13ピンに確認用LEDを出しているので、手元に普通のLEDが余っていればつなげます。また、トランジスタを使う場合は、タイプによってピンの順番が全然違うので気をつける必要があります。

サンプルスケッチ

以下が上手くいったサンプルスケッチです。

#define LED    13
#define IRLED  3
#define SW     8
#define SW_PUSH     700
#define NIKON_RANGE 8


void setup() {
  pinMode(LED, OUTPUT);
  pinMode(IRLED, OUTPUT);
  pinMode(SW, INPUT_PULLUP);
}


void loop() {
  unsigned int gauge;
  while (!digitalRead(SW)) gauge++;
  if (gauge > SW_PUSH)
  {
    digitalWrite(LED, HIGH);
    SHUTTER_GO();
    delay(100);
    digitalWrite(LED, LOW);
  }
}

unsigned short ShutterDATA[NIKON_RANGE][NIKON_RANGE] = {
  {2000 , 1},
  {28000, 0},
  {400  , 1},
  {1580 , 0},
  {400  , 1},
  {3580 , 0},
  {400  , 1},
  {63200, 0}
};

void SHUTTER_GO() {

  digitalWrite(IRLED, 0);   // IR LED off forcely
  delay(100);

  // send total IR Signal 2 times
  for (byte repeat = 0 ; repeat < 2 ; repeat++)
  {
    //each IR Pulse
    for (byte i = 0 ; i < NIKON_RANGE ; i++)
    {
      unsigned long time_bit = micros();  // get start 

      while ((micros() - time_bit) <= ShutterDATA[i][0])
      {
        digitalWrite(IRLED, ShutterDATA[i][1]);
        delayMicroseconds(5);
        digitalWrite(IRLED, 0);
        delayMicroseconds(10);
      }
    }
  }
  digitalWrite(IRLED, 0); // IR LED off forcely
}

とりあえずコピペでIDEに載せて、動くか確認をどうぞ。

注意点

基本的に普通のLEDと同じなので単純なんですが、リモコンとして機能させるには気をつけないといけないことがあります。

赤外線LED

不可視

普通のLEDと違って、赤外線は人の目に見えません。しかも“点灯”ではなく“点滅”させることで機能させるので、ちゃんと信号が機能しているのか非常に分かりづらいです。

一眼レフやビデオカメラなどを通してみると視認できるようなので、可能なら実験・検証中はそういった機材を用意しておくといい、かと思います。(iPhoneなどのカメラはムリなようです。)

指向性

手軽に入手できる赤外線LEDは指向性が狭いものが多いです。というか、赤外線LED自体が全方向に照射されるようなものでは無いようです。

赤外線LEDの指向性

これはちょっと狙いがズレただけでも、受信機に届かないことを意味します。「そんな筈は」と思うかもしれませんが、身の回りのリモコンを確認してみると、確かにそれをカバーするかのように“複数のLED”が使われているタイプもあったりします。

家電リモコンのLED

最初、それを知らなかったので、ずっと上手くいきませんでした。

動作確認では、なるべく受信機に近づけて、まず信号のやりとりが上手くいってるのかを調べたほうがいいです。飛距離はその先の話です。

ただ、今回の実験でも、結局1m半くらいしか届かなかったので、それなりに機能させたければ、パーツの選定や回路に、ここで書かれているより工夫が必要です。

カメラ設定

送信の確認をするために、実際カメラへ飛ばすことになると思いますが、カメラ側で受信待機状態にしないと反応しないタイプがあると思います。というかNikonの場合は必要なので、まず設定します。

リモコン受信設定

メニュー

撮影メニュー

レリーズモード

瞬時リモコン

ちなみにこの設定、電源を落とすと戻ってしまうみたいです。要注意。

赤外線受信部

自分のD5200 / D5300では前後で計2箇所の受信部があります。多分、他の機種もほぼ同じような場所にあると思います。赤外線LEDはここを狙って放ちます。

解説

オンオフ信号

赤外線通信は大雑把な話、デジタル信号のやり取りを赤外線LEDの点滅に置き換えているようなものです。

例えば、一般的にデジタル信号は、一定の時間で区切り、その間に起きた電圧のオンオフ(HIGH・LOW)の違いでビットを検出しています。

一定時間で区切った中で、長ければ1、短ければ0

これがLEDの明滅具合に置き換わっているわけです。

ただし、理屈は一緒でも、具体的には、赤外線LEDのやりとりはメーカーや製品によって、時間間隔(周波数)など送受信のフォーマットが違います。ガッチリと“規格化”されたスタンダードがない状態です。

とは言え、NEC/家製協/SONYの三種が設定しているフォーマットが、ほぼ現実的にメインストリームとなっていて、ある程度、共通的な組み方があるようです。

共通するのは、リーダーと呼ばれる長いオン時間があり、その後ON・OFFが繰り返されます。

詳細 ELM – 赤外線リモコンの通信フォーマット

で、Nikonのシャッター用赤外線は以下のような時間尺ルールになっています。

SB-Projects – Nikon IR-Remote Controlより

この点滅を63.2ミリ秒空けて2回繰り返すそうです。

単純に考えれば、この時間設定で赤外線LEDをオンオフすれば、命令信号を発信したことになるハズ。

ということで、そういうスケッチを書いてみます。digitalWriteとdelayを組み合わせた簡単なスケッチです。

#define IRLED 3
#define SW    8

void setup() {
  pinMode(SW, INPUT_PULLUP);
  pinMode(IRLED, OUTPUT);
}

void loop() {
  int gauge;
  //switch read without chattering
  while (!digitalRead(SW)) gauge++;

  //send shutter command
  if (gauge > 700)
  {
    for (byte i = 0 ; i < 2 ; i++)
    {
      digitalWrite(IRLED, HIGH);
      delayMicroseconds(2000);
      digitalWrite(IRLED, LOW);
      delayMicroseconds(28000);
      digitalWrite(IRLED, HIGH);
      delayMicroseconds(400);
      digitalWrite(IRLED, LOW);
      delayMicroseconds(1580);
      digitalWrite(IRLED, HIGH);
      delayMicroseconds(400);
      digitalWrite(IRLED, LOW);
      delayMicroseconds(3580);
      digitalWrite(IRLED, HIGH);
      delayMicroseconds(400);
      digitalWrite(IRLED, LOW);
      delayMicroseconds(63200);
    }
  }
}

ところが、こんなのでは反応してくれません。

赤外線LEDの扱いではもうひとつ大事なルールがあります。

キャリアとサブキャリア

目に見えない赤外線は自然界にも飛び交っているので、赤外線LEDで出す光はこの自然界のノイズに埋もれてしまいます。結果、命令信号として識別することが出来ないようです。

そこで、指定時間中、赤外線LEDをずっと光らせるのではなく、一定周期で休憩を挟み、その分エネルギー(電気)を貯めて、一気に放出するようにします。

これによって、ノイズとはっきり区別できる赤外線を放つことが出来るわけです。

そして、受信側もその周期に合わせたタイミングで確認するようにすれば、結果として同等の信号として復元、認識出来るようになります。

これが赤外線通信で「キャリア」と「サブキャリア」と呼ばれている方式の仕組みのようです。「キャリア」が送信・受信の点滅周期、「サブキャリア」が実際に送りたいデータということになります。

SB-Projectsによると今回のNikonの場合、38kHzのキャリア波で送るそうなので、

1000000(1秒*1000ミリ秒*1000マイクロ秒) ÷ 38000Hz = 26.31…

大体、26マイクロ秒ごとにオンオフが繰り返された上でサブキャリアを送信できれば、命令信号として機能することになります。

そこで、切替え尺を配列に入れて、ループの中で一定周期ごとにキャリア用点滅を繰り返させます。

unsigned short ShutterDATA[7][7] = {
  {2000 , 1},
  {28000, 0},
  {400  , 1},
  {1580 , 0},
  {400  , 1},
  {3580 , 0},
  {400  , 1}
};

ただし、Arduinoの関数は、1つの命令だけで数マイクロ秒を使ってしまうものもあり、それを考慮した時間で、キャリア用delayを挟む必要があります。

なんですが、都合よく、ERESTAGEさんの記事で、その尺を記してくれています。

// 赤外線の各点滅時間分繰り返し
for (byte i = 0 ; i < 7 ; i++)
{
  // データ1つ分の始めた時間を取得
  unsigned long time_bit = micros();

  // 始めた時間からデータの指定時間になるまで繰り返し
  while ((micros() - time_bit) <= ShutterDATA[i][0])
  {
    // 配列の設定に沿ってLEDを点灯・消灯
    digitalWrite(IRLED, ShutterDATA[i][1]);
    // キャリア周期用待ち時間1
    delayMicroseconds(5);
    // キャリア周期用にオフへ
    digitalWrite(IRLED, 0);
    // キャリア周期用待ち時間2
    delayMicroseconds(10);
  }
}

上述の理屈から言えば、休憩時間の多い方が距離が伸びるはずなので、参考のdelay時間を少し変えてみました(結果に結びついているかは謎)。while文内の1周が約26マイクロ秒になっている(と思われます)算段です。

以上を踏まえた結果が、前述のサンプルスケッチになります。

最後に

ここまで書いておいてなんですが、この類のリモコン、Amazonだとかなり安価で手に入ります。回路以外のコストと労力を考えると、全然割に合わないです。というか、純正自体がそんな高いものでもないので、安物買いの銭失いが嫌なら、素直にNikon製を購入したほうがいいですね。

ただ、赤外線通信の仕組みを知るという事であれば、凄い良い教材ではないかと…。

なんて、まとめてしまうのも寂しいので、サンプルスケッチへ「タイマー機能」をつけたスケッチを作ってみました。ただし、赤外線の発信を遅らせているだけなので、赤外線LEDはカメラに向け続けないとダメですが…。

スケッチ Arduino de Nikon IR Shutter

回路図は上述と同じです。ただ、操作用として13ピンのLEDは必須になります。

操作法

スイッチを押すと、すぐシャッターを切ります。

長押しし続けるとLEDがゆっくり点滅します。離すと3秒間LEDが点灯するので、その間に指定したい秒数分スイッチを押します。その後LEDが秒数分タイマー点滅し、シャッターを切ります。

*このスケッチは「B2CS ライブラリ」を使用しています。

#include <B2CS.h>

#define LED    13
#define IRLED  3
#define SW     8
#define DATA_SIZE 8

boolean led_status;
static unsigned long time_led;

B2CS nik;

void setup() {
  pinMode(LED, OUTPUT);
  pinMode(IRLED, OUTPUT);
  nik.setSW(SW, INPUT_PULLUP, 0);
  nik.GO();
}

void loop() {
  byte sw_status = nik.read(SW);

  // shutter timer command
  if (sw_status == SW_RELEASED_LONG)
  {
    SHUTTER_TIMER_GO();
    delay(50);

    // simply shutter command
  } else if (sw_status == SW_RELEASED) {
    LED_FLICKER(6, 50);
    SHUTTER_GO();
    delay(50);

  } else if (sw_status == SW_PUSHED_LONG) {
    if ((millis() - time_led) > 500)
    {
      led_status = !led_status;
      time_led   = millis();
    }

  } else if (sw_status == SW_PUSHED) {
    led_status = 1;
    digitalWrite(LED, HIGH);

  } else {
    led_status = 0;
    led_status = false;
  }
  digitalWrite(LED, led_status);
}


void SHUTTER_TIMER_GO() {
  byte push_to_time;
  led_status = 1;
  digitalWrite(LED, led_status);


  // get interval time from pushing

  unsigned long time_get_val  = millis();
  unsigned long time_led_push = millis();

  while ((millis() - time_get_val) < 3000)
  {
    byte sw_status = nik.read(SW);
    if (sw_status >= SW_RELEASED)
    {
      time_led_push = millis() + 50;
      push_to_time++;
    }

    led_status = (time_led_push > millis()) ? 0 : 1;
    digitalWrite(LED, led_status);
  }


  // waiting until shutter time

  unsigned long timer_wait = millis();
  bool cancel = false;
  led_status = 0;

  nik.read(SW);  // reset switch status

  while ((millis() - timer_wait) < (push_to_time * 1000))
  {
    // cancel function
    byte cancel_sw = nik.read(SW);
    if (cancel_sw == SW_RELEASED_LONG)
    {
      cancel = true;
      break;
    }

    if (((millis() - timer_wait) % 1000) < 10) LED_FLICKER(4, 100);
    led_status = 0;
    digitalWrite(LED, led_status);
  }

  // shutter task
  if (cancel == 0)
  {
    LED_FLICKER(10, 50);
    SHUTTER_GO();
  }
}

void LED_FLICKER(byte times, byte dur) {
  for (byte i = 0 ; i < times ; i++)
  {
    digitalWrite(LED, i % 2);
    delay(dur);
  }
}

unsigned short ShutterDATA[DATA_SIZE] = {
  2000, 28000, 400, 1580, 400, 3580, 400, 63200
};

void SHUTTER_GO() {
  led_status = 1;
  digitalWrite(IRLED, led_status);   // IR LED off forcely
  delay(50);

  // send total IR Signal 2 times
  for (byte repeat = 0 ; repeat < 2 ; repeat++)
  {
    // for each IR Pulse duration
    for (byte i = 0 ; i < DATA_SIZE ; i++)
    {
      // get start time
      unsigned long time_bit = micros();

      // repeat during its duration
      while ((micros() - time_bit) <= ShutterDATA[i])
      {
        digitalWrite(IRLED, !(i % 2));
        delayMicroseconds(4);
        digitalWrite(IRLED, 0);
        delayMicroseconds(11);
      }
    }
  }
  led_status = 0;
  digitalWrite(IRLED, led_status); // IR LED off forcely
}

多少は自作する意味が出てくるかな…

参考サイト

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください