From 1d4a31435a0aa6765f6459fba550e8942be6ce99 Mon Sep 17 00:00:00 2001 From: Ryan Everett Date: Fri, 13 Sep 2019 11:37:54 -0400 Subject: [PATCH 1/5] Update StripType definitions from rpi-ws281x/rpi-ws281x-powershell Adds definitions from PR 4. --- src/rpi_ws281x/StripType.cs | 62 ++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/src/rpi_ws281x/StripType.cs b/src/rpi_ws281x/StripType.cs index e895d9b..7c30de5 100644 --- a/src/rpi_ws281x/StripType.cs +++ b/src/rpi_ws281x/StripType.cs @@ -11,6 +11,66 @@ namespace rpi_ws281x public enum StripType { Unknown = 0, - WS2812_STRIP = 0x00081000 + WS2812_STRIP = 0x00081000, + + /// + /// SK6812_STRIP_RGBW + /// + SK6812_STRIP_RGBW = 0x18100800, + + /// + /// SK6812_STRIP_RBGW + /// + SK6812_STRIP_RBGW = 0x18100008, + + /// + /// SK6812_STRIP_GRBW + /// + SK6812_STRIP_GRBW = 0x18081000, + + /// + /// SK6812_STRIP_GBRW + /// + SK6812_STRIP_GBRW = 0x18080010, + + /// + /// SK6812_STRIP_BRGW + /// + SK6812_STRIP_BRGW = 0x18001008, + + /// + /// SK6812_STRIP_BGRW + /// + SK6812_STRIP_BGRW = 0x18000810, + + /// + /// WS2811_STRIP_RGB + /// + WS2811_STRIP_RGB = 0x00100800, + + /// + /// WS2811_STRIP_RBG + /// + WS2811_STRIP_RBG = 0x00100008, + + /// + /// WS2811_STRIP_GRB + /// + WS2811_STRIP_GRB = 0x00081000, + + /// + /// WS2811_STRIP_GBR + /// + WS2811_STRIP_GBR = 0x00080010, + + /// + /// WS2811_STRIP_BRG + /// + WS2811_STRIP_BRG = 0x00001008, + + /// + /// WS2811_STRIP_BGR + /// + WS2811_STRIP_BGR = 0x00000810, } } From 178b74b832e763f5f50909913adc0e1a7a184c7a Mon Sep 17 00:00:00 2001 From: Ryan Everett Date: Fri, 13 Sep 2019 11:51:43 -0400 Subject: [PATCH 2/5] Update Native and WS281x from rpi-ws281x/rpi-ws281x-powershell Changes definitions per PR 4 to address #2. --- src/rpi_ws281x/Native/PInvoke.cs | 13 ++++++++---- src/rpi_ws281x/Native/ws2811_t.cs | 7 +++++-- src/rpi_ws281x/WS281x.cs | 34 ++++++++++++++++--------------- 3 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/rpi_ws281x/Native/PInvoke.cs b/src/rpi_ws281x/Native/PInvoke.cs index f865429..040fe8b 100644 --- a/src/rpi_ws281x/Native/PInvoke.cs +++ b/src/rpi_ws281x/Native/PInvoke.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; namespace Native @@ -7,17 +8,21 @@ internal class PInvoke { public const int RPI_PWM_CHANNELS = 2; + [SuppressMessage("IDE1006", "IDE1006", Justification = "Native methods have different naming conventions.")] [DllImport("ws2811.so")] - public static extern ws2811_return_t ws2811_init(ref ws2811_t ws2811); + public static extern ws2811_return_t ws2811_init(IntPtr ws2811); + [SuppressMessage("IDE1006", "IDE1006", Justification = "Native methods have different naming conventions.")] [DllImport("ws2811.so")] - public static extern ws2811_return_t ws2811_render(ref ws2811_t ws2811); + public static extern ws2811_return_t ws2811_render(IntPtr ws2811); + [SuppressMessage("IDE1006", "IDE1006", Justification = "Native methods have different naming conventions.")] [DllImport("ws2811.so")] - public static extern ws2811_return_t ws2811_wait(ref ws2811_t ws2811); + public static extern ws2811_return_t ws2811_wait(IntPtr ws2811); + [SuppressMessage("IDE1006", "IDE1006", Justification = "Native methods have different naming conventions.")] [DllImport("ws2811.so")] - public static extern void ws2811_fini(ref ws2811_t ws2811); + public static extern void ws2811_fini(IntPtr ws2811); [DllImport("ws2811.so")] public static extern IntPtr ws2811_get_return_t_str(int state); diff --git a/src/rpi_ws281x/Native/ws2811_t.cs b/src/rpi_ws281x/Native/ws2811_t.cs index 7ba56bf..684da76 100644 --- a/src/rpi_ws281x/Native/ws2811_t.cs +++ b/src/rpi_ws281x/Native/ws2811_t.cs @@ -3,6 +3,7 @@ namespace Native { + [SuppressMessage("IDE1006", "IDE1006", Justification = "Native methods have different naming conventions.")] [StructLayout(LayoutKind.Sequential)] internal struct ws2811_t { @@ -11,7 +12,9 @@ internal struct ws2811_t public IntPtr rpi_hw; public uint freq; public int dmanum; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = PInvoke.RPI_PWM_CHANNELS)] - public ws2811_channel_t[] channel; + /* [MarshalAs(UnmanagedType.ByValArray, SizeConst = PInvoke.RPI_PWM_CHANNELS)] + public ws2811_channel_t[] channel; */ + public ws2811_channel_t channel_1; + public ws2811_channel_t channel_2; } } diff --git a/src/rpi_ws281x/WS281x.cs b/src/rpi_ws281x/WS281x.cs index dc167e6..bd4f3d8 100644 --- a/src/rpi_ws281x/WS281x.cs +++ b/src/rpi_ws281x/WS281x.cs @@ -28,7 +28,9 @@ public WS281x(Settings settings) _ws2811.dmanum = settings.DMAChannel; _ws2811.freq = settings.Frequency; - _ws2811.channel = new ws2811_channel_t[PInvoke.RPI_PWM_CHANNELS]; + //_ws2811.channel = new ws2811_channel_t[PInvoke.RPI_PWM_CHANNELS]; + _ws2811.channel_1 = default; + InitChannel(ref _ws2811.channel_1, settings.Channel); for(int i=0; i<= _ws2811.channel.Length -1; i++) { @@ -40,7 +42,7 @@ public WS281x(Settings settings) Settings = settings; - var initResult = PInvoke.ws2811_init(ref _ws2811); + var initResult = PInvoke.ws2811_init(_ws2811Handle.AddrOfPinnedObject()); if (initResult != ws2811_return_t.WS2811_SUCCESS) { var returnMessage = GetMessageForStatusCode(initResult); @@ -62,11 +64,11 @@ public void Render() if (Settings.Channels[i] != null) { var ledColor = Settings.Channels[i].LEDs.Select(x => x.RGBValue).ToArray(); - Marshal.Copy(ledColor, 0, _ws2811.channel[i].leds, ledColor.Count()); + Marshal.Copy(ledColor, 0, _ws2811.channel_1.leds, ledColor.Count()); } } - var result = PInvoke.ws2811_render(ref _ws2811); + var result = PInvoke.ws2811_render(_ws2811Handle.AddrOfPinnedObject()); if (result != ws2811_return_t.WS2811_SUCCESS) { var returnMessage = GetMessageForStatusCode(result); @@ -93,20 +95,20 @@ public void SetLEDColor(int channelIndex, int ledID, Color color) /// /// Initialize the channel propierties /// - /// Index of the channel tu initialize + /// Channel to initialize /// Settings for the channel - private void InitChannel(int channelIndex, Channel channelSettings) + private void InitChannel(ref ws2811_channel_t channel, Channel channelSettings) { - _ws2811.channel[channelIndex].count = channelSettings.LEDs.Count; - _ws2811.channel[channelIndex].gpionum = channelSettings.GPIOPin; - _ws2811.channel[channelIndex].brightness = channelSettings.Brightness; - _ws2811.channel[channelIndex].invert = Convert.ToInt32(channelSettings.Invert); + channel.count = channelSettings.LEDs.Count; + channel.gpionum = channelSettings.GPIOPin; + channel.brightness = channelSettings.Brightness; + channel.invert = Convert.ToInt32(channelSettings.Invert); if(channelSettings.StripType != StripType.Unknown) { //Strip type is set by the native assembly if not explicitly set. //This type defines the ordering of the colors e. g. RGB or GRB, ... - _ws2811.channel[channelIndex].strip_type = (int)channelSettings.StripType; + channel.strip_type = (int)channelSettings.StripType; } } @@ -138,9 +140,10 @@ protected virtual void Dispose(bool disposing) if(_isDisposingAllowed) { - PInvoke.ws2811_fini(ref _ws2811); - _ws2811Handle.Free(); - + PInvoke.ws2811_fini(_ws2811Handle.AddrOfPinnedObject()); + if (_ws2811Handle.IsAllocated) { + _ws2811Handle.Free(); + } _isDisposingAllowed = false; } @@ -160,8 +163,7 @@ public void Dispose() { // Do not change this code. Put cleanup code in Dispose(bool disposing) above. Dispose(true); - // TODO: uncomment the following line if the finalizer is overridden above. - // GC.SuppressFinalize(this); + GC.SuppressFinalize(this); } #endregion } From 1fd2504e17adc1c5e2f4bc002260778d02235894 Mon Sep 17 00:00:00 2001 From: Ryan Everett Date: Mon, 7 Oct 2019 16:34:25 -0400 Subject: [PATCH 3/5] Confirmed working on dotnet core --- src/rpi-ws281x-dotnet.sln | 25 +++++++ src/rpi_ws281x/Channel.cs | 47 ++++++------- src/rpi_ws281x/LED.cs | 2 +- src/rpi_ws281x/Native/PInvoke.cs | 9 ++- src/rpi_ws281x/Native/ws2811_channel_t.cs | 4 +- src/rpi_ws281x/Native/ws2811_return_t.cs | 7 +- src/rpi_ws281x/Native/ws2811_t.cs | 7 +- src/rpi_ws281x/Settings.cs | 34 ++++----- src/rpi_ws281x/StripType.cs | 12 ++-- src/rpi_ws281x/WS281x.cs | 84 ++++++++++------------- src/rpi_ws281x/rpi-ws281x-dotnet.csproj | 9 +++ 11 files changed, 128 insertions(+), 112 deletions(-) create mode 100644 src/rpi-ws281x-dotnet.sln create mode 100644 src/rpi_ws281x/rpi-ws281x-dotnet.csproj diff --git a/src/rpi-ws281x-dotnet.sln b/src/rpi-ws281x-dotnet.sln new file mode 100644 index 0000000..c9c0a84 --- /dev/null +++ b/src/rpi-ws281x-dotnet.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.757 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "rpi-ws281x-dotnet", "rpi_ws281x\rpi-ws281x-dotnet.csproj", "{5432FD49-28E2-40CC-AAE8-E340503981ED}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5432FD49-28E2-40CC-AAE8-E340503981ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5432FD49-28E2-40CC-AAE8-E340503981ED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5432FD49-28E2-40CC-AAE8-E340503981ED}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5432FD49-28E2-40CC-AAE8-E340503981ED}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {05598D32-E9AC-47E9-8CC6-D5EED433D349} + EndGlobalSection +EndGlobal diff --git a/src/rpi_ws281x/Channel.cs b/src/rpi_ws281x/Channel.cs index ade7f4b..e205c6c 100644 --- a/src/rpi_ws281x/Channel.cs +++ b/src/rpi_ws281x/Channel.cs @@ -1,32 +1,13 @@ using System.Collections.Generic; using System.Collections.ObjectModel; -namespace rpi_ws281x +namespace WS281x { /// /// Represents the channel which holds the LEDs /// public class Channel { - public Channel() : this(0, 0) { } - - public Channel(int ledCount, int gpioPin) : this(ledCount, gpioPin, 255, false, rpi_ws281x.StripType.Unknown) { } - - public Channel(int ledCount, int gpioPin, byte brightness, bool invert, StripType stripType) - { - GPIOPin = gpioPin; - Invert = invert; - Brightness = brightness; - StripType = stripType; - - var ledList = new List(); - for(int i= 0; i<= ledCount-1; i++) - { - ledList.Add(new LED(i)); - } - - LEDs = new ReadOnlyCollection(ledList); - } /// /// Returns the GPIO pin which is connected to the LED strip @@ -41,7 +22,7 @@ public Channel(int ledCount, int gpioPin, byte brightness, bool invert, StripTy /// /// Gets or sets the brightness of the LEDs - /// 0 = darkes, 255 = brightest + /// 0 = darkest, 255 = brightest /// public byte Brightness { get; set; } @@ -54,9 +35,29 @@ public Channel(int ledCount, int gpioPin, byte brightness, bool invert, StripTy /// /// Returns all LEDs on this channel /// - public ReadOnlyCollection LEDs { get; private set; } + public List Leds { get; private set; } - public int LEDCount { get => LEDs.Count; } + public int LEDCount { get => Leds.Count; } + + //public Channel() : this(0, 0) { } + public Channel(int ledCount, int gpioPin) : this(ledCount, gpioPin, 255, false, StripType.Unknown) { } + + public Channel(int ledCount, int gpioPin, byte brightness, bool invert, StripType stripType) + { + GPIOPin = gpioPin; + Invert = invert; + Brightness = brightness; + StripType = stripType; + + Leds = new List(); + for(int i= 0; i< ledCount; i++) + { + Leds.Add(new LED(i)); + } + + //LEDs = new ReadOnlyCollection(ledList); + } + } } diff --git a/src/rpi_ws281x/LED.cs b/src/rpi_ws281x/LED.cs index f480e5c..e879fc8 100644 --- a/src/rpi_ws281x/LED.cs +++ b/src/rpi_ws281x/LED.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace rpi_ws281x +namespace WS281x { /// /// Represents a LED which can be controlled by the WS281x controller diff --git a/src/rpi_ws281x/Native/PInvoke.cs b/src/rpi_ws281x/Native/PInvoke.cs index 040fe8b..2eaa2fe 100644 --- a/src/rpi_ws281x/Native/PInvoke.cs +++ b/src/rpi_ws281x/Native/PInvoke.cs @@ -2,16 +2,14 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -namespace Native +namespace WS281x.Native { internal class PInvoke { - public const int RPI_PWM_CHANNELS = 2; - [SuppressMessage("IDE1006", "IDE1006", Justification = "Native methods have different naming conventions.")] [DllImport("ws2811.so")] public static extern ws2811_return_t ws2811_init(IntPtr ws2811); - + [SuppressMessage("IDE1006", "IDE1006", Justification = "Native methods have different naming conventions.")] [DllImport("ws2811.so")] public static extern ws2811_return_t ws2811_render(IntPtr ws2811); @@ -19,11 +17,12 @@ internal class PInvoke [SuppressMessage("IDE1006", "IDE1006", Justification = "Native methods have different naming conventions.")] [DllImport("ws2811.so")] public static extern ws2811_return_t ws2811_wait(IntPtr ws2811); - + [SuppressMessage("IDE1006", "IDE1006", Justification = "Native methods have different naming conventions.")] [DllImport("ws2811.so")] public static extern void ws2811_fini(IntPtr ws2811); + [SuppressMessage("IDE1006", "IDE1006", Justification = "Native methods have different naming conventions.")] [DllImport("ws2811.so")] public static extern IntPtr ws2811_get_return_t_str(int state); } diff --git a/src/rpi_ws281x/Native/ws2811_channel_t.cs b/src/rpi_ws281x/Native/ws2811_channel_t.cs index 35ba97d..845d452 100644 --- a/src/rpi_ws281x/Native/ws2811_channel_t.cs +++ b/src/rpi_ws281x/Native/ws2811_channel_t.cs @@ -1,13 +1,15 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; -namespace Native +namespace WS281x.Native { [StructLayout(LayoutKind.Sequential)] + [SuppressMessage("IDE1006", "IDE1006", Justification = "Native methods have different naming conventions.")] internal struct ws2811_channel_t { public int gpionum; diff --git a/src/rpi_ws281x/Native/ws2811_return_t.cs b/src/rpi_ws281x/Native/ws2811_return_t.cs index fb65ac7..b1d1b61 100644 --- a/src/rpi_ws281x/Native/ws2811_return_t.cs +++ b/src/rpi_ws281x/Native/ws2811_return_t.cs @@ -1,5 +1,8 @@ -namespace Native +using System.Diagnostics.CodeAnalysis; + +namespace WS281x.Native { + [SuppressMessage("IDE1006", "IDE1006", Justification = "Native methods have different naming conventions.")] internal enum ws2811_return_t { WS2811_SUCCESS = 0, @@ -18,4 +21,4 @@ internal enum ws2811_return_t WS2811_ERROR_SPI_SETUP = -13, WS2811_ERROR_SPI_TRANSFER = -14 } -} \ No newline at end of file +} diff --git a/src/rpi_ws281x/Native/ws2811_t.cs b/src/rpi_ws281x/Native/ws2811_t.cs index 684da76..2d52e53 100644 --- a/src/rpi_ws281x/Native/ws2811_t.cs +++ b/src/rpi_ws281x/Native/ws2811_t.cs @@ -1,19 +1,18 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -namespace Native +namespace WS281x.Native { [SuppressMessage("IDE1006", "IDE1006", Justification = "Native methods have different naming conventions.")] [StructLayout(LayoutKind.Sequential)] - internal struct ws2811_t + internal class ws2811_t { public long render_wait_time; public IntPtr device; public IntPtr rpi_hw; public uint freq; public int dmanum; - /* [MarshalAs(UnmanagedType.ByValArray, SizeConst = PInvoke.RPI_PWM_CHANNELS)] - public ws2811_channel_t[] channel; */ public ws2811_channel_t channel_1; public ws2811_channel_t channel_2; } diff --git a/src/rpi_ws281x/Settings.cs b/src/rpi_ws281x/Settings.cs index e295741..a934e24 100644 --- a/src/rpi_ws281x/Settings.cs +++ b/src/rpi_ws281x/Settings.cs @@ -1,35 +1,25 @@ -using Native; -using System.Collections.Generic; +using System.Collections.Generic; using System.Collections.ObjectModel; +using WS281x.Native; -namespace rpi_ws281x +namespace WS281x { /// /// Settings which are required to initialize the WS281x controller /// public class Settings { - - /// - /// Settings to initialize the WS281x controller - /// - /// Set frequency in Hz - /// Set DMA channel to use - public Settings(uint frequency, int dmaChannel) + + /// + /// Settings to initialize the WS281x controller + /// + /// Set frequency in Hz + /// Set DMA channel to use + public Settings(Channel channel, uint frequency = 800000, int dmaChannel = 10) { + Channel = channel; Frequency = frequency; DMAChannel = dmaChannel; - Channels = new Channel[PInvoke.RPI_PWM_CHANNELS]; - } - - /// - /// Returns default settings. - /// Use a frequency of 800000 Hz and DMA channel 10 - /// - /// - public static Settings CreateDefaultSettings() - { - return new Settings(800000, 10); } /// @@ -45,6 +35,6 @@ public static Settings CreateDefaultSettings() /// /// Returns the channels which holds the LEDs /// - public Channel[] Channels { get; private set; } + public Channel Channel { get; set; } } } diff --git a/src/rpi_ws281x/StripType.cs b/src/rpi_ws281x/StripType.cs index 7c30de5..80dea68 100644 --- a/src/rpi_ws281x/StripType.cs +++ b/src/rpi_ws281x/StripType.cs @@ -2,17 +2,19 @@ using System.Collections.Generic; using System.Text; -namespace rpi_ws281x +namespace WS281x { /// /// The type of the LED strip defines the ordering of the colors (e. g. RGB, GRB, ...). /// Maybe the RGBValue property of the LED class needs to be changed if there are other strip types. /// - public enum StripType - { + public enum StripType + { + /// + /// Unknown / unset. + /// Unknown = 0, - WS2812_STRIP = 0x00081000, - + /// /// SK6812_STRIP_RGBW /// diff --git a/src/rpi_ws281x/WS281x.cs b/src/rpi_ws281x/WS281x.cs index bd4f3d8..aa6b46a 100644 --- a/src/rpi_ws281x/WS281x.cs +++ b/src/rpi_ws281x/WS281x.cs @@ -1,10 +1,10 @@ -using Native; -using System; +using System; using System.Drawing; using System.Linq; using System.Runtime.InteropServices; +using WS281x.Native; -namespace rpi_ws281x +namespace WS281x { /// /// Wrapper class to controll WS281x LEDs @@ -22,35 +22,24 @@ public class WS281x : IDisposable public WS281x(Settings settings) { _ws2811 = new ws2811_t(); - //Pin the object in memory. Otherwies GC will probably move the object to another memory location. - //This would cause errors because the native library has a pointer on the memory location of the object. _ws2811Handle = GCHandle.Alloc(_ws2811, GCHandleType.Pinned); _ws2811.dmanum = settings.DMAChannel; _ws2811.freq = settings.Frequency; - //_ws2811.channel = new ws2811_channel_t[PInvoke.RPI_PWM_CHANNELS]; - _ws2811.channel_1 = default; + _ws2811.channel_1 = default(ws2811_channel_t); InitChannel(ref _ws2811.channel_1, settings.Channel); - for(int i=0; i<= _ws2811.channel.Length -1; i++) - { - if(settings.Channels[i] != null) - { - InitChannel(i, settings.Channels[i]); - } - } - Settings = settings; var initResult = PInvoke.ws2811_init(_ws2811Handle.AddrOfPinnedObject()); if (initResult != ws2811_return_t.WS2811_SUCCESS) { var returnMessage = GetMessageForStatusCode(initResult); - throw new Exception(String.Format("Error while initializing.{0}Error code: {1}{0}Message: {2}", Environment.NewLine, initResult.ToString(), returnMessage)); - } + throw new Exception($"Error while initializing.{Environment.NewLine}Error code: {initResult.ToString()}{Environment.NewLine}Message: {returnMessage}"); + } //Disposing is only allowed if the init was successfull. - //Otherwise the native cleanup function throws an error. + //Otherwise the native cleanUp function throws an error. _isDisposingAllowed = true; } @@ -59,20 +48,14 @@ public WS281x(Settings settings) /// public void Render() { - for(int i=0; i<= Settings.Channels.Length -1; i++) - { - if (Settings.Channels[i] != null) - { - var ledColor = Settings.Channels[i].LEDs.Select(x => x.RGBValue).ToArray(); - Marshal.Copy(ledColor, 0, _ws2811.channel_1.leds, ledColor.Count()); - } - } - + var ledColor = Settings.Channel.Leds.Select(x => x.RGBValue).ToArray(); + Marshal.Copy(ledColor, 0, _ws2811.channel_1.leds, ledColor.Count()); + var result = PInvoke.ws2811_render(_ws2811Handle.AddrOfPinnedObject()); if (result != ws2811_return_t.WS2811_SUCCESS) { var returnMessage = GetMessageForStatusCode(result); - throw new Exception(String.Format("Error while rendering.{0}Error code: {1}{0}Message: {2}", Environment.NewLine, result.ToString(), returnMessage)); + throw new Exception($"Error while rendering.{Environment.NewLine}Error code: {result.ToString()}{Environment.NewLine}Message: {returnMessage}"); } } @@ -82,9 +65,17 @@ public void Render() /// Channel which controls the LED /// ID/Index of the LED /// New color - public void SetLEDColor(int channelIndex, int ledID, Color color) + public void SetLEDColor(int ledID, Color color) { - Settings.Channels[channelIndex].LEDs[ledID].Color = color; + Settings.Channel.Leds[ledID].Color = color; + } + + public void SetColorOnAllLEDs(Color color) + { + for(int i = 0 ; i < Settings.Channel.Leds.Count ; ++i) + { + Settings.Channel.Leds[i].Color = color; + } } /// @@ -93,21 +84,19 @@ public void SetLEDColor(int channelIndex, int ledID, Color color) public Settings Settings { get; private set; } /// - /// Initialize the channel propierties + /// Initialize the channel properties. /// - /// Channel to initialize - /// Settings for the channel + /// Channel to initialize. + /// Settings for the channel. private void InitChannel(ref ws2811_channel_t channel, Channel channelSettings) { - channel.count = channelSettings.LEDs.Count; - channel.gpionum = channelSettings.GPIOPin; - channel.brightness = channelSettings.Brightness; - channel.invert = Convert.ToInt32(channelSettings.Invert); + channel.count = channelSettings.LEDCount; + channel.gpionum = channelSettings.GPIOPin; + channel.brightness = channelSettings.Brightness; + channel.invert = Convert.ToInt32(channelSettings.Invert); - if(channelSettings.StripType != StripType.Unknown) + if (channelSettings.StripType != StripType.Unknown) { - //Strip type is set by the native assembly if not explicitly set. - //This type defines the ordering of the colors e. g. RGB or GRB, ... channel.strip_type = (int)channelSettings.StripType; } } @@ -123,7 +112,6 @@ private string GetMessageForStatusCode(ws2811_return_t statusCode) return Marshal.PtrToStringAuto(strPointer); } - #region IDisposable Support private bool disposedValue = false; // To detect redundant calls protected virtual void Dispose(bool disposing) @@ -141,30 +129,28 @@ protected virtual void Dispose(bool disposing) if(_isDisposingAllowed) { PInvoke.ws2811_fini(_ws2811Handle.AddrOfPinnedObject()); - if (_ws2811Handle.IsAllocated) { + if(_ws2811Handle.IsAllocated) + { _ws2811Handle.Free(); - } + } + _isDisposingAllowed = false; } - + disposedValue = true; } } - // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. ~WS281x() { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. Dispose(false); } - // This code added to correctly implement the disposable pattern. public void Dispose() { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. Dispose(true); GC.SuppressFinalize(this); } - #endregion } } diff --git a/src/rpi_ws281x/rpi-ws281x-dotnet.csproj b/src/rpi_ws281x/rpi-ws281x-dotnet.csproj new file mode 100644 index 0000000..e2ca10b --- /dev/null +++ b/src/rpi_ws281x/rpi-ws281x-dotnet.csproj @@ -0,0 +1,9 @@ + + + + netcoreapp2.2 + rpi_ws281x_dotnet + 7.1 + + + From 963b929aef059900add01de1ee79aaebb96cc2a4 Mon Sep 17 00:00:00 2001 From: Ryan Everett Date: Mon, 7 Oct 2019 18:47:24 -0400 Subject: [PATCH 4/5] Fix multichannel support --- src/rpi_ws281x/Channel.cs | 66 +++++++++++------------ src/rpi_ws281x/LED.cs | 2 +- src/rpi_ws281x/Native/PInvoke.cs | 2 +- src/rpi_ws281x/Native/ws2811_channel_t.cs | 2 +- src/rpi_ws281x/Native/ws2811_return_t.cs | 2 +- src/rpi_ws281x/Native/ws2811_t.cs | 2 +- src/rpi_ws281x/Settings.cs | 63 ++++++++++++++++------ src/rpi_ws281x/StripType.cs | 6 ++- src/rpi_ws281x/WS281x.cs | 57 ++++++++++++-------- src/rpi_ws281x/rpi-ws281x-dotnet.csproj | 2 +- 10 files changed, 124 insertions(+), 80 deletions(-) diff --git a/src/rpi_ws281x/Channel.cs b/src/rpi_ws281x/Channel.cs index e205c6c..4e43109 100644 --- a/src/rpi_ws281x/Channel.cs +++ b/src/rpi_ws281x/Channel.cs @@ -1,18 +1,37 @@ using System.Collections.Generic; using System.Collections.ObjectModel; -namespace WS281x +namespace rpi_ws281x { /// /// Represents the channel which holds the LEDs /// public class Channel { - - /// - /// Returns the GPIO pin which is connected to the LED strip - /// - public int GPIOPin { get; private set; } + public Channel() : this(0, 0) { } + + public Channel(int ledCount, int gpioPin) : this(ledCount, gpioPin, 255, false, StripType.Unknown) { } + + public Channel(int ledCount, int gpioPin, byte brightness, bool invert, StripType stripType) + { + GPIOPin = gpioPin; + Invert = invert; + Brightness = brightness; + StripType = stripType; + + var ledList = new List(); + for (int i = 0; i <= ledCount - 1; i++) + { + ledList.Add(new LED(i)); + } + + LEDs = new ReadOnlyCollection(ledList); + } + + /// + /// Returns the GPIO pin which is connected to the LED strip + /// + public int GPIOPin { get; private set; } /// /// Returns a value which indicates if the signal needs to be inverted. @@ -32,32 +51,11 @@ public class Channel /// public StripType StripType { get; private set; } - /// - /// Returns all LEDs on this channel - /// - public List Leds { get; private set; } - - public int LEDCount { get => Leds.Count; } - - //public Channel() : this(0, 0) { } - - public Channel(int ledCount, int gpioPin) : this(ledCount, gpioPin, 255, false, StripType.Unknown) { } - - public Channel(int ledCount, int gpioPin, byte brightness, bool invert, StripType stripType) - { - GPIOPin = gpioPin; - Invert = invert; - Brightness = brightness; - StripType = stripType; - - Leds = new List(); - for(int i= 0; i< ledCount; i++) - { - Leds.Add(new LED(i)); - } - - //LEDs = new ReadOnlyCollection(ledList); - } - - } + /// + /// Returns all LEDs on this channel + /// + public ReadOnlyCollection LEDs { get; private set; } + + public int LEDCount { get => LEDs.Count; } + } } diff --git a/src/rpi_ws281x/LED.cs b/src/rpi_ws281x/LED.cs index e879fc8..f480e5c 100644 --- a/src/rpi_ws281x/LED.cs +++ b/src/rpi_ws281x/LED.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace WS281x +namespace rpi_ws281x { /// /// Represents a LED which can be controlled by the WS281x controller diff --git a/src/rpi_ws281x/Native/PInvoke.cs b/src/rpi_ws281x/Native/PInvoke.cs index 2eaa2fe..9ae521d 100644 --- a/src/rpi_ws281x/Native/PInvoke.cs +++ b/src/rpi_ws281x/Native/PInvoke.cs @@ -2,7 +2,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -namespace WS281x.Native +namespace rpi_ws281x.Native { internal class PInvoke { diff --git a/src/rpi_ws281x/Native/ws2811_channel_t.cs b/src/rpi_ws281x/Native/ws2811_channel_t.cs index 845d452..5d3be16 100644 --- a/src/rpi_ws281x/Native/ws2811_channel_t.cs +++ b/src/rpi_ws281x/Native/ws2811_channel_t.cs @@ -6,7 +6,7 @@ using System.Text; using System.Threading.Tasks; -namespace WS281x.Native +namespace rpi_ws281x.Native { [StructLayout(LayoutKind.Sequential)] [SuppressMessage("IDE1006", "IDE1006", Justification = "Native methods have different naming conventions.")] diff --git a/src/rpi_ws281x/Native/ws2811_return_t.cs b/src/rpi_ws281x/Native/ws2811_return_t.cs index b1d1b61..2f8d8ca 100644 --- a/src/rpi_ws281x/Native/ws2811_return_t.cs +++ b/src/rpi_ws281x/Native/ws2811_return_t.cs @@ -1,6 +1,6 @@ using System.Diagnostics.CodeAnalysis; -namespace WS281x.Native +namespace rpi_ws281x.Native { [SuppressMessage("IDE1006", "IDE1006", Justification = "Native methods have different naming conventions.")] internal enum ws2811_return_t diff --git a/src/rpi_ws281x/Native/ws2811_t.cs b/src/rpi_ws281x/Native/ws2811_t.cs index 2d52e53..76d5926 100644 --- a/src/rpi_ws281x/Native/ws2811_t.cs +++ b/src/rpi_ws281x/Native/ws2811_t.cs @@ -2,7 +2,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -namespace WS281x.Native +namespace rpi_ws281x.Native { [SuppressMessage("IDE1006", "IDE1006", Justification = "Native methods have different naming conventions.")] [StructLayout(LayoutKind.Sequential)] diff --git a/src/rpi_ws281x/Settings.cs b/src/rpi_ws281x/Settings.cs index a934e24..d2051c2 100644 --- a/src/rpi_ws281x/Settings.cs +++ b/src/rpi_ws281x/Settings.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; using System.Collections.ObjectModel; -using WS281x.Native; +using rpi_ws281x.Native; -namespace WS281x +namespace rpi_ws281x { /// /// Settings which are required to initialize the WS281x controller @@ -11,30 +11,63 @@ public class Settings { /// - /// Settings to initialize the WS281x controller + /// Settings to initialize the WS281x controller with one channel /// /// Set frequency in Hz /// Set DMA channel to use - public Settings(Channel channel, uint frequency = 800000, int dmaChannel = 10) - { - Channel = channel; - Frequency = frequency; - DMAChannel = dmaChannel; - } + public Settings(Channel channel, uint frequency = 800000, int dmaChannel = 10) : this (channel, null, frequency, dmaChannel) { } - /// - /// Returns the used frequency in Hz + /// + /// Settings to initialize the WS281x controller with up to two channels + /// + /// Set frequency in Hz + /// Set DMA channel to use + public Settings(Channel channel1, Channel channel2, uint frequency = 800000, int dmaChannel = 10) + { + Channel_1 = channel1; + if (channel2 == null) + ChannelCount = 1; + else { + Channel_2 = channel2; + ChannelCount = 2; + } + Frequency = frequency; + DMAChannel = dmaChannel; + } + + /// + /// Returns default settings. + /// Use a frequency of 800000 Hz and DMA channel 10 /// - public uint Frequency { get; private set; } + /// + public static Settings CreateDefaultSettings() + { + return new Settings(null, 800000, 10); + } + + /// + /// Returns the used frequency in Hz + /// + public uint Frequency { get; private set; } /// /// Returns the DMA channel /// public int DMAChannel { get; private set; } + /// + /// Returns the number of channels being used + /// + public int ChannelCount { get; private set; } + /// - /// Returns the channels which holds the LEDs + /// Returns Channel 1 + /// + public Channel Channel_1 { get; set; } + + /// + /// Returns Channel 1 /// - public Channel Channel { get; set; } - } + public Channel Channel_2 { get; set; } + } } diff --git a/src/rpi_ws281x/StripType.cs b/src/rpi_ws281x/StripType.cs index 80dea68..17f2213 100644 --- a/src/rpi_ws281x/StripType.cs +++ b/src/rpi_ws281x/StripType.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace WS281x +namespace rpi_ws281x { /// /// The type of the LED strip defines the ordering of the colors (e. g. RGB, GRB, ...). @@ -74,5 +74,7 @@ public enum StripType /// WS2811_STRIP_BGR /// WS2811_STRIP_BGR = 0x00000810, - } + + WS2812_STRIP = 0x00081000 + } } diff --git a/src/rpi_ws281x/WS281x.cs b/src/rpi_ws281x/WS281x.cs index aa6b46a..dd619fa 100644 --- a/src/rpi_ws281x/WS281x.cs +++ b/src/rpi_ws281x/WS281x.cs @@ -2,9 +2,9 @@ using System.Drawing; using System.Linq; using System.Runtime.InteropServices; -using WS281x.Native; +using rpi_ws281x.Native; -namespace WS281x +namespace rpi_ws281x { /// /// Wrapper class to controll WS281x LEDs @@ -27,9 +27,13 @@ public WS281x(Settings settings) _ws2811.dmanum = settings.DMAChannel; _ws2811.freq = settings.Frequency; _ws2811.channel_1 = default(ws2811_channel_t); - InitChannel(ref _ws2811.channel_1, settings.Channel); + _ws2811.channel_2 = default(ws2811_channel_t); + if (settings.Channel_1 != null) + InitChannel(ref _ws2811.channel_1, settings.Channel_1); + if (settings.Channel_2 != null) + InitChannel(ref _ws2811.channel_2, settings.Channel_2); - Settings = settings; + Settings = settings; var initResult = PInvoke.ws2811_init(_ws2811Handle.AddrOfPinnedObject()); if (initResult != ws2811_return_t.WS2811_SUCCESS) @@ -48,10 +52,18 @@ public WS281x(Settings settings) /// public void Render() { - var ledColor = Settings.Channel.Leds.Select(x => x.RGBValue).ToArray(); - Marshal.Copy(ledColor, 0, _ws2811.channel_1.leds, ledColor.Count()); - - var result = PInvoke.ws2811_render(_ws2811Handle.AddrOfPinnedObject()); + if (Settings.Channel_1 != null) { + var ledColor = Settings.Channel_1.LEDs.Select(x => x.RGBValue).ToArray(); + Marshal.Copy(ledColor, 0, _ws2811.channel_1.leds, ledColor.Count()); + } + if (Settings.Channel_2 != null) + { + var ledColor = Settings.Channel_2.LEDs.Select(x => x.RGBValue).ToArray(); + Marshal.Copy(ledColor, 0, _ws2811.channel_2.leds, ledColor.Count()); + } + + + var result = PInvoke.ws2811_render(_ws2811Handle.AddrOfPinnedObject()); if (result != ws2811_return_t.WS2811_SUCCESS) { var returnMessage = GetMessageForStatusCode(result); @@ -65,18 +77,13 @@ public void Render() /// Channel which controls the LED /// ID/Index of the LED /// New color - public void SetLEDColor(int ledID, Color color) - { - Settings.Channel.Leds[ledID].Color = color; - } - - public void SetColorOnAllLEDs(Color color) + public void SetLEDColor(int channelIndex, int ledID, Color color) { - for(int i = 0 ; i < Settings.Channel.Leds.Count ; ++i) - { - Settings.Channel.Leds[i].Color = color; - } - } + if (channelIndex == 0) + Settings.Channel_1.LEDs[ledID].Color = color; + else if (channelIndex == 1) + Settings.Channel_2.LEDs[ledID].Color = color; + } /// /// Returns the settings which are used to initialize the component @@ -97,7 +104,9 @@ private void InitChannel(ref ws2811_channel_t channel, Channel channelSettings) if (channelSettings.StripType != StripType.Unknown) { - channel.strip_type = (int)channelSettings.StripType; + //Strip type is set by the native assembly if not explicitly set. + //This type defines the ordering of the colors e. g. RGB or GRB, ... + channel.strip_type = (int)channelSettings.StripType; } } @@ -112,7 +121,8 @@ private string GetMessageForStatusCode(ws2811_return_t statusCode) return Marshal.PtrToStringAuto(strPointer); } - private bool disposedValue = false; // To detect redundant calls +#region IDisposable Support + private bool disposedValue = false; // To detect redundant calls protected virtual void Dispose(bool disposing) { @@ -143,7 +153,7 @@ protected virtual void Dispose(bool disposing) ~WS281x() { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. Dispose(false); } @@ -152,5 +162,6 @@ public void Dispose() Dispose(true); GC.SuppressFinalize(this); } - } + #endregion + } } diff --git a/src/rpi_ws281x/rpi-ws281x-dotnet.csproj b/src/rpi_ws281x/rpi-ws281x-dotnet.csproj index e2ca10b..e36c2be 100644 --- a/src/rpi_ws281x/rpi-ws281x-dotnet.csproj +++ b/src/rpi_ws281x/rpi-ws281x-dotnet.csproj @@ -1,7 +1,7 @@ - netcoreapp2.2 + netcoreapp3.0 rpi_ws281x_dotnet 7.1 From afca11fa8ac105f5a5a16c5f1e38e2443173e004 Mon Sep 17 00:00:00 2001 From: Ryan Everett Date: Mon, 7 Oct 2019 18:59:10 -0400 Subject: [PATCH 5/5] Update TestApp --- src/TestApp/ColorWipe.cs | 4 ++-- src/TestApp/RainbowColorAnimation.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/TestApp/ColorWipe.cs b/src/TestApp/ColorWipe.cs index 0014ed6..fb0af39 100644 --- a/src/TestApp/ColorWipe.cs +++ b/src/TestApp/ColorWipe.cs @@ -22,7 +22,7 @@ public void Execute(AbortRequest request) //Set brightness to maximum (255) //Use Unknown as strip type. Then the type will be set in the native assembly. - settings.Channels[0] = new Channel(ledCount, 18, 255, false, StripType.WS2812_STRIP); + settings.Channel_1 = new Channel(ledCount, 18, 255, false, StripType.WS2812_STRIP); using (var controller = new WS281x(settings)) { @@ -37,7 +37,7 @@ public void Execute(AbortRequest request) private static void Wipe(WS281x controller, Color color) { - for (int i = 0; i <= controller.Settings.Channels[0].LEDs.Count - 1; i++) + for (int i = 0; i <= controller.Settings.Channel_1.LEDs.Count - 1; i++) { controller.SetLEDColor(0, i, color); controller.Render(); diff --git a/src/TestApp/RainbowColorAnimation.cs b/src/TestApp/RainbowColorAnimation.cs index 40148a6..359b81b 100644 --- a/src/TestApp/RainbowColorAnimation.cs +++ b/src/TestApp/RainbowColorAnimation.cs @@ -24,7 +24,7 @@ public void Execute(AbortRequest request) //Set brightness to maximum (255) //Use Unknown as strip type. Then the type will be set in the native assembly. - settings.Channels[0] = new Channel(ledCount, 18, 255, false, StripType.WS2812_STRIP); + settings.Channel_1 = new Channel(ledCount, 18, 255, false, StripType.WS2812_STRIP); using (var controller = new WS281x(settings)) { @@ -32,7 +32,7 @@ public void Execute(AbortRequest request) while (!request.IsAbortRequested) { - for (int i = 0; i <= controller.Settings.Channels[0].LEDCount - 1; i++) + for (int i = 0; i <= controller.Settings.Channel_1.LEDCount - 1; i++) { var colorIndex = (i + colorOffset) % colors.Count; controller.SetLEDColor(0, i, colors[colorIndex]);