A BIFFerent kind of e-mail notifier
The most important canine in the history of UNIX is called
biff, and its name
was used to name the program which notifies users of incoming
mail. The biff program
notifies the user's terminal when it is told to do so by the
comsat server
process after receiving a report of incoming mail.

With the advent of Web-based and desktop e-mail clients, the popularity of
biff has diminished as these systems usually have some other way of notifying
a user of new mail. Even so, as a first little Arduino project, I've
created an alternative biff-like system in the form of a US letter
box. (Some more
examples).
I want to keep this simple, concentrating on trying to understand what
the Arduino is capable of, and looking into doing fun things with it.
Even though I have an Ethernet shield for my Arduino, I'm using a
serial connection here, which many will prefer, as it lowers the cost
of the total setup. Speaking of which, here is what I used:
- Arduino Duemillanove, but any other board will suffice.
- An inexpensive 5V servo motor.
- Four (4) LEDs, any colour.
- Piezzo buzzer.
- Half a bar of white chocolate.
Depending on what type of e-mail setup you have, there are a number of
ways how to determine you have
new messages (i.e. unread e-mail) in your mail box. You'd have to modify this portion if
you want, for example, to find out how many unread messages you have
in Apple's Mail.app, Outlook, or whichever.
On UNIX these two are typical:
-
With a trivial comsat-type daemon you run on UNIX/Linux. This
UDP-based service is sent a UDP datagram by a mail delivery agent
as soon as your (and anybody else's) mailbox on the system gets a
message deposited on it. Note, that you'll be able to use this only
if you don't already use a system-wide comsat service. There are
several programs out there which inform comsat of new mail: one
of these, is the versatile procmail delivery agent. -
I'll be discussing a small Perl program which
queries your IMAP account to determine the number of unread
messages waiting for you.
If you've been following closely, you'll have noticed there is a
fundamental difference between these two methods. Using comsat, we
are informed that one (1) new message has arrived each time it
arrives, whereas the second, gives us a total count of unread messages
each time we query the server. Keep this in mind for now, and we'll
see later, how I handle this. (I'll be using a "protocol" to tell the
Arduino whether to increment or to set the count of unread messages.)
This is what the finished "product" looks like:

The biff machine consists of the parts stuffed into a box. My
young "art designer" was responsible for the colours.
The sketch running on the Arduino handles the serial communication,
reading a line of text (using the Arduino String
library) from the serial connection, incrementing or
setting the message counter depending on whether the input line starts
with a "+" or "=" respectively. If the unread count
is non-zero, the letterbox "arm" is raised, and the unread count is
displayed in binary on the four LEDs. (You can use more if you like –
simply change the definition of BITS to match.) Note that I
force the unread count to the maximum value allowed by BITS
to ensure that all LEDs are lit, because an overflow of bits could
indicate all LEDs off, even though unread count is set.
* by Jan-Piet Mens
* April 2009
*/
#define SPEAKERPIN 7
#define SERVOPIN 9
#define NBITS 4
/* Bit positions 3 2 1 0 */
int ledPins[NBITS] = { 10, 11, 12, 13};
#include <WString.h>
#include <Servo.h>
Servo myservo; // create servo object to control a servo
String cmd = String(60); // external command to this sketch (i.e. what must we do?)
unsigned int nmails = 0; // number of unread e-mail messages
boolean flagged = false; // true if has been flagged
void setup()
{
int n;
pinMode(SPEAKERPIN, OUTPUT);
digitalWrite(SPEAKERPIN, LOW);
for (n =0; n < 50; n++) {
digitalWrite(SPEAKERPIN, HIGH);
delayMicroseconds(200);
}
Serial.begin(9600); // connect to the serial port
myservo.attach(SERVOPIN); // attaches the servo to the servo object
mailflag(false); // bring down the flag
}
void loop()
{
if (Serial.available() > 0) {
serialEvent();
}
}
void serialEvent()
{
char ch = Serial.read(); // get latest character
if ((ch != '\\n') && (ch != '\\r') && (ch != 'E')) {
cmd.append(ch); // fill cmd line
}
else {
process();
}
}
void process()
{
if (cmd.startsWith("+")) {
nmails += atoi(cmd + 1); // increment
}
else if (cmd.startsWith("=")) {
nmails = atoi(cmd + 1); // set
}
Serial.print("nmails = ");
Serial.println(nmails);
if (nmails >= (2 << (NBITS - 1)) ) { // sanitize value
nmails = 2 << (NBITS - 1) | 0xFFFF;
}
if (flagged == false && nmails > 0) {
flagged = true;
mailflag(true);
melody();
}
if (nmails == 0) {
if (flagged == true) {
mailflag(false);
flagged = false;
}
}
showbits(nmails);
cmd = ""; // reset command
}
void showbits(int val)
{
int n, bit, leds[NBITS];
Serial.print("val = ");
Serial.print(val);
Serial.print(" ");
for (n = (NBITS - 1); n >= 0; n--) {
bit = val & 0x01;
val >>= 1;
leds[n] = bit;
}
for (n = 0; n < NBITS; n++) {
Serial.print(leds[n]);
digitalWrite(ledPins[n], (leds[n]) ? LOW : HIGH);
}
Serial.println();
}
void mailflag(boolean up)
{
int pos;
if (up) {
for(pos = 0; pos < 90; pos += 2) {
myservo.write(pos);
delay(10);
}
}
else {
for(pos = 90; pos>=1; pos -= 2) {
myservo.write(pos);
delay(10);
}
}
}
/* swiped from: Melody (cleft) 2005 D. Cuartielles for K3 */
int length = 2; // the number of notes
char notes[] = "gc"; // a space represents a rest
int beats[] = { 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 4 };
int tempo = 300;
void playTone(int tone, int duration) {
for (long i = 0; i < duration * 1000L; i += tone * 2) {
digitalWrite(SPEAKERPIN, HIGH);
delayMicroseconds(tone);
digitalWrite(SPEAKERPIN, LOW);
delayMicroseconds(tone);
}
}
void playNote(char note, int duration) {
char names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C' };
int tones[] = { 1915, 1700, 1519, 1432, 1275, 1136, 1014, 956 };
// play the tone corresponding to the note name
for (int i = 0; i < 8; i++) {
if (names[i] == note) {
playTone(tones[i], duration);
}
}
}
void melody() {
for (int i = 0; i < length; i++) {
if (notes[i] == ' ') {
delay(beats[i] * tempo); // rest
} else {
playNote(notes[i], beats[i] * tempo);
}
// pause between notes
delay(tempo / 2);
}
}
Download this code: arduino/biff/biff.pde
I tested the sketch with the serial monitor in the IDE, sending it
either a +1E (one new message) or something like =5E
(a total of five unread messages).

When the Arduino modifies the unread count, it emits a beep on the
piezo buzzer; you can make it play a nicer
sound, however, if you
desire.
The Perl program below connects to the serial proxy on the machine to which my Arduino is connected via USB. The serial proxy translates a TCP connection to a port you specify in serproxy.cfg to a particular serial interface.
use strict;
use IO::Socket;
use Mail::IMAPClient;
my $serproxy_host = "192.168.1.77";
my $imap_host = '<your-imap-server>';
my $user = '<you>';
my $password = '<secret>';
my $folder = 'INBOX';
my $imap = new Mail::IMAPClient(
'Server' => $imap_host ,
'User' => $user ,
'Authmechanism' => 'LOGIN',
'Password' => $password ) or die "Unable to connect to imap server\\n";
my $socket = IO::Socket::INET->new(PeerAddr => $serproxy_host,
PeerPort => 5331,
Proto => "tcp",
Type => SOCK_STREAM)
or die "Couldn't connect to $serproxy_host:5331 : $@\\n";
while (1) {
$imap->examine($folder) or die "Could not examine: $@\\n";
my @unread = $imap->unseen;
print "Unread messages: " . ($#unread + 1) . "\\n";
print $socket "=" . ($#unread + 1) . "E";
sleep(20);
}
close($socket);
$imap->disconnect() or die "Unable to disconnect\\n";
Download this code: arduino/biff/imaphandler.pl
The wiring on the breadboard isn't difficult, although mine is
looks very messy. (Remeber, it is my first time…)

Here is the board layout (with one LED only), done with Fritzing (a nice program — if only I could lay the wires out nicely).
You'll notice I'm
using the marvellous 1 cent Arduino
undershield
(Open Source too!
) to fasten the microcontroller to the breadboard.
As far as the "finished" product is concerned, ahem, here are some pictures: first the inside of the box:

and then the "biff machine" with a raised flag and showing eleven unread messages:

What I've learnt, you ask? Well, quite a bit:
- Arduino is a huge bit of fun.
- I need a new pair of eye glasses so that I can correctly identify the teeny tiny little bits and pieces. I'm not kidding.
- My "enclosure" sucks, and it is a very far cry from professionalism.


Hallo!

Ich bin so frei und antworte mal deutsch…
Zum einen möchte ich mal danke sagen für Deine interessanten Informationen (die mich u.a. zum SparkFun Protoshield geführt haben) – und als "Gegenleistung" verrate ich Dir, dass man bei Fritzing die Shift-Taste festhalten kann, um Kabel "ordentlich" zu verlegen
Gruß vom linken Niederrhein
Danke, Markus: guter Tipp. Jetzt hoffen wir mal, dass Fritzing irgendwann mal "erwachsen" wird.
Ja, das wäre klasse. Aber, wenn ich mal ehrlich bin: warum soll ich mir für Fritzing etwas wünschen – was ich selber nach weit über 30 Jahren noch nicht geschafft habe?!
Ob es jemals wieder ein S65-Shield geben wird?
Ich denke es wird wieder S65 Shields geben: ich sah am 20.07.2009: Neue Revision des S65-Shields.
Das wäre toll. Hatte es schon gefunden, bevor ich auf Deine Seite gestossen bin – aber erst hier wurde ich "heiß" drauf