Sunday, February 9, 2014

Arduino micros() function with 0.5us precision - using my Timer2_Counter Library

...merging the world of Arduino and Radio Control, one tool at a time...
...CODE FOR A PRECISE MICROS() FUNCTION IS POSTED BELOW...

"I wrote a libary to get 0.5us precision on a "micros()" replacement function, so that I can get repeatable results reading a PWM or PPM signal, to within 1us.  I searched all around the internet and could not find something comparable (or that was easy to use, and maintained the Arduino's ability to write PWM signals via the Servo Libary), so I think this is my first real contribution to the world of Arduino and Radio Control."  

Download the code by clicking here --> then go to "Download ZIP" at the bottom of the right-hand pane.  Extract the files, then run the example by opening the "Timer2_Counter_Basic_Example.ino" file and uploading it to your Arduino.  Then, open the Serial Monitor to see the output.


(font above is type "standard," from here)

By Gabriel Staples
Written: 9 Feb. 2014
Last Updated: 17 May 2014
-added PPM/PWM links at very bottom of post - 26 Feb. 2014
-moved code from Google Drive to GitHub, & updated download links - 12 April 2014
-I have now turned this into a full-blown, true Arduino library!  Now you can actually install it like a normal Arduino library, and use the #include <eRCaGuy_Timer2_Counter.h> line and everything!  Download the latest code by clicking the download link - 17 May 2014
-26 July 2014 - minor changes to function names (removed the "T2" parts since I made that change in my library several months back)

========================================================================
Please support my work and contributions by visiting my brand new store and purchasing an Arduino from me here.
========================================================================

Related Articles:

In order to interface Arduino with the world of Radio Control, I need to be able to interpret and even duplicate the signals which Radio Control vehicles use.  These signals include Pulse Position Modulation (PPM) and Pulse Width Modulation (PWM).  I will not go into the details of how these signals work, but I will say that I have spent a great deal of time learning about them to a very detailed level.  This way, I can have the knowledge to read and manipulate them.

For those of you who don't know: the signal coming out of the trainer port, or going into the trainer port, on the back of an RC transmitter (Tx), is a PPM signal.  The signal coming out of a receiver (Rx) and to a servo or Electronic Speed Conroller (ESC) is a PWM signal.

If an Arduino can read the PPM signal coming out of the back of a Tx, this is EXTREMELY useful in debugging your radio and setup by allowing you to see what pulse widths your Tx is outputting for each channel.  Additionally, it allows you to use your RC Tx as an INPUT DEVICE to control an Arduino!

You now have access to all these analog and digital inputs (sticks, scroll wheels, switches, etc), to manipulate your Arduino!  And, since many RC hobbyists (like me) have many cheap radios from RTF (Ready to Fly) kits and things lying around, this is very useful so that we can finally put them to use doing something else.  For instance, I plan on using one of my old radios, with an Arduino nano and a Leonardo-compatible device, to act as a wireless mouse for controlling my wife's computer when connected to the TV.  Then, I can sit on the couch and control the screen with an RC Tx!

If an Arduino can create a PPM signal to go into the trainer port on the back of a Tx, this is EXTREMELY useful because it allows you to use the Arduino to drive your RC vehicle via Feed-Forward loops (ie: you can preprogram commands in the Arduino, like "go forward 20 feet, then turn left 45 deg," or "do continuous figure 8's."

Similarly, putting your Arduino in at the Rx end is also very useful, because you can create your own custom mixes to manipulate your servos or speed controllers, or you can create or modify an autopilot.  Also, knowing how to read the PPM and PWM signals from a receiver also enables you to use a standard RC Tx/Rx combination as a wireless communication device for your Arduino!  You can even send data from one Arduino to another, over open-air distances up to 1km, using this technique, again, if you understand the signals well enough.  This makes RC and Arduino a very powerful combination, and to the Arduino user: I should note that many older 72MHz RC Tx/Rx combos exist out there that are being sold for ~$20 used by Radio Control hobbyists who are transitioning to the newer 2.4GHz spread-spectrum systems.  If you see a 72MHz system for only a few bucks, grab it!  It's a great tool for your Arduino.

Now...on to the title of this post.  In learning to manipulate and read PWM and PPM signals, it quickly became apparent to me that timing is critical.  I originally used the built-in Arduino micros() function for my timing, but it only has a resolution of 4us.  This is very poor, as it means that I get errors up to +/- 8us, over time (determined experimentally).  PWM and PPM signals usually range from 900~2100us max, but are more commonly 1100~1900us (this is default on a Spektrum brand Tx).  In your standard "computerized" (ie: microcontroller-run) RC Tx, when you change your "%" range or endpoint settings, each percent corresponds to 4us.  (This is calculated as follows: 1900-1100 = 800us, which is +/-100% of the standard travel, so 1500 to 1900us is the +100% portion, which covers 400us, and 1100 to 1500 is the -100% portion, which covers 400us.  Therefore, 400us/100% = 4us per 1%.)  Therefore, assuming an error span of 12us, that's a fluctuation in simply reading or writing a fixed channel value, of up to 3% as seen in the radio settings.

I didn't like that, so I wrote a "libary" to get 0.5us precision on a "micros()" replacement function, so that I can get repeatable results reading a PWM or PPM signal, to within 1us.  I searched all around the internet and could not find something comparable (or that was easy to use, and maintained the Arduino's ability to write PWM signals via the Servo Libary), so I think this is my first major contribution to the world of Arduino and Radio Control.

Here is the code:

First, I will post an example code which shows how to use my Timer2_Counter "Library."  I have thoroughly commented all of my code.  You will see that I use micros() as a comparison, to show that my code does in fact work.

-Due to line wrap when I pasted the code on my blog, the formatting of the code below is not perfect.  When you view it in the Arduino IDE (Integrated Development Environment), however, it looks good.

Download the code by clicking here --> then go to "Download ZIP" at the bottom of the right-hand pane.  Extract the files, then run the example by opening the "Timer2_Counter_Basic_Example.ino" file and uploading it to your Arduino.  Then, open the Serial Monitor to see the output.

Function Definitions of Timer2_Counter:
-This is a list of the available functions, for a full description of each function, refer to the comments at the top of the "Timer2_Counter.ino" file.

  • setup();
  • get_count();
  • get_micros();
  • reset();
  • revert_to_normal();
  • unsetup();
  • overflow_interrupt_off();
  • overflow_interrupt_on();

1) Example Code
/*
Timer2 Counter Basic Example
-A timer function with 0.5us precision, rather than 4us precision like the built-in Arduino micros() function has.
By Gabriel Staples
Visit my blog at http://electricrcaircraftguy.blogspot.com/
-My contact info is available by clicking the "Contact Me" tab at the top of my blog.
-Please support my work & contributions by buying something here: https://sites.google.com/site/ercaguystore1/
My original post containing this code can be found here: http://electricrcaircraftguy.blogspot.com/2014/02/Timer2Counter-more-precise-Arduino-micros-function.html
Written: 8 Feb. 2014
Updated: 9 Feb. 2014
*/

/*
===================================================================================================
  LICENSE & DISCLAIMER
  Copyright (C) 2014 Gabriel Staples.  All right reserved.
  
  This code was written entirely at home, during my own personal time, and is neither a product of work nor my employer.
  Furthermore, unless otherwise stated, it is owned entirely by myself.
  
  ------------------------------------------------------------------------------------------------
  License: GNU General Public License Version 3 (GPLv3) - http://www.gnu.org/licenses/gpl.html
  ------------------------------------------------------------------------------------------------

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see http://www.gnu.org/licenses/
===================================================================================================
*/

//CODE DESCRIPTION:
//This code demonstrates the use of my Timer2, which provides a more precise timer than micros().  
//micros() has a precision of only 4us.  However, Timer2 keeps track of time to a precision of 0.5us.
//This is especially important in my code which reads an RC receiver PWM signal, which varies from 900~2100us. 
//Though this code demonstrates the use of the Timer_2 functions I have written, it does not adequately demonstrate the 
//real utility of the code, so I will state the following:
//By using my Timer2 timer to measure the PWM high time interval on an RC receiver, in place of using micros(), I can get repeatable 
//pulse width reads with a fluctuation of ~1us, rather than having a read-in range fluctuating by as much as +/- 4~8 us when I use micros().
//This is an increase in precision of ~8x.

  
void setup() {
  //configure Timer2
  setup_T2(); //this MUST be done before the other functions work; Note: since this messes up PWM outputs on pins 11 & 3, 
              //you can always revert Timer2 back to normal by calling unsetup_T2()
  
  //prepare serial
  Serial.begin(115200);  
  
  //Output a header of info:
  Serial.println("Notes:");
  Serial.println("micros() has a precision of 4us");
  Serial.println("get_T2_count() with unsigned long final data type has a final precision of 1us, and is fast");
  Serial.println("get_T2_count() with float final data type has a final precision of 0.5us, and is not quite as fast");
  Serial.println("get_T2_micros() has a precision of 0.5us, and is slower than the above 2 methods, so one of the above 2 methods is preferred");
  Serial.println("==============================================");
}


void loop() {
  //Grab Start Times
  unsigned long t_start1 = micros(); //us; get the current time using the built-in Arduino function micros(), to a precision of 4us
  unsigned long t_start2 = get_T2_count(); //count units of 0.5us each; get my Timer2 count, where each count represents 0.5us; PREFERRED METHOD
  float t_start3 = get_T2_micros(); //us; get the current time using my Timer2; Note: THE METHOD ONE LINE ABOVE IS PREFERRED OVER THIS METHOD
                                    //since using floats is slower than using unsigned longs
  
  //Wait a bit                       
  delayMicroseconds(1000);
  
  //Grab End Times
  unsigned long t_end1 = micros(); //us; using built-in Arduino function that has a precision of 4us
  unsigned long t_end2 = get_T2_count(); //count units of 0.5us each; using my Timer2 count, where each count represents 0.5us
  float t_end3 = get_T2_micros(); //us; using my Timer2 micros, which has a precision of 0.5us
  
  //Calculate elapsed times
  unsigned int t_elapsed1 = t_end1 - t_start1; //us; using micros()
  unsigned int t_elapsed2_ul = (t_end2 - t_start2)/2; //us; to a precision of 1us, due to using unsigned long data type truncation, using Timer2 count
  float t_elapsed2_fl = (t_end2 - t_start2)/2.0; //us; to a precision of 0.5us, due to using float data type for final time difference calc; note that I divide by 2.0, NOT 2
  float t_elapsed3 = t_end3 - t_start3; //us; to a precision of 0.5us
  
  //Display the results
  Serial.println(""); //insert a space
  Serial.print("elapsed time using micros() = ");
  Serial.print(t_elapsed1);
  Serial.println("us");
  Serial.print("elapsed time using get_T2_count() with unsigned long final data type = ");
  Serial.print(t_elapsed2_ul);
  Serial.println("us");
  Serial.print("elapsed time using get_T2_count() with float final data type = ");
  Serial.print(t_elapsed2_fl);
  Serial.println("us");
  Serial.print("elapsed time using get_T2_micros() = ");
  Serial.print(t_elapsed3);
  Serial.println("us");
  
  //Wait a second before repeating
  delay(1000); 
}


2) Timer2_Counter main "Library" file:
/*
Timer2 Counter
-A timer function with 0.5us precision, rather than 4us precision like the built-in Arduino micros() function has.
By Gabriel Staples
Visit my blog at http://electricrcaircraftguy.blogspot.com/
-My contact info is available by clicking the "Contact Me" tab at the top of my blog.
-Please support my work & contributions by buying something here: https://sites.google.com/site/ercaguystore1/
My original post containing this code can be found here: http://electricrcaircraftguy.blogspot.com/2014/02/Timer2Counter-more-precise-Arduino-micros-function.html
Written: 7 Dec. 2013
Last Updated: 11 Dec. 2013
*/

/*
===================================================================================================
  LICENSE & DISCLAIMER
  Copyright (C) 2014 Gabriel Staples.  All right reserved.
  
  This code was written entirely at home, during my own personal time, and is neither a product of work nor my employer.
  Furthermore, unless otherwise stated, it is owned entirely by myself.
  
  ------------------------------------------------------------------------------------------------
  License: GNU General Public License Version 3 (GPLv3) - http://www.gnu.org/licenses/gpl.html
  ------------------------------------------------------------------------------------------------

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see http://www.gnu.org/licenses/
===================================================================================================
*/

/*
CODE DESCRIPTION:
BACKGROUND:
-This code uses Timer2 to create a more precise timer than micros().  Micros() updates only every 4us.  However, I want something that will update every 0.5us, and that's 
what this provides. 
-The downside is that it changes the behavior of PWM output (using analogWrite) on Pins 3 & 11.
-The upside is that I have managed to get a precise timer using the 8-bit Timer2, rather than the 16-bit Timer1, so that I can keep the Timer1 unmodified so I can continue
 to use the servo library as desired.  Note here that the servo library relies on the Atmega328 16-bit Timer1.  I have deciphered this by knowing that
 A) using the servo library at all disables PWM output on pins 9 & 10; see here: http://www.arduino.cc/en/Reference/Servo
 and B) PWM on pins 9 & 10 is controlled by Timer1; see here: http://playground.arduino.cc/Main/TimerPWMCheatsheet
-Note: every now and then, but not very often, you will see that the value returned by get_T2_count or get_T2_micros is late by ~4us.  This is because Timer2 is only
 an 8-bit timer, so every 128us the overflow interrupt is called to increment the overflow counter, and stepping into and out of the interrupt takes 4~5us
 according to Nick Gammon. Source: Nick Gammon; "Interrupts" article; "How long does it take to execute an ISR?" section, found here: http://www.gammon.com.au/forum/?id=11488
IMPLEMENTATION:
-To use this code, simply copy this file (I have it named "Timer2_Counter.ino") into your directory (folder) where you are already working on a specific Arduino file.  
Next time you open up your main file you are working on, this code will automatically open up as an additional tab in the Arduino IDE.  
-I have tested this code ONLY on Arduinos using the Atmega328 microcontroller; more specifically, the Arduino Nano.

I heavily reference the 660 pg. Atmega328 datasheet, which can be found here:  http://www.atmel.com/Images/Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet.pdf


Version History:
(most recent event last; format year-month-day, ex: 20131211 is Dec. 11, 2013):
20131211 - completed first iteration of code

============================================================================================================================================================================
Function Definitions
 ___  _   _  _  _   ___  _____  ___  ___   _  _      ___   ___  ___  ___  _  _  ___  _____  ___  ___   _  _  ___ 
| __|| | | || \| | / __||_   _||_ _|/ _ \ | \| |    |   \ | __|| __||_ _|| \| ||_ _||_   _||_ _|/ _ \ | \| |/ __|
| _| | |_| || .` || (__   | |   | || (_) || .` |    | |) || _| | _|  | | | .` | | |   | |   | || (_) || .` |\__ \
|_|   \___/ |_|\_| \___|  |_|  |___|\___/ |_|\_|    |___/ |___||_|  |___||_|\_||___|  |_|  |___|\___/ |_|\_||___/
                                                     
setup_T2();  //This function MUST be called before any of the other Timer2 functions will work.  This function will generally only be called one time in your setup() loop.
             //"setup_T2()" prepares Timer2 and speeds it up to provide greater precision than micros() can give.
get_T2_count();  //gets the counter from Timer 2. Returns the Timer2 counter value as an unsigned long.  Each count represents a time interval of 0.5us.
                 //note that the time returned WILL update even in Interrupt Service Routines (ISRs), so if you call this function in an ISR, and you want the time to be
                 //as close to possible as a certain event that occured which called the ISR you are in, make sure to call "get_T2_count()" first thing when you enter
                 //the ISR.
                 //Also, note that calling "get_T2_count()" is faster than calling "get_T2_micros()," and is therefore the preferable way to measure a time interval.
                 //For example: call "get_T2_count()" at the beginning of some event, then at the end.  Take the difference and divide it by 2 to get the time interval
                 //in microseconds.
get_T2_micros(); //returns the Timer2 microsecond time, with a precision of 0.5us, as a float.  This function is slower than calling "get_T2_count()," and therefore is not
                 //the preferred way of getting time.  It is better to get the time by calling "get_T2_count()" then dividing the value by 2.  By choosing whether or not
                 //you want to call "get_T2_count()" or "get_T2_micros()," you can decide if you need the extra precision of a float, or not, at the cost of having 
                 //slightly slower code.
reset_T2(); //resets the Timer2 counters back to 0.  Very useful if you want to count up from a specific moment in time, or obtain an "elapsed time."
revert_T2_to_normal(); //this function might also be called "unsetup_T2".  It simply returns Timer2 to its normal state that Arduino had it in prior to calling "setup_T2"
unsetup_T2(); //the exact same as "revert_T2_to_normal()"
T2_overflow_interrupt_off(); //turns off the Timer 2 overflow interrrupt so that you no longer interrupt your code every 128us in order to increment your overflow counter. 
                       //This may be desirable when you are no longer referencing the T2 counter or timer and want your main code to run a touch faster, but you don't want
                       //to call unsetup_T2() in order to change all of Timer2's settings back to default.
                       //Since an interrupt takes ~5us to execute, and my Timer2 will overflow every 128us, disabling the Timer2 overflow interrupt will prevent 
                       //you from losing that amount of time (~5us) every 128us.
                       //Source: Nick Gammon; "Interrupts" article; "How long does it take to execute an ISR?" section, found here: http://www.gammon.com.au/forum/?id=11488
                       //Note: If you diable the Timer 2 overflow interrupt but still call get_TC_count() or get_TC_micros() at least every 128us, you will notice no 
                       //difference in the counter, since calling get_TC_count() or get_TC_micros() also checks the interrupt flag and increments the overflow counter 
                       //automatically.  You have to wait > 128us before you see any missed overflow counts.
T2_overflow_interrupt_on(); //turns Timer 2's overflow interrupt back on, so that the overflow counter will start to increment again; see "T2_overflow_interrupt_off()"
                            //explanation for more details.
============================================================================================================================================================================
References:
-cool font, type "small", from: http://www.network-science.de/ascii/

Additional Resources:
-Nick Gammon's "Timers & Counters" article - http://www.gammon.com.au/forum/?id=11504
-Nick Gammon's "Interrupts" article - http://www.gammon.com.au/forum/?id=11488
*/


//Set up Global Variables
//volatile (used in ISRs)
volatile unsigned long T2_overflow_count = 0; //initialize Timer2 overflow counter
volatile unsigned long T2_total_count = 0; //initialize Timer2 total counter
//normal variables
byte tccr2a_save; //initialize; will be used to backup default settings
byte tccr2b_save; //initialize; will be used to backup default settings


//Interrupt Service Routine (ISR) for when Timer2's counter overflows; this will occur every 128us
ISR(TIMER2_OVF_vect) //Timer2's counter has overflowed 
{
  T2_overflow_count++; //increment the timer2 overflow counter
}


//Configure Timer2
void setup_T2()
{
  //backup variables
  tccr2a_save = TCCR2A; //first, backup some values
  tccr2b_save = TCCR2B; //backup some more values
  
  //increase the speed of timer2; see below link, as well as the datasheet pg 158-159.
  TCCR2B = TCCR2B & 0b11111000 | 0x02; //Timer2 is now faster than default; see here for more info: http://playground.arduino.cc/Main/TimerPWMCheatsheet
  //Note: don't forget that when you speed up Timer2 like this you are also affecting any PWM output (using analogWrite) on Pins 3 & 11.  
  //Refer to the link just above, as well as to this source here:  http://www.oreilly.de/catalog/arduinockbkger/Arduino_Kochbuch_englKap_18.pdf

  //Enable Timer2 overflow interrupt; see datasheet pg. 159-160
  TIMSK2 |= 0b00000001; //enable Timer2 overflow interrupt. (by making the right-most bit in TIMSK2 a 1)
  //TIMSK2 &= 0b11111110; //use this code to DISABLE the Timer2 overflow interrupt, if you ever wish to do so later. (see datasheet pg. 159-160)
  
  //set timer2 to "normal" operation mode.  See datasheet pg. 147, 155, & 157-158 (incl. Table 18-8).  
  //-This is important so that the timer2 counter, TCNT2, counts only UP and not also down.
  //-To do this we must make WGM22, WGM21, & WGM20, within TCCR2A & TCCR2B, all have values of 0.
  TCCR2A &= 0b11111100; //set WGM21 & WGM20 to 0 (see datasheet pg. 155).
  TCCR2B &= 0b11110111; //set WGM22 to 0 (see pg. 158).
}  
  

//get total count for Timer2
unsigned long get_T2_count()
{
  noInterrupts(); //prepare for critical section of code
  uint8_t tcnt2_save = TCNT2; //grab the counter value from Timer2
  boolean flag_save = bitRead(TIFR2,0); //grab the timer2 overflow flag value
  if (flag_save) { //if the overflow flag is set
    tcnt2_save = TCNT2; //update variable just saved since the overflow flag could have just tripped between previously saving the TCNT2 value and reading bit 0 of TIFR2.  
                        //If this is the case, TCNT2 might have just changed from 255 to 0, and so we need to grab the new value of TCNT2 to prevent an error of up 
                        //to 127.5us in any time obtained using the T2 counter (ex: T2_micros). (Note: 255 counts / 2 counts/us = 127.5us)
                        //Note: this line of code DID in fact fix the error just described, in which I periodically saw an error of ~127.5us in some values read in
                        //by some PWM read code I wrote.
    T2_overflow_count++; //force the overflow count to increment
    TIFR2 |= 0b00000001; //reset Timer2 overflow flag since we just manually incremented above; see datasheet pg. 160; this prevents execution of Timer2's overflow ISR
  }
  T2_total_count = T2_overflow_count*256 + tcnt2_save; //get total Timer2 count
  interrupts(); //allow interrupts again
  return T2_total_count;
}


//get the time in microseconds, as determined by Timer2; the precision will be 0.5 microseconds instead of the 4 microsecond precision of micros()
float get_T2_micros()
{
  float T2_micros = get_T2_count()/2.0; 
  return T2_micros;
}


//reset Timer2's counters
void reset_T2()
{
  T2_overflow_count = 0; //reset overflow counter
  T2_total_count = 0; //reset total counter
  TCNT2 = 0; //reset Timer2 counter
  TIFR2 |= 0b00000001; //reset Timer2 overflow flag; see datasheet pg. 160; this prevents an immediate execution of Timer2's overflow ISR
}


//undo configuration changes for Timer2
void revert_T2_to_normal()
{
  T2_overflow_interrupt_off(); //turn off Timer2 overflow interrupts
  TCCR2A = tccr2a_save; //restore default settings
  TCCR2B = tccr2b_save; //restore default settings
}


//same as revert_T2_to_normal()
void unsetup_T2()
{
  revert_T2_to_normal();
}


//Turn off the Timer2 Overflow Interrupt
void T2_overflow_interrupt_off()
{
//  TIMSK2 &= 0b11111110; //use this code to DISABLE the Timer2 overflow interrupt; see datasheet pg. 159-160
  TIMSK2 &= ~(_BV(TOIE2)); //alternate code to do the above; see here for use of _BV: http://194.81.104.27/~brian/microprocessor/BVMacro.pdf 
}


//Turn the Timer2 Overflow Interrupt Back On
void T2_overflow_interrupt_on()
{
//  TIMSK2 |= 0b00000001; //enable Timer2 overflow interrupt. (by making the right-most bit in TIMSK2 a 1); see datasheet pg. 159-160
  TIMSK2 |= _BV(TOIE2); //alternate code to do the above; see here for use of _BV: http://194.81.104.27/~brian/microprocessor/BVMacro.pdf 
}


Additional References to Get Started Learning about Radio Control PPM (Pulse Position Modulation) and PWM (Pulse Width Modulation) Signals:

  1. http://www.camelsoftware.com/firetail/blog/radio/reading-pwm-signals-from-a-remote-control-receiver-with-arduino/ - Reading PWM Signals from a Remote Control Receiver, with Arduino
  2. http://www.mftech.de/ppm_en.htm - PPM Encoding
  3. http://www.mftech.de/buchsen_en.htm - R/C buddy box plugs/ pin assignment



========================================================================
Please support my work and contributions by visiting my brand new store and purchasing an Arduino from me here.
========================================================================

THE END


2 comments:

  1. Gabriel, I'm potentially interested in using your library but the GPL3 license is probably going to be too restrictive for me. As such I haven't even looked through what you have to see if it will work for me. Can you contact me so we can discuss what the license actually means and limits me to and so I can let you know what I have in mind for your library. My work is "mostly" for charity so maybe we can come to an arrangement. thanks. --Dave (contact@racecoordinator.net)

    ReplyDelete
    Replies
    1. Dave, thanks for the note! I emailed you. We can discuss more there.

      Delete

Thanks for your comment or question! If it is a question, I will try to get back to you quickly. Sincerely, Gabriel Staples.