includes go-rpi-rgb-led-matrix as a dep

This commit is contained in:
2023-12-21 11:06:27 -05:00
parent d1d6e2d9f9
commit 4dcea50309
187 changed files with 1174372 additions and 0 deletions
@@ -0,0 +1,2 @@
*.dll
*.exe
@@ -0,0 +1,21 @@
CSHARP_LIB=RGBLedMatrix.dll
SOURCES=RGBLedCanvas.cs RGBLedMatrix.cs RGBLedFont.cs
CSHARP_COMPILER=mcs
RGB_LIBDIR=../../lib
RGB_LIBRARY_NAME=rgbmatrix
RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).so.1
EXAMPLES_DIR=examples
$(CSHARP_LIB) : $(SOURCES) $(RGB_LIBRARY)
$(CSHARP_COMPILER) -target:library -out:$@ $(SOURCES)
$(RGB_LIBRARY):
$(MAKE) -C $(RGB_LIBDIR)
build: $(CSHARP_LIB)
$(MAKE) -C $(EXAMPLES_DIR) all
clean:
rm -f $(CSHARP_LIB)
@@ -0,0 +1,31 @@
C# bindings for RGB Matrix library
======================================
Building
--------
To build the C# wrapper for the RGB Matrix C library you need to first have mono installed.
### Install Mono
```shell
$ sudo apt-get update
$ sudo apt-get install mono-complete
```
Then, in the root directory for the matrix library type
```shell
make build-csharp
```
To run the example applications in the c#\examples folder
```shell
sudo mono minimal-example.exe
```
Notes
--------
C# applications look for libraries in the working directory of the application. To use this library for your own projects you will need to ensure you have RGBLedMatrix.dll and librgbmatrix.so in the same folder as your exe.
@@ -0,0 +1,100 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace rpi_rgb_led_matrix_sharp
{
public class RGBLedCanvas
{
#region DLLImports
[DllImport("librgbmatrix.so")]
internal static extern void led_canvas_get_size(IntPtr canvas, out int width, out int height);
[DllImport("librgbmatrix.so")]
internal static extern void led_canvas_set_pixel(IntPtr canvas, int x, int y, byte r, byte g, byte b);
[DllImport("librgbmatrix.so")]
internal static extern void led_canvas_clear(IntPtr canvas);
[DllImport("librgbmatrix.so")]
internal static extern void led_canvas_fill(IntPtr canvas, byte r, byte g, byte b);
[DllImport("librgbmatrix.so")]
internal static extern void draw_circle(IntPtr canvas, int xx, int y, int radius, byte r, byte g, byte b);
[DllImport("librgbmatrix.so")]
internal static extern void draw_line(IntPtr canvas, int x0, int y0, int x1, int y1, byte r, byte g, byte b);
#endregion
// 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;
int width;
int height;
led_canvas_get_size(_canvas, out width, out height);
Width = width;
Height = height;
}
public int Width {get; private set; }
public int Height { get; private set; }
public void SetPixel(int x, int y, Color color)
{
led_canvas_set_pixel(_canvas, x, y, color.R, color.G, color.B);
}
public void Fill(Color color)
{
led_canvas_fill(_canvas, color.R, color.G, color.B);
}
public void Clear()
{
led_canvas_clear(_canvas);
}
public void DrawCircle(int x0, int y0, int radius, Color color)
{
draw_circle(_canvas, x0, y0, radius, color.R, color.G, color.B);
}
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);
}
public int DrawText(RGBLedFont font, int x, int y, Color color, string text, int spacing=0, bool vertical=false)
{
return font.DrawText(_canvas, x, y, color, text, spacing, vertical);
}
}
public struct Color
{
public Color (int r, int g, int b)
{
R = (byte)r;
G = (byte)g;
B = (byte)b;
}
public Color(byte r, byte g, byte b)
{
R = r;
G = g;
B = b;
}
public byte R;
public byte G;
public byte B;
}
}
@@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace rpi_rgb_led_matrix_sharp
{
public class RGBLedFont : IDisposable
{
[DllImport("librgbmatrix.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern IntPtr load_font(string bdf_font_file);
[DllImport("librgbmatrix.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern int draw_text(IntPtr canvas, IntPtr font, int x, int y, byte r, byte g, byte b, string utf8_text, int extra_spacing);
[DllImport("librgbmatrix.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern int vertical_draw_text(IntPtr canvas, IntPtr font, int x, int y, byte r, byte g, byte b, string utf8_text, int kerning_offset);
[DllImport("librgbmatrix.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern void delete_font(IntPtr font);
public RGBLedFont(string bdf_font_file_path)
{
_font = load_font(bdf_font_file_path);
}
internal IntPtr _font;
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);
}
#region IDisposable Support
private bool disposedValue = false;
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
delete_font(_font);
disposedValue = true;
}
}
~RGBLedFont()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}
}
@@ -0,0 +1,299 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace rpi_rgb_led_matrix_sharp
{
public class RGBLedMatrix : IDisposable
{
#region DLLImports
[DllImport("librgbmatrix.so")]
internal static extern IntPtr led_matrix_create(int rows, int chained, int parallel);
[DllImport("librgbmatrix.so", CallingConvention= CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern IntPtr led_matrix_create_from_options_const_argv(
ref InternalRGBLedMatrixOptions options,
int argc,
string[] argv);
[DllImport("librgbmatrix.so")]
internal static extern void led_matrix_delete(IntPtr matrix);
[DllImport("librgbmatrix.so")]
internal static extern IntPtr led_matrix_create_offscreen_canvas(IntPtr matrix);
[DllImport("librgbmatrix.so")]
internal static extern IntPtr led_matrix_swap_on_vsync(IntPtr matrix, IntPtr canvas);
[DllImport("librgbmatrix.so")]
internal static extern IntPtr led_matrix_get_canvas(IntPtr matrix);
[DllImport("librgbmatrix.so")]
internal static extern byte led_matrix_get_brightness(IntPtr matrix);
[DllImport("librgbmatrix.so")]
internal static extern void led_matrix_set_brightness(IntPtr matrix, byte brightness);
#endregion
public RGBLedMatrix(int rows, int chained, int parallel)
{
matrix= led_matrix_create(rows, chained, parallel);
}
public RGBLedMatrix(RGBLedMatrixOptions options)
{
var opt = new InternalRGBLedMatrixOptions();
try {
// pass in options to internal data structure
opt.chain_length = options.ChainLength;
opt.rows = options.Rows;
opt.cols = options.Cols;
opt.hardware_mapping = options.HardwareMapping != null ? Marshal.StringToHGlobalAnsi(options.HardwareMapping) : IntPtr.Zero;
opt.inverse_colors = (byte)(options.InverseColors ? 1 : 0);
opt.led_rgb_sequence = options.LedRgbSequence != null ? Marshal.StringToHGlobalAnsi(options.LedRgbSequence) : IntPtr.Zero;
opt.pixel_mapper_config = options.PixelMapperConfig != null ? Marshal.StringToHGlobalAnsi(options.PixelMapperConfig) : IntPtr.Zero;
opt.panel_type = options.PanelType != null ? Marshal.StringToHGlobalAnsi(options.PanelType) : IntPtr.Zero;
opt.parallel = options.Parallel;
opt.multiplexing = options.Multiplexing;
opt.pwm_bits = options.PwmBits;
opt.pwm_lsb_nanoseconds = options.PwmLsbNanoseconds;
opt.pwm_dither_bits = options.PwmDitherBits;
opt.scan_mode = options.ScanMode;
opt.show_refresh_rate = (byte)(options.ShowRefreshRate ? 1 : 0);
opt.limit_refresh_rate_hz = options.LimitRefreshRateHz;
opt.brightness = options.Brightness;
opt.disable_hardware_pulsing = (byte)(options.DisableHardwarePulsing ? 1 : 0);
opt.row_address_type = options.RowAddressType;
string[] cmdline_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], so this is why our slowdown_arg
// array will have these two elements.
//
// Given that we can't initialize the C# struct with a slowdown
// that is not 0, we just override it here with 1 if we see 0
// (zero only really is usable on super-slow vey old Rpi1,
// but for everyone else, it would be a nuisance. So we use
// 0 as our sentinel).
string[] slowdown_arg = new string[] {cmdline_args[0], "--led-slowdown-gpio="+(options.GpioSlowdown == 0 ? 1 : options.GpioSlowdown) };
string[] argv = new string[ 2 + cmdline_args.Length-1];
// Progname + slowdown arg first
slowdown_arg.CopyTo(argv, 0);
// Remaining args (excluding program name) then. This allows
// the user to not only provide any of the other --led-*
// options, but also override the --led-slowdown-gpio arg on
// the commandline.
Array.Copy(cmdline_args, 1, argv, 2, cmdline_args.Length-1);
int argc = argv.Length;
matrix = led_matrix_create_from_options_const_argv(ref opt, argc, argv);
}
finally
{
if (options.HardwareMapping != null) Marshal.FreeHGlobal(opt.hardware_mapping);
if (options.LedRgbSequence != null) Marshal.FreeHGlobal(opt.led_rgb_sequence);
if (options.PixelMapperConfig != null) Marshal.FreeHGlobal(opt.pixel_mapper_config);
if (options.PanelType != null) Marshal.FreeHGlobal(opt.panel_type);
}
}
private IntPtr matrix;
public RGBLedCanvas CreateOffscreenCanvas()
{
var canvas=led_matrix_create_offscreen_canvas(matrix);
return new RGBLedCanvas(canvas);
}
public RGBLedCanvas GetCanvas()
{
var canvas = led_matrix_get_canvas(matrix);
return new RGBLedCanvas(canvas);
}
public RGBLedCanvas SwapOnVsync(RGBLedCanvas canvas)
{
canvas._canvas = led_matrix_swap_on_vsync(matrix, canvas._canvas);
return canvas;
}
public byte Brightness
{
get { return led_matrix_get_brightness(matrix); }
set { led_matrix_set_brightness(matrix, value); }
}
#region IDisposable Support
private bool disposedValue = false;
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
led_matrix_delete(matrix);
disposedValue = true;
}
}
~RGBLedMatrix() {
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
#region RGBLedMatrixOptions struct
[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;
};
#endregion
}
public struct RGBLedMatrixOptions
{
/// <summary>
/// Name of the hardware mapping used. If passed NULL here, the default is used.
/// </summary>
public string HardwareMapping;
/// <summary>
/// The "rows" are the number of rows supported by the display, so 32 or 16.
/// Default: 32.
/// </summary>
public int Rows;
/// <summary>
/// The "cols" are the number of columns per panel. Typically something
/// like 32, but also 64 is possible. Sometimes even 40.
/// cols * chain_length 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;
/// <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;
/// <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 rows * parallel. Default: 1
/// </summary>
public int Parallel;
/// <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;
/// <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;
/// <summary>
/// The lower bits can be time-dithered for higher refresh rate.
/// </summary>
public int PwmDitherBits;
/// <summary>
/// The initial brightness of the panel in percent. Valid range is 1..100
/// </summary>
public int Brightness;
/// <summary>
/// Scan mode: 0=progressive, 1=interlaced
/// </summary>
public int ScanMode;
/// <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;
/// <summary>
/// Type of multiplexing. 0 = direct, 1 = stripe, 2 = checker (typical 1:8)
/// </summary>
public int Multiplexing;
/// <summary>
/// In case the internal sequence of mapping is not "RGB", this contains the real mapping. Some panels mix up these colors.
/// </summary>
public string LedRgbSequence;
/// <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;
/// <summary>
/// Panel type. Typically just empty, but certain panels (FM6126)
/// requie an initialization sequence
/// </summary>
public string PanelType;
/// <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;
public bool ShowRefreshRate;
public bool InverseColors;
/// <summary>
/// Limit refresh rate of LED panel. This will help on a loaded system
// to keep a constant refresh rate. <= 0 for no limit.
/// </summary>
public int LimitRefreshRateHz;
/// <summary>
/// Slowdown GPIO. Needed for faster Pis/slower panels.
/// </summary>
public int GpioSlowdown;
};
}
@@ -0,0 +1,41 @@
CSHARP_LIB=RGBLedMatrix.dll
CSHARP_COMPILER=mcs
CSHARP_LIBDIR=..
CSHARP_LIBRARY=$(CSHARP_LIBDIR)/$(CSHARP_LIB)
RGB_LIBDIR=../../../lib
RGB_LIBRARY_NAME=librgbmatrix
RGB_LIBRARY=$(RGB_LIBDIR)/$(RGB_LIBRARY_NAME).so.1
all: $(CSHARP_LIB)
cp $(RGB_LIBRARY) $(RGB_LIBRARY_NAME).so
cp $(CSHARP_LIBRARY) $(CSHARP_LIB)
$(CSHARP_COMPILER) -r:$(CSHARP_LIB) -out:minimal-example.exe minimal-example.cs
$(CSHARP_COMPILER) -r:$(CSHARP_LIB) -out:matrix-rain.exe matrix-rain.cs
$(CSHARP_COMPILER) -r:$(CSHARP_LIB) -out:font-example.exe font-example.cs
$(CSHARP_COMPILER) -r:$(CSHARP_LIB) -out:pulsing-brightness.exe pulsing-brightness.cs
minimal-example.exe: $(CSHARP_LIB)
cp $(RGB_LIBRARY) $(RGB_LIBRARY_NAME).so
cp $(CSHARP_LIBRARY) $(CSHARP_LIB)
$(CSHARP_COMPILER) -r:$(CSHARP_LIB) -out:minimal-example.exe minimal-example.cs
matrix-rain.exe: $(CSHARP_LIB)
cp $(RGB_LIBRARY) $(RGB_LIBRARY_NAME).so
cp $(CSHARP_LIBRARY) $(CSHARP_LIB)
$(CSHARP_COMPILER) -r:$(CSHARP_LIB) -out:matrix-rain.exe matrix-rain.cs
font-example.exe: $(CSHARP_LIB)
cp $(RGB_LIBRARY) $(RGB_LIBRARY_NAME).so
cp $(CSHARP_LIBRARY) $(CSHARP_LIB)
$(CSHARP_COMPILER) -r:$(CSHARP_LIB) -out:font-example.exe font-example.cs
pulsing-brightness.exe: $(CSHARP_LIB)
cp $(RGB_LIBRARY) $(RGB_LIBRARY_NAME).so
cp $(CSHARP_LIBRARY) $(CSHARP_LIB)
$(CSHARP_COMPILER) -r:$(CSHARP_LIB) -out:pulsing-brightness.exe pulsing-brightness.cs
$(CSHARP_LIB) :
$(MAKE) -C $(CSHARP_LIBDIR)
.PHONY : all
@@ -0,0 +1,36 @@
using rpi_rgb_led_matrix_sharp;
using System;
using System.Threading;
namespace font_example
{
class Program
{
static int Main(string[] args)
{
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];
var matrix = new RGBLedMatrix(32, 2, 1);
var canvas = matrix.CreateOffscreenCanvas();
var font = new RGBLedFont(args[0]);
canvas.DrawText(font, 1, 6, new Color(0, 255, 0), text);
matrix.SwapOnVsync(canvas);
while (!Console.KeyAvailable)
{
Thread.Sleep(250);
}
return 0;
}
}
}
@@ -0,0 +1,90 @@
using rpi_rgb_led_matrix_sharp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
namespace matrix_rain
{
class Program
{
const int MAX_HEIGHT = 16;
const int COLOR_STEP = 15;
const int FRAME_STEP = 1;
static int Main(string[] args)
{
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>();
int frame = 0;
var stopwatch = new Stopwatch();
while (!Console.KeyAvailable) {
stopwatch.Restart();
frame++;
if (frame % FRAME_STEP == 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)
{
point.y++;
if (point.y - MAX_HEIGHT > canvas.Height)
{
point.recycled = true;
recycled.Push(point);
}
for (var i=0; i< MAX_HEIGHT; i++)
{
canvas.SetPixel(point.x, point.y - i, new Color(0, 255 - i * COLOR_STEP, 0));
}
}
}
canvas = matrix.SwapOnVsync(canvas);
// force 30 FPS
var elapsed= stopwatch.ElapsedMilliseconds;
if (elapsed < 33)
{
Thread.Sleep(33 - (int)elapsed);
}
}
return 0;
}
class Point
{
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
public int x;
public int y;
public bool recycled;
}
}
}
@@ -0,0 +1,32 @@
using rpi_rgb_led_matrix_sharp;
namespace minimal_example
{
class Program
{
static int Main(string[] args)
{
var matrix= new RGBLedMatrix(32, 2, 1);
var canvas = matrix.CreateOffscreenCanvas();
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(canvas.Width / 2, canvas.Height / 2, 6, new Color(0, 0, 255));
canvas.DrawLine(canvas.Width / 2 - 3, canvas.Height / 2 - 3, canvas.Width / 2 + 3, canvas.Height / 2 + 3, new Color(0, 0, 255));
canvas.DrawLine(canvas.Width / 2 - 3, canvas.Height / 2 + 3, canvas.Width / 2 + 3, canvas.Height / 2 - 3, new Color(0, 0, 255));
canvas = matrix.SwapOnVsync(canvas);
}
return 0;
}
}
}
@@ -0,0 +1,54 @@
using rpi_rgb_led_matrix_sharp;
using System;
using System.Threading;
namespace pulsing_brightness
{
class Program
{
static int Main(string[] args)
{
var matrix = new RGBLedMatrix(new RGBLedMatrixOptions {Rows = 32, Cols = 64});
var canvas = matrix.CreateOffscreenCanvas();
var maxBrightness = matrix.Brightness;
var count = 0;
const int c = 255;
while (!Console.KeyAvailable)
{
if (matrix.Brightness < 1)
{
matrix.Brightness = maxBrightness;
count += 1;
}
else
{
matrix.Brightness -= 1;
}
switch (count % 4)
{
case 0:
canvas.Fill(new Color(c, 0, 0));
break;
case 1:
canvas.Fill(new Color(0, c, 0));
break;
case 2:
canvas.Fill(new Color(0, 0, c));
break;
case 3:
canvas.Fill(new Color(c, c, c));
break;
}
canvas = matrix.SwapOnVsync(canvas);
Thread.Sleep(20);
}
return 0;
}
}
}