Another PulsePosition Library Question.

Status
Not open for further replies.

dereckbc

Well-known member
After some encouragement I received here I thought I would try my hand at writing some code for a project I am working on. In a nutshell a 6-channel RC PPM Encoder using a Joystick and Slider. All analog Pots and a few switches. What I am not sure about is how to physically use the library. It consist of two files, a cpp and h file. First try is I opened the cpp file and tried to figure out how to insert my code. Got no where. Some said start with the LoopBack example and go from there. It finally hit me how to bring in the PPM files with the Include statement, duh? Sorry I am a newb.

OK anyway I have got a decent start on a sketch I think, but stuck and need some help. Code below. What is below does compile, and comes with Warning a Var not being used. It loads, and does generate a 6 channel PPM string. So as you look at the code, all my work is everything before the first WRITE instruction is where I got lost. Not even sure what I did write works like I think it should. So before the WRITE commands is where I think I declared my variables, assigned an Analog pin, then with the begin function I assume that means to read the Analog voltage. I hope I have it right up to this point.

OK my first stumbling block is to replace the current Write commands that now are just writing set numbers I put in there to verify on a Scope it did what I think it should and does. Problem is I do not know the command or syntax to do that. Would it be:

PPMOut.analogWrite(A0);

or

PPMOut.analogWrite(Ailerons);

or

PPMOut.write(1, A0);

or something else?

Lastly everything below the Write Commands is Greek to me and is where the WARNING comes from the compiler and it reads:

PULSE_TEST:32: warning: unused variable 'i'
int i, num;

^

PULSE_TEST:32: warning: variable 'num' set but not used
int i, num;

All the code in Red I do not know how to work with that came with the Loopback example. I know it updates a register some where and only changes if the analog input changes between pulses. I know I need it, but I got the code messed up. So any help or pointers is appreciated.




Code:
#include <PulsePosition.h>

// From Pulse Position Library
// 6-channel PPM Encoder

PulsePositionOutput PPMOut(FALLING); //declare PPM signal out, 6-channels, falling edge.
PulsePositionInput Alerons; //declare ch 1
PulsePositionInput Rudder; //declare ch 2
PulsePositionInput Throttle; //declare ch 3
PulsePositionInput Elevators; //declare ch 4
PulsePositionInput Gear; //declare ch 5
PulsePositionInput Aux; // declare ch 6


void setup() {
  PPMOut.begin(10);  // Start 6-Channel PPM OutPut
  Alerons.begin(A0); // Start reading Ailerons
  Rudder.begin(A1); // Start reading Rudder
  Throttle.begin(A2); //Start reading Throttle
  Elevators.begin(A3); // Start reading Elevators
  Gear.begin(A4); // Start reading Gear
  Aux.begin(A5); // Start reading Aux



  PPMOut.write(1, 2200);// [COLOR="#FF0000"]need to change all this to write the analog inputs[/COLOR]
  PPMOut.write(2, 2200);
  PPMOut.write(3, 2200);
  // slots 4 and 5 will default to 1500 us
  PPMOut.write(6, 2200);
}

[COLOR="#FF0000"]int count=0;

void loop() {
  int i, num;

  // Every time new data arrives, simply print it
  // to the Arduino Serial Monitor.
  num = Alerons.available();
  num = Rudder.available();[/COLOR]
  
  }
 
Last edited:
Hi Dereck,

Suggestion, it is sometimes easier to follow your questions, if instead of creating new threads and the like, you would start up a main project thread, like:
Creating my own flight controller, or ...

I am sort of lost here. What is it that is hooked up to pins A0-A5? Does each of them generate a pulse that you need to measure? Or are they some simple analog device, like a pot or slider?

I was assuming the later? So You don't have all of these things like Alerons.begin, unless, maybe it is a control that takes care of smoothing the output? Otherwise you are simple needing to do something like analogRead of the pin to get a value (0-123) unless you change number analog settings.

Note: earlier on some of my remote controls, I did averaging, by taking the average of the last lets say 8 analog Readings.
The pseudo code looked like:
Code:
uint16_t vals_array[8];
uint16_t vals_sum = 0;
uint8_t val_index = 0;

uint16_t getAveragedAnalog() {
    vals_sum -= vals_array[val_index];   // remove the last value
    vals_array[val_index] = analogRead(pin);
    vals_sum += vals_array[val_index];
    val_index = (val_index +1) & 0x7;
    return vals_sum/8;
}

It is not very eloquent, but worked OK. Also could include checks to see if the value from analogRead looked OK as compared to the other values...

Again I am not really understanding your setup. I can not imagine you have that many pulsePositionInput devices (if any). The input device is the opposite of the output device, where it reads in multiple pulse widths on one pin, probably like I got when I hacked a Hitec RC receiver to get one pin off of the clock chip...

Could be wrong.
 
Yep, it's not clear what you are trying to do. If you have a joystick and slider connected to A0, A1, A2, and you want to produce a PPM data stream on pin 7, presumably driving a radio transmitter to your RC device, then you would need things like
Code:
PulsePositionOutput myOut;
 in setup() you would myOut.begin(7);
in loop()  you would reach each analog input, map the values from 0-1023 to 600-2400
   adcval = analogRead(A0);
   ppmval = map(adcval, 0,1023, 600,2400);
   myOut.write(1, ppmval);
   adcval = analogRead(A1);
   ppmval = map(adcval, 0,1023, 600,2400);
   myOut.write(2, ppmval);
...
Obviously, the order of the PPM channels should correspond to what the receiver is expecting.

if instead, you're reading a PPM stream, then you would need lots of Servo instances to drive the various motors throttle, rudder etc.

You might try some of the simple Analog examples in the IDE, also see
https://www.arduino.cc/en/Reference/HomePage
 
I am sort of lost here. What is it that is hooked up to pins A0-A5? Does each of them generate a pulse that you need to measure? Or are they some simple analog device, like a pot or slider?

Simple Analog Linear taper 5K Pots device to provide 0 to 3.3 volts from a Joystick pots

Since I asked my question I figured out a few things, stil have something hosed. But here is the new code cleaned up.

int AI_Pin_AUX = 6; // Ana In - Pot or Switch 5

Code:
//Raw AI Input Signal 0 to 3.3 volts
int AI_Raw_AEL; // Ana In raw var - 0->1023
int AI_Raw_ELE; // Ana In raw var - 0->1023
int AI_Raw_THR; // Ana In raw var - 0->1023
int AI_Raw_RUD; // Ana In raw var - 0->1023
int AI_Raw_GEAR; // Ana In raw var - 0->1023
int AI_Raw_AUX; // Ana In raw var - 0->1023

//AI_Raw Analog variable Input mapped and cleaned to go into WRITE COMMAND
int AI_AEL; // Ana In var - 0->1023 compensated
int AI_ELE; // Ana In var - 0->1023 compensated
int AI_THR; // Ana In var - 0->1023 compensated
int AI_RUD; // Ana In var - 0->1023 compensated
int AI_GEAR; // Ana In var - 0->1023 compensated
int AI_AUX; // Ana In var - 0->1023 compensated

void setup() {
  PPMOut.begin(10);  // Start 6-Channel PPM OutPut
 PPMOut.begin(10, 8);//
 
  PPMOut.write(1, AI_AEL);
  PPMOut.write(2, AI_ELE);
  PPMOut.write(3, AI_THR);
  PPMOut.write(4, AI_RUD);
  PPMOut.write(5, AI_GEAR);
  PPMOut.write(6, AI-AUX);
  }

void loop() {

 AI_Raw_AEL = analogRead(AI_Pin_AEL);
 AI_Raw_ELE = analogRead(AI_Pin_ELE);
 AI_Raw_THR = analogRead(AI_Pin_THR);
 AI_Raw_RUD = analogRead(AI_Pin_RUD);
 AI_Raw_GEAR = analogRead(AI_Pin_GEAR);
 AI_Raw_AUX = analogRead(AI_Pin_AUX);

  AI_AEL = map(AI_Raw_AEL, 0, 8191, 0, 1000) - 0; // Invert Aeleron pot and slight centre offset
  AI_ELE = map(AI_Raw_ELE, 0, 8191, 0, 1000) - 0; // Invert Elevator pot and slight centre offset
  AI_THR = map(AI_Raw_THR, 0, 8191, 0, 1000) + 0; // Throttle
  AI_RUD = map(AI_Raw_RUD, 0, 8192, 0, 1000) + 0; // Rudder
  AI_GEAR = map(AI_Raw_GEAR, 0, 8191, 1000, 0) + 0; // Gear channel (TI)
  AI_AUX = map(AI_Raw_AUX, 0, 8191, 1000, 0) + 0; // Aux channel (TI)

}

The code compile and loads. However it does not work. For whatever reason the WRITE commands are not working. All I get is a single pulse of 100 uS. I am pretty sure it is the WRITE command because if edit this line:

PPMOut.write(6, AI-AUX);

to

PPMOut.write(6, 2000);

It works half arse, channels 1 to 5 default to 1500 uS, and channel 6 is whatever number I put in it from 1000 to 2000 uS. It has me scratching my head. In these Newb eyes the code looks good as I did generate the variables ad mapped them to micro seconds. At least I thought I did.

Can anyone tell me what is wrong?
 
Last edited:
In setup you do a set of writs with variables that have not been set,so may default to zero. Then in loop you calculate values, but never write out new values.
 
In setup you do a set of writs with variables that have not been set,so may default to zero. Then in loop you calculate values, but never write out new values.

Thanks Kurt. The reason I put the WRITE command in Setup is because that is where the Pulse Position Library puts Write in the Loopback example. Here is the original code in the Loopback example:

Code:
#include <PulsePosition.h>

// Simple loopback test: create 1 output to transmit
// test pulses, and 1 input to receive the pulses
PulsePositionOutput myOut;
PulsePositionInput myIn;

void setup() {
  myOut.begin(9);  // connect pins 9 and 10 together...
  myIn.begin(10);
  myOut.write(1, 600.03);
  myOut.write(2, 1500);
  myOut.write(3, 759.24);
  // slots 4 and 5 will default to 1500 us
  myOut.write(6, 1234.56);
}

int count=0;

void loop() {
  int i, num;

  // Every time new data arrives, simply print it
  // to the Arduino Serial Monitor.
  num = myIn.available();
  if (num > 0) {
    count = count + 1;
    Serial.print(count);
    Serial.print(" :  ");
    for (i=1; i <= num; i++) {
      float val = myIn.read(i);
      Serial.print(val);
      Serial.print("  ");
    }
    Serial.println();
  }
}

I then edit out all RX PPM and Printer stuff and ended up with this as my basic code:

Code:
#include <PulsePosition.h>
PulsePositionOutput myOut;


void setup() {
  myOut.begin(9);  // connect pins 9 and 10 together...
   myOut.write(1, 2000);
  myOut.write(2, 2000);
  myOut.write(3, 2000);
  // slots 4 and 5 will default to 1500 us
  myOut.write(6, 2000);
}

void loop() {
  
}

That code worked great and there is nothing in the LOOP. I ran it all night hooked up to a Scope. Did exactly what I expected 6 pretty little 2000 mS pulses in 20,000 uS frame. So my thinking was replace the the numbers (2000) with a Variable.

Anyway I did as you suggested and appears to work but I do not understand why exactly. Here is where the code is now:

Code:
#include <PulsePosition.h>

PulsePositionOutput PPMOut(FALLING); //PPM signal out

//Declar and Assign Analog Input Pins
int AI_Pin_AEL = 6; // Ana In - Aeleron potentiometer (Ana In Ch.0 playing up?)6
int AI_Pin_ELE = 1; // Ana In - Elevator potentiometer 1
int AI_Pin_THR = 2; // Ana In - Throttle potentiometer 2
int AI_Pin_RUD = 3; // Ana In - Rudder potentiometer 3
int AI_Pin_GEAR = 4; // Ana In - Pot or Switch 4
int AI_Pin_AUX = 5; // Ana In - Pot or Switch 5

//Raw AI Signal 0 to 3.3 volts
int AI_Raw_AEL = 0; // Ana In raw var - 0->1023
int AI_Raw_ELE = 0; // Ana In raw var - 0->1023
int AI_Raw_THR = 0; // Ana In raw var - 0->1023
int AI_Raw_RUD = 0; // Ana In raw var - 0->1023
int AI_Raw_GEAR = 0; // Ana In raw var - 0->1023
int AI_Raw_AUX = 0; // Ana In raw var - 0->1023

//AI_Raw date mapped and compensated in microseconds
int AI_AEL; // Ana In var - 0->1023 compensated
int AI_ELE; // Ana In var - 0->1023 compensated
int AI_THR; // Ana In var - 0->1023 compensated
int AI_RUD; // Ana In var - 0->1023 compensated
int AI_GEAR; // Ana In var - 0->1023 compensated
int AI_AUX; // Ana In var - 0->1023 compensated

void setup() {
  PPMOut.begin(10);  // Start 6-Channel PPM OutPut
 PPMOut.begin(10, 8);//

  }

void loop() {
 AI_Raw_AEL = analogRead(AI_Pin_AEL);
 AI_Raw_ELE = analogRead(AI_Pin_ELE);
 AI_Raw_THR = analogRead(AI_Pin_THR);
 AI_Raw_RUD = analogRead(AI_Pin_RUD);
 AI_Raw_GEAR = analogRead(AI_Pin_GEAR);
 AI_Raw_AUX = analogRead(AI_Pin_AUX);

  AI_AEL = map(AI_Raw_AEL, 0, 1023, 1000, 2000) - 0; // Invert Aeleron pot and slight centre offset
  AI_ELE = map(AI_Raw_ELE, 0, 1023, 1000, 2000) - 0; // Invert Elevator pot and slight centre offset
  AI_THR = map(AI_Raw_THR, 0, 1023, 1000, 2000) + 0; // Throttle
  AI_RUD = map(AI_Raw_RUD, 0, 1023, 1000, 2000) + 0; // Rudder
  AI_GEAR = map(AI_Raw_GEAR, 0, 1023, 1000, 2000) + 0; // Thermal Intelligence pot (TI)
  AI_AUX = map(AI_Raw_AUX, 0, 1023, 1000, 2000) + 0; // Thermal Intelligence pot (TI)

  PPMOut.write(1, AI_AEL);
  PPMOut.write(2, AI_ELE);
  PPMOut.write(3, AI_THR);
  PPMOut.write(4, AI_RUD);
  PPMOut.write(5, AI_GEAR);
  PPMOut.write(6, AI_AUX);
}
 
Thanks Kurt. The reason I put the WRITE command in Setup is because that is where the Pulse Position Library puts Write in the Loopback example. Here is the original code in the Loopback example:

That code worked great and there is nothing in the LOOP. I ran it all night hooked up to a Scope. Did exactly what I expected 6 pretty little 2000 mS pulses in 20,000 uS frame. So my thinking was replace the the numbers (2000) with a Variable.

Anyway I did as you suggested and appears to work but I do not understand why exactly.

The PPM write(channel,value); basically starts a timer-controlled pulse stream that runs continuously "in the background", so if you do write's in setup() and nothing in loop() it will just continue emitting the PPM stream with those initial values. Reading your ADC values and writing new data into the PPM stream is what you need to be doing (and are now doing) in loop().
 
The PPM write(channel,value); basically starts a timer-controlled pulse stream that runs continuously "in the background", so if you do write's in setup() and nothing in loop() it will just continue emitting the PPM stream with those initial values. Reading your ADC values and writing new data into the PPM stream is what you need to be doing (and are now doing) in loop().

Thank you and I understand what you are saying. I guess what is throwing me besides being a Newb is I do not understand how Include Libraries work. The Loopback Example placed the Write into SETUP, so that is where I left it. Correct me if I am wrong but it appears the INCLUDE statements makes up some codes not listed. Maybe I have missed it but there is no Writel(channel, value); command I have seen. I am missing something how those invisible INCLUDE Libraries, get called up. Example PulsePositionOutput PPMOut(FALLING); There

One that really has me scratching my head is the opening int statements:

Code:
int AI_Pin_AEL = [B]6[/B]; // Ana In - Aeleron potentiometer (Ana In Ch.0 playing up?)
int AI_Pin_ELE = [B]1[/B]; // Ana In - Elevator potentiometer 
int AI_Pin_THR = [B]2[/B]; // Ana In - Throttle potentiometer
int AI_Pin_RUD = [B][/B]3; // Ana In - Rudder potentiometer 
int AI_Pin_GEAR = [B]4[/B]; // Ana In - Pot or Switch 
int AI_Pin_AUX = [B]5[/B]; // Ana In - Pot or Switch

Now if I look up the "int" statement:

int AI_Pin_AEL = 6;

As I understand it I am declaring a varible named "int AI_Pin_Ael" and setting the value to 6. However that is not what it is really doing. It does declare a variable called "AI-Pin_Ael", but makes it Analog Input pin A5. That just confuses the crap out of me. Pin A5 on the Teensy 3.2 is on pin 19, not pin 6.

My pea brain says the command should be int AI_Pin_Ael = A5;, and then declare it as an Analog InPut pin. Something is going on I do not understand.

WTF Over?
 
Last edited:
These are all valid questions, but to really reach some sort of useful understanding, you also *must* do some experimenting. No matter how throughly everyone explains it here in writing on this forum, useful understanding only comes from actually using this stuff and seeing the results.
 
Now if I look up the "int" statement:

int AI_Pin_AEL = 6;

As I understand it I am declaring a varible named "int AI_Pin_Ael" and setting the value to 6. However that is not what it is really doing. It does declare a variable called "AI-Pin_Ael", but makes it Analog Input pin A5. That just confuses the crap out of me. Pin A5 on the Teensy 3.2 is on pin 19, not pin 6.
Actually this statement is doing exactly what it says, create a variable which in the case of Teensy is 4 bytes and is signed and store the initial value 6 to it.

You then use it in the line:
Code:
 AI_Raw_AEL = analogRead(AI_Pin_AEL);
So you are calling the function analogRead, which takes one parameter, to which you pass in AI_Pin_AEL which has the value 6 in it so you are reading the analog value of pin 6 and then the return value from that call is put ito the variable AI_Raw_AEL.

Note, as you are passing a variable to the analogRead function, it will evaluate the contents of the variable AI_Pin_AEL at the time you do the call. So if in this case if AI_Pin_AEL never changes, I usually let the system know it...
Sometimes by instead using #define like
Code:
#define AI_Pin_AEL 6
So this generates the exact same code like: analogRead(6)
Another way that may work the same with the compiler is to mark the variable as const. like:
Code:
const int AI_Pin_AEL = 6;

Note: I have not experimented enough with using the const version to know if it will generate the same code. Sometimes more important in calls like digitalWrite() as some of this code is setup to know that it is being called with literal values and generates very specific code in this case...

Again I second what Paul said and that is try different things and see what it does.... One of my first teachers (or was it magaizine like Dr Dobbs) once said, that programming is not a spectator sport!
 
Actually this statement is doing exactly what it says, create a variable which in the case of Teensy is 4 bytes and is signed and store the initial value 6 to it.

You then use it in the line:
Code:
 AI_Raw_AEL = analogRead(AI_Pin_AEL);
So you are calling the function analogRead, which takes one parameter, to which you pass in AI_Pin_AEL which has the value 6 in it so you are reading the analog value of pin 6 and then the return value from that call is put ito the variable AI_Raw_AEL.

Thanks Kurt, but this is where I am getting lost. I understand it makes a Variable called AI_Pin_AEL, so I am good up to there. Where I part ways is I am thinking it is assigning Pin 6 as my Analog Input Pin, and calling it AI_Pin_AEL. The value comes from a 5K Pot and is 0 to 3.3 volts analog, not a fixed value of 6. It gets converted to 0 to 1023 in the map.

Or at least that is what I think it is suppose to do. Something tells me it needs to be written as int AI_Pin_AEL = A5; or pin 19???
 
Last edited:
First whenever unsure what is going on, try google or look in Arduino help. Example AnalogRead: https://www.arduino.cc/en/Reference/AnalogRead

Remember AnalogRead is just a plain old function that takes one parameter. Imagine the code:

Code:
// Define your variable
int AI_Pin_AEL = 6;

// define simple function
int MyFunction(int myPin) {
    return myPin / 2;
}

void loop() {
...
    AI_RAW_AEL  = MyFunction(AI_Pin_AEL);
...
}

In this simple case when you call MyFunction, it sees your variable, and inside MyFunction it assigns the value in this case 6 to the variable myPin and then it returns the value myPin/2, so in this case it returns 3, which is assigned to the variable AI_RAW_AEL

So in your real case:
Code:
AI_Raw_AEL = analogRead(AI_Pin_AEL);
It really is no different, just the function being called has a lot more complexity in the internals. Example:
Code:
int analogRead(uint8_t pin)
{
	int result;
	uint8_t channel;

	//serial_phex(pin);
	//serial_print(" ");

	if (pin >= sizeof(pin2sc1a)) return 0;
	channel = pin2sc1a[pin];
	if (channel == 255) return 0;

	if (calibrating) wait_for_cal();

#ifdef HAS_KINETIS_ADC1
	if (channel & 0x80) goto beginADC1;
#endif

	__disable_irq();
startADC0:
	//serial_print("startADC0\n");
#if defined(__MKL26Z64__)
	if (channel & 0x40) {
		ADC0_CFG2 &= ~ADC_CFG2_MUXSEL;
		channel &= 0x3F;
	} else {
		ADC0_CFG2 |= ADC_CFG2_MUXSEL;
	}
#endif
	ADC0_SC1A = channel;
	analogReadBusyADC0 = 1;
	__enable_irq();
	while (1) {
		__disable_irq();
		if ((ADC0_SC1A & ADC_SC1_COCO)) {
			result = ADC0_RA;
			analogReadBusyADC0 = 0;
			__enable_irq();
			result >>= analog_right_shift;
			return result;
		}
		// detect if analogRead was used from an interrupt
		// if so, our analogRead got canceled, so it must
		// be restarted.
		if (!analogReadBusyADC0) goto startADC0;
		__enable_irq();
		yield();
	}

#ifdef HAS_KINETIS_ADC1
beginADC1:
	__disable_irq();
startADC1:
	//serial_print("startADC1\n");
	// ADC1_CFG2[MUXSEL] bit selects between ADCx_SEn channels a and b.
	if (channel & 0x40) {
		ADC1_CFG2 &= ~ADC_CFG2_MUXSEL;
	} else {
		ADC1_CFG2 |= ADC_CFG2_MUXSEL;
	}
	ADC1_SC1A = channel & 0x3F;
	analogReadBusyADC1 = 1;
	__enable_irq();
	while (1) {
		__disable_irq();
		if ((ADC1_SC1A & ADC_SC1_COCO)) {
			result = ADC1_RA;
			analogReadBusyADC1 = 0;
			__enable_irq();
			result >>= analog_right_shift;
			return result;
		}
		// detect if analogRead was used from an interrupt
		// if so, our analogRead got canceled, so it must
		// be restarted.
		if (!analogReadBusyADC1) goto startADC1;
		__enable_irq();
		yield();
	}
#endif
}
Again you don't need to understand what all is going on internally here. You simply have to understand that you are calling analogRead with a pin number. And assuming the pin number is a valid pin number the code will do the right thing to get the analog value back from that pin. If you are ever interested in exactly what is going on the sources for most everything is installed when you install Teensyduino (and Arduino as well). So for example to see the code for the above on my machine for this install is located at:
C:\arduino-1.6.12\hardware\teensy\avr\cores\teensy3\analog.c

And again if you are having difficulties understanding things like what is a function or subroutine or... Google and Help is your friend: https://www.arduino.cc/en/Reference/FunctionDeclaration

Hope that helps
 
Thanks again Kurt, but I figured out most of it by going back and reading. Everything is working now, and tested with POTS connected to each channel, and verifying on Oscope and serial print. Here is where I landed.

Code:
#include <PulsePosition.h>

PulsePositionOutput PPMOut(FALLING); //PPM signal out

//Set and Name Analog Input Pins A0 - A5 for each channel
const int AI_Pin_AEL = A0; // Ana In - Aeleron potentiometer 
const int AI_Pin_ELE = A1; // Ana In - Elevator potentiometer 
const int AI_Pin_THR = A2; // Ana In - Throttle potentiometer
const int AI_Pin_RUD = A3; // Ana In - Rudder potentiometer
const int AI_Pin_GEAR = A4; // Ana In - Pot or Switch 
const int AI_Pin_AUX = A5; // Ana In - Pot or Switch  

//Set Analog Input Pin Values
int AI_Raw_AEL = 0; // Ana In raw var - 0->1023
int AI_Raw_ELE = 0; // Ana In raw var - 0->1023
int AI_Raw_THR = 0; // Ana In raw var - 0->1023
int AI_Raw_RUD = 0; // Ana In raw var - 0->1023
int AI_Raw_GEAR = 0; // Ana In raw var - 0->1023
int AI_Raw_AUX = 0; // Ana In raw var - 0->1023

//AI_Analog Output Values to each channel via Write command
int AI_AEL; // Ana In var - 0->1023 compensated
int AI_ELE; // Ana In var - 0->1023 compensated
int AI_THR; // Ana In var - 0->1023 compensated
int AI_RUD; // Ana In var - 0->1023 compensated
int AI_GEAR; // Ana In var - 0->1023 compensated
int AI_AUX; // Ana In var - 0->1023 compensated

void setup() {
  PPMOut.begin(10);  // Starts PPM Output on Pin 10 to send to Trainer Port
 PPMOut.begin(10, 8);// Starts Frame Pin
 //Serial.begin(9600);// Serial test

  }

void loop() {

  //Read Analog Input Voltages
 AI_Raw_AEL = analogRead(AI_Pin_AEL);
 AI_Raw_ELE = analogRead(AI_Pin_ELE);
 AI_Raw_THR = analogRead(AI_Pin_THR);
 AI_Raw_RUD = analogRead(AI_Pin_RUD);
 AI_Raw_GEAR = analogRead(AI_Pin_GEAR);
 AI_Raw_AUX = analogRead(AI_Pin_AUX);

 
// Converts Analog Input Voltage to microseconds for Write Command
  AI_AEL = map(AI_Raw_AEL, 0, 1023, 1000, 2023); // Aileron 
  AI_ELE = map(AI_Raw_ELE, 0, 1023, 1000, 2023); // Elevator
  AI_THR = map(AI_Raw_THR, 0, 1023, 1000, 2023); // Throttle
  AI_RUD = map(AI_Raw_RUD, 0, 1023, 1000, 2023); // Rudder
  AI_GEAR = map(AI_Raw_GEAR, 0, 1023, 1000, 2023); // Pot or Switch
  AI_AUX = map(AI_Raw_AUX, 0, 1023, 1000, 2023); // Pot or Switch

  // Output PPM channels 1 - 6 on pin 10
PPMOut.write(1, AI_AEL);
  PPMOut.write(2, AI_ELE);
  PPMOut.write(3, AI_THR);
  PPMOut.write(4, AI_RUD);
  PPMOut.write(5, AI_GEAR);
  PPMOut.write(6, AI_AUX);


// Serial test.
 // Serial.print("AI_AEL = ");
  //Serial.print(AI_AEL);
  //delay (2);
}

Basically just changed the first 6 "int statements". Example I changed "int AI_Pin_AEL = 6;" to "const int AI_Pin_AEL = A0;". Where I was thrown off track is the code I used was from a similar project code I copied. instead of setting the pin to say A0 or A1, somehow transposed over to 1 to 6 which made no sense to me because pins 1 to 6 are Digital I/O pins . Now that I changed that, almost everything is working like I expect.

I have problem in the MAP commands not working like I expected. example AI_AEL = map(AI_Raw_AEL, 0, 2047, 1000, 2000); does not produce an output of 1000 to 2000. It produced and output of 1000 to 1500. Then I tried AI_AEL = map(AI_Raw_AEL, 0, 4095, 1000, 2000); That does not work either and produces an output of 1000 to 1250. Doh!

Where I landed from testing was "AI_AEL = map(AI_Raw_AEL, 0, 1023, 1000, 2023);" That produces an output of 1000 to 2023. Not exactly what I want as I want an output of 1000 to 2000. I did try AI_AEL = map(AI_Raw_AEL, 0, 1023, 1000, 2000); but still get the same output 1000 to 2023. Why I do not know. I can live with the error if I have to, but the Scaling does not work like I expect. Can anyone explain where I am going wrong with scaling and mapping.
 
Last edited:
AI_AEL = map(AI_Raw_AEL, 0, 1023, 1000, 2000);
should work. The analogRead() function returns values from 0 to 1023, that's why you use those as arguments 2 and 3.
See https://www.arduino.cc/en/Reference/Map, what it does:
Code:
long map(long x, long in_min, long in_max, long out_min, long out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

you probably do not need PPMOut.begin(10, 8);// Starts Frame Pin
unless you are using 74HCT164 chip.
 
Last edited:
AI_AEL = map(AI_Raw_AEL, 0, 1023, 1000, 2000);
should work. The analogRead() function returns values from 0 to 1023, that's why you use those as arguments 2 and 3.
See https://www.arduino.cc/en/Reference/Map, what it does:

It does work sort of with one caveat, AI_AEL = map(AI_Raw_AEL, 0, 1023, 1000, 2000); returns 1000 to 2023. Beats me why. By default I am pretty sure the Teensy 3.2 analog resolution is 1023 or 10 bits. I have been told it can be 10, 11, 12, or 13 bits. Question is how do you make use 11, 12, or 13 bits?

Reason I asked is I tried:
AI_AEL = map(AI_Raw_AEL, 0, 2047, 1000, 2000);
AI_AEL = map(AI_Raw_AEL, 0, 4096, 1000, 2000);
AI_AEL = map(AI_Raw_AEL, 0, 8191, 1000, 2000);

Does not work. Example AI_AEL = map(AI_Raw_AEL, 0, 2047, 1000, 2000); returns 1000 to 1500, and AI_AEL = map(AI_Raw_AEL, 0, 8191, 1000, 2000); returns 1000 to 1200. Then it occurred to me most likely the MAP command does not set the ADC resolution. That has to be done elsewhere unknown to me.

you probably do not need PPMOut.begin(10, 8);// Starts Frame Pin unless you are using 74HCT164 chip.

Yes I am aware of that, thank you. I left it in there for now to use it as a Sync Trigger signal for an Oscope. Once I get the code where I want it I will disable the code with // Same for the Serial Print stuff as I use that when I tweak and play with the map commands to see what numbers it generates.
 
Last edited:
to set resolution analogReadResolution(bits)
again, see https://www.arduino.cc/en/Reference/HomePage

and in the IDE, play with some of the examples, File > Examples > Analog ....

i'm pretty sure you wont get 1023 from AI_AEL = map(AI_Raw_AEL, 0, 1023, 1000, 2000); if AI_Raw_AEL is from 0 to 1023
 
Last edited:
Also note, there is no magic in the map function. It is simply a formula. The actual code looks like:
Code:
long map(long x, long in_min, long in_max, long out_min, long out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
So if the value you are receiving out, is not in the range you are expecting, then the input value is not in the range you passed in.
 
Also note, there is no magic in the map function. It is simply a formula. The actual code looks like:
Code:
long map(long x, long in_min, long in_max, long out_min, long out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
So if the value you are receiving out, is not in the range you are expecting, then the input value is not in the range you passed in.

You are right, I ran the math and have tried several things now. 11 bit ADC appears to be working as does 12 and 13 bits. I can make it work from 10 to 13 bits. So now my next question is what are the Pros and Cons of using more bits?

I understand using more bits has better resolution which can be desirable if needed. But what do I have to give up and trade for? What is the down side to using higher resolution? If I had to guess analog read time will be longer. For Radio Control PPM 1 uS resolution is very good because RC PPM Pulse is 1 mS wide from low (1 mS) to high (2 ms).
 
I guess, with potentiometers, you'll notice no difference.

I bet, your fingers aren't exact enough to turn the pot in a way that you can use 13 Bits :rolleyes: 13 Bits are 8192 different positions on your potentiometer .
Then, you will hardly notice the second difference, the speed. It will be a little slower with more bits, but.. again... there are still several thousand measures per second.
 
OK I have everything working like I want, so thanks for the help, I appreciate it.

One last thing I want to do is Smooth the 4 of the 6 analog inputs with what I think is called an Array by running the average of 5 to 10 counts on each analog input. I have watched few videos on Smoothing and Arrays and understand the concept. Bu tI have no idea how to set it up and insert into my code.

I think I first need to initialize the array something like:

int PotAray = [5] (A0, A1. A2, A3);

Not sure that is right and no clue where to go from there. Here is where the code is today. The inputs I want to Smooth or Average are in RED

Code:
#include <PulsePosition.h>

PulsePositionOutput PPMOut(FALLING); //PPM signal out

//Set and Name Analog Input Pins A0 - A5 for each channel
[COLOR="#FF0000"][B]const int AI_Pin_AEL = A0; // Ana In - Aeleron potentiometer 
const int AI_Pin_ELE = A1; // Ana In - Elevator potentiometer 
const int AI_Pin_THR = A2; // Ana In - Throttle potentiometer
const int AI_Pin_RUD = A3; // Ana In - Rudder potentiometer[/B][/COLOR]
const int AI_Pin_GEAR = A4; // Ana In - Pot or Switch 
const int AI_Pin_AUX = A5; // Ana In - Pot or Switch  

//Set Analog Input Pin Values
int AI_Raw_AEL = 0; // Ana In raw var - 0->1023
int AI_Raw_ELE = 0; // Ana In raw var - 0->1023
int AI_Raw_THR = 0; // Ana In raw var - 0->1023
int AI_Raw_RUD = 0; // Ana In raw var - 0->1023
int AI_Raw_GEAR = 0; // Ana In raw var - 0->1023
int AI_Raw_AUX = 0; // Ana In raw var - 0->1023

//AI_Analog Output Values to each channel via Write command
int AI_AEL = 0; // Ana In var - 0->1023 compensated
int AI_ELE; // Ana In var - 0->1023 compensated
int AI_THR; // Ana In var - 0->1023 compensated
int AI_RUD; // Ana In var - 0->1023 compensated
int AI_GEAR; // Ana In var - 0->1023 compensated
int AI_AUX; // Ana In var - 0->1023 compensated

void setup() {
  PPMOut.begin(10);  // Starts PPM Output on Pin 10 to send to Trainer Port
 PPMOut.begin(10, 8);// Starts Frame Pin
Serial.begin(9600);

  }

void loop() {

  analogReadResolution(11);

  //Read Analog Input Voltages
 AI_Raw_AEL = analogRead(AI_Pin_AEL);
 AI_Raw_ELE = analogRead(AI_Pin_ELE);
 AI_Raw_THR = analogRead(AI_Pin_THR);
 AI_Raw_RUD = analogRead(AI_Pin_RUD);
 AI_Raw_GEAR = analogRead(AI_Pin_GEAR);
 AI_Raw_AUX = analogRead(AI_Pin_AUX);

 
// Convert Analog Input Voltage to microseconds for Write Command
  AI_AEL = map(AI_Raw_AEL, 0, 2047, 800, 2200); // Invert Aeleron pot and slight centre offset
  AI_ELE = map(AI_Raw_ELE, 0, 2047, 800, 2200); // Invert Elevator pot and slight centre offset
  AI_THR = map(AI_Raw_THR, 0, 2047, 800, 2200); // Throttle
  AI_RUD = map(AI_Raw_RUD, 0, 2047, 800, 2200); // Rudder
  AI_GEAR = map(AI_Raw_GEAR, 0, 2047, 800, 2200); // Thermal Intelligence pot (TI)
  AI_AUX = map(AI_Raw_AUX, 0, 2047, 800, 2200); // Thermal Intelligence pot (TI)

  PPMOut.write(1, AI_AEL);
  PPMOut.write(2, AI_ELE);
  PPMOut.write(3, AI_THR);
  PPMOut.write(4, AI_RUD);
  PPMOut.write(5, AI_GEAR);
  PPMOut.write(6, AI_AUX);

Serial.print("AI_AEL = ");
Serial.print(AI_AEL);
delay (2);
}
 
the teensy 3 ADC can do averaging in the hardware (4, 8, 16 ,32),in setup() use
analogReadAveraging(8); // average 8 readings
 
I think I first need to initialize the array something like:

int PotAray = [5] (A0, A1. A2, A3);

In all programming, there's a natural human tendency to make things more complicated than necessary. Usually the thinking involves overestimating the benefit of making things more flexible (than they need to be) and underestimating the long-term cost and difficulty of more complex code. Many experts take this to extremes.

Especially as a beginner, I highly recommend you keep things simple. If replacing 5 (simpler) copies of code with 1 (more complex) piece of code is the only benefit, you should really reconsider the merit. Simpler code is usually much better. Five copies might not look as pretty and may require more time to edit details in 5 places, but that time is probably much less than the effort to make more complex code work properly.

Of course, if you want to experiment for the sake of learning, go for it. But even then, best to get things working first the simplest way.
 
the teensy 3 ADC can do averaging in the hardware (4, 8, 16 ,32),in setup() use
analogReadAveraging(8); // average 8 readings

LOL. Well butter my butt and call it a biscuit that was easy. Now this brings up a generic question. Where is a list of commands that Teensy does and Arduino does not?

Thank you very much, added it and works great. Very stable signal data coming in.
 
In all programming, there's a natural human tendency to make things more complicated than necessary. Usually the thinking involves overestimating the benefit of making things more flexible (than they need to be) and underestimating the long-term cost and difficulty of more complex code. Many experts take this to extremes.

Well I have been an EE for over 30 years and we call that KISS design, and is what I practice.

Especially as a beginner, I highly recommend you keep things simple. If replacing 5 (simpler) copies of code with 1 (more complex) piece of code is the only benefit, you should really reconsider the merit. Simpler code is usually much better. Five copies might not look as pretty and may require more time to edit details in 5 places, but that time is probably much less than the effort to make more complex code work properly.

Of course, if you want to experiment for the sake of learning, go for it. But even then, best to get things working first the simplest way.

Simple was the goal from the start. I took a more complicated code made for Arduino Nano and converted it to Teensy 3.2. When I started I was aiming at a different approach from the same person with a much more complex code and hardware. I wanted to use any PC USB Joystick, plug it in, and converts HID Joystick to RC PPM. That takes either a Teensy, Nano, Uno, or Mega plus a Host Shield Board. The code for that uses 7 libraries and very complex code I could not even begin to follow.

As for the Averaging, it is an absolute Must Have for RC PPM flying a planes. The Encoder is not sensitive to Frame and Channel jitter, encoder could care less. But the TX Radio Trainer Port and especially the RX in the Plane is extremely sensitive to Channel and Frame Jitter. Frame rates is 40 to 50 Hz, and with 6 Analog Inputs sampling at I have no clue how many times a seconds other than it is more than enough to generate a number 50 times a second x 6 channels. Off the top of my head running at 96 Mhz Clock I would say at least a 1000 times for each Frame. Way more than fast enough for the application. I turned it up to 32 to experiment and did not record any delay.

So for me yeah adding one lin eof code to get the job done is as easy as buttering your butt and calling it a biscuit. It is my first go at it, I learned a lot fast. The code is simpler than anything else I have seen that does the same thing. FWIW Pul it is you who convinced me to do this with the PulsePosition Library. You stated it would be much easier and you were right in the end. The Code I wrote looks like a Family Tree of someone from Arkansas, a straight line. East to follow as a recipe to make P&B sandwiches.
 
Thank you everyone

I want to say think you to everyone that helped me out, especially:

KurtE
PaulStoffregen
Manitou

Yeah I might be a PIA but you guys taught me a lot and I appreciate it. Well done. Manitou does your username have anything to do with the Town in CO? I go to COS every July 4th. Last two years in Maitou for the fireworks. Love that place.
 
Status
Not open for further replies.
Back
Top