#include <DHTesp.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
#include <ArduinoJson.h>
// Constants for intervals
const unsigned long cycleInterval = 300000; // 1 h interval in milisec for cycles
const unsigned long sensorReadInterval = 60000; // 1.2 seconds interval for sensor readings
const unsigned long countdownInterval = 5000; // 1 second for countdown updates
// Sensor setup
DHTesp dhtSensor;
DHTesp dhtSensor2;
const int dhtPin = 15; // Pin for the first sensor
const int dhtPin2 = 13; // Pin for the second sensor
const int echo = 4;
const int trig = 2;
String globalSecondSensorData;
float pressure;
float duration;
float distance;
// Network and NTP setup
char ssid[] = "Wokwi-GUEST"; // Update with your actual WiFi details
char pass[] = ""; // Update with your actual WiFi password
const char* transylinkURL = "https://transylink.com/api/transi_data/process.php";
const char* transylinkToken = "aabdc9d363cf855a2ef767fde5a83f60ffdbad2ac2de20fb54d736e7c18140010a6231258d40";
WiFiClient client;
WiFiUDP ntpUDP;
long utcOffsetInSeconds = 7200; // UTC+2 for standard time
//NTPClient timeClient(ntpUDP, "pool.ntp.org", 0, 60000); // NTP client setup for UTC
NTPClient timeClient(ntpUDP, "pool.ntp.org", utcOffsetInSeconds);
// JSON data handling
const size_t JSON_CAPACITY = 2048 * 10; // Adjust size as necessary
DynamicJsonDocument sensorDataJson(JSON_CAPACITY);
DynamicJsonDocument sensorDataJson2(JSON_CAPACITY); // Second sensor data
// Timing control
unsigned long lastCycleStartTime = 0;
unsigned long lastSensorReadTime = 0;
unsigned long lastCountdownTime = 0;
int cycleNumber = 5; //cycle no starting from
void setup() {
Serial.begin(115200);
pinMode(trig, OUTPUT);
pinMode(echo, INPUT);
dhtSensor.setup(dhtPin, DHTesp::DHT22); // Setup first DHT sensor
dhtSensor2.setup(dhtPin2, DHTesp::DHT22); // Setup second DHT sensor
WiFi.mode(WIFI_STA);
getwifi();
timeClient.begin();
lastCycleStartTime = millis();
lastSensorReadTime = millis();
lastCountdownTime = millis();
}
void loop() {
unsigned long currentTime = millis();
timeClient.update(); // Ensure time is updated from NTP server regularly
// Countdown Timer
if (currentTime - lastCountdownTime >= countdownInterval) {
lastCountdownTime = currentTime;
if (currentTime - lastCycleStartTime < cycleInterval) {
unsigned long timeLeft = cycleInterval - (currentTime - lastCycleStartTime);
Serial.print("Time until next cycle: ");
Serial.print(timeLeft / 1000);
Serial.println(" seconds");
}
}
// Sensor reading logic
if (currentTime - lastSensorReadTime >= sensorReadInterval) {
lastSensorReadTime = currentTime; // Update the last read time
readAndLogSensorData();
}
// Cycle logic
if (currentTime - lastCycleStartTime >= cycleInterval) {
lastCycleStartTime = currentTime;
cycleNumber++;
// Send start cycle notification
sendNotification("Start", cycleNumber);
// Cycle data handling and end notification
sendCycleData(cycleNumber); // Sends the collected data
// Send start cycle notification
sendNotification("End", cycleNumber);
// Clear the JSON document for the next cycle
sensorDataJson.clear();
sensorDataJson2.clear();
}
}
void sendNotification(String status, int cycleNumber) {
HTTPClient http;
http.begin(transylinkURL);
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
int dataType = (status == "Start") ? 300 : 301;
String payload = "{\"data_type\":" + String(dataType) + ", \"data_int\":" + String(cycleNumber) +
", \"data_date\":\"" + formatISO8601Timestamp() +
"\", \"data_text\":\"CycleStatus:" + status +
"\", \"data_long_text\":\"CycleNumber:" + String(cycleNumber) + "\"}";
http.POST(String("token=") + transylinkToken + "&payload=" + payload);
http.end();
}
float getPressure() {
digitalWrite(trig, LOW);
delayMicroseconds(2);
digitalWrite(trig, HIGH);
delayMicroseconds(10);
digitalWrite(trig, LOW);
duration = pulseIn(echo, HIGH);
float calculatedPressure = duration * 0.034 / 2;
// Ensure rounding to 2 decimal places is applied correctly
calculatedPressure = round(calculatedPressure * 100) / 100.0;
return calculatedPressure;
}
void readAndLogSensorData() {
TempAndHumidity data = dhtSensor.getTempAndHumidity();
TempAndHumidity data2 = dhtSensor2.getTempAndHumidity();
float calculatedPressure = getPressure(); // Use the existing getPressure function to fetch pressure
// Adjust to two decimal places
calculatedPressure = int(calculatedPressure * 100) / 100.0;
String currentTimeStamp = formatISO8601Timestamp();
// First sensor data
JsonArray tmp1Array = sensorDataJson["Tmp1"];
if (tmp1Array.isNull()) {
tmp1Array = sensorDataJson.createNestedArray("Tmp1");
}
JsonObject tmp1Obj = tmp1Array.createNestedObject();
tmp1Obj[currentTimeStamp] = data.temperature;
JsonArray prs1Array = sensorDataJson["Prs1"];
if (prs1Array.isNull()) {
prs1Array = sensorDataJson.createNestedArray("Prs1");
}
JsonObject prs1Obj = prs1Array.createNestedObject();
prs1Obj[currentTimeStamp] = data.humidity;
JsonArray uvArray = sensorDataJson["UV"];
if (uvArray.isNull()) {
uvArray = sensorDataJson.createNestedArray("UV");
}
JsonObject uvObj = uvArray.createNestedObject();
uvObj[currentTimeStamp] = 1; // Static UV data
// Second sensor data
JsonArray tmp2Array = sensorDataJson2["Tmp2"];
if (tmp2Array.isNull()) {
tmp2Array = sensorDataJson2.createNestedArray("Tmp2");
}
JsonObject tmp2Obj = tmp2Array.createNestedObject();
tmp2Obj[currentTimeStamp] = data2.temperature;
JsonArray cur1Array = sensorDataJson2["Cur1"];
if (cur1Array.isNull()) {
cur1Array = sensorDataJson2.createNestedArray("Cur1");
}
JsonObject cur1Obj = cur1Array.createNestedObject();
cur1Obj[currentTimeStamp] = data2.humidity; // Now renamed to Cur1
JsonArray frq1Array = sensorDataJson2["Frq1"];
if (frq1Array.isNull()) {
frq1Array = sensorDataJson2.createNestedArray("Frq1");
}
// Formatting pressure reading just before serialization if rounding above doesn't hold
JsonObject frq1Obj = frq1Array.createNestedObject();
frq1Obj[currentTimeStamp] = String(calculatedPressure, 2); // Convert float to String limiting to 2 decimals
}
String formatISO8601Timestamp() {
time_t now = timeClient.getEpochTime();
struct tm * timeinfo = gmtime(&now);
char buffer[25];
strftime(buffer, 25, "%Y-%m-%d %H:%M:%S", timeinfo);
return String(buffer);
}
void sendCycleData(int cycleNumber) {
readAndLogSensorData(); // This will update sensorDataJson and sensorDataJson2 with the latest sensor data
String sensorDataString;
serializeJson(sensorDataJson, sensorDataString); // Serialize the first sensor's JSON object
sensorDataString.replace("\"", "\\\""); // Properly escape double quotes for JSON string inclusion
String secondSensorDataString;
serializeJson(sensorDataJson2, secondSensorDataString); // Serialize the second sensor's JSON object
secondSensorDataString.replace("\"", "\\\""); // Properly escape double quotes for JSON string inclusion
HTTPClient http;
http.begin(transylinkURL);
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
// Calculate the cycle interval in minutes
float cycleIntervalMinutes = cycleInterval / (1000.0 * 60.0);
// data_int_extra2 represents the time in minutes between CycleStatus:Start and CycleStatus:End
float dataIntExtra2 = cycleIntervalMinutes;
// data_int_extra3 represents the cycle duration in minutes, corresponding to 1 liter/minute
float dataIntExtra3 = cycleIntervalMinutes;
String payload = "{\"data_type\":302, \"data_int\":" + String(cycleNumber) +
", \"data_date\":\"" + formatISO8601Timestamp() +
"\", \"data_text\":\"CycleDataTransmission\", \"data_int_extra1\":0, " +
"\"data_int_extra2\":" + String(dataIntExtra2, 2) +
", \"data_int_extra3\":" + String(dataIntExtra3, 2) +
", \"data_int_extra4\":0, " +
"\"data_long_text\":\"" + sensorDataString + "\", " +
"\"data_text_extra1\":\"" + secondSensorDataString + "\"}";
Serial.println("Sending Cycle Data to Server: " + payload);
int httpResponseCode = http.POST(String("token=") + transylinkToken + "&payload=" + payload);
handleHTTPResponse(httpResponseCode, http.getString());
http.end();
}
void handleHTTPResponse(int httpResponseCode, String responseBody) {
if (httpResponseCode == 200) {
Serial.println("HTTP/1.1 200 OK");
Serial.println("Response: " + responseBody);
DynamicJsonDocument jsonDoc(1024);
DeserializationError error = deserializeJson(jsonDoc, responseBody);
if (error) {
Serial.println("Failed to parse response JSON");
return;
}
const char* result = jsonDoc["Result"];
int code = jsonDoc["Code"];
if (strcmp(result, "OK") == 0 && code == 0) {
Serial.println("{'Result'=>'OK', 'Code'=>0} – request processed");
} else if (strcmp(result, "ERROR") == 0) {
switch (code) {
case 1:
Serial.println("{'Result'=>'ERROR', 'Code'=>1, 'Message'=>'Payload json not valid or invalid data_type!'}");
break;
case 2:
Serial.println("{'Result'=>'ERROR', 'Code'=>2, 'Message'=>'Database insert error!'}");
break;
case 3:
Serial.println("{'Result'=>'ERROR', 'Code'=>3, 'Message'=>'Device not found (token may not be valid)!'}");
break;
default:
Serial.println("Unknown error code");
break;
}
}
} else if (httpResponseCode == 401) {
Serial.println("HTTP/1.1 401 Unauthorized – Any other errors - token or payload not provided with the request, provided token is malformed, IP address blacklisted.");
} else {
Serial.print("Error in HTTP request, Response code: ");
Serial.println(httpResponseCode);
}
}
void getwifi() {
if (WiFi.status() != WL_CONNECTED) {
Serial.print("Attempting to connect");
while (WiFi.status() != WL_CONNECTED) {
WiFi.begin(ssid, pass);
Serial.print(".");
delay(3000);
}
Serial.println("\nConnected");
}
}