Friday 22 November 2013

Telephone dialler (DTMF tone generator using PIC 16F1825) for dementia sufferers

A lady called Pauline popped into BuildBrighton the other day and asked for help with a particular project. Now, quite often, we're all busy with our own projects and very often the odd chancer turns up, with promises of millions of pounds of venture capital seed money who proves nothing more than a distraction - while looking to get their "ground-breaking" invention prototyped for free. These people, we usually give a few pointers to, and let them find their own way towards professional prototyping companies (we're not running a free consultancy service for people to exploit our skills: we're a generous and knowledgeable lot, down at BuildBrighton, but we're not stupid!)

Anyway, Pauline had an idea for a product of a different kind.
And it resonated with a few members who have worked in the public sector - and in particular, within the NHS. Pauline isn't looking to launch a product and try to make a lot of money; she's looking for help making something that will genuinely change lives.

Pauline's mother has mild onset dementia.
She doesn't understand things like the time of day any more. If it's winter, and it's dark, and it's five o'clock, there's no indicator to say whether it's 5am or 5pm (and, having dementia,  her mother can't really understand what am/pm means - or remember what that little dot on the digital clock is actually there for - and a 24-hour clock is just plain confusing).

Because of this, Pauline's mother phones her at all hours of the day - often just to hear a voice, or for reassurance. And sometimes - when it's in the early hours of the morning, the phone might not be answered - which distresses her mother; or it might be (meaning both people are now up and out of bed in the small hours of the morning!)

One solution we came up with was a telephone "auto-dialler".
Something that could monitor which phone numbers were being called, and check the current time (against a real-time clock). If the call were being made during "unsociable hours" the clever gizmo would terminate the call immediately (so it didn't ring at the other end) and then phone an alternative number instead (such as a 24 hour careline). If we can achieve this seamlessly, it means that Pauline can get some sleep during the night, while her mother can be put through to someone on the phone, whatever time she decides to ring!

There are basically two parts to this project:
The first is to be able to monitor the phone line and "read" the DTMF tones generated by the telephone handset as a call is being made. As each tone is received, it is decoded and turned back into a number. If the entire phone number matches one of those in the "redirect these calls please" list, we can look up the time and if it's outside certain hours, hang up the phone.

The second part of the project is actually making the call out to the alternative number.
This is actually the easier part of the project, so that's where we'll start - with a device which can take the phone "off-hook" and dial any number.

We're going to be using an HT9200A DTMF tone generator to make the calls; hooking into the phone lines is serious stuff and not something anyone should do lightly, so for now we're going to concentrate on generating the correct tones to dial any given number.
Here's how we did it:

In this circuit, we're going to play the DTMF tones through a speaker, just to check that everything is working as it should. In the final version, we'll connect to the phone lines through a 1:1 transformer, to ensure everything is safely isolated.

Using the same basic amplification circuit that we used for our clock project a while back, we simply use the DTMF tone to toggle a BS170 transistor, connecting a speaker to ground. The capacitor is simply a DC blocking capacitor, to ensure that we can't accidentally leave DC current running through the coil.

The HT9200A accepts data as 5-bit values (yeah, weird that they didn't go for a full, padded, byte - but the datasheet says five bits, so that's what we'll use) over an I2C interface. I2C is so easy to work with - it's just pin wiggling!

To send a single bit of data, hold the clock line high, then put the single bit onto the data line (high = 1, low = 0). Then send the clock line low. On the falling edge of the clock, the HT9200A puts whatever value was on the data line into it's shift register buffer. Data is sent LSB (least significant bit) first.

After receiving 5 bits, the DTMF generator will either play the appropriate tone(s) or stop playing the previous tone. So if we send, for example, the value 6 over serial (00110 in binary) the DTMF generator receives this and plays a combination of tones at 770Hz and 1477Hz



To tell the DTMF generator to stop playing, we send it the decimal value 24.
With all this information, we can write some code to dial a telephone number. We got our PIC to dial the BuildBrighton VOIP phone number: (ok, it's generating the DTMF tones that would dial the BB number, but you get the idea)

#include <system.h>
// ##################################
// using a 32 Mhz internal oscillator
// on a PIC16F1825
#pragma DATA _CONFIG1, 0x0804
#pragma DATA _CONFIG2, 0x1DFF
#pragma CLOCK_FREQ 32000000
// ##################################

#define cs portc.0
#define dta portc.1
#define clk portc.2
#define ledTest portc.3

const char DTMF_OFF=24;

char state;
char k;
char c;
char phoneNumber[16];

// program initialise
void initialise(){
   
     osccon=0b11110000;       // oscillator=32mhz
     delay_ms(50);            // wait for internal voltages to settle

     intcon=0b11000000;       // global interrupts and peripheral interrupts
     apfcon0=0b01000000;      // alternative tx/rx so pickit can use pins for uart
     wpua=0xFF;               // inputs on portA have weak pull-ups
     wpuc=0xFF;               // inputs on portC have weak pull-ups
     trisa=0b00000010;        // RA.1 is an input (uart rx)
     trisc=0b00000000;        // PORTC are outputs
     ansela=0x00;             // turn off analogue pins on portA
     anselc=0x00;             // turn off analogue pins on portC
     porta=0x00;              // set all output pins on portA off
     portc=0x00;              // turn off all output pins on portC
     option_reg=0x00;
}

void initUART(){
     //
     // UART
     //
     baudcon.4 = 0; // SCKP synchronous bit polarity
     baudcon.3 = 0; // BRG16 enable 16 bit brg
     baudcon.1 = 0; // WUE wake up enable off
     baudcon.0 = 0; // ABDEN auto baud detect

     txsta.6 = 0; // TX9 8 bit transmission
     txsta.5 = 1; // TXEN transmit enable
     txsta.4 = 0; // SYNC async mode
     txsta.3 = 0; // SEDNB break character
     txsta.2 = 0; // BRGH high baudrate
     txsta.0 = 0; // TX9D bit 9

     rcsta.7 = 1; // SPEN serial port enable
     rcsta.6 = 0; // RX9 8 bit operation
     rcsta.5 = 1; // SREN enable receiver
     rcsta.4 = 1; // CREN continuous receive enable

     spbrgh = 0; // brg high byte
     spbrg = 51; // brg low byte (see datasheet for 9600@32Mhz)

     apfcon0.2=1; // tx onto RA.0 (for PicKit2 sharing)
}

void UARTSend(unsigned char c){
     txreg = c;
     while(!txsta.1);
}

void uart_print(char t){UARTSend(t);}

void uart_print(char *s){
     while(*s) {
           UARTSend(*s);
           s++;
     }
}

void uart_println(char *s){
     uart_print(s);
     uart_print("\r\n");
}

void uart_print_dec(char number){
     if(number > 99) {uart_print(((number / 100) % 10) + '0');}
     if(number > 9) {uart_print(((number / 10) % 10) + '0');}
     uart_print((number % 10) + '0');
}

void setUpPhoneNumber(){
     phoneNumber[0]=0;
     phoneNumber[1]=1;
     phoneNumber[2]=2;
     phoneNumber[3]=7;
     phoneNumber[4]=3;
     phoneNumber[5]=3;
     phoneNumber[6]=5;
     phoneNumber[7]=8;
     phoneNumber[8]=2;
     phoneNumber[9]=6;
     phoneNumber[10]=3;
     phoneNumber[11]=99;      // we'll use termination characters later
}

void dtmf_out(unsigned char v){
     char digit=v;
     char i=0;
     char r=0;
     for(c=0; c<=1; c++){
           if(c==0){digit=v;}
           if(c==1){digit=DTMF_OFF;}
           if(digit==0){digit=10;}
           for(i=0; i<5; i++){
                 clk=1;                  // clock high while setting data
                 r=digit & 1;
                 if(r==0){
                       dta=0;
                 }else{
                       dta=1;
                 }
                 delay_us(5);      // delay 1/2 of 100khz
                 clk=0;                  // send the clock line low
                 delay_us(5);      // delay 1/2 of 100khz
                 digit=digit>>1;      // bit-shift so we start with LSB
           }
         
           // now wait 100ms (1/10th sec) and the tone plays
           delay_ms(100);
         
           // now the tone has finished playing,
           // send the command to turn off the signal
     }
}

void initHT9200(){
     cs=1;                  // disable the cs line
     clk=1;                  // force the clock line high
     cs=0;                  // enable the chip
     delay_ms(10);      // let the oscillator settle
     dtmf_out(DTMF_OFF);      // send the off command
   
}

void callNumber(){
     char ix;
     char iy;
     for(ix=0; ix<11; ix++){
           iy=phoneNumber[ix];
           dtmf_out(iy);
     }
}

// main program executes from here
void main(){

     initialise();
     initUART();
     delay_ms(250);
     ledTest=1;
   
     setUpPhoneNumber();
     initHT9200();
   
     // this is the main code loop
     while(1){
           callNumber();
           for(c=0; c<8; c++){
                 delay_ms(250);
           }
           if(ledTest==1){ledTest=0;}else{ledTest=1;}
     }
}

Here's a quick video showing the result:

1 comment: