Hello!
I'd like to build a camcorder remote control using LANC with ATTiny85. So far I had success using Arduino Uno, the following sketch and the standard LANC interface pictured below.
/*Send a Start/Sop Recording command to the LANC port of a video camera.
Tested with Sony DCR-HC45 camcorder
This code requires a simple interface see http://micro.arocholl.com
Feel free to use this code in any way you want.
Comprehensive LANC info: www.boehmel.de/lanc.htm
http://controlyourcamera.blogspot.com/2011/02/arduino-controlled-video-recording-over.html
*/
#define cmdPin 7 //pin D7
#define lancPin 11 //pin D11
#define recButton 2
#define ZIButton 3
#define ZOButton 4
#define backLight 5
#define powerOff 6
int cmdRepeatCount;
int bitDuration = 104; //Duration of one LANC bit in microseconds.
unsigned long time_now = 0;
void sendLANC(unsigned long raw_byte0, unsigned long raw_byte1){
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
for (int Bit = 0; Bit < 8; Bit++) { //send byte0
if (raw_byte0 & (unsigned long) 1 << Bit) {
//digitalWrite(cmdPin, HIGH);
PORTD |= (1 << PORTD7); // set pin D7 (cmdPin) to HIGH
delayMicroseconds(bitDuration);
} else {
//digitalWrite(cmdPin, LOW);
PORTD &= ~(1 << PORTD7); // set pin D7 (cmdPin) to LOW
delayMicroseconds(bitDuration);
}
}
//Byte 0 is written now put LANC line back to +5V
//digitalWrite(cmdPin, LOW);
PORTD &= ~(1 << PORTD7); // set pin D7 (cmdPin) to 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
for (int Bit = 0; Bit < 8; Bit++) { //send byte1
if (raw_byte1 & (unsigned long) 1 << Bit) {
//digitalWrite(cmdPin, HIGH);
PORTD |= (1 << PORTD7); // set pin D7 (cmdPin) to HIGH
delayMicroseconds(bitDuration);
} else {
//digitalWrite(cmdPin, LOW);
PORTD &= ~(1 << PORTD7); // set pin D7 (cmdPin) to LOW
delayMicroseconds(bitDuration);
}
}
//Byte 1 is written now put LANC line back to +5V
//digitalWrite(cmdPin, LOW);
PORTD &= ~(1 << PORTD7); // set pin D7 (cmdPin) to LOW
delayMicroseconds(10); //make sure to be in the stop bit before byte 2
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.*/
}
}
void setup() {
pinMode(lancPin, INPUT); //listens to the LANC line
pinMode(cmdPin, OUTPUT); //writes to the LANC line
pinMode(recButton, INPUT_PULLUP);
pinMode(ZIButton, INPUT_PULLUP);
pinMode(ZOButton, INPUT_PULLUP);
pinMode(backLight, INPUT_PULLUP); //for testing purposes
pinMode(powerOff, INPUT_PULLUP);
digitalWrite(cmdPin, LOW); //set LANC line to +5V
sendLANC(0x18, 0x5C); //Power On
//bitDuration = bitDuration - 8; //Writing to the digital port takes about 8 microseconds so only 96 microseconds are left till the end of each bit;
//direct port manipulation takes about 2 clock cycles - at 1MHz is 2 microseconds
}
void loop() {
if (!digitalRead(recButton)) {
time_now = millis();
sendLANC(0x18, 0x33); //Rec
while(millis() < time_now + 500){
//wait approx. 500 ms to debounce
}
}
else if (!digitalRead(ZIButton)) { //do not debounce!
sendLANC(0x28, 0x0E); //ZoomIn fastest speed
//sendLANC(0x28, 0x00);//ZoomIn T1 slowest, next 0x02, 0x04, 0x06
//sendLANC(0x28, 0x08);//ZoomIn T5
}
else if (!digitalRead(ZOButton)) { // do not debounce!
sendLANC(0x28, 0x1E);//ZoomOut fastest speed
//sendLANC(0x28, 0x10);//ZoomOut W1 slowest, next 0x02, 0x04, 0x06
//sendLANC(0x28, 0x18);//ZoomOut W5
}
else if (!digitalRead(backLight)) {
time_now = millis();
sendLANC(0x28, 0x51); // Backlight
while(millis() < time_now + 500){
//wait approx. 500 ms
}
}
else if (!digitalRead(powerOff)) { // Power off
time_now = millis();
sendLANC(0x18, 0x5E);
while(millis() < time_now + 500){
//wait approx. 500 ms
}
}
}
// full list of commands here http://www.boehmel.de/lanc; 0x18 0x5E power off
// keep alive LANC connection http://www-e2.ijs.si/damir.vrancic/personal/TRV900/Cameras/default.htm
// power on camera by pulling down LANC 5V to GND for more than 120ms
/*
DDRD |= (1 << DDD7); // set PORTD (digital 7) to output
PORTD |= (1 << PORTD7); // set pin D7 to HIGH
PORTD &= ~(1 << PORTD7); // set pin D7 to LOW
Arduino micros() resolution is 4 microseconds
*/
My issue is that I have available for buttons only 3 pins on ATTiny85 because the standard LANC interface uses 2 pins, one for input and one for output (Reset, VCC, GND out of 8 pins).
I found the following comment (credit user DC42) “The interface circuitry is more complicated than necessary. All that is needed is one I/O pin with the 4K7 pullup to +5V and a 100 ohm series resistor to Lanc signal. The open-collector output can be emulated by switching the pin mode (or data direction register) to Output to send a Low, and Input to send a High.”
By using a single pin on ATTiny85 to communicate with the camcorder instead of 2 pins, I could have all the needed pins (Rec, Zoom In, Zoom Out, Shutdown). I tried to draw a schematic from DC42’s comment – image below.
Please advise on what components to use and how to place them on this single wire LANC interface to MCU, to avoid damaging the camcorder. I have already modified the Arduino sketch to work on a single wire – see below – but I can’t test it until I can get some feedback/confirmation from Arduino community that the schematics for the one wire LANC to MCU hardware interface is good.
/*Send a Start/Sop Recording command to the LANC port of a video camera.
Tested with a Sony DCR-HC45 camcorder
This code requires a simple interface see http://micro.arocholl.com
Feel free to use this code in any way you want.
Comprehensive LANC info: www.boehmel.de/lanc.htm
http://controlyourcamera.blogspot.com/2011/02/arduino-controlled-video-recording-over.html
*/
#define lancPin 7 //LANC data, input and output
#define recButton 2
#define ZIButton 3
#define ZOButton 4
#define backLight 5
#define powerOff 6
int cmdRepeatCount;
int bitDuration = 104; //Duration of one LANC bit in microseconds.
unsigned long time_now = 0;
void sendLANC(unsigned long raw_byte0, unsigned long raw_byte1){
cmdRepeatCount = 0;
while (cmdRepeatCount < 5) { //repeat 5 times to make sure the camera accepts the command
// set lancPin (D7) as Input
DDRD &= ~(1 << DDD7);
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
// set lancPin (D7) as Output
DDRD |= (1 << DDD7);
delayMicroseconds(bitDuration); //wait START bit duration
for (int Bit = 0; Bit < 8; Bit++) { //send byte0
if (raw_byte0 & (unsigned long) 1 << Bit) {
//digitalWrite(cmdPin, HIGH);
PORTD |= (1 << PORTD7); // set pin D7 (lancPin) to HIGH
delayMicroseconds(bitDuration);
} else {
//digitalWrite(cmdPin, LOW);
PORTD &= ~(1 << PORTD7); // set pin D7 (lancPin) to LOW
delayMicroseconds(bitDuration);
}
}
//Byte 0 is written now put LANC line back to +5V
//digitalWrite(cmdPin, LOW);
PORTD &= ~(1 << PORTD7); // set pin D7 (lancPin) to LOW
//set lancPin (D7) as Input
DDRD &= ~(1 << DDD7);
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
// set lancPin (D7) as Output
DDRD |= (1 << DDD7);
delayMicroseconds(bitDuration); //wait START bit duration
for (int Bit = 0; Bit < 8; Bit++) { //send byte1
if (raw_byte1 & (unsigned long) 1 << Bit) {
//digitalWrite(cmdPin, HIGH);
PORTD |= (1 << PORTD7); // set pin D7 (lancPin) to HIGH
delayMicroseconds(bitDuration);
} else {
//digitalWrite(cmdPin, LOW);
PORTD &= ~(1 << PORTD7); // set pin D7 (lancPin) to LOW
delayMicroseconds(bitDuration);
}
}
//Byte 1 is written now put LANC line back to +5V
//digitalWrite(cmdPin, LOW);
PORTD &= ~(1 << PORTD7); // set pin D7 (lancPin) to LOW
//set lancPin as Input
DDRD &= ~(1 << DDD7); // lancPin Input
delayMicroseconds(10); //make sure to be in the stop bit before byte 2
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.*/
}
}
void setup() {
pinMode(lancPin, INPUT); //listens to the LANC line
PORTD &= ~(1 << PORTD7); // set lancPin to LOW - no INPUT_PULLUP
pinMode(recButton, INPUT_PULLUP);
pinMode(ZIButton, INPUT_PULLUP);
pinMode(ZOButton, INPUT_PULLUP);
pinMode(backLight, INPUT_PULLUP); //for testing purposes
pinMode(powerOff, INPUT_PULLUP);
sendLANC(0x18, 0x5C); //Power On
//bitDuration = bitDuration - 8; //Writing to the digital port takes about 8 microseconds so only 96 microseconds are left till the end of each bit
//direct port manipulation takes about 2 clock cycles - at 1MHz is 2 microseconds
}
void loop() {
if (!digitalRead(recButton)) {
time_now = millis();
sendLANC(0x18, 0x33); //Rec
while(millis() < time_now + 500){
//wait approx. 500 ms to debounce
}
}
else if (!digitalRead(ZIButton)) { //do not debounce!
sendLANC(0x28, 0x0E); //ZoomIn fastest speed
//sendLANC(0x28, 0x00);//ZoomIn T1 slowest, next 0x02, 0x04, 0x06
//sendLANC(0x28, 0x08);//ZoomIn T5
}
else if (!digitalRead(ZOButton)) { // do not debounce!
sendLANC(0x28, 0x1E);//ZoomOut fastest speed
//sendLANC(0x28, 0x10);//ZoomOut W1 slowest, next 0x02, 0x04, 0x06
//sendLANC(0x28, 0x18);//ZoomOut W5
}
else if (!digitalRead(backLight)) {
time_now = millis();
sendLANC(0x28, 0x51); // Backlight
while(millis() < time_now + 500){
//wait approx. 500 ms
}
}
else if (!digitalRead(powerOff)) {
time_now = millis();
sendLANC(0x18, 0x5E); // Power off
while(millis() < time_now + 500){
//wait approx. 500 ms
}
}
}
// full list of commands here http://www.boehmel.de/lanc; 0x18 0x5E power off
// keep alive LANC connection http://www-e2.ijs.si/damir.vrancic/personal/TRV900/Cameras/default.htm
// power on camera by pulling down LANC 5V to GND for more than 120ms
/*
DDRD |= (1 << DDD7); // set PORTD (digital 7) to output
DDRD &= ~(1 << DDD7); // set PORTD (digital 7) to input
PORTD |= (1 << PORTD7); // set pin D7 to HIGH
PORTD &= ~(1 << PORTD7); // set pin D7 to LOW
Arduino micros() resolution is 4 microseconds
*/
If I make a mistake in the sketch, and the MCU sends bytes over the data line while the camcorder also sends bytes will there be any risk to fry the camcorder? What if the next camcorder will output more than 5V on the LANC power line?
Any advice is highly appreciated.
PS. The 2 wires LANC communication code ported to ATTiny85. Not tested yet.
//Modified IR Remote Wand for LANC transmission
//The MCU is powered by LANC +5V line.
#include <avr/io.h>
// Buttons
const int Rec = 3; // Rec - Play
const int S3 = 5; // Reset - Mode
const int ZIn = 2; //ZoomIn - Stop
const int ZOu = 4; //ZoomOut - Rewind
const int LancIn = 0; // LANC input on PB0
const int LancOut = 1; // LANC output on PB1
const int BitDuration = 104;
unsigned long time_now = 0;
void sendLANC(unsigned long raw_code1, unsigned long raw_code2) {
int cmdRepeatCount;
cmdRepeatCount = 0;
while (cmdRepeatCount < 5) { //repeat 5 times to make sure the camera accepts the command
while (pulseIn(LancIn, 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 on LancPin after long pause means the START bit of Byte 0 is here
delayMicroseconds(BitDuration); //wait START bit duration
for (int Bit = 0; Bit<8; Bit++) {
if (raw_code1 & (unsigned long) 1<<Bit) {
//digitalWrite(LancOut, HIGH);
PORTB |= (1<<PB1); //set PB1 to HIGH
delayMicroseconds(BitDuration);
} else {
//digitalWrite(LancOut, LOW);
PORTB &= ~(1<<PB1); //set PB1 to LOW
delayMicroseconds(BitDuration);
}
}
//Byte 0 is written, now put LANC line back to +5V
//digitalWrite(LancOut, LOW);
PORTB &= ~(1<<PB1); //set PB1 to LOW
delayMicroseconds(10); //make sure to be in the stop bit before byte 1
while (digitalRead(LancIn == HIGH)) {
//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
// sending Byte 1
for (int Bit = 0; Bit<8; Bit++) {
if (raw_code2 & (unsigned long) 1<<Bit) {
//digitalWrite(LancOut, HIGH);
PORTB |= (1<<PB1); //set PB1 to HIGH
delayMicroseconds(BitDuration);
} else {
//digitalWrite(LancOut, LOW);
PORTB &= ~(1<<PB1); //set PB1 to LOW
delayMicroseconds(BitDuration);
}
}
//Byte 1 is written now put LANC line back to +5V
//digitalWrite(LancOut, LOW);
PORTB &= ~(1<<PB1); //set PB1 to 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.*/
}
}
void setup() {
OSCCAL = 100; // valid only for the previous MCU
pinMode(LancOut, OUTPUT); //LANC output
pinMode(LancIn, INPUT);
pinMode(Rec, INPUT_PULLUP);
pinMode(S3, INPUT_PULLUP); // no floating Reset
pinMode(ZIn, INPUT_PULLUP);
pinMode(ZOu, INPUT_PULLUP);
//digitalWrite(LancOut, LOW); //set LANC line to +5V
PORTB &= ~(1<<PB1); //set PB1 to LOW
sendLANC(0x18, 0x5C); //Power On
}
void loop() {
if (!digitalRead(Rec)) {
time_now = millis();
sendLANC(0x18, 0x33); //Rec
while(millis() < time_now + 500){
//wait approx. 500 ms to debounce
}
}
else if (!digitalRead(ZIn)) { //do not debounce!
sendLANC(0x28, 0x0E); //ZoomIn fastest speed
//sendLANC(0x28, 0x00);//ZoomIn T1 slowest, next 0x02, 0x04, 0x06
//sendLANC(0x28, 0x08);//ZoomIn T5
}
else if (!digitalRead(ZOu)) { // do not debounce!
sendLANC(0x28, 0x1E);//ZoomOut fastest speed
//sendLANC(0x28, 0x10);//ZoomOut W1 slowest, next 0x02, 0x04, 0x06
//sendLANC(0x28, 0x18);//ZoomOut W5
}
}
/*
Port manipulation example
DDRB|=(1<<DDB1) equivalent to pinMode(PB1, Output)
DDRB&=~(1<<DDB1) equivalent to pinMode(PB1, Input)
PORTB|=(1<<PB1) equivalent to digitalWrite(PB1, HIGH)
PORTB&=~(1<<PB1) equivalent to digitalWrite(PB1, LOW)
*/