TKK_E32230688/tempe.ino

520 lines
12 KiB
C++

#include <ESP8266WiFi.h>
#include <Firebase_ESP_Client.h>
#include "addons/TokenHelper.h"
#include "addons/RTDBHelper.h"
#include <OneWire.h>
#include <DallasTemperature.h>
#include <SimpleKalmanFilter.h>
#include <time.h>
/* ================= WIFI ================= */
#define WIFI_SSID "WONG PUSAT"
#define WIFI_PASSWORD "probolinggo01"
/* ================= FIREBASE ================= */
#define API_KEY "AIzaSyANiVsJP6_-x7EWlCyFs3wzRHiHVE92gZg"
#define DATABASE_URL "https://tempe-56d26-default-rtdb.firebaseio.com/"
#define FIREBASE_PROJECT_ID "tempe-56d26"
/* ================= PIN ================= */
#define ONE_WIRE_BUS D4
#define RELAY_HEATER D1
#define RELAY_FAN_HOT D2
#define RELAY_FAN_COLD D5
/* ================= FIREBASE ================= */
FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;
FirebaseData firestoreData;
/* ================= SENSOR ================= */
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
/* ================= LINKED LIST ================= */
class LinkedList {
private:
float* arr;
int max_size;
int current_size;
public:
LinkedList(int size) {
max_size = size;
current_size = 0;
arr = new float[max_size];
}
void update(float value) {
if (current_size < max_size) {
arr[current_size++] = value;
}
else {
for (int i = 1; i < max_size; i++) {
arr[i - 1] = arr[i];
}
arr[max_size - 1] = value;
}
}
float getIndex(int index) {
if (index < 0 || index >= current_size)
return 0;
return arr[index];
}
int size() {
return current_size;
}
};
/* ================= REGRESI ================= */
class LinearRegression {
public:
LinearRegression(int max_size) {
data = new LinkedList(max_size);
}
void update(float new_data) {
data->update(new_data);
}
float predict(int n_next = 1) {
int n = data->size();
if (n < 2)
return data->getIndex(0);
float sumX = 0;
float sumY = 0;
float sumXY = 0;
float sumX2 = 0;
for (int i = 0; i < n; i++) {
sumX += i;
sumY += data->getIndex(i);
sumXY += i * data->getIndex(i);
sumX2 += i * i;
}
float denominator = (n * sumX2 - sumX * sumX);
if (denominator == 0)
return data->getIndex(n - 1);
float m = (n * sumXY - sumX * sumY) / denominator;
float c = (sumY - m * sumX) / n;
return m * (n + n_next - 1) + c;
}
private:
LinkedList* data;
};
/* ================= REGRESI ================= */
LinearRegression lr(5);
/* ================= VAR ================= */
float t1, t2, t3;
float tf1, tf2, tf3;
float tMin, tMax, tAvg;
float prediksi;
/* ================= ERROR ================= */
float prediksi_lama = 0;
unsigned long waktu_prediksi = 0;
bool siapBanding = false;
float errorTerakhir = 0;
unsigned long lastUpload = 0;
const unsigned long uploadInterval = 60000; // 1 menit
/* ================= KALMAN ================= */
SimpleKalmanFilter kf1(2, 2, 0.01);
SimpleKalmanFilter kf2(2, 2, 0.01);
SimpleKalmanFilter kf3(2, 2, 0.01);
/* ================= PEMBULATAN ================= */
float bulat2(float x) {
return round(x * 100.0) / 100.0;
}
void kontrolSuhu();
void tampilSerial();
void kirimFirebase();
void kirimFirestore();
/* ================= SETUP ================= */
void setup() {
Serial.begin(9600);
delay(2000);
pinMode(RELAY_HEATER, OUTPUT);
pinMode(RELAY_FAN_HOT, OUTPUT);
pinMode(RELAY_FAN_COLD, OUTPUT);
digitalWrite(RELAY_HEATER, HIGH);
digitalWrite(RELAY_FAN_HOT, HIGH);
digitalWrite(RELAY_FAN_COLD, HIGH);
sensors.begin();
Serial.println("=== SISTEM FERMENTASI TEMPE IoT ===");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Menghubungkan WiFi");
unsigned long startTime = millis();
while (time(nullptr) < 100000 &&
millis() - startTime < 30000) {
Serial.print(".");
delay(500);
}
if (time(nullptr) < 100000) {
Serial.println("Gagal sinkron waktu, lanjutkan program");
} else {
Serial.println("Waktu berhasil sinkron");
}
Serial.println("\nWiFi Terhubung");
Serial.println(WiFi.localIP());
configTime(7 * 3600, 0, "pool.ntp.org", "time.nist.gov");
Serial.print("Sinkronisasi waktu");
while (time(nullptr) < 100000) {
Serial.print(".");
delay(500);
}
Serial.println("\nWaktu berhasil sinkron");
config.api_key = API_KEY;
config.database_url = DATABASE_URL;
if (Firebase.signUp(&config, &auth, "", "")) {
Serial.println("Firebase signup berhasil");
}
else {
Serial.printf("Signup gagal: %s\n",
config.signer.signupError.message.c_str());
}
config.token_status_callback = tokenStatusCallback;
Firebase.begin(&config, &auth);
Firebase.reconnectWiFi(true);
Serial.print("Jumlah sensor terdeteksi: ");
Serial.println(sensors.getDeviceCount());
}
/* ================= LOOP ================= */
void loop() {
if (millis() - lastUpload >= uploadInterval) {
lastUpload = millis();
/* ===== BACA SENSOR ===== */
sensors.requestTemperatures();
t1 = sensors.getTempCByIndex(0);
t2 = sensors.getTempCByIndex(1);
t3 = sensors.getTempCByIndex(2);
/* ===== VALIDASI SENSOR ===== */
if (t1 == -127 || t1 == 85) t1 = tf1;
if (t2 == -127 || t2 == 85) t2 = tf2;
if (t3 == -127 || t3 == 85) t3 = tf3;
/* ===== FILTER KALMAN ===== */
tf1 = bulat2(kf1.updateEstimate(t1));
tf2 = bulat2(kf2.updateEstimate(t2));
tf3 = bulat2(kf3.updateEstimate(t3));
/* ===== STATISTIK ===== */
tMin = min(min(tf1, tf2), tf3);
tMax = max(max(tf1, tf2), tf3);
tMin = bulat2(tMin);
tMax = bulat2(tMax);
tAvg = bulat2((tf1 + tf2 + tf3) / 3.0);
/* ===== REGRESI ===== */
lr.update(tAvg);
prediksi = bulat2(lr.predict(5));
/* ===== ERROR PREDIKSI ===== */
if (!siapBanding) {
prediksi_lama = prediksi;
waktu_prediksi = millis();
siapBanding = true;
}
if (siapBanding &&
millis() - waktu_prediksi >= 300000) {
errorTerakhir = abs(prediksi_lama - tAvg);
siapBanding = false;
}
/* ===== KONTROL ===== */
kontrolSuhu();
/* ===== SERIAL ===== */
tampilSerial();
/* ===== FIREBASE ===== */
kirimFirebase();
kirimFirestore();
Serial.println("Upload berhasil");
}
}
void kirimFirestore() {
if (!Firebase.ready() || auth.token.uid == "")
return;
FirebaseJson content;
content.set("fields/sensor1/doubleValue", tf1);
content.set("fields/sensor2/doubleValue", tf2);
content.set("fields/sensor3/doubleValue", tf3);
content.set("fields/min/doubleValue", tMin);
content.set("fields/max/doubleValue", tMax);
content.set("fields/rata_rata/doubleValue", tAvg);
content.set("fields/prediksi/doubleValue", prediksi);
content.set("fields/error/doubleValue", errorTerakhir);
content.set(
"fields/pemanas/stringValue",
digitalRead(RELAY_HEATER) == LOW ? "ON" : "OFF");
content.set(
"fields/kipas_panas/stringValue",
digitalRead(RELAY_FAN_HOT) == LOW ? "ON" : "OFF");
content.set(
"fields/kipas_dingin/stringValue",
digitalRead(RELAY_FAN_COLD) == LOW ? "ON" : "OFF");
time_t now = time(nullptr);
struct tm *tm_info = localtime(&now);
char tanggal[11];
char jam[6];
strftime(tanggal, sizeof(tanggal), "%Y-%m-%d", tm_info);
strftime(jam, sizeof(jam), "%H:%M", tm_info);
content.set("fields/tanggal/stringValue", tanggal);
content.set("fields/jam/stringValue", jam);
String documentPath =
String("monitoring/") +
tanggal +
"/data/" +
jam;
if (Firebase.Firestore.createDocument(
&firestoreData,
FIREBASE_PROJECT_ID,
"",
documentPath.c_str(),
content.raw())) {
Serial.println("Firestore berhasil");
}
else {
Serial.println("Firestore gagal");
Serial.println(firestoreData.errorReason());
}
}
/* ================= KONTROL ================= */
void kontrolSuhu() {
if (tMin < 30) {
digitalWrite(RELAY_HEATER, LOW);
digitalWrite(RELAY_FAN_HOT, LOW);
digitalWrite(RELAY_FAN_COLD, HIGH);
}
else if (tMax > 40) {
digitalWrite(RELAY_HEATER, HIGH);
digitalWrite(RELAY_FAN_HOT, HIGH);
digitalWrite(RELAY_FAN_COLD, LOW);
}
else {
if (prediksi < 30) {
digitalWrite(RELAY_HEATER, LOW);
digitalWrite(RELAY_FAN_HOT, LOW);
digitalWrite(RELAY_FAN_COLD, HIGH);
}
else if (prediksi > 40) {
digitalWrite(RELAY_HEATER, HIGH);
digitalWrite(RELAY_FAN_HOT, HIGH);
digitalWrite(RELAY_FAN_COLD, LOW);
}
else {
digitalWrite(RELAY_HEATER, HIGH);
digitalWrite(RELAY_FAN_HOT, HIGH);
digitalWrite(RELAY_FAN_COLD, HIGH);
}
}
}
/* ================= SERIAL ================= */
void tampilSerial() {
String statusPemanas = "OFF";
String statusKipasPanas = "OFF";
String statusKipasDingin = "OFF";
if (digitalRead(RELAY_HEATER) == LOW)
statusPemanas = "ON";
if (digitalRead(RELAY_FAN_HOT) == LOW)
statusKipasPanas = "ON";
if (digitalRead(RELAY_FAN_COLD) == LOW)
statusKipasDingin = "ON";
Serial.println("\n==================================================");
Serial.println(" MONITORING FERMENTASI TEMPE");
Serial.println("==================================================");
Serial.println("\n[SENSOR SUHU]");
Serial.print(" S1 : ");
Serial.print(tf1, 2);
Serial.println(" °C");
Serial.print(" S2 : ");
Serial.print(tf2, 2);
Serial.println(" °C");
Serial.print(" S3 : ");
Serial.print(tf3, 2);
Serial.println(" °C");
Serial.println("\n[STATISTIK]");
Serial.print(" Min : ");
Serial.print(tMin, 2);
Serial.println(" °C");
Serial.print(" Max : ");
Serial.print(tMax, 2);
Serial.println(" °C");
Serial.print(" Rata : ");
Serial.print(tAvg, 2);
Serial.println(" °C");
Serial.println("\n[PREDIKSI]");
Serial.print(" 5 Menit ke depan : ");
Serial.print(prediksi, 2);
Serial.println(" °C");
Serial.println("\n[STATUS SISTEM]");
Serial.print(" Pemanas : ");
Serial.println(statusPemanas);
Serial.print(" Kipas Panas : ");
Serial.println(statusKipasPanas);
Serial.print(" Kipas Dingin : ");
Serial.println(statusKipasDingin);
Serial.println("==================================================\n");
}
/* ================= FIREBASE ================= */
void kirimFirebase() {
if (Firebase.ready()) {
FirebaseJson json;
/* ===== SENSOR ===== */
json.set("sensor1", tf1);
json.set("sensor2", tf2);
json.set("sensor3", tf3);
/* ===== STATISTIK ===== */
json.set("min", tMin);
json.set("max", tMax);
json.set("rata_rata", tAvg);
/* ===== PREDIKSI ===== */
json.set("prediksi", prediksi);
/* ===== ERROR ===== */
json.set("error", errorTerakhir);
/* ===== STATUS ===== */
json.set("status/pemanas",
digitalRead(RELAY_HEATER) == LOW ? "ON" : "OFF");
json.set("status/kipas_panas",
digitalRead(RELAY_FAN_HOT) == LOW ? "ON" : "OFF");
json.set("status/kipas_dingin",
digitalRead(RELAY_FAN_COLD) == LOW ? "ON" : "OFF");
/* ===== KIRIM KE FIREBASE ===== */
if (Firebase.RTDB.setJSON(&fbdo,
"/fermentasi",
&json)) {
}
else {
Serial.println("Firebase gagal");
Serial.println(fbdo.errorReason());
}
}
}