From fce3dfaeb935d9f0e8ad3238896737984ab33782 Mon Sep 17 00:00:00 2001 From: Darth Affe Date: Tue, 25 Dec 2018 18:56:39 +0100 Subject: [PATCH] Added device-providers for WS281X-controller (bitwizard and custom arduino) --- .../Arduino/ArduinoWS2812USBDevice.cs | 78 +++++++++++ .../Arduino/ArduinoWS2812USBDeviceInfo.cs | 51 +++++++ .../Arduino/ArduinoWS2812USBUpdateQueue.cs | 105 +++++++++++++++ .../Arduino/ArduinoWS281XDeviceDefinition.cs | 73 +++++++++++ .../Bitwizard/BitwizardWS2812USBDevice.cs | 68 ++++++++++ .../Bitwizard/BitwizardWS2812USBDeviceInfo.cs | 51 +++++++ .../BitwizardWS2812USBUpdateQueue.cs | 47 +++++++ .../BitwizardWS281XDeviceDefinition.cs | 72 ++++++++++ .../Generic/IWS281XDeviceDefinition.cs | 18 +++ .../Generic/SerialPortUpdateQueue.cs | 85 ++++++++++++ .../RGB.NET.Devices.WS281X.csproj | 72 ++++++++++ .../RGB.NET.Devices.WS281X.csproj.DotSettings | 2 + .../Sketches/RGB.NET_Arduino.ino | 114 ++++++++++++++++ .../Sketches/RGB.NET_Bitwizard_compatible.ino | 124 ++++++++++++++++++ .../WS281XDeviceProvider.cs | 110 ++++++++++++++++ .../WS281XDeviceProviderLoader.cs | 27 ++++ RGB.NET.sln | 7 + RGB.NET.sln.DotSettings | 1 + 18 files changed, 1105 insertions(+) create mode 100644 RGB.NET.Devices.WS281X/Arduino/ArduinoWS2812USBDevice.cs create mode 100644 RGB.NET.Devices.WS281X/Arduino/ArduinoWS2812USBDeviceInfo.cs create mode 100644 RGB.NET.Devices.WS281X/Arduino/ArduinoWS2812USBUpdateQueue.cs create mode 100644 RGB.NET.Devices.WS281X/Arduino/ArduinoWS281XDeviceDefinition.cs create mode 100644 RGB.NET.Devices.WS281X/Bitwizard/BitwizardWS2812USBDevice.cs create mode 100644 RGB.NET.Devices.WS281X/Bitwizard/BitwizardWS2812USBDeviceInfo.cs create mode 100644 RGB.NET.Devices.WS281X/Bitwizard/BitwizardWS2812USBUpdateQueue.cs create mode 100644 RGB.NET.Devices.WS281X/Bitwizard/BitwizardWS281XDeviceDefinition.cs create mode 100644 RGB.NET.Devices.WS281X/Generic/IWS281XDeviceDefinition.cs create mode 100644 RGB.NET.Devices.WS281X/Generic/SerialPortUpdateQueue.cs create mode 100644 RGB.NET.Devices.WS281X/RGB.NET.Devices.WS281X.csproj create mode 100644 RGB.NET.Devices.WS281X/RGB.NET.Devices.WS281X.csproj.DotSettings create mode 100644 RGB.NET.Devices.WS281X/Sketches/RGB.NET_Arduino.ino create mode 100644 RGB.NET.Devices.WS281X/Sketches/RGB.NET_Bitwizard_compatible.ino create mode 100644 RGB.NET.Devices.WS281X/WS281XDeviceProvider.cs create mode 100644 RGB.NET.Devices.WS281X/WS281XDeviceProviderLoader.cs diff --git a/RGB.NET.Devices.WS281X/Arduino/ArduinoWS2812USBDevice.cs b/RGB.NET.Devices.WS281X/Arduino/ArduinoWS2812USBDevice.cs new file mode 100644 index 0000000..5fa3c85 --- /dev/null +++ b/RGB.NET.Devices.WS281X/Arduino/ArduinoWS2812USBDevice.cs @@ -0,0 +1,78 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System.Collections.Generic; +using System.Linq; +using RGB.NET.Core; + +namespace RGB.NET.Devices.WS281X.Arduino +{ + // ReSharper disable once InconsistentNaming + /// + /// + /// Represents an arduino WS2812 device. + /// + public class ArduinoWS2812USBDevice : AbstractRGBDevice + { + #region Properties & Fields + + /// + /// Gets the update queue performing updates for this device. + /// + public ArduinoWS2812USBUpdateQueue UpdateQueue { get; } + + /// + public override ArduinoWS2812USBDeviceInfo DeviceInfo { get; } + + /// + /// Gets the channel (as defined in the arduino-sketch) this device is attached to. + /// + public int Channel { get; } + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The update trigger used by this queue. + /// The update queue performing updates for this device. + /// The channel (as defined in the arduino-sketch) this device is attached to. + public ArduinoWS2812USBDevice(ArduinoWS2812USBDeviceInfo deviceInfo, ArduinoWS2812USBUpdateQueue updateQueue, int channel) + { + this.DeviceInfo = deviceInfo; + this.UpdateQueue = updateQueue; + this.Channel = channel; + } + + #endregion + + #region Methods + + internal void Initialize(int ledCount) + { + for (int i = 0; i < ledCount; i++) + InitializeLed(LedId.LedStripe1 + i, new Rectangle(i * 10, 0, 10, 10)); + + //TODO DarthAffe 23.12.2018: Allow to load a layout. + + if (Size == Size.Invalid) + { + Rectangle ledRectangle = new Rectangle(this.Select(x => x.LedRectangle)); + Size = ledRectangle.Size + new Size(ledRectangle.Location.X, ledRectangle.Location.Y); + } + } + + /// + protected override object CreateLedCustomData(LedId ledId) => (Channel, (int)ledId - (int)LedId.LedStripe1); + + /// + protected override IEnumerable GetLedsToUpdate(bool flushLeds) => (flushLeds || LedMapping.Values.Any(x => x.IsDirty)) ? LedMapping.Values : null; + + /// + protected override void UpdateLeds(IEnumerable ledsToUpdate) => UpdateQueue.SetData(ledsToUpdate.Where(x => x.Color.A > 0)); + + #endregion + } +} diff --git a/RGB.NET.Devices.WS281X/Arduino/ArduinoWS2812USBDeviceInfo.cs b/RGB.NET.Devices.WS281X/Arduino/ArduinoWS2812USBDeviceInfo.cs new file mode 100644 index 0000000..c8f0282 --- /dev/null +++ b/RGB.NET.Devices.WS281X/Arduino/ArduinoWS2812USBDeviceInfo.cs @@ -0,0 +1,51 @@ +using System; +using RGB.NET.Core; + +namespace RGB.NET.Devices.WS281X.Arduino +{ + // ReSharper disable once InconsistentNaming + /// + /// + /// Represents a generic information for a . + /// + public class ArduinoWS2812USBDeviceInfo : IRGBDeviceInfo + { + #region Properties & Fields + + /// + public string DeviceName { get; } + + /// + public RGBDeviceType DeviceType => RGBDeviceType.LedStripe; + + /// + public string Manufacturer => "Arduino"; + + /// + public string Model => "WS2812 USB"; + + /// + public RGBDeviceLighting Lighting => RGBDeviceLighting.Key; + + /// + public bool SupportsSyncBack => false; + + /// + public Uri Image { get; set; } + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The name of this device. + public ArduinoWS2812USBDeviceInfo(string name) + { + this.DeviceName = name; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.WS281X/Arduino/ArduinoWS2812USBUpdateQueue.cs b/RGB.NET.Devices.WS281X/Arduino/ArduinoWS2812USBUpdateQueue.cs new file mode 100644 index 0000000..696f54e --- /dev/null +++ b/RGB.NET.Devices.WS281X/Arduino/ArduinoWS2812USBUpdateQueue.cs @@ -0,0 +1,105 @@ +using System.Collections.Generic; +using System.Linq; +using RGB.NET.Core; + +namespace RGB.NET.Devices.WS281X.Arduino +{ + // ReSharper disable once InconsistentNaming + /// + /// + /// Represents the update-queue performing updates for arduino WS2812 devices. + /// + public class ArduinoWS2812USBUpdateQueue : SerialPortUpdateQueue + { + #region Constants + + private static readonly byte[] COUNT_COMMAND = { 0x01 }; + private static readonly byte[] UPDATE_COMMAND = { 0x02 }; + private static readonly byte[] ASK_PROMPT_COMMAND = { 0x0F }; + + #endregion + + #region Properties & Fields + + private readonly Dictionary _dataBuffer = new Dictionary(); + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The update trigger used by this queue. + /// The name of the serial-port to connect to. + /// The baud-rate used by the serial-connection. + public ArduinoWS2812USBUpdateQueue(IDeviceUpdateTrigger updateTrigger, string portName, int baudRate = 115200) + : base(updateTrigger, portName, baudRate) + { } + + #endregion + + #region Methods + + /// + protected override void OnStartup(object sender, CustomUpdateData customData) + { + base.OnStartup(sender, customData); + + SendCommand(ASK_PROMPT_COMMAND); // Get initial prompt + } + + /// + protected override IEnumerable GetCommands(Dictionary dataSet) + { + foreach (IGrouping channelData in dataSet.Select(x => (((int channel, int key))x.Key, x.Value)) + .GroupBy(x => x.Item1.channel)) + { + int channel = channelData.Key; + if (!_dataBuffer.TryGetValue(channel, out byte[] dataBuffer) || (dataBuffer.Length != ((dataSet.Count * 3) + 1))) + _dataBuffer[channel] = dataBuffer = new byte[(dataSet.Count * 3) + 1]; + + dataBuffer[0] = (byte)((channel << 4) | UPDATE_COMMAND[0]); + int i = 1; + foreach ((byte _, byte r, byte g, byte b) in channelData.OrderBy(x => x.Item1.key) + .Select(x => x.Value)) + { + dataBuffer[i++] = r; + dataBuffer[i++] = g; + dataBuffer[i++] = b; + } + yield return dataBuffer; + } + + yield return UPDATE_COMMAND; + } + + /// + protected override void SendCommand(byte[] command) => SerialPort.Write(command, 0, command.Length); + + internal IEnumerable<(int channel, int ledCount)> GetChannels() + { + if (!SerialPort.IsOpen) + SerialPort.Open(); + + SerialPort.DiscardInBuffer(); + SendCommand(ASK_PROMPT_COMMAND); + + SerialPort.ReadTo(Prompt); + SendCommand(COUNT_COMMAND); + int channelCount = SerialPort.ReadByte(); + + for (int i = 1; i <= channelCount; i++) + { + SerialPort.ReadTo(Prompt); + byte[] channelLedCountCommand = { (byte)((i << 4) | COUNT_COMMAND[0]) }; + SendCommand(channelLedCountCommand); + int ledCount = SerialPort.ReadByte(); + if (ledCount > 0) + yield return (i, ledCount); + } + } + + #endregion + } +} diff --git a/RGB.NET.Devices.WS281X/Arduino/ArduinoWS281XDeviceDefinition.cs b/RGB.NET.Devices.WS281X/Arduino/ArduinoWS281XDeviceDefinition.cs new file mode 100644 index 0000000..dc28760 --- /dev/null +++ b/RGB.NET.Devices.WS281X/Arduino/ArduinoWS281XDeviceDefinition.cs @@ -0,0 +1,73 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +using System.Collections.Generic; +using RGB.NET.Core; + +namespace RGB.NET.Devices.WS281X.Arduino +{ + // ReSharper disable once InconsistentNaming + /// + /// + /// Represents a definition of an arduino WS2812 devices. + /// + public class ArduinoWS281XDeviceDefinition : IWS281XDeviceDefinition + { + #region Properties & Fields + + /// + /// Gets the name of the serial-port to connect to. + /// + public string Port { get; } + + /// + /// Gets the baud-rate used by the serial-connection. + /// + public int BaudRate { get; set; } = 115200; + + /// + /// Gets or sets the name used by this device. + /// This allows to use {0} as a placeholder for a incrementing number if multiple devices are created. + /// + public string Name { get; set; } + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The name of the serial-port to connect to. + public ArduinoWS281XDeviceDefinition(string port) + { + this.Port = port; + } + + #endregion + + #region Methods + + /// + public IEnumerable CreateDevices() + { + DeviceUpdateTrigger updateTrigger = new DeviceUpdateTrigger(); + + ArduinoWS2812USBUpdateQueue queue = new ArduinoWS2812USBUpdateQueue(updateTrigger, Port, BaudRate); + IEnumerable<(int channel, int ledCount)> channels = queue.GetChannels(); + int counter = 0; + foreach ((int channel, int ledCount) in channels) + { + string name = string.Format(Name ?? $"Arduino WS2812 USB ({Port}) [{{0}}]", ++counter); + ArduinoWS2812USBDevice device = new ArduinoWS2812USBDevice(new ArduinoWS2812USBDeviceInfo(name), queue, channel); + device.Initialize(ledCount); + yield return device; + } + + updateTrigger.Start(); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.WS281X/Bitwizard/BitwizardWS2812USBDevice.cs b/RGB.NET.Devices.WS281X/Bitwizard/BitwizardWS2812USBDevice.cs new file mode 100644 index 0000000..0e4f7a9 --- /dev/null +++ b/RGB.NET.Devices.WS281X/Bitwizard/BitwizardWS2812USBDevice.cs @@ -0,0 +1,68 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System.Collections.Generic; +using System.Linq; +using RGB.NET.Core; + +namespace RGB.NET.Devices.WS281X.Bitwizard +{ + // ReSharper disable once InconsistentNaming + /// + /// + /// Represents an bitwizard WS2812 USB device. + /// + public class BitwizardWS2812USBDevice : AbstractRGBDevice + { + #region Properties & Fields + + /// + /// Gets the update queue performing updates for this device. + /// + public BitwizardWS2812USBUpdateQueue UpdateQueue { get; } + + /// + public override BitwizardWS2812USBDeviceInfo DeviceInfo { get; } + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The update trigger used by this queue. + /// The update queue performing updates for this device. + public BitwizardWS2812USBDevice(BitwizardWS2812USBDeviceInfo deviceInfo, BitwizardWS2812USBUpdateQueue updateQueue) + { + this.DeviceInfo = deviceInfo; + this.UpdateQueue = updateQueue; + } + + #endregion + + #region Methods + + internal void Initialize(int ledCount) + { + for (int i = 0; i < ledCount; i++) + InitializeLed(LedId.LedStripe1 + i, new Rectangle(i * 10, 0, 10, 10)); + + //TODO DarthAffe 23.12.2018: Allow to load a layout. + + if (Size == Size.Invalid) + { + Rectangle ledRectangle = new Rectangle(this.Select(x => x.LedRectangle)); + Size = ledRectangle.Size + new Size(ledRectangle.Location.X, ledRectangle.Location.Y); + } + } + + /// + protected override object CreateLedCustomData(LedId ledId) => (int)ledId - (int)LedId.LedStripe1; + + /// + protected override void UpdateLeds(IEnumerable ledsToUpdate) => UpdateQueue.SetData(ledsToUpdate.Where(x => x.Color.A > 0)); + + #endregion + } +} diff --git a/RGB.NET.Devices.WS281X/Bitwizard/BitwizardWS2812USBDeviceInfo.cs b/RGB.NET.Devices.WS281X/Bitwizard/BitwizardWS2812USBDeviceInfo.cs new file mode 100644 index 0000000..7e86dae --- /dev/null +++ b/RGB.NET.Devices.WS281X/Bitwizard/BitwizardWS2812USBDeviceInfo.cs @@ -0,0 +1,51 @@ +using System; +using RGB.NET.Core; + +namespace RGB.NET.Devices.WS281X.Bitwizard +{ + // ReSharper disable once InconsistentNaming + /// + /// + /// Represents a generic information for a . + /// + public class BitwizardWS2812USBDeviceInfo : IRGBDeviceInfo + { + #region Properties & Fields + + /// + public string DeviceName { get; } + + /// + public RGBDeviceType DeviceType => RGBDeviceType.LedStripe; + + /// + public string Manufacturer => "Bitwizard"; + + /// + public string Model => "WS2812 USB"; + + /// + public RGBDeviceLighting Lighting => RGBDeviceLighting.Key; + + /// + public bool SupportsSyncBack => false; + + /// + public Uri Image { get; set; } + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The name of this device. + public BitwizardWS2812USBDeviceInfo(string name) + { + this.DeviceName = name; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.WS281X/Bitwizard/BitwizardWS2812USBUpdateQueue.cs b/RGB.NET.Devices.WS281X/Bitwizard/BitwizardWS2812USBUpdateQueue.cs new file mode 100644 index 0000000..4ea1152 --- /dev/null +++ b/RGB.NET.Devices.WS281X/Bitwizard/BitwizardWS2812USBUpdateQueue.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using RGB.NET.Core; + +namespace RGB.NET.Devices.WS281X.Bitwizard +{ + // ReSharper disable once InconsistentNaming + /// + /// + /// Represents the update-queue performing updates for a bitwizard WS2812 device. + /// + public class BitwizardWS2812USBUpdateQueue : SerialPortUpdateQueue + { + #region Constructors + + /// + /// + /// Initializes a new instance of the class. + /// + /// The update trigger used by this queue. + /// The name of the serial-port to connect to. + /// The baud-rate used by the serial-connection. + public BitwizardWS2812USBUpdateQueue(IDeviceUpdateTrigger updateTrigger, string portName, int baudRate = 115200) + : base(updateTrigger, portName, baudRate) + { } + + #endregion + + #region Methods + + /// + protected override void OnStartup(object sender, CustomUpdateData customData) + { + base.OnStartup(sender, customData); + + SendCommand(string.Empty); // Get initial prompt + } + + /// + protected override IEnumerable GetCommands(Dictionary dataSet) + { + foreach (KeyValuePair data in dataSet) + yield return $"pix {(int)data.Key} {data.Value.AsRGBHexString()}"; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.WS281X/Bitwizard/BitwizardWS281XDeviceDefinition.cs b/RGB.NET.Devices.WS281X/Bitwizard/BitwizardWS281XDeviceDefinition.cs new file mode 100644 index 0000000..d8bc4f3 --- /dev/null +++ b/RGB.NET.Devices.WS281X/Bitwizard/BitwizardWS281XDeviceDefinition.cs @@ -0,0 +1,72 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +using System.Collections.Generic; +using RGB.NET.Core; + +namespace RGB.NET.Devices.WS281X.Bitwizard +{ + // ReSharper disable once InconsistentNaming + /// + /// + /// Represents a definition of an bitwizard WS2812 devices. + /// + public class BitwizardWS281XDeviceDefinition : IWS281XDeviceDefinition + { + #region Properties & Fields + + /// + /// Gets the name of the serial-port to connect to. + /// + public string Port { get; } + + /// + /// Gets the baud-rate used by the serial-connection. + /// + public int BaudRate { get; set; } = 115200; + + /// + /// Gets or sets the name used by this device. + /// + public string Name { get; set; } + + /// + /// Gets or sets the amount of leds of this device. + /// + public int StripLength { get; set; } = 384; + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The name of the serial-port to connect to. + public BitwizardWS281XDeviceDefinition(string port) + { + this.Port = port; + } + + #endregion + + #region Methods + + /// + public IEnumerable CreateDevices() + { + DeviceUpdateTrigger updateTrigger = new DeviceUpdateTrigger(); + + BitwizardWS2812USBUpdateQueue queue = new BitwizardWS2812USBUpdateQueue(updateTrigger, Port, BaudRate); + string name = Name ?? $"Bitwizard WS2812 USB ({Port})"; + BitwizardWS2812USBDevice device = new BitwizardWS2812USBDevice(new BitwizardWS2812USBDeviceInfo(name), queue); + device.Initialize(StripLength); + yield return device; + + updateTrigger.Start(); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.WS281X/Generic/IWS281XDeviceDefinition.cs b/RGB.NET.Devices.WS281X/Generic/IWS281XDeviceDefinition.cs new file mode 100644 index 0000000..8ff5fa9 --- /dev/null +++ b/RGB.NET.Devices.WS281X/Generic/IWS281XDeviceDefinition.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using RGB.NET.Core; + +namespace RGB.NET.Devices.WS281X +{ + /// + /// Marker interface for WS281X device definitions. + /// + // ReSharper disable once InconsistentNaming + public interface IWS281XDeviceDefinition + { + /// + /// Gets the devices defined by this definition. + /// + /// The initialized devices defined by this definition. + IEnumerable CreateDevices(); + } +} diff --git a/RGB.NET.Devices.WS281X/Generic/SerialPortUpdateQueue.cs b/RGB.NET.Devices.WS281X/Generic/SerialPortUpdateQueue.cs new file mode 100644 index 0000000..21504cf --- /dev/null +++ b/RGB.NET.Devices.WS281X/Generic/SerialPortUpdateQueue.cs @@ -0,0 +1,85 @@ +using System.Collections.Generic; +using System.IO.Ports; +using RGB.NET.Core; + +namespace RGB.NET.Devices.WS281X +{ + /// + /// + /// Represents a update queue for serial devices. + /// + /// The type of data sent through the serial connection. + public abstract class SerialPortUpdateQueue : UpdateQueue + { + #region Properties & Fields + + /// + /// Gets or sets the prompt to wait for between sending commands. + /// + // ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global + protected string Prompt { get; set; } = ">"; + + /// + /// Gets the serial port used by this queue. + /// + protected SerialPort SerialPort { get; } + + #endregion + + #region Constructors + + /// + /// + /// Initializes a new instance of the class. + /// + /// The update trigger used by this queue. + /// The name of the serial-port to connect to. + /// The baud-rate used by the serial-connection. + internal SerialPortUpdateQueue(IDeviceUpdateTrigger updateTrigger, string portName, int baudRate = 115200) + : base(updateTrigger) + { + SerialPort = new SerialPort(portName, baudRate); + } + + #endregion + + #region Methods + + /// + protected override void OnStartup(object sender, CustomUpdateData customData) + { + base.OnStartup(sender, customData); + + if (!SerialPort.IsOpen) + SerialPort.Open(); + + SerialPort.DiscardInBuffer(); + } + + /// + protected override void Update(Dictionary dataSet) + { + foreach (TData command in GetCommands(dataSet)) + { + SerialPort.ReadTo(Prompt); + SendCommand(command); + } + } + + /// + /// Gets the commands that need to be sent to the device to update the requested colors. + /// + /// The set of data that needs to be updated. + /// The commands to be sent. + protected abstract IEnumerable GetCommands(Dictionary dataSet); + + /// + /// Sends a command as a string followed by a line-break to the device. + /// This most likely needs to be overwritten if the data-type isn't string. + /// + /// The command to be sent. + protected virtual void SendCommand(TData command) => SerialPort.WriteLine((command as string) ?? string.Empty); + + #endregion + } +} diff --git a/RGB.NET.Devices.WS281X/RGB.NET.Devices.WS281X.csproj b/RGB.NET.Devices.WS281X/RGB.NET.Devices.WS281X.csproj new file mode 100644 index 0000000..a2d0f40 --- /dev/null +++ b/RGB.NET.Devices.WS281X/RGB.NET.Devices.WS281X.csproj @@ -0,0 +1,72 @@ + + + netstandard2.0;net45 + win7-x86;win7-x64 + + Darth Affe + Wyrez + en-US + en-US + RGB.NET.Devices.WS281X + RGB.NET.Devices.WS281X + RGB.NET.Devices.WS281X + RGB.NET.Devices.WS281X + RGB.NET.Devices.WS281X + WS281X-Device-Implementations of RGB.NET + WS281X-Device-Implementations of RGB.NET, a C# (.NET) library for accessing various RGB-peripherals + Copyright © Wyrez 2018 + Copyright © Wyrez 2018 + http://lib.arge.be/icon.png + https://github.com/DarthAffe/RGB.NET + https://raw.githubusercontent.com/DarthAffe/RGB.NET/master/LICENSE + Github + https://github.com/DarthAffe/RGB.NET + True + + + + 0.0.1 + 0.0.1 + 0.0.1 + + ..\bin\ + true + True + True + latest + + + + NETCORE;NETSTANDARD;NETSTANDARD2_0 + + + + NET45;NETFULL + + + + $(DefineConstants);TRACE;DEBUG + true + full + false + + + + pdbonly + true + $(NoWarn);CS1591;CS1572;CS1573 + $(DefineConstants);RELEASE + + + + + + + + + + + + + + \ No newline at end of file diff --git a/RGB.NET.Devices.WS281X/RGB.NET.Devices.WS281X.csproj.DotSettings b/RGB.NET.Devices.WS281X/RGB.NET.Devices.WS281X.csproj.DotSettings new file mode 100644 index 0000000..acc5fca --- /dev/null +++ b/RGB.NET.Devices.WS281X/RGB.NET.Devices.WS281X.csproj.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/RGB.NET.Devices.WS281X/Sketches/RGB.NET_Arduino.ino b/RGB.NET.Devices.WS281X/Sketches/RGB.NET_Arduino.ino new file mode 100644 index 0000000..30ae05a --- /dev/null +++ b/RGB.NET.Devices.WS281X/Sketches/RGB.NET_Arduino.ino @@ -0,0 +1,114 @@ +#include "FastLED.h" + +// This sketch allows to use the arduino as ArduinoWS2812USBDevice. +// The amount of leds that can be handled in realtime mostly depends on the speef of the arduino, +// but an arduino UNO is doing quite well. + +//#### CONFIGURATION #### + +#define CHANNELS 4 // change this only if you add or remove channels in the implementation-part. To disable channels set them to 0 leds. + +// no more than 255 leds per channel +#define LEDS_CHANNEL_1 32 +#define LEDS_CHANNEL_2 0 +#define LEDS_CHANNEL_3 0 +#define LEDS_CHANNEL_4 0 + +#define PIN_CHANNEL_1 6 +#define PIN_CHANNEL_2 7 +#define PIN_CHANNEL_3 8 +#define PIN_CHANNEL_4 9 + +#define BAUD_RATE 115200 +#define SERIAL_PROMPT ">" + +//####################### + +CRGB leds_channel_1[LEDS_CHANNEL_1]; +CRGB leds_channel_2[LEDS_CHANNEL_2]; +CRGB leds_channel_3[LEDS_CHANNEL_3]; +CRGB leds_channel_4[LEDS_CHANNEL_4]; + +byte command = 0; + +//----------------------- + +void setup() +{ + if(LEDS_CHANNEL_1 > 0) { FastLED.addLeds(leds_channel_1, LEDS_CHANNEL_1); } + if(LEDS_CHANNEL_2 > 0) { FastLED.addLeds(leds_channel_2, LEDS_CHANNEL_2); } + if(LEDS_CHANNEL_3 > 0) { FastLED.addLeds(leds_channel_3, LEDS_CHANNEL_3); } + if(LEDS_CHANNEL_4 > 0) { FastLED.addLeds(leds_channel_4, LEDS_CHANNEL_4); } + + Serial.begin(BAUD_RATE); + Serial.print(SERIAL_PROMPT); +} + +void loop() +{ + if(command > 0) + { + switch(command) + { + // ### default ### + case 0x01: // get channel-count + Serial.write(CHANNELS); + break; + + case 0x02: // update + FastLED.show(); + break; + + case 0x0F: // ask for prompt + break; + + // ### channel 1 ### + case 0x11: // get led-count of channel 1 + Serial.write(LEDS_CHANNEL_1); + break; + case 0x12: // set leds of channel 1 + Serial.readBytes(((uint8_t*)leds_channel_1), (LEDS_CHANNEL_1 * 3)); + break; + + // ### channel 2 ### + case 0x21: // get led-count of channel 2 + Serial.write(LEDS_CHANNEL_2); + break; + case 0x22: // set leds of channel 2 + Serial.readBytes(((uint8_t*)leds_channel_2), (LEDS_CHANNEL_2 * 3)); + break; + + // ### channel 3 ### + case 0x31: // get led-count of channel 3 + Serial.write(LEDS_CHANNEL_3); + break; + case 0x32: // set leds of channel 3 + Serial.readBytes(((uint8_t*)leds_channel_3), (LEDS_CHANNEL_3 * 3)); + break; + + // ### channel 4 ### + case 0x41: // get led-count of channel 4 + Serial.write(LEDS_CHANNEL_4); + break; + case 0x42: // set leds of channel 4 + Serial.readBytes(((uint8_t*)leds_channel_4), (LEDS_CHANNEL_4 * 3)); + break; + + // ### default ### + default: + command = 0; + return; // no prompt + } + + Serial.print(SERIAL_PROMPT); + command = 0; + } +} + +void serialEvent() +{ + if (Serial.available()) + { + command = Serial.read(); + } +} diff --git a/RGB.NET.Devices.WS281X/Sketches/RGB.NET_Bitwizard_compatible.ino b/RGB.NET.Devices.WS281X/Sketches/RGB.NET_Bitwizard_compatible.ino new file mode 100644 index 0000000..34faf30 --- /dev/null +++ b/RGB.NET.Devices.WS281X/Sketches/RGB.NET_Bitwizard_compatible.ino @@ -0,0 +1,124 @@ +#include "FastLED.h" + +// This sketch allows to use the arduino as a BitwizardWS2812USBDevice. +// Since the communication is text-based it's to slow to process realtime data, +// but it can be used from a basic terminal by hand. +// +// If you want to use it only with RGB.NET you should in nearly all cases prefer the 'RGB.NET_Arduino'-sketch. + +//#### CONFIGURATION #### + +#define NUM_LEDS 384 +#define LED_PIN 6 + +#define BAUD_RATE 115200 +#define SERIAL_PROMPT '>' + +//####################### + +bool serialCommandReceived = false; +int bufferLength = 0; +char inputBuffer[256] = ""; + +CRGB leds[NUM_LEDS]; + +//----------------------- + +void setLed(int led, long r, long g, long b) +{ + leds[led].setRGB(r, g, b); +} + +void setLed(int led, long color) +{ + long r = color >> 16; + long g = color >> 8 & 0xFF; + long b = color & 0xFF; + setLed(led, r, g, b); +} + +void setAllLeds(long r, long g, long b) +{ + fill_solid(leds, NUM_LEDS, CRGB(r, g, b)); +} + +void updateLeds() +{ + FastLED.show(); +} + +void setup() +{ + FastLED.addLeds(leds, NUM_LEDS); + + Serial.begin(BAUD_RATE); + Serial.println(SERIAL_PROMPT); +} + +void loop() +{ + if(serialCommandReceived) + { + if(strncmp(&inputBuffer[0], "set", 3) == 0) + { + char *end; + int led = (int)strtol(&inputBuffer[3], &end, 10); + long color = strtol(end, NULL, 16); + setLed(led, color); + } + else if(strncmp(&inputBuffer[0], "update", 6) == 0) + { + updateLeds(); + } + else if(strncmp(&inputBuffer[0], "pix", 3) == 0) // shortcut for set and update (as in the bitwizard controller) + { + char *end; + int led = (int)strtol(&inputBuffer[3], &end, 10); + long color = strtol(end, NULL, 16); + setLed(led, color); + updateLeds(); + } + else if(strncmp(&inputBuffer[0], "black", 5) == 0) // shortcut for all set black and update + { + setAllLeds(0, 0, 0); + updateLeds(); + } + else if(strncmp(&inputBuffer[0], "white", 5) == 0) // shortcut for all set white and update + { + setAllLeds(255, 255, 255); + updateLeds(); + } + else if(bufferLength > 0) + { + Serial.println("unknown command '" + String(inputBuffer) + "'"); + } + + Serial.println(SERIAL_PROMPT); + + memset(&inputBuffer[0], 0, sizeof(inputBuffer)); + bufferLength = 0; + serialCommandReceived = false; + } +} + +void serialEvent() +{ + while (Serial.available() && !serialCommandReceived) + { + char inChar = (char)Serial.read(); + switch(inChar) + { + case '\r': + serialCommandReceived = true; + break; + + case '\n': + serialCommandReceived = true; + break; + + default: + inputBuffer[bufferLength++] = inChar; + break; + } + } +} diff --git a/RGB.NET.Devices.WS281X/WS281XDeviceProvider.cs b/RGB.NET.Devices.WS281X/WS281XDeviceProvider.cs new file mode 100644 index 0000000..127e54b --- /dev/null +++ b/RGB.NET.Devices.WS281X/WS281XDeviceProvider.cs @@ -0,0 +1,110 @@ +// ReSharper disable MemberCanBePrivate.Global + +using System; +using System.Collections.Generic; +using RGB.NET.Core; + +namespace RGB.NET.Devices.WS281X +{ + /// + /// + /// Represents a device provider responsible for WS2812B- and WS2811-Led-devices. + /// + // ReSharper disable once InconsistentNaming + public class WS281XDeviceProvider : IRGBDeviceProvider + { + #region Properties & Fields + + private static WS281XDeviceProvider _instance; + /// + /// Gets the singleton instance. + /// + public static WS281XDeviceProvider Instance => _instance ?? new WS281XDeviceProvider(); + + /// + public bool IsInitialized { get; private set; } + + /// + public IEnumerable Devices { get; private set; } + + /// + public bool HasExclusiveAccess => false; + + /// + /// Gets a list of all defined device-definitions. + /// + // ReSharper disable once CollectionNeverUpdated.Global + // ReSharper disable once ReturnTypeCanBeEnumerable.Global + public List DeviceDefinitions { get; } = new List(); + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// Thrown if this constructor is called even if there is already an instance of this class. + public WS281XDeviceProvider() + { + if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(WS281XDeviceProvider)}"); + _instance = this; + } + + #endregion + + #region Methods + + /// + /// Adds the given to this device-provider. + /// + /// The to add. + // ReSharper disable once UnusedMember.Global + public void AddDeviceDefinition(IWS281XDeviceDefinition deviceDefinition) => DeviceDefinitions.Add(deviceDefinition); + + /// + public bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.Unknown, bool exclusiveAccessIfPossible = false, bool throwExceptions = false) + { + IsInitialized = false; + + try + { + List devices = new List(); + foreach (IWS281XDeviceDefinition deviceDefinition in DeviceDefinitions) + { + try + { + devices.AddRange(deviceDefinition.CreateDevices()); + } + catch { if (throwExceptions) throw; } + } + Devices = devices; + + IsInitialized = true; + } + catch + { + if (throwExceptions) + throw; + return false; + } + + return true; + } + + /// + public void ResetDevices() + { } + + /// + public void Dispose() + { + if (IsInitialized) + foreach (IRGBDevice device in Devices) + if (device is IDisposable disposable) + disposable.Dispose(); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.WS281X/WS281XDeviceProviderLoader.cs b/RGB.NET.Devices.WS281X/WS281XDeviceProviderLoader.cs new file mode 100644 index 0000000..10781a9 --- /dev/null +++ b/RGB.NET.Devices.WS281X/WS281XDeviceProviderLoader.cs @@ -0,0 +1,27 @@ +using RGB.NET.Core; + +namespace RGB.NET.Devices.WS281X +{ + /// + /// + /// Represents a device provider loaded used to dynamically load WS281X devices into an application. + /// + // ReSharper disable once UnusedMember.Global + // ReSharper disable once InconsistentNaming + public class WS281XDeviceProviderLoader : IRGBDeviceProviderLoader + { + #region Properties & Fields + + /// + public bool RequiresInitialization => true; + + #endregion + + #region Methods + + /// + public IRGBDeviceProvider GetDeviceProvider() => WS281XDeviceProvider.Instance; + + #endregion + } +} diff --git a/RGB.NET.sln b/RGB.NET.sln index 281164a..b6ea9d4 100644 --- a/RGB.NET.sln +++ b/RGB.NET.sln @@ -37,6 +37,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RGB.NET.Decorators", "RGB.N EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RGB.NET.Groups", "RGB.NET.Groups\RGB.NET.Groups.csproj", "{6FEBDC9E-909D-4EE2-B003-EDFBEF5FFF40}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RGB.NET.Devices.WS281X", "RGB.NET.Devices.WS281X\RGB.NET.Devices.WS281X.csproj", "{0AD382DA-E999-4F74-9658-8D402EE9BF3F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -103,6 +105,10 @@ Global {6FEBDC9E-909D-4EE2-B003-EDFBEF5FFF40}.Debug|Any CPU.Build.0 = Debug|Any CPU {6FEBDC9E-909D-4EE2-B003-EDFBEF5FFF40}.Release|Any CPU.ActiveCfg = Release|Any CPU {6FEBDC9E-909D-4EE2-B003-EDFBEF5FFF40}.Release|Any CPU.Build.0 = Release|Any CPU + {0AD382DA-E999-4F74-9658-8D402EE9BF3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0AD382DA-E999-4F74-9658-8D402EE9BF3F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0AD382DA-E999-4F74-9658-8D402EE9BF3F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0AD382DA-E999-4F74-9658-8D402EE9BF3F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -122,6 +128,7 @@ Global {B159FB51-5939-490E-A1BA-FB55D4D7ADDF} = {EBC33090-8494-4DF4-B4B6-64D0E531E93F} {8725C448-818C-41F7-B23F-F97E062BF233} = {EBC33090-8494-4DF4-B4B6-64D0E531E93F} {6FEBDC9E-909D-4EE2-B003-EDFBEF5FFF40} = {EBC33090-8494-4DF4-B4B6-64D0E531E93F} + {0AD382DA-E999-4F74-9658-8D402EE9BF3F} = {D13032C6-432E-4F43-8A32-071133C22B16} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7F222AD4-1F9E-4AAB-9D69-D62372D4C1BA} diff --git a/RGB.NET.sln.DotSettings b/RGB.NET.sln.DotSettings index ed9c9b8..563aafd 100644 --- a/RGB.NET.sln.DotSettings +++ b/RGB.NET.sln.DotSettings @@ -342,6 +342,7 @@ True True True + True True True True