#include <LiquidCrystal_I2C.h>
#include <PubSubClient.h>
#include "time.h"
#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 bool IS_SIMULATED_RETURN_DATETIME = true;
// -=-=-=-=-=-=-=-=-
// 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 buzzer
#define BUZZER_PIN 32
#define LED_PIN 33
/**
Função responsável pela inicialização do buzzer
@return void
*/
void initBuzzerSensor() {
showMessageSerial("Inicializando buzzer...", true);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(LED_PIN, OUTPUT);
showMessageSerial("Buzzer inicializado com sucesso.");
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// Funções referentes ao sensor de temperatura
#define NTC_PIN 34
const float BETA = 3950;
/**
Função responsável pela inicialização do sensor de temperatura
@return void
*/
void initTemperatureSensor() {
showMessageSerial("Inicializando sensor de temperatura...", true);
analogReadResolution(12);
showMessageSerial("Sensor de temperatura inicializado com sucesso.");
}
/**
Função responsável por retornar a temperatura
@return float
*/
float getTemperature() {
int ntcValue = analogRead(NTC_PIN);
float temperature = 1 / (log(1 / (4095.0 / ntcValue - 1)) / BETA + 1.0 / 298.15) - 273.15;
if (isnan(temperature)) temperature = -999.9;
return temperature;
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Funções referentes ao sensor de batimentos cardíacos
#define HEART_PIN 35
/**
Função responsável pela inicialização do sensor de batimentos cardíacos
@return void
*/
void initHeartbeatSensor() {
showMessageSerial("Inicializando sensor de batimentos cardíacos...", true);
pinMode(HEART_PIN, INPUT);
showMessageSerial("Sensor de batimentos cardíacos inicializado com sucesso.");
}
/**
Função responsável por retornar os batimentos cardíacos
@return float
*/
int getHeartbeat() {
int16_t pulseValue = analogRead(HEART_PIN);
float voltage = pulseValue * (5 / 4095.0);
int heartbeat = (voltage / 3.3) * 675;
return heartbeat;
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// 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;
bool IS_SIMULATED_WIFI_SUCCESS = true;
/**
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ções referentes à conexão com MQTT
PubSubClient pubSubClient(wifiClientSecure);
const char* MQTT_SERVER = "02472493227244b3a357b79bfced864a.s1.eu.hivemq.cloud";
const char* MQTT_USER = "mqtt-test-s2c3p2";
const char* MQTT_PASSWORD = "dfd7e6dc-26e0-48b7-Bde2-f8643750e56b";
const int MQTT_PORT = 8883;
const int RETRIES_MQTT = 6;
bool IS_SIMULATED_MQTT_SUCCESS = true;
/**
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 manutenções na conexão MQTT
@return void
*/
void mqttLoop() {
pubSubClient.loop();
}
/**
Função responsável por enviar dados para o serviço "s2c3p2/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 s2c3p2_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("s2c3p2/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 = true;
/**
Função responsável pela geração do json contendo os dados de medição
@return string
*/
String generateJsonData() {
float temperature = getTemperature();
int heartbeat = getHeartbeat();
String datetime = getCurrentDatetime();
showMessageDisplayLcd("Temp.: " + String(temperature) + " C", 1);
showMessageDisplayLcd("B/M: " + String(heartbeat), 2, false);
char jsonData[150];
snprintf(jsonData, sizeof(jsonData),
"{\"temperature\":%.2f,\"heartbeat\":%d,\"datetime\":\"%s\"}",
temperature, heartbeat, datetime.c_str());
return String(jsonData);
}
/**
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 = s2c3p2_saveData(jsonData, IS_SIMULATED_SEND_DATA);
else {
if (!isStatusWiFiConnected)
showMessageSerial("Sem conexão com rede Wi-Fi.");
if (!isStatusMqttConnected)
showMessageSerial("Sem conexão com serviço MQTT.");
}
}
/**
Função responsável por checar a temperatura
> Regras adotadas:
- Menor que 36 : Hipotermia
- Entre 36 e 37.5 : Temperatura normal
- Entre 37.5 e 38 : Febre leve
- Entre 38 e 39 : Febre moderada
- Maior que 39 : Febre alta
*/
int checkTemperature() {
float temperature = getTemperature();
int frequence = 0;
if (temperature <= 36)
frequence = 1000;
else if (temperature > 36 && temperature <= 37.5)
frequence = 0;
else if (temperature > 37.5 && temperature <= 38)
frequence = 800;
else if (temperature > 38 && temperature <= 39)
frequence = 1000;
else if (temperature > 39)
frequence = 1200;
return frequence;
}
/**
Função responsável por checar os batimentos cardíacos
> Regras adotadas:
- Menor que 40 : Bradicardia grave
- Entre 40 e 59 : Bradicardia leve
- Entre 60 e 100 : BPM normal
- Entre 101 e 120 : Taquicardia leve
- Entre 121 e 160 : Taquicardia moderada
- Maior que 161 : Taquicardia severa
*/
int checkHeartbeat() {
int heartbeat = getHeartbeat();
int frequence = 0;
if (heartbeat < 40)
frequence = 1000;
else if (heartbeat >= 40 && heartbeat <= 59)
frequence = 800;
else if (heartbeat >= 40 && heartbeat <= 100)
frequence = 0;
else if (heartbeat >= 101 && heartbeat <= 120)
frequence = 800;
else if (heartbeat >= 121 && heartbeat <= 160)
frequence = 1000;
else if (heartbeat >= 161)
frequence = 1200;
return frequence;
}
/**
Função responsável por checar de parâmetros
*/
void checkParameters() {
int frequenceTemperature = checkTemperature();
int frequenceHeartbeat = checkHeartbeat();
if (frequenceTemperature > 0 || frequenceHeartbeat > 0) {
int frequence = (frequenceTemperature >= frequenceHeartbeat) ? frequenceTemperature : frequenceHeartbeat;
tone(BUZZER_PIN, frequence);
digitalWrite(LED_PIN, HIGH);
}
else {
noTone(BUZZER_PIN);
digitalWrite(LED_PIN, LOW);
}
}
// -=-=-=-=-=-=-=-=-=-=-=-=
// Execução dos componentes
void setup() {
Serial.begin(115200);
initBuzzerSensor();
initTemperatureSensor();
initHeartbeatSensor();
initDisplayLcd();
connectWiFi();
initDatetime();
connectMqtt();
resetDisplayLcd();
}
unsigned long lastCheckDanger = 0;
unsigned long lastDataSend = 0;
void loop() {
mqttLoop();
// > Regras: Intervalo para execução da validação de parâmetros
if (millis() - lastCheckDanger >= 500) {
lastCheckDanger = millis();
checkParameters();
}
// > Regras: Intervalo para execução do processo padrão de envio dados
if (millis() - lastDataSend >= INTERVAL_DELAY_PROCESS) {
lastDataSend = millis();
sendData();
}
}
Informações importantes:
- Por padrão, o sensor de temperatura inicia em 24.00 ºC
- Por padrão, o sensor de BPM inicia em 0
- Por conta dos valores padrões acima, o sensor inicializará emitindo sinal sonoro
- A temperatura normal fica entre 36 ºC e 37.5 ºC
- O BPM normal fica entre 60 e 100