Are A2 (or A1) Application Class SD cards marketing BS?

People often misunderstand the speed ratings of SD cards. They are built cheaply, and historically have primarily targeted digital cameras. That meant they emphasized sequential IO performance, because that’s how pictures and video are handled. When we plug an SD card into a Raspberry Pi, however, most of our applications will use random reads and writes, which is often abysmal on these cards.

Enter the A1 and A2 speed ratings. These were created by the SD Card Association to guarantee a minimum random IO performance, which is exactly what we want for single board computers.

There’s been a blog post floating around about how the A2 class is marketing BS. In some cases, I’ve seen this cited as evidence of the A1 class also being marketing BS. After all, the top cards on the chart for random IO, the Samsung Evo+ and Pro+, don’t have any A class marks on them.

This misunderstands what these marks are for and how companies make their cards. Your card has to meet a minimum speed to have a given mark. You are free to exceed it. It just so happens that Samsung makes some really good cards and have yet to apply for the mark on them.

Samsung could change the underlying parts to one that still meets its certification marks for the model, but with far worse random IO performance. I don’t think Samsung would do that, but they would technically be within their rights to do so. This kind of thing has happened in the storage industry before. Just this past year, Western Digital put Shingled Magnetic Recording on NAS drives (SMR is a hard drive tech that craters random write performance, making them completely unsuitable for NAS usage). XPG swapped out the controller on the SX8200 Pro to a cheaper, slower one invaliding the praiseworthy reviews they got at launch.

Even if Samsung wouldn’t do that, it’s still something where you have to go out of your way to find the best SD card. Well-informed consumers will do that, but the Raspberry Pi serves a broad market. Remember, its original purpose was education. You can’t expect people to even know what to research when they’re just starting out. It also doesn’t help that major review sites don’t touch on SD cards. Benchmarking can be a tricky thing to do right, and most hobbyist bloggers don’t have the resources to do a good, controlled test, even if they mean well.

What the A class marks do is give a clear indication of meeting a certain minimum standard, at least in theory. Independent reviews are always good in order to keep everyone honest, but if you don’t have time to look at them, you can grab an A1 card and put it in your Pi and you’ll be fine.

As the blog post noted above states, A2 cards don’t always live up to their specs. According to a followup post, this appears to be due to things the OS kernel needs to support, rather than the card itself. It’s also possible for a company to try to pull a fast one, where they launch with a card that meets spec, and then quietly change it. However, if they do, they don’t just have consumer backlash to contend with, but the SD Card Association lawyers. Since they hold the trademark on the A class marks, they have the right to sue someone who is misusing it.

Marketing isn’t just about making people into mindless consumers. It can also be about conveying correct information about your product. That’s what the A classes are intended to do. Nobody knew that Samsung Evo and Pro cards were good until somebody tested them independently. With the A class marks, we have at least some kind of promise backed up with legal implications for breaking it.

The Seeed XIAO

The Seeed XIAO board

Seeed has been picking up in the maker community as a source of low cost alternatives to popular development boards. The Rock Pi S is an excellent competitor to the Raspberry Pi Zero, providing the quad core processor that the Pi Zero lacks. They have also released the Seeed XIAO, a small ARM-based microcontroller competing with Arduino or Teensy boards, and sent me units for this review.

The board is small but capable, with an ARM Cortex M0+ 32-bit processor running at 48MHz, 256KB of flash, and 32KB of SRAM. Programming is done through a USB-C port, which also powers the device. There are 14 pins, including one 5V output, one 3.3V output, and one ground. The rest are I/O pins, all of which are capable of digital GPIO, analog input, and PWM output. There’s also a single DAC channel, plus I2C, SPI, and UART. Also included is a set of stickers that fit on the board’s main chip and shows the basic pinout of the main 10 pins.

Shipping came directly from China, and took several months. I’m sure at least some of the delay was likely due to Coronavirus issues. Still, this isn’t the first time I’ve seen Seeed be slow to ship. If you’re interested in this product, I suggest ordering in advance.

Once it arrived, the board was easy to setup. Pin headers come in the package, but will need to be soldered. Their wiki page for the board was easy to follow, especially if you have previous experience with Arduino-based boards. Programming is done through the Arduino IDE, using a plugin that provides the right compiler environment and firmware flasher. Many new boards are preferring the free version of Microsoft Visual Studio with PlatformIO, but Seeed is choosing to stick with Arduino’s IDE for this one.

The board can be tricky to get into programming mode. When uploading code, I often encountered an error like this in the IDE console (on a Windows machine):
Caused by: Error touching serial port 'COM5'.
Caused by: jssc.SerialPortException: Port name - COM5; Method name - openPort(); Exception type - Port busy.

Fixing this is done by double tapping the reset pad with a wire over to the pad right next to it. When it goes into bootloader mode correctly, Windows would often pop up a file manager for the device, as if a USB flash drive had been connected. This may also change the COM port, so be sure to check that before trying to upload again.

With that issue sorted, I uploaded the basic LED blinker example from Arduino, and it worked fine against the LED on the board itself. That shows the environment was sane and working.

I next turned my attention to a project I’ve had sitting on my shelf for a while: modifying an old fashioned oil lantern for a Neopixel. I received this lamp from a secret santa exchange party last year, and was told that while it had all the functional parts, it wasn’t designed to take the heat of a real burning wick. So I picked up Adafruit’s 7 Neopixel jewel, and combined it with NeoCandle, which would give a slight flickering effect. NeoCandle was designed for 8 Neopixels rather than 7, but commenting out the call to the eighth pixel worked fine.

Complicated libraries like this are often made for Arduino boards first, and everything else second, so I was bracing myself for a long debugging session to figure out compatibility. My worry was unnecessary, as the Adafruit Neopixel library worked without a hitch.

Neopixel lantern

(White balance on my phone camera wasn’t cooperating. The color of the light is more orange in person. It also has a very subtle flicker effect.)

At $4.90 each, this is a nice little board with a good feature set. It’s cheaper than the Teensy LC, and in terms of processing power, more capable than Arduino Minis. Getting the binary uploaded can be janky, but to be fair, that’s common on a lot of ARM-based microcontrollers. When a project doesn’t need a lot of output pins, I would use this in place of a Teensy LC. Just watch for those shipping times.

Game::Collisions v0.1 – Fast 2D collision detection

Some years ago, I wrote a little top-down shooter in Perl. It was not a good game, and was never released, but I learned a lot making it. One problem I encountered was collision detection. Checking every object for collisions against every other object is not feasible past a small size. Especially in pure Perl, since this is a straight numeric problem, but it’s still a struggle for C to do it.

We have faster processors now, so the number of objects you can throw in there has increased a lot. Still, it’d be better to have a faster algorithm than brute force. The typical choice is to use a binary partition tree to split up Axis Aligned Bounding Boxes. This lets you do very fast collision detection even with gigantic lists of objects. I won’t go into detail here, as there’s an excellent blog post that already goes into it:

Most likely, you would use this to quickly prune the list of possible collisions (“broad phase”), and then use more expensive techniques to get pixel-perfect (“narrow phase”).

This is what’s implemented in Game::Collisions v0.1. Here’s some benchmarks comparing the tree-based technique to brute force (running on a modest laptop CPU, an Intel i7-6500U):

Running tree-based benchmark
    Ran 1000 objects 6000 times in 1.845779 sec
    3250660.01942811 objects/sec
    54177.6669904685 per frame @60 fps

Running brute force benchmark
    Ran 1000 objects 6000 times in 6.201839 sec
    967454.975854742 objects/sec
    16124.249597579 per frame @60 fps

This creates a tree of 1000 leaf objects and then gets all the collisions for a specific AABB. As the number of AABBs increases, we would expect brute force to go up linearly, but the tree method to go up logarithmically, so these numbers will get further apart as the tree grows.

Sous Vide and Thermocouples

A while back, I built a DIY sous vide cooker. It’s gone through a major revision since the original. My current one uses power outlets to connect the teacup heaters, rather than hard wiring them in. I’m in the midst of making another big change to it, which is to use an ESP8266 with custom programming, rather than an off the shelf PID controller, and also to use a different temperature probe.

Which brings me to this post. The original instructions used a k-type thermocouple, probably because it’s cheap and easily available. After some research, I think the accuracy range of the k-type thermocouple is unacceptable for sous vide.

The accuracy of a k-type thermocouple, for the temperature range we care about for sous vide, is ±2.2C. Now, the FDA recommends that food never be stored between 5C and 54.5C for more than four hours. That means if you’re cooking beef at a medium-rare temperature of 56.5C, a k-type thermocouple could be below the safe temperature.

It’s also worth mentioning that the k-type thermocouple’s temperature range is far beyond what we need for sous vide. We want something that will be accurate inside 50C and 100C. The tighter range of the t-type thermocouple is still more than we need, and its accuracy (for what we care about) would be ±1.0C.

We may be able to do better still. Resistance Temperature Detectors (RTD) probes are more accurate still. A probe with Class-A accuracy would be ±0.35 or less. It may also be cheaper, as you avoid the need for an amplifier chip, which you need with a thermocouple to convert its microvolts into something readable on normal electronics. RTDs just need something that can read resistance.

Which would be no problem on an Arduino with two ADC channels. You need the second one to read a resistor with a known value and then do some math to compare. Unfortunately, the ESP8266 (needed for some of the wireless features I want) only has one ADC channel. There’s some ways to fix that, but they involve some extra hardware. Might end up being a wash in terms of price and complexity. Still, I think it’s worth it for the extra accuracy.

Edit: doing it with a single ADC is possible with a voltage divider, which just needs a resistor with a known value. Cheap and easy.

Device::WebIO::RaspberryPi 0.900, now with AnyEvent and RPi::WiringPi backend

Device::WebIO::RaspberryPi 0.900 has been released. The big changes are to change the backend from HiPi to RPi::WiringPi, as well as put in a new input event system based on AnyEvent. There’s also some slight updates to interrupt handling in Device::WebIO in version 0.022 to support this.

It works by having a condvar with a subref. You can get the pin number and its value out of $cv->recv:

use v5.14;
use Device::WebIO;
use Device::WebIO::RaspberryPi;
use AnyEvent;

use constant INPUT_PIN  => 2;

my $rpi = Device::WebIO::RaspberryPi->new;
my $webio = Device::WebIO->new;
$webio->register( 'rpi', $rpi );

my $input_cv = AnyEvent->condvar;
$input_cv->cb( sub {
    my ($cv) = @_;
    my ($pin, $setting) = $cv->recv;
    say "Pin $pin set to $setting";
$webio->set_anyevent_condvar( 'rpi', INPUT_PIN, $input_cv );

say "Waiting for input";
my $cv = AE::cv;

This is one part of a series of updates to support an upcoming Device::WebIO::MQTT. The original versions of the Device::WebIO family were meant to an HTTP interface (like the one in Device::WebIO::Dancer). Where HTTP pulls data, MQTT is push. The new AnyEvent system should integrate easily with AnyEvent::MQTT.

MQTT is also part of our stretch goals for the Raspberry Pi Perl eBook. Don’t worry though–I’ll be working on Device::WebIO::MQTT regardless of the funding level on the book.

New Perl Raspberry Pi ebook campaign!

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.

New Device::WebIO release

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.

SQL::Functional Cookbook–Updates

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.

Callback Functions Between C and Perl

Everything you ever want to do with xs is documented somewhere in perlxs, perlguts, perlapi, perlxstypemap, and perlcall. Figuring out where it’s documented, and how it relates to everything else, is the hard part.

In my case, the part I wanted was in perlcall, in the section “Strategies for Storing Callback Context Information”. What I wanted to do was to glue together the callbacks for the Perl interface in Games::Chipmunk to the Chipmunk 2D physics library.

From Perl, we want to be able to say things like this:

cpBodySetVelocityUpdateFunc( $ballBody, sub {
    my ($body, $gravity, $damping, $dt) = @_;
    cpBodyUpdateVelocity( $body, $gravity, $damping, $dt );

The C version of cpBodySetVelocityUpdateFunc() does not know what a Perl sub is. It sees an SV, which happens to contain a bunch of things that the Perl interpreter can execute as a Perl sub. What we need to do is hand off a C function to the callback, and then use that to grab our SV and use call_sv() to call it.

If C supported closures, this would be easy. C does not support closures.

Some C libraries with callbacks have a parameter that will pass whatever data you give it directly into the callback later on. Chipmunk has this on a few functions, but not consistently.

One thing the Chipmunk libraries do give us is a pseduo-Object Oriented interface, where we pass the associated datastructure in as the first parameter on every function (Object Oriented languages move this parameter to the left of the function call). This gives us something we can grab on to for getting the SV we need that stores our Perl sub.

The “object” is a pointer, and pointers are just numbers. Numbers can be looked up in a hash. So we’ll make a bunch of global hashes, one for each set of callbacks, and use the address as the lookup key and the SV as the value.

The examples below use Perl’s context macros, which means those global hashes are still thread-safe. You can read about them in perlxs, under the section “Safely Storing Static Data in XS”. Their use won’t be detailed here.

Using cpBodySetVelocityUpdateFunc as an example, we start with the xs declaration:

cpBodySetVelocityUpdateFunc( body, velocityFunc )
    cpBody *body
    SV* velocityFunc

        (cpBodyVelocityFunc) __perlCpBodyVelocityFunc

We’re taking an SV* and assuming it holds a reference to the sub. We store it in the HV* bodyVelocityFuncs, which is initialized elsewhere. Using (char*)&body (C is a true bastion of type safety), we convert the address of the cpBody* into a char pointer, which the hash can use as a key. Lastly, we call the actual cpBodySetVelocityUpdateFunc() in the C library, and pass it our own C function as a callback.

That C function looks like this:

static void __perlCpBodyVelocityFunc(
    cpBody* body,
    cpVect gravity,
    cpFloat damping,
    cpFloat dt
static void
    cpBody* body,
    cpVect gravity,
    cpFloat damping,
    cpFloat dt
) {
    SV ** perl_func = hv_fetch(
    if( perl_func == (SV**) NULL ) {
        croak( "No cpBodyVelocityFunc found" );

    EXTEND( SP, 4 );
    PUSHs( sv_2mortal( sv_setref_pv( newSV(0), "cpBodyPtr", body ) ) );
    PUSHs( sv_2mortal( sv_setref_pv( newSV(0), "cpVectPtr", &gravity ) ) );
    PUSHs( sv_2mortal( newSVnv( damping ) ) );
    PUSHs( sv_2mortal( newSVnv( dt ) ) );

    call_sv( *perl_func, G_VOID );

This goes at the top of your xs file, before any PACKAGE declarations, up with #include "EXTERN.h" and such. The perlxs doc doesn’t show this part in its examples very well, but you need to put the dTHX; call here to declare the context for a bunch of Perl macros that come later. Otherwise, you’ll get a bunch of cryptic compiler errors and spend a few hours scratching your head, until you finally come across the section in perlguts entitled “How multiple interpreters and concurrency are supported”. Like I said, everything is documented, you just won’t know where.

Going past the Perl macros, we get to hv_fetch(). This function should have gotten the same cpBody* as we got earlier, so we once again torture the type system and pretend it’s a pointer to a char array for the sake of a hash lookup key. The person who wrote this part of the interface was a two-star C programmer, so we better check that we actually got the SV we wanted by checking perl_func == (SV**) NULL.

Now we’ll need to grow the stack so we can push the subref’s arguments there. In this case, the callback received four arguments, and we want to pass all four to the subref. In this particular case, the last two arguments are easy. They’re double precision floats, which we can pass directly into a Perl SV that contains a number.

The first two are the tricky ones. They’re pointers to complex structs. In my typemap, I made cpBody* into a T_PTROBJ. This means it converts the ‘*’ into ‘Ptr’, collapses any whitespace, and uses the resulting name as the Perl class. You can make an SV contain a pointer to this C object and then carry it around in Perl. The cpVect* comes out the same way (since we take a pointer to the gravity struct).

Finally, we can call call_sv() to call the actual subref.

The good news is that Games::Chipmunk v0.3 has most of the callbacks implemented. There’s some in cpSpatialIndex that aren’t there because they don’t follow the same conventions. Still, this should be good enough to write real physics systems for games in Perl.