Quicker way to work with Channel changes (hardware interrupts)

The method I am using takes too much time to check the state of my RC connection

  Channel1 = (pulseIn (channel1, HIGH)); // Checks the value of channel1
  //Serial.println (Channel1); //Prints the channels value on the serial monitor
 
  Channel2 = (pulseIn (channel2, HIGH)); // Checks the value of channel1
  //Serial.println (Channel2); //Prints the channels value on the serial monitor

Is there a faster way out there? Can someone explain Hardware Interrupts to me a little better or give me source code on it (Heard this could work better). Thanks :slight_smile:

pulseIn() is probably one of the most evil functions in the Arduino framework. When you need just one of them it works great but more than one? Not a good way of doing things.

Start with Nick Gammon's page on interrupts. Or you may prefer the style of http://www.engblaze.com/we-interrupt-this-program-to-bring-you-a-tutorial-on-arduino-interrupts/.

Check out this post from a couple days ago, there is some code you can use as an example and some explanation of using the code for reading RC channels/

http://forum.arduino.cc/index.php?topic=327016.msg2262783#msg2262783

Are you able to reset the ISR once its offline? Like when the controller goes offline, I can return to the program routine without hard resetting?

Delta_G:
and then returns to wherever the code was when the ISR was fired

Answered my question just fine.

So this is what I have set up right now (Thanks BH)

#define dt 20         // [ms] Task time 

#define CH1_int 0     // Channel 1 interrupt # 
#define CH1_pin 2     // Respective channel Hardware interrupt pin number

#define CH2_int 0     // Channel 2 interrupt # 
#define CH2_pin 3     // Respective channel Hardware interrupt pin number

#define valid_pulse_limit 3000 // [uS] Valid output high pulse time limit for RC controller
#define max_high_time 1895 // [uS] Maximum expected high time
#define min_high_time 1090 // [uS] Minimum expected high time
 

unsigned long t=0;

void init_rc_rx();
void read_rc_rx();  

volatile unsigned long CH1_t=0, CH1_delta=0, CH2_t=0, CH2_delta=0 ;
float CH1,CH2;


// Interrupt ISRs

void CH1_int_ISR()
{
  if ((micros()-CH1_t) < valid_pulse_limit){
    CH1_delta = micros()-CH1_t;
  }
  CH1_t = micros();
}

void CH2_int_ISR()
{
  if ((micros()-CH2_t) < valid_pulse_limit){
    CH2_delta = micros()-CH2_t;
  }
  CH2_t = micros();
}

// Call able function

void init_rc_rx(){
  
  Serial.print("Channel 1 connected to pin number....\t");
  Serial.println(CH1_pin);
  Serial.print("Channel 2 connected to pin number....\t");
  Serial.println(CH2_pin);
  
  pinMode(CH1_pin, INPUT);
  pinMode(CH2_pin, INPUT);

  attachInterrupt(CH1_int, CH1_int_ISR, CHANGE);
  attachInterrupt(CH2_int, CH2_int_ISR, CHANGE);

}
void read_rc_rx(){
  CH1 = ((float)CH1_delta-(float)min_high_time)*100/(max_high_time-min_high_time);
  CH2 = ((float)CH2_delta-(float)min_high_time)*100/(max_high_time-min_high_time);

}
void setup(){
  Serial.begin(115200); // Initlizing serial port before calling init_rc_rx() function 
  init_rc_rx(); 
}

void loop(){ 
  t = micros()/1000;
  
  read_rc_rx();        // CH1, CH2, CH3 and CH4 float variables will be calculated with this function is called
  
  Serial.print(CH1);
  Serial.print("\t");
  Serial.print(CH2);
  Serial.print("\t");
  Serial.println(((micros()/1000)- (float)t)*100/dt);
  
 
  
  
}

But where do I place my code so that the RC will work? This needs to run while the interrupt is on and stay off when the interrupt is off.

     //BACK MOTOR
   analogOutputF = map(Channel2, 990, 1900, 0, 180); 
   ST1.write(analogOutputF);
     Serial.println (Channel2);
   
     //STEERING
   analogOutputB = map(Channel1, 1260, 1660, 0, 180); 
   if (Channel1 > 1500 && Channel1 < 1700){
     ST2.write(180);
        //Serial.println (180);

   }else if (Channel1 < 1400 && Channel1 < 1300){
     ST2.write(0);
        //Serial.println (0);

   }else if (Channel1 > 1400 && Channel1 < 1500){
     ST2.write(90);
        //Serial.println (110);

You are going to just want to stick that code in your loop() but with slight modification. For instance here

   analogOutputB = map(Channel1, 1260, 1660, 0, 180);
   if (Channel1 > 1500 && Channel1 < 1700){
     ST2.write(180);
        //Serial.println (180);

You will want to use CH1_delta from the ISR to get the microsecond value of the pulse width.

So like this

   analogOutputB = map(CH1_delta, 1260, 1660, 0, 180);
   if (CH1_delta > 1500 && CH1_delta < 1700){
     ST2.write(180);
        //Serial.println (180);

You can also remove

read_rc_rx();

From loop() as this this is giving CH1 and CH2 a 0-100 value based of the pulse width time returned from the ISR (not something you are using).

Basically, you can look at it like this. The ISRs run in the background and continuously update CH1_delta and CH2_delta based off of received pulse width. When you want to use those values they are available and reflect the most current pulse width measurement. They are doing the same job as

CH1_delta = (pulseIn (channel1, HIGH)); // Checks the value of channel1

would do, except they are doing it without all of the associated delay. You don't need to call the ISR to get the value, or update the ISR or anything, it just stays in the background and does it's job.

So I combined my code into a large mess and I think where I am still having issues is what to do with if (Channel1 == 0 && Channel2 == 0){

Here is my code

// -----LIBRARIES

#include <Servo.h>
//
#define dt 20         // [ms] Task time 

#define CH1_int 0     // Channel 1 interrupt # 
#define CH1_pin 9     // Respective channel Hardware interrupt pin number

#define CH2_int 1     // Channel 2 interrupt # 
#define CH2_pin 10     // Respective channel Hardware interrupt pin number

#define valid_pulse_limit 3000 // [uS] Valid output high pulse time limit for RC controller
#define max_high_time 1895 // [uS] Maximum expected high time
#define min_high_time 1090 // [uS] Minimum expected high time
//

int b = 3; //This is distance in feet forward
int a = 1; //Speed
// 1 = Fast forward
// 2 = medium forward
// 3 = slow forward
// 4 == slow back
// 5 == medium back
// 6 == fast back
int power_mapping [] = { 0, 160, 135, 110, 70, 50, 30 }; //Speed Settings
int power_mappingReversed [] = { 0, 30, 50, 70, 110, 135, 160 }; //Speed Settings
float timer_mapping [] = { 0, 163, 341, 1245, 1131, 389, 207 }; //Speed in Feet Per Second
float timer_mappingReversed [] = { 0, 207, 389, 1131, 1245, 341, 163 }; //Speed in Feet Per Second
//160(0546) 135(1139) 110(4150) 70(3770) 50(1298) 30(0690)

int channel1 = 9; // defines the channels that are connected
int channel2 = 10;// to pins 9 and 10 of arduino respectively
int analogOutputF ; //Used later to
int analogOutputB ; // store values
int Channel1 ; // Used later to 
int Channel2 ; // store values
int switchingServo = 0;
volatile unsigned long CH1_t=0, CH1_delta=0, CH2_t=0, CH2_delta=0 ;
float CH1,CH2;
unsigned long t=0;
void init_rc_rx();
void read_rc_rx();  

//------- VARIABLES (will change)

Servo ST1;
Servo ST2;  
int servoInt = 0;          //    will be changed to negative value for movement in the other direction
unsigned long currentMillis = 0;    // stores the value of millis() in each iteration of loop()
unsigned long previousServoMillis = 0; // the time when the servo was last moved

//========
void CH1_int_ISR()
{
  if ((micros()-CH1_t) < valid_pulse_limit){
    CH1_delta = micros()-CH1_t;
  }
  CH1_t = micros();
}

void CH2_int_ISR()
{
  if ((micros()-CH2_t) < valid_pulse_limit){
    CH2_delta = micros()-CH2_t;
  }
  CH2_t = micros();
}

// Call able function

void init_rc_rx(){
  
  Serial.print("Channel 1 connected to pin number....\t");
  Serial.println(CH1_pin);
  Serial.print("Channel 2 connected to pin number....\t");
  Serial.println(CH2_pin);
  
  pinMode(CH1_pin, INPUT);
  pinMode(CH2_pin, INPUT);

  attachInterrupt(CH1_int, CH1_int_ISR, CHANGE);
  attachInterrupt(CH2_int, CH2_int_ISR, CHANGE);

}


//========
void setup() {

   pinMode (channel1, INPUT);// initialises the channels
   pinMode (channel2, INPUT);// as inputs
 Serial.begin(115200);
 ST1.attach(8);
 ST2.attach(7);
}

//=======

void loop() {

     currentMillis = millis();   // capture the latest value of millis()
     
        if (Channel1 == 0 && Channel2 == 0){

if (currentMillis - previousServoMillis >= servoInt) {
      previousServoMillis = currentMillis;

    if (switchingServo == 0){
      switchingServo = 1;
      ST1.write(power_mapping [a]);
      servoInt = (b*timer_mapping [a]);
      Serial.println(servoInt);
    }else if (switchingServo == 1){
      switchingServo = 2;
      ST1.write(90);
      servoInt = (1000);
      Serial.println(servoInt);
    }else if (switchingServo == 2){
      switchingServo = 3;
      ST1.write(power_mappingReversed [a]);
      servoInt = (b*timer_mappingReversed [a]);
      Serial.println(servoInt);   
    }else if (switchingServo == 3){
      switchingServo = 0;
      ST1.write(90);
      servoInt = (1000);
      Serial.println(servoInt);
    }}
  }else{
       
  RC();
   
    }
  }



void RC(){
  
   previousServoMillis = 0; 
   servoInt = 0;
   switchingServo = 0;

   
     //BACK MOTOR
   analogOutputF = map(CH2_delta, 990, 1900, 0, 180); 
    ST1.write(analogOutputF);
     //Serial.println (Channel2);
   
     //STEERING
   analogOutputB = map(CH1_delta, 1260, 1660, 0, 180); 
   if (CH1_delta > 1500 && CH1_delta < 1700){
     ST2.write(180);
        //Serial.println (180);

   }else if (CH1_delta < 1400 && CH1_delta < 1300){
     ST2.write(0);
        //Serial.println (0);

   }else if (CH1_delta > 1400 && CH1_delta < 1500){
     ST2.write(90);
        //Serial.println (110);
       
  
}
}


//=====END

Or I guess what I am asking is how does it actually interrupt the program? I've never used anything like this before. Thanks for any help :slight_smile:

eckaus15:
Or I guess what I am asking is how does it actually interrupt the program? I've never used anything like this before. Thanks for any help :slight_smile:

The program will run though setup() once, then repeatedly through loop() as normal. Should an interrupt occur, such as pin 2 going HIGH (where attachInterrupt() has been used to associate interrupt 0 with a trigger of HIGH) then the program will save its current state, execute the code in the associated ISR, return to where it was when the interrupt occurred, restore its state and carry on.

Interrupts are automatically disabled during ISRs so anything that uses them, such as Serial.print() or delay(), cannot be used. Neither should an ISR contain any code that would block operation, such as a while loop. The ISR should run as quickly as possible, maybe set a flag to indicate that it had been triggered, increment a counter, save the current value of millis() etc to be used in the main program, and exit. The values changed by the ISR must be declared volatile if they are to be accessed by the main program.

My isr isnt being called, what do I look at to fix this? Code above

The first thing to check are the connections. Are the RC Rx channel outputs wired to the correct Arduino pins ? Do the Rx and Arduino have a common GND ? How are you powering the Arduino and Rx ? Put some Serial.print()s in the code (but not in the ISRs) so you know which sections of code are being executed and the value of relevant variables at that point.

Everything is wired up how it should be, Arduino powering the receiver completely. (works with old method)

Serial.println (CH2_delta);
Serial.println(CH1_delta);

Ch2 and Ch1 never change when I power up the remote, but when I used the old method I was able to get ranges from 1000-2000

It's not running the set up

void init_rc_rx(){
  
  Serial.print("Channel 1 connected to pin number....\t");
  Serial.println(CH1_pin);
  Serial.print("Channel 2 connected to pin number....\t");
  Serial.println(CH2_pin);
  
  pinMode(CH1_pin, INPUT);
  pinMode(CH2_pin, INPUT);

  attachInterrupt(CH1_int, CH1_int_ISR, CHANGE);
  attachInterrupt(CH2_int, CH2_int_ISR, CHANGE);

}


//========
void setup() {

   pinMode (channel1, INPUT);// initialises the channels
   pinMode (channel2, INPUT);// as inputs
   init_rc_rx();
 Serial.begin(115200);
 ST1.attach(8);
 ST2.attach(7);
}

So I just moved the code from the init into the set up and that seems to work, still not getting any results though.

//========
void CH1_int_ISR()
{
  if ((micros()-CH1_t) < valid_pulse_limit){
    CH1_delta = micros()-CH1_t;
  }
  CH1_t = micros();
}

void CH2_int_ISR()
{
  if ((micros()-CH2_t) < valid_pulse_limit){
    CH2_delta = micros()-CH2_t;
  }
  CH2_t = micros();
}

// Call able function



//========
void setup() {

   pinMode (channel1, INPUT);// initialises the channels
   pinMode (channel2, INPUT);// as inputs
 Serial.begin(115200);
 ST1.attach(8);
 ST2.attach(7);
   
  Serial.print("Channel 1 connected to pin number....\t");
  Serial.println(CH1_pin);
  Serial.print("Channel 2 connected to pin number....\t");
  Serial.println(CH2_pin);
  
  pinMode(CH1_pin, INPUT);
  pinMode(CH2_pin, INPUT);

  attachInterrupt(CH1_int, CH1_int_ISR, CHANGE);
  attachInterrupt(CH2_int, CH2_int_ISR, CHANGE);

}

It has to be an error somewhere in calling the attachInterrupt

It's not running the set up

That's because you are not calling it.

Did you move the function and/or have you put code in to call it ?

UPDATE: Pins had to be changed to the correct interrupt pins. Its working just fine other than when I turn the RC controller off it doesn't clear the value of the controller to resume the program. Any ideas?

// -----LIBRARIES

#include <Servo.h>
#define CH1_int 0    // Channel 1 interrupt # 
#define CH1_pin 2     // Respective channel Hardware interrupt pin number

#define CH2_int 1     // Channel 2 interrupt # 
#define CH2_pin 3     // Respective channel Hardware interrupt pin number

#define valid_pulse_limit 2000 // [uS] Valid output high pulse time limit for RC controller
#define max_high_time 2300 // [uS] Maximum expected high time
#define min_high_time 500 // [uS] Minimum expected high time
//

int b = 3.333; //This is distance in feet forward
int a = 2; 

//    a = Speed
// 1 = Fast forward
// 2 = medium forward
// 3 = slow forward **time fix
// 4 == slow back **time fix
// 5 == medium back
// 6 == fast back

int power_mapping [] = { 0, 160, 135, 110, 70, 50, 30 }; //Speed Settings
int power_mappingReversed [] = { 0, 30, 50, 70, 110, 135, 160 }; //Speed Settings
float timer_mapping [] = { 0, 131, 160, 1245, 1131, 150, 137 }; //Speed in Feet Per Second
float timer_mappingReversed [] = { 0, 137, 150, 1131, 1245, 160, 131 }; //Speed in Feet Per Second
//160(0546) 135(1139) 110(4150) 70(3770) 50(1298) 30(0690)

int channel1 = 2; // defines the channels that are connected
int channel2 = 3;// to pins 9 and 10 of arduino respectively
int analogOutputF ; //Used later to
int analogOutputB ; // store values
int Channel1 ; // Used later to 
int Channel2 ; // store values
int switchingServo = 0;
volatile unsigned long CH1_t=0, CH1_delta=0, CH2_t=0, CH2_delta=0 ;
float CH1,CH2;
unsigned long t=0;
//------- VARIABLES (will change)

Servo ST1;
Servo ST2;  
int servoInt = 0;          //    will be changed to negative value for movement in the other direction
unsigned long currentMillis = 0;    // stores the value of millis() in each iteration of loop()
unsigned long previousServoMillis = 0; // the time when the servo was last moved

//========
void CH1_int_ISR()
{
  if ((micros()-CH1_t) < valid_pulse_limit){
    CH1_delta = micros()-CH1_t;
  }
  CH1_t = micros();
}

void CH2_int_ISR()
{
  if ((micros()-CH2_t) < valid_pulse_limit){
    CH2_delta = micros()-CH2_t;
  }
  CH2_t = micros();
}

// Call able function



//========
void setup() {

   pinMode (channel1, INPUT);// initialises the channels
   pinMode (channel2, INPUT);// as inputs
   Serial.begin(115200);
   ST1.attach(8);
   ST2.attach(7);
   
  Serial.print("Channel 1 connected to pin number....\t");
  Serial.println(CH1_pin);
  Serial.print("Channel 2 connected to pin number....\t");
  Serial.println(CH2_pin);
  
  pinMode(CH1_pin, INPUT);
  pinMode(CH2_pin, INPUT);

  attachInterrupt(CH1_int, CH1_int_ISR, CHANGE);
  attachInterrupt(CH2_int, CH2_int_ISR, CHANGE);


}


//=======

void loop() {

currentMillis = millis();
  Serial.println(CH1_delta);
 // Serial.println(CH2_delta);

if (CH1_delta == 0 && CH2_delta == 0){

 if (currentMillis - previousServoMillis >= servoInt) {
      previousServoMillis = currentMillis;

    if (switchingServo == 0){
      switchingServo = 1;
      ST1.write(power_mapping [a]);
      servoInt = (b*timer_mapping [a]);
      Serial.println(servoInt);

    }else if (switchingServo == 1){
      switchingServo = 2;
      ST1.write(90);
      servoInt = (1000);
      Serial.println(servoInt);

    }else if (switchingServo == 2){
      switchingServo = 3;
      ST1.write(power_mappingReversed [a]);
      servoInt = (b*timer_mappingReversed [a]);
      Serial.println(servoInt); 
      
    }else if (switchingServo == 3){
      switchingServo = 0;
      ST1.write(90);
      servoInt = (1000);
      Serial.println(servoInt);

    }}
  }else{
       
   previousServoMillis = 0; 
   servoInt = 0;
   switchingServo = 0;

   
     //BACK MOTOR
   analogOutputF = map(CH2_delta, 990, 1900, 0, 180); 
    ST1.write(analogOutputF);
     Serial.println (CH2_delta);
     Serial.println(CH1_delta);
      //STEERING
   analogOutputB = map(CH1_delta, 1260, 1660, 0, 180); 
   if (CH1_delta > 1500 && CH1_delta < 1700){
     ST2.write(180);
      //Serial.println (180);

   }else if (CH1_delta < 1400 && CH1_delta < 1300){
     ST2.write(0);
      //Serial.println (0);

   }else if (CH1_delta > 1400 && CH1_delta < 1500){
     ST2.write(90);
      //Serial.println (110);
   
    }
  }
}



//=====END

What do your servos do when you turn off the transmitter? AFAIK every receiver I have used just continued to keep the servos at the last set position after the the transmitter was powered off (maybe they made a little jump but did not go "limp"), the receiver should not ever output a "0" pulse to the servo. Therefore CH1_delta and CH2_delta will never equal 0 unless you turn off the receiver. You can test this yourself, make sure transmitter is off, turn on receiver, try to turn the servo by hand, if it resists the transmitter is still controlling it and still sending position data.

I honestly do not know a surefire method to detect if the transmitter is shut off, perhaps you could do something like store the value of CH1_delta in a variable (CH1_previousDelta) and the same for CH_2, in your loop you could compare current CH1_delta to CH1_previousDelta and do the same for CH2. Every time the value checked for both is equal to both previous values then increase a count, when the count reaches a certain point the Arduino assumes that since no changes were received in the time period that the transmitter is off. This solution would fail if you regularly would be keeping the controls for both channels in the same position for an extended time period. Sometimes it is easier to explain in code...

int count;   // in declarations
int ch1_previousDelta;
int ch2_previousDelta;
boolean transmitterOff;


//in loop()
if(CH1_delta == Ch1_pereviousDelta && CH2_delta == Ch2_previousDelta)
{
  count++;
}
else
{
Ch1_previousDelta = CH1_delta;
Ch2_previousDelta = CH2_delta;
transmitterOff = false;
count=0;
}

if(count>5000)
{
transmitterOff = true;
}

And replace if (CH1_delta == 0 && CH2_delta == 0){ with if (transmitterOff){

You would need to play with the value of "count" to determine how long it should count before it decides transmitter is off.

If you do not receive pulses for a couple of milliseconds then the Tx has stopped transmitting. Detect that and act on it.

every receiver I have used just continued to keep the servos at the last set position after the the transmitter was powered off

Many receivers have the ability to move the controls to a preset (failsafe) position if the transmitter signal is lost. We do not know which Rx the OP is using and whether it has this facility.