Showing posts with label DTMF. Show all posts
Showing posts with label DTMF. Show all posts

Thursday, August 5, 2010

Arduino-Twilio Dialer Application

Well, it has been a while since I posted an Honest-to-God Arduino project here! What with getting my eye surgery done and one thing and another, this has taken me quite a while. Besides, there were more than a few challenges in getting it all to work along the way!

This project is a concept for an information kiosk or unit that could be placed in a public area where passers-by could request information. A user enters their mobile phone number into the keypad and they are then immediately called by the Twilio cloud-based telephony system (check out Twilio here) and presented with a simple phone menu that allows them to:
  1. Talk to an operator
  2. Leave a voice message
  3. Receive an SMS message
Here is the hardware in it’s not very pretty form:








































Here is the obligatory video showing how it works:




Hardware

The hardware consists of:
  • Arduino Duemilanove
  • Arduino Ethernetshield
  • Sparkfun serial enabled 20 x 4 LCD display (LCD-09568)
  • Sparkfun 12 button keypad (COM-08653)
Here is the circuit diagram:


The hookup of the keypad is based on this article by amando96 on Instructables. You will have to sign up for membership to see the whole article or you can just follow the above diagram. One odd thing I noticed was that the pull down resistors turned out to not be required. Originally I had them included and everything worked fine, then at a certain point in the development the keypresses started looping and I removed the resistors and everything worked fine!

This just uses the standard “keypad.h” library found on the Arduino website, but slightly modified so the pins do not interfere with the Ethernetshield. There is also an Instructable post on multiplexing the button input to use fewer digital pins, but since I didn’t need any extra digital pins I figured this was simpler.

The serial LCD is quite straightforward to hookup, but just note that I am using one of the A1 Analog pin as the Tx for the serial since I am not using any analog inputs anyway.

Software

As with the previous Twilio-Arduino project, this one requires:
  • An account with Twilio 
  • A web-server with PHP
  •  An Ethernetshield
The Arduino communicates with one PHP script running on my web-server, this then triggers Twilio to set up a call and Twilio then goes to my web-server and checks for another PHP file on what options to present to the user. It is a reasonably complex mesh of files that need to be setup, but actually the Twilio XML programming was easily the simplest part of the setup.

This diagram shows the overall flow:





The first part is the Arduino software:





/*
*  Twilio Arduino Information Service Dialer
* When used in conjunction with "dialer_twilio.php PHP script and the Twilio cloud-based
* telephony environment (www.twilio.com) this allows a user to enter a phone number into a keypad
* on an Arduino and have a call placed to their cell phone with various options for information.
*
* Uncomment the //Serial lines for troubleshooting/debug info.
*
* This code is in the public domain. Please provide credit if it is used
* in another project.
* 
* written by Chris Armour, August 4th, 2010
*
* Full description psted at http://opensource-torchris.blogspot.com/
*
*/

//=====================Libraries=============/
#include 
#include 
#include  
#include 

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //MAC address for Arduino
byte ip[] = { 192,168,0,34 }; // IP address you wish to assign to Arduino
byte server[] = { 192, 168, 0, 171 }; // IP address of your PHP server
int ServerPort = 55455;
const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns
char keys[ROWS][COLS] = {
  {'1','2','3'},
  {'4','5','6'},
  {'7','8','9'},
  {'*','0','#'}
};
byte rowPins[ROWS] = {5, 4, 3, 2}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {8, 7, 6}; //connect to the column pinouts of the keypad
boolean ConnectedState = false;
char DialNum[12]={"xxxxxxxxxxx"};
int a;
NewSoftSerial mySerial =  NewSoftSerial(14,15); //osft serial running off of Analog Pin 1

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
Client client(server, ServerPort); 
TextFinder finder( client);

void setup(){
  Ethernet.begin(mac, ip);
// Serial.begin(9600);
  mySerial.begin(9600); 
  mySerial.print(254, BYTE);
  delay(100);
  mySerial.print(1, BYTE);
  delay(100);
  mySerial.print("Started - Press any key to conect to the server."); //Display initial message on LCD.
}
  
void loop(){
    
  char key = keypad.getKey();

  if (key != NO_KEY){
    if (ConnectedState == false){
       if (client.connect()){      
         if(finder.find("results:") == true){
           char c = client.read();
         // Serial.print("Value of the connect code: ");
         // Serial.println(c);
           mySerial.print(254, BYTE);
           delay(100);
           mySerial.print(1, BYTE);
           delay(100);
           mySerial.print("Connected. ");
           delay(2000);
           WelcomeMsg();    
           //If connection successful, then print Welcome Msg and flip the connected state variable.
           ConnectedState = true;
         // Serial.println(DialNum);
           }
       }
       else {
       ConnectedState == false;
       ConnectFailMsg(); //If connection fails, then print an error.
       delay(2000);
       }
    }
    if (key == '*') {
    //When it picks up the "*" then it starts gathering the digits.
    // Serial.println("Got the *");
      EnterNumMsg();       
      do {
        key = keypad.getKey();
        if (key != NO_KEY){
          delay(150);
        // Serial.println(key);
          mySerial.print(key); //Prints the keypad input to the LCD for user feedback.
          DialNum[a] = key; //Puts the key info entered into an array.
          a++;
          }

        } while (key != '#');
        
      }
      if (key == '#'){
        //When the "#" is pressed, the info is sent over to the PHP script.
         delay(250);
         mySerial.print(DialNum);
         client.println(DialNum); //Dialnum array sent to PHP.
         if(finder.find("results:") == true){//PHP script evaluates number & sends back response.
             char c = client.read();
           // Serial.print("Value of the return code: ");
           // Serial.println(c);
             if (c == 'Y'){//Y back from the script means it is a valid number & success displayed.               
               NumAcceptedMsg();
               }
             if (c == 'N'){//If it is not a valid number, then N returned & error displayed.
               NumNotAcceptedMsg();
//               client.println(DialNum);
               delay(3000);
               WelcomeMsg();//Go back to Welcome message.
               }
         delay(200);
         if(finder.find("results:") == true){
             char c = client.read();
           // Serial.print("Value of the return code: ");
           // Serial.println(c);
             if (c == 'O'){//If the call was successully placed display success then go back to Welcome.
              NumCalledMsg();
              delay(4000);
              WelcomeMsg();
         }
          if (c == 'X'){//If the call couldn't be made then error message & return to welcome.
              NumNotCalledMsg();
              delay(3000);
              WelcomeMsg();
         }
        } 
   
  // Serial.println(DialNum);
  // Serial.println(a);
    a = 0;
      }
    }
  }
}

void WelcomeMsg(){
       mySerial.print(254, BYTE);
       delay(100);
       mySerial.print(1, BYTE);
       delay(100);
       mySerial.print("Welcome to the      Information service.Press the * key to  start.");
  }
    
void EnterNumMsg(){
       mySerial.print(254, BYTE);
       delay(100);
       mySerial.print(1, BYTE);
       delay(100);
       mySerial.print("Enter your 10 digit phone number        followed by #:      ");
}  

void ConnectFailMsg(){
       mySerial.print(254, BYTE);
       delay(100);
       mySerial.print(1, BYTE);
       delay(100);
       mySerial.print("Connection Failed.  Press any key to    reconnect.");
}

void NumAcceptedMsg(){
      // Serial.println("Number accepted by server");
         mySerial.print(254, BYTE);
         delay(100);
         mySerial.print(1, BYTE);
         delay(100);
         mySerial.print("Number Accepted. ");
}

void NumNotAcceptedMsg(){
        // Serial.println("Number NOT accepted by server");
           mySerial.print(254, BYTE);
         delay(100);
         mySerial.print(1, BYTE);
         delay(100);
         mySerial.print("Number not valid.");
}

void NumCalledMsg(){
            // Serial.println("Number called successfully.");
               mySerial.print(254, BYTE);
               delay(200);
               mySerial.print(1, BYTE);
               delay(200);
               mySerial.print("Number called       successfully.");
}

void NumNotCalledMsg(){
// Serial.println("Number cannot be called.");
  mySerial.print(254, BYTE);
               delay(100);
               mySerial.print(1, BYTE);
               delay(100);
               mySerial.print("Number cannot be    called.");
}


This uses the Arduino Ethernet library to set up a client application, the Keypad library to get the keypresses, NewSoftSerial to send information to the LCD and finally the TextFinder library to parse the result codes from the server. I have put all the LCD messages into functions just to clean up the code since each message needs to have a screen clear send and some delay. I’m sure I could further clean that up to just have a function that did the screen clear, but this works.

The Arduino client in term communicates with the PHP server program (dialer_twilio.php):


request("/$ApiVersion/Accounts/$AccountSid/Calls", 
     "POST", array(
     "Caller" => "XXXXXXXXXXXXXXX",
     "Called" => $dial_num,
     "Url" => "http://xxxxx.xxxxx.xxx/twi_test.php"
    
    ));
    if($response->IsError){
     echo "Error: {$response->ErrorMessage}\n";
   socket_write($spawn, "results:X\n");
   echo "Sent over X\n";
  }  else {
     echo "Started call: {$response->ResponseXml->Call->Sid}\n";  
   socket_write($spawn, "results:O\n");
   echo "Sent over O\n";
   }
} else {
  echo ("Invalid number\n");
  socket_write($spawn, "results:N\n");
  echo "Sent over N\n";
 }

   usleep(5000);
}
}while(true);

?>




Anything marked “XXXX” above is info on the account and phone numbers to be used.

This script does a quick and dirty check if the number is valid (just making sure there are no stray “x” or “#” characters and it is the right length) then uses theTwilio REST API to  set up the call. Twilio will go to the “twi_test.php” script for info on how to handle the call.

So, when Twilio goes to twi_test.php it sees:




    header("content-type: text/xml");
    echo "\n";
?>



    Hello. Welcome to the Arduino Information Service.
    Press 1 to be connected to an operator.
    Press 2 to leave a voicemail.
    Press 3 to receive an SMS message.
    Press 4 to repeat this menu.
    Press 5 to hangup.


This sets up the top level menu of the simple IVR and then directs to go to “twi_action.php” once a button is pushed. The XML such as and are the Twilio “verbs” that defines actions on the Twilio cloud system.

So here is “twi_action.php";


$stringData = $_REQUEST['Digits'];

switch($stringData){
case 5:
    echo "";
    echo " Thank you, Good bye.";
    echo "";
    echo "";
    break;
case 4:
    echo "";
    echo "";
    echo "http://xxxxx.xxxxx.xxx/twi_test.php";
    echo "";
    echo "";
    break;
 case 3:
    echo "";
        echo "You are being sent an SMS message.";
        echo "You are receiving this message from the Twilio Arduino dialer application.";
    echo "";
    break; 
case 2:
    echo "";
    echo "Leave your message after the tone and press # when you are done.";
    echo "";
    echo "";   
    break;
case 1:
    echo "";
        echo "XXXXXXX";
        echo "Goodbye";
    echo "";       
    break;
}

?>


This parses the digit pressed from the POST command and then takes action on the digits, which is either:



  1. Connect to an operator (in this case it dials through to my Skype account).
  2. Leave a voicemail. Currently this records a sound file which you can download from the Twilio website, but it could also have the sound file sent to an email address or transcribe the voice into text and have that sent along.
  3. Receive and SMS message. This sends a canned message that could be some general information to the cell phone that made the call.
  4. Repeat the menu.
  5. Hangup.
The final small script is the one that handles the voicemail recording (voicerecorder.php):



    header("content-type: text/xml");
    echo "\n";
?>

    Thanks for the message.  Here is what you recorded.
    
    Goodbye.


This one is very simple. It just plays back the message that was just recorded. That’s all there is to it! :-)

Conclusion

This project could have some practical uses as a low cost way to place a simple information service in some public place. It does, however, have the obvious limitation that there is no validation that the number entered really does belong to the user’s cell phone and not to some little old lady in Detroit, so in it’s current form it could just be a nuisance generator. This could probably be fixed with an exchange of a PIN via SMS.

What other things could this kind of project be used for? Here are some ideas:


  • Make a super secure lock by combining this project with an RFID reader lock so that once the card is swiped the user also has to enter a PIN and a code received via SMS at a cell phone that has been pre-registered with the system. This way an intruder would have to steal not only the users RFID badge, but also their phone and their PIN number.
  • This could also be adapted to trigger phone calls or SMS messages for various conditions driven by digital or analog inputs - such as temperature readings or intruder alarms. Conversely, as discussed in my Twilio LED project, a call could be used to control switches, motors or valves remotely using a telephone.
  • If it used Power Over Ethernet it would eliminate the extra power supply requirement. Even better would be using wi-fi or GSM cellular data to eliminate the need to have it wired to a router.
  • Clever use of the PROGMEM would probably allow all the response strings to be stored on the Arduino so it might be possible to dispense with using an outside webserver altogether!
This was a pretty challenging project and I hope it provides inspiration for some further cool projects!


Friday, May 7, 2010

Arduino Phone Control via Twilio

Since Jeff from Twilio was nice enough to leave a comment on my last post, I have put together what must be my quickest project ever! This is just a very simple demonstration proof-of-concept to show the most basic of integration, but it does work. Using the Twilio cloud-based telephony API (www.twilio.com), this project uses a very basic IVR menu to control an LED on an Arduino. As a digital output, of course, the LED could be a relay controlling an AC current doing home automation or any number of digital control devices. This is a very simplistic project which only shows a small portion of what can be done with Twilio. Hopefully in the next few weeks I can do something more sophisticated when I understand their API better.

First, here is the obligatory video:



The hardware is a very simple Diecimila + Adafruit Ethernet Shield sandwich:




I have the LED going to digital output pin 11.



So, how does it work? The Twilio system is really more geared to allowing web-based control of telephony functions and to quickly build IVRs and messaging systems in with a cloud-based back-end, so using to control physical hardware is a bit of a hack. Fortunately, they have a simple to use API and lots of PHP-based example code available.

What is involved, however, is having a bunch of scripts/files all working together to pass the info down to the Arduino in a format it can digest. Here is a general flow of how it works:



The first file is the PHP file that generates the XML read by Twilio:

<?php
    header("content-type: text/xml");
    echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
?>

<Response>
<Say>Hello</Say>
<Gather action="twi_do.php" numDigits ='1' method="POST">
    <Say>Press 1 to turn on the L.E.D. Press 2 to turn it off. Press 3 to repeat this menu and press 4 to hang up.</Say>
</Gather>
</Response>

This file is on my personal webserver and accessible to Twilo. When I call the access number, Twilio goes to this webpage and acts on the XML instructions presented. The XML uses the Twilio verbs "Say" to repeat the menu options and "Gather" to get the keypresses. Here is the twi_do.php script that is called by the above code:

<?php

$stringData = $_REQUEST['Digits'];

switch($stringData){
case 4:
    echo "<Response>";
    echo "<Say> Thank you, Good bye.</Say>";
    echo "<hangup/>";
    echo "</Response>";
    break;
case 3:
    echo "<Response>";
    echo "<Redirect>";
    echo "http://xxx,xxxxxx,xxx/twi_test.php";
    echo "</Redirect>";
    echo "</Response>";
    break;
case 2:
    echo "<Response>";
    echo "<Say> You have turned the L.E.D. off.</Say>";
    echo "<Redirect>";
    echo "http://xxx,xxxxxx,xxx/twi_test.php";
    echo "</Redirect>";
    echo "</Response>";
    $myFile = "twi_File.txt";
    $fh = fopen($myFile, 'w') or die("can't open file");
    fwrite($fh, "NUM" . $stringData);
    fclose($fh);
    break;
case 1:
    echo "<Response>";
    echo "<Say> You have turned the L.E.D on.</Say>";
    echo "<Redirect>";
    echo "http://xxx,xxxxxx,xxx/twi_test.php";
    echo "</Redirect>";
    echo "</Response>";
    $myFile = "twi_File.txt";
    $fh = fopen($myFile, 'w') or die("can't open file");
    fwrite($fh, "NUM" . $stringData);
    fclose($fh);
    break;
}

?>

This responds to the keypress value by presenting the different prompts and, in the case of the digits 1 and 2 it writes the value to a small file (twi_File.txt) which just contains either "NUM1" or "NUM2". So, now we have the kepress value stored in a locally accessible file!

Running along with my webserver is a small PHP sockets server program (Arduino_twilio.php):

<?php

$host_ard = "192.168.0.171";
$port_ard = "55455";

//=====================Create Socket to listen for Arduino======================//

ob_implicit_flush();

//Open socket to listen for Arduino
$socket_ard = socket_create(AF_INET, SOCK_STREAM, 0) or die ("Could not bind to socket\n");
$result_ard = socket_bind($socket_ard, $host_ard, $port_ard) or die("Could not bind to socket\n");

// start listening for connections
$result_ard = socket_listen($socket_ard, 3) or die("Could not set up socket listener\n");
// accept incoming connections
// spawn another socket to handle communication
$spawn = socket_accept($socket_ard) or die("Could not accept incoming connection\n");
// read client input
socket_write($spawn,"C\n");
echo "Device connected.\n";

do{

$input = socket_read($spawn, 2048, PHP_NORMAL_READ) or die("Could not read input\n");

echo ("This is the value of input: " . $input . "\n");

if (trim($input[0]) == "C"){
    usleep(10000);
    $data = file_get_contents("twi_File.txt");
    echo ("The value of data is: " . $data . "\n");
    socket_write($spawn, "X" . $data . "\n");
}

    usleep(10000);

}while(true);

?>

If a client is connected to this server, then the script periodically reads the twi_File.txt file and presents it on to the Arduino client when requested. Given the simplicity of this project, I could probably have dispensed entirely with a "preprocessor" script like this and done all the manipulation on the Arduino, but this was just easier to do since I had the other code lying around.

Finally, here is the Arduino code:

#include <NewSoftSerial.h>
#include <AF_XPort.h>

#define XPORT_RXPIN 2
#define XPORT_TXPIN 3
#define XPORT_RESETPIN 4
#define XPORT_DTRPIN 5
#define XPORT_CTSPIN 6
#define XPORT_RTSPIN 7
#define IPADDR "192.168.0.171" //IP Address of the Arduino Server
#define PORT 55455 //IP port of the server

AF_XPort xport = AF_XPort(XPORT_RXPIN, XPORT_TXPIN, XPORT_RESETPIN, XPORT_DTRPIN, XPORT_RTSPIN, XPORT_CTSPIN);

uint8_t ret;
int ledPin = 11;
char linebuffer[16];

void setup() 
{
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
  Serial.println("Starting up!");
  // set the data rate for the NewSoftSerial port
  xport.begin(9600);

}

void loop()                     // run over and over again
{

       xport.connect(IPADDR, PORT);
       delay(300);
       delay(300);

  ret = xport.readline_timeout(linebuffer, 32, 600); // get first line
  Serial.println(linebuffer);
  if (linebuffer[0] == 'N'){
    if(linebuffer[3] == '1'){
      digitalWrite(ledPin, HIGH);
    }
    if(linebuffer[3] == '2'){
      digitalWrite(ledPin, LOW);
    }
  }

}

This is based on the Adafruit AF_Xport library and when the Xport connects it sends out a C plus the IP address and port. When the PHP server receives the "C" it sends the value to the Arduino and this program then either turns the light on or off depending on what it gets back.

So what?


Well, like the email projects, this is also a good way to do remote control since simple DTMF telelphony is ubiquitious. If you have a cell phone, you could call into your Arduino from anywhere in the world and control household appliances, monitor temperatures, control a robot - anything in the huge universe of whacky stuff Arduino builders build!

What's next?


Of course, this project really just uses the very simplest of features of the Twilio API. I think next up I will looking into how an Arduino could be made to send out an SMS or do a click-to-call implementation.