Saturday, April 14, 2012

How To Read Multiple RC Channels

This post builds on previous posts to show a technique for reading multiple radio control receiver channels using an Arduino.

Update 27/03/2013 -  The Arduino Leonardo and Micro use the ATMega 32u4 chip which supports interrupts on fewer pins than the ATMega 328 used in Arduino UNOs and Minis. The pins used by the sketch have been rearranged so that the code can now be run the Leonardo and Micro as well.

If you have previously used the code, please ensure that you update your connecteds by swapping the input and output pins.


The following posts provide the necessary background to this post -

In the first post in this series we looked at the nature of an RC Receiver signal and provided a non blocking interrupt based approach for reading this signal with Arduino -

In the next post we looked at the Arduino servo library and how this allows us to generate the same signals that a radio control receiver generates. We can use this library to drive upto 12 servos or electronic speed controllers -
In the final post, we looked at the pinchangeint library which provides a convenient mechanism to access over twenty interrupts with a standard Arduino UNO. This overcomes the limitation of having only two external interrupts and allows us to use a standard Arduino to read more than twenty RC Channels -


Reading RC Channel Signals

Reading a single RC channel is relatively simple and can be outlined as -

1) Attach a pin change interrupt to the signal pin.
2) Create an interrupt service routine which will be called whenever the signal pin changes from high to low or low to high.

In the interrupt service routine we need to -

1) Check if the signal pin is high or low
2) If its high, this is the rising edge of the pulse, record the time using micros()
3) If its low, this is the falling edge. We are interested in the pulse duration, so if we call micros() to get the current time and then subtract from it the time we recorded in 2) for the rising edge we get the pulse duration.

The pulse duration calculated in 3) Above represents the input signal for the channel, refer to this post for a refresher - http://rcarduino.blogspot.co.uk/2012/01/how-to-read-rc-receiver-with.html

To attach the interrupt using the pin change interrupt library we call -

PCintPort::attachInterrupt(THROTTLE_IN_PIN, calcThrottle,CHANGE);

The interrupt service routine can be written as follows -

void calcThrottle()
{
  // if the pin is high, its a rising edge of the signal pulse,
  // so lets record its value
  if(digitalRead(THROTTLE_IN_PIN) == HIGH)
  {
    ulThrottleStart = micros();
  }
  else
  {
    // else it must be a falling edge, so lets get the time and subtract the time
    // of the rising edge this gives use the time between the rising and falling 
    // edges i.e. the pulse duration.
    unThrottleInShared = (uint16_t)(micros() - ulThrottleStart);

  }
}

So all we need to do is cut and paste the attachInterrupt and interrupt service routine code three times to read three channels ?

No. Its not that simple, but its really not very complicated either.

There are four considerations which apply when using interrupts -

1) Volatile Shared Variables
2) Variable Access
3) Fast Interrupt Service Routines
4) Timing

1) Volatile Shared Variables

When you upload a program with the Arduino IDE, your code will be compiled into an executable program that can be run on your Arduino. As part of this process the compiler will optimise the executable code to be smaller and/or faster. If we are not careful, this optimization can have unexpected results when using interrupts.

Example optimisations -

1) Replacing variables with constants
One of the optimisations a compiler will attempt is to look through your functions for any variables that are not being updated, the compiler can replace these with constant values.

2) Using registers to hold variables within functions
Another optimisation is to assign a variable to a register within a function rather than reading it from memory repeatedly.



The compiler makes optimisation decisions based on blocks of code, not entire projects. A common optimisation problem with interrupts occurs when a variable is updated in an interrupt service routine and read in the main code. Here the compiler may look at the main code and decide 'hey this variable never gets updated so I am going to replace it with a constant value'. In this case whatever the ISR does to the shared variable, your main code will never be effected because as far as the compiler could see, there was nothing that could update the variable within loop and so the executable code that was downloaded to you Arduino was generated to refer to a constant value - see 1) Above. 

A similar situation occurs when a variable is optimised to be held in a register rather than read from memory. One part of your executable code will be reading a value held in memory while another part will be updating a value held in a register. The end effect is that it appears as if your variable is not getting updated despite your ISR being called correctly and your code appearing correct - see 2) above.

Fortunately there is a keyword we can use in our code which tells the compiler 'never try and store this variable in a register, or replace it with a constant' the keyword is volatile. Any variables we wish to access from both our main code and an interrupt service routine must be declared volatile.

Really ?

Yes, I recently built an Audino, its an easy to build, fun to use synthesizer based on Arduino. The loop function reads five analogue inputs which control the sound. These variables are also used in a timer based interrupt service routine which actually creates the sound.

When I first built the project it didn't work. Reading through the source code I noticed that the analog inputs read in loop and used in the ISR were not declared as volatile. Adding the volatile declaration to these five variables fixed the problem.

Interestingly, the Audino has been around for years, it is likely that it worked perfectly with earlier versions of the Arduino compiler, but newer version may be optimizing differently. Whatever the case, the volatile keyword should always be used to tell the compiler not to optimize our shared variables.


In our code we must declare the channel input values as volatile in order that we can update them in the ISR and read them in loop -

volatile uint16_t unThrottleInShared;
volatile uint16_t unSteeringInShared;
volatile uint16_t unAuxInShared;


2) Variable Access

New Arduino programmers are often aware of the volatile keyword and assume that it is all that is needed to access shared variables. It isn't.

An interrupt service routine can be run at any point in your main code. Its called an interrupt because it interrupts your main code to do something else. This can lead to strange and difficult to trace errors if you do not control when your variables are being accessed.

How do these strange and difficult to trace errors happen ?

The simplest explanation is that if your main code is reading a variable when it is interrupted and the interrupting service routine changes the variable value, your main code will be left with part of the old value and part of the new value which is not what you intended or your code expected. This is often experienced as glitches blamed on hardware, but is in fact an easily solved software problem.

As a simple example a two byte integer changing from 256 to 255 in an interrupt service routine could be seen in loop as 511.

                                      Byte 1      Byte 2
Original value = 256 = 00000001 00000000
Updated value = 255 = 00000000 11111111

Loop reads byte 1 = 00000001

The ISR is called and updates the 2 byte integer from 256 to 255

Loop reads byte 2 = 11111111

Loop sees byte 1 + byte 2 as integer 0000000111111111 = 511

This unexpected value is a result of reading byte 1 before the interrupt and byte 2 after the interrupt. We must always ensure that our multi byte shared variables are read, updated or compared without any interrupts occuring.

To prevent this problem we need to ensure that whenever we are reading, writting or comparing a values larger than a single byte we cannot be interrupted until the operation is complete. The data types int, long and float are all multi byte data types ( int = 2 bytes, long = 4 bytes, float = 4 bytes) and therefore access must be controlled between the your main code and loop. The recommended approach to controlling access is to temporarily disable interrupts in order that your main code can perform a read, write or compare without being interrupted (remember the danger is that the interrupt will change part of the value that we have already read,written or compared in which case the results will be unpredictable and most likley lead to program errors - disabling interrupts for the read, write or compare prevents this).
  
To disable interrupts we call nointerrupts(), to enable then again we call interrupts().

// turn interrupts off 
noInterrupts();
    
    // Access your shared variables here ...

// finished with the shared variables, so turn interrupts back on
interrupts();

Note - While interrupts are disabled we will not be able to read incoming signals and this can effect our accuracy. See the example code where local variables as used to minimize the time that interrupts are disabled.
Using the status register directly vs interrupts() and noInterrupts()

Note - There is some criticism of noInterrupts and interrupts on the basis that the interrupts function reenables interrupts regardless of whether they were enabled or disabled before noInterrupts was called. This could cause problems where your code is called by a library which requires interrupts to be disabled. The preferred technique is to record the global status register, update the status register to disable interrupts, then restore it to its previous value - this maintains the status that was inplace before your code rather than blindly (and unsafely) restoring interrupts.

byte sregRestore = SREG;
cli() // clear the global interrupt enable flag

// Access your shared variables here ....

SREG = sregRestore; // restore the status register to its previous value


3) Fast Interrupt Service Routines

When an interrupt is triggered the microprocessor disables further interrupts before calling the interrupt service routine. While we are inside our interrupt service routine, we cannot process any new incoming events, therefore it is important for the accuracy of our input signal that we keep our interrupt service routines as short as possible.

As a guideline the interrupt service routine should record the occurrence of an event, but should not attempt to perform any extended processing in response to the event.

In our example of reading three receiver channels and outputting 3 servo signals we are processing 500 interrupts per second. The difference between an idle throttle and full throttle is only 0.5 thousandths of a second - timing is going to be important.

If we want our main code to process an event, we need some mechanism for the interrupt service routine to flag that an event has occurred, one option is the use of bit flags.

Bit Flags

We can use bit flags to signal to our main code that new information is available from an ISR. Bit flags take advantage of the fact that a micro processor can read a single byte without being interrupted.

How do but flags work ?

If we treat a byte as being made up of eight bits, b0 to b7, we can use each bit as a signal between our main code and ISRs.The bits have the decimal values 1,2,4,8,16,32,64,128 which we can use to set, test and clear the individual bit flags.

// These bit flags are set in bUpdateFlagsShared to indicate which
// channels have new signals
#define THROTTLE_FLAG 1
#define STEERING_FLAG 2
#define AUX_FLAG 4

// a single byte we can use to hold the update bit flags defined above
volatile uint8_t bUpdateFlagsShared;

to test, set and unset bit flags we can use -

// test to see if a flag has been set - use the bitwise AND operator &
if(bUpdateFlags & THROTTLE_FLAG)
{
   // flag was set ...
}

// set a bit flag - use the bitwise OR operator |
// set the throttle flag to indicate that a new throttle signal has been received
bUpdateFlagsShared |= THROTTLE_FLAG;

// clear a bit flag - use the bitwise XOR operator ^
bUpdateFlagsShared ^= THROTTLE_FLAG;


Bit flags provide us with a simple mechanism to communicate new events between the ISRs and loop. This is particularly important where we need to perform an extended calculation in response to the event. In the example code, we use the bit flag to tell loop that a new signal is available from a channel, loop then uses this information to updated the output for the channel.

4) Timing

We have already covered two of the issues which will effect our accuracy, we can add library functions for a more complete list as presented below -

1) Library functions that temporarily disable interrupts - some library functions will temporarily disable interrupts we cant control this but we can be careful about where we use these functions
2) Disabling interrupts in our own code - we will need to do this, but we can take care to minimise the time that interrupts are disabled.
3) Interrupt Service Routines - the microprocessor automatically disables interrupts before calling them and reenables them afterwards. Again we have to live with this but can be careful to minimize the time spent inside the ISR so that interrupts are enabled again as quickly as possible.

Putting it all together

The following code sample can be used to read three RC Channels through one set of Arduino pins and output those same channels on another set of pins. The code uses dedicated interrupt service routines for each channel, this is for clarity and could be changed to use a single interrupt service routine for all channels.

The code demonstrates the use of -

 - Bit flags to communicated events between the ISRs and Loop
 - Interrupt service routines to read rc channel signals
 - Enabling and disabling interrupts
 - Volatile shared variables
 - Taking local copies of variables to minimise the time that interrupts are disabled
 - Using the servo library to output RC Channels to servos or electronic speed controllers
 - Using the pin change interrupt library to access additional interrupts.

The sample code below the video was used in creating the video. A step by step guide to connecting the ESC, Steering servo, RC Receiver and Arduino will be provided in a followup post.

video

With two channels - throttle and steering - being read from the receiver and output to the car by Arduino

video

Sample Code - MultiChannels rcarduino.blogspot.com

// MultiChannels
//

// rcarduino.blogspot.com
//
// A simple approach for reading three RC Channels using pin change interrupts
//
// See related posts -
// http://rcarduino.blogspot.co.uk/2012/01/how-to-read-rc-receiver-with.html
// http://rcarduino.blogspot.co.uk/2012/03/need-more-interrupts-to-read-more.html
// http://rcarduino.blogspot.co.uk/2012/01/can-i-control-more-than-x-servos-with.html
//
// rcarduino.blogspot.com
//

// include the pinchangeint library - see the links in the related topics section above for details
#include <PinChangeInt.h>

#include <Servo.h>

// Assign your channel in pins
#define THROTTLE_IN_PIN 8
#define STEERING_IN_PIN 9
#define AUX_IN_PIN 10

// Assign your channel out pins
#define THROTTLE_OUT_PIN 5
#define STEERING_OUT_PIN 6
#define AUX_OUT_PIN 7

// Servo objects generate the signals expected by Electronic Speed Controllers and Servos
// We will use the objects to output the signals we read in
// this example code provides a straight pass through of the signal with no custom processing
Servo servoThrottle;
Servo servoSteering;
Servo servoAux;

// These bit flags are set in bUpdateFlagsShared to indicate which
// channels have new signals
#define THROTTLE_FLAG 1
#define STEERING_FLAG 2
#define AUX_FLAG 4

// holds the update flags defined above
volatile uint8_t bUpdateFlagsShared;

// shared variables are updated by the ISR and read by loop.
// In loop we immediatley take local copies so that the ISR can keep ownership of the
// shared ones. To access these in loop
// we first turn interrupts off with noInterrupts
// we take a copy to use in loop and the turn interrupts back on
// as quickly as possible, this ensures that we are always able to receive new signals
volatile uint16_t unThrottleInShared;
volatile uint16_t unSteeringInShared;
volatile uint16_t unAuxInShared;

// These are used to record the rising edge of a pulse in the calcInput functions
// They do not need to be volatile as they are only used in the ISR. If we wanted
// to refer to these in loop and the ISR then they would need to be declared volatile
uint32_t ulThrottleStart;
uint32_t ulSteeringStart;
uint32_t ulAuxStart;

void setup()
{
  Serial.begin(9600);
 
  Serial.println("multiChannels");

  // attach servo objects, these will generate the correct
  // pulses for driving Electronic speed controllers, servos or other devices
  // designed to interface directly with RC Receivers 
  servoThrottle.attach(THROTTLE_OUT_PIN);
  servoSteering.attach(STEERING_OUT_PIN);
  servoAux.attach(AUX_OUT_PIN);

  // using the PinChangeInt library, attach the interrupts
  // used to read the channels
  PCintPort::attachInterrupt(THROTTLE_IN_PIN, calcThrottle,CHANGE);
  PCintPort::attachInterrupt(STEERING_IN_PIN, calcSteering,CHANGE);
  PCintPort::attachInterrupt(AUX_IN_PIN, calcAux,CHANGE);
}

void loop()
{
  // create local variables to hold a local copies of the channel inputs
  // these are declared static so that thier values will be retained
  // between calls to loop.
  static uint16_t unThrottleIn;
  static uint16_t unSteeringIn;
  static uint16_t unAuxIn;
  // local copy of update flags
  static uint8_t bUpdateFlags;

  // check shared update flags to see if any channels have a new signal
  if(bUpdateFlagsShared)
  {
    noInterrupts(); // turn interrupts off quickly while we take local copies of the shared variables

    // take a local copy of which channels were updated in case we need to use this in the rest of loop
    bUpdateFlags = bUpdateFlagsShared;
   
    // in the current code, the shared values are always populated
    // so we could copy them without testing the flags
    // however in the future this could change, so lets
    // only copy when the flags tell us we can.
   
    if(bUpdateFlags & THROTTLE_FLAG)
    {
      unThrottleIn = unThrottleInShared;
    }
   
    if(bUpdateFlags & STEERING_FLAG)
    {
      unSteeringIn = unSteeringInShared;
    }
   
    if(bUpdateFlags & AUX_FLAG)
    {
      unAuxIn = unAuxInShared;
    }
    
    // clear shared copy of updated flags as we have already taken the updates
    // we still have a local copy if we need to use it in bUpdateFlags
    bUpdateFlagsShared = 0;
   
    interrupts(); // we have local copies of the inputs, so now we can turn interrupts back on
    // as soon as interrupts are back on, we can no longer use the shared copies, the interrupt
    // service routines own these and could update them at any time. During the update, the
    // shared copies may contain junk. Luckily we have our local copies to work with :-)
  }
 
  // do any processing from here onwards
  // only use the local values unAuxIn, unThrottleIn and unSteeringIn, the shared
  // variables unAuxInShared, unThrottleInShared, unSteeringInShared are always owned by
  // the interrupt routines and should not be used in loop
 
  // the following code provides simple pass through
  // this is a good initial test, the Arduino will pass through
  // receiver input as if the Arduino is not there.
  // This should be used to confirm the circuit and power
  // before attempting any custom processing in a project.
 
  // we are checking to see if the channel value has changed, this is indicated 
  // by the flags. For the simple pass through we don't really need this check,
  // but for a more complex project where a new signal requires significant processing
  // this allows us to only calculate new values when we have new inputs, rather than
  // on every cycle.
  if(bUpdateFlags & THROTTLE_FLAG)
  {
    if(servoThrottle.readMicroseconds() != unThrottleIn)
    {
      servoThrottle.writeMicroseconds(unThrottleIn);
    }
  }
 
  if(bUpdateFlags & STEERING_FLAG)
  {
    if(servoSteering.readMicroseconds() != unSteeringIn)
    {
      servoSteering.writeMicroseconds(unSteeringIn);
    }
  }
 
  if(bUpdateFlags & AUX_FLAG)
  {
    if(servoAux.readMicroseconds() != unAuxIn)
    {
      servoAux.writeMicroseconds(unAuxIn);
    }
  }
 
  bUpdateFlags = 0;
}


// simple interrupt service routine
void calcThrottle()
{
  // if the pin is high, its a rising edge of the signal pulse, so lets record its value
  if(digitalRead(THROTTLE_IN_PIN) == HIGH)
  {
    ulThrottleStart = micros();
  }
  else
  {
    // else it must be a falling edge, so lets get the time and subtract the time of the rising edge
    // this gives use the time between the rising and falling edges i.e. the pulse duration.
    unThrottleInShared = (uint16_t)(micros() - ulThrottleStart);
    // use set the throttle flag to indicate that a new throttle signal has been received
    bUpdateFlagsShared |= THROTTLE_FLAG;
  }
}

void calcSteering()
{
  if(digitalRead(STEERING_IN_PIN) == HIGH)
  {
    ulSteeringStart = micros();
  }
  else
  {
    unSteeringInShared = (uint16_t)(micros() - ulSteeringStart);
    bUpdateFlagsShared |= STEERING_FLAG;
  }
}

void calcAux()
{
  if(digitalRead(AUX_IN_PIN) == HIGH)
  {
    ulAuxStart = micros();
  }
  else
  {
    unAuxInShared = (uint16_t)(micros() - ulAuxStart);
    bUpdateFlagsShared |= AUX_FLAG;
  }
}

Coming soon - A walk through connecting the three radio channels to the Arduino and then the processed signals back out to a car, robot or other application.

Duane B

128 comments:

  1. Hi, I really like your code and I think you did a great job and it works perfectly. However, I have been reading through your code for the last few hours and I am having trouble figuring out how I could complete this task:

    I have an 6DOF IMU that is connected to my arduino and is outputting euler angles. I would like to be able to use the unAuxIn value to switch from manually controlling the servos with my transmitter to using the angles from the IMU to control the position of the servo. In essence all I think I might need would be an if statements that would say something like "if unAuxIn > 1800" but I don't know where this would go and I don't know if there is a better way to do this. I know this is a bit of a big question but I have been following you posts over the past few months and I think you are doing a great job. If you want to find out a bit more about the IMU I am using so that you can better understand my question, you can go to this website:

    http://bildr.org/2012/03/stable-orientation-digital-imu-6dof-arduino/

    ReplyDelete
  2. Hi,
    Thanks for the feedback.

    Your idea to test for AuxIn > 1800 is correct. I would add a new variable to track the current mode. I would set the mode to AUTO_PILOT if AuxIn > 1800 and to MANUAL_PILOT if AuxIn < 1200 - I assume that your auxiliary channel is a two position swicth like mine. You will then need to a new if condition which will only update the throttle and steering channel if mode = MANUAL_PILOT. As for what you do with the IMU, thats up to you.

    Duane B.

    ReplyDelete
  3. Hello,

    I'm a beginner and I'm trying your code...

    I'm surpised because the AUX channel has a very slow response time and I don't no why, do you think this is normal ?

    Sev

    ReplyDelete
  4. Hi,
    As a quick test, try swapping the connection of your Aux channel with one of the other channels, for example, connect your throttle channel to aux and aux to the throttle, is the response of aux still slow ? This will tell whether it is a transmitter setting or the problem is in the code.

    Duane B

    ReplyDelete
  5. Hello,

    I allready tested to move PIN attribution from :
    // Assign your channel in pins
    #define THROTTLE_IN_PIN 5
    #define STEERING_IN_PIN 6
    #define AUX_IN_PIN 7

    to :
    #define THROTTLE_IN_PIN 4
    #define STEERING_IN_PIN 5
    #define AUX_IN_PIN 6

    My receiver has been tested without Arduino, connected to 3 servo.
    I allready try to plug "Aux Servo" and "Steering Servo" one on each other.

    And this is allways the

    That's why I'm seriously thinking about a code specification on Aux Channel. (AUX_OUT_PIN 10)

    Maybe I'll try to put Steering-out on 10 and Aux-out on 9...

    ReplyDelete
  6. Hi,
    Can you clarify that it is always the AUX_OUT_PIN which is slow whatever is connected to it and can you be more specific about the slow response

    Thanks

    ReplyDelete
  7. I've tested changing output pin (9/10), supressing conditions flags... and this always happening with Aux Channel, but finaly I found something strange :

    This is mostly happening when Throttle Channel is at the middle point...
    Maybe the "GoingUp Aux signal" is "masked" by the "GoingDown Throttle signal", is this possible ?

    ReplyDelete
  8. Hi,
    one thing I wanted to eliminate is 'channel mixing' where your radio is set up to adjust the output of one channel based on another channel. What radio and receiver are you using and what do you have connected to the aux channel ?

    also, what type of vehicle ?

    Thanks

    Duane

    ReplyDelete
  9. One more thing, have you tried using the original code as it is posted here, but with the throttle input and output physically swapped with the aux input and output. If the code is causing the response this should now effect the throttle not aux.

    Duane b

    ReplyDelete
  10. I'm using Turnigy9CH-Tx with Eurgle3ch-Rx, for the moment, it is just for testing, so I just plug 2 servos (Steer&Aux) and 1 little 8A/ESC with type400 motor, (the ESC/BEC feed the servos 5V).

    As you said, Next test will be to swap PIN again (Thro & Aux, IN and OUT), if this is a masking effect, I should be fixed with this test (I'll try to post it on YouTube)

    ReplyDelete
  11. Hi, is it this radio, if so the link suggests that version 1 has problems

    http://www.hobbyking.com/hobbyking/store/__8992__Turnigy_9X_9Ch_Transmitter_w_Module_8ch_Receiver_Mode_2_v2_Firmware_.html

    Duane B

    ReplyDelete
  12. Link to manual - http://www.filadelfosrc.com/Manuals/TX-9X-M2-Manual.pdf

    ReplyDelete
  13. Hi,
    From the serial output you posted, it looks as if everything is working fine as far as the Arduino is concerned, the AUX Channel shows a steady input of 1468.

    I notice that you are using a 9 channel transmitter with a 3 channel receiver, my guess is that the receiver processes the first 3 pulses it receives and ignores any further pulses until the next frame begins. It is possible that what is happening is that your transmitter AUX signal is not within the first three pulses so the transmitter is receiving throttle, steering, and then some other channel which is not Aux. The Aux pulse is later in the frame and being ignored by the receiver.

    Try your test again but using the full range of controls/channels available on the transmitter, my guess is that one of them should cause the full range of movement in AUX.

    To keep the serial output short and clear, you can stop outputting columns 1 and 3 we are only interested in the channel value.

    Duane B.

    ReplyDelete
  14. Thank for your responses, My Rx/Tx system is fully fonctionnal AND programmable, I'm using V2 radio with custom firmware (er9x), so the original manual is not usefull for me.
    I'm using this system with planes, AR Drone (with MiruMod Arduino), and lots of little things I builded.

    Before this test, I've programmed my radio to send what I want on the first 3 Channels, the Rx is only reading 3 first Channels.

    When I connect everything directly on the Tx, everything is working fine, even the 3rd channel with a servo.

    On my serialOutput, please take a look at the numbers I just colored in red, I think this is the problem.
    The strange thing is that ulAuxStart is no more frequently updated BUT unAuxInShared continue to be (how is it possible ?)... if Arduino really do not see AuxGoingUp signal, unAuxInShared should be unrealist (too big) !!

    The servo lagging problem is occuring precisely at this moment.

    ReplyDelete
  15. Hi,
    Can you post your loop function or the ISR, whichever it is that you are generating the serial from

    Thanks

    ReplyDelete
  16. Solved ! I hope.

    I have just breadboarded a quick set up here and I am saddened to say I had exactly the same problem. Sometimes the Aux would respond instantly, other times it would be two or more seconds before Aux would respond.

    My test set up was Aux into the Arduino and out to a servo.

    As a second test, I connected the steering out of my receiver into the Aux in of my Arduino, the response was again lagging.

    Cut a long story short, I was missing pulses due to a bad connection. Looking at your trace it looks as if you are also missing pulses.

    To get my original connection to work I have to hold it into the receiver with hand pressure, like this there is no lag. If I take my hand off, the connection is not so good and I can expect to miss pulses resulting in what looks like lag.

    I hope this is the solution for you also, let me know.

    Duane

    ReplyDelete
  17. You were totally right !!

    My receiver do not have a real 3rd channel, I've done a test with the original 8ch Rx and everything is okay...

    I now understand why I crashed a fast plane with this kind of receiver...

    Thank you for all your help.

    Sev

    ReplyDelete
  18. i have a small question i am trying to control 2 DC motors using RC transmitter and receiver adn i want the motors to go forward backward and stop without controlling the speed or anything (will move with digital signal high or low only) and i dont know how to read the signal from the receiver and transform it to digital signal to drive the DC motors?

    ReplyDelete
  19. Try this -
    http://rcarduino.blogspot.com/2012/05/interfacing-rc-channels-to-l293d-motor.html

    Duane B

    ReplyDelete
  20. Hi,
    I've worked with your code on using a single RC signal and one interrupt and it worked fine for me. (http://rcarduino.blogspot.co.nz/2012/01/how-to-read-rc-receiver-with.html) But now that I'm reading this code on multiple inputs, I've tried to copy the code and try it straight away before playing around with it, but it comes up with an error saying " 'PCintPort' has not been declared " I've gone through and installed the library again for PinChangeInt but that hasn't changed it. This code is all new to me, though I've played with arduino stuff as a hobby for a little while. Any ideas on what I'm doing wrong?

    The code that I'm using is an exact copy of the big sample code above.

    Cheers,
    Ash

    ReplyDelete
  21. Hi,
    It should be something simple, what version of the library are you using and where did you get it from ? Also what version of Arduino are you using ?

    Duane B

    ReplyDelete
  22. I'm using an arduino Uno R3, bought about 2 months ago, and the version of PinChangeInt is 1.72 from this source (http://code.google.com/p/arduino-pinchangeint/downloads/detail?name=pinchangeint-v1.72.zip) and the version of arduino software is 1.0.1 for windows.

    When I click on 'sketch' up the top and then 'import library' it now shows me that I now have the option to import the PinChangeInt library, but I've just realised that when I click it doesn't write the '#include' statement in the sketch. Could this just be an install error?

    Ash

    ReplyDelete
  23. Hi Duane,

    Your code works perfectly, it was my fault for not installing the library properly, I accidently put the PinChangeInt library an extra file deep when i unzipped it.

    Thanks for your suggestions, they made me think a bit simpler.

    Ash

    ReplyDelete
  24. Hey Duane, I tried out the concept behind your code for controlling four servos. The code can be found here:https://docs.google.com/open?id=0BzJW8_XBaNMRUm9FX3JZRElVRGc

    What I am trying to do is to control 4 servos using two channels and one channel for controlling the motor. My underlying aim is to control the servos such that if I deflect the analog stick of the transmitter in one direction, it will rotate one servo a full 180 degrees and if i deflect it in the opposite direction then i will get another 180 deflection is another servo. This is because the transmitter isnt able to provide the throw that i need.

    The problem that i am facing, is that two servos can be controlled very easily, but the third servo is moving eratically.

    Please check if there is a blunder in the code or if you have a better solution to the task that i mentioned.

    Thanks
    Summit

    ReplyDelete
  25. Hi,

    I can see a lot wrong with your code, I do not have time to go through line by line and so your best bet might be to re-read this post, and use the code provided as the base for your own project.

    Sorry,

    Duane B

    ReplyDelete
    Replies
    1. Hey
      Thanks for the reply. I re read the post many times but the code that i have written is for Arduino Mega not for Uno and hence i could use the available interrupts on it rather than using the software interrupts that are generated using the PinChange library.
      If you could just give me the gist of what is wrong with the code (even a very light gist) or how to proceed in correcting it then it would be great.

      Thanks again
      Summit

      Delete
  26. Hi,
    The code I have provided in this post has been used by many people on both UNO's and Mega's. To use the code on a Mega replace PCintPort::attachInterrupt with attachInterrupt and make sure that the pin you use is one of the Mega interrupt pins.

    I have provided this code for people to use, lots do and all of the feedback indicates that it works very well.

    My suggestion for you is to replace the PCintPort::attachInterrupt with attachInterrupt, use the code as it is provided here to control your project in the normal way, then once you have it working make whatever project specific changes you need.

    Duane

    ReplyDelete
  27. Hi,
    I am trying to use the example you provided with an Arduino Mega Pro, and it doesn't want to read any of the interrupts. I have the signals from my radio receiver in ports 2, 3, and 18 which according to this: http://arduino.cc/it/Reference/AttachInterrupt
    are all interrupt ports. When I measure the voltages from the signal cables I get values from ~.2 to ~.4 volts depending on what the remote is doing (changing throttle/steering). Are these just too low for my arduino to read?

    ReplyDelete
  28. Hi,
    Read part one here to understand why you are seeing those low values and use the code provided in part one to test your basic connectivity.

    http://rcarduino.blogspot.com/2012/01/how-to-read-rc-receiver-with.html

    Duane B

    ReplyDelete
  29. Hi,
    The example from part one works fine. I noticed that the example for three inputs doesn't have both the interrupt pin id and the digital id. So I changed the attatchInterrupts to use the interrupt id (0, 1, and 5 for me) and now calcAux gets called one time, and then the program stops looping.

    Should I just be using the interrupt port id and not the actual digital port id?

    ReplyDelete
  30. Hi,
    If you are using a Mega, you have enough interrupts available so you do not need to use the PinChangeInt library. To use the interrupts directly replace PCintPort::attachInterrupt with just attachInterrupt in the setup function. You should not be able to use the interrupts. The attachInterrupt function accepts the digital pin number rather than the interrupt number. On my UNO i need to pass 2 as the pin number in order to attach to int0 which is digital pin 2.

    Duane B

    ReplyDelete
  31. I'm using the digital pin numbers, but no matter what is input, the output remains .38 volts. Also none of my Serial.prints are actually being printed which makes me think something is hanging up somewhere. Any more ideas?

    ReplyDelete
  32. Its probably something simple, post your code and forget about the volts. Servo signals are mostly empty space as explained in part one - your multi meter can't tell you this so it comes up with .38 volts - 5 volts for 10% of the time = more or less .5 volts.

    Duane.

    ReplyDelete
  33. I can't post it all. It is the example code with the ports changed to 3, 2, and 18 and the PCintPort::s removed.

    // include the pinchangeint library - see the links in the related topics section above for details
    //#include

    #include

    // Assign your channel in pins
    #define THROTTLE_IN_PIN 3
    #define STEERING_IN_PIN 2
    #define AUX_IN_PIN 18

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

    void setup()
    {
    Serial.begin(9600);

    Serial.println("multiChannels");

    // attach servo objects, these will generate the correct
    // pulses for driving Electronic speed controllers, servos or other devices
    // designed to interface directly with RC Receivers
    servoThrottle.attach(THROTTLE_OUT_PIN);
    servoSteering.attach(STEERING_OUT_PIN);
    servoAux.attach(AUX_OUT_PIN);

    // using the PinChangeInt library, attach the interrupts
    // used to read the channels
    attachInterrupt(THROTTLE_IN_PIN, calcThrottle,CHANGE);
    attachInterrupt(STEERING_IN_PIN, calcSteering,CHANGE);
    attachInterrupt(AUX_IN_PIN, calcAux,CHANGE);
    }

    ReplyDelete
  34. Try this test, first disconnect any external circuitry, then uses the defines below. What I am doing is using the Servo object to output a test signal on the interrupt pins, the output will quickly go out of range because small errors will feedback into themselves but you should see readings starting at around 1500us and quickly ramping up or down as the errors accumulate. Once this works we can figure out which pins to use to drive external components.

    // Assign your channel in pins
    #define THROTTLE_IN_PIN 0
    #define STEERING_IN_PIN 1
    #define AUX_IN_PIN 2

    // Assign your channel out pins
    #define THROTTLE_OUT_PIN 2
    #define STEERING_OUT_PIN 3
    #define AUX_OUT_PIN 21

    As above, the output pins are mapped to themselves using the Mega interrupt to digital pin number mapping outlined in the first paragraph of this link - http://arduino.cc/it/Reference/AttachInterrupt

    Duane B.

    ReplyDelete
  35. Hi Steven,
    Looking at your Mega code and the online documentation for attachInterrupt -

    Most Arduino boards have two external interrupts: numbers 0 (on digital pin 2) and 1 (on digital pin 3). The Arduino Mega has an additional four: numbers 2 (pin 21), 3 (pin 20), 4 (pin 19), and 5 (pin 18).

    I can see that you are attaching your interrupts to the wrong pins.

    The problem comes from the fact that attachInterrupt takes the interrupt number, but digitalRead uses the digital pin number. Its not a problem with pinchangeint because the library uses digital pin numbers, but using attachInterrupt you will need to do the following -

    To fix this, make the following changes -

    // Assign your channel in pins, these are pin numbers for use in digitalRead
    #define THROTTLE_IN_PIN 2 // Pin 2 = Interrupt 0
    #define STEERING_IN_PIN 3 // pin 3 = Interrupt 1
    #define AUX_IN_PIN 21 // pin 21 = Interrupt 2

    Add this -

    // Assign interrupt numbers, we will only use these in setup
    #define THROTTLE_IN_INTERRUPT 0 // Pin 2 = Interrupt 0
    #define STEERING_IN_INTERRUPT 1 // pin 3 = Interrupt 1
    #define AUX_IN_INTERRUPT 2 // pin 21 = Interrupt 2

    Now in setup use -

    attachInterrupt(THROTTLE_IN_INTERRUPT, calcThrottle,CHANGE);
    attachInterrupt(STEERING_IN_INTERRUPT, calcSteering,CHANGE);
    attachInterrupt(AUX_IN_INTERRUPT, calcAux,CHANGE);

    If you want to change pins, refer to the attachInterrupt documentation for the mapping between Mega pins and interrupts.

    Duane B.

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
  36. Thank you so much Duane. I had tried adding in the interrupt and pin ports earlier, but it didn't work. Maybe I missed something last time. Thanks again for all your work developing and supporting your guides.

    ReplyDelete
  37. Thanks for this great posts!

    As I see you are using 3 pins for the 3 signals. Is it possible to use this approach (http://diydrones.com/profiles/blogs/705844:BlogPost:38393) - reading the sum signal - with your method? If yes, how?
    Do you think, it will help to increase performance?

    ReplyDelete
  38. Hi, Personally I prefer to use three pins if I have the three decoded signals available. I have tried reading the single stream, but have found it less resilient to noise - everything works on the kitchen table, but at speed and distance its a lot more complicated - so if you have the three, four, five or six channels available individually from you receiver I would personally trust the existing decode software/hardware rather than take it out of the loop and reinvent it just to save a few pins. Duane B

    ReplyDelete
  39. Great post. One interesting note on enabling/disabling interrupts - you might like to use the following as provided by avr/atomic.h:

    ATOMIC_BLOCK ( ATOMIC_RESTORESTATE ) {
    //copy to local
    }

    This can replace the following code and takes care of restoring SREG correctly:

    noInterrupts();
    ///copy to local
    interrupts();

    ReplyDelete
  40. (Sorry, docs for above -> http://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html#gaaaea265b31dabcfb3098bec7685c39e4)

    ReplyDelete
  41. Hi, There are a few things that can be done to optimize this code, but I spent a long time thinking about whether to optimize it or make it as readable as possible. In the end I went for readable and given the number of people that are now successfully using the code I think it was the right choice. In a few weeks I will post a follow up with two or three optimizations for people who are familiar with the code as it stands and want to progress. One of them will be accessing SREG directly, the others will trim a few microseconds from the ISRs - however given that there are faster Arduino compatibles based on different processors coming soon - teensie 3.0 and Arduino Due - it might be better to stay at the high level for portability. Duane B

    ReplyDelete
  42. Hello,
    I am having an unusual problem. When I load the code from my laptop and leave it plugged in, everything works perfectly. I am powering my receiver from the 5v output from the arduino. However, when I unplug the board and power it from my battery, my servos do not move at all. Same issue if I power the receiver from a seperate battery. Any advice??
    Thanks in advance.

    ReplyDelete
  43. Hi,
    This is normally something to do with the way your standalone project is wired, I am snowed under at the moment so your best bet will be to start a topic on the Arduino forum and include a diagram of your standalone circuit.

    Duane B

    ReplyDelete
  44. If you do, let me know and I will take a look at it

    Duane B

    ReplyDelete
  45. Hi Duane
    Thanks for this extensive in-depth explanation.
    I have 2 questions about the code that are (as far as I see) not yet answered. I need some text to explain my questions so please bear with me.

    In your main loop in the part that copies the volatile reads to the local reads there id a test on the flag for each copy.
    if(bUpdateFlags & THROTTLE_FLAG)
    {
    RC_Channel_1_Value = unThrottleInShared;
    }
    From what I understand the test is not needed and is increasing the time you are in the interrupt blocking code.
    I think the condition is not needed because interrupts themselves can not be interrupted so you can not read incorrect values. As to my understanding the flag and the value are always in sync.
    There may be a startup sync problem (before the very first read on each channel) but as your code uses the flag to action the values your code as is would handle this properly.

    My first question is: Do you see any drawbacks of replacing the conditional copy by a plain copy.

    Continue thinking in the same way: Do you see a real life drawback of replacing
    if (bUpdateFlags & THROTTLE_FLAG){
    copy volatile vars
    }
    conditionally do actions

    by
    if (bUpdateFlags & (THROTTLE_FLAG | STEERING_FLAG | AUX_FLAG ) ) {
    copy volatile vars
    do actions
    }
    I realize this will reduce the response time as you are waiting for all channels to arrive before acting upon them. I can imagine that someone who wants to get everything out of his material prefers your proposal. But would this make a difference to someone who does not want to squeeze everything out of his material?

    So my second question is: Can you as an RC specialist give your opinion whether "waiting for all channels" will make any difference in the world of "standard users".

    Best regards
    Jantje

    ReplyDelete
  46. Rofl
    Sorry just read this in the code
    // in the current code, the shared values are always populated
    // so we could copy them without testing the flags
    // however in the future this could change, so lets
    // only copy when the flags tell us we can.
    My second question still stands (for now)

    Best regards
    Jantje

    ReplyDelete
  47. To answer this question - Can you as an RC specialist give your opinion whether "waiting for all channels" will make any difference in the world of "standard users". - It really depends on the application, for example if you are trying to stabalize something (quadcopters) or stop a skid developing (RC Car/yaw control) or optimise traction the faster you can respond to a developing condition the better. The difference between the code here and what you suggest is small, the difference between the code and using pulseIn or some other blocking approach is massive. I tend to treat the channels individually so that I can intercept a throttle or steering signal as soon as I have it rather than waiting a few additional uS for a complete frame worth of signals. Last night I came across some code I wrote in the summer that uses an alternative servo class to provide faster servo refresh and optimise the time spent in both the Servo and RC Channel ISRS. I will publish it in the next few days. Duane B

    ReplyDelete
  48. Duane
    Thanks for the answer.
    I'm currently (amongst others) working on a library based on the code above.
    The idea is to hide the complexity in the library and keep the functional part in the code.

    The pattern of a sketch would look like this:
    #include "PinChangeInt.h"
    #define NUM_RC_CHANNELS 5

    // Assign your pins to channels
    PROGMEM const byte RC_Channel_Pin[NUM_RC_CHANNELS]={5,6,7,8,9};
    // create local variables to hold a local copies of the channel inputs
    uint16_t RC_Channel_Value[NUM_RC_CHANNELS];
    //assign interrupt functions to channels
    #include "RCLib.h"

    void setup()
    {
    //Serial.begin(9600);

    //Serial.println("multiChannels");

    // attach the interrupts used to read the channels
    SetRCInterrupts();

    }

    void loop()
    {

    // If you want to wait for all channels to have reported the change you can use the code below
    if(haveAllRCChannelsReceivedInfo())
    {
    //do your channel actions here
    //The values received are in RC_Channel_Value[]
    }
    //End of: If you want to wait for all channels to have reported the change you can use the code below

    //If you prefer to react as soon as possible
    uint8_t UpdateFlag = getChannelsReceiveInfo();
    if (UpdateFlag & 1)
    {
    //Channel 1 changed so do what needs to be done for a channel 1 change
    }
    //end of: If you prefer to react as soon as possible
    }

    Can you tell me what you think?
    Best regards
    Jantje

    ReplyDelete
  49. I have stayed away from releasing a library because all of the array access and pin mapping slows things down a lot inside the ISR - too much for me to have ever been happy enough to release something - I have built a few different approaches including some fast low level ones but too much of it is either reinventing the wheel (re implementing pin change int ) or just not fast enough. The thing I like about this code is that it will move straight over to the new ARM platforms because its at a sufficiently high level and still gives good performance on the AVRs. Duane B

    ReplyDelete
  50. Duane
    I came to the conclusion that the answer to my question ""waiting for all channels" will make any difference in the world of "standard users" is Yes, it does. Why? One broken line and all RC control is gone :-(. So I dumped the idea.

    The library is going fine
    The usage is simple (internal it is not :-( ) and I have been able to test it on a duemilanove with the PinChangeInt library and without. I want to test it with a mega and leonardo this weekend.
    I'm fighting PROGMEM because it changes the actual values read. I'm not sure what to think about that.
    Best regards
    Jantje

    ReplyDelete
    Replies
    1. Hi Duane
      Sorry I missed this remark.
      I need PROGMEM to store fixed values needed. one example is to be able to map pins to interrupts.
      It works fine now but I get to many warnings in regards to progmem which hides the warnings I create.
      You can see my lib at https://github.com/jantje/libraries
      I didn't do any testing but I assume I get very similar speed measurements as to your code and it is lots easier to write.
      For instance if you do not include pinchangeint you use the arduino attachInterrupt() and pins.
      If you include pinchangeint you automatically use the pinchangeint attachInterrupt() and pins.
      Och the code is dirty (I mean hard to read) because I extensively used MACRO's to solve problems because I wanted "fast code" rather that "readable code"
      Tell me what you think about it.
      Best regards
      Jantje

      Delete
  51. I'm not sure I see why you're using Servo as an output routine. What would be wrong with simply echoing what the input pins do?

    For example, the throttle input rises and triggers the ISR to simply set the throttle output pin. Likewise, a falling pin would simply trigger the ISR to clear the throttle output pin.

    ReplyDelete
  52. If all you ever wanted to do was read the input and pass it straight to the output, there would be no need for the Arduino at all. My expectation is that once people are reading the input and are confident that the servo library output does what they expect, they will do something more interesting, as an example see this -

    http://rcarduino.blogspot.com/2012/07/rcarduino-yaw-control-part-2.html

    or this -

    http://rcarduino.blogspot.com/2012/05/interfacing-rc-channels-to-l293d-motor.html

    or this -

    http://rcarduino.blogspot.com/2012/01/traction-control-part-13-we-have.html

    Duane B

    ReplyDelete
  53. When reading the serial output, should the be a specific value or is a small variance normal..

    i.e.

    Throttle (F) 1908/1912
    Throttle (N) 1508/1512
    Throttle (R) 1112/1116

    Steering (R) 1908/1912
    Steering (N) 1512/1516
    Steering (L) 1112/1116

    ReplyDelete
  54. The small variance is normal, you can find an explanation and recommendations for reducing it here but note that its generally not enough to move a servo or ESC and is also smaller than the general level of radio interference you can expect, see -

    explanation and reduction
    http://rcarduino.blogspot.com/2012/11/how-to-read-rc-channels-rcarduinofastlib.html

    radio interference illustration
    http://rcarduino.blogspot.com/2012/03/reading-from-rc-reveiver-do-you-need.html

    Duane B

    ReplyDelete
  55. Duane, thank you for your code and for all of the help you offer to all of us!

    ReplyDelete
  56. Hi Duane,
    +1 to anonymous ^. Thanks for this set of posts, they're really informative and well written.

    I've been programming with visual C# for a while now so I'm competent in basic programming, but have very little idea about interrupts.
    For a university project I'm trying to make an autopilot routine for a quadcopter, plugging an arduino uno between the Rx and the quadcopter's brain.
    With my extremely limited knowledge of interrupts I've tried to extend your code above to more channels, but it's not working.

    Could I ask you a favour? Would it take up much of your time to put up a second example somewhere that does the same thing but for, say, 8 channels?

    I've searched everywhere ever for similar code that works for 6 or 8 channels, but there is only stuff involving using Timer1, which, from what i've read (see: some of your own comments) cannot be used in conjunction with the servo library. I think there would be a lot of people out there including me who would be very grateful for an 8 channel PWM reader + passthrough.

    Preemptive Thanks,
    Patrick L

    P.S. This Arduino stuff seems really cool and I'm definitely going to learn how to use interrupts in the future but for this particular projects time is short and the only thing holding me up is making the arduino invisible when autopilot is inactive.

    ReplyDelete
  57. To add more channels, its simply a case of cutting and pasting. The code is organized into clear sections, for example the three channel input pins are define, then the output put pins, then the variables to hold the values and so it continues throughout. To add an additional channel, look through the code line by line and wherever you see the Aux channel referenced, copy it and rename it to add your new channel. It really is that easy. Duane B

    ReplyDelete
  58. What you mean I actually have to work to get what I want?! Even if 'working' is just copying and pasting XD

    First I have a couple of questions.
    1) Why did you not start at pin 1?

    2) Do the Flags assigned at 1,2 and 4 have any relation to pins, or are they their own set of numbers? (i.e. can I have a pin 1 as well as a flag 1?)

    3)Why did you skip flag 3?

    So I did added another channel the way you said, but my quadcopter won't arm, meaning the 4th Channel isn't right. When I print servoAux2.readMicroseconds and unAux2In to serial they're exactly the same but the correct PWM is definitely not getting to the 'copter.

    Any ideas why?
    I'm hoping you're going to say 'ah this is a common problem...'

    The code is here if you're interested
    http://www.pastebucket.com/5242

    Thanks again,
    Patrick

    ReplyDelete
  59. The flag is the only thing you have wrong, the flags are 'bit flags' meaning each one relates to a different bit in a byte. Bits have the values 1,2,4,8,16,32,64 and 128, these are the values you should use for your flags, change 5 to 8 and your code looks as if it should work.

    Duane B

    ReplyDelete
  60. haha not my code, /your/ code.
    Nope I changed the flag to 8 and still nothing happens.
    If I print servoAux2.readMicroseconds to serial, is what's on my screen definitely what's coming out of the pin? or not necessarily so?

    ReplyDelete
  61. It is what is coming out of the pin, what does it show ? does it change if you move any or all of the transmitter controls ?

    Duane B

    ReplyDelete
    Replies
    1. All the pins are putting out expected results - minimum ~1100 maximum ~1900.
      I don't know if you know anything about quadcopters, but you have to arm them so they don't start spinning when you're too close.
      This is done by moving throttle to minimum and rudder to maximum.
      With my arduino plugged in, the arming light comes on when throttle is full regardless of rudder position, but after that no response from anything, and it disarms when throttle goes to minimum.

      Could it be a conflict because the arduino is not outputting into esc's but into another microcontroller? is there something slightly off with timing or something which confuses the quadcopter brain?

      Patrick

      Delete
  62. Hi, So what you have is the Arduino accepting and passing through all of your channels correct, but the second uC (microcontroller) is not passing these correctly on to the ESCs. Can you check that you have a common ground connection between the two uCs and also do you have an oscilloscope that you can check that the output of the second uC is following the output from the Arduino ? if not, try a servo.

    Duane B

    ReplyDelete
  63. Hi!
    Dumb question, but where IS the library on your site? I see no download area. Sorry if i missed it.

    Tom

    ReplyDelete
  64. There are quite a few libraries dotted around, which one are you looking for ?

    Duane B

    ReplyDelete
  65. Hi Duane,
    Thanks for the great explanations. Still a newb, but learning fast. Wondering if you have any thoughts on how to work with 760 microsecond, 560 Hz high speed servos. I've been mucking with the servo.h library to try and get it to refresh faster, but I'm honestly in way over my head on that :)
    Cheers,
    Jon

    ReplyDelete
  66. Hi Duane, i am a super new beginner. I am also struggling to get a RC car moving using a Futaba recevier and transmitter. For the above project , do you happen to have the full schematic diagram? A Thousand thanks for your assistance

    ReplyDelete
  67. Hi, I dont have a schematic, but the only thing you really need to think about is how are you going to power the Arduino. WhAt type of baTtery is powering the car and how many volts is it ?

    Duane B

    ReplyDelete
  68. Hi I am new to all of this, i load the single channel code and all worked fine. Now having load the above code all i see in the serial monitor is tyhe word "multi channels" i have confirmed the the RC RX is working and giving oit data. I have tried using a MEGA 2560 and UNO board and it does the same?

    ReplyDelete
  69. Hi, If you read through the code you will see that it does not attempt to output to serial, instead it directly controls servos attached to pins 8,9 or 10.

    If you dont have any servos and just want to see serial output instead, you can add it next to the writeMicroseconds.

    Duane B

    ReplyDelete
  70. Hi Duane, I am using an Arduino Uno and whenever I try to compile the code you provided, I get the following error msg

    Servo\Servo.cpp.o: In function `__vector_11':
    E:\arduino-1.0.1\libraries\Servo/Servo.cpp:103: multiple definition of `__vector_11'
    RCArduinoFastLib\RCArduinoFastLib.cpp.o:E:\arduino-1.0.1\libraries\RCArduinoFastLib/RCArduinoFastLib.cpp:51: first defined here
    It seems that both RCArduinoFastLib and Servo Library use the same timer

    May I know if there is anything I'm missing?

    Thanks

    Thomas

    ReplyDelete
  71. Hi,
    The RCArduinoFastLib is an alternative to the servo library which has been optimised for use in RC Projects. As its an alternative to the Servo library, it uses the same Timer resource on the basis that where RCArduinoFastLib is used, the servo library will not be used. Not sure what code you are compiling to see an error, the code in this post does not include RCArduinoFastLib so there should be no clashes.

    Duane B

    ReplyDelete
  72. This is awesome, thanks for sharing! I really learned a lot about the Arduino language and signal processing from your post and from playing around with my controller/servos this weekend.

    This is, this sketch does not work at all on an Arduino Micro. It works perfect on my Duemilanove and Mini, but not on the (Leonardo based) Micro. Any ideas on a work-around? Thanks!

    -Anthony

    ReplyDelete
  73. Hi,
    I do not have a micro but have an idea of what your problem is - it also ties into a post I was planning to write soon.

    As a first step, lets try reading a single channel using some simpler code here - http://rcarduino.blogspot.ae/2012/01/how-to-read-rc-receiver-with.html

    You will need to make some small changes to be able to write to serial on the micro, I think you know these already.

    Let me know if this works, it might not in which case I know what the problem is.

    Duane B.

    ReplyDelete
  74. Okay, let's do this!

    First though, I just uploaded the "MultiChannel" code again with the "while (!Serial) {;}" and a "Serial.println("test")" thrown in. This is the exact same code as Sunday, but now it's actually printing "test". Throttle is still showing all zeros though :(

    Now on to the code from "How To Read an RC Receiver With A Microcontroller - Part 1". At first, no Serial at all. After adding, "while (!Serial) {;}" I can print "test" if inserted into the void loop(). Nothing else prints though. If the Serial.println("test"); command is inside the "if(bNewThrottleSignal)" function it again does not print even while manipulating the controller throttle.

    Ideas?

    -Anthony

    ReplyDelete
  75. Hi, What I think is happening is that the Micro has a different pin mappings between interrupts and digital pins.

    It looks as if the Micro has interrupts 0 to 3 mapped to digital pins 0 to 3 the UNO has interrupts 0 to 1 mapped to pins 2 to 3. The code is based on interrupt 0 being mapped to pin 2 which it is not on the Micro, to test this, try changing the following line -

    attachInterrupt(THROTTLE_SIGNAL_IN,calcInput,CHANGE);

    to

    attachInterrupt(INT2,calcInput,CHANGE);

    Let me know what happens.

    For anyone else following this, please note that this refers to the code in this post

    http://rcarduino.blogspot.ae/2012/01/how-to-read-rc-receiver-with.html

    which represents the simplest test of an Arduino to RC Receiver connection, not the code above which uses a slightly different approach.

    Duane B

    ReplyDelete
  76. Hi, Looking at the documentation here -

    http://arduino.cc/en/Reference/AttachInterrupt

    It looks as if INT1 is mapped to pin 2 on the Leonardo, so if INT2, doesnt work as suggest above try


    attachInterrupt(INT1,calcInput,CHANGE);

    Duane B

    ReplyDelete
  77. Hi Duane B,

    INT2 did nothing that I could tell, but INT1 produced a huge Serial print, but nothing no pattern appeared from manipulating the controller's throttle.

    Here is a segment of what was printed. This string just repeats over and over again:
    6484
    9036
    8916
    8824
    8768
    8764
    8772
    8576
    8356
    8296
    8276
    8308
    8424
    8624
    8700
    8704
    8700
    8708
    8708
    8712
    8708
    8720
    8712
    8708
    8720
    8720
    8712
    8724
    8716
    8716
    8716
    8716
    8708
    8720
    8716
    8708
    8716
    8768
    9008
    9248
    9476
    9708
    140
    452
    8428
    88
    80
    8400
    76
    8536
    72
    8644
    60
    8720
    52
    8692
    36
    8696
    24
    8708
    20
    8700
    20
    8712
    24
    8704
    20
    8704
    20
    8712
    20
    8712
    20
    8708
    16
    8712
    20
    8712
    24
    8712
    48
    8712
    44
    8724
    44
    8712
    52
    8716
    52
    8720
    56
    8724
    60
    8716
    60
    8712
    72
    8712
    84
    8732
    208
    9044


    And then I remembered to ground the Arduino to the RC receiver. After that, Serial.print is outputting numbers I expect: 1500s for neutral, 1100s for reverse, 1900s for forward.

    Now I plan to use the below bit of code to translate the throttle signal into PWM to use on a MOSFET for speed control.
    if ((unThrottleIn < 1665) && (unThrottleIn > 1365))
    {
    Throttle = 0;
    analogWrite (MOTORFWD, Throttle);
    analogWrite (MOTORRVS, Throttle);
    }
    else if(unThrottleIn > 1665)
    {
    Throttle = (unThrottleIn - 1665);
    analogWrite(MOTORFWD, Throttle);
    analogWrite(MOTORRVS, 0);
    }
    else if (unThrottleIn < 1365)
    {
    Throttle = (1365 - unThrottleIn);
    analogWrite(MOTORFWD, 0);
    analogWrite(MOTORRVS, Throttle);
    }
    }



    Thanks for your help! I really appreciate your time; it's folks like you willing to help a complete stranger across the globe that make this community so great. I look forward to the day when I am able to help the next n00b!

    -Anthony

    ReplyDelete
  78. I may have spoke too soon... Switching to INT1 worked on "How To Read an RC Receiver With A Microcontroller - Part 1" but not in How To Read Multiple RC Channels.

    Also, INT0 on Pint 3 worked great for AUX channel.

    ReplyDelete
  79. Hi,
    Your getting one step ahead, so far all we have done is adapt one sketch to work with the Arduino Micro, now that we know the pin mappings on the Micro are different than on the UNO and Mega, we can adapt the second multi channel sketch to work on the micro.

    Looking at the pin mapping for the micro, it should be possible to read six channels without using any external libraries.

    I will put together a post showing how to do this on the UNO (5 interrupts) Leonardo/Mico (6 interrupts) and Mega (Loads of interrupts). I will share the draft post with you so that you can test it first.

    Can you send me you email address through the Arduino forum and I will send you a link to the draft post.

    Duane B

    ReplyDelete
  80. Hi,

    Thank you for sharing your hard work. Your blog is very inspiring and i've learned so much. There is still one question though. Can you tell me if one can use all this stuff along with the wire library? I mean do these all libraries use different timers and other resources so that they can be used together in one program?

    Cheers,
    Peter

    ReplyDelete
  81. Hi,

    I spent a good part of today trying to figure out why the single servo program would work and I couldn't get the multiple servo program working. I read through all of the comments and it's possible that I missed something, but I thought I would share here to try to get a better understanding.

    In the end it seems like only 1 variable is used for both the interrupt and the pin # in the multiple servo program whereas in the single servo program there is a pin definition and an interrupt definition. When I set the interrupt number to 0 in the setup everything works (attachInterrupt(0, calcThrottle,CHANGE); with the throttle pin set to 2)

    Am I overlooking something obvious here?

    Thanks for posting your code and comments, they have been extremely helpful!

    ReplyDelete
    Replies
    1. Solved by following this procedure -

      1) Use the original code from the blog here but with the following changes -

      http://rcarduino.blogspot.ae/2012/04/how-to-read-multiple-rc-channels-draft.html

      2) Change this


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


      to this -

      // Assign your channel in pins
      #define THROTTLE_IN_PIN 8
      #define STEERING_IN_PIN 9
      #define AUX_IN_PIN 10

      // Assign your channel out pins
      #define THROTTLE_OUT_PIN 5
      #define STEERING_OUT_PIN 6
      #define AUX_OUT_PIN 7

      Basically pins 8,9,10 and 11 are the only ones on a the micro that support pin change interrupts. For this to work you will also need to reconnect your circuit so that the RC Channels are coming into pins 8,9,10 and the servo signals are going out through 5,6 and 7.

      Duane B

      Delete
  82. Yes, your overlooking something.

    The multi channel example above uses the pinchangeint library, you can see this where the interrupt is attached using -

    PCintPort::attachInterrupt(AUX_IN_PIN, calcAux,CHANGE);

    instead of the standard syntax as used in the single channel example -

    attachInterrupt(AUX_IN_PIN, calcAux,CHANGE);

    I always found it a bit confusing that the standard Arduino attachInterrupt function uses the interrupt number rather than the pin number. That is why in the single channel example we need two variables, the first is the interrupt number, the second is the pin number. It is also the reason why the UNO example did not work on your Arduino Micro. If the Arduino team had used pin numbers instead, the function could have easily figured out which interrupt is associated with which pin on which board.

    At the moment, my assumption is that the pinchangeint library is not supported on the Arduino Micro and that is why this example is not working for you.

    I have an alternative approach which is more efficient that I hope to post later today, stay tuned.

    Duane B

    ReplyDelete
    Replies
    1. Thanks for the response. I forgot to mention that I am using a Arduino mego so I turned off the pinchangeint library.

      I'm getting a weird twitch every couple of seconds where the arduino is reading a non-center stick value (~10-20% of full scale) for what appears to be a single reading. Do you know if there is any way to eliminate twitch at the expense of accuracy? I'll be driving some high power motors with this and would like to avoid pulsing them when they shouldn't be moving.

      Thanks again!

      Delete
    2. Hi,
      It gets hard to follow when people comment as anonymous, can you use a nickname in your comments so that I can at least follow which anonymous is which.

      The twitch can be one of two things -

      1) Its a result of interrupt clashes between the servo library timer interrupt and the incoming RC Signal interrupts

      I would expect a variation of about 5-10us in this case, thats one or two percent of the servo range, enough to tick, not jerk.

      2) An error in your modified code - I am assuming this is not the case.

      If its just a twitch its 1) above, if its more than a twitch is probably 2)

      Are you planning to use the servo library to drive ESCs in your final project or are you using analogWrite to PWM Motors through a shield ?

      If you are not planning to use servos, remove the code and the clashes will go away. If you are planning to to use servos, try this approach, it has come optimizations to reduce clashes, it also has an explanation of where the clashes come from which might be helpful

      http://rcarduino.blogspot.ae/2012/11/how-to-read-rc-channels-rcarduinofastlib.html

      As you are not using pinchangeint, you will need to replace

      if(PCintPort::pinState)

      with the direct port manipulation equivalent i.e.

      if(PORTD & (1<<3)) // test INT3 on Mega Digital Pin 18

      If you need help with this, let me know

      Duane B

      Delete
    3. Thanks again for all of your help. I will not be using the servos so I tried taking the code out along with the code that I had added other than:

      //Mid point bounces between 1556 and 1560

      if(unThrottleIn > 1560)
      {
      Serial.println(unThrottleIn);
      }

      With this code in there I would get bundles of 15 numbers that would print to serial, they ranged from 1564 to 1588 and would come every few seconds or so. That's roughly 30 us of error on the large end so I'm thinking I must be doing something wrong.

      To find out if it was just the multiple servo code I went back to the single servo program and added only the above section of code in the part where it says "// other processing ...". Once again the numbers came in bunches of 15, but the number was always 1564, so only ~5 us of error.

      I appreciate the help but I understand if this is something you don't have time to chase down.

      Thanks again,
      Woody (person using the mega and getting a twitchy response)

      Delete
    4. Hi Woody,
      It looks as if its a problem with the modified sketch you are using, can you send me a copy to have a look, the easiest way is to send it to me through the PM system on Arduino forum.

      Duane B

      Delete
  83. if have some glitch in the signal after have connected the grawnd of receiver to arduino grawnd, put a 2,2nF capacitor between signal and grown, it will delete all glitch and make the signal stable :)

    ReplyDelete
  84. Hi, I need to control two servos and a esc. The receiver is 2.4 ghz called tactic. Here is a photo of the receiver and servos.

    http://www.aquacraftmodels.com/boats/aqub0200-vela/aqub0200-servo-lg.jpg

    ReplyDelete
  85. Love your Library.
    I have used it to make an Autonomous car/boat/plane with Adafruit Ultimate GPS.
    The difficulty was the conflicts with SoftwareSerial. Problem solved!
    Let me know if you want to see the code.
    It uses less than 2% of the Uno CPU resources.

    ReplyDelete
    Replies
    1. How about some pictures or a video, I would like to see more of what people end up building

      Duane B

      Delete
  86. I'm trying to build my own Quadcopter using Arduino. This article on reading receiver signals using interrupts is super useful in this regard. Its also really well written...thanks a ton Duane!!!

    ReplyDelete
  87. Thank you for your work and sharing it. My project is a quadcopter. That´s my new hobby and I hope see it flying... The frame is ready and now I´m studing the Arduino Uno and how work the escs, signal from rc spektrum dx6i, gps, imu, and so many things. I have learned very much with your work.
    Carlos/Lisbon

    ReplyDelete
  88. Hi Duane,

    I have a question regarding timer interrupt. I'm already using your code above to read various channels from my RC receiver and pass the signals to the 4 motors of my quadcopter.

    I would like to now read in accelerometer and gyro values at a constant time interval for attitude determination and control using a timer interrupt.

    Will the timer interrupt work alongside your code which also uses interrupts.

    Thanks once again for sharing your code and knowledge for free!

    ReplyDelete
  89. I am very impressed with your code and thank you for describing it.
    I would like to know how the FLAGS such as THROTTLE_FLAG 1 are assigned to the
    volatile uint8_t bUpdateFlagsShared, since there is no reference to the memory location name in the define statement nor is there a mention of the the flag in the declaration of the flagregister.
    Wally

    ReplyDelete
  90. Wally, your over complicating things. Its just C Code, there is no direct access to memory or registers.

    Duane

    ReplyDelete
  91. Thank you Duane. Sorry for my inexperience with flags in C. I see that I misunderstood the comment "holds the update flags defined above" . That had me going in the wrong direction.

    #define THROTTLE_FLAG 1
    #define STEERING_FLAG 2
    #defineAUX_FLAG 4
    volatile unit8_t bUpdateFlagsShared;//holds the update flags defined above

    I see now that it is the flag place values that are "defined above", and these will be &'ed with the value in the bUpdateFlags image of the bUpdateFlagsShared to test for the flag.
    Wally

    ReplyDelete
  92. HI,

    Thanks for sharing your great work.

    I'm using it to build a Kid mode for my RC short course truck.

    I started from your example with tree channels, and got it to work perfectly.
    I've combined with two extra interrupts for calculating distance and speed from a sonar and a button to set limited throttle modes.

    All work fine, I'm able to limit speed and based on the distance and speed readings and prevent frontal impacts while a 3 year old drives...

    The only issue I struggling width, is that my radio only has two channels (in fact for this purpose I only need 1), so I tried to remove the AUX channel by the following changes :
    1- Commenting everything regarding the AUX channel (Attach and interrupts):
    2 - #define SERVO_FRAME_SPACE 2
    3 - CRCArduinoFastServos::setFrameSpaceA(SERVO_FRAME_SPACE,8*2000);

    But after this change it stops working. Can you give me a clue for the reason?

    Thanks in advance.

    ReplyDelete
  93. Hello!
    I and My Friends are Trying to Build a RC Quadcopter and i need Some Help on the Code for Arduino to Control The Quadcopter.
    It would be great If You can help me with a Sample code or a Tutorial to Arduino as I'm New to Arduino.
    Thanks!

    ReplyDelete
  94. Hola ya probe en la 1- parte pero estas estas un poco dificil en la 2- parte
    porque me marca error?

    Arduino: 1.5.4 (Windows NT (unknown)), Board: "Arduino Mega (ATmega1280)"

    multicanales.ino: In function 'void setup()':
    multicanales:78: error: 'PCintPort' has not been declared
    multicanales:79: error: 'PCintPort' has not been declared
    multicanales:80: error: 'PCintPort' has not been declared

    This report would have more information with
    "Show verbose output during compilation"
    enabled in File > Preferences.

    ReplyDelete
  95. Hey, Thanks for the wonderful tutorial. I've got one problem. Whenever i try to see the pulse width of any channel in serial terminal of arduino, it returns 0. I am calling Serial.print() within an interrupt.
    The code for the ISR is same as yours, except for the command for printing the pulseWidth.

    void calcThrottle()
    {
    // if the pin is high, its a rising edge of the signal pulse, so lets record its value
    if(digitalRead(THROTTLE_IN_PIN) == HIGH)
    {
    ulThrottleStart = micros();
    }
    else
    {
    // else it must be a falling edge, so lets get the time and subtract the time of the rising edge
    // this gives use the time between the rising and falling edges i.e. the pulse duration.
    unThrottleInShared = (uint16_t)(micros() - ulThrottleStart);
    // use set the throttle flag to indicate that a new throttle signal has been received
    bUpdateFlagsShared |= THROTTLE_FLAG;
    Serial.println(unThrottleInShared);

    }

    }

    ReplyDelete
    Replies
    1. Did you try this first ?

      http://rcarduino.blogspot.ae/2012/01/how-to-read-rc-receiver-with.html

      Duane

      Delete
    2. Yup I got it. Serial data is captured using interrupts and hence it cannot be used in another ISR.
      Hope i got it right.
      Thank you!!

      Delete
  96. I tried to read print multiple channels, but noticed a vague behaviour. I the code that you have uploaded, whenever I declare the input pins (like throttleInPin, etc) below pin number 9(i.e. from 2 to 8) and then when i try to print the Time period read by them n serial window, I either get garbage values or nothing at all. But if I do the same for the input pins above 9 (9 till 13), I get the proper values on the serial terminal of arduino.
    Don't know if it is because of the PinChangeInt library or because of some serial comm buffer's connection with these pins.
    Can you help me out?
    Code snippet for printing is-

    // on every cycle.
    if(bUpdateFlags & THROTTLE_FLAG)
    {


    servoThrottle.writeMicroseconds(unThrottleIn);
    Serial.println(unThrottleIn);

    }
    I know there's nothing wrong in it, as it prints the values if input pins are defined between 9 and 13, both inclusive.
    Thank you!!

    ReplyDelete
    Replies
    1. and yes, I read and tried your earlier post. It worked.
      Thank You.

      Delete
  97. Which model of Arduino are you using ? Not all of them support PCInt on every pin, see here - http://rcarduino.blogspot.ae/2013/04/the-problem-and-solutions-with-arduino.html

    Duane B

    ReplyDelete
  98. Hi,

    Many thanks for the time you have put into developing this code.

    I'd like to copy this and expand it to at least 5 channels and run it on the mega 2560 board. Other than the changes to how the interrupt routines are called, I gather it will still work fine if I just duplicate the code for the extra channels I need?

    Mike

    ReplyDelete
  99. Hi, It will work on the Mega using the built in AttachInterrupt function on the pins listed here http://rcarduino.blogspot.ae/2013/04/the-problem-and-solutions-with-arduino.html Here is an example of 8 channels using PCInt on an Arduino UNO http://rcarduino.blogspot.ae/2013/04/problem-reading-rc-channels-rcarduino.html

    Duane.

    ReplyDelete
  100. Hi, i want to control 4 dc motors which are 3 motors in both directions and the other motor just in one direction. I planned to use the Walkera TX5805 and RX701 which both have 7 channels. Is it possible to do the configuration with the DEVO F7 transmitter? If yes, how does the configuration looks like? Hope to get a feedback esepcially on the coding. Thank you

    ReplyDelete
  101. Hi, I'm trying to read the signal from AR600 Spektrum rc receiver but i can't seem to find a solution. Nothing works... i tried pulseIn(), using a timer - micros(), usiong a timer with interrupts, reading from analog input. I'm working on a school project on APM 2.5, using input pin 1 = 13 and A0 = 97. I can't get something from the receiver and I know the receiver works since without the arduino board i manage to control servos only with it. I've been struggling for a few days now and I don't know what to do. Any help? Thanks.

    s.paul1408@gmail.com

    ReplyDelete
  102. No idea what an APM 2.5 is, but if its anything like and Arduino, the most common mistake is not connecting the ground of the receiver to the ground of the Arduino to form a common ground between the two.

    Duane

    ReplyDelete
  103. ...Or you could use a spectrum satelite receiver, and connect it to the serial port.
    http://forum.arduino.cc/index.php/topic,22266.0.html

    edit
    apm 2.5 is ardupilot mega, which does not have a serial-in from spectrum satelite
    that is - if you are using the normal arducopter firmware

    Try this, just replace "ar8000" with "ar600" both receivers send the same type of servo signal
    http://diydrones.com/forum/topics/connecting-apm-2-5-with-spektrum-ar8000

    ReplyDelete
  104. Hi Dear,

    I practiced this sketch with Arduino Mega 2560 and was working well. BUT I noticed that it's working for 10,11 and 12 pins only and use standard interrupts with pins 2 and 3. does it means we can have five pulse In pins only. please advise me if something missing.

    Thanks a lot,
    Fateh

    ReplyDelete
  105. I'm using this sketch to act as a "cruise control" for a model train. Haven't yet got my RC equipment but will in the future. At this point i'm using a infrared switch to count wheel rotations. I'm counting "pulses" from the switch in a 1/10 second time frame. There is a delay(100) in the loop. Will this mess up how the sketch works to sense pulse widths from the RX when i get the RC stuff? I don't need super responsive servo action; a slight delay is OK. Just need to make sure i'm reading the correct signal from the RX.
    Thx.
    Marty

    ReplyDelete
  106. No, a delay(100) will not effect the RC Reading as it uses interrupts. However you could easily adapt one of the RC Channels in the code above to read the wheel speed using interrupts.

    Duane B

    ReplyDelete
  107. hi, i have following problem: when i disconnect a rc receiver wire, there should be GND over a Pulldown Resistor on the Pin. sometimes it is recognized some times not. i don't know what to do. pulldown is about 20kohm

    thanks

    ReplyDelete
  108. Try a pull up instead and/or Try a smaller value pull down like a 10K and/or Use a multimeter to make sure that the ground is a good connection to the same ground the Arduino is using.

    Duane B

    ReplyDelete
  109. Thank you for sharing this information this is very nice blog thank you for giving this info.
    I share your post link in my new site ,

    ReplyDelete
  110. Duane, thx for all your work w/ your code. It's working well for me on my little steam engine. I've received my R/C stuff and am deciding how i want to use it. The Arduino gives so much flexibility. Here is a little video of my loco in "cruise control" mode. http://youtu.be/jntED_VEsMk
    Marty

    ReplyDelete
  111. Hello Duane B

    I design a code based on your example that sample 10 RC signals. 4 of them I pass directly to 4 servos using servo library. I use a Pro Mini card with ATMEGA328 5V/16M. It works fine but a get a tickle on servos. This tickle persist even when I send a constant value of 1500 at the servos on setup function and on loop function I don't actualize this value, and of course the pin change interrupt is working. When I disable the RC signals (close the radio Tx) the tickle disappear. So you have any idea how I can solve the problem of the servo tickling?

    Thank you very much
    Sorin

    ReplyDelete
    Replies
    1. Hi, If I understand, you write 1500 to your servos in the setup function and then do not change this value. You use interrupts to read RC Signals but do not use the signals you read to update servo positions. Your servos make tiny movements as if there is some small interference. These tiny movements stop if you turn the radio off.

      There is an explanation and solution here - http://rcarduino.blogspot.ae/2012/11/how-to-read-rc-channels-rcarduinofastlib.html Duane B on How To Read Multiple RC Channels

      Duane B

      Delete
  112. This comment has been removed by the author.

    ReplyDelete