pulseIn() function returning semi random values every 23 - 25 seconds

I am making a flight computer for an rc plabe using an arduino uno rev3 for my computer science A level project, and have been expirencing a problem the pulseIn() function.

The flight controller uses the yaw (rudder) and throttle values in order to vary the thrust between two motors on an rc aircraft in order to create asymetrical thrust, allowing the aircraft to perform maneuvers otherwise impossible.

The Arduino is reading two PWM values from the reciver, one for the engine power, and another for the rudder. Every 23 - 25 seconds the input from both the rudder and throttle pins goes through a semi-predicatble pattern detailed below and in the Dif_thrust input.jpg graph.

  1. The input decreases, increase, decreases then increases again before spasming. This happens pretty much every cycle, and is noticable on lower throttle input settings.

  2. This is the stage that causes the most problems, where the input quickly changes from approximately the correct value to a lower calue. This lower value increases for a period, before dropping suddenly and increasing again.

  3. The input increases gradually in two stepsbefore returning to the correct value.

This behaviour occurs no matter what throttle input, or if the throttle input is changing (see dif thrust changing input.jpg). The behaviour is slightly different for the rudder input, but follows a simialr pattern (see rudder input.jpg).

My Computer Science teacher and I have tried/discovered the following things:

  • regularly repeats every 23-25 seconds
  • using servo.write() vs servo.writeMicroseconds() has no observable difference
  • increasing the timeout for the pulseIn() function has no effect
  • removing one PWM input and/or adding a 100ms delay has no effect

Does anyone know either what is causing this issue or how to fix it? If you need any more details please ask.

Here is the code:

#include <Servo.h>




Servo eng1; //left engine (from pilots perspective)
Servo eng2; //right engine
Servo throttle;
Servo rudder_rec;
int eng1_pin = 3;
int eng2_pin = 4;
int throttle_pin = 13;
int rudder_pin = 12;
int throttle_input;
int rudder_input;
int eng1_output;
int eng2_output;


void setup() {
  Serial.begin(9600);
  eng1.attach(eng1_pin);
  eng2.attach(eng2_pin);
  pinMode(rudder_pin, INPUT);
  pinMode(throttle_pin, INPUT); 
  TIMSK0=0;
}


void loop() {
//taking input from the recivers
throttle_input = pulseIn(13,HIGH);
rudder_input = pulseIn(12, HIGH);


//Serial.println(rudder_input);
Serial.println(throttle_input);
eng1_output = dif_thrust1(throttle_input, rudder_input);
eng2_output = dif_thrust2(throttle_input, rudder_input);
//Serial.println(eng1_output);
//Serial.println(eng2_output);
eng1.write(eng1_output);
eng2.write(eng2_output);




}


//functions manipulating the throttle input and rudder input


//for eng2
int dif_thrust2(int throttle_input, int rudder_input){//works out power for first motor
  int a;


  //slows down engine 2 if 
  if (rudder_input > 1600){
  a = throttle_input - (rudder_input - 1600)*1.2;
  
  }
  
  //if the output is less than the minumum value, it is set to the minimum value
  if (a < 992){
    a=992;
  }
  
  //sets the output to the throttle input if the rudder input does not require eng1 to slow
  if (rudder_input <= 1600){
    a = throttle_input;
  }
  
  //between these values the motor doesnt have enough power to turn smoothly
  if (a < 1052 and a > 1010){
    return 1000;
  }
  else
  {
    return a;
  }
  
}


//for eng1
int dif_thrust1(int throttle_input, int rudder_input){//works out power for first motor
  int a;


  if (rudder_input < 1400){
  a = throttle_input + (rudder_input - 1400)*1.2;
  
  }
  if (a > 1970){
    a=1970;
  }
  if (a < 992){
    a=992;
  }
  if (rudder_input >= 1400){
    a = throttle_input;
  }
  if (a < 1052 and a > 1010){
    return 1000;
  }
  else
  {
    return a;
  }
}


//Throttle values
//min value = 992
//max value = 1999 
//neutral value = 1500
//min spin value = 1052
//no spin max value = 1010




//Rudder values
//neutral value = 1470 - 1510
//max value = 1940
//min value = 1000

a standard RC-signal creates a pulse every 20 milli-seconds.
Your code contains low-speed serial-output at 9600 baud. This means 9600 bits per second
So serial.printing four characters are 32 bits which needs 1/9600 * 32 = 3.3 milliseconds

The pulse-length of a RC-signal you want to measure by pulseIn() is 1 - 2 milliseconds long
so delaying your code for 3.3 milliseconds will make your code missing some pulses.
So it might be that when missing a pulse you measure 20000 microseconds. Just to be sure I would change
the variabletype from integer to unsigned long.

In general in a code that needs high perfomance and low latency and your code is
such a case you should avoid any things that delay your loop-executiontime

if you want to keep the serial output
You can give it a try by setting the baudrate to the max an arduino can do which is 2.000.000 baud
(see the difference to 9600 baud?)

You should measure the loop-execution-time through storing two snapsshots with function
micros() to see how long the execution time is.

For this job I would change from an arduino to a teensy 4.1 which has 32bits running at 600MHz.
A teensy 4.1 can be programmed with the arduino-IDE after installing additional files provided by the manufacturer of the teensys,

Or I would switch over to the propeller-chip from parallax.
This is a 8 core-microcontroller that is able to run code in real parallel execution.
Any single-core-microcontroller does high-speed sequential switching from task to task
A propeller-chip has up to 7 tasks running real parallel. So measuring the RC-pulse-length can be done
very accurate without beeing disturbed by any other code-execution while other code can be executed.

You can program a propeller-chip in SPIN which is very similar to C++ or in C++

Before you put a plane in the air:
be sure that you have thoroughly tested your code for any kind of exceptional situations
you can think of. Really any you can think of.

What does your code do if no rc-signal is received? (oopps!!! pulsein waits and waits and waits)
What does your code do if the rc-signal is right on the border between to states or is changing quickly above and below such a border-value?
etc. etc. etc.

For real flight-testing I would use a very light and very durable and cheap construction
You should get advice from experienced RC-pilots which airframe is most forgiving and almost unstallable.
There are huge differencies how easy or how hard a plane can be stalled.

or as an alternative a completely independend working parachute-rescue-system. Which means a second remote-control with a second receiver with its own power-supply. An assistant is permantly watching the airplanes behaviour with his fingers on the switch to pop.out the parachute as soon as something strange happens.

Or a special kind of receiver with an automatic rescue function:
The best auto-rescue-system is made by Horizon-hobby Spektrum. It is called SAFE S)ensor A)ssisted F)light E)nvelope
So to test your system and beeing able to switch to this second system I would set it up like this:

There is a watchdog-timer that keeps two parallel multicontact-relays under power to connect the servos to your flight-controller
Whenever the watchdog-timer times out the relay's current is switched off and the servos get connected to the SAFE-receiver which will do automatically created servo-commands to bring back the plane into flat level flight.

The Spektrum-SAFE-System is so perfomant that most of the time within 2 seconds maximum after 5 seconds the plane is back in flat level attitude and will keep it in this attitude until you push/pull the sticks on the second remote-control.
This SAFE-receiver will even keep your plane
in a controlled attitude if you have full signal-loss. It will cut throttle off and will circle down the plane in big circles.
This receiver is equiped with G-sensors like your smartphone to sense its attitude compared against gravity.
And this enables to control the planes attitude by the receiver itself.
This receiver is type AR637 from Spektrum.

I'm a RC-pilot myself who has quite a lot experience with this SAFE-system.

I would comment out this line to see if that helps.

  TIMSK0=0;

If you turn off the Timer0 overflow interrupt you will have problems if you use any Arduino libraries that rely on millis() or micros(). The pulseIIn() function doesn't seem to (unless you use pulseInLong()) so I don't think it is a DIRECT cause of your glitches but may be an indirect cause somehow.