Une guirlande électrique de Noël en panne ?

Une guirlande électrique de Noël en panne ?

L’année dernière, j’ai récupéré une guirlande de Noël extérieure qui fonctionnait sur 3 piles AA. Visiblement, elle était en panne. Au lieu de la jeter, pourquoi ne pas la bidouiller pour la refaire fonctionner ?

La conception a l’air plutôt simple : un boitier pour les piles et un poussoir pour choisir le programme de clignotement, des LED rouges, jaunes, bleues et vertes, le tout branché sur seulement deux fils. En branchant une pile aux bornes les LED rouges et jaunes se sont allumés, et en branchant la pile dans l’autre sens, les vertes et bleues étaient allumées… le câblage est donc assez évident et doit ressembler à ça, le groupe de 4 LED se répétant sur toute la longueur :

Je suppose donc que pour la contrôler, il va falloir mettre le courant dans un sens ou dans l’autre, et également faire du PWM afin de moduler l’intensité de chacun des deux groupes. Ça tombe bien, il existe un truc très populaire qui fait ça très bien : le pont en H.

Le pont en H

Sans trop de surprise, son nom provient de la forme de son schéma. Il est composé de 4 commutateurs (interrupteurs, relais, transistors…) judicieusement placés et selon l’état de chacun permet de ne pas faire circuler de courant (1er schéma), de faire circuler le courant à travers la charge dans un sens ou dans l’autre (schémas 2 et 3), de court-circuiter la charge (à la masse par exemple sur le schéma 4) ou de faire cramer le circuit (schéma 5, ce cas est à éviter, et est normalement impossible sur les circuits intégrés de pont tout fait).

Si la charge est un moteur à courant continu, ça permet de le laisser en roue libre (schéma 1), de le faire tourner dans un sens ou dans l’autre (2 et 3), ou de le freiner (schéma 4).

Petit module de chez ali-rapide

Vu le courant mis en jeu pour piloter la guirlande, un mini module pont en H fera l’affaire. Ça tombe bien, j’avais ça dans un tiroir. Le circuit intégré s’appelle MX1508, et je n’ai trouvé aucune datasheet digne de ce nom en ligne, juste ceci sur un site chinois : MX1508. Enfin même en chinois, on devine la table de vérité et quelques caractéristiques électriques.

C’est un double pont en H. Les entrées IN1 et IN2 contrôlent la sortie A, et IN3 et IN4  la sortie B.

Câblage sur l’ESP32

Pour une guirlande qui n’est pas trop longue (la mienne fait 5m), l’alimentation 3.3V du régulateur AMS1117 du module ESP32 fera l’affaire. J’ai 300mA avec un groupe de LED allumé.

Pilotage

Pour allumer les deux groupes de LED en « même temps », on va entrelacer deux signaux PWM sur 2 GPIO de l’ESP32. En vrai, il s’allumeront l’un après l’autre très rapidement et la persistance rétinienne fera croire que tout est allumé. Chaque canal sera allumé au maximum 50% du temps, donc le rapport cyclique de chaque PWM ne devra pas dépasser 50%. Les deux signaux PWM doivent être synchronisés entre eux, pour cela, on va utiliser le Motor Control Pulse Width Modulator (MCPWM) de l’ESP32.

Pour contrôler ça à partir d’un logiciel pupitre DMX (ArtNet), j’ai ajouté après dans le programme final une bibliothèque ArtNet.

#include "driver/mcpwm.h"
#include "soc/mcpwm_reg.h"
#include "soc/mcpwm_struct.h"

#define GPIO_RED_YELLOW 18
#define GPIO_GREEN_BLUE 19

/* initialise l'unité 0 du MCPWM avec les signaux MCPWM0A
et MCPWM0AB vers les GPIO 18 et 19 */
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, GPIO_RED_YELLOW);
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0B, GPIO_GREEN_BLUE);

//inverse le signal PWM sur le groupe vert/bleu
GPIO.func_out_sel_cfg[GPIO_GREEN_BLUE].inv_sel = 1;

/* configure la fréquence, le rapport cyclique de départ
(float de 0 à 100.0) */
mcpwm_config_t pwm_config;
pwm_config.frequency = 10000;
pwm_config.cmpr_a = 0.0;
//canal inversé, donc 100 = rapport de 0.
pwm_config.cmpr_b = 100.0;
pwm_config.counter_mode = MCPWM_UP_DOWN_COUNTER;
pwm_config.duty_mode = MCPWM_DUTY_MODE_0;

//initialise l'unité 0 avec le timer 0 et la config ci-dessous
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config);


//appeller plus tard selon les effets lumineux choisis
//modifie le rapport cyclique de chaque canal à 25%
//attention le B est inversé, donc on met 100-rapport cyclique.
mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, 25.0);
mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B, 75.0);
Extrait du code du périphérique MCPWM

 

Tests

Pour les tests, à partir du pupitre DMX les canaux utilisés sont :

  • 0: Master dimmer (intensité de toute la guirlande)
  • 1: Programme (toujours 0 pour l’instant, servira à choisir un effet pré-programmé)
  • 2: Intensité du canal rouge/jaune
  • 3: intensité du canal vert/bleu

L’adresse DMX de départ étant 20 (car c’était utilisé jusqu’à 19 dans mon cas), ça donne les canaux 20 à 23 sur le pupitre.

La trace jaune représente le canal 2 (donc adresse DMX 22, valeur 127), et la trace bleue le canal 3 (donc adresse DMX 23, valeur 255), pondéré par le master dimmer, canal 0 (adresse DMX 20).

 

Le master dimmer prend la valeur maximale (255), le PWM bleu est donc au maximum (50% de rapport cyclique), le PWM jaune est à la moitié, donc 25% de rapport cyclique. Visuellement, toute la guirlande est allumée, mais les LED rouges et jaunes brillent deux fois moins que les vertes et bleues.

 

Tout est au maximum, les deux PWM sont à 50% de rapport cyclique et sont parfaitement synchronisés. Visuellement, toute la guirlande est allumée, toues les LED brillent aussi fort.

 

Programme final

#include <stdint.h>
#include <ArtnetWiFi.h>

#include "driver/mcpwm.h"
#include "soc/mcpwm_reg.h"
#include "soc/mcpwm_struct.h"

#define GPIO_RED_YELLOW 18
#define GPIO_GREEN_BLUE 19

//DMX channels
#define DMX_START 20
uint8_t masterDimmer = 0; //CHAN0: Master dimmer [0:255]
uint8_t program = 0; //CHAN1: Color program
uint8_t param1 = 0; //CHAN2: Param1
uint8_t param2 = 0; //CHAN3: Param1


const char* ssid = "....";
const char* pwd = "......";
const IPAddress ip(192, 168, 1, 210);
const IPAddress gateway(192, 168, 1, 1);
const IPAddress subnet(255, 255, 255, 0);


ArtnetWiFiReceiver artnet;

void setup() {
Serial.begin(115200);

Serial.println("Setup MCPWM GPIO");
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, GPIO_RED_YELLOW);
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0B, GPIO_GREEN_BLUE);

GPIO.func_out_sel_cfg[GPIO_GREEN_BLUE].inv_sel = 1;

mcpwm_config_t pwm_config;

pwm_config.frequency = 10000;
pwm_config.cmpr_a = 0.0;
pwm_config.cmpr_b = 100.0; //inverted output, so 100 means off
pwm_config.counter_mode = MCPWM_UP_DOWN_COUNTER;
pwm_config.duty_mode = MCPWM_DUTY_MODE_0;

mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config);

WiFi.begin(ssid, pwd);
WiFi.config(ip, gateway, subnet);
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(250);
delay(250);
}
Serial.print("WiFi connected, IP = ");
Serial.println(WiFi.localIP());


artnet.begin();
artnet.subscribe(0, artNetCallback);
}

void artNetCallback(const uint8_t* data, const uint16_t dataSize) {
for (size_t iChan = 1; iChan <= dataSize; ++iChan) {
if(iChan < DMX_START) continue;
else if(iChan == (DMX_START)) masterDimmer = data[iChan-1];

else if(iChan == (DMX_START+1)) program = data[iChan-1];
else if(iChan == (DMX_START+2)) param1 = data[iChan-1];
else if(iChan == (DMX_START+3)) param2 = data[iChan-1];

else break;
}

}

float mergeDimmer(uint8_t master, uint8_t local){
//return merged value between 0.0 and 50.0
float merged = (master * local) / 1300.5;
return merged;
}

void updateLight(uint8_t ry, uint8_t gb){
float ryf = mergeDimmer(masterDimmer, ry);
float gbf = 100.0-mergeDimmer(masterDimmer, gb);

mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, ryf);
mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B, gbf);
}


void loop() {
artnet.parse();
//printDebugDmx();


switch(program){
case 0: // static color: param1 set red/yellow, param2 set green/blue
updateLight(param1, param2);
break;

default:
updateLight(param1, param2);
}

}

void printDebugDmx(){
Serial.println("DMX:");
Serial.print(" 1: masterDimmer: ");
Serial.println(masterDimmer);

Serial.print(" 2: program: ");
Serial.println(program);

Serial.print(" 3: param1: ");
Serial.println(param1);

Serial.print(" 4: param2: ");
Serial.println(param2);

Serial.println("---------------------");
}
Programme final ArtNet-Guirlande

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *