Showing posts with label Ethernet. Show all posts
Showing posts with label Ethernet. Show all posts

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!

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.

Thursday, January 21, 2010

Arduino Motion Control over Ethernet

Watching the numbers on Google Analytics definitely tells me my reading public doesn't care about my welding projects! So, let's get back to the Arduino.
When I set up to do my POP3 project, I ended up buying an Arduino Ethernet shield and an Adafruit Ethernet shield plus a Lantronix Xport. Having both, I figured it would be a good challenge to get both talking to each other and the result is this fairly simple project which actually ended up taking a lot of thought. It is loosely based on "Networked Game" project in Tom Igoe's indispensable book "Making Things Talk", but Tom's example does the much harder job of writing everything in the low-level serial commands to the Xport while I am lazy and used the published libraries! The challenge here is that the two network shields have two different Arduino libraries which really don't work quite the same way. Also, the Adafruit library is pretty light on explanation and the examples provided have no comments in the code!
Here is the basic setup of this project, basically the accelerometer on the "server" controls two servos on the "client":

The servos, of course, could be hooked up to any number of things like the X-Y axis of a camera mount or a motion base for a game of some kind. Conceptually, of course, through the magic of the Internet and with the right networking setup, the client could be in New York and the server in Instanbul. Also, through the miracle of standard IP protocols I can use one make of shield to successfully communicate with another using two totally different libraries!
Here is a quick video of the rig in action:
Note that the servos jitter slightly when they are hooked up because of slight variations of the analog readings on the accelerometer. I could probably do some averaging to smooth out the readings to eliminate that jitter.
The Server
The server is actually very simple. I used the Adafruit ADXL335 accelerometer (info) that is hooked up to the first three analog pins on the Arduino Diecimilia. The Arduino reads and sends out the X, Y and Z axis, but the client only uses the X and Y values because I only have two servos. It would be pretty easy to build support in for all three axes on the client. Apart from the accelerometer, the only other component is the LED that indicates connected or not connected.




The code is equally simple. It reads the accelerometer and when it receives a "g" from the client it prints out the X, Y & Z readings. When it receives an "x" it ends the session. That's it! Note that this uses the Ethernet.h library distributed with the Arduino software.

* Simple Ethernet Server
*
* A simple server that shows the value of the analog input pins 0 - 2 
* which are connected to an ADXL335 Accelerometer.

by Chris Armour, Jan 2010
*/

#include 

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //Setting the MAC address
byte ip[] = { 192, 168, 0, 177 }; //IP address of the server
int XPin = 0;
int YPin = 1;
int ZPin = 2;
int ValX = 0;
int ValY = 0;
int ValZ = 0;
int ledPin = 9;
char c;

Server server(54321); //This is the port the server listens on.

//==============Setup pins, server & serial ===================//

void setup()
{
Ethernet.begin(mac, ip);
server.begin();
Serial.begin(9600);
pinMode(ledPin, OUTPUT); 
digitalWrite(ledPin, LOW);
}

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

void loop()
{
digitalWrite(ledPin, LOW); //If there is no client connected the LED is off.
Client client = server.available();

while (client.connected()) {

if (client.available()) {
digitalWrite(ledPin, HIGH);  
char c = client.read(); //Read one character at a time.
Serial.print("Value received:  ");
Serial.println(c); 

if (c == 'g') { //"g means "get"
ValX = analogRead(XPin);
ValY = analogRead(YPin);
ValZ = analogRead(ZPin);
Serial.print("Value sent:  ");
Serial.print(ValX);
Serial.print(",");
Serial.print(ValY);
Serial.print(",");
Serial.println(ValZ);
client.print(">");
client.print(ValX);
client.print(",");
client.print(ValY);
client.print(",");
client.println(ValZ);
break;
}
if (c == 'x'){ //x means kill the session.
digitalWrite(ledPin, LOW); //turn off the LED
client.flush();
client.stop();
}
}
}
delay(50);
}

The Client

The client is a slightly more complex beast in that it has a switch to initiate a connection, an LED to indicate the connection state and the servos:



The code is also somewhat more involved. Part of that is because of using the AF_Xport library from Adafruit which takes a bit more fiddling, but also because the client needs the extra logic to initiate and disengage the connection. The main problem area is on resetting the Xport which doesn't always seem to want to connect on the first try so I had to build a loop in to retry the reset until it connects. This is a bit inelegant, but it looks like others have had this problem too.

/*
A network client based on Adafruit Ethernet Shield using the 
Xport ethernet-to-serial adapter. It moves servos based on analog
input from a server with an ADXL335 Accelerometer.

by Chris Armour, Jan 2010
*/

#include 
#include 
#include 
#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.177" //IP Address of the Arduino Server
#define PORT 54321 //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; //The return variable needs to be formated as a unsigned integer of length 8 bits 
int Xval = 0;
int Yval = 0;
int Xservo = 0;
int Yservo = 0;
int buttonPin = 11;
int buttonWas = 0;
int buttonIs = 1;
int ledState = 0; //I believe these are actually flipped 0 = On 1 = off
int ledPin = 13;
boolean ConnectState = false;
char linebuffer[16]; //Very small line buffer because we just need the X-Y readings.
Servo Xservobj;
Servo Yservobj;
int loopCount = 0;

//===============Setup pins, servo, xport ==========//
void setup()  
{
 pinMode(buttonPin, INPUT);
 pinMode(ledPin, OUTPUT);
 Serial.begin(9600);
 xport.begin(9600);
 delay(300);
 Serial.println("Finished Setup...");
 Xservobj.attach(9);
 Yservobj.attach(8);
 buttonIs = digitalRead(buttonPin);
}

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

void getButton() { 
  buttonWas = buttonIs; // Set the old state of the button to be the current state since we're creating a new current state. 
  buttonIs = digitalRead(buttonPin); //Read the buttong state.
}

void errorBlink(){ //Flash 4 times for an error
     for (int x=0; x <=3; x++){  
   digitalWrite(ledPin, HIGH);  
    delay(100);  
    digitalWrite(ledPin,LOW);  
    delay(100);  
   }  
}

char * resetXport(){ //Handles the regular reset of the Xport, returns reset errors
        ret = xport.reset();
       switch (ret) {
          case  ERROR_TIMEDOUT: {
           Serial.println("Timed out on reset!");
           errorBlink();
         return 0;
      }
      case ERROR_BADRESP:  {
         Serial.println("Bad response on reset!");
         errorBlink();
         return 0;
    }
  case ERROR_NONE: {
     Serial.println("Reset OK!");
     break;
    }
    default:
      Serial.println("Unknown error");
      errorBlink();
      return 0;
       }
  delay(250);
}


char * ConnectToggle(){ //This function turns the connection on or off
  
  if (ledState == 1){ //if the LED is off, then the client is not connected.
  resetXport(); //run the reset function
  ret = xport.connect(IPADDR, PORT);
  switch (ret) { //swtich case handles various error states
    case  ERROR_TIMEDOUT: {
     Serial.println("Timed out on connect");
      ConnectState = false;
      errorBlink();
     return 0;
  }
  case ERROR_BADRESP:  {
     Serial.println("Failed to connect");
      ConnectState = false;
      loopCount++;
      Serial.print("Loop count =");
      Serial.println(loopCount);
      ConnectToggle();
      if (loopCount >  2){  //For reasons I have not been able to figure out, it usually takes two tries to connect    
         errorBlink();
         ConnectState = false;
         break;
  }
  }
  case ERROR_NONE: {
    Serial.println("Connected..."); 
    ConnectState = true;
    Serial.println(ConnectState);
    digitalWrite(ledPin, HIGH);
    ledState = 0;
    loopCount = 0;
    break;
  }
  default:
    Serial.println("Unknown error");
     ConnectState = false;
     errorBlink();
    return 0;
 }
 }
  else { //Toggle off connection
    ledState = 1;
    digitalWrite(ledPin, LOW); //turn OFF the LED
    xport.println("x"); //Send the x command that kills the session.
    delay(100); 
    xport.disconnect(); 
    Serial.println("Disconnected.");
    ConnectState = false;
  }
}

void fetchStuff() { //Gets the linebuffer
   xport.flush(100);
   xport.println("g"); 
   ret=xport.readline_timeout(linebuffer, 16, 200); // get first line
   while(ret!=0){
     ret=xport.readline_timeout(linebuffer,16,200);
  }
}

void moveServos(){ //Note that the server sends out the X, Y & Z values, but Z is not used. It could be.
   Xval = (((linebuffer[1] - 48) * 100) + ((linebuffer[2] - 48) * 10) + (linebuffer[3] - 48)); //Extract X value & convert the ASCII to integers
//  Serial.print("Xval:  ");
//  Serial.println(Xval, DEC); 
  
  Xservo = map(Xval, 275, 425, 0, 180); //Map the range of X values from the server to the servos
   Serial.print("Xservo:  ");
  Serial.println(Xservo, DEC);
  Xservobj.write(Xservo);
  delay(15);  
   
   Yval = (((linebuffer[5] - 48) * 100) + ((linebuffer[6] - 48) * 10) + (linebuffer[7] - 48));//Extract Y value & convert the ASCII to integers
//   Serial.print("Yval:  ");
//   Serial.println(Yval, DEC); 

  Yservo = map(Yval, 275, 425, 0, 180); //Map the range of Y values from the server to the servos
   Serial.print("Yservo:  ");
  Serial.println(Yservo, DEC);
  Yservobj.write(Yservo);
  delay(15);     
}
  
//==============Main loop==================//

void loop()   { // run over and over again

getButton(); //Run to check if button has been pressed.

if((buttonIs == 1) && (buttonWas == 0)){
  ConnectToggle(); //If there is a change in the button state, toggle the connection on or off.
}

if (ConnectState == true){ //If the client is connected, do some work!!
  fetchStuff();
  moveServos();
  }
}

Further work
The system needs a way to detect dropped connections - possibly using "millis()" to determine if a response or query has taken too long. As well, I really should smooth out those analog readings so the servos don't jitter! As well, I might just build a motion ase of some sort to hook up to the servos so they can do something slightly more cool that just flap their arms.
So, that's my first Arduino project in a few months! Thanks for the comments and questions. The site has apparently had over 11,000 hits, which completely amazes me. Please keep the comments and suggestions coming. I think next up will be another Chinese knock-off phone review.


Thursday, September 3, 2009

Arduino POP3 Email Checker

Since it turns out people actually occasionally READ this blog, I decided it was time to go back to do an Arduino project and try something different. Since I hadn't done any Arduino work with the Internet before, I decided to try something I thought would be relatively simple - having the Arduino check my email and give me some sort of visual indication of how many emails I have. I started out with:
Both were bought from the good people at Robotshop.ca.




These are just combined with a high intensity LED - which really should have a resistor and will have one eventually!



Here is a quick video - sorry it is a bit murky and the LED is a bit bright!



This was actually probably the hardest Arduino code I ever wrote! For one thing, I hadn't done anything substantial with the Arduino for a while and I found I had forgotten much of what I thought I knew. Also, getting the timing right and getting the number of emails out of the return string actually proved pretty difficult. I made things more difficult for myself by trying to use the String library (formerly TextString), which for some reason didn't return consistent results and was generally not documented and finicky. Then I found the usual LED 13 wouldn't work properly - perhaps because the Ethernet Shield was interfering with it. All-in-all, this small program must have taken me three weeks to write!

I did have a bit of help with the code from Digger450 on the Arduino forum in this exchange, which I am very grateful for!

However, now that it is done, this is a nice little demo of Ardunio on the Internet that does do something at least semi-useful. My next extension may be to hook it up to a servo so that it shows my emails on a physical chart or something. I could also use my SparkFun SerialLCD unit to display the subject lines or something.

Here is the source code:

/*=================================================
Ethernet POP3 Mail Checker & indicator

Checks how many messages are waiting on the POP 3 server
and flashed LED on Pin 9 to indicate number of messages.

It will handle up to 99 messages in the POP3 mailbox.

Uncomment the serial lines for troubleshooting.

Copyright by Chris Armour
3 September 2009
http://opensourceprojects-torchris.blogspot.com/

===================================================*/

#include <Ethernet.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192,168,0,167 }; // IP address you wish to assign to Arduino
byte server[] = { XXX, XXX, XXX, XXX }; // IP address of your POP3 server
char inString[165]; // Number of characters to be collected
int i = 0;
int mailNum1 = 0; // First digit of the email number
int mailNum2 = 0; // Second digit
int mailTotal = 0; // Total # of messsage
char d;
int ledPin = 9;

Client client(server, 110); //The default POP port is 110

long updateTimer;
boolean clientConnected = false;

void setup()
{
//  Serial.begin(9600);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
}

void loop()
{
   
updateClient();
d = checkAvail();
if (d >= 10){
getMailNum();
}

}

/*========================================
        Functions
=========================================*/

void updateClient() //This function contacts the POP3 server
{
if ((millis() - updateTimer) > 5000)
{
 Ethernet.begin(mac, ip);
//    Serial.println("connecting...");
 delay(1000);
 if (client.connect())
 {
//    Serial.println("connected");
 client.println("user Your.Name"); //Insert your usual email login name
 client.println("pass PassWord"); //And your password here
 client.println("quit");
 client.println();
 clientConnected = true;
 }
 else
 {
//    Serial.println("connection failed");
 }
 updateTimer = millis();
}
}

char checkAvail() //This checks if there is data available and returns a char
{
if (clientConnected)
{
 if (client.available())
 {
 char c = client.read();
     return(c);
 }
 if (!client.connected())
 {
//    Serial.println();
//    Serial.println("disconnecting.");
 client.stop();
 clientConnected = false;
 }
}
}

int getMailNum() //This actually loads the char returned by checkAvail() and puts in into an array
{
inString[i] = d;
i++;
if (i == 165){
   i = 0;
       client.flush();
       mailNum1 = inString[106] - 48; //Array position 106 contains the first digit
       mailNum2 = inString[107] - 48; //Array position 107 contains the 2nd digit if it is available
       if ((mailNum2 >= 0) && (mailNum2 <= 9)){ //If mailNum2 is present, then it is a two digit mail count
         mailTotal = (mailNum1 * 10) + mailNum2; //when 2 digits are present, multiply the 1st by 10 then add to mailTotal
//            Serial.print("Total emails:  ");
//            Serial.println(mailTotal);
          blinkLED(); //Run the blink function as many times as there are emails
       }
       else {
         if ((mailNum1 >= 0) && (mailNum1 <= 9)){//if there is only one digit, then that is mailTotal
         mailTotal = mailNum1;
//        Serial.print("Total emails:  ");
//        Serial.println(mailTotal);
         blinkLED(); //Blink the LED
         }
         }
     }
 }

void blinkLED(){ //Blinks the LED for as many times as indicated by mailTotal
         for(int x = mailTotal; x >= 1; x--){
           digitalWrite(ledPin, HIGH);
           delay(200);             
           digitalWrite(ledPin, LOW);
           delay(200);
         }
}