www.alxm.org

Hello Arduboy

I wanted to play around with Arduino for a long time, and the Arduboy handheld was the perfect excuse to get started! My Hello World program is ArduboyMouse, a sketch that lets you use the Arduboy as a 2-button mouse.

Contents

Setting Up#

Check out the excellent Quick Start Guide on the community forums, it covers everything.

I tried the Arduino IDE from the Ubuntu 16.04 repo, but version 1.0.5 is too old and it does not support the library fetching functionality mentioned in the Quick Start guide.

The avr-gcc and avr-dude tools in the repo are about as recent as those bundled with Arduino 1.8.1 though, which is great because in the future I would like to build Arduboy games from the command line without the IDE.


Upload a Program - Troubleshooting#

On Linux, Arduboy shows up as a serial device under /dev/ttyACM0. However, only the root user and the dialout group have access permissions to it, so trying to upload a sketch with the Arduino IDE will fail. According to the Debian Wiki, the dialout system group is for full and direct access to serial ports, which sounds like exactly what we need.

1
2
$ ls -l /dev/ttyACM0
crw-rw---- 1 root dialout 166, 0 Jan 24 18:37 /dev/ttyACM0

The solution is to add your user to this group:

1
$ sudo usermod -a -G dialout $USER

You can confirm the change by running the groups command after logging out and logging back in again.

More About /dev/ttyACM0#

While researching this issue, I found an article that suggested changing the permissions on /dev/ttyACM0 instead, to allow rw by everyone. That does not work for the Arduboy though, because the serial device is re-enumerated when the board is reset and put in flash mode, which loses the permission changes you made on the file.

The ATmega32U4 microcontroller that runs programs also handles the USB connection, so when the MCU is reset the USB connection is reset too. Read Differences from the Arduino Uno for more details.


Using the Arduboy as a HID Mouse#

This is very easy thanks to the Arduino HID Mouse and Keyboard libraries available on ATmega32U4 boards like the Arduboy, which present themselves as USB HID devices.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
void loop()
{
    if(!arduboy.nextFrame()) {
        return;
    }

    #define MAX_SPEED  8
    #define ACCELERATE 1
    #define DECELERATE 2

    static signed char moveX = 0;
    static signed char moveY = 0;

    arduboy.pollButtons();

    if(arduboy.pressed(LEFT_BUTTON)) {
        moveX = max(moveX - ACCELERATE, -MAX_SPEED);
    } else if(arduboy.pressed(RIGHT_BUTTON)) {
        moveX = min(moveX + ACCELERATE, MAX_SPEED);
    } else if(moveX < 0) {
        moveX = min(moveX + DECELERATE, 0);
    } else if(moveX > 0) {
        moveX = max(moveX - DECELERATE, 0);
    }

    if(arduboy.pressed(UP_BUTTON)) {
        moveY = max(moveY - ACCELERATE, -MAX_SPEED);
    } else if(arduboy.pressed(DOWN_BUTTON)) {
        moveY = min(moveY + ACCELERATE, MAX_SPEED);
    } else if(moveY < 0) {
        moveY = min(moveY + DECELERATE, 0);
    } else if(moveY > 0) {
        moveY = max(moveY - DECELERATE, 0);
    }

    if(moveX != 0 || moveY != 0) {
        Mouse.move(moveX, moveY, 0);
    }

    if(arduboy.justPressed(A_BUTTON)) {
        Mouse.press(MOUSE_LEFT);
    } else if(arduboy.justReleased(A_BUTTON)) {
        Mouse.release(MOUSE_LEFT);
    }

    if(arduboy.justPressed(B_BUTTON)) {
        Mouse.press(MOUSE_RIGHT);
    } else if(arduboy.justReleased(B_BUTTON)) {
        Mouse.release(MOUSE_RIGHT);
    }
}

Mouse.move moves the cursor by the amount specified relative to its original position.


Displaying Images on the Arduboy#

The Arduboy's framebuffer has an interesting format fitting a 1bpp (bit-per-pixel) screen, where each byte encodes a vertical strip of 8 pixels. Strips then flow horizontally left to right, and wrap to the next row of 8-pixel strips.

For example, here is a sprite, with its screen-format pixel strips highlighted, and finally the bytes as they are in memory.

bytes[0..2]=0x00, bytes[3]=0x80, bytes[4]=0x70, bytes[5]=0x08, etc.

Arduboy framebuffer format

I wrote a Python 3 script to convert normal RGB images to this format:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
from PIL import Image

def main(ImageName, UniqueName):
    image = Image.open(ImageName)
    width, height = image.size
    pixels = image.load()
    encoded_bytes = []

    for y_start in range(0, height, 8):
        for x in range(0, width):
            byte = 0

            for y in range(0, min(height - y_start, 8)):
                r, g, b = pixels[x, y_start + y]

                if (r + g + b) / 3 > 128:
                    byte |= 1 << y

            encoded_bytes.append(byte)

    formatted_bytes = ''

    for index, byte in enumerate(encoded_bytes):
        if index % 16 == 0:
            formatted_bytes += '\n    '

        formatted_bytes += '0x{:0>2x},'.format(byte)

    contents = """\
#pragma once

#include <stdint.h>

static const uint8_t gfx_{0}_w = {1};
static const uint8_t gfx_{0}_h = {2};

PROGMEM static const uint8_t gfx_{0}_data[] = {{{3}
}};
""".format(UniqueName, width, height, formatted_bytes)

    print(contents)

ArduboyMouse's graphic, mousegfx.h, was generated by calling:

1
$ python3 ArduboyImage.py mouse.png mouse > mousegfx.h

Next Steps#

Next up is building projects directly with Makefiles and avr-gcc, and making a few small games. Fun times ahead!

ArduboyMouse running on an Arduboy