#include <OneWire.h>
#include <DallasTemperature.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
// Pins
#define POTENTIOMETER_PIN 34
#define DS18B20_PIN 4
#define MQ2_PIN 35
#define RED_LED_PIN 26
#define GREEN_LED_PIN 27
const char* ssid = "Wokwi-GUEST";
const char* password = "";
const char* thingSpeakServer = "api.thingspeak.com";
const char* writeAPIKey = "QYYFF61W9IZO6AV7";
const char* googleScriptUrl = "https://script.google.com/macros/s/AKfycbzqET0BFMskcnB7ZGL00S0GKIh56M4L52CDGIC78e6Z4W8HNeoChTOxUfstp-D7BNfK/exec";
OneWire oneWire(DS18B20_PIN);
DallasTemperature sensors(&oneWire);
WiFiClientSecure client;
HTTPClient https;
unsigned long previousLedMillis = 0;
const unsigned long ledToggleInterval = 250; // blink every 250ms
enum LedMode {
LED_OFF,
LED_RED_BLINK,
LED_GREEN_BLINK
};
LedMode currentLedMode = LED_OFF;
bool ledState = LOW;
const char* getHttpErrorDescription(int code) {
switch (code) {
case -1: return "Connection failed or timeout";
case -2: return "Send header failed";
case -3: return "Send payload failed";
case -4: return "Not connected";
case -5: return "Connection lost";
case -6: return "No stream available";
case -7: return "No HTTP server";
case -8: return "Out of memory";
case -9: return "Encoding error";
case -10: return "Local network error";
case -11: return "Timeout";
case 302: return "Redirect (Check URL)";
case 403: return "Forbidden - Check API key or permissions";
case 404: return "Not found - Check URL";
case 500: return "Server error";
default: return "Unknown error";
}
}
void connectToWiFi() {
Serial.print("Connecting to WiFi");
WiFi.begin(ssid, password);
int retries = 0;
while (WiFi.status() != WL_CONNECTED && retries < 20) {
delay(500);
Serial.print(".");
retries++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println(" Connected");
} else {
Serial.println(" Failed to connect to WiFi");
}
}
void setup() {
Serial.begin(115200);
sensors.begin();
analogSetAttenuation(ADC_11db);
pinMode(RED_LED_PIN, OUTPUT);
pinMode(GREEN_LED_PIN, OUTPUT);
digitalWrite(RED_LED_PIN, LOW);
digitalWrite(GREEN_LED_PIN, LOW);
connectToWiFi();
if (WiFi.status() == WL_CONNECTED) {
configTime(0, 0, "pool.ntp.org", "time.nist.gov");
Serial.println("Waiting for NTP time sync...");
int timeRetries = 0;
struct tm timeinfo;
while (!getLocalTime(&timeinfo, 1000) && timeRetries < 20) {
delay(500);
Serial.print(".");
timeRetries++;
}
Serial.println();
}
client.setInsecure(); // Disable SSL cert validation (for dev/testing)
}
void sendSensorData(float temperatureC, int airQualityIndex, int soilMoisturePercent) {
if (temperatureC == -127.0) {
Serial.println("DS18B20 sensor error, skipping upload.");
return;
}
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi disconnected, reconnecting...");
connectToWiFi();
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi reconnect failed, skipping data upload.");
return;
}
}
// ThingSpeak (HTTP)
HTTPClient http;
char url[256];
snprintf(url, sizeof(url), "http://%s/update?api_key=%s&field1=%.2f&field2=%d&field3=%d",
thingSpeakServer, writeAPIKey, temperatureC, airQualityIndex, soilMoisturePercent);
Serial.println("Sending to ThingSpeak...");
http.begin(url);
int httpCode = http.GET();
if (httpCode == 200) {
Serial.println("ThingSpeak: Success");
} else {
Serial.printf("ThingSpeak HTTP %d (%s)\n", httpCode, getHttpErrorDescription(httpCode));
}
http.end();
delay(500);
// Google Sheets (HTTPS)
https.begin(client, googleScriptUrl);
https.addHeader("Content-Type", "application/json");
char jsonPayload[256];
snprintf(jsonPayload, sizeof(jsonPayload),
"{\"temperature\":%.2f,\"airQuality\":%d,\"soilMoisture\":%d}",
temperatureC, airQualityIndex, soilMoisturePercent);
Serial.println("Sending to Google Sheets...");
httpCode = https.POST(jsonPayload);
if (httpCode == 200) {
Serial.println("Google Sheets: Success");
} else {
Serial.printf("Google Sheets HTTP %d (%s)\n", httpCode, getHttpErrorDescription(httpCode));
}
https.end();
}
void updateLedBlink() {
unsigned long currentMillis = millis();
if (currentMillis - previousLedMillis >= ledToggleInterval) {
previousLedMillis = currentMillis;
ledState = !ledState;
switch (currentLedMode) {
case LED_RED_BLINK:
digitalWrite(RED_LED_PIN, ledState);
digitalWrite(GREEN_LED_PIN, LOW);
break;
case LED_GREEN_BLINK:
digitalWrite(GREEN_LED_PIN, ledState);
digitalWrite(RED_LED_PIN, LOW);
break;
case LED_OFF:
default:
digitalWrite(RED_LED_PIN, LOW);
digitalWrite(GREEN_LED_PIN, LOW);
ledState = LOW; // reset
break;
}
}
}
void loop() {
static unsigned long lastSendMillis = 0;
unsigned long currentMillis = millis();
// Send and read data every 2 seconds
if (currentMillis - lastSendMillis >= 2000) {
lastSendMillis = currentMillis;
sensors.requestTemperatures();
float temperatureC = sensors.getTempCByIndex(0);
int potValue = analogRead(POTENTIOMETER_PIN);
int soilMoisturePercent = map(potValue, 0, 4095, 100, 0);
int mq2Value = analogRead(MQ2_PIN);
int airQualityIndex = map(mq2Value, 0, 4095, 0, 100);
Serial.printf("Temp: %.2f C, Soil: %d %%, Air: %d\n", temperatureC, soilMoisturePercent, airQualityIndex);
sendSensorData(temperatureC, airQualityIndex, soilMoisturePercent);
LedMode newMode = LED_OFF;
if (temperatureC > 35 && soilMoisturePercent > 50 && airQualityIndex > 50) {
newMode = LED_RED_BLINK;
} else if (temperatureC < 35 && soilMoisturePercent < 50 && airQualityIndex < 50) {
newMode = LED_GREEN_BLINK;
}
if (newMode != currentLedMode) {
currentLedMode = newMode;
ledState = LOW;
previousLedMillis = currentMillis;
digitalWrite(RED_LED_PIN, LOW);
digitalWrite(GREEN_LED_PIN, LOW);
Serial.print("LED mode changed to: ");
if (newMode == LED_RED_BLINK) Serial.println("RED BLINK");
else if (newMode == LED_GREEN_BLINK) Serial.println("GREEN BLINK");
else Serial.println("LED OFF");
}
}
updateLedBlink();
}