Community project

POV Spinner Display

Photo of POV Spinner Display
Generated with AI

Alexander Taksikov

Published November 15, 2026

This POV (Persistence of Vision) spinner display creates animated images by rapidly illuminating LED strips on a rotating three-armed platform. As the arms spin, a Hall effect sensor detects a magnet to synchronize the LED patterns, creating the illusion of a complete image in mid-air. The project uses an Arduino Uno to control three WS2812B addressable LED strips, each with 15 pixels, generating smooth animations through precise timing.

Builders will receive a complete wiring diagram showing connections between the Arduino, three LED strips, the Hall effect sensor, and power supply, along with a full parts list and pre-written firmware. The guide includes step-by-step assembly instructions for preparing the LED strips, mounting the sensor and magnet for reliable detection, and calibrating the synchronization to ensure flicker-free POV effects.

Wiring diagram

Wiring diagram for POV Spinner Display
Loading diagram…

Gather all the parts

QtyComponent
1

WS2812B

15 pixels, 5 V

Addressable RGB LED strip

1

Resistor

330 Ω, 1/4 W

Through-hole resistor (current-limiting in series with an LED)

1

Electrolytic Capacitor

1000 µF, 10 V

Radial aluminium electrolytic capacitor bought by value and voltage rating, such as 10 µF, 100 µF or 1000 µF 10 V. It is polarised: the + lead goes to the higher voltage and the - lead, marked by the stripe on the sleeve, to the lower voltage or ground. It is mostly used for bulk filtering across a power rail, with a voltage rating comfortably above the rail.

1

USB power bank, regulated 5 V output

Enclosed rechargeable USB power bank with protected 5 V output rated at least 3 A. Use a low-current/always-on mode if available.

1

SS49E Linear Hall Effect Sensor

Ratiometric linear Hall-effect sensor IC in a TO-92 package. Outputs an analog voltage proportional to the applied magnetic flux density, centered around half the supply voltage at zero field.

1

Neodymium Disc Magnet 10 x 3 mm

Small cylindrical neodymium magnet used as a magnetic target for Hall-effect homing or position sensing. It has no electrical pins and should be mounted mechanically on the moving split-flap wheel or index point, close enough for the chosen Hall sensor to detect repeatably without striking the sensor.

1

WS2812B

15 pixels, 5 V

Addressable RGB LED strip

1

WS2812B

15 pixels, 5 V

Addressable RGB LED strip

1

Resistor

330 Ω, 1/4 W

Through-hole resistor (current-limiting in series with an LED)

1

Resistor

330 Ω, 1/4 W

Through-hole resistor (current-limiting in series with an LED)

Assemble it in 6 steps

1. Подготовьте ленту

Отрежьте три участка WS2812B по 15 светодиодов только по отмеченным линиям. Закрепите на трёх одинаковых жёстких лучах через 120 градусов. У всех лент DIN и первый светодиод расположены у оси, последний — на внешнем конце. Луч 1 подключается к D2, луч 2 к D3, луч 3 к D4; нумеруйте их по направлению вращения. Удалите прежние отдельные светодиоды и резисторы.

2. Подключите управление

D2 → резистор 330 Ом → DIN луча 1; D3 → отдельный резистор 330 Ом → DIN луча 2; D4 → отдельный резистор 330 Ом → DIN луча 3: это передача цвета. Резистор установите у DIN. DATA в схеме означает именно DIN, не DOUT. Стрелки на ленте направлены от входа к следующим светодиодам. GND всех трёх лент соедините с GND Nano.

3. Подключите питание 5 В

Используйте готовый закрытый USB-пауэрбанк с выходом 5 В не менее 3 А. Через USB-кабель с выведенными проводами питания соедините +5 В с контактом 5V Nano и +5V всех трёх лент отдельными парами проводов от центральной точки питания, минус с общей GND. Проверьте полярность и напряжение, не ориентируйтесь только на цвет проводов. Конденсатор 1000 мкФ 10 В подключите у ленты: плюс к +5V, минус с полосой к GND. При малой нагрузке включите режим постоянного питания, если он есть.

  • Старую батарею 9 В и её разъём полностью уберите. 9 В уничтожат ленту.
  • Перед подключением Nano к USB компьютера отключите оба провода питания пауэрбанка. Не соединяйте два источника питания одновременно.
  • Переполюсовка конденсатора может привести к его разрыву.

4. Надёжно закрепите детали

Закрепите Nano, три ленты и провода на вращающейся основе. Питание тоже должно вращаться с ними: закрепите пауэрбанк у оси и сбалансируйте конструкцию. Если безопасно закрепить его нельзя, не вращайте конструкцию. Изолируйте пайку, разгрузите провода у разъёмов. Не используйте свободную макетную плату. Во время движения всё питание и провода должны двигаться вместе с рукояткой.

  • Не вращайте с кабелем к компьютеру, рядом с людьми или с мотором. Не отпускайте рукоятку.

5. Установите датчик и магнит

Датчик SS49E закрепите на вращающейся основе рядом с осью. VCC → 3V3 Nano (питание), GND → GND (общий минус), OUT → A0 (измерение магнитного поля). Проверьте выводы по чертежу SS49E перед пайкой. Магнит закрепите на НЕВРАЩАЮЩЕЙСЯ части рукоятки: датчик должен проходить мимо него один раз за полный оборот, не касаясь. Начните с зазора 3–5 мм и подберите расстояние. Все провода остаются на вращающейся части. Если просто махать всей конструкцией вместе с магнитом, синхронизации не будет.

  • Надёжно закрепите магнит, чтобы он не вылетел; не допускайте контакта с датчиком. Держите магнит вдали от детей.

6. Проверьте синхронизацию

При включении держите магнит далеко от датчика примерно одну секунду: измеряется фон. Затем вращайте вручную относительно неподвижного магнита. После двух проходов появится один цветной шеврон на оборот. При остановке лента гаснет. Старайтесь вращать равномерно: скорость вычисляется по предыдущему обороту. Начинайте с 1–3 оборотов в секунду. Три луча рисуют один общий шеврон со сдвигом на 120 градусов. Для обратного вращения поменяйте местами подключения D3 и D4 при отключённом питании. Яркость ограничена в прошивке; 45 светодиодов на полном белом могут потребовать до 2,7 А без учёта Nano. Без безопасной оси и неподвижной рукоятки полные обороты не выполняйте. WS2812B не обеспечивают чёткое изображение при высокой скорости.

Review all connections

1. Connections between "data_resistor" and "Arduino"

Functiondata_resistorArduino
digitalP1GPIO 2
digitalP2 → WS2812B DATAEXT

2. Connections between "bank" and "Arduino"

FunctionbankArduino
powerVBUS5V
groundGNDGND

3. Connections between "strip" and "Arduino"

FunctionstripArduino
powerVCC → USB power bank, regulated 5 V output VBUSEXT
groundGND → USB power bank, regulated 5 V output GNDEXT

4. Connections between "bulk" and "Arduino"

FunctionbulkArduino
power+ → WS2812B VCCEXT
ground- → WS2812B GNDEXT

5. Connections between "hall" and "Arduino"

FunctionhallArduino
powerVCC3V3
groundGNDGND
analogOUTGPIO 14

6. Connections between "data_resistor2" and "Arduino"

Functiondata_resistor2Arduino
digitalP1GPIO 3
digitalP2 → WS2812B DATAEXT

7. Connections between "data_resistor3" and "Arduino"

Functiondata_resistor3Arduino
digitalP1GPIO 4
digitalP2 → WS2812B DATAEXT

8. Connections between "strip2" and "Arduino"

Functionstrip2Arduino
powerVCC → USB power bank, regulated 5 V output VBUSEXT
groundGND → USB power bank, regulated 5 V output GNDEXT

9. Connections between "strip3" and "Arduino"

Functionstrip3Arduino
powerVCC → USB power bank, regulated 5 V output VBUSEXT
groundGND → USB power bank, regulated 5 V output GNDEXT

Deploy the firmware

#include <Arduino.h>
#include <Adafruit_NeoPixel.h>
constexpr uint8_t PIXELS = 15, COLUMNS = 96;
Adafruit_NeoPixel strip(PIXELS, 2, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel strip2(PIXELS, 3, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel strip3(PIXELS, 4, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel* arms[] = {&strip, &strip2, &strip3};
uint16_t previous[3] = {0xffff,0xffff,0xffff};
int baseline;
bool nearMagnet = false, haveIndex = false;
uint32_t lastIndex = 0, period = 0;
uint16_t pattern(uint8_t column) {
  if (column > 14) return 0;
  uint8_t radius = column <= 7 ? column : 14-column;
  return (uint16_t(1) << radius) | (uint16_t(1) << (14-radius));
}
void showColumn(uint8_t arm, uint16_t bits) {
  if (bits == previous[arm]) return;
  previous[arm] = bits;
  for (uint8_t i=0; i<PIXELS; ++i) {
    if (bits & (uint16_t(1) << i))
      arms[arm]->setPixelColor(i, uint8_t(42-i*2), uint8_t(i*3), uint8_t(18));
    else arms[arm]->setPixelColor(i, 0);
  }
  arms[arm]->show();
}
void setup() {
  for (uint8_t arm=0; arm<3; ++arm) { arms[arm]->begin(); showColumn(arm,0); }
  delay(500);
  long total = 0;
  for (uint8_t i=0; i<64; ++i) { total += analogRead(A0); delay(4); }
  baseline = total/64;
}
void loop() {
  int distance = abs(analogRead(A0)-baseline);
  uint32_t now = micros();
  if (!nearMagnet && distance >= 60) {
    nearMagnet = true;
    uint32_t elapsed = now-lastIndex;
    if (!haveIndex || elapsed >= 80000UL) {
      period = haveIndex && elapsed <= 2000000UL ? elapsed : 0;
      lastIndex = now;
      haveIndex = true;
    }
  } else if (nearMagnet && distance <= 30) nearMagnet = false;
  for (uint8_t arm=0; arm<3; ++arm) {
    // Re-read time because each arm transmission takes about 0.45 ms.
    uint32_t phase = micros()-lastIndex;
    if (!period || phase >= period || phase > 2000000UL) showColumn(arm,0);
    else {
      uint8_t column = (phase*COLUMNS)/period;
      // Arms numbered in the direction of rotation, at 0, 120, 240 degrees.
      column = (column + arm*32) % COLUMNS;
      showColumn(arm,pattern(column));
    }
  }
}

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace, diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik