#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include "hardware/i2c.h"
#include "hardware/pwm.h" // --- NOVO --- Incluído para usar PWM
#include "pico/unique_id.h"
// Includes para Rede, MQTT e TCP
#include "lwip/apps/mqtt.h"
#include "lwip/dns.h"
#include "lwip/tcp.h"
#include "lwip/err.h"
// --- CONFIGURAÇÕES DO USUÁRIO ---
#define WIFI_SSID "iPhone de Leonardo"
#define WIFI_PASSWORD "12345678"
// Broker 1: ThingsBoard
#define MQTT_HOST_THINGSBOARD "mqtt.thingsboard.cloud"
#define MQTT_USER_THINGSBOARD "rR63vXMzWqR1zlovy2ZQ"
#define MQTT_TOPIC_THINGSBOARD "v1/devices/me/telemetry"
// --- NOVO: Tópico para alertas de presença. Usaremos o mesmo tópico de telemetria, mas com um payload diferente. ---
// Broker 2: Interscity (Servidor da UFMA)
#define MQTT_HOST_INTERSCITY "cidadesinteligentes.lsdi.ufma.br"
#define INTERSCITY_RESOURCE_UUID "5de897cb-c645-40fb-9a00-45f5c6ea66b9"
// API de Geolocalização por IP
#define GEO_API_HOST "ip-api.com"
// --- NOVO: DEFINIÇÕES DE PINOS PARA NOVOS HARDWARES ---
#define PRESENCE_SENSOR_PIN 15 // Pino GPIO para o sensor de presença (PIR)
#define BUZZER_PIN 16 // Pino GPIO para o buzzer (deve ser um pino com capacidade PWM)
#define RELAY_PIN 17 // Pino GPIO para o módulo relé
// --- VARIÁVEIS GLOBAIS ---
char client_id_str[20];
volatile float g_latitude = 0.0f;
volatile float g_longitude = 0.0f;
volatile bool g_location_retrieved = false;
static bool g_last_presence_state = false; // --- NOVO --- Armazena o último estado do sensor de presença
// --- SENSOR AHT10 (sem alterações) ---
bool read_aht10(float *temperature, float *humidity) {
uint8_t buffer[6];
uint8_t trigger_cmd[] = {0xAC, 0x33, 0x00};
if (i2c_write_blocking(i2c0, 0x38, trigger_cmd, 3, false) != 3) return false;
sleep_ms(80);
if (i2c_read_blocking(i2c0, 0x38, buffer, 6, false) != 6) return false;
if ((buffer[0] & 0x80) != 0) {
uint8_t init_cmd[] = {0xE1, 0x08, 0x00};
i2c_write_blocking(i2c0, 0x38, init_cmd, 3, false);
sleep_ms(300);
return false;
}
uint32_t raw_humidity = ((buffer[1] << 16) | (buffer[2] << 8) | buffer[3]) >> 4;
uint32_t raw_temp = ((buffer[3] & 0x0F) << 16) | (buffer[4] << 8) | buffer[5];
*humidity = ((float)raw_humidity / 1048576.0) * 100.0;
*temperature = ((float)raw_temp / 1048576.0) * 200.0 - 50.0;
return true;
}
// --- LÓGICA MQTT E DNS (sem alterações) ---
typedef struct MQTT_CLIENT_STATE_T_ {
ip_addr_t remote_ip;
mqtt_client_t *mqtt_client;
const char* name;
} MQTT_CLIENT_STATE_T;
static void mqtt_connection_cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t status);
static void mqtt_dns_found_cb(const char *name, const ip_addr_t *ipaddr, void *callback_arg) {
MQTT_CLIENT_STATE_T *state = (MQTT_CLIENT_STATE_T *)callback_arg;
if (ipaddr != NULL) {
state->remote_ip = *ipaddr;
printf("DNS resolvido para %s: %s\n", name, ip4addr_ntoa(ipaddr));
struct mqtt_connect_client_info_t ci;
memset(&ci, 0, sizeof(ci));
ci.client_id = client_id_str;
if (strcmp(state->name, "ThingsBoard") == 0) {
ci.client_user = MQTT_USER_THINGSBOARD;
} else if (strcmp(state->name, "Interscity") == 0) {
ci.client_user = NULL;
ci.client_pass = NULL;
}
err_t err = mqtt_client_connect(state->mqtt_client, &state->remote_ip, 1883, mqtt_connection_cb, state, &ci);
if (err != ERR_OK) {
printf("Falha ao iniciar conexão MQTT com [%s]: %d\n", state->name, err);
}
} else {
printf("Falha ao resolver DNS para %s\n", name);
}
}
static void mqtt_connection_cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t status) {
MQTT_CLIENT_STATE_T *state = (MQTT_CLIENT_STATE_T *)arg;
if (status == MQTT_CONNECT_ACCEPTED) {
printf(">>> SUCESSO! Conexão MQTT com [%s] estabelecida! <<<\n", state->name);
} else {
printf(">>> FALHA! Conexão com [%s] falhou, status: %d.\n", state->name, status);
}
}
// --- LÓGICA HTTP (GEOLOCALIZAÇÃO) (sem alterações) ---
typedef struct HTTP_GET_STATE_T_ {
struct tcp_pcb *pcb;
ip_addr_t remote_ip;
char http_request[256];
char http_response_buffer[2048];
int buffer_pos;
bool headers_done;
} HTTP_GET_STATE_T;
static err_t http_close_connection(void *arg, struct tcp_pcb *pcb) {
HTTP_GET_STATE_T *state = (HTTP_GET_STATE_T*)arg;
err_t err = ERR_OK;
if (pcb != NULL) {
tcp_arg(pcb, NULL);
tcp_recv(pcb, NULL);
tcp_err(pcb, NULL);
err = tcp_close(pcb);
if (err != ERR_OK) {
printf("Falha ao fechar conexão TCP, abortando. err=%d\n", err);
tcp_abort(pcb);
err = ERR_ABRT;
}
}
if (state) {
free(state);
}
return err;
}
static void parse_location_from_json(const char* json_body) {
const char *lat_key = "\"lat\":";
const char *lon_key = "\"lon\":";
char *lat_ptr = strstr(json_body, lat_key);
char *lon_ptr = strstr(json_body, lon_key);
if (lat_ptr && lon_ptr) {
sscanf(lat_ptr + strlen(lat_key), "%f", (float*)&g_latitude);
sscanf(lon_ptr + strlen(lon_key), "%f", (float*)&g_longitude);
printf(">>> SUCESSO! Localização obtida: Lat=%.4f, Lon=%.4f\n", g_latitude, g_longitude);
g_location_retrieved = true;
} else {
printf(">>> FALHA! Não foi possível encontrar lat/lon no JSON.\n");
g_location_retrieved = true;
}
}
static err_t http_recv_cb(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) {
HTTP_GET_STATE_T *state = (HTTP_GET_STATE_T*)arg;
if (p == NULL) {
char *body = strstr(state->http_response_buffer, "\r\n\r\n");
if (body) {
parse_location_from_json(body + 4);
} else {
printf(">>> FALHA! Resposta HTTP inválida recebida.\n");
g_location_retrieved = true;
}
return http_close_connection(arg, pcb);
}
if (state->buffer_pos < sizeof(state->http_response_buffer)) {
int len_to_copy = p->tot_len;
if (state->buffer_pos + len_to_copy > sizeof(state->http_response_buffer) -1) {
len_to_copy = sizeof(state->http_response_buffer) - 1 - state->buffer_pos;
}
memcpy(state->http_response_buffer + state->buffer_pos, p->payload, len_to_copy);
state->buffer_pos += len_to_copy;
state->http_response_buffer[state->buffer_pos] = '\0';
}
tcp_recved(pcb, p->tot_len);
pbuf_free(p);
return ERR_OK;
}
static err_t http_connected_cb(void *arg, struct tcp_pcb *pcb, err_t err) {
if (err != ERR_OK) {
printf("Falha na conexão TCP: %d\n", err);
return http_close_connection(arg, pcb);
}
HTTP_GET_STATE_T *state = (HTTP_GET_STATE_T*)arg;
printf("Enviando requisição HTTP para %s...\n", GEO_API_HOST);
tcp_write(pcb, state->http_request, strlen(state->http_request), TCP_WRITE_FLAG_COPY);
tcp_output(pcb);
return ERR_OK;
}
static void http_err_cb(void *arg, err_t err) {
printf("Erro de TCP: %d\n", err);
http_close_connection(arg, NULL);
g_location_retrieved = true;
}
static void geo_dns_found_cb(const char *name, const ip_addr_t *ipaddr, void *callback_arg) {
HTTP_GET_STATE_T *state = (HTTP_GET_STATE_T *)callback_arg;
if (ipaddr) {
state->remote_ip = *ipaddr;
printf("DNS para geolocalização resolvido: %s\n", ip4addr_ntoa(ipaddr));
state->pcb = tcp_new_ip_type(IP_GET_TYPE(ipaddr));
tcp_arg(state->pcb, state);
tcp_recv(state->pcb, http_recv_cb);
tcp_err(state->pcb, http_err_cb);
cyw43_arch_lwip_begin();
tcp_connect(state->pcb, ipaddr, 80, http_connected_cb);
cyw43_arch_lwip_end();
} else {
printf("Falha ao resolver DNS para %s\n", name);
free(state);
g_location_retrieved = true;
}
}
void get_location_from_ip() {
printf("Iniciando consulta de geolocalização por IP...\n");
HTTP_GET_STATE_T *state = calloc(1, sizeof(HTTP_GET_STATE_T));
if (!state) {
printf("Falha ao alocar memória para estado HTTP\n");
g_location_retrieved = true;
return;
}
snprintf(state->http_request, sizeof(state->http_request),
"GET /json HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n",
GEO_API_HOST);
cyw43_arch_lwip_begin();
err_t err = dns_gethostbyname(GEO_API_HOST, &state->remote_ip, geo_dns_found_cb, state);
cyw43_arch_lwip_end();
if (err == ERR_INPROGRESS) {
printf("Requisição DNS para geolocalização em andamento...\n");
} else if (err != ERR_OK) {
printf("Erro imediato na requisição DNS: %d\n", err);
free(state);
g_location_retrieved = true;
}
}
// --- INÍCIO: NOVAS FUNÇÕES DE HARDWARE ---
void init_buzzer() {
gpio_set_function(BUZZER_PIN, GPIO_FUNC_PWM);
uint slice_num = pwm_gpio_to_slice_num(BUZZER_PIN);
// Configura o PWM para uma frequência audível (aprox. 500 Hz)
// Clock do sistema (125MHz) / Divisor (125) / Wrap (2000) = 500 Hz
pwm_config config = pwm_get_default_config();
pwm_config_set_clkdiv(&config, 125.f);
pwm_config_set_wrap(&config, 2000);
pwm_init(slice_num, &config, false); // Inicia desligado
}
void set_buzzer_state(bool active) {
uint slice_num = pwm_gpio_to_slice_num(BUZZER_PIN);
if (active) {
// Liga o buzzer com 50% de duty cycle (som constante)
pwm_set_gpio_level(BUZZER_PIN, 1000); // 1000 de 2000 (wrap) = 50%
pwm_set_enabled(slice_num, true);
} else {
// Desliga o buzzer
pwm_set_enabled(slice_num, false);
}
}
void init_extra_hardware() {
// Sensor de Presença
gpio_init(PRESENCE_SENSOR_PIN);
gpio_set_dir(PRESENCE_SENSOR_PIN, GPIO_IN);
// Relé
gpio_init(RELAY_PIN);
gpio_set_dir(RELAY_PIN, GPIO_OUT);
gpio_put(RELAY_PIN, 0); // Garante que o relé comece desligado
// Buzzer
init_buzzer();
}
// --- FIM: NOVAS FUNÇÕES DE HARDWARE ---
int main() {
stdio_init_all();
sleep_ms(2000);
// Gera um Client ID único
char board_id_full[2 * PICO_UNIQUE_BOARD_ID_SIZE_BYTES + 1];
pico_get_unique_board_id_string(board_id_full, sizeof(board_id_full));
int full_id_len = strlen(board_id_full);
snprintf(client_id_str, sizeof(client_id_str), "pico-w-%s", board_id_full + full_id_len - 6);
printf("Client ID gerado: %s\n", client_id_str);
// Inicialização do I2C e Wi-Fi
i2c_init(i2c0, 100 * 1000);
gpio_set_function(0, GPIO_FUNC_I2C);
gpio_set_function(1, GPIO_FUNC_I2C);
gpio_pull_up(0);
gpio_pull_up(1);
// --- NOVO --- Inicializa os novos componentes (PIR, Buzzer, Relé)
init_extra_hardware();
if (cyw43_arch_init()) return -1;
cyw43_arch_enable_sta_mode();
printf("Conectando ao Wi-Fi: %s...\n", WIFI_SSID);
if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) {
printf("Falha ao conectar ao Wi-Fi.\n");
return -1;
}
printf("Conectado ao Wi-Fi!\n");
// Obtém a localização por IP
get_location_from_ip();
printf("Aguardando obtenção da geolocalização...\n");
while(!g_location_retrieved) {
cyw43_arch_poll();
sleep_ms(100);
}
// Configuração dos clientes MQTT
MQTT_CLIENT_STATE_T *state_tb = calloc(1, sizeof(MQTT_CLIENT_STATE_T));
state_tb->mqtt_client = mqtt_client_new();
state_tb->name = "ThingsBoard";
MQTT_CLIENT_STATE_T *state_isc = calloc(1, sizeof(MQTT_CLIENT_STATE_T));
state_isc->mqtt_client = mqtt_client_new();
state_isc->name = "Interscity";
cyw43_arch_lwip_begin();
dns_gethostbyname(MQTT_HOST_THINGSBOARD, &state_tb->remote_ip, mqtt_dns_found_cb, state_tb);
dns_gethostbyname(MQTT_HOST_INTERSCITY, &state_isc->remote_ip, mqtt_dns_found_cb, state_isc);
cyw43_arch_lwip_end();
// Loop principal
float temp, hum;
while (true) {
// --- INÍCIO: ALTERAÇÕES NO LOOP PRINCIPAL ---
// 1. Leitura e Ação do Sensor de Presença
bool presence_detected = gpio_get(PRESENCE_SENSOR_PIN);
set_buzzer_state(presence_detected); // Buzzer reflete o estado atual
// Verifica se o estado mudou de 'sem presença' para 'com presença'
if (presence_detected && !g_last_presence_state) {
printf("\n!!! ALERTA DE PRESENÇA DETECTADA !!!\n");
// Envia um alerta para o ThingsBoard apenas na transição
if (mqtt_client_is_connected(state_tb->mqtt_client)) {
char alert_payload[50];
snprintf(alert_payload, sizeof(alert_payload), "{\"presence_alert\":true}");
printf("Publicando ALERTA para [%s]: %s\n", state_tb->name, alert_payload);
mqtt_publish(state_tb->mqtt_client, MQTT_TOPIC_THINGSBOARD, alert_payload, strlen(alert_payload), 1, 0, NULL, NULL);
}
}
g_last_presence_state = presence_detected; // Atualiza o último estado
// 2. Leitura do Sensor de Temp/Umid e Ação do Relé
if (read_aht10(&temp, &hum)) {
printf("\nSensor Lido: Temp=%.2f C, Umid=%.2f %%\n", temp, hum);
// Controle do relé baseado na temperatura
if (temp >= 30.0) {
printf("Temperatura >= 30C. ATIVANDO RELÉ.\n");
gpio_put(RELAY_PIN, 1);
} else {
//printf("Temperatura < 30C. Desativando relé.\n"); // Opcional: para não poluir o log
gpio_put(RELAY_PIN, 0);
}
// Publica dados de telemetria para o ThingsBoard (com latitude e longitude)
if (mqtt_client_is_connected(state_tb->mqtt_client)) {
char payload[200];
snprintf(payload, sizeof(payload),
"{\"temperature\":%.2f, \"humidity\":%.2f, \"latitude\":%.6f, \"longitude\":%.6f}",
temp, hum, g_latitude, g_longitude);
printf("Publicando para [%s]: %s\n", state_tb->name, payload);
mqtt_publish(state_tb->mqtt_client, MQTT_TOPIC_THINGSBOARD, payload, strlen(payload), 1, 0, NULL, NULL);
}
// Publica para o Interscity (UFMA) (com latitude e longitude)
if (mqtt_client_is_connected(state_isc->mqtt_client)) {
char topic_isc[256];
char payload_isc[50];
// Publica temperatura
snprintf(topic_isc, sizeof(topic_isc), "actuator/%s/capability/temperature", INTERSCITY_RESOURCE_UUID);
snprintf(payload_isc, sizeof(payload_isc), "%.2f", temp);
printf("Publicando para [%s]: Tópico=%s, Payload=%s\n", state_isc->name, topic_isc, payload_isc);
mqtt_publish(state_isc->mqtt_client, topic_isc, payload_isc, strlen(payload_isc), 1, 0, NULL, NULL);
// Publica umidade
snprintf(topic_isc, sizeof(topic_isc), "actuator/%s/capability/humidity", INTERSCITY_RESOURCE_UUID);
snprintf(payload_isc, sizeof(payload_isc), "%.2f", hum);
printf("Publicando para [%s]: Tópico=%s, Payload=%s\n", state_isc->name, topic_isc, payload_isc);
mqtt_publish(state_isc->mqtt_client, topic_isc, payload_isc, strlen(payload_isc), 1, 0, NULL, NULL);
// Publica latitude
snprintf(topic_isc, sizeof(topic_isc), "actuator/%s/capability/latitude", INTERSCITY_RESOURCE_UUID);
snprintf(payload_isc, sizeof(payload_isc), "%.6f", g_latitude);
printf("Publicando para [%s]: Tópico=%s, Payload=%s\n", state_isc->name, topic_isc, payload_isc);
mqtt_publish(state_isc->mqtt_client, topic_isc, payload_isc, strlen(payload_isc), 1, 0, NULL, NULL);
// Publica longitude
snprintf(topic_isc, sizeof(topic_isc), "actuator/%s/capability/longitude", INTERSCITY_RESOURCE_UUID);
snprintf(payload_isc, sizeof(payload_isc), "%.6f", g_longitude);
printf("Publicando para [%s]: Tópico=%s, Payload=%s\n", state_isc->name, topic_isc, payload_isc);
mqtt_publish(state_isc->mqtt_client, topic_isc, payload_isc, strlen(payload_isc), 1, 0, NULL, NULL);
}
} else {
printf("Falha ao ler dados do sensor AHT10.\n");
}
// --- FIM: ALTERAÇÕES NO LOOP PRINCIPAL ---
cyw43_arch_poll();
sleep_ms(10000); // Intervalo de 10 segundos entre publicações
}
}Loading
pi-pico-w
pi-pico-w