From bb67ca906068dc494b8417909d6ddb0f5c66a9f4 Mon Sep 17 00:00:00 2001 From: Noah Date: Wed, 6 Jan 2021 14:24:42 +0100 Subject: [PATCH] Basic support for driving a display with rpi_ws281x NOTE: weatherscene.py is known to NOT work under Python 3.x due to an incompatibility with MicroPython's handling of strings and bytes. This can be easily resolved although I opted to not do this, to preserve compabilitity with the original code. To use this on a Raspberry Pi, try: sudo apt install -y python-pip python-requests sudo pip install rpi_ws281x Then connect the display's data line to the Raspberry Pi's GPIO 18 (PCM CLK) (see https://pinout.xyz/) References: - https://github.com/noahwilliamsson/lamatrix/issues/1 - https://github.com/rpi-ws281x/rpi-ws281x-python (userspace WS281x driver) - https://github.com/jgarff/rpi_ws281x (wiring docs) --- README.md | 3 ++- main.py | 12 +++++++--- raspberrypihal.py | 58 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 raspberrypihal.py diff --git a/README.md b/README.md index 10ef354..62595f8 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ This is a project to drive a 32x8 or 16x16 LED matrix based on the popular WS2812 RGB LEDs using a microcontroller running [MicroPython](https://micropython.org). There is experimental support for allowing a more powerful host computer (e.g. a Raspberry Pi Zero W) to remotely control a microcontroller without WiFi (e.g. a Teensy 3.x) and the display connected to it over USB serial. Low FPS video of a standalone Pycom LoPy 1 development board cycling through the scenes: -![LED matrix animated](docs/lamatrix.gif) +TODO: implement![LED matrix animated](docs/lamatrix.gif) Static picture with clock scene. For some reason the colors aren't captured as vidvid as they are in real life. @@ -47,6 +47,7 @@ Features: Primary development has been made on [Pycom](https://www.pycom.io)'s development boards, including the (obsolete) LoPy 1 and the newer WiPy 3. There is also an Arduino [sketch](ArduinoSer2FastLED/ArduinoSer2FastLED.ino) for Teensy 3.1/3.2 boards that implements a custom serial protocol that is spoken by the host software ([main.py](main.py) and [arduinoserialhal.py](arduinoserialhal.py)) that allows the LED matrix to be remotely controlled. +**Update 2021**: If you want to use this with a Raspberry Pi instead of an MCU running MicroPython, see the issue [Using Raspberry PI directly to 8 x 32 not working?](https://github.com/noahwilliamsson/lamatrix/issues/1). ## Building and deploying the MCU diff --git a/main.py b/main.py index 8144964..0d4d515 100755 --- a/main.py +++ b/main.py @@ -36,12 +36,18 @@ tmp = None del uname else: - # Emulate https://docs.pycom.io/firmwareapi/micropython/utime.html - time.ticks_ms = lambda: int(time.time() * 1000) import json import os import signal - from arduinoserialhal import ArduinoSerialHAL as HAL + # Kludge to allow this project to be used with a Raspberry Pi instead of + # an MCU: see https://github.com/noahwilliamsson/lamatrix/issues/1 + try: + # If the rpi_ws281x Python module is available, then use that... + from raspberrypihal import RaspberryPiHAL as HAL + except: + # ...else assume that there's an MCU (driving the display) connected + # to a serial port + from arduinoserialhal import ArduinoSerialHAL as HAL gc.collect() from renderloop import RenderLoop diff --git a/raspberrypihal.py b/raspberrypihal.py new file mode 100644 index 0000000..11351f8 --- /dev/null +++ b/raspberrypihal.py @@ -0,0 +1,58 @@ +# HAL for Raspberry Pi with https://github.com/rpi-ws281x/rpi-ws281x-python +# See https://github.com/jgarff/rpi_ws281x for more details on this library. +# +# The below code assumes the LED strip is connected to GPIO 18 (PCM CLK) +# (see https://pinout.xyz) and that you've installed the rpi_ws281x library. +# +# For Python 2.x: +# +# sudo apt install -y python-pip; sudo pip install rpi_ws281x +# +# For Python 3.x: +# +# sudo apt install -y python3-pip; sudo pip3 install rpi_ws281x +# +# +from rpi_ws281x import PixelStrip, Color + +# LED strip configuration: +LED_PIN = 18 # GPIO pin connected to the pixels (18 uses PWM!). +# LED_PIN = 10 # GPIO pin connected to the pixels (10 uses SPI /dev/spidev0.0). +LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz) +LED_DMA = 10 # DMA channel to use for generating signal (try 10) +LED_BRIGHTNESS = 255 # Set to 0 for darkest and 255 for brightest +LED_INVERT = False # True to invert the signal (when using NPN transistor level shift) +LED_CHANNEL = 0 # set to '1' for GPIOs 13, 19, 41, 45 or 53 + +class RaspberryPiHAL: + def __init__(self, config): + self.num_pixels = config['LedMatrix']['columns'] * config['LedMatrix']['stride'] + self.strip = PixelStrip(self.num_pixels, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL) + self.strip.begin() + def init_display(self, num_pixels=64): + self.clear_display() + def clear_display(self): + c = Color(0, 0, 0) + for i in range(self.num_pixels): + self.strip.setPixelColor(i, c) + self.strip.show() + def update_display(self, num_modified_pixels): + if not num_modified_pixels: + return + self.strip.show() + def put_pixel(self, addr, r, g, b): + self.strip.setPixelColor(addr % self.num_pixels, Color(r, g, b)) + def reset(self): + self.clear_display() + def process_input(self): + #TODO: implement + return 0 + def set_rtc(self, t): + #Not relevant + pass + def set_auto_time(self, enable=True): + #Not relevant + pass + def suspend_host(self, restart_timeout_seconds): + #Not relevant + pass