Calling Processing with Asterisk
1-800-Processing from shiffman on Vimeo.
Code you need:
- Asterisk configuration: extensions.conf, runEAGI.sh
- JEAGI Client: JEAGIClient.java, Client.java
- JEAGI Server: JEAGIServer.java, ServerThread.java
- Processing examples: Asterisk.pde, AsteriskMulti.zip
Helpful Links:
- The Asterisk Site
- A Reference Guide to all things VoIP
- Shawn Van Every’s Redial Syllabus (Special thanks to Shawn for all of his help in getting these examples working!!)
Getting Started: Asterisk
This tutorial is designed to outline all the steps you’ll need to have your Processing sketch (or any TCP/IP client for that matter) receive a phone call. None of this will work without a telephone line as well as Asterisk (created by Mark Spencer in 1999), “the world’s leading open source telephony engine and tool kit.”
Asterisk is technically a PBX (“Private Branch Exchange”), a connnection between (usually) a private business and the PSTN (“Public Switch Telephone Network”). Asterisk, however, also supports VoIP and is configurable/scriptable by a number of different programming languages. In our case, getting the phone call into Processing will be a multi-step process.
- User makes a phone call.
- Asterisk answers call.
- Asterisk triggers shell script.
- Shell script launches Java application (JEAGIClient.java)
- JEAGIClient connects to local server (JEAGIServer.java)
- JEAGIServer passes data from the phone call from JEAGIClient to Processing
The remainder of this tutorial will assume that you have a telephone line and Asterisk running. The Asterisk support site provides instructions on how to install Asterisk.
Java Answers the Phone
Once you have Asterisk running, you will need to create a “Dialplan.” The Dialplan is controlled by a configuration file named “extensions.conf” and will control how incoming and outgoing calls are handled and routed. You can read more about extensions.conf at voip-info or on Shawn Van Every’s syllabus. For us, however, we only need to do one thing when a call is received: trigger a shell script which will in turn allow us to launch a Java process. Here is the example extensions.conf that I am using:
extensions.conf:
[dts204_default]
exten => _X,1,Goto(s,1);
exten => _X.,1,Goto(s,1);
exten => s,1,Answer();
exten => s,n,EAGI(/path/toyour/shellscript/runEAGI.sh);
The shell script itself launches the Java app:
runEAGI.sh:
#!/bin/sh
/usr/java/jdk1.5.0_10/bin/java -classpath /path/toyour/javaclass/ JEAGIClient $$
Incidentally, EAGI stands for “Enhanced Asterisk Gateway Interface”, which is what allows us to program for Asterisk in Java (the “E” is for controlling to the sound channel.)
So, just what does JEAGIClient.java do? The JEAGIClient reads input from and writes output to Asterisk. It can execute commands (such as “Wait for Digit”) and listen to activity on the phone call (such as “The digit 1 was pressed.”). It’s a pretty simple application that has a BufferedReader for input and an OutputStream for output. Here is a snippet of code showing these elements declared and initialized.
public class JEAGIClient
{
// EAGI Streams
BufferedReader bin; // input stream from asterisk
OutputStream out; // communication with asterisk
OutputStream err; // communication with asterisk
public JEAGIClient() {
out = System.out;
err = System.err;
bin = new BufferedReader(new InputStreamReader(System.in));
}
We can now issue commands to asterisk. The following will wait 10 seconds for the caller to press a digit.
// Get started waiting for digits
String wait = "WAIT FOR DIGIT -1n";
out.write(wait.getBytes());
We can also read the input:
String line = null;
boolean loop = true;
while ((line = bin.readLine()) != null && loop) {
// whatever asterisk tells us will show up here as the String line
// if we want to quit we can set loop = false (i.e. in the case of a hangup)
}
The phone number of the caller will show up as “agi_callerid:##########” and when a digit is pressed, a message will arrive in the form “200 result=49″ with 49 being the ASCII code for “1″. The JEAGIClient can therefore extract this information using some simple String parsing techniques:
String agi_callerid = "agi_callerid:";
int index = line.indexOf(agi_callerid);
if (index > -1) {
String phoneNumber = line.substring(index + agi_callerid.length()+1,line.length());
}
Simple Server in the Middle
Once the information is read from Asterisk, it needs to be passed to a server running locally (that Processing will also connect to). This is done by building a simple Java Client class that can be included inside JEAGIClient.java. The Client will run as its own thread and connect to localhost on an arbitrarily chosen port.
public class JEAGIClient
{
Client client;
public JEAGIClient() {
client = new Client("localhost", 9001);
client.start();
}
Later, when the phone number is parsed, the Client can send that number out to the server:
if (index > -1) {
String id = line.substring(index + agi_callerid.length()+1,line.length());
client.send(id);
}
The Client itself just needs three objects: Socket (to make a socket connection to the Server), a PrintWriter (to write out to the Server), and a BufferedReader (to read in from the Server).
Here is the full code for JEAGIClient.java and Client.java.
The Server itself is also quite simple and requires a ServerSocket (to listen for incoming connections). Each connection is farmed out to another class, which manages reading and writing to that connection as its own thread and added to an ArrayList. This is a very simple chat server.
ArrayList allConnections = new ArrayList();
ServerSocket server = new ServerSocket(9001);
while (true) {
Socket connection = server.accept();
ServerThread newConnection = new ServerThread(connection);
newConnection.start();
allConnections.add(newConnection);
}
The ServerThread class is quite similar to Client.java and includes a Socket, PrintWriter, and BufferedReader.
Here is the full code for JEAGIServer.java and ServerThread.java.
A quick note about putting this together. You’ll need whatever machine you are running Asterisk on to have Java installed, of course. You’ll need to compile and execute JEAGIServer.java on your own, however, for JEAGIClient, you only need to compile the classes since it will be executed by the shell script: runEAGI.sh. You’ll have to make sure that your paths are set up correctly in runEAGI.sh and that you are using the same port number in both the JEAGIClient and JEAGIServer.
Processing gets the Call
The quickest way to make sure everything is working is to just telnet to your server and make a call. You’ll see that you will receive the callerID along with digits pressed, etc. This is not automatic, this is how we set up JEAGIClient.java to broadcast the information (so you should feel free to design your own communication protocol). Once you’ve confirmed all the pieces are working, you are ready to connect to the JEAGIServer (on the machine running Asterisk) via Processing using the net library.
import processing.net.*;
// Declare a client
Client client;
void setup() {
client = new Client(this, "SERVER ADDRESS", 9001);
}
In draw(), you can check and see if any messages are available:
// If there is information available to read from the Server
if (client.available() > 0) {
String messageFromServer = client.readString(); // Read it as a String
}
And it’s as simple as that!
Here is a basic example that displays information from a call: Asterisk.pde.
A List of Call Objects
One of the first things you will likely want to do when receiving phone calls in Processing is spawn an object for every new caller. Most of the time when you want to have multiple objects, a simple array or even ArrayList will do. Arrays allow you to store an ordered list of data with each element accessible via its index (the location in the array).
In this case, however, an array or ArrayList is not a good data structure for storing multiple call objects. Consider the following scenario (which will continually occur in our program):
- Processing receives: “5558675309,newcall” Create a new object and add it to our ArrayList.
- Processing receives: “5558675309,5″ Find the object associated with the phone # 555-867-5309 and pass it the command “5″
- Processing receives: “5558675309,*” Find the object associated with the phone # 555-867-5309 and pass it the command “*”
In other words, when we look up an object in our list of objects, we want to be able to find it via its unique phone number, not via its index in an array. One solution would be to loop through the array and check each object’s phone number until we find a match, but this isn’t terribly efficient (although to be honest, with only a handful of callers, this really isn’t a big deal.)
It would be preferable to write code that resembles:
if (listOfCalls.contains("5558675309")) {
Call call = listOfCalls.get("5558675309");
call.issueCommand("5");
}
One data structure that will allow us to do this is a Hash Table. In Java, we’ll use the HashMap class. Here is a tutorial I wrote up last year about Hash Tables. Every object stored in a hash map must have both a key and a value. The key is what we use to look up the object, and the value is the object itself. We need the key to be unique and this is perfect since by definition every call has a unique key: the phone number itself! So, when we receive a message from the server:
“5558675309,newcall” or “5558675309,5″ or “5558675309,*” or “5558675309,hangup”
We should first split the message into two String tokens:
String[] tokens = msg.trim().split(",");
We can then check and see if the phone number is already a key in the HashMap (making a new object if not):
if (!calls.containsKey(tokens[0])) {
Call call = new Call(tokens[0],random(width),random(height));
calls.put(call.id,call);
}
We can also issue a command via the digit that was pressed:
Call call = (Call) calls.get(tokens[0]);
if (call != null) {
call.command(tokens[1]);
}
Or remove it if the caller hangs up:
if (tokens[1].equals("hangup")) {
calls.remove(tokens[0]);
}
The full example is available here: