// Attiny85 for O.S.Pixie, Engel Handy and Citizenship replica with extra throttle button // Simple S/C sequential or compound transmitter with separate sequential throttle for ATtiny85(SMD) // Normal mode is compound, when pressing the button on startup it switches to sequential mode. // Connections: // D1=buzzer(P1), D2=button(P2), D3=throttle(P3), A2=low voltage alert(P4). GND is Pin 4, VCC is Pin 8. // The code is based on Phil_G`s 7chTx code. Low voltage alert from Ron (ronstv). Thanks for that. static int Futaba = 1; // set to 1 for Futaba channel order AETR. Set to 0 for JR/Spektrum order TAER static int buzzer = 1; // inactivity buzzer, Pin 6 on ATtiny (P1) static int sw_btn = 2; // pushbutton for S/C, Pin 7 on ATtiny (P2) static int sw_thr = 3; // pushbutton for sequential throttle, Pin 2 on ATtiny (P3) int ppm = 0, ppmPulse=300, neutralPulse=1200, ch, startup=1; // PPM Pin 5 on Attiny(P0) float ch0val, chtemp, channel[]={1200,1200,1200,1200,1200,1200,8000}; // last is sync float scrudd=0, scelev=0, vout=0, vin=0, R1=100000, R2=10000; // connect R1(100K)from A2(pin3,P4)to plus and R2(10K)from A2 to GND. byte fiftyhz=0, sc=0, flipped=0, flipr=0, rptr=0, drosptr=0, btnState=0, lastBtnState=1; static int r[]={-36,-72,-108,-144,-180,-216,-252,-216,-180,-144,-108,-72,-36,0,36,72,108,144,180,216,252,216,180,144,108,72,36,0}; static int dros[]={0,500,1000,500}; unsigned int counter=0, inact=0, value=0; void setup() { noInterrupts(); pinMode(ppm, OUTPUT); pinMode(buzzer, OUTPUT); pinMode(sw_btn, INPUT_PULLUP); pinMode(sw_thr, INPUT_PULLUP); } void loop() { if (startup == 1 && digitalRead(sw_btn) == 0) sc=1; // switches to sequential mode //low voltage alert value = analogRead(A2); vout = (value * 5.14) / 1024.0; vin = vout / (R2 / (R1 + R2)); channel[0]=0; // rudder channel[1]=0; // elevator channel[2]=-500; // throttle channel[3]=digitalRead(sw_btn)==0 ?500:-500; // for rubber escapement use channel[4]=0; // channel 5 channel[5]=0; // channel 6 // single-channel emulation scrudd=0; scelev=0; if (sc == 0) { // compound button if(rptr==0 && digitalRead(sw_btn)==0) ++rptr; if(rptr!=0) {scrudd=r[rptr]; if(digitalRead(sw_btn)==0 && (rptr==6||rptr==20)) --rptr; if(digitalRead(sw_btn)==0 && rptr==27) {--rptr; scelev=250;} ++rptr; if(rptr==28) rptr=0;}} if (sc == 1) {if(digitalRead(sw_btn)==1) flipped=0; else {if (flipped==0) {flipr^=1; flipped=1;} scrudd=flipr==0 ?-250:250;}} // sequential button channel[0]-=scrudd; channel[1]+=scelev; ch0val=channel[0]; // sequential throttle btnState = digitalRead(sw_thr); if (btnState != lastBtnState) {if (btnState==0) {++drosptr; counter=0; digitalWrite(buzzer, HIGH);}} if(digitalRead(sw_thr)==0 && ++counter >=30) drosptr=0; lastBtnState = btnState; if(drosptr==4) drosptr=0; channel[2]+=dros[drosptr]; // end of once-only startup routines startup=0; if (vin < 7.3 && bitRead(fiftyhz,0)==1 && bitRead(fiftyhz,3)==1) digitalWrite(buzzer, HIGH); // low voltage alert set to 7.3V // format the frame channel[6]=0; // 6th element is sync for (ch=0; ch<6; ch++) {channel[ch]+=neutralPulse; channel[6]+=channel[ch];} channel[6] = 15500-channel[6]; // this puts the channels in Futaba or Spektrum/JR channel order as required if(Futaba!=1) {chtemp=channel[2]; channel[2]=channel[1]; channel[1]=channel[0]; channel[0]=chtemp;} //send ppm frame, last channel holds sync value for (int ch=0; ch<7; ch++) {digitalWrite(ppm, HIGH); delayMicroseconds(ppmPulse); digitalWrite(ppm, LOW); delayMicroseconds(channel[ch]);} ++fiftyhz; // inactivity timer. 10 mins is approx 30000 frames. if (ch0val <-100||ch0val >100) inact=0; if (digitalRead(sw_btn)==0) inact=0; if (++inact >30000 && bitRead(inact,3)==1 && bitRead(inact,5)==1) digitalWrite(buzzer, HIGH); else digitalWrite(buzzer, LOW); }