
Sistema de Alarme Residencial Baseado no Protocolo SMTP
O objetivo deste artigo é apresentar uma solução utilizando o Nodemcu (IoT) com o intuito de simular um sistema de alarme residencial, baseado no protocolo SMTP, onde, ao captar uma mudança de luz no ambiente, é disparado um e-mail para o proprietário.
A respeito do dispositivo utilizado, o Nodemcu é uma placa de desenvolvimento que combina o chip ESP8266 com uma conexão USB-serial em uma tensão de 3.3V.

O projeto consiste em:
• Implementação da leitura do sensor de luz;
• Implementação do envio de e-mail;
• Implementação de WebServer integrado ao Nodemcu para definir os valores do e-mail e do valor de intensidade de luz (valor base para envio de e-mail para o e-mail cadastrado).

É possível observar, na figura anterior como foi criado o protótipo do Nodemcu com o sensor de luz.

Escolha da Linguagem
Para a implementação deste projeto, foi utilizado a linguagem C, utilizando a IDE do Arduino. Apesar das diversas opções de linguagens para IoT, como C, Python e Lua, a equipe do trabalho optou pela linguagem C, por conta do domínio com a mesma no uso com Arduino. Além disso, apesar da linguagem não ser de tão fácil aprendizado quanto as outras, no entanto, a mesma é simples e apresenta os erros de forma clara, não tendo demais abstrações.
Explicação do Código Fonte e Libs Utilizadas
Para a implementação do envio de e-mail, foi utilizado o SMTP2GO o qual permite criar um servidor de e-mail de forma gratuita e efetuar o redirecionamento de e-mails.
Para a implementação do programa no NodeMCU, utilizando a linguagem, foi necessário a utilização das libs: SPI, ESP8266WiFi e FS, sendo que o código ficou da seguinte forma:
#include <SPI.h> #include <ESP8266WiFi.h> #include "FS.h"
• SPI: O SPI é uma interface de conexão serial.
• FS: O FS controla o FileSystem, ou seja, para fazer as operações de escrita e leitura no NodeMCU.
• ESP8266WiFi: É o módulo do ESP8266 para qualquer coisa que envolva conexão WiFi. É utilizado para rodar o Web Server.
String readString = ""; WiFiClient client; WiFiClient clientEmail; WiFiServer server(8080); const char* ssid = "Informe o seu SSID aqui"; const char* password = "Informa a senha da sua rede aqui"; const char* host = "Insira o seu host aqui"; String sEmailFS; int iValorLimiteFS; unsigned long ulEsperaEnvioEmail = 0; void setup() { Serial.begin(115200); delay(10); Serial.println(); Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); server.begin(); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); SPIFFS.begin(); createFile(); String sRead = readFile(); Serial.println(sRead.length()); int iIndex = sRead.indexOf("|"); if (iIndex > 0) { iValorLimiteFS = sRead.substring(0, iIndex).toInt(); sEmailFS = sRead.substring(iIndex + 1); //sEmailFS = sEmailFS.substring(0, sEmailFS.length() - 1); } }
O trecho de código anterior, efetua o setup do programa, bem como, inicializa as variáveis.
Para o correto funcionamento, antes de implementar a função de loop deve-se implementar as funções de escrita a leitura do arquivo.
void createFile() { File wFile; //Cria o arquivo se ele não existir if (!SPIFFS.exists("/config.txt")) { Serial.println("Criando o arquivo..."); wFile = SPIFFS.open("/config.txt", "w+"); if (!wFile) Serial.println("Erro ao criar arquivo!"); else Serial.println("Arquivo criado com sucesso!"); } wFile.close(); } void writeFile(String msg) { SPIFFS.remove("/config.txt"); File rFile = SPIFFS.open("/config.txt", "a+"); if (!rFile) Serial.println("Erro ao abrir arquivo!"); else rFile.println(msg); rFile.close(); } String readFile() { String buf; //Faz a leitura do arquivo File rFile = SPIFFS.open("/config.txt", "r"); while (rFile.available()) { buf += rFile.readStringUntil('\n'); } buf.trim(); rFile.close(); Serial.println(buf); return buf; }
A função de loop, o qual é chamada sempre pelo programa, deve ser implementada da seguinte forma:
void loop() { delay(10); int iValorLido = analogRead(0); if (iValorLido > iValorLimiteFS) { if (sEmailFS.length() > 0 && (millis() - ulEsperaEnvioEmail) > 10000) sendEmail(iValorLido); } client = server.available(); if (client) { Serial.println("new client"); // an http request ends with a blank line boolean currentLineIsBlank = true; while (client.connected()) { if (client.available()) { char c = client.read(); Serial.write(c); readString += c; // if you've gotten to the end of the line (received a newline // character) and the line is blank, the http request has ended, // so you can send a reply if (c == '\n' && currentLineIsBlank) { // send a standard http response header client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println("Connection: close"); // the connection will be closed after completion of the response //client.println("Refresh: "); // refresh the page automatically every 5 sec client.println(); client.println("<!DOCTYPE html>"); client.println("<html>"); client.println("<head>"); client.println(" <meta charset=\"utf-8\" />"); client.println(" <title>Trabalho Final</title>"); client.println(" <link rel='icon' href='data:,'>"); client.println("</head>"); client.println("<body>"); client.println("<div>"); client.println("<form>"); client.println(" <label>Valor Limite</label>"); client.println(" <input type=\"text\" id=\"valor_limite\" name=\"valor_limite\" />"); client.println(" <br />"); client.println(" <label>E-mail</label>"); client.println(" <input type=\"text\" id=\"email\" name=\"email\" />"); client.println(" <br />"); client.println(" <button onclick=\"window.location=this.href+'?valor_limite='+document.getElementById('valor_limite').value+'&email='+document.getElementById('email').value\">Enviar</button>"); client.println("</form>"); client.println(" </div>"); if (iValorLimiteFS && sEmailFS.length()) { client.println(" <div>"); client.println(" <p>E-mail cadastrado:" + sEmailFS + "</p><br />"); client.println(" <p>Valor limite cadastrado:" + String(iValorLimiteFS) + "</p>"); client.println(" </div>"); } client.println(" </body>"); client.println("</html>"); break; } if (c == '\n') currentLineIsBlank = true; else if (c != '\r') currentLineIsBlank = false; } } delay(1); client.stop(); Serial.println(readString); String sValorLimite; int iIndexValorLimite = readString.indexOf("valor_limite"); if (iIndexValorLimite >= 0) { iIndexValorLimite += 13; sValorLimite = readString.substring(iIndexValorLimite, readString.indexOf("&")); } String sEmail; int iIndexEmail = readString.indexOf("email"); if (iIndexEmail >= 0) { iIndexEmail += 6; sEmail = readString.substring(iIndexEmail, readString.indexOf(" HTTP")); sEmail.replace("%40", "@"); } if (sValorLimite.length() > 0 && sEmail.length() > 0) { String sValue = sValorLimite + "|" + sEmail; writeFile(sValue); sEmailFS = sEmail; sEmailFS.trim(); iValorLimiteFS = sValorLimite.toInt(); } readString = ""; } }
Por fim, devemos criar as funções de envio de envio de e-mail, assim como uma função auxiliar para saber se o envio está sendo feito de forma correta.
void sendEmail(int iValorLido) { ulEsperaEnvioEmail = millis(); if (clientEmail.connect(host, 2525)) { String sRCPT = "RCPT To: <"; sRCPT += sEmailFS; sRCPT += ">"; Serial.println(sRCPT); sRCPT.replace("=", ""); Serial.println("connected"); if (!received()) return; // Make a HTTP request: clientEmail.println("EHLO INFORME SEU IP AQUI"); if (!received()) return; clientEmail.println("AUTH LOGIN"); if (!received()) return; clientEmail.println("bGVvbmFyZG9fZmllZGxlckBob3RtYWlsLmNvbQ=="); if (!received()) return; clientEmail.println("MjlkVEdZR20xT3Yz"); if (!received()) return; clientEmail.println("MAIL From: <INFORME O DE AQUI>"); if (!received()) return; //clientEmail.println("RCPT To: <INFORME O PARA AQUI>"); clientEmail.println(sRCPT); if (!received()) return; clientEmail.println("DATA"); if (!received()) return; clientEmail.println("To: You <INFORME O PARA AQUI>"); clientEmail.println("From: Me <INFORME O DE AQUI>"); clientEmail.println("Subject: Atencao alarme disparado\r\n"); String sValorLido = "Valor: " + String(iValorLido); clientEmail.println("Reconhecemos uma variacao no sensor de luminosidade"); clientEmail.println(sValorLido); clientEmail.println("."); if (!received()) return; clientEmail.println("QUIT"); if (!received()) return; clientEmail.stop(); } else { // kf you didn't get a connection to the server: Serial.println("connection failed"); } } boolean received() { byte respCode; int thisByte; int loopCount = 0; while (clientEmail.available() == 0) { delay(1); loopCount++; // if nothing received for 10 seconds, timeout if (loopCount > 10000) { clientEmail.stop(); Serial.println(F("\r\nTimeout")); return false; } } String result = "0"; while (clientEmail.available() > 0) { thisByte = clientEmail.read(); Serial.write(thisByte); result += (char) thisByte; } if (result.indexOf("250") > -1 || result.indexOf("220") > -1 || result.indexOf("354") > -1 || result.indexOf("334") > -1 || result.indexOf("235") > -1 || result.indexOf("221") > -1) { return true; } else { Serial.println("Codigo de resultado invalido. Conexao interrompida."); return false; } }
Fluxograma

Dificuldades Encontradas
• Na hora de enviar o post do web server, o caractere @ é substituído por %40.
• Dificuldades em implementar o File System, por conta do arquivo sempre adicionar o cursor no final e não no inicial do arquivo (para sobrescrita).
• Envio de e-mail – são disparadas múltiplas chamadas a partir do momento em que é atingido o valor. Para contornar este problema, o ideal seria implementar mais um parâmetro para configurar quantos e-mails deseja-se enviar e implementar uma lógica de controle para isso.
• Em alguns momentos, foi necessário utilizar comandos de replace e trim para caracteres indesejados nas variáveis.
Procedimento para permitir o Nodemcu receber programas
Para o correto funcionamento do Nodemcu na linguagem C, utilizando a IDE do Arduino, é necessário apenas instalar os pacotes do Esp8266 pela própria IDE do Arduino.
Referências Utilizadas
• http://pedrominatel.com.br/pt/esp8266/webserver-log-no-esp8266-com-spiffs/
• https://www.arduino.cc/en/Main/Software
Código Fonte
Baixe o código fonte clicando aqui.
Equipe do Projeto:
- Flávio Losada (flavio.losada@outlook.com)
- Jader Tomelin (jader.tomelin@gmail.com)
- Leonardo Fiedler (leonardo_fiedler@hotmail.com)
- Matheus Pereira (pereiramateduardo@gmail.com)
O senhor caveira é programador web, mobile, Desktop, amante da Tecnologia e filósofo de boteco.