ÍNDICE DE CONTEÚDO
- Raspberry PI – GPIO output com Python
- Raspberry PI – GPIO input com Python
- Raspberry PI – PWM com Python
- Raspberry Pi – Display LCD com Python
- Raspberry Pi – Projeto Termômetro Digital com Python – Teoria
- Raspberry Pi – Projeto Termômetro Digital com Python – Prática
- Raspberry Pi – Comunicação Serial (UART) entre a Raspberry Pi e Arduino em Python e Wiring
Introdução
A primeira parte da série de interação com o GPIO (General Purpose Input Output) do Raspberry PI no modo Output possui os passos fundamentais para esta segunda etapa como a preparação do ambiente. Nesta segunda etapa vamos ver o modo Input, e para a prática será utilizado o Raspberry PI B, protoboard, pushbutton e alguns resistores. Serão mencionados os possíveis modos de trabalho com Input utilizando resistores de pull-up e pull-down, internos ou externos, e recursos como detecção de borda, debounce por software e uma “interrupção” baseada na detecção por borda.
Configuração do ambiente host
-
Board: Raspberry PI B;
-
Distribuição: Raspbian – Debian Wheezy (2014-01-07);
-
Kernel 3.12.22;
-
Python 2.7.3;
-
RPi.GPIO 0.5.7.
RPi.GPIO
Já vimos na primeira parte da série as funções RPi.GPIO.setmode(), RPi.GPIO.setup(), RPi.GPIO.output(), RPi.GPIO.input() e RPi.GPIO.cleanup(), e agora vamos ver algumas funções e parâmetros que tornam o modo input muito interessante.
- RPi.GPIO.RISING => Borda de subida, quando passa de 0V para 3.3V;
- RPi.GPIO.FALLING => Borda de descida, quando passa de 3.3V para 0V;
- RPi.GPIO.BOTH => Os dois estados, ocorre ação tanto na subida [0V → 3.3V] quanto na descida [3.3V → 0V];
- RPi.GPIO.LOW => Nivel lógico baixo [low] ou 0V;
- RPi.GPIO.HIGH => Nivel lógico alto [high] ou 3.3V;
- RPi.GPIO.PUD_UP => Pino Input em modo pull-up;
- RPi.GPIO.PUD_DOWN => Pino Input em modo pull-down;
- RPi.GPIO.PUD_OFF => Padrão.
- RPi.GPIO.wait_for_edge() => Bloqueia a execução até que o botão seja pressionado ou a ação setada aconteça;
- RPi.GPIO.event_detected() => Guarda o estado de um evento no pino, e quando for verificado no loop, indica se houve ou não mudança no pino. Pode ser utilizado dentro do loop e não irá perder a informação;
- RPi.GPIO.add_event_detect() => Adiciona um evento baseado no pino, se o evento irá ocorrer na borda de descida [FALLING], subida [RISING] ou em ambos [BOTH]. Adiciona uma função para ser chamada e aceita parâmetro de tempo debounce;
- RPi.GPIO.remove_event_detect() => Permite remover um evento em qualquer parte do código se não for mais utilizado.
A Figura 1 ilustra os comportamentos que ocorrerão no decorrer do artigo ao acionar o pushbutton.
Olhando a Figura 1, fica fácil entender os parâmetros LOW (representado por 0V), HIGH (como 3.3V), Edge Rising ou RISING (transição de 0V a 3.3V), Edge Falling ou FALLING (transição de 3.3V a 0V) e o efeito bouncing. Esse último, se você não conhece, pode ver o artigo Leitura de chaves mecânicas e o processo de debounce, escrito pelo Rodrigo Almedia, indicando como contornar o problema via hardware e software, e neste artigo iremos estudar a solução via software.
PushButton com pull-down interno
O primeiro exemplo prático será adicionar um pushbutton. Uma de suas extremidades será conectada ao 3.3V (pino 1) e a outra ao pino 16 (ou GPIO 23) da Raspberry Pi no modo BCM, como na Figura 2. Configuraremos via software utilizando RPi.GPIO o pino 16 (GPIO 23) no modo INPUT e com pull-down interno.
Código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#! /usr/bin/python # -*- coding: utf-8 -*- import RPi.GPIO as gpio import time gpio.setmode(gpio.BCM) gpio.setup(23, gpio.IN, pull_up_down = gpio.PUD_DOWN) while True: if(gpio.input(23) == 1): print(“Botão pressionado”) else: print(“Botao desligado”) time.sleep(1) gpio.cleanup() exit() |
Executando o código:
1 2 3 4 5 6 7 8 |
pi@raspberrypi ~/python/rpio/ $ sudo python pushbutton_pulldown_interno.py Botão desligado Botão desligado Botão desligado Botão pressionado Botão pressionado Botão desligado Botão desligado |
PushButton com pull-up interno
No segundo exemplo prático o mesmo PushButton será utilizado, trocando apenas a extremidade de 3.3V (pino 1) para 0V GND (pino 6), como na Figura 3. Configuraremos via software utilizando RPi.GPIO o pino 16 (GPIO 23) no modo INPUT e com pull-up interno.
Código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#! /usr/bin/python # -*- coding: utf-8 -*- import RPi.GPIO as gpio import time gpio.setmode(gpio.BCM) # Com pull-up interno gpio.setup(23, gpio.IN, pull_up_down = gpio.PUD_UP) while True: if gpio.input(23) == gpio.LOW: print(“Botão acionado”) break else: printf(“Botão desligado”) time.sleep(1) gpio.cleanup() exit() |
O nosso código acima é praticamente o mesmo do exemplo anterior com algumas alterações como: o setup() do GPIO 23 como pull-up; o teste que verifica o estado do pino faz uso da constante gpio.LOW, que poderia ser 0, seguindo a analogia do exemplo anterior; o botão, quando pressionado, faz o programa parar.
Executando o código:
1 2 3 4 5 |
pi@raspberrypi ~/python/rpio/ $ sudo python pushbutton_pullup_interno.py Botao desligado Botao desligado Botao desligado Botao pressionado |
PushButton com pull-down externo
O próximo exemplo é utilizando resistores externos para configurar como pull-up ou pull-down e, como na Figura 4, vamos utilizar 2 resistores: um de 1k e outro de 10k. A configuração que foi utilizada é o resistor de 1k entre o GPIO e o PushButton e o 10k entre GPIO e o GND, caracterizando ser pull-down.
Código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#! /usr/bin/python # -*- coding: utf-8 -*- import RPi.GPIO as gpio import time """ Global """ PIN=23 # Usando modo BCM """ Funcoes """ def action_press_button(gpio_pin): print “O botão no pino %d foi pressionado!” % gpio_pin print “Saindo...” """ Configurando GPIO """ # Configurando o modo dos pinos como BCM gpio.setmode(gpio.BCM) # Configurando PIN como INPUT gpio.setup(PIN, gpio.IN) while True: if gpio.input(PIN) == gpio.HIGH: action_press_button(PIN) break else: print “Botão desligado” time.sleep(1) gpio.cleanup() exit() |
Executando o código:
1 2 3 4 5 6 |
pi@raspberrypi ~/python/rpio/ $ sudo python pushbutton_pulldown_externo.py Botão desligado Botão desligado Botão desligado O botão no pino 23 foi pressionado! Saindo... |
PushButton com pull-up externo
O esquema final da Figura 5, praticamente o mesmo que o anterior, apenas com a alteração do resistor 10k que é entre VCC (3.3V) com GPIO e o PushButton agora é entre o pino do GPIO e o GND, caracterizando ser pull-up. A mesma ideia deve-se seguir no código.
Código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
#! /usr/bin/python # -*- coding: utf-8 -*- import RPi.GPIO as gpio import time """ Global """ PIN=23 # Usando modo BCM """ Funcoes """ def action_press_button(gpio_pin): print “O botão no pino %d foi pressionado!” % gpio_pin print “Saindo...” """ Configurando GPIO """ # Configurando o modo dos pinos como BCM gpio.setmode(gpio.BCM) # Configurando PIN como INPUT gpio.setup(PIN, gpio.IN) while True: #if gpio.input(PIN) == gpio.LOW: if gpio.input(PIN) == 0: action_press_button(PIN) break else: print “Botão desligado” time.sleep(1) gpio.cleanup() exit() |
Executando o código:
1 2 3 4 5 6 7 8 |
pi@raspberrypi ~/python/rpio/ $ sudo python pushbutton_pullup_externo.py Botão desligado Botão desligado Botão desligado Botão desligado Botão desligado O botão no pino 23 foi pressionado! Saindo... |
Não errei o código não, é que utilizando resistores externos altera-se apenas a linha do if de gpio.HIGH para gpio.LOW. No caso ainda dei uma valorizada, onde troquei gpio.LOW por 0.
Eventos e Interrupções
Os exemplos utilizados até agora foram legais e interessantes, porém na prática sabemos que é um pouco inconveniente aguardar o loop ou, como no exemplo que fizemos, utilizarmos um delay de 1s. Em alguns casos isso é um absurdo e pode ser que a ação de pressionar o botão não seja capturada, sendo uma falha grave e que pode ser facilmente contornada com as funções de “detecção de eventos” event_detect do RPi.GPIO, que tem uma grande relação com Edge Rising (Borda de Subida), Edge Falling (Borda de Descida) ou Both (Ambos sentidos). Utilizaremos o esquemático da Figura 2 por ser mais simples de montar, o PushButton com pull-down e vamos ao código.
Código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# -*- coding: utf-8 -*- #! /usr/bin/python import RPi.GPIO as gpio import time """ Global """ PIN=23 """ Funcoes """ def action_press_button(gpio_pin): print “O botão no pino %d foi pressionado!” % gpio_pin print “Saindo...” """ Configurando GPIO """ # Configurando o modo dos pinos como BCM gpio.setmode(gpio.BCM) # Configurando PIN como INPUT e modo pull-donw interno gpio.setup(PIN, gpio.IN, pull_up_down = gpio.PUD_DOWN) # Adicionando um evento ao GPIO 23 na mudança RISING 0V[LOW] - > 3.3V[HIGH] gpio.add_event_detect(PIN, gpio.RISING) while True: print “Polling...” if gpio.event_detected(PIN): action_press_button(PIN) break time.sleep(1) gpio.cleanup() exit() |
Executando o código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
pi@raspberrypi ~/python/rpio/ $ sudo python pushbutton_event_detect01.py Polling... Polling... Polling... Polling... Polling... Polling... Polling... Polling... Polling... Polling... Polling... Polling... Polling... Polling... O botão no pino 23 foi pressionado! Saindo... |
Você pode testar, e pode ver que independente do momento que pressionar o pushbutton, na ação dele ser pressionado (RISING), indo de 0V para 3.3V, como estamos com pull-down, ele irá “guardar” esta mudança de estado. Na próxima vez que for executado o loop, este evento será tratado, no nosso caso sendo chamada a função action_press_button() e saindo do programa.
E se por acaso quiséssemos que a ação ocorresse ao soltar o pushbutton e não ao pressionar o mesmo?
Código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# -*- coding: utf-8 -*- #! /usr/bin/python import RPi.GPIO as gpio import time """ Global """ PIN=23 """ Funcoes """ def action_press_button(gpio_pin): print “O botão no pino %d foi pressionado!” % gpio_pin print “Saindo...” """ Configurando GPIO """ # Configurando o modo dos pinos como BCM gpio.setmode(gpio.BCM) # Configurando PIN como INPUT e modo pull-donw interno gpio.setup(PIN, gpio.IN, pull_up_down = gpio.PUD_DOWN) # Adicionando um evento ao GPIO 23 na mudança FALLING 3.3V[HIGH] - > 0V[LOW] gpio.add_event_detect(PIN, gpio.FALLING) while True: print “Polling...” if gpio.event_detected(PIN): action_press_button(PIN) break time.sleep(1) gpio.cleanup() exit() |
Executando o código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
pi@raspberrypi ~/python/rpio/ $ sudo python pushbutton_event_detect02.py Polling... Polling... Polling... Polling... Polling... Polling... Polling... Polling... Polling... Polling... Polling... Polling... Polling... Polling... Polling... Polling... Polling... Polling... Polling... Polling... Polling... Polling... Polling... Polling... O botão no pino 23 foi pressionado! Saindo... |
Não mudou nada na saída do código, tirando que o programa só parou quando pressionamos e soltamos o PushButton. Enquanto o botão estiver pressionado, nada acontece. O mesmo pode ser feito com BOTH, que a ação ocorrerá ao pressionar e soltar.
Outra opção interessante no event_detect() é poder adicionar uma função a ser chamada assim que ocorrer a mudança de estado, utilizando o parâmetro callback. Seria a correspondência de uma “Interrupção”, porém eu não gosto muito deste termo e prefiro aceitar o de uma chamada em paralelo ou execução já que uma segunda thread é disparada neste caso. Vamos ver!
Código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# -*- coding: utf-8 -*- #! /usr/bin/python import RPi.GPIO as gpio import time """ Global """ PIN=23 """ Funcoes """ def action_press_button_loop(gpio_pin): print “O botão no pino %d foi pressionado!” % gpio_pin print “Saindo...” def action_press_button(gpio_pin): print “Tratando o botão no pino %d que foi pressionado!” % gpio_pin """ Configurando GPIO """ # Configurando o modo dos pinos como BCM gpio.setmode(gpio.BCM) # Configurando PIN como INPUT e modo pull-donw interno gpio.setup(PIN, gpio.IN, pull_up_down = gpio.PUD_DOWN) # Adicionando um evento ao GPIO 23 na mudança FALLING 0V[LOW] - > 3.3V[HIGH] gpio.add_event_detect(PIN, gpio.RISING, callback=action_press_button) # Junto com o parametro callback podemos utilizar ainda o bouncetime # na linha abaixo estamos dizendo para ignorar nos primeiro 300ms # gpio.add_event_detect(PIN, gpio.RISING, callback=action_press_button, bouncetime=300) while True: print “Polling...” if gpio.event_detected(PIN): action_press_button_loop(PIN) break time.sleep(1) gpio.cleanup() exit() |
Executando o código:
1 2 3 4 5 6 7 8 9 10 11 12 |
pi@raspberrypi ~/python/rpio/ $ sudo python pushbutton_event_detect03.py Polling... Polling... Polling... Polling... Polling... Polling... Polling... Tratando o botão no pino 23 que foi pressionado! Polling... O botão no pino 23 foi pressionado! Saindo... |
Usando callback no add_event_detect(), podemos ver que existem “prioridades” na chamada, porque assim que o evento ocorre é lançada uma segunda thread para a chamada. Após isso vem a continuação do loop e depois é atendida nossa mudança de estado no próprio loop como antes. Assim conseguimos prioridade no tratamento de input independente do tamanho e tempo de nosso loop, caracterizando a interrupção dita. Comentando sobre a opção de utilizar o debounce via software, como na linha 30, com ou sem o callback, podemos definir o parâmetro bouncetime, que é um valor de tempo em ms para ser ignorado na detecção da borda.
A função remove_event_detect(), pode ser chamada e passado como parâmetro o pino. A qualquer momento o evento sobre o mesmo pino passa a não ser mais válido e você pode remover a ação sobre a borda naquele pino. Vamos ver no próximo código.
Código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
#! /usr/bin/python # -*- coding: utf-8 -*- import Rpi.GPIO as gpio import time """ Global """ PIN=23 """ Funcoes """ def action_press_button(gpio_pin): print "Você pressionou o botão do pino %d e agora ele sera desativado" % gpio_pin """ Configurando GPIO """ # Configurando o modo do GPIO como BCM gpio.setmode(gpio.BCM) # Configurando PIN's como INPUT e modo pull-down interno gpio.setup(PIN, gpio.IN, pull_up_down = gpio.PUD_DOWN) # Adicionando um evento ao GPIO 23 na mudança RISING 0V[LOW] -> 3.3V[HIGH] gpio.add_event_detect(PIN, gpio.RISING) while True: try: if gpio.event_detected(PIN): action_press_button(PIN) gpio.remove_event_detect(PIN) else: print("Botão Desligado") time.sleep(1) except (KeyboardInterrupt): print("Saindo...") gpio.cleanup() exit() |
Executando o código:
1 2 3 4 5 6 7 8 9 |
pi@raspberrypi ~/python/rpio/ $ sudo python pushbutton_event_detect04.py Botão desligado Botão desligado Botão desligado Você pressionou o botão no pino 23 e agora ele sera desativado Botão desligado Botão desligado Botão desligado ^CSaindo... |
E por último o wait_for_edge(), que podemos parar a execução do programa até que a mudança no pino ocorra, também sendo válido para a mudança RISING, FALLING ou BOTH.
Código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#! /usr/bin/python # -*- coding: utf-8 -*- import Rpi.GPIO as gpio import time """ Global """ PIN1=23 """ Configurando GPIO """ # Configurando o modo do GPIO como BCM gpio.setmode(gpio.BCM) # Configurando PIN's como INPUT e modo pull-down interno gpio.setup(PIN1, gpio.IN, pull_up_down = gpio.PUD_DOWN) while True: try: print “Inicio loop...” gpio.wait_for_edge(PIN1, gpio.RISING) print “Botão foi pressionado” gpio.wait_for_edge(PIN1, gpio.FALLING) print “Botão foi liberado” print “Continua loop...” time.sleep(1) except (KeyboardInterrupt): print “Saindo...” gpio.cleanup() exit() |
Executando o código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
pi@raspberrypi ~/python/rpio/ $ sudo python pushbutton_event_detect05.py Inicio loop... Botão foi pressionado Botão foi liberado Continua loop... Inicio loop... Botão foi pressionado Botão foi liberado Continua loop... Inicio loop... Botão foi pressionado Botão foi liberado Continua loop... Inicio loop... Botão foi pressionado Botão foi liberado Continua loop... ^CSaindo... |
Conclusão
Podemos ver mais uma vez como Python é uma ferramenta fantástica para prototipagem. Com poucas linhas e utilizando o RPi.GPIO conseguimos manipular de diversas maneiras o GPIO do Raspberry Pi no modo input.
O que vem por aí?
No próximo artigo vamos continuar brincando com o GPIO do Raspberry Pi, só que desta vez partindo para PWM.
Olá, muito obrigada pelo tutorial!
Todos os pinos da Rasp apresentam pull-up / pull-down interno? Qual seria a diferença em relação a usar um pull-up/pull-down externo?
Agradeço
Material Fantástico e Esclarecedor!!!
Os códigos de interrupção realmente funcionam? Testei no raspberry pi 3 e apenas fica retornando polling mesmo clicando no botão.
Olá Jonathan, com a RaspberryPI B que foi escrito o artigo funcionava, como faz pouco mais de 3 anos, e a biblioteca com certeza lançou novas versões assim como RaspberryPI precisaria testar novamente com este ambiente atualizado e verificar o que mudou, ficou devendo uma posição sobre o atual funcionamento.
Olá Cleiton,
Você pode me tirar uma dúvida? Qual a função do “break” dentro do if?
“while True:
if gpio.input(23) == gpio.LOW:
print(“Botão acionado”)
break
else:
printf(“Botão desligado”)”
Muito obrigado!
Olá Emerson,
o break neste caso serve para assim que o botão for pressionado sai do loop while True.
Olá Cleiton.
Apenas gostaria de agradecer pela brilhante explicação sobre os programas para Raspberry, você conseguiu tirar bastantes dúvidas minhas. Meu próximo passo, assim como o Roniere, é integrar o display de LCD com o controlador.
Muito obrigado! Abraços.
Obrigado Emerson, fico feliz que tenha gostado.
Um abraço.
Emerson, eu já consegui colocar para funcionar o LCD caso tenha alguma dúvida pode me procurar nas redes sociais que te ajudo.
Abraço!
Sua ajuda seria muito bem vinda Roniere. Estou trabalhando no projeto de TCC e isso vai alavancar uma boa parte do trabalho.
Olá Cleiton Bueno, o seu artigo está muito bom e atendeu meus objetivos de entender como utilizar as GPIOs como entrada. Mas pesquisei aqui sobre o seu próximo artigo sobre PWM e não o encontrei. Houve sequência nos seus artigos? Abraço!
Olá Roniere, que bom que gostou do artigo. Você tem razão era para ter uma sequencia com PWM, posso testar novamente, na época com a versão utilizada o PWM ficou muito ruim e com alto overload de CPU, não achei plausível de um artigo.
Acho que vou retestar para tentar cumprir esta promessa.
Um abraço.
Cleiton, esses dias eu fiz dois códigos usando o controle PWM. Um usando dois leds, um aumentava a sua luminosidade ao mesmo tempo que o outro iria diminuindo quando chegava nos seus limites retornavam ao estado inicia.O outro, eu controlava o duty cycle usando dois botões, um para aumenta e outro para dimunuir. Ficaram bem simples e interessantes para usuários iniciantes como eu. Se quiser, posso te envio os códigos e você faz o artigo em cima deles. Ou gostaria de sugerir, um artigo falando como usar display LCD na Raspberry, é isso que estou estudando como fazer agora. Abraço!
Legal Roniere, tenho uma proposta melhor para você, porque tu não escreve um artigo para o Embarcados? Eu ajudo na revisão.
Podemos pensar nisso Cleiton. Mas podemos conversar mais reservadamente sobre isso, pode me passar alguma forma de contatar você?
Cleiton, como podemos iniciar os trabalhos. Estou no seu aguardo!