Arduino – 3chanel IR remote control from mini helicopter – processing


The other day I got this from my “local” toys store (Hamleys, on Regent Street in London):

Syma_s026_MiniHeli

After some frustration with controlling it (yes, even these supposedly “simple” ones are still not so obvious to master…) I started to be more and more intrigued by its remote control:

Syma_s026_IRremote

It’s a 3 channels, 3 selectable frequencies IR one.

I’ve been planning for soooo long to add some sort of remote controlling abilities to my rover (see the corresponding post here) and the “TV remote control” type as I did for my LEGO truck was pretty frustrating : you had to keep a button pressed to increase the speed, etc. … Now I want something progressive and smooth, like any “proper” RC toy… like the remote for this new mini helicopter…

Step 1: IR receiver compatibility

See if the IR receiver module that I have (used for the other projects, the most basic one that you can find, 38kHz bought from Radioshack while I was still in the US…) does manage to receive the IR signals from the new remote (frequencies close enough)

YES, lucky me…

I used my Freeduino and this program that I found onthe Arduino website and I was getting nice signals back …


#define TIMER_RESET  TCNT1 = 0

#define SAMPLE_SIZE  80

#define SIGNAL_SIZE 28

int IRpin = 7;

unsigned int TimerValue[SAMPLE_SIZE];

unsigned int SignalValue[SIGNAL_SIZE];

char direction[SAMPLE_SIZE];

byte change_count;

long time;

void setup() {

Serial.begin(115200);

Serial.println("Analyze IR Remote");

TCCR1A = 0x00;          // COM1A1=0, COM1A0=0 =Disconnect Pin OC1 from Timer/Counter 1 -- PWM11=0,PWM10=0 = PWM Operation disabled

// ICNC1=0 = Capture Noise Canceler disabled -- ICES1=0 =Input Capture Edge Select (not used) -- CTC1=0 =Clear Timer/Counter 1 on Compare/Match

// CS12=0 CS11=1 CS10=1 =Set prescaler to clock/64

TCCR1B = 0x03;          // 16MHz clock with prescaler means TCNT1 increments every 4uS

// ICIE1=0 = Timer/Counter 1, Input Capture Interrupt Enable -- OCIE1A=0 =Output Compare A Match Interrupt Enable -- OCIE1B=0 =Output Compare B Match Interrupt Enable

// TOIE1=0 =Timer 1 Overflow Interrupt Enable

TIMSK1 = 0x00;

pinMode(IRpin, INPUT);

}

void loop()

{

Serial.println("Waiting...");

change_count = 0;

while(digitalRead(IRpin) == HIGH) {}

TIMER_RESET;

TimerValue[change_count] = TCNT1;

direction[change_count++] = '0';

while (change_count < SAMPLE_SIZE) {

if (direction[change_count-1] == '0') {

while(digitalRead(IRpin) == LOW) {}

TimerValue[change_count] = TCNT1;

direction[change_count++] = '1';

} else {

while(digitalRead(IRpin) == HIGH) {}

TimerValue[change_count] = TCNT1;

direction[change_count++] = '0';

}

}

Serial.println("Bit stream detected!");

change_count = 1;

time = (long) TimerValue[change_count] * 4;

Serial.print(time);

Serial.print("\t");

Serial.println(direction[change_count++]);

while (change_count < SAMPLE_SIZE) {

if(direction[change_count] == '1'){

time = (TimerValue[change_count] - TimerValue[change_count-1]) * 4;

Serial.println(time);

}

change_count ++;

}

Serial.println("Bit stream end!");

delay(2000);

}

Step 2: What to do with those signals…

The most complex part, it took me almost an entire Sunday…

Try to do some graphs (thanks Gnuplot, much better than Excel !) and figure out what the used protocol is.

Signals_GnuPlot

Signals_GnuPlot2

So now, from this and the raw data I know that:

1) there are 300μs HIGH pulses that separate the LOW ones, of variable length -> the low ones must encode the data, the other are just separators

2) I found 3 different lengths of LOW pulses, and here are their meaning (from the data and my “feeling”):

– around 500μs = binary 0

– around 900μs = binary 1

– around 1600μs = start of encoding

3) The pulses come in groups of 29 (in between the 1600μs ones, that serve as delimiters)

Soo good news for now, it’s digital, at some point I was afraid it was analogic, much trickier to dechipher…

Interesting note: when you point the remote not directly to the receiver but to a wall or something, the length of the pulses becomes longer.

I initially thought about something like the Doppler effect, but that would be if I was to run away very quickly from the receiver…:)  so I think it’s simply some interference or noise…

So make the range for 0 and 1 quite large. For example, I initially used 450 – 550μs which was enough if pointing the remote directly at the receiver, but I had to modify it to 400-600μs to be really usable…

Step 3 : This isn’t enough

I’m getting there, but plotting this pulses is not enough I need something more dynamic, to see how they evolve in response to me moving the sticks on the remote.

Here comes Processing and the following sketch:


import processing.serial.*;

Serial myPort;

int[] mPulses = new int[29];

static int PULSE_WIDTH = 30;

void setup() {

size(1000, 600);

// List all the available serial ports:

println(Serial.list());

String portName = Serial.list()[1];

myPort = new Serial(this, portName, 115200);

}

void draw() {

background(0);

stroke(255);

for(int i=0; i<29; i++){

rect(i*(PULSE_WIDTH + 3), 0, PULSE_WIDTH, mPulses[i] - 400);

}

}

void serialEvent(Serial myPort) {

String oneLine = myPort.readStringUntil('\n');

if(oneLine != null){

String[] lineElements = oneLine.split("\t");

//println(lineElements);

if(lineElements.length == 30){

for(int i=0; i<29; i++){

mPulses[i] = Integer.valueOf(lineElements[i]);

}

println("OK " + asInt(mPulses, 0, 6) + " - " + asInt(mPulses, 7, 12));

}

}

}

private int asInt(int[] pulses, int start, int end){

StringBuilder strB = new StringBuilder();

for(int i=start; i<end; i++){

strB.append(getBit(pulses[i]));

}

return Integer.parseInt(strB.toString(), 2);

}

private int getBit(int pulse){

if(pulse > 450 && pulse < 550) return 0;

if(pulse > 850 && pulse < 950) return 1;

return -1;

}

Now at the other end of the serial cable I need Arduino to provide real time data, so I can see the signal diagram evolving.

However I’m not very happy with the code previously used (too complex for me, I still don’t fully understand what TCCR1A, TCCR1B, TIMSK1 and all these “obscure”, timer related parameters are) so here comes the my simplified version (oh so much more elegant…):


const int IRpin = 7;

#define SIGNAL_SIZE 29

unsigned int SignalValues[SIGNAL_SIZE];

void setup(){

Serial.begin(115200);

pinMode(IRpin, INPUT);

}

void loop(){

while(pulseIn(IRpin, LOW)  < 1600){

}

for(int i=0; i < SIGNAL_SIZE; i++){

SignalValues[i]=pulseIn(IRpin, LOW);

}

unsigned int last = pulseIn(IRpin, LOW);

if(last < 1600){

Serial.print(last);

Serial.println("   PROBLEM");

}else{

//Serial.println("New Data:");

for(int i=0; i < SIGNAL_SIZE; i++){

Serial.print(SignalValues[i]);

Serial.print("\t");

}

Serial.println();

}

}

And here are the results (these bars were moving in real time):
Signals_Processing

So now, while seeing the bars (shortone means a 0, long one 1) moving while I play with the sticks on the remonte I infer the following:

1) the first 7 bars ONLY move when I move the gas

2) the next 6 bars ONLY move when I move the left-right stick

3) it’s a little bit trickier for the forward-backward stick, it seems to move more than the last ones, and I don’t get why still 16 remaining… too much information… but for now, the 7 + 6 bits seems to be pretty accurate, so I will be able to use 2 of the channels at least.

Here is the console output of the Processing program:


Stable Library

=========================================

Native lib Version = RXTX-2.1-7

Java lib Version   = RXTX-2.1-7

[0] "COM3"

[1] "COM5"

OK 7 - 16

OK 17 - 16

OK 21 - 16

OK 22 - 16

OK 19 - 16

OK 30 - 16

OK 15 - 16

OK 0 - 16

OK 0 - 16

OK 6 - 16

OK 7 - 16

OK 10 - 16

OK 19 - 16

OK 4 - 16

OK 0 - 16

OK 11 - 16

OK 15 - 16

OK 14 - 16

OK 14 - 16

OK 0 - 16

OK 0 - 16

OK 18 - 16

Which confirms that my “theories”, and shows that the GAS is variable between 0 and 55 and the left-right from 0 to 31 (16 in the middle).

Now I’ll have to integrate this with the Rover… to be followed in that post…

OK 14 – 16
OK 14 – 16
OK 0 – 16
OK 0 – 16
OK 18 – 16
OK 17 – 16
OK 18 – 16
OK 18 – 16
OK 17 – 16
OK 17 – 16
OK 20 – 16
OK 53 – 16
OK 53 – 16
OK 53 – 16
OK 53 – 16
OK 53 – 16
OK 53 – 16

6 Responses to Arduino – 3chanel IR remote control from mini helicopter – processing

  1. Jonathan Johnson says:

    Hi. I realize this is an old post but wondering if you can help me. I’m working w/ my son to build a simple auto-pilot to control this exact helicopter (well, the one I ordered has a gyroscope). In the body of the copter we’ll add an arduino Nano, tilt-compensated compass, ultrasonic distance sensor, and an IR transmitter (all powered by a very small 12v battery) — I’m thinking w/ two motors it should be able to lift all of this.

    I’ve found several posts that have code for the Syma S017G copter (http://www.jimhung.co.uk/?p=1138). Do you know if the IR code is the same for this model? Do you still have the arduino compatible code for this model?

    Thanks for any help/advice!!

  2. Pingback: ATtiny Hacks: Infrared guidance and navigation « FOOTBALL, SEX & ALCOHOL

  3. Pingback: ATtiny Hacks: Infrared guidance and navigation | You've been blogged!

  4. Pingback: ATtiny Hacks: Infrared guidance and navigation - Hack a Day

  5. Pingback: IR Remote (Syma s026) dedicated board « Robotics / Electronics / Physical Computing

Leave a comment