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.

ArduboyMouse running on an Arduboy

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 it's too old (1.0.5) and it doesn't 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 since in the future I'd like to build Arduboy games without the IDE.

Back to top

Upload a Program - Troubleshooting

On Linux, Arduboy shows up as a serial device under /dev/ttyACM0. However, only root and the dialout group have access permissions on it, so trying to upload a sketch with the IDE will fail. According to the Debian Wiki, the dialout system group is for full and direct access to serial ports, 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 <your_username>

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

Aside

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.

Back to top

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);
    }
}

Back to top

Displaying Images on the Arduboy

The Arduboy's framebuffer has an interesting format fitting a 1bpp 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.

Here is a sprite, with the pixel strips highlighted, and finally the bytes as they are in memory. Here byte0..2=0x0, byte3=0x80, byte4=0x70, byte5=0x08, byte6=0xC6, etc.

Arduboy framebuffer format

I wrote a Python script that converts 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

Back to top

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

Back to top