INPUT MY OUTPUT
RESEARCH & EXPERIMENTS
Category: JavaScript

Rehuddle.com is a web application I built with Phil Groman that makes group calling as easy as individual calling. We built the web application in order to give small creative businesses the ability to setup conference calls without paying for enterprise level telephony systems. Our goal was to make conference rooms easier to create, easier to join, and easier to share.

Traditional conference rooms often come with frustrating user experiences where many callers can’t seem to get in, missed the invitation, or can’t seem to find the e-mail where the call details were provided. With Rehuddle, users are given several super simple ways to share: 1) click to automatically copy the call details to your clipboard 2) click to auto generate an e-mail with the call details 3) text message someone to join the room 4) share the room via facebook and 5) generate a call that places a caller directly in the room (no extension needed).

The site is built with Ruby, Asterisk, Javascript, Node.js, and MySQL. Each user that joins a “huddle” is provided an avatar based on the last four digits of their phone-number. This allows each user to be uniquely identified and understand who is on the call. The avatars are currently being generated using Node.js and Underscore.js. Node is being used to ping our mySQL server every second and pipe the results into a socket that the client can read by parsing JSON. Underscore.js is being used to compare the current list of callers with a previous list of callers. This allows the client to know which callers to add for each browser and which callers to remove. Each conference room also gets a unique URL. If a user creates a room called “Sony Meeting” – Rehuddle will auto generate a URL Rehuddle.com/sony-meeting where the call can live. This url generation is being done with Ruby utilizing Sinatra and Datamapper/MySQL. Within the room our copy to clipboard share feature is using a JQuery library called zClip. The SMS messages are being routed through a Twilio’s Ruby API, the Facebook share call-to-action is using the standard Facebook Developers share button, and the call generator is happening in Asterisk, but is routed through Flowroute through a SIP registry.

In future iterations Phil and I are looking to add document sharing features, editable name tags for each avatar, and potentially unique phone numbers for each room. We both feel that there’s a huge gap in the market for conference systems that are simple, easy to use, easy to share, and provide an informative and enjoyable web interface. We both look forward to more user testing to see where this product takes us. A big thank you goes to Chris Kairalla for teaching us Asterisk and helping us debug throughout the build!

Jabber Mouth is a website I made that uses Asterisk, Node, Socket.io, Ruby AGI, and JQuery in order to allow users to call into a website and control a mouth based on a users phones audio levels. The essence of this experiment is to understand how to control screens through your phone using Node.js. The illustration I used was appropriated from an illustrator I love: Jason Levesque. In a future iteration I plan to record the audio coming into the call and play it back to the user through the mouth. Overall, I found this code to be magical in the fact that one can pretty much send through anything once you open up a server port with node.js. A huge thanks goes to Chris Kairalla who is teaching us asterisk and node.
I’m currently learning node.js and was super excited to get a chat server to work. In this example I’m using telnet to connect to my server 3 times which generates three sockets where data is transfered in realtime between all parties. Below is the code:

var net = require('net') //TCP Server 
 
var sockets = [] //empty array for ppl who connect to server
 
var server = net.createServer(function(socket){
	sockets.push(socket); //add person to socket
 
	socket.on('data', function(data){
		for(var i = 0; i<sockets.length; i++){
			if(sockets[i] == socket) continue; //dont broadcast your own echo
			sockets[i].write(data); //when data is received write
		}
	});
 
	//remove socket from arary
	socket.on('end', function(){
		var i = sockets.indexOf(socket);
		sockets.splice(i,1); //remove person from array
		//could also do-- delete socket.[0]
	});
});
 
server.listen(8000);

The Arduino code is as follows:

#define FORWARD 10
#define REVERSE 20
#define STOPPED 30
 
#define forwardPin 6
#define reversePin 7
#define leftPin  8
#define rightPin 9
 
#define pulseDuration 500
#include <SPI.h>
#include <Ethernet.h>
//setup          10    A5    DA    00    85    70
byte mac[] = { 0x10, 0xA5, 0xDA, 0x00, 0x85, 0x70 };
IPAddress server (XXX,XXX,XXX,XX);  // my server
int port = 12002;
EthernetClient client;
 
unsigned long startPulseTime = 0;
int drivingState = STOPPED;
int inByte = 0;         // incoming serial byte
// The setup() method runs once, when the sketch starts
 
void setup()   { 
  // initialize the digital pin as an output:
  pinMode(forwardPin, OUTPUT);
  pinMode(reversePin, OUTPUT);
  pinMode(leftPin, OUTPUT);
  pinMode(rightPin, OUTPUT);
 
  Ethernet.begin(mac);
  Serial.begin(9600);
  delay(1000);
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // no point in carrying on, so do nothing forevermore:
    for(;;)
      ;
  } 
  Serial.println("Ethernet reday");
  serverConnect();
}
void serverConnect(){
  Serial.println("connecting to remote server...");
  if (client.connect(server, port)) {
    Serial.println("connected to remote server");
    client.println('A');
  } 
  else {
    Serial.println("remote connection failed");
  }
}
 
// the loop() method runs over and over again,
// as long as the Arduino has power
 
void loop() {
  // if we get a valid byte, read analog ins:
  if (client.available()) {
    // get incoming byte:
    inByte = client.read();
    //Serial.print(inByte);
    switch(inByte){
    case '2': 
      Serial.println("forward"); 
      forward(); 
      break;
    case '8': 
      Serial.println("reverse"); 
      reverse(); 
      break;
    case '4': 
      Serial.println("left"); 
      turnLeft(); 
      break;
    case '6': 
      Serial.println("right"); 
      turnRight(); 
      break;
    }
  } else {
    if (drivingState != STOPPED){
      if ((millis() - startPulseTime) > pulseDuration){
        Serial.println("pulse ended");
        powerStop(); 
      }
    }
  }
}
 
void forward(){
  drivingState = FORWARD;
  startPulseTime = millis();
  digitalWrite(reversePin, LOW);
  digitalWrite(forwardPin,HIGH);
}
 
void reverse(){
  drivingState = REVERSE;
  startPulseTime = millis();
  digitalWrite(forwardPin, LOW);
  digitalWrite(reversePin, HIGH);
}
 
void turnLeft(){
  digitalWrite(rightPin,LOW);
  digitalWrite(leftPin, HIGH);
}
 
void turnRight(){
  digitalWrite(leftPin, LOW);
  digitalWrite(rightPin,HIGH);
}
This week for my telephony class we experimented with Node.js to create a socket where information could be passed on a Rackspace Ubuntu web server. The socket essentially opened up two ports on my server to be able to transfer data to each other. I used Arduino and an Ethernet shield to connect to the socket and read the numbers my phone pressed. The phone presses got pushed into the open port via an AGI call.

The node code is as follows:

var agi_net = require('net');
var remote_net = require('net');
 
var REMOTE_PORT=12002;
 
var AGI_HOST = '127.0.0.1';
var AGI_PORT = 12001;
 
/**
 * AGI will only send key presses, and that's it.
 * There's no identifying characteristics, just raw press events.
*/
 
/** Remote clients send no info to this server.
 * all they do is receive byte representations of digits.
 * (0-9, #, *) on the phone's keypad as ascii bytes*/
 
//remote screens or physical objects
var remoteClients = [];
remote_net.createServer(function(sock){
  console.log('CONNECTED REMOTE CLIENT: ' + sock.remoteAddress +':'+ sock.remotePort);
  remoteClients.push(sock); //add client
      // Add a 'close' event handler to this instance of socket
    sock.on('close', function() {
        for(var i = 0; i < remoteClients.length; i++) {
            if(remoteClients[i] == sock) { //remove client
                remoteClients.splice(i,1);
                break;
            }
        }
        console.log('DISCONNECTED REMOTE CLIENT: ' + sock.remoteAddress +':'+ sock.remotePort);
    });
 
    // Add a 'data' event handler to this instance of socket
    sock.on('data', function(data) {
        for ( var i = 0; i < data.length; i++){
            handleByte(data[i]);
        }
        function handleByte(buf){
         console.log(buf);   
        }
 
    });
}).listen(REMOTE_PORT);
console.log('Server listening for remote connections on ' + REMOTE_PORT);
 
agi_net.createServer(function(sock){
  console.log('CONNECTED AGI CLIENT: ' + sock.remoteAddress +':'+ sock.remotePort);
      // Add a 'close' event handler to this instance of socket
    sock.on('close', function(data) {
        console.log('CLOSED AGI CLIENT: ' + sock.remoteAddress +':'+ sock.remotePort);
    });
 
    // Add a 'data' event handler to this instance of socket
    sock.on('data', function(data) {
        console.log(data);
        //broadcast data to all remote clients
        for (var i = 0; i < remoteClients.length; i++){
            var client = remoteClients[i];
            client.write(data);
        }
    });
}).listen(AGI_PORT, AGI_HOST);
 
console.log('Server listening for AGI connections on ' + AGI_HOST +':'+ AGI_PORT);

The AGI script is as follows:

#!/usr/bin/ruby
 
#very simple implementation of asterisk -> server -> client stack
#only sends the digit and the caller id.
#designed for very simple control, like arduino or web page
 
require 'rubygems'
require 'ruby-agi'
require 'socket' 
 
agi = AGI.new
host = 'localhost'
port = 12001
looping = true
s = TCPSocket.open(host, port)
agi.stream_file("vm-extension")
while looping
    result = agi.wait_for_digit(-1) # wait forever
	if result.digit
        s.write "#{result.digit}"
	else #hangup broke the pending AGI request
        looping = false
    end
end

The asterisk dialplan is as follows:

[rtt233_socketIO_arduino]
exten => 1,1,Answer();
exten => 1,n,AGI(/home/rtilton1/asterisk_agi/socketIO_arduino.rb);
exten => 1,n,Hangup();

All code examples and knowledge are curtesy to our awesome professor Chris Kairalla! I’m excited to continue on node and download the free book/pdf found here: http://visionmedia.github.com/masteringnode/