#include <WiFi.h>
#include "ThingsBoard.h"
#include <LiquidCrystal_I2C.h>
#include "time.h"
#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
#define TOKEN "KjoeKD3MuPiXiyFYPhe2"
#define THINGSBOARD_SERVER "thingsboard.cloud"
#define WIFI_SSID "Wokwi-GUEST"
#define WIFI_PASSWORD ""
WiFiClient espClient;
ThingsBoard tb(espClient);
int status = WL_IDLE_STATUS;
// Array with LEDs that should be controlled from ThingsBoard, one by one
uint8_t leds_control[] = {0, 5, 23, 4, 2, 18};
#define Termo 33
#define Hijau 27
#define Merah 14
#define Kuning 12
#define Biru 13
#define Shijau 23
#define Smerah 5
#define Skuning 4
#define Sbiru 2
#define saklar 18
#define Lmanual 19
#define Echo 35
#define Trig 32
// Asumsi jarak sensor ultrasonik = tinggi kolam = 130 cm diukur dari dasar kolam
const int TINGGI_SENSOR = 130; // cm
const int BATAS_BAWAH_KOLAM = 80 ; // cm
const int BATAS_ATAS_KOLAM = 100; // cm
const float BETA = 3950;
float posisiSaklar;
float Suhu;
long interval = 10000;
struct tm timeinfo;
const int NIGHT_HOUR_START = 18; // 18.00 WIB
const int NIGHT_HOUR_END = 6; // 05.00 WIB
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 7 * 60 * 60; // GMT + 7 (WIB)
const int daylightOffset_sec = 0;
LiquidCrystal_I2C lcd(0x27,20,4);
// Main application loop delay
int quant = 20;
// Initial period of LED cycling.
int led_delay = 1000;
// Period of sending a temperature/humidity data.
int send_delay = 2000;
// Time passed after LED was turned ON, milliseconds.
int led_passed = 0;
// Time passed after temperature/humidity data was sent, milliseconds.
int send_passed = 0;
// Set to true if application is subscribed for the RPC messages.
bool subscribed = false;
// LED number that is currenlty ON.
int current_led = 0;
RPC_Response processDelayChange(const RPC_Data &data)
{
Serial.println("Received the set delay RPC method");
// Process data
led_delay = data;
Serial.print("Set new delay: ");
Serial.println(led_delay);
return String(led_delay);
}
RPC_Response processGetDelay(const RPC_Data &data)
{
Serial.println("Received the get value method");
return String(led_delay);
}
RPC_Response processSetGpioState(const RPC_Data &data)
{
Serial.println("Received the set GPIO RPC method");
int pin = data["pin"];
bool enabled = data["enabled"];
if (pin < COUNT_OF(leds_control)) {
Serial.print("Setting LED ");
Serial.print(pin);
Serial.print(" to state ");
Serial.println(enabled);
digitalWrite(leds_control[pin], enabled);
}
return String("{\"" + String(pin) + "\": " + String(enabled?"true":"false") + "}");
//return RPC_Response(data["pin"], (bool)data["enabled"]);
}
RPC_Response processGetGpioState(const RPC_Data &data)
{
Serial.println("Received the get GPIO RPC method");
String respStr = "{";
for (size_t i = 0; i < COUNT_OF(leds_control); ++i) {
int pin = leds_control[i];
Serial.print("Getting LED ");
Serial.print(pin);
Serial.print(" state ");
bool ledState = digitalRead(pin);
Serial.println(ledState);
respStr += String("\"" + String(i) + "\": " + String(ledState?"true":"false") + ", ");
}
respStr = respStr.substring(0, respStr.length() - 2);
respStr += "}";
return respStr;
}
// RPC handlers
RPC_Callback callbacks[] = {
{ "setValue", processDelayChange },
{ "getValue", processGetDelay },
{ "setGpioStatus", processSetGpioState },
{ "getGpioStatus", processGetGpioState },
};
void InitWiFi()
{
Serial.println("Connecting to AP ....");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("Connected to AP");
}
void reconnect()
{
status = WiFi.status();
if ( status != WL_CONNECTED)
{
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("Connected to AP");
}
}
void setup()
{
Serial.begin(9600);
// Wi-Fi Initialitaztion
InitWiFi();
// LCD Initialization
lcd.init();
lcd.backlight();
lcd.setCursor(0,0);
lcd.print(">>>IoT4-16 Team 4<<<");
lcd.setCursor(0,1);
lcd.print("Penyetabil Suhu pada");
lcd.setCursor(0,2);
lcd.print(" Budidaya ");
lcd.setCursor(0,3);
lcd.print(" Udang Vaname ");
delay(3000);
lcd.setCursor(0,0);
lcd.print(">>>IoT4-16 Team 4<<<");
lcd.setCursor(0,1);
lcd.print("Samuel - Turikan");
lcd.setCursor(0,2);
lcd.print("Jason - Thoriq");
lcd.setCursor(0,3);
lcd.print("Jamil - Anton");
delay(3000);
lcd.setCursor(0,0);
lcd.print(" ");
lcd.setCursor(0,1);
lcd.print(" ");
lcd.setCursor(0,2);
lcd.print(" ");
lcd.setCursor(0,3);
lcd.print(" ");
// NTP Initial Setup
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
analogReadResolution(10);
pinMode(Termo, INPUT);
pinMode(Hijau, OUTPUT);
pinMode(Merah, OUTPUT);
pinMode(Kuning, OUTPUT);
pinMode(Biru, OUTPUT);
// pinMode(Shijau, INPUT);
// pinMode(Smerah, INPUT);
// pinMode(Skuning, INPUT);
// pinMode(Sbiru, INPUT);
// pinMode(saklar, INPUT);
pinMode(Lmanual, OUTPUT);
pinMode(Trig, OUTPUT);
pinMode(Echo, INPUT);
// Pinconfig
for (size_t i = 0; i < COUNT_OF(leds_control); ++i) {
pinMode(leds_control[i], OUTPUT);
}
}
float cekTinggiKolam(){
digitalWrite(Trig, HIGH);
delayMicroseconds(10);
digitalWrite(Trig, LOW);
int durasi = pulseIn(Echo,HIGH);
float jarak_sensor_permukaan = 0.034 * durasi / 2;
float tinggi_kolam = TINGGI_SENSOR - jarak_sensor_permukaan;
tb.sendTelemetryFloat("Tinggi air", tinggi_kolam);
Serial.print("Kedalaman Kolam :");
Serial.print(tinggi_kolam);
Serial.println(" cm");
return tinggi_kolam;
}
boolean isAirPenuh(){
int tinggi = cekTinggiKolam();
return tinggi >= BATAS_ATAS_KOLAM;
}
boolean isAirKurang(){
int tinggi = cekTinggiKolam();
return tinggi <= BATAS_BAWAH_KOLAM;
}
boolean isMalam(){
getLocalTime(&timeinfo);
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
char timeHour[3];
strftime(timeHour,3, "%H", &timeinfo);
if((int)timeHour >= NIGHT_HOUR_START || (int)timeHour <= NIGHT_HOUR_END){
Serial.println("MALAM");
return true;
}
Serial.println("TIDAK MALAM");
return false;
}
void tambahAir()
{
posisiSaklar = digitalRead((saklar));
if(!posisiSaklar) {
while(!isAirPenuh()){
digitalWrite(Biru, HIGH);
Serial.println("Pompa Tambah Air Menyala");
lcd.setCursor(0,1);
lcd.print("Intake Pump : ON ");
tb.sendTelemetryInt("Pompa tambah air", 1);
// force manual
posisiSaklar = digitalRead((saklar));
if(posisiSaklar){
stopBuangAir();
stopTambahAir();
return;
}
// check suhu ideal
if(cekSuhuAir() > 24 && cekSuhuAir() < 32){
stopTambahAir();
shadingBuka();
return;
}
}
stopTambahAir();
buangAir();
}
else{
digitalWrite(Biru, HIGH);
Serial.println("Pompa Tambah Air Menyala");
lcd.setCursor(0,1);
lcd.print("Intake Pump : ON ");
tb.sendTelemetryInt("Pompa tambah air", 1);
}
}
void stopTambahAir()
{
digitalWrite(Biru, LOW);
Serial.println("Pompa Tambah Air Mati");
lcd.setCursor(0,1);
lcd.print("Intake Pump : OFF ");
tb.sendTelemetryInt("Pompa tambah air", 0);
}
void buangAir()
{
posisiSaklar = digitalRead((saklar));
if(!posisiSaklar) {
while(!isAirKurang()){
digitalWrite(Kuning, HIGH);
Serial.println("Pompa Buang Air Menyala");
lcd.setCursor(0,2);
lcd.print("Drain Pump : ON ");
tb.sendTelemetryInt("Pompa buang air", 1);
// force manual
posisiSaklar = digitalRead((saklar));
if(posisiSaklar){
stopBuangAir();
stopTambahAir();
return;
}
// check suhu ideal
if(cekSuhuAir() > 24 && cekSuhuAir() < 32){
stopBuangAir();
shadingTutup();
return;
}
}
stopBuangAir();
tambahAir();
}
}
void stopBuangAir()
{
digitalWrite(Kuning, LOW);
Serial.println("Pompa Buang Air Mati");
lcd.setCursor(0,2);
lcd.print("Drain Pump : OFF ");
tb.sendTelemetryInt("Pompa buang air", 0);
}
void shadingBuka()
{
long previousMillisBuka = 0;
unsigned long currentMillisBuka = millis();
digitalWrite(Hijau, HIGH);
int shadingBukaState = HIGH;
Serial.println("Shading net membuka");
lcd.setCursor(0,3);
lcd.print(" Open Shading Net ");
tb.sendTelemetryInt("Shading Net", 1);
if(currentMillisBuka - previousMillisBuka > interval) {
previousMillisBuka = currentMillisBuka;
if (shadingBukaState == HIGH){
tb.sendTelemetryInt("Shading Net", 0);
digitalWrite(Hijau, LOW);
shadingBukaState = LOW;
}
}
}
void shadingTutup()
{
long previousMillisTutup = 0;
unsigned long currentMillisTutup = millis();
digitalWrite(Merah, HIGH);
int shadingTutupState = HIGH;
Serial.println("Shading net menutup");
lcd.setCursor(0,3);
lcd.print(" Close Shading Net ");
tb.sendTelemetryInt("Shading Net", 0);
if(currentMillisTutup - previousMillisTutup > interval) {
previousMillisTutup = currentMillisTutup;
if (shadingTutupState == HIGH){
tb.sendTelemetryInt("Shading Net", 0);
digitalWrite(Merah, LOW);
shadingTutupState = LOW;
}
}
}
float cekSuhuAir()
{
int analogValue = analogRead(Termo);
Suhu = 1 / (log(1 / (1023. / analogValue - 1)) / BETA + 1.0 / 298.15) - 273.15;
lcd.setCursor(0,0);
lcd.print("Temp:");
lcd.print(Suhu);
lcd.print((char)223);
lcd.print("C");
Serial.println((String)"Suhu air tambak : " + Suhu + " ℃");
tb.sendTelemetryFloat("Suhu air", Suhu);
return Suhu;
}
void loop()
{
delay(quant);
led_passed += quant;
send_passed += quant;
if (WiFi.status() != WL_CONNECTED)
{
reconnect();
}
if (!tb.connected())
{
Serial.print("Connecting to: ");
Serial.print(THINGSBOARD_SERVER);
Serial.print(" with token ");
Serial.println(TOKEN);
if (!tb.connect(THINGSBOARD_SERVER, TOKEN))
{
Serial.println("Failed to connect");
return;
}
}
// Subscribe for RPC, if needed
if (!subscribed) {
Serial.println("Subscribing for RPC... ");
// Perform a subscription. All consequent data processing will happen in
// callbacks as denoted by callbacks[] array.
if (!tb.RPC_Subscribe(callbacks, COUNT_OF(callbacks))) {
Serial.println("Failed to subscribe for RPC");
return;
}
Serial.println("Subscribe done");
subscribed = true;
}
cekSuhuAir();
cekTinggiKolam();
posisiSaklar = digitalRead((saklar));
tb.sendTelemetryInt("Automatic", posisiSaklar);
Serial.println("====================");
Serial.print("Saklar : ");
Serial.println(posisiSaklar);
// MANUAL
if(posisiSaklar == HIGH) {
stopBuangAir();
stopTambahAir();
lcd.setCursor(14,0);
lcd.print("MANUAL");
digitalWrite(Lmanual, HIGH);
if (digitalRead(Shijau) == HIGH){
Serial.println("Tombol Merah tertekan");
shadingBuka();
}
if (digitalRead(Smerah) == HIGH){
Serial.println("Tombol Merah tertekan");
shadingTutup();
}
if (digitalRead(Skuning) == HIGH){
Serial.println("Tombol Kuning tertekan");
buangAir();
}
if (digitalRead(Sbiru) == HIGH){
Serial.println("Tombol Biru tertekan");
tambahAir();
}
}
// OTOMATIS
else {
digitalWrite(Lmanual, LOW);
lcd.setCursor(14,0);
lcd.print(" AUTO ");
if(Suhu > 24 && Suhu < 32){
stopTambahAir();
stopBuangAir();
}
if(Suhu < 24){
// NIGHT
if(isMalam()){
// stopBuangAir();
tambahAir();
}
// DAY
else{
// stopTambahAir();
buangAir();
}
}
if(Suhu > 32){
shadingTutup();
tambahAir();
stopBuangAir();
}
}
delay(1000);
tb.loop();
}Range : 2 - 130cm
Untuk RPC, Virtual Pin 5 di Dashboard
harus diposisi ON kan sebelum remote