ÍNDICE DE CONTEÚDO
- Monitoramento de água com IoT – Parte 1
- Monitoramento de água com IoT – Parte 2
- Monitoramento de água com IoT – Parte 3
Esta é a terceira parte da série de artigos que descrevem um projeto de monitoramento de água usando IoT. A série é composta de três artigos, sendo eles:
- Parte 1 – objetiva introduzir o leitor ao que o projeto consiste e em como configurar a comunicação ZigBEE utilizada no projeto;
- Parte 2 – visa apresentar a parte bare-metal da solução, com explicações detalhadas do código e conceitos e estratégias de desenvolvimento;
- Parte 3 – parte final da série, destinada a explicar em detalhes a parte de Linux Embarcado do projeto e interação com usuário via Internet (aqui o conceito de IoT é dominante).
Este artigo tornou-se viável graças ao apoio da Eletrogate, a qual forneceu o sensor de fluxo de água utilizado no projeto.
Pré-requisitos
Devido à multidisciplinaridade deste projeto, para uma boa compreensão dos conteúdos desta série de artigos são desejados os seguintes pré-requisitos:
- Conhecimento básico de Linux Embarcado;
- Conhecimento de linguagem de programação C;
- Conhecimento de linguagem de programação Python;
- Conhecimentos de MQTT (recomendo fortemente a leitura do artigo MQTT e Intel Edison);
- Conhecimento em desenvolvimento de sistemas embarcados bare-metal (ter noção das principais técnicas e conceitos deste tipo de sistema, tais como: interrupções externas, timers, comunicação serial por interrupção, watchdog timer, etc.).
Sistema Linux Embarcado do projeto
Nesta terceira e última parte da série de artigos, mostramos a parte da solução que usa Linux Embarcado (Intel Edison). Este sistema consiste em um sistema rodando em Linux Embarcado, sendo este responsável por interfacear o usuário final com as medições obtidas na placa com sistema bare-metal, além de acrescentar funcionalidades de alto nível (serão melhor explicadas com o decorrer deste artigo).
Além disso, esta parte do sistema se comunica via MQTT com uma página web, sendo esta a interface com usuário final.
Em diagrama, esta parte do sistema pode ser descrita conforme a figura 1.
As funcionalidades deste sistema são:
- Solicitar consumo acumulado e vazão instantânea ao sistema bare-metal;
- Solicitar versão do firmware do sistema bare-metal;
- Resetar/zerar consumo acumulado total. Deste modo pode-se medir o consumo acumulado a partir do momento que o usuário desejar;
- Estabelecer um limite de consumo de água. Se o limite for excedido, um e-mail é enviado ao usuário avisando da situação. Além disso, na interface com o usuário, haverá um aviso que o limite de consumo foi atingido.
Todas as interações com o usuário ocorrerão através de um site / tela no browser, sendo que os dados enviados e recebidos para o sistema utilizarão MQTT (veja este artigo para mais detalhes sobre MQTT).
O que será utilizado para compor o sistema Linux Embarcado?
Os recursos utilizados para compor este sistema estão descritos abaixo:
Recursos de hardware
O hardware deste sistema consiste em um Intel Edison, uma placa expansora de I/O Arduino Breakout Board e um dongle ZigBEE ligado à porta USB Host da placa expansora.
A Intel Edison consiste em um sistema poderoso, capaz de rodar um sistema operacional Linux (de fábrica, já vem com uma distribuição baseada em Yocto instalada). Possui um hardware muito interessante para suas dimensões físicas (próximas às dimensões de um SD Card) e conectividade WiFi e Bluetooth embutidas, sendo assim uma solução muito boa para sistemas IoT e sistemas stand-alone (sobretudo pelo fato de não possuir interface gráfica, o que significa que o consumo é muito baixo em relação a uma Rapiberry Pi, por exemplo). Mais informações sobre a Intel Edison podem ser obtidas neste artigo aqui.
Tal sistema possui conectividade Wi-Fi com a internet. Além disso, uma fotne 5V / 1,5A é utilizada para alimentar todo o sistema Linux Embarcado.
Recursos de software
O software do sistema é feito totalmente em Python, dada a maior facilidade de entender o código-fonte final (em relação a um feito em C puro, por exemplo).
Preparando a Intel Edison
Antes de prosseguirmos com o desenvolvimento do sistema em si, precisamos preparar a Intel Edison para se comunicar serialmente com o dongle ZigBEE. Este dongle utiliza um chip FTDI, logo é necessário instalar o driver dele na Intel Edison. Para isto, seguir os passos abaixo:
- Certificar-se que a Intel Edison está conectada à internet. Caso estiver utilizando o Wi-Fi do próprio Intel Edison para isto, esta verificação pode ser feita com o seguinte comando:
1ifconfig
- Serão exibidas todas as ionterfaces de comunicação da Intel Edison, assim se na interface wlan0 estiver um IP associado, a Intel Edison está corretamente conectada à internet;
- A configuração do repositório de pacotes conforme este artigo instrui;
- Atualizar a lista de pacotes disponíveis com o seguinte comando:
1opkg update
- Instalar o driver do chip FTDI com o comando:
1opkg install kernel-module-ftdi-sio
Após feito isso, coloque na USB Host da Arduino Breakboard o dongle ZigBEE. Aguarde alguns segundos e execute o comando abaixo:
1 |
dmesg | grep tty |
Este comando mostra todas as interfaces seriais disponíveis. Deverá aparecer uma entrada serial (no meu caso /dev/ttyUSB0) para o chip FTDI.
Além disso, é necessário baixar o pyserial (biblioteca Python para lidar com serial). Para isso, Primeiro baixe o pyserial com o seguinte comando:
1 |
wget https://pypi.python.org/packages/source/p/pyserial/pyserial-2.7.tar.gz#md5=794506184df83ef2290de0d18803dd11 --no-check-certificate |
Descomprima o arquivo baixado com o seguinte comando:
1 |
tar xvf pyserial-2.7.tar.gz |
E, por fim, execute os comandos a seguir para instalar o pyserial:
1 2 |
cd pyserial-2.7 python setup.py install |
Observações gerais sobre a comunicação com sistema bare-metal
Segue abaixo algumas observações gerais sobre a comunicação com o sistema bare-metal:
- Dada a chance de o comando enviado não chegar ao sistema bare-metal (dados corrompidos / perda de integridade dos dados, interferência, etc.), são feitas tentativas seguidas de envio do comando (até o sistema bare-metal responder corretamente, ou seja, o checksum dos dados recebidos coincidir com o calculado, garantindo assim a integridade dos dados recebidos);
- Cada tentativa de envio do comando tem um timeout de 1 segundo;
- Não há fila de transmissão e recepção.
Código-fonte
O script Python pode ser divido em duas partes distintas: contínua e sob demanda. A parte contínua roda “sem parar” (enquanto o script Python estiver em execução), enquanto a parte sob demanda responde a comandos enviados via MQTT da página de monitoramento.
A parte contínua faz a leitura das informações de vazão, consumo acumulado e versão de firmware do sistema bare-metal de cinco em cinco segundos (em outras palavras, consiste no laço principal).
Após a leitura, estes dados são enviados via MQTT para a página de monitoramento. Além destes dados, são enviados também a meta estabelecida de consumo e o status do consumo (se excedeu a meta de consumo de água ou não) e esta parte também é a responsável por enviar o e-mail de aviso ao usuário caso o consumo acumulado de água exceder a meta estipulada.
A parte sob demanda é responsável por responder comandos MQTT recebidos da página de monitoração. Seguem esses comandos:
- Reset de consumo acumulado;
- Início de calibração;
- Finalização de calibração;
- Requisição de informações (vazão, consumo acumulado e versão de firmware do sistema bare-metal, meta configurada e status de consumo de água);
- Envio de meta de consumo de água e e-mail para avisar usuário em caso de a meta ser excedida.*
*Neste caso, ambos os dados são gravados em arquivos texto na Intel Edison.
Será necessário ter uma conta de e-mail YahooMail, pois foi o único e-mail grátis que não barrou e-mail disparado pelo Python (não consegui fazer o mesmo com o GMail). Portanto, crie uma conta no YahooMail e preencha os dados de login e senha no código antes de rodar o script.
Abaixo, segue o código-fonte completo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 |
"Sistema de monitoramento de agua com IoT (parte Linux Embarcado)" __copyright__ = 'Copyright (C) 2011 Pedro Bertoleti' __version__ = '0.1' __date__ = 'Sep 2015' __license__ = 'MIT' import sys import time try: import paho.mqtt.client as mqtt import mraa import serial import smtplib from binascii import hexlify except ImportError, mod: print "%s not found" % (mod) sys.exit(7) TempoEntreLeituraSensores = 5 #Tempo (em segundos) entre duas leituras de sensores TimeoutConexao = 5 #Timeout da conexao com broker TopicoSubscriber = "MQTTAguaIOTEnvia" #topico usado para receber dados do websocket TopicoPublisher = "MQTTAguaIOTRecebe" #topico usado para enviar dados do websocket EmailConsumoJaFoiEnviado = 0 #variavel de controle do envio de e-mail (alerta que a meta de consumo de agua foi excedida) EstaEmCalibracao = 0 #variavel que indica se a placa esta em modo de calibracao def CalculaChecksum(BytesRecebidos): """ Funcao: Calcula checksum da mensagem recebida parametros: array de bytes a ser calculado checksum retorno: resposta do comando """ CheckSumCalculado = 0 SomaCks = sum(BytesRecebidos) CheckSumCalculado = ((~SomaCks) + 1)&0xFF return CheckSumCalculado def EnviaComandoSerial(ComandoASerEnviado, OpcodeResposta): """ Funcao: Envia requisicao ate receber resposta parametros: Comando a ser enviado (string) e opcode que deve estar presente na resposta retorno: resposta do comando """ comando_bytes = ComandoASerEnviado.decode("hex") RecebeuResposta=0 #tenta finalizar a calibracao ate conseguir. As tentativas sao feitas em intervalos de 1 segundo. while RecebeuResposta == 0: usart.write(comando_bytes) dados_lidos = usart.readline() #se ha o opcode esperado na resposta, trata-se de uma mensagem valida if (OpcodeResposta in dados_lidos): #obtem checksum recebido e tamanho recebido CksRecebidoStr = dados_lidos[3] HexCksRecebido = hexlify(CksRecebidoStr) CksRecebido = int (HexCksRecebido,16) TamanhoString = dados_lidos[2] hex = hexlify(TamanhoString) TamInt = int(hex, 16) DadosLidosBytes = bytearray() DadosLidosBytes.extend(dados_lidos[4:4+TamInt]) #verificacao do checksum if (CalculaChecksum(DadosLidosBytes) == CksRecebido): RecebeuResposta = 1 print "- Checksum do comando de opcode ",dados_lidos[1]," esta OK!" else: print "- Checksum Recebido: ",repr(CksRecebido)," Cks Calculado: ",CalculaChecksum(DadosLidosBytes)," Opcode: ",dados_lidos[1] return dados_lidos def EnviaEmailAvisoMetaAtingida(): """ Funcao: envia e-mail de aviso de meta de consumo atingido parametros: Meta configurada retorno: nenhum """ global EmailConsumoJaFoiEnviado if (EmailConsumoJaFoiEnviado == 1): #se o e-mail ja foi enviado, nada eh feito return 0 #gravacao do e-mail em arquivo-texto externo arqEmail = open('email.txt', 'r') destinatario = arqEmail.readline() arqEmail.close() #informacoes referentes ao e-mail #IMPORTANTE: descomentar a linha abaixo e inserir o e-mail remetente desejado #remetente = '' mensagem = "Aviso do sistema de monitoramento de Agua com IoT: seu consumo de agua excedeu a meta estabelecida. Consulte sua pagina de monitoramento." #IMPORTANTE: descomentar as linhas abaixo e inserir os dados de sua conta YahooMail #login = '' #senha = '' #envio do e-mail server = smtplib.SMTP('smtp.mail.yahoo.com', 587) server.starttls() server.login(login,senha) server.sendmail(remetente, destinatario, mensagem) server.quit() #sinlaiza que e-mail (alerta que a meta de consumo de agua foi excedida) ja foi enviado EmailConsumoJaFoiEnviado = 1 def LeMetaGravada(): """ Funcao: le a meta gravada no arquivo externo parametros: nenhum retorno: meta lida do arquivo externo """ MetaLida=0 try: #leitura (do arquivo texto) da meta estabelecida) e conversao do dado lido para float arqMeta = open('meta.txt', 'r') MetaLida = float(arqMeta.readline()) arqMeta.close() except IOError: print "- Falha ao ler arquivo de meta" return MetaLida def VerificaSeAtingiuMeta(ConsumoLido,MetaDeConsumo): """ Funcao: verifica se a meta de consumo foi atingida e, se sim, dispara um e-mail ao usuario parametros: consumo lido retorno: "ok" - consumo acumulado abaixo da meta estabelecida "naook" - consumo acumulado acima da meta estabelecida """ StatusMeta="ok" try: ValorConsumoLido = float(ConsumoLido) print "Consumo usado na comparacao: ",repr(ConsumoLido) print "Meta lida: ",str(MetaDeConsumo) print "Consumo lido: ",ConsumoLido #verifica se consumo excedeu a meta if (ValorConsumoLido > MetaDeConsumo): StatusMeta="naook" #se a meta de consumo foi excedida, envia e-mail alertando o usuario EnviaEmailAvisoMetaAtingida() except IOError: print "- Falha ao ler arquivo de meta" except ValueError: print "- Problemas ao converter leitura do consumo em float. Bytes recebidos: ",repr(ConsumoLido) return StatusMeta def GravaEmailEMeta(EmailRecebido, MetaRecebida): """ Funcao: grava em um arquivo texto o e-mail e meta informados parametros: email e meta informados pela pagina de monitoramento retorno: nenhum """ try: #gravacao do e-mail em arquivo-texto externo arqEmail = open('email.txt', 'w') arqEmail.write(EmailRecebido) arqEmail.close() print "- Arquivo de e-mail gravado com sucesso" #gravacao da meta em arquivo-texto externo arqMeta = open('meta.txt', 'w') arqMeta.write(MetaRecebida) arqMeta.close() print "- Arquivo de meta gravado com sucesso" except IOError: print "- Falha ao gravar email e meta" def EnviaInformacoes(): """ Funcao: requisita informacoes ao sistema bare-metal e as envia, por mqtt, a pagina de monitoramento parametros: nenhum retorno: nenhum """ global EstaEmCalibracao #se esta em modo de calibracao, nao ha porque solicitar informacoes da placa. if (EstaEmCalibracao == 1): return 0 #requisita versao de firmware #tenta ler a versao do firmware ate conseguir. As leituras sao feitas em intervalos de 1 segundo. dados_lidos = EnviaComandoSerial("02510000","Q") #String da versao VersaoString = dados_lidos[4:9] #requisita consumo acumulado #tenta ler o consumo acumulado ate conseguir. As leituras sao feitas em intervalos de 1 segundo. dados_lidos = EnviaComandoSerial("024C0000","L") #String do consumo acumulado ConsumoAcumuladoString = dados_lidos[4:11] #requisita vazao instantanea #tenta ler a vazao instantanea ate conseguir. As leituras sao feitas em intervalos de 1 segundo. dados_lidos = EnviaComandoSerial("02560000","V") #String da vazao instantanea VazaoString = dados_lidos[4:11] #envia as informacoes via MQTT MetaConfigurada = LeMetaGravada() #verifica se meta de consumo foi atingida / excedida StatusMeta = VerificaSeAtingiuMeta(ConsumoAcumuladoString,MetaConfigurada) #envia para a pagina de monitoramento, por MQTT, as informacoes obtidas do sistema bare-metal client.publish(TopicoPublisher, VersaoString+">"+ConsumoAcumuladoString+">"+VazaoString+">"+str(MetaConfigurada)+">"+StatusMeta) def on_connect(client, userdata, flags, rc): """ Callback - chamada quando a conexao eh estabelecida """ print("Conectado ao broker. Retorno da conexao: "+str(rc)) # Informa que o topico que este subscriber ira escutar client.subscribe(TopicoSubscriber) def on_message(client, userdata, msg): """ Callback - chamado quando alguma mensagem eh recebida Corresponde a parte do sistema que opera sob demanda. """ global EmailConsumoJaFoiEnviado global EstaEmCalibracao #escreve na tela a mensagem recebida print("Topico: "+msg.topic+" - Mensagem recebida: "+str(msg.payload)) #recebeu comando para resetar consumo acumulado if (str(msg.payload) == "ResetConsumo"): #tenta resetar / zerar o consumo acumulado ate conseguir. As tentativas sao feitas em intervalos de 1 segundo. dados_lidos = EnviaComandoSerial("02520000","R") #reinicia variavel que controla envio do email (alerta que a meta de consumo de agua foi excedida) EmailConsumoJaFoiEnviado = 0 #le e envia informacoes do sistema bare-metal por MQTT para pagina de monitoramento EnviaInformacoes() return 0 if (str(msg.payload) == "IniciaCalibracao"): #tenta iniciar a calibracao ate conseguir. As tentativas sao feitas em intervalos de 1 segundo. dados_lidos = EnviaComandoSerial("02450000","E") EstaEmCalibracao = 1 print "--- Entrada no modo de calibracao ---" return 0 if (str(msg.payload) == "FinalizaCalibracao"): #tenta finalizar a calibracao ate conseguir. As tentativas sao feitas em intervalos de 1 segundo. dados_lidos = EnviaComandoSerial("02530000","S") EstaEmCalibracao = 0 print "--- Saida do modo de calibracao ---" EnviaInformacoes() return 0 if (str(msg.payload) == "RequisitaInformacoes"): #le e envia informacoes do sistema bare-metal por MQTT para pagina de monitoramento EnviaInformacoes() return 0 if ("EnviaConfigsMeta" in str(msg.payload)): #meta de consumo e agua e e-mail para aviso foram recebidos. Sera feita a gravacao dos mesmos em arquivos-texto externos DadosConfigs = str(msg.payload).split(">") GravaEmailEMeta(DadosConfigs[2], DadosConfigs[1]) #reinicia variavel que controla envio do email (alerta que a meta de consumo de agua foi excedida) EmailConsumoJaFoiEnviado = 0 if __name__ == '__main__': #--------------------------- # Programa principal #--------------------------- #configuracao da serial (FTDI): 9600 bauds, 8 bits, sem paridade e 1 stop-bit try: port = "/dev/ttyUSB0" usart = serial.Serial (port,9600,serial.EIGHTBITS,serial.PARITY_NONE,serial.STOPBITS_ONE, 1) except serial.SerialException as e: print("Falha ao abrir porta serial '{0}'".format(e)) sys.exit(9) client = mqtt.Client() client.on_connect = on_connect #configura callback (de quando eh estabelecida a conexao) client.on_message = on_message #configura callback (de quando eh recebida uma mensagem) # tenta se conectar ao broker na porta 1883 (o parametro '60' eh o tempo de keepalive). # Nesse caso, se nenhuma mensagem for trafegada em ate 60 segundos, eh enviado um ping ao # broker de tempos em tempos (para manter a conexao ativa) client.connect("test.mosquitto.org", 1883, 60) #loop infinito. Gerencia conexao periodicamente e executa demais tarefas necessarias StatusConexaoBroker=0 TempoInicialSegundos=int(time.time()) TempoAtualSegundos=0 #------------------------------------------------------------------------- # Laco principal do sistema. Corresponde a parte continua do sistema #------------------------------------------------------------------------- while True: StatusConexaoBroker = client.loop(TimeoutConexao) if (StatusConexaoBroker > 0): client.connect("test.mosquitto.org", 1883, 60) #se ocorrer algum erro, reconecta-se ao broker TempoAtualSegundos = int(time.time()) #verifica se chegou o momento de executar o envio das informacoes para a pagina de monitoramento if ((TempoAtualSegundos-TempoInicialSegundos) > TempoEntreLeituraSensores): #reinicia a contagem de tempo TempoInicialSegundos = TempoAtualSegundos #le e envia informacoes do sistema bare-metal por MQTT para pagina de monitoramento EnviaInformacoes() |
Ou, se preferir, baixe o arquivo com o script Python:
MonitoramentoAguaIOTPython.zip
3.87 KBExecução do script Python
Durante a execução, no terminal de dados conectado à Intel Edison (via USB-Serial ou SSH) serão escritas algumas informações gerais da comunicação: recepção de dados solicitados da placa bare-metal (incluindo se o checksum “bateu” ou não, para se ter uma noção da qualidade da comunicação sem fio), leitura da meta configurada e alertas de entrada e saída do modo de calibração.
O script Python pode ser executado da forma clássica (comando: python AguaIOT.py). Porém, para uma aplicação embarcada que deve ser alimentada e sair funcionando, seria muito útil a execução automática do script ao inicializar a placa, não é mesmo?
Para isso, basta seguir um tutorial clicando aqui.
Inteface Web para monitoramento de água
O código-fonte completo da interface Web pode ser baixado aqui:
Sistema online.rar
196.45 KBEsta consiste de um site simples, com alguns arquivos .js e imagens (ou seja, pode ser hospedado em qualquer servidor grátis por aí ou, até mesmo, rodar na sua própria máquina). Este se comunica via MQTT com um broker, assim como a Intel Edison. Deste modo, a Intel Edison e a página Web podem estabelecer comunicação. E o melhor: esta página roda em tablet, PC e smartphone, independente da “religião” (fãs das maçãs, robozinhos verdes e janelas agradecem)! Enfim, qualquer coisa que tenha um navegador com suporte a JavaScript consegue acessar. Em suma, trata-se de uma interface de usuário realmente portável.
Na figura 2 pode-se ver a tela de interface com usuário.
Este sistema pode ser acessado online também. Para isto, clique aqui.
Vídeo – Sistema completo
Um vídeo do sistema completo funcionando pode ser visto abaixo:
The end of the beginning – Considerações finais e possibilidades de expansão
Este sistema foi construído com o intuito de monitoramento inteligente de água utilizando conceitos de IoT, porém isto não pára por aqui.
Conforme a arquitetura de comunicação entre Intel Edison e sistema-bare metal adotado, sobretudo a topologia de comunicação (em estrela), com poucas modificações (no protocolo de comunicação) é possível colocar mais módulos sendo lidos. Logo, é possível controlar periféricos ou ler o consumo de energia remotamente, via Internet, adicionando um módulo/sistema bare-metal para cada funcionalidade extra. Portanto, eu gostaria muito que este projeto servisse de base para projetos maiores, com mais medições e controles via IoT sendo usados. Afinal, tudo evolui, certo?
Como sugestões de implementações futuras / sistemas bare-metal a serem acoplados no sistema, seguem:
- Medição de consumo de energia elétrica;
- Controle on/off e monitoramento de cargas (lâmpadas, irrigadores, eletrodomésticos, etc.);
- Monitoramento de temperatura em diferentes ambientes;
- Monitoramento de poluição sonora em diferentes ambientes;
- Monitoramento de consumo de água em vários locais diferentes de uma mesma planta, como o caso de um hotel, por exemplo;
- Monitoramento de consumo de água potável em bebedouros (para fins de se ter uma média de consumo de água por período ou por consumidor);
- …E por aí vai!
Ou seja, este é o fim desta série, mas espero que não seja o fim de projetos utilizando os conceitos e sistemas aqui apresentados.
Agradecimentos
Sobre a realização desta parte do projeto, eu gostaria de agradecer ao Cleiton Bueno pela ajuda nas dúvidas e melhorias no script em Python construído.
Olá! Gostaria de saber se o dongle zigbee é compatível com a minibreakout board se eu usar um conversor usb -> microusb
Parabéns novamente pelo artigo, pretendo implementar a mesma solução porém utilizando ESP8266 e Raspbery. Esse artigo ajudou a esclarecer diversas dúvidas e ter algumas idéias e com certeza deverá aparecer nas referências do projeto.
João, muito obrigado!
Fico feliz que você curtiu a série e que irá utilizar os conceitos aqui apresentados. É gratificante saber que este projeto pode inspirar outros!