Electronics Btrieve Motorcycling Software If you find this site useful and want to support, click on few ads to throw some money into hat.

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-