CHILLER MONITORING DASHBOARD

CONNECTING TO EMQX BROKER...

TEMPERATURE & HUMIDITY

DEW POINT
00.0 °C
NORMAL
🌡️
Water In
00.0 °C
Water Out
00.0 °C
Air Temp
00.0 °C
Humidity
00.0 %
Dew Point
00.0 °C

PRESSURE GAUGE

📊
0.0 hPa

FLOW METER

💧
0.0 L/min

DIGITAL COMPASS

🧭
Azimuth:
Direction: NORTH

EQUIPMENT STATUS

⚙️
COMPRESSOR: OFF
WATER PUMP: OFF
Water Temp In: 0.0°C
Water Temp Out: 0.0°C

MAGNETOMETER DATA

🧲
Magnetic X: 0.0 µT
Magnetic Y: 0.0 µT
Magnetic Z: 0.0 µT
Magnetic Strength: 0.0 µT

MQTT PROTOCOL & DATA STRUCTURE INFORMATION

📡

MQTT CONNECTION DETAILS

Broker URL: broker.emqx.io
Protocol: TCP (Port 1883)
Port: 1883
Keep Alive: 30 seconds
Reconnect Period: 2000 ms (Exponential backoff)
Connect Timeout: 10000 ms
Topic: chiller
QoS Level: 0 (At most once)

CONNECTION STATUS DETAILS

Client ID: web-client-xxxxxxxx
Last Message: Never
Messages Received: 0
Connection Uptime: 00:00:00
Reconnection Attempts: 0
Last Error: None
Ping: -- ms
Data Rate: -- msg/s
Connection Quality: --

DATA STRUCTURE FROM ESP32

Data Format: $Chiller,tempIn,tempOut,flowRate,airTemp,humidity,pressure,compressor,pump,mx,my,mz,azimuth

FIELD DESCRIPTIONS:

$Chiller Message identifier prefix
tempIn Water inlet temperature (°C)
tempOut Water outlet temperature (°C)
flowRate Water flow rate (L/min)
airTemp Ambient air temperature (°C)
humidity Relative humidity (%)
pressure System pressure (hPa)
compressor Compressor status (0=OFF, 100=ON)
pump Water pump status (0=OFF, 100=ON)
mx Magnetic field X-axis (µT)
my Magnetic field Y-axis (µT)
mz Magnetic field Z-axis (µT)
azimuth Compass heading (degrees)

ESP32 SENSOR CONFIGURATION

  • Temperature Sensors: DS18B20 (Water In/Out), DHT22 (Air Temp & Humidity)
  • Pressure Sensor: BMP180
  • Magnetometer: HMC5883L
  • Flow Meter: Simulated (16-24 L/min ±20%)
  • Relay Control: Compressor (Pin 25), Rotating Lamp (Pin 26)
  • Dry Contacts: D32 (Compressor), D33 (Water Pump)
  • Status LED: D14 (Active Low, blinks on successful MQTT transmission)
  • Serial Communication: RX2/TX2 for additional data transmission
×
CONNECTING TO EMQX BROKER...
/* //Terpasang di PCBA untuk CHILLER CONTROLLER //Sejak 15 Oktober 2025 //Modifikasi: Tambah sensor BMP180 & HMC5883, MQTT tiap 500ms, Simulasi FlowMeter //Ditambahkan D14 Active Low berkedip 30mS setiap data berhasil dikirim via MQTT //Ditambahkan komunikasi serial melalui RX2/TX2 #include #include #include #include #include #include #include #include #include #include #include // OLED display TWI address #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // Define pins #define DHTPIN 19 #define ONE_WIRE_BUS 23 #define D32 32 #define D33 33 #define RELAY_COMPRESSOR 25 #define RELAY_ROTATING_LAMP 26 #define LED_BUILTIN 2 #define D14 27 // Added D14 pin definition // Define DHT type #define DHTTYPE DHT22 DHT dht(DHTPIN, DHTTYPE); // OneWire bus OneWire oneWire(ONE_WIRE_BUS); DallasTemperature sensors(&oneWire); // Sensor objects Adafruit_BMP085 bmp; Adafruit_HMC5883_Unified mag = Adafruit_HMC5883_Unified(12345); // WiFi and MQTT - SESUAIKAN DENGAN HTML const char *ssid = "Kawata Hotspot"; const char *password = "K1htsPt0123@!"; const char *mqttBroker = "broker.hivemq.com"; // GANTI KE HIVEMQ const int mqttPort = 8883; // PORT SSL HIVEMQ const char *mqttTopic = "chiller"; const char *mqttClientId = "ESP32_Chiller_Monitor"; WiFiClient espClient; PubSubClient client(espClient); // Variables - SESUAIKAN DENGAN FORMAT HTML float tempIn, tempOut, airTemp, humidity, pressure; int compressorStatus, pumpStatus; float mx, my, mz, azimuth; float flowRate = 0.0; unsigned long lastMsg = 0; unsigned long lastBlink = 0; bool blinkState = false; char msg[250]; bool bmpAvailable = false; bool hmcAvailable = false; // Serial2 for RX2/TX2 communication #define SERIAL2_BAUD 9600 HardwareSerial Serial2(2); // Use UART2 (RX2=16, TX2=17) // Simulasi flow meter: 20 L/min ±20% float generateRandomFlowRate() { float minFlow = 16.0; float maxFlow = 24.0; return minFlow + (random(0, 10001) / 10000.0) * (maxFlow - minFlow); } // Fungsi untuk kalkulasi magnetic strength float calculateMagneticStrength(float x, float y, float z) { return sqrt(x*x + y*y + z*z); } void setup() { Serial.begin(9600); Serial2.begin(SERIAL2_BAUD); // Initialize Serial2 WiFi.setSleep(false); // Jaga koneksi WiFi tetap aktif // Initialize OLED if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { Serial.println(F("SSD1306 allocation failed")); while (true); } display.display(); delay(2000); display.clearDisplay(); // Initialize sensors dht.begin(); sensors.begin(); // Initialize BMP180 bmpAvailable = bmp.begin(); if (!bmpAvailable) { Serial.println("BMP180 not found!"); } else { Serial.println("BMP180 initialized"); } // Initialize HMC5883 hmcAvailable = mag.begin(); if (!hmcAvailable) { Serial.println("HMC5883 not found!"); } else { Serial.println("HMC5883 initialized"); } // Initialize pins pinMode(D32, INPUT_PULLUP); digitalWrite(D32, HIGH); pinMode(D33, INPUT_PULLUP); digitalWrite(D33, HIGH); pinMode(RELAY_COMPRESSOR, OUTPUT); digitalWrite(RELAY_COMPRESSOR, HIGH); pinMode(RELAY_ROTATING_LAMP, OUTPUT); digitalWrite(RELAY_ROTATING_LAMP, HIGH); pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, HIGH); pinMode(D14, OUTPUT); digitalWrite(D14, HIGH); // Initialize D14 as Active Low (HIGH = off) // Setup WiFi setup_wifi(); // Setup MQTT client.setServer(mqttBroker, mqttPort); client.setCallback(callback); Serial.println("System initialized with Serial2 support"); Serial2.println("Serial2 initialized"); // Send test message on Serial2 // Initial sensor reading readSensors(); } void setup_wifi() { Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); unsigned long startAttemptTime = millis(); const unsigned long wifiTimeout = 30000; // 30 seconds timeout while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < wifiTimeout) { delay(500); Serial.print("."); digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); } if (WiFi.status() != WL_CONNECTED) { Serial.println("\nWiFi Failed. Restarting..."); ESP.restart(); } digitalWrite(LED_BUILTIN, HIGH); Serial.println("\nWiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); } void reconnect() { const byte maxRetries = 10; byte retryCount = 0; while (!client.connected() && retryCount < maxRetries) { Serial.print("Attempting MQTT connection to HiveMQ..."); // Generate unique client ID String clientId = String(mqttClientId) + "_" + String(random(0xffff), HEX); if (client.connect(clientId.c_str())) { Serial.println("connected to HiveMQ"); // Subscribe jika diperlukan // client.subscribe("chiller/control"); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(". Retrying in 2 seconds..."); retryCount++; delay(2000); } } if (!client.connected()) { Serial.println("MQTT connection failed after maximum retries"); } } void callback(char* topic, byte* payload, unsigned int length) { // Handle incoming MQTT messages (untuk kontrol jika diperlukan) Serial.print("Message arrived ["); Serial.print(topic); Serial.print("]: "); String message; for (int i = 0; i < length; i++) { message += (char)payload[i]; } Serial.println(message); } void readSensors() { // Baca sensor DHT22 airTemp = dht.readTemperature(); humidity = dht.readHumidity(); // Cek jika pembacaan DHT gagal if (isnan(airTemp) || isnan(humidity)) { Serial.println("Failed to read from DHT sensor!"); airTemp = 25.0; // Default value humidity = 50.0; // Default value } // Baca sensor suhu DS18B20 sensors.requestTemperatures(); tempIn = sensors.getTempCByIndex(0); tempOut = sensors.getTempCByIndex(1); // Cek jika pembacaan suhu gagal if (tempIn == DEVICE_DISCONNECTED_C) tempIn = 20.0; if (tempOut == DEVICE_DISCONNECTED_C) tempOut = 18.0; // Baca status dry contact compressorStatus = digitalRead(D32) ? 0 : 1; // 0=OFF, 1=ON pumpStatus = digitalRead(D33) ? 0 : 1; // 0=OFF, 1=ON // Baca pressure dari BMP180 if (bmpAvailable) { pressure = bmp.readPressure() / 100.0; // Convert Pa to hPa } else { pressure = 1013.25; // Default sea level pressure } // Baca magnetometer if (hmcAvailable) { sensors_event_t event; mag.getEvent(&event); mx = event.magnetic.x; my = event.magnetic.y; mz = event.magnetic.z; // Calculate azimuth (compass heading) float heading = atan2(my, mx); if (heading < 0) heading += 2 * PI; azimuth = heading * 180 / PI; } else { mx = my = mz = azimuth = 0.0; } // Generate flow rate flowRate = generateRandomFlowRate(); } void sendDataViaSerial2() { // Format data untuk Serial2 String serial2Data = "$Chiller," + String(tempIn, 1) + "," + String(tempOut, 1) + "," + String(flowRate, 1) + "," + String(airTemp, 1) + "," + String(humidity, 1) + "," + String(pressure, 1) + "," + String(compressorStatus) + "," + String(pumpStatus) + "," + String(mx, 1) + "," + String(my, 1) + "," + String(mz, 1) + "," + String(azimuth, 1); Serial2.println(serial2Data); Serial.println("Sent to Serial2: " + serial2Data); } void updateRelays() { // Kontrol compressor berdasarkan suhu if (tempIn < 5.0 || tempOut < 5.0) { digitalWrite(RELAY_COMPRESSOR, LOW); // ON - antifreezing protection } else if (tempIn > 25.0) { digitalWrite(RELAY_COMPRESSOR, LOW); // ON - cooling needed } else { digitalWrite(RELAY_COMPRESSOR, HIGH); // OFF } // Kontrol rotating lamp (contoh: nyala saat compressor aktif) digitalWrite(RELAY_ROTATING_LAMP, digitalRead(RELAY_COMPRESSOR) ? HIGH : LOW); } void updateDisplay() { display.clearDisplay(); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(0, 0); display.println("CHILLER MONITOR DASHBOARD"); display.println("IN:" + String(tempIn,1) + "C OUT:" + String(tempOut,1) + "C"); display.println("Flow:" + String(flowRate,1) + "L/min Air:" + String(airTemp,1) + "C"); display.println("Hum:" + String(humidity,1) + "% Press:" + String(pressure,1) + "hPa"); display.println("Comp:" + String(compressorStatus) + " Pump:" + String(pumpStatus)); display.println("Mag:" + String(mx,1) + "," + String(my,1) + "," + String(mz,1)); display.println("Azimuth:" + String(azimuth,1) + "deg"); display.println("MQTT: " + String(client.connected() ? "OK" : "DISCONNECTED")); display.display(); } void loop() { // Cek koneksi WiFi if (WiFi.status() != WL_CONNECTED) { Serial.println("WiFi lost. Reconnecting..."); setup_wifi(); } // Cek koneksi MQTT if (!client.connected()) { reconnect(); } client.loop(); unsigned long now = millis(); // Handle LED blink timeout if (blinkState && (now - lastBlink >= 30)) { digitalWrite(D14, HIGH); blinkState = false; } // Kirim data setiap 500ms (sesuai dengan HTML yang expect data real-time) if (now - lastMsg >= 500) { lastMsg = now; // Baca semua sensor readSensors(); // Update relay control updateRelays(); // Format data SESUAI DENGAN HTML: $Chiller,tempIn,tempOut,flowRate,airTemp,humidity,pressure,compressor,pump,mx,my,mz,azimuth snprintf(msg, sizeof(msg), "$Chiller,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%d,%d,%.1f,%.1f,%.1f,%.1f", tempIn, tempOut, flowRate, airTemp, humidity, pressure, compressorStatus, pumpStatus, mx, my, mz, azimuth); Serial.print("Publishing: "); Serial.println(msg); // Publish ke MQTT bool publishSuccess = client.publish(mqttTopic, msg); // Kirim data via Serial2 juga sendDataViaSerial2(); // Blink LED jika publish berhasil if (publishSuccess) { digitalWrite(D14, LOW); // Active Low blink blinkState = true; lastBlink = millis(); } // Update display updateDisplay(); } // Handle incoming Serial2 data if (Serial2.available()) { String received = Serial2.readStringUntil('\n'); received.trim(); Serial.println("Received from Serial2: " + received); // Process commands from Serial2 if needed if (received == "RESTART") { ESP.restart(); } } // Blink built-in LED untuk indikasi aktivitas static unsigned long lastLedToggle = 0; if (now - lastLedToggle >= 1000) { digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); lastLedToggle = now; } delay(10); // Small delay untuk stability } */