1
0
mirror of https://github.com/DarthAffe/RGB.NET.git synced 2025-12-12 17:48:31 +00:00

Added device-providers for WS281X-controller (bitwizard and custom arduino)

This commit is contained in:
Darth Affe 2018-12-25 18:56:39 +01:00
parent fde6d17bda
commit fce3dfaeb9
18 changed files with 1105 additions and 0 deletions

View File

@ -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
/// <inheritdoc />
/// <summary>
/// Represents an arduino WS2812 device.
/// </summary>
public class ArduinoWS2812USBDevice : AbstractRGBDevice<ArduinoWS2812USBDeviceInfo>
{
#region Properties & Fields
/// <summary>
/// Gets the update queue performing updates for this device.
/// </summary>
public ArduinoWS2812USBUpdateQueue UpdateQueue { get; }
/// <inheritdoc />
public override ArduinoWS2812USBDeviceInfo DeviceInfo { get; }
/// <summary>
/// Gets the channel (as defined in the arduino-sketch) this device is attached to.
/// </summary>
public int Channel { get; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="ArduinoWS2812USBDevice"/> class.
/// </summary>
/// <param name="deviceInfo">The update trigger used by this queue.</param>
/// <param name="updateQueue">The update queue performing updates for this device.</param>
/// <param name="channel">The channel (as defined in the arduino-sketch) this device is attached to.</param>
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);
}
}
/// <inheritdoc />
protected override object CreateLedCustomData(LedId ledId) => (Channel, (int)ledId - (int)LedId.LedStripe1);
/// <inheritdoc />
protected override IEnumerable<Led> GetLedsToUpdate(bool flushLeds) => (flushLeds || LedMapping.Values.Any(x => x.IsDirty)) ? LedMapping.Values : null;
/// <inheritdoc />
protected override void UpdateLeds(IEnumerable<Led> ledsToUpdate) => UpdateQueue.SetData(ledsToUpdate.Where(x => x.Color.A > 0));
#endregion
}
}

View File

@ -0,0 +1,51 @@
using System;
using RGB.NET.Core;
namespace RGB.NET.Devices.WS281X.Arduino
{
// ReSharper disable once InconsistentNaming
/// <inheritdoc />
/// <summary>
/// Represents a generic information for a <see cref="T:RGB.NET.Devices.WS281X.Arduino.ArduinoWS2812USBDevice" />.
/// </summary>
public class ArduinoWS2812USBDeviceInfo : IRGBDeviceInfo
{
#region Properties & Fields
/// <inheritdoc />
public string DeviceName { get; }
/// <inheritdoc />
public RGBDeviceType DeviceType => RGBDeviceType.LedStripe;
/// <inheritdoc />
public string Manufacturer => "Arduino";
/// <inheritdoc />
public string Model => "WS2812 USB";
/// <inheritdoc />
public RGBDeviceLighting Lighting => RGBDeviceLighting.Key;
/// <inheritdoc />
public bool SupportsSyncBack => false;
/// <inheritdoc />
public Uri Image { get; set; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="ArduinoWS2812USBDeviceInfo"/> class.
/// </summary>
/// <param name="name">The name of this device.</param>
public ArduinoWS2812USBDeviceInfo(string name)
{
this.DeviceName = name;
}
#endregion
}
}

View File

@ -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
/// <inheritdoc />
/// <summary>
/// Represents the update-queue performing updates for arduino WS2812 devices.
/// </summary>
public class ArduinoWS2812USBUpdateQueue : SerialPortUpdateQueue<byte[]>
{
#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<int, byte[]> _dataBuffer = new Dictionary<int, byte[]>();
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="ArduinoWS2812USBUpdateQueue"/> class.
/// </summary>
/// <param name="updateTrigger">The update trigger used by this queue.</param>
/// <param name="portName">The name of the serial-port to connect to.</param>
/// <param name="baudRate">The baud-rate used by the serial-connection.</param>
public ArduinoWS2812USBUpdateQueue(IDeviceUpdateTrigger updateTrigger, string portName, int baudRate = 115200)
: base(updateTrigger, portName, baudRate)
{ }
#endregion
#region Methods
/// <inheritdoc />
protected override void OnStartup(object sender, CustomUpdateData customData)
{
base.OnStartup(sender, customData);
SendCommand(ASK_PROMPT_COMMAND); // Get initial prompt
}
/// <inheritdoc />
protected override IEnumerable<byte[]> GetCommands(Dictionary<object, Color> dataSet)
{
foreach (IGrouping<int, ((int channel, int key), Color Value)> 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;
}
/// <inheritdoc />
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
}
}

View File

@ -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
/// <inheritdoc />
/// <summary>
/// Represents a definition of an arduino WS2812 devices.
/// </summary>
public class ArduinoWS281XDeviceDefinition : IWS281XDeviceDefinition
{
#region Properties & Fields
/// <summary>
/// Gets the name of the serial-port to connect to.
/// </summary>
public string Port { get; }
/// <summary>
/// Gets the baud-rate used by the serial-connection.
/// </summary>
public int BaudRate { get; set; } = 115200;
/// <summary>
/// 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.
/// </summary>
public string Name { get; set; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="ArduinoWS281XDeviceDefinition"/> class.
/// </summary>
/// <param name="portName">The name of the serial-port to connect to.</param>
public ArduinoWS281XDeviceDefinition(string port)
{
this.Port = port;
}
#endregion
#region Methods
/// <inheritdoc />
public IEnumerable<IRGBDevice> 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
}
}

View File

@ -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
/// <inheritdoc />
/// <summary>
/// Represents an bitwizard WS2812 USB device.
/// </summary>
public class BitwizardWS2812USBDevice : AbstractRGBDevice<BitwizardWS2812USBDeviceInfo>
{
#region Properties & Fields
/// <summary>
/// Gets the update queue performing updates for this device.
/// </summary>
public BitwizardWS2812USBUpdateQueue UpdateQueue { get; }
/// <inheritdoc />
public override BitwizardWS2812USBDeviceInfo DeviceInfo { get; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="BitwizardWS2812USBDevice"/> class.
/// </summary>
/// <param name="deviceInfo">The update trigger used by this queue.</param>
/// <param name="updateQueue">The update queue performing updates for this device.</param>
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);
}
}
/// <inheritdoc />
protected override object CreateLedCustomData(LedId ledId) => (int)ledId - (int)LedId.LedStripe1;
/// <inheritdoc />
protected override void UpdateLeds(IEnumerable<Led> ledsToUpdate) => UpdateQueue.SetData(ledsToUpdate.Where(x => x.Color.A > 0));
#endregion
}
}

View File

@ -0,0 +1,51 @@
using System;
using RGB.NET.Core;
namespace RGB.NET.Devices.WS281X.Bitwizard
{
// ReSharper disable once InconsistentNaming
/// <inheritdoc />
/// <summary>
/// Represents a generic information for a <see cref="T:RGB.NET.Devices.WS281X.Bitwizard.BitwizardWS2812USBDeviceInfo" />.
/// </summary>
public class BitwizardWS2812USBDeviceInfo : IRGBDeviceInfo
{
#region Properties & Fields
/// <inheritdoc />
public string DeviceName { get; }
/// <inheritdoc />
public RGBDeviceType DeviceType => RGBDeviceType.LedStripe;
/// <inheritdoc />
public string Manufacturer => "Bitwizard";
/// <inheritdoc />
public string Model => "WS2812 USB";
/// <inheritdoc />
public RGBDeviceLighting Lighting => RGBDeviceLighting.Key;
/// <inheritdoc />
public bool SupportsSyncBack => false;
/// <inheritdoc />
public Uri Image { get; set; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="BitwizardWS2812USBDeviceInfo"/> class.
/// </summary>
/// <param name="name">The name of this device.</param>
public BitwizardWS2812USBDeviceInfo(string name)
{
this.DeviceName = name;
}
#endregion
}
}

View File

@ -0,0 +1,47 @@
using System.Collections.Generic;
using RGB.NET.Core;
namespace RGB.NET.Devices.WS281X.Bitwizard
{
// ReSharper disable once InconsistentNaming
/// <inheritdoc />
/// <summary>
/// Represents the update-queue performing updates for a bitwizard WS2812 device.
/// </summary>
public class BitwizardWS2812USBUpdateQueue : SerialPortUpdateQueue<string>
{
#region Constructors
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Devices.WS281X.Bitwizard.BitwizardWS2812USBUpdateQueue" /> class.
/// </summary>
/// <param name="updateTrigger">The update trigger used by this queue.</param>
/// <param name="portName">The name of the serial-port to connect to.</param>
/// <param name="baudRate">The baud-rate used by the serial-connection.</param>
public BitwizardWS2812USBUpdateQueue(IDeviceUpdateTrigger updateTrigger, string portName, int baudRate = 115200)
: base(updateTrigger, portName, baudRate)
{ }
#endregion
#region Methods
/// <inheritdoc />
protected override void OnStartup(object sender, CustomUpdateData customData)
{
base.OnStartup(sender, customData);
SendCommand(string.Empty); // Get initial prompt
}
/// <inheritdoc />
protected override IEnumerable<string> GetCommands(Dictionary<object, Color> dataSet)
{
foreach (KeyValuePair<object, Color> data in dataSet)
yield return $"pix {(int)data.Key} {data.Value.AsRGBHexString()}";
}
#endregion
}
}

View File

@ -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
/// <inheritdoc />
/// <summary>
/// Represents a definition of an bitwizard WS2812 devices.
/// </summary>
public class BitwizardWS281XDeviceDefinition : IWS281XDeviceDefinition
{
#region Properties & Fields
/// <summary>
/// Gets the name of the serial-port to connect to.
/// </summary>
public string Port { get; }
/// <summary>
/// Gets the baud-rate used by the serial-connection.
/// </summary>
public int BaudRate { get; set; } = 115200;
/// <summary>
/// Gets or sets the name used by this device.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Gets or sets the amount of leds of this device.
/// </summary>
public int StripLength { get; set; } = 384;
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="BitwizardWS281XDeviceDefinition"/> class.
/// </summary>
/// <param name="portName">The name of the serial-port to connect to.</param>
public BitwizardWS281XDeviceDefinition(string port)
{
this.Port = port;
}
#endregion
#region Methods
/// <inheritdoc />
public IEnumerable<IRGBDevice> 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
}
}

View File

@ -0,0 +1,18 @@
using System.Collections.Generic;
using RGB.NET.Core;
namespace RGB.NET.Devices.WS281X
{
/// <summary>
/// Marker interface for WS281X device definitions.
/// </summary>
// ReSharper disable once InconsistentNaming
public interface IWS281XDeviceDefinition
{
/// <summary>
/// Gets the devices defined by this definition.
/// </summary>
/// <returns>The initialized devices defined by this definition.</returns>
IEnumerable<IRGBDevice> CreateDevices();
}
}

View File

@ -0,0 +1,85 @@
using System.Collections.Generic;
using System.IO.Ports;
using RGB.NET.Core;
namespace RGB.NET.Devices.WS281X
{
/// <inheritdoc />
/// <summary>
/// Represents a update queue for serial devices.
/// </summary>
/// <typeparam name="TData">The type of data sent through the serial connection.</typeparam>
public abstract class SerialPortUpdateQueue<TData> : UpdateQueue
{
#region Properties & Fields
/// <summary>
/// Gets or sets the prompt to wait for between sending commands.
/// </summary>
// ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global
protected string Prompt { get; set; } = ">";
/// <summary>
/// Gets the serial port used by this queue.
/// </summary>
protected SerialPort SerialPort { get; }
#endregion
#region Constructors
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="SerialPortUpdateQueue{TData}"/> class.
/// </summary>
/// <param name="updateTrigger">The update trigger used by this queue.</param>
/// <param name="portName">The name of the serial-port to connect to.</param>
/// <param name="baudRate">The baud-rate used by the serial-connection.</param>
internal SerialPortUpdateQueue(IDeviceUpdateTrigger updateTrigger, string portName, int baudRate = 115200)
: base(updateTrigger)
{
SerialPort = new SerialPort(portName, baudRate);
}
#endregion
#region Methods
/// <inheritdoc />
protected override void OnStartup(object sender, CustomUpdateData customData)
{
base.OnStartup(sender, customData);
if (!SerialPort.IsOpen)
SerialPort.Open();
SerialPort.DiscardInBuffer();
}
/// <inheritdoc />
protected override void Update(Dictionary<object, Color> dataSet)
{
foreach (TData command in GetCommands(dataSet))
{
SerialPort.ReadTo(Prompt);
SendCommand(command);
}
}
/// <summary>
/// Gets the commands that need to be sent to the device to update the requested colors.
/// </summary>
/// <param name="dataSet">The set of data that needs to be updated.</param>
/// <returns>The commands to be sent.</returns>
protected abstract IEnumerable<TData> GetCommands(Dictionary<object, Color> dataSet);
/// <summary>
/// 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.
/// </summary>
/// <param name="command">The command to be sent.</param>
protected virtual void SendCommand(TData command) => SerialPort.WriteLine((command as string) ?? string.Empty);
#endregion
}
}

View File

@ -0,0 +1,72 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net45</TargetFrameworks>
<RuntimeIdentifiers>win7-x86;win7-x64</RuntimeIdentifiers>
<Authors>Darth Affe</Authors>
<Company>Wyrez</Company>
<Language>en-US</Language>
<NeutralLanguage>en-US</NeutralLanguage>
<Title>RGB.NET.Devices.WS281X</Title>
<AssemblyName>RGB.NET.Devices.WS281X</AssemblyName>
<AssemblyTitle>RGB.NET.Devices.WS281X</AssemblyTitle>
<PackageId>RGB.NET.Devices.WS281X</PackageId>
<RootNamespace>RGB.NET.Devices.WS281X</RootNamespace>
<Description>WS281X-Device-Implementations of RGB.NET</Description>
<Summary>WS281X-Device-Implementations of RGB.NET, a C# (.NET) library for accessing various RGB-peripherals</Summary>
<Copyright>Copyright © Wyrez 2018</Copyright>
<PackageCopyright>Copyright © Wyrez 2018</PackageCopyright>
<PackageIconUrl>http://lib.arge.be/icon.png</PackageIconUrl>
<PackageProjectUrl>https://github.com/DarthAffe/RGB.NET</PackageProjectUrl>
<PackageLicenseUrl>https://raw.githubusercontent.com/DarthAffe/RGB.NET/master/LICENSE</PackageLicenseUrl>
<RepositoryType>Github</RepositoryType>
<RepositoryUrl>https://github.com/DarthAffe/RGB.NET</RepositoryUrl>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageReleaseNotes></PackageReleaseNotes>
<Version>0.0.1</Version>
<AssemblyVersion>0.0.1</AssemblyVersion>
<FileVersion>0.0.1</FileVersion>
<OutputPath>..\bin\</OutputPath>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<IncludeSource>True</IncludeSource>
<IncludeSymbols>True</IncludeSymbols>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<DefineConstants>NETCORE;NETSTANDARD;NETSTANDARD2_0</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'net45'">
<DefineConstants>NET45;NETFULL</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<DefineConstants>$(DefineConstants);TRACE;DEBUG</DefineConstants>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<NoWarn>$(NoWarn);CS1591;CS1572;CS1573</NoWarn>
<DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\RGB.NET.Core\RGB.NET.Core.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net45'">
<PackageReference Include="System.ValueTuple" Version="4.4.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="System.IO.Ports" Version="4.4.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generic/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@ -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<NEOPIXEL, PIN_CHANNEL_1>(leds_channel_1, LEDS_CHANNEL_1); }
if(LEDS_CHANNEL_2 > 0) { FastLED.addLeds<NEOPIXEL, PIN_CHANNEL_2>(leds_channel_2, LEDS_CHANNEL_2); }
if(LEDS_CHANNEL_3 > 0) { FastLED.addLeds<NEOPIXEL, PIN_CHANNEL_3>(leds_channel_3, LEDS_CHANNEL_3); }
if(LEDS_CHANNEL_4 > 0) { FastLED.addLeds<NEOPIXEL, PIN_CHANNEL_4>(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();
}
}

View File

@ -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<NEOPIXEL, LED_PIN>(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;
}
}
}

View File

@ -0,0 +1,110 @@
// ReSharper disable MemberCanBePrivate.Global
using System;
using System.Collections.Generic;
using RGB.NET.Core;
namespace RGB.NET.Devices.WS281X
{
/// <inheritdoc />
/// <summary>
/// Represents a device provider responsible for WS2812B- and WS2811-Led-devices.
/// </summary>
// ReSharper disable once InconsistentNaming
public class WS281XDeviceProvider : IRGBDeviceProvider
{
#region Properties & Fields
private static WS281XDeviceProvider _instance;
/// <summary>
/// Gets the singleton <see cref="WS281XDeviceProvider"/> instance.
/// </summary>
public static WS281XDeviceProvider Instance => _instance ?? new WS281XDeviceProvider();
/// <inheritdoc />
public bool IsInitialized { get; private set; }
/// <inheritdoc />
public IEnumerable<IRGBDevice> Devices { get; private set; }
/// <inheritdoc />
public bool HasExclusiveAccess => false;
/// <summary>
/// Gets a list of all defined device-definitions.
/// </summary>
// ReSharper disable once CollectionNeverUpdated.Global
// ReSharper disable once ReturnTypeCanBeEnumerable.Global
public List<IWS281XDeviceDefinition> DeviceDefinitions { get; } = new List<IWS281XDeviceDefinition>();
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="WS281XDeviceProvider"/> class.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if this constructor is called even if there is already an instance of this class.</exception>
public WS281XDeviceProvider()
{
if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(WS281XDeviceProvider)}");
_instance = this;
}
#endregion
#region Methods
/// <summary>
/// Adds the given <see cref="IWS281XDeviceDefinition" /> to this device-provider.
/// </summary>
/// <param name="deviceDefinition">The <see cref="IWS281XDeviceDefinition"/> to add.</param>
// ReSharper disable once UnusedMember.Global
public void AddDeviceDefinition(IWS281XDeviceDefinition deviceDefinition) => DeviceDefinitions.Add(deviceDefinition);
/// <inheritdoc />
public bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.Unknown, bool exclusiveAccessIfPossible = false, bool throwExceptions = false)
{
IsInitialized = false;
try
{
List<IRGBDevice> devices = new List<IRGBDevice>();
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;
}
/// <inheritdoc />
public void ResetDevices()
{ }
/// <inheritdoc />
public void Dispose()
{
if (IsInitialized)
foreach (IRGBDevice device in Devices)
if (device is IDisposable disposable)
disposable.Dispose();
}
#endregion
}
}

View File

@ -0,0 +1,27 @@
using RGB.NET.Core;
namespace RGB.NET.Devices.WS281X
{
/// <inheritdoc />
/// <summary>
/// Represents a device provider loaded used to dynamically load WS281X devices into an application.
/// </summary>
// ReSharper disable once UnusedMember.Global
// ReSharper disable once InconsistentNaming
public class WS281XDeviceProviderLoader : IRGBDeviceProviderLoader
{
#region Properties & Fields
/// <inheritdoc />
public bool RequiresInitialization => true;
#endregion
#region Methods
/// <inheritdoc />
public IRGBDeviceProvider GetDeviceProvider() => WS281XDeviceProvider.Instance;
#endregion
}
}

View File

@ -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}

View File

@ -342,6 +342,7 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>