Monday, April 9, 2012

Tutorial: how to control an IR helicopter programmatically with Arduino, Android, Kinect, brain, and more!

For those of you who are not familiar with an infrared helicopter, please search "SYMA S107" on eBay or on YouTube. It's a little indoor coaxial helicopter around 20 USD, recharged through USB cable.




The original remote control that comes with the helicopter is pretty good, at least a lot easier to use than a touchscreen. 



However, our passion doesn't end here with mere manual control. Inspired by all those robodance projects, we need more joy of robotic automation. 

Let's hack the SYMA S107 helicopter to give it some artificial intelligence! 

You might wonder why this particular model (Syma S107)? Well, simply because it is perhaps the most popular one in the IR helicopter market. And many brilliant hackers have already done the work for us. We don't have to reinvent the wheel to hack it again.

As usual, let's start with an Arduino, the most simple way to prototype electronic projects. Our goal here is to make an arduino based IR transmitter to programmatically control the helicopter. 

Step 1. Prepare the circuit 


The circuit is extremely simple, we just need:

1 Arduino, 1 IR LED and 1 resister of 200 to 1000 ohm.

Optional: an IR receiver (for testing).



That's all to transmit IR signal from Arduino to the helicopter. If you already have an Arduino board, it costs you almost nothing to get the rest parts.

If you are interested in IR tests, here is a detailed tutorial: http://www.ladyada.net/learn/sensors/ir.html

Otherwise, we can directly load the Arduino code and control the helicopter!

Step 2. Program the Arduino

Fortunately, there are several existing Arduino projects we can directly use and customize. 

The first working code (tested):
//Arduino code to control a helicotper.
int IRledPin =  12;    
int incomingByte = 0;
String incomingString;
int pulseValues[33];
int pulseLength = 0;

void setup()   {                
  // initialize the IR digital pin as an output:
  pinMode(IRledPin, OUTPUT);      
  pinMode(13, OUTPUT);   
  Serial.begin(9600);

  for (int i=0; i < 13; i++) 
    pulseValues[i] = 0;


}

void loop()                     
{
  SendCode();
}


void pulseIR(long microsecs) {
  cli();  // this turns off any background interrupts

  while (microsecs > 0) {
    // 38 kHz is about 13 microseconds high and 13 microseconds low
    digitalWrite(IRledPin, HIGH);  // this takes about 3 microseconds to happen
    delayMicroseconds(10);         // hang out for 10 microseconds
    digitalWrite(IRledPin, LOW);   // this also takes about 3 microseconds
    delayMicroseconds(10);         // hang out for 10 microseconds

    // so 26 microseconds altogether
    microsecs -= 26;

  }

  sei();  // this turns them back on
}

void Zero()
{  
  pulseIR(300);
  delayMicroseconds(300);
  pulseLength += 600;
}

void One()
{
  pulseIR(300);
  delayMicroseconds(600); 
  pulseLength += 900;
}

void sendPulseValue(int pulseValue)
{
  if (pulseValue == 1)
    One();
  else
    Zero(); 
}

void checkPulseChanges()
{
  if (Serial.available() > 0)
  {
    incomingByte = Serial.read();

    //Pulse 1
    if (incomingByte == 'a')
      pulseValues[0] = 0;
    if (incomingByte == 'A')
      pulseValues[0] = 1;

    //Pulse 2
    if (incomingByte == 'b')
      pulseValues[1] = 0;
    if (incomingByte =='B')
      pulseValues[1] = 1;

    //Pulse 3
    if (incomingByte == 'c')
      pulseValues[2] = 0;
    if (incomingByte == 'C')
      pulseValues[2] = 1;

    //Pulse 4
    if (incomingByte == 'd')
      pulseValues[3] = 0;
    if (incomingByte == 'D')
      pulseValues[3] = 1;

    //Pulse 5
    if (incomingByte == 'e')
      pulseValues[4] = 0;
    if (incomingByte == 'E')
      pulseValues[4] = 1;

    //Pulse 6
    if (incomingByte == 'f')
      pulseValues[5] = 0;
    if (incomingByte == 'F')
      pulseValues[5] = 1;

    //Pulse 7
    if (incomingByte == 'g')
      pulseValues[6] = 0;
    if (incomingByte == 'G')
      pulseValues[6] = 1;    

    //Pulse 8
    if (incomingByte == 'h')
      pulseValues[7] = 0;
    if (incomingByte == 'H')
      pulseValues[7] = 1;

    //Pulse 9
    if (incomingByte == 'i')
      pulseValues[8] = 0;
    if (incomingByte == 'I')
      pulseValues[8] = 1;

    //Pulse 10
    if (incomingByte == 'j')
      pulseValues[9] = 0;
    if (incomingByte == 'J')
      pulseValues[9] = 1;

    //Pulse 11
    if (incomingByte == 'k')
      pulseValues[10] = 0;
    if (incomingByte == 'K')
      pulseValues[10] = 1;

    //Pulse 12
    if (incomingByte == 'l')
      pulseValues[11] = 0;
    if (incomingByte == 'L')
      pulseValues[11] = 1;

    //Pulse 13
    if (incomingByte == 'm')
      pulseValues[12] = 0;
    if (incomingByte == 'M')
      pulseValues[12] = 1;

    //Pulse 14
    if (incomingByte == 'o')
      pulseValues[13] = 0;
    if (incomingByte == 'O')
      pulseValues[13] = 1;

    //Pulse 15
    if (incomingByte == 'p')
      pulseValues[14] = 0;
    if (incomingByte == 'P')
      pulseValues[14] = 1;

    //Pulse 16
    if (incomingByte == 'q')
      pulseValues[15] = 0;
    if (incomingByte == 'Q')
      pulseValues[15] = 1;

    //Pulse 17
    if (incomingByte == 'r')
      pulseValues[16] = 0;
    if (incomingByte == 'R')
      pulseValues[16] = 1;

    //Pulse 18
    if (incomingByte == 's')
      pulseValues[17] = 0;
    if (incomingByte == 'S')
      pulseValues[17] = 1;

    //Pulse 19
    if (incomingByte == 't')
      pulseValues[18] = 0;
    if (incomingByte == 'T')
      pulseValues[18] = 1;

    //Pulse 20
    if (incomingByte == 'u')
      pulseValues[19] = 0;
    if (incomingByte == 'U')
      pulseValues[19] = 1;

    //Pulse 21
    if (incomingByte == 'v')
      pulseValues[20] = 0;
    if (incomingByte == 'V')
      pulseValues[20] = 1;

    //Pulse 22
    if (incomingByte == 'w')
      pulseValues[21] = 0;
    if (incomingByte == 'W')
      pulseValues[21] = 1;

    //Pulse 23
    if (incomingByte == 'x')
      pulseValues[22] = 0;
    if (incomingByte == 'X')
      pulseValues[22] = 1;

    //Pulse 24
    if (incomingByte == 'y')
      pulseValues[23] = 0;
    if (incomingByte == 'Y')
      pulseValues[23] = 1;

    //Pulse 25
    if (incomingByte == 'z')
      pulseValues[24] = 0;
    if (incomingByte == 'Z')
      pulseValues[24] = 1;

    //Pulse 26
    if (incomingByte == '1')
      pulseValues[25] = 0;
    if (incomingByte == '2')
      pulseValues[25] = 1;

    //Pulse 27
    if (incomingByte == '3')
      pulseValues[26] = 0;
    if (incomingByte == '4')
      pulseValues[26] = 1;

    //Pulse 28
    if (incomingByte == '5')
      pulseValues[27] = 0;
    if (incomingByte == '6')
      pulseValues[27] = 1;

    //Pulse 29
    if (incomingByte == '7')
      pulseValues[28] = 0;
    if (incomingByte == '8')
      pulseValues[28] = 1;

    //Pulse 30
    if (incomingByte == '9')
      pulseValues[29] = 0;
    if (incomingByte == '!')
      pulseValues[29] = 1;

    //Pulse 31
    if (incomingByte == '@')
      pulseValues[30] = 0;
    if (incomingByte == '#')
      pulseValues[30] = 1;

    //Pulse 32
    if (incomingByte == '$')
      pulseValues[31] = 0;
    if (incomingByte == '%')
      pulseValues[31] = 1;

    //Pulse 33
    if (incomingByte == '^')
      pulseValues[32] = 0;
    if (incomingByte == '&')
      pulseValues[32] = 1;      
  }

}

void SendCode() {

  while (true)
  {
    checkPulseChanges();

    pulseIR(4000);
    delayMicroseconds(2000);
    pulseLength=6000;

    sendPulseValue(pulseValues[0]);
    sendPulseValue(pulseValues[1]);
    sendPulseValue(pulseValues[2]);
    sendPulseValue(pulseValues[3]);
    sendPulseValue(pulseValues[4]);
    sendPulseValue(pulseValues[5]);
    sendPulseValue(pulseValues[6]);
    sendPulseValue(pulseValues[7]);
    sendPulseValue(pulseValues[8]);
    sendPulseValue(pulseValues[9]);
    sendPulseValue(pulseValues[10]);
    sendPulseValue(pulseValues[11]);
    sendPulseValue(pulseValues[12]);
    sendPulseValue(pulseValues[13]);
    sendPulseValue(pulseValues[14]);
    sendPulseValue(pulseValues[15]);
    sendPulseValue(pulseValues[16]);
    sendPulseValue(pulseValues[17]);
    sendPulseValue(pulseValues[18]);
    sendPulseValue(pulseValues[19]);
    sendPulseValue(pulseValues[20]);
    sendPulseValue(pulseValues[21]);
    sendPulseValue(pulseValues[22]);
    sendPulseValue(pulseValues[23]);
    sendPulseValue(pulseValues[24]);
    sendPulseValue(pulseValues[25]);
    sendPulseValue(pulseValues[26]);
    sendPulseValue(pulseValues[27]);
    sendPulseValue(pulseValues[28]);
    sendPulseValue(pulseValues[29]);
    sendPulseValue(pulseValues[30]);
    sendPulseValue(pulseValues[31]);

    //Footer
    pulseIR(360); 
    delayMicroseconds( (28600 - pulseLength) ); 
   } 
}

This is the processing code to control the helicopter using the PC's camera and mouse wheel:
 
import processing.serial.*; 
import controlP5.*;
import JMyron.*;

JMyron m;

ControlP5 controlP5;
CheckBox checkbox;
Button b;

float boxX;
float boxY;
int boxSize = 20;
boolean mouseOverBox = false;
byte[] previousFlags = new byte[32];
byte[] flagsToSend = new byte[32];
Serial port; 
String outString;

int helicopterUpSpeed = 0;
int helicopterPitch = 63;
int helicopterYaw = 68;

void setup() {
  m = new JMyron();
  m.start(640,480);
  size(640, 480);

  controlP5 = new ControlP5(this);
  checkbox = controlP5.addCheckBox("checkBox", 20, 20);  
  // make adjustments to the layout of a checkbox.
  checkbox.setColorForeground(color(120));
  checkbox.setColorActive(color(255));
  checkbox.setColorLabel(color(128));
  checkbox.setItemsPerRow(8);
  checkbox.setSpacingColumn(30);
  checkbox.setSpacingRow(10);
  // add items to a checkbox.
  checkbox.addItem("1", 0);
  checkbox.addItem("2", 0);
  checkbox.addItem("3", 0);
  checkbox.addItem("4", 0);
  checkbox.addItem("5", 0);
  checkbox.addItem("6", 0);
  checkbox.addItem("7", 0);
  checkbox.addItem("8", 0);
  checkbox.addItem("9", 0);
  checkbox.addItem("10", 0);
  checkbox.addItem("11", 0);
  checkbox.addItem("12", 0);
  checkbox.addItem("13", 0);
  checkbox.addItem("14", 0);
  checkbox.addItem("15", 0);
  checkbox.addItem("16", 0);
  checkbox.addItem("17", 0);
  checkbox.addItem("18", 0);
  checkbox.addItem("19", 0);
  checkbox.addItem("20", 0);
  checkbox.addItem("21", 0);
  checkbox.addItem("22", 0);
  checkbox.addItem("23", 0);
  checkbox.addItem("24", 0);
  checkbox.addItem("25", 0);
  checkbox.addItem("26", 0);
  checkbox.addItem("27", 0);
  checkbox.addItem("28", 0);
  checkbox.addItem("29", 0);
  checkbox.addItem("30", 0);
  checkbox.addItem("31", 0);
  checkbox.addItem("32", 0);

  checkbox.deactivateAll();


  controlP5.addButton("Up", 0, 120, 120, 35, 20);
  controlP5.addButton("Down", 0, 120, 160, 35, 20);
  
  controlP5.addButton("Forward", 0, 180, 120, 45, 20);
  controlP5.addButton("Backward", 0, 180, 160, 45, 20);  
  
  controlP5.addButton("TurnLeft", 0, 60, 120, 40, 20);
  controlP5.addButton("TurnRight", 0, 60, 160, 40, 20);  
  

  port = new Serial(this, Serial.list()[0], 9600);

  for (int i=0;i<32;i++) 
  {
    flagsToSend[i] = 0;
    previousFlags[i] = 0;
  }

  addMouseWheelListener(new java.awt.event.MouseWheelListener() { 
    public void mouseWheelMoved(java.awt.event.MouseWheelEvent evt) { 
      mouseWheel(evt.getWheelRotation());
    }
  }
  ); 

  startSetUp();
}

String addForwardZeroesTT(String inputString, int totalLength)
{

  String outString = "";
  for (int i = 0; i < (totalLength - inputString.length()); i++)
    outString += "0";

  outString = outString + inputString;

  return outString;
}


//Incremental like bits
//0000, 0001, 0010, 0011, 0100, etc
void Up()
{
  String currentSpeed = addForwardZeroesTT(binary(helicopterUpSpeed), 7);

  if(helicopterUpSpeed <= 125)
    helicopterUpSpeed += 1;

  String newSpeed = addForwardZeroesTT(binary(helicopterUpSpeed), 7);

  setNewSpeed(currentSpeed, newSpeed);
}

void Down()
{
  String currentSpeed = addForwardZeroesTT(binary(helicopterUpSpeed), 7);
  if (helicopterUpSpeed > 0)
    helicopterUpSpeed -= 1;
  String newSpeed = addForwardZeroesTT(binary(helicopterUpSpeed), 7);

  setNewSpeed(currentSpeed, newSpeed);
} 


void Backward()
{
  String currentSpeed = addForwardZeroesTT(binary(helicopterPitch), 7);

  helicopterPitch += 1;

  String newSpeed = addForwardZeroesTT(binary(helicopterPitch), 7);

   setNewPitch(currentSpeed, newSpeed);
}

void Forward()
{
  String currentSpeed = addForwardZeroesTT(binary(helicopterPitch), 7);

  helicopterPitch -= 1;

  String newSpeed = addForwardZeroesTT(binary(helicopterPitch), 7);

   setNewPitch(currentSpeed, newSpeed);
}

void TurnLeft()
{
  String currentSpeed = addForwardZeroesTT(binary(helicopterYaw), 7);

  helicopterYaw -= 1;

  String newSpeed = addForwardZeroesTT(binary(helicopterYaw), 7);

  setNewYaw(currentSpeed, newSpeed);
}

void TurnRight()
{
  String currentSpeed = addForwardZeroesTT(binary(helicopterYaw), 7);

  helicopterYaw += 1;

  String newSpeed = addForwardZeroesTT(binary(helicopterYaw), 7);

  setNewYaw(currentSpeed, newSpeed);
}


void setNewSpeed(String currentSpeed, String newSpeed)
{

  //Compare each bit and see if it needs changing.
  if  (newSpeed.charAt(6) != currentSpeed.charAt(6) )
    checkbox.toggle(23);

  if  (newSpeed.charAt(5) != currentSpeed.charAt(5) )
    checkbox.toggle(22);

  if  (newSpeed.charAt(4) != currentSpeed.charAt(4) )
    checkbox.toggle(21);    

  if  (newSpeed.charAt(3) != currentSpeed.charAt(3) )
    checkbox.toggle(20);    

  if  (newSpeed.charAt(2) != currentSpeed.charAt(2) )
    checkbox.toggle(19);    

  if  (newSpeed.charAt(1) != currentSpeed.charAt(1) )
    checkbox.toggle(18);    

  if  (newSpeed.charAt(0) != currentSpeed.charAt(0) )
    checkbox.toggle(17);
}

void setNewPitch(String currentSpeed, String newSpeed)
{
  if  (newSpeed.charAt(6) != currentSpeed.charAt(6) )
    checkbox.toggle(15);

  if  (newSpeed.charAt(5) != currentSpeed.charAt(5) )
    checkbox.toggle(14);

  if  (newSpeed.charAt(4) != currentSpeed.charAt(4) )
    checkbox.toggle(13);    

  if  (newSpeed.charAt(3) != currentSpeed.charAt(3) )
    checkbox.toggle(12);    

  if  (newSpeed.charAt(2) != currentSpeed.charAt(2) )
    checkbox.toggle(11);    

  if  (newSpeed.charAt(1) != currentSpeed.charAt(1) )
    checkbox.toggle(10);    

  if  (newSpeed.charAt(0) != currentSpeed.charAt(0) )
    checkbox.toggle(9);
}

void setNewYaw(String currentSpeed, String newSpeed)
{
  if  (newSpeed.charAt(6) != currentSpeed.charAt(6) )
    checkbox.toggle(7);

  if  (newSpeed.charAt(5) != currentSpeed.charAt(5) )
    checkbox.toggle(6);

  if  (newSpeed.charAt(4) != currentSpeed.charAt(4) )
    checkbox.toggle(5);    

  if  (newSpeed.charAt(3) != currentSpeed.charAt(3) )
    checkbox.toggle(4);    

  if  (newSpeed.charAt(2) != currentSpeed.charAt(2) )
    checkbox.toggle(3);    

  if  (newSpeed.charAt(1) != currentSpeed.charAt(1) )
    checkbox.toggle(2);    

  if  (newSpeed.charAt(0) != currentSpeed.charAt(0) )
    checkbox.toggle(1);
}

void startSetUp()
{
  //First clear the arduino.
  port.write('a'); 
  port.write('b'); 
  port.write('c'); 
  port.write('d'); 
  port.write('e'); 
  port.write('f'); 
  port.write('g'); 
  port.write('h'); 
  port.write('i'); 
  port.write('j'); 
  port.write('k'); 
  port.write('l');
  port.write('m'); 
  port.write('o'); 
  port.write('p'); 
  port.write('q');
  port.write('r'); 
  port.write('s'); 
  port.write('t'); 
  port.write('u');
  port.write('v'); 
  port.write('w'); 
  port.write('x'); 
  port.write('y'); 
  port.write('z'); 
  port.write('1'); 
  port.write('3'); 
  port.write('5');
  port.write('7'); 
  port.write('9'); 
  port.write('@'); 
  port.write('$'); 
  port.write('^');


  //Set the pulse to the basic configuration.
  checkbox.toggle(1);
  checkbox.toggle(6);


  checkbox.toggle(10);
  checkbox.toggle(11);

  checkbox.toggle(12);
  checkbox.toggle(13);
  checkbox.toggle(14);
  checkbox.toggle(15);
  checkbox.toggle(16);

  checkbox.toggle(25);

  checkbox.toggle(28);
  checkbox.toggle(29);
  checkbox.toggle(30);
}


void draw() 
{ 
  background(200);
  
  m.update();
  int[] img = m.image();
  
  //first draw the camera view onto the screen
  loadPixels();
  
  for(int i=0;i<640*480;i++){
      pixels[i] = img[i];
  }
  updatePixels();
   noFill();
  int[][] a;
   
  CheckHelicopterPosition();  
  
  
  text(" Current Speed: " + helicopterUpSpeed, 230, 135);
  text(" Pitch: " + helicopterPitch, 230, 165);
  text(" Yaw: " + helicopterYaw, 230, 195);  
}


void CheckHelicopterPosition()
{
  
  noFill();
  int[][] a;
  
  m.trackColor(255,255,0,255);
  //draw bounding boxes of globs
  a = m.globBoxes();
  stroke(255,0,0);
  
  int averageY = 0;
  
  for(int i=0;i<a.length;i++){
    int[] b = a[i];
    rect(b[0], b[1], b[2], b[3]);
    
    averageY += b[1];
    
  }
  
  if (a.length > 0)
  {
  averageY = averageY / a.length;
  line(0,averageY,640,averageY);
  
    text(" Average Y: " + averageY, 230, 215);  
    
    if (averageY > 240)
     {
      text(" Action: up ", 350, 20);
      delay(150);
      //Up();
     } 
     else
     {
      text(" Action down ", 350,20); 
      //delay(250);
      //Down();
     }
  }  

  
}  
  
  
  
void controlEvent(ControlEvent theEvent) {
  if (theEvent.isGroup()) {

    for (int i=0;i<theEvent.group().arrayValue().length;i++) 
    {
      byte n = (byte)theEvent.group().arrayValue()[i];
      flagsToSend[i] = n;
      //there was a change in the flags, send the update.
      if (previousFlags[i] != flagsToSend[i])
      {
        println(i);

        if (i==0) {   
          if (n == 0) { 
            port.write('a');
          } 
          else { 
            port.write('A');
          }
        }
        if (i==1) {   
          if (n == 0) { 
            port.write('b');
          } 
          else { 
            port.write('B');
          }
        } 
        if (i==2) {   
          if (n == 0) { 
            port.write('c');
          } 
          else { 
            port.write('C');
          }
        }
        if (i==3) {   
          if (n == 0) { 
            port.write('d');
          } 
          else { 
            port.write('D');
          }
        }                 
        if (i==4) {   
          if (n == 0) { 
            port.write('e');
          } 
          else { 
            port.write('E');
          }
        }    
        if (i==5) {   
          if (n == 0) { 
            port.write('f');
          } 
          else { 
            port.write('F');
          }
        }  
        if (i==6) {   
          if (n == 0) { 
            port.write('g');
          } 
          else { 
            port.write('G');
          }
        }
        if (i==7) {   
          if (n == 0) { 
            port.write('h');
          } 
          else { 
            port.write('H');
          }
        }
        if (i==8) {   
          if (n == 0) { 
            port.write('i');
          } 
          else { 
            port.write('I');
          }
        }
        if (i==9) {   
          if (n == 0) { 
            port.write('j');
          } 
          else { 
            port.write('J');
          }
        }
        if (i==10) {   
          if (n == 0) { 
            port.write('k');
          } 
          else { 
            port.write('K');
          }
        }
        if (i==11) {   
          if (n == 0) { 
            port.write('l');
          } 
          else { 
            port.write('L');
          }
        }
        if (i==12) {   
          if (n == 0) { 
            port.write('m');
          } 
          else { 
            port.write('M');
          }
        }
        if (i==13) {   
          if (n == 0) { 
            port.write('o');
          } 
          else { 
            port.write('O');
          }
        }  
        if (i==14) {   
          if (n == 0) { 
            port.write('p');
          } 
          else { 
            port.write('P');
          }
        }
        if (i==15) {   
          if (n == 0) { 
            port.write('q');
          } 
          else { 
            port.write('Q');
          }
        }
        if (i==16) {   
          if (n == 0) { 
            port.write('r');
          } 
          else { 
            port.write('R');
          }
        }
        if (i==17) {   
          if (n == 0) { 
            port.write('s');
          } 
          else { 
            port.write('S');
          }
        }
        if (i==18) {   
          if (n == 0) { 
            port.write('t');
          } 
          else { 
            port.write('T');
          }
        }
        if (i==19) {   
          if (n == 0) { 
            port.write('u');
          } 
          else { 
            port.write('U');
          }
        }
        if (i==20) {   
          if (n == 0) { 
            port.write('v');
          } 
          else { 
            port.write('V');
          }
        }
        if (i==21) {   
          if (n == 0) { 
            port.write('w');
          } 
          else { 
            port.write('W');
          }
        }
        if (i==22) {   
          if (n == 0) { 
            port.write('x');
          } 
          else { 
            port.write('X');
          }
        }
        if (i==23) {   
          if (n == 0) { 
            port.write('y');
          } 
          else { 
            port.write('Y');
          }
        }
        if (i==24) {   
          if (n == 0) { 
            port.write('z');
          } 
          else { 
            port.write('Z');
          }
        }
        if (i==25) {   
          if (n == 0) { 
            port.write('1');
          } 
          else { 
            port.write('2');
          }
        }
        if (i==26) {   
          if (n == 0) { 
            port.write('3');
          } 
          else { 
            port.write('4');
          }
        }
        if (i==27) {   
          if (n == 0) { 
            port.write('5');
          } 
          else { 
            port.write('6');
          }
        }
        if (i==28) {   
          if (n == 0) { 
            port.write('7');
          } 
          else { 
            port.write('8');
          }
        }
        if (i==29) {   
          if (n == 0) { 
            port.write('9');
          } 
          else { 
            port.write('!');
          }
        }
        if (i==30) {   
          if (n == 0) { 
            port.write('@');
          } 
          else { 
            port.write('#');
          }
        }
        if (i==31) {   
          if (n == 0) { 
            port.write('$');
          } 
          else { 
            port.write('%');
          }
        }
        if (i==32) {   
          if (n == 0) { 
            port.write('^');
          } 
          else { 
            port.write('&');
          }
        }
      }

      previousFlags[i]=n;
    }
  }
}


void mouseWheel(int delta) {
  if (delta == 1)
    Down();
  else
    Up();
}
We have tested it. It worked like a charm. Exactly as the project creator says:

"Utilizing the input from the webcam, it adjusts the speed until the helicopter is in the middle of the screen.
If it goes too high, it lowers the speed. If it gets too low or is stopped, it slowly increases the upwards speed."



For more details on this project, please have a glance here: http://www.avergottini.com/2011/05/arduino-helicopter-infrared-controller.html


The second working code (not yet tested by DIY Phone Gadgets):


Of course, your SYMA S107 helicopter might not always be exactly the same as others'. The first code might not work for your helicopter. Don't worry, it is possible that you have a 3-channel (30-bit) version, which uses a different protocol. Just load the following Arduino code:
/* S107 3-channel with checksum helicopter control code
 * Copyright (C) 2012, Andrew Barry, Dan Barry
 *
 * Uses an Arduino to control a S107 helicopter
 *
 *
 * Instructions:
 *  Connect an IR LED array to pin 8 (using a FET to amplify the signal)
 *  and use the serial monitor to send commands to the system
 *
 */

#define LED 8

#define STATUS 13

//#define TAKEOFF_THROTTLE 240
//#define HOLDING_THROTTLE 130

byte yawCmd, pitchCmd, throttleCmd, trimCmd;

// Set this value for the default channel
// A = 0
// B = 1
// C = 2
byte channel = 0;

/*
 * Setup function that initializes the serial port and
 * sets some default values for the control variables.
 * Also sets up the pins we'll be using.
 */
void setup()
{
 Serial.begin(9600);
 pinMode(STATUS,OUTPUT);
 digitalWrite(STATUS,LOW);

 pinMode(LED,OUTPUT);
 digitalWrite(LED,LOW);

 yawCmd = 8;
 pitchCmd = 8;
 trimCmd = 0;
 throttleCmd = 0;

 Serial.println("throttle = 0, standing by for commands.");
}

/*
 * Function that does the actual work of converting commands into
 * IR LED pulses and changes the pins in the appropriate manner.
 */
byte sendPacket(byte yaw, byte pitch, byte throttle, byte trim)
{

 int packetData[100];
 int pulseNum;

 digitalWrite(STATUS,HIGH);

 float channelDelayValue = 136500;

 // channel A B or C
 // A is 10 with 136500us packet delay
 // B is 01 with 105200us packet delay
 // C is 11 with 168700us packet delay
 if (channel == 0)
 {
   packetData[0] = 1;
   packetData[1] = 0;
   channelDelayValue = 136500;

 } else if (channel == 1)
 {
   packetData[0] = 0;
   packetData[1] = 1;
   channelDelayValue = 105200;

 } else {
   packetData[0] = 1;
   packetData[1] = 1;
   channelDelayValue = 168700;

 }
 packetData[2] = 0;
 packetData[3] = 0;

 // pitch

 packetData[7] = (pitch & 0b1000) >> 3; // direction bit

 if (pitch < 8)  {    pitch = 8 - pitch;  }  packetData[6] = (pitch & 0b0100) >> 2; // others are speed bits, note that they are reversed
 packetData[5] = (pitch & 0b0010) >> 1;
 packetData[4] = (pitch & 0b0001);

 // throttle
 // bits are reversed in the throttle command
 packetData[15] =  (throttle & 0b10000000) >> 7;
 packetData[14] =  (throttle & 0b01000000) >> 6;
 packetData[13] = (throttle & 0b00100000) >> 5;
 packetData[12] = (throttle & 0b00010000) >> 4;

 packetData[11] = (throttle & 0b00001000) >> 3;
 packetData[10] = (throttle & 0b00000100) >> 2;
 packetData[9] = (throttle & 0b00000010) >> 1;
 packetData[8] = (throttle & 0b00000001);

 // yaw
 packetData[19] = (yaw & 0b1000) >> 3; // direction bit
 if (yaw < 8)  {    yaw = 8 - yaw;  }  packetData[18] = (yaw & 0b0100) >> 2;
 packetData[17] = (yaw & 0b0010) >> 1;
 packetData[16] = (yaw & 0b0001);

 // these 4 bits are the checksum, so make sure they
 // are 0s so they don't change the XOR later on
 packetData[20] = 0;
 packetData[21] = 0;
 packetData[22] = 0;
 packetData[23] = 0;

 // yaw trim / yaw adjust (the little dial on the controller)
 // 6 bits
 packetData[24] = 0;
 packetData[25] = 0;
 packetData[26] = 0;
 packetData[27] = 0;

 packetData[28] = 0;
 packetData[29] = 0;

 // these bits are never sent but we do the checksum
 // computation in 4-bit chunks, with the trailing two
 // bits set to zero, so we set them to zero here to make
 // the checksum a bit easier to compute
 packetData[30] = 0;
 packetData[31] = 0;

 int i;

 int checksum[10];
 checksum[0] = 0;
 checksum[1] = 0;
 checksum[2] = 0;
 checksum[3] = 0;

 // compute checksum -- bitwise XOR of 4-bit chunks
 // with two zeros padding the *end* of the last two bits
 for (i=0; i 0)
  {
    if (Serial.available() == true)
    {
      Serial.println("HOLD ABORTED");
      break;
    }

    packetDelay = sendPacket(yawIn, pitchIn, throttleIn, trimCmd);
    delayTime = delayTime - packetDelay;

    delay(packetDelay);

    delay(delayAmount);
    delayTime = delayTime - delayAmount;
  }
  Serial.println("Done holding.");
}

void Land()
{
 static int i;
 Serial.println("Landing");
 for(i=throttleCmd;i>0;i--){
   HoldCommand(8,8,throttleCmd,50);
 }
 throttleCmd = 0;
}

/*
 * Function that manages receiving data from the serial port.
 * Mostly changes the global variables that are passed to the
 * control functions.
 */
void serialEvent()
{
 char cmd = Serial.read();
 Serial.println();
 Serial.print("command received is ");
 Serial.println(cmd);

 switch (cmd)
 {
   // Take off with 't'
   case 't':
     Serial.println("Taking Off");

     // Yaw: 1-15
     //    8 = no turn
     //    1 = max right turn
     //    15 = max left turn
     //
     // Pitch: 1-15
     //    8 = no pitch
     //    15 = max forward
     //    1 = max backwards
     //
     // Throttle: 0-255
     //    0 = off
     //    ~130 = steady flight
     //    ~240 = fast climb

     // First, go up with lots of throttle for 650ms
     // yaw: 8 --> no yaw
     // pitch: 8 --> no pitch
     // throttle: 240 --> fast climb
     // delay: 650ms --> enough time to climb, not too long so won't hit ceiling

     // HoldCommand: a function that sends the same data for a given amount of time
     // HoldCommand(yaw, pitch, throttle, time-to-hold-in-ms);
     HoldCommand(8, 8, 240, 650);

     // set the *global* throttle to steady flight throttle
     throttleCmd = 130;
     break;

   // land with 'x' or 'q'
   case 'x':
   case 'q':
     Land();
     break;

   // throttle commands
   case '0':
   case '1':
   case '2':
   case '3':
   case '4':
   case '5':
   case '6':
   case '7':
   case '8':
   case '9':
     throttleCmd = atoi(&cmd) * 25;  //single character, so we can go from 0 to 255 by inputting 0 to 9 in the serial monitor
     break;

   // turn left
   case 'a':
     if (yawCmd < 15)      {        yawCmd ++;      }      Serial.print("Yaw is ");      Serial.println(yawCmd);      break;    // turn right    case 'd':      if (yawCmd > 1)
     {
       yawCmd --;
     }
     Serial.print("Yaw is ");
     Serial.println(yawCmd);
     break;

   // move forwards
   case 'w':
     if (pitchCmd < 15){        pitchCmd ++;  // moves forward      }      Serial.print("Pitch is ");      Serial.println(pitchCmd);      break;      // move backwards    case 's':      if (pitchCmd > 1)
     {
       pitchCmd --;  // moves backward
     }
     Serial.print("Pitch is ");
     Serial.println(pitchCmd);
     break;

   // increase throttle
   case 'u':
     if (throttleCmd < 255 - 6)      {        throttleCmd += 6;      }      Serial.print("Throttle is ");      Serial.println(throttleCmd);      break;        // decrease throttle    case 'j':      if (throttleCmd > 6)
     {
       throttleCmd -= 6;
     }
     Serial.print("Trottle is ");
     Serial.println(throttleCmd);
     break;

   // change channel
   case 'c':
     Serial.println("Changing channel");
     if (channel >= 2)
     {
       channel = 0;
     } else
     {
       channel ++;
     }
     Serial.print("Channel is: ");
     Serial.println(channel);
     break;

   // reset yaw and pitch
   case 'r':
     Serial.println("resetting yaw and pitch");
     yawCmd = 8;
     pitchCmd = 8;
     break;

   default:
     Serial.println("Unknown command");
 }
 Serial.print("Throttle is at ");
 Serial.println(throttleCmd);
}

/*
 * Loops continuously sending and delaying for the transmission
 */
void loop()

{
   // Note that serialEvent() gets called on each path of the loop
   // and runs if there is data at the serial port

   // we call delay here on the return value of sendPacket because that will
   // cause us to put the right amount of time between packets.  The delay is
   // not constant, but is instead based on how long the packet was
   // that we sent
   delay(sendPacket(yawCmd, pitchCmd, throttleCmd, trimCmd));
}


To use the code, open the Serial Monitor (Tools > Serial Monitor) and use the following commands:

0-9: throttle
w: forward
a: left
s: backwards
d: right
t: take off
u: increase throttle
j: decrease throttle
r: reset pitch and yaw

For more details on this project, please go to:
http://www.abarry.org/likelytobeforgotten/?p=55

What's next?


Now that we can successfully send programmatical commands from Arduino, we can take advantage of the solutions we have learned here  in DIY Phone Gadgets to control the IR helicopter using PCs, game consoles, Kinect, tablets, smartphones or whatever electronic gadgets.

Here is a project using Kinect:


Here is a project using brain (your mind) to control a helicopter:


Or a Nunchuk-Wiimote-controlled helicopter, if you need more accuracy:



How to decode IR signal (very useful if you don't have a SYMA S107):

Of course, you can always decode the IR signal from scratch, if your helicopter is not SYMA S107. Here is a great video tutorial:


Here is the project owner's original blog: http://technologyonmymind.blogspot.fr/2012/03/helicopter-auto-pilot-introduction.html

DIY Phone-controlled helicopters with Arduino:


Needless to say, using Arduino as a bridge, we can easily control helicopters. Here is "Yan's helicopter Controller" from DIY Phone Gadgets.

Here is how it works:

1. The Android phone is controlling the Arduino using bluetooth.
2. Arduino is controlling the original helicopter transmitter.
3. The transmitter is programmatically controlling the helicopter.











DIY Phone-controlled helicopters with audio dongle:


There are already some fantastic existing tools in the toy market that are really helpful. Like these audio jack dongles:




These dongles capture the audio signal in the 3.5 audio jack and translate the audio signal into wireless signal.

For normal users, these dongles are just wireless transmitters, compatible with Android or iPhone apps. They can use it to control the helicopter with their smartphones.

For DIYers, the dongles should be capable of transmitting IR or other wireless signals from any smartphone or tablet (Android, iPhone, Blackberry or Windows Phone). You simply produce programmatically some audio sound from the phone. To make a touchscreen controller is so boring because the physical joysticks are way better and more precise. A phone should be a mobile command station to interact with the helicopter with a lot more intelligence. The helicopter should be able to dance with your own written program!

The easiest way to get these dongles is searching "iPhone Android Helicopter" on eBay. Then write your iOS or Android code to produce some audio signal, control your TV, fly your helicopter! Don't forget to share your exciting discoveries with the DIY Phone Gadgets community.

Imagine that you are playing your favorite music while watching a bunch of helicopters dancing in the sky, following the melody and rhythm. Yes, that's so geeky. But you are so happy. What's more beautiful than a creative mind? 

71 comments:

  1. This is great! How could I do this with a Raspberry PI and in Python?

    ReplyDelete
    Replies
    1. Using the same Arduino code, and use Raspberry Pi and Python to control Arduino:)

      Delete
  2. how to use the 1st code that you said is tested. "The first working code (tested)". after you upload that program to your arduino it will automatically run or do you have to press something or whats so ever?? can i ask for the exact schematic diagram without the receiver thing? thank you.

    ReplyDelete
    Replies
    1. Anthony, you don't have too press anything. It will run automatically. The only thing you need to do is to fix the camera and the helicopter's initial positions so that the helicopter will always stay in the sight of the camera.

      Delete
  3. Searching the internet has led me to different versions of Arduino. Is there a certain one that works better than others for this coding?

    ReplyDelete
    Replies
    1. Normally we use Arduino Uno, others will definitely work too.

      Delete
  4. first I am surprised to know that IR helicopter can be controlled by the programmatically with android or another mobile application.I is very nice to read this post.

    ReplyDelete
  5. Sounds easy to me, thanks for the steps though, now I can control my helecopter with my android phone..

    ReplyDelete
  6. Hi! I have the basic (one mode only) S107 helicopter, but the Arduino code didn't work properly for me. No idea why. Possibly because I tested it in Arduino Mega, although that shouldn't be the reason.

    In any case, did some modifications and now it works. Hope it helps someone:

    https://gist.github.com/alfongj/5425213

    ReplyDelete
  7. Hey... i want to control syma107 helicopter using kinect sensor n arduino mega. i am working on it for a month but still facing problems. Can you provide me the processing code for controlling it through kinect? Will be extremely thankful to you.

    ReplyDelete
  8. I want to know can I use arduino uno R3 instead of the one you used?? Can I change the helicopter?? And can do all this without changing coding ?? And if PC camera coding would work for kinect or not ?? Plz answer all of my questions

    ReplyDelete
  9. Hi, thanks for sharing your work.
    I have a problem whith the code.
    Processing send a error in line 397.
    "the function arrayValue() does not exists"
    help me please.

    ReplyDelete
  10. The big rc helicopters reviews will react better to your control contrasted with ones with bring down quality edges.

    ReplyDelete
  11. Now, I don't mean to belittle a time honored classic carnival game like the fish pond, but as I stood there watching child after child saunter up, toss their line over and wait for the telling tug that signaled the arrival of a really great sticker, an ugly plastic bug or an enigmatic Chinese finger trap I thought, "Surely there's a better way".Mark Briggs

    ReplyDelete
  12. That was a great message in my carrier, and It's wonderful commands like mind relaxes with understand words of knowledge by information's.
    Python Online certification training
    python Training institute in Chennai
    Python training institute in Bangalore

    ReplyDelete
  13. Usually I never comment on blogs but your article is so convincing that I never stop myself to say something about it. You’re doing a great job Man, I like it..Heli Banners Australia..Keep it Up!

    ReplyDelete
  14. Really very nice blog information for this one and more technical skills are improve,i like that kind of post.
    apache spark online training

    ReplyDelete
  15. Thanks for your article on social media marketing course in Delhi! You targeted a topic we have already considered a few times. Our result in the past was a "light" version from what you have suggested. But your proposed way is far more structured and therefore we will follow it the next time we touch that topic :) Thanks again!

    ReplyDelete
  16. Really inspirational to hear someone pursuing their dreams and becoming successful instead of following the traditional path. I have read your article about This topic. I think it's good and impressed to know your service. Thanks for share this Information.
    airplane leasing

    ReplyDelete
  17. This is really nice to read content of this blog. A is very extensive and vast knowledgeable platform has been given by this blog. I really appreciate this blog to has such kind of educational knowledge.
    Helicopter lessons

    ReplyDelete
  18. I am speechless as to how great this article is. The content is simple to understand and very engaging.
    SAP training in Mumbai
    Best SAP training in Mumbai
    SAP training institute Mumbai

    ReplyDelete
  19. This comment has been removed by the author.

    ReplyDelete
  20. MP Board 12th Class Blueprint 2021 English Medium & Hindi Medium PDF download, MPBSE 12th Blueprint 2021 Pdf Download, mpbse.nic.in 12th Blue

    Print, Marking Scheme and Arts, Commerce and Science Streams Chapter wise Weightage pdf download. MP Board 12th Blue Print || MPBSE 12th Model Papers || MPBSE 10th Model Papers

    Manabadi AP Intermediate 2nd Year Model Question Paper 2021 MPC, BIPC, CEC, MEC group TM, EM Subject wise Blue Print, Download BIEAP

    Intermediate Second Year Model Question Papers, AP Senior Inter Test Papers, Chapter wise important Questions download. || AP Inter MPC, Bi.PC, CEC Blue Print || AP Inter 1st / 2nd Year Model Papers || AP 2nd year inter Test Papers

    Kar 1st / 2nd PUC Blue Print || UP Board 12th Blueprint 2021

    ReplyDelete
  21. I’m really happy with this informative blog, thank you so much for sharing this. Ogen Infosystem infosystem is a leading Web Designing and SEO Service provider in Delhi, India.
    Website Designing Company in Delhi

    ReplyDelete
  22. Thanks for Sharing a Very Informative Post & I read Your Article & I must say that is very helpful post for us.

    ReplyDelete
  23. Thank you for sharing this useful material. The information you have mentioned here will be useful. I would like to share with you all one useful source of Travel LaTomatina festival in Spain. Explore the world with a Tour to Review where you can find your destination at one place. Here you can get all the information about travel. La Tomatina is an annual festival celebrated in the small town of Bunol, the largest tomato fight in the world. Which may be interesting for you as well.

    ReplyDelete
  24. Thank you for sharing this useful content. The information you describe here would be useful. I want to share with you all a useful source Telstra Customer Service Number +61-1 800 431 401, which may be interesting for you as well. If you are troubled by problems in your internet or Telecom connectivity and your net surfing then do not worry about it. Telstra Support Number Australia corrects your internet issue.

    ReplyDelete
  25. Each user when purchase printing machine, their primary proverb is to get the quality printouts of any archives. Brother printer error code Ts-02 Along these lines, when we talk about quality-based printing administration, Brother Printer is been ideal as it has novel and progressed highlights that make the print work simple. While few errors may also occur during printing from your Brother Printer. brother Error TS-02 is the one most basic blunder numerous users have gone up against during their print work and raised a voice of help to fix it. This particular error issue seems when the WLAN passage or switch can't be distinguished by the framework while printing. You don't have to sway off; we are here to manage you to manage a particularly risky mistake code in an easy way.

    ReplyDelete
  26. Very nice post. I just stumbled upon your weblog and wanted to say that I’ve truly enjoyed surfing around your blog posts. In any case I will be subscribing to your feed and I hope you write again soon!CBD oil

    ReplyDelete
  27. As I do not know how to activate Roku, read the recent blog post on your portal. I could understand how to activate Roku and fix activation errors after reading. You have done a great job. I’m happy to mark the best review rating and share the post with new Roku users
    Please provide some information regarding the Roku.com/support services offered.

    ReplyDelete
  28. Thank you for sharing useful and don't forget, keep sharing useful info: Aluminium pipe manufacturer

    ReplyDelete
  29. Great blog article. Really looking forward to read more. Will read on… 야한동영상

    ReplyDelete
  30. With thanks! Valuable information! I think this is among the most vital info for me. 일본야동

    ReplyDelete
  31. This article is an appealing wealth of useful informative that is interesting and well-written. I commend your hard work on this and thank you for this information. I know it very well that if anyone visits your blog, then he/she will surely revisit it again. 한국야동닷컴

    ReplyDelete
  32. Thank you. I authentically greeting your way for writing an article. I safe as a majority loved it to my bookmark website sheet list and will checking rear quite than later. 국산야동

    ReplyDelete
  33. I have a read all blogs. Your blog is so interactive and information. Keep update more information
    https://shrishuddhinashamuktikendrabhopal.business.site/?m=true

    ReplyDelete
  34. Then call a windshield chip and crack repair technician for repair as soon as you can don't be the one testing your luck with that chipped or cracked windshield. how to get cracked mac apps What's more, the catalogue is being constantly updated with free casual games.

    ReplyDelete
  35. 토토사이트 Satisfying blog I am highly satisfied I have been here

    ReplyDelete
  36. 스포츠토토
    Remarkable posting! A lot of useful details and idea

    ReplyDelete
  37. I was eager to find this page. I needed to thank you for ones time for this especially awesome read!! I certainly truly preferred all aspects of it and I likewise have you book-set apart to look at new data in your blog.

    Medicare Insurance

    ReplyDelete
  38. Enjoyed reading the article above , really explains everything in detail,the article is very interesting and effective. Thank you and good luck for the upcoming articles

    ReplyDelete
  39. Xpertscm construction Company is leading construction Company and ranking at the top most position in the list of construction companies in Bihar. They are involve in many commercial and residential projects.

    ReplyDelete
  40. Do you want the best air hostess/cabin crew training institutes then There are many training institutes which train youngsters to be cabin crew in flights.But all depends on you what can you do for your better future. So I will recommend you to join Groundtosky Airhostess Training institutes.They are providing quality of training with placements.Here the training is based on the some special features. which require and help you to clear the all rounds of interview step by step.

    ReplyDelete
  41. Having read this I thought it was extremely enlightening. I like you investing some time and energy to assemble this short article. I once atain wind up investing a ton of energy both perusing and posting comments . But so what, it wass still advantageous.

    ReplyDelete
  42. Now online shopping is the way of life, we need the best shopping apps to make our lives easier.

    ReplyDelete
  43. I think this is one of the most crucial data for me.And i'm happy perusing your article. In any case, should comment on scarcely any broad things, The site style is awesome, the articles is truly fantastic.

    ReplyDelete
  44. Get the best deals on the latest software and bundles, Get thousands of latest software at a significant discount. We’ve everything you need for your business.

    ReplyDelete
  45. UPVC Pipes are our finest offering, made with high-quality raw materials and cutting-edge technology, and are suitable for both hot and cold water applications. When compared to traditional plastic piping systems, these pipes can sustain higher temperatures.

    ReplyDelete
  46. Best interior designing company in patna Bihar. We are the best interior design service provider in patna, the best interior designing company in patna.

    ReplyDelete
  47. Very interesting article. Many articles I come across these days really not provide anything that attracts others, but believe me the way you interact is literally awesome.
    Salesforce Offshore Support Services

    ReplyDelete
  48. Awesome blog.I find it interesting and is pretty fascinating, By reading your blog i got very useful information.Thanks for sharing this blog.
    SharePoint Support

    ReplyDelete
  49. Thanks for sharing this Informative content. Well explained. Got to learn new things from your Blog.
    FinancialForce offshore Support

    ReplyDelete
  50. i'm unequipped for perusing articles online especially every now and again, but Im cheerful I did these days. it is chosen dexterously composed, and your focuses are skillfully communicated. I request you agreeably, engage, dont at any point lower composing.! Pain Tool Sai Crack

    ReplyDelete
  51. Epubor Ultimate Crack Download is a solitary of the top of the line use of it's sort. This application will help you to change over your records! Thums Up Epubor Torrent

    ReplyDelete
  52. Thanks for sharing this post, it was great article. Unlock the power of phonics sounds with Ziyyara Edutech's top-notch online phonics classes. Our dedicated platform offers the best phonics classes online.
    visit Best phonics classes online

    ReplyDelete
  53. Wow, such an amazing chopper it is! Here you can also see some of the best kids puzzles. Such kinds of games can help you to improve your kid's brain and stress capacity.

    ReplyDelete
  54. Great experience I got good information from your blog. Join online tuition center for class 7 with Ziyyara's 100% qualified tutors who are dedicated to unlocking the full potential of every student.
    Book A Free Demo Today visit home tuition near me for class 7th

    ReplyDelete

Disqus for DIY Phone Gadgets