Cheap Helicopters In My Living Room

Ned Jackson Lovely

@nedjl / njl@njl.us

slides @ www.njl.us

Python + Arudino + Helicopter

Helicopters!

The Syma S107G is Less Than $20 on Amazon

It Goes Up and Down

It Rotates Left and Right

It Goes Forwards and Backwards

It Has a Blinking Light

USB Charger

The IR Protocol has been reverse engineered

howardignatius

"throttle"

0 to 127

George M. Groutas

"yaw" & "trim"

0 to 127

63 is the middle

"pitch"

0 to 127

63 is "off"

The IR Protocol is easy

Four bytes, one for each setting

Arduinos!

An Arduino Uno is
$30 on Amazon

int led = 13;

void setup() {                
  pinMode(led, OUTPUT);     
}

void loop() {
  digitalWrite(led, HIGH);
  delay(1000);
  digitalWrite(led, LOW);
  delay(1000);
}
    

yum9me

int led = 13;

void setup(){
    pinMode(led, OUTPUT);
    Serial.begin(9600);
}

void serialEvent(){
    byte in = Serial.read();
    if(in == 0){
        digitalWrite(led, LOW);
    }else if(in == 1){
        digitalWrite(led, HIGH);
    }
}

void loop(){
    delay(1000);
}
import serial, time

s = serial.Serial('/dev/tty.usbmodem1421', 9600)

while True:
    s.write([1])
    time.sleep(1)
    s.write([0])
    time.sleep(1)

Martin Bircher

Martin Kenny

Tudor Barker


byte cmds[] = {63, 63, 0, 63};
//yaw, pitch, throttle, trim

void setup(){
    pinMode(LED, OUTPUT);
    digitalWrite(LED, HIGH);
    Serial.begin(9600);
}
    
void serialEvent(){
    if(Serial.available() != 4){
        return;
    }
    for(int i=0;i<4;++i){
        cmds[i] = Serial.read();
    }
}
byte sendPacket(byte cmds[]){
    //Blinks LED, returns time to sleep
}

void loop(){
    delay(sendPacket(cmds));
}
    
    class Heli(object):
        def __init__(self):
            path = [x for x in os.listdir('/dev') 
                        if x.startswith('tty.usb')][0]
            path = '/dev/'+path
            self._s = serial.Serial(path, 9600)

            self.yaw = 63
            self.pitch = 63
            self.throttle = 0
            self.trim = 63
    
    def send(self):
        msg = [self.yaw, self.pitch,
                self.throttle, self.trim]
        self._s.write(msg)
        self._s.flush()
        print('sent {}'.format(msg))

Belts & Suspenders

    def land(self):
        for _ in range(3):
            self._s.write([63, 63, 0, 63])
            self._s.flush()
            time.sleep(0.01)
        sys.exit()

Level 1

    def takeoff(self):
        self.yaw = self.pitch = self.trim = 63
        self.throttle = 40
        self.send()
        time.sleep(5)

        self.throttle = 90
        self.send()
        time.sleep(1)

        self.throttle = 68 #Magic
        self.send()
if __name__ == '__main__':
    h = Heli()
    h.takeoff()

    h.yaw = 0
    h.pitch = 50
    h.send()

    time.sleep(10)
    h.land()

Level 2

import heli
from flask import Flask, request, jsonify

app = Flask(__name__)
h = heli.Heli()

_ARGS = ('yaw', 'pitch', 'throttle', 'trim',)

@app.route('/api', methods=['POST'])
def api():
    for k,v in request.values.items():
        if k not in _ARGS:
            continue
        setattr(h, k, int(v))
    h.send()
    return jsonify(**{k:getattr(h,k) for k in _ARGS})

Level 3

def main():
    vc = cv2.VideoCapture(0)
    tracker = Tracker()
    h = Heli()

    while True:
        rval, frame = vc.read()
        if not rval:
            break

        tracker.handle_frame(frame)
        h.correct_drift(tracker)

        if cv2.waitKey(20) == 27:
            break
    h.land()

Live Demo...

It's (almost) All Just Software!

Thank You!

Ned Jackson Lovely

@nedjl / njl@njl.us

slides @ www.njl.us