mirror of
https://github.com/DarthAffe/RGB.NET.git
synced 2025-12-13 10:08:31 +00:00
Added a REST-API to NodeMCU devices and added using it as an optional update-mode
This commit is contained in:
parent
dd5fb6db89
commit
b8a3593792
21
RGB.NET.Devices.WS281X/NodeMCU/NodeMCUUpdateMode.cs
Normal file
21
RGB.NET.Devices.WS281X/NodeMCU/NodeMCUUpdateMode.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
namespace RGB.NET.Devices.WS281X.NodeMCU
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contaisn a list of possible update-modes for NodeMCU-devices.
|
||||||
|
/// </summary>
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
public enum NodeMCUUpdateMode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Updates through the HTTP-REST-API.
|
||||||
|
/// Slow, but reliable.
|
||||||
|
/// </summary>
|
||||||
|
Http,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates through a UDP-connection.
|
||||||
|
/// Fast, but might skip updates if the network connection is bad.
|
||||||
|
/// </summary>
|
||||||
|
Udp
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,10 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net.Http;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using RGB.NET.Core;
|
using RGB.NET.Core;
|
||||||
|
|
||||||
namespace RGB.NET.Devices.WS281X.NodeMCU
|
namespace RGB.NET.Devices.WS281X.NodeMCU
|
||||||
@ -13,94 +16,169 @@ namespace RGB.NET.Devices.WS281X.NodeMCU
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class NodeMCUWS2812USBUpdateQueue : UpdateQueue
|
public class NodeMCUWS2812USBUpdateQueue : UpdateQueue
|
||||||
{
|
{
|
||||||
#region Constants
|
|
||||||
|
|
||||||
private static readonly byte UPDATE_COMMAND = 0x02;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Properties & Fields
|
#region Properties & Fields
|
||||||
|
|
||||||
private readonly string _hostname;
|
private readonly string _hostname;
|
||||||
|
|
||||||
private readonly UdpClient _udpClient;
|
private HttpClient _httpClient = new HttpClient();
|
||||||
|
private UdpClient _udpClient;
|
||||||
|
|
||||||
private readonly Dictionary<int, byte[]> _dataBuffer = new Dictionary<int, byte[]>();
|
private readonly Dictionary<int, byte[]> _dataBuffer = new Dictionary<int, byte[]>();
|
||||||
private readonly Dictionary<int, byte> _sequenceNumbers = new Dictionary<int, byte>();
|
private readonly Dictionary<int, byte> _sequenceNumbers = new Dictionary<int, byte>();
|
||||||
|
|
||||||
|
private readonly Action<byte[]> _sendDataAction;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Constructors
|
#region Constructors
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="NodeMCUWS2812USBUpdateQueue"/> class.
|
/// Initializes a new instance of the <see cref="NodeMCUWS2812USBUpdateQueue"/> class.
|
||||||
|
/// If this constructor is used UDP updates are disabled.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="updateTrigger">The update trigger used by this queue.</param>
|
/// <param name="updateTrigger">The update trigger used by this queue.</param>
|
||||||
/// <param name="hostname">The hostname to connect to.</param>
|
/// <param name="hostname">The hostname to connect to.</param>
|
||||||
/// <param name="port">The port used by the web-connection.</param>
|
public NodeMCUWS2812USBUpdateQueue(IDeviceUpdateTrigger updateTrigger, string hostname)
|
||||||
public NodeMCUWS2812USBUpdateQueue(IDeviceUpdateTrigger updateTrigger, string hostname, int port)
|
|
||||||
: base(updateTrigger)
|
: base(updateTrigger)
|
||||||
{
|
{
|
||||||
this._hostname = hostname;
|
this._hostname = hostname;
|
||||||
|
|
||||||
_udpClient = new UdpClient(_hostname, port);
|
_sendDataAction = SendHttp;
|
||||||
_udpClient.Connect(_hostname, port);
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="NodeMCUWS2812USBUpdateQueue"/> class.
|
||||||
|
/// If this constructor is used UDP updates are enabled.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="updateTrigger">The update trigger used by this queue.</param>
|
||||||
|
/// <param name="hostname">The hostname to connect to.</param>
|
||||||
|
/// <param name="udpPort">The port used by the UDP-connection.</param>
|
||||||
|
public NodeMCUWS2812USBUpdateQueue(IDeviceUpdateTrigger updateTrigger, string hostname, int udpPort)
|
||||||
|
: base(updateTrigger)
|
||||||
|
{
|
||||||
|
this._hostname = hostname;
|
||||||
|
|
||||||
|
_udpClient = new UdpClient();
|
||||||
|
EnableUdp(udpPort);
|
||||||
|
|
||||||
|
_sendDataAction = SendUdp;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Methods
|
#region Methods
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnStartup(object sender, CustomUpdateData customData)
|
||||||
|
{
|
||||||
|
base.OnStartup(sender, customData);
|
||||||
|
|
||||||
|
ResetDevice();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Update(Dictionary<object, Color> dataSet)
|
protected override void Update(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))
|
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))
|
.GroupBy(x => x.Item1.channel))
|
||||||
{
|
{
|
||||||
int channel = channelData.Key;
|
byte[] buffer = GetBuffer(channelData);
|
||||||
byte[] buffer = _dataBuffer[channel];
|
_sendDataAction(buffer);
|
||||||
|
|
||||||
buffer[0] = GetSequenceNumber(channel);
|
|
||||||
buffer[1] = (byte)((channel << 4) | UPDATE_COMMAND);
|
|
||||||
int i = 2;
|
|
||||||
foreach ((byte _, byte r, byte g, byte b) in channelData.OrderBy(x => x.Item1.key)
|
|
||||||
.Select(x => x.Value.GetRGBBytes()))
|
|
||||||
{
|
|
||||||
buffer[i++] = r;
|
|
||||||
buffer[i++] = g;
|
|
||||||
buffer[i++] = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
Send(buffer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SendHttp(byte[] buffer)
|
||||||
|
{
|
||||||
|
string data = Convert.ToBase64String(buffer);
|
||||||
|
lock (_httpClient) _httpClient?.PostAsync(GetUrl("update"), new StringContent(data, Encoding.ASCII)).Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SendUdp(byte[] buffer)
|
||||||
|
{
|
||||||
|
_udpClient?.Send(buffer, buffer.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] GetBuffer(IGrouping<int, ((int channel, int key), Color Value)> data)
|
||||||
|
{
|
||||||
|
int channel = data.Key;
|
||||||
|
byte[] buffer = _dataBuffer[channel];
|
||||||
|
|
||||||
|
buffer[0] = GetSequenceNumber(channel);
|
||||||
|
buffer[1] = (byte)channel;
|
||||||
|
int i = 2;
|
||||||
|
foreach ((byte _, byte r, byte g, byte b) in data.OrderBy(x => x.Item1.key)
|
||||||
|
.Select(x => x.Value.GetRGBBytes()))
|
||||||
|
{
|
||||||
|
buffer[i++] = r;
|
||||||
|
buffer[i++] = g;
|
||||||
|
buffer[i++] = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
internal IEnumerable<(int channel, int ledCount)> GetChannels()
|
internal IEnumerable<(int channel, int ledCount)> GetChannels()
|
||||||
{
|
{
|
||||||
WebClient webClient = new WebClient();
|
string configString;
|
||||||
webClient.DownloadString($"http://{_hostname}/reset");
|
lock (_httpClient) configString = _httpClient.GetStringAsync(GetUrl("config")).Result;
|
||||||
|
|
||||||
int channelCount = int.Parse(webClient.DownloadString($"http://{_hostname}/channels"));
|
configString = configString.Replace(" ", "")
|
||||||
for (int channel = 1; channel <= channelCount; channel++)
|
.Replace("\r", "")
|
||||||
|
.Replace("\n", "");
|
||||||
|
|
||||||
|
//HACK DarthAffe 13.07.2020: Adding a JSON-Parser dependency just for this is not really worth it right now ...
|
||||||
|
MatchCollection channelMatches = Regex.Matches(configString, "\\{\"channel\":(?<channel>\\d+),\"leds\":(?<leds>\\d+)\\}");
|
||||||
|
foreach (Match channelMatch in channelMatches)
|
||||||
{
|
{
|
||||||
int ledCount = int.Parse(webClient.DownloadString($"http://{_hostname}/channel/{channel}"));
|
int channel = int.Parse(channelMatch.Groups["channel"].Value);
|
||||||
if (ledCount > 0)
|
int leds = int.Parse(channelMatch.Groups["leds"].Value);
|
||||||
|
if (leds > 0)
|
||||||
{
|
{
|
||||||
_dataBuffer[channel] = new byte[(ledCount * 3) + 2];
|
_dataBuffer[channel] = new byte[(leds * 3) + 2];
|
||||||
_sequenceNumbers[channel] = 0;
|
_sequenceNumbers[channel] = 0;
|
||||||
yield return (channel, ledCount);
|
yield return (channel, leds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Send(byte[] data) => _udpClient.Send(data, data.Length);
|
internal void ResetDevice()
|
||||||
|
{
|
||||||
|
lock (_httpClient) _httpClient.GetStringAsync(GetUrl("reset")).Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnableUdp(int port)
|
||||||
|
{
|
||||||
|
_httpClient.PostAsync(GetUrl("enableUDP"), new StringContent(port.ToString(), Encoding.UTF8, "application/json")).Result.Content.ReadAsStringAsync().Wait();
|
||||||
|
_udpClient.Connect(_hostname, port);
|
||||||
|
}
|
||||||
|
|
||||||
private byte GetSequenceNumber(int channel)
|
private byte GetSequenceNumber(int channel)
|
||||||
{
|
{
|
||||||
byte sequenceNumber = (byte)((_sequenceNumbers[channel] + 1) % byte.MaxValue);
|
byte sequenceNumber = (byte)Math.Max(1, (_sequenceNumbers[channel] + 1) % byte.MaxValue);
|
||||||
_sequenceNumbers[channel] = sequenceNumber;
|
_sequenceNumbers[channel] = sequenceNumber;
|
||||||
return sequenceNumber;
|
return sequenceNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
lock (_httpClient)
|
||||||
|
{
|
||||||
|
base.Dispose();
|
||||||
|
|
||||||
|
#if NETSTANDARD
|
||||||
|
_udpClient?.Dispose();
|
||||||
|
#endif
|
||||||
|
_udpClient = null;
|
||||||
|
|
||||||
|
ResetDevice();
|
||||||
|
_httpClient.Dispose();
|
||||||
|
_httpClient = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetUrl(string path) => $"http://{_hostname}/{path}";
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
// ReSharper disable UnusedMember.Global
|
// ReSharper disable UnusedMember.Global
|
||||||
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
|
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using RGB.NET.Core;
|
using RGB.NET.Core;
|
||||||
|
|
||||||
@ -22,9 +23,14 @@ namespace RGB.NET.Devices.WS281X.NodeMCU
|
|||||||
public string Hostname { get; }
|
public string Hostname { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the port of the UDP connection.
|
/// Gets or sets the port of the UDP connection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Port { get; }
|
public int Port { get; set; } = 1872;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the update-mode of the device.
|
||||||
|
/// </summary>
|
||||||
|
public NodeMCUUpdateMode UpdateMode { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the name used by this device.
|
/// Gets or sets the name used by this device.
|
||||||
@ -40,11 +46,11 @@ namespace RGB.NET.Devices.WS281X.NodeMCU
|
|||||||
/// Initializes a new instance of the <see cref="NodeMCUWS281XDeviceDefinition"/> class.
|
/// Initializes a new instance of the <see cref="NodeMCUWS281XDeviceDefinition"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="hostname">The hostname to connect to.</param>
|
/// <param name="hostname">The hostname to connect to.</param>
|
||||||
/// <param name="port">The port of the UDP connection.</param>
|
/// <param name="updateMode">The update mode of the device.</param>
|
||||||
public NodeMCUWS281XDeviceDefinition(string hostname, int port = 1872)
|
public NodeMCUWS281XDeviceDefinition(string hostname, NodeMCUUpdateMode updateMode = NodeMCUUpdateMode.Udp)
|
||||||
{
|
{
|
||||||
this.Hostname = hostname;
|
this.Hostname = hostname;
|
||||||
this.Port = port;
|
this.UpdateMode = updateMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -54,7 +60,13 @@ namespace RGB.NET.Devices.WS281X.NodeMCU
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IEnumerable<IRGBDevice> CreateDevices(IDeviceUpdateTrigger updateTrigger)
|
public IEnumerable<IRGBDevice> CreateDevices(IDeviceUpdateTrigger updateTrigger)
|
||||||
{
|
{
|
||||||
NodeMCUWS2812USBUpdateQueue queue = new NodeMCUWS2812USBUpdateQueue(updateTrigger, Hostname, Port);
|
NodeMCUWS2812USBUpdateQueue queue = UpdateMode switch
|
||||||
|
{
|
||||||
|
NodeMCUUpdateMode.Http => new NodeMCUWS2812USBUpdateQueue(updateTrigger, Hostname),
|
||||||
|
NodeMCUUpdateMode.Udp => new NodeMCUWS2812USBUpdateQueue(updateTrigger, Hostname, Port),
|
||||||
|
_ => throw new ArgumentOutOfRangeException()
|
||||||
|
};
|
||||||
|
|
||||||
IEnumerable<(int channel, int ledCount)> channels = queue.GetChannels();
|
IEnumerable<(int channel, int ledCount)> channels = queue.GetChannels();
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
foreach ((int channel, int ledCount) in channels)
|
foreach ((int channel, int ledCount) in channels)
|
||||||
|
|||||||
@ -64,6 +64,7 @@
|
|||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net45'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'net45'">
|
||||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||||
|
<PackageReference Include="Microsoft.Net.Http" Version="2.2.29" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
|
||||||
|
|||||||
295
RGB.NET.Devices.WS281X/Sketches/RGB.NET_NodeMCU.ino
Normal file
295
RGB.NET.Devices.WS281X/Sketches/RGB.NET_NodeMCU.ino
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
#define FASTLED_ESP8266_RAW_PIN_ORDER
|
||||||
|
|
||||||
|
#include "FastLED.h"
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include <ESP8266WebServer.h>
|
||||||
|
#include <WiFiUdp.h>
|
||||||
|
#include "base64.hpp"
|
||||||
|
|
||||||
|
//#### CONFIGURATION ####
|
||||||
|
|
||||||
|
// WLAN settings
|
||||||
|
const char* ssid = ""; // WLAN-network-name
|
||||||
|
const char* password = ""; // WLAN-password
|
||||||
|
|
||||||
|
#define CHANNELS 4 // change this only if you add or remove channels in the implementation-part. To disable channels set them to 0 leds.
|
||||||
|
|
||||||
|
// should not exceed 168 leds, since that results in the maximum paket size that is safe to transmit. Everything above could potentially be dropped.
|
||||||
|
// no more than 255 leds per channel (hard limit)
|
||||||
|
#define LEDS_CHANNEL_1 3
|
||||||
|
#define LEDS_CHANNEL_2 0
|
||||||
|
#define LEDS_CHANNEL_3 0
|
||||||
|
#define LEDS_CHANNEL_4 0
|
||||||
|
|
||||||
|
#define PIN_CHANNEL_1 15 // D8
|
||||||
|
#define PIN_CHANNEL_2 13 // D7
|
||||||
|
#define PIN_CHANNEL_3 12 // D6
|
||||||
|
#define PIN_CHANNEL_4 14 // D5
|
||||||
|
|
||||||
|
#define WEBSERVER_PORT 80
|
||||||
|
|
||||||
|
//#######################
|
||||||
|
|
||||||
|
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];
|
||||||
|
|
||||||
|
ESP8266WebServer server(80);
|
||||||
|
WiFiUDP Udp;
|
||||||
|
|
||||||
|
bool isUDPEnabled;
|
||||||
|
int udpPort;
|
||||||
|
byte incomingPacket[767]; // 255 (max leds) * 3 + 2 (header)
|
||||||
|
byte lastSequenceNumbers[CHANNELS];
|
||||||
|
|
||||||
|
bool checkSequenceNumber(int channel, byte currentSequenceNumber)
|
||||||
|
{
|
||||||
|
bool isValid = (currentSequenceNumber > lastSequenceNumbers[channel]) || ((lastSequenceNumbers[channel] > 200) && (currentSequenceNumber < 50));
|
||||||
|
if(isValid)
|
||||||
|
{
|
||||||
|
lastSequenceNumbers[channel] = currentSequenceNumber;
|
||||||
|
}
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void processUDP()
|
||||||
|
{
|
||||||
|
int packetSize = Udp.parsePacket();
|
||||||
|
if (packetSize)
|
||||||
|
{
|
||||||
|
// receive incoming UDP packets
|
||||||
|
byte sequenceNumber = Udp.read();
|
||||||
|
byte channel = Udp.read();
|
||||||
|
if(checkSequenceNumber(channel, sequenceNumber))
|
||||||
|
{
|
||||||
|
switch(channel)
|
||||||
|
{
|
||||||
|
case 1: // set leds of channel 1
|
||||||
|
Udp.read((uint8_t*)leds_channel_1, (LEDS_CHANNEL_1 * 3));
|
||||||
|
FastLED.show();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ### channel 2 ###
|
||||||
|
case 2: // set leds of channel 2
|
||||||
|
Udp.read((uint8_t*)leds_channel_2, (LEDS_CHANNEL_2 * 3));
|
||||||
|
FastLED.show();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ### channel 3 ###
|
||||||
|
case 3: // set leds of channel 3
|
||||||
|
Udp.read((uint8_t*)leds_channel_3, (LEDS_CHANNEL_3 * 3));
|
||||||
|
FastLED.show();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ### channel 4 ###
|
||||||
|
case 4: // set leds of channel 4
|
||||||
|
Udp.read((uint8_t*)leds_channel_4, (LEDS_CHANNEL_4 * 3));
|
||||||
|
FastLED.show();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ### default ###
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleRoot()
|
||||||
|
{
|
||||||
|
String infoSite = (String)"<html>\
|
||||||
|
<head>\
|
||||||
|
<title>RGB.NET</title>\
|
||||||
|
</head>\
|
||||||
|
<body>\
|
||||||
|
<h1>RGB.NET</h1>\
|
||||||
|
This device is currently running the NodeMCU WS281X RGB.NET-Sketch.<br />\
|
||||||
|
<br />\
|
||||||
|
Check <a href=\"https://github.com/DarthAffe/RGB.NET\">https://github.com/DarthAffe/RGB.NET</a> for more info and the latest version of this sketch.<br />\
|
||||||
|
<br />\
|
||||||
|
<h3>Configuration:</h3>\
|
||||||
|
<b>UDP:</b>\ " + (isUDPEnabled ? ((String)"enabled (" + udpPort + ")") : "disabled") + "<br />\
|
||||||
|
<br />\
|
||||||
|
<b>Channel 1</b><br />\
|
||||||
|
Leds: " + LEDS_CHANNEL_1 + "<br />\
|
||||||
|
Pin: " + PIN_CHANNEL_1 + "<br />\
|
||||||
|
<br />\
|
||||||
|
<b>Channel 2</b><br />\
|
||||||
|
Leds: " + LEDS_CHANNEL_2 + "<br />\
|
||||||
|
Pin: " + PIN_CHANNEL_2 + "<br />\
|
||||||
|
<br />\
|
||||||
|
<b>Channel 4</b><br />\
|
||||||
|
Leds: " + LEDS_CHANNEL_3 + "<br />\
|
||||||
|
Pin: " + PIN_CHANNEL_3 + "<br />\
|
||||||
|
<br />\
|
||||||
|
<b>Channel 4</b><br />\
|
||||||
|
Leds: " + LEDS_CHANNEL_4 + "<br />\
|
||||||
|
Pin: " + PIN_CHANNEL_4 + "<br />\
|
||||||
|
<br />\
|
||||||
|
</body>\
|
||||||
|
</html>";
|
||||||
|
|
||||||
|
server.send(200, "text/html", infoSite);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleConfig()
|
||||||
|
{
|
||||||
|
String config = (String)"{\
|
||||||
|
\"channels\": [\
|
||||||
|
{\
|
||||||
|
\"channel\": 1,\
|
||||||
|
\"leds\": " + LEDS_CHANNEL_1 + "\
|
||||||
|
},\
|
||||||
|
{\
|
||||||
|
\"channel\": 2,\
|
||||||
|
\"leds\": " + LEDS_CHANNEL_2 + "\
|
||||||
|
},\
|
||||||
|
{\
|
||||||
|
\"channel\": 3,\
|
||||||
|
\"leds\": " + LEDS_CHANNEL_3 + "\
|
||||||
|
},\
|
||||||
|
{\
|
||||||
|
\"channel\": 4,\
|
||||||
|
\"leds\": " + LEDS_CHANNEL_4 + "\
|
||||||
|
}\
|
||||||
|
]\
|
||||||
|
}";
|
||||||
|
|
||||||
|
server.send(200, "application/json", config);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleEnableUDP()
|
||||||
|
{
|
||||||
|
if(isUDPEnabled)
|
||||||
|
{
|
||||||
|
Udp.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
udpPort = server.arg(0).toInt();
|
||||||
|
|
||||||
|
Udp.begin(udpPort);
|
||||||
|
isUDPEnabled = true;
|
||||||
|
|
||||||
|
server.send(200, "text/html", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleDisableUDP()
|
||||||
|
{
|
||||||
|
if(isUDPEnabled)
|
||||||
|
{
|
||||||
|
Udp.stop();
|
||||||
|
isUDPEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.send(200, "text/html", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleReset()
|
||||||
|
{
|
||||||
|
for(int i = 0; i < CHANNELS; i++)
|
||||||
|
{
|
||||||
|
lastSequenceNumbers[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < LEDS_CHANNEL_1; i++)
|
||||||
|
{
|
||||||
|
leds_channel_1[i] = CRGB::Black;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < LEDS_CHANNEL_2; i++)
|
||||||
|
{
|
||||||
|
leds_channel_2[i] = CRGB::Black;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < LEDS_CHANNEL_3; i++)
|
||||||
|
{
|
||||||
|
leds_channel_3[i] = CRGB::Black;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < LEDS_CHANNEL_4; i++)
|
||||||
|
{
|
||||||
|
leds_channel_4[i] = CRGB::Black;
|
||||||
|
}
|
||||||
|
|
||||||
|
FastLED.show();
|
||||||
|
|
||||||
|
server.send(200, "text/html", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleUpdate()
|
||||||
|
{
|
||||||
|
unsigned int dataLength = decode_base64((unsigned char*)server.arg(0).c_str(), incomingPacket);
|
||||||
|
|
||||||
|
byte channel = (byte)incomingPacket[1];
|
||||||
|
switch(channel)
|
||||||
|
{
|
||||||
|
case 1: // set leds of channel 1
|
||||||
|
memcpy((uint8_t*)leds_channel_1, &incomingPacket[2], (LEDS_CHANNEL_1 * 3));
|
||||||
|
FastLED.show();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ### channel 2 ###
|
||||||
|
case 2: // set leds of channel 2
|
||||||
|
memcpy((uint8_t*)leds_channel_2, &incomingPacket[2], (LEDS_CHANNEL_2 * 3));
|
||||||
|
FastLED.show();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ### channel 3 ###
|
||||||
|
case 3: // set leds of channel 3
|
||||||
|
memcpy((uint8_t*)leds_channel_3, &incomingPacket[2], (LEDS_CHANNEL_3 * 3));
|
||||||
|
FastLED.show();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ### channel 4 ###
|
||||||
|
case 4: // set leds of channel 4
|
||||||
|
memcpy((uint8_t*)leds_channel_4, &incomingPacket[2], (LEDS_CHANNEL_4 * 3));
|
||||||
|
FastLED.show();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ### default ###
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.send(200, "text/html", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
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); }
|
||||||
|
|
||||||
|
WiFi.begin(ssid, password);
|
||||||
|
while (WiFi.status() != WL_CONNECTED)
|
||||||
|
{
|
||||||
|
delay(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(100);
|
||||||
|
|
||||||
|
server.on("/", handleRoot);
|
||||||
|
server.on("/config", handleConfig);
|
||||||
|
server.on("/enableUDP", handleEnableUDP);
|
||||||
|
server.on("/disableUDP", handleDisableUDP);
|
||||||
|
server.on("/reset", handleReset);
|
||||||
|
server.on("/update", handleUpdate);
|
||||||
|
server.onNotFound(handleRoot);
|
||||||
|
|
||||||
|
server.begin();
|
||||||
|
|
||||||
|
handleReset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
server.handleClient();
|
||||||
|
|
||||||
|
if(isUDPEnabled)
|
||||||
|
{
|
||||||
|
processUDP();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -1,210 +0,0 @@
|
|||||||
#define FASTLED_ESP8266_RAW_PIN_ORDER
|
|
||||||
|
|
||||||
#include "FastLED.h"
|
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
#include <WiFiUdp.h>
|
|
||||||
|
|
||||||
//#### CONFIGURATION ####
|
|
||||||
|
|
||||||
// WLAN settings
|
|
||||||
const char* ssid = ""; // WLAN-network-name
|
|
||||||
const char* password = ""; // WLAN-password
|
|
||||||
|
|
||||||
#define CHANNELS 4 // change this only if you add or remove channels in the implementation-part. To disable channels set them to 0 leds.
|
|
||||||
|
|
||||||
// should not exceed 168 leds, since that results in the maximum paket size that is safe to transmit. Everything above could potentially be dropped.
|
|
||||||
// no more than 255 leds per channel (hard limit)
|
|
||||||
#define LEDS_CHANNEL_1 3
|
|
||||||
#define LEDS_CHANNEL_2 0
|
|
||||||
#define LEDS_CHANNEL_3 0
|
|
||||||
#define LEDS_CHANNEL_4 0
|
|
||||||
|
|
||||||
#define PIN_CHANNEL_1 15 // D8
|
|
||||||
#define PIN_CHANNEL_2 13 // D7
|
|
||||||
#define PIN_CHANNEL_3 12 // D6
|
|
||||||
#define PIN_CHANNEL_4 14 // D5
|
|
||||||
|
|
||||||
#define PORT 1872 // if changed needs to be configured in RGB.NET (default: 1872)
|
|
||||||
|
|
||||||
#define WEBSERVER_PORT 80
|
|
||||||
|
|
||||||
//#######################
|
|
||||||
|
|
||||||
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];
|
|
||||||
|
|
||||||
WiFiServer server(WEBSERVER_PORT);
|
|
||||||
WiFiUDP Udp;
|
|
||||||
|
|
||||||
byte incomingPacket[767]; // 255 (max leds) * 3 + 2 (header)
|
|
||||||
byte lastSequenceNumbers[CHANNELS];
|
|
||||||
|
|
||||||
String header;
|
|
||||||
|
|
||||||
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); }
|
|
||||||
|
|
||||||
for(int i = 0; i < CHANNELS; i++)
|
|
||||||
{
|
|
||||||
lastSequenceNumbers[i] = 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
WiFi.begin(ssid, password);
|
|
||||||
while (WiFi.status() != WL_CONNECTED)
|
|
||||||
{
|
|
||||||
delay(500);
|
|
||||||
}
|
|
||||||
delay(100);
|
|
||||||
|
|
||||||
Udp.begin(PORT);
|
|
||||||
server.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool checkSequenceNumber(int channel, byte currentSequenceNumber)
|
|
||||||
{
|
|
||||||
bool isValid = (currentSequenceNumber > lastSequenceNumbers[channel]) || ((lastSequenceNumbers[channel] > 200) && (currentSequenceNumber < 50));
|
|
||||||
if(isValid)
|
|
||||||
{
|
|
||||||
lastSequenceNumbers[channel] = currentSequenceNumber;
|
|
||||||
}
|
|
||||||
return isValid;
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
// Web client
|
|
||||||
WiFiClient client = server.available();
|
|
||||||
if (client)
|
|
||||||
{
|
|
||||||
String currentLine = "";
|
|
||||||
while (client.connected())
|
|
||||||
{
|
|
||||||
if (client.available())
|
|
||||||
{
|
|
||||||
char c = client.read();
|
|
||||||
header += c;
|
|
||||||
if (c == '\n') {
|
|
||||||
if (currentLine.length() == 0) {
|
|
||||||
client.println("HTTP/1.1 200 OK");
|
|
||||||
client.println("Content-type:text/html");
|
|
||||||
client.println("Connection: close");
|
|
||||||
client.println();
|
|
||||||
|
|
||||||
if (header.indexOf("GET /reset") >= 0)
|
|
||||||
{
|
|
||||||
for(int i = 0; i < CHANNELS; i++)
|
|
||||||
{
|
|
||||||
lastSequenceNumbers[i] = 255;
|
|
||||||
}
|
|
||||||
for(int i = 0; i < LEDS_CHANNEL_1; i++)
|
|
||||||
{
|
|
||||||
leds_channel_1[i] = CRGB::Black;
|
|
||||||
}
|
|
||||||
for(int i = 0; i < LEDS_CHANNEL_2; i++)
|
|
||||||
{
|
|
||||||
leds_channel_2[i] = CRGB::Black;
|
|
||||||
}
|
|
||||||
for(int i = 0; i < LEDS_CHANNEL_3; i++)
|
|
||||||
{
|
|
||||||
leds_channel_3[i] = CRGB::Black;
|
|
||||||
}
|
|
||||||
for(int i = 0; i < LEDS_CHANNEL_4; i++)
|
|
||||||
{
|
|
||||||
leds_channel_4[i] = CRGB::Black;
|
|
||||||
}
|
|
||||||
FastLED.show();
|
|
||||||
}
|
|
||||||
else if (header.indexOf("GET /channels") >= 0)
|
|
||||||
{
|
|
||||||
client.println(CHANNELS);
|
|
||||||
}
|
|
||||||
else if (header.indexOf("GET /channel/1") >= 0)
|
|
||||||
{
|
|
||||||
client.println(LEDS_CHANNEL_1);
|
|
||||||
}
|
|
||||||
else if (header.indexOf("GET /channel/2") >= 0)
|
|
||||||
{
|
|
||||||
client.println(LEDS_CHANNEL_2);
|
|
||||||
}
|
|
||||||
else if (header.indexOf("GET /channel/3") >= 0)
|
|
||||||
{
|
|
||||||
client.println(LEDS_CHANNEL_3);
|
|
||||||
}
|
|
||||||
else if (header.indexOf("GET /channel/4") >= 0)
|
|
||||||
{
|
|
||||||
client.println(LEDS_CHANNEL_4);
|
|
||||||
}
|
|
||||||
client.println();
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
currentLine = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (c != '\r')
|
|
||||||
{
|
|
||||||
currentLine += c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
header = "";
|
|
||||||
client.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Color update
|
|
||||||
int packetSize = Udp.parsePacket();
|
|
||||||
if (packetSize)
|
|
||||||
{
|
|
||||||
// receive incoming UDP packets
|
|
||||||
byte sequenceNumber = Udp.read();
|
|
||||||
byte command = Udp.read();
|
|
||||||
switch(command)
|
|
||||||
{
|
|
||||||
// ### channel 1 ###
|
|
||||||
case 0x12: // set leds of channel 1
|
|
||||||
if(checkSequenceNumber(0, sequenceNumber))
|
|
||||||
{
|
|
||||||
Udp.read(((uint8_t*)leds_channel_1), (LEDS_CHANNEL_1 * 3));
|
|
||||||
FastLED.show();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// ### channel 2 ###
|
|
||||||
case 0x22: // set leds of channel 2
|
|
||||||
if(checkSequenceNumber(1, sequenceNumber))
|
|
||||||
{
|
|
||||||
Udp.read(((uint8_t*)leds_channel_2), (LEDS_CHANNEL_2 * 3));
|
|
||||||
FastLED.show();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// ### channel 3 ###
|
|
||||||
case 0x32: // set leds of channel 3
|
|
||||||
if(checkSequenceNumber(2, sequenceNumber))
|
|
||||||
{
|
|
||||||
Udp.read(((uint8_t*)leds_channel_3), (LEDS_CHANNEL_3 * 3));
|
|
||||||
FastLED.show();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// ### channel 4 ###
|
|
||||||
case 0x42: // set leds of channel 4
|
|
||||||
if(checkSequenceNumber(3, sequenceNumber))
|
|
||||||
{
|
|
||||||
Udp.read(((uint8_t*)leds_channel_4), (LEDS_CHANNEL_4 * 3));
|
|
||||||
FastLED.show();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// ### default ###
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -4,6 +4,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using RGB.NET.Core;
|
using RGB.NET.Core;
|
||||||
|
using RGB.NET.Devices.WS281X.NodeMCU;
|
||||||
|
|
||||||
namespace RGB.NET.Devices.WS281X
|
namespace RGB.NET.Devices.WS281X
|
||||||
{
|
{
|
||||||
@ -105,7 +106,11 @@ namespace RGB.NET.Devices.WS281X
|
|||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void ResetDevices()
|
public void ResetDevices()
|
||||||
{ }
|
{
|
||||||
|
foreach (IRGBDevice device in Devices)
|
||||||
|
if (device is NodeMCUWS2812USBDevice nodemcuDevice)
|
||||||
|
nodemcuDevice.UpdateQueue.ResetDevice();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user