First impressions of the Arduino/Intel-hybrid Galileo board - an x86 Arduino! - a small example project, and our likes and dislikes.
The list of official Arduino boards continues to grow, and, continuing with the famous-Italian-person trend, now includes the Galileo. The Galileo is Intel's toe-dip into the Arduino waters. It features their Quark SoC X1000 -- a 32-bit, x86 system-on-a-chip that can run at up to 400MHz with 512 kB of built-in SRAM.
The Galileo board supports the Quark with a wide range of external peripherals. There's 8MB Flash (to store firmware), 11kB EEPROM, a µSD socket (supports up to 32GB), 10/100Mb Ethernet, USB 2.0 host and device ports, an RS-232 port, and a mini PCI Express (mPCIE) socket. On top of all that, it's got that same, familiar Arduino pinout we all love (to hate?). The Arduino pins -- including six analog inputs, SPI, I2C, UART, and PWM outputs -- are all exactly where an Arduino user would expect them to be.
What the Galileo tries to do is meld the ease of Arduino's hardware manipulation with the power of a Linux operating system. Most sketches written for Arduino Unos, Leonardos, and other boards can be ported directly over to the Galileo. You can still use popular Arduino libraries like SD, Ethernet, WiFi, EEPROM, SPI, and Wire, but you can also make requests of the Linux kernel with system()
calls. This gives your Arduino sketch access to powerful utilities like Python, Node.js, OpenCV, and all sorts of fun Linux-y stuff.
We were fortunate enough to receive a couple of early Galileo boards for evaluation, one of which landed in my hands to futz around with. To get a feel for the board, I tried to whip together a quick project that melded a few of the Galileo's unique features together.
Most of our work communication occurs in emails, but I'm often turned away from my computer, wielding a soldering wand or banging my head against my Pi workstation as those emails pile up. It's imperative that I stay up-to-date with the latest SparkFun memes and cool robot videos, so a simple unread-email-counter would be great to have permanently installed over my workbench. Sounds like a perfect project for the Galileo!
First, I needed a utility to check how many unread emails I have. Well, I don't need much of an excuse to whip out some Python, so I did a quick search and found the script below. It logs into our email server with my credentials, checks how many unread emails there are in the inbox, and prints that value.
# pyMailCheck.py - Logs into your gmail and prints the number of unread emails.
# Place this file in the top level of your Galileo's SD card.
import imaplib # Used to connect to an IMAP4 server.
obj = imaplib.IMAP4_SSL('imap.gmail.com', '993') # Connect to an IMAP4 sever over SSL, port 993
obj.login('my_email_address@gmail.com','myPassword') # Identify the client user and password
obj.select() # Select a the 'INBOX' mailbox (default parameter)
# Search mailbox no (None) charset, criterion:"UnSeen". Will return a tuple, grab the second part,
# split each string into a list, and return the length of that list:
print len(obj.search(None,'UnSeen')[1][0].split())
I loaded that onto a µSD card (also including the Galileo's SD-bootable "bigger" Linux image, required for Python), and proceeded to the next task.
To display the email count (and retain the Arduino-y-ness of the Galileo) I used an OpenSegment Shield, controlled over SPI. No modifications, just plop it right on top of the Galileo. A lot of the Arduino code below might look familiar, especially if you've used the SPI, SD, or WiFi libraries, which all work swimmingly with the Galileo.
/* Galileo Email Checker
by: Jim Lindblom
SparkFun Electronics
date: January 7, 2013
license: Beerware. Please use, reuse, and modify this code.
If you find it useful, you can buy me a beer when we meet.
This code connects a Galileo to WiFi. Then runs a Python script
- https://gist.github.com/jimblom/8292444#file-pymailcheck-py -
that lives on the Galileo's SD card to check how many unread
emails you have. That number is displayed on an OpenSegment
Shield - https://www.sparkfun.com/products/11846 - which is
controlled over SPI.
*/
#include <SPI.h> // SPI is used to control the OpenSegment Shield
#include <WiFi.h> // WiFi (could be swapped out for Ethernet)
#include <SD.h> // The SD library is used to read a temporary file,
// where the py script stores an unread email count.
//////////////////////
// WiFi Definitions //
//////////////////////
char ssid[] = "WiFiSSID"; // your network SSID (name)
char pass[] = "WiFiPassword"; // your network password (use for WPA, or use as key for WEP)
IPAddress ip;
int status = WL_IDLE_STATUS;
/////////////////////
// Pin Definitions //
/////////////////////
const int SS_PIN = 10; // SPI slave select pin (10 on the shield)
const int STAT_LED = 13; // The Galileo's status LED on pin 13
////////////////////////////////
// Email-Checking Global Vars //
////////////////////////////////
const int emailUpdateRate = 10000; // Update rate in ms (10 s)
long emailLastMillis = 0; // Stores our last email check time
int emailCount = 0; // Stores the last email count
// setup() initializes the OpenSegment, the SD card, and WiFi
// If it exits successfully, the status LED will turn on.
void setup()
{
pinMode(STAT_LED, OUTPUT);
digitalWrite(STAT_LED, LOW);
Serial.begin(9600); // Serial monitor is used for debug
initDisplay(); // starts up SPI and resets the OS Shield
initSDCard(); // Initializes the SD class
// Initialize WiFI. On success turn on the stat LED. On fail,
// print an error on the LED, and go into an infinite loop.
if (initWiFi() == 1) // If WiFi connects, turn on Stat LED
digitalWrite(STAT_LED, HIGH);
else
{ // If WiFi connect fails, print error, inifinite loop
SPIWriteString("----", 4);
while (1)
;
}
}
// loop() checks for the unread email count every emailUpdateRate
// milliseconds. If the count has changed, update the display.
void loop()
{
// Only check email if emailUpdateRate ms have passed
if (millis() > emailLastMillis + emailUpdateRate)
{
emailLastMillis = millis(); // update emailLastMillis
// Get unread email count, and store into temporary variable
int tempCount = getEmailCount();
if (tempCount != emailCount) // If it's a new value, update
{ // Do this to prevent blinking the same #
emailCount = tempCount; // update emailCount variable
printEmailCount(emailCount); // print the unread count
}
}
// Bit of protection in case millis overruns:
if (millis() < emailLastMillis)
emailLastMillis = 0;
}
// printEmailCount(emails) prints the given number to both the
// serial monitor, and the OpenSegment Shield.
void printEmailCount(int emails)
{
Serial.print("You have ");
Serial.print(emails);
Serial.println(" unread mail.");
SPIWriteByte('v'); // Clear display
for (int i=3; i>= 0; i--)
{
SPIWriteByte((int) emails / pow(10, i));
emails = emails - ((int)(emails / pow(10, i)) * pow(10, i));
}
}
// getEmailCount runs the pyMail.py script, and reads the output.
// It'll return the value printed by the pyMail.py script.
int getEmailCount()
{
int digits = 0;
int temp[10];
int emailCnt = 0;
// Send a system call to run our python script and route the
// output of the script to a file.
system("python /media/realroot/pyMail.py > /media/realroot/emails");
File emailsFile = SD.open("emails"); // open emails for reading
// read from emails until we hit the end or a newline
while ((emailsFile.peek() != '\n') && (emailsFile.available()))
temp[digits++] = emailsFile.read() - 48; // Convert from ASCII to a number
emailsFile.close(); // close the emails file
system("rm /media/realroot/emails"); // remove the emails file
// Turn the inidividual digits into a single integer:
for (int i=0; i<digits; i++)
emailCnt += temp[i] * pow(10, digits - 1 - i);
return emailCnt;
}
// initDisplay() starts SPI, and clears the display to "0000"
void initDisplay()
{
initSPI();
SPIWriteByte('v');
SPIWriteString("0000", 4);
}
// initSPI() begins the SPI class, and sets up our SS_PIN output
void initSPI()
{
pinMode(SS_PIN, OUTPUT);
digitalWrite(SS_PIN, HIGH);
SPI.begin();
SPI.setBitOrder(MSBFIRST);
SPI.setClockDivider(SPI_CLOCK_DIV64);
SPI.setDataMode(SPI_MODE0);
}
// initSDCard() begins the SD class
void initSDCard()
{
SD.begin();
}
// initWiFi() initializes WiFi, connecting to the ssid/passkey
// combo defined up above.
// This function will attempt 3 connections to WiFi. If we
// succeed a 1 is returned, and the IP address is printed.
// If connections fail, a 0 is returned.
uint8_t initWiFi()
{
byte timeout = 0;
while ((status != WL_CONNECTED) && (timeout < 3))
{
Serial.print("Setting up WiFi : attempt ");
Serial.println(timeout + 1);
// Connect to WPA/WPA2 network. Change this line if using
// open or WEP network:
status = WiFi.begin(ssid, pass);
//delay(10000); // Wait 10 seconds between connect attempts
timeout++;
}
if (timeout >= 3)
return 0;
else
{
Serial.print("Connected to ");
ip = WiFi.localIP();
Serial.println(WiFi.SSID());
Serial.println(ip);
// !!! Idea here: print IP address to seven segment display.
// That'll make it easier to know which address to ssh over to
// if we need.
return 1;
}
}
// SPIWriteString will write an array of chars (str) of length
// len out to SPI. Write order is [0], [1], ..., [len-1].
void SPIWriteString(char * str, uint8_t len)
{
digitalWrite(SS_PIN, LOW);
for (int i=0; i<len; i++)
{
SPI.transfer(str[i]);
}
digitalWrite(SS_PIN, HIGH);
}
// SPIWriteByte will write a single byte out of SPI.
void SPIWriteByte(uint8_t b)
{
digitalWrite(SS_PIN, LOW);
SPI.transfer(b);
digitalWrite(SS_PIN, HIGH);
}
(Check out the GitHub Gist for a nice, syntax-highlighted copy of this code.)
One of the key, uniquely-Galilean functions this code incorporates is the system()
function, which can issue requests to the Linux kernel. With the below line of code, we can run a Python script from the comfy confines of Arduino:
system("python /media/realroot/pyMail.py > /media/realroot/emails");
The output of the script is routed to a file named "emails." Then we can use the Arduino SD library to read the "emails" file, which simply contains the number of unread emails there are. After a bit of parsing, and SPI-ing, we've got an unread email count printed to the OpenSegment Shield!
(Now if only I could program it to answer those emails...)
After toying around with the board for a few days, here are a few of my thoughts on the pros and cons of the Galileo:
apt-get
makes for a sad Linux-newbie). I definitely recommend throwing an SD card in there with the "big" Linux image, which adds WiFi support as well as the fun stuff like Python, SSH, node.js, ALSA, V4L2, and openCV.The Galileo is a unique entry into the growing Arduino board catalog. I'm sure there are plenty of projects out there that can make use of the unique blend of Arduino and Linux. Is the Galileo ever going to unseat the Raspberry Pi? Probably not. But they both serve their own, unique purpose. The Pi, with its HDMI output, gives you a complete, mini-computer, while the Galileo is more of a hardware-focused platform, with a smaller Linux backbone (and Arduino shield compatibility!).
Have you had a chance to play with the Galileo yet? What are your thoughts? Looking to pick one up? It looks like availability is beginning to increase; I hope to see the Galileo community continue to grow, and seed more projects that can make use of the board.