Go Down

Topic: Arduino to flight controller communication issue. (Read 1 time) previous topic - next topic

saikatd005

Things I am using Transmitter----->Receiver----->Arduino----->APM 2.8(Flight controller)---->Escs(Motor)

The problem is, the signal values that I am sending via Arduino to flight controller is not stable its fluctuating a lot. (while I am keeping the stick of the transmitter stable)

These values can be seen in mission planner.

Mission Planner is a full-featured ground station application for the ArduPilot APM 2.8(Flight controller I am using).http://ardupilot.org/planner/

Things I am using Transmitter----->Receiver----->Arduino----->APM 2.8(Flight controller)---->Escs(Motor)

When I was using without the arduino the signal value were stable.

How to solve this issue.

Any help will be appreciated..

here is the code I am using
Code: [Select]
#include <Servo.h>
#define LED 13

int X,Y,P,Q;

Servo AIL;
Servo ELE;
Servo THR;
Servo RUD;
Servo AUX;

int recAIL;
int recELE;
int recTHR;
int recRUD;
int recAUX;

void setup() {
  Serial.begin(9600);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);

  pinMode(3, INPUT);
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  pinMode(6, INPUT);
  pinMode(7, INPUT);
 
  AIL.attach(9);
  ELE.attach(10);
  THR.attach(11);
  RUD.attach(12);
  delay(500);
}

void loop() {


  recAIL = pulseIn(3, HIGH, 200000);
  recELE = pulseIn(4, HIGH, 200000);
  recTHR = pulseIn(5, HIGH, 200000);
  recRUD = pulseIn(6, HIGH, 200000);
  recAUX = pulseIn(7, HIGH, 200000);

  AIL.writeMicroseconds(recAIL);
  ELE.writeMicroseconds(recELE);
  THR.writeMicroseconds(recTHR);
  RUD.writeMicroseconds(recRUD);

 
  Serial.print("AIL: ");
  Serial.print(recAIL);
  Serial.print('\t');
  Serial.print("ELE: ");
  Serial.print(recELE);
  Serial.print('\t');
  Serial.print("THR: ");
  Serial.print(recTHR);
  Serial.print('\t');
  Serial.print("RUD: ");
  Serial.print(recRUD);
  Serial.print('\t');
  Serial.print("AUX: ");
  Serial.print(recAUX);
  Serial.print('\n');
}

bms001

I would start with...

Output the measured pulse lengths to the serial monitor rather than viewing in mission planner.
Are they still fluctuating 'alot'?

If yes then try commenting out all the code for every channel except one.
Are the values still fluctuating?

saikatd005

If yes then try commenting out all the code for every channel except one.
Are the values still fluctuating?
The values  are still fluctuating..

bms001



If you're going to be doing much else on the Arduino aside from reading and writing the pulses, you may want to change from using pulseIn() to using interupts to measure the length, perhaps using the PinChangeInterrupt approach.


But this wouldn't fix the issue that you have described.

Can you please post the code you used to test the single channel, and show an example of the output. Also provide an example of what 'stable' values are shown in mission planner when the Arduino is not used.

MartinL

#4
Mar 07, 2018, 09:50 am Last Edit: Mar 07, 2018, 09:59 am by MartinL
Hi saikatd005,

The process of passing through the receiver signals is more difficult than it first appears.

The pulses coming out of the RC receiver channels, throttle, airelon, elevator, etc... are accurate to around 1us (microsecond).

On the output side the servo library isn't accurate enough, neither is analogWrite(). The only way to get the required PWM resolution is to program the microcontroller's 16-bit timer registers directly.

Unfortunately, the Uno/Nano only has two 16-bit timer outputs, the Leonardo/Micro fares a little better with four, the Mega twelve, while ARM based Zero and Due have eight plus additional auxiliary timers capable of PWM.

On the input side, as bms001 suggests, you'll need to use interrupts rather than PulseIn(), as this function blocks. A good starting point is here: http://rcarduino.blogspot.co.uk/2012/01/how-to-read-rc-receiver-with.html.

The other consideration if you're using AVR based Arduinos, is that the micros() function is only accurate to 4us. (ARM based Arduinos are accurate to 1us). However, it's possible to use Timer2 on the Uno/Nano/Mega, (Timer4 on the Leonardo/Micro) to generate a micros() function accurate to 1us.

Your aircraft will be controllable with a 4us accuracy, but 1us will give you a finer degree of control. I can provide you with the code for the more accurate micros() function if you require it?

bms001

On the output side the servo library isn't accurate enough...
Are you sure about this? The standard servo library uses a prescaler of 8 on the Atmega328P Timer1 which gives a resolution of 0.5us. If the overhead on the interupt on the start/end of the pulse is different then maybe you won't get quite this resolution but should be close.
The only possible 'issue' with the servo library that I am aware of is that your refresh rate is limited. Default is 50Hz but can be increased depending on how many pulses you need to fit in to each refresh window. Although for the purposes described here i.e. to simulate a receiver, I imagine it will be fine.


A good starting point is here: http://rcarduino.blogspot.co.uk/2012/01/how-to-read-rc-receiver-with.html.
Yes! I was looking for this when I replied before as I have found it really useful myself.

saikatd005

I will try using Arduino Due and  it would be great if you provide the code.

Thanks a lot MartinL.

MartinL

#7
Mar 07, 2018, 10:46 am Last Edit: Mar 07, 2018, 10:59 am by MartinL
Hi saikad005,

Ok, using the Arduino Due the micros() function is already accurate to 1us.

On the input side, to use the interrupts, just call the attachInterrupts() function for each channel:

Code: [Select]
attachInterrupt(THROTTLE_PIN, calcThrottle, CHANGE);
...and then for the callback function...

Code: [Select]
void calcThrottle()
{
  static unsigned long throttleStartTime;
 
  if (REG_PIOB_PDSR & PIO_ODSR_P21)  // if (digitalRead(THROTTLE_PIN) == HIGH)
  {
    throttleStartTime = micros();
  }
  else
  {       
    isrPulsewidth[THROTTLE] = micros() - throttleStartTime;   
  }
}

Note that isrPulsewidth is declared as a global variable at the beginning of your sketch:

Code: [Select]
volatile unsigned long isrPulsewidth[RADIO_CHANNEL_NO];
In the loop() portion of your code just copy this value accross to another variable:

Code: [Select]
noInterrupts();           // Switch off interrupts
for (byte i = 0; i < RADIO_CHANNEL_NO; i++)
{
    rxPulsewidth[i] = isrPulsewidth[i];
}       
interrupts();            // Switch on interrupts

On the output side just use the following code, this is for 8-channel PWM at 50Hz:

Code: [Select]
// Enable dual slope, 14-bit resolution PWM at 50Hz on 8 channels
void setup() {
  // PWM set-up on pins DAC1, A8, A9, A10, D9, D8, D7 and D6 for channels 0 through to 7 respectively
  REG_PMC_PCER1 |= PMC_PCER1_PID36;                                               // Enable PWM
  REG_PIOB_ABSR |= PIO_ABSR_P19 | PIO_ABSR_P18 | PIO_ABSR_P17 | PIO_ABSR_P16;     // Set the port B PWM pins to peripheral type B
  REG_PIOC_ABSR |= PIO_ABSR_P24 | PIO_ABSR_P23 | PIO_ABSR_P22 | PIO_ABSR_P21;     // Set the port C PWM pins to peripheral type B
  REG_PIOB_PDR |= PIO_PDR_P19 | PIO_PDR_P18 | PIO_PDR_P17 | PIO_PDR_P16;          // Set the port B PWM pins to outputs
  REG_PIOC_PDR |= PIO_PDR_P24 | PIO_PDR_P23 | PIO_PDR_P22 | PIO_PDR_P21;          // Set the port C PWM pins to outputs
  REG_PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(42);                               // Set the PWM clock A rate to 2MHz (84MHz/42)
 
  for (uint8_t i = 0; i < PWMCH_NUM_NUMBER; i++)                       // Loop for each PWM channel (8 in total)
  {
    PWM->PWM_CH_NUM[i].PWM_CMR = PWM_CMR_CALG | PWM_CMR_CPRE_CLKA;     // Enable dual slope PWM and set the clock source as CLKA
    PWM->PWM_CH_NUM[i].PWM_CPRD = 20000;                               // Set the PWM frequency 2MHz/(2 * 20000) = 50Hz;
  }
 
  REG_PWM_ENA = PWM_ENA_CHID7 | PWM_ENA_CHID6 | PWM_ENA_CHID5 | PWM_ENA_CHID4 |    // Enable all PWM channels
                PWM_ENA_CHID3 | PWM_ENA_CHID2 | PWM_ENA_CHID1 | PWM_ENA_CHID0;
  for (uint8_t i = 0; i < PWMCH_NUM_NUMBER; i++)                      // Loop for each PWM channel (8 in total)
  {
    PWM->PWM_CH_NUM[i].PWM_CDTYUPD = 1000;                            // Set the PWM duty cycle to 1000 min throttle, 2000 max throttle
  }
}

void loop() {}

Just load the PWM_CDTYUPD[] registers with the value in microsconds that you'd like the PWM be and it'll output it a 50Hz. For example 1000 low throttle, through to 2000 max throttle.

Go Up