Policyjny stroboskop DIY # 1 na Arduino i Digispark
Kilka razy, widząc jadący radiowóz, zastanawiałem się (tak wiem, dziwne) jak wygląda dokładnie sekwencja i przebiegi czasowe migania niebieskiej i czerwonej części stroboskopu policyjnego. Myślałem nawet, żeby sfilmować błyskające lampy, a później otworzyć film klatka po klatce i wyliczyć czasy przerwy i świecenia. Na szczęście są prostsze metody, by uzyskać podobny lub nawet lepszy efekt mniejszym nakładem pracy.
Jak zbudować sterownik policyjnego stroboskopu?
Bez problemu można kupić zestaw do samodzielnego montażu, imitujący policyjny stroboskop. Często jest to generator przebiegu prostokątnego o regulowanej częstotliwości, w rytm którego lampy zapalają się i gasną oraz jakiś układ logiczny (np. licznik 4017) odpowiadający za sekwencję typu 3 mignięcia czerwone, 3 mignięcia niebieskie.
Chciałem trochę bardziej poeksperymentować i od razu przyszedł mi do głowy pomysł wykorzystania mikrokontrolera. Szukając w pudełku jakiejś płytki testowej, wpadł mi w ręce nieco zapomniany, opisywany przeze mnie kilka lat temu Digispark z ATtiny85 na pokładzie. Co prawda ta platforma została porzucona przez Digistump, ale nadal jest wspierana w Arduino IDE. Mała płytka do takiego projektu nada się idealnie – programowanie wprost z USB, wyprowadzone kilka pinów i zasilanie. Wziąłem jeszcze na wszelki wypadek klon Arduino NANO, który kupiłem kiedyś za skandalicznie małe pieniądze.
Druga sprawa to kod. Napisałem coś na szybko na wzór układu na bazie licznika 4017, ale przecież mamy dzisiaj do wyboru kilka modeli AI, dla których wygenerowanie kodu pod konkretną platformę sprzętową trwa kilka sekund. Aż grzech nie skorzystać.
Różne wersje stroboskopu policyjnego na Digispark
Poprosiłem o kod na Digisparka realizujący efekt stroboskopu policyjnego. Pytanie powtarzałem dla kilku modeli AI i otrzymałem naprawdę fajne efekty. Przy okazji dostałem kod emulujący efekt migania w amerykańskich radiowozach, oraz opis, jak krok po kroku wgrać firmware do mikrokontrolera. Dla początkujących super sprawa.
W ogóle cały projekt robiłem na cienkim kliencie HP T630 z Linuksem Mint na pokładzie, który opisywałem tutaj. Prace zacząłem od pobrania Arduino IDE 2.3.10. Krok drugi to zapewnienie obsługi płytek Digispark. W środowisku Arduino trzeba kliknąć File -> Preferences i w Additional boards manager URLs trzeba wkleić link:
https://raw.githubusercontent.com/ArminJo/DigistumpArduino/master/package_digistump_index.json
Po zatwierdzeniu przechodzimy do managera płytek (druga ikona w pionowym pasku w lewej części okna) i w wyszukiwarkę wpisujemy digispark. Powinno nam wyświetlić Digistump AVR Boards w wersji 1.7.5, którą trzeba zainstalować.
Teraz można pisać, albo wkleić 😉 kod. Europejska wersja stroboskopu policyjnego prezentuje się następująco:
// ============================================================
// Światła policyjne EU dla Digispark (ATtiny85)
// Kanał A: pin P0 (niebieski)
// Kanał B: pin P1 (czerwony / wbudowana LED)
// Bezpieczne piny: P0, P1, P2 (P3/P4 = USB, nie używaj!)
// ============================================================
const byte CH_A = 0; // niebieski
const byte CH_B = 1; // czerwony
// ---- Strojenie czasów (ms) ----------------------------------
const int T_BLYSK_ON = 60; // czas świecenia jednego błysku
const int T_BLYSK_OFF = 55; // czas przerwy między błyskami
const int T_PAUZA = 90; // przerwa po serii przed zmianą
const int T_ZMIANA = 20; // mikroprzerwa przy zmianie strony
const byte BLYSKI = 3; // liczba błysków na stronę
// ------------------------------------------------------------
// Funkcja: seria błysków na jednym kanale
void seria(byte pin, byte ile) {
for (byte i = 0; i < ile; i++) {
digitalWrite(pin, LOW);
delay(T_BLYSK_ON);
digitalWrite(pin, HIGH);
// skrócona przerwa po ostatnim błysku — lepszy rytm
delay((i < ile - 1) ? T_BLYSK_OFF : T_ZMIANA);
}
}
void setup() {
pinMode(CH_A, OUTPUT);
pinMode(CH_B, OUTPUT);
digitalWrite(CH_A, HIGH);
digitalWrite(CH_B, HIGH);
}
void loop() {
// --- Faza 1: niebieski 3x, czerwony 3x (naprzemiennie)
seria(CH_A, BLYSKI);
delay(T_PAUZA);
seria(CH_B, BLYSKI);
delay(T_PAUZA);
// --- Faza 2: podwójne szybkie błyski obydwu stron
for (byte i = 0; i < 2; i++) {
seria(CH_A, 2);
delay(T_PAUZA / 2);
seria(CH_B, 2);
delay(T_PAUZA / 2);
}
// --- Krótka przerwa przed powtórzeniem cyklu
delay(T_PAUZA * 2);
}
W środowisku Arduino „ptaszek” kompiluje, a „->” wgrywa firmware do płytki. Digispark działa tak, że po wsadzeniu do portu USB (podaniu zasilania) przez około 5 sekund bootloader czeka na programowanie, później przechodzi już do wykonania kodu i flashowanie nie jest możliwe. Z kolei w Arduino IDE po wciśnięciu strzałki mamy 60 sekund na podłączenie Digisparka do portu USB. Zrzut z programowania:
Dodatkowy hardware to niebieska i czerwona dioda LED, dwa rezystory 220-470Ω ograniczające prąd. LEDy są zapalane niskim stanem logicznym: niebieska z portu P0, czerwona z portu P1. Anody diod są połączone przez rezystory do plusa zasilania o napięciu 5V (pobierane z płytki Digispark). Działa to tak:
Druga wersja, według AI bardziej agresywna, to amerykańska odmiana stroboskopu. Kod jest nieco dłuższy:
// ============================================================
// Światła policyjne USA dla Digispark (ATtiny85)
// Wzorzec: wig-wag (naprzemienny) + faza split (razem)
//
// CH_A = P0 = niebieski (lewa strona pojazdu)
// CH_B = P1 = czerwony (prawa strona pojazdu)
// Nie używaj P3/P4 — to linie USB D+/D−!
// ============================================================
const byte CH_A = 0; // niebieski
const byte CH_B = 1; // czerwony
// ---- Strojenie czasów (ms) ----------------------------------
const int WW_ON = 80; // czas świecenia w wig-wag
const int WW_OFF = 15; // martwa przerwa między zmianami
const int SP_ON = 55; // czas błysku w trybie split
const int SP_OFF = 50; // przerwa w trybie split
const int SP_GAP = 120; // przerwa między parami w splicie
// ------------------------------------------------------------
// --- Pojedynczy błysk ----------------------------------------
void blysk(byte pin, int tOn, int tOff) {
digitalWrite(pin, LOW);
delay(tOn);
digitalWrite(pin, HIGH);
delay(tOff);
}
// --- WIG-WAG: szybkie naprzemienne, bez przerwy --------------
// (klasyczny wzorzec US — jeden kanał wyłącza się,
// drugi zaraz się włącza — "prąd przemienia się")
void wigwag(byte ile) {
for (byte i = 0; i < ile; i++) {
digitalWrite(CH_A, LOW);
digitalWrite(CH_B, HIGH);
delay(WW_ON);
digitalWrite(CH_A, HIGH);
delay(WW_OFF);
digitalWrite(CH_B, LOW);
digitalWrite(CH_A, HIGH);
delay(WW_ON);
digitalWrite(CH_B, HIGH);
delay(WW_OFF);
}
}
// --- SPLIT: seria błysków każdej strony osobno (po 2) --------
void splitPhase(byte cykle) {
for (byte i = 0; i < cykle; i++) {
// 2x lewy
blysk(CH_A, SP_ON, SP_OFF);
blysk(CH_A, SP_ON, SP_GAP);
// 2x prawy
blysk(CH_B, SP_ON, SP_OFF);
blysk(CH_B, SP_ON, SP_GAP);
}
}
// --- SIMUL: oba razem — krótka seria synchroniczna -----------
void simul(byte ile) {
for (byte i = 0; i < ile; i++) {
digitalWrite(CH_A, LOW);
digitalWrite(CH_B, LOW);
delay(SP_ON);
digitalWrite(CH_A, HIGH);
digitalWrite(CH_B, HIGH);
delay(SP_OFF);
}
}
void setup() {
pinMode(CH_A, OUTPUT);
pinMode(CH_B, OUTPUT);
digitalWrite(CH_A, HIGH);
digitalWrite(CH_B, HIGH);
}
void loop() {
// Faza 1: wig-wag — 6 cykli naprzemiennych (główny wzorzec USA)
wigwag(6);
// Faza 2: split — 2 cykle po 2 błyski każdej strony
splitPhase(2);
// Faza 3: krótkie "attention burst" — oba jednocześnie 3x
simul(3);
// Faza 4: powrót do wig-wag — 4 cykle przed pełnym powtórzeniem
wigwag(4);
}
I efekt wideo:
Policyjne stroboskopy na Arduino
Kod na Digisparka jest bardzo prosty i po niewielkich przeróbkach da się odtworzyć te efekty na Arduino UNO czy NANO. Poniżej daję gotowca, sprawdzonego w boju.
Wersja EU i USA scalona. Jeśli pin D6 zostanie podłączony do masy, to wykonywana jest wersja EU. W innym przypadku (pin wisi w powietrzu lub jest podłączony do plusa) układ miga według kombinacji USA. Diody są podłączone katodami do pinów – niebieska do D12 a czerwona do D2 – tak mi pasowało na płytce stykowej.
// ============================================================
// Policyjne światła LED - Arduino
// Niebieska LED: katoda do D12
// Czerwona LED: katoda do D2
// Wybór trybu:
// D6 = LOW -> EU
// D6 = HIGH -> USA (pin "wisi w powietrzu" dzięki INPUT_PULLUP)
// ============================================================
const byte LED_BLUE = 12; // niebieska
const byte LED_RED = 2; // czerwona
const byte MODE_PIN = 6; // wybór trybu
// LED-y są aktywne stanem niskim:
// katoda LED do pinu, anoda przez rezystor do +5V
void ledOn(byte pin) {
digitalWrite(pin, LOW);
}
void ledOff(byte pin) {
digitalWrite(pin, HIGH);
}
// ===================== EU =====================
const int EU_T_BLYSK_ON = 60;
const int EU_T_BLYSK_OFF = 55;
const int EU_T_PAUZA = 90;
const int EU_T_ZMIANA = 20;
const byte EU_BLYSKI = 3;
void euSeria(byte pin, byte ile) {
for (byte i = 0; i < ile; i++) {
ledOn(pin);
delay(EU_T_BLYSK_ON);
ledOff(pin);
delay((i < ile - 1) ? EU_T_BLYSK_OFF : EU_T_ZMIANA);
}
}
void runEU() {
euSeria(LED_BLUE, EU_BLYSKI);
delay(EU_T_PAUZA);
euSeria(LED_RED, EU_BLYSKI);
delay(EU_T_PAUZA);
for (byte i = 0; i < 2; i++) {
euSeria(LED_BLUE, 2);
delay(EU_T_PAUZA / 2);
euSeria(LED_RED, 2);
delay(EU_T_PAUZA / 2);
}
delay(EU_T_PAUZA * 2);
}
// ===================== USA =====================
const int US_WW_ON = 80;
const int US_WW_OFF = 15;
const int US_SP_ON = 55;
const int US_SP_OFF = 50;
const int US_SP_GAP = 120;
void usBlysk(byte pin, int tOn, int tOff) {
ledOn(pin);
delay(tOn);
ledOff(pin);
delay(tOff);
}
void usWigwag(byte ile) {
for (byte i = 0; i < ile; i++) {
ledOn(LED_BLUE);
ledOff(LED_RED);
delay(US_WW_ON);
ledOff(LED_BLUE);
delay(US_WW_OFF);
ledOn(LED_RED);
ledOff(LED_BLUE);
delay(US_WW_ON);
ledOff(LED_RED);
delay(US_WW_OFF);
}
}
void usSplitPhase(byte cykle) {
for (byte i = 0; i < cykle; i++) {
usBlysk(LED_BLUE, US_SP_ON, US_SP_OFF);
usBlysk(LED_BLUE, US_SP_ON, US_SP_GAP);
usBlysk(LED_RED, US_SP_ON, US_SP_OFF);
usBlysk(LED_RED, US_SP_ON, US_SP_GAP);
}
}
void usSimul(byte ile) {
for (byte i = 0; i < ile; i++) {
ledOn(LED_BLUE);
ledOn(LED_RED);
delay(US_SP_ON);
ledOff(LED_BLUE);
ledOff(LED_RED);
delay(US_SP_OFF);
}
}
void runUSA() {
usWigwag(6);
usSplitPhase(2);
usSimul(3);
usWigwag(4);
}
// ===================== SETUP / LOOP =====================
void setup() {
pinMode(LED_BLUE, OUTPUT);
pinMode(LED_RED, OUTPUT);
pinMode(MODE_PIN, INPUT_PULLUP);
ledOff(LED_BLUE);
ledOff(LED_RED);
}
void loop() {
if (digitalRead(MODE_PIN) == LOW) {
runEU();
} else {
runUSA();
}
}
AI ładnie zrobiło komentarze w kodzie, także zasada działania dla osób obeznanych z programowaniem nie będzie stanowiła tajemnicy.
Podsumowanie
Zrealizowałem kolejny projekt, który miałem gdzieś tam z tyłu głowy. Jak widać, ciężko wyrosnąć z elektronicznych błyskotek :). Oczywiście ten projekt to tylko zarys, bez stopnia końcowego zasilające większą ilość diod LED dużej mocy. Taki układ typowo realizuje się na tranzystorach bipolarnych lub lepiej – mosfet. Można też dodać nowe funkcje, np. miganie tylko lampy (jednej strony stroboskopu). Temat na tyle mi się spodobał, że spróbuję odpalić go na jeszcze innych mikrokontrolerach, w tym MCS-51 czy Nuvoton.






[…] ogóle cały projekt robiłem na cienkim kliencie HP T630 z Linuksem Mint na pokładzie, który opisywałem tutaj. Prace zacząłem…
[…] Szukając w pudełku jakiejś płytki testowej, wpadł mi w ręce nieco zapomniany, opisywany przeze mnie kilka lat temu Digispark…
Windows 11 24H2, poszło bez problemu.
Na jakim windowskie przygotowywałeś usb do aktualizacji biosa? Bo jak walczyłem z rok temu z aktualizacją T730 to na W11…
[…] Terminal, inaczej cienki klient (z ang. thin client) HP T630 to mały komputer o niewielkiej mocy obliczeniowej, który ma…