├── bindings ├── python │ ├── .gitignore │ ├── rgbmatrix │ │ ├── graphics.pxd │ │ ├── __init__.py │ │ ├── Makefile │ │ ├── core.pxd │ │ ├── graphics.pyx │ │ └── cppinc.pxd │ ├── samples │ │ ├── image-viewer.py │ │ ├── graphics.py │ │ ├── pulsing-brightness.py │ │ ├── simple-square.py │ │ ├── runtext.py │ │ ├── pulsing-colors.py │ │ ├── grayscale-block.py │ │ ├── image-scroller.py │ │ ├── image-draw.py │ │ ├── rotating-block-generator.py │ │ └── samplebase.py │ ├── setup.py │ ├── Makefile │ └── README.md ├── c# │ ├── .gitignore │ ├── Makefile │ ├── README.md │ ├── examples │ │ ├── font-example.cs │ │ ├── minimal-example.cs │ │ ├── Makefile │ │ ├── pulsing-brightness.cs │ │ └── matrix-rain.cs │ ├── RGBLedFont.cs │ └── RGBLedCanvas.cs └── README.md ├── .gitignore ├── utils ├── .gitignore └── Makefile ├── lib ├── .gitignore ├── gpio-bits.h ├── multiplex-mappers-internal.h ├── hardware-mapping.h ├── utf8-internal.h ├── thread.cc ├── gpio.h ├── graphics.cc ├── bdf-font.cc ├── content-streamer.cc ├── framebuffer-internal.h └── Makefile ├── img ├── hub75.jpg ├── powerbar.jpg ├── active3-pcb.png ├── adafruit-mod.jpg ├── coordinates.png ├── hub75-other.jpg ├── passive3-pcb.png ├── running-vid.jpg ├── time-display.jpg ├── wire-up-icon.png ├── chained-64x64.jpg ├── raspberry-gpio.jpg ├── text-ghosting.jpg ├── active3-schematic.png ├── passive-rpi1-pcb.png ├── pixelpusher-vid.jpg ├── text-no-ghosting.jpg ├── user-action-shot.jpg ├── adafruit-64x64-back.jpg ├── idc-hub75-connector.jpg ├── Vmapper_Z_192x160_3x5.jpg ├── adafruit-64x64-front.jpg ├── Makefile ├── active3-pcb-config-pin4.png ├── active3-pcb-config-pin8.png ├── active3-pcb-config-default.png └── three-parallel-panels-soic.jpg ├── adapter ├── active-3 │ ├── Makefile │ ├── schematic.pdf │ ├── active3-rpi-hub75-adapter-fab.zip │ ├── active3-rpi-hub75-adapter.pro │ └── README.md ├── passive-3 │ ├── Makefile │ ├── passive3-rpi-hub75-adapter-fab.zip │ ├── passive3-rpi-hub75-adapter.pro │ ├── README.md │ └── passive3-rpi-hub75-adapter-cache.lib ├── passive-rpi1 │ ├── Makefile │ ├── passive-rpi-hub75-adapter-fab.zip │ ├── README.md │ ├── passive-rpi-hub75-adapter.pro │ ├── passive-rpi-hub75-adapter-cache.lib │ └── passive-rpi-hub75-adapter.sch ├── kicad-scripts │ ├── makefile.inc │ └── kicad-fab.py └── README.md ├── examples-api-use ├── runtext.ppm ├── runtext16.ppm ├── .gitignore ├── c-example.c ├── Makefile ├── input-example.cc ├── minimal-example.cc ├── ledcat.cc ├── image-example.cc ├── scrolling-text-example.cc ├── text-example.cc ├── clock.cc └── pixel-mover.cc ├── .github └── workflows │ └── c-cpp.yml ├── .editorconfig ├── Makefile ├── include ├── canvas.h ├── thread.h ├── content-streamer.h ├── threaded-canvas-manipulator.h ├── pixel-mapper.h └── graphics.h └── fonts ├── AUTHORS └── README.md /bindings/python/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | *~ 4 | *.so 5 | -------------------------------------------------------------------------------- /bindings/c#/.gitignore: -------------------------------------------------------------------------------- 1 | *.dll 2 | *.exe 3 | -------------------------------------------------------------------------------- /utils/.gitignore: -------------------------------------------------------------------------------- 1 | led-image-viewer 2 | video-viewer 3 | -------------------------------------------------------------------------------- /lib/.gitignore: -------------------------------------------------------------------------------- 1 | compiler-flags 2 | librgbmatrix.a 3 | librgbmatrix.so.1 4 | -------------------------------------------------------------------------------- /img/hub75.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/hub75.jpg -------------------------------------------------------------------------------- /img/powerbar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/powerbar.jpg -------------------------------------------------------------------------------- /img/active3-pcb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/active3-pcb.png -------------------------------------------------------------------------------- /img/adafruit-mod.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/adafruit-mod.jpg -------------------------------------------------------------------------------- /img/coordinates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/coordinates.png -------------------------------------------------------------------------------- /img/hub75-other.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/hub75-other.jpg -------------------------------------------------------------------------------- /img/passive3-pcb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/passive3-pcb.png -------------------------------------------------------------------------------- /img/running-vid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/running-vid.jpg -------------------------------------------------------------------------------- /img/time-display.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/time-display.jpg -------------------------------------------------------------------------------- /img/wire-up-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/wire-up-icon.png -------------------------------------------------------------------------------- /img/chained-64x64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/chained-64x64.jpg -------------------------------------------------------------------------------- /img/raspberry-gpio.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/raspberry-gpio.jpg -------------------------------------------------------------------------------- /img/text-ghosting.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/text-ghosting.jpg -------------------------------------------------------------------------------- /adapter/active-3/Makefile: -------------------------------------------------------------------------------- 1 | include ../kicad-scripts/makefile.inc 2 | 3 | active3-rpi-hub75-adapter-fab.zip: 4 | -------------------------------------------------------------------------------- /adapter/passive-3/Makefile: -------------------------------------------------------------------------------- 1 | include ../kicad-scripts/makefile.inc 2 | 3 | passive3-rpi-hub75-adapter-fab.zip: 4 | -------------------------------------------------------------------------------- /img/active3-schematic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/active3-schematic.png -------------------------------------------------------------------------------- /img/passive-rpi1-pcb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/passive-rpi1-pcb.png -------------------------------------------------------------------------------- /img/pixelpusher-vid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/pixelpusher-vid.jpg -------------------------------------------------------------------------------- /img/text-no-ghosting.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/text-no-ghosting.jpg -------------------------------------------------------------------------------- /img/user-action-shot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/user-action-shot.jpg -------------------------------------------------------------------------------- /adapter/passive-rpi1/Makefile: -------------------------------------------------------------------------------- 1 | include ../kicad-scripts/makefile.inc 2 | 3 | passive-rpi-hub75-adapter-fab.zip: 4 | -------------------------------------------------------------------------------- /img/adafruit-64x64-back.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/adafruit-64x64-back.jpg -------------------------------------------------------------------------------- /img/idc-hub75-connector.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/idc-hub75-connector.jpg -------------------------------------------------------------------------------- /adapter/active-3/schematic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/adapter/active-3/schematic.pdf -------------------------------------------------------------------------------- /examples-api-use/runtext.ppm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/examples-api-use/runtext.ppm -------------------------------------------------------------------------------- /examples-api-use/runtext16.ppm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/examples-api-use/runtext16.ppm -------------------------------------------------------------------------------- /img/Vmapper_Z_192x160_3x5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/Vmapper_Z_192x160_3x5.jpg -------------------------------------------------------------------------------- /img/adafruit-64x64-front.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/adafruit-64x64-front.jpg -------------------------------------------------------------------------------- /img/Makefile: -------------------------------------------------------------------------------- 1 | 2 | coordinates.png: coordinates.svg 3 | 4 | %.png : %.svg 5 | inkscape --export-png=$@ --export-dpi=160 $^ 6 | -------------------------------------------------------------------------------- /img/active3-pcb-config-pin4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/active3-pcb-config-pin4.png -------------------------------------------------------------------------------- /img/active3-pcb-config-pin8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/active3-pcb-config-pin8.png -------------------------------------------------------------------------------- /img/active3-pcb-config-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/active3-pcb-config-default.png -------------------------------------------------------------------------------- /img/three-parallel-panels-soic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/img/three-parallel-panels-soic.jpg -------------------------------------------------------------------------------- /examples-api-use/.gitignore: -------------------------------------------------------------------------------- 1 | demo 2 | minimal-example 3 | text-example 4 | c-example 5 | clock 6 | scrolling-text-example 7 | ledcat 8 | -------------------------------------------------------------------------------- /adapter/active-3/active3-rpi-hub75-adapter-fab.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/adapter/active-3/active3-rpi-hub75-adapter-fab.zip -------------------------------------------------------------------------------- /adapter/passive-3/passive3-rpi-hub75-adapter-fab.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/adapter/passive-3/passive3-rpi-hub75-adapter-fab.zip -------------------------------------------------------------------------------- /adapter/passive-rpi1/passive-rpi-hub75-adapter-fab.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibico/rpi-rgb-led-matrix/master/adapter/passive-rpi1/passive-rpi-hub75-adapter-fab.zip -------------------------------------------------------------------------------- /bindings/README.md: -------------------------------------------------------------------------------- 1 | Various bindings to other programming languages. 2 | Typically these are wrapping the [C-binding](../include/led-matrix-c.h) that 3 | comes with rpi-rgb-led-matrix -------------------------------------------------------------------------------- /bindings/python/rgbmatrix/graphics.pxd: -------------------------------------------------------------------------------- 1 | cimport cppinc 2 | 3 | cdef class Color: 4 | cdef cppinc.Color __color 5 | 6 | cdef class Font: 7 | cdef cppinc.Font __font 8 | 9 | # Local Variables: 10 | # mode: python 11 | # End: 12 | -------------------------------------------------------------------------------- /bindings/python/rgbmatrix/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import 3 | 4 | __version__ = "0.0.1" 5 | __author__ = "Christoph Friedrich " 6 | 7 | from .core import RGBMatrix, FrameCanvas, RGBMatrixOptions 8 | -------------------------------------------------------------------------------- /adapter/kicad-scripts/makefile.inc: -------------------------------------------------------------------------------- 1 | # -*- Makefile -*- 2 | 3 | %-fab.zip : %-fab.kicad_pcb 4 | python ../kicad-scripts/kicad-fab.py $< 5 | zip -r $@ plot/ 6 | 7 | %-fab.kicad_pcb : %.kicad_pcb 8 | sed "s/%%gitversion%%/`git log --date=short --pretty=format:'%h@%cd' -n 1`/" < $^ > $@ 9 | -------------------------------------------------------------------------------- /.github/workflows/c-cpp.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: make 17 | run: make 18 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor config file, see http://editorconfig.org/ 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.{h,cc}] 12 | indent_size = 2 13 | 14 | [Makefile] 15 | indent_style = tab 16 | -------------------------------------------------------------------------------- /bindings/python/rgbmatrix/Makefile: -------------------------------------------------------------------------------- 1 | # The *.cpp files are included in the distribution; this is only needed when 2 | # working on the pyx files. 3 | # 4 | # Please check in modified *.cpp files with distribution to not require cython 5 | # to be installed on the users' machine. 6 | # for python3: make PYTHON=$(which python3) CYTHON=$(which cython3) 7 | CYTHON ?= cython 8 | 9 | all : core.cpp graphics.cpp 10 | 11 | %.cpp : %.pyx 12 | $(CYTHON) --cplus -o $@ $^ 13 | 14 | clean: 15 | rm -rf core.cpp graphics.cpp 16 | -------------------------------------------------------------------------------- /bindings/c#/Makefile: -------------------------------------------------------------------------------- 1 | CSHARP_LIB=RGBLedMatrix.dll 2 | SOURCES=RGBLedCanvas.cs RGBLedMatrix.cs RGBLedFont.cs 3 | CSHARP_COMPILER=mcs 4 | 5 | RGB_LIBDIR=../../lib 6 | RGB_LIBRARY_NAME=rgbmatrix 7 | RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).so.1 8 | 9 | EXAMPLES_DIR=examples 10 | 11 | $(CSHARP_LIB) : $(SOURCES) $(RGB_LIBRARY) 12 | $(CSHARP_COMPILER) -target:library -out:$@ $(SOURCES) 13 | 14 | $(RGB_LIBRARY): 15 | $(MAKE) -C $(RGB_LIBDIR) 16 | 17 | build: $(CSHARP_LIB) 18 | $(MAKE) -C $(EXAMPLES_DIR) all 19 | 20 | clean: 21 | rm -f $(CSHARP_LIB) 22 | -------------------------------------------------------------------------------- /adapter/active-3/active3-rpi-hub75-adapter.pro: -------------------------------------------------------------------------------- 1 | update=Sat 06 Jul 2019 08:45:23 PM PDT 2 | version=1 3 | last_client=kicad 4 | [cvpcb] 5 | version=1 6 | NetIExt=net 7 | [cvpcb/libraries] 8 | EquName1=devcms 9 | [general] 10 | version=1 11 | [pcbnew] 12 | version=1 13 | PageLayoutDescrFile= 14 | LastNetListRead= 15 | UseCmpFile=1 16 | PadDrill=0.6 17 | PadDrillOvalY=0.6 18 | PadSizeH=1.5 19 | PadSizeV=1.5 20 | PcbTextSizeV=1.5 21 | PcbTextSizeH=1.5 22 | PcbTextThickness=0.3 23 | ModuleTextSizeV=1 24 | ModuleTextSizeH=1 25 | ModuleTextSizeThickness=0.15 26 | SolderMaskClearance=0 27 | SolderMaskMinWidth=0 28 | DrawSegmentWidth=0.2 29 | BoardOutlineThickness=0.09999999999999999 30 | ModuleOutlineThickness=0.15 31 | [eeschema] 32 | version=1 33 | LibDir= 34 | -------------------------------------------------------------------------------- /bindings/python/rgbmatrix/core.pxd: -------------------------------------------------------------------------------- 1 | cimport cppinc 2 | 3 | cdef class Canvas: 4 | cdef cppinc.Canvas *__getCanvas(self) except + 5 | 6 | cdef class FrameCanvas(Canvas): 7 | cdef cppinc.FrameCanvas *__canvas 8 | 9 | cdef class RGBMatrix(Canvas): 10 | cdef cppinc.RGBMatrix *__matrix 11 | 12 | cdef class RGBMatrixOptions: 13 | cdef cppinc.Options __options 14 | cdef cppinc.RuntimeOptions __runtime_options 15 | # Must keep a reference to the encoded bytes for the strings, 16 | # otherwise, when the Options struct is used, it will be garbage collected 17 | cdef bytes __py_encoded_hardware_mapping 18 | cdef bytes __py_encoded_led_rgb_sequence 19 | cdef bytes __py_encoded_pixel_mapper_config 20 | cdef bytes __py_encoded_panel_type 21 | 22 | # Local Variables: 23 | # mode: python 24 | # End: 25 | -------------------------------------------------------------------------------- /bindings/c#/README.md: -------------------------------------------------------------------------------- 1 | C# bindings for RGB Matrix library 2 | ====================================== 3 | 4 | Building 5 | -------- 6 | 7 | To build the C# wrapper for the RGB Matrix C library you need to first have mono installed. 8 | 9 | ### Install Mono 10 | 11 | ```shell 12 | $ sudo apt-get update 13 | $ sudo apt-get install mono-complete 14 | ``` 15 | 16 | Then, in the root directory for the matrix library type 17 | 18 | ```shell 19 | make build-csharp 20 | ``` 21 | 22 | To run the example applications in the c#\examples folder 23 | 24 | ```shell 25 | sudo mono minimal-example.exe 26 | ``` 27 | 28 | Notes 29 | -------- 30 | 31 | C# applications look for libraries in the working directory of the application. To use this library for your own projects you will need to ensure you have RGBLedMatrix.dll and librgbmatrix.so in the same folder as your exe. 32 | -------------------------------------------------------------------------------- /adapter/passive-rpi1/README.md: -------------------------------------------------------------------------------- 1 | Adapter PCB to connect to Rasbperry Pi1 2 | ======================================= 3 | 4 | * Passive board. Simple, but might need to define `--led-slowdown-gpio` if you see 5 | glitches. 6 | * Supports one panel connected to a 26 pin Raspberry Pi 1. If you have a newer RPi, 7 | check out the [Passive 3](../passive-3) or [Active 3](../active-3) adapter. 8 | * Open source KiCAD PCB EDA format. 9 | * (not very pretty layout, was just lazy and let the auto-router do it). 10 | * The FAB files are provided as [passive-rpi-hub75-adapter-fab.zip](./passive-rpi-hub75-adapter-fab.zip) 11 | 12 | This board is [shared on OSH Park][osh-passive-rpi] (not affiliated) 13 | 14 | ![Preview][rendering] 15 | 16 | [rendering]: ../../img/passive-rpi1-pcb.png 17 | [osh-passive-rpi]: https://oshpark.com/shared_projects/afEA1gNt 18 | -------------------------------------------------------------------------------- /bindings/python/samples/image-viewer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import time 3 | import sys 4 | 5 | from rgbmatrix import RGBMatrix, RGBMatrixOptions 6 | from PIL import Image 7 | 8 | if len(sys.argv) < 2: 9 | sys.exit("Require an image argument") 10 | else: 11 | image_file = sys.argv[1] 12 | 13 | image = Image.open(image_file) 14 | 15 | # Configuration for the matrix 16 | options = RGBMatrixOptions() 17 | options.rows = 32 18 | options.chain_length = 1 19 | options.parallel = 1 20 | options.hardware_mapping = 'regular' # If you have an Adafruit HAT: 'adafruit-hat' 21 | 22 | matrix = RGBMatrix(options = options) 23 | 24 | # Make image fit our screen. 25 | image.thumbnail((matrix.width, matrix.height), Image.ANTIALIAS) 26 | 27 | matrix.SetImage(image.convert('RGB')) 28 | 29 | try: 30 | print("Press CTRL-C to stop.") 31 | while True: 32 | time.sleep(100) 33 | except KeyboardInterrupt: 34 | sys.exit(0) 35 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # This toplevel Makefile compiles the library in the lib subdirectory. 2 | # If you want to see how to integrate the library in your own projects, check 3 | # out the sub-directories examples-api-use/ and utils/ 4 | RGB_LIBDIR=./lib 5 | RGB_LIBRARY_NAME=rgbmatrix 6 | RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a 7 | 8 | # Some language bindings. 9 | PYTHON_LIB_DIR=bindings/python 10 | CSHARP_LIB_DIR=bindings/c\# 11 | 12 | all : $(RGB_LIBRARY) 13 | 14 | $(RGB_LIBRARY): FORCE 15 | $(MAKE) -C $(RGB_LIBDIR) 16 | $(MAKE) -C examples-api-use 17 | 18 | clean: 19 | $(MAKE) -C lib clean 20 | $(MAKE) -C utils clean 21 | $(MAKE) -C examples-api-use clean 22 | $(MAKE) -C $(PYTHON_LIB_DIR) clean 23 | 24 | build-csharp: 25 | $(MAKE) -C $(CSHARP_LIB_DIR) build 26 | 27 | build-python: $(RGB_LIBRARY) 28 | $(MAKE) -C $(PYTHON_LIB_DIR) build 29 | 30 | install-python: build-python 31 | $(MAKE) -C $(PYTHON_LIB_DIR) install 32 | 33 | FORCE: 34 | .PHONY: FORCE 35 | -------------------------------------------------------------------------------- /bindings/python/samples/graphics.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from samplebase import SampleBase 3 | from rgbmatrix import graphics 4 | import time 5 | 6 | 7 | class GraphicsTest(SampleBase): 8 | def __init__(self, *args, **kwargs): 9 | super(GraphicsTest, self).__init__(*args, **kwargs) 10 | 11 | def run(self): 12 | canvas = self.matrix 13 | font = graphics.Font() 14 | font.LoadFont("../../../fonts/7x13.bdf") 15 | 16 | red = graphics.Color(255, 0, 0) 17 | graphics.DrawLine(canvas, 5, 5, 22, 13, red) 18 | 19 | green = graphics.Color(0, 255, 0) 20 | graphics.DrawCircle(canvas, 15, 15, 10, green) 21 | 22 | blue = graphics.Color(0, 0, 255) 23 | graphics.DrawText(canvas, font, 2, 10, blue, "Text") 24 | 25 | time.sleep(10) # show display for 10 seconds before exit 26 | 27 | 28 | # Main function 29 | if __name__ == "__main__": 30 | graphics_test = GraphicsTest() 31 | if (not graphics_test.process()): 32 | graphics_test.print_help() 33 | -------------------------------------------------------------------------------- /bindings/c#/examples/font-example.cs: -------------------------------------------------------------------------------- 1 | using rpi_rgb_led_matrix_sharp; 2 | using System; 3 | using System.Threading; 4 | 5 | namespace font_example 6 | { 7 | class Program 8 | { 9 | static int Main(string[] args) 10 | { 11 | if (args.Length < 1) 12 | { 13 | Console.WriteLine("font-example.exe [font_path] "); 14 | return -1; 15 | } 16 | string text = "Hello World!"; 17 | if (args.Length > 1) 18 | text = args[1]; 19 | 20 | 21 | var matrix = new RGBLedMatrix(32, 2, 1); 22 | var canvas = matrix.CreateOffscreenCanvas(); 23 | var font = new RGBLedFont(args[0]); 24 | 25 | canvas.DrawText(font, 1, 6, new Color(0, 255, 0), text); 26 | matrix.SwapOnVsync(canvas); 27 | 28 | while (!Console.KeyAvailable) 29 | { 30 | Thread.Sleep(250); 31 | } 32 | 33 | return 0; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/gpio-bits.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // Copyright (C) 2013 Henner Zeller 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation version 2. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see 15 | 16 | // This file needs to compile in C and C++ context, so deliberately broken out. 17 | 18 | #ifndef RPI_GPIOBITS_H 19 | #define RPI_GPIOBITS_H 20 | 21 | #include 22 | #ifdef ENABLE_WIDE_GPIO_COMPUTE_MODULE 23 | typedef uint64_t gpio_bits_t; 24 | #else 25 | typedef uint32_t gpio_bits_t; 26 | #endif 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /bindings/python/samples/pulsing-brightness.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from samplebase import SampleBase 3 | 4 | 5 | class GrayscaleBlock(SampleBase): 6 | def __init__(self, *args, **kwargs): 7 | super(GrayscaleBlock, self).__init__(*args, **kwargs) 8 | 9 | def run(self): 10 | max_brightness = self.matrix.brightness 11 | count = 0 12 | c = 255 13 | 14 | while (True): 15 | if self.matrix.brightness < 1: 16 | self.matrix.brightness = max_brightness 17 | count += 1 18 | else: 19 | self.matrix.brightness -= 1 20 | 21 | if count % 4 == 0: 22 | self.matrix.Fill(c, 0, 0) 23 | elif count % 4 == 1: 24 | self.matrix.Fill(0, c, 0) 25 | elif count % 4 == 2: 26 | self.matrix.Fill(0, 0, c) 27 | elif count % 4 == 3: 28 | self.matrix.Fill(c, c, c) 29 | 30 | self.usleep(20 * 1000) 31 | 32 | # Main function 33 | if __name__ == "__main__": 34 | grayscale_block = GrayscaleBlock() 35 | if (not grayscale_block.process()): 36 | grayscale_block.print_help() 37 | -------------------------------------------------------------------------------- /bindings/python/samples/simple-square.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from samplebase import SampleBase 3 | 4 | 5 | class SimpleSquare(SampleBase): 6 | def __init__(self, *args, **kwargs): 7 | super(SimpleSquare, self).__init__(*args, **kwargs) 8 | 9 | def run(self): 10 | offset_canvas = self.matrix.CreateFrameCanvas() 11 | while True: 12 | for x in range(0, self.matrix.width): 13 | offset_canvas.SetPixel(x, x, 255, 255, 255) 14 | offset_canvas.SetPixel(offset_canvas.height - 1 - x, x, 255, 0, 255) 15 | 16 | for x in range(0, offset_canvas.width): 17 | offset_canvas.SetPixel(x, 0, 255, 0, 0) 18 | offset_canvas.SetPixel(x, offset_canvas.height - 1, 255, 255, 0) 19 | 20 | for y in range(0, offset_canvas.height): 21 | offset_canvas.SetPixel(0, y, 0, 0, 255) 22 | offset_canvas.SetPixel(offset_canvas.width - 1, y, 0, 255, 0) 23 | offset_canvas = self.matrix.SwapOnVSync(offset_canvas) 24 | 25 | 26 | # Main function 27 | if __name__ == "__main__": 28 | simple_square = SimpleSquare() 29 | if (not simple_square.process()): 30 | simple_square.print_help() 31 | -------------------------------------------------------------------------------- /bindings/c#/examples/minimal-example.cs: -------------------------------------------------------------------------------- 1 | using rpi_rgb_led_matrix_sharp; 2 | 3 | namespace minimal_example 4 | { 5 | class Program 6 | { 7 | static int Main(string[] args) 8 | { 9 | 10 | var matrix= new RGBLedMatrix(32, 2, 1); 11 | var canvas = matrix.CreateOffscreenCanvas(); 12 | 13 | for (var i = 0; i < 1000; ++i) 14 | { 15 | for (var y = 0; y < canvas.Height; ++y) 16 | { 17 | for (var x = 0; x < canvas.Width; ++x) 18 | { 19 | canvas.SetPixel(x, y, new Color(i & 0xff, x, y)); 20 | } 21 | } 22 | canvas.DrawCircle(canvas.Width / 2, canvas.Height / 2, 6, new Color(0, 0, 255)); 23 | canvas.DrawLine(canvas.Width / 2 - 3, canvas.Height / 2 - 3, canvas.Width / 2 + 3, canvas.Height / 2 + 3, new Color(0, 0, 255)); 24 | canvas.DrawLine(canvas.Width / 2 - 3, canvas.Height / 2 + 3, canvas.Width / 2 + 3, canvas.Height / 2 - 3, new Color(0, 0, 255)); 25 | 26 | canvas = matrix.SwapOnVsync(canvas); 27 | } 28 | 29 | return 0; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /bindings/python/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from distutils.core import setup, Extension 3 | 4 | core_ext = Extension( 5 | name = 'core', 6 | sources = ['rgbmatrix/core.cpp'], 7 | include_dirs = ['../../include'], 8 | library_dirs = ['../../lib'], 9 | libraries = ['rgbmatrix'], 10 | extra_compile_args = ["-O3", "-Wall"], 11 | language = 'c++' 12 | ) 13 | 14 | graphics_ext = Extension( 15 | name = 'graphics', 16 | sources = ['rgbmatrix/graphics.cpp'], 17 | include_dirs = ['../../include'], 18 | library_dirs = ['../../lib'], 19 | libraries = ['rgbmatrix'], 20 | extra_compile_args = ["-O3", "-Wall"], 21 | language = 'c++' 22 | ) 23 | 24 | setup( 25 | name = 'rgbmatrix', 26 | version = '0.0.1', 27 | author = 'Christoph Friedrich', 28 | author_email = 'christoph.friedrich@vonaffenfels.de', 29 | classifiers = ['Development Status :: 3 - Alpha'], 30 | ext_package = 'rgbmatrix', 31 | ext_modules = [core_ext, graphics_ext], 32 | packages = ['rgbmatrix'] 33 | ) 34 | -------------------------------------------------------------------------------- /adapter/README.md: -------------------------------------------------------------------------------- 1 | PCB adapter for Raspberry Pi to Hub75 RGB Matrixes 2 | ================================================== 3 | 4 | Since hand-wiring can be a little tedious, here are some PCBs that help 5 | with the wiring when using the `rpi-rgb-led-matrix` code. 6 | 7 | * [Passive-3](./passive-3) Supports three parallel chains, directly connected 8 | to the output of a Rapsberry Pi with 40 GPIO pins. Works, but usually it is better to 9 | buffer the outputs using the ... 10 | * [Active-3](./active-3) board. Supports three parallel chains with active buffering 11 | and 3.3V -> 5V level shifting for best reliability. Requires SMD soldering. 12 | 13 | As another option you can buy it from these locations not affiliated with this project. 14 | They are given to help you locate premade boards but no guarantees are given or implied: 15 | * https://www.electrodragon.com/product/rgb-matrix-panel-drive-board-raspberry-pi/ 16 | ($3/board, but fairly long and/or expensive shipping from HKG) 17 | * Seller #2 (fill me) 18 | * The [Passive-RPi1](./passive-rpi1) adapter board is to connect one panel to 19 | Raspberry Pi 1 with 26 GPIO pins. 20 | 21 | ![Three Panels connected][three-panels] 22 | 23 | [three-panels]: ../img/three-parallel-panels-soic.jpg 24 | -------------------------------------------------------------------------------- /bindings/python/samples/runtext.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Display a runtext with double-buffering. 3 | from samplebase import SampleBase 4 | from rgbmatrix import graphics 5 | import time 6 | 7 | 8 | class RunText(SampleBase): 9 | def __init__(self, *args, **kwargs): 10 | super(RunText, self).__init__(*args, **kwargs) 11 | self.parser.add_argument("-t", "--text", help="The text to scroll on the RGB LED panel", default="Hello world!") 12 | 13 | def run(self): 14 | offscreen_canvas = self.matrix.CreateFrameCanvas() 15 | font = graphics.Font() 16 | font.LoadFont("../../../fonts/7x13.bdf") 17 | textColor = graphics.Color(255, 255, 0) 18 | pos = offscreen_canvas.width 19 | my_text = self.args.text 20 | 21 | while True: 22 | offscreen_canvas.Clear() 23 | len = graphics.DrawText(offscreen_canvas, font, pos, 10, textColor, my_text) 24 | pos -= 1 25 | if (pos + len < 0): 26 | pos = offscreen_canvas.width 27 | 28 | time.sleep(0.05) 29 | offscreen_canvas = self.matrix.SwapOnVSync(offscreen_canvas) 30 | 31 | 32 | # Main function 33 | if __name__ == "__main__": 34 | run_text = RunText() 35 | if (not run_text.process()): 36 | run_text.print_help() 37 | -------------------------------------------------------------------------------- /bindings/python/samples/pulsing-colors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from samplebase import SampleBase 3 | 4 | 5 | class PulsingColors(SampleBase): 6 | def __init__(self, *args, **kwargs): 7 | super(PulsingColors, self).__init__(*args, **kwargs) 8 | 9 | def run(self): 10 | self.offscreen_canvas = self.matrix.CreateFrameCanvas() 11 | continuum = 0 12 | 13 | while True: 14 | self.usleep(5 * 1000) 15 | continuum += 1 16 | continuum %= 3 * 255 17 | 18 | red = 0 19 | green = 0 20 | blue = 0 21 | 22 | if continuum <= 255: 23 | c = continuum 24 | blue = 255 - c 25 | red = c 26 | elif continuum > 255 and continuum <= 511: 27 | c = continuum - 256 28 | red = 255 - c 29 | green = c 30 | else: 31 | c = continuum - 512 32 | green = 255 - c 33 | blue = c 34 | 35 | self.offscreen_canvas.Fill(red, green, blue) 36 | self.offscreen_canvas = self.matrix.SwapOnVSync(self.offscreen_canvas) 37 | 38 | # Main function 39 | if __name__ == "__main__": 40 | pulsing_colors = PulsingColors() 41 | if (not pulsing_colors.process()): 42 | pulsing_colors.print_help() 43 | -------------------------------------------------------------------------------- /bindings/python/samples/grayscale-block.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from samplebase import SampleBase 3 | import time 4 | 5 | 6 | class GrayscaleBlock(SampleBase): 7 | def __init__(self, *args, **kwargs): 8 | super(GrayscaleBlock, self).__init__(*args, **kwargs) 9 | 10 | def run(self): 11 | sub_blocks = 16 12 | width = self.matrix.width 13 | height = self.matrix.height 14 | x_step = max(1, width / sub_blocks) 15 | y_step = max(1, height / sub_blocks) 16 | count = 0 17 | 18 | while True: 19 | for y in range(0, height): 20 | for x in range(0, width): 21 | c = sub_blocks * int(y / y_step) + int(x / x_step) 22 | if count % 4 == 0: 23 | self.matrix.SetPixel(x, y, c, c, c) 24 | elif count % 4 == 1: 25 | self.matrix.SetPixel(x, y, c, 0, 0) 26 | elif count % 4 == 2: 27 | self.matrix.SetPixel(x, y, 0, c, 0) 28 | elif count % 4 == 3: 29 | self.matrix.SetPixel(x, y, 0, 0, c) 30 | 31 | count += 1 32 | time.sleep(2) 33 | 34 | 35 | # Main function 36 | if __name__ == "__main__": 37 | grayscale_block = GrayscaleBlock() 38 | if (not grayscale_block.process()): 39 | grayscale_block.print_help() 40 | -------------------------------------------------------------------------------- /adapter/passive-rpi1/passive-rpi-hub75-adapter.pro: -------------------------------------------------------------------------------- 1 | update=Sun 14 Jun 2015 04:45:24 PM PDT 2 | version=1 3 | last_client=kicad 4 | [cvpcb] 5 | version=1 6 | NetIExt=net 7 | [cvpcb/libraries] 8 | EquName1=devcms 9 | [general] 10 | version=1 11 | [eeschema] 12 | version=1 13 | LibDir= 14 | [eeschema/libraries] 15 | LibName1=power 16 | LibName2=device 17 | LibName3=transistors 18 | LibName4=conn 19 | LibName5=linear 20 | LibName6=regul 21 | LibName7=74xx 22 | LibName8=cmos4000 23 | LibName9=adc-dac 24 | LibName10=memory 25 | LibName11=xilinx 26 | LibName12=microcontrollers 27 | LibName13=dsp 28 | LibName14=microchip 29 | LibName15=analog_switches 30 | LibName16=motorola 31 | LibName17=texas 32 | LibName18=intel 33 | LibName19=audio 34 | LibName20=interface 35 | LibName21=digital-audio 36 | LibName22=philips 37 | LibName23=display 38 | LibName24=cypress 39 | LibName25=siliconi 40 | LibName26=opto 41 | LibName27=atmel 42 | LibName28=contrib 43 | LibName29=valves 44 | [pcbnew] 45 | version=1 46 | PageLayoutDescrFile= 47 | LastNetListRead= 48 | PadDrill=3.048 49 | PadDrillOvalY=3.048 50 | PadSizeH=4.064 51 | PadSizeV=4.064 52 | PcbTextSizeV=1.5 53 | PcbTextSizeH=1.5 54 | PcbTextThickness=0.3 55 | ModuleTextSizeV=1 56 | ModuleTextSizeH=1 57 | ModuleTextSizeThickness=0.15 58 | SolderMaskClearance=0 59 | SolderMaskMinWidth=0 60 | DrawSegmentWidth=0.2 61 | BoardOutlineThickness=0.09999999999999999 62 | ModuleOutlineThickness=0.15 63 | -------------------------------------------------------------------------------- /adapter/passive-3/passive3-rpi-hub75-adapter.pro: -------------------------------------------------------------------------------- 1 | update=Wed 17 Jun 2015 07:02:16 PM PDT 2 | version=1 3 | last_client=kicad 4 | [cvpcb] 5 | version=1 6 | NetIExt=net 7 | [cvpcb/libraries] 8 | EquName1=devcms 9 | [general] 10 | version=1 11 | [pcbnew] 12 | version=1 13 | PageLayoutDescrFile= 14 | LastNetListRead=passive3-rpi-hub75-adapter.net 15 | UseCmpFile=0 16 | PadDrill=3.048 17 | PadDrillOvalY=3.048 18 | PadSizeH=4.064 19 | PadSizeV=4.064 20 | PcbTextSizeV=1.5 21 | PcbTextSizeH=1.5 22 | PcbTextThickness=0.3 23 | ModuleTextSizeV=1 24 | ModuleTextSizeH=1 25 | ModuleTextSizeThickness=0.15 26 | SolderMaskClearance=0 27 | SolderMaskMinWidth=0 28 | DrawSegmentWidth=0.2 29 | BoardOutlineThickness=0.09999999999999999 30 | ModuleOutlineThickness=0.15 31 | [eeschema] 32 | version=1 33 | LibDir= 34 | [eeschema/libraries] 35 | LibName1=power 36 | LibName2=device 37 | LibName3=transistors 38 | LibName4=conn 39 | LibName5=linear 40 | LibName6=regul 41 | LibName7=74xx 42 | LibName8=cmos4000 43 | LibName9=adc-dac 44 | LibName10=memory 45 | LibName11=xilinx 46 | LibName12=microcontrollers 47 | LibName13=dsp 48 | LibName14=microchip 49 | LibName15=analog_switches 50 | LibName16=motorola 51 | LibName17=texas 52 | LibName18=intel 53 | LibName19=audio 54 | LibName20=interface 55 | LibName21=digital-audio 56 | LibName22=philips 57 | LibName23=display 58 | LibName24=cypress 59 | LibName25=siliconi 60 | LibName26=opto 61 | LibName27=atmel 62 | LibName28=contrib 63 | LibName29=valves 64 | -------------------------------------------------------------------------------- /bindings/python/samples/image-scroller.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import time 3 | from samplebase import SampleBase 4 | from PIL import Image 5 | 6 | 7 | class ImageScroller(SampleBase): 8 | def __init__(self, *args, **kwargs): 9 | super(ImageScroller, self).__init__(*args, **kwargs) 10 | self.parser.add_argument("-i", "--image", help="The image to display", default="../../../examples-api-use/runtext.ppm") 11 | 12 | def run(self): 13 | if not 'image' in self.__dict__: 14 | self.image = Image.open(self.args.image).convert('RGB') 15 | self.image.resize((self.matrix.width, self.matrix.height), Image.ANTIALIAS) 16 | 17 | double_buffer = self.matrix.CreateFrameCanvas() 18 | img_width, img_height = self.image.size 19 | 20 | # let's scroll 21 | xpos = 0 22 | while True: 23 | xpos += 1 24 | if (xpos > img_width): 25 | xpos = 0 26 | 27 | double_buffer.SetImage(self.image, -xpos) 28 | double_buffer.SetImage(self.image, -xpos + img_width) 29 | 30 | double_buffer = self.matrix.SwapOnVSync(double_buffer) 31 | time.sleep(0.01) 32 | 33 | # Main function 34 | # e.g. call with 35 | # sudo ./image-scroller.py --chain=4 36 | # if you have a chain of four 37 | if __name__ == "__main__": 38 | image_scroller = ImageScroller() 39 | if (not image_scroller.process()): 40 | image_scroller.print_help() 41 | -------------------------------------------------------------------------------- /adapter/passive-3/README.md: -------------------------------------------------------------------------------- 1 | Adapter PCB to support up to 3 panel chains 2 | =========================================== 3 | 4 | * This is a passive board. It is simple, but the logic level will be out of 5 | spec for the LED matrix (3.3V vs. 5V) which might or might not work. 6 | Driving long cables with the GPIO pins is also not a good idea. 7 | * You typically want to consider using the [active board](../active-3). 8 | * Works for Matrix up to 1:16 multiplexing (32 rows). For 1:32 multiplexing, 9 | you want to use the [active board](../active-3). You can of also hack 10 | this board as [suggested in this bugtracker entry](https://github.com/hzeller/rpi-rgb-led-matrix/issues/360#issuecomment-321104348) to make it work with 11 | 64x64 boards. 12 | * Only really advisable, if the LED panels have 74HCT245 (as opposed to just 13 | 74HC245) in their input stage, because then they can deal properly with 14 | the 3.3V logic levels coming from the Pi. 15 | * Supports up to three panel chains for newer plus models and 16 | Raspberry Pi 2/3 that have 40 GPIO pins. 17 | * Open source KiCAD PCB EDA format. 18 | * (not very pretty layout, was just lazy and let the auto-router do it). 19 | * The FAB files are provided as [passive3-rpi-hub75-adapter-fab.zip](./passive3-rpi-hub75-adapter-fab.zip) 20 | 21 | This board is [shared on OSH Park][osh-passive3] (not affiliated). 22 | 23 | ![Preview][rendering] 24 | 25 | [rendering]: ../../img/passive3-pcb.png 26 | [osh-passive3]: https://oshpark.com/shared_projects/FNAtZUsP 27 | -------------------------------------------------------------------------------- /lib/multiplex-mappers-internal.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // Copyright (C) 2017 Henner Zeller 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation version 2. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see 15 | 16 | #include 17 | 18 | #include "pixel-mapper.h" 19 | 20 | namespace rgb_matrix { 21 | namespace internal { 22 | 23 | class MultiplexMapper : public PixelMapper { 24 | public: 25 | // Function that edits the original rows and columns of the panels 26 | // provided by the user to the actual underlying mapping. This is called 27 | // before we do the actual set-up of the GPIO mapping as this influences 28 | // the hardware interface. 29 | // This is so that the user can provide the rows/columns they see. 30 | virtual void EditColsRows(int *cols, int *rows) const = 0; 31 | }; 32 | 33 | // Returns a vector of the registered Multiplex mappers. 34 | typedef std::vector MuxMapperList; 35 | const MuxMapperList &GetRegisteredMultiplexMappers(); 36 | 37 | } // namespace internal 38 | } // namespace rgb_matrix 39 | -------------------------------------------------------------------------------- /bindings/c#/examples/Makefile: -------------------------------------------------------------------------------- 1 | CSHARP_LIB=RGBLedMatrix.dll 2 | CSHARP_COMPILER=mcs 3 | CSHARP_LIBDIR=.. 4 | CSHARP_LIBRARY=$(CSHARP_LIBDIR)/$(CSHARP_LIB) 5 | 6 | RGB_LIBDIR=../../../lib 7 | RGB_LIBRARY_NAME=librgbmatrix 8 | RGB_LIBRARY=$(RGB_LIBDIR)/$(RGB_LIBRARY_NAME).so.1 9 | 10 | all: $(CSHARP_LIB) 11 | cp $(RGB_LIBRARY) $(RGB_LIBRARY_NAME).so 12 | cp $(CSHARP_LIBRARY) $(CSHARP_LIB) 13 | $(CSHARP_COMPILER) -r:$(CSHARP_LIB) -out:minimal-example.exe minimal-example.cs 14 | $(CSHARP_COMPILER) -r:$(CSHARP_LIB) -out:matrix-rain.exe matrix-rain.cs 15 | $(CSHARP_COMPILER) -r:$(CSHARP_LIB) -out:font-example.exe font-example.cs 16 | $(CSHARP_COMPILER) -r:$(CSHARP_LIB) -out:pulsing-brightness.exe pulsing-brightness.cs 17 | 18 | minimal-example.exe: $(CSHARP_LIB) 19 | cp $(RGB_LIBRARY) $(RGB_LIBRARY_NAME).so 20 | cp $(CSHARP_LIBRARY) $(CSHARP_LIB) 21 | $(CSHARP_COMPILER) -r:$(CSHARP_LIB) -out:minimal-example.exe minimal-example.cs 22 | 23 | matrix-rain.exe: $(CSHARP_LIB) 24 | cp $(RGB_LIBRARY) $(RGB_LIBRARY_NAME).so 25 | cp $(CSHARP_LIBRARY) $(CSHARP_LIB) 26 | $(CSHARP_COMPILER) -r:$(CSHARP_LIB) -out:matrix-rain.exe matrix-rain.cs 27 | 28 | font-example.exe: $(CSHARP_LIB) 29 | cp $(RGB_LIBRARY) $(RGB_LIBRARY_NAME).so 30 | cp $(CSHARP_LIBRARY) $(CSHARP_LIB) 31 | $(CSHARP_COMPILER) -r:$(CSHARP_LIB) -out:font-example.exe font-example.cs 32 | 33 | pulsing-brightness.exe: $(CSHARP_LIB) 34 | cp $(RGB_LIBRARY) $(RGB_LIBRARY_NAME).so 35 | cp $(CSHARP_LIBRARY) $(CSHARP_LIB) 36 | $(CSHARP_COMPILER) -r:$(CSHARP_LIB) -out:pulsing-brightness.exe pulsing-brightness.cs 37 | 38 | $(CSHARP_LIB) : 39 | $(MAKE) -C $(CSHARP_LIBDIR) 40 | 41 | .PHONY : all 42 | -------------------------------------------------------------------------------- /bindings/c#/examples/pulsing-brightness.cs: -------------------------------------------------------------------------------- 1 | using rpi_rgb_led_matrix_sharp; 2 | using System; 3 | using System.Threading; 4 | 5 | namespace pulsing_brightness 6 | { 7 | class Program 8 | { 9 | static int Main(string[] args) 10 | { 11 | var matrix = new RGBLedMatrix(new RGBLedMatrixOptions {Rows = 32, Cols = 64}); 12 | var canvas = matrix.CreateOffscreenCanvas(); 13 | var maxBrightness = matrix.Brightness; 14 | var count = 0; 15 | const int c = 255; 16 | 17 | while (!Console.KeyAvailable) 18 | { 19 | if (matrix.Brightness < 1) 20 | { 21 | matrix.Brightness = maxBrightness; 22 | count += 1; 23 | } 24 | else 25 | { 26 | matrix.Brightness -= 1; 27 | } 28 | 29 | switch (count % 4) 30 | { 31 | case 0: 32 | canvas.Fill(new Color(c, 0, 0)); 33 | break; 34 | case 1: 35 | canvas.Fill(new Color(0, c, 0)); 36 | break; 37 | case 2: 38 | canvas.Fill(new Color(0, 0, c)); 39 | break; 40 | case 3: 41 | canvas.Fill(new Color(c, c, c)); 42 | break; 43 | } 44 | 45 | canvas = matrix.SwapOnVsync(canvas); 46 | 47 | Thread.Sleep(20); 48 | } 49 | 50 | return 0; 51 | } 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /lib/hardware-mapping.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | * Copyright (C) 2013 Henner Zeller 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation version 2. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see 15 | */ 16 | #ifndef RPI_HARDWARE_MAPPING_H 17 | #define RPI_HARDWARE_MAPPING_H 18 | 19 | #ifdef __cplusplus 20 | extern "C" { 21 | #endif 22 | 23 | #include "gpio-bits.h" 24 | 25 | struct HardwareMapping { 26 | const char *name; 27 | int max_parallel_chains; 28 | 29 | gpio_bits_t output_enable; 30 | gpio_bits_t clock; 31 | gpio_bits_t strobe; 32 | 33 | gpio_bits_t a, b, c, d, e; 34 | 35 | gpio_bits_t p0_r1, p0_g1, p0_b1; 36 | gpio_bits_t p0_r2, p0_g2, p0_b2; 37 | 38 | gpio_bits_t p1_r1, p1_g1, p1_b1; 39 | gpio_bits_t p1_r2, p1_g2, p1_b2; 40 | 41 | gpio_bits_t p2_r1, p2_g1, p2_b1; 42 | gpio_bits_t p2_r2, p2_g2, p2_b2; 43 | 44 | gpio_bits_t p3_r1, p3_g1, p3_b1; 45 | gpio_bits_t p3_r2, p3_g2, p3_b2; 46 | 47 | gpio_bits_t p4_r1, p4_g1, p4_b1; 48 | gpio_bits_t p4_r2, p4_g2, p4_b2; 49 | 50 | gpio_bits_t p5_r1, p5_g1, p5_b1; 51 | gpio_bits_t p5_r2, p5_g2, p5_b2; 52 | }; 53 | 54 | extern struct HardwareMapping matrix_hardware_mappings[]; 55 | 56 | #ifdef __cplusplus 57 | } // extern C 58 | #endif 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /utils/Makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS=-O3 -W -Wall -Wextra -Wno-unused-parameter -D_FILE_OFFSET_BITS=64 2 | OBJECTS=led-image-viewer.o text-scroller.o 3 | BINARIES=led-image-viewer text-scroller 4 | 5 | OPTIONAL_OBJECTS=video-viewer.o 6 | OPTIONAL_BINARIES=video-viewer 7 | 8 | # Where our library resides. You mostly only need to change the 9 | # RGB_LIB_DISTRIBUTION, this is where the library is checked out. 10 | RGB_LIB_DISTRIBUTION=.. 11 | RGB_INCDIR=$(RGB_LIB_DISTRIBUTION)/include 12 | RGB_LIBDIR=$(RGB_LIB_DISTRIBUTION)/lib 13 | RGB_LIBRARY_NAME=rgbmatrix 14 | RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a 15 | RGB_LDFLAGS+=-L$(RGB_LIBDIR) -l$(RGB_LIBRARY_NAME) -lrt -lm -lpthread 16 | 17 | # Imagemagic flags, only needed if actually compiled. 18 | MAGICK_CXXFLAGS?=$(shell GraphicsMagick++-config --cppflags --cxxflags) 19 | MAGICK_LDFLAGS?=$(shell GraphicsMagick++-config --ldflags --libs) 20 | AV_CXXFLAGS=$(shell pkg-config --cflags libavcodec libavformat libswscale libavutil) 21 | AV_LDFLAGS=$(shell pkg-config --cflags --libs libavcodec libavformat libswscale libavutil) 22 | 23 | simple: $(BINARIES) 24 | 25 | all : $(BINARIES) $(OPTIONAL_BINARIES) 26 | 27 | $(RGB_LIBRARY): FORCE 28 | $(MAKE) -C $(RGB_LIBDIR) 29 | 30 | text-scroller: text-scroller.o $(RGB_LIBRARY) 31 | $(CXX) $(CXXFLAGS) text-scroller.o -o $@ $(LDFLAGS) $(RGB_LDFLAGS) 32 | 33 | led-image-viewer: led-image-viewer.o $(RGB_LIBRARY) 34 | $(CXX) $(CXXFLAGS) led-image-viewer.o -o $@ $(LDFLAGS) $(RGB_LDFLAGS) $(MAGICK_LDFLAGS) 35 | 36 | video-viewer: video-viewer.o $(RGB_LIBRARY) 37 | $(CXX) $(CXXFLAGS) video-viewer.o -o $@ $(LDFLAGS) $(RGB_LDFLAGS) $(AV_LDFLAGS) 38 | 39 | %.o : %.cc 40 | $(CXX) -I$(RGB_INCDIR) $(CXXFLAGS) -c -o $@ $< 41 | 42 | led-image-viewer.o : led-image-viewer.cc 43 | $(CXX) -I$(RGB_INCDIR) $(CXXFLAGS) $(MAGICK_CXXFLAGS) -c -o $@ $< 44 | 45 | clean: 46 | rm -f $(OBJECTS) $(BINARIES) $(OPTIONAL_OBJECTS) $(OPTIONAL_BINARIES) 47 | 48 | FORCE: 49 | .PHONY: FORCE 50 | -------------------------------------------------------------------------------- /bindings/python/rgbmatrix/graphics.pyx: -------------------------------------------------------------------------------- 1 | # distutils: language = c++ 2 | 3 | from libcpp cimport bool 4 | from libc.stdint cimport uint8_t, uint32_t 5 | 6 | cimport core 7 | 8 | cdef class Color: 9 | def __init__(self, uint8_t red = 0, uint8_t green = 0, uint8_t blue = 0): 10 | self.__color.r = red 11 | self.__color.g = green 12 | self.__color.b = blue 13 | 14 | property red: 15 | def __get__(self): return self.__color.r 16 | def __set__(self, uint8_t value): self.__color.r = value 17 | 18 | property green: 19 | def __get__(self): return self.__color.g 20 | def __set__(self, uint8_t value): self.__color.g = value 21 | 22 | property blue: 23 | def __get__(self): return self.__color.b 24 | def __set__(self, uint8_t value): self.__color.b = value 25 | 26 | cdef class Font: 27 | def CharacterWidth(self, uint32_t char): 28 | return self.__font.CharacterWidth(char) 29 | 30 | def LoadFont(self, file): 31 | if (not self.__font.LoadFont(file.encode('utf-8'))): 32 | raise Exception("Couldn't load font " + file) 33 | 34 | def DrawGlyph(self, core.Canvas c, int x, int y, Color color, uint32_t char): 35 | return self.__font.DrawGlyph(c.__getCanvas(), x, y, color.__color, char) 36 | 37 | property height: 38 | def __get__(self): return self.__font.height() 39 | 40 | property baseline: 41 | def __get__(self): return self.__font.baseline() 42 | 43 | def DrawText(core.Canvas c, Font f, int x, int y, Color color, text): 44 | return cppinc.DrawText(c.__getCanvas(), f.__font, x, y, color.__color, text.encode('utf-8')) 45 | 46 | def DrawCircle(core.Canvas c, int x, int y, int r, Color color): 47 | cppinc.DrawCircle(c.__getCanvas(), x, y, r, color.__color) 48 | 49 | def DrawLine(core.Canvas c, int x1, int y1, int x2, int y2, Color color): 50 | cppinc.DrawLine(c.__getCanvas(), x1, y1, x2, y2, color.__color) 51 | 52 | # Local Variables: 53 | # mode: python 54 | # End: 55 | -------------------------------------------------------------------------------- /bindings/python/samples/image-draw.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # (This is an example similar to an example from the Adafruit fork 4 | # to show the similarities. Most important difference currently is, that 5 | # this library wants RGB mode.) 6 | # 7 | # A more complex RGBMatrix example works with the Python Imaging Library, 8 | # demonstrating a few graphics primitives and image loading. 9 | # Note that PIL graphics do not have an immediate effect on the display -- 10 | # image is drawn into a separate buffer, which is then copied to the matrix 11 | # using the SetImage() function (see examples below). 12 | # Requires rgbmatrix.so present in the same directory. 13 | 14 | # PIL Image module (create or load images) is explained here: 15 | # http://effbot.org/imagingbook/image.htm 16 | # PIL ImageDraw module (draw shapes to images) explained here: 17 | # http://effbot.org/imagingbook/imagedraw.htm 18 | 19 | from PIL import Image 20 | from PIL import ImageDraw 21 | import time 22 | from rgbmatrix import RGBMatrix, RGBMatrixOptions 23 | 24 | # Configuration for the matrix 25 | options = RGBMatrixOptions() 26 | options.rows = 32 27 | options.chain_length = 1 28 | options.parallel = 1 29 | options.hardware_mapping = 'regular' # If you have an Adafruit HAT: 'adafruit-hat' 30 | 31 | matrix = RGBMatrix(options = options) 32 | 33 | # RGB example w/graphics prims. 34 | # Note, only "RGB" mode is supported currently. 35 | image = Image.new("RGB", (32, 32)) # Can be larger than matrix if wanted!! 36 | draw = ImageDraw.Draw(image) # Declare Draw instance before prims 37 | # Draw some shapes into image (no immediate effect on matrix)... 38 | draw.rectangle((0, 0, 31, 31), fill=(0, 0, 0), outline=(0, 0, 255)) 39 | draw.line((0, 0, 31, 31), fill=(255, 0, 0)) 40 | draw.line((0, 31, 31, 0), fill=(0, 255, 0)) 41 | 42 | # Then scroll image across matrix... 43 | for n in range(-32, 33): # Start off top-left, move off bottom-right 44 | matrix.Clear() 45 | matrix.SetImage(image, n, n) 46 | time.sleep(0.05) 47 | 48 | matrix.Clear() 49 | -------------------------------------------------------------------------------- /examples-api-use/c-example.c: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | * 3 | * Using the C-API of this library. 4 | * 5 | */ 6 | #include "led-matrix-c.h" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | int main(int argc, char **argv) { 13 | struct RGBLedMatrixOptions options; 14 | struct RGBLedMatrix *matrix; 15 | struct LedCanvas *offscreen_canvas; 16 | int width, height; 17 | int x, y, i; 18 | 19 | memset(&options, 0, sizeof(options)); 20 | options.rows = 32; 21 | options.chain_length = 1; 22 | 23 | /* This supports all the led commandline options. Try --led-help */ 24 | matrix = led_matrix_create_from_options(&options, &argc, &argv); 25 | if (matrix == NULL) 26 | return 1; 27 | 28 | /* Let's do an example with double-buffering. We create one extra 29 | * buffer onto which we draw, which is then swapped on each refresh. 30 | * This is typically a good aproach for animations and such. 31 | */ 32 | offscreen_canvas = led_matrix_create_offscreen_canvas(matrix); 33 | 34 | led_canvas_get_size(offscreen_canvas, &width, &height); 35 | 36 | fprintf(stderr, "Size: %dx%d. Hardware gpio mapping: %s\n", 37 | width, height, options.hardware_mapping); 38 | 39 | for (i = 0; i < 1000; ++i) { 40 | for (y = 0; y < height; ++y) { 41 | for (x = 0; x < width; ++x) { 42 | led_canvas_set_pixel(offscreen_canvas, x, y, i & 0xff, x, y); 43 | } 44 | } 45 | 46 | /* Now, we swap the canvas. We give swap_on_vsync the buffer we 47 | * just have drawn into, and wait until the next vsync happens. 48 | * we get back the unused buffer to which we'll draw in the next 49 | * iteration. 50 | */ 51 | offscreen_canvas = led_matrix_swap_on_vsync(matrix, offscreen_canvas); 52 | } 53 | 54 | /* 55 | * Make sure to always call led_matrix_delete() in the end to reset the 56 | * display. Installing signal handlers for defined exit is a good idea. 57 | */ 58 | led_matrix_delete(matrix); 59 | 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /lib/utf8-internal.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // Copyright (C) 2013 Henner Zeller 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation version 2. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see 15 | #ifndef RPI_GRAPHICS_UTF8_H 16 | #define RPI_GRAPHICS_UTF8_H 17 | 18 | #include 19 | 20 | // Utility function that reads UTF-8 encoded codepoints from byte iterator. 21 | // No error checking, we assume string is UTF-8 clean. 22 | template 23 | uint32_t utf8_next_codepoint(byte_iterator &it) { 24 | uint32_t cp = *it++; 25 | if (cp < 0x80) { 26 | return cp; // iterator already incremented. 27 | } 28 | else if ((cp & 0xE0) == 0xC0) { 29 | cp = ((cp & 0x1F) << 6) + (*it & 0x3F); 30 | } 31 | else if ((cp & 0xF0) == 0xE0) { 32 | cp = ((cp & 0x0F) << 12) + ((*it & 0x3F) << 6); 33 | cp += (*++it & 0x3F); 34 | } 35 | else if ((cp & 0xF8) == 0xF0) { 36 | cp = ((cp & 0x07) << 18) + ((*it & 0x3F) << 12); 37 | cp += (*++it & 0x3F) << 6; 38 | cp += (*++it & 0x3F); 39 | } 40 | else if ((cp & 0xFC) == 0xF8) { 41 | cp = ((cp & 0x03) << 24) + ((*it & 0x3F) << 18); 42 | cp += (*++it & 0x3F) << 12; 43 | cp += (*++it & 0x3F) << 6; 44 | cp += (*++it & 0x3F); 45 | } 46 | else if ((cp & 0xFE) == 0xFC) { 47 | cp = ((cp & 0x01) << 30) + ((*it & 0x3F) << 24); 48 | cp += (*++it & 0x3F) << 18; 49 | cp += (*++it & 0x3F) << 12; 50 | cp += (*++it & 0x3F) << 6; 51 | cp += (*++it & 0x3F); 52 | } 53 | ++it; 54 | return cp; 55 | } 56 | 57 | #endif // RPI_GRAPHICS_UTF8_H 58 | -------------------------------------------------------------------------------- /examples-api-use/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-Wall -O3 -g -Wextra -Wno-unused-parameter 2 | CXXFLAGS=$(CFLAGS) 3 | OBJECTS=demo-main.o minimal-example.o c-example.o text-example.o scrolling-text-example.o clock.o ledcat.o input-example.o pixel-mover.o 4 | BINARIES=demo minimal-example c-example text-example scrolling-text-example clock ledcat input-example pixel-mover 5 | 6 | # Where our library resides. You mostly only need to change the 7 | # RGB_LIB_DISTRIBUTION, this is where the library is checked out. 8 | RGB_LIB_DISTRIBUTION=.. 9 | RGB_INCDIR=$(RGB_LIB_DISTRIBUTION)/include 10 | RGB_LIBDIR=$(RGB_LIB_DISTRIBUTION)/lib 11 | RGB_LIBRARY_NAME=rgbmatrix 12 | RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a 13 | LDFLAGS+=-L$(RGB_LIBDIR) -l$(RGB_LIBRARY_NAME) -lrt -lm -lpthread 14 | 15 | # To compile image-example 16 | MAGICK_CXXFLAGS?=$(shell GraphicsMagick++-config --cppflags --cxxflags) 17 | MAGICK_LDFLAGS?=$(shell GraphicsMagick++-config --ldflags --libs) 18 | 19 | all : $(BINARIES) 20 | 21 | $(RGB_LIBRARY): FORCE 22 | $(MAKE) -C $(RGB_LIBDIR) 23 | 24 | demo : demo-main.o $(RGB_LIBRARY) 25 | $(CXX) $< -o $@ $(LDFLAGS) 26 | 27 | minimal-example : minimal-example.o 28 | input-example : input-example.o 29 | text-example: text-example.o 30 | scrolling-text-example : scrolling-text-example.o 31 | clock : clock.o 32 | ledcat : ledcat.o 33 | pixel-mover : pixel-mover.o 34 | 35 | # All the binaries that have the same name as the object file.q 36 | % : %.o $(RGB_LIBRARY) 37 | $(CXX) $< -o $@ $(LDFLAGS) 38 | 39 | image-example.o : image-example.cc 40 | $(CXX) -I$(RGB_INCDIR) $(CXXFLAGS) $(MAGICK_CXXFLAGS) -c -o $@ $< 41 | 42 | image-example: image-example.o 43 | $(CXX) $< -o $@ $(LDFLAGS) $(MAGICK_LDFLAGS) 44 | 45 | # Since the C example uses the C++ library underneath, which depends on C++ 46 | # runtime stuff, you still have to also link -lstdc++ 47 | c-example : c-example.o $(RGB_LIBRARY) 48 | $(CC) $< -o $@ $(LDFLAGS) -lstdc++ 49 | 50 | %.o : %.cc 51 | $(CXX) -I$(RGB_INCDIR) $(CXXFLAGS) -c -o $@ $< 52 | 53 | %.o : %.c 54 | $(CC) -I$(RGB_INCDIR) $(CFLAGS) -c -o $@ $< 55 | 56 | clean: 57 | rm -f $(OBJECTS) $(BINARIES) 58 | 59 | FORCE: 60 | .PHONY: FORCE 61 | -------------------------------------------------------------------------------- /examples-api-use/input-example.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // Small example how to use the input bits 3 | // 4 | // This code is public domain 5 | // (but note, that the led-matrix library this depends on is GPL v2) 6 | 7 | #include "led-matrix.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using rgb_matrix::RGBMatrix; 15 | using rgb_matrix::Canvas; 16 | 17 | volatile bool interrupt_received = false; 18 | static void InterruptHandler(int signo) { 19 | interrupt_received = true; 20 | } 21 | 22 | int main(int argc, char *argv[]) { 23 | RGBMatrix::Options defaults; 24 | defaults.hardware_mapping = "regular"; // or e.g. "adafruit-hat" 25 | defaults.rows = 32; 26 | defaults.chain_length = 1; 27 | defaults.parallel = 1; 28 | RGBMatrix *matrix = RGBMatrix::CreateFromFlags(&argc, &argv, &defaults); 29 | if (matrix == NULL) 30 | return 1; 31 | 32 | // It is always good to set up a signal handler to cleanly exit when we 33 | // receive a CTRL-C for instance. 34 | signal(SIGTERM, InterruptHandler); 35 | signal(SIGINT, InterruptHandler); 36 | 37 | // Let's request all input bits and see which are actually available. 38 | // This will differ depending on which hardware mapping you use and how 39 | // many parallel chains you have. 40 | const uint64_t available_inputs = matrix->RequestInputs(0xffffffff); 41 | fprintf(stderr, "Available GPIO-bits: "); 42 | for (int b = 0; b < 32; ++b) { 43 | if (available_inputs & (1<AwaitInputChange(100); 51 | 52 | // Minimal output: let's show the bits with LEDs in the first row 53 | for (int b = 0; b < 32; ++b) { 54 | uint8_t col = (inputs & (1<SetPixel(32-b, 0, col, col, col); 56 | } 57 | } 58 | 59 | fprintf(stderr, "Exiting.\n"); 60 | matrix->Clear(); 61 | delete matrix; 62 | 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /include/canvas.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // Copyright (C) 2014 Henner Zeller 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation version 2. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see 15 | 16 | #ifndef RPI_CANVAS_H 17 | #define RPI_CANVAS_H 18 | #include 19 | 20 | namespace rgb_matrix { 21 | // An interface for things a Canvas can do. The RGBMatrix implements this 22 | // interface, so you can use it directly wherever a canvas is needed. 23 | // 24 | // This abstraction also allows you to e.g. create delegating 25 | // implementations that do a particular transformation, e.g. re-map 26 | // pixels (as you might lay out the physical RGB matrix in a different way), 27 | // compose images (OR, XOR, transparecy), scale, rotate, anti-alias or 28 | // translate coordinates in a funky way. 29 | // 30 | // It is a good idea to have your applications use the concept of 31 | // a Canvas to write the content to instead of directly using the RGBMatrix. 32 | class Canvas { 33 | public: 34 | virtual ~Canvas() {} 35 | virtual int width() const = 0; // Pixels available in x direction. 36 | virtual int height() const = 0; // Pixels available in y direction. 37 | 38 | // Set pixel at coordinate (x,y) with given color. Pixel (0,0) is the 39 | // top left corner. 40 | // Each color is 8 bit (24bpp), 0 black, 255 brightest. 41 | virtual void SetPixel(int x, int y, 42 | uint8_t red, uint8_t green, uint8_t blue) = 0; 43 | 44 | // Clear screen to be all black. 45 | virtual void Clear() = 0; 46 | 47 | // Fill screen with given 24bpp color. 48 | virtual void Fill(uint8_t red, uint8_t green, uint8_t blue) = 0; 49 | }; 50 | 51 | } // namespace rgb_matrix 52 | #endif // RPI_CANVAS_H 53 | -------------------------------------------------------------------------------- /examples-api-use/minimal-example.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // Small example how to use the library. 3 | // For more examples, look at demo-main.cc 4 | // 5 | // This code is public domain 6 | // (but note, that the led-matrix library this depends on is GPL v2) 7 | 8 | #include "led-matrix.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | using rgb_matrix::RGBMatrix; 16 | using rgb_matrix::Canvas; 17 | 18 | volatile bool interrupt_received = false; 19 | static void InterruptHandler(int signo) { 20 | interrupt_received = true; 21 | } 22 | 23 | static void DrawOnCanvas(Canvas *canvas) { 24 | /* 25 | * Let's create a simple animation. We use the canvas to draw 26 | * pixels. We wait between each step to have a slower animation. 27 | */ 28 | canvas->Fill(0, 0, 255); 29 | 30 | int center_x = canvas->width() / 2; 31 | int center_y = canvas->height() / 2; 32 | float radius_max = canvas->width() / 2; 33 | float angle_step = 1.0 / 360; 34 | for (float a = 0, r = 0; r < radius_max; a += angle_step, r += angle_step) { 35 | if (interrupt_received) 36 | return; 37 | float dot_x = cos(a * 2 * M_PI) * r; 38 | float dot_y = sin(a * 2 * M_PI) * r; 39 | canvas->SetPixel(center_x + dot_x, center_y + dot_y, 40 | 255, 0, 0); 41 | usleep(1 * 1000); // wait a little to slow down things. 42 | } 43 | } 44 | 45 | int main(int argc, char *argv[]) { 46 | RGBMatrix::Options defaults; 47 | defaults.hardware_mapping = "regular"; // or e.g. "adafruit-hat" 48 | defaults.rows = 32; 49 | defaults.chain_length = 1; 50 | defaults.parallel = 1; 51 | defaults.show_refresh_rate = true; 52 | Canvas *canvas = RGBMatrix::CreateFromFlags(&argc, &argv, &defaults); 53 | if (canvas == NULL) 54 | return 1; 55 | 56 | // It is always good to set up a signal handler to cleanly exit when we 57 | // receive a CTRL-C for instance. The DrawOnCanvas() routine is looking 58 | // for that. 59 | signal(SIGTERM, InterruptHandler); 60 | signal(SIGINT, InterruptHandler); 61 | 62 | DrawOnCanvas(canvas); // Using the canvas. 63 | 64 | // Animation finished. Shut down the RGB matrix. 65 | canvas->Clear(); 66 | delete canvas; 67 | 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /bindings/c#/RGBLedFont.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace rpi_rgb_led_matrix_sharp 9 | { 10 | public class RGBLedFont : IDisposable 11 | { 12 | [DllImport("librgbmatrix.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] 13 | internal static extern IntPtr load_font(string bdf_font_file); 14 | 15 | [DllImport("librgbmatrix.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] 16 | internal static extern int draw_text(IntPtr canvas, IntPtr font, int x, int y, byte r, byte g, byte b, string utf8_text, int extra_spacing); 17 | 18 | [DllImport("librgbmatrix.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] 19 | internal static extern int vertical_draw_text(IntPtr canvas, IntPtr font, int x, int y, byte r, byte g, byte b, string utf8_text, int kerning_offset); 20 | 21 | [DllImport("librgbmatrix.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] 22 | internal static extern void delete_font(IntPtr font); 23 | 24 | public RGBLedFont(string bdf_font_file_path) 25 | { 26 | _font = load_font(bdf_font_file_path); 27 | } 28 | internal IntPtr _font; 29 | 30 | internal int DrawText(IntPtr canvas, int x, int y, Color color, string text, int spacing=0, bool vertical=false) 31 | { 32 | if (!vertical) 33 | return draw_text(canvas, _font, x, y, color.R, color.G, color.B, text, spacing); 34 | else 35 | return vertical_draw_text(canvas, _font, x, y, color.R, color.G, color.B, text, spacing); 36 | } 37 | 38 | #region IDisposable Support 39 | private bool disposedValue = false; 40 | 41 | protected virtual void Dispose(bool disposing) 42 | { 43 | if (!disposedValue) 44 | { 45 | delete_font(_font); 46 | disposedValue = true; 47 | } 48 | } 49 | ~RGBLedFont() 50 | { 51 | Dispose(false); 52 | } 53 | public void Dispose() 54 | { 55 | Dispose(true); 56 | GC.SuppressFinalize(this); 57 | } 58 | #endregion 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /fonts/AUTHORS: -------------------------------------------------------------------------------- 1 | The identity of the designer(s) of the original ASCII repertoire and 2 | the later Latin-1 extension of the misc-fixed BDF fonts appears to 3 | have been lost in history. (It is likely that many of these 7-bit 4 | ASCII fonts were created in the early or mid 1980s as part of MIT's 5 | Project Athena, or at its industrial partner, DEC.) 6 | 7 | In 1997, Markus Kuhn at the University of Cambridge Computer 8 | Laboratory initiated and headed a project to extend the misc-fixed BDF 9 | fonts to as large a subset of Unicode/ISO 10646 as is feasible for 10 | each of the available font sizes, as part of a wider effort to 11 | encourage users of POSIX systems to migrate from ISO 8859 to UTF-8. 12 | 13 | Robert Brady and Birger Langkjer 14 | contributed thousands of glyphs and made 15 | very substantial contributions and improvements on almost all fonts. 16 | Constantine Stathopoulos contributed all the 17 | Greek characters. Markus Kuhn did 18 | most 6x13 glyphs and the italic fonts and provided many more glyphs, 19 | coordination, and quality assurance for the other fonts. Mark Leisher 20 | contributed to 6x13 Armenian, Georgian, the 21 | first version of Latin Extended Block A and some Cyrillic. Serge V. 22 | Vakulenko donated the original Cyrillic glyphs 23 | from his 6x13 ISO 8859-5 font. Nozomi Ytow 24 | contributed 6x13 halfwidth Katakana. Henning Brunzel 25 | contributed glyphs to 10x20.bdf. Theppitak 26 | Karoonboonyanan contributed Thai for 7x13, 27 | 7x13B, 7x13O, 7x14, 7x14B, 8x13, 8x13B, 8x13O, 9x15, 9x15B, and 10x20. 28 | Karl Koehler contributed Arabic to 9x15, 29 | 9x15B, and 10x20 and Roozbeh Pournader and 30 | Behdad Esfahbod revised and extended Arabic in 10x20. Raphael Finkel 31 | revised Hebrew/Yiddish in 10x20. Jungshik Shin 32 | prepared 18x18ko.bdf. Won-kyu Park 33 | prepared the Hangul glyphs used in 12x13ja. 34 | Janne V. Kujala contributed 4x6. Daniel Yacob 35 | revised some Ethiopic glyphs. Ted Zlatanov 36 | did some 7x14. Mikael Öhman 37 | worked on 6x12. 38 | 39 | The fonts are still maintained by Markus Kuhn and the original 40 | distribution can be found at: 41 | 42 | http://www.cl.cam.ac.uk/~mgk25/ucs-fonts.html 43 | -------------------------------------------------------------------------------- /adapter/kicad-scripts/kicad-fab.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Based on gen_gerber_and_drill_files_board.py in kicad/demos directory. 3 | ''' 4 | 5 | import sys 6 | 7 | from pcbnew import * 8 | filename=sys.argv[1] 9 | 10 | board = LoadBoard(filename) 11 | 12 | plotDir = "plot/" 13 | 14 | pctl = PLOT_CONTROLLER(board) 15 | 16 | popt = pctl.GetPlotOptions() 17 | 18 | popt.SetOutputDirectory(plotDir) 19 | 20 | # Set some important plot options: 21 | popt.SetPlotFrameRef(False) 22 | popt.SetLineWidth(FromMM(0.35)) 23 | 24 | popt.SetAutoScale(False) 25 | popt.SetScale(1) 26 | popt.SetMirror(False) 27 | popt.SetUseGerberAttributes(False) 28 | popt.SetUseGerberProtelExtensions(True) 29 | popt.SetExcludeEdgeLayer(True); 30 | popt.SetScale(1) 31 | popt.SetUseAuxOrigin(True) 32 | 33 | # This by gerbers only (also the name is truly horrid!) 34 | popt.SetSubtractMaskFromSilk(False) 35 | 36 | # param 0 is the layer ID 37 | # param 1 is a string added to the file base name to identify the drawing 38 | # param 2 is a comment 39 | # Create filenames in a way that if they are sorted alphabetically, they 40 | # are shown in exactly the layering the board would look like. So 41 | # gerbv * 42 | # just makes sense. 43 | plot_plan = [ 44 | ( Edge_Cuts, "0-Edge_Cuts", "Edges" ), 45 | 46 | ( F_Paste, "1-PasteTop", "Paste top" ), 47 | ( F_SilkS, "2-SilkTop", "Silk top" ), 48 | ( F_Mask, "3-MaskTop", "Mask top" ), 49 | ( F_Cu, "4-CuTop", "Top layer" ), 50 | 51 | ( B_Cu, "5-CuBottom", "Bottom layer" ), 52 | ( B_Mask, "6-MaskBottom", "Mask bottom" ), 53 | ( B_SilkS, "7-SilkBottom", "Silk top" ), 54 | ( B_Paste, "8-PasteBottom", "Paste Bottom" ), 55 | ] 56 | 57 | 58 | for layer_info in plot_plan: 59 | pctl.SetLayer(layer_info[0]) 60 | pctl.OpenPlotfile(layer_info[1], PLOT_FORMAT_GERBER, layer_info[2]) 61 | pctl.PlotLayer() 62 | 63 | # At the end you have to close the last plot, otherwise you don't know when 64 | # the object will be recycled! 65 | pctl.ClosePlot() 66 | 67 | # Fabricators need drill files. 68 | # sometimes a drill map file is asked (for verification purpose) 69 | drlwriter = EXCELLON_WRITER( board ) 70 | drlwriter.SetMapFileFormat( PLOT_FORMAT_PDF ) 71 | 72 | mirror = False 73 | minimalHeader = False 74 | offset = wxPoint(0,0) 75 | mergeNPTH = True 76 | drlwriter.SetOptions( mirror, minimalHeader, offset, mergeNPTH ) 77 | 78 | metricFmt = True 79 | drlwriter.SetFormat( metricFmt ) 80 | 81 | genDrl = True 82 | genMap = True 83 | drlwriter.CreateDrillandMapFilesSet( plotDir, genDrl, genMap ); 84 | -------------------------------------------------------------------------------- /examples-api-use/ledcat.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // A program that reads frames form STDIN as RGB24, much like 3 | // https://github.com/polyfloyd/ledcat does. 4 | // 5 | // This code is public domain 6 | // (but note, that the led-matrix library this depends on is GPL v2) 7 | 8 | #include "led-matrix.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #define FPS 60 17 | 18 | using rgb_matrix::RGBMatrix; 19 | using rgb_matrix::Canvas; 20 | 21 | volatile bool interrupt_received = false; 22 | static void InterruptHandler(int signo) { 23 | interrupt_received = true; 24 | } 25 | 26 | int main(int argc, char *argv[]) { 27 | RGBMatrix::Options defaults; 28 | defaults.hardware_mapping = "regular"; // or e.g. "adafruit-hat" 29 | defaults.rows = 32; 30 | defaults.chain_length = 1; 31 | defaults.parallel = 1; 32 | Canvas *canvas = RGBMatrix::CreateFromFlags(&argc, &argv, &defaults); 33 | if (canvas == NULL) { 34 | return 1; 35 | } 36 | 37 | // It is always good to set up a signal handler to cleanly exit when we 38 | // receive a CTRL-C for instance. The DrawOnCanvas() routine is looking 39 | // for that. 40 | signal(SIGTERM, InterruptHandler); 41 | signal(SIGINT, InterruptHandler); 42 | 43 | ssize_t frame_size = canvas->width() * canvas->height() * 3; 44 | uint8_t buf[frame_size]; 45 | 46 | while (1) { 47 | struct timespec start; 48 | timespec_get(&start, TIME_UTC); 49 | 50 | ssize_t nread; 51 | ssize_t total_nread = 0; 52 | while ((nread = read(STDIN_FILENO, &buf[total_nread], frame_size - total_nread)) > 0) { 53 | if (interrupt_received) { 54 | return 1; 55 | } 56 | total_nread += nread; 57 | } 58 | if (total_nread < frame_size){ 59 | break; 60 | } 61 | 62 | for (int y = 0; y < canvas->height(); y++) { 63 | for (int x = 0; x < canvas->width(); x++) { 64 | uint8_t *p = &buf[(y * canvas->width() + x) * 3]; 65 | uint8_t r = *(p+0), g = *(p+1), b = *(p+2); 66 | canvas->SetPixel(x, y, r, g, b); 67 | } 68 | } 69 | 70 | struct timespec end; 71 | timespec_get(&end, TIME_UTC); 72 | long tudiff = (end.tv_nsec / 1000 + end.tv_sec * 1000000) - (start.tv_nsec / 1000 + start.tv_sec * 1000000); 73 | if (tudiff < 1000000l / FPS) { 74 | usleep(1000000l / FPS - tudiff); 75 | } 76 | } 77 | 78 | // Animation finished. Shut down the RGB matrix. 79 | canvas->Clear(); 80 | delete canvas; 81 | return 0; 82 | } 83 | -------------------------------------------------------------------------------- /bindings/python/samples/rotating-block-generator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from samplebase import SampleBase 3 | import math 4 | 5 | 6 | def scale_col(val, lo, hi): 7 | if val < lo: 8 | return 0 9 | if val > hi: 10 | return 255 11 | return 255 * (val - lo) / (hi - lo) 12 | 13 | 14 | def rotate(x, y, sin, cos): 15 | return x * cos - y * sin, x * sin + y * cos 16 | 17 | 18 | class RotatingBlockGenerator(SampleBase): 19 | def __init__(self, *args, **kwargs): 20 | super(RotatingBlockGenerator, self).__init__(*args, **kwargs) 21 | 22 | def run(self): 23 | cent_x = self.matrix.width / 2 24 | cent_y = self.matrix.height / 2 25 | 26 | rotate_square = min(self.matrix.width, self.matrix.height) * 1.41 27 | min_rotate = cent_x - rotate_square / 2 28 | max_rotate = cent_x + rotate_square / 2 29 | 30 | display_square = min(self.matrix.width, self.matrix.height) * 0.7 31 | min_display = cent_x - display_square / 2 32 | max_display = cent_x + display_square / 2 33 | 34 | deg_to_rad = 2 * 3.14159265 / 360 35 | rotation = 0 36 | 37 | # Pre calculate colors 38 | col_table = [] 39 | for x in range(int(min_rotate), int(max_rotate)): 40 | col_table.insert(x, scale_col(x, min_display, max_display)) 41 | 42 | offset_canvas = self.matrix.CreateFrameCanvas() 43 | 44 | while True: 45 | rotation += 1 46 | rotation %= 360 47 | 48 | # calculate sin and cos once for each frame 49 | angle = rotation * deg_to_rad 50 | sin = math.sin(angle) 51 | cos = math.cos(angle) 52 | 53 | for x in range(int(min_rotate), int(max_rotate)): 54 | for y in range(int(min_rotate), int(max_rotate)): 55 | # Our rotate center is always offset by cent_x 56 | rot_x, rot_y = rotate(x - cent_x, y - cent_x, sin, cos) 57 | 58 | if x >= min_display and x < max_display and y >= min_display and y < max_display: 59 | x_col = col_table[x] 60 | y_col = col_table[y] 61 | offset_canvas.SetPixel(rot_x + cent_x, rot_y + cent_y, x_col, 255 - y_col, y_col) 62 | else: 63 | offset_canvas.SetPixel(rot_x + cent_x, rot_y + cent_y, 0, 0, 0) 64 | 65 | offset_canvas = self.matrix.SwapOnVSync(offset_canvas) 66 | 67 | 68 | # Main function 69 | if __name__ == "__main__": 70 | rotating_block_generator = RotatingBlockGenerator() 71 | if (not rotating_block_generator.process()): 72 | rotating_block_generator.print_help() 73 | -------------------------------------------------------------------------------- /fonts/README.md: -------------------------------------------------------------------------------- 1 | ## Provided fonts 2 | These are BDF fonts, a simple bitmap font-format that can be created 3 | by many font tools. Given that these are bitmap fonts, they will look good on 4 | very low resolution screens such as the LED displays. 5 | 6 | Fonts in this directory (except tom-thumb.bdf) are public domain (see the [README](./README)) and 7 | help you to get started with the font support in the API or the `text-util` 8 | from the utils/ directory. 9 | 10 | Tom-Thumb.bdf is included in this directory under [MIT license](http://vt100.tarunz.org/LICENSE). Tom-thumb.bdf was created by [@robey](http://twitter.com/robey) and originally published at https://robey.lag.net/2010/01/23/tiny-monospace-font.html 11 | 12 | The texguire-27.bdf font was created using the [otf2bdf] tool from the TeX Gyre font. 13 | ``` 14 | otf2bdf -v -o texgyre-27.bdf -r 72 -p 27 texgyreadventor-regular.otf 15 | ``` 16 | 17 | ## Create your own 18 | 19 | Fonts are in a human readable and editbable `*.bdf` format, but unless you 20 | like reading and writing pixels in hex, generating them is probably easier :) 21 | 22 | You can use any font-editor to generate a BDF font or use the conversion 23 | tool [otf2bdf] to create one from some other font format. 24 | 25 | Here is an example how you could create a 30pixel high BDF font from some 26 | TrueType font: 27 | 28 | ```bash 29 | otf2bdf -v -o myfont.bdf -r 72 -p 30 /path/to/font-Bold.ttf 30 | ``` 31 | 32 | ## Getting otf2bdf 33 | 34 | Installing the tool should be fairly straight-foward 35 | 36 | ``` 37 | sudo apt-get install otf2bdf 38 | ``` 39 | 40 | ## Compiling otf2bdf 41 | 42 | If you like to compile otf2bdf, you might notice that the configure script 43 | uses some old way of getting the freetype configuration. There does not seem 44 | to be much activity on the mature code, so let's patch that first: 45 | 46 | ``` 47 | sudo apt-get install -y libfreetype6-dev pkg-config autoconf 48 | git clone https://github.com/jirutka/otf2bdf.git # check it out 49 | cd otf2bdf 50 | patch -p1 <<"EOF" 51 | --- a/configure.in 52 | +++ b/configure.in 53 | @@ -5,8 +5,8 @@ AC_INIT(otf2bdf.c) 54 | AC_PROG_CC 55 | 56 | OLDLIBS=$LIBS 57 | -LIBS="$LIBS `freetype-config --libs`" 58 | -CPPFLAGS="$CPPFLAGS `freetype-config --cflags`" 59 | +LIBS="$LIBS `pkg-config freetype2 --libs`" 60 | +CPPFLAGS="$CPPFLAGS `pkg-config freetype2 --cflags`" 61 | AC_CHECK_LIB(freetype, FT_Init_FreeType, LIBS="$LIBS -lfreetype",[ 62 | AC_MSG_ERROR([Can't find Freetype library! Compile FreeType first.])]) 63 | AC_SUBST(LIBS) 64 | EOF 65 | 66 | autoconf # rebuild configure script 67 | ./configure # run configure 68 | make # build the software 69 | sudo make install # install it 70 | ``` 71 | 72 | [otf2bdf]: https://github.com/jirutka/otf2bdf 73 | -------------------------------------------------------------------------------- /bindings/python/Makefile: -------------------------------------------------------------------------------- 1 | # TODO: This contains a lot of {c|p}ython build boilerplate, this needs cleanup. 2 | PYTHON ?= python 3 | SETUP := setup.py 4 | BUILD_ARGS := build --build-lib . 5 | INST_ARGS := install 6 | ifdef DESTDIR 7 | INST_ARGS += --root=$(DESTDIR) 8 | endif 9 | CLEAN_ARGS := clean --all 10 | 11 | MANPAGES := $(patsubst %.txt,%,$(wildcard *.txt)) 12 | TXTTOMAN := a2x -f manpage 13 | 14 | # Where our library resides. It is split between includes and the binary 15 | # library in lib 16 | RGB_LIBDIR=../../lib 17 | RGB_LIBRARY_NAME=rgbmatrix 18 | RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a 19 | 20 | ifneq "$(wildcard debian/changelog)" "" 21 | PKGNAME := $(shell dpkg-parsechangelog | sed -n 's/^Source: //p') 22 | VERSION := $(shell dpkg-parsechangelog | sed -n 's/^Version: \([^-]*\).*/\1/p') 23 | UPSDIST := $(PKGNAME)-$(VERSION).tar.gz 24 | DEBDIST := $(PKGNAME)_$(VERSION).orig.tar.gz 25 | endif 26 | 27 | all: build 28 | build: build-python 29 | install: install-python 30 | clean: clean-python 31 | find ./rgbmatrix -type f -name \*.so -delete 32 | find . -type f -name \*.pyc -delete 33 | $(RM) build-* install-* test-* 34 | 35 | $(RGB_LIBRARY): FORCE 36 | $(MAKE) -C $(RGB_LIBDIR) 37 | 38 | test: test-python 39 | test-python: 40 | ifneq "$(wildcard tests/*.py)" "" 41 | nosetests -v -w tests 42 | else 43 | $(info Test suite is not implemented...) 44 | endif 45 | 46 | ifneq "$(wildcard debian/control)" "" 47 | PYVERS := $(shell pyversions -r -v debian/control) 48 | PYEXEC := $(shell pyversions -d) 49 | BUILD_ARGS += --executable=/usr/bin/$(PYEXEC) 50 | INST_ARGS += --no-compile -O0 51 | 52 | build-python: $(PYVERS:%=build-python-%) 53 | build-python-%: $(RGB_LIBRARY) 54 | $(info * Doing build for $(PYTHON)$* ...) 55 | $(PYTHON)$* $(SETUP) $(BUILD_ARGS) 56 | 57 | install-python: $(PYVERS:%=install-python-%) 58 | install-python-%: 59 | $(info * Doing install for $(PYTHON)$* ...) 60 | $(PYTHON)$* $(SETUP) $(INST_ARGS) 61 | 62 | clean-python: $(PYVERS:%=clean-python-%) 63 | clean-python-%: 64 | $(PYTHON)$* $(SETUP) $(CLEAN_ARGS) 65 | else 66 | build-python: $(RGB_LIBRARY) 67 | $(PYTHON) $(SETUP) $(BUILD_ARGS) 68 | 69 | install-python: 70 | $(PYTHON) $(SETUP) $(INST_ARGS) 71 | 72 | clean-python: 73 | $(PYTHON) $(SETUP) $(CLEAN_ARGS) 74 | endif 75 | 76 | distclean: clean 77 | dist: distclean 78 | $(info * Creating ../$(UPSDIST) and ../$(DEBDIST)) 79 | @tar --exclude='.svn' \ 80 | --exclude='*.swp' \ 81 | --exclude='debian' \ 82 | -czvf ../$(UPSDIST) \ 83 | -C ../ $(notdir $(CURDIR)) 84 | @cp ../$(UPSDIST) ../$(DEBDIST) 85 | @if test -d ../tarballs; then \ 86 | mv -v ../$(DEBDIST) ../tarballs; \ 87 | fi 88 | 89 | FORCE: 90 | .PHONY: FORCE 91 | .PHONY: build install test clean dist distclean 92 | .PHONY: build-python install-python clean-python 93 | -------------------------------------------------------------------------------- /bindings/python/rgbmatrix/cppinc.pxd: -------------------------------------------------------------------------------- 1 | from libcpp cimport bool 2 | from libc.stdint cimport uint8_t, uint32_t 3 | 4 | ######################## 5 | ### External classes ### 6 | ######################## 7 | 8 | cdef extern from "canvas.h" namespace "rgb_matrix": 9 | cdef cppclass Canvas: 10 | int width() 11 | int height() 12 | void SetPixel(int, int, uint8_t, uint8_t, uint8_t) nogil 13 | void Clear() nogil 14 | void Fill(uint8_t, uint8_t, uint8_t) nogil 15 | 16 | cdef extern from "led-matrix.h" namespace "rgb_matrix": 17 | cdef cppclass RGBMatrix(Canvas): 18 | bool SetPWMBits(uint8_t) 19 | uint8_t pwmbits() 20 | void set_luminance_correct(bool) 21 | bool luminance_correct() 22 | void SetBrightness(uint8_t) 23 | uint8_t brightness() 24 | FrameCanvas *CreateFrameCanvas() 25 | FrameCanvas *SwapOnVSync(FrameCanvas*) 26 | 27 | cdef cppclass FrameCanvas(Canvas): 28 | bool SetPWMBits(uint8_t) 29 | uint8_t pwmbits() 30 | void SetBrightness(uint8_t) 31 | uint8_t brightness() 32 | 33 | struct RuntimeOptions: 34 | RuntimeOptions() except + 35 | int gpio_slowdown 36 | int daemon 37 | int drop_privileges 38 | 39 | 40 | RGBMatrix *CreateMatrixFromOptions(Options &options, RuntimeOptions runtime_options) 41 | 42 | 43 | 44 | cdef extern from "led-matrix.h" namespace "rgb_matrix::RGBMatrix": 45 | cdef struct Options: 46 | Options() except + 47 | 48 | const char *hardware_mapping 49 | 50 | int rows 51 | int cols 52 | int chain_length 53 | int parallel 54 | int pwm_bits 55 | int pwm_lsb_nanoseconds 56 | int brightness 57 | int scan_mode 58 | int row_address_type 59 | int multiplexing 60 | int pwm_dither_bits 61 | int limit_refresh_rate_hz 62 | 63 | bool disable_hardware_pulsing 64 | bool show_refresh_rate 65 | bool inverse_colors 66 | 67 | const char *led_rgb_sequence 68 | const char *pixel_mapper_config 69 | const char *panel_type 70 | 71 | cdef extern from "graphics.h" namespace "rgb_matrix": 72 | cdef struct Color: 73 | Color(uint8_t, uint8_t, uint8_t) except + 74 | uint8_t r 75 | uint8_t g 76 | uint8_t b 77 | 78 | cdef cppclass Font: 79 | Font() except + 80 | bool LoadFont(const char*) 81 | int height() 82 | int baseline() 83 | int CharacterWidth(uint32_t) 84 | int DrawGlyph(Canvas*, int, int, const Color, uint32_t); 85 | 86 | cdef int DrawText(Canvas*, const Font, int, int, const Color, const char*) 87 | cdef void DrawCircle(Canvas*, int, int, int, const Color) 88 | cdef void DrawLine(Canvas*, int, int, int, int, const Color) 89 | -------------------------------------------------------------------------------- /bindings/c#/examples/matrix-rain.cs: -------------------------------------------------------------------------------- 1 | using rpi_rgb_led_matrix_sharp; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Threading; 6 | 7 | namespace matrix_rain 8 | { 9 | class Program 10 | { 11 | const int MAX_HEIGHT = 16; 12 | const int COLOR_STEP = 15; 13 | const int FRAME_STEP = 1; 14 | 15 | static int Main(string[] args) 16 | { 17 | 18 | var matrix = new RGBLedMatrix(new RGBLedMatrixOptions { ChainLength = 2 }); 19 | var canvas = matrix.CreateOffscreenCanvas(); 20 | var rnd = new Random(); 21 | var points = new List(); 22 | var recycled = new Stack(); 23 | int frame = 0; 24 | var stopwatch = new Stopwatch(); 25 | 26 | while (!Console.KeyAvailable) { 27 | stopwatch.Restart(); 28 | 29 | frame++; 30 | 31 | if (frame % FRAME_STEP == 0) 32 | { 33 | if (recycled.Count == 0) 34 | points.Add(new Point(rnd.Next(0, canvas.Width - 1), 0)); 35 | else 36 | { 37 | var point = recycled.Pop(); 38 | point.x = rnd.Next(0, canvas.Width - 1); 39 | point.y = 0; 40 | point.recycled = false; 41 | } 42 | } 43 | 44 | canvas.Clear(); 45 | 46 | foreach (var point in points) 47 | { 48 | if (!point.recycled) 49 | { 50 | point.y++; 51 | 52 | if (point.y - MAX_HEIGHT > canvas.Height) 53 | { 54 | point.recycled = true; 55 | recycled.Push(point); 56 | } 57 | 58 | for (var i=0; i< MAX_HEIGHT; i++) 59 | { 60 | canvas.SetPixel(point.x, point.y - i, new Color(0, 255 - i * COLOR_STEP, 0)); 61 | } 62 | } 63 | } 64 | 65 | canvas = matrix.SwapOnVsync(canvas); 66 | 67 | // force 30 FPS 68 | var elapsed= stopwatch.ElapsedMilliseconds; 69 | if (elapsed < 33) 70 | { 71 | Thread.Sleep(33 - (int)elapsed); 72 | } 73 | } 74 | 75 | return 0; 76 | } 77 | 78 | class Point 79 | { 80 | public Point(int x, int y) 81 | { 82 | this.x = x; 83 | this.y = y; 84 | } 85 | public int x; 86 | public int y; 87 | public bool recycled; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /include/thread.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // Copyright (C) 2013 Henner Zeller 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation version 2. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see 15 | 16 | #ifndef RPI_THREAD_H 17 | #define RPI_THREAD_H 18 | 19 | #include 20 | #include 21 | 22 | namespace rgb_matrix { 23 | // Simple thread abstraction. 24 | class Thread { 25 | public: 26 | Thread(); 27 | 28 | // The destructor waits for Run() to return so make sure it does. 29 | virtual ~Thread(); 30 | 31 | // Wait for the Run() method to return. 32 | void WaitStopped(); 33 | 34 | // Start thread. If realtime_priority is > 0, then this will be a 35 | // thread with SCHED_FIFO and the given priority. 36 | // If cpu_affinity is set !=, chooses the given bitmask of CPUs 37 | // this thread should have an affinity to. 38 | // On a Raspberry Pi 1, this doesn't matter, as there is only one core, 39 | // Raspberry Pi 2 can has 4 cores, so any combination of (1<<0) .. (1<<3) is 40 | // valid. 41 | virtual void Start(int realtime_priority = 0, uint32_t cpu_affinity_mask = 0); 42 | 43 | // Override this to do the work. 44 | // 45 | // This will be called in a thread once Start() has been called. You typically 46 | // will have an endless loop doing stuff. 47 | // 48 | // It is a good idea to provide a way to communicate to the thread that 49 | // it should stop (see ThreadedCanvasManipulator for an example) 50 | virtual void Run() = 0; 51 | 52 | private: 53 | static void *PthreadCallRun(void *tobject); 54 | bool started_; 55 | pthread_t thread_; 56 | }; 57 | 58 | // Non-recursive Mutex. 59 | class Mutex { 60 | public: 61 | Mutex() { pthread_mutex_init(&mutex_, NULL); } 62 | ~Mutex() { pthread_mutex_destroy(&mutex_); } 63 | void Lock() { pthread_mutex_lock(&mutex_); } 64 | void Unlock() { pthread_mutex_unlock(&mutex_); } 65 | 66 | // Wait on condition. If "timeout_ms" is < 0, it waits forever, otherwise 67 | // until timeout is reached. 68 | // Returns 'true' if condition is met, 'false', if wait timed out. 69 | bool WaitOn(pthread_cond_t *cond, long timeout_ms = -1); 70 | 71 | private: 72 | pthread_mutex_t mutex_; 73 | }; 74 | 75 | // Useful RAII wrapper around mutex. 76 | class MutexLock { 77 | public: 78 | MutexLock(Mutex *m) : mutex_(m) { mutex_->Lock(); } 79 | ~MutexLock() { mutex_->Unlock(); } 80 | private: 81 | Mutex *const mutex_; 82 | }; 83 | 84 | } // end namespace rgb_matrix 85 | 86 | #endif // RPI_THREAD_H 87 | -------------------------------------------------------------------------------- /include/content-streamer.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // 3 | // Abstractions to read and write FrameCanvas objects to streams. This allows 4 | // you to create canned streams of content with minimal overhead at runtime 5 | // to play with extreme pixel-throughput which also minimizes overheads in 6 | // the Pi to avoid stuttering or brightness glitches. 7 | // 8 | // The disadvantage is, that this represents the full expanded internal 9 | // representation of a frame, so is very large memory wise. 10 | // 11 | // These abstractions are used in util/led-image-viewer.cc to read and 12 | // write such animations to disk. It is also used in util/video-viewer.cc 13 | // to write a version to disk that then can be played with the led-image-viewer. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | namespace rgb_matrix { 23 | class FrameCanvas; 24 | 25 | // An abstraction of a data stream. 26 | class StreamIO { 27 | public: 28 | virtual ~StreamIO() {} 29 | 30 | // Rewind stream. 31 | virtual void Rewind() = 0; 32 | 33 | // Read bytes into buffer. Similar to Posix behavior that allows short reads. 34 | virtual ssize_t Read(void *buf, size_t count) = 0; 35 | 36 | // Write bytes from buffer. Similar to Posix behavior that allows short 37 | // writes. 38 | virtual ssize_t Append(const void *buf, size_t count) = 0; 39 | }; 40 | 41 | class FileStreamIO : public StreamIO { 42 | public: 43 | explicit FileStreamIO(int fd); 44 | ~FileStreamIO(); 45 | 46 | virtual void Rewind(); 47 | virtual ssize_t Read(void *buf, size_t count); 48 | virtual ssize_t Append(const void *buf, size_t count); 49 | 50 | private: 51 | const int fd_; 52 | }; 53 | 54 | class MemStreamIO : public StreamIO { 55 | public: 56 | virtual void Rewind(); 57 | virtual ssize_t Read(void *buf, size_t count); 58 | virtual ssize_t Append(const void *buf, size_t count); 59 | 60 | private: 61 | std::string buffer_; // super simplistic. 62 | size_t pos_; 63 | }; 64 | 65 | class StreamWriter { 66 | public: 67 | // Does not take ownership of StreamIO 68 | StreamWriter(StreamIO *io); 69 | 70 | // Stream out given canvas at the given time. "hold_time_us" indicates 71 | // for how long this frame is to be shown in microseconds. 72 | bool Stream(const FrameCanvas &frame, uint32_t hold_time_us); 73 | 74 | private: 75 | void WriteFileHeader(const FrameCanvas &frame, size_t len); 76 | 77 | StreamIO *const io_; 78 | bool header_written_; 79 | }; 80 | 81 | class StreamReader { 82 | public: 83 | // Does not take ownership of StreamIO 84 | StreamReader(StreamIO *io); 85 | ~StreamReader(); 86 | 87 | // Go back to the beginning. 88 | void Rewind(); 89 | 90 | // Get next frame and its timestamp. Returns 'false' if there is an error 91 | // or end of stream reached.. 92 | bool GetNext(FrameCanvas *frame, uint32_t* hold_time_us); 93 | 94 | private: 95 | enum State { 96 | STREAM_AT_BEGIN, 97 | STREAM_READING, 98 | STREAM_ERROR, 99 | }; 100 | bool ReadFileHeader(const FrameCanvas &frame); 101 | 102 | StreamIO *io_; 103 | size_t frame_buf_size_; 104 | State state_; 105 | 106 | char *header_frame_buffer_; 107 | }; 108 | } 109 | -------------------------------------------------------------------------------- /bindings/c#/RGBLedCanvas.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace rpi_rgb_led_matrix_sharp 9 | { 10 | public class RGBLedCanvas 11 | { 12 | #region DLLImports 13 | [DllImport("librgbmatrix.so")] 14 | internal static extern void led_canvas_get_size(IntPtr canvas, out int width, out int height); 15 | 16 | [DllImport("librgbmatrix.so")] 17 | internal static extern void led_canvas_set_pixel(IntPtr canvas, int x, int y, byte r, byte g, byte b); 18 | 19 | [DllImport("librgbmatrix.so")] 20 | internal static extern void led_canvas_clear(IntPtr canvas); 21 | 22 | [DllImport("librgbmatrix.so")] 23 | internal static extern void led_canvas_fill(IntPtr canvas, byte r, byte g, byte b); 24 | 25 | [DllImport("librgbmatrix.so")] 26 | internal static extern void draw_circle(IntPtr canvas, int xx, int y, int radius, byte r, byte g, byte b); 27 | 28 | [DllImport("librgbmatrix.so")] 29 | internal static extern void draw_line(IntPtr canvas, int x0, int y0, int x1, int y1, byte r, byte g, byte b); 30 | #endregion 31 | 32 | // This is a wrapper for canvas no need to implement IDisposable here 33 | // because RGBLedMatrix has ownership and takes care of disposing canvases 34 | internal IntPtr _canvas; 35 | 36 | // this is not called directly by the consumer code, 37 | // consumer uses factory methods in RGBLedMatrix 38 | internal RGBLedCanvas(IntPtr canvas) 39 | { 40 | _canvas = canvas; 41 | int width; 42 | int height; 43 | led_canvas_get_size(_canvas, out width, out height); 44 | Width = width; 45 | Height = height; 46 | } 47 | 48 | public int Width {get; private set; } 49 | public int Height { get; private set; } 50 | 51 | public void SetPixel(int x, int y, Color color) 52 | { 53 | led_canvas_set_pixel(_canvas, x, y, color.R, color.G, color.B); 54 | } 55 | 56 | public void Fill(Color color) 57 | { 58 | led_canvas_fill(_canvas, color.R, color.G, color.B); 59 | } 60 | 61 | public void Clear() 62 | { 63 | led_canvas_clear(_canvas); 64 | } 65 | 66 | public void DrawCircle(int x0, int y0, int radius, Color color) 67 | { 68 | draw_circle(_canvas, x0, y0, radius, color.R, color.G, color.B); 69 | } 70 | 71 | public void DrawLine (int x0, int y0, int x1, int y1, Color color) 72 | { 73 | draw_line(_canvas, x0, y0, x1, y1, color.R, color.G, color.B); 74 | } 75 | 76 | public int DrawText(RGBLedFont font, int x, int y, Color color, string text, int spacing=0, bool vertical=false) 77 | { 78 | return font.DrawText(_canvas, x, y, color, text, spacing, vertical); 79 | } 80 | } 81 | 82 | public struct Color 83 | { 84 | public Color (int r, int g, int b) 85 | { 86 | R = (byte)r; 87 | G = (byte)g; 88 | B = (byte)b; 89 | } 90 | public Color(byte r, byte g, byte b) 91 | { 92 | R = r; 93 | G = g; 94 | B = b; 95 | } 96 | public byte R; 97 | public byte G; 98 | public byte B; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /lib/thread.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // Copyright (C) 2013 Henner Zeller 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation version 2. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see 15 | 16 | #include "thread.h" 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | namespace rgb_matrix { 26 | void *Thread::PthreadCallRun(void *tobject) { 27 | reinterpret_cast(tobject)->Run(); 28 | return NULL; 29 | } 30 | 31 | Thread::Thread() : started_(false) {} 32 | Thread::~Thread() { 33 | WaitStopped(); 34 | } 35 | 36 | void Thread::WaitStopped() { 37 | if (!started_) return; 38 | int result = pthread_join(thread_, NULL); 39 | if (result != 0) { 40 | perror("Issue joining thread"); 41 | } 42 | started_ = false; 43 | } 44 | 45 | void Thread::Start(int priority, uint32_t affinity_mask) { 46 | assert(!started_); // Did you call WaitStopped() ? 47 | pthread_create(&thread_, NULL, &PthreadCallRun, this); 48 | int err; 49 | 50 | if (priority > 0) { 51 | struct sched_param p; 52 | p.sched_priority = priority; 53 | if ((err = pthread_setschedparam(thread_, SCHED_FIFO, &p))) { 54 | char buffer[PATH_MAX]; 55 | const char *bin = realpath("/proc/self/exe", buffer); // Linux specific. 56 | fprintf(stderr, "Can't set realtime thread priority=%d: %s.\n" 57 | "\tYou are probably not running as root ?\n" 58 | "\tThis will seriously mess with color stability and flicker\n" 59 | "\tof the matrix. Please run as `root` (e.g. by invoking this\n" 60 | "\tprogram with `sudo`), or setting the capability on this\n" 61 | "\tbinary by calling\n" 62 | "\tsudo setcap 'cap_sys_nice=eip' %s\n", 63 | p.sched_priority, strerror(err), bin ? bin : ""); 64 | } 65 | } 66 | 67 | if (affinity_mask != 0) { 68 | cpu_set_t cpu_mask; 69 | CPU_ZERO(&cpu_mask); 70 | for (int i = 0; i < 32; ++i) { 71 | if ((affinity_mask & (1< 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation version 2. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see 15 | 16 | // Utility base class for continuously updating the canvas. 17 | 18 | // Note: considering removing this, as real applications likely have something 19 | // similar, but this might not be quite usable. 20 | // Since it is just a few lines of code, it is probably better 21 | // implemented in the application for readability. 22 | // 23 | // So for simplicity of the API, consider ThreadedCanvasManipulator deprecated. 24 | 25 | #ifndef RPI_THREADED_CANVAS_MANIPULATOR_H 26 | #define RPI_THREADED_CANVAS_MANIPULATOR_H 27 | 28 | #include "thread.h" 29 | #include "canvas.h" 30 | 31 | namespace rgb_matrix { 32 | // 33 | // Typically, your programs will crate a canvas and then updating the image 34 | // in a loop. If you want to do stuff in parallel, then this utility class 35 | // helps you doing that. Also a demo for how to use the Thread class. 36 | // 37 | // Extend it, then just implement Run(). Example: 38 | /* 39 | class MyCrazyDemo : public ThreadedCanvasManipulator { 40 | public: 41 | MyCrazyDemo(Canvas *canvas) : ThreadedCanvasManipulator(canvas) {} 42 | virtual void Run() { 43 | unsigned char c; 44 | while (running()) { 45 | // Calculate the next frame. 46 | c++; 47 | for (int x = 0; x < canvas()->width(); ++x) { 48 | for (int y = 0; y < canvas()->height(); ++y) { 49 | canvas()->SetPixel(x, y, c, c, c); 50 | } 51 | } 52 | usleep(15 * 1000); 53 | } 54 | } 55 | }; 56 | 57 | // Later, in your main method. 58 | RGBMatrix *matrix = RGBMatrix::CreateFromOptions(...); 59 | MyCrazyDemo *demo = new MyCrazyDemo(matrix); 60 | demo->Start(); // Start doing things. 61 | // This now runs in the background, you can do other things here, 62 | // e.g. aquiring new data or simply wait. But for waiting, you wouldn't 63 | // need a thread in the first place. 64 | demo->Stop(); 65 | delete demo; 66 | */ 67 | class ThreadedCanvasManipulator : public Thread { 68 | public: 69 | ThreadedCanvasManipulator(Canvas *m) : running_(false), canvas_(m) {} 70 | virtual ~ThreadedCanvasManipulator() { Stop(); } 71 | 72 | virtual void Start(int realtime_priority=0, uint32_t affinity_mask=0) { 73 | { 74 | MutexLock l(&mutex_); 75 | running_ = true; 76 | } 77 | Thread::Start(realtime_priority, affinity_mask); 78 | } 79 | 80 | // Stop the thread at the next possible time Run() checks the running_ flag. 81 | void Stop() { 82 | MutexLock l(&mutex_); 83 | running_ = false; 84 | } 85 | 86 | // Implement this and run while running() returns true. 87 | virtual void Run() = 0; 88 | 89 | protected: 90 | inline Canvas *canvas() { return canvas_; } 91 | inline bool running() { 92 | MutexLock l(&mutex_); 93 | return running_; 94 | } 95 | 96 | private: 97 | Mutex mutex_; 98 | bool running_; 99 | Canvas *const canvas_; 100 | }; 101 | } // namespace rgb_matrix 102 | 103 | #endif // RPI_THREADED_CANVAS_MANIPULATOR_H 104 | -------------------------------------------------------------------------------- /adapter/active-3/README.md: -------------------------------------------------------------------------------- 1 | Adapter PCB to support up to 3 panel chains 2 | =========================================== 3 | 4 | * Supports up to three panel chains for newer plus models and 5 | Raspberry Pi 2 that have 40 GPIO pins. 6 | * Uses HCT245 to level shift signals from 3.3V to 5V and shield 7 | the Raspberry Pi GPIOs from overloading. 8 | * Open source KiCAD PCB EDA format. 9 | * Optional: Pads to power the Pi with 5V, including optional capacitor footprints. 10 | * Connector for RxD input (literally the only GPIO pin left) in case you want to 11 | make your panel controlled with a serial interface (3.3V logic level). 12 | * Provides a way to choose the pinout for different kinds of 64x64 matrixes. 13 | * (not very pretty layout, was just lazy and let the auto-router generate the first pass) 14 | * BOM: 15 | - 4x 74HCT245 or 74AHCT245 in 20-SOIC, 7.5mm package which should make 16 | it easy to hand-solder. Make sure to get the variant with the **T**: HC**T** or AHC**T** 17 | (there are also HC or AHC, don't use these). 18 | - 4x 100nF ceramic capacitor (0805 package) 19 | - 1x 10kOhm resistor (0805 package). Not critcial, just a pullup (2.2k .. 15k probably ok). 20 | - 3x 16pin IDC (=2x8) male receptible to connect the panels. 21 | - 1x 40pin female connector to connect to the RPi. 22 | - 1x (optional) 22μF .. 100μF capacitor for 5V rail (either 1206 SMD or 23 | radial electrolytic with 2.5mm pitch/6.3mm diameter) 24 | * The Gerber FAB files are provided as [active3-rpi-hub75-adapter-fab.zip](./active3-rpi-hub75-adapter-fab.zip) 25 | 26 | The board is also [shared on OSH Park][osh-active3] (not affiliated). 27 | 28 | ![Preview][rendering] 29 | ![Real World][real-world] 30 | 31 | Essentially, this is connecting the output pins through level shifting buffers (they 32 | are operated at 5V, but the HCT series chips accept 3.3V input levels from the Pi). The 33 | strobe, OE and clock signals are separately buffered for each connector. 34 | 35 | ![Schematic][schematic] 36 | 37 | ## Optional 38 | 39 | ### Power in 40 | 41 | The area on the left has 5V/GND input pads, that allow you to power your Raspberry Pi from 42 | a 5V source ... which you are likely to have as you are powering the LED Matrix. This is often 43 | more convenient than using the USB connector to power the Pi. 44 | 45 | If you do that, there are pads to add a capacitor to smooth the supply - two footprints are 46 | provided: C5 and C6 for through-hole or surface mount components. The value is not critical; 47 | I usually use a 22μF/6.3V ceramic capacitor on the C6 pads. 48 | 49 | ### Choose E-Line for 64x64 panels with 1:32 multiplexing 50 | 51 | If you have a 64x64 matrix with 1:32 multiplexing, you need to supply an `E`-address line to it. 52 | While the Address lines `A` to `D` have fixed positions on the Hub75 connector, there 53 | seem to be two different ways to connect the `E` address line: it is either on pin 4 or pin 8 54 | of the connector. So this adapter board provides the flexibility to choose the right pin for 55 | your matrix. 56 | 57 | Look at the back of the matrix or the documentation to find out for your specific board where 58 | to connect E. 59 | 60 | Once you know that, the jumper area in the bottom left of this adapter board allows to choose 61 | to which pin to connect the E-address line to. The corresponding other pin should be connected 62 | to GND. Simply solder a wire bridge as indicated below (or use a jumper that you can change later). 63 | 64 | If you are not using such 64x64 matrix, you can connect both these pins to GND. 65 | 66 | Here are the typical configurations: 67 | 68 | No 1:32 64x64: to GND | E-Line on Pin 4 | E-Line on Pin 8| 69 | -------------------------|-------------------|---------------- 70 | ![][config-default] |![][config-pin4] |![][config-pin8] 71 | 72 | ### Input for serial RxD 73 | 74 | If you are not using a 64x64 display that occupies the E-Line, you can use the RxD serial input - 75 | this might come in handy if you are using the display to be controlled by a serial line. Be aware 76 | that the input requires 3.3V level, so if you have a RS232, make sure to first adapt the levels. 77 | 78 | [rendering]: ../../img/active3-pcb.png 79 | [config-default]: ../../img/active3-pcb-config-default.png 80 | [config-pin4]: ../../img/active3-pcb-config-pin4.png 81 | [config-pin8]: ../../img/active3-pcb-config-pin8.png 82 | [schematic]: ../../img/active3-schematic.png 83 | [real-world]: ../../img/three-parallel-panels-soic.jpg 84 | [osh-active3]: https://oshpark.com/shared_projects/6xAD1VXr 85 | -------------------------------------------------------------------------------- /adapter/passive-rpi1/passive-rpi-hub75-adapter-cache.lib: -------------------------------------------------------------------------------- 1 | EESchema-LIBRARY Version 2.3 2 | #encoding utf-8 3 | # 4 | # CONN_02X08 5 | # 6 | DEF CONN_02X08 P 0 1 Y N 1 F N 7 | F0 "P" 0 450 50 H V C CNN 8 | F1 "CONN_02X08" 0 0 50 V V C CNN 9 | F2 "" 0 -1200 60 H V C CNN 10 | F3 "" 0 -1200 60 H V C CNN 11 | $FPLIST 12 | Pin_Header_Straight_2X08 13 | Pin_Header_Angled_2X08 14 | Socket_Strip_Straight_2X08 15 | Socket_Strip_Angled_2X08 16 | $ENDFPLIST 17 | DRAW 18 | S -100 -345 -50 -355 0 1 0 N 19 | S -100 -245 -50 -255 0 1 0 N 20 | S -100 -145 -50 -155 0 1 0 N 21 | S -100 -45 -50 -55 0 1 0 N 22 | S -100 55 -50 45 0 1 0 N 23 | S -100 155 -50 145 0 1 0 N 24 | S -100 255 -50 245 0 1 0 N 25 | S -100 355 -50 345 0 1 0 N 26 | S -100 400 100 -400 0 1 0 N 27 | S 50 -345 100 -355 0 1 0 N 28 | S 50 -245 100 -255 0 1 0 N 29 | S 50 -145 100 -155 0 1 0 N 30 | S 50 -45 100 -55 0 1 0 N 31 | S 50 55 100 45 0 1 0 N 32 | S 50 155 100 145 0 1 0 N 33 | S 50 255 100 245 0 1 0 N 34 | S 50 355 100 345 0 1 0 N 35 | X P1 1 -250 350 150 R 50 50 1 1 P 36 | X P2 2 250 350 150 L 50 50 1 1 P 37 | X P3 3 -250 250 150 R 50 50 1 1 P 38 | X P4 4 250 250 150 L 50 50 1 1 P 39 | X P5 5 -250 150 150 R 50 50 1 1 P 40 | X P6 6 250 150 150 L 50 50 1 1 P 41 | X P7 7 -250 50 150 R 50 50 1 1 P 42 | X P8 8 250 50 150 L 50 50 1 1 P 43 | X P9 9 -250 -50 150 R 50 50 1 1 P 44 | X P10 10 250 -50 150 L 50 50 1 1 P 45 | X P11 11 -250 -150 150 R 50 50 1 1 P 46 | X P12 12 250 -150 150 L 50 50 1 1 P 47 | X P13 13 -250 -250 150 R 50 50 1 1 P 48 | X P14 14 250 -250 150 L 50 50 1 1 P 49 | X P15 15 -250 -350 150 R 50 50 1 1 P 50 | X P16 16 250 -350 150 L 50 50 1 1 P 51 | ENDDRAW 52 | ENDDEF 53 | # 54 | # CONN_02X13 55 | # 56 | DEF CONN_02X13 P 0 1 Y N 1 F N 57 | F0 "P" 0 700 50 H V C CNN 58 | F1 "CONN_02X13" 0 0 50 V V C CNN 59 | F2 "" 0 -1150 60 H V C CNN 60 | F3 "" 0 -1150 60 H V C CNN 61 | $FPLIST 62 | Pin_Header_Straight_2X13 63 | Pin_Header_Angled_2X13 64 | Socket_Strip_Straight_2X13 65 | Socket_Strip_Angled_2X13 66 | $ENDFPLIST 67 | DRAW 68 | S -100 -595 -50 -605 0 1 0 N 69 | S -100 -495 -50 -505 0 1 0 N 70 | S -100 -395 -50 -405 0 1 0 N 71 | S -100 -295 -50 -305 0 1 0 N 72 | S -100 -195 -50 -205 0 1 0 N 73 | S -100 -95 -50 -105 0 1 0 N 74 | S -100 5 -50 -5 0 1 0 N 75 | S -100 105 -50 95 0 1 0 N 76 | S -100 205 -50 195 0 1 0 N 77 | S -100 305 -50 295 0 1 0 N 78 | S -100 405 -50 395 0 1 0 N 79 | S -100 505 -50 495 0 1 0 N 80 | S -100 605 -50 595 0 1 0 N 81 | S -100 650 100 -650 0 1 0 N 82 | S 50 -595 100 -605 0 1 0 N 83 | S 50 -495 100 -505 0 1 0 N 84 | S 50 -395 100 -405 0 1 0 N 85 | S 50 -295 100 -305 0 1 0 N 86 | S 50 -195 100 -205 0 1 0 N 87 | S 50 -95 100 -105 0 1 0 N 88 | S 50 5 100 -5 0 1 0 N 89 | S 50 105 100 95 0 1 0 N 90 | S 50 205 100 195 0 1 0 N 91 | S 50 305 100 295 0 1 0 N 92 | S 50 405 100 395 0 1 0 N 93 | S 50 505 100 495 0 1 0 N 94 | S 50 605 100 595 0 1 0 N 95 | X P1 1 -250 600 150 R 50 50 1 1 P 96 | X P2 2 250 600 150 L 50 50 1 1 P 97 | X P3 3 -250 500 150 R 50 50 1 1 P 98 | X P4 4 250 500 150 L 50 50 1 1 P 99 | X P5 5 -250 400 150 R 50 50 1 1 P 100 | X P6 6 250 400 150 L 50 50 1 1 P 101 | X P7 7 -250 300 150 R 50 50 1 1 P 102 | X P8 8 250 300 150 L 50 50 1 1 P 103 | X P9 9 -250 200 150 R 50 50 1 1 P 104 | X P10 10 250 200 150 L 50 50 1 1 P 105 | X P20 20 250 -300 150 L 50 50 1 1 P 106 | X P11 11 -250 100 150 R 50 50 1 1 P 107 | X P21 21 -250 -400 150 R 50 50 1 1 P 108 | X P12 12 250 100 150 L 50 50 1 1 P 109 | X P22 22 250 -400 150 L 50 50 1 1 P 110 | X P13 13 -250 0 150 R 50 50 1 1 P 111 | X P23 23 -250 -500 150 R 50 50 1 1 P 112 | X P14 14 250 0 150 L 50 50 1 1 P 113 | X P24 24 250 -500 150 L 50 50 1 1 P 114 | X P15 15 -250 -100 150 R 50 50 1 1 P 115 | X P25 25 -250 -600 150 R 50 50 1 1 P 116 | X P16 16 250 -100 150 L 50 50 1 1 P 117 | X P26 26 250 -600 150 L 50 50 1 1 P 118 | X P17 17 -250 -200 150 R 50 50 1 1 P 119 | X P18 18 250 -200 150 L 50 50 1 1 P 120 | X P19 19 -250 -300 150 R 50 50 1 1 P 121 | ENDDRAW 122 | ENDDEF 123 | # 124 | # GND 125 | # 126 | DEF GND #PWR 0 0 Y Y 1 F P 127 | F0 "#PWR" 0 -250 50 H I C CNN 128 | F1 "GND" 0 -150 50 H V C CNN 129 | F2 "" 0 0 60 H V C CNN 130 | F3 "" 0 0 60 H V C CNN 131 | DRAW 132 | P 6 0 1 0 0 0 0 -50 50 -50 0 -100 -50 -50 0 -50 N 133 | X GND 1 0 0 0 D 50 50 1 1 W N 134 | ENDDRAW 135 | ENDDEF 136 | # 137 | # PWR_FLAG 138 | # 139 | DEF PWR_FLAG #FLG 0 0 N N 1 F P 140 | F0 "#FLG" 0 95 50 H I C CNN 141 | F1 "PWR_FLAG" 0 180 50 H V C CNN 142 | F2 "" 0 0 60 H V C CNN 143 | F3 "" 0 0 60 H V C CNN 144 | DRAW 145 | X pwr 1 0 0 0 U 20 20 0 0 w 146 | P 6 0 1 0 0 0 0 50 -75 100 0 150 75 100 0 50 N 147 | ENDDRAW 148 | ENDDEF 149 | # 150 | #End Library 151 | -------------------------------------------------------------------------------- /bindings/python/samples/samplebase.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import time 3 | import sys 4 | import os 5 | 6 | sys.path.append(os.path.abspath(os.path.dirname(__file__) + '/..')) 7 | from rgbmatrix import RGBMatrix, RGBMatrixOptions 8 | 9 | 10 | class SampleBase(object): 11 | def __init__(self, *args, **kwargs): 12 | self.parser = argparse.ArgumentParser() 13 | 14 | self.parser.add_argument("-r", "--led-rows", action="store", help="Display rows. 16 for 16x32, 32 for 32x32. Default: 32", default=32, type=int) 15 | self.parser.add_argument("--led-cols", action="store", help="Panel columns. Typically 32 or 64. (Default: 32)", default=32, type=int) 16 | self.parser.add_argument("-c", "--led-chain", action="store", help="Daisy-chained boards. Default: 1.", default=1, type=int) 17 | 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) 18 | 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) 19 | self.parser.add_argument("-b", "--led-brightness", action="store", help="Sets brightness level. Default: 100. Range: 1..100", default=100, type=int) 20 | 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'], type=str) 21 | 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) 22 | 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) 23 | self.parser.add_argument("--led-show-refresh", action="store_true", help="Shows the current refresh rate of the LED panel") 24 | self.parser.add_argument("--led-slowdown-gpio", action="store", help="Slow down writing to GPIO. Range: 0..4. Default: 1", default=1, type=int) 25 | self.parser.add_argument("--led-no-hardware-pulse", action="store", help="Don't use hardware pin-pulse generation") 26 | self.parser.add_argument("--led-rgb-sequence", action="store", help="Switch if your matrix has led colors swapped. Default: RGB", default="RGB", type=str) 27 | self.parser.add_argument("--led-pixel-mapper", action="store", help="Apply pixel mappers. e.g \"Rotate:90\"", default="", type=str) 28 | 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]) 29 | 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) 30 | self.parser.add_argument("--led-panel-type", action="store", help="Needed to initialize special panels. Supported: 'FM6126A'", default="", type=str) 31 | 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') 32 | self.parser.set_defaults(drop_privileges=True) 33 | 34 | def usleep(self, value): 35 | time.sleep(value / 1000000.0) 36 | 37 | def run(self): 38 | print("Running") 39 | 40 | def process(self): 41 | self.args = self.parser.parse_args() 42 | 43 | options = RGBMatrixOptions() 44 | 45 | if self.args.led_gpio_mapping != None: 46 | options.hardware_mapping = self.args.led_gpio_mapping 47 | options.rows = self.args.led_rows 48 | options.cols = self.args.led_cols 49 | options.chain_length = self.args.led_chain 50 | options.parallel = self.args.led_parallel 51 | options.row_address_type = self.args.led_row_addr_type 52 | options.multiplexing = self.args.led_multiplexing 53 | options.pwm_bits = self.args.led_pwm_bits 54 | options.brightness = self.args.led_brightness 55 | options.pwm_lsb_nanoseconds = self.args.led_pwm_lsb_nanoseconds 56 | options.led_rgb_sequence = self.args.led_rgb_sequence 57 | options.pixel_mapper_config = self.args.led_pixel_mapper 58 | options.panel_type = self.args.led_panel_type 59 | 60 | 61 | if self.args.led_show_refresh: 62 | options.show_refresh_rate = 1 63 | 64 | if self.args.led_slowdown_gpio != None: 65 | options.gpio_slowdown = self.args.led_slowdown_gpio 66 | if self.args.led_no_hardware_pulse: 67 | options.disable_hardware_pulsing = True 68 | if not self.args.drop_privileges: 69 | options.drop_privileges=False 70 | 71 | self.matrix = RGBMatrix(options = options) 72 | 73 | try: 74 | # Start loop 75 | print("Press CTRL-C to stop sample") 76 | self.run() 77 | except KeyboardInterrupt: 78 | print("Exiting\n") 79 | sys.exit(0) 80 | 81 | return True 82 | -------------------------------------------------------------------------------- /include/pixel-mapper.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // Copyright (C) 2018 Henner Zeller 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation version 2. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see 15 | #ifndef RGBMATRIX_PIXEL_MAPPER 16 | #define RGBMATRIX_PIXEL_MAPPER 17 | 18 | #include 19 | #include 20 | 21 | namespace rgb_matrix { 22 | 23 | // A pixel mapper is a way for you to map pixels of LED matrixes to a different 24 | // layout. If you have an implementation of a PixelMapper, you can give it 25 | // to the RGBMatrix::ApplyPixelMapper(), which then presents you a canvas 26 | // that has the new "visible_width", "visible_height". 27 | class PixelMapper { 28 | public: 29 | virtual ~PixelMapper() {} 30 | 31 | // Get the name of this PixelMapper. Each PixelMapper needs to have a name 32 | // so that it can be referred to with command line flags. 33 | virtual const char *GetName() const = 0; 34 | 35 | // Pixel mappers receive the chain and parallel information and 36 | // might receive optional user-parameters, e.g. from command line flags. 37 | // 38 | // This is a single string containing the parameters. 39 | // You can be used from simple scalar parameters, such as the angle for 40 | // the rotate transformer, or more complex parameters that describe a mapping 41 | // of panels for instance. 42 | // Keep it concise (as people will give parameters on the command line) and 43 | // don't use semicolons in your string (as they are 44 | // used to separate pixel mappers on the command line). 45 | // 46 | // For instance, the rotate transformer is invoked like this 47 | // --led-pixel-mapper=rotate:90 48 | // And the parameter that is passed to SetParameter() is "90". 49 | // 50 | // Returns 'true' if parameter was parsed successfully. 51 | virtual bool SetParameters(int chain, int parallel, 52 | const char *parameter_string) { 53 | return true; 54 | } 55 | 56 | // Given a underlying matrix (width, height), returns the 57 | // visible (width, height) after the mapping. 58 | // E.g. a 90 degree rotation might map matrix=(64, 32) -> visible=(32, 64) 59 | // Some multiplexing matrices will double the height and half the width. 60 | // 61 | // While not technically necessary, one would expect that the number of 62 | // pixels stay the same, so 63 | // matrix_width * matrix_height == (*visible_width) * (*visible_height); 64 | // 65 | // Returns boolean "true" if the mapping can be successfully done with this 66 | // mapper. 67 | virtual bool GetSizeMapping(int matrix_width, int matrix_height, 68 | int *visible_width, int *visible_height) 69 | const = 0; 70 | 71 | // Map where a visible pixel (x,y) is mapped to the underlying matrix (x,y). 72 | // 73 | // To be convienently stateless, the first parameters are the full 74 | // matrix width and height. 75 | // 76 | // So for many multiplexing methods this means to map a panel to a double 77 | // length and half height panel (32x16 -> 64x8). 78 | // The logic_x, logic_y are output parameters and guaranteed not to be 79 | // nullptr. 80 | virtual void MapVisibleToMatrix(int matrix_width, int matrix_height, 81 | int visible_x, int visible_y, 82 | int *matrix_x, int *matrix_y) const = 0; 83 | }; 84 | 85 | // This is a place to register PixelMappers globally. If you register your 86 | // PixelMapper before calling RGBMatrix::CreateFromFlags(), the named 87 | // PixelMapper is available in the --led-pixel-mapper options. 88 | // 89 | // Note, you don't _have_ to register your mapper, you can always call 90 | // RGBMatrix::ApplyPixelMapper() directly. Registering is for convenience and 91 | // commandline-flag support. 92 | // 93 | // There are a few standard mappers registered by default. 94 | void RegisterPixelMapper(PixelMapper *mapper); 95 | 96 | // Get a list of the names of available pixel mappers. 97 | std::vector GetAvailablePixelMappers(); 98 | 99 | // Given a name (e.g. "rotate") and a parameter (e.g. "90"), return the 100 | // parametrized PixelMapper with that name. Returns NULL if mapper 101 | // can not be found or parameter is invalid. 102 | // Ownership of the returned object is _NOT_ transferred to the caller. 103 | // Current available mappers are "U-mapper" and "Rotate". The "Rotate" 104 | // gets a parameter denoting the angle. 105 | const PixelMapper *FindPixelMapper(const char *name, 106 | int chain, int parallel, 107 | const char *parameter = NULL); 108 | } // namespace rgb_matrix 109 | 110 | #endif // RGBMATRIX_PIXEL_MAPPER 111 | -------------------------------------------------------------------------------- /adapter/passive-rpi1/passive-rpi-hub75-adapter.sch: -------------------------------------------------------------------------------- 1 | EESchema Schematic File Version 2 2 | LIBS:power 3 | LIBS:device 4 | LIBS:transistors 5 | LIBS:conn 6 | LIBS:linear 7 | LIBS:regul 8 | LIBS:74xx 9 | LIBS:cmos4000 10 | LIBS:adc-dac 11 | LIBS:memory 12 | LIBS:xilinx 13 | LIBS:microcontrollers 14 | LIBS:dsp 15 | LIBS:microchip 16 | LIBS:analog_switches 17 | LIBS:motorola 18 | LIBS:texas 19 | LIBS:intel 20 | LIBS:audio 21 | LIBS:interface 22 | LIBS:digital-audio 23 | LIBS:philips 24 | LIBS:display 25 | LIBS:cypress 26 | LIBS:siliconi 27 | LIBS:opto 28 | LIBS:atmel 29 | LIBS:contrib 30 | LIBS:valves 31 | EELAYER 25 0 32 | EELAYER END 33 | $Descr A4 11693 8268 34 | encoding utf-8 35 | Sheet 1 1 36 | Title "" 37 | Date "" 38 | Rev "" 39 | Comp "" 40 | Comment1 "" 41 | Comment2 "" 42 | Comment3 "" 43 | Comment4 "" 44 | $EndDescr 45 | $Comp 46 | L CONN_02X08 Panel-1 47 | U 1 1 54ECB236 48 | P 6000 3450 49 | F 0 "Panel-1" H 6000 3900 50 0000 C CNN 50 | F 1 "CONN_02X08" V 6000 3450 50 0000 C CNN 51 | F 2 "Pin_Headers:Pin_Header_Straight_2x08" H 6000 2250 60 0001 C CNN 52 | F 3 "" H 6000 2250 60 0000 C CNN 53 | 1 6000 3450 54 | 1 0 0 -1 55 | $EndComp 56 | $Comp 57 | L CONN_02X13 P1 58 | U 1 1 54ECB2B7 59 | P 4500 3350 60 | F 0 "P1" H 4500 4100 50 0000 C CNN 61 | F 1 "RPi-Header" V 4500 3350 50 0000 C CNN 62 | F 2 "Pin_Headers:Pin_Header_Straight_2x13" H 4500 2400 60 0001 C CNN 63 | F 3 "" H 4500 2400 60 0000 C CNN 64 | 1 4500 3350 65 | 1 0 0 -1 66 | $EndComp 67 | $Comp 68 | L GND #PWR01 69 | U 1 1 54ECB3E1 70 | P 4850 3350 71 | F 0 "#PWR01" H 4850 3350 30 0001 C CNN 72 | F 1 "GND" H 4850 3280 30 0001 C CNN 73 | F 2 "" H 4850 3350 60 0000 C CNN 74 | F 3 "" H 4850 3350 60 0000 C CNN 75 | 1 4850 3350 76 | 0 -1 -1 0 77 | $EndComp 78 | Wire Wire Line 79 | 4750 3350 4850 3350 80 | $Comp 81 | L GND #PWR02 82 | U 1 1 54ECB417 83 | P 4850 3650 84 | F 0 "#PWR02" H 4850 3650 30 0001 C CNN 85 | F 1 "GND" H 4850 3580 30 0001 C CNN 86 | F 2 "" H 4850 3650 60 0000 C CNN 87 | F 3 "" H 4850 3650 60 0000 C CNN 88 | 1 4850 3650 89 | 0 -1 -1 0 90 | $EndComp 91 | Wire Wire Line 92 | 4750 3650 4850 3650 93 | $Comp 94 | L GND #PWR03 95 | U 1 1 54ECB4A1 96 | P 4850 2950 97 | F 0 "#PWR03" H 4850 2950 30 0001 C CNN 98 | F 1 "GND" H 4850 2880 30 0001 C CNN 99 | F 2 "" H 4850 2950 60 0000 C CNN 100 | F 3 "" H 4850 2950 60 0000 C CNN 101 | 1 4850 2950 102 | 0 -1 -1 0 103 | $EndComp 104 | Wire Wire Line 105 | 4750 2950 4850 2950 106 | $Comp 107 | L GND #PWR04 108 | U 1 1 54ECB7BC 109 | P 4150 3950 110 | F 0 "#PWR04" H 4150 3950 30 0001 C CNN 111 | F 1 "GND" H 4150 3880 30 0001 C CNN 112 | F 2 "" H 4150 3950 60 0000 C CNN 113 | F 3 "" H 4150 3950 60 0000 C CNN 114 | 1 4150 3950 115 | 0 1 1 0 116 | $EndComp 117 | Wire Wire Line 118 | 4150 3950 4250 3950 119 | Text GLabel 4250 3050 0 51 Output ~ 0 120 | strobe 121 | Text GLabel 4250 3450 0 51 Output ~ 0 122 | row_A 123 | Text GLabel 4750 3450 2 51 Output ~ 0 124 | row_B 125 | Text GLabel 4750 3550 2 51 Output ~ 0 126 | row_C 127 | Text GLabel 4750 3750 2 51 Output ~ 0 128 | row_D 129 | Text GLabel 4250 3250 0 51 Output ~ 0 130 | clock 131 | Text GLabel 4250 3850 0 51 Output ~ 0 132 | p0_r1 133 | Text GLabel 4250 3350 0 51 Output ~ 0 134 | p0_g1 135 | Text GLabel 4750 3950 2 51 Output ~ 0 136 | p0_b1 137 | Text GLabel 4750 3850 2 51 Output ~ 0 138 | p0_r2 139 | Text GLabel 4250 3750 0 51 Output ~ 0 140 | p0_g2 141 | Text GLabel 4250 3650 0 51 Output ~ 0 142 | p0_b2 143 | Text GLabel 4750 3250 2 51 Output ~ 0 144 | OE 145 | Wire Wire Line 146 | 6250 3400 6800 3400 147 | $Comp 148 | L GND #PWR05 149 | U 1 1 54ECD031 150 | P 6800 3850 151 | F 0 "#PWR05" H 6800 3850 30 0001 C CNN 152 | F 1 "GND" H 6800 3780 30 0001 C CNN 153 | F 2 "" H 6800 3850 60 0000 C CNN 154 | F 3 "" H 6800 3850 60 0000 C CNN 155 | 1 6800 3850 156 | 1 0 0 -1 157 | $EndComp 158 | Wire Wire Line 159 | 6250 3800 6800 3800 160 | Wire Wire Line 161 | 6250 3200 6800 3200 162 | Wire Wire Line 163 | 6800 3200 6800 3850 164 | Connection ~ 6800 3400 165 | Connection ~ 6800 3800 166 | Text GLabel 6250 3300 2 51 Input ~ 0 167 | p0_g2 168 | Text GLabel 6250 3100 2 51 Input ~ 0 169 | p0_g1 170 | Text GLabel 6250 3600 2 51 Input ~ 0 171 | row_D 172 | Text GLabel 6250 3500 2 51 Input ~ 0 173 | row_B 174 | Text GLabel 5750 3100 0 51 Input ~ 0 175 | p0_r1 176 | Text GLabel 5750 3700 0 51 Input ~ 0 177 | clock 178 | Text GLabel 5750 3400 0 51 Input ~ 0 179 | p0_b2 180 | Text GLabel 5750 3200 0 51 Input ~ 0 181 | p0_b1 182 | Text GLabel 5750 3300 0 51 Input ~ 0 183 | p0_r2 184 | Text GLabel 5750 3800 0 51 Input ~ 0 185 | OE 186 | Text GLabel 5750 3500 0 51 Input ~ 0 187 | row_A 188 | Text GLabel 5750 3600 0 51 Input ~ 0 189 | row_C 190 | Text GLabel 6250 3700 2 51 Input ~ 0 191 | strobe 192 | NoConn ~ 4250 2750 193 | NoConn ~ 4250 3150 194 | NoConn ~ 4250 3550 195 | NoConn ~ 4750 3150 196 | NoConn ~ 4750 2750 197 | NoConn ~ 4750 2850 198 | NoConn ~ 4250 2850 199 | NoConn ~ 4250 2950 200 | NoConn ~ 4750 3050 201 | $Comp 202 | L PWR_FLAG #FLG06 203 | U 1 1 557E1359 204 | P 4200 4050 205 | F 0 "#FLG06" H 4200 4145 50 0001 C CNN 206 | F 1 "PWR_FLAG" H 4200 4230 50 0000 C CNN 207 | F 2 "" H 4200 4050 60 0000 C CNN 208 | F 3 "" H 4200 4050 60 0000 C CNN 209 | 1 4200 4050 210 | -1 0 0 1 211 | $EndComp 212 | Wire Wire Line 213 | 4200 3950 4200 4050 214 | Connection ~ 4200 3950 215 | $EndSCHEMATC 216 | -------------------------------------------------------------------------------- /lib/gpio.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // Copyright (C) 2013 Henner Zeller 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation version 2. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see 15 | 16 | #ifndef RPI_GPIO_INTERNAL_H 17 | #define RPI_GPIO_INTERNAL_H 18 | 19 | #include "gpio-bits.h" 20 | 21 | #include 22 | 23 | // Putting this in our namespace to not collide with other things called like 24 | // this. 25 | namespace rgb_matrix { 26 | // For now, everything is initialized as output. 27 | class GPIO { 28 | public: 29 | GPIO(); 30 | 31 | // Initialize before use. Returns 'true' if successful, 'false' otherwise 32 | // (e.g. due to a permission problem). 33 | bool Init(int slowdown); 34 | 35 | // Initialize outputs. 36 | // Returns the bits that were available and could be set for output. 37 | // (never use the optional adafruit_hack_needed parameter, it is used 38 | // internally to this library). 39 | gpio_bits_t InitOutputs(gpio_bits_t outputs, 40 | bool adafruit_hack_needed = false); 41 | 42 | // Request given bitmap of GPIO inputs. 43 | // Returns the bits that were available and could be reserved. 44 | gpio_bits_t RequestInputs(gpio_bits_t inputs); 45 | 46 | // Set the bits that are '1' in the output. Leave the rest untouched. 47 | inline void SetBits(gpio_bits_t value) { 48 | if (!value) return; 49 | WriteSetBits(value); 50 | for (int i = 0; i < slowdown_; ++i) { 51 | WriteSetBits(value); 52 | } 53 | } 54 | 55 | // Clear the bits that are '1' in the output. Leave the rest untouched. 56 | inline void ClearBits(gpio_bits_t value) { 57 | if (!value) return; 58 | WriteClrBits(value); 59 | for (int i = 0; i < slowdown_; ++i) { 60 | WriteClrBits(value); 61 | } 62 | } 63 | 64 | // Write all the bits of "value" mentioned in "mask". Leave the rest untouched. 65 | inline void WriteMaskedBits(gpio_bits_t value, gpio_bits_t mask) { 66 | // Writing a word is two operations. The IO is actually pretty slow, so 67 | // this should probably be unnoticable. 68 | ClearBits(~value & mask); 69 | SetBits(value & mask); 70 | } 71 | 72 | inline gpio_bits_t Read() const { return ReadRegisters() & input_bits_; } 73 | 74 | // Return if this is appears to be a Pi4 75 | static bool IsPi4(); 76 | 77 | private: 78 | inline gpio_bits_t ReadRegisters() const { 79 | return (static_cast(*gpio_read_bits_low_) 80 | #ifdef ENABLE_WIDE_GPIO_COMPUTE_MODULE 81 | | (static_cast(*gpio_read_bits_low_) << 32) 82 | #endif 83 | ); 84 | } 85 | 86 | inline void WriteSetBits(gpio_bits_t value) { 87 | *gpio_set_bits_low_ = static_cast(value & 0xFFFFFFFF); 88 | #ifdef ENABLE_WIDE_GPIO_COMPUTE_MODULE 89 | if (uses_64_bit_) 90 | *gpio_set_bits_high_ = static_cast(value >> 32); 91 | #endif 92 | } 93 | 94 | inline void WriteClrBits(gpio_bits_t value) { 95 | *gpio_clr_bits_low_ = static_cast(value & 0xFFFFFFFF); 96 | #ifdef ENABLE_WIDE_GPIO_COMPUTE_MODULE 97 | if (uses_64_bit_) 98 | *gpio_clr_bits_high_ = static_cast(value >> 32); 99 | #endif 100 | } 101 | 102 | private: 103 | gpio_bits_t output_bits_; 104 | gpio_bits_t input_bits_; 105 | gpio_bits_t reserved_bits_; 106 | int slowdown_; 107 | 108 | volatile uint32_t *gpio_set_bits_low_; 109 | volatile uint32_t *gpio_clr_bits_low_; 110 | volatile uint32_t *gpio_read_bits_low_; 111 | 112 | #ifdef ENABLE_WIDE_GPIO_COMPUTE_MODULE 113 | bool uses_64_bit_; 114 | volatile uint32_t *gpio_set_bits_high_; 115 | volatile uint32_t *gpio_clr_bits_high_; 116 | volatile uint32_t *gpio_read_bits_high_; 117 | #endif 118 | }; 119 | 120 | // A PinPulser is a utility class that pulses a GPIO pin. There can be various 121 | // implementations. 122 | class PinPulser { 123 | public: 124 | // Factory for a PinPulser. Chooses the right implementation depending 125 | // on the context (CPU and which pins are affected). 126 | // "gpio_mask" is the mask that should be output (since we only 127 | // need negative pulses, this is what it does) 128 | // "nano_wait_spec" contains a list of time periods we'd like 129 | // invoke later. This can be used to pre-process timings if needed. 130 | static PinPulser *Create(GPIO *io, gpio_bits_t gpio_mask, 131 | bool allow_hardware_pulsing, 132 | const std::vector &nano_wait_spec); 133 | 134 | virtual ~PinPulser() {} 135 | 136 | // Send a pulse with a given length (index into nano_wait_spec array). 137 | virtual void SendPulse(int time_spec_number) = 0; 138 | 139 | // If SendPulse() is asynchronously implemented, wait for pulse to finish. 140 | virtual void WaitPulseFinished() {} 141 | }; 142 | 143 | // Get rolling over microsecond counter. We get this from a hardware register 144 | // if possible and a terrible slow fallback otherwise. 145 | uint32_t GetMicrosecondCounter(); 146 | 147 | } // end namespace rgb_matrix 148 | 149 | #endif // RPI_GPIO_INGERNALH 150 | -------------------------------------------------------------------------------- /examples-api-use/image-example.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // 3 | // Example how to display an image, including animated images using 4 | // ImageMagick. For a full utility that does a few more things, have a look 5 | // at the led-image-viewer in ../utils 6 | // 7 | // Showing an image is not so complicated, essentially just copy all the 8 | // pixels to the canvas. How to get the pixels ? In this example we're using 9 | // the graphicsmagick library as universal image loader library that 10 | // can also deal with animated images. 11 | // You can of course do your own image loading or use some other library. 12 | // 13 | // This requires an external dependency, so install these first before you 14 | // can call `make image-example` 15 | // sudo apt-get update 16 | // sudo apt-get install libgraphicsmagick++-dev libwebp-dev -y 17 | // make image-example 18 | 19 | #include "led-matrix.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | using rgb_matrix::Canvas; 31 | using rgb_matrix::RGBMatrix; 32 | using rgb_matrix::FrameCanvas; 33 | 34 | // Make sure we can exit gracefully when Ctrl-C is pressed. 35 | volatile bool interrupt_received = false; 36 | static void InterruptHandler(int signo) { 37 | interrupt_received = true; 38 | } 39 | 40 | using ImageVector = std::vector; 41 | 42 | // Given the filename, load the image and scale to the size of the 43 | // matrix. 44 | // // If this is an animated image, the resutlting vector will contain multiple. 45 | static ImageVector LoadImageAndScaleImage(const char *filename, 46 | int target_width, 47 | int target_height) { 48 | ImageVector result; 49 | 50 | ImageVector frames; 51 | try { 52 | readImages(&frames, filename); 53 | } catch (std::exception &e) { 54 | if (e.what()) 55 | fprintf(stderr, "%s\n", e.what()); 56 | return result; 57 | } 58 | 59 | if (frames.empty()) { 60 | fprintf(stderr, "No image found."); 61 | return result; 62 | } 63 | 64 | // Animated images have partial frames that need to be put together 65 | if (frames.size() > 1) { 66 | Magick::coalesceImages(&result, frames.begin(), frames.end()); 67 | } else { 68 | result.push_back(frames[0]); // just a single still image. 69 | } 70 | 71 | for (Magick::Image &image : result) { 72 | image.scale(Magick::Geometry(target_width, target_height)); 73 | } 74 | 75 | return result; 76 | } 77 | 78 | 79 | // Copy an image to a Canvas. Note, the RGBMatrix is implementing the Canvas 80 | // interface as well as the FrameCanvas we use in the double-buffering of the 81 | // animted image. 82 | void CopyImageToCanvas(const Magick::Image &image, Canvas *canvas) { 83 | const int offset_x = 0, offset_y = 0; // If you want to move the image. 84 | // Copy all the pixels to the canvas. 85 | for (size_t y = 0; y < image.rows(); ++y) { 86 | for (size_t x = 0; x < image.columns(); ++x) { 87 | const Magick::Color &c = image.pixelColor(x, y); 88 | if (c.alphaQuantum() < 256) { 89 | canvas->SetPixel(x + offset_x, y + offset_y, 90 | ScaleQuantumToChar(c.redQuantum()), 91 | ScaleQuantumToChar(c.greenQuantum()), 92 | ScaleQuantumToChar(c.blueQuantum())); 93 | } 94 | } 95 | } 96 | } 97 | 98 | // An animated image has to constantly swap to the next frame. 99 | // We're using double-buffering and fill an offscreen buffer first, then show. 100 | void ShowAnimatedImage(const ImageVector &images, RGBMatrix *matrix) { 101 | FrameCanvas *offscreen_canvas = matrix->CreateFrameCanvas(); 102 | while (!interrupt_received) { 103 | for (const auto &image : images) { 104 | if (interrupt_received) break; 105 | CopyImageToCanvas(image, offscreen_canvas); 106 | offscreen_canvas = matrix->SwapOnVSync(offscreen_canvas); 107 | usleep(image.animationDelay() * 10000); // 1/100s converted to usec 108 | } 109 | } 110 | } 111 | 112 | int usage(const char *progname) { 113 | fprintf(stderr, "Usage: %s [led-matrix-options] \n", 114 | progname); 115 | rgb_matrix::PrintMatrixFlags(stderr); 116 | return 1; 117 | } 118 | 119 | int main(int argc, char *argv[]) { 120 | Magick::InitializeMagick(*argv); 121 | 122 | // Initialize the RGB matrix with 123 | RGBMatrix::Options matrix_options; 124 | rgb_matrix::RuntimeOptions runtime_opt; 125 | if (!rgb_matrix::ParseOptionsFromFlags(&argc, &argv, 126 | &matrix_options, &runtime_opt)) { 127 | return usage(argv[0]); 128 | } 129 | 130 | if (argc != 2) 131 | return usage(argv[0]); 132 | const char *filename = argv[1]; 133 | 134 | signal(SIGTERM, InterruptHandler); 135 | signal(SIGINT, InterruptHandler); 136 | 137 | RGBMatrix *matrix = RGBMatrix::CreateFromOptions(matrix_options, runtime_opt); 138 | if (matrix == NULL) 139 | return 1; 140 | 141 | ImageVector images = LoadImageAndScaleImage(filename, 142 | matrix->width(), 143 | matrix->height()); 144 | switch (images.size()) { 145 | case 0: // failed to load image. 146 | break; 147 | case 1: // Simple example: one image to show 148 | CopyImageToCanvas(images[0], matrix); 149 | while (!interrupt_received) sleep(1000); // Until Ctrl-C is pressed 150 | break; 151 | default: // More than one image: this is an animation. 152 | ShowAnimatedImage(images, matrix); 153 | break; 154 | } 155 | 156 | matrix->Clear(); 157 | delete matrix; 158 | 159 | return 0; 160 | } 161 | -------------------------------------------------------------------------------- /include/graphics.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // Very simple graphics library to do simple things. 3 | // 4 | // Might be useful to consider using Cairo instead and just have an interface 5 | // between that and the Canvas. Well, this is a quick set of things to get 6 | // started (and nicely self-contained). 7 | #ifndef RPI_GRAPHICS_H 8 | #define RPI_GRAPHICS_H 9 | 10 | #include "canvas.h" 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | namespace rgb_matrix { 18 | struct Color { 19 | Color() : r(0), g(0), b(0) {} 20 | Color(uint8_t rr, uint8_t gg, uint8_t bb) : r(rr), g(gg), b(bb) {} 21 | uint8_t r; 22 | uint8_t g; 23 | uint8_t b; 24 | }; 25 | 26 | // Font loading bdf files. If this ever becomes more types, just make virtual 27 | // base class. 28 | class Font { 29 | public: 30 | // Initialize font, but it is only usable after LoadFont() has been called. 31 | Font(); 32 | ~Font(); 33 | 34 | bool LoadFont(const char *path); 35 | 36 | // Return height of font in pixels. Returns -1 if font has not been loaded. 37 | int height() const { return font_height_; } 38 | 39 | // Return baseline. Pixels from the topline to the baseline. 40 | int baseline() const { return base_line_; } 41 | 42 | // Return width of given character, or -1 if font is not loaded or character 43 | // does not exist. 44 | int CharacterWidth(uint32_t unicode_codepoint) const; 45 | 46 | // Draws the unicode character at position "x","y" 47 | // with "color" on "background_color" (background_color can be NULL for 48 | // transparency. 49 | // The "y" position is the baseline of the font. 50 | // If we don't have it in the font, draws the replacement character "�" if 51 | // available. 52 | // Returns how much we advance on the screen, which is the width of the 53 | // character or 0 if we didn't draw any chracter. 54 | int DrawGlyph(Canvas *c, int x, int y, 55 | const Color &color, const Color *background_color, 56 | uint32_t unicode_codepoint) const; 57 | 58 | // Same without background. Deprecated, use the one above instead. 59 | int DrawGlyph(Canvas *c, int x, int y, const Color &color, 60 | uint32_t unicode_codepoint) const; 61 | 62 | // Create a new font derived from this font, which represents an outline 63 | // of the original font, essentially pixels tracing around the original 64 | // letter. 65 | // This can be used in situations in which it is desirable to frame a letter 66 | // in a different color to increase contrast. 67 | // The ownership of the returned pointer is passed to the caller. 68 | Font *CreateOutlineFont() const; 69 | 70 | private: 71 | Font(const Font& x); // No copy constructor. Use references or pointer instead. 72 | 73 | struct Glyph; 74 | typedef std::map CodepointGlyphMap; 75 | 76 | const Glyph *FindGlyph(uint32_t codepoint) const; 77 | 78 | int font_height_; 79 | int base_line_; 80 | CodepointGlyphMap glyphs_; 81 | }; 82 | 83 | // -- Some utility functions. 84 | 85 | // Utility function: set an image from the given buffer containting pixels. 86 | // 87 | // Draw image of size "image_width" and "image_height" from pixel at 88 | // canvas-offset "canvas_offset_x", "canvas_offset_y". Image will be shown 89 | // cropped on the edges if needed. 90 | // 91 | // The canvas offset can be negative, i.e. the image start can be shifted 92 | // outside the image frame on the left/top edge. 93 | // 94 | // The buffer needs to be organized as rows with columns of three bytes 95 | // organized as rgb or bgr. Thus the size of the buffer needs to be exactly 96 | // (3 * image_width * image_height) bytes. 97 | // 98 | // The "image_buffer" parameters contains the data, "buffer_size_bytes" the 99 | // size in bytes. 100 | // 101 | // If "is_bgr" is true, the buffer is treated as BGR pixel arrangement instead 102 | // of RGB. 103 | // Returns 'true' if image was shown within canvas. 104 | bool SetImage(Canvas *c, int canvas_offset_x, int canvas_offset_y, 105 | const uint8_t *image_buffer, size_t buffer_size_bytes, 106 | int image_width, int image_height, 107 | bool is_bgr); 108 | 109 | // Draw text, a standard NUL terminated C-string encoded in UTF-8, 110 | // with given "font" at "x","y" with "color". 111 | // "color" always needs to be set (hence it is a reference), 112 | // "background_color" is a pointer to optionally be NULL for transparency. 113 | // "kerning_offset" allows for additional spacing between characters (can be 114 | // negative) 115 | // Returns how many pixels we advanced on the screen. 116 | int DrawText(Canvas *c, const Font &font, int x, int y, 117 | const Color &color, const Color *background_color, 118 | const char *utf8_text, int kerning_offset = 0); 119 | 120 | // Same without background. Deprecated, use the one above instead. 121 | int DrawText(Canvas *c, const Font &font, int x, int y, const Color &color, 122 | const char *utf8_text); 123 | 124 | // Draw text, a standard NUL terminated C-string encoded in UTF-8, 125 | // with given "font" at "x","y" with "color". 126 | // Draw text as above, but vertically (top down). 127 | // The text is a standard NUL terminated C-string encoded in UTF-8. 128 | // "font, "x", "y", "color" and "background_color" are same as DrawText(). 129 | // "kerning_offset" allows for additional spacing between characters (can be 130 | // negative). 131 | // Returns font height to advance up on the screen. 132 | int VerticalDrawText(Canvas *c, const Font &font, int x, int y, 133 | const Color &color, const Color *background_color, 134 | const char *utf8_text, int kerning_offset = 0); 135 | 136 | // Draw a circle centered at "x", "y", with a radius of "radius" and with "color" 137 | void DrawCircle(Canvas *c, int x, int y, int radius, const Color &color); 138 | 139 | // Draw a line from "x0", "y0" to "x1", "y1" and with "color" 140 | void DrawLine(Canvas *c, int x0, int y0, int x1, int y1, const Color &color); 141 | 142 | } // namespace rgb_matrix 143 | 144 | #endif // RPI_GRAPHICS_H 145 | -------------------------------------------------------------------------------- /examples-api-use/scrolling-text-example.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // Small example how to scroll text. 3 | // 4 | // This code is public domain 5 | // (but note, that the led-matrix library this depends on is GPL v2) 6 | 7 | // For a utility with a few more features see 8 | // ../utils/text-scroller.cc 9 | 10 | #include "led-matrix.h" 11 | #include "graphics.h" 12 | 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | using namespace rgb_matrix; 23 | 24 | volatile bool interrupt_received = false; 25 | static void InterruptHandler(int signo) { 26 | interrupt_received = true; 27 | } 28 | 29 | static int usage(const char *progname) { 30 | fprintf(stderr, "usage: %s [options] \n", progname); 31 | fprintf(stderr, "Takes text and scrolls it with speed -s\n"); 32 | fprintf(stderr, "Options:\n"); 33 | fprintf(stderr, 34 | "\t-s : Approximate letters per second. " 35 | "(Zero for no scrolling)\n" 36 | "\t-l : Number of loops through the text. " 37 | "-1 for endless (default)\n" 38 | "\t-f : Use given font.\n" 39 | "\t-x : X-Origin of displaying text (Default: 0)\n" 40 | "\t-y : Y-Origin of displaying text (Default: 0)\n" 41 | "\t-t : Spacing pixels between letters (Default: 0)\n" 42 | "\n" 43 | "\t-C : Text-Color. Default 255,255,0\n" 44 | "\t-B : Background-Color. Default 0,0,0\n" 45 | "\n" 46 | ); 47 | rgb_matrix::PrintMatrixFlags(stderr); 48 | return 1; 49 | } 50 | 51 | static bool parseColor(Color *c, const char *str) { 52 | return sscanf(str, "%hhu,%hhu,%hhu", &c->r, &c->g, &c->b) == 3; 53 | } 54 | 55 | static bool FullSaturation(const Color &c) { 56 | return (c.r == 0 || c.r == 255) 57 | && (c.g == 0 || c.g == 255) 58 | && (c.b == 0 || c.b == 255); 59 | } 60 | 61 | int main(int argc, char *argv[]) { 62 | RGBMatrix::Options matrix_options; 63 | rgb_matrix::RuntimeOptions runtime_opt; 64 | if (!rgb_matrix::ParseOptionsFromFlags(&argc, &argv, 65 | &matrix_options, &runtime_opt)) { 66 | return usage(argv[0]); 67 | } 68 | 69 | Color color(255, 255, 0); 70 | Color bg_color(0, 0, 0); 71 | 72 | const char *bdf_font_file = NULL; 73 | std::string line; 74 | /* x_origin is set by default just right of the screen */ 75 | const int x_default_start = (matrix_options.chain_length 76 | * matrix_options.cols) + 5; 77 | int x_orig = x_default_start; 78 | int y_orig = 0; 79 | int letter_spacing = 0; 80 | float speed = 7.0f; 81 | int loops = -1; 82 | 83 | int opt; 84 | while ((opt = getopt(argc, argv, "x:y:f:C:B:t:s:l:")) != -1) { 85 | switch (opt) { 86 | case 's': speed = atof(optarg); break; 87 | case 'l': loops = atoi(optarg); break; 88 | case 'x': x_orig = atoi(optarg); break; 89 | case 'y': y_orig = atoi(optarg); break; 90 | case 'f': bdf_font_file = strdup(optarg); break; 91 | case 't': letter_spacing = atoi(optarg); break; 92 | case 'C': 93 | if (!parseColor(&color, optarg)) { 94 | fprintf(stderr, "Invalid color spec: %s\n", optarg); 95 | return usage(argv[0]); 96 | } 97 | break; 98 | case 'B': 99 | if (!parseColor(&bg_color, optarg)) { 100 | fprintf(stderr, "Invalid background color spec: %s\n", optarg); 101 | return usage(argv[0]); 102 | } 103 | break; 104 | default: 105 | return usage(argv[0]); 106 | } 107 | } 108 | 109 | for (int i = optind; i < argc; ++i) { 110 | line.append(argv[i]).append(" "); 111 | } 112 | 113 | if (line.empty()) { 114 | fprintf(stderr, "Add the text you want to print on the command-line.\n"); 115 | return usage(argv[0]); 116 | } 117 | 118 | if (bdf_font_file == NULL) { 119 | fprintf(stderr, "Need to specify BDF font-file with -f\n"); 120 | return usage(argv[0]); 121 | } 122 | 123 | /* 124 | * Load font. This needs to be a filename with a bdf bitmap font. 125 | */ 126 | rgb_matrix::Font font; 127 | if (!font.LoadFont(bdf_font_file)) { 128 | fprintf(stderr, "Couldn't load font '%s'\n", bdf_font_file); 129 | return 1; 130 | } 131 | 132 | RGBMatrix *canvas = RGBMatrix::CreateFromOptions(matrix_options, runtime_opt); 133 | if (canvas == NULL) 134 | return 1; 135 | 136 | const bool all_extreme_colors = (matrix_options.brightness == 100) 137 | && FullSaturation(color) 138 | && FullSaturation(bg_color); 139 | if (all_extreme_colors) 140 | canvas->SetPWMBits(1); 141 | 142 | signal(SIGTERM, InterruptHandler); 143 | signal(SIGINT, InterruptHandler); 144 | 145 | printf("CTRL-C for exit.\n"); 146 | 147 | // Create a new canvas to be used with led_matrix_swap_on_vsync 148 | FrameCanvas *offscreen_canvas = canvas->CreateFrameCanvas(); 149 | 150 | int delay_speed_usec = 1000000; 151 | if (speed > 0) { 152 | delay_speed_usec = 1000000 / speed / font.CharacterWidth('W'); 153 | } else if (x_orig == x_default_start) { 154 | // There would be no scrolling, so text would never appear. Move to front. 155 | x_orig = 0; 156 | } 157 | 158 | int x = x_orig; 159 | int y = y_orig; 160 | int length = 0; 161 | 162 | while (!interrupt_received && loops != 0) { 163 | offscreen_canvas->Fill(bg_color.r, bg_color.g, bg_color.b); 164 | // length = holds how many pixels our text takes up 165 | length = rgb_matrix::DrawText(offscreen_canvas, font, 166 | x, y + font.baseline(), 167 | color, nullptr, 168 | line.c_str(), letter_spacing); 169 | 170 | if (speed > 0 && --x + length < 0) { 171 | x = x_orig; 172 | if (loops > 0) --loops; 173 | } 174 | 175 | // Swap the offscreen_canvas with canvas on vsync, avoids flickering 176 | offscreen_canvas = canvas->SwapOnVSync(offscreen_canvas); 177 | usleep(delay_speed_usec); 178 | } 179 | 180 | // Finished. Shut down the RGB matrix. 181 | delete canvas; 182 | 183 | return 0; 184 | } 185 | -------------------------------------------------------------------------------- /examples-api-use/text-example.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // Small example how write text. 3 | // 4 | // This code is public domain 5 | // (but note, that the led-matrix library this depends on is GPL v2) 6 | 7 | #include "led-matrix.h" 8 | #include "graphics.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | using namespace rgb_matrix; 17 | 18 | static int usage(const char *progname) { 19 | fprintf(stderr, "usage: %s [options]\n", progname); 20 | fprintf(stderr, "Reads text from stdin and displays it. " 21 | "Empty string: clear screen\n"); 22 | fprintf(stderr, "Options:\n"); 23 | fprintf(stderr, 24 | "\t-f : Use given font.\n" 25 | "\t-x : X-Origin of displaying text (Default: 0)\n" 26 | "\t-y : Y-Origin of displaying text (Default: 0)\n" 27 | "\t-S : Spacing pixels between letters (Default: 0)\n" 28 | "\t-C : Color. Default 255,255,0\n" 29 | "\t-B : Font Background-Color. Default 0,0,0\n" 30 | "\t-O : Outline-Color, e.g. to increase contrast.\n" 31 | "\t-F : Panel flooding-background color. Default 0,0,0\n" 32 | "\n" 33 | ); 34 | rgb_matrix::PrintMatrixFlags(stderr); 35 | return 1; 36 | } 37 | 38 | static bool parseColor(Color *c, const char *str) { 39 | return sscanf(str, "%hhu,%hhu,%hhu", &c->r, &c->g, &c->b) == 3; 40 | } 41 | 42 | static bool FullSaturation(const Color &c) { 43 | return (c.r == 0 || c.r == 255) 44 | && (c.g == 0 || c.g == 255) 45 | && (c.b == 0 || c.b == 255); 46 | } 47 | 48 | int main(int argc, char *argv[]) { 49 | RGBMatrix::Options matrix_options; 50 | rgb_matrix::RuntimeOptions runtime_opt; 51 | if (!rgb_matrix::ParseOptionsFromFlags(&argc, &argv, 52 | &matrix_options, &runtime_opt)) { 53 | return usage(argv[0]); 54 | } 55 | 56 | Color color(255, 255, 0); 57 | Color bg_color(0, 0, 0); 58 | Color flood_color(0, 0, 0); 59 | Color outline_color(0,0,0); 60 | bool with_outline = false; 61 | 62 | const char *bdf_font_file = NULL; 63 | int x_orig = 0; 64 | int y_orig = 0; 65 | int letter_spacing = 0; 66 | 67 | int opt; 68 | while ((opt = getopt(argc, argv, "x:y:f:C:B:O:S:F:")) != -1) { 69 | switch (opt) { 70 | case 'x': x_orig = atoi(optarg); break; 71 | case 'y': y_orig = atoi(optarg); break; 72 | case 'f': bdf_font_file = strdup(optarg); break; 73 | case 'S': letter_spacing = atoi(optarg); break; 74 | case 'C': 75 | if (!parseColor(&color, optarg)) { 76 | fprintf(stderr, "Invalid color spec: %s\n", optarg); 77 | return usage(argv[0]); 78 | } 79 | break; 80 | case 'B': 81 | if (!parseColor(&bg_color, optarg)) { 82 | fprintf(stderr, "Invalid background color spec: %s\n", optarg); 83 | return usage(argv[0]); 84 | } 85 | break; 86 | case 'O': 87 | if (!parseColor(&outline_color, optarg)) { 88 | fprintf(stderr, "Invalid outline color spec: %s\n", optarg); 89 | return usage(argv[0]); 90 | } 91 | with_outline = true; 92 | break; 93 | case 'F': 94 | if (!parseColor(&flood_color, optarg)) { 95 | fprintf(stderr, "Invalid background color spec: %s\n", optarg); 96 | return usage(argv[0]); 97 | } 98 | break; 99 | default: 100 | return usage(argv[0]); 101 | } 102 | } 103 | 104 | if (bdf_font_file == NULL) { 105 | fprintf(stderr, "Need to specify BDF font-file with -f\n"); 106 | return usage(argv[0]); 107 | } 108 | 109 | /* 110 | * Load font. This needs to be a filename with a bdf bitmap font. 111 | */ 112 | rgb_matrix::Font font; 113 | if (!font.LoadFont(bdf_font_file)) { 114 | fprintf(stderr, "Couldn't load font '%s'\n", bdf_font_file); 115 | return 1; 116 | } 117 | 118 | /* 119 | * If we want an outline around the font, we create a new font with 120 | * the original font as a template that is just an outline font. 121 | */ 122 | rgb_matrix::Font *outline_font = NULL; 123 | if (with_outline) { 124 | outline_font = font.CreateOutlineFont(); 125 | } 126 | 127 | RGBMatrix *canvas = RGBMatrix::CreateFromOptions(matrix_options, runtime_opt); 128 | if (canvas == NULL) 129 | return 1; 130 | 131 | const bool all_extreme_colors = (matrix_options.brightness == 100) 132 | && FullSaturation(color) 133 | && FullSaturation(bg_color) 134 | && FullSaturation(outline_color); 135 | if (all_extreme_colors) 136 | canvas->SetPWMBits(1); 137 | 138 | const int x = x_orig; 139 | int y = y_orig; 140 | 141 | if (isatty(STDIN_FILENO)) { 142 | // Only give a message if we are interactive. If connected via pipe, be quiet 143 | printf("Enter lines. Full screen or empty line clears screen.\n" 144 | "Supports UTF-8. CTRL-D for exit.\n"); 145 | } 146 | 147 | canvas->Fill(flood_color.r, flood_color.g, flood_color.b); 148 | char line[1024]; 149 | while (fgets(line, sizeof(line), stdin)) { 150 | const size_t last = strlen(line); 151 | if (last > 0) line[last - 1] = '\0'; // remove newline. 152 | bool line_empty = strlen(line) == 0; 153 | if ((y + font.height() > canvas->height()) || line_empty) { 154 | canvas->Fill(flood_color.r, flood_color.g, flood_color.b); 155 | y = y_orig; 156 | } 157 | if (line_empty) 158 | continue; 159 | if (outline_font) { 160 | // The outline font, we need to write with a negative (-2) text-spacing, 161 | // as we want to have the same letter pitch as the regular text that 162 | // we then write on top. 163 | rgb_matrix::DrawText(canvas, *outline_font, 164 | x - 1, y + font.baseline(), 165 | outline_color, &bg_color, line, letter_spacing - 2); 166 | } 167 | // The regular text. Unless we already have filled the background with 168 | // the outline font, we also fill the background here. 169 | rgb_matrix::DrawText(canvas, font, x, y + font.baseline(), 170 | color, outline_font ? NULL : &bg_color, line, 171 | letter_spacing); 172 | y += font.height(); 173 | } 174 | 175 | // Finished. Shut down the RGB matrix. 176 | delete canvas; 177 | 178 | return 0; 179 | } 180 | -------------------------------------------------------------------------------- /adapter/passive-3/passive3-rpi-hub75-adapter-cache.lib: -------------------------------------------------------------------------------- 1 | EESchema-LIBRARY Version 2.3 2 | #encoding utf-8 3 | # 4 | <<<<<<< HEAD 5 | # CONN_01X01 6 | # 7 | DEF CONN_01X01 P 0 40 Y N 1 F N 8 | F0 "P" 0 100 50 H V C CNN 9 | F1 "CONN_01X01" 100 0 50 V V C CNN 10 | F2 "" 0 0 60 H V C CNN 11 | F3 "" 0 0 60 H V C CNN 12 | $FPLIST 13 | Pin_Header_Straight_1X01 14 | Pin_Header_Angled_1X01 15 | Socket_Strip_Straight_1X01 16 | Socket_Strip_Angled_1X01 17 | $ENDFPLIST 18 | DRAW 19 | S -50 5 10 -5 0 1 0 N 20 | S -50 50 50 -50 0 1 0 N 21 | X P1 1 -200 0 150 R 50 50 1 1 P 22 | ENDDRAW 23 | ENDDEF 24 | # 25 | ======= 26 | >>>>>>> d9ecea532afc911de3244f458589b6eadd051949 27 | # CONN_02X08 28 | # 29 | DEF CONN_02X08 P 0 1 Y N 1 F N 30 | F0 "P" 0 450 50 H V C CNN 31 | F1 "CONN_02X08" 0 0 50 V V C CNN 32 | F2 "" 0 -1200 60 H V C CNN 33 | F3 "" 0 -1200 60 H V C CNN 34 | $FPLIST 35 | Pin_Header_Straight_2X08 36 | Pin_Header_Angled_2X08 37 | Socket_Strip_Straight_2X08 38 | Socket_Strip_Angled_2X08 39 | $ENDFPLIST 40 | DRAW 41 | S -100 -345 -50 -355 0 1 0 N 42 | S -100 -245 -50 -255 0 1 0 N 43 | S -100 -145 -50 -155 0 1 0 N 44 | S -100 -45 -50 -55 0 1 0 N 45 | S -100 55 -50 45 0 1 0 N 46 | S -100 155 -50 145 0 1 0 N 47 | S -100 255 -50 245 0 1 0 N 48 | S -100 355 -50 345 0 1 0 N 49 | S -100 400 100 -400 0 1 0 N 50 | S 50 -345 100 -355 0 1 0 N 51 | S 50 -245 100 -255 0 1 0 N 52 | S 50 -145 100 -155 0 1 0 N 53 | S 50 -45 100 -55 0 1 0 N 54 | S 50 55 100 45 0 1 0 N 55 | S 50 155 100 145 0 1 0 N 56 | S 50 255 100 245 0 1 0 N 57 | S 50 355 100 345 0 1 0 N 58 | X P1 1 -250 350 150 R 50 50 1 1 P 59 | X P2 2 250 350 150 L 50 50 1 1 P 60 | X P3 3 -250 250 150 R 50 50 1 1 P 61 | X P4 4 250 250 150 L 50 50 1 1 P 62 | X P5 5 -250 150 150 R 50 50 1 1 P 63 | X P6 6 250 150 150 L 50 50 1 1 P 64 | X P7 7 -250 50 150 R 50 50 1 1 P 65 | X P8 8 250 50 150 L 50 50 1 1 P 66 | X P9 9 -250 -50 150 R 50 50 1 1 P 67 | X P10 10 250 -50 150 L 50 50 1 1 P 68 | X P11 11 -250 -150 150 R 50 50 1 1 P 69 | X P12 12 250 -150 150 L 50 50 1 1 P 70 | X P13 13 -250 -250 150 R 50 50 1 1 P 71 | X P14 14 250 -250 150 L 50 50 1 1 P 72 | X P15 15 -250 -350 150 R 50 50 1 1 P 73 | X P16 16 250 -350 150 L 50 50 1 1 P 74 | ENDDRAW 75 | ENDDEF 76 | # 77 | # CONN_02X20 78 | # 79 | DEF CONN_02X20 P 0 1 Y N 1 F N 80 | F0 "P" 0 1050 50 H V C CNN 81 | F1 "CONN_02X20" 0 0 50 V V C CNN 82 | F2 "" 0 -950 60 H V C CNN 83 | F3 "" 0 -950 60 H V C CNN 84 | $FPLIST 85 | Pin_Header_Straight_2X20 86 | Pin_Header_Angled_2X20 87 | Socket_Strip_Straight_2X20 88 | Socket_Strip_Angled_2X20 89 | $ENDFPLIST 90 | DRAW 91 | S -100 -945 -50 -955 0 1 0 N 92 | S -100 -845 -50 -855 0 1 0 N 93 | S -100 -745 -50 -755 0 1 0 N 94 | S -100 -645 -50 -655 0 1 0 N 95 | S -100 -545 -50 -555 0 1 0 N 96 | S -100 -445 -50 -455 0 1 0 N 97 | S -100 -345 -50 -355 0 1 0 N 98 | S -100 -245 -50 -255 0 1 0 N 99 | S -100 -145 -50 -155 0 1 0 N 100 | S -100 -45 -50 -55 0 1 0 N 101 | S -100 55 -50 45 0 1 0 N 102 | S -100 155 -50 145 0 1 0 N 103 | S -100 255 -50 245 0 1 0 N 104 | S -100 355 -50 345 0 1 0 N 105 | S -100 455 -50 445 0 1 0 N 106 | S -100 555 -50 545 0 1 0 N 107 | S -100 655 -50 645 0 1 0 N 108 | S -100 755 -50 745 0 1 0 N 109 | S -100 855 -50 845 0 1 0 N 110 | S -100 955 -50 945 0 1 0 N 111 | S -100 1000 100 -1000 0 1 0 N 112 | S 50 -945 100 -955 0 1 0 N 113 | S 50 -845 100 -855 0 1 0 N 114 | S 50 -745 100 -755 0 1 0 N 115 | S 50 -645 100 -655 0 1 0 N 116 | S 50 -545 100 -555 0 1 0 N 117 | S 50 -445 100 -455 0 1 0 N 118 | S 50 -345 100 -355 0 1 0 N 119 | S 50 -245 100 -255 0 1 0 N 120 | S 50 -145 100 -155 0 1 0 N 121 | S 50 -45 100 -55 0 1 0 N 122 | S 50 55 100 45 0 1 0 N 123 | S 50 155 100 145 0 1 0 N 124 | S 50 255 100 245 0 1 0 N 125 | S 50 355 100 345 0 1 0 N 126 | S 50 455 100 445 0 1 0 N 127 | S 50 555 100 545 0 1 0 N 128 | S 50 655 100 645 0 1 0 N 129 | S 50 755 100 745 0 1 0 N 130 | S 50 855 100 845 0 1 0 N 131 | S 50 955 100 945 0 1 0 N 132 | X P1 1 -250 950 150 R 50 50 1 1 P 133 | X P2 2 250 950 150 L 50 50 1 1 P 134 | X P3 3 -250 850 150 R 50 50 1 1 P 135 | X P4 4 250 850 150 L 50 50 1 1 P 136 | X P5 5 -250 750 150 R 50 50 1 1 P 137 | X P6 6 250 750 150 L 50 50 1 1 P 138 | X P7 7 -250 650 150 R 50 50 1 1 P 139 | X P8 8 250 650 150 L 50 50 1 1 P 140 | X P9 9 -250 550 150 R 50 50 1 1 P 141 | X P10 10 250 550 150 L 50 50 1 1 P 142 | X P20 20 250 50 150 L 50 50 1 1 P 143 | X P30 30 250 -450 150 L 50 50 1 1 P 144 | X P40 40 250 -950 150 L 50 50 1 1 P 145 | X P11 11 -250 450 150 R 50 50 1 1 P 146 | X P21 21 -250 -50 150 R 50 50 1 1 P 147 | X P31 31 -250 -550 150 R 50 50 1 1 P 148 | X P12 12 250 450 150 L 50 50 1 1 P 149 | X P22 22 250 -50 150 L 50 50 1 1 P 150 | X P32 32 250 -550 150 L 50 50 1 1 P 151 | X P13 13 -250 350 150 R 50 50 1 1 P 152 | X P23 23 -250 -150 150 R 50 50 1 1 P 153 | X P33 33 -250 -650 150 R 50 50 1 1 P 154 | X P14 14 250 350 150 L 50 50 1 1 P 155 | X P24 24 250 -150 150 L 50 50 1 1 P 156 | X P34 34 250 -650 150 L 50 50 1 1 P 157 | X P15 15 -250 250 150 R 50 50 1 1 P 158 | X P25 25 -250 -250 150 R 50 50 1 1 P 159 | X P35 35 -250 -750 150 R 50 50 1 1 P 160 | X P16 16 250 250 150 L 50 50 1 1 P 161 | X P26 26 250 -250 150 L 50 50 1 1 P 162 | X P36 36 250 -750 150 L 50 50 1 1 P 163 | X P17 17 -250 150 150 R 50 50 1 1 P 164 | X P27 27 -250 -350 150 R 50 50 1 1 P 165 | X P37 37 -250 -850 150 R 50 50 1 1 P 166 | X P18 18 250 150 150 L 50 50 1 1 P 167 | X P28 28 250 -350 150 L 50 50 1 1 P 168 | X P38 38 250 -850 150 L 50 50 1 1 P 169 | X P19 19 -250 50 150 R 50 50 1 1 P 170 | X P29 29 -250 -450 150 R 50 50 1 1 P 171 | X P39 39 -250 -950 150 R 50 50 1 1 P 172 | ENDDRAW 173 | ENDDEF 174 | # 175 | # GND 176 | # 177 | DEF GND #PWR 0 0 Y Y 1 F P 178 | F0 "#PWR" 0 -250 50 H I C CNN 179 | F1 "GND" 0 -150 50 H V C CNN 180 | F2 "" 0 0 60 H V C CNN 181 | F3 "" 0 0 60 H V C CNN 182 | DRAW 183 | P 6 0 1 0 0 0 0 -50 50 -50 0 -100 -50 -50 0 -50 N 184 | X GND 1 0 0 0 D 50 50 1 1 W N 185 | ENDDRAW 186 | ENDDEF 187 | # 188 | <<<<<<< HEAD 189 | ======= 190 | # PWR_FLAG 191 | # 192 | DEF PWR_FLAG #FLG 0 0 N N 1 F P 193 | F0 "#FLG" 0 95 50 H I C CNN 194 | F1 "PWR_FLAG" 0 180 50 H V C CNN 195 | F2 "" 0 0 60 H V C CNN 196 | F3 "" 0 0 60 H V C CNN 197 | DRAW 198 | X pwr 1 0 0 0 U 20 20 0 0 w 199 | P 6 0 1 0 0 0 0 50 -75 100 0 150 75 100 0 50 N 200 | ENDDRAW 201 | ENDDEF 202 | # 203 | >>>>>>> d9ecea532afc911de3244f458589b6eadd051949 204 | # VCC 205 | # 206 | DEF VCC #PWR 0 0 Y Y 1 F P 207 | F0 "#PWR" 0 -150 50 H I C CNN 208 | F1 "VCC" 0 150 50 H V C CNN 209 | F2 "" 0 0 60 H V C CNN 210 | F3 "" 0 0 60 H V C CNN 211 | DRAW 212 | C 0 75 25 0 1 0 N 213 | P 2 0 1 0 0 0 0 50 N 214 | X VCC 1 0 0 0 U 50 50 1 1 W N 215 | ENDDRAW 216 | ENDDEF 217 | # 218 | #End Library 219 | -------------------------------------------------------------------------------- /lib/graphics.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // Copyright (C) 2014 Henner Zeller 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation version 2. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see 15 | 16 | #include "graphics.h" 17 | #include "utf8-internal.h" 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | namespace rgb_matrix { 24 | bool SetImage(Canvas *c, int canvas_offset_x, int canvas_offset_y, 25 | const uint8_t *buffer, size_t size, 26 | const int width, const int height, 27 | bool is_bgr) { 28 | if (3 * width * height != (int)size) // Sanity check 29 | return false; 30 | 31 | int image_display_w = width; 32 | int image_display_h = height; 33 | 34 | size_t skip_start_row = 0; // Bytes to skip before each row 35 | if (canvas_offset_x < 0) { 36 | skip_start_row = -canvas_offset_x * 3; 37 | image_display_w += canvas_offset_x; 38 | if (image_display_w <= 0) return false; // Done. outside canvas. 39 | canvas_offset_x = 0; 40 | } 41 | if (canvas_offset_y < 0) { 42 | // Skip buffer to the first row we'll be showing 43 | buffer += 3 * width * -canvas_offset_y; 44 | image_display_h += canvas_offset_y; 45 | if (image_display_h <= 0) return false; // Done. outside canvas. 46 | canvas_offset_y = 0; 47 | } 48 | const int w = std::min(c->width(), canvas_offset_x + image_display_w); 49 | const int h = std::min(c->height(), canvas_offset_y + image_display_h); 50 | 51 | // Bytes to skip for wider than canvas image at the end of a row 52 | const size_t skip_end_row = (canvas_offset_x + image_display_w > w) 53 | ? (canvas_offset_x + image_display_w - w) * 3 54 | : 0; 55 | 56 | // Let's make this a combined skip per row and ajust where we start. 57 | const size_t next_row_skip = skip_start_row + skip_end_row; 58 | buffer += skip_start_row; 59 | 60 | if (is_bgr) { 61 | for (int y = canvas_offset_y; y < h; ++y) { 62 | for (int x = canvas_offset_x; x < w; ++x) { 63 | c->SetPixel(x, y, buffer[2], buffer[1], buffer[0]); 64 | buffer += 3; 65 | } 66 | buffer += next_row_skip; 67 | } 68 | } else { 69 | for (int y = canvas_offset_y; y < h; ++y) { 70 | for (int x = canvas_offset_x; x < w; ++x) { 71 | c->SetPixel(x, y, buffer[0], buffer[1], buffer[2]); 72 | buffer += 3; 73 | } 74 | buffer += next_row_skip; 75 | } 76 | } 77 | return true; 78 | } 79 | 80 | int DrawText(Canvas *c, const Font &font, 81 | int x, int y, const Color &color, 82 | const char *utf8_text) { 83 | return DrawText(c, font, x, y, color, NULL, utf8_text); 84 | } 85 | 86 | int DrawText(Canvas *c, const Font &font, 87 | int x, int y, const Color &color, const Color *background_color, 88 | const char *utf8_text, int extra_spacing) { 89 | const int start_x = x; 90 | while (*utf8_text) { 91 | const uint32_t cp = utf8_next_codepoint(utf8_text); 92 | x += font.DrawGlyph(c, x, y, color, background_color, cp); 93 | x += extra_spacing; 94 | } 95 | return x - start_x; 96 | } 97 | 98 | // There used to be a symbol without the optional extra_spacing parameter. Let's 99 | // define this here so that people linking against an old library will still 100 | // have their code usable. Now: 2017-06-04; can probably be removed in a couple 101 | // of months. 102 | int DrawText(Canvas *c, const Font &font, 103 | int x, int y, const Color &color, const Color *background_color, 104 | const char *utf8_text) { 105 | return DrawText(c, font, x, y, color, background_color, utf8_text, 0); 106 | } 107 | 108 | int VerticalDrawText(Canvas *c, const Font &font, int x, int y, 109 | const Color &color, const Color *background_color, 110 | const char *utf8_text, int extra_spacing) { 111 | const int start_y = y; 112 | while (*utf8_text) { 113 | const uint32_t cp = utf8_next_codepoint(utf8_text); 114 | font.DrawGlyph(c, x, y, color, background_color, cp); 115 | y += font.height() + extra_spacing; 116 | } 117 | return y - start_y; 118 | } 119 | 120 | void DrawCircle(Canvas *c, int x0, int y0, int radius, const Color &color) { 121 | int x = radius, y = 0; 122 | int radiusError = 1 - x; 123 | 124 | while (y <= x) { 125 | c->SetPixel(x + x0, y + y0, color.r, color.g, color.b); 126 | c->SetPixel(y + x0, x + y0, color.r, color.g, color.b); 127 | c->SetPixel(-x + x0, y + y0, color.r, color.g, color.b); 128 | c->SetPixel(-y + x0, x + y0, color.r, color.g, color.b); 129 | c->SetPixel(-x + x0, -y + y0, color.r, color.g, color.b); 130 | c->SetPixel(-y + x0, -x + y0, color.r, color.g, color.b); 131 | c->SetPixel(x + x0, -y + y0, color.r, color.g, color.b); 132 | c->SetPixel(y + x0, -x + y0, color.r, color.g, color.b); 133 | y++; 134 | if (radiusError<0){ 135 | radiusError += 2 * y + 1; 136 | } else { 137 | x--; 138 | radiusError+= 2 * (y - x + 1); 139 | } 140 | } 141 | } 142 | 143 | void DrawLine(Canvas *c, int x0, int y0, int x1, int y1, const Color &color) { 144 | int dy = y1 - y0, dx = x1 - x0, gradient, x, y, shift = 0x10; 145 | 146 | if (abs(dx) > abs(dy)) { 147 | // x variation is bigger than y variation 148 | if (x1 < x0) { 149 | std::swap(x0, x1); 150 | std::swap(y0, y1); 151 | } 152 | gradient = (dy << shift) / dx ; 153 | 154 | for (x = x0 , y = 0x8000 + (y0 << shift); x <= x1; ++x, y += gradient) { 155 | c->SetPixel(x, y >> shift, color.r, color.g, color.b); 156 | } 157 | } else if (dy != 0) { 158 | // y variation is bigger than x variation 159 | if (y1 < y0) { 160 | std::swap(x0, x1); 161 | std::swap(y0, y1); 162 | } 163 | gradient = (dx << shift) / dy; 164 | for (y = y0 , x = 0x8000 + (x0 << shift); y <= y1; ++y, x += gradient) { 165 | c->SetPixel(x >> shift, y, color.r, color.g, color.b); 166 | } 167 | } else { 168 | c->SetPixel(x0, y0, color.r, color.g, color.b); 169 | } 170 | } 171 | 172 | }//namespace 173 | -------------------------------------------------------------------------------- /examples-api-use/clock.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // Example of a clock. This is very similar to the text-example, 3 | // except that it shows the time :) 4 | // 5 | // This code is public domain 6 | // (but note, that the led-matrix library this depends on is GPL v2) 7 | 8 | #include "led-matrix.h" 9 | #include "graphics.h" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | using namespace rgb_matrix; 23 | 24 | volatile bool interrupt_received = false; 25 | static void InterruptHandler(int signo) { 26 | interrupt_received = true; 27 | } 28 | 29 | static int usage(const char *progname) { 30 | fprintf(stderr, "usage: %s [options]\n", progname); 31 | fprintf(stderr, "Reads text from stdin and displays it. " 32 | "Empty string: clear screen\n"); 33 | fprintf(stderr, "Options:\n"); 34 | fprintf(stderr, 35 | "\t-d : Default '%%H:%%M'. See strftime()\n" 36 | "\t Can be provided multiple times for multiple " 37 | "lines\n" 38 | "\t-f : Use given font.\n" 39 | "\t-x : X-Origin of displaying text (Default: 0)\n" 40 | "\t-y : Y-Origin of displaying text (Default: 0)\n" 41 | "\t-s : Extra spacing between lines when multiple -d given\n" 42 | "\t-S : Extra spacing between letters (Default: 0)\n" 43 | "\t-C : Color. Default 255,255,0\n" 44 | "\t-B : Background-Color. Default 0,0,0\n" 45 | "\t-O : Outline-Color, e.g. to increase contrast.\n" 46 | "\n" 47 | ); 48 | rgb_matrix::PrintMatrixFlags(stderr); 49 | return 1; 50 | } 51 | 52 | static bool parseColor(Color *c, const char *str) { 53 | return sscanf(str, "%hhu,%hhu,%hhu", &c->r, &c->g, &c->b) == 3; 54 | } 55 | 56 | static bool FullSaturation(const Color &c) { 57 | return (c.r == 0 || c.r == 255) 58 | && (c.g == 0 || c.g == 255) 59 | && (c.b == 0 || c.b == 255); 60 | } 61 | 62 | int main(int argc, char *argv[]) { 63 | RGBMatrix::Options matrix_options; 64 | rgb_matrix::RuntimeOptions runtime_opt; 65 | if (!rgb_matrix::ParseOptionsFromFlags(&argc, &argv, 66 | &matrix_options, &runtime_opt)) { 67 | return usage(argv[0]); 68 | } 69 | 70 | // We accept multiple format lines 71 | 72 | std::vector format_lines; 73 | Color color(255, 255, 0); 74 | Color bg_color(0, 0, 0); 75 | Color outline_color(0,0,0); 76 | bool with_outline = false; 77 | 78 | const char *bdf_font_file = NULL; 79 | int x_orig = 0; 80 | int y_orig = 0; 81 | int letter_spacing = 0; 82 | int line_spacing = 0; 83 | 84 | int opt; 85 | while ((opt = getopt(argc, argv, "x:y:f:C:B:O:s:S:d:")) != -1) { 86 | switch (opt) { 87 | case 'd': format_lines.push_back(optarg); break; 88 | case 'x': x_orig = atoi(optarg); break; 89 | case 'y': y_orig = atoi(optarg); break; 90 | case 'f': bdf_font_file = strdup(optarg); break; 91 | case 's': line_spacing = atoi(optarg); break; 92 | case 'S': letter_spacing = atoi(optarg); break; 93 | case 'C': 94 | if (!parseColor(&color, optarg)) { 95 | fprintf(stderr, "Invalid color spec: %s\n", optarg); 96 | return usage(argv[0]); 97 | } 98 | break; 99 | case 'B': 100 | if (!parseColor(&bg_color, optarg)) { 101 | fprintf(stderr, "Invalid background color spec: %s\n", optarg); 102 | return usage(argv[0]); 103 | } 104 | break; 105 | case 'O': 106 | if (!parseColor(&outline_color, optarg)) { 107 | fprintf(stderr, "Invalid outline color spec: %s\n", optarg); 108 | return usage(argv[0]); 109 | } 110 | with_outline = true; 111 | break; 112 | default: 113 | return usage(argv[0]); 114 | } 115 | } 116 | 117 | if (format_lines.empty()) { 118 | format_lines.push_back("%H:%M"); 119 | } 120 | 121 | if (bdf_font_file == NULL) { 122 | fprintf(stderr, "Need to specify BDF font-file with -f\n"); 123 | return usage(argv[0]); 124 | } 125 | 126 | /* 127 | * Load font. This needs to be a filename with a bdf bitmap font. 128 | */ 129 | rgb_matrix::Font font; 130 | if (!font.LoadFont(bdf_font_file)) { 131 | fprintf(stderr, "Couldn't load font '%s'\n", bdf_font_file); 132 | return 1; 133 | } 134 | rgb_matrix::Font *outline_font = NULL; 135 | if (with_outline) { 136 | outline_font = font.CreateOutlineFont(); 137 | } 138 | 139 | RGBMatrix *matrix = RGBMatrix::CreateFromOptions(matrix_options, runtime_opt); 140 | if (matrix == NULL) 141 | return 1; 142 | 143 | const bool all_extreme_colors = (matrix_options.brightness == 100) 144 | && FullSaturation(color) 145 | && FullSaturation(bg_color) 146 | && FullSaturation(outline_color); 147 | if (all_extreme_colors) 148 | matrix->SetPWMBits(1); 149 | 150 | const int x = x_orig; 151 | int y = y_orig; 152 | 153 | FrameCanvas *offscreen = matrix->CreateFrameCanvas(); 154 | 155 | char text_buffer[256]; 156 | struct timespec next_time; 157 | next_time.tv_sec = time(NULL); 158 | next_time.tv_nsec = 0; 159 | struct tm tm; 160 | 161 | signal(SIGTERM, InterruptHandler); 162 | signal(SIGINT, InterruptHandler); 163 | 164 | while (!interrupt_received) { 165 | offscreen->Fill(bg_color.r, bg_color.g, bg_color.b); 166 | localtime_r(&next_time.tv_sec, &tm); 167 | 168 | int line_offset = 0; 169 | for (const std::string &line : format_lines) { 170 | strftime(text_buffer, sizeof(text_buffer), line.c_str(), &tm); 171 | if (outline_font) { 172 | rgb_matrix::DrawText(offscreen, *outline_font, 173 | x - 1, y + font.baseline() + line_offset, 174 | outline_color, NULL, text_buffer, 175 | letter_spacing - 2); 176 | } 177 | rgb_matrix::DrawText(offscreen, font, 178 | x, y + font.baseline() + line_offset, 179 | color, NULL, text_buffer, 180 | letter_spacing); 181 | line_offset += font.height() + line_spacing; 182 | } 183 | 184 | // Wait until we're ready to show it. 185 | clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &next_time, NULL); 186 | 187 | // Atomic swap with double buffer 188 | offscreen = matrix->SwapOnVSync(offscreen); 189 | 190 | next_time.tv_sec += 1; 191 | } 192 | 193 | // Finished. Shut down the RGB matrix. 194 | delete matrix; 195 | 196 | write(STDOUT_FILENO, "\n", 1); // Create a fresh new line after ^C on screen 197 | return 0; 198 | } 199 | -------------------------------------------------------------------------------- /examples-api-use/pixel-mover.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // Small example how to use the input bits 3 | // 4 | // This code is public domain 5 | // (but note, that the led-matrix library this depends on is GPL v2) 6 | 7 | #include "led-matrix.h" 8 | #include "graphics.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | 21 | using namespace rgb_matrix; 22 | 23 | volatile bool interrupt_received = false; 24 | static void InterruptHandler(int signo) { 25 | interrupt_received = true; 26 | } 27 | 28 | static void InteractiveUseMessage() { 29 | fprintf(stderr, 30 | "Move around with common movement keysets \n" 31 | " W,A,S,D (gaming move style) or\n" 32 | " H,J,K,L (vi console move style)\n" 33 | " Quit with 'q' or \n" 34 | "The pixel position cannot be less than 0 or greater than the " 35 | "display height and width.\n"); 36 | } 37 | 38 | static int usage(const char *progname) { 39 | fprintf(stderr, "usage: %s [options]\n", progname); 40 | fprintf(stderr, "Display single pixel with any colour.\n"); 41 | InteractiveUseMessage(); 42 | fprintf(stderr, "Options:\n\n"); 43 | fprintf(stderr, 44 | "\t-C : Color at front of trail. Default 255,255,0\n" 45 | "\t-t : Length of trail behind dot (default:0)\n" 46 | "\t-c : Color at end of trail. Default 0,0,255\n\n" 47 | ); 48 | rgb_matrix::PrintMatrixFlags(stderr); 49 | return 1; 50 | } 51 | 52 | static bool parseColor(Color *c, const char *str) { 53 | return sscanf(str, "%hhu,%hhu,%hhu", &c->r, &c->g, &c->b) == 3; 54 | } 55 | 56 | static char getch() { 57 | static bool is_terminal = isatty(STDIN_FILENO); 58 | 59 | struct termios old; 60 | if (is_terminal) { 61 | if (tcgetattr(0, &old) < 0) 62 | perror("tcsetattr()"); 63 | 64 | // Set to unbuffered mode 65 | struct termios no_echo = old; 66 | no_echo.c_lflag &= ~ICANON; 67 | no_echo.c_lflag &= ~ECHO; 68 | no_echo.c_cc[VMIN] = 1; 69 | no_echo.c_cc[VTIME] = 0; 70 | if (tcsetattr(0, TCSANOW, &no_echo) < 0) 71 | perror("tcsetattr ICANON"); 72 | } 73 | 74 | char buf = 0; 75 | if (read(STDIN_FILENO, &buf, 1) < 0) 76 | perror ("read()"); 77 | 78 | if (is_terminal) { 79 | // Back to original terminal settings. 80 | if (tcsetattr(0, TCSADRAIN, &old) < 0) 81 | perror ("tcsetattr ~ICANON"); 82 | } 83 | 84 | return buf; 85 | } 86 | 87 | // Interpolation of color between head and tail of trail. 88 | static uint8_t quantize(float c) { 89 | return c < 0 ? 0 : c > 255 ? 255 : roundf(c); 90 | } 91 | static Color interpolate(const Color &c1, const Color &c2, float fraction) { 92 | float c2_fraction = 1 - fraction; 93 | return { quantize(c1.r * fraction + c2.r * c2_fraction), 94 | quantize(c1.g * fraction + c2.g * c2_fraction), 95 | quantize(c1.b * fraction + c2.b * c2_fraction)}; 96 | } 97 | 98 | int main(int argc, char *argv[]) { 99 | RGBMatrix::Options matrix_options; 100 | rgb_matrix::RuntimeOptions runtime_opt; 101 | if (!rgb_matrix::ParseOptionsFromFlags(&argc, &argv, 102 | &matrix_options, &runtime_opt)) { 103 | return usage(argv[0]); 104 | } 105 | 106 | Color front_color(255, 255, 0); 107 | Color back_color(0, 0, 255); 108 | 109 | int trail_len = 0; 110 | int opt; 111 | while ((opt = getopt(argc, argv, "C:c:t:")) != -1) { 112 | switch (opt) { 113 | case 't': 114 | trail_len = std::max(0, atoi(optarg)); 115 | break; 116 | case 'C': 117 | if (!parseColor(&front_color, optarg)) { 118 | fprintf(stderr, "Invalid color spec: %s\n", optarg); 119 | return usage(argv[0]); 120 | } 121 | break; 122 | case 'c': 123 | if (!parseColor(&back_color, optarg)) { 124 | fprintf(stderr, "Invalid color spec: %s\n", optarg); 125 | return usage(argv[0]); 126 | } 127 | break; 128 | default: 129 | return usage(argv[0]); 130 | } 131 | } 132 | 133 | RGBMatrix *matrix = RGBMatrix::CreateFromOptions(matrix_options, runtime_opt); 134 | if (matrix == NULL) 135 | return usage(argv[0]); 136 | 137 | rgb_matrix::FrameCanvas *canvas = matrix->CreateFrameCanvas(); 138 | signal(SIGTERM, InterruptHandler); 139 | signal(SIGINT, InterruptHandler); 140 | 141 | std::deque> trail; 142 | int x_pos = 0; 143 | int y_pos = 0; 144 | trail.push_back({x_pos, y_pos}); 145 | 146 | InteractiveUseMessage(); 147 | const bool output_is_terminal = isatty(STDOUT_FILENO); 148 | 149 | bool running = true; 150 | while (!interrupt_received && running) { 151 | canvas->Clear(); 152 | int distance_from_head = trail.size(); 153 | for (const auto &pos : trail) { // Draw from tail -> head 154 | distance_from_head--; 155 | Color c = interpolate(front_color, back_color, 156 | 1.0 - 1.0f * distance_from_head / trail.size()); 157 | canvas->SetPixel(pos.first, pos.second, c.r, c.g, c.b); 158 | } 159 | canvas = matrix->SwapOnVSync(canvas); 160 | 161 | printf("%sX,Y = %d,%d%s", 162 | output_is_terminal ? "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" : "", 163 | x_pos, y_pos, 164 | output_is_terminal ? " " : "\n"); 165 | fflush(stdout); 166 | 167 | const char c = tolower(getch()); 168 | switch (c) { 169 | case 'w': case 'k': // Up 170 | if (y_pos > 0) { 171 | y_pos--; 172 | trail.push_back({x_pos, y_pos}); 173 | } 174 | break; 175 | case 's': case 'j': // Down 176 | if (y_pos < canvas->height() - 1) { 177 | y_pos++; 178 | trail.push_back({x_pos, y_pos}); 179 | } 180 | break; 181 | case 'a': case 'h': // Left 182 | if (x_pos > 0) { 183 | x_pos--; 184 | trail.push_back({x_pos, y_pos}); 185 | } 186 | break; 187 | case 'd': case 'l': // Right 188 | if (x_pos < canvas->width() - 1) { 189 | x_pos++; 190 | trail.push_back({x_pos, y_pos}); 191 | } 192 | break; 193 | // All kinds of conditions which we use to exit 194 | case 0x1B: // Escape 195 | case 'q': // 'Q'uit 196 | case 0x04: // End of file 197 | case 0x00: // Other issue from getch() 198 | running = false; 199 | break; 200 | } 201 | 202 | while ((int)trail.size() > trail_len + 1) 203 | trail.pop_front(); // items on front are the oldest dots 204 | } 205 | 206 | // Finished. Shut down the RGB matrix. 207 | delete matrix; 208 | printf("\n"); 209 | return 0; 210 | } 211 | -------------------------------------------------------------------------------- /bindings/python/README.md: -------------------------------------------------------------------------------- 1 | Python bindings for RGB Matrix library 2 | ====================================== 3 | 4 | Building 5 | -------- 6 | 7 | If you have a different than the standard wiring (for instance if you have an 8 | Adafruit HAT), you can edit the [../../lib/Makefile](../../lib/Makefile#L26) first to choose 9 | the hardware in question (see below for setting it via command line argument). 10 | 11 | Then, in the root directory for the matrix library simply type: 12 | 13 | ### Python 2 14 | 15 | ```shell 16 | sudo apt-get update && sudo apt-get install python2.7-dev python-pillow -y 17 | make build-python 18 | sudo make install-python 19 | ``` 20 | 21 | ### Python 3 22 | You can also build for Python 3: 23 | 24 | ```shell 25 | sudo apt-get update && sudo apt-get install python3-dev python3-pillow -y 26 | make build-python PYTHON=$(command -v python3) 27 | sudo make install-python PYTHON=$(command -v python3) 28 | ``` 29 | 30 | ### PyPy 31 | The cython binding to PyPy seems to be somewhat working but extremely slow (20x 32 | slower even than the regular Python binding, 160x slower than C++), so this is 33 | not recommended. 34 | 35 | So Cython is not good together with PyPy which works best with a 36 | [CFFI](https://cffi.readthedocs.io/) binding. @Duality4Y did an experiment here 37 | https://github.com/Duality4Y/rgb-matrix-cffi which works well with PyPy and is 38 | about twice as fast as running Python3+cython (but Python3+cffi is slower than 39 | Python3+cython, so we can't just replace everything with cffi). 40 | 41 | Of course, it would be nice to have the fastest possible binding to all kinds 42 | of Python interpreters. If anyone wants to work on that, this would certainly 43 | be a welcome pull request. 44 | 45 | Performance 46 | ----------- 47 | The simplicity of scripting comes at a price: Python is slower than C++ of 48 | course. 49 | If you have to do a lot of pixel updates in your demo, this can be too slow 50 | depending on what you do. Here are some rough numbers for calling `SetPixel()` 51 | in a tight loop: 52 | 53 | * On a Pi-2 and Pi-3, a Python script will be about 1/8 of the speed compared 54 | to the corresponding C++ program (pushing ~0.43 Megapixels/s Python 55 | vs. ~3.5 Megapixels/s C++ on a Pi-3 for instance) 56 | * On a Pi-1/Pi Zero, the difference is even worse: 1/24 of the speed to the 57 | corresponding C++ program. Given that this Pi is already about 1/10 the 58 | speed of a Pi-3, this almost makes Python unusable on a Pi-1 59 | (~0.015 Megapixels/s Python vs. ~0.36 Megapixels/s C++) 60 | * Also interesting: Python3 is a little bit slower than Python2.7. 61 | So if you can, stick with Python2.7 for now. 62 | * The good news is, that this is due to overhead per function call. If you 63 | can do more per function call, then this is less problematic. For instance 64 | if you have an image to be displayed with `SetImage()`, that will much 65 | faster per pixel (internally this then copies the pixels natively). 66 | 67 | The ~0.015 Megapixels/s on a Pi-1 means that you can update a 32x32 matrix 68 | at most with ~15fps. If you have chained 5, then you barely reach 3fps. 69 | In a Pi-3, you get about 400fps update rate (85fps for 5-chain) with a Python 70 | program (while with C++, you can do the same thing with a comfortable 3500fps 71 | (700fps for 5)). Keep in mind that this is if all you do is just calling 72 | `SetPixel()`, it does not include any time of what you actually want to do in 73 | your demo - so anything in addition to that will drop your update rate. 74 | 75 | If you can prepare the animation you want to show, then you can either prepare 76 | images and then use the much faster call to `SetImage()`, or can fill 77 | entire offscreen-frames (create with `CreateFrameCanvas()`) and then 78 | swap with `SwapOnVSync()` (this is the fastest method). 79 | 80 | Using the library 81 | ----------------- 82 | 83 | Be aware of the fact that using the full performance of the RGBMatrix requires root privileges. 84 | Therefore you should run all you python scripts as using `sudo`. 85 | 86 | You may find examples in the [samples/](./samples) subdirectory. 87 | The examples all use the [samplebase.py](./samples/samplebase.py) that provides 88 | some basic capabilities to all example programs, such as command-line parsing: all 89 | sample-programs accept `--led-rows`, `--led-chain` and `--led-parallel` as 90 | command line options to adapt to your configuration 91 | 92 | ```bash 93 | cd samples 94 | sudo ./runtext.py --led-chain=4 95 | ``` 96 | 97 | To use different wiring without recompiling the library to change the default, 98 | you can use `--led-gpio-mapping` (or `-m`). For example, to use Adafruit HAT: 99 | ```bash 100 | sudo ./runtext.py --led-gpio-mapping=adafruit-hat 101 | ``` 102 | 103 | Here is a complete example showing how to write an image viewer: 104 | ```python 105 | #!/usr/bin/env python 106 | import time 107 | import sys 108 | 109 | from rgbmatrix import RGBMatrix, RGBMatrixOptions 110 | from PIL import Image 111 | 112 | if len(sys.argv) < 2: 113 | sys.exit("Require an image argument") 114 | else: 115 | image_file = sys.argv[1] 116 | 117 | image = Image.open(image_file) 118 | 119 | # Configuration for the matrix 120 | options = RGBMatrixOptions() 121 | options.rows = 32 122 | options.chain_length = 1 123 | options.parallel = 1 124 | options.hardware_mapping = 'regular' # If you have an Adafruit HAT: 'adafruit-hat' 125 | 126 | matrix = RGBMatrix(options = options) 127 | 128 | # Make image fit our screen. 129 | image.thumbnail((matrix.width, matrix.height), Image.ANTIALIAS) 130 | 131 | matrix.SetImage(image.convert('RGB')) 132 | 133 | try: 134 | print("Press CTRL-C to stop.") 135 | while True: 136 | time.sleep(100) 137 | except KeyboardInterrupt: 138 | sys.exit(0) 139 | ``` 140 | 141 | ## API 142 | 143 | The source of truth for what is available in the Python bindings may be found [here](rgbmatrix/core.pyx) (RGBMatrix, FrameCanvas, RGBMatrixOptions) and [here](rgbmatrix/graphics.pyx) (graphics). The underlying implementation's ground truth documentation may be found [here](../../include), specifically for [RGBMatrix, RGBMatrixOptions, and FrameCanvas](../../include/led-matrix.h), [Canvas](../../include/canvas.h) (base class of RGBMatrix), and [graphics methods and Font](../../include/graphics.h). 144 | 145 | ### User 146 | 147 | As noted in the Performance section above, Python programs not run as `root` will not be as high-performance as those run as `root`. When running as `root`, be aware of a potentially-unexpected behavior: to reduce the security attack surface, initializing an RGBMatrix as `root` changes the user from `root` to `daemon` (see [#1170](https://github.com/hzeller/rpi-rgb-led-matrix/issues/1170) for more information) by default. This means, for instance, that some file operations possible before initializing the RGBMatrix will not be possible after initialization. To disable this behavior, set `drop_privileges=False` in RGBMatrixOptions, but be aware that doing so will reduce security. 148 | -------------------------------------------------------------------------------- /lib/bdf-font.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // Copyright (C) 2014 Henner Zeller 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation version 2. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see 15 | 16 | // Some old g++ installations need this macro to be defined for PRIx64. 17 | #ifndef __STDC_FORMAT_MACROS 18 | # define __STDC_FORMAT_MACROS 19 | #endif 20 | #include 21 | 22 | #include "graphics.h" 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | // The little question-mark box "�" for unknown code. 29 | static const uint32_t kUnicodeReplacementCodepoint = 0xFFFD; 30 | 31 | // Bitmap for one row. This limits the number of available columns. 32 | // Make wider if running into trouble. 33 | typedef uint64_t rowbitmap_t; 34 | 35 | namespace rgb_matrix { 36 | struct Font::Glyph { 37 | int device_width, device_height; 38 | int width, height; 39 | int x_offset, y_offset; 40 | rowbitmap_t bitmap[0]; // contains 'height' elements. 41 | }; 42 | 43 | Font::Font() : font_height_(-1), base_line_(0) {} 44 | Font::~Font() { 45 | for (CodepointGlyphMap::iterator it = glyphs_.begin(); 46 | it != glyphs_.end(); ++it) { 47 | free(it->second); 48 | } 49 | } 50 | 51 | // TODO: that might not be working for all input files yet. 52 | bool Font::LoadFont(const char *path) { 53 | if (!path || !*path) return false; 54 | FILE *f = fopen(path, "r"); 55 | if (f == NULL) 56 | return false; 57 | uint32_t codepoint; 58 | char buffer[1024]; 59 | int dummy; 60 | Glyph tmp; 61 | Glyph *current_glyph = NULL; 62 | int row = 0; 63 | 64 | int bitmap_shift = 0; 65 | while (fgets(buffer, sizeof(buffer), f)) { 66 | if (sscanf(buffer, "FONTBOUNDINGBOX %d %d %d %d", 67 | &dummy, &font_height_, &dummy, &base_line_) == 4) { 68 | base_line_ += font_height_; 69 | } 70 | else if (sscanf(buffer, "ENCODING %ud", &codepoint) == 1) { 71 | // parsed. 72 | } 73 | else if (sscanf(buffer, "DWIDTH %d %d", &tmp.device_width, &tmp.device_height 74 | ) == 2) { 75 | // parsed. 76 | } 77 | else if (sscanf(buffer, "BBX %d %d %d %d", &tmp.width, &tmp.height, 78 | &tmp.x_offset, &tmp.y_offset) == 4) { 79 | current_glyph = (Glyph*) malloc(sizeof(Glyph) 80 | + tmp.height * sizeof(rowbitmap_t)); 81 | *current_glyph = tmp; 82 | // We only get number of bytes large enough holding our width. We want 83 | // it always left-aligned. 84 | bitmap_shift = 85 | 8 * (sizeof(rowbitmap_t) - ((current_glyph->width + 7) / 8)) 86 | - current_glyph->x_offset; 87 | row = -1; // let's not start yet, wait for BITMAP 88 | } 89 | else if (strncmp(buffer, "BITMAP", strlen("BITMAP")) == 0) { 90 | row = 0; 91 | } 92 | else if (current_glyph && row >= 0 && row < current_glyph->height 93 | && (sscanf(buffer, "%" PRIx64, ¤t_glyph->bitmap[row]) == 1)) { 94 | current_glyph->bitmap[row] <<= bitmap_shift; 95 | row++; 96 | } 97 | else if (strncmp(buffer, "ENDCHAR", strlen("ENDCHAR")) == 0) { 98 | if (current_glyph && row == current_glyph->height) { 99 | free(glyphs_[codepoint]); // just in case there was one. 100 | glyphs_[codepoint] = current_glyph; 101 | current_glyph = NULL; 102 | } 103 | } 104 | } 105 | fclose(f); 106 | return true; 107 | } 108 | 109 | Font *Font::CreateOutlineFont() const { 110 | Font *r = new Font(); 111 | const int kBorder = 1; 112 | r->font_height_ = font_height_ + 2*kBorder; 113 | r->base_line_ = base_line_ + kBorder; 114 | for (CodepointGlyphMap::const_iterator it = glyphs_.begin(); 115 | it != glyphs_.end(); ++it) { 116 | const Glyph *orig = it->second; 117 | const int height = orig->height + 2 * kBorder; 118 | const size_t alloc_size = sizeof(Glyph) + height * sizeof(rowbitmap_t); 119 | Glyph *const tmp_glyph = (Glyph*) calloc(1, alloc_size); 120 | tmp_glyph->width = orig->width + 2*kBorder; 121 | tmp_glyph->height = height; 122 | tmp_glyph->device_width = orig->device_width + 2*kBorder; 123 | tmp_glyph->device_height = height; 124 | tmp_glyph->y_offset = orig->y_offset - kBorder; 125 | // TODO: we don't really need bounding box, right ? 126 | const rowbitmap_t fill_pattern = 0b111; 127 | const rowbitmap_t start_mask = 0b010; 128 | // Fill the border 129 | for (int h = 0; h < orig->height; ++h) { 130 | rowbitmap_t fill = fill_pattern; 131 | rowbitmap_t orig_bitmap = orig->bitmap[h] >> kBorder; 132 | for (rowbitmap_t m = start_mask; m; m <<= 1, fill <<= 1) { 133 | if (orig_bitmap & m) { 134 | tmp_glyph->bitmap[h+kBorder-1] |= fill; 135 | tmp_glyph->bitmap[h+kBorder+0] |= fill; 136 | tmp_glyph->bitmap[h+kBorder+1] |= fill; 137 | } 138 | } 139 | } 140 | // Remove original font again. 141 | for (int h = 0; h < orig->height; ++h) { 142 | rowbitmap_t orig_bitmap = orig->bitmap[h] >> kBorder; 143 | tmp_glyph->bitmap[h+kBorder] &= ~orig_bitmap; 144 | } 145 | r->glyphs_[it->first] = tmp_glyph; 146 | } 147 | return r; 148 | } 149 | 150 | const Font::Glyph *Font::FindGlyph(uint32_t unicode_codepoint) const { 151 | CodepointGlyphMap::const_iterator found = glyphs_.find(unicode_codepoint); 152 | if (found == glyphs_.end()) 153 | return NULL; 154 | return found->second; 155 | } 156 | 157 | int Font::CharacterWidth(uint32_t unicode_codepoint) const { 158 | const Glyph *g = FindGlyph(unicode_codepoint); 159 | return g ? g->device_width : -1; 160 | } 161 | 162 | int Font::DrawGlyph(Canvas *c, int x_pos, int y_pos, 163 | const Color &color, const Color *bgcolor, 164 | uint32_t unicode_codepoint) const { 165 | const Glyph *g = FindGlyph(unicode_codepoint); 166 | if (g == NULL) g = FindGlyph(kUnicodeReplacementCodepoint); 167 | if (g == NULL) return 0; 168 | y_pos = y_pos - g->height - g->y_offset; 169 | for (int y = 0; y < g->height; ++y) { 170 | const rowbitmap_t row = g->bitmap[y]; 171 | rowbitmap_t x_mask = (1LL<<63); 172 | for (int x = 0; x < g->device_width; ++x, x_mask >>= 1) { 173 | if (row & x_mask) { 174 | c->SetPixel(x_pos + x, y_pos + y, color.r, color.g, color.b); 175 | } else if (bgcolor) { 176 | c->SetPixel(x_pos + x, y_pos + y, bgcolor->r, bgcolor->g, bgcolor->b); 177 | } 178 | } 179 | } 180 | return g->device_width; 181 | } 182 | 183 | int Font::DrawGlyph(Canvas *c, int x_pos, int y_pos, const Color &color, 184 | uint32_t unicode_codepoint) const { 185 | return DrawGlyph(c, x_pos, y_pos, color, NULL, unicode_codepoint); 186 | } 187 | 188 | } // namespace rgb_matrix 189 | -------------------------------------------------------------------------------- /lib/content-streamer.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | 3 | #include "content-streamer.h" 4 | #include "led-matrix.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | #include "gpio-bits.h" 16 | 17 | namespace rgb_matrix { 18 | 19 | // Pre-c++11 helper 20 | #define STATIC_ASSERT(msg, c) typedef int static_assert_##msg[(c) ? 1 : -1] 21 | 22 | namespace { 23 | // We write magic values as integers to automatically detect endian issues. 24 | // Streams are stored in little-endian. This is the ARM default (running 25 | // the Raspberry Pi, but also x86; so it is possible to create streams easily 26 | // on a different x86 Linux PC. 27 | static const uint32_t kFileMagicValue = 0xED0C5A48; 28 | struct FileHeader { 29 | uint32_t magic; // kFileMagicValue 30 | uint32_t buf_size; 31 | uint32_t width; 32 | uint32_t height; 33 | uint64_t future_use1; 34 | uint64_t is_wide_gpio : 1; 35 | uint64_t flags_future_use : 63; 36 | }; 37 | STATIC_ASSERT(file_header_size_changed, sizeof(FileHeader) == 32); 38 | 39 | static const uint32_t kFrameMagicValue = 0x12345678; 40 | struct FrameHeader { 41 | uint32_t magic; // kFrameMagic 42 | uint32_t size; 43 | uint32_t hold_time_us; // How long this frame lasts in usec. 44 | uint32_t future_use1; 45 | uint64_t future_use2; 46 | uint64_t future_use3; 47 | }; 48 | STATIC_ASSERT(file_header_size_changed, sizeof(FrameHeader) == 32); 49 | } 50 | 51 | FileStreamIO::FileStreamIO(int fd) : fd_(fd) { 52 | posix_fadvise(fd_, 0, 0, POSIX_FADV_SEQUENTIAL); 53 | } 54 | FileStreamIO::~FileStreamIO() { close(fd_); } 55 | 56 | void FileStreamIO::Rewind() { lseek(fd_, 0, SEEK_SET); } 57 | 58 | ssize_t FileStreamIO::Read(void *buf, const size_t count) { 59 | return read(fd_, buf, count); 60 | } 61 | 62 | ssize_t FileStreamIO::Append(const void *buf, const size_t count) { 63 | return write(fd_, buf, count); 64 | } 65 | 66 | void MemStreamIO::Rewind() { pos_ = 0; } 67 | ssize_t MemStreamIO::Read(void *buf, size_t count) { 68 | const size_t amount = std::min(count, buffer_.size() - pos_); 69 | memcpy(buf, buffer_.data() + pos_, amount); 70 | pos_ += amount; 71 | return amount; 72 | } 73 | ssize_t MemStreamIO::Append(const void *buf, size_t count) { 74 | buffer_.append((const char*)buf, count); 75 | return count; 76 | } 77 | 78 | // Read exactly count bytes including retries. Returns success. 79 | static bool FullRead(StreamIO *io, void *buf, const size_t count) { 80 | int remaining = count; 81 | char *char_buffer = (char*)buf; 82 | while (remaining > 0) { 83 | int r = io->Read(char_buffer, remaining); 84 | if (r < 0) return false; 85 | if (r == 0) break; // EOF. 86 | char_buffer += r; remaining -= r; 87 | } 88 | return remaining == 0; 89 | } 90 | 91 | // Write exactly count bytes including retries. Returns success. 92 | static bool FullAppend(StreamIO *io, const void *buf, const size_t count) { 93 | int remaining = count; 94 | const char *char_buffer = (const char*) buf; 95 | while (remaining > 0) { 96 | int w = io->Append(char_buffer, remaining); 97 | if (w < 0) return false; 98 | char_buffer += w; remaining -= w; 99 | } 100 | return remaining == 0; 101 | } 102 | 103 | StreamWriter::StreamWriter(StreamIO *io) : io_(io), header_written_(false) {} 104 | bool StreamWriter::Stream(const FrameCanvas &frame, uint32_t hold_time_us) { 105 | const char *data; 106 | size_t len; 107 | frame.Serialize(&data, &len); 108 | 109 | if (!header_written_) { 110 | WriteFileHeader(frame, len); 111 | } 112 | FrameHeader h = {}; 113 | h.magic = kFrameMagicValue; 114 | h.size = len; 115 | h.hold_time_us = hold_time_us; 116 | FullAppend(io_, &h, sizeof(h)); 117 | return FullAppend(io_, data, len) == (ssize_t)len; 118 | } 119 | 120 | void StreamWriter::WriteFileHeader(const FrameCanvas &frame, size_t len) { 121 | FileHeader header = {}; 122 | header.magic = kFileMagicValue; 123 | header.width = frame.width(); 124 | header.height = frame.height(); 125 | header.buf_size = len; 126 | header.is_wide_gpio = (sizeof(gpio_bits_t) > 4); 127 | FullAppend(io_, &header, sizeof(header)); 128 | header_written_ = true; 129 | } 130 | 131 | StreamReader::StreamReader(StreamIO *io) 132 | : io_(io), state_(STREAM_AT_BEGIN), header_frame_buffer_(NULL) { 133 | io_->Rewind(); 134 | } 135 | StreamReader::~StreamReader() { delete [] header_frame_buffer_; } 136 | 137 | void StreamReader::Rewind() { 138 | io_->Rewind(); 139 | state_ = STREAM_AT_BEGIN; 140 | } 141 | 142 | bool StreamReader::GetNext(FrameCanvas *frame, uint32_t* hold_time_us) { 143 | if (state_ == STREAM_AT_BEGIN && !ReadFileHeader(*frame)) return false; 144 | if (state_ != STREAM_READING) return false; 145 | 146 | // Read header and expected buffer size. 147 | if (!FullRead(io_, header_frame_buffer_, 148 | sizeof(FrameHeader) + frame_buf_size_)) { 149 | return false; 150 | } 151 | 152 | const FrameHeader &h = *reinterpret_cast(header_frame_buffer_); 153 | 154 | // TODO: we might allow for this to be a kFileMagicValue, to allow people 155 | // to just concatenate streams. In that case, we just would need to read 156 | // ahead past this header (both headers are designed to be same size) 157 | if (h.magic != kFrameMagicValue) { 158 | state_ = STREAM_ERROR; 159 | return false; 160 | } 161 | 162 | // In the future, we might allow larger buffers (audio?), but never smaller. 163 | // For now, we need to make sure to exactly match the size, as our assumption 164 | // above is that we can read the full header + frame in one FullRead(). 165 | if (h.size != frame_buf_size_) 166 | return false; 167 | 168 | if (hold_time_us) *hold_time_us = h.hold_time_us; 169 | return frame->Deserialize(header_frame_buffer_ + sizeof(FrameHeader), 170 | frame_buf_size_); 171 | } 172 | 173 | bool StreamReader::ReadFileHeader(const FrameCanvas &frame) { 174 | FileHeader header; 175 | FullRead(io_, &header, sizeof(header)); 176 | if (header.magic != kFileMagicValue) { 177 | state_ = STREAM_ERROR; 178 | return false; 179 | } 180 | if ((int)header.width != frame.width() 181 | || (int)header.height != frame.height()) { 182 | fprintf(stderr, "This stream is for %dx%d, can't play on %dx%d. " 183 | "Please use the same settings for record/replay\n", 184 | header.width, header.height, frame.width(), frame.height()); 185 | state_ = STREAM_ERROR; 186 | return false; 187 | } 188 | if (header.is_wide_gpio != (sizeof(gpio_bits_t) == 8)) { 189 | fprintf(stderr, "This stream was written with %s GPIO width support but " 190 | "this library is compiled with %d bit GPIO width (see " 191 | "ENABLE_WIDE_GPIO_COMPUTE_MODULE setting in lib/Makefile)\n", 192 | header.is_wide_gpio ? "wide (64-bit)" : "narrow (32-bit)", 193 | int(sizeof(gpio_bits_t) * 8)); 194 | state_ = STREAM_ERROR; 195 | return false; 196 | } 197 | state_ = STREAM_READING; 198 | frame_buf_size_ = header.buf_size; 199 | if (!header_frame_buffer_) 200 | header_frame_buffer_ = new char [ sizeof(FrameHeader) + header.buf_size ]; 201 | return true; 202 | } 203 | } // namespace rgb_matrix 204 | -------------------------------------------------------------------------------- /lib/framebuffer-internal.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // Copyright (C) 2013 Henner Zeller 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation version 2. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see 15 | #ifndef RPI_RGBMATRIX_FRAMEBUFFER_INTERNAL_H 16 | #define RPI_RGBMATRIX_FRAMEBUFFER_INTERNAL_H 17 | 18 | #include 19 | #include 20 | 21 | #include "hardware-mapping.h" 22 | 23 | namespace rgb_matrix { 24 | class GPIO; 25 | class PinPulser; 26 | namespace internal { 27 | class RowAddressSetter; 28 | 29 | // An opaque type used within the framebuffer that can be used 30 | // to copy between PixelMappers. 31 | struct PixelDesignator { 32 | PixelDesignator() : gpio_word(-1), r_bit(0), g_bit(0), b_bit(0), mask(~0u){} 33 | long gpio_word; 34 | gpio_bits_t r_bit; 35 | gpio_bits_t g_bit; 36 | gpio_bits_t b_bit; 37 | gpio_bits_t mask; 38 | }; 39 | 40 | class PixelDesignatorMap { 41 | public: 42 | PixelDesignatorMap(int width, int height, const PixelDesignator &fill_bits); 43 | ~PixelDesignatorMap(); 44 | 45 | // Get a writable version of the PixelDesignator. Outside Framebuffer used 46 | // by the RGBMatrix to re-assign mappings to new PixelDesignatorMappers. 47 | PixelDesignator *get(int x, int y); 48 | 49 | inline int width() const { return width_; } 50 | inline int height() const { return height_; } 51 | 52 | // All bits that set red/green/blue pixels; used for Fill(). 53 | const PixelDesignator &GetFillColorBits() { return fill_bits_; } 54 | 55 | private: 56 | const int width_; 57 | const int height_; 58 | const PixelDesignator fill_bits_; // Precalculated for fill. 59 | PixelDesignator *const buffer_; 60 | }; 61 | 62 | // Internal representation of the frame-buffer that as well can 63 | // write itself to GPIO. 64 | // Our internal memory layout mimicks as much as possible what needs to be 65 | // written out. 66 | class Framebuffer { 67 | public: 68 | // Maximum usable bitplanes. 69 | // 70 | // 11 bits seems to be a sweet spot in which we still get somewhat useful 71 | // refresh rate and have good color richness. This is the default setting 72 | // However, in low-light situations, we want to be able to scale down 73 | // brightness more, having more bits at the bottom. 74 | // TODO(hzeller): make the default 15 bit or so, but slide the use of 75 | // timing to lower bits if fewer bits requested to not affect the overall 76 | // refresh in that case. 77 | // This needs to be balanced to not create too agressive timing however. 78 | // To be explored in a separete commit. 79 | // 80 | // For now, if someone needs very low level of light, change this to 81 | // say 13 and recompile. Run with --led-pwm-bits=13. Also, consider 82 | // --led-pwm-dither-bits=2 to have the refresh rate not suffer too much. 83 | static constexpr int kBitPlanes = 11; 84 | static constexpr int kDefaultBitPlanes = 11; 85 | 86 | Framebuffer(int rows, int columns, int parallel, 87 | int scan_mode, 88 | const char* led_sequence, bool inverse_color, 89 | PixelDesignatorMap **mapper); 90 | ~Framebuffer(); 91 | 92 | // Initialize GPIO bits for output. Only call once. 93 | static void InitHardwareMapping(const char *named_hardware); 94 | static void InitGPIO(GPIO *io, int rows, int parallel, 95 | bool allow_hardware_pulsing, 96 | int pwm_lsb_nanoseconds, 97 | int dither_bits, 98 | int row_address_type); 99 | static void InitializePanels(GPIO *io, const char *panel_type, int columns); 100 | 101 | // Set PWM bits used for output. Default is 11, but if you only deal with 102 | // simple comic-colors, 1 might be sufficient. Lower require less CPU. 103 | // Returns boolean to signify if value was within range. 104 | bool SetPWMBits(uint8_t value); 105 | uint8_t pwmbits() { return pwm_bits_; } 106 | 107 | // Map brightness of output linearly to input with CIE1931 profile. 108 | void set_luminance_correct(bool on) { do_luminance_correct_ = on; } 109 | bool luminance_correct() const { return do_luminance_correct_; } 110 | 111 | // Set brightness in percent; range=1..100 112 | // This will only affect newly set pixels. 113 | void SetBrightness(uint8_t b) { 114 | brightness_ = (b <= 100 ? (b != 0 ? b : 1) : 100); 115 | } 116 | uint8_t brightness() { return brightness_; } 117 | 118 | void DumpToMatrix(GPIO *io, int pwm_bits_to_show); 119 | 120 | void Serialize(const char **data, size_t *len) const; 121 | bool Deserialize(const char *data, size_t len); 122 | void CopyFrom(const Framebuffer *other); 123 | 124 | // Canvas-inspired methods, but we're not implementing this interface to not 125 | // have an unnecessary vtable. 126 | int width() const; 127 | int height() const; 128 | void SetPixel(int x, int y, uint8_t red, uint8_t green, uint8_t blue); 129 | void Clear(); 130 | void Fill(uint8_t red, uint8_t green, uint8_t blue); 131 | 132 | private: 133 | static const struct HardwareMapping *hardware_mapping_; 134 | static RowAddressSetter *row_setter_; 135 | 136 | // This returns the gpio-bit for given color (one of 'R', 'G', 'B'). This is 137 | // returning the right value in case "led_sequence" is _not_ "RGB" 138 | static gpio_bits_t GetGpioFromLedSequence(char col, const char *led_sequence, 139 | gpio_bits_t default_r, 140 | gpio_bits_t default_g, 141 | gpio_bits_t default_b); 142 | 143 | void InitDefaultDesignator(int x, int y, const char *led_sequence, 144 | PixelDesignator *designator); 145 | inline void MapColors(uint8_t r, uint8_t g, uint8_t b, 146 | uint16_t *red, uint16_t *green, uint16_t *blue); 147 | const int rows_; // Number of rows. 16 or 32. 148 | const int parallel_; // Parallel rows of chains. 1 or 2. 149 | const int height_; // rows * parallel 150 | const int columns_; // Number of columns. Number of chained boards * 32. 151 | 152 | const int scan_mode_; 153 | const bool inverse_color_; 154 | 155 | uint8_t pwm_bits_; // PWM bits to display. 156 | bool do_luminance_correct_; 157 | uint8_t brightness_; 158 | 159 | const int double_rows_; 160 | const size_t buffer_size_; 161 | 162 | // The frame-buffer is organized in bitplanes. 163 | // Highest level (slowest to cycle through) are double rows. 164 | // For each double-row, we store pwm-bits columns of a bitplane. 165 | // Each bitplane-column is pre-filled IoBits, of which the colors are set. 166 | // Of course, that means that we store unrelated bits in the frame-buffer, 167 | // but it allows easy access in the critical section. 168 | gpio_bits_t *bitplane_buffer_; 169 | inline gpio_bits_t *ValueAt(int double_row, int column, int bit); 170 | 171 | PixelDesignatorMap **shared_mapper_; // Storage in RGBMatrix. 172 | }; 173 | } // namespace internal 174 | } // namespace rgb_matrix 175 | #endif // RPI_RGBMATRIX_FRAMEBUFFER_INTERNAL_H 176 | -------------------------------------------------------------------------------- /lib/Makefile: -------------------------------------------------------------------------------- 1 | # Creating RGB matrix library 2 | # When you link this library with your binary, you need to add -lrt -lm -lpthread 3 | # So 4 | # -lrgbmatrix 5 | ## 6 | OBJECTS=gpio.o led-matrix.o options-initialize.o framebuffer.o \ 7 | thread.o bdf-font.o graphics.o led-matrix-c.o hardware-mapping.o \ 8 | pixel-mapper.o multiplex-mappers.o \ 9 | content-streamer.o 10 | 11 | TARGET=librgbmatrix 12 | 13 | ### 14 | # After you change any of the following DEFINES, make sure to 'make' again. 15 | # 16 | # ########### NOTE ########### 17 | # all of these options can now can be set programmatically and 18 | # via command line flags as well. No real need to change them in the Makefile. 19 | # (So be prepared for these to be removed at some point) 20 | ### 21 | 22 | # There are several different pinouts for various breakout boards that uses 23 | # this library. If you are using the described pinout in the toplevel README.md 24 | # or the standard active-3 breakout board, then 'regular' is the one you'd like 25 | # to use. 26 | # 27 | # Adafruit also made a breakout board, if you want to use that, choose 28 | # 'adafruit-hat' 29 | # 30 | # These are the choices 31 | # regular # Following this project wiring and using these PCBs 32 | # adafruit-hat # If you have a RGB matrix HAT from Adafruit 33 | # adafruit-hat-pwm # If you have an Adafruit HAT with PWM hardware mod. 34 | # regular-pi1 # If you have an old Pi1 and regular didn't work. 35 | # classic # (deprecated) Classic Pi1/2/. Not used anymore. 36 | # classic-pi1 # (deprecated) Classic pinout on Rasperry Pi 1 37 | HARDWARE_DESC?=regular 38 | 39 | # If you see that your display is inverse, you might have a matrix variant 40 | # has uses inverse logic for the RGB bits. In that case: uncomment this. 41 | # Flag: --led-inverse 42 | #DEFINES+=-DINVERSE_RGB_DISPLAY_COLORS 43 | 44 | # For curiosity reasons and while tweaking values for LSB_PWM_NANOSECONDS, 45 | # uncomment to see refresh rate in terminal. 46 | # Flag: --led-show-refresh 47 | #DEFINES+=-DSHOW_REFRESH_RATE 48 | 49 | # For low refresh rates below 100Hz (e.g. a lot of panels), the eye will notice 50 | # some flicker. With this option enabled, the refreshed lines are interleaved, 51 | # so it is less noticeable. But looks less pleasant with fast eye movements. 52 | # Flag: --led-scan-mode=1 53 | #DEFINES+=-DRGB_SCAN_INTERLACED=1 54 | 55 | # The signal can be too fast for some LED panels, in particular with newer 56 | # (faster) Raspberry Pi 2s - in that case, the LED matrix only shows garbage. 57 | # This allows to slow down the GPIO for these cases. 58 | # 59 | # Set to 1 for RPi2 or RPi3 (default below), because they are typically 60 | # faster than the panels can digest. 61 | # 62 | # Set to 0 (or comment out) for RPi1, that are slow enough. 63 | # 64 | # Sometimes, you even have to give RGB_SLOWDOWN_GPIO=2 or even 3 for 65 | # particularly slow panels or bad signal cable situations. If that happens, you 66 | # typically should double check cables and add TTL level converter if you 67 | # haven't. 68 | # Flag: --led-slowdown-gpio 69 | #DEFINES+=-DRGB_SLOWDOWN_GPIO=1 70 | 71 | # This allows to change the base time-unit for the on-time in the lowest 72 | # significant bit in nanoseconds. 73 | # Higher numbers provide better quality (more accurate color, less ghosting), 74 | # but have a negative impact on the frame rate. 75 | # 76 | # For the same frame-rate, displays with higher multiplexing (e.g. 1:16 or 1:32) 77 | # require lower values. 78 | # 79 | # Good values for full-color display (PWM=11) are somewhere between 100 and 300. 80 | # 81 | # If you you use reduced bit color (e.g. PWM=1 for 8 colors like for text), 82 | # then higher values might be good to minimize ghosting (and you can afford 83 | # that, because lower PWM values result in higher frame-rates). 84 | # 85 | # How to decide ? Just leave the default if things are fine. If you see 86 | # ghosting in high-contrast applications (e.g. text), increase the value. 87 | # If you want to tweak, watch the framerate (-DSHOW_FRAME_RATE) while playing 88 | # with this number and the PWM values. 89 | # Flag: --led-pwm-lsb-nanoseconds 90 | #DEFINES+=-DLSB_PWM_NANOSECONDS=130 91 | 92 | # This is to debug problems with the hardware pulse generation. The PWM hardware 93 | # module is also used by Raspberry Pi sound system, so there might be 94 | # interference. Note, you typically don't want the hardware pulses disabled, as 95 | # the image will have visible brightness glitches; but for debugging, this is 96 | # a good choice. 97 | # Flag: --led-no-hardware-pulses 98 | #DEFINES+=-DDISABLE_HARDWARE_PULSES 99 | 100 | # This allows to fix the refresh rate to a particular refresh time in 101 | # microseconds. 102 | # 103 | # This can be used to mitigate some situations in which you have a rare 104 | # faint flicker, which can happen due to hardware events (network access) 105 | # or other situations such as other IO or heavy memory access by other 106 | # processes (all of which seem to break the isolation we request from the 107 | # kernel. You did set isolcpus=3 right ?) 108 | # You trade a slightly slower refresh rate and display brightness for less 109 | # visible flicker situations. 110 | # 111 | # For this to calibrate, run your program for a while with --led-show-refresh 112 | # and watch the line that shows the refresh time and the maximum microseconds 113 | # for a frame observed. The maximum number is updated whenever the frame 114 | # refresh take a little bit longer. So wait a while until that value doesn't 115 | # change anymore (at least a minute, so that you catch tasks that happen once 116 | # a minute). Some value might read e.g. 117 | # 204.6Hz max: 5133usec 118 | # Now take this maximum value you see there (here: 5133) and put in 119 | # this define (don't forget to remove the # in front). 120 | # 121 | # The refresh rate will now be adapted to always have this amount of time 122 | # between frames, so faster refreshes will be slowed down, but the occasional 123 | # delayed frame will fit into the time-window as well, thus reducing visible 124 | # brightness fluctuations. 125 | # 126 | # You can play with value a little and reduce until you find a good balance 127 | # between refresh rate (which is reduce the higher this value is) and 128 | # flicker suppression (which is better with higher values). 129 | # Flag: --led-limit-refresh 130 | #DEFINES+=-DFIXED_FRAME_MICROSECONDS=5000 131 | 132 | # Enable wide 64 bit GPIO offered with the compute module. 133 | # This will use more memory to internally represent the frame buffer, so 134 | # caches can't be utilized as much. 135 | # So only switch this on if you really use the compute module and use more 136 | # than 3 parallel chains. 137 | # (this is untested right now, waiting for hardware to arrive for testing) 138 | #DEFINES+=-DENABLE_WIDE_GPIO_COMPUTE_MODULE 139 | 140 | # ---- Pinout options for hardware variants; usually no change needed here ---- 141 | 142 | # Uncomment if you want to use the Adafruit HAT with stable PWM timings. 143 | # The newer version of this library allows for much more stable (less flicker) 144 | # output, but it does not work with the Adafruit HAT unless you do a 145 | # simple hardware hack on them: 146 | # connect GPIO 4 (old OE) with 18 (the new OE); there are 147 | # convenient solder holes labeled 4 and 18 on the Adafruit HAT, pretty 148 | # close together. 149 | # Then you can set the flag --led-gpio-mapping=adafruit-hat-pwm 150 | # .. or uncomment the following line. 151 | #HARDWARE_DESC=adafruit-hat-pwm 152 | 153 | # Typically, a Hub75 panel is split in two half displays, so that a 1:16 154 | # multiplexing actually multiplexes over two half displays and gives 32 lines. 155 | # There are some other displays out there that you might experiment with 156 | # that are internally wired to only have one sub-panel. In that case you might 157 | # want to try this define to get a more reasonable canvas mapping. 158 | # This option is typically _not_ needed, only use when you attempt to connect 159 | # some oddball old (typically one-colored) display, such as Hub12. 160 | #DEFINES+=-DONLY_SINGLE_SUB_PANEL 161 | 162 | # If someone gives additional values on the make commandline e.g. 163 | # make USER_DEFINES="-DSHOW_REFRESH_RATE" 164 | DEFINES+=$(USER_DEFINES) 165 | 166 | DEFINES+=-DDEFAULT_HARDWARE='"$(HARDWARE_DESC)"' 167 | INCDIR=../include 168 | CFLAGS=-W -Wall -Wextra -Wno-unused-parameter -O3 -g -fPIC $(DEFINES) 169 | CXXFLAGS=$(CFLAGS) -fno-exceptions -std=c++11 170 | 171 | all : $(TARGET).a $(TARGET).so.1 172 | 173 | $(TARGET).a : $(OBJECTS) 174 | $(AR) rcs $@ $^ 175 | 176 | $(TARGET).so.1 : $(OBJECTS) 177 | $(CXX) -shared -Wl,-soname,$@ -o $@ $^ -lpthread -lrt -lm -lpthread 178 | 179 | led-matrix.o: led-matrix.cc $(INCDIR)/led-matrix.h 180 | thread.o : thread.cc $(INCDIR)/thread.h 181 | framebuffer.o: framebuffer.cc framebuffer-internal.h 182 | graphics.o: graphics.cc utf8-internal.h 183 | 184 | %.o : %.cc compiler-flags 185 | $(CXX) -I$(INCDIR) $(CXXFLAGS) -c -o $@ $< 186 | 187 | %.o : %.c compiler-flags 188 | $(CC) -I$(INCDIR) $(CFLAGS) -c -o $@ $< 189 | 190 | clean: 191 | rm -f $(OBJECTS) $(TARGET).a $(TARGET).so.1 192 | 193 | compiler-flags: FORCE 194 | @echo '$(CXX) $(CXXFLAGS)' | cmp -s - $@ || echo '$(CXX) $(CXXFLAGS)' > $@ 195 | 196 | .PHONY: FORCE 197 | --------------------------------------------------------------------------------