/* * rxtx433.c ... Keeloq receiver/transmitter * * Copyright (C) 2006 Jiri Pittner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * */ // //!!!!NOTE!!!! must not be used on the old testing board where PB2 is output of IR RECEIVER // //hardware supported: atmega8-atmega32 @ 7.3728 or 14.7456MHz; //hardware in my box: atmega32 @ 14.7456MHz //UPLOAD also eeprom!o // //Note: output pin MUST BE OC1A (PB1 or PD5), input (PD6) and TX_ON (PB2) pins you can change freely //INT0 ... button //PC0-2 LED //PD6 ... input from 433/868 RX //PB2 ... TX_ON //reset connected via DTR signal //PC3 enable ... for HOPERF module //PC4 condif ... for HOPERF module //PUT YOUR OWN DEVICE CODE (not manufacturer code) HERE #define KEELOQ_CRYPT 0x0123456789abcdefLL #define KEELOQ_PLAIN {0x85,0x20,0x06,0xc8} #define KEELOQ_DISCRIM {0x11,0x22} #include uint64_t keeloq_crypt0 __attribute__ ((section(".eeprom"))) =KEELOQ_CRYPT; uint8_t keeloq_plain0[4] __attribute__ ((section(".eeprom"))) =KEELOQ_PLAIN; uint8_t keeloq_discrim0[2] __attribute__ ((section(".eeprom"))) =KEELOQ_DISCRIM; //HCS200 programming interface pinout #define DDRPROG DDRA #define PORTPROG PORTA #define PINPROG PINA #define PROGDATA PA0 #define PROGCLOCK PA1 #include "backward.h" #include #include #include #include #include #include #include #include #include #include #if defined(at90s2313) || defined(at90s8535) #else #define ATmega #endif #ifdef ATmega #define USR UCSRA #endif #define BAUD 115200 #if (XTAL == 14745600L) #define oversampling 4 #else #define oversampling 2 #endif #ifndef MCU #define emulate #else #define assembler #endif #ifdef emulate #define uint8_t unsigned char #define uint16_t unsigned short #define uint32_t unsigned int #define uint64_t unsigned long long #define int16_t short #define PSTR(X) (X) #define fputs_P fputs #define printP print #define strcpy_P strcpy #define strcat_P strcat #define print(X) fputs(X,stdout) #define scan(X,Y) fgets(X,(Y)-(X),stdin) #define itoa10(N,S) sprintf(S,"%d",N) #define itoa16(N,S) sprintf(S,"%x",N) #else #define itoa10(N,S) itoa(N,S,10) #define itoa16(N,S) itoa(N,S,16) #endif void printP (PGM_P string){ char c; c=pgm_read_byte(string); while (c) { loop_until_bit_is_set(USR, UDRE); UDR = c; c=pgm_read_byte(++string); } return; } void print (char *string){ while (*string) { loop_until_bit_is_set(USR, UDRE); UDR = *string++; } return; } void scan(char *string){ char c; do { do { loop_until_bit_is_set(USR, RXC); c =UDR; } while bit_is_set(USR, FE); *string++ = c; //echo the character loop_until_bit_is_set(USR, UDRE); UDR = c; } while ( c != '\r' ); loop_until_bit_is_set(USR, UDRE); UDR = '\n'; string[-1]=0; } //UART initialize #ifdef ATmega #define UCR UCSRB #define UART_INIT(baud) { \ UBRRH=0; \ UBRRL= (XTAL/baud+15)/16-1; \ UCSRB=(1<6) state=upmark; break; case upmark: if(time1>=3*oversampling && time1<=6*oversampling && time0>=30*oversampling&& time0<=60*oversampling) {state=ready; incode[0]=bitcount=bytecount=0;break;} if(time1>=3*oversampling && time1<=6*oversampling && time0>=3*oversampling && time0<=6*oversampling) headercount++; else {state=noise; headercount=bitcount=bytecount=0;} break; case ready: //{char c[6]; print(itoa(time1,c,10)); print(":"); print(itoa(time0,c,10)); print("\n"); } if(time1==time0||time1<2*oversampling || time1 >12*oversampling || time0<2*oversampling) {state=noise; incode[0]=incodelen=bitcount=bytecount=0; break;} //unexpected abort incode[bytecount]|= (time1>time0?0:1)<60*oversampling || bytecount>=MAXCODELEN+1) {state=noise; incodelen=bitcount+(bytecount<<3)-1; } //legal end of transmition break; } } void receive_car(uint8_t level,uint16_t time) { static uint16_t time1=0; //{char c[6]; print(level?"1:":"0:"); print(itoa(time,c,10)); print("\n");} if(level) time1=time; else receive_car2(time1,time); } void receive_floatcode2(uint16_t time1, uint16_t time0) { static STATE state=noise; switch(state) { case noise: if(time1 <= 14*oversampling && time1 >= 11*oversampling && time0 <= 14*oversampling && time0 >= 11*oversampling) {state=ready; } break; case ready: if(time1 <= 14*oversampling && time1 >= 11*oversampling || bytecount>=MAXCODELEN+1) {state=noise; incodelen=bitcount+(bytecount<<3); bitcount=bytecount=0; return; } //legal end of transmition if(time0 < 3*oversampling || time0 > 11*oversampling ||time1 < 3*oversampling || time1 > 11*oversampling || time1==time0) {state=noise; incode[0]=incodelen=bitcount=bytecount=0; break;} //unexpected abort incode[bytecount]|= (time1>time0?0:1)<= 11*oversampling && time0 <= 15*oversampling && time0 >= 11*oversampling) {state=upmark;} break; case upmark: if(time1 > 15*oversampling || time0 > 15*oversampling || time0 < 5*oversampling || time1 < 5*oversampling ) {state=noise; break;} if(time1 <= 8*oversampling && time0 <= 8*oversampling) {incode[0]=bitcount=bytecount=0; state=ready;} else break; case ready: if(time0 <= 3*oversampling || time0 > 15*oversampling || time1 <= 3*oversampling || time1 > 15*oversampling ) {state=noise; incodelen=bitcount+(bytecount<<3); bitcount=bytecount=0; return;} if(time0 >= 10*oversampling) incode[bytecount]|= 1<= 10*oversampling) incode[bytecount]|= 2<90*oversampling) {state=noise; incodelen=bitcount+(bytecount<<3); bitcount=bytecount=0; return;} if(time <=oversampling && state==ready) {state=noise; bitcount=bytecount=0; return;} //{char c[6]; print(level?"1:":"0:"); print(itoa(time,c,10)); print("\n");} switch(state) { case noise: if(level && time >=75*oversampling && time <90*oversampling) {state=upmark; } break; case upmark: if(!level && time >=35*oversampling && time <=50*oversampling) {state=ready; incode[0]=bitcount=bytecount=0; } else {state=noise;bitcount=bytecount=0;} break; case ready: //{char c[6]; print(level?"1:":"0:"); print(itoa(time,c,10)); print("\n");} if(level) { if(time>=4*oversampling && time <= 6*oversampling) { incode[bytecount]|= 1< 10*oversampling*21) {state=noise; incodelen=bitcount+(bytecount<<3); break;} //legal end if(time > 7*oversampling) { uint8_t n; time -= 6*oversampling; n=(time+5*oversampling)/(10*oversampling+1); if(n>1) {state=noise; bitcount=bytecount=0;} //error, these devices never send two neighboring zeros bitcount++; } } break; } if(bitcount==8) {bitcount=incode[++bytecount]=0;} if(bytecount>=MAXCODELEN) {incodelen=bitcount+(bytecount<<3); state=noise;} return; } static volatile uint16_t count[2]={0,0}; static volatile uint8_t laststate=0; static volatile uint8_t recvtype=0; //also for transmit #define MAXRECVGEN 96 static volatile uint8_t rptr; static volatile uint8_t recvlevel[MAXRECVGEN]; static volatile uint16_t recvtime[MAXRECVGEN]; //this generates a run length encoded stream of data from the receiver and passes control to specialized decoders //@@@maybe it could be replaced by an edge-interrupt with timer capture? //but it seems to work reasonably like this INTERRUPT(SIG_OUTPUT_COMPARE1B) { uint8_t state; TCNT1=0; //with B it cannot run in CTC mode state=(PIND&(1<1?60000:100*oversampling)) return;} //state!=laststate or same state for too long { uint8_t l; uint16_t t; t=count[l=laststate]; laststate=state; count[state]=1; count[l]=0; if(incodelen) return; //buffer is full, main has to read it - ignore new data switch(recvtype) { case 2: case 3: //general { recvlevel[rptr]=l; recvtime[rptr]=t; ++rptr; if(rptr >= MAXRECVGEN) {rptr=0; incodelen=1;} return; } break; case 1: receive_fixed(l,t); break; case 0: receive_car(l,t); break; case 4: //another floating code receive_floatcode(l,t); break; case 5: receive_hyundai(l,t); break; } } } volatile uint8_t outcode[MAXOUTCODELEN]; volatile uint8_t outcodelen=0; //0 indicates ready to receive, non-zero indicates ready to read it in main volatile static uint8_t obitcount=0, obytecount=0; //working counters typedef enum {init,oupmark,downmark,data,trailer} OSTATE; static OSTATE ostate=init; static uint8_t ocount=0; void txcar(void) { switch (ostate) { case init: //the user has just written the request to transmit //check maxcodelen ocount=13; ostate=oupmark; cbi(TIMSK,TOIE1); ICR1=(uint16_t)(XTAL*8./10000+.5)-1; OCR1A=((uint16_t)(XTAL*4./10000+.5)-1); TCNT1=0; cbi(TCCR1A,COM1A0); cbi(TCCR1A,COM1A1); sbi(TIMSK,TOIE1); sbi(TIMSK,OCIE1A); break; case oupmark: #if (MCU == atmega8 ) if(--ocount) {sbi(PORTB,PB1); return;} cbi(PORTB,PB1); #else //ATmega 16 and 32 if(--ocount) {sbi(PORTD,PD5); return;} cbi(PORTD,PD5); #endif cbi(TIMSK,TOIE1); TCNT1=0; ICR1=(uint16_t)(XTAL*6.1/10000+.5)-1; sbi(TIMSK,TOIE1); ostate=downmark; ocount=2; break; case downmark: if(--ocount) return; obitcount=obytecount=0; ostate=data; cbi(TIMSK,TOIE1); ICR1=(uint16_t)(XTAL*12./10000+.5)-1; sbi(TIMSK,TOIE1); break; case data: { uint8_t n; n=obitcount+(obytecount<<3); if(n>outcodelen+1) { //data finished #if (MCU == atmega8 ) cbi(PORTB,PB1); #else //ATmega 16 and 32 cbi(PORTD,PD5); #endif cbi(TIMSK,OCIE1A); ostate=trailer; ocount=13; OCR1A=0; } else { #if (MCU == atmega8 ) if(n) sbi(PORTB,PB1); //avoid spurious first peak #else //ATmega 16 and 32 if(n) sbi(PORTD,PD5); //avoid spurious first peak #endif if(n>outcodelen) { OCR1A=((uint16_t)(XTAL*12./10000+.5)-1)*1/3; ++obitcount; } else { OCR1A=((uint16_t)(XTAL*12./10000+.5)-1)* ((outcode[obytecount]& (1<outcodelen) { //data finished ostate=trailer; ocount=35; cbi(TCCR1A,COM1A1); cbi(TCCR1A,COM1A0); OCR1A=0; } else { sbi(TCCR1A,COM1A1); OCR1A=((outcode[obytecount]& (1<outcodelen) { //data finished ostate=trailer; cbi(TCCR1A,COM1A0); cbi(TCCR1A,COM1A1); ocount=9; OCR1A=0; } else { OCR1A= outcode[obytecount]& (1<=outcodelen) { cbi(TIMSK,TOIE1); ICR1=10000; //determines the frequency with which it is checked for newly set outcodelen #if (MCU == atmega8 ) cbi(PORTB,PB1); #else //ATmega 16 and 32 cbi(PORTD,PD5); #endif ostate=init; outcodelen=0; sbi(TIMSK,TOIE1); } else { register uint8_t n=outcode[obytecount]; if(n&0x80) #if (MCU == atmega8 ) sbi(PORTB,PB1); #else //ATmega 16 and 32 sbi(PORTD,PD5); #endif else #if (MCU == atmega8 ) cbi(PORTB,PB1); #else //ATmega 16 and 32 cbi(PORTD,PD5); #endif ICR1= (n & 0x7f) * (uint16_t) (XTAL/80000.); ++obytecount; } } break; } } INTERRUPT(SIG_OVERFLOW1) { if(!outcodelen) return; switch(recvtype) { case 0: txcar(); break; case 1: txfixed(); break; case 2: txzvonek(); break; case 3: txgeneral(); break; } } INTERRUPT(SIG_OUTPUT_COMPARE1A) //not used in recvtype 2 { #if (MCU == atmega8 ) cbi(PORTB,PB1); #else //ATmega 16 and 32 cbi(PORTD,PD5); #endif } //programming routines // uint8_t letter2bin (char c) { return c>'9' ? c+10-(c>='a'?'a':'A') : c-'0'; } uint8_t octet2bin(char* octet) { return (letter2bin(octet[0])<<4) | letter2bin(octet[1]); } void bin2letter(char *c, uint8_t b) { *c = b<10? '0'+b : 'A'+b-10; } void bin2octet(char *octet, uint8_t bin) { bin2letter(octet,bin>>4); bin2letter(octet+1,bin&0x0f); } void delay_xs(uint16_t xs) __attribute__((noinline)); void delay_xs(uint16_t xs) //xs takes 4 clocks time { asm volatile ( "\n" "L_dl1%=:" "\n\t" "sbiw %0, 1" "\n\t" "brne L_dl1%=" "\n\t" : "=&w" (xs) ); return; } void delay_ms(uint16_t ms) //ms takes 1/1000 s { while(ms--) delay_xs(XTAL/4/1000); } #define delay_dms(dms) delay_xs(XTAL*(dms)/4/10000) #define delay_cms(cms) delay_xs((XTAL/100)*(cms)/4/1000) #define delay_us(us) delay_xs((XTAL/1000)*(us)/4/1000); void program_word(uint16_t data) { uint8_t i; for(i=0; i<16; ++i) { if(data&1) sbi(PORTPROG,PROGDATA); else cbi(PORTPROG,PROGDATA); data >>= 1; delay_us(10); sbi(PORTPROG,PROGCLOCK); delay_us(60); cbi(PORTPROG,PROGCLOCK); delay_us(50); } cbi(PORTPROG,PROGCLOCK); cbi(PORTPROG,PROGDATA); delay_ms(60); print("."); } uint16_t verify_word(void) { uint8_t i; uint16_t x,data=0; delay_us(50); for(i=0; i<16; ++i) { delay_us(55); sbi(PORTPROG,PROGCLOCK); delay_us(55); x=bit_is_set(PINPROG,PROGDATA)?0x8000:0; data >>=1; data |=x; delay_us(105); cbi(PORTPROG,PROGCLOCK); } print("x"); return data; } uint64_t keeloq_crypt; uint8_t keeloq_plain[4]; uint8_t keeloq_discrim[2]; void txcw(void) { sbi(PORTB,PB2); delay_ms(1000); #if (MCU == atmega8 ) sbi(PORTB,PB1); #else //ATmega 16 and 32 sbi(PORTD,PD5); #endif while(1); } //TXgeneral time in 0.1ms #ifdef vodarna #define n_output_gen 30 #define NREP 4 static output_gen[n_output_gen] = {120,128+51,51,128+51,51,128+51,25,128+25, 51,128+51,25,128+25,51,128+25,25,128+25, 25,128+51,25,128+25,25,128+25,71,128+77, 51,128+25,25,128+25,25,128+25}; #endif #define marantec #ifdef marantec #define n_output_gen 80 #define NREP 3 //nrep*n_output_gen < 256 static output_gen[n_output_gen] = { 100, 20 +0x80, 10, 10 +0x80, 20, 10 +0x80, 10, 20 +0x80, 10, 10 +0x80, 10, 10 +0x80, 10, 10 +0x80, 20, 10 +0x80, 10, 10 +0x80, 10, 20 +0x80, 20, 20 +0x80, 10, 10 +0x80, 10, 10 +0x80, 10, 10 +0x80, 10, 10 +0x80, 10, 10 +0x80, 20, 20 +0x80, 10, 10 +0x80, 10, 10 +0x80, 10, 10 +0x80, 10, 10 +0x80, 20, 10 +0x80, 10, 10 +0x80, 10, 10 +0x80, 10, 10 +0x80, 10, 20 +0x80, 20, 10 +0x80, 10, 20 +0x80, 10, 10 +0x80, 10, 10 +0x80, 10, 10 +0x80, 20, 10 +0x80, 10, 20 +0x80, 10, 10 +0x80, 10, 10 +0x80, 20, 10 +0x80, 10, 10 +0x80, 10, 20 +0x80, 10, 10 +0x80, 10, 10 +0x80, }; #endif void txtest(void) { int i; outcodelen=0; { TCCR1A= (1<1 && recvtype<4) { printP(PSTR("New batch: (time 36 = 1ms) \n")); uint8_t i; for(i=0; i>3); incodelen=0; //print raw (and possibly decode keeloq) itoa10(len,c); print(c); print(": "); for(i=0; i<(len+7)>>3; ++i) { itoa16(buf[i],c); print(c); print(" "); } print("\n"); if(recvtype==0 && (len==65 || len==66)) { decrypt((uint32_t *)buf,&keeloq_crypt); print("decrypted: "); for(i=0; i<4; ++i) { itoa16(buf[i],c); print(c); print(" "); } print(" = "); itoa10(buf[0]+buf[1]*256,c); print(c); print("\n"); } if(recvtype==5) //postprocess hyundai { uint8_t hyundai[MAXCODELEN+1]; uint8_t len2,hbitcount,hbytecount; len2=bitcount=hbitcount=bytecount=hbytecount=hyundai[0]=0; while(bitcount + 8*bytecount < len) { if(buf[bytecount] & (1<>3; ++i) { itoa16(hyundai[i],c); print(c); print(" "); } herror: print("\n"); } } } uint8_t programHCS(const uint16_t *data, uint16_t *data2) { uint8_t i,e; //program cbi(PORTPROG,PROGDATA); cbi(PORTPROG,PROGCLOCK); sbi(DDRPROG,PROGDATA); sbi(DDRPROG,PROGCLOCK); delay_ms(100); sbi(PORTPROG,PROGCLOCK); delay_ms(4); sbi(PORTPROG,PROGDATA); delay_ms(4); cbi(PORTPROG,PROGDATA); delay_us(70); cbi(PORTPROG,PROGCLOCK); delay_ms(5); for(i=0; i<12; ++i) program_word(data[i]); print("\n"); //verify cbi(DDRPROG,PROGDATA); for(i=0; i<12; ++i) data2[i] = verify_word(); print("\n"); cbi(DDRPROG,PROGCLOCK); e=0; for(i=0; i<12; ++i) if(data2[i]!=data[i]) ++e; return e; } static volatile uint8_t workmode=0; static volatile jmp_buf env; INTERRUPT(SIG_INTERRUPT0) { ++workmode; if(workmode==5) {wdt_enable(WDTO_15MS); wdt_reset(); while(1);} //reset longjmp(env,1); } int main(void) { char c[12]; uint8_t z; eeprom_read_block(&keeloq_crypt,&keeloq_crypt0,8); eeprom_read_block(keeloq_plain,keeloq_plain0,4); eeprom_read_block(keeloq_discrim,keeloq_discrim0,2); UART_INIT(BAUD); cbi(DDRD,PD6); cbi(PORTD,PD6); //can be any sbi(DDRB,PB2); cbi(PORTB,PB2); //voltage for the transmitter #if (MCU == atmega8 ) sbi(DDRB,PB1); cbi(PORTB,PB1); //OC1A needed #else //ATmega 16 and 32 sbi(DDRD,PD5); cbi(PORTD,PD5); //OC1A needed #endif //LED sbi(DDRC,PC0); sbi(DDRC,PC1); sbi(DDRC,PC2); //button cbi(DDRD,PD2); sbi(PORTD,PD2); cbi(MCUCR,ISC00); sbi(MCUCR,ISC01); sbi(GIMSK,INT0); //global interrupt activate sbi(SREG,7); //start setjmp(env); //do not care about how we got here, just switch based on workmode { char c[3]; c[1]='\n'; c[2]=0; printP(PSTR("Workmode ")); c[0]='0'+workmode; print(c); } switch(workmode) { case 3: //TXgeneral cbi(PORTC,PC0); sbi(PORTC,PC1); //red sbi(PORTC,PC2); //green recvtype=3; txtest(); break; case 4: //RX keeloq //MUST be last - something hinders further transmissions cbi(PORTC,PC0); cbi(PORTC,PC1); sbi(PORTC,PC2); //green recvtype=0; cbi(PORTB,PB2); #if (MCU == atmega8 ) cbi(PORTB,PB1); #else //ATmega 16 and 32 cbi(PORTD,PD5); #endif rxloop(); break; case 2: //TX repeatedly a test code sbi(PORTC,PC0); cbi(PORTC,PC1); //red cbi(PORTC,PC2); recvtype=0; txtest(); break; case 1: //TX CW cbi(PORTC,PC0); //blue sbi(PORTC,PC1); //red cbi(PORTC,PC2); //green txcw(); break; default: case 0: //go into the text driven modus sbi(PORTC,PC0); sbi(PORTC,PC1); sbi(PORTC,PC2); break; } starttext: #ifdef testing c[0]='t'; #else printP(PSTR("Keeloq or Receive or Transmit or Program?\n")); scan(c); #endif switch(c[0]) { case 'k': { char z[32]; uint8_t i,data[8]; printP(PSTR("Enter keeloq password (64bits in hex):\n")); scan(z); if(isxdigit(*z)) { for(i=0; i<8; ++i) data[7-i] = octet2bin(z+2*i); memcpy(&keeloq_crypt,data,sizeof(uint64_t)); eeprom_write_block(&keeloq_crypt,&keeloq_crypt0,sizeof(uint64_t)); } printP(PSTR("Enter plaintext code incl. buttons (32bits in hex):\n")); scan(z); if(isxdigit(*z)) { for(i=0; i<4; ++i) data[i] = octet2bin(z+2*i); memcpy(keeloq_plain,data,4); eeprom_write_block(keeloq_plain,keeloq_plain0,4); } printP(PSTR("Enter discrimination code + buttons (16bits in hex):\n")); scan(z); if(isxdigit(*z)) { for(i=0; i<2; ++i) data[i] = octet2bin(z+2*i); memcpy(keeloq_discrim,data,2); eeprom_write_block(keeloq_discrim,keeloq_discrim0,4); } } goto starttext; case 'p': while(1) { uint8_t data[24],data2[24]; uint8_t i,r; char z[32]; memset(data,0,24*sizeof(uint8_t)); printP(PSTR("Enter keeloq code 64bits:")); scan(z); for(i=0; i<8; ++i) data[7-i] = octet2bin(z+2*i); /* printP(PSTR("Enter counter 16 bits:")); scan(z); for(i=0; i<2; ++i) data[9-i] = octet2bin(z+2*i); */ printP(PSTR("Enter serial number 31 bits:")); scan(z); for(i=0; i<4; ++i) data[15-i] = octet2bin(z+2*i); data[15] &= 0x7f; printP(PSTR("Enter seed value 32bits:")); scan(z); for(i=0; i<4; ++i) data[19-i] = octet2bin(z+2*i); printP(PSTR("Enter discrimination 10 bits:")); scan(z); for(i=0; i<2; ++i) data[23-i] = octet2bin(z+2*i); data[23] &= 3; data[23] |= 0x10; z[2]=0; for(i=0; i<24; ++i) { bin2octet(z,data[i]); print(z); print(" "); } print("\n"); do{ printP(PSTR("Connect the programmer and press enter now!\n")); scan(c); r=programHCS((const uint16_t *) data,(uint16_t *)data2); if(r) { printP(PSTR("Programming failed!\n")); z[2]=0; for(i=0; i<24; ++i) { bin2octet(z,data2[i]); print(z); print(" "); } print("\n"); } else printP(PSTR("Programming successfull!\n")); } while(r); } break; case 'r': { while(1) { #ifdef testing recvtype=1; #else printP(PSTR("Receive type (0=Keeloq floating code, 1=obsolete fixed code, 2=doorbell, 3=general, 4=another floating code, 5=hyundai):\n")); scan(c); recvtype=c[0]-'0'; #endif rxloop(); } } break; case 't': //transmit ... use timer1 in fast PWM mode { int i; sbi(PORTB,PB2); //voltage for transmitter module #ifdef testing recvtype=1; #else print("send type (0=Keeloq floating code, 1=obsolete fixed, 2=doorbell, 3=general, 4=cw):\n"); scan(c); recvtype=c[0]-'0'; #endif { TCCR1A= (1<>8; memcpy(outcode+2, keeloq_discrim,2); outcode[3] &= 0x0f; outcode[3] |= button; encrypt((uint32_t *)outcode,&keeloq_crypt); memcpy(outcode+4, keeloq_plain,4); outcode[7] &= 0x0f; outcode[7] |= button; outcode[8]=0x02; print("TX: "); for(i=0; i<(65+7)>>3; ++i) { itoa16(outcode[i],c); print(c); print(" "); } print("\n"); for(i=0; i<5; ++i) { do{}while(outcodelen); outcodelen=65; //transmit } } break; }//switch } break; }//switch } return; }