
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.