#include <PubSubClient.h>
#include <WiFi.h>
#include <ArduinoJson.h>
//led info
#define LEDPIN 12 //which pin We use
#define PWM1_Ch 0 //first channel of 16
#define PWM1_Res 8
#define PWM1_Freq 1000
//#define debug //uncomment to debug
//#define debug2 //uncomment to debug (show just when a new beacon is recived)
// MQTT WiFi details
const char* ssid = "Wokwi-GUEST";
const char* password = "";
// MQTT broker details
const char* mqttServer = "broker.hivemq.com";
const int mqttPort = 1883;
// MQTT client details
#define MQTT_TOPIC "progetto/LucaMuscoDesecondi-FrancescoGenovese"
#define CLIENT_ID "client3"
#define CLIENT_CODE 3
#define ACTUATOR_CODE 1
// Wi-Fi client and MQTT client
WiFiClient espClient;
PubSubClient client(espClient);
int notConnectedCounter = 0;
//json file
StaticJsonDocument<200> doc;
StaticJsonDocument<100> messaggio;
unsigned long startTime =0; // Track the elapsed time (in milliseconds)
//info from the PanC and sensors
int BI, CAP, CFP, ts, myTurn;
int ActivePart;
int att;
float hum;
// variable we need if we want to use deep sleep mode
int uS_TO_mS_FACTOR = 1000; // Conversion factor for micro seconds to milli seconds
//time wake up
int TWUBWW= 1500; //time wake up bug wokwi. we need this variable to wake up early the device to make it work on wokwi. otherwise, it sleeps too much in real time and loose a beacon
//status
bool IRecivedABeacon = false;
void setup() {
Serial.begin(115200);
wificonnect(); //connection to wifi
// Connect to MQTT broker and set callback
client.setServer(mqttServer, mqttPort);
client.setCallback(callback);
//first connection to the MQTT broker
while (!client.connected()) {
if (client.connect(CLIENT_ID)) {
Serial.println("Connected to MQTT broker");
client.subscribe(MQTT_TOPIC);
} else {
Serial.print("Failed to connect to MQTT broker, rc=");
Serial.print(client.state());
Serial.println(" Retrying in 2 seconds...");
delay(2000);
}
}
//Set led pin as output
pinMode(LEDPIN, OUTPUT);
// configure LED PWM functionalitites
ledcSetup(PWM1_Ch, PWM1_Freq, PWM1_Res);
// attach the channel to the Pin to be controlled
ledcAttachPin(LEDPIN, PWM1_Ch);
}
void wificonnect() {
delay(500); //we give some time to start
// setup Wi-Fi
WiFi.mode(WIFI_STA); //setting the wifi in station mode
WiFi.begin(ssid, password); //speciyng the SSID and password for the wifi
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(200); //we keep waiting 0.2 seconds more for a connection until we get it
notConnectedCounter++;
if (notConnectedCounter > 20) { // Reset board if not connected after 20 tries
Serial.println("Resetting due to Wifi not connecting...");
ESP.restart();
}
}
Serial.println("Connected to WiFi");
}
//maintaininig the MQTT connection while waiting for the beacon
void loop() {
//reconnect if not connected to the MQTT broker
if (!client.connected()) {
reconnect();
}
//go to deep sleep when we finish the active part. millis-startTime is the actual time since a beacon reception
if ((ActivePart) < (millis() - startTime) && (IRecivedABeacon))
{
#ifdef debug
Serial.print ("we are in the inactive part: "); Serial.println(millis()-startTime);
#endif
goToDeepSleep();
}
client.loop();
}
//reconnecting to the broker when a disconnection happen
void reconnect() {
while (!client.connected()) {
Serial.println("Attempting MQTT connection...");
if (client.connect(CLIENT_ID)) {
Serial.println("Connected to MQTT broker");
client.subscribe(MQTT_TOPIC);
} else {
Serial.print("Failed to connect to MQTT broker, rc=");
Serial.print(client.state());
Serial.println(" Retrying in 0.5 seconds...");
delay(500);
}
}
}
// Handle incoming MQTT messages
void callback(char * topic, byte * message, unsigned int length){
DeserializationError e = deserializeJson(doc, message);
if(e){
return;
}
int localatt = (int)doc["attuatore"]; //reading the actuator in a local variable
if (localatt == ACTUATOR_CODE) {
att = localatt; //if the sensor is sending the message to THIS actuator this esp32 understand that the message is only for it
}
if (((int)doc["BI"]) != 0) {
#ifdef debug2
Serial.println("New beacon received:");
Serial.print("Beacon number: "); Serial.println((int)doc["count"]);
Serial.print("Time between this new beacon and the previous one is: "); Serial.println((millis() - startTime));
#endif
startTime = millis(); //now we update the startTime, when the new beacon is recived
IRecivedABeacon = true;
BI = (int)doc["BI"]; //reading the beacon interval
ts = (int)doc["ts"]; //reading the time slot
CAP = (int)doc["CAP"]; //reading the CAP
CFP = (int)doc["CFP"]; //reading the CFP
String s="c" + String(CLIENT_CODE); //creating the string to identify my turn
#ifdef debug
Serial.print("my name in the beacon is: "); Serial.println(s);
#endif
myTurn = (int)doc[s]; //reading my slot index to trasmitt
ActivePart=(ts+CAP+CFP);
#ifdef debug
Serial.print("BI = "); Serial.println(BI);
Serial.print("TS = "); Serial.println(ts);
Serial.print("CAP = "); Serial.println(CAP);
Serial.print("CFP = "); Serial.println(CFP);
//Check if the client's name is in the Beacon
if (!myTurn) {
Serial.println("We are not currently enrolled");
Serial.println("---");
} else {
Serial.print("my slot number is: "); Serial.println(myTurn); //check if my index is right
Serial.println("---");
}
#endif
workTime();
} else if ((doc["tipo"] == 1) && (myTurn != 0) && (att == ACTUATOR_CODE)){ //check if the message I recived comes from a sensor and is headed to me
hum = (int)doc["valore"];
#ifdef debug
Serial.print("my humidity is: "); Serial.println(hum);
Serial.print("I read the humidity at: ");Serial.print(millis()-startTime); Serial.println(" since the beacon reception");
#endif
att = 0; //reset the variable waiting for a new sensor read for this client
Turn_On();
}
}
//things to do during the beacon interval
void workTime(){
//beacon slot
if (((millis() - startTime) < ts)) { //we wait for the end of the beacon slot
delay(ts - (millis() - startTime));
}
//registration during the CAP
if (!myTurn) { //if we are not registered, we register
registration();
}
else { //if we are registered and we get out from the CAP
if ((millis() - startTime) < (ts + CAP)) { //beacon slot+CAP
delay((ts + CAP) - (millis() - startTime)); //we delay to get in CFP
}
}
} //exiting from the worktime fuction, then from callback, and going back to the loop, at the beggining of the CFP
// here is where the clients ask the registration to the PanC
void registration () {
messaggio["tipo"] = 0; //registration code
messaggio["valore"] = CLIENT_CODE; // client id value
char msgout[100];
serializeJson(messaggio, msgout);
client.publish(MQTT_TOPIC, msgout); //publish on the topic that i want to register and my code
#ifdef debug
Serial.print("my name is: "); Serial.println(CLIENT_ID);
#endif
}
//Turning on the led
void Turn_On() {
#ifdef debug
Serial.print("we are turning on the led at: "); Serial.println((millis() - startTime));
#endif
if ((ActivePart) > (millis() - startTime) && (IRecivedABeacon)) { //just to check if we are still in the active part
byte led_dim = map(hum, 0, 100, 0, 255); //this function regulates the luminosity intensity in a range from 0 to 255, starting from a value that goes from 0 to 100
ledcWrite(PWM1_Ch, led_dim); //turn on the led
}
}
// when the active part terminates, we do nothing for the inactive part
void goToDeepSleep() {
#ifdef debug
Serial.println("time to sleep");
Serial.print("we go to sleep at: "); Serial.println((millis() - startTime));
#endif
delay(BI - (millis() - startTime)-TWUBWW); //we substitute the deep sleep with a delay and subtract some time because of the wokwi bug
#ifdef debug
Serial.println("I'm awake!");
Serial.print("I woke up at: "); Serial.println((millis() - startTime));
Serial.println("---");
#endif
IRecivedABeacon = false; //we must wait a new beacon before going to sleep again
}