Netherlands
Asked — Edited
Resolved Resolved by Jeremie!

Can I Read The Signal From An RC Receiver?

I would like to read the signal incoming from an RC receiver. How do I do that? Use-case: I drive my robot with radio-control, animations are automatic and controlled by EZ-B. Still want to pass on radio signals to EZ-B to trigger certain animation scripts. How can I read the radio signal I get in the receiver. From a receiver I can steer a servo by just plugging it in, how can I get that signal to EZ-B in an easy way. Maybe I am overlooking something very simple but spend some time searching and still no hint.


Related Hardware EZ-B v4

ARC Pro

Upgrade to ARC Pro

ARC Pro will give you immediate updates and new features needed to unleash your robot's potential!

PRO
USA
#1   — Edited

Quote:

I would like to read the signal incoming from an RC receiver. How do I do that?
you need extra hardware. If you search the internet there are a few how-tos on how to do this. You will need an arduino controller and customized some c code, compile with Arduino IDE and flash/upload the firmware to the controller. You can instruct your arduino to read the ppm signals and send it through the serial port, then you can connect the arduino to the PC.

Quote:

Still want to pass on radio signals to EZ-B to trigger certain animation scripts.
EZ-B controller has specific features built-in (firmware), what you want to do is not on the "features menu".

Quote:

how can I get that signal to EZ-B in an easy way.
the simple answer is not possible. EZ-B can read analog and digital signals.

Quote:

Maybe I am overlooking something very simple
I believe you are overlooking the full picture.

So let's brainstorm the idea:

  1. you want to connect a RC receiver to EZ-B, so EZ-B has digital ports 0/1 values, analog ports can read 0-255, and serial ports (RX). How do you plan to connect this RC signal ? Let's imagine you hook it to an ADC port.

  2. ARC software: you can read ADC port, how frequent you want to read the ADC port ? To start each second, so your ARC logic reads a value (0-255) each second what you plan to do with it ?

  3. Bear in mind you are not streaming the values, you are polling the values from the EZ-B so the reaction time is constrained to specific intervals e.g. 1 second, 1/2 second, 1/4 second or less if you use a wired connection.

Deconstructing a "simple idea" can help you understand the limitations and the possibilities.

If you want to implement your idea you will need extra hardware and/or code/copy-paste some arduino code and then you will need to code your logic on ARC.

PRO
Canada
#2  

What @PTP said is spot on. Just to add to his assessment, is that you will need to decode the signals coming from the RC Receiver in order to use those specific commands to trigger events. There are Arduino projects out there that could do it, all that's required is to decode a PWM servo signal. It's likely that ARC could do this job (with any EZ-B) but it would have to be added as a feature request, as I don't think it exists yet.

ADC is an option too as different PWM servo signals will also be different in their analog voltage representation.

You could also simply use the EZ-B to trigger events/actions when a certain channel goes high, this would just require a bunch of channels and some scripts running in ARC.

PRO
Netherlands
#3  

Thank you for the quick feedback and time to give an answer. The short summary is: not a simple solution that can be handled in SW currently. Looks like pwm to voltage converter is the best option, will do some internet searching. Thanks!

PRO
Canada
#4   — Edited

ADC pins (EZ-B v4 has 8) do automatic PWM to voltage conversion, give it a shot!

PRO
Netherlands
#5  

That is what I tried first but if I connect RC signal to an ADC pin I didn't detect a signal (eg different voltages) when I operated the remote control?

PRO
Canada
#6  

Hmmm....you are correct @Charel, it seems that the average voltage is quite low and the EZ-B's ADC has a hard time picking it up. The voltage is between 0.16V and 0.4V (0-180 servo signals). You could likely use a DAC chip or an RC Low-pass filter to make this work.

PRO
Netherlands
#7   — Edited

I have solved it the following way, I left a note for others to find it and hopefully provide a solution.

I bought a simple Arduino which is reading the  PWM RC signals and sending the results via I2C to EZ-B. Arduino: Arduino Pro Mini 328 - 5V/16MHz.

I need to read 6 RC channels, 2 channels for a joystick (throttle, rudder) and 4 channels for levers (SC, SF, SA, SD). The levers can have 2 or 3 positions. The joystick I wanted to have a range from -25 to + 25.

Arduino code

#include <Wire.h>

//
// R2D2 RC conversion to I2C EZ-B interface
//
// October 2016

double pin_read;                // read a pin
int out_thro;                   // output throttle stick temp variable
int out_rud;                    // output rudder stick  temp variable
char out_I2C[7];                // I2C output signal
int ledState = LOW;             // ledState used to set the LED


void setup() {
    pinMode(2,INPUT);             // Setup pin 2 for input throttle = up/down
    pinMode(3,INPUT);             // Setup pin 3 for input Rudder = left/right
    pinMode(4,INPUT);             // Setup pin 4 for input Gear / SC = trigger
    pinMode(5,INPUT);             // Setup pin 5 for input Aux1 / SF = tbd
    pinMode(6,INPUT);             // Setup pin 6 for input Aux2 / SA = 3*3 script id
    pinMode(7,INPUT);             // Setup pin 7 for input Aux3 / SD = 3*3 script id
    pinMode(LED_BUILTIN, OUTPUT); // Setup led pin for output
    Serial.begin(19200);          // Setup serial for monitoring
    Wire.begin(8);                // join i2c bus with address #8
    Wire.onRequest(requestEvent); // register event
    Serial.println("RC to I2C started.");
}

void loop() {
    // Do nothing except waiting for interrupt request for I2C event
    delay(100);
};    

void requestEvent() {
  // Event function that executes whenever data is requested by I2C EZ-B master
  
   //Pin 2 = throttle = up/down
   pin_read = pulseIn(2, HIGH);
   out_thro = map(pin_read, 990, 2000, 50, 100); //range of 50 
   out_I2C[0] = (char) out_thro; // use characters in the plain ascii range, to get to correct number substract 75

   //Pin 3 = Rudder = left/right
   pin_read = pulseIn(3, HIGH);
   out_rud = map(pin_read, 990, 2000, 50, 100); //range of 50 
   out_I2C[1] = (char) out_rud; // use characters in the plain ascii range, to get to correct number substract 75

   // Pin 4 = Gear = SC = trigger, 2 positions
   pin_read = pulseIn(4, HIGH); 
   if (pin_read > 1700) {
     out_I2C[2]='B';
   } else {
     out_I2C[2]='A';
   };

   // Pin 5 = Aux1 / SF = tbd, 2 positions
   pin_read = pulseIn(5, HIGH); 
   if (pin_read > 1700) {
     out_I2C[3]='B';
   } else {
     out_I2C[3]='A';
   };

    // Pin 6 = Aux2 / SA = 3 positions
   pin_read = pulseIn(6, HIGH); 
   if (pin_read < 1200) {
     out_I2C[4]='A';
   } else if (pin_read >1700) {
     out_I2C[4]='C';
   } else {
     out_I2C[4]='B';
   };

  // Pin 7 = Aux3 / SD = 3 positions
   pin_read = pulseIn(7, HIGH); 
   if (pin_read < 1200) {
     out_I2C[5]='A';
   } else if (pin_read >1700) {
     out_I2C[5]='C';
   } else {
     out_I2C[5]='B';
   };

  // Very simple control byte to check for garbled signals
  out_I2C[6] = 'c';

  // Write the I2C signal
  Wire.write(out_I2C); // respond with message of 7 bytes as expected by EZ-B master

  //Signal I2C request as blinking led signal
  
  // if the LED is off turn it on and vice-versa:
  if (ledState == LOW) {
    ledState = HIGH;
  } else {
    ledState = LOW;
  }
  digitalWrite(LED_BUILTIN, ledState);
  
  // Serial prints handy for debugging
  Serial.print(out_I2C);
  Serial.print(" [ ");
  out_thro = (int) out_I2C[0]-75;
  Serial.print(out_thro);
  Serial.print(" , ");
  out_rud = (int) out_I2C[1]-75;
  Serial.print(out_rud);
  Serial.println(" ] ");

  
}


And the follwing code is running in the EZ-B

#----------------------------------------#
# Script to read RC signals via Arduino  #
#----------------------------------------#

#Init
$RC_SC_mem = false
$RC_rud_mem=0
$Input_I2C="error init value"

:Loopforever

#Loop
$Input_I2C=I2CRead(1, 8,7 )
#print($Input_I2C)

# control checks
if (Length($Input_I2C)!=7)
  goto(Loopforever)
endif 

if (GetCharat($Input_I2C,6)!="c")
  # very simple control byte, if not c the data is garbled
  goto(Loopforever)
endif
 
# get the data
$RC_thro=GetByteAt($Input_I2C,0)-75
$RC_rud=GetByteAt($Input_I2C,1)-75
$RC_SC=GetCharAt( $Input_I2C, 2 ) 
$RC_SF=GetCharAt( $Input_I2C, 3 ) 
$RC_SA=GetCharAt( $Input_I2C, 4 ) 
$RC_SD=GetCharAt( $Input_I2C, 5 ) 
#print($RC_thro + " > "  + $RC_rud + " > "+ $RC_SC + " > " + $RC_SF + " > " + $RC_SA + " > " +$RC_SD )

# Process data

#-------------------------------------#
# Trigger Dome movement from RC stick #
#-------------------------------------#

if ($RC_rud=1) or ($RC_rud=-1)
  $RC_rud = 0  # dead band
endif
$RC_rud = $RC_rud * -1 # change directions + is move right

if ($RC_rud>25) or ($RC_rud<-25)
  # Should not be possible, safety
  $RC_rud=0
endif 

if ($RC_rud!=0) or ($RC_rud_mem!=0)
  # Do not send a signal if two times 0 in a row, that way you can overwrite automated scripts running in parallel and end with 0 but not 
  # continiously stopping scripts if in neutral position by resending 0 all the time, normal automated scripts can continue to function.
  $RC_rud_mem=$RC_rud #Update memory
  $Dome_command = "1, s" + $RC_rud *72 + "\r\n"
  #print($Dome_command)
  SendSerial(1.d0, 9600,$Dome_command) # in my R2D2 the dome motor is controlled by a sabertooth via serial
endif 


# todo assign a function to the throttle stick

#--------------------------------#
# Trigger scripts from RC levers #
#--------------------------------#

# SC lever is used to trigger a script identified by SA and SD 

if ($RC_SC != "B")
  # No trigger, do nothing, reset the memory
  $RC_SC_mem = false
  goto(Loopforever)
Elseif ($RC_SC="B") and ($RC_SC_mem = true)
  # Two trigger in a row, do nothing, no memory reset
  goto(Loopforever)
endif 

# New Trigger to execute
$RC_SC_mem = true #set the memory to prevent double triggers

# Trigger a script depending on SA and SD setting

if ($RC_SA="A") and ($RC_SD="A")
  print(" Ping AA remember vader")
  ControlCommand("Script Manager Animations", "ScriptStart", "Remember vader")
endif 

if ($RC_SA="A") and ($RC_SD="B")
  print(" Ping AB remember cantina")
  ControlCommand("Script Manager Animations", "ScriptStart", "Remember cantina")
endif
 
if ($RC_SA="A") and ($RC_SD="C")
  print(" Ping AC disco")
  ControlCommand("Script Manager Animations", "ScriptStart", "Disco")
endif 

sleep(50)
goto(Loopforever)

Hope it is useful for you!

PRO
Netherlands
#8  

The Arduino code above worked perfectly for an EZ-B connected over wifi, but somehow does not work if your EZ-B is connected over serial/USB. The problem is if the RC has not yet been connected, the Arduino transmits "INVALID" (by design) and serial/UCB connected EZ-B hangs itself after some time. It reads numerous "INVALID"'s correct but after some time it hangs. the Arduino code below solves that problem (but it does not work for an wifi connected EZ-B), sigh.

#include <Wire.h>

//
// R2D2 RC conversion to I2C EZ-B interface
//
// C.A. van Hoof
// January 2023

double pin_read;                // read a pin
int out_thro;                   // output throttle stick temp variable
int out_rud;                    // output rudder stick  temp variable
char out_I2C[7];                // I2C output signal
int ledState = LOW;             // ledState used to set the LED

void setup() {
  pinMode(2,INPUT);             // Setup pin 2 for input throttle = up/down
  pinMode(3,INPUT);             // Setup pin 3 for input Rudder = left/right
  pinMode(4,INPUT);             // Setup pin 4 for input Gear / SC = trigger
  pinMode(5,INPUT);             // Setup pin 5 for input Aux1 / SF = safety
  pinMode(6,INPUT);             // Setup pin 6 for input Aux2 / SA = 3*3 script id
  pinMode(7,INPUT);             // Setup pin 7 for input Aux3 / SD = 3*3 script id
  pinMode(LED_BUILTIN, OUTPUT); // Setup led pin for output
  Serial.begin(19200);          // Setup serial for monitoring
  Wire.begin(8);                // join i2c bus with address #8
  Wire.onRequest(requestEvent); // register event
  Serial.println("RC arduino version: 17-01-2023 rewrite V2");
  Serial.println("RC to I2C started, waiting for RC connection");

}

void loop() {

   // RC receiver transmits settings if connected
   // RC receiver transmits fail-safe settings if RC transmitter is not connected
   // In my case all 0 (middle positions, e.g. pinread around 1500)
   // Except when RC transmitter-receiver was never first time connected, then no signal is transmitted
   // This V2 works with an EZ-B connected with serial/USB
   
   // Main loop is empty since the interrupt signal triggers the response with sending the data
  
    delay(1000); // 
}    

void requestEvent() {
  // Event function that executes whenever data is requested by I2C EZ-B master

  pin_read = pulseIn(2, HIGH); // note always read a pin in the request event, else I2C goes wrong with EZ-B somehow 
    
  if (pin_read < 100) {
    // No valid signal within the time-out, RC not yet connected once, signal should be in 900-2100 range
    out_I2C[0] = 'I';
    out_I2C[1] = 'N';
    out_I2C[2] = 'V';
    out_I2C[3] = 'A';
    out_I2C[4] = 'L';
    out_I2C[5] = 'I';
    out_I2C[6] = 'D'; // force invalid control character
    goto bailout;
  }
  
   //Pin 2 = throttle = up/down
   out_thro = map(pin_read, 990, 2010, 50, 100); //range of 50 
   out_I2C[0] = (char) out_thro;
   
   //Pin 3 = Rudder = left/right
   pin_read = pulseIn(3, HIGH);
   out_rud = map(pin_read, 990, 2010, 50, 100); //range of 50 
   out_I2C[1] = (char) out_rud;

   // Pin 4 = Gear = SC = trigger, 2 positions
   pin_read = pulseIn(4, HIGH); 
   if (pin_read > 1700) {
     out_I2C[2]='B';
   } else {
     out_I2C[2]='A';
   };

   // Pin 5 = Aux1 / SF = safety, 2 positions
   pin_read = pulseIn(5, HIGH); 
   if (pin_read > 1700) {
     out_I2C[3]='B';
   } else {
     out_I2C[3]='A';
   };

    // Pin 6 = Aux2 / SA = 3 positions
   pin_read = pulseIn(6, HIGH); 
   if (pin_read < 1200) {
     out_I2C[4]='A';
   } else if (pin_read >1700) {
     out_I2C[4]='C';
   } else {
     out_I2C[4]='B';
   };

  // Pin 7 = Aux3 / SD = 3 positions
   pin_read = pulseIn(7, HIGH); 
   if (pin_read < 1200) {
     out_I2C[5]='A';
   } else if (pin_read >1700) {
     out_I2C[5]='C';
   } else {
     out_I2C[5]='B';
   };

  // Very simple control byte to check for garbled signals
  out_I2C[6] = 'c';

  bailout:
  
  // Write the I2C signal
  Wire.write(out_I2C); // respond with message of 7 bytes as expected by EZ-B master

  //Signal I2C request as blinking led signal
  
  // if the LED is off turn it on and vice-versa:
  if (ledState == LOW) {
    ledState = HIGH;
  } else {
    ledState = LOW;
  }
  digitalWrite(LED_BUILTIN, ledState);
  

  Serial.print(out_I2C);
  Serial.print(" [ ");
  out_thro = (int) out_I2C[0]-75;
  Serial.print(out_thro);
  Serial.print(" , ");
  out_rud = (int) out_I2C[1]-75;
  Serial.print(out_rud);
  Serial.println(" ] ");

  
}