Freestanding cyclical POV?


Persistence-of-vision projects like our SpokePOV kit use a magnetic Hall effect sensor to gauge both time and position — a magnet on the bike frame or fork provides an absolute point of reference as the wheel spins.

At least once a week we’re asked whether it’s possible to do this sort of “cyclical” persistence-of-vision freestanding without a frame — perhaps on a staff or spinning poi — could it be done with data from an accelerometer or gyro?

In theory, yes, even a basic accelerometer should provide sufficient data to do this…but it’s like finding a whisper in a sandstorm of noise. Those with the know-how are probably (I hope) off earning good money designing consumer products like fitness trackers and game controllers, using signal processing and higher math to pull the subtlest gestures from the cheapest possible sensors.

For those of us less enlightened, I’d been wondering if the BNO055 9-DOF absolute orientation sensor might provide us a crutch. This chip combines accelerometer, gyro and compass along with some math to provide more “meaningful” numbers — for example, it can distinguish between gravity and the device’s own acceleration vector separately. Could this get us muggles where we want to be?

The gyro should give a pretty good indication of current rotation rate, but we need an absolute frame of reference — an up or a down — to create steady, repeating images. Maybe we could poll for the device’s absolute orientation, or alternately the gravity vector, watching for changes where we’re pointing “up-ish” or “down-ish” to establish a horizon line.


I threw together a “napkin sketch” with the sensor, an Arduino board, battery and some NeoPixels for visual feedback, and the requisite cable ties (they’re cleaner than duct tape), plus a little code:

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BNO055.h>
#include <Adafruit_NeoPixel.h>

Adafruit_BNO055 bno = Adafruit_BNO055();
Adafruit_NeoPixel strip = Adafruit_NeoPixel(15, 4);

void setup(void) {

boolean prev;

void loop(void) {
 imu::Vector<3> euler = bno.getVector(Adafruit_BNO055::VECTOR_GRAVITY);
 boolean plusx = (euler.x() >= 0);

 if(!prev && plusx) fill(0xFF0000); // RED = Change from -x to +x
 else if(prev && !plusx) fill(0x00FF00); // GREEN = Change from +x to -x
 prev = plusx;

void fill(uint32_t color) {
 for(uint8_t i=0; i<15; i++) strip.setPixelColor(i, color);;

The result:

Does it work? No, not quite. Could it work? I suspect so. Biggest issues are lag (a delay from when a measurement is taken and when the Arduino reads it, which can be compensated for), and temporal aliasing (“time jaggies” resulting from the different interval at which the BNO055 performs its “sensor fusion” math — about 100 Hz — and the speed of rotation (which might not be an even multiple of that interval), and how often we’re polling the device.

In the video, notice the red and green flashes, which should indicate when the device crosses the horizon as it’s moving up or down. It’s not just that they’re off (lag), but that they drift (not seen in the short video: it doesn’t continually drift, but jumps back periodically). Our makeshift poi is spinning at about 2 Hz, meaning only 50 distinct samples per loop…rather coarse resolution.

So the bad news: no, there’s no quick and easy solution to the freestanding POV problem, yet.

The good news: with the BNO055 I suspect we’re halfway there, that a little more work could produce a steady image. Maybe not perfect, but good enough is my suspicion.

This was a few minutes’ kludge. One could surely do better…maybe using the gyro as a primary data source, and one of the other readouts as a sanity check, that our gyro-derived position estimate is getting ahead or behind? Maybe the gravity vector is a bad choice? There are countless things that could be done with a little more thought and testing.

Adafruit publishes a wide range of writing and video content, including interviews and reporting on the maker market and the wider technology world. Our standards page is intended as a guide to best practices that Adafruit uses, as well as an outline of the ethical standards Adafruit aspires to. While Adafruit is not an independent journalistic institution, Adafruit strives to be a fair, informative, and positive voice within the community – check it out here:

Join Adafruit on Mastodon

Adafruit is on Mastodon, join in!

Stop breadboarding and soldering – start making immediately! Adafruit’s Circuit Playground is jam-packed with LEDs, sensors, buttons, alligator clip pads and more. Build projects with Circuit Playground in a few minutes with the drag-and-drop MakeCode programming site, learn computer science using the CS Discoveries class on, jump into CircuitPython to learn Python and hardware together, TinyGO, or even use the Arduino IDE. Circuit Playground Express is the newest and best Circuit Playground board, with support for CircuitPython, MakeCode, and Arduino. It has a powerful processor, 10 NeoPixels, mini speaker, InfraRed receive and transmit, two buttons, a switch, 14 alligator clip pads, and lots of sensors: capacitive touch, IR proximity, temperature, light, motion and sound. A whole wide world of electronics and coding is waiting for you, and it fits in the palm of your hand.

Have an amazing project to share? The Electronics Show and Tell is every Wednesday at 7pm ET! To join, head over to YouTube and check out the show’s live chat – we’ll post the link there.

Join us every Wednesday night at 8pm ET for Ask an Engineer!

Join over 36,000+ makers on Adafruit’s Discord channels and be part of the community!

CircuitPython – The easiest way to program microcontrollers –

Maker Business — “Packaging” chips in the US

Wearables — Enclosures help fight body humidity in costumes

Electronics — Transformers: More than meets the eye!

Python for Microcontrollers — Python on Microcontrollers Newsletter: Silicon Labs introduces CircuitPython support, and more! #CircuitPython #Python #micropython @ThePSF @Raspberry_Pi

Adafruit IoT Monthly — Guardian Robot, Weather-wise Umbrella Stand, and more!

Microsoft MakeCode — MakeCode Thank You!

EYE on NPI — Maxim’s Himalaya uSLIC Step-Down Power Module #EyeOnNPI @maximintegrated @digikey

New Products – Adafruit Industries – Makers, hackers, artists, designers and engineers! — #NewProds 7/19/23 Feat. Adafruit Matrix Portal S3 CircuitPython Powered Internet Display!

Get the only spam-free daily newsletter about wearables, running a "maker business", electronic tips and more! Subscribe at !

1 Comment

  1. Hi,

    with a rotating "rope" like in the picture, there is still a fixed pivoting point in the hand, so a rotary encoder could be put there, that’s what I am experimenting with for version 2 of my JMP-rope

    However it is true indeed that a freestand solution is more general and would work better for poi balls and even for for something like a bo staff or a devilstick.

    Keep up the good work.

    Ciao ciao,

Sorry, the comment form is closed at this time.