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.

asked Jun 25, 2016 at 23:25
1
  • As a matter of course, not that it's related to your current issue, but your use of the String class at the end of loop is terribly wasteful and the over-use of the + operator on memory limited systems like an Arduino is known to lead to crashes. There's no reason to assemble that all into a single string to send it. You could just use back to back Serial.print lines and you would use a lot less memory. Probably not an issue on this little test code, but as you advance that one will get you into trouble later. Commented May 27, 2020 at 21:32

1 Answer 1

1

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);
}
answered Jun 26, 2016 at 10:15
3
  • Would this sort of integration count? playground.arduino.cc/Main/PinChangeInterrupt Commented Jun 26, 2016 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). Commented Jun 26, 2016 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. Commented Jul 6, 2016 at 22:58

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.