Prise WiFi électronique

Prise de courant gigogne pilotée par WiFi et MQTT

Prise gigogne WiFi MQTT

Besoin de piloter un appareil électrique à distance ?
Cet appareil, réalisé autour d’un ESP32 C3 supermini, fait le job !

Elle est un complément de l’article traitant de :

Cette prise télécommandée, associée à une sonde de température et un thermostat virtuel, est capable de réguler la température d’une pièce.

Thermostat Home Assistant
Thermostat Home Assistant

Fonctionnement

Un microcontrôleur actionne un relais d’après les ordres reçu par MQTT via WiFi ou par l’appui sur le bouton poussoir. Cela permet de gérer des équipements divers.

Il y a lieu d’être prudent, car ce montage commute des tensions dangereuses pouvant être mortelles. Ne vous y risquez pas si vous ne maîtrisez pas ce domaine !

Matériel

  • Un microcontrôleur ESP32 C3 Supermini;
  • Un boîtier prises gigognes;
  • Un relais SPST 5V DC;
  • Une diode de type 1N914 ou 1N4148;
  • Un transistor NPN type 2N2222 ou équivalent avec un gain de 200;
  • Une résistance de 1200 ohms;
  • Une alimentation Meanwell IRM10-05.

Schéma

Programmation

Platformio.ini

[env:esp32-c3-devkitm-1]
platform = espressif32
board = esp32-c3-devkitm-1
framework = arduino
monitor_speed = 115200
build_flags = 
	-DARDUINO_USB_MODE=1
	-DARDUINO_USB_CDC_ON_BOOT=1
lib_deps = 
	thomasfredericks/Bounce2@^2.72
	marvinroger/AsyncMqttClient@^0.9.0

Jagprod.h

#ifndef jagprod_h
#define jagprod_h

// WiFi
#define sid "votre SSID"
#define wifi_sesame "Votre mot de passe WiFi"

// IP (Si utilisation en DHCP, supprimer cette partie)
const uint8_t ip[4] = {Adresse IP de l'hôte dans votre réseau};
const uint8_t mask[4] = {Masque de sous-réseaux};
const uint8_t ns[4] = {Adresse IP du serveur DNS};
const uint8_t gw[4] = {Adresse de la passerelle};

// MQTT
const char *mqtt_serveur = "adresse IP du serveur MQTT";
const uint16_t mqtt_port = port MQTT;
const char *mqtt_identifiant = "identifiant MQTT";
const char *mqtt_sesame = "Mot de passe MQTT";

#endif

main.cpp

// Si erreur de compilation  :  Supprimer le dossier ESPAsyncTCP du dossier .pio/libdeps
#include <Arduino.h>
#include <jagprod.h>

//-----WIFI-----
#include <WiFi.h>
void WiFiEvent(WiFiEvent_t event);
bool wifi_statut = false;

void WiFiEvent(WiFiEvent_t event)
{
  switch (event)
  {
  case SYSTEM_EVENT_STA_CONNECTED:
    Serial.println("Assoscié");
    break;
  case SYSTEM_EVENT_STA_GOT_IP:
    Serial.println("Connecté");
    wifi_statut = true;
    break;
  case SYSTEM_EVENT_STA_DISCONNECTED:
    Serial.println("Wifi perdu");
    wifi_statut = false;
    break;
  default:
    break;
  }
}

// Relais et bouton
#include <Bounce2.h>
const int pin_relais = 2;
bool etatrelais = false;
const int poussoir1_pin = 1;
Bounce2::Button poussoir1 = Bounce2::Button();

//-----MQTT-----
#include <AsyncMqttClient.h>
AsyncMqttClient mqtt_client;
unsigned long derniere_connexion_mqtt = 0;
unsigned long mqtt_timeout = 5000;
bool mqtt_statut = false;
static String message;

// === MQTT EVENTS ===
void onMqttConnect(bool sessionPresent)
{
  Serial.println("✅ Connecté au broker MQTT !");
  mqtt_statut = true;
  mqtt_client.subscribe("prisewifi2/relais", 1); // QoS 1
    mqtt_client.subscribe("homeassistant/status", 1);
  mqtt_client.publish("prisewifi2/connecte", 1, false, "online");
}

void onMqttDisconnect(AsyncMqttClientDisconnectReason reason)
{
  Serial.printf("⚠️ Déconnecté du broker MQTT (raison: %d)\n", static_cast<uint8_t>(reason));
  mqtt_statut = false;
}

void onMqttPublish(uint16_t packetId)
{
  Serial.printf("📤 Publication MQTT confirmée (packetId: %u)\n", packetId);
}

void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total)
{
  message = "";
  for (size_t i = 0; i < len; i++)
  {
    message += (char)payload[i];
  }
  Serial.printf("📩 Message reçu [%s] : %s\n", topic, message.c_str());

   if (strcmp(topic, "prisewifi2/relais") == 0)
  {
    etatrelais = message.toInt();
    digitalWrite(pin_relais, etatrelais);
    mqtt_client.publish("prisewifi2/etat", 1, true, etatrelais ? "ON" : "OFF");
  }
  else if (strcmp(topic, "homeassistant/status") == 0)
  {
    if (message == "online")
    {
        mqtt_client.publish("prisewifi2/connecte", 1, false, "online");
    }
}
}

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

  // Bouton
  poussoir1.attach(poussoir1_pin, INPUT_PULLUP);
  poussoir1.setPressedState(LOW);
  poussoir1.interval(5);

  // Relais
  pinMode(pin_relais, OUTPUT);
  digitalWrite(pin_relais, etatrelais);

  //----WIFI----
  WiFi.setSleep(false);
  WiFi.setAutoReconnect(true);
  WiFi.config(ip, gw, mask, ns);
  WiFi.onEvent(WiFiEvent);
  WiFi.begin(sid, wifi_sesame);

  //----MQTT----
  mqtt_client.setServer(mqtt_serveur, mqtt_port);
  mqtt_client.setCredentials(mqtt_identifiant, mqtt_sesame);
  mqtt_client.setClientId("PriseWifi2"); // nom du client mqtt
  mqtt_client.onConnect(onMqttConnect);
  mqtt_client.onDisconnect(onMqttDisconnect);
  mqtt_client.onMessage(onMqttMessage);
  mqtt_client.onPublish(onMqttPublish);
}

void loop()
{
   if (wifi_statut == true and mqtt_statut == false and millis() - derniere_connexion_mqtt > mqtt_timeout)
  {
    Serial.println("Tentative de connexion MQTT");
    mqtt_client.connect();
    derniere_connexion_mqtt = millis();
  }
  poussoir1.update();
  if (poussoir1.pressed())
  {
    etatrelais = !etatrelais;
    digitalWrite(pin_relais, etatrelais);
    if (mqtt_client.connected())
    {
      mqtt_client.publish("prisewifi2/etat", 1, true, etatrelais ? "ON" : "OFF");
    }
  }
}

Boîtier

Un boîtier trouvé il y a bien longtemps sur un site de commerce polonais.

J’ai percé quelques trous sur le dessus et le dessous pour l’aération, bien que le montage en fonctionnement chauffe très peu.

J’ai également réalisé une ouverture sur le dessous pour avoir accès au connecteur USB, afin d’en modifier la programmation sans devoir ouvrir le boîtier.

N’hésitez pas à laisser un commentaire ou poser vos questions via le formulaire ci-dessous

Laissez un commentaire et posez vos questions ci-dessous

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