Botol Air Pintar Arduino – Pemantauan & Peringatan Real-Time
Komponen dan Perlengkapan
- SparkFun Arduino Pro Mini 328 - 5V/16MHz – 1 unit
- Sensor Ultrasonik Tahan Air – 1 unit
- Real Time Clock (RTC) – 1 unit
- SparkFun Common-cathode 4-digit 7-segment Display – 1 unit
- Buzzer – 1 unit
- SparkFun Pushbutton switch 12mm – 1 unit
- Transistor Umum NPN – 1 unit
- Baterai Li-Ion 1000mAh – 1 unit
- Charger Baterai Li Ion USB Adafruit – 1 unit
- Resistor 221 ohm – 8 unit
- Resistor 1k ohm – 4 unit
- Resistor 4.75k ohm – 2 unit
Alat dan Mesin yang Diperlukan
- Soldering iron (generik)
- Hot glue gun (generik)
Aplikasi dan Layanan Online
Tentang Proyek Ini
Pendahuluan:
Minum air yang cukup sangat penting untuk kesehatan kita. Minum lebih banyak air dapat mengarah pada kulit yang lebih jernih, kesehatan umum yang lebih baik, produktivitas dan fungsi otak yang meningkat, tingkat energi yang meningkat, dan bahkan penurunan berat badan.
Dalam kehidupan yang sibuk kita, sangat sulit untuk ingat untuk minum air yang cukup. Dan sebagian besar waktu kita lupa untuk minum air yang cukup baik di rumah, kantor atau bepergian. Sekitar 75% Amerika mengalami dehidrasi kronis sepanjang waktu.
Jadi, untuk membangun kebiasaan minum air yang sehat, penting untuk melacak asupan air Anda setiap hari.
Untuk melacak asupan air saya saya membuat botol air saya pintar menggunakan Arduino. Botol ini dapat:
- Melacak asupan air harian saya
- Melacak rata-rata asupan air mingguan saya
- Mengingatkan saya untuk minum air
- Melacak waktu asupan terakhir
- Berjalan lebih dari satu bulan dalam satu charge.
Membuat Modul RTC
Untuk mencatat atau melacak informasi asupan air, membutuhkan informasi waktu dan tanggal saat ini untuk disimpan bersama dengan data yang terkumpul. Chip Real-Time Clock (RTC) seperti DS1307 dengan baterai cadangan yang cocok dapat digunakan untuk menyediakan informasi yang diperlukan. Proses pemrograman chip RTC (dalam perangkat lunak) juga sangat sederhana dan didukung dalam sebagian besar lingkungan pemrograman.
Di sini adalah desain modul RTC yang kompak, berdasarkan IC RTC populer DS1307, untuk proyek mikrokontroler sehari-hari Anda. DS1307 serial real-time clock (RTC) adalah jam/kalendaru BCD biner penuh daya rendah ditambah 56 byte NV SRAM. Alamat dan data ditransfer secara serial melalui bus I2C dua arah. Jam/kalendaru format 24-jam/12-jam menyediakan informasi detik, menit, jam, hari, tanggal, bulan, dan tahun, termasuk koreksi untuk tahun kabisat.
Mari kita buat modul jam waktu nyata sesuai dengan skematik yang dilampirkan. DS1307 RTC memerlukan kristal eksternal untuk beroperasi dengan benar. Dua resistor pull-up diperlukan untuk pin SCL dan SDA dari IC. Nilai resistor mungkin sekitar 2k hingga 10k. Saya menggunakan 4.7k. Saat pengelasan saya mencoba menjaga modul sekecil mungkin karena ruang yang terbatas untuk rangkaian saya. Anda dapat menggunakan DS3231 sebagai gantinya DS1307 dan IC memiliki osilator kristal internal. Jadi, Anda tidak perlu menambahkan kristal eksternal. Anda juga dapat membeli modul RTC siap pakai kecil jika Anda tidak suka membuatnya sendiri. Karena sistem lengkap akan beroperasi dari baterai, karena itu saya menghubungkan pin VBAT RTC ke ground tanpa menggunakan sel koin.
Pin Penting DS1307:
- Pin 5V: Jika pin ini tinggi, maka ds1307 mengirim data, dan jika rendah, berjalan pada baterai tombol cadangan.
- GND: Ini adalah pin ground untuk modul. Ground baterai dan sumber daya diikat bersama.
- SCL: Ini adalah pin jam I2C yang berkomunikasi dengan mikrokontroler. Harus terhubung ke pin SCL Arduino.
- SDA: Ini adalah pin data I2C yang berkomunikasi dengan mikrokontroler. Harus terhubung ke pin SDA Arduino.
- VBAT: Input baterai untuk sel lithium 3V standar atau sumber energi lain. Harus di-ground jika tidak digunakan.
Membuat Papan 7-Segment Display
Untuk modul tampilan di sini saya menggunakan modul common-cathode yang terpadu, kompak yang berisi empat display numerik 7-segment LED yang bagus untuk menampilkan data numerik dengan beberapa karakter sederhana.
Setiap segmen dalam modul tampilan diplexing, artinya berbagi titik koneksi anoda yang sama. Dan setiap dari empat digit dalam modul memiliki titik koneksi katoda umum sendiri. Ini memungkinkan setiap digit untuk dinyalakan atau dimatikan secara independen. Juga, teknik diplexing ini mengubah jumlah besar pin mikrokontroler yang diperlukan untuk mengontrol tampilan menjadi sebelas atau dua belas (di tempat tiga puluh dua)!
Segmen LED dari tampilan memerlukan resistor pembatas arus saat diberi daya dari pin logika 5 V. Nilai resistor biasanya antara 330 dan 470 ohm untuk 5 V. Untuk operasi baterai Li-ion mungkin 220 ohm. Dan, transistor driver direkomendasikan untuk memberikan arus tambahan ke segmen LED, karena setiap pin mikrokontroler hanya dapat menyource atau menyinkronisasi hampir 40 mA arus. Ketika semua (tujuh) segmen tampilan menyala sekaligus (numeral 8), permintaan arus akan melebihi batas 40 mA ini. Gambar yang ditunjukkan di bawah ini menunjukkan diagram kabel dasar dari resistor pembatas arus dan transistor driver.
Bagian tampilan 7-segment 4-digit ini dirakit sekitar empat display 7-segment LED common-cathode, dan empat transistor NPN 2N2222. Resistor 1K digunakan untuk pembatas arus dasar, dan resistor 220R membatasi arus operasi segmen display LED.
Pada papan Arduino, output digital dari D10 hingga D17 digunakan untuk mengemudikan segmen (a hingga g & dp), dan output digital D6 hingga D9 digunakan untuk digit (D0-D3) dari display LED 4×7.
Tombol switch ditambahkan dengan modul tampilan untuk mengakses opsi berbeda. Ini juga akan digunakan untuk membangunkan Arduino dari mode tidur menggunakan interupsi hardware eksternal. jadi, tombol terhubung ke pin digital Arduino #2 (INT0).
Menghubungkan Papan Tampilan dengan Arduino Mini Pro
Menurut skematik yang disediakan sebelumnya solder semua pin segmen ke pin Arduino mini. Lalu solder 4 pin umum ke pin kolektor transistor. Basis transistor terhubung ke pin Arduino. Setelah menghubungkan tampilan hubungkan tombol switch ke pin digital Arduino 2 (INT 0). Pastikan Anda menghubungkan tombol ke pin Arduino 2 karena kita akan menerapkan interupsi hardware eksternal untuk membangunkan Arduino dari mode tidur menggunakan tombol ini.
Menghubungkan Modul RTC dan Buzzer
Sekarang, hubungkan modul RTC yang Anda buat sebelumnya ke papan Arduino. Pin SDA modul harus terhubung ke pin SDA (A4) Arduino dan pin SCL modul harus terhubung ke pin SCL (A5) Arduino. Lalu hubungkan buzzer ke papan Arduino.
Koneksi:
RTC ------> Arduino Mini Pro
- SDA ------> A4
- SCL ------> A5
- Buzzer ------> D3
Melekatkan Sensor Ultrasonik dengan Tutup Botol
Buat lubang ke dalam tutup botol seperti yang ditunjukkan dalam gambar. Bawa empat kabel sensor ultrasonik melalui lubang dan pasang sensor ultrasonik di tengah tutup botol. Sensor harus dipasang di sisi dalam tutup botol dan harus ditempatkan di posisi tengah.
Koneksi:
Sensor Ultrasonik ------> Arduino Mini Pro
- VCC ------> VCC
- GND ------> GND
- Trigger ------> D4
- Echo ------> D5
Mengunggah Program & Pengujian
Setelah menyelesaikan semua koneksi ini adalah waktu yang tepat untuk mengunggah program ke papan Arduino dan menguji fungsionalitas. Anda akan membutuhkan beberapa perpustakaan agar program berfungsi dengan benar. Untuk antarmuka tombol Anda perlu menambahkan perpustakaan OneButton. Karena perangkat akan beroperasi dari baterai, konsumsi daya adalah masalah penting. Saya menerapkan mode tidur pada sketsa Arduino saya. Timer watchdog dan interupsi hardware eksternal digunakan untuk membangunkan Arduino dari mode tidur. Arduino mengambil pembacaan sensor tiga kali, mengejar rata-ratanya dan menghitung informasi yang diperlukan dan menampilkan informasi selama 10 detik lalu pergi ke mode tidur selama 1 menit. Ia akan bangun kapan saja saat tombol yang terhubung ke INT0 (pin digital 2) ditekan. Jadi, jika Anda ingin melihat informasi tentang asupan air Anda tekan tombol opsi kapan saja. Ia akan bangun untuk Anda. Tanpa menekan tombol apa pun ia akan mengambil pembacaan setiap 1 menit. Untuk menerapkan mode tidur saya menggunakan perpustakaan Arduino Low-Power. Untuk membaca waktu dari modul RTC DS1307 saya menggunakan perpustakaan DS1307RTC. Jadi, untuk mengompilasi program yang dilampirkan tambahkan semua perpustakaan ke lingkungan Anda.
Saya akan menjelaskan di sini hanya algoritma perhitungan secara singkat. Lihat file yang dilampirkan untuk sketsa lengkap. Perhitungan dilakukan dari rata-rata lima pembacaan sensor untuk membuatnya cukup akurat. Pembacaan harian di-reset setiap 24 jam dan disiapkan untuk pembacaan baru untuk hari baru.
Rata-rata asupan air disiapkan dari rata-rata asupan harian tujuh hari sebelumnya. Anda 2 jam berlalu tanpa asupan air apa pun itu akan memperingatkan pengguna dengan bip ganda setiap 15 menit dan berhenti selama 2 jam setelah minum air.
Unduh kode yang dilampirkan dan unggah ke Arduino Mini Pro Anda. Hubungkan semua modul dan nyalakan untuk menguji apakah semua komponen berfungsi dengan benar atau tidak. Jika tampilan menunjukkan beberapa hasil, maka selamat! Anda telah menyelesaikan hal-hal yang sulit.
Memasang Arduino dengan Tutup Botol
Menggunakan lem pan tempelkan Arduino mini ke sisi atas tutup botol. Ingatlah bahwa Anda harus memasang semua komponen ke dalam tutup botol. Tambahkan cukup lem sehingga terpasang erat ke tutup botol.
Melekatkan Baterai dan Modul Charger
Sekarang, pasang baterai Li-ion di atas Arduino. Hati-hati tentang shorting baterai dengan pin apa pun yang terbuka. Lalu hubungkan modul charger dengan tutup. Simpan port USB mudah diakses dari sisi luar sehingga dapat dihubungkan dengan mudah ke charger.
Membuat Semua Rangkaian Tahan Air
Setelah menghubungkan semua komponen dan modul ini adalah waktu yang tepat untuk membuat perangkat kami sepenuhnya tahan air. Ini sangat penting karena ini adalah botol air dan setiap saat air dapat tumpah ke rangkaian dan mungkin merusak rangkaian. Untuk membuatnya sepenuhnya tahan air tambahkan cukup lem di setiap bagian luar rangkaian tanpa port USB. Anda dapat membuat tutup bagus menggunakan lem untuk membuatnya bulat seperti tutup asli.
Jangan merendam tutup botol dalam air.
Nikmati
Gambar produk akhir ditampilkan di bawah ini.
Kode
Sketsa ArduinoArduino
// Date and time functions using a DS1307 RTC connected via I2C and Wire lib #include <Time.h> #include <TimeLib.h> #include <Wire.h> #include <DS1307RTC.h> #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]; int 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 {0,1,1,0,0,0,0,0}, //1 {1,1,0,1,1,0,1,0}, //2 {1,1,1,1,0,0,1,0}, //3 {0,1,1,0,0,1,1,0}, //4 {1,0,1,1,0,1,1,0}, //5 {1,0,1,1,1,1,1,0}, //6 {1,1,1,0,0,0,0,0}, //7 {1,1,1,1,1,1,1,0}, //8 {1,1,1,1,0,1,1,0}}; //9 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]; int water_intake_ounch[15]; int water_intake_days[7]; int water_intake_times = 0; int previous_water_amount = 0; int total_water_intake_today = 0; int average_intake_last_week = 0; int inatke_day = 1; float average_water_level = 0; //store average of multiple reading int water_amount_in_ounce = 0; //store calculated amount of water int idle_time = 0; int intake_day = 1; int previous_value = 0; void setup() { Serial.begin(9600); pinMode(interruptPin, INPUT_PULLUP); // put your setup code here, to run once: 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; int daily_intake = 0; int weekly_intake = 0; long sleep_time = millis(); void loop() { read_time(); button.tick(); // keep watching the push buttons: 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); LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); detachInterrupt(digitalPinToInterrupt(interruptPin)); sleep_time = millis(); } } void display_digit(int digit){ for(int i=0; i<8; i++){ digitalWrite(segment[i], number[digit][i]); } } void display_number(int number){ int i=0; while(number>0){ digit_value[2-i] = number%10; number = number/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] = 0; digit_value[1] = 0; digit_value[2] = 0; } void display_first_2(int number){ digitalWrite(digit[2], LOW); digitalWrite(digit[3], LOW); int i=0; while(number>0){ digit_value[1-i] = number%10; number = number/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 number){ digitalWrite(digit[0], LOW); digitalWrite(digit[1], LOW); int i=0; while(number>0){ digit_value1[1-i] = number%10; number = number/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(){ digitalWrite(digit[0], LOW); digitalWrite(digit[1], LOW); digitalWrite(digit[2], LOW); digitalWrite(digit[3], LOW); for(int i=0; i<8; i++){ digitalWrite(segment[i], LOW); } delay(5); } void read_time() { tmElements_t tm; if (RTC.read(tm)) { seconds = tm.Second; minutes = tm.Minute; hours = tm.Hour; } } int distance_in_cm(){ long duration, cm; // The sensor is triggered by a HIGH pulse of 10 or more microseconds. // Give a short LOW pulse beforehand to ensure a clean HIGH pulse: digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); // Read the signal from the sensor: a HIGH pulse whose // duration is the time (in microseconds) from the sending // of the ping to the reception of its echo off of an object. duration = pulseIn(echoPin, HIGH); // convert the time into a distance cm = microsecondsToCentimeters(duration); return cm; } long microsecondsToCentimeters(long microseconds) { // The speed of sound is 340 m/s or 29 microseconds per centimeter. // The ping travels out and back, so to find the distance of the // object we take half of the distance travelled. return microseconds / 29 / 2; } void alert(){ tone(piezoPin, 2000, 50); tone(piezoPin, 2000, 200); //delay(10); } void blank() { //tone(piezoPin, 2000, 100); //state++; } // This function will be called when the button1 was pressed 1 time (and no 2. button press followed). void pressed() { //Serial.println("Button 1 click."); button_press_count++; alert(); if(button_press_count == 5){ button_press_count = 1; } } // click // This function will be called when the button1 was pressed 2 times in a short timeframe. void doubleclick() { Serial.println("Button 1 doubleclick."); } // doubleclick // This function will be called once, when the button1 is pressed for a long time. void longPressStart() { Serial.println("Button 1 longPress start"); } // longPressStart // This function will be called often, while the button1 is pressed for a long time. void longPress() { Serial.println("Button 1 longPress..."); water_intake_ounch[water_intake_times - 1] = 0; //ignore last value } // longPress void calculation(){ float water_level = 0;// store level in every step int read_value = 0; //read sensor reading in cm for(int i=0; i<5; i++){ //take five reading read_value = distance_in_cm(); if(read_value>16 || read_value<3){// unstable reading return; //return to calling function because reading is unstable } else if(read_value<=16 && read_value>=3){//valid value water_level = water_level + read_value; } delay(10); } average_water_level = 17 - water_level/5; //find average from five reading, 17 = botole height water_amount_in_ounce = int(average_water_level*1.87);//16 cm water level = 30 ounch 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)){//some water is consumed 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){ //water is refilled //water refil here previous_water_amount = water_amount_in_ounce; } else if(water_amount_in_ounce == previous_water_amount){ //no water consumed or reafill idle_time+=1; } if(hours==23 && minutes==59){ // a day is over and all values start from zero for new day 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(){//calculate total water intake in a day total_water_intake_today = 0; for(int i=0; i<water_intake_times; i++){ total_water_intake_today = total_water_intake_today + water_intake_ounch[i]; } water_intake_days[intake_day] = total_water_intake_today; return total_water_intake_today; } int average_water_intake_last_week(){//calculate average water intake last week for(int i=1; i<=intake_day; i++){ average_intake_last_week = average_intake_last_week + water_intake_days[i-1]; } average_intake_last_week = average_intake_last_week/intake_day; return average_intake_last_week; }
Skematik