DTMF rebooter

I have a linux box that is hosted at ISP. Couple of times I've managed to lock myself out of it by messing with network settings, and after the latest successful shot at my foot I decided to do something about it. After some websurfing for ideas, this is what came out:

The thing is meant to be connected to mobile phone via handsfree connector (I used an old Nokia 3310 handset). JP1 pin1 goes to microphone wire, pin3 is audio output wire. The phone gets its charging voltage from JP3. JP5 is wired to reset the PC, and CN1 is a male side of disk drive power connector. The phone is configured to auto-answer incoming calls, and does not require PIN code when started.

MT8870D is DTMF detector, when you make a call to the mobile then the call is automatically answered and you can enter DTMF codes that the detector will detect and decode to Q1-Q4 outputs, then it pulses the STD output high. AVR microcontroller that runs off the DTMF decoders clock generator is interrupted, reads the code and reacts accordingly. AVR's PB4 is wired as audio output, so that the device can send back beeps. The reset signal is controlled by small 5V relay, driven by Q1.

The software is quite rudimentary, it recognizes an activation command of 4 digit PIN followed by pound sign (#), and allows chaning PIN by entering a PIN code followed by asterisk (*), a new pin code, another asterisk, re-entry of new pin code finally followed by pound sign. If anything goes wrong, you get audiable beep back. If everything goes fine then the commands are silently obeyed - PIN is changed, or the relay is activated for about 2 seconds.

; DTMF rebooter v1.0 ; Atmel AVR 90S2313 microcontroller ; ; Madis Kaal ; mast@nomad.ee ; ; dtmf sequences recognized are ; ; DDDD# - trigger relay on for 2 seconds ; DDDD*NNNN*NNNN# - change pin to NNNN ; ; PIN is kept in 2 first locations of EEPROM as BCD value ; high byte at address 0, low byte at address 1 ; for example, setting two first bytes of EEPROM to 0x12,0x34 will ; result in PIN value 1234 ; .include "2313def.inc" ; tmr0 reload value for 5ms interval (actually 5.006ms) ; .equ tmrloadvalue = 186 ; port b pins ; .equ REL_AH = 0 ; relay output, high activates relay .equ PULSE = 1 ; debug output, low pulse in DTMF receiver .equ TMRCLOCK = 2 ; debug output pin, short low puls on every timer interrupt .equ LED_AL = 3 ; led, on while relay active, flashes when error beep is emitted .equ SPKROUT = 4 ; audio output pin ; port d pins ; .equ STD_AH = 2 ; strobe wired to PD2 (INT0) .equ Q1 = 3 ; dtmf detector output pins .equ Q2 = 4 .equ Q3 = 5 .equ Q4 = 6 .set GIE = 7 ;interrupt enable bit in sreg ;data segment ;not reserving any space here, but 0x60..0x6f is serial ;data receive buffer ; .DSEG .org 0x60 ;variable space begins here .def pinh = R11 .def pinl = R12 .def beepf = R13 .def tick1s = R14 .def acc = R16 .def beepcnt = R15 .def creload = R17 ;scrambled int TMR0 interrupt handler .def tick5ms = R18 .def codeh = R19 .def codel = R20 .def entryh = R21 .def entryl = R22 .def dtmfin = R23 ;dtmf value read (4 low bits) .def state = R24 ;count of dtmf values read since last ; z is used for state machine calls .CSEG .org 0 rjmp RESET ; Reset Handler rjmp EXT_INT0 ; IRQ0 Handler rjmp EXT_INT1 ; IRQ1 Handler rjmp TIM_CAPT1 ; Timer1 Capture Handler rjmp TIM_COMP1 ; Timer1 Compare Handler rjmp TIM_OVF1 ; Timer1 Overflow Handler rjmp TIM_OVF0 ; Timer0 Overflow Handler rjmp UART_RXC ; UART RX Complete Handler rjmp UART_DRE ; UDR Empty Handler rjmp UART_TXC ; UART TX Complete Handler rjmp ANA_COMP ; Analog Comparator Handler UART_RXC: ; UART RX Complete Handler EXT_INT1: ; IRQ1 Handler TIM_CAPT1: ; Timer1 Capture Handler TIM_COMP1: ; Timer1 Compare Handler TIM_OVF1: ; Timer1 Overflow Handler UART_DRE: ; UDR Empty Handler UART_TXC: ; UART TX Complete Handler ANA_COMP: ; Analog Comparator Handler reti ; timer interrupt handler ; TIM_OVF0: push R0 in R0,SREG ;save status flags cbi portb,TMRCLOCK out tcnt0,creload dec tick5ms brne tof1 ldi tick5ms,200 tst tick1s breq tof1 dec tick1s brne tof1 ; timeout, reset state machine ; also deactivates relay if it was active clr state cbi portb,REL_AH sbi portb,LED_AL rcall readpin tof1: sbi portb,TMRCLOCK out SREG,R0 ;restore status flags pop R0 reti ; read dtmf code, return it in dtmfin ; MT8870 outputs decoded values, then rises STD output high ; DTMF to output binay value translation: ; D - 0x0 ; 1 - 0x1 ; 2 - 0x2 ; 3 - 0x3 ; ... ; 0 - 0xa ; * - 0xb ; # - 0xc ; A - 0xd ; B - 0xe ; C - 0xf ; getdtmf: in dtmfin,pind lsr dtmfin lsr dtmfin lsr dtmfin andi dtmfin,0x0f mov acc,dtmfin ret ; invoked when there is new DTMF digit available ; digit code can be read from pd3..pd6 ; EXT_INT0: push R0 in R0,SREG cbi portb,PULSE tst beepf brne exitint0 push acc rcall getdtmf ;read the DTMF code rcall esmach ;run it through the state machine pop acc exitint0: sbi portb,PULSE out SREG,R0 pop R0 reti ; entry state machine ; jmp to $100+(state*2) ; entry points have to return with ret esmach: rcall clrtimeout ldi r31,0x01 mov r30,state ijmp RESET: ldi acc,0xdf ;set stack to end of ram out SPL,acc ; enable watchdog ldi acc,0x1b out wdtcr,acc ; set I/O directions ; d1 output ; d0,d2..d6 output ; b0..b4 output ; b5..b7 input ; ldi acc,0x0e out portb,acc ldi acc,0x1f out ddrb,acc ldi acc,0x02 out ddrd,acc ldi tick5ms,200 clr tick1s clr state clr beepf ; read pin from eeprom ; rcall readpin ; set up timer, load initial value, enable overflow interrupts ; ldi acc,4 out tccr0,acc ldi creload,tmrloadvalue out tcnt0,creload ldi acc,0x02 out timsk,acc ;enable timer 0 overflow interrupts ldi acc,0x03 out mcucr,acc ;int0 ineterrupt on rising edge ldi acc,0x40 out gimsk,acc ;enable interrupts on int0 bset GIE ;enable all interrupts ;mainloop does only beeps ; mainloop: wdr tst beepf breq mainloop rcall beep clr beepf rjmp mainloop ;and loop the loop ; read codeh,codel from eeprom ; readpin: clr acc ;start from address 0 out eear,acc ;EEPROM address sbi eecr,EERE ;EEPROM read strobe inc acc ;move to next adr in codeh,eedr ;get the eeprom byte out eear,acc sbi eecr,EERE in codel,eedr ret ; write one byte to eeprom ; byte must be in eedr ; addr must be in eear ; eewrite: ldi acc,4 out eecr,acc sbi eecr,EEWE ; wait for eeprom write to finish ; eewait: wdr sbic eecr,EEWE rjmp eewait ret ; write codeh,codel to eeprom ; writepin: bclr GIE rcall eewait clr acc out eear,acc out eedr,entryh rcall eewrite ldi acc,1 out eear,acc out eedr,entryl rcall eewrite bset GIE ;enable all interrupts ret ; emit 1000 Hz on SPKROUT for 1 sec ; also flash the led twice ; beep: cbi portb,LED_AL rcall b0 rcall b0 cbi portb,LED_AL rcall b0 b0: ldi acc,250 mov beepcnt,acc b2: sbi portb,SPKROUT rcall w500us cbi portb,SPKROUT rcall w500us dec beepcnt brne b2 sbi portb,LED_AL ret ; approx 500us wait w500us: ldi acc,198 b11: dec acc push acc pop acc nop wdr brne b11 ret ;--------------------------------------------------------------- .org 0x100 rjmp pin1 rjmp pin2 rjmp pin3 rjmp pin4 rjmp poundorstar rjmp pin1 rjmp pin2 rjmp pin3 rjmp pin4 rjmp star2 rjmp pin1 rjmp pin2 rjmp pin3 rjmp pin4 rjmp pound2 ; when bad pin is entered then reset the state ; and make an error beep badpin: clr state inc beepf ret ; add dtmf code in dtmfin to code accumulator, reset timeout counter ; adddtmf: lsl entryl rol entryh ;*2 lsl entryl rol entryh ;*4 lsl entryl rol entryh ;*8 lsl entryl rol entryh ;*16 or entryl,dtmfin ret ; check the pin in entryl,entryh against stored PIN ; chkpin: cp entryl,codel brne cp1 cp entryh,codeh cp1: ret ; first 4 pin digits are just accumulated ; pin1: clr entryl clr entryh pin2: pin3: pin4: rcall adddtmf inc state ret ; pound after pin terminates input and executes ; star after pin prepares for pin change ; * - 0xb ; # - 0xc poundorstar: cpi dtmfin,0xc ; is it #? breq checkexec ; yes, proceed with command cpi dtmfin,0xb brne badpin ; was not * or #, break out rcall chkpin ; check if the entered pin was correct brne badpin ; no, will not change inc state ; correct pin, proceeding with pin change ret ; check pin, and execute if correct ; checkexec: rcall chkpin ; correct pin? brne badpin ; no, do nothing ; execute command ; execute: sbi portb,REL_AH ; activate reset cbi portb,LED_AL clr state ; switch to initial state ; reset timeout timer to 2 seconds ; clrtimeout: ldi tick5ms,200 ldi acc,2 mov tick1s,acc ; set timer for 2 seconds ret ; timer interrupt handler will deactivate the signal ; time to get the second asterisk ; star2: cpi dtmfin,0xb brne badpin ; was not '*', break out mov pinl,entryl mov pinh,entryh inc state ; pinh,pinl contain a first pin entry now ret ; end of pin change, must be pound ; pound2: cpi dtmfin,0xc ; # now? brne badpin ; no, will not change pin cp pinl,entryl brne badpin ; entries not the same cp pinh,entryh brne badpin rcall writepin ; write new pin rcall readpin ; read back to codeh,codel ret

Copyright © Madis Kaal 2000-