Gruselige Süßigkeitentruhe (Raspberry Pi Build Hat)

In meinem Projekt zu Halloween 2023 habe ich eine gruselige Süßigkeitentruhe gebaut.

Hardware

Durch die Nutzung eines Raspberry Pis für dieses Projekt haben sich einige neue Möglichkeiten ergeben. Einerseits können mit dem Build hat 4 Powered Up Geräte direkt an den Raspberry Pi angeschlossen werden. Andererseits verfügt der Pi über viele gängige Schnittstellen wie USB und WLAN, sodass ich ein USB LEGO Dimensions Pad nutzen konnte.
Insgesamt habe ich die folgende Hardware genutzt:
  • Raspberry Pi 4
  • Raspberry Pi Build hat
  • Großer Winkelmotor
  • Powered Up Licht (x2)
  • Dimensions Toypad (nicht XBOX-Version)
Raspberry Pi 4 und Build hat steuern das komplete Modell. Das Powered Up Licht und das Dimensions Toypad sorgen für die richtige Stimmung. Außerdem kann das Dimensions Toypad NFC Tags lesen, um die Süßigkeiten in der Box für die richtige Person freigeben zu können. Zum Öffnen der Box ist ein großer Winkelmotor enthalten.
Neben den LEDs gibt es einige fluoreszierende Teile in dem MOC. Allerdings fallen sie in dem fertigen Modell nicht wirklich auf.

Quellcode (geschrieben in Python)

Für die Nutzung des Projektes muss neben dem Raspberry Pi das Raspberry Pi Build hat eingerichtet sein. Außerdem muss für das Dimensions Pad pyusb mit dem Befehl "sudo apt install python3-usb" installiert werden. Für das Dimensions Pad wurde anhand des musicfig-Projektes eine Klasse zur Ansteuerung in Python geschrieben. Diese ist unter der gleichen Lizenz wie das musicfig-Projekt lizensiert.

import usb.core
import usb.util
from time import sleep

TOYPAD_INIT = [0x55, 0x0f, 0xb0, 0x01, 0x28, 0x63, 0x29, 0x20, 0x4c, 0x45, 0x47, 0x4f, 0x20, 0x32, 0x30, 0x31, 0x34, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]


class Dimensions():

    def __init__(self):
        try:
           self.dev = self.init_usb()
        except Exception as e:
            print("initException")
            print(e)
            return

    def init_usb(self):
        dev = usb.core.find(idVendor=0x0e6f, idProduct=0x0241)

        if dev is None:
            print('Lego Dimensions pad not found')
            raise ValueError('Device not found')
        
        print("deviceFound")
        if dev.is_kernel_driver_active(0):
            dev.detach_kernel_driver(0)
        
        
        print(usb.util.get_string(dev, dev.iProduct))
        # Initialise portal
        dev.set_configuration()
        dev.write(1,TOYPAD_INIT)
        return dev

    def send_command(self, command):
        checksum = 0
        for word in command:
            checksum = checksum + word
            if checksum >= 256:
                checksum -= 256
            message = command+[checksum]

        while(len(message) < 32):
            message.append(0x00)

        try:
            self.dev.write(1, message)
        except Exception:
            pass

    def switch_pad(self, pad, colour):
        self.send_command([0x55, 0x06, 0xc0, 0x02, pad, colour[0], 
                          colour[1], colour[2],])
        return

    def fade_pad(self, pad, pulse_time, pulse_count, colour):
        self.send_command([0x55, 0x08, 0xc2, 0x0f, pad, pulse_time, 
                          pulse_count, colour[0], colour[1], colour[2],])
        return

    def flash_pad(self, pad, on_length, off_length, pulse_count, colour):
        self.send_command([0x55, 0x09, 0xc3, 0x03, pad, 
                          on_length, off_length, pulse_count, 
                          colour[0], colour[1], colour[1],])
        return

    def update_nfc(self):
        try:
            inwards_packet = self.dev.read(0x81, 32, timeout = 10)
            bytelist = list(inwards_packet)
            if not bytelist:
                return
            if bytelist[0] != 0x56:
                return
            pad_num = bytelist[2]
            uid_bytes = bytelist[6:13]
            #identifier = binascii.hexlify(bytearray(uid_bytes)).decode("utf-8")
            #identifier = identifier.replace('000000','')
            #removed = bool(bytelist[5])
            #if removed:
            #    response = 'removed:%s:%s' % (pad_num, identifier)
            #else:
            #    response = 'added:%s:%s' % (pad_num, identifier)
            #return response
            return bytelist
        except Exception:
            return


Der Quellcode für das Projekt selbst muss als super user ausgeführt werden, damit das Programm die Berechtigungen für den USB Zugriff hat. Das geht mit einer Eingabeaufforderung in dem Entsprechenden Verzeichnis mit dem folgenden Befehl: "sudo python ./dimensionsPad.py".

from buildhat import Motor, Light
from random import seed
from random import randint
from time import sleep
import dimensionsPad

#defines for the dimensions toypad
ALL_PADS = 0
CENTER_PAD = 1
LEFT_PAD = 2
RIGHT_PAD = 3


pad = dimensionsPad.Dimensions()
pad.switch_pad(ALL_PADS, [255, 50, 0])

motor = Motor('B')
light1 = Light('C')
light2 = Light('D')

seed(1) #seed for random generator for LED flickering

motor.run_to_position(0, speed=10, blocking=True, direction='shortest') #slow motor so that it doesn't destroy itself
light1.brightness(0)
light2.brightness(0)

while True:
    light1.brightness(randint(5, 40)) #led brightness is a random value between 5 and 40 (100 is maximum brightness)
    light2.brightness(randint(5, 40)) #same for led 2
    #pad.switch_pad(ALL_PADS, [randint(200, 255), 127, 0]) #removed because spamming the pad with LED commands seems to interfere with tag reading
   
    tag = pad.update_nfc()
    if (tag is not None): #pad reads a NFC tag
        if (tag[5] == 0): #tag is put onto the toypad (would be 1 if it got removed)
            if [4, 62, 105, 98, 24, 74, 128] == tag[6:13]: #only accept one tag (in this case pumpkin)
                pad.switch_pad(CENTER_PAD, [0, 255, 0])
                light1.brightness(100)
                light2.brightness(100)
                motor.run_to_position(130, speed=10, blocking=True, direction='shortest')
                sleep(6)
                exit()

Video


Kommentare