This, Jen, is the Internet
Last updated: Nov. 2, 2013, 2:22 p.m.
For OggCamp this year I had the idea of taking along a replica of The Internet from the British TV series The IT Crowd. If you haven't seen the episode, it revolves around a clueless IT manager being selected as the employee of the month. As employee of the month she is expected to make a presentation to the shareholders about her area of expertise. Keen to show her up, the two support guys manage to convince her that a small black box with a red flashing light is actually the Internet.
The box has become a favourite amongst fans of the program with dozens of versions of it having been built. It even made a brief appearance in the final episode of the series broadcast earlier this year.
WiFi access point
To make the black box a bit more interesting I decided to build it around a Raspberry Pi and a WiFi adapter so that it could act as a wireless hot spot. To power the unit for an extended period I needed a decent power supply, a small WiFi adapter and suitable code to serve up something when people connected and to blink the red LED on the top of the box.
The power supply solution is based on my camping phone charger which uses an old Asus EeePC 701 battery and a regulator to provide a USB power supply. I added an external switch to the end of the box to turn off the power connection between the battery and the regulator. This was important because there was no way to charge the battery without getting out a screwdriver and dismantling the box.
The WiFi adapter I used was an Edimax Nano USB Adapter which seems to be a really popular choice with the Raspberry Pi and was really easy to fit in the case.
I used the latest version of Raspbian from the Raspberry Pi website, I went for the latest raw image as that's the way I've always used my Pi and I was working from a Linux laptop so imaging an SD card is easy. Once installed I chose the options to expand the file system, start the SSH server automatically and boot to console since I had no need for a graphical interface in this project.
Once the hardware was up and running I installed:
- Apache and PHP to serve some content
- dnsmasq to make all domain names point to the local IP address
- udhcpd as a DHCP server
- hostapd (installed from source) to make a wireless access point
- RPi.GPIO python library to flash the LED
So the basic setup was hostapd running as a wireless access point, udhcpd providing devices that connected with an IP address and providing the Raspberry Pi's own IP address as the DNS server, dnsmasq running as the DNS server with a catch-all that directs all domains to the local IP address. Once a request was made to the local server it served the same content for all URLs (using a custom 404 that points to index.php) which presented viewers with a screen-shot from the IT crowd episode and a random quote from the same episode. In the background I had a process running which toggled the LED on the top of the box once a second.
To get the access-point working I followed the procedure I found on the Raspberry Pi forums, building the source from Realtek which was really easy to do, just download the source file and make sure you have run sudo apt-get install build-essential
then it's just a case of typing make
and waiting a while for it to finish and then sudo make install
. Like it says in the forum post I installed the hostapd package from the repository first, then used sudo apt-get remove
to un-install it again. This leaves the startup file in place if you copy the binaries as it says. I have found that there is a problem with USB wi-fi adapters and hostapd when I did something similar with the Beaglebone, if hostapd starts before the interface is brought up by the kernel the access point will be active (you can see the network when you browse wireless networks from another device) but the WiFi adapter has no IP address. This means the DHCP and DNS servers won't work. I added sleep 15
to the hostapd startup script to give the interface time to come up and this seems to have made startup rock solid.
I used udhcpd as the DHCP server as that is what I used in the Beaglebone project, I used the same config file but with a full 256 address range for clients. I also added a line to indicate that the Raspberry Pi should be used as DNS server. I could have just used dnsmasq as that includes both DHCP and DNS but by the time I got as far as DNS I'd already setup the DHCP and didn't bother to change it.
I really wanted to have the device intuitive, the access point was just called "the internet" and I hoped that if anyone came past and saw the box sitting on the table they'd be able to find the access point and view the amusing quotes. I realised to get that to work the box would have to redirect requests for other pages so that no matter what address you entered you'd end up on the landing page. To do this I needed to set up a DNS server that replied with the IP address of the access point to any domain name lookup. This only took a one line config and the dnsmasq package from the Raspbian repositories.
The easiest bit was serving up some content, all I did was install apache2 and php5, replaced the default index.html in /var/www/
with a custom index.php and put the screen shot I wanted in that folder too. I added a line to the default config file to make the 404 page point to index.php so if people tried to connect to somesite.com/foo the DNS wildcard would direct them to the local server but would still request /foo which didn't exist. I just added the line:
ErrorDocument 404 /index.php
under the ErrorLog line in /etc/apache2/sites-available/default
.
Finally I stole the daemon code and startup procedure from my server monitor project and replaced the run() method with a simple function that sets GPIO 7 to output and then toggles it once a second. To control the GPIO pins on the Pi I followed the instructions on openmicros.org. It took me a while to figure out why I couldn't use the GPIO pin I thought I was but it turns out that on the early run of Raspberry Pis the pins were numbered differently, and this one had been sat on a shelf for a long time. Since the daemon is running in the background and is started as root there are no privilege issues with accessing the GPIO.
Config Files
Probably the most tricky bit about setting up something like this is getting all the config settings right so I'm including as many of the files as I can here as examples of how to get something working.
/etc/udhcpd.conf
# The start and end of the IP lease block
start 192.168.10.20 #default: 192.168.0.20
end 192.168.10.254 #default: 192.168.0.254
# The interface that udhcpd will use
interface wlan0 #default: eth0
option subnet 255.255.255.0
option dns 192.168.10.1
Notes:
This configures the DHCP server, make sure that you select your WiFi adapter and use a different subnet (here its 192.168.10.x) from your main network, then you can still plug the Raspberry Pi into your LAN with a cable to download software or troubleshoot via SSH.
/etc/hostapd/hostapd.conf
#change wlan0 to your wireless device
interface=wlan0
driver=rtl871xdrv
ssid=the-internet
channel=1
/etc/network/interfaces
auto lo
iface lo inet loopback
iface eth0 inet dhcp
auto wlan0
iface wlan0 inet static
address 192.168.10.1
netmask 255.255.255.0
Notes:
You need to make sure that the WiFi adapter is set up with a static IP address as it will be running the DHCP server and can't assign itself an address. Obviously it needs to be in the same subnet as the DHCP range (shown above).
/etc/init.d/hostapd
#!/bin/sh
### BEGIN INIT INFO
# Provides: hostapd
# Required-Start: $remote_fs
# Required-Stop: $remote_fs
# Should-Start: $network
# Should-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Advanced IEEE 802.11 management daemon
# Description: Userspace IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP
# Authenticator
### END INIT INFO
PATH=/sbin:/bin:/usr/sbin:/usr/bin
DAEMON_SBIN=/usr/sbin/hostapd
DAEMON_DEFS=/etc/default/hostapd
DAEMON_CONF=/etc/hostapd/hostapd.conf
NAME=hostapd
DESC="advanced IEEE 802.11 management"
PIDFILE=/var/run/hostapd.pid
[ -x "$DAEMON_SBIN" ] || exit 0
[ -s "$DAEMON_DEFS" ] && . /etc/default/hostapd
[ -n "$DAEMON_CONF" ] || exit 0
DAEMON_OPTS="-B -P $PIDFILE $DAEMON_OPTS $DAEMON_CONF"
. /lib/lsb/init-functions
case "$1" in
start)
sleep 15
log_daemon_msg "Starting $DESC" "$NAME"
start-stop-daemon --start --oknodo --quiet --exec "$DAEMON_SBIN" \
--pidfile "$PIDFILE" -- $DAEMON_OPTS >/dev/null
log_end_msg "$?"
;;
stop)
log_daemon_msg "Stopping $DESC" "$NAME"
start-stop-daemon --stop --oknodo --quiet --exec "$DAEMON_SBIN" \
--pidfile "$PIDFILE"
log_end_msg "$?"
;;
reload)
log_daemon_msg "Reloading $DESC" "$NAME"
start-stop-daemon --stop --signal HUP --exec "$DAEMON_SBIN" \
--pidfile "$PIDFILE"
log_end_msg "$?"
;;
restart|force-reload)
$0 stop
sleep 8
$0 start
;;
status)
status_of_proc "$DAEMON_SBIN" "$NAME"
exit $?
;;
*)
N=/etc/init.d/$NAME
echo "Usage: $N {start|stop|restart|force-reload|reload|status}" >&2
exit 1
;;
esac
exit 0
Notes:
The only bits changed here are the config file and I added the line sleep 15 as discussed above just under "start)"
/etc/dnsmasq.conf
address=/#/192.168.10.1
Notes:
That's it, the whole thing in one line. The # matches any sequence of characters as domain name and directs them to the local IP address as set in interfaces above.
blinker
#!/usr/bin/env python import time from daemon import Daemon import RPi.GPIO as GPIO import sys class MyDaemon(Daemon): def run(self): GPIO.setup(7,GPIO.OUT) while True: GPIO.output(7,False) time.sleep(1) GPIO.output(7,True) time.sleep(1) if __name__ == "__main__": daemon = MyDaemon('/tmp/monitor_daemon.pid') daemon.test = False if len(sys.argv) == 2: if 'start' == sys.argv[1]: daemon.start() elif 'stop' == sys.argv[1]: daemon.stop() elif 'restart' == sys.argv[1]: daemon.restart() elif 'test' == sys.argv[1]: daemon.test = True daemon.run() else: print "Unknown command" sys.exit(2) sys.exit(0) else: print "usage: %s start|stop|restart" % sys.argv[0] sys.exit(2)
Notes
This is the daemon that runs to keep the LED flashing in the background. It relies on the same daemon.py that I've used in my server monitor which you can download from that article. To get this to run, I created a symbolic link to blinker in /etc/init.d and then added /etc/init.d/blinker start
to a new line in /etc/rc.local.
Comments
Posting comments is not currently possible. If you want to discuss this article you can reach me on twitter or via email.