Noticias

Tutorial Raspberry Pi HAT Parte 2: ¡Blinkenlights y el micro-Pong!

Fotografiado por la revista Feelfarbig, sin salpicaduras

Bienvenido a la parte 2 de la serie de tutoriales Raspberry Pi HAT de nuestros desarrolladores residentes de Pi, Dave ‘waveform’ Jones y William ‘jawn-smith’ Wilson.

En esta publicación, nos enseñan cómo construir un dispositivo portátil de ping pong en miniatura con el Unicorn HAT Mini y seguir con el monitor del sistema en el Unicorn HAT HD.

Echa un vistazo a la Parte 1, William Comencemos con Unicorn pHAT, ¡y Dave comparte su tablero de proyectos de piwheels!

Esta es la publicación de invitado de William Blog, amablemente nos permitió compartirlo aquí. Visite su sitio web para obtener más tutoriales geniales de Pi y algunos proyectos de impresión 3D igualmente coloridos.

Eso es suficiente para mí, para William y Dave.


A partir de Ubuntu 22.04, Raspberry Pi Pimoroni Gorro Unicornio Soporte de Ubuntu fuera de la caja. Esto incluye el Unicorn Hat estándar, Unicorn pHAT, Unicorn HAT Mini y Unicorn HAT HD.

Para instalar la biblioteca para cada HAT, ejecute el siguiente comando:

sudo apt install python3-unicornhat
sudo apt install python3-unicornhathd
sudo apt install python3-unicornhatmini

¡Aquí hay algunos ejemplos de cómo usarlos!

Tutorial: Micro-Pong en Unicorn HAT Mini

Sección escrita por William Wilson

El Unicorn HAT Mini es pequeño, pero tiene más píxeles que el pHAT, por lo que puede mostrar más. También tiene cuatro botones que hacen posibles cosas como juegos simples. Este ejemplo es un juego de Pong muy simple que creé. Cada vez que la pelota golpea la raqueta, el color de la pelota cambia y el juego se acelera un poco.

import time
import math
import random
import colorsys

from gpiozero import Button
from unicornhatmini import UnicornHATMini

unicornhatmini = UnicornHATMini()
unicornhatmini.set_brightness(0.5)
width, height = unicornhatmini.get_shape()

class Pong():
    def __init__(self):
        # start the paddles roughly halfway vertically
        self.l_paddle_y = 3
        self.r_paddle_y = 3
        self.delay = 0.3
        self.ball_x = 1
        self.ball_y = 1
        self.ball_horiz = 1
        self.ball_vert = 1
        self.game_on = True
        self.paddle_height = 3
        self.colors = (
            (255, 0, 0),
            (0, 255, 0),
            (0, 0, 255),
            (255, 255, 0),
            (255, 0, 255),
            (0, 255, 255),
            (255, 255, 255)
        )
        self.color = random.choice(self.colors)

    def l_paddle_down(self):
        if self.l_paddle_y < height - self.paddle_height:
            self.l_paddle_y += 1

    def l_paddle_up(self):
        if self.l_paddle_y > 0:
            self.l_paddle_y -= 1

    def r_paddle_down(self):
        if self.r_paddle_y < height - self.paddle_height:
            self.r_paddle_y += 1

    def r_paddle_up(self):
        if self.r_paddle_y > 0:
            self.r_paddle_y -= 1

    def update(self):
        # check if the game is over
        if self.ball_x in (0, width - 1):
            self.game_on = False
            return

        # clear the board state
        unicornhatmini.clear()

        # set the position of the paddles
        for i in range(3):
            unicornhatmini.set_pixel(0,
                    self.l_paddle_y + i,
                    255, 255, 255)
            unicornhatmini.set_pixel(width - 1,
                    self.r_paddle_y + i,
                    255, 255, 255)

        # calculate the next position of the ball
        ball_x_next = self.ball_x + self.ball_horiz
        ball_y_next = self.ball_y + self.ball_vert

        # check if the ball needs to bounce off of a paddle
        if (
                (ball_x_next == 0 and ball_y_next in
                    (self.l_paddle_y + i for i in range(3))) or
                (ball_x_next == width - 1 and ball_y_next in
                    (self.r_paddle_y + i for i in range(3)))
            ):
            # the paddle has hit the ball, so change direction
            self.ball_horiz = -self.ball_horiz
            # recalculate ball_x_next
            ball_x_next = self.ball_x + self.ball_horiz

            # since the ball hit a paddle
            # reduce the delay to speed up the game
            self.delay -= 0.01

            # change the color of the ball every time
            # the ball hits a paddle
            self.color = random.choice(self.colors)

        # check if the ball needs to bounce off of an edge
        if (
                (self.ball_y == 6 and self.ball_vert == 1) or
                (self.ball_y == 0 and self.ball_vert == -1)
            ):
            self.ball_vert = -self.ball_vert
            ball_y_next = self.ball_y + self.ball_vert

        self.ball_x = ball_x_next
        self.ball_y = ball_y_next
        unicornhatmini.set_pixel(self.ball_x,
                self.ball_y,
                *self.color)

        # show the game state
        unicornhatmini.show()

pong_game = Pong()

button_a = Button(5)   # left paddle up
button_b = Button(6)   # left paddle down
button_x = Button(16)  # right paddle up
button_y = Button(24)  # right paddle down

button_a.when_pressed = pong_game.l_paddle_up
button_b.when_pressed = pong_game.l_paddle_down
button_x.when_pressed = pong_game.r_paddle_up
button_y.when_pressed = pong_game.r_paddle_down

while pong_game.game_on:
    pong_game.update()
    time.sleep(pong_game.delay)

Pimoroni y más ejemplo en su repositorio de GitHub!

Tutorial: Monitor de sistema Unicorn HAT HD

Sección escrita por Dave Jones

Píxeles de Moore!

Unicorn HAT HD tiene una asombrosa cantidad de nuevos píxeles esperando para escupir arco iris al ojo desprevenido. Tanto es así que parece una pena desperdiciarlos todos en algo tan mundano como un servicio de monitoreo de dos tonos. Probemos más… ¡colorido!

Las computadoras viejas tenían toneladas de luces, ahora comúnmente llamadas (un poco irónicas) luz intermitentePor lo general, estos son registros de máquina reales visualizados, direcciones de bus, etc. La memoria es demasiado grande y demasiado rápida para usarla en estos días. Sin embargo, es bastante típico (ejecutar algo como un navegador, terminal, etc. en una imagen de escritorio de Ubuntu para Raspberry Pi) que hay muchos procesos de usuario que encajan perfectamente en los 256 píxeles nuevos en Unicorn HAT HD.

Construyamos una variante moderna de luz parpadeante de una tabla de progreso visual. Usaremos el rojo para el uso de la CPU, el verde para el uso de la memoria y el azul para la edad invertida del proceso (por lo que un proceso completamente nuevo aparecerá en azul brillante y luego se desvanecerá a medida que envejece).

Podemos usar ps para consultar la tabla de procesos. Vamos a excluir PID 2, que en Linux representa el kernel, y cualquier proceso bajo PID 2 (los diversos «hilos» del kernel son en gran parte estáticos y, por lo tanto, no son muy interesantes visualmente).Preguntaremos PD por c.p. (Uso de CPU por planta), RSS (tamaño del conjunto residente, una medida de la cantidad real de RAM que utiliza un proceso), y era (Edad del proceso en segundos). Finalmente, desactivaremos los encabezados ya que no los necesitamos:

$ ps --pid 2 --ppid 2 --deselect -o cp,rss,etimes --no-headers
  1 10760   91980
  0 35128   91975
  0 23432   91975
  0  2916   91975
  0  3332   91971
  3  3364   91971
  0  7368   91970
  0  3868   91970
  0  3428   91969
  ....

Esto parece razonable, así que comencemos a codificar. Primero, algunas importaciones:

#!/usr/bin/python3

import shlex
import subprocess as sp
from time import sleep
from threading import Thread, Event
from itertools import zip_longest

import unicornhathd

A continuación, nuestras principales funciones de monitorización:

def monitor():
    unicornhathd.rotation(0)
    unicornhathd.brightness(1.0)
    width, height = unicornhathd.get_shape()

    processes = {
        # coord  CPU   mem   age
        (x, y): (None, None, None)
        for y in range(height)
        for x in range(width)
    }
    limits = {'max-cpu': 0, 'max-mem': 0}
    updates = UpdateThread(processes, limits)
    updates.start()
    try:
        while True:
            if limits['max-cpu']:
                for (x, y), (cpu, mem, age) in processes.items():
                    if cpu is not None:
                        r = min(255, int(255 * cpu / limits['max-cpu']))
                        g = min(255, int(255 * mem / limits['max-mem']))
                        b = max(0, min(255, int(255 * (10 - age) / 10)))
                        unicornhathd.set_pixel(y, x, r, g, b)
                    else:
                        unicornhathd.set_pixel(y, x, 0, 0, 0)
            unicornhathd.show()
            sleep(1/30)
    finally:
        unicornhathd.off()
        updates.stop()
        updates.join()

Es bastante simple, pero vayamos paso a paso. Empezamos con la inicialización:

  • Unicorn HAT HD en sí mismo, con unicornio módulo. También agarramos su forma (siempre debe ser 16×16).
  • proceso – Un diccionario que asigna coordenadas en la pantalla a triples que representan el uso de la CPU, el uso de la RAM y la antigüedad del proceso.estos serán no cualquiera Por defecto (para cualquier píxel que actualmente no represente un proceso).
  • límite – Un diccionario simple como una forma de pasar algunos otros valores (uso máximo actual de CPU y memoria) entre el hilo de actualización y el hilo principal.
  • renovar – una instancia actualizar hilo (que definiremos más adelante), empezamos antes de entrar en el bucle principal.

El ciclo principal simplemente verifica si tenemos un valor de CPU máxima válido, luego usa algunas matemáticas simples para actualizar la visualización del valor en el diccionario de procesos. Todos los valores se escalan al uso máximo de CPU y memoria, por lo que obtenemos una buena distribución de colores.

Ahora actualizar hilo sí mismo:

class UpdateThread(Thread):
    def __init__(self, processes, limits):
        super().__init__(target=self.update, args=(processes, limits),
                         daemon=True)
        self._done = Event()

    def stop(self):
        self._done.set()

    def update(self, processes, limits):
        cmdline = shlex.split(
            'ps --pid 2 --ppid 2 --deselect -o cp,rss,etimes --no-headers')
        while not self._done.wait(1/30):
            proc = sp.run(cmdline, capture_output=True, text=True)
            output = proc.stdout.splitlines()
            max_cpu = max_mem = 0
            for coord, line in zip_longest(processes, output):
                if not coord:
                    break
                elif line:
                    cpu, mem, age = (int(i) for i in line.split())
                    max_cpu = max(cpu, max_cpu)
                    max_mem = max(mem, max_mem)
                    processes[coord] = (cpu, mem, age)
                else:
                    processes[coord] = (None, None, None)
            limits['max-cpu'] = max_cpu
            limits['max-mem'] = max_mem

Esto también es bastante simple; esto renovar el método solo llama PD y recorra las líneas de salida, pegando los valores en proceso Diccionarios y Actualizaciones límite Finalmente encuentre el diccionario con los valores máximos de CPU y memoria.

Espera… ¿cómo es esto seguro? Seguramente tanto el hilo «principal» como nuestro hilo de actualización en segundo plano están atacando estas estructuras al mismo tiempo. ! En la mayoría de los idiomas, esto es realmente un no-no. En Python es seguro, si El diccionario no crece ni se reduce a medida que iteramos sobre él.

notas: La limitación de no insertar/eliminar diccionarios al iterar sobre diccionarios es la razón principal de la estructura de estos dos scripts de demostración. En ambos casos, el tamaño fijo del diccionario evita la necesidad de un bloqueo explícito, lo que hace que el script sea simple y razonablemente eficiente.

Para aquellos curiosos sobre lo que sucede detrás de escena, todavía hay algo de bloqueo: branquia de pitón Asegúrese de que los dos subprocesos mantengan la estructura en un estado coherente entre las declaraciones de Python (en realidad, entre códigos de bytes, ¡pero no nos bifurquemos!).

También vale la pena señalar que, dado que no hay bloqueo, no podemos garantizar límite Cuando el subproceso de visualización principal itera proceso diccionario.Esta es la razón por la que usamos minuto y máximo Sujete los resultados del cálculo en el hilo principal.

Finalmente, solo tenemos que empezar:

if __name__ == '__main__':
    monitor()

¡Hemos terminado!correr guion completo (Usted no necesita sudo ¡Porque el unicornio HAT HD tiene mecanismos de control muy diferentes) y está bañado por la luz de das blinkenlights!

Intente detener e iniciar su navegador web mientras se está ejecutando para ver cuántos procesos encapsulan los navegadores modernos.

Es posible que se sorprenda al ver que los procesos aparecen y desaparecen constantemente al «final» de la tabla. En realidad, esto es bastante normal para los sistemas modernos. También puede ver un proceso rojo continuo (carga de CPU) y azul continuo («nuevo») al final de la tabla cuando su sistema está inactivo. ¡Piense en qué procesos podrían ser estos!Especialmente por qué es un proceso siempre «nuevo» …

Presione Ctrl+C para salir del script.

Si desea que el script se ejecute automáticamente al inicio, agregue esta definición de servicio existir /etc/systemd/system/das-blinkenlights.servicio (Esto supone que ha guardado el script como /usr/local/bin/blinkenlights.py):

[Unit]
Description=Unicorn HAT HD based process table monitor
After=local-fs.target

[Service]
Type=simple
User=ubuntu
Restart=on-failure
ExecStart=/usr/bin/python3 /usr/local/bin/blinkenlights.py

[Install]
WantedBy=multi-user.target

Luego ejecute el siguiente comando y debería encontrar que el monitor se iniciará automáticamente en el próximo reinicio:

$ sudo systemctl daemon-reload
$ sudo systemctl enable das-blinkenlights

Ahora puede realizar un seguimiento del rendimiento de su sistema sin tener que buscar en su dispositivo.


¡Eso es todo por esta serie! Muchas gracias a William y Dave por permitirnos reproducir sus tutoriales.

Si estas ideas despiertan la imaginación, no olvides que puedes Categoría Raspberry Pi ¡Hablando de Ubuntu!

Para obtener consejos sobre cómo comenzar con una Raspberry Pi y más ideas de proyectos, consulte algunos de los enlaces a continuación.

tutorial

proyecto

Publicaciones relacionadas

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Botón volver arriba