done
This commit is contained in:
136
bot.js
Normal file
136
bot.js
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
import { makeWASocket, useMultiFileAuthState } from "@whiskeysockets/baileys";
|
||||||
|
import qrcode from "qrcode-terminal";
|
||||||
|
import express from "express";
|
||||||
|
import bodyParser from "body-parser";
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
app.use(bodyParser.json());
|
||||||
|
|
||||||
|
let sock; // Variabel global untuk menyimpan socket WhatsApp
|
||||||
|
|
||||||
|
// Objek untuk menyimpan nilai terbaru
|
||||||
|
let latestData = {
|
||||||
|
tds: null,
|
||||||
|
flow: null,
|
||||||
|
ph: null,
|
||||||
|
suhu: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
async function startBot() {
|
||||||
|
const { state, saveCreds } = await useMultiFileAuthState("auth_info");
|
||||||
|
sock = makeWASocket({
|
||||||
|
auth: state,
|
||||||
|
printQRInTerminal: true, // QR code akan muncul di terminal
|
||||||
|
});
|
||||||
|
|
||||||
|
sock.ev.on("creds.update", saveCreds);
|
||||||
|
|
||||||
|
sock.ev.on("connection.update", (update) => {
|
||||||
|
const { connection, lastDisconnect } = update;
|
||||||
|
if (connection === "close") {
|
||||||
|
console.log("Koneksi terputus, mencoba reconnect...");
|
||||||
|
startBot();
|
||||||
|
} else if (connection === "open") {
|
||||||
|
console.log("Bot WhatsApp berhasil terhubung!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sock.ev.on("messages.upsert", async (m) => {
|
||||||
|
const msg = m.messages[0];
|
||||||
|
if (!msg.message) return;
|
||||||
|
|
||||||
|
const sender = msg.key.remoteJid;
|
||||||
|
const text = msg.message.conversation || msg.message.extendedTextMessage?.text || "";
|
||||||
|
|
||||||
|
console.log(`Pesan masuk dari ${sender}: ${text}`);
|
||||||
|
|
||||||
|
// Kirim nilai terbaru jika diminta
|
||||||
|
if (text.toLowerCase() === "suhu") {
|
||||||
|
await sock.sendMessage(sender, { text: `🌡 Suhu: ${latestData.suhu || "belum ada data"} °C` });
|
||||||
|
} else if (text.toLowerCase() === "ph") {
|
||||||
|
await sock.sendMessage(sender, { text: `🧪 pH: ${latestData.ph || "belum ada data"}` });
|
||||||
|
} else if (text.toLowerCase() === "tds") {
|
||||||
|
await sock.sendMessage(sender, { text: `⚡ TDS: ${latestData.tds || "belum ada data"} ppm` });
|
||||||
|
} else if (text.toLowerCase() === "debit") {
|
||||||
|
await sock.sendMessage(sender, { text: `💧 Debit: ${latestData.flow || "0"} L` });
|
||||||
|
} else if (text.toLowerCase() === "data") {
|
||||||
|
const message = `📊 Data Sensor Saat Ini:\n⚡ TDS: ${latestData.tds || "belum ada data"} ppm\n💧 Debit: ${latestData.flow || "0"} L\n🧪 PH: ${latestData.ph || "belum ada data"}\n🌡 Suhu: ${latestData.suhu || "belum ada data"} °C`;
|
||||||
|
await sock.sendMessage(sender, { text: message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Endpoint API untuk menerima data dan memperbarui nilai terbaru
|
||||||
|
let notifiedFlow = false;
|
||||||
|
|
||||||
|
app.post("/send-data", async (req, res) => {
|
||||||
|
let { tds, flow, ph, suhu } = req.body;
|
||||||
|
|
||||||
|
// Konversi ke float dan pastikan 2 desimal
|
||||||
|
tds = parseFloat(tds).toFixed(2);
|
||||||
|
flow = parseFloat(flow).toFixed(2);
|
||||||
|
ph = parseFloat(ph).toFixed(2);
|
||||||
|
suhu = parseFloat(suhu).toFixed(2);
|
||||||
|
|
||||||
|
// Pastikan tetap float
|
||||||
|
tds = parseFloat(tds);
|
||||||
|
flow = parseFloat(flow);
|
||||||
|
ph = parseFloat(ph);
|
||||||
|
suhu = parseFloat(suhu);
|
||||||
|
|
||||||
|
if (isNaN(tds) || isNaN(flow) || isNaN(ph) || isNaN(suhu)) {
|
||||||
|
return res.status(400).send("Semua parameter harus berupa angka.");
|
||||||
|
}
|
||||||
|
|
||||||
|
latestData = { tds, flow, ph, suhu };
|
||||||
|
console.log("Data diperbarui:", latestData);
|
||||||
|
|
||||||
|
// Kirim notifikasi
|
||||||
|
if (flow >= 100.00 && !notifiedFlow) {
|
||||||
|
try {
|
||||||
|
await sock.sendMessage('6281919281102@s.whatsapp.net', {
|
||||||
|
text: `⚠Peringatan⚠ \nDebit air melebihi batas harian\nTotal Debit: ${flow} Liter`
|
||||||
|
});
|
||||||
|
console.log("Notifikasi dikirim ke WhatsApp!");
|
||||||
|
notifiedFlow = true; // Set status notifikasi agar tidak dikirim berulang
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Gagal mengirim notifikasi:", error);
|
||||||
|
}
|
||||||
|
}else if (tds<1000){
|
||||||
|
await sock.sendMessage('6281919281102@s.whatsapp.net', {
|
||||||
|
text: `⚠Peringatan⚠ \nTDS air melebihi batas\nTDS: ${tds} ppm`
|
||||||
|
});
|
||||||
|
console.log("Notifikasi dikirim ke WhatsApp!");
|
||||||
|
}else if (ph<6 || ph>9){
|
||||||
|
await sock.sendMessage('6281919281102@s.whatsapp.net', {
|
||||||
|
text: `⚠Peringatan⚠ \nTDS air melebihi batas\nTDS: ${tds} ppm`
|
||||||
|
});
|
||||||
|
console.log("Notifikasi dikirim ke WhatsApp!");
|
||||||
|
}else if (suhu>30){
|
||||||
|
await sock.sendMessage('6281919281102@s.whatsapp.net', {
|
||||||
|
text: `⚠Peringatan⚠ \nSuhu air melebihi batas\nSuhu: ${tds} °C`
|
||||||
|
});
|
||||||
|
console.log("Notifikasi dikirim ke WhatsApp!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset status notifikasi jika flow turun di bawah 100
|
||||||
|
if (flow < 100.00) {
|
||||||
|
notifiedFlow = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
res.status(200).send("Data berhasil diperbarui.");
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Endpoint API untuk mengambil data terbaru
|
||||||
|
app.get("/get-data", (req, res) => {
|
||||||
|
res.status(200).json(latestData);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Jalankan server API
|
||||||
|
const PORT = 3000;
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.log(`Server API berjalan di http://localhost:${PORT}`);
|
||||||
|
startBot(); // Jalankan bot WhatsApp
|
||||||
|
});
|
3742
package-lock.json
generated
Normal file
3742
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
19
package.json
Normal file
19
package.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "code",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "bot.js",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node bot.js",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"description": "",
|
||||||
|
"dependencies": {
|
||||||
|
"@whiskeysockets/baileys": "^6.7.12",
|
||||||
|
"express": "^4.21.2",
|
||||||
|
"qrcode-terminal":"^0.12.0"
|
||||||
|
}
|
||||||
|
}
|
169
program.py
Normal file
169
program.py
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
import csv
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
import RPi.GPIO as GPIO
|
||||||
|
from adafruit_ads1x15.ads1115 import ADS1115
|
||||||
|
from adafruit_ads1x15.analog_in import AnalogIn
|
||||||
|
from RPLCD.i2c import CharLCD
|
||||||
|
import board
|
||||||
|
import busio
|
||||||
|
from w1thermsensor import W1ThermSensor
|
||||||
|
|
||||||
|
# --- Konstanta ---
|
||||||
|
PULSES_PER_LITER = 290
|
||||||
|
INPUT_PIN = 16
|
||||||
|
BUZZER_PIN = 18
|
||||||
|
CSV_FILENAME = 'data_pengukuran.csv'
|
||||||
|
API_URL = 'http://robotika.upnvj.ac.id:3000/send-data'
|
||||||
|
|
||||||
|
# --- Inisialisasi Perangkat ---
|
||||||
|
lcd = CharLCD(i2c_expander='PCF8574', address=0x27, port=1, cols=20, rows=4, dotsize=8)
|
||||||
|
i2c = busio.I2C(board.SCL, board.SDA)
|
||||||
|
ads = ADS1115(i2c)
|
||||||
|
ads.gain = 1
|
||||||
|
ph_channel = AnalogIn(ads, 0)
|
||||||
|
tds_channel = AnalogIn(ads, 1)
|
||||||
|
temperature_sensor = W1ThermSensor()
|
||||||
|
|
||||||
|
GPIO.cleanup()
|
||||||
|
GPIO.setmode(GPIO.BOARD)
|
||||||
|
GPIO.setup(INPUT_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
|
||||||
|
GPIO.setup(BUZZER_PIN, GPIO.OUT)
|
||||||
|
GPIO.output(BUZZER_PIN, GPIO.LOW)
|
||||||
|
|
||||||
|
# --- Kelas Flow Sensor ---
|
||||||
|
class FlowSensor:
|
||||||
|
def __init__(self, ppl=PULSES_PER_LITER):
|
||||||
|
self.pulse_count = 0
|
||||||
|
self.ppl = ppl
|
||||||
|
self.last_time = datetime.now()
|
||||||
|
|
||||||
|
def pulse_callback(self, _):
|
||||||
|
self.pulse_count += 1
|
||||||
|
self.last_time = datetime.now()
|
||||||
|
|
||||||
|
def get_volume(self):
|
||||||
|
return self.pulse_count / self.ppl
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.pulse_count = 0
|
||||||
|
|
||||||
|
flow_sensor = FlowSensor()
|
||||||
|
GPIO.add_event_detect(INPUT_PIN, GPIO.RISING, callback=flow_sensor.pulse_callback)
|
||||||
|
|
||||||
|
# --- Fungsi Pendukung ---
|
||||||
|
def display_data(tds, ph, temp, flow):
|
||||||
|
lcd.clear()
|
||||||
|
lcd.write_string(f"TDS : {tds:.2f} ppm")
|
||||||
|
lcd.cursor_pos = (1, 0)
|
||||||
|
lcd.write_string(f"pH : {ph:.2f}")
|
||||||
|
lcd.cursor_pos = (2, 0)
|
||||||
|
lcd.write_string(f"Suhu : {temp:.2f} C")
|
||||||
|
lcd.cursor_pos = (3, 0)
|
||||||
|
lcd.write_string(f"Flow : {flow:.2f} L")
|
||||||
|
|
||||||
|
def read_average_voltage(channel, samples=20, delay=0.2, min_valid=1.0):
|
||||||
|
total = 0
|
||||||
|
valid = 0
|
||||||
|
for _ in range(samples):
|
||||||
|
voltage = channel.voltage
|
||||||
|
if voltage >= min_valid:
|
||||||
|
total += voltage
|
||||||
|
valid += 1
|
||||||
|
time.sleep(delay)
|
||||||
|
return total / valid if valid else 0
|
||||||
|
|
||||||
|
def calculate_ph(voltage, temperature):
|
||||||
|
k = 0.008
|
||||||
|
T_ref = 27.0
|
||||||
|
return (-7.0381 * voltage + 28.4177) + k * (temperature - T_ref)
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_tds(voltage):
|
||||||
|
return 487.1959 * voltage - 117.0762
|
||||||
|
|
||||||
|
def check_thresholds_and_buzzer(tds, ph, suhu, flow):
|
||||||
|
if tds > 500 or ph < 6 or ph > 9 or suhu > 35 or flow > 100 :
|
||||||
|
for i in range(5):
|
||||||
|
GPIO.output(BUZZER_PIN, GPIO.HIGH)
|
||||||
|
time.sleep(1)
|
||||||
|
GPIO.output(BUZZER_PIN, GPIO.LOW)
|
||||||
|
time.sleep(0.3)
|
||||||
|
else:
|
||||||
|
GPIO.output(BUZZER_PIN, GPIO.LOW)
|
||||||
|
|
||||||
|
def send_data_to_server(tds, ph, suhu, flow):
|
||||||
|
data = {
|
||||||
|
"tds": round(tds, 2),
|
||||||
|
"ph": round(ph, 2),
|
||||||
|
"suhu": round(suhu, 2),
|
||||||
|
"flow": round(flow, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
headers = {'Content-Type': 'application/json'}
|
||||||
|
response = requests.post(API_URL, headers=headers, json=data)
|
||||||
|
if response.status_code == 200:
|
||||||
|
print("Data berhasil dikirim ke server.")
|
||||||
|
else:
|
||||||
|
print(f"Gagal mengirim data. Status code: {response.status_code}")
|
||||||
|
except requests.RequestException as e:
|
||||||
|
print(f"Kesalahan saat mengirim data: {e}")
|
||||||
|
|
||||||
|
def save_data_to_csv(timestamp, tds, ph, suhu, flow):
|
||||||
|
file_exists = os.path.isfile(CSV_FILENAME)
|
||||||
|
with open(CSV_FILENAME, mode='a', newline='') as file:
|
||||||
|
writer = csv.writer(file)
|
||||||
|
if not file_exists:
|
||||||
|
writer.writerow(['Timestamp', 'pH', 'Suhu (C)', 'TDS (ppm)', 'Volume Air (L)'])
|
||||||
|
writer.writerow([timestamp, f"{ph:.2f}", f"{suhu:.2f}", f"{tds:.2f}", f"{flow:.2f}"])
|
||||||
|
|
||||||
|
# --- Program Utama ---
|
||||||
|
try:
|
||||||
|
last_reset_date = datetime.now().date()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
now = datetime.now()
|
||||||
|
timestamp = now.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
if now.hour == 0 and now.minute == 0 and last_reset_date != now.date():
|
||||||
|
flow_sensor.reset()
|
||||||
|
last_reset_date = now.date()
|
||||||
|
|
||||||
|
suhu = temperature_sensor.get_temperature()
|
||||||
|
avg_voltage_ph = read_average_voltage(ph_channel)
|
||||||
|
avg_voltage_tds = read_average_voltage(tds_channel, min_valid=0.5)
|
||||||
|
ph = calculate_ph(avg_voltage_ph, suhu)
|
||||||
|
tds = calculate_tds(avg_voltage_tds)
|
||||||
|
flow = flow_sensor.get_volume()
|
||||||
|
|
||||||
|
# Cetak data ke terminal
|
||||||
|
print("=" * 50)
|
||||||
|
print(f"Timestamp : {timestamp}")
|
||||||
|
print(f"Suhu : {suhu:.2f} C")
|
||||||
|
print(f"pH : {ph:.2f}")
|
||||||
|
print(f"TDS : {tds:.2f} ppm")
|
||||||
|
print(f"Debit Air : {flow:.2f} L")
|
||||||
|
|
||||||
|
save_data_to_csv(timestamp, tds, ph, suhu, flow)
|
||||||
|
display_data(tds, ph, suhu, flow)
|
||||||
|
check_thresholds_and_buzzer(tds, ph, suhu, flow)
|
||||||
|
send_data_to_server(tds, ph, suhu, flow)
|
||||||
|
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("Program dihentikan oleh pengguna.")
|
||||||
|
lcd.clear()
|
||||||
|
GPIO.cleanup()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Terjadi kesalahan: {e}")
|
||||||
|
GPIO.cleanup()
|
||||||
|
|
||||||
|
finally:
|
||||||
|
GPIO.cleanup()
|
Reference in New Issue
Block a user