Friday, February 4, 2011

Arduino powered simple LANC remote

This is a follow up on the previous post on starting video recording using the LANC port.



The Arduino sketch below let's you send the REC command, two ZOOM and two FOCUS commands. The LANC commands are arrays containing the bit values for byte 0 and byte 1 of the specific command. All switches should be push buttons.

/*
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 
http://controlyourcamera.blogspot.com/2011/02/arduino-controlled-video-recording-over.html
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
}

35 comments:

  1. Great! thanks!

    Tested it with a Sony TRV 900 E worked right away.

    Cheers,

    Lourenço

    ReplyDelete
  2. Hi -- Just tested your code with a Sony HDR-CX190 and the interface schematic as presented. Zooms and Record worked beautifully on the first try. Focus options did not. I assume this is a camera specific "feature" although it does not have a "real" manual/auto button -- no reason to think it is related to your code. If I fix this I'll let you know. Also, out of curiosity I tweaked the cmdRepeatCount. On my camera it seemed to work 100% down to cmdRepeatCount<2. Failed 100% at <1; so once is clearly not enough, but various cameras may not need all 5 repeats (but there may well be no advantage in reducing the count from 5). Anyway, GREAT JOB -- THANKS!!!

    ReplyDelete
  3. Thanks for posting this. Your code is well written and very helpful. I am having some difficulties with my HDR SR 12, but nothing that seems insurmountable. I'm wondering. You recommended playing with delayMicroseconds() values; should I adjust only line 156, or others? Thanks again for all of your hard work.

    ReplyDelete
  4. Hi Bangalore, I mean experimenting with the bitDuration variable. because writing to the digital port takes *about* 8 microseconds so only 96 microseconds are left for each bit. Therefore the line bitDuration = bitDuration - 8;

    ReplyDelete
  5. Thanks. Still no luck. I'm wondering if it is because my camera has a D style connector. In order to use LANC, a 100k resistor must be put in between pins 7 (Func control) and pin 3 (GND). LANC works fine with a board I made using designs from http://plazma.kapsi.fi/diy/lanc_controller/index.html
    But, I want to get this working with Arduino so as to do some more complex things. Right now, the camera powers up the board, so the resistor is doing its job, but the LANC signal itself seems to be doing nothing. I've played with the bitDuration from 4 to around 15, but still no dice. Any ideas?

    ReplyDelete
  6. Thanks, this worked great! Do you know if this will work with the 3.3v Arduino Fio? Can I use the same transistor and resistors?

    ReplyDelete
  7. Hi, I'm trying to connect 2 LANC inputs to the Arduino without using the LANC to power the Arduino.

    I assume that would make the circuit easier however I'm not sure whether I can just ignore the +5-8v from the LANC cable.

    The coding I can handle, but the only H/W examples I can find are for a single connection that powers the Arduino.

    Any help would be greatly appreciated.

    ReplyDelete
  8. This worked great on both an old Sony DSR-PD170 and newer Canon XF105.

    ReplyDelete
  9. Dear Martin,
    Your code works fine with a Canon HF G30 as well. Thanks for publishing!
    I tried to control my video camera for capturing still photos. However, I failed. I tested commands like 0x18 0x39, 0x18 0x2B, 0x1E 0x58 and others. My camera does not react. Did you ever test this feature?
    Best Regards,
    Urs

    ReplyDelete
  10. Dear Urs, thanks for the update. Sorry but I haven't tried contolling still photo shooting.

    ReplyDelete
  11. Hi, can you make a rc-remote control with the LANC Remote controller?
    Best Regards
    Mike

    ReplyDelete
  12. Hi, can you make a rc-remote control with your LANC Remote controller?
    Best Regards
    Mike

    ReplyDelete
  13. Hi, can you make a rc-remote control for a SONY A5100 with your LANC Remote controller?
    Best Regards
    Mike

    ReplyDelete
    Replies
    1. i doubt it, i have a A5100 with a cheap lanc controller and nothing responds!

      Delete
  14. Any idea if it would be possible to control the absolute zoom position? Maybe by counting the zoom commands?

    thanks
    Wolfgang R.

    ReplyDelete
  15. Great code!

    Anyone know how i can add a RECORD status LED?

    Thanks

    ReplyDelete
  16. Add a LED and a resistor to PIN13 as described here https://www.arduino.cc/en/Tutorial/Blink

    Then make this change to the code:

    Above setup() write:

    boolean toggle = true;

    In loop() add:

    if (!digitalRead(recButton)) {
    lancCommand(REC);
    if (toggle) {
    digitalWrite(LED_BUILTIN, HIGH);
    toggle = false
    } else {
    digitalWrite(LED_BUILTIN, LOW);
    toggle = true;
    }
    }

    ReplyDelete
    Replies
    1. I was change the codes but its not working. Can you write the codes with record led and share pls?

      Delete
  17. Yes... but i like to have it as a reading of status from the camera.

    ReplyDelete
  18. As you can see i used your code to build my quad pan tilt LANC controler....
    i trigger REC from controller and get a big RECORD on my oled when i toggle a camera...but with 4 cameras it can be confusing and not so sure if it's recording...
    That's why i like to have a real reading from camera...
    https://forums.vmix.com/default.aspx?g=posts&m=36124#post36124
    https://forums.vmix.com/default.aspx?g=posts&m=36185#post36185

    Thanks

    ReplyDelete
  19. Thank you for codes, it is work! But I have a question, how I use the zoom controller with spring return potentiometer(like on the camera)? is there a code for this ?

    ReplyDelete
  20. Thanks a lot for this great info here.

    The code works for my Sony HDR-CX305E.

    Where I didn't succeed is in getting control over
    the zoom step counter. I would like to control
    the view without using a remote monitor. Simply
    counting the commanded zoom steps.

    I played around with the cmdRepeatCount between 2 to 5
    loops. There seems no reliable way to command the step
    numbers.

    Next analysing the status bytes 2-7 was not helpful
    as well. Bytes 2-5 are fixed. Byte 6 (probably a counter)
    and byte 7 changes in the status. I've spend some time
    in finding a pattern to see the zoom stepping reported
    there. But no idea.

    Does someone has an idea if is or is not possible to
    track the absolute zoom factor?

    Thanks in advance
    Wolfgang R.

    ReplyDelete
    Replies
    1. Hi Wolfgang,

      Did you checked this link which describes the data structure ?
      http://www.boehmel.de/lanc.htm

      My problem is that I can't figure out how I can read the status bytes. Can you tell me how do you read these status bytes ? Because I'd like to put a red led when recording, and use the camera status, more reliable than a flag that remembers I've sent a record command without a stop command.

      Regards,
      Olivier.

      Delete
    2. Hi Oliver,
      I tested based on this script
      http://controlyourcamera.blogspot.com/2011/02/finding-out-lanc-remote-commands.html

      But commanding on/off was very reliable. I expect it gets harder by looking at the status.

      Here a snippet to look at the status byte

      ...
      /*************** byte 4 *******************/
      //0V after the previous stop bit means the START bit of Byte 1 is here
      delayMicroseconds(bitDuration); //wait START bit duration
      delayMicroseconds(bitDuration/2); //wait until the middle of bit 0 of byte 1

      //Note that the command bits have to be read in reverse order with the least significant, right-most bit (bit 0) first
      for (int i=7; i>-1; i--) {
      lancBit_Byte4[i] = digitalRead(lancPin); //read bits
      delayMicroseconds(bitDuration);
      }

      Serial.println("BITS [4]: ");
      for (int i=0; i<8; i++) {
      Serial.print((lancBit_Byte4[i]-1)*-1); //invert the bits
      }
      Serial.println("");
      ...

      HTH
      Wolfgang R.

      Delete
    3. Hi Wolfgang,
      Thanx for your reply. I will check that later, as I don't have much time during the week.
      Regards,
      Olivier.

      Delete
    4. Hi Wolfgang,

      I had problem with my breadbord for arduino, and I couldn't control the camera.

      I don't know where do I have to put the code you mentioned in the program, which is an exact copy of the program top above.

      Greetings and thank you for your help.

      Olivier.

      Delete
    5. Hi Olivier,

      here the code I was using.

      Wolfgang R.

      /*
      LANC SNIFFER
      Version 1.0
      Finds out LANC commands from a REMOTE
      For the interface ciruit 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".

      http://controlyourcamera.blogspot.de/2011/02/finding-out-lanc-remote-commands.html
      */

      #define lancPin 9

      int bitDuration = 104; //Duration of one LANC bit in microseconds.

      int lancBit[16];


      int lancBit_Byte2[8];
      int lancBit_Byte3[8];
      int lancBit_Byte4[8];
      int lancBit_Byte5[8];
      int lancBit_Byte6[8];
      int lancBit_Byte7[8];


      void setup() {
      Serial.begin(9600); //open the serial port
      pinMode(lancPin, INPUT); //listens to the LANC line
      delay(5000); //Wait for camera to power up completly
      bitDuration = bitDuration- 8; //Reading the digital port takes about 8 microseconds so only 96 microseconds are left for each bit
      }

      void loop() {
      readLancCommand();
      delay(1000);
      }

      void readLancCommand() {

      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
      delayMicroseconds(bitDuration/2); //wait until the middle of bit 0 of byte 0

      //Read the 8 bits of byte 0
      //Note that the command bits come in in reverse order with the least significant, right-most bit (bit 0) first
      for (int i=7; i>-1; i--) {
      lancBit[i] = digitalRead(lancPin); //read bits.
      delayMicroseconds(bitDuration);
      }

      //Byte 0 is read

      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
      delayMicroseconds(bitDuration/2); //wait until the middle of bit 0 of byte 1

      //Read the 8 bits of Byte 1
      //Note that the command bits have to be read in reverse order with the least significant, right-most bit (bit 0) first
      for (int i=15; i>7; i--) {
      lancBit[i] = digitalRead(lancPin); //read bits
      delayMicroseconds(bitDuration);
      }

      //Byte 1 is read


      //Print byte 0 and byte 1 to the Serial Monitor console of the Arduino developing environment
      Serial.println("BITS: ");
      for (int i=0; i<16; i++) {
      Serial.print((lancBit[i]-1)*-1); //invert the bits
      if (i==7) Serial.print(" ");
      }
      Serial.println("");



      /*************** byte 4 *******************/
      //0V after the previous stop bit means the START bit of Byte 1 is here
      delayMicroseconds(bitDuration); //wait START bit duration
      delayMicroseconds(bitDuration/2); //wait until the middle of bit 0 of byte 1

      //Note that the command bits have to be read in reverse order with the least significant, right-most bit (bit 0) first
      for (int i=7; i>-1; i--) {
      lancBit_Byte4[i] = digitalRead(lancPin); //read bits
      delayMicroseconds(bitDuration);
      }

      Serial.println("BITS [4]: ");
      for (int i=0; i<8; i++) {
      Serial.print((lancBit_Byte4[i]-1)*-1); //invert the bits
      }
      Serial.println("");
      }

      Delete
    6. I had to shorten the code as it was too long for posting here. You have to supply all blocks, not only _Byte4

      int lancBit_Byte2[8];
      int lancBit_Byte3[8];
      int lancBit_Byte4[8];
      int lancBit_Byte5[8];
      int lancBit_Byte6[8];
      int lancBit_Byte7[8];

      Delete
    7. Hi Wolfgang,

      I couldn't fix my board yet, but I analyzed the code you supplied.

      Why don't you make a loop to read the 8 bytes ? (i.e. for i = 0 to 7 ...) Instead of duplicating the code ? Is there a reason for that ?

      Thank you.

      Delete
  21. Hello I am from Argentina.
    I'm testing on a Cannon HF G10 and it works. I only have one problem, the zoom is not fluid, has jumps (tug); Why can this happen?
    Greetings and thanks for sharing.

    ReplyDelete
  22. Best instruction
    This work for many sony camera.
    I think for variable speed example joystick for change zoomin and zoomout button

    Btw thank's martin

    ReplyDelete
  23. Can I simly leave out the Ring part if my arduino gets powered seperatly?

    ReplyDelete
  24. I have the same question. I don't believe my BMPCC has enough current at the lanc plug to power the Arduino Uno. As JoniBoni ask, can we leave the power ring disconnected. And then power the Arduino with a 9v battery?

    ReplyDelete
    Replies
    1. You may use the usb input to power the board.

      Delete
  25. What is the iris (diafragma) "Open & Close" CODE, on Sony?

    ReplyDelete