2

Scenario

I've plugged 6 pins from my RC receiver into my Arduino in order to read the incoming values.

Problem

I'm using Digital PWM~ pins 2-6. Oddly the Arduino only reads odd channels:

1480, 0, 1175, 0, 991, 0

I've that if I physically reverse the cables (2-7, 3-6, 4-5) going into the Arduino I get ALL values:

988, 983, 1511, 1174, 1490, 1487

I've found that if I go back to my initial pin setup and merely flip neighbors (2-3, 4-5, 6-7) that works as well.

Question

What could possibly be causing this?

Code

Here is the current code I've written

int ch1, ch2, ch3, ch4, ch5, ch6;
int ch1Pin = 2;
int ch2Pin = 3;
int ch3Pin = 4;
int ch4Pin = 5;
int ch5Pin = 6;
int ch6Pin = 7;

void setup() {
  pinMode(ch1Pin, INPUT);
  pinMode(ch2Pin, INPUT);
  pinMode(ch3Pin, INPUT);
  pinMode(ch4Pin, INPUT);
  pinMode(ch5Pin, INPUT);
  pinMode(ch6Pin, INPUT);

  Serial.begin(9600);
}

void loop() {
  ch1 = pulseIn(ch1Pin, HIGH, 25000);
  ch2 = pulseIn(ch2Pin, HIGH, 25000);
  ch3 = pulseIn(ch3Pin, HIGH, 25000);
  ch4 = pulseIn(ch4Pin, HIGH, 25000);
  ch5 = pulseIn(ch5Pin, HIGH, 25000);
  ch6 = pulseIn(ch6Pin, HIGH, 25000);

  String p = (String)ch1;
  p += ", " + (String)ch2; 
  p += ", " + (String)ch3; 
  p += ", " + (String)ch4;
  p += ", " + (String)ch5;
  p += ", " + (String)ch6;
  Serial.println(p);

//  delay(100);
}

Photo

transmitter pwm pins to arduino digital input

EDIT

Since posting this question I've found that reversing the chXpin values accomplishes the same effect as reversing the physical pins. This makes me thing it's a software issue so I'm digging in further.

TEMPORARY SOLUTION

I'm not sure why this works but flipping the neighbor order of pulseIn fixed the issue:

  /* OLD
  ch1 = pulseIn(ch1Pin, HIGH, 25000);
  ch2 = pulseIn(ch2Pin, HIGH, 25000);
  ch3 = pulseIn(ch3Pin, HIGH, 25000);
  ch4 = pulseIn(ch4Pin, HIGH, 25000);
  ch5 = pulseIn(ch5Pin, HIGH, 25000);
  ch6 = pulseIn(ch6Pin, HIGH, 25000);
  */

  ch2 = pulseIn(ch2Pin, HIGH, 25000);
  ch1 = pulseIn(ch1Pin, HIGH, 25000);
  ch4 = pulseIn(ch4Pin, HIGH, 25000);
  ch3 = pulseIn(ch3Pin, HIGH, 25000);
  ch6 = pulseIn(ch6Pin, HIGH, 25000);
  ch5 = pulseIn(ch5Pin, HIGH, 25000);

Why did that work? It makes no sense to me.

2

pulseIn() is a blocking function: it waits until it gets a complete pulse, or until it times out. While it's waiting, the Arduino cannot do anything else, and it will miss pulses on other channels.

The RC receiver sends the pulses sequentially. Then, if you try to read them in the right sequence, it may work. If you read them out of sequence, you will miss some pulses, and the corresponding pulseIn() calls can timeout, returning zeros.

For this kind of job I would use the pin change interrupt on pins 2 – 7. This should allow reading the six pulses continuously without blocking the whole program. But it's low-level (AVR-level) programming, probably not doable with the standard Arduino libraries.

Edit: Here is what I had in mind when suggesting the use of the pin change interrupt. Notice that the pin numbers are “hardwired” into the code and cannot be changed easily. This is because:

  1. Pins 2 – 7 share a single pin change interrupt request (PCINT2_vect), thus a single ISR is needed to time the six channels.
  2. They also share a port (port D), meaning they can all be read by the ISR in a single instruction.

The other possible choices having these nice properties would be pins 8 – 13 (PCINT0_vect, port B) or A0 – A5 (PCINT1_vect, port C).

/*
 * Read 6 PWM signals from an RC receiver.
 *
 * Channels 0-5 are on digital pins 2-7 (i.e. PD2-PD7 = PCINT18-23).
 */

// This always contains the last length measured on each channel.
volatile uint16_t last_pulse_lengths[6];

// Interrupt triggered on every change in pins 2-7.
ISR(PCINT2_vect)
{
    static uint8_t last_pin_states;
    static uint16_t start_times[6];  // start time for each channel
    uint8_t pin_states = PIND >> 2;  // shifted to bits 0-5
    uint16_t now = micros();

    // Check every changed bit of the port.
    for (int i = 0; i < 6; i++) {
        uint8_t mask = _BV(i);

        // Record start time on rising edge.
        if ((pin_states & mask) && !(last_pin_states & mask))
            start_times[i] = now;

        // Record pulse length on falling edge.
        if (!(pin_states & mask) && (last_pin_states & mask))
            last_pulse_lengths[i] = now - start_times[i];
    }
    last_pin_states = pin_states;
}

// Return the pulse lengths avoiding race conditions.
static void get_pulse_lengths(uint16_t lengths[6])
{
    for (int i = 0; i < 6; i++) {
        noInterrupts();
        uint16_t tmp = last_pulse_lengths[i];
        interrupts();
        lengths[i] = tmp;
    }
}

void setup()
{
    // Setup the pin change interrupt.
    PCIFR  = _BV(PCIF2);  // clear pin change interrupt flag
    PCICR  = _BV(PCIE2);  // enable pin change interrupt
    PCMSK2 = 0xfc;        // sense changes on pins 2-7 (i.e. PD2-PD7)

    Serial.begin(9600);
}

void loop()
{
    // Report the pulse lengths on the serial console.
    uint16_t pulse_lengths[6];
    get_pulse_lengths(pulse_lengths);
    for (int i = 0; i < 6; i++) {
        Serial.print(pulse_lengths[i]);
        if (i < 5) Serial.print(", ");
        else Serial.println();
    }

    delay(1000);
}
  • Would this sort of integration count? playground.arduino.cc/Main/PinChangeInterrupt – Jacksonkr Jun 26 '16 at 13:38
  • 1
    The code you linked to sets individual bits in PCMSKn, PCIFR and PCICR, and it defines the ISR(PCINTn_vect). This is exactly the kind of “low-level” programming I was talking about. Note that if using pins 2 – 7, you only need to set one PCMSK (namely PCMSK2) and only define ISR(PCINT2_vect). – Edgar Bonet Jun 26 '16 at 17:40
  • I see you added example code afterward, I must have missed it the first time. I plugged it in and it worked with my RC flawlessly the first time. I modified the delay from 1000 to 10 and that's it. This answer deserves more than the +1 I already gave it. – Jacksonkr Jul 6 '16 at 22:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.