Wednesday, July 08, 2009

Breath-over-IP


The "Breath-over-IP" project is a PHY2PHY project to deliver someone's breath across the Internet. It is based on an Arduino with a double stack of shields. The first shield is a SparkFun Arduino protoshield where I mount some resistors and a solid-state relay. I used wire-wrap sockets to provide a stand-off from the Arduino:

Protoshield with wire wrap sockets

Then on top of that I added an AdaFruit Xport shield, which I populated with a Lantronix Xport Direct:


Stacking Arduino, Protoshield, and Ethernet shield

Arduino, Protoshield, and Ethernet shield

Now I had to build the Breath sensor. I tried using gutted case fans, but they were always a bit too hard to blow. Here I am testing an IR led and IR phototransistor on a breadboard with a gutted case fan:

IR emitter/detector pair and fan

I eventually decided on using a replacement Kestrel anemometer impeller. To provide some kind of structure to mount the IR LED and phototransistor to, I used FastSteel:

FastSteel FastSteel Kestrel Anemometer Impeller Impeller encased in FastSteel

Then I added the side-looking NTE 3029B IR LED (with a 220 Ohm current-limiting resistor off 5V) and NTE 3034A IR Phototransistor detector (with a 10K Ohm pull-up to 5V) held in place with a bit more FastSteel:

Breath Impeller

And for flexibility, I mounted it on a mini-tripod:

Breath Impeller Breath Impeller System

For the "first side" of Breath, I used a solid-state relay capable of AC or DC operation. I figured I might want the Breath to drive a full-sized AC fan, so the "output" is an AC plug. I found a powerful, yet compact AC fan for standard operation. Then I tested it and mounted it in a box:

Breath box in testing

Breath Box


"Home Base" Breath System

I realized that I would be shipping the "other side" of Breath all over the planet, so perhaps it should be a little more compact, and DC only so I wouldn't have to worry about different AC voltages/frequencies in other countries. So I found a powerful 12VDC fan. I mounted the impeller and IR LED and phototransistor into a piece of PVC pipe, and JB Welded the pipe on top of the fan. The pipe is long enough so that the wind from the fan doesn't make the impeller spin, as it is very sensitive! The result:

"Mobile" Breath System

Here is the Arduino program, which used the NewSoftSerial library to talk to the Xport Direct:
#include NewSoftSerial.h

// breath

unsigned long time = 0; // time of last edge
int minTime = 25; // minimum time for edge
int good = 0; // number of consecutive good (fast) edges
int minGoods = 20; // minimum number of consecutive good (fast) edges to indicate breath

int ledPin = 13; // on-board LED pin
int irLedPin = 10; // IR LED attached to pin 12 with 220 ohm resistor
int irDetPin = 0; // IR phototransistor attached to analog 0 with 5V and 10K
int relayPin = 11; // solid-state relay pin

unsigned long onTime = 0; // relay on time'
unsigned long pingTime = 0; // time to "ping" server

int i=0; // counts up received breath packets for diagnostics
int light=0; // whether IR detector sees light or dark
int lastLight=0; // what IR detector saw last time

unsigned long sendTime=0; // last time breath packet was sent out

NewSoftSerial xport(3,2);

void setup()
{
pinMode(relayPin,OUTPUT);
digitalWrite(relayPin, LOW);

Serial.begin(57600);
xport.begin(9600);

pinMode(ledPin, OUTPUT);
pinMode(irLedPin, OUTPUT);
digitalWrite(irLedPin, HIGH);
Serial.print("Starting"); //for debugging
}

void loop()
{
if(analogRead(irDetPin)>850){
light=0;
}
else{
light=1;
}

if(light != lastLight){
// edge
if(millis()-timeminGoods) {
// enough good (fast) edges
digitalWrite(ledPin,HIGH);
if(millis()-sendTime>100){
// send a breath, not too fast though
xport.print("B2");
sendTime = millis();
}
}
else{
// not enough good (fast) edges
digitalWrite(ledPin,LOW);
}

if(millis() - onTime > 100){
digitalWrite(relayPin,LOW);
}

if (xport.available()) {
if((char)xport.read()=='B'){
// receive a breath
digitalWrite(relayPin,HIGH);
onTime = millis();
//Serial.println(onTime);
}
}
if(millis() - pingTime > 1000){
xport.print("A2");
pingTime=millis();
}
}
The last thing needed is a "meet in the middle" server. The two sides of breath send UDP packets to the server. Every second they send a "ping" to make sure the server knows where they are (and this also helps to "punch out" through a firewall). Then when the sides sense a breath, they send a "breath" signal to the server, which relays the breath signal to the other side. Here is the Python program:
from socket import *
s=socket(AF_INET,SOCK_DGRAM) # create UDP socket
s.bind(('[ip address]',[port])) # bind to port
count=0
a1=('10.0.0.0',80) # will hold address of Breath side 1
a2=('10.0.0.0',80) # will hold address of Breath side 2
while 1:
[msg,addr]=s.recvfrom(128) # receive packet of up to 4 bytes
print(addr,':',msg)
if(msg[0:2] == "A1"): # address "ping" from Breath side 1
a1=addr
if(msg[0:2] == "A2"): # address "ping" from Breath side 2
a2=addr
if(msg[0:2] == "B1"): # breath sensed on Breath side 1
a1=addr
s.sendto("B",a2) # relay breath to Breath side 2
if(msg[0:2] == "B2"): # breath sensed on Breath side 2
a2=addr
s.sendto("B",a1) # relay breath to Breath side 1

1 comment:

Son of The Wind said...

Blow-over-IP

I will get the trademark for: Blow-over-IP ©

:-)