niveau_cuve.ino
A ToF sensor mounted above a tank turns distance into fill percentage: constant height, subtraction, low-level alert on the green LED. Contactless level sensing — no float, no wetted parts.
Hardware
An Arduino Nicla Vision (STM32H7 M7+M4, 23×23 mm) with GC2145 camera, LSM6DSOX IMU, PDM mic, ToF (VL53Lx), ESP32 Wi-Fi/BLE module and RGB LED (LEDR/G/B). The board fixed to the lid, ToF window pointing down; a tank of known height (600 mm here). Beware very clear or foaming liquids that fool infrared.
Wiring
No wiring for the on-board ToF. Supply the board stably (USB or 3.3 V) and keep direct sunlight off the sensor window.
How it works
Continuous ranging gives sensor-to-surface distance in mm; fill = 100 − (empty × 100 / height). The 20-1000 mm bounds reject out-of-range ToF readings.
Running it
Programmed from the Arduino IDE (ArduinoCore-mbed, NICLA_VISION target) over USB-C; Camera, PDM, Arduino_LSM6DSOX, WiFiNINA and ArduinoBLE libraries provided.
Variants
Calibrate the height by initializing with an empty tank, average 10 readings for agitated liquids, or report the level over BLE/Wi-Fi to a dashboard.
niveau_cuve.ino
// Niveau de cuve par ToF : distance capteur → surface = vide.
// Cuve de HAUTEUR constante : remplissage = 100 - vide%.
#include <Wire.h>
#include <VL53L1X.h>
VL53L1X tof;
#define HAUTEUR_CUVE_MM 600 // 60 cm de fond à capteur
void setup() {
Serial.begin(115200);
Wire.begin();
tof.setTimeout(500);
if (!tof.init()) {
Serial.println("ToF absent");
while (1) { ; }
}
tof.startContinuous();
}
void loop() {
uint16_t mm = tof.readRangeContinuousMillimeters();
if (!tof.timeoutOccurred() && mm > 20 && mm < 1000) {
int remplissage = 100 - (mm * 100 / HAUTEUR_CUVE_MM);
if (remplissage < 0) remplissage = 0;
if (remplissage > 100) remplissage = 100;
Serial.print("vide="); Serial.print(mm);
Serial.print(" mm -> niveau ");
Serial.print(remplissage);
Serial.println(" %");
if (remplissage < 15) {
digitalWrite(LEDG, HIGH); // alerte niveau bas (LEDG = 24)
} else {
digitalWrite(LEDG, LOW);
}
}
delay(500);
}