A Color Picker for Microcontrollers

The color wheel is a common user interface element in desktop and mobile apps that allows the user to select a color. User interfaces to select a color are rare on displays powered by embedded microcontrollers. When present, the interface is primitive, perhaps an array of color boxes. The color-picker example implements a true color wheel that works well on embedded devices.

The color picker provides a simple user interface element built using content objects from the Piu user interface framework. It is packaged as a module making it easy to incorporate into any project built using Piu. The example app imports the color picker module to build its user interface.

This post describes:

  • projects we've used the color picker in
  • how to use the color picker into your own projects
  • techniques that make the color wheel possible on an embedded device

Projects

I originally implemented the color picker for our office light switch. This light switch is built with an ESP8266 and the same capacitive touchscreen on Moddable One and Moddable Two. It can be configured to control the basic on/off light attached to one of the walls and all of the Wi-Fi lightbulbs we have around the office (which we reprogrammed to run our own software, as detailed in an earlier blog post).

Some time later, my colleague Andy used the color picker in the qwiictwist example, which he built to test the SparkFun Qwiic Twist (a digital RGB rotary encoder). This example displays the numbers of twists and presses of the encoder and allows you to change its color using the color picker.

We decided to package the code of the color picker into a module to make it easier for other developers to reuse. The color-picker module is available now in the Moddable SDK for anyone to use in their own projects. The "Using the Color Picker" section below explains how to incorporate the color picker in your project. The "How it Works" section after that explains the implementation.

Using the Color Picker

To add the color picker to your app, first update your app's manifest by adding the color-picker module to the include object.

"include": [
    /* other includes here */
    "$(MODULES)/input/color-picker/manifest.json"
]

The color picker is made available by the export default of the color-picker module. The following line imports the color picker object and names it ColorPicker.

import ColorPicker from "color-picker";

ColorPicker is a constructor for a Piu Container object, so you can add instances of it into your containment hierarchy like any other Piu content object. For example, the color-picker example simply adds a color picker to the application object:

const ColorPickerApp = Application.template($ => ({
    ...
    contents: [
        new ColorPicker($, { top: 100 }),
        ...
    ],
    ...
}));

When the user drags the picker over a new color, the onColorPicked event is distributed throughout the app. To respond to this event, add an onColorPicked method to behaviors of content objects in your app. The following code from the color-picker example converts the color to a hex color string and uses the string to update the on-screen label.

class AppBehavior extends Behavior {
    ...
    onColorPicked(app, color){
        let r = this.toHex(color.red);
        let g = this.toHex(color.green);
        let b = this.toHex(color.blue);
        let hexColor = `#${r}${g}${b}`;

        let data = this.data;
        data["COLOR_SAMPLE"].skin = new Skin({ fill: hexColor });
        data["COLOR_SAMPLE"].string = hexColor;
    }
    ...
}

How It Works

The implementation of the color-picker module is not particularly complicated. The graphical elements of the color picker are Piu content objects, like the ones you see in all other Piu examples. However, the code that determines the selected color uses Poco and Commodetto APIs in a way you won't find in other examples. This section describes that code.

Theory

The color wheel is stored as an image file. This is necessary because calculating the color of each pixel in the color wheel is too computationally complex to be done in real time on a microcontroller. Therefore, to determine the color selected by the user, the color-picker needs to get the value of one pixel from the color wheel image.

The obvious approach is to have the color picker read a pixel from the image file. This approach is complicated by the fact that the image may be stored in different file formats depending on the host configuration. Instead, the color picker draws the image file to a bitmap stored in memory. The bitmap is just one pixel in size, and the image is clipped so that the selected color is drawn to the bitmap. From there, it is a simple matter of retrieving the pixel from the bitmap's memory and extracting the red, green, and blue color components.

Not only is this approach resilient to changes in the image file format, it is very fast. Further, the color picker code has no dependency on the content of the image. If you want to use a different image for the color-wheel, you can do that just by editing or resizing the original image—no code changes are required.

Practice

You create offscreen bitmaps using the Commodetto BufferOut class. The onCreate method of ColorWheelBehavior creates an offscreen bitmap that has a height and width of 1 pixel and uses the pixel format of the display driver. It then wraps this offscreen bitmap in a new instance of the Poco renderer to be able to draw to it.

onCreate(wheel, data) {
    ...
    this.wheelGraphic = parseBMP(new Resource("color-color.bmp"));
    let offscreen = new BufferOut({ width: 1, height: 1, pixelFormat: screen.pixelFormat });
    this.pocoOff = new Poco(offscreen);
}

Each time the color picker is moved, the getColor method of ColorWheelBehavior is called to determine the color of the pixel that was selected when the user moved their finger. The built-in Piu touch events—onTouchBegan, onTouchMoved, and onTouchEnded—provide the x and y coordinates of the touch event. These events pass the coordinates as arguments to the getColor method, which uses them to clip drawing to the selected pixel by using the source clip rectangle arguments to drawBitmap.

getColor(x, y) {
    this.pocoOff.begin();
    this.pocoOff.drawBitmap(this.wheelGraphic, 0, 0, x, y, 1, 1);
    this.pocoOff.end();
    ...

Then it uses a DataView to read the data from the offscreen bitmap, and converts the data to a dictionary with red, green, and blue properties.

    ...
        let view = new DataView(buff);
        let testcolor = view.getUint16(0, true);

        let red = ((testcolor >> 11) & 0x1F);
        let green = ((testcolor >> 5) & 0x3F);
        let blue = (testcolor & 0x1F);
        red = red << 3 | red >> 2;
        green = green << 2 | green >> 6;
        blue = blue << 3 | blue >> 2;

        return { red, green, blue };
}

The color value is distributed throughout the app through an onColorPicked event.

Conclusion

The Moddable SDK color picker module provides a simple user interface for selecting colors that is easy to incorporate into your own apps. This interface is common in mobile and web apps, but is certainly not something you see every day on displays powered by microcontrollers.

We'd love to see developers use the color picker in their own projects. Let us know on Twitter or our Gitter chatroom if you do!