Monday, May 31, 2010

PRK - Post Surgery Day 3

Well, a bit of a late post today because unfortunately the day hasn't gone so well.

Initially I had high hopes I would be getting the bandage contacts out today and all would be right with the world and my prescription would be starting to lock in somewhat. The doctor had told me to be sure to hit my eyes with artificial tears every five minutes for an hour before my appointment so that they would be well lubed up to slip out, which I dutifully did.

When I got there he did the usual microscope routine and pronounced the epithelium healed enough to allow taking the lenses out. He took out the right lens just fine, but when he went for the left lens it unfortunately tore at the new layer of skin. Amazingly, the pain is pretty much what you would expect from ripping freshly grown skin off your eyeball - excruciating.

For pretty much the whole day I have had to keep my left eye closed as much as possible since the sensation of opening it had been akin to having glass rubbed in my eye. Initially, I came home and had a Percocet which just caused me to have a very weird, hallucinogenic nap then later I had Tylenol and Tylenol with codeine. Now, about 7 hours after it happened I can keep my left eye open a bit while I type this, but it definitely still hurts a lot. Of course, this is pretty discouraging as well as painful since I was hoping today would really be when I turned a corner. I can read the shampoo bottle labels in the shower, which is more than I ever could without my glasses, so there is some progress.

The right eye does feel quite good, fortunately, but the prescription is still very off. My doctor says it should be starting to improve, but then he's the guy who just ripped the skin off my eyeball!

Sunday, May 30, 2010

PRK - Post surgery Day 2

OK, this is supposed to be the worst day and it really is. I can barely see what I am writing even with the screen fonts & browser scaling turned up full. My vision today is still somewhat better than my previous uncorrected vision, but lots of blurring and today some slight haziness. Last night it was definitely more painful and I'm getting lots of the "eye lash in the eye" feeling.

That being said, this is all completely normal and my eye doctor this morning said everything in healing up just fine. My left eye is bugging me the most and he says that is the one that is healing the fastest! So, I will keep a good thought and just be patient today.

So far I have only had to take two of the Percocets they gave me and a few Extra Strength Tylenols. Again, I would still say this is less painful than a dental procedure, but the deteriorating vision is definitely irritating and interferes with doing a lot of normal things I would do to kill time like gardening, working on the computer or watching TV. Good thing I downloaded a bunch of episodes of "Car Talk" from NPR before the surgery. By the time this is done I will be a master mechanic and/or have a Boston accent.

Many thanks to my wife for keeping all the drops straight. She really should be a nurse. One thing you do have to watch with this is all the various eye drops you need to be squirting in your eyes!

Saturday, May 29, 2010

PRK Post-surgery - Day 1 after

Well, this is my 1st day after the surgery and I will need to keep this quick since it's pretty hard for me to see the screen.

I had my first follow-up with my local eye doctor and he said everything was healing fine. In fact, since my surgery was quite early yesterday he said I was slightly ahead of the curve. The family all went out for a good breakfast this morning and in general I have felt pretty good. I had a long nap in the afternoon and my energy level seems a bit low.


For most of the morning I really couldn't feel the bandage contacts at all and so far I have only had two extra strength Tylenols for the discomfort. I've had a bit of the "eyelash in the eye" feeling in one eye, but only occasionally.  Right now later in the day and after having been outside, and with trying to concentrate on my computer screen to write this, my eyes definitely feel tired and a bit scratchy.

My vision is very poor and probably worse than yesterday. I would describe it as about a third or half of my previous uncorrected -9 diopter prescription, so I can function at most things, but it is still tough - lots of halos and so on.

The strangest thing is my eyes look perfectly normal - they aren't blood shot or weepy or swollen or anything. As my wife says, if I had sneaked out and had the surgery, it would be impossible to tell! Even the bandage contact lenses aren't all that obvious. Contrast this with a friend of mine who had Lasik who had some bloodshot appearance a week later. I think that's because there is a ring they place on your eye for cutting the flap in Lasik that isn't required for PRK.

So, that's probably enough for now, but just to mention one last thing. If you are going for this surgery you definitely, absolutely will need a committed partner to help see you through it and the after care. Immediately after the surgery you are really pretty helpless and I still feel pretty helpless while I have the bandage lenses in. There is also a pretty complex regime of eye drops to manage. It gets especially complicated when there are also children to look after! My friends who have had Lasik are generally right back to fairly normal routine the next day, but that just isn't the case with PRK!

I made the dumb mistake of planning this for when our primary child care helpers (my in-laws) happened to be out of town which has made this somewhat more complicated than it might be normally. My wife has been wonderfully supportive and taken on the extra load without complaint and I appreciate it and love her even more for it! I will definitely make it up to her with some time off for her later in the summer when this is all over.

Friday, May 28, 2010

PRK Surgery Day

Just a quick note today because the nurses warned me that "the doctor can tell if you have been working on your computer!"

The first part was paperwork & payment then a quick meeting with the doctor. He explained that because of my extreme prescription, they would be taking off nearly a quarter of my cornea, but the actual laser time was only 58 seconds. I have a somewhat elevated chance of needing a touch up later, but fingers crossed. As well there was a briefing on the post-op drop regime.

The surgery went well and had somewhat of an alien abduction feel. Ahead of the surgery you get a course of drops (more antibiotics and steroidal drops) then the topical anesthetic. The weird thing was being ushered into the room with no glasses on, so effectively blind, and being laid out on the surgical bed.

The first part of the procedure is getting the ocular speculum in to hold open the eyelid on the first eye (rather uncomfortable) then some measurements and getting everything set up.  Then the doctor used what seemed like a mini-electric toothbrush to abrade off the out epithelial layer on my cornea.   The topical anesthetic worked fine and it just tickled a bit. but was definitely odd. After scrapping off the residue, then it was laser time!

For the actual lasering, I had to concentrate on a flashing light, then you could hear the clicking of the laser and the technician counting down the time. Gradually my vision grew cloudier & cloudier and there was a bit of a disconcerting "burning flesh" smell, but it didn't last too long.

After that the doctor put Mitomecin on my eye, which is a cancer drug that helps prevent hazing and scarring then flushed the eye with water, put on some drops then slipped in the bandage contact lens. All very quick and I'm sure no more than 5 minutes. The second eye was just the same. Definitely a freaky feel and somewhat hard to describe, but not too terrible. Not to give away too much of my medical history, but I found it harder than a dental procedure (like a filling), but much easier than my vasectomy. Everyone at the clinic was very professional and nice.

Afterwords they check you out one more time and then you're done! I felt reasonably well after the surgery, so we went over to a nearby Chapters so my wife could find a book she needed and I just settled in to listen to my iPod and have a latte. On the long drive home, my eyes started to feel a bit irritated just with so much sun and I guess from drying out a bit.

At home, we went through a drop treatment and I took a Percocet and slept for a couple of hours. Now I am up and about with sunglasses on and my eyes feel fine. My vision is definitely very distorted, but I would say it is about at half my previous uncorrected myopia blurriness. It is a drag to not just get immediately perfect correction like the Lasik people, but at least I don't feel uncomfortable and I can more or less see well enough to do basic things. Thank heavens I learned touch typing or I would never be able to write this up!!

Thursday, May 27, 2010

PRK Laser Srugery - Prologue

This will be a somewhat different set of posts than usual - perhaps this will be more like an actual blog then a set of technical recipes. You see, tomorrow I will be going in for laser eye surgery to get rid of my glasses after wearing them for some 38 years and I thought it might be a good idea to keep a diary of my recovery.

Normally, for most people these days, getting Lasik surgery is quite routine, but I have...

  1. A pretty extreme perscription at -9 diopters
  2. Apparently the epithelial layer of cells on my eye ball are somewhat loose (who knew?)
Because of these, I need to go for PRK surgery (Photorefractive keratectomy, which you can read about here). Since that does away with the Lasik flap, I will have to regrow the epithelium over my cornea, which can take a few days. The only thing that makes me a bit nervous is that sometimes it can be somewhat painful and it can take a few weeks for your vision to completely "lock in" to it's final perscription.

So, assuming I can even see my computer monitor, I am going to keep a record on my blog so that I can track just how well I am doing and how long it takes to recover. I've read a few other patients journals from their recovery and I found it really helpful to understand the process. Hopefully my journal will be helpful to someone else.

I do have a very cool Arduino project I will finish once I am up to it!

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.

Monday, May 3, 2010

Arduino Email Manager - Part 3 - Arduino Code

Now we come to the last part of the Email Manager - the Arduino firmware. Most of it is reasonably straightforward, but there are a few things to note.

To drive the LCD I used the LCD4Bit_mod library recommended to go with the DFRobot LCD shield (available from here). In general this seems to work fine, but it doesn't seems consistent about one line flowing over to the next which is why I have "lcd.cursorTo" lines to put the cursor on the 1st or 2nd line.

The buttons actually are run over Analog pin 0 and the "get_key" function determines the analog value returned and lines it up with the appropriate key number. The trickiest thing for me was figuring the various modes for the buttons in different states - especially the Up/Down buttons that can also be used to delete messages, which is why there are a lot of "if-this-key-and-that-mode-do-this" statements. Likely they could be cleaned up further!

Finding the from/subject info in the data stream proved to be harder than I would have thought as explained in the first post so I ended up using the TextFinder library available from here.  This allows you to search in the data stream (either serial or Ethernet) for keywords or values. Come to think of it, I could probably have used this to do the whole email parsing job and then dispensed with the PHP script, but I noticed when I ran the "getValue" feature it temporarily blacked out the LCD screen - maybe a short processor lock up? Anyway, if anyone can think of a way to use TexFinder to eliminate the PHP script, let me know!

So, here is the Arduino code (with lots of comments) in its glory:

/*
*  Arduino POP Mail Manager
* Uses the DFRobot LCD Shield, Arduino Ethernet Sheild
* and an Arduino Duemilanove to build a simple system for seeing
* how many POP eamils you have and deleting ones you don't want. Separate
* PHP script must be used with it. Complete description at:
* http://opensourceprojects-torchris.blogspot.com/
*
* Uncomment the //Serial lines for troubleshooting/debug info.
*
* This code is in the public domain. Please provide credit if it is used
* in another project.
* 
* written by Chris Armour, Arpil 30th, 2010
*
*/

//=====================Libraries=============/
#include <Ethernet.h>
#include <LCD4Bit_mod.h> 
#include <TextFinder.h>

//===============Set up variables =============/

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //MAC address for Arduino
byte ip[] = { 192,168,0,34 }; // IP address you wish to assign to Arduino
byte server[] = { 192, 168, 0, 171 }; // IP address of your PHP server
int ServerPort = 12345;
int  adc_key_val[5] ={30, 150, 360, 535, 760 };
int NUM_KEYS = 5;
int adc_key_in;
int key=-1;
int oldkey=-1;
char NumRecd[4];
char SubjRecd[32];
boolean ConnectedState = false;
int MsgNumber = 1;
char TotalMsgNumChar[4] = "   ";
int MsgTotalNum = 1;
char TotalMsgCharAr[2];
boolean DelMode = false;
unsigned long ButtonMillis = 0;
unsigned long CurrentMillis = 0;
unsigned long interval = 15000;

//===============Setup instances===============/

LCD4Bit_mod lcd = LCD4Bit_mod(2); 
Client client(server, ServerPort); 
TextFinder finder( client);

//=================Setup========================/

void setup() { 
  Ethernet.begin(mac, ip);
  lcd.init();
  lcd.clear();
  lcd.cursorTo(1,0);
  lcd.printIn("POP Mail Started");
  lcd.cursorTo(2,0);
  lcd.printIn("Press S to conct");
  //Serial.begin(9600);
  DelMode = false;
  ConnectedState = false;
}

//=================Main program loop ==================/

void loop() {

  CurrentMillis = millis(); 
  
  if (((CurrentMillis - ButtonMillis) > interval) && (ConnectedState == true)){
    //If connected to the PHP script, then every 15 seconds display the number of emails.
    ButtonMillis = CurrentMillis;
    //Serial.println("15 Seconds have passed!");
    GetTotalMsgs(); //Get total message count
    delay(100);
    PrintTotalMsgs(); //Display total messages
  }
  
  if (((CurrentMillis - ButtonMillis) > interval) && (ConnectedState != true)){
    //If not connected, then just display the not connected error.
    ButtonMillis = CurrentMillis;
    //Serial.println("15 Seconds have passed!");
    NotConnectedError();
  } 
  

//The following items are from the original LCD example and they read the value of the key pressed. 
adc_key_in = analogRead(0);    // read the value from the sensor 

key = get_key(adc_key_in); // convert into key press
   
if (key != oldkey) // if keypress is detected
    {
    delay(70);        // wait for debounce time
    adc_key_in = analogRead(0);    // read the value from the sensor 
    key = get_key(adc_key_in);                // convert into key press
    //Serial.println(key);
  
    if (key != oldkey)               
    {           
      oldkey = key;
    }
  }
  
  if (key == 4) {
    //If the Select button is pushed, run the routine to connect to the PHP server.
    ButtonMillis = millis();
       if (client.connect()) {
          char c = client.read();
        if (c == 'C') {
          //Serial.println("connected");
          lcd.clear();
          lcd.printIn("Connected");
          ConnectedState = true; //Sets ConnectedState to true
          delay(200);
        }
         } else {
           //If can't connect, print an error.
          //Serial.println("connection failed");
          ConnectedState = false; // Set connected state to false
          lcd.clear();
          lcd.printIn("Connection Fail");
          lcd.cursorTo(2, 0);  //line=2, x=0 
          lcd.printIn("Check PHP script");

      }
  }
  
  if ((key == 3) && (ConnectedState == true)) {
    //If Right button pressed and it's connected to the PHP script, then dispaly the toal messages.
          ButtonMillis = millis(); //these detect when the button was pushed
          GetTotalMsgs();
          delay(100);
          PrintTotalMsgs();
         }
        
if ((key == 3) && (ConnectedState == false)){
   //Serial.println("Not Connected!!");
   //If ConnectedState is false, print error message.
    NotConnectedError();
    } 


if ((key == 2) && (ConnectedState == false)){
   //Serial.println("Not Connected!!");
    NotConnectedError();
    } 

if ((key == 2)&& (DelMode != true) && (ConnectedState == true)) {
  //If it is connected AND not in delete mode, then display email info
  ButtonMillis = millis();
  GetTotalMsgs();
  delay(200);
  //Serial.println("DN button push detected");
  MsgNumber++; //Increments the messages.
   if (MsgNumber >= MsgTotalNum){ //if we reach the max message number, then go to message #1
   MsgNumber = 1;
    }  
   CallMsg();
   delay(150);
   //Serial.print("Total Messages = ");
   //Serial.println(MsgTotalNum);

}
         
if ((key == 2) && (DelMode == true)&& (ConnectedState == true)){
  //If it is connected, but in Delete mode, then this is the confirm delete button.
  ButtonMillis = millis();
  //Serial.println("DN button push detected");
  //Serial.print("Deleting message number =");
  //Serial.println(MsgNumber);
  DelMode = !DelMode;
  DelMsg();
  lcd.clear();
  lcd.printIn("Message Deleted");
  MsgNumber--;
  delay(250);
  CallMsg();
}

if ((key == 1) && (ConnectedState == false)){
   //Serial.println("Not Connected!!");
    NotConnectedError();
    } 

if ((key == 1)  && (DelMode != true) && (ConnectedState == true)) {
  //If we are connected AND not in delete mode, then disply email info
  ButtonMillis = millis();
  GetTotalMsgs();
  delay(200);
  //Serial.println("UP button push detected");
  MsgNumber--; //decrements the message number
  if (MsgNumber <= 1){ //If we decrement down to 1, then go to the highest message number
     MsgNumber = MsgTotalNum;
   }
   //Serial.print("Total Messages = ");
   //Serial.println(MsgTotalNum);
   delay(150);
   CallMsg();
    }

if ((key == 1) && (DelMode == true)&& (ConnectedState == true)){
  //If we're in delete mode, then this cancels the deletion.
    ButtonMillis = millis();
  //Serial.println("UP button push detected");
  //Serial.print("Cancelling deletion");
  //Serial.println(MsgNumber);
   //Serial.println("DelMode is set to false");
  DelMode = false;
  delay(200);
  lcd.clear();
  lcd.printIn("Delete Cancelled");
  CallMsg();
  }

if ((key == 0) && (ConnectedState == false)){
  //Serial.println("Not Connected!!");
    NotConnectedError();
    } 

if ((key == 0) && (ConnectedState == true)) {
//Finally, if we are connected then this is the delete button.
  DelMode = true; //triggers delete mode
  //Serial.println("DelMode is set to True");
  //prints instructions for deleting or cancelling.
  lcd.clear();
  lcd.cursorTo(1,0);
  lcd.printIn("Delete Message?");
  lcd.cursorTo(2,0);
  lcd.printIn("UP=N  /  DN=Y");
  }
}

//======================Functions=====================//

// Convert ADC value to key number
int get_key(unsigned int input)
{
    int k; 
    for (k = 0; k < NUM_KEYS; k++)
    {
        if (input < adc_key_val[k])
        {
          return k;
        }
    }   
    if (k >= NUM_KEYS)
        k = -1;     // No valid key pressed  
    return k;
}

//Deletes a message
void DelMsg(){
     //Serial.print("Deleting MsgNumber = ");
     //Serial.println(MsgNumber);
     client.print ("D.");
     client.println(MsgNumber);
     lcd.printIn("Message Deleted");
     delay(150); 
}

//Sends the command to the PHP script to get the from/subject info for the current message number
void CallMsg(){
     //Serial.print("MsgNumber = ");
     //Serial.println(MsgNumber);
     client.print ("S.");
     client.println(MsgNumber);
     delay(150); 
     GetFrmSubj();
}

void GetFrmSubj(){
  //parses out the from/subject info from the PHP return info
     if(finder.find("MSG") == true ){
       //Uses the TextFinder library to locate the from/subject info.
           for (int z = 0; z <= 32; z++) {
             delay(20);
             char c = client.read(); 
             if ((c > 31) && (c < 128)){
             SubjRecd[z] = c;
             }
           }
           Print2LCD();
           client.flush();
     }
     else if (finder.find("MSG") != true) {
       //If the from/subj can't be retreived, then display an error.
       //Usually a transient authentication error on the PHP side.
       //Serial.println("Didn't get it!");
       lcd.clear();
       lcd.printIn("Error getting Msg");  
       client.flush();
     }
}
   
void Print2LCD(){
  //prints the from/subject info to the LCD
  lcd.clear();
  lcd.cursorTo(1,0);
  for (int y = 0; y <= 15; y++) {
    lcd.print(SubjRecd[y]);
    //Serial.print(SubjRecd[y]);
    }
  lcd.cursorTo(2,0);
  //Serial.println();
  for (int z = 16; z <= 32; z++){
    lcd.print(SubjRecd[z]);
    //Serial.print(SubjRecd[z]);
                    }
  client.flush();
}

void GetTotalMsgs () {
  //Gets the total message number, which is stored as both an array & an integer
   client.println("N");
   delay(150);
   if(finder.find("Total Number of Messages:") == true )  {   
     for (int w = 0; w <= 2; w++) {
        delay(20);
        char c = client.read(); 
        if ((c > 31) && (c < 128)){
        TotalMsgCharAr[w] = c;
       }
  }
  MsgTotalNum = atoi(TotalMsgCharAr); //Use ASCII to Integer to convert the array.
  }
}

void NotConnectedError(){
  //Simple error message
  lcd.clear();
  lcd.cursorTo(1,0);
  lcd.printIn("Not Connected"); 
  lcd.cursorTo(2, 0);  //line=2, x=0 
  lcd.printIn("Press S to conct");
}

void PrintTotalMsgs(){
  //This takes the total message array & prints it to the LCD.
  //Note that is will max out at 99 messages
    lcd.clear();
    lcd.printIn("Number of emails"); 
    lcd.cursorTo(2, 0);  //line=2, x=0 
    //Serial.print("Total messages = ");
    //Serial.println(MsgTotalNum);
       for (int d = 0; d <= 1; d++){
         if ((TotalMsgCharAr[d] >= 48) && (TotalMsgCharAr[d] <= 57)){
         lcd.print(TotalMsgCharAr[d]);
         }
      }
}

So what?


Well, why bother doing all this since I can, of course, much more easily delete spam from my Inbox with a regular email client or my iPhone? Well, as with my previous Arduino/POP3 interfacing project, I like to have a running visual indication of my email load, and, of course, the stock answer is "Why not? It's my time to waste!". Actually, email is quite lightweight an ubiquitious and so could be easily used for remote control. We web-based control would be more elegant, but not always accessible via a remote device for whatever reason.

About 10 years ago I build a device that interfaced with the PC parallel port and then wrote a Visual Basic program that received email and interpreted the subject line to do basic remote control via a couple of solid state relays. It could turn on or off simple AC devices with email and worked like a charm. With an Arduino, you could have a light sensor that then could tell if the light is, in fact, on and send an SMTP message (via the PHP script) to confirm that the action had been taken.

Next steps...


Next up, I should get this properly working with Gmail - possibly using the PHP classes available for IMAP rather than POP3.I also hope to get one of those new-fangled Wi-fi WiShield things and try to take this wireless!

Another future project may be to use the Twilio web-based API for voice-enabling applications to interface to an Arduino (check it out here). I have played with Twilio a bit and using a PHP pre-processor should make it quite easy to interface to an Arduino. This would allow a simple IVR-type remote control & monitoring - "Press 1 to turn on your lights, Press 2 to hear the temperature." I will try and not make it five months between posts!

Arduino Email Manager - Part 2 - The Preprocessor

I freely admit that I am not the greatest programmer in the world. Never having been formally trained, my programs tend to be rather loopy and don't include all the error checking they should. This PHP script that I have written is a case in point. It will work with my POP email provider (Rogers in Canada), but it may NOT work with your provider! It seems like there are minor variations in the way some POP servers will respond to the standard commands and these can throw off the script slightly. The best bet is to log into your POP server via telnet and carefully watch the messages back and forth.

Also note that this will support SSL (if you have it set up with your PHP installation) just by putting "ssl:/" in front of the host name and using whatever port number has been assigned - usually 995.You can manually log into an SSL-enabled account using the OpenSSL client available from OpenSSL.org instead of telnet.

At this point, the script does not support Gmail, so don't write to me about that! I have been trying to get it to work and you can definitely log in, but it seems to give inconsistent results and the mail total includes both the inbox and sent folder somehow (which doesn't make sense to me). If anyone out there knows the trick with Gmail, please let me know. I see that there are some PHP classes available for accessing Gmail via IMAP and I will investigate these and post revised code when I can.

So, how does this script work? The script needs to run on a server with the current version of PHP running. This could be a Mac or a Linux/Windows box or it could be running on a remote webserver - assuming you have all the networking and firewall issues dealt with. There is a new project called Yaler that may help with the networking issues if your script is remotely hosted - check it out here. You need to start the script before pressing the "Connect" (or Select) button on the Arduino.

When running, the Preprocessor is a "host" for the requests from the Arduino, but is in turn acting as a "client" to the POP server. It maintains two separate PHP sockets - one to the Arduino and one to the POP server.



The script implements a very simple protocol with the Arduino:

Arduino Sends...
Preprocessor returns...
Arduino connects to IP/portthe ASCII "C"
NCurrent number of emails on the POP server.
S.x (where x is an integer)The from/subject information for email number "x" prefixed with MSG.
D.x (where x is an integer)Deletes email number "x".
EKills the socket and ends session - currently not implemented.

The script also provides the following error messages back (although I am not currently using these on the Arduino):

  • X.1 - Could not open socket to POP server
  • X.2 - Error from server
  • X.3 - Authentication failure
  • X.4 - Bad connection

I tend to prefer one letter protocols with the Arduino since it simplifies the text handling on the Arduino side.

And, without further ado, here is the code...

<?php

/*
*  PHP Pre-processor script for Arduino POP Mail Manager
*
* this is the pre-processor script for use with the Arduino email
* manager described on my blog. Complete description at:
* http://opensourceprojects-torchris.blogspot.com/
*
* This script can be used with SSL enabled POP services by putting
* "ssl:/" infront of the host name. It does NOT currently work with
* GMail POP service.
*
* This code is in the public domain. Please provide credit if it is used
* in another project.
*
* written by Chris Armour, Arpil 30th, 2010
*
*/

//=============================IP, Port, User Info=================//
//Modify to suit your network.

define("HOST_POP", "your.popserver..com");
define("PORT_POP", "110");
define("USER_POP", "user.name");
define("PASS_POP", "YourPassword");
//Server settings
$host_ard = "192.168.0.171"; //This is the IP of the server running the script.
$port_ard = "12345"; //Port number being used by the Arduino


//==========================Functions===================//

//GetMailCount grabs the number of emails in the mailbox using the STAT command
function GetMailCount()
{
$fp_pop = fsockopen (HOST_POP, PORT_POP, $errno, $errstr);
    // if a handle is not returned
    ob_implicit_flush();
    if (!$fp_pop)
        {
        echo("Error: could not open socket connection\n");
        return("X.1");
        }
    else
        {
        // get the welcome message
        $welcome = fgets ($fp_pop, 150);
        // check for success code
        if (substr($welcome, 0, 3) == "+OK")
        {
        // send username and read response
    fputs ($fp_pop, "user " . USER_POP . "\n");
    //fgets($fp_pop, 50);
    // send password and read response
    fputs ($fp_pop, "pass " . PASS_POP . "\n");
    $ack = fgets($fp_pop, 50);
    // check for success code
    if (substr($ack, 0, 3) == "+OK")
        {
        // send status request and read response
        fputs ($fp_pop, "STAT\n");
        $status = fgets($fp_pop, 50);
    if (substr($status, 0, 3) == "+OK")
        {
        // shut down connection
        fputs ($fp_pop, "QUIT\n");
        fclose ($fp_pop);
        }
// error getting status
    else  {
        echo ("Error - server said: $status");
        return("X.2");
        }
        }
    // auth failure
    else  {
        echo ("Error - server said: $ack");
        return("X.3");
        }
        }
        // bad welcome message
    else {
        echo ("Error - bad connection string\n");
        return("X.4");
        }
// get status string
// split by spaces
$arr = explode(" ", $status);
// the second element contains the total number of messages
echo $arr[3] . " messages in mailbox\n";
$GotMail = $arr[3];
return $GotMail;
}
}

//GetFromSubj grabs the content of the email MailNumber then passes this back to the main loop for extraction of the From & subject
//You may need to check the output from your POP3 server using telnet to check on the responses. This seems to vary from POP server to POP
//server.
function GetFromSubj($MailNumber){
$fp_pop = fsockopen (HOST_POP, PORT_POP, $errno, $errstr);
    // if a handle is not returned
    ob_implicit_flush();
    if (!$fp_pop)
        {
        echo("Error: could not open socket connection\n");
        return("X.1");
        }
    else
        {
        // get the welcome message
        $welcome = fgets ($fp_pop, 150);
        // check for success code
        if (substr($welcome, 0, 3) == "+OK")
        {
        // send username and read response
    fputs ($fp_pop, "user " . USER_POP . "\n");
    fgets($fp_pop);
    // send password and read response
    fputs ($fp_pop, "pass " . PASS_POP . "\n");
   $ack = fgets($fp_pop);
    // check for success code
    if (substr($ack, 0, 3) == "+OK")
       {
        // send request for email content & read response

        fputs ($fp_pop, "retr " . $MailNumber . "\n");
        $EmailContent = fread($fp_pop, 4096);
//        echo $EmailContent;
    if (substr($EmailContent, 0, 3) == "+OK")
        {
        // shut down connection
        return $EmailContent;
        fputs ($fp_pop, "QUIT\n");
        fclose ($fp_pop);
        }
// error getting status
    else
        {
        echo ("Error - Server said: $EmailContent");
        return("X.2");
        }
        }
    // auth failure
    else
        {
        echo ("Error - Server said: $ack");
        return("X.3");
        }
        }
        // bad welcome message
    else
        {
        echo ("Error - Bad connection string\n");
        return("X.4");
        }
}
}

//MsgDelete delets message # MailNumber using the dele command
function MsgDelete($MailNumber){

$fp_pop = fsockopen (HOST_POP, PORT_POP, $errno, $errstr);
    // if a handle is not returned
    ob_implicit_flush();
    if (!$fp_pop)
        {
        echo("Error: could not open socket connection\n");
        return("X.1");
        }
    else
        {
        // get the welcome message
        $welcome = fgets ($fp_pop, 150);
        // check for success code
        if (substr($welcome, 0, 3) == "+OK")
        {
        // send username and read response
    fputs ($fp_pop, "user " . USER_POP . "\n");
    fgets($fp_pop);
    // send password and read response
    fputs ($fp_pop, "pass " . PASS_POP . "\n");
   $ack = fgets($fp_pop);
    // check for success code
    if (substr($ack, 0, 3) == "+OK")
       {
        // send delete command

        fputs ($fp_pop, "dele " . $MailNumber . "\n");
        $DeleteAck = fgets($fp_pop);
//        echo $EmailContent;
    if (substr($DeleteAck, 0, 3) == "+OK")
        {
        // shut down connection
        echo ("Message " . $MailNumber . " deleted. \n");
        fputs ($fp_pop, "QUIT\n");
        fclose ($fp_pop);
        }
// error getting status
   else
        {
        echo ("Error - Server said: $EmailContent");
        return ("X.2");
        }
        }
    // auth failure
    else
        {
        echo ("Error - Server said: $ack");
        return("X.3");
        }
        }
        // bad welcome message
    else
        {
        echo ("Error - Bad connection string\n");
        return("X.4");
        }
        }
}

//=====================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";

//=====================Main Socket loop======================//
do {

$input = socket_read($spawn, 2048, PHP_NORMAL_READ) or die("Could not read input\n");

echo $input;

if (trim($input[0]) == "N" || trim($input[0]) == "S" || trim($input[0]) == "D" || trim($input[0]) == "E" || trim($input[0]) == "\r\n") {
//Only do anything if N, S, D or E received.

    if (trim($input) == "N"){
        // This gets the mailcount using the GetMailCount function.
        $MailCount = GetMailCount();
        socket_write($spawn, "Total Number of Messages:" . $MailCount . "@");
        }

    if (trim($input[0]) == "S") {
        //This gets the from/subject info is an S.num is received.
       $MsgNum = explode (".",$input);
       //Use explode to break the info from the Arduino into an array.
        $MsgNumber = $MsgNum[1];
        //Get the Message number from the array.
        echo ("Value of MsgNumber= " . $MsgNumber . "\n");
        $MsgFromSubj = GetFromSubj($MsgNumber);
        //run the function to get the info from the POP server
            if ($MsgFromSubj[0] == "X")
            {
            //If there's an error, write back the server error text.
                socket_write($spawn,$MsgFromSubj);
            }
        else
            {
            $MsgFromLocn = strpos($MsgFromSubj, "From: ");
            $MsgFromName = substr($MsgFromSubj, ($MsgFromLocn + 6), 14);
            print ( "MSG". "F:" . $MsgFromName );
            $MsgSubjLocn = strpos($MsgFromSubj, "Subject: ");
            $MsgSubj = substr($MsgFromSubj, ($MsgSubjLocn + 9), 14);
            echo ("S:" . $MsgSubj );
            usleep(20000);
            socket_write ($spawn,  "MSG" . "F:" . $MsgFromName . "S:" . $MsgSubj . "\n");
            }
        }

    if (trim($input[0]) == "D") {
    //This sends the command to delete an email.
       $MsgNum = explode (".",$input);
        $MsgNumber = $MsgNum[1];
        $MsgDelRet = MsgDelete($MsgNumber);
        if ($MsgDelRet[0] == "X")
            {
                socket_write($spawn,$MsgDelRet ."\n");
            }
        else
            {
        socket_write($spawn, "D." . $MsgNum[1]);
        }
    }

    if (trim($input) == "E"){
  //This isn't actually currently used by the Arduino.
        socket_shutdown($spawn, 2);
        usleep(1000);
        socket_close($spawn);
        socket_shutdown($socket_ard, 2);
        usleep(1000);
        socket_close($socket_ard);
        echo "Sockets terminated\n";
 //       break;
        }
}

} while(true);

?>

Note that this code does not provide good handling of disconnect/reconnect nor can it handle multiple connections simultaneously. In other words, if the Arduino is reset, then the script needs to be restarted. Also, if multiple connection requests are received, it will just die. If anyone can improve on this, please make the suggestions.

Next up will be the Arduino code!

Saturday, May 1, 2010

Arduino Email Manager - Part 1 - Overview

I always seem to start every blog entry moaning about how long it's been since the last project I posted and this one is no exception. I think writing this is pushing me to do more complex projects and what with having a life and all, it just takes a long time to get everything done. This project in particular turned out to be quite a bit more difficult than I thought it would be and it has ended up taking me months and months to work out all the kinks. Because of that, I will be breaking up the project description into a couple of entries. This first one will just introduce the project.

First the obligatory video!



As explained in the video, this project uses three easily available Arduino components to allow you to:

  • See how many emails you have in your POP3 email account
  • Review the "from" and "subject" of your email
  • Delete unwanted emails

Here is what it looks like assembled:





The system is a sandwich of..

  • Arduino Duemilanova
  • Arduino Ethernet Shield
  • DFRobot LCD/button shield (available here from Robotshop among others)

I also used some Shield Stacking Headers (available from Adafruit) since the LCD shield wouldn't clear the Ethernet jack on the network shield. Here it is disassembled:





Getting the LCD Shield to work with the Ethernet Shield was a time consuming process. Initially, it looked like there would be no pin conflicts, since the Ethernet Shield uses pins 10, 11, 12 & 13 and the LCD Shield in theory uses 5,6,7,8 and 9, but for whatever reason I just couldn't make it work. I did finally find a blog posting by David Delabassee (here) that pointed out that, in fact, the LCD Shield uses more pins than is documented and shows how to modify LCD4Bit_mod.cpp to remap the pins slightly. Very useful, but it still seemed to blank out for some reason.

So, I ended up testing every single pin combination (which took hours) and I finally determined that if the LCD Shield was on digital pin 13, it just wouldn't work for whatever reason (even though, in theory, that pin is not used by the shield). So, just bending back that leg on the LCD Shield took pin 13 out of the circuit and it now works fine.

I had also wanted to add in one or two LEDs to signal the message count or connected state, but for whatever reason again when I added them in, it just wouldn't work consistently. If anyone has any ideas on how to fix that it would be appreciated!

As explained in the video, I have used the five buttons available on the LCD shield to page through the emails and delete any that I don't want.




I will post the software in subsequent entries, but for now I will just explain that I decided to go with using a mail "pre-processor" application written in PHP. Originally I wanted to do everything on the Arduino so it would be a complete, stand-alone solution, but, alas, it just proved to be too hard for the Arduino's limited text processing abilities and my small brain. Basically, even with the Duemilanova based on the ATMEGA328, you really can't load a string array with more than a couple of hundred characters before the Arduino locks up. Unfortunately, you need to look through at least the first 1,500 - 2,000 characters to get the "from" and "subject" info I wanted. I looking into using PROGMEM, but I just couldn't get my head around it (looks like all that fancy "pointer" stuff I can never figure out!). Then there's the Arduiniana Flash library, but it seems limited to strings you load at the beginning, rather than dynamic strings.

Finally, I just decided to go with a pre-processor written in PHP. I already know enough PHP to be functional and it is excellent for text processing. When I went to figure out how to do sockets programming with PHP, the first tutorial I found was on writing a script to access a POP3 email server (check it out)! Using a pre-processor isn't cheating too much. Tom Igoe uses one in Making Things Talk for his "Networked Air Quality Meter" project. Of course, on a much vaster scale, that is essentially how the RIM Blackberry email system works - emails are routed through their preprocessor software which compresses and reroutes them them down to the smartphone.

That is enough for now. Next up will be the Arduino software & PHP script.