Use o Raspberry Pi para controlar o NodeMCU – Parte 1 Deixe um comentário

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:

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.

Exemplo de uso do 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:

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.

Exemplos de widgets com TKinter
Imagem: http://www.anderswallin.net/

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:

Usando Raspberry Pi para controlar o NodeMCU
Figura 1 – GUI do projeto

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:

Figura 2 - resultado (mensagens MQTT) ao pressionar os botões
Figura 2 – resultado (mensagens MQTT) ao pressionar os botões

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.

Faça seu comentário

Acesse sua conta e participe