#include <ArduinoJson.h>
#include <PubSubClient.h>
#include <DHTesp.h>
#include <WiFi.h>
#include <Wire.h>
/**
CLIENT MQTT SIMULATOR
ALL CLIENTS SENDER/RECEIVER HAVE THE SAME CODE
ALL CLIENTS FOLLOW THE SAME PROTOCOL:
the uplink and downlink slots and its device
id is define by the node-server.
The DEVICE_ID changes bewteen the devices and
the atribute SENDER defines if the device is a
sender or a receiver.
**/
#define DEVICE_ID 1 // GLOBAL ID OF THE DEVICE
#define SENDER false // TYPE OF DEVICE
/**
CONFIGURATION
**/
// 1. MQTT Broker
#define ID_MQTT "c1"
const char* mqtt_server = "broker.hivemq.com";
const char* topic = "pancoordinato/vzzskdjldsjf";
// WIFI
const char* ssid = "Wokwi-GUEST";
const char* password = "";
WiFiClient espClient;
PubSubClient client(espClient);
// 2. HUMIDITY SENSOR
#define PIN_DHT 13 // GPIO of humidity sensor
DHTesp dht; // Humidity
static char strHumidity[10] = {0};
// 3. TIMER AND SLOT COUNTER
long lastMsgTimestamp = 0; // Timer millisEC
int current_cfp_slot = -1;
int device_index, device;
int slot_duration = 9999;
bool msgSent = false; // only one msg is sent by slot
// MQTT MSG VARIABLES
StaticJsonDocument<500> msgJson; // Mqtt to receive messages
StaticJsonDocument<500> associationJson; // Mqtt to send messages
StaticJsonDocument<500> jsonDocStaticHelp;
// BEACON STATE
bool firstBeaconNotReceive = true; // First beacon inits the system
bool beaconReceived = false; // No beacon received yet
bool deviceIsAssociated = false; // device is register in the node-red server
// BEACON CONTENT
int tcap, tcfp, BI, GTS_slots, current_slot, current_slot_init_timestamp;
const char* GTS_distribution; // each device slot assignation
// HELPERS AND DEBUG VARIABLES
JsonArray GTS_distribution_serialize; // auxiliar variable to parse the GTS_distribution into an array
boolean printed1 = false;
boolean printed = false; // to print debbug msgs only once per slot
/**
##############################################################
**/
void setupWifi() {
delay(10);
/**
CONNECT WIFI NETWORK
**/
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void callback(char* topic, byte* message, unsigned int length) {
// PARSE MSG PAYLOAD
String messageTemp;
for (int i = 0; i < length; i++) {
messageTemp += (char)message[i];
}
deserializeJson(msgJson,messageTemp);
// IF IS A BEACON TYPE MSG
if (msgJson["tcap"]) {
Serial.print("..::BEACON RECEIVED ");
serializeJson(msgJson,Serial);
beaconReceived = true;
// PARSE BEACON VALUES
tcap = msgJson["tcap"]; // Começa depois de associado, Associa direto, apenas para novos clients (já envia direto clients ids)
tcfp = msgJson["tcfp"]; // >= 4 TCFP
BI = msgJson["BI"]; // slots
slot_duration = msgJson["slot_duration"]; //seconds
GTS_slots = msgJson["GTS_slots"]; // slots per device
GTS_distribution = msgJson["GTS_distribution"]; // TCFP device slot distribution
// serialize GTS_dist
jsonDocStaticHelp.clear();
deserializeJson(jsonDocStaticHelp, GTS_distribution);
GTS_distribution_serialize = jsonDocStaticHelp.as<JsonArray>();
} else {
// IF IS A DATA TYPE MSG
if (!SENDER && msgJson["from"]=="PAN") {
Serial.print("..::DATA RECEIVED ");
serializeJson(msgJson,Serial);
} else {
// the own device receives its msgs sent
//Serial.print("..::OWN MSG RECEIVED ");
//serializeJson(msgJson,Serial);
}
}
Serial.println("");
}
void reconnect() {
// IF MQTT DESCONNECTS TRY RECONNECTION
//Serial.println("..::RECONNECTION");
while (!client.connected()) {
// Attempt to connect
if (client.connect(ID_MQTT)) {
//Serial.println("..::CLIENT CONNECTED AGAIN");
client.subscribe(topic);
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println("try again in 5 seconds");
delay(500);
}
}
}
float getHumidity() {
/**
HUMIDITY SENSOR VALUES
**/
TempAndHumidity data = dht.getTempAndHumidity();
if (!(isnan(data.humidity)))
return data.humidity;
else
return -99.99;
}
void print(String msg) {
// to help to print debug msgs
if (!printed1) {
Serial.print(msg);
}
}
void printnl(String msg) {
// to help to print debug msgs
if (!printed1) {
Serial.print(msg);
}
}
void setup() {
// Serial communication
Serial.begin(115200);
randomSeed(analogRead(0));
// Humidity sensors initializations
dht.setup(PIN_DHT, DHTesp::DHT22);
// Wifi initialization
setupWifi();
// MQTT Connection
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
if (client.connect(ID_MQTT)) {
Serial.println("..::CLIENT CONNECTED TO MQTT");
client.subscribe(topic);
} else {
Serial.println("..::ERROR TO CONNECTO TO MQTT");
}
}
void loop() {
if (!client.connected()) {
// Check connection to broker
reconnect();
}
// CALCULATE THE CURRENT SLOT BASED ON THE CURREN TIMESTAMP
if ((millis() - current_slot_init_timestamp) >= slot_duration) {
current_slot_init_timestamp = millis();
current_slot = (current_slot+1 ) % BI; // update slot counter
current_cfp_slot = current_slot - (1 + tcap) + 1; //update the Tcfp slot counter
msgSent = false; //reset, only a msg can be sent by slot
// DEBUG MSGS
printed = false;
printed1 = false;
}
if (beaconReceived) {
/*
* The system INITS only after the first
* beacon received.
*
*/
if (firstBeaconNotReceive && !deviceIsAssociated) {
delay(slot_duration); // beacon slot
/*
This conditional is meant only to run
once after the first beacon if received
It sets the initial slot id and other counters
and associated the device to the node-server
*/
firstBeaconNotReceive = false;
deviceIsAssociated = true;
// prepare ASSOCIACION msg
associationJson.clear();
associationJson["clientId"] = DEVICE_ID;
associationJson["sender"] = SENDER;
char msgBuffer[200];
serializeJson(associationJson, msgBuffer);
// send to broker
client.publish(topic, msgBuffer);
// DEBUG MSGS
Serial.print("..::MSG SENT ");
serializeJson(associationJson, Serial);
Serial.println("");
Serial.println("..::INIT PROTOCOL SLOTS");
current_slot = 1; // beacon ocuppies first slot
current_slot_init_timestamp = millis();
}
/*
PROTOCOL SLOTS
1 BEACON
2 TCAP
3 TCP
4 SLEEP
The code is divided by the slot type
and its function
*/
bool currentIsTCFP = current_cfp_slot > 0 && current_cfp_slot <= tcfp;
bool currentIsTCAP = current_slot <= tcap;
// BEACON SLOT
if ( current_slot == 0 ) {
print("BEACON ");
// TCAP SLOTS
} else if (currentIsTCAP) {
print("TCAP ");
// TCFP SLOTS
} else if (currentIsTCFP) {
// odd slots uplink even slots downlink
bool isUplinkSlot = current_cfp_slot % 2 == 1;
/*
SEND HUMIDITY MSG if SENDER DEVICE
*/
// Get device of current slot
device_index = current_cfp_slot / GTS_slots;
device = GTS_distribution_serialize[device_index];
// if the current slot if from the device
if (device == DEVICE_ID && isUplinkSlot && !msgSent && SENDER) {
// prepare DATA to send
associationJson.clear();
sprintf(strHumidity, "%.4f", getHumidity()); // Convert the value to a char array
associationJson["value"] = strHumidity;
associationJson["dest"] = random(3, 5);
associationJson["from"] = DEVICE_ID;
// get JSON
char msgBuffer[200];
serializeJson(associationJson, msgBuffer);
// Publish to broker
client.publish(topic, msgBuffer);
// Update counters and variables of slot
msgSent = true; // send only one msg per slot
lastMsgTimestamp = millis();
// debug print
print("..::SEND HUMIDITY MSG ");
serializeJson(associationJson, Serial);
Serial.println("");
}
/*
DEBUG MSGS
*/
print("TCFP device ");
if (!printed1) Serial.print(device);
print(" - ");
// TCAP SLOTS
} else {
// SLEEP SLOTS
print("SLEEP ");
}
/*
DEBUG MSGS
*/
if (!printed1) {
printed1 = true;
Serial.println(current_slot);
}
}
client.loop();
}