/* PLACA DO JARVIS — NodeMCU / ESP8266 (11/11/2026) O Jarvis do celular fala com esta placa pela rede de casa: GET /jarvis -> quem é a placa, os canais e os sensores (JSON) GET /comando?token=...&canal=luz&acao=ligar (ligar | desligar | alternar) COMO USAR 1. Na IDE do Arduino: Arquivo > Preferências > URLs adicionais: http://arduino.esp8266.com/stable/package_esp8266com_index.json Ferramentas > Placa > Gerenciador de placas: instale "esp8266". Placa: "NodeMCU 1.0 (ESP-12E Module)". 2. Mude o bloco "MUDE AQUI" (rede, nome, token e os canais) e grave. 3. Abra o Monitor Serial (115200): ele mostra o IP da placa. 4. No app Jarvis: "Procurar placas na rede" (ou digite o IP), escreva o MESMO token e toque em "Cadastrar a placa". Depois é só falar: "Jarvis, liga a luz da sala". O TOKEN é a senha da placa: sem ele, qualquer aparelho da rede (ou um site aberto no celular) ligaria as coisas da casa. Invente um comprido. Relé: a maioria dos módulos de relé liga com o pino em LOW — por isso "invertido = true". Se o seu ligar ao contrário, troque para false. */ #include #include #include // ================== MUDE AQUI ================== const char* WIFI_NOME = "NOME_DO_WIFI"; const char* WIFI_SENHA = "SENHA_DO_WIFI"; const char* NOME_DA_PLACA = "Sala"; const char* TOKEN = "troque-por-uma-senha-comprida"; struct Canal { const char* id; const char* nome; uint8_t pino; bool invertido; bool estado; }; Canal CANAIS[] = { // id (sem espaço) nome falado pino invertido começa { "luz", "Luz da sala", D1, true, false }, { "ventilador", "Ventilador", D2, true, false }, }; // Sensor no A0 (opcional): luz ambiente com LDR, umidade da terra etc. const bool USAR_A0 = true; const char* NOME_A0 = "Luz ambiente"; const char* UNIDADE_A0 = "%"; // =============================================== const int N_CANAIS = sizeof(CANAIS) / sizeof(CANAIS[0]); ESP8266WebServer servidor(80); void aplicar(Canal& c) { digitalWrite(c.pino, (c.estado ^ c.invertido) ? HIGH : LOW); } // Comparação que não entrega o token pelo tempo da resposta. bool tokenConfere(const String& veio) { const size_t n = strlen(TOKEN); if (veio.length() != n) return false; uint8_t dif = 0; for (size_t i = 0; i < n; i++) dif |= (uint8_t)(veio[i] ^ TOKEN[i]); return dif == 0; } String escapar(const char* s) { String r; for (const char* p = s; *p; p++) { if (*p == '"' || *p == '\\') r += '\\'; r += *p; } return r; } String estadoEmJson() { String j = "{\"jarvis\":1,\"nome\":\"" + escapar(NOME_DA_PLACA) + "\",\"canais\":["; for (int i = 0; i < N_CANAIS; i++) { if (i) j += ","; j += "{\"id\":\"" + escapar(CANAIS[i].id) + "\",\"nome\":\"" + escapar(CANAIS[i].nome) + "\",\"estado\":" + (CANAIS[i].estado ? "true" : "false") + "}"; } j += "],\"sensores\":["; if (USAR_A0) { int valor = map(analogRead(A0), 0, 1023, 0, 100); j += "{\"id\":\"a0\",\"nome\":\"" + escapar(NOME_A0) + "\",\"valor\":" + String(valor) + ",\"unidade\":\"" + escapar(UNIDADE_A0) + "\"}"; } j += "]}"; return j; } void responder(int codigo, const String& corpo) { servidor.send(codigo, "application/json", corpo); } void rotaJarvis() { responder(200, estadoEmJson()); } void rotaComando() { if (!tokenConfere(servidor.arg("token"))) { responder(403, "{\"erro\":\"token\"}"); return; } String canal = servidor.arg("canal"), acao = servidor.arg("acao"); for (int i = 0; i < N_CANAIS; i++) { if (canal != CANAIS[i].id) continue; if (acao == "ligar") CANAIS[i].estado = true; else if (acao == "desligar") CANAIS[i].estado = false; else if (acao == "alternar") CANAIS[i].estado = !CANAIS[i].estado; else { responder(400, "{\"erro\":\"acao\"}"); return; } aplicar(CANAIS[i]); Serial.printf("%s -> %s\n", CANAIS[i].id, CANAIS[i].estado ? "ligado" : "desligado"); responder(200, estadoEmJson()); return; } responder(404, "{\"erro\":\"canal\"}"); } void conectar() { WiFi.mode(WIFI_STA); WiFi.begin(WIFI_NOME, WIFI_SENHA); Serial.print("Conectando no Wi-Fi"); for (int i = 0; i < 60 && WiFi.status() != WL_CONNECTED; i++) { delay(500); Serial.print("."); } Serial.println(); if (WiFi.status() == WL_CONNECTED) { Serial.print("Placa do Jarvis no ar. IP: "); Serial.println(WiFi.localIP()); } else { Serial.println("Nao conectou: confira WIFI_NOME e WIFI_SENHA."); } } void setup() { Serial.begin(115200); for (int i = 0; i < N_CANAIS; i++) { pinMode(CANAIS[i].pino, OUTPUT); aplicar(CANAIS[i]); } conectar(); String nomeMdns = String("jarvis-") + NOME_DA_PLACA; nomeMdns.toLowerCase(); nomeMdns.replace(" ", "-"); MDNS.begin(nomeMdns.c_str()); servidor.on("/jarvis", HTTP_GET, rotaJarvis); servidor.on("/comando", HTTP_GET, rotaComando); servidor.onNotFound([]() { responder(404, "{\"erro\":\"rota\"}"); }); servidor.begin(); } unsigned long ultimaConferida = 0; void loop() { servidor.handleClient(); MDNS.update(); // Caiu o Wi-Fi? Tenta de novo a cada 15 s, sem travar a placa. if (WiFi.status() != WL_CONNECTED && millis() - ultimaConferida > 15000) { ultimaConferida = millis(); WiFi.reconnect(); } }