Well, sorta. It's still about month away but I already ordered my birthday presents, just to make sure they'll be there on time (sure...). But anyway, I'll be the proud owner of an Arduino Mega 2560 (Atmega2560) and a clone Nano (with an Atmega168), so I can finally test whether or not my code actually works on some other hardware than an Atmega328p. I'm especially interested in the 2560 with its truckload of pins.
In the past I've tried building a transmitter using an existing case and a Nano, but I quickly noticed that I needed more pins than the Nano had to offer. Once you want to add a graphical display you quickly run out of free pins (it needs like a dozen or more). One solution would be to add a second nano; one for the user interface and the other for input processing and output. It's easy to make them communicate with I2C, but tedious as well. A little demo here. Having a single microcontroller handle everything should make things easier (I hope) and the extra Flash and SRAM is a welcome addition.
The new Nano should be fun too. It's a bit of an oddball, it has a switch to activate a 3.3V voltage regulator which makes the whole thing run on 3.3V instead of 5.0V, this comes in handy as most sensors also run on 3.3V and it's not always guaranteed that their IO is 5V tolerant...
Hello Daniel,
I have just stumbled apon your project for ArduinoRC and your library (0.3). Extremely nice work!
Did you ever get to completing 0.4 or 0.5 and if so, is it available for use?
I'm fairly new to C++ and Arduino but have an application that could benefit greatly from your work.
Let me know if you would be so kind.
Tony Elliott
Hi Tony,
I'm no longer actively working on this project for a while now. I never got around to finishing v 0.4. You can find the latest/greatest version in the subversion repository ( https://sourceforge.net/p/arduinorclib/code/HEAD/tree/trunk/ ).
If you have any questions let me know.
Daniel
Daniel, since you offered support for questions, I have one. Jeti Models have introduced a 24 channel upgrade to their 16 channel transmitter systems. I'll be using their serial link (EX-bus) to get the channel data but your libraries currently have a limitation of 18 channels. is that a hard limit or can the channel count be increased to 24? If so, what other changes needed to be made other than rc_config, inputchannel and outputchannel?
Tony, the channel limit is rather arbitrary: it's what the top model of Futaba was supporting at the time.
The changes you suggested should suffice. You may want to keep an eye on SRAM usage. The Arduino IDE should mention something like this:
Global variables use 9 bytes (0%) of dynamic memory, leaving 2,039 bytes for local variables. Maximum is 2,048 bytes.
Once that gets up to 80 or 90 % you can start getting all sorts of weird problems.
Daniel, thanks for the quick response. I'll get these updated to support 24 channels. I intend to use the Teensy that has significanly more SRAM (64k I believe). Should be plenty but I will sure keep an eye on things!
Again, appreciate your help. Tony
Good luck,
Just keep in mind that certain pieces of code are very ATmega328 specific. There's some parts that directly access timers like the PPM, Servo and Timer classes.I have no idea how compatible that is with the Teensy. But the compiler will probably inform you of those bits :)
Hello Daniel. I was wondering if you could guide me a little with the library. Firstly, I'm new to OOP and I'm essentially "reverse engineering" the classes you've written and I'm attempting to identify all the linkages between the core classes. It's a challenge for me.
If you have the time, I'd like to understand the following if possible.
You have five buffers - switch, input, output, outputchannel and inputchannel. My application will use a serial data feed from a receiver (Futaba's SBUS, Jeti's EXBUS etc), and as such I will not need ServoIn or PPMIn. Additionally, many of the classes you've created will not be used in my application since this is a downstream application from the receiver.
As background, I'm creating a programmable "channel expander' where the primary bank of servos are simple pass-though signals via your pipe class. I think I understand that. Any clones of a specific channel will need subtrim, EPA-High, EPA-Low and reversing and the Channel class provides that. The data for clones is part of a programming function that can be entered on power up if needed the results of which will be stored in EEPROM for later retrieval for Channel.
My first assumption is that I will need to create "Channel" instances of every "cloned" channel as well as the primary channels (primary channels number 24). Is that a correct assumption?
Channels can have 2, 3, 4 or 5 "clones". With a 24 channel wide system, this could yield 80 to 96 servos in total. "Clones" or "banks" 2, 3, 4 and 5 are all fully programmable as noted above.
My second assumption is that I need to load the channel deflection data (in uS) from the serial feeds into InputChannel buffer. Is that correct?
What is the core difference/purpose between the Output and OutputChannel buffers? I'm not sure I understand this aspect of your overall architecture.
Lastly, since my data source is a pre-defined serial data feed from the transmitter (via the receiver), I'm assuming that the switch and input buffers are not needed for my application. Are there any consequences to that and are there any steps I need to consider to mitigate issues downstream?
I thank you in advance for spending the time to assist me on this but also understand if you don't.
Thank you. Tony
Hi Tony,
The buffers are there to separate functionality from the technical bits.
Inputchannel containts raw input data from, for example, a PPM signal, or multiple channels of a receiver. The values are in microseconds.
The input buffer identifies functional inputs. Such as aileron, elevator, throttle, rudder, etc. By setting up a mapping between inputchannel and input you can implement assignable switches for example. The input buffer contains signed values between -256 and +256. The InputChannelToInputPipe can be used to read values from the InputChannel buffer, convert them from microseconds to "normalized" values and write them in the input buffer. Other classes like the Gimbal class or AnalogSwitch class read from hardware pins and write directly to the input buffer.
The switches buffer does exactly what the name implies :) Some classes read from this buffer, like the ThrottleHold class. It reads a specified switch and modifies the throttle entry in the input buffer.
The output buffer contains functional output, such as multiple engines, flaps, rudders, etcetera. Still normalized values. An RC helicopter, for example, has a swash plate which needs mixing. So aileron, elevator and throttle input are mixed to control three servos simultaneously. So the SwashPlate class reads the ail, ele and thr entries from the input buffer, does some calculations, and writes the result to AIL1, ELE1 and THR1 in the output buffer. On a multi-engine airplane, rudder input may be mixed into THR1 and THR2. Basically, this is where mixing happens. The InputToOutputPipe simply copies values between the input and output buffer.
The outputchannel buffer contains raw output data in microseconds and its values can be used to transmit using PPM or to control servos directly. The Channel class reads from the output buffer, performs endpoints, subtrimming and reversing, and writes the result to the outputchannel buffer. The OutputToOutputChannelPipe skips these calculations and simply converts the normalized values to microseconds directly.
For a quick overview of all classes, see https://sourceforge.net/p/arduinorclib/wiki/Input-Output%20system/
So in your case you want the output buffer to have 24 entries, and the outputChannel buffer to have 96 entries and set up 80 Channel instances so you can have subtrim for each of the 80 cloned servos.
OutputToOutputChannelPipe(0, 0) // first bank, pass-through
OutputToOutputChannelPipe(1, 1)
...
OutputToOutputChannelPipe(23, 23)
Channel(0, 24) // second bank, first set of clones
Channel(1, 25)
...
Channel(23, 47)
Channel(0, 48) // third bank, second set of clones
etc
If you can get the incoming 24 channels in microsecond data then you can write that directly to the inputchannel buffer, set up 24 InputChannelToInputPipes, 24 InputToOutputPipes, 24 OutputToOutputChannelPipes and 80 Channels.
In your case, you may want to ditch the inputbuffer entirely and modify the InputChannelToInputPipe so it writes to the outputbuffer directly.
And you don't need the switches buffer either.
So, all your assumptions seem to be correct :)
What are you going to do on the output side of things? Will you be controlling 96 servos directly? Or do you have separate hardware that can do that for you?
Daniel, thanks for the indepth response. I finally decided to draw a flow chart last night to try and understand this and have just "flowed" through it with your description - I think I've got most of it.
One question on your paragraph after the channel layout section. You mention the need to have the pipes in place between InputChannelToInput and InputToOutput. You then mention eliminating the InputChannel buffer which I see and agree with in general. If I eliminate the InputBuffer altogether and write directly to the Output buffer, I'm assuming I dont need the following:
switch buffer,
input buffer
InputChannelToInputPipe
InputToOutputPipe.
BUT, the Output buffer is normalize, the InputChannel buffer is not. I'm not sure I can eliminate the InputChannel buffer given the difference between the data ranges in each buffer. I'm not an experienced programmer at all so this might be a difficult change for me to make. I do understand about modifying the InputChannelToInputPipe to be InputChannelToOutputPipe and I'd have to take a hard look at what's needed to accomplish this.
Regarding output, I'm using separate hardware, specifically the NXP PCA9685, 16 channel LED PWM driver chip over I2C. 5 chips offers 80 channels. I have solutions already coded where one or two 24 channel banks come directly from the Mega output pins but the "ticking" that accompanies these solutions utilizing the servo library is unacceptable. The PCA9685 offers a much more solid and stable output. I'm using the Adafruit PCA9685 shileds as a development platform on my Mega2560 and a separate UNO to simulate the Jeti EXBus data stream. It get's too cumbersome to have my Jeti TX and receivers attached so the UNO is a great development aid - and portable!!.
Daniel, again, many thanks.
Tony
Hi Tony,
Try the attached files (I haven't compiled or tested them), they're a modified version of the InputChannelToInputPipe that reads from the InputChannel and writes directly to the output buffer. I renamed the class as well.
I ran into the same problems while trying to run servos directly from the Arduino; it was a complete disaster; jitter everywhere. Hence my question :)
Yikes, that was fast! Thank you. I'm still trying to get my head around the structure so I don't even have a sketch pulled together yet but when I do, I will include this and see how it goes.
Hi Daniel. I just got around to working with your new pipe. Unfortunately, I have an error on compile that I don't know how to resolve. I suspect you do!!
Arduino: 1.6.7 (Windows 7), TD: 1.28, Board: "Arduino/Genuino Mega or Mega 2560, ATmega2560 (Mega 2560)"
C:\Users\Tony\Documents\Arduino\libraries\RC\InputChannelToOutputPipe.cpp: In member function 'void rc::InputChannelToOutputPipe::apply() const':
C:\Users\Tony\Documents\Arduino\libraries\RC\InputChannelToOutputPipe.cpp:37:43: error: 'getOutputChannel' is not a member of 'rc'
writeOutputValue(rc::microsToNormalized(rc::getOutputChannel(m_source)));
exit status 1
Error compiling for "Arduino/Genuino Mega or Mega 2560"
I did try to include the outputchannel.h into the pipe thinking that would resolve but it didnt. Any pointers?
Tony
Last edit: Anthony Elliott 2016-05-05
Daniel, I think I found it. In writeOutputValue, the source should be getInputChannel and not getOutputChannel. I have changed and it is compiling well.
Here's my development platform!! The board between the Mega on the bottom and the Adafruit PWM shields is a custom made breakout that also includes a MAX6818 switch debouncer, four push-button programming switches and 4 positional toggle switches to simulate limit switches for sequencer development. It has a beeper, reset switch and power LED. It is powered separately from the Mega and will drive servos from the external power source.
Last edit: Anthony Elliott 2016-05-02
Hello again Daniel. I hope I am not bothering you to frequently but I have a question to ask. I need to get access to the contents of each of the buffers and to print them to the serial port for viewing. I see you have "getRawxxxxxx" functions that return a pointer. I'm nowhere close to being educated on pointers when they apply to an array embedded in a namespace/class etc. I assume I can use that pointer to step through memory locations but I have been unable to determine the correct syntax for that "function" within my loop. I believe it is something like this (I'm using 96 channel locations):
for (uint16_t ptr = rc::getRawOutputChannels(); ptr < ptr + 96; ++ptr){
Serial.println(ptr);
}
but I'm not getting the results I would expect. This seems to read thousands more entries than just the 96 I need. Can you help?
Thanks again, Tony
Daniel - Ignore again. It seems that after posting my questions, I have epiphanies and find the correct answer!!! Not sure why!
for (uint16_t ptr = rc::getRawOutputChannels(); ptr < rc::getRawOutputChannels() + 96; ++ptr){
Serial.println(ptr);
}
almost had it!!
Hello again Daniel.
I have a favor to ask you. I'd like to really simplify my implementation. I have a working solution for 96 channels but the processing time for the input data stream and all the updates is approaching the maximum time I have available to update signals to the servos. I'd like to widen that margin becuase it is real close. So, I'd like to eliminate the InputChannel buffer (as you previously noted) and go directly to the Output Buffer and OutputChannel buffer.
To that end, I'd like to eliminate normalization in the Output buffer since my application is not a TX. I'm currently using InputChannelToOutputPipe, Channel, OutputToOutputChannelPipe and util to achieve the functionality I need. What classes would need changes to remove normalization? I believe I can make the changes myself but I want to ensure I have everything identified up front before starting the process.
My assumptions up front are as follows:
1). The value limits in Channel need to be adjusted for subtrim, EPAMax and EPAMin. Reverse and speed can stay as they are.
2). Channel::apply(uint16_t p_value) needs to have the rc::normalizedToMircos() call removed
3). I can load Output directly with my data stream microseconds with setOutput. I do this already for the InputChannel load.
4). in OutputToOutputChannelPipe, remove the call to rc::normalizedToMicros().
Are there any other areas that need to be considered beyond these? Recall that I do not need mixing, Inputs or switches.
Thanks in advance for you help.
Tony
FYI, I attached a block diagram - Top = what you have today, bottom = what I would like to do.
Last edit: Anthony Elliott 2016-05-10
Hi Tony,
You're almost correct with your assumptions.
I think you have all the points of interest covered :)