Showing posts sorted by relevance for query caller ID. Sort by date Show all posts
Showing posts sorted by relevance for query caller ID. Sort by date Show all posts

Thursday, December 13, 2012

Caller ID Project - part six

I got the LCD code integrated with the Caller ID app and began integration testing of the Version 0.9 code this evening.  Looking good!

Next steps

  • Complete integration testing (just let it run for a couple of days...)
  • Wire everything together and do the final testing


























Here's the code with the LCD routines integrated (see Phil Bambridge's blog for complete details on the LCD routines.)

/*
 *    main.c
 *
 *    M. Amos
 *    (LCD code by Phil Bambridge)
 *
 *      Listens for Caller ID info from USB modem, decodes it, writes it to the console, formats the info onto a
 *      web page and writes the caller name and number to an LCD display
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>

#include <arpa/inet.h>
#include <net/if.h>

#include <sys/sysinfo.h>
#include <sys/vfs.h>

#include <time.h>

#include <unistd.h>

#include <signal.h>

#include "lcd.h"

struct lcdmodule module;

int get_fld(char *, int);
int hex_to_ascii(int, int);
int hex_to_int(int);
int write_web_page(char *, char *, char *);

int main(int argc, char **argv) {
    struct lcdmodule module2;
    
    // Variables for Caller ID
    int  msg_len;
    int  msg_end;
    long fld_type;
    long fld_len;
    char fld_val [64] = "";
    int  str_ptr;
    char str_date_time [20] = "  /     :  ";
    char str_nbr [20] = "   -   -    ";
    char str_nam [64] = "";
    char str_input [256] = "\0";
    
    // Here we create and initialise two LCD modules.
    // The arguments are, in order, EN, RS, D4, D5, D6, D7.
    module = lcdInit(17, 18, 22, 23, 24, 25);
    module2 = lcdInit(21, 18, 22, 23, 24, 25);
    
    // Send configuration strings to modem
    // On Raspberry Pi, leaving echo on and status messages on causes problems with some kind of "loop back" on the
    // /dev/ttyACM0 port.  So, we have to turn them off.
    FILE *out_file;
    out_file = fopen("/dev/ttyACM0", "w");
    if (out_file == NULL) {
        fprintf(stderr, "Can't open output file\n");
    }
    fprintf(out_file,"ATE0");                                 // Turn off echo
    fprintf(out_file,"%c%c",0x0d,0x0a);
    fprintf(out_file,"ATQ1");                                 // Turn off status messages
    fprintf(out_file,"%c%c",0x0d,0x0a);
    fprintf(out_file,"AT#CID=2");                             // Turn on unformatted Caller ID
    fprintf(out_file,"%c%c",0x0d,0x0a);
    fclose(out_file);
    
    // Open modem for input
    FILE *in_file;
    in_file = fopen("/dev/ttyACM0", "r");
    if (in_file == NULL) {
        fprintf(stderr, "Can't open input file\n");
    }
    
    // display splash screen on LCD
    gotoXy(module, 0,0);
    prints(module, "Caller ID       ");
    gotoXy(module, 0,1);
    prints(module, "Version 1.0     ");
    
    while (fgets(str_input, 250, in_file) != NULL ){            // Check to see if there's input available
        if(strstr(str_input,"MESG=80")){                        // Check to see if we received a CID message
            
            // Get Message Length
            str_ptr = 7;                                        // position pointer to point at the message length field
            msg_len = get_fld(str_input, str_ptr);
            msg_end = (msg_len * 2) + 8;                        //  Adjust for two bytes per hexx digit, plus "header" + length
            str_ptr+=2;                                         // position pointer to point to the start of first field
            
            // Parse message while there's still input to be had
            while (str_ptr < msg_end){
                fld_type = get_fld(str_input, str_ptr);         // Get Field Type
                str_ptr+=2;
                
                fld_len = get_fld(str_input, str_ptr);          // Get Field Length
                str_ptr+=2;
                fld_len = fld_len * 2;
                
                int j = 0;                                      // Get Field Value
                int i = 0;
                for (i=str_ptr; i< fld_len + str_ptr - 1; i+=2){
                    fld_val[j++] = hex_to_ascii(str_input[i], str_input[i+1]);
                    fld_val[j] = '\0';
                }
                
                str_ptr += fld_len;                             // decode field values using field type
                switch (fld_type) {
                    case 1: {
                        strcpy(str_date_time, fld_val);
                    } break;
                    case 2: {
                        strcpy(str_nbr, fld_val);
                    } break;
                    case 4: {
                        strcpy(str_nbr, "No Number");
                    } break;
                    case 7: {
                        strcpy(str_nam, fld_val);
                    } break;
                    case 8: {
                        strcpy(str_nam, "No Name");
                    } break;
                }
            }
            // end while parse message
            
            // Echo date, number and name to console
            printf("Date: %s\n",str_date_time);
            printf("Number: %s\n",str_nbr);
            printf("Name: %s\n",str_nam);
            
            // Create the HTML for the web page displaying the Caller ID info
            write_web_page(str_date_time, str_nbr, str_nam);
            
            // Display Caller ID info on LCD
            gotoXy(module, 0,0);
            prints(module,str_nbr);
            prints(module,"                ");
            gotoXy(module, 0,1);
            prints(module,str_nam);
            prints(module,"                ");
        }
        else {                                                  // else extraneous input (i.e. not MESG=...)
            printf("%s\n",str_input);                           // shouldn't be any - we turned off echo and status messages
        }
    }                                                           // end while fgets
    
    
    return 0;
} // main

// Create HTML for web page to display the Caller ID info:
int write_web_page(char *str_date_time, char *str_nbr, char *str_nam){
    FILE *web_file;
    web_file = fopen("/var/www/index.html", "w");  // for Raspberry Pi use /var/www/index.html
    if (web_file == NULL) {
        fprintf(stderr, "Can't open web page output file\n");
    }
    // format the date and time and output a record to the web server.
    fprintf(web_file,"<html><body>\n");
    fprintf(web_file,"<p><b>Caller ID</b></p>\n");
    fprintf(web_file,"<p>Date: %c%c-%c%c</p>\n",str_date_time[0], str_date_time[1], str_date_time[2], str_date_time[3]);
    fprintf(web_file,"<p>Time: %c%c:%c%c</p>\n",str_date_time[4], str_date_time[5], str_date_time[6], str_date_time[7]);
    fprintf(web_file,"<p>Number: %s</p>\n", str_nbr);
    fprintf(web_file,"<p>Name: %s</p>\n", str_nam);
    fprintf(web_file,"</body></html>\n");
    
    fclose(web_file);
    
    return 0;
}

// Convert input hex characters to integer 
int hex_to_int(int c){
    int first = c / 16 - 3;
    int second = c % 16;
    int result = first * 10 + second;
    if(result > 9) result--;
    return result;
}

int hex_to_ascii(int c, int d){
    int high = hex_to_int(c) * 16;
    int low = hex_to_int(d);
    return high+low;
}

// Convert two characters from the incoming string and return as an integer
int get_fld(char *fldBuf, int bufPtr){
    char fldStr[3] = "00";
    char *p;
    fldStr[0] = fldBuf[bufPtr++];
    fldStr[1] = fldBuf[bufPtr++];
    return (int) strtoul(fldStr, &p, 16);
}

Tuesday, December 4, 2012

Caller ID project - part one

The first task in my Caller ID project is to research the USB Modem output to see what I have to work with.

I'm using a USR 5637 56 Kbps USB modem for my project because I had one on hand that hasn't seen use in a few years.  They're about $45 new, $25 on eBay.  I checked the specs and this one supports Caller ID as does my local phone company.  

I plugged the modem into the Mac, fired up ZTerm and connected to usbmodem0000001.


I typed ATH (the code for hang up) just to see whether ZTerm was connected to the Mac - I got the expected 'OK' response.


Then I typed AT#CID=2 - this sets the caller ID output to unformatted.  I figured this might be a little easier to parse. Apparently this setting is not "sticky" and needs to be set each time the USB modem powers on.


I plugged in a phone cable, called my number and saw this (this string has extra spaces that I put in for clarity):

RING
MESG=80 27 01 08 31 32 30 33 31 39 32 34 02 0A 34 31 39 35 35 35 31 32 31 32 07 0F 43 65 6C 6C 20 50 68 6F 6E 65 20 20 20 4F 48 FF<CR>

The message is a string of Hexadecimal numbers followed by a Carriage Return.  The format of this message is:

message type
length of message
field type 1
field length 2
field 1
field type 2
field length 2
field 2
.
.
.

field type N
field length N
field N
Checksum

The message type is '80' - MESG=80 will be a good indicator of the start of the message.


Field types:

  • 1 - date & time
  • 2 - phone number
  • 4 - number not present
  • 7 - name
  • 8 - name not present
If the calling party's number is not available or hidden, the name field contains an "O" or "P" respectively. Type 4 and type 8 are apparently always length 1 followed by an "O" or a "P".

So manually parsing the message above, I get:

MESG=80 - start of message 
39 character long message 
Type 1 - date & Time 
8 characters for the date and time  
12031924 (call was received on 12/03 at 19:24) 
Type 2 - phone number 
10 characters for the caller's number  
4195551212
Type 7 - name 
15 characters for the name 
Cell Phone   OH (there are 3 spaces between "Phone" and "OH" 
Checksum: 255 

The USB Modem, sans case: 


I'm planning to encase it with the rest of the project components.












Next step - write a program to parse this.  Hmmm: C or Python?  I think C...

Saturday, December 15, 2012

Caller ID Project - part seven

Above is a screen print of the Caller ID web page 
produced by the app.
The integration test went well - a couple of days reporting various caller ID's with no problems.  This morning I made some minor code changes - did some cleanup and comment editing.  I then added code to start the app when the Raspberry Pi boots.  Thanks to Martin O'Hanlon for his post in StuffAboutCode that explains starting up a user app at boot time.

The app starts at init.d time.  The debugging output goes to the console and the Caller ID goes to the web and the LCD.  Very handy.




Here's an example of a portable digital TV I built into a 
lunchbox a couple of years ago.






I'm thinking about putting the whole thing in a metal tin just to "dress it up".

Metal tins and metal lunchboxes make great electronics projects boxes:  they're very easy to cut, they shield the contents and keep them from emitting lots of noisy RF.

Now on to wiring it up!


Monday, December 10, 2012

Caller ID Project - part four

So... What have I learned so far in this project?
  • How to use Xcode IDE to code up a C program
  • Installing and using GCC on the Mac
  • Communicating to a USB modem from a C application
  • The format of Caller ID messages
  • How to serve web pages on the Raspberry Pi
I found an LCD app for the Raspberry Pi written in C (by Phil Bambridge, of Bristol) so I've got something to hack for this application.  Thanks Phil!!

The app ran over night and all day today on the RPi without crashing - serving web pages, displaying caller ID's of a dozen callers and generally behaving, so I'd say the unit test is a "pass".

I've been using SSH to communicate with the RPi, editing and compiling in vi in the SSH window.  I only had to make two minor changes to the code when moving to the Raspberry Pi: change the name of the USB Modem and the path to the web page.  I had thought about building a cross compiling environment for this work, but the performance of the RPi just isn't that bad...

Good project so far!




Sunday, December 2, 2012

Next project?


Here are some thoughts on my next project.  I'd like to be able to tell who's calling on the landline without getting up and going into the other room to look at the phone to see who's calling.  This will be especially useful in political campaign season where the signal/noise ratio is about 0.05... and the rest of the year when the Calls-for-Cindy/Calls-for-Mark ratio is about the same...

Here's what I'm thinking:
I'll use a USB Modem I've got laying around to receive the ANI caller information - they're pretty cheap on ebay, or I could use an old US Robotics modem and a serial port instead of USB. Just need to make sure the one I pick supplies ANI...  I'll retrieve the caller ID from the modem using a little C program and post it to a web page (hosted on a Raspberry Pi) that I can view with my phone or tablet from the couch.  I'll write the output to an LCD display as well that I can view from my desk. 

So, how will I go about it?
- Investigate the Caller ID info, document it's format, baud rate, etc.
- Figure out how to read the USB modem to an application 
- Figure out how to automatically start up a web server on the Pi
- Figure out how to write to an LCD display.  Maybe use a PIC app to listen to a serial port?
- Code up the app in C, or maybe Python

Why do it?
It'll be fun - and most of the projects I do, I do to learn.  In this case  I'll get to learn all of the stuff above, most of which will be useful for other projects. 

If you're into RPi, check out the latest edition of MagPI A Magazine for Raspberry Pi Users - available on-line at: http://issuu.com/themagpi/docs/issue_8_final?mode=window

And soon to be in print:

The whole idea behind this blog and the Raspberry Pi (and the Arduino and myriad other hobbyist tools) is all about turning your thoughts into reality.  Sometimes, all you need is "permission" - or the knowledge that it is indeed possible!  Check out MagPI for inspiration.



Wednesday, December 12, 2012

Caller ID Project - part five

I got the LCD display running with the Raspberry Pi (thanks again to Phil Bambridge's software.)

Next: integrate the LCD software with the Caller ID modules and integration test!


Tuesday, January 1, 2013

Home PBX Project - Redux

The FreePBX home PBX is working great.  I've got a half dozen extensions connected including X-Lite for PC's, Bria for the iPads/iPhone, an OBi202 WiFi analog phone "adapter" connected to our wireless phones and a Nortel LG-1535 for my office.  These Nortel desksets are inexpensive, but they're really nice.  I got this one on E-bay - it was packaged in a new box, but was set up for Turkish... (simple config option to get it to English.)

I've forwarded my home phone number to the new PBX and changed the outgoing Caller ID to match the home phone's number.  So, now the whole house is using the new service transparently (i.e. you just pick up the phone and dial, all the phones ring with either incoming number, CallerID works, etc.)  I'm using the AXVoice SIP International Home service so I get unlimited voice to the US and 40+ countries and when one phone is busy, I can just pick up another and dial.  It comes out to about $18 / month.




The PBX is currently running on an Intel Atom mini ITX board (a D945GCLF2) running a headless version of FreePBX / Asterisk / Centos. (I think you could get this configuration running on just about any old PC you have laying around. I first brought it up on an old Dell tower.)

This past weekend, thanks to Technical How To  and Asterisk: the Definitive Guide,  I got a "raw" Asterisk instance running on Ubuntu 12.10.  This doesn't have all the bells and whistles of the Centos version, but it did give me a look at configuring Asterisk using it's own configuration files.  I brought up a simple 2 extension PBX connected to AXVoice just to demonstrate that it works.  It took a couple of hours, some digging in the Asterisk book and Google - and two config files.  In addition to being a great Asterisk technical reference, the book has some great background on telephony.

RPi CID on a tiny 3" screen



I'm still using my CID decoder running on the Raspberry Pi to produce a web page with the Caller ID, but I'm thinking I'll move this function to the PBX - where it belongs - and reuse the RPi for something else!

The idea behind this project was to A.) learn something about Asterisk; B.) get unlimited calling; C.) have some fun - and all of these objectives were met!   If you're at all interested in learning about telephony (or a home PBX) I'd highly recommend the exercise!

Friday, December 7, 2012

Caller ID project - part two

OK - it's been, ahem, "several" years since I've done any serious programming in C.

But, the way I see it, it's just like falling off a bike:  once you forget about it, it hurts more the next time you do it...

In any case, I've got a basic parser coded.  The pseudo code looks something like this right now:
Magically acquire Caller ID string 
Look for "MESG=80" in the input string 
If you find it 
    Retrieve the message length 
    While there's still message left to parse 
        Get the field type 
        Get the field length 
        Get the field 
    end while 
end if
I'm rather embarrassed to say that coding this took a couple of hours using Xcode on the Mac.  It's not pretty - it currently looks like the C equivalent of duct tape.  

Challenges:

Remember how to do string/array manipulation in C 
Convert strings of hex characters to numbers

Next steps
Clean up code (subroutine-ize it, abstract the code for field handling, make it pretty, etc.) 
Implement a read-string-from-USB function
Write date/time and number somewhere that the web server can find and use it
Figure out how to serve this information on a web server
Figure out how to get the information on an LCD display
Wire everything up
Put it in a case 
  




     
     

Sunday, December 9, 2012

Caller ID Project - part three


Today I did some cleanup of the code, migrated it to the Raspberry Pi and did some unit testing.

Below is the C program that reads the USB modem output and parses the results.  I used printf's to to output to the console just to verify that it's working.  I also wrote the output to an index.html file that I'm serving with Apache on the RPi.

It took some time to get this working on the Raspberry Pi.  For some reason the /dev/ttyACM0 port (that the USB Modem was hooked up to) had a problem with command echo and the status messages being "on" - so I turned them off.

It appeared that the status messages were causing some kind of "loopback" problem.  Odd, that.

I turned on the web server on the Raspberry Pi.  Now whenever anyone calls, the web page is updated and looks something like this - still have some formatting to do...


Date: 12-09
Time: 20:46
Number: 4195551212
Name: Amos Mark

Next steps:
- Figure out how to display to an LCD display hooked up to the RPi
- Get an LCD display hooked up and tested
- Wire everything together and do integration testing

Here's the code so far:

//
//  main.c
//  USB Reader
//
//  Created by Mark Amos on 12/7/12.
//  Copyright (c) 2012 Mark Amos. All rights reserved.
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int get_fld(char *, int);
int hex_to_ascii(int, int);
int hex_to_int(int);
int write_web_page(char *, char *, char *);

int main(int argc, const char * argv[])
{

    int  msg_len;
    int  msg_end;
    long fld_type;
    long fld_len;
    char fld_val [64] = "";
    int  str_ptr;
    char str_date_time [20] = "  /     :  ";
    char str_nbr [20] = "   -   -    ";
    char str_nam [64] = "";
    char str_input [256] = "\0";
    
    // Send configuration strings to modem
    // On Raspberry Pi, leaving echo on and status messages on causes problems with some kind of "loop back" on the
    // /dev/ttyACM0 port.  So, we have to turn them off.
    FILE *out_file;
    out_file = fopen("/dev/tty.usbmodem0000001", "w");
    if (out_file == NULL) {
        fprintf(stderr, "Can't open output file\n");
    }
    fprintf(out_file,"ATE0\n");                                    // Turn off echo
    fprintf(out_file,"ATQ1\n");                                   // Turn off status messages
    fprintf(out_file,"AT#CID=2\n");                            // Turn on unformatted Caller ID
    fclose(out_file);
    
    // Open modem for input
    FILE *in_file;
    in_file = fopen("/dev/tty.usbmodem0000001", "r");
    if (in_file == NULL) {
        fprintf(stderr, "Can't open input file\n");
    }    
    
    // loop getting input from modem
    while (fgets(str_input, 250, in_file) != NULL ){            // Check to see if there's input available
        if(strstr(str_input,"MESG=80")){                          // Check to see if we received a CID message
            
            // Get Message Length
            str_ptr = 7;                                                // position pointer to point at the message length field
            msg_len = get_fld(str_input, str_ptr);
            msg_end = (msg_len * 2) + 8;                       // Adjust for two bytes per hexx digit, plus "header" + length
            str_ptr+=2;                                                // position pointer to point to the start of first field
            
            while (str_ptr < msg_end){                           // Parse message while there's still input to be had               
                fld_type = get_fld(str_input, str_ptr);          // Get Field Type
                str_ptr+=2;
                
                fld_len = get_fld(str_input, str_ptr);            // Get Field Length
                str_ptr+=2;
                fld_len = fld_len * 2;
                
                int j = 0;                                              // Get Field Value
                for (int i=str_ptr; i< fld_len + str_ptr - 1; i+=2){
                    fld_val[j++] = hex_to_ascii(str_input[i], str_input[i+1]);
                    fld_val[j] = '\0';
                }
                
                str_ptr += fld_len;                                 // decode field values using field type
                switch (fld_type) {
                    case 1: {
                        strcpy(str_date_time, fld_val);
                    } break;
                    case 2: {
                        strcpy(str_nbr, fld_val);
                    } break;
                    case 4: {
                        strcpy(str_nbr, "No Number");
                    } break;
                    case 7: {
                        strcpy(str_nam, fld_val);
                    } break;
                    case 8: {
                        strcpy(str_nam, "No Name");
                    } break;
                }
            }                                                         // end while parse message
            printf("Date: %s\n",str_date_time);
            printf("Number: %s\n",str_nbr);
            printf("Name: %s\n",str_nam);
            write_web_page(str_date_time, str_nbr, str_nam);
        }
        else {                                                      // else extraneous input (i.e. not MESG=...)
            printf("%s\n",str_input);                          // shouldn't be any - we turned off echo and status messages
        }
    }                                                               // end while fgets
}                                                                   // end main()

// Write out the web page that shows date, time, number, name
int write_web_page(char *str_date_time, char *str_nbr, char *str_nam){
    FILE *web_file;
    web_file = fopen("/users/markamos/Sites/index.html", "w");  // for Raspberry Pi use /var/www/index.html
    if (web_file == NULL) {
        fprintf(stderr, "Can't open output file\n");
    }
    // format the date and time and output a record to the web server.
    fprintf(web_file,"<html><body>\n");
    fprintf(web_file,"<p>Date: %c%c-%c%c</p>\n",str_date_time[0], str_date_time[1], str_date_time[2], str_date_time[3]);
    fprintf(web_file,"<p>Time: %c%c:%c%c</p>\n",str_date_time[4], str_date_time[5], str_date_time[6], str_date_time[7]);
    fprintf(web_file,"<p>Number: %s</p>\n", str_nbr);
    fprintf(web_file,"<p>Name: %s</p>\n", str_nam);
    fprintf(web_file,"</body></html>\n");
    fclose(web_file);
    
    return 0;
}

// Convert input hex characters to integer
int hex_to_int(int c){
    int first = c / 16 - 3;
    int second = c % 16;
    int result = first * 10 + second;
    if(result > 9) result--;
    return result;
}

int hex_to_ascii(int c, int d){
    int high = hex_to_int(c) * 16;
    int low = hex_to_int(d);
    return high+low;
}

int get_fld(char *fldBuf, int bufPtr){
    char fldStr[3] = "00";
    char *p;
    fldStr[0] = fldBuf[bufPtr++];
    fldStr[1] = fldBuf[bufPtr++];
    return (int) strtoul(fldStr, &p, 16);
}

Monday, December 17, 2012

Caller ID Project - part eight

Testing is complete - works great.  I've created a shortcut on my iPad so that whenever the phone rings, I can immediately go to the web page to see who's calling.  The only thing that would be better would be for it to send me an immediate text message - I'll put that on the enhancement list.

Regarding a metal chassis for the project - since it uses a wireless connection to my network, I can't really encase the project in metal... (Duh...)  so I'll put the modem and RPi in a plastic box with power and phone jacks on one side. 

Also, I've decided not to include the LCD display.  Since the output is available to any browser on the network and since it's likely to be hidden behind a cabinet somewhere close to an otherwise unused phone jack, it just doesn't make sense.  Also, the LCD display (the one I was testing with) is an energy hog. Less heat, fewer parts -> more reliability.

plastic project box