Arduino で LANC コントロール
ArduinoでLANCのキーワードで当ブログに来られる方が多いのですが、知りたいところにたどり着いて無いと思いますので、きちんとまとめてみました。(MP-101 コントロールのシリーズ中にバラバラ書いてあるのですが、余りきちんと書いてませんでした。)
LANCの説明
LANCは簡単に説明すると昔SONYが作ったVTR用のコントロール用の規格。Canonも一時期使用していた。Panasonic も使っていたらしいが、私は見たことがありません。Blackmagicのカメラにも採用されていました。Victor(JVC)も業務機を中心に、リモート端子という名称で非公式?ながらLANCで動作するようになっています。このリモート端子に接続する機器のJVC推奨コントローラが実はLANC機器なので大丈夫でしょう。最近ではスマホのコントロールが当たり前のように業務用機器にも搭載されていますが、安定性は断然LANCの方が優れていると思います。ただし2.5mmの3極端子を使用しているため、若干その部分の安定性に欠けますので、がっつり業務用というわけではなく家庭用やハイアマチュア用の機材に使用されていました。
規格の詳しい説明はかなり古い素材ですがHow SONY’s LANC(tm) protocol worksが詳しいです。
LANCはどうやって使用するのか
主に専用のコントローラーを使います。amazonなどではお安く売られています。
こういったものを使用するのが一番簡単です、距離を離したい場合は3端子のステレオプラグなどの延長で伸ばすことも可能です。私は自作2.5mmプラグの延長で約10m程度延長させて使用してましたが、特に安定性では気になりませんでした。かなり古くからある規格でかなり余裕を持っているのだろうと思います。
さて、この手の製品ではREC ON-OFF と可変ズーム位しか利用できないのですが、LANCの規格自体にはもう少し機能があったり、独自拡張などされています。具体的には AF/MF 切り替え MF時のマニュアルフォーカス。絞り AUTO/MANUAL 手動でも絞り調整、JVCの場合はカメラ独自のオリジナルアサインボタンの割り当てなどが使用できます。リモコン的に使用するためにはもっとボタンやコントロール機能が多い装置が必要になります。昔SONYが多機能のLANCリモコンを販売しておりましたが、現在入手は困難と思います。
そこで自らの手で作ってしまおうというわけです。たまたま私は電動雲台もコントロールしたかったためワイヤレス化もしましたが、実は別途有線用のLANCリモコンも作って使用してます。
専用のICもあるにはありますが、これもかなり入手困難です。私は一個だけ興味でカナダから輸入してみましたが結局使っていません。
LANCをArduinoでコントロールする技術を発表した最初の人?
結構昔にArduinoでコントロールする回路とプロトコルを発表している方がいて、以後LANCとarduinoで検索すると、ほぼその回路とスケッチが出てきます。
私が探した中で一番古いのは http://controlyourcamera.blogspot.com/2011/02/arduino-controlled-video-recording-over.html
http://controlyourcamera.blogspot.com/2011/02/arduino-powered-lanc-remote.html
2011年の記事ですがこの記事が始まりのようです。それ以降ほぼ皆さんこれを使用している感じですね。
スケッチはオリジナルを同ページより転載しておきます。フリーで使って良いよと言っていただいており感謝です。だからこればっかりが流通しているのかもしれませんね。
/*
SIMPLE LANC REMOTE
Version 1.0
Sends LANC commands to the LANC port of a video camera.
Tested with a Canon XF300 camcorder
For the interface circuit interface see
Feel free to use this code in any way you want.
2011, Martin Koch
"LANC" is a registered trademark of SONY.
CANON calls their LANC compatible port "REMOTE".
*/
#define cmdPin 7
#define lancPin 11
#define recButton 6
#define zoomOutButton 5
#define zoomInButton 4
#define focusNearButton 3
#define focusFarButton 2
int cmdRepeatCount;
int bitDuration = 104; //Duration of one LANC bit in microseconds.
//LANC commands byte 0 + byte 1
//Tested with Canon XF300
//Start-stop video recording
boolean REC[] = {LOW,LOW,LOW,HIGH,HIGH,LOW,LOW,LOW, LOW,LOW,HIGH,HIGH,LOW,LOW,HIGH,HIGH}; //18 33
//Zoom in from slowest to fastest speed
boolean ZOOM_IN_0[] = {LOW,LOW,HIGH,LOW,HIGH,LOW,LOW,LOW, LOW,LOW,LOW,LOW,LOW,LOW,LOW,LOW}; //28 00
boolean ZOOM_IN_1[] = {LOW,LOW,HIGH,LOW,HIGH,LOW,LOW,LOW, LOW,LOW,LOW,LOW,LOW,LOW,HIGH,LOW}; //28 02
boolean ZOOM_IN_2[] = {LOW,LOW,HIGH,LOW,HIGH,LOW,LOW,LOW, LOW,LOW,LOW,LOW,LOW,HIGH,LOW,LOW}; //28 04
boolean ZOOM_IN_3[] = {LOW,LOW,HIGH,LOW,HIGH,LOW,LOW,LOW, LOW,LOW,LOW,LOW,LOW,HIGH,HIGH,LOW}; //28 06
boolean ZOOM_IN_4[] = {LOW,LOW,HIGH,LOW,HIGH,LOW,LOW,LOW, LOW,LOW,LOW,LOW,HIGH,LOW,LOW,LOW}; //28 08
boolean ZOOM_IN_5[] = {LOW,LOW,HIGH,LOW,HIGH,LOW,LOW,LOW, LOW,LOW,LOW,LOW,HIGH,LOW,HIGH,LOW}; //28 0A
boolean ZOOM_IN_6[] = {LOW,LOW,HIGH,LOW,HIGH,LOW,LOW,LOW, LOW,LOW,LOW,LOW,HIGH,HIGH,LOW,LOW}; //28 0C
boolean ZOOM_IN_7[] = {LOW,LOW,HIGH,LOW,HIGH,LOW,LOW,LOW, LOW,LOW,LOW,LOW,HIGH,HIGH,HIGH,LOW}; //28 0E
//Zoom out from slowest to fastest speed
boolean ZOOM_OUT_0[] = {LOW,LOW,HIGH,LOW,HIGH,LOW,LOW,LOW, LOW,LOW,LOW,HIGH,LOW,LOW,LOW,LOW}; //28 10
boolean ZOOM_OUT_1[] = {LOW,LOW,HIGH,LOW,HIGH,LOW,LOW,LOW, LOW,LOW,LOW,HIGH,LOW,LOW,HIGH,LOW}; //28 12
boolean ZOOM_OUT_2[] = {LOW,LOW,HIGH,LOW,HIGH,LOW,LOW,LOW, LOW,LOW,LOW,HIGH,LOW,HIGH,LOW,LOW}; //28 14
boolean ZOOM_OUT_3[] = {LOW,LOW,HIGH,LOW,HIGH,LOW,LOW,LOW, LOW,LOW,LOW,HIGH,LOW,HIGH,HIGH,LOW}; //28 16
boolean ZOOM_OUT_4[] = {LOW,LOW,HIGH,LOW,HIGH,LOW,LOW,LOW, LOW,LOW,LOW,HIGH,HIGH,LOW,LOW,LOW}; //28 18
boolean ZOOM_OUT_5[] = {LOW,LOW,HIGH,LOW,HIGH,LOW,LOW,LOW, LOW,LOW,LOW,HIGH,HIGH,LOW,HIGH,LOW}; //28 1A
boolean ZOOM_OUT_6[] = {LOW,LOW,HIGH,LOW,HIGH,LOW,LOW,LOW, LOW,LOW,LOW,HIGH,HIGH,HIGH,LOW,LOW}; //28 1C
boolean ZOOM_OUT_7[] = {LOW,LOW,HIGH,LOW,HIGH,LOW,LOW,LOW, LOW,LOW,LOW,HIGH,HIGH,HIGH,HIGH,LOW}; //28 1E
//Focus control. Camera must be switched to manual focus
boolean FOCUS_NEAR[] = {LOW,LOW,HIGH,LOW,HIGH,LOW,LOW,LOW, LOW,HIGH,LOW,LOW,LOW,HIGH,HIGH,HIGH}; //28 47
boolean FOCUS_FAR[] = {LOW,LOW,HIGH,LOW,HIGH,LOW,LOW,LOW, LOW,HIGH,LOW,LOW,LOW,HIGH,LOW,HIGH}; //28 45
boolean FOCUS_AUTO[] = {LOW,LOW,HIGH,LOW,HIGH,LOW,LOW,LOW, LOW,HIGH,LOW,LOW,LOW,LOW,LOW,HIGH}; //28 41
//boolean POWER_OFF[] = {LOW,LOW,LOW,HIGH,HIGH,LOW,LOW,LOW, LOW,HIGH,LOW,HIGH,HIGH,HIGH,HIGH,LOW}; //18 5E
//boolean POWER_ON[] = {LOW,LOW,LOW,HIGH,HIGH,LOW,LOW,LOW, LOW,HIGH,LOW,HIGH,HIGH,HIGH,LOW,LOW}; //18 5C Doesn't work because there's no power supply from the LANC port when the camera is off
//boolean POWER_OFF2[] = {LOW,LOW,LOW,HIGH,HIGH,LOW,LOW,LOW, LOW,LOW,HIGH,LOW,HIGH,LOW,HIGH,LOW}; //18 2A Turns the XF300 off and then on again
//boolean POWER_SAVE[] = {LOW,LOW,LOW,HIGH,HIGH,LOW,LOW,LOW, LOW,HIGH,HIGH,LOW,HIGH,HIGH,LOW,LOW}; //18 6C Didn't work
void setup() {
pinMode(lancPin, INPUT); //listens to the LANC line
pinMode(cmdPin, OUTPUT); //writes to the LANC line
pinMode(recButton, INPUT); //start-stop recording button
digitalWrite(recButton, HIGH); //turn on an internal pull up resistor
pinMode(zoomOutButton, INPUT);
digitalWrite(zoomOutButton, HIGH);
pinMode(zoomInButton, INPUT);
digitalWrite(zoomInButton, HIGH);
pinMode(focusNearButton, INPUT);
digitalWrite(focusNearButton, HIGH);
pinMode(focusFarButton, INPUT);
digitalWrite(focusFarButton, HIGH);
digitalWrite(cmdPin, LOW); //set LANC line to +5V
delay(5000); //Wait for camera to power up completly
bitDuration = bitDuration - 8; //Writing to the digital port takes about 8 microseconds so only 96 microseconds are left for each bit
}
void loop() {
if (!digitalRead(recButton)) {
lancCommand(REC);
}
if (!digitalRead(zoomOutButton)) {
lancCommand(ZOOM_OUT_4);
}
if (!digitalRead(zoomInButton)) {
lancCommand(ZOOM_IN_4);
}
if (!digitalRead(focusNearButton)) {
lancCommand(FOCUS_NEAR);
}
if (!digitalRead(focusFarButton)) {
lancCommand(FOCUS_FAR);
}
}
void lancCommand(boolean lancBit[]) {
cmdRepeatCount = 0;
while (cmdRepeatCount < 5) { //repeat 5 times to make sure the camera accepts the command
while (pulseIn(lancPin, HIGH) < 5000) {
//"pulseIn, HIGH" catches any 0V TO +5V TRANSITION and waits until the LANC line goes back to 0V
//"pulseIn" also returns the pulse duration so we can check if the previous +5V duration was long enough (>5ms) to be the pause before a new 8 byte data packet
//Loop till pulse duration is >5ms
}
//LOW after long pause means the START bit of Byte 0 is here
delayMicroseconds(bitDuration); //wait START bit duration
//Write the 8 bits of byte 0
//Note that the command bits have to be put out in reverse order with the least significant, right-most bit (bit 0) first
for (int i=7; i>-1; i--) {
digitalWrite(cmdPin, lancBit[i]); //Write bits.
delayMicroseconds(bitDuration);
}
//Byte 0 is written now put LANC line back to +5V
digitalWrite(cmdPin, LOW);
delayMicroseconds(10); //make sure to be in the stop bit before byte 1
while (digitalRead(lancPin)) {
//Loop as long as the LANC line is +5V during the stop bit
}
//0V after the previous stop bit means the START bit of Byte 1 is here
delayMicroseconds(bitDuration); //wait START bit duration
//Write the 8 bits of Byte 1
//Note that the command bits have to be put out in reverse order with the least significant, right-most bit (bit 0) first
for (int i=15; i>7; i--) {
digitalWrite(cmdPin,lancBit[i]); //Write bits
delayMicroseconds(bitDuration);
}
//Byte 1 is written now put LANC line back to +5V
digitalWrite(cmdPin, LOW);
cmdRepeatCount++; //increase repeat count by 1
/*Control bytes 0 and 1 are written, now don’t care what happens in Bytes 2 to 7
and just wait for the next start bit after a long pause to send the first two command bytes again.*/
}//While cmdRepeatCount < 5
}
注意点
私もこの回路で数機作成してますが、一点だけ注意を
int bitDuration = 104; //Duration of one LANC bit in microseconds.
bitDuration = bitDuration – 8; //Writing to the digital port takes about 8 microseconds so only 96 microseconds are left for each bit
使用するarduino CPUに応じてこの差分のところ(-8)を変更する必要あります。bitDuration = 104;で擬似的にクロックを作っているわけですが、ポートを設定するために8microseconds かかるのでその分引いてありますよ。と言うわけで。SEEED XAIOはこの係数で動きましたが、M5STAMP PICO では正常に動作しませんでした。ちなみにおそらくSEEED XAIOでは260kHzでポートは動作しましたので3か4くらいが適切だったのかと思います。遅いarduinoでは誤差範囲くらいで動いていたのでしょう。高速動作するPICOでは以前測定しましたが、number of tries: 100000 duration [us]: 12685なので1クロック送出するのに0.1268microsecondしかかりませんので0か1にすると動作します。8の場合待ち時間が少なすぎて、この時間でクロックタイミングを作っている関係上、早すぎて追従できていないようです。最初何も考えずにテストしてコマンドによって動作したりしなかったりしており、最初かなり悩みましたね。どうせなら全く動かないとかなら早く気が付いたのでしょうけど、動くコマンドがあると、別なハード的な点、電圧とか、波形がなまっているのでは等、オシロつないで確認してしまいました。
いまさらLANCを使い倒そうという人は出てこないと思いますが、参考になれば。