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.

No comments:

Post a Comment