import network
import time
import machine
import dht
import ujson
import urequests
from machine import Pin, ADC, I2C
from umqtt.simple import MQTTClient
print("=== ESTACAO METEOROLOGICA INICIANDO ===")
# ─────────────────────────────────────────────────────────────
# WIFI
# ─────────────────────────────────────────────────────────────
WIFI_SSID = "Wokwi-GUEST"
WIFI_PASS = ""
# ─────────────────────────────────────────────────────────────
# MQTT - HiveMQ Cloud (CONFIGURAÇÃO PRIVADA)
# ─────────────────────────────────────────────────────────────
MQTT_BROKER = "514d3ac7989b4b83a9e61615eff10474.s1.eu.hivemq.cloud"
MQTT_PORT = 8883
MQTT_USER = "Gustavo"
MQTT_PASS = "Guga@1908"
MQTT_CLIENT_ID = b"esp32_estacao_gustavo"
MQTT_TOPIC = b"gustavo/estacao_meteo/dados"
# Google Sheets (URL fornecida)
SHEETS_URL = "https://script.google.com/macros/s/AKfycbzkj64zNi5XXY4v1LjSZ_4w8zgCQV-Mv63VMQQxSJJ6iFbeT_0bhV9gMnk7jWcCWf6u0g/exec"
# SMTP Gmail (Relatório Diário)
SMTP_SERVER = "smtp.gmail.com"
SMTP_PORT = 465
EMAIL_USER = "[email protected]" # Substitua pelo seu Gmail
EMAIL_PASS = "ojqs sojw tasj hzdo" # Senha de App de 16 dígitos do Google
DESTINATARIO = "[email protected]"
# OpenWeatherMap
OWM_API_KEY = "57d0fc720ecedf210e95aafe5af25188"
OWM_URL = "http://api.openweathermap.org/data/2.5/weather?q=Sao%20Paulo,BR&appid={}&units=metric".format(OWM_API_KEY)
# ─────────────────────────────────────────────────────────────
# OPEN WEATHER MAP
# ─────────────────────────────────────────────────────────────
OWM_API_KEY = "57d0fc720ecedf210e95aafe5af25188"
OWM_CIDADE = "Sao Paulo,BR"
OWM_URL = (
"http://api.openweathermap.org/data/2.5/weather?q={}"
"&appid={}&units=metric"
).format(OWM_CIDADE.replace(" ", "%20"), OWM_API_KEY)
# ─────────────────────────────────────────────────────────────
# I2C / OLED
# ─────────────────────────────────────────────────────────────
i2c = I2C(0, scl=Pin(22), sda=Pin(21), freq=400000)
print("Escaneando I2C...")
devs = i2c.scan()
print("I2C:", [hex(d) for d in devs])
oled = None
oled_ok = False
if devs:
try:
from ssd1306 import SSD1306_I2C
oled = SSD1306_I2C(128, 64, i2c, addr=devs[0])
oled_ok = True
print("OLED OK")
except Exception as e:
print("OLED ERRO:", e)
else:
print("OLED nao encontrado - apenas terminal")
# ─────────────────────────────────────────────────────────────
# SENSORES
# ─────────────────────────────────────────────────────────────
sensor_dht = dht.DHT22(Pin(15))
sensor_ldr = ADC(Pin(34))
sensor_ldr.atten(ADC.ATTN_11DB)
sensor_gas = ADC(Pin(35))
sensor_gas.atten(ADC.ATTN_11DB)
botao = Pin(13, Pin.IN, Pin.PULL_UP)
led = Pin(26, Pin.OUT)
led.value(0)
# ─────────────────────────────────────────────────────────────
# VARIAVEIS
# ─────────────────────────────────────────────────────────────
tela_atual = 0
TOTAL_TELAS = 4
ultimo_clique = 0
ultimo_mqtt = 0
ultimo_dht = 0
ultimo_owm = 0
ultimo_pisca = 0
pisca_estado = 0
client = None
wifi_ok = False
mqtt_ok = False
temp = 25.0
umid = 60.0
luz = 0
gas = 0
hi = 25.0
conforto = "Regular"
pressao = 0
previsao = "Aguardando"
# ─────────────────────────────────────────────────────────────
# OLED
# ─────────────────────────────────────────────────────────────
def oled_msg(l1, l2="", l3=""):
if not oled_ok: return
oled.fill(0)
oled.text(l1[:16], 0, 10)
if l2: oled.text(l2[:16], 0, 26)
if l3: oled.text(l3[:16], 0, 42)
oled.show()
def exibir_oled(alerta):
if not oled_ok: return
oled.fill(0)
if alerta:
oled.fill_rect(0, 0, 128, 12, 1)
oled.text(alerta[:16], 0, 2, 0)
else:
oled.text("ESTACAO CLIMA", 10, 2)
oled.hline(0, 14, 128, 1)
for i in range(TOTAL_TELAS):
x = 44 + i * 10
if i == tela_atual: oled.fill_rect(x, 58, 7, 5, 1)
else: oled.rect(x, 58, 7, 5, 1)
if tela_atual == 0:
oled.text("Temp:{:.1f}C".format(temp), 0, 18)
oled.text("Umid:{:.1f}%".format(umid), 0, 30)
oled.text("HIdx:{:.1f}C".format(hi), 0, 42)
oled.text(conforto, 0, 54)
elif tela_atual == 1:
pct_luz = int(luz / 4095 * 100)
oled.text("Luz (LDR):", 0, 18)
oled.text("Valor: {}".format(luz), 0, 30)
oled.text("{:.1f}% Solar".format(pct_luz), 0, 42)
elif tela_atual == 2:
oled.text("Qualidade Ar:", 0, 18)
oled.text("MQ2: {}".format(gas), 0, 30)
oled.text("ALERTA!" if gas > 1000 else "Ar: Limpo", 0, 42)
elif tela_atual == 3:
oled.text("Previsao:", 0, 18)
oled.text(previsao[:16], 0, 30)
oled.text("{}hPa".format(pressao), 0, 42)
oled.show()
# ─────────────────────────────────────────────────────────────
# WIFI
# ─────────────────────────────────────────────────────────────
def conectar_wifi():
global wifi_ok
print("Conectando WiFi...")
oled_msg("Conectando", "WiFi...")
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(WIFI_SSID, WIFI_PASS)
for _ in range(20):
if wlan.isconnected():
wifi_ok = True
ip = wlan.ifconfig()[0]
print("WiFi OK:", ip)
oled_msg("WiFi OK", ip)
time.sleep(1)
return
time.sleep(0.5)
print("WiFi ERRO")
oled_msg("WiFi ERRO")
time.sleep(1)
# ─────────────────────────────────────────────────────────────
# MQTT (CORREÇÃO PARA BROKER PRIVADO)
# ─────────────────────────────────────────────────────────────
def conectar_mqtt():
global client, mqtt_ok
try:
print("Conectando ao Broker PRIVADO...")
# Se usar HiveMQ Cloud (Porta 8883), mantenha ssl=True
# Se usar Mosquitto Local (Porta 1883), mude para ssl=False
client = MQTTClient(
MQTT_CLIENT_ID,
MQTT_BROKER,
port=MQTT_PORT,
user=MQTT_USER,
password=MQTT_PASS,
ssl=(True if MQTT_PORT == 8883 else False),
keepalive=60
)
client.connect()
mqtt_ok = True
print("MQTT OK - Conexão Privada Estabelecida")
oled_msg("MQTT OK!", "Privado")
time.sleep(1)
except Exception as e:
mqtt_ok = False
print("ERRO CRITICO: Nao foi possivel conectar ao broker privado:", e)
oled_msg("MQTT ERRO", "Privado Off")
# Removido o fallback para broker.hivemq.com para garantir privacidade
# ─────────────────────────────────────────────────────────────
# CALCULOS
# ─────────────────────────────────────────────────────────────
def calcular_conforto(t, h):
if t < 20: return "Frio"
elif 20 <= t <= 27 and 40 <= h <= 60: return "Ideal"
elif t > 35: return "Perigo"
elif t > 30: return "Quente"
return "Regular"
def calcular_hi(t, h):
if t < 27: return round(t, 1)
v = (-8.784695
+ 1.61139411 * t + 2.3385549 * h
- 0.14611605 * t * h
- 0.012308094 * t * t
- 0.016424828 * h * h
+ 0.002211732 * t * t * h
+ 0.00072546 * t * h * h
- 0.000003582 * t * t * h * h)
return round(v, 1)
def calcular_previsao(p, t):
if p == 0: return "Sem dados"
if p >= 1022: return "Ensolarado" if t > 25 else "Sol"
elif p >= 1013: return "Parc. Nublado"
elif p >= 1003: return "Nublado"
elif p >= 993: return "Chuva"
return "Tempestade"
# ─────────────────────────────────────────────────────────────
# OPEN WEATHER MAP
# ─────────────────────────────────────────────────────────────
def consultar_owm():
global pressao, previsao
try:
print("[OWM] Consultando...")
r = urequests.get(OWM_URL, timeout=10)
if r.status_code == 200:
d = r.json()
pressao = d["main"]["pressure"]
previsao = calcular_previsao(pressao, temp)
print("[OWM] P={}hPa -> {}".format(pressao, previsao))
else:
previsao = "Erro HTTP"
r.close()
except Exception as e:
print("[OWM] ERRO:", e)
previsao = "Sem dados"
# ─────────────────────────────────────────────────────────────
# LED
# ─────────────────────────────────────────────────────────────
def controlar_led(alerta, agora):
global ultimo_pisca, pisca_estado
if alerta:
if time.ticks_diff(agora, ultimo_pisca) > 300:
pisca_estado = 1 - pisca_estado
led.value(pisca_estado)
ultimo_pisca = agora
else:
led.value(0)
pisca_estado = 0
# ─────────────────────────────────────────────────────────────
# INICIO
# ─────────────────────────────────────────────────────────────
oled_msg("ESTACAO", "INICIANDO")
time.sleep(2)
conectar_wifi()
if wifi_ok:
conectar_mqtt()
consultar_owm()
ultimo_dht = time.ticks_ms()
ultimo_owm = time.ticks_ms()
print("=== LOOP INICIADO ===")
# ─────────────────────────────────────────────────────────────
# LOOP
# ─────────────────────────────────────────────────────────────
while True:
agora = time.ticks_ms()
# Botao
if not botao.value():
if time.ticks_diff(agora, ultimo_clique) > 300:
tela_atual = (tela_atual + 1) % TOTAL_TELAS
ultimo_clique = agora
print("Tela:", tela_atual)
# DHT22 a cada 3s
if time.ticks_diff(agora, ultimo_dht) >= 3000:
try:
sensor_dht.measure()
temp = sensor_dht.temperature()
umid = sensor_dht.humidity()
hi = calcular_hi(temp, umid)
conforto = calcular_conforto(temp, umid)
print("DHT OK: T={:.1f}C H={:.1f}%".format(temp, umid))
except Exception as e:
print("DHT ERRO:", e)
ultimo_dht = agora
# OWM a cada 10 min
if wifi_ok and time.ticks_diff(agora, ultimo_owm) >= 600000:
consultar_owm()
ultimo_owm = agora
# Sensores
luz = sensor_ldr.read()
gas = sensor_gas.read()
# Alerta
alerta = ""
if temp > 35: alerta = "TEMP ALTA"
if gas > 2500: alerta = "GAS!"
controlar_led(alerta, agora)
exibir_oled(alerta)
# MQTT a cada 5s
if mqtt_ok and time.ticks_diff(agora, ultimo_mqtt) > 5000:
try:
payload = ujson.dumps({
"temp" : round(temp, 1),
"umid" : round(umid, 1),
"heat_idx": hi,
"conforto": conforto,
"luz_pct" : int(luz / 4095 * 100),
"gas" : gas,
"ar" : "Limpo" if gas <= 1000 else "Ruim",
"alerta" : alerta if alerta else "normal",
"pressao" : pressao,
"previsao": previsao
})
client.publish(MQTT_TOPIC, payload)
print("MQTT pub Privado:", payload)
ultimo_mqtt = agora
except Exception as e:
print("MQTT ERRO reconectando...", e)
mqtt_ok = False
conectar_mqtt()
time.sleep(0.5)