Project I am working on including servo control and reading a rc receiver

Hi guys'

Currently I am working on a small Rc project using a Arduino Nano. The project requires a small form factor therefore the Nano. Keep in mind that I am fairly new to Arduino but have a basic training in other programming languages such as java, visual basic, ..

The purpose of the the program is to interpret information from a standard Rc receiver, processes the information and use it to control a servo. I started from a from I found on rcaruino to interpret the information coming from the receiver using interrupts to minimize the amount of data processed by the arduino.

When the throttle of the Rc receiver is pushed the servo controlled by one of the channels should wait until a certain value is reached and then rotate a certain amount of degrees.
When pressed even further (full throttle) the servo should move even further by a set amount of degrees.
However as the first value is reached a certain delay about 1s should pass before moving the servo, the same when passed the second value

When giving full brake (or reverse) the servo should rotate in the other direction a set amount of degrees instantly so there can't be any delay.

The values for full brake and throttle I tested using serial monitor so these are correct.

Next I will show you the code I have so far

#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

#define NEUTRAL_THROTTLE 1500 // this is the duration in microseconds of neutral throttle on an electric RC Car
#include <Servo.h>

Servo myservo; // create servo object to control a servo
// twelve servo objects can be created on most boards

int pos = 0; // variable to store the servo position
volatile int nThrottleIn = NEUTRAL_THROTTLE; // volatile, we set this in the Interrupt and read it in loop so it must be declared volatile
volatile unsigned long ulStartPeriod = 0; // set in the interrupt
volatile boolean bNewThrottleSignal = false; // set in the interrupt and read in the loop
// we could use nThrottleIn = 0 in loop instead of a separate variable, but using bNewThrottleSignal to indicate we have a new signal
// is clearer for this first example

void setup()
{
// tell the Arduino we want the function calcInput to be called whenever INT0 (digital pin 2) changes from HIGH to LOW or LOW to HIGH
// catching these changes will allow us to calculate how long the input pulse is
attachInterrupt(THROTTLE_SIGNAL_IN,calcInput,CHANGE);

Serial.begin(9600);

myservo.attach(9); // attaches the servo on pin 9 to the servo object
myservo.write(0);
}

void loop()
{
// if a new throttle signal has been measured, lets print the value to serial, if not our code could carry on with some other processing
if(bNewThrottleSignal)
{

Serial.println(nThrottleIn);

// set this back to false when we have finished
// with nThrottleIn, while true, calcInput will not update
// nThrottleIn
bNewThrottleSignal = false;
}

if((nThrottleIn) < 1400) // interval for braking
{

myservo.write(140); // tell servo to go to position 140 degrees

}

if((nThrottleIn) > 1400 && (nThrottleIn) < 1600 ) //interval for neutral
{
myservo.write(90); // tell servo to go to position neutral

if((nThrottleIn) > 1600 && (nThrottleIn) < 1800 ) // interval for first throttle
{
delay(1000);
myservo.write(20); // tell servo to go to position 20 degrees

}
if((nThrottleIn) > 1800 && (nThrottleIn) < 1900 )// interval for second throttle
{
delay(1000);
myservo.write(50); // tell servo to go to position 50 degrees
// waits 15ms for the servo to reach the position
}
}
if((nThrottleIn) > 1900) // interval for third throttle
{
delay(500);
myservo.write(90); // tell servo to go to position 90 degrees

}

}

void calcInput()
{
// if the pin is high, its the start of an interrupt
if(digitalRead(THROTTLE_SIGNAL_IN_PIN) == HIGH)
{
// get the time using micros - when our code gets really busy this will become inaccurate, but for the current application its
// easy to understand and works very well
ulStartPeriod = micros();
}
else
{
// if the pin is low, its the falling edge of the pulse so now we can calculate the pulse duration by subtracting the
// start time ulStartPeriod from the current time returned by micros()
if(ulStartPeriod && (bNewThrottleSignal == false))
{
nThrottleIn = (int)(micros() - ulStartPeriod);
ulStartPeriod = 0;

// tell loop we have a new signal on the throttle channel
// we will not update nThrottleIn until loop sets
// bNewThrottleSignal back to false
bNewThrottleSignal = true;
}
}
}

Now I noticed that It works partly but still some bugs like it doesn't seem to brake?
I also noticed that in the example program of the servo that the program a delay to wait until the servo reaches its position, but is this necessary?

In the future I would like to adjust the program so that movement is gradually(not full speed from a certain degree to an other) so maybe incorporating a loop with a smaller delay between each degree increment but don't know exactly how to do this. Still the time from neutral to full throttle should take approximately 2,5 Seconds. Also I noticed that servo movement is small no where near 180 degrees no matter which degree I enter.
If you could help me I would be grateful.

Those delay()s will cause problems in the future and probably now because nothing can happen while they occur. You should consider changing to millis() for timing. Save the time an event happens then every time through loop() test whether the required period has elapsed and if so act on it by changing to a new state.

Your program has a number of states within it so would lend itself to implementing as a state machine. Give each state a number then use switch/case to execute only the code for the current state, such as testing whether a time period has elapsed.

Outside of the switch/case you can read inputs etc and react to them quickly by perhaps changing to a different state.

Life is so much easier for everyone if you use code tags

#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

#define NEUTRAL_THROTTLE 1500 // this is the duration in microseconds of neutral throttle on an electric RC Car
#include <Servo.h>
 
Servo myservo;  // create servo object to control a servo
                // twelve servo objects can be created on most boards
 
int pos = 0;    // variable to store the servo position
volatile int nThrottleIn = NEUTRAL_THROTTLE; // volatile, we set this in the Interrupt and read it in loop so it must be declared volatile
volatile unsigned long ulStartPeriod = 0; // set in the interrupt
volatile boolean bNewThrottleSignal = false; // set in the interrupt and read in the loop
// we could use nThrottleIn = 0 in loop instead of a separate variable, but using bNewThrottleSignal to indicate we have a new signal
// is clearer for this first example

void setup()
{
  // tell the Arduino we want the function calcInput to be called whenever INT0 (digital pin 2) changes from HIGH to LOW or LOW to HIGH
  // catching these changes will allow us to calculate how long the input pulse is
  attachInterrupt(THROTTLE_SIGNAL_IN,calcInput,CHANGE);

  Serial.begin(9600);

  myservo.attach(9);  // attaches the servo on pin 9 to the servo object
myservo.write(0);
}


void loop()
{
	 // if a new throttle signal has been measured, lets print the value to serial, if not our code could carry on with some other processing
	 if(bNewThrottleSignal)
	 {

	   Serial.println(nThrottleIn); 

	   // set this back to false when we have finished
	   // with nThrottleIn, while true, calcInput will not update
	   // nThrottleIn
	   bNewThrottleSignal = false;
	 }
	 
	if((nThrottleIn) < 1400) // interval for braking
	{
	 
		myservo.write(140);              // tell servo to go to position 140 degrees
						   
	}

	 if((nThrottleIn) > 1400 && (nThrottleIn) < 1600 ) //interval for neutral
	{
		myservo.write(90);              // tell servo to go to position neutral
							 
							 
		if((nThrottleIn) > 1600 && (nThrottleIn) < 1800 ) // interval for first throttle
	{
		delay(1000);
		myservo.write(20);              // tell servo to go to position 20 degrees
							 
	}
	if((nThrottleIn) > 1800 && (nThrottleIn) < 1900 )// interval for second throttle
	{
		delay(1000);
		myservo.write(50);              // tell servo to go to position 50 degrees
							  // waits 15ms for the servo to reach the position
	}
	}
	if((nThrottleIn) > 1900) // interval for third throttle
	{
		delay(500);
		myservo.write(90);              // tell servo to go to position 90 degrees
					   
}

}

void calcInput()
{
  // if the pin is high, its the start of an interrupt
  if(digitalRead(THROTTLE_SIGNAL_IN_PIN) == HIGH)
  {
    // get the time using micros - when our code gets really busy this will become inaccurate, but for the current application its
    // easy to understand and works very well
    ulStartPeriod = micros();
  }
  else
  {
    // if the pin is low, its the falling edge of the pulse so now we can calculate the pulse duration by subtracting the
    // start time ulStartPeriod from the current time returned by micros()
    if(ulStartPeriod && (bNewThrottleSignal == false))
    {
      nThrottleIn = (int)(micros() - ulStartPeriod);
      ulStartPeriod = 0;

      // tell loop we have a new signal on the throttle channel
      // we will not update nThrottleIn until loop sets
      // bNewThrottleSignal back to false
      bNewThrottleSignal = true;
    }
  }
}

You should use the value from nThrottleIn slightly differently so it does not change in the middle of loop()

Save it to another variable and use that rather than the variable that is updated by the interrupt. Also temporarily stop interrupts while you read the value. For example

noInterrupts();
latestThrottle = nThrottleIn;
interrupts();

And use millis() rather than delay() to manage your timing, as illustrated in several things at a time

...R

Ok will give the millis() function a go and rework it into a state machine.
And sorry Robin, didn't know i could do that thanks :).

thanks for the help!

PhilippeNivelle:
didn't know i could do that thanks :).

There is a lot of useful information to be had if you read several Threads on the Forum and not just your own.

...R