QR Code module for the Moddable SDK

In recent months, Moddable has received inquiries for the ability to render a QR Code in the Moddable SDK. This article describes the module we created to support QR Code rendering, together with some of the design and implementation choices we made along the way to provide efficient and flexible QR Code support.

What's a QR Code?

A QR Code can be thought of as a two dimensional version of the one dimensional bar code used for UPC codes on many products. A QR Code is a machine readable encoding of data into a square subdivided into black and white squares called modules. The QR Code is designed to be read by the camera of a mobile phone to provide a URL, phone number, or other information. A QR Code can encode up to 1264 bytes of data, but typically contains quite a bit less.

Implementation

Generating a QR Code from a string of characters or bytes is complicated. There are very specific rules about how the information is organized and how to apply Reed-Solomon error correction to ensure the QR Code is read correctly. The specification for the QR Code algorithm is available from ISO. However, we chose to start with an existing implementation rather than create one from scratch.

Existing open source libraries

Since the Moddable SDK makes using standard JavaScript possible on low cost embedded microcontrollers, we began our search for open source QR Code implementations looking for a JavaScript library.

The QR Code Generator by Kazuhiko Arase is widely used and fully featured. We built it into the Moddable SDK and it ran perfectly the first time (three cheers for the work of test262 to ensure such a high level of compatibility across JavaScript engines). The library appears to be designed to support both client (browser) and server, with many great features such as outputting the QR Code to GIF or rendering it to an HTML5 Canvas. Unfortunately, that makes the code bigger and code space is limited in embedded devices. The implementation is quite modular, so the unneeded features can be easily removed. However, we found that encoding a small QR Code requires about 70KB of RAM. That's no problem in a web browser, but a big deal on a microcontroller with 128 KB or less of total RAM. Further, encoding the QR Code with the library takes a few seconds. Certainly these issues could be addressed to some degree with optimization work. However, the combination of code size, RAM use, and performance sent us looking for a native C implementation of QR Code encoding.

There are many C implementations of QR Code encoding available. The QR Code Generator from Project Nayuki stands out as small, clean, and focused. Integrating this code into the Moddable SDK required creating a native function using the XS in C API to connect the C code to JavaScript. Once that was in place, the native implementation worked the first time in the Moddable simulator. The implementation requires no more than 8 KB of RAM, under 1 KB in common cases, and runs very quickly. The code is reasonably small (about 1000 lines of C code). However, this native QR Code Generator provides only one output option -- an API to read an array of bits to render the QR Code. So, the next step is to design an efficient QR Code generator API for use with embedded JavaScript.

API Design

A common starting point for designing a JavaScript API today is to begin with a class definition. The QR Code Generator in JavaScript by Kazuhiko Arase predates JavaScript classes, but still effectively provides a class-like API. The first pass of the Moddable QR Code JavaScript class looked something like this:

class QRCode {
    constructor();
    setInputText(text);
    setInputArrayBuffer(arrayBuffer);
    setErrorCorrectionLevel(level);
    setDimension(modulesPerSide);
    setMode(mode);
    getBitmap();
}

This style is natural, but eventually a simpler approach came into focus. The native C interface to the QR Code Generator from Project Nayuki is, more or less, a single function to generate the QR Code and a two helper functions to read the generated QR Code for rendering. This suggested the possibility of a single JavaScript API for the QR Code Generator. The first attempt was compact:

let bitmap = qrCode("Welcome to Moddable", 3, 20);

Unfortunately, this approach is unreadable because the parameters are unlabeled. Replacing the parameters with an object dictionary provides labeling of all parameters and eliminates the need to specify unused parameters:

let bitmap = qrCode({input: "Welcome to Moddable"));

To encode binary data, pass an ArrayBuffer instead of a String:

let array = Uint8Array.of(1, 2, 3, 4);
let code = qrCode({input: array.buffer));

This approach works nicely, and easily accommodates the addition of new parameters in the future by extending the dictionary with new properties. For example, the caller can specify the maximum QR Code version number to use when encoding. This allows the library to allocate only the memory required, rather than the worst case allocation (about 8 KB):

let bitmap = qrCode({input: "Welcome to Moddable", maxVersion: 3));

The return value is an ArrayBuffer where byte represents one module (sub-block) of the QR Code. The ArrayBuffer has a size property which indicates the number of modules on each side of the square QR Code.

Rendering a QR Code

Here's an example of generating a QR Code:

import qrCode from "qrcode";

// generate the QR Code
let code = qrCode({input: "Welcome to Moddable", maxVersion: 4});

The number modules per side is in code.size which is used to iterate over the X and Y axes. This example traces the QR Code to the console using X for black blocks and . for white blocks:

// get the number of modules (sub-blocks) per side
let size = code.size;

// wrap returned ArrayBuffer in Uint8Array
code = new Uint8Array(code);

// trace QR code to console
for (let y = 0; y < size; y++) {
    for (let x = 0; x < size; x++) {
        if (code[(y * size) + x])
            trace("X");
        else
            trace(".");
    }
    trace("\n");
}

Notice that the return value of the qrCode function is a JavaScript ArrayBuffer and consequently isn't directly linked to any particular graphics library. This is important as it allows the QRCode function to be used as-is with any graphics library. For example, here is an example of rendering the QR Code generated in the previous example using the Poco graphics engine. This example is simplified from the qrcode example in the Moddable SDK.

const pixels = 5;       // size to render each module (sub-block)
for (let y = 0; y < size; y++) {
    render.begin(0, y * pixels, size * pixels, pixels);
    render.fillRectangle(render.makeColor(255, 255, 255), 0, 0, render.width, render.height);
    for (let x = 0; x < size; x++) {
        if (code[(y * size) + x])
            render.fillRectangle(render.makeColor(0, 0, 0), x * pixels, y * pixels, pixels, pixels);
    }
    render.end();
}

Opportunities for future work

The QR Code module in the Moddable SDK is functional and useful. It is not complete, as it does not support all of the features of the native QR Code Generator code it builds on. The following is a list of some features that should be possible to support with additional properties to the dictionary.

Error correction level

The implementation currently uses ECC level Medium. A caller should be able to specify the ECC level explicitly as LOW, MEDIUM, QUARTILE, or HIGH.

Return result as bit array instead of byte array

The return value of the qrCode function uses one byte per module (sub-block), which uses eight times more data than necessary. Bytes are used for convenience. To save memory, an option to return a bit array would be useful.

Encoding mode

The implementation currently encodes all ArrayBuffers as qrcodegen_Mode_BYTE and all text as either qrcodegen_Mode_NUMERIC or qrcodegen_Mode_ALPHANUMERIC based on auto-detection. For a particular situation, the user of the QRCode module may want to specify the mode.

Multiple input segments

A single QR Code can contain sections of data in different encodings (byte, number, alphanumeric, etc). The current implementation only allows a single segment.

Modifications to Project Nayuki's QR Code Generator

The QR Code Generator worked unmodified with the Moddable simulator. However, to achieve optimal results on a range of microcontrollers required a few simple changes. These changes are motivated by the ESP8266 but work on all device targets when using the Moddable SDK. The changes are:

  • Reduce static RAM use by adding attribute ICACHE_XS6RO_ATTR to global static data structures to allow linker to place them in ROM, not RAM. This effects ALPHANUMERIC_CHARSET, ECC_CODEWORDS_PER_BLOCK, and NUM_ERROR_CORRECTION_BLOCKS.
  • Use c_* macros from xsPlatform.h for string and memory functions (e.g. c_strlen instead of strlen) to use the flash-safe versions of these functions on ESP8266.
  • Use c_read8 to read bytes from input string, to allow it to work on strings in ROM.