diff --git a/diagram.json b/diagram.json index d9665ff..081a680 100644 --- a/diagram.json +++ b/diagram.json @@ -3,186 +3,74 @@ "author": "Amaro Lopes", "editor": "wokwi", "parts": [ - { - "type": "board-esp32-devkit-c-v4", - "id": "esp", - "top": 0, - "left": 0, - "attrs": {} - }, - { - "type": "chip-heartrate", - "id": "chip1", - "top": -114.18, - "left": -148.8, - "attrs": {} - }, + { "type": "board-esp32-devkit-c-v4", "id": "esp", "top": 0, "left": 0, "attrs": {} }, + { "type": "chip-heartrate", "id": "chip1", "top": -114.18, "left": -148.8, "attrs": {} }, { "type": "wokwi-resistor", "id": "r1", "top": -33.6, "left": -67.75, "rotate": 90, - "attrs": { - "value": "1000" - } + "attrs": { "value": "1000" } + }, + { "type": "wokwi-slide-switch", "id": "sw1", "top": -130, "left": 127.9, "attrs": {} }, + { "type": "wokwi-mpu6050", "id": "imu1", "top": 167.02, "left": 175.12, "attrs": {} }, + { "type": "wokwi-led", "id": "led1", "top": -32.4, "left": 167, "attrs": { "color": "red" } }, + { + "type": "wokwi-text", + "id": "text1", + "top": -163.2, + "left": 67.2, + "attrs": { "text": "devagar" } }, { - "type": "wokwi-slide-switch", - "id": "sw1", - "top": -130, - "left": 127.9, - "attrs": {} + "type": "wokwi-text", + "id": "text2", + "top": -163.2, + "left": 153.6, + "attrs": { "text": "rápido" } }, { - "type": "wokwi-mpu6050", - "id": "imu1", - "top": 99.82, - "left": -199.28, - "attrs": {} + "type": "board-ssd1306", + "id": "oled1", + "top": 99.14, + "left": 288.23, + "attrs": { "i2cAddress": "0x3c" } }, { - "type": "wokwi-led", - "id": "led1", - "top": -32.4, - "left": 167, - "attrs": { - "color": "red" - } - } + "type": "wokwi-pushbutton", + "id": "btn1", + "top": 111.8, + "left": -144, + "attrs": { "color": "green", "xray": "1" } + }, + { "type": "wokwi-vcc", "id": "vcc1", "top": -37.64, "left": 326.4, "attrs": {} }, + { "type": "wokwi-vcc", "id": "vcc2", "top": -37.64, "left": -230.4, "attrs": {} } ], "connections": [ - [ - "esp:TX", - "$serialMonitor:RX", - "", - [] - ], - [ - "esp:RX", - "$serialMonitor:TX", - "", - [] - ], - [ - "esp:3V3", - "chip1:VCC", - "green", - [ - "h-187.01", - "v-134.4" - ] - ], - [ - "chip1:GND", - "esp:GND.2", - "black", - [ - "v28.8", - "h244.76" - ] - ], - [ - "r1:1", - "chip1:BTN", - "green", - [ - "h0" - ] - ], - [ - "r1:2", - "esp:3V3", - "green", - [ - "h0" - ] - ], - [ - "esp:35", - "chip1:OUT", - "green", - [ - "h-23.81", - "v-182.4" - ] - ], - [ - "sw1:1", - "chip1:BTN", - "green", - [ - "v0" - ] - ], - [ - "sw1:2", - "sw1:3", - "black", - [ - "v0" - ] - ], - [ - "sw1:3", - "esp:GND.2", - "black", - [ - "v0" - ] - ], - [ - "imu1:VCC", - "esp:3V3", - "red", - [ - "v0" - ] - ], - [ - "imu1:GND", - "esp:GND.2", - "black", - [ - "v-134.4", - "h28.88" - ] - ], - [ - "imu1:SCL", - "esp:22", - "green", - [ - "v9.6", - "h268.88", - "v-67.2" - ] - ], - [ - "imu1:SDA", - "esp:21", - "green", - [ - "v0" - ] - ], - [ - "esp:GND.2", - "led1:C", - "black", - [ - "v-19.2", - "h-4.76" - ] - ], - [ - "esp:17", - "led1:A", - "green", - [ - "h0" - ] - ] + [ "esp:TX", "$serialMonitor:RX", "", [] ], + [ "esp:RX", "$serialMonitor:TX", "", [] ], + [ "esp:3V3", "chip1:VCC", "red", [ "h-187.01", "v-134.4" ] ], + [ "chip1:GND", "esp:GND.2", "black", [ "v28.8", "h297.6", "v96" ] ], + [ "r1:1", "chip1:BTN", "red", [ "h0" ] ], + [ "esp:35", "chip1:OUT", "green", [ "h-23.81", "v-182.4" ] ], + [ "sw1:1", "chip1:BTN", "green", [ "v0" ] ], + [ "sw1:2", "sw1:3", "black", [ "v0" ] ], + [ "sw1:3", "esp:GND.2", "black", [ "v0" ] ], + [ "imu1:SDA", "esp:21", "green", [ "v0" ] ], + [ "esp:GND.2", "led1:C", "black", [ "h81.24" ] ], + [ "esp:17", "led1:A", "green", [ "h0" ] ], + [ "oled1:GND", "esp:GND.2", "black", [ "v0" ] ], + [ "oled1:SCL", "esp:22", "green", [ "v0" ] ], + [ "esp:21", "oled1:SDA", "green", [ "h254.44" ] ], + [ "esp:3V3", "r1:2", "red", [ "h0" ] ], + [ "oled1:VCC", "vcc1:VCC", "red", [ "v0" ] ], + [ "vcc2:VCC", "esp:3V3", "red", [ "v0" ] ], + [ "imu1:SCL", "esp:22", "green", [ "v0" ] ], + [ "imu1:GND", "esp:GND.2", "black", [ "v0" ] ], + [ "imu1:VCC", "vcc1:VCC", "red", [ "v-153.6", "h86.48" ] ], + [ "btn1:1.r", "esp:25", "green", [ "v0" ] ], + [ "esp:GND.1", "btn1:2.r", "black", [ "h0" ] ] ], "dependencies": {} } \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 261c781..769c204 100644 --- a/platformio.ini +++ b/platformio.ini @@ -16,3 +16,4 @@ lib_deps = hwspeedy/DHT-Sensor knolleary/PubSubClient adafruit/Adafruit MPU6050@^2.2.8 + arduinogetstarted/ezButton diff --git a/src/main.ino b/src/main.ino index a8ac417..8050b91 100644 --- a/src/main.ino +++ b/src/main.ino @@ -1,52 +1,88 @@ // ============================================================================ -// Sistema de monitoramento de cozinha com ESP32 -// Monitora temperatura, umidade e gás com detecção avançada de incêndio -// Envia dados via MQTT e controla coifa e ar-condicionado +// Monitor de Repetições e Passos com ESP32 +// Monitora aceleração (MPU6050), frequência cardíaca (sensor de pulso) +// Exibe dados em OLED, envia via MQTT +// Modo Repetição: detecta exercícios com análise de tempo e intensidade +// Modo Pedômetro: conta passos baseado em aceleração // ============================================================================ +// ============================================================================ // INCLUDES +// ============================================================================ +// Comunicação e rede +#include +#include + +// Sensores e I2C +#include #include #include -#include // MQTT -#include // WiFi -#include + +// Display +#include +#include + +// Interface +#include // ============================================================================ -// DEFINES - Pinos GPIO +// CONFIGURAÇÃO - Pinos GPIO // ============================================================================ -#define PULSE_PIN 35 // Pin for the pulse sensor -#define LED_PIN 17 // Pin for the LED +#define PULSE_PIN 35 +#define LED_PIN 17 +#define MODE_BUTTON_PIN 25 // ============================================================================ -// DEFINES - Configurações estáticas +// CONFIGURAÇÃO - Display OLED SSD1306 // ============================================================================ -#define SAMPLE_PERIOD_MS 10 // 100 Hz -#define FILTER_ALPHA 0.9 +#define SCREEN_WIDTH 128 // Largura em pixels +#define SCREEN_HEIGHT 64 // Altura em pixels +#define OLED_RESET -1 // Pin de reset (compartilhado) +#define SCREEN_ADDRESS 0x3C // Endereço I2C do display -#define TH_START 1.15 -#define TH_END 1.05 - -#define MIN_PEAK_AMPLITUDE 1.5 - -#define MIN_REP_TIME 800 -#define MAX_REP_TIME 5000 // ============================================================================ -// ENUMS +// CONFIGURAÇÃO - Algoritmo de Detecção de Repetições +// ============================================================================ +#define SAMPLE_PERIOD_MS 10 // Período de amostragem em ms (100 Hz) +#define FILTER_ALPHA 0.9 // Fator de filtro exponencial (0-1) +#define ACCEL_REST_THRESHOLD 0.3 // Aceleração < 0.3 m/s² = repouso +#define ACCEL_ACTIVE_THRESHOLD 0.8// Aceleração > 0.8 m/s² = movimento ativo +#define MIN_PEAK_AMPLITUDE 1.5 // Amplitude mínima do pico em m/s² +#define MIN_REP_TIME 800 // Tempo mínimo de repetição em ms +#define MAX_REP_TIME 5000 // Tempo máximo de repetição em ms + +// ============================================================================ +// CONFIGURAÇÃO - Pedômetro +// ============================================================================ +#define STEP_ACCEL_THRESHOLD 0.8 // Limiar de aceleração para passo +#define STEP_DEBOUNCE_MS 200 // Tempo mínimo entre passos +#define BUTTON_DEBOUNCE_MS 50 // Debounce do botão +#define PEDOMETER_IDLE_TIMEOUT_MS 30000 // Timeout de inatividade (ms) +#define DISPLAY_UPDATE_INTERVAL_MS 100 // Limita display a 10 FPS + +// ============================================================================ +// CONFIGURAÇÃO - WiFi e MQTT +// ============================================================================ + +#define WIFI_SSID "Wokwi-GUEST" // SSID da rede WiFi +#define WIFI_PASSWORD "" // Senha da rede WiFi +#define MQTT_BROKER "77.37.69.84" // Endereço do broker MQTT +#define MQTT_PORT 1883 // Porta MQTT padrão +#define MQTT_RECONNECT_INTERVAL_MS 5000 // Intervalo de reconexão + +// ============================================================================ +// ENUMS - Estados do Sistema // ============================================================================ enum EstadoRepeticao { OCIOSO, - DESCANSO, SUBINDO, - DESCENDO + DESCENDO, + DESCANSO }; // ============================================================================ -// CONSTANTES - WiFi e MQTT +// CONSTANTES - MQTT // ============================================================================ -const char* WIFI_SSID = "Wokwi-GUEST"; -const char* WIFI_PASSWORD = ""; -const char* MQTT_BROKER = "77.37.69.84"; -const int MQTT_PORT = 1883; const char* MQTT_SUB_TOPICS[] = { "academia/reps", "academia/sets", @@ -56,203 +92,330 @@ const char* MQTT_PUB_TOPIC = "academia"; const int MQTT_SUB_COUNT = sizeof(MQTT_SUB_TOPICS) / sizeof(MQTT_SUB_TOPICS[0]); // ============================================================================ -// VARIÁVEIS GLOBAIS - Objetos +// OBJETOS GLOBAIS // ============================================================================ WiFiClient espClient; PubSubClient mqtt(espClient); Adafruit_MPU6050 mpu; sensors_event_t event; +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); +ezButton button(MODE_BUTTON_PIN); // ============================================================================ -// VARIÁVEIS GLOBAIS - Estado do sistema +// VARIÁVEIS GLOBAIS - Estado Atual do Sistema // ============================================================================ - EstadoRepeticao estado = OCIOSO; +bool pedometroAtivo = false; -unsigned long ultimaAmostra = 0; - -unsigned long t_inicio = 0; -unsigned long t_pico = 0; -unsigned long t_fim = 0; -unsigned long t_descanso = 30; - +// ============================================================================ +// VARIÁVEIS GLOBAIS - Leitura e Processamento de Sensores +// ============================================================================ +// Aceleração (MPU6050) float aceleracaoFiltrada = 1.0; float aceleracaoAnterior = 1.0; float aceleracaoPico = 0; +unsigned long ultimaAmostra = 0; + +// Frequência cardíaca (sensor de pulso) +int frequenciaCardiacaAtual = 0; + +// ============================================================================ +// VARIÁVEIS GLOBAIS - Detecção de Repetições +// ============================================================================ +unsigned long tInicio = 0; +unsigned long tPico = 0; +unsigned long tFim = 0; +unsigned long tDescanso = 30; int repeticoes = 0; -int max_reps = 0; -int set = 0; -int sets = 0; +int maxRepeticoes = 0; +int serie = 0; +int series = 0; -// Frequência cardíaca (atual e agregados por repetição) -int frequenciaCardiacaAtual = 0; +// Agregação de frequência cardíaca por repetição unsigned long somaFrequenciaCardiaca = 0; unsigned int contagemFrequenciaCardiaca = 0; -// MQTT reconnect control -unsigned long ultimoMqttAttempt = 0; -const unsigned long MQTT_RECONNECT_INTERVAL_MS = 5000; +// ============================================================================ +// VARIÁVEIS GLOBAIS - Pedômetro +// ============================================================================ +long totalPassos = 0; +unsigned long ultimoTempoPassso = 0; +unsigned long ultimaAtividadePedometro = 0; -// Buffer MQTT +// ============================================================================ +// VARIÁVEIS GLOBAIS - Comunicação MQTT +// ============================================================================ char msg[256]; char MQTT_CLIENTID[32]; +unsigned long ultimoMqttAttempt = 0; +unsigned long ultimoDisplayUpdate = 0; // ============================================================================ -// FUNÇÕES AUXILIARES - Callbacks MQTT +// COMUNICAÇÃO - MQTT // ============================================================================ + void mqttCallback(char* topic, byte* payload, unsigned int length) { - Serial.print("[MQTT] Mensagem em ["); - Serial.print(topic); - Serial.println("]"); - - // Copia payload para buffer null-terminated char buf[64]; int n = (length < (int)sizeof(buf) - 1) ? length : (int)sizeof(buf) - 1; memcpy(buf, payload, n); buf[n] = '\0'; - Serial.print("[MQTT] payload: "); - Serial.println(buf); - - // Processa tópicos if (strcmp(topic, "academia/reps") == 0) { - int v = atoi(buf); - if (!isnan(v)) { - Serial.print("[MQTT] reps = "); - Serial.println(v); - max_reps = v; + int valor = atoi(buf); + if (!isnan(valor)) { + maxRepeticoes = valor; } } else if (strcmp(topic, "academia/sets") == 0) { - int v = atoi(buf); - if (!isnan(v)) { - Serial.print("[MQTT] sets = "); - Serial.println(v); - sets = v; + int valor = atoi(buf); + if (!isnan(valor)) { + series = valor; } } else if (strcmp(topic, "academia/t_descanso") == 0) { - int v = atoi(buf); - if (!isnan(v)) { - Serial.print("[MQTT] t_descanso = "); - Serial.println(v); - t_descanso = v; + int valor = atoi(buf); + if (!isnan(valor)) { + tDescanso = valor; } } } // ============================================================================ -// FUNÇÕES AUXILIARES - Conectividade +// CONECTIVIDADE - WiFi e MQTT // ============================================================================ -// Conecta o ESP32 à rede WiFi + void connectWiFi() { - Serial.print("[WiFi] Conectando..."); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); - int attempts = 0; - while (WiFi.status() != WL_CONNECTED && attempts < 20) { + int tentativas = 0; + while (WiFi.status() != WL_CONNECTED && tentativas < 20) { delay(500); - Serial.print("."); - attempts++; + tentativas++; } - Serial.println("\r\n[WiFi] Conectado!"); } -// Inscreve em tópicos MQTT para receber atualizações de limiares void mqttSubscribe() { - Serial.println("[MQTT] Conectado!"); for (int i = 0; i < MQTT_SUB_COUNT; ++i) { - if (mqtt.subscribe(MQTT_SUB_TOPICS[i])) { - Serial.print("[MQTT] Tópico: "); - Serial.println(MQTT_SUB_TOPICS[i]); - } + mqtt.subscribe(MQTT_SUB_TOPICS[i]); } } -// Conecta ao broker MQTT void connectMQTT() { mqtt.setServer(MQTT_BROKER, MQTT_PORT); mqtt.setCallback(mqttCallback); while (!mqtt.connected()) { - Serial.println("[MQTT] Conectando..."); if (mqtt.connect(MQTT_CLIENTID)) { mqttSubscribe(); break; } else { - Serial.print("[MQTT] Falha rc="); - Serial.println(mqtt.state()); delay(2000); } } } // ============================================================================ -// FUNÇÕES AUXILIARES - Sensores +// INTERFACE - Display OLED // ============================================================================ -// Lê valores de aceleração do sensor MPU + +void displayRepetitionStatus() +{ + display.clearDisplay(); + display.setTextSize(1); + display.setTextColor(SSD1306_WHITE); + display.setCursor(0, 0); + display.println("Modo Repeticao"); + + display.setTextSize(2); + display.setCursor(0, 15); + display.print("Reps: "); + display.println(repeticoes); + + display.setCursor(0, 35); + display.print("Set: "); + display.print(serie); + display.print("/"); + display.println(series); + + display.setTextSize(1); + display.setCursor(0, 55); + display.print("FC: "); + display.print(frequenciaCardiacaAtual); + display.println(" bpm"); + + display.display(); +} + +void displayPedometerStatus() +{ + display.clearDisplay(); + display.setTextSize(1); + display.setTextColor(SSD1306_WHITE); + display.setCursor(0, 0); + display.println("Modo Pedometro"); + + display.setTextSize(2); + display.setCursor(0, 20); + display.print("Passos: "); + display.println(totalPassos); + + display.setTextSize(1); + display.setCursor(0, 55); + display.print("FC: "); + display.print(frequenciaCardiacaAtual); + display.println(" bpm"); + + display.display(); +} + +void displayRestTimer() +{ + display.clearDisplay(); + display.setTextSize(1); + display.setTextColor(SSD1306_WHITE); + display.setCursor(0, 0); + display.println("DESCANSO"); + + unsigned long tempoDecorrido = (millis() - tFim) / 1000; + long tempoRestante = tDescanso - tempoDecorrido; + + display.setTextSize(2); + display.setCursor(0, 20); + if (tempoRestante > 0) { + display.print("Restam: "); + display.println(tempoRestante); + } else { + display.println("PRONTO!"); + } + + display.setTextSize(1); + display.setCursor(0, 55); + display.print("FC: "); + display.print(frequenciaCardiacaAtual); + display.println(" bpm"); + + display.display(); +} + +void displayPedometerIdleScreen() +{ + display.clearDisplay(); + display.setTextSize(1); + display.setTextColor(SSD1306_WHITE); + display.setCursor(0, 0); + display.println("Pedometro OCIOSO"); + + display.setTextSize(2); + display.setCursor(0, 25); + display.println("Aguardando..."); + + display.setTextSize(1); + display.setCursor(0, 55); + display.print("FC: "); + display.print(frequenciaCardiacaAtual); + display.println(" bpm"); + + display.display(); +} + +// ============================================================================ +// SENSORES - Leitura e Processamento +// ============================================================================ + void amostraMPU() { sensors_event_t a, g, temp; mpu.getEvent(&a, &g, &temp); float magnitudeAceleracao = sqrt( - a.acceleration.x * a.acceleration.x - + a.acceleration.y * a.acceleration.y - + a.acceleration.z * a.acceleration.z); + a.acceleration.x * a.acceleration.x + + a.acceleration.y * a.acceleration.y + + a.acceleration.z * a.acceleration.z + ); float aceleracaoCorrigida = magnitudeAceleracao - 9.81; + aceleracaoFiltrada = FILTER_ALPHA * aceleracaoFiltrada + + (1.0 - FILTER_ALPHA) * aceleracaoCorrigida; - aceleracaoFiltrada = FILTER_ALPHA * aceleracaoFiltrada + (1.0 - FILTER_ALPHA) * aceleracaoCorrigida; + if (abs(aceleracaoFiltrada) > 0.1) { + ultimaAtividadePedometro = millis(); + } - detectarRepeticao(aceleracaoFiltrada); + if (!pedometroAtivo) { + detectarRepeticao(aceleracaoFiltrada); + } else { + detectaPasso(aceleracaoFiltrada); + } +} + +void detectaPasso(float aceleracaoAtual) +{ + unsigned long tempoAtual = millis(); + + if ((tempoAtual - ultimoTempoPassso) > STEP_DEBOUNCE_MS) { + if (aceleracaoAtual > STEP_ACCEL_THRESHOLD && + aceleracaoAnterior < STEP_ACCEL_THRESHOLD) { + totalPassos++; + ultimoTempoPassso = tempoAtual; + ultimaAtividadePedometro = tempoAtual; + + snprintf(msg, sizeof(msg), + "{\"passos\": %ld, \"fc\": %d}", + totalPassos, frequenciaCardiacaAtual); + mqtt.publish(MQTT_PUB_TOPIC, msg); + } + } + + aceleracaoAnterior = aceleracaoAtual; } // ============================================================================ -// FUNÇÕES - Detecção de alarmes +// DETECÇÃO - Repetições // ============================================================================ -// Verifica qual alarme deve ser ativado baseado nos limites + void detectarRepeticao(float sinal) { unsigned long agora = millis(); switch (estado) { - case OCIOSO: - if (sinal > TH_START) { - t_inicio = agora; + if (sinal > ACCEL_ACTIVE_THRESHOLD) { + tInicio = agora; aceleracaoPico = sinal; estado = SUBINDO; - // Inicia acumulação de frequência cardíaca para esta repetição somaFrequenciaCardiaca = 0; contagemFrequenciaCardiaca = 0; + digitalWrite(LED_PIN, HIGH); } break; case SUBINDO: - if (sinal > aceleracaoPico) + if (sinal > aceleracaoPico) { aceleracaoPico = sinal; + } if (sinal < aceleracaoAnterior) { - t_pico = agora; + tPico = agora; estado = DESCENDO; } break; case DESCENDO: - if (sinal < TH_END) { - t_fim = agora; - + if (sinal < ACCEL_REST_THRESHOLD) { + tFim = agora; analisarRepeticao(); verificarSerie(); + digitalWrite(LED_PIN, LOW); } break; + + case DESCANSO: + break; } - // Se estamos no meio de uma repetição, acumula a frequência cardíaca atual - if (estado != OCIOSO) { + if (estado != OCIOSO && estado != DESCANSO) { somaFrequenciaCardiaca += (unsigned long)frequenciaCardiacaAtual; contagemFrequenciaCardiaca++; } @@ -262,52 +425,39 @@ void detectarRepeticao(float sinal) void analisarRepeticao() { - unsigned long tempoTotal = t_fim - t_inicio; - unsigned long tempoSubida = t_pico - t_inicio; - unsigned long tempoDescida = t_fim - t_pico; - - bool ruim = false; - - Serial.print("Total: "); - Serial.print(tempoTotal); - Serial.print(" ms"); + unsigned long tempoTotal = tFim - tInicio; + unsigned long tempoSubida = tPico - tInicio; + unsigned long tempoDescida = tFim - tPico; + bool invalida = false; if (aceleracaoPico < MIN_PEAK_AMPLITUDE) { - Serial.print(" | Repetição parcial"); - ruim = true; + invalida = true; + Serial.print(" Repetição parcial "); } if (tempoTotal < MIN_REP_TIME) { - Serial.print(" | Muito rápido"); - ruim = true; + invalida = true; + Serial.print(" Rápido demais "); } - if (tempoTotal > MAX_REP_TIME) { - Serial.print(" | Muito lento"); - ruim = true; + invalida = true; + Serial.print(" Lento demais "); } if (tempoDescida < tempoSubida * 0.6) { - Serial.print(" | Deixando cair o peso"); - ruim = true; + invalida = true; + Serial.print(" Deixando o peso cair "); } - if (!ruim) { - Serial.print(" | Boa repetição"); + if (!invalida) { repeticoes++; - Serial.print(" | Repetição "); - Serial.print(repeticoes); } - // Calcula e mostra frequência cardíaca média durante a repetição + unsigned int fcMedia = 0; if (contagemFrequenciaCardiaca > 0) { fcMedia = (unsigned int)(somaFrequenciaCardiaca / contagemFrequenciaCardiaca); - Serial.print(" | FC média: "); - Serial.print(fcMedia); - Serial.print(" bpm"); } - // Zera agregados após uso somaFrequenciaCardiaca = 0; contagemFrequenciaCardiaca = 0; @@ -316,21 +466,19 @@ void analisarRepeticao() fcMedia, tempoTotal, tempoSubida, tempoDescida); mqtt.publish(MQTT_PUB_TOPIC, msg); - Serial.println(); } void verificarSerie() { - if (repeticoes >= max_reps && max_reps > 0) { - Serial.println("[SÉRIE] Série finalizada, iniciando descanso..."); + if (repeticoes >= maxRepeticoes && maxRepeticoes > 0) { repeticoes = 0; estado = DESCANSO; - set++; digitalWrite(LED_PIN, HIGH); - if (set >= sets && sets > 0) { - Serial.println("[TREINO] Treino finalizado, parabéns!"); - set = 0; + serie++; + + if (serie >= series && series > 0) { + serie = 0; } } else { estado = OCIOSO; @@ -338,42 +486,45 @@ void verificarSerie() } // ============================================================================ -// FUNÇÕES - Inicialização +// INICIALIZAÇÃO // ============================================================================ -// Inicializa o ESP32 e configura os periféricos + void setup() { Serial.begin(115200); delay(100); - Serial.println("\n[SETUP] Iniciando sistema..."); - - // Gera ID único MQTT snprintf(MQTT_CLIENTID, sizeof(MQTT_CLIENTID), "esp32_%08X", (uint32_t)(ESP.getEfuseMac() & 0xFFFFFFFF)); - // Conecta + Wire.begin(); + + if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) { + for (;;); + } + connectWiFi(); connectMQTT(); while (!mpu.begin()) { - Serial.println("MPU6050 not connected!"); delay(1000); } + mpu.setAccelerometerRange(MPU6050_RANGE_4_G); mpu.setGyroRange(MPU6050_RANGE_500_DEG); mpu.setFilterBandwidth(MPU6050_BAND_21_HZ); - Serial.println("MPU6050 ready!"); pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LOW); - Serial.println("[SETUP] Pronto!\n"); + + pinMode(MODE_BUTTON_PIN, INPUT_PULLUP); + button.setDebounceTime(BUTTON_DEBOUNCE_MS); } // ============================================================================ // LOOP PRINCIPAL // ============================================================================ -// Loop principal - executa continuamente + void loop() { if (!mqtt.connected()) { @@ -381,22 +532,58 @@ void loop() } mqtt.loop(); - if (millis() - ultimaAmostra >= SAMPLE_PERIOD_MS && estado != DESCANSO) { - // Verifica MQTT + button.loop(); + if (button.isPressed()) { + pedometroAtivo = !pedometroAtivo; + + if (pedometroAtivo) { + totalPassos = 0; + ultimoTempoPassso = 0; + aceleracaoAnterior = 1.0; + ultimaAtividadePedometro = millis(); + } else { + estado = OCIOSO; + repeticoes = 0; + serie = 0; + aceleracaoAnterior = 1.0; + somaFrequenciaCardiaca = 0; + contagemFrequenciaCardiaca = 0; + } + + display.clearDisplay(); + display.display(); + } + + if (millis() - ultimaAmostra >= SAMPLE_PERIOD_MS) { int16_t valorPulso = analogRead(PULSE_PIN); - - // Converter valorPulso para tensão float tensao = valorPulso * (5.0 / 4095.0); - - // Atualiza frequência cardíaca atual (valor usado nas acumulações) frequenciaCardiacaAtual = (int)((tensao / 3.3) * 675); - ultimaAmostra += SAMPLE_PERIOD_MS; amostraMPU(); - } else if (millis() - t_fim >= t_descanso * 100 && estado == DESCANSO) { + ultimaAmostra += SAMPLE_PERIOD_MS; + } + + // Atualizar display apenas a cada DISPLAY_UPDATE_INTERVAL_MS (100ms = 10 FPS) + if (millis() - ultimoDisplayUpdate >= DISPLAY_UPDATE_INTERVAL_MS) { + ultimoDisplayUpdate = millis(); + + if (estado == DESCANSO) { + displayRestTimer(); + } else if (pedometroAtivo && + (millis() - ultimaAtividadePedometro > PEDOMETER_IDLE_TIMEOUT_MS)) { + displayPedometerIdleScreen(); + } else { + if (pedometroAtivo) { + displayPedometerStatus(); + } else { + displayRepetitionStatus(); + } + } + } + + if (estado == DESCANSO && millis() - tFim >= tDescanso * 1000) { estado = OCIOSO; digitalWrite(LED_PIN, LOW); - Serial.println("[DESCANSO] Descanso finalizado, pronto para próxima série."); } } \ No newline at end of file