#include <Arduino.h>
#include <HX711.h>
#include <Wire.h>
#include <RTClib.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <WiFi.h>
#include <HTTPClient.h>
// Pin Definitions
const int HX711_DT = 19;
const int HX711_SCK = 18;
const int BUZZER_PIN = 14;
const int LED1_PIN = 33;
const int LED2_PIN = 32;
const int BUTTON_PIN = 15;
// Display Settings
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDR 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// Sensor Objects
HX711 scale;
RTC_DS3231 rtc;
// WiFi Credentials
const char* ssid = "Wokwi-GUEST"; // Default Wokwi WiFi
const char* password = "";
// CallMeBot API
const String phoneNumber = "YOUR_PHONE_NUMBER"; // Format: +628123456789
const String apiKey = "YOUR_API_KEY";
// Calibration Constants
const float EMPTY_WEIGHT = 50.0; // Berat gelas kosong (gram)
const float TARGET_VOLUME = 240.0; // Volume target (ml)
const float MAX_VOLUME = 300.0; // Volume maksimum (ml)
const float ML_PER_GRAM = 1.0; // Konversi gram ke ml
// System Variables
unsigned long lastDrinkTime = 0;
bool alarmActive = false;
bool glassPlaced = true;
float currentVolume = 0;
bool systemStarted = false;
bool hardwareError = false;
void setup() {
Serial.begin(115200);
// Initialize GPIO
pinMode(LED1_PIN, OUTPUT);
pinMode(LED2_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
// Initialize I2C
Wire.begin();
// Initialize Display
if(!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
Serial.println("SSD1306 allocation failed");
hardwareError = true;
}
// Initialize RTC
if (!rtc.begin()) {
Serial.println("Couldn't find RTC");
updateDisplay("RTC Error", "Check connection");
hardwareError = true;
}
// Initialize Scale
scale.begin(HX711_DT, HX711_SCK);
scale.set_scale();
scale.tare();
// Show boot message
updateDisplay("System Boot", "Press button");
// Wait for button press
while (digitalRead(BUTTON_PIN) == HIGH) {
delay(100);
}
// Connect to WiFi
connectToWiFi();
// Calibrate scale
calibrateScale();
// Set initial time
lastDrinkTime = millis();
systemStarted = true;
updateDisplay("System Ready", "Waiting...");
}
void loop() {
if (!systemStarted || hardwareError) return;
unsigned long currentTime = millis();
// Read current volume
currentVolume = getCurrentVolume();
// Check water level
checkWaterLevel(currentVolume);
// Check if glass is present
bool glassDetected = (currentVolume > 10.0);
// Drink reminder logic (2 minutes for simulation)
if (currentTime - lastDrinkTime >= 120000) { // 2 menit = 120,000 ms
if (!alarmActive) {
// Activate alarm
alarmActive = true;
activateAlarm(true);
sendWhatsApp("Waktunya minum air! Silakan ambil gelas Anda.");
updateDisplay("Beep Beep", "Minum sayangg :)");
}
// Check if glass has been taken
if (glassDetected) {
// Glass still present, keep alarming
activateAlarm(true);
} else {
// Glass taken, deactivate alarm
alarmActive = false;
activateAlarm(false);
lastDrinkTime = currentTime;
// Show thank you message
updateDisplay("Sudah diminum?", "Muach!");
delay(5000);
// Ask to place glass again
updateDisplay("Taruh gelasnya", "lagi yaa :)");
// Wait until glass is placed again
while (getCurrentVolume() < 10.0) {
delay(1000);
}
updateDisplay("Terima kasih", "Sistem aktif");
}
} else {
// Normal operation, update display
char volumeStr[20];
snprintf(volumeStr, 20, "Volume: %.1f ml", currentVolume);
updateDisplay("Smart Glass", volumeStr);
}
delay(1000);
}
void connectToWiFi() {
updateDisplay("Connecting to", "WiFi...");
WiFi.begin(ssid, password);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
updateDisplay("WiFi Connected", "IP: " + WiFi.localIP().toString());
Serial.println("WiFi connected");
delay(2000);
} else {
updateDisplay("WiFi Failed", "Using offline");
Serial.println("WiFi connection failed");
delay(2000);
}
}
void updateDisplay(String line1, String line2) {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println(line1);
display.setCursor(0, 20);
display.println(line2);
display.display();
}
void activateAlarm(bool activate) {
static unsigned long lastBlink = 0;
static bool ledState = false;
if (activate) {
unsigned long currentMillis = millis();
if (currentMillis - lastBlink > 100) { // Blink every 100ms
ledState = !ledState;
digitalWrite(LED1_PIN, ledState);
digitalWrite(LED2_PIN, !ledState);
digitalWrite(BUZZER_PIN, ledState);
lastBlink = currentMillis;
}
} else {
digitalWrite(LED1_PIN, LOW);
digitalWrite(LED2_PIN, LOW);
digitalWrite(BUZZER_PIN, LOW);
}
}
void sendWhatsApp(String message) {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("Cannot send WhatsApp: No WiFi");
return;
}
String url = "https://api.callmebot.com/whatsapp.php?phone=" + phoneNumber +
"&text=" + urlEncode(message) +
"&apikey=" + apiKey;
HTTPClient http;
http.begin(url);
int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
Serial.println("WhatsApp sent successfully");
} else {
Serial.printf("WhatsApp failed, error: %d\n", httpCode);
}
http.end();
}
String urlEncode(String str) {
String encodedString = "";
char c;
for (int i = 0; i < str.length(); i++) {
c = str.charAt(i);
if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
encodedString += c;
} else if (c == ' ') {
encodedString += "%20";
} else {
encodedString += '%';
char hex = (c >> 4) & 0x0F;
encodedString += (hex < 10) ? hex + '0' : hex + 'A' - 10;
hex = c & 0x0F;
encodedString += (hex < 10) ? hex + '0' : hex + 'A' - 10;
}
}
return encodedString;
}
float getCurrentVolume() {
// Read the scale (average 5 readings)
float weight = scale.get_units(5);
// If weight is less than empty weight, return 0
if (weight < EMPTY_WEIGHT) {
return 0.0;
}
// Convert to volume: weight (grams) to ml
float volume = (weight - EMPTY_WEIGHT) * ML_PER_GRAM;
return volume;
}
void checkWaterLevel(float volume) {
if (volume > 0 && volume < TARGET_VOLUME) {
char msg[20];
snprintf(msg, 20, "Hanya %.1f ml", volume);
updateDisplay("Belum penuh!", msg);
delay(3000);
} else if (volume > MAX_VOLUME) {
char msg[20];
snprintf(msg, 20, "%.1f ml!", volume);
updateDisplay("Kebanyakan coy", msg);
delay(3000);
}
}
void calibrateScale() {
updateDisplay("Calibrating", "Remove weight");
delay(3000);
// Tare the scale
scale.tare();
updateDisplay("Place 240ml", "water...");
delay(5000); // Wait for user to place water
// Read the current value
float reading = scale.get_units(10);
float scaleFactor = reading / TARGET_VOLUME;
scale.set_scale(scaleFactor);
char msg[30];
snprintf(msg, 30, "Calibrated: %.2f", scaleFactor);
updateDisplay("Calibration OK", msg);
delay(3000);
}