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