#include <DHT.h>
#include <LiquidCrystal_I2C.h>
#include <map>
#include <PubSubClient.h>
#include <SD.h>
#include <SPI.h>
#include "time.h"
#include <vector>
#include <Wire.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
// Constantes referentes à intervalos de processamento
unsigned long CURRENT_MILLIS = 0;
const int INTERVAL_DELAY_PROCESS = 4000;
const int INTERVAL_RESEND_LOCAL_DATA = 60000;
const bool IS_SIMULATED_RETURN_DATETIME = true;
/*
Constantes referentes ao tipo de armazenamento de dados local
> Importante: Como não existe a possibilidade de criar uma partição para armazenamento de arquivos no Wokiwi, abaixo, serão
disponibilizados os seguintes tipos para definição da constante SAVE_DATA_TYPE:
- LOCAL_STORAGE: Para armazenamento temporário em um storage local;
- SD_CARD: Para armazenamento dos dados em arquivos no cartão SD;
*/
const char* DATA_SEPARATOR = "|";
const char* SAVE_DATA_LOCAL_STORAGE = "LOCAL_STORAGE";
const char* SAVE_DATA_SD_CARD = "SD_CARD";
const char* SAVE_DATA_TYPE = SAVE_DATA_SD_CARD;
const char* FILE_NAME_STANDARD = "temp_data.txt";
const char* STORAGE_NAME_STANDARD = "temp_data";
const int MAX_LOCAL_DATA = 500;
std::vector<String> getLocalData();
int getLocalDataLength();
// -=-=-=-=-=-=-=-=-
// Funções genéricas
/**
Função responsável por exibição de mensagens no serial
@param message: Mensagem que deverá ser exibida ( string )
@param isBreakLineBefore: Status informando se deverá ser efetuada quebra de linha antes a exibição da mensagem ( bool )
@param isBreakLineAfter: Status informando se deverá ser efetuada quebra de linha após a exibição da mensagem ( bool )
@return void
*/
void showMessageSerial(String message, bool isBreakLineBefore = false, bool isBreakLineAfter = true) {
if (isBreakLineBefore) Serial.println();
if (isBreakLineAfter) Serial.println(message);
else Serial.print(message);
}
/**
Função responsável pela inicialização do objeto contendo data e hora corrente
@return void
*/
void initDatetime() {
showMessageSerial("Inicializando componente de data e hora...", true);
if (IS_SIMULATED_RETURN_DATETIME == true) showMessageSerial("Componente de data e hora inicializado com sucesso.");
else {
if (!isWiFiConnected()) {
showMessageSerial("Sem conexão com rede Wi-Fi.");
return;
}
configTime(-3 * 3600, 0, "pool.ntp.org");
struct tm timeinfo;
if (!getLocalTime(&timeinfo))
showMessageSerial("Ocorreu um erro no processo de definição da data e hora corrente.");
else
showMessageSerial("Componente de data e hora inicializado com sucesso.");
}
}
/**
Função responsável por retornar a data e hora corrente
@return string
*/
String getCurrentDatetime() {
if (IS_SIMULATED_RETURN_DATETIME == true) return "1970-01-01 00:00:00";
if (!isWiFiConnected()) return "1970-01-01 00:00:00";
struct tm timeinfo;
if (!getLocalTime(&timeinfo))
return "1970-01-01 00:00:00";
char datetime[20];
strftime(datetime, sizeof(datetime), "%Y-%m-%d %H:%M:%S", &timeinfo);
return String(datetime);
}
/**
Função responsável por executar ações de delay
@param interval: Intervalo que deverá ser executado ( int )
@param isDelay: Status informando se, durante o intervalo, o processamento deverá ser congelado ( bool )
return bool
*/
bool executeWait(int interval = 0, bool isDelay = true) {
if (isDelay) {
delay(interval);
return true;
}
else {
if (millis() - CURRENT_MILLIS >= interval) {
CURRENT_MILLIS = millis();
return true;
}
return false;
}
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// Funções referentes ao sensor de temperatura
#define DHT_PIN 15
#define DHTTYPE DHT22
DHT dht(DHT_PIN, DHTTYPE);
/**
Função responsável pela inicialização do sensor de temperatura
@return void
*/
void initTemperatureSensor() {
showMessageSerial("Inicializando sensor de temperatura...", true);
dht.begin();
showMessageSerial("Sensor de temperatura inicializado com sucesso.");
}
/**
Função responsável por retornar a temperatura
@return float
*/
float getTemperature() {
float temperature = dht.readTemperature();
if (isnan(temperature)) temperature = -999.9;
return temperature;
}
/**
Função responsável por retornar a umidade
@return float
*/
float getHumidity() {
float humidity = dht.readHumidity();
if (isnan(humidity)) humidity = -999.9;
return humidity;
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Funções referentes ao sensor de luminosidade
const float GAMMA = 0.7;
const float RL10 = 33;
#define LDR_PIN 34
/**
Função responsável por retornar a medição de lux
@return float
*/
float getLux() {
int ldrValue = analogRead(LDR_PIN);
float voltage = ldrValue / 4096. * 3.3;
float resistance = 2000 * voltage / (1 - voltage / 3.3);
float lux = pow(RL10 * 1e3 * pow(10, GAMMA) / resistance, (1 / GAMMA));
if (isnan(lux)) lux = -999.9;
return lux;
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Funções referentes ao armazenamento de dados utilizando um storage local
std::map<String, String> LOCAL_STORAGE;
/**
Função responsável pela inicialização do storage local
@return void
*/
void initLocalStorage() {
showMessageSerial("Inicializando storage local...", true);
LOCAL_STORAGE.clear();
showMessageSerial("Storage local inicializado com sucesso.");
}
/**
Função responsável por verificar se um storage existe
@param storageName: Nome do storage ( string )
return bool
*/
bool isExistsStorage(String storageName = STORAGE_NAME_STANDARD) {
return LOCAL_STORAGE.find(storageName) != LOCAL_STORAGE.end();
}
/**
Função responsável pela criação de storages
@param storageName: Nome do storage ( string )
return bool
*/
bool createStorage(String storageName = STORAGE_NAME_STANDARD) {
if (isExistsStorage(storageName)) {
showMessageSerial("O storage '" + storageName + "' já existe.");
return false;
}
LOCAL_STORAGE[storageName] = "";
return true;
}
/**
Método responsável por adicionar conteúdos em storages
@param content: Conteúdo que deverá ser adicionado ( string )
@param storageName: Nome do storage ( string )
@return void
*/
void writeStorage(String content, String storageName = STORAGE_NAME_STANDARD) {
if (content.length() == 0) return;
if (!isExistsStorage(storageName))
createStorage(storageName);
std::vector<String> localDataItems = getLocalData();
localDataItems.push_back(content);
while (localDataItems.size() > MAX_LOCAL_DATA) {
localDataItems.erase(localDataItems.begin(), localDataItems.begin() + (localDataItems.size() - MAX_LOCAL_DATA));
}
String contentData = "";
for (String jsonData : localDataItems) {
contentData += jsonData + DATA_SEPARATOR;
}
LOCAL_STORAGE[storageName] = contentData;
}
/**
Método responsável por atualizar todo o conteúdo em storages
@param content: Conteúdo que deverá ser adicionado ( string )
@param storageName: Nome do storage ( string )
@return void
*/
void updateStorage(String content, String storageName = STORAGE_NAME_STANDARD) {
if (!isExistsStorage(storageName))
createStorage(storageName);
LOCAL_STORAGE[storageName] = content;
}
/**
Método responsável por retornar conteúdos em storages
@param storageName: Nome do storage ( string )
@return String
*/
String readStorage(String storageName = STORAGE_NAME_STANDARD) {
if (!isExistsStorage(storageName))return "";
return LOCAL_STORAGE[storageName];
}
/**
Método responsável por excluir storages
@param storageName: Nome do storage ( string )
@return bool
*/
bool deleteStorage(String storageName = STORAGE_NAME_STANDARD) {
if (!isExistsStorage(storageName)) {
showMessageSerial("O storage '" + storageName + "' não existe.");
return false;
}
LOCAL_STORAGE.erase(storageName);
return true;
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// Funções referentes ao armazenamento de dados utilizando cartão SD
#define SD_CS 25
#define SD_SCK 32
#define SD_MOSI 33
#define SD_MISO 35
SPIClass spiSD(VSPI);
const char* DIRECTORY_SEPARATOR = "/";
/**
Função responsável pela inicialização do cartão SD
@return void
*/
void initSD() {
showMessageSerial("Inicializando SD...", true);
spiSD.begin(SD_SCK, SD_MISO, SD_MOSI, SD_CS);
bool isBegin = SD.begin(SD_CS, spiSD);
if (!isBegin)
showMessageSerial("Ocorreu um erro ao inicializar o SD.");
else
showMessageSerial("SD inicializado com sucesso.");
}
/**
Função responsável por verificar se um arquivo existe
@param fileName: Nome do arquivo ( string )
return bool
*/
bool isExistsFile(String fileName = FILE_NAME_STANDARD) {
return SD.exists(String(DIRECTORY_SEPARATOR) + fileName);
}
/**
Função responsável pela criação de arquivos
@param fileName: Nome do arquivo ( string )
return bool
*/
bool createFile(String fileName = FILE_NAME_STANDARD) {
if (isExistsFile(fileName)) {
showMessageSerial("O arquivo '" + fileName + "' já existe.");
return false;
}
File file = SD.open(String(DIRECTORY_SEPARATOR) + fileName, FILE_WRITE);
if (!file) {
showMessageSerial("Erro ao criar o arquivo '" + fileName + "'.");
return false;
}
file.close();
return true;
}
/**
Método responsável por adicionar conteúdos em arquivos
@param content: Conteúdo que deverá ser adicionado ( string )
@param fileName: Nome do arquivo ( string )
@return void
*/
void writeFile(String content, String fileName = FILE_NAME_STANDARD) {
if (content.length() == 0) return;
if (!isExistsFile(fileName))
createFile(fileName);
std::vector<String> localDataItems = getLocalData();
localDataItems.push_back(content);
while (localDataItems.size() > MAX_LOCAL_DATA) {
localDataItems.erase(localDataItems.begin(), localDataItems.begin() + (localDataItems.size() - MAX_LOCAL_DATA));
}
String contentData = "";
for (String jsonData : localDataItems) {
contentData += jsonData + DATA_SEPARATOR;
}
File file = SD.open(String(DIRECTORY_SEPARATOR) + fileName, FILE_WRITE);
if (file) {
file.print(contentData);
file.close();
}
}
/**
Método responsável por atualizar todo o conteúdo em arquivos
@param content: Conteúdo que deverá ser adicionado ( string )
@param fileName: Nome do arquivo ( string )
@return void;
*/
void updateFile(String content, String fileName = FILE_NAME_STANDARD) {
File file = SD.open(String(DIRECTORY_SEPARATOR) + fileName, FILE_WRITE);
if (file) {
file.print(content);
file.close();
}
}
/**
Método responsável por retornar conteúdos em arquivos
@param fileName: Nome do arquivo ( string )
@return String
*/
String readFile(String fileName = FILE_NAME_STANDARD) {
if (!isExistsFile(fileName)) return "";
File file = SD.open(String(DIRECTORY_SEPARATOR) + fileName, FILE_READ);
String content = "";
while (file.available()) {
content += (char)file.read();
}
file.close();
return content;
}
/**
Método responsável por excluir arquivos
@param fileName: Nome do arquivo ( string )
@return bool
*/
bool deleteFile(String fileName = FILE_NAME_STANDARD) {
if (!isExistsFile(fileName)) {
showMessageSerial("O arquivo '" + fileName + "' não existe.");
return false;
}
bool isRemove = SD.remove(String(DIRECTORY_SEPARATOR) + fileName);
if (!isRemove)
showMessageSerial("Erro ao excluir o arquivo '" + fileName + "'.");
return isRemove;
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// Funções referentes ao display LCD
LiquidCrystal_I2C lcdI2C(0x27, 16, 2);
/**
Função responsável pela inicialização do display LCD
@return void
*/
void initDisplayLcd() {
showMessageSerial("Inicializando display LCD...", true);
lcdI2C.init();
lcdI2C.backlight();
lcdI2C.setCursor(0, 0);
showMessageSerial("Display LCD inicializado com sucesso.");
}
/**
Função responsável pelo "reset" do display LCD
@return void
*/
void resetDisplayLcd() {
lcdI2C.clear();
}
/**
Função responsável por exibição de mensagens no display LCD
@param message: Mensagem que deverá ser exibida ( string )
@param line: Número da linha no display LCD ( int )
@param isReset: Status informando se a mensagem atual do display deverá ser "resetada" ( bool )
@return void
*/
void showMessageDisplayLcd(String message, int line = 1, bool isReset = true) {
if (message.length() == 0) return;
if (isReset) resetDisplayLcd();
lcdI2C.setCursor(0, (line - 1));
lcdI2C.print(message);
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Funções referentes à conexão com Wi-Fi
WiFiClientSecure wifiClientSecure;
const char* WIFI_SSID = "WIFI-TEST";
const char* WIFI_PASSWORD = "c8dc4607-33ed-4c2d-98bc-42be3f6f05d0";
const int RETRIES_WIFI = 6;
const bool IS_SIMULATED_WIFI_DISCONNECT_RECONNECT_SUCCESS = true;
bool IS_SIMULATED_WIFI_SUCCESS = false;
/**
Função responsável por verificar o status de conexão com Wi-Fi
@return bool
*/
bool isWiFiConnected() {
if (IS_SIMULATED_WIFI_SUCCESS == true) return true;
return (WiFi.status() == WL_CONNECTED);
}
/**
Função responsável por efetuar conexão com Wi-Fi
@return void
*/
void connectWiFi() {
showMessageSerial("Conectando Wi-Fi...", true);
WiFi.disconnect(true);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
int retries = 0;
while (!isWiFiConnected() && retries < RETRIES_WIFI) {
executeWait(500);
showMessageSerial(".", false, false);
retries++;
}
if (retries > 0) showMessageSerial("");
if (isWiFiConnected())
showMessageSerial("Conexão com a rede Wi-Fi '" + String(WIFI_SSID) + "' efetuada com sucesso.");
else {
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
showMessageSerial("Ocorreu um erro ao conectar à rede Wi-Fi '" + String(WIFI_SSID) + "'.");
}
}
/**
Função responsável por executar uma simulação de reconexão com Wi-Fi
@return void
*/
void testConnectWiFi() {
if (IS_SIMULATED_WIFI_DISCONNECT_RECONNECT_SUCCESS == true && IS_SIMULATED_WIFI_SUCCESS == false)
IS_SIMULATED_WIFI_SUCCESS = true;
}
/**
Função responsável por executar uma simulação de desconexão com Wi-Fi
@return void
*/
void testDisconnectWiFi() {
if (IS_SIMULATED_WIFI_DISCONNECT_RECONNECT_SUCCESS == true)
IS_SIMULATED_WIFI_SUCCESS = false;
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// Funções referentes à conexão com MQTT
PubSubClient pubSubClient(wifiClientSecure);
const char* MQTT_SERVER = "185d027c94a842aeaaa6325eabb8d5b7.s1.eu.hivemq.cloud";
const char* MQTT_USER = "mqtt-test-s2c3p1";
const char* MQTT_PASSWORD = "f5e5e107-6C08-426a-9bc7-19c90c195615";
const int MQTT_PORT = 8883;
const int RETRIES_MQTT = 6;
const bool IS_SIMULATED_MQTT_DISCONNECT_RECONNECT_SUCCESS = true;
bool IS_SIMULATED_MQTT_SUCCESS = false;
/**
Função responsável por verificar o status de conexão do MQTT
@return bool
*/
bool isMqttConnected() {
if (IS_SIMULATED_MQTT_SUCCESS == true) return true;
return (pubSubClient.connected());
}
/**
Função responsável por efetuar conexão com MQTT
@return void
*/
void connectMqtt() {
// > Regras: A conexão com MQTT somente será executada caso a conexão com Wi-Fi tenha sido executada com sucesso
if (!isWiFiConnected()) return;
showMessageSerial("Conectando MQTT...", true);
if (IS_SIMULATED_MQTT_SUCCESS == true)
showMessageSerial("Conexão com o MQTT' " + String(MQTT_SERVER) + "' efetuada com sucesso.");
else {
pubSubClient.setServer(MQTT_SERVER, MQTT_PORT);
int retries = 0;
while (!isMqttConnected() && retries < RETRIES_MQTT) {
if (pubSubClient.connect("ESP32Client", MQTT_USER, MQTT_PASSWORD)) {
showMessageSerial("Conexão com o MQTT' " + String(MQTT_SERVER) + "' efetuada com sucesso.");
break;
}
else {
executeWait(500);
showMessageSerial(".", false, false);
retries++;
}
}
if (retries > 0) showMessageSerial("");
if (!isMqttConnected())
showMessageSerial("Ocorreu um erro ao conectar o MQTT '" + String(MQTT_SERVER) + "'.");
}
}
/**
Função responsável por executar uma simulação de reconexão com MQTT
@return void
*/
void testConnectMqtt() {
if (IS_SIMULATED_MQTT_DISCONNECT_RECONNECT_SUCCESS == true && IS_SIMULATED_MQTT_SUCCESS == false)
IS_SIMULATED_MQTT_SUCCESS = true;
}
/**
Função responsável por executar uma simulação de desconexão com MQTT
@return void
*/
void testDisconnectMqtt() {
if (IS_SIMULATED_MQTT_DISCONNECT_RECONNECT_SUCCESS == true)
IS_SIMULATED_MQTT_SUCCESS = false;
}
/**
Função responsável por executar manutenções na conexão MQTT
@return void
*/
void mqttLoop() {
pubSubClient.loop();
}
/**
Função responsável por enviar dados para o serviço "s2c3p1/save-data"
@param jsonData: Strin jscon contendo os dados ( string )
@param isTestSendData: Status informando se deverá ser executada uma simulação de envio de dados ( bool )
return bool
*/
bool s2c3p1_saveData(String jsonData, bool isTestSendData = false) {
if (jsonData.length() == 0) return false;
if (!isMqttConnected()) {
showMessageSerial("Não foi possível concluir o processo pois MQTT não está conectado.");
return false;
}
if (isTestSendData == true) {
showMessageSerial("Dados enviados por MQTT com sucesso.");
showMessageSerial(String(jsonData));
return true;
}
else {
bool isSend = pubSubClient.publish("s2c3p1/save-data", jsonData.c_str());
if (!isSend) {
showMessageSerial("Ocorreu um erro no processo de envio dos dados por MQTT.");
return false;
}
return true;
}
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// Funções referentes ao armazenamento e envio dos dados
bool IS_SIMULATED_SEND_DATA = false;
bool IS_SIMULATED_RESEND_DATA = true;
/**
Função responsável pela geração do json contendo os dados de medição
@return string
*/
String generateJsonData() {
float temperature = getTemperature();
float humidity = getHumidity();
float lux = getLux();
String datetime = getCurrentDatetime();
showMessageDisplayLcd("Temp.: " + String(temperature) + " C", 1);
showMessageDisplayLcd("Lux: " + String(lux), 2, false);
char jsonData[150];
snprintf(jsonData, sizeof(jsonData),
"{\"temperature\":%.2f,\"humidity\":%.2f,\"lux\":%.2f,\"datetime\":\"%s\"}",
temperature, humidity, lux, datetime.c_str());
return String(jsonData);
}
/**
Método responsável por retornar os dados armazenados no ambiente local
@return std::vector<String>
*/
std::vector<String> getLocalData() {
String localData;
// Opção para armazenamento local em um storage
if (strcmp(SAVE_DATA_TYPE, SAVE_DATA_LOCAL_STORAGE) == 0)
localData = readStorage();
// Opção para armazenamento local em arquivo
else if (strcmp(SAVE_DATA_TYPE, SAVE_DATA_SD_CARD) == 0)
localData = readFile();
std::vector<String> localDataItems;
if (localData.length() > 0) {
int start = 0;
int end = localData.indexOf(String(DATA_SEPARATOR));
while (end != -1) {
localDataItems.push_back(localData.substring(start, end));
start = end + 1;
end = localData.indexOf(String(DATA_SEPARATOR), start);
}
if (start < localData.length())
localDataItems.push_back(localData.substring(start));
}
return localDataItems;
}
/**
Método responsável por retornar os dados armazenados no ambiente local
@return int
*/
int getLocalDataLength() {
return getLocalData().size();
}
/**
Função responsável pelo envio dos dados
@return void
*/
void sendData() {
showMessageSerial("Iniciando leitura de dados para envio...", true);
String jsonData = generateJsonData();
std::vector<String> unsentData;
bool isStatusWiFiConnected = isWiFiConnected();
bool isStatusMqttConnected = isMqttConnected();
if (isStatusWiFiConnected && isStatusMqttConnected) {
bool isSendData = s2c3p1_saveData(jsonData, IS_SIMULATED_SEND_DATA);
if (!isSendData)
unsentData.push_back(jsonData);
}
else {
unsentData.push_back(jsonData);
if (!isStatusWiFiConnected)
showMessageSerial("Sem conexão com rede Wi-Fi.");
if (!isStatusMqttConnected)
showMessageSerial("Sem conexão com serviço MQTT.");
}
if (unsentData.size() > 0) {
for (String jsonDataItem : unsentData) {
// Opção para armazenamento local em um storage
if (strcmp(SAVE_DATA_TYPE, SAVE_DATA_LOCAL_STORAGE) == 0) {
writeStorage(jsonDataItem);
showMessageSerial("Dados armazenados em storage local para envio futuro.");
}
// Opção para armazenamento local em arquivo
else if (strcmp(SAVE_DATA_TYPE, SAVE_DATA_SD_CARD) == 0) {
writeFile(jsonDataItem);
showMessageSerial("Dados armazenados em arquivo local para envio futuro.");
}
}
}
}
/**
Função responsável pelo envio dos dados armazenados no ambiente local
@return void
*/
void sendLocalData() {
int localDataLength = getLocalDataLength();
if (localDataLength == 0) return;
showMessageSerial("> Existe(m) " + String(localDataLength) + " item(s) para ser(em) enviado(s) em lote. Iniciando processo de envio...", true);
if (!isWiFiConnected()) {
/*
> Regras: Essa ação será executada para simular um cenário de reconexão para que os dados armazenados localmente possam ser enviados em lote. Em
um cenário real, o processo de reconexão seria executado através da função connectWiFi(), na qual possui os componentes necessários para acessar
uma rede Wi-Fi verdadeira.
*/
testConnectWiFi();
connectWiFi();
}
if (!isMqttConnected()) {
/*
> Regras: Essa ação será executada para simular um cenário de reconexão junto ao serviço HiveMQ. Em um cenário real, o processo de reconexão
seria executado através da função connectMqtt(), na qual possui os componentes necessários para acessar o serviço.
*/
testConnectMqtt();
connectMqtt();
}
if (isWiFiConnected() && isMqttConnected()) {
std::vector<String> localDataItems = getLocalData();
std::vector<String> unsentData;
for (String jsonData : localDataItems) {
showMessageSerial("");
bool isSendData = s2c3p1_saveData(jsonData, IS_SIMULATED_RESEND_DATA);
if (!isSendData) {
unsentData.push_back(jsonData);
showMessageSerial("Os dados continuarão armazenados para envio futuro.");
}
}
String updatedData = "";
if (unsentData.size() > 0) {
for (String jsonDataItem : unsentData) {
updatedData += jsonDataItem + DATA_SEPARATOR;
}
}
// Opção para armazenamento local em um storage
if (strcmp(SAVE_DATA_TYPE, SAVE_DATA_LOCAL_STORAGE) == 0)
updateStorage(updatedData);
// Opção para armazenamento local em arquivo
else if (strcmp(SAVE_DATA_TYPE, SAVE_DATA_SD_CARD) == 0)
updateFile(updatedData);
}
/**
> Regras: Essas ações serão executadas para simular um cenário de falta de conexão no processo de envio de dados padrão ( sendData() ). Desta forma,
será possível executar a ação de envio de dados armazenados localmente ( sendLocalData() ).
*/
testDisconnectMqtt();
testDisconnectWiFi();
showMessageSerial("> Verificação de dados locais finalizada.", true);
}
// -=-=-=-=-=-=-=-=-=-=-=-=
// Execução dos componentes
void setup() {
Serial.begin(115200);
// Opção para armazenamento local em um storage
if (strcmp(SAVE_DATA_TYPE, SAVE_DATA_LOCAL_STORAGE) == 0)
initLocalStorage();
// Opção para armazenamento local em arquivo
else if (strcmp(SAVE_DATA_TYPE, SAVE_DATA_SD_CARD) == 0)
initSD();
initTemperatureSensor();
initDisplayLcd();
connectWiFi();
initDatetime();
connectMqtt();
resetDisplayLcd();
}
unsigned long lastDataSend = 0;
unsigned long lastLocalSend = 0;
void loop() {
mqttLoop();
// > Regras: Intervalo para execução do processo padrão de envio dados
if (millis() - lastDataSend >= INTERVAL_DELAY_PROCESS) {
lastDataSend = millis();
sendData();
}
// > Regras: Intervalo para execução do processo de envio de possíveis dados armazenados no ambiente local a cada x tempo
if (millis() - lastLocalSend >= INTERVAL_RESEND_LOCAL_DATA) {
lastLocalSend = millis();
sendLocalData();
}
}