Mètre Numérique — Télémètre / Surveillance

Télémètre ultrasonique basé sur un ESP32 30 broches, un HC-SR04 et un écran OLED SSD1306 128×64. Le projet mesure la distance, affiche une barre graphique, héberge une interface web locale et propose un mode surveillance capable d'envoyer une alerte par Gmail.

DocumentéDifficulté Débutant / intermédiaireDurée 1 week-endNiveau Bases en électronique, Arduino et Wi-Fi
Mètre Numérique — Télémètre / Surveillance

Le projet

La version réellement réalisée utilise un ESP32 classique au format 30 broches. L'OLED SSD1306 fonctionne en I²C sur GPIO 21 (SDA) et GPIO 22 (SCL), tandis que le HC-SR04 utilise GPIO 4 pour TRIG et GPIO 5 pour ECHO. Le capteur a été validé séparément avec l'écran : l'adresse I²C de l'OLED est 0x3C. Le mode surveillance permet de prendre la distance courante comme référence, de définir un seuil de variation et de confirmer un mouvement sur plusieurs mesures consécutives. Lorsque la variation est confirmée, l'ESP32 peut envoyer un email via le serveur SMTP de Gmail. L'interface web est servie directement par l'ESP32 et permet de consulter la distance, la barre graphique, l'état de la surveillance, la référence, le seuil et de lancer un email de test.

Étapes de réalisation

  1. Matériel validéESP32 30 broches + HC-SR04 + OLED SSD1306 128×64 I²C. L'OLED utilise l'adresse 0x3C et fonctionne sur GPIO 21/22.
  2. MesureLe HC-SR04 est déclenché sur GPIO 4 et son retour ECHO est lu sur GPIO 5. La distance est calculée à partir de la durée de l'impulsion ultrasonique, avec une plage logicielle de 2 à 450 cm.
  3. AffichageL'OLED affiche le mode Télémètre ou Surveillance, la distance en centimètres, l'état d'alarme et une barre graphique représentant la distance sur une échelle de 0 à 200 cm.
  4. Interface webLe serveur HTTP intégré à l'ESP32 expose une page de contrôle locale : distance en temps réel, référence, seuil, activation/désactivation de la surveillance et test Gmail.
  5. SurveillanceLa distance actuelle peut devenir la référence. Une variation supérieure ou égale au seuil est confirmée après 3 mesures consécutives. Une alerte email est alors envoyée, avec un délai minimal de 60 secondes entre alertes.
  6. GmailL'envoi utilise smtp.gmail.com sur le port 465 avec un mot de passe d'application Google. Le mot de passe normal du compte Google ne doit pas être utilisé.
ESP32 30 brochesHC-SR04UltrasonSSD1306Wi-FiServeur WebGmailSurveillance

Spécifications

MicrocontrôleurESP32 classique — carte 30 broches
Capteur de distanceHC-SR04 ultrasonique
Portée logicielle2 à 450 cm
AffichageOLED SSD1306 128×64 I²C
Adresse I²C OLED0x3C
Bus OLEDI²C
MesureToutes les 300 ms
Barre graphique0 à 200 cm
SurveillanceRéférence + seuil réglable + 3 confirmations
AlerteGmail SMTP, port 465
Serveur webHTTP local sur port 80
BibliothèquesAdafruit GFX, Adafruit SSD1306, ESP Mail Client

Interface web intégrée

FonctionDescription
📏 DistanceDistance actuelle en temps réel + barre graphique 0–200 cm.
🛡️ SurveillanceActivation/désactivation du mode surveillance.
🎯 RéférencePrendre la distance actuelle comme référence.
⚙️ SeuilRégler le seuil de déclenchement en centimètres.
📧 GmailBouton d'envoi d'un email de test.
📡 SystèmeÉtat Wi-Fi, adresse IP, nombre de mesures et RSSI.

API HTTP

RouteRôle
GET /Interface web principale
GET /api/statusÉtat et mesures du télémètre
POST /api/referenceDéfinit la distance actuelle comme référence
POST /api/threshold?value=XModifie le seuil de surveillance
POST /api/surveillanceActive ou désactive la surveillance
POST /api/test-emailEnvoie un email de test via Gmail

Brochage GPIO

OLED
SDAGPIO 21
SCLGPIO 22
VCC3.3 V
GNDGND ESP32
HC-SR04
TRIGGPIO 4
ECHOGPIO 5 — via pont diviseur 5 V → 3.3 V
VCCGPIO 5 V
GNDGND commun avec l'ESP32

Guide pratique

Matériel nécessaire

  • Microcontrôleur : ESP32 classique — carte 30 broches
  • Capteur de distance : HC-SR04 ultrasonique
  • Affichage : OLED SSD1306 128×64 I²C
  • OLED
  • HC-SR04

Alimentation

Respecte la tension d’alimentation de chaque composant (voir les spécifications).

Ne dépasse jamais la tension maximale d’un composant et vérifie la compatibilité des niveaux logiques avant de raccorder.

Bibliothèques

  • WiFi.h : Connexion Wi-Fi de l’ESP32
  • WebServer.h : Serveur web intégré
  • Wire.h : Écran OLED, communication I²C
  • Adafruit_GFX.h : Écran OLED, fonctions graphiques
  • Adafruit_SSD1306.h : Écran OLED SSD1306
  • ESP_Mail_Client.h : Envoi d’emails (SMTP)

Préparation

  1. Installe l’environnement Arduino adapté à la carte.
  2. Installe les bibliothèques indiquées.
  3. Vérifie le brochage et l’alimentation avant la mise sous tension.
  4. Assemble puis teste périphérique par périphérique.
  5. Charge le firmware et vérifie le démarrage.

Tests de vérification

  1. Vérifie l’alimentation et l’absence de court-circuit.
  2. Vérifie que chaque périphérique est câblé.
  3. Démarre le microcontrôleur et surveille le moniteur série.
  4. Teste la fonction principale avant de refermer le boîtier.

Dépannage

ProblèmeCause possibleSolution
Le projet ne démarre pasAlimentation, câblage ou court-circuitCoupe l’alimentation et vérifie chaque connexion.
Un périphérique n’est pas détectéGPIO, alimentation ou bus mal câbléReprends le tableau de brochage et teste le périphérique seul.
Le firmware affiche une erreurBibliothèque manquante ou mauvaise versionInstalle les bibliothèques listées et vérifie la carte sélectionnée.

Fichiers

Firmware Arduino, à ouvrir dans l'IDE. Renseigne les paramètres en haut du fichier (Wi-Fi, identifiants) avant de le téléverser.

metre_numerique_surveillance.inoTélécharger
/*
 * ============================================================
 * KAIHATSU LAB — TÉLÉMÈTRE / SURVEILLANCE
 * ESP32 30 broches + HC-SR04 + SSD1306 + serveur web + Gmail
 * ============================================================
 *
 * OLED SSD1306 I2C :
 *   SDA -> GPIO 21
 *   SCL -> GPIO 22
 *   VCC -> 3.3V
 *   GND -> GND
 *
 * HC-SR04 :
 *   TRIG -> GPIO 4
 *   ECHO -> GPIO 5
 *
 * IMPORTANT :
 * Le HC-SR04 classique est généralement alimenté en 5 V et son
 * signal ECHO peut monter à 5 V. L'entrée ESP32 n'est pas 5 V
 * tolérante : utiliser un pont diviseur 5 V -> 3.3 V sur ECHO.
 *
 * Gmail :
 *   smtp.gmail.com / port 465
 *   Utiliser un mot de passe d'application Google.
 *   Ne jamais mettre le mot de passe normal du compte Google.
 *
 * Bibliothèques :
 *   Adafruit GFX
 *   Adafruit SSD1306
 *   ESP Mail Client
 * ============================================================
 */

#include <WiFi.h>
#include <WebServer.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ESP_Mail_Client.h>

// ============================================================
// WIFI
// ============================================================

const char* WIFI_SSID = "TON_WIFI";
const char* WIFI_PASSWORD = "TON_MOT_DE_PASSE_WIFI";

// ============================================================
// GMAIL
// ============================================================

#define SMTP_HOST "smtp.gmail.com"
#define SMTP_PORT 465

#define AUTHOR_EMAIL "tonadresse@gmail.com"
#define AUTHOR_PASSWORD "MOT_DE_PASSE_APPLICATION"
#define RECIPIENT_EMAIL "tonadresse@gmail.com"

// ============================================================
// GPIO — ESP32 30 BROCHES
// ============================================================

#define OLED_SDA 21
#define OLED_SCL 22

#define TRIG_PIN 4
#define ECHO_PIN 5

// ============================================================
// OLED
// ============================================================

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

// ============================================================
// SERVEUR WEB
// ============================================================

WebServer server(80);

// ============================================================
// SMTP
// ============================================================

SMTPSession smtp;
Session_Config smtpConfig;
SMTP_Message message;

// ============================================================
// ÉTAT DU SYSTÈME
// ============================================================

float currentDistance = -1.0;
float referenceDistance = -1.0;
float surveillanceThreshold = 5.0;

bool surveillanceEnabled = false;
bool alarmTriggered = false;

unsigned long measurementCount = 0;
unsigned long lastMeasurement = 0;
unsigned long lastAlert = 0;

const unsigned long alertCooldown = 60000UL;
const int REQUIRED_CONFIRMATIONS = 3;

int movementConfirmations = 0;

// ============================================================
// MESURE HC-SR04
// ============================================================

float readDistanceCM() {
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);

  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);

  digitalWrite(TRIG_PIN, LOW);

  long duration = pulseIn(ECHO_PIN, HIGH, 30000);

  if (duration == 0) return -1.0;

  float distance = duration * 0.0343 / 2.0;

  if (distance < 2.0 || distance > 450.0) return -1.0;

  return distance;
}

// ============================================================
// OLED
// ============================================================

void updateOLED() {
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);

  display.setTextSize(1);
  display.setCursor(0, 0);

  if (surveillanceEnabled) {
    display.print("SURVEILLANCE");
    if (alarmTriggered) display.print(" !");
  } else {
    display.print("TELEMETRE");
  }

  display.setTextSize(2);
  display.setCursor(0, 15);

  if (currentDistance < 0) {
    display.println("HORS");
    display.setCursor(0, 35);
    display.println("PORTEE");
  } else {
    display.print(currentDistance, 1);
    display.println(" cm");
  }

  // Barre graphique : 0 à 200 cm
  const int barX = 0;
  const int barY = 56;
  const int barW = 128;
  const int barH = 7;

  display.drawRect(barX, barY, barW, barH, SSD1306_WHITE);

  if (currentDistance >= 0) {
    float ratio = currentDistance / 200.0;
    if (ratio > 1.0) ratio = 1.0;

    int fillWidth = (int)((barW - 2) * ratio);

    display.fillRect(
      barX + 1,
      barY + 1,
      fillWidth,
      barH - 2,
      SSD1306_WHITE
    );
  }

  display.display();
}

// ============================================================
// GMAIL
// ============================================================

void smtpCallback(SMTP_Status status) {
  Serial.println(status.info());

  if (status.success()) {
    Serial.println("Email envoyé avec succès.");
  }
}

void setupSMTP() {
  smtpConfig.server.host_name = SMTP_HOST;
  smtpConfig.server.port = SMTP_PORT;

  smtpConfig.login.email = AUTHOR_EMAIL;
  smtpConfig.login.password = AUTHOR_PASSWORD;
  smtpConfig.login.user_domain = "";

  smtp.callback(smtpCallback);
}

bool sendEmail(String subject, String body) {
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("WiFi non connecté : email impossible.");
    return false;
  }

  smtpConfig.time.ntp_server = "pool.ntp.org,time.nist.gov";
  smtpConfig.time.gmt_offset = 1;
  smtpConfig.time.day_light_offset = 1;

  if (!smtp.connect(&smtpConfig)) {
    Serial.println("Impossible de se connecter à Gmail.");
    return false;
  }

  message.sender.name = "Kaihatsu Lab";
  message.sender.email = AUTHOR_EMAIL;
  message.subject = subject.c_str();

  message.addRecipient("Destinataire", RECIPIENT_EMAIL);

  message.text.content = body.c_str();
  message.text.charSet = "utf-8";
  message.text.transfer_encoding = "base64";

  bool success = MailClient.sendMail(&smtp, &message);

  message.clearRecipients();
  smtp.sendingResult.clear();
  smtp.closeSession();

  if (success) {
    Serial.println("EMAIL ENVOYÉ !");
  } else {
    Serial.print("ERREUR EMAIL : ");
    Serial.println(smtp.errorReason());
  }

  return success;
}

// ============================================================
// SURVEILLANCE
// ============================================================

void triggerAlarm() {
  if (!surveillanceEnabled) return;
  if (referenceDistance < 0) return;
  if (alarmTriggered) return;

  unsigned long now = millis();

  if (lastAlert != 0 && now - lastAlert < alertCooldown) {
    Serial.println("Cooldown alerte actif.");
    return;
  }

  alarmTriggered = true;
  lastAlert = now;

  Serial.println("!!! MOUVEMENT DETECTE !!!");

  String body =
    "Une variation a été détectée par le télémètre Kaihatsu Lab.

"
    "Distance de référence : " + String(referenceDistance, 1) + " cm
" +
    "Distance actuelle : " + String(currentDistance, 1) + " cm
" +
    "Écart : " + String(abs(currentDistance - referenceDistance), 1) + " cm

" +
    "ESP32 + HC-SR04 + SSD1306";

  sendEmail(
    "Kaihatsu Lab - Alerte surveillance",
    body
  );
}

void updateSurveillance() {
  if (!surveillanceEnabled) {
    movementConfirmations = 0;
    alarmTriggered = false;
    return;
  }

  if (referenceDistance < 0 || currentDistance < 0) {
    movementConfirmations = 0;
    return;
  }

  float difference = abs(currentDistance - referenceDistance);

  if (difference >= surveillanceThreshold) {
    movementConfirmations++;

    if (movementConfirmations >= REQUIRED_CONFIRMATIONS) {
      triggerAlarm();
    }
  } else {
    movementConfirmations = 0;

    if (alarmTriggered) {
      Serial.println("Retour à la normale.");
      alarmTriggered = false;
    }
  }
}

// ============================================================
// INTERFACE WEB
// ============================================================

String htmlPage() {
  return R"rawliteral(
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Kaihatsu Lab — Télémètre</title>
<style>
*{box-sizing:border-box}
body{margin:0;font-family:Arial,sans-serif;background:#0b0b0b;color:#fff}
header{padding:20px;text-align:center;border-bottom:1px solid #333}
h1{margin:0;font-size:24px}
.container{max-width:700px;margin:auto;padding:20px}
.card{background:#151515;border:1px solid #303030;border-radius:14px;padding:20px;margin-bottom:15px}
.distance{font-size:52px;font-weight:bold;text-align:center;margin:15px 0}
.bar{height:22px;background:#282828;border-radius:12px;overflow:hidden}
.barFill{height:100%;width:0%;background:#fff;transition:width .3s}
button{width:100%;padding:14px;border:0;border-radius:10px;margin-top:10px;font-size:16px;cursor:pointer}
input{width:100%;padding:12px;margin-top:6px;margin-bottom:12px;border-radius:8px;border:1px solid #444;background:#202020;color:#fff}
.status{padding:12px;border-radius:8px;background:#202020;margin-top:10px}
.on{border:1px solid #fff}
.alarm{background:#fff;color:#000}
.small{color:#aaa;font-size:13px}
</style>
</head>
<body>
<header>
<h1>KAIHATSU LAB</h1>
<div class="small">Télémètre ultrasonique ESP32</div>
</header>
<div class="container">

<div class="card">
<h2>📏 Distance</h2>
<div class="distance" id="distance">-- cm</div>
<div class="bar"><div class="barFill" id="bar"></div></div>
</div>

<div class="card">
<h2>🛡️ Surveillance</h2>
<div class="status" id="surveillance">Désactivée</div>

<label>Distance de référence</label>
<input id="reference" type="number" step="0.1">

<label>Seuil de déclenchement (cm)</label>
<input id="threshold" type="number" step="0.1" value="5">

<button onclick="setReference()">🎯 Prendre la distance actuelle comme référence</button>
<button onclick="setThreshold()">⚙️ Enregistrer le seuil</button>
<button onclick="toggleSurveillance()">🛡️ Activer / désactiver la surveillance</button>
</div>

<div class="card">
<h2>📧 Gmail</h2>
<button onclick="testEmail()">📨 Envoyer un mail de test</button>
<div class="status" id="emailStatus">Prêt</div>
</div>

<div class="card">
<h2>📡 Système</h2>
<div class="status" id="system">Chargement...</div>
</div>

</div>
<script>
async function update(){
  try{
    const r=await fetch('/api/status');
    const d=await r.json();

    document.getElementById('distance').innerText =
      d.distance<0 ? 'Hors portée' : d.distance.toFixed(1)+' cm';

    let percent=d.distance<0?0:Math.min(100,(d.distance/200)*100);
    document.getElementById('bar').style.width=percent+'%';

    document.getElementById('reference').value =
      d.reference>=0 ? d.reference : '';

    document.getElementById('threshold').value=d.threshold;

    const surveillance=document.getElementById('surveillance');

    if(d.surveillance){
      surveillance.innerText=d.alarm?'⚠️ MOUVEMENT DÉTECTÉ':'🟢 Surveillance active';
      surveillance.className=d.alarm?'status alarm':'status on';
    }else{
      surveillance.innerText='⚪ Surveillance désactivée';
      surveillance.className='status';
    }

    document.getElementById('system').innerHTML =
      'Wi-Fi : '+(d.wifi?'connecté':'déconnecté')+'<br>'+
      'IP : '+d.ip+'<br>'+
      'Mesures : '+d.measurements+'<br>'+
      'RSSI : '+d.rssi+' dBm';
  }catch(e){console.log(e)}
}

async function setReference(){
  const r=await fetch('/api/reference',{method:'POST'});
  alert(await r.text()); update();
}

async function setThreshold(){
  const value=document.getElementById('threshold').value;
  const r=await fetch('/api/threshold?value='+encodeURIComponent(value),{method:'POST'});
  alert(await r.text()); update();
}

async function toggleSurveillance(){
  const r=await fetch('/api/surveillance',{method:'POST'});
  alert(await r.text()); update();
}

async function testEmail(){
  const status=document.getElementById('emailStatus');
  status.innerText='Envoi en cours...';
  const r=await fetch('/api/test-email',{method:'POST'});
  status.innerText=await r.text();
}

update();
setInterval(update,1000);
</script>
</body>
</html>
)rawliteral";
}

// ============================================================
// API
// ============================================================

void handleStatus() {
  String json = "{";

  json += ""distance":" + String(currentDistance, 1) + ",";
  json += ""reference":" + String(referenceDistance, 1) + ",";
  json += ""threshold":" + String(surveillanceThreshold, 1) + ",";
  json += ""surveillance":" + String(surveillanceEnabled ? "true" : "false") + ",";
  json += ""alarm":" + String(alarmTriggered ? "true" : "false") + ",";
  json += ""measurements":" + String(measurementCount) + ",";
  json += ""wifi":" + String(WiFi.status() == WL_CONNECTED ? "true" : "false") + ",";
  json += ""ip":"" + WiFi.localIP().toString() + "",";
  json += ""rssi":" + String(WiFi.RSSI());

  json += "}";

  server.send(200, "application/json", json);
}

void handleReference() {
  if (currentDistance < 0) {
    server.send(400, "text/plain", "Distance invalide.");
    return;
  }

  referenceDistance = currentDistance;
  alarmTriggered = false;
  movementConfirmations = 0;

  server.send(
    200,
    "text/plain",
    "Référence définie à " + String(referenceDistance, 1) + " cm."
  );
}

void handleThreshold() {
  if (!server.hasArg("value")) {
    server.send(400, "text/plain", "Valeur manquante.");
    return;
  }

  float value = server.arg("value").toFloat();

  if (value <= 0) {
    server.send(400, "text/plain", "Seuil invalide.");
    return;
  }

  surveillanceThreshold = value;

  server.send(
    200,
    "text/plain",
    "Seuil : " + String(surveillanceThreshold, 1) + " cm."
  );
}

void handleSurveillance() {
  surveillanceEnabled = !surveillanceEnabled;
  movementConfirmations = 0;
  alarmTriggered = false;

  if (surveillanceEnabled && referenceDistance < 0) {
    referenceDistance = currentDistance;
  }

  server.send(
    200,
    "text/plain",
    surveillanceEnabled
      ? "Surveillance active."
      : "Surveillance désactivée."
  );
}

void handleTestEmail() {
  bool result = sendEmail(
    "Kaihatsu Lab - Test",
    "Ceci est un email de test envoyé par l'ESP32.

"
    "Le système Gmail fonctionne."
  );

  server.send(
    result ? 200 : 500,
    "text/plain",
    result ? "Email de test envoyé !" : "Échec de l'envoi."
  );
}

void handleNotFound() {
  server.send(404, "text/plain", "404 - Page inexistante");
}

// ============================================================
// WIFI / SERVEUR
// ============================================================

void connectWiFi() {
  Serial.print("Connexion WiFi à ");
  Serial.println(WIFI_SSID);

  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  int attempts = 0;

  while (WiFi.status() != WL_CONNECTED && attempts < 40) {
    delay(500);
    Serial.print(".");
    attempts++;
  }

  Serial.println();

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("WiFi CONNECTÉ");
    Serial.print("Adresse IP : ");
    Serial.println(WiFi.localIP());
    Serial.print("RSSI : ");
    Serial.print(WiFi.RSSI());
    Serial.println(" dBm");
  } else {
    Serial.println("ÉCHEC WIFI");
  }
}

void setupWebServer() {
  server.on("/", HTTP_GET, [](){
    server.send(200, "text/html", htmlPage());
  });

  server.on("/api/status", HTTP_GET, handleStatus);
  server.on("/api/reference", HTTP_POST, handleReference);
  server.on("/api/threshold", HTTP_POST, handleThreshold);
  server.on("/api/surveillance", HTTP_POST, handleSurveillance);
  server.on("/api/test-email", HTTP_POST, handleTestEmail);

  server.on("/favicon.ico", HTTP_GET, [](){
    server.send(204);
  });

  server.onNotFound(handleNotFound);
  server.begin();

  Serial.println("Serveur Web démarré.");
}

// ============================================================
// SETUP
// ============================================================

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

  Serial.println();
  Serial.println("================================");
  Serial.println(" KAIHATSU LAB");
  Serial.println(" ESP32 TÉLÉMÈTRE / SURVEILLANCE");
  Serial.println("================================");

  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  digitalWrite(TRIG_PIN, LOW);

  Wire.begin(OLED_SDA, OLED_SCL);

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("ERREUR OLED !");
    while (true) delay(1000);
  }

  Serial.println("OLED OK");

  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(2);
  display.setCursor(10, 10);
  display.println("KAIHATSU");
  display.setTextSize(1);
  display.setCursor(30, 40);
  display.println("Telemetre");
  display.display();

  delay(1500);

  connectWiFi();
  setupSMTP();
  setupWebServer();

  Serial.println("Système prêt.");

  if (WiFi.status() == WL_CONNECTED) {
    Serial.print("Ouvre dans votre navigateur : http://");
    Serial.println(WiFi.localIP());
  }
}

// ============================================================
// LOOP
// ============================================================

void loop() {
  server.handleClient();

  unsigned long now = millis();

  if (now - lastMeasurement >= 300) {
    lastMeasurement = now;

    currentDistance = readDistanceCM();
    measurementCount++;

    Serial.print("Distance : ");

    if (currentDistance < 0) {
      Serial.println("Hors de portée");
    } else {
      Serial.print(currentDistance, 1);
      Serial.println(" cm");
    }

    updateSurveillance();
    updateOLED();
  }
}

Composants

Les guides des composants utilisés dans ce projet : câblage, code réutilisable et outils dans le navigateur.

Acheter

La boutique n'est pas encore ouverte. Les prix et dates seront annoncés par email.

Mètre Numérique — carte nue

Carte PCB nue du projet Mètre Numérique. Composants non fournis.

45 × 30 mm · 2 couches · 1.6 mm · HASL (sans plomb)

Bientôt

Mètre Numérique — kit

Télémètre ultrasonique ESP32 + HC-SR04 avec OLED SSD1306, interface web, surveillance et alertes Gmail. PCB + composants nécessaires inclus.

Bientôt

Projet suivant : Mini Arcade

Recevoir les prochaines sorties

Un email quand un projet, un guide, un PCB ou un kit est publié. Pas de spam, désinscription à tout moment.

Être prévenu

Recevez un email quand une nouveauté est publiée.