Delving the depths of computing,
hoping not to get eaten by a wumpus
2018-03-28
The Perl Raspberry Pi ebook campaign is going great so far. As promised, here’s another sample chapter, where we cover the lineup of Raspberry Pi models, as well as some tools you’ll want/need.
Let’s Get Down to Basics
The Raspberry Pi has expanded into a range of devices, which may seem overwhelming at first. There’s also the matter of which tools you’ll need once you do end up requiring one.
Raspberry Pi Models
Model A and B(+)
The initial offerings were the Pi Model A and B in 2012. These were both based on the Broadcom BCM2835 System on a Chip. The Model A had 256MB of RAM for $25, while the Model B had 512MB for $35.
These models also standardized the layout for most future Pi’s, including the location of the pin header and HDMI port. These models also include a composite video connector.
In 2014, the A+ and B+ models were released, with prices reduced to $20 and $25, respectively. They expanded the pin header, which has been used on other Pi releases ever since. The separate composite video connector was also removed, with its functionality still accessible through a 3.5mm connector. The A+ also shrunk its form factor.
Compute Module
Released in 2014, the Compute Module was designed for industrial use. Instead of external ports, it uses the same connector as DDR2 SODIMM RAM (but don’t try plugging it into your laptop). These require a special development kit for programming.
Zero and Zero W
The Raspberry Pi Foundation made headlines in 2015 when they included an entire computer bundled with a magazine. The Pi Zero had an MSRP of $5, or free if you bought the December issue of the MagPi magazine. It introduced a smaller form factor that’s not much bigger than a stick of gum, and used the same BCM2835 used in the original Model A and B. In 2017, the Pi Zero W was released for $10 that included onboard WiFi and Bluetooth, just like the B3/B3+ (see below).
B2 and B3
In early 2015, the Pi B2 was introduced with a new BCM2836 SoC. This upgraded the CPU from single to quad core, and from 700MHz to 900MHz. It also bumped the memory to 1GB of RAM. This would be followed in 2016 by the B3, which has a BMC2837, bringing the clockrate to 1.2GHz. It also gave you built-in WiFi and Bluetooth, which means you no longer needed a separate dongle to connect your Pi to the Internet.
Both the B2 and B3 had an MSRP of $35.
B3+
While we wait for the next big iteration, the B3+ was released. It pushed the clockrate further to 1.4GHz, and now includes dual-band 802.11ac WiFi. It’s also the first Pi to have gigabit Ethernet. Following tradition, it’s priced at $35.
The Future?
The Raspberry Pi Foundation isn’t sitting still with the B3+ at the top of the model lineup, and are hard at work on the next major revision. Hoped-for improvements are support for USB3, a faster CPU, and breaking the 1GB RAM limit. We’ll have to wait and see.
Which Model Should You Buy?
The hardware projects in this book can be easily done on any of the more recent models–Zero (W), Pi2, or Pi3. The Compute Module requires an extra development kit, which you will probably want to avoid until you have a specific need and experience working with the more mainstream boards.
If you need to fit the project in a small space, run off of low power, or want the cheapest option available, the Zero or Zero W are your best options. If you want speed and don’t need to worry about power usage, the Pi3 is the best option. If you find a Pi2 for cheap, that’s a perfectly good option, as well.
The Tools You Need
Power Supply
When the Raspberry Pi was first released, many people saw that it used a micro USB power port, and tried to use their leftover cellphone chargers to power it.
This did not work.
Cellphone chargers are meant for charging cellphone batteries. Under load, their voltage may sag below the level needed to keep the Pi running. A charger rated for 500mA may not be enough when you add a WiFi dongle, a monitor, a keyboard, and a mouse. It might boot up, but you’ll likely run into strange problems with no obvious fix due to undervoltage/undercurrent.
The CanaKit micro USB power adapter is the standard way to power the Pi. It’s rated for 2500mA, which is plenty to run even the Pi3.
WiFi Dongle
Most of the Pi lineup doesn’t have built-in WiFi. If you buy the Pi3 or the Zero W, you won’t need one. If you can use wired ethernet and have a Pi with a wired port (not the A(+), Zero (W), or Compute Module), then you can also skip a WiFi dongle.
Otherwise, there are a number of cheap USB dongles that will work with the Pi. Edimax sells a small nub adapter. If you need more signal, the EDUP adapter has an external antenna, and works well with the Pi.
SD Card
The main storage system on the Pi is an SD card (the Pi3 requires a micro SD card). If you take a quick look at SD card speed classes, you may be tempted to buy the first Class 10 or U3 card you can find.
Before you do that, note that the speed classes on SD cards refers to the sequential writing speed. This is good if you were putting your SD card in a camera, since streaming 60 frames of 4k video every second takes a lot of sequential writing. Running an operating system off that SD card does a lot of random access, not sequential. In practice, you’ll often find that a quality Class 4 card works better than a cheap Class 10.
Recently, the A1 and A2 performance classes were released. These specify a minimum number of random IO operations per second, which is exactly what we want for the Pi. These are worth looking into.
You should look at benchmarks to compare SD cards on the Pi. The Pi’s own SD card access rate is admittedly limited, so you may see the Pi hit a bottleneck before the SD card itself is maxed out.
As for size, at least 8GB will do. You can get away with 4GB, but you’ll find yourself limited at that size.
USB Micro to USB A adapter, and hub (for the Zero and Zero W)
The Zero and Zero W have only one USB port for connecting external devices, and it’s a micro port. To connect anything, you’ll need a male micro to female USB A adapter. If you need to connect more than one device, then you’ll also need a hub, preferably a powered one so that you’re not drawing too much from the Pi itself.
Soldering Iron
When I was young, I bought a $15 Radio Shack stick soldering iron that plugged directly into the wall. I was always frustrated at getting bad solder joints that would never form into the nice little volcano-shaped mounds you usually see on PCBs. I dreaded soldering pins that were close together, because it was always a struggle to keep the solder from spilling over and bridging the connections together. I thought I was bad at soldering.
I eventually bought a nice soldering iron with a good temperature control unit. Turned out, I was pretty good at soldering, I just had the wrong tool from the start.
Where that old stick iron simply heated the element up with power right from the socket, a temperature controlled iron measures the heat of the tip and supplies power to keep it near a setting. They heat up much faster, as well.
The one that I bought is the Kendal 937D, and I still use it as my main iron. The Hakko FX-888D is one of the more popular models out there, though I’m not a fan. It’s more expensive than the Kendal, and setting the temperature is more cumbersome.
Solder
Be sure to get solder meant for electronics. Plumbing solder can damage electronics components. Other than that, something around 0.8mm thickness will do fine.
Solder Sucker and Wick
Even with a good iron, I sometimes make mistakes. The Solder Sucker uses a plunger or a squeeze bottle to suck the molten solder away. The wick uses capillary action to do the same. I tend to prefer the sucker method, but this is personal opinion. Either option is cheap, so it doesn’t hurt to have both around.
Solderless Breadboard
Not everything has to be soldered together. A solderless breadboard has pins connected in rows, which is nice for prototyping before laying everything down on a circuit board and making it permanent. Buy a few different sizes.
Jumper Wires
These connect the Pi and other devices together using either the pin headers or a solderless breadboard. You’ll need male-to-male, male-to-female, and female-to-female connectors. These are often available in a variety pack, in a flat strip where the individual wires can be pulled apart, and often you find these wire variety packs bundled with a breadboard (mentioned above).
Resistors, LEDs, Switches, and Capacitors
These four components are the bread and butter of electronics. You’ll want a variety of all these. 100 Ohm resistors are especially needed, since this is a good value for protecting LEDs from pulling too much current. Other than that, assortment packs are readily available for all of these.
Breakout Boards
Modern electronics manufacturing has moved towards surface mount components, which can’t be plugged into a breadboard directly. They need a specific footprint on a PCB.
Fortunately, two of the major hobbyist electronics suppliers, Sparkfun and Adafruit, sell breakout boards for many common components. These are a small PCB with a pin header that can be plugged into a breadboard, and from there into a Pi using jumper wires.
We’re going to be using the TMP102 temperature sensor chip in this book. It’s available in a breakout board from both Sparkfun and Adafruit. The chapter on GPS was tested using the Adafruit Ultimate GPS Breakout, though any GPS unit that puts out NEMA over a serial connection should work.
Pi Camera
The chapter on taking pictures will be using the Pi camera. It uses a special connector on the Pi, and is generally higher quality than any USB webcam you could buy for the same price.
2018-03-27
With the campaign for the Perl Raspberry Pi ebook underway, we’re releasing a few sample chapters. First one for today is a short intro chapter. Tomorrow, we’ll be releasing one with more meat, covering the basics of the Raspberry Pi range and some other tools you might need to get started. For now, here’s the intro:
Introduction
Welcome! If you’re reading this, you’ve probably been using Perl for a while and heard about the wonderful Raspberry Pi. If you haven’t programmed Perl before, I suggest starting with Learning Perl by Randal L. Schwartz, brian d foy, and Tom Phoenix. With that said, we try to keep the examples in this book as simple and concise as possible, sticking to the most basic of expressions wherever possible. Even without previous Perl experience, you should be able to grasp the concepts we’ve presented within if you’ve done any programming in other languages.
No previous electronics experience is necessary. We’ll be covering some of the basics where appropriate. Most of the projects can be completed without any soldering. In others, minimal soldering skills may be necessary. However, being able to solder is a valuable skill to have if you are to grow further in the field of electronics.
About the Raspberry Pi
In 2012, the Raspberry Pi Foundation was started by employees of Broadcom, intending to use one of Broadcom’s inexpensive System on a Chip (SoC) fabs to create a low-cost computer for education. Hobbyists quickly grabbed them up and started hacking away. Today, the line has expanded to several models, ranging from the $5 Pi Zero, to the $35 Pi3 (most recently, the Pi3B+).
About Perl
Perl was released by Larry Wall in 1987 inspired by a combination of several other programming languages. A major revision to Perl 5 was done in 1994. Work on Perl 6 began in 2000 as a completely separate language from Perl 5 (often referred to as its “sister” language). Since then, work on Perl 5 has continued. We’ll be using Perl 5 in this book. We recommend ensuring the most recent version of Perl is installed before attempting the projects outlined in this book, that version being 5.26 at the time of writing.
The language was important for the early web, being used in many of the first CGIs, a simple way to write server applications. Since then, it has evolved with the rest of the web and is still used by several large companies today.
It also continues as popular language for managing systems, and is installed by default on the Raspberry Pi.
2018-03-27
The Perl Raspberry Pi ebook campaign is now live! Steve Bertrand, the author of the excellent RPi::WiringPi module (among other RPi modules), has agreed to help.
The aim is to not just hand you a bunch of prebuilt modules that connect to temperature sensors and accelerometers and such. In CPAN tradition, there are quite a few modules already. But there will always be more, and it’s important that people know how to implement new hardware themselves. It’s admittedly not the easiest skill to learn (or to teach, for that matter), but I’m hoping we can get some new people hacking away, helping CPAN expand into whole new areas.
2018-03-27
Device::WebIO was originally based on a Python project, WebIOPi. It duplicated the REST API (with some slight corrections), which means it was built around the pull-style system of HTTP.
I wanted to expand it to MQTT, which means doing more push-style. It’s not a difficult addition, but it does need to be able to track input pins using async interrupts.
So version 0.020 is released, with the Device::WebIO::Device::DigitalInputAnyEvent
. This sets up the basics of putting an async loop together with the hardware portion. Next step is to integrate this into Device::WebIO::RaspberryPi, and then write Device::WebIO::MQTT to take advantage of all this.
2018-03-05
I guess the title says it all.
We’re well past the time when Perl should have an ebook about programming on the Raspberry Pi, so let’s fix that. I’d like to get a feel for what people will want to get out of this.
It will probably be based around Steve Bertrand’s series of RPi::*
modules. We’ll expand from there into web projects (Device::WebIO::Dancer
), capturing from the camera (GStreamer1
), and interfacing with Arduino (Device::Firmata
).
For i2c/SPI examples, we’ll show how to interface directly with some low-cost sensors. Tentatively, these will be an accelerometer for SPI (ADXL345) and a temperature sensor for i2c (MPL115A2). Both are available with breakout boards from Adafruit and Sparkfun.
Then there will be real project examples, such as a garage door opener and a temperature logger.
Here’s my chapter outline so far:
-
Introduction
-
RPi Models, Basic Setup, and Tools/Hardware You Need
-
GPIO [blink an LED, read from a switch, pullups/pulldowns]
-
SPI [ADXL345 accelerometer?]
-
i2c [MPL115A2 temperature sensor?]
-
Camera [GStreamer1]
-
Serial [GPS]
-
Expanding with Firmata
-
Interfacing from the Web [Device::WebIO]
-
PWM and Analog Input
-
Asynchronous code
-
Build Project: Garage Door Opener
-
Build Project: Temperature Logger
And there’s even a start on a cover:
2018-02-01
Let’s say you had an API that served JSON. Some of the responses don’t change very often. Perhaps something like user data:
{
"uid": 12345,
"addresses": [
{
"state": "SC",
"city": "Mayberry",
"street": "1234 Lalaberry Lane",
"zip": 12345
}
],
"lname": "Smith",
"fname": "John"
}
It’s an obvious case where caching could help. Perhaps you stick the data in memcached and write it out directly from your app.
This means you’re still hitting application code. Wouldn’t it be nice if you could have nginx write the cached data back to the client as if it were a static file? This is possible using a ramdisk and nginx’s try_files
.
Start with this little Mojolicious app:
#!perl
use v5.20;
use warnings;
use Mojolicious::Lite;
use File::Spec::Functions 'catfile';
use Cpanel::JSON::XS 'encode_json';
use constant CACHE_FILE_PATH => 'html';
use constant CACHE_DATASTRUCTURE => {
uid => 12345,
fname => 'John',
lname => 'Smith',
addresses => [{
street => '1234 Lalaberry Lane',
city => 'Mayberry',
state => 'SC',
zip => 12345,
}],
};
use constant CACHE_JSON => encode_json( CACHE_DATASTRUCTURE );
get '/ramdisk/*' => sub {
my ($c) = @_;
sleep 5;
my $url_path = $c->req->url->path;
my $path = catfile( CACHE_FILE_PATH, $url_path );
# TODO SECURITY ensure $path is actually under the absolute path to
# CACHE_FILE_PATH, cleaning up any '..' or other path miscreants
open( my $out, '>', $path )
or die "Can't write to $path: $!\n";
print $out CACHE_JSON;
close $out;
$c->render(
data => CACHE_JSON,
format => 'json',
);
};
get '/direct/*' => sub {
my ($c) = @_;
$c->render(
data => CACHE_JSON,
format => 'json',
);
};
app->start;
This provides two paths to the same JSON. The first one, /ramdisk/*
, will write the JSON to a path we specify under our nginx root. This has a deliberate sleep 5
call, which simulates the first request being very slow. The second, /direct/*
is for benchmarking. It dumps some pre-encoded JSON back to the client, which gives us an upper limit on how fast we could go if we pulled that data out of memcached or something.
(If you use this code for anything, do note the security warning. The code as written here could allow an attacker to overwrite arbitrary files. You need to ensure the place you’re writing is underneath the subdirectory you expect. I didn’t want to clutter up the example too much with details, so this is left as an exercise to the reader.)
Save it as mojo.pl
in a directory like this:
$ ls
html
mojo.pl
The html
dir will be the place where nginx serves its static files. Create html/ramdisk
and then mount a ramdisk there:
$ sudo mount -t tmpfs -o size=10M,mode=0777 tmpfs html/ramdisk/
This will give you a 10MB ramdisk writable by all users. When the mojo app above is called with /ramdisk/foo
, it will write the JSON to this ramdisk and return it.
Now for the nginx config. Using try_files
, we first check if the URI is directly available. If so, nginx will return it verbatim. If not, we have it proxy to our mojo app.
worker_processes 10;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
keepalive_timeout 65;
server {
listen 8001;
server_name localhost;
root html;
location /ramdisk {
default_type application/json;
try_files $uri $uri/ @cache_build;
}
location @cache_build {
proxy_pass http://localhost:8002;
}
}
}
Start this up and call http://localhost:8001/ramdisk/foo
. If the file hadn’t been created yet, then that sleep
from earlier will force it to take about 5 seconds to return a response. Once the file is created, the response should be nearly instant.
How “instant”? Very instant. Here’s the result from ab
of calling this 100,000 times, with 100 concurrent requests (all on localhost):
Concurrency Level: 100
Time taken for tests: 4.629 seconds
Complete requests: 100000
Failed requests: 0
Total transferred: 37300000 bytes
HTML transferred: 13400000 bytes
Requests per second: 21604.02 [#/sec] (mean)
Time per request: 4.629 [ms] (mean)
Time per request: 0.046 [ms] (mean, across all concurrent requests)
Transfer rate: 7869.43 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 2 0.5 2 6
Processing: 1 3 0.6 3 10
Waiting: 0 2 0.6 2 10
Total: 2 5 0.6 5 13
Percentage of the requests served within a certain time (ms)
50% 5
66% 5
75% 5
80% 5
90% 5
95% 5
98% 6
99% 7
100% 13 (longest request)
And the results from calling the mojo app with /direct/foo
:
Concurrency Level: 100
Time taken for tests: 87.616 seconds
Complete requests: 100000
Failed requests: 0
Total transferred: 28500000 bytes
HTML transferred: 13400000 bytes
Requests per second: 1141.34 [#/sec] (mean)
Time per request: 87.616 [ms] (mean)
Time per request: 0.876 [ms] (mean, across all concurrent requests)
Transfer rate: 317.66 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.3 0 17
Processing: 8 88 32.7 88 174
Waiting: 8 87 32.7 88 174
Total: 8 88 32.7 88 174
Percentage of the requests served within a certain time (ms)
50% 88
66% 101
75% 111
80% 117
90% 132
95% 142
98% 152
99% 157
100% 174 (longest request)
We took 88ms down to just 5ms. This is on an Intel Core i7-6500 @ 2.5GHz.
If you’re wondering, I didn’t see any benefit to using sendfile
or tcp_nopush
in nginx. This may be because I’m doing everything over localhost.
What I like even more is that you don’t need any special tools to manipulate the cache. Unix provides everything you need. Want to see the contents? cat [file]
. Want to clear a cache file? rm [file]
. Want to set a local override? EDITOR-OF-CHOICE [file]
.
Now to go find a use for this.
2017-09-29
Recently, my makerspace got a new laser cutter that can fit 4x2 foot plywood sheets. This means I can redo our arcade’s control panel with a design in OpenSCAD. Here’s the mockup:
The design is based on the old X-Men 6-player cabinet, though the final build got cut down to 4-players for space reasons. The red areas you see on the bottom are keepout sections for the structure underneath. There’s also a cutout in back, which is for a built-in keyboard. It’s 3/8 inch thick, and intended to be made from two layers of 1/8 inch plywood plus 1/8 inch acrylic on top.
Now, how do you go from an OpenSCAD file to a laser cutter?
In theory, you can have OpenSCAD generate an STL file, and then import that into a 3D printer slicer. A slicer’s job is to take a 3D model, break it into 2D slices, and then generate the printer head’s path for each slice. So you might think that you can set the slicer to have a layer height of 1/8 inch, and then have it output some kind of vector file format for each slice. Slic3r does indeed have an SVG output, which gives you hope.
Your hope will soon be dashed as you run into impedance mismatches. First, Slic3r wants the nozzle diameter to be larger than the layer height. We’re not going to be dealing with a physical nozzle here, but we have to set it to a ridiculous size (like 10mm) just to make Slic3r happy. Once we get past that, we get our SVG output in a single file with multiple layers. Slic3r’s SVG output is meant for resin 3D printers, and isn’t going to work well for laser cutters. What we want is one file per layer with simple paths, not filled-in.
It turns out we can hack our own slicer with OpenSCAD’s projection()
command, combined with importing an STL. It works like this:
projection( cut = true )
translate( v = [ 0, 0, -0.1 ] )
import( "arcade_control_panel.stl" );
The translate()
brings the object’s Z coord down by -0.1mm. The projection()
command then creates a 2D slice of whatever intersects at Z axis zero. Note that you need the cut = true
here. Without it, OpenSCAD creates the projection as a kind of shadow of the entire object, rather than whatever is on the Z axis.
That gets us a vector image like this:
If we loaded the code above into OpenSCAD, compiled it, and exported to a vector file like SVG or DXF, we would get the slice of the 0.1 layer. Then we’d have to do -0.1 - 3.175
(3.175mm is about 1/8 inch), compile and export, and then again for the third layer. This is too much manual effort, even for just the 3 layers I need for the control panel. I would never want to do it for something like a terrain piece.
Fortunately, OpenSCAD has a command line interface for compiling and exporting models. What we can do, then, is programmatically create the OpenSCAD projection code, run the command line, and get each layer in turn.
Which is exactly what Laser Slicer does. Here’s how to run it:
$ python laser_slicer.py --input arcade_control_panel.stl --end-height 7 --start-height 0.1 --layer-height 3.175
Input file: arcade_control_panel.stl
Output type: dxf
Start slicing height: 0.1
End slicing height: 7.0
Layer height: 3.175
==========
Creating layer 1 at height 0.1
Outputting to arcade_control_panel.stl_1.dxf
Creating layer 2 at height 3.275
Outputting to arcade_control_panel.stl_2.dxf
Creating layer 3 at height 6.45
Outputting to arcade_control_panel.stl_3.dxf
We put in a start height of 0.1 (you might get weird results if you try to start with zero), then go up 3.175mm each step, ending whenever we go above 7mm. It then generates 3 dxf files, one for each layer. It’s probably slower per layer than 3D printer slicers, but since the layers are thicker, it doesn’t need to generate as many of them.
All code on Github now.
2017-05-02
Games::Chipmunk, the Perl bindings for the Chipmunk 2D graphics library, are now at version 0.5.
There are no changes to the bindings themselves in this version, but there is an example Pachniko program:
You click the mouse to drop a ball in that spot, and away it goes. Things seem to be at the point of doing real games with it.
2017-04-04
Updates are easy, too.
my ($sql, @sql_params) = UPDATE 'foo', SET(
op( 'bar', '=', 1 ),
op( 'baz', '=', 2 ),
),
WHERE match( 'qux', '=', 3 );
UPDATE
takes a table to update, followed by SET
and WHERE
. In SET
, we’re using the op
function, which is actually an alias for match
. Calling it op
is more readable here, since we’re not matching anything. Note that the data there is still handled as placeholders.
The WHERE
part works just like it does in SELECT
.
2017-04-01
We can build an insert statement easily, too.
my ($sql, @sql_params) = INSERT INTO 'foo',
[
'bar',
],
VALUES [
1,
];
INTO
takes a table, which it feeds to INSERT
. The INSERT
function likewise takes a list of table columns (in an array ref), followed by VALUES
, which itself takes an arrayref of the data you’re going to put into the columns. All the scalars in that arrayref are passed back in @sql_params
and filled in as placeholders.
Inserting with a subselect is supported, which we will cover later.
← Prev
Next →
Copyright © 2024 Timm Murray
CC BY-NC
Email: tmurray@wumpus-cave.net
Opinions expressed are solely my own and do not express the views or opinions
of my employer.