Showing posts with label Arduino. Show all posts
Showing posts with label Arduino. Show all posts

Friday, June 6, 2014

Wire Wrap Introduction Video

This post is to provide a link to my new video on YouTube and a bit more accompanying explanation.

First off, here is the video:


For those who are curious about some further explanation of the process and purchasing wire wrap supplies, I see that Jameco has a great online article and carries the supplies.(Check it out here). Wikipedia also has a great article on wire wrapping here.

I recall working for a company in the 1990s that was still using it extensively for building aircraft maintenance simulators because it is reliable, quick and easy and well suited to very small production run products. Early computers used the technique extensively. Check out this picture of a Z80 backplane from 1977:


The main challenge is obviously keeping track of the many point-to-point connections when you are looking at the back of the board! I seem to recall the firm I worked for had some sort of system of tables for keeping track of what went from where to where. Generally, I find that if I am going to have problems with a wire wrap assembly, it will be an error I have made in losing track of which pin is which when I flip the board over!

That's it for now! Soon I will do a longer post on my first work with the nRF24L01 radios and my DigiX board. I have something almost working and when it's done I will provide more details.




Wednesday, May 14, 2014

Bringing it all together!

Over the last four blog posts, I have been working step-by-step on creating a basic sensor network in which my BeagleBone takes information from my BlueLine Innovations PowerCost meter and my DigiX Arduino and presents it in a single web interface. Finally, after around three months, I have this working at a level I feel I can write up.

The source code is available on GitHub here.

The system diagram for this project has now expanded to include the new components:


To see how the DigiX is connected to the OneWire sensor, see my previous post here.

Here is a "family portrait" of the hardware on my desk:



I have also updated the layout of the web interface in Jade to move a few elements off to a sidebar:




Here is the code...

App.js


This takes the code I worked up in the last few projects and wraps into one package. Check out GitHub for the full source, but here are a few of the highlights.

The "getData" function has been updated to include two different Request functions - one for the power and outside temperature from the BlueLine Innovations PowerCost gateway and the other from the DigiX OneWire sensor.


function getData() {
    request({
    uri: "http://192.168.1.33/pcmconfig.htm"
    }, function(error, response, body) {
        if (!error && response.statusCode == 200) {
            var n = body.search("Present Demand");
            usage = body.substr((n + 44), 5);
            usage = usage.trim();
            console.log("Power Usage from PowerCost" + usage);
            var n2 = body.search("Sensor Temp");
            temp = body.substr((n2 + 40), 3);
            temp = temp.trim();
            temp = temp - 2;
            console.log("Temp from PowerCost:" + temp);
        }
    });
    request({
        uri: "http://192.168.1.7:3010/temp"
    }, function(error, response, body) {
        if (error) {
            //If there is an error or the DigiX is unreachable, set the value to an error.
            console.log("Error: DigiX not available");
            tempDigiX = 19.99;
            DigiXAvailable = "FALSE";
            console.log("DigiX status: " + DigiXAvailable);
        }
        else if (!error && response.statusCode == 200) {
            var n = body.search("TEMP:");
            tempDigiX = body.substr((n + 6), 5);
            tempDigiX = tempDigiX.trim();
            console.log("Current temp reading on DigiX: " + tempDigiX);
            DigiXAvailable = "TRUE";
            console.log("DigiX status: " + DigiXAvailable);
        }
    });
    var readingInfo = new Readings({
        temp2: temp,
        usage2: usage,
        DigiTemp: tempDigiX
    });
    readingInfo.save(function(err, readingInfo) {
        if (err) return console.error(err);
        console.dir(readingInfo);
    });
}

//run the above function

setInterval(getData, (process.env.REFRESHINT * 1000));




(See my previous post here on the slightly "hacky" way I get the temp info off of the DigiX.)

The info is them popped into the "readingInfo" object and then into the TingoDB database. Then a 'setInterval" runs the GetData function on the timing set by the REFRESHINT environment variable. If the Request function for the DigiX can't connect then it sets the DigiXAvailable variable to FALSE and also sets the temperature to a nominal "19.99". I played around with various mechanisms to test and flag that the link to the DigiX was up or down and this was the best I could come up with for now. It does have a lot of lag since it won't set the unavailable status on the web page until it has run through a full three minute cycle.

The other big part of App.js is the Socket.io section:

io.sockets.on('connection', function(socket) {
    console.log('A new user connected!');
    Readings.find({}, {}, {
    sort: {
        'time': -1
    },
            limit: process.env.SAMPLES
}, function(err, readings) {
            socket.broadcast.emit('readingsData', readings);
            console.log(process.env.SAMPLES + ' readings sent over');
            console.log('DigiX Status sent over socket = ' + DigiXAvailable);
            socket.broadcast.emit('digiXStatus', DigiXAvailable);
        });
    setInterval(function() {
        Readings.find({}, {}, {
            sort: {
                'time': -1
            },
            limit: process.env.SAMPLES
        }, function(err, readings) {
            socket.broadcast.emit('readingsData', readings);
            console.log(process.env.SAMPLES + ' readings sent over');
            console.log('DigiX Status sent over socket = ' + DigiXAvailable);
            socket.broadcast.emit('digiXStatus', DigiXAvailable);
        });
    }, (process.env.REFRESHINT * 1000));
    socket.on('sampleInput', function(sampleInputSetting) {
        console.log('setting data = ' + sampleInputSetting);
        process.env.SAMPLES = sampleInputSetting;
        socket.broadcast.emit('sampleSetting', sampleInputSetting);
        console.log('Sending sample rate back out');
    });
    socket.on('refreshInput', function(refreshInputSetting) {
        console.log('setting data = ' + refreshInputSetting);
        process.env.REFRESHINT = refreshInputSetting;
        socket.broadcast.emit('refreshSetting', refreshInputSetting);
        console.log('Sending refresh rate back out');
        Readings.find({}, {}, {
            sort: {
                'time': -1
            },
            limit: process.env.SAMPLES
        }, function(err, readings) {
            socket.broadcast.emit('readingsData', readings);
            socket.emit('readingsData', readings);
        });
    });
});

This is pretty similar to the original Socket.io version of my project (check it out here)it's just that now it also sends over the DigiXAvailable flag to the browser for processing to alert end-users of the status of the DigiX.

I really must start using the "Promises" framework in Node to simplify some of these nested functions!


Main.js

 

 

On the client side, the codes is again pretty similar to the last version of this project, except now I have included this in the Socket.io section:

    socket.on('digiXStatus', function(digiXStatusData) {
       console.log('DigiX Status received: ' + digiXStatusData);
        if (digiXStatusData == "FALSE"){
            $('#DigiStatus').text('Offline');
            $('#DigiStatus').css('color', 'red');
            } else {
            console.log("DigiX online");
            $('#DigiStatus').text('Online');
            $('#DigiStatus').css('color', 'green');
            }
    });    




This will take the DigiXAvaialble flag from the socket (renamed digiXStatusData when it gets over to the browser) and then write info to the "#DigiStatus" DIV on the webpage using jQuery.



Index.Jade

 

I decided to change the layout to put a "status" block and the controls off to the left side to make the layout a bit more horizontal. To do this I created CSS elements in Stylesheet.css for the "Status" on the left side and the "Content" area where the chart data goes:

status {
  position: absolute;
  left: 0;
  width: 15em;
}

#content {
  margin-left: 15em;
}




I then created a DIV in Jade and put the content where it belonged:

extends layout

block content    
  #status
    h2 System Status
    table
      tr
        td
          b DigiX:
        td
          #DigiStatus
      tr
        td 
          b BeagleBone:
        td
          #logger 

    h2 Settings
    p Data to graph: 
       input#dataSampleInput(value= '#{sampleNum}', type='text')
    p Time between refresh (sec)
       input#refreshTimeInput(value= '#{refreshRate}', type='text')

    button#submitBtn Submit

  #content
    h1= title

    #legendholder(style='margin-left: 110px;')
    #placeholder(style='width:700px;height:300px')

    h2 Current Readings
    #tablediv
      table
       thead
         tr 
           td 
             b Timestamp
           td
             b Outside Temp
           td
             b Power usage
           td
             b DigiX Temp
         tbody
           tr
             td
               #currTime
             td
               #currTemp
             td
               #currUsage
             td
               #currDigiX


Jade takes some getting used to, but it does allow you to generate a webpage without many lines of code!

DigiX software



This is identical to my post here - the DigiX serves up information depending on which URL is requested. I chose to not implement the digital On/Off controls in this version, but they would be easy enough to include in the future.

Conclusion and next steps


Well, that's about it for this phase. There are a few things I still need to track down...

  • As I said, there is a lot of lag in alerting on the browser if the DigiX goes offline (which it does occasionally) - basically it goes through at least one of it's 180 second cycles. I played around with a "heartbeat" or "ping" function, but I couldn't quite get it to work.
  • Since the DigiX has an SD card slot, it would be good to be able to buffer readings when there is a network interruption and then send them over once the connection is re-established.
  • It would be good to throw an LCD display onto the DigiX to display the sensor readings. 
  • I need to add a few more sensors onto the DigiX as well - light level and humidity, for instance!

Next up, I want to exercise the mesh radio features of the DigiX. A few months back, I bought a few of these NRF24L01+ radios with serial modules off of eBay (check them out here):
I can hopefully use one of these with one of my regular Arduinos just using serial! Check back in a few weeks on that!





Monday, April 21, 2014

Integrating the Beaglebone and DigiX - Part 2 of 2

This is the second part of my adventures with the Digix! In this blog, I will describe the software and the slightly "hackified" way I got the DigiX and my Beaglebone to talk to one another.

The software is all posted to GitHub here:

https://github.com/torchris/digixbeagle

If you missed the first section of this, check it out here.

Hardware setup

 

Here is a diagram of the breadboard I did up in Fritzing. Note that I have used an Arduino Due instead of the DigiX because there is not yet a DigiX image for Fritzing and the Dues is a similar size and shape.



This is very straightforward and there are many good articles on using the DS18B20 OneWire sensor with the Arduino. Here is an excellent article by Simon Tushev. There are also two LEDs. One shows when requests are being processed and the other shows how a digital output on the DigiX can be controlled by the web page off the BeagleBone.

Server side


The server code for this running on the BeagleBone is a basic Express site modified to use Socket.io and using the Request library to get information from the DigiX. The server then uses Socket.io to push the out to a browser. It also takes input from the browser and sends it back over to the DigiX.

The one thing to note here, which I should have remembered from the last time around is that you can't use the Express template with Socket.io right off the bat. This post from Stackoverflow explains the situation. Just to extract the most salient bit:
Express 3 requires that you instantiate a http.Server to attach socket.io to first:

meaning - (1) you must create a server instance:

var app = express();
var http = require('http').createServer(app);
 
(2) couple it with the socket.io:

var io = require('socket.io');
io.listen(http);
 
and ONLY THEN - (3) make the server listen:

http.listen(8080);
 
make sure you keep this order!
I must tattoo  that on my forehead so I don't waste time with Socket.io not working!!

Anyway, the App.js code is basically all stock except for the change above and this function that gets the info from the DigiX using web page calls and the Request library (more on that later on) and then does some simple text processing to extract the temp.:

function getData() {
    request({
        uri: "http://192.168.1.7:3010/temp"
    }, function(error, response, body) {
        if (error){
            //If there is an error or the DigiX is unreachable, set the value to an error.
            console.log("Error: DigiX not available");
            temp = "DigiX not available";
            }
        else if (!error && response.statusCode == 200) {
            var n = body.search("TEMP:");
            temp = body.substr((n + 6), 5);
            temp = temp.trim();
            console.log("Current temp reading: " + temp);
        }
    });
    setTimeout(getData, 10000);
}
getData();



The other main part of App.js is the Socket.io section:

io.sockets.on('connection', function(socket) {
    console.log('A new user connected!');
    console.log("Sending over initial LED state: " + ledStatOn);
    socket.broadcast.emit('ledStatOn', ledStatOn);
    setInterval(function() {
        socket.broadcast.emit('tempData', temp);
        console.log("Temp sent over sockets to client; " + temp);
    }, 10000);
    socket.on('ledStatOn', function(ledStatOn) {
        if (ledStatOn == "ledOn") {
            console.log('LED data = ' + ledStatOn);
            request({
                uri: "http://192.168.1.7:3010/on"
            }, function(error, response, body) {
                if (!error && response.statusCode == 200) {
                    var x = body.search("ledCmded:");
                    ledOnStat = body.substr((x + 10), 2);
                    ledOnStat = ledOnStat.trim();
                    console.log("LED status is " + ledOnStat);
                    socket.emit('ledStatOn', ledOnStat);
                }
            });
        }
        else if (ledStatOn === "ledOff") {
            console.log('LED data = ' + ledStatOn);
            request({
                uri: "http://192.168.1.7:3010/off"
            }, function(error, response, body) {
                if (!error && response.statusCode == 200) {
                    var x = body.search("ledCmded:");
                    ledOnStat = body.substr((x + 10), 3);
                    ledOnStat = ledOnStat.trim();
                    console.log("LED status is " + ledOnStat);
                    socket.emit('ledStatOn', ledOnStat);
                }
            });
        }
    });

});





When the Socket.io connection is established, this sends over the temerature info from the DS18S20to be displayed on the browser. As well, when commands to turn on or off the LED come over from the browser via Socket.io, the Request library is used to trigger these actions on the DigiX.

Browser side

 

The web interface for this is very, very simple. Just a basic Jade-generated page:


Here is the JavaScript code for the page:

var socket = io.connect();



$(document).ready(function() {

 socket.on('ledStatOn', function(ledStatOn) {
        console.log('Received LED status ' + ledStatOn);
    });   
    
$("input:radio[name=ledStat]").click(function() {
    var val = $('input:radio[name=ledStat]:checked').val();
    console.log(val);
        socket.emit('ledStatOn', val);
});

    
    socket.on('tempData', function(tempData) {
        $('#logger').text('Web server connected.');
        $('#logger').css('color', 'green');
        console.log("Server Connected");
        console.log(tempData);
        $('#tempData').html(tempData);
        socket.on('disconnect', function() {
            // visually disconnect
            $('#logger').text('Web server disconnected.');
            $('#logger').css('color', 'red');
        });
    
    });
});





This reuses the server connected/disconnected status used in the previous project. It also uses "socket.emit" to send over to teh server the user selection for the digital output on the DigiX.

Index.jade is also pretty simple - just the DIVs to write the info into and the radio buttons for the LED:

extends layout

block content

  h1= title
  p Welcome to #{title}
  #logger
  br
  p Curent temperature from DigiX:
  b#tempData
  br
  br
  input(type='radio', name='ledStat', value='ledOff', checked='CHECKED')
  | LED Off
  input(type='radio', name='ledStat', value='ledOn')
  | LED On



DigiX

 

This is where it maybe gets a bit silly, but it does work. As I explained in my last post, I couldn't figure out how to get Socket.io or even good-old Websockets (which I used four years ago with this project in PHP). I am sure someone with more brains or patience could easily come up with an answer, but I got impatient to get this going, so I came up with this solution.

I noticed in the DigiX webserver demo application that the DigiX was parsing the value of the page requested (of course it would have to) and I figured that with different pages being called, the sketch could execute different Arduino commands. In other words, to get the application to do something on the on the DigiX, I just called different web addresses. Here is what this sketch implements. When I call these URLs, I get the following responses:
  • http://192.168.1.7/on = turn ON the LED at digital output 8
  • http://192.168.1.7/off = turn OFF the LED at digital output 8
  • http://192.168.1.7/temp = print a simple webpage with the DS18B20 OneWire temp reading
Nice and simple! Also easy to troubleshoot because I can just use any browser to navigate to those pages and see the results. Is this a basic RESTful API? (I use the LED on pin 9 to show when a web request is being processed.)

Here is the code:

#include 
#include 
#include 
DigiFi wifi;
int ledCmded = 8;
int ledStat = 9;
#define ONE_WIRE_BUS 10

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

void setup()
{
  pinMode(ledCmded, OUTPUT);
  pinMode(ledStat, OUTPUT);
  digitalWrite(ledCmded, LOW);
  digitalWrite(ledStat, LOW);
  Serial.begin(9600);
  wifi.begin(9600);
  sensors.begin();
  //DigiX trick - since we are on serial over USB wait for character to be entered in serial terminal
  while (!Serial.available()) {
    Serial.println("Enter any key to begin");
    delay(1000);
  }

  Serial.println("Starting");

  while (wifi.ready() != 1)
  {
    Serial.println("Error connecting to network");
    delay(15000);
  }

  Serial.println("Connected to wifi!");
  Serial.print("Server running at: ");
  String address = wifi.server(3010);//sets up server and returns IP
  Serial.println(address);

  //  wifi.close();
}

void loop()
{

  if ( wifi.serverRequest()) {
    Serial.print("Request for: ");
    Serial.println(wifi.serverRequestPath());
    if (wifi.serverRequestPath() == "/off") {
      digitalWrite(ledStat, HIGH);
      digitalWrite(ledCmded, LOW);
      Serial.println("ledCmded off");
      wifi.serverResponse("

ledCmded: OFF

");       digitalWrite(ledStat, LOW);     }     else if (wifi.serverRequestPath() == "/on") {       digitalWrite(ledStat, HIGH);       digitalWrite(ledCmded, HIGH);       Serial.println("ledCmded on");       wifi.serverResponse("

ledCmded: ON

");       digitalWrite(ledStat, LOW);     } else if (wifi.serverRequestPath() == "/temp") {       digitalWrite(ledStat, HIGH);       float gotTemp;       gotTemp = getTemp();       Serial.print("Temp =  ");       Serial.println(gotTemp);       wifi.println("HTTP/1.1 200 OK");       wifi.println("Content-Type: text/html");       wifi.println("Connection: close");  // the connection will be closed after completion of the response       wifi.println();       wifi.println("");       wifi.print("

TEMP: ");       wifi.print(gotTemp);       wifi.print("

");       digitalWrite(ledStat, LOW);     }     else {       wifi.serverResponse("

Nothing doing

"); //defaults to 200     }   }   delay(10); } float getTemp() {   float currTemp;   sensors.requestTemperatures(); // Send the command to get temperatures   currTemp = sensors.getTempCByIndex(0);   Serial.println(currTemp); // Why "byIndex"? You can have more than one IC on the same bus. 0 refers to the first IC on the wire   return currTemp; }


This works as an easy way to get the kind of interaction I wanted for this project, which is just to exchange some simple sensor info in a non-realtime way and test a Server -> Arduino digital output. However, this has some obvious limitations. It would be hard, for instance, to exchange analog information from the server to the Arduino (for instance, to dim an LED using PWM). You could perhaps have the BeagleBone server send over a series of URLs like "http://192.168.1.7/analog789" where "789" is the analog value and the Arduino code parses that out of the request and then sets the PWM value, but I doubt you could do this more than a few times per minute.  In other words, this is a nice easy way to get this project working, but I can't see how you could do something like my earlier project where I exchanged realtime movement info over the network (see this project).

That's it for now. Next up will be to include this DigiX sensor data in my previous code so I start to have a real "sensor network". Eventually, I want to use the mesh networking off of the DigiX to get sensor data from several other Arduinos (hopefully the tiny DigiSparks if I can make them work).



Friday, April 11, 2014

Getting started with the DigiX - Part 1 of 2

This is the first part of my project to integrate the Digistump DigiX board with the BeagleBone. In this post, I'll provide a review of the DigiX and some of the good and less good points about working with it. The next section will describe in more detail the software and process I used to build the project.

First, for your multimedia viewing pleasure,  here is a quick video tour of this fairly simple project:


Introducing the DigiX

The DigiX is produced by DigiStump which was founded in 2012 by Mr. Erik Kettenburg in the Seattle area. I first became aware of them when I backed his successful Kickstarter for the DigiSpark which is a super-small $9 Arduino development board  - I will include them in a later part of my monitoring system. A few months after the DigiSpark shipped, I saw the Kickstarter come up for the DigiX and decided to go for it.

The specs on the DigiX are, frankly over the top! This is the Monster Killer Arduino of all Time (at least for now) - check it out:


In form factor, it looks a bit similar to the Arduino Due, but it has a double row of IO pins down the one side whereas the Due only has a single row.

This is a beast!! As well it has BOTH on-board wifi and mesh networking via the popular, low-cost nRF24L01+ wireless module and and a micro SD card slot. All this for only $69.95 USD!

 That being said, given that the BeagleBone Black is only $44.95 and the RaspBerry Pi is only $39.95 (prices from SparkFun) and both of them run full up Linux. Is it worth the extra money to have the DigiX? Here are a few thoughts on pros and cons...

Pros

  1. Rather than having to wrestle with the complexity of managing GPIO in the Linux environment, the DigiX supports the usual Arduino IDE and it's familiar interface. If you're used to stock Arduinos, then there is no ramp up.
  2. The onboard wifi works as advertised and is a breeze to configure according to the DigiX Wiki article (check it out here).
  3. There are more IO pins than any reasonable person could ever use and there is loads of program memory space (524,288 bytes).
  4. There is a good range of sample programs that exercise the functionality of the board and they all seem to compile and work right away. I was amazed that I had a simple web server up and running in about 15 minutes!
  5. I haven't tried the mesh networking yet, but I am assuming it works and it's awesome to have it on the same board.
  6. This board is a 3.3 VDC board (like the other newer Arduinos), but Digistump sells a level shifting shield so you can reuse older 5 VDC shields, which is very useful if you're an old-timer like me.
  7. There is a reasonable amount of documentation and active forums for help.

Cons


The main draw back is is it not quite a stock Arduino and a couple of compromises have been made to keep the board affordable. Let's look at two things I have found...

Wifi

The wifi module on the DigiX is not the same as on the Arduino Yun. The Yun uses the AR9331 while the DigiX uses an Atheros Silicon based embedded UART/WiFi module(source here). This means the DigiFi wifi library is mostly compatible with the Arduino Ethernet library, but not 100%.

Consequently, for the project I just completed, I wanted to use Socket.io or websockets to exchange information between my Beaglebone and the DigiX. I tried various sockets libraries and just could not find a way to make them work with the DigiX! I posted a thread to the DigiX forums, but, since they're somewhat less active than the Arduino boards, I so far haven't found a solution. You will see in my upcoming project that the solution I found works, but is definitely "suboptimal" from a design standpoint.


USB Serial and programming

Mr. Kettenburg provides a good technical description of what happens with USB serial on the DigiX and Due boards here. You can read the detailed explanation, but what it boils down to is that the Arduino Due has two USB ports - one for programming and one for native USB - while the DigiX has only one USB port which has to work for programming and USB. Unfortunately, on the DigiX it seems to sometimes be a coin toss as to which is going to be available when you boot up the board, which caused me some headaches at first.

When I was first using the board, it would do something like this when doing programming:
  1. The Arduino IDE is set on COM16 and you hit the upload button in the IDE.
  2. The program compiles and if there are no errors, then it goes to upload and says "The COM port isn't available".
  3. Restart the DigiX and if you are lucky it comes up with COM15.
  4. While on COM15, hit the upload button and it goes through and programs the board.
  5. However when you go to the IDE to select the serial port for the serial monitor, it is back to COM16!
This could be a huge hassle sometimes requiring multiple restarts of the board and plugging and unplugging the USB until the right port came up. As well, the IDE serial monitor would sometimes not fire up and that required more restarts.

At the moment, I seem to have solved this and it is working smoothly. What I did was follow this advice from the DigiX wiki:

If the COM port isn't showing in the Arduino IDE - unplug and replug the board. If that doesn't work - while plugged in, hold down the erase button on the board for a moment and then unplug and replug - you may then have to select it from the com port menu as it may be on a different port - but it is a sure way to get it to respond even if your sketch crashed the USB stack. 

At the same time as doing this, I moved it to another USB port on my system and Windows reloaded the drivers. It works normally now, but it leaves me concerned it will stop working again in the future. I guess I shouldn't complain because it is a bit of an RTFM situation, but perhaps future versions of the board could have the two port solution like the Due or maybe a jumper block to put the USB port into one mode or the other to avoid this confusion?

Summary


Despite the minor hassles I would still say the DigiX is a great board and worth the money. It gives you loads of room to grow and build very sophisticated networked projects. If you just want something relatively simple to take some digital/analog inputs and then do some digital.analog outputs, then this board may be too much and you should look at some of the simpler Arduinos on the market.

As the "Internet of Things" movement develops, it will be de rigueur to be able to to hook your embedded project to the Internet and to other devices via either mesh or Bluetooth Low Energy. The DigiX has you covered with wifi and mesh on board and while it is more expensive than some boards, you save by having all the connectivity integrated and not having to get separate shields and so on.

Nest up, the software the drives this project!








Wednesday, October 23, 2013

Resurecting the Arduino/Xbee/Gumstix sensor network

About four years ago, I built a simple sensor network that used a couple of Arduinos, a Gumstix Linux mini computer and Xbee wireless that acted as an indoor-outdoor temperature display. A couple of years back, the outdoor part of the system stopped working. I poked it and prodded it and finally just decided the Arduino clone I was using had died.

Finally, after it had sat on my shelf for a couple of years, I figured I should start to look at salvaging some of the parts for something new. Just as a last chance, I once again unplugged the processor and juggled the wires and suddenly, it works like a charm!! As with most thing, the threat of imminent destruction got it moving!

Anyway, in honour of it's miraculous recovery, I have fixed up my blog posts and reinserted the graphics Google lost for me. The posts can be found at:

  1. Overview of the sensor network
  2. Indoor Outdoor temp display
  3. Xbee -Gumstix hardware
  4. Gumstix-Arduino software

This project is pretty old now and obviously could be done much more easily with newer models of Arduino like the Yun, but it does show some good basics on getting Zigbee and PHP and Arduino all working together. Apparently, too, it is capable of great longevity!

Sunday, October 6, 2013

Apologies and fixes!

Hello all,

It has been a very, very, very long time since I have updated this blog, but I am still around!

I just wanted to take a moment to apologize to the few of you who might stumble onto this blog and find that the graphics are missing. It seems a while ago Google changed the way it stored embedded graphics in Blogger and it messed up all the links in most of my posts! This happened a while ago and I hadn't visited in a while, but I finally got around to fixing up at least most of the posts.

I know a few people have gotten to my blog from Arduino-related searches, so I have focused on trying to fix up my Arduino related posts - although most of these are on rather antiquated hardware.

Hopefully soon I will get to some projects with the Digistump DigiSpark which is a small form factor Arduino compatible board. You can read more about it here.

Thanks!

Thursday, August 5, 2010

Arduino-Twilio Dialer Application

Well, it has been a while since I posted an Honest-to-God Arduino project here! What with getting my eye surgery done and one thing and another, this has taken me quite a while. Besides, there were more than a few challenges in getting it all to work along the way!

This project is a concept for an information kiosk or unit that could be placed in a public area where passers-by could request information. A user enters their mobile phone number into the keypad and they are then immediately called by the Twilio cloud-based telephony system (check out Twilio here) and presented with a simple phone menu that allows them to:
  1. Talk to an operator
  2. Leave a voice message
  3. Receive an SMS message
Here is the hardware in it’s not very pretty form:








































Here is the obligatory video showing how it works:




Hardware

The hardware consists of:
  • Arduino Duemilanove
  • Arduino Ethernetshield
  • Sparkfun serial enabled 20 x 4 LCD display (LCD-09568)
  • Sparkfun 12 button keypad (COM-08653)
Here is the circuit diagram:


The hookup of the keypad is based on this article by amando96 on Instructables. You will have to sign up for membership to see the whole article or you can just follow the above diagram. One odd thing I noticed was that the pull down resistors turned out to not be required. Originally I had them included and everything worked fine, then at a certain point in the development the keypresses started looping and I removed the resistors and everything worked fine!

This just uses the standard “keypad.h” library found on the Arduino website, but slightly modified so the pins do not interfere with the Ethernetshield. There is also an Instructable post on multiplexing the button input to use fewer digital pins, but since I didn’t need any extra digital pins I figured this was simpler.

The serial LCD is quite straightforward to hookup, but just note that I am using one of the A1 Analog pin as the Tx for the serial since I am not using any analog inputs anyway.

Software

As with the previous Twilio-Arduino project, this one requires:
  • An account with Twilio 
  • A web-server with PHP
  •  An Ethernetshield
The Arduino communicates with one PHP script running on my web-server, this then triggers Twilio to set up a call and Twilio then goes to my web-server and checks for another PHP file on what options to present to the user. It is a reasonably complex mesh of files that need to be setup, but actually the Twilio XML programming was easily the simplest part of the setup.

This diagram shows the overall flow:





The first part is the Arduino software:





/*
*  Twilio Arduino Information Service Dialer
* When used in conjunction with "dialer_twilio.php PHP script and the Twilio cloud-based
* telephony environment (www.twilio.com) this allows a user to enter a phone number into a keypad
* on an Arduino and have a call placed to their cell phone with various options for information.
*
* 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, August 4th, 2010
*
* Full description psted at http://opensource-torchris.blogspot.com/
*
*/

//=====================Libraries=============/
#include 
#include 
#include  
#include 

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 = 55455;
const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns
char keys[ROWS][COLS] = {
  {'1','2','3'},
  {'4','5','6'},
  {'7','8','9'},
  {'*','0','#'}
};
byte rowPins[ROWS] = {5, 4, 3, 2}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {8, 7, 6}; //connect to the column pinouts of the keypad
boolean ConnectedState = false;
char DialNum[12]={"xxxxxxxxxxx"};
int a;
NewSoftSerial mySerial =  NewSoftSerial(14,15); //osft serial running off of Analog Pin 1

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
Client client(server, ServerPort); 
TextFinder finder( client);

void setup(){
  Ethernet.begin(mac, ip);
// Serial.begin(9600);
  mySerial.begin(9600); 
  mySerial.print(254, BYTE);
  delay(100);
  mySerial.print(1, BYTE);
  delay(100);
  mySerial.print("Started - Press any key to conect to the server."); //Display initial message on LCD.
}
  
void loop(){
    
  char key = keypad.getKey();

  if (key != NO_KEY){
    if (ConnectedState == false){
       if (client.connect()){      
         if(finder.find("results:") == true){
           char c = client.read();
         // Serial.print("Value of the connect code: ");
         // Serial.println(c);
           mySerial.print(254, BYTE);
           delay(100);
           mySerial.print(1, BYTE);
           delay(100);
           mySerial.print("Connected. ");
           delay(2000);
           WelcomeMsg();    
           //If connection successful, then print Welcome Msg and flip the connected state variable.
           ConnectedState = true;
         // Serial.println(DialNum);
           }
       }
       else {
       ConnectedState == false;
       ConnectFailMsg(); //If connection fails, then print an error.
       delay(2000);
       }
    }
    if (key == '*') {
    //When it picks up the "*" then it starts gathering the digits.
    // Serial.println("Got the *");
      EnterNumMsg();       
      do {
        key = keypad.getKey();
        if (key != NO_KEY){
          delay(150);
        // Serial.println(key);
          mySerial.print(key); //Prints the keypad input to the LCD for user feedback.
          DialNum[a] = key; //Puts the key info entered into an array.
          a++;
          }

        } while (key != '#');
        
      }
      if (key == '#'){
        //When the "#" is pressed, the info is sent over to the PHP script.
         delay(250);
         mySerial.print(DialNum);
         client.println(DialNum); //Dialnum array sent to PHP.
         if(finder.find("results:") == true){//PHP script evaluates number & sends back response.
             char c = client.read();
           // Serial.print("Value of the return code: ");
           // Serial.println(c);
             if (c == 'Y'){//Y back from the script means it is a valid number & success displayed.               
               NumAcceptedMsg();
               }
             if (c == 'N'){//If it is not a valid number, then N returned & error displayed.
               NumNotAcceptedMsg();
//               client.println(DialNum);
               delay(3000);
               WelcomeMsg();//Go back to Welcome message.
               }
         delay(200);
         if(finder.find("results:") == true){
             char c = client.read();
           // Serial.print("Value of the return code: ");
           // Serial.println(c);
             if (c == 'O'){//If the call was successully placed display success then go back to Welcome.
              NumCalledMsg();
              delay(4000);
              WelcomeMsg();
         }
          if (c == 'X'){//If the call couldn't be made then error message & return to welcome.
              NumNotCalledMsg();
              delay(3000);
              WelcomeMsg();
         }
        } 
   
  // Serial.println(DialNum);
  // Serial.println(a);
    a = 0;
      }
    }
  }
}

void WelcomeMsg(){
       mySerial.print(254, BYTE);
       delay(100);
       mySerial.print(1, BYTE);
       delay(100);
       mySerial.print("Welcome to the      Information service.Press the * key to  start.");
  }
    
void EnterNumMsg(){
       mySerial.print(254, BYTE);
       delay(100);
       mySerial.print(1, BYTE);
       delay(100);
       mySerial.print("Enter your 10 digit phone number        followed by #:      ");
}  

void ConnectFailMsg(){
       mySerial.print(254, BYTE);
       delay(100);
       mySerial.print(1, BYTE);
       delay(100);
       mySerial.print("Connection Failed.  Press any key to    reconnect.");
}

void NumAcceptedMsg(){
      // Serial.println("Number accepted by server");
         mySerial.print(254, BYTE);
         delay(100);
         mySerial.print(1, BYTE);
         delay(100);
         mySerial.print("Number Accepted. ");
}

void NumNotAcceptedMsg(){
        // Serial.println("Number NOT accepted by server");
           mySerial.print(254, BYTE);
         delay(100);
         mySerial.print(1, BYTE);
         delay(100);
         mySerial.print("Number not valid.");
}

void NumCalledMsg(){
            // Serial.println("Number called successfully.");
               mySerial.print(254, BYTE);
               delay(200);
               mySerial.print(1, BYTE);
               delay(200);
               mySerial.print("Number called       successfully.");
}

void NumNotCalledMsg(){
// Serial.println("Number cannot be called.");
  mySerial.print(254, BYTE);
               delay(100);
               mySerial.print(1, BYTE);
               delay(100);
               mySerial.print("Number cannot be    called.");
}


This uses the Arduino Ethernet library to set up a client application, the Keypad library to get the keypresses, NewSoftSerial to send information to the LCD and finally the TextFinder library to parse the result codes from the server. I have put all the LCD messages into functions just to clean up the code since each message needs to have a screen clear send and some delay. I’m sure I could further clean that up to just have a function that did the screen clear, but this works.

The Arduino client in term communicates with the PHP server program (dialer_twilio.php):


request("/$ApiVersion/Accounts/$AccountSid/Calls", 
     "POST", array(
     "Caller" => "XXXXXXXXXXXXXXX",
     "Called" => $dial_num,
     "Url" => "http://xxxxx.xxxxx.xxx/twi_test.php"
    
    ));
    if($response->IsError){
     echo "Error: {$response->ErrorMessage}\n";
   socket_write($spawn, "results:X\n");
   echo "Sent over X\n";
  }  else {
     echo "Started call: {$response->ResponseXml->Call->Sid}\n";  
   socket_write($spawn, "results:O\n");
   echo "Sent over O\n";
   }
} else {
  echo ("Invalid number\n");
  socket_write($spawn, "results:N\n");
  echo "Sent over N\n";
 }

   usleep(5000);
}
}while(true);

?>




Anything marked “XXXX” above is info on the account and phone numbers to be used.

This script does a quick and dirty check if the number is valid (just making sure there are no stray “x” or “#” characters and it is the right length) then uses theTwilio REST API to  set up the call. Twilio will go to the “twi_test.php” script for info on how to handle the call.

So, when Twilio goes to twi_test.php it sees:




    header("content-type: text/xml");
    echo "\n";
?>



    Hello. Welcome to the Arduino Information Service.
    Press 1 to be connected to an operator.
    Press 2 to leave a voicemail.
    Press 3 to receive an SMS message.
    Press 4 to repeat this menu.
    Press 5 to hangup.


This sets up the top level menu of the simple IVR and then directs to go to “twi_action.php” once a button is pushed. The XML such as and are the Twilio “verbs” that defines actions on the Twilio cloud system.

So here is “twi_action.php";


$stringData = $_REQUEST['Digits'];

switch($stringData){
case 5:
    echo "";
    echo " Thank you, Good bye.";
    echo "";
    echo "";
    break;
case 4:
    echo "";
    echo "";
    echo "http://xxxxx.xxxxx.xxx/twi_test.php";
    echo "";
    echo "";
    break;
 case 3:
    echo "";
        echo "You are being sent an SMS message.";
        echo "You are receiving this message from the Twilio Arduino dialer application.";
    echo "";
    break; 
case 2:
    echo "";
    echo "Leave your message after the tone and press # when you are done.";
    echo "";
    echo "";   
    break;
case 1:
    echo "";
        echo "XXXXXXX";
        echo "Goodbye";
    echo "";       
    break;
}

?>


This parses the digit pressed from the POST command and then takes action on the digits, which is either:



  1. Connect to an operator (in this case it dials through to my Skype account).
  2. Leave a voicemail. Currently this records a sound file which you can download from the Twilio website, but it could also have the sound file sent to an email address or transcribe the voice into text and have that sent along.
  3. Receive and SMS message. This sends a canned message that could be some general information to the cell phone that made the call.
  4. Repeat the menu.
  5. Hangup.
The final small script is the one that handles the voicemail recording (voicerecorder.php):



    header("content-type: text/xml");
    echo "\n";
?>

    Thanks for the message.  Here is what you recorded.
    
    Goodbye.


This one is very simple. It just plays back the message that was just recorded. That’s all there is to it! :-)

Conclusion

This project could have some practical uses as a low cost way to place a simple information service in some public place. It does, however, have the obvious limitation that there is no validation that the number entered really does belong to the user’s cell phone and not to some little old lady in Detroit, so in it’s current form it could just be a nuisance generator. This could probably be fixed with an exchange of a PIN via SMS.

What other things could this kind of project be used for? Here are some ideas:


  • Make a super secure lock by combining this project with an RFID reader lock so that once the card is swiped the user also has to enter a PIN and a code received via SMS at a cell phone that has been pre-registered with the system. This way an intruder would have to steal not only the users RFID badge, but also their phone and their PIN number.
  • This could also be adapted to trigger phone calls or SMS messages for various conditions driven by digital or analog inputs - such as temperature readings or intruder alarms. Conversely, as discussed in my Twilio LED project, a call could be used to control switches, motors or valves remotely using a telephone.
  • If it used Power Over Ethernet it would eliminate the extra power supply requirement. Even better would be using wi-fi or GSM cellular data to eliminate the need to have it wired to a router.
  • Clever use of the PROGMEM would probably allow all the response strings to be stored on the Arduino so it might be possible to dispense with using an outside webserver altogether!
This was a pretty challenging project and I hope it provides inspiration for some further cool projects!


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!