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

Gather all the parts
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"
2. Connections between "bank" and "Arduino"
3. Connections between "strip" and "Arduino"
4. Connections between "bulk" and "Arduino"
5. Connections between "hall" and "Arduino"
6. Connections between "data_resistor2" and "Arduino"
7. Connections between "data_resistor3" and "Arduino"
8. Connections between "strip2" and "Arduino"
9. Connections between "strip3" and "Arduino"
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.




