import network
import time
import dht
import ujson
from machine import Pin, SoftI2C
import ssd1306
# --- INSTALAÇÃO AUTOMÁTICA MQTT ---
try:
from umqtt.simple import MQTTClient
except ImportError:
import mip
mip.install("umqtt.simple")
from umqtt.simple import MQTTClient
# --- CONFIGURAÇÕES ---
SSID = "Wokwi-GUEST"
SENHA = ""
TB_SERVER = "thingsboard.cloud"
TB_TOKEN = "f78ihwXRvbQ2tkYIFp9F"
# Tópicos
TOPICO_TELEMETRIA = b"v1/devices/me/telemetry"
TOPICO_RPC_SUB = b"v1/devices/me/rpc/request/+"
# --- VARIÁVEIS GLOBAIS (O ESTADO DO SISTEMA) ---
target_temp = 24.0 # Meta inicial: 24 graus
temp_atual = 0.0
ultimo_envio_sensor = 0
# --- HARDWARE ---
sensor = dht.DHT22(Pin(15))
btn_up = Pin(12, Pin.IN, Pin.PULL_UP) # Botão +
btn_down = Pin(14, Pin.IN, Pin.PULL_UP) # Botão -
led_azul = Pin(2, Pin.OUT) # Resfriamento
led_vermelho = Pin(4, Pin.OUT) # Aquecimento
# Display OLED
i2c = SoftI2C(scl=Pin(22), sda=Pin(21))
oled = ssd1306.SSD1306_I2C(128, 64, i2c)
# --- FUNÇÃO 1: ATUALIZAR OLED ---
def atualizar_display(status):
oled.fill(0)
oled.text(f"Sala: {temp_atual:.1f} C", 0, 0)
oled.text(f"Meta: {target_temp:.1f} C", 0, 16) # Mostra a meta atualizada
oled.text("-" * 16, 0, 26)
oled.text(f"Status:", 0, 36)
oled.text(status, 0, 48)
oled.show()
# --- FUNÇÃO 2: SINCRONIZAR COM A NUVEM ---
# Esta função é chamada sempre que a meta muda (seja por botão ou site)
# Ela avisa o widget para ele atualizar o número na tela
def enviar_sincronia():
if mqtt_client:
try:
# Enviamos a meta como telemetria. O Widget 'Value Stepper' vai ler isso.
msg = ujson.dumps({"target_temp": target_temp})
mqtt_client.publish(TOPICO_TELEMETRIA, msg)
print(f"Sincronia enviada: Meta agora é {target_temp}")
except:
print("Erro ao sincronizar nuvem")
# --- INTERRUPÇÕES (BOTÕES FÍSICOS) ---
# Usamos debounce para evitar cliques duplos
ultimo_click = 0
def tratar_botoes(pino):
global target_temp, ultimo_click
agora = time.ticks_ms()
if time.ticks_diff(agora, ultimo_click) > 200: # 200ms de espera
if pino == btn_up:
target_temp += 1.0
elif pino == btn_down:
target_temp -= 1.0
ultimo_click = agora
# O PULO DO GATO:
# Mudou no físico? Avisa a nuvem imediatamente!
enviar_sincronia()
# Configura os gatilhos dos botões
btn_up.irq(trigger=Pin.IRQ_FALLING, handler=tratar_botoes)
btn_down.irq(trigger=Pin.IRQ_FALLING, handler=tratar_botoes)
# --- CALLBACK (QUANDO VEM DA NUVEM) ---
def recebeu_comando(topico, msg):
global target_temp
print(f"Comando Remoto: {msg}")
try:
dados = ujson.loads(msg)
if dados["method"] == "setTarget":
# Atualiza a variável local com o valor que veio do site
target_temp = float(dados["params"])
# Envia de volta só para garantir que todos fiquem iguais
enviar_sincronia()
except:
print("Erro ao processar comando")
# --- CONEXÃO ---
def conectar():
wlan = network.WLAN(network.STA_IF); wlan.active(True)
wlan.connect(SSID, SENHA)
while not wlan.isconnected(): time.sleep(0.1)
client = MQTTClient("ESP_Final", TB_SERVER, user=TB_TOKEN, password="")
client.set_callback(recebeu_comando)
client.connect()
client.subscribe(TOPICO_RPC_SUB)
return client
# --- LOOP PRINCIPAL ---
oled.text("Iniciando...", 0, 0); oled.show()
mqtt_client = conectar()
print("--- SISTEMA HÍBRIDO ONLINE ---")
enviar_sincronia() # Alinha o site com o valor inicial (24.0)
while True:
# 1. Checa mensagens da nuvem
mqtt_client.check_msg()
# 2. Leitura do Sensor (A cada 2 seg)
agora = time.time()
if agora - ultimo_envio_sensor > 2:
try:
sensor.measure()
temp_atual = sensor.temperature()
# Envia leitura do sensor para o gráfico
msg = ujson.dumps({"temperatura": temp_atual})
mqtt_client.publish(TOPICO_TELEMETRIA, msg)
ultimo_envio_sensor = agora
except:
pass
# 3. CÉREBRO DO TERMOSTATO (HISTERESE)
# Define uma margem de 0.5 grau para não ficar ligando/desligando freneticamente
margem = 0.5
status_sistema = "ESTAVEL"
if temp_atual > (target_temp + margem):
# Está mais quente que a meta -> Liga Resfriamento
led_azul.on()
led_vermelho.off()
status_sistema = "RESFRIANDO"
elif temp_atual < (target_temp - margem):
# Está mais frio que a meta -> Liga Aquecedor
led_azul.off()
led_vermelho.on()
status_sistema = "AQUECENDO"
else:
# Está dentro da zona de conforto
led_azul.off()
led_vermelho.off()
status_sistema = "CONFORTAVEL"
# 4. Atualiza Interface Local
atualizar_display(status_sistema)
time.sleep(0.1) # Pequena pausa para estabilidade
Loading
ssd1306
ssd1306