Quoi de mieux qu’un petit projet d’anneau lumineux, pour apprendre à utiliser un ATtiny ? C’est en tout cas ce que je vous propose de découvrir aujourd’hui, en ma compagnie !
Ici, je vous détaillerais le schéma électronique et PCB de cet anneau (ring) lumineux, fonctionnant autour d’un ATtiny212 (seulement 8 broches !), et des leds RVB (rouge/vert/bleu) type NeoPixel, modèle SMD2020. Nous verrons également plusieurs exemples de programmes Arduino, permettant de donner vie à cet « ATtiny Ring » !
Remarque : comme toujours, il s’agit là d’un projet éducatif. C’est pourquoi il n’est pas optimisé, tant au niveau matériel (choix de composants basiques), qu’au niveau logiciel (avec des noms de variables/fonctions parfois à rallonge !). Le tout étant seulement, comme à chaque fois, de guider ceux qui débutent avec les microcontrôleurs ATtiny, de mettre en application le programmateur d’ATtiny présenté récemment, et plus globalement, de vous inspirer pour la réalisation de vos propres montages. Alors en avant !
Qu’est-ce que ce Tiny Ring ? (vidéo démo)
Comme vous l’aurez compris, il s’agit ici d’un projet fait pour apprendre l’électronique, et plus particulièrement :
- la mise en œuvre de microcontrôleurs ATtiny, avec leur programmation via UPDI
- le pilotage des leds adressables, aussi connues sous le nom de Neo Pixel
Le nom « Tiny Ring » (ou ATtiny Ring) signifiant simplement que nous allons utiliser un µC ATtiny dans ce projet (un ATtiny212, pour être plus précis), ce projet prenant la forme d’un anneau (ring) qui sera animé/lumineux !
Ce mini-projet pourra être alimenté de 3 manières différentes :
- en 3,3V ou 5V, via le programmateur d’ATtiny (UPDI)
- en 5V, via une prise USB-C prévue à cet effet
- en 3V à 4,2V, via un accu LIR2032 (monté sur embase prévu à cet effet, au dos du PCB)
Comme une animation visuelle vaut toujours mieux que mille discours, voici un exemple d’animation possible, avec cet « ATtiny Ring » (là alimenté sur batterie LIR 2032, donc pas de fils !) :

Bien sûr, le but premier n’est pas ici de faire un gadget visuellement sympathique (quoi que !). En fait, il s’agira de voir ensemble comment créer des animations lumineuses avec un ATtiny et des leds RGB type NeoPixels, au niveau physique (composants électroniques) et programmation (C++/Arduino). Ça vous dit ? Alors commençons par l’examen du schéma électronique de cet ATtiny Ring, sans plus tarder !
Schéma électronique
Sans plus attendre, voici le schéma électronique de cet anneau lumineux à base d’ATtiny et NeoPixels :

Comme vous pouvez le constater, il n’y a rien de bien compliqué ! En fait, ici, on retrouve essentiellement 3 blocs :
- un bloc alimentation, qui permet de sélectionner la source d’alimentation du montage (qui peut être soit le programmateur UPDI, soit la prise USB-C, soit la pile LIR2032) ; à noter que je n’ai pas mis 3 diodes anti-retour au niveau de ces 3 sources, du fait qu’une chute de tension de 0,3V au niveau de l’accu LIR2032 aurait significativement impacté son « autonomie disponible »
- un bloc « microcontrôleur ATtiny », qui tourne autour d’un petit ATtiny212 ; ce dernier recevra, alimentation mise à part :
- sur sa broche 3 (PA7) une tension égale à VCC/2, qui nous permettra de surveiller la tension d’alimentation, par rapport à une référence ATtiny interne à 2,5 volts
- sur sa broche 5 (PA2) une chaîne de leds adressables (Neo Pixels), précédée d’une classique résistance de 330 ohms (R1) en amont
- sur sa broche 4 (PA1) une LED permettant d’indiquer à l’utilisateur si jamais l’alimentation venait à être trop faible (impératif, si vous fonctionnez avec une batterie LIR 2032, car il faut à tout prix éviter de trop descendre en dessous de 3 volts, pour éviter les décharges profondes)
- sur sa broche 6 (UPDI-PA0) une ligne provenant du programmateur UPDI (pour rappel, les ATtiny peuvent se programmer via « un fil » seulement, en utilisant le protocole UPDI)
- et un bloc de LEDs adressables RGB, qui sont ni plus ni moins qu’une mise en série de NeoPixels (modèle SMD2020) en RGB (de l’anglais « Red Green Blue », pour les composantes rouge/vert/bleu) ; pour info, chaque neo pixel est composé en interne de 3 petites leds (1 rouge, 1 verte, et 1 bleue), et d’un contrôleur
WS2812C (permettant le chaînage, le pilotage, et l’adressage de ces « micro » leds intérieures).
Du reste, concernant la programmation du microcontrôleur, je vous renvoie vers l’article que j’avais fait à ce sujet : comment programmer un µC ATtiny avec un programmateur UPDI fait-maison ?
Soudage composants sur PCB (circuit imprimé)
Pour commencer, voici à quoi ressemble le circuit imprimé (PCB double face) de cet ATtiny Ring :

Comme vous l’avez certainement remarqué, sur ce PCB de forme ronde, il faudra souder aussi bien des CMS (composants montés en surface), que des composants traversants. Et concernant ces CMS, vous noterez que certaines empreintes de composants sont de toutes petites tailles (au niveau des leds adressables / NeoPixels, disposées en cercle, au bord du PCB).
Du coup, voici comment je m’y suis pris, pour souder l’ensemble de ces composants (CMS et traversants). Alors :
- pour souder les CMS de la « face avant » de cet anneau lumineux, j’ai :
- utilisé un stencil (un pochoir que j’avais commandé en même temps que le PCB) pour mettre de la pâte à souder uniquement aux bons endroits
- posé chaque composant CMS sur chaque plot de pâte à souder (attention aux sens/polarités des composants)
- fait prendre la soudure à l’aide d’une plaque chauffante réglable (température 20 à 260 °C)
- pour souder les CMS de la « face arrière » de ce Tiny Ring, j’ai :
- mis quelques plots de pâte à souder basse température à la seringue
- placé les bons composants sur ces plots (en faisant bien attention aux sens/polarités, comme toujours !)
- fait prendre la soudure au pistolet à air chaud
- et pour les composants traversants, j’ai tout simplement joué du fer à souder, avec de la soudure plomb/étain qu’il me restait
Je vous propose donc de voir ça en détail, à présent !
Soudure des CMS à l’avant, avec stencil + plaque chauffante
Voici à quoi ressemble le stencil et la paque chauffante :



La 1ère photo vous montre comment j’ai placé le stencil (fine feuille d’alu, avec des trous au niveau des empreintes de composants CMS). J’ai ensuite étalé la pâte dessus (mélange étain/bismuth, pour un soudage « basse température »), et fait pénétrer les trous avec une raclette appropriée (si vous n’avez pas ce genre d’outil, une simple carte type « carte bancaire » fera l’affaire !). Une fois fait, il ne reste plus qu’à retirer ce pochoir (stencil), et poser les composants concernés à chacun de leurs emplacements.
La 2ème et 3ème photo vous montre le PCB une fois soudé, avec une plaque chauffante réglable de 20°C à 260°C (perso, j’ai préchauffé le tout à 120°C, puis fait prendre les soudures à 180/200°C). À noter qu’un professionnel ne ferait certainement pas comme ça, mais en tout cas, cette méthode est simple, rapide, et efficace !
Soudure des CMS à l’arrière, avec pistolet à air chaud
Voici à quoi ressemble cette deuxième séance de soudage de composants CMS, à l’arrière du PCB, cette fois-ci :


Bon ben là… rien de bien compliqué ! Car il suffit de mettre quelques points/plots de pâte à braser étain/bismuth aux endroits où les CMS reposeront, puis de mettre les CMS dessus, et de souffler de l’air chaud de manière circulaire, jusqu’à temps que ça prenne (perso, j’ai utilisé un « petit » débit d’air, à 120°C pour préchauffer, puis 200 °C pour souder, sur un pistolet à air chaud réglable en température/débit).
Soudure des composants traversants
Voilà ! Il ne reste plus qu’à souder les composants traversants, aussi bien devant qu’au dos du circuit imprimé, comme visible ci-dessous :


Ici, j’ai simplement utilisé un fer à souder électronique (filaire rapide), pour fixer ces derniers composants ! Du reste, voyons à présent la liste de tous les composants mis en œuvre ici !
Liste des composants + matériel utilisé
Pour réaliser ce projet, voici les composants que j’ai utilisé :
| Qté | Désignation | Lien achat |
|---|---|---|
| 1 | Support accu LIR2032 / pile CR2032 (CMS, réf. AAA-BAT-054) | |
| 1 | Batterie LIR 2032 (3,6V Li-ion rechargeable) | |
| 1 | Condensateur MLCC 10 µF 16V (CMS, format 1206) | |
| 1 | Condensateur MLCC 100 nF 50V (CMS, format 1206) | |
| 2 | Diode 1N5819 (CMS, format SOD 123) | |
| 1 | Connecteur femelle à 3 broches (pinSocket vertical/traversant, écartement 2,54 mm) | |
| 1 | Embase USB-C « power only » (modèle droit/traversant, 2 pins, femelle) | |
| 12 | Led adressable RGB type NeoPixels (CMS, modèle SMD2020/WS2812C) | |
| 1 | Led rouge (CMS, format 1206) | |
| 1 | Résistance de 330 ohms (CMS, format 1206) | |
| 2 | Résistance de 100 Kohms (CMS, format 1206) | (idem) |
| 1 | Résistance de 10 Kohms (CMS, format 1206) | (idem) |
| 2 | Interrupteur à glissière type MSS22D18G2 (modèle traversant) | |
| 1 | Microcontrôleur ATtiny212 (CMS, format SOIC-8) – Remarque : un ATtiny412-SSNR ou SSF conviendrait également ! | |
| 1 | Circuit imprimé PCB (cf. fin d’article : fichier Gerber zippé, pour reproduire ce projet à l’identique) | – |
Et les matériels que j’ai utilisé :
soudure étain/bismuth (Sn42/Bi58)soudure étain/bismuth (Sn64.7/Bi35/Ag0.3), format seringue- plaque chauffante CMS 80×100 mm 220V (température réglable, de 20 à 260 °C)
- pistolet à air chaud SMD modèle 858D, réglable débit/température air soufflé
- fer à souder électronique filaire 65 watts, chauffe rapide / réglable
- programmateur UPDI (exemple de réalisation simple)
En espérant n’avoir rien oublié, et que les fournisseurs/fabricants ne changent pas leurs modèles entre temps !
Comment programmer l’ATtiny de ce projet ?
Pour programmer ce Tiny Ring, et plus précisément, pour programmer l’ATtiny 212 embarqué sur ce projet, vous aurez besoin de deux choses :
- sur le plan matériel : un convertisseur USB → UPDI (que j’appelle, par abus de langage, un « programmateur UPDI »)
- sur le plan logiciel : un éditeur/compilateur/uploadeur de code (genre Arduino IDE ou VS Code)
Alors, pour la partie matérielle, je vous renvoie vers le convertisseur USB/UPDI que j’avais réalisé sur ce site, qui est un exemple de programmateur UPDI. Ainsi, vous pourrez programmer tous vos ATtiny, à partir des 3 broches de sorties qu’il fournit.
Voici quelques photos du programmateur UPDI, une fois embroché sur notre ATtiny Ring :






Pour la partie logicielle, j’utilise tout simplement Arduino IDE, avec le gestionnaire de carte « megatinycore » installé dessus. Pour plus d’infos à ce niveau, je vous renvoie également vers cet article, où je vous avais détaillé comment installer le « megaTinyCore » sur Arduino IDE.
Du reste, pour uploader un programme C++ sur un ATtiny à partir d’Arduino IDE, il suffit de sélectionner les bons éléments dans le menu « Outils » de l’IDE Arduino. Plus précisément : » (en clair : la famille qui contient notre ATtiny 212)
- dans le menu Outils > Carte > megaTinyCore, il faudra sélectionner la famille ATtiny412/402/212/202 (car nous employons un ATtiny212, dans notre montage)
- dans le menu Outils > Port, il faudra sélectionner le port COM relatif au programmateur ATtiny (dans mon cas, sous Windows, il s’agissait du port COM3, apparu quelques secondes après le branchement du programmateur sur mon ordi ; pour vous, ce sera fort probablement un autre port)
- dans le menu Outils > Chip, il faudra sélectionner « ATtiny212 » (puisque précisément, nous utilisons un ATtiny 212 dans cet anneau lumineux ATtiny)
- dans le menu Outils > Clock, il faudra sélectionner « 4 MHz internal » (pour info, vous pouvez monter jusqu’à 20 MHz, mais dans ce cas, si vous fonctionnez sur batterie LIR 2032, l’autonomie de cette dernière sera bien plus limitée)
- dans le menu Outils > Programmateur, il faudra sélectionner « SerialUPDI – SLOW: 57600 bauds » ; là encore, on pourrait aller plus vite, mais ce faisant, on pourrait alors avoir des bugs de transmission, lors de l’upload (donc mieux vaut prendre son temps, surtout que le téléchargement est super rapide !)
Et c’est seulement une fois tous ces paramètres sélectionnés, que vous pourrez uploader votre code dans votre Tiny Ring.
À présent, voyons 3 exemples de code, pour donner vie à cet ATtiny Ring lumineux !
Exemple test #1 : allumer toutes les leds (NeoPixels)
Premier test que nous allons faire sur notre ATtiny Ring (anneau lumineux à base d’ATtiny) : allumer toutes les LEDS ! Car oui, il faut vérifier que tout fonctionne bien, et qu’il n’y ait pas de ponts/courts-circuits ou mauvaises soudures quelque part ! Voici ce que ça donne, en couleurs, de mon côté :




Et voici le programme arduino (code C++ écrit et uploadé avec Arduino IDE, je veux dire) pour ce premier test :
/*
______ _ _///_ _ _ _
/ _ \ (_) | ___| | | | (_) | [_| |__ ___ ___ _ ___ _ __ | |__ | | ___ ___| |_ _ __ ___ _ __ _ ___ _ _ ___ | ___/ _ \| __|| __| |/ _ \| '_ \_____| __|| |/ _ \/ _| _| '__/ \| '_ \| |/ \| | | |/ _ \ | | | ( ) |__ ||__ | | ( ) | | | |____| |__ | | __/| (_| |_| | | (_) | | | | | (_) | |_| | __/
\__| \__,_|___||___|_|\___/|_| [_| \____/|_|\___|\____\__\_| \___/|_| |_|_|\__ |\__,_|\___| | |
\_|
Fichier : prg01-TinyRing-TestLeds.ino
Description : Programme permettant de tester toutes les leds (NeoPixels et led "batterie faible") du projet TinyRing
(les NeoPixels seront allumées en rouge → vert → bleu → vert, puis à nouveau rouge → ...)
Auteur : Jérôme TOMSKI (https://passionelectronique.fr/)
Licence : BY-NC-ND 4.0 CC (https://creativecommons.org/licenses/by-nc-nd/4.0/deed.fr)
Créé le : 24.04.2025
*/
#include <tinyNeoPixel_Static.h>
// Définition des broches utilisées
#define brochePilotageNeoPixels PIN_PA2 // La pin PA2 (broche 5) pilote la ligne de neopixels
#define brocheLedBatterieFaible PIN_PA1 // La pin PA1 (broche 4) commande la LED "batterie faible"
// Préparation mémoire, pour les 12 NeoPixels qui seront pilotés par notre programme
#define nbreDeLedsBrancheesEnSerie 12
byte pixels[nbreDeLedsBrancheesEnSerie * 3];
// Instanciation librairie
tinyNeoPixel leds = tinyNeoPixel(nbreDeLedsBrancheesEnSerie, brochePilotageNeoPixels, NEO_GRB, pixels);
// ========================
// Initialisation programme
// ========================
void setup() {
// Configuration des broches de µC
pinMode(brochePilotageNeoPixels, OUTPUT);
pinMode(brocheLedBatterieFaible, OUTPUT);
// Allumage de la led "batterie faible"
digitalWrite(brocheLedBatterieFaible, HIGH);
// Réglage de la luminosité de tous les NeoPixels (0-255), avant de les allumer
leds.setBrightness(10);
// Configuration des couleurs des NeoPixels
// Remarque : "setPixelColor" a pour paramètres : l'index de la LED, le taux de rouge (0-255), le taux de vert (0-255), et le taux de bleu (0-255)
leds.setPixelColor(0, 255, 0, 0); // Définition de la 1ère LED en "rouge fixe"
leds.setPixelColor(1, 0, 255, 0); // Définition de la 2ème LED en "vert fixe"
leds.setPixelColor(2, 0, 0, 255); // Définition de la 3ème LED en "bleu fixe"
leds.setPixelColor(3, 0, 255, 0); // Définition de la 4ème LED en "vert fixe"
leds.setPixelColor(4, 255, 0, 0); // Définition de la 5ème LED en "rouge fixe"
leds.setPixelColor(5, 0, 255, 0); // ...
leds.setPixelColor(6, 0, 0, 255); // ...
leds.setPixelColor(7, 0, 255, 0); // ...
leds.setPixelColor(8, 255, 0, 0); // ...
leds.setPixelColor(9, 0, 255, 0); // ...
leds.setPixelColor(10, 0, 0, 255); // Définition de la 10ème LED en "bleu fixe"
leds.setPixelColor(11, 0, 255, 0); // Définition de la 11ème LED en "vert fixe"
// Envoi des commandes, pour allumage des leds en fonction
leds.show();
}
// =================
// Boucle principale
// =================
void loop() {
// Rien ici, car tout se passe dans la fonction loop
}
Le programme est relativement simple, mais il y a plusieurs points à expliquer tout de même.
Tout d’abord, on inclut la librairie « tinyNeoPixel_Static.h » dans ce projet, et non la librairie classique « tinyNeoPixel.h ». Ceci est dû au fait qu’il faut au maximum économiser la place mémoire (flash/ram), car celle-ci est particulièrement limitée sur ces petits ATtiny. Du coup, on utilise la version « Static » de cette librairie, car elle est bien plus légère et économe au niveau des ressources mémoires, pour piloter nos NeoPixels à partir de notre ATtiny 212.
Autre chose remarquable, avec la ligne « byte pixels[nbreDeLedsBrancheesEnSerie * 3]; » : on réserve précisément l’espace mémoire dont nous aurons besoin, pour sauvegarder l’état (les 3 couleurs RVB, en fait) de chaque Neo Pixel (qui sont au nombre de « nbreDeLedsBrancheesEnSerie »). En clair, on alloue un espace mémoire précis, correspondant au besoin exacte de « nbreDeLedsBrancheesEnSerie * 3 » octets (byte, car les couleurs peuvent aller de 0 à 255).
J’ai ajouté une ligne « leds.setBrightness(10); » afin de diminuer fortement la luminosité des leds RGB, pour économiser au maximum le courant consommé (sinon, lors d’un fonctionnement de cet anneau lumineux sur pile/accu LIR 2032, l’autonomie serait très limitée).
Ah oui, une autre chose à savoir au sujet des NeoPixels pilotés depuis le code arduino : ce n’est que lorsqu’on appelle la commande « .show() » que toutes les modifications que l’on a apporté aux leds sont prises en compte. Pour être plus clair : lorsqu’on écrit « leds.setPixelColor(4, 255, 0, 0); » par exemple, cela signifie qu’on souhaite changer les couleurs du NeoPixel #4 (le premier ayant l’indice 0) avec les couleurs rouge=255, vert=0, et bleu=0. Mais cette commande change l’état des leds en mémoire tampon, mais pas l’état des leds physiquement, telles qu’elles nous apparaissent visuellement ; et ce n’est donc que lorsqu’on appellera la fonction « leds.show() » que toutes les mises à jours d’état/de couleurs seront « transmises » aux leds des Neo Pixels.
À noter que j’ai également fait allumer la led « centrale », qui indiquera (dans les programmes suivants) un niveau faible de tension batterie (accu LIR2032, plaçable au dos du PCB). Ainsi, tout sera parfaitement bien vérifié !
Dernière remarque que j’ai à faire ici : voici ce qui est retourné par Arduino IDE, lorsqu’on compile ce programme.
Le croquis utilise 1308 octets (63%) de l'espace de stockage de programmes. Le maximum est de 2048 octets.
Les variables globales utilisent 67 octets (52%) de mémoire dynamique, ce qui laisse 61 octets pour les variables locales. Le maximum est de 128 octets.Vous entrepercevez là toutes les limites de cet ATtiny212, car ce tout petit programme occupe déjà 63 % de la mémoire programme (flash) et 52 % de la mémoire vive (RAM). Autant vous dire qu’on est extrêmement limité au niveau espace mémoire, avec ce petit ATtiny !
Exemple test #2 : animer l’anneau de NeoPixels en rouge, vert, puis bleu
Maintenant que nous avons testé toutes nos leds/neo pixels, nous pouvons réaliser une première animation ! Et voici ce que je vous propose de réaliser, visuellement parlant :

Et le code arduino (programme C++) permettant de faire cela :
/*
______ _ _///_ _ _ _
/ _ \ (_) | ___| | | | (_) | [_| |__ ___ ___ _ ___ _ __ | |__ | | ___ ___| |_ _ __ ___ _ __ _ ___ _ _ ___ | ___/ _ \| __|| __| |/ _ \| '_ \_____| __|| |/ _ \/ _| _| '__/ \| '_ \| |/ \| | | |/ _ \ | | | ( ) |__ ||__ | | ( ) | | | |____| |__ | | __/| (_| |_| | | (_) | | | | | (_) | |_| | __/
\__| \__,_|___||___|_|\___/|_| [_| \____/|_|\___|\____\__\_| \___/|_| |_|_|\__ |\__,_|\___| | |
\_|
Fichier : prg02-TinyRing-1ere-Animation.ino
Description : Programme permettant d'animer les leds du projet TinyRing
(avec détection batterie faible, le cas échéant)
Auteur : Jérôme TOMSKI (https://passionelectronique.fr/)
Licence : BY-NC-ND 4.0 CC (https://creativecommons.org/licenses/by-nc-nd/4.0/deed.fr)
Créé le : 25.04.2025
*/
// Inclusion des librairies dont nous aurons besoin ici
#include <avr/sleep.h> // Pour gérer le mode veille
#include <tinyNeoPixel_Static.h> // Pour gérer les NeoPixels (nota : la version "Static" de cette librairie est une version très légère, idéale pour notre ATtiny212)
// Définition des broches utilisées
#define brochePilotageNeoPixels PIN_PA2 // La pin PA2 (broche 5) pilote la ligne de neopixels
#define brocheLedBatterieFaible PIN_PA1 // La pin PA1 (broche 4) commande la LED "batterie faible"
#define brocheCaptageDemiTensionAccu PIN_PA7 // La pin PA7 (broche 3) reçoit la tension d'alimentation divisée par deux (Vcc/2)
// Autres constantes
#define tensionAlerteBatterie 3 // Si la tension de l'accu (LIR2032) descend en dessous de 3V, alors on "coupe tout" (en allumant led "batterie faible")
// Préparation mémoire, pour les 12 NeoPixels qui seront pilotés par notre programme
#define nbreDeLedsBrancheesEnSerie 12
byte pixels[nbreDeLedsBrancheesEnSerie * 3];
// Instanciation librairie
tinyNeoPixel leds = tinyNeoPixel(nbreDeLedsBrancheesEnSerie, brochePilotageNeoPixels, NEO_GRB, pixels);
// ========================
// Initialisation programme
// ========================
void setup() {
// Configuration des broches de µC
pinMode(brochePilotageNeoPixels, OUTPUT);
pinMode(brocheLedBatterieFaible, OUTPUT);
pinMode(brocheCaptageDemiTensionAccu, INPUT);
// Extinction de la led "batterie faible"
digitalWrite(brocheLedBatterieFaible, LOW);
// Configuration de la référence de tension de l'ADC à 2,5 V
analogReference(INTERNAL2V5);
// Réglage de la luminosité de tous les NeoPixels (0-255), avant de les allumer
leds.setBrightness(10);
// Petite pause d'une seconde, pour stabilisation, avant de passer à la fonction "loop"
delay(1000);
}
// =================
// Boucle principale
// =================
void loop() {
// **********************************************************************************************
// Arrêt du programme, si tension batterie (accu LIR 2032) inférieure à seuil défini tout en haut
// **********************************************************************************************
if(litTensionAccu() < tensionAlerteBatterie * 1000) {
// Extinction des NeoPixels allumés
leds.clear();
// Désactivation de l'ADC, pour économiser de l'énergie
ADC0.CTRLA &= ~(1 << ADC_ENABLE_bp); // ADC_ENABLE_bp = "ADC Enable" bit position
// Allumage LED "batterie faible"
digitalWrite(brocheLedBatterieFaible, HIGH);
// Configuration du mode veille de l'ATtiny (Power-Down)
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
// Enclenchement du mode veille (le programme s'arrête ici, donc, tout en gardant les broches GPIO actives)
sleep_cpu();
}
// *****************
// Cycle d'animation
// *****************
// Allumage en ROUGE des NeoPixels, un après l'autre
animerNeoPixelsAvecCetteCouleur(255, 0, 0);
// Allumage en VERT des NeoPixels, un après l'autre
animerNeoPixelsAvecCetteCouleur(0, 255, 0);
// Allumage en BLEU des NeoPixels, un après l'autre
animerNeoPixelsAvecCetteCouleur(0, 0, 255);
}
// ==========================================
// Fonction : animerNeoPixelsAvecCetteCouleur
// ==========================================
// Infos : permet d'allumer toutes les leds une à une, en cercle
void animerNeoPixelsAvecCetteCouleur(uint8_t rouge, uint8_t vert, uint8_t bleu) {
// "Effacement" de toutes les LEDS, pour commencer
leds.clear();
// Parcours des 12 leds
for (uint8_t i = 0; i < nbreDeLedsBrancheesEnSerie; i++) {
leds.setPixelColor(i, rouge, vert, bleu); // Configuration de la LED #i (0..11, couvrant nos 12 neopixels)
leds.show(); // Mise à jour de l'affichage de tous les NeoPixels
delay(100); // Petite pause avant de passer d'une led à l'autre
}
}
// =========================
// Fonction : litTensionAccu
// =========================
// Nota : les variables "float" consommant beaucoup d'espace mémoire, j'ai pris du uint16
uint16_t litTensionAccu() {
// Lit la valeur ADC sur PA7 (0 à 1023)
int valeurADC = analogRead(brocheCaptageDemiTensionAccu);
// Calcul la tension de l'accu en millivolts
// -----------------------------------------
// Vcc/2 = (valeurADC * 2500) / 1023
// Vcc = 2 * (valeurADC * 2500) / 1023
uint32_t tensionAccu_mV = ((uint32_t)valeurADC * 5000) / 1023;
// Retourne la tension en mV
return (uint16_t)tensionAccu_mV;
}La base de ce programme ATtiny est ni plus ni moins identique au programme précédent, avec quelques subtilités en plus. À savoir :
- une ligne « #include <avr/sleep.h> » a été rajoutée pour pouvoir ensuite mettre en sommeil notre ATtiny Ring, en cas de tension trop faible (ce qui peut arriver, lorsqu’on l’alimente sur accu LIR 2032)
- une ligne « #define tensionAlerteBatterie 3 » permet d’indiquer dans le programme que si jamais la tension d’alim venait à descendre en dessous de 3 volts, alors on « couperait » tout (pour ne pas décharger profondément ou trop rapidement la charge restante dans la batterie LIR2032)
- une ligne « analogReference(INTERNAL2V5); » a également été rajoutée ici, permettant de dire que la référence de tension interne à notre ATtiny sera de 2,5 volts ; cette référence permettra au microcontrôleur d’effectuer des mesures de tension de 0 à 2,5V sur ses entrées analogiques (via l’ADC). Pour rappel, notre montage envoie une tension égale à Vcc/2 au µC, soit 2,5 volts max si on alimente l’anneau lumineux en +5V, ou de 1,5 à 2,1 volts si on alimente le projet avec un accu LIR 2032 (dont la tension peut varier entre 3 et 4,2 volts ; le double donc, comme vous l’aurez compris)
Du reste, au niveau des fonctions, on retrouve :
- une fonction setup, où on initialise tout le nécessaire
- une fonction loop (boucle perpétuelle), où on scanne à chaque cycle l’état de la batterie et agissons en conséquence (si elle est trop faible, on arrête le programme ; si elle est suffisante, on continue les animations lumineuses)
- une fonction « animerNeoPixelsAvecCetteCouleur » qui permet d’allumer les leds une à une, avec 100 millisecondes d’intervalle, avec une couleur prédéfinie
- et une fonction « litTensionAccu » qui retourne la tension estimée, en millivolts (à noter que je n’ai pas utilisé de variable de type float ici, bien que ça eut été plus simple d’usage, du fait que ça prenait bien trop de place en mémoire, et faisait tout planter !)
Une fois compilé, ce programme consomme déjà, en mémoire :
Remarque : dans ce programme, j’utilise la fonction « leds.clear() », qui permet d’effacer l’état (les couleurs) de tous les NeoPixels. Cela n’éteint donc pas toutes les leds directement (seulement dans la mémoire tampon, en fait), et c’est seulement lors de l’appel de la fonction « leds.show() » que tous les nouveaux états de leds seront pris en compte.
Le croquis utilise 1674 octets (81%) de l'espace de stockage de programmes. Le maximum est de 2048 octets.
Les variables globales utilisent 67 octets (52%) de mémoire dynamique, ce qui laisse 61 octets pour les variables locales. Le maximum est de 128 octets.Autant dire qu’on est presque « plein », au niveau de la mémoire programme (pourtant, on ne fait pas grand-chose, côté animation !).
Enfin, une chose que je ne vous avais pas illustré jusqu’à présent : lorsque la tension d’alimentation est trop faible, l’animation des Neo Pixels s’arrête et l’ATtiny se met en mode veille. Ainsi, toutes les leds s’éteignent, à l’exception de la led « batterie faible », bien évidemment, qui est là pour signifier la faiblesse d’alimentation de ce Tiny Ring. Voici à quoi ça ressemble, visuellement parlant :

Exemple test #3 : enchaîner des variations douces de couleurs (RVB)
Troisième programme, un peu plus élaboré, histoire de ne pas se limiter à l’affichage rouge, du vert, et du bleu uniquement ! Ici, je vous propose de faire un balayage de couleurs (une sorte d’arc-en-ciel, si vous préférez), avec ce Tiny Ring. Voici ce que ça donne, en vidéo :

Et tout ceci est permis grâce au programme ATtiny suivant :
/*
______ _ _///_ _ _ _
/ _ \ (_) | ___| | | | (_) | [_| |__ ___ ___ _ ___ _ __ | |__ | | ___ ___| |_ _ __ ___ _ __ _ ___ _ _ ___ | ___/ _ \| __|| __| |/ _ \| '_ \_____| __|| |/ _ \/ _| _| '__/ \| '_ \| |/ \| | | |/ _ \ | | | ( ) |__ ||__ | | ( ) | | | |____| |__ | | __/| (_| |_| | | (_) | | | | | (_) | |_| | __/
\__| \__,_|___||___|_|\___/|_| [_| \____/|_|\___|\____\__\_| \___/|_| |_|_|\__ |\__,_|\___| | |
\_|
Fichier : prg03-TinyRing-2eme-Animation.ino
Description : Programme permettant d'animer les leds du projet TinyRing, d'une autre manière !
(avec détection batterie faible, le cas échéant)
Auteur : Jérôme TOMSKI (https://passionelectronique.fr/)
Licence : BY-NC-ND 4.0 CC (https://creativecommons.org/licenses/by-nc-nd/4.0/deed.fr)
Créé le : 25.04.2025
*/
// Inclusion des librairies dont nous aurons besoin ici
#include <avr/sleep.h> // Pour gérer le mode veille
#include <tinyNeoPixel_Static.h> // Pour gérer les NeoPixels (nota : la version "Static" de cette librairie est une version très légère, idéale pour notre ATtiny212)
// Définition des broches utilisées
#define brochePilotageNeoPixels PIN_PA2 // La pin PA2 (broche 5) pilote la ligne de neopixels
#define brocheLedBatterieFaible PIN_PA1 // La pin PA1 (broche 4) commande la LED "batterie faible"
#define brocheCaptageDemiTensionAccu PIN_PA7 // La pin PA7 (broche 3) reçoit la tension d'alimentation divisée par deux (Vcc/2)
// Autres constantes
#define tensionAlerteBatterie 3 // Si la tension de l'accu (LIR2032) descend en dessous de 3V, alors on "coupe tout" (en allumant led "batterie faible")
#define colorationRougeVersVerte 0
#define colorationVerteVersBleue 1
#define colorationBleueVersRouge 2
// Préparation mémoire, pour les 12 NeoPixels qui seront pilotés par notre programme
#define nbreDeLedsBrancheesEnSerie 12
byte pixels[nbreDeLedsBrancheesEnSerie * 3];
// Préparation mémoire (définition couleur), et initialisations
byte couleur[3] = {255, 0, 0}; // Etat initial : rouge à 255, vert à 0, et bleu à 0
uint8_t colorationEnCours = colorationRougeVersVerte; // Coloration initiale : rouge → verte
// Instanciation librairie
tinyNeoPixel leds = tinyNeoPixel(nbreDeLedsBrancheesEnSerie, brochePilotageNeoPixels, NEO_GRB, pixels);
// ========================
// Initialisation programme
// ========================
void setup() {
// Configuration des broches de µC
pinMode(brochePilotageNeoPixels, OUTPUT);
pinMode(brocheLedBatterieFaible, OUTPUT);
pinMode(brocheCaptageDemiTensionAccu, INPUT);
// Extinction de la led "batterie faible"
digitalWrite(brocheLedBatterieFaible, LOW);
// Configuration de la référence de tension de l'ADC à 2,5 V
analogReference(INTERNAL2V5);
// Réglage de la luminosité de tous les NeoPixels (0-255), avant de les allumer
leds.setBrightness(10);
// Petite pause d'une seconde, pour stabilisation, avant de passer à la fonction "loop"
delay(1000);
}
// =================
// Boucle principale
// =================
void loop() {
// **********************************************************************************************
// Arrêt du programme, si tension batterie (accu LIR 2032) inférieure à seuil défini tout en haut
// **********************************************************************************************
if(litTensionAccu() < tensionAlerteBatterie * 1000) {
// Extinction des NeoPixels allumés
leds.clear(); // Vide la mémoire tampon
leds.show(); // Affiche ce qu'il y a en mémoire tampon
// Désactivation de l'ADC, pour économiser de l'énergie
ADC0.CTRLA &= ~(1 << ADC_ENABLE_bp); // ADC_ENABLE_bp = "ADC Enable" bit position
// Allumage LED "batterie faible"
digitalWrite(brocheLedBatterieFaible, HIGH);
// Configuration du mode veille de l'ATtiny (Power-Down)
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
// Enclenchement du mode veille (le programme s'arrête ici, donc, tout en gardant les broches GPIO actives)
sleep_cpu();
}
// *****************
// Cycle d'animation
// *****************
genereCouleur();
coloreNeoPixels();
// Petite pause, avant de redémarrer cette boucle "loop"
delay(5);
}
// ========================
// Fonction : genereCouleur
// ========================
// Séquence : rouge → vert → bleu → rouge → vert → ...
void genereCouleur() {
if(colorationEnCours == colorationRougeVersVerte) {
if(couleur[0] == 0) {
colorationEnCours = colorationVerteVersBleue;
} else {
couleur[0] = couleur[0] - 1;
couleur[1] = couleur[1] + 1;
}
}
if(colorationEnCours == colorationVerteVersBleue) {
if(couleur[1] == 0) {
colorationEnCours = colorationBleueVersRouge;
} else {
couleur[1] = couleur[1] - 1;
couleur[2] = couleur[2] + 1;
}
}
if(colorationEnCours == colorationBleueVersRouge) {
if(couleur[2] == 0) {
colorationEnCours = colorationRougeVersVerte;
} else {
couleur[2] = couleur[2] - 1;
couleur[0] = couleur[0] + 1;
}
}
}
// ==========================
// Fonction : coloreNeoPixels
// ==========================
// Infos : allume tous les NeoPixels de la même couleur (comme défini dans la variable globale "couleur", qui change à chaque boucle "loop")
void coloreNeoPixels() {
// Parcours des 12 leds, pour les reconfigurer
for (uint8_t i = 0; i < nbreDeLedsBrancheesEnSerie; i++) {
leds.setPixelColor(i, couleur[0], couleur[1], couleur[2]);
}
// Mise à jour de l'état des leds
leds.show();
}
// =========================
// Fonction : litTensionAccu
// =========================
// Nota : les variables "float" consommant beaucoup d'espace mémoire, j'ai pris du uint16
uint16_t litTensionAccu() {
// Lit la valeur ADC sur PA7 (0 à 1023)
int valeurADC = analogRead(brocheCaptageDemiTensionAccu);
// Calcul la tension de l'accu en millivolts
// -----------------------------------------
// Vcc/2 = (valeurADC * 2500) / 1023
// Vcc = 2 * (valeurADC * 2500) / 1023
uint32_t tensionAccu_mV = ((uint32_t)valeurADC * 5000) / 1023;
// Retourne la tension en mV
return (uint16_t)tensionAccu_mV;
}Là encore, peu de changement par rapport au programme précédent, si ce n’est la « mécanique » de cette animation. En effet, ici, on passera d’une couleur de led à l’autre, en les faisant varier en séquence et en opposition 2 par 2. D’un point de vue pratique, voici comment seront pilotées les leds RGB (rouge/vert/bleu) internes à chaque NeoPixel :
- on passera du rouge au vert en diminuant la teinte de rouge, tout en augmentant la teinte de vert
- on passera du vert au bleu en diminuant la teinte de vert, tout en augmentant la teinte de bleu
- et on passera du bleu au rouge (bouclant ainsi la boucle) en diminuant la teinte de bleu, tout en augmentant la teinte de rouge
Bien évidemment, on commencera avec le « rouge à fond », dans ce cas, pour pouvoir amorcer ces cycles !
Au niveau du code, on retrouve donc tous ces éléments, à savoir :
- la variable « byte couleur[3] = {255, 0, 0}; » contiendra la couleur RVB en cours, en commençant par le « rouge vif » (comme expliqué précédemment)
- la variable « uint8_t colorationEnCours = colorationRougeVersVerte; » contiendra la phase en cours, parmi les 3 possibles (que sont rouge → vert, vert → bleu, ou bleu → rouge), en commençant par la phase « ROUGE vers VERT »
- la fonction « genereCouleur() » qui permet de gérer l’avancement des teintes et séquences
Franchement, rien de bien compliqué, une fois qu’on a compris comment fonctionnent les séquences de montée/descente des couleurs par paires 😉
Dernière chose, comme évoqué dans les programmes précédents : la mémoire de l’ATtiny 212 est vraiment limitée ; il faut donc bien faire attention à comment on écrit son code, et tout et tout ! Pour info, voici l’espace mémoire occupé par ce 3ème programme :
Le croquis utilise 1736 octets (84%) de l'espace de stockage de programmes. Le maximum est de 2048 octets.
Les variables globales utilisent 72 octets (56%) de mémoire dynamique, ce qui laisse 56 octets pour les variables locales. Le maximum est de 128 octets.En clair, il ne reste plus beaucoup d’espace libre ! Donc attention, si vous utilisez ces « toutes petites puces » ATtiny !
Pour info : l’autonomie de l’accu LIR 2032 pouvant alimenter ce projet dépend de tout un tas de choses, dont : sa charge initiale, le nombre de leds allumées en même temps, à quel rythme se font les animations, avec quelle intensité, etc. Ici, avec cette troisième démo, l’autonomie était d’environ 5 heures, lors de mes essais (la tension batterie LIR2032 était de 4,2 volts au départ, donc celle-ci était pleinement chargée, et tout s’est coupé comme prévu à 3 volts, une fois la sécurité « batterie faible » activée).
Améliorations possibles, de cet anneau lumineux à base d’ATtiny
Comme vous vous en doutez, ce « Tiny Ring » est loin d’être parfait !
Tout d’abord, on aurait pu utiliser un ATtiny412 (même famille / même série / même empreinte à 8 broches), qui aurait offert 2 fois plus de mémoire flash (programme), et 2 fois plus de RAM. Le remplacement est ailleurs parfaitement faisable ici, à tout moment ; pour cela, il suffit de souder un ATtiny412 à place de l’ATtiny212, et le tour est joué ! Perso, comme j’avais déjà fait tous les documents (schéma/pcb) et toutes les photos, j’ai laissé ainsi ; de plus, je ne cherchais pas à faire quelque chose de super performant, mais quelque chose d’éducatif, afin que ceux qui veulent apprendre l’électronique et/ou apprendre à programmer un ATtiny puissent le faire (et cette illustration de « mémoire limitée » est plutôt parlante au final, je trouve !).
Autre chose que j’ai remarqué, lorsque j’avais fait le 2ème exemple de code (programme test #2) : j’ai disposé la LED 1 non pas tout en haut/au centre (à « 12 heures » ou midi, si vous préférez), mais à la position « 13 heures » (on peut effectivement comparer la position des NeoPixels de cet anneau lumineux ATtiny à celles des heures, car il y a en a 12 au total, équitablement répartis en cercle). Du coup, si j’avais mis la 1ère led à la position « midi » plutôt que « 13h », l’animation de l’exemple #2 aurait démarré tout en haut, en non de manière décalée. Là encore, comme j’avais déjà tout réalisé (pcb, soudage, photos, etc), j’ai laissé tel quel ! Mais bon, vous voyez l’idée 😉
Enfin, je n’ai pas eu le temps d’imprimer un boîtier en 3D, mais je suis sûr que ça aurait donné plus de style !
Liens utiles et téléchargements
Concernant ce projet, voici les documents ayant servis à la conception et réalisation de ce Tiny Ring :
- schéma électronique (format PDF)
- dossier Gerber (format ZIP), pour faire reproduire le PCB à l’identique
Et des documentations techniques, relatifs à l’ATtiny :
- documentation du « megaTinyCore » de Spence Konde
- documentation du « tinyNeoPixel » et « tinyNeoPixel_Static »
- pinout et caractéristiques de l’ATtiny212
ATtiny Ring : conclusion !
Voilà ! J’espère que cette petite réalisation d’anneau lumineux à base de microcontrôleur ATtiny vous aura plu, et permis d’apprendre des choses ! Et puis, ça illustre bien aussi comment utiliser/piloter des Neo Pixels RGB 🙂
Du reste, vu que ce projet s’alimente via une batterie LIR 2032 (accu li-ion, du même format qu’un pile CR 2032), je vous partagerai un petit chargeur d’accus LIR2032 en suivant ! Vous allez voir, c’est pas si compliqué que ça à réaliser ! Alors à bientôt !
Jérôme.
À découvrir aussi : les réalisations concrètes publiées sur Passion Électronique, jusqu’à présent !
(*) Mis à jour le 07/07/2025
Bonjour Jérôme,
Je viens de parcourir l’article et le trouve particulièrement attractif et pédagogique. Merci pour le temps mis à contribution, je vous encourage à continuer.
Bonne journée!
Au plaisir ! À bientôt, alors 🙂
Bonjour Jérome et un grand merci pour cet article didactique super bien fait comme d’habitude
Bernard
Bonjour Jérôme, merci beaucoup pour les détails de ton article très didactique !
De rien ! Avec plaisir !