/* Arduino Code to control Syma S107G Helicopter using a PS3 "NAVIGATION" wireless controller PS3 and bluetooth code courtesy of Oleg and Lauszus @ Circuits@home.com Syma S107G IR code courtesy of Kerry Wong Sketch and code edited, tweaked, and combined by Matt Duncan Hardware setup consists of: Syma S107G Helicopter PS3 Navigation bluethooth controller Arduino Uno R3 USB Host Shield from Sparkfun.com Kensington USB bluetooth adapter #K33902US 3 Radioshack 100ma IR LED's #276-0143 switched by a single PN2222A NPN transistor(500ma max)|| Use 1k resistor from Arduino pin to BASE, 47K reistor to ground on BASE(pulldown) 9 volts input to Arduino and LED Annodes, LED cathodes switched by transistor and using Resistors of choice for LED current */ /* Basic Code Description: Arduino recieves 0-255 values from each analog control button on the PS3 Navigation remote via bluetooth and USB dongle/host shield (L2 trigger, and X/Y axis "HAT"). L2 Trigger data- 0= off, 255= fully depressed X hat Axis = Left and Right control- 0= full left, 255= full right, 127 center hat position Y hat Axis = Forward and backward control- 0 = full forward, 255= full backward , 127 center hat position The 0-255 values are divided by 2, to give 0-127 resolution for each left, right, forward, and backward Throttle values are also divided by 2, but instead mapped for better trigger control and resolution of helicopter lift(see comment about Channel selection and trigger values in loop code) The PS3 bluetooth code can also read button presses from L1, Cross, Circle, Up, Down, Left, Right, and PS buttons(PS button used to connect/disconnect controller from Arduino) */ #include #include USB Usb; BTD Btd(&Usb); PS3BT PS3(&Btd,0x00,0x1B,0xDC,0x0F,0xEF,0xD9); // This will store the bluetooth address - this can be obtained from the dongle when running the sketch- current address is Kensington adapter #define MODULATED 1 const int IR_PIN = 3; // Infarred LED on output pin 3 -- Use Transistor and high current IR LEDs for good helicopter range const unsigned long DURATION = 180000l; // < IR code constants const int HEADER_DURATION = 2000; const int HIGH_DURATION = 380; const int ZERO_LOW_DURATION = 220; const int ONE_LOW_DURATION = 600; int THROTTLE = 0; // Setup Variables for control and working with data recieved from Navi Controller int LFTRT = 0 ; int FWDBWD = 0 ; int TRIM = 118 ; // TRIM value = Use serial window with LeftRight println to find starting trim point. Find that value, multiply *2, input that value here. 127(63 in serial window) is mathmatically "center" int Throttle, LeftRight, FwdBack; void setup() { Serial.begin(115200); if (Usb.Init() == -1) { Serial.print(F("\r\nOSC did not start")); while(1); //halt } Serial.print(F("\r\nPS3 Bluetooth Library Started")); pinMode(IR_PIN, OUTPUT); digitalWrite(IR_PIN, LOW); //setup interrupt interval: 180ms Timer1.initialize(DURATION); Timer1.attachInterrupt(timerISR); //setup PWM: f=38Khz PWM=0.5 byte v = 8000 / 38; TCCR2A = _BV(WGM20); TCCR2B = _BV(WGM22) | _BV(CS20); OCR2A = v; OCR2B = v / 2; } void loop() { Usb.Task(); // Serial.println(Throttle); // uncomment(one at at time) to view data in serial monitor - good for debugging // Serial.println(LeftRight); // Serial.println(FwdBack); if(PS3.PS3Connected || PS3.PS3NavigationConnected) { int L2Button = PS3.getAnalogButton(L2_ANALOG) ; // variable to store L2 trigger data L2Button = map(L2Button, 0, 255, 155, 255) ; // map throttle to 155-255 // 155 is used for low throttle-needs at least this for copter lift, also gives more throttle resolution // *****Channel "A" uses throttle range 0-127 (0= off, 127 full throttle), Channel "B" uses throttle range 128-255 (128= off, 255 = full throttle) if(PS3.getAnalogHat(LeftHatX) >100 && PS3.getAnalogHat(LeftHatX) <155) // setting a cutoff point for very slight stick movement- basically desensitizes stick at center position for better control of helicopter { // I found the x/y axis stick to be very sensitive to light touch. eg, would create unwanted turns/ forward backward movements LFTRT = TRIM ; // TRIM value is referenced as "center" eg, no left or right turn input } else { LFTRT = PS3.getAnalogHat(LeftHatX) ; } if(PS3.getButtonClick(LEFT)) // Left and Right "arrow" buttons will serve as "trim" control to keep helicopter from turning without directional input, { // functionally the same as the trim control on the OEM copter transmitter TRIM = TRIM + 1; } if(PS3.getButtonClick(RIGHT)) { TRIM = TRIM - 1 ; } if(PS3.getAnalogHat(LeftHatY) >100 && PS3.getAnalogHat(LeftHatY) <155) // Again, setting cutoff points for forward and backward movements { FWDBWD = 127; // 127 is tail rotor "off" } else { FWDBWD = PS3.getAnalogHat(LeftHatY) ; } if(L2Button > 155) // sets throttle output data { THROTTLE = L2Button ; } else { THROTTLE = 128 ; // keeps throttle off when L2 trigger is not pressed // Channel "B" off value is 128, Channel "A" off value is "0" } if(PS3.getButtonClick(PS)) { // connect or disconnect Navigation Controller using PS button Serial.print(F("\r\nPS")); PS3.disconnect(); } } } ///// Code Functions Below ////// void sendHeader() { #ifndef MODULATED digitalWrite(IR_PIN, HIGH); #else TCCR2A |= _BV(COM2B1); #endif delayMicroseconds(HEADER_DURATION); #ifndef MODULATED digitalWrite(IR_PIN, LOW); #else TCCR2A &= ~_BV(COM2B1); #endif delayMicroseconds(HEADER_DURATION); #ifndef MODULATED digitalWrite(IR_PIN, HIGH); #else TCCR2A |= _BV(COM2B1); #endif delayMicroseconds(HIGH_DURATION); #ifndef MODULATED digitalWrite(IR_PIN, LOW); #else TCCR2A &= ~_BV(COM2B1); #endif } void sendZero() { delayMicroseconds(ZERO_LOW_DURATION); #ifndef MODULATED digitalWrite(IR_PIN, HIGH); #else TCCR2A |= _BV(COM2B1); #endif delayMicroseconds(HIGH_DURATION); #ifndef MODULATED digitalWrite(IR_PIN, LOW); #else TCCR2A &= ~_BV(COM2B1); #endif } void sendOne() { delayMicroseconds(ONE_LOW_DURATION); #ifndef MODULATED digitalWrite(IR_PIN, HIGH); #else TCCR2A |= _BV(COM2B1); #endif delayMicroseconds(HIGH_DURATION); #ifndef MODULATED digitalWrite(IR_PIN, LOW); #else TCCR2A &= ~_BV(COM2B1); #endif } void sendCommand(int throttle, int leftRight, int forwardBackward) // sends the 3 byte values to the IR LEDS { byte b; sendHeader(); for (int i = 7; i >=0; i--) { b = (leftRight & (1 << i)) >> i; if (b > 0) sendOne(); else sendZero(); } for (int i = 7; i >=0; i--) { b = (forwardBackward & (1 << i)) >> i; if (b > 0) sendOne(); else sendZero(); } for (int i = 7; i >=0; i--) { b = (throttle & (1 << i)) >> i; if (b > 0) sendOne(); else sendZero(); } } void timerISR() { //read control values from PS3 Navi Controller code and port to control variables for IR portion of code Throttle = THROTTLE ; LeftRight = LFTRT ; FwdBack = FWDBWD ; LeftRight = LeftRight / 2 ; // convert-- 0-255 to 0-127 Needs to be split for left and right actions FwdBack = FwdBack / 2 ; // convert-- 0-255 to 0-127 Needs to be split for forward and backward actions sendCommand(Throttle, LeftRight, FwdBack); // sends byte data to void sendcommand function variables }