TKK_E32220549/kode_arduino_nutriplant/kode_arduino_nutriplant.ino

286 lines
7.6 KiB
C++

#include <WiFi.h>
#include <Firebase_ESP_Client.h>
#include <ModbusMaster.h>
#include <HardwareSerial.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "time.h"
// ========== OLED ==========
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// ========== WIFI ==========
#define WIFI_SSID "AGIL"
#define WIFI_PASSWORD "agil2626"
// ========== FIREBASE ==========
#define API_KEY "AIzaSyCefzDqzoTLblHtOsxMiDw3BB1QAox0VIQ"
#define DATABASE_URL "https://nutriplant-579c9-default-rtdb.firebaseio.com/"
#define LEGACY_TOKEN "yEFKFhgPmBoLWrdkRSf2pgiYY3ebS12uWDoikwkl"
// ========== LED & BUTTON ==========
#define LED_PIN 15
#define BUTTON_PIN 14
bool lastButtonState = HIGH;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50;
bool manualMode = false;
// ========== MODBUS RS485 ==========
#define RXD2 16
#define TXD2 17
#define RE_PIN 19
#define DE_PIN 18
HardwareSerial MySerial(2);
ModbusMaster node;
// ========== SOIL SENSOR ==========
#define SOIL_PIN 34
// Firebase objects
FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;
// Variables
uint16_t nitrogen = 0, phosphorus = 0, potassium = 0;
float soilPercent = 0;
int currentPage = 0;
const int totalPages = 4;
unsigned long lastSensorDisplayTime = 0;
// Fungsi Modbus TX control
void preTransmission() {
digitalWrite(DE_PIN, HIGH);
digitalWrite(RE_PIN, HIGH);
}
void postTransmission() {
digitalWrite(DE_PIN, LOW);
digitalWrite(RE_PIN, LOW);
}
// Fungsi teks rata tengah
void centerText(const String &text, int y, int textSize = 2) {
display.setTextSize(textSize);
int16_t x1, y1;
uint16_t w, h;
display.getTextBounds(text, 0, y, &x1, &y1, &w, &h);
int x = (SCREEN_WIDTH - w) / 2;
display.setCursor(x, y);
display.print(text);
}
// Fungsi menampilkan ikon WiFi
void drawWiFiIcon() {
display.drawPixel(118, 0, WHITE);
display.drawLine(117, 1, 119, 1, WHITE);
display.drawLine(116, 2, 120, 2, WHITE);
display.drawLine(115, 3, 121, 3, WHITE);
}
// Fungsi waktu update ke string
String getFormattedTime() {
time_t now;
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
return "unknown";
}
char buffer[30];
strftime(buffer, sizeof(buffer), "%d/%m/%Y %H:%M:%S", &timeinfo);
return String(buffer);
}
void setup() {
Serial.begin(115200);
// OLED
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("OLED failed"));
while (true);
}
display.clearDisplay();
display.setTextColor(WHITE);
centerText("Nutriplant", 20, 2);
display.display();
delay(1500);
display.clearDisplay();
// Pin setup
pinMode(LED_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(RE_PIN, OUTPUT);
pinMode(DE_PIN, OUTPUT);
digitalWrite(RE_PIN, LOW);
digitalWrite(DE_PIN, LOW);
// WiFi
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Connecting to WiFi");
unsigned long start = millis();
while (WiFi.status() != WL_CONNECTED && millis() - start < 10000) {
Serial.print(".");
digitalWrite(LED_PIN, !digitalRead(LED_PIN));
delay(500);
}
digitalWrite(LED_PIN, LOW);
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi connected");
manualMode = false;
configTime(25200, 0, "pool.ntp.org"); // GMT+7 (WIB)
} else {
Serial.println("\nWiFi not connected - Manual Mode");
manualMode = true;
}
// Firebase
config.api_key = API_KEY;
config.database_url = DATABASE_URL;
config.signer.tokens.legacy_token = LEGACY_TOKEN;
Firebase.begin(&config, &auth);
Firebase.reconnectWiFi(true);
// RS485 setup
MySerial.begin(4800, SERIAL_8N1, RXD2, TXD2);
node.begin(1, MySerial);
node.preTransmission(preTransmission);
node.postTransmission(postTransmission);
delay(1000);
}
void loop() {
unsigned long currentMillis = millis();
static unsigned long wifiDisconnectedSince = 0;
// === Read NPK ===
uint8_t result = node.readHoldingRegisters(0x0000, 3);
if (result == node.ku8MBSuccess) {
nitrogen = node.getResponseBuffer(0);
phosphorus = node.getResponseBuffer(1);
potassium = node.getResponseBuffer(2);
if (!manualMode) {
Firebase.RTDB.setInt(&fbdo, "sensor_data/nitrogen", nitrogen);
Firebase.RTDB.setInt(&fbdo, "sensor_data/phosphorus", phosphorus);
Firebase.RTDB.setInt(&fbdo, "sensor_data/potassium", potassium);
Firebase.RTDB.setString(&fbdo, "sensor_data/error_npk", "");
// Set waktu update
String timeNow = getFormattedTime();
Firebase.RTDB.setString(&fbdo, "sensor_data/update", timeNow);
}
} else {
if (!manualMode) {
Firebase.RTDB.setString(&fbdo, "sensor_data/error_npk", "Modbus Error: " + String(result));
}
}
// === Read Soil Moisture ===
int soilAnalog = analogRead(SOIL_PIN);
soilPercent = map(soilAnalog, 4095, 1500, 0, 100);
soilPercent = constrain(soilPercent, 0, 100);
if (!manualMode) {
Firebase.RTDB.setFloat(&fbdo, "sensor_data/soil_moisture", soilPercent);
// Update waktu juga
String timeNow = getFormattedTime();
Firebase.RTDB.setString(&fbdo, "sensor_data/update", timeNow);
}
// === Tombol untuk reconnect WiFi ===
bool reading = digitalRead(BUTTON_PIN);
if (reading != lastButtonState) {
lastDebounceTime = currentMillis;
}
if ((currentMillis - lastDebounceTime) > debounceDelay) {
if (lastButtonState == HIGH && reading == LOW) {
Serial.println("Reconnect button pressed");
display.clearDisplay();
centerText("Connecting WiFi", 10);
display.display();
WiFi.disconnect();
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
unsigned long reconnectStart = millis();
bool reconnected = false;
while (millis() - reconnectStart < 10000) {
if (WiFi.status() == WL_CONNECTED) {
reconnected = true;
break;
}
delay(500);
}
manualMode = !reconnected;
display.clearDisplay();
if (reconnected) {
centerText("WiFi Connected", 10);
Serial.println("Reconnected to WiFi, online mode.");
} else {
centerText("Reconnect Failed", 10);
Serial.println("Reconnect failed, still in manual mode.");
}
display.display();
delay(1500);
}
}
lastButtonState = reading;
// === Auto-switch ke Manual jika WiFi putus ===
if (WiFi.status() != WL_CONNECTED) {
if (wifiDisconnectedSince == 0) {
wifiDisconnectedSince = currentMillis;
} else if (!manualMode && currentMillis - wifiDisconnectedSince >= 10000) {
manualMode = true;
display.clearDisplay();
centerText("No WiFi - Manual", 10);
display.display();
Serial.println("Auto-switched to Manual Mode");
}
} else {
wifiDisconnectedSince = 0;
}
// === Tampilkan Sensor Setiap 5 Detik ===
if (currentMillis - lastSensorDisplayTime >= 5000) {
lastSensorDisplayTime = currentMillis;
currentPage = (currentPage + 1) % totalPages;
display.clearDisplay();
switch (currentPage) {
case 0:
centerText("N (mg/kg)", 5, 2);
centerText(String(nitrogen), 30, 3);
break;
case 1:
centerText("P (mg/kg)", 5, 2);
centerText(String(phosphorus), 30, 3);
break;
case 2:
centerText("K (mg/kg)", 5, 2);
centerText(String(potassium), 30, 3);
break;
case 3:
centerText("Soil Moisture", 5, 1);
centerText(String(soilPercent, 1) + "%", 30, 3);
break;
}
if (!manualMode) {
drawWiFiIcon();
}
display.display();
}
digitalWrite(LED_PIN, manualMode ? LOW : HIGH);
delay(100);
}