LANC remote control using ATTiny84a help request

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)
*/

Post the code, NOT links.

1 Like

Sorry.Fixed.

I am wandering... what's the point of doing this on 8pin attiny?

If you need a small size, take atmega in TQFP32 package, it is even smaller than tiny85 in DIP8

I'm not good at drawing tiny traces and at soldering very tiny legs. I can handle a DIP package of 8 pins though.
The ATTiny85 version is not working. What am I missing? The MCU has its internal oscillator calibrated and running at 1MHz.

Sorry, did you say you used the revised one-pin LANC successfully on the UNO?

Oh, never mind, I see you don't wanna break anything, good attitude.

If you look carefully at the two pin arrangement, you will see that the output pin can only ground the signal line.

So if you are careful, this will work. Just never ever let the pin be in output mode and HIGH.

// for input, just set the mode, and use digitslRead:
    pinMode(thePin, INPUT);


// for output NOT

// pinMode(thePin, OUTPUT);
// digitalWrite(thePin, theValue);

// rather

    if (theValue == LOW) {
      digitalWrite(thePin, LOW);
      pinMode(thePin, OUTPUT;
    }
    else {
      pinMode(thePin, INPUT);  // let pin be pulled high externally
    }

It will be easier testing it, and TBH doing as much development as possible on a regular Arduino board. Shrinkifying and making a teeny little PCB should be among the very last steps when there are no more questions or surprises.

I'd write a function

void writeLANC(int thePin, int theValue)
{
    if (theValue == LOW) {
      digitalWrite(thePin, LOW);
      pinMode(thePin, OUTPUT;
    }
    else {
      pinMode(thePin, INPUT);  // let pin be pulled high externally
    }
}

a7

For now I have issues with the 2 wire LANC on ATTiny85, I can't figure it out why it's not working. The output pin has to be HIGH for BitDuration, to send Bit1. Maybe delayMicroseconds() it's not working properly...

If you doubt the clock frequency or behaviour of delayMicroseconds(), write a sketch that simply wiggles an output pin using delays and measure the frequency.

So… you have done the two wire LANC successfully on the UNO.

You could make code easier to read if you just use regular digitalWrite() and digitalRead().

I can't see any error, but it's a kind I mightn't see maybe.

In the meantime or for a break you could try the one wire circuit using an UNO.

The 2 wire LANC on ATTiny85 code looks like it only one pin?

a7

If you are only going to press one button at a time, and one of the button pins can be used as an analog input, then you could read several pins with one analog input.
If you Google "arduino how to read buttons using one analog input" the first few results I got were all for posts in this forum.

I got my hands on ATTiny84a, with more than enough pins. No need for one wire LANC interface or a staircase of rezistors .
The problem persists, the Arduino code ported to ATTiny84a does not work. MCU clock is 8MHz, with the internal oscillator calibrated using the serial method. It seems that delayMicrosec() and millis() are not working properly. I found two related articles, by Edgar Bonnet and Johnson Davies, that use ATTiny85/ATTiny84 as receiver through USI UART. I need the opposite, to send two bytes at a certain moment.
I think that I have to use Timer/Counter 1 to generate the delays and PWM to pulse the LANC output with Bit1 (HIGH for 104 microsec) and Bit0 (LOW for 104 microsec).

/* IR Remote Wand v2
  Ported to ATTiny84a
  Set MCU at 8MHz
*/
//#warning "This is the CLOCKWISE pin mapping - make sure you're using the pinout diagram with the pins in clockwise order"
// ATMEL ATTINY84 / ARDUINO
//
//                           +-\/-+
//                     VCC  1|    |14  GND
//            (D  10)  PB0  2|    |13  PA0  (D  0)        AREF
//             (D  9)  PB1  3|    |12  PA1  (D  1)
//             (D 11)  PB3  4|    |11  PA2  (D  2)
//  PWM  INT0  (D  8)  PB2  5|    |10  PA3  (D  3)
//  PWM        (D  7)  PA7  6|    |9   PA4  (D  4)
//  PWM        (D  6)  PA6  7|    |8   PA5  (D  5)        PWM
//                           +----+

#include <avr/io.h>

volatile int count=0;
volatile unsigned int measure = 0;

// IR transmitter **********************************************

// Buttons

const int recButton = 10;  // Rec PB0
const int ZIButton = 9;  // Zoom In  PB1
const int ZOButton = 8;  // Zoom Out  PB2
const int backLight = 7;
const int powerOff = 4;
const int S7 = 3;
const int S8 = 2;
const int S9 = 1;

const int LancIn = 5; // LANC data input PA5
const int LancOut = 6;  // LANC output PA6

const int LED_state = 0;  //Blink to debug

const int BitDuration = 104;
unsigned long time_now = 0;

const int top = 103;    // 1000000/104 = 9.6kHz
const int match = 0;  // continuous signal HIGH on PA6 for OCR1A=match
// Set up Timer/Counter1 to output PCM on OC1A (PA6)
//void SetupPCM() {
  // Inverted fast PWM output on OC1A (PA6) divide by 1 with TOP value ICR1
//  TCCR1A = (1 << COM1A1) | (1 << COM1A0) | (1 << WGM11);
//  TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10);
//  ICR1 = top;   // aprox.9600Hz
//  OCR1A = top;  // Keep output low
//}

void delayuSec(unsigned long waitUsecs) { //also not working
    unsigned long startUsecs = micros();
    while (micros() - startUsecs < waitUsecs) {
  
 }
}

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
    // sending Byte 0
    for (int Bit = 0; Bit<8; Bit++) {
      if (raw_code1 & (unsigned long) 1<<Bit) {
        //digitalWrite(LancOut, HIGH);
        PORTA |= (1<<PORTA6); //set PA6 to HIGH
        delayMicroseconds(BitDuration);
        } else {
          //digitalWrite(LancOut, LOW);
          PORTA &= ~(1<<PORTA6); //set PA6 to LOW
          delayMicroseconds(BitDuration);
        }
      }
    //Byte 0 is written, now put LANC line back to +5V
    //digitalWrite(LancOut, LOW);
    PORTA &= ~(1<<PORTA6); //set PA6 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);
        PORTA |= (1<<PORTA6); //set PA6 to HIGH
        delayMicroseconds(BitDuration);
        } else {
          //digitalWrite(LancOut, LOW);
          PORTA &= ~(1<<PORTA6); //set PA6 to LOW
          delayMicroseconds(BitDuration); 
        }
    }
    //Byte 1 is written now put LANC line back to +5V
    //digitalWrite(LancOut, LOW);
    PORTA &= ~(1<<PORTA6); //set PA6 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 BlinkLed (int count) {
  for (int c = 0; c<count; c++){
    digitalWrite(LED_state, HIGH);  // turn on the LED
    while(millis() < time_now + 500){
      //wait approx. 500 ms
    }
    digitalWrite(LED_state, LOW);  //turn off the LED
    while(millis() < time_now + 500){
      //wait approx. 500 ms
    }

  }

}


void setup() {

  pinMode(LancIn, INPUT);
  pinMode(LancOut, OUTPUT);
  //digitalWrite(LancOut, LOW);
  PORTA &= ~(1<<PORTA6); //set PA6 to LOW
  pinMode(LED_state, OUTPUT);
  //SetupPCM();

  pinMode(recButton, INPUT_PULLUP); 
  pinMode(ZIButton, INPUT_PULLUP);
  pinMode(ZOButton, INPUT_PULLUP);
  pinMode(backLight, INPUT_PULLUP);
  pinMode(powerOff, INPUT_PULLUP);
  pinMode(S7, INPUT_PULLUP);
  pinMode(S8, INPUT_PULLUP);
  pinMode(S9, INPUT_PULLUP);
  pinMode(11, INPUT_PULLUP); //Reset, prevent floating pin
  digitalWrite(LED_state, LOW);
  sendLANC(0x18, 0x5C); //Power On
  BlinkLed(10);
 
}

void loop() {
  if (!digitalRead(recButton)) {
    time_now = millis();
    sendLANC(0x18, 0x33); //Rec 
    BlinkLed(3);
    while(millis() < time_now + 500){
    //wait approx. 500 ms to debounce
    }
  }
  else if (!digitalRead(ZIButton)) { //do not debounce!
    sendLANC(0x28, 0x0E); //ZoomIn fastest speed
    BlinkLed(3);
    //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
      BlinkLed(3);
      //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
    BlinkLed(3);
    while(millis() < time_now + 500){
    //wait approx. 500 ms
    }
  }
  else if (!digitalRead(powerOff)) { // Power off
    time_now = millis();
    sendLANC(0x18, 0x5E);
    BlinkLed(3);
    while(millis() < time_now + 500){
    //wait approx. 500 ms
    }
  }
  else if (!digitalRead(S9)) { // test S9
    time_now = millis();
    sendLANC(0x18, 0x33);
    BlinkLed(3);
    while(millis() < time_now + 500){
    //wait approx. 500 ms
    }
  }
}

The LED blinks once instead of 3 times, even at startup, when I should have 10 blinks.

Your BlinkLed() is doing what you wrote. Your eyes are deceiving you maybe. Or something.

If you added some printing to it, with time stamps, you might get a clue. Move the function over to the UNO if you can't debug it without the serial monitor.

# define LED_state 7

unsigned long time_now;

void BlinkLed (int count) {
  for (int c = 0; c<count; c++){
    digitalWrite(LED_state, HIGH);  // turn on the LED
    Serial.println("ON");
    while(millis() < time_now + 500){
      //wait approx. 500 ms
    }
    digitalWrite(LED_state, LOW);  //turn off the LED
    Serial.println("OFF");
    while(millis() < time_now + 500){
      //wait approx. 500 ms
    }
  }
}

void setup() {
  Serial.begin(115200);
  Serial.println("\nWTF\n");

  pinMode(LED_state, OUTPUT);
  BlinkLed(10);
}

void loop() {
  // put your main code here, to run repeatedly:
}

a7

The sketch I uploaded

# define LED_state 7
//PA1 is TX
//PA2 is RX

unsigned long time_now;

void BlinkLed (int count) {
  for (int c = 0; c<count; c++){
    digitalWrite(LED_state, HIGH);  // turn on the LED
    Serial.println("ON");
    while(millis() < time_now + 500){
      //wait approx. 500 ms
    }
    digitalWrite(LED_state, LOW);  //turn off the LED
    Serial.println("OFF");
    while(millis() < time_now + 500){
      //wait approx. 500 ms
    }
  }
}

void setup() {
  OSCCAL=119;
  Serial.begin(115200);
  Serial.println("\nWTF\n");

  pinMode(LED_state, OUTPUT);
  BlinkLed(10);
}

void loop() {
  // put your main code here, to run repeatedly:
  BlinkLed(4);
  while(millis() < time_now + 1000){
      //wait approx. 500 ms
    }
  Serial.println("\n5 Blinks 4x0.5sec + 1 sec\n");
  Serial.println(millis());
}

The LED is permanently on, no blinking
And I get in Serial Monitor only squares at baud rate 115200.

But the MCU work just fine with this sketch

/* IR Remote Wand v2 with Mode from Reset - see http://www.technoblogy.com/show?25TN
  Ported to ATTiny84a
  Set MCU at 1MHz
*/
//#warning "This is the CLOCKWISE pin mapping - make sure you're using the pinout diagram with the pins in clockwise order"
// ATMEL ATTINY84 / ARDUINO
//
//                           +-\/-+
//                     VCC  1|    |14  GND
//            (D  10)  PB0  2|    |13  PA0  (D  0)        AREF
//             (D  9)  PB1  3|    |12  PA1  (D  1)
//             (D 11)  PB3  4|    |11  PA2  (D  2)
//  PWM  INT0  (D  8)  PB2  5|    |10  PA3  (D  3)
//  PWM        (D  7)  PA7  6|    |9   PA4  (D  4)
//  PWM        (D  6)  PA6  7|    |8   PA5  (D  5)        PWM
//                           +----+
#include <avr/sleep.h>
#include <avr/io.h>
volatile int count=0;
volatile unsigned int measure = 0;

// IR transmitter **********************************************

// Buttons

const int S1 = 10;  // Rec PB0
const int S2 = 9;  // Zoom In  PB1
const int S3 = 8;  // Zoom Out  PB2
const int S4 = 7;
const int S5 = 5;

const int S6 = 4;
const int S7 = 3;
const int S8 = 2;
const int S9 = 1;

const int LED = 6;        // IR LED output PA6
const int LED_state = 0;  //Blink when change cam

ISR(PCINT1_vect) {  
  int in = PINB;
  if (((in & (1 << PINB0)) == 0) && (count == 0)) SendSony(15, 0x5C99);  //Rec on PB0 for Sony
  else if (((in & (1 << PINB0)) == 0) && (count == 1)) SendCanonP(0xFC03, 0xE383);  //Rec on PB0 for Canon
  else if (((in & (1 << PINB0)) == 0) && (count == 2)) SendPanasonic(0x94CC28, 0x702002);  //Rec on PB0 for Panasonic
  
  else if (((in & (1 << PINB1)) == 0) && (count == 0)) {  //PB1
    while (digitalRead(S2) == LOW) SendSony(15, 0x6C9A);  //ZoomIn Sony
  } 
  else if (((in & (1 << PINB2)) == 0) && (count == 0)) {  //PB2
    while (digitalRead(S3) == LOW) SendSony(15, 0x6C9B);  //ZoomOut Sony
  }
  else if (((in & (1 << PINB1)) == 0) && (count == 1)) {  //PB1
    while (digitalRead(S2) == LOW) SendCanonP(0xE31C, 0xE383);  //ZoomIn Canon
  } 
  else if (((in & (1 << PINB2)) == 0) && (count == 1)) {  //PB2
    while (digitalRead(S3) == LOW) SendCanonP(0xE21D, 0xE383);  //ZoomOut Canon
  }
  else if (((in & (1 << PINB1)) == 0) && (count == 2)) {  //PB1
    while (digitalRead(S2) == LOW) SendPanasonic(0x3A6228, 0x702002);  //ZoomIn Panasonic 
  } 
  else if (((in & (1 << PINB2)) == 0) && (count == 2)) {  //PB2
    while (digitalRead(S3) == LOW) SendPanasonic(0x3B6328, 0x702002);  //ZoomOut Panasonic
  }

}

ISR(PCINT0_vect) {  //pentru pinii din portul PA
  int ia = PINA;
  if (((ia & 1 << S4) == 0) && (count == 0)) SendSony(12, 0x3DA);       //Display Sony
  else if (((ia & 1 << S4) == 0) && (count == 1)) SendCanonP(0xB14E, 0xE383);       //Display Canon
  else if (((ia & 1 << S4) == 0) && (count == 2)) SendPanasonic(0xF5728, 0x702002);       //Display Panasonic

  else if (((ia & 1 << S5) == 0) && (count == 0)) SendSony(12, 0x39A);  //Play Sony
  else if (((ia & 1 << S5) == 0) && (count == 1)) SendCanonP(0xDB24, 0xE383);  //Enter Canon
  else if (((ia & 1 << S5) == 0) && (count == 2)) SendPanasonic(0x5828, 0x702002);  //Enter Panasonic
  
  else if (((ia & 1 << S6) == 0) && (count == 0)) SendSony(12, 0x398);  //Stop Sony
  else if (((ia & 1 << S6) == 0) && (count == 1)) SendCanonP(0xE817, 0xE383);  //Stop Canon
  else if (((ia & 1 << S6) == 0) && (count == 2)) SendPanasonic(0x580028, 0x702002);  //Stop Panasonic

  else if (((ia & 1 << S7) == 0) && (count == 0)) SendSony(12, 0x39B);  //Rewind Sony
  else if (((ia & 1 << S7) == 0) && (count == 1)) SendCanonP(0xDC23, 0xE383);  //Left Canon
  else if (((ia & 1 << S7) == 0) && (count == 2)) SendPanasonic(0x8AD329, 0x702002);  //Left Panasonic

  else if (((ia & 1 << S8) == 0) && (count == 0)) SendSony(12, 0x39C);  //Fast-Forward
  else if (((ia & 1 << S8) == 0) && (count == 1)) SendCanonP(0xDD22, 0xE383);  //Right Canon
  else if (((ia & 1 << S8) == 0) && (count == 2)) SendPanasonic(0x8BD229, 0x702002);  //Right Panasonic

  else if ((ia & 1 << S9) == 0) {
    count = count+1;
    if (count>2) (count=0);
    for (int i=0; i<(count+1); i++) {
      BlinkLed(10000,10000); // blink LED once- Sony, twice Canon, 3 times Panasonic
    }      
    
  }
}

const int top = 25;    // 1000000/26 = 38.5kHz
const int match = 19;  // approx. 25% mark/space ratio

// Set up Timer/Counter1 to output PCM on OC1A (PB1)
void SetupPCM() {
  // Inverted fast PWM output on OC1A (PA6) divide by 1 with TOP value ICR1
  TCCR1A = (1 << COM1A1) | (1 << COM1A0) | (1 << WGM11);
  TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10);
  ICR1 = top;   // 38.5kHz
  OCR1A = top;  // Keep output low
}

// Generate count cycles of carrier followed by gap cycles of gap
void Pulse(int count, int gap) {
  OCR1A = match;  // Generate pulses
  for (int i = 0; i < 2; i++) {
    for (int c = 0; c < count; c++) {
      while ((TIFR1 & 1 << TOV1) == 0);
      TIFR1 = (1 << TOV1);
    }
    count = gap;  // Generate gap
    OCR1A = top;
  }
}

void BlinkLed(int count, int gap) {
  digitalWrite(LED_state, HIGH);  // Blink status LED
  for (int i = 0; i < 2; i++) {
    for (int c = 0; c < count; c++) {
      while ((TIFR1 & 1 << TOV1) == 0);
      TIFR1 = (1 << TOV1);
    }
    count = gap;  // Generate gap
  digitalWrite(LED_state, LOW);
  }
}

void SendSony(int NumberBits, unsigned long raw_code) {  //rutina pentru trimiterea codurilor Sony, 40KHz
  const int HeaderUp = 96;       //number of overflows for the time interval
  const int HeaderGap = 24;
  const int Bit1Up = 48;
  const int Bit1Gap = 24;
  const int Bit0Up = 24;
  const int Bit0Gap = 24;

  for (int repeat = 0; repeat < 5; repeat++) {  
    Pulse(HeaderUp, HeaderGap);
    for (int Bit = 0; Bit < NumberBits; Bit++) {
      if (raw_code & (unsigned long)1 << Bit) {
        //Serial.print('1');
        Pulse(Bit1Up, Bit1Gap);
      } else {
        //Serial.print('0');
        Pulse(Bit0Up, Bit0Gap);
      }
    }
    Pulse(0, 778);  //gap between transmissions
  }
}

void SendCanonP(unsigned long raw_code2, unsigned long raw_code1) {
  const int HeaderUp = 349;  
  const int HeaderGap = 174; 
  const int Bit1Up = 21;  //  count/gap = time[microsec]/(top+1)
  const int Bit1Gap = 62; //
  const int Bit0Up = 21;
  const int Bit0Gap = 21;
  TCNT1 = 0;                            // Start counting from 0  
  
  Pulse(HeaderUp, HeaderGap);  
  for (int Bit = 0; Bit<16; Bit++) {
    if (raw_code1 & (unsigned long) 1<<Bit) {
     Pulse(Bit1Up, Bit1Gap);
     } else {       
     Pulse(Bit0Up, Bit0Gap);
    }
  }
  for (int Bit = 0; Bit<16; Bit++) {
    if (raw_code2 & (unsigned long) 1<<Bit) {
      Pulse(Bit1Up, Bit1Gap);
    } else {
     Pulse(Bit0Up, Bit0Gap);
    }
  }
  Pulse(Bit0Up, 735); //end of transmission 0 bit and gap between transmissions
}

void SendPanasonic(unsigned long raw_code2, unsigned long raw_code1) { 
  const int HeaderUp = 128; 
  const int HeaderGap = 64; 
  const int Bit1Up = 16;
  const int Bit1Gap = 48;
  const int Bit0Up = 16;
  const int Bit0Gap = 16;
  TCNT1 = 0;                            // Start counting from 0  
  
  Pulse(HeaderUp, HeaderGap); 
  for (int Bit = 0; Bit<24; Bit++) {
    if (raw_code1 & (unsigned long) 1<<Bit) {
     Pulse(Bit1Up, Bit1Gap);
     } else {       
     Pulse(Bit0Up, Bit0Gap);
    }
  }
  for (int Bit = 0; Bit<24; Bit++) {
    if (raw_code2 & (unsigned long) 1<<Bit) {
      Pulse(Bit1Up, Bit1Gap);
    } else {
     Pulse(Bit0Up, Bit0Gap);
    }
  }
  Pulse(Bit0Up, 2725); //end of transmission 0 bit and gap between transmissions
}

// Setup demo **********************************************

void setup() {
  OSCCAL = 128; 
  pinMode(LED, OUTPUT);
  pinMode(LED_state, OUTPUT);
  pinMode(S1, INPUT_PULLUP); 
  pinMode(S2, INPUT_PULLUP);
  pinMode(S3, INPUT_PULLUP);
  pinMode(S4, INPUT_PULLUP);
  pinMode(S5, INPUT_PULLUP);
  pinMode(S6, INPUT_PULLUP);
  pinMode(S7, INPUT_PULLUP);
  pinMode(S8, INPUT_PULLUP);
  pinMode(S9, INPUT_PULLUP);
  pinMode(11, INPUT_PULLUP); //Reset, prevent floating pin
  digitalWrite(LED_state, LOW);

  SetupPCM();
  // Configure pin change interrupts to wake on button presses
  PCMSK1 |= (1<< PCINT8) | (1<< PCINT9) | (1<< PCINT10); //Mask PB pins
  PCMSK0 |= (1<< PCINT1) | (1<< PCINT2) | (1<< PCINT3) | (1<< PCINT4) | (1<< PCINT5) | (1<< PCINT7); //Mask PA pins
  GIMSK |= (1 << PCIE1) | (1 << PCIE0);  // Enable interrupts
  GIFR |= (1 << PCIF1) | (1 << PCIF0);   // Clear interrupt flag
  // Disable what we don't need to save power
  ADCSRA &= ~(1 << ADEN);         // Disable ADC
  PRR = 1 << PRUSI | 1 << PRADC;  // Turn off clocks to unused peripherals
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
}

// Stay asleep and just respond to interrupts
void loop() {
  sleep_enable();
  sleep_cpu();
}

No issues with infrared transmission. Note that it uses PWM and counting of overflows, no millis() .

To what did you upload the sketch? Why did you modify my sketch?

Do you not see the errors that make you think you know what it is doing?

Why do you get blocks from the modified code? Could it be the processor clock speed?

Srsly finding subtle problems will be hard if you can't get my short sketch to function and then notice what's wrong with it and fix it.

Was it this thread where someone said

why yes, it was. Do that. Or figure out another way.

a7

I simply added 4 blinks in the loop(), as you said " // put your main code here, to run repeatedly: "

I uploaded the code to ATiny84a using Arduino as a programmer.
I searched the ATTiny core on github to find out the TX and RX pins of Tiny84 and put them inside as comments.
As you said "If you added some printing to it, with time stamps, you might get a clue. "
I put a print with millis() on every loop, after 4 blinks. I didn't know that I should leave the sketch untouched. I'll make a test right now with your sketch untouched.
Here's another sketch that work just fine, no millis()

// 1MHz
#include <avr/io.h>
const int LED = 6;        // IR LED output PA6
const int LED_state = 0;  //Blink when change cam

const int top = 25;    // 1000000/26 = 38.5kHz
const int match = 19;  // approx. 25% mark/space ratio

// Set up Timer/Counter1 to output PCM on OC1A (PB1)
void SetupPCM() {
  // Inverted fast PWM output on OC1A (PA6) divide by 1 with TOP value ICR1
  TCCR1A = (1 << COM1A1) | (1 << COM1A0) | (1 << WGM11);
  TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10);
  ICR1 = top;   // 38.5kHz
  OCR1A = top;  // Keep output low
}

void BlinkLed(int count, int gap) {
  //digitalWrite(LED_state, HIGH);
  PORTA |= (1<<PORTA0);; //PA0 HIGH
  for (int i = 0; i < 2; i++) {
    for (int c = 0; c < count; c++) {
      while ((TIFR1 & 1 << TOV1) == 0);
      TIFR1 = (1 << TOV1);
    }
    count = gap;  // Generate gap
    PORTA &= ~(1<<PORTA0); //PA0 LOW
  }
}

void setup() {
  OSCCAL=128;
  pinMode(LED_state, OUTPUT);
  pinMode(LED, OUTPUT);
  //digitalWrite(LED_state, LOW);
  PORTA &= ~(1<<PORTA0); //PA0 LOW

  SetupPCM();
}

void loop() {
  // put your main code here, to run repeatedly:
  BlinkLed(5000,5000);
  BlinkLed(10000,10000);
  BlinkLed(5000,10000);
  BlinkLed(5000,10000);
  BlinkLed(0,50000);
}

Exactly 4 blinks in a row, one slighly longer.
NB.I've disabled LTO.

With your original sketch: only squares on the serial monitor but 9 blinks, the first one longer. I guess I have to calibrate again the internal oscillator through serial.

Where exactly do you ever set the variable time_now? With that not set, it gets initialized to 0 so the first time you call BlinkLed, the first while() loop may pause for a brief period of time, but then once you turn the LED off, millis() will immediately be greater than time_now + 500 and will continue to be true forever.

You should set that variable at the entrance to the function.


void BlinkLed (int count) {
  unsigned long time_now = millis();
  for (int c = 0; c<count; c++){
    digitalWrite(LED_state, HIGH);  // turn on the LED
    Serial.println("ON");
    while(millis() < time_now + 500){
      //wait approx. 500 ms
    }
    digitalWrite(LED_state, LOW);  //turn off the LED
    Serial.println("OFF");
    while(millis() < time_now + 500){
      //wait approx. 500 ms
    }
  }
}

Thank you, that line slipped when copy-paste.
I managed to use the blink sketch. But only Software Serial is working.
I've made a custom delay function using Timer/Counter 1

void SetupTimer() {
  // CTC timer with TOP value OCR1A
  TCCR1B = (1 << WGM12) | (1 << CS10); //no prescaler
  OCR1A = top;
}

void delayTimer(int top){
  TCNT1=0; // start the counting from 0
  while ((TIFR1&(1<<OCF1A)) == 0); //does nothing until it counts to OCR1A value and raise the flag in OCF1A
  TIFR1 = (1<<OCF1A); //clear OCF1A flag
}

void BlinkLed() { // for my purpose I need a delay of 104 microsec so top=103
  PORTA |= (1<<PORTA0);; //PA0 HIGH
   delayTimer(103);
   PORTA &= ~(1<<PORTA0); //PA0 LOW
    delayTimer(103);
  }
}

I have a hard time measuring the pulse length with pulseIn(inputPin, HIGH) with Arduino Uno, I get values from 112 to 135 microsec, no 2 succesive values the same. The internal oscillator is calibrated.

With that code, there is no guarantee that OCF1A is not already set when you enter the function so you exit immediately. Your counter is still running in the background all the while other code is running. Of course, this is just a snippet of your code so nobody knows for sure.

As you mentioned, I've added the TOV1 flag clear at the beginning of delay function, to be sure it was 0 when the timer started to count.
I think that I've got a good pulse value, at least when sending a single pulse, between 101-108 microsec of HIGH.
I cannot understand why when I send 10101010 11011011 I get
108, 104, 107, 101, 103 for pulseIn(inputPin, HIGH)
110, 106, 0, 95, 97 for pulseIn(inputPin, LOW)
I've expected that the fifth value for HIGH to be 202-214 (2x104) and two more 202 after.
For LOW I don't know why I 've got those values.
The ATTiny84 sketch is

// 8MHz prescaler 1/8
#include <avr/io.h>

const int LancOut = 6;        // LANC output PA6
const int LED_state = 0;  //Blink when change cam
//const int LancInput = 2; //Lanc Sync signal on PA2

volatile int top = 101;    // best value for about 104 microsec of delay
const int match = 0;  // continuous signal

void SetupPWM(){
  TCCR1A = (1<<COM1A1) | (1<<COM1A0) |(1<<WGM11); // inverted fast PWM output on OC1A (PA6) with top value ICR1
  TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS11); // prescaler 1/8 for 8MHz
  ICR1 = top; // time interval in microsec until TOV1 flag is set
  OCR1A = top; // keep output on PA6 LOW

}

void delayTimerPWM(int top){
  TIFR1 = (1<<TOV1); // clears the flag for overflow
  TCNT1=0; // reset count to zero
  while ((TIFR1&(1<<TOV1)) == 0); // does nothing until the count reach the top value
  TIFR1 = (1<<TOV1); // clears the flag for overflow
}

void sendByte(unsigned long raw_code){
  for (int Bit = 0; Bit<8; Bit++) {
    if (raw_code & (unsigned long) 1<<Bit) {
      OCR1A = match; //set PA6 HIGH to send Bit1
      delayTimerPWM(101);

      } else {
        OCR1A = top; //set PA6 LOW to send Bit0
        delayTimerPWM(101);

    }
  }

}

void sendLanc(unsigned long raw_code1, unsigned long raw_code2) { //adauga cod pentru repetarea comenzii daca totul se transmite bine
 /* while (pulseIn(LancInput, 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
  */
  delayTimerPWM(101); // wait startbit duration
  sendByte(raw_code1); //sending byte 0
  //Byte 0 is written, now put LANC line back to +5V
  OCR1A = top; //set PA6 LOW
  //make sure to be in the stop bit before byte 1
  delayTimerPWM(9);
 /* while (digitalRead(LancInput) == HIGH) {
  //Loop as long as the LANC line is +5V during the stop bit
  } */
  //wait START bit duration
  delayTimerPWM(101);
  // sending Byte 1
  sendByte(raw_code2);
  //Byte 1 is written now put LANC line back to +5V
  OCR1A = top; //set PA6 LOW
  delayTimerPWM(9);

}

void setup() {
  OSCCAL=119; //pt 8MHz

  pinMode(LED_state, OUTPUT);
  pinMode(LancOut, OUTPUT);
  pinMode(5, INPUT_PULLUP); // test button
//  pinMode(LancInput, INPUT);
  SetupPWM();
}

void loop() {
  // put your main code here, to run repeatedly:
  if (!digitalRead(5)) {
    sendLanc(0xAA, 0xDB);
    for (int count = 0; count<5000; count++) { // to debounce, aprox 500ms
      delayTimerPWM(101);
    }

  }
}

The sketch I used to measure the pulse is

#include <Arduino.h>
#include "PinDefinitionsAndMore.h" // Define macros for input and output pin etc.
unsigned long value = 0;

void setup() {
    pinMode(2, INPUT);
    Serial.begin(115200);
    // Just to know which program is running on my Arduino
    Serial.print(F("Ready to receive electric signal: "));


}

void loop() {
  value = pulseIn(2, LOW);
  Serial.println(value);
  ///delay(1000);
}

run on Arduino Nano. I have no other means to measure the output apart from one Arduino Uno and one Nano. Software Serial is not working with my ATT84 sketch, probably because I messed up the Timer1.
I can't understand why the standard sketch, that run without any problems on Arduino Uno, without any register manipulation specific to Arduino, is not working at all with ATTiny84 (no compiler errors).
In retrospect, the Infrared remote sketch from Arduino also didn't work on ATTiny due to the wrong timings despite the calibration of the internal oscillator and I had to port Jhonson's IR Remote wand.
For testing the signal I also tried LANC Sniffer from Control your camera, both with a working Arduino Uno as a remote+camcorder and sniffer untouched, and with ATTiny84 as remote and a modified sniffer to generate the +5V signal and LOWS for startbit signaling (emulating the camera), to no avail. For the untouched sniffer+Arduino remote+camcorder all I got was 00000000 00000000, and for modified sniffer + ATTiny I got nothing. It's like pulseIn(0 function does not work on ATTiny84, so it couldn't get a startbit to send something. The parts of the code involving the LancInput pin were un-commented at the time of testing.

/*
LANC SNIFFER
Version 1.0
Finds out LANC commands from a REMOTE
2011, Martin Koch
*/

#define lancPin 11
#define LANC_startbit_signal 7

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

int lancBit[16];


void setup() {
  Serial.begin(9600); //open the serial port
  pinMode(lancPin, INPUT); //listens to the LANC line
  pinMode(LANC_startbit_signal, OUTPUT);
  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
  Serial.println("Ready to capture LANC commands");
}

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

void generateStartBit(){  // generate startbits on pin D7
  //digitalWrite(LANC_startbit_signal, HIGH);
  PORTD |= (1<<PORTD7); // set pin D7 HIGH
  time_now = micros();
  while (micros() - time_now < 5400) {
    // interval between telegrams 5400 microsec
  }
  for ( int i=0; i<7; i++){ // generate startbits and bytes 0-7
    //digitalWrite(LANC_startbit_signal, LOW);
    PORTD &= ~(1<<PORTD7); // set pin D7 LOW
    time_now = micros();
    while (micros() - time_now < 104) {
    // send StartBit
    }
    //digitalWrite(LANC_startbit_signal, HIGH);
    PORTD |= (1<<PORTD7); // set pin D7 HIGH
    time_now = micros();
    while (micros() - time_now < 1672) {
    //wait from the start of byte 0 until the next byte
    }

  }
  //digitalWrite(LANC_startbit_signal, LOW);
  PORTD &= ~(1<<PORTD7); // send startbit for byte 7
  time_now = micros();
  while (micros() - time_now < 104) {
  // send StartBit for bit 7
  }
  //digitalWrite(LANC_startbit_signal, HIGH);
  PORTD |= (1<<PORTD7); // set pin D7 HIGH
  time_now = micros();
  while (micros() - time_now < 832) {
  // wait for the length of byte 7
    }

}

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("");
}

If there's no solution I may as well buy ATMega328p for 3$ an be done with this project.

One thing I just realized is that your AT84 is clocking out your bits LSB first so your code 'AA' which is 0b1010 1010 is going out on the wire as 0101 0101

Since it is doing that, I would send a series of bytes to see what PulseIn() is receiving, each with an increasing number of 0s between the 1s. Also note that PulseIn(HIGH) starts timing on the LOW to HIGH transition and stops on the HIGH to LOW

(binary notation in lsb first, hex values in actual value)

byte 1: 0b0100 0000 -> 0x02
byte 2: 0b0110 0000 -> 0x06
byte 3: 0b0111 0000 -> 0x0E
...

For 0x02 I get 108 - 1
For 0x06 I get 217 - 11
For 0x0E I get 326 - 111
For 0xFF I get 869 - 11111111
For 0x02 0xFF I get 109 868 - 1 11111111
For 0x0E 0xFF I get 326/878 - 111 11111111
I think the timing good. I've sent also 0x00 0x0E and 0x0E 0x00, the result is the same, 326, so nothing wrong with the second byte.
The things go south with:
0xAA 0xDB consistently return 109, 103, 198, 220 meaning 1, 1, 11, 11.
0xFF 0xFF return only 875, as if one byte is not sent
Is this a limitation of pulseIn() as a reader?
The PWM version posted above is broken and I couln't fix it. These results are from the CTC version

// 8MHz cu prescaler 1/8
#include <avr/io.h>
const int LED = 6;        // IR LED output PA0
const int LED_state = 0;  //Blink when change cam

const int Button1 = 1;
const int Button2 = 2;
const int Button3 = 3;
const int Button4 = 4;
const int Button5 = 5;
const int Button6 = 6;

volatile int top = 101;    // approx 104 microsec to overflow


void SetupTimer() {
  // CTC timer no output TOP value OCR1A
  TCCR1B = (1 << WGM12) | (1 << CS11); // prescaler 1/8
  OCR1A = top;
}

void delayTimer(int top){
  TIFR1 = (1<<OCF1A); //clear OCF1A flag
  TCNT1=0; // start counting from 0
  while ((TIFR1&(1<<OCF1A)) == 0); //does nothing until OCF1A flag is raised
}

void sendByte(unsigned long command){
  for (int Bit = 0; Bit<8; Bit++) {
    if (command & (unsigned long) 1<<Bit) {
      PORTA |= (1<<PORTA0); //set PA0 to HIGH
      delayTimer(101);
      } else {
        PORTA &= ~(1<<PORTA0); //set PA0 to LOW
        delayTimer(101);
        }
  }
}

void sendLanc(unsigned long raw_code1, unsigned long raw_code2) {
  //delayTimer(101);  //wait START bit duration
  sendByte(raw_code1);
   //Byte 0 is written, now put LANC line back to +5V
  PORTA &= ~(1<<PORTA0); //set PA0 to LOW
  //  delayTimer(9); //make sure to be in the stop bit before byte 1
  //delayTimer(101);  //wait START bit duration
    // sending Byte 1
  sendByte(raw_code2);
    //Byte 1 is written now put LANC line back to +5V
  PORTA &= ~(1<<PORTA0); //set PA0 to LOW

}

void setup() {
  OSCCAL=119; // 8MHz MCU
  pinMode(LED_state, OUTPUT);
  pinMode(LED, OUTPUT);
  pinMode(Button1, INPUT_PULLUP); // test
  pinMode(Button2, INPUT_PULLUP); // test
  pinMode(Button3, INPUT_PULLUP); // test
  pinMode(Button4, INPUT_PULLUP); // test
  pinMode(Button5, INPUT_PULLUP); // test
  pinMode(Button6, INPUT_PULLUP); // test 
  PORTA &= ~(1<<PORTA0); //PA0 LOW

  SetupTimer();
}

void loop() {
  if (!digitalRead(Button1)) {
    sendLanc(0xAA, 0xDB);
    for (int count = 0; count<5000; count++) {
      delayTimer(101);
    }

  }
    if (!digitalRead(Button2)) {
    sendLanc(0x02, 0x00);
    for (int count = 0; count<5000; count++) {
      delayTimer(101);
    }

  }
    if (!digitalRead(Button3)) {
    sendLanc(0x06, 0x00);
    for (int count = 0; count<5000; count++) {
      delayTimer(101);
    }

  }
    if (!digitalRead(Button4)) {
    sendLanc(0x0E, 0x00);
    for (int count = 0; count<5000; count++) {
      delayTimer(101);
    }

  }
    if (!digitalRead(Button5)) {
    sendLanc(0x00, 0x0E);
    for (int count = 0; count<5000; count++) {
      delayTimer(101);
    }

  }
    if (!digitalRead(Button6)) {
    sendByte(0x0E);
    for (int count = 0; count<5000; count++) {
      delayTimer(101);
    }

  }

}

If the timing is good for Bit 1 (HIGH) I think that the timing is also good for Bit 0 (LOW), they use the same delayTimer() function with the same argument.
I can't understand why values like 0xFF 0xFF and 0xAA 0xDB are not sent correctly.

Just crossed my mind that LANC protocol is a serial protocol with baud rate 9600 and I can check what the MCU is sending with a Serial.read() from Arduino. To send my commands as valid Serial frames towards Arduino, I must send only 1 byte of payload, with a startbit of LOW, a parity bit and a stop bit of HIGH, and MCU's data out must be HIGH when not transmitting.

void sendSerial(unsigned long raw_code) {
  PORTA &= ~(1<<PORTA0); //set PA0 to LOW to send startbit
  delayTimer(101); //wait startbit duration
  sendByte(raw_code);
  //no need for parity bit
   //Byte is written, now put LANC line back to +5V
  PORTA |= (1<<PORTA0); //set PA0 to HIGH
  delayTimer(101); //wait for stopbit to end
}

The receiving sketch on the Arduino's end

// Receiving binary data by serial
byte Byte1;

void setup() {
    Serial.begin(9600);
    Serial.println("<Arduino is ready>");
}

void printBin(byte value) //thanks to UKHeliBob
{
  for (int b= 7; b >=0; b--)
  {
    Serial.print(bitRead(value, b));
  }
}

void loop() {
     if(Serial.available())
     {
        Byte1 = Serial.read();
        //Serial.println(Byte1, BIN); // does not print leading 0 bits
        printBin(Byte1); //print all bits one by one
        Serial.println(" ");
        }
}

Arduino via Serial at 9600 bits/sec says that all LANC commands from ATTiny84 are received fine, no bit mismatch.
To to:

  • rewrite pulseIn() for ATTiny84 using Timer1 +/- interrupts: find out when the HIGH pulse on the LANC Input is over 5000 microsec
  • generate and test delay of 10 microsec with delayTimer(9) on ATiny84
  • add the function to generate startbits to Arduino serial test sketch (mimic camcorder behaviour) to check the sync (done)

The function that mimic the camcorder signal, used on Arduino Nano

void generateStartBit(){  // generate startbits on pin D7, non-blocking

  for ( int i=0; i<8; i++){ // generate startbits for bytes 0-7
    //digitalWrite(LANC_startbit_signal, LOW);
    PORTD &= ~(1<<PORTD7); // set pin D7 LOW
    time_now = micros();
    while (micros() - time_now < 104) {
    // send StartBit as LOW for 104 microsec
    }
    //digitalWrite(LANC_startbit_signal, HIGH);
    PORTD |= (1<<PORTD7); // set pin D7 HIGH
    time_now = micros();
    while ((micros() - time_now) < 1296) {
    // put the line back to HIGH for the interval until the startbit of the next byte
    }

  }
  PORTD |= (1<<PORTD7); // set pin D7 HIGH
  time_now = micros();
  while ((micros() - time_now) < 10000) {
  // wait for the time between telegrams (sets of 8 bytes)
    }

}

always I get the result from Arduino detector using pulseIn() 1285, 1291, 1295 1285, 1291, 1295, 1285, 1291, 1295, 11216 , adding 2 extra intervals of 1291 microsec (one is fused to the big value 11216) for a baud rate of 9600. If I move to 115200 baud rate I get only 3 small intervals and one big. What's wrong?