Campainha IoT
Índice
Sobre
O LHC precisava de uma campainha. A ideia não era ser uma campainha tradicional do tipo "dim-dom", mas uma campainha IoT/Wifi.
Desta maneira é possível saber quando chegar alguém que não possua a chave ou alguma entrega.
Funcionamento
- Um ESP-01 permanece a maior parte do tempo em "Deep Sleep" preservando assim bateria.
- Pressionado o botão da campainha que está conectado aos pinos RST e GND o ESP é retirado do modo Deep Sleep.
- O ESP inicializa-se e se conecta a rede Wifi. (Para garantir uma associação mais rápida o IP é configurado como estático).
- Uma vez associado a rede o ESP envia um HTTP request para um servidor.
- O ESP pisca um led indicando que enviou o request para o servidor e que aguarda um confirmação. Este passo geralmente é muito rápido, cerca de 1 a 2 segundos. Caso o led continue piscando por mais de 15 segundos, significa que o servidor não recebeu o request.
- Recebido o request pelo servidor um processo toca uma música aleatória localizada em um diretório pré-configurado.
- O ESP recebe uma confirmação do servidor que a campainha foi tocada e entra novamente em Deep Sleep.
- Caso a o ESP não receba confirmação do servidor em até 15 segundos ele entra em Deep Sleep.
Componentes
- ESP8266 (Modelo: ESP01) - http://www.esp8266.com, https://en.wikipedia.org/wiki/ESP8266
- Pulsador de Campainha 10A 250V (Marca: APOIO, Modelo: XBC2021)
- Circuito simples com conector de 8 pinos para encaixe do ESP-01 .
- 2 Pilhas tamanho AA (1,5V).
- Suporte para pilhas AA.
Circuito
Monitoramento
Quando o ESP envia o request para o servidor tocar a música ele envia também a voltagem de alimentação (em milivolts) utilizando a função node.readvdd33().
O servidor por sua vez envia este valor para a plataforma ThingSpeak sendo assim possível saber quando a campainha foi acionada e monitorar a voltagem da bateria no decorrer no tempo estimando a necessidade de substituição.
Nota: Em algumas versões mais recentes do ESP e do firmware NodeMCU a função node.readvdd33() foi removida e substituída pela função adc.readvdd33(). Esta função no entanto só consegue ler o voltagem se o ESP estiver desconectado do wifi o que é feito através da função wifi.sta.disconnect(). Altere a função read_voltage() de acordo com a versão do seu ESP e firmware.
- Para acessar o canal de monitoramento da Campainha do LHC clique aqui
Firmware/Software
Cliente (Campainha)
-- Campainha IoT - LHC - v1.1
-- ESP Inicializa pinos, Configura e Conecta no Wifi, Cria conexão TCP
-- e na resposta de um "Tocou" coloca o ESP em modo DeepSleep para economizar bateria.
-- Se nenhuma resposta for recebida em 15 segundos coloca o ESP em DeepSleep.
led_pin = 3
status_led = gpio.LOW
ip_servidor = "192.168.1.10"
ip_campainha = "192.168.1.20"
voltagem=3333
function desliga_circuito()
print("Colocando ESP em Deep Sleep")
node.dsleep(0)
end
function read_voltage()
-- Desconecta do wifi para poder ler a voltagem de alimentação do ESP.
wifi.sta.disconnect()
voltagem = adc.readvdd33()
print("Voltagem: "..voltagem)
-- Inicializa o Wifi e conecta no servidor
print("Inicializando WiFi")
init_wifi()
end
function pisca_led()
gpio.write(led_pin, status_led)
if status_led == gpio.LOW then
status_led = gpio.HIGH
else
status_led = gpio.LOW
end
end
function init_pins()
gpio.mode(led_pin, gpio.OUTPUT)
gpio.write(led_pin, status_led)
end
function init_wifi()
wifi.setmode(wifi.STATION)
wifi.sta.config("SSID", "password")
wifi.sta.connect()
wifi.sta.setip({ip=ip_campainha,netmask="255.255.255.0",gateway="192.168.1.1"})
-- Aguarda conexão com Wifi antes de enviar o request.
function try_connect()
if (wifi.sta.status() == 5) then
tmr.stop(0)
print("Conectado, mandando request")
manda_request()
-- Se nenhuma confirmação for recebida em 15 segundos, desliga o ESP.
tmr.alarm(2,15000,0, desliga_circuito)
else
print("Conectando...")
end
end
tmr.alarm(0,1000,1, function() try_connect() end )
end
function manda_request()
tmr.alarm(1, 200, 1, pisca_led)
print("Request enviado")
-- Cria a conexão TCP
conn=net.createConnection(net.TCP,false)
-- Envia o toque da campainha e voltagem para o servidor
conn:on("connection", function(conn)
conn:send("GET /?bateria=" ..voltagem.. " HTTP/1.0\r\n\r\n")
end)
-- Se receber "Tocou" do servidor, desliga o ESP.
conn:on("receive", function(conn, data)
if data:find("Tocou") ~= nil then
desliga_circuito()
end
end)
-- Conectar no servidor
conn:connect(9999,ip_servidor)
end
print("Inicializando pinos")
init_pins()
print ("Lendo voltagem")
read_voltage()
Servidor
#!/usr/bin/python2
import time
import BaseHTTPServer
import os
import random
import string
import requests
from urlparse import parse_qs, urlparse
HOST_NAME = '0.0.0.0'
PORT_NUMBER = 9999
# A variável MP3_DIR será construida tendo como base o diretório HOME do usuário + Music/Campainha
# (e.g: /home/usuario/Music/Campainha)
MP3_DIR = os.path.join(os.getenv('HOME'), 'Music', 'Campainha')
VALID_CHARS = set(string.ascii_letters + string.digits + '_.')
CHAVE_THINGSPEAK = 'XYZ11ZYX99XYZ1XX'
# Salva o arquivo de log no diretório do usuário (e.g: /home/usuário/campainha.log)
ARQUIVO_LOG = os.path.join(os.getenv('HOME'), 'campainha.log')
def filtra(mp3):
if not mp3.endswith('.mp3'):
return False
for c in mp3:
if not c in VALID_CHARS:
return False
return True
def log(msg, output_file=None):
if output_file is None:
output_file = open(ARQUIVO_LOG, 'a')
output_file.write('%s: %s\n' % (time.asctime(), msg))
output_file.flush()
class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_GET(s):
s.send_header("Content-type", "text/plain")
query = urlparse(s.path).query
if not query:
s.send_response(404)
s.end_headers()
s.wfile.write('Not found')
return
components = dict(qc.split('=') for qc in query.split('&'))
if not 'bateria' in components:
s.send_response(404)
s.end_headers()
s.wfile.write('Not found')
return
s.send_response(200)
s.end_headers()
s.wfile.write('Tocou')
s.wfile.flush()
log("Atualizando thingspeak")
r = requests.post('https://api.thingspeak.com/update',
data={'api_key': CHAVE_THINGSPEAK, 'field1': components['bateria']})
log("Thingspeak retornou: %d" % r.status_code)
log("Tocando MP3")
mp3s = [f for f in os.listdir(MP3_DIR) if filtra(f)]
mp3 = random.choice(mp3s)
os.system("mpv " + os.path.join(MP3_DIR, mp3))
if __name__ == '__main__':
server_class = BaseHTTPServer.HTTPServer
httpd = server_class((HOST_NAME, PORT_NUMBER), MyHandler)
log("Server Starts - %s:%s" % (HOST_NAME, PORT_NUMBER))
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass
httpd.server_close()
log("Server Stops - %s:%s" % (HOST_NAME, PORT_NUMBER))
Roadmap
- Permitir atualização remota do código. Ao invés de "Tocou" se o ESP-01 receber um "Upgrade" ele deverá buscar o arquivo init.lua no servidor e atualiza-se.
- Construir uma Campainha móvel com um Buzzer, um ESP-01, e um botão liga/desliga para levar a outras áreas do LHC aonde não seja possível ouvir a música do servidor (ex: Quintal, Marcenaria).
Fotos
Quem
- Fernando Frediani
- Leandro Pereira
- Ronaldo Teodoro
Referências
- Fontes de sons para toques da campainha - http://www.myinstants.com/