// Simple Galloping Ghost recoder for DigiSpark ATTiny85 board and DRV8838 bridge - Phil_G // For Tobe GG actuator, genuine Rand or MM Birdcage actuator // Rate 2-14 pulses per second controlled by elevator channel // Mark-space between 30%-70% controlled by rudder channel // No specific GG throttle, use conventional ESC or throttle servo for IC #define rudder_ch 0 // receiver channel connected to P0 (ATTiny physical pin 5) Set PCMSK to match #define elev_ch 3 // receiver channel connected to P3 (ATTiny physical pin 2) #define LED 1 // on-board LED #define DRV8838 2 // bridge drive, output to GG actuator motor #define PW_FAST 86 // to match PIC recoder values #define PW_SLOW 270 // to match PIC recoder values volatile unsigned long timer_ch1, timer_ch2; // all timer variables are unsigned long volatile int pulse_time1 = 1500, pulse_time2 = 1500; volatile byte ch1Was = 0, ch2Was = 0; // previous hi/lo state int elevator_time = 1500, rudder_time = 1500; // measured channel pulses from rx unsigned long elev_rate = 0, rudder_marksp = 0, settle_time; unsigned long bridge_cycle = 0; // total cycle time in ms unsigned long bridge_left = 0, bridge_right = 0; // bridge left/right times in ms void setup() { pinMode(rudder_ch, INPUT_PULLUP); // pullups in case rx isnt connected pinMode(elev_ch, INPUT_PULLUP); // avoids picking up hash pinMode(DRV8838, OUTPUT); // o/p to DRV8838 pinMode(LED, OUTPUT); // Flash the LED in sync with the bridge pinMode(DRV8838, OUTPUT); // o/p to DRV8838 timer_ch1 = 0; timer_ch2 = 0; GIMSK = (1 << PCIE); // Enable Pin Change Interrupts PCMSK = (1 << rudder_ch) | (1 << elev_ch); // Enable interrupts for rx channel inputs sei(); settle_time = micros() + 500000; // 500 milliseconds to allow rx outputs to settle on power-up } void loop() { if (micros() < settle_time) { pulse_time1 = 1500; pulse_time2 = 1500; } cli(); rudder_time = pulse_time1; // do an atomic copy in the quickest way elevator_time = pulse_time2; sei(); rudder_time = constrain(rudder_time, 1000, 2000); elevator_time = constrain(elevator_time, 1000, 2000); rudder_marksp = map(rudder_time, 1000, 2000, 27, 73); // generous map to 30% - 70% bridge_cycle = map(elevator_time, 1000, 2000, PW_FAST, PW_SLOW); // 86ms=12hz to 270ms=3.7hz, matches PIC recoder bridge_left = bridge_cycle * rudder_marksp / 100; bridge_right = bridge_cycle - bridge_left; // waggle the bridge left & right digitalWrite(LED, HIGH); digitalWrite(DRV8838, HIGH); delay(bridge_left); digitalWrite(LED, LOW); digitalWrite(DRV8838, LOW); delay(bridge_right); } ISR(PCINT0_vect) // read the receiver channels into timer_ch1 & 2 { if (PINB & 0b00000001) { // Rx ch 1 if (ch1Was == 0) { timer_ch1 = micros(); ch1Was = 1; } } else { if (ch1Was == 1) { pulse_time1 = ((volatile int)micros() - timer_ch1); ch1Was = 0; } } if (PINB & 0b00001000) { // Rx ch 2 if (ch2Was == 0) { timer_ch2 = micros(); ch2Was = 1; } } else { if (ch2Was == 1) { pulse_time2 = ((volatile int)micros() - timer_ch2); ch2Was = 0; } } }