Bouteille d'eau intelligente Arduino : Suivi temps réel et alertes
Composants et fournitures
- Arduino Pro Mini 328 5V/16MHz (SparkFun) × 1
- Capteur ultrasonique étanche × 1
- Module RTC (horloge temps réel) × 1
- Afficheur 7 segments 4 digits cathode commune (SparkFun) × 1
- Buzzer × 1
- Bouton poussoir 12 mm (SparkFun) × 1
- Transistor NPN usage général (ex. 2N2222) × 1
- Batterie Li-Ion 1000 mAh × 1
- Chargeur USB Li-Ion (Adafruit) × 1
- Résistances 221 Ω × 8, 1 kΩ × 4, 4,75 kΩ × 2
Outils nécessaires
- Fer à souder
- Pistolet à colle chaude
Applications et services en ligne
- Arduino IDE
À propos de ce projet
Introduction
Boire suffisamment d'eau est essentiel : peau plus nette, meilleure santé, productivité et cognition améliorées, énergie accrue, perte de poids facilitée. Pourtant, 75 % des Américains sont chroniquement déshydratés. Dans nos vies trépidantes, on oublie de boire. Cette bouteille connectée aide à créer une habitude saine en :
- Suivant la consommation quotidienne.
- Calculant la moyenne hebdomadaire.
- Rappelant de boire (alerte sonore).
- Affichant l'heure de la dernière gorgée.
- Tenant > 1 mois sur une charge.
Fabrication du module RTC
Basé sur DS1307 (I²C, 56 octets RAM non volatile, format 12/24 h, année bissextile). Quartz externe 32,768 kHz requis. Deux résistances de tirage (pull-up) 4,7 kΩ sur SDA/SCL. Alimentation 5 V ; broche VBAT mise à la masse (pas de pile bouton, le système est sur batterie principale). Broches : 5V, GND, SCL (→ A5), SDA (→ A4), VBAT (→ GND).
Fabrication de la carte d'affichage 7 segments
Module 4 digits cathode commune, multiplexé : anodes communes par segment, cathodes séparées par digit. 12 broches au lieu de 32 grâce au multiplexage. Courant segment limité par résistances 220 Ω (alimentation Li-Ion). Transistors 2N2222 (bases → 1 kΩ) pour commander les cathodes (digits D0–D3 sur pins D6–D9). Segments a–g + dp sur pins D10–D17. Bouton sur pin D2 (INT0) pour réveil par interruption externe.
Connexion carte affichage → Arduino Pro Mini
Souder segments → pins D10–D17 ; cathodes → collecteurs transistors ; bases transistors → pins D6–D9 ; bouton → pin D2 (INT0).
Connexion module RTC et buzzer
- RTC SDA → A4, SCL → A5
- Buzzer → D3
Fixation capteur ultrasonique sur le bouchon
Percer le bouchon au centre. Passer les 4 fils du capteur. Fixer le capteur côté intérieur, centré. Connexions : VCC→VCC, GND→GND, Trig→D4, Echo→D5.
Téléversement et tests
Bibliothèques requises : OneButton (bouton), LowPower (veille), DS1307RTC + Time/TimeLib (RTC). Algorithme :
- 5 mesures ultrasoniques → moyenne → calcul niveau d'eau (hauteur bouteille = 17 cm, 16 cm eau = 30 oz).
- Détection consommation : niveau actuel < niveau précédent - 1 oz → incrémenter compteur journalier, reset timer inactivité.
- Détection remplissage : niveau actuel > niveau précédent → mise à jour référence.
- Inactivité ≥ 2 h (120 cycles de 1 min) → double bip toutes les 15 min jusqu'à nouvelle consommation.
- Reset journalier à 23:59 ; moyenne hebdomadaire sur 7 jours glissants.
- Bouton : simple clic = cycle d'affichage (Quotidien / Hebdo / Niveau actuel / Heure) ; double clic / appui long = fonctions annexes.
- Veille : affichage 10 s → sommeil 1 min (watchdog 8s × 7) → réveil par interruption (bouton) ou cycle suivant.
Fixation Arduino sur le bouchon
Coller l'Arduino Pro Mini sur la face externe du bouchon (colle chaude, bien remplir).
Batterie et module chargeur
Fixer la batterie Li-Ion sur l'Arduino (attention aux courts-circuits). Connecter le module chargeur USB, port accessible depuis l'extérieur.
Étanchéité totale
Recouvrir toutes les parties extérieures du circuit de colle chaude (sauf port USB). Former un dôme arrondi sur le bouchon. Ne pas immerger le bouchon.
Résultat
Bouteille fonctionnelle, autonomie > 1 mois, affichage lisible, alertes sonores efficaces.
Code
Sketch Arduino
#include
#include
#include
#include
#include "LowPower.h"
#include "OneButton.h"
OneButton button(2, true);
const byte interruptPin = 2;
volatile int state = 0;
const int trigPin = 4;
const int echoPin = 5;
int piezoPin = 3;
const int digit[4] = {9,6,7,8};
int digit_value[4], digit_value1[4];
int button_press_count = 1;
const int segment[8] = {16,10,12,14,15,17,11,13};
const byte number[10][8] = {
{1,1,1,1,1,1,0,0}, {0,1,1,0,0,0,0,0}, {1,1,0,1,1,0,1,0}, {1,1,1,1,0,0,1,0},
{0,1,1,0,0,1,1,0}, {1,0,1,1,0,1,1,0}, {1,0,1,1,1,1,1,0}, {1,1,1,0,0,0,0,0},
{1,1,1,1,1,1,1,0}, {1,1,1,1,0,1,1,0}
};
const byte d[8] = {0,1,1,1,1,0,1,1};
const byte a[8] = {1,1,1,0,1,1,1,1};
const byte r[8] = {0,0,0,0,1,0,1,1};
const byte t[8] = {0,0,0,1,1,1,1,1};
int seconds, minutes, hours;
int water_in_ounch[15], water_intake_ounch[15], water_intake_days[7];
int water_intake_times = 0, previous_water_amount = 0;
int total_water_intake_today = 0, average_intake_last_week = 0;
int intake_day = 1;
float average_water_level = 0;
int water_amount_in_ounce = 0, idle_time = 0, previous_value = 0;
void setup() {
Serial.begin(9600);
pinMode(interruptPin, INPUT_PULLUP);
for(int i=6;i<=17;i++) pinMode(i, OUTPUT);
pinMode(trigPin, OUTPUT); pinMode(echoPin, INPUT);
button.attachClick(pressed);
button.attachDoubleClick(doubleclick);
button.attachLongPressStart(longPressStart);
button.attachDuringLongPress(longPress);
}
long previous_state = millis();
int count = 1, daily_intake = 0, weekly_intake = 0;
long sleep_time = millis();
void loop() {
read_time(); button.tick(); calculation();
daily_intake = total_water_intake_in_day();
weekly_intake = average_water_intake_last_week();
if (button_press_count == 1) { display_d(); display_number(daily_intake); }
else if (button_press_count == 2) { display_a(); display_number(weekly_intake); }
else if (button_press_count == 3) { display_r(); display_number(water_amount_in_ounce); }
else if (button_press_count == 4) { display_first_2(hours); display_last_2(minutes); }
if (idle_time >= 120) { alert(); alert(); }
if ((millis() - sleep_time) >= 15000) {
display_off();
attachInterrupt(digitalPinToInterrupt(interruptPin), blank, FALLING);
for(int i=0;i<7;i++) LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
detachInterrupt(digitalPinToInterrupt(interruptPin));
sleep_time = millis();
}
}
// --- Fonctions affichage multiplexé ---
void display_digit(int dgt) {
for(int i=0;i<8;i++) digitalWrite(segment[i], number[dgt][i]);
}
void display_number(int num) {
int i=0; while(num>0){ digit_value[2-i]=num%10; num/=10; i++; }
digitalWrite(digit[1],HIGH); digitalWrite(digit[2],LOW); digitalWrite(digit[3],LOW); display_digit(digit_value[0]); delay(5);
digitalWrite(digit[1],LOW); digitalWrite(digit[2],HIGH); digitalWrite(digit[3],LOW); display_digit(digit_value[1]); delay(5);
digitalWrite(digit[1],LOW); digitalWrite(digit[2],LOW); digitalWrite(digit[3],HIGH); display_digit(digit_value[2]); delay(5);
digitalWrite(digit[3],LOW); digit_value[0]=digit_value[1]=digit_value[2]=0;
}
void display_first_2(int num) {
digitalWrite(digit[2],LOW); digitalWrite(digit[3],LOW);
int i=0; while(num>0){ digit_value[1-i]=num%10; num/=10; i++; }
digitalWrite(digit[0],HIGH); digitalWrite(digit[1],LOW); display_digit(digit_value[0]); delay(3);
digitalWrite(digit[0],LOW); digitalWrite(digit[1],HIGH); display_digit(digit_value[1]); delay(3);
}
void display_last_2(int num) {
digitalWrite(digit[0],LOW); digitalWrite(digit[1],LOW);
int i=0; while(num>0){ digit_value1[1-i]=num%10; num/=10; i++; }
digitalWrite(digit[2],HIGH); digitalWrite(digit[3],LOW); display_digit(digit_value1[0]); delay(3);
digitalWrite(digit[2],LOW); digitalWrite(digit[3],HIGH); display_digit(digit_value1[1]); delay(3);
}
void display_d(){ digitalWrite(digit[0],HIGH); for(int i=0;i<8;i++) digitalWrite(segment[i],d[i]); delay(5); digitalWrite(digit[0],LOW); }
void display_a(){ digitalWrite(digit[0],HIGH); for(int i=0;i<8;i++) digitalWrite(segment[i],a[i]); delay(5); digitalWrite(digit[0],LOW); }
void display_r(){ digitalWrite(digit[0],HIGH); for(int i=0;i<8;i++) digitalWrite(segment[i],r[i]); delay(5); digitalWrite(digit[0],LOW); }
void display_t(){ digitalWrite(digit[0],HIGH); for(int i=0;i<8;i++) digitalWrite(segment[i],t[i]); delay(5); digitalWrite(digit[0],LOW); }
void display_off(){ for(int i=0;i<4;i++) digitalWrite(digit[i],LOW); for(int i=0;i<8;i++) digitalWrite(segment[i],LOW); delay(5); }
// --- Capteurs & logique ---
void read_time() {
tmElements_t tm; if (RTC.read(tm)) { seconds=tm.Second; minutes=tm.Minute; hours=tm.Hour; }
}
int distance_in_cm() {
digitalWrite(trigPin,LOW); delayMicroseconds(2);
digitalWrite(trigPin,HIGH); delayMicroseconds(10);
digitalWrite(trigPin,LOW);
long duration = pulseIn(echoPin,HIGH);
return duration / 29 / 2;
}
void alert(){ tone(piezoPin,2000,50); tone(piezoPin,2000,200); }
void blank(){ /* state++; */ }
void pressed(){ button_press_count++; alert(); if(button_press_count==5) button_press_count=1; }
void doubleclick(){ Serial.println("Double clic"); }
void longPressStart(){ Serial.println("Appui long début"); }
void longPress(){ Serial.println("Appui long..."); water_intake_ounch[water_intake_times-1]=0; }
void calculation(){
float water_level=0; int read_value;
for(int i=0;i<5;i++){
read_value = distance_in_cm();
if(read_value>16 || read_value<3) return; // lecture instable
water_level += read_value;
delay(10);
}
average_water_level = 17 - water_level/5; // 17 cm = hauteur bouteille
water_amount_in_ounce = int(average_water_level * 1.87); // 16 cm = 30 oz
if(water_intake_times==0){ previous_water_amount = water_amount_in_ounce; water_intake_times=1; }
if((water_amount_in_ounce < previous_water_amount-1) && (hours<24)){
water_intake_ounch[water_intake_times-1] = previous_water_amount - water_amount_in_ounce;
water_intake_times++; previous_water_amount = water_amount_in_ounce; idle_time=0;
} else if(water_amount_in_ounce > previous_water_amount){
previous_water_amount = water_amount_in_ounce; // remplissage
} else if(water_amount_in_ounce == previous_water_amount){
idle_time++;
}
if(hours==23 && minutes==59){
for(int i=0;i<15;i++) water_intake_ounch[i]=0;
water_intake_times=0; intake_day++; if(intake_day==8) intake_day=1;
}
}
int total_water_intake_in_day(){
total_water_intake_today=0;
for(int i=0;i
Schémas
Schéma principal (RTC, afficheur, transistors, capteur, buzzer, alimentation) et schéma carte afficheuse (détail multiplexage).
Processus de fabrication
- Comment fonctionnent les arroseurs de pelouse : conception, histoire et fabrication moderne
- Technologie du Lave-Vaisselle : Histoire, Design et Innovations Futures
- L'évolution des pistolets à eau à pompe : de l'invention au leader mondial du marché
- L'évolution, les matériaux et la fabrication des toilettes modernes
- Bornes d'Incendie : Conception, Fabrication et Rôle dans la Sécurité Urbaine
- L'Évolution et la Science Derrière les Bouteilles Isothermes Modernes
- L'eau : Histoire, Types et Procédés de Traitement Modernes
- La science du shampoing : ingrédients, fabrication et tendances futures
- Kit MOSMusic : MOSFET, Arduino et plus pour éclairage musical