Sem dúvidas, a automação é algo que sempre fascinou a maioria das pessoas que gostam e trabalham com eletrônica e sistemas embarcados. Afinal, fazer sistemas que trazem mais conforto, comodidade, qualidade na produção industrial, redução de custos e maior qualidade de vida é algo muito atrativo. Nesta série de posts, será mostrado como controlar o NodeMCU (até 5 deles) remotamente utilizando uma Raspberry PI e um display LCD 3.5 touch.
Neste post em específico, serão abordados os primeiros pontos deste projeto: construção de uma GUI (Graphic User Interface) em Python e integração desta com MQTT, tudo feito em Python, e na parte 2 mostramos especificamente como controlar o NodeMCU.
Material necessário
Para fazer este projeto para controlar o NodeMCU usando Raspberry Pi, você precisará de:
- Display LCD touch 3.5″
- Raspberry PI (pode ser a 3B ou a PI Zero W, fica a sua escolha)
- Cartão de memória de, no mínimo, 8GB para a Raspberry PI
- ESP8266 NodeMCU
Também recomendo fortemente a leitura deste nosso post sobre como instalar o display LCD 3.5″ touch na Raspberry PI. Assume-se aqui que você já esteja com a Raspberry PI totalmente operante, com a distribuição Raspbian mais nova instalada e display LCD 3.5″ touch já configurado e funcionando.
GUI em Python
Como o próprio nome diz, uma GUI (Graphic User Interface) é uma interface gráfica a qual o usuário do sistema utiliza para interagir com o mesmo. As GUIs são recursos fundamentais em quase qualquer sistema atualmente, pois, além de ser mais bonito que uma “tela preta com comandos”, torna muito mais intuitivo o modo como o usuário deve interagir com um determinado sistema.
Tome como exemplo qualquer sistema operacional hoje em dia: você preferiria ter telas e janelas com nomes e itens sugestivos ou seria melhor utilizar 100% do tempo comandos com as mais variadas sintaxes e complexidades? Aposto que você opta pelas janelas e afins.
Hoje em dia, há inúmeras opções de interfaces gráficas que podemos usar, em qualquer que seja o sistema operacional utilizado e em praticamente qualquer linguagem de programação. Neste projeto em específico, é utilizada a linguagem Python, o que restringe a gama de opções de interfaces gráficas a alguns candidatos viáveis. Dentre estes, foi escolhido o TKinter.
TKinter é a biblioteca padrão do Python para desenvolvimento de interfaces gráficas. Além de ser bem completa, a biblioteca TKinter é muito difundida e utilizada pela comunidade, o que significa que é muito rica em termos de documentação e exemplos de uso disponíveis.
Como referência para leitura, indico os seguintes sites:
- Site oficial: https://docs.python.org/2/library/tkinter.html
- Wiki TKinter: https://wiki.python.org/moin/TkInter
- Guia de introdução ao uso do TKinter: http://python-textbok.readthedocs.io/en/1.0/Introduction_to_GUI_Programming.html
- Artigo para compreensão de construção de layouts em TKinter: http://zetcode.com/gui/tkinter/layout/
Principais diferenças na programação Python quando utilizando TKinter para GUIs
Se você está acostumado a programar em Python sem interfaces gráficas, será necessário se familiarizar com alguns conceitos novos ao programar GUIs em TKinter. Isto será necessário pelo seguinte: ao utilizar o TKinter, as ações e fluxos do programa serão event driven, ou seja, serão gerados eventos / chamadas para cada ação do usuário utilizando a GUI. Portanto, o programa não tem um fluxo puramente “linear”, mas sim repartido em chamadas específicas da GUI. Isso significa que há uma função específica a ser executada para quando um botão for clicado, um checkbox selecionado, etc, enquanto a biblioteca se encarrega em manter a tela em execução.
Outro ponto de atenção é o seguinte: quando é utilizada a biblioteca TKinter, a função/método mainloop() é responsável por manter a tela em execução. E a má notícia é que esta função é blocante. Ou seja, uma vez chamada a execução da tela, nada mais “a frente” da chamada desta função é executado até que a tela seja fechada. Uma consequência disso é que, agora, não se pode mais pensar em programas com loops infinitos, mas sim em Threads utilizadas para as funcionalidades do seu programa que não podem parar de executar. Em sistemas grandes, isso implica em alto uso de processamento e chance de concorrência de Threads por recursos computacionais, algo a ser monitorado com MUITO cuidado.
Em suma, o “fluxo principal” do programa será 100% ocupado pela execução da tela / GUI, enquanto demais funcionalidades do seu programa que precisem ser executadas o tempo todo (exemplo: conectividade MQTT) terão que ser feitas em Threads.
Widgets em uma janela
No TKinter, qualquer elemento gráfico que não seja a janela de fato é chamado de Widget. Portanto, os botões, edit texts, labels, check boxes , combo boxes, etc. são todos widgets. Além disso, cada Widget tem “vida própria”, ou seja, parâmetros e ações (events) próprios.
Alguns dos principais parâmetros dos widgets são:
- text: corresponde ao texto a ser exibido no Widget (por exemplo, o texto de um label ou texto de um botão)
- fg: este parâmetro corresponde ao foreground color. Na grande maioria dos casos, esta cor corresponde à cor da fonte dos Widgets. Este parâmetro aceita padrão RGB (#RRGGBB) para cores.
- bg: este parâmetro corresponde ao background color. Na grande maioria dos casos, esta cor corresponde à cor de fundo dos Widgets (cor de um botão ou cor de fundo de um label, por exemplo). Este parâmetro aceita padrão RGB (#RRGGBB) para cores.
- font: fonte a ser utilizada no texto exibido no Widget. Aceita nome de qualquer fonte TTF instalada no sistema, bem como permite configurar o tamanho de exibição de tal fonte.
- command: utilizado principalmente em botões, indica qual função / método será chamado quando a ação do Widget for executada pelo usuário. No caso do botão, é a função / método a ser executado quando for detectado click no botão.
Montando a GUI
O primeiro ponto a ser observado ao se montar uma GUI para seu sistema é a resolução do display que será usado e a porção da tela a ser ocupada. No caso deste projeto, trata-se de um display de 480 x 320 pixels, e será utilizada toda a área útil de display. Logo, a GUI deste projeto será full-screen.
O segundo ponto a ser observado é quais Widgets serão utilizados. No caso deste projeto, haverá um label e cinco botões (cada um responsável por fazer um toogle no output de um NodeMCU diferente).
Por último, devem ser pensados quais eventos devem ser utilizados. Como o projeto aqui feito é composto apenas de Widgets do tipo Label e botão, haverão cinco eventos relacionados aos botões.
Para controlar o NodeMCU, será montada uma GUI conforme mostra a figura 1:
Instalando bibliotecas necessárias
O TKinter já vem instalado junto com o Python na Raspberry (considerando que você está utilizando a distribuição Raspbian). Porém, será necessária a instalação da biblioteca paho-mqtt. Para tal, utilize o seguinte comando:
sudo pip install paho-mqtt
Dicas para escrever o código-fonte
Recomendo fortemente que você escreva o código-fonte no seu computador para depois colá-lo (ou transferi-lo) para a Raspberry PI. O motivo é que desenvolver o código na telinha de 480 x 320 pode não ser uma experiência muito confortável.
Código-fonte do projeto para controlar o NodeMCU
Segue abaixo o código-fonte do projeto para controlar o NodeMCU usando Raspberry Pi. Salve-o como GUITkinter.py. Deve-se atentar aos comentários para melhor compreensão de seu funcionamento.
IMPORTANTE: mude o valor do tópico de publish do artigo. para algo o mais único possível pra você. O motivo é que, fazendo desta maneira, diminui-se o risco de alguém utilizar o mesmo tópico que você e interferir no funcionamento de seu projeto.
#Programa: Controlar o Nodemcu usando Raspberry Pi import paho.mqtt.client as mqtt import sys from Tkinter import * from threading import Thread #Variaveis e objetos globais janela = Tk() #objeto que representa a janela / GUI #definicoes: client = mqtt.Client() Broker = "iot.eclipse.org" PortaBroker = 1883 KeepAliveBroker = 60 TimeoutConexao = 5 #em segundos TopicoPublish = "MQTTDisplayRaspPi" #substitua este topico por algum de sua escolha (de preferencia, algo "unico" pra voce) ######## #Eventos ######## def EventoBotao1(): global client print "[BOTAO] Botao 1 pressionado!" client.publish(TopicoPublish,"BT1") return def EventoBotao2(): global client print "[BOTAO] Botao 2 pressionado!" client.publish(TopicoPublish,"BT2") return def EventoBotao3(): global client print "[BOTAO] Botao 3 pressionado!" client.publish(TopicoPublish,"BT3") return def EventoBotao4(): global client print "[BOTAO] Botao 4 pressionado!" client.publish(TopicoPublish,"BT4") return def EventoBotao5(): global client print "[BOTAO] Botao 5 pressionado!" client.publish(TopicoPublish,"BT5") return def FinalizaPrograma(): global client print "O programa esta sendo finalizado." client.disconnect() janela.destroy() sys.exit(0) ########################### #Funcao de desenho de tela ########################### def DesenhaTela(): global janela # - coloca o titulo da janela como 'GUI - Python' # - muda sua cor de fundo # - define o tamanho da janela (nesse caso, 480x320, mesma resolucao do display) # - faz com que a janela seja posicionada no meio da tela # - coloca botoes na tela # - Coloca o logo MakerHero janela.title("GUI - Python") janela.configure(bg='#3F3F3F') width=480 height=320 screen_width = janela.winfo_screenwidth() screen_height = janela.winfo_screenheight() xCentro = (screen_width/2) - (width/2) yCentro = (screen_height/2) - (height/2) janela.geometry('%dx%d+%d+%d' % (width, height, xCentro, yCentro)) #widgets label1 = Label(text="Blog MakerHero", fg='#00E0F9', bg='#3F3F3F', font=("Helvetica",12) ) label1.grid(row = 1, column = 1, padx=5, pady=5) botao1 = Button(janela, text='Botao 1', command = EventoBotao1, height=5, width=15) botao1.grid(row = 11, column = 1, padx=5, pady=5) botao2 = Button(janela, text='Botao 2', command = EventoBotao2, height=2, width=15) botao2.grid(row = 21, column = 1, padx=5, pady=5) botao3 = Button(janela, text='Botao 3', command = EventoBotao3, height=1, width=15) botao3.grid(row = 1, column = 16, padx=5, pady=5) botao4 = Button(janela, text='Botao 4', command = EventoBotao4, height=5, width=15) botao4.grid(row =11, column = 16, padx=5, pady=5) botao5 = Button(janela, text='Botao 5', command = EventoBotao5, height=2, width=15) botao5.grid(row = 21, column = 16, padx=5, pady=5) #associa funcao ao fechamento da janela (evento de fechamento) janela.protocol('WM_DELETE_WINDOW', FinalizaPrograma) return #Callback - conexao ao broker realizada def on_connect(client, userdata, flags, rc): print("[STATUS] Conectado ao Broker. Resultado de conexao: "+str(rc)) return #Callback - mensagem recebida do broker def on_message(client, userdata, msg): MensagemRecebida = str(msg.payload) print("[MSG RECEBIDA] Topico: "+msg.topic+" / Mensagem: "+MensagemRecebida) return #programa principal: #inicializa MQTT print("[STATUS] Inicializando MQTT...") client.on_connect = on_connect client.on_message = on_message client.connect(Broker, PortaBroker, KeepAliveBroker) #incializa tela DesenhaTela() #inicializa thread de mqtt ThMQTT = Thread(target=client.loop_forever) ThMQTT.start() janela.mainloop()
Execute o projeto com o seguinte comando:
python GUITKinter.py
Projeto em ação!
Com qualquer client MQTT (como o MQTTLens, por exemplo), conecte-se ao broker iot.eclipse.org e de subscribe no tópico que você utilizar (no caso do exemplo, MQTTDisplayRaspPi). Execute o projeto e pressione os botões. Você verá as mensagens dos botões pressionados (“BT1”, “BT2”, “BT3”, “BT4” e “BT5”), como os exibidos na figura 2. Essas mensagens serão usadas posteriormente para controlar o NodeMCU:
Próximos passos
No próximo post desta série, será mostrado como controlar o NodeMCU (até 5 deles), permitindo acionamento remoto dos mesmos de qualquer lugar do mundo em que haja conectividade à Internet!
Gostou do projeto usando Raspberry Pi para controlar o NodeMCU? Deixe seu comentário logo abaixo.