Como utilizar o PIO da Raspberry Pi Pico para Comunicar sensores DHT11 ou DHT22 - MakerHero
Como utilizar o PIO da Raspberry Pi Pico para Comunicar sensores DHT11 ou DHT22

Como utilizar o PIO da Raspberry Pi Pico para Comunicar sensores DHT11 ou DHT22 Deixe um comentário

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.

Como utilizar o PIO da Raspberry Pi Pico para Comunicar sensores 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:

  1. Configurar o pino conectado ao sensor como saída
  2. Manter o pino em nível baixo por pelo menos 18ms (DHT11) ou 1ms (DHT22)
  3. Reconfigurar o pino para entrada
  4. 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
  5. Em seguida, o sensor vai enviar 40 bits de resposta. Cada bit é composto de duas partes:
    1. Um nível baixo por 50us
    2. Um nível alto de 26 a 28uS (se bit 0) ou 70uS (se bit 1)
Como utilizar o PIO da Raspberry Pi Pico para Comunicar sensores DHT11 ou DHT22
À esquerda uma leitura completa, à direita o início ampliado

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.

Como utilizar o PIO da Raspberry Pi Pico para Comunicar sensores DHT11 ou DHT22

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:

As figuras abaixo mostram as conexões dos módulos DHT11 e DHT22 ao Raspberry Pi Pico:

Como utilizar o PIO da Raspberry Pi Pico para Comunicar sensores DHT11 ou DHT22

  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:

Como utilizar o PIO da Raspberry Pi Pico para Comunicar sensores DHT11 ou 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.

Faça seu comentário

Acesse sua conta e participe