Nov 25

R/C receiver diagnostic on iPhone

Do you remember my post about a R/C USB adapter ? Today, I tried a Redpark TTL cable for iPhone.

Starting with the R/C USB adapter, I did not changed anything on the Arduino side. I only ported the Processing code on iOS, and connected the Redpark cable to the Arduino. That’s it ! I can now check the output of my R/C receivers on the ground only with my iPhone, an Arduino, and the cable.

You can see the result in the following video :

On the hardware side, the only thing to take care is the voltage used on the serial connection between the Redpark cable and the Arduino. The cable (and the iPhone) uses a 3.3V logic, whereas the Arduino uses a 5V one. Connecting cable TX directly to Arduino RX is OK as Arduino can handle a 3.3V input, but connecting cable RX directly to Arduino TX will damage your cable and maybe your iPhone. You have to make a level-shifter. Best solutions uses transistor or zener diode, but two resistors in voltage-diviser do the job.

For the iPhone app, using the Redpark cable is quite easy, thanks to its SDK. It totally masks the EAAccessory framework. It’s very handy for prototyping using the iPhone dock connector without a MFi access, but you will not learn the EAAccessory framework.

To release it on the AppStore, I need your support in order to subscribe the Apple Developer Program.

Nov 16

WebIOPi 0.5.1 release

Post update : 0.5 release has been updated by 0.5.1 to fix a setup issue.

Around 2000 downloads after the first WebIOPi release, I’m glad to present you the 0.5.1 release. It includes many internal changes to offer you a powerful and simplified tool :

  • New Features
    • REV 2 boards support
    • Added setup script to ease WebIOPi install
    • Use WebIOPi in your own Python scripts
    • Login/Password protection
    • Software PWM
    • Binary sequence output
  • Python Server & REST API
    • Usable as a library
    • Added Python 3 support
    • Removed RPi.GPIO library dependency
    • Improved security
    • Improved file serving
    • Improved REST API
    • Added ability to use custom REST macro
    • Added ability to output a single pulse
    • Added ability to output a binary sequence
    • Added software PWM
  • Javascript Library
    • Improved and simplified
    • Added ability to create custom buttons with one or two callbacks (mousedown and mouseup)
    • Added helpers for new REST functions (macro, pulse, sequence, PWM, …)
  • Other changes
    • PHP Server discontinued

Check the project page and the wiki to download it and get install instructions. Please use the official topic on the RaspberryPi forum to report issues.

Christmas wish list

Everything is done on my free-time, I have many ideas but I miss some things to go further. This is my wish list :

In order to make new cool things for WebIOPi, like SPI, I2C, Serial, Gertboard support

To build and distribute some cool, free, no-ad Android apps for your Raspberry

To distribute some cool, free, no-ad iOS apps for your Raspberry

If you like what I do, and want more free stuff, please encourage and help me with donations. Each Euro count, and all donations will be very much appreciated and be used for the RPi and Open community.




Nov 15

R/C receiver USB adapter – Part 1

This is an article I wrote a year ago, but I didn’t take time to finish and publish it…

We often see projects to control an R/C car/plane/craft from a device like a smartphone or gamepad using Arduino. It’s fun and let you play with Arduino, but it’s not as accurate as a real R/C radio.

Another fun and useful thing to do is the opposite : use an RC transmitter to play games ! It perfectly suits RC simulators. Aero pilots already know that, as most of RTF kits include a USB cord for the transmitter and a simulator. Car and boat pilots aren’t so lucky, and we need to buy a dedicated USB pistol or a radio receiver adapter.

Using Arduino, we can create two kinds of adapter to plug into the radio receiver :

  • USB serial, with Processing onto the computer, to diagnose radio receiver’s output channels.
  • USB HID, to use the radio as an HID joystick, in order to play RC simulators.

This first part covers the radio diagnostic tool, but before, I must remind how RC systems work.

The key signal to handle is PWM. An RC radio receiver is linked to servos and controllers with 3 wires : 2 for the power, and one for the signal. The signal consists of pulses proportionals to the input on the transmitter. A pulse is sent every 20ms, and it’s length varies from 1ms to 2ms. So we just need to measure pulses on Arduino, then send the value though the serial interface.

If we take a look on a Futaba servo spec, we can see :

  • Control System: Pulse Width Control 1520usec Neutral
  • Operating Angle: 45 Deg. one side pulse traveling 400usec

So each pulse has a duration between 1120µs (-45°/-100%) and 1920µs (+45°/+100%). Neutral has a duration of 1520µs.

The wiring is very simple, and only 4 wires are required : 2 for the power and 1 for each channel. Connect the + and – of the receiver’s BAT plug to +5V and ground of Arduino, then Channel-1 and Channel-2 signals to Arduino pins 2 and 3.

The simplest way to measure pulses, is the bult-in pulseIn() Arduino function, but it’s not the most accurate one. The best thing to do is to use interrupts. There is only 2 interrupts on the Uno, but it’s enough for the direction and gaz channels. If required, third (and fourth) channel could be measured with pulseIn() function. But, worst than inaccurate, they are blocking functions. Without enter details, the sampling frequency will decrease, divided by 4 in worst case. You can also use a Mega which offer 6 interrupts. One time we have an accurate acquisition, we can directly send pulse duration through the serial interface, but we need an accurate serial transmission.

I also must remind that the serial link between the AT328 and the serial-usb bridge doesn’t include hardware flow control. It means we cannot just send values each time we go in the loop. A buffer overflow and a byte loss will occur. We have to implement a software flow control : when value sent, wait a little time (10µS), then wait for an acknowledgment before sending another value. Decreasing speed will also increase accuracy, 9600bps is enough. A single byte handle a [-100,100] range value. With 2 channels, we have to send 2 bytes. At 9600bps, we can send 9600/8/2 = 600 samples each seconds, whereas the receiver only outputs 50 samples per second. Even best radios will only outputs 100 samples per second.

Well done ! Using Processing.org with the Serial library, we can receive, acknowledge, compute and display data using a bar graph :

This video was recorded and published in september 2011, with a poor Hobbyking radio. After checked its output, I understood why it was so hard to have a reliable setup. I quickly changed for a Futaba one ;)

Arduino sketch :

// convenient macro to fast check pin state
#define intDir (PIND & 0b00000100)
#define intGaz (PIND & 0b00001000)

unsigned long dPulse = 0;
unsigned long gPulse = 0;
long tdir = 0;
long tgaz = 0;

void setup() {
  // Init serial and wait for a remote client
  Serial.begin(115200);
  while (Serial.available() == 0) {
  }
  while (Serial.read() != '*') {
  }

  // attach interrupts, called to both raising and falling edge
  // see http://arduino.cc/en/Reference/AttachInterrupt for int/pin mapping
  attachInterrupt(0, dirChange, CHANGE);
  attachInterrupt(1, gazChange, CHANGE);
}

// int.0 (D2) : channel 1 (direction)
void dirChange() {
  // timestamp
  long t = micros();

  // was a raising edge, store timestamp
  if (intDir) {
    tdir = t;
  }
  // was a falling edge, compute and filter littles changes
  else {
    long delta = t - tdir;
    if ((delta - dPulse) > 4)
      dPulse = delta;
  }
}

// int.1 (D3) : channel 2 (gaz)
void gazChange() {
  // timestamp
  long t = micros();

  // was a raising edge, store timestamp
  if (intGaz) {
    tgaz = t;
  }
  // was a falling edge, compute and filter littles changes
  else  {
    long delta = t - tgaz;
    if ((delta - gPulse) > 4)
      gPulse = delta;
  }
}

void loop() {
  // write direction pulse duration then gaz pulse duration
  Serial.print(dPulse);
  Serial.print(" ");
  Serial.println(gPulse);
  delay(10);
  // wait for an acknowledgment
  while (Serial.read() != '*') {
  }
}

Processing sketch :

import processing.serial.*;

Serial myPort;
String s;

float dirMin = 1020.0;
float dirMax = 2020.0;
float gazMin = 1020.0;
float gazMax = 2020.0;

float dir = 1520.0;
float gaz = 1520.0;

PFont myFont;

void setup() 
{
  // init display and font
  size(500, 200);
  myFont = createFont("Verdana", 32);
  textFont(myFont);

  // init serial
  String portName = "/dev/tty.usbserial-A700dCO3";
  myPort = new Serial(this, portName, 115200);

  while (myPort.available () == 0) 
    myPort.write("*");
}

void draw()
{
  // scale pulses to [-100%, +100%]
  float dirRate = (dir-dirMin)/(dirMax-dirMin);
  float gazRate = (gaz-gazMin)/(gazMax-gazMin);

  // scale pulses to [-45°, +45°]
  float dirAngle = (dir - 1520.0) * 90.0 / 800.0;
  float gazAngle = (gaz - 1520.0) * 90.0 / 800.0;

  background(255);

  // draw bars frame
  fill(255);
  rect(10, 40, 480, 40);
  rect(10, 120, 480, 40);

  // print values
  fill(0);
  text("Direction : " + nf(dirAngle, 1, 2) + "° " + (int)dir + "µS", 10, 30);
  text("Speed : " + nf(gazAngle, 1, 2) + "° " + (int)gaz + "µS", 10, 110);

  // fill bars according 
  rect(10, 40, dirRate*480.0, 40);
  rect(10, 120, gazRate*480.0, 40);

  // check if data present
  if (myPort.available() > 0) {
    s = myPort.readStringUntil('\n');
  }

  // loop back if no data received
  if (s == null)
    return;

  // else acknowledge data
  myPort.write("*");

  // check data format validity
  int i = s.indexOf(' ');
  if (i < 0)
    return;

  String s1 = s.substring(0, i);
  String s2 = s.substring(i+1, s.length() - 1);

  // parse data
  dir = Float.parseFloat(s1);
  gaz = Float.parseFloat(s2);
}

In those sketches, we use plain text representation for signal values through the serial interface. It helps debugging the system. In the second part, I will explain how to change the serial bridge firmware and the Arduino sketch to make it as an HID Joystick and how to optimize data transfert by sending a data structure to the USB bridge.