IoT Com MicroPython e NodeMCU - Claudio Luis Vieira Oliveira, H
IoT Com MicroPython e NodeMCU - Claudio Luis Vieira Oliveira, H
MicroPython
e NodeMCU
Novatec
Copyright © 2022 da Novatec Editora Ltda.
Todos os direitos reservados e protegidos pela Lei 9.610 de 19/02/1998. É proibida a
reprodução desta obra, mesmo parcial, por qualquer processo, sem prévia autorização,
por escrito, do autor e da Editora.
Editor: Rubens Prates GRA20220125
Revisão gramatical: Tássia Carvalho
Capa: Wellinton Lenzi
Ilustração de capa: freepik.com
ISBN do impresso: 978-65-86057-86-7
ISBN do ebook: 978-65-86057-87-4
Histórico de impressões:
Fevereiro/2022 Primeira edição
Novatec Editora Ltda.
Rua Luís Antônio dos Santos 110
02460-000 – São Paulo, SP – Brasil
Tel.: +55 11 2959-6529
Email: [email protected]
Site: https://novatec.com.br
Twitter: twitter.com/novateceditora
Facebook: facebook.com/novatec
LinkedIn: linkedin.com/in/novatec
GRA20220125
À minha esposa Claudia, por tantos e tantos anos
de companheirismo, comunhão e amor.
À minha filha Franciele, com muito amor.
Aos meus pais Maria Creyde e Manoel,
pela dedicação e pelo amor à família.
Ao grande amigo Humberto,
pelos inúmeros projetos compartilhados.
– Cláudio Vieira Oliveira
À minha esposa Flavia, por seu incentivo e dedicação.
Aos meus pais Alberto e Célia, e meus irmãos Luis Gustavo e Pedro,
por sempre acreditarem em mim.
Ao meu grande amigo Cláudio, pela parceria nos projetos nesses anos.
E à minha princesa Betina, por tornar meus dias cada vez mais felizes.
– Humberto Zanetti
Arquivos para download
Imagens coloridas e códigos-fontes podem ser baixados em
https://novatec.com.br/livros/iot-micropython-nodemcu/.
Sumário
Sobre os autores
Apresentação
capítulo 1 NodeMCU e MicroPython
NodeMCU ESP8266-12 e ESP32
MicroPython
Programas utilizados
Atualização do firmware
Material necessário
capítulo 6 Displays
Display de cristal líquido (LCD) I2C
Termômetro digital – versão 1
Display de LEDs de 7 segmentos e 4 dígitos
Relógio digital
Relógio e termômetro digital
Display OLED
Exibição de imagens no display OLED
Termômetro digital – versão 2
capítulo 8 Node-RED
Olá Node-RED
JavaScript
Requisições HTTP
Interação com broker MQTT
Dashboard
Sistema de monitoramento
Armazenamento de dados no MongoDB
Comunicação entre serviços
NodeMCU e MicroPython
MicroPython
O MicroPython é uma portabilidade, otimizada para microcontroladores,
da linguagem de programação Python 3, que é amplamente utilizada no
desenvolvimento dos mais diversos tipos de aplicação. A linguagem de
programação Python é bastante poderosa, e seu principal atrativo é
possuir uma estrutura sintática bastante simples. Além disso, possui uma
infinidade de módulos desenvolvidos, uma comunidade muito atuante,
farta documentação e suporte a orientação a objetos. A implementação do
MicroPython utiliza-se das principais funcionalidades da linguagem
Python convencional, sendo uma ótima opção tanto para o iniciante
quanto para projetos profissionais.
O IDE (ambiente integrado de desenvolvimento) que será abordado nesta
obra é o Thonny (Figura 1.3), que está disponível gratuitamente em
https://thonny.org/. É uma ferramenta bastante completa e fácil de
utilizar, possibilitando uma ótima produtividade no desenvolvimento das
soluções. Porém, caso o leitor tenha familiaridade com outros ambientes
de desenvolvimento para o MicroPython, estes podem ser utilizados. No
livro, os códigos em MicroPython a serem executados no
microcontrolador estão indicados pelo símbolo z.
Figura 1.3: Janela principal do Thonny.
Programas utilizados
Além do ambiente de desenvolvimento Thonny, também devemos instalar
o Python, que está disponível gratuitamente em https://www.python.org/.
Também será necessária a instalação da ferramenta esptool, que consiste
em um conjunto de ferramentas que possibilitam a comunicação com o
bootloader dos módulos ESP8266 e ESP32 da Espressif Systems. O esptool
é fundamental para que seja possível instalar ou atualizar o firmware do
NodeMCU e está disponível gratuitamente em
https://github.com/espressif/esptool. Porém, a forma mais fácil de realizar
a instalação é usar o utilitário PIP do Python, que pode ser executado
pelo prompt de comandos do sistema operacional do seu computador,
conforme podemos notar a seguir.
pip install esptool
Entre muitas funcionalidades, podemos utilizar o esptool no terminal
para obter informações do módulo ESP presente na NodeMCU utilizando
o parâmetro flash_id da maneira indicada a seguir.
esptool.py flash_id
O comando deverá fornecer um resultado similar ao mostrado pela
Figura 1.4, em que podemos identificar o tipo do chip e o tamanho da
memória flash, entre outras informações.
Figura 1.4: Informações do módulo ESP.
Outro utilitário que será utilizado em alguns dos projetos desenvolvidos
neste livro é o Ampy, que possibilita manipular o sistema de arquivos do
NodeMCU e sua instalação também pode ser realizada utilizando o
utilitário PIP, conforme mostrado a seguir.
pip install adafruit-ampy
Atualização do firmware
Após a instalação do Python, Thonny e esptool, o primeiro passo para
utilizarmos o MicroPython no NodeMCU é realizar a instalação do
firmware; para isso, acesse https://micropython.org, entre na opção DOWNLOAD
e baixe o arquivo adequado para a sua placa NodeMCU.
Realize a conexão do seu NodeMCU a uma das portas USB do
computador. Execute o programa Thonny.
No Thonny, entre na opção Executar e escolha Select Interpreter. Na janela que
será aberta (Figura 1.5), clique na aba Interpretador e selecione MicroPython (ESP32)
ou MicroPython (ESP8266), conforme o modelo de NodeMCU que você irá usar.
Figura 1.5: Seleção do interpretador.
Em seguida, o quadro Details (Figura 1.6) será mostrado na mesma janela, a
porta será detectada automaticamente, mas é importante que o driver
apropriado à sua NodeMCU já tenha sido instalado. Clique no botão Install
or update firmware.
Na nova janela que foi apresentada (Figura 1.7), selecione a porta e o
arquivo que contém o firmware que foi baixado do site do MicroPython.
Por fim, clique no botão Install e aguarde o término do processo de
instalação ou atualização. É importante salientar que o Thonny utilizará o
esptool para realizar a instalação, dessa forma, é fundamental que a
ferramenta tenha sido previamente instalada.
Figura 1.6: Detalhes da conexão.
Material necessário
A seguir, apresentamos a relação do material que será empregado nos projetos contidos
nesta obra.
Material Quantidade
1 Módulo BMP180
1 Módulo BH1750
2 Sensores DS18B20
2 Chave táctil
1 Potenciômetro 10k Ohms
1 Sensor DHT11
1 LDR
Introdução ao Python
Variáveis e operadores
Em Python, uma variável, atributo ou até mesmo o valor de retorno de
um método não precisa ser previamente declarado. Conforme o valor
atribuído, a variável assume o tipo de dado necessário para armazenar
devidamente o conteúdo. Dessa forma, para armazenar um determinado
valor inteiro em uma variável, devemos simplesmente escrever:
x = 5
A linguagem Python também permite múltiplas atribuições, por exemplo:
x, y = 5, 10
Nesse caso, a variável x receberá o valor 5 enquanto y receberá o valor 10.
Quando desejamos atribuir um mesmo valor para diversas variáveis,
podemos especificar:
x = y = 5
Ou seja, nessa situação ambas as variáveis, isto é, x e y, receberão o valor 5.
Na Tabela 2.1, temos os principais operadores de atribuição, aritméticos,
relacionais e lógicos adotados em Python.
Tabela 2.1: Principais operadores da linguagem Python
Operador Símbolo Operador Símbolo
= ==
Atribuição Igual a
+ !=
Adição Diferente de
- >
Subtração Maior
* >=
Multiplicação Maior ou igual
/ <
Divisão Menor
% <=
Resto Menor ou igual
** and
Exponenciação E (And)
++ or
Incremento Ou (Or)
-- not
Decremento Não (Not)
Seleção e repetição
As estruturas de controle que permitem determinar a execução ou não de
determinado bloco de código também são responsáveis pela possibilidade
de repetição de um bloco de código.
A estrutura if else é utilizada para realizar a execução condicional de um
determinado bloco de código. No próximo exemplo, essa estrutura será
utilizada para determinar se um número é positivo. Observe que a
instrução else é opcional e, para este caso, não foi utilizada.
positivo.py
numero = int(input("Digite um número: "))
if numero > 0:
print ("O número é positivo.")
No próximo programa, a partir do exemplo do cálculo de idade
desenvolvido anteriormente, vamos acrescentar uma estrutura de seleção
para determinar se a pessoa é maior ou menor de idade.
maioridade.py
import time
Listas
As listas podem ser entendidas como conjuntos de itens. No trecho de
código-fonte a seguir, podemos notar que o simples uso de colchetes
permite especificar uma lista vazia.
numeros = []
No exemplo a seguir, notamos que é possível criar uma lista contendo um
conjunto de valores predefinidos.
numeros = [12, 18, 33, 2, 88]
Nesse caso, estamos declarando uma lista contendo cinco números
inteiros. Em uma lista, o primeiro elemento é referenciado pelo índice 0
(zero). Dessa forma, para acessar um elemento dentro de uma lista
devemos referenciar entre colchetes o índice da posição desejada,
conforme mostra o seguinte trecho de programa.
numeros = [12, 18, 33, 2, 88]
print(numeros[2])
Será exibido o elemento que se encontra no índice 2 da lista numeros que,
nesse exemplo, seria o valor 33.
Aplicando o conceito de lista, vamos a seguir criar um programa que, a
partir de cinco números reais digitados pelo usuário, deve calcular e exibir
o valor da média.
media.py
numeros = []
soma = 0.0
for i in range(5):
numeros.append(float(input("Número? ")))
soma = soma + numeros[i]
i = i + 1
media = soma / len(numeros)
print ("A media é %3.1f" % media)
Nesse exemplo, é importante salientar que podemos acrescentar
elementos à lista com o método append. Dessa forma, criamos uma lista
vazia e vamos inserindo os elementos durante a execução do programa.
Note também que a função len pode ser usada determinar o tamanho da
lista.
Funções
Na medida em que os programas vão se tornando mais complexos, torna-
se necessário estruturar o código em partes menores com funcionalidades
específicas. Além da organização, as funções também apresentam a
vantagem de permitir a reutilização de parte do programa, evitando assim
que um mesmo trecho do código tenha de ser escrito várias vezes.
Como exemplo, vamos considerar uma aplicação que implemente uma
calculadora básica, em que o usuário irá digitar dois números e escolher
qual das quatro operações aritméticas básicas será realizada. Em seguida,
o programa irá calcular e exibir o resultado com base na operação que foi
escolhida. Vamos resolver esse problema aplicando o conceito de funções.
Inicialmente vamos desenvolver apenas a função que fará a adição.
No trecho de programa a seguir, observe que criamos uma função que
recebe dois números como parâmetros, realiza o cálculo e retorna o valor
da soma.
def somar (valor1, valor2):
return (valor1 + valor2)
A seguir vamos desenvolver o conteúdo da rotina principal. Observe que,
quando o usuário digitar o sinal de adição, a função somar será executada,
conforme mostrado no código-fonte a seguir.
calculadora-somar.py
def somar (num1, num2):
return (num1 + num2)
fim = 'n'
while fim == 'n':
valor1 = float(input('Primeiro valor: '))
valor2 = float(input('Segundo valor: '))
opcao = input('Operação (+ - * /)? ')
if opcao == '+':
res = somar (valor1, valor2)
print ('A soma é ', res)
fim = input('Deseja encerrar o programa (s/n)? ')
A seguir temos o programa completo, em que foram implementadas as
funções que realizam a subtração, multiplicação e divisão.
calculadora-funcao.py
def somar (num1, num2):
return (num1 + num2)
fim = 'n'
while fim == 'n':
valor1 = float(input('Primeiro valor: '))
valor2 = float(input('Segundo valor: '))
opcao = input('Operação (+ - * /)? ')
if opcao == '+':
res = somar (valor1, valor2)
print ('A soma é ', res)
elif opcao == '-':
res = subtrair (valor1, valor2)
print ('A subtração é ', res)
elif opcao == '*':
res = multiplicar (valor1, valor2)
print ('A multiplicação é ', res)
elif opcao == '/':
res = dividir (valor1, valor2)
print ('A divisão é ', res)
else:
print ('Opção inválida!')
fim = input('Deseja encerrar o programa (s/n)? ')
capítulo 3
Saída digital
Este primeiro projeto não irá precisar da montagem de um circuito
eletrônico, pois usaremos o próprio LED que está na placa do NodeMCU
para ilustrar os conceitos iniciais sobre o uso da GPIO.
O NodeMCU (ESP8266) possui dois LEDs integrados na placa e que
estão conectados à GPIO2 e GPIO16. Esses LEDs são ativos (ligados) com
nível 0 (zero) e desligados com nível 1 (um). Por outro lado, o NodeMCU
(ESP12) apresenta apenas um LED que está conectado à GPIO2. Esse
LED é ligado com nível 1 e desligado com nível 0.
Selecione o interpretador Python adequado à sua placa. Após iniciar o Thonny,
entre na opção Executar do menu e escolha o item Select Interpreter.... Na janela que
será aberta, clique na aba Interpretador e escolha MicroPython (ESP8266) ou MicroPython
(ESP32) e pressione o botão Ok. Não se esqueça também de conectar a NodeMCU na
porta USB do computador.
Figura 3.2: Funções dos terminais do NodeMCU (ESP32).
No Thonny, implemente o código-fonte apresentado a seguir (pisca-pisca-
1.py).
z pisca-pisca-1.py
from time import sleep❶
from machine import Pin❷
led = Pin(2, Pin.OUT) ❸
while True:
led.value(1)
sleep(0.5) ❹
led.value(0)
sleep(0.5) ❺
Após implementar o programa, clique no botão Executar (ou pressione a
tecla F5) para executar o programa, e quando desejar finalizar clique em
Stop (ou pressione a combinação de teclas Ctrl F2).
Analisando o programa, em ❶ e ❷ importamos as funções que serão
usadas no programa. Em ❸ definimos que o terminal (pino) GPIO2 será
utilizado como saída (Pin.OUT), ou seja, será usado para acionar um
determinado elemento que pode ser, por exemplo, LED, motor ou relê,
entre outros tipos de atuadores. Em seguida, na estrutura de repetição
while alternamos o LED entre aceso e apagado com intervalos de
0,5 segundo. Note o uso da função sleep ( ❹ e ❺ ), que irá pausar o
processamento durante o intervalo especificado de tempo.
Podemos definir que atuador é qualquer tipo de circuito eletrônico ou módulo que
será controlado por um dispositivo microcontrolado, como é o caso do NodeMCU.
Agora podemos realizar a montagem do nosso primeiro circuito
eletrônico e utilizar o mesmo programa que acabamos de implementar
para acionar um LED externo à placa NodeMCU. Comece identificando e
separando os materiais relacionados a seguir.
Relação de materiais
1 NodeMCU (ESP8266 ou ESP32)
1 Resistor de 220 Ohms (vermelho, vermelho, marrom) ou 330 Ohms
(laranja, laranja, marrom)
1 LED (qualquer cor)
1 Protoboard
Cabos de ligação
Em seguida, realize a montagem da maneira indicada pela Figura 3.3, caso
esteja utilizando o NodeMCU (ESP8266).
Tratamento de exceções
Você provavelmente notou no programa anterior que, quando
interrompemos a execução do programa, o LED poderá permanecer tanto
aceso quanto apagado, pois não temos controle sobre o momento exato
em que o programa foi interrompido.
O tratamento de exceção é uma boa prática em programação, que permite
evitar que um programa termine de forma abrupta e, mesmo se ocorrer
algo inesperado, ele continuará em execução. Em Python existem as
instruções try e except, que permitem implementar o tratamento de
exceções. No try, colocamos as instruções a serem executadas e, no
except, o que deverá ser feito quando o bloco de instruções especificado
no try gerar algum tipo de erro ou for encerrado de forma intempestiva
por algum evento externo.
Figura 3.4: Ligação do LED à GPIO2 (NodeMCU-ESP32).
Com o objetivo de ilustrar esse conceito, vamos manter a montagem
usada no projeto anterior. O programa, basicamente, é o mesmo usado
anteriormente, porém tem o acréscimo do tratamento de exceções.
z pisca-pisca-2.py
from time import sleep
from machine import Pin
led=Pin(2, Pin.OUT)
try:
while True:
led.value(1)
sleep(0.5)
led.value(0)
sleep(0.5)
except KeyboardInterrupt: ❶
led.value(0) ❷
Observe que nesse caso o programa pode ser interrompido por um evento
externo que é o KeyboardInterrupt ( ❶ ). Esse evento é gerado no momento
que o usuário pressiona a combinação de teclas Ctrl C no shell ou pressiona
o botão Stop e, quando isso ocorrer, em ❷ o LED será desligado antes que
o programa termine.
Simulação de um semáforo
O intuito desse projeto é continuar abordando o conceito das portas do
NodeMCU sendo programadas como saída. Dessa maneira, iremos
realizar a montagem usando três LEDs que simularão o funcionamento
de um sinal de trânsito. Cada LED estará conectado a um pino da GPIO,
sendo controlado individualmente. Vamos começar o projeto
identificando os materiais relacionados a seguir.
Relação de materiais
1 NodeMCU (ESP8266 ou ESP32)
3 Resistores de 220 Ohms (vermelho, vermelho, marrom) ou 330 Ohms
(laranja, laranja, marrom)
1 LED vermelho
1 LED verde
1 LED amarelo
1 Protoboard
Cabos de ligação
Na Figura 3.5, temos a montagem do circuito eletrônico para o NodeMCU
(ESP8266), na qual os LEDs vermelho, amarelo e verde são conectados aos
pinos GPIO2, GPIO4 e GPIO5, respectivamente.
Na Figura 3.6, apresentamos o diagrama para a montagem do circuito
eletrônico para o NodeMCU (ESP32). Note que os LEDs serão conectados
da mesma maneira, ou seja, LED vermelho à GPIO2, LED amarelo à
GPIO4 e LED verde à GPIO5.
try:
while True: ⓬
led_vm.value(1)❻
sleep(0.5)
led_vd.value(1)❼
led_vm.value(0)❽
sleep(0.5)
led_am.value(1)❾
led_vd.value(0)❿
sleep(0.5)
led_am.value(0)⓫
except KeyboardInterrupt:
led_vm.value(0) ⓭
led_am.value(0) ⓮
led_vd.value(0) ⓯
Em ❻ o LED vermelho é aceso, ficando nessa condição por 0,5 segundo.
Em seguida, em ❼ o LED verde é aceso e o vermelho é apagado em ❽ ,
após meio segundo o LED amarelo é aceso em ❾ enquanto o LED verde é
apagado em ❿ . Novamente, após meio segundo em ⓫ o LED amarelo é
apagado e o ciclo recomeça em ⓬ devido ao uso da estrutura de repetição
while.
Quando a execução do programa for interrompida, todos os LEDs são
apagados, como podemos observar em ⓭, ⓮ e ⓯.
Figura 3.6: Circuito do semáforo (NodeMCU-ESP32).
Entrada digital
Os terminais da GPIO podem ser programados para atuarem como saída
digital, na qual acionam um determinado circuito ou módulo eletrônico,
ou como entrada digital, em que recebem dados de um determinado
módulo ou circuito eletrônico.
Circuitos ou módulos eletrônicos que enviam dados para um microcontrolador são
chamados de sensores.
Neste projeto vamos utilizar um pino da GPIO que estará conectado a
uma chave táctil para determinar se essa chave táctil está ou não
pressionada. Os materiais necessários para a montagem do projeto estão
relacionados a seguir.
Relação de materiais
1 NodeMCU (ESP8266 ou ESP32)
1 Resistor de 220 Ohms (vermelho, vermelho, marrom) ou 330 Ohms
(laranja, laranja, marrom)
1 Resistor de 10k Ohms (marrom, preto, laranja)
1 LED (qualquer cor)
1 Chave táctil
1 Protoboard
Cabos de ligação
Em seguida, realize a montagem da maneira indicada pela Figura 3.7, caso
esteja utilizando o NodeMCU (ESP8266).
Por outro lado, se estiver usando o NodeMCU (ESP32), adote a Figura 3.8
como referência.
No Thonny, implemente o código-fonte apresentado a seguir.
z botao-led-1.py
from machine import Pin
from time import sleep
LED RGB
O LED RGB é um componente eletrônico que apresenta em um mesmo
invólucro três LEDs, sendo um na cor vermelha, outro na cor verde e
outro na cor azul. Além disso, cada um dos LEDs pode ser controlado
individualmente. Existem dois tipos de LEDs RGB. Um é chamado de
cátodo comum, pois os três LEDs estão interligados pelo terminal
negativo (cátodo). Nesse tipo de LED RGB, cada um dos LEDs é ligado
pelo nível 1 e desligado com nível 0.
O outro tipo de LED RGB é o ânodo comum. Nesse caso, os três LEDs
estão interligados pelo terminal positivo (ânodo) e cada um dos LEDs é
ativado com nível 0 e desligado com nível 1.
Comece o projeto identificando e separando os seguintes materiais.
Relação de materiais
1 NodeMCU (ESP8266 ou ESP32)
1 Resistor de 220 Ohms (vermelho, vermelho, marrom) ou 330 Ohms
(laranja, laranja, marrom)
1 LED RGB (cátodo ou ânodo comuns)
1 Protoboard
Cabos de ligação
Em seguida, realize a montagem da maneira indicada pela Figura 3.11,
escolhendo a opção adequada ao tipo de LED RGB, caso esteja utilizando
o NodeMCU (ESP8266).
Adote como referência a Figura 3.12, escolhendo a opção adequada ao tipo
de LED RGB, caso utilize o NodeMCU (ESP32).
Figura 3.11: Conexões (NodeMCU-ESP8266).
Figura 3.12: Conexões (NodeMCU-ESP32).
Após a montagem do circuito eletrônico no Thonny, digite o seguinte
programa.
z led-rgb-random.py
from machine import Pin, PWM
from time import sleep
from random import getrandbits
ldr = ADC(0)
try:
while True:
valor_ldr = ldr.read()
valor = int(mapear(valor_ldr, 0, 1024, 0, 4))
print ("ADC: ", valor_ldr, "Mapear:", valor)
if valor == 0:
led_vd.value(0)
led_am.value(0)
led_vm.value(0)
elif valor == 1:
led_vd.value(1)
led_am.value(0)
led_vm.value(0)
elif valor == 2:
led_vd.value(1)
led_am.value(1)
led_vm.value(0)
elif valor == 3:
led_vd.value(1)
led_am.value(1)
led_vm.value(1)
sleep(0.1)
except:
led_vm.value(0)
led_am.value(0)
led_vd.value(0)
A montagem para o NodeMCU (ESP32) é mostrada na Figura 3.16.
Figura 3.16: Circuito eletrônico com o NodeMCU (ESP32).
Na sequência, apresentamos o programa específico para o NodeMCU
(ESP32).
z adc-ldr-nivel-esp32.py
from machine import Pin, ADC
from time import sleep
ldr = ADC(Pin(34))
ldr.atten(ADC.ATTN_11DB)
try:
while True:
valor_ldr = ldr.read()
valor = int(mapear(valor_ldr, 0, 4095, 0, 4))
print ("ADC: ", valor_ldr, "Mapear:", valor)
if valor == 0:
led_vd.value(0)
led_am.value(0)
led_vm.value(0)
elif valor == 1:
led_vd.value(1)
led_am.value(0)
led_vm.value(0)
elif valor == 2:
led_vd.value(1)
led_am.value(1)
led_vm.value(0)
elif valor == 3:
led_vd.value(1)
led_am.value(1)
led_vm.value(1)
sleep(0.1)
except:
led_vm.value(0)
led_am.value(0)
led_vd.value(0)
Analisando ambos os programas, criamos em ❶ e ❷ a função mapear. Essa
função basicamente realiza uma regra de três convertendo o valor obtido
do ADC, que no NodeMCU (ESP8266) é de 0 a 1023 e no NodeMCU
(ESP32) é de 0 a 4095, para a quantidade de LEDs que devem ser acesos.
def obterTemperatura(termistor): ❶
tempK = log(10000.0 * (1024.0 / termistor - 1)) ❷
tempK = 1 / (0.001129148 + (0.000234125
+ (0.0000000876741 * tempK * tempK )) * tempK)
tempC = tempK - 273.15
return tempC
adc = ADC(0)
while True:
temp = obterTemperatura(adc.read())
print ("Temperatura: ", round(temp, 1), "°C")
sleep(1.0)
Em seguida, na Figura 3.18 mostramos o circuito eletrônico para o
NodeMCU (ESP32).
Figura 3.18: Circuito eletrônico com o NodeMCU (ESP32).
Implemente o programa para o NodeMCU (ESP32).
z adc-termistor-esp32.py
from machine import Pin, ADC
from time import sleep
from math import log
def obterTemperatura(termistor): ❶
tempK = log(10000.0 * (4096.0 / termistor - 1)) ❷
tempK = 1 / (0.001129148 + (0.000234125
+ (0.0000000876741 * tempK * tempK )) * tempK)
tempC = tempK - 273.15
return tempC
adc = ADC(Pin(34))
adc.atten(ADC.ATTN_11DB)
while True:
temp = obterTemperatura(adc.read())
print ("Temperatura: ", round(temp, 1), "°C")
sleep(1.0)
Em ❶ de ambos os programas, definimos a função obterTemperatura. Essa
função recebe como parâmetro o valor obtido pelo ADC e converte para
Kelvin e, em seguida, para graus Celsius. Observe em ❷ que devemos
colocar na fórmula a resolução do ADC. Então, no caso do NodeMCU
(ESP8266) colocamos o valor 1024,0 e no NodeMCU (ESP32) o valor
4096,0.
capítulo 4
Orientação a objetos
Classe LED
Aplicando os conceitos iniciais de Orientação a Objetos (objetos, classes,
atributos e métodos), é possível criar uma abstração do componente
eletrônico LED, que foi abordado no capítulo anterior, representando uma
classe que define suas características e comportamentos. Mas, em
primeiro lugar, comece identificando e separando os materiais
relacionados a seguir.
Relação de materiais
1 NodeMCU (ESP8266 ou ESP32)
1 Resistor de 220 Ohms (vermelho, vermelho, marrom) ou 330 Ohms
(laranja, laranja, marrom)
1 LED (qualquer cor)
1 Protoboard
Cabos de ligação
Em seguida, realize a montagem da maneira indicada pela Figura 4.1, caso
esteja utilizando o NodeMCU (ESP8266).
Figura 4.1: Ligação do LED à GPIO2 (NodeMCU-ESP8266).
Por outro lado, se estiver usando o NodeMCU (ESP32), adote a Figura 4.2
como referência.
Agora podemos passar para a criação da classe Led. O primeiro passo
observamos em ❶ e consiste em realizar a definição da classe. Em ❷ é
realizada a definição do método construtor. A partir da palavra reservada
self, em ❸ e ❹ definimos os atributos da classe LED que, neste
programa, serão __pwm, que representa o pino onde o LED foi conectado, e
__estado, isto é, se o LED está ligado ou desligado.
A palavra-reservada self é utilizada para referenciar um atributo ou
método da própria classe evitando ambiguidade em relação aos
parâmetros ou variáveis declaradas dentro de um método da classe.
Figura 4.2: Ligação do LED à GPIO2 (NodeMCU-ESP32).
Observe também que na definição dos atributos realizada em ❸ e ❹ foi
colocado __ (underscore) na frente do nome do atributo. Ele define que o
atributo é privado e, dessa forma, pode ser acessado apenas dentro da
classe. Esse conceito é chamado de encapsulamento e consiste na
separação de características internas e externas de um determinado
objeto. Esse recurso é comumente utilizado para evitar o acesso direto aos
atributos desse objeto, permitindo, dessa forma, que apenas os métodos
consigam alterar esses atributos.
z led.py
from time import sleep
from machine import Pin, PWM
class Led(object): ❶
def __init__(self, pino): ❷
self.__pwm = PWM(Pin(pino), freq=20000, duty=0) ❸
self.__estado = False ❹
def ligar(self):
self.__pwm.duty(1023)
def desligar(self):
self.__pwm.duty(0)
led = Led(2) ❶
led.piscar(0.2, 5) ❷
Note no próximo código-fonte, em ❶ e ❷ , que podemos definir usar o
nome dos parâmetros na chamada de um método. Essa técnica deixa o
programa mais legível, facilitando o entendimento.
z pisca-pisca-2-oo.py
from led import Led
led = Led(pino=2) ❶
led.piscar(tempo=0.2, vezes=5) ❷
Abstração da chave táctil
Neste projeto vamos implementar os detalhes de funcionamento da chave
táctil em uma classe chamada Botao. Os materiais necessários para a
montagem do projeto estão relacionados a seguir.
Relação de materiais
1 NodeMCU (ESP8266 ou ESP32)
1 Resistor de 220 Ohms (vermelho, vermelho, marrom) ou 330 Ohms
(laranja, laranja, marrom)
1 Resistor de 10k Ohms (marrom, preto, laranja)
1 LED (qualquer cor)
1 Chave táctil
1 Protoboard
Cabos de ligação
Em seguida, realize a montagem da maneira indicada pela Figura 4.3, caso
esteja utilizando o NodeMCU (ESP8266).
Figura 4.3: Montagem do circuito (NodeMCU-ESP8266).
Por outro lado, se estiver usando o NodeMCU (ESP32), adote a Figura 4.4
como referência.
A classe terá os atributos relacionados ao pino utilizado para ligar o botão
(__botao), o estado atual (__estado), isto é, se o botão está ou não
pressionado, e também o estado anterior (__anterior), conforme podemos
observar em ❶ , ❷ e ❸ . O método pressionado irá retornar o estado do
botão no momento. Por outro lado, o método estado irá manter o estado
do botão até que um novo pressionamento ocorra.
class Botao(object):
def __init__(self, pino):
self.__botao = Pin(pino, Pin.IN) ❶
self.__estado = 0 ❷
self.__anterior = 0❸
def pressionado(self):
return self.__botao.value()
led = Led(5)
botao = Botao(4)
led = Led(5)
botao = Botao(4)
try:
while True:
if botao.estado() == 1: ❶
led.ligar()
else:
led.desligar()
except KeyboardInterrupt:
led.desligar()
Analisando o código-fonte, em ❶ temos o uso do método estado. Como
não passamos um parâmetro para o método, ele assumirá o valor padrão
que é dois, ou seja, atuará com dois estados possíveis (0 e 1).
class LedRGB(object):
VERMELHO = 0
VERDE = 1
AZUL = 2
CATODO_COMUM = 3
ANODO_COMUM = 4
class ConversorAD(object):
def __init__(self, pino=0):
if os.uname()[0] == 'esp8266':
self.__adc = ADC(0)
self._ADC_MAX = 1024
else:
self.__adc = ADC(Pin(pino))
self.__adc.atten(ADC.ATTN_11DB)
self._ADC_MAX = 4096
def valor(self):
return self.__adc.read()
Analisando o programa, podemos ver que no MicroPython o conceito de
encapsulamento é implementado pelo uso do caractere _ antes do nome
de atributos e métodos. Quando definimos o nome sem usar o caractere _
esse atributo ou método será público. Por exemplo, o método chamado
valor é público e pode ser acessível por métodos implementados em
qualquer outra classe do programa.
Quanto usamos o caractere _ uma única vez na frente do nome do
atributo ou método, por exemplo o atributo _ADC_MAX, este será protegido e
pode ser acessível por métodos da própria classe ou das subclasses.
Por outro lado, se o caractere _ é usado duas vezes antes do nome do
atributo ou método, por exemplo o atributo __adc, este passa a ser privado,
podendo ser acessado exclusivamente por métodos implementados dentro
da própria classe.
Prosseguindo com a aplicação do conceito de herança, vamos
implementar uma subclasse para representar o potenciômetro. Em ❶
ocorre a herança. Observe que a classe Potenciometro, que está sendo
desenvolvida, terá a classe ConversorAD como superclasse. No método
construtor declarado em ❷ , observe em ❸ que usamos o método super
para executar o construtor da superclasse, que é onde ocorre a
configuração do ADC. Em ❹ é possível notar o uso do método valor e do
atributo ADC_MAX, herdados da superclasse.
z potenciometro.py
from conversorad import ConversorAD
class Potenciometro(ConversorAD): ❶
def __init__(self, pino=0): ❷
super().__init__(pino) ❸
def __mapear(self, x, in_min, in_max, out_min, out_max):
return (x - in_min) * (out_max - out_min) / (in_max - in_min) +
out_min
# NodeMCU (ESP8266)
pot = Potenciometro() ❶
# NodeMCU (ESP32)
# pot = Potenciometro(34) ❷
while True:
print (pot.valor(), pot.escalonar(0, 10)) ❸
sleep(0.2)
Ao executar o programa, observe que são mostrados no shell do Thonny
os valores obtidos pelo ADC a partir do método valor e em uma escala de
0 a 10 devido ao uso do método escalonar mostrado em ❸. Note que esses
valores são modificados à medida que o eixo do potenciômetro é girado.
Prosseguindo com a aplicação do conceito de herança, vamos criar uma
subclasse para uso do termístor. Os materiais necessários para a
montagem do circuito eletrônico são especificados a seguir.
Relação de materiais
1 NodeMCU (ESP8266 ou ESP32)
1 Resistor de 10k Ohms (marrom, preto, laranja)
1 Termístor NTC 10k Ohms
1 Protoboard
Cabos de ligação
Na Figura 4.9, temos o circuito eletrônico para o NodeMCU (ESP8266).
Figura 4.9: Circuito eletrônico com o NodeMCU (ESP8266).
Em seguida, na Figura 4.10, mostramos o circuito eletrônico para o
NodeMCU (ESP32).
Figura 4.10: Circuito eletrônico com o NodeMCU (ESP32).
Implemente a classe Termistor como sendo uma subclasse de ConversorAD
conforme mostrado em ❶ . No método construtor observe em ❷ que
usamos o método super para executar o construtor da superclasse, que é
onde irá ocorrer a configuração do ADC.
z termistor.py
from conversorad import ConversorAD
from math import log
class Termistor(ConversorAD): ❶
def __init__(self, pino=0):
super().__init__(pino) ❷
def temperatura(self):
tempK = log(10000.0 * (self._ADC_MAX / self.__adc.read() - 1))
tempK = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * tempK
* tempK)) * tempK)
tempC = tempK - 273.15
return tempC
Use o utilitário Ampy para copiar o arquivo criado (termistor.py) e o
arquivo que contém a classe ConversorAD (conversorad.py) para o sistema de
arquivos do NodeMCU, conforme mostrado a seguir. Não se esqueça de
trocar a porta usada no exemplo (com6) pela porta na qual o NodeMCU
está conectado ao seu computador.
ampy --port com6 put conversorad.py
ampy --port com6 put termistor.py
Em seguida, implemente o programa que irá usar a classe Termistor para
obter os dados de temperatura.
z termistor-oo.py
from termistor import Termistor
from time import sleep
# NodeMCU (ESP8266):
termistor = Termistor() ❶
# NodeMCU (ESP32):
# termistor = Termistor(34) ❷
while True:
print('Temperatura: ', round(termistor.temperatura(), 1), '°C')
sleep(5.0)
Note que em ❶ ou ❷ é realizada a criação do objeto de acordo com o
modelo usado de NodeMCU. Caso esteja usando o ESP8266, não use
comentário (#) em ❶ e coloque o comentário em ❷ . Por outro lado, se
estiver usando o ESP32, coloque o comentário em ❶ deixando ❷ sem
comentário.
class Display7Segmentos(object):
CATODO_COMUM = 0
ANODO_COMUM = 1
def apagar(self):
for seg in self.__segmentos:
seg.value(self.__tipo)
# NodeMCU (ESP8266):
ds = Display7Segmentos([15, 13, 12, 14, 2, 0, 4], 5,
Display7Segmentos.CATODO_COMUM) ❶
# NodeMCU (ESP32):
# ds = Display7Segmentos([15, 2, 4, 5, 18, 19, 21], 22,
Display7Segmentos.CATODO_COMUM) ❷
for i in range(16):
ds.exibir(i)
sleep(1.0)
ds.apagar()
Neste projeto, vamos unir os conceitos abordados sobre o display de
LEDs com sete segmentos e a chave táctil para criar um contador
hexadecimal no qual o dígito a ser exibido é alterado sempre que a chave
táctil é pressionada. Na Figura 4.16, mostraremos o circuito eletrônico
considerando o uso do NodeMCU (ESP8266) e um display com cátodo
comum. Porém, como já apresentamos anteriormente, o projeto pode ser
facilmente alterado para funcionar com o NodeMCU (ESP32) e outro tipo
de display.
Use o Ampy para copiar os arquivos necessários para o NodeMCU,
conforme mostrado a seguir. Não se esqueça de trocar a porta usada no
exemplo (com6) pela porta na qual o NodeMCU está conectado ao seu
computador.
ampy --port com6 put display7segmentos.py
ampy --port com6 put botao.py
O programa em MicroPython irá inicialmente criar os objetos para o
display e para o botão. Perceba que em ❶ e ❷ ou ❸ e ❹ você deve utilizar
a criação do objeto de acordo com seu modelo de NodeMCU (ESP8266
ou ESP32), o tipo de display de LED e o terminal no qual o botão foi
conectado. Em relação ao objeto que representa a chave táctil, iremos usar
em ❺ o método estado para obter o valor que será incrementado cada vez
que a chave táctil for pressionada.
# NodeMCU (ESP8266):
ds = Display7Segmentos([15, 13, 12, 14, 2, 0, 4], 5,
Display7Segmentos.CATODO_COMUM) ❶
botao = Botao(16) ❷
# NodeMCU (ESP32):
# ds = Display7Segmentos([15, 2, 4, 5, 18, 19, 21], 22,
Display7Segmentos.CATODO_COMUM) ❸
# botao = Botao(23) ❹
try:
while True:
ds.exibir(botao.estado(16)) ❺
except KeyboardInterrupt:
ds.apagar()
Outra possibilidade é apresentada no próximo projeto, em que vamos
unir os conceitos abordados sobre o display de LEDs com 7 segmentos e
o potenciômetro para criar um contador decimal (0 até 9) no qual o dígito
a ser exibido é definido em função da posição do eixo do potenciômetro.
Na Figura 4.17, iremos mostrar a montagem do circuito eletrônico
considerando o uso do NodeMCU (ESP8266) e um display com cátodo
comum. Porém, o projeto pode ser facilmente alterado para funcionar
com o NodeMCU (ESP32) e outro tipo de display.
Figura 4.17: NodeMCU (ESP8266) e display com cátodo comum.
Use o Ampy para copiar os arquivos necessários para o NodeMCU,
conforme mostramos a seguir. Não se esqueça de trocar a porta usada no
exemplo (com6) pela porta na qual o NodeMCU está conectado ao seu
computador.
ampy --port com6 put display7segmentos.py
ampy --port com6 put conversorad.py
ampy --port com6 put potenciometro.py
O programa, que será implementado no Thonny, irá inicialmente criar os
objetos para o display e para o potenciômetro. Perceba que em ❶ , ❷ , ❸ e
❹ você deve utilizar a criação do objeto de acordo com seu modelo de
NodeMCU (ESP8266 ou ESP32) e tipo de display de LED. Em relação ao
objeto que representa o potenciômetro, note em ❺ que vamos usar o
método escalonar para obter o valor que será incrementado ou
decrementado conforme o giro do eixo.
z display-3-oo.py
from display7segmentos import Display7Segmentos
from potenciometro import Potenciometro
from time import sleep
# NodeMCU (ESP8266):
ds = Display7Segmentos([15, 13, 12, 14, 2, 0, 4], 5,
Display7Segmentos.CATODO_COMUM) ❶
pot = Potenciometro() ❷
# NodeMCU (ESP32):
# ds = Display7Segmentos([15, 2, 4, 5, 18, 19, 21], 22,
Display7Segmentos.CATODO_COMUM) ❸
# pot = Potenciometro(32) ❹
try:
while True:
ds.exibir(pot.escalonar(0, 9)) ❺
sleep(0.1)
except KeyboardInterrupt:
ds.apagar()
capítulo 5
Comunicação em rede
Wi-Fi integrado
Uma das melhores características do NodeMCU é possuir Wi-Fi
integrado. A partir dele é possível atuar como um access point, em que
outros dispositivos podem se conectar ou atuar como cliente se conectado
a um access point já existente. Não é preciso realizar qualquer montagem
de circuito eletrônico para os comandos mostrados a seguir, visto que
vamos explorar o Wi-Fi integrado utilizando o próprio shell do
NodeMCU e, posteriormente, nos projetos seguintes vamos integrando os
conceitos abordados aplicando-os a projetos completos.
A biblioteca network implementa o conjunto de funções relativas à rede.
Então o primeiro passo consiste em carregá-la a partir do uso da
instrução import.
z import network
Em seguida, é importante definir como o NodeMCU irá atuar, ou seja,
como estação (network.STA_IF).
z wifi = network.WLAN(network.STA_IF)
Ou como um access point (network.AP_IF).
z wifi = network.WLAN(network.AP_IF)
Para o exemplo que vamos desenvolver na sequência, configure como
estação. Em primeiro lugar, ative a interface de comunicação Wi-Fi, use o
método active e depois o método scan irá identificar os access points que
estão disponíveis.
z wifi.active(True)
wifi.scan()
Na Figura 5.1, mostramos um exemplo do resultado exibido após a
execução do método scan.
Soquetes
Podemos definir, de maneira simplista, que um soquete é uma forma de
estabelecer uma comunicação regrada entre dois aplicativos interligados
por uma rede. Um exemplo bastante comum para entendermos o conceito
de soquete é pensarmos no modelo de comunicação entre um servidor e
um navegador web, como quando requisitamos uma página dentro de um
website. Em programação, um soquete é uma abstração dos serviços e
protocolos utilizados para a comunicação entre dois ou mais sistemas.
Com o intuito de visualizarmos o conceito na prática, vamos implementar
em MicroPython um servidor web (HTTP) e depois usamos um
navegador para enviar uma requisição e, em seguida, receber os dados
enviados pelo servidor.
z http-ola.py
import network
import usocket as socket
import gc
gc.collect()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ❺
s.bind(('', 80)) ❻
s.listen(5) ❼
try:
while True: ❽
conexao, endereco = s.accept()
print('Conexao de %s' % str(endereco))
requisicao = conexao.recv(1024)
requisicao = str(requisicao)
print('Conteudo = %s' % requisicao)
conexao.send('HTTP/1.1 200 OK\n') ❾
conexao.send('Content-Type: text/html\n') ❿
conexao.send('Connection: close\n\n') ⓫
conexao.sendall(html) ⓬
conexao.close() ⓭
except KeyboardInterrupt:
s.close()
estacao.active(False)
Analisando o código-fonte, em ❶ definimos a variável que irá conter a
página HTML que será enviada pelo servidor.
Uma ótima referência para conhecer mais sobre HTML é a Mozilla Developer
Network, acesse: https://developer.mozilla.org/pt-BR/docs/Web/HTML
Em seguida, entre em ❷ , ❸ e ❹ para realizarmos a conexão a um access
point, conforme já detalhamos anteriormente.
Em ❺ , criamos um soquete e, em ❻ , realizamos a ligação do soquete à
porta TCP 80, que é a porta padrão usada pelo protocolo HTTP. Em ❼ ,
colocamos a porta associada do soquete em modo de escuta, em que o
parâmetro passado para o método listen, que neste exemplo é 5, indica o
máximo de solicitações de conexões simultâneas.
Em ❽ , o programa entra em um laço, em que aceita pedidos de conexão,
processa a requisição realizada pelo cliente e envia o conteúdo. O
conteúdo está dividido em duas partes, então em ❾ , ❿ e ⓫ é enviado o
cabeçalho e, posteriormente, em ⓬ , as instruções HTML. Em ⓭ , a
conexão com o cliente é encerrada, mas note que o soquete permanece
ativo, aguardando novas conexões.
Após executar o programa no Thonny, aguarde o processo de conexão ao
access point. Em seguida, o endereço atribuído ao NodeMCU será
exibido no shell, conforme é possível notar na Figura 5.2.
Figura 5.2: Endereço IP atribuído ao NodeMCU.
Na sequência, digite o endereço IP do NodeMCU no navegador web de
sua preferência e a página deverá ser carregada (Figura 5.3).
estacao = network.WLAN(network.STA_IF)
estacao.active(True)
estacao.connect('ap', 'senha')
while estacao.isconnected() == False:
pass
print('Conexao realizada.')
print(estacao.ifconfig())
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 80))
s.listen(5)
try:
while True:
conexao, endereco = s.accept()
print("Conexao de %s" % str(endereco))
requisicao = conexao.recv(1024)
requisicao = str(requisicao)
print("Conteudo = %s" % requisicao)
if requisicao.find('GET /?LED=ON') != -1: ❷
LED.value(1) ❸
if requisicao.find('GET /?LED=OFF') != -1: ❹
LED.value(0) ❺
conexao.send('HTTP/1.1 200 OK\n')
conexao.send('Content-Type: text/html\n')
conexao.send('Connection: close\n\n')
conexao.sendall(html)
conexao.close()
except KeyboardInterrupt:
s.close()
estacao.active(False)
Figura 5.5: Circuito eletrônico (NodeMCU-ESP32).
Execute o programa no Thonny e acesse pelo navegador o endereço IP do
NodeMCU. Deverá ser mostrada uma página similar à da Figura 5.6.
Clicando nos botões Ligar e Desligar, o LED deve acender ou apagar.
Figura 5.6: Página para controle do LED.
Quando desenvolvemos aplicações para a Internet, devemos considerar a
vasta gama de dispositivos que hoje acessam a rede. Vamos usar uma
biblioteca de funções JavaScript muito utilizada chamada jQuery. Foi
criada com o objetivo de simplificar a seleção de elementos, a criação de
efeitos visuais e a manipulação de eventos. É uma ferramenta bastante
versátil, possibilitando inclusive a criação de plugins.
Além da jQuery, usaremos o Bootstrap, que é um framework HTML, CSS
e JavaScript para desenvolvimento de interfaces responsivas para
aplicações Internet e que, dessa forma, irão funcionar adequadamente
tanto em computadores como em dispositivos móveis.
Esse programa implementará um servidor HTTP que irá obter uma
página HTML, gravada no sistema de arquivos do NodeMCU, e enviará
para o navegador. Essa página usa o Bootstrap para criar uma interface
visualmente mais sofisticada e responsiva.
O foco deste livro é o MicroPython, mas conhecer HTML, CSS, JavaScript, AJAX e
frameworks, como jQuery e Bootstrap, ajuda a criar aplicações IoT amigáveis e
robustas. Há muitos tutoriais na Internet que abordam essas tecnologias.
O circuito eletrônico é o mesmo usado no projeto anterior, ou seja, um
LED conectado ao pino GPIO5 do NodeMCU. A página HTML que será
usada no projeto é mostrada a seguir.
http-led-bootstrap.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>NodeMCU IoT</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-
scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/
bootstrap/4.0.0-beta.2/css/bootstrap.min.css">
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.mi
n.js">
</script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.6/umd/
popper.min.js">
</script>
<script
src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-
beta.2/js/bootstrap.min.js">
</script>
<script>
$(document).ready(function(){
$("#on").click(function(){
$.ajax({url: "/led/1", success: function(result){
$('#on').removeClass('btn-success').addClass('btn-
default');
$('#off').removeClass('btn-default').addClass('btn-
danger');
}});❶
});
$("#off").click(function(){
$.ajax({url: "/led/0", success: function(result){
$('#on').removeClass('btn-default').addClass('btn-
success');
$('#off').removeClass('btn-danger').addClass('btn-
default');
}}); ❷
});
});
</script>
</head>
<body>
<div class="container">
<h1>NodeMCU IoT</h1>
Controle do LED<br /><br />
<div class="btn-group">
<button id="on" type="button" class="btn btn-success btn-
lg">Acender
</button><br><br>
<button id="off" type="button" class="btn btn-default btn-
lg">Apagar
</button>
</div>
</div>
</body>
</html>
Observe em ❶ e ❷ que, no evento clique dos botões Acender e Apagar, usamos
requisições AJAX (Asynchronous Javascript and XML).
Podemos entender que AJAX é uma forma de realizar uma requisição ao
servidor em que este apenas retornará dados, e não uma página HTML
completa com marcadores e conteúdo, muito usado em aplicações como
redes sociais na alimentação de “feeds”, por exemplo. Dessa maneira, é
possível carregar apenas uma parte de uma página, aumentando o
desempenho e possibilitando uma maior interação. O jQuery facilita a
implementação de AJAX, oferecendo um método específico para isso.
Após criar o arquivo HTML, use o utilitário Ampy para copiá-lo para o
sistema de arquivos do NodeMCU, conforme mostrado a seguir. Apenas
tenha a precaução de trocar a porta usada no exemplo (com6) pela porta na
qual o NodeMCU está conectado ao seu computador.
ampy --port com6 put http-led-bootstrap.html
No Thonny, implemente o código-fonte do servidor HTML. Note que ele
é muito similar ao utilizado no projeto anterior. A principal diferença está
na função obter_arquivo, declarada em ❶ . Essa função obtém e retorna o
conteúdo de um arquivo existente no sistema de arquivos do NodeMCU.
Observe em ❷
que usamos a função para obter o conteúdo da página
http-led-bootstrap.html, desenvolvida anteriormente e copiada para o
NodeMCU pelo do Ampy.
z http-led-2.py
import network
import usocket as socket
import machine
import gc
gc.collect()
def obter_arquivo(arquivo): ❶
conteudo = ''
a = open(arquivo, 'rb')
conteudo = a.read()
a.close()
return conteudo
estacao = network.WLAN(network.STA_IF)
estacao.active(True)
estacao.connect('ap', 'senha')
while estacao.isconnected() == False:
pass
print('Conexao realizada.')
print(estacao.ifconfig())
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 80))
s.listen(5)
try:
while True:
conexao, endereco = s.accept()
print('Conexao de %s' % str(endereco))
requisicao = conexao.recv(1024)
requisicao = str(requisicao)
print('Conteudo = %s' % requisicao)
if requisicao.find('/led/1') != -1:
print('Acender')
LED.value(1)
elif requisicao.find('/led/0') != -1:
print('Apagar')
LED.value(0)
else:
LED.value(0)
html = obter_arquivo('http-led-bootstrap.html') ❷
conexao.send('HTTP/1.1 200 OK\n')
conexao.send('Content-Type: text/html\n')
conexao.send('Connection: close\n\n')
conexao.sendall(html)
conexao.close()
except KeyboardInterrupt:
s.close()
estacao.active(False)
Após executar o arquivo no Thonny, use o navegador para acessar o
servidor pelo endereço IP que foi atribuído ao NodeMCU (Figura 5.7).
rtc = RTC()
estacao = network.WLAN(network.STA_IF)
estacao.active(True)
estacao.connect('ap', 'senha')
while estacao.isconnected() == False:
pass
print('Conexao realizada.')
print(estacao.ifconfig())
data_hora_atual = utime.ticks_ms()
url = "http://worldtimeapi.org/api/timezone/
America/Sao_Paulo"
resposta = urequests.get(url) ❶
if resposta.status_code == 200:
print("JSON:", resposta.text)
json = resposta.json() ❷
agora = str(json["datetime"]) ❸
ano = int(agora[0:4]) ❹
mês = int(agora[5:7]) ❺
dia = int(agora[8:10])❻
hora = int(agora[11:13]) ❼
minuto = int(agora[14:16]) ❽
segundo = int(agora[17:19]) ❾
subsegundo = int(round(int(agora[20:26]) / 10000)) ❿
# Atualizar o RTC do NodeMCU
rtc.datetime((ano, mes, dia, 0, hora, minuto, segundo, subsegundo))
⓫
data_hora_atual = utime.ticks_ms()
print('RTC atualizado.')
else:
data_hora_atual = utime.ticks_ms()
$(document).ready(function() {
atualizar();
});
</script>
</head>
<body>
<div class="container">
<div class="jumbotron">
<h1>Temperatura</h1>
</div>
<h1>
<img src="images/termometro.png" align="middle"> ❷
<span id="temperatura"></span>°C
</h1>
</div>
</body>
</html>
Em seguida, use o utilitário Ampy para copiar os arquivos http-
termistor.html e termometro.png para o sistema de arquivos do NodeMCU,
conforme mostrado a seguir. Apenas tenha a precaução de trocar a porta
usada no exemplo (com6) pela porta na qual o NodeMCU está conectado
ao seu computador.
ampy --port com6 put http-termistor.html
ampy --port com6 put termometro.png
No ambiente de desenvolvimento Thonny, implemente o código-fonte
apresentado a seguir.
z http-termistor.py
import network
import usocket as socket
from machine import ADC, Pin
from math import log
import os
import gc
gc.collect()
if os.uname()[0] == 'esp8266': ❶
adc = ADC(0) ❷
else: ❸
adc = ADC(Pin(34)) ❹
adc.atten(ADC.ATTN_11DB) ❺
def obter_temperatura(termistor):
if os.uname()[0] == 'esp8266':
fator = 1024.0
else:
fator = 4096.0
tempK = log(10000.0 * (fator / termistor - 1))
tempK = 1 / (0.001129148 + (0.000234125
+ (0.0000000876741 * tempK * tempK )) * tempK)
tempC = tempK - 273.15
return tempC
def obter_arquivo(arquivo):
conteudo = ''
a = open(arquivo, 'rb')
conteudo = a.read()
a.close()
return conteudo
estacao = network.WLAN(network.STA_IF)
estacao.active(True)
estacao.connect('CasaClaudio1', 'c1l2v6o8')
while estacao.isconnected() == False:
pass
print('Conexao realizada.')
print(estacao.ifconfig())
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 80))
s.listen(5)
try:
while True:
conexao, endereco = s.accept()
print('Conexao de %s' % str(endereco))
requisicao = conexao.recv(1024) ❻
requisicao = str(requisicao)
print('Conteudo = %s' % requisicao)
if requisicao.find('obter/temperatura') != -1:
temperatura = obter_temperatura(adc.read()) ❼
html = str(round(temperatura, 1))
elif requisicao.find('/termometro.png') != -1:
html = obter_arquivo('termometro.png')
elif requisicao.find('/') != -1:
html = obter_arquivo('http-termistor.html')
conexao.send('HTTP/1.1 200 OK\n')
conexao.send('Content-Type: text/html\n')
conexao.send('Connection: close\n\n')
conexao.sendall(html) ❽
conexao.close()
except KeyboardInterrupt:
s.close()
estacao.active(False)
Após executar o programa no Thonny, aguarde o processo de conexão ao
access point. Em seguida, o endereço atribuído ao NodeMCU será
exibido no shell, conforme é possível notar na Figura 5.11.
Serviços de rede
No programa a seguir, vamos nos conectar a um access point, para ter
acesso à Internet, obter a data e hora de um servidor NTP (Network Time
Protocol) e sincronizar o relógio interno do NodeMCU (RTC).
O NTP é um protocolo para sincronização dos relógios dos computadores
baseado no protocolo TCP/IP, pela porta 123. É utilizado para sincronizar os
relógios de computadores, smartphones ou qualquer outro dispositivo que se
conecte à rede.
Não é preciso realizar qualquer montagem de circuito eletrônico para o
NodeMCU, pois apenas usaremos o comando print para exibir as
informações no shell do ambiente de desenvolvimento Thonny. Assim
sendo, implemente o programa apresentado a seguir.
z ntp-data-hora-1.py
import network
import ntptime
import machine
import time
wifi = network.WLAN(network.STA_IF) ❶
wifi.active(True) ❷
wifi.connect('ap', 'senha') ❸
while wifi.isconnected() == False:
machine.idle()
print('Conexao realizada:', wifi.ifconfig())
ntptime.settime() ❹
agora = time.localtime() ❺
print ('Data e hora do servidor NTP:', agora)
wifi.active(False)
Observe que em ❶ , ❷ e ❸ realizamos a conexão com um access point.
Em ❹ , ajustamos o Relógio de Tempo Real (Real Time Clock – RTC) do
NodeMCU com as informações obtidas do servidor NTP. Em seguida, em
❺, obtemos as informações de data e hora usando a função localtime.
A biblioteca ntptime retorna o horário no padrão UTC; dessa forma, é necessário
realizar o ajuste de fuso horário. Por exemplo, para o horário de São Paulo,
devemos subtrair 3 horas (America/Sao_Paulo: UTC/GMT -03:00).
Note que a função localtime retorna uma lista contendo os dados, por
exemplo (2020, 4, 18, 19, 26, 59, 5, 109). Dessa forma, podemos
manipular individualmente os elementos da lista para exibi-los
formatados para o padrão usado no Brasil, como mostra o programa a
seguir.
z ntp-data-hora-2.py
import network
import ntptime
import machine
import time
wifi = network.WLAN(network.STA_IF)
wifi.active(True)
wifi.connect('ap', 'senha')
while wifi.isconnected() == False:
machine.idle()
print('Conexao realizada:', wifi.ifconfig())
ntptime.settime()
agora = time.localtime()
print ('Data: %d/%02d/%d' % (agora[2], agora[1], agora[0])) ❶
print ('Hora: %d:%02d' % (agora[3] - 3, agora[4])) ❷
wifi.active(False)
Em ❶ , exibimos o dia, mês e ano, utilizando os elementos da lista com
índice 2, 1 e 0, respectivamente. Enquanto em ❷ mostramos hora e
minuto, usando os elementos da lista com índice 3 e 4. Note também que
realizamos o ajuste do fuso horário para São Paulo, isto é, subtraímos 3
do elemento da lista com índice 3.
capítulo 6
Displays
if len(dispositivos) == 0:
print("Nenhum dispositivo encontrado!")
else:
print('Dispositivos encontrados:', len(dispositivos))
for dispositivo in dispositivos:
print("Endereço:", hex(dispositivo))
Figura 6.2: Ligação do LCD (NodeMCU-ESP32).
Após montar o circuito eletrônico, conectar o NodeMCU à porta USB do
computador e executar o programa, anote o endereço que foi encontrado.
Por exemplo, na Figura 6.3 podemos ver que o LCD I2C possui o endereço
0x27, sendo que o prefixo 0x indica que esse valor foi apresentado em
notação hexadecimal.
def obterTemperatura(termistor):
if uname()[0] == 'esp8266': ❶
fator = 1024.0
else:
fator = 4096.0
tempK = log(10000.0 * (fator / termistor - 1))
tempK = 1 / (0.001129148 + (0.000234125
+ (0.0000000876741 * tempK * tempK )) * tempK)
tempC = tempK - 273.15
return tempC
if uname()[0] == 'esp8266': ❷
adc = ADC(0)
else:
adc = ADC(Pin(34))
adc.atten(ADC.ATTN_11DB)
try:
while True:
temp = obterTemperatura(adc.read())
print ("Temperatura: ", round(temp, 1), "°C")
LCD.puts("Temperatura:", 0, 0)
LCD.puts(str(round(temp, 1)) + str(chr(223)) + 'C', 0, 1)
sleep(1.0)
except:
LCD.clear()
LCD.backlight(False)
tm = TM1637(clk=Pin(5), dio=Pin(4)) ❶
# Ativa todos os LEDs do módulo "88:88"
tm.write([127, 255, 127, 127]) ❷
sleep(2.0)
# Desliga todos os LEDs
tm.write([0, 0, 0, 0])
Note no programa que, em ❶ , criamos o objeto relativo ao display,
indicando os pinos de clock (clk) e dados (dio). Em ❷ , ligamos todos os
LEDs do display utilizando o método write, sendo que cada item da lista
corresponde a um dígito do display, observando que o segundo item
também controla o acendimento do símbolo “:” (dois pontos) do display.
Após 2,0 segundos desligamos todos os LEDs usando novamente o
método write, porém passando 0 para todos os elementos da lista.
Com o intuito de entender os valores passados para o método write,
usando uma lista, observe na Figura 6.9 que cada dígito é composto de
7 segmentos. Para compor o valor do dígito, devemos especificar se
determinado segmento está ligado (1) ou desligado (0), organizando os
valores do segmento “g” até o segmento “a”.
Relógio digital
Este projeto irá usar o Wi-Fi integrado do NodeMCU para buscar na
Internet a hora atual em um servidor NTP, ajustar o fuso horário e exibi-
la no display.
Mantendo a montagem do projeto anterior que empregou o módulo
TM1637, use o ambiente de desenvolvimento Thonny para implementar o
programa apresentado a seguir.
z tm-relogio.py
from machine import Pin, idle
from time import sleep, localtime
import network
import ntptime
import tm1637
tm = tm1637.TM1637(clk=Pin(5), dio=Pin(4))
tm.show('Sinc')
wifi = network.WLAN(network.STA_IF) ❶
wifi.active(True) ❷
wifi.connect('ap', 'senha') ❸
while wifi.isconnected() == False:
idle()
print('Conexao realizada.')
print(wifi.ifconfig())
ntptime.settime() ❹
wifi.active(False) ❺
try:
fuso = -3
sep = True
while True:
agora = localtime() ❻
tm.numbers (agora[3] + fuso, agora[4], sep) ❼
sep = not sep
sleep(2.0)
except KeyboardInterrupt:
tm.write([0, 0, 0, 0])
Analisando o código-fonte, podemos observar que em ❶ , ❷ e ❸ é
realizada a conexão com um access point. Em ❹ , o relógio interno (RTC)
do NodeMCU é ajustado de acordo com o horário no servidor NTP e, em
❺ , desligamos o módulo Wi-Fi, pois a partir desse momento a hora será
obtida do próprio RTC do NodeMCU.
Em ❻ , a data e a hora são obtidas do RTC, em ❼ usamos o método
numbers para exibir a hora, com o devido ajuste do fuso horário, e os
minutos no display de LEDs.
def obter_temperatura(termistor):
if os.uname()[0] == 'esp8266':
fator = 1024.0
else:
fator = 4096.0
tempK = log(10000.0 * (fator / termistor - 1))
tempK = 1 / (0.001129148 + (0.000234125
+ (0.0000000876741 * tempK * tempK )) * tempK)
tempC = tempK - 273.15
return tempC
tm = tm1637.TM1637(clk=Pin(5), dio=Pin(4))
tm.brightness(brilho)
tm.show('Sinc')
wifi = network.WLAN(network.STA_IF)
wifi.active(True)
wifi.connect('ap', 'senha')
while wifi.isconnected() == False:
idle()
print('Conexao realizada.')
print(wifi.ifconfig())
ntptime.settime()
wifi.active(False)
try:
fuso = -3
sep = True
mostrar = 0
while True:
if intensidade.value() == True: ❸
brilho = brilho + 1 ❹
if brilho == 4: ❺
brilho = 0 ❻
tm.brightness(brilho) ❼
❽
if selecao.value() == True:
mostrar = mostrar + 1 ❾
if mostrar == 2:❿
mostrar = 0 ⓫
if mostrar == 0:⓬
agora = localtime() ⓭
tm.numbers (agora[3] + fuso, agora[4], sep) ⓮
elif mostrar == 1: ⓯
temp = 0 ⓰
for i in range(10): ⓱
temp = temp + obter_temperatura(adc.read()) ⓲
tm.temperature(int(temp/10)) ⓳
sep = not sep
sleep(0.5)
except:
tm.write([0, 0, 0, 0])
Concluindo a análise do programa, em ❽, ❾, ❿, ⓫, ⓬, ⓭, ⓮, ⓯, ⓰, ⓱, ⓲
e ⓳ usamos o botão selecao e a variável mostrar para alternar entre a
exibição da hora atual ou da temperatura.
Display OLED
O display OLED (Organic Light-Emitting Diode) possibilita a exibição
não apenas de caracteres alfanuméricos, mas também de imagens e outros
elementos gráficos. O modelo de 128x64 pixels é o mais facilmente
encontrado, estando disponível com interface I2C, que pode ser visto à
esquerda na Figura 6.12, ou SPI, que está à direita na mesma figura.
Ambos são protocolos de comunicação serial muito utilizados na
comunicação de módulos com dispositivos microcontrolados.
Figura 6.12: Displays OLED.
Como já abordado anteriormente, o I²C é um barramento serial usado para
conectar módulos de baixa velocidade a sistemas microcontrolados. Apresenta um
barramento com um sinal de clock (SCL) e linhas de dados (SDA). O SPI também
utiliza linhas separadas para clock e dados, além de uma linha de seleção para que
seja possível escolher o módulo com o qual o microcontrolador irá estabelecer a
comunicação.
Neste primeiro projeto com o display OLED, usaremos o modelo com o
barramento I²C. Dessa forma, identifique os seguintes materiais que são
necessários para a montagem.
Relação de materiais
1 NodeMCU (ESP8266 ou ESP32)
1 Display OLED 128x64 I2C
1 Protoboard
Cabos de ligação
Em seguida, realize a montagem da maneira indicada pela Figura 6.13,
caso esteja utilizando o NodeMCU (ESP8266).
Figura 6.13: Ligação do OLED I2C ao NodeMCU-ESP8266.
Por outro lado, se estiver usando o NodeMCU (ESP32), adote a Figura 6.14
como referência.
Figura 6.14: Ligação do OLED I2C ao NodeMCU-ESP32.
Resumindo o que é mostrado nas figuras 5.13 e 5.14, a conexão dos
terminais do OLED aos terminais dos dois modelos de NodeMCU devem
ser realizadas conforme a Tabela 6.1.
Tabela 6.1: Conexão dos terminais
OLED I2C NodeMCU (ESP8266 ou ESP32)
VDD 3V3
GND GND
CS GPIO15
DC GPIO4
VDD 3V3
GND GND
Figura 6.16: Ligação do OLED SPI ao NodeMCU-ESP8266.
A biblioteca ssd1306, que funciona com displays de OLED com barramento SPI e
I2C, está disponível em:
https://github.com/micropython/micropython/tree/master/drivers/display.
Realize o download do arquivo ssd1306.py para o seu computador.
Antes de implementar o programa, utilize o utilitário Ampy para copiar a
biblioteca para o sistema de arquivos do NodeMCU. Troque a porta com6,
mostrada no comando a seguir, para a porta na qual o NodeMCU está
conectado ao seu computador.
ampy --port com6 put ssd1306.py
Figura 6.17: Ligação do OLED SPI ao NodeMCU-ESP32.
Em seguida, no Thonny implemente o seguinte código-fonte.
z oled-spi-ola-1.py
from machine import Pin, SPI
from time import sleep
import ssd1306
if os.uname()[0] == 'esp8266':
adc = ADC(0)
else:
adc = ADC(Pin(34))
adc.atten(ADC.ATTN_11DB)
def obter_temperatura(termistor):
if os.uname()[0] == 'esp8266':
fator = 1024.0
else:
fator = 4096.0
tempK = log(10000.0 * (fator / termistor - 1))
tempK = 1 / (0.001129148 + (0.000234125
+ (0.0000000876741 * tempK * tempK )) * tempK)
tempC = tempK - 273.15
return tempC
try:
while True:
temp = obter_temperatura(adc.read()) ❶
oled.fill_rect(66, 25, 62, 20, 0)
oled.show()
oled.text('Temp.:', 66, 25) ❷
oled.text(str(round(temp, 1)) + "'C", 66, 35) ❸
oled.show()❹
sleep(5.0) ❺
except KeyboardInterrupt:
oled.poweroff()
spi.deinit()
Assim como no programa anterior, observe que ocorre leitura do arquivo
e a exibição da imagem no display OLED. Na sequência do programa é
realizada em ❶ a leitura do termístor e em ❷ , ❸ , ❹ e ❺ a exibição da
temperatura no display a cada 5 segundos. Note que apenas a informação
de temperatura é atualizada com a imagem permanecendo fixa na tela,
pois a imagem é carregada e exibida antes da instrução while. Na
Figura 6.23, apresentamos um exemplo de execução do programa.
Redes de sensores
if len(dispositivos) == 0:
print("Nenhum dispositivo encontrado!")
else:
print('Dispositivos encontrados:', len(dispositivos))
for dispositivo in dispositivos:
print("Endereço:", hex(dispositivo))
Figura 7.2: Conexões (NodeMCU-ESP8266).
Figura 7.3: Conexões (NodeMCU-ESP32).
Após a execução do programa, no shell deveremos observar o endereço
dos três dispositivos I2C conectados ao NodeMCU, conforme Figura 7.4.
Figura 7.4: Dispositivos I2C detectados.
Em seguida, digite o seguinte programa no ambiente de desenvolvimento
Thonny. Em ❶ , note que configuramos o I2C, na sequência o BMP180, o
BH1750 e o LCD1602 são conectados logicamente ao barramento, em que
cada um possui um endereço exclusivo, conforme pode ser observado em
❷, ❸ e ❹.
z lcd-i2c-bmp180-bh1750.py
from machine import Pin, I2C
from time import sleep
from mp_i2c_lcd1602 import LCD1602
from bmp180 import BMP180
from bh1750 import BH1750
import gc
gc.collect()
1-Wire
Neste projeto iremos abordar o uso do DS18B20, que consiste em um
termômetro digital que utiliza o sistema de barramento 1-Wire para se
comunicar com o NodeMCU ou qualquer outro sistema microcontrolado.
O 1-Wire tem a vantagem de usar apenas um terminal da GPIO para
todos os dispositivos, sendo que esses dispositivos são identificados por
meio de um endereço único.
O 1-Wire consiste em um sistema de barramento projetado pela Dallas
Semiconductor que possibilita comunicação de dados em baixa
velocidade. O princípio de funcionamento é similar ao I²C, mas apresenta
uma velocidade menor de transmissão de dados, e por outro lado, oferece
um maior alcance. Atua com o paradigma mestre-escravo, em que o
dispositivo microcontrolado assume o papel de mestre e os demais são
escravos.
Identifique e separe os seguintes materiais.
Relação de materiais
1 NodeMCU (ESP8266 ou ESP32)
1 Resistor de 4,7k Ohms (amarelo, violeta, vermelho)
1 Display LCD 1602 I2C
2 Sensores DS18B20
1 Protoboard
Cabos de ligação
Realize a montagem da maneira indicada pela Figura 7.5, caso esteja
utilizando o NodeMCU (ESP8266).
Figura 7.5: Conexões (NodeMCU-ESP8266).
Adote como referência a Figura 7.6, se estiver usando o NodeMCU
(ESP32).
Agora no Thonny vamos usar o programa mostrado a seguir para
verificar a conexão dos sensores ao barramento 1-Wire. Em ❶ , indicamos
o pino da GPIO que irá receber os dados dos sensores 1-Wire e em ❷
usamos o método scan para identificar os sensores que estão no
barramento.
Em ❸ e ❹ , percorremos a lista retornada pelo método scan e exibimos a
identificação única de cada um deles.
z 1-wire-scan.py
from machine import Pin
from onewire import OneWire
from ds18x20 import DS18X20
from os import uname
rede = DS18X20(OneWire(Pin(2))) ❶
sensores = rede.scan() ❷
try:
while True:
rede.convert_temp() ❸
sleep_ms(750) ❹
i = 1
for sensor in sensores: ❺
temp = rede.read_temp(sensor) ❻
LCD.puts('Temp. Sensor #' + str(i) + ':', 0, 0)
LCD.puts(str(round(temp, 1)) + str(chr(223)) + 'C', 0, 1)
i = i + 1
sleep(2.0)
except:
LCD.clear()
LCD.backlight(False)
Após configurar a comunicação 1-Wire e em ❶ e ❷ identificar os sensores
que estão conectados, em ❸ executamos o método convert_temp para
iniciar o processo de leitura nos sensores, sendo que devemos aguardar
750ms para que essas leituras sejam realizadas, conforme mostrado em ❹.
Posteriormente, em ❺ e ❻, obtemos a temperatura de cada sensor usando
o método read_temp e as exibimos no display LCD.
MQTT (Message Queue Telemetry Transport)
O MQTT consiste em um protocolo de mensagens leve e assíncrono
voltado para sensores, sistemas microcontrolados e pequenos dispositivos
móveis. Fundamentado na pilha de protocolos TCP/IP, a troca de
mensagens é realizada a partir de um modelo Publicador-Subscritor,
bastante simples e leve.
O uso do MQTT possibilita reduzir o uso de banda de rede e dos
recursos dos equipamentos, tem como características básicas desacoplar o
emissor e o receptor da mensagem e é escalável em ambientes de rede de
baixo desempenho e qualidade de transmissão. É o protocolo ideal para
aplicações para IoT em que existe uma enorme gama de dispositivos
trocando pequenas mensagens entre si.
As mensagens no MQTT adotam uma estrutura de tópicos, em que os
níveis são separados por uma barra (“/”), por exemplo,
casa/sensor/cozinha/temperatura.
O termo broker será usado para o servidor que recebe mensagens dos
clientes, sendo que um cliente pode ser qualquer tipo de dispositivo que
pode interagir com o broker, enviando e/ou recebendo mensagens.
Existem alguns brokers públicos disponíveis na Internet e é possível
instalar um broker no seu computador.
Nesta obra, vamos utilizar o Eclipse Mosquitto, que é um broker de código-fonte
aberto. Ele está disponível para download gratuito em https://mosquitto.org/. O
desenvolvimento dos projetos seguintes exige que o Mosquitto esteja
devidamente instalado no seu computador.
Após instalar Eclipse Mosquitto e ativar o respectivo serviço, vamos
construir um projeto em que enviaremos uma mensagem, por intermédio
de um tópico no broker, que irá possibilitar ligar ou desligar um LED
conectado ao NodeMCU.
Existe também a possibilidade de usar o ambiente de teste do Eclipse Mosquitto,
disponibilizado no próprio site do Eclipse Mosquitto (https://mosquitto.org/).
Nesse caso, substitua o endereço IP do seu computador por test.mosquitto.org
nos projetos.
Comece identificando e separando os materiais relacionados a seguir.
Relação de materiais
1 NodeMCU (ESP8266 ou ESP32)
1 Resistor de 220 Ohms (vermelho, vermelho, marrom) ou 330 Ohms
(laranja, laranja, marrom)
1 LED (qualquer cor)
1 Protoboard
Cabos de ligação
Em seguida, realize a montagem da maneira indicada pela Figura 7.8, caso
esteja utilizando o NodeMCU (ESP8266).
estacao = network.WLAN(network.STA_IF) ❶
estacao.active(True) ❷
estacao.connect('ap', 'senha') ❸
while estacao.isconnected() == False:
pass
print('Conexao realizada.')
print(estacao.ifconfig())
Node-RED
Olá Node-RED
Vamos criar um primeiro fluxo, que é como chamamos as aplicações
desenvolvidas no Node-RED. Adote como referência a Figura 8.2 e
identifique na paleta, que está no lado esquerdo da tela, os nós inject e debug
e os arraste para a área central do programa. Em seguida, realize a
conexão entre ambos.
Figura 8.2: Criação de um fluxo.
Realize um duplo clique do mouse sobre o nó inject para abrir o painel de
edição do nó. Altere o tipo do atributo Payload para string e digite o texto
Olá Pessoal!, conforme podemos notar na Figura 8.3. Pressione o botão
Done para concluir a alteração do nó.
Figura 8.3: Edição do nó inject.
Na Figura 8.4, mostramos como o fluxo deverá se assemelhar após a
edição do nó.
JavaScript
Do mesmo modo que desenvolvemos o fluxo anterior, identifique na
paleta os nós inject e debug e os arraste para a área central do programa. Em
seguida, realize a conexão entre ambos (Figura 8.7).
Requisições HTTP
O Node-RED, como já mencionado anteriormente, possibilita a
comunicação de dispositivos de hardware com serviços online. Esse
projeto irá demonstrar essa possibilidade, usando o nó http request. Comece
identificando e separando os materiais relacionados a seguir.
Relação de materiais
1 NodeMCU (ESP8266 ou ESP32)
1 Resistor de 220 Ohms (vermelho, vermelho, marrom) ou 330 Ohms
(laranja, laranja, marrom)
1 LED (qualquer cor)
1 Protoboard
Cabos de ligação
Em seguida, realize a montagem da maneira indicada pela Figura 8.14,
caso esteja utilizando o NodeMCU (ESP8266).
def obter_arquivo(arquivo):
conteudo = ''
a = open(arquivo, 'rb')
conteudo = a.read()
a.close()
return conteudo
estacao = network.WLAN(network.STA_IF)
estacao.active(True)
estacao.connect('ap', 'senha')
while estacao.isconnected() == False:
pass
print('Conexao realizada.')
print(estacao.ifconfig())
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 80))
s.listen(5)
try:
while True:
conexao, endereco = s.accept()
print('Conexao de %s' % str(endereco))
requisicao = conexao.recv(1024)
requisicao = str(requisicao)
print('Conteudo = %s' % requisicao)
if requisicao.find('/led/1') != -1:
print('Acender')
LED.value(1)
elif requisicao.find('/led/0') != -1:
print('Apagar')
LED.value(0)
else:
LED.value(0)
html = obter_arquivo('http-led-bootstrap.html')
conexao.send('HTTP/1.1 200 OK\n')
conexao.send('Content-Type: text/html\n')
conexao.send('Connection: close\n\n')
conexao.sendall(html)
conexao.close()
except KeyboardInterrupt:
s.close()
estacao.active(False)
Mantenha o programa executando no Thonny e vamos passar para o
Node-RED. Adicione um novo fluxo, conforme ilustra a Figura 8.16.
No fluxo criado, insira dois nós do tipo inject e um nó http request e realize as
conexões da maneira mostrada na Figura 8.17.
estacao = network.WLAN(network.STA_IF)
estacao.active(True)
estacao.connect('ap', 'senha')
while estacao.isconnected() == False:
pass
print('Conexao realizada.')
print(estacao.ifconfig())
Dashboard
O Node-RED apresenta um conjunto de nós que possibilitam a
construção de interfaces gráficas que irão facilitar para o usuário a
interação e visualização das informações. Adicione à paleta o módulo
node-red-dashboard. Para isso, clique no menu principal e, em seguida, na
opção Manage palette, conforme ilustra a Figura 8.25.
Figura 8.25: Acesso ao menu do Node-RED.
Em seguida, clique na aba Install (Figura 8.26), procure pelo módulo node-
red-dashboard e clique no botão install.
No Node-RED, realize a criação de um fluxo. Em seguida, insira dois nós
do tipo switch e um nó mqtt out, realizando a conexão entre eles, conforme
mostra a Figura 8.27.
Entre na edição do nó switch, realizando um clique duplo do mouse sobre
ele. O primeiro passo consiste em criar um Grupo. Para isso, selecione a
opção Add new ui_group... presente no atributo Group e clique no botão de Edição,
que é identificado pelo ícone do lápis. Agora selecione o atributo Tab,
escolha a opção Add new ui_tab... e clique no botão de Edição (ícone do lápis).
Defina a propriedade Name com o valor Painel de Controle, conforme
mostra a Figura 8.28, e clique no botão Update.
Agora defina o atributo Nome com o valor LED e, para o atributo Tab,
selecione a opção Painel de Controle (Figura 8.29). Clique no botão Update
para finalizar a edição do nó dashboard group.
Figura 8.26: Instalação dos módulos.
print('Iniciando...')
i2c = I2C(sda=Pin(5), scl=Pin(4)) ❶
bmp = BMP180(i2c)
bmp.oversample_sett = 2
bmp.baseline = 101325
bh = BH1750(i2c)
estacao = network.WLAN(network.STA_IF) ❷
estacao.active(True) ❸
estacao.connect('ap', 'senha') ❹
while estacao.isconnected() == False:
pass
print('Conexao realizada.')
print(estacao.ifconfig())
servidor = 'test.mosquitto.org' ❺
topico = 'sensor/bmp_bh' ❻
cliente = MQTTClient('NodeMCU', servidor, 1883) ❼
try:
while True:
temperatura = int(bmp.temperature) ❽
ressão = int(bmp.pressure / 100) ❾
altitude = int(bmp.altitude) ❿
luminosidade = int(bh.luminance(BH1750.ONCE_HIRES_1)) ⓫
conteudo = '{"temperatura" : ' + str(temperatura) + ', "pressao" :
'
+ str(pressao) + ', "altitude" : ' + str(altitude) + ',
"luminosidade" : '
+ str(luminosidade) + '}' ⓬
print('Publicando no servidor MQTT')
cliente.connect() ⓭
cliente.publish(topico.encode(), conteudo.encode()) ⓮
cliente.disconnect() ⓯
print ('Envio realizado.')
gc.collect()
sleep(60.0)
except KeyboardInterrupt:
estacao.disconnect()
estacao.active(False)
print("Fim.")
Execute o programa criado em MicroPython e, na sequência, passamos
para o desenvolvimento da aplicação no Node-RED. Conforme ilustra a
Figura 8.34, realize a criação de um fluxo, insira e conecte os nós mqtt in e
debug.
th, td {
padding: 5px;
}
.titulo {
background-color: #1aa3ff;
color: white;
}
.linha {
background-color: #cce6ff;
}
</style>
<table>
<tr>
<td class='titulo'>Data/Hora</td>
<td class='titulo'>Temperatura</td>
<td class='titulo'>Pressão</td>
<td class='titulo'>Altitude</td>
<td class='titulo'>Luminosidade</td>
</tr>
<tr ng-repeat='(chave, valor) in msg.payload'>
<td ng-if='$even'>{{valor.data_hora
| date:'dd/MM/yyyy hh:mm'}}</td>
<td ng-if='$odd' class='linha'>{{valor.data_hora |
date:'dd/MM/yyyy hh:mm'}}</td>
<td ng-if='$even'>{{valor.temperatura}}°C</td>
<td ng-if='$odd' class='linha'>{{valor.temperatura}}°C</td>
<td ng-if='$even'>{{valor.pressao}} hPa</td>
<td ng-if='$odd' class='linha'>{{valor.pressao}} hPa</td>
<td ng-if='$even'>{{valor.altitude}} m.</td>
<td ng-if='$odd' class='linha'>{{valor.altitude}} m.</td>
<td ng-if='$even'>{{valor.luminosidade}} lux</td>
<td ng-if='$odd' class='linha'>{{valor.luminosidade}} lux</td>
</tr>
</table>
Note na criação da tabela o uso de alguns atributos que começam com o
prefixo ng-; eles são elementos do Angular, que são suportados pelos
componentes gráficos do Node-RED.
O Angular consiste em uma plataforma de desenvolvimento e um framework para
projeto de aplicações para a web e para dispositivos móveis. Conheça mais em
https://angular.io/.
Após finalizar a configuração dos nós, clique no botão Deploy. Abra uma
nova aba no navegador e digite a URL http://localhost:1880/ui. Note que
nesse fluxo devemos pressionar o botão Atualizar sempre que desejar realizar
a atualização dos dados que estão sendo exibidos na tabela (Figura 8.53).
Figura 8.53: Exibição da tabela.
print("Iniciando...")
dht = DHT11(Pin(12, Pin.IN, Pin.PULL_UP))
estacao = network.WLAN(network.STA_IF) ❶
estacao.active(True) ❷
estacao.connect('ap', 'senha') ❸
while estacao.isconnected() == False:
pass
print('Conexao realizada.')
print(estacao.ifconfig())
print("Iniciando...")
dht = DHT11(Pin(12, Pin.IN, Pin.PULL_UP))
estacao = network.WLAN(network.STA_IF)
estacao.active(True)
estacao.connect('ap', 'senha')
while estacao.isconnected() == False:
machine.idle()
print('Conexao realizada.')
print(estacao.ifconfig())
device = Device(
device_id='nodemcu',
device_type='NodeMCU',
token='TOKEN_CRIADO',
org='ID_DE_USUARIO',
username='use-token-auth'
) ❶
try:
while True:
dht.measure()
temp = dht.temperature()
umid = dht.humidity()
print('Temperatura: %3.1f °C' %temp)
print('Umidade: %3.1f %%' %umid)
print ('Conectando a Watson IoT...')
device.connect()
device.publishEvent('dht11', {'temperatura': str(temp),
'umidade': str(umid)}, message_format='json', qos=1) ❷
device.disconnect()
print ('Envio realizado.')
gc.collect()
time.sleep(60.0)
except KeyboardInterrupt:
estacao.disconnect()
estacao.active(False)
print("Fim.")
Deixe o programa executando no Thonny e vá para o dashboard do
Watson IoT; selecione a opção Dispositivos no menu que está à esquerda da
janela. Depois, conforme ilustra a Figura 9.8, identifique o dispositivo e
clique na opção Eventos recentes para visualizar as informações recebidas.
Figura 9.8: Dados recebidos no Watson IoT.
Uma vez que os dados já estão sendo recebidos no Watson IoT, vamos
criar um quadro para exibição dos dados recebidos. No menu lateral da
esquerda, clique na opção Placas. Será mostrado um conjunto customizável
de painéis (Figura 9.9); clique no painel VISÃO GERAL DE UTILIZAÇÃO.
Comandos
Nos projetos anteriores, exploramos o envio de dados obtidos de sensores
para uma plataforma IoT em nuvem. Por outro lado, é possível criar
aplicações que irão, por intermédio do Watson IoT, enviar mensagens
(comandos) para dispositivos como o NodeMCU. Este projeto irá
demonstrar essa possibilidade. Comece identificando e separando os
materiais relacionados a seguir.
Relação de materiais
1 NodeMCU (ESP8266 ou ESP32)
1 Resistor de 220 Ohms (vermelho, vermelho, marrom) ou 330 Ohms
(laranja, laranja, marrom)
1 LED (qualquer cor)
1 Protoboard
Cabos de ligação
Em seguida, realize a montagem da maneira indicada pela Figura 9.30,
caso esteja utilizando o NodeMCU (ESP8266).
print("Iniciando...")
estacao = network.WLAN(network.STA_IF)
estacao.active(True)
estacao.connect('ap', 'senha')
while estacao.isconnected() == False:
machine.idle()
print('Conexao realizada.')
print(estacao.ifconfig())
device = Device(
device_id='nodemcu',
device_type='NodeMCU',
token='TOKEN_CRIADO',
org='ID_DE_USUARIO',
username='use-token-auth'
) ❶
def controle_led(dados): ❷
if 'acao' in dados.keys():
print('LED:', dados['acao'])
led.value(int(dados['acao']))
else:
print ('Chave não encontrada!')
device.set_command('led', controle_led) ❸
device.connect()
try:
while device.is_connected:
device.sync_loop() ❹
gc.collect()
except KeyboardInterrupt:
device.disconnect()
estacao.disconnect()
estacao.active(False)
led.value(0)
print('Fim.')
Prosseguindo com a análise do código-fonte, o passo seguinte consiste em
definir a função de callback, ou seja, a função que deverá ser executada
sempre que uma mensagem for publicada no Watson IoT. Essa função,
neste exemplo, foi chamada de controle_led e está definida em ❷ , sendo
que esta é associada ao dispositivo em ❸ pelo método set_command.
Por último, em ❹ o método sync_loop, que foi implementado dentro de
um laço de repetição que ficará em execução enquanto o dispositivo
permanecer conectado ao ambiente em nuvem, monitora se há mensagens
postadas. Então, quando uma mensagem é identificada, a função de
callback é executada, definindo o estado do LED que está conectado ao
GPIO5.
Mantenha o programa em execução no NodeMCU e, em seguida, acesse o
serviço do Node-RED que está em execução na nuvem da IBM. Crie um
fluxo, entre no menu principal e escolha Manage palette (Figura 9.32).
$("#off").click(function(){
$.ajax({url: "/led/0", success: function(result){
$('#on').removeClass('btn-default').addClass('btn-
success');
$('#off').removeClass('btn-danger').addClass('btn-
default');
}});
});
});
</script>
</head>
<body>
<div class="container">
<h1>NodeMCU IoT</h1>
Controle do LED<br /><br />
<div class="btn-group">
<button id="on" type="button"
class="btn btn-success btn-lg">Acender</button><br><br>
<button id="off" type="button"
class="btn btn-default btn-lg">Apagar</button>
</div>
</div>
</body>
</html>
Em seguida, o nó http response irá enviar a página web para o navegador
(cliente) que realizou a requisição. No mesmo fluxo vamos, agora,
implementar os nós que serão responsáveis pela resposta às requisições
AJAX e envio dos respectivos comandos (mensagens) ao Watson IoT.
Observe na Figura 9.41 que será necessário realizar a inserção de dois nós
http in, um deles receberá requisição AJAX [get] /led/1, enquanto o outro
nó irá receber a requisição AJAX [get] /led/0.
Na sequência, coloque dois nós change. Entre na edição de cada um dos nós
e realize a operação Set em msg.payload e defina o comando, em formato
JSON, que será enviado para o Watson IoT, isto é, {"acao":"1"} para um
dos nós e {"acao":"0"} para o outro. Na Figura 9.42, mostramos o exemplo
de edição do nó quando a requisição para ligar o LED for recebida.
ThingSpeak
Canais
Após criar uma conta de usuário e realizar o acesso à plataforma,
devemos criar um canal para realizar o armazenamento dos dados. Para
fazer isso, selecione a opção Channels no menu, depois selecione My Channels e
clique no botão New Channel, conforme ilustra a Figura 10.3.
print("Iniciando...")
dht = DHT11(Pin(12, Pin.IN, Pin.PULL_UP))
estacao = network.WLAN(network.STA_IF)
estacao.active(True)
estacao.connect('ap', 'senha')
while estacao.isconnected() == False:
machine.idle()
print('Conexao realizada.')
print(estacao.ifconfig())
SERVIDOR = "mqtt.thingspeak.com" ❶
CHANNEL_ID = "COLOQUE_O_ID_DO_CANAL"❷
WRITE_API_KEY = "COLOQUE_A_CHAVE" ❸
topico = "channels/" + CHANNEL_ID + "/publish/" + WRITE_API_KEY ❹
cliente = MQTTClient("umqtt_client", SERVIDOR) ❺
try:
while True:
dht.measure()
temp = dht.temperature()
umid = dht.humidity()
print('Temperatura: %3.1f °C' %temp)
print('Umidade: %3.1f %%' %umid)
conteudo = "field1=" + str(temp) + "&field2=" + str(umid)
print ('Conectando a ThingSpeak...')
cliente.connect() ❻
cliente.publish(topico, conteudo) ❼
cliente.disconnect() ❽
print ('Envio realizado.')
time.sleep(600.0) ❾
except KeyboardInterrupt:
estacao.disconnect()
estacao.active(False)
print("Fim.")
Analisando o programa, em ❶ , ❷ , ❸ e ❹ devemos especificar os dados
para conexão à plataforma ThingSpeak. Em ❺ , o cliente MQTT é criado
e, posteriormente, em ❻ , ❼ e ❽ os dados são publicados no broker
MQTT da ThingSpeak. Em ❾, definimos a frequência de envio que, neste
exemplo, é 600 segundos, isto é, 10 minutos. Na Figura 10.7, temos um
exemplo da visualização dos dados do canal.
TalkBack App
A TalkBack App possibilita o envio de uma fila de comandos para um
dispositivo. O conceito básico é possibilitar o controle remoto e a
execução de ações pela Internet, sem que exista a necessidade de a
aplicação acessar diretamente o dispositivo. O projeto proposto irá
possibilitar ligar ou desligar um determinado equipamento remotamente.
Para isso, iremos utilizar os materiais relacionados a seguir.
Relação de materiais
1 NodeMCU (ESP8266 ou ESP32)
1 Módulo Relê de 5 Volts
1 Protoboard
Cabos de ligação
Em seguida, realize a montagem da maneira indicada pela Figura 10.8,
caso esteja utilizando o NodeMCU (ESP8266).
THINGSPEAK_API = 'api.thingspeak.com' ❶
TALKBACK_ID = 'id_da_talkback' ❷
TALKBACK_API_KEY = 'chave_da_api' ❸
URL = 'http://' + THINGSPEAK_API + '/talkbacks/'
+ TALKBACK_ID + '/commands/execute.json'
CABECALHO = {'content-type': 'application/json'}
DADOS = ujson.dumps({'api_key': TALKBACK_API_KEY})
try:
while True:
result = urequests.post(URL, headers=CABECALHO, data=DADOS).json()
❹
if 'command_string' in result: ❺
if result['command_string'] == 'LIGAR':
RELE.value(1)
elif result['command_string'] == 'DESLIGAR':
RELE.value(0)
sleep(60.0) ❻
except KeyboardInterrupt:
RELE.value(0)
estacao.disconnect()
estacao.active(False)
É importante salientar que cada comando, uma vez processado, é retirado
da fila. Além disso, novos comandos serão sempre inseridos ao final da
fila.
A seguir vamos criar uma aplicação no Node-RED que possibilitará
adicionarmos comandos à fila da TalkBack. Após acessar o Node-RED e
criar um fluxo, adicione os nós inject, function, http request e debug, conectando-os
da maneira indicada na Figura 10.16.
Figura 10.16: Adicionar um comando à fila.
Os nós inject deverão conter na propriedade Payload os comandos LIGAR e
DESLIGAR. O nó function deverá ser editado e deverá conter o código-fonte
mostrado a seguir. Essa função será responsável pela definição do
conteúdo do cabeçalho (msg.headers) e dos dados (msg.payload) que serão
enviados pela requisição web.
msg.headers = {};
msg.headers['content-type'] = 'application/json';
var dados = {'api_key':'chave_api', 'command_string':msg.payload};
msg.payload = dados;
return msg;
O passo seguinte consiste na edição do nó http request. Observe na
Figura 10.17 que devemos selecionar a opção POST para a propriedade
Method e a propriedade URL deverá conter o endereço da requisição, no
seguinte formato:
http://api.thingspeak.com/talkbacks/id_da_talkback/commands.json.
Conclua a configuração do nó selecionando a opção a parsed JSON object
para a propriedade Return.
Figura 10.17: Edição do nó http request.
Mantenha o programa em MicroPython (talkback-rele.py) em execução
no NodeMCU e clique no botão Deploy para executar o fluxo. Ao clicar nos
nós inject o respectivo comando será adicionado à fila da TalkBack.
Com base nesses conceitos podemos criar uma aplicação completa, onde
será implementada uma página web que possibilitará a adição dos
comandos. Adicione um fluxo no Node-RED, realize a inserção dos nós
http in, switch, function, http request, debug, template e http response, realizando as conexões
da maneira ilustrada na Figura 10.18.
IFTTT
estacao = network.WLAN(network.STA_IF)
estacao.active(True)
estacao.connect('ap', 'senha')
while estacao.isconnected() == False:
pass
print('Conexao realizada.')
print(estacao.ifconfig())
WEBHOOKS_URL = 'http://maker.ifttt.com/trigger/
NOME_GATILHO/with/key/
SUA_CHAVE_DO_WEBHOOKS' ❶
CABECALHO = {'content-type': 'application/json'} ❷
i2c = I2C(sda=Pin(5), scl=Pin(4))
bmp = BMP180(i2c) # 0x77
bmp.oversample_sett = 2
bmp.baseline = 101325
try:
while True:
temperatura = str(bmp.temperature).replace('.', ',')
pressao = str(bmp.pressure / 100).replace('.', ',')
altitude = str(bmp.altitude).replace('.', ',')
DADOS = {'value1': temperatura, 'value2': pressao, 'value3':
altitude} ❸
resp = urequests.post(WEBHOOKS_URL, headers=CABECALHO, json=DADOS)
if resp is not None and resp.status_code < 400:
print('Webhook: Sucesso:')
else:
print('Webhook: Falhou.')
sleep(600.0)
except KeyboardInterrupt:
estacao.disconnect()
estacao.active(False)
Analisando o código-fonte, observe que em ❶ e ❷ realizamos a
configuração da URL e do cabeçalho. Posteriormente, em ❸ preparamos
os dados, em formato JSON, para envio.
Na Figura 11.13, temos um exemplo dos dados enviados pelo NodeMCU
para o applet que, por sua vez, insere na planilha do Google Drive.
estacao = network.WLAN(network.STA_IF)
estacao.active(True)
estacao.connect('ap', 'senha')
while estacao.isconnected() == False:
pass
print('Conexao realizada.')
print(estacao.ifconfig())
WEBHOOKS_URL = 'http://maker.ifttt.com/trigger/
NOME_GATILHO/with/key/
SUA_CHAVE_DO_WEBHOOKS'
CABECALHO = {'content-type': 'application/json'}
try:
while True:
if bmp.temperature > 20.0: ❶
temperatura = str(bmp.temperature).replace('.', ',')
pressao = str(bmp.pressure / 100).replace('.', ',')
altitude = str(bmp.altitude).replace('.', ',')
DADOS = {'value1': temperatura, 'value2': pressao, 'value3':
altitude}
resp = urequests.post(WEBHOOKS_URL, headers=CABECALHO,
json=DADOS)
if resp is not None and resp.status_code < 400:
print('Webhook: Sucesso:')
print (DADOS)
else:
print('Webhook: Falhou.')
sleep(600.0)
except KeyboardInterrupt:
estacao.disconnect()
estacao.active(False)
Observe que o programa é muito parecido com aquele que foi
desenvolvido no projeto anterior, sendo que a diferença consiste no fato
de que a requisição é enviada ao IFTTT somente quando a temperatura
ultrapassa 20°C, conforme é indicado em ❶ . Na Figura 11.23, mostramos
um exemplo de email recebido por intermédio do applet que foi criado.
Sensor de presença
Neste projeto, vamos aplicar o conceito de interrupção criando um
sistema de alarme usando um sensor de presença infravermelho (PIR) e
um módulo relê, que poderá disparar uma campainha quando o sensor
for acionado.
Observe que a interrupção será gerada quando ocorrer uma mudança de
estado no sensor que irá, por sua vez, alterar o estado do pino em que a
interrupção foi programada.
As interrupções permitem que uma rotina seja executada em paralelo ao
programa principal quando ocorre um evento predeterminado. Os
materiais necessários para a montagem do projeto estão relacionados a
seguir.
Relação de materiais
1 NodeMCU (ESP8266 ou ESP32)
1 Módulo relê
1 Sensor de Presença Infravermelho (PIR)
1 Protoboard
Cabos de ligação
Em seguida, realize a montagem da maneira indicada pela Figura 12.1,
caso esteja utilizando o NodeMCU (ESP8266).
movimento = False
def movimento_detectado(pino): ❷
global movimento
movimento = True
Matriz de LEDs
As matrizes de LEDs são largamente empregadas em letreiros e outros
dispositivos de sinalização. Os modelos mais populares e acessíveis são as
matrizes de LEDs de 8 linhas por 8 colunas, sendo que os módulos
podem ser interconectados possibilitando uma ampla área de exibição.
Neste projeto iremos usar um módulo de matriz de LEDs de 8x8 com
4 dígitos para ilustrar o uso de threads no NodeMCU. Os materiais
necessários para a montagem do projeto estão relacionados a seguir.
Relação de materiais
1 NodeMCU (ESP8266 ou ESP32)
1 Módulo Matriz de LEDs de 8x8 com 4 dígitos
1 Protoboard
Cabos de ligação
Em seguida, realize a montagem da maneira indicada pela Figura 12.3,
caso esteja utilizando o NodeMCU (ESP8266).
Caso utilize o NodeMCU (ESP32), a Figura 12.4 deverá ser adotada como
referência para a montagem do projeto.
def exibir_mensagem():❶
global posicao, mensagem, fim
try:
while fim == False:
while posicao < len(mensagem) and fim == False:
display.text(mensagem[posicao: posicao+4], 0, 0, 1)
display.show()
posicao = posicao + 1
sleep(0.5)
display.fill(False)
display.show()
posicao = 0
except:
fim = True
try:
_thread.start_new_thread(exibir_mensagem, ()) ❷
while True:
mensagem = ' ' + input('Digite a mensagem: ')
posicao = 0
except:
fim = True
display.fill(False)
display.show()
spi.deinit()
Observe em ❶ que a função exibir_mensagem irá mostrar o texto na matriz
de LEDs realizando a rolagem (scroll). Essa função será executada em ❷ a
partir de uma thread, isto é, irá funcionar de modo paralelo à rotina
principal que é executada na instrução while. Dessa maneira, enquanto o
texto é rolado na matriz de LEDs, também é possível digitar a nova
mensagem a ser exibida e, quando a tecla Enter é pressionada, o conteúdo
é imediatamente atualizado na matriz de LEDs.
Web-Clock
O objetivo deste projeto é demonstrar o uso do conceito de
temporizadores (timers) a partir da construção de um relógio, que irá se
sincronizar com a World Time API, já usada anteriormente, de modo a
manter o horário exibido sempre atualizado. Observe que os
temporizadores podem ser programados para executar uma função
depois de um determinado tempo ou em intervalos regulares. Os
materiais necessários para a montagem do projeto são mostrados a seguir.
Relação de materiais
1 NodeMCU (ESP8266 ou ESP32)
1 Módulo Matriz de LEDs de 8x8 com 4 dígitos
1 Protoboard
Cabos de ligação
Em seguida, realize a montagem da maneira indicada pela Figura 12.5,
caso esteja utilizando o NodeMCU (ESP8266).
Figura 12.5: Montagem do circuito (NodeMCU-ESP8266).
Caso utilize o NodeMCU (ESP32), a Figura 12.6 deverá ser adotada como
referência para a montagem do projeto.
A biblioteca MAX7219 deverá ser usada e está disponível em
https://github.com/mcauser/micropython-max7219. Faça o download do
arquivo max7219.py para o seu computador e, em seguida, utilize o utilitário ampy
para copiar o arquivo para o NodeMCU.
Figura 12.6: Montagem do circuito (NodeMCU-ESP32).
No Thonny, implemente o programa apresentado a seguir (m8x8-
relogio.py). A função conectar_wlan, definida em ❶ , irá realizar a conexão
à rede sem fio. Em ❷ , temos a função sincronizar_relogio que utilizará a
World Time API para obter a data e hora atuais e atualizar o relógio de
tempo real (RTC) do NodeMCU. Na sequência do programa, as funções
mostrar_hora e mostrar_dois_pontos são usadas para formatar a hora que
será exibida na matriz.
Em ❸ , definimos o timer (temporizador) que será usado para sincronizar
periodicamente o relógio de tempo real com o serviço da Internet. Em ❹ ,
definimos os parâmetros do temporizador, ou seja, nesse programa a
função sincronizar_relogio será executada a cada 7.200.000 ms, ou seja,
2 horas.
z m8x8-relogio.py
from max7219 import Matrix8x8
from machine import Pin, SPI, RTC, Timer
from time import sleep
import network
import urequests
import utime
def conectar_wlan(): ❶
global estacao
estacao = network.WLAN(network.STA_IF)
estacao.active(True)
estacao.connect('ap', 'senha')
while estacao.isconnected() == False:
pass
print('Conexao realizada.')
print(estacao.ifconfig())
def desconectar_wlan():
global estacao
estacao.active(False)
def sincronizar_relogio(): ❷
global anterior
conectar_wlan()
url = "http://worldtimeapi.org/api/timezone/America/Sao_Paulo"
resposta = urequests.get(url)
if resposta.status_code == 200:
#print("JSON:", resposta.text)
json = resposta.json()
agora = str(json["datetime"])
ano = int(agora[0:4])
mes = int(agora[5:7])
dia = int(agora[8:10])
hora = int(agora[11:13])
minuto = int(agora[14:16])
segundo = int(agora[17:19])
subsegundo = int(round(int(agora[20:26]) / 10000))
# Atualizar o RTC do NodeMCU
rtc.datetime((ano, mes, dia, 0, hora, minuto, segundo, subsegundo))
print('RTC atualizado:', *rtc.datetime())
else:
print('Atenção: RTC não atualizado!')
desconectar_wlan()
anterior = ''
def limpar():
display.fill(False)
display.show()
def mostrar_hora(digito):
display.text(digito[0], 0, 0, 1)
display.text(' ' + digito[1], -1, 0, 1)
display.text(' ' + digito[3], +1, 0, 1)
display.text(' ' + digito[4], 0, 0, 1)
display.show()
def mostrar_dois_pontos(ponto):
display.pixel(15, 1, ponto)
display.pixel(15, 2, ponto)
display.pixel(16, 1, ponto)
display.pixel(16, 2, ponto)
display.pixel(15, 4, ponto)
display.pixel(15, 5, ponto)
display.pixel(16, 4, ponto)
display.pixel(16, 5, ponto)
display.show()
ponto = True
anterior = ''
sincronizar_relogio()
try:
timer = Timer(-1) ❸
# 2 horas: 7200000ms
timer.init(period=7200000, mode=Timer.PERIODIC,
callback=lambda t:sincronizar_relogio()) ❹
while True:
horario = "{4:02d}:{5:02d}".format(*rtc.datetime())
if horario != anterior:
limpar()
mostrar_hora(horario)
mostrar_dois_pontos(ponto)
anterior = horario
ponto = not ponto
sleep(0.5)
except KeyboardInterrupt:
limpar()
timer.deinit()
spi.deinit()
Deep sleep
Este projeto tem como objetivo exemplificar o uso da prática de deep
sleep, que consiste em economizar no consumo de energia elétrica em
dispositivos embarcados. Usualmente projetos com microcontroladores se
utilizam de fontes de energia finitas, como baterias. Portanto, em
determinado momento podemos fazer com que nosso dispositivo
"hiberne" quando não precisar mais fazer alguma tarefa, promovendo
uma economia no consumo de bateria.
Uma vez que o dispositivo esteja em estado de hibernação, devemos
implementar alguma maneira de acordá-lo. Nesse projeto a seguir,
faremos com que o dispositivo desperte dando um reset, e fazendo com
que o programa retorne assim que seja feito o seu boot.
Relação de materiais
1 NodeMCU (ESP8266 ou ESP32)
1 Resistor de 220 Ohms (vermelho, vermelho, marrom) ou 330 Ohms
(laranja, laranja, marrom)
2 Resistores de 10k Ohms (marrom, preto, laranja)
1 LED (qualquer cor)
2 Chaves tácteis
Cabos de ligação
Em seguida, realize a montagem da maneira indicada pela Figura 12.7,
caso esteja utilizando o NodeMCU (ESP8266).
Figura 12.7: Montagem do circuito (NodeMCU-ESP8266).
Caso utilize o NodeMCU (ESP32), a Figura 12.8 deverá ser adotada como
referência para a montagem do projeto.
No Thonny, implemente o programa apresentado a seguir (deep_sleep.py).
A função pressionou_dormir, definida em ❶, será chamada quando a chave
táctil for pressionada. Em ❷, vemos os parâmetros de chamada da função
pressionou_dormiu. Essa função faz com que a variável pressionada mude
de valor, fazendo com que a linha indicada em ❸ execute a função
deepsleep, fazendo com que o dispositivo hiberne. Será possível notar que
o piscar do led irá cessar e aparecerá a mensagem "Dormindo..." antes da
hibernação.
pressionou = False
def pressionou_dormir(pino): ❶
global pressionou
pressionou = True