Os sensores DHT11 e DHT22 são opções populares para a leitura de temperatura e umidade. Utilizando somente um fio para comunicação e suportando operação a 3,3 e 5V, a conexão elétrica deste sensor a um microcontrolador é muito simples. A dificuldade está no software, pois o protocolo utilizado se baseia em tempos curtos (da ordem de microssegundos). A solução normalmente adotada é manter o processador preso em um loop, com interrupções inibidas, enquanto os tempos são gerados e medidos. Essa solução não é viável em linguagens interpretadas, como o MicroPython.
Felizmente o Raspberry Pi Pico possui o recurso PIO (Programmable Input Output) que permite a execução de operações complexas de entrada e saída em paralelo com a execução dos programas nos seus dois núcleos ARM Cortex M0+.
Neste artigo vamos ver como usar o PIO em um programa MicroPython para obter temperatura e umidade de um sensor DHT11 ou DHT22.
O Protocolo dos Sensores DHT11 e DHT22
A comunicação do sensor com o microcontrolador utiliza um único sinal bidirecional. Um “pull-up” interno ao sensor faz com o sinal fique em nível alto quando não estiver sendo acionado por nenhuma das pontas.
Para realizar uma leitura o microcontrolador deve:
- Configurar o pino conectado ao sensor como saída
- Manter o pino em nível baixo por pelo menos 18ms (DHT11) ou 1ms (DHT22)
- Reconfigurar o pino para entrada
- O sensor vai indicar que recebeu a solicitação de leitura mantendo o sinal em nível baixo por 80us e depois em nível alto por mais 80us
- Em seguida, o sensor vai enviar 40 bits de resposta. Cada bit é composto de duas partes:
- Um nível baixo por 50us
- Um nível alto de 26 a 28uS (se bit 0) ou 70uS (se bit 1)
Os 40 bits recebidos devem ser interpretados como 5 bytes:
- Dois bytes com a umidade
- Dois bytes com a temperatura
- Um byte para conferência (checksum) que deve ser a soma dos outros quatro
A interpretação dos valores de umidade e temperatura é diferente para os dois modelos:
- O DHT11 permite ler temperaturas de 0 a 50ºC com precisão de 2 graus
- O primeiro byte da umidade é a parte inteira e o segundo é a parte decimal. Por exemplo, se os bytes forem 42 e 3, a umidade é 42+(3*0,1) = 42,3%
- O primeiro byte da temperatura é a parte inteira e o segundo é a parte decimal (normalmente zero). Por exemplo, se os bytes forem 21 e 0, a temperatura é 21+(0*0,1) = 21ºC
- O DHT22 permite ler temperaturas de -40 a +80ºC com precisão de 0,5 grau
- A umidade é um valor inteiro de 16 bits, em décimos, com o byte mais significativo primeiro. Por exemplo, se os valores forem 3 e 42, a umidade é (3*256+42)*0,1 = 81,0%
- Os dois bytes de temperatura devem ser tratados como um valor de 16 bits, com o byte mais significativo primeiro. O bit mais significativo do valor é o sinal, 1 se for negativo ou 0 se for positivo. Os 15 bits restantes são o valor em décimos de grau. Por exemplo, se os bytes forem 1 e 8, a temperatura é (1*256+8)*0,1 = 33,6ºC. Já se os bytes forem 129 e o 8 a temperatura é -(1*256+8)*0,1 = -33,6ºC.
Um último detalhe é que as leituras devem ser espaçadas de 2 segundos.
O PIO
O PIO é um periférico do RP2040 (microcontrolador usado na Raspberry Pi Pico). Existem duas PIOs no RP2040 e cada uma tem 4 “máquinas de estado” que são as responsáveis pela execução de pequenos programas de interface com os pinos de entrada e saída digital.
Os programas para o PIO são escritos usando nove instruções específicas e manipulam:
- Pinos de entrada ou saída
- A direção (entrada ou saída) dos pinos
- Dois registradores internos de uso geral (“X” e “Y”)
- Dois registradores de deslocamento (um para entrada e outro de saída)
- Duas filas para comunicação com os núcleos ARM (uma para entrada e outra para saída).
- Estas duas filas podem ser concatenados se a comunicação for só num sentido
As instruções disponíveis são:
- JMP: desvio (condicional ou incondicional)
- WAIT: espera (tipicamente por um nível em um pino)
- IN: lê o estado em um ou mais pinos de entrada
- OUT: posiciona um ou mais pinos de saída
- PUSH: coloca um valor na fila de recepção
- PULL: lê um valor da fila de transmissão
- MOV: copia valor entre dois registradores/pinos
- IRQ: controla o sinal de interrupção
- SET: coloca um valor constante em um registrador ou nos pinos
O datasheet do RP2040 descreve com detalhes cada uma destas instruções.
Programa MicroPython para Comunicar com os Sensores DHT11 e DHT22
O port do MicroPython para a Raspberry Pi Pico permite codificar programas para a PIO diretamente dentro do fonte Python.
import utime import rp2 from rp2 import PIO, asm_pio from machine import Pin # Programa para o PIO # coloca automaticamente na fila a cada 8 bits recebidos @asm_pio(set_init=(PIO.OUT_HIGH),autopush=True, push_thresh=8) def DHT_PIO(): # aguarda uma solicitação do programa pull() # mantem dado em 0 pelo tempo informado pelo programa set(pindirs,1) #set pin to output set(pins,0) #set pin low mov (x,osr) label ('waitx') nop() [25] jmp(x_dec,'waitx') # espera tempo*26/clock=x # inicia leitura da resposta set(pindirs,0) # muda o pino para entrada wait(1,pin,0) # aguarda voltar ao nível alto wait(0,pin,0) # aguarda pulso inicial wait(1,pin,0) wait(0,pin,0) # aguarda inicio do primeiro bit # lê os bits label('readdata') wait(1,pin,0) # espera o sinal ir para nivel alto set(x,20) # registrador x é o timeout para descer label('countdown') jmp(pin,'continue') # continua contando se sinal permanece alto # pino foi para o nível baixo antes da contagem terminar -> bit 0 set(y,0) in_(y, 1) # coloca um 'zero' no resultado jmp('readdata') # ler o próximo bit # pino continua no nível alto label('continue') jmp(x_dec,'countdown') # decrementar a contagem # contagem terminou -> bit 1 set(y,1) in_(y, 1) # coloca um 'um' no resultado wait(0,pin,0) # espera voltar ao nível baixo jmp('readdata') # ler o próximo bit DHT11 = 0 DHT22 = 1 class DHT: # Construtor # dataPin: pino de dados # modelo: DHT11 ou DHT22 # smID: identificador da máquina de estados def __init__(self, dataPin, modelo, smID=0): self.dataPin = dataPin self.modelo = modelo self.smID = smID self.sm = rp2.StateMachine(self.smID) self.ultleitura = 0 self.data=[] # faz uma leitura no sensor def leitura(self): data=[] self.sm.init(DHT_PIO,freq=1400000,set_base=self.dataPin,in_base=self.dataPin,jmp_pin=self.dataPin) self.sm.active(1) if self.modelo == DHT11: self.sm.put(969) # espera 18 milisegundos else: self.sm.put(54) # espera 1 milisegundo for i in range(5): # lê os 5 bytes da resposta data.append(self.sm.get()) self.sm.active(0) total=0 for i in range(4): total=total+data[i] if data[4] == (total & 0xFF): # checksum ok, salvar os dados self.data = data self.ultleitura = utime.ticks_ms() return True else: return False # le ou usa dados já existentes def obtemDados(self): # garante ter dados while len(self.data) == 0: if not self.leitura(): utime.sleep_ms(2000) # só tenta ler se já passou pelo menos 2 segundos da última leitura agora = utime.ticks_ms() if self.ultleitura > agora: self.ultleitura = agora # contador deu a volta if (self.ultleitura+2000) < agora: self.leitura() # informa a umidade def umidade(self): self.obtemDados() if self.modelo == DHT11: return self.data[0] + self.data[1]*0.1 else: return ((self.data[0] << 8) + self.data[1]) * 0.1 # informa a temperatura def temperatura(self): self.obtemDados() if self.modelo == DHT11: return self.data[2] + self.data[3]*0.1 else: s = 1 if (self.data[2] & 0x80) == 1: s = -1 return s * (((self.data[2] & 0x7F) << 8) + self.data[3]) * 0.1 #main program dht_data = Pin(16, Pin.IN, Pin.PULL_UP) dht = DHT(dht_data, DHT11, 0) # Torque por DHT22 se usar este modelo while True: print("Umidade: %.1f%%, Temperatura: %.1fC" % (dht.umidade(), dht.temperatura())) utime.sleep_ms(3000)
Obs: A programação do PIO no programa acima foi adaptada de https://github.com/ashchap/PIO_DHT11_Python, aproveitando ideias de da https://github.com/danjperron/PicoDHT22.
Para usar este código você precisa:
- Instalar no seu computador a IDE Thonny
- Baixar o interpretador MicroPytho para a Raspberry Pi Pico
- Apertar o botão de Boot da Raspberry Pi Pico e ligá-la a um PC, a placa deve ser reconhecida como um pendrive
- Copiar o interpretador para o pendrive
- Executar o Thonny. No canto inferior direito selecione o interpretador MicroPython (Raspberry Pi Pico).
Você encontra estas instruções mais detalhadas no post “Primeiros passos com Python na Raspberry Pi Pico” da Rosana Guse.
Montagem para Teste
Você vai precisar de:
- Uma Raspberry Pi Pico, com os pinos soldados
- Uma protoboard
- Um módulo DHT11 ou DHT22
- Fios para conexão
- Cabo micro USB para conectar a Pi Pico ao PC
As figuras abaixo mostram as conexões dos módulos DHT11 e DHT22 ao Raspberry Pi Pico:
⚠ Se você for usar um módulo diferente dos indicados, confira a pinagem!
Resultado e Conclusão
A figura abaixo mostra o programa em execução com um DHT22:
Agora que você já conhece um pouco sobre o PIO da Raspberry Pi Pico e como conectar sensores DHT11 e DHT22, que tal fazer algum projeto com eles?
Se você tiver dúvidas, sugestões ou experiências para compartilhar, fique a vontade para deixar um comentário abaixo. E para não perder nenhum conteúdo como este, não deixe de nos seguir no Instagram.