// ====== Core libs ======
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"
// ====== Display ======
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// ====== DHT ======
#include "DHT.h"
// ====== Wi-Fi / AIO Credentials ======
#define WIFI_SSID "Wokwi-GUEST"
#define WIFI_PASS ""
#define AIO_SERVER "io.adafruit.com"
#define AIO_SERVERPORT 8883 // Secure MQTT (TLS)
#define IO_USERNAME "Username" // <-- your Adafruit IO username
#define IO_KEY "Key" // <-- your Adafruit IO key
// ====== TLS root CA for io.adafruit.com ======
static const char* AIO_ROOT_CA PROGMEM =
"-----BEGIN CERTIFICATE-----\n"
"MIIEjTCCA3WgAwIBAgIQDQd4KhM/xvmlcpbhMf/ReTANBgkqhkiG9w0BAQsFADBh\n"
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n"
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH\n"
"MjAeFw0xNzExMDIxMjIzMzdaFw0yNzExMDIxMjIzMzdaMGAxCzAJBgNVBAYTAlVT\n"
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n"
"b20xHzAdBgNVBAMTFkdlb1RydXN0IFRMUyBSU0EgQ0EgRzEwggEiMA0GCSqGSIb3\n"
"DQEBAQUAA4IBDwAwggEKAoIBAQC+F+jsvikKy/65LWEx/TMkCDIuWegh1Ngwvm4Q\n"
"yISgP7oU5d79eoySG3vOhC3w/3jEMuipoH1fBtp7m0tTpsYbAhch4XA7rfuD6whU\n"
"gajeErLVxoiWMPkC/DnUvbgi74BJmdBiuGHQSd7LwsuXpTEGG9fYXcbTVN5SATYq\n"
"DfbexbYxTMwVJWoVb6lrBEgM3gBBqiiAiy800xu1Nq07JdCIQkBsNpFtZbIZhsDS\n"
"fzlGWP4wEmBQ3O67c+ZXkFr2DcrXBEtHam80Gp2SNhou2U5U7UesDL/xgLK6/0d7\n"
"6TnEVMSUVJkZ8VeZr+IUIlvoLrtjLbqugb0T3OYXW+CQU0kBAgMBAAGjggFAMIIB\n"
"PDAdBgNVHQ4EFgQUlE/UXYvkpOKmgP792PkA76O+AlcwHwYDVR0jBBgwFoAUTiJU\n"
"IBiV5uNu5g/6+rkS7QYXjzkwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsG\n"
"AQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMDQGCCsGAQUFBwEB\n"
"BCgwJjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEIGA1Ud\n"
"HwQ7MDkwN6A1oDOGMWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEds\n"
"b2JhbFJvb3RHMi5jcmwwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEW\n"
"HGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDQYJKoZIhvcNAQELBQADggEB\n"
"AIIcBDqC6cWpyGUSXAjjAcYwsK4iiGF7KweG97i1RJz1kwZhRoo6orU1JtBYnjzB\n"
"c4+/sXmnHJk3mlPyL1xuIAt9sMeC7+vreRIF5wFBC0MCN5sbHwhNN1JzKbifNeP5\n"
"ozpZdQFmkCo+neBiKR6HqIA+LMTMCMMuv2khGGuPHmtDze4GmEGZtYLyF8EQpa5Y\n"
"jPuV6k2Cr/N3XxFpT3hRpt/3usU/Zb9wfKPtWpoznZ4/44c1p9rzFcZYrWkj3A+7\n"
"TNBJE0GmP2fhXhP1D/XVfIW/h0yCJGEiV9Glm/uGOa3DXHlmbAcxSyCRraG+ZBkA\n"
"7h4SeM6Y8l/7MBRpPCz6l8Y=\n"
"-----END CERTIFICATE-----\n";
// ====== MQTT client ======
WiFiClientSecure secureClient;
Adafruit_MQTT_Client mqtt(&secureClient, AIO_SERVER, AIO_SERVERPORT, IO_USERNAME, IO_KEY);
// ====== MQTT Feeds ======
Adafruit_MQTT_Publish statusPub = Adafruit_MQTT_Publish(&mqtt, IO_USERNAME "/feeds/status");
Adafruit_MQTT_Publish valuePub = Adafruit_MQTT_Publish(&mqtt, IO_USERNAME "/feeds/value");
Adafruit_MQTT_Publish percentPub = Adafruit_MQTT_Publish(&mqtt, IO_USERNAME "/feeds/percent");
Adafruit_MQTT_Publish temperaturePub = Adafruit_MQTT_Publish(&mqtt, IO_USERNAME "/feeds/temperature");
Adafruit_MQTT_Publish humidityPub = Adafruit_MQTT_Publish(&mqtt, IO_USERNAME "/feeds/humidity");
// ====== OLED ======
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// ====== Pins ======
#define RED 18
#define GREEN 19
#define BLUE 23
#define BUZZER 14
#define POT 34
// ====== DHT ======
#define DHTPIN 13
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);
// ====== LED Type ======
const bool COMMON_ANODE = false; // true if LED common goes to 3.3V
// ====== Thresholds ======
#define DRY_THRESHOLD 1365 // ~33% of 4095
#define WET_THRESHOLD 2730 // ~66% of 4095
// ====== Publish cadence / change detection ======
const unsigned long PUBLISH_MIN_MS = 2000;
unsigned long lastPublishMs = 0;
String lastStatus = "";
int lastValue = -1;
int lastPct = -1;
// ====== Helpers ======
void setColor(bool r, bool g, bool b) {
auto drive = [](int pin, bool on, bool commonAnode) {
if (commonAnode) digitalWrite(pin, on ? LOW : HIGH);
else digitalWrite(pin, on ? HIGH : LOW);
};
drive(RED, r, COMMON_ANODE);
drive(GREEN, g, COMMON_ANODE);
drive(BLUE, b, COMMON_ANODE);
}
void showMessage(const __FlashStringHelper* message, int value, int pct) {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 8);
display.println(message);
display.setTextSize(1);
display.setCursor(0, 40);
display.print(F("Value: "));
display.println(value);
display.setCursor(0, 52);
display.print(F("Percent: "));
display.print(pct);
display.println('%');
display.display();
}
void mqttConnect() {
if (mqtt.connected()) return;
int8_t ret;
uint8_t retries = 5;
while ((ret = mqtt.connect()) != 0) {
mqtt.disconnect();
delay(2000);
if (!--retries) return;
}
}
void publishIfChanged(const String& status, int value, int pct, float tC, float h) {
unsigned long now = millis();
bool changed = (status != lastStatus) || (value != lastValue) || (pct != lastPct);
bool timeOk = (now - lastPublishMs) >= PUBLISH_MIN_MS;
if (!timeOk) return;
if (!mqtt.connected()) mqttConnect();
if (!mqtt.connected()) return;
if (changed) {
statusPub.publish(status.c_str());
valuePub.publish((int32_t)value); // explicit to avoid overload ambiguity
percentPub.publish((int32_t)pct); // explicit to avoid overload ambiguity
lastStatus = status;
lastValue = value;
lastPct = pct;
}
if (!isnan(tC)) temperaturePub.publish((double)tC);
if (!isnan(h)) humidityPub.publish((double)h);
lastPublishMs = now;
}
// ====== Setup ======
void setup() {
Serial.begin(115200);
pinMode(RED, OUTPUT);
pinMode(GREEN, OUTPUT);
pinMode(BLUE, OUTPUT);
pinMode(BUZZER, OUTPUT);
setColor(false, false, false);
#if defined(ESP32)
analogReadResolution(12); // 0..4095
#endif
// I2C and OLED
Wire.begin(21, 22); // SDA=21, SCL=22 on ESP32 by default
if (display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println(F("WiFi connecting..."));
display.display();
}
// Wi-Fi
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(250);
}
// Secure MQTT
secureClient.setCACert(AIO_ROOT_CA);
// If TLS is problematic in sim, you can temporarily use:
// secureClient.setInsecure(); // (not recommended for production)
// DHT
dht.begin();
if (display.width()) {
display.clearDisplay();
display.setCursor(0, 0);
display.println(F("WiFi OK. MQTT..."));
display.display();
}
mqttConnect();
if (display.width()) {
display.clearDisplay();
display.setCursor(0, 0);
display.println(mqtt.connected() ? F("MQTTS connected") : F("MQTTS failed"));
display.display();
delay(600);
}
}
// ====== Loop ======
void loop() {
// Keep MQTT alive
if (!mqtt.connected()) mqttConnect();
mqtt.processPackets(10);
if (!mqtt.ping()) {
mqtt.disconnect();
}
// Read sensors
int potValue = analogRead(POT);
int pct = map(potValue, 0, 4095, 0, 100);
float h = dht.readHumidity();
float t = dht.readTemperature();
// Decide status + outputs
String status;
if (potValue < DRY_THRESHOLD) {
status = "Too dry";
setColor(true, false, false);
tone(BUZZER, 1000);
} else if (potValue < WET_THRESHOLD) {
status = "Good";
setColor(false, true, false);
noTone(BUZZER);
} else {
status = "Too wet";
setColor(false, false, true);
tone(BUZZER, 400);
}
// Update OLED
showMessage(status == "Too dry" ? F("Too dry") :
status == "Too wet" ? F("Too wet") : F("Good"),
potValue, pct);
// Publish (rate-limited, only changed for pot-derived data; DHT always if valid)
publishIfChanged(status, potValue, pct, t, h);
delay(200);
}