Image Player Firmware

This project requires CircuitPython version 4.0 or higher. Refer to the HalloWing CircuitPython page for ensuring you upload the HalloWing version of CircuitPython 4.0 or greater.

To load the CircuitPython firmware, plug in the HalloWing over a known good USB cable to your computer and then double-click the reset button on the HalloWing. This will put it into bootloader mode, allowing you to change the firmware. You'll see a USB drive appear called HALLOWBOOT.

Drag the .uf2 file you downloaded onto the HALLOWBOOT drive. Once it copies over, it will automatically restart and show a flash drive named CIRCUITPY. Then you are ready to load a CircuitPython program that can take advantage of the display capabilities.

File Format

To prepare your graphics for the HalloWing, you'll need to size them to 128 x 128 pixels. You can do this in nearly any graphics program, such as Photoshop, GIMP, Preview on osx, ImageMagik, MS Paint, you name it!

Save your file as a 24-bit .bmp file. Here you can see some example graphics.

sensors_lookslikeyes.bmp

sensors_nodoubtaboutit.bmp

sensors_verylikely.bmp

Here is the full set of answers for your Magic 9 Ball. Download the file and then unzip it.

Drag the .bmp files onto your CIRCUITPY drive as shown.

Tip: Images inside of a folder on your HalloWing won't be seen by this image player code below, only files at the root level of the HalloWing are seen. So, you can swap files in and out of folders to quickly change your image set!

Magic 9 Ball Code

This program is pretty simple in what it does -- it loads a random image onto the screen using displayio.TileGrid while fading the backlight up with  board.DISPLAY.brightness

Then, whenever the HalloWing's accelerometer detects the board has been turned upside down or shaken, it dims the backlight, selects and displays a new image, and brings the backlight up again.

Adafruit really likes using the Mu editor to edit the CircuitPython code. See this guide on loading and using Mu.

# SPDX-FileCopyrightText: 2018 John Edgar Park for Adafruit Industries
#
# SPDX-License-Identifier: MIT

# Magic 9 Ball
# Turn HalloWing face down and then face up to change images at random
# place 128 x 128 pixel 24-bit .bmp images at root level of HalloWing

import time
import os
import random
import board
import displayio
import adafruit_lis3dh

splash = displayio.Group()
board.DISPLAY.root_group = splash

SENSITIVITY = 5   # reading in Z direction to trigger, adjustable

images = list(filter(lambda x: x.endswith("bmp"), os.listdir("/")))

i = random.randint(0, (len(images)-1))  # initial image is randomly selected

# Set up accelerometer on I2C bus, 4G range:
i2c = board.I2C()  # uses board.SCL and board.SDA
# i2c = board.STEMMA_I2C()  # For using the built-in STEMMA QT connector on a microcontroller
ACCEL = adafruit_lis3dh.LIS3DH_I2C(i2c, address=0x18)

ACCEL.range = adafruit_lis3dh.RANGE_4_G

while True:
    shaken = False

    print("Image load {}".format(images[i]))
    # CircuitPython 6 & 7 compatible
    try:
        f = open(images[i], "rb")
        odb = displayio.OnDiskBitmap(f)
    except ValueError:
        print("Image unsupported {}".format(images[i]))
        del images[i]
        continue
    face = displayio.TileGrid(odb, pixel_shader=getattr(odb, 'pixel_shader', displayio.ColorConverter()))

    # # CircuitPython 7+ compatible
    # try:
    #     odb = displayio.OnDiskBitmap(images[i])
    # except ValueError:
    #     print("Image unsupported {}".format(images[i]))
    #     del images[i]
    #     continue
    # face = displayio.TileGrid(odb, pixel_shader=odb.pixel_shader)

    splash.append(face)
    # Wait for the image to load.
    try:
        board.DISPLAY.refresh(target_frames_per_second=60)
    except AttributeError:
        board.DISPLAY.wait_for_frame()

    # Fade up the backlight
    for b in range(101):
        board.DISPLAY.brightness = b / 100
        time.sleep(0.01)  # default (0.01)

    # Wait until the board gets shaken
    while not shaken:
        try:
            ACCEL_Z = ACCEL.acceleration[2]  # Read Z axis acceleration
        except OSError:
            pass
        # print(ACCEL_Z)  # uncomment to see the accelerometer z reading
        if ACCEL_Z > SENSITIVITY:
            shaken = True

    # Fade down the backlight
    for b in range(100, 0, -1):
        board.DISPLAY.brightness = b
        time.sleep(0.005)  # default (0.005)

    splash.pop()

    i = random.randint(0, (len(images)-1))  # pick a new random image
    # print("shaken")
    faceup = False
    i %= len(images) - 1

Copy the code and then paste it into Mu. Save the file to your CIRCUITPY board as code.py.

The board will restart and then play an image. Try turning the Magic 9 Ball upside down and then back up -- you'll see a new image appear!

Answer Any Question

You can now unplug the USB cable (plug it back in any time to charge the battery) and use the Magic 9 Ball!

Ask it a yes or no question, such as "Have we reached the mother ship yet?", then turn the ball over (or give it a shake), then turn it right side up and the answer will present itself in all of its wisdom!

This guide was first published on Sep 22, 2018. It was last updated on Mar 29, 2024.

This page (Code with CircuitPython) was last updated on Mar 28, 2024.

Text editor powered by tinymce.