Many consumer devices (set top boxes, routers, media centers) use lightweight embedded web servers as user interface. In this walkthrough, I’ll show you how to install a complete web server in a Raspberry Pi, which is a cheap ARM-based computer that is perfect for prototyping such appliances.
In embedded systems, we aim at minimizing computational expenditure. Therefore, we’ll use Lighttpd as the web server, and SQLite as the DBMS. For server-side scripting, we’ll use PHP, a nice framework for protyping : easy to learn, and very popular.
Installing a fresh copy of Raspbian in an SD card
I’ll start from scratch, with a fresh Raspbian system. This is not strictly necessary : you can probably succeed from any working Raspbian installation.
You can start by downloading the Raspbian image, and installing it into the micro SD card. There are different instructions for Linux, for Windows, and for OS X.
For OS X, there are two sets of instructions. In my setup (MacBook Pro 13″, early 2011, Yosemite), only the second set worked, and I’ll explain it here. (If it doesn’t work for you, try the other one.)
Attention : we will be doing low-level disk transfers here. One single distraction and you may lose all your data. Your motivational PowerPoints. Those pictures of the 70th birthday of Auntie Joaquina. That spreadsheet you were almost finishing with all continuity mistakes in Bervely Hills, 90210. Gone ! Forever ! (No, seriously : backup your stuff. Really !)
(And my lawyer insists that I remind you : as always, proceed at your own risk. If you follow those instructions and your computer turns to a brick, or your cat divorces you, I’m not liable.)
First let’s unzip the disk image. Open a terminal in the folder where the image file is located, and type :
unzip 2015-05-05-raspbian-wheezy.zip
Now, let’s find out the device path to the SD card. First ensure that the SD card slot is empty, and type df -h
in the shell. You should get something like :
$ df -h Filesystem Size Used Avail Capacity iused ifree %iused Mounted on /dev/disk1 930Gi 486Gi 444Gi 53% 127405636 116484474 52% / devfs 183Ki 183Ki 0Bi 100% 636 0 100% /dev map -hosts 0Bi 0Bi 0Bi 100% 0 0 100% /net map auto_home 0Bi 0Bi 0Bi 100% 0 0 100% /home localhost:/p9Q694fzz20lCp5sv3CZyj 930Gi 930Gi 0Bi 100% 0 0 100% /Volumes/MobileBackups
Now, insert the micro SD card in the slot (you might need an adapter), count five seconds, and type df-h
again. You should get something like :
$ df -h Filesystem Size Used Avail Capacity iused ifree %iused Mounted on /dev/disk1 930Gi 486Gi 444Gi 53% 127405653 116484457 52% / devfs 185Ki 185Ki 0Bi 100% 642 0 100% /dev map -hosts 0Bi 0Bi 0Bi 100% 0 0 100% /net map auto_home 0Bi 0Bi 0Bi 100% 0 0 100% /home localhost:/p9Q694fzz20lCp5sv3CZyj 930Gi 930Gi 0Bi 100% 0 0 100% /Volumes/MobileBackups /dev/disk2s1 56Mi 20Mi 36Mi 36% 512 0 100% /Volumes/boot
Got it ? There’s a new device /dev/disk2s1
for the SD card. You might get several new lines if the card currently has more than one partition (e.g. /dev/disk2s1
, /dev/disk2s2
, etc.)
We need to unmount all those partitions, but without ejecting the device (if you eject it, you’ll have to restart the whole process) :
sudo diskutil unmount /dev/disk2s1
Repeat that command for each partition in the device.
Now for the dangerous command. We will use the low-level copy command dd
, using the disk image as input, and the SD card device as output. Take a deep breath and check each character thrice before hitting Enter
, because overwriting the wrong device will be a nightmare.
Check out the partition names above. If you got /dev/disk2s1
, you’ll write to /dev/rdisk2
. If you got /dev/disk3s2
, you’ll write to /dev/rdisk3
, etc. Got it ? Just throw away the s<number> suffix and add an r to the beginning :
sudo dd bs=1m if=2015-05-05-raspbian-wheezy.img of=<device path to write>
This might take a while — in my system, 5 minutes or so. You may type ctrl+T
to send the SIGINFO
signal, and get an status update. Also, you might need to write bs=1m
as bs=1M
, depending on the version of dd
.
This is it ! Eject the the card. Time to move to the Pi.
Configuring Raspbian
Insert the newly formatted SD card into the Pi, plug the needed devices. We will need at least : an ethernet connection to the Internet, a keyboard, and a monitor. Plug the power last.
With some luck, the Raspberry Pi Software Configuration Tool will appear during the first boot. (If it doesn’t appear, or if you are not using a fresh system, you can launch it by typing sudo raspi-config
in the command shell.)
Be sure to at least activate option 1 (“Expand Filesystem”) so Raspbian will use the entire SD card. Otherwise the upgrade commands below risk running out of space. I will also use option 2 (“Change User Password”) to make access to the system secure ; option 3 (“Enable Boot to Desktop/Scratch“) to ensure that the system boots to Console Text (no need for graphical desktop in this small web server) ; option 4 (“Internationalization Options“) to configure my keyboard to US International; and option 8 (“Advanced Options“) to set the hostname, and to ensure that SSH access is enabled.
Updating and upgrading the system
Once the system reboots, log in (the default user is pi
, and the defaul password, if you didn’t change it with option 2 above, is raspberry
).
We’ll start by bringing the system up to speed :
sudo apt-get -y update sudo apt-get -y dist-upgrade
Installing Lighttpd, SQLite, and PHP
Time to install the components of the web server. The instructions are adapted from smching’s Instructable.
sudo apt-get -y install lighttpd sudo apt-get -y install sqlite3 sudo apt-get -y install php5 php5-common php5-cgi php5-sqlite
Now for a bit of configuration. We’ll want to enable fastcgi to handle the PHP pages :
sudo lighty-enable-mod fastcgi sudo lighty-enable-mod fastcgi-php sudo service lighttpd force-reload # restart the Lighttpd service
Setting permissions
Finally, we need to set up the permissions of /var/www
(the root of the web server), and include the default user on the group that can read/write that directory.
sudo chown -R www-data:www-data /var/www sudo chmod -R 775 /var/www sudo usermod -a -G www-data pi
Time for a reboot !
sudo reboot
Testing
If you have a second computer attached to your network, you can use it to access the recently installed web server. Before leaving the Pi, type ifconfig
to get the IP. You should get something like…
$ ifconfig eth0 Link encap:Ethernet HWaddr b8:27:eb:7e:b2:3c inet addr:10.0.1.7 Bcast:10.0.1.255 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:218 errors:0 dropped:0 overruns:0 frame:0 TX packets:128 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:47677 (46.5 KiB) TX bytes:18722 (18.2 KiB) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:8 errors:0 dropped:0 overruns:0 frame:0 TX packets:8 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:1104 (1.0 KiB) TX bytes:1104 (1.0 KiB)
…showing that the IP attributed to the Pi on ethernet is 10.0.1.7.
You can type http://10.0.1.7/
on the browser of another computer to check the web server. You should see the placeholder page for Lighttpd showing that the web server is correctly installed.
Also — if you haven’t disabled SSH — you can type ssh pi@10.0.1.7
to access the Pi remotely. This is useful if you have a single set of monitor/keyboard/mouse.
If the Pi is your only computer, you can start the graphical desktop with the command startx
, and then navigate to the newly installed web server. Typing the IP address of the machine, as before, will work, but so does using the loopback interface : http://127.0.0.1/
or http://localhost/
.
Next, let’s test if PHP is working. Paste this simple test page…
<?PHP phpinfo() ?>
…into /var/www/test.php
.
Then, type in your browser http://10.0.1.7/test.php
(change 10.0.1.7 for the actual IP of your Pi, or use http://localhost/test.php
if browsing from inside the Pi). You should see the PHP info page.
Finally, let's run a small test on PHP + SQLite. I adapted this code from Veit Osiander's post on Scandio. Paste the following...
<?php try { // Create file "scandio_test.db" as database $db = new PDO('sqlite:scandio_test.db'); // Throw exceptions on error $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $sql = <<<SQL CREATE TABLE IF NOT EXISTS posts ( id INTEGER PRIMARY KEY, message TEXT, created_at INTEGER ) SQL; $db->exec($sql); $data = array( 'Test '.rand(0, 10), 'Data: '.uniqid(), 'Date: '.date('d.m.Y H:i:s') ); $sql = <<<SQL INSERT INTO posts (message, created_at) VALUES (:message, :created_at) SQL; $stmt = $db->prepare($sql); foreach ($data as $message) { $stmt->bindParam(':message', $message, SQLITE3_TEXT); $stmt->bindParam(':created_at', time()); $stmt->execute(); } $result = $db->query('SELECT * FROM posts'); foreach($result as $row) { list($id, $message, $createdAt) = $row; $output = "Id: $id<br/>\n"; $output .= "Message: $message<br/>\n"; $output .= "Created at: ".date('d.m.Y H:i:s', $createdAt)."<br/>\n"; echo $output; } $db->exec("DROP TABLE posts"); } catch(PDOException $e) { echo $e->getMessage(); echo $e->getTraceAsString(); } ?>
...into /var/www/testdb.php
.
Navigate to http://10.0.1.7/testdb.php
(or http://localhost/testdb.php
), and if everything goes well, you should get something like :
Id: 1 Message: Test 1 Created at: 30.09.2015 05:34:33 Id: 2 Message: Data: 560b746950a70 Created at: 30.09.2015 05:34:33 Id: 3 Message: Date: 30.09.2015 05:34:33 Created at: 30.09.2015 05:34:33
If you get an ugly error, like "General error: 14 unable to open database file", double check the permissions of /var/www
(and everything inside it). The user www-data
must have reading and writing permission on everything (see the section "Setting permissions" above).
Otherwise, you are ready to start working on your project !