├── .gitignore ├── .gitmodules ├── Config.cpp ├── Config.h ├── GridTransformer.cpp ├── GridTransformer.h ├── LICENSE ├── Makefile ├── README.md ├── display-test.cpp ├── glcdfont.c ├── glcdfont.h ├── matrix.cfg └── rpi-fb-matrix.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | display-test 2 | rpi-fb-matrix 3 | # Compiled Object files 4 | *.slo 5 | *.lo 6 | *.o 7 | *.obj 8 | 9 | # Precompiled Headers 10 | *.gch 11 | *.pch 12 | 13 | # Compiled Dynamic libraries 14 | *.so 15 | *.dylib 16 | *.dll 17 | 18 | # Fortran module files 19 | *.mod 20 | 21 | # Compiled Static libraries 22 | *.lai 23 | *.la 24 | *.a 25 | *.lib 26 | 27 | # Executables 28 | *.exe 29 | *.out 30 | *.app 31 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "rpi-rgb-led-matrix"] 2 | path = rpi-rgb-led-matrix 3 | url = https://github.com/hzeller/rpi-rgb-led-matrix.git 4 | -------------------------------------------------------------------------------- /Config.cpp: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // Matrix configuration parsing class implementation. 3 | // Author: Tony DiCola 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include "Config.h" 13 | 14 | using namespace std; 15 | 16 | // Get value if it exists, otherwise return default. 17 | static int getWithDefault(const libconfig::Setting& root, const char *key, 18 | int default_value) { 19 | return root.exists(key) ? root[key] : default_value; 20 | } 21 | 22 | Config::Config(rgb_matrix::RGBMatrix::Options *options, 23 | const string& filename) 24 | : _moptions(options), 25 | _display_width(-1), 26 | _display_height(-1), 27 | _panel_width(-1), 28 | _crop_x(-1), 29 | _crop_y(-1) 30 | { 31 | try { 32 | // Load config file with libconfig. 33 | libconfig::Config cfg; 34 | cfg.readFile(filename.c_str()); 35 | libconfig::Setting& root = cfg.getRoot(); 36 | // Parse out the matrix configuration values. If not given, we use 37 | // reasonable defaults or defaults provided by the flags. 38 | _panel_width = getWithDefault(root, "panel_width", 32); 39 | _moptions->rows = getWithDefault(root, "panel_height", _moptions->rows); 40 | _chain_length = getWithDefault(root, "chain_length", 41 | _moptions->chain_length); 42 | // While all the code for the transformer assumes number of panels, for 43 | // the internal representation for the matrix code, we need to normalize 44 | // that to 32 wide panels. 45 | _moptions->chain_length = _chain_length * (_panel_width / 32); 46 | 47 | _moptions->parallel = getWithDefault(root, "parallel_count", 48 | _moptions->parallel); 49 | 50 | _display_width = getWithDefault(root, "display_width", 51 | getPanelWidth() * getChainLength()); 52 | _display_height = getWithDefault(root, "display_height", 53 | getPanelHeight() * getParallelCount()); 54 | 55 | // Load optional crop_origin value. 56 | if (root.exists("crop_origin")) { 57 | libconfig::Setting& crop_origin = root["crop_origin"]; 58 | if (crop_origin.getLength() != 2) { 59 | throw invalid_argument("crop_origin must be a list with two values, the X and Y coordinates of the crop box origin!"); 60 | } 61 | _crop_x = crop_origin[0]; 62 | _crop_y = crop_origin[1]; 63 | } 64 | 65 | // Do basic validation of configuration. 66 | if (_panel_width % 32 != 0) { 67 | throw invalid_argument("Panel width must be multiple of 32. Typically that is 32, but sometimes 64."); 68 | } 69 | 70 | if (_display_width % _panel_width != 0) { 71 | throw invalid_argument("display_width must be a multiple of panel_width!"); 72 | } 73 | if (_display_height % getPanelHeight() != 0) { 74 | throw invalid_argument("display_height must be a multiple of panel_height!"); 75 | } 76 | std::string message; 77 | if (!_moptions->Validate(&message)) { 78 | throw invalid_argument(message); 79 | } 80 | 81 | // Parse out the individual panel configurations. 82 | if (root.exists("panels")) { 83 | libconfig::Setting& panels_config = root["panels"]; 84 | for (int i = 0; i < panels_config.getLength(); ++i) { 85 | libconfig::Setting& row = panels_config[i]; 86 | for (int j = 0; j < row.getLength(); ++j) { 87 | GridTransformer::Panel panel; 88 | // Read panel order (required setting for each panel). 89 | panel.order = row[j]["order"]; 90 | // Set default values for rotation and parallel chain, then override 91 | // them with any panel-specific configuration values. 92 | panel.rotate = 0; 93 | panel.parallel = 0; 94 | row[j].lookupValue("rotate", panel.rotate); 95 | row[j].lookupValue("parallel", panel.parallel); 96 | // Perform validation of panel values. 97 | // If panels are square allow rotations that are a multiple of 90, otherwise 98 | // only allow a rotation of 180 degrees. 99 | if ((_panel_width == getPanelHeight()) && (panel.rotate % 90 != 0)) { 100 | stringstream error; 101 | error << "Panel " << i << "," << j << " rotation must be a multiple of 90 degrees!"; 102 | throw invalid_argument(error.str()); 103 | } 104 | else if ((_panel_width != getPanelHeight()) && (panel.rotate % 180 != 0)) { 105 | stringstream error; 106 | error << "Panel row " << j << ", column " << i << " can only be rotated 180 degrees!"; 107 | throw invalid_argument(error.str()); 108 | } 109 | // Check that parallel is value between 0 and 2 (up to 3 parallel chains). 110 | if ((panel.parallel < 0) || (panel.parallel > 2)) { 111 | stringstream error; 112 | error << "Panel row " << j << ", column " << i << " parallel value must be 0, 1, or 2!"; 113 | throw invalid_argument(error.str()); 114 | } 115 | // Add the panel to the list of panel configurations. 116 | _panels.push_back(panel); 117 | } 118 | } 119 | // Check the number of configured panels matches the expected number 120 | // of panels (# of panel columns * # of panel rows). 121 | const int expected = (getDisplayWidth() / getPanelWidth()) 122 | * (getDisplayHeight() / getPanelHeight()); 123 | if (_panels.size() != (unsigned int)expected) { 124 | stringstream error; 125 | error << "Expected " << expected << " panels in configuration but found " << _panels.size() << "!"; 126 | throw invalid_argument(error.str()); 127 | } 128 | } 129 | } 130 | catch (const libconfig::FileIOException& fioex) { 131 | throw runtime_error("IO error while reading configuration file. Does the file exist?"); 132 | } 133 | catch (const libconfig::ParseException& pex) { 134 | stringstream error; 135 | error << "Config file error at " << pex.getFile() << ":" << pex.getLine() 136 | << " - " << pex.getError(); 137 | throw invalid_argument(error.str()); 138 | } 139 | catch (const libconfig::SettingNotFoundException& nfex) { 140 | stringstream error; 141 | error << "Expected to find setting: " << nfex.getPath(); 142 | throw invalid_argument(error.str()); 143 | } 144 | catch (const libconfig::ConfigException& ex) { 145 | throw runtime_error("Error loading configuration!"); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /Config.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // Matrix configuration parsing class declaration. 3 | // Author: Tony DiCola 4 | #ifndef CONFIG_H 5 | #define CONFIG_H 6 | 7 | #include 8 | #include 9 | 10 | #include "GridTransformer.h" 11 | #include "led-matrix.h" 12 | 13 | class Config { 14 | public: 15 | Config(rgb_matrix::RGBMatrix::Options *options, 16 | const std::string& filename); 17 | 18 | // Attribute accessors: 19 | int getDisplayWidth() const { 20 | return (_display_width < 0) 21 | ? getPanelWidth() * getChainLength() 22 | : _display_width; 23 | } 24 | int getDisplayHeight() const { 25 | return (_display_height < 0) 26 | ? getPanelHeight() * getParallelCount() 27 | : _display_height; 28 | } 29 | int getPanelWidth() const { 30 | return (_panel_width) < 0 ? 32 : _panel_width; 31 | } 32 | int getPanelHeight() const { 33 | return _moptions->rows; 34 | } 35 | int getChainLength() const { 36 | return _chain_length; 37 | } 38 | int getParallelCount() const { 39 | return _moptions->parallel; 40 | } 41 | bool hasTransformer() const { return !_panels.empty(); } 42 | GridTransformer getGridTransformer() const { 43 | return GridTransformer(getDisplayWidth(), getDisplayHeight(), 44 | getPanelWidth(), getPanelHeight(), 45 | getChainLength(), _panels); 46 | } 47 | bool hasCropOrigin() const { 48 | return (_crop_x > -1) && (_crop_y > -1); 49 | } 50 | int getCropX() const { 51 | return _crop_x; 52 | } 53 | int getCropY() const { 54 | return _crop_y; 55 | } 56 | 57 | private: 58 | rgb_matrix::RGBMatrix::Options* const _moptions; 59 | int _display_width, 60 | _display_height, 61 | _panel_width, 62 | _chain_length, 63 | _crop_x, 64 | _crop_y; 65 | std::vector _panels; 66 | }; 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /GridTransformer.cpp: -------------------------------------------------------------------------------- 1 | // LED matrix library transformer to map a rectangular canvas onto a complex 2 | // chain of matrices. 3 | // Author: Tony DiCola 4 | #include "GridTransformer.h" 5 | 6 | using namespace rgb_matrix; 7 | using namespace std; 8 | 9 | GridTransformer::GridTransformer(int width, int height, int panel_width, int panel_height, 10 | int chain_length, const std::vector& panels): 11 | _width(width), 12 | _height(height), 13 | _panel_width(panel_width), 14 | _panel_height(panel_height), 15 | _chain_length(chain_length), 16 | _source(NULL), 17 | _panels(panels) 18 | { 19 | // Display width must be a multiple of the panel pixel column count. 20 | assert(_width % _panel_width == 0); 21 | // Display height must be a multiple of the panel pixel row count. 22 | assert(_height % _panel_height == 0); 23 | // Compute number of rows and columns of panels. 24 | _rows = _height / _panel_height; 25 | _cols = _width / _panel_width; 26 | // Check panel definition list has exactly the expected number of panels. 27 | assert((_rows * _cols) == (int)_panels.size()); 28 | } 29 | 30 | void GridTransformer::SetPixel(int x, int y, uint8_t red, uint8_t green, uint8_t blue) { 31 | assert(_source != NULL); 32 | if ((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) { 33 | return; 34 | } 35 | 36 | // Figure out what row and column panel this pixel is within. 37 | int row = y / _panel_height; 38 | int col = x / _panel_width; 39 | 40 | // Get the panel information for this pixel. 41 | Panel panel = _panels[_cols*row + col]; 42 | 43 | // Compute location of the pixel within the panel. 44 | x = x % _panel_width; 45 | y = y % _panel_height; 46 | 47 | // Perform any panel rotation to the pixel. 48 | // NOTE: 90 and 270 degree rotation only possible on 32 row (square) panels. 49 | if (panel.rotate == 90) { 50 | assert(_panel_height == _panel_width); 51 | int old_x = x; 52 | x = (_panel_height-1)-y; 53 | y = old_x; 54 | } 55 | else if (panel.rotate == 180) { 56 | x = (_panel_width-1)-x; 57 | y = (_panel_height-1)-y; 58 | } 59 | else if (panel.rotate == 270) { 60 | assert(_panel_height == _panel_width); 61 | int old_y = y; 62 | y = (_panel_width-1)-x; 63 | x = old_y; 64 | } 65 | 66 | // Determine x offset into the source panel based on its order along the chain. 67 | // The order needs to be inverted because the matrix library starts with the 68 | // origin of an image at the end of the chain and not at the start (where 69 | // ordering begins for this transformer). 70 | int x_offset = ((_chain_length-1)-panel.order)*_panel_width; 71 | 72 | // Determine y offset into the source panel based on its parrallel chain value. 73 | int y_offset = panel.parallel*_panel_height; 74 | 75 | _source->SetPixel(x_offset + x, 76 | y_offset + y, 77 | red, green, blue); 78 | } 79 | 80 | Canvas* GridTransformer::Transform(Canvas* source) { 81 | assert(source != NULL); 82 | int swidth = source->width(); 83 | int sheight = source->height(); 84 | assert((_width * _height) == (swidth * sheight)); 85 | _source = source; 86 | return this; 87 | } 88 | -------------------------------------------------------------------------------- /GridTransformer.h: -------------------------------------------------------------------------------- 1 | // LED matrix library transformer to map a rectangular canvas onto a complex 2 | // chain of matrices. 3 | // Author: Tony DiCola 4 | #ifndef GRIDTRANSFORMER_H 5 | #define GRIDTRANSFORMER_H 6 | 7 | #include 8 | #include 9 | 10 | #include "led-matrix.h" 11 | 12 | 13 | class GridTransformer: public rgb_matrix::Canvas, public rgb_matrix::CanvasTransformer { 14 | public: 15 | struct Panel { 16 | int order; 17 | int rotate; 18 | int parallel; 19 | }; 20 | 21 | GridTransformer(int width, int height, int panel_width, int panel_height, 22 | int chain_length, const std::vector& panels); 23 | virtual ~GridTransformer() {} 24 | 25 | // Canvas interface implementation: 26 | virtual int width() const { 27 | return _width; 28 | } 29 | virtual int height() const { 30 | return _height; 31 | } 32 | virtual void Clear() { 33 | assert(_source != NULL); 34 | _source->Clear(); 35 | } 36 | virtual void Fill(uint8_t red, uint8_t green, uint8_t blue) { 37 | assert(_source != NULL); 38 | _source->Fill(red, green, blue); 39 | } 40 | virtual void SetPixel(int x, int y, uint8_t red, uint8_t green, uint8_t blue); 41 | 42 | // Transformer interface implementation: 43 | virtual rgb_matrix::Canvas* Transform(rgb_matrix::Canvas* source); 44 | 45 | // Other attribute accessors. 46 | int getRows() const { 47 | return _rows; 48 | } 49 | int getColumns() const { 50 | return _cols; 51 | } 52 | 53 | private: 54 | int _width, 55 | _height, 56 | _panel_width, 57 | _panel_height, 58 | _chain_length, 59 | _rows, 60 | _cols; 61 | rgb_matrix::Canvas* _source; 62 | std::vector _panels; 63 | }; 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Configure the rpi-rgb-led-matrix library here: 2 | # The HARDWARE_DESC=adafruit-hat value configures the library to use the 3 | # Adafruit LED matrix HAT wiring, and the -DRGB_SLOWDOWN_GPIO=1 value configures 4 | # the library to work with a Raspberry Pi 2. For a Pi 1 (or perhaps even on 5 | # a Pi 2, but I found it necessary in my testing) you can remove the 6 | # -DRGB_SLOWDOWN_GPIO=1 option (You also can set this value at runtime via the 7 | # command line option --led-slowdown-gpio=1). 8 | # You can also add any other rpi-rgb-led-matrix library defines here 9 | # to configure the library for more special needs. See the library's docs for 10 | # details on options: 11 | # https://github.com/hzeller/rpi-rgb-led-matrix/blob/master/lib/Makefile 12 | export HARDWARE_DESC=adafruit-hat 13 | export USER_DEFINES=-DRGB_SLOWDOWN_GPIO=1 14 | 15 | # Configure compiler and libraries: 16 | CXX = g++ 17 | CXXFLAGS = -Wall -std=c++11 -O3 -I. -I./rpi-rgb-led-matrix/include -I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads -I/opt/vc/include/interface/vmcs_host -I/opt/vc/include/interface/vmcs_host/linux -L./rpi-rgb-led-matrix/lib -L/opt/vc/lib 18 | LIBS = -lrgbmatrix -lrt -lm -lpthread -lbcm_host -lconfig++ 19 | 20 | # Makefile rules: 21 | all: rpi-fb-matrix display-test 22 | 23 | rpi-fb-matrix: rpi-fb-matrix.o GridTransformer.o Config.o ./rpi-rgb-led-matrix/lib/librgbmatrix.a 24 | $(CXX) -o $@ $^ $(CXXFLAGS) $(LIBS) 25 | 26 | display-test: display-test.o GridTransformer.o Config.o glcdfont.o ./rpi-rgb-led-matrix/lib/librgbmatrix.a 27 | $(CXX) -o $@ $^ $(CXXFLAGS) $(LIBS) 28 | 29 | %.o: %.cpp $(DEPS) 30 | $(CXX) -c -o $@ $< $(CXXFLAGS) 31 | 32 | ./rpi-rgb-led-matrix/lib/librgbmatrix.a: 33 | $(MAKE) -C ./rpi-rgb-led-matrix/lib 34 | 35 | .PHONY: clean 36 | 37 | clean: 38 | rm -f *.o rpi-fb-matrix display-test 39 | $(MAKE) -C ./rpi-rgb-led-matrix/lib clean 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rpi-fb-matrix 2 | Raspberry Pi framebuffer copy tool for RGB LED matrices. Show what's on a Pi HDMI output on a big RGB LED matrix chain! 3 | See the full guide with details on installation and configuration at: https://learn.adafruit.com/raspberry-pi-led-matrix-display/overview 4 | 5 | ## Setup 6 | 7 | You **must** clone this repository with the recursive option so that necessary 8 | submodules are also cloned. Run this command: 9 | 10 | git clone --recursive https://github.com/adafruit/rpi-fb-matrix.git 11 | 12 | Next you will need a few dependencies (libconfig++) to compile the code. Run 13 | these commands to install the dependencies: 14 | 15 | sudo apt-get update 16 | sudo apt-get install -y build-essential libconfig++-dev 17 | 18 | Now if necessary you can change how the RGB LED matrix library is configured 19 | by editing the variables set in the Makefile. By default the Makefile is 20 | configured to work with the Adafruit LED matrix HAT and a Raspberry Pi 2. If 21 | you're using a different configuration open the Makefile and edit the `export DEFINES=...` 22 | line at the top. 23 | 24 | Build the project by running make: 25 | 26 | make 27 | 28 | Once compiled there will be two executables: 29 | 30 | * `rpi-fb-matrix`: The main program that will copy the contents of the primary 31 | display (HDMI output) to attached LED matrices. 32 | * `display-test`: A program to display the order and orientation of chained 33 | together LED matrices. Good for building complex display chains. 34 | 35 | Both executables understand the standard command line flags provided in the 36 | rpi-rgb-led-matrix library, for instance for choosing the gpio mapping. 37 | The default compile-choice gpio mapping is `adafruit-hat`, but you can change 38 | that to any [supported gpio mapping] depending on your set-up. 39 | 40 | The [configuration file](./matrix.cfg) allows to describe the geometry and 41 | panel-layout. It overrides geometry-related settings provided as flags 42 | (e.g. `--led-chain`). 43 | 44 | You can get a list of available command line options by giving `--led-help` 45 | ``` 46 | $ ./rpi-fb-matrix --led-help 47 | Usage: ./rpi-fb-matrix [flags] [config-file] 48 | Flags: 49 | --led-gpio-mapping= : Name of GPIO mapping used. Default "adafruit-hat" 50 | --led-rows= : Panel rows. 8, 16, 32 or 64. (Default: 32). 51 | --led-chain= : Number of daisy-chained panels. (Default: 1). 52 | --led-parallel= : For A/B+ models or RPi2,3b: parallel chains. range=1..3 (Default: 1). 53 | --led-pwm-bits=<1..11> : PWM bits (Default: 11). 54 | --led-brightness=: Brightness in percent (Default: 100). 55 | --led-scan-mode=<0..1> : 0 = progressive; 1 = interlaced (Default: 0). 56 | --led-show-refresh : Show refresh rate. 57 | --led-inverse : Switch if your matrix has inverse colors on. 58 | --led-rgb-sequence : Switch if your matrix has led colors swapped (Default: "RGB") 59 | --led-pwm-lsb-nanoseconds : PWM Nanoseconds for LSB (Default: 130) 60 | --led-no-hardware-pulse : Don't use hardware pin-pulse generation. 61 | --led-slowdown-gpio=<0..2>: Slowdown GPIO. Needed for faster Pis and/or slower panels (Default: 1). 62 | --led-daemon : Make the process run in the background as daemon. 63 | ``` 64 | 65 | ## Acknowledgments 66 | 67 | This program makes use of the following excellent libraries: 68 | 69 | * [rpi-rgb-led-matrix](https://github.com/hzeller/rpi-rgb-led-matrix) 70 | * [libconfig](http://www.hyperrealm.com/libconfig/) 71 | 72 | Framebuffer capture code based on information from the [rpi-fbcp](https://github.com/tasanakorn/rpi-fbcp) tool. 73 | 74 | ## License 75 | 76 | Released under the GPL v2.0 license, see LICENSE.txt for details. 77 | 78 | [supported gpio mapping]: https://github.com/hzeller/rpi-rgb-led-matrix/blob/master/lib/Makefile#L19 79 | -------------------------------------------------------------------------------- /display-test.cpp: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // Program to aid in the testing of LED matrix chains. 3 | // Author: Tony DiCola 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include "Config.h" 14 | #include "glcdfont.h" 15 | #include "GridTransformer.h" 16 | 17 | using namespace std; 18 | using namespace rgb_matrix; 19 | 20 | 21 | // Global to keep track of if the program should run. 22 | // Will be set false by a SIGINT handler when ctrl-c is 23 | // pressed, then the main loop will cleanly exit. 24 | volatile bool running = true; 25 | 26 | void printCanvas(Canvas* canvas, int x, int y, const string& message, 27 | int r = 255, int g = 255, int b = 255) { 28 | // Loop through all the characters and print them starting at the provided 29 | // coordinates. 30 | for (auto c: message) { 31 | // Loop through each column of the character. 32 | for (int i=0; i<5; ++i) { 33 | unsigned char col = glcdfont[c*5+i]; 34 | x += 1; 35 | // Loop through each row of the column. 36 | for (int j=0; j<8; ++j) { 37 | // Put a pixel for each 1 in the column byte. 38 | if ((col >> j) & 0x01) { 39 | canvas->SetPixel(x, y+j, r, g, b); 40 | } 41 | } 42 | } 43 | // Add a column of padding between characters. 44 | x += 1; 45 | } 46 | } 47 | 48 | static void sigintHandler(int s) { 49 | running = false; 50 | } 51 | 52 | static void usage(const char* progname) { 53 | std::cerr << "Usage: " << progname << " [flags] [config-file]" << std::endl; 54 | std::cerr << "Flags:" << std::endl; 55 | rgb_matrix::PrintMatrixFlags(stderr); 56 | } 57 | 58 | int main(int argc, char** argv) { 59 | try { 60 | // Initialize from flags. 61 | rgb_matrix::RGBMatrix::Options matrix_options; 62 | rgb_matrix::RuntimeOptions runtime_options; 63 | if (!rgb_matrix::ParseOptionsFromFlags(&argc, &argv, 64 | &matrix_options, &runtime_options)) { 65 | usage(argv[0]); 66 | return 1; 67 | } 68 | 69 | Config config(&matrix_options, argc >= 2 ? argv[1] : "/dev/null"); 70 | cout << "Using config values: " << endl 71 | << " display_width: " << config.getDisplayWidth() << endl 72 | << " display_height: " << config.getDisplayHeight() << endl 73 | << " panel_width: " << config.getPanelWidth() << endl 74 | << " panel_height: " << config.getPanelHeight() << endl 75 | << " chain_length: " << config.getChainLength() << endl 76 | << " parallel_count: " << config.getParallelCount() << endl; 77 | 78 | // Initialize matrix library. 79 | // Create canvas and apply GridTransformer. 80 | RGBMatrix *canvas = CreateMatrixFromOptions(matrix_options, runtime_options); 81 | 82 | int panel_rows = config.getParallelCount(); 83 | int panel_columns = config.getChainLength(); 84 | if (config.hasTransformer()) { 85 | GridTransformer grid = config.getGridTransformer(); 86 | canvas->ApplyStaticTransformer(grid); 87 | panel_rows = grid.getRows(); 88 | panel_columns = grid.getColumns(); 89 | } 90 | 91 | cout << " grid rows: " << panel_rows << endl 92 | << " grid cols: " << panel_columns << endl; 93 | 94 | // Clear the canvas, then draw on each panel. 95 | canvas->Fill(0, 0, 0); 96 | for (int j=0; jClear(); 114 | delete canvas; 115 | } 116 | catch (const exception& ex) { 117 | cerr << ex.what() << endl; 118 | usage(argv[0]); 119 | return -1; 120 | } 121 | return 0; 122 | } 123 | -------------------------------------------------------------------------------- /glcdfont.c: -------------------------------------------------------------------------------- 1 | // Standard ASCII 5x7 font 2 | const unsigned char glcdfont[] = { 3 | 0x00, 0x00, 0x00, 0x00, 0x00, 4 | 0x3E, 0x5B, 0x4F, 0x5B, 0x3E, 5 | 0x3E, 0x6B, 0x4F, 0x6B, 0x3E, 6 | 0x1C, 0x3E, 0x7C, 0x3E, 0x1C, 7 | 0x18, 0x3C, 0x7E, 0x3C, 0x18, 8 | 0x1C, 0x57, 0x7D, 0x57, 0x1C, 9 | 0x1C, 0x5E, 0x7F, 0x5E, 0x1C, 10 | 0x00, 0x18, 0x3C, 0x18, 0x00, 11 | 0xFF, 0xE7, 0xC3, 0xE7, 0xFF, 12 | 0x00, 0x18, 0x24, 0x18, 0x00, 13 | 0xFF, 0xE7, 0xDB, 0xE7, 0xFF, 14 | 0x30, 0x48, 0x3A, 0x06, 0x0E, 15 | 0x26, 0x29, 0x79, 0x29, 0x26, 16 | 0x40, 0x7F, 0x05, 0x05, 0x07, 17 | 0x40, 0x7F, 0x05, 0x25, 0x3F, 18 | 0x5A, 0x3C, 0xE7, 0x3C, 0x5A, 19 | 0x7F, 0x3E, 0x1C, 0x1C, 0x08, 20 | 0x08, 0x1C, 0x1C, 0x3E, 0x7F, 21 | 0x14, 0x22, 0x7F, 0x22, 0x14, 22 | 0x5F, 0x5F, 0x00, 0x5F, 0x5F, 23 | 0x06, 0x09, 0x7F, 0x01, 0x7F, 24 | 0x00, 0x66, 0x89, 0x95, 0x6A, 25 | 0x60, 0x60, 0x60, 0x60, 0x60, 26 | 0x94, 0xA2, 0xFF, 0xA2, 0x94, 27 | 0x08, 0x04, 0x7E, 0x04, 0x08, 28 | 0x10, 0x20, 0x7E, 0x20, 0x10, 29 | 0x08, 0x08, 0x2A, 0x1C, 0x08, 30 | 0x08, 0x1C, 0x2A, 0x08, 0x08, 31 | 0x1E, 0x10, 0x10, 0x10, 0x10, 32 | 0x0C, 0x1E, 0x0C, 0x1E, 0x0C, 33 | 0x30, 0x38, 0x3E, 0x38, 0x30, 34 | 0x06, 0x0E, 0x3E, 0x0E, 0x06, 35 | 0x00, 0x00, 0x00, 0x00, 0x00, 36 | 0x00, 0x00, 0x5F, 0x00, 0x00, 37 | 0x00, 0x07, 0x00, 0x07, 0x00, 38 | 0x14, 0x7F, 0x14, 0x7F, 0x14, 39 | 0x24, 0x2A, 0x7F, 0x2A, 0x12, 40 | 0x23, 0x13, 0x08, 0x64, 0x62, 41 | 0x36, 0x49, 0x56, 0x20, 0x50, 42 | 0x00, 0x08, 0x07, 0x03, 0x00, 43 | 0x00, 0x1C, 0x22, 0x41, 0x00, 44 | 0x00, 0x41, 0x22, 0x1C, 0x00, 45 | 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 46 | 0x08, 0x08, 0x3E, 0x08, 0x08, 47 | 0x00, 0x80, 0x70, 0x30, 0x00, 48 | 0x08, 0x08, 0x08, 0x08, 0x08, 49 | 0x00, 0x00, 0x60, 0x60, 0x00, 50 | 0x20, 0x10, 0x08, 0x04, 0x02, 51 | 0x3E, 0x51, 0x49, 0x45, 0x3E, 52 | 0x00, 0x42, 0x7F, 0x40, 0x00, 53 | 0x72, 0x49, 0x49, 0x49, 0x46, 54 | 0x21, 0x41, 0x49, 0x4D, 0x33, 55 | 0x18, 0x14, 0x12, 0x7F, 0x10, 56 | 0x27, 0x45, 0x45, 0x45, 0x39, 57 | 0x3C, 0x4A, 0x49, 0x49, 0x31, 58 | 0x41, 0x21, 0x11, 0x09, 0x07, 59 | 0x36, 0x49, 0x49, 0x49, 0x36, 60 | 0x46, 0x49, 0x49, 0x29, 0x1E, 61 | 0x00, 0x00, 0x14, 0x00, 0x00, 62 | 0x00, 0x40, 0x34, 0x00, 0x00, 63 | 0x00, 0x08, 0x14, 0x22, 0x41, 64 | 0x14, 0x14, 0x14, 0x14, 0x14, 65 | 0x00, 0x41, 0x22, 0x14, 0x08, 66 | 0x02, 0x01, 0x59, 0x09, 0x06, 67 | 0x3E, 0x41, 0x5D, 0x59, 0x4E, 68 | 0x7C, 0x12, 0x11, 0x12, 0x7C, 69 | 0x7F, 0x49, 0x49, 0x49, 0x36, 70 | 0x3E, 0x41, 0x41, 0x41, 0x22, 71 | 0x7F, 0x41, 0x41, 0x41, 0x3E, 72 | 0x7F, 0x49, 0x49, 0x49, 0x41, 73 | 0x7F, 0x09, 0x09, 0x09, 0x01, 74 | 0x3E, 0x41, 0x41, 0x51, 0x73, 75 | 0x7F, 0x08, 0x08, 0x08, 0x7F, 76 | 0x00, 0x41, 0x7F, 0x41, 0x00, 77 | 0x20, 0x40, 0x41, 0x3F, 0x01, 78 | 0x7F, 0x08, 0x14, 0x22, 0x41, 79 | 0x7F, 0x40, 0x40, 0x40, 0x40, 80 | 0x7F, 0x02, 0x1C, 0x02, 0x7F, 81 | 0x7F, 0x04, 0x08, 0x10, 0x7F, 82 | 0x3E, 0x41, 0x41, 0x41, 0x3E, 83 | 0x7F, 0x09, 0x09, 0x09, 0x06, 84 | 0x3E, 0x41, 0x51, 0x21, 0x5E, 85 | 0x7F, 0x09, 0x19, 0x29, 0x46, 86 | 0x26, 0x49, 0x49, 0x49, 0x32, 87 | 0x03, 0x01, 0x7F, 0x01, 0x03, 88 | 0x3F, 0x40, 0x40, 0x40, 0x3F, 89 | 0x1F, 0x20, 0x40, 0x20, 0x1F, 90 | 0x3F, 0x40, 0x38, 0x40, 0x3F, 91 | 0x63, 0x14, 0x08, 0x14, 0x63, 92 | 0x03, 0x04, 0x78, 0x04, 0x03, 93 | 0x61, 0x59, 0x49, 0x4D, 0x43, 94 | 0x00, 0x7F, 0x41, 0x41, 0x41, 95 | 0x02, 0x04, 0x08, 0x10, 0x20, 96 | 0x00, 0x41, 0x41, 0x41, 0x7F, 97 | 0x04, 0x02, 0x01, 0x02, 0x04, 98 | 0x40, 0x40, 0x40, 0x40, 0x40, 99 | 0x00, 0x03, 0x07, 0x08, 0x00, 100 | 0x20, 0x54, 0x54, 0x78, 0x40, 101 | 0x7F, 0x28, 0x44, 0x44, 0x38, 102 | 0x38, 0x44, 0x44, 0x44, 0x28, 103 | 0x38, 0x44, 0x44, 0x28, 0x7F, 104 | 0x38, 0x54, 0x54, 0x54, 0x18, 105 | 0x00, 0x08, 0x7E, 0x09, 0x02, 106 | 0x18, 0xA4, 0xA4, 0x9C, 0x78, 107 | 0x7F, 0x08, 0x04, 0x04, 0x78, 108 | 0x00, 0x44, 0x7D, 0x40, 0x00, 109 | 0x20, 0x40, 0x40, 0x3D, 0x00, 110 | 0x7F, 0x10, 0x28, 0x44, 0x00, 111 | 0x00, 0x41, 0x7F, 0x40, 0x00, 112 | 0x7C, 0x04, 0x78, 0x04, 0x78, 113 | 0x7C, 0x08, 0x04, 0x04, 0x78, 114 | 0x38, 0x44, 0x44, 0x44, 0x38, 115 | 0xFC, 0x18, 0x24, 0x24, 0x18, 116 | 0x18, 0x24, 0x24, 0x18, 0xFC, 117 | 0x7C, 0x08, 0x04, 0x04, 0x08, 118 | 0x48, 0x54, 0x54, 0x54, 0x24, 119 | 0x04, 0x04, 0x3F, 0x44, 0x24, 120 | 0x3C, 0x40, 0x40, 0x20, 0x7C, 121 | 0x1C, 0x20, 0x40, 0x20, 0x1C, 122 | 0x3C, 0x40, 0x30, 0x40, 0x3C, 123 | 0x44, 0x28, 0x10, 0x28, 0x44, 124 | 0x4C, 0x90, 0x90, 0x90, 0x7C, 125 | 0x44, 0x64, 0x54, 0x4C, 0x44, 126 | 0x00, 0x08, 0x36, 0x41, 0x00, 127 | 0x00, 0x00, 0x77, 0x00, 0x00, 128 | 0x00, 0x41, 0x36, 0x08, 0x00, 129 | 0x02, 0x01, 0x02, 0x04, 0x02, 130 | 0x3C, 0x26, 0x23, 0x26, 0x3C, 131 | 0x1E, 0xA1, 0xA1, 0x61, 0x12, 132 | 0x3A, 0x40, 0x40, 0x20, 0x7A, 133 | 0x38, 0x54, 0x54, 0x55, 0x59, 134 | 0x21, 0x55, 0x55, 0x79, 0x41, 135 | 0x22, 0x54, 0x54, 0x78, 0x42, // a-umlaut 136 | 0x21, 0x55, 0x54, 0x78, 0x40, 137 | 0x20, 0x54, 0x55, 0x79, 0x40, 138 | 0x0C, 0x1E, 0x52, 0x72, 0x12, 139 | 0x39, 0x55, 0x55, 0x55, 0x59, 140 | 0x39, 0x54, 0x54, 0x54, 0x59, 141 | 0x39, 0x55, 0x54, 0x54, 0x58, 142 | 0x00, 0x00, 0x45, 0x7C, 0x41, 143 | 0x00, 0x02, 0x45, 0x7D, 0x42, 144 | 0x00, 0x01, 0x45, 0x7C, 0x40, 145 | 0x7D, 0x12, 0x11, 0x12, 0x7D, // A-umlaut 146 | 0xF0, 0x28, 0x25, 0x28, 0xF0, 147 | 0x7C, 0x54, 0x55, 0x45, 0x00, 148 | 0x20, 0x54, 0x54, 0x7C, 0x54, 149 | 0x7C, 0x0A, 0x09, 0x7F, 0x49, 150 | 0x32, 0x49, 0x49, 0x49, 0x32, 151 | 0x3A, 0x44, 0x44, 0x44, 0x3A, // o-umlaut 152 | 0x32, 0x4A, 0x48, 0x48, 0x30, 153 | 0x3A, 0x41, 0x41, 0x21, 0x7A, 154 | 0x3A, 0x42, 0x40, 0x20, 0x78, 155 | 0x00, 0x9D, 0xA0, 0xA0, 0x7D, 156 | 0x3D, 0x42, 0x42, 0x42, 0x3D, // O-umlaut 157 | 0x3D, 0x40, 0x40, 0x40, 0x3D, 158 | 0x3C, 0x24, 0xFF, 0x24, 0x24, 159 | 0x48, 0x7E, 0x49, 0x43, 0x66, 160 | 0x2B, 0x2F, 0xFC, 0x2F, 0x2B, 161 | 0xFF, 0x09, 0x29, 0xF6, 0x20, 162 | 0xC0, 0x88, 0x7E, 0x09, 0x03, 163 | 0x20, 0x54, 0x54, 0x79, 0x41, 164 | 0x00, 0x00, 0x44, 0x7D, 0x41, 165 | 0x30, 0x48, 0x48, 0x4A, 0x32, 166 | 0x38, 0x40, 0x40, 0x22, 0x7A, 167 | 0x00, 0x7A, 0x0A, 0x0A, 0x72, 168 | 0x7D, 0x0D, 0x19, 0x31, 0x7D, 169 | 0x26, 0x29, 0x29, 0x2F, 0x28, 170 | 0x26, 0x29, 0x29, 0x29, 0x26, 171 | 0x30, 0x48, 0x4D, 0x40, 0x20, 172 | 0x38, 0x08, 0x08, 0x08, 0x08, 173 | 0x08, 0x08, 0x08, 0x08, 0x38, 174 | 0x2F, 0x10, 0xC8, 0xAC, 0xBA, 175 | 0x2F, 0x10, 0x28, 0x34, 0xFA, 176 | 0x00, 0x00, 0x7B, 0x00, 0x00, 177 | 0x08, 0x14, 0x2A, 0x14, 0x22, 178 | 0x22, 0x14, 0x2A, 0x14, 0x08, 179 | 0x55, 0x00, 0x55, 0x00, 0x55, // #176 (25% block) missing in old code 180 | 0xAA, 0x55, 0xAA, 0x55, 0xAA, // 50% block 181 | 0xFF, 0x55, 0xFF, 0x55, 0xFF, // 75% block 182 | 0x00, 0x00, 0x00, 0xFF, 0x00, 183 | 0x10, 0x10, 0x10, 0xFF, 0x00, 184 | 0x14, 0x14, 0x14, 0xFF, 0x00, 185 | 0x10, 0x10, 0xFF, 0x00, 0xFF, 186 | 0x10, 0x10, 0xF0, 0x10, 0xF0, 187 | 0x14, 0x14, 0x14, 0xFC, 0x00, 188 | 0x14, 0x14, 0xF7, 0x00, 0xFF, 189 | 0x00, 0x00, 0xFF, 0x00, 0xFF, 190 | 0x14, 0x14, 0xF4, 0x04, 0xFC, 191 | 0x14, 0x14, 0x17, 0x10, 0x1F, 192 | 0x10, 0x10, 0x1F, 0x10, 0x1F, 193 | 0x14, 0x14, 0x14, 0x1F, 0x00, 194 | 0x10, 0x10, 0x10, 0xF0, 0x00, 195 | 0x00, 0x00, 0x00, 0x1F, 0x10, 196 | 0x10, 0x10, 0x10, 0x1F, 0x10, 197 | 0x10, 0x10, 0x10, 0xF0, 0x10, 198 | 0x00, 0x00, 0x00, 0xFF, 0x10, 199 | 0x10, 0x10, 0x10, 0x10, 0x10, 200 | 0x10, 0x10, 0x10, 0xFF, 0x10, 201 | 0x00, 0x00, 0x00, 0xFF, 0x14, 202 | 0x00, 0x00, 0xFF, 0x00, 0xFF, 203 | 0x00, 0x00, 0x1F, 0x10, 0x17, 204 | 0x00, 0x00, 0xFC, 0x04, 0xF4, 205 | 0x14, 0x14, 0x17, 0x10, 0x17, 206 | 0x14, 0x14, 0xF4, 0x04, 0xF4, 207 | 0x00, 0x00, 0xFF, 0x00, 0xF7, 208 | 0x14, 0x14, 0x14, 0x14, 0x14, 209 | 0x14, 0x14, 0xF7, 0x00, 0xF7, 210 | 0x14, 0x14, 0x14, 0x17, 0x14, 211 | 0x10, 0x10, 0x1F, 0x10, 0x1F, 212 | 0x14, 0x14, 0x14, 0xF4, 0x14, 213 | 0x10, 0x10, 0xF0, 0x10, 0xF0, 214 | 0x00, 0x00, 0x1F, 0x10, 0x1F, 215 | 0x00, 0x00, 0x00, 0x1F, 0x14, 216 | 0x00, 0x00, 0x00, 0xFC, 0x14, 217 | 0x00, 0x00, 0xF0, 0x10, 0xF0, 218 | 0x10, 0x10, 0xFF, 0x10, 0xFF, 219 | 0x14, 0x14, 0x14, 0xFF, 0x14, 220 | 0x10, 0x10, 0x10, 0x1F, 0x00, 221 | 0x00, 0x00, 0x00, 0xF0, 0x10, 222 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 223 | 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 224 | 0xFF, 0xFF, 0xFF, 0x00, 0x00, 225 | 0x00, 0x00, 0x00, 0xFF, 0xFF, 226 | 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 227 | 0x38, 0x44, 0x44, 0x38, 0x44, 228 | 0xFC, 0x4A, 0x4A, 0x4A, 0x34, // sharp-s or beta 229 | 0x7E, 0x02, 0x02, 0x06, 0x06, 230 | 0x02, 0x7E, 0x02, 0x7E, 0x02, 231 | 0x63, 0x55, 0x49, 0x41, 0x63, 232 | 0x38, 0x44, 0x44, 0x3C, 0x04, 233 | 0x40, 0x7E, 0x20, 0x1E, 0x20, 234 | 0x06, 0x02, 0x7E, 0x02, 0x02, 235 | 0x99, 0xA5, 0xE7, 0xA5, 0x99, 236 | 0x1C, 0x2A, 0x49, 0x2A, 0x1C, 237 | 0x4C, 0x72, 0x01, 0x72, 0x4C, 238 | 0x30, 0x4A, 0x4D, 0x4D, 0x30, 239 | 0x30, 0x48, 0x78, 0x48, 0x30, 240 | 0xBC, 0x62, 0x5A, 0x46, 0x3D, 241 | 0x3E, 0x49, 0x49, 0x49, 0x00, 242 | 0x7E, 0x01, 0x01, 0x01, 0x7E, 243 | 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 244 | 0x44, 0x44, 0x5F, 0x44, 0x44, 245 | 0x40, 0x51, 0x4A, 0x44, 0x40, 246 | 0x40, 0x44, 0x4A, 0x51, 0x40, 247 | 0x00, 0x00, 0xFF, 0x01, 0x03, 248 | 0xE0, 0x80, 0xFF, 0x00, 0x00, 249 | 0x08, 0x08, 0x6B, 0x6B, 0x08, 250 | 0x36, 0x12, 0x36, 0x24, 0x36, 251 | 0x06, 0x0F, 0x09, 0x0F, 0x06, 252 | 0x00, 0x00, 0x18, 0x18, 0x00, 253 | 0x00, 0x00, 0x10, 0x10, 0x00, 254 | 0x30, 0x40, 0xFF, 0x01, 0x01, 255 | 0x00, 0x1F, 0x01, 0x01, 0x1E, 256 | 0x00, 0x19, 0x1D, 0x17, 0x12, 257 | 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 258 | 0x00, 0x00, 0x00, 0x00, 0x00 // #255 NBSP 259 | }; 260 | -------------------------------------------------------------------------------- /glcdfont.h: -------------------------------------------------------------------------------- 1 | #ifndef GLCDFONT_H 2 | #define GLCDFONT_H 3 | 4 | extern const unsigned char glcdfont[]; 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /matrix.cfg: -------------------------------------------------------------------------------- 1 | // LED Matrix Display Configuration 2 | 3 | // Define the entire width and height of the display in pixels. 4 | // This is the _total_ width and height of the rectangle defined by all the 5 | // chained panels. The width should be a multiple of the panel pixel width (32), 6 | // and the height should be a multiple of the panel pixel height (8, 16, or 32). 7 | display_width = 64; 8 | display_height = 64; 9 | 10 | // Define the width of each panel in pixels. This should always be 32 (but can 11 | // in theory be changed). 12 | panel_width = 32; 13 | 14 | // Define the height of each panel in pixels. This is typically 8, 16, or 32. 15 | // NOTE: Each panel in the display _must_ be the same height! You cannot mix 16 | // 16 and 32 pixel high panels for example. 17 | panel_height = 32; 18 | 19 | // Define the total number of panels in each chain. Count up however many 20 | // panels are connected together and put that value here. If you're using 21 | // multiple parallel chains count each one up separately and pick the largest 22 | // value for this configuration. 23 | chain_length = 4; 24 | 25 | // Define the total number of parallel chains. If using the Adafruit HAT you 26 | // can only have one chain so stick with the value 1. The Pi 2 can support up 27 | // to 3 parallel chains, see the rpi-rgb-led-matrix library for more information: 28 | // https://github.com/hzeller/rpi-rgb-led-matrix#chaining-parallel-chains-and-coordinate-system 29 | parallel_count = 1; 30 | 31 | // Configure each LED matrix panel. 32 | // This is a two-dimensional array with an entry for each panel. The array 33 | // defines the grid that will subdivide the display, so for example a 64x64 size 34 | // display with 32x32 pixel panels would be a 2x2 array of panel configurations. 35 | // 36 | // For each panel you must set the order that it is within its chain, i.e. the 37 | // first panel in a chain is order = 0, the next one is order = 1, etc. You can 38 | // also set a rotation for each panel to account for changes in panel orientation 39 | // (like when 'snaking' a series of panels end to end for shorter wire runs). 40 | // 41 | // For example the configuration below defines this grid display of panels and 42 | // their wiring (starting from the upper right panel and snaking left, down, and 43 | // right to the bottom right panel): 44 | // ______________ ______________ 45 | // | Panel | | Panel | 46 | // /==| order = 1 |<=| order = 0 |<= Chain start (from Pi) 47 | // | | rotate = 0 | | rotate = 0 | 48 | // | |______________| |______________| 49 | // | ______________ ______________ 50 | // | | Panel | | Panel | 51 | // \==| order = 2 |=>| order = 3 | 52 | // | rotate = 180 | | rotate = 180 | 53 | // |______________| |______________| 54 | // 55 | // Notice the chain starts in the upper right and snakes around to the bottom 56 | // right. The order of each panel is set as its position along the chain, 57 | // and rotation is applied to the lower panels that are flipped around relative 58 | // to the panels above them. 59 | // 60 | // Not shown but if you're using parallel chains you can specify for each entry 61 | // in the panels list a 'parallel = x;' option where x is the ID of a parallel 62 | // chain (0, 1, or 2). 63 | panels = ( 64 | ( { order = 1; rotate = 0; }, { order = 0; rotate = 0; } ), 65 | ( { order = 2; rotate = 180; }, { order = 3; rotate = 180; } ) 66 | ) 67 | 68 | // By default the rpi-fb-matrix tool will resize and scale down the screen 69 | // to fit the resolution of the display panels. However you can instead grab 70 | // a specific pixel-perfect copy of a region of the screen by setting the x, y 71 | // screen pixel coordinates below. A rectangle of the exact size of the display 72 | // (i.e. display_width x display_height pixels) will be copied from the screen 73 | // starting at the provided x, y coordinates. Comment this out to disable 74 | // this crop behavior and instead resize the screen down to the matrix display. 75 | //crop_origin = (0, 0) 76 | -------------------------------------------------------------------------------- /rpi-fb-matrix.cpp: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- 2 | // Program to copy the contents of the Raspberry Pi primary display to LED matrices. 3 | // Author: Tony DiCola 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "Config.h" 18 | #include "GridTransformer.h" 19 | 20 | using namespace std; 21 | using namespace rgb_matrix; 22 | 23 | // Global to keep track of if the program should run. 24 | // Will be set false by a SIGINT handler when ctrl-c is 25 | // pressed, then the main loop will cleanly exit. 26 | volatile bool running = true; 27 | 28 | // Class to encapsulate all the logic for capturing an image of the Pi's primary 29 | // display. Manages all the BCM GPU and CPU resources automatically while in scope. 30 | class BCMDisplayCapture { 31 | public: 32 | BCMDisplayCapture(int width=-1, int height=-1): 33 | _width(width), 34 | _height(height), 35 | _display(0), 36 | _screen_resource(0), 37 | _screen_data(NULL) 38 | { 39 | // Get information about primary/HDMI display. 40 | _display = vc_dispmanx_display_open(0); 41 | if (!_display) { 42 | throw runtime_error("Unable to open primary display!"); 43 | } 44 | DISPMANX_MODEINFO_T display_info; 45 | if (vc_dispmanx_display_get_info(_display, &display_info)) { 46 | throw runtime_error("Unable to get primary display information!"); 47 | } 48 | cout << "Primary display:" << endl 49 | << " resolution: " << display_info.width << "x" << display_info.height << endl 50 | << " format: " << display_info.input_format << endl; 51 | // If no width and height were specified then grab the entire screen. 52 | if ((_width == -1) || (_height == -1)) { 53 | _width = display_info.width; 54 | _height = display_info.height; 55 | } 56 | // Create a GPU image surface to hold the captured screen. 57 | uint32_t image_prt; 58 | _screen_resource = vc_dispmanx_resource_create(VC_IMAGE_RGB888, _width, _height, &image_prt); 59 | if (!_screen_resource) { 60 | throw runtime_error("Unable to create screen surface!"); 61 | } 62 | // Create a rectangular region of the captured screen size. 63 | vc_dispmanx_rect_set(&_rect, 0, 0, _width, _height); 64 | // Allocate CPU memory for copying out the captured screen. Must be aligned 65 | // to a larger size because of GPU surface memory size constraints. 66 | _pitch = ALIGN_UP(_width*3, 32); 67 | _screen_data = new uint8_t[_pitch*_height]; 68 | } 69 | 70 | void capture() { 71 | // Capture the primary display and copy it from GPU to CPU memory. 72 | vc_dispmanx_snapshot(_display, _screen_resource, (DISPMANX_TRANSFORM_T)0); 73 | vc_dispmanx_resource_read_data(_screen_resource, &_rect, _screen_data, _pitch); 74 | } 75 | 76 | void getPixel(int x, int y, uint8_t* r, uint8_t* g, uint8_t* b) { 77 | // Grab the requested pixel from the last captured display image. 78 | uint8_t* row = _screen_data + (y*_pitch); 79 | *r = row[x*3]; 80 | *g = row[x*3+1]; 81 | *b = row[x*3+2]; 82 | } 83 | 84 | ~BCMDisplayCapture() { 85 | // Clean up BCM and other resources. 86 | if (_screen_resource != 0) { 87 | vc_dispmanx_resource_delete(_screen_resource); 88 | } 89 | if (_display != 0) { 90 | vc_dispmanx_display_close(_display); 91 | } 92 | if (_screen_data != NULL) { 93 | delete[] _screen_data; 94 | } 95 | } 96 | 97 | private: 98 | int _width, 99 | _height, 100 | _pitch; 101 | DISPMANX_DISPLAY_HANDLE_T _display; 102 | DISPMANX_RESOURCE_HANDLE_T _screen_resource; 103 | VC_RECT_T _rect; 104 | uint8_t* _screen_data; 105 | }; 106 | 107 | static void sigintHandler(int s) { 108 | running = false; 109 | } 110 | 111 | static void usage(const char* progname) { 112 | std::cerr << "Usage: " << progname << " [flags] [config-file]" << std::endl; 113 | std::cerr << "Flags:" << std::endl; 114 | rgb_matrix::RGBMatrix::Options matrix_options; 115 | rgb_matrix::RuntimeOptions runtime_options; 116 | runtime_options.drop_privileges = -1; // Need root 117 | rgb_matrix::PrintMatrixFlags(stderr, matrix_options, runtime_options); 118 | } 119 | 120 | int main(int argc, char** argv) { 121 | try { 122 | // Initialize from flags. 123 | rgb_matrix::RGBMatrix::Options matrix_options; 124 | rgb_matrix::RuntimeOptions runtime_options; 125 | runtime_options.drop_privileges = -1; // Need root 126 | if (!rgb_matrix::ParseOptionsFromFlags(&argc, &argv, 127 | &matrix_options, &runtime_options)) { 128 | usage(argv[0]); 129 | return 1; 130 | } 131 | 132 | // Read additional configuration from config file if it exists 133 | Config config(&matrix_options, argc >= 2 ? argv[1] : "/dev/null"); 134 | cout << "Using config values: " << endl 135 | << " display_width: " << config.getDisplayWidth() << endl 136 | << " display_height: " << config.getDisplayHeight() << endl 137 | << " panel_width: " << config.getPanelWidth() << endl 138 | << " panel_height: " << config.getPanelHeight() << endl 139 | << " chain_length: " << config.getChainLength() << endl 140 | << " parallel_count: " << config.getParallelCount() << endl; 141 | 142 | // Set screen capture state depending on if a crop region is specified or not. 143 | // When not cropped grab the entire screen and resize it down to the LED display. 144 | // However when cropping is enabled instead grab the entire screen (by 145 | // setting the capture_width and capture_height to -1) and specify an offset 146 | // to the start of the crop rectangle. 147 | int capture_width = config.getDisplayWidth(); 148 | int capture_height = config.getDisplayHeight(); 149 | int x_offset = 0; 150 | int y_offset = 0; 151 | if (config.hasCropOrigin()) { 152 | cout << " crop_origin: (" << config.getCropX() << ", " << config.getCropY() << ")" << endl; 153 | capture_width = -1; 154 | capture_height = -1; 155 | x_offset = config.getCropX(); 156 | y_offset = config.getCropY(); 157 | } 158 | 159 | 160 | // Initialize matrix library. 161 | // Create canvas and apply GridTransformer. 162 | RGBMatrix *canvas = CreateMatrixFromOptions(matrix_options, runtime_options); 163 | if (config.hasTransformer()) { 164 | canvas->ApplyStaticTransformer(config.getGridTransformer()); 165 | } 166 | canvas->Clear(); 167 | 168 | // Initialize BCM functions and display capture class. 169 | bcm_host_init(); 170 | BCMDisplayCapture displayCapture(capture_width, capture_height); 171 | 172 | // Loop forever waiting for Ctrl-C signal to quit. 173 | signal(SIGINT, sigintHandler); 174 | cout << "Press Ctrl-C to quit..." << endl; 175 | while (running) { 176 | // Capture the current display image. 177 | displayCapture.capture(); 178 | // Loop through the frame data and set the pixels on the matrix canvas. 179 | for (int y=0; ySetPixel(x, y, red, green, blue); 184 | } 185 | } 186 | // Sleep for 25 milliseconds (40Hz refresh) 187 | usleep(25 * 1000); 188 | } 189 | canvas->Clear(); 190 | delete canvas; 191 | } 192 | catch (const exception& ex) { 193 | cerr << ex.what() << endl; 194 | usage(argv[0]); 195 | return -1; 196 | } 197 | return 0; 198 | } 199 | --------------------------------------------------------------------------------