Introduction
Nous avons réalisé une horloge "Persistance Of Vision": un support rectangulaire horizontal, fixé sur la partie tournante d'un ventilateur de PC, 12V (dont les pales ont été ôtées), axe vertical . A l'extrémité du support sont fixées 12 petites Leds CMS blanches verticales chacune électriquement reliée à travers une résistance de 56 Ω aux 12 sorties d'un Arduino Nano (D2 à D13) fixé à plat sur le support.
L'ensemble tourne et, en envoyant des impulsions correctes dans les Leds, on peut afficher 3 fois par tour l'heure sous la forme 12:34:56 en faisant clignoter les ":" chaque seconde.
Une fourche optique montée sur le support tournant et une petite équerre en aluminium collée sur la partie fixe, permettent de synchroniser la position de l'heure (indispensable) :
Si le support vertical qui maintient les 12 Leds est petit, en rotation, il est quasi invisible : on voit donc "flotter" l'heure dans l'air et l'effet est relativement spectaculaire.
Il faut évidemment alimenter l'Arduino. Pour cela, plusieurs solutions :
Des joints avec frotteurs sur des bagues : nous avons essayé avec 2 entretoises laiton dont une isolée montée sur une vis collée sur l'axe du moteur. Problèmes :
Durée de vie : au bout de quelques jours, les bagues sont déjà creusées par les frotteurs
Couple : les frottements des frotteurs ralentissent le moteur
Démontage plus compliqué
Bruit : un "couinement" permanent faible mais agaçant
2 bobines, une mobile et une fixe, proches, concentriques et un générateur "HF" et un récepteur. A noter que l'on trouve des ensembles tout fait à environ 3.5€ : Aliexpress : Un émetteur alimenté en 12V avec sa bobine et le récepteur avec sa bobine qui fournit du 5V 1A : (1A : à voir... mais probablement suffisamment pour alimenter la partie tournante de l'horloge)
Avantages du joint à inductance :
Durée de vie "infinie"
Aucun bruit
Démontage du support rectangulaire plus facile
Aucun couple sur le moteur du ventilateur. Ce qui permet de le faire tourner plus vite ou, de diminuer la tension d'alimentation pour augmenter sa durée de vie (le moteur du ventilateur tourne en permanence)
Le code : nous avons demandé à ChatGPT5 de nous générer le code : (345 lignes en 5 mn !)
Prompt :
écris le code C Arduino nano pour une horloge "pov" : 12 Leds verticales reliées avec chacune une résistance de 56 ohms sur les 12 sorties D2 à D13; montées sur un ventilateur de PC 12V qui tourne à 600 RPM. l'horloge doit afficher 3 fois l'heure (24h) avec le format : 13:24:56. Les : clignotent une fois par seconde. Il n'y a pas de réglages. L'horloge démarre à 12h. chaque chiffre est codé par 8x12 points.
Résultat : (nous avons retouché le code de l'IA pour rajouter la synchronisation, optimiser la forme des caractères et simplifier le code) :
#define VERSION 1.1
#define DATE "07/11/2025"
/*
POV Clock 12 LEDs (D2..D13)
VERSION avec des délais fixes en us
synchro 3 blocs, horloge avec fonction millis
- 12 LED verticales, résistances 330 Ω, anodes sur D2..D13, cathodes au GND
- D2 en bas, D13 en haut
- Affiche 3 fois "HH:MM:SS" par tour
- Format chiffres 8x12 (8 colonnes, 12 lignes/LED)
- Deux-points clignotants chaque seconde
- Démarrage à 12:00:00 sans réglages
- Synchronisation temporelle basée sur 100 ms par révolution
- Microcontrôleur: Arduino Nano (ATmega328P)
Perplexity GPT5
ETAT : Téléversé : OUI **********************************
correction quartz Arduino 993 au lieu de 1000 pour ms->s à ajuster sur 24h...
1 Fichier, Enregistrer
2 Fichier, Préférences, Paramètres, Afficher les résultats détaillés pendant, Téléversement, OK
3 Croquis, Vérifier, Compiler : pas d'erreurs
4 Brancher cordon USB C entre PC et Nano
5 Outils, type de carte Arduino Nano
6 Outils, Processeur, Atmega 328P (Old Bootloader) (sinon erreur avrdude...)
7 Outils, Port, COM4
8 Croquis, Téléverser
9 Affiche Téléversement....
*/
#include <Arduino.h>
// Mapping des 12 LED: index 0..11 correspond aux sorties D2..D13
const uint8_t LED_PINS[12] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
unsigned long timer = 0; //compter les s avec millis pour l'horloge
// Dimensions des glyphes
const uint8_t DIGIT_COLS = 8; // 8 colonnes par chiffre
const uint8_t COLS_COLON = 2; // 2 colonnes pour ":"
const uint16_t pixel=20; //durée pixel en us
// Mise en page "HH:MM:SS"
struct Symbol
{
bool isColon;
uint8_t digit; // 0..9 si isColon == false
};
// Police 8x12 pour chiffres 0..9, bit 0 = LED rang 0 (en bas si vous câblez ainsi), bit 11 = LED rang 11 (haut)
//
// Chaque entrée est un tableau de 8 colonnes, chaque colonne est un uint16_t dont seuls 12 bits (0..11) sont utilisés.
// orientation suivant montage: si la première LED en bas doit être bit 11, inverser les bits lors de l’affichage.
const uint16_t font8x12[10][8] =
{
// 0
{ //Les chiffres sont tournés de 90° et inversés en miroir
0b001111111100,
0b011111111110,
0b110000000011,
0b110000000011,
0b110000000011,
0b110000000011,
0b011111111110,
0b001111111100
},
// 1
{
0b000000000000,
0b011000000011,
0b110000000011,
0b111111111111,
0b111111111111,
0b000000000011,
0b000000000000,
0b000000000000
},
// 2
{
0b000000001111,
0b011100001111,
0b111100011011,
0b110000110011,
0b110001100011,
0b110011000011,
0b111110000011,
0b011100000011
},
// 3
{
0b011000001100,
0b110000000110,
0b110000000011,
0b110011000011,
0b110011000011,
0b110011000011,
0b111111111111,
0b011100011100
},
// 4
{
0b000001111000,
0b000111111000,
0b001100011000,
0b011000011000,
0b110000011000,
0b111111111111,
0b011111111111,
0b000000011000,
},
// 5
{
0b111111000110,
0b111111000011,
0b110011000011,
0b110011000011,
0b110011000011,
0b110001100011,
0b110000111111,
0b110000001110
},
// 6
{
0b011111111110,
0b111111111111,
0b110111000011,
0b110011000011,
0b110011000011,
0b110011000011,
0b110011000011,
0b011001111110
},
// 7
{
0b110000000000,
0b110000000000,
0b110000000000,
0b110000000011,
0b110000001110,
0b110001111000,
0b111111000000,
0b111100000000
},
// 8
{
0b011110111110,
0b111111111111,
0b110001100011,
0b110001100011,
0b110001100011,
0b110001100011,
0b111111111111,
0b011110111110
},
// 9
{
0b011111000110,
0b111111100011,
0b110001100011,
0b110001100011,
0b110001100011,
0b110001100011,
0b111111111111,
0b011111111110
}
};
// Deux-points ":" en 12 rangées sur 2 colonnes (points vers rangs ~4 et ~7)
// Colonnes identiques pour simplifier, actives si blinkColon == true
const uint16_t colonCol = (1 << 4) | (1 << 5) | (1 << 7) | (1 << 8); // 2 points un peu épais (2 rangs chacun)
volatile uint32_t lastMicros = 0;
// Temps logiciel
uint8_t sec = 0, min_ = 0, hour = 12; //12:00:00 à la mise sous tension ***************************
// Allume les LED selon un masque 12 bits
inline void showColumn(uint16_t mask12)
{
for (uint8_t i = 0; i < 12; i++) digitalWrite(LED_PINS[i], (mask12 & (1 << i)) ? LOW : HIGH);//LOW pour allumer les Leds, HIGH pour les éteindre
}
// Éteint toutes les LED
inline void clearAll()
{
for (uint8_t i = 0; i < 12; i++) digitalWrite(LED_PINS[i], HIGH); //LOW pour allumer les Leds, HIGH pour les éteindre
}
// Affiche une colonne d’un chiffre donné (0..9), index de colonne 0..7
inline void showDigitCol(uint8_t digit, uint8_t col)
{
uint16_t colMask = font8x12[digit][col];
showColumn(colMask);
}
// Affiche une colonne du ":" (2 colonnes identiques)
inline void showColonCol(bool blinkOn)
{
showColumn(blinkOn ? colonCol : 0);
}
// Convertit chiffre décimal 0..59 -> dizaines et unités
inline void split2(uint8_t v, uint8_t &tens, uint8_t &ones)
{
tens = v / 10;
ones = v % 10;
}
// Un bloc = "HH:MM:SS"
void displayBlock(bool blinkColon)
{
// Répartition du temps: 6 symboles chiffres (8 colonnes chacun) + 2 colonnes ":" deux fois
// On affiche dans l’ordre: H1 H2 : M1 M2 : S1 S2
uint8_t hT, hO, mT, mO, sT, sO; //heures minutes secondes
split2(hour, hT, hO); //élabore les chiffres à partir des nombres
split2(min_, mT, mO);
split2(sec, sT, sO);
// Définir séquence des symboles
Symbol seq[8] =
{
{false, hT}, {false, hO}, {true, 0}, {false, mT},
{false, mO}, {true, 0}, {false, sT}, {false, sO}
};
// Nombre de colonnes totales: 6*8 + 2*2 = 52 colonnes
const uint16_t totalCols = 52;
// Durée par colonne dans la fenêtre
for (uint8_t si = 0; si < 8; si++)
{
if (seq[si].isColon)
{
for (uint8_t c = 0; c < COLS_COLON; c++)
{
showColonCol(blinkColon); //affiche la colonne
// attente jusqu’au prochain slot
delayMicroseconds(pixel);
clearAll(); //éteint toutes les Leds
}
}
else
{
// chiffre
for (uint8_t c = 0; c < DIGIT_COLS; c++)
{
showDigitCol(seq[si].digit, c);
delayMicroseconds(pixel);
clearAll();
}
}
delayMicroseconds(800);//blanc entre les chiffres **************************************
}
}
// Avance l’horloge logicielle de 1 seconde
inline void tickOneSecond()
{
sec++; //incrément secondes
if (sec >= 60)
{
sec = 0;
min_++; //incrément minutes
if (min_ >= 60)
{
min_ = 0;
hour++; //incrément heures
//sec+=12; //compensation retard horloge 12s/h=120s/10h=2mn/10h ************************************************
if (hour >= 24) hour = 0;
}
}
}
void setup()
{
pinMode(A0, INPUT_PULLUP); //synchro ILS
for (uint8_t i = 0; i < 12; i++)
{
pinMode(LED_PINS[i], OUTPUT);
digitalWrite(LED_PINS[i], HIGH);//éteint tout
}
for (uint8_t i = 0; i < 12; i++)
{
digitalWrite(LED_PINS[i], LOW);//allume une à la fois en montant TEST
delay(100);
digitalWrite(LED_PINS[i], HIGH);//éteint
}
for (uint8_t i = 0; i < 12; i++)
{
pinMode(LED_PINS[i], OUTPUT);
digitalWrite(LED_PINS[i], LOW);//allume tout TEST
}
delay(200);
// Démarre à 12:00:00
hour = 12; min_ = 0; sec = 0;
lastMicros = micros();
}
void loop()
{
bool blinkColon = (sec % 2 == 0); // clignote chaque seconde (visible sur toutes les répétitions de cette seconde)
uint32_t i;
for(i=0;i<100000;i++) //attend la synchro maxi suffisamment grand
{//on attend la synchro puis 2 ou 3 blocs puis on attend de nouveau la synchro
//pour éviter de rater la synchro pendant un bloc, il faut que le moteur ait une vitesse minimum...
if (digitalRead(A0) == LOW) break;//synchro on arrête d'attendre et on sort de la boucle
delayMicroseconds(10); //durée faible pour un faible jitter
}
displayBlock(blinkColon); //B1
delay(7); //intervalle entre blocs
displayBlock(blinkColon); //B2
//seulement 2 blocs si moteur lent (frottement des balais)
delay(7);
displayBlock(blinkColon); //B3 si moteur assez rapide
if (millis() - timer >= 993) //remplacer 1000 par 994 pour compenser le retard quartz Arduino
{
timer += 993; //le timer (long) "suit" millis()
tickOneSecond(); //avance d'une seconde l'horloge
}
}
Réalisation :
Il faut commencer par récupérer un ventilateur de PC 12V silencieux et couper les pales.
Nota : les ventilateurs à roulements à billes ont une plus grande durée de vie que les ventilateurs à paliers lisses mais ils sont souvent un peu plus bruyants. De même les ventilateurs lents et peu puissants ont des durées de vie souvent plus élevées. (l'horloge tourne en permanence...)
Il faut ensuite réaliser un support rectangulaire d'environ 25x100 mm (ici, en époxy). A une extrémité est fixé l'Arduino (prise USB-C vers l'extérieur) et à l'autre le petit support vertical (le plus petit possible) qui maintient les 12 Leds. Quelque part (voir plus loin), le support rectangulaire est percé d'un trou Ø3 pour le fixer (équilibré) sur le centre du ventilateur.
Une partie du matériel nécessaire :
1 Ventilateur 12V
2 Circuits imprimés avec trous pour le support et le support Leds
Des Leds rouges qui vont être remplacées par des Leds CMS blanches.
Attention : Les Leds rouges ont une tension plus faibles que les blanches. Avec ces dernières, il faut des résistances de plus faibles valeurs : 56 Ω. Il faut prendre des Leds avec un grand angle d'ouverture 120°. Si l'angle d'ouverture est faible : ex : 30°, en face de l'horloge, les minutes brillent plus que les heures et les secondes...
1 Arduino Nano USB-C
12 Résistances de 56 Ω
Remarques :
Il faut que les Leds brillent suffisamment fort pour, qu'avec la persistance rétinienne, l'intensité de l'affichage soit correcte (Dans notre horloge, la luminosité des Leds rouge était insuffisante dans une pièce lumineuse : nous les avons remplacées par des blanches).
Pour télécharger l'Arduino, il faut, évidemment que l'horloge ne tourne pas, ce qui peut être un problème si on veut ajuster le logiciel.
Il est possible de fabriquer le support complet avec l'Arduino, les condensateurs, les résistances, le support de Leds et les Leds, la fourche optique, le récepteur de bobine ou AVANT de percer un trou pour le centrer sur l'axe du ventilateur, pour qu'il soit bien équilibré...
L'ensemble, devrait consommer un peu plus de 200 mA en 12V (avec les joints à bobines). Les Leds ont un courant max ~12mA.
Il faut éviter de toucher l'affichage pendant que l'horloge tourne. Sinon il faut prévoir une protection transparente... C'est aussi une des raisons pour faire tourner le support "le moins vite possible"
Le ventilateur est monté dans une coupelle en plastique noir (pour les pots de fleurs) lestée avec du plâtre pour améliorer la stabilité
Si on installait les Leds en arc de cercle, on obtiendrait un affichage sphérique...
Le ventilateur sans ses pales : nous avons creusé un petit trou au centre duquel est collé, à l'Araldite, à 50°C, la tête d'une vis M3x20 qui recevra le support rectangulaire (fixé avec un écrou et une rondelle à dents).
On peut réaliser un support provisoire déséquilibré avec une Led (ici, une Led rouge) et une résistance pour tester la luminosité.
Sur cette photo avec le support provisoire ci-dessus, c'est le temps d'ouverture de l'appareil photo qui donne l'impression que la Led n'est pas allumée sur tout le cercle.
Réalisation du support rectangulaire (en époxy). Après avoir installé l'Arduino, les condensateurs de découplage, les Leds et leur support, nous avons vérifié l'équilibre du support (en le posant sur la pointe dune épingle pointée vers le haut) à partir du trou et soudé les résistances de 56 Ω du côté Arduino pour équilibrer au mieux. Puis nous avons ajusté l'équilibre en ajoutant un fil de cuivre avec isolant.
L'Arduino est fixé par 4 fils de cuivre au support rectangulaire
La fourche optique montée sur le côté de l'Arduino, l'équerre en aluminium, collée, est visible à droite
Les 12 résistances de 56 Ω sont soudées directement sur l'Arduino
La prise Jack fixée à l'intérieur du ventilateur
L'USB-C est, évidemment, du côté extérieur
3 condensateurs de découplage (220uF, 25V sur l'alimentation Leds et 330uF, 6.3V sur le 5V)
Les 12 Leds blanches CMS (récupérées d'une ampoule Leds 230V HS) sont directement soudées, côté positif sur un fil de cuivre rigide et de l'autre à 13 fils fins colorés. Les Leds sont collés entre elles.
L'ensemble des 12 Leds est collé (Araldite) au support rectangulaire
La LDR entourée d'une gaine thermorétractable noire et d'adhésif aluminium pour limiter l'entrée de lumière
Avec la transmission de l'électricité avec le joint tournant à bobines Aliexpress, la position de la carte récepteur (la petite), fixée sous le support rectangulaire. La bobine de réception est collée sous le support rectangulaire tournant et la bobine d'émission est collée sur le dessus de la coupelle plastique. La distance recommandée entre les 2 bobines est de 3 à 6 mm.
Pour que la précision de l'horloge ne dépende pas du déclenchement de la synchro qui dépend de la vitesse du moteur (variable), il faut utiliser la fonction Millis() Arduino.
Le POV est difficile à photographier.... (En réalité, l'heure est affichée 3 fois par tour).
Photos sans la coupelle en plastique noir :
A noter que la dérive de fréquence d'un Arduino est de l'ordre de 1 à 2s par jour. Pour faire mieux, il faut ajouter un module RTC (DS3221). Il faut alors prévoir des boutons pour régler l'heure (module arrêté : en aveugle...). On trouve à bas prix certain de ces modules avec une batterie de sauvegarde en cas de coupure secteur. Leur stabilité est de l'ordre de 60s/an. On peut corriger la vitesse d'horloge par logiciel...
Nota : une solution de dépannage pour que l'horloge soit à l'heure : la faire démarrer à 00h00 et la relier à un programmateur qui s'éteint tous les jours de 23h59 à 00h00... Ça permet aussi à l'horloge de rester à l'heure en cas de coupure de courant...
EN OPTION : Luminosité variable :
Réalisation :
Insérer une résistance de 4.7kΩ entre le commun des Leds et le +5V (définit la luminosité minimum de l'horloge)
Relier un transistor PNP BC327 ou équivalent :
Émetteur sur +5V
Collecteur sur le commun des Leds
Base reliée à une LDR
L'autre broche de la LDR reliée à la masse
Passer une gaine thermo noire sur la LDR pour qu'elle dépasse d'environ 10mm orientée vers le haut
Rétracter l'extrémité de la gaine thermo ou la raccourcir pour adapter la variation de luminosité en fonction de la lumière ambiante... On peut aussi couler un peu de colle à chaud translucide dans le trou de la gaine thermo ou même de l'Araldite.
Ça fonctionne plutôt bien, même sans condensateur de découplage...
Photo de la coupelle noire vue de dessous :
Du plâtre peint en blanc pour la stabilité et les vibrations
1 prise jack femelle noire alimentation 12V montée sur le côté du ventilateur
1 diode Zener 3.9V en série avec le moteur du ventilateur pour limiter sa vitesse, le bruit et les vibrations et améliorer sa durée de vie.
1 petite carte chinoise "émetteur" pour alimenter la bobine (collée sur le dessus de la coupelle et invisible sur cette photo)
2 fils émaillés de la bobine (qui passent à travers 2 trous percés dans la coupelle)
Le ventilateur est fixé à la coupelle par 4 vis écrous M4 noirs
La bobine chinoise de l'émetteur est reliée à une petite carte alimentée en 12V, visible sur la photo du haut. La bobine du récepteur est collée sur le dessous du support et la carte récepteur est reliée au régulateur qui ne sert d'ailleurs plus à rien.
Vue du montage (ça commence un peu à être le b... L'idéal serait de concevoir un circuit imprimé) :
Les 12 Leds CMS blanches collées ensemble
Les 13 fils multicolores
Les condensateurs de découplage
Un fil de couture bleu fin pour ligaturer tous les fils
le transistor BC327 qui alimente les Leds et la résistance de 4.7kΩ pour l'intensité minimale dans l'obscurité
La gaine thermorétractable noire (avec un adhésif aluminium) montée sur la LDR (il faut beaucoup atténuer la lumière)
La vis M3 fixée du l'axe du moteur du ventilateur qui maintient le régulateur 7805
l'Arduino Nano avec les résistances
La fourche optique et son équerre en aluminium pour la synchronisation
Un fil de cuivre rigide bleu pour équilibrer en longueur et en largeur
Les 2 bobines : la bobine fixe collée sur la coupelle et la bobine mobile collée sous le support sont visibles (elles sont espacées de 3 ou 4 mm)
Les vis qui maintiennent le ventilateur contre la coupelle
Une autre solution pour régler dans les 2 axes la position du trou M3 pour l'équilibrage : 2 rainures 3x10 dans le support, de chaque côté de l'Arduino. Dans chaque rainure, une vis M3 et un écrou ou une entretoise que l'on fait coulisser pour équilibrer en longueur. Le fait d'avoir 2 rainures permet aussi d'équilibrer en largeur.
Vue de l'horloge avec la coupelle : (L'heure est affichée 3 fois par tour mais sur la photo, à cause de l'angle d'ouverture des Leds, une seule est visible).
L'adaptation à la lumière ambiante fonctionne plutôt bien.
L'affichage semble suspendu dans le vide !
L'afficheur est plus grand que l'horloge !
Une horloge holographique avec un réseau de micro lasers et un rideau optique quantique... (je plaisante).
That's All Folks !
Commencé le 26/10/2025
A jour au 22/11/2025