7 Ejemplos para programar tu Raspberry Pi Pico

En este tutorial te enseñaremos a programar la Raspberry Pi Pico por medio de 7 ejemplos que puedes seguir paso a paso. Antes de comenzar asegurate de tener instalado micropython en la Raspberry Pi Pico, e instalar Thonny en tu PC . Si aun no lo haces, revisa este tutorial “Raspberry Pi Pico paso a paso” para obtener más información sobre como hacerlo

Lecturas Recomendadas

Antes de continuar te recomentamos leer los siguiente tutoriales:

¿Cómo usar una Protoboard / Breadboard?
Modulación por ancho de pulsos PWM
Voltaje, Corriente, Resistencia, y Ley de Ohm

Materiales que utilizaremos

Los siguientes son los materiales que necesitarás para realizar las 7 experiencias:

  • Led verde x2
  • Led amarillo x 2
  • Led rojo x 2
  • Resistencias 330ohm x 10
  • Buzzer x 1
  • Botones x 2
  • Cables macho macho x 20
  • Raspberry Pi Pico x 1
  • Breadboard x1
  • Cable micro USB x1

Puedes comprar todos los materiales por separado o bien te recomendamos este kit que incluye todo lo que necesitas para estos ejemplos. https://mcielectronics.cl/shop/product/kit-raspberry-pi-pico-mci-electronics-29740/

Experiencia 1: ¿Cómo encender un Led?

La Rpi Pico incorpora un LED. En nuestra primera experiencia lo utilizaremos para comprobar de que todo está configurado correctamente. Adicionalmente veremos como utilizar una salida digital.

En Thonny, crea el sigiuente código (file->new)

import machine
import utime
#Configurar el pin del LED interno como salida
LED = machine.Pin(25,machine.Pin.OUT)
while True:
	LED.value(1)
	utime.sleep_ms(500) 
	LED.value(0)
	utime.sleep_ms(500) 

Guarda el código. Thonny te ofrecerá la opción de guardar el código en tu Rasperry Pi Pico o en tu PC. La Raspberry Pi Pico tiene una cantidad de memoria limitada de almacenamiento, pero es lo suficientemente grande para guardar todos los ejemplos de este tutorial.

Para cada experiencia, crea un nuevo archivo y guardalo con el nombre de le experiencia.

Si llamas al archivo main.py entonces cada vez que la Rpi Pico se reinicie correrá este código. Esto es útil cuando quieres correr el código sin que la tarjeta esté conectada a un PC.

Una vez que hayas guardado el archivo lo puedes correr utilizando el botón ‘play’

Diagrama del Circuito, Experiencia 1

El LED incorporado está conectado al pin 25, por lo que lo configuraremos como una salida, y luego en el ciclo while True: escribiremos 1 para encender el LED y 0 para apagarlo. Debido a que la Rpi Pico puede ejecutar el código muy rápido, le pedimos que duerma un poco entre el encendido y apagado, en este caso, 500 ms, o 1/2 segundo.

Debido a que el código tiene un ciclo while True: una vez que comience a correr el código, este se ejecutará para siempre. Para cambiar el código o guardarlo, debemos presionar el botón “stop” en el IDE Thonny.

Experiencia 2: Utiliza una entrada para controlar una salida

El objetivo de este experiencia es aprender como utilizar un botón como entrada para la Raspberry Pi Pico y como procesar una entrada para controlar una salida

Los microcontroladores son comúnmente utilizados para controlar algo como respuesta a una entrada que viene del mundo físico. En esta experiencia utilizaremos un botón para controlar el encendido y apagado del LED incorporado en la tarjeta.

Componentes a utilizar:

1 x Botón
3 x Cables macho macho

import machine
#Configurar el pin del LED interno como salida
LED = machine.Pin(25,machine.Pin.OUT)
Button = machine.Pin(0,machine.Pin.IN,machine.Pin.PULL_DOWN)
while True:
	LED.value(Button.value())
Diagrama del circuito, Experiencias 2 y 3

El botón está conectado a la salida 3v3 de la Raspberry Pi Pico, por lo que al presionarlo, la entrada subirá a 3.3 V y generará un ‘1’ lógico. Debido a que el pin está configurado como una entrada, no necesitamos preocuparnos por limitar la corriente que extraemos de la salida de 3.3v de la Rpi Pico. Los pines de entrada generalmente tienen una resistencia bastante alta, por lo que la cantidad de corriente que toma una entrada es pequeña.

El código configura un pin de entrada en GP0. Los pines digitales tienen solo 2 estados, ‘1’ o ‘0’. Cuando configuramos el pin, le indicamos que esté predeterminado en el valor ‘0’, esto lo hacemos con la configuración Pull Down. Esto activa una resistencia interna en la Raspberry Pi Pico que conecta el pin a 0V.

Luego usamos el valor que se lee del botón directamente para controlar el LED integrado.

Debido a que la Raspberry Pi Pico ejecuta el código rápidamente, pareciera que el botón está directamente controlando el LED.

Experiencia 3: Interrupciones

El objetivo de este experimento es:

  • Aprender que es una interrupción
  • Aprender cómo manejar una interrupción
  • Aprender como controlar una GPIO cuando ocurre una interrupción

Componentes a utilizar:

1x botón
3 x cable macho macho

En lugar de hacer un bucle rápido para leer el valor del botón, los microcontroladores también pueden ser notificados de un cambio en forma inmediata. Podemos hacer esto con una interrupción, también conocida como IRQ (Interrupt ReQuest).

Una IRQ permite que el procesador sea interrumpido de su ciclo principal para hacer una cosa, como lidiar con presionar un botón, para luego volver a el código que estaba ejecutando anteriormente.

import machine
#Setup the onboard LED Pin as an output
LED = machine.Pin (25, machine.Pin.OUT)
Button = machine.Pin (0,machine.Pin.IN, machine.Pin.PULL_DOWN)
LEDState = False
#This IRQ Handler toggles the variable we use to light the
#LED. It should, but doesnt check which pin raised the IRQ,
# as we only have 1 pin wired.
def ButtonIRQHandler (pin):
    global LEDState
    if LEDState == True:
        LEDState = False
    else:
        LEDState = True
#setup the IRQ and hook it to the handler
Button.irq (trigger = machine.Pin.IRQ_RISING, handler = ButtonIRQHandler)
#now loop and light the LED with the LED State
while True:
    LED.value (LEDState)

En este código creamos una rutina de manejo de interrupción IRQ, este código se ejecuta cuando se genera una interrupción al presionar el botón. El controlador también se puede denominar Rutina de servicio de interrupción (ISR). Configuramos la IRQ para que ocurra en un flanco ascendente, es decir, cuando ocurra la transición de 0 a 1. También hay otras opciones disponibles, como el flanco descendente, de 1 a 0, o cuando la entrada es alta o baja. Cuando se presiona el botón, los procesadores interrumpen su ejecución normal y hacen que se llame la rutina de servicio de interrupciones para que se ejecute. Luego, el controlador cambia el estado de una variable antes de salir.

En Python, las variables no tienen que tener un nombre único. Cuando se declara una variable tambien se establecen los límites en los que ésta se puede utilizar. El uso de la palabra global informa a Python que queremos modificar la variable LEDState que ya existe, en lugar de crear una variable llamada LEDState solo para usarla dentro del controlador IRQ. Debido a que IRQs puede provenir de muchas fuentes, el controlador generalmente comprueba si debe ejecutarse. En este caso hemos omitido el código que hace esto. Veremos un ejemplo en el siguiente experimento.

El bucle principal usa la misma variable para controlar el LED. De esta forma, la IRQ y el bucle principal pueden ‘comunicarse’. Cuando la IRQ se está ejecutando, el bucle principal se detiene o se interrumpe. Esta es la razón por la que el código en los controladores de interrupciones debe ser lo más simple posible y nunca tener un ciclo infinito; en ese caso, la interrupción nunca se cerrará y el programa principal nunca se reanudará.

Experiencia 4: Haciendo ruido (PWM)

El objetivo de este experimento es:

  • Aprender a conectar múltiples fuentes de interrupción a un controlador
  • Aprender a distinguir las fuentes de interrupción
  • Aprender cómo las computadoras simulan salidas analógicas

En este experimento usaremos 2 botones para controlar la IRQ, y luego cree una salida ‘analógica’ para controlar un zumbador piezoeléctrico.

Componentes a utilizar:

2 x botón
8 x cables macho macho
1 x buzzer (zumbador piezoeléctrico)

import machine
#Setup 2 digital inputs for buttons
ButtonA = machine.Pin (0, machine.Pin.IN, machine.Pin.PULL_DOWN)
ButtonB = machine.Pin (1, machine.Pin.IN, machine.Pin.PULL_DOWN)
#setup a PWM Output
Buzzer = machine.PWM (machine.Pin(15))
Buzzer.duty_u16 (32767) #make it 50% duty cycle (32767/65535)
Frequency = 1000 #set a starting frequency of 1 Khz
def ButtonIRQHandler (pin):
    global Frequency
    if pin == ButtonA: #up the frequency
        if Frequency < 2000:
            Frequency += 50
    elif pin == ButtonB: #lower the frequency
        if Frequency > 100:
            Frequency -= 50
#setup the IRQ and hook it to the handler
ButtonA.irq (trigger = machine.Pin.IRQ_RISING, handler = ButtonIRQHandler)
ButtonB.irq (trigger = machine.Pin.IRQ_RISING, handler = ButtonIRQHandler)
while True:
    Buzzer.freq (Frequency)
Diagrama del circuito, experiencia 4

El programa inicia su bucle principal activando el zumbador a través del objeto PWM. Seguirá haciendo esto para siempre. Como antes, tenemos un controlador IRQ (rutina de servicio de interrupción – ISR). Esta vez, sin embargo, hemos conectado 2 botones. El ISR comprueba qué botón hizo que se llamara y aumenta o disminuye la variable de frecuencia. Nuevamente usamos la palabra clave global para cambiar la versión de la variable en todo el programa, en lugar de crear una local. El controlador también verifica el valor de la variable Frecuency y la limita a 100 – 2000. Este es un buen rango de frecuencia para el zumbador y para que lo escuche la mayoría de la gente.

El sonido es una señal analógica. Las computadoras generalmente no pueden crear señales analógicas. Para simular salidas analógicas, la Raspberry pi Pico utiliza una técnica llamada modulación de ancho de pulso (PWM). Esto implica encender y apagar una señal digital rápidamente para que parezca analógica. Hay 2 parámetros para una señal PWM: la frecuencia de conmutación y el ciclo de trabajo: cuánto tiempo está encendida la señal en comparación con estar apagada.

El diagrama muestra 2 conjuntos de formas de onda digitales (encendido/apagado). El conjunto superior cambia el ciclo de trabajo, pero no la frecuencia. Con estas ondas el zumbador sonaría de un solo tono, pero con diferentes volúmenes. El conjunto inferior de formas de onda cambia la frecuencia, pero no el ciclo de trabajo. Con estas formas de onda, el zumbador tendría el mismo volumen, pero el tono cambiaría.

Esta técnica se utiliza a menudo para controlar la velocidad de un motor, donde al variar el ciclo de trabajo varía la potencia enviada al motor. Aquí estamos fijando el ciclo de trabajo al 50%, pero variando la frecuencia, lo que cambia el tono de la nota que hace el zumbador.

Experiencia 5: Mas interrupciones!

El objetivo de este experimento es:

  • Aprender sobre los LED y las resistencias de límite de corriente
  • Cómo hacer interrupciones independientes y usarlas para controlar salidas independientes

En este experimento, usaremos 2 botones para controlar de forma independiente 2 LEDs, uno en la placa y el LED integrado en la Rpi Pico.

El LED externo necesita una resistencia limitadora de corriente para que no nos arriesguemos a dañar el pin de salida la Raspberry Pi Pico.

Componentes a utilizar:

2 x botones
7 x cables macho macho
1 x Led Rojo
1 x Resistencia de 330 ohms

import machine
#Setup 2 digital outputs to drive LEDS
LED1 = machine.Pin (25, machine.Pin.OUT)
LED2 = machine.Pin (10, machine.Pin.OUT)
#Setup 2 digital inputs for buttons
ButtonA = machine.Pin (0, machine.Pin.IN, machine.Pin.PULL_DOWN)
ButtonB = machine.Pin (1, machine.Pin.IN, machine.Pin.PULL_DOWN)
#initialise the LEDs as 'Off'
LEDStatel = False
LEDState2 = False
#These IRQ Handlers toggle the variables we use to light
#the LEDs. They check which pin caused the IRQ so 1 button
#controls the onboard LED and the other button controls
#the breadboard LED
def ButtonAIRQHandler (pin):
    global LEDStatel
    if pin == ButtonA:
        if LEDStatel == True:
            LEDStatel = False
        else:
            LEDStatel = True
def ButtonBIRQHandler (pin):
    global LEDState2
    if pin == ButtonB:
        if LEDState2 == True:
            LEDState2 = False
        else:
            LEDState2 = True
#setup the IRQ and hook it to the handler
ButtonA.irq (trigger = machine.Pin.IRQ_RISING, handler = ButtonAIRQHandler)
ButtonB.irq (trigger = machine.Pin.IRQ_RISING, handler = ButtonBIRQHandler)
#now loop and light each LED with its LED State
while True:
    LED1.value (LEDStatel)
    LED2.value (LEDState2)
Diagrama del circuito, experiencia 5

La Raspberry Pi Pico puede conducir hasta 300 mA con su salida de alimentación de 3.3V. Para evitar que conduzca demasiada corriente al LED, usamos una resistencia limitadora de corriente. Los LED necesitan una resistencia de 330 ohms para limitar la corriente a alrededor de 4 mA por LED. Para obtener más información sobre los LED y las resistencias limitadoras de corriente, puedes revisar el siguiente tutorial Resistencias limitadoras de corriente para LEDs

El circuito ahora tiene 2 LED con los 2 botones. Los 2 botones ahora están conectados a 2 controladores de interrupción separados. Estos controladores modifican 2 variables separadas, que se usan en el bucle principal para controlar los 2 LED.

Al tener 2 ISRs separados, los LED se pueden operar de forma independiente y sin que el bucle principal tenga que sondear para verificar si se presionan los botones.

Se requiere la resistencia 330 ohms para controlar la cantidad de corriente que fluye en el circuito, limitándola a 2,7 mA. Esto es para asegurar que el flujo de corriente sea menor que el límite seguro para el pin de Pico (4mA) El flujo de corriente, el voltaje de suministro y el valor de resistencia están todos relacionados por la Ley de Ohm:

Voltaje (V, voltios) = corriente (I, amperios) x resistencia (R, ohmios)

Para determinar el valor de resistencia apropiado, se debe calcular la caída de voltaje en los diferentes componentes. Al mirar la hoja de datos del LED, se puede ver que el voltaje directo, Vf, es de 2.1V. Dado que el voltaje de suministro se establece en 3.3 V, la caída de voltaje en la resistencia se puede calcular de la siguiente manera:

3.3 – 2.1 = 1.2 V

Con la caída de voltaje calculada como 1,2 V y la corriente máxima dada como 4 mA (0.004 A), el valor de resistencia más bajo aceptable se calcula de la siguiente manera con la ecuación de la Ley de Ohm reorganizada:

R = V/I
R = 1.2 V/0.004 A
R = 300 ohms

El valor de resistencia disponible más cercano es 330 ohms (debe ser superior a 300 ohms para mantener la corriente por debajo de 4 mA), por lo que se ha utilizado en el circuito.

Experiencia 6: Programación Multi tarea

El objetivo de este experimento es:

  • Aprender sobre ‘hilos de ejecución’
  • Aprender que la Rpi Pico puede hacer 2 cosas al mismo tiempo
  • Aprender a crear y detener un hilo

La Raspberry Pi Pico tiene un procesador de doble núcleo, por lo que puede ejecutar 2 cosas a la vez.

Uno de ellos es el ciclo while True: principal. También podemos ejecutar una segunda pieza de código en el otro núcleo al mismo tiempo. la ejecución del código a menudo se denomina hilo de ejecución.

Componentes a utilizar:

1 x botón
9 x cables macho a macho
1 x led rojo
1 x led amarillo
1 x led verde
3 x resistencias de 330 ohm

import machine
import utime
import _thread
#Setup digital input for buttons.
ButtonA = machine.Pin (0, machine.Pin.IN, machine.Pin.PULL_DOWN)
#Setup 3 digital outputs to drive LEDs
Red = machine.Pin (10, machine.Pin.OUT)
Yellow = machine.Pin (11, machine.Pin.OUT)
Green = machine.Pin (12, machine.Pin.OUT)
#Setup a PWM Output
Buzzer = machine.PWM(machine.Pin(15))
Buzzer.duty_u16 (0) #Start with the buzzer off
Frequency = 1000 #set a frequency of 1 Khz
#Control Variable.
Beeping = False
#This is the thread routine, which will beep the buzzer
def Beep ():
    global Beeping
    OnTime = 50
    print ("Start Beeping Thread")
    while Beeping:
        Buzzer.duty_u16 (32767)
        utime.sleep_ms (OnTime)
        Buzzer.duty_u16 (0)
        utime.sleep_ms (1000-OnTime)
    print ("End Beeping Thread")
def ButtonAIRQHandler (pin):
    global Beeping
    if Beeping == False:
        print ("Start Beep")
        Beeping = True
        _thread.start_new_thread (Beep, ())
    else:
        Beeping = False #this causes the thread to exit
        print ("Stop Beep")
#setup the IRQ and hook it to the handler
ButtonA.irq (trigger = machine.Pin.IRQ_RISING, handler = ButtonAIRQHandler)
#The main loop runs the LEDs in a chase pattern
while True:
    Red.toggle()
    utime.sleep_ms(100)
    Yellow.toggle()
    utime.sleep_ms(100)
    Green.toggle()
    utime.sleep_ms(100)
Diagrama del circuito, experiencia 6

Tenemos un bucle principal que simplemente enciende y apaga los LED en forma repetitiva. El botón está conectado a un ISR. Esto significa que se dará servicio al presionar un botón y luego el bucle principal continuará.

Cuando se presiona el botón, activa el ISR para verificar la variable de control. Esto indica si el zumbador ya está sonando. Si no es así, establece el indicador de control en verdadero e inicia un segundo hilo, que hace que el zumbador haga ‘bip’ una vez por segundo. Cuando se vuelve a presionar el botón, el ISR restablece la bandera, lo que hace que finalice el bucle en el subproceso y, por lo tanto, el subproceso termine.

Los subprocesos se ejecutan de la misma manera que el resto del código: si tienen un bucle eterno, se ejecutarán para siempre, si solo tienen unas pocas declaraciones, entonces los ejecutarán y saldrán. En nuestro subproceso tenemos un bucle controlado por la variable ‘Beeping‘, por lo que el ISR puede detener la ejecución del subproceso. Esta es una forma de comunicación entre procesos, similar a la bandera en el ISR que se comunica con el bucle principal.

Si presiona el botón rápidamente, es posible que vea un mensaje de error en el Shell de Thonny; esto indica que estamos tratando de iniciar otro hilo, pero ambos núcleos ya están en uso. Porque el hilo ‘Beep’ tiene 1 segundo dormir como parte de su código puede tardar hasta 1 segundo en finalizar (dependiendo de en qué parte del ciclo se encuentre cuando se restablece la bandera).

Si el subproceso no ha salido cuando intentamos iniciar otro, obtenemos un error del sistema operativo que nos dice que el núcleo que queremos usar ya está en uso. Algunos procesadores pueden ejecutar varios subprocesos por núcleo, pero la Raspberry Pi Pico solo puede ejecutar un subproceso por núcleo, o 2 por procesador.

Experiencia 7: Poniendo en practica todo lo anterior, un ejemplo de la vida real:

El objetivo de este experimento es:

  • Crear y simular un semáforo
  • Usar interrupciones y subprocesos
  • Utilizar salidas digitales y analógicas y entradas digitales

Ahora tenemos todas las piezas para construir un sistema de semáforo, con LEDS, zumbador e interacción con el paso de peatones.

Componentes a utilizar:

2 x boton
14 x cables macho macho
2 x led rojo
2 x led amarillo
2 x led verde
6 x resistencia 330 ohms

import machine
import utime
import _thread
#Setup 2 digital inputs for buttons
ButtonA = machine.Pin (0, machine.Pin.IN, machine.Pin.PULL_DOWN)
ButtonB = machine.Pin (1, machine.Pin.IN, machine.Pin.PULL_DOWN)
#Setup 6 digital outputs to drive LEDs
Red = machine.Pin (10, machine.Pin.OUT)
Yellow = machine.Pin (11, machine.Pin.OUT)
Green = machine.Pin (12, machine.Pin.OUT)
PedestrianRed = machine.Pin (6, machine.Pin.OUT)
PedestrianWait = machine.Pin (7, machine.Pin.OUT)
PedestrianGreen = machine.Pin (8, machine.Pin.OUT)
#Setup a PWM Output for the beeping of a crossing
Buzzer = machine.PWM (machine.Pin (15))
Buzzer.duty_u16 (0) #Start with the buzzer off
Frequency = 1000 #set a frequency of 1 Khz
#Control Variable
CrossRequested = False
#This is a thread that takes care of beeping the buzzer
def PedestrianCross ():
    global CrossRequested
    PedestrianRed (0)
    PedestrianGreen (1)
    PedestrianWait (0)
    OnTime = 50
    print ("Beeping")
    for Beeping in range (10):
        Buzzer.duty_u16 (32767)
        utime.sleep_ms (OnTime)
        Buzzer.duty_u16 (0)
        utime.sleep_ms (1000-OnTime)
    print ("End Beep Thread")
    PedestrianRed (1)
    PedestrianGreen (0)
    CrossRequested = False
#Only set the request flag once if its already set we
#just exit the IRQ. 
#We dont need to check who asked for the cross, it does
#the same for both "sides" of the road - either Button
def ButtonIRQHandler (pin):
    global CrossRequested
    if CrossRequested == False:
        print ("Button Pressed")
        CrossRequested = True
        PedestrianWait.value (1) #Indicate to wait.
#setup the IRQ and hook it to the handler
ButtonA.irq (trigger = machine.Pin.IRQ_RISING, handler = ButtonIRQHandler)
ButtonB.irq (trigger = machine.Pin.IRQ_RISING, handler = ButtonIRQHandler)
#Setup the intial Light states
#Road stopped, pedestrian stopped, wait off.
Red.value (1)
Yellow.value (0)
Green.value (0)
PedestrianRed.value (1)
PedestrianGreen.value (0)
PedestrianWait.value (0)
#this sleep here is so the start set of Red occurs
utime.sleep (2)
#The main loop runs the LEDS one and off.
while True:
    #We start with the Traffic Lights on Stop,
    #so check if anyone wants to cross
    if CrossRequested == True:
        _thread.start_new_thread (PedestrianCross, ())
        #Now hang around until the pedestrian is done.
        while CrossRequested:
            utime.sleep (1)
    else: #drive the traffic light sequence
        Yellow.value (1)
        utime.sleep (1)
        Red. value (0)
        Yellow.value (0)
        Green.value (1)
        utime.sleep (2)
        Yellow.value (1)
        Green.value (0)
        utime.sleep (1)
        Red.value (1)
        Yellow.value (0)
        utime.sleep (2)
Diagrama del circuito, experiencia 7

El programa que simula un Semáforo tiene varias partes. El bucle principal ejecuta la secuencia habitual Rojo – Rojo y Amarillo – Verde – Amarillo – Rojo. Los botones representan los botones del cruce peatonal. Cuando un peatón quiere cruzar la calzada pulsa el botón. Se llama al ISR, ya que las entradas del botón están conectadas al ISR. Ambos botones están conectados al mismo ISR, ya que no importa de qué lado de la carretera la secuencia de cruce sea la misma. El ISR reconoce la solicitud encendiendo la luz amarilla de ‘esperar’. Luego se establece el indicador CrossRequested, que se utiliza para indicar al bucle principal que alguien quiere cruzar la calle.

El bucle principal comprueba este indicador cuando los semáforos están en rojo y, si está activado, inicia un subproceso independiente para gestionar la secuencia de semáforos para peatones. El hilo de cruce de peatones enciende las luces de peatones y también emite un pitido para indicar que es seguro cruzar.

Cuando el período de cruce ha expirado, el subproceso borra el indicador CrossRequested y luego sale del subproceso.

No es necesario usar un subproceso separado para la sección peatonal, mientras se ejecuta el bucle principal este solo está esperando. Sin embargo, separa muy bien las secciones del código e ilustra todas las cosas que se han aprendido en un solo programa. En un ejemplo más complejo, podrían estar sucediendo otras cosas en el ciclo principal mientras se ejecutaba el segundo subproceso.

¿Aún no tienes tu Raspberry Pi pico? Encuéntrala aquí: https://raspberrypi.cl/producto/raspberry-pi-pico/

Scroll al inicio