diff --git a/Console/Common/BaseCommand.cs b/Console/Common/BaseCommand.cs index aa6cf83a8..73bec384e 100644 --- a/Console/Common/BaseCommand.cs +++ b/Console/Common/BaseCommand.cs @@ -82,22 +82,54 @@ protected List GetRenderers(IConfiguration config, HashSet } } - if (config.ZeDMD.Enabled) - { - var zeDMD = ZeDMD.GetInstance(); - if (zeDMD.IsAvailable) - { + if (config.ZeDMD.Enabled) { + var zeDMD = ZeDMD.GetInstance(config.ZeDMD.Debug, config.ZeDMD.Brightness, config.ZeDMD.RgbOrder, config.ZeDMDHD.Port); + if (zeDMD.IsAvailable) { renderers.Add(zeDMD); Logger.Info("Added ZeDMD renderer."); reportingTags.Add("Out:ZeDMD"); Analytics.Instance.AddDestination(zeDMD); - } - else - { + } else { Logger.Warn("Device {0} is not available.", zeDMD); } } + if (config.ZeDMDHD.Enabled) { + var zeDMDHD = ZeDMDHD.GetInstance(config.ZeDMDHD.Debug, config.ZeDMDHD.Brightness, config.ZeDMDHD.RgbOrder, config.ZeDMDHD.Port, config.ZeDMDHD.ScaleRgb24); + if (zeDMDHD.IsAvailable) { + renderers.Add(zeDMDHD); + Logger.Info("Added ZeDMD renderer."); + reportingTags.Add("Out:ZeDMD"); + Analytics.Instance.AddDestination(zeDMDHD); + } else { + Logger.Warn("Device {0} is not available.", zeDMDHD); + } + } + + if (config.ZeDMDWiFi.Enabled) { + var zeDMDWiFi = ZeDMDWiFi.GetInstance(config.ZeDMDWiFi.Debug, config.ZeDMDWiFi.Brightness, config.ZeDMDWiFi.RgbOrder, config.ZeDMDHD.Port, config.ZeDMDWiFi.WifiAddress, config.ZeDMDWiFi.WifiPort, config.ZeDMDWiFi.WifiSsid, config.ZeDMDWiFi.WifiPassword); + if (zeDMDWiFi.IsAvailable) { + renderers.Add(zeDMDWiFi); + Logger.Info("Added ZeDMD WiFi renderer."); + reportingTags.Add("Out:ZeDMDWiFi"); + Analytics.Instance.AddDestination(zeDMDWiFi); + } else { + Logger.Warn("Device {0} is not available.", zeDMDWiFi); + } + } + + if (config.ZeDMDHDWiFi.Enabled) { + var zeDMDHDWiFi = ZeDMDHDWiFi.GetInstance(config.ZeDMDHDWiFi.Debug, config.ZeDMDHDWiFi.Brightness, config.ZeDMDHDWiFi.RgbOrder, config.ZeDMDHD.Port, config.ZeDMDHD.ScaleRgb24, config.ZeDMDHDWiFi.WifiAddress, config.ZeDMDHDWiFi.WifiPort, config.ZeDMDHDWiFi.WifiSsid, config.ZeDMDHDWiFi.WifiPassword); + if (zeDMDHDWiFi.IsAvailable) { + renderers.Add(zeDMDHDWiFi); + Logger.Info("Added ZeDMD HD WiFi renderer."); + reportingTags.Add("Out:ZeDMDHDWiFi"); + Analytics.Instance.AddDestination(zeDMDHDWiFi); + } else { + Logger.Warn("Device {0} is not available.", zeDMDHDWiFi); + } + } + if (config.Pin2Dmd.Enabled) { var pin2Dmd = Pin2Dmd.GetInstance(config.Pin2Dmd.Delay); if (pin2Dmd.IsAvailable) { diff --git a/Console/Common/BaseOptions.cs b/Console/Common/BaseOptions.cs index f3e590148..3203db162 100644 --- a/Console/Common/BaseOptions.cs +++ b/Console/Common/BaseOptions.cs @@ -5,12 +5,13 @@ using LibDmd.Input; using LibDmd.Output.Virtual.AlphaNumeric; using LibDmd.Output.Virtual.Dmd; +using LibDmd.Output.ZeDMD; namespace DmdExt.Common { internal abstract class BaseOptions : IConfiguration { - [Option('d', "destination", HelpText = "The destination where the DMD data is sent to. One of: [ auto, pindmdv1, pindmdv2, pindmdv3, pin2dmd, virtual, alphanumeric, network ]. Default: \"virtual\".")] + [Option('d', "destination", HelpText = "The destination where the DMD data is sent to. One of: [ auto, pindmdv1, pindmdv2, pindmdv3, zedmd, zdmdhd, zedmdwifi, zedmdhdwifi, pin2dmd, virtual, alphanumeric, network ]. Default: \"virtual\".")] public DestinationType Destination { get; set; } = DestinationType.Virtual; [Option('r', "resize", HelpText = "How the source image is resized. One of: [ stretch, fill, fit ]. Default: \"stretch\".")] @@ -141,6 +142,30 @@ internal abstract class BaseOptions : IConfiguration [Option("--pac-key", HelpText = "Key to decrypt PAC files, in hex.")] public string PacKey { get; set; } = null; + [Option("zedmd-debug", HelpText = "If set, ZeDMD will show its debug informations. Default: false.")] + public bool Debug { get; set; } = false; + + [Option("zedmd-brightness", HelpText = "Change ZeDMD brightness between 0 and 15.")] + public int Brightness { get; set; } = -1; + + [Option("zedmd-rgborder", HelpText = "Change ZeDMD RGB order between 0 and 5.")] + public int RgbOrder { get; set; } = -1; + + [Option("zedmd-scalergb24", HelpText = "Upscale pure RGB24 content on ZeDMD HD. Default: true.")] + public bool ScaleRgb24 { get; set; } = true; + + [Option("zedmd-wifi-address", HelpText = "Connect to ZeDMD in WiFi mode using this IP address.")] + public string WifiAddress { get; set; } = null; + + [Option("zedmd-wifi-port", HelpText = "Connect to ZeDMD in WiFi mode using this port. Default: 3333.")] + public int WifiPort { get; set; } = 3333; + + [Option("zedmd-wifi-ssid", HelpText = "Configure ZeDMD to use this SSID for WiFi mode.")] + public string WifiSsid { get; set; } = null; + + [Option("zedmd-wifi-port", HelpText = "Configure ZeDMD to use this password for WiFi mode.")] + public string WifiPassword { get; set; } = null; + public IGlobalConfig Global { get; } public IVirtualDmdConfig VirtualDmd { get; } public IVirtualAlphaNumericDisplayConfig VirtualAlphaNumericDisplay { get; } @@ -148,6 +173,9 @@ internal abstract class BaseOptions : IConfiguration public IPinDmd2Config PinDmd2 { get; } public IPinDmd3Config PinDmd3 { get; } public IZeDMDConfig ZeDMD { get; } + public IZeDMDConfig ZeDMDHD { get; } + public IZeDMDWiFiConfig ZeDMDWiFi { get; } + public IZeDMDWiFiConfig ZeDMDHDWiFi { get; } public IPin2DmdConfig Pin2Dmd { get; } public IPixelcadeConfig Pixelcade { get; } public IVideoConfig Video { get; } @@ -170,6 +198,9 @@ protected BaseOptions() PinDmd2 = new PinDmd2Options(this); PinDmd3 = new PinDmd3Options(this); ZeDMD = new ZeDMDOptions(this); + ZeDMDHD = new ZeDMDHDOptions(this); + ZeDMDWiFi = new ZeDMDWiFiOptions(this); + ZeDMDHDWiFi = new ZeDMDHDWiFiOptions(this); Pin2Dmd = new Pin2DmdOptions(this); Pixelcade = new PixelcadeOptions(this); Video = new VideoOptions(); @@ -184,7 +215,7 @@ protected BaseOptions() public enum DestinationType { - Auto, PinDMDv1, PinDMDv2, PinDMDv3, zeDMD, PIN2DMD, PIN2DMDXL, PIN2DMDHD, PIXELCADE, Virtual, AlphaNumeric, Network + Auto, PinDMDv1, PinDMDv2, PinDMDv3, zeDMD, zeDMDHD, zeDMDWiFi, zeDMDHDWiFi, PIN2DMD, PIN2DMDXL, PIN2DMDHD, PIXELCADE, Virtual, AlphaNumeric, Network } public void Validate() @@ -369,7 +400,73 @@ public ZeDMDOptions(BaseOptions options) public bool Enabled => _options.Destination == BaseOptions.DestinationType.Auto || _options.Destination == BaseOptions.DestinationType.zeDMD; + public bool Debug => _options.Debug; + public int Brightness => _options.Brightness; + public int RgbOrder => _options.RgbOrder; + public string Port => _options.Port; + public bool ScaleRgb24 => _options.ScaleRgb24; + } + + internal class ZeDMDHDOptions : IZeDMDConfig + { + private readonly BaseOptions _options; + + public ZeDMDHDOptions(BaseOptions options) + { + _options = options; + } + + public bool Enabled => _options.Destination == BaseOptions.DestinationType.Auto || + _options.Destination == BaseOptions.DestinationType.zeDMDHD; + public bool Debug => _options.Debug; + public int Brightness => _options.Brightness; + public int RgbOrder => _options.RgbOrder; + public string Port => _options.Port; + public bool ScaleRgb24 => _options.ScaleRgb24; + } + + internal class ZeDMDWiFiOptions : IZeDMDWiFiConfig + { + private readonly BaseOptions _options; + + public ZeDMDWiFiOptions(BaseOptions options) + { + _options = options; + } + + public bool Enabled => _options.Destination == BaseOptions.DestinationType.Auto || + _options.Destination == BaseOptions.DestinationType.zeDMDWiFi; + public bool Debug => _options.Debug; + public int Brightness => _options.Brightness; + public int RgbOrder => _options.RgbOrder; + public string Port => _options.Port; + public bool ScaleRgb24 => _options.ScaleRgb24; + public string WifiAddress => _options.WifiAddress; + public int WifiPort => _options.WifiPort; + public string WifiSsid => _options.WifiSsid; + public string WifiPassword => _options.WifiPassword; + } + + internal class ZeDMDHDWiFiOptions : IZeDMDWiFiConfig + { + private readonly BaseOptions _options; + + public ZeDMDHDWiFiOptions(BaseOptions options) + { + _options = options; + } + + public bool Enabled => _options.Destination == BaseOptions.DestinationType.Auto || + _options.Destination == BaseOptions.DestinationType.zeDMDHDWiFi; + public bool Debug => _options.Debug; + public int Brightness => _options.Brightness; + public int RgbOrder => _options.RgbOrder; public string Port => _options.Port; + public bool ScaleRgb24 => _options.ScaleRgb24; + public string WifiAddress => _options.WifiAddress; + public int WifiPort => _options.WifiPort; + public string WifiSsid => _options.WifiSsid; + public string WifiPassword => _options.WifiPassword; } internal class Pin2DmdOptions : IPin2DmdConfig diff --git a/LibDmd.Test/DmdDevice/TestConfiguration.cs b/LibDmd.Test/DmdDevice/TestConfiguration.cs index d53f369bb..ee428c372 100644 --- a/LibDmd.Test/DmdDevice/TestConfiguration.cs +++ b/LibDmd.Test/DmdDevice/TestConfiguration.cs @@ -16,6 +16,9 @@ public class TestConfiguration : IConfiguration public IPinDmd2Config PinDmd2 { get; set; } = new TestPinDmd2Config(); public IPinDmd3Config PinDmd3 { get; set; } = new TestPinDmd3Config(); public IZeDMDConfig ZeDMD { get; set; } = new TestZeDMDConfig(); + public IZeDMDConfig ZeDMDHD { get; set; } = new TestZeDMDConfig(); + public IZeDMDWiFiConfig ZeDMDWiFi { get; set; } = new TestZeDMDWiFiConfig(); + public IZeDMDWiFiConfig ZeDMDHDWiFi { get; set; } = new TestZeDMDWiFiConfig(); public IPin2DmdConfig Pin2Dmd { get; set; } = new TestPin2DmdConfig(); public IPixelcadeConfig Pixelcade { get; set; } = new TestPixelcadeConfig(); public IVideoConfig Video { get; set; } = new TestVideoConfig(); @@ -104,9 +107,21 @@ public class TestPinDmd3Config : IPinDmd3Config public class TestZeDMDConfig : IZeDMDConfig { public bool Enabled { get; set; } + public bool Debug { get; set; } + public int Brightness { get; set; } + public int RgbOrder { get; set; } public string Port { get; set; } + public bool ScaleRgb24 { get; set; } } - + + public class TestZeDMDWiFiConfig : TestZeDMDConfig, IZeDMDWiFiConfig + { + public string WifiAddress { get; set; } + public int WifiPort { get; set; } + public string WifiSsid { get; set; } + public string WifiPassword { get; set; } + } + public class TestPin2DmdConfig : IPin2DmdConfig { public bool Enabled { get; set; } diff --git a/LibDmd/Costura32/libminiz-3.0.2.dll b/LibDmd/Costura32/libminiz-3.0.2.dll deleted file mode 100644 index 3d1b1b2c6..000000000 Binary files a/LibDmd/Costura32/libminiz-3.0.2.dll and /dev/null differ diff --git a/LibDmd/Costura32/libserialport.dll b/LibDmd/Costura32/libserialport.dll new file mode 100644 index 000000000..bc838d04e Binary files /dev/null and b/LibDmd/Costura32/libserialport.dll differ diff --git a/LibDmd/Costura32/zedmd.dll b/LibDmd/Costura32/zedmd.dll new file mode 100644 index 000000000..b2508e231 Binary files /dev/null and b/LibDmd/Costura32/zedmd.dll differ diff --git a/LibDmd/Costura64/libminiz-3.0.2.dll b/LibDmd/Costura64/libminiz-3.0.2.dll deleted file mode 100644 index 0f083fcbb..000000000 Binary files a/LibDmd/Costura64/libminiz-3.0.2.dll and /dev/null differ diff --git a/LibDmd/Costura64/libserialport64.dll b/LibDmd/Costura64/libserialport64.dll new file mode 100644 index 000000000..c64894b72 Binary files /dev/null and b/LibDmd/Costura64/libserialport64.dll differ diff --git a/LibDmd/Costura64/zedmd64.dll b/LibDmd/Costura64/zedmd64.dll new file mode 100644 index 000000000..4128b59c1 Binary files /dev/null and b/LibDmd/Costura64/zedmd64.dll differ diff --git a/LibDmd/DmdDevice/Configuration.cs b/LibDmd/DmdDevice/Configuration.cs index e0142c221..bedd175a6 100644 --- a/LibDmd/DmdDevice/Configuration.cs +++ b/LibDmd/DmdDevice/Configuration.cs @@ -29,6 +29,9 @@ public class Configuration : IConfiguration public IPinDmd2Config PinDmd2 { get; private set; } public IPinDmd3Config PinDmd3 { get; private set; } public IZeDMDConfig ZeDMD { get; private set; } + public IZeDMDConfig ZeDMDHD { get; private set; } + public IZeDMDWiFiConfig ZeDMDWiFi { get; private set; } + public IZeDMDWiFiConfig ZeDMDHDWiFi { get; private set; } public IPin2DmdConfig Pin2Dmd { get; private set; } public IPixelcadeConfig Pixelcade { get; private set; } public IVideoConfig Video { get; private set; } @@ -135,6 +138,9 @@ private void SetupConfig() PinDmd2 = new PinDmd2Config(_data, this); PinDmd3 = new PinDmd3Config(_data, this); ZeDMD = new ZeDMDConfig(_data, this); + ZeDMDHD = new ZeDMDHDConfig(_data, this); + ZeDMDWiFi = new ZeDMDWiFiConfig(_data, this); + ZeDMDHDWiFi = new ZeDMDHDWiFiConfig(_data, this); Pin2Dmd = new Pin2DmdConfig(_data, this); Pixelcade = new PixelcadeConfig(_data, this); Video = new VideoConfig(_data, this); @@ -302,12 +308,47 @@ public class ZeDMDConfig : AbstractConfiguration, IZeDMDConfig { public override string Name { get; } = "zedmd"; public bool Enabled => GetBoolean("enabled", false); - public bool AllowHdScaling => GetBoolean("scaletohd", true); + public bool Debug => GetBoolean("debug", false); + public int Brightness => GetInt("brightness", -1); + public int RgbOrder => GetInt("rgborder", -1); + public string Port => GetString("port", null); + public bool ScaleRgb24 => GetBoolean("scalergb24", true); + public bool AllowHdScaling => false; public ZeDMDConfig(IniData data, Configuration parent) : base(data, parent) { } } + public class ZeDMDHDConfig : ZeDMDConfig + { + public override string Name { get; } = "zedmdhd"; + public new bool AllowHdScaling => true; + public ZeDMDHDConfig(IniData data, Configuration parent) : base(data, parent) + { + } + } + + public class ZeDMDWiFiConfig : ZeDMDConfig, IZeDMDWiFiConfig + { + public override string Name { get; } = "zedmdwifi"; + public string WifiAddress => GetString("wifi.address", null); + public int WifiPort => GetInt("wifi.port", 3333); + public string WifiSsid => GetString("wifi.ssid", null); + public string WifiPassword => GetString("wifi.password", null); + public ZeDMDWiFiConfig(IniData data, Configuration parent) : base(data, parent) + { + } + } + + public class ZeDMDHDWiFiConfig : ZeDMDWiFiConfig + { + public override string Name { get; } = "zedmdhdwifi"; + public new bool AllowHdScaling => true; + public ZeDMDHDWiFiConfig(IniData data, Configuration parent) : base(data, parent) + { + } + } + public class Pin2DmdConfig : AbstractConfiguration, IPin2DmdConfig { public override string Name { get; } = "pin2dmd"; diff --git a/LibDmd/DmdDevice/DmdDevice.cs b/LibDmd/DmdDevice/DmdDevice.cs index a78a87682..72124bf68 100644 --- a/LibDmd/DmdDevice/DmdDevice.cs +++ b/LibDmd/DmdDevice/DmdDevice.cs @@ -466,7 +466,7 @@ private void SetupGraphs() } } if (_config.ZeDMD.Enabled) { - var zeDmd = ZeDMD.GetInstance(); + var zeDmd = ZeDMD.GetInstance(_config.ZeDMD.Debug, _config.ZeDMD.Brightness, _config.ZeDMD.RgbOrder, _config.ZeDMD.Port); if (zeDmd.IsAvailable) { renderers.Add(zeDmd); Logger.Info("Added ZeDMD renderer."); @@ -474,6 +474,33 @@ private void SetupGraphs() Analytics.Instance.AddDestination(zeDmd); } } + if (_config.ZeDMDHD.Enabled) { + var zeDmdHd = ZeDMDHD.GetInstance(_config.ZeDMDHD.Debug, _config.ZeDMDHD.Brightness, _config.ZeDMDHD.RgbOrder, _config.ZeDMDHD.Port, _config.ZeDMDHD.ScaleRgb24); + if (zeDmdHd.IsAvailable) { + renderers.Add(zeDmdHd); + Logger.Info("Added ZeDMD HD renderer."); + ReportingTags.Add("Out:ZeDMDHD"); + Analytics.Instance.AddDestination(zeDmdHd); + } + } + if (_config.ZeDMDWiFi.Enabled) { + var zeDmdWifi = ZeDMDWiFi.GetInstance(_config.ZeDMDWiFi.Debug, _config.ZeDMDWiFi.Brightness, _config.ZeDMDWiFi.RgbOrder, _config.ZeDMDWiFi.Port, _config.ZeDMDWiFi.WifiAddress, _config.ZeDMDWiFi.WifiPort, _config.ZeDMDWiFi.WifiSsid, _config.ZeDMDWiFi.WifiPassword); + if (zeDmdWifi.IsAvailable) { + renderers.Add(zeDmdWifi); + Logger.Info("Added ZeDMD WiFi renderer."); + ReportingTags.Add("Out:ZeDMDWiFi"); + Analytics.Instance.AddDestination(zeDmdWifi); + } + } + if (_config.ZeDMDHDWiFi.Enabled) { + var zeDmdHdWifi = ZeDMDHDWiFi.GetInstance(_config.ZeDMDHDWiFi.Debug, _config.ZeDMDHDWiFi.Brightness, _config.ZeDMDHDWiFi.RgbOrder, _config.ZeDMDHDWiFi.Port, _config.ZeDMDHD.ScaleRgb24, _config.ZeDMDHDWiFi.WifiAddress, _config.ZeDMDHDWiFi.WifiPort, _config.ZeDMDHDWiFi.WifiSsid, _config.ZeDMDHDWiFi.WifiPassword); + if (zeDmdHdWifi.IsAvailable) { + renderers.Add(zeDmdHdWifi); + Logger.Info("Added ZeDMD HD WiFi renderer."); + ReportingTags.Add("Out:ZeDMDHDWiFi"); + Analytics.Instance.AddDestination(zeDmdHdWifi); + } + } if (_config.Pin2Dmd.Enabled) { var pin2Dmd = Pin2Dmd.GetInstance(_config.Pin2Dmd.Delay); if (pin2Dmd.IsAvailable) { diff --git a/LibDmd/DmdDevice/IConfiguration.cs b/LibDmd/DmdDevice/IConfiguration.cs index 7d1cc81ec..91ac173a3 100644 --- a/LibDmd/DmdDevice/IConfiguration.cs +++ b/LibDmd/DmdDevice/IConfiguration.cs @@ -15,6 +15,9 @@ public interface IConfiguration IPinDmd2Config PinDmd2 { get; } IPinDmd3Config PinDmd3 { get; } IZeDMDConfig ZeDMD { get; } + IZeDMDConfig ZeDMDHD { get; } + IZeDMDWiFiConfig ZeDMDWiFi { get; } + IZeDMDWiFiConfig ZeDMDHDWiFi { get; } IPin2DmdConfig Pin2Dmd { get; } IPixelcadeConfig Pixelcade { get; } IVideoConfig Video { get; } @@ -68,6 +71,19 @@ public interface IPinDmd3Config public interface IZeDMDConfig { bool Enabled { get; } + bool Debug { get; } + int Brightness { get; } + int RgbOrder { get; } + string Port { get; } + bool ScaleRgb24 { get; } + } + + public interface IZeDMDWiFiConfig : IZeDMDConfig + { + string WifiAddress { get; } + int WifiPort { get; } + string WifiSsid { get; } + string WifiPassword { get; } } public interface IPin2DmdConfig diff --git a/LibDmd/LibDmd.csproj b/LibDmd/LibDmd.csproj index 934ecf736..34376ea03 100644 --- a/LibDmd/LibDmd.csproj +++ b/LibDmd/LibDmd.csproj @@ -558,8 +558,12 @@ VirtualDmdControl.xaml + + + + + - @@ -600,7 +604,8 @@ - + + @@ -611,7 +616,8 @@ - + + diff --git a/LibDmd/Output/ZeDMD/ZeDMD.cs b/LibDmd/Output/ZeDMD/ZeDMD.cs index 7b280806c..970a94294 100644 --- a/LibDmd/Output/ZeDMD/ZeDMD.cs +++ b/LibDmd/Output/ZeDMD/ZeDMD.cs @@ -1,287 +1,95 @@ -using System.Linq; -using System.Windows.Media; -using LibDmd.Common; -using LibDmd.Frame; -using NLog; +using LibDmd.Frame; namespace LibDmd.Output.ZeDMD { /// /// ZeDMD - real DMD with LED matrix display controlled with a cheap ESP32. - /// So you need 2 64x32 pannels compatible (check the Readme.md in the link above), the main constraint is to have a 1/16 scan. - /// To build the ZeDMD HD version you either need 2 128x64 or 4 64x54 panels. - /// Check "ZeDMD Project Page" (https://github.com/zesinger/ZeDMD_ESP32) for details. + /// Check "ZeDMD Project Page" https://github.com/PPUC/ZeDMD) for details. + /// This implementation supports ZeDMD and ZeDMD HD. /// - public class ZeDMD : IGray2Destination, IGray4Destination, IColoredGray2Destination, IColoredGray4Destination, IColoredGray6Destination, IRawOutput, IResizableDestination + public class ZeDMD : ZeDMDBase, IGray2Destination, IGray4Destination, IColoredGray2Destination, IColoredGray4Destination, IColoredGray6Destination, IMultiSizeDestination, IColorRotationDestination { - public string Name => "ZeDMD"; - public bool IsAvailable { get; private set; } - public bool NeedsDuplicateFrames => false; + public override string Name => "ZeDMD"; - public int Delay { get; set; } = 100; - - public Dimensions FixedSize { get; private set; } = Dimensions.Standard; - - // We get a DMD_ESP32 instance to communicate with ESP32 - private readonly ZeDMDComm pDMD = new ZeDMDComm(); + // To leverage ZeDMD's own advanced downscaling we can't use FixedSize and RGB24Stream like ZeDMD HD. + // By not declaring 192x62 supported, we get a centered 256x64 frame. + public Dimensions[] Sizes { get; } = { new Dimensions(128, 16), Dimensions.Standard, new Dimensions(256, 64) }; private static ZeDMD _instance; - private byte[] _frameBuffer; - - private const byte RGB24 = 3; - private const byte ColGray6 = 11; - //private const byte Gray4 = 7; - private const byte ColGray4 = 9; - private const byte Gray2 = 8; - - private Color[] _currentPalette = ColorUtil.GetPalette(new[] { Colors.Black, Colors.OrangeRed }, 4); - - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - private const int MAX_COLOR_ROTATIONS = 8; // maximum amount of color rotations per frame - - private Dimensions RomDimensions = Dimensions.Standard; // The size of the frames in the rom + private Dimensions _currentDimensions = Dimensions.Standard; /// - /// Returns the current instance of the PinDMD API. + /// Returns the current instance of ZeDMD. /// /// New or current instance - public static ZeDMD GetInstance() + public static ZeDMD GetInstance(bool debug, int brightness, int rgbOrder, string port) { if (_instance == null) { - _instance = new ZeDMD(); + _instance = new ZeDMD { Debug = debug, Brightness = brightness, RgbOrder = rgbOrder, Port = port }; + _instance.Init(); } - else _instance.Init(); - return _instance; - } - /// - /// Constructor, initializes the DMD. - /// - private ZeDMD() - { - Init(); - // Different modes require different buffer sizes. This one should be safe for all. - _frameBuffer = new byte[1 + FixedSize.Surface * 4]; - - if (IsAvailable) ClearColor(); + return _instance; } - public void SetDimensions(Dimensions newDim) + private new void Init() { - byte[] tdata = new byte[5]; - RomDimensions = newDim; - tdata[0] = 2; // send dimension mode - tdata[1] = (byte)(newDim.Width & 0xff); - tdata[2] = (byte)((newDim.Width >> 8) & 0xff); - tdata[3] = (byte)(newDim.Height & 0xff); - tdata[4] = (byte)((newDim.Height >> 8) & 0xff); - RenderRaw(tdata); + base.Init(); + OpenUSBConnection(); + SendConfiguration(); + ZeDMD_SetFrameSize(_pZeDMD, _currentDimensions.Width, _currentDimensions.Height); + ZeDMD_EnforceStreaming(_pZeDMD); + ZeDMD_EnablePreDownscaling(_pZeDMD); + ZeDMD_EnablePreUpscaling(_pZeDMD); } - public void Init() + private void SetDimensions(Dimensions newDim) { - IsAvailable = (pDMD.Open(out var width, out var height) == 1); - - if (!IsAvailable) { - Logger.Info(Name + " device not found"); - return; + if (_currentDimensions != newDim) { + _currentDimensions = newDim; + ZeDMD_SetFrameSize(_pZeDMD, newDim.Width, newDim.Height); } - - FixedSize = new Dimensions(width, height); - _frameBuffer = new byte[1 + FixedSize.Surface * 4]; - - Logger.Info($"{Name} device found on port {pDMD.nCOM} with a resolution of {FixedSize} LEDs"); } public void RenderGray2(DmdFrame frame) { - if (RomDimensions != frame.Dimensions) { - SetDimensions(frame.Dimensions); - } - frame.CopyPlanesTo(_frameBuffer, 13); - - // send frame buffer to device - RenderRaw(_frameBuffer, Gray2, 1 + 12 + RomDimensions.Surface / 4); + SetDimensions(frame.Dimensions); + ZeDMD_RenderGray2(_pZeDMD, frame.Data); } public void RenderColoredGray2(ColoredFrame frame) { - if (RomDimensions != frame.Dimensions) { - SetDimensions(frame.Dimensions); - } - - // copy palette - CopyPalette(frame.Palette, _frameBuffer, 4); - - // copy frame - frame.CopyPlanesTo(_frameBuffer, 13); - - // send frame buffer to device - RenderRaw(_frameBuffer, Gray2, 1 + 12 + RomDimensions.Surface / 4); - } - - private float CalcBrightness(float x) - { - // function to improve the brightness with fx=ax²+bc+c, f(0)=0, f(1)=1, f'(1.1)=0 - return (-x * x + 2.1f * x) / 1.1f; + SetDimensions(frame.Dimensions); + SetPalette(frame.Palette); + ZeDMD_RenderGray2(_pZeDMD, frame.Data); } public void RenderGray4(DmdFrame frame) { - if (RomDimensions != frame.Dimensions) { - SetDimensions(frame.Dimensions); - } - - frame.CopyPlanesTo(_frameBuffer, 49); - - // copy palette - for (int ti = 0; ti < 16; ti++) - { - _frameBuffer[1 + ti * 3] = (byte)(255.0f * CalcBrightness(ti / 15.0f)); - _frameBuffer[1 + ti * 3 + 1] = (byte)(109.0f * CalcBrightness(ti / 15.0f)); - _frameBuffer[1 + ti * 3 + 2] = (byte)(0.0f * CalcBrightness(ti / 15.0f)); - } - - // send frame buffer to device - RenderRaw(_frameBuffer, ColGray4, 1 + 48 + RomDimensions.Surface / 2); + SetDimensions(frame.Dimensions); + ZeDMD_RenderGray4(_pZeDMD, frame.Data); } public void RenderColoredGray4(ColoredFrame frame) { - if (RomDimensions != frame.Dimensions) { - SetDimensions(frame.Dimensions); - } - - // copy palette - CopyPalette(frame.Palette, _frameBuffer, 16); - - // copy frame - frame.CopyPlanesTo(_frameBuffer, 49); - - // send frame buffer to device - RenderRaw(_frameBuffer, ColGray4, 1 + 48 + RomDimensions.Surface / 2); + SetDimensions(frame.Dimensions); + SetPalette(frame.Palette); + ZeDMD_RenderGray4(_pZeDMD, frame.Data); } public void RenderColoredGray6(ColoredFrame frame) { - if (RomDimensions != frame.Dimensions) { - SetDimensions(frame.Dimensions); - } - - // copy palette - CopyPalette(frame.Palette, _frameBuffer, 64); - - // copy frame - frame.CopyPlanesTo(_frameBuffer, 193); - - // send frame buffer to device - if (frame.RotateColors) { - for (int ti = 0; ti < 3 * MAX_COLOR_ROTATIONS; ti++) { - _frameBuffer[ti + 1 + 192 + RomDimensions.Surface * 6 / 8] = frame.Rotations[ti]; - } - - } else { - for (int ti = 0; ti < MAX_COLOR_ROTATIONS; ti++) { - _frameBuffer[ti * 3 + 1 + 192 + RomDimensions.Surface * 6 / 8] = 255; - } - } - RenderRaw(_frameBuffer, ColGray6, 1 + 192 + 6 * RomDimensions.Surface / 8 + 3 * 8); - } - - private static void CopyPalette(Color[] palette,byte[] framebuffer,int ncol) - { - var paletteChanged = false; - for (var i = 0; i < ncol; i++) - { - var color = palette[i]; - var j = i * 3 + 1; - paletteChanged = paletteChanged || (framebuffer[j] != color.R || framebuffer[j + 1] != color.G || framebuffer[j + 2] != color.B); - framebuffer[j] = color.R; - framebuffer[j + 1] = color.G; - framebuffer[j + 2] = color.B; - } + SetDimensions(frame.Dimensions); + SetPalette(frame.Palette); + ZeDMD_RenderColoredGray6(_pZeDMD, frame.Data, frame.Rotations); + _lastFrame = (ColoredFrame)frame.Clone(); } public void RenderRgb24(DmdFrame frame) { - if (RomDimensions != frame.Dimensions) { - SetDimensions(frame.Dimensions); - } - - // can directly be sent to the device. - _frameBuffer[0] = RGB24; - - // copy data to frame buffer - frame.CopyDataTo(_frameBuffer, 1); - pDMD.QueueFrame(_frameBuffer.Take(RomDimensions.Surface * 3 + 1).ToArray()); - } - - private void RenderRaw(byte[] data, byte mode, int length) - { - data[0] = mode; - if (pDMD.Opened) - { - pDMD.QueueFrame(data.Take(length).ToArray()); - } - } - - public void RenderRaw(byte[] data) - { - if (pDMD.Opened) - { - pDMD.QueueFrame(data); - } - } - - public void ClearDisplay() - { - for (var i = 1; i < _frameBuffer.Length - 1; i++) - { - _frameBuffer[i] = 0; - } - byte[] tempbuf = new byte[1]; - tempbuf[0] = 10; // clear screen - pDMD.QueueFrame(tempbuf); - } - - public void SetColor(Color color) - { - _currentPalette = ColorUtil.GetPalette(new[] { Colors.Black, color }, 4); - WritePalette(_currentPalette); - } - - public void SetPalette(Color[] colors) - { - _currentPalette = ColorUtil.GetPalette(colors, 4); - WritePalette(_currentPalette); - } - - private void WritePalette(Color[] palette) - { - var pos = 1; - for (var i = 0; i < palette.Length; i++) - { - _frameBuffer[pos] = palette[i].R; - _frameBuffer[pos + 1] = palette[i].G; - _frameBuffer[pos + 2] = palette[i].B; - pos += 3; - } - } - - public void ClearPalette() - { - ClearColor(); - } - - public void ClearColor() - { - SetColor(RenderGraph.DefaultColor); - } - - public void Dispose() - { - pDMD.Close(); + SetDimensions(frame.Dimensions); + ZeDMD_RenderRgb24(_pZeDMD, frame.Data); } } } diff --git a/LibDmd/Output/ZeDMD/ZeDMDBase.cs b/LibDmd/Output/ZeDMD/ZeDMDBase.cs new file mode 100644 index 000000000..ac083dbf6 --- /dev/null +++ b/LibDmd/Output/ZeDMD/ZeDMDBase.cs @@ -0,0 +1,298 @@ +using System; +using System.Runtime.InteropServices; +using LibDmd.Common; +using System.Windows.Media; +using NLog; +using System.Linq; + +namespace LibDmd.Output.ZeDMD +{ + /// + /// ZeDMD - real DMD with LED matrix display controlled with a cheap ESP32. + /// Check "ZeDMD Project Page" https://github.com/PPUC/ZeDMD) for details. + /// This implementation supports ZeDMD and ZeDMD HD. + /// + /// DLL documentation can be found here: https://ppuc.github.io/libzedmd/docs/html/class_ze_d_m_d.html + /// + public abstract class ZeDMDBase + { + public abstract string Name { get; } + public bool IsAvailable { get; protected set; } + public bool NeedsDuplicateFrames => false; + protected bool Debug { get; set; } + protected int Brightness { get; set; } + protected int RgbOrder { get; set; } + protected string Port { get; set; } + protected bool ScaleRgb24 { get; set; } + + protected IntPtr _pZeDMD = IntPtr.Zero; + protected readonly Logger Logger = LogManager.GetCurrentClassLogger(); + protected ColoredFrame _lastFrame = null; + + protected void Init() + { + _pZeDMD = ZeDMD_GetInstance(); + } + + protected void OpenUSBConnection() + { + if (!string.IsNullOrEmpty(Port)) { + ZeDMD_SetDevice(_pZeDMD, @"\\.\" + Port); + } + + IsAvailable = ZeDMD_Open(_pZeDMD); + + if (!IsAvailable) { + Logger.Info(Name + " device not found"); + return; + } + Logger.Info(Name + " device found"); + } + + protected void SendConfiguration(bool save = false) + { + if (Debug) { + ZeDMD_EnableDebug(_pZeDMD); + } + if (Brightness >= 0 && Brightness <= 15) { + ZeDMD_SetBrightness(_pZeDMD, Brightness); + } + if (RgbOrder >= 0 && RgbOrder <= 5) { + ZeDMD_SetRGBOrder(_pZeDMD, RgbOrder); + } + if (save) { + ZeDMD_SaveSettings(_pZeDMD); + } + } + + public void ClearDisplay() + { + if (_pZeDMD != IntPtr.Zero) { + ZeDMD_ClearScreen(_pZeDMD); + } + } + + public void Dispose() + { + ClearDisplay(); + } + + public void SetPalette(Color[] colors) + { + if (_pZeDMD == IntPtr.Zero) { + return; + } + + byte[] paletteBuffer = Enumerable.Repeat((byte)0x0, 64 * 3).ToArray(); + var numOfColors = colors.Length; + + if (numOfColors != 4 && numOfColors != 16 && numOfColors != 64) { + return; + } + + // Custom palettes could be defined with less colors as required by the ROM. + // So we interpolate bigger palettes up to 64 colors. + while (numOfColors <= 64) { + var palette = ColorUtil.GetPalette(colors, numOfColors); + var pos = 0; + + foreach (var color in palette) { + paletteBuffer[pos++] = color.R; + paletteBuffer[pos++] = color.G; + paletteBuffer[pos++] = color.B; + } + ZeDMD_SetPalette(_pZeDMD, paletteBuffer, numOfColors); + + numOfColors *= 4; + } + } + + public void UpdatePalette(Color[] palette) + { + // For Rgb24, we get a new frame for each color rotation. + // But for ColoredGray6, we have to trigger the frame with + // an updated palette here. + if (_lastFrame != null) { + SetPalette(palette); + ZeDMD_RenderColoredGray6(_pZeDMD, _lastFrame.Data, _lastFrame.Rotations); + } + } + + public void SetColor(Color color) + { + SetPalette(ColorUtil.GetPalette(new[] { Colors.Black, color }, 4)); + } + + public void ClearPalette() + { + } + + public void ClearColor() + { + } + + #region libzedmd + + /// + /// libzedmd functions declarations + /// See https://ppuc.github.io/libzedmd/docs/html/class_ze_d_m_d.html + /// + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern IntPtr ZeDMD_GetInstance(); + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern bool ZeDMD_SetDevice(IntPtr pZeDMD, string device); + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern bool ZeDMD_Open(IntPtr pZeDMD); + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern void ZeDMD_Close(IntPtr pZeDMD); + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern void ZeDMD_EnableDebug(IntPtr pZeDMD); + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern void ZeDMD_EnablePreDownscaling(IntPtr pZeDMD); + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern void ZeDMD_DisablePreDownscaling(IntPtr pZeDMD); + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern void ZeDMD_EnablePreUpscaling(IntPtr pZeDMD); + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern void ZeDMD_DisablePreUpscaling(IntPtr pZeDMD); + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern void ZeDMD_EnableUpscaling(IntPtr pZeDMD); + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern void ZeDMD_DisableUpscaling(IntPtr pZeDMD); + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern void ZeDMD_EnforceStreaming(IntPtr pZeDMD); + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern void ZeDMD_SetBrightness(IntPtr pZeDMD, int brightness); + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern void ZeDMD_SetRGBOrder(IntPtr pZeDMD, int order); + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern void ZeDMD_SaveSettings(IntPtr pZeDMD); + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern void ZeDMD_SetFrameSize(IntPtr pZeDMD, int width, int height); + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern void ZeDMD_SetPalette(IntPtr pZeDMD, byte[] palette, int numColors); + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern void ZeDMD_ClearScreen(IntPtr pZeDMD); + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern void ZeDMD_RenderGray2(IntPtr pZeDMD, byte[] frame); + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern void ZeDMD_RenderGray4(IntPtr pZeDMD, byte[] frame); + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern void ZeDMD_RenderColoredGray6(IntPtr pZeDMD, byte[] frame, byte[] rotations); + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern void ZeDMD_RenderRgb24(IntPtr pZeDMD, byte[] frame); + + #endregion + + } +} diff --git a/LibDmd/Output/ZeDMD/ZeDMDComm.cs b/LibDmd/Output/ZeDMD/ZeDMDComm.cs deleted file mode 100644 index 1b5495290..000000000 --- a/LibDmd/Output/ZeDMD/ZeDMDComm.cs +++ /dev/null @@ -1,257 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.IO; -using System.IO.Ports; -using System.Linq; -using System.Threading.Tasks; -using NLog; - -namespace LibDmd.Output.ZeDMD -{ - public class ZeDMDComm // Class for locating the COM port of the ESP32 and communicating with it - { - public string nCOM; - public const int BaudRate = 921600; - private const int SERIAL_TIMEOUT = 120; - public bool Opened; - private SerialPort _serialPort; - private const int MAX_SERIAL_WRITE_AT_ONCE = 8192; - private const int SLOW_FRAMES_THRESHOLD = 4096; - private const int FRAME_BUFFER_SIZE = 128; - private const int COMMAND_SIZE_LIMIT = 100; // any buffer shorter then 100 is considered to be command like "reset palette" - public const int N_CTRL_CHARS = 6; - public const int N_INTERMEDIATE_CTR_CHARS = 4; - public static readonly byte[] CtrlCharacters = { 0x5a, 0x65, 0x64, 0x72, 0x75, 0x6d }; - - private BlockingCollection _frames = new BlockingCollection(FRAME_BUFFER_SIZE); - - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - - public ZeDMDComm() - { - Task.Run(() => - { - Logger.Info("Starting ZeDMD frame thread."); - bool sleep = false; - int bufferedFramesThreshold = FRAME_BUFFER_SIZE; - while (true) { - byte[] frame; - try { - frame = _frames.Take(); - if (frame.Length < COMMAND_SIZE_LIMIT) { - // we might have a new mode, try to avoid sleeps until an error occurs - sleep = false; - bufferedFramesThreshold = FRAME_BUFFER_SIZE; - } - else if (frame.Length > SLOW_FRAMES_THRESHOLD) { - sleep = false; - bufferedFramesThreshold = 4; - } - else { - bufferedFramesThreshold = 32; - } - - // in case of an error, activate sleeps to slow down - sleep = !StreamBytes(frame) || sleep; - - // for some modes it is important to let ZeDMD perform its rendering, before sending the next frame - if (sleep) { - System.Threading.Thread.Sleep(8); - } - } - catch (InvalidOperationException) { } - - // in case ZeDMD falls behind, drop some frames - while (_frames.Count >= bufferedFramesThreshold) { - // drop frame - frame = _frames.Take(); - if (frame.Length < COMMAND_SIZE_LIMIT) { - // in case of a command, drop all frames and re-add the command - while (_frames.Count > 0) { - _frames.Take(); - } - _frames.Add(frame); - } - } - } - }); - } - - public void QueueFrame(byte[] frame) - { - Task.Run(() => - { - if (_frames.Count < FRAME_BUFFER_SIZE) { - byte[] buffer; - - if (frame.Length > 1) { - byte[] pCompressedBytes; - NetMiniZ.NetMiniZ.MZCompress(frame.Skip(1).ToArray(), out pCompressedBytes); - - buffer = new byte[CtrlCharacters.Length + 1 + 2 + pCompressedBytes.Length]; - CtrlCharacters.CopyTo(buffer, 0); - buffer[CtrlCharacters.Length] = frame[0]; - buffer[CtrlCharacters.Length + 1] = (byte)((pCompressedBytes.Length >> 8) & 0xFF); - buffer[CtrlCharacters.Length + 2] = (byte)(pCompressedBytes.Length & 0xFF); - pCompressedBytes.CopyTo(buffer, CtrlCharacters.Length + 3); - } else { - buffer = new byte[CtrlCharacters.Length + 1]; - CtrlCharacters.CopyTo(buffer, 0); - frame.CopyTo(buffer, CtrlCharacters.Length); - } - - _frames.Add(buffer); - } - }); - } - - private void SafeClose() - { - try { - // In case of error discard serial data and close - //_serialPort.DiscardInBuffer(); - //_serialPort.DiscardOutBuffer(); - _serialPort.Close(); - System.Threading.Thread.Sleep(100); // otherwise the next device will fail - } - catch { - // do nothing - } - } - - private bool Connect(string port, out int width, out int height) - { - // Try to find an ESP32 on the COM port and check if it answers with the shake-hand bytes - try - { - _serialPort = new SerialPort(port, BaudRate, Parity.None, 8, StopBits.One) - { - ReadTimeout = SERIAL_TIMEOUT, - WriteBufferSize = MAX_SERIAL_WRITE_AT_ONCE, - WriteTimeout = SERIAL_TIMEOUT - }; - _serialPort.Open(); - _serialPort.Write(CtrlCharacters.Concat(new byte[] { 12 }).ToArray(), 0, CtrlCharacters.Length + 1); - System.Threading.Thread.Sleep(200); - var result = new byte[Math.Max(N_CTRL_CHARS + 1, N_INTERMEDIATE_CTR_CHARS + 4)]; - _serialPort.Read(result, 0, N_INTERMEDIATE_CTR_CHARS + 4); - System.Threading.Thread.Sleep(200); - if (!result.Take(4).SequenceEqual(CtrlCharacters.Take(4))) - { - SafeClose(); - width = 0; - height = 0; - return false; - } - width = result[N_INTERMEDIATE_CTR_CHARS] + result[N_INTERMEDIATE_CTR_CHARS + 1] * 256; - height = result[N_INTERMEDIATE_CTR_CHARS + 2] + result[N_INTERMEDIATE_CTR_CHARS + 3] * 256; - - if (_serialPort.ReadByte() == 'R') - { - // enable compression - _serialPort.Write(CtrlCharacters.Concat(new byte[] { 14 }).ToArray(), 0, CtrlCharacters.Length + 1); - System.Threading.Thread.Sleep(4); - - if (_serialPort.ReadByte() == 'A' && _serialPort.ReadByte() == 'R') - { - // increase serial transfer chunk size - - _serialPort.Write(CtrlCharacters.Concat(new byte[] { 13, MAX_SERIAL_WRITE_AT_ONCE / 256 }).ToArray(), 0, CtrlCharacters.Length + 2); - System.Threading.Thread.Sleep(4); - - if (_serialPort.ReadByte() == 'A') - { - nCOM = port; - Opened = true; - return true; - } - } - } - } - catch - { - if (_serialPort != null && _serialPort.IsOpen) SafeClose(); - } - width = 0; - height = 0; - return false; - } - - private void Disconnect() - { - Opened = false; - if (_serialPort != null) SafeClose(); - } - - private bool StreamBytes(byte[] pBytes) - { - if (_serialPort.IsOpen) - { - try - { - if (_serialPort.ReadByte() == 'R') - { - // send bytes - int position = 0; - while (position < pBytes.Length) { - _serialPort.Write(pBytes, position, ((pBytes.Length - position) < MAX_SERIAL_WRITE_AT_ONCE) ? (pBytes.Length - position) : MAX_SERIAL_WRITE_AT_ONCE); - if (_serialPort.ReadByte() == 'A') { - // Received (A)cknowledge, ready to send the next chunk. - position += MAX_SERIAL_WRITE_AT_ONCE; - } else { - // Something went wrong. Terminate current transmission of the buffer and return. - return false; - } - } - - return true; - } - } - catch (Exception e) - { - Logger.ForExceptionEvent(e).Log(); - } - } - - return false; - } - - public int Open(out int width, out int height) - { - // Try to find an ZeDMD on each COM port available - bool IsAvailable = false; - var ports = SerialPort.GetPortNames(); - width = 0; - height = 0; - foreach (var portName in ports) - { - IsAvailable = Connect(portName, out width, out height); - if (IsAvailable) break; - } - if (!IsAvailable) return 0; - - Opened = true; - return 1; - } - - public bool Close() - { - if (Opened) - { - Disconnect(); - } - - Opened = false; - return true; - } - - protected static byte[] Compress(byte[] inData) - { - using (var outStream = new MemoryStream()) - using (var inStream = new MemoryStream(inData)) { - NetMiniZ.NetMiniZ.Compress(inStream, outStream, 9); - return outStream.ToArray(); - } - } - } -} diff --git a/LibDmd/Output/ZeDMD/ZeDMDHD.cs b/LibDmd/Output/ZeDMD/ZeDMDHD.cs new file mode 100644 index 000000000..cfcd926f9 --- /dev/null +++ b/LibDmd/Output/ZeDMD/ZeDMDHD.cs @@ -0,0 +1,93 @@ +using LibDmd.Frame; + +namespace LibDmd.Output.ZeDMD +{ + /// + /// ZeDMD - real DMD with LED matrix display controlled with a cheap ESP32. + /// Check "ZeDMD Project Page" https://github.com/PPUC/ZeDMD) for details. + /// This implementation supports ZeDMD and ZeDMD HD. + /// + public class ZeDMDHD : ZeDMDBase, IGray2Destination, IGray4Destination, IColoredGray2Destination, IColoredGray4Destination, IColoredGray6Destination, IFixedSizeDestination, IColorRotationDestination + { + public override string Name => "ZeDMD HD"; + public virtual Dimensions FixedSize { get; } = new Dimensions(256, 64); + public virtual bool DmdAllowHdScaling { get; protected set; } = true; + + private static ZeDMDHD _instance; + + /// + /// Returns the current instance of ZeDMD. + /// + /// New or current instance + public static ZeDMDHD GetInstance(bool debug, int brightness, int rgbOrder, string port, bool scaleRgb24) + { + if (_instance == null) { + _instance = new ZeDMDHD { Debug = debug, Brightness = brightness, RgbOrder = rgbOrder, Port = port, ScaleRgb24 = scaleRgb24 }; + _instance.Init(); + } + + return _instance; + } + + /// + /// Constructor, initializes the DMD. + /// + protected ZeDMDHD() + { + } + + protected new void Init() + { + base.Init(); + OpenUSBConnection(); + SendConfiguration(); + ZeDMD_SetFrameSize(_pZeDMD, FixedSize.Width, FixedSize.Height); + } + + public void RenderGray2(DmdFrame frame) + { + DmdAllowHdScaling = true; + ZeDMD_EnablePreUpscaling(_pZeDMD); + ZeDMD_RenderGray2(_pZeDMD, frame.Data); + } + + public void RenderColoredGray2(ColoredFrame frame) + { + DmdAllowHdScaling = true; + ZeDMD_EnablePreUpscaling(_pZeDMD); + SetPalette(frame.Palette); + ZeDMD_RenderGray2(_pZeDMD, frame.Data); + } + + public void RenderGray4(DmdFrame frame) + { + DmdAllowHdScaling = true; + ZeDMD_DisablePreUpscaling(_pZeDMD); + ZeDMD_RenderGray4(_pZeDMD, frame.Data); + } + + public void RenderColoredGray4(ColoredFrame frame) + { + DmdAllowHdScaling = true; + ZeDMD_DisablePreUpscaling(_pZeDMD); + SetPalette(frame.Palette); + ZeDMD_RenderGray4(_pZeDMD, frame.Data); + } + + public void RenderColoredGray6(ColoredFrame frame) + { + DmdAllowHdScaling = true; + ZeDMD_EnablePreUpscaling(_pZeDMD); + SetPalette(frame.Palette); + ZeDMD_RenderColoredGray6(_pZeDMD, frame.Data, frame.Rotations); + _lastFrame = (ColoredFrame)frame.Clone(); + } + + public void RenderRgb24(DmdFrame frame) + { + DmdAllowHdScaling = ScaleRgb24; + ZeDMD_EnablePreUpscaling(_pZeDMD); + ZeDMD_RenderRgb24(_pZeDMD, frame.Data); + } + } +} diff --git a/LibDmd/Output/ZeDMD/ZeDMDHDWiFi.cs b/LibDmd/Output/ZeDMD/ZeDMDHDWiFi.cs new file mode 100644 index 000000000..8d18d2363 --- /dev/null +++ b/LibDmd/Output/ZeDMD/ZeDMDHDWiFi.cs @@ -0,0 +1,78 @@ +using LibDmd.Frame; + +namespace LibDmd.Output.ZeDMD +{ + /// + /// ZeDMD - real DMD with LED matrix display controlled with a cheap ESP32. + /// Check "ZeDMD Project Page" https://github.com/PPUC/ZeDMD) for details. + /// This implementation supports ZeDMD and ZeDMD HD. + /// + public class ZeDMDHDWiFi : ZeDMDWiFiBase, IGray2Destination, IGray4Destination, IColoredGray2Destination, IColoredGray4Destination, IColoredGray6Destination, IFixedSizeDestination, IColorRotationDestination + { + public override string Name => "ZeDMD HD WiFi"; + public virtual Dimensions FixedSize { get; } = new Dimensions(256, 64); + public virtual bool DmdAllowHdScaling { get; protected set; } = true; + + private static ZeDMDHDWiFi _instance; + + /// + /// Returns the current instance of ZeDMD. + /// + /// New or current instance + public static ZeDMDHDWiFi GetInstance(bool debug, int brightness, int rgbOrder, string port, bool scaleRgb24, string wifiAddress, int wifiPort, string wifiSsid, string wifiPassword) + { + if (_instance == null) { + _instance = new ZeDMDHDWiFi { Debug = debug, Brightness = brightness, RgbOrder = rgbOrder, Port = port, ScaleRgb24 = scaleRgb24, WifiAddress = wifiAddress, WifiPort = wifiPort, WifiSsid = wifiSsid, WifiPassword = wifiPassword }; + _instance.Init(); + } + + return _instance; + } + + private new void Init() + { + base.Init(); + ZeDMD_SetFrameSize(_pZeDMD, FixedSize.Width, FixedSize.Height); + } + + public void RenderGray2(DmdFrame frame) + { + DmdAllowHdScaling = true; + ZeDMD_RenderGray2(_pZeDMD, frame.Data); + } + + public void RenderColoredGray2(ColoredFrame frame) + { + DmdAllowHdScaling = true; + SetPalette(frame.Palette); + ZeDMD_RenderGray2(_pZeDMD, frame.Data); + } + + public void RenderGray4(DmdFrame frame) + { + DmdAllowHdScaling = true; + ZeDMD_RenderGray4(_pZeDMD, frame.Data); + } + + public void RenderColoredGray4(ColoredFrame frame) + { + DmdAllowHdScaling = true; + SetPalette(frame.Palette); + ZeDMD_RenderGray4(_pZeDMD, frame.Data); + } + + public void RenderColoredGray6(ColoredFrame frame) + { + DmdAllowHdScaling = true; + SetPalette(frame.Palette); + ZeDMD_RenderColoredGray6(_pZeDMD, frame.Data, frame.Rotations); + _lastFrame = (ColoredFrame)frame.Clone(); + } + + public void RenderRgb24(DmdFrame frame) + { + DmdAllowHdScaling = ScaleRgb24; + ZeDMD_RenderRgb24(_pZeDMD, frame.Data); + } + } +} diff --git a/LibDmd/Output/ZeDMD/ZeDMDWiFi.cs b/LibDmd/Output/ZeDMD/ZeDMDWiFi.cs new file mode 100644 index 000000000..04a6684f3 --- /dev/null +++ b/LibDmd/Output/ZeDMD/ZeDMDWiFi.cs @@ -0,0 +1,91 @@ +using LibDmd.Frame; + +namespace LibDmd.Output.ZeDMD +{ + /// + /// ZeDMD - real DMD with LED matrix display controlled with a cheap ESP32. + /// Check "ZeDMD Project Page" https://github.com/PPUC/ZeDMD) for details. + /// This implementation supports ZeDMD and ZeDMD HD. + /// + public class ZeDMDWiFi : ZeDMDWiFiBase, IGray2Destination, IGray4Destination, IColoredGray2Destination, IColoredGray4Destination, IColoredGray6Destination, IMultiSizeDestination, IColorRotationDestination + { + public override string Name => "ZeDMD WiFi"; + // To leverage ZeDMD's own advanced downscaling we can't use FixedSize and RGB24Stream like ZeDMD HD. + // By not declaring 192x62 supported, we get a centered 256x64 frame. + public Dimensions[] Sizes { get; } = { new Dimensions(128, 16), Dimensions.Standard, new Dimensions(256, 64) }; + + private static ZeDMDWiFi _instance; + private Dimensions _currentDimensions = Dimensions.Standard; + + /// + /// Returns the current instance of ZeDMD. + /// + /// New or current instance + public static ZeDMDWiFi GetInstance(bool debug, int brightness, int rgbOrder, string port, string wifiAddress, int wifiPort, string wifiSsid, string wifiPassword) + { + if (_instance == null) + { + _instance = new ZeDMDWiFi { Debug = debug, Brightness = brightness, RgbOrder = rgbOrder, Port = port, WifiAddress = wifiAddress, WifiPort = wifiPort, WifiSsid = wifiSsid, WifiPassword = wifiPassword }; + _instance.Init(); + } + + return _instance; + } + + private new void Init() + { + base.Init(); + ZeDMD_SetFrameSize(_pZeDMD, _currentDimensions.Width, _currentDimensions.Height); + ZeDMD_EnablePreDownscaling(_pZeDMD); + ZeDMD_EnablePreUpscaling(_pZeDMD); + } + + private void SetDimensions(Dimensions newDim) + { + if (_currentDimensions != newDim) { + _currentDimensions = newDim; + ZeDMD_SetFrameSize(_pZeDMD, newDim.Width, newDim.Height); + } + } + + public void RenderGray2(DmdFrame frame) + { + SetDimensions(frame.Dimensions); + ZeDMD_RenderGray2(_pZeDMD, frame.Data); + } + + public void RenderColoredGray2(ColoredFrame frame) + { + SetDimensions(frame.Dimensions); + SetPalette(frame.Palette); + ZeDMD_RenderGray2(_pZeDMD, frame.Data); + } + + public void RenderGray4(DmdFrame frame) + { + SetDimensions(frame.Dimensions); + ZeDMD_RenderGray4(_pZeDMD, frame.Data); + } + + public void RenderColoredGray4(ColoredFrame frame) + { + SetDimensions(frame.Dimensions); + SetPalette(frame.Palette); + ZeDMD_RenderGray4(_pZeDMD, frame.Data); + } + + public void RenderColoredGray6(ColoredFrame frame) + { + SetDimensions(frame.Dimensions); + SetPalette(frame.Palette); + ZeDMD_RenderColoredGray6(_pZeDMD, frame.Data, frame.Rotations); + _lastFrame = (ColoredFrame)frame.Clone(); + } + + public void RenderRgb24(DmdFrame frame) + { + SetDimensions(frame.Dimensions); + ZeDMD_RenderRgb24(_pZeDMD, frame.Data); + } + } +} diff --git a/LibDmd/Output/ZeDMD/ZeDMDWiFiBase.cs b/LibDmd/Output/ZeDMD/ZeDMDWiFiBase.cs new file mode 100644 index 000000000..32b140a91 --- /dev/null +++ b/LibDmd/Output/ZeDMD/ZeDMDWiFiBase.cs @@ -0,0 +1,90 @@ +using System.Runtime.InteropServices; +using System; +using NLog; + +namespace LibDmd.Output.ZeDMD +{ + /// + /// ZeDMD - real DMD with LED matrix display controlled with a cheap ESP32. + /// Check "ZeDMD Project Page" https://github.com/PPUC/ZeDMD) for details. + /// This implementation supports ZeDMD and ZeDMD HD. + /// + public abstract class ZeDMDWiFiBase : ZeDMDBase + { + protected string WifiAddress { get; set; } + protected int WifiPort { get; set; } + protected string WifiSsid { get; set; } + protected string WifiPassword { get; set; } + + protected new void Init() + { + base.Init(); + + if (string.IsNullOrEmpty(WifiSsid) && string.IsNullOrEmpty(WifiPassword) && !string.IsNullOrEmpty(WifiAddress) && WifiPort > 0) { + + IsAvailable = ZeDMD_OpenWiFi(_pZeDMD, WifiAddress, WifiPort); + } + else { + OpenUSBConnection(); + + if (IsAvailable && !string.IsNullOrEmpty(WifiSsid) && !string.IsNullOrEmpty(WifiPassword) && WifiPort > 0) { + ZeDMD_EnableDebug(_pZeDMD); + ZeDMD_SetWiFiSSID(_pZeDMD, WifiSsid); + ZeDMD_SetWiFiPassword(_pZeDMD, WifiPassword); + ZeDMD_SetWiFiPort(_pZeDMD, WifiPort); + ZeDMD_SaveSettings(_pZeDMD); + Logger.Info(Name + " WiFi credentials submitted"); + ZeDMD_Close(_pZeDMD); + IsAvailable = false; + return; + } + } + + if (!IsAvailable) + { + Logger.Info(Name + " device not found"); + return; + } + Logger.Info(Name + " device found"); + + SendConfiguration(); + } + + #region libzedmd + + /// + /// WiFi specific libzedmd functions declarations + /// See https://ppuc.github.io/libzedmd/docs/html/class_ze_d_m_d.html + /// + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern bool ZeDMD_OpenWiFi(IntPtr pZeDMD, string ip, int port); + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern void ZeDMD_SetWiFiSSID(IntPtr pZeDMD, string ssid); + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern void ZeDMD_SetWiFiPassword(IntPtr pZeDMD, string password); + +#if PLATFORM_X64 + [DllImport("zedmd64.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport("zedmd.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] +#endif + protected static extern void ZeDMD_SetWiFiPort(IntPtr pZeDMD, int port); + + #endregion + } +} diff --git a/PinMameDevice/DmdDevice.ini b/PinMameDevice/DmdDevice.ini index bab6ee53a..9e6d538bf 100644 --- a/PinMameDevice/DmdDevice.ini +++ b/PinMameDevice/DmdDevice.ini @@ -236,6 +236,93 @@ port = ; if false, doesn't bother looking for a ZeDMD enabled = false +; if true, ZeDMD displays its debug informations +;debug = false + +; optionally set the brightness from 0 to 15 +;brightness = 6 + +; optionally set the RGB order from 0 to 5 +;rgborder = 3 + +; optionally set the COM port and disable auto discovery in case it causes trouble with other devices +;port = COM3 + +[zedmdhd] + +; if false, doesn't bother looking for a ZeDMD HD +enabled = false + +; if true, ZeDMD displays its debug informations +;debug = false + +; optionally set the brightness from 0 to 15 +;brightness = 6 + +; optionally set the RGB order from 0 to 5 +;rgborder = 3 + +; optionally set the COM port and disable auto discovery in case it causes trouble with other devices +;port = COM3 + +; optionally disable upscaling of pur RGB24 content +;scaleRgb24 = false + +[zedmdwifi] + +; if false, doesn't bother looking for a ZeDMD WiFi +enabled = false + +; if true, ZeDMD displays its debug informations +;debug = false + +; optionally set the brightness from 0 to 15 +;brightness = 6 + +; optionally set the RGB order from 0 to 5 +;rgborder = 3 + +; optionally set the COM port and disable auto discovery in case it causes trouble with other devices +; (the COM connection via USB will be established to set the WiFi credentials below) +;port = COM3 + +; if set, the WiFi mode is used +;wifi.address = 192.168.0.27 +;wifi.port = 3333 + +; the "one time" config options +;wifi.ssid = YOUR_SECRET_SSID +;wifi.password = YOUR_SECRET_PASSWORD + +[zedmdhdwifi] + +; if false, doesn't bother looking for a ZeDMD HD WiFi +enabled = false + +; if true, ZeDMD displays its debug informations +;debug = false + +; optionally set the brightness from 0 to 15 +;brightness = 6 + +; optionally set the RGB order from 0 to 5 +;rgborder = 3 + +; optionally set the COM port and disable auto discovery in case it causes trouble with other devices +; (the COM connection via USB will be established to set the WiFi credentials below) +;port = COM3 + +; optionally disable upscaling of pur RGB24 content +;scaleRgb24 = false + +; if set, the WiFi mode is used +;wifi.address = 192.168.0.27 +;wifi.port = 3333 + +; the "one time" config options +;wifi.ssid = YOUR_SECRET_SSID +;wifi.password = YOUR_SECRET_PASSWORD + [pin2dmd] ; if false, doesn't bother looking for a PIN2DMD diff --git a/README.md b/README.md index de6bc2afd..13ce1190e 100644 --- a/README.md +++ b/README.md @@ -378,6 +378,18 @@ The output are described by block below. | `--port` | [pindmd3]
port | COM port, e.g. `COM3`. | | `-d pin2dmd` | [pin2dmd]
enabled | Enables the RGB24 PIN2DMD display. | | *n/a* | [pin2dmd]
delay | Delay in milliseconds to wait after loading a palette. | +| `-d zedmd` | [zedmd]
enabled | Enables the RGB24 ZeDMD display. | +| `-d zedmdhd` | [zedmdhd]
enabled | Enables the RGB24 ZeDMD HD display. | +| `-d zedmdwifi` | [zedmdwifi]
enabled | Enables the RGB24 ZeDMD WiFi display. | +| `-d zedmdhdwifi` | [zedmdhdwifi]
enabled | Enables the RGB24 ZeDMD HD WiFi display. | +| `--zedmd-debug` | [zedmd]
debug | Let any ZeDMD show its debug informations. | +| `--zedmd-brightness` | [zedmd]
brightness | Change any ZeDMD brightness between 0 and 15. | +| `--zedmd-rgborder` | [zedmd]
rgborder | Change any ZeDMD RGB order between 0 and 5. | +| `--zedmd-scalergb24` | [zedmd]
scalergb24 | Scale pure RGB24 content on ZeDMD HD. Default: true. | +| `--zedmd-wifi-address` | [zedmdwifi]
wifi.address | Connect to ZeDMD (HD) WiFi using this IP address. | +| `--zedmd-wifi-port` | [zedmdwifi]
wifi.port | Connect to ZeDMD (HD) WiFi using this port. Default: 3333. | +| `--zedmd-wifi-ssid` | [zedmdwifi]
wifi.ssid | Configure ZeDMD (HD) WiFi to use this SSID. | +| `--zedmd-wifi-password` | [zedmdwifi]
wifi.password | Configure ZeDMD (HD) WiFi to use this password. | | *n/a* | [video]
enabled | Enables creating an .avi video from the DMD frames. | | *n/a* | [video]
path | Path to folder or .avi file. If a folder is given, it will create a file named after the current game. | | *n/a* | [browserstream]
enabled | Enables streaming the DMD in real time to your browser in your LAN. | @@ -477,6 +489,43 @@ dmdext test dmdext test --format gray2 ``` +### ZeDMD + +The [ZeDMD library](https://github.com/PPUC/libzedmd) (`zedmd.dll`) is bundled with `DmdDevice.dll` and `dmdext.exe`. +However, you can also replace this library with a newer version if you wish. +To do that, download the latest DLL from [here](https://github.com/PPUC/libzedmd/releases) and place it alongside +with `DmdDevice.dll` or `dmdext.exe`. Note that this might break compatibility, so check your log if ZeDMD suddenly stops +working. + +#### USB mode + +This is the default maode for ZeDMD. You don't need to configuire anything except setting `enabled` to `true` in +`DmdDevice.ini` for `[zedmd]` or `[zedmdhd]`. +BUit in case you have multiple devices or run into issues with the auto detection and other USB devices attached, +you can set a conctrete COM port to use in `DmdDevice.ini` using `port`. + +#### WiFi mode + +Alternatively ZeDMD could be flashed with a firmware that provides a WiFi mode. +In `DmdDevice.ini` there're special devices named `[zedmdwifi]` and `[zedmdhdwifi]` to use it. + +To run the ZeDMD in WiFi mode it needs WiFi credentials to establish the network connection. +These could also be set once via the DMD Extension. +Run `dmdext.exe -d zedmd --zedmd-wifi-ssid YOUR_SECRET_SSID --zedmd-wifi-password YOUR_SECRET_PASSWORD`. +At the next start, ZeDMD will display the IP address it obtained from you WiFI network in the top left corner. +This address has to be added as `wifi.address` to `DmdDevice.ini`. + +You can also perform that configuration without `dmdext.exe` using this multi-step process: +First you have to add the SSID and the password in the zedmd section of `DmdDevice.ini` using +`wifi.ssid` and `wifi.password`. These will be submitted to ZeDMD the next time you start DMD Extensions. +Once done you should quit DMD Extensions. +Now you have to remove `wifi.ssid` and `wifi.password` from `DmdDevice.ini`. +At the next start, ZeDMD will display the IP address it obtained from you WiFI network in the top left corner. +This address has to be added as `wifi.address` to `DmdDevice.ini`. + +**ZeDMD will store the WiFi credentials internally! So ensure to erase them using one of the processes described +above before giving your ZeDMD to someone else!** + ### Colorization If you are a PC monitor user or have an RGB display (PinDMDv3, PIN2DMD, Pixelcade or ZeDMD), you can enable frame-by-frame @@ -570,6 +619,12 @@ upscaled. In the native VNI/PAL/PAC colorizer, you can choose which upscale algo ## Breaking Changes +### v2.x.x + +- In DmdDevice.ini, the `[zedmd]` has been split into `[zedmd]`, `[zedmdhd]`, `[zedmdwifi]`, `[zedmdhdwifi]`. +- ZeDMD HD devices don't use their built-in scaler anymore when attached to DMD Extension. They now leverage the common `scalermode` setting. +- ZeDMD firmware versions before 3.5.0 aren't supported anymore. + ### v2.3.0 - Removed --scale-to-hd in favor of --scaler-mode. Set scaler mode to `none` if you want to disable scaling.