From 9914cd4b2364554c9d8b7a0e7cb2516e7c9e7760 Mon Sep 17 00:00:00 2001 From: waltem01 Date: Wed, 22 Nov 2023 15:45:55 +0100 Subject: [PATCH] added examples --- .gitignore | 2 - API/deps/samplebase.py | 81 ++++++++++++++++++++ API/deps/samples/graphics.py | 32 ++++++++ API/deps/samples/grayscale-block.py | 39 ++++++++++ API/deps/samples/image-draw.py | 48 ++++++++++++ API/deps/samples/image-scroller.py | 40 ++++++++++ API/deps/samples/image-viewer.py | 34 ++++++++ API/deps/samples/menu.py | 35 +++++++++ API/deps/samples/pulsing-brightness.py | 36 +++++++++ API/deps/samples/pulsing-colors.py | 42 ++++++++++ API/deps/samples/rotating-block-generator.py | 72 +++++++++++++++++ API/deps/samples/runtext.py | 36 +++++++++ API/deps/samples/simple-square.py | 30 ++++++++ 13 files changed, 525 insertions(+), 2 deletions(-) create mode 100644 API/deps/samplebase.py create mode 100644 API/deps/samples/graphics.py create mode 100644 API/deps/samples/grayscale-block.py create mode 100644 API/deps/samples/image-draw.py create mode 100644 API/deps/samples/image-scroller.py create mode 100644 API/deps/samples/image-viewer.py create mode 100644 API/deps/samples/menu.py create mode 100644 API/deps/samples/pulsing-brightness.py create mode 100644 API/deps/samples/pulsing-colors.py create mode 100644 API/deps/samples/rotating-block-generator.py create mode 100644 API/deps/samples/runtext.py create mode 100644 API/deps/samples/simple-square.py diff --git a/.gitignore b/.gitignore index 4763b62..472503a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,5 @@ API/bin API/obj -API/deps/samples - .vscode .idea diff --git a/API/deps/samplebase.py b/API/deps/samplebase.py new file mode 100644 index 0000000..168c90d --- /dev/null +++ b/API/deps/samplebase.py @@ -0,0 +1,81 @@ +import argparse +import time +import sys +import os + +# sys.path.append(os.path.abspath(os.path.dirname(__file__) + '/..')) +from rgbmatrix import RGBMatrix, RGBMatrixOptions + + +class SampleBase(object): + def __init__(self, *args, **kwargs): + self.parser = argparse.ArgumentParser() + + self.parser.add_argument("-r", "--led-rows", action="store", help="Display rows. 16 for 16x32, 32 for 32x32. Default: 64", default=64, type=int) + self.parser.add_argument("--led-cols", action="store", help="Panel columns. Typically 32 or 64. (Default: 64)", default=64, type=int) + self.parser.add_argument("-c", "--led-chain", action="store", help="Daisy-chained boards. Default: 1.", default=1, type=int) + self.parser.add_argument("-P", "--led-parallel", action="store", help="For Plus-models or RPi2: parallel chains. 1..3. Default: 1", default=1, type=int) + self.parser.add_argument("-p", "--led-pwm-bits", action="store", help="Bits used for PWM. Something between 1..11. Default: 11", default=11, type=int) + self.parser.add_argument("-b", "--led-brightness", action="store", help="Sets brightness level. Default: 100. Range: 1..100", default=100, type=int) + self.parser.add_argument("-m", "--led-gpio-mapping", help="Hardware Mapping: regular, adafruit-hat, adafruit-hat-pwm", choices=['regular', 'regular-pi1', 'adafruit-hat', 'adafruit-hat-pwm'], default='adafruit-hat', type=str) + self.parser.add_argument("--led-scan-mode", action="store", help="Progressive or interlaced scan. 0 Progressive, 1 Interlaced (default)", default=1, choices=range(2), type=int) + self.parser.add_argument("--led-pwm-lsb-nanoseconds", action="store", help="Base time-unit for the on-time in the lowest significant bit in nanoseconds. Default: 130", default=130, type=int) + self.parser.add_argument("--led-show-refresh", action="store_true", help="Shows the current refresh rate of the LED panel") + self.parser.add_argument("--led-slowdown-gpio", action="store", help="Slow down writing to GPIO. Range: 0..4. Default: 1", default=4, type=int) + self.parser.add_argument("--led-no-hardware-pulse", action="store", help="Don't use hardware pin-pulse generation") + self.parser.add_argument("--led-rgb-sequence", action="store", help="Switch if your matrix has led colors swapped. Default: RGB", default="RGB", type=str) + self.parser.add_argument("--led-pixel-mapper", action="store", help="Apply pixel mappers. e.g \"Rotate:90\"", default="", type=str) + self.parser.add_argument("--led-row-addr-type", action="store", help="0 = default; 1=AB-addressed panels; 2=row direct; 3=ABC-addressed panels; 4 = ABC Shift + DE direct", default=0, type=int, choices=[0,1,2,3,4]) + self.parser.add_argument("--led-multiplexing", action="store", help="Multiplexing type: 0=direct; 1=strip; 2=checker; 3=spiral; 4=ZStripe; 5=ZnMirrorZStripe; 6=coreman; 7=Kaler2Scan; 8=ZStripeUneven... (Default: 0)", default=0, type=int) + self.parser.add_argument("--led-panel-type", action="store", help="Needed to initialize special panels. Supported: 'FM6126A'", default="", type=str) + self.parser.add_argument("--led-no-drop-privs", dest="drop_privileges", help="Don't drop privileges from 'root' after initializing the hardware.", action='store_false') + self.parser.set_defaults(drop_privileges=True) + + def usleep(self, value): + time.sleep(value / 1000000.0) + + def run(self): + print("Running") + + def process(self): + self.args = self.parser.parse_args() + + options = RGBMatrixOptions() + + if self.args.led_gpio_mapping != None: + options.hardware_mapping = self.args.led_gpio_mapping + options.rows = self.args.led_rows + options.cols = self.args.led_cols + options.chain_length = self.args.led_chain + options.parallel = self.args.led_parallel + options.row_address_type = self.args.led_row_addr_type + options.multiplexing = self.args.led_multiplexing + options.pwm_bits = self.args.led_pwm_bits + options.brightness = self.args.led_brightness + options.pwm_lsb_nanoseconds = self.args.led_pwm_lsb_nanoseconds + options.led_rgb_sequence = self.args.led_rgb_sequence + options.pixel_mapper_config = self.args.led_pixel_mapper + options.panel_type = self.args.led_panel_type + + + if self.args.led_show_refresh: + options.show_refresh_rate = 1 + + if self.args.led_slowdown_gpio != None: + options.gpio_slowdown = self.args.led_slowdown_gpio + if self.args.led_no_hardware_pulse: + options.disable_hardware_pulsing = True + if not self.args.drop_privileges: + options.drop_privileges=False + + self.matrix = RGBMatrix(options = options) + + try: + # Start loop + print("Press CTRL-C to stop sample") + self.run() + except KeyboardInterrupt: + print("Exiting\n") + sys.exit(0) + + return True diff --git a/API/deps/samples/graphics.py b/API/deps/samples/graphics.py new file mode 100644 index 0000000..6fd89e1 --- /dev/null +++ b/API/deps/samples/graphics.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +from samplebase import SampleBase +from rgbmatrix import graphics +import time + + +class GraphicsTest(SampleBase): + def __init__(self, *args, **kwargs): + super(GraphicsTest, self).__init__(*args, **kwargs) + + def run(self): + canvas = self.matrix + font = graphics.Font() + font.LoadFont("../../../fonts/7x13.bdf") + + red = graphics.Color(255, 0, 0) + graphics.DrawLine(canvas, 5, 5, 22, 13, red) + + green = graphics.Color(0, 255, 0) + graphics.DrawCircle(canvas, 15, 15, 10, green) + + blue = graphics.Color(0, 0, 255) + graphics.DrawText(canvas, font, 2, 10, blue, "Text") + + time.sleep(10) # show display for 10 seconds before exit + + +# Main function +if __name__ == "__main__": + graphics_test = GraphicsTest() + if (not graphics_test.process()): + graphics_test.print_help() diff --git a/API/deps/samples/grayscale-block.py b/API/deps/samples/grayscale-block.py new file mode 100644 index 0000000..0f01add --- /dev/null +++ b/API/deps/samples/grayscale-block.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +from samplebase import SampleBase +import time + + +class GrayscaleBlock(SampleBase): + def __init__(self, *args, **kwargs): + super(GrayscaleBlock, self).__init__(*args, **kwargs) + + def run(self): + sub_blocks = 16 + width = self.matrix.width + height = self.matrix.height + x_step = max(1, width / sub_blocks) + y_step = max(1, height / sub_blocks) + count = 0 + + while True: + for y in range(0, height): + for x in range(0, width): + c = sub_blocks * int(y / y_step) + int(x / x_step) + if count % 4 == 0: + self.matrix.SetPixel(x, y, c, c, c) + elif count % 4 == 1: + self.matrix.SetPixel(x, y, c, 0, 0) + elif count % 4 == 2: + self.matrix.SetPixel(x, y, 0, c, 0) + elif count % 4 == 3: + self.matrix.SetPixel(x, y, 0, 0, c) + + count += 1 + time.sleep(2) + + +# Main function +if __name__ == "__main__": + grayscale_block = GrayscaleBlock() + if (not grayscale_block.process()): + grayscale_block.print_help() diff --git a/API/deps/samples/image-draw.py b/API/deps/samples/image-draw.py new file mode 100644 index 0000000..5b1f62b --- /dev/null +++ b/API/deps/samples/image-draw.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +# (This is an example similar to an example from the Adafruit fork +# to show the similarities. Most important difference currently is, that +# this library wants RGB mode.) +# +# A more complex RGBMatrix example works with the Python Imaging Library, +# demonstrating a few graphics primitives and image loading. +# Note that PIL graphics do not have an immediate effect on the display -- +# image is drawn into a separate buffer, which is then copied to the matrix +# using the SetImage() function (see examples below). +# Requires rgbmatrix.so present in the same directory. + +# PIL Image module (create or load images) is explained here: +# http://effbot.org/imagingbook/image.htm +# PIL ImageDraw module (draw shapes to images) explained here: +# http://effbot.org/imagingbook/imagedraw.htm + +from PIL import Image +from PIL import ImageDraw +import time +from rgbmatrix import RGBMatrix, RGBMatrixOptions + +# Configuration for the matrix +options = RGBMatrixOptions() +options.rows = 32 +options.chain_length = 1 +options.parallel = 1 +options.hardware_mapping = 'regular' # If you have an Adafruit HAT: 'adafruit-hat' + +matrix = RGBMatrix(options = options) + +# RGB example w/graphics prims. +# Note, only "RGB" mode is supported currently. +image = Image.new("RGB", (32, 32)) # Can be larger than matrix if wanted!! +draw = ImageDraw.Draw(image) # Declare Draw instance before prims +# Draw some shapes into image (no immediate effect on matrix)... +draw.rectangle((0, 0, 31, 31), fill=(0, 0, 0), outline=(0, 0, 255)) +draw.line((0, 0, 31, 31), fill=(255, 0, 0)) +draw.line((0, 31, 31, 0), fill=(0, 255, 0)) + +# Then scroll image across matrix... +for n in range(-32, 33): # Start off top-left, move off bottom-right + matrix.Clear() + matrix.SetImage(image, n, n) + time.sleep(0.05) + +matrix.Clear() diff --git a/API/deps/samples/image-scroller.py b/API/deps/samples/image-scroller.py new file mode 100644 index 0000000..f18a351 --- /dev/null +++ b/API/deps/samples/image-scroller.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +import time +from samplebase import SampleBase +from PIL import Image + + +class ImageScroller(SampleBase): + def __init__(self, *args, **kwargs): + super(ImageScroller, self).__init__(*args, **kwargs) + self.parser.add_argument("-i", "--image", help="The image to display", default="../../../examples-api-use/runtext.ppm") + + def run(self): + if not 'image' in self.__dict__: + self.image = Image.open(self.args.image).convert('RGB') + self.image.resize((self.matrix.width, self.matrix.height), Image.ANTIALIAS) + + double_buffer = self.matrix.CreateFrameCanvas() + img_width, img_height = self.image.size + + # let's scroll + xpos = 0 + while True: + xpos += 1 + if (xpos > img_width): + xpos = 0 + + double_buffer.SetImage(self.image, -xpos) + double_buffer.SetImage(self.image, -xpos + img_width) + + double_buffer = self.matrix.SwapOnVSync(double_buffer) + time.sleep(0.01) + +# Main function +# e.g. call with +# sudo ./image-scroller.py --chain=4 +# if you have a chain of four +if __name__ == "__main__": + image_scroller = ImageScroller() + if (not image_scroller.process()): + image_scroller.print_help() diff --git a/API/deps/samples/image-viewer.py b/API/deps/samples/image-viewer.py new file mode 100644 index 0000000..34fb390 --- /dev/null +++ b/API/deps/samples/image-viewer.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +import time +import sys + +from rgbmatrix import RGBMatrix, RGBMatrixOptions +from PIL import Image + +if len(sys.argv) < 2: + sys.exit("Require an image argument") +else: + image_file = sys.argv[1] + +image = Image.open(image_file) + +# Configuration for the matrix +options = RGBMatrixOptions() +options.rows = 32 +options.chain_length = 1 +options.parallel = 1 +options.hardware_mapping = 'regular' # If you have an Adafruit HAT: 'adafruit-hat' + +matrix = RGBMatrix(options = options) + +# Make image fit our screen. +image.thumbnail((matrix.width, matrix.height), Image.ANTIALIAS) + +matrix.SetImage(image.convert('RGB')) + +try: + print("Press CTRL-C to stop.") + while True: + time.sleep(100) +except KeyboardInterrupt: + sys.exit(0) diff --git a/API/deps/samples/menu.py b/API/deps/samples/menu.py new file mode 100644 index 0000000..3dff06a --- /dev/null +++ b/API/deps/samples/menu.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +from samplebase import SampleBase +from rgbmatrix import graphics +import time, keyboard, os + + +class TextMenu(SampleBase): + def __init__(self, *args, **kwargs): + super(TextMenu, self).__init__(*args, **kwargs) + + def run(self): + offscreen_canvas = self.matrix.CreateFrameCanvas() + font = graphics.Font() + font.LoadFont("7x13.bdf") + textColor = graphics.Color(255, 255, 255) + my_text = "hahahahahahah" + + while True: + offscreen_canvas.Clear() + len = graphics.DrawText(offscreen_canvas, font, 7, 13, textColor, my_text) + if (keyboard.is_pressed('1')): + os.system('sudo /var/SnakeGame/Client/SnakeGame') + break + elif (keyboard.is_pressed('2')): + os.system('sudo /var/FlappyBird/FlappyBird') + break + time.sleep(0.05) + offscreen_canvas = self.matrix.SwapOnVSync(offscreen_canvas) + + +# Main function +if __name__ == "__main__": + run_text = TextMenu() + if (not run_text.process()): + run_text.print_help() diff --git a/API/deps/samples/pulsing-brightness.py b/API/deps/samples/pulsing-brightness.py new file mode 100644 index 0000000..e927c35 --- /dev/null +++ b/API/deps/samples/pulsing-brightness.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +from samplebase import SampleBase + + +class GrayscaleBlock(SampleBase): + def __init__(self, *args, **kwargs): + super(GrayscaleBlock, self).__init__(*args, **kwargs) + + def run(self): + max_brightness = self.matrix.brightness + count = 0 + c = 255 + + while (True): + if self.matrix.brightness < 1: + self.matrix.brightness = max_brightness + count += 1 + else: + self.matrix.brightness -= 1 + + if count % 4 == 0: + self.matrix.Fill(c, 0, 0) + elif count % 4 == 1: + self.matrix.Fill(0, c, 0) + elif count % 4 == 2: + self.matrix.Fill(0, 0, c) + elif count % 4 == 3: + self.matrix.Fill(c, c, c) + + self.usleep(20 * 1000) + +# Main function +if __name__ == "__main__": + grayscale_block = GrayscaleBlock() + if (not grayscale_block.process()): + grayscale_block.print_help() diff --git a/API/deps/samples/pulsing-colors.py b/API/deps/samples/pulsing-colors.py new file mode 100644 index 0000000..c585174 --- /dev/null +++ b/API/deps/samples/pulsing-colors.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +from samplebase import SampleBase + + +class PulsingColors(SampleBase): + def __init__(self, *args, **kwargs): + super(PulsingColors, self).__init__(*args, **kwargs) + + def run(self): + self.offscreen_canvas = self.matrix.CreateFrameCanvas() + continuum = 0 + + while True: + self.usleep(5 * 1000) + continuum += 1 + continuum %= 3 * 255 + + red = 0 + green = 0 + blue = 0 + + if continuum <= 255: + c = continuum + blue = 255 - c + red = c + elif continuum > 255 and continuum <= 511: + c = continuum - 256 + red = 255 - c + green = c + else: + c = continuum - 512 + green = 255 - c + blue = c + + self.offscreen_canvas.Fill(red, green, blue) + self.offscreen_canvas = self.matrix.SwapOnVSync(self.offscreen_canvas) + +# Main function +if __name__ == "__main__": + pulsing_colors = PulsingColors() + if (not pulsing_colors.process()): + pulsing_colors.print_help() diff --git a/API/deps/samples/rotating-block-generator.py b/API/deps/samples/rotating-block-generator.py new file mode 100644 index 0000000..0bc412b --- /dev/null +++ b/API/deps/samples/rotating-block-generator.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python +from samplebase import SampleBase +import math + + +def scale_col(val, lo, hi): + if val < lo: + return 0 + if val > hi: + return 255 + return 255 * (val - lo) / (hi - lo) + + +def rotate(x, y, sin, cos): + return x * cos - y * sin, x * sin + y * cos + + +class RotatingBlockGenerator(SampleBase): + def __init__(self, *args, **kwargs): + super(RotatingBlockGenerator, self).__init__(*args, **kwargs) + + def run(self): + cent_x = self.matrix.width / 2 + cent_y = self.matrix.height / 2 + + rotate_square = min(self.matrix.width, self.matrix.height) * 1.41 + min_rotate = cent_x - rotate_square / 2 + max_rotate = cent_x + rotate_square / 2 + + display_square = min(self.matrix.width, self.matrix.height) * 0.7 + min_display = cent_x - display_square / 2 + max_display = cent_x + display_square / 2 + + deg_to_rad = 2 * 3.14159265 / 360 + rotation = 0 + + # Pre calculate colors + col_table = [] + for x in range(int(min_rotate), int(max_rotate)): + col_table.insert(x, scale_col(x, min_display, max_display)) + + offset_canvas = self.matrix.CreateFrameCanvas() + + while True: + rotation += 1 + rotation %= 360 + + # calculate sin and cos once for each frame + angle = rotation * deg_to_rad + sin = math.sin(angle) + cos = math.cos(angle) + + for x in range(int(min_rotate), int(max_rotate)): + for y in range(int(min_rotate), int(max_rotate)): + # Our rotate center is always offset by cent_x + rot_x, rot_y = rotate(x - cent_x, y - cent_x, sin, cos) + + if x >= min_display and x < max_display and y >= min_display and y < max_display: + x_col = col_table[x] + y_col = col_table[y] + offset_canvas.SetPixel(rot_x + cent_x, rot_y + cent_y, x_col, 255 - y_col, y_col) + else: + offset_canvas.SetPixel(rot_x + cent_x, rot_y + cent_y, 0, 0, 0) + + offset_canvas = self.matrix.SwapOnVSync(offset_canvas) + + +# Main function +if __name__ == "__main__": + rotating_block_generator = RotatingBlockGenerator() + if (not rotating_block_generator.process()): + rotating_block_generator.print_help() diff --git a/API/deps/samples/runtext.py b/API/deps/samples/runtext.py new file mode 100644 index 0000000..a6d39cd --- /dev/null +++ b/API/deps/samples/runtext.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# Display a runtext with double-buffering. +from samplebase import SampleBase +from rgbmatrix import graphics +import time + + +class RunText(SampleBase): + def __init__(self, *args, **kwargs): + super(RunText, self).__init__(*args, **kwargs) + self.parser.add_argument("-t", "--text", help="The text to scroll on the RGB LED panel", default="Hello world!") + + def run(self): + offscreen_canvas = self.matrix.CreateFrameCanvas() + font = graphics.Font() + font.LoadFont("../../../fonts/7x13.bdf") + textColor = graphics.Color(255, 255, 0) + pos = offscreen_canvas.width + my_text = self.args.text + + while True: + offscreen_canvas.Clear() + len = graphics.DrawText(offscreen_canvas, font, pos, 10, textColor, my_text) + pos -= 1 + if (pos + len < 0): + pos = offscreen_canvas.width + + time.sleep(0.05) + offscreen_canvas = self.matrix.SwapOnVSync(offscreen_canvas) + + +# Main function +if __name__ == "__main__": + run_text = RunText() + if (not run_text.process()): + run_text.print_help() diff --git a/API/deps/samples/simple-square.py b/API/deps/samples/simple-square.py new file mode 100644 index 0000000..77512e9 --- /dev/null +++ b/API/deps/samples/simple-square.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python +from samplebase import SampleBase + + +class SimpleSquare(SampleBase): + def __init__(self, *args, **kwargs): + super(SimpleSquare, self).__init__(*args, **kwargs) + + def run(self): + offset_canvas = self.matrix.CreateFrameCanvas() + while True: + for x in range(0, self.matrix.width): + offset_canvas.SetPixel(x, x, 255, 255, 255) + offset_canvas.SetPixel(offset_canvas.height - 1 - x, x, 255, 0, 255) + + for x in range(0, offset_canvas.width): + offset_canvas.SetPixel(x, 0, 255, 0, 0) + offset_canvas.SetPixel(x, offset_canvas.height - 1, 255, 255, 0) + + for y in range(0, offset_canvas.height): + offset_canvas.SetPixel(0, y, 0, 0, 255) + offset_canvas.SetPixel(offset_canvas.width - 1, y, 0, 255, 0) + offset_canvas = self.matrix.SwapOnVSync(offset_canvas) + + +# Main function +if __name__ == "__main__": + simple_square = SimpleSquare() + if (not simple_square.process()): + simple_square.print_help()