Timing Problem with Lanc & pulsein

I am trying to control a Sony VideoCamera through an RC Transmitter.
The Lanc Code I am using is from Michael Koch,

The Code works great, but if I add my pulsein Code for reading PPM/PWM Signal from the Reciever,

button = pulseIn(ppmSwitch, HIGH, 20000);
  poti = pulseIn(ppmPoti, HIGH, 20000);

the Zoom & Focus Control is choppy, because of the 20000ms Timing needed for reading the Pwm Signals.

How could I overcome the Problem?

#define cmdPin 7 
#define lancPin 11
#define ppmPoti 13
#define ppmSwitch 4
int cmdRepeatCount;
int bitDuration = 104; //Duration of one LANC bit in microseconds. 
int button;
int poti;
int recordstate = 0;

//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 - not in Use

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

*/

void setup() {

  pinMode(lancPin, INPUT); //listens to the LANC line
  pinMode(cmdPin, OUTPUT); //writes to the LANC line
  pinMode(ppmSwitch, INPUT); //PPM RX Switch Input
  pinMode(ppmPoti, INPUT); //PPM RX Poti Input

  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() {
    
  
  button = pulseIn(ppmSwitch, HIGH, 20000);
  poti = pulseIn(ppmPoti, HIGH, 20000);
  
  if(button>1500 && recordstate==0)
  {
    recordstate = 1;
    lancCommand(REC);
  }
  
  if(button<1400 && recordstate==1)
  {
    recordstate = 0;
    lancCommand(REC);
  }
  
    
  if (poti>1580 && poti<1630){lancCommand(ZOOM_IN_0);}
  if (poti>1630 && poti<1680){lancCommand(ZOOM_IN_1);}
  if (poti>1680 && poti<1730){lancCommand(ZOOM_IN_2);}
  if (poti>1730 && poti<1780){lancCommand(ZOOM_IN_3);}
  if (poti>1780 && poti<1830){lancCommand(ZOOM_IN_4);}
  if (poti>1830 && poti<1880){lancCommand(ZOOM_IN_5);}
  if (poti>1880 && poti<1930){lancCommand(ZOOM_IN_6);}
  if (poti>1930){lancCommand(ZOOM_IN_7);}
  
  
  if (poti<1420 && poti>1370){lancCommand(ZOOM_OUT_0);}
  if (poti<1370 && poti>1320){lancCommand(ZOOM_OUT_1);}
  if (poti<1320 && poti>1270){lancCommand(ZOOM_OUT_2);}
  if (poti<1270 && poti>1220){lancCommand(ZOOM_OUT_3);}
  if (poti<1220 && poti>1170){lancCommand(ZOOM_OUT_4);}
  if (poti<1170 && poti>1120){lancCommand(ZOOM_OUT_5);}
  if (poti<1120 && poti>1070){lancCommand(ZOOM_OUT_6);}
  if (poti<1070){lancCommand(ZOOM_OUT_7);}
  
  
}


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
}

You could use interrupts to detect the start and end of each pulse (and use micros() to determine the interval between the start/end) rather than using a blocking call to pulseIn(). If your interrupt handler stored the calculated pulse length in a volatile byte variable then your main code reading it in loop() could refer to the variable whenever it wanted to, the method of keeping the variable up to date would be transparent to it.

As a complete Noob, I couldn't figure out how I have to work with Interrupts to detect start and end of the pwm/ppm pulse.

Does someone maybe have a sample Code, to read the PWM Signal from one Pin through Interrupts?
I looked at some Examples, but they are to complex for me, to figure this part out.

boolean ZOOM_IN_0[] = {LOW,LOW,HIGH,LOW,HIGH,LOW,LOW,LOW,   LOW,LOW,LOW,LOW,LOW,LOW,LOW,LOW}; //28 00
...

booleans are true or false. Try making them "byte" type.

Stuff about interrupts: http://www.gammon.com.au/interrupts

?

It seems to me the coder (not the OP I don't think) just chose to deal with an array of 8 "bits" here as "bool bits[]={0,0,1,0, 1,0,0,0};" rather than a more conventional "u_int8 bits=0x28;" (Ok, maybe he "wasted" 7 bytes of memory-- but then so does your recommendation byte bits[]={0,0,1,0, 1,0,0,0};).

I'm not trying to confront, just trying to identify if I'm missing something...

Thanks,
John

It will probably use the same amount of memory, I'm just saying it is the "wrong" datatype. I didn't recommend 0/1 BTW, the values LOW/HIGH are intended to go into the byte type, not the boolean type.

Let's hypothetically say that one day HIGH is defined as 0xFF, and that the boolean type can only hold one bit. Then you can't put HIGH into a boolean. (Today you probably can).

Ok, I see where you're coming from, thanks. I agree this will make his declaration more consistent/correct.

I doubt it will have an effect on his problem though :slight_smile:

Cheers,
John

I changed the boolean to byte, and yes it works just like before :slight_smile:

About the Interrupts, I found a page providing a good sample Code,
but I still don't get it to work to read the pwm Signal through Interrupts.

Hi John,

as soon as I change the Value below 15000 the PPM Signals recieved are not correct anymore, 2 of 3 Results will have 0 instead of the PPM Value.
Anyway, I just found a simpler Code which works with External Interrupts. Therefore I can only use 2 Pins, but for me this is just as much as I need.

Here is the Example:

Hi,
I wrote both of the links you have posted.

I suspect that you were reading 0's with the first sketch because of the location of your serial print statement. Would you mind trying the sketch again with the serial print in this location, if you use it in any other location without testing if(bUpdateFlags & THROTTLE_FLAG) you cannot assume that it will have a valid value.

   if(bUpdateFlags & THROTTLE_FLAG)
    {
      unThrottleIn = unThrottleInShared;
      // Serial.println(unThrottleIn);
    }
  
    if(bUpdateFlags & STEERING_FLAG)
    {
      unSteeringIn = unSteeringInShared;
    }
  
    if(bUpdateFlags & AUX_FLAG)
    {
      unAuxIn = unAuxInShared;
    }

Duane B

Hi Duane,

right, all I got were 0's :slight_smile:
I will try your Code as soon as I am back home and post back afterwards.

Mario

Hi Duane,

sorry, but I am still not able to get it to work yet.
If I grab any of your Examples:
RCArduino: How To Read Multiple RC Channels or
http://rcarduino.blogspot.co.at/2012/11/how-to-read-rc-channels-rcarduinofastlib.html

and load them unmodified onto my Arduino Mega, I am not able to do any Serial.print, nor will the Example work with an attached Servo.
Looking over the Code, it looks that your sample should be working without Changes on the predefined Pins, therefore I connected the RX "Channel 1" to Pin 5 and the Servo to Pin 8.
The Servo actually turns into neutral Position "1500" but stays there. As said, I am also not able to do any Serial.prints

But as said, I can probably work with your Example of the External Interrupts.

thanks,
Mario

Hi,
I didn't see that you were using a Mega.

From this link

it appears that int0 is digital pin 21 on the mega, try this as the PPM pin - not sure where you got pin 5 from ?

Duane B

rcarduino.blogspot.com

Hi,

sorry but I just dived into the arduino platform 2 Days ago, so I might have made some big mistakes,
but I guessed from your Code

// Assign your channel in pins
#define THROTTLE_IN_PIN 5
#define STEERING_IN_PIN 6
#define AUX_IN_PIN 7

// Assign your channel out pins
#define THROTTLE_OUT_PIN 8
#define STEERING_OUT_PIN 9
#define AUX_OUT_PIN 10

that Pin 5 would be the ppm signal in & Pin 8 would be ppm out to the Servo / Esc.
Looking at your Video, you used the same Pin's too - 5 & 8. Arduino Uno has it's int1 on Pin2.
Sample Code from: RCArduino: How To Read Multiple RC Channels
Just to clarify, I am not using a PPM-Sum Signal, it's a single Channel PPM/PWM Signal. (Don't actually know if it is pwm or ppm now, as some say it's ppm)

Mario

Keeping it simple, the starting point should be this sketch, its the most basic and the starting point for all of the others -

The sketch assumes you are using an UNO, to change it for a mega, change the following code

#define THROTTLE_SIGNAL_IN 0 // INTERRUPT 0 = DIGITAL PIN 2 - use the interrupt number in attachInterrupt
#define THROTTLE_SIGNAL_IN_PIN 2 // INTERRUPT 0 = DIGITAL PIN 2 - use the PIN number in digitalRead

To

#define THROTTLE_SIGNAL_IN 0 // INTERRUPT 0 = DIGITAL PIN 21 - use the interrupt number in attachInterrupt
#define THROTTLE_SIGNAL_IN_PIN 21 // INTERRUPT 0 = DIGITAL PIN 21 - use the PIN number in digitalRead

Try this with the signal wire from the RC Receiver connected to pin 21 of the mega.

Duane B

Hi Duane,

yes, that's the Code I am using since Yesterday which works great. Look at my Reply #8
The Arduino Mega still uses Pin 2 & 3 + extra Int Pins on 18-21
See -> http://arduino.cc/en/Reference/AttachInterrupt

But it is still driving me nuts that I am not getting your great Example to work, because I want to use the Code with an Arduino Mini 328, and might need more than 2 Channels,
but for now I am all good.

Thanks,
mario

Hi,
So its the multi channel code that you are having trouble with ?

At one time there was a bug in the pinchangeint library which stopped it working on some pins, which version are you using ?

I will dig out a Mega tomorrow and get the code up and running just to make sure I haven't done anything daft my side - I use 328 based UNOs most of the time.

Duane B

I am using the latest Verion 2.19 beta of the PinChangeInt library.

I will get my 328 Micro Boards sometime this week, so currently I am stuck with the mega.