Model Boat Mayhem

Please login or register.

Login with username, password and session length.
Pages: [1]   Go Down

Author Topic: Rc transmitter to arduino  (Read 546 times)

Pete m

  • Shipmate
  • *
  • Offline Offline
  • Posts: 7
  • Model Boat Mayhem is Great!
  • Location: New zealand
Rc transmitter to arduino
« on: September 06, 2021, 09:31:48 am »

hello everyone
I am having issues with my arduino uno  and hope someone can help
I am trying to input my 4 spare switches on my flysky i6x into the arduino to control output to relays to switch lights on and off
Does anyone have a sketch pre done that they would share ,or would it be easier just to purchase switches that can be controlled directly off of the receiver channels,I would go arduino as I already have got one in a box of goodies from a sale,programming not my strong suit ,
Thankyou
Logged

C-3PO

  • Full Mayhemer
  • *****
  • Online Online
  • Posts: 1,017
  • I thought that hairy beast would be the end of me
  • Location: Outer Rim world of Tatooine
Re: Rc transmitter to arduino
« Reply #1 on: September 06, 2021, 12:55:42 pm »

Hello Pete,

Yes - several ways to approach this one. The example you give may well be easier with a dedicated off the shelf "switcher" unit

If you want to use your Uno then it is also very easy but will require a relay board or MOSFET's to achieve the actual "switching" controlled by the Arduino interfaced to your radio receiver.

A simple 4 x relay board (UK price £3.50) pictured below - ideally the board would encompass Optocouplers and have selectable high/low logic triggers

Step One - Have you got a sketch at the moment that reads the receiver outputs?

Regards
C-3PO

Logged
I think it's the way I have learnt most of my stuff - getting very stuck first...

Pete m

  • Shipmate
  • *
  • Offline Offline
  • Posts: 7
  • Model Boat Mayhem is Great!
  • Location: New zealand
Re: Rc transmitter to arduino
« Reply #2 on: September 10, 2021, 09:13:51 am »

thankyou  i think i have managed to work out the inputs  as i can now see them in the serial monitor, however i think i might go the way of dedicated switches (easy option ) time to fire up ebay or aliexpress,  again thanks for the advice
Logged

C-3PO

  • Full Mayhemer
  • *****
  • Online Online
  • Posts: 1,017
  • I thought that hairy beast would be the end of me
  • Location: Outer Rim world of Tatooine
Re: Rc transmitter to arduino
« Reply #3 on: September 10, 2021, 09:42:24 am »

Hi Pete,

You will no doubt have seen the values in your serial monitor ranging from 1000-2000 where mid stick is approx 1500

If you did want to proceed with your Arduino solution then you would simply test the state of each receiver channel value and then switch a digital pin high or low that would trigger the relevant relay

Code would be something like this:

if (rxCH01>1650) {
   digitalWrite(9, HIGH);
}
   else
{
   digitalWrite(9, LOW);
}



if (rxCH01>1650) { // 1650 is the receiver channel value we are testing if greater than

// do stuff if the condition is true

// set relay pin on

digitalWrite(9, HIGH);

}

 else

 {

// do stuff if the condition is false

// set relay pin off

digitalWrite(9, LOW);

}

Regards
C-3PO
Logged
I think it's the way I have learnt most of my stuff - getting very stuck first...

Pete m

  • Shipmate
  • *
  • Offline Offline
  • Posts: 7
  • Model Boat Mayhem is Great!
  • Location: New zealand
Re: Rc transmitter to arduino
« Reply #4 on: September 13, 2021, 06:50:17 am »

hi again i have managed a sketch to set inputs and outputs but for some reason on my uno only pin2 (input) is reading and sending output to pin13 none of the other input pins seem to work  i have included my sketch please feel free to edit it
thanks peter



// set variables
int channel1;
int channel2;
int channel3;
int channel4;
int channel5;
int channel6;

void setup() {  //input and outputs
  // input pins
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  pinMode(6, INPUT);
  pinMode(7, INPUT);

  // output pins
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);

// time for serial
  Serial.begin(9600);

}

void loop() {
  // read inputs
  channel1 = pulseIn(2, HIGH, 25000);
  channel2 = pulseIn(3, HIGH, 25000);
  channel3 = pulseIn(4, HIGH, 25000);
  channel4 = pulseIn(5, HIGH, 25000);
  channel5 = pulseIn(6, HIGH, 25000);
  channel6 = pulseIn(7, HIGH, 25000);

// pin 13 (channel 1)
  if (channel1 > 1400 )
  {
    digitalWrite(13, HIGH);
  }
  else
  {
    digitalWrite(13, LOW);
  }
   // pin 12 (channel 2)
  if (channel2 >1400 )
  {
    digitalWrite(12, HIGH);
  }
  else
  {
    digitalWrite(12, LOW);
  }
    // pin 11 (channel 3)
  if (channel3 >1400 )
  {
    digitalWrite(11, HIGH);
  }
  else
  {
    digitalWrite(11, LOW);
  }
      // pin 10 (channel 4)
  if (channel4 >1400 )
  {
    digitalWrite(10, HIGH);
  }
  else
  {
    digitalWrite(10, LOW);
  }
 
}
Logged

C-3PO

  • Full Mayhemer
  • *****
  • Online Online
  • Posts: 1,017
  • I thought that hairy beast would be the end of me
  • Location: Outer Rim world of Tatooine
Re: Rc transmitter to arduino
« Reply #5 on: September 13, 2021, 07:48:06 am »

Hello Peter,

pulseIn() is a "blocking" command - the loop stops and waits for the command to finish (timeout) before it goes on to the next line in the loop - the fallout is odd behaviour which is often difficult to decipher - the net result is it does not do what you want.

pulseIn() has it's place but is fraught with problems - I never use it!

I could dig out a sketch that used pulseIn() and sort of works - but it will only be delaying future heart ache!!

The best way to read the output of multiple receiver channels is using interrupts - if you have not used them before they take a bit of understanding but it's 100% the way to go.

Interrupts - Basically you attach an interrupt to an Arduino pin to tell it to run some code when the pin value changes - the rest of the time the loop keeps running. So using multiple interrupts each time the receiver values changes on the pins you are monitoring the Arduino instantly reacts and captures the reciever output value

Have a look at this EXCELLENT article https://ryanboland.com/blog/reading-rc-receiver-values

and you can use this code below based on his solution (just noticed this code has another code blocking command delay() - only use delay in development!! This only written for 4 channels but I have versions reading 10 and more channels

Regards
C-3PO

Note it uses an array to store the RC channel values - to get access to them use the reference to the array rc_values(x) where x is the channel you want - note array starts at zero so channel one is rc_values(0)!

    #include <EnableInterrupt.h>
    #define SERIAL_PORT_SPEED 57600
    #define RC_NUM_CHANNELS 4

    #define RC_CH1 0
    #define RC_CH2 1
    #define RC_CH3 2
    #define RC_CH4 3

    #define RC_CH1_INPUT A0
    #define RC_CH2_INPUT A1
    #define RC_CH3_INPUT A2
    #define RC_CH4_INPUT A3

    uint16_t rc_values[RC_NUM_CHANNELS];
    uint32_t rc_start[RC_NUM_CHANNELS];
    volatile uint16_t rc_shared[RC_NUM_CHANNELS];

    void rc_read_values() {
    noInterrupts();
    memcpy(rc_values, (const void *)rc_shared, sizeof(rc_shared));
    interrupts();
    }

    void calc_input(uint8_t channel, uint8_t input_pin) {
    if (digitalRead(input_pin) == HIGH) {
    rc_start[channel] = micros();
    } else {
    uint16_t rc_compare = (uint16_t)(micros() - rc_start[channel]);
    rc_shared[channel] = rc_compare;
    }
    }

    void calc_ch1() { calc_input(RC_CH1, RC_CH1_INPUT); }
    void calc_ch2() { calc_input(RC_CH2, RC_CH2_INPUT); }
    void calc_ch3() { calc_input(RC_CH3, RC_CH3_INPUT); }
    void calc_ch4() { calc_input(RC_CH4, RC_CH4_INPUT); }

    void setup() {
    Serial.begin(SERIAL_PORT_SPEED);
    pinMode(RC_CH1_INPUT, INPUT);
    pinMode(RC_CH2_INPUT, INPUT);
    pinMode(RC_CH3_INPUT, INPUT);
    pinMode(RC_CH4_INPUT, INPUT);

    enableInterrupt(RC_CH1_INPUT, calc_ch1, CHANGE);
    enableInterrupt(RC_CH2_INPUT, calc_ch2, CHANGE);
    enableInterrupt(RC_CH3_INPUT, calc_ch3, CHANGE);
    enableInterrupt(RC_CH4_INPUT, calc_ch4, CHANGE);
    }

    void loop() {
    rc_read_values();
    Serial.print("CH1:"); Serial.print(rc_values[RC_CH1]); Serial.print("\t");
    Serial.print("CH2:"); Serial.print(rc_values[RC_CH2]); Serial.print("\t");
    Serial.print("CH3:"); Serial.print(rc_values[RC_CH3]); Serial.print("\t");
    Serial.print("CH4:"); Serial.println(rc_values[RC_CH4]);
    delay(200);
    }


I just found some text I posted in 2018 about the lovely PulseIn()

PulseIn (a blocking polling approach)

PulseIn is a function available with the Arduino that implements an approach know as 'polling'. It essentially sits around waiting for something to happen, until something happens the rest of your code is blocked. This is okay for a simple lab exercise to read and print values from a receiver but it is a hopeless approach for a real world application.

If all we are interested in is the pulse duration, why not use pulseIn ? after all there is nothing else of interest in the signal.

The Arduino UNO is able to perform 16 million operations in one second. In the 2 milli second duration of a full throttle pulse, the Arduino could have performed 32,000 operations ! Thats a lot of wasted power. But it gets worse, what if you call pulseIn immediately after a pulse, you will have to wait a whole 20 milliseconds for the next pulse to arrive and complete. Thats a full 320,000 operations useful operations your code could have completed.

On average you can expect to call pulseIn in the center between two pulses, this means that over an extended period, half or your available processing power is wasted just waiting for the next pulse to arrive and complete. If you add more inputs, the approach becomes quickly unsustainable as you can only give your attention to a single input at a time.

Don't Use Pulse In !
Logged
I think it's the way I have learnt most of my stuff - getting very stuck first...

Pete m

  • Shipmate
  • *
  • Offline Offline
  • Posts: 7
  • Model Boat Mayhem is Great!
  • Location: New zealand
Re: Rc transmitter to arduino
« Reply #6 on: September 13, 2021, 10:20:31 am »

I think my head is about to explode. Wow thatís a code that will take me a while to get my tiny brain around
will the inputs need to be changed to digital ports 2-5. As A0-A4 are analog and where do I put the outputs.
 Thanks again. So confused. Peter
Logged

C-3PO

  • Full Mayhemer
  • *****
  • Online Online
  • Posts: 1,017
  • I thought that hairy beast would be the end of me
  • Location: Outer Rim world of Tatooine
Re: Rc transmitter to arduino
« Reply #7 on: September 13, 2021, 10:30:50 am »

Hello Peter,

Read through the article by Ryan - at the end it explains what the sections of code are doing - however you could just use it as is

Regarding the pins input pins A0-A5 analog pins can be used as digital as well! - or you could leave the physical wiring you have and amend code to

    #define RC_CH1_INPUT 2
    #define RC_CH2_INPUT 3
    #define RC_CH3_INPUT 4
    #define RC_CH4_INPUT 5

And of course you need another 2 channels configured in the code

Would you like a 6 channel piece of code based on your code's input and outputs to test?

Regards
C-3PO
Logged
I think it's the way I have learnt most of my stuff - getting very stuck first...

C-3PO

  • Full Mayhemer
  • *****
  • Online Online
  • Posts: 1,017
  • I thought that hairy beast would be the end of me
  • Location: Outer Rim world of Tatooine
Re: Rc transmitter to arduino
« Reply #8 on: September 13, 2021, 10:54:13 am »

Untested code but should work with your current wiring/pin assignment

A quick analogy of what an interrupt is and why it is useful

Picture a fire alarm system with a processor, an alarm sounder and a fire detection sensor.

If the program loop took 1 minute to loop back to the start, if the fire detector sensor triggered, it could take almost 1 minute to reach the code line where the alarm sounder is triggered.

Using an "ISR - interrupt service routine" attached to the sensor pin means as soon the the pin changes state the processor will halt loop execution, jump out of the loop and run the defined ISR subroutine ( sound the alarm ) and then when finished executing the ISR subroutine will pass back to the loop allowing it to continue to run.

When you are trying to measure the time period of pulses - using a solution that can instantly record the time the pulse changes (goes HIGH, goes LOW or CHANGES state) is a must

#include <EnableInterrupt.h>
#define SERIAL_PORT_SPEED 57600
#define RC_NUM_CHANNELS 6

// define the array cell positions that we will store our values in, e.g. channel one will be stored in array position zero, channel 2 in array position 1 and so on...
#define RC_CH1 0
#define RC_CH2 1
#define RC_CH3 2
#define RC_CH4 3
#define RC_CH5 4
#define RC_CH6 5

// define physical pins used to connect Arduino to the radio receiver - can be any digital or analog pins - best to avoid pin 13
#define RC_CH1_INPUT 2
#define RC_CH2_INPUT 3
#define RC_CH3_INPUT 4
#define RC_CH4_INPUT 5
#define RC_CH5_INPUT 6
#define RC_CH6_INPUT 7

// define 3 arrays to store time values
uint16_t rc_values[RC_NUM_CHANNELS]; // the final channel readings will be stored here
uint32_t rc_start[RC_NUM_CHANNELS]; // the start time of the pulse - i.e. the time the pin changes to HIGH will be stored here
volatile uint16_t rc_shared[RC_NUM_CHANNELS]; // the channel times (current time minus start time = pulse width)are stored here during the interrupt routine but they must be copied from this array to our rc_values array to be used in the loop due to their volatile nature - e.g. the values were changed outside of the loop by the (ISR) interrupt service routine

// subroutine called in loop to copy volatile array values from memory to rc_values array so we can use them in our loop
void rc_read_values() {
  noInterrupts(); // turn off interrupts
  memcpy(rc_values, (const void *)rc_shared, sizeof(rc_shared)); // copy volatile array values from rc_shared array to rc_values array
  interrupts(); // turn interrupts on
}

// subroutine to calculate all channel time values - we pass 2 patameters into the subroutine - the channel number e.g. RC_CH1 and the pin number e.g. RC_CH1_INPUT and it stores the value in our shared (volatile) array
void calc_input(uint8_t channel, uint8_t input_pin) {
  if (digitalRead(input_pin) == HIGH) {
    rc_start[channel] = micros(); // if pin went high its the start of a pulse so store the time the pin changed
  } else {
    uint16_t rc_compare = (uint16_t)(micros() - rc_start[channel]); // pin has gone low so calulate "pulse period" -  current time - start time to get channel time value
    rc_shared[channel] = rc_compare; // store channel time value in our shared volatile array
  }
}

// series of routines  - passing in the parameters - values of storage array number and input pin number using the generic calc_input(storage array number, input pin number)
void calc_ch1() {
  calc_input(RC_CH1, RC_CH1_INPUT);
}
void calc_ch2() {
  calc_input(RC_CH2, RC_CH2_INPUT);
}
void calc_ch3() {
  calc_input(RC_CH3, RC_CH3_INPUT);
}
void calc_ch4() {
  calc_input(RC_CH4, RC_CH4_INPUT);
}
void calc_ch5() {
  calc_input(RC_CH5, RC_CH5_INPUT);
}
void calc_ch6() {
  calc_input(RC_CH6, RC_CH6_INPUT);
}


void setup() {
  Serial.begin(SERIAL_PORT_SPEED);
 
  // define pin modes
  // output pins
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);

  // input pins
  pinMode(RC_CH1_INPUT, INPUT);
  pinMode(RC_CH2_INPUT, INPUT);
  pinMode(RC_CH3_INPUT, INPUT);
  pinMode(RC_CH4_INPUT, INPUT);
  pinMode(RC_CH5_INPUT, INPUT);
  pinMode(RC_CH6_INPUT, INPUT);

  // assign interrupts (Interrupt Service Routine) to pins and the name of the subroutine code to run when the pin changes
  // enableInterrupt(pin number, subroutine name, state - options HIGH/LOW/CHANGE)
  enableInterrupt(RC_CH1_INPUT, calc_ch1, CHANGE);
  enableInterrupt(RC_CH2_INPUT, calc_ch2, CHANGE);
  enableInterrupt(RC_CH3_INPUT, calc_ch3, CHANGE);
  enableInterrupt(RC_CH4_INPUT, calc_ch4, CHANGE);
  enableInterrupt(RC_CH5_INPUT, calc_ch5, CHANGE);
  enableInterrupt(RC_CH6_INPUT, calc_ch6, CHANGE);
}
void loop() {
  rc_read_values(); // refresh values by copying values from memory to array each loop
  Serial.print("CH1:"); Serial.print(rc_values[RC_CH1]); Serial.print("\t");
  Serial.print("CH2:"); Serial.print(rc_values[RC_CH2]); Serial.print("\t");
  Serial.print("CH3:"); Serial.print(rc_values[RC_CH3]); Serial.print("\t");
  Serial.print("CH4:"); Serial.print(rc_values[RC_CH4]); Serial.print("\t");
  Serial.print("CH5:"); Serial.print(rc_values[RC_CH5]); Serial.print("\t");
  Serial.print("CH6:"); Serial.println(rc_values[RC_CH6]);

  // swtich our pins high of low

  // pin 13 (channel 1)
  if (rc_values[RC_CH1] > 1400 )
  {
    digitalWrite(13, HIGH);
  }
  else
  {
    digitalWrite(13, LOW);
  }
 
  // pin 12 (channel 2)
  if (rc_values[RC_CH2] > 1400 )
  {
    digitalWrite(12, HIGH);
  }
  else
  {
    digitalWrite(12, LOW);
  }
 
  // pin 11 (channel 3)
  if (rc_values[RC_CH3] > 1400 )
  {
    digitalWrite(11, HIGH);
  }
  else
  {
    digitalWrite(11, LOW);
  }
 
  // pin 10 (channel 4)
  if (rc_values[RC_CH4] > 1400 )
  {
    digitalWrite(10, HIGH);
  }
  else
  {
    digitalWrite(10, LOW);
  }

  // pin 9 (channel 5)
  if (rc_values[RC_CH5] > 1400 )
  {
    digitalWrite(9, HIGH);
  }
  else
  {
    digitalWrite(9, LOW);
  }

// pin 8 (channel 6)
  if (rc_values[RC_CH6] > 1400 )
  {
    digitalWrite(8, HIGH);
  }
  else
  {
    digitalWrite(8, LOW);
  }

}

Info about volatile values

A variable should be declared volatile whenever its value can be changed by something beyond the control of the code section in which it appears, such as a concurrently executing thread. In the Arduino, the only place that this is likely to occur is in sections of code associated with interrupts, called an interrupt service routine.

https://www.arduino.cc/reference/en/language/variables/variable-scope-qualifiers/volatile/
Logged
I think it's the way I have learnt most of my stuff - getting very stuck first...
Pages: [1]   Go Up