IR Remote for Camera:Help with Hardware Interrupts and micros() function

Hey, I’m trying to make a DIY remote for my Sony NEX 5K, I’m running into some trouble with hardware interrupts and the micros() function that I’m using to generate the actual pulse for the shutter release IR code. My code is somehow not using the micros() function properly (in the void loop() function) to generate the required pulse even though I think it escapes the ISR by the time it comes to that bit (at least that’s what I think :S)

/*

Code to implement a DIY IR remote for Sony NEX 5K. The remote has 2 modes.

Mode1: timelapse function, takes a picture every "td" milliseconds where td is decided by a trim pot connected to Analog0
Mode2: Manual click with clickbutton

I'm using a hardware interrupt to switch modes with the switch input on INT0. The click button switch for mode2 is connected to digital 8. 
I've implemented Schmitt Trigger based hardware debouncing for both switches.

Source of IR code for Sony NEX 5K Shutter release code:
http://www.l8ter.com/?p=333 

--Mayank Joneja
 */

int modeButton = 0; // Mode button wired to INT0
int clickButton = 8;// Click button wired to digital pin 8

int modeLed = 7; //An indicator LED to show the mode
int clickLed = 13; //An indicator LED for the clicking in mode2
int pinIRLED = 12; //The actual IR LED to transmit the pulse

volatile boolean mode = false;

void setup()
{
  //Set up digital pin 7,13 as output LEDs for mode and shutter

  pinMode(modeLed,OUTPUT);
  pinMode(clickLed,OUTPUT);
  pinMode(clickButton,INPUT);
  
  //Attach interrupt to INT0 for mode 
  attachInterrupt(modeButton, modeSwitch, RISING);
  
}

void modeSwitch()
{
  mode =!mode;
  digitalWrite(modeLed,mode);
}



void pulseON(int pulseTime) {
  unsigned long endPulse = micros() + pulseTime;        // create the microseconds to pulse for
  while( micros() < endPulse) {
    digitalWrite(pinIRLED, HIGH);                       // turn IR on
    delayMicroseconds(13);                              // half the clock cycle for 38Khz  - e.g. the 'on' part of our wave
    digitalWrite(pinIRLED, LOW);                        // turn IR off
    delayMicroseconds(13);                              // delay for the other half of the cycle to generate wave/ oscillation
  }
}


void pulseOFF(unsigned long startDelay) {
  unsigned long endDelay = micros() + startDelay;       // create the microseconds to delay for
  while(micros() < endDelay);
}


void takePicture() {
  for (int i=0; i < 2; i++) {
pulseON(2336);
pulseOFF(646);
pulseON(1168);
pulseOFF(621);
pulseON(572);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(572);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(572);
pulseOFF(646);
pulseON(572);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(572);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(572);
pulseOFF(621);
pulseON(572);
pulseOFF(646);
pulseON(572);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(1168);
pulseOFF(11008);
pulseON(2336);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(572);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(572);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(572);
pulseOFF(646);
pulseON(572);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(572);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(572);
pulseOFF(621);
pulseON(572);
pulseOFF(646);
pulseON(572);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(1168);
pulseOFF(11008);
pulseON(2336);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(572);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(572);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(572);
pulseOFF(646);
pulseON(572);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(572);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(1168);
pulseOFF(621);
pulseON(1093);
pulseOFF(696);
pulseON(572);
pulseOFF(621);
pulseON(572);
pulseOFF(621);
pulseON(572);
pulseOFF(621);
pulseON(572);
pulseOFF(1218);
pulseON(497);
pulseOFF(1292);
pulseON(422);
pulseOFF(1367);
pulseON(373);
pulseOFF(11803);
pulseON(298);
pulseOFF(2659);
pulseON(199);
pulseOFF(1590);
pulseON(174);
pulseOFF(1019);
pulseON(174);
pulseOFF(1615);
pulseON(174);
pulseOFF(1615);
pulseON(149);
pulseOFF(1044);
pulseON(149);
pulseOFF(1640);
pulseON(124);
pulseOFF(1093);
pulseON(149);
pulseOFF(1044);
pulseON(124);
pulseOFF(1665);
pulseON(124);
pulseOFF(1068);
pulseON(124);
pulseOFF(1665);
pulseON(99);
pulseOFF(1690);
pulseON(99);
pulseOFF(1690);
pulseON(99);
pulseOFF(1093);
pulseON(99);
pulseOFF(1118);
pulseON(99);
pulseOFF(1093);
pulseON(99);
pulseOFF(1690);
pulseON(99);
pulseOFF(1690);
pulseON(75);
pulseOFF(1715);
pulseON(75);
pulseOFF(12101);
pulseON(149);
pulseOFF(2833);
pulseON(75);
pulseOFF(1715);
pulseON(75);
pulseOFF(1118);
pulseON(75);
pulseOFF(1715);
pulseON(75);
pulseOFF(1715);
pulseON(75);
pulseOFF(1118);
pulseON(75);
pulseOFF(1715);
pulseON(75);
pulseOFF(1118);
pulseON(99);
pulseOFF(1093);
pulseON(99);
pulseOFF(1690);
pulseON(99);
pulseOFF(1093);
pulseON(99);
pulseOFF(1690);
pulseON(99);
pulseOFF(1690);
pulseON(99);
pulseOFF(1690);
pulseON(99);
pulseOFF(1093);
pulseON(99);
pulseOFF(1118);
pulseON(99);
pulseOFF(1093);
pulseON(99);
pulseOFF(1690);
pulseON(99);
pulseOFF(1690);
pulseON(99);
pulseOFF(1690);
pulseON(99);
pulseOFF(646);
  }                                                     // loop the signal twice.
}


void loop()
{
  int td=analogRead(A0)*20; //time delay for time lapse, 20 is a scaling factor, will calibrate it later on
  
  if(mode==false) // mode1
  {
    while(mode == false) // as long as the device is in mode1 (will switch modes if interrupted by mode switch being pressed
    {
      takePicture();
      delay(td);
    }
  }
  else if ((digitalRead(clickButton) == HIGH)&& mode == true) //Click is pressed and device is in mode2
  {
    digitalWrite(clickLed,HIGH); //indicator LED
    takePicture();
    digitalWrite(clickLed,LOW);
  } 
  
}

As of now, I’m able to switch modes using the mode button (Please read the comments in the code for a better picture) without any issues. But whenever I call the takePicture() function it doesn’t transmit the IR code. I tried connecting both the buttons to interrupts earlier but learnt that I can’t use the millis(), micros() and delay functions inside Interrupt Service Routines for Arduino, so I changed my code and setup to use an interrupt to just switch modes. As you can see, the takePicture() function should be able to use the micros() function because I think it has exited the ISR for the mode switch as soon as its done changing the modeLed status.

Please let me know what I’m doing/understanding incorrectly and what I can do to make it work.

Thanks :~

digitalWrite() is not fast. It takes 4-5 microseconds. It is an Arduino library call. This may be messing up your timing. There are fast alternatives often discussed in this forum and elsewhere.

I believe you cannot call digitalWrite from within an interrupt handler. Unless you know precisely what you are doing, and what the library function does, you should probably limit code within a signal handler to setting a single volatile variable that is then inspected in the loop code.

You can find a library (IRremote) here that knows how to send/receive many different IR codes, including Sony. Note, IRremote uses a hardwired pin for the IR led, that depends on the actual hardware (IRremote uses an interrupt, and it must be tied to that pin). On the Due/Mega it uses pin 9, while on most of the other boards like Uno, it uses pin 3. Be sure to go to the github site to get the updated version that works with the current IDE: A Multi-Protocol Infrared Remote Library for the Arduino

@joe mcd

I'll look into that, but the takePicture() code works ok when I use it directly in the void loop() without any interrupts..even with interrupts, I think its not like I'm interrupting the takePicture() function in any way, I'm sorry if I come across too confused, but the way I see it, whenever I give a hardware interrupt on pin0, my code branches from my void loop to the ISR i've written using modeSwitch() and as soon as it runs through that it comes back to my void loop and continues execution.

I know that the timer gets suspended as soon as an Interrupt is made, which means I can't use delay(), micros(), millis() inside an interrupt (I think that has been taken care of in Arduino IDE 1.5.4 I guess, still have to look into that). What I suspect is that because of my timer getting suspended inside the ISR, when it comes back to the loop, it's not able to use the micros() function accurately somehow or something because it switches my clickLed on and off, and the code doesn't hang, but it doesn't actually send the pulse successfully.

@MichaelMeissner
Are you referring to the digitalWrite inside modeSwitch to switch the LED on and off? That's working fine. :~
Also, thanks for the link, I have done IR comm. using Ken Shirriff's library and that works like a breeze. The main reason why I'm doing it this way is because for my particular camera, for the shutter release code, this is the only working pulse/IR code that I could find online. (I used the library for TV remotes etc and it doesn't give any issues). Is it possible to convert the signal from this form into a hex value that would be compatible with the IR Remote lib?

Using the IRremote (Or IRLib) library is the best approach for this. Even if the camera protocol is not directly supported by the library you can always use the RAW mode to record and later replay any typical signal, with no need for code like your PulseON/PulseOFF.

Silly me, :sweat_smile: didn’t think of that :smiley: Thanks a lot! I’ll try that, although I would appreciate it if someone could help me somehow figure out what exactly is happening at the MicroC level which is causing something like this to happen

A possible problem with your Pulse function is that the Arduino microsecond granularity is 4 uSecs. This means that trying to time 13 uSecs 'could' lead you anywhere between 9 and 17 uSecs. Also your 2 x digitalWrites will add more delay as will your micros call and finally your While loop.
The best way to achieve a mudulated signal for IR is with PWM or Timer interrupts & direct port access. (you need to search & study a bit)
Of course you could try to hack your code a bit by reducing the 13 in steps of 1(you might be lucky) or actually calculating the delays by figuring out the number of instruction cycles etc.

However, IRremote (or IRLib) is the way to go as it has solved all this stuff for you already!.

I believe you cannot call digitalWrite from within an interrupt handler.

I can’t think why you shouldn’t.