Compare commits

..

11 Commits
master ... main

Author SHA1 Message Date
Nathan Wagner 3d3b3fbf7b new playAnimationUntil that stops animation when notify channel receives message
build lightwatch / build (push) Has started running Details
10 months ago
Nathan Wagner 6e27fc641b duh exporting
build lightwatch / build (push) Successful in 5m39s Details
1 year ago
Nathan Wagner 66d55c15de export field
build lightwatch / build (push) Successful in 5m41s Details
1 year ago
Nathan Wagner 7df4020194 removed submodule, adding rt_options
build lightwatch / build (push) Successful in 6m36s Details
1 year ago
nathan c413a2302d nailing it
build lightwatch / build (push) Successful in 11m29s Details
1 year ago
nathan ed18dd911a removing submodule
build lightwatch / build (push) Failing after 5m6s Details
1 year ago
nathan d539dc9119 Update 'Dockerfile'
build lightwatch / build (push) Failing after 1m1s Details
1 year ago
nathan c7028fb8ed Update '.gitea/workflows/build.yaml' 1 year ago
nathan 9a9d5ecf47 Update 'Dockerfile'
build lightwatch / build (push) Failing after 46s Details
1 year ago
nathan 2da123a095 Delete 'Jenkinsfile' 1 year ago
nathan 83505d780c actions!
rgbarmlib64/pipeline/head Build queued... Details
1 year ago

@ -0,0 +1,25 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/go
{
"name": "Go",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/go:1-1.21-bullseye",
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
}
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "go version",
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}

@ -0,0 +1,40 @@
name: build lightwatch
run-name: ${{ gitea.actor }} is testing out Gitea Actions 🚀
on:
push:
branches:
- main
paths-ignore:
- ".gitea/workflows/**"
jobs:
build:
runs-on: ubuntu-latest
container:
image: registry.local/catthehacker:act-latest-wagnerca
volumes:
- /var/run/user/1000/docker.sock:/var/run/docker.sock
- /var/run/secrets/kubernetes.io/serviceaccount:/var/run/secrets/kubernetes.io/serviceaccount
env:
KUBERNETES_PORT: "tcp://10.43.0.1:443"
KUBERNETES_PORT_443_TCP: "tcp://10.43.0.1:443"
KUBERNETES_PORT_443_TCP_ADDR: "10.43.0.1"
KUBERNETES_PORT_443_TCP_PORT: "443"
KUBERNETES_PORT_443_TCP_PROTO: "tcp"
KUBERNETES_SERVICE_HOST: "10.43.0.1"
KUBERNETES_SERVICE_PORT: "443"
KUBERNETES_SERVICE_PORT_HTTPS: "443"
BUILDKIT_HOST: "docker-container://wagner-buildkitd"
steps:
- uses: actions/checkout@v3
- run: docker buildx create --use --name=wagner-buildkitd --driver=remote
- run: docker buildx ls
- name: short-sha
id: short-sha
run: echo "short-sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- run: |
docker buildx build . \
-t registry.local/rgbarm64lib:${{ steps.short-sha.outputs.short-sha }} \
-t registry.local/rgbarm64lib:latest \
--platform=linux/arm64,linux/amd64 \
--push

@ -7,10 +7,10 @@ RUN apk add git make g++
RUN go mod tidy -e
RUN go mod vendor
RUN mkdir -p /usr/local/include
WORKDIR /usr/src/app/vendor/rpi-rgb-led-matrix/
RUN git submodule update --init
WORKDIR /usr/src/app/depends/rpi-rgb-led-matrix/
#RUN git submodule update --init
RUN make
WORKDIR /usr/src/app
RUN go install -v ./...
RUN cp /usr/src/app/vendor/rpi-rgb-led-matrix/include/* /usr/local/include
RUN cp /usr/src/app/vendor/rpi-rgb-led-matrix/lib/* /usr/local/lib
#RUN go install -v ./...
RUN cp /usr/src/app/depends/rpi-rgb-led-matrix/include/* /usr/local/include
RUN cp /usr/src/app/depends/rpi-rgb-led-matrix/lib/* /usr/local/lib

25
Jenkinsfile vendored

@ -1,25 +0,0 @@
pipeline {
agent any
stages {
/*stage('GitCheckOut') {
steps{
checkout([$class: 'GitSCM', branches: [[name: "${env.CHECKOUT}"]], extensions: [], userRemoteConfigs: [[credentialsId: 'gitea', url: "$GIT_URL"]]])
}
}*/
stage('buildPush') {
steps{
sh 'docker buildx rm jenkins-agent || echo "none there"'
sh '''#!/bin/bash
docker buildx create --bootstrap --name=jenkins-agent --driver=kubernetes --driver-opt=namespace=jenkinsagent --driver-opt=qemu.install=true --driver-opt=\\"nodeselector=kubernetes.io/arch=amd64\\" --driver-opt=\\"image=registry.local/buildkit-wagnerca:stable-3-rootless\\" --use
'''
sh 'docker buildx build . -t registry.local/rgbarm64lib:v0.0.$BUILD_NUMBER --push --platform=linux/arm64,linux/amd64'
sh 'docker buildx build . -t registry.local/rgbarm64lib:latest --push --platform=linux/arm64,linux/amd64'
sh 'docker buildx rm jenkins-agent'
}
}
}
}

@ -0,0 +1,15 @@
# Editor config file, see http://editorconfig.org/
root = true
[*]
charset = utf-8
indent_style = space
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.{h,cc}]
indent_size = 2
[Makefile]
indent_style = tab

@ -0,0 +1,6 @@
*.o
*.a
*~
*.so
.envrc
.direnv

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

@ -0,0 +1,35 @@
# This toplevel Makefile compiles the library in the lib subdirectory.
# If you want to see how to integrate the library in your own projects, check
# out the sub-directories examples-api-use/ and utils/
RGB_LIBDIR=./lib
RGB_LIBRARY_NAME=rgbmatrix
RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a
# Some language bindings.
PYTHON_LIB_DIR=bindings/python
CSHARP_LIB_DIR=bindings/c\#
all : $(RGB_LIBRARY)
$(RGB_LIBRARY): FORCE
$(MAKE) -C $(RGB_LIBDIR)
$(MAKE) -C examples-api-use
clean:
$(MAKE) -C lib clean
$(MAKE) -C utils clean
$(MAKE) -C examples-api-use clean
$(MAKE) -C $(PYTHON_LIB_DIR) clean
build-csharp:
$(MAKE) -C $(CSHARP_LIB_DIR) nuget
$(MAKE) -C $(CSHARP_LIB_DIR) build
build-python: $(RGB_LIBRARY)
$(MAKE) -C $(PYTHON_LIB_DIR) build
install-python: build-python
$(MAKE) -C $(PYTHON_LIB_DIR) install
FORCE:
.PHONY: FORCE

@ -0,0 +1,812 @@
Controlling RGB LED display with Raspberry Pi GPIO
==================================================
A library to control commonly available 64x64, 32x32 or 16x32 RGB LED panels
with the Raspberry Pi. Can support PWM up to 11Bit per channel, providing
true 24bpp color with CIE1931 profile.
Supports 3 chains with many panels each on a regular Pi.
On a Raspberry Pi 2 or 3, you can easily chain 12 panels in that chain
(so 36 panels total), but you can theoretically stretch that to up
to 96-ish panels (32 chain length) and still reach
around 100Hz refresh rate with full 24Bit color (theoretical - never tested
this; there might likely be timing problems with the panels that will creep
up then).
With fewer colors or so-called 'outdoor panels' you can control even more,
faster.
The LED-matrix library is (c) Henner Zeller <h.zeller@acm.org>, licensed with
[GNU General Public License Version 2.0](http://www.gnu.org/licenses/gpl-2.0.txt)
(which means, if you use it in a product somewhere, you need to make the
source and all your modifications available to the receiver of such product so
that they have the freedom to adapt and improve).
## Discourse discussion group
If you'd like help, please do not file a bug, use the discussion board instead:
https://rpi-rgb-led-matrix.discourse.group/
Overview
--------
The RGB LED matrix panels can be scored at [Sparkfun][sparkfun],
[AdaFruit][ada] or eBay and Aliexpress. If you are in China, I'd try to get
them directly from some manufacturer, Taobao or Alibaba.
The `RGBMatrix` class provided in `include/led-matrix.h` does what is needed
to control these. You can use this as a library in your own projects or just
use the demo binary provided here which provides some useful examples.
Check out [utils/ directory for some ready-made tools](./utils) to get started
using the library, or the [examples-api-use/](./examples-api-use) directory if
you want to get started programming your own utils.
All Raspberry Pi versions supported
-----------------------------------
This supports the old Raspberry Pi's Version 1 with 26 pin header and also the
B+ models, the Pi Zero, Raspberry Pi 2 and 3 with 40 pins, as well as the
Compute Modules which have 44 GPIOs.
The 26 pin models can drive one chain of RGB panels, the 40 pin models
**up to three** chains in parallel (each chain 12 or more panels long).
The Compute Module can drive **up to 6 chains in parallel**.
The Raspberry Pi 2 and 3 are faster and generally perferred to the older
models (and the Pi Zero). With the faster models, the panels sometimes
can't keep up with the speed; check out
this [troubleshooting section](#troubleshooting) what to do.
A lightweight, non-GUI, distribution such as [DietPi] is recommended.
[Raspbian Lite][raspbian-lite] is a bit easier to get started with and
is a good second choice.
Types of Displays
-----------------
There are various types of displays that come all with the same Hub75 connector.
They vary in the way the multiplexing is happening so this library supports
options to choose that.
All these are configured by flags (or, programmatically, in an [Options struct](include/led-matrix.h#L57)).
If you have a 64x32 display, you need to supply the flags
`--led-cols=64 --led-rows=32` for instance.
Depending on the Matrix, there are various configuration options that
you might need to set for it to work. See further below in the README for the
[detailed description of these](#changing-parameters-via-command-line-flags).
While the `--led-rows` and `--led-cols` can be derived from simply looking
at the panels, the other options might require some experimenting to find the
right setting if there is no description provided by the manufacturer of
the panel. Going through these options for experiments would typically not do
harm, so you're free to experiment to find your setting.
Flag                                | Description
:--------------- | :-----------------
`--led-cols` | Columns in the LED matrix, the 'width'.
`--led-rows` | Rows in the LED matrix, the 'height'.
`--led-multiplexing` | In particular bright outdoor panels with small multiplex ratios require this. Often an indicator: if there are fewer address lines than expected: ABC (instead of ABCD) for 32 high panels and ABCD (instead of ABCDE) for 64 high panels.
`--led-row-addr-type` | Adressing of rows; in particular panels with only AB address lines might indicate that this is needed.
`--led-panel-type` | Chipset of the panel. In particular if it doesn't light up at all, you might need to play with this option because it indicates that the panel requires a particular initialization sequence.
Panels can be chained by connecting the output of one panel to the input of
the next panel. You can chain quite a few together, but the refresh rate will
reduce with longer chains.
The 64x64 matrixes typically come in two kinds: with 5 address
lines (A, B, C, D, E), or (A, B); the latter needs a `--led-row-addr-type=1`
parameter. So-called 'outdoor panels' are typically brighter and allow for
faster refresh-rate for the same size, but do some multiplexing internally
of which there are a few types out there; they can be chosen with
the `--led-multiplexing` parameter.
There are some panels that have a different chip-set than the default HUB75.
These require some initialization sequence. The current supported types are
`--led-panel-type=FM6126A` and `--led-panel-type=FM6127`.
Generally, the higher scan-rate (e.g. 1:8), a.k.a. outdoor panels generally
allow faster refresh rate, but you might need to figure out the multiplexing
mapping if one of the three provided does not work.
Some 32x16 outdoor matrixes with 1:4 scan (e.g. [Qiangli Q10(1/4) or X10(1/4)](http://qiangliled.com/products-63.html))
have 4 address line (A, B, C, D). For such matrices is necessary to
use `--led-row-addr-type=2` parameter. Also the matrix Qiangli Q10(1/4)
have "Z"-stripe pixel mapping and in this case, you'd use two parameters
at the same time `--led-row-addr-type=2 --led-multiplexing=4`.
Let's do it
------------
This documentation is split into parts that help you through the process
1. <a href="wiring.md"><img src="img/wire-up-icon.png"></a>
[**Wire up the matrix to your Pi**](./wiring.md). This document describes
what goes where. You might also be interested
in [breakout boards](./adapter) for that.
If you have an [Adafruit HAT] or [Adafruit Bonnet], you can choose that with
a command line option [described below](#if-you-have-an-adafruit-hat-or-bonnet)
2. Run a demo. You find that in the
[examples-api-use/](./examples-api-use#running-some-demos) directory:
```
make -C examples-api-use
sudo examples-api-use/demo -D0
```
3. Use the utilities. The [utils](./utils) directory has some ready-made
useful utilities to show content. [Go there](./utils) to see how to
compile and run these.
4. Write your own programs using the Matrix in C++ or one of the
bindings such as Python or C#.
### Utilities
The [utils directory](./utils) is meant for ready utilities to show images or
animated gifs or videos. Read the [README](./utils/README.md) there for
instructions how to compile.
There are external projects that use this library and provide higher level
network protocols, such as the
* [FlaschenTaschen implementation](https://github.com/hzeller/flaschen-taschen)
(VLC can send videos to it natively)
* [PixelPusher implementation](https://github.com/hzeller/rpi-matrix-pixelpusher) (common in light art installations)
* [ZeroMQ-server](https://github.com/Knifa/led-matrix-zmq-server) to receive
content.
* Marc's [FastLED_RPIRGBPanel_GFX](http://marc.merlins.org/perso/arduino/post_2020-01-01_Running-FastLED_-Adafruit_GFX_-and-LEDMatrix-code-on-High-Resolution-RGBPanels-with-a-Raspberry-Pi.html) allows running arduino code on linux/rPi and display on bigger RGBPanel matrices than arduino chips, can.
### API
The library comes as an API that you can use for your own utilities and use-cases.
* The native library is a C++ library (see [include/](./include)).
Example uses you find in the [examples-api-use/](./examples-api-use)
directory.
* If you prefer to program in C, there is also a
[C API](./include/led-matrix-c.h).
* In the [python](./bindings/python) subdirectory, you find a Python API including a
couple of [examples](./bindings/python/samples) to get started.
* There are a couple of external bindings, such as
* [Nodejs binding] by Maxime Journaux
* [Nodejs/Typescript binding] by Alex Eden
* [Go binding] by Máximo Cuadros
* [Rust binding] by Vincent Pasquier
### Changing parameters via command-line flags
For the programs in this distribution and also automatically in your own
programs using this library, there are a lot of parameters provided as command
line flags, so that you don't have to re-compile your programs to tweak them.
Some might need to be changed for your particular kind of panel.
Here is a little run-down of what these command-line flags do and when you'd
like to change them.
First things first: if you have a different wiring than described in
[wiring](./wiring.md), for instance if you have an Adafruit HAT/Bonnet, you can
choose these here:
```
--led-gpio-mapping=<gpio-mapping>: Name of GPIO mapping used. Default "regular"
```
This can have values such as
- `--led-gpio-mapping=regular` The standard mapping of this library, described in the [wiring](./wiring.md) page.
- `--led-gpio-mapping=adafruit-hat` The Adafruit HAT/Bonnet, that uses this library or
- `--led-gpio-mapping=adafruit-hat-pwm` Adafruit HAT with the anti-flicker hardware mod [described below](#improving-flicker).
- `--led-gpio-mapping=compute-module` Additional 3 parallel chains can be used with the Compute Module.
Learn more about the mappings in the [wiring documentation](wiring.md#alternative-hardware-mappings).
#### GPIO speed
```
--led-slowdown-gpio=<0..4>: Slowdown GPIO. Needed for faster Pis and/or slower panels (Default: 1).
```
The Raspberry Pi starting with Pi2 are putting out data too fast for almost
all LED panels I have seen. In this case, you want to slow down writing to
GPIO. Zero for this parameter means 'no slowdown'.
The default 1 (one) typically works fine, but often you have to even go further
by setting it to 2 (two). If you have a Raspberry Pi with a slower processor
(Model A, A+, B+, Zero), then a value of 0 (zero) might work and is desirable.
A Raspberry Pi 3 or Pi4 might even need higher values for the panels to be
happy.
#### Panel Connection
The next most important flags describe the type and number of displays connected
```
--led-rows=<rows> : Panel rows. Typically 8, 16, 32 or 64. (Default: 32).
--led-cols=<cols> : Panel columns. Typically 32 or 64. (Default: 32).
--led-chain=<chained> : Number of daisy-chained panels. (Default: 1).
--led-parallel=<parallel>: For A/B+ models or RPi2,3b: parallel chains. range=1..3 (Default: 1, 6 for Compute Module).
```
These are the most important ones: here you choose how many panels you have
connected and how many rows are in each panel. Panels can be chained (each panel
has an input and output connector, see the
[wiring documentation](wiring.md#chains)) -- the `--led-chain` flag tells the
library how many panels are chained together. The newer Raspberry Pi's allow
to connect multiple chains in parallel, the `--led-parallel` flag tells it how
many there are.
This illustrates what each of these parameters mean:
<a href="wiring.md#chaining-parallel-chains-and-coordinate-system"><img src="img/coordinates.png"></a>
##### Panel Type
Typically, panels should just work out of the box, but some panels use a
different chip-set that requires some initialization. If you don't see any
output on your panel, try setting:
```
--led-panel-type=FM6126A
```
Some panels have the FM6127 chip, which is also an option.
##### Multiplexing
If you have some 'outdoor' panels or panels with different multiplexing,
the following will be useful:
```
--led-multiplexing=<0..17> : Mux type: 0=direct; 1=Stripe; 2=Checkered...
```
The outdoor panels have different multiplexing which allows them to be faster
and brighter, but by default their output looks jumbled up.
They require some pixel-mapping of which there are a few
types you can try and hopefully one of them works for your panel; The default=0
is no mapping ('standard' panels), while 1, 2, ... are different mappings
to try with. If your panel has a different mapping, you find everything you
need to implement one in [lib/multiplex-mappers.cc](lib/multiplex-mappers.cc).
Please send a pull request if you encounter a panel for which you needed to
implement a new mapping.
Note that you have to set the `--led-rows` and `--led-cols` to the rows and
columns that are physically on each chained panel so that the multiplexing
option can work properly. For instance a `32x16` panel with `1:4` multiplexing
would be controlled with `--led-rows=16 --led-cols=32 --led-multiplexing=1` (or
whatever multiplexing type your panel is, so it can also be `--led-multiplexing=2` ...).
For `64x32` panels with `1:8` multiplexing, this would typically be
`--led-rows=32 --led-cols=64 --led-multiplexing=1`;
however, there are some panels that internally behave like
two chained panels, so then you'd use
`--led-rows=32 --led-cols=32 --led-chain=2 --led-multiplexing=1`;
```
--led-row-addr-type=<0..4>: 0 = default; 1 = AB-addressed panels; 2 = direct row select; 3 = ABC-addressed panels; 4 = ABC Shift + DE direct (Default: 0).
```
This option is useful for certain 64x64 or 32x16 panels. For 64x64 panels,
that only have an `A` and `B` address line, you'd use `--led-row-addr-type=1`.
This is only tested with one panel so far, so if it doesn't work for you,
please send a pull request.
For 32x16 outdoor panels, that have have 4 address line (A, B, C, D), it is
necessary to use `--led-row-addr-type=2`.
#### Panel Arrangement
```
--led-pixel-mapper : Semicolon-separated list of pixel-mappers to arrange pixels.
```
Optional params after a colon e.g. "U-mapper;Rotate:90"
Available | Parameter after colon| Example
----------|----------------------|----------
Mirror | `H` or `V` for horizontal/vertical mirror. | `Mirror:H`
Rotate | Degrees. | `Rotate:90`
U-mapper | -
Mapping the logical layout of your boards to your physical arrangement. See
more in [Remapping coordinates](./examples-api-use#remapping-coordinates).
#### Misc Options
```
--led-brightness=<percent>: Brightness in percent (Default: 100).
```
Self explanatory.
```
--led-pwm-bits=<1..11> : PWM bits (Default: 11).
```
The LEDs can only be switched on or off, so the shaded brightness perception
is achieved via PWM (Pulse Width Modulation). In order to get a good 8 Bit
per color resolution (24Bit RGB), the 11 bits default per color are good
(why ? Because our eyes are actually perceiving brightness logarithmically, so
we need a lot more physical resolution to get 24Bit sRGB).
With this flag, you can change how many bits it should use for this; lowering it
means the lower bits (=more subtle color nuances) are omitted.
Typically you might be mostly interested in the extremes: 1 Bit for situations
that only require 8 colors (e.g. for high contrast text displays) or 11 Bit
for everything else (e.g. showing images or videos). Why would you bother at all ?
Lower number of bits use slightly less CPU and result in a higher refresh rate.
```
--led-show-refresh : Show refresh rate.
```
This shows the current refresh rate of the LED panel, the time to refresh
a full picture. Typically, you want this number to be pretty high, because the
human eye is pretty sensitive to flicker. Depending on the settings, the
refresh rate with this library are typically in the hundreds of Hertz but
can drop low with very long chains. Humans have different levels of perceiving
flicker - some are fine with 100Hz refresh, others need 250Hz.
So if you are curious, this gives you the number (shown on the terminal).
The refresh rate depends on a lot of factors, from `--led-rows` and `--led-chain`
to `--led-pwm-bits`, `--led-pwm-lsb-nanoseconds` and `--led-pwm-dither-bits`.
If you are tweaking these parameters, showing the refresh rate can be a
useful tool.
```
--led-limit-refresh=<Hz> : Limit refresh rate to this frequency in Hz. Useful to keep a
constant refresh rate on loaded system. 0=no limit. Default: 0
```
This allows to limit the refresh rate to a particular frequency to approach
a fixed refresh rate.
This can be used to mitigate some situations in which you have a faint flicker,
which can happen due to hardware events (network access)
or other situations such as other IO or heavy memory access by other
processes. Also when you see wildly changing refresh frequencies with
`--led-show-refresh`.
You trade a slightly slower refresh rate and display brightness for less
visible flicker situations.
For this to calibrate, run your program for a while with --led-show-refresh
and watch the line that shows the current refresh rate and minimum refresh
rate observed. So wait a while until that value doesn't
change anymore (e.g. a minute, so that you catch tasks that happen once
a minute, such as ntp updated).
Use this as a guidance what value to choose with `--led-limit-refresh`.
The refresh rate will now be adapted to always reach this value
between frames, so faster refreshes will be slowed down, but the occasional
delayed frame will fit into the time-window as well, thus reducing visible
brightness fluctuations.
You can play with value a little and reduce until you find a good balance
between refresh rate and flicker suppression.
Use this also if you want to have a stable baseline refresh rate when using
the vsync-multiple flag `-V` in the [led-image-viewer] or
[video-viewer] utility programs.
```
--led-scan-mode=<0..1> : 0 = progressive; 1 = interlaced (Default: 0).
```
This switches from progressive scan and interlaced scan. The latter might
look be a little nicer when you have a very low refresh rate, but typically
it is more annoying because of the comb-effect (remember 80ies TV ?).
```
--led-pwm-lsb-nanoseconds : PWM Nanoseconds for LSB (Default: 130)
```
This allows to change the base time-unit for the on-time in the lowest
significant bit in nanoseconds.
Lower values will allow higher frame-rate, but will also negatively impact
qualty in some panels (less accurate color or more ghosting).
Good values for full-color display (PWM=11) are somewhere between 100 and 300.
If you you use reduced bit color (e.g. PWM=1) and have sharp contrast
applications, then higher values might be good to minimize ghosting.
How to decide ? Just leave the default if things are fine. But some panels have
trouble with sharp contrasts and short pulses that results
in ghosting. It is particularly apparent in situations such as bright text
on black background. In these cases increase the value until you don't see
this ghosting anymore.
The following example shows how this might look like:
Ghosting with low --led-pwm-lsb-nanoseconds | No ghosting after tweaking
---------------------------------------------|------------------------------
![](img/text-ghosting.jpg) |![](img/text-no-ghosting.jpg)
If you tweak this value, watch the framerate (`--led-show-refresh`) while playing
with this number.
```
--led-pwm-dither-bits : Time dithering of lower bits (Default: 0)
```
The lower bits can be time dithered, i.e. their brightness contribution is
achieved by only showing them some frames (this is possible,
because the PWM is implemented as binary code modulation).
This will allow higher refresh rate (or same refresh rate with increased
`--led-pwm-lsb-nanoseconds`).
The disadvantage could be slightly lower brightness, in particular for longer
chains, and higher CPU use.
CPU use is not of concern for Rasbperry Pi 2 or 3 (as we run on a dedicated
core anyway) but proably for Raspberry Pi 1 or Pi Zero.
Default: no dithering; if you have a Pi 3 and struggle with low frame-rate due
to high multiplexing panels (1:16 or 1:32) or long chains, it might be
worthwhile to try.
```
--led-no-hardware-pulse : Don't use hardware pin-pulse generation.
```
This library uses a hardware subsystem that also is used by the sound. You can't
use them together. If your panel does not work, this might be a good start
to debug if it has something to do with the sound subsystem (see Troubleshooting
section). This is really only recommended for debugging; typically you actually
want the hardware pulses as it results in a much more stable picture.
<a name="no-drop-priv"/>
```
--led-no-drop-privs : Don't drop privileges from 'root' after initializing the hardware.
```
You need to start programs as root as it needs to access some low-level hardware
at initialization time. After that, it is typically not desirable to stay in this
role, so the library then drops the privileges.
This flag allows to switch off this behavior, so that you stay root.
Not recommended unless you have a specific reason for it (e.g. you need root
to access other hardware or you do the privilege dropping yourself).
```
--led-daemon : Make the process run in the background as daemon.
```
If this is set, the program puts itself into the background (running
as 'daemon').
You might want this if started from an init script at boot-time.
```
--led-inverse : Switch if your matrix has inverse colors on.
--led-rgb-sequence : Switch if your matrix has led colors swapped (Default: "RGB")
```
These are if you have a different kind of LED panel in which the logic of the
color bits is reversed (`--led-inverse`) or where the Red, Green and Blue LEDs
are mixed up (`--led-rgb-sequence`). You know it when you see it.
Troubleshooting
---------------
Here are some tips in case things don't work as expected.
### Use minimal Raspbian distribution
In general, run a minimal configuration on your Pi.
* Do not use a graphical user interface (Even though the
Raspberry Pi foundation makes you believe that you can do that: don't.
Using a Pi with a GUI is a frustratingly slow use of an otherwise
perfectly good embedded device.).
Always operate your Raspberry Pi [headless].
* Switch off on-board sound (`dtparam=audio=off` in `/boot/config.txt`).
External USB sound adapters work, and are much better quality anyway,
so that is recommended if you happen to need sound. The on-board sound
uses a timing circuit that the RGB-Matrix needs (it seems in some
distributions, such as arch-linux, this is not enough and you need
to explicitly blacklist the snd_bcm2835 module).
* Don't run anything that messes in parallel with the GPIO pins, e.g.
PiGPIO library/daemon or devices that use the i2c or 1-wire interface if
they are on the same pins you need for the panel.
* I have also seen reports that on some Pis, the one-wire protocol is
enabled (w1-gpio). This will also not work (disable by removing
`dtoverlay=w1-gpio` in `/boot/config.txt`; or using `raspi-config`,
Interface Options -> 1-Wire)
* If you see some regular flickering, make sure that there is no other
process running on the system that could cause that. For instance, it is
known that merely running `top` creates a faint flicker every second it
updates. Or a regular ntp run can also cause flicker once a minute
(switch off with `sudo timedatectl set-ntp false`). Maybe instead you
might want to run ntp at system start-up but then not regularly updating.
There might be other things running regularly you don't need;
consider a `sudo systemctl stop cron` for instance.
To address some irregular flicker, consider the
[`--led-limit-refresh`](#misc-options) option.
* There are probably other processes that are running that you don't need
and remove them; I usually remove right away stuff I really don't need e.g.
```
sudo apt-get remove bluez bluez-firmware pi-bluetooth triggerhappy pigpio
```
Take a close look at your systemd (`systemctl`) and see if there are other
things running you don't need. If you have seen packages in standard
Raspbians that interfere with the matrix code, let me know to include it
here.
In general: This is why starting with a minimal installation is a good
idea: there is simply less cruft that you have to disable.
* It seems that more recent version of Raspbian Lite result in some faint
brightness fluctuations of the displays and it is not quite clear why (see
issue [#483](https://github.com/hzeller/rpi-rgb-led-matrix/issues/483)).
If you are a Kernel person and can help figuring out what is
happening that would be very appreciated. Also, you might know a minimal
Linux distribution that is more suited for near realtime applications ?
The default install of **[Raspbian Lite][raspbian-lite]** or **[DietPi]**
seem to be good starting points, as they have a reasonably minimal
configuration to begin with. Raspbian Lite is not as lite anymore
as it used to be; I prefer DietPi these days.
### Bad interaction with Sound
If sound is enabled on your Pi, this will not work together with the LED matrix,
as both need the same internal hardware sub-system (a first test to see if you
are affected is to run the progrem with `--led-no-hardware-pulse` and see if
things work fine then).
If you run `lsmod` and see the `snd_bcm2835` module, this could be causing trouble.
(The library actually exits if it finds this module to be loaded).
In that case, you should create a kernel module blacklist file like the
following on your system and update your initramfs:
```
cat <<EOF | sudo tee /etc/modprobe.d/blacklist-rgb-matrix.conf
blacklist snd_bcm2835
EOF
sudo update-initramfs -u
```
Reboot and confirm that the module is not loaded.
### I have followed some tutorial on the Internet and it doesn't work
Well, if you use this library, please read the documentation provided _here_,
not on some other website. Most important for you to get started
is the [wiring guide](./wiring.md). There are some tutorials floating around
that refer to a very old version of this library.
### I have a Pi1 Revision1 and top part of Panel doesn't show green
Use `--led-gpio-mapping=regular-pi1`
### Logic level voltage not sufficient
Some panels don't interpret the 3.3V logic level well, or the RPi output drivers
have trouble driving longer cables, in particular with
faster Raspberry Pis Version 2. This results in artifacts like randomly
showing up pixels, color fringes, or parts of the panel showing 'static'.
If you encounter this, try these things
- Make sure to have as short as possible flat-cables connecting your
Raspberry Pi with the LED panel.
- In particular if the chips close to the input of the LED panel
read 74HC245 instead of 74HCT245 or 74AHCT245, then this board will not
work properly with 3.3V inputs coming from the Pi.
Use an [adapter board](./adapter/active-3) with a bus-driver that acts as
level shifter between 3.3V and 5V.
(In any case, it is always a good idea to use the level shifters).
- A temporary hack to make HC245 inputs work with the 3.3V levels is to
supply only like 4V to the LED panel. But the colors will be off, so not
really useable as long-term solution.
- If you can't implement the above things, or still have problems, you can
slow down the GPIO writing a bit. This will of course reduce the
frame-rate, so it comes at a cost.
For GPIO slow-down, add the flag `--led-slowdown-gpio=2` to the invocation of
the binary.
If you have an Adafruit HAT or Bonnet
---------------------------
Generally, if you want to connect RGB panels via an adapter instead of
hand-wiring, I suggest to build one of the adapters whose open-hardware
files you find in the [adapter/](./adapter) subdirectory. It is a fun solder
exercise with large surface mount components.
However, Adafruit [offers an adapter][adafruit-hat] which is already ready-made,
but it only allows for a single chain. If the
ready-made vs. single-chain tradeoff is worthwhile, then you might go for that
(I am not affiliated with Adafruit).
### Switch the Pinout
The Adafruit HAT/Bonnet uses this library but a modified pinout to support other
features on the HAT. You can choose the Adafruit pinout with a command line
flag.
Just pass the option `--led-gpio-mapping=adafruit-hat`. This works on the C++
and Python examples.
### Improving flicker
To improve flicker, we need to do a little hardware modification,
but it is very simple: solder a wire between GPIO 4 and 18 as shown in the
following picture (click to enlarge):
<a href="img/adafruit-mod.jpg"><img src="img/adafruit-mod.jpg" height="80px"></a>
Then, start your programs with `--led-gpio-mapping=adafruit-hat-pwm`.
Now you should have less visible flicker. This essentially
switches on the hardware pulses feature for the Adafruit HAT/Bonnet.
### 64x64 with E-line on Adafruit HAT/Bonnet
There are LED panels that have 64x64 LEDs packed, but they need 5 address lines,
which is 1:32 multiplexing (they have an `E` address-line). The first generation
of the Adafruit HAT/Bonnet was not prepared for this, but it can be done with another
hardware mod. Beginning October 2018, Adafruit began selling an updated version of
the HAT that supports 64x64 panels simply by bridging two pads on the PCB with solder.
You can identify which HAT you have by looking for the **Address E** pads, circled here:
<a href="https://cdn-learn.adafruit.com/assets/assets/000/063/005/original/led_matrices_addr-e-pad.jpg" target="_blank"><img src="https://cdn-learn.adafruit.com/assets/assets/000/063/005/original/led_matrices_addr-e-pad.jpg" height=80></a>
### New Adafruit RGB Matrix Hat (with Address E pads)
Look for the Address E pads located between the HUB75 connector and Pi camera cutout.
Melt a blob of solder between the center “E” pad the the “8” pad just above it
(for 64x64 matrices in the Adafruit shop)…*_or_* the “16” pad below (rare, for some
third-party 64x64 matrices…check datasheet).
### Old Adafruit HAT/Bonnet (without)
It is a little more advanced hack, so it is only really for people who are
comfortable with this kind of thing.
First, you have to figure out which is the input of the E-Line on your matrix
(they seem to be either on Pin 4 or Pin 8 of the IDC connector).
You need to disconnect that Pin from the ground plane (e.g. with an Exacto
knife) and connect GPIO 24 to it. The following images illustrate the case for
IDC Pin 4.
<a href="img/adafruit-64x64-front.jpg"><img src="img/adafruit-64x64-front.jpg" height="80px"></a>
<a href="img/adafruit-64x64-back.jpg"><img src="img/adafruit-64x64-back.jpg" height="80px"></a>
If the direct connection does not work, you need to send it through a free
level converter of the Adafruit HAT/Bonnet. Since all unused inputs are grounded
with traces under the chip, this involves lifting a leg from the
HCT245 (figure out a free bus driver from the schematic). If all of the
above makes sense to you, you have the Ninja level to do it!
It might be more convienent at this point to consider the [Active3 adapter](./adapter/active-3)
that has that covered already.
Running as root
---------------
The library requires to access hardware registers to control the LED matrix,
and create accurate timings. These hardware accesses require to run as root
user.
For security reasons, it is usually not a good idea to run an application
as root entirely, so this library makes sure to drop privileges immediately
after the hardware is initialized.
You can switch off the privilege dropping with the
[`--led-no-drop-privs`](#user-content-no-drop-priv) flag, or, if you do this
programmatically,
choose the configuration in the
[`RuntimeOptions struct`](https://github.com/hzeller/rpi-rgb-led-matrix/blob/master/include/led-matrix.h#L401).
Note, you _could_ run as non-root, which will use `/dev/gpiomem`
to at least write to GPIO, however the precise timing hardware registers are
not accessible. This will result in flicker and color degradation. Starting
as non-root is not recommended.
CPU use
-------
These displays need to be updated constantly to show an image with PWMed
LEDs. This is dependent on the length of the chain: for each chain element,
about 1'000'000 write operations have to happen every second!
(chain_length * 32 pixel long * 16 rows * 11 bit planes * 180 Hz refresh rate).
We can't use hardware support for writing these as DMA is too slow,
thus the constant CPU use on an RPi is roughly 30-40% of one core.
Keep that in mind if you plan to run other things on this computer (This
is less noticable on Raspberry Pi, Version 2 or 3 that has more cores).
Also, the output quality is susceptible to other heavy tasks running on that
computer - there might be changes in the overall brigthness when this affects
the referesh rate.
If you have a loaded system and one of the newer Pis with 4 cores, you can
reserve one core just for the refresh of the display:
```
isolcpus=3
```
.. at the end of the line of `/boot/cmdline.txt` (needs to be in the same as
the other arguments, no newline). This will use the last core
only to refresh the display then, but it also means, that no other process can
utilize it then. Still, I'd typically recommend it.
Performance improvements and limits
-----------------------------------
Regardless of which driving hardware you use, ultimately you can only push pixels
so fast to a string of panels before you get flickering due to too low a refresh
rate (less than 80-100Hz), or before you refresh the panel lines too fast and they
appear too dim because each line is not displayed long enough before it is turned off.
Basic performance tips:
- Use --led-show-refresh to see the refresh rate while you try parameters
- use an active-3 board with led-parallel=3
- led-pwm-dither-bits=1 gives you a speed boost but less brightness
- led-pwm-lsb-nanoseconds=50 also gives you a speed boost but less brightness
- led-pwm-bits=7 or even lower decrease color depth but increases refresh speed
- AB panels and other panels with that use values of led-multiplexing bigger than 0,
will also go faster, although as you tune more options given above, their advantage will decrease.
- 32x16 ABC panels are faster than ABCD which are faster than ABCDE, which are faster than 128x64 ABC panels
(which do use 5 address lines, but over only 3 wires)
- Use at least an rPi3 (rPi4 is still slightly faster but may need --led-slowdown-gpio=2)
Maximum resolutions reasonably achievable:
A general rule of thumb is that running 16K pixels (128x128 or otherwise) on a single chain,
is already pushing limits and you will have to make tradeoffs in visual quality. 32K pixels
(like 128x256) is definitely pushing things and you'll get 100Hz or less depending on the
performance options you choose.
This puts the maximum reasonable resolution around 100K pixels (like 384x256) for 3 chains.
You can see more examples and video capture of speed on [Marc MERLIN's page 'RGB Panels, from 192x80, to 384x192, to 384x256 and maybe not much beyond'](http://marc.merlins.org/perso/arduino/post_2020-03-13_RGB-Panels_-from-192x80_-to-384x192_-to-384x256-and-maybe-not-much-beyond.html)
If your refresh rate is below 300Hz, expect likely black bars when taking cell phone pictures.
A real camera with shutter speed lowered accordingly, will get around this.
Ultimately, you should not expect to go past 64K pixels using 3 chains without significant
quality tradeoffs. If you need bigger displays, you should use multiple boards and synchronize the
output.
Limitations
-----------
If you are using the Adafruit HAT/Bonnet in the default configuration, then we
can't make use of the PWM hardware (which only outputs
to a particular pin), so you'll see random brightness glitches. I strongly
suggest to do the aforementioned hardware mod.
The system needs constant CPU to update the display. Using the DMA controller
was considered but after extensive experiments
( https://github.com/hzeller/rpi-gpio-dma-demo )
dropped due to its slow speed..
There is an upper limit in how fast the GPIO pins can be controlled, which
limits the frame-rate. Raspberry Pi 2's and newer are generally faster.
Even with everything in place, you might see faint brightness fluctuations
in particular if there is something going on on the network or in a terminal
on the Pi; this could probably be mitigated with some more real-time
kernel for the Pi; maybe there are also hardware limitations (memory bus
contention?). Anyway, if you have a realtime kernel configuration that you
have optimized for this application, let me know.
To address the brightness fluctuations, you might experiment with the
`FIXED_FRAME_MICROSECONDS` compile time option in [lib/Makefile](lib/Makefile)
that has instructions how to set it up.
Fun
---
I am always happy to see users successfully using the software for wonderful
things, like this installation by Dirk in Scharbeutz, Germany:
![](./img/user-action-shot.jpg)
[led-image-viewer]: ./utils#image-viewer
[video-viewer]: ./utils#video-viewer
[matrix64]: ./img/chained-64x64.jpg
[sparkfun]: https://www.sparkfun.com/products/12584
[ada]: http://www.adafruit.com/product/1484
[rt-paper]: https://www.osadl.org/fileadmin/dam/rtlws/12/Brown.pdf
[adafruit-hat]: https://www.adafruit.com/products/2345
[raspbian-lite]: https://downloads.raspberrypi.org/raspbian_lite_latest
[DietPi]: https://dietpi.com/
[Adafruit HAT]: https://www.adafruit.com/products/2345
[Adafruit Bonnet]: https://www.adafruit.com/product/3211
[Nodejs binding]: https://github.com/zeitungen/node-rpi-rgb-led-matrix
[Go binding]: https://github.com/mcuadros/go-rpi-rgb-led-matrix
[Rust binding]: https://crates.io/crates/rpi-led-matrix
[Nodejs/Typescript binding]: https://github.com/alexeden/rpi-led-matrix
[headless]: https://www.raspberrypi.com/documentation/computers/configuration.html#setting-up-a-headless-raspberry-pi

@ -0,0 +1,23 @@
PCB adapter for Raspberry Pi to Hub75 RGB Matrixes
==================================================
Since hand-wiring can be a little tedious, here are some PCBs that help
with the wiring when using the `rpi-rgb-led-matrix` code.
* [Passive-3](./passive-3) Supports three parallel chains, directly connected
to the output of a Rapsberry Pi with 40 GPIO pins. Works, but usually it is better to
buffer the outputs using the ...
* [Active-3](./active-3) board. Supports three parallel chains with active buffering
and 3.3V -> 5V level shifting for best reliability. Requires SMD soldering.
As another option you can buy it from these locations not affiliated with this project.
They are given to help you locate premade boards but no guarantees are given or implied:
* https://www.electrodragon.com/product/rgb-matrix-panel-drive-board-raspberry-pi/
($3/board, but fairly long and/or expensive shipping from HKG)
* Seller #2 (fill me)
* The [Passive-RPi1](./passive-rpi1) adapter board is to connect one panel to
Raspberry Pi 1 with 26 GPIO pins.
![Three Panels connected][three-panels]
[three-panels]: ../img/three-parallel-panels-soic.jpg

@ -0,0 +1,3 @@
include ../kicad-scripts/makefile.inc
active3-rpi-hub75-adapter-fab.zip:

@ -0,0 +1,84 @@
Adapter PCB to support up to 3 panel chains
===========================================
* Supports up to three panel chains for newer plus models and
Raspberry Pi 2 that have 40 GPIO pins.
* Uses HCT245 to level shift signals from 3.3V to 5V and shield
the Raspberry Pi GPIOs from overloading.
* Open source KiCAD PCB EDA format.
* Optional: Pads to power the Pi with 5V, including optional capacitor footprints.
* Connector for RxD input (literally the only GPIO pin left) in case you want to
make your panel controlled with a serial interface (3.3V logic level).
* Provides a way to choose the pinout for different kinds of 64x64 matrixes.
* (not very pretty layout, was just lazy and let the auto-router generate the first pass)
* BOM:
- 4x 74HCT245 or 74AHCT245 in 20-SOIC, 7.5mm package which should make
it easy to hand-solder. Make sure to get the variant with the **T**: HC**T** or AHC**T**
(there are also HC or AHC, don't use these).
- 4x 100nF ceramic capacitor (0805 package)
- 1x 10kOhm resistor (0805 package). Not critcial, just a pullup (2.2k .. 15k probably ok).
- 3x 16pin IDC (=2x8) male receptible to connect the panels.
- 1x 40pin female connector to connect to the RPi.
- 1x (optional) 22μF .. 100μF capacitor for 5V rail (either 1206 SMD or
radial electrolytic with 2.5mm pitch/6.3mm diameter)
* The Gerber FAB files are provided as [active3-rpi-hub75-adapter-fab.zip](./active3-rpi-hub75-adapter-fab.zip)
The board is also [shared on OSH Park][osh-active3] (not affiliated).
![Preview][rendering]
![Real World][real-world]
Essentially, this is connecting the output pins through level shifting buffers (they
are operated at 5V, but the HCT series chips accept 3.3V input levels from the Pi). The
strobe, OE and clock signals are separately buffered for each connector.
![Schematic][schematic]
## Optional
### Power in
The area on the left has 5V/GND input pads, that allow you to power your Raspberry Pi from
a 5V source ... which you are likely to have as you are powering the LED Matrix. This is often
more convenient than using the USB connector to power the Pi.
If you do that, there are pads to add a capacitor to smooth the supply - two footprints are
provided: C5 and C6 for through-hole or surface mount components. The value is not critical;
I usually use a 22μF/6.3V ceramic capacitor on the C6 pads.
### Choose E-Line for 64x64 panels with 1:32 multiplexing
If you have a 64x64 matrix with 1:32 multiplexing, you need to supply an `E`-address line to it.
While the Address lines `A` to `D` have fixed positions on the Hub75 connector, there
seem to be two different ways to connect the `E` address line: it is either on pin 4 or pin 8
of the connector. So this adapter board provides the flexibility to choose the right pin for
your matrix.
Look at the back of the matrix or the documentation to find out for your specific board where
to connect E.
Once you know that, the jumper area in the bottom left of this adapter board allows to choose
to which pin to connect the E-address line to. The corresponding other pin should be connected
to GND. Simply solder a wire bridge as indicated below (or use a jumper that you can change later).
If you are not using such 64x64 matrix, you can connect both these pins to GND.
Here are the typical configurations:
No 1:32 64x64: to GND | E-Line on Pin 4 | E-Line on Pin 8|
-------------------------|-------------------|----------------
![][config-default] |![][config-pin4] |![][config-pin8]
### Input for serial RxD
If you are not using a 64x64 display that occupies the E-Line, you can use the RxD serial input -
this might come in handy if you are using the display to be controlled by a serial line. Be aware
that the input requires 3.3V level, so if you have a RS232, make sure to first adapt the levels.
[rendering]: ../../img/active3-pcb.png
[config-default]: ../../img/active3-pcb-config-default.png
[config-pin4]: ../../img/active3-pcb-config-pin4.png
[config-pin8]: ../../img/active3-pcb-config-pin8.png
[schematic]: ../../img/active3-schematic.png
[real-world]: ../../img/three-parallel-panels-soic.jpg
[osh-active3]: https://oshpark.com/shared_projects/6xAD1VXr

@ -0,0 +1,352 @@
EESchema-LIBRARY Version 2.4
#encoding utf-8
#
# 74xx_74HC245
#
DEF 74xx_74HC245 U 0 40 Y Y 1 L N
F0 "U" -300 650 50 H V C CNN
F1 "74xx_74HC245" -300 -650 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
ALIAS 74HC245
$FPLIST
DIP?20*
$ENDFPLIST
DRAW
S -300 600 300 -600 1 1 10 f
P 3 1 0 0 -25 -50 -25 50 25 50 N
P 4 1 0 0 -50 -50 25 -50 25 50 50 50 N
X A->B 1 -500 -400 200 R 50 50 1 0 I
X GND 10 0 -800 200 U 50 50 1 0 W
X B7 11 500 -200 200 L 50 50 1 0 T
X B6 12 500 -100 200 L 50 50 1 0 T
X B5 13 500 0 200 L 50 50 1 0 T
X B4 14 500 100 200 L 50 50 1 0 T
X B3 15 500 200 200 L 50 50 1 0 T
X B2 16 500 300 200 L 50 50 1 0 T
X B1 17 500 400 200 L 50 50 1 0 T
X B0 18 500 500 200 L 50 50 1 0 T
X CE 19 -500 -500 200 R 50 50 1 0 I I
X A0 2 -500 500 200 R 50 50 1 0 T
X VCC 20 0 800 200 D 50 50 1 0 W
X A1 3 -500 400 200 R 50 50 1 0 T
X A2 4 -500 300 200 R 50 50 1 0 T
X A3 5 -500 200 200 R 50 50 1 0 T
X A4 6 -500 100 200 R 50 50 1 0 T
X A5 7 -500 0 200 R 50 50 1 0 T
X A6 8 -500 -100 200 R 50 50 1 0 T
X A7 9 -500 -200 200 R 50 50 1 0 T
ENDDRAW
ENDDEF
#
# Connector_Generic_Conn_01x01
#
DEF Connector_Generic_Conn_01x01 J 0 40 Y N 1 F N
F0 "J" 0 100 50 H V C CNN
F1 "Connector_Generic_Conn_01x01" 0 -100 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
Connector*:*
$ENDFPLIST
DRAW
S -50 5 0 -5 1 1 6 N
S -50 50 50 -50 1 1 10 f
X Pin_1 1 -200 0 150 R 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Connector_Generic_Conn_01x02
#
DEF Connector_Generic_Conn_01x02 J 0 40 Y N 1 F N
F0 "J" 0 100 50 H V C CNN
F1 "Connector_Generic_Conn_01x02" 0 -200 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
Connector*:*_1x??_*
$ENDFPLIST
DRAW
S -50 -95 0 -105 1 1 6 N
S -50 5 0 -5 1 1 6 N
S -50 50 50 -150 1 1 10 f
X Pin_1 1 -200 0 150 R 50 50 1 1 P
X Pin_2 2 -200 -100 150 R 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Connector_Generic_Conn_02x01
#
DEF Connector_Generic_Conn_02x01 J 0 40 Y N 1 F N
F0 "J" 50 100 50 H V C CNN
F1 "Connector_Generic_Conn_02x01" 50 -100 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
Connector*:*_2x??_*
$ENDFPLIST
DRAW
S -50 5 0 -5 1 1 6 N
S -50 50 150 -50 1 1 10 f
S 150 5 100 -5 1 1 6 N
X Pin_1 1 -200 0 150 R 50 50 1 1 P
X Pin_2 2 300 0 150 L 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Connector_Generic_Conn_02x08_Odd_Even
#
DEF Connector_Generic_Conn_02x08_Odd_Even J 0 40 Y N 1 F N
F0 "J" 50 400 50 H V C CNN
F1 "Connector_Generic_Conn_02x08_Odd_Even" 50 -500 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
Connector*:*_2x??_*
$ENDFPLIST
DRAW
S -50 -395 0 -405 1 1 6 N
S -50 -295 0 -305 1 1 6 N
S -50 -195 0 -205 1 1 6 N
S -50 -95 0 -105 1 1 6 N
S -50 5 0 -5 1 1 6 N
S -50 105 0 95 1 1 6 N
S -50 205 0 195 1 1 6 N
S -50 305 0 295 1 1 6 N
S -50 350 150 -450 1 1 10 f
S 150 -395 100 -405 1 1 6 N
S 150 -295 100 -305 1 1 6 N
S 150 -195 100 -205 1 1 6 N
S 150 -95 100 -105 1 1 6 N
S 150 5 100 -5 1 1 6 N
S 150 105 100 95 1 1 6 N
S 150 205 100 195 1 1 6 N
S 150 305 100 295 1 1 6 N
X Pin_1 1 -200 300 150 R 50 50 1 1 P
X Pin_10 10 300 -100 150 L 50 50 1 1 P
X Pin_11 11 -200 -200 150 R 50 50 1 1 P
X Pin_12 12 300 -200 150 L 50 50 1 1 P
X Pin_13 13 -200 -300 150 R 50 50 1 1 P
X Pin_14 14 300 -300 150 L 50 50 1 1 P
X Pin_15 15 -200 -400 150 R 50 50 1 1 P
X Pin_16 16 300 -400 150 L 50 50 1 1 P
X Pin_2 2 300 300 150 L 50 50 1 1 P
X Pin_3 3 -200 200 150 R 50 50 1 1 P
X Pin_4 4 300 200 150 L 50 50 1 1 P
X Pin_5 5 -200 100 150 R 50 50 1 1 P
X Pin_6 6 300 100 150 L 50 50 1 1 P
X Pin_7 7 -200 0 150 R 50 50 1 1 P
X Pin_8 8 300 0 150 L 50 50 1 1 P
X Pin_9 9 -200 -100 150 R 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Connector_Generic_Conn_02x20_Odd_Even
#
DEF Connector_Generic_Conn_02x20_Odd_Even J 0 40 Y N 1 F N
F0 "J" 50 1000 50 H V C CNN
F1 "Connector_Generic_Conn_02x20_Odd_Even" 50 -1100 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
Connector*:*_2x??_*
$ENDFPLIST
DRAW
S -50 -995 0 -1005 1 1 6 N
S -50 -895 0 -905 1 1 6 N
S -50 -795 0 -805 1 1 6 N
S -50 -695 0 -705 1 1 6 N
S -50 -595 0 -605 1 1 6 N
S -50 -495 0 -505 1 1 6 N
S -50 -395 0 -405 1 1 6 N
S -50 -295 0 -305 1 1 6 N
S -50 -195 0 -205 1 1 6 N
S -50 -95 0 -105 1 1 6 N
S -50 5 0 -5 1 1 6 N
S -50 105 0 95 1 1 6 N
S -50 205 0 195 1 1 6 N
S -50 305 0 295 1 1 6 N
S -50 405 0 395 1 1 6 N
S -50 505 0 495 1 1 6 N
S -50 605 0 595 1 1 6 N
S -50 705 0 695 1 1 6 N
S -50 805 0 795 1 1 6 N
S -50 905 0 895 1 1 6 N
S -50 950 150 -1050 1 1 10 f
S 150 -995 100 -1005 1 1 6 N
S 150 -895 100 -905 1 1 6 N
S 150 -795 100 -805 1 1 6 N
S 150 -695 100 -705 1 1 6 N
S 150 -595 100 -605 1 1 6 N
S 150 -495 100 -505 1 1 6 N
S 150 -395 100 -405 1 1 6 N
S 150 -295 100 -305 1 1 6 N
S 150 -195 100 -205 1 1 6 N
S 150 -95 100 -105 1 1 6 N
S 150 5 100 -5 1 1 6 N
S 150 105 100 95 1 1 6 N
S 150 205 100 195 1 1 6 N
S 150 305 100 295 1 1 6 N
S 150 405 100 395 1 1 6 N
S 150 505 100 495 1 1 6 N
S 150 605 100 595 1 1 6 N
S 150 705 100 695 1 1 6 N
S 150 805 100 795 1 1 6 N
S 150 905 100 895 1 1 6 N
X Pin_1 1 -200 900 150 R 50 50 1 1 P
X Pin_10 10 300 500 150 L 50 50 1 1 P
X Pin_11 11 -200 400 150 R 50 50 1 1 P
X Pin_12 12 300 400 150 L 50 50 1 1 P
X Pin_13 13 -200 300 150 R 50 50 1 1 P
X Pin_14 14 300 300 150 L 50 50 1 1 P
X Pin_15 15 -200 200 150 R 50 50 1 1 P
X Pin_16 16 300 200 150 L 50 50 1 1 P
X Pin_17 17 -200 100 150 R 50 50 1 1 P
X Pin_18 18 300 100 150 L 50 50 1 1 P
X Pin_19 19 -200 0 150 R 50 50 1 1 P
X Pin_2 2 300 900 150 L 50 50 1 1 P
X Pin_20 20 300 0 150 L 50 50 1 1 P
X Pin_21 21 -200 -100 150 R 50 50 1 1 P
X Pin_22 22 300 -100 150 L 50 50 1 1 P
X Pin_23 23 -200 -200 150 R 50 50 1 1 P
X Pin_24 24 300 -200 150 L 50 50 1 1 P
X Pin_25 25 -200 -300 150 R 50 50 1 1 P
X Pin_26 26 300 -300 150 L 50 50 1 1 P
X Pin_27 27 -200 -400 150 R 50 50 1 1 P
X Pin_28 28 300 -400 150 L 50 50 1 1 P
X Pin_29 29 -200 -500 150 R 50 50 1 1 P
X Pin_3 3 -200 800 150 R 50 50 1 1 P
X Pin_30 30 300 -500 150 L 50 50 1 1 P
X Pin_31 31 -200 -600 150 R 50 50 1 1 P
X Pin_32 32 300 -600 150 L 50 50 1 1 P
X Pin_33 33 -200 -700 150 R 50 50 1 1 P
X Pin_34 34 300 -700 150 L 50 50 1 1 P
X Pin_35 35 -200 -800 150 R 50 50 1 1 P
X Pin_36 36 300 -800 150 L 50 50 1 1 P
X Pin_37 37 -200 -900 150 R 50 50 1 1 P
X Pin_38 38 300 -900 150 L 50 50 1 1 P
X Pin_39 39 -200 -1000 150 R 50 50 1 1 P
X Pin_4 4 300 800 150 L 50 50 1 1 P
X Pin_40 40 300 -1000 150 L 50 50 1 1 P
X Pin_5 5 -200 700 150 R 50 50 1 1 P
X Pin_6 6 300 700 150 L 50 50 1 1 P
X Pin_7 7 -200 600 150 R 50 50 1 1 P
X Pin_8 8 300 600 150 L 50 50 1 1 P
X Pin_9 9 -200 500 150 R 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Device_C
#
DEF Device_C C 0 10 N Y 1 F N
F0 "C" 25 100 50 H V L CNN
F1 "Device_C" 25 -100 50 H V L CNN
F2 "" 38 -150 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
C_*
$ENDFPLIST
DRAW
P 2 0 1 20 -80 -30 80 -30 N
P 2 0 1 20 -80 30 80 30 N
X ~ 1 0 150 110 D 50 50 1 1 P
X ~ 2 0 -150 110 U 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Device_CP_Small
#
DEF Device_CP_Small C 0 10 N N 1 F N
F0 "C" 10 70 50 H V L CNN
F1 "Device_CP_Small" 10 -80 50 H V L CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
CP_*
$ENDFPLIST
DRAW
S -60 -12 60 -27 0 1 0 F
S -60 27 60 12 0 1 0 N
P 2 0 1 0 -50 60 -30 60 N
P 2 0 1 0 -40 50 -40 70 N
X ~ 1 0 100 73 D 50 50 1 1 P
X ~ 2 0 -100 73 U 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Device_C_Small
#
DEF Device_C_Small C 0 10 N N 1 F N
F0 "C" 10 70 50 H V L CNN
F1 "Device_C_Small" 10 -80 50 H V L CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
C_*
$ENDFPLIST
DRAW
P 2 0 1 13 -60 -20 60 -20 N
P 2 0 1 12 -60 20 60 20 N
X ~ 1 0 100 80 D 50 50 1 1 P
X ~ 2 0 -100 80 U 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Device_R
#
DEF Device_R R 0 0 N Y 1 F N
F0 "R" 80 0 50 V V C CNN
F1 "Device_R" 0 0 50 V V C CNN
F2 "" -70 0 50 V I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
R_*
$ENDFPLIST
DRAW
S -40 -100 40 100 0 1 10 N
X ~ 1 0 150 50 D 50 50 1 1 P
X ~ 2 0 -150 50 U 50 50 1 1 P
ENDDRAW
ENDDEF
#
# power_GND
#
DEF power_GND #PWR 0 0 Y Y 1 F P
F0 "#PWR" 0 -250 50 H I C CNN
F1 "power_GND" 0 -150 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
DRAW
P 6 0 1 0 0 0 0 -50 50 -50 0 -100 -50 -50 0 -50 N
X GND 1 0 0 0 D 50 50 1 1 W N
ENDDRAW
ENDDEF
#
# power_PWR_FLAG
#
DEF power_PWR_FLAG #FLG 0 0 N N 1 F P
F0 "#FLG" 0 75 50 H I C CNN
F1 "power_PWR_FLAG" 0 150 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
DRAW
P 6 0 1 0 0 0 0 50 -40 75 0 100 40 75 0 50 N
X pwr 1 0 0 0 U 50 50 0 0 w
ENDDRAW
ENDDEF
#
# power_VCC
#
DEF power_VCC #PWR 0 0 Y Y 1 F P
F0 "#PWR" 0 -150 50 H I C CNN
F1 "power_VCC" 0 150 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
DRAW
C 0 75 25 0 1 0 N
P 2 0 1 0 0 0 0 50 N
X VCC 1 0 0 0 U 50 50 1 1 W N
ENDDRAW
ENDDEF
#
#End Library

@ -0,0 +1,33 @@
update=Sat 06 Jul 2019 08:45:23 PM PDT
version=1
last_client=kicad
[cvpcb]
version=1
NetIExt=net
[cvpcb/libraries]
EquName1=devcms
[general]
version=1
[pcbnew]
version=1
PageLayoutDescrFile=
LastNetListRead=
UseCmpFile=1
PadDrill=0.6
PadDrillOvalY=0.6
PadSizeH=1.5
PadSizeV=1.5
PcbTextSizeV=1.5
PcbTextSizeH=1.5
PcbTextThickness=0.3
ModuleTextSizeV=1
ModuleTextSizeH=1
ModuleTextSizeThickness=0.15
SolderMaskClearance=0
SolderMaskMinWidth=0
DrawSegmentWidth=0.2
BoardOutlineThickness=0.09999999999999999
ModuleOutlineThickness=0.15
[eeschema]
version=1
LibDir=

@ -0,0 +1,83 @@
'''
Based on gen_gerber_and_drill_files_board.py in kicad/demos directory.
'''
import sys
from pcbnew import *
filename=sys.argv[1]
board = LoadBoard(filename)
plotDir = "plot/"
pctl = PLOT_CONTROLLER(board)
popt = pctl.GetPlotOptions()
popt.SetOutputDirectory(plotDir)
# Set some important plot options:
popt.SetPlotFrameRef(False)
popt.SetLineWidth(FromMM(0.35))
popt.SetAutoScale(False)
popt.SetScale(1)
popt.SetMirror(False)
popt.SetUseGerberAttributes(False)
popt.SetUseGerberProtelExtensions(True)
popt.SetExcludeEdgeLayer(True);
popt.SetScale(1)
popt.SetUseAuxOrigin(True)
# This by gerbers only (also the name is truly horrid!)
popt.SetSubtractMaskFromSilk(False)
# param 0 is the layer ID
# param 1 is a string added to the file base name to identify the drawing
# param 2 is a comment
# Create filenames in a way that if they are sorted alphabetically, they
# are shown in exactly the layering the board would look like. So
# gerbv *
# just makes sense.
plot_plan = [
( Edge_Cuts, "0-Edge_Cuts", "Edges" ),
( F_Paste, "1-PasteTop", "Paste top" ),
( F_SilkS, "2-SilkTop", "Silk top" ),
( F_Mask, "3-MaskTop", "Mask top" ),
( F_Cu, "4-CuTop", "Top layer" ),
( B_Cu, "5-CuBottom", "Bottom layer" ),
( B_Mask, "6-MaskBottom", "Mask bottom" ),
( B_SilkS, "7-SilkBottom", "Silk top" ),
( B_Paste, "8-PasteBottom", "Paste Bottom" ),
]
for layer_info in plot_plan:
pctl.SetLayer(layer_info[0])
pctl.OpenPlotfile(layer_info[1], PLOT_FORMAT_GERBER, layer_info[2])
pctl.PlotLayer()
# At the end you have to close the last plot, otherwise you don't know when
# the object will be recycled!
pctl.ClosePlot()
# Fabricators need drill files.
# sometimes a drill map file is asked (for verification purpose)
drlwriter = EXCELLON_WRITER( board )
drlwriter.SetMapFileFormat( PLOT_FORMAT_PDF )
mirror = False
minimalHeader = False
offset = wxPoint(0,0)
mergeNPTH = True
drlwriter.SetOptions( mirror, minimalHeader, offset, mergeNPTH )
metricFmt = True
drlwriter.SetFormat( metricFmt )
genDrl = True
genMap = True
drlwriter.CreateDrillandMapFilesSet( plotDir, genDrl, genMap );

@ -0,0 +1,8 @@
# -*- Makefile -*-
%-fab.zip : %-fab.kicad_pcb
python ../kicad-scripts/kicad-fab.py $<
zip -r $@ plot/
%-fab.kicad_pcb : %.kicad_pcb
sed "s/%%gitversion%%/`git log --date=short --pretty=format:'%h@%cd' -n 1`/" < $^ > $@

@ -0,0 +1,3 @@
include ../kicad-scripts/makefile.inc
passive3-rpi-hub75-adapter-fab.zip:

@ -0,0 +1,26 @@
Adapter PCB to support up to 3 panel chains
===========================================
* This is a passive board. It is simple, but the logic level will be out of
spec for the LED matrix (3.3V vs. 5V) which might or might not work.
Driving long cables with the GPIO pins is also not a good idea.
* You typically want to consider using the [active board](../active-3).
* Works for Matrix up to 1:16 multiplexing (32 rows). For 1:32 multiplexing,
you want to use the [active board](../active-3). You can of also hack
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
64x64 boards.
* Only really advisable, if the LED panels have 74HCT245 (as opposed to just
74HC245) in their input stage, because then they can deal properly with
the 3.3V logic levels coming from the Pi.
* Supports up to three panel chains for newer plus models and
Raspberry Pi 2/3 that have 40 GPIO pins.
* Open source KiCAD PCB EDA format.
* (not very pretty layout, was just lazy and let the auto-router do it).
* The FAB files are provided as [passive3-rpi-hub75-adapter-fab.zip](./passive3-rpi-hub75-adapter-fab.zip)
This board is [shared on OSH Park][osh-passive3] (not affiliated).
![Preview][rendering]
[rendering]: ../../img/passive3-pcb.png
[osh-passive3]: https://oshpark.com/shared_projects/FNAtZUsP

@ -0,0 +1,218 @@
EESchema-LIBRARY Version 2.3
#encoding utf-8
#
<<<<<<< HEAD
# CONN_01X01
#
DEF CONN_01X01 P 0 40 Y N 1 F N
F0 "P" 0 100 50 H V C CNN
F1 "CONN_01X01" 100 0 50 V V C CNN
F2 "" 0 0 60 H V C CNN
F3 "" 0 0 60 H V C CNN
$FPLIST
Pin_Header_Straight_1X01
Pin_Header_Angled_1X01
Socket_Strip_Straight_1X01
Socket_Strip_Angled_1X01
$ENDFPLIST
DRAW
S -50 5 10 -5 0 1 0 N
S -50 50 50 -50 0 1 0 N
X P1 1 -200 0 150 R 50 50 1 1 P
ENDDRAW
ENDDEF
#
=======
>>>>>>> d9ecea532afc911de3244f458589b6eadd051949
# CONN_02X08
#
DEF CONN_02X08 P 0 1 Y N 1 F N
F0 "P" 0 450 50 H V C CNN
F1 "CONN_02X08" 0 0 50 V V C CNN
F2 "" 0 -1200 60 H V C CNN
F3 "" 0 -1200 60 H V C CNN
$FPLIST
Pin_Header_Straight_2X08
Pin_Header_Angled_2X08
Socket_Strip_Straight_2X08
Socket_Strip_Angled_2X08
$ENDFPLIST
DRAW
S -100 -345 -50 -355 0 1 0 N
S -100 -245 -50 -255 0 1 0 N
S -100 -145 -50 -155 0 1 0 N
S -100 -45 -50 -55 0 1 0 N
S -100 55 -50 45 0 1 0 N
S -100 155 -50 145 0 1 0 N
S -100 255 -50 245 0 1 0 N
S -100 355 -50 345 0 1 0 N
S -100 400 100 -400 0 1 0 N
S 50 -345 100 -355 0 1 0 N
S 50 -245 100 -255 0 1 0 N
S 50 -145 100 -155 0 1 0 N
S 50 -45 100 -55 0 1 0 N
S 50 55 100 45 0 1 0 N
S 50 155 100 145 0 1 0 N
S 50 255 100 245 0 1 0 N
S 50 355 100 345 0 1 0 N
X P1 1 -250 350 150 R 50 50 1 1 P
X P2 2 250 350 150 L 50 50 1 1 P
X P3 3 -250 250 150 R 50 50 1 1 P
X P4 4 250 250 150 L 50 50 1 1 P
X P5 5 -250 150 150 R 50 50 1 1 P
X P6 6 250 150 150 L 50 50 1 1 P
X P7 7 -250 50 150 R 50 50 1 1 P
X P8 8 250 50 150 L 50 50 1 1 P
X P9 9 -250 -50 150 R 50 50 1 1 P
X P10 10 250 -50 150 L 50 50 1 1 P
X P11 11 -250 -150 150 R 50 50 1 1 P
X P12 12 250 -150 150 L 50 50 1 1 P
X P13 13 -250 -250 150 R 50 50 1 1 P
X P14 14 250 -250 150 L 50 50 1 1 P
X P15 15 -250 -350 150 R 50 50 1 1 P
X P16 16 250 -350 150 L 50 50 1 1 P
ENDDRAW
ENDDEF
#
# CONN_02X20
#
DEF CONN_02X20 P 0 1 Y N 1 F N
F0 "P" 0 1050 50 H V C CNN
F1 "CONN_02X20" 0 0 50 V V C CNN
F2 "" 0 -950 60 H V C CNN
F3 "" 0 -950 60 H V C CNN
$FPLIST
Pin_Header_Straight_2X20
Pin_Header_Angled_2X20
Socket_Strip_Straight_2X20
Socket_Strip_Angled_2X20
$ENDFPLIST
DRAW
S -100 -945 -50 -955 0 1 0 N
S -100 -845 -50 -855 0 1 0 N
S -100 -745 -50 -755 0 1 0 N
S -100 -645 -50 -655 0 1 0 N
S -100 -545 -50 -555 0 1 0 N
S -100 -445 -50 -455 0 1 0 N
S -100 -345 -50 -355 0 1 0 N
S -100 -245 -50 -255 0 1 0 N
S -100 -145 -50 -155 0 1 0 N
S -100 -45 -50 -55 0 1 0 N
S -100 55 -50 45 0 1 0 N
S -100 155 -50 145 0 1 0 N
S -100 255 -50 245 0 1 0 N
S -100 355 -50 345 0 1 0 N
S -100 455 -50 445 0 1 0 N
S -100 555 -50 545 0 1 0 N
S -100 655 -50 645 0 1 0 N
S -100 755 -50 745 0 1 0 N
S -100 855 -50 845 0 1 0 N
S -100 955 -50 945 0 1 0 N
S -100 1000 100 -1000 0 1 0 N
S 50 -945 100 -955 0 1 0 N
S 50 -845 100 -855 0 1 0 N
S 50 -745 100 -755 0 1 0 N
S 50 -645 100 -655 0 1 0 N
S 50 -545 100 -555 0 1 0 N
S 50 -445 100 -455 0 1 0 N
S 50 -345 100 -355 0 1 0 N
S 50 -245 100 -255 0 1 0 N
S 50 -145 100 -155 0 1 0 N
S 50 -45 100 -55 0 1 0 N
S 50 55 100 45 0 1 0 N
S 50 155 100 145 0 1 0 N
S 50 255 100 245 0 1 0 N
S 50 355 100 345 0 1 0 N
S 50 455 100 445 0 1 0 N
S 50 555 100 545 0 1 0 N
S 50 655 100 645 0 1 0 N
S 50 755 100 745 0 1 0 N
S 50 855 100 845 0 1 0 N
S 50 955 100 945 0 1 0 N
X P1 1 -250 950 150 R 50 50 1 1 P
X P2 2 250 950 150 L 50 50 1 1 P
X P3 3 -250 850 150 R 50 50 1 1 P
X P4 4 250 850 150 L 50 50 1 1 P
X P5 5 -250 750 150 R 50 50 1 1 P
X P6 6 250 750 150 L 50 50 1 1 P
X P7 7 -250 650 150 R 50 50 1 1 P
X P8 8 250 650 150 L 50 50 1 1 P
X P9 9 -250 550 150 R 50 50 1 1 P
X P10 10 250 550 150 L 50 50 1 1 P
X P20 20 250 50 150 L 50 50 1 1 P
X P30 30 250 -450 150 L 50 50 1 1 P
X P40 40 250 -950 150 L 50 50 1 1 P
X P11 11 -250 450 150 R 50 50 1 1 P
X P21 21 -250 -50 150 R 50 50 1 1 P
X P31 31 -250 -550 150 R 50 50 1 1 P
X P12 12 250 450 150 L 50 50 1 1 P
X P22 22 250 -50 150 L 50 50 1 1 P
X P32 32 250 -550 150 L 50 50 1 1 P
X P13 13 -250 350 150 R 50 50 1 1 P
X P23 23 -250 -150 150 R 50 50 1 1 P
X P33 33 -250 -650 150 R 50 50 1 1 P
X P14 14 250 350 150 L 50 50 1 1 P
X P24 24 250 -150 150 L 50 50 1 1 P
X P34 34 250 -650 150 L 50 50 1 1 P
X P15 15 -250 250 150 R 50 50 1 1 P
X P25 25 -250 -250 150 R 50 50 1 1 P
X P35 35 -250 -750 150 R 50 50 1 1 P
X P16 16 250 250 150 L 50 50 1 1 P
X P26 26 250 -250 150 L 50 50 1 1 P
X P36 36 250 -750 150 L 50 50 1 1 P
X P17 17 -250 150 150 R 50 50 1 1 P
X P27 27 -250 -350 150 R 50 50 1 1 P
X P37 37 -250 -850 150 R 50 50 1 1 P
X P18 18 250 150 150 L 50 50 1 1 P
X P28 28 250 -350 150 L 50 50 1 1 P
X P38 38 250 -850 150 L 50 50 1 1 P
X P19 19 -250 50 150 R 50 50 1 1 P
X P29 29 -250 -450 150 R 50 50 1 1 P
X P39 39 -250 -950 150 R 50 50 1 1 P
ENDDRAW
ENDDEF
#
# GND
#
DEF GND #PWR 0 0 Y Y 1 F P
F0 "#PWR" 0 -250 50 H I C CNN
F1 "GND" 0 -150 50 H V C CNN
F2 "" 0 0 60 H V C CNN
F3 "" 0 0 60 H V C CNN
DRAW
P 6 0 1 0 0 0 0 -50 50 -50 0 -100 -50 -50 0 -50 N
X GND 1 0 0 0 D 50 50 1 1 W N
ENDDRAW
ENDDEF
#
<<<<<<< HEAD
=======
# PWR_FLAG
#
DEF PWR_FLAG #FLG 0 0 N N 1 F P
F0 "#FLG" 0 95 50 H I C CNN
F1 "PWR_FLAG" 0 180 50 H V C CNN
F2 "" 0 0 60 H V C CNN
F3 "" 0 0 60 H V C CNN
DRAW
X pwr 1 0 0 0 U 20 20 0 0 w
P 6 0 1 0 0 0 0 50 -75 100 0 150 75 100 0 50 N
ENDDRAW
ENDDEF
#
>>>>>>> d9ecea532afc911de3244f458589b6eadd051949
# VCC
#
DEF VCC #PWR 0 0 Y Y 1 F P
F0 "#PWR" 0 -150 50 H I C CNN
F1 "VCC" 0 150 50 H V C CNN
F2 "" 0 0 60 H V C CNN
F3 "" 0 0 60 H V C CNN
DRAW
C 0 75 25 0 1 0 N
P 2 0 1 0 0 0 0 50 N
X VCC 1 0 0 0 U 50 50 1 1 W N
ENDDRAW
ENDDEF
#
#End Library

@ -0,0 +1,63 @@
update=Wed 17 Jun 2015 07:02:16 PM PDT
version=1
last_client=kicad
[cvpcb]
version=1
NetIExt=net
[cvpcb/libraries]
EquName1=devcms
[general]
version=1
[pcbnew]
version=1
PageLayoutDescrFile=
LastNetListRead=passive3-rpi-hub75-adapter.net
UseCmpFile=0
PadDrill=3.048
PadDrillOvalY=3.048
PadSizeH=4.064
PadSizeV=4.064
PcbTextSizeV=1.5
PcbTextSizeH=1.5
PcbTextThickness=0.3
ModuleTextSizeV=1
ModuleTextSizeH=1
ModuleTextSizeThickness=0.15
SolderMaskClearance=0
SolderMaskMinWidth=0
DrawSegmentWidth=0.2
BoardOutlineThickness=0.09999999999999999
ModuleOutlineThickness=0.15
[eeschema]
version=1
LibDir=
[eeschema/libraries]
LibName1=power
LibName2=device
LibName3=transistors
LibName4=conn
LibName5=linear
LibName6=regul
LibName7=74xx
LibName8=cmos4000
LibName9=adc-dac
LibName10=memory
LibName11=xilinx
LibName12=microcontrollers
LibName13=dsp
LibName14=microchip
LibName15=analog_switches
LibName16=motorola
LibName17=texas
LibName18=intel
LibName19=audio
LibName20=interface
LibName21=digital-audio
LibName22=philips
LibName23=display
LibName24=cypress
LibName25=siliconi
LibName26=opto
LibName27=atmel
LibName28=contrib
LibName29=valves

@ -0,0 +1,408 @@
EESchema Schematic File Version 2
LIBS:power
LIBS:device
LIBS:transistors
LIBS:conn
LIBS:linear
LIBS:regul
LIBS:74xx
LIBS:cmos4000
LIBS:adc-dac
LIBS:memory
LIBS:xilinx
LIBS:microcontrollers
LIBS:dsp
LIBS:microchip
LIBS:analog_switches
LIBS:motorola
LIBS:texas
LIBS:intel
LIBS:audio
LIBS:interface
LIBS:digital-audio
LIBS:philips
LIBS:display
LIBS:cypress
LIBS:siliconi
LIBS:opto
LIBS:atmel
LIBS:contrib
LIBS:valves
EELAYER 25 0
EELAYER END
$Descr A4 11693 8268
encoding utf-8
Sheet 1 1
Title ""
Date ""
Rev ""
Comp ""
Comment1 ""
Comment2 ""
Comment3 ""
Comment4 ""
$EndDescr
$Comp
L CONN_02X08 Panel-1
U 1 1 54ECB236
P 6950 2550
F 0 "Panel-1" H 6950 3000 50 0000 C CNN
F 1 "CONN_02X08" V 6950 2550 50 0000 C CNN
F 2 "Pin_Headers:Pin_Header_Straight_2x08" H 6950 1350 60 0001 C CNN
F 3 "" H 6950 1350 60 0000 C CNN
1 6950 2550
1 0 0 -1
$EndComp
$Comp
L CONN_02X20 P1
U 1 1 54ECB2B7
P 4500 3700
F 0 "P1" H 4500 4750 50 0000 C CNN
F 1 "CONN_02X20" V 4500 3700 50 0000 C CNN
F 2 "Pin_Headers:Pin_Header_Straight_2x20" H 4500 2750 60 0001 C CNN
F 3 "" H 4500 2750 60 0000 C CNN
1 4500 3700
1 0 0 -1
$EndComp
Wire Wire Line
4800 2700 4800 2850
Wire Wire Line
4750 2750 4950 2750
Wire Wire Line
4800 2850 4750 2850
Connection ~ 4800 2750
$Comp
L GND #PWR01
U 1 1 54ECB3E1
P 4850 3350
F 0 "#PWR01" H 4850 3350 30 0001 C CNN
F 1 "GND" H 4850 3280 30 0001 C CNN
F 2 "" H 4850 3350 60 0000 C CNN
F 3 "" H 4850 3350 60 0000 C CNN
1 4850 3350
0 -1 -1 0
$EndComp
Wire Wire Line
4750 3350 4850 3350
$Comp
L GND #PWR02
U 1 1 54ECB417
P 4850 3650
F 0 "#PWR02" H 4850 3650 30 0001 C CNN
F 1 "GND" H 4850 3580 30 0001 C CNN
F 2 "" H 4850 3650 60 0000 C CNN
F 3 "" H 4850 3650 60 0000 C CNN
1 4850 3650
0 -1 -1 0
$EndComp
Wire Wire Line
4750 3650 4850 3650
$Comp
L GND #PWR03
U 1 1 54ECB4A1
P 4850 2950
F 0 "#PWR03" H 4850 2950 30 0001 C CNN
F 1 "GND" H 4850 2880 30 0001 C CNN
F 2 "" H 4850 2950 60 0000 C CNN
F 3 "" H 4850 2950 60 0000 C CNN
1 4850 2950
0 -1 -1 0
$EndComp
Wire Wire Line
4750 2950 4850 2950
$Comp
L GND #PWR04
U 1 1 54ECB5FE
P 4150 4650
F 0 "#PWR04" H 4150 4650 30 0001 C CNN
F 1 "GND" H 4150 4580 30 0001 C CNN
F 2 "" H 4150 4650 60 0000 C CNN
F 3 "" H 4150 4650 60 0000 C CNN
1 4150 4650
0 1 1 0
$EndComp
Wire Wire Line
4150 4650 4250 4650
$Comp
L GND #PWR05
U 1 1 54ECB73E
P 4850 4350
F 0 "#PWR05" H 4850 4350 30 0001 C CNN
F 1 "GND" H 4850 4280 30 0001 C CNN
F 2 "" H 4850 4350 60 0000 C CNN
F 3 "" H 4850 4350 60 0000 C CNN
1 4850 4350
0 -1 -1 0
$EndComp
Wire Wire Line
4750 4350 4850 4350
$Comp
L GND #PWR06
U 1 1 54ECB7BC
P 4150 3950
F 0 "#PWR06" H 4150 3950 30 0001 C CNN
F 1 "GND" H 4150 3880 30 0001 C CNN
F 2 "" H 4150 3950 60 0000 C CNN
F 3 "" H 4150 3950 60 0000 C CNN
1 4150 3950
0 1 1 0
$EndComp
Wire Wire Line
4150 3950 4250 3950
Text GLabel 4250 3050 0 51 Output ~ 0
strobe
Text GLabel 4750 4250 2 51 Output ~ 0
p1_r1
Text GLabel 4250 4150 0 51 Output ~ 0
p1_g1
Text GLabel 4250 4250 0 51 Output ~ 0
p1_b1
Text GLabel 4250 4450 0 51 Output ~ 0
p1_r2
Text GLabel 4250 4350 0 51 Output ~ 0
p1_g2
Text GLabel 4750 4550 2 51 Output ~ 0
p1_b2
Text GLabel 4250 3450 0 51 Output ~ 0
row_A
Text GLabel 4750 3450 2 51 Output ~ 0
row_B
Text GLabel 4750 3550 2 51 Output ~ 0
row_C
Text GLabel 4750 3750 2 51 Output ~ 0
row_D
Text GLabel 4250 3250 0 51 Output ~ 0
clock
Text GLabel 4250 3850 0 51 Output ~ 0
p0_r1
Text GLabel 4250 3350 0 51 Output ~ 0
p0_g1
Text GLabel 4750 3950 2 51 Output ~ 0
p0_b1
Text GLabel 4750 3850 2 51 Output ~ 0
p0_r2
Text GLabel 4250 3750 0 51 Output ~ 0
p0_g2
Text GLabel 4250 3650 0 51 Output ~ 0
p0_b2
Text GLabel 4750 3250 2 51 Output ~ 0
OE
Wire Wire Line
7200 2500 7750 2500
$Comp
L GND #PWR07
U 1 1 54ECD031
P 7750 2950
F 0 "#PWR07" H 7750 2950 30 0001 C CNN
F 1 "GND" H 7750 2880 30 0001 C CNN
F 2 "" H 7750 2950 60 0000 C CNN
F 3 "" H 7750 2950 60 0000 C CNN
1 7750 2950
1 0 0 -1
$EndComp
Wire Wire Line
7200 2900 7750 2900
Wire Wire Line
7200 2300 7750 2300
$Comp
L CONN_02X08 Panel-2
U 1 1 54ECE201
P 6950 3800
F 0 "Panel-2" H 6950 4250 50 0000 C CNN
F 1 "CONN_02X08" V 6950 3800 50 0000 C CNN
F 2 "Pin_Headers:Pin_Header_Straight_2x08" H 6950 2600 60 0001 C CNN
F 3 "" H 6950 2600 60 0000 C CNN
1 6950 3800
1 0 0 -1
$EndComp
Wire Wire Line
7200 3750 7750 3750
$Comp
L GND #PWR08
U 1 1 54ECE20E
P 7750 4200
F 0 "#PWR08" H 7750 4200 30 0001 C CNN
F 1 "GND" H 7750 4130 30 0001 C CNN
F 2 "" H 7750 4200 60 0000 C CNN
F 3 "" H 7750 4200 60 0000 C CNN
1 7750 4200
1 0 0 -1
$EndComp
Wire Wire Line
7200 4150 7750 4150
Wire Wire Line
7200 3550 7750 3550
$Comp
L VCC #PWR09
U 1 1 54ECD3DE
P 4800 2700
F 0 "#PWR09" H 4800 2800 30 0001 C CNN
F 1 "VCC" H 4800 2800 30 0000 C CNN
F 2 "" H 4800 2700 60 0000 C CNN
F 3 "" H 4800 2700 60 0000 C CNN
1 4800 2700
1 0 0 -1
$EndComp
Text GLabel 7200 3650 2 51 Input ~ 0
p1_g2
Wire Wire Line
7750 3550 7750 4200
Connection ~ 7750 4150
Connection ~ 7750 3750
Text GLabel 7200 3450 2 51 Input ~ 0
p1_g1
Wire Wire Line
7750 2300 7750 2950
Connection ~ 7750 2500
Connection ~ 7750 2900
Text GLabel 7200 2400 2 51 Input ~ 0
p0_g2
Text GLabel 7200 2200 2 51 Input ~ 0
p0_g1
Text GLabel 7200 2700 2 51 Input ~ 0
row_D
Text GLabel 7200 2600 2 51 Input ~ 0
row_B
Text GLabel 7200 3850 2 51 Input ~ 0
row_B
Text GLabel 7200 3950 2 51 Input ~ 0
row_D
Text GLabel 6700 2200 0 51 Input ~ 0
p0_r1
Text GLabel 6700 2800 0 51 Input ~ 0
clock
Text GLabel 6700 2500 0 51 Input ~ 0
p0_b2
Text GLabel 6700 2300 0 51 Input ~ 0
p0_b1
Text GLabel 6700 2400 0 51 Input ~ 0
p0_r2
Text GLabel 6700 2900 0 51 Input ~ 0
OE
Text GLabel 6700 3450 0 51 Input ~ 0
p1_r1
Text GLabel 6700 3550 0 51 Input ~ 0
p1_b1
Text GLabel 6700 3650 0 51 Input ~ 0
p1_r2
Text GLabel 6700 3750 0 51 Input ~ 0
p1_b2
Text GLabel 6700 4050 0 51 Input ~ 0
clock
Text GLabel 6700 4150 0 51 Input ~ 0
OE
Text GLabel 4750 3050 2 51 Output ~ 0
p2_r1
Text GLabel 4250 2850 0 51 Output ~ 0
p2_g1
Text GLabel 4250 2950 0 51 Output ~ 0
p2_b1
Text GLabel 4250 4550 0 51 Output ~ 0
p2_r2
Text GLabel 4750 4450 2 51 Output ~ 0
p2_g2
Text GLabel 4750 4650 2 51 Output ~ 0
p2_b2
Text GLabel 6700 3850 0 51 Input ~ 0
row_A
Text GLabel 6700 3950 0 51 Input ~ 0
row_C
Text GLabel 6700 2600 0 51 Input ~ 0
row_A
Text GLabel 6700 2700 0 51 Input ~ 0
row_C
Text GLabel 7200 4050 2 51 Input ~ 0
strobe
Text GLabel 7200 2800 2 51 Input ~ 0
strobe
$Comp
L CONN_02X08 Panel-3
U 1 1 54F3E6D5
P 6950 5250
F 0 "Panel-3" H 6950 5700 50 0000 C CNN
F 1 "CONN_02X08" V 6950 5250 50 0000 C CNN
F 2 "Pin_Headers:Pin_Header_Straight_2x08" H 6950 4050 60 0001 C CNN
F 3 "" H 6950 4050 60 0000 C CNN
1 6950 5250
1 0 0 -1
$EndComp
Wire Wire Line
7200 5200 7750 5200
$Comp
L GND #PWR010
U 1 1 54F3E6DC
P 7750 5650
F 0 "#PWR010" H 7750 5650 30 0001 C CNN
F 1 "GND" H 7750 5580 30 0001 C CNN
F 2 "" H 7750 5650 60 0000 C CNN
F 3 "" H 7750 5650 60 0000 C CNN
1 7750 5650
1 0 0 -1
$EndComp
Wire Wire Line
7200 5600 7750 5600
Wire Wire Line
7200 5000 7750 5000
Text GLabel 7200 5100 2 51 Input ~ 0
p2_g2
Wire Wire Line
7750 5000 7750 5650
Connection ~ 7750 5600
Connection ~ 7750 5200
Text GLabel 7200 4900 2 51 Input ~ 0
p2_g1
Text GLabel 7200 5300 2 51 Input ~ 0
row_B
Text GLabel 7200 5400 2 51 Input ~ 0
row_D
Text GLabel 6700 4900 0 51 Input ~ 0
p2_r1
Text GLabel 6700 5000 0 51 Input ~ 0
p2_b1
Text GLabel 6700 5100 0 51 Input ~ 0
p2_r2
Text GLabel 6700 5200 0 51 Input ~ 0
p2_b2
Text GLabel 6700 5500 0 51 Input ~ 0
clock
Text GLabel 6700 5600 0 51 Input ~ 0
OE
Text GLabel 6700 5300 0 51 Input ~ 0
row_A
Text GLabel 6700 5400 0 51 Input ~ 0
row_C
Text GLabel 7200 5500 2 51 Input ~ 0
strobe
NoConn ~ 4250 2750
NoConn ~ 4250 3150
NoConn ~ 4250 3550
NoConn ~ 4250 4050
NoConn ~ 4750 4050
NoConn ~ 4750 4150
NoConn ~ 4750 3150
$Comp
L PWR_FLAG #FLG011
U 1 1 557B29FD
P 4950 2750
F 0 "#FLG011" H 4950 2845 50 0001 C CNN
F 1 "PWR_FLAG" H 4950 2930 50 0000 C CNN
F 2 "" H 4950 2750 60 0000 C CNN
F 3 "" H 4950 2750 60 0000 C CNN
1 4950 2750
1 0 0 -1
$EndComp
$Comp
L PWR_FLAG #FLG012
U 1 1 557B2BFC
P 4200 4700
F 0 "#FLG012" H 4200 4795 50 0001 C CNN
F 1 "PWR_FLAG" H 4200 4880 50 0000 C CNN
F 2 "" H 4200 4700 60 0000 C CNN
F 3 "" H 4200 4700 60 0000 C CNN
1 4200 4700
-1 0 0 1
$EndComp
Wire Wire Line
4200 4650 4200 4700
Connection ~ 4200 4650
$EndSCHEMATC

@ -0,0 +1,3 @@
include ../kicad-scripts/makefile.inc
passive-rpi-hub75-adapter-fab.zip:

@ -0,0 +1,17 @@
Adapter PCB to connect to Rasbperry Pi1
=======================================
* Passive board. Simple, but might need to define `--led-slowdown-gpio` if you see
glitches.
* Supports one panel connected to a 26 pin Raspberry Pi 1. If you have a newer RPi,
check out the [Passive 3](../passive-3) or [Active 3](../active-3) adapter.
* Open source KiCAD PCB EDA format.
* (not very pretty layout, was just lazy and let the auto-router do it).
* The FAB files are provided as [passive-rpi-hub75-adapter-fab.zip](./passive-rpi-hub75-adapter-fab.zip)
This board is [shared on OSH Park][osh-passive-rpi] (not affiliated)
![Preview][rendering]
[rendering]: ../../img/passive-rpi1-pcb.png
[osh-passive-rpi]: https://oshpark.com/shared_projects/afEA1gNt

@ -0,0 +1,150 @@
EESchema-LIBRARY Version 2.3
#encoding utf-8
#
# CONN_02X08
#
DEF CONN_02X08 P 0 1 Y N 1 F N
F0 "P" 0 450 50 H V C CNN
F1 "CONN_02X08" 0 0 50 V V C CNN
F2 "" 0 -1200 60 H V C CNN
F3 "" 0 -1200 60 H V C CNN
$FPLIST
Pin_Header_Straight_2X08
Pin_Header_Angled_2X08
Socket_Strip_Straight_2X08
Socket_Strip_Angled_2X08
$ENDFPLIST
DRAW
S -100 -345 -50 -355 0 1 0 N
S -100 -245 -50 -255 0 1 0 N
S -100 -145 -50 -155 0 1 0 N
S -100 -45 -50 -55 0 1 0 N
S -100 55 -50 45 0 1 0 N
S -100 155 -50 145 0 1 0 N
S -100 255 -50 245 0 1 0 N
S -100 355 -50 345 0 1 0 N
S -100 400 100 -400 0 1 0 N
S 50 -345 100 -355 0 1 0 N
S 50 -245 100 -255 0 1 0 N
S 50 -145 100 -155 0 1 0 N
S 50 -45 100 -55 0 1 0 N
S 50 55 100 45 0 1 0 N
S 50 155 100 145 0 1 0 N
S 50 255 100 245 0 1 0 N
S 50 355 100 345 0 1 0 N
X P1 1 -250 350 150 R 50 50 1 1 P
X P2 2 250 350 150 L 50 50 1 1 P
X P3 3 -250 250 150 R 50 50 1 1 P
X P4 4 250 250 150 L 50 50 1 1 P
X P5 5 -250 150 150 R 50 50 1 1 P
X P6 6 250 150 150 L 50 50 1 1 P
X P7 7 -250 50 150 R 50 50 1 1 P
X P8 8 250 50 150 L 50 50 1 1 P
X P9 9 -250 -50 150 R 50 50 1 1 P
X P10 10 250 -50 150 L 50 50 1 1 P
X P11 11 -250 -150 150 R 50 50 1 1 P
X P12 12 250 -150 150 L 50 50 1 1 P
X P13 13 -250 -250 150 R 50 50 1 1 P
X P14 14 250 -250 150 L 50 50 1 1 P
X P15 15 -250 -350 150 R 50 50 1 1 P
X P16 16 250 -350 150 L 50 50 1 1 P
ENDDRAW
ENDDEF
#
# CONN_02X13
#
DEF CONN_02X13 P 0 1 Y N 1 F N
F0 "P" 0 700 50 H V C CNN
F1 "CONN_02X13" 0 0 50 V V C CNN
F2 "" 0 -1150 60 H V C CNN
F3 "" 0 -1150 60 H V C CNN
$FPLIST
Pin_Header_Straight_2X13
Pin_Header_Angled_2X13
Socket_Strip_Straight_2X13
Socket_Strip_Angled_2X13
$ENDFPLIST
DRAW
S -100 -595 -50 -605 0 1 0 N
S -100 -495 -50 -505 0 1 0 N
S -100 -395 -50 -405 0 1 0 N
S -100 -295 -50 -305 0 1 0 N
S -100 -195 -50 -205 0 1 0 N
S -100 -95 -50 -105 0 1 0 N
S -100 5 -50 -5 0 1 0 N
S -100 105 -50 95 0 1 0 N
S -100 205 -50 195 0 1 0 N
S -100 305 -50 295 0 1 0 N
S -100 405 -50 395 0 1 0 N
S -100 505 -50 495 0 1 0 N
S -100 605 -50 595 0 1 0 N
S -100 650 100 -650 0 1 0 N
S 50 -595 100 -605 0 1 0 N
S 50 -495 100 -505 0 1 0 N
S 50 -395 100 -405 0 1 0 N
S 50 -295 100 -305 0 1 0 N
S 50 -195 100 -205 0 1 0 N
S 50 -95 100 -105 0 1 0 N
S 50 5 100 -5 0 1 0 N
S 50 105 100 95 0 1 0 N
S 50 205 100 195 0 1 0 N
S 50 305 100 295 0 1 0 N
S 50 405 100 395 0 1 0 N
S 50 505 100 495 0 1 0 N
S 50 605 100 595 0 1 0 N
X P1 1 -250 600 150 R 50 50 1 1 P
X P2 2 250 600 150 L 50 50 1 1 P
X P3 3 -250 500 150 R 50 50 1 1 P
X P4 4 250 500 150 L 50 50 1 1 P
X P5 5 -250 400 150 R 50 50 1 1 P
X P6 6 250 400 150 L 50 50 1 1 P
X P7 7 -250 300 150 R 50 50 1 1 P
X P8 8 250 300 150 L 50 50 1 1 P
X P9 9 -250 200 150 R 50 50 1 1 P
X P10 10 250 200 150 L 50 50 1 1 P
X P20 20 250 -300 150 L 50 50 1 1 P
X P11 11 -250 100 150 R 50 50 1 1 P
X P21 21 -250 -400 150 R 50 50 1 1 P
X P12 12 250 100 150 L 50 50 1 1 P
X P22 22 250 -400 150 L 50 50 1 1 P
X P13 13 -250 0 150 R 50 50 1 1 P
X P23 23 -250 -500 150 R 50 50 1 1 P
X P14 14 250 0 150 L 50 50 1 1 P
X P24 24 250 -500 150 L 50 50 1 1 P
X P15 15 -250 -100 150 R 50 50 1 1 P
X P25 25 -250 -600 150 R 50 50 1 1 P
X P16 16 250 -100 150 L 50 50 1 1 P
X P26 26 250 -600 150 L 50 50 1 1 P
X P17 17 -250 -200 150 R 50 50 1 1 P
X P18 18 250 -200 150 L 50 50 1 1 P
X P19 19 -250 -300 150 R 50 50 1 1 P
ENDDRAW
ENDDEF
#
# GND
#
DEF GND #PWR 0 0 Y Y 1 F P
F0 "#PWR" 0 -250 50 H I C CNN
F1 "GND" 0 -150 50 H V C CNN
F2 "" 0 0 60 H V C CNN
F3 "" 0 0 60 H V C CNN
DRAW
P 6 0 1 0 0 0 0 -50 50 -50 0 -100 -50 -50 0 -50 N
X GND 1 0 0 0 D 50 50 1 1 W N
ENDDRAW
ENDDEF
#
# PWR_FLAG
#
DEF PWR_FLAG #FLG 0 0 N N 1 F P
F0 "#FLG" 0 95 50 H I C CNN
F1 "PWR_FLAG" 0 180 50 H V C CNN
F2 "" 0 0 60 H V C CNN
F3 "" 0 0 60 H V C CNN
DRAW
X pwr 1 0 0 0 U 20 20 0 0 w
P 6 0 1 0 0 0 0 50 -75 100 0 150 75 100 0 50 N
ENDDRAW
ENDDEF
#
#End Library

@ -0,0 +1,591 @@
(kicad_pcb (version 4) (host pcbnew "(2015-10-16 BZR 6271, Git e177d75)-product")
(general
(links 19)
(no_connects 0)
(area 83.657 38.37 122.550001 58.502333)
(thickness 1.6)
(drawings 9)
(tracks 128)
(zones 0)
(modules 2)
(nets 15)
)
(page A4)
(layers
(0 F.Cu signal)
(31 B.Cu signal)
(32 B.Adhes user)
(33 F.Adhes user)
(34 B.Paste user)
(35 F.Paste user)
(36 B.SilkS user)
(37 F.SilkS user)
(38 B.Mask user)
(39 F.Mask user)
(40 Dwgs.User user)
(41 Cmts.User user)
(42 Eco1.User user)
(43 Eco2.User user)
(44 Edge.Cuts user)
(45 Margin user)
(46 B.CrtYd user)
(47 F.CrtYd user)
(48 B.Fab user)
(49 F.Fab user)
)
(setup
(last_trace_width 0.254)
(trace_clearance 0.254)
(zone_clearance 0.508)
(zone_45_only no)
(trace_min 0.254)
(segment_width 0.2)
(edge_width 0.1)
(via_size 0.889)
(via_drill 0.635)
(via_min_size 0.889)
(via_min_drill 0.508)
(uvia_size 0.508)
(uvia_drill 0.127)
(uvias_allowed no)
(uvia_min_size 0.508)
(uvia_min_drill 0.127)
(pcb_text_width 0.3)
(pcb_text_size 1.5 1.5)
(mod_edge_width 0.15)
(mod_text_size 1 1)
(mod_text_width 0.15)
(pad_size 4.064 4.064)
(pad_drill 3.048)
(pad_to_mask_clearance 0)
(aux_axis_origin 0 0)
(visible_elements FFFFEF7F)
(pcbplotparams
(layerselection 0x010f0_80000001)
(usegerberextensions true)
(excludeedgelayer true)
(linewidth 0.100000)
(plotframeref false)
(viasonmask false)
(mode 1)
(useauxorigin false)
(hpglpennumber 1)
(hpglpenspeed 20)
(hpglpendiameter 15)
(hpglpenoverlay 2)
(psnegative false)
(psa4output false)
(plotreference true)
(plotvalue true)
(plotinvisibletext false)
(padsonsilk false)
(subtractmaskfromsilk false)
(outputformat 1)
(mirror false)
(drillshape 0)
(scaleselection 1)
(outputdirectory fab/))
)
(net 0 "")
(net 1 GND)
(net 2 strobe)
(net 3 p0_r1)
(net 4 p0_g1)
(net 5 OE)
(net 6 p0_b1)
(net 7 p0_r2)
(net 8 p0_g2)
(net 9 row_D)
(net 10 row_C)
(net 11 p0_b2)
(net 12 clock)
(net 13 row_B)
(net 14 row_A)
(net_class Default "This is the default net class."
(clearance 0.254)
(trace_width 0.254)
(via_dia 0.889)
(via_drill 0.635)
(uvia_dia 0.508)
(uvia_drill 0.127)
(add_net OE)
(add_net clock)
(add_net p0_b1)
(add_net p0_b2)
(add_net p0_g1)
(add_net p0_g2)
(add_net p0_r1)
(add_net p0_r2)
(add_net row_A)
(add_net row_B)
(add_net row_C)
(add_net row_D)
(add_net strobe)
)
(net_class power ""
(clearance 0.254)
(trace_width 0.254)
(via_dia 0.889)
(via_drill 0.635)
(uvia_dia 0.508)
(uvia_drill 0.127)
(add_net GND)
)
(module Pin_Headers:Pin_Header_Straight_2x08 (layer F.Cu) (tedit 557E1BCB) (tstamp 54F3AB27)
(at 95.25 53.34 90)
(descr "Through hole pin header")
(tags "pin header")
(path /54ECB236)
(fp_text reference Panel-1 (at 1.27 -2.54 90) (layer F.SilkS) hide
(effects (font (size 1 1) (thickness 0.15)))
)
(fp_text value CONN_02X08 (at 0 -3.1 90) (layer F.SilkS) hide
(effects (font (size 1 1) (thickness 0.15)))
)
(fp_line (start -1.75 -1.75) (end -1.75 19.55) (layer F.CrtYd) (width 0.05))
(fp_line (start 4.3 -1.75) (end 4.3 19.55) (layer F.CrtYd) (width 0.05))
(fp_line (start -1.75 -1.75) (end 4.3 -1.75) (layer F.CrtYd) (width 0.05))
(fp_line (start -1.75 19.55) (end 4.3 19.55) (layer F.CrtYd) (width 0.05))
(fp_line (start 3.81 19.05) (end 3.81 -1.27) (layer F.SilkS) (width 0.15))
(fp_line (start -1.27 1.27) (end -1.27 19.05) (layer F.SilkS) (width 0.15))
(fp_line (start 3.81 19.05) (end -1.27 19.05) (layer F.SilkS) (width 0.15))
(fp_line (start 3.81 -1.27) (end 1.27 -1.27) (layer F.SilkS) (width 0.15))
(fp_line (start 0 -1.55) (end -1.55 -1.55) (layer F.SilkS) (width 0.15))
(fp_line (start 1.27 -1.27) (end 1.27 1.27) (layer F.SilkS) (width 0.15))
(fp_line (start 1.27 1.27) (end -1.27 1.27) (layer F.SilkS) (width 0.15))
(fp_line (start -1.55 -1.55) (end -1.55 0) (layer F.SilkS) (width 0.15))
(pad 1 thru_hole rect (at 0 0 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 3 p0_r1))
(pad 2 thru_hole oval (at 2.54 0 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 4 p0_g1))
(pad 3 thru_hole oval (at 0 2.54 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 6 p0_b1))
(pad 4 thru_hole oval (at 2.54 2.54 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 1 GND))
(pad 5 thru_hole oval (at 0 5.08 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 7 p0_r2))
(pad 6 thru_hole oval (at 2.54 5.08 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 8 p0_g2))
(pad 7 thru_hole oval (at 0 7.62 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 11 p0_b2))
(pad 8 thru_hole oval (at 2.54 7.62 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 1 GND))
(pad 9 thru_hole oval (at 0 10.16 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 14 row_A))
(pad 10 thru_hole oval (at 2.54 10.16 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 13 row_B))
(pad 11 thru_hole oval (at 0 12.7 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 10 row_C))
(pad 12 thru_hole oval (at 2.54 12.7 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 9 row_D))
(pad 13 thru_hole oval (at 0 15.24 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 12 clock))
(pad 14 thru_hole oval (at 2.54 15.24 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 2 strobe))
(pad 15 thru_hole oval (at 0 17.78 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 5 OE))
(pad 16 thru_hole oval (at 2.54 17.78 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 1 GND))
(model Pin_Headers.3dshapes/Pin_Header_Straight_2x08.wrl
(at (xyz 0.05 -0.35 0))
(scale (xyz 1 1 1))
(rotate (xyz 0 0 90))
)
)
(module Pin_Headers:Pin_Header_Straight_2x13 (layer F.Cu) (tedit 557E1BBF) (tstamp 557E12E7)
(at 88.9 43.815 90)
(descr "Through hole pin header")
(tags "pin header")
(path /54ECB2B7)
(fp_text reference P1 (at -2.413 -0.508 180) (layer F.SilkS)
(effects (font (size 1 1) (thickness 0.15)))
)
(fp_text value RPi-Header (at -0.381 -4.064 90) (layer F.Fab) hide
(effects (font (size 1 1) (thickness 0.15)))
)
(fp_line (start -1.75 -1.75) (end -1.75 32.25) (layer F.CrtYd) (width 0.05))
(fp_line (start 4.3 -1.75) (end 4.3 32.25) (layer F.CrtYd) (width 0.05))
(fp_line (start -1.75 -1.75) (end 4.3 -1.75) (layer F.CrtYd) (width 0.05))
(fp_line (start -1.75 32.25) (end 4.3 32.25) (layer F.CrtYd) (width 0.05))
(fp_line (start 3.81 -1.27) (end 3.81 31.75) (layer F.SilkS) (width 0.15))
(fp_line (start -1.27 1.27) (end -1.27 31.75) (layer F.SilkS) (width 0.15))
(fp_line (start 3.81 31.75) (end -1.27 31.75) (layer F.SilkS) (width 0.15))
(fp_line (start 3.81 -1.27) (end 1.27 -1.27) (layer F.SilkS) (width 0.15))
(fp_line (start 0 -1.55) (end -1.55 -1.55) (layer F.SilkS) (width 0.15))
(fp_line (start 1.27 -1.27) (end 1.27 1.27) (layer F.SilkS) (width 0.15))
(fp_line (start 1.27 1.27) (end -1.27 1.27) (layer F.SilkS) (width 0.15))
(fp_line (start -1.55 -1.55) (end -1.55 0) (layer F.SilkS) (width 0.15))
(pad 1 thru_hole rect (at 0 0 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS))
(pad 2 thru_hole oval (at 2.54 0 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS))
(pad 3 thru_hole oval (at 0 2.54 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS))
(pad 4 thru_hole oval (at 2.54 2.54 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS))
(pad 5 thru_hole oval (at 0 5.08 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS))
(pad 6 thru_hole oval (at 2.54 5.08 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 1 GND))
(pad 7 thru_hole oval (at 0 7.62 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 2 strobe))
(pad 8 thru_hole oval (at 2.54 7.62 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS))
(pad 9 thru_hole oval (at 0 10.16 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS))
(pad 10 thru_hole oval (at 2.54 10.16 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS))
(pad 11 thru_hole oval (at 0 12.7 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 12 clock))
(pad 12 thru_hole oval (at 2.54 12.7 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 5 OE))
(pad 13 thru_hole oval (at 0 15.24 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 4 p0_g1))
(pad 14 thru_hole oval (at 2.54 15.24 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 1 GND))
(pad 15 thru_hole oval (at 0 17.78 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 14 row_A))
(pad 16 thru_hole oval (at 2.54 17.78 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 13 row_B))
(pad 17 thru_hole oval (at 0 20.32 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS))
(pad 18 thru_hole oval (at 2.54 20.32 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 10 row_C))
(pad 19 thru_hole oval (at 0 22.86 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 11 p0_b2))
(pad 20 thru_hole oval (at 2.54 22.86 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 1 GND))
(pad 21 thru_hole oval (at 0 25.4 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 8 p0_g2))
(pad 22 thru_hole oval (at 2.54 25.4 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 9 row_D))
(pad 23 thru_hole oval (at 0 27.94 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 3 p0_r1))
(pad 24 thru_hole oval (at 2.54 27.94 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 7 p0_r2))
(pad 25 thru_hole oval (at 0 30.48 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 1 GND))
(pad 26 thru_hole oval (at 2.54 30.48 90) (size 1.7272 1.7272) (drill 1.016) (layers *.Cu *.Mask F.SilkS)
(net 6 p0_b1))
)
(gr_text %%gitversion%% (at 118.872 47.244) (layer B.SilkS)
(effects (font (size 1.2 1.2) (thickness 0.2)) (justify left mirror))
)
(gr_text "↖RPi corner" (at 90.17 39.37) (layer F.SilkS)
(effects (font (size 1 1) (thickness 0.2)))
)
(gr_text "↑video connector" (at 121.5 47.5 270) (layer F.SilkS)
(effects (font (size 1 1) (thickness 0.2)))
)
(gr_text github.com/hzeller/rpi-rgb-led-matrix (at 104 47.5) (layer F.SilkS)
(effects (font (size 1.1 1.1) (thickness 0.2)))
)
(gr_line (start 102.235 56.642) (end 106.045 56.642) (angle 90) (layer F.SilkS) (width 1.5))
(gr_line (start 85.5 57.5) (end 85.5 38.5) (angle 90) (layer Edge.Cuts) (width 0.1) (tstamp 556C01FD))
(gr_line (start 85.5 38.5) (end 122.5 38.5) (angle 90) (layer Edge.Cuts) (width 0.1))
(gr_line (start 122.5 38.5) (end 122.5 57.5) (angle 90) (layer Edge.Cuts) (width 0.1) (tstamp 556BCFD7))
(gr_line (start 122.5 57.5) (end 85.5 57.5) (angle 90) (layer Edge.Cuts) (width 0.1) (tstamp 556BCFD8))
(segment (start 104.14 41.275) (end 105.3849 41.275) (width 0.254) (layer F.Cu) (net 1))
(segment (start 112.3295 41.275) (end 111.0595 42.545) (width 0.254) (layer F.Cu) (net 1))
(segment (start 111.0595 42.545) (end 106.1391 42.545) (width 0.254) (layer F.Cu) (net 1))
(segment (start 106.1391 42.545) (end 105.3849 41.7908) (width 0.254) (layer F.Cu) (net 1))
(segment (start 105.3849 41.7908) (end 105.3849 41.275) (width 0.254) (layer F.Cu) (net 1))
(segment (start 112.3295 41.275) (end 113.0049 41.275) (width 0.254) (layer F.Cu) (net 1))
(segment (start 111.76 41.275) (end 112.3295 41.275) (width 0.254) (layer F.Cu) (net 1))
(segment (start 118.6854 43.8997) (end 117.3307 42.545) (width 0.254) (layer F.Cu) (net 1))
(segment (start 117.3307 42.545) (end 113.7591 42.545) (width 0.254) (layer F.Cu) (net 1))
(segment (start 113.7591 42.545) (end 113.0049 41.7908) (width 0.254) (layer F.Cu) (net 1))
(segment (start 113.0049 41.7908) (end 113.0049 41.275) (width 0.254) (layer F.Cu) (net 1))
(segment (start 118.8105 43.815) (end 118.7258 43.8997) (width 0.254) (layer F.Cu) (net 1))
(segment (start 118.7258 43.8997) (end 118.6854 43.8997) (width 0.254) (layer F.Cu) (net 1))
(segment (start 113.03 49.5551) (end 118.6854 43.8997) (width 0.254) (layer F.Cu) (net 1))
(segment (start 113.03 50.8) (end 113.03 49.5551) (width 0.254) (layer F.Cu) (net 1))
(segment (start 119.38 43.815) (end 118.8105 43.815) (width 0.254) (layer F.Cu) (net 1))
(segment (start 97.79 43.2214) (end 97.79 49.5492) (width 0.254) (layer B.Cu) (net 1))
(segment (start 95.2249 41.275) (end 95.2249 41.7419) (width 0.254) (layer B.Cu) (net 1))
(segment (start 95.2249 41.7419) (end 96.0531 42.5701) (width 0.254) (layer B.Cu) (net 1))
(segment (start 96.0531 42.5701) (end 97.1387 42.5701) (width 0.254) (layer B.Cu) (net 1))
(segment (start 97.1387 42.5701) (end 97.79 43.2214) (width 0.254) (layer B.Cu) (net 1))
(segment (start 104.14 41.275) (end 102.895 42.52) (width 0.254) (layer B.Cu) (net 1))
(segment (start 102.895 42.52) (end 98.4914 42.52) (width 0.254) (layer B.Cu) (net 1))
(segment (start 98.4914 42.52) (end 97.79 43.2214) (width 0.254) (layer B.Cu) (net 1))
(segment (start 97.79 49.5492) (end 97.79 50.8) (width 0.254) (layer B.Cu) (net 1))
(segment (start 97.79 49.5492) (end 100.8698 49.5492) (width 0.254) (layer B.Cu) (net 1))
(segment (start 100.8698 49.5492) (end 101.6251 50.3045) (width 0.254) (layer B.Cu) (net 1))
(segment (start 101.6251 50.3045) (end 101.6251 50.8) (width 0.254) (layer B.Cu) (net 1))
(segment (start 102.87 50.8) (end 101.6251 50.8) (width 0.254) (layer B.Cu) (net 1))
(segment (start 93.98 41.275) (end 95.2249 41.275) (width 0.254) (layer B.Cu) (net 1))
(segment (start 110.49 50.8) (end 109.2451 50.8) (width 0.254) (layer F.Cu) (net 2))
(segment (start 96.52 43.815) (end 96.52 45.0599) (width 0.254) (layer F.Cu) (net 2))
(segment (start 96.52 45.0599) (end 93.9931 47.5868) (width 0.254) (layer F.Cu) (net 2))
(segment (start 93.9931 47.5868) (end 93.9931 54.4202) (width 0.254) (layer F.Cu) (net 2))
(segment (start 93.9931 54.4202) (end 94.1662 54.5933) (width 0.254) (layer F.Cu) (net 2))
(segment (start 94.1662 54.5933) (end 100.9179 54.5933) (width 0.254) (layer F.Cu) (net 2))
(segment (start 100.9179 54.5933) (end 101.6 53.9112) (width 0.254) (layer F.Cu) (net 2))
(segment (start 101.6 53.9112) (end 101.6 52.835) (width 0.254) (layer F.Cu) (net 2))
(segment (start 101.6 52.835) (end 102.365 52.07) (width 0.254) (layer F.Cu) (net 2))
(segment (start 102.365 52.07) (end 108.4909 52.07) (width 0.254) (layer F.Cu) (net 2))
(segment (start 108.4909 52.07) (end 109.2451 51.3158) (width 0.254) (layer F.Cu) (net 2))
(segment (start 109.2451 51.3158) (end 109.2451 50.8) (width 0.254) (layer F.Cu) (net 2))
(segment (start 95.25 53.34) (end 96.4949 53.34) (width 0.254) (layer F.Cu) (net 3))
(segment (start 116.84 43.815) (end 115.5951 43.815) (width 0.254) (layer F.Cu) (net 3))
(segment (start 115.5951 43.815) (end 115.5951 44.2818) (width 0.254) (layer F.Cu) (net 3))
(segment (start 115.5951 44.2818) (end 111.8058 48.0711) (width 0.254) (layer F.Cu) (net 3))
(segment (start 111.8058 48.0711) (end 103.7779 48.0711) (width 0.254) (layer F.Cu) (net 3))
(segment (start 103.7779 48.0711) (end 101.6 50.249) (width 0.254) (layer F.Cu) (net 3))
(segment (start 101.6 50.249) (end 101.6 51.295) (width 0.254) (layer F.Cu) (net 3))
(segment (start 101.6 51.295) (end 100.7999 52.0951) (width 0.254) (layer F.Cu) (net 3))
(segment (start 100.7999 52.0951) (end 97.2729 52.0951) (width 0.254) (layer F.Cu) (net 3))
(segment (start 97.2729 52.0951) (end 96.4949 52.8731) (width 0.254) (layer F.Cu) (net 3))
(segment (start 96.4949 52.8731) (end 96.4949 53.34) (width 0.254) (layer F.Cu) (net 3))
(segment (start 95.25 50.8) (end 96.4949 50.8) (width 0.254) (layer F.Cu) (net 4))
(segment (start 104.14 43.815) (end 102.8951 43.815) (width 0.254) (layer F.Cu) (net 4))
(segment (start 102.8951 43.815) (end 102.8951 44.2819) (width 0.254) (layer F.Cu) (net 4))
(segment (start 102.8951 44.2819) (end 102.1171 45.0599) (width 0.254) (layer F.Cu) (net 4))
(segment (start 102.1171 45.0599) (end 101.7192 45.0599) (width 0.254) (layer F.Cu) (net 4))
(segment (start 101.7192 45.0599) (end 96.4949 50.2842) (width 0.254) (layer F.Cu) (net 4))
(segment (start 96.4949 50.2842) (end 96.4949 50.8) (width 0.254) (layer F.Cu) (net 4))
(segment (start 113.03 53.34) (end 113.03 52.0951) (width 0.254) (layer B.Cu) (net 5))
(segment (start 101.6 41.275) (end 102.8449 41.275) (width 0.254) (layer B.Cu) (net 5))
(segment (start 102.8449 41.275) (end 102.8449 40.8082) (width 0.254) (layer B.Cu) (net 5))
(segment (start 102.8449 40.8082) (end 104.1729 39.4802) (width 0.254) (layer B.Cu) (net 5))
(segment (start 104.1729 39.4802) (end 114.3167 39.4802) (width 0.254) (layer B.Cu) (net 5))
(segment (start 114.3167 39.4802) (end 115.57 40.7335) (width 0.254) (layer B.Cu) (net 5))
(segment (start 115.57 40.7335) (end 115.57 50.0709) (width 0.254) (layer B.Cu) (net 5))
(segment (start 115.57 50.0709) (end 113.5458 52.0951) (width 0.254) (layer B.Cu) (net 5))
(segment (start 113.5458 52.0951) (end 113.03 52.0951) (width 0.254) (layer B.Cu) (net 5))
(segment (start 97.79 53.34) (end 99.0349 53.34) (width 0.254) (layer B.Cu) (net 6))
(segment (start 119.38 41.275) (end 119.38 42.5199) (width 0.254) (layer B.Cu) (net 6))
(segment (start 119.38 42.5199) (end 119.8468 42.5199) (width 0.254) (layer B.Cu) (net 6))
(segment (start 119.8468 42.5199) (end 120.6524 43.3255) (width 0.254) (layer B.Cu) (net 6))
(segment (start 120.6524 43.3255) (end 120.6524 48.1982) (width 0.254) (layer B.Cu) (net 6))
(segment (start 120.6524 48.1982) (end 112.6587 56.1919) (width 0.254) (layer B.Cu) (net 6))
(segment (start 112.6587 56.1919) (end 101.371 56.1919) (width 0.254) (layer B.Cu) (net 6))
(segment (start 101.371 56.1919) (end 99.0349 53.8558) (width 0.254) (layer B.Cu) (net 6))
(segment (start 99.0349 53.8558) (end 99.0349 53.34) (width 0.254) (layer B.Cu) (net 6))
(segment (start 100.33 53.34) (end 101.5749 53.34) (width 0.254) (layer B.Cu) (net 7))
(segment (start 116.84 41.275) (end 116.84 42.5199) (width 0.254) (layer B.Cu) (net 7))
(segment (start 116.84 42.5199) (end 117.3068 42.5199) (width 0.254) (layer B.Cu) (net 7))
(segment (start 117.3068 42.5199) (end 118.11 43.3231) (width 0.254) (layer B.Cu) (net 7))
(segment (start 118.11 43.3231) (end 118.11 50.0216) (width 0.254) (layer B.Cu) (net 7))
(segment (start 118.11 50.0216) (end 112.4481 55.6835) (width 0.254) (layer B.Cu) (net 7))
(segment (start 112.4481 55.6835) (end 103.4026 55.6835) (width 0.254) (layer B.Cu) (net 7))
(segment (start 103.4026 55.6835) (end 101.5749 53.8558) (width 0.254) (layer B.Cu) (net 7))
(segment (start 101.5749 53.8558) (end 101.5749 53.34) (width 0.254) (layer B.Cu) (net 7))
(segment (start 114.3 43.815) (end 113.0551 43.815) (width 0.254) (layer F.Cu) (net 8))
(segment (start 113.0551 43.815) (end 113.0551 44.2819) (width 0.254) (layer F.Cu) (net 8))
(segment (start 113.0551 44.2819) (end 112.2771 45.0599) (width 0.254) (layer F.Cu) (net 8))
(segment (start 112.2771 45.0599) (end 106.0701 45.0599) (width 0.254) (layer F.Cu) (net 8))
(segment (start 106.0701 45.0599) (end 100.33 50.8) (width 0.254) (layer F.Cu) (net 8))
(segment (start 114.3 41.275) (end 113.0551 41.275) (width 0.254) (layer B.Cu) (net 9))
(segment (start 113.0551 41.275) (end 113.0551 40.8082) (width 0.254) (layer B.Cu) (net 9))
(segment (start 113.0551 40.8082) (end 112.2605 40.0136) (width 0.254) (layer B.Cu) (net 9))
(segment (start 112.2605 40.0136) (end 108.7063 40.0136) (width 0.254) (layer B.Cu) (net 9))
(segment (start 108.7063 40.0136) (end 107.95 40.7699) (width 0.254) (layer B.Cu) (net 9))
(segment (start 107.95 40.7699) (end 107.95 50.8) (width 0.254) (layer B.Cu) (net 9))
(segment (start 107.95 53.34) (end 107.95 52.0951) (width 0.254) (layer B.Cu) (net 10))
(segment (start 109.22 41.275) (end 109.22 42.5199) (width 0.254) (layer B.Cu) (net 10))
(segment (start 109.22 42.5199) (end 109.6868 42.5199) (width 0.254) (layer B.Cu) (net 10))
(segment (start 109.6868 42.5199) (end 110.49 43.3231) (width 0.254) (layer B.Cu) (net 10))
(segment (start 110.49 43.3231) (end 110.49 47.1986) (width 0.254) (layer B.Cu) (net 10))
(segment (start 110.49 47.1986) (end 109.22 48.4686) (width 0.254) (layer B.Cu) (net 10))
(segment (start 109.22 48.4686) (end 109.22 51.3409) (width 0.254) (layer B.Cu) (net 10))
(segment (start 109.22 51.3409) (end 108.4658 52.0951) (width 0.254) (layer B.Cu) (net 10))
(segment (start 108.4658 52.0951) (end 107.95 52.0951) (width 0.254) (layer B.Cu) (net 10))
(segment (start 111.76 43.815) (end 111.76 53.8323) (width 0.254) (layer B.Cu) (net 11))
(segment (start 111.76 53.8323) (end 110.4228 55.1695) (width 0.254) (layer B.Cu) (net 11))
(segment (start 110.4228 55.1695) (end 104.6995 55.1695) (width 0.254) (layer B.Cu) (net 11))
(segment (start 104.6995 55.1695) (end 102.87 53.34) (width 0.254) (layer B.Cu) (net 11))
(segment (start 110.49 53.34) (end 109.2451 53.34) (width 0.254) (layer B.Cu) (net 12))
(segment (start 101.6 43.815) (end 101.6 45.0599) (width 0.254) (layer B.Cu) (net 12))
(segment (start 101.6 45.0599) (end 104.14 47.5999) (width 0.254) (layer B.Cu) (net 12))
(segment (start 104.14 47.5999) (end 104.14 53.8544) (width 0.254) (layer B.Cu) (net 12))
(segment (start 104.14 53.8544) (end 104.94 54.6544) (width 0.254) (layer B.Cu) (net 12))
(segment (start 104.94 54.6544) (end 108.4465 54.6544) (width 0.254) (layer B.Cu) (net 12))
(segment (start 108.4465 54.6544) (end 109.2451 53.8558) (width 0.254) (layer B.Cu) (net 12))
(segment (start 109.2451 53.8558) (end 109.2451 53.34) (width 0.254) (layer B.Cu) (net 12))
(segment (start 105.41 50.8) (end 105.41 49.5551) (width 0.254) (layer B.Cu) (net 13))
(segment (start 105.41 49.5551) (end 105.41 43.3231) (width 0.254) (layer B.Cu) (net 13))
(segment (start 105.41 43.3231) (end 106.2132 42.5199) (width 0.254) (layer B.Cu) (net 13))
(segment (start 106.2132 42.5199) (end 106.68 42.5199) (width 0.254) (layer B.Cu) (net 13))
(segment (start 106.68 41.275) (end 106.68 42.5199) (width 0.254) (layer B.Cu) (net 13))
(segment (start 105.41 53.34) (end 105.41 52.0951) (width 0.254) (layer B.Cu) (net 14))
(segment (start 106.68 43.815) (end 106.68 51.3409) (width 0.254) (layer B.Cu) (net 14))
(segment (start 106.68 51.3409) (end 105.9258 52.0951) (width 0.254) (layer B.Cu) (net 14))
(segment (start 105.9258 52.0951) (end 105.41 52.0951) (width 0.254) (layer B.Cu) (net 14))
(zone (net 1) (net_name GND) (layer F.Cu) (tstamp 557E155F) (hatch edge 0.508)
(connect_pads (clearance 0.508))
(min_thickness 0.254)
(fill yes (arc_segments 16) (thermal_gap 0.508) (thermal_bridge_width 0.508))
(polygon
(pts
(xy 122.5 57.5) (xy 85.5 57.5) (xy 85.5 38.5) (xy 122.5 38.5)
)
)
(filled_polygon
(pts
(xy 121.815 56.815) (xy 86.185 56.815) (xy 86.185 39.747041) (xy 88.9 39.747041) (xy 88.326511 39.861115)
(xy 87.84033 40.185971) (xy 87.515474 40.672152) (xy 87.4014 41.245641) (xy 87.4014 41.304359) (xy 87.515474 41.877848)
(xy 87.827301 42.34453) (xy 87.794277 42.350937) (xy 87.581473 42.490727) (xy 87.439023 42.70176) (xy 87.38896 42.9514)
(xy 87.38896 44.6786) (xy 87.435937 44.920723) (xy 87.575727 45.133527) (xy 87.78676 45.275977) (xy 88.0364 45.32604)
(xy 89.7636 45.32604) (xy 90.005723 45.279063) (xy 90.218527 45.139273) (xy 90.360977 44.92824) (xy 90.369179 44.88734)
(xy 90.38033 44.904029) (xy 90.866511 45.228885) (xy 91.44 45.342959) (xy 92.013489 45.228885) (xy 92.49967 44.904029)
(xy 92.71 44.589248) (xy 92.92033 44.904029) (xy 93.406511 45.228885) (xy 93.98 45.342959) (xy 94.553489 45.228885)
(xy 95.03967 44.904029) (xy 95.25 44.589248) (xy 95.46033 44.904029) (xy 95.543001 44.959268) (xy 93.454285 47.047985)
(xy 93.289104 47.295195) (xy 93.2311 47.5868) (xy 93.2311 54.4202) (xy 93.289104 54.711805) (xy 93.404766 54.884905)
(xy 93.454285 54.959015) (xy 93.627384 55.132115) (xy 93.702342 55.1822) (xy 93.874595 55.297296) (xy 94.1662 55.3553)
(xy 100.9179 55.3553) (xy 101.209505 55.297296) (xy 101.456715 55.132115) (xy 102.019822 54.569008) (xy 102.296511 54.753885)
(xy 102.87 54.867959) (xy 103.443489 54.753885) (xy 103.92967 54.429029) (xy 104.14 54.114248) (xy 104.35033 54.429029)
(xy 104.836511 54.753885) (xy 105.41 54.867959) (xy 105.983489 54.753885) (xy 106.46967 54.429029) (xy 106.68 54.114248)
(xy 106.89033 54.429029) (xy 107.376511 54.753885) (xy 107.95 54.867959) (xy 108.523489 54.753885) (xy 109.00967 54.429029)
(xy 109.22 54.114248) (xy 109.43033 54.429029) (xy 109.916511 54.753885) (xy 110.49 54.867959) (xy 111.063489 54.753885)
(xy 111.54967 54.429029) (xy 111.76 54.114248) (xy 111.97033 54.429029) (xy 112.456511 54.753885) (xy 113.03 54.867959)
(xy 113.603489 54.753885) (xy 114.08967 54.429029) (xy 114.414526 53.942848) (xy 114.5286 53.369359) (xy 114.5286 53.310641)
(xy 114.414526 52.737152) (xy 114.08967 52.250971) (xy 113.818839 52.070008) (xy 114.236821 51.68849) (xy 114.484968 51.159027)
(xy 114.364469 50.927) (xy 113.157 50.927) (xy 113.157 50.947) (xy 112.903 50.947) (xy 112.903 50.927)
(xy 112.883 50.927) (xy 112.883 50.673) (xy 112.903 50.673) (xy 112.903 49.466183) (xy 112.670974 49.345042)
(xy 113.389026 49.345042) (xy 113.157 49.466183) (xy 113.157 50.673) (xy 114.364469 50.673) (xy 114.484968 50.440973)
(xy 114.236821 49.91151) (xy 113.804947 49.517312) (xy 113.389026 49.345042) (xy 112.670974 49.345042) (xy 112.255053 49.517312)
(xy 111.823179 49.91151) (xy 111.765664 50.034228) (xy 111.54967 49.710971) (xy 111.063489 49.386115) (xy 110.49 49.272041)
(xy 109.916511 49.386115) (xy 109.43033 49.710971) (xy 109.22 50.025752) (xy 109.00967 49.710971) (xy 108.523489 49.386115)
(xy 107.95 49.272041) (xy 107.376511 49.386115) (xy 106.89033 49.710971) (xy 106.68 50.025752) (xy 106.46967 49.710971)
(xy 105.983489 49.386115) (xy 105.41 49.272041) (xy 104.836511 49.386115) (xy 104.35033 49.710971) (xy 104.134336 50.034228)
(xy 104.076821 49.91151) (xy 103.644947 49.517312) (xy 103.47833 49.448301) (xy 104.093531 48.8331) (xy 111.8058 48.8331)
(xy 112.097405 48.775096) (xy 112.344615 48.609915) (xy 115.942286 45.012245) (xy 116.266511 45.228885) (xy 116.84 45.342959)
(xy 117.413489 45.228885) (xy 117.89967 44.904029) (xy 118.115664 44.580772) (xy 118.173179 44.70349) (xy 118.605053 45.097688)
(xy 119.020974 45.269958) (xy 119.253 45.148817) (xy 119.253 43.942) (xy 119.507 43.942) (xy 119.507 45.148817)
(xy 119.739026 45.269958) (xy 120.154947 45.097688) (xy 120.586821 44.70349) (xy 120.834968 44.174027) (xy 120.714469 43.942)
(xy 119.507 43.942) (xy 119.253 43.942) (xy 119.233 43.942) (xy 119.233 43.688) (xy 119.253 43.688)
(xy 119.253 43.668) (xy 119.507 43.668) (xy 119.507 43.688) (xy 120.714469 43.688) (xy 120.834968 43.455973)
(xy 120.586821 42.92651) (xy 120.168839 42.544992) (xy 120.43967 42.364029) (xy 120.764526 41.877848) (xy 120.8786 41.304359)
(xy 120.8786 41.245641) (xy 120.764526 40.672152) (xy 120.43967 40.185971) (xy 119.953489 39.861115) (xy 119.38 39.747041)
(xy 118.806511 39.861115) (xy 118.32033 40.185971) (xy 118.11 40.500752) (xy 117.89967 40.185971) (xy 117.413489 39.861115)
(xy 116.84 39.747041) (xy 116.266511 39.861115) (xy 115.78033 40.185971) (xy 115.57 40.500752) (xy 115.35967 40.185971)
(xy 114.873489 39.861115) (xy 114.3 39.747041) (xy 113.726511 39.861115) (xy 113.24033 40.185971) (xy 113.024336 40.509228)
(xy 112.966821 40.38651) (xy 112.534947 39.992312) (xy 112.119026 39.820042) (xy 111.887 39.941183) (xy 111.887 41.148)
(xy 111.907 41.148) (xy 111.907 41.402) (xy 111.887 41.402) (xy 111.887 41.422) (xy 111.633 41.422)
(xy 111.633 41.402) (xy 111.613 41.402) (xy 111.613 41.148) (xy 111.633 41.148) (xy 111.633 39.941183)
(xy 111.400974 39.820042) (xy 110.985053 39.992312) (xy 110.553179 40.38651) (xy 110.495664 40.509228) (xy 110.27967 40.185971)
(xy 109.793489 39.861115) (xy 109.22 39.747041) (xy 108.646511 39.861115) (xy 108.16033 40.185971) (xy 107.95 40.500752)
(xy 107.73967 40.185971) (xy 107.253489 39.861115) (xy 106.68 39.747041) (xy 106.106511 39.861115) (xy 105.62033 40.185971)
(xy 105.404336 40.509228) (xy 105.346821 40.38651) (xy 104.914947 39.992312) (xy 104.499026 39.820042) (xy 104.267 39.941183)
(xy 104.267 41.148) (xy 104.287 41.148) (xy 104.287 41.402) (xy 104.267 41.402) (xy 104.267 41.422)
(xy 104.013 41.422) (xy 104.013 41.402) (xy 103.993 41.402) (xy 103.993 41.148) (xy 104.013 41.148)
(xy 104.013 39.941183) (xy 103.780974 39.820042) (xy 103.365053 39.992312) (xy 102.933179 40.38651) (xy 102.875664 40.509228)
(xy 102.65967 40.185971) (xy 102.173489 39.861115) (xy 101.6 39.747041) (xy 101.026511 39.861115) (xy 100.54033 40.185971)
(xy 100.33 40.500752) (xy 100.11967 40.185971) (xy 99.633489 39.861115) (xy 99.06 39.747041) (xy 98.486511 39.861115)
(xy 98.00033 40.185971) (xy 97.79 40.500752) (xy 97.57967 40.185971) (xy 97.093489 39.861115) (xy 96.52 39.747041)
(xy 95.946511 39.861115) (xy 95.46033 40.185971) (xy 95.244336 40.509228) (xy 95.186821 40.38651) (xy 94.754947 39.992312)
(xy 94.339026 39.820042) (xy 94.107 39.941183) (xy 94.107 41.148) (xy 94.127 41.148) (xy 94.127 41.402)
(xy 94.107 41.402) (xy 94.107 41.422) (xy 93.853 41.422) (xy 93.853 41.402) (xy 93.833 41.402)
(xy 93.833 41.148) (xy 93.853 41.148) (xy 93.853 39.941183) (xy 93.620974 39.820042) (xy 93.205053 39.992312)
(xy 92.773179 40.38651) (xy 92.715664 40.509228) (xy 92.49967 40.185971) (xy 92.013489 39.861115) (xy 91.44 39.747041)
(xy 90.866511 39.861115) (xy 90.38033 40.185971) (xy 90.17 40.500752) (xy 89.95967 40.185971) (xy 89.473489 39.861115)
(xy 88.9 39.747041) (xy 86.185 39.747041) (xy 86.185 39.185) (xy 121.815 39.185) (xy 121.815 56.815)
)
)
(filled_polygon
(pts
(xy 102.997 50.673) (xy 103.017 50.673) (xy 103.017 50.927) (xy 102.997 50.927) (xy 102.997 50.947)
(xy 102.743 50.947) (xy 102.743 50.927) (xy 102.723 50.927) (xy 102.723 50.673) (xy 102.743 50.673)
(xy 102.743 50.653) (xy 102.997 50.653) (xy 102.997 50.673)
)
)
(filled_polygon
(pts
(xy 97.917 50.673) (xy 97.937 50.673) (xy 97.937 50.927) (xy 97.917 50.927) (xy 97.917 50.947)
(xy 97.663 50.947) (xy 97.663 50.927) (xy 97.643 50.927) (xy 97.643 50.673) (xy 97.663 50.673)
(xy 97.663 50.653) (xy 97.917 50.653) (xy 97.917 50.673)
)
)
)
(zone (net 1) (net_name GND) (layer B.Cu) (tstamp 557E1560) (hatch edge 0.508)
(connect_pads (clearance 0.508))
(min_thickness 0.254)
(fill yes (arc_segments 16) (thermal_gap 0.508) (thermal_bridge_width 0.508))
(polygon
(pts
(xy 122.5 57.5) (xy 85.5 57.5) (xy 85.5 38.5) (xy 122.5 38.5)
)
)
(filled_polygon
(pts
(xy 102.497714 40.077756) (xy 102.173489 39.861115) (xy 101.6 39.747041) (xy 101.026511 39.861115) (xy 100.54033 40.185971)
(xy 100.33 40.500752) (xy 100.11967 40.185971) (xy 99.633489 39.861115) (xy 99.06 39.747041) (xy 98.486511 39.861115)
(xy 98.00033 40.185971) (xy 97.79 40.500752) (xy 97.57967 40.185971) (xy 97.093489 39.861115) (xy 96.52 39.747041)
(xy 95.946511 39.861115) (xy 95.46033 40.185971) (xy 95.244336 40.509228) (xy 95.186821 40.38651) (xy 94.754947 39.992312)
(xy 94.339026 39.820042) (xy 94.107 39.941183) (xy 94.107 41.148) (xy 94.127 41.148) (xy 94.127 41.402)
(xy 94.107 41.402) (xy 94.107 41.422) (xy 93.853 41.422) (xy 93.853 41.402) (xy 93.833 41.402)
(xy 93.833 41.148) (xy 93.853 41.148) (xy 93.853 39.941183) (xy 93.620974 39.820042) (xy 93.205053 39.992312)
(xy 92.773179 40.38651) (xy 92.715664 40.509228) (xy 92.49967 40.185971) (xy 92.013489 39.861115) (xy 91.44 39.747041)
(xy 90.866511 39.861115) (xy 90.38033 40.185971) (xy 90.17 40.500752) (xy 89.95967 40.185971) (xy 89.473489 39.861115)
(xy 88.9 39.747041) (xy 88.326511 39.861115) (xy 87.84033 40.185971) (xy 87.515474 40.672152) (xy 87.4014 41.245641)
(xy 87.4014 41.304359) (xy 87.515474 41.877848) (xy 87.827301 42.34453) (xy 87.794277 42.350937) (xy 87.581473 42.490727)
(xy 87.439023 42.70176) (xy 87.38896 42.9514) (xy 87.38896 44.6786) (xy 87.435937 44.920723) (xy 87.575727 45.133527)
(xy 87.78676 45.275977) (xy 88.0364 45.32604) (xy 89.7636 45.32604) (xy 90.005723 45.279063) (xy 90.218527 45.139273)
(xy 90.360977 44.92824) (xy 90.369179 44.88734) (xy 90.38033 44.904029) (xy 90.866511 45.228885) (xy 91.44 45.342959)
(xy 92.013489 45.228885) (xy 92.49967 44.904029) (xy 92.71 44.589248) (xy 92.92033 44.904029) (xy 93.406511 45.228885)
(xy 93.98 45.342959) (xy 94.553489 45.228885) (xy 95.03967 44.904029) (xy 95.25 44.589248) (xy 95.46033 44.904029)
(xy 95.946511 45.228885) (xy 96.52 45.342959) (xy 97.093489 45.228885) (xy 97.57967 44.904029) (xy 97.79 44.589248)
(xy 98.00033 44.904029) (xy 98.486511 45.228885) (xy 99.06 45.342959) (xy 99.633489 45.228885) (xy 100.11967 44.904029)
(xy 100.33 44.589248) (xy 100.54033 44.904029) (xy 100.84787 45.109521) (xy 100.896004 45.351505) (xy 101.061185 45.598715)
(xy 103.378 47.915531) (xy 103.378 49.406745) (xy 103.229026 49.345042) (xy 102.997 49.466183) (xy 102.997 50.673)
(xy 103.017 50.673) (xy 103.017 50.927) (xy 102.997 50.927) (xy 102.997 50.947) (xy 102.743 50.947)
(xy 102.743 50.927) (xy 102.723 50.927) (xy 102.723 50.673) (xy 102.743 50.673) (xy 102.743 49.466183)
(xy 102.510974 49.345042) (xy 102.095053 49.517312) (xy 101.663179 49.91151) (xy 101.605664 50.034228) (xy 101.38967 49.710971)
(xy 100.903489 49.386115) (xy 100.33 49.272041) (xy 99.756511 49.386115) (xy 99.27033 49.710971) (xy 99.054336 50.034228)
(xy 98.996821 49.91151) (xy 98.564947 49.517312) (xy 98.149026 49.345042) (xy 97.917 49.466183) (xy 97.917 50.673)
(xy 97.937 50.673) (xy 97.937 50.927) (xy 97.917 50.927) (xy 97.917 50.947) (xy 97.663 50.947)
(xy 97.663 50.927) (xy 97.643 50.927) (xy 97.643 50.673) (xy 97.663 50.673) (xy 97.663 49.466183)
(xy 97.430974 49.345042) (xy 97.015053 49.517312) (xy 96.583179 49.91151) (xy 96.525664 50.034228) (xy 96.30967 49.710971)
(xy 95.823489 49.386115) (xy 95.25 49.272041) (xy 94.676511 49.386115) (xy 94.19033 49.710971) (xy 93.865474 50.197152)
(xy 93.7514 50.770641) (xy 93.7514 50.829359) (xy 93.865474 51.402848) (xy 94.177301 51.86953) (xy 94.144277 51.875937)
(xy 93.931473 52.015727) (xy 93.789023 52.22676) (xy 93.73896 52.4764) (xy 93.73896 54.2036) (xy 93.785937 54.445723)
(xy 93.925727 54.658527) (xy 94.13676 54.800977) (xy 94.3864 54.85104) (xy 96.1136 54.85104) (xy 96.355723 54.804063)
(xy 96.568527 54.664273) (xy 96.710977 54.45324) (xy 96.719179 54.41234) (xy 96.73033 54.429029) (xy 97.216511 54.753885)
(xy 97.79 54.867959) (xy 98.363489 54.753885) (xy 98.658341 54.556871) (xy 100.832184 56.730715) (xy 100.958326 56.815)
(xy 86.185 56.815) (xy 86.185 39.185) (xy 103.390469 39.185) (xy 102.497714 40.077756)
)
)
(filled_polygon
(pts
(xy 113.24033 44.904029) (xy 113.726511 45.228885) (xy 114.3 45.342959) (xy 114.808 45.241912) (xy 114.808 49.75527)
(xy 114.369234 50.194036) (xy 114.236821 49.91151) (xy 113.804947 49.517312) (xy 113.389026 49.345042) (xy 113.157 49.466183)
(xy 113.157 50.673) (xy 113.177 50.673) (xy 113.177 50.927) (xy 113.157 50.927) (xy 113.157 50.947)
(xy 112.903 50.947) (xy 112.903 50.927) (xy 112.883 50.927) (xy 112.883 50.673) (xy 112.903 50.673)
(xy 112.903 49.466183) (xy 112.670974 49.345042) (xy 112.522 49.406745) (xy 112.522 45.102926) (xy 112.81967 44.904029)
(xy 113.03 44.589248) (xy 113.24033 44.904029)
)
)
(filled_polygon
(pts
(xy 119.507 43.688) (xy 119.527 43.688) (xy 119.527 43.942) (xy 119.507 43.942) (xy 119.507 45.148817)
(xy 119.739026 45.269958) (xy 119.8904 45.207261) (xy 119.8904 47.88257) (xy 118.872 48.90097) (xy 118.872 45.208255)
(xy 119.020974 45.269958) (xy 119.253 45.148817) (xy 119.253 43.942) (xy 119.233 43.942) (xy 119.233 43.688)
(xy 119.253 43.688) (xy 119.253 43.668) (xy 119.507 43.668) (xy 119.507 43.688)
)
)
(filled_polygon
(pts
(xy 102.933179 42.16349) (xy 103.351161 42.545008) (xy 103.08033 42.725971) (xy 102.87 43.040752) (xy 102.65967 42.725971)
(xy 102.388828 42.545) (xy 102.65967 42.364029) (xy 102.875664 42.040772) (xy 102.933179 42.16349)
)
)
(filled_polygon
(pts
(xy 111.887 41.148) (xy 111.907 41.148) (xy 111.907 41.402) (xy 111.887 41.402) (xy 111.887 41.422)
(xy 111.633 41.422) (xy 111.633 41.402) (xy 111.613 41.402) (xy 111.613 41.148) (xy 111.633 41.148)
(xy 111.633 41.128) (xy 111.887 41.128) (xy 111.887 41.148)
)
)
(filled_polygon
(pts
(xy 104.267 41.148) (xy 104.287 41.148) (xy 104.287 41.402) (xy 104.267 41.402) (xy 104.267 41.422)
(xy 104.013 41.422) (xy 104.013 41.402) (xy 103.993 41.402) (xy 103.993 41.148) (xy 104.013 41.148)
(xy 104.013 41.128) (xy 104.267 41.128) (xy 104.267 41.148)
)
)
)
)

@ -0,0 +1,62 @@
update=Sun 14 Jun 2015 04:45:24 PM PDT
version=1
last_client=kicad
[cvpcb]
version=1
NetIExt=net
[cvpcb/libraries]
EquName1=devcms
[general]
version=1
[eeschema]
version=1
LibDir=
[eeschema/libraries]
LibName1=power
LibName2=device
LibName3=transistors
LibName4=conn
LibName5=linear
LibName6=regul
LibName7=74xx
LibName8=cmos4000
LibName9=adc-dac
LibName10=memory
LibName11=xilinx
LibName12=microcontrollers
LibName13=dsp
LibName14=microchip
LibName15=analog_switches
LibName16=motorola
LibName17=texas
LibName18=intel
LibName19=audio
LibName20=interface
LibName21=digital-audio
LibName22=philips
LibName23=display
LibName24=cypress
LibName25=siliconi
LibName26=opto
LibName27=atmel
LibName28=contrib
LibName29=valves
[pcbnew]
version=1
PageLayoutDescrFile=
LastNetListRead=
PadDrill=3.048
PadDrillOvalY=3.048
PadSizeH=4.064
PadSizeV=4.064
PcbTextSizeV=1.5
PcbTextSizeH=1.5
PcbTextThickness=0.3
ModuleTextSizeV=1
ModuleTextSizeH=1
ModuleTextSizeThickness=0.15
SolderMaskClearance=0
SolderMaskMinWidth=0
DrawSegmentWidth=0.2
BoardOutlineThickness=0.09999999999999999
ModuleOutlineThickness=0.15

@ -0,0 +1,215 @@
EESchema Schematic File Version 2
LIBS:power
LIBS:device
LIBS:transistors
LIBS:conn
LIBS:linear
LIBS:regul
LIBS:74xx
LIBS:cmos4000
LIBS:adc-dac
LIBS:memory
LIBS:xilinx
LIBS:microcontrollers
LIBS:dsp
LIBS:microchip
LIBS:analog_switches
LIBS:motorola
LIBS:texas
LIBS:intel
LIBS:audio
LIBS:interface
LIBS:digital-audio
LIBS:philips
LIBS:display
LIBS:cypress
LIBS:siliconi
LIBS:opto
LIBS:atmel
LIBS:contrib
LIBS:valves
EELAYER 25 0
EELAYER END
$Descr A4 11693 8268
encoding utf-8
Sheet 1 1
Title ""
Date ""
Rev ""
Comp ""
Comment1 ""
Comment2 ""
Comment3 ""
Comment4 ""
$EndDescr
$Comp
L CONN_02X08 Panel-1
U 1 1 54ECB236
P 6000 3450
F 0 "Panel-1" H 6000 3900 50 0000 C CNN
F 1 "CONN_02X08" V 6000 3450 50 0000 C CNN
F 2 "Pin_Headers:Pin_Header_Straight_2x08" H 6000 2250 60 0001 C CNN
F 3 "" H 6000 2250 60 0000 C CNN
1 6000 3450
1 0 0 -1
$EndComp
$Comp
L CONN_02X13 P1
U 1 1 54ECB2B7
P 4500 3350
F 0 "P1" H 4500 4100 50 0000 C CNN
F 1 "RPi-Header" V 4500 3350 50 0000 C CNN
F 2 "Pin_Headers:Pin_Header_Straight_2x13" H 4500 2400 60 0001 C CNN
F 3 "" H 4500 2400 60 0000 C CNN
1 4500 3350
1 0 0 -1
$EndComp
$Comp
L GND #PWR01
U 1 1 54ECB3E1
P 4850 3350
F 0 "#PWR01" H 4850 3350 30 0001 C CNN
F 1 "GND" H 4850 3280 30 0001 C CNN
F 2 "" H 4850 3350 60 0000 C CNN
F 3 "" H 4850 3350 60 0000 C CNN
1 4850 3350
0 -1 -1 0
$EndComp
Wire Wire Line
4750 3350 4850 3350
$Comp
L GND #PWR02
U 1 1 54ECB417
P 4850 3650
F 0 "#PWR02" H 4850 3650 30 0001 C CNN
F 1 "GND" H 4850 3580 30 0001 C CNN
F 2 "" H 4850 3650 60 0000 C CNN
F 3 "" H 4850 3650 60 0000 C CNN
1 4850 3650
0 -1 -1 0
$EndComp
Wire Wire Line
4750 3650 4850 3650
$Comp
L GND #PWR03
U 1 1 54ECB4A1
P 4850 2950
F 0 "#PWR03" H 4850 2950 30 0001 C CNN
F 1 "GND" H 4850 2880 30 0001 C CNN
F 2 "" H 4850 2950 60 0000 C CNN
F 3 "" H 4850 2950 60 0000 C CNN
1 4850 2950
0 -1 -1 0
$EndComp
Wire Wire Line
4750 2950 4850 2950
$Comp
L GND #PWR04
U 1 1 54ECB7BC
P 4150 3950
F 0 "#PWR04" H 4150 3950 30 0001 C CNN
F 1 "GND" H 4150 3880 30 0001 C CNN
F 2 "" H 4150 3950 60 0000 C CNN
F 3 "" H 4150 3950 60 0000 C CNN
1 4150 3950
0 1 1 0
$EndComp
Wire Wire Line
4150 3950 4250 3950
Text GLabel 4250 3050 0 51 Output ~ 0
strobe
Text GLabel 4250 3450 0 51 Output ~ 0
row_A
Text GLabel 4750 3450 2 51 Output ~ 0
row_B
Text GLabel 4750 3550 2 51 Output ~ 0
row_C
Text GLabel 4750 3750 2 51 Output ~ 0
row_D
Text GLabel 4250 3250 0 51 Output ~ 0
clock
Text GLabel 4250 3850 0 51 Output ~ 0
p0_r1
Text GLabel 4250 3350 0 51 Output ~ 0
p0_g1
Text GLabel 4750 3950 2 51 Output ~ 0
p0_b1
Text GLabel 4750 3850 2 51 Output ~ 0
p0_r2
Text GLabel 4250 3750 0 51 Output ~ 0
p0_g2
Text GLabel 4250 3650 0 51 Output ~ 0
p0_b2
Text GLabel 4750 3250 2 51 Output ~ 0
OE
Wire Wire Line
6250 3400 6800 3400
$Comp
L GND #PWR05
U 1 1 54ECD031
P 6800 3850
F 0 "#PWR05" H 6800 3850 30 0001 C CNN
F 1 "GND" H 6800 3780 30 0001 C CNN
F 2 "" H 6800 3850 60 0000 C CNN
F 3 "" H 6800 3850 60 0000 C CNN
1 6800 3850
1 0 0 -1
$EndComp
Wire Wire Line
6250 3800 6800 3800
Wire Wire Line
6250 3200 6800 3200
Wire Wire Line
6800 3200 6800 3850
Connection ~ 6800 3400
Connection ~ 6800 3800
Text GLabel 6250 3300 2 51 Input ~ 0
p0_g2
Text GLabel 6250 3100 2 51 Input ~ 0
p0_g1
Text GLabel 6250 3600 2 51 Input ~ 0
row_D
Text GLabel 6250 3500 2 51 Input ~ 0
row_B
Text GLabel 5750 3100 0 51 Input ~ 0
p0_r1
Text GLabel 5750 3700 0 51 Input ~ 0
clock
Text GLabel 5750 3400 0 51 Input ~ 0
p0_b2
Text GLabel 5750 3200 0 51 Input ~ 0
p0_b1
Text GLabel 5750 3300 0 51 Input ~ 0
p0_r2
Text GLabel 5750 3800 0 51 Input ~ 0
OE
Text GLabel 5750 3500 0 51 Input ~ 0
row_A
Text GLabel 5750 3600 0 51 Input ~ 0
row_C
Text GLabel 6250 3700 2 51 Input ~ 0
strobe
NoConn ~ 4250 2750
NoConn ~ 4250 3150
NoConn ~ 4250 3550
NoConn ~ 4750 3150
NoConn ~ 4750 2750
NoConn ~ 4750 2850
NoConn ~ 4250 2850
NoConn ~ 4250 2950
NoConn ~ 4750 3050
$Comp
L PWR_FLAG #FLG06
U 1 1 557E1359
P 4200 4050
F 0 "#FLG06" H 4200 4145 50 0001 C CNN
F 1 "PWR_FLAG" H 4200 4230 50 0000 C CNN
F 2 "" H 4200 4050 60 0000 C CNN
F 3 "" H 4200 4050 60 0000 C CNN
1 4200 4050
-1 0 0 1
$EndComp
Wire Wire Line
4200 3950 4200 4050
Connection ~ 4200 3950
$EndSCHEMATC

@ -0,0 +1,3 @@
Various bindings to other programming languages.
Typically these are wrapping the [C-binding](../include/led-matrix-c.h) that
comes with rpi-rgb-led-matrix

@ -0,0 +1,5 @@
**/.vs
**/bin
**/obj
**/*.sln
**/*.csproj.user

@ -0,0 +1,85 @@
global using static RPiRgbLEDMatrix.Bindings;
using System.Runtime.InteropServices;
namespace RPiRgbLEDMatrix;
/*
Some of the extern methods listed below are marked with [SuppressGCTransition].
This disables some GC checks that may take a long time. But such methods should
be fast and trivial, otherwise the managed code may become unstable (see docs).
Keep this in mind when changing the C/C++ side.
https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.suppressgctransitionattribute
*/
internal static class Bindings
{
private const string Lib = "librgbmatrix.so.1";
[DllImport(Lib)]
public static extern IntPtr led_matrix_create(int rows, int chained, int parallel);
[DllImport(Lib, CharSet = CharSet.Ansi)]
public static extern IntPtr led_matrix_create_from_options_const_argv(
ref InternalRGBLedMatrixOptions options,
int argc,
string[] argv);
[DllImport(Lib)]
public static extern void led_matrix_delete(IntPtr matrix);
[DllImport(Lib)]
public static extern IntPtr led_matrix_create_offscreen_canvas(IntPtr matrix);
[DllImport(Lib)]
public static extern IntPtr led_matrix_swap_on_vsync(IntPtr matrix, IntPtr canvas);
[DllImport(Lib)]
public static extern IntPtr led_matrix_get_canvas(IntPtr matrix);
[DllImport(Lib)]
[SuppressGCTransition]
public static extern byte led_matrix_get_brightness(IntPtr matrix);
[DllImport(Lib)]
[SuppressGCTransition]
public static extern void led_matrix_set_brightness(IntPtr matrix, byte brightness);
[DllImport(Lib, CharSet = CharSet.Ansi)]
public static extern IntPtr load_font(string bdf_font_file);
[DllImport(Lib, CharSet = CharSet.Ansi)]
public 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);
[DllImport(Lib, CharSet = CharSet.Ansi)]
public 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);
[DllImport(Lib, CharSet = CharSet.Ansi)]
public static extern void delete_font(IntPtr font);
[DllImport(Lib)]
[SuppressGCTransition]
public static extern void led_canvas_get_size(IntPtr canvas, out int width, out int height);
[DllImport(Lib)]
[SuppressGCTransition]
public static extern void led_canvas_set_pixel(IntPtr canvas, int x, int y, byte r, byte g, byte b);
[DllImport(Lib)]
public static extern void led_canvas_set_pixels(IntPtr canvas, int x, int y, int width, int height,
ref Color colors);
[DllImport(Lib)]
public static extern void led_canvas_clear(IntPtr canvas);
[DllImport(Lib)]
public static extern void led_canvas_fill(IntPtr canvas, byte r, byte g, byte b);
[DllImport(Lib)]
public static extern void draw_circle(IntPtr canvas, int xx, int y, int radius, byte r, byte g, byte b);
[DllImport(Lib)]
public static extern void draw_line(IntPtr canvas, int x0, int y0, int x1, int y1, byte r, byte g, byte b);
}

@ -0,0 +1,38 @@
namespace RPiRgbLEDMatrix;
/// <summary>
/// Represents an RGB (red, green, blue) color
/// </summary>
public struct Color
{
/// <summary>
/// The red component value of this instance.
/// </summary>
public byte R;
/// <summary>
/// The green component value of this instance.
/// </summary>
public byte G;
/// <summary>
/// The blue component value of this instance.
/// </summary>
public byte B;
/// <summary>
/// Creates a new color from the specified color values (red, green, and blue).
/// </summary>
/// <param name="r">The red component value.</param>
/// <param name="g">The green component value.</param>
/// <param name="b">The blue component value.</param>
public Color(int r, int g, int b) : this((byte)r, (byte)g, (byte)b) { }
/// <summary>
/// Creates a new color from the specified color values (red, green, and blue).
/// </summary>
/// <param name="r">The red component value.</param>
/// <param name="g">The green component value.</param>
/// <param name="b">The blue component value.</param>
public Color(byte r, byte g, byte b) => (R, G, B) = (r, g, b);
}

@ -0,0 +1,50 @@
using System.Runtime.InteropServices;
namespace RPiRgbLEDMatrix;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct InternalRGBLedMatrixOptions
{
public IntPtr hardware_mapping;
public int rows;
public int cols;
public int chain_length;
public int parallel;
public int pwm_bits;
public int pwm_lsb_nanoseconds;
public int pwm_dither_bits;
public int brightness;
public int scan_mode;
public int row_address_type;
public int multiplexing;
public IntPtr led_rgb_sequence;
public IntPtr pixel_mapper_config;
public IntPtr panel_type;
public byte disable_hardware_pulsing;
public byte show_refresh_rate;
public byte inverse_colors;
public int limit_refresh_rate_hz;
public InternalRGBLedMatrixOptions(RGBLedMatrixOptions opt)
{
chain_length = opt.ChainLength;
rows = opt.Rows;
cols = opt.Cols;
hardware_mapping = Marshal.StringToHGlobalAnsi(opt.HardwareMapping);
inverse_colors = (byte)(opt.InverseColors ? 1 : 0);
led_rgb_sequence = Marshal.StringToHGlobalAnsi(opt.LedRgbSequence);
pixel_mapper_config = Marshal.StringToHGlobalAnsi(opt.PixelMapperConfig);
panel_type = Marshal.StringToHGlobalAnsi(opt.PanelType);
parallel = opt.Parallel;
multiplexing = (int)opt.Multiplexing;
pwm_bits = opt.PwmBits;
pwm_lsb_nanoseconds = opt.PwmLsbNanoseconds;
pwm_dither_bits = opt.PwmDitherBits;
scan_mode = (int)opt.ScanMode;
show_refresh_rate = (byte)(opt.ShowRefreshRate ? 1 : 0);
limit_refresh_rate_hz = opt.LimitRefreshRateHz;
brightness = opt.Brightness;
disable_hardware_pulsing = (byte)(opt.DisableHardwarePulsing ? 1 : 0);
row_address_type = opt.RowAddressType;
}
};

@ -0,0 +1,34 @@
# This Makefile is intended to be used only by the toplevel Makefile.
# For any other purposes, use .NET SDK build tools directly
# Don't forget to synchronize these variables with the 'RPiRgbLEDMatrix.csproj' file
RGB_LIBDIR=../../lib
RGB_LIBRARY_NAME=rgbmatrix
RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).so.1
NUGET_VERSION = 1.0.0
NUGET_ID = HZeller.RPiRgbLEDMatrix
NUGET_CONFIG = Release
NUGET_PACKAGE = /bin/$(NUGET_CONFIG)/$(NUGET_ID).$(NUGET_VERSION).nupkg
$(NUGET_PACKAGE): $(RGB_LIBRARY)
dotnet pack -c $(NUGET_CONFIG) -p:SkipNative=false -p:PackageId=$(NUGET_ID) -p:Version=$(NUGET_VERSION)
# The examples also depend on the 'RPiRgbLEDMatrix.csproj', but this will be handled by 'dotnet'
build: $(RGB_LIBRARY)
dotnet build examples/FontExample/FontExample.csproj -p:SkipNative=false
dotnet build examples/MatrixRain/MatrixRain.csproj -p:SkipNative=false
dotnet build examples/MinimalExample/MinimalExample.csproj -p:SkipNative=false
dotnet build examples/PulsingBrightness/PulsingBrightness.csproj -p:SkipNative=false
dotnet build examples/Rotating3DCube/Rotating3DCube.csproj -p:SkipNative=false
dotnet build examples/PlayGIF/PlayGIF.csproj -p:SkipNative=false
$(RGB_LIBRARY):
$(MAKE) -C $(RGB_LIBDIR)
# Used by toplevel Makefile
nuget: $(NUGET_PACKAGE)
# Used in 'RPiRgbLEDMatrix.csproj'
library: $(RGB_LIBRARY)

@ -0,0 +1,11 @@
namespace RPiRgbLEDMatrix;
/// <summary>
/// Type of multiplexing.
/// </summary>
public enum Multiplexing : int
{
Direct = 0,
Stripe = 1,
Checker = 2
}

@ -0,0 +1,16 @@
C# bindings for RGB Matrix library
======================================
Building
--------
To build the C# wrapper for the RGB Matrix C library you need to first have __.NET SDK__ installed.
### Install .NET SDK
`sudo apt install dotnet6` should work in most cases.
For some old distributions, read [docs](https://learn.microsoft.com/dotnet/core/install/linux)
Then, in the `bindings/c#` directory type: `dotnet build`
To run the example applications in the c#\examples\EXAMPLE folder: `sudo dotnet run`

@ -0,0 +1,100 @@
namespace RPiRgbLEDMatrix;
/// <summary>
/// Represents a canvas whose pixels can be manipulated.
/// </summary>
public class RGBLedCanvas
{
// This is a wrapper for canvas no need to implement IDisposable here
// because RGBLedMatrix has ownership and takes care of disposing canvases
internal IntPtr _canvas;
// this is not called directly by the consumer code,
// consumer uses factory methods in RGBLedMatrix
internal RGBLedCanvas(IntPtr canvas)
{
_canvas = canvas;
led_canvas_get_size(_canvas, out var width, out var height);
Width = width;
Height = height;
}
/// <summary>
/// The width of the canvas in pixels.
/// </summary>
public int Width { get; private set; }
/// <summary>
/// The height of the canvas in pixels.
/// </summary>
public int Height { get; private set; }
/// <summary>
/// Sets the color of a specific pixel.
/// </summary>
/// <param name="x">The X coordinate of the pixel.</param>
/// <param name="y">The Y coordinate of the pixel.</param>
/// <param name="color">New pixel color.</param>
public void SetPixel(int x, int y, Color color) => led_canvas_set_pixel(_canvas, x, y, color.R, color.G, color.B);
/// <summary>
/// Copies the colors from the specified buffer to a rectangle on the canvas.
/// </summary>
/// <param name="x">The X coordinate of the top-left pixel of the rectangle.</param>
/// <param name="y">The Y coordinate of the top-left pixel of the rectangle.</param>
/// <param name="width">Width of the rectangle.</param>
/// <param name="height">Height of the rectangle.</param>
/// <param name="colors">Buffer containing the colors to copy.</param>
public void SetPixels(int x, int y, int width, int height, Span<Color> colors)
{
if (colors.Length < width * height)
throw new ArgumentOutOfRangeException(nameof(colors));
led_canvas_set_pixels(_canvas, x, y, width, height, ref colors[0]);
}
/// <summary>
/// Sets the color of the entire canvas.
/// </summary>
/// <param name="color">New canvas color.</param>
public void Fill(Color color) => led_canvas_fill(_canvas, color.R, color.G, color.B);
/// <summary>
/// Cleans the entire canvas.
/// </summary>
public void Clear() => led_canvas_clear(_canvas);
/// <summary>
/// Draws a circle of the specified color.
/// </summary>
/// <param name="x">The X coordinate of the center.</param>
/// <param name="y">The Y coordinate of the center.</param>
/// <param name="radius">The radius of the circle, in pixels.</param>
/// <param name="color">The color of the circle.</param>
public void DrawCircle(int x, int y, int radius, Color color) =>
draw_circle(_canvas, x, y, radius, color.R, color.G, color.B);
/// <summary>
/// Draws a line of the specified color.
/// </summary>
/// <param name="x0">The X coordinate of the first point.</param>
/// <param name="y0">The Y coordinate of the first point.</param>
/// <param name="x1">The X coordinate of the second point.</param>
/// <param name="y1">The Y coordinate of the second point.</param>
/// <param name="color">The color of the line.</param>
public void DrawLine(int x0, int y0, int x1, int y1, Color color) =>
draw_line(_canvas, x0, y0, x1, y1, color.R, color.G, color.B);
/// <summary>
/// Draws the text with the specified color.
/// </summary>
/// <param name="font">Font to draw text with.</param>
/// <param name="x">The X coordinate of the starting point.</param>
/// <param name="y">The Y coordinate of the starting point.</param>
/// <param name="color">The color of the text.</param>
/// <param name="text">Text to draw.</param>
/// <param name="spacing">Additional spacing between characters.</param>
/// <param name="vertical">Whether to draw the text vertically.</param>
/// <returns>How many pixels was advanced on the screen.</returns>
public int DrawText(RGBLedFont font, int x, int y, Color color, string text, int spacing = 0, bool vertical = false) =>
font.DrawText(_canvas, x, y, color, text, spacing, vertical);
}

@ -0,0 +1,40 @@
namespace RPiRgbLEDMatrix;
/// <summary>
/// Represents a <c>.BDF</c> font.
/// </summary>
public class RGBLedFont : IDisposable
{
internal IntPtr _font;
private bool disposedValue = false;
/// <summary>
/// Loads the BDF font from the specified file.
/// </summary>
/// <param name="bdfFontPath">The path to the BDF file to load.</param>
public RGBLedFont(string bdfFontPath) => _font = load_font(bdfFontPath);
internal int DrawText(IntPtr canvas, int x, int y, Color color, string text, int spacing = 0, bool vertical = false)
{
if (!vertical)
return draw_text(canvas, _font, x, y, color.R, color.G, color.B, text, spacing);
else
return vertical_draw_text(canvas, _font, x, y, color.R, color.G, color.B, text, spacing);
}
protected virtual void Dispose(bool disposing)
{
if (disposedValue) return;
delete_font(_font);
disposedValue = true;
}
~RGBLedFont() => Dispose(false);
/// <inheritdoc/>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}

@ -0,0 +1,112 @@
using System.Buffers;
using System.Runtime.InteropServices;
namespace RPiRgbLEDMatrix;
/// <summary>
/// Represents a RGB matrix.
/// </summary>
public class RGBLedMatrix : IDisposable
{
private IntPtr matrix;
private bool disposedValue = false;
/// <summary>
/// Initializes a new matrix.
/// </summary>
/// <param name="rows">Size of a single module. Can be 32, 16 or 8.</param>
/// <param name="chained">How many modules are connected in a chain.</param>
/// <param name="parallel">How many modules are connected in a parallel.</param>
public RGBLedMatrix(int rows, int chained, int parallel)
{
matrix = led_matrix_create(rows, chained, parallel);
if (matrix == (IntPtr)0)
throw new ArgumentException("Could not initialize a new matrix");
}
/// <summary>
/// Initializes a new matrix.
/// </summary>
/// <param name="options">A configuration of a matrix.</param>
public RGBLedMatrix(RGBLedMatrixOptions options)
{
InternalRGBLedMatrixOptions opt = default;
try
{
opt = new(options);
var args = Environment.GetCommandLineArgs();
// Because gpio-slowdown is not provided in the options struct,
// we manually add it.
// Let's add it first to the command-line we pass to the
// matrix constructor, so that it can be overridden with the
// users' commandline.
// As always, as the _very_ first, we need to provide the
// program name argv[0].
var argv = new string[args.Length + 1];
argv[0] = args[0];
argv[1] = $"--led-slowdown-gpio={options.GpioSlowdown}";
Array.Copy(args, 1, argv, 2, args.Length - 1);
matrix = led_matrix_create_from_options_const_argv(ref opt, argv.Length, argv);
if (matrix == (IntPtr)0)
throw new ArgumentException("Could not initialize a new matrix");
}
finally
{
if(options.HardwareMapping is not null) Marshal.FreeHGlobal(opt.hardware_mapping);
if(options.LedRgbSequence is not null) Marshal.FreeHGlobal(opt.led_rgb_sequence);
if(options.PixelMapperConfig is not null) Marshal.FreeHGlobal(opt.pixel_mapper_config);
if(options.PanelType is not null) Marshal.FreeHGlobal(opt.panel_type);
}
}
/// <summary>
/// Creates a new backbuffer canvas for drawing on.
/// </summary>
/// <returns>An instance of <see cref="RGBLedCanvas"/> representing the canvas.</returns>
public RGBLedCanvas CreateOffscreenCanvas() => new(led_matrix_create_offscreen_canvas(matrix));
/// <summary>
/// Returns a canvas representing the current frame buffer.
/// </summary>
/// <returns>An instance of <see cref="RGBLedCanvas"/> representing the canvas.</returns>
/// <remarks>Consider using <see cref="CreateOffscreenCanvas"/> instead.</remarks>
public RGBLedCanvas GetCanvas() => new(led_matrix_get_canvas(matrix));
/// <summary>
/// Swaps this canvas with the currently active canvas. The active canvas
/// becomes a backbuffer and is mapped to <paramref name="canvas"/> instance.
/// <br/>
/// This operation guarantees vertical synchronization.
/// </summary>
/// <param name="canvas">Backbuffer canvas to swap.</param>
public void SwapOnVsync(RGBLedCanvas canvas) =>
canvas._canvas = led_matrix_swap_on_vsync(matrix, canvas._canvas);
/// <summary>
/// The general brightness of the matrix.
/// </summary>
public byte Brightness
{
get => led_matrix_get_brightness(matrix);
set => led_matrix_set_brightness(matrix, value);
}
protected virtual void Dispose(bool disposing)
{
if (disposedValue) return;
led_matrix_delete(matrix);
disposedValue = true;
}
~RGBLedMatrix() => Dispose(false);
/// <inheritdoc/>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}

@ -0,0 +1,124 @@
namespace RPiRgbLEDMatrix;
/// <summary>
/// Represents the matrix settings.
/// </summary>
public struct RGBLedMatrixOptions
{
/// <summary>
/// Name of the hardware mapping used. If passed
/// <see langword="null"/> here, the default is used.
/// </summary>
public string? HardwareMapping = null;
/// <summary>
/// The "rows" are the number of rows supported by the display, so 32 or 16.
/// Default: 32.
/// </summary>
public int Rows = 32;
/// <summary>
/// The "cols" are the number of columns per panel. Typically something
/// like 32, but also 64 is possible. Sometimes even 40.
/// <c>cols * chain_length</c> is the total length of the display, so you can
/// represent a 64 wide display as cols=32, chain=2 or cols=64, chain=1;
/// same thing, but more convenient to think of.
/// </summary>
public int Cols = 32;
/// <summary>
/// The chain_length is the number of displays daisy-chained together
/// (output of one connected to input of next). Default: 1
/// </summary>
public int ChainLength = 1;
/// <summary>
/// The number of parallel chains connected to the Pi; in old Pis with 26
/// GPIO pins, that is 1, in newer Pis with 40 interfaces pins, that can also
/// be 2 or 3. The effective number of pixels in vertical direction is then
/// thus <c>rows * parallel</c>. Default: 1
/// </summary>
public int Parallel = 1;
/// <summary>
/// Set PWM bits used for output. Default is 11, but if you only deal with limited
/// comic-colors, 1 might be sufficient. Lower require less CPU and increases refresh-rate.
/// </summary>
public int PwmBits = 11;
/// <summary>
/// Change the base time-unit for the on-time in the lowest significant bit in
/// nanoseconds. Higher numbers provide better quality (more accurate color, less
/// ghosting), but have a negative impact on the frame rate.
/// </summary>
public int PwmLsbNanoseconds = 130;
/// <summary>
/// The lower bits can be time-dithered for higher refresh rate.
/// </summary>
public int PwmDitherBits = 0;
/// <summary>
/// The initial brightness of the panel in percent. Valid range is 1..100
/// </summary>
public int Brightness = 100;
/// <summary>
/// Scan mode.
/// </summary>
public ScanModes ScanMode = ScanModes.Progressive;
/// <summary>
/// Default row address type is 0, corresponding to direct setting of the
/// row, while row address type 1 is used for panels that only have A/B,
/// typically some 64x64 panels
/// </summary>
public int RowAddressType = 0;
/// <summary>
/// Type of multiplexing.
/// </summary>
public Multiplexing Multiplexing = Multiplexing.Direct;
/// <summary>
/// In case the internal sequence of mapping is not <c>"RGB"</c>, this
/// contains the real mapping. Some panels mix up these colors.
/// </summary>
public string? LedRgbSequence = null;
/// <summary>
/// A string describing a sequence of pixel mappers that should be applied
/// to this matrix. A semicolon-separated list of pixel-mappers with optional
/// parameter.
public string? PixelMapperConfig = null;
/// <summary>
/// Panel type. Typically just empty, but certain panels (FM6126)
/// requie an initialization sequence
/// </summary>
public string? PanelType = null;
/// <summary>
/// Allow to use the hardware subsystem to create pulses. This won't do
/// anything if output enable is not connected to GPIO 18.
/// </summary>
public bool DisableHardwarePulsing = false;
public bool ShowRefreshRate = false;
public bool InverseColors = false;
/// <summary>
/// Limit refresh rate of LED panel. This will help on a loaded system
/// to keep a constant refresh rate. &lt;= 0 for no limit.
/// </summary>
public int LimitRefreshRateHz = 0;
/// <summary>
/// Slowdown GPIO. Needed for faster Pis/slower panels.
/// </summary>
public int GpioSlowdown = 1;
/// <summary>
/// Creates default matrix settings.
/// </summary>
public RGBLedMatrixOptions() { }
}

@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<!-- Skip builing native libraries by default on Windows -->
<PropertyGroup Condition="$(SkipNative) == '' and $(OS) != 'Unix'">
<SkipNative>true</SkipNative>
</PropertyGroup>
<ItemGroup>
<Compile Remove="examples\**" />
<None Remove="examples\**" />
<None Condition="$(SkipNative) != 'true'" Pack="true"
Include="..\..\lib\librgbmatrix.so.1"
PackagePath="\runtimes\linux-arm64\native" >
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<Target Name="CompileNative" BeforeTargets="Compile" Condition="$(SkipNative) != 'true'">
<Message Text="Building native libraries" />
<Exec Command="make library" />
</Target>
</Project>

@ -0,0 +1,10 @@
namespace RPiRgbLEDMatrix;
/// <summary>
/// Scan modes.
/// </summary>
public enum ScanModes
{
Progressive = 0,
Interlaced = 1
}

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\RPiRgbLEDMatrix.csproj"/>
</ItemGroup>
</Project>

@ -0,0 +1,29 @@
using RPiRgbLEDMatrix;
if (args.Length < 1)
{
Console.WriteLine("font-example.exe [font_path] <text>");
return -1;
}
string text = "Hello World!";
if (args.Length > 1)
text = args[1];
using var matrix = new RGBLedMatrix(32, 2, 1);
var canvas = matrix.CreateOffscreenCanvas();
using var font = new RGBLedFont(args[0]);
canvas.DrawText(font, 1, 6, new Color(0, 255, 0), text);
matrix.SwapOnVsync(canvas);
// run until user presses Ctrl+C
var running = true;
Console.CancelKeyPress += (_, e) =>
{
running = false;
e.Cancel = true; // do not terminate program with Ctrl+C, we need to dispose
};
while (running) Thread.Yield();
return 0;

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\RPiRgbLEDMatrix.csproj"/>
</ItemGroup>
</Project>

@ -0,0 +1,74 @@
using RPiRgbLEDMatrix;
const int MaxHeight = 16;
const int ColorStep = 15;
const int FrameStep = 1;
using var matrix = new RGBLedMatrix(new RGBLedMatrixOptions { ChainLength = 2 });
var canvas = matrix.CreateOffscreenCanvas();
var rnd = new Random();
var points = new List<Point>();
var recycled = new Stack<Point>();
var frame = 0;
var running = true;
Console.CancelKeyPress += (s, e) =>
{
running = false;
e.Cancel = true; // don't terminate, we need to dispose
};
// run until user presses Ctrl+C
while (running)
{
var frameStart = Environment.TickCount64;
frame++;
if (frame % FrameStep == 0)
{
if (recycled.Count == 0)
points.Add(new Point(rnd.Next(0, canvas.Width - 1), 0));
else
{
var point = recycled.Pop();
point.X = rnd.Next(0, canvas.Width - 1);
point.Y = 0;
point.Recycled = false;
}
}
canvas.Clear();
foreach (var point in points)
{
if (point.Recycled) continue;
point.Y++;
if (point.Y - MaxHeight > canvas.Height)
{
point.Recycled = true;
recycled.Push(point);
}
for (var i = 0; i < MaxHeight; i++)
{
canvas.SetPixel(point.X, point.Y - i, new Color(0, 255 - i * ColorStep, 0));
}
}
matrix.SwapOnVsync(canvas);
// force 30 FPS
var elapsed = Environment.TickCount64 - frameStart;
if (elapsed < 33) Thread.Sleep(33 - (int)elapsed);
}
class Point
{
public int X;
public int Y;
public bool Recycled = false;
public Point(int x, int y) => (X, Y) = (x, y);
}

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\RPiRgbLEDMatrix.csproj"/>
</ItemGroup>
</Project>

@ -0,0 +1,19 @@
using RPiRgbLEDMatrix;
using var matrix = new RGBLedMatrix(32, 2, 1);
var canvas = matrix.CreateOffscreenCanvas();
var centerX = canvas.Width / 2;
var centerY = canvas.Height / 2;
for (var i = 0; i < 1000; ++i)
{
for (var y = 0; y < canvas.Height; ++y)
for (var x = 0; x < canvas.Width; ++x)
canvas.SetPixel(x, y, new Color(i & 0xFF, x, y));
canvas.DrawCircle(centerX, centerY, 6, new Color(0, 0, 255));
canvas.DrawLine(centerX - 3, centerY - 3, centerX + 3, centerY + 3, new Color(0, 0, 255));
canvas.DrawLine(centerX - 3, centerY + 3, centerX + 3, centerY - 3, new Color(0, 0, 255));
matrix.SwapOnVsync(canvas);
}

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\RPiRgbLEDMatrix.csproj" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.1" />
</ItemGroup>
</Project>

@ -0,0 +1,40 @@
using RPiRgbLEDMatrix;
using System.Runtime.InteropServices;
using Color = RPiRgbLEDMatrix.Color;
Console.Write("GIF path: ");
var path = Console.ReadLine()!;
using var matrix = new RGBLedMatrix(32, 2, 1);
var canvas = matrix.CreateOffscreenCanvas();
Configuration.Default.PreferContiguousImageBuffers = true;
using var image = Image.Load<Rgb24>(path);
image.Mutate(o => o.Resize(canvas.Width, canvas.Height));
var running = true;
Console.CancelKeyPress += (s, e) =>
{
running = false;
e.Cancel = true; // don't terminate, we need to dispose
};
var frame = -1;
// preprocess frames to get delays and pixel buffers
var frames = image.Frames
.Select(f => (
Pixels: f.DangerousTryGetSinglePixelMemory(out var memory) ? memory : throw new("Could not get pixel buffer"),
Delay: f.Metadata.GetGifMetadata().FrameDelay * 10
)).ToArray();
// run until user presses Ctrl+C
while (running)
{
frame = (frame + 1) % frames.Length;
var data = MemoryMarshal.Cast<Rgb24, Color>(frames[frame].Pixels.Span);
canvas.SetPixels(0, 0, canvas.Width, canvas.Height, data);
matrix.SwapOnVsync(canvas);
Thread.Sleep(frames[frame].Delay);
}

@ -0,0 +1,33 @@
using RPiRgbLEDMatrix;
using var matrix = new RGBLedMatrix(new RGBLedMatrixOptions { Rows = 32, Cols = 64 });
var canvas = matrix.CreateOffscreenCanvas();
var maxBrightness = matrix.Brightness;
var rnd = new Random();
// run until user presses Ctrl+C
var running = true;
Console.CancelKeyPress += (_, e) =>
{
running = false;
e.Cancel = true; // do not terminate program with Ctrl+C, we need to dispose
};
var color = new Color(rnd.Next(0, 256), rnd.Next(0, 256), rnd.Next(0, 256));
while (running)
{
if (matrix.Brightness < 1)
{
matrix.Brightness = maxBrightness;
color = new Color(rnd.Next(0, 256), rnd.Next(0, 256), rnd.Next(0, 256));
}
else
{
matrix.Brightness--;
}
canvas.Fill(color);
matrix.SwapOnVsync(canvas);
Thread.Sleep(20);
}

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\RPiRgbLEDMatrix.csproj"/>
</ItemGroup>
</Project>

@ -0,0 +1,90 @@
using RPiRgbLEDMatrix;
using System.Numerics;
const float MaxModuleSpeed = 0.1f;
const float FOV = 60f;
const float Scale = 1.1f;
const float LerpPow = 0.002f;
const int ChangePerFrames = 50;
using var leds = new RGBLedMatrix(32, 1, 1);
var canvas = leds.CreateOffscreenCanvas();
var (centerX, centerY) = (canvas.Width / 2, canvas.Height / 2);
var rnd = new Random();
var angleSpeed = new Vector3();
var nextAngleSpeed = new Vector3();
var frame = -1;
var rotateMatrix = Matrix4x4.Identity;
var scaleMatrix = Matrix4x4.CreateScale(Scale);
var projectMatrix = Matrix4x4.CreatePerspectiveFieldOfView(FOV / 180 * MathF.PI, 1, 0.1f, 100f);
var cameraMatrix = Matrix4x4.CreateLookAt(new(0, 0, 4), new(0, 0, 0), new(0, 1, 0));
// run until user presses Ctrl+C
var running = true;
Console.CancelKeyPress += (_, e) =>
{
running = false;
e.Cancel = true; // do not terminate program with Ctrl+C, we need to dispose
};
while (running)
{
var frameStart = Environment.TickCount64;
// update angle speed
frame = (frame + 1) % ChangePerFrames;
if(frame == 0)
nextAngleSpeed = new Vector3(
(rnd.NextSingle() * 2 - 1) * MaxModuleSpeed,
(rnd.NextSingle() * 2 - 1) * MaxModuleSpeed,
(rnd.NextSingle() * 2 - 1) * MaxModuleSpeed
);
angleSpeed = Vector3.Lerp(angleSpeed, nextAngleSpeed, LerpPow);
// update matrices
rotateMatrix *= Matrix4x4.CreateRotationX(angleSpeed.X);
rotateMatrix *= Matrix4x4.CreateRotationY(angleSpeed.Y);
rotateMatrix *= Matrix4x4.CreateRotationZ(angleSpeed.Z);
var matrix = scaleMatrix * rotateMatrix * cameraMatrix * projectMatrix;
// calculate points
var top1 = Vector4.Transform(new Vector3( 1, 1, 1), matrix);
var top2 = Vector4.Transform(new Vector3(-1, 1, 1), matrix);
var top3 = Vector4.Transform(new Vector3(-1, 1, -1), matrix);
var top4 = Vector4.Transform(new Vector3( 1, 1, -1), matrix);
var bot1 = Vector4.Transform(new Vector3( 1, -1, 1), matrix);
var bot2 = Vector4.Transform(new Vector3(-1, -1, 1), matrix);
var bot3 = Vector4.Transform(new Vector3(-1, -1, -1), matrix);
var bot4 = Vector4.Transform(new Vector3( 1, -1, -1), matrix);
// draw
canvas.Fill(new(0, 0, 0));
DrawLine(top1, top2);
DrawLine(top2, top3);
DrawLine(top3, top4);
DrawLine(top4, top1);
DrawLine(bot1, bot2);
DrawLine(bot2, bot3);
DrawLine(bot3, bot4);
DrawLine(bot4, bot1);
DrawLine(top1, bot1);
DrawLine(top2, bot2);
DrawLine(top3, bot3);
DrawLine(top4, bot4);
leds.SwapOnVsync(canvas);
// force 30 FPS
var elapsed = Environment.TickCount64 - frameStart;
if (elapsed < 33) Thread.Sleep(33 - (int)elapsed);
}
void DrawLine(Vector4 a, Vector4 b) => canvas.DrawLine(
(int)(a.X * a.W + centerX), (int)(a.Y * a.W + centerY),
(int)(b.X * b.W + centerX), (int)(b.Y * b.W + centerY),
new(255, 255, 255));

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\RPiRgbLEDMatrix.csproj"/>
</ItemGroup>
</Project>

@ -0,0 +1,92 @@
# TODO: This contains a lot of {c|p}ython build boilerplate, this needs cleanup.
PYTHON ?= python
SETUP := setup.py
BUILD_ARGS := build --build-lib .
INST_ARGS := install
ifdef DESTDIR
INST_ARGS += --root=$(DESTDIR)
endif
CLEAN_ARGS := clean --all
MANPAGES := $(patsubst %.txt,%,$(wildcard *.txt))
TXTTOMAN := a2x -f manpage
# Where our library resides. It is split between includes and the binary
# library in lib
RGB_LIBDIR=../../lib
RGB_LIBRARY_NAME=rgbmatrix
RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a
ifneq "$(wildcard debian/changelog)" ""
PKGNAME := $(shell dpkg-parsechangelog | sed -n 's/^Source: //p')
VERSION := $(shell dpkg-parsechangelog | sed -n 's/^Version: \([^-]*\).*/\1/p')
UPSDIST := $(PKGNAME)-$(VERSION).tar.gz
DEBDIST := $(PKGNAME)_$(VERSION).orig.tar.gz
endif
all: build
build: build-python
install: install-python
clean: clean-python
find ./rgbmatrix -type f -name \*.so -delete
find . -type f -name \*.pyc -delete
$(RM) build-* install-* test-*
$(RGB_LIBRARY): FORCE
$(MAKE) -C $(RGB_LIBDIR)
test: test-python
test-python:
ifneq "$(wildcard tests/*.py)" ""
nosetests -v -w tests
else
$(info Test suite is not implemented...)
endif
ifneq "$(wildcard debian/control)" ""
PYVERS := $(shell pyversions -r -v debian/control)
PYEXEC := $(shell pyversions -d)
BUILD_ARGS += --executable=/usr/bin/$(PYEXEC)
INST_ARGS += --no-compile -O0
build-python: $(PYVERS:%=build-python-%)
build-python-%: $(RGB_LIBRARY)
$(info * Doing build for $(PYTHON)$* ...)
$(PYTHON)$* $(SETUP) $(BUILD_ARGS)
install-python: $(PYVERS:%=install-python-%)
install-python-%:
$(info * Doing install for $(PYTHON)$* ...)
$(PYTHON)$* $(SETUP) $(INST_ARGS)
clean-python: $(PYVERS:%=clean-python-%)
clean-python-%:
$(PYTHON)$* $(SETUP) $(CLEAN_ARGS)
else
build-python: $(RGB_LIBRARY)
$(PYTHON) $(SETUP) $(BUILD_ARGS)
install-python:
$(PYTHON) $(SETUP) $(INST_ARGS)
clean-python:
$(PYTHON) $(SETUP) $(CLEAN_ARGS)
endif
distclean: clean
dist: distclean
$(info * Creating ../$(UPSDIST) and ../$(DEBDIST))
@tar --exclude='.svn' \
--exclude='*.swp' \
--exclude='debian' \
-czvf ../$(UPSDIST) \
-C ../ $(notdir $(CURDIR))
@cp ../$(UPSDIST) ../$(DEBDIST)
@if test -d ../tarballs; then \
mv -v ../$(DEBDIST) ../tarballs; \
fi
FORCE:
.PHONY: FORCE
.PHONY: build install test clean dist distclean
.PHONY: build-python install-python clean-python

@ -0,0 +1,147 @@
Python bindings for RGB Matrix library
======================================
Building
--------
If you have a different than the standard wiring (for instance if you have an
Adafruit HAT), you can edit the [../../lib/Makefile](../../lib/Makefile#L26) first to choose
the hardware in question (see below for setting it via command line argument).
Then, in the root directory for the matrix library simply type:
### Python 2
```shell
sudo apt-get update && sudo apt-get install python2.7-dev python-pillow -y
make build-python
sudo make install-python
```
### Python 3
You can also build for Python 3:
```shell
sudo apt-get update && sudo apt-get install python3-dev python3-pillow -y
make build-python PYTHON=$(command -v python3)
sudo make install-python PYTHON=$(command -v python3)
```
### PyPy
The cython binding to PyPy seems to be somewhat working but extremely slow (20x
slower even than the regular Python binding, 160x slower than C++), so this is
not recommended.
So Cython is not good together with PyPy which works best with a
[CFFI](https://cffi.readthedocs.io/) binding. @Duality4Y did an experiment here
https://github.com/Duality4Y/rgb-matrix-cffi which works well with PyPy and is
about twice as fast as running Python3+cython (but Python3+cffi is slower than
Python3+cython, so we can't just replace everything with cffi).
Of course, it would be nice to have the fastest possible binding to all kinds
of Python interpreters. If anyone wants to work on that, this would certainly
be a welcome pull request.
Performance
-----------
The simplicity of scripting comes at a price: Python is slower than C++ of
course.
If you have to do a lot of pixel updates in your demo, this can be too slow
depending on what you do. Here are some rough numbers for calling `SetPixel()`
in a tight loop:
* On a Pi-2 and Pi-3, a Python script will be about 1/8 of the speed compared
to the corresponding C++ program (pushing ~0.43 Megapixels/s Python
vs. ~3.5 Megapixels/s C++ on a Pi-3 for instance)
* On a Pi-1/Pi Zero, the difference is even worse: 1/24 of the speed to the
corresponding C++ program. Given that this Pi is already about 1/10 the
speed of a Pi-3, this almost makes Python unusable on a Pi-1
(~0.015 Megapixels/s Python vs. ~0.36 Megapixels/s C++)
* Also interesting: Python3 is a little bit slower than Python2.7.
So if you can, stick with Python2.7 for now.
* The good news is, that this is due to overhead per function call. If you
can do more per function call, then this is less problematic. For instance
if you have an image to be displayed with `SetImage()`, that will much
faster per pixel (internally this then copies the pixels natively).
The ~0.015 Megapixels/s on a Pi-1 means that you can update a 32x32 matrix
at most with ~15fps. If you have chained 5, then you barely reach 3fps.
In a Pi-3, you get about 400fps update rate (85fps for 5-chain) with a Python
program (while with C++, you can do the same thing with a comfortable 3500fps
(700fps for 5)). Keep in mind that this is if all you do is just calling
`SetPixel()`, it does not include any time of what you actually want to do in
your demo - so anything in addition to that will drop your update rate.
If you can prepare the animation you want to show, then you can either prepare
images and then use the much faster call to `SetImage()`, or can fill
entire offscreen-frames (create with `CreateFrameCanvas()`) and then
swap with `SwapOnVSync()` (this is the fastest method).
Using the library
-----------------
Be aware of the fact that using the full performance of the RGBMatrix requires root privileges.
Therefore you should run all you python scripts as using `sudo`.
You may find examples in the [samples/](./samples) subdirectory.
The examples all use the [samplebase.py](./samples/samplebase.py) that provides
some basic capabilities to all example programs, such as command-line parsing: all
sample-programs accept `--led-rows`, `--led-chain` and `--led-parallel` as
command line options to adapt to your configuration
```bash
cd samples
sudo ./runtext.py --led-chain=4
```
To use different wiring without recompiling the library to change the default,
you can use `--led-gpio-mapping` (or `-m`). For example, to use Adafruit HAT:
```bash
sudo ./runtext.py --led-gpio-mapping=adafruit-hat
```
Here is a complete example showing how to write an image viewer:
```python
#!/usr/bin/env python
import time
import sys
from rgbmatrix import RGBMatrix, RGBMatrixOptions
from PIL import Image
if len(sys.argv) < 2:
sys.exit("Require an image argument")
else:
image_file = sys.argv[1]
image = Image.open(image_file)
# Configuration for the matrix
options = RGBMatrixOptions()
options.rows = 32
options.chain_length = 1
options.parallel = 1
options.hardware_mapping = 'regular' # If you have an Adafruit HAT: 'adafruit-hat'
matrix = RGBMatrix(options = options)
# Make image fit our screen.
image.thumbnail((matrix.width, matrix.height), Image.ANTIALIAS)
matrix.SetImage(image.convert('RGB'))
try:
print("Press CTRL-C to stop.")
while True:
time.sleep(100)
except KeyboardInterrupt:
sys.exit(0)
```
## API
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).
### User
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.

@ -0,0 +1,15 @@
# The *.cpp files are included in the distribution; this is only needed when
# working on the pyx files.
#
# Please check in modified *.cpp files with distribution to not require cython
# to be installed on the users' machine.
# for python3: make PYTHON=$(which python3) CYTHON=$(which cython3)
CYTHON ?= cython
all : core.cpp graphics.cpp
%.cpp : %.pyx
$(CYTHON) --cplus -o $@ $^
clean:
rm -rf core.cpp graphics.cpp

@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
__version__ = "0.0.1"
__author__ = "Christoph Friedrich <christoph.friedrich@vonaffenfels.de>"
from .core import RGBMatrix, FrameCanvas, RGBMatrixOptions

File diff suppressed because it is too large Load Diff

@ -0,0 +1,24 @@
cimport cppinc
cdef class Canvas:
cdef cppinc.Canvas *__getCanvas(self) except +
cdef class FrameCanvas(Canvas):
cdef cppinc.FrameCanvas *__canvas
cdef class RGBMatrix(Canvas):
cdef cppinc.RGBMatrix *__matrix
cdef class RGBMatrixOptions:
cdef cppinc.Options __options
cdef cppinc.RuntimeOptions __runtime_options
# Must keep a reference to the encoded bytes for the strings,
# otherwise, when the Options struct is used, it will be garbage collected
cdef bytes __py_encoded_hardware_mapping
cdef bytes __py_encoded_led_rgb_sequence
cdef bytes __py_encoded_pixel_mapper_config
cdef bytes __py_encoded_panel_type
# Local Variables:
# mode: python
# End:

@ -0,0 +1,272 @@
# distutils: language = c++
from libcpp cimport bool
from libc.stdint cimport uint8_t, uint32_t, uintptr_t
from PIL import Image
import cython
cdef class Canvas:
cdef cppinc.Canvas* __getCanvas(self) except +:
raise Exception("Not implemented")
def SetImage(self, image, int offset_x = 0, int offset_y = 0, unsafe=True):
if (image.mode != "RGB"):
raise Exception("Currently, only RGB mode is supported for SetImage(). Please create images with mode 'RGB' or convert first with image = image.convert('RGB'). Pull requests to support more modes natively are also welcome :)")
if unsafe:
#In unsafe mode we directly access the underlying PIL image array
#in cython, which is considered unsafe pointer accecss,
#however it's super fast and seems to work fine
#https://groups.google.com/forum/#!topic/cython-users/Dc1ft5W6KM4
img_width, img_height = image.size
self.SetPixelsPillow(offset_x, offset_y, img_width, img_height, image)
else:
# First implementation of a SetImage(). OPTIMIZE_ME: A more native
# implementation that directly reads the buffer and calls the underlying
# C functions can certainly be faster.
img_width, img_height = image.size
pixels = image.load()
for x in range(max(0, -offset_x), min(img_width, self.width - offset_x)):
for y in range(max(0, -offset_y), min(img_height, self.height - offset_y)):
(r, g, b) = pixels[x, y]
self.SetPixel(x + offset_x, y + offset_y, r, g, b)
@cython.boundscheck(False)
@cython.wraparound(False)
def SetPixelsPillow(self, int xstart, int ystart, int width, int height, image):
cdef cppinc.FrameCanvas* my_canvas = <cppinc.FrameCanvas*>self.__getCanvas()
cdef int frame_width = my_canvas.width()
cdef int frame_height = my_canvas.height()
cdef int row, col
cdef uint8_t r, g, b
cdef uint32_t **image_ptr
cdef uint32_t pixel
image.load()
ptr_tmp = dict(image.im.unsafe_ptrs)['image32']
image_ptr = (<uint32_t **>(<uintptr_t>ptr_tmp))
for col in range(max(0, -xstart), min(width, frame_width - xstart)):
for row in range(max(0, -ystart), min(height, frame_height - ystart)):
pixel = image_ptr[row][col]
r = (pixel ) & 0xFF
g = (pixel >> 8) & 0xFF
b = (pixel >> 16) & 0xFF
my_canvas.SetPixel(xstart+col, ystart+row, r, g, b)
cdef class FrameCanvas(Canvas):
def __dealloc__(self):
if <void*>self.__canvas != NULL:
self.__canvas = NULL
cdef cppinc.Canvas* __getCanvas(self) except *:
if <void*>self.__canvas != NULL:
return self.__canvas
raise Exception("Canvas was destroyed or not initialized, you cannot use this object anymore")
def Fill(self, uint8_t red, uint8_t green, uint8_t blue):
(<cppinc.FrameCanvas*>self.__getCanvas()).Fill(red, green, blue)
def Clear(self):
(<cppinc.FrameCanvas*>self.__getCanvas()).Clear()
def SetPixel(self, int x, int y, uint8_t red, uint8_t green, uint8_t blue):
(<cppinc.FrameCanvas*>self.__getCanvas()).SetPixel(x, y, red, green, blue)
property width:
def __get__(self): return (<cppinc.FrameCanvas*>self.__getCanvas()).width()
property height:
def __get__(self): return (<cppinc.FrameCanvas*>self.__getCanvas()).height()
property pwmBits:
def __get__(self): return (<cppinc.FrameCanvas*>self.__getCanvas()).pwmbits()
def __set__(self, pwmBits): (<cppinc.FrameCanvas*>self.__getCanvas()).SetPWMBits(pwmBits)
property brightness:
def __get__(self): return (<cppinc.FrameCanvas*>self.__getCanvas()).brightness()
def __set__(self, val): (<cppinc.FrameCanvas*>self.__getCanvas()).SetBrightness(val)
cdef class RGBMatrixOptions:
def __cinit__(self):
self.__options = cppinc.Options()
self.__runtime_options = cppinc.RuntimeOptions()
# RGBMatrix::Options properties
property hardware_mapping:
def __get__(self): return self.__options.hardware_mapping
def __set__(self, value):
self.__py_encoded_hardware_mapping = value.encode('utf-8')
self.__options.hardware_mapping = self.__py_encoded_hardware_mapping
property rows:
def __get__(self): return self.__options.rows
def __set__(self, uint8_t value): self.__options.rows = value
property cols:
def __get__(self): return self.__options.cols
def __set__(self, uint32_t value): self.__options.cols = value
property chain_length:
def __get__(self): return self.__options.chain_length
def __set__(self, uint8_t value): self.__options.chain_length = value
property parallel:
def __get__(self): return self.__options.parallel
def __set__(self, uint8_t value): self.__options.parallel = value
property pwm_bits:
def __get__(self): return self.__options.pwm_bits
def __set__(self, uint8_t value): self.__options.pwm_bits = value
property pwm_lsb_nanoseconds:
def __get__(self): return self.__options.pwm_lsb_nanoseconds
def __set__(self, uint32_t value): self.__options.pwm_lsb_nanoseconds = value
property brightness:
def __get__(self): return self.__options.brightness
def __set__(self, uint8_t value): self.__options.brightness = value
property scan_mode:
def __get__(self): return self.__options.scan_mode
def __set__(self, uint8_t value): self.__options.scan_mode = value
property multiplexing:
def __get__(self): return self.__options.multiplexing
def __set__(self, uint8_t value): self.__options.multiplexing = value
property row_address_type:
def __get__(self): return self.__options.row_address_type
def __set__(self, uint8_t value): self.__options.row_address_type = value
property disable_hardware_pulsing:
def __get__(self): return self.__options.disable_hardware_pulsing
def __set__(self, value): self.__options.disable_hardware_pulsing = value
property show_refresh_rate:
def __get__(self): return self.__options.show_refresh_rate
def __set__(self, value): self.__options.show_refresh_rate = value
property inverse_colors:
def __get__(self): return self.__options.inverse_colors
def __set__(self, value): self.__options.inverse_colors = value
property led_rgb_sequence:
def __get__(self): return self.__options.led_rgb_sequence
def __set__(self, value):
self.__py_encoded_led_rgb_sequence = value.encode('utf-8')
self.__options.led_rgb_sequence = self.__py_encoded_led_rgb_sequence
property pixel_mapper_config:
def __get__(self): return self.__options.pixel_mapper_config
def __set__(self, value):
self.__py_encoded_pixel_mapper_config = value.encode('utf-8')
self.__options.pixel_mapper_config = self.__py_encoded_pixel_mapper_config
property panel_type:
def __get__(self): return self.__options.panel_type
def __set__(self, value):
self.__py_encoded_panel_type = value.encode('utf-8')
self.__options.panel_type = self.__py_encoded_panel_type
property pwm_dither_bits:
def __get__(self): return self.__options.pwm_dither_bits
def __set__(self, uint8_t value): self.__options.pwm_dither_bits = value
property limit_refresh_rate_hz:
def __get__(self): return self.__options.limit_refresh_rate_hz
def __set__(self, value): self.__options.limit_refresh_rate_hz = value
# RuntimeOptions properties
property gpio_slowdown:
def __get__(self): return self.__runtime_options.gpio_slowdown
def __set__(self, uint8_t value): self.__runtime_options.gpio_slowdown = value
property daemon:
def __get__(self): return self.__runtime_options.daemon
def __set__(self, uint8_t value): self.__runtime_options.daemon = value
property drop_privileges:
def __get__(self): return self.__runtime_options.drop_privileges
def __set__(self, uint8_t value): self.__runtime_options.drop_privileges = value
cdef class RGBMatrix(Canvas):
def __cinit__(self, int rows = 0, int chains = 0, int parallel = 0,
RGBMatrixOptions options = None):
# If RGBMatrixOptions not provided, create defaults and set any optional
# parameters supplied
if options == None:
options = RGBMatrixOptions()
if rows > 0:
options.rows = rows
if chains > 0:
options.chain_length = chains
if parallel > 0:
options.parallel = parallel
self.__matrix = cppinc.CreateMatrixFromOptions(options.__options,
options.__runtime_options)
def __dealloc__(self):
self.__matrix.Clear()
del self.__matrix
cdef cppinc.Canvas* __getCanvas(self) except *:
if <void*>self.__matrix != NULL:
return self.__matrix
raise Exception("Canvas was destroyed or not initialized, you cannot use this object anymore")
def Fill(self, uint8_t red, uint8_t green, uint8_t blue):
self.__matrix.Fill(red, green, blue)
def SetPixel(self, int x, int y, uint8_t red, uint8_t green, uint8_t blue):
self.__matrix.SetPixel(x, y, red, green, blue)
def Clear(self):
self.__matrix.Clear()
def CreateFrameCanvas(self):
return __createFrameCanvas(self.__matrix.CreateFrameCanvas())
# The optional "framerate_fraction" parameter allows to choose which
# multiple of the global frame-count to use. So it slows down your animation
# to an exact integer fraction of the refresh rate.
# Default is 1, so immediately next available frame.
# (Say you have 140Hz refresh rate, then a value of 5 would give you an
# 28Hz animation, nicely locked to the refresh-rate).
# If you combine this with RGBMatrixOptions.limit_refresh_rate_hz you can create
# time-correct animations.
def SwapOnVSync(self, FrameCanvas newFrame, uint8_t framerate_fraction = 1):
return __createFrameCanvas(self.__matrix.SwapOnVSync(newFrame.__canvas, framerate_fraction))
property luminanceCorrect:
def __get__(self): return self.__matrix.luminance_correct()
def __set__(self, luminanceCorrect): self.__matrix.set_luminance_correct(luminanceCorrect)
property pwmBits:
def __get__(self): return self.__matrix.pwmbits()
def __set__(self, pwmBits): self.__matrix.SetPWMBits(pwmBits)
property brightness:
def __get__(self): return self.__matrix.brightness()
def __set__(self, brightness): self.__matrix.SetBrightness(brightness)
property height:
def __get__(self): return self.__matrix.height()
property width:
def __get__(self): return self.__matrix.width()
cdef __createFrameCanvas(cppinc.FrameCanvas* newCanvas):
canvas = FrameCanvas()
canvas.__canvas = newCanvas
return canvas
# Local Variables:
# mode: python
# End:

@ -0,0 +1,88 @@
from libcpp cimport bool
from libc.stdint cimport uint8_t, uint32_t
########################
### External classes ###
########################
cdef extern from "canvas.h" namespace "rgb_matrix":
cdef cppclass Canvas:
int width()
int height()
void SetPixel(int, int, uint8_t, uint8_t, uint8_t) nogil
void Clear() nogil
void Fill(uint8_t, uint8_t, uint8_t) nogil
cdef extern from "led-matrix.h" namespace "rgb_matrix":
cdef cppclass RGBMatrix(Canvas):
bool SetPWMBits(uint8_t)
uint8_t pwmbits()
void set_luminance_correct(bool)
bool luminance_correct()
void SetBrightness(uint8_t)
uint8_t brightness()
FrameCanvas *CreateFrameCanvas()
FrameCanvas *SwapOnVSync(FrameCanvas*, uint8_t)
cdef cppclass FrameCanvas(Canvas):
bool SetPWMBits(uint8_t)
uint8_t pwmbits()
void SetBrightness(uint8_t)
uint8_t brightness()
struct RuntimeOptions:
RuntimeOptions() except +
int gpio_slowdown
int daemon
int drop_privileges
RGBMatrix *CreateMatrixFromOptions(Options &options, RuntimeOptions runtime_options)
cdef extern from "led-matrix.h" namespace "rgb_matrix::RGBMatrix":
cdef struct Options:
Options() except +
const char *hardware_mapping
int rows
int cols
int chain_length
int parallel
int pwm_bits
int pwm_lsb_nanoseconds
int brightness
int scan_mode
int row_address_type
int multiplexing
int pwm_dither_bits
int limit_refresh_rate_hz
bool disable_hardware_pulsing
bool show_refresh_rate
bool inverse_colors
const char *led_rgb_sequence
const char *pixel_mapper_config
const char *panel_type
cdef extern from "graphics.h" namespace "rgb_matrix":
cdef struct Color:
Color(uint8_t, uint8_t, uint8_t) except +
uint8_t r
uint8_t g
uint8_t b
cdef cppclass Font:
Font() except +
bool LoadFont(const char*)
int height()
int baseline()
int CharacterWidth(uint32_t)
int DrawGlyph(Canvas*, int, int, const Color, uint32_t);
cdef int DrawText(Canvas*, const Font, int, int, const Color, const char*)
cdef void DrawCircle(Canvas*, int, int, int, const Color)
cdef void DrawLine(Canvas*, int, int, int, int, const Color)

@ -0,0 +1,11 @@
cimport cppinc
cdef class Color:
cdef cppinc.Color __color
cdef class Font:
cdef cppinc.Font __font
# Local Variables:
# mode: python
# End:

@ -0,0 +1,54 @@
# distutils: language = c++
from libcpp cimport bool
from libc.stdint cimport uint8_t, uint32_t
cimport core
cdef class Color:
def __init__(self, uint8_t red = 0, uint8_t green = 0, uint8_t blue = 0):
self.__color.r = red
self.__color.g = green
self.__color.b = blue
property red:
def __get__(self): return self.__color.r
def __set__(self, uint8_t value): self.__color.r = value
property green:
def __get__(self): return self.__color.g
def __set__(self, uint8_t value): self.__color.g = value
property blue:
def __get__(self): return self.__color.b
def __set__(self, uint8_t value): self.__color.b = value
cdef class Font:
def CharacterWidth(self, uint32_t char):
return self.__font.CharacterWidth(char)
def LoadFont(self, file):
if (not self.__font.LoadFont(file.encode('utf-8'))):
raise Exception("Couldn't load font " + file)
def DrawGlyph(self, core.Canvas c, int x, int y, Color color, uint32_t char):
return self.__font.DrawGlyph(c.__getCanvas(), x, y, color.__color, char)
property height:
def __get__(self): return self.__font.height()
property baseline:
def __get__(self): return self.__font.baseline()
def DrawText(core.Canvas c, Font f, int x, int y, Color color, text):
return cppinc.DrawText(c.__getCanvas(), f.__font, x, y, color.__color, text.encode('utf-8'))
def DrawCircle(core.Canvas c, int x, int y, int r, Color color):
cppinc.DrawCircle(c.__getCanvas(), x, y, r, color.__color)
def DrawLine(core.Canvas c, int x1, int y1, int x2, int y2, Color color):
cppinc.DrawLine(c.__getCanvas(), x1, y1, x2, y2, color.__color)
# Local Variables:
# mode: python
# End:

@ -0,0 +1,60 @@
#!/usr/bin/env python
import time
import sys
from rgbmatrix import RGBMatrix, RGBMatrixOptions
from PIL import Image
if len(sys.argv) < 2:
sys.exit("Require a gif argument")
else:
image_file = sys.argv[1]
gif = Image.open(image_file)
try:
num_frames = gif.n_frames
except Exception:
sys.exit("provided image is not a gif")
# Configuration for the matrix
options = RGBMatrixOptions()
options.rows = 32
options.cols = 32
options.chain_length = 1
options.parallel = 1
options.hardware_mapping = 'regular' # If you have an Adafruit HAT: 'adafruit-hat'
matrix = RGBMatrix(options = options)
# Preprocess the gifs frames into canvases to improve playback performance
canvases = []
print("Preprocessing gif, this may take a moment depending on the size of the gif...")
for frame_index in range(0, num_frames):
gif.seek(frame_index)
# must copy the frame out of the gif, since thumbnail() modifies the image in-place
frame = gif.copy()
frame.thumbnail((matrix.width, matrix.height), Image.ANTIALIAS)
canvas = matrix.CreateFrameCanvas()
canvas.SetImage(frame.convert("RGB"))
canvases.append(canvas)
# Close the gif file to save memory now that we have copied out all of the frames
gif.close()
print("Completed Preprocessing, displaying gif")
try:
print("Press CTRL-C to stop.")
# Infinitely loop through the gif
cur_frame = 0
while(True):
matrix.SwapOnVSync(canvases[cur_frame], framerate_fraction=10)
if cur_frame == num_frames - 1:
cur_frame = 0
else:
cur_frame += 1
except KeyboardInterrupt:
sys.exit(0)

@ -0,0 +1,32 @@
#!/usr/bin/env python
from samplebase import SampleBase
from rgbmatrix import graphics
import time
class GraphicsTest(SampleBase):
def __init__(self, *args, **kwargs):
super(GraphicsTest, self).__init__(*args, **kwargs)
def run(self):
canvas = self.matrix
font = graphics.Font()
font.LoadFont("../../../fonts/7x13.bdf")
red = graphics.Color(255, 0, 0)
graphics.DrawLine(canvas, 5, 5, 22, 13, red)
green = graphics.Color(0, 255, 0)
graphics.DrawCircle(canvas, 15, 15, 10, green)
blue = graphics.Color(0, 0, 255)
graphics.DrawText(canvas, font, 2, 10, blue, "Text")
time.sleep(10) # show display for 10 seconds before exit
# Main function
if __name__ == "__main__":
graphics_test = GraphicsTest()
if (not graphics_test.process()):
graphics_test.print_help()

@ -0,0 +1,39 @@
#!/usr/bin/env python
from samplebase import SampleBase
import time
class GrayscaleBlock(SampleBase):
def __init__(self, *args, **kwargs):
super(GrayscaleBlock, self).__init__(*args, **kwargs)
def run(self):
sub_blocks = 16
width = self.matrix.width
height = self.matrix.height
x_step = max(1, width / sub_blocks)
y_step = max(1, height / sub_blocks)
count = 0
while True:
for y in range(0, height):
for x in range(0, width):
c = sub_blocks * int(y / y_step) + int(x / x_step)
if count % 4 == 0:
self.matrix.SetPixel(x, y, c, c, c)
elif count % 4 == 1:
self.matrix.SetPixel(x, y, c, 0, 0)
elif count % 4 == 2:
self.matrix.SetPixel(x, y, 0, c, 0)
elif count % 4 == 3:
self.matrix.SetPixel(x, y, 0, 0, c)
count += 1
time.sleep(2)
# Main function
if __name__ == "__main__":
grayscale_block = GrayscaleBlock()
if (not grayscale_block.process()):
grayscale_block.print_help()

@ -0,0 +1,48 @@
#!/usr/bin/env python
# (This is an example similar to an example from the Adafruit fork
# to show the similarities. Most important difference currently is, that
# this library wants RGB mode.)
#
# A more complex RGBMatrix example works with the Python Imaging Library,
# demonstrating a few graphics primitives and image loading.
# Note that PIL graphics do not have an immediate effect on the display --
# image is drawn into a separate buffer, which is then copied to the matrix
# using the SetImage() function (see examples below).
# Requires rgbmatrix.so present in the same directory.
# PIL Image module (create or load images) is explained here:
# http://effbot.org/imagingbook/image.htm
# PIL ImageDraw module (draw shapes to images) explained here:
# http://effbot.org/imagingbook/imagedraw.htm
from PIL import Image
from PIL import ImageDraw
import time
from rgbmatrix import RGBMatrix, RGBMatrixOptions
# Configuration for the matrix
options = RGBMatrixOptions()
options.rows = 32
options.chain_length = 1
options.parallel = 1
options.hardware_mapping = 'regular' # If you have an Adafruit HAT: 'adafruit-hat'
matrix = RGBMatrix(options = options)
# RGB example w/graphics prims.
# Note, only "RGB" mode is supported currently.
image = Image.new("RGB", (32, 32)) # Can be larger than matrix if wanted!!
draw = ImageDraw.Draw(image) # Declare Draw instance before prims
# Draw some shapes into image (no immediate effect on matrix)...
draw.rectangle((0, 0, 31, 31), fill=(0, 0, 0), outline=(0, 0, 255))
draw.line((0, 0, 31, 31), fill=(255, 0, 0))
draw.line((0, 31, 31, 0), fill=(0, 255, 0))
# Then scroll image across matrix...
for n in range(-32, 33): # Start off top-left, move off bottom-right
matrix.Clear()
matrix.SetImage(image, n, n)
time.sleep(0.05)
matrix.Clear()

@ -0,0 +1,40 @@
#!/usr/bin/env python
import time
from samplebase import SampleBase
from PIL import Image
class ImageScroller(SampleBase):
def __init__(self, *args, **kwargs):
super(ImageScroller, self).__init__(*args, **kwargs)
self.parser.add_argument("-i", "--image", help="The image to display", default="../../../examples-api-use/runtext.ppm")
def run(self):
if not 'image' in self.__dict__:
self.image = Image.open(self.args.image).convert('RGB')
self.image.resize((self.matrix.width, self.matrix.height), Image.ANTIALIAS)
double_buffer = self.matrix.CreateFrameCanvas()
img_width, img_height = self.image.size
# let's scroll
xpos = 0
while True:
xpos += 1
if (xpos > img_width):
xpos = 0
double_buffer.SetImage(self.image, -xpos)
double_buffer.SetImage(self.image, -xpos + img_width)
double_buffer = self.matrix.SwapOnVSync(double_buffer)
time.sleep(0.01)
# Main function
# e.g. call with
# sudo ./image-scroller.py --chain=4
# if you have a chain of four
if __name__ == "__main__":
image_scroller = ImageScroller()
if (not image_scroller.process()):
image_scroller.print_help()

@ -0,0 +1,34 @@
#!/usr/bin/env python
import time
import sys
from rgbmatrix import RGBMatrix, RGBMatrixOptions
from PIL import Image
if len(sys.argv) < 2:
sys.exit("Require an image argument")
else:
image_file = sys.argv[1]
image = Image.open(image_file)
# Configuration for the matrix
options = RGBMatrixOptions()
options.rows = 32
options.chain_length = 1
options.parallel = 1
options.hardware_mapping = 'regular' # If you have an Adafruit HAT: 'adafruit-hat'
matrix = RGBMatrix(options = options)
# Make image fit our screen.
image.thumbnail((matrix.width, matrix.height), Image.ANTIALIAS)
matrix.SetImage(image.convert('RGB'))
try:
print("Press CTRL-C to stop.")
while True:
time.sleep(100)
except KeyboardInterrupt:
sys.exit(0)

@ -0,0 +1,36 @@
#!/usr/bin/env python
from samplebase import SampleBase
class GrayscaleBlock(SampleBase):
def __init__(self, *args, **kwargs):
super(GrayscaleBlock, self).__init__(*args, **kwargs)
def run(self):
max_brightness = self.matrix.brightness
count = 0
c = 255
while (True):
if self.matrix.brightness < 1:
self.matrix.brightness = max_brightness
count += 1
else:
self.matrix.brightness -= 1
if count % 4 == 0:
self.matrix.Fill(c, 0, 0)
elif count % 4 == 1:
self.matrix.Fill(0, c, 0)
elif count % 4 == 2:
self.matrix.Fill(0, 0, c)
elif count % 4 == 3:
self.matrix.Fill(c, c, c)
self.usleep(20 * 1000)
# Main function
if __name__ == "__main__":
grayscale_block = GrayscaleBlock()
if (not grayscale_block.process()):
grayscale_block.print_help()

@ -0,0 +1,42 @@
#!/usr/bin/env python
from samplebase import SampleBase
class PulsingColors(SampleBase):
def __init__(self, *args, **kwargs):
super(PulsingColors, self).__init__(*args, **kwargs)
def run(self):
self.offscreen_canvas = self.matrix.CreateFrameCanvas()
continuum = 0
while True:
self.usleep(5 * 1000)
continuum += 1
continuum %= 3 * 255
red = 0
green = 0
blue = 0
if continuum <= 255:
c = continuum
blue = 255 - c
red = c
elif continuum > 255 and continuum <= 511:
c = continuum - 256
red = 255 - c
green = c
else:
c = continuum - 512
green = 255 - c
blue = c
self.offscreen_canvas.Fill(red, green, blue)
self.offscreen_canvas = self.matrix.SwapOnVSync(self.offscreen_canvas)
# Main function
if __name__ == "__main__":
pulsing_colors = PulsingColors()
if (not pulsing_colors.process()):
pulsing_colors.print_help()

@ -0,0 +1,72 @@
#!/usr/bin/env python
from samplebase import SampleBase
import math
def scale_col(val, lo, hi):
if val < lo:
return 0
if val > hi:
return 255
return 255 * (val - lo) / (hi - lo)
def rotate(x, y, sin, cos):
return x * cos - y * sin, x * sin + y * cos
class RotatingBlockGenerator(SampleBase):
def __init__(self, *args, **kwargs):
super(RotatingBlockGenerator, self).__init__(*args, **kwargs)
def run(self):
cent_x = self.matrix.width / 2
cent_y = self.matrix.height / 2
rotate_square = min(self.matrix.width, self.matrix.height) * 1.41
min_rotate = cent_x - rotate_square / 2
max_rotate = cent_x + rotate_square / 2
display_square = min(self.matrix.width, self.matrix.height) * 0.7
min_display = cent_x - display_square / 2
max_display = cent_x + display_square / 2
deg_to_rad = 2 * 3.14159265 / 360
rotation = 0
# Pre calculate colors
col_table = []
for x in range(int(min_rotate), int(max_rotate)):
col_table.insert(x, scale_col(x, min_display, max_display))
offset_canvas = self.matrix.CreateFrameCanvas()
while True:
rotation += 1
rotation %= 360
# calculate sin and cos once for each frame
angle = rotation * deg_to_rad
sin = math.sin(angle)
cos = math.cos(angle)
for x in range(int(min_rotate), int(max_rotate)):
for y in range(int(min_rotate), int(max_rotate)):
# Our rotate center is always offset by cent_x
rot_x, rot_y = rotate(x - cent_x, y - cent_x, sin, cos)
if x >= min_display and x < max_display and y >= min_display and y < max_display:
x_col = col_table[x]
y_col = col_table[y]
offset_canvas.SetPixel(rot_x + cent_x, rot_y + cent_y, x_col, 255 - y_col, y_col)
else:
offset_canvas.SetPixel(rot_x + cent_x, rot_y + cent_y, 0, 0, 0)
offset_canvas = self.matrix.SwapOnVSync(offset_canvas)
# Main function
if __name__ == "__main__":
rotating_block_generator = RotatingBlockGenerator()
if (not rotating_block_generator.process()):
rotating_block_generator.print_help()

@ -0,0 +1,36 @@
#!/usr/bin/env python
# Display a runtext with double-buffering.
from samplebase import SampleBase
from rgbmatrix import graphics
import time
class RunText(SampleBase):
def __init__(self, *args, **kwargs):
super(RunText, self).__init__(*args, **kwargs)
self.parser.add_argument("-t", "--text", help="The text to scroll on the RGB LED panel", default="Hello world!")
def run(self):
offscreen_canvas = self.matrix.CreateFrameCanvas()
font = graphics.Font()
font.LoadFont("../../../fonts/7x13.bdf")
textColor = graphics.Color(255, 255, 0)
pos = offscreen_canvas.width
my_text = self.args.text
while True:
offscreen_canvas.Clear()
len = graphics.DrawText(offscreen_canvas, font, pos, 10, textColor, my_text)
pos -= 1
if (pos + len < 0):
pos = offscreen_canvas.width
time.sleep(0.05)
offscreen_canvas = self.matrix.SwapOnVSync(offscreen_canvas)
# Main function
if __name__ == "__main__":
run_text = RunText()
if (not run_text.process()):
run_text.print_help()

@ -0,0 +1,81 @@
import argparse
import time
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(__file__) + '/..'))
from rgbmatrix import RGBMatrix, RGBMatrixOptions
class SampleBase(object):
def __init__(self, *args, **kwargs):
self.parser = argparse.ArgumentParser()
self.parser.add_argument("-r", "--led-rows", action="store", help="Display rows. 16 for 16x32, 32 for 32x32. Default: 32", default=32, type=int)
self.parser.add_argument("--led-cols", action="store", help="Panel columns. Typically 32 or 64. (Default: 32)", default=32, type=int)
self.parser.add_argument("-c", "--led-chain", action="store", help="Daisy-chained boards. Default: 1.", default=1, type=int)
self.parser.add_argument("-P", "--led-parallel", action="store", help="For Plus-models or RPi2: parallel chains. 1..3. Default: 1", default=1, type=int)
self.parser.add_argument("-p", "--led-pwm-bits", action="store", help="Bits used for PWM. Something between 1..11. Default: 11", default=11, type=int)
self.parser.add_argument("-b", "--led-brightness", action="store", help="Sets brightness level. Default: 100. Range: 1..100", default=100, type=int)
self.parser.add_argument("-m", "--led-gpio-mapping", help="Hardware Mapping: regular, adafruit-hat, adafruit-hat-pwm" , choices=['regular', 'regular-pi1', 'adafruit-hat', 'adafruit-hat-pwm'], type=str)
self.parser.add_argument("--led-scan-mode", action="store", help="Progressive or interlaced scan. 0 Progressive, 1 Interlaced (default)", default=1, choices=range(2), type=int)
self.parser.add_argument("--led-pwm-lsb-nanoseconds", action="store", help="Base time-unit for the on-time in the lowest significant bit in nanoseconds. Default: 130", default=130, type=int)
self.parser.add_argument("--led-show-refresh", action="store_true", help="Shows the current refresh rate of the LED panel")
self.parser.add_argument("--led-slowdown-gpio", action="store", help="Slow down writing to GPIO. Range: 0..4. Default: 1", default=1, type=int)
self.parser.add_argument("--led-no-hardware-pulse", action="store", help="Don't use hardware pin-pulse generation")
self.parser.add_argument("--led-rgb-sequence", action="store", help="Switch if your matrix has led colors swapped. Default: RGB", default="RGB", type=str)
self.parser.add_argument("--led-pixel-mapper", action="store", help="Apply pixel mappers. e.g \"Rotate:90\"", default="", type=str)
self.parser.add_argument("--led-row-addr-type", action="store", help="0 = default; 1=AB-addressed panels; 2=row direct; 3=ABC-addressed panels; 4 = ABC Shift + DE direct", default=0, type=int, choices=[0,1,2,3,4])
self.parser.add_argument("--led-multiplexing", action="store", help="Multiplexing type: 0=direct; 1=strip; 2=checker; 3=spiral; 4=ZStripe; 5=ZnMirrorZStripe; 6=coreman; 7=Kaler2Scan; 8=ZStripeUneven... (Default: 0)", default=0, type=int)
self.parser.add_argument("--led-panel-type", action="store", help="Needed to initialize special panels. Supported: 'FM6126A'", default="", type=str)
self.parser.add_argument("--led-no-drop-privs", dest="drop_privileges", help="Don't drop privileges from 'root' after initializing the hardware.", action='store_false')
self.parser.set_defaults(drop_privileges=True)
def usleep(self, value):
time.sleep(value / 1000000.0)
def run(self):
print("Running")
def process(self):
self.args = self.parser.parse_args()
options = RGBMatrixOptions()
if self.args.led_gpio_mapping != None:
options.hardware_mapping = self.args.led_gpio_mapping
options.rows = self.args.led_rows
options.cols = self.args.led_cols
options.chain_length = self.args.led_chain
options.parallel = self.args.led_parallel
options.row_address_type = self.args.led_row_addr_type
options.multiplexing = self.args.led_multiplexing
options.pwm_bits = self.args.led_pwm_bits
options.brightness = self.args.led_brightness
options.pwm_lsb_nanoseconds = self.args.led_pwm_lsb_nanoseconds
options.led_rgb_sequence = self.args.led_rgb_sequence
options.pixel_mapper_config = self.args.led_pixel_mapper
options.panel_type = self.args.led_panel_type
if self.args.led_show_refresh:
options.show_refresh_rate = 1
if self.args.led_slowdown_gpio != None:
options.gpio_slowdown = self.args.led_slowdown_gpio
if self.args.led_no_hardware_pulse:
options.disable_hardware_pulsing = True
if not self.args.drop_privileges:
options.drop_privileges=False
self.matrix = RGBMatrix(options = options)
try:
# Start loop
print("Press CTRL-C to stop sample")
self.run()
except KeyboardInterrupt:
print("Exiting\n")
sys.exit(0)
return True

@ -0,0 +1,30 @@
#!/usr/bin/env python
from samplebase import SampleBase
class SimpleSquare(SampleBase):
def __init__(self, *args, **kwargs):
super(SimpleSquare, self).__init__(*args, **kwargs)
def run(self):
offset_canvas = self.matrix.CreateFrameCanvas()
while True:
for x in range(0, self.matrix.width):
offset_canvas.SetPixel(x, x, 255, 255, 255)
offset_canvas.SetPixel(offset_canvas.height - 1 - x, x, 255, 0, 255)
for x in range(0, offset_canvas.width):
offset_canvas.SetPixel(x, 0, 255, 0, 0)
offset_canvas.SetPixel(x, offset_canvas.height - 1, 255, 255, 0)
for y in range(0, offset_canvas.height):
offset_canvas.SetPixel(0, y, 0, 0, 255)
offset_canvas.SetPixel(offset_canvas.width - 1, y, 0, 255, 0)
offset_canvas = self.matrix.SwapOnVSync(offset_canvas)
# Main function
if __name__ == "__main__":
simple_square = SimpleSquare()
if (not simple_square.process()):
simple_square.print_help()

@ -0,0 +1,33 @@
#!/usr/bin/python
from distutils.core import setup, Extension
core_ext = Extension(
name = 'core',
sources = ['rgbmatrix/core.cpp'],
include_dirs = ['../../include'],
library_dirs = ['../../lib'],
libraries = ['rgbmatrix'],
extra_compile_args = ["-O3", "-Wall"],
language = 'c++'
)
graphics_ext = Extension(
name = 'graphics',
sources = ['rgbmatrix/graphics.cpp'],
include_dirs = ['../../include'],
library_dirs = ['../../lib'],
libraries = ['rgbmatrix'],
extra_compile_args = ["-O3", "-Wall"],
language = 'c++'
)
setup(
name = 'rgbmatrix',
version = '0.0.1',
author = 'Christoph Friedrich',
author_email = 'christoph.friedrich@vonaffenfels.de',
classifiers = ['Development Status :: 3 - Alpha'],
ext_package = 'rgbmatrix',
ext_modules = [core_ext, graphics_ext],
packages = ['rgbmatrix']
)

@ -0,0 +1,7 @@
demo
minimal-example
text-example
c-example
clock
scrolling-text-example
ledcat

@ -0,0 +1,60 @@
CFLAGS=-Wall -O3 -g -Wextra -Wno-unused-parameter
CXXFLAGS=$(CFLAGS)
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
BINARIES=demo minimal-example c-example text-example scrolling-text-example clock ledcat input-example pixel-mover
# Where our library resides. You mostly only need to change the
# RGB_LIB_DISTRIBUTION, this is where the library is checked out.
RGB_LIB_DISTRIBUTION=..
RGB_INCDIR=$(RGB_LIB_DISTRIBUTION)/include
RGB_LIBDIR=$(RGB_LIB_DISTRIBUTION)/lib
RGB_LIBRARY_NAME=rgbmatrix
RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a
LDFLAGS+=-L$(RGB_LIBDIR) -l$(RGB_LIBRARY_NAME) -lrt -lm -lpthread
# To compile image-example
MAGICK_CXXFLAGS?=$(shell GraphicsMagick++-config --cppflags --cxxflags)
MAGICK_LDFLAGS?=$(shell GraphicsMagick++-config --ldflags --libs)
all : $(BINARIES)
$(RGB_LIBRARY): FORCE
$(MAKE) -C $(RGB_LIBDIR)
demo : demo-main.o $(RGB_LIBRARY)
$(CXX) $< -o $@ $(LDFLAGS)
minimal-example : minimal-example.o
input-example : input-example.o
text-example: text-example.o
scrolling-text-example : scrolling-text-example.o
clock : clock.o
ledcat : ledcat.o
pixel-mover : pixel-mover.o
# All the binaries that have the same name as the object file.q
% : %.o $(RGB_LIBRARY)
$(CXX) $< -o $@ $(LDFLAGS)
image-example.o : image-example.cc
$(CXX) -I$(RGB_INCDIR) $(CXXFLAGS) $(MAGICK_CXXFLAGS) -c -o $@ $<
image-example: image-example.o $(RGB_LIBRARY)
$(CXX) $< -o $@ $(LDFLAGS) $(MAGICK_LDFLAGS)
# Since the C example uses the C++ library underneath, which depends on C++
# runtime stuff, you still have to also link -lstdc++
c-example : c-example.o $(RGB_LIBRARY)
$(CC) $< -o $@ $(LDFLAGS) -lstdc++
%.o : %.cc
$(CXX) -I$(RGB_INCDIR) $(CXXFLAGS) -c -o $@ $<
%.o : %.c
$(CC) -I$(RGB_INCDIR) $(CFLAGS) -c -o $@ $<
clean:
rm -f $(OBJECTS) $(BINARIES)
FORCE:
.PHONY: FORCE

@ -0,0 +1,461 @@
Running some demos
------------------
Let's start by running some demos, then we can dive into code. The
[demo-main.cc](demo-main.cc) has some testing demos. Via command line flags,
you can choose the display type you have (16x32 or 32x32), and how many you
have chained and paralleled. For detailed description of these flags see the
[main README section](../README.md#changing-parameters-via-command-line-flags)
about it.
```
$ make
$ sudo ./demo
usage: ./demo <options> -D <demo-nr> [optional parameter]
Options:
-D <demo-nr> : Always needs to be set
--led-gpio-mapping=<name> : Name of GPIO mapping used. Default "regular"
--led-rows=<rows> : Panel rows. Typically 8, 16, 32 or 64. (Default: 32).
--led-cols=<cols> : Panel columns. Typically 32 or 64. (Default: 32).
--led-chain=<chained> : Number of daisy-chained panels. (Default: 1).
--led-parallel=<parallel> : Parallel chains. range=1..3 (Default: 1).
--led-multiplexing=<0..17> : Mux type: 0=direct; 1=Stripe; 2=Checkered; 3=Spiral; 4=ZStripe; 5=ZnMirrorZStripe; 6=coreman; 7=Kaler2Scan; 8=ZStripeUneven; 9=P10-128x4-Z; 10=QiangLiQ8; 11=InversedZStripe; 12=P10Outdoor1R1G1-1; 13=P10Outdoor1R1G1-2; 14=P10Outdoor1R1G1-3; 15=P10CoremanMapper; 16=P8Outdoor1R1G1; 17=FlippedStripe (Default: 0)
--led-pixel-mapper : Semicolon-separated list of pixel-mappers to arrange pixels.
Optional params after a colon e.g. "U-mapper;Rotate:90"
Available: "Mirror", "Rotate", "U-mapper", "V-mapper". Default: ""
--led-pwm-bits=<1..11> : PWM bits (Default: 11).
--led-brightness=<percent>: Brightness in percent (Default: 100).
--led-scan-mode=<0..1> : 0 = progressive; 1 = interlaced (Default: 0).
--led-row-addr-type=<0..4>: 0 = default; 1 = AB-addressed panels; 2 = direct row select; 3 = ABC-addressed panels; 4 = ABC Shift + DE direct (Default: 0).
--led-show-refresh : Show refresh rate.
--led-limit-refresh=<Hz> : Limit refresh rate to this frequency in Hz. Useful to keep a
constant refresh rate on loaded system. 0=no limit. Default: 0
--led-inverse : Switch if your matrix has inverse colors on.
--led-rgb-sequence : Switch if your matrix has led colors swapped (Default: "RGB")
--led-pwm-lsb-nanoseconds : PWM Nanoseconds for LSB (Default: 130)
--led-pwm-dither-bits=<0..2> : Time dithering of lower bits (Default: 0)
--led-no-hardware-pulse : Don't use hardware pin-pulse generation.
--led-panel-type=<name> : Needed to initialize special panels. Supported: 'FM6126A', 'FM6127'
--led-slowdown-gpio=<0..4>: Slowdown GPIO. Needed for faster Pis/slower panels (Default: 1).
--led-daemon : Make the process run in the background as daemon.
--led-no-drop-privs : Don't drop privileges from 'root' after initializing the hardware.
Demos, choosen with -D
0 - some rotating square
1 - forward scrolling an image (-m <scroll-ms>)
2 - backward scrolling an image (-m <scroll-ms>)
3 - test image: a square
4 - Pulsing color
5 - Grayscale Block
6 - Abelian sandpile model (-m <time-step-ms>)
7 - Conway's game of life (-m <time-step-ms>)
8 - Langton's ant (-m <time-step-ms>)
9 - Volume bars (-m <time-step-ms>)
10 - Evolution of color (-m <time-step-ms>)
11 - Brightness pulse generator
Example:
./demo -D 1 runtext.ppm
Scrolls the runtext until Ctrl-C is pressed
```
To run the actual demos, you need to run this as root so that the
GPIO pins can be accessed; as soon as that established, the program will drop
the privileges.
Here is how demo '1' looks. It requires a ppm (type raw) with a height of
32 pixel - it is infinitely scrolled over the screen; for
convenience, there is a little runtext.ppm example included:
$ sudo ./demo -D 1 runtext.ppm
Here is a video of how it looks
[![Runtext][run-vid]](http://youtu.be/OJvEWyvO4ro)
Other Examples
--------------
There are a few other examples that you can use as starting point for your
own exploration of the API. If you just type `make` in this directory, the
Makefile will build all of these, so they are ready to use. Some examples
need BDF fonts, of which there are a few provided in [../fonts](../fonts).
Some of these example programs are described in more detail further down this
page.
* [minimal-example](./minimal-example.cc) Good to get started with the API
* [image-example](./image-example.cc) How to show an image (requires to install the graphics magic library, see in the header of that demo)
* [text-example](./text-example.cc) Reads text from stdin and displays it.
* [scrolling-text-example](./scrolling-text-example.cc) Scrolls a text
given on the command-line.
* [clock](./clock.cc) Shows a clock with choosable date format string in
one or multiple lines.
* [input-example](./input-example.cc) Example how to use the LED-Matrix but
also read inputs from free GPIO-pins. Needed if you build some interactive
piece.
* [ledcat](./ledcat.cc) LED-cat compatible reading of pixels from stdin.
* [pixel-mover](./pixel-mover.cc) Displays pixel on the display
and it's expected position on the terminal. Helpful for testing panels and
figuring out new multiplexing mappings.
Shows single dot or leaves a trail with length passed with `-t` option
(think of 'snake').
Can move around the pixel with W=Up, S=Down, A=Left, D=Right keys.
Using the API
-------------
While there is the demo program and the [utilities](../utils), this code can
be used independently as a library to be used in your own programs.
The includes are in `include/`, the library to link is built
in `lib/`. This is a C++ also with C bindings. There is also a
[Python](../bindings/python) and [c#](../bindings/c%23) bindings.
Getting started
---------------
The relevant part to start with is to look at
[led-matrix.h](../include/led-matrix.h).
You can would typically use the `RGBMatrix::CreateFromFlags()` factory to
create an RGBMatrix and then go from there.
```C++
#include "led-matrix.h"
using rgb_matrix::RGBMatrix;
int main(int argc, char **argv) {
// Set some defaults
RGBMatrix::Options my_defaults;
my_defaults.hardware_mapping = "regular"; // or e.g. "adafruit-hat" or "adafruit-hat-pwm"
my_defaults.chain_length = 3;
my_defaults.show_refresh_rate = true;
rgb_matrix::RuntimeOptions runtime_defaults;
// If you drop privileges, the root user you start the program with
// to be able to initialize the hardware will be switched to an unprivileged
// user to minimize a potential security attack surface.
runtime_defaults.drop_privileges = 1;
RGBMatrix *matrix = RGBMatrix::CreateFromFlags(&argc, &argv,
&my_defaults,
&runtime_defaults);
if (matrix == NULL) {
PrintMatrixFlags(stderr, my_defaults, runtime_defaults);
return 1;
}
// matrix->ApplyPixelMapper(...); // Optional
// Do your own command line handling with the remaining options.
// .. now use matrix
delete matrix; // Make sure to delete it in the end.
}
```
The `RGBMatrix` is essentially a canvas, it provides some basic functionality
such as `SetPixel()`, `Fill()` or `Clear()`. If you want to do more, you
might be interested in functions provided in the
[graphics.h](../include/graphics.h) header.
If you have animations, you might be interested in double-buffering. There is
a way to create new canvases with `CreateFrameCanvas()`, and then use
`SwapOnVSync()` to change the content atomically. See API documentation for
details.
Start with the [minimal-example.cc](./minimal-example.cc) to start.
If you are interested in drawing text and the font drawing functions in
graphics.h, have a look at the [text example](./text-example.cc):
```
sudo ./text-example -f ../fonts/8x13.bdf
hello
```
<img src="../img/text-no-ghosting.jpg" height="100px">
How about a clock ?
```
sudo ./clock -f ../fonts/7x13.bdf --led-chain=2 -d "%A" -d "%H:%M:%S"
```
<img src="../img/time-display.jpg" height="100px">
Fonts are in a human readable and editbable `*.bdf` format. There are some
public domain fonts available in the [`../fonts/`](../fonts) directory. Any
other fonts you might want to use or scale to the size you need can be
converted to a BDF format (either with a font editor or the [otf2bdf] tool).
Here is an example how you could create a 30pixel high BDF font from some
TrueType font:
```bash
otf2bdf -v -o myfont.bdf -r 72 -p 30 /path/to/font-Bold.ttf
```
Integrating in your own application
-----------------------------------
Until this library shows up in your favorite Linux distribution, you can just
include the library via github; it is pretty easy to be up-to-date.
I suggest to add this code as a sub-module in your git repository. That way
you can use that particular version and easily update it if there are changes:
git submodule add https://github.com/hzeller/rpi-rgb-led-matrix.git matrix
(Read more about how to use [submodules in git][git-submodules])
This will check out the repository in a subdirectory `matrix/`.
The library to build would be in directory `matrix/lib`, so let's hook that
into your toplevel Makefile.
I suggest to set up some variables like this; you only need to change the
location `RGB_LIB_DISTRIBUTION` is pointing to; in the sub-module example, this
was the `matrix` directory:
RGB_LIB_DISTRIBUTION=matrix
RGB_INCDIR=$(RGB_LIB_DISTRIBUTION)/include
RGB_LIBDIR=$(RGB_LIB_DISTRIBUTION)/lib
RGB_LIBRARY_NAME=rgbmatrix
RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a
LDFLAGS+=-L$(RGB_LIBDIR) -l$(RGB_LIBRARY_NAME) -lrt -lm -lpthread
Also, you want to add a target to build the libary in your sub-module
# (FYI: Make sure, there is a TAB-character in front of the $(MAKE))
$(RGB_LIBRARY):
$(MAKE) -C $(RGB_LIBDIR)
Now, your final binary needs to depend on your objects and also the
`$(RGB_LIBRARY)`
my-binary : $(OBJECTS) $(RGB_LIBRARY)
$(CXX) $(CXXFLAGS) $(OBJECTS) -o $@ $(LDFLAGS)
As an example, see the [PixelPusher implementation][pixelpush] which is using
this library in a git sub-module.
If you are writing your own Makefile, make sure to pass the `-O3` option to
the compiler to make sure to generate fast code.
Note, all the types provided are in the `rgb_matrix` namespace. That way, they
won't clash with other types you might use in your code; in particular pretty
common names such as `GPIO` or `Canvas` might run into clashing trouble.
Anyway, for convenience you just might add using-declarations in your
code:
// Types exported by the RGB-Matrix library.
using rgb_matrix::Canvas;
using rgb_matrix::GPIO;
using rgb_matrix::RGBMatrix;
Or, if you are lazy, just import the whole namespace:
using namespace rgb_matrix;
Read the [`minimal-example.cc`](./minimal-example.cc) to get started, then
have a look into [`demo-main.cc`](./demo-main.cc).
## Remapping coordinates ##
You might choose a different physical layout than the wiring provides.
There is an option `--led-pixel-mapper` that allows you to choose between
some re-mapping options, and also programmatic ways to do so.
### Standard mappers
#### U-mapper (U-shape connection)
Say you have 4 displays with 32x32 and only a single output
like with a Raspberry Pi 1 or the Adafruit HAT -- if we chain
them, we get a display 32 pixel high, (4*32)=128 pixel long. If we arrange
the boards in a U-shape so that they form a square, we get a logical display
of 64x64 pixels:
<img src="../img/chained-64x64.jpg" width="400px"> In action:
[![PixelPusher video][pp-vid]](http://youtu.be/ZglGuMaKvpY)
```
So the following chain (Viewed looking at the LED-side of the panels)
[<][<][<][<] }- Raspbery Pi connector
is arranged in this U-shape (on its side)
[<][<] }----- Raspberry Pi connector
[>][>]
```
Now we need to internally map pixels the pixels so that the 'folded' 128x32
screen behaves like a 64x64 screen.
There is a pixel-mapper that can help with this "U-Arrangement", you choose
it with `--led-pixel-mapper=U-mapper`. So in this particular case,
```
./demo --led-chain=4 --led-pixel-mapper="U-mapper"
```
This works for longer and more than one chain as well. Here an arrangement with
two chains with 8 panels each
```
[<][<][<][<] }--- Pi connector #1
[>][>][>][>]
[<][<][<][<] }--- Pi connector #2
[>][>][>][>]
```
(`--led-chain=8 --led-parallel=2 --led-pixel-mapper="U-mapper"`).
#### V-mapper and Vmapper:Z (Vertical arrangement)
By default, when you add panels on a chain, they are added horizontally.
If you have 2 panels of 64x32, you get 128x32.
The V-mapper allows the stacking to be vertical and not horizontal and
get the 64x64 you might want.
By default, all the panels are correct side up, and you need more cable length
as you need to cross back to the start of the next panel.
If you wish to use shorter cables, you can add use Vmapper:Z which will give
you serpentine cabling and every other panel will be upside down (see below
for an example).
It is compatible with parallel chains, so you can have multiple stacks
of panels all building a coherent overall display.
Here an example with 3 chains of 4 panels (128x64) for a total of about
98k display pixels.
```
./demo --led-rows=64 --led-cols=128 --led-chain=4 -led-parallel=3 --led-pixel-mapper=V-mapper -D0
```
Viewed looking the LED-side of the panels:
```
Vmapper Vmapper:Z
[O < I] [O < I] [O < I] [I > O] [I > O] [I > O]
,---^ ,---^ ,---^ ^ ^ ^
[O < I] [O < I] [O < I] [O < I] [O < I] [O < I]
,---^ ,---^ ,---^ ^ ^ ^
[O < I] [O < I] [O < I] [I > O] [I > O] [I > O]
,---^ ,---^ ,---^ ^ ^ ^
[O < I] [O < I] [O < I] [O < I] [O < I] [O < I]
^ ^ ^ ^ ^ ^
#1 #2 #3 #1 #2 #3
Pi connector (three parallel chains of len 4)
```
(This is also a good time to notice that 384x256 with 12 128x64 panels, is probably an
upper limit of what you can reasonably output without having an unusable fresh
rate (Try these options to help: --led-pwm-bits=7 --led-pwm-dither-bits=1 and get about 100Hz)).
This shows the wiring of a 3x5 Vmapper:Z array built by Marc MERLIN, using 15x 64x32 panels:
![Vmapper_Z_192x160_3x5.jpg](../img/Vmapper_Z_192x160_3x5.jpg)
With --led-pwm-bits=7 --led-pwm-dither-bits=1, it gets a better 300Hz refresh
but only offers around 31K pixels instead of 98K pixels in the previous example.
Please note that Vmapper can also be used to improve the refresh rate of a long
display even if it is only one panel high (e.g. for a text running output) by
splitting the load into multiple parallel chains.
```
[O < I] [O < I] [O < I]
^ ^ ^
#1 #2 #3 Pi connector (three parallel chains of len 1)
```
#### Rotate
The "Rotate" mapper allows you to rotate your screen. It takes an angle
as parameter after a colon:
```
./demo --led-pixel-mapper="Rotate:90"
```
#### Mirror
The 'Mirror' mapper allows to mirror the output horizontally or vertically.
Without parameter, it mirrors horizontally. The parameter is a single character
'H' or 'V' for horizontal or vertical mirroring.
```
./demo --led-pixel-mapper="Mirror:H"
```
#### Combining Mappers
You can chain multiple mappers in the configuration, by separating them
with a semicolon. The mappers are applied in the sequence you give them, so
if you want to arrange a couple of panels with the U-arrangement, and then
rotate the resulting screen, use
```
./demo --led-chain=8 --led-parallel=3 --led-pixel-mapper="U-mapper;Rotate:90"
```
Here, we first create a 128x192 screen (4 panels wide (`4*32=128`),
with three folded chains (`6*32=192`)) and then rotate it by 90 degrees to
get a 192x128 screen.
#### Programmatic access
If you want to choose these mappers programmatically from your program and
not via the flags, you can do this by setting the `pixel_mapper_config` option
in the options struct in C++ or Python.
```
options.pixel_mapper_config = "Rotate:90";
```
### Writing your own mappers
If you want to write your own mappers, e.g. if you have a fancy panel
arrangement, you can do so using the API provided.
In the API, there is an interface to implement,
a [`PixelMapper`](../include/pixel-mapper.h) that allows to program
re-arrangements of pixels in any way. You can plug such an implementation of
a `PixelMapper` into the RGBMatrix to use it:
```
bool RGBMatrix::ApplyPixelMapper(const PixelMapper *mapper);
```
If you want, you can also register your PixelMapper globally before you
parse the command line options; then this pixel-mapper is automatically
provided in the `--led-pixel-mapper` command line option:
```
RegisterPixelMapper(new MyOwnPixelMapper());
RGBMatrix *matrix = RGBMatrix::CreateFromFlags(...);
```
Now your mapper can be used alongside (and combined with) the standard
mappers already there (e.g. "U-mapper" or "Rotate"). Your mapper can have
parameters: In the command-line flag, parameters provided after `:` are passed
as-is to your `SetParameters()` implementation
(e.g. using `--led-pixel-mapper="Rotate:90"`, the `Rotate` mapper
gets a parameter string `"90"` as parameter).
#### Multiplex Mappers
Sometimes you even need this for the panel itself: In some panels
(typically the 'outdoor panels', often with 1:4 multiplexing) the pixels
are not mapped in a straight-forward way, but in a snake arrangement for
instance.
There are simplified pixel mappers for this purpose, the
[multiplex mappers](../lib/multiplex-mappers.cc). These are defined there
and then can be accessed via the command line flag `--led-multiplexing=...`.
If you find that whatever parameter you give to `--led-multiplexing=` doesn't
work, you might need to write your own mapper (extend `MultiplexMapperBase`
and implement the one method `MapSinglePanel()`). Then register them with
the `CreateMultiplexMapperList()` function in that file. When you do this,
this will automatically become available in the `--led-multiplexing=` command
line option in C++ and Python.
[run-vid]: ../img/running-vid.jpg
[git-submodules]: http://git-scm.com/book/en/Git-Tools-Submodules
[pixelpush]: https://github.com/hzeller/rpi-matrix-pixelpusher
[pp-vid]: ../img/pixelpusher-vid.jpg
[otf2bdf]: https://github.com/jirutka/otf2bdf

@ -0,0 +1,61 @@
/* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
*
* Using the C-API of this library.
*
*/
#include "led-matrix-c.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char **argv) {
struct RGBLedMatrixOptions options;
struct RGBLedMatrix *matrix;
struct LedCanvas *offscreen_canvas;
int width, height;
int x, y, i;
memset(&options, 0, sizeof(options));
options.rows = 32;
options.chain_length = 1;
/* This supports all the led commandline options. Try --led-help */
matrix = led_matrix_create_from_options(&options, &argc, &argv);
if (matrix == NULL)
return 1;
/* Let's do an example with double-buffering. We create one extra
* buffer onto which we draw, which is then swapped on each refresh.
* This is typically a good aproach for animations and such.
*/
offscreen_canvas = led_matrix_create_offscreen_canvas(matrix);
led_canvas_get_size(offscreen_canvas, &width, &height);
fprintf(stderr, "Size: %dx%d. Hardware gpio mapping: %s\n",
width, height, options.hardware_mapping);
for (i = 0; i < 1000; ++i) {
for (y = 0; y < height; ++y) {
for (x = 0; x < width; ++x) {
led_canvas_set_pixel(offscreen_canvas, x, y, i & 0xff, x, y);
}
}
/* Now, we swap the canvas. We give swap_on_vsync the buffer we
* just have drawn into, and wait until the next vsync happens.
* we get back the unused buffer to which we'll draw in the next
* iteration.
*/
offscreen_canvas = led_matrix_swap_on_vsync(matrix, offscreen_canvas);
}
/*
* Make sure to always call led_matrix_delete() in the end to reset the
* display. Installing signal handlers for defined exit is a good idea.
*/
led_matrix_delete(matrix);
return 0;
}

@ -0,0 +1,198 @@
// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
// Example of a clock. This is very similar to the text-example,
// except that it shows the time :)
//
// This code is public domain
// (but note, that the led-matrix library this depends on is GPL v2)
#include "led-matrix.h"
#include "graphics.h"
#include <getopt.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <vector>
#include <string>
using namespace rgb_matrix;
volatile bool interrupt_received = false;
static void InterruptHandler(int signo) {
interrupt_received = true;
}
static int usage(const char *progname) {
fprintf(stderr, "usage: %s [options]\n", progname);
fprintf(stderr, "Reads text from stdin and displays it. "
"Empty string: clear screen\n");
fprintf(stderr, "Options:\n");
fprintf(stderr,
"\t-d <time-format> : Default '%%H:%%M'. See strftime()\n"
"\t Can be provided multiple times for multiple "
"lines\n"
"\t-f <font-file> : Use given font.\n"
"\t-x <x-origin> : X-Origin of displaying text (Default: 0)\n"
"\t-y <y-origin> : Y-Origin of displaying text (Default: 0)\n"
"\t-s <line-spacing> : Extra spacing between lines when multiple -d given\n"
"\t-S <spacing> : Extra spacing between letters (Default: 0)\n"
"\t-C <r,g,b> : Color. Default 255,255,0\n"
"\t-B <r,g,b> : Background-Color. Default 0,0,0\n"
"\t-O <r,g,b> : Outline-Color, e.g. to increase contrast.\n"
"\n"
);
rgb_matrix::PrintMatrixFlags(stderr);
return 1;
}
static bool parseColor(Color *c, const char *str) {
return sscanf(str, "%hhu,%hhu,%hhu", &c->r, &c->g, &c->b) == 3;
}
static bool FullSaturation(const Color &c) {
return (c.r == 0 || c.r == 255)
&& (c.g == 0 || c.g == 255)
&& (c.b == 0 || c.b == 255);
}
int main(int argc, char *argv[]) {
RGBMatrix::Options matrix_options;
rgb_matrix::RuntimeOptions runtime_opt;
if (!rgb_matrix::ParseOptionsFromFlags(&argc, &argv,
&matrix_options, &runtime_opt)) {
return usage(argv[0]);
}
// We accept multiple format lines
std::vector<std::string> format_lines;
Color color(255, 255, 0);
Color bg_color(0, 0, 0);
Color outline_color(0,0,0);
bool with_outline = false;
const char *bdf_font_file = NULL;
int x_orig = 0;
int y_orig = 0;
int letter_spacing = 0;
int line_spacing = 0;
int opt;
while ((opt = getopt(argc, argv, "x:y:f:C:B:O:s:S:d:")) != -1) {
switch (opt) {
case 'd': format_lines.push_back(optarg); break;
case 'x': x_orig = atoi(optarg); break;
case 'y': y_orig = atoi(optarg); break;
case 'f': bdf_font_file = strdup(optarg); break;
case 's': line_spacing = atoi(optarg); break;
case 'S': letter_spacing = atoi(optarg); break;
case 'C':
if (!parseColor(&color, optarg)) {
fprintf(stderr, "Invalid color spec: %s\n", optarg);
return usage(argv[0]);
}
break;
case 'B':
if (!parseColor(&bg_color, optarg)) {
fprintf(stderr, "Invalid background color spec: %s\n", optarg);
return usage(argv[0]);
}
break;
case 'O':
if (!parseColor(&outline_color, optarg)) {
fprintf(stderr, "Invalid outline color spec: %s\n", optarg);
return usage(argv[0]);
}
with_outline = true;
break;
default:
return usage(argv[0]);
}
}
if (format_lines.empty()) {
format_lines.push_back("%H:%M");
}
if (bdf_font_file == NULL) {
fprintf(stderr, "Need to specify BDF font-file with -f\n");
return usage(argv[0]);
}
/*
* Load font. This needs to be a filename with a bdf bitmap font.
*/
rgb_matrix::Font font;
if (!font.LoadFont(bdf_font_file)) {
fprintf(stderr, "Couldn't load font '%s'\n", bdf_font_file);
return 1;
}
rgb_matrix::Font *outline_font = NULL;
if (with_outline) {
outline_font = font.CreateOutlineFont();
}
RGBMatrix *matrix = RGBMatrix::CreateFromOptions(matrix_options, runtime_opt);
if (matrix == NULL)
return 1;
const bool all_extreme_colors = (matrix_options.brightness == 100)
&& FullSaturation(color)
&& FullSaturation(bg_color)
&& FullSaturation(outline_color);
if (all_extreme_colors)
matrix->SetPWMBits(1);
const int x = x_orig;
int y = y_orig;
FrameCanvas *offscreen = matrix->CreateFrameCanvas();
char text_buffer[256];
struct timespec next_time;
next_time.tv_sec = time(NULL);
next_time.tv_nsec = 0;
struct tm tm;
signal(SIGTERM, InterruptHandler);
signal(SIGINT, InterruptHandler);
while (!interrupt_received) {
offscreen->Fill(bg_color.r, bg_color.g, bg_color.b);
localtime_r(&next_time.tv_sec, &tm);
int line_offset = 0;
for (const std::string &line : format_lines) {
strftime(text_buffer, sizeof(text_buffer), line.c_str(), &tm);
if (outline_font) {
rgb_matrix::DrawText(offscreen, *outline_font,
x - 1, y + font.baseline() + line_offset,
outline_color, NULL, text_buffer,
letter_spacing - 2);
}
rgb_matrix::DrawText(offscreen, font,
x, y + font.baseline() + line_offset,
color, NULL, text_buffer,
letter_spacing);
line_offset += font.height() + line_spacing;
}
// Wait until we're ready to show it.
clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &next_time, NULL);
// Atomic swap with double buffer
offscreen = matrix->SwapOnVSync(offscreen);
next_time.tv_sec += 1;
}
// Finished. Shut down the RGB matrix.
delete matrix;
write(STDOUT_FILENO, "\n", 1); // Create a fresh new line after ^C on screen
return 0;
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,160 @@
// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
//
// Example how to display an image, including animated images using
// ImageMagick. For a full utility that does a few more things, have a look
// at the led-image-viewer in ../utils
//
// Showing an image is not so complicated, essentially just copy all the
// pixels to the canvas. How to get the pixels ? In this example we're using
// the graphicsmagick library as universal image loader library that
// can also deal with animated images.
// You can of course do your own image loading or use some other library.
//
// This requires an external dependency, so install these first before you
// can call `make image-example`
// sudo apt-get update
// sudo apt-get install libgraphicsmagick++-dev libwebp-dev -y
// make image-example
#include "led-matrix.h"
#include <math.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <exception>
#include <Magick++.h>
#include <magick/image.h>
using rgb_matrix::Canvas;
using rgb_matrix::RGBMatrix;
using rgb_matrix::FrameCanvas;
// Make sure we can exit gracefully when Ctrl-C is pressed.
volatile bool interrupt_received = false;
static void InterruptHandler(int signo) {
interrupt_received = true;
}
using ImageVector = std::vector<Magick::Image>;
// Given the filename, load the image and scale to the size of the
// matrix.
// // If this is an animated image, the resutlting vector will contain multiple.
static ImageVector LoadImageAndScaleImage(const char *filename,
int target_width,
int target_height) {
ImageVector result;
ImageVector frames;
try {
readImages(&frames, filename);
} catch (std::exception &e) {
if (e.what())
fprintf(stderr, "%s\n", e.what());
return result;
}
if (frames.empty()) {
fprintf(stderr, "No image found.");
return result;
}
// Animated images have partial frames that need to be put together
if (frames.size() > 1) {
Magick::coalesceImages(&result, frames.begin(), frames.end());
} else {
result.push_back(frames[0]); // just a single still image.
}
for (Magick::Image &image : result) {
image.scale(Magick::Geometry(target_width, target_height));
}
return result;
}
// Copy an image to a Canvas. Note, the RGBMatrix is implementing the Canvas
// interface as well as the FrameCanvas we use in the double-buffering of the
// animted image.
void CopyImageToCanvas(const Magick::Image &image, Canvas *canvas) {
const int offset_x = 0, offset_y = 0; // If you want to move the image.
// Copy all the pixels to the canvas.
for (size_t y = 0; y < image.rows(); ++y) {
for (size_t x = 0; x < image.columns(); ++x) {
const Magick::Color &c = image.pixelColor(x, y);
if (c.alphaQuantum() < 256) {
canvas->SetPixel(x + offset_x, y + offset_y,
ScaleQuantumToChar(c.redQuantum()),
ScaleQuantumToChar(c.greenQuantum()),
ScaleQuantumToChar(c.blueQuantum()));
}
}
}
}
// An animated image has to constantly swap to the next frame.
// We're using double-buffering and fill an offscreen buffer first, then show.
void ShowAnimatedImage(const ImageVector &images, RGBMatrix *matrix) {
FrameCanvas *offscreen_canvas = matrix->CreateFrameCanvas();
while (!interrupt_received) {
for (const auto &image : images) {
if (interrupt_received) break;
CopyImageToCanvas(image, offscreen_canvas);
offscreen_canvas = matrix->SwapOnVSync(offscreen_canvas);
usleep(image.animationDelay() * 10000); // 1/100s converted to usec
}
}
}
int usage(const char *progname) {
fprintf(stderr, "Usage: %s [led-matrix-options] <image-filename>\n",
progname);
rgb_matrix::PrintMatrixFlags(stderr);
return 1;
}
int main(int argc, char *argv[]) {
Magick::InitializeMagick(*argv);
// Initialize the RGB matrix with
RGBMatrix::Options matrix_options;
rgb_matrix::RuntimeOptions runtime_opt;
if (!rgb_matrix::ParseOptionsFromFlags(&argc, &argv,
&matrix_options, &runtime_opt)) {
return usage(argv[0]);
}
if (argc != 2)
return usage(argv[0]);
const char *filename = argv[1];
signal(SIGTERM, InterruptHandler);
signal(SIGINT, InterruptHandler);
RGBMatrix *matrix = RGBMatrix::CreateFromOptions(matrix_options, runtime_opt);
if (matrix == NULL)
return 1;
ImageVector images = LoadImageAndScaleImage(filename,
matrix->width(),
matrix->height());
switch (images.size()) {
case 0: // failed to load image.
break;
case 1: // Simple example: one image to show
CopyImageToCanvas(images[0], matrix);
while (!interrupt_received) sleep(1000); // Until Ctrl-C is pressed
break;
default: // More than one image: this is an animation.
ShowAnimatedImage(images, matrix);
break;
}
matrix->Clear();
delete matrix;
return 0;
}

@ -0,0 +1,64 @@
// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
// Small example how to use the input bits
//
// This code is public domain
// (but note, that the led-matrix library this depends on is GPL v2)
#include "led-matrix.h"
#include <unistd.h>
#include <math.h>
#include <stdio.h>
#include <signal.h>
using rgb_matrix::RGBMatrix;
using rgb_matrix::Canvas;
volatile bool interrupt_received = false;
static void InterruptHandler(int signo) {
interrupt_received = true;
}
int main(int argc, char *argv[]) {
RGBMatrix::Options defaults;
defaults.hardware_mapping = "regular"; // or e.g. "adafruit-hat"
defaults.rows = 32;
defaults.chain_length = 1;
defaults.parallel = 1;
RGBMatrix *matrix = RGBMatrix::CreateFromFlags(&argc, &argv, &defaults);
if (matrix == NULL)
return 1;
// It is always good to set up a signal handler to cleanly exit when we
// receive a CTRL-C for instance.
signal(SIGTERM, InterruptHandler);
signal(SIGINT, InterruptHandler);
// Let's request all input bits and see which are actually available.
// This will differ depending on which hardware mapping you use and how
// many parallel chains you have.
const uint64_t available_inputs = matrix->RequestInputs(0xffffffff);
fprintf(stderr, "Available GPIO-bits: ");
for (int b = 0; b < 32; ++b) {
if (available_inputs & (1<<b))
fprintf(stderr, "%d ", b);
}
fprintf(stderr, "\n");
while (!interrupt_received) {
// Block and wait until any input bit changed or 100ms passed
uint32_t inputs = matrix->AwaitInputChange(100);
// Minimal output: let's show the bits with LEDs in the first row
for (int b = 0; b < 32; ++b) {
uint8_t col = (inputs & (1<<b)) ? 255 : 0;
matrix->SetPixel(32-b, 0, col, col, col);
}
}
fprintf(stderr, "Exiting.\n");
matrix->Clear();
delete matrix;
return 0;
}

@ -0,0 +1,82 @@
// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
// A program that reads frames form STDIN as RGB24, much like
// https://github.com/polyfloyd/ledcat does.
//
// This code is public domain
// (but note, that the led-matrix library this depends on is GPL v2)
#include "led-matrix.h"
#include <math.h>
#include <signal.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#define FPS 60
using rgb_matrix::RGBMatrix;
using rgb_matrix::Canvas;
volatile bool interrupt_received = false;
static void InterruptHandler(int signo) {
interrupt_received = true;
}
int main(int argc, char *argv[]) {
RGBMatrix::Options defaults;
defaults.hardware_mapping = "regular"; // or e.g. "adafruit-hat"
defaults.rows = 32;
defaults.chain_length = 1;
defaults.parallel = 1;
Canvas *canvas = RGBMatrix::CreateFromFlags(&argc, &argv, &defaults);
if (canvas == NULL) {
return 1;
}
// It is always good to set up a signal handler to cleanly exit when we
// receive a CTRL-C for instance. The DrawOnCanvas() routine is looking
// for that.
signal(SIGTERM, InterruptHandler);
signal(SIGINT, InterruptHandler);
ssize_t frame_size = canvas->width() * canvas->height() * 3;
uint8_t buf[frame_size];
while (1) {
struct timespec start;
timespec_get(&start, TIME_UTC);
ssize_t nread;
ssize_t total_nread = 0;
while ((nread = read(STDIN_FILENO, &buf[total_nread], frame_size - total_nread)) > 0) {
if (interrupt_received) {
return 1;
}
total_nread += nread;
}
if (total_nread < frame_size){
break;
}
for (int y = 0; y < canvas->height(); y++) {
for (int x = 0; x < canvas->width(); x++) {
uint8_t *p = &buf[(y * canvas->width() + x) * 3];
uint8_t r = *(p+0), g = *(p+1), b = *(p+2);
canvas->SetPixel(x, y, r, g, b);
}
}
struct timespec end;
timespec_get(&end, TIME_UTC);
long tudiff = (end.tv_nsec / 1000 + end.tv_sec * 1000000) - (start.tv_nsec / 1000 + start.tv_sec * 1000000);
if (tudiff < 1000000l / FPS) {
usleep(1000000l / FPS - tudiff);
}
}
// Animation finished. Shut down the RGB matrix.
canvas->Clear();
delete canvas;
return 0;
}

@ -0,0 +1,69 @@
// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
// Small example how to use the library.
// For more examples, look at demo-main.cc
//
// This code is public domain
// (but note, that the led-matrix library this depends on is GPL v2)
#include "led-matrix.h"
#include <unistd.h>
#include <math.h>
#include <stdio.h>
#include <signal.h>
using rgb_matrix::RGBMatrix;
using rgb_matrix::Canvas;
volatile bool interrupt_received = false;
static void InterruptHandler(int signo) {
interrupt_received = true;
}
static void DrawOnCanvas(Canvas *canvas) {
/*
* Let's create a simple animation. We use the canvas to draw
* pixels. We wait between each step to have a slower animation.
*/
canvas->Fill(0, 0, 255);
int center_x = canvas->width() / 2;
int center_y = canvas->height() / 2;
float radius_max = canvas->width() / 2;
float angle_step = 1.0 / 360;
for (float a = 0, r = 0; r < radius_max; a += angle_step, r += angle_step) {
if (interrupt_received)
return;
float dot_x = cos(a * 2 * M_PI) * r;
float dot_y = sin(a * 2 * M_PI) * r;
canvas->SetPixel(center_x + dot_x, center_y + dot_y,
255, 0, 0);
usleep(1 * 1000); // wait a little to slow down things.
}
}
int main(int argc, char *argv[]) {
RGBMatrix::Options defaults;
defaults.hardware_mapping = "regular"; // or e.g. "adafruit-hat"
defaults.rows = 32;
defaults.chain_length = 1;
defaults.parallel = 1;
defaults.show_refresh_rate = true;
Canvas *canvas = RGBMatrix::CreateFromFlags(&argc, &argv, &defaults);
if (canvas == NULL)
return 1;
// It is always good to set up a signal handler to cleanly exit when we
// receive a CTRL-C for instance. The DrawOnCanvas() routine is looking
// for that.
signal(SIGTERM, InterruptHandler);
signal(SIGINT, InterruptHandler);
DrawOnCanvas(canvas); // Using the canvas.
// Animation finished. Shut down the RGB matrix.
canvas->Clear();
delete canvas;
return 0;
}

@ -0,0 +1,210 @@
// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
// Small example how to use the input bits
//
// This code is public domain
// (but note, that the led-matrix library this depends on is GPL v2)
#include "led-matrix.h"
#include "graphics.h"
#include <ctype.h>
#include <getopt.h>
#include <math.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <deque>
using namespace rgb_matrix;
volatile bool interrupt_received = false;
static void InterruptHandler(int signo) {
interrupt_received = true;
}
static void InteractiveUseMessage() {
fprintf(stderr,
"Move around with common movement keysets \n"
" W,A,S,D (gaming move style) or\n"
" H,J,K,L (vi console move style)\n"
" Quit with 'q' or <ESC>\n"
"The pixel position cannot be less than 0 or greater than the "
"display height and width.\n");
}
static int usage(const char *progname) {
fprintf(stderr, "usage: %s [options]\n", progname);
fprintf(stderr, "Display single pixel with any colour.\n");
InteractiveUseMessage();
fprintf(stderr, "Options:\n\n");
fprintf(stderr,
"\t-C <r,g,b> : Color at front of trail. Default 255,255,0\n"
"\t-t <trail-len> : Length of trail behind dot (default:0)\n"
"\t-c <r,g,b> : Color at end of trail. Default 0,0,255\n\n"
);
rgb_matrix::PrintMatrixFlags(stderr);
return 1;
}
static bool parseColor(Color *c, const char *str) {
return sscanf(str, "%hhu,%hhu,%hhu", &c->r, &c->g, &c->b) == 3;
}
static char getch() {
static bool is_terminal = isatty(STDIN_FILENO);
struct termios old;
if (is_terminal) {
if (tcgetattr(0, &old) < 0)
perror("tcsetattr()");
// Set to unbuffered mode
struct termios no_echo = old;
no_echo.c_lflag &= ~ICANON;
no_echo.c_lflag &= ~ECHO;
no_echo.c_cc[VMIN] = 1;
no_echo.c_cc[VTIME] = 0;
if (tcsetattr(0, TCSANOW, &no_echo) < 0)
perror("tcsetattr ICANON");
}
char buf = 0;
if (read(STDIN_FILENO, &buf, 1) < 0)
perror ("read()");
if (is_terminal) {
// Back to original terminal settings.
if (tcsetattr(0, TCSADRAIN, &old) < 0)
perror ("tcsetattr ~ICANON");
}
return buf;
}
// Interpolation of color between head and tail of trail.
static uint8_t quantize(float c) {
return c < 0 ? 0 : c > 255 ? 255 : roundf(c);
}
static Color interpolate(const Color &c1, const Color &c2, float fraction) {
float c2_fraction = 1 - fraction;
return { quantize(c1.r * fraction + c2.r * c2_fraction),
quantize(c1.g * fraction + c2.g * c2_fraction),
quantize(c1.b * fraction + c2.b * c2_fraction)};
}
int main(int argc, char *argv[]) {
RGBMatrix::Options matrix_options;
rgb_matrix::RuntimeOptions runtime_opt;
if (!rgb_matrix::ParseOptionsFromFlags(&argc, &argv,
&matrix_options, &runtime_opt)) {
return usage(argv[0]);
}
Color front_color(255, 255, 0);
Color back_color(0, 0, 255);
int trail_len = 0;
int opt;
while ((opt = getopt(argc, argv, "C:c:t:")) != -1) {
switch (opt) {
case 't':
trail_len = std::max(0, atoi(optarg));
break;
case 'C':
if (!parseColor(&front_color, optarg)) {
fprintf(stderr, "Invalid color spec: %s\n", optarg);
return usage(argv[0]);
}
break;
case 'c':
if (!parseColor(&back_color, optarg)) {
fprintf(stderr, "Invalid color spec: %s\n", optarg);
return usage(argv[0]);
}
break;
default:
return usage(argv[0]);
}
}
RGBMatrix *matrix = RGBMatrix::CreateFromOptions(matrix_options, runtime_opt);
if (matrix == NULL)
return usage(argv[0]);
rgb_matrix::FrameCanvas *canvas = matrix->CreateFrameCanvas();
signal(SIGTERM, InterruptHandler);
signal(SIGINT, InterruptHandler);
std::deque<std::pair<int, int>> trail;
int x_pos = 0;
int y_pos = 0;
trail.push_back({x_pos, y_pos});
InteractiveUseMessage();
const bool output_is_terminal = isatty(STDOUT_FILENO);
bool running = true;
while (!interrupt_received && running) {
canvas->Clear();
int distance_from_head = trail.size();
for (const auto &pos : trail) { // Draw from tail -> head
distance_from_head--;
Color c = interpolate(front_color, back_color,
1.0 - 1.0f * distance_from_head / trail.size());
canvas->SetPixel(pos.first, pos.second, c.r, c.g, c.b);
}
canvas = matrix->SwapOnVSync(canvas);
printf("%sX,Y = %d,%d%s",
output_is_terminal ? "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" : "",
x_pos, y_pos,
output_is_terminal ? " " : "\n");
fflush(stdout);
const char c = tolower(getch());
switch (c) {
case 'w': case 'k': // Up
if (y_pos > 0) {
y_pos--;
trail.push_back({x_pos, y_pos});
}
break;
case 's': case 'j': // Down
if (y_pos < canvas->height() - 1) {
y_pos++;
trail.push_back({x_pos, y_pos});
}
break;
case 'a': case 'h': // Left
if (x_pos > 0) {
x_pos--;
trail.push_back({x_pos, y_pos});
}
break;
case 'd': case 'l': // Right
if (x_pos < canvas->width() - 1) {
x_pos++;
trail.push_back({x_pos, y_pos});
}
break;
// All kinds of conditions which we use to exit
case 0x1B: // Escape
case 'q': // 'Q'uit
case 0x04: // End of file
case 0x00: // Other issue from getch()
running = false;
break;
}
while ((int)trail.size() > trail_len + 1)
trail.pop_front(); // items on front are the oldest dots
}
// Finished. Shut down the RGB matrix.
delete matrix;
printf("\n");
return 0;
}

@ -0,0 +1,184 @@
// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
// Small example how to scroll text.
//
// This code is public domain
// (but note, that the led-matrix library this depends on is GPL v2)
// For a utility with a few more features see
// ../utils/text-scroller.cc
#include "led-matrix.h"
#include "graphics.h"
#include <string>
#include <getopt.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
using namespace rgb_matrix;
volatile bool interrupt_received = false;
static void InterruptHandler(int signo) {
interrupt_received = true;
}
static int usage(const char *progname) {
fprintf(stderr, "usage: %s [options] <text>\n", progname);
fprintf(stderr, "Takes text and scrolls it with speed -s\n");
fprintf(stderr, "Options:\n");
fprintf(stderr,
"\t-s <speed> : Approximate letters per second. "
"(Zero for no scrolling)\n"
"\t-l <loop-count> : Number of loops through the text. "
"-1 for endless (default)\n"
"\t-f <font-file> : Use given font.\n"
"\t-x <x-origin> : X-Origin of displaying text (Default: 0)\n"
"\t-y <y-origin> : Y-Origin of displaying text (Default: 0)\n"
"\t-t <track=spacing>: Spacing pixels between letters (Default: 0)\n"
"\n"
"\t-C <r,g,b> : Text-Color. Default 255,255,0\n"
"\t-B <r,g,b> : Background-Color. Default 0,0,0\n"
"\n"
);
rgb_matrix::PrintMatrixFlags(stderr);
return 1;
}
static bool parseColor(Color *c, const char *str) {
return sscanf(str, "%hhu,%hhu,%hhu", &c->r, &c->g, &c->b) == 3;
}
static bool FullSaturation(const Color &c) {
return (c.r == 0 || c.r == 255)
&& (c.g == 0 || c.g == 255)
&& (c.b == 0 || c.b == 255);
}
int main(int argc, char *argv[]) {
RGBMatrix::Options matrix_options;
rgb_matrix::RuntimeOptions runtime_opt;
if (!rgb_matrix::ParseOptionsFromFlags(&argc, &argv,
&matrix_options, &runtime_opt)) {
return usage(argv[0]);
}
Color color(255, 255, 0);
Color bg_color(0, 0, 0);
const char *bdf_font_file = NULL;
std::string line;
/* x_origin is set by default just right of the screen */
const int x_default_start = (matrix_options.chain_length
* matrix_options.cols) + 5;
int x_orig = x_default_start;
int y_orig = 0;
int letter_spacing = 0;
float speed = 7.0f;
int loops = -1;
int opt;
while ((opt = getopt(argc, argv, "x:y:f:C:B:t:s:l:")) != -1) {
switch (opt) {
case 's': speed = atof(optarg); break;
case 'l': loops = atoi(optarg); break;
case 'x': x_orig = atoi(optarg); break;
case 'y': y_orig = atoi(optarg); break;
case 'f': bdf_font_file = strdup(optarg); break;
case 't': letter_spacing = atoi(optarg); break;
case 'C':
if (!parseColor(&color, optarg)) {
fprintf(stderr, "Invalid color spec: %s\n", optarg);
return usage(argv[0]);
}
break;
case 'B':
if (!parseColor(&bg_color, optarg)) {
fprintf(stderr, "Invalid background color spec: %s\n", optarg);
return usage(argv[0]);
}
break;
default:
return usage(argv[0]);
}
}
for (int i = optind; i < argc; ++i) {
line.append(argv[i]).append(" ");
}
if (line.empty()) {
fprintf(stderr, "Add the text you want to print on the command-line.\n");
return usage(argv[0]);
}
if (bdf_font_file == NULL) {
fprintf(stderr, "Need to specify BDF font-file with -f\n");
return usage(argv[0]);
}
/*
* Load font. This needs to be a filename with a bdf bitmap font.
*/
rgb_matrix::Font font;
if (!font.LoadFont(bdf_font_file)) {
fprintf(stderr, "Couldn't load font '%s'\n", bdf_font_file);
return 1;
}
RGBMatrix *canvas = RGBMatrix::CreateFromOptions(matrix_options, runtime_opt);
if (canvas == NULL)
return 1;
const bool all_extreme_colors = (matrix_options.brightness == 100)
&& FullSaturation(color)
&& FullSaturation(bg_color);
if (all_extreme_colors)
canvas->SetPWMBits(1);
signal(SIGTERM, InterruptHandler);
signal(SIGINT, InterruptHandler);
printf("CTRL-C for exit.\n");
// Create a new canvas to be used with led_matrix_swap_on_vsync
FrameCanvas *offscreen_canvas = canvas->CreateFrameCanvas();
int delay_speed_usec = 1000000;
if (speed > 0) {
delay_speed_usec = 1000000 / speed / font.CharacterWidth('W');
} else if (x_orig == x_default_start) {
// There would be no scrolling, so text would never appear. Move to front.
x_orig = 0;
}
int x = x_orig;
int y = y_orig;
int length = 0;
while (!interrupt_received && loops != 0) {
offscreen_canvas->Fill(bg_color.r, bg_color.g, bg_color.b);
// length = holds how many pixels our text takes up
length = rgb_matrix::DrawText(offscreen_canvas, font,
x, y + font.baseline(),
color, nullptr,
line.c_str(), letter_spacing);
if (speed > 0 && --x + length < 0) {
x = x_orig;
if (loops > 0) --loops;
}
// Swap the offscreen_canvas with canvas on vsync, avoids flickering
offscreen_canvas = canvas->SwapOnVSync(offscreen_canvas);
usleep(delay_speed_usec);
}
// Finished. Shut down the RGB matrix.
delete canvas;
return 0;
}

@ -0,0 +1,179 @@
// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
// Small example how write text.
//
// This code is public domain
// (but note, that the led-matrix library this depends on is GPL v2)
#include "led-matrix.h"
#include "graphics.h"
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
using namespace rgb_matrix;
static int usage(const char *progname) {
fprintf(stderr, "usage: %s [options]\n", progname);
fprintf(stderr, "Reads text from stdin and displays it. "
"Empty string: clear screen\n");
fprintf(stderr, "Options:\n");
fprintf(stderr,
"\t-f <font-file> : Use given font.\n"
"\t-x <x-origin> : X-Origin of displaying text (Default: 0)\n"
"\t-y <y-origin> : Y-Origin of displaying text (Default: 0)\n"
"\t-S <spacing> : Spacing pixels between letters (Default: 0)\n"
"\t-C <r,g,b> : Color. Default 255,255,0\n"
"\t-B <r,g,b> : Font Background-Color. Default 0,0,0\n"
"\t-O <r,g,b> : Outline-Color, e.g. to increase contrast.\n"
"\t-F <r,g,b> : Panel flooding-background color. Default 0,0,0\n"
"\n"
);
rgb_matrix::PrintMatrixFlags(stderr);
return 1;
}
static bool parseColor(Color *c, const char *str) {
return sscanf(str, "%hhu,%hhu,%hhu", &c->r, &c->g, &c->b) == 3;
}
static bool FullSaturation(const Color &c) {
return (c.r == 0 || c.r == 255)
&& (c.g == 0 || c.g == 255)
&& (c.b == 0 || c.b == 255);
}
int main(int argc, char *argv[]) {
RGBMatrix::Options matrix_options;
rgb_matrix::RuntimeOptions runtime_opt;
if (!rgb_matrix::ParseOptionsFromFlags(&argc, &argv,
&matrix_options, &runtime_opt)) {
return usage(argv[0]);
}
Color color(255, 255, 0);
Color bg_color(0, 0, 0);
Color flood_color(0, 0, 0);
Color outline_color(0,0,0);
bool with_outline = false;
const char *bdf_font_file = NULL;
int x_orig = 0;
int y_orig = 0;
int letter_spacing = 0;
int opt;
while ((opt = getopt(argc, argv, "x:y:f:C:B:O:S:F:")) != -1) {
switch (opt) {
case 'x': x_orig = atoi(optarg); break;
case 'y': y_orig = atoi(optarg); break;
case 'f': bdf_font_file = strdup(optarg); break;
case 'S': letter_spacing = atoi(optarg); break;
case 'C':
if (!parseColor(&color, optarg)) {
fprintf(stderr, "Invalid color spec: %s\n", optarg);
return usage(argv[0]);
}
break;
case 'B':
if (!parseColor(&bg_color, optarg)) {
fprintf(stderr, "Invalid background color spec: %s\n", optarg);
return usage(argv[0]);
}
break;
case 'O':
if (!parseColor(&outline_color, optarg)) {
fprintf(stderr, "Invalid outline color spec: %s\n", optarg);
return usage(argv[0]);
}
with_outline = true;
break;
case 'F':
if (!parseColor(&flood_color, optarg)) {
fprintf(stderr, "Invalid background color spec: %s\n", optarg);
return usage(argv[0]);
}
break;
default:
return usage(argv[0]);
}
}
if (bdf_font_file == NULL) {
fprintf(stderr, "Need to specify BDF font-file with -f\n");
return usage(argv[0]);
}
/*
* Load font. This needs to be a filename with a bdf bitmap font.
*/
rgb_matrix::Font font;
if (!font.LoadFont(bdf_font_file)) {
fprintf(stderr, "Couldn't load font '%s'\n", bdf_font_file);
return 1;
}
/*
* If we want an outline around the font, we create a new font with
* the original font as a template that is just an outline font.
*/
rgb_matrix::Font *outline_font = NULL;
if (with_outline) {
outline_font = font.CreateOutlineFont();
}
RGBMatrix *canvas = RGBMatrix::CreateFromOptions(matrix_options, runtime_opt);
if (canvas == NULL)
return 1;
const bool all_extreme_colors = (matrix_options.brightness == 100)
&& FullSaturation(color)
&& FullSaturation(bg_color)
&& FullSaturation(outline_color);
if (all_extreme_colors)
canvas->SetPWMBits(1);
const int x = x_orig;
int y = y_orig;
if (isatty(STDIN_FILENO)) {
// Only give a message if we are interactive. If connected via pipe, be quiet
printf("Enter lines. Full screen or empty line clears screen.\n"
"Supports UTF-8. CTRL-D for exit.\n");
}
canvas->Fill(flood_color.r, flood_color.g, flood_color.b);
char line[1024];
while (fgets(line, sizeof(line), stdin)) {
const size_t last = strlen(line);
if (last > 0) line[last - 1] = '\0'; // remove newline.
bool line_empty = strlen(line) == 0;
if ((y + font.height() > canvas->height()) || line_empty) {
canvas->Fill(flood_color.r, flood_color.g, flood_color.b);
y = y_orig;
}
if (line_empty)
continue;
if (outline_font) {
// The outline font, we need to write with a negative (-2) text-spacing,
// as we want to have the same letter pitch as the regular text that
// we then write on top.
rgb_matrix::DrawText(canvas, *outline_font,
x - 1, y + font.baseline(),
outline_color, &bg_color, line, letter_spacing - 2);
}
// The regular text. Unless we already have filled the background with
// the outline font, we also fill the background here.
rgb_matrix::DrawText(canvas, font, x, y + font.baseline(),
color, outline_font ? NULL : &bg_color, line,
letter_spacing);
y += font.height();
}
// Finished. Shut down the RGB matrix.
delete canvas;
return 0;
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save