Thursday, July 14, 2022
HomeOperating SystemRaspberry Pi HAT tutorials half 2 – Blinkenlights and micro-Pong!

Raspberry Pi HAT tutorials half 2 – Blinkenlights and micro-Pong!


Photograph by Feelfarbig Journal, unsplash

Welcome to half 2 of our Raspberry Pi HAT tutorial collection written by our resident Pi builders, Dave ‘waveform’ Jones and William ‘jawn-smith’ Wilson.

On this put up, they educate us how you can construct a handheld micro-Pong machine with the Unicorn HAT Mini and observe it up with a system monitor on the Unicorn HAT HD.

Try half 1, the place William received us began with the Unicorn pHAT and Dave shared his piwheels challenge dashboard!

It is a visitor put up from William’s personal weblog, which he’s kindly allowed us to share right here. Try his web site for extra nice Pi tutorials in addition to some equally colourful 3D printing initiatives.

That’s sufficient from me, over to William and Dave.


As of Ubuntu 22.04, the Raspberry Pi Pimoroni Unicorn HATs are supported on Ubuntu out of the field. This consists of the usual Unicorn Hat, Unicorn pHAT, Unicorn HAT Mini, and Unicorn HAT HD.

To put in the libraries for every HAT, run the next instructions:

sudo apt set up python3-unicornhat
sudo apt set up python3-unicornhathd
sudo apt set up python3-unicornhatmini

Under are some examples of how you can use them!

Tutorial: Micro-Pong on the Unicorn HAT Mini

Part written by William Wilson

Whereas small, the Unicorn HAT Mini has extra pixels than the pHAT so it might show rather more. It additionally has 4 buttons, making issues like easy video games attainable. This instance is a quite simple recreation of Pong that I’ve created. Each time the ball hits a paddle, the colour of the ball modifications and the sport quickens a bit of.

import time
import math
import random
import colorsys

from gpiozero import Button
from unicornhatmini import UnicornHATMini

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

class Pong():
    def __init__(self):
        # begin the paddles roughly midway 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.colours = (
            (255, 0, 0),
            (0, 255, 0),
            (0, 0, 255),
            (255, 255, 0),
            (255, 0, 255),
            (0, 255, 255),
            (255, 255, 255)
        )
        self.coloration = random.alternative(self.colours)

    def l_paddle_down(self):
        if self.l_paddle_y < peak - 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 < peak - 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 replace(self):
        # test if the sport is over
        if self.ball_x in (0, width - 1):
            self.game_on = False
            return

        # clear the board state
        unicornhatmini.clear()

        # set the place of the paddles
        for i in vary(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 subsequent place of the ball
        ball_x_next = self.ball_x + self.ball_horiz
        ball_y_next = self.ball_y + self.ball_vert

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

            # for the reason that ball hit a paddle
            # scale back the delay to hurry up the sport
            self.delay -= 0.01

            # change the colour of the ball each time
            # the ball hits a paddle
            self.coloration = random.alternative(self.colours)

        # test if the ball must 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.coloration)

        # present the sport state
        unicornhatmini.present()

pong_game = Pong()

button_a = Button(5)   # left paddle up
button_b = Button(6)   # left paddle down
button_x = Button(16)  # proper paddle up
button_y = Button(24)  # proper 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

whereas pong_game.game_on:
    pong_game.replace()
    time.sleep(pong_game.delay)

Pimoroni has many extra examples on their GitHub repo!

Tutorial: Unicorn HAT HD system monitor

Part written by Dave Jones

Moar Pixels!

The Unicorn HAT HD has an outrageous variety of neopixels, simply ready to spew rainbows at unsuspecting eyeballs. A lot so, it appears a disgrace to waste all of them on one thing as mundane as a two-tone monitoring service. Let’s strive for one thing extra … colourful!

Previous computer systems had big arrays of lights, now typically referred to (barely tongue in cheek) as blinkenlights. These typically visualized precise machine registers, bus addresses and the like. Lately, reminiscence is just too giant and too quick to make this convenient. Nevertheless, it’s pretty typical (on an Ubuntu for Raspberry Pi desktop picture operating a number of issues like a browser, terminal, and so forth.) to have various userland processes that will match fairly properly inside the 256 neopixels on the Unicorn HAT HD.

Let’s construct a contemporary blinkenlights variation that visualises the method desk. We’ll use crimson to characterize CPU utilization, inexperienced to characterize reminiscence utilization, and blue to characterize the inverted age of a course of (so model new processes will present up brilliant blue, then fade as they age).

We will use ps to question the method desk. We’ll exclude PID 2, which on Linux represents the kernel, and any course of underneath PID 2 (the kernel’s varied “threads” that are largely static and thus not very visually fascinating). We’ll ask ps for cp (per-mill CPU utilization), rss (resident set dimension, a measure of the quantity of precise RAM a course of is utilizing), and etimes (age of the method in seconds). Lastly, we’ll flip off the headers as a result of we don’t want them:

$ 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
  ....

This seems cheap, so let’s get coding. First, some imports:

#!/usr/bin/python3

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

import unicornhathd

Subsequent, our foremost monitor perform:

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

    processes = {
        # coord  CPU   mem   age
        (x, y): (None, None, None)
        for y in vary(peak)
        for x in vary(width)
    }
    limits = {'max-cpu': 0, 'max-mem': 0}
    updates = UpdateThread(processes, limits)
    updates.begin()
    strive:
        whereas True:
            if limits['max-cpu']:
                for (x, y), (cpu, mem, age) in processes.objects():
                    if cpu just isn't 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.present()
            sleep(1/30)
    lastly:
        unicornhathd.off()
        updates.cease()
        updates.be part of()

That is pretty easy however let’s undergo it piece by piece. We begin off by initializing:

  • The Unicorn HAT HD itself, with the unicornhathd module. We additionally seize its form (which ought to at all times be 16×16).
  • processes – a dictionary mapping coordinates on the show to a three-tuple representing CPU utilization, RAM utilization, and course of age. These can be None by default (for any pixel not at present representing a course of).
  • limits – a easy dictionary performing as a solution to go a few different values (the present most CPU and reminiscence utilization) between the replace thread and the primary thread.
  • updates – an occasion of UpdateThread (which we’ll outline a bit in a while), which we begin earlier than coming into the primary loop.

The principle loop merely checks that now we have a sound max-cpu worth then updates the show from the values within the processes dictionary with some easy maths. All of the values are scaled to the utmost CPU and reminiscence utilization so we get a pleasant distribution of coloration.

Now the UpdateThread itself:

class UpdateThread(Thread):
    def __init__(self, processes, limits):
        tremendous().__init__(goal=self.replace, args=(processes, limits),
                         daemon=True)
        self._done = Occasion()

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

    def replace(self, processes, limits):
        cmdline = shlex.cut up(
            'ps --pid 2 --ppid 2 --deselect -o cp,rss,etimes --no-headers')
        whereas not self._done.wait(1/30):
            proc = sp.run(cmdline, capture_output=True, textual content=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.cut up())
                    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

That is additionally pretty easy; the replace methodology simply calls ps and loops over the strains of output, sticking values into the processes dictionary and updating the limits dictionary with the largest CPU and reminiscence values discovered on the finish.

Grasp on a minute … how is that this protected? Certainly each the “foremost” thread, and our background replace thread are going to be bashing away at these constructions concurrently?! In most languages this may certainly be a no-no. In Python it’s protected, offered that the dictionary doesn’t develop or shrink whereas we’re iterating over it.

Observe: The limitation on not inserting into / deleting from dictionaries whereas iterating over them is the first motive for the construction of each these demo scripts. The mounted dimension of the dictionaries in each circumstances avoids the necessity for any express locking, maintaining the scripts each easy and fairly environment friendly.

For these inquisitive about what goes on underneath the covers, there may be nonetheless some locking happening: the Python GIL is making certain that each threads depart the constructions in a constant state between Python statements (really between bytecodes, however let’s not cut up hairs!).

It’s additionally price noting that, as there’s no locking, we will’t assure that  the limits are literally correct when the primary show thread is iterating over the processes dictionary. That is why we’re utilizing min and max to clamp the calculation leads to the primary thread.

Lastly, we simply must kick issues off:

if __name__ == '__main__':
    monitor()

And we’re executed! Run the complete script (you don’t want sudo for this one because the Unicorn HAT HD has a really completely different management mechanism) and bask within the glow of das blinkenlights!

Strive stopping and beginning your net browser whereas it’s operating and see simply what number of processes a contemporary browser encapsulates.

You could be stunned to see processes showing and disappearing continuously on the “finish” of the desk. That is really fairly regular for a contemporary system. You may additionally,see that the top of the desk has one persistently crimson (CPU load) and one persistently blue (“new”) course of, when your system is idle. Have a take into consideration which processes these could be! Specifically, why is one course of at all times “new” …

Press Ctrl+C to exit the script.

In the event you want to run the script routinely on boot, place this service definition in /and so forth/systemd/system/das-blinkenlights.service (this assumes you’ve saved the script as /usr/native/bin/blinkenlights.py):

[Unit]
Description=Unicorn HAT HD primarily based course of desk monitor
After=local-fs.goal

[Service]
Kind=easy
Consumer=ubuntu
Restart=on-failure
ExecStart=/usr/bin/python3 /usr/native/bin/blinkenlights.py

[Install]
WantedBy=multi-user.goal

Then run the next and you need to discover that the monitor will begin routinely on the subsequent reboot:

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

Now, you may maintain monitor of your system efficiency with out digging round in your machine.


And that’s all for this collection! An enormous because of William and Dave for permitting us to breed their tutorials.

If these concepts have sparked the creativeness, don’t neglect you may share your HAT initiatives within the Raspberry Pi class on the Ubuntu Discourse!

For recommendations on getting began with the Raspberry Pi in addition to additional challenge concepts, try among the hyperlinks beneath.

Tutorials

Tasks

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments