#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
#include "time.h"
#include <ArduinoJson.h>
const char* ssid = "Wokwi-GUEST"; // ssid ของไวไฟ (ใช้ในซิมห้ามเปลี่ยน ถ้าใช้ของจริงต้องเปลี่ยน)
const char* password = ""; // password ของไวไฟ (ใช้ในซิมห้ามเปลี่ยน ถ้าของจริงต้องเปลี่ยน)
const char* ntpServer = "pool.ntp.org"; // NTP SERVER เทียบเวลานาฬิกาโลก
const long gmtOffset_sec = 25200; // Timezone Bangkok GMT+ 7 ( 7hrs *60 mins *60secs)
const int daylightOffset_sec = 0; // There is no Daylight saving time in Thailand.
const char* mqttServer = "vpx20.duckdns.org"; // เปลี่ยนเป็น vpx ของตัวเอง เช่น vpx1.duckdns.org
const int mqttPort = 18920; // เปลี่ยนเป็นหมายเลข port ของตัวเอง
const char* mqttTopic = "wokwi/sensor";
const char* mqtt_user="dsel20"; // username ที่สร้างไว้ใน mosquitto
const char* mqtt_pass="Ve4cZ1"; // password ที่สร้างไว้ใน mosquitto
const char* mqtt_name="001" ; // สำหรับเชื่อมต่อกับ Mqtt ทั้งระบบห้ามซ้ำกัน
const char* ESP_ID="001"; //<--หมายเลขประจำบอร์ด
// ตัวแปรเก็บ topic และ payload จาก callback
String receivedTopic = "";
String receivedPayload = "";
bool isMessageAvailable = false; // ตัวบ่งชี้ว่ามีข้อมูลใหม่หรือไม่
DynamicJsonDocument doc(1024);
#define DHT_PIN 25
#define SOIL_PIN 36 // Analog pin
#define LED_PIN 2 // 1) 3.3v +-------------------+ 38) GND
#define CH1_PIN 17 // 2) Reset EN | | 37) GPIO23 VSPI MOSI
#define CH2_PIN 16 // 3) ADC0 GPI-36 | +-------+ | 36) GPIO22 I2C SCL
#define CH3_PIN 15 // 4) ADC3 GPI-39 | | |[o] | 35) GPIO1 TX0
#define CH4_PIN 13 // 5) ADC6 GPI-34 | | +---+ | 34) GPIO3 RX0
#define CH5_PIN 26 // 6) ADC7 GPI-35 | | ESP32U | | 33) GPIO21 I2C SDA
#define CH6_PIN 14 // 7) ADC4 GPIO32 | | | | 32) GND
#define CH7_PIN 33 // 8) ADC5 GPIO33 | +-----------+ | 31) GPIO19 VSPI MISO
#define CH8_PIN 32 // 9) AD18 GPIO25 | | 30) GPIO18 VSPI SCK
// 10) ADC19 GPIO26 | | 29) GPIO5 VSPI SS
// 11) ADC17 GPIO27 | LED | 28) GPIO17 TX2
#define BTN1_PIN 5 // 12) ADC16 GPIO14 | [X] [X] | 27) GPIO16 RX2
#define BTN2_PIN 18 // 13) ADC15 GPIO12 | PWR Pin2 | 26) GPIO4 ADC10
#define BTN3_PIN 19 // 14) GND | | 25) GPIO0 ADC11
#define BTN4_PIN 23 // 15) ADC14 GPIO13 | | 24) GPIO2 ADC12
#define BTN5_PIN 27 // 16) FLASH_D2 GPIO9 | USB | 23) GPIO15 ADC13
#define BTN6_PIN 39 // 17) FLASH_D3 GPIO10 | [B] +---+ [B] | 22) GPIO8 FLASH_D1
#define BTN7_PIN 34 // 18) CMD | RST | | boot | 21) GPIO7 FLASH_D0
#define BTN8_PIN 35 // 19) Vin 5V +-------+---+-------+ 20) GPIO6 FLASH_CH
#define PERIOD 10000
#define DHTPIN 001
#define DHTTYPE DHT22
#define ESP_ID "001" //<--หมายเลขประจำบอร์ด
#define INI 0
#define WAITING 1
#define STAYWAITING 2
#define MQTTin 3
#define MQTTreq 4
#define SETdata 5
#define SETntp 6
#define DHTS 7
#define SOIL 8
#define ONCH1 9
#define ONCH2 10
#define ONCH3 11
#define ONCH4 12
#define ONCH5 13
#define ONCH6 14
#define ONCH7 15
#define ONCH8 16
#define OFFCH1 17
#define OFFCH2 18
#define OFFCH3 19
#define OFFCH4 20
#define OFFCH5 21
#define OFFCH6 22
#define OFFCH7 23
#define OFFCH8 24
#define PUBLISH 25
String OnOff[2]={"off","on"};
int ActiveLow[] = {1, 0};
int CH1=0;
int CH2=0;
int CH3=0;
int CH4=0;
int CH5=0;
int CH6=0;
int CH7=0;
int CH8=0;
unsigned long myTime;
int state=INI;
float Temp;
float Humi;
String Payload;
struct tm my_time;
DHT dht(DHTPIN, DHTTYPE);
WiFiClient espClient;
PubSubClient client(espClient);
void connectToWiFi() {
Serial.println("Connecting to Wi-Fi...");
WiFi.begin(ssid, password);
// Wait for connection
int retries = 0;
while (WiFi.status() != WL_CONNECTED && retries < 20) {
delay(500);
Serial.print(".");
retries++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWi-Fi connected!");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
} else {
Serial.println("\nFailed to connect to Wi-Fi.");
}
}
void checkWiFi() {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("Wi-Fi disconnected. Reconnecting...");
connectToWiFi();
}
}
bool mqttcheckconnect() {
// พยายามเชื่อมต่อกับ MQTT Broker
if (!client.connected()) {
// พยายามเชื่อมต่อ
if (client.connect(mqtt_name, mqtt_user, mqtt_pass)) {
client.subscribe("dsel/cmd");
return true;
}
else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println("try again next time");
return false;
}
}
}
void callback(char* topic, byte* payload, unsigned int length) {
Serial.println("Message received!");
// แปลง topic และ payload ให้อยู่ในรูปแบบ String
receivedTopic = String(topic);
receivedPayload = "";
for (unsigned int i = 0; i < length; i++) {
receivedPayload += (char)payload[i];
}
Serial.println("Topic: " + receivedTopic);
Serial.println("Payload: " + receivedPayload);
isMessageAvailable = true; // แจ้งว่า message ใหม่พร้อมประมวลผล
}
void printLocalTime() //for display
{
struct tm timeinfo;
if(!getLocalTime(&timeinfo)){
Serial.println("Failed to obtain time");
return;
}
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
}
char* get_time_str(void){ // for payload
// string holding well formated time.
static char my_time_string[60];
// structure holding the time
// defination is as follow
// struct tm
// {
// int tm_sec; // seconds from 0 to 59
// int tm_min; // minutes from 0 to 59
// int tm_hour; // hours from 0 to 23
// int tm_mday; // day of the month from 1 to 30
// int tm_mon; // month of the year from 0 to 11
// int tm_year; // number of years from 1900
// ... other variables not in the scope of the tutorial..
// };
struct tm my_time;
// read the time into timeinfo structure.
if(!getLocalTime(&my_time)){
Serial.println("Failed to obtain time");
return NULL;
}
snprintf(my_time_string, // string to add the formated time to.
60, // string length to prevent overflow
"%04d-%02d-%02d %02d:%02d:%02d", // time string format
(my_time.tm_year + 1900),
(my_time.tm_mon + 1),
my_time.tm_mday,
my_time.tm_hour,my_time.tm_min,my_time.tm_sec);
// return the formated time string
return my_time_string;
}
bool getNtp(){
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
return true;
}
void setup() {
#ifdef CH1_PIN
pinMode(CH1_PIN,OUTPUT);
#endif
#ifdef CH1_PIN
pinMode(CH1_PIN,OUTPUT);
#endif
#ifdef CH2_PIN
pinMode(CH2_PIN,OUTPUT);
#endif
Serial.begin(115200);
dht.begin();
connectToWiFi();
if(getNtp()) {
printLocalTime();
Serial.println("\n Setting Time pass");
}
client.setServer(mqttServer, mqttPort);
client.setCallback(callback);
mqttcheckconnect();
state=INI;
}
void loop() {
checkWiFi();
if (!client.connected()) {
mqttcheckconnect(); // เช็คสถานะและพยายามเชื่อมต่อใหม่
}
client.loop();
switch (state) {
case INI: {
Serial.println("Initializing");
digitalWrite(CH1_PIN,ActiveLow[CH1]);
digitalWrite(CH2_PIN,ActiveLow[CH2]);
Serial.print(" CH1:");Serial.print(OnOff[CH1]);
Serial.print(", CH2:");Serial.print(OnOff[CH2]);
Serial.print(" time:");printLocalTime();
Payload="{\"id\":\""+String(ESP_ID)+"\","+
"\"timestamp\":\""+String(get_time_str())+"\","+
"\"cmd\":\"req\"}";
client.publish(mqttTopic, Payload.c_str());
client.loop();
state=WAITING;
break;
state=MQTTreq;
break; }
case WAITING: { myTime = millis();
Serial.print("Waiting State ");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
state=STAYWAITING;
break;}
case STAYWAITING: {
if (isMessageAvailable) {
isMessageAvailable = false; // เคลียร์ flag
Serial.println("Processing incomming message from mqtt ");
state= MQTTin;
} else
if ((millis()-myTime)>=PERIOD) {state=DHTS;myTime=millis();}
delay(10);
break;}
case MQTTin: {
DeserializationError error = deserializeJson(doc, receivedPayload);
// Test if parsing succeeds.
if (error) {
Serial.print(("deserializeJson() failed: "));
Serial.println(error.f_str());
} else{
if(doc["id"]==ESP_ID) {
Serial.println("\n\n");
Serial.println("-----------------------------");
Serial.print("Getting command from MQTT Server");
if (doc["cmd"]=="getdata") { state=PUBLISH; Serial.println("getdata");break;}
if (doc["cmd"]=="readdht") { state=DHTS; Serial.println("Read DHT");break;}
if (doc["cmd"]=="setNTP") {state=SETntp; Serial.println("set clock");break;}
if (doc["cmd"]=="reset") { Serial.println("RESET");delay(1000);ESP.restart();break;}
if (doc["cmd"]=="setdata") { state=SETdata; Serial.println("setdata");
if (doc["ch1"]=="on") CH1=1;
if (doc["ch1"]=="off") CH1=0;
if (doc["ch2"]=="on") CH2=1;
if (doc["ch2"]=="off") CH2=0;
if (doc["temp"]!=0.0) Temp=doc["temp"];
if (doc["humi"]!=0.0) Humi=doc["humi"];
state=WAITING;
break;}
if (doc["ch1"]=="on") { state=ONCH1; Serial.println(" CH1 On");break;}
if (doc["ch1"]=="off") { state=OFFCH1; Serial.println(" CH1 Off");break;}
if (doc["ch2"]=="on") { state=ONCH2; Serial.println(" CH2 On");break;}
if (doc["ch2"]=="off") { state=OFFCH2; Serial.println(" CH2 Off");break;}
Serial.println("-----------------------------");
}
}
state=WAITING;
break;
}
case MQTTreq: {
state=PUBLISH;break;}
case SETdata:{ digitalWrite(CH1_PIN,ActiveLow[CH1]);
digitalWrite(CH2_PIN,ActiveLow[CH2]);
state=WAITING;
break; }
case SETntp:{ if(getNtp()){printLocalTime();
Serial.println("\n Setting Time pass");}
state=WAITING;
break;
}
case DHTS: { float temp;
float humi;
temp = dht.readTemperature();if (!isnan(temp)) {Temp=temp;}
humi = dht.readHumidity();if (!isnan(humi)) {Humi=humi;}
Serial.print("time:");
printLocalTime();
Payload="{\"id\":\""+String(ESP_ID)+"\","+
"\"timestamp\":\""+String(get_time_str())+"\","+
"\"Temp\":\""+String(Temp)+"\","+
"\"Humi\":\""+String(Humi)+"\"}";
client.publish(mqttTopic, Payload.c_str());
client.loop();;
state=PUBLISH;
break;
}
case ONCH1: { Serial.println("Turn CH1 On");
CH1=1;
digitalWrite(CH1_PIN,ActiveLow[CH1]);
Payload="{\"id\":\""+String(ESP_ID)+"\","+
"\"timestamp\":\""+String(get_time_str())+"\","+
"\"ch1\":\""+OnOff[CH1]+"\"}";
client.publish(mqttTopic, Payload.c_str());
client.loop();
state=WAITING;
break; }
case ONCH2: { Serial.println("Turn CH2 On");
CH2=1;
digitalWrite(CH2_PIN,ActiveLow[CH2]);
Payload="{\"id\":\""+String(ESP_ID)+"\","+
"\"timestamp\":\""+String(get_time_str())+"\","+
"\"ch2\":\""+OnOff[CH2]+"\"}";
client.publish(mqttTopic, Payload.c_str());
client.loop();
state=WAITING;
break; }
case OFFCH1: {
Serial.println("Turn CH1 Off");
CH1=0;
digitalWrite(CH1_PIN,ActiveLow[CH1]);
Payload="{\"id\":\""+String(ESP_ID)+"\","+
"\"timestamp\":\""+String(get_time_str())+"\","+
"\"ch1\":\""+OnOff[CH1]+"\"}";
client.publish(mqttTopic, Payload.c_str());
client.loop();
state=WAITING;
break; }
case OFFCH2: {
Serial.println("Turn CH2 Off");
CH2=0;
digitalWrite(CH2_PIN,ActiveLow[CH2]);
Payload="{\"id\":\""+String(ESP_ID)+"\","+
"\"timestamp\":\""+String(get_time_str())+"\","+
"\"ch2\":\""+OnOff[CH2]+"\"}";
client.publish(mqttTopic, Payload.c_str());
state=WAITING;
break; }
case PUBLISH:{
Serial.print("Broadcast data");
Serial.print("time:");
printLocalTime();
Payload="{\"id\":\""+String(ESP_ID)+"\","+
"\"timestamp\":\""+String(get_time_str())+"\","+
"\"Temp\":\""+String(Temp)+"\","+
"\"Humi\":\""+String(Humi)+"\","+
"\"ch1\":\""+OnOff[CH1]+"\","+
"\"ch2\":\""+OnOff[CH2]+"\"}";
client.publish(mqttTopic, Payload.c_str());
client.loop();
state=WAITING;
break;
}
}
client.loop();
}