A Physical Object Calls You

We’ve already experimented with a “click to call” web app that will generate a call file using the parameters included in the URL.  We can use a similar strategy to generate a call from a physical object using an Arduino and the Arduino Ethernet shield.  In fact, the script is much simpler, as we don’t want any front-end interface.  We simply want the page to generate a call and to reply with a simple “success/failure” response.

Here’s the script that I’m using in Sinatra.

require 'sinatra'

#generates a call file that will connect to the arduino context
def gencallfile(numbertocall)
  time = (Time.now.to_f * 1000).to_i #current timestamp
  temp_dir = "/tmp/"
  callfile = "call_" + time.to_s + ".call"
  startcallfile = temp_dir + callfile
  end_dir = "/var/spool/asterisk/outgoing/"
  endcallfile = end_dir + callfile
  #write file to disk
  file = File.open(startcallfile,"w")
  file.puts("Channel: SIP/flowroute/#{numbertocall}\n")
  file.puts("MaxRetries: 1\n")
  file.puts("RetryTime: 60\n")
  file.puts("WaitTime: 30\n")
  file.puts("CallerID: Arduino <9175346464>\n")
  file.puts("Context: ck987_arduinocall\n")
  file.puts("Extension: s\n")
  file.close
  #change file permission
  File.chmod(0777, startcallfile)
  FileUtils.chown(ENV['USER'],'asterisk',startcallfile)
  #move file to /var/spool/outgoing
  FileUtils.mv(startcallfile,endcallfile)
end

# Main route  - this is the form where we take the input
get '/' do
	call_number=params[:callnumber]
	if call_number
		gencallfile(call_number)
  		# params[:callnumber] will be used to generate a call file
  		# which will play back a message from the "Arduino".
  		"Called #{call_number}"
  else
  		"missing \"callnumber\" param"
  end
end

extensions.conf context:

;---- Arduino call
[ck987_arduinocall]
exten => s,1,Answer()
exten => s,n,Playback(/home/ck987/asterisk_sounds/hit_a_button)
exten => s,n,Hangup()

I can test this code in any browser to make sure it’s working:

http://asterisk.itp-redial.com/~ck989/sinatra/ard_call?callnumber=16469998888

The trick is to replace the browser with an Arduino.  Using an Ethernet shield or WiFi shield you can have the arduino hit the URL with a predefined phone number.  The possibilities are endless as to what should actually trigger the phone call.  It could be anything from the push of a button to sensor values determining that an event has happened in the physical world.  My demo uses a button press, but other projects have used various sensors.  For example, Botanicalls used a moisture sensor to trigger a phone call when a plant’s soil was too dry.

Here’s the Arduino code for my very simple button press demo:

/*
This code is designed to generate a call when ever a button is pushed.  The httpDelay
variable will determine how long the code should wait before generating another call.
This is so a flaky button won't generate dozens of calls to my phone.

The HTTP_ACTIVE and HTTP_IDLE states are used to make sure that you don't start a request
before the last request has finished.
*/

//state constants
#define HTTP_ACTIVE 10
#define HTTP_IDLE 20
#include <SPI.h>
#include <Ethernet.h> 

//setup          00    AD    BE    EF    ED    1A
byte mac[] = { 0x00, 0xAD, 0xBE, 0xEF, 0xED, 0x1A };
IPAddress server (166, 78, 48, 135); // redial-asterisk web server
//client stuff
char httpCommand[] = "GET /~ck987/sinatra/ard_call?callnumber=19997778888 HTTP/1.0";
EthernetClient client;
byte httpState = HTTP_IDLE;
int httpDelay = 5000; //wait at least this long in millis before sending another request
unsigned long connectTime = 0;

//button stuff
const int buttonPin = 5; 
int buttonState = 0;

void setup() {
    Serial.begin(9600);
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // no point in carrying on, so do nothing forevermore:
    for(;;)
      ;
  }
  // give the Ethernet shield a second to initialize:
  delay(1000);
  Serial.println("ready.");
}

void loop() {
  //read input from server, if any.
  if (client.available()) {
    char c = client.read();
    Serial.print(c);
  }
  //check button, only read if there are no active http connections and we're past minimum delay
  if (httpState == HTTP_IDLE && pastHttpDelay()) {
    buttonState = digitalRead(buttonPin);
    if (buttonState == HIGH) {
      serverConnect();
    }

  }
  //close client if disconnected
  if (!client.connected() && httpState == HTTP_ACTIVE) {
    client.stop();
    httpState = HTTP_IDLE;
    Serial.println();
    Serial.println("disconnected.");
  }
}

boolean pastHttpDelay(){
  if ((millis() - connectTime) > httpDelay) {
    return true; 
  } 
  else {
    return false; 
  }
}

void serverConnect(){
  Serial.println("connecting to asterisk.itp-redial.com");
  if (client.connect(server, 80)) {
    httpState = HTTP_ACTIVE;
    connectTime = millis(); //when did we connect?
    Serial.println("connected");
    client.println(httpCommand);
    client.println();
  } 
  else {
    httpState = HTTP_IDLE;
    Serial.println("connection failed");
  }
}

And here’s a video of the magic: