Pages

Wednesday, April 24, 2013

Problem reading RC Channels - The RCArduino Loop Back Test

Many people experience problems adding additional channels to the RCArduino test sketches or porting them to new devices, its usually down to mistakes in the way the additional channels are coded

This post presents an example of managing 8 input/output channels, its a very simple sketch which outputs 8 RC Channels and reads them right back in again.

The sketch can be used in three steps -

1) Initially the sketch outputs fixed values for the servos and reads them back in as a loop back test.

2) Once you are comfortable with the code, you can replace one or more of the loop back connections to start reading RC Channels.

3) Once you are confident that the sketch is reading your channels correctly you can connect your servos and ESCs to the outputs and your done, 8 channels in, 8 channels out in three easy steps.

Step 1 - Loop back testing
To use the loop back test in step 1, connect pins 2 to 9 (the channel inputs) to pins 10 to 13 and A0 to A3 (the channel outputs).

Each channel is set to a fixed output from 1100 to 1800, if the code works on your board, your should see these values being output in the serial monitor.

Step 2 - RC Channel reading

Once you have this up and running, you can start swapping the connections to an RC receiver one by one. You should now see the channel values for the receiver channels updating in your serial monitor. If the values are within a range of around 1000 to 2000, your ready to move to step 3.

Step 3 - Full Control
To start outputting the values that you have read in, remove the comments from the servoName.writeMicroseconds functions to have full control of upto 8 servos/escs using 8 RC Receiver channels.

If it does not work, let me know which board you have, the code is easily adapted to work on any of the 8-bit Arduino boards. If you have a 32 bit Arduino Due, there is a dedicated post here -

// 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 processingServo servoChannel1;Servo servoChannel2;Servo servoChannel3;Servo servoChannel4;Servo servoChannel5;Servo servoChannel6;Servo servoChannel7;Servo servoChannel8;

// 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 signalsvolatile uint16_t unChannel1InShared;volatile uint16_t unChannel2InShared;volatile uint16_t unChannel3InShared;volatile uint16_t unChannel4InShared;volatile uint16_t unChannel5InShared;volatile uint16_t unChannel6InShared;volatile uint16_t unChannel7InShared;volatile uint16_t unChannel8InShared;

uint8_t bUpdateFlags = 0; // check shared update flags to see if any channels have a new signal // for nicely formatted serial output use this if(bUpdateFlagsShared == 0xFF) // for more responsive projects update any channels whenever a new signal is available using this // 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.

// 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 if(bUpdateFlags & CHANNEL1_FLAG) { // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it // servoChannel1.writeMicroseconds(unChannel1In); Serial.println(""); Serial.print(bUpdateFlags); Serial.print(","); Serial.print(unChannel1In); Serial.print(","); }

if(bUpdateFlags & CHANNEL2_FLAG) { // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it // servoChannel2.writeMicroseconds(unChannel2In); Serial.print(unChannel2In); Serial.print(","); }

if(bUpdateFlags & CHANNEL3_FLAG) { // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it // servoChannel3.writeMicroseconds(unChannel3In); Serial.print(unChannel3In); Serial.print(","); }

if(bUpdateFlags & CHANNEL4_FLAG) { // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it // servoChannel4.writeMicroseconds(unChannel4In); Serial.print(unChannel4In); Serial.print(","); }

if(bUpdateFlags & CHANNEL5_FLAG) { // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it // servoChannel5.writeMicroseconds(unChannel5In); Serial.print(unChannel5In); Serial.print(","); }

if(bUpdateFlags & CHANNEL6_FLAG) { // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it // servoChannel6.writeMicroseconds(unChannel6In); Serial.print(unChannel6In); Serial.print(","); }

if(bUpdateFlags & CHANNEL7_FLAG) { // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it // servoChannel7.writeMicroseconds(unChannel7In); Serial.print(unChannel7In); Serial.print(","); }

if(bUpdateFlags & CHANNEL8_FLAG) { // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it // servoChannel8.writeMicroseconds(unChannel8In); Serial.print(unChannel8In); Serial.print(","); }}

21 comments:

Hi Duane! Maybe you can help me out!I'm able to compile and run your code. Generating the PPM-Signal seems to work fine (i cheked it with a logic analyser). But i think the input interrupts don't work. In the Serial Monitor i only see "multiChannels". I'm working on an Arduino Mega 1280.

Hi, This has an easy solution, the Arduino UNO, Leonardo, Mega 1280 and Mega 2560 are all based on different chips. Each chip has the interrupts attached to different pins. Before I suggest a solution for you, how many channels do you need ?

Duane, I've wired the loop-back on a Arduino UNO. This is (part of) the serial monitor result:255,1112,1204,1304,1404,1504,1620,1708,1796,255,1108,1204,1304,1404,1504,1620,1708,1796,255,1108,1204,1300,1404,1504,1624,1708,1796,255,1112,1204,1300,1404,1500,1624,1712,1800,255,1112,1204,1304,1404,1504,1620,1712,1800,255,1112,1204,1304,1400,1504,1620,1708,1796,255,1108,1204,1304,1404,1504,1624,1708,1796,255,1108,1204,1300,1404,1504,1624,1708,1796,255,1112,1204,1304,1404,1504,1624,1712,1800,My Pro Mini is soldered into a project, can't easily do the loop-back wiring. I've ordered another Pro Mini, with which I will do just that. Not sure if results are expected to be different for UNO and Pro Mini.

My initial reason for commenting had to do with using PWM out, which doesnt work (Pro Mini) on pin 9 and 12, while it does on 3,5,6. For my project I figured I can do without PWM and just emulate by quickly switching via digitalWrite (not tested yet).

Does this info give you any clue, as you requested in my previous comment?

Duane, full test runs fine (I was initially confused by the channel reading 255, which is the flag, not a channel). Receiving works on all channels, sending also. Now starting to add PWM on the Uno...Regards, JH

Duane, me again. In the loopback test script, everything seems to stop if I comment out the line PCintPort::attachInterrupt(CHANNEL8_IN_PIN, calcChannel8,CHANGE);I don't know why, can u clarify? I'm working towards 3 RC in channels and 1 servo out channel.Other tests with PWM give very unclear results, mostly increased servo jitter.Hope to trigger a clue by commenting, while I keep searching on myself... Thanks, JH

I suggest using the original code posted above for now and interface your RC Receiver one channel at a time without making any changes to the code.

Once you have the channels up and running I can advise the changes you need for your 3 channel, 1 Servo and PWM Project, but for now, lets just get the channels working.

If you following this procedure and seeing unexpected results it is almost certainly due to the wiring, do you have a common ground between the receiver and the Arduino ? How are you powering the Arduino ?

"it is almost certainly due to the wiring", that most likely has been it. My Mini project is working fine now, though I'm not fully sure what did it in the end. It's not your library, which now works great! Thanks for that JH

I'm having an issue with the loopback test on my Pro Micro, which uses the ATmega32U4. I tried the initial sketch and wasn't getting any serial output back.

I tried commenting everything in the setup and void loops out, then adding a simple Serial.println("test"); to the loop to see if the board was responding. This test worked, and I got my response back.

Then I tried uncommenting the setup loop, and the problem came back. I uncommented it line by line and it appears that the issue is in the PinChangeInt library. The program just locks up when it gets to that point. Any ideas?

I have set up the sketch just as describe above, using a Uno. The loop back seems to work just fine. However, I comment out channel one and attach a servo, (yes, it has a separate power rail and yes, everything has a common ground. However, the serial monitor shows "multichannels", and then nothing. The servo does not respond. This is a simplified explanation, I have quadruple checked libraries, the sketch, and all hardware. The system seems to just hang. Rick Harmsrick@inharmsway.org. PS The Arduino forum seems to be non-operational.

Hi, No need to comment out anything, just swap the wire that connects the output to the input so that the input is connected to your receiver. You should see each channel that you swap over is now being controlled by your transmitter/receiver there is no need to change any of the code to do this.

Once you have your channels up and running this way - i.e. updating in the serial monitor - I will advise you how to start controlling the servos.

Thank youOK, I did as you advised, it works fine. I have one channel of the receiver connected and can indeed see the channel change in the serial monitor.A clarification from my previous post. My next step, I thought, would be to comment out this: servoChannel1.writeMicroseconds(1100); and uncomment this: // servoChannel1.writeMicroseconds(unChannel1In);I await your correction. :-) Thank youPS I am also having compile problems with RCArduinoFastLib, but, for now, one step at a time

Hi, In order to get nicely formatted output, the loopback code waits to get a signal from all of the channels before it prints anything. My guess is that in you earlier test you disconnected a channel in hardware or software. Now that you have things working as expected, your right about the change you need to make - just uncomment the line that writes a fixed value and write out the value that is read in instead.

OK, got it working. Thank you.Now, I went ahead and set up channel two, (in and out) and got it working.My goal is to build a "super" mixer. So, I decided to use the input channel one to drive the servo channels one and two. I need to go through the code, get a better handle on how it works. I see that even if i am not using, for example, the channel two input, it needs to have input or everything comes to a halt. Next step, I want to drive 16 servos. The Uno, by itself, cannot do that.Options1. Use the Mega?2. Use your fast lib and the 4017 decoder.3. use the I2C 10 bit servo driver board from Adafruit (16 channels)4. Use the 8 bit serial connected servo driver board from Pololu (up to 24 channels)

There are pros and cons for each of the above. This is my idea of fun. I think you understand :-)Rick Harms

I would normally react to a channel whenever I get a signal on that channel, regardless of whether I have signals on any other channel. This works great in real projects, however for the loop back where you initially want to see nicely formatted result I wait for a signal on all channels before printing the inputs.

To change this to the more responsive behaviour, see the comments in the code here -

uint8_t bUpdateFlags = 0; // check shared update flags to see if any channels have a new signal // for nicely formatted serial output use this if(bUpdateFlagsShared == 0xFF) // for more responsive projects update any channels whenever a new signal is available using this // if(bUpdateFlagsShared)

Your servos will work just fine, but your serial output will look a mess - now that you are starting to add servos you can start removing the serial output anyway.

As for lots of servos, it depends on how complex the logic to drive them will be, you can drive them using two 30 cent components and this library http://rcarduino.blogspot.ae/2012/10/arduino-serial-servos-20-servos-4-pins.html with your UNO, but you might need the extra memory of a Mega for the driving algorithms.

Hey,I'm keen on RC-Cars and got into programming recently.I found your interesting code and uploaded it to a Nano V3.0 board.Step 1 (loop back testing) worked well, the serial monitor showed values between 1100 and 1800 all the time.In Step 2 I disconnected output of channel 1 and connected the input of channel 1 to the signalport of my receiver. The serial monitor showed values above 4000!What to do now?