From 186792fde8706d4ffe3e338df3b1efa1b9cc112f Mon Sep 17 00:00:00 2001 From: BlubbFish Date: Tue, 3 Dec 2019 18:43:54 +0100 Subject: [PATCH] Coding styles --- Unosquare.RaspberryIO/Camera/CameraColor.cs | 268 ++-- .../Camera/CameraController.cs | 400 +++--- Unosquare.RaspberryIO/Camera/CameraRect.cs | 162 +-- .../Camera/CameraSettingsBase.cs | 695 ++++----- .../Camera/CameraStillSettings.cs | 235 ++-- .../Camera/CameraVideoSettings.cs | 186 +-- Unosquare.RaspberryIO/Camera/Enums.cs | 798 ++++++----- Unosquare.RaspberryIO/Computer/DsiDisplay.cs | 140 +- .../Computer/NetworkAdapterInfo.cs | 85 +- .../Computer/NetworkSettings.cs | 512 ++++--- Unosquare.RaspberryIO/Computer/OsInfo.cs | 98 +- Unosquare.RaspberryIO/Computer/PiVersion.cs | 260 ++-- Unosquare.RaspberryIO/Computer/SystemInfo.cs | 679 +++++---- .../Computer/WirelessNetworkInfo.cs | 46 +- Unosquare.RaspberryIO/Gpio/Enums.cs | 1095 +++++++-------- Unosquare.RaspberryIO/Gpio/GpioController.cs | 1160 ++++++++------- Unosquare.RaspberryIO/Gpio/GpioPin.Factory.cs | 366 +++-- Unosquare.RaspberryIO/Gpio/GpioPin.cs | 1247 ++++++++--------- Unosquare.RaspberryIO/Gpio/I2CBus.cs | 180 ++- Unosquare.RaspberryIO/Gpio/I2CDevice.cs | 388 +++-- Unosquare.RaspberryIO/Gpio/SpiBus.cs | 138 +- Unosquare.RaspberryIO/Gpio/SpiChannel.cs | 302 ++-- Unosquare.RaspberryIO/Native/Delegates.cs | 21 +- .../Native/HardwareException.cs | 140 +- .../Native/HighResolutionTimer.cs | 56 +- Unosquare.RaspberryIO/Native/Standard.cs | 158 +-- Unosquare.RaspberryIO/Native/SystemName.cs | 87 +- Unosquare.RaspberryIO/Native/ThreadLockKey.cs | 48 +- Unosquare.RaspberryIO/Native/Timing.cs | 210 +-- Unosquare.RaspberryIO/Native/WiringPi.I2C.cs | 155 +- .../Native/WiringPi.SerialPort.cs | 143 +- .../Native/WiringPi.Shift.cs | 69 +- .../Native/WiringPi.SoftPwm.cs | 125 +- Unosquare.RaspberryIO/Native/WiringPi.Spi.cs | 103 +- Unosquare.RaspberryIO/Native/WiringPi.cs | 780 +++++------ Unosquare.RaspberryIO/Pi.cs | 231 +-- .../Resources/EmbeddedResources.cs | 114 +- 37 files changed, 5871 insertions(+), 6009 deletions(-) diff --git a/Unosquare.RaspberryIO/Camera/CameraColor.cs b/Unosquare.RaspberryIO/Camera/CameraColor.cs index 6fb0c9c..25431bb 100644 --- a/Unosquare.RaspberryIO/Camera/CameraColor.cs +++ b/Unosquare.RaspberryIO/Camera/CameraColor.cs @@ -1,134 +1,140 @@ -namespace Unosquare.RaspberryIO.Camera -{ - using Swan; - using System; - using System.Linq; - +using Unosquare.Swan; +using System; +using System.Linq; + +namespace Unosquare.RaspberryIO.Camera { + /// + /// A simple RGB color class to represent colors in RGB and YUV colorspaces. + /// + public class CameraColor { /// - /// A simple RGB color class to represent colors in RGB and YUV colorspaces. + /// Initializes a new instance of the class. /// - public class CameraColor - { - /// - /// Initializes a new instance of the class. - /// - /// The red. - /// The green. - /// The blue. - public CameraColor(int r, int g, int b) - : this(r, g, b, string.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The red. - /// The green. - /// The blue. - /// The well-known color name. - public CameraColor(int r, int g, int b, string name) - { - RGB = new[] { Convert.ToByte(r.Clamp(0, 255)), Convert.ToByte(g.Clamp(0, 255)), Convert.ToByte(b.Clamp(0, 255)) }; - - var y = (R * .299000f) + (G * .587000f) + (B * .114000f); - var u = (R * -.168736f) + (G * -.331264f) + (B * .500000f) + 128f; - var v = (R * .500000f) + (G * -.418688f) + (B * -.081312f) + 128f; - - YUV = new byte[] { (byte)y.Clamp(0, 255), (byte)u.Clamp(0, 255), (byte)v.Clamp(0, 255) }; - Name = name; - } - - #region Static Definitions - - /// - /// Gets the predefined white color. - /// - public static CameraColor White => new CameraColor(255, 255, 255, nameof(White)); - - /// - /// Gets the predefined red color. - /// - public static CameraColor Red => new CameraColor(255, 0, 0, nameof(Red)); - - /// - /// Gets the predefined green color. - /// - public static CameraColor Green => new CameraColor(0, 255, 0, nameof(Green)); - - /// - /// Gets the predefined blue color. - /// - public static CameraColor Blue => new CameraColor(0, 0, 255, nameof(Blue)); - - /// - /// Gets the predefined black color. - /// - public static CameraColor Black => new CameraColor(0, 0, 0, nameof(Black)); - - #endregion - - /// - /// Gets the well-known color name. - /// - public string Name { get; } - - /// - /// Gets the red byte. - /// - public byte R => RGB[0]; - - /// - /// Gets the green byte. - /// - public byte G => RGB[1]; - - /// - /// Gets the blue byte. - /// - public byte B => RGB[2]; - - /// - /// Gets the RGB byte array (3 bytes). - /// - public byte[] RGB { get; } - - /// - /// Gets the YUV byte array (3 bytes). - /// - public byte[] YUV { get; } - - /// - /// Returns a hexadecimal representation of the RGB byte array. - /// Preceded by 0x and all in lowercase - /// - /// if set to true [reverse]. - /// A string - public string ToRgbHex(bool reverse) - { - var data = RGB.ToArray(); - if (reverse) Array.Reverse(data); - return ToHex(data); - } - - /// - /// Returns a hexadecimal representation of the YUV byte array. - /// Preceded by 0x and all in lowercase - /// - /// if set to true [reverse]. - /// A string - public string ToYuvHex(bool reverse) - { - var data = YUV.ToArray(); - if (reverse) Array.Reverse(data); - return ToHex(data); - } - - /// - /// Returns a hexadecimal representation of the data byte array - /// - /// The data. - /// A string - private static string ToHex(byte[] data) => $"0x{BitConverter.ToString(data).Replace("-", string.Empty).ToLowerInvariant()}"; - } + /// The red. + /// The green. + /// The blue. + public CameraColor(Int32 r, Int32 g, Int32 b) + : this(r, g, b, String.Empty) { + } + + /// + /// Initializes a new instance of the class. + /// + /// The red. + /// The green. + /// The blue. + /// The well-known color name. + public CameraColor(Int32 r, Int32 g, Int32 b, String name) { + this.RGB = new[] { Convert.ToByte(r.Clamp(0, 255)), Convert.ToByte(g.Clamp(0, 255)), Convert.ToByte(b.Clamp(0, 255)) }; + + Single y = this.R * .299000f + this.G * .587000f + this.B * .114000f; + Single u = this.R * -.168736f + this.G * -.331264f + this.B * .500000f + 128f; + Single v = this.R * .500000f + this.G * -.418688f + this.B * -.081312f + 128f; + + this.YUV = new Byte[] { (Byte)y.Clamp(0, 255), (Byte)u.Clamp(0, 255), (Byte)v.Clamp(0, 255) }; + this.Name = name; + } + + #region Static Definitions + + /// + /// Gets the predefined white color. + /// + public static CameraColor White => new CameraColor(255, 255, 255, nameof(White)); + + /// + /// Gets the predefined red color. + /// + public static CameraColor Red => new CameraColor(255, 0, 0, nameof(Red)); + + /// + /// Gets the predefined green color. + /// + public static CameraColor Green => new CameraColor(0, 255, 0, nameof(Green)); + + /// + /// Gets the predefined blue color. + /// + public static CameraColor Blue => new CameraColor(0, 0, 255, nameof(Blue)); + + /// + /// Gets the predefined black color. + /// + public static CameraColor Black => new CameraColor(0, 0, 0, nameof(Black)); + + #endregion + + /// + /// Gets the well-known color name. + /// + public String Name { + get; + } + + /// + /// Gets the red byte. + /// + public Byte R => this.RGB[0]; + + /// + /// Gets the green byte. + /// + public Byte G => this.RGB[1]; + + /// + /// Gets the blue byte. + /// + public Byte B => this.RGB[2]; + + /// + /// Gets the RGB byte array (3 bytes). + /// + public Byte[] RGB { + get; + } + + /// + /// Gets the YUV byte array (3 bytes). + /// + public Byte[] YUV { + get; + } + + /// + /// Returns a hexadecimal representation of the RGB byte array. + /// Preceded by 0x and all in lowercase + /// + /// if set to true [reverse]. + /// A string + public String ToRgbHex(Boolean reverse) { + Byte[] data = this.RGB.ToArray(); + if(reverse) { + Array.Reverse(data); + } + + return ToHex(data); + } + + /// + /// Returns a hexadecimal representation of the YUV byte array. + /// Preceded by 0x and all in lowercase + /// + /// if set to true [reverse]. + /// A string + public String ToYuvHex(Boolean reverse) { + Byte[] data = this.YUV.ToArray(); + if(reverse) { + Array.Reverse(data); + } + + return ToHex(data); + } + + /// + /// Returns a hexadecimal representation of the data byte array + /// + /// The data. + /// A string + private static String ToHex(Byte[] data) => $"0x{BitConverter.ToString(data).Replace("-", String.Empty).ToLowerInvariant()}"; + } } \ No newline at end of file diff --git a/Unosquare.RaspberryIO/Camera/CameraController.cs b/Unosquare.RaspberryIO/Camera/CameraController.cs index ce42d68..e7db20a 100644 --- a/Unosquare.RaspberryIO/Camera/CameraController.cs +++ b/Unosquare.RaspberryIO/Camera/CameraController.cs @@ -1,216 +1,190 @@ -namespace Unosquare.RaspberryIO.Camera -{ - using Swan.Abstractions; - using System; - using Swan.Components; - using System.IO; - using System.Threading; - using System.Threading.Tasks; - +using Unosquare.Swan.Abstractions; +using System; +using Unosquare.Swan.Components; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Unosquare.RaspberryIO.Camera { + /// + /// The Raspberry Pi's camera controller wrapping raspistill and raspivid programs. + /// This class is a singleton + /// + public class CameraController : SingletonBase { + #region Private Declarations + + private static readonly ManualResetEventSlim OperationDone = new ManualResetEventSlim(true); + private static readonly Object SyncRoot = new Object(); + private static CancellationTokenSource _videoTokenSource = new CancellationTokenSource(); + private static Task _videoStreamTask; + + #endregion + + #region Properties + /// - /// The Raspberry Pi's camera controller wrapping raspistill and raspivid programs. - /// This class is a singleton + /// Gets a value indicating whether the camera module is busy. /// - public class CameraController : SingletonBase - { - #region Private Declarations - - private static readonly ManualResetEventSlim OperationDone = new ManualResetEventSlim(true); - private static readonly object SyncRoot = new object(); - private static CancellationTokenSource _videoTokenSource = new CancellationTokenSource(); - private static Task _videoStreamTask; - - #endregion - - #region Properties - - /// - /// Gets a value indicating whether the camera module is busy. - /// - /// - /// true if this instance is busy; otherwise, false. - /// - public bool IsBusy => OperationDone.IsSet == false; - - #endregion - - #region Image Capture Methods - - /// - /// Captures an image asynchronously. - /// - /// The settings. - /// The ct. - /// The image bytes - /// Cannot use camera module because it is currently busy. - public async Task CaptureImageAsync(CameraStillSettings settings, CancellationToken ct = default) - { - if (Instance.IsBusy) - throw new InvalidOperationException("Cannot use camera module because it is currently busy."); - - if (settings.CaptureTimeoutMilliseconds <= 0) - throw new ArgumentException($"{nameof(settings.CaptureTimeoutMilliseconds)} needs to be greater than 0"); - - try - { - OperationDone.Reset(); - - var output = new MemoryStream(); - var exitCode = await ProcessRunner.RunProcessAsync( - settings.CommandName, - settings.CreateProcessArguments(), - (data, proc) => - { - output.Write(data, 0, data.Length); - }, - null, - true, - ct); - - return exitCode != 0 ? new byte[] { } : output.ToArray(); - } - finally - { - OperationDone.Set(); - } - } - - /// - /// Captures an image. - /// - /// The settings. - /// The image bytes - public byte[] CaptureImage(CameraStillSettings settings) - { - return CaptureImageAsync(settings).GetAwaiter().GetResult(); - } - - /// - /// Captures a JPEG encoded image asynchronously at 90% quality. - /// - /// The width. - /// The height. - /// The ct. - /// The image bytes - public Task CaptureImageJpegAsync(int width, int height, CancellationToken ct = default) - { - var settings = new CameraStillSettings - { - CaptureWidth = width, - CaptureHeight = height, - CaptureJpegQuality = 90, - CaptureTimeoutMilliseconds = 300, - }; - - return CaptureImageAsync(settings, ct); - } - - /// - /// Captures a JPEG encoded image at 90% quality. - /// - /// The width. - /// The height. - /// The image bytes - public byte[] CaptureImageJpeg(int width, int height) => CaptureImageJpegAsync(width, height).GetAwaiter().GetResult(); - - #endregion - - #region Video Capture Methods - - /// - /// Opens the video stream with a timeout of 0 (running indefinitely) at 1080p resolution, variable bitrate and 25 FPS. - /// No preview is shown - /// - /// The on data callback. - /// The on exit callback. - public void OpenVideoStream(Action onDataCallback, Action onExitCallback = null) - { - var settings = new CameraVideoSettings - { - CaptureTimeoutMilliseconds = 0, - CaptureDisplayPreview = false, - CaptureWidth = 1920, - CaptureHeight = 1080 - }; - - OpenVideoStream(settings, onDataCallback, onExitCallback); - } - - /// - /// Opens the video stream with the supplied settings. Capture Timeout Milliseconds has to be 0 or greater - /// - /// The settings. - /// The on data callback. - /// The on exit callback. - /// Cannot use camera module because it is currently busy. - /// CaptureTimeoutMilliseconds - public void OpenVideoStream(CameraVideoSettings settings, Action onDataCallback, Action onExitCallback) - { - if (Instance.IsBusy) - throw new InvalidOperationException("Cannot use camera module because it is currently busy."); - - if (settings.CaptureTimeoutMilliseconds < 0) - throw new ArgumentException($"{nameof(settings.CaptureTimeoutMilliseconds)} needs to be greater than or equal to 0"); - - try - { - OperationDone.Reset(); - _videoStreamTask = Task.Factory.StartNew(() => VideoWorkerDoWork(settings, onDataCallback, onExitCallback), _videoTokenSource.Token); - } - catch - { - OperationDone.Set(); - throw; - } - } - - /// - /// Closes the video stream of a video stream is open. - /// - public void CloseVideoStream() - { - lock (SyncRoot) - { - if (IsBusy == false) - return; - } - - if (_videoTokenSource.IsCancellationRequested == false) - { - _videoTokenSource.Cancel(); - _videoStreamTask.Wait(); - } - - _videoTokenSource = new CancellationTokenSource(); - } - - private static async Task VideoWorkerDoWork( - CameraVideoSettings settings, - Action onDataCallback, - Action onExitCallback) - { - try - { - await ProcessRunner.RunProcessAsync( - settings.CommandName, - settings.CreateProcessArguments(), - (data, proc) => onDataCallback?.Invoke(data), - null, - true, - _videoTokenSource.Token); - - onExitCallback?.Invoke(); - } - catch - { - // swallow - } - finally - { - Instance.CloseVideoStream(); - OperationDone.Set(); - } - } - #endregion - } + /// + /// true if this instance is busy; otherwise, false. + /// + public Boolean IsBusy => OperationDone.IsSet == false; + + #endregion + + #region Image Capture Methods + + /// + /// Captures an image asynchronously. + /// + /// The settings. + /// The ct. + /// The image bytes + /// Cannot use camera module because it is currently busy. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0067:Objekte verwerfen, bevor Bereich verloren geht", Justification = "")] + public async Task CaptureImageAsync(CameraStillSettings settings, CancellationToken ct = default) { + if(Instance.IsBusy) { + throw new InvalidOperationException("Cannot use camera module because it is currently busy."); + } + + if(settings.CaptureTimeoutMilliseconds <= 0) { + throw new ArgumentException($"{nameof(settings.CaptureTimeoutMilliseconds)} needs to be greater than 0"); + } + + try { + OperationDone.Reset(); + + MemoryStream output = new MemoryStream(); + Int32 exitCode = await ProcessRunner.RunProcessAsync( + settings.CommandName, + settings.CreateProcessArguments(), + (data, proc) => output.Write(data, 0, data.Length), + null, + true, + ct); + + return exitCode != 0 ? new Byte[] { } : output.ToArray(); + } finally { + OperationDone.Set(); + } + } + + /// + /// Captures an image. + /// + /// The settings. + /// The image bytes + public Byte[] CaptureImage(CameraStillSettings settings) => this.CaptureImageAsync(settings).GetAwaiter().GetResult(); + + /// + /// Captures a JPEG encoded image asynchronously at 90% quality. + /// + /// The width. + /// The height. + /// The ct. + /// The image bytes + public Task CaptureImageJpegAsync(Int32 width, Int32 height, CancellationToken ct = default) { + CameraStillSettings settings = new CameraStillSettings { + CaptureWidth = width, + CaptureHeight = height, + CaptureJpegQuality = 90, + CaptureTimeoutMilliseconds = 300, + }; + + return this.CaptureImageAsync(settings, ct); + } + + /// + /// Captures a JPEG encoded image at 90% quality. + /// + /// The width. + /// The height. + /// The image bytes + public Byte[] CaptureImageJpeg(Int32 width, Int32 height) => this.CaptureImageJpegAsync(width, height).GetAwaiter().GetResult(); + + #endregion + + #region Video Capture Methods + + /// + /// Opens the video stream with a timeout of 0 (running indefinitely) at 1080p resolution, variable bitrate and 25 FPS. + /// No preview is shown + /// + /// The on data callback. + /// The on exit callback. + public void OpenVideoStream(Action onDataCallback, Action onExitCallback = null) { + CameraVideoSettings settings = new CameraVideoSettings { + CaptureTimeoutMilliseconds = 0, + CaptureDisplayPreview = false, + CaptureWidth = 1920, + CaptureHeight = 1080 + }; + + this.OpenVideoStream(settings, onDataCallback, onExitCallback); + } + + /// + /// Opens the video stream with the supplied settings. Capture Timeout Milliseconds has to be 0 or greater + /// + /// The settings. + /// The on data callback. + /// The on exit callback. + /// Cannot use camera module because it is currently busy. + /// CaptureTimeoutMilliseconds + public void OpenVideoStream(CameraVideoSettings settings, Action onDataCallback, Action onExitCallback) { + if(Instance.IsBusy) { + throw new InvalidOperationException("Cannot use camera module because it is currently busy."); + } + + if(settings.CaptureTimeoutMilliseconds < 0) { + throw new ArgumentException($"{nameof(settings.CaptureTimeoutMilliseconds)} needs to be greater than or equal to 0"); + } + + try { + OperationDone.Reset(); + _videoStreamTask = Task.Factory.StartNew(() => VideoWorkerDoWork(settings, onDataCallback, onExitCallback), _videoTokenSource.Token); + } catch { + OperationDone.Set(); + throw; + } + } + + /// + /// Closes the video stream of a video stream is open. + /// + public void CloseVideoStream() { + lock(SyncRoot) { + if(this.IsBusy == false) { + return; + } + } + + if(_videoTokenSource.IsCancellationRequested == false) { + _videoTokenSource.Cancel(); + _videoStreamTask.Wait(); + } + + _videoTokenSource = new CancellationTokenSource(); + } + + private static async Task VideoWorkerDoWork(CameraVideoSettings settings, Action onDataCallback, Action onExitCallback) { + try { + await ProcessRunner.RunProcessAsync( + settings.CommandName, + settings.CreateProcessArguments(), + (data, proc) => onDataCallback?.Invoke(data), + null, + true, + _videoTokenSource.Token); + + onExitCallback?.Invoke(); + } catch { + // swallow + } finally { + Instance.CloseVideoStream(); + OperationDone.Set(); + } + } + #endregion + } } \ No newline at end of file diff --git a/Unosquare.RaspberryIO/Camera/CameraRect.cs b/Unosquare.RaspberryIO/Camera/CameraRect.cs index b793081..bb5e5f1 100644 --- a/Unosquare.RaspberryIO/Camera/CameraRect.cs +++ b/Unosquare.RaspberryIO/Camera/CameraRect.cs @@ -1,82 +1,86 @@ -namespace Unosquare.RaspberryIO.Camera -{ - using Swan; - using System.Globalization; - +using Unosquare.Swan; +using System; +using System.Globalization; + +namespace Unosquare.RaspberryIO.Camera { + /// + /// Defines the Raspberry Pi camera's sensor ROI (Region of Interest) + /// + public struct CameraRect { /// - /// Defines the Raspberry Pi camera's sensor ROI (Region of Interest) + /// The default ROI which is the entire area. /// - public struct CameraRect - { - /// - /// The default ROI which is the entire area. - /// - public static readonly CameraRect Default = new CameraRect { X = 0M, Y = 0M, W = 1.0M, H = 1.0M }; - - /// - /// Gets or sets the x in relative coordinates. (0.0 to 1.0) - /// - /// - /// The x. - /// - public decimal X { get; set; } - - /// - /// Gets or sets the y location in relative coordinates. (0.0 to 1.0) - /// - /// - /// The y. - /// - public decimal Y { get; set; } - - /// - /// Gets or sets the width in relative coordinates. (0.0 to 1.0) - /// - /// - /// The w. - /// - public decimal W { get; set; } - - /// - /// Gets or sets the height in relative coordinates. (0.0 to 1.0) - /// - /// - /// The h. - /// - public decimal H { get; set; } - - /// - /// Gets a value indicating whether this instance is equal to the default (The entire area). - /// - /// - /// true if this instance is default; otherwise, false. - /// - public bool IsDefault - { - get - { - Clamp(); - return X == Default.X && Y == Default.Y && W == Default.W && H == Default.H; - } - } - - /// - /// Clamps the members of this ROI to their minimum and maximum values - /// - public void Clamp() - { - X = X.Clamp(0M, 1M); - Y = Y.Clamp(0M, 1M); - W = W.Clamp(0M, 1M - X); - H = H.Clamp(0M, 1M - Y); - } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() => $"{X.ToString(CultureInfo.InvariantCulture)},{Y.ToString(CultureInfo.InvariantCulture)},{W.ToString(CultureInfo.InvariantCulture)},{H.ToString(CultureInfo.InvariantCulture)}"; - } + public static readonly CameraRect Default = new CameraRect { X = 0M, Y = 0M, W = 1.0M, H = 1.0M }; + + /// + /// Gets or sets the x in relative coordinates. (0.0 to 1.0) + /// + /// + /// The x. + /// + public Decimal X { + get; set; + } + + /// + /// Gets or sets the y location in relative coordinates. (0.0 to 1.0) + /// + /// + /// The y. + /// + public Decimal Y { + get; set; + } + + /// + /// Gets or sets the width in relative coordinates. (0.0 to 1.0) + /// + /// + /// The w. + /// + public Decimal W { + get; set; + } + + /// + /// Gets or sets the height in relative coordinates. (0.0 to 1.0) + /// + /// + /// The h. + /// + public Decimal H { + get; set; + } + + /// + /// Gets a value indicating whether this instance is equal to the default (The entire area). + /// + /// + /// true if this instance is default; otherwise, false. + /// + public Boolean IsDefault { + get { + this.Clamp(); + return this.X == Default.X && this.Y == Default.Y && this.W == Default.W && this.H == Default.H; + } + } + + /// + /// Clamps the members of this ROI to their minimum and maximum values + /// + public void Clamp() { + this.X = this.X.Clamp(0M, 1M); + this.Y = this.Y.Clamp(0M, 1M); + this.W = this.W.Clamp(0M, 1M - this.X); + this.H = this.H.Clamp(0M, 1M - this.Y); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override String ToString() => $"{this.X.ToString(CultureInfo.InvariantCulture)},{this.Y.ToString(CultureInfo.InvariantCulture)},{this.W.ToString(CultureInfo.InvariantCulture)},{this.H.ToString(CultureInfo.InvariantCulture)}"; + } } diff --git a/Unosquare.RaspberryIO/Camera/CameraSettingsBase.cs b/Unosquare.RaspberryIO/Camera/CameraSettingsBase.cs index fcc27b2..605b989 100644 --- a/Unosquare.RaspberryIO/Camera/CameraSettingsBase.cs +++ b/Unosquare.RaspberryIO/Camera/CameraSettingsBase.cs @@ -1,340 +1,361 @@ -namespace Unosquare.RaspberryIO.Camera -{ - using Swan; - using System.Globalization; - using System.Text; - +using Unosquare.Swan; +using System.Globalization; +using System.Text; +using System; + +namespace Unosquare.RaspberryIO.Camera { + /// + /// A base class to implement raspistill and raspivid wrappers + /// Full documentation available at + /// https://www.raspberrypi.org/documentation/raspbian/applications/camera.md + /// + public abstract class CameraSettingsBase { /// - /// A base class to implement raspistill and raspivid wrappers - /// Full documentation available at - /// https://www.raspberrypi.org/documentation/raspbian/applications/camera.md + /// The Invariant Culture shorthand /// - public abstract class CameraSettingsBase - { - /// - /// The Invariant Culture shorthand - /// - protected static readonly CultureInfo Ci = CultureInfo.InvariantCulture; - - #region Capture Settings - - /// - /// Gets or sets the timeout milliseconds. - /// Default value is 5000 - /// Recommended value is at least 300 in order to let the light collectors open - /// - public int CaptureTimeoutMilliseconds { get; set; } = 5000; - - /// - /// Gets or sets a value indicating whether or not to show a preview window on the screen - /// - public bool CaptureDisplayPreview { get; set; } = false; - - /// - /// Gets or sets a value indicating whether a preview window is shown in full screen mode if enabled - /// - public bool CaptureDisplayPreviewInFullScreen { get; set; } = true; - - /// - /// Gets or sets a value indicating whether video stabilization should be enabled. - /// - public bool CaptureVideoStabilizationEnabled { get; set; } = false; - - /// - /// Gets or sets the display preview opacity only if the display preview property is enabled. - /// - public byte CaptureDisplayPreviewOpacity { get; set; } = 255; - - /// - /// Gets or sets the capture sensor region of interest in relative coordinates. - /// - public CameraRect CaptureSensorRoi { get; set; } = CameraRect.Default; - - /// - /// Gets or sets the capture shutter speed in microseconds. - /// Default -1, Range 0 to 6000000 (equivalent to 6 seconds) - /// - public int CaptureShutterSpeedMicroseconds { get; set; } = -1; - - /// - /// Gets or sets the exposure mode. - /// - public CameraExposureMode CaptureExposure { get; set; } = CameraExposureMode.Auto; - - /// - /// Gets or sets the picture EV compensation. Default is 0, Range is -10 to 10 - /// Camera exposure compensation is commonly stated in terms of EV units; - /// 1 EV is equal to one exposure step (or stop), corresponding to a doubling of exposure. - /// Exposure can be adjusted by changing either the lens f-number or the exposure time; - /// which one is changed usually depends on the camera's exposure mode. - /// - public int CaptureExposureCompensation { get; set; } = 0; - - /// - /// Gets or sets the capture metering mode. - /// - public CameraMeteringMode CaptureMeteringMode { get; set; } = CameraMeteringMode.Average; - - /// - /// Gets or sets the automatic white balance mode. By default it is set to Auto - /// - public CameraWhiteBalanceMode CaptureWhiteBalanceControl { get; set; } = CameraWhiteBalanceMode.Auto; - - /// - /// Gets or sets the capture white balance gain on the blue channel. Example: 1.25 - /// Only takes effect if White balance control is set to off. - /// Default is 0 - /// - public decimal CaptureWhiteBalanceGainBlue { get; set; } = 0M; - - /// - /// Gets or sets the capture white balance gain on the red channel. Example: 1.75 - /// Only takes effect if White balance control is set to off. - /// Default is 0 - /// - public decimal CaptureWhiteBalanceGainRed { get; set; } = 0M; - - /// - /// Gets or sets the dynamic range compensation. - /// DRC changes the images by increasing the range of dark areas, and decreasing the brighter areas. This can improve the image in low light areas. - /// - public CameraDynamicRangeCompensation CaptureDynamicRangeCompensation { get; set; } = - CameraDynamicRangeCompensation.Off; - - #endregion - - #region Image Properties - - /// - /// Gets or sets the width of the picture to take. - /// Less than or equal to 0 in either width or height means maximum resolution available. - /// - public int CaptureWidth { get; set; } = 640; - - /// - /// Gets or sets the height of the picture to take. - /// Less than or equal to 0 in either width or height means maximum resolution available. - /// - public int CaptureHeight { get; set; } = 480; - - /// - /// Gets or sets the picture sharpness. Default is 0, Range form -100 to 100 - /// - public int ImageSharpness { get; set; } = 0; - - /// - /// Gets or sets the picture contrast. Default is 0, Range form -100 to 100 - /// - public int ImageContrast { get; set; } = 0; - - /// - /// Gets or sets the picture brightness. Default is 50, Range form 0 to 100 - /// - public int ImageBrightness { get; set; } = 50; // from 0 to 100 - - /// - /// Gets or sets the picture saturation. Default is 0, Range form -100 to 100 - /// - public int ImageSaturation { get; set; } = 0; - - /// - /// Gets or sets the picture ISO. Default is -1 Range is 100 to 800 - /// The higher the value, the more light the sensor absorbs - /// - public int ImageIso { get; set; } = -1; - - /// - /// Gets or sets the image capture effect to be applied. - /// - public CameraImageEffect ImageEffect { get; set; } = CameraImageEffect.None; - - /// - /// Gets or sets the color effect U coordinates. - /// Default is -1, Range is 0 to 255 - /// 128:128 should be effectively a monochrome image. - /// - public int ImageColorEffectU { get; set; } = -1; // 0 to 255 - - /// - /// Gets or sets the color effect V coordinates. - /// Default is -1, Range is 0 to 255 - /// 128:128 should be effectively a monochrome image. - /// - public int ImageColorEffectV { get; set; } = -1; // 0 to 255 - - /// - /// Gets or sets the image rotation. Default is no rotation - /// - public CameraImageRotation ImageRotation { get; set; } = CameraImageRotation.None; - - /// - /// Gets or sets a value indicating whether the image should be flipped horizontally. - /// - public bool ImageFlipHorizontally { get; set; } - - /// - /// Gets or sets a value indicating whether the image should be flipped vertically. - /// - public bool ImageFlipVertically { get; set; } - - /// - /// Gets or sets the image annotations using a bitmask (or flags) notation. - /// Apply a bitwise OR to the enumeration to include multiple annotations - /// - public CameraAnnotation ImageAnnotations { get; set; } = CameraAnnotation.None; - - /// - /// Gets or sets the image annotations text. - /// Text may include date/time placeholders by using the '%' character, as used by strftime. - /// Example: ABC %Y-%m-%d %X will output ABC 2015-10-28 20:09:33 - /// - public string ImageAnnotationsText { get; set; } = string.Empty; - - /// - /// Gets or sets the font size of the text annotations - /// Default is -1, range is 6 to 160 - /// - public int ImageAnnotationFontSize { get; set; } = -1; - - /// - /// Gets or sets the color of the text annotations. - /// - /// - /// The color of the image annotation font. - /// - public CameraColor ImageAnnotationFontColor { get; set; } = null; - - /// - /// Gets or sets the background color for text annotations. - /// - /// - /// The image annotation background. - /// - public CameraColor ImageAnnotationBackground { get; set; } = null; - - #endregion - - #region Interface - - /// - /// Gets the command file executable. - /// - public abstract string CommandName { get; } - - /// - /// Creates the process arguments. - /// - /// The string that represents the process arguments - public virtual string CreateProcessArguments() - { - var sb = new StringBuilder(); - sb.Append("-o -"); // output to standard output as opposed to a file. - sb.Append($" -t {(CaptureTimeoutMilliseconds < 0 ? "0" : CaptureTimeoutMilliseconds.ToString(Ci))}"); - - // Basic Width and height - if (CaptureWidth > 0 && CaptureHeight > 0) - { - sb.Append($" -w {CaptureWidth.ToString(Ci)}"); - sb.Append($" -h {CaptureHeight.ToString(Ci)}"); - } - - // Display Preview - if (CaptureDisplayPreview) - { - if (CaptureDisplayPreviewInFullScreen) - sb.Append(" -f"); - - if (CaptureDisplayPreviewOpacity != byte.MaxValue) - sb.Append($" -op {CaptureDisplayPreviewOpacity.ToString(Ci)}"); - } - else - { - sb.Append(" -n"); // no preview - } - - // Picture Settings - if (ImageSharpness != 0) - sb.Append($" -sh {ImageSharpness.Clamp(-100, 100).ToString(Ci)}"); - - if (ImageContrast != 0) - sb.Append($" -co {ImageContrast.Clamp(-100, 100).ToString(Ci)}"); - - if (ImageBrightness != 50) - sb.Append($" -br {ImageBrightness.Clamp(0, 100).ToString(Ci)}"); - - if (ImageSaturation != 0) - sb.Append($" -sa {ImageSaturation.Clamp(-100, 100).ToString(Ci)}"); - - if (ImageIso >= 100) - sb.Append($" -ISO {ImageIso.Clamp(100, 800).ToString(Ci)}"); - - if (CaptureVideoStabilizationEnabled) - sb.Append(" -vs"); - - if (CaptureExposureCompensation != 0) - sb.Append($" -ev {CaptureExposureCompensation.Clamp(-10, 10).ToString(Ci)}"); - - if (CaptureExposure != CameraExposureMode.Auto) - sb.Append($" -ex {CaptureExposure.ToString().ToLowerInvariant()}"); - - if (CaptureWhiteBalanceControl != CameraWhiteBalanceMode.Auto) - sb.Append($" -awb {CaptureWhiteBalanceControl.ToString().ToLowerInvariant()}"); - - if (ImageEffect != CameraImageEffect.None) - sb.Append($" -ifx {ImageEffect.ToString().ToLowerInvariant()}"); - - if (ImageColorEffectU >= 0 && ImageColorEffectV >= 0) - { - sb.Append( - $" -cfx {ImageColorEffectU.Clamp(0, 255).ToString(Ci)}:{ImageColorEffectV.Clamp(0, 255).ToString(Ci)}"); - } - - if (CaptureMeteringMode != CameraMeteringMode.Average) - sb.Append($" -mm {CaptureMeteringMode.ToString().ToLowerInvariant()}"); - - if (ImageRotation != CameraImageRotation.None) - sb.Append($" -rot {((int)ImageRotation).ToString(Ci)}"); - - if (ImageFlipHorizontally) - sb.Append(" -hf"); - - if (ImageFlipVertically) - sb.Append(" -vf"); - - if (CaptureSensorRoi.IsDefault == false) - sb.Append($" -roi {CaptureSensorRoi}"); - - if (CaptureShutterSpeedMicroseconds > 0) - sb.Append($" -ss {CaptureShutterSpeedMicroseconds.Clamp(0, 6000000).ToString(Ci)}"); - - if (CaptureDynamicRangeCompensation != CameraDynamicRangeCompensation.Off) - sb.Append($" -drc {CaptureDynamicRangeCompensation.ToString().ToLowerInvariant()}"); - - if (CaptureWhiteBalanceControl == CameraWhiteBalanceMode.Off && - (CaptureWhiteBalanceGainBlue != 0M || CaptureWhiteBalanceGainRed != 0M)) - sb.Append($" -awbg {CaptureWhiteBalanceGainBlue.ToString(Ci)},{CaptureWhiteBalanceGainRed.ToString(Ci)}"); - - if (ImageAnnotationFontSize > 0) - { - sb.Append($" -ae {ImageAnnotationFontSize.Clamp(6, 160).ToString(Ci)}"); - sb.Append($",{(ImageAnnotationFontColor == null ? "0xff" : ImageAnnotationFontColor.ToYuvHex(true))}"); - - if (ImageAnnotationBackground != null) - { - ImageAnnotations |= CameraAnnotation.SolidBackground; - sb.Append($",{ImageAnnotationBackground.ToYuvHex(true)}"); - } - } - - if (ImageAnnotations != CameraAnnotation.None) - sb.Append($" -a {((int)ImageAnnotations).ToString(Ci)}"); - - if (string.IsNullOrWhiteSpace(ImageAnnotationsText) == false) - sb.Append($" -a \"{ImageAnnotationsText.Replace("\"", "'")}\""); - - return sb.ToString(); - } - - #endregion - } + protected static readonly CultureInfo Ci = CultureInfo.InvariantCulture; + + #region Capture Settings + + /// + /// Gets or sets the timeout milliseconds. + /// Default value is 5000 + /// Recommended value is at least 300 in order to let the light collectors open + /// + public Int32 CaptureTimeoutMilliseconds { get; set; } = 5000; + + /// + /// Gets or sets a value indicating whether or not to show a preview window on the screen + /// + public Boolean CaptureDisplayPreview { get; set; } = false; + + /// + /// Gets or sets a value indicating whether a preview window is shown in full screen mode if enabled + /// + public Boolean CaptureDisplayPreviewInFullScreen { get; set; } = true; + + /// + /// Gets or sets a value indicating whether video stabilization should be enabled. + /// + public Boolean CaptureVideoStabilizationEnabled { get; set; } = false; + + /// + /// Gets or sets the display preview opacity only if the display preview property is enabled. + /// + public Byte CaptureDisplayPreviewOpacity { get; set; } = 255; + + /// + /// Gets or sets the capture sensor region of interest in relative coordinates. + /// + public CameraRect CaptureSensorRoi { get; set; } = CameraRect.Default; + + /// + /// Gets or sets the capture shutter speed in microseconds. + /// Default -1, Range 0 to 6000000 (equivalent to 6 seconds) + /// + public Int32 CaptureShutterSpeedMicroseconds { get; set; } = -1; + + /// + /// Gets or sets the exposure mode. + /// + public CameraExposureMode CaptureExposure { get; set; } = CameraExposureMode.Auto; + + /// + /// Gets or sets the picture EV compensation. Default is 0, Range is -10 to 10 + /// Camera exposure compensation is commonly stated in terms of EV units; + /// 1 EV is equal to one exposure step (or stop), corresponding to a doubling of exposure. + /// Exposure can be adjusted by changing either the lens f-number or the exposure time; + /// which one is changed usually depends on the camera's exposure mode. + /// + public Int32 CaptureExposureCompensation { get; set; } = 0; + + /// + /// Gets or sets the capture metering mode. + /// + public CameraMeteringMode CaptureMeteringMode { get; set; } = CameraMeteringMode.Average; + + /// + /// Gets or sets the automatic white balance mode. By default it is set to Auto + /// + public CameraWhiteBalanceMode CaptureWhiteBalanceControl { get; set; } = CameraWhiteBalanceMode.Auto; + + /// + /// Gets or sets the capture white balance gain on the blue channel. Example: 1.25 + /// Only takes effect if White balance control is set to off. + /// Default is 0 + /// + public Decimal CaptureWhiteBalanceGainBlue { get; set; } = 0M; + + /// + /// Gets or sets the capture white balance gain on the red channel. Example: 1.75 + /// Only takes effect if White balance control is set to off. + /// Default is 0 + /// + public Decimal CaptureWhiteBalanceGainRed { get; set; } = 0M; + + /// + /// Gets or sets the dynamic range compensation. + /// DRC changes the images by increasing the range of dark areas, and decreasing the brighter areas. This can improve the image in low light areas. + /// + public CameraDynamicRangeCompensation CaptureDynamicRangeCompensation { + get; set; + } = + CameraDynamicRangeCompensation.Off; + + #endregion + + #region Image Properties + + /// + /// Gets or sets the width of the picture to take. + /// Less than or equal to 0 in either width or height means maximum resolution available. + /// + public Int32 CaptureWidth { get; set; } = 640; + + /// + /// Gets or sets the height of the picture to take. + /// Less than or equal to 0 in either width or height means maximum resolution available. + /// + public Int32 CaptureHeight { get; set; } = 480; + + /// + /// Gets or sets the picture sharpness. Default is 0, Range form -100 to 100 + /// + public Int32 ImageSharpness { get; set; } = 0; + + /// + /// Gets or sets the picture contrast. Default is 0, Range form -100 to 100 + /// + public Int32 ImageContrast { get; set; } = 0; + + /// + /// Gets or sets the picture brightness. Default is 50, Range form 0 to 100 + /// + public Int32 ImageBrightness { get; set; } = 50; // from 0 to 100 + + /// + /// Gets or sets the picture saturation. Default is 0, Range form -100 to 100 + /// + public Int32 ImageSaturation { get; set; } = 0; + + /// + /// Gets or sets the picture ISO. Default is -1 Range is 100 to 800 + /// The higher the value, the more light the sensor absorbs + /// + public Int32 ImageIso { get; set; } = -1; + + /// + /// Gets or sets the image capture effect to be applied. + /// + public CameraImageEffect ImageEffect { get; set; } = CameraImageEffect.None; + + /// + /// Gets or sets the color effect U coordinates. + /// Default is -1, Range is 0 to 255 + /// 128:128 should be effectively a monochrome image. + /// + public Int32 ImageColorEffectU { get; set; } = -1; // 0 to 255 + + /// + /// Gets or sets the color effect V coordinates. + /// Default is -1, Range is 0 to 255 + /// 128:128 should be effectively a monochrome image. + /// + public Int32 ImageColorEffectV { get; set; } = -1; // 0 to 255 + + /// + /// Gets or sets the image rotation. Default is no rotation + /// + public CameraImageRotation ImageRotation { get; set; } = CameraImageRotation.None; + + /// + /// Gets or sets a value indicating whether the image should be flipped horizontally. + /// + public Boolean ImageFlipHorizontally { + get; set; + } + + /// + /// Gets or sets a value indicating whether the image should be flipped vertically. + /// + public Boolean ImageFlipVertically { + get; set; + } + + /// + /// Gets or sets the image annotations using a bitmask (or flags) notation. + /// Apply a bitwise OR to the enumeration to include multiple annotations + /// + public CameraAnnotation ImageAnnotations { get; set; } = CameraAnnotation.None; + + /// + /// Gets or sets the image annotations text. + /// Text may include date/time placeholders by using the '%' character, as used by strftime. + /// Example: ABC %Y-%m-%d %X will output ABC 2015-10-28 20:09:33 + /// + public String ImageAnnotationsText { get; set; } = String.Empty; + + /// + /// Gets or sets the font size of the text annotations + /// Default is -1, range is 6 to 160 + /// + public Int32 ImageAnnotationFontSize { get; set; } = -1; + + /// + /// Gets or sets the color of the text annotations. + /// + /// + /// The color of the image annotation font. + /// + public CameraColor ImageAnnotationFontColor { get; set; } = null; + + /// + /// Gets or sets the background color for text annotations. + /// + /// + /// The image annotation background. + /// + public CameraColor ImageAnnotationBackground { get; set; } = null; + + #endregion + + #region Interface + + /// + /// Gets the command file executable. + /// + public abstract String CommandName { + get; + } + + /// + /// Creates the process arguments. + /// + /// The string that represents the process arguments + public virtual String CreateProcessArguments() { + StringBuilder sb = new StringBuilder(); + _ = sb.Append("-o -"); // output to standard output as opposed to a file. + _ = sb.Append($" -t {(this.CaptureTimeoutMilliseconds < 0 ? "0" : this.CaptureTimeoutMilliseconds.ToString(Ci))}"); + + // Basic Width and height + if(this.CaptureWidth > 0 && this.CaptureHeight > 0) { + _ = sb.Append($" -w {this.CaptureWidth.ToString(Ci)}"); + _ = sb.Append($" -h {this.CaptureHeight.ToString(Ci)}"); + } + + // Display Preview + if(this.CaptureDisplayPreview) { + if(this.CaptureDisplayPreviewInFullScreen) { + _ = sb.Append(" -f"); + } + + if(this.CaptureDisplayPreviewOpacity != Byte.MaxValue) { + _ = sb.Append($" -op {this.CaptureDisplayPreviewOpacity.ToString(Ci)}"); + } + } else { + _ = sb.Append(" -n"); // no preview + } + + // Picture Settings + if(this.ImageSharpness != 0) { + _ = sb.Append($" -sh {this.ImageSharpness.Clamp(-100, 100).ToString(Ci)}"); + } + + if(this.ImageContrast != 0) { + _ = sb.Append($" -co {this.ImageContrast.Clamp(-100, 100).ToString(Ci)}"); + } + + if(this.ImageBrightness != 50) { + _ = sb.Append($" -br {this.ImageBrightness.Clamp(0, 100).ToString(Ci)}"); + } + + if(this.ImageSaturation != 0) { + _ = sb.Append($" -sa {this.ImageSaturation.Clamp(-100, 100).ToString(Ci)}"); + } + + if(this.ImageIso >= 100) { + _ = sb.Append($" -ISO {this.ImageIso.Clamp(100, 800).ToString(Ci)}"); + } + + if(this.CaptureVideoStabilizationEnabled) { + _ = sb.Append(" -vs"); + } + + if(this.CaptureExposureCompensation != 0) { + _ = sb.Append($" -ev {this.CaptureExposureCompensation.Clamp(-10, 10).ToString(Ci)}"); + } + + if(this.CaptureExposure != CameraExposureMode.Auto) { + _ = sb.Append($" -ex {this.CaptureExposure.ToString().ToLowerInvariant()}"); + } + + if(this.CaptureWhiteBalanceControl != CameraWhiteBalanceMode.Auto) { + _ = sb.Append($" -awb {this.CaptureWhiteBalanceControl.ToString().ToLowerInvariant()}"); + } + + if(this.ImageEffect != CameraImageEffect.None) { + _ = sb.Append($" -ifx {this.ImageEffect.ToString().ToLowerInvariant()}"); + } + + if(this.ImageColorEffectU >= 0 && this.ImageColorEffectV >= 0) { + _ = sb.Append( + $" -cfx {this.ImageColorEffectU.Clamp(0, 255).ToString(Ci)}:{this.ImageColorEffectV.Clamp(0, 255).ToString(Ci)}"); + } + + if(this.CaptureMeteringMode != CameraMeteringMode.Average) { + _ = sb.Append($" -mm {this.CaptureMeteringMode.ToString().ToLowerInvariant()}"); + } + + if(this.ImageRotation != CameraImageRotation.None) { + _ = sb.Append($" -rot {((Int32)this.ImageRotation).ToString(Ci)}"); + } + + if(this.ImageFlipHorizontally) { + _ = sb.Append(" -hf"); + } + + if(this.ImageFlipVertically) { + _ = sb.Append(" -vf"); + } + + if(this.CaptureSensorRoi.IsDefault == false) { + _ = sb.Append($" -roi {this.CaptureSensorRoi}"); + } + + if(this.CaptureShutterSpeedMicroseconds > 0) { + _ = sb.Append($" -ss {this.CaptureShutterSpeedMicroseconds.Clamp(0, 6000000).ToString(Ci)}"); + } + + if(this.CaptureDynamicRangeCompensation != CameraDynamicRangeCompensation.Off) { + _ = sb.Append($" -drc {this.CaptureDynamicRangeCompensation.ToString().ToLowerInvariant()}"); + } + + if(this.CaptureWhiteBalanceControl == CameraWhiteBalanceMode.Off && + (this.CaptureWhiteBalanceGainBlue != 0M || this.CaptureWhiteBalanceGainRed != 0M)) { + _ = sb.Append($" -awbg {this.CaptureWhiteBalanceGainBlue.ToString(Ci)},{this.CaptureWhiteBalanceGainRed.ToString(Ci)}"); + } + + if(this.ImageAnnotationFontSize > 0) { + _ = sb.Append($" -ae {this.ImageAnnotationFontSize.Clamp(6, 160).ToString(Ci)}"); + _ = sb.Append($",{(this.ImageAnnotationFontColor == null ? "0xff" : this.ImageAnnotationFontColor.ToYuvHex(true))}"); + + if(this.ImageAnnotationBackground != null) { + this.ImageAnnotations |= CameraAnnotation.SolidBackground; + _ = sb.Append($",{this.ImageAnnotationBackground.ToYuvHex(true)}"); + } + } + + if(this.ImageAnnotations != CameraAnnotation.None) { + _ = sb.Append($" -a {((Int32)this.ImageAnnotations).ToString(Ci)}"); + } + + if(String.IsNullOrWhiteSpace(this.ImageAnnotationsText) == false) { + _ = sb.Append($" -a \"{this.ImageAnnotationsText.Replace("\"", "'")}\""); + } + + return sb.ToString(); + } + + #endregion + } } \ No newline at end of file diff --git a/Unosquare.RaspberryIO/Camera/CameraStillSettings.cs b/Unosquare.RaspberryIO/Camera/CameraStillSettings.cs index 3deac71..c1e2edf 100644 --- a/Unosquare.RaspberryIO/Camera/CameraStillSettings.cs +++ b/Unosquare.RaspberryIO/Camera/CameraStillSettings.cs @@ -1,120 +1,121 @@ -namespace Unosquare.RaspberryIO.Camera -{ - using Swan; - using System; - using System.Collections.Generic; - using System.Text; - +using Unosquare.Swan; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Unosquare.RaspberryIO.Camera { + /// + /// Defines a wrapper for the raspistill program and its settings (command-line arguments) + /// + /// + public class CameraStillSettings : CameraSettingsBase { + private Int32 _rotate; + + /// + public override String CommandName => "raspistill"; + /// - /// Defines a wrapper for the raspistill program and its settings (command-line arguments) + /// Gets or sets a value indicating whether the preview window (if enabled) uses native capture resolution + /// This may slow down preview FPS /// - /// - public class CameraStillSettings : CameraSettingsBase - { - private int _rotate; - - /// - public override string CommandName => "raspistill"; - - /// - /// Gets or sets a value indicating whether the preview window (if enabled) uses native capture resolution - /// This may slow down preview FPS - /// - public bool CaptureDisplayPreviewAtResolution { get; set; } = false; - - /// - /// Gets or sets the encoding format the hardware will use for the output. - /// - public CameraImageEncodingFormat CaptureEncoding { get; set; } = CameraImageEncodingFormat.Jpg; - - /// - /// Gets or sets the quality for JPEG only encoding mode. - /// Value ranges from 0 to 100 - /// - public int CaptureJpegQuality { get; set; } = 90; - - /// - /// Gets or sets a value indicating whether the JPEG encoder should add raw bayer metadata. - /// - public bool CaptureJpegIncludeRawBayerMetadata { get; set; } = false; - - /// - /// JPEG EXIF data - /// Keys and values must be already properly escaped. Otherwise the command will fail. - /// - public Dictionary CaptureJpegExtendedInfo { get; } = new Dictionary(); - - /// - /// Gets or sets a value indicating whether [horizontal flip]. - /// - /// - /// true if [horizontal flip]; otherwise, false. - /// - public bool HorizontalFlip { get; set; } = false; - - /// - /// Gets or sets a value indicating whether [vertical flip]. - /// - /// - /// true if [vertical flip]; otherwise, false. - /// - public bool VerticalFlip { get; set; } = false; - - /// - /// Gets or sets the rotation. - /// - /// Valid range 0-359 - public int Rotation - { - get => _rotate; - set - { - if (value < 0 || value > 359) - { - throw new ArgumentOutOfRangeException(nameof(value), "Valid range 0-359"); - } - - _rotate = value; - } - } - - /// - public override string CreateProcessArguments() - { - var sb = new StringBuilder(base.CreateProcessArguments()); - sb.Append($" -e {CaptureEncoding.ToString().ToLowerInvariant()}"); - - // JPEG Encoder specific arguments - if (CaptureEncoding == CameraImageEncodingFormat.Jpg) - { - sb.Append($" -q {CaptureJpegQuality.Clamp(0, 100).ToString(Ci)}"); - - if (CaptureJpegIncludeRawBayerMetadata) - sb.Append(" -r"); - - // JPEG EXIF data - if (CaptureJpegExtendedInfo.Count > 0) - { - foreach (var kvp in CaptureJpegExtendedInfo) - { - if (string.IsNullOrWhiteSpace(kvp.Key) || string.IsNullOrWhiteSpace(kvp.Value)) - continue; - - sb.Append($" -x \"{kvp.Key.Replace("\"", "'")}={kvp.Value.Replace("\"", "'")}\""); - } - } - } - - // Display preview settings - if (CaptureDisplayPreview && CaptureDisplayPreviewAtResolution) sb.Append(" -fp"); - - if (Rotation != 0) sb.Append($" -rot {Rotation}"); - - if (HorizontalFlip) sb.Append(" -hf"); - - if (VerticalFlip) sb.Append(" -vf"); - - return sb.ToString(); - } - } + public Boolean CaptureDisplayPreviewAtResolution { get; set; } = false; + + /// + /// Gets or sets the encoding format the hardware will use for the output. + /// + public CameraImageEncodingFormat CaptureEncoding { get; set; } = CameraImageEncodingFormat.Jpg; + + /// + /// Gets or sets the quality for JPEG only encoding mode. + /// Value ranges from 0 to 100 + /// + public Int32 CaptureJpegQuality { get; set; } = 90; + + /// + /// Gets or sets a value indicating whether the JPEG encoder should add raw bayer metadata. + /// + public Boolean CaptureJpegIncludeRawBayerMetadata { get; set; } = false; + + /// + /// JPEG EXIF data + /// Keys and values must be already properly escaped. Otherwise the command will fail. + /// + public Dictionary CaptureJpegExtendedInfo { get; } = new Dictionary(); + + /// + /// Gets or sets a value indicating whether [horizontal flip]. + /// + /// + /// true if [horizontal flip]; otherwise, false. + /// + public Boolean HorizontalFlip { get; set; } = false; + + /// + /// Gets or sets a value indicating whether [vertical flip]. + /// + /// + /// true if [vertical flip]; otherwise, false. + /// + public Boolean VerticalFlip { get; set; } = false; + + /// + /// Gets or sets the rotation. + /// + /// Valid range 0-359 + public Int32 Rotation { + get => this._rotate; + set { + if(value < 0 || value > 359) { + throw new ArgumentOutOfRangeException(nameof(value), "Valid range 0-359"); + } + + this._rotate = value; + } + } + + /// + public override String CreateProcessArguments() { + StringBuilder sb = new StringBuilder(base.CreateProcessArguments()); + _ = sb.Append($" -e {this.CaptureEncoding.ToString().ToLowerInvariant()}"); + + // JPEG Encoder specific arguments + if(this.CaptureEncoding == CameraImageEncodingFormat.Jpg) { + _ = sb.Append($" -q {this.CaptureJpegQuality.Clamp(0, 100).ToString(Ci)}"); + + if(this.CaptureJpegIncludeRawBayerMetadata) { + _ = sb.Append(" -r"); + } + + // JPEG EXIF data + if(this.CaptureJpegExtendedInfo.Count > 0) { + foreach(KeyValuePair kvp in this.CaptureJpegExtendedInfo) { + if(String.IsNullOrWhiteSpace(kvp.Key) || String.IsNullOrWhiteSpace(kvp.Value)) { + continue; + } + + _ = sb.Append($" -x \"{kvp.Key.Replace("\"", "'")}={kvp.Value.Replace("\"", "'")}\""); + } + } + } + + // Display preview settings + if(this.CaptureDisplayPreview && this.CaptureDisplayPreviewAtResolution) { + _ = sb.Append(" -fp"); + } + + if(this.Rotation != 0) { + _ = sb.Append($" -rot {this.Rotation}"); + } + + if(this.HorizontalFlip) { + _ = sb.Append(" -hf"); + } + + if(this.VerticalFlip) { + _ = sb.Append(" -vf"); + } + + return sb.ToString(); + } + } } \ No newline at end of file diff --git a/Unosquare.RaspberryIO/Camera/CameraVideoSettings.cs b/Unosquare.RaspberryIO/Camera/CameraVideoSettings.cs index f2470d8..58c10ec 100644 --- a/Unosquare.RaspberryIO/Camera/CameraVideoSettings.cs +++ b/Unosquare.RaspberryIO/Camera/CameraVideoSettings.cs @@ -1,94 +1,98 @@ -namespace Unosquare.RaspberryIO.Camera -{ - using Swan; - using System.Text; - +using Unosquare.Swan; +using System; +using System.Text; + +namespace Unosquare.RaspberryIO.Camera { + /// + /// Represents the raspivid camera settings for video capture functionality + /// + /// + public class CameraVideoSettings : CameraSettingsBase { + /// + public override String CommandName => "raspivid"; + /// - /// Represents the raspivid camera settings for video capture functionality + /// Use bits per second, so 10Mbits/s would be -b 10000000. For H264, 1080p30 a high quality bitrate would be 15Mbits/s or more. + /// Maximum bitrate is 25Mbits/s (-b 25000000), but much over 17Mbits/s won't show noticeable improvement at 1080p30. + /// Default -1 /// - /// - public class CameraVideoSettings : CameraSettingsBase - { - /// - public override string CommandName => "raspivid"; - - /// - /// Use bits per second, so 10Mbits/s would be -b 10000000. For H264, 1080p30 a high quality bitrate would be 15Mbits/s or more. - /// Maximum bitrate is 25Mbits/s (-b 25000000), but much over 17Mbits/s won't show noticeable improvement at 1080p30. - /// Default -1 - /// - public int CaptureBitrate { get; set; } = -1; - - /// - /// Gets or sets the framerate. - /// Default 25, range 2 to 30 - /// - public int CaptureFramerate { get; set; } = 25; - - /// - /// Sets the intra refresh period (GoP) rate for the recorded video. H264 video uses a complete frame (I-frame) every intra - /// refresh period, from which subsequent frames are based. This option specifies the number of frames between each I-frame. - /// Larger numbers here will reduce the size of the resulting video, and smaller numbers make the stream less error-prone. - /// - public int CaptureKeyframeRate { get; set; } = 25; - - /// - /// Sets the initial quantisation parameter for the stream. Varies from approximately 10 to 40, and will greatly affect - /// the quality of the recording. Higher values reduce quality and decrease file size. Combine this setting with a - /// bitrate of 0 to set a completely variable bitrate. - /// - public int CaptureQuantisation { get; set; } = 23; - - /// - /// Gets or sets the profile. - /// Sets the H264 profile to be used for the encoding. - /// Default is Main mode - /// - public CameraH264Profile CaptureProfile { get; set; } = CameraH264Profile.Main; - - /// - /// Forces the stream to include PPS and SPS headers on every I-frame. Needed for certain streaming cases - /// e.g. Apple HLS. These headers are small, so don't greatly increase the file size. - /// - /// - /// true if [interleave headers]; otherwise, false. - /// - public bool CaptureInterleaveHeaders { get; set; } = true; - - /// - /// Switch on an option to display the preview after compression. This will show any compression artefacts in the preview window. In normal operation, - /// the preview will show the camera output prior to being compressed. This option is not guaranteed to work in future releases. - /// - /// - /// true if [capture display preview encoded]; otherwise, false. - /// - public bool CaptureDisplayPreviewEncoded { get; set; } = false; - - /// - public override string CreateProcessArguments() - { - var sb = new StringBuilder(base.CreateProcessArguments()); - - sb.Append($" -pf {CaptureProfile.ToString().ToLowerInvariant()}"); - if (CaptureBitrate < 0) - sb.Append($" -b {CaptureBitrate.Clamp(0, 25000000).ToString(Ci)}"); - - if (CaptureFramerate >= 2) - sb.Append($" -fps {CaptureFramerate.Clamp(2, 30).ToString(Ci)}"); - - if (CaptureDisplayPreview && CaptureDisplayPreviewEncoded) - sb.Append(" -e"); - - if (CaptureKeyframeRate > 0) - sb.Append($" -g {CaptureKeyframeRate.ToString(Ci)}"); - - if (CaptureQuantisation >= 0) - sb.Append($" -qp {CaptureQuantisation.Clamp(0, 40).ToString(Ci)}"); - - if (CaptureInterleaveHeaders) - sb.Append(" -ih"); - - return sb.ToString(); - } - } + public Int32 CaptureBitrate { get; set; } = -1; + + /// + /// Gets or sets the framerate. + /// Default 25, range 2 to 30 + /// + public Int32 CaptureFramerate { get; set; } = 25; + + /// + /// Sets the intra refresh period (GoP) rate for the recorded video. H264 video uses a complete frame (I-frame) every intra + /// refresh period, from which subsequent frames are based. This option specifies the number of frames between each I-frame. + /// Larger numbers here will reduce the size of the resulting video, and smaller numbers make the stream less error-prone. + /// + public Int32 CaptureKeyframeRate { get; set; } = 25; + + /// + /// Sets the initial quantisation parameter for the stream. Varies from approximately 10 to 40, and will greatly affect + /// the quality of the recording. Higher values reduce quality and decrease file size. Combine this setting with a + /// bitrate of 0 to set a completely variable bitrate. + /// + public Int32 CaptureQuantisation { get; set; } = 23; + + /// + /// Gets or sets the profile. + /// Sets the H264 profile to be used for the encoding. + /// Default is Main mode + /// + public CameraH264Profile CaptureProfile { get; set; } = CameraH264Profile.Main; + + /// + /// Forces the stream to include PPS and SPS headers on every I-frame. Needed for certain streaming cases + /// e.g. Apple HLS. These headers are small, so don't greatly increase the file size. + /// + /// + /// true if [interleave headers]; otherwise, false. + /// + public Boolean CaptureInterleaveHeaders { get; set; } = true; + + /// + /// Switch on an option to display the preview after compression. This will show any compression artefacts in the preview window. In normal operation, + /// the preview will show the camera output prior to being compressed. This option is not guaranteed to work in future releases. + /// + /// + /// true if [capture display preview encoded]; otherwise, false. + /// + public Boolean CaptureDisplayPreviewEncoded { get; set; } = false; + + /// + public override String CreateProcessArguments() { + StringBuilder sb = new StringBuilder(base.CreateProcessArguments()); + + _ = sb.Append($" -pf {this.CaptureProfile.ToString().ToLowerInvariant()}"); + if(this.CaptureBitrate < 0) { + _ = sb.Append($" -b {this.CaptureBitrate.Clamp(0, 25000000).ToString(Ci)}"); + } + + if(this.CaptureFramerate >= 2) { + _ = sb.Append($" -fps {this.CaptureFramerate.Clamp(2, 30).ToString(Ci)}"); + } + + if(this.CaptureDisplayPreview && this.CaptureDisplayPreviewEncoded) { + _ = sb.Append(" -e"); + } + + if(this.CaptureKeyframeRate > 0) { + _ = sb.Append($" -g {this.CaptureKeyframeRate.ToString(Ci)}"); + } + + if(this.CaptureQuantisation >= 0) { + _ = sb.Append($" -qp {this.CaptureQuantisation.Clamp(0, 40).ToString(Ci)}"); + } + + if(this.CaptureInterleaveHeaders) { + _ = sb.Append(" -ih"); + } + + return sb.ToString(); + } + } } \ No newline at end of file diff --git a/Unosquare.RaspberryIO/Camera/Enums.cs b/Unosquare.RaspberryIO/Camera/Enums.cs index 9473147..d9d5200 100644 --- a/Unosquare.RaspberryIO/Camera/Enums.cs +++ b/Unosquare.RaspberryIO/Camera/Enums.cs @@ -1,423 +1,413 @@ -namespace Unosquare.RaspberryIO.Camera -{ - using System; - +using System; + +namespace Unosquare.RaspberryIO.Camera { + /// + /// Defines the available encoding formats for the Raspberry Pi camera module + /// + public enum CameraImageEncodingFormat { /// - /// Defines the available encoding formats for the Raspberry Pi camera module + /// The JPG /// - public enum CameraImageEncodingFormat - { - /// - /// The JPG - /// - Jpg, - - /// - /// The BMP - /// - Bmp, - - /// - /// The GIF - /// - Gif, - - /// - /// The PNG - /// - Png, - } - + Jpg, + /// - /// Defines the different exposure modes for the Raspberry Pi's camera module + /// The BMP /// - public enum CameraExposureMode - { - /// - /// The automatic - /// - Auto, - - /// - /// The night - /// - Night, - - /// - /// The night preview - /// - NightPreview, - - /// - /// The backlight - /// - Backlight, - - /// - /// The spotlight - /// - Spotlight, - - /// - /// The sports - /// - Sports, - - /// - /// The snow - /// - Snow, - - /// - /// The beach - /// - Beach, - - /// - /// The very long - /// - VeryLong, - - /// - /// The fixed FPS - /// - FixedFps, - - /// - /// The anti shake - /// - AntiShake, - - /// - /// The fireworks - /// - Fireworks - } - + Bmp, + /// - /// Defines the different AWB (Auto White Balance) modes for the Raspberry Pi's camera module + /// The GIF /// - public enum CameraWhiteBalanceMode - { - /// - /// No white balance - /// - Off, - - /// - /// The automatic - /// - Auto, - - /// - /// The sun - /// - Sun, - - /// - /// The cloud - /// - Cloud, - - /// - /// The shade - /// - Shade, - - /// - /// The tungsten - /// - Tungsten, - - /// - /// The fluorescent - /// - Fluorescent, - - /// - /// The incandescent - /// - Incandescent, - - /// - /// The flash - /// - Flash, - - /// - /// The horizon - /// - Horizon - } - + Gif, + /// - /// Defines the available image effects for the Raspberry Pi's camera module + /// The PNG /// - public enum CameraImageEffect - { - /// - /// No effect - /// - None, - - /// - /// The negative - /// - Negative, - - /// - /// The solarise - /// - Solarise, - - /// - /// The whiteboard - /// - Whiteboard, - - /// - /// The blackboard - /// - Blackboard, - - /// - /// The sketch - /// - Sketch, - - /// - /// The denoise - /// - Denoise, - - /// - /// The emboss - /// - Emboss, - - /// - /// The oil paint - /// - OilPaint, - - /// - /// The hatch - /// - Hatch, - - /// - /// Graphite Pen - /// - GPen, - - /// - /// The pastel - /// - Pastel, - - /// - /// The water colour - /// - WaterColour, - - /// - /// The film - /// - Film, - - /// - /// The blur - /// - Blur, - - /// - /// The saturation - /// - Saturation, - - /// - /// The solour swap - /// - SolourSwap, - - /// - /// The washed out - /// - WashedOut, - - /// - /// The colour point - /// - ColourPoint, - - /// - /// The colour balance - /// - ColourBalance, - - /// - /// The cartoon - /// - Cartoon - } - + Png, + } + + /// + /// Defines the different exposure modes for the Raspberry Pi's camera module + /// + public enum CameraExposureMode { /// - /// Defines the different metering modes for the Raspberry Pi's camera module + /// The automatic /// - public enum CameraMeteringMode - { - /// - /// The average - /// - Average, - - /// - /// The spot - /// - Spot, - - /// - /// The backlit - /// - Backlit, - - /// - /// The matrix - /// - Matrix, - } - + Auto, + /// - /// Defines the different image rotation modes for the Raspberry Pi's camera module + /// The night /// - public enum CameraImageRotation - { - /// - /// No rerotation - /// - None = 0, - - /// - /// 90 Degrees - /// - Degrees90 = 90, - - /// - /// 180 Degrees - /// - Degrees180 = 180, - - /// - /// 270 degrees - /// - Degrees270 = 270 - } - + Night, + /// - /// Defines the different DRC (Dynamic Range Compensation) modes for the Raspberry Pi's camera module - /// Helpful for low light photos + /// The night preview /// - public enum CameraDynamicRangeCompensation - { - /// - /// The off setting - /// - Off, - - /// - /// The low - /// - Low, - - /// - /// The medium - /// - Medium, - - /// - /// The high - /// - High - } - + NightPreview, + /// - /// Defines the bit-wise mask flags for the available annotation elements for the Raspberry Pi's camera module + /// The backlight /// - [Flags] - public enum CameraAnnotation - { - /// - /// The none - /// - None = 0, - - /// - /// The time - /// - Time = 4, - - /// - /// The date - /// - Date = 8, - - /// - /// The shutter settings - /// - ShutterSettings = 16, - - /// - /// The caf settings - /// - CafSettings = 32, - - /// - /// The gain settings - /// - GainSettings = 64, - - /// - /// The lens settings - /// - LensSettings = 128, - - /// - /// The motion settings - /// - MotionSettings = 256, - - /// - /// The frame number - /// - FrameNumber = 512, - - /// - /// The solid background - /// - SolidBackground = 1024, - } - + Backlight, + /// - /// Defines the different H.264 encoding profiles to be used when capturing video. + /// The spotlight /// - public enum CameraH264Profile - { - /// - /// BP: Primarily for lower-cost applications with limited computing resources, - /// this profile is used widely in videoconferencing and mobile applications. - /// - Baseline, - - /// - /// MP: Originally intended as the mainstream consumer profile for broadcast - /// and storage applications, the importance of this profile faded when the High profile was developed for those applications. - /// - Main, - - /// - /// HiP: The primary profile for broadcast and disc storage applications, particularly - /// for high-definition television applications (this is the profile adopted into HD DVD and Blu-ray Disc, for example). - /// - High - } + Spotlight, + + /// + /// The sports + /// + Sports, + + /// + /// The snow + /// + Snow, + + /// + /// The beach + /// + Beach, + + /// + /// The very long + /// + VeryLong, + + /// + /// The fixed FPS + /// + FixedFps, + + /// + /// The anti shake + /// + AntiShake, + + /// + /// The fireworks + /// + Fireworks + } + + /// + /// Defines the different AWB (Auto White Balance) modes for the Raspberry Pi's camera module + /// + public enum CameraWhiteBalanceMode { + /// + /// No white balance + /// + Off, + + /// + /// The automatic + /// + Auto, + + /// + /// The sun + /// + Sun, + + /// + /// The cloud + /// + Cloud, + + /// + /// The shade + /// + Shade, + + /// + /// The tungsten + /// + Tungsten, + + /// + /// The fluorescent + /// + Fluorescent, + + /// + /// The incandescent + /// + Incandescent, + + /// + /// The flash + /// + Flash, + + /// + /// The horizon + /// + Horizon + } + + /// + /// Defines the available image effects for the Raspberry Pi's camera module + /// + public enum CameraImageEffect { + /// + /// No effect + /// + None, + + /// + /// The negative + /// + Negative, + + /// + /// The solarise + /// + Solarise, + + /// + /// The whiteboard + /// + Whiteboard, + + /// + /// The blackboard + /// + Blackboard, + + /// + /// The sketch + /// + Sketch, + + /// + /// The denoise + /// + Denoise, + + /// + /// The emboss + /// + Emboss, + + /// + /// The oil paint + /// + OilPaint, + + /// + /// The hatch + /// + Hatch, + + /// + /// Graphite Pen + /// + GPen, + + /// + /// The pastel + /// + Pastel, + + /// + /// The water colour + /// + WaterColour, + + /// + /// The film + /// + Film, + + /// + /// The blur + /// + Blur, + + /// + /// The saturation + /// + Saturation, + + /// + /// The solour swap + /// + SolourSwap, + + /// + /// The washed out + /// + WashedOut, + + /// + /// The colour point + /// + ColourPoint, + + /// + /// The colour balance + /// + ColourBalance, + + /// + /// The cartoon + /// + Cartoon + } + + /// + /// Defines the different metering modes for the Raspberry Pi's camera module + /// + public enum CameraMeteringMode { + /// + /// The average + /// + Average, + + /// + /// The spot + /// + Spot, + + /// + /// The backlit + /// + Backlit, + + /// + /// The matrix + /// + Matrix, + } + + /// + /// Defines the different image rotation modes for the Raspberry Pi's camera module + /// + public enum CameraImageRotation { + /// + /// No rerotation + /// + None = 0, + + /// + /// 90 Degrees + /// + Degrees90 = 90, + + /// + /// 180 Degrees + /// + Degrees180 = 180, + + /// + /// 270 degrees + /// + Degrees270 = 270 + } + + /// + /// Defines the different DRC (Dynamic Range Compensation) modes for the Raspberry Pi's camera module + /// Helpful for low light photos + /// + public enum CameraDynamicRangeCompensation { + /// + /// The off setting + /// + Off, + + /// + /// The low + /// + Low, + + /// + /// The medium + /// + Medium, + + /// + /// The high + /// + High + } + + /// + /// Defines the bit-wise mask flags for the available annotation elements for the Raspberry Pi's camera module + /// + [Flags] + public enum CameraAnnotation { + /// + /// The none + /// + None = 0, + + /// + /// The time + /// + Time = 4, + + /// + /// The date + /// + Date = 8, + + /// + /// The shutter settings + /// + ShutterSettings = 16, + + /// + /// The caf settings + /// + CafSettings = 32, + + /// + /// The gain settings + /// + GainSettings = 64, + + /// + /// The lens settings + /// + LensSettings = 128, + + /// + /// The motion settings + /// + MotionSettings = 256, + + /// + /// The frame number + /// + FrameNumber = 512, + + /// + /// The solid background + /// + SolidBackground = 1024, + } + + /// + /// Defines the different H.264 encoding profiles to be used when capturing video. + /// + public enum CameraH264Profile { + /// + /// BP: Primarily for lower-cost applications with limited computing resources, + /// this profile is used widely in videoconferencing and mobile applications. + /// + Baseline, + + /// + /// MP: Originally intended as the mainstream consumer profile for broadcast + /// and storage applications, the importance of this profile faded when the High profile was developed for those applications. + /// + Main, + + /// + /// HiP: The primary profile for broadcast and disc storage applications, particularly + /// for high-definition television applications (this is the profile adopted into HD DVD and Blu-ray Disc, for example). + /// + High + } } diff --git a/Unosquare.RaspberryIO/Computer/DsiDisplay.cs b/Unosquare.RaspberryIO/Computer/DsiDisplay.cs index aff143f..418f0c2 100644 --- a/Unosquare.RaspberryIO/Computer/DsiDisplay.cs +++ b/Unosquare.RaspberryIO/Computer/DsiDisplay.cs @@ -1,80 +1,66 @@ -namespace Unosquare.RaspberryIO.Computer -{ - using Swan.Abstractions; - using System.Globalization; - using System.IO; - +using Unosquare.Swan.Abstractions; +using System.Globalization; +using System.IO; +using System; + +namespace Unosquare.RaspberryIO.Computer { + /// + /// The Official Raspberry Pi 7-inch touch display from the foundation + /// Some docs available here: + /// http://forums.pimoroni.com/t/official-7-raspberry-pi-touch-screen-faq/959 + /// + public class DsiDisplay : SingletonBase { + private const String BacklightFilename = "/sys/class/backlight/rpi_backlight/bl_power"; + private const String BrightnessFilename = "/sys/class/backlight/rpi_backlight/brightness"; + /// - /// The Official Raspberry Pi 7-inch touch display from the foundation - /// Some docs available here: - /// http://forums.pimoroni.com/t/official-7-raspberry-pi-touch-screen-faq/959 + /// Prevents a default instance of the class from being created. /// - public class DsiDisplay : SingletonBase - { - private const string BacklightFilename = "/sys/class/backlight/rpi_backlight/bl_power"; - private const string BrightnessFilename = "/sys/class/backlight/rpi_backlight/brightness"; - - /// - /// Prevents a default instance of the class from being created. - /// - private DsiDisplay() - { - // placeholder - } - - /// - /// Gets a value indicating whether the Pi Foundation Display files are present. - /// - /// - /// true if this instance is present; otherwise, false. - /// - public bool IsPresent => File.Exists(BrightnessFilename); - - /// - /// Gets or sets the brightness of the DSI display via filesystem. - /// - /// - /// The brightness. - /// - public byte Brightness - { - get - { - if (IsPresent == false) return 0; - - return byte.TryParse(File.ReadAllText(BrightnessFilename).Trim(), out var brightness) ? brightness : (byte)0; - } - set - { - if (IsPresent == false) return; - File.WriteAllText(BrightnessFilename, value.ToString(CultureInfo.InvariantCulture)); - } - } - - /// - /// Gets or sets a value indicating whether the backlight of the DSI display on. - /// This operation is performed via the file system - /// - /// - /// true if this instance is backlight on; otherwise, false. - /// - public bool IsBacklightOn - { - get - { - if (IsPresent == false) return false; - - if (int.TryParse(File.ReadAllText(BacklightFilename).Trim(), out var backlight)) - return backlight == 0; - - return false; - } - set - { - if (IsPresent == false) return; - - File.WriteAllText(BacklightFilename, value ? "0" : "1"); - } - } - } + private DsiDisplay() { + // placeholder + } + + /// + /// Gets a value indicating whether the Pi Foundation Display files are present. + /// + /// + /// true if this instance is present; otherwise, false. + /// + public Boolean IsPresent => File.Exists(BrightnessFilename); + + /// + /// Gets or sets the brightness of the DSI display via filesystem. + /// + /// + /// The brightness. + /// + public Byte Brightness { + get => this.IsPresent == false ? (Byte)0 : Byte.TryParse(File.ReadAllText(BrightnessFilename).Trim(), out Byte brightness) ? brightness : (Byte)0; + set { + if(this.IsPresent == false) { + return; + } + + File.WriteAllText(BrightnessFilename, value.ToString(CultureInfo.InvariantCulture)); + } + } + + /// + /// Gets or sets a value indicating whether the backlight of the DSI display on. + /// This operation is performed via the file system + /// + /// + /// true if this instance is backlight on; otherwise, false. + /// + public Boolean IsBacklightOn { + get => this.IsPresent == false ? false : Int32.TryParse(File.ReadAllText(BacklightFilename).Trim(), out Int32 backlight) ? backlight == 0 : false; + set { + if(this.IsPresent == false) { + return; + } + + File.WriteAllText(BacklightFilename, value ? "0" : "1"); + } + } + } } diff --git a/Unosquare.RaspberryIO/Computer/NetworkAdapterInfo.cs b/Unosquare.RaspberryIO/Computer/NetworkAdapterInfo.cs index d90ba47..a63c8eb 100644 --- a/Unosquare.RaspberryIO/Computer/NetworkAdapterInfo.cs +++ b/Unosquare.RaspberryIO/Computer/NetworkAdapterInfo.cs @@ -1,40 +1,51 @@ -namespace Unosquare.RaspberryIO.Computer -{ - using System.Net; - +using System; +using System.Net; + +namespace Unosquare.RaspberryIO.Computer { + /// + /// Represents a Network Adapter + /// + public class NetworkAdapterInfo { /// - /// Represents a Network Adapter + /// Gets the name. /// - public class NetworkAdapterInfo - { - /// - /// Gets the name. - /// - public string Name { get; internal set; } - - /// - /// Gets the IP V4 address. - /// - public IPAddress IPv4 { get; internal set; } - - /// - /// Gets the IP V6 address. - /// - public IPAddress IPv6 { get; internal set; } - - /// - /// Gets the name of the access point. - /// - public string AccessPointName { get; internal set; } - - /// - /// Gets the MAC (Physical) address. - /// - public string MacAddress { get; internal set; } - - /// - /// Gets a value indicating whether this instance is wireless. - /// - public bool IsWireless { get; internal set; } - } + public String Name { + get; internal set; + } + + /// + /// Gets the IP V4 address. + /// + public IPAddress IPv4 { + get; internal set; + } + + /// + /// Gets the IP V6 address. + /// + public IPAddress IPv6 { + get; internal set; + } + + /// + /// Gets the name of the access point. + /// + public String AccessPointName { + get; internal set; + } + + /// + /// Gets the MAC (Physical) address. + /// + public String MacAddress { + get; internal set; + } + + /// + /// Gets a value indicating whether this instance is wireless. + /// + public Boolean IsWireless { + get; internal set; + } + } } diff --git a/Unosquare.RaspberryIO/Computer/NetworkSettings.cs b/Unosquare.RaspberryIO/Computer/NetworkSettings.cs index 6d03de0..d8e690f 100644 --- a/Unosquare.RaspberryIO/Computer/NetworkSettings.cs +++ b/Unosquare.RaspberryIO/Computer/NetworkSettings.cs @@ -1,266 +1,252 @@ -namespace Unosquare.RaspberryIO.Computer -{ - using Swan; - using Swan.Abstractions; - using Swan.Components; - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Net; - using System.Text; - +using Unosquare.Swan; +using Unosquare.Swan.Abstractions; +using Unosquare.Swan.Components; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; + +namespace Unosquare.RaspberryIO.Computer { + /// + /// Represents the network information + /// + public class NetworkSettings : SingletonBase { + private const String EssidTag = "ESSID:"; + /// - /// Represents the network information + /// Gets the local machine Host Name. /// - public class NetworkSettings : SingletonBase - { - private const string EssidTag = "ESSID:"; - - /// - /// Gets the local machine Host Name. - /// - public string HostName => Network.HostName; - - /// - /// Retrieves the wireless networks. - /// - /// The adapter. - /// A list of WiFi networks - public List RetrieveWirelessNetworks(string adapter) => RetrieveWirelessNetworks(new[] { adapter }); - - /// - /// Retrieves the wireless networks. - /// - /// The adapters. - /// A list of WiFi networks - public List RetrieveWirelessNetworks(string[] adapters = null) - { - var result = new List(); - - foreach (var networkAdapter in adapters ?? RetrieveAdapters().Where(x => x.IsWireless).Select(x => x.Name)) - { - var wirelessOutput = ProcessRunner.GetProcessOutputAsync("iwlist", $"{networkAdapter} scanning").Result; - var outputLines = - wirelessOutput.Split('\n') - .Select(x => x.Trim()) - .Where(x => string.IsNullOrWhiteSpace(x) == false) - .ToArray(); - - for (var i = 0; i < outputLines.Length; i++) - { - var line = outputLines[i]; - - if (line.StartsWith(EssidTag) == false) continue; - - var network = new WirelessNetworkInfo() - { - Name = line.Replace(EssidTag, string.Empty).Replace("\"", string.Empty) - }; - - while (true) - { - if (i + 1 >= outputLines.Length) break; - - // should look for two lines before the ESSID acording to the scan - line = outputLines[i - 2]; - - if (line.StartsWith("Quality=")) - { - network.Quality = line.Replace("Quality=", string.Empty); - break; - } - } - - while (true) - { - if (i + 1 >= outputLines.Length) break; - - // should look for a line before the ESSID acording to the scan - line = outputLines[i - 1]; - - if (line.StartsWith("Encryption key:")) - { - network.IsEncrypted = line.Replace("Encryption key:", string.Empty).Trim() == "on"; - break; - } - } - - if (result.Any(x => x.Name == network.Name) == false) - result.Add(network); - } - } - - return result.OrderBy(x => x.Name).ToList(); - } - - /// - /// Setups the wireless network. - /// - /// Name of the adapter. - /// The network ssid. - /// The password. - /// The 2-letter country code in uppercase. Default is US. - /// True if successful. Otherwise, false. - public bool SetupWirelessNetwork(string adapterName, string networkSsid, string password = null, string countryCode = "US") - { - // TODO: Get the country where the device is located to set 'country' param in payload var - var payload = $"country={countryCode}\nctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\nupdate_config=1\n"; - payload += string.IsNullOrEmpty(password) - ? $"network={{\n\tssid=\"{networkSsid}\"\n\t}}\n" - : $"network={{\n\tssid=\"{networkSsid}\"\n\tpsk=\"{password}\"\n\t}}\n"; - try - { - File.WriteAllText("/etc/wpa_supplicant/wpa_supplicant.conf", payload); - ProcessRunner.GetProcessOutputAsync("pkill", "-f wpa_supplicant").Wait(); - ProcessRunner.GetProcessOutputAsync("ifdown", adapterName).Wait(); - ProcessRunner.GetProcessOutputAsync("ifup", adapterName).Wait(); - } - catch (Exception ex) - { - ex.Log(nameof(NetworkSettings)); - return false; - } - - return true; - } - - /// - /// Retrieves the network adapters. - /// - /// A list of network adapters. - public List RetrieveAdapters() - { - const string hWaddr = "HWaddr "; - const string ether = "ether "; - - var result = new List(); - var interfacesOutput = ProcessRunner.GetProcessOutputAsync("ifconfig").Result; - var wlanOutput = ProcessRunner.GetProcessOutputAsync("iwconfig") - .Result.Split('\n') - .Where(x => x.Contains("no wireless extensions.") == false) - .ToArray(); - - var outputLines = interfacesOutput.Split('\n').Where(x => string.IsNullOrWhiteSpace(x) == false).ToArray(); - - for (var i = 0; i < outputLines.Length; i++) - { - // grab the current line - var line = outputLines[i]; - - // skip if the line is indented - if (char.IsLetterOrDigit(line[0]) == false) - continue; - - // Read the line as an adatper - var adapter = new NetworkAdapterInfo - { - Name = line.Substring(0, line.IndexOf(' ')).TrimEnd(':') - }; - - // Parse the MAC address in old version of ifconfig; it comes in the first line - if (line.IndexOf(hWaddr) >= 0) - { - var startIndexHwd = line.IndexOf(hWaddr) + hWaddr.Length; - adapter.MacAddress = line.Substring(startIndexHwd, 17).Trim(); - } - - // Parse the info in lines other than the first - for (var j = i + 1; j < outputLines.Length; j++) - { - // Get the contents of the indented line - var indentedLine = outputLines[j]; - - // We have hit the next adapter info - if (char.IsLetterOrDigit(indentedLine[0])) - { - i = j - 1; - break; - } - - // Parse the MAC address in new versions of ifconfig; it no longer comes in the first line - if (indentedLine.IndexOf(ether) >= 0 && string.IsNullOrWhiteSpace(adapter.MacAddress)) - { - var startIndexHwd = indentedLine.IndexOf(ether) + ether.Length; - adapter.MacAddress = indentedLine.Substring(startIndexHwd, 17).Trim(); - } - - // Parse the IPv4 Address - { - var addressText = ParseOutputTagFromLine(indentedLine, "inet addr:") ?? ParseOutputTagFromLine(indentedLine, "inet "); - - if (addressText != null) - { - if (IPAddress.TryParse(addressText, out var outValue)) - adapter.IPv4 = outValue; - } - } - - // Parse the IPv6 Address - { - var addressText = ParseOutputTagFromLine(indentedLine, "inet6 addr:") ?? ParseOutputTagFromLine(indentedLine, "inet6 "); - - if (addressText != null) - { - if (IPAddress.TryParse(addressText, out var outValue)) - adapter.IPv6 = outValue; - } - } - - // we have hit the end of the output in an indented line - if (j >= outputLines.Length - 1) - i = outputLines.Length; - } - - // Retrieve the wireless LAN info - var wlanInfo = wlanOutput.FirstOrDefault(x => x.StartsWith(adapter.Name)); - - if (wlanInfo != null) - { - adapter.IsWireless = true; - var essidParts = wlanInfo.Split(new[] { EssidTag }, StringSplitOptions.RemoveEmptyEntries); - if (essidParts.Length >= 2) - { - adapter.AccessPointName = essidParts[1].Replace("\"", string.Empty).Trim(); - } - } - - // Add the current adapter to the result - result.Add(adapter); - } - - return result.OrderBy(x => x.Name).ToList(); - } - - /// - /// Retrieves current wireless connected network name. - /// - /// The connected network name. - public string GetWirelessNetworkName() => ProcessRunner.GetProcessOutputAsync("iwgetid", "-r").Result; - - /// - /// Parses the output tag from the given line. - /// - /// The indented line. - /// Name of the tag. - /// The value after the tag identifier - private static string ParseOutputTagFromLine(string indentedLine, string tagName) - { - if (indentedLine.IndexOf(tagName) < 0) - return null; - - var startIndex = indentedLine.IndexOf(tagName) + tagName.Length; - var builder = new StringBuilder(1024); - for (var c = startIndex; c < indentedLine.Length; c++) - { - var currentChar = indentedLine[c]; - if (!char.IsPunctuation(currentChar) && !char.IsLetterOrDigit(currentChar)) - break; - - builder.Append(currentChar); - } - - return builder.ToString(); - } - } + public String HostName => Network.HostName; + + /// + /// Retrieves the wireless networks. + /// + /// The adapter. + /// A list of WiFi networks + public List RetrieveWirelessNetworks(String adapter) => this.RetrieveWirelessNetworks(new[] { adapter }); + + /// + /// Retrieves the wireless networks. + /// + /// The adapters. + /// A list of WiFi networks + public List RetrieveWirelessNetworks(String[] adapters = null) { + List result = new List(); + + foreach(String networkAdapter in adapters ?? this.RetrieveAdapters().Where(x => x.IsWireless).Select(x => x.Name)) { + String wirelessOutput = ProcessRunner.GetProcessOutputAsync("iwlist", $"{networkAdapter} scanning").Result; + String[] outputLines = + wirelessOutput.Split('\n') + .Select(x => x.Trim()) + .Where(x => String.IsNullOrWhiteSpace(x) == false) + .ToArray(); + + for(Int32 i = 0; i < outputLines.Length; i++) { + String line = outputLines[i]; + + if(line.StartsWith(EssidTag) == false) { + continue; + } + + WirelessNetworkInfo network = new WirelessNetworkInfo() { + Name = line.Replace(EssidTag, String.Empty).Replace("\"", String.Empty) + }; + + while(true) { + if(i + 1 >= outputLines.Length) { + break; + } + + // should look for two lines before the ESSID acording to the scan + line = outputLines[i - 2]; + + if(line.StartsWith("Quality=")) { + network.Quality = line.Replace("Quality=", String.Empty); + break; + } + } + + while(true) { + if(i + 1 >= outputLines.Length) { + break; + } + + // should look for a line before the ESSID acording to the scan + line = outputLines[i - 1]; + + if(line.StartsWith("Encryption key:")) { + network.IsEncrypted = line.Replace("Encryption key:", String.Empty).Trim() == "on"; + break; + } + } + + if(result.Any(x => x.Name == network.Name) == false) { + result.Add(network); + } + } + } + + return result.OrderBy(x => x.Name).ToList(); + } + + /// + /// Setups the wireless network. + /// + /// Name of the adapter. + /// The network ssid. + /// The password. + /// The 2-letter country code in uppercase. Default is US. + /// True if successful. Otherwise, false. + public Boolean SetupWirelessNetwork(String adapterName, String networkSsid, String password = null, String countryCode = "US") { + // TODO: Get the country where the device is located to set 'country' param in payload var + String payload = $"country={countryCode}\nctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\nupdate_config=1\n"; + payload += String.IsNullOrEmpty(password) + ? $"network={{\n\tssid=\"{networkSsid}\"\n\t}}\n" + : $"network={{\n\tssid=\"{networkSsid}\"\n\tpsk=\"{password}\"\n\t}}\n"; + try { + File.WriteAllText("/etc/wpa_supplicant/wpa_supplicant.conf", payload); + ProcessRunner.GetProcessOutputAsync("pkill", "-f wpa_supplicant").Wait(); + ProcessRunner.GetProcessOutputAsync("ifdown", adapterName).Wait(); + ProcessRunner.GetProcessOutputAsync("ifup", adapterName).Wait(); + } catch(Exception ex) { + ex.Log(nameof(NetworkSettings)); + return false; + } + + return true; + } + + /// + /// Retrieves the network adapters. + /// + /// A list of network adapters. + public List RetrieveAdapters() { + const String hWaddr = "HWaddr "; + const String ether = "ether "; + + List result = new List(); + String interfacesOutput = ProcessRunner.GetProcessOutputAsync("ifconfig").Result; + String[] wlanOutput = ProcessRunner.GetProcessOutputAsync("iwconfig") + .Result.Split('\n') + .Where(x => x.Contains("no wireless extensions.") == false) + .ToArray(); + + String[] outputLines = interfacesOutput.Split('\n').Where(x => String.IsNullOrWhiteSpace(x) == false).ToArray(); + + for(Int32 i = 0; i < outputLines.Length; i++) { + // grab the current line + String line = outputLines[i]; + + // skip if the line is indented + if(Char.IsLetterOrDigit(line[0]) == false) { + continue; + } + + // Read the line as an adatper + NetworkAdapterInfo adapter = new NetworkAdapterInfo { + Name = line.Substring(0, line.IndexOf(' ')).TrimEnd(':') + }; + + // Parse the MAC address in old version of ifconfig; it comes in the first line + if(line.IndexOf(hWaddr) >= 0) { + Int32 startIndexHwd = line.IndexOf(hWaddr) + hWaddr.Length; + adapter.MacAddress = line.Substring(startIndexHwd, 17).Trim(); + } + + // Parse the info in lines other than the first + for(Int32 j = i + 1; j < outputLines.Length; j++) { + // Get the contents of the indented line + String indentedLine = outputLines[j]; + + // We have hit the next adapter info + if(Char.IsLetterOrDigit(indentedLine[0])) { + i = j - 1; + break; + } + + // Parse the MAC address in new versions of ifconfig; it no longer comes in the first line + if(indentedLine.IndexOf(ether) >= 0 && String.IsNullOrWhiteSpace(adapter.MacAddress)) { + Int32 startIndexHwd = indentedLine.IndexOf(ether) + ether.Length; + adapter.MacAddress = indentedLine.Substring(startIndexHwd, 17).Trim(); + } + + // Parse the IPv4 Address + { + String addressText = ParseOutputTagFromLine(indentedLine, "inet addr:") ?? ParseOutputTagFromLine(indentedLine, "inet "); + + if(addressText != null) { + if(IPAddress.TryParse(addressText, out IPAddress outValue)) { + adapter.IPv4 = outValue; + } + } + } + + // Parse the IPv6 Address + { + String addressText = ParseOutputTagFromLine(indentedLine, "inet6 addr:") ?? ParseOutputTagFromLine(indentedLine, "inet6 "); + + if(addressText != null) { + if(IPAddress.TryParse(addressText, out IPAddress outValue)) { + adapter.IPv6 = outValue; + } + } + } + + // we have hit the end of the output in an indented line + if(j >= outputLines.Length - 1) { + i = outputLines.Length; + } + } + + // Retrieve the wireless LAN info + String wlanInfo = wlanOutput.FirstOrDefault(x => x.StartsWith(adapter.Name)); + + if(wlanInfo != null) { + adapter.IsWireless = true; + String[] essidParts = wlanInfo.Split(new[] { EssidTag }, StringSplitOptions.RemoveEmptyEntries); + if(essidParts.Length >= 2) { + adapter.AccessPointName = essidParts[1].Replace("\"", String.Empty).Trim(); + } + } + + // Add the current adapter to the result + result.Add(adapter); + } + + return result.OrderBy(x => x.Name).ToList(); + } + + /// + /// Retrieves current wireless connected network name. + /// + /// The connected network name. + public String GetWirelessNetworkName() => ProcessRunner.GetProcessOutputAsync("iwgetid", "-r").Result; + + /// + /// Parses the output tag from the given line. + /// + /// The indented line. + /// Name of the tag. + /// The value after the tag identifier + private static String ParseOutputTagFromLine(String indentedLine, String tagName) { + if(indentedLine.IndexOf(tagName) < 0) { + return null; + } + + Int32 startIndex = indentedLine.IndexOf(tagName) + tagName.Length; + StringBuilder builder = new StringBuilder(1024); + for(Int32 c = startIndex; c < indentedLine.Length; c++) { + Char currentChar = indentedLine[c]; + if(!Char.IsPunctuation(currentChar) && !Char.IsLetterOrDigit(currentChar)) { + break; + } + + _ = builder.Append(currentChar); + } + + return builder.ToString(); + } + } } diff --git a/Unosquare.RaspberryIO/Computer/OsInfo.cs b/Unosquare.RaspberryIO/Computer/OsInfo.cs index 80ecfde..104ba17 100644 --- a/Unosquare.RaspberryIO/Computer/OsInfo.cs +++ b/Unosquare.RaspberryIO/Computer/OsInfo.cs @@ -1,46 +1,58 @@ -namespace Unosquare.RaspberryIO.Computer -{ +using System; + +namespace Unosquare.RaspberryIO.Computer { + /// + /// Represents the OS Information + /// + public class OsInfo { /// - /// Represents the OS Information + /// System name /// - public class OsInfo - { - /// - /// System name - /// - public string SysName { get; set; } - - /// - /// Node name - /// - public string NodeName { get; set; } - - /// - /// Release level - /// - public string Release { get; set; } - - /// - /// Version level - /// - public string Version { get; set; } - - /// - /// Hardware level - /// - public string Machine { get; set; } - - /// - /// Domain name - /// - public string DomainName { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() => $"{SysName} {Release} {Version}"; - } + public String SysName { + get; set; + } + + /// + /// Node name + /// + public String NodeName { + get; set; + } + + /// + /// Release level + /// + public String Release { + get; set; + } + + /// + /// Version level + /// + public String Version { + get; set; + } + + /// + /// Hardware level + /// + public String Machine { + get; set; + } + + /// + /// Domain name + /// + public String DomainName { + get; set; + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override String ToString() => $"{this.SysName} {this.Release} {this.Version}"; + } } diff --git a/Unosquare.RaspberryIO/Computer/PiVersion.cs b/Unosquare.RaspberryIO/Computer/PiVersion.cs index a6af028..15d86fe 100644 --- a/Unosquare.RaspberryIO/Computer/PiVersion.cs +++ b/Unosquare.RaspberryIO/Computer/PiVersion.cs @@ -1,134 +1,132 @@ -namespace Unosquare.RaspberryIO.Computer -{ +namespace Unosquare.RaspberryIO.Computer { + /// + /// Defines the board revision codes of the different versions of the Raspberry Pi + /// http://www.raspberrypi-spy.co.uk/2012/09/checking-your-raspberry-pi-board-version/ + /// + public enum PiVersion { /// - /// Defines the board revision codes of the different versions of the Raspberry Pi - /// http://www.raspberrypi-spy.co.uk/2012/09/checking-your-raspberry-pi-board-version/ + /// The unknown version /// - public enum PiVersion - { - /// - /// The unknown version - /// - Unknown = 0, - - /// - /// The model b rev1 - /// - ModelBRev1 = 0x0002, - - /// - /// The model b rev1 ec N0001 - /// - ModelBRev1ECN0001 = 0x0003, - - /// - /// The model b rev2x04 - /// - ModelBRev2x04 = 0x0004, - - /// - /// The model b rev2x05 - /// - ModelBRev2x05 = 0x0005, - - /// - /// The model b rev2x06 - /// - ModelBRev2x06 = 0x0006, - - /// - /// The model ax07 - /// - ModelAx07 = 0x0007, - - /// - /// The model ax08 - /// - ModelAx08 = 0x0008, - - /// - /// The model ax09 - /// - ModelAx09 = 0x0009, - - /// - /// The model b rev2x0d - /// - ModelBRev2x0d, - - /// - /// The model b rev2x0e - /// - ModelBRev2x0e, - - /// - /// The model b rev2x0f - /// - ModelBRev2x0f = 0x000f, - - /// - /// The model b plus0x10 - /// - ModelBPlus0x10 = 0x0010, - - /// - /// The model b plus0x13 - /// - ModelBPlus0x13 = 0x0013, - - /// - /// The compute module0x11 - /// - ComputeModule0x11 = 0x0011, - - /// - /// The compute module0x14 - /// - ComputeModule0x14 = 0x0014, - - /// - /// The model a plus0x12 - /// - ModelAPlus0x12 = 0x0012, - - /// - /// The model a plus0x15 - /// - ModelAPlus0x15 = 0x0015, - - /// - /// The pi2 model B1V1 sony - /// - Pi2ModelB1v1Sony = 0xa01041, - - /// - /// The pi2 model B1V1 embest - /// - Pi2ModelB1v1Embest = 0xa21041, - - /// - /// The pi2 model B1V2 - /// - Pi2ModelB1v2 = 0xa22042, - - /// - /// The pi zero1v2 - /// - PiZero1v2 = 0x900092, - - /// - /// The pi zero1v3 - /// - PiZero1v3 = 0x900093, - - /// - /// The pi3 model b sony - /// - Pi3ModelBSony = 0xa02082, - - /// - /// The pi3 model b embest - /// - Pi3ModelBEmbest = 0xa22082 - } + Unknown = 0, + + /// + /// The model b rev1 + /// + ModelBRev1 = 0x0002, + + /// + /// The model b rev1 ec N0001 + /// + ModelBRev1ECN0001 = 0x0003, + + /// + /// The model b rev2x04 + /// + ModelBRev2x04 = 0x0004, + + /// + /// The model b rev2x05 + /// + ModelBRev2x05 = 0x0005, + + /// + /// The model b rev2x06 + /// + ModelBRev2x06 = 0x0006, + + /// + /// The model ax07 + /// + ModelAx07 = 0x0007, + + /// + /// The model ax08 + /// + ModelAx08 = 0x0008, + + /// + /// The model ax09 + /// + ModelAx09 = 0x0009, + + /// + /// The model b rev2x0d + /// + ModelBRev2x0d, + + /// + /// The model b rev2x0e + /// + ModelBRev2x0e, + + /// + /// The model b rev2x0f + /// + ModelBRev2x0f = 0x000f, + + /// + /// The model b plus0x10 + /// + ModelBPlus0x10 = 0x0010, + + /// + /// The model b plus0x13 + /// + ModelBPlus0x13 = 0x0013, + + /// + /// The compute module0x11 + /// + ComputeModule0x11 = 0x0011, + + /// + /// The compute module0x14 + /// + ComputeModule0x14 = 0x0014, + + /// + /// The model a plus0x12 + /// + ModelAPlus0x12 = 0x0012, + + /// + /// The model a plus0x15 + /// + ModelAPlus0x15 = 0x0015, + + /// + /// The pi2 model B1V1 sony + /// + Pi2ModelB1v1Sony = 0xa01041, + + /// + /// The pi2 model B1V1 embest + /// + Pi2ModelB1v1Embest = 0xa21041, + + /// + /// The pi2 model B1V2 + /// + Pi2ModelB1v2 = 0xa22042, + + /// + /// The pi zero1v2 + /// + PiZero1v2 = 0x900092, + + /// + /// The pi zero1v3 + /// + PiZero1v3 = 0x900093, + + /// + /// The pi3 model b sony + /// + Pi3ModelBSony = 0xa02082, + + /// + /// The pi3 model b embest + /// + Pi3ModelBEmbest = 0xa22082 + } } \ No newline at end of file diff --git a/Unosquare.RaspberryIO/Computer/SystemInfo.cs b/Unosquare.RaspberryIO/Computer/SystemInfo.cs index fad1ec0..6fa9ebc 100644 --- a/Unosquare.RaspberryIO/Computer/SystemInfo.cs +++ b/Unosquare.RaspberryIO/Computer/SystemInfo.cs @@ -1,344 +1,343 @@ -namespace Unosquare.RaspberryIO.Computer -{ - using Native; - using Swan.Abstractions; - using System; - using System.Collections.Generic; - using System.Globalization; - using System.IO; - using System.Linq; - using System.Reflection; - +using Unosquare.RaspberryIO.Native; +using Unosquare.Swan.Abstractions; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; + +namespace Unosquare.RaspberryIO.Computer { + /// + /// http://raspberry-pi-guide.readthedocs.io/en/latest/system.html + /// + public sealed class SystemInfo : SingletonBase { + private const String CpuInfoFilePath = "/proc/cpuinfo"; + private const String MemInfoFilePath = "/proc/meminfo"; + private const String UptimeFilePath = "/proc/uptime"; + private static readonly StringComparer StringComparer = StringComparer.InvariantCultureIgnoreCase; + [System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0052:Ungelesene private Member entfernen", Justification = "")] + private static readonly Object SyncRoot = new Object(); + /// - /// http://raspberry-pi-guide.readthedocs.io/en/latest/system.html + /// Prevents a default instance of the class from being created. /// - public sealed class SystemInfo : SingletonBase - { - private const string CpuInfoFilePath = "/proc/cpuinfo"; - private const string MemInfoFilePath = "/proc/meminfo"; - private const string UptimeFilePath = "/proc/uptime"; - private static readonly StringComparer StringComparer = StringComparer.InvariantCultureIgnoreCase; - - private static readonly object SyncRoot = new object(); - - /// - /// Prevents a default instance of the class from being created. - /// - /// Could not initialize the GPIO controller - private SystemInfo() - { - #region Obtain and format a property dictionary - - var properties = - typeof(SystemInfo).GetTypeInfo() - .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) - .Where( - p => - p.CanWrite && p.CanRead && - (p.PropertyType == typeof(string) || p.PropertyType == typeof(string[]))) - .ToArray(); - var propDictionary = new Dictionary(StringComparer); - - foreach (var prop in properties) - { - propDictionary[prop.Name.Replace(" ", string.Empty).ToLowerInvariant().Trim()] = prop; - } - - #endregion - - #region Extract CPU information - - if (File.Exists(CpuInfoFilePath)) - { - var cpuInfoLines = File.ReadAllLines(CpuInfoFilePath); - - foreach (var line in cpuInfoLines) - { - var lineParts = line.Split(new[] { ':' }, 2); - if (lineParts.Length != 2) - continue; - - var propertyKey = lineParts[0].Trim().Replace(" ", string.Empty); - var propertyStringValue = lineParts[1].Trim(); - - if (!propDictionary.ContainsKey(propertyKey)) continue; - - var property = propDictionary[propertyKey]; - if (property.PropertyType == typeof(string)) - { - property.SetValue(this, propertyStringValue); - } - else if (property.PropertyType == typeof(string[])) - { - var propertyArrayAvalue = propertyStringValue.Split(' '); - property.SetValue(this, propertyArrayAvalue); - } - } - } - - #endregion - - #region Extract Memory Information - - if (File.Exists(MemInfoFilePath)) - { - var memInfoLines = File.ReadAllLines(MemInfoFilePath); - foreach (var line in memInfoLines) - { - var lineParts = line.Split(new[] { ':' }, 2); - if (lineParts.Length != 2) - continue; - - if (lineParts[0].ToLowerInvariant().Trim().Equals("memtotal") == false) - continue; - - var memKb = lineParts[1].ToLowerInvariant().Trim().Replace("kb", string.Empty).Trim(); - - if (int.TryParse(memKb, out var parsedMem)) - { - InstalledRam = parsedMem * 1024; - break; - } - } - } - - #endregion - - #region Board Version and Form Factor - - try - { - if (string.IsNullOrWhiteSpace(Revision) == false && - int.TryParse( - Revision.ToUpperInvariant(), - NumberStyles.HexNumber, - CultureInfo.InvariantCulture, - out var boardVersion)) - { - RaspberryPiVersion = PiVersion.Unknown; - if (Enum.GetValues(typeof(PiVersion)).Cast().Contains(boardVersion)) - { - RaspberryPiVersion = (PiVersion)boardVersion; - } - } - - WiringPiBoardRevision = WiringPi.PiBoardRev(); - } - catch - { - /* Ignore */ - } - - #endregion - - #region Version Information - - { - var libParts = WiringPi.WiringPiLibrary.Split('.'); - var major = int.Parse(libParts[libParts.Length - 2]); - var minor = int.Parse(libParts[libParts.Length - 1]); - var version = new Version(major, minor); - WiringPiVersion = version; - } - - #endregion - - #region Extract OS Info - - try - { - Standard.Uname(out var unameInfo); - OperatingSystem = new OsInfo - { - DomainName = unameInfo.DomainName, - Machine = unameInfo.Machine, - NodeName = unameInfo.NodeName, - Release = unameInfo.Release, - SysName = unameInfo.SysName, - Version = unameInfo.Version - }; - } - catch - { - OperatingSystem = new OsInfo(); - } - - #endregion - } - - /// - /// Gets the wiring pi library version. - /// - public Version WiringPiVersion { get; } - - /// - /// Gets the OS information. - /// - /// - /// The os information. - /// - public OsInfo OperatingSystem { get; } - - /// - /// Gets the Raspberry Pi version. - /// - public PiVersion RaspberryPiVersion { get; } - - /// - /// Gets the Wiring Pi board revision (1 or 2). - /// - /// - /// The wiring pi board revision. - /// - public int WiringPiBoardRevision { get; } - - /// - /// Gets the number of processor cores. - /// - public int ProcessorCount - { - get - { - if (int.TryParse(Processor, out var outIndex)) - { - return outIndex + 1; - } - - return 0; - } - } - - /// - /// Gets the installed ram in bytes. - /// - public int InstalledRam { get; } - - /// - /// Gets a value indicating whether this CPU is little endian. - /// - public bool IsLittleEndian => BitConverter.IsLittleEndian; - - /// - /// Gets the CPU model name. - /// - public string ModelName { get; private set; } - - /// - /// Gets a list of supported CPU features. - /// - public string[] Features { get; private set; } - - /// - /// Gets the CPU implementer hex code. - /// - public string CpuImplementer { get; private set; } - - /// - /// Gets the CPU architecture code. - /// - public string CpuArchitecture { get; private set; } - - /// - /// Gets the CPU variant code. - /// - public string CpuVariant { get; private set; } - - /// - /// Gets the CPU part code. - /// - public string CpuPart { get; private set; } - - /// - /// Gets the CPU revision code. - /// - public string CpuRevision { get; private set; } - - /// - /// Gets the hardware model number. - /// - public string Hardware { get; private set; } - - /// - /// Gets the hardware revision number. - /// - public string Revision { get; private set; } - - /// - /// Gets the serial number. - /// - public string Serial { get; private set; } - - /// - /// Gets the system uptime (in seconds). - /// - public double Uptime - { - get - { - try - { - if (File.Exists(UptimeFilePath) == false) return 0; - var parts = File.ReadAllText(UptimeFilePath).Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - if (parts.Length >= 1 && float.TryParse(parts[0], out var result)) - return result; - } - catch - { - /* Ignore */ - } - - return 0; - } - } - - /// - /// Gets the uptime in TimeSpan. - /// - public TimeSpan UptimeTimeSpan => TimeSpan.FromSeconds(Uptime); - - /// - /// Placeholder for processor index - /// - private string Processor { get; set; } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - var properties = typeof(SystemInfo).GetTypeInfo().GetProperties(BindingFlags.Instance | BindingFlags.Public) - .Where(p => p.CanRead && ( - p.PropertyType == typeof(string) || - p.PropertyType == typeof(string[]) || - p.PropertyType == typeof(int) || - p.PropertyType == typeof(bool) || - p.PropertyType == typeof(TimeSpan))) - .ToArray(); - - var properyValues = new List - { + /// Could not initialize the GPIO controller + private SystemInfo() { + #region Obtain and format a property dictionary + + PropertyInfo[] properties = + typeof(SystemInfo).GetTypeInfo() + .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + .Where( + p => + p.CanWrite && p.CanRead && + (p.PropertyType == typeof(String) || p.PropertyType == typeof(String[]))) + .ToArray(); + Dictionary propDictionary = new Dictionary(StringComparer); + + foreach(PropertyInfo prop in properties) { + propDictionary[prop.Name.Replace(" ", String.Empty).ToLowerInvariant().Trim()] = prop; + } + + #endregion + + #region Extract CPU information + + if(File.Exists(CpuInfoFilePath)) { + String[] cpuInfoLines = File.ReadAllLines(CpuInfoFilePath); + + foreach(String line in cpuInfoLines) { + String[] lineParts = line.Split(new[] { ':' }, 2); + if(lineParts.Length != 2) { + continue; + } + + String propertyKey = lineParts[0].Trim().Replace(" ", String.Empty); + String propertyStringValue = lineParts[1].Trim(); + + if(!propDictionary.ContainsKey(propertyKey)) { + continue; + } + + PropertyInfo property = propDictionary[propertyKey]; + if(property.PropertyType == typeof(String)) { + property.SetValue(this, propertyStringValue); + } else if(property.PropertyType == typeof(String[])) { + String[] propertyArrayAvalue = propertyStringValue.Split(' '); + property.SetValue(this, propertyArrayAvalue); + } + } + } + + #endregion + + #region Extract Memory Information + + if(File.Exists(MemInfoFilePath)) { + String[] memInfoLines = File.ReadAllLines(MemInfoFilePath); + foreach(String line in memInfoLines) { + String[] lineParts = line.Split(new[] { ':' }, 2); + if(lineParts.Length != 2) { + continue; + } + + if(lineParts[0].ToLowerInvariant().Trim().Equals("memtotal") == false) { + continue; + } + + String memKb = lineParts[1].ToLowerInvariant().Trim().Replace("kb", String.Empty).Trim(); + + if(Int32.TryParse(memKb, out Int32 parsedMem)) { + this.InstalledRam = parsedMem * 1024; + break; + } + } + } + + #endregion + + #region Board Version and Form Factor + + try { + if(String.IsNullOrWhiteSpace(this.Revision) == false && + Int32.TryParse( + this.Revision.ToUpperInvariant(), + NumberStyles.HexNumber, + CultureInfo.InvariantCulture, + out Int32 boardVersion)) { + this.RaspberryPiVersion = PiVersion.Unknown; + if(Enum.GetValues(typeof(PiVersion)).Cast().Contains(boardVersion)) { + this.RaspberryPiVersion = (PiVersion)boardVersion; + } + } + + this.WiringPiBoardRevision = WiringPi.PiBoardRev(); + } catch { + /* Ignore */ + } + + #endregion + + #region Version Information + + { + String[] libParts = WiringPi.WiringPiLibrary.Split('.'); + Int32 major = Int32.Parse(libParts[libParts.Length - 2]); + Int32 minor = Int32.Parse(libParts[libParts.Length - 1]); + Version version = new Version(major, minor); + this.WiringPiVersion = version; + } + + #endregion + + #region Extract OS Info + + try { + _ = Standard.Uname(out SystemName unameInfo); + this.OperatingSystem = new OsInfo { + DomainName = unameInfo.DomainName, + Machine = unameInfo.Machine, + NodeName = unameInfo.NodeName, + Release = unameInfo.Release, + SysName = unameInfo.SysName, + Version = unameInfo.Version + }; + } catch { + this.OperatingSystem = new OsInfo(); + } + + #endregion + } + + /// + /// Gets the wiring pi library version. + /// + public Version WiringPiVersion { + get; + } + + /// + /// Gets the OS information. + /// + /// + /// The os information. + /// + public OsInfo OperatingSystem { + get; + } + + /// + /// Gets the Raspberry Pi version. + /// + public PiVersion RaspberryPiVersion { + get; + } + + /// + /// Gets the Wiring Pi board revision (1 or 2). + /// + /// + /// The wiring pi board revision. + /// + public Int32 WiringPiBoardRevision { + get; + } + + /// + /// Gets the number of processor cores. + /// + public Int32 ProcessorCount => Int32.TryParse(this.Processor, out Int32 outIndex) ? outIndex + 1 : 0; + + /// + /// Gets the installed ram in bytes. + /// + public Int32 InstalledRam { + get; + } + + /// + /// Gets a value indicating whether this CPU is little endian. + /// + public Boolean IsLittleEndian => BitConverter.IsLittleEndian; + + /// + /// Gets the CPU model name. + /// + public String ModelName { + get; private set; + } + + /// + /// Gets a list of supported CPU features. + /// + public String[] Features { + get; private set; + } + + /// + /// Gets the CPU implementer hex code. + /// + public String CpuImplementer { + get; private set; + } + + /// + /// Gets the CPU architecture code. + /// + public String CpuArchitecture { + get; private set; + } + + /// + /// Gets the CPU variant code. + /// + public String CpuVariant { + get; private set; + } + + /// + /// Gets the CPU part code. + /// + public String CpuPart { + get; private set; + } + + /// + /// Gets the CPU revision code. + /// + public String CpuRevision { + get; private set; + } + + /// + /// Gets the hardware model number. + /// + public String Hardware { + get; private set; + } + + /// + /// Gets the hardware revision number. + /// + public String Revision { + get; private set; + } + + /// + /// Gets the serial number. + /// + public String Serial { + get; private set; + } + + /// + /// Gets the system uptime (in seconds). + /// + public Double Uptime { + get { + try { + if(File.Exists(UptimeFilePath) == false) { + return 0; + } + + String[] parts = File.ReadAllText(UptimeFilePath).Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if(parts.Length >= 1 && Single.TryParse(parts[0], out Single result)) { + return result; + } + } catch { + /* Ignore */ + } + + return 0; + } + } + + /// + /// Gets the uptime in TimeSpan. + /// + public TimeSpan UptimeTimeSpan => TimeSpan.FromSeconds(this.Uptime); + + /// + /// Placeholder for processor index + /// + private String Processor { + get; set; + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override String ToString() { + PropertyInfo[] properties = typeof(SystemInfo).GetTypeInfo().GetProperties(BindingFlags.Instance | BindingFlags.Public) + .Where(p => p.CanRead && ( + p.PropertyType == typeof(String) || + p.PropertyType == typeof(String[]) || + p.PropertyType == typeof(Int32) || + p.PropertyType == typeof(Boolean) || + p.PropertyType == typeof(TimeSpan))) + .ToArray(); + + List properyValues = new List + { "System Information", - $"\t{nameof(WiringPiVersion),-22}: {WiringPiVersion}", - $"\t{nameof(RaspberryPiVersion),-22}: {RaspberryPiVersion}" - }; - - foreach (var property in properties) - { - if (property.PropertyType != typeof(string[])) - { - properyValues.Add($"\t{property.Name,-22}: {property.GetValue(this)}"); - } - else if (property.GetValue(this) is string[] allValues) - { - var concatValues = string.Join(" ", allValues); - properyValues.Add($"\t{property.Name,-22}: {concatValues}"); - } - } - - return string.Join(Environment.NewLine, properyValues.ToArray()); - } - } + $"\t{nameof(this.WiringPiVersion),-22}: {this.WiringPiVersion}", + $"\t{nameof(this.RaspberryPiVersion),-22}: {this.RaspberryPiVersion}" + }; + + foreach(PropertyInfo property in properties) { + if(property.PropertyType != typeof(String[])) { + properyValues.Add($"\t{property.Name,-22}: {property.GetValue(this)}"); + } else if(property.GetValue(this) is String[] allValues) { + String concatValues = String.Join(" ", allValues); + properyValues.Add($"\t{property.Name,-22}: {concatValues}"); + } + } + + return String.Join(Environment.NewLine, properyValues.ToArray()); + } + } } \ No newline at end of file diff --git a/Unosquare.RaspberryIO/Computer/WirelessNetworkInfo.cs b/Unosquare.RaspberryIO/Computer/WirelessNetworkInfo.cs index 71d0cb9..f820698 100644 --- a/Unosquare.RaspberryIO/Computer/WirelessNetworkInfo.cs +++ b/Unosquare.RaspberryIO/Computer/WirelessNetworkInfo.cs @@ -1,23 +1,29 @@ -namespace Unosquare.RaspberryIO.Computer -{ +using System; + +namespace Unosquare.RaspberryIO.Computer { + /// + /// Represents a wireless network information + /// + public class WirelessNetworkInfo { /// - /// Represents a wireless network information + /// Gets the ESSID of the Wireless network. /// - public class WirelessNetworkInfo - { - /// - /// Gets the ESSID of the Wireless network. - /// - public string Name { get; internal set; } - - /// - /// Gets the network quality. - /// - public string Quality { get; internal set; } - - /// - /// Gets a value indicating whether this instance is encrypted. - /// - public bool IsEncrypted { get; internal set; } - } + public String Name { + get; internal set; + } + + /// + /// Gets the network quality. + /// + public String Quality { + get; internal set; + } + + /// + /// Gets a value indicating whether this instance is encrypted. + /// + public Boolean IsEncrypted { + get; internal set; + } + } } diff --git a/Unosquare.RaspberryIO/Gpio/Enums.cs b/Unosquare.RaspberryIO/Gpio/Enums.cs index 95e0032..5f7a7e7 100644 --- a/Unosquare.RaspberryIO/Gpio/Enums.cs +++ b/Unosquare.RaspberryIO/Gpio/Enums.cs @@ -1,579 +1,566 @@ -namespace Unosquare.RaspberryIO.Gpio -{ +namespace Unosquare.RaspberryIO.Gpio { + /// + /// Defines the different drive modes of a GPIO pin + /// + public enum GpioPinDriveMode { /// - /// Defines the different drive modes of a GPIO pin + /// Input drive mode (perform reads) /// - public enum GpioPinDriveMode - { - /// - /// Input drive mode (perform reads) - /// - Input = 0, - - /// - /// Output drive mode (perform writes) - /// - Output = 1, - - /// - /// PWM output mode (only certain pins support this -- 2 of them at the moment) - /// - PwmOutput = 2, - - /// - /// GPIO Clock output mode (only a pin supports this at this time) - /// - GpioClock = 3 - } - + Input = 0, + /// - /// The GPIO pin resistor mode. This is used on input pins so that their - /// lines are not floating + /// Output drive mode (perform writes) /// - public enum GpioPinResistorPullMode - { - /// - /// Pull resistor not active. Line floating - /// - Off = 0, - - /// - /// Pull resistor sets a default value of 0 on no-connects - /// - PullDown = 1, - - /// - /// Pull resistor sets a default value of 1 on no-connects - /// - PullUp = 2, - } - + Output = 1, + /// - /// The PWM mode. + /// PWM output mode (only certain pins support this -- 2 of them at the moment) /// - public enum PwmMode - { - /// - /// PWM pulses are sent using mark-sign patterns (old school) - /// - MarkSign = 0, - - /// - /// PWM pulses are sent as a balanced signal (default, newer mode) - /// - Balanced = 1, - } - + PwmOutput = 2, + /// - /// Defines the different edge detection modes for pin interrupts + /// GPIO Clock output mode (only a pin supports this at this time) /// - public enum EdgeDetection - { - /// - /// Assumes edge detection was already setup externally - /// - ExternalSetup = 0, - - /// - /// Falling Edge - /// - FallingEdge = 1, - - /// - /// Rising edge - /// - RisingEdge = 2, - - /// - /// Both, rising and falling edges - /// - RisingAndFallingEdges = 3 - } - + GpioClock = 3 + } + + /// + /// The GPIO pin resistor mode. This is used on input pins so that their + /// lines are not floating + /// + public enum GpioPinResistorPullMode { /// - /// Defines the GPIO Pin values 0 for low, 1 for High + /// Pull resistor not active. Line floating /// - public enum GpioPinValue - { - /// - /// Digital high - /// - High = 1, - - /// - /// Digital low - /// - Low = 0 - } - + Off = 0, + /// - /// Defines the Header connectors available + /// Pull resistor sets a default value of 0 on no-connects /// - public enum GpioHeader - { - /// - /// Not defined - /// - None, - - /// - /// The P1 connector (main connector) - /// - P1, - - /// - /// The P5 connector (auxiliary, not commonly used) - /// - P5, - } - + PullDown = 1, + /// - /// Defines all the available Wiring Pi Pin Numbers + /// Pull resistor sets a default value of 1 on no-connects /// - public enum WiringPiPin - { - /// - /// The unknown - /// - Unknown = -1, - - /// - /// The pin00 - /// - Pin00 = 0, - - /// - /// The pin01 - /// - Pin01 = 1, - - /// - /// The pin02 - /// - Pin02 = 2, - - /// - /// The pin03 - /// - Pin03 = 3, - - /// - /// The pin04 - /// - Pin04 = 4, - - /// - /// The pin05 - /// - Pin05 = 5, - - /// - /// The pin06 - /// - Pin06 = 6, - - /// - /// The pin07 - /// - Pin07 = 7, - - /// - /// The pin08 - /// - Pin08 = 8, - - /// - /// The pin09 - /// - Pin09 = 9, - - /// - /// The pin10 - /// - Pin10 = 10, - - /// - /// The pin11 - /// - Pin11 = 11, - - /// - /// The pin12 - /// - Pin12 = 12, - - /// - /// The pin13 - /// - Pin13 = 13, - - /// - /// The pin14 - /// - Pin14 = 14, - - /// - /// The pin15 - /// - Pin15 = 15, - - /// - /// The pin16 - /// - Pin16 = 16, - - /// - /// The pin17 - /// - Pin17 = 17, - - /// - /// The pin18 - /// - Pin18 = 18, - - /// - /// The pin19 - /// - Pin19 = 19, - - /// - /// The pin20 - /// - Pin20 = 20, - - /// - /// The pin21 - /// - Pin21 = 21, - - /// - /// The pin22 - /// - Pin22 = 22, - - /// - /// The pin23 - /// - Pin23 = 23, - - /// - /// The pin24 - /// - Pin24 = 24, - - /// - /// The pin25 - /// - Pin25 = 25, - - /// - /// The pin26 - /// - Pin26 = 26, - - /// - /// The pin27 - /// - Pin27 = 27, - - /// - /// The pin28 - /// - Pin28 = 28, - - /// - /// The pin29 - /// - Pin29 = 29, - - /// - /// The pin30 - /// - Pin30 = 30, - - /// - /// The pin31 - /// - Pin31 = 31, - } - + PullUp = 2, + } + + /// + /// The PWM mode. + /// + public enum PwmMode { /// - /// Enumerates the different pins on the P1 Header - /// as commonly referenced by Raspberry Pi Documentation. - /// Enumeration values correspond to the physical pin number. + /// PWM pulses are sent using mark-sign patterns (old school) /// - public enum P1 - { - /// - /// Header P1, GPIO Pin 02 - /// - Gpio02 = 3, - - /// - /// Header P1, GPIO Pin 03 - /// - Gpio03 = 5, - - /// - /// Header P1, GPIO Pin 04 - /// - Gpio04 = 7, - - /// - /// Header P1, GPIO Pin 17 - /// - Gpio17 = 11, - - /// - /// Header P1, GPIO Pin 27 - /// - Gpio27 = 13, - - /// - /// Header P1, GPIO Pin 22 - /// - Gpio22 = 15, - - /// - /// Header P1, GPIO Pin 10 - /// - Gpio10 = 19, - - /// - /// Header P1, GPIO Pin 09 - /// - Gpio09 = 21, - - /// - /// Header P1, GPIO Pin 11 - /// - Gpio11 = 23, - - /// - /// Header P1, GPIO Pin 05 - /// - Gpio05 = 29, - - /// - /// Header P1, GPIO Pin 06 - /// - Gpio06 = 31, - - /// - /// Header P1, GPIO Pin 13 - /// - Gpio13 = 33, - - /// - /// Header P1, GPIO Pin 19 - /// - Gpio19 = 35, - - /// - /// Header P1, GPIO Pin 26 - /// - Gpio26 = 37, - - /// - /// Header P1, GPIO Pin 14 - /// - Gpio14 = 8, - - /// - /// Header P1, GPIO Pin 15 - /// - Gpio15 = 10, - - /// - /// Header P1, GPIO Pin 18 - /// - Gpio18 = 12, - - /// - /// Header P1, GPIO Pin 23 - /// - Gpio23 = 16, - - /// - /// Header P1, GPIO Pin 24 - /// - Gpio24 = 18, - - /// - /// Header P1, GPIO Pin 25 - /// - Gpio25 = 22, - - /// - /// Header P1, GPIO Pin 08 - /// - Gpio08 = 24, - - /// - /// Header P1, GPIO Pin 07 - /// - Gpio07 = 26, - - /// - /// Header P1, GPIO Pin 12 - /// - Gpio12 = 32, - - /// - /// Header P1, GPIO Pin 16 - /// - Gpio16 = 36, - - /// - /// Header P1, GPIO Pin 20 - /// - Gpio20 = 38, - - /// - /// Header P1, GPIO Pin 21 - /// - Gpio21 = 40 - } - + MarkSign = 0, + /// - /// Enumerates the different pins on the P5 Header - /// as commonly referenced by Raspberry Pi documentation. - /// Enumeration values correspond to the physical pin number. + /// PWM pulses are sent as a balanced signal (default, newer mode) /// - public enum P5 - { - /// - /// Header P5, GPIO Pin 28 - /// - Gpio28 = 3, - - /// - /// Header P5, GPIO Pin 29 - /// - Gpio29 = 4, - - /// - /// Header P5, GPIO Pin 30 - /// - Gpio30 = 5, - - /// - /// Header P5, GPIO Pin 31 - /// - Gpio31 = 6 - } - + Balanced = 1, + } + + /// + /// Defines the different edge detection modes for pin interrupts + /// + public enum EdgeDetection { /// - /// Defines the different pin capabilities + /// Assumes edge detection was already setup externally /// - public enum PinCapability - { - /// - /// General Purpose capability: Digital and Analog Read/Write - /// - GP, - - /// - /// General Purpose Clock (not PWM) - /// - GPCLK, - - /// - /// i2c data channel - /// - I2CSDA, - - /// - /// i2c clock channel - /// - I2CSCL, - - /// - /// SPI Master Out, Slave In channel - /// - SPIMOSI, - - /// - /// SPI Master In, Slave Out channel - /// - SPIMISO, - - /// - /// SPI Clock channel - /// - SPICLK, - - /// - /// SPI Chip Select Channel - /// - SPICS, - - /// - /// UART Request to Send Channel - /// - UARTRTS, - - /// - /// UART Transmit Channel - /// - UARTTXD, - - /// - /// UART Receive Channel - /// - UARTRXD, - - /// - /// Hardware Pule Width Modulation - /// - PWM - } - + ExternalSetup = 0, + /// - /// Defines the SPI channel numbers + /// Falling Edge /// - internal enum SpiChannelNumber - { - /// - /// The channel 0 - /// - Channel0 = 0, - - /// - /// The channel 1 - /// - Channel1 = 1, - } - + FallingEdge = 1, + /// - /// Defines GPIO controller initialization modes + /// Rising edge /// - internal enum ControllerMode - { - /// - /// The not initialized - /// - NotInitialized, - - /// - /// The direct with wiring pi pins - /// - DirectWithWiringPiPins, - - /// - /// The direct with BCM pins - /// - DirectWithBcmPins, - - /// - /// The direct with header pins - /// - DirectWithHeaderPins, - - /// - /// The file stream with hardware pins - /// - FileStreamWithHardwarePins, - } + RisingEdge = 2, + + /// + /// Both, rising and falling edges + /// + RisingAndFallingEdges = 3 + } + + /// + /// Defines the GPIO Pin values 0 for low, 1 for High + /// + public enum GpioPinValue { + /// + /// Digital high + /// + High = 1, + + /// + /// Digital low + /// + Low = 0 + } + + /// + /// Defines the Header connectors available + /// + public enum GpioHeader { + /// + /// Not defined + /// + None, + + /// + /// The P1 connector (main connector) + /// + P1, + + /// + /// The P5 connector (auxiliary, not commonly used) + /// + P5, + } + + /// + /// Defines all the available Wiring Pi Pin Numbers + /// + public enum WiringPiPin { + /// + /// The unknown + /// + Unknown = -1, + + /// + /// The pin00 + /// + Pin00 = 0, + + /// + /// The pin01 + /// + Pin01 = 1, + + /// + /// The pin02 + /// + Pin02 = 2, + + /// + /// The pin03 + /// + Pin03 = 3, + + /// + /// The pin04 + /// + Pin04 = 4, + + /// + /// The pin05 + /// + Pin05 = 5, + + /// + /// The pin06 + /// + Pin06 = 6, + + /// + /// The pin07 + /// + Pin07 = 7, + + /// + /// The pin08 + /// + Pin08 = 8, + + /// + /// The pin09 + /// + Pin09 = 9, + + /// + /// The pin10 + /// + Pin10 = 10, + + /// + /// The pin11 + /// + Pin11 = 11, + + /// + /// The pin12 + /// + Pin12 = 12, + + /// + /// The pin13 + /// + Pin13 = 13, + + /// + /// The pin14 + /// + Pin14 = 14, + + /// + /// The pin15 + /// + Pin15 = 15, + + /// + /// The pin16 + /// + Pin16 = 16, + + /// + /// The pin17 + /// + Pin17 = 17, + + /// + /// The pin18 + /// + Pin18 = 18, + + /// + /// The pin19 + /// + Pin19 = 19, + + /// + /// The pin20 + /// + Pin20 = 20, + + /// + /// The pin21 + /// + Pin21 = 21, + + /// + /// The pin22 + /// + Pin22 = 22, + + /// + /// The pin23 + /// + Pin23 = 23, + + /// + /// The pin24 + /// + Pin24 = 24, + + /// + /// The pin25 + /// + Pin25 = 25, + + /// + /// The pin26 + /// + Pin26 = 26, + + /// + /// The pin27 + /// + Pin27 = 27, + + /// + /// The pin28 + /// + Pin28 = 28, + + /// + /// The pin29 + /// + Pin29 = 29, + + /// + /// The pin30 + /// + Pin30 = 30, + + /// + /// The pin31 + /// + Pin31 = 31, + } + + /// + /// Enumerates the different pins on the P1 Header + /// as commonly referenced by Raspberry Pi Documentation. + /// Enumeration values correspond to the physical pin number. + /// + public enum P1 { + /// + /// Header P1, GPIO Pin 02 + /// + Gpio02 = 3, + + /// + /// Header P1, GPIO Pin 03 + /// + Gpio03 = 5, + + /// + /// Header P1, GPIO Pin 04 + /// + Gpio04 = 7, + + /// + /// Header P1, GPIO Pin 17 + /// + Gpio17 = 11, + + /// + /// Header P1, GPIO Pin 27 + /// + Gpio27 = 13, + + /// + /// Header P1, GPIO Pin 22 + /// + Gpio22 = 15, + + /// + /// Header P1, GPIO Pin 10 + /// + Gpio10 = 19, + + /// + /// Header P1, GPIO Pin 09 + /// + Gpio09 = 21, + + /// + /// Header P1, GPIO Pin 11 + /// + Gpio11 = 23, + + /// + /// Header P1, GPIO Pin 05 + /// + Gpio05 = 29, + + /// + /// Header P1, GPIO Pin 06 + /// + Gpio06 = 31, + + /// + /// Header P1, GPIO Pin 13 + /// + Gpio13 = 33, + + /// + /// Header P1, GPIO Pin 19 + /// + Gpio19 = 35, + + /// + /// Header P1, GPIO Pin 26 + /// + Gpio26 = 37, + + /// + /// Header P1, GPIO Pin 14 + /// + Gpio14 = 8, + + /// + /// Header P1, GPIO Pin 15 + /// + Gpio15 = 10, + + /// + /// Header P1, GPIO Pin 18 + /// + Gpio18 = 12, + + /// + /// Header P1, GPIO Pin 23 + /// + Gpio23 = 16, + + /// + /// Header P1, GPIO Pin 24 + /// + Gpio24 = 18, + + /// + /// Header P1, GPIO Pin 25 + /// + Gpio25 = 22, + + /// + /// Header P1, GPIO Pin 08 + /// + Gpio08 = 24, + + /// + /// Header P1, GPIO Pin 07 + /// + Gpio07 = 26, + + /// + /// Header P1, GPIO Pin 12 + /// + Gpio12 = 32, + + /// + /// Header P1, GPIO Pin 16 + /// + Gpio16 = 36, + + /// + /// Header P1, GPIO Pin 20 + /// + Gpio20 = 38, + + /// + /// Header P1, GPIO Pin 21 + /// + Gpio21 = 40 + } + + /// + /// Enumerates the different pins on the P5 Header + /// as commonly referenced by Raspberry Pi documentation. + /// Enumeration values correspond to the physical pin number. + /// + public enum P5 { + /// + /// Header P5, GPIO Pin 28 + /// + Gpio28 = 3, + + /// + /// Header P5, GPIO Pin 29 + /// + Gpio29 = 4, + + /// + /// Header P5, GPIO Pin 30 + /// + Gpio30 = 5, + + /// + /// Header P5, GPIO Pin 31 + /// + Gpio31 = 6 + } + + /// + /// Defines the different pin capabilities + /// + public enum PinCapability { + /// + /// General Purpose capability: Digital and Analog Read/Write + /// + GP, + + /// + /// General Purpose Clock (not PWM) + /// + GPCLK, + + /// + /// i2c data channel + /// + I2CSDA, + + /// + /// i2c clock channel + /// + I2CSCL, + + /// + /// SPI Master Out, Slave In channel + /// + SPIMOSI, + + /// + /// SPI Master In, Slave Out channel + /// + SPIMISO, + + /// + /// SPI Clock channel + /// + SPICLK, + + /// + /// SPI Chip Select Channel + /// + SPICS, + + /// + /// UART Request to Send Channel + /// + UARTRTS, + + /// + /// UART Transmit Channel + /// + UARTTXD, + + /// + /// UART Receive Channel + /// + UARTRXD, + + /// + /// Hardware Pule Width Modulation + /// + PWM + } + + /// + /// Defines the SPI channel numbers + /// + internal enum SpiChannelNumber { + /// + /// The channel 0 + /// + Channel0 = 0, + + /// + /// The channel 1 + /// + Channel1 = 1, + } + + /// + /// Defines GPIO controller initialization modes + /// + internal enum ControllerMode { + /// + /// The not initialized + /// + NotInitialized, + + /// + /// The direct with wiring pi pins + /// + DirectWithWiringPiPins, + + /// + /// The direct with BCM pins + /// + DirectWithBcmPins, + + /// + /// The direct with header pins + /// + DirectWithHeaderPins, + + /// + /// The file stream with hardware pins + /// + FileStreamWithHardwarePins, + } } \ No newline at end of file diff --git a/Unosquare.RaspberryIO/Gpio/GpioController.cs b/Unosquare.RaspberryIO/Gpio/GpioController.cs index 48e6a7e..99f6d09 100644 --- a/Unosquare.RaspberryIO/Gpio/GpioController.cs +++ b/Unosquare.RaspberryIO/Gpio/GpioController.cs @@ -1,594 +1,572 @@ -namespace Unosquare.RaspberryIO.Gpio -{ - using Native; - using Swan; - using Swan.Abstractions; - using System; - using System.Collections; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Linq; - using System.Threading.Tasks; - +using Unosquare.RaspberryIO.Native; +using Unosquare.Swan; +using Unosquare.Swan.Abstractions; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; + +namespace Unosquare.RaspberryIO.Gpio { + /// + /// Represents a singleton of the Raspberry Pi GPIO controller + /// as an IReadOnlyCollection of GpioPins + /// Low level operations are accomplished by using the Wiring Pi library. + /// Use the Instance property to access the singleton's instance + /// + public sealed class GpioController : SingletonBase, IReadOnlyCollection { + #region Private Declarations + + private const String WiringPiCodesEnvironmentVariable = "WIRINGPI_CODES"; + private static readonly Object SyncRoot = new Object(); + private readonly Dictionary _pinsByWiringPiPinNumber = new Dictionary(); + + #endregion + + #region Constructors and Initialization + /// - /// Represents a singleton of the Raspberry Pi GPIO controller - /// as an IReadOnlyCollection of GpioPins - /// Low level operations are accomplished by using the Wiring Pi library. - /// Use the Instance property to access the singleton's instance + /// Prevents a default instance of the class from being created. + /// It in turn initializes the controller and registers the pin -- in that order. /// - public sealed class GpioController : SingletonBase, IReadOnlyCollection - { - #region Private Declarations - - private const string WiringPiCodesEnvironmentVariable = "WIRINGPI_CODES"; - private static readonly object SyncRoot = new object(); - private readonly ReadOnlyCollection _pinCollection; - private readonly ReadOnlyDictionary _headerP1Pins; - private readonly ReadOnlyDictionary _headerP5Pins; - private readonly Dictionary _pinsByWiringPiPinNumber = new Dictionary(); - - #endregion - - #region Constructors and Initialization - - /// - /// Prevents a default instance of the class from being created. - /// It in turn initializes the controller and registers the pin -- in that order. - /// - /// Unable to initialize the GPIO controller. - private GpioController() - { - if (_pinCollection != null) - return; - - if (IsInitialized == false) - { - var initResult = Initialize(ControllerMode.DirectWithWiringPiPins); - if (initResult == false) - throw new Exception("Unable to initialize the GPIO controller."); - } - - #region Pin Registration (32 WiringPi Pins) - - RegisterPin(GpioPin.Pin00.Value); - RegisterPin(GpioPin.Pin01.Value); - RegisterPin(GpioPin.Pin02.Value); - RegisterPin(GpioPin.Pin03.Value); - RegisterPin(GpioPin.Pin04.Value); - RegisterPin(GpioPin.Pin05.Value); - RegisterPin(GpioPin.Pin06.Value); - RegisterPin(GpioPin.Pin07.Value); - RegisterPin(GpioPin.Pin08.Value); - RegisterPin(GpioPin.Pin09.Value); - RegisterPin(GpioPin.Pin10.Value); - RegisterPin(GpioPin.Pin11.Value); - RegisterPin(GpioPin.Pin12.Value); - RegisterPin(GpioPin.Pin13.Value); - RegisterPin(GpioPin.Pin14.Value); - RegisterPin(GpioPin.Pin15.Value); - RegisterPin(GpioPin.Pin16.Value); - RegisterPin(GpioPin.Pin17.Value); - RegisterPin(GpioPin.Pin18.Value); - RegisterPin(GpioPin.Pin19.Value); - RegisterPin(GpioPin.Pin20.Value); - RegisterPin(GpioPin.Pin21.Value); - RegisterPin(GpioPin.Pin22.Value); - RegisterPin(GpioPin.Pin23.Value); - RegisterPin(GpioPin.Pin24.Value); - RegisterPin(GpioPin.Pin25.Value); - RegisterPin(GpioPin.Pin26.Value); - RegisterPin(GpioPin.Pin27.Value); - RegisterPin(GpioPin.Pin28.Value); - RegisterPin(GpioPin.Pin29.Value); - RegisterPin(GpioPin.Pin30.Value); - RegisterPin(GpioPin.Pin31.Value); - - #endregion - - _pinCollection = new ReadOnlyCollection(_pinsByWiringPiPinNumber.Values.ToArray()); - var headerP1 = new Dictionary(_pinCollection.Count); - var headerP5 = new Dictionary(_pinCollection.Count); - foreach (var pin in _pinCollection) - { - var target = pin.Header == GpioHeader.P1 ? headerP1 : headerP5; - target[pin.HeaderPinNumber] = pin; - } - - _headerP1Pins = new ReadOnlyDictionary(headerP1); - _headerP5Pins = new ReadOnlyDictionary(headerP5); - } - - /// - /// Determines if the underlying GPIO controller has been initialized properly. - /// - /// - /// true if the controller is properly initialized; otherwise, false. - /// - public static bool IsInitialized - { - get - { - lock (SyncRoot) - { - return Mode != ControllerMode.NotInitialized; - } - } - } - - /// - /// Gets the number of registered pins in the controller. - /// - public int Count => _pinCollection.Count; - - #endregion - - #region Pin Addressing - - /// - /// Gets the PWM base frequency (in Hz). - /// - public int PwmBaseFrequency => 19200000; - - /// - /// Gets a red-only collection of all registered pins. - /// - public ReadOnlyCollection Pins => _pinCollection; - - /// - /// Provides all the pins on Header P1 of the Pi as a lookup by physical header pin number. - /// This header is the main header and it is the one commonly used. - /// - public ReadOnlyDictionary HeaderP1 => _headerP1Pins; - - /// - /// Provides all the pins on Header P5 of the Pi as a lookup by physical header pin number. - /// This header is the secondary header and it is rarely used. - /// - public ReadOnlyDictionary HeaderP5 => _headerP5Pins; - - #endregion - - #region Individual Pin Properties - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 00. - /// - public GpioPin Pin00 => GpioPin.Pin00.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 01. - /// - public GpioPin Pin01 => GpioPin.Pin01.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 02. - /// - public GpioPin Pin02 => GpioPin.Pin02.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 03. - /// - public GpioPin Pin03 => GpioPin.Pin03.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 04. - /// - public GpioPin Pin04 => GpioPin.Pin04.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 05. - /// - public GpioPin Pin05 => GpioPin.Pin05.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 06. - /// - public GpioPin Pin06 => GpioPin.Pin06.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 07. - /// - public GpioPin Pin07 => GpioPin.Pin07.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 08. - /// - public GpioPin Pin08 => GpioPin.Pin08.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 09. - /// - public GpioPin Pin09 => GpioPin.Pin09.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 10. - /// - public GpioPin Pin10 => GpioPin.Pin10.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 11. - /// - public GpioPin Pin11 => GpioPin.Pin11.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 12. - /// - public GpioPin Pin12 => GpioPin.Pin12.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 13. - /// - public GpioPin Pin13 => GpioPin.Pin13.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 14. - /// - public GpioPin Pin14 => GpioPin.Pin14.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 15. - /// - public GpioPin Pin15 => GpioPin.Pin15.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 16. - /// - public GpioPin Pin16 => GpioPin.Pin16.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 17. - /// - public GpioPin Pin17 => GpioPin.Pin17.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 18. - /// - public GpioPin Pin18 => GpioPin.Pin18.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 19. - /// - public GpioPin Pin19 => GpioPin.Pin19.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 20. - /// - public GpioPin Pin20 => GpioPin.Pin20.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 21. - /// - public GpioPin Pin21 => GpioPin.Pin21.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 22. - /// - public GpioPin Pin22 => GpioPin.Pin22.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 23. - /// - public GpioPin Pin23 => GpioPin.Pin23.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 24. - /// - public GpioPin Pin24 => GpioPin.Pin24.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 25. - /// - public GpioPin Pin25 => GpioPin.Pin25.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 26. - /// - public GpioPin Pin26 => GpioPin.Pin26.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 27. - /// - public GpioPin Pin27 => GpioPin.Pin27.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 28. - /// - public GpioPin Pin28 => GpioPin.Pin28.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 29. - /// - public GpioPin Pin29 => GpioPin.Pin29.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 30. - /// - public GpioPin Pin30 => GpioPin.Pin30.Value; - - /// - /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 31. - /// - public GpioPin Pin31 => GpioPin.Pin31.Value; - - #endregion - - #region Indexers - - /// - /// Gets or sets the initialization mode. - /// - private static ControllerMode Mode { get; set; } = ControllerMode.NotInitialized; - - /// - /// Gets the with the specified Wiring Pi pin number. - /// - /// - /// The . - /// - /// The pin number. - /// A reference to the GPIO pin - public GpioPin this[WiringPiPin pinNumber] => _pinsByWiringPiPinNumber[pinNumber]; - - /// - /// Gets the with the specified pin number. - /// - /// - /// The . - /// - /// The pin number. - /// A reference to the GPIO pin - public GpioPin this[P1 pinNumber] => HeaderP1[(int)pinNumber]; - - /// - /// Gets the with the specified pin number. - /// - /// - /// The . - /// - /// The pin number. - /// A reference to the GPIO pin - public GpioPin this[P5 pinNumber] => HeaderP5[(int)pinNumber]; - - /// - /// Gets the with the specified Wiring Pi pin number. - /// Use the HeaderP1 and HeaderP5 lookups if you would like to retrieve pins by physical pin number. - /// - /// - /// The . - /// - /// The pin number as defined by Wiring Pi. This is not the header pin number as pin number in headers are obvoisly repeating. - /// A reference to the GPIO pin - /// When the pin index is not found - public GpioPin this[int wiringPiPinNumber] - { - get - { - if (Enum.IsDefined(typeof(WiringPiPin), wiringPiPinNumber) == false) - throw new IndexOutOfRangeException($"Pin {wiringPiPinNumber} is not registered in the GPIO controller."); - - return _pinsByWiringPiPinNumber[(WiringPiPin)wiringPiPinNumber]; - } - } - - #endregion - - #region Pin Group Methods (Read, Write, Pad Drive) - - /// - /// This sets the “strength” of the pad drivers for a particular group of pins. - /// There are 3 groups of pins and the drive strength is from 0 to 7. - /// Do not use this unless you know what you are doing. - /// - /// The group. - /// The value. - public void SetPadDrive(int group, int value) - { - lock (SyncRoot) - { - WiringPi.SetPadDrive(group, value); - } - } - - /// - /// This sets the “strength” of the pad drivers for a particular group of pins. - /// There are 3 groups of pins and the drive strength is from 0 to 7. - /// Do not use this unless you know what you are doing. - /// - /// The group. - /// The value. - /// The awaitable task - public Task SetPadDriveAsync(int group, int value) => Task.Run(() => { SetPadDrive(group, value); }); - - /// - /// This writes the 8-bit byte supplied to the first 8 GPIO pins. - /// It’s the fastest way to set all 8 bits at once to a particular value, - /// although it still takes two write operations to the Pi’s GPIO hardware. - /// - /// The value. - /// PinMode - public void WriteByte(byte value) - { - lock (SyncRoot) - { - if (this.Skip(0).Take(8).Any(p => p.PinMode != GpioPinDriveMode.Output)) - { - throw new InvalidOperationException( - $"All firts 8 pins (0 to 7) need their {nameof(GpioPin.PinMode)} to be set to {GpioPinDriveMode.Output}"); - } - - WiringPi.DigitalWriteByte(value); - } - } - - /// - /// This writes the 8-bit byte supplied to the first 8 GPIO pins. - /// It’s the fastest way to set all 8 bits at once to a particular value, - /// although it still takes two write operations to the Pi’s GPIO hardware. - /// - /// The value. - /// The awaitable task - public Task WriteByteAsync(byte value) => Task.Run(() => { WriteByte(value); }); - - /// - /// This reads the 8-bit byte supplied to the first 8 GPIO pins. - /// It’s the fastest way to get all 8 bits at once to a particular value. - /// Please note this function is undocumented and unsopported - /// - /// A byte from the GPIO - /// PinMode - public byte ReadByte() - { - lock (SyncRoot) - { - if (this.Skip(0).Take(8).Any(p => - p.PinMode != GpioPinDriveMode.Input && p.PinMode != GpioPinDriveMode.Output)) - { - throw new InvalidOperationException( - $"All firts 8 pins (0 to 7) need their {nameof(GpioPin.PinMode)} to be set to {GpioPinDriveMode.Input} or {GpioPinDriveMode.Output}"); - } - - return (byte)WiringPi.DigitalReadByte(); - } - } - - /// - /// This reads the 8-bit byte supplied to the first 8 GPIO pins. - /// It’s the fastest way to get all 8 bits at once to a particular value. - /// Please note this function is undocumented and unsopported - /// - /// A byte from the GPIO - public Task ReadByteAsync() => Task.Run(() => ReadByte()); - - #endregion - - #region IReadOnlyCollection Implementation - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// - /// A that can be used to iterate through the collection. - /// - public IEnumerator GetEnumerator() => _pinCollection.GetEnumerator(); - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() => _pinCollection.GetEnumerator(); - - #endregion - - #region Helper and Init Methods - - /// - /// Gets the GPIO pin by BCM pin number. - /// - /// The BCM pin number. - /// The GPIO pin - public GpioPin GetGpioPinByBcmPinNumber(int bcmPinNumber) => this.First(pin => pin.BcmPinNumber == bcmPinNumber); - - /// - /// Converts the Wirings Pi pin number to the BCM pin number. - /// - /// The wiring pi pin number. - /// The converted pin - internal static int WiringPiToBcmPinNumber(int wiringPiPinNumber) - { - lock (SyncRoot) - { - return WiringPi.WpiPinToGpio(wiringPiPinNumber); - } - } - - /// - /// Converts the Physical (Header) pin number to BCM pin number. - /// - /// The header pin number. - /// The converted pin - internal static int HaderToBcmPinNumber(int headerPinNumber) - { - lock (SyncRoot) - { - return WiringPi.PhysPinToGpio(headerPinNumber); - } - } - - /// - /// Short-hand method of registering pins - /// - /// The pin. - private void RegisterPin(GpioPin pin) - { - if (_pinsByWiringPiPinNumber.ContainsKey(pin.WiringPiPinNumber) == false) - _pinsByWiringPiPinNumber[pin.WiringPiPinNumber] = pin; - else - throw new InvalidOperationException($"Pin {pin.WiringPiPinNumber} has been registered"); - } - - /// - /// Initializes the controller given the initialization mode and pin numbering scheme - /// - /// The mode. - /// True when successful. - /// - /// This library does not support the platform - /// - /// Library was already Initialized - /// The init mode is invalid - private bool Initialize(ControllerMode mode) - { - if (Runtime.OS != Swan.OperatingSystem.Unix) - throw new PlatformNotSupportedException("This library does not support the platform"); - - lock (SyncRoot) - { - if (IsInitialized) - throw new InvalidOperationException($"Cannot call {nameof(Initialize)} more than once."); - - Environment.SetEnvironmentVariable(WiringPiCodesEnvironmentVariable, "1", EnvironmentVariableTarget.Process); - int setpuResult; - - switch (mode) - { - case ControllerMode.DirectWithWiringPiPins: - { - setpuResult = WiringPi.WiringPiSetup(); - break; - } - - case ControllerMode.DirectWithBcmPins: - { - setpuResult = WiringPi.WiringPiSetupGpio(); - break; - } - - case ControllerMode.DirectWithHeaderPins: - { - setpuResult = WiringPi.WiringPiSetupPhys(); - break; - } - - case ControllerMode.FileStreamWithHardwarePins: - { - setpuResult = WiringPi.WiringPiSetupSys(); - break; - } - - default: - { - throw new ArgumentException($"'{mode}' is not a valid initialization mode."); - } - } - - Mode = setpuResult == 0 ? mode : ControllerMode.NotInitialized; - return IsInitialized; - } - } - - #endregion - - } + /// Unable to initialize the GPIO controller. + private GpioController() { + if(this.Pins != null) { + return; + } + + if(IsInitialized == false) { + Boolean initResult = this.Initialize(ControllerMode.DirectWithWiringPiPins); + if(initResult == false) { + throw new Exception("Unable to initialize the GPIO controller."); + } + } + + #region Pin Registration (32 WiringPi Pins) + + this.RegisterPin(GpioPin.Pin00.Value); + this.RegisterPin(GpioPin.Pin01.Value); + this.RegisterPin(GpioPin.Pin02.Value); + this.RegisterPin(GpioPin.Pin03.Value); + this.RegisterPin(GpioPin.Pin04.Value); + this.RegisterPin(GpioPin.Pin05.Value); + this.RegisterPin(GpioPin.Pin06.Value); + this.RegisterPin(GpioPin.Pin07.Value); + this.RegisterPin(GpioPin.Pin08.Value); + this.RegisterPin(GpioPin.Pin09.Value); + this.RegisterPin(GpioPin.Pin10.Value); + this.RegisterPin(GpioPin.Pin11.Value); + this.RegisterPin(GpioPin.Pin12.Value); + this.RegisterPin(GpioPin.Pin13.Value); + this.RegisterPin(GpioPin.Pin14.Value); + this.RegisterPin(GpioPin.Pin15.Value); + this.RegisterPin(GpioPin.Pin16.Value); + this.RegisterPin(GpioPin.Pin17.Value); + this.RegisterPin(GpioPin.Pin18.Value); + this.RegisterPin(GpioPin.Pin19.Value); + this.RegisterPin(GpioPin.Pin20.Value); + this.RegisterPin(GpioPin.Pin21.Value); + this.RegisterPin(GpioPin.Pin22.Value); + this.RegisterPin(GpioPin.Pin23.Value); + this.RegisterPin(GpioPin.Pin24.Value); + this.RegisterPin(GpioPin.Pin25.Value); + this.RegisterPin(GpioPin.Pin26.Value); + this.RegisterPin(GpioPin.Pin27.Value); + this.RegisterPin(GpioPin.Pin28.Value); + this.RegisterPin(GpioPin.Pin29.Value); + this.RegisterPin(GpioPin.Pin30.Value); + this.RegisterPin(GpioPin.Pin31.Value); + + #endregion + + this.Pins = new ReadOnlyCollection(this._pinsByWiringPiPinNumber.Values.ToArray()); + Dictionary headerP1 = new Dictionary(this.Pins.Count); + Dictionary headerP5 = new Dictionary(this.Pins.Count); + foreach(GpioPin pin in this.Pins) { + Dictionary target = pin.Header == GpioHeader.P1 ? headerP1 : headerP5; + target[pin.HeaderPinNumber] = pin; + } + + this.HeaderP1 = new ReadOnlyDictionary(headerP1); + this.HeaderP5 = new ReadOnlyDictionary(headerP5); + } + + /// + /// Determines if the underlying GPIO controller has been initialized properly. + /// + /// + /// true if the controller is properly initialized; otherwise, false. + /// + public static Boolean IsInitialized { + get { + lock(SyncRoot) { + return Mode != ControllerMode.NotInitialized; + } + } + } + + /// + /// Gets the number of registered pins in the controller. + /// + public Int32 Count => this.Pins.Count; + + #endregion + + #region Pin Addressing + + /// + /// Gets the PWM base frequency (in Hz). + /// + public Int32 PwmBaseFrequency => 19200000; + + /// + /// Gets a red-only collection of all registered pins. + /// + public ReadOnlyCollection Pins { + get; + } + + /// + /// Provides all the pins on Header P1 of the Pi as a lookup by physical header pin number. + /// This header is the main header and it is the one commonly used. + /// + public ReadOnlyDictionary HeaderP1 { + get; + } + + /// + /// Provides all the pins on Header P5 of the Pi as a lookup by physical header pin number. + /// This header is the secondary header and it is rarely used. + /// + public ReadOnlyDictionary HeaderP5 { + get; + } + + #endregion + + #region Individual Pin Properties + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 00. + /// + public GpioPin Pin00 => GpioPin.Pin00.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 01. + /// + public GpioPin Pin01 => GpioPin.Pin01.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 02. + /// + public GpioPin Pin02 => GpioPin.Pin02.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 03. + /// + public GpioPin Pin03 => GpioPin.Pin03.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 04. + /// + public GpioPin Pin04 => GpioPin.Pin04.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 05. + /// + public GpioPin Pin05 => GpioPin.Pin05.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 06. + /// + public GpioPin Pin06 => GpioPin.Pin06.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 07. + /// + public GpioPin Pin07 => GpioPin.Pin07.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 08. + /// + public GpioPin Pin08 => GpioPin.Pin08.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 09. + /// + public GpioPin Pin09 => GpioPin.Pin09.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 10. + /// + public GpioPin Pin10 => GpioPin.Pin10.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 11. + /// + public GpioPin Pin11 => GpioPin.Pin11.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 12. + /// + public GpioPin Pin12 => GpioPin.Pin12.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 13. + /// + public GpioPin Pin13 => GpioPin.Pin13.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 14. + /// + public GpioPin Pin14 => GpioPin.Pin14.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 15. + /// + public GpioPin Pin15 => GpioPin.Pin15.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 16. + /// + public GpioPin Pin16 => GpioPin.Pin16.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 17. + /// + public GpioPin Pin17 => GpioPin.Pin17.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 18. + /// + public GpioPin Pin18 => GpioPin.Pin18.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 19. + /// + public GpioPin Pin19 => GpioPin.Pin19.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 20. + /// + public GpioPin Pin20 => GpioPin.Pin20.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 21. + /// + public GpioPin Pin21 => GpioPin.Pin21.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 22. + /// + public GpioPin Pin22 => GpioPin.Pin22.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 23. + /// + public GpioPin Pin23 => GpioPin.Pin23.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 24. + /// + public GpioPin Pin24 => GpioPin.Pin24.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 25. + /// + public GpioPin Pin25 => GpioPin.Pin25.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 26. + /// + public GpioPin Pin26 => GpioPin.Pin26.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 27. + /// + public GpioPin Pin27 => GpioPin.Pin27.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 28. + /// + public GpioPin Pin28 => GpioPin.Pin28.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 29. + /// + public GpioPin Pin29 => GpioPin.Pin29.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 30. + /// + public GpioPin Pin30 => GpioPin.Pin30.Value; + + /// + /// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 31. + /// + public GpioPin Pin31 => GpioPin.Pin31.Value; + + #endregion + + #region Indexers + + /// + /// Gets or sets the initialization mode. + /// + private static ControllerMode Mode { get; set; } = ControllerMode.NotInitialized; + + /// + /// Gets the with the specified Wiring Pi pin number. + /// + /// + /// The . + /// + /// The pin number. + /// A reference to the GPIO pin + public GpioPin this[WiringPiPin pinNumber] => this._pinsByWiringPiPinNumber[pinNumber]; + + /// + /// Gets the with the specified pin number. + /// + /// + /// The . + /// + /// The pin number. + /// A reference to the GPIO pin + public GpioPin this[P1 pinNumber] => this.HeaderP1[(Int32)pinNumber]; + + /// + /// Gets the with the specified pin number. + /// + /// + /// The . + /// + /// The pin number. + /// A reference to the GPIO pin + public GpioPin this[P5 pinNumber] => this.HeaderP5[(Int32)pinNumber]; + + /// + /// Gets the with the specified Wiring Pi pin number. + /// Use the HeaderP1 and HeaderP5 lookups if you would like to retrieve pins by physical pin number. + /// + /// + /// The . + /// + /// The pin number as defined by Wiring Pi. This is not the header pin number as pin number in headers are obvoisly repeating. + /// A reference to the GPIO pin + /// When the pin index is not found + public GpioPin this[Int32 wiringPiPinNumber] { + get { + if(Enum.IsDefined(typeof(WiringPiPin), wiringPiPinNumber) == false) { + throw new IndexOutOfRangeException($"Pin {wiringPiPinNumber} is not registered in the GPIO controller."); + } + + return this._pinsByWiringPiPinNumber[(WiringPiPin)wiringPiPinNumber]; + } + } + + #endregion + + #region Pin Group Methods (Read, Write, Pad Drive) + + /// + /// This sets the “strength” of the pad drivers for a particular group of pins. + /// There are 3 groups of pins and the drive strength is from 0 to 7. + /// Do not use this unless you know what you are doing. + /// + /// The group. + /// The value. + public void SetPadDrive(Int32 group, Int32 value) { + lock(SyncRoot) { + _ = WiringPi.SetPadDrive(group, value); + } + } + + /// + /// This sets the “strength” of the pad drivers for a particular group of pins. + /// There are 3 groups of pins and the drive strength is from 0 to 7. + /// Do not use this unless you know what you are doing. + /// + /// The group. + /// The value. + /// The awaitable task + public Task SetPadDriveAsync(Int32 group, Int32 value) => Task.Run(() => this.SetPadDrive(group, value)); + + /// + /// This writes the 8-bit byte supplied to the first 8 GPIO pins. + /// It’s the fastest way to set all 8 bits at once to a particular value, + /// although it still takes two write operations to the Pi’s GPIO hardware. + /// + /// The value. + /// PinMode + public void WriteByte(Byte value) { + lock(SyncRoot) { + if(this.Skip(0).Take(8).Any(p => p.PinMode != GpioPinDriveMode.Output)) { + throw new InvalidOperationException( + $"All firts 8 pins (0 to 7) need their {nameof(GpioPin.PinMode)} to be set to {GpioPinDriveMode.Output}"); + } + + WiringPi.DigitalWriteByte(value); + } + } + + /// + /// This writes the 8-bit byte supplied to the first 8 GPIO pins. + /// It’s the fastest way to set all 8 bits at once to a particular value, + /// although it still takes two write operations to the Pi’s GPIO hardware. + /// + /// The value. + /// The awaitable task + public Task WriteByteAsync(Byte value) => Task.Run(() => this.WriteByte(value)); + + /// + /// This reads the 8-bit byte supplied to the first 8 GPIO pins. + /// It’s the fastest way to get all 8 bits at once to a particular value. + /// Please note this function is undocumented and unsopported + /// + /// A byte from the GPIO + /// PinMode + public Byte ReadByte() { + lock(SyncRoot) { + if(this.Skip(0).Take(8).Any(p => + p.PinMode != GpioPinDriveMode.Input && p.PinMode != GpioPinDriveMode.Output)) { + throw new InvalidOperationException( + $"All firts 8 pins (0 to 7) need their {nameof(GpioPin.PinMode)} to be set to {GpioPinDriveMode.Input} or {GpioPinDriveMode.Output}"); + } + + return (Byte)WiringPi.DigitalReadByte(); + } + } + + /// + /// This reads the 8-bit byte supplied to the first 8 GPIO pins. + /// It’s the fastest way to get all 8 bits at once to a particular value. + /// Please note this function is undocumented and unsopported + /// + /// A byte from the GPIO + public Task ReadByteAsync() => Task.Run(() => this.ReadByte()); + + #endregion + + #region IReadOnlyCollection Implementation + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public IEnumerator GetEnumerator() => this.Pins.GetEnumerator(); + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() => this.Pins.GetEnumerator(); + + #endregion + + #region Helper and Init Methods + + /// + /// Gets the GPIO pin by BCM pin number. + /// + /// The BCM pin number. + /// The GPIO pin + public GpioPin GetGpioPinByBcmPinNumber(Int32 bcmPinNumber) => this.First(pin => pin.BcmPinNumber == bcmPinNumber); + + /// + /// Converts the Wirings Pi pin number to the BCM pin number. + /// + /// The wiring pi pin number. + /// The converted pin + internal static Int32 WiringPiToBcmPinNumber(Int32 wiringPiPinNumber) { + lock(SyncRoot) { + return WiringPi.WpiPinToGpio(wiringPiPinNumber); + } + } + + /// + /// Converts the Physical (Header) pin number to BCM pin number. + /// + /// The header pin number. + /// The converted pin + internal static Int32 HaderToBcmPinNumber(Int32 headerPinNumber) { + lock(SyncRoot) { + return WiringPi.PhysPinToGpio(headerPinNumber); + } + } + + /// + /// Short-hand method of registering pins + /// + /// The pin. + private void RegisterPin(GpioPin pin) { + if(this._pinsByWiringPiPinNumber.ContainsKey(pin.WiringPiPinNumber) == false) { + this._pinsByWiringPiPinNumber[pin.WiringPiPinNumber] = pin; + } else { + throw new InvalidOperationException($"Pin {pin.WiringPiPinNumber} has been registered"); + } + } + + /// + /// Initializes the controller given the initialization mode and pin numbering scheme + /// + /// The mode. + /// True when successful. + /// + /// This library does not support the platform + /// + /// Library was already Initialized + /// The init mode is invalid + private Boolean Initialize(ControllerMode mode) { + if(Runtime.OS != Swan.OperatingSystem.Unix) { + throw new PlatformNotSupportedException("This library does not support the platform"); + } + + lock(SyncRoot) { + if(IsInitialized) { + throw new InvalidOperationException($"Cannot call {nameof(Initialize)} more than once."); + } + + Environment.SetEnvironmentVariable(WiringPiCodesEnvironmentVariable, "1", EnvironmentVariableTarget.Process); + Int32 setpuResult; + + switch(mode) { + case ControllerMode.DirectWithWiringPiPins: { + setpuResult = WiringPi.WiringPiSetup(); + break; + } + + case ControllerMode.DirectWithBcmPins: { + setpuResult = WiringPi.WiringPiSetupGpio(); + break; + } + + case ControllerMode.DirectWithHeaderPins: { + setpuResult = WiringPi.WiringPiSetupPhys(); + break; + } + + case ControllerMode.FileStreamWithHardwarePins: { + setpuResult = WiringPi.WiringPiSetupSys(); + break; + } + + default: { + throw new ArgumentException($"'{mode}' is not a valid initialization mode."); + } + } + + Mode = setpuResult == 0 ? mode : ControllerMode.NotInitialized; + return IsInitialized; + } + } + + #endregion + + } } \ No newline at end of file diff --git a/Unosquare.RaspberryIO/Gpio/GpioPin.Factory.cs b/Unosquare.RaspberryIO/Gpio/GpioPin.Factory.cs index f4a9d6a..f865253 100644 --- a/Unosquare.RaspberryIO/Gpio/GpioPin.Factory.cs +++ b/Unosquare.RaspberryIO/Gpio/GpioPin.Factory.cs @@ -1,201 +1,167 @@ -namespace Unosquare.RaspberryIO.Gpio -{ - using System; - - public partial class GpioPin - { - #region Static Pin Definitions - - internal static readonly Lazy Pin08 = new Lazy(() => new GpioPin(WiringPiPin.Pin08, 3) - { - Capabilities = new[] { PinCapability.GP, PinCapability.I2CSDA }, - Name = "BCM 2 (SDA)" - }); - - internal static readonly Lazy Pin09 = new Lazy(() => new GpioPin(WiringPiPin.Pin09, 5) - { - Capabilities = new[] { PinCapability.GP, PinCapability.I2CSCL }, - Name = "BCM 3 (SCL)" - }); - - internal static readonly Lazy Pin07 = new Lazy(() => new GpioPin(WiringPiPin.Pin07, 7) - { - Capabilities = new[] { PinCapability.GP, PinCapability.GPCLK }, - Name = "BCM 4 (GPCLK0)" - }); - - internal static readonly Lazy Pin00 = new Lazy(() => new GpioPin(WiringPiPin.Pin00, 11) - { - Capabilities = new[] { PinCapability.GP, PinCapability.UARTRTS }, - Name = "BCM 17" - }); - - internal static readonly Lazy Pin02 = new Lazy(() => new GpioPin(WiringPiPin.Pin02, 13) - { - Capabilities = new[] { PinCapability.GP }, - Name = "BCM 27" - }); - - internal static readonly Lazy Pin03 = new Lazy(() => new GpioPin(WiringPiPin.Pin03, 15) - { - Capabilities = new[] { PinCapability.GP }, - Name = "BCM 22" - }); - - internal static readonly Lazy Pin12 = new Lazy(() => new GpioPin(WiringPiPin.Pin12, 19) - { - Capabilities = new[] { PinCapability.GP, PinCapability.SPIMOSI }, - Name = "BCM 10 (MOSI)" - }); - - internal static readonly Lazy Pin13 = new Lazy(() => new GpioPin(WiringPiPin.Pin13, 21) - { - Capabilities = new[] { PinCapability.GP, PinCapability.SPIMISO }, - Name = "BCM 9 (MISO)" - }); - - internal static readonly Lazy Pin14 = new Lazy(() => new GpioPin(WiringPiPin.Pin14, 23) - { - Capabilities = new[] { PinCapability.GP, PinCapability.SPICLK }, - Name = "BCM 11 (SCLCK)" - }); - - internal static readonly Lazy Pin30 = new Lazy(() => new GpioPin(WiringPiPin.Pin30, 27) - { - Capabilities = new[] { PinCapability.I2CSDA }, - Name = "BCM 0 (ID_SD)" - }); - - internal static readonly Lazy Pin31 = new Lazy(() => new GpioPin(WiringPiPin.Pin31, 28) - { - Capabilities = new[] { PinCapability.I2CSCL }, - Name = "BCM 1 (ID_SC)" - }); - - internal static readonly Lazy Pin11 = new Lazy(() => new GpioPin(WiringPiPin.Pin11, 26) - { - Capabilities = new[] { PinCapability.GP, PinCapability.SPICS }, - Name = "BCM 7 (CE1)" - }); - - internal static readonly Lazy Pin10 = new Lazy(() => new GpioPin(WiringPiPin.Pin10, 24) - { - Capabilities = new[] { PinCapability.GP, PinCapability.SPICS }, - Name = "BCM 8 (CE0)" - }); - - internal static readonly Lazy Pin06 = new Lazy(() => new GpioPin(WiringPiPin.Pin06, 22) - { - Capabilities = new[] { PinCapability.GP }, - Name = "BCM 25" - }); - internal static readonly Lazy Pin05 = new Lazy(() => new GpioPin(WiringPiPin.Pin05, 18) - { - Capabilities = new[] { PinCapability.GP }, - Name = "BCM 24" - }); - - internal static readonly Lazy Pin04 = new Lazy(() => new GpioPin(WiringPiPin.Pin04, 16) - { - Capabilities = new[] { PinCapability.GP }, - Name = "BCM 23" - }); - - internal static readonly Lazy Pin01 = new Lazy(() => new GpioPin(WiringPiPin.Pin01, 12) - { - Capabilities = new[] { PinCapability.GP, PinCapability.PWM }, - Name = "BCM 18 (PWM0)" - }); - - internal static readonly Lazy Pin16 = new Lazy(() => new GpioPin(WiringPiPin.Pin16, 10) - { - Capabilities = new[] { PinCapability.UARTRXD }, - Name = "BCM 15 (RXD)" - }); - - internal static readonly Lazy Pin15 = new Lazy(() => new GpioPin(WiringPiPin.Pin15, 8) - { - Capabilities = new[] { PinCapability.UARTTXD }, - Name = "BCM 14 (TXD)" - }); - - internal static readonly Lazy Pin21 = new Lazy(() => new GpioPin(WiringPiPin.Pin21, 29) - { - Capabilities = new[] { PinCapability.GP }, - Name = "BCM 5" - }); - - internal static readonly Lazy Pin22 = new Lazy(() => new GpioPin(WiringPiPin.Pin22, 31) - { - Capabilities = new[] { PinCapability.GP }, - Name = "BCM 6" - }); - - internal static readonly Lazy Pin23 = new Lazy(() => new GpioPin(WiringPiPin.Pin23, 33) - { - Capabilities = new[] { PinCapability.GP, PinCapability.PWM }, - Name = "BCM 13 (PWM1)" - }); - - internal static readonly Lazy Pin24 = new Lazy(() => new GpioPin(WiringPiPin.Pin24, 35) - { - Capabilities = new[] { PinCapability.GP, PinCapability.SPIMISO }, - Name = "BCM 19 (MISO)" - }); - - internal static readonly Lazy Pin25 = new Lazy(() => new GpioPin(WiringPiPin.Pin25, 37) - { - Capabilities = new[] { PinCapability.GP }, - Name = "BCM 26" - }); - - internal static readonly Lazy Pin29 = new Lazy(() => new GpioPin(WiringPiPin.Pin29, 40) - { - Capabilities = new[] { PinCapability.GP, PinCapability.SPICLK }, - Name = "BCM 21 (SCLK)" - }); - - internal static readonly Lazy Pin28 = new Lazy(() => new GpioPin(WiringPiPin.Pin28, 38) - { - Capabilities = new[] { PinCapability.GP, PinCapability.SPIMOSI }, - Name = "BCM 20 (MOSI)" - }); - - internal static readonly Lazy Pin27 = new Lazy(() => new GpioPin(WiringPiPin.Pin27, 36) - { - Capabilities = new[] { PinCapability.GP }, - Name = "BCM 16" - }); - internal static readonly Lazy Pin26 = new Lazy(() => new GpioPin(WiringPiPin.Pin26, 32) - { - Capabilities = new[] { PinCapability.GP }, - Name = "BCM 12 (PWM0)" - }); - - internal static readonly Lazy Pin17 = new Lazy(() => new GpioPin(WiringPiPin.Pin17, 3) - { - Capabilities = new[] { PinCapability.GP, PinCapability.I2CSDA }, - Name = "BCM 28 (SDA)" - }); - - internal static readonly Lazy Pin18 = new Lazy(() => new GpioPin(WiringPiPin.Pin18, 4) - { - Capabilities = new[] { PinCapability.GP, PinCapability.I2CSCL }, - Name = "BCM 29 (SCL)" - }); - - internal static readonly Lazy Pin19 = new Lazy(() => new GpioPin(WiringPiPin.Pin19, 5) - { - Capabilities = new[] { PinCapability.GP }, - Name = "BCM 30" - }); - - internal static readonly Lazy Pin20 = new Lazy(() => new GpioPin(WiringPiPin.Pin20, 6) - { - Capabilities = new[] { PinCapability.GP }, - Name = "BCM 31" - }); - - #endregion - } +using System; + +namespace Unosquare.RaspberryIO.Gpio { + public partial class GpioPin { + #region Static Pin Definitions + + internal static readonly Lazy Pin08 = new Lazy(() => new GpioPin(WiringPiPin.Pin08, 3) { + Capabilities = new[] { PinCapability.GP, PinCapability.I2CSDA }, + Name = "BCM 2 (SDA)" + }); + + internal static readonly Lazy Pin09 = new Lazy(() => new GpioPin(WiringPiPin.Pin09, 5) { + Capabilities = new[] { PinCapability.GP, PinCapability.I2CSCL }, + Name = "BCM 3 (SCL)" + }); + + internal static readonly Lazy Pin07 = new Lazy(() => new GpioPin(WiringPiPin.Pin07, 7) { + Capabilities = new[] { PinCapability.GP, PinCapability.GPCLK }, + Name = "BCM 4 (GPCLK0)" + }); + + internal static readonly Lazy Pin00 = new Lazy(() => new GpioPin(WiringPiPin.Pin00, 11) { + Capabilities = new[] { PinCapability.GP, PinCapability.UARTRTS }, + Name = "BCM 17" + }); + + internal static readonly Lazy Pin02 = new Lazy(() => new GpioPin(WiringPiPin.Pin02, 13) { + Capabilities = new[] { PinCapability.GP }, + Name = "BCM 27" + }); + + internal static readonly Lazy Pin03 = new Lazy(() => new GpioPin(WiringPiPin.Pin03, 15) { + Capabilities = new[] { PinCapability.GP }, + Name = "BCM 22" + }); + + internal static readonly Lazy Pin12 = new Lazy(() => new GpioPin(WiringPiPin.Pin12, 19) { + Capabilities = new[] { PinCapability.GP, PinCapability.SPIMOSI }, + Name = "BCM 10 (MOSI)" + }); + + internal static readonly Lazy Pin13 = new Lazy(() => new GpioPin(WiringPiPin.Pin13, 21) { + Capabilities = new[] { PinCapability.GP, PinCapability.SPIMISO }, + Name = "BCM 9 (MISO)" + }); + + internal static readonly Lazy Pin14 = new Lazy(() => new GpioPin(WiringPiPin.Pin14, 23) { + Capabilities = new[] { PinCapability.GP, PinCapability.SPICLK }, + Name = "BCM 11 (SCLCK)" + }); + + internal static readonly Lazy Pin30 = new Lazy(() => new GpioPin(WiringPiPin.Pin30, 27) { + Capabilities = new[] { PinCapability.I2CSDA }, + Name = "BCM 0 (ID_SD)" + }); + + internal static readonly Lazy Pin31 = new Lazy(() => new GpioPin(WiringPiPin.Pin31, 28) { + Capabilities = new[] { PinCapability.I2CSCL }, + Name = "BCM 1 (ID_SC)" + }); + + internal static readonly Lazy Pin11 = new Lazy(() => new GpioPin(WiringPiPin.Pin11, 26) { + Capabilities = new[] { PinCapability.GP, PinCapability.SPICS }, + Name = "BCM 7 (CE1)" + }); + + internal static readonly Lazy Pin10 = new Lazy(() => new GpioPin(WiringPiPin.Pin10, 24) { + Capabilities = new[] { PinCapability.GP, PinCapability.SPICS }, + Name = "BCM 8 (CE0)" + }); + + internal static readonly Lazy Pin06 = new Lazy(() => new GpioPin(WiringPiPin.Pin06, 22) { + Capabilities = new[] { PinCapability.GP }, + Name = "BCM 25" + }); + internal static readonly Lazy Pin05 = new Lazy(() => new GpioPin(WiringPiPin.Pin05, 18) { + Capabilities = new[] { PinCapability.GP }, + Name = "BCM 24" + }); + + internal static readonly Lazy Pin04 = new Lazy(() => new GpioPin(WiringPiPin.Pin04, 16) { + Capabilities = new[] { PinCapability.GP }, + Name = "BCM 23" + }); + + internal static readonly Lazy Pin01 = new Lazy(() => new GpioPin(WiringPiPin.Pin01, 12) { + Capabilities = new[] { PinCapability.GP, PinCapability.PWM }, + Name = "BCM 18 (PWM0)" + }); + + internal static readonly Lazy Pin16 = new Lazy(() => new GpioPin(WiringPiPin.Pin16, 10) { + Capabilities = new[] { PinCapability.UARTRXD }, + Name = "BCM 15 (RXD)" + }); + + internal static readonly Lazy Pin15 = new Lazy(() => new GpioPin(WiringPiPin.Pin15, 8) { + Capabilities = new[] { PinCapability.UARTTXD }, + Name = "BCM 14 (TXD)" + }); + + internal static readonly Lazy Pin21 = new Lazy(() => new GpioPin(WiringPiPin.Pin21, 29) { + Capabilities = new[] { PinCapability.GP }, + Name = "BCM 5" + }); + + internal static readonly Lazy Pin22 = new Lazy(() => new GpioPin(WiringPiPin.Pin22, 31) { + Capabilities = new[] { PinCapability.GP }, + Name = "BCM 6" + }); + + internal static readonly Lazy Pin23 = new Lazy(() => new GpioPin(WiringPiPin.Pin23, 33) { + Capabilities = new[] { PinCapability.GP, PinCapability.PWM }, + Name = "BCM 13 (PWM1)" + }); + + internal static readonly Lazy Pin24 = new Lazy(() => new GpioPin(WiringPiPin.Pin24, 35) { + Capabilities = new[] { PinCapability.GP, PinCapability.SPIMISO }, + Name = "BCM 19 (MISO)" + }); + + internal static readonly Lazy Pin25 = new Lazy(() => new GpioPin(WiringPiPin.Pin25, 37) { + Capabilities = new[] { PinCapability.GP }, + Name = "BCM 26" + }); + + internal static readonly Lazy Pin29 = new Lazy(() => new GpioPin(WiringPiPin.Pin29, 40) { + Capabilities = new[] { PinCapability.GP, PinCapability.SPICLK }, + Name = "BCM 21 (SCLK)" + }); + + internal static readonly Lazy Pin28 = new Lazy(() => new GpioPin(WiringPiPin.Pin28, 38) { + Capabilities = new[] { PinCapability.GP, PinCapability.SPIMOSI }, + Name = "BCM 20 (MOSI)" + }); + + internal static readonly Lazy Pin27 = new Lazy(() => new GpioPin(WiringPiPin.Pin27, 36) { + Capabilities = new[] { PinCapability.GP }, + Name = "BCM 16" + }); + internal static readonly Lazy Pin26 = new Lazy(() => new GpioPin(WiringPiPin.Pin26, 32) { + Capabilities = new[] { PinCapability.GP }, + Name = "BCM 12 (PWM0)" + }); + + internal static readonly Lazy Pin17 = new Lazy(() => new GpioPin(WiringPiPin.Pin17, 3) { + Capabilities = new[] { PinCapability.GP, PinCapability.I2CSDA }, + Name = "BCM 28 (SDA)" + }); + + internal static readonly Lazy Pin18 = new Lazy(() => new GpioPin(WiringPiPin.Pin18, 4) { + Capabilities = new[] { PinCapability.GP, PinCapability.I2CSCL }, + Name = "BCM 29 (SCL)" + }); + + internal static readonly Lazy Pin19 = new Lazy(() => new GpioPin(WiringPiPin.Pin19, 5) { + Capabilities = new[] { PinCapability.GP }, + Name = "BCM 30" + }); + + internal static readonly Lazy Pin20 = new Lazy(() => new GpioPin(WiringPiPin.Pin20, 6) { + Capabilities = new[] { PinCapability.GP }, + Name = "BCM 31" + }); + + #endregion + } } diff --git a/Unosquare.RaspberryIO/Gpio/GpioPin.cs b/Unosquare.RaspberryIO/Gpio/GpioPin.cs index ec4f6db..951dc79 100644 --- a/Unosquare.RaspberryIO/Gpio/GpioPin.cs +++ b/Unosquare.RaspberryIO/Gpio/GpioPin.cs @@ -1,648 +1,605 @@ -namespace Unosquare.RaspberryIO.Gpio -{ - using Native; - using Swan; - using System; - using System.Linq; - using System.Threading.Tasks; - +using Unosquare.RaspberryIO.Native; +using Unosquare.Swan; +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace Unosquare.RaspberryIO.Gpio { + /// + /// Represents a GPIO Pin, its location and its capabilities. + /// Full pin reference available here: + /// http://pinout.xyz/pinout/pin31_gpio6 and http://wiringpi.com/pins/ + /// + public sealed partial class GpioPin { + #region Property Backing + + private readonly Object _syncLock = new Object(); + private GpioPinDriveMode m_PinMode; + private GpioPinResistorPullMode m_ResistorPullMode; + private Int32 m_PwmRegister; + private PwmMode m_PwmMode = PwmMode.Balanced; + private UInt32 m_PwmRange = 1024; + private Int32 m_PwmClockDivisor = 1; + private Int32 m_SoftPwmValue = -1; + private Int32 m_SoftToneFrequency = -1; + + #endregion + + #region Constructor + /// - /// Represents a GPIO Pin, its location and its capabilities. - /// Full pin reference available here: - /// http://pinout.xyz/pinout/pin31_gpio6 and http://wiringpi.com/pins/ + /// Initializes a new instance of the class. /// - public sealed partial class GpioPin - { - #region Property Backing - - private readonly object _syncLock = new object(); - private GpioPinDriveMode m_PinMode; - private GpioPinResistorPullMode m_ResistorPullMode; - private int m_PwmRegister; - private PwmMode m_PwmMode = PwmMode.Balanced; - private uint m_PwmRange = 1024; - private int m_PwmClockDivisor = 1; - private int m_SoftPwmValue = -1; - private int m_SoftToneFrequency = -1; - - #endregion - - #region Constructor - - /// - /// Initializes a new instance of the class. - /// - /// The wiring pi pin number. - /// The header pin number. - private GpioPin(WiringPiPin wiringPiPinNumber, int headerPinNumber) - { - PinNumber = (int)wiringPiPinNumber; - WiringPiPinNumber = wiringPiPinNumber; - BcmPinNumber = GpioController.WiringPiToBcmPinNumber((int)wiringPiPinNumber); - HeaderPinNumber = headerPinNumber; - Header = (PinNumber >= 17 && PinNumber <= 20) ? GpioHeader.P5 : GpioHeader.P1; - } - - #endregion - - #region Pin Properties - - /// - /// Gets or sets the Wiring Pi pin number as an integer. - /// - public int PinNumber { get; } - - /// - /// Gets the WiringPi Pin number - /// - public WiringPiPin WiringPiPinNumber { get; } - - /// - /// Gets the BCM chip (hardware) pin number. - /// - public int BcmPinNumber { get; } - - /// - /// Gets or the physical header (physical board) pin number. - /// - public int HeaderPinNumber { get; } - - /// - /// Gets the pin's header (physical board) location. - /// - public GpioHeader Header { get; } - - /// - /// Gets the friendly name of the pin. - /// - public string Name { get; private set; } - - /// - /// Gets the hardware mode capabilities of this pin. - /// - public PinCapability[] Capabilities { get; private set; } - - #endregion - - #region Hardware-Specific Properties - - /// - /// Gets or sets the pin operating mode. - /// - /// - /// The pin mode. - /// - /// Thrown when a pin does not support the given operation mode. - public GpioPinDriveMode PinMode - { - get => m_PinMode; - - set - { - lock (_syncLock) - { - var mode = value; - if ((mode == GpioPinDriveMode.GpioClock && Capabilities.Contains(PinCapability.GPCLK) == false) || - (mode == GpioPinDriveMode.PwmOutput && Capabilities.Contains(PinCapability.PWM) == false) || - (mode == GpioPinDriveMode.Input && Capabilities.Contains(PinCapability.GP) == false) || - (mode == GpioPinDriveMode.Output && Capabilities.Contains(PinCapability.GP) == false)) - { - throw new NotSupportedException( - $"Pin {WiringPiPinNumber} '{Name}' does not support mode '{mode}'. Pin capabilities are limited to: {string.Join(", ", Capabilities)}"); - } - - WiringPi.PinMode(PinNumber, (int)mode); - m_PinMode = mode; - } - } - } - - /// - /// Gets the interrupt callback. Returns null if no interrupt - /// has been registered. - /// - public InterruptServiceRoutineCallback InterruptCallback { get; private set; } - - /// - /// Gets the interrupt edge detection mode. - /// - public EdgeDetection InterruptEdgeDetection { get; private set; } = EdgeDetection.ExternalSetup; - - #endregion - - #region Hardware PWM Members - - /// - /// This sets or gets the pull-up or pull-down resistor mode on the pin, which should be set as an input. - /// Unlike the Arduino, the BCM2835 has both pull-up an down internal resistors. - /// The parameter pud should be; PUD_OFF, (no pull up/down), PUD_DOWN (pull to ground) or PUD_UP (pull to 3.3v) - /// The internal pull up/down resistors have a value of approximately 50KΩ on the Raspberry Pi. - /// - public GpioPinResistorPullMode InputPullMode - { - get => PinMode == GpioPinDriveMode.Input ? m_ResistorPullMode : GpioPinResistorPullMode.Off; - - set - { - lock (_syncLock) - { - if (PinMode != GpioPinDriveMode.Input) - { - m_ResistorPullMode = GpioPinResistorPullMode.Off; - throw new InvalidOperationException( - $"Unable to set the {nameof(InputPullMode)} for pin {PinNumber} because operating mode is {PinMode}." - + $" Setting the {nameof(InputPullMode)} is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Input}"); - } - - WiringPi.PullUpDnControl(PinNumber, (int)value); - m_ResistorPullMode = value; - } - } - } - - /// - /// Gets or sets the PWM register. Values should be between 0 and 1024 - /// - /// - /// The PWM register. - /// - public int PwmRegister - { - get => m_PwmRegister; - - set - { - lock (_syncLock) - { - if (PinMode != GpioPinDriveMode.PwmOutput) - { - m_PwmRegister = 0; - - throw new InvalidOperationException( - $"Unable to write PWM register for pin {PinNumber} because operating mode is {PinMode}." - + $" Writing the PWM register is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.PwmOutput}"); - } - - var val = value.Clamp(0, 1024); - - WiringPi.PwmWrite(PinNumber, val); - m_PwmRegister = val; - } - } - } - - /// - /// The PWM generator can run in 2 modes – “balanced” and “mark:space”. The mark:space mode is traditional, - /// however the default mode in the Pi is “balanced”. - /// - /// - /// The PWM mode. - /// - /// When pin mode is not set a Pwn output - public PwmMode PwmMode - { - get => PinMode == GpioPinDriveMode.PwmOutput ? m_PwmMode : PwmMode.Balanced; - - set - { - lock (_syncLock) - { - if (PinMode != GpioPinDriveMode.PwmOutput) - { - m_PwmMode = PwmMode.Balanced; - - throw new InvalidOperationException( - $"Unable to set PWM mode for pin {PinNumber} because operating mode is {PinMode}." - + $" Setting the PWM mode is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.PwmOutput}"); - } - - WiringPi.PwmSetMode((int)value); - m_PwmMode = value; - } - } - } - - /// - /// This sets the range register in the PWM generator. The default is 1024. - /// - /// - /// The PWM range. - /// - /// When pin mode is not set to PWM output - public uint PwmRange - { - get => PinMode == GpioPinDriveMode.PwmOutput ? m_PwmRange : 0; - - set - { - lock (_syncLock) - { - if (PinMode != GpioPinDriveMode.PwmOutput) - { - m_PwmRange = 1024; - - throw new InvalidOperationException( - $"Unable to set PWM range for pin {PinNumber} because operating mode is {PinMode}." - + $" Setting the PWM range is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.PwmOutput}"); - } - - WiringPi.PwmSetRange(value); - m_PwmRange = value; - } - } - } - - /// - /// Gets or sets the PWM clock divisor. - /// - /// - /// The PWM clock divisor. - /// - /// When pin mode is not set to PWM output - public int PwmClockDivisor - { - get => PinMode == GpioPinDriveMode.PwmOutput ? m_PwmClockDivisor : 0; - - set - { - lock (_syncLock) - { - if (PinMode != GpioPinDriveMode.PwmOutput) - { - m_PwmClockDivisor = 1; - - throw new InvalidOperationException( - $"Unable to set PWM range for pin {PinNumber} because operating mode is {PinMode}." - + $" Setting the PWM range is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.PwmOutput}"); - } - - WiringPi.PwmSetClock(value); - m_PwmClockDivisor = value; - } - } - } - - #endregion - - #region Software Tone Members - - /// - /// Gets a value indicating whether this instance is in software based tone generator mode. - /// - /// - /// true if this instance is in soft tone mode; otherwise, false. - /// - public bool IsInSoftToneMode => m_SoftToneFrequency >= 0; - - /// - /// Gets or sets the soft tone frequency. 0 to 5000 Hz is typical - /// - /// - /// The soft tone frequency. - /// - /// When soft tones cannot be initialized on the pin - public int SoftToneFrequency - { - get => m_SoftToneFrequency; - - set - { - lock (_syncLock) - { - if (IsInSoftToneMode == false) - { - var setupResult = WiringPi.SoftToneCreate(PinNumber); - if (setupResult != 0) - { - throw new InvalidOperationException( - $"Unable to initialize soft tone on pin {PinNumber}. Error Code: {setupResult}"); - } - } - - WiringPi.SoftToneWrite(PinNumber, value); - m_SoftToneFrequency = value; - } - } - } - - #endregion - - #region Software PWM Members - - /// - /// Gets a value indicating whether this pin is in software based PWM mode. - /// - /// - /// true if this instance is in soft PWM mode; otherwise, false. - /// - public bool IsInSoftPwmMode => m_SoftPwmValue >= 0; - - /// - /// Gets or sets the software PWM value on the pin. - /// - /// - /// The soft PWM value. - /// - /// StartSoftPwm - public int SoftPwmValue - { - get => m_SoftPwmValue; - - set - { - lock (_syncLock) - { - if (IsInSoftPwmMode && value >= 0) - { - WiringPi.SoftPwmWrite(PinNumber, value); - m_SoftPwmValue = value; - } - else - { - throw new InvalidOperationException($"Software PWM requires a call to {nameof(StartSoftPwm)}."); - } - } - } - } - - /// - /// Gets the software PWM range used upon starting the PWM. - /// - public int SoftPwmRange { get; private set; } = -1; - - /// - /// Starts the software based PWM on this pin. - /// - /// The value. - /// The range. - /// When the pin does not suppoert PWM - /// StartSoftPwm - /// or - public void StartSoftPwm(int value, int range) - { - lock (_syncLock) - { - if (Capabilities.Contains(PinCapability.GP) == false) - throw new NotSupportedException($"Pin {PinNumber} does not support software PWM"); - - if (IsInSoftPwmMode) - throw new InvalidOperationException($"{nameof(StartSoftPwm)} has already been called."); - - var startResult = WiringPi.SoftPwmCreate(PinNumber, value, range); - - if (startResult == 0) - { - m_SoftPwmValue = value; - SoftPwmRange = range; - } - else - { - throw new InvalidOperationException( - $"Could not start software based PWM on pin {PinNumber}. Error code: {startResult}"); - } - } - } - - #endregion - - #region Output Mode (Write) Members - - /// - /// Writes the specified pin value. - /// This method performs a digital write - /// - /// The value. - public void Write(GpioPinValue value) - { - lock (_syncLock) - { - if (PinMode != GpioPinDriveMode.Output) - { - throw new InvalidOperationException( - $"Unable to write to pin {PinNumber} because operating mode is {PinMode}." - + $" Writes are only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Output}"); - } - - WiringPi.DigitalWrite(PinNumber, (int)value); - } - } - - /// - /// Writes the value asynchronously. - /// - /// The value. - /// The awaitable task - public Task WriteAsync(GpioPinValue value) => Task.Run(() => { Write(value); }); - - /// - /// Writes the specified bit value. - /// This method performs a digital write - /// - /// if set to true [value]. - public void Write(bool value) - => Write(value ? GpioPinValue.High : GpioPinValue.Low); - - /// - /// Writes the specified bit value. - /// This method performs a digital write - /// - /// The value. - /// - /// The awaitable task - /// - public Task WriteAsync(bool value) => Task.Run(() => { Write(value); }); - - /// - /// Writes the specified value. 0 for low, any other value for high - /// This method performs a digital write - /// - /// The value. - public void Write(int value) => Write(value != 0 ? GpioPinValue.High : GpioPinValue.Low); - - /// - /// Writes the specified value. 0 for low, any other value for high - /// This method performs a digital write - /// - /// The value. - /// The awaitable task - public Task WriteAsync(int value) => Task.Run(() => { Write(value); }); - - /// - /// Writes the specified value as an analog level. - /// You will need to register additional analog modules to enable this function for devices such as the Gertboard. - /// - /// The value. - public void WriteLevel(int value) - { - lock (_syncLock) - { - if (PinMode != GpioPinDriveMode.Output) - { - throw new InvalidOperationException( - $"Unable to write to pin {PinNumber} because operating mode is {PinMode}." - + $" Writes are only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Output}"); - } - - WiringPi.AnalogWrite(PinNumber, value); - } - } - - /// - /// Writes the specified value as an analog level. - /// You will need to register additional analog modules to enable this function for devices such as the Gertboard. - /// - /// The value. - /// The awaitable task - public Task WriteLevelAsync(int value) => Task.Run(() => { WriteLevel(value); }); - - #endregion - - #region Input Mode (Read) Members - - /// - /// Wait for specific pin status - /// - /// status to check - /// timeout to reach status - /// true/false - public bool WaitForValue(GpioPinValue status, int timeOutMillisecond) - { - if (PinMode != GpioPinDriveMode.Input) - { - throw new InvalidOperationException( - $"Unable to read from pin {PinNumber} because operating mode is {PinMode}." - + $" Reads are only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Input}"); - } - - var hrt = new HighResolutionTimer(); - hrt.Start(); - do - { - if (ReadValue() == status) - return true; - - Pi.Timing.SleepMicroseconds(101); // 101 uses nanosleep as opposed to a loop. - } - while (hrt.ElapsedMilliseconds <= timeOutMillisecond); - - return false; - } - - /// - /// Reads the digital value on the pin as a boolean value. - /// - /// The state of the pin - public bool Read() - { - lock (_syncLock) - { - if (PinMode != GpioPinDriveMode.Input && PinMode != GpioPinDriveMode.Output) - { - throw new InvalidOperationException( - $"Unable to read from pin {PinNumber} because operating mode is {PinMode}." - + $" Reads are only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Input} or {GpioPinDriveMode.Output}"); - } - - return WiringPi.DigitalRead(PinNumber) != 0; - } - } - - /// - /// Reads the digital value on the pin as a boolean value. - /// - /// The state of the pin - public Task ReadAsync() => Task.Run(() => Read()); - - /// - /// Reads the digital value on the pin as a High or Low value. - /// - /// The state of the pin - public GpioPinValue ReadValue() - => Read() ? GpioPinValue.High : GpioPinValue.Low; - - /// - /// Reads the digital value on the pin as a High or Low value. - /// - /// The state of the pin - public Task ReadValueAsync() => Task.Run(() => ReadValue()); - - /// - /// Reads the analog value on the pin. - /// This returns the value read on the supplied analog input pin. You will need to register - /// additional analog modules to enable this function for devices such as the Gertboard, - /// quick2Wire analog board, etc. - /// - /// The analog level - /// When the pin mode is not configured as an input. - public int ReadLevel() - { - lock (_syncLock) - { - if (PinMode != GpioPinDriveMode.Input) - { - throw new InvalidOperationException( - $"Unable to read from pin {PinNumber} because operating mode is {PinMode}." - + $" Reads are only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Input}"); - } - - return WiringPi.AnalogRead(PinNumber); - } - } - - /// - /// Reads the analog value on the pin. - /// This returns the value read on the supplied analog input pin. You will need to register - /// additional analog modules to enable this function for devices such as the Gertboard, - /// quick2Wire analog board, etc. - /// - /// The analog level - public Task ReadLevelAsync() => Task.Run(() => ReadLevel()); - - #endregion - - #region Interrupts - - /// - /// Registers the interrupt callback on the pin. Pin mode has to be set to Input. - /// - /// The edge detection. - /// The callback. - /// callback - /// - /// An interrupt callback was already registered. - /// or - /// RegisterInterruptCallback - /// - public void RegisterInterruptCallback(EdgeDetection edgeDetection, InterruptServiceRoutineCallback callback) - { - if (callback == null) - throw new ArgumentException($"{nameof(callback)} cannot be null"); - - if (InterruptCallback != null) - throw new InvalidOperationException("An interrupt callback was already registered."); - - if (PinMode != GpioPinDriveMode.Input) - { - throw new InvalidOperationException( - $"Unable to {nameof(RegisterInterruptCallback)} for pin {PinNumber} because operating mode is {PinMode}." - + $" Calling {nameof(RegisterInterruptCallback)} is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Input}"); - } - - lock (_syncLock) - { - var registerResult = WiringPi.WiringPiISR(PinNumber, (int)edgeDetection, callback); - if (registerResult == 0) - { - InterruptEdgeDetection = edgeDetection; - InterruptCallback = callback; - } - else - { - HardwareException.Throw(nameof(GpioPin), nameof(RegisterInterruptCallback)); - } - } - } - - #endregion - } + /// The wiring pi pin number. + /// The header pin number. + private GpioPin(WiringPiPin wiringPiPinNumber, Int32 headerPinNumber) { + this.PinNumber = (Int32)wiringPiPinNumber; + this.WiringPiPinNumber = wiringPiPinNumber; + this.BcmPinNumber = GpioController.WiringPiToBcmPinNumber((Int32)wiringPiPinNumber); + this.HeaderPinNumber = headerPinNumber; + this.Header = (this.PinNumber >= 17 && this.PinNumber <= 20) ? GpioHeader.P5 : GpioHeader.P1; + } + + #endregion + + #region Pin Properties + + /// + /// Gets or sets the Wiring Pi pin number as an integer. + /// + public Int32 PinNumber { + get; + } + + /// + /// Gets the WiringPi Pin number + /// + public WiringPiPin WiringPiPinNumber { + get; + } + + /// + /// Gets the BCM chip (hardware) pin number. + /// + public Int32 BcmPinNumber { + get; + } + + /// + /// Gets or the physical header (physical board) pin number. + /// + public Int32 HeaderPinNumber { + get; + } + + /// + /// Gets the pin's header (physical board) location. + /// + public GpioHeader Header { + get; + } + + /// + /// Gets the friendly name of the pin. + /// + public String Name { + get; private set; + } + + /// + /// Gets the hardware mode capabilities of this pin. + /// + public PinCapability[] Capabilities { + get; private set; + } + + #endregion + + #region Hardware-Specific Properties + + /// + /// Gets or sets the pin operating mode. + /// + /// + /// The pin mode. + /// + /// Thrown when a pin does not support the given operation mode. + public GpioPinDriveMode PinMode { + get => this.m_PinMode; + + set { + lock(this._syncLock) { + GpioPinDriveMode mode = value; + if(mode == GpioPinDriveMode.GpioClock && this.Capabilities.Contains(PinCapability.GPCLK) == false || + mode == GpioPinDriveMode.PwmOutput && this.Capabilities.Contains(PinCapability.PWM) == false || + mode == GpioPinDriveMode.Input && this.Capabilities.Contains(PinCapability.GP) == false || + mode == GpioPinDriveMode.Output && this.Capabilities.Contains(PinCapability.GP) == false) { + throw new NotSupportedException( + $"Pin {this.WiringPiPinNumber} '{this.Name}' does not support mode '{mode}'. Pin capabilities are limited to: {String.Join(", ", this.Capabilities)}"); + } + + WiringPi.PinMode(this.PinNumber, (Int32)mode); + this.m_PinMode = mode; + } + } + } + + /// + /// Gets the interrupt callback. Returns null if no interrupt + /// has been registered. + /// + public InterruptServiceRoutineCallback InterruptCallback { + get; private set; + } + + /// + /// Gets the interrupt edge detection mode. + /// + public EdgeDetection InterruptEdgeDetection { get; private set; } = EdgeDetection.ExternalSetup; + + #endregion + + #region Hardware PWM Members + + /// + /// This sets or gets the pull-up or pull-down resistor mode on the pin, which should be set as an input. + /// Unlike the Arduino, the BCM2835 has both pull-up an down internal resistors. + /// The parameter pud should be; PUD_OFF, (no pull up/down), PUD_DOWN (pull to ground) or PUD_UP (pull to 3.3v) + /// The internal pull up/down resistors have a value of approximately 50KΩ on the Raspberry Pi. + /// + public GpioPinResistorPullMode InputPullMode { + get => this.PinMode == GpioPinDriveMode.Input ? this.m_ResistorPullMode : GpioPinResistorPullMode.Off; + + set { + lock(this._syncLock) { + if(this.PinMode != GpioPinDriveMode.Input) { + this.m_ResistorPullMode = GpioPinResistorPullMode.Off; + throw new InvalidOperationException( + $"Unable to set the {nameof(this.InputPullMode)} for pin {this.PinNumber} because operating mode is {this.PinMode}." + + $" Setting the {nameof(this.InputPullMode)} is only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Input}"); + } + + WiringPi.PullUpDnControl(this.PinNumber, (Int32)value); + this.m_ResistorPullMode = value; + } + } + } + + /// + /// Gets or sets the PWM register. Values should be between 0 and 1024 + /// + /// + /// The PWM register. + /// + public Int32 PwmRegister { + get => this.m_PwmRegister; + + set { + lock(this._syncLock) { + if(this.PinMode != GpioPinDriveMode.PwmOutput) { + this.m_PwmRegister = 0; + + throw new InvalidOperationException( + $"Unable to write PWM register for pin {this.PinNumber} because operating mode is {this.PinMode}." + + $" Writing the PWM register is only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.PwmOutput}"); + } + + Int32 val = value.Clamp(0, 1024); + + WiringPi.PwmWrite(this.PinNumber, val); + this.m_PwmRegister = val; + } + } + } + + /// + /// The PWM generator can run in 2 modes – “balanced” and “mark:space”. The mark:space mode is traditional, + /// however the default mode in the Pi is “balanced”. + /// + /// + /// The PWM mode. + /// + /// When pin mode is not set a Pwn output + public PwmMode PwmMode { + get => this.PinMode == GpioPinDriveMode.PwmOutput ? this.m_PwmMode : PwmMode.Balanced; + + set { + lock(this._syncLock) { + if(this.PinMode != GpioPinDriveMode.PwmOutput) { + this.m_PwmMode = PwmMode.Balanced; + + throw new InvalidOperationException( + $"Unable to set PWM mode for pin {this.PinNumber} because operating mode is {this.PinMode}." + + $" Setting the PWM mode is only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.PwmOutput}"); + } + + WiringPi.PwmSetMode((Int32)value); + this.m_PwmMode = value; + } + } + } + + /// + /// This sets the range register in the PWM generator. The default is 1024. + /// + /// + /// The PWM range. + /// + /// When pin mode is not set to PWM output + public UInt32 PwmRange { + get => this.PinMode == GpioPinDriveMode.PwmOutput ? this.m_PwmRange : 0; + + set { + lock(this._syncLock) { + if(this.PinMode != GpioPinDriveMode.PwmOutput) { + this.m_PwmRange = 1024; + + throw new InvalidOperationException( + $"Unable to set PWM range for pin {this.PinNumber} because operating mode is {this.PinMode}." + + $" Setting the PWM range is only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.PwmOutput}"); + } + + WiringPi.PwmSetRange(value); + this.m_PwmRange = value; + } + } + } + + /// + /// Gets or sets the PWM clock divisor. + /// + /// + /// The PWM clock divisor. + /// + /// When pin mode is not set to PWM output + public Int32 PwmClockDivisor { + get => this.PinMode == GpioPinDriveMode.PwmOutput ? this.m_PwmClockDivisor : 0; + + set { + lock(this._syncLock) { + if(this.PinMode != GpioPinDriveMode.PwmOutput) { + this.m_PwmClockDivisor = 1; + + throw new InvalidOperationException( + $"Unable to set PWM range for pin {this.PinNumber} because operating mode is {this.PinMode}." + + $" Setting the PWM range is only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.PwmOutput}"); + } + + WiringPi.PwmSetClock(value); + this.m_PwmClockDivisor = value; + } + } + } + + #endregion + + #region Software Tone Members + + /// + /// Gets a value indicating whether this instance is in software based tone generator mode. + /// + /// + /// true if this instance is in soft tone mode; otherwise, false. + /// + public Boolean IsInSoftToneMode => this.m_SoftToneFrequency >= 0; + + /// + /// Gets or sets the soft tone frequency. 0 to 5000 Hz is typical + /// + /// + /// The soft tone frequency. + /// + /// When soft tones cannot be initialized on the pin + public Int32 SoftToneFrequency { + get => this.m_SoftToneFrequency; + + set { + lock(this._syncLock) { + if(this.IsInSoftToneMode == false) { + Int32 setupResult = WiringPi.SoftToneCreate(this.PinNumber); + if(setupResult != 0) { + throw new InvalidOperationException( + $"Unable to initialize soft tone on pin {this.PinNumber}. Error Code: {setupResult}"); + } + } + + WiringPi.SoftToneWrite(this.PinNumber, value); + this.m_SoftToneFrequency = value; + } + } + } + + #endregion + + #region Software PWM Members + + /// + /// Gets a value indicating whether this pin is in software based PWM mode. + /// + /// + /// true if this instance is in soft PWM mode; otherwise, false. + /// + public Boolean IsInSoftPwmMode => this.m_SoftPwmValue >= 0; + + /// + /// Gets or sets the software PWM value on the pin. + /// + /// + /// The soft PWM value. + /// + /// StartSoftPwm + public Int32 SoftPwmValue { + get => this.m_SoftPwmValue; + + set { + lock(this._syncLock) { + if(this.IsInSoftPwmMode && value >= 0) { + WiringPi.SoftPwmWrite(this.PinNumber, value); + this.m_SoftPwmValue = value; + } else { + throw new InvalidOperationException($"Software PWM requires a call to {nameof(StartSoftPwm)}."); + } + } + } + } + + /// + /// Gets the software PWM range used upon starting the PWM. + /// + public Int32 SoftPwmRange { get; private set; } = -1; + + /// + /// Starts the software based PWM on this pin. + /// + /// The value. + /// The range. + /// When the pin does not suppoert PWM + /// StartSoftPwm + /// or + public void StartSoftPwm(Int32 value, Int32 range) { + lock(this._syncLock) { + if(this.Capabilities.Contains(PinCapability.GP) == false) { + throw new NotSupportedException($"Pin {this.PinNumber} does not support software PWM"); + } + + if(this.IsInSoftPwmMode) { + throw new InvalidOperationException($"{nameof(StartSoftPwm)} has already been called."); + } + + Int32 startResult = WiringPi.SoftPwmCreate(this.PinNumber, value, range); + + if(startResult == 0) { + this.m_SoftPwmValue = value; + this.SoftPwmRange = range; + } else { + throw new InvalidOperationException( + $"Could not start software based PWM on pin {this.PinNumber}. Error code: {startResult}"); + } + } + } + + #endregion + + #region Output Mode (Write) Members + + /// + /// Writes the specified pin value. + /// This method performs a digital write + /// + /// The value. + public void Write(GpioPinValue value) { + lock(this._syncLock) { + if(this.PinMode != GpioPinDriveMode.Output) { + throw new InvalidOperationException( + $"Unable to write to pin {this.PinNumber} because operating mode is {this.PinMode}." + + $" Writes are only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Output}"); + } + + WiringPi.DigitalWrite(this.PinNumber, (Int32)value); + } + } + + /// + /// Writes the value asynchronously. + /// + /// The value. + /// The awaitable task + public Task WriteAsync(GpioPinValue value) => Task.Run(() => this.Write(value)); + + /// + /// Writes the specified bit value. + /// This method performs a digital write + /// + /// if set to true [value]. + public void Write(Boolean value) + => this.Write(value ? GpioPinValue.High : GpioPinValue.Low); + + /// + /// Writes the specified bit value. + /// This method performs a digital write + /// + /// The value. + /// + /// The awaitable task + /// + public Task WriteAsync(Boolean value) => Task.Run(() => this.Write(value)); + + /// + /// Writes the specified value. 0 for low, any other value for high + /// This method performs a digital write + /// + /// The value. + public void Write(Int32 value) => this.Write(value != 0 ? GpioPinValue.High : GpioPinValue.Low); + + /// + /// Writes the specified value. 0 for low, any other value for high + /// This method performs a digital write + /// + /// The value. + /// The awaitable task + public Task WriteAsync(Int32 value) => Task.Run(() => this.Write(value)); + + /// + /// Writes the specified value as an analog level. + /// You will need to register additional analog modules to enable this function for devices such as the Gertboard. + /// + /// The value. + public void WriteLevel(Int32 value) { + lock(this._syncLock) { + if(this.PinMode != GpioPinDriveMode.Output) { + throw new InvalidOperationException( + $"Unable to write to pin {this.PinNumber} because operating mode is {this.PinMode}." + + $" Writes are only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Output}"); + } + + WiringPi.AnalogWrite(this.PinNumber, value); + } + } + + /// + /// Writes the specified value as an analog level. + /// You will need to register additional analog modules to enable this function for devices such as the Gertboard. + /// + /// The value. + /// The awaitable task + public Task WriteLevelAsync(Int32 value) => Task.Run(() => this.WriteLevel(value)); + + #endregion + + #region Input Mode (Read) Members + + /// + /// Wait for specific pin status + /// + /// status to check + /// timeout to reach status + /// true/false + public Boolean WaitForValue(GpioPinValue status, Int32 timeOutMillisecond) { + if(this.PinMode != GpioPinDriveMode.Input) { + throw new InvalidOperationException( + $"Unable to read from pin {this.PinNumber} because operating mode is {this.PinMode}." + + $" Reads are only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Input}"); + } + + HighResolutionTimer hrt = new HighResolutionTimer(); + hrt.Start(); + do { + if(this.ReadValue() == status) { + return true; + } + + Pi.Timing.SleepMicroseconds(101); // 101 uses nanosleep as opposed to a loop. + } + while(hrt.ElapsedMilliseconds <= timeOutMillisecond); + + return false; + } + + /// + /// Reads the digital value on the pin as a boolean value. + /// + /// The state of the pin + public Boolean Read() { + lock(this._syncLock) { + if(this.PinMode != GpioPinDriveMode.Input && this.PinMode != GpioPinDriveMode.Output) { + throw new InvalidOperationException( + $"Unable to read from pin {this.PinNumber} because operating mode is {this.PinMode}." + + $" Reads are only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Input} or {GpioPinDriveMode.Output}"); + } + + return WiringPi.DigitalRead(this.PinNumber) != 0; + } + } + + /// + /// Reads the digital value on the pin as a boolean value. + /// + /// The state of the pin + public Task ReadAsync() => Task.Run(() => this.Read()); + + /// + /// Reads the digital value on the pin as a High or Low value. + /// + /// The state of the pin + public GpioPinValue ReadValue() + => this.Read() ? GpioPinValue.High : GpioPinValue.Low; + + /// + /// Reads the digital value on the pin as a High or Low value. + /// + /// The state of the pin + public Task ReadValueAsync() => Task.Run(() => this.ReadValue()); + + /// + /// Reads the analog value on the pin. + /// This returns the value read on the supplied analog input pin. You will need to register + /// additional analog modules to enable this function for devices such as the Gertboard, + /// quick2Wire analog board, etc. + /// + /// The analog level + /// When the pin mode is not configured as an input. + public Int32 ReadLevel() { + lock(this._syncLock) { + if(this.PinMode != GpioPinDriveMode.Input) { + throw new InvalidOperationException( + $"Unable to read from pin {this.PinNumber} because operating mode is {this.PinMode}." + + $" Reads are only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Input}"); + } + + return WiringPi.AnalogRead(this.PinNumber); + } + } + + /// + /// Reads the analog value on the pin. + /// This returns the value read on the supplied analog input pin. You will need to register + /// additional analog modules to enable this function for devices such as the Gertboard, + /// quick2Wire analog board, etc. + /// + /// The analog level + public Task ReadLevelAsync() => Task.Run(() => this.ReadLevel()); + + #endregion + + #region Interrupts + + /// + /// Registers the interrupt callback on the pin. Pin mode has to be set to Input. + /// + /// The edge detection. + /// The callback. + /// callback + /// + /// An interrupt callback was already registered. + /// or + /// RegisterInterruptCallback + /// + public void RegisterInterruptCallback(EdgeDetection edgeDetection, InterruptServiceRoutineCallback callback) { + if(callback == null) { + throw new ArgumentException($"{nameof(callback)} cannot be null"); + } + + if(this.InterruptCallback != null) { + throw new InvalidOperationException("An interrupt callback was already registered."); + } + + if(this.PinMode != GpioPinDriveMode.Input) { + throw new InvalidOperationException( + $"Unable to {nameof(RegisterInterruptCallback)} for pin {this.PinNumber} because operating mode is {this.PinMode}." + + $" Calling {nameof(RegisterInterruptCallback)} is only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Input}"); + } + + lock(this._syncLock) { + Int32 registerResult = WiringPi.WiringPiISR(this.PinNumber, (Int32)edgeDetection, callback); + if(registerResult == 0) { + this.InterruptEdgeDetection = edgeDetection; + this.InterruptCallback = callback; + } else { + HardwareException.Throw(nameof(GpioPin), nameof(RegisterInterruptCallback)); + } + } + } + + #endregion + } } diff --git a/Unosquare.RaspberryIO/Gpio/I2CBus.cs b/Unosquare.RaspberryIO/Gpio/I2CBus.cs index cbecc3c..0f530c6 100644 --- a/Unosquare.RaspberryIO/Gpio/I2CBus.cs +++ b/Unosquare.RaspberryIO/Gpio/I2CBus.cs @@ -1,93 +1,87 @@ -namespace Unosquare.RaspberryIO.Gpio -{ - using Native; - using Swan.Abstractions; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Linq; - - /// - /// A simple wrapper for the I2c bus on the Raspberry Pi - /// - public class I2CBus : SingletonBase - { - // TODO: It would be nice to integrate i2c device detection. - private static readonly object SyncRoot = new object(); - private readonly Dictionary _devices = new Dictionary(); - - /// - /// Prevents a default instance of the class from being created. - /// - private I2CBus() - { - // placeholder - } - - /// - /// Gets the registered devices as a read only collection. - /// - public ReadOnlyCollection Devices => new ReadOnlyCollection(_devices.Values.ToArray()); - - /// - /// Gets the with the specified device identifier. - /// - /// - /// The . - /// - /// The device identifier. - /// A reference to an I2C device - public I2CDevice this[int deviceId] => GetDeviceById(deviceId); - - /// - /// Gets the device by identifier. - /// - /// The device identifier. - /// The device reference - public I2CDevice GetDeviceById(int deviceId) - { - lock (SyncRoot) - { - return _devices[deviceId]; - } - } - - /// - /// Adds a device to the bus by its Id. If the device is already registered it simply returns the existing device. - /// - /// The device identifier. - /// The device reference - /// When the device file descriptor is not found - public I2CDevice AddDevice(int deviceId) - { - lock (SyncRoot) - { - if (_devices.ContainsKey(deviceId)) - return _devices[deviceId]; - - var fileDescriptor = SetupFileDescriptor(deviceId); - if (fileDescriptor < 0) - throw new KeyNotFoundException($"Device with id {deviceId} could not be registered with the I2C bus. Error Code: {fileDescriptor}."); - - var device = new I2CDevice(deviceId, fileDescriptor); - _devices[deviceId] = device; - return device; - } - } - - /// - /// This initializes the I2C system with your given device identifier. - /// The ID is the I2C number of the device and you can use the i2cdetect program to find this out. - /// wiringPiI2CSetup() will work out which revision Raspberry Pi you have and open the appropriate device in /dev. - /// The return value is the standard Linux filehandle, or -1 if any error – in which case, you can consult errno as usual. - /// - /// The device identifier. - /// The Linux file handle - private static int SetupFileDescriptor(int deviceId) - { - lock (SyncRoot) - { - return WiringPi.WiringPiI2CSetup(deviceId); - } - } - } -} +using Unosquare.RaspberryIO.Native; +using Unosquare.Swan.Abstractions; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System; + +namespace Unosquare.RaspberryIO.Gpio { + /// + /// A simple wrapper for the I2c bus on the Raspberry Pi + /// + public class I2CBus : SingletonBase { + // TODO: It would be nice to integrate i2c device detection. + private static readonly Object SyncRoot = new Object(); + private readonly Dictionary _devices = new Dictionary(); + + /// + /// Prevents a default instance of the class from being created. + /// + private I2CBus() { + // placeholder + } + + /// + /// Gets the registered devices as a read only collection. + /// + public ReadOnlyCollection Devices => new ReadOnlyCollection(this._devices.Values.ToArray()); + + /// + /// Gets the with the specified device identifier. + /// + /// + /// The . + /// + /// The device identifier. + /// A reference to an I2C device + public I2CDevice this[Int32 deviceId] => this.GetDeviceById(deviceId); + + /// + /// Gets the device by identifier. + /// + /// The device identifier. + /// The device reference + public I2CDevice GetDeviceById(Int32 deviceId) { + lock(SyncRoot) { + return this._devices[deviceId]; + } + } + + /// + /// Adds a device to the bus by its Id. If the device is already registered it simply returns the existing device. + /// + /// The device identifier. + /// The device reference + /// When the device file descriptor is not found + public I2CDevice AddDevice(Int32 deviceId) { + lock(SyncRoot) { + if(this._devices.ContainsKey(deviceId)) { + return this._devices[deviceId]; + } + + Int32 fileDescriptor = SetupFileDescriptor(deviceId); + if(fileDescriptor < 0) { + throw new KeyNotFoundException($"Device with id {deviceId} could not be registered with the I2C bus. Error Code: {fileDescriptor}."); + } + + I2CDevice device = new I2CDevice(deviceId, fileDescriptor); + this._devices[deviceId] = device; + return device; + } + } + + /// + /// This initializes the I2C system with your given device identifier. + /// The ID is the I2C number of the device and you can use the i2cdetect program to find this out. + /// wiringPiI2CSetup() will work out which revision Raspberry Pi you have and open the appropriate device in /dev. + /// The return value is the standard Linux filehandle, or -1 if any error – in which case, you can consult errno as usual. + /// + /// The device identifier. + /// The Linux file handle + private static Int32 SetupFileDescriptor(Int32 deviceId) { + lock(SyncRoot) { + return WiringPi.WiringPiI2CSetup(deviceId); + } + } + } +} diff --git a/Unosquare.RaspberryIO/Gpio/I2CDevice.cs b/Unosquare.RaspberryIO/Gpio/I2CDevice.cs index b9d42bf..ffefc24 100644 --- a/Unosquare.RaspberryIO/Gpio/I2CDevice.cs +++ b/Unosquare.RaspberryIO/Gpio/I2CDevice.cs @@ -1,195 +1,193 @@ -namespace Unosquare.RaspberryIO.Gpio -{ - using System; - using System.Threading.Tasks; - using Native; - - /// - /// Represents a device on the I2C Bus - /// - public class I2CDevice - { - private readonly object _syncLock = new object(); - - /// - /// Initializes a new instance of the class. - /// - /// The device identifier. - /// The file descriptor. - internal I2CDevice(int deviceId, int fileDescriptor) - { - DeviceId = deviceId; - FileDescriptor = fileDescriptor; - } - - /// - /// Gets the device identifier. - /// - /// - /// The device identifier. - /// - public int DeviceId { get; } - - /// - /// Gets the standard POSIX file descriptor. - /// - /// - /// The file descriptor. - /// - public int FileDescriptor { get; } - - /// - /// Reads a byte from the specified file descriptor - /// - /// The byte from device - public byte Read() - { - lock (_syncLock) - { - var result = WiringPi.WiringPiI2CRead(FileDescriptor); - if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(Read)); - return (byte)result; - } - } - - /// - /// Reads a byte from the specified file descriptor - /// - /// The byte from device - public Task ReadAsync() => Task.Run(() => Read()); - - /// - /// Reads a buffer of the specified length, one byte at a time - /// - /// The length. - /// The byte array from device - public byte[] Read(int length) - { - lock (_syncLock) - { - var buffer = new byte[length]; - for (var i = 0; i < length; i++) - { - var result = WiringPi.WiringPiI2CRead(FileDescriptor); - if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(Read)); - buffer[i] = (byte)result; - } - - return buffer; - } - } - - /// - /// Reads a buffer of the specified length, one byte at a time - /// - /// The length. - /// The byte array from device - public Task ReadAsync(int length) => Task.Run(() => Read(length)); - - /// - /// Writes a byte of data the specified file descriptor. - /// - /// The data. - public void Write(byte data) - { - lock (_syncLock) - { - var result = WiringPi.WiringPiI2CWrite(FileDescriptor, data); - if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(Write)); - } - } - - /// - /// Writes a byte of data the specified file descriptor. - /// - /// The data. - /// The awaitable task - public Task WriteAsync(byte data) => Task.Run(() => { Write(data); }); - - /// - /// Writes a set of bytes to the specified file descriptor. - /// - /// The data. - public void Write(byte[] data) - { - lock (_syncLock) - { - foreach (var b in data) - { - var result = WiringPi.WiringPiI2CWrite(FileDescriptor, b); - if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(Write)); - } - } - } - - /// - /// Writes a set of bytes to the specified file descriptor. - /// - /// The data. - /// The awaitable task - public Task WriteAsync(byte[] data) - { - return Task.Run(() => { Write(data); }); - } - - /// - /// These write an 8 or 16-bit data value into the device register indicated. - /// - /// The register. - /// The data. - public void WriteAddressByte(int address, byte data) - { - lock (_syncLock) - { - var result = WiringPi.WiringPiI2CWriteReg8(FileDescriptor, address, data); - if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(WriteAddressByte)); - } - } - - /// - /// These write an 8 or 16-bit data value into the device register indicated. - /// - /// The register. - /// The data. - public void WriteAddressWord(int address, ushort data) - { - lock (_syncLock) - { - var result = WiringPi.WiringPiI2CWriteReg16(FileDescriptor, address, data); - if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(WriteAddressWord)); - } - } - - /// - /// These read an 8 or 16-bit value from the device register indicated. - /// - /// The register. - /// The address byte from device - public byte ReadAddressByte(int address) - { - lock (_syncLock) - { - var result = WiringPi.WiringPiI2CReadReg8(FileDescriptor, address); - if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(ReadAddressByte)); - - return (byte)result; - } - } - - /// - /// These read an 8 or 16-bit value from the device register indicated. - /// - /// The register. - /// The address word from device - public ushort ReadAddressWord(int address) - { - lock (_syncLock) - { - var result = WiringPi.WiringPiI2CReadReg16(FileDescriptor, address); - if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(ReadAddressWord)); - - return Convert.ToUInt16(result); - } - } - } -} +using System; +using System.Threading.Tasks; +using Unosquare.RaspberryIO.Native; + +namespace Unosquare.RaspberryIO.Gpio { + /// + /// Represents a device on the I2C Bus + /// + public class I2CDevice { + private readonly Object _syncLock = new Object(); + + /// + /// Initializes a new instance of the class. + /// + /// The device identifier. + /// The file descriptor. + internal I2CDevice(Int32 deviceId, Int32 fileDescriptor) { + this.DeviceId = deviceId; + this.FileDescriptor = fileDescriptor; + } + + /// + /// Gets the device identifier. + /// + /// + /// The device identifier. + /// + public Int32 DeviceId { + get; + } + + /// + /// Gets the standard POSIX file descriptor. + /// + /// + /// The file descriptor. + /// + public Int32 FileDescriptor { + get; + } + + /// + /// Reads a byte from the specified file descriptor + /// + /// The byte from device + public Byte Read() { + lock(this._syncLock) { + Int32 result = WiringPi.WiringPiI2CRead(this.FileDescriptor); + if(result < 0) { + HardwareException.Throw(nameof(I2CDevice), nameof(Read)); + } + + return (Byte)result; + } + } + + /// + /// Reads a byte from the specified file descriptor + /// + /// The byte from device + public Task ReadAsync() => Task.Run(() => this.Read()); + + /// + /// Reads a buffer of the specified length, one byte at a time + /// + /// The length. + /// The byte array from device + public Byte[] Read(Int32 length) { + lock(this._syncLock) { + Byte[] buffer = new Byte[length]; + for(Int32 i = 0; i < length; i++) { + Int32 result = WiringPi.WiringPiI2CRead(this.FileDescriptor); + if(result < 0) { + HardwareException.Throw(nameof(I2CDevice), nameof(Read)); + } + + buffer[i] = (Byte)result; + } + + return buffer; + } + } + + /// + /// Reads a buffer of the specified length, one byte at a time + /// + /// The length. + /// The byte array from device + public Task ReadAsync(Int32 length) => Task.Run(() => this.Read(length)); + + /// + /// Writes a byte of data the specified file descriptor. + /// + /// The data. + public void Write(Byte data) { + lock(this._syncLock) { + Int32 result = WiringPi.WiringPiI2CWrite(this.FileDescriptor, data); + if(result < 0) { + HardwareException.Throw(nameof(I2CDevice), nameof(Write)); + } + } + } + + /// + /// Writes a byte of data the specified file descriptor. + /// + /// The data. + /// The awaitable task + public Task WriteAsync(Byte data) => Task.Run(() => this.Write(data)); + + /// + /// Writes a set of bytes to the specified file descriptor. + /// + /// The data. + public void Write(Byte[] data) { + lock(this._syncLock) { + foreach(Byte b in data) { + Int32 result = WiringPi.WiringPiI2CWrite(this.FileDescriptor, b); + if(result < 0) { + HardwareException.Throw(nameof(I2CDevice), nameof(Write)); + } + } + } + } + + /// + /// Writes a set of bytes to the specified file descriptor. + /// + /// The data. + /// The awaitable task + public Task WriteAsync(Byte[] data) => Task.Run(() => this.Write(data)); + + /// + /// These write an 8 or 16-bit data value into the device register indicated. + /// + /// The register. + /// The data. + public void WriteAddressByte(Int32 address, Byte data) { + lock(this._syncLock) { + Int32 result = WiringPi.WiringPiI2CWriteReg8(this.FileDescriptor, address, data); + if(result < 0) { + HardwareException.Throw(nameof(I2CDevice), nameof(WriteAddressByte)); + } + } + } + + /// + /// These write an 8 or 16-bit data value into the device register indicated. + /// + /// The register. + /// The data. + public void WriteAddressWord(Int32 address, UInt16 data) { + lock(this._syncLock) { + Int32 result = WiringPi.WiringPiI2CWriteReg16(this.FileDescriptor, address, data); + if(result < 0) { + HardwareException.Throw(nameof(I2CDevice), nameof(WriteAddressWord)); + } + } + } + + /// + /// These read an 8 or 16-bit value from the device register indicated. + /// + /// The register. + /// The address byte from device + public Byte ReadAddressByte(Int32 address) { + lock(this._syncLock) { + Int32 result = WiringPi.WiringPiI2CReadReg8(this.FileDescriptor, address); + if(result < 0) { + HardwareException.Throw(nameof(I2CDevice), nameof(ReadAddressByte)); + } + + return (Byte)result; + } + } + + /// + /// These read an 8 or 16-bit value from the device register indicated. + /// + /// The register. + /// The address word from device + public UInt16 ReadAddressWord(Int32 address) { + lock(this._syncLock) { + Int32 result = WiringPi.WiringPiI2CReadReg16(this.FileDescriptor, address); + if(result < 0) { + HardwareException.Throw(nameof(I2CDevice), nameof(ReadAddressWord)); + } + + return Convert.ToUInt16(result); + } + } + } +} diff --git a/Unosquare.RaspberryIO/Gpio/SpiBus.cs b/Unosquare.RaspberryIO/Gpio/SpiBus.cs index ba9ebc6..7ca7a73 100644 --- a/Unosquare.RaspberryIO/Gpio/SpiBus.cs +++ b/Unosquare.RaspberryIO/Gpio/SpiBus.cs @@ -1,72 +1,72 @@ -namespace Unosquare.RaspberryIO.Gpio -{ - using Swan.Abstractions; - +using System; +using Unosquare.Swan.Abstractions; + +namespace Unosquare.RaspberryIO.Gpio { + /// + /// The SPI Bus containing the 2 SPI channels + /// + public class SpiBus : SingletonBase { /// - /// The SPI Bus containing the 2 SPI channels + /// Prevents a default instance of the class from being created. /// - public class SpiBus : SingletonBase - { - /// - /// Prevents a default instance of the class from being created. - /// - private SpiBus() - { - // placeholder - } - - #region SPI Access - - /// - /// Gets or sets the channel 0 frequency in Hz. - /// - /// - /// The channel0 frequency. - /// - public int Channel0Frequency { get; set; } - - /// - /// Gets the SPI bus on channel 1. - /// - /// - /// The channel0. - /// - public SpiChannel Channel0 - { - get - { - if (Channel0Frequency == 0) - Channel0Frequency = SpiChannel.DefaultFrequency; - - return SpiChannel.Retrieve(SpiChannelNumber.Channel0, Channel0Frequency); - } - } - - /// - /// Gets or sets the channel 1 frequency in Hz - /// - /// - /// The channel1 frequency. - /// - public int Channel1Frequency { get; set; } - - /// - /// Gets the SPI bus on channel 1. - /// - /// - /// The channel1. - /// - public SpiChannel Channel1 - { - get - { - if (Channel1Frequency == 0) - Channel1Frequency = SpiChannel.DefaultFrequency; - - return SpiChannel.Retrieve(SpiChannelNumber.Channel1, Channel1Frequency); - } - } - - #endregion - } + private SpiBus() { + // placeholder + } + + #region SPI Access + + /// + /// Gets or sets the channel 0 frequency in Hz. + /// + /// + /// The channel0 frequency. + /// + public Int32 Channel0Frequency { + get; set; + } + + /// + /// Gets the SPI bus on channel 1. + /// + /// + /// The channel0. + /// + public SpiChannel Channel0 { + get { + if(this.Channel0Frequency == 0) { + this.Channel0Frequency = SpiChannel.DefaultFrequency; + } + + return SpiChannel.Retrieve(SpiChannelNumber.Channel0, this.Channel0Frequency); + } + } + + /// + /// Gets or sets the channel 1 frequency in Hz + /// + /// + /// The channel1 frequency. + /// + public Int32 Channel1Frequency { + get; set; + } + + /// + /// Gets the SPI bus on channel 1. + /// + /// + /// The channel1. + /// + public SpiChannel Channel1 { + get { + if(this.Channel1Frequency == 0) { + this.Channel1Frequency = SpiChannel.DefaultFrequency; + } + + return SpiChannel.Retrieve(SpiChannelNumber.Channel1, this.Channel1Frequency); + } + } + + #endregion + } } diff --git a/Unosquare.RaspberryIO/Gpio/SpiChannel.cs b/Unosquare.RaspberryIO/Gpio/SpiChannel.cs index 8e5f90e..eadfdeb 100644 --- a/Unosquare.RaspberryIO/Gpio/SpiChannel.cs +++ b/Unosquare.RaspberryIO/Gpio/SpiChannel.cs @@ -1,154 +1,154 @@ -namespace Unosquare.RaspberryIO.Gpio -{ - using Native; - using Swan; - using System; - using System.Collections.Generic; - using System.Threading.Tasks; - +using Unosquare.RaspberryIO.Native; +using Unosquare.Swan; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Unosquare.RaspberryIO.Gpio { + /// + /// Provides access to using the SPI buses on the GPIO. + /// SPI is a bus that works like a ring shift register + /// The number of bytes pushed is equal to the number of bytes received. + /// + public sealed class SpiChannel { /// - /// Provides access to using the SPI buses on the GPIO. - /// SPI is a bus that works like a ring shift register - /// The number of bytes pushed is equal to the number of bytes received. + /// The minimum frequency of an SPI Channel /// - public sealed class SpiChannel - { - /// - /// The minimum frequency of an SPI Channel - /// - public const int MinFrequency = 500000; - - /// - /// The maximum frequency of an SPI channel - /// - public const int MaxFrequency = 32000000; - - /// - /// The default frequency of SPI channels - /// This is set to 8 Mhz wich is typical in modern hardware. - /// - public const int DefaultFrequency = 8000000; - - private static readonly object SyncRoot = new object(); - private static readonly Dictionary Buses = new Dictionary(); - private readonly object _syncLock = new object(); - - /// - /// Initializes a new instance of the class. - /// - /// The channel. - /// The frequency. - private SpiChannel(SpiChannelNumber channel, int frequency) - { - lock (SyncRoot) - { - Frequency = frequency.Clamp(MinFrequency, MaxFrequency); - Channel = (int)channel; - FileDescriptor = WiringPi.WiringPiSPISetup((int)channel, Frequency); - - if (FileDescriptor < 0) - { - HardwareException.Throw(nameof(SpiChannel), channel.ToString()); - } - } - } - - /// - /// Gets the standard initialization file descriptor. - /// anything negative means error. - /// - /// - /// The file descriptor. - /// - public int FileDescriptor { get; } - - /// - /// Gets the channel. - /// - public int Channel { get; } - - /// - /// Gets the frequency. - /// - public int Frequency { get; } - - /// - /// Sends data and simultaneously receives the data in the return buffer - /// - /// The buffer. - /// The read bytes from the ring-style bus - public byte[] SendReceive(byte[] buffer) - { - if (buffer == null || buffer.Length == 0) - return null; - - lock (_syncLock) - { - var spiBuffer = new byte[buffer.Length]; - Array.Copy(buffer, spiBuffer, buffer.Length); - - var result = WiringPi.WiringPiSPIDataRW(Channel, spiBuffer, spiBuffer.Length); - if (result < 0) HardwareException.Throw(nameof(SpiChannel), nameof(SendReceive)); - - return spiBuffer; - } - } - - /// - /// Sends data and simultaneously receives the data in the return buffer - /// - /// The buffer. - /// - /// The read bytes from the ring-style bus - /// - public Task SendReceiveAsync(byte[] buffer) => Task.Run(() => SendReceive(buffer)); - - /// - /// Writes the specified buffer the the underlying FileDescriptor. - /// Do not use this method if you expect data back. - /// This method is efficient if used in a fire-and-forget scenario - /// like sending data over to those long RGB LED strips - /// - /// The buffer. - public void Write(byte[] buffer) - { - lock (_syncLock) - { - var result = Standard.Write(FileDescriptor, buffer, buffer.Length); - - if (result < 0) - HardwareException.Throw(nameof(SpiChannel), nameof(Write)); - } - } - - /// - /// Writes the specified buffer the the underlying FileDescriptor. - /// Do not use this method if you expect data back. - /// This method is efficient if used in a fire-and-forget scenario - /// like sending data over to those long RGB LED strips - /// - /// The buffer. - /// The awaitable task - public Task WriteAsync(byte[] buffer) => Task.Run(() => { Write(buffer); }); - - /// - /// Retrieves the spi bus. If the bus channel is not registered it sets it up automatically. - /// If it had been previously registered, then the bus is simply returned. - /// - /// The channel. - /// The frequency. - /// The usable SPI channel - internal static SpiChannel Retrieve(SpiChannelNumber channel, int frequency) - { - lock (SyncRoot) - { - if (Buses.ContainsKey(channel)) - return Buses[channel]; - - var newBus = new SpiChannel(channel, frequency); - Buses[channel] = newBus; - return newBus; - } - } - } + public const Int32 MinFrequency = 500000; + + /// + /// The maximum frequency of an SPI channel + /// + public const Int32 MaxFrequency = 32000000; + + /// + /// The default frequency of SPI channels + /// This is set to 8 Mhz wich is typical in modern hardware. + /// + public const Int32 DefaultFrequency = 8000000; + + private static readonly Object SyncRoot = new Object(); + private static readonly Dictionary Buses = new Dictionary(); + private readonly Object _syncLock = new Object(); + + /// + /// Initializes a new instance of the class. + /// + /// The channel. + /// The frequency. + private SpiChannel(SpiChannelNumber channel, Int32 frequency) { + lock(SyncRoot) { + this.Frequency = frequency.Clamp(MinFrequency, MaxFrequency); + this.Channel = (Int32)channel; + this.FileDescriptor = WiringPi.WiringPiSPISetup((Int32)channel, this.Frequency); + + if(this.FileDescriptor < 0) { + HardwareException.Throw(nameof(SpiChannel), channel.ToString()); + } + } + } + + /// + /// Gets the standard initialization file descriptor. + /// anything negative means error. + /// + /// + /// The file descriptor. + /// + public Int32 FileDescriptor { + get; + } + + /// + /// Gets the channel. + /// + public Int32 Channel { + get; + } + + /// + /// Gets the frequency. + /// + public Int32 Frequency { + get; + } + + /// + /// Sends data and simultaneously receives the data in the return buffer + /// + /// The buffer. + /// The read bytes from the ring-style bus + public Byte[] SendReceive(Byte[] buffer) { + if(buffer == null || buffer.Length == 0) { + return null; + } + + lock(this._syncLock) { + Byte[] spiBuffer = new Byte[buffer.Length]; + Array.Copy(buffer, spiBuffer, buffer.Length); + + Int32 result = WiringPi.WiringPiSPIDataRW(this.Channel, spiBuffer, spiBuffer.Length); + if(result < 0) { + HardwareException.Throw(nameof(SpiChannel), nameof(SendReceive)); + } + + return spiBuffer; + } + } + + /// + /// Sends data and simultaneously receives the data in the return buffer + /// + /// The buffer. + /// + /// The read bytes from the ring-style bus + /// + public Task SendReceiveAsync(Byte[] buffer) => Task.Run(() => this.SendReceive(buffer)); + + /// + /// Writes the specified buffer the the underlying FileDescriptor. + /// Do not use this method if you expect data back. + /// This method is efficient if used in a fire-and-forget scenario + /// like sending data over to those long RGB LED strips + /// + /// The buffer. + public void Write(Byte[] buffer) { + lock(this._syncLock) { + Int32 result = Standard.Write(this.FileDescriptor, buffer, buffer.Length); + + if(result < 0) { + HardwareException.Throw(nameof(SpiChannel), nameof(Write)); + } + } + } + + /// + /// Writes the specified buffer the the underlying FileDescriptor. + /// Do not use this method if you expect data back. + /// This method is efficient if used in a fire-and-forget scenario + /// like sending data over to those long RGB LED strips + /// + /// The buffer. + /// The awaitable task + public Task WriteAsync(Byte[] buffer) => Task.Run(() => this.Write(buffer)); + + /// + /// Retrieves the spi bus. If the bus channel is not registered it sets it up automatically. + /// If it had been previously registered, then the bus is simply returned. + /// + /// The channel. + /// The frequency. + /// The usable SPI channel + internal static SpiChannel Retrieve(SpiChannelNumber channel, Int32 frequency) { + lock(SyncRoot) { + if(Buses.ContainsKey(channel)) { + return Buses[channel]; + } + + SpiChannel newBus = new SpiChannel(channel, frequency); + Buses[channel] = newBus; + return newBus; + } + } + } } diff --git a/Unosquare.RaspberryIO/Native/Delegates.cs b/Unosquare.RaspberryIO/Native/Delegates.cs index 6b0cbbd..c270a13 100644 --- a/Unosquare.RaspberryIO/Native/Delegates.cs +++ b/Unosquare.RaspberryIO/Native/Delegates.cs @@ -1,12 +1,11 @@ -namespace Unosquare.RaspberryIO.Native -{ - /// - /// A delegate defining a callback for an Interrupt Service Routine - /// - public delegate void InterruptServiceRoutineCallback(); - - /// - /// Defines the body of a thread worker - /// - public delegate void ThreadWorker(); +namespace Unosquare.RaspberryIO.Native { + /// + /// A delegate defining a callback for an Interrupt Service Routine + /// + public delegate void InterruptServiceRoutineCallback(); + + /// + /// Defines the body of a thread worker + /// + public delegate void ThreadWorker(); } diff --git a/Unosquare.RaspberryIO/Native/HardwareException.cs b/Unosquare.RaspberryIO/Native/HardwareException.cs index 3ac43b2..fbfda58 100644 --- a/Unosquare.RaspberryIO/Native/HardwareException.cs +++ b/Unosquare.RaspberryIO/Native/HardwareException.cs @@ -1,73 +1,73 @@ -namespace Unosquare.RaspberryIO.Native -{ - using Swan; - using System; - using System.Runtime.InteropServices; - +using Unosquare.Swan; +using System; +using System.Runtime.InteropServices; + +namespace Unosquare.RaspberryIO.Native { + /// + /// Represents a low-level exception, typically thrown when return codes from a + /// low-level operation is non-zero or in some cases when it is less than zero. + /// + /// + public class HardwareException : Exception { /// - /// Represents a low-level exception, typically thrown when return codes from a - /// low-level operation is non-zero or in some cases when it is less than zero. + /// Initializes a new instance of the class. /// - /// - public class HardwareException : Exception - { - /// - /// Initializes a new instance of the class. - /// - /// The error code. - /// The component. - public HardwareException(int errorCode, string component) - : base($"A hardware exception occurred. Error Code: {errorCode}") - { - ExtendedMessage = null; - - try - { - ExtendedMessage = Standard.Strerror(errorCode); - } - catch - { - // TODO: strerror not working great... - $"Could not retrieve native error description using {nameof(Standard.Strerror)}".Error(Pi.LoggerSource); - } - - ErrorCode = errorCode; - Component = component; - } - - /// - /// Gets the error code. - /// - /// - /// The error code. - /// - public int ErrorCode { get; } - - /// - /// Gets the component. - /// - /// - /// The component. - /// - public string Component { get; } - - /// - /// Gets the extended message (could be null). - /// - /// - /// The extended message. - /// - public string ExtendedMessage { get; } - - /// - /// Throws a new instance of a hardware error by retrieving the last error number (errno). - /// - /// Name of the class. - /// Name of the method. - /// When an error thrown by an API call occurs - public static void Throw(string className, string methodName) => throw new HardwareException(Marshal.GetLastWin32Error(), $"{className}.{methodName}"); - - /// - public override string ToString() => $"{GetType()}{(string.IsNullOrWhiteSpace(Component) ? string.Empty : $" on {Component}")}: ({ErrorCode}) - {Message}"; - } + /// The error code. + /// The component. + public HardwareException(Int32 errorCode, String component) + : base($"A hardware exception occurred. Error Code: {errorCode}") { + this.ExtendedMessage = null; + + try { + this.ExtendedMessage = Standard.Strerror(errorCode); + } catch { + // TODO: strerror not working great... + $"Could not retrieve native error description using {nameof(Standard.Strerror)}".Error(Pi.LoggerSource); + } + + this.ErrorCode = errorCode; + this.Component = component; + } + + /// + /// Gets the error code. + /// + /// + /// The error code. + /// + public Int32 ErrorCode { + get; + } + + /// + /// Gets the component. + /// + /// + /// The component. + /// + public String Component { + get; + } + + /// + /// Gets the extended message (could be null). + /// + /// + /// The extended message. + /// + public String ExtendedMessage { + get; + } + + /// + /// Throws a new instance of a hardware error by retrieving the last error number (errno). + /// + /// Name of the class. + /// Name of the method. + /// When an error thrown by an API call occurs + public static void Throw(String className, String methodName) => throw new HardwareException(Marshal.GetLastWin32Error(), $"{className}.{methodName}"); + + /// + public override String ToString() => $"{this.GetType()}{(String.IsNullOrWhiteSpace(this.Component) ? String.Empty : $" on {this.Component}")}: ({this.ErrorCode}) - {this.Message}"; + } } diff --git a/Unosquare.RaspberryIO/Native/HighResolutionTimer.cs b/Unosquare.RaspberryIO/Native/HighResolutionTimer.cs index dd8e624..d96257c 100644 --- a/Unosquare.RaspberryIO/Native/HighResolutionTimer.cs +++ b/Unosquare.RaspberryIO/Native/HighResolutionTimer.cs @@ -1,32 +1,30 @@ -namespace Unosquare.RaspberryIO.Native -{ - using System; - using System.Diagnostics; - +using System; +using System.Diagnostics; + +namespace Unosquare.RaspberryIO.Native { + /// + /// Provides access to a high- esolution, time measuring device. + /// + /// + public class HighResolutionTimer : Stopwatch { /// - /// Provides access to a high- esolution, time measuring device. + /// Initializes a new instance of the class. /// - /// - public class HighResolutionTimer : Stopwatch - { - /// - /// Initializes a new instance of the class. - /// - /// High-resolution timer not available - public HighResolutionTimer() - { - if (!IsHighResolution) - throw new NotSupportedException("High-resolution timer not available"); - } - - /// - /// Gets the numer of microseconds per timer tick. - /// - public static double MicrosecondsPerTick { get; } = 1000000d / Frequency; - - /// - /// Gets the elapsed microseconds. - /// - public long ElapsedMicroseconds => (long)(ElapsedTicks * MicrosecondsPerTick); - } + /// High-resolution timer not available + public HighResolutionTimer() { + if(!IsHighResolution) { + throw new NotSupportedException("High-resolution timer not available"); + } + } + + /// + /// Gets the numer of microseconds per timer tick. + /// + public static Double MicrosecondsPerTick { get; } = 1000000d / Frequency; + + /// + /// Gets the elapsed microseconds. + /// + public Int64 ElapsedMicroseconds => (Int64)(this.ElapsedTicks * MicrosecondsPerTick); + } } diff --git a/Unosquare.RaspberryIO/Native/Standard.cs b/Unosquare.RaspberryIO/Native/Standard.cs index 4ce2ac1..b4b6e79 100644 --- a/Unosquare.RaspberryIO/Native/Standard.cs +++ b/Unosquare.RaspberryIO/Native/Standard.cs @@ -1,84 +1,80 @@ -namespace Unosquare.RaspberryIO.Native -{ - using Swan; - using System; - using System.Runtime.InteropServices; - using System.Text; - +using Unosquare.Swan; +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace Unosquare.RaspberryIO.Native { + /// + /// Provides standard libc calls using platform-invoke + /// + internal static class Standard { + internal const String LibCLibrary = "libc"; + + #region LibC Calls + /// - /// Provides standard libc calls using platform-invoke + /// Strerrors the specified error. /// - internal static class Standard - { - internal const string LibCLibrary = "libc"; - - #region LibC Calls - - /// - /// Strerrors the specified error. - /// - /// The error. - /// - public static string Strerror(int error) - { - if (!Runtime.IsUsingMonoRuntime) return StrError(error); - - try - { - var buffer = new StringBuilder(256); - var result = Strerror(error, buffer, (ulong)buffer.Capacity); - return (result != -1) ? buffer.ToString() : null; - } - catch (EntryPointNotFoundException) - { - return null; - } - } - - /// - /// Changes file permissions on a Unix file system - /// - /// The filename. - /// The mode. - /// The result - [DllImport(LibCLibrary, EntryPoint = "chmod", SetLastError = true)] - public static extern int Chmod(string filename, uint mode); - - /// - /// Converts a string to a 32 bit integer. Use endpointer as IntPtr.Zero - /// - /// The number string. - /// The end pointer. - /// The number base. - /// The result - [DllImport(LibCLibrary, EntryPoint = "strtol", SetLastError = true)] - public static extern int StringToInteger(string numberString, IntPtr endPointer, int numberBase); - - /// - /// The write() function attempts to write nbytes from buffer to the file associated with handle. On text files, it expands each LF to a CR/LF. - /// The function returns the number of bytes written to the file. A return value of -1 indicates an error, with errno set appropriately. - /// - /// The fd. - /// The buffer. - /// The count. - /// The result - [DllImport(LibCLibrary, EntryPoint = "write", SetLastError = true)] - public static extern int Write(int fd, byte[] buffer, int count); - - /// - /// Fills in the structure with information about the system. - /// - /// The name. - /// The result - [DllImport(LibCLibrary, EntryPoint = "uname", SetLastError = true)] - public static extern int Uname(out SystemName name); - - [DllImport(LibCLibrary, EntryPoint = "strerror", SetLastError = true)] - private static extern string StrError(int errnum); - - [DllImport("MonoPosixHelper", EntryPoint = "Mono_Posix_Syscall_strerror_r", SetLastError = true)] - private static extern int Strerror(int error, [Out] StringBuilder buffer, ulong length); - - #endregion - } + /// The error. + /// + public static String Strerror(Int32 error) { + if(!Runtime.IsUsingMonoRuntime) { + return StrError(error); + } + + try { + StringBuilder buffer = new StringBuilder(256); + Int32 result = Strerror(error, buffer, (UInt64)buffer.Capacity); + return (result != -1) ? buffer.ToString() : null; + } catch(EntryPointNotFoundException) { + return null; + } + } + + /// + /// Changes file permissions on a Unix file system + /// + /// The filename. + /// The mode. + /// The result + [DllImport(LibCLibrary, EntryPoint = "chmod", SetLastError = true)] + public static extern Int32 Chmod(String filename, UInt32 mode); + + /// + /// Converts a string to a 32 bit integer. Use endpointer as IntPtr.Zero + /// + /// The number string. + /// The end pointer. + /// The number base. + /// The result + [DllImport(LibCLibrary, EntryPoint = "strtol", SetLastError = true)] + public static extern Int32 StringToInteger(String numberString, IntPtr endPointer, Int32 numberBase); + + /// + /// The write() function attempts to write nbytes from buffer to the file associated with handle. On text files, it expands each LF to a CR/LF. + /// The function returns the number of bytes written to the file. A return value of -1 indicates an error, with errno set appropriately. + /// + /// The fd. + /// The buffer. + /// The count. + /// The result + [DllImport(LibCLibrary, EntryPoint = "write", SetLastError = true)] + public static extern Int32 Write(Int32 fd, Byte[] buffer, Int32 count); + + /// + /// Fills in the structure with information about the system. + /// + /// The name. + /// The result + [DllImport(LibCLibrary, EntryPoint = "uname", SetLastError = true)] + public static extern Int32 Uname(out SystemName name); + + [DllImport(LibCLibrary, EntryPoint = "strerror", SetLastError = true)] + private static extern String StrError(Int32 errnum); + + [DllImport("MonoPosixHelper", EntryPoint = "Mono_Posix_Syscall_strerror_r", SetLastError = true)] + private static extern Int32 Strerror(Int32 error, [Out] StringBuilder buffer, UInt64 length); + + #endregion + } } \ No newline at end of file diff --git a/Unosquare.RaspberryIO/Native/SystemName.cs b/Unosquare.RaspberryIO/Native/SystemName.cs index 424fe8e..3b9e69f 100644 --- a/Unosquare.RaspberryIO/Native/SystemName.cs +++ b/Unosquare.RaspberryIO/Native/SystemName.cs @@ -1,47 +1,46 @@ -namespace Unosquare.RaspberryIO.Native -{ - using System.Runtime.InteropServices; - +using System; +using System.Runtime.InteropServices; + +namespace Unosquare.RaspberryIO.Native { + /// + /// OS uname structure + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + internal struct SystemName { /// - /// OS uname structure + /// System name /// - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] - internal struct SystemName - { - /// - /// System name - /// - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)] - public string SysName; - - /// - /// Node name - /// - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)] - public string NodeName; - - /// - /// Release level - /// - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)] - public string Release; - - /// - /// Version level - /// - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)] - public string Version; - - /// - /// Hardware level - /// - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)] - public string Machine; - - /// - /// Domain name - /// - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)] - public string DomainName; - } + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)] + public String SysName; + + /// + /// Node name + /// + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)] + public String NodeName; + + /// + /// Release level + /// + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)] + public String Release; + + /// + /// Version level + /// + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)] + public String Version; + + /// + /// Hardware level + /// + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)] + public String Machine; + + /// + /// Domain name + /// + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)] + public String DomainName; + } } \ No newline at end of file diff --git a/Unosquare.RaspberryIO/Native/ThreadLockKey.cs b/Unosquare.RaspberryIO/Native/ThreadLockKey.cs index 52ed4fa..6fcc702 100644 --- a/Unosquare.RaspberryIO/Native/ThreadLockKey.cs +++ b/Unosquare.RaspberryIO/Native/ThreadLockKey.cs @@ -1,28 +1,26 @@ -namespace Unosquare.RaspberryIO.Native -{ +namespace Unosquare.RaspberryIO.Native { + /// + /// Defines the different threading locking keys + /// + public enum ThreadLockKey { /// - /// Defines the different threading locking keys + /// The lock 0 /// - public enum ThreadLockKey - { - /// - /// The lock 0 - /// - Lock0 = 0, - - /// - /// The lock 1 - /// - Lock1 = 1, - - /// - /// The lock 2 - /// - Lock2 = 2, - - /// - /// The lock 3 - /// - Lock3 = 3, - } + Lock0 = 0, + + /// + /// The lock 1 + /// + Lock1 = 1, + + /// + /// The lock 2 + /// + Lock2 = 2, + + /// + /// The lock 3 + /// + Lock3 = 3, + } } diff --git a/Unosquare.RaspberryIO/Native/Timing.cs b/Unosquare.RaspberryIO/Native/Timing.cs index b0c91fb..e36df61 100644 --- a/Unosquare.RaspberryIO/Native/Timing.cs +++ b/Unosquare.RaspberryIO/Native/Timing.cs @@ -1,108 +1,108 @@ -namespace Unosquare.RaspberryIO.Native -{ - using Swan; - using Swan.Abstractions; - using System; - +using Unosquare.Swan; +using Unosquare.Swan.Abstractions; +using System; + +namespace Unosquare.RaspberryIO.Native { + /// + /// Provides access to timing and threading properties and methods + /// + public class Timing : SingletonBase { /// - /// Provides access to timing and threading properties and methods + /// Prevents a default instance of the class from being created. /// - public class Timing : SingletonBase - { - /// - /// Prevents a default instance of the class from being created. - /// - /// Could not initialize the GPIO controller - private Timing() - { - // placeholder - } - - /// - /// This returns a number representing the number of milliseconds since your program - /// initialized the GPIO controller. - /// It returns an unsigned 32-bit number which wraps after 49 days. - /// - /// - /// The milliseconds since setup. - /// - public uint MillisecondsSinceSetup => WiringPi.Millis(); - - /// - /// This returns a number representing the number of microseconds since your - /// program initialized the GPIO controller - /// It returns an unsigned 32-bit number which wraps after approximately 71 minutes. - /// - /// - /// The microseconds since setup. - /// - public uint MicrosecondsSinceSetup => WiringPi.Micros(); - - /// - /// This causes program execution to pause for at least howLong milliseconds. - /// Due to the multi-tasking nature of Linux it could be longer. - /// Note that the maximum delay is an unsigned 32-bit integer or approximately 49 days. - /// - /// The value. - public static void SleepMilliseconds(uint value) => WiringPi.Delay(value); - - /// - /// This causes program execution to pause for at least howLong microseconds. - /// Due to the multi-tasking nature of Linux it could be longer. - /// Note that the maximum delay is an unsigned 32-bit integer microseconds or approximately 71 minutes. - /// Delays under 100 microseconds are timed using a hard-coded loop continually polling the system time, - /// Delays over 100 microseconds are done using the system nanosleep() function – - /// You may need to consider the implications of very short delays on the overall performance of the system, - /// especially if using threads. - /// - /// The value. - public void SleepMicroseconds(uint value) => WiringPi.DelayMicroseconds(value); - - /// - /// This attempts to shift your program (or thread in a multi-threaded program) to a higher priority and - /// enables a real-time scheduling. The priority parameter should be from 0 (the default) to 99 (the maximum). - /// This won’t make your program go any faster, but it will give it a bigger slice of time when other programs - /// are running. The priority parameter works relative to others – so you can make one program priority 1 and - /// another priority 2 and it will have the same effect as setting one to 10 and the other to 90 - /// (as long as no other programs are running with elevated priorities) - /// - /// The priority. - public void SetThreadPriority(int priority) - { - priority = priority.Clamp(0, 99); - var result = WiringPi.PiHiPri(priority); - if (result < 0) HardwareException.Throw(nameof(Timing), nameof(SetThreadPriority)); - } - - /// - /// This is really nothing more than a simplified interface to the Posix threads mechanism that Linux supports. - /// See the manual pages on Posix threads (man pthread) if you need more control over them. - /// - /// The worker. - /// worker - public void CreateThread(ThreadWorker worker) - { - if (worker == null) - throw new ArgumentNullException(nameof(worker)); - - var result = WiringPi.PiThreadCreate(worker); - if (result != 0) HardwareException.Throw(nameof(Timing), nameof(CreateThread)); - } - - /// - /// These allow you to synchronize variable updates from your main program to any threads running in your program. - /// keyNum is a number from 0 to 3 and represents a “key”. When another process tries to lock the same key, - /// it will be stalled until the first process has unlocked the same key. - /// - /// The key. - public void Lock(ThreadLockKey key) => WiringPi.PiLock((int)key); - - /// - /// These allow you to synchronize variable updates from your main program to any threads running in your program. - /// keyNum is a number from 0 to 3 and represents a “key”. When another process tries to lock the same key, - /// it will be stalled until the first process has unlocked the same key. - /// - /// The key. - public void Unlock(ThreadLockKey key) => WiringPi.PiUnlock((int)key); - } + /// Could not initialize the GPIO controller + private Timing() { + // placeholder + } + + /// + /// This returns a number representing the number of milliseconds since your program + /// initialized the GPIO controller. + /// It returns an unsigned 32-bit number which wraps after 49 days. + /// + /// + /// The milliseconds since setup. + /// + public UInt32 MillisecondsSinceSetup => WiringPi.Millis(); + + /// + /// This returns a number representing the number of microseconds since your + /// program initialized the GPIO controller + /// It returns an unsigned 32-bit number which wraps after approximately 71 minutes. + /// + /// + /// The microseconds since setup. + /// + public UInt32 MicrosecondsSinceSetup => WiringPi.Micros(); + + /// + /// This causes program execution to pause for at least howLong milliseconds. + /// Due to the multi-tasking nature of Linux it could be longer. + /// Note that the maximum delay is an unsigned 32-bit integer or approximately 49 days. + /// + /// The value. + public static void SleepMilliseconds(UInt32 value) => WiringPi.Delay(value); + + /// + /// This causes program execution to pause for at least howLong microseconds. + /// Due to the multi-tasking nature of Linux it could be longer. + /// Note that the maximum delay is an unsigned 32-bit integer microseconds or approximately 71 minutes. + /// Delays under 100 microseconds are timed using a hard-coded loop continually polling the system time, + /// Delays over 100 microseconds are done using the system nanosleep() function – + /// You may need to consider the implications of very short delays on the overall performance of the system, + /// especially if using threads. + /// + /// The value. + public void SleepMicroseconds(UInt32 value) => WiringPi.DelayMicroseconds(value); + + /// + /// This attempts to shift your program (or thread in a multi-threaded program) to a higher priority and + /// enables a real-time scheduling. The priority parameter should be from 0 (the default) to 99 (the maximum). + /// This won’t make your program go any faster, but it will give it a bigger slice of time when other programs + /// are running. The priority parameter works relative to others – so you can make one program priority 1 and + /// another priority 2 and it will have the same effect as setting one to 10 and the other to 90 + /// (as long as no other programs are running with elevated priorities) + /// + /// The priority. + public void SetThreadPriority(Int32 priority) { + priority = priority.Clamp(0, 99); + Int32 result = WiringPi.PiHiPri(priority); + if(result < 0) { + HardwareException.Throw(nameof(Timing), nameof(SetThreadPriority)); + } + } + + /// + /// This is really nothing more than a simplified interface to the Posix threads mechanism that Linux supports. + /// See the manual pages on Posix threads (man pthread) if you need more control over them. + /// + /// The worker. + /// worker + public void CreateThread(ThreadWorker worker) { + if(worker == null) { + throw new ArgumentNullException(nameof(worker)); + } + + Int32 result = WiringPi.PiThreadCreate(worker); + if(result != 0) { + HardwareException.Throw(nameof(Timing), nameof(CreateThread)); + } + } + + /// + /// These allow you to synchronize variable updates from your main program to any threads running in your program. + /// keyNum is a number from 0 to 3 and represents a “key”. When another process tries to lock the same key, + /// it will be stalled until the first process has unlocked the same key. + /// + /// The key. + public void Lock(ThreadLockKey key) => WiringPi.PiLock((Int32)key); + + /// + /// These allow you to synchronize variable updates from your main program to any threads running in your program. + /// keyNum is a number from 0 to 3 and represents a “key”. When another process tries to lock the same key, + /// it will be stalled until the first process has unlocked the same key. + /// + /// The key. + public void Unlock(ThreadLockKey key) => WiringPi.PiUnlock((Int32)key); + } } diff --git a/Unosquare.RaspberryIO/Native/WiringPi.I2C.cs b/Unosquare.RaspberryIO/Native/WiringPi.I2C.cs index b0775b9..581cd14 100644 --- a/Unosquare.RaspberryIO/Native/WiringPi.I2C.cs +++ b/Unosquare.RaspberryIO/Native/WiringPi.I2C.cs @@ -1,79 +1,78 @@ -namespace Unosquare.RaspberryIO.Native -{ - using System.Runtime.InteropServices; - - public partial class WiringPi - { - #region WiringPi - I2C Library Calls - - /// - /// Simple device read. Some devices present data when you read them without having to do any register transactions. - /// - /// The fd. - /// The result - [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CRead", SetLastError = true)] - public static extern int WiringPiI2CRead(int fd); - - /// - /// These read an 8-bit value from the device register indicated. - /// - /// The fd. - /// The reg. - /// The result - [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CReadReg8", SetLastError = true)] - public static extern int WiringPiI2CReadReg8(int fd, int reg); - - /// - /// These read a 16-bit value from the device register indicated. - /// - /// The fd. - /// The reg. - /// The result - [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CReadReg16", SetLastError = true)] - public static extern int WiringPiI2CReadReg16(int fd, int reg); - - /// - /// Simple device write. Some devices accept data this way without needing to access any internal registers. - /// - /// The fd. - /// The data. - /// The result - [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CWrite", SetLastError = true)] - public static extern int WiringPiI2CWrite(int fd, int data); - - /// - /// These write an 8-bit data value into the device register indicated. - /// - /// The fd. - /// The reg. - /// The data. - /// The result - [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CWriteReg8", SetLastError = true)] - public static extern int WiringPiI2CWriteReg8(int fd, int reg, int data); - - /// - /// These write a 16-bit data value into the device register indicated. - /// - /// The fd. - /// The reg. - /// The data. - /// The result - [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CWriteReg16", SetLastError = true)] - public static extern int WiringPiI2CWriteReg16(int fd, int reg, int data); - - /// - /// This initialises the I2C system with your given device identifier. - /// The ID is the I2C number of the device and you can use the i2cdetect program to find this out. wiringPiI2CSetup() - /// will work out which revision Raspberry Pi you have and open the appropriate device in /dev. - /// The return value is the standard Linux filehandle, or -1 if any error – in which case, you can consult errno as usual. - /// E.g. the popular MCP23017 GPIO expander is usually device Id 0x20, so this is the number you would pass into wiringPiI2CSetup(). - /// - /// The dev identifier. - /// The result - [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CSetup", SetLastError = true)] - public static extern int WiringPiI2CSetup(int devId); - - #endregion - - } +using System; +using System.Runtime.InteropServices; + +namespace Unosquare.RaspberryIO.Native { + public partial class WiringPi { + #region WiringPi - I2C Library Calls + + /// + /// Simple device read. Some devices present data when you read them without having to do any register transactions. + /// + /// The fd. + /// The result + [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CRead", SetLastError = true)] + public static extern Int32 WiringPiI2CRead(Int32 fd); + + /// + /// These read an 8-bit value from the device register indicated. + /// + /// The fd. + /// The reg. + /// The result + [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CReadReg8", SetLastError = true)] + public static extern Int32 WiringPiI2CReadReg8(Int32 fd, Int32 reg); + + /// + /// These read a 16-bit value from the device register indicated. + /// + /// The fd. + /// The reg. + /// The result + [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CReadReg16", SetLastError = true)] + public static extern Int32 WiringPiI2CReadReg16(Int32 fd, Int32 reg); + + /// + /// Simple device write. Some devices accept data this way without needing to access any internal registers. + /// + /// The fd. + /// The data. + /// The result + [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CWrite", SetLastError = true)] + public static extern Int32 WiringPiI2CWrite(Int32 fd, Int32 data); + + /// + /// These write an 8-bit data value into the device register indicated. + /// + /// The fd. + /// The reg. + /// The data. + /// The result + [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CWriteReg8", SetLastError = true)] + public static extern Int32 WiringPiI2CWriteReg8(Int32 fd, Int32 reg, Int32 data); + + /// + /// These write a 16-bit data value into the device register indicated. + /// + /// The fd. + /// The reg. + /// The data. + /// The result + [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CWriteReg16", SetLastError = true)] + public static extern Int32 WiringPiI2CWriteReg16(Int32 fd, Int32 reg, Int32 data); + + /// + /// This initialises the I2C system with your given device identifier. + /// The ID is the I2C number of the device and you can use the i2cdetect program to find this out. wiringPiI2CSetup() + /// will work out which revision Raspberry Pi you have and open the appropriate device in /dev. + /// The return value is the standard Linux filehandle, or -1 if any error – in which case, you can consult errno as usual. + /// E.g. the popular MCP23017 GPIO expander is usually device Id 0x20, so this is the number you would pass into wiringPiI2CSetup(). + /// + /// The dev identifier. + /// The result + [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CSetup", SetLastError = true)] + public static extern Int32 WiringPiI2CSetup(Int32 devId); + + #endregion + + } } diff --git a/Unosquare.RaspberryIO/Native/WiringPi.SerialPort.cs b/Unosquare.RaspberryIO/Native/WiringPi.SerialPort.cs index ea54bd3..7b96005 100644 --- a/Unosquare.RaspberryIO/Native/WiringPi.SerialPort.cs +++ b/Unosquare.RaspberryIO/Native/WiringPi.SerialPort.cs @@ -1,73 +1,72 @@ -namespace Unosquare.RaspberryIO.Native -{ - using System.Runtime.InteropServices; - - public partial class WiringPi - { - #region WiringPi - Serial Port - - /// - /// This opens and initialises the serial device and sets the baud rate. It sets the port into “raw” mode (character at a time and no translations), - /// and sets the read timeout to 10 seconds. The return value is the file descriptor or -1 for any error, in which case errno will be set as appropriate. - /// The wiringSerial library is intended to provide simplified control – suitable for most applications, however if you need advanced control - /// – e.g. parity control, modem control lines (via a USB adapter, there are none on the Pi’s on-board UART!) and so on, - /// then you need to do some of this the old fashioned way. - /// - /// The device. - /// The baud. - /// The result - [DllImport(WiringPiLibrary, EntryPoint = "serialOpen", SetLastError = true)] - public static extern int SerialOpen(string device, int baud); - - /// - /// Closes the device identified by the file descriptor given. - /// - /// The fd. - /// The result - [DllImport(WiringPiLibrary, EntryPoint = "serialClose", SetLastError = true)] - public static extern int SerialClose(int fd); - - /// - /// Sends the single byte to the serial device identified by the given file descriptor. - /// - /// The fd. - /// The c. - [DllImport(WiringPiLibrary, EntryPoint = "serialPutchar", SetLastError = true)] - public static extern void SerialPutchar(int fd, byte c); - - /// - /// Sends the nul-terminated string to the serial device identified by the given file descriptor. - /// - /// The fd. - /// The s. - [DllImport(WiringPiLibrary, EntryPoint = "serialPuts", SetLastError = true)] - public static extern void SerialPuts(int fd, string s); - - /// - /// Returns the number of characters available for reading, or -1 for any error condition, - /// in which case errno will be set appropriately. - /// - /// The fd. - /// The result - [DllImport(WiringPiLibrary, EntryPoint = "serialDataAvail", SetLastError = true)] - public static extern int SerialDataAvail(int fd); - - /// - /// Returns the next character available on the serial device. - /// This call will block for up to 10 seconds if no data is available (when it will return -1) - /// - /// The fd. - /// The result - [DllImport(WiringPiLibrary, EntryPoint = "serialGetchar", SetLastError = true)] - public static extern int SerialGetchar(int fd); - - /// - /// This discards all data received, or waiting to be send down the given device. - /// - /// The fd. - [DllImport(WiringPiLibrary, EntryPoint = "serialFlush", SetLastError = true)] - public static extern void SerialFlush(int fd); - - #endregion - } +using System; +using System.Runtime.InteropServices; + +namespace Unosquare.RaspberryIO.Native { + public partial class WiringPi { + #region WiringPi - Serial Port + + /// + /// This opens and initialises the serial device and sets the baud rate. It sets the port into “raw” mode (character at a time and no translations), + /// and sets the read timeout to 10 seconds. The return value is the file descriptor or -1 for any error, in which case errno will be set as appropriate. + /// The wiringSerial library is intended to provide simplified control – suitable for most applications, however if you need advanced control + /// – e.g. parity control, modem control lines (via a USB adapter, there are none on the Pi’s on-board UART!) and so on, + /// then you need to do some of this the old fashioned way. + /// + /// The device. + /// The baud. + /// The result + [DllImport(WiringPiLibrary, EntryPoint = "serialOpen", SetLastError = true)] + public static extern Int32 SerialOpen(String device, Int32 baud); + + /// + /// Closes the device identified by the file descriptor given. + /// + /// The fd. + /// The result + [DllImport(WiringPiLibrary, EntryPoint = "serialClose", SetLastError = true)] + public static extern Int32 SerialClose(Int32 fd); + + /// + /// Sends the single byte to the serial device identified by the given file descriptor. + /// + /// The fd. + /// The c. + [DllImport(WiringPiLibrary, EntryPoint = "serialPutchar", SetLastError = true)] + public static extern void SerialPutchar(Int32 fd, Byte c); + + /// + /// Sends the nul-terminated string to the serial device identified by the given file descriptor. + /// + /// The fd. + /// The s. + [DllImport(WiringPiLibrary, EntryPoint = "serialPuts", SetLastError = true)] + public static extern void SerialPuts(Int32 fd, String s); + + /// + /// Returns the number of characters available for reading, or -1 for any error condition, + /// in which case errno will be set appropriately. + /// + /// The fd. + /// The result + [DllImport(WiringPiLibrary, EntryPoint = "serialDataAvail", SetLastError = true)] + public static extern Int32 SerialDataAvail(Int32 fd); + + /// + /// Returns the next character available on the serial device. + /// This call will block for up to 10 seconds if no data is available (when it will return -1) + /// + /// The fd. + /// The result + [DllImport(WiringPiLibrary, EntryPoint = "serialGetchar", SetLastError = true)] + public static extern Int32 SerialGetchar(Int32 fd); + + /// + /// This discards all data received, or waiting to be send down the given device. + /// + /// The fd. + [DllImport(WiringPiLibrary, EntryPoint = "serialFlush", SetLastError = true)] + public static extern void SerialFlush(Int32 fd); + + #endregion + } } \ No newline at end of file diff --git a/Unosquare.RaspberryIO/Native/WiringPi.Shift.cs b/Unosquare.RaspberryIO/Native/WiringPi.Shift.cs index dfab07f..0808f39 100644 --- a/Unosquare.RaspberryIO/Native/WiringPi.Shift.cs +++ b/Unosquare.RaspberryIO/Native/WiringPi.Shift.cs @@ -1,36 +1,35 @@ -namespace Unosquare.RaspberryIO.Native -{ - using System.Runtime.InteropServices; - - public partial class WiringPi - { - #region WiringPi - Shift Library - - /// - /// This shifts an 8-bit data value in with the data appearing on the dPin and the clock being sent out on the cPin. - /// Order is either LSBFIRST or MSBFIRST. The data is sampled after the cPin goes high. - /// (So cPin high, sample data, cPin low, repeat for 8 bits) The 8-bit value is returned by the function. - /// - /// The d pin. - /// The c pin. - /// The order. - /// The result - [DllImport(WiringPiLibrary, EntryPoint = "shiftIn", SetLastError = true)] - public static extern byte ShiftIn(byte dPin, byte cPin, byte order); - - /// - /// The shifts an 8-bit data value val out with the data being sent out on dPin and the clock being sent out on the cPin. - /// order is as above. Data is clocked out on the rising or falling edge – ie. dPin is set, then cPin is taken high then low - /// – repeated for the 8 bits. - /// - /// The d pin. - /// The c pin. - /// The order. - /// The value. - [DllImport(WiringPiLibrary, EntryPoint = "shiftOut", SetLastError = true)] - public static extern void ShiftOut(byte dPin, byte cPin, byte order, byte val); - - #endregion - - } +using System; +using System.Runtime.InteropServices; + +namespace Unosquare.RaspberryIO.Native { + public partial class WiringPi { + #region WiringPi - Shift Library + + /// + /// This shifts an 8-bit data value in with the data appearing on the dPin and the clock being sent out on the cPin. + /// Order is either LSBFIRST or MSBFIRST. The data is sampled after the cPin goes high. + /// (So cPin high, sample data, cPin low, repeat for 8 bits) The 8-bit value is returned by the function. + /// + /// The d pin. + /// The c pin. + /// The order. + /// The result + [DllImport(WiringPiLibrary, EntryPoint = "shiftIn", SetLastError = true)] + public static extern Byte ShiftIn(Byte dPin, Byte cPin, Byte order); + + /// + /// The shifts an 8-bit data value val out with the data being sent out on dPin and the clock being sent out on the cPin. + /// order is as above. Data is clocked out on the rising or falling edge – ie. dPin is set, then cPin is taken high then low + /// – repeated for the 8 bits. + /// + /// The d pin. + /// The c pin. + /// The order. + /// The value. + [DllImport(WiringPiLibrary, EntryPoint = "shiftOut", SetLastError = true)] + public static extern void ShiftOut(Byte dPin, Byte cPin, Byte order, Byte val); + + #endregion + + } } diff --git a/Unosquare.RaspberryIO/Native/WiringPi.SoftPwm.cs b/Unosquare.RaspberryIO/Native/WiringPi.SoftPwm.cs index 30a75b5..19e245b 100644 --- a/Unosquare.RaspberryIO/Native/WiringPi.SoftPwm.cs +++ b/Unosquare.RaspberryIO/Native/WiringPi.SoftPwm.cs @@ -1,64 +1,63 @@ -namespace Unosquare.RaspberryIO.Native -{ - using System.Runtime.InteropServices; - - public partial class WiringPi - { - #region WiringPi - Soft PWM (https://github.com/WiringPi/WiringPi/blob/master/wiringPi/softPwm.h) - - /// - /// This creates a software controlled PWM pin. You can use any GPIO pin and the pin numbering will be that of the wiringPiSetup() - /// function you used. Use 100 for the pwmRange, then the value can be anything from 0 (off) to 100 (fully on) for the given pin. - /// The return value is 0 for success. Anything else and you should check the global errno variable to see what went wrong. - /// - /// The pin. - /// The initial value. - /// The PWM range. - /// The result - [DllImport(WiringPiLibrary, EntryPoint = "softPwmCreate", SetLastError = true)] - public static extern int SoftPwmCreate(int pin, int initialValue, int pwmRange); - - /// - /// This updates the PWM value on the given pin. The value is checked to be in-range and pins that haven’t previously - /// been initialized via softPwmCreate will be silently ignored. - /// - /// The pin. - /// The value. - [DllImport(WiringPiLibrary, EntryPoint = "softPwmWrite", SetLastError = true)] - public static extern void SoftPwmWrite(int pin, int value); - - /// - /// This function is undocumented - /// - /// The pin. - [DllImport(WiringPiLibrary, EntryPoint = "softPwmStop", SetLastError = true)] - public static extern void SoftPwmStop(int pin); - - /// - /// This creates a software controlled tone pin. You can use any GPIO pin and the pin numbering will be that of the wiringPiSetup() function you used. - /// The return value is 0 for success. Anything else and you should check the global errno variable to see what went wrong. - /// - /// The pin. - /// The result - [DllImport(WiringPiLibrary, EntryPoint = "softToneCreate", SetLastError = true)] - public static extern int SoftToneCreate(int pin); - - /// - /// This function is undocumented - /// - /// The pin. - [DllImport(WiringPiLibrary, EntryPoint = "softToneStop", SetLastError = true)] - public static extern void SoftToneStop(int pin); - - /// - /// This updates the tone frequency value on the given pin. The tone will be played until you set the frequency to 0. - /// - /// The pin. - /// The freq. - [DllImport(WiringPiLibrary, EntryPoint = "softToneWrite", SetLastError = true)] - public static extern void SoftToneWrite(int pin, int freq); - - #endregion - - } +using System; +using System.Runtime.InteropServices; + +namespace Unosquare.RaspberryIO.Native { + public partial class WiringPi { + #region WiringPi - Soft PWM (https://github.com/WiringPi/WiringPi/blob/master/wiringPi/softPwm.h) + + /// + /// This creates a software controlled PWM pin. You can use any GPIO pin and the pin numbering will be that of the wiringPiSetup() + /// function you used. Use 100 for the pwmRange, then the value can be anything from 0 (off) to 100 (fully on) for the given pin. + /// The return value is 0 for success. Anything else and you should check the global errno variable to see what went wrong. + /// + /// The pin. + /// The initial value. + /// The PWM range. + /// The result + [DllImport(WiringPiLibrary, EntryPoint = "softPwmCreate", SetLastError = true)] + public static extern Int32 SoftPwmCreate(Int32 pin, Int32 initialValue, Int32 pwmRange); + + /// + /// This updates the PWM value on the given pin. The value is checked to be in-range and pins that haven’t previously + /// been initialized via softPwmCreate will be silently ignored. + /// + /// The pin. + /// The value. + [DllImport(WiringPiLibrary, EntryPoint = "softPwmWrite", SetLastError = true)] + public static extern void SoftPwmWrite(Int32 pin, Int32 value); + + /// + /// This function is undocumented + /// + /// The pin. + [DllImport(WiringPiLibrary, EntryPoint = "softPwmStop", SetLastError = true)] + public static extern void SoftPwmStop(Int32 pin); + + /// + /// This creates a software controlled tone pin. You can use any GPIO pin and the pin numbering will be that of the wiringPiSetup() function you used. + /// The return value is 0 for success. Anything else and you should check the global errno variable to see what went wrong. + /// + /// The pin. + /// The result + [DllImport(WiringPiLibrary, EntryPoint = "softToneCreate", SetLastError = true)] + public static extern Int32 SoftToneCreate(Int32 pin); + + /// + /// This function is undocumented + /// + /// The pin. + [DllImport(WiringPiLibrary, EntryPoint = "softToneStop", SetLastError = true)] + public static extern void SoftToneStop(Int32 pin); + + /// + /// This updates the tone frequency value on the given pin. The tone will be played until you set the frequency to 0. + /// + /// The pin. + /// The freq. + [DllImport(WiringPiLibrary, EntryPoint = "softToneWrite", SetLastError = true)] + public static extern void SoftToneWrite(Int32 pin, Int32 freq); + + #endregion + + } } diff --git a/Unosquare.RaspberryIO/Native/WiringPi.Spi.cs b/Unosquare.RaspberryIO/Native/WiringPi.Spi.cs index 4a36c97..c53eaae 100644 --- a/Unosquare.RaspberryIO/Native/WiringPi.Spi.cs +++ b/Unosquare.RaspberryIO/Native/WiringPi.Spi.cs @@ -1,53 +1,52 @@ -namespace Unosquare.RaspberryIO.Native -{ - using System.Runtime.InteropServices; - - public partial class WiringPi - { - #region WiringPi - SPI Library Calls - - /// - /// This function is undocumented - /// - /// The channel. - /// Unknown - [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPIGetFd", SetLastError = true)] - public static extern int WiringPiSPIGetFd(int channel); - - /// - /// This performs a simultaneous write/read transaction over the selected SPI bus. Data that was in your buffer is overwritten by data returned from the SPI bus. - /// That’s all there is in the helper library. It is possible to do simple read and writes over the SPI bus using the standard read() and write() system calls though – - /// write() may be better to use for sending data to chains of shift registers, or those LED strings where you send RGB triplets of data. - /// Devices such as A/D and D/A converters usually need to perform a concurrent write/read transaction to work. - /// - /// The channel. - /// The data. - /// The length. - /// The result - [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPIDataRW", SetLastError = true)] - public static extern int WiringPiSPIDataRW(int channel, byte[] data, int len); - - /// - /// This function is undocumented - /// - /// The channel. - /// The speed. - /// The mode. - /// Unkown - [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPISetupMode", SetLastError = true)] - public static extern int WiringPiSPISetupMode(int channel, int speed, int mode); - - /// - /// This is the way to initialize a channel (The Pi has 2 channels; 0 and 1). The speed parameter is an integer - /// in the range 500,000 through 32,000,000 and represents the SPI clock speed in Hz. - /// The returned value is the Linux file-descriptor for the device, or -1 on error. If an error has happened, you may use the standard errno global variable to see why. - /// - /// The channel. - /// The speed. - /// The Linux file descriptor for the device or -1 for error - [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPISetup", SetLastError = true)] - public static extern int WiringPiSPISetup(int channel, int speed); - - #endregion - } +using System; +using System.Runtime.InteropServices; + +namespace Unosquare.RaspberryIO.Native { + public partial class WiringPi { + #region WiringPi - SPI Library Calls + + /// + /// This function is undocumented + /// + /// The channel. + /// Unknown + [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPIGetFd", SetLastError = true)] + public static extern Int32 WiringPiSPIGetFd(Int32 channel); + + /// + /// This performs a simultaneous write/read transaction over the selected SPI bus. Data that was in your buffer is overwritten by data returned from the SPI bus. + /// That’s all there is in the helper library. It is possible to do simple read and writes over the SPI bus using the standard read() and write() system calls though – + /// write() may be better to use for sending data to chains of shift registers, or those LED strings where you send RGB triplets of data. + /// Devices such as A/D and D/A converters usually need to perform a concurrent write/read transaction to work. + /// + /// The channel. + /// The data. + /// The length. + /// The result + [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPIDataRW", SetLastError = true)] + public static extern Int32 WiringPiSPIDataRW(Int32 channel, Byte[] data, Int32 len); + + /// + /// This function is undocumented + /// + /// The channel. + /// The speed. + /// The mode. + /// Unkown + [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPISetupMode", SetLastError = true)] + public static extern Int32 WiringPiSPISetupMode(Int32 channel, Int32 speed, Int32 mode); + + /// + /// This is the way to initialize a channel (The Pi has 2 channels; 0 and 1). The speed parameter is an integer + /// in the range 500,000 through 32,000,000 and represents the SPI clock speed in Hz. + /// The returned value is the Linux file-descriptor for the device, or -1 on error. If an error has happened, you may use the standard errno global variable to see why. + /// + /// The channel. + /// The speed. + /// The Linux file descriptor for the device or -1 for error + [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPISetup", SetLastError = true)] + public static extern Int32 WiringPiSPISetup(Int32 channel, Int32 speed); + + #endregion + } } diff --git a/Unosquare.RaspberryIO/Native/WiringPi.cs b/Unosquare.RaspberryIO/Native/WiringPi.cs index df347e7..4acf1c9 100644 --- a/Unosquare.RaspberryIO/Native/WiringPi.cs +++ b/Unosquare.RaspberryIO/Native/WiringPi.cs @@ -1,394 +1,392 @@ -namespace Unosquare.RaspberryIO.Native -{ - using System; - using System.Runtime.InteropServices; - +using System; +using System.Runtime.InteropServices; + +namespace Unosquare.RaspberryIO.Native { + /// + /// Provides native C WiringPi Library function call wrappers + /// All credit for the native library goes to the author of http://wiringpi.com/ + /// The wrappers were written based on https://github.com/WiringPi/WiringPi/blob/master/wiringPi/wiringPi.h + /// + public partial class WiringPi { + internal const String WiringPiLibrary = "libwiringPi.so.2.46"; + + #region WiringPi - Core Functions (https://github.com/WiringPi/WiringPi/blob/master/wiringPi/wiringPi.h) + /// - /// Provides native C WiringPi Library function call wrappers - /// All credit for the native library goes to the author of http://wiringpi.com/ - /// The wrappers were written based on https://github.com/WiringPi/WiringPi/blob/master/wiringPi/wiringPi.h + /// This initialises wiringPi and assumes that the calling program is going to be using the wiringPi pin numbering scheme. + /// This is a simplified numbering scheme which provides a mapping from virtual pin numbers 0 through 16 to the real underlying Broadcom GPIO pin numbers. + /// See the pins page for a table which maps the wiringPi pin number to the Broadcom GPIO pin number to the physical location on the edge connector. + /// This function needs to be called with root privileges. /// - public partial class WiringPi - { - internal const string WiringPiLibrary = "libwiringPi.so.2.46"; - - #region WiringPi - Core Functions (https://github.com/WiringPi/WiringPi/blob/master/wiringPi/wiringPi.h) - - /// - /// This initialises wiringPi and assumes that the calling program is going to be using the wiringPi pin numbering scheme. - /// This is a simplified numbering scheme which provides a mapping from virtual pin numbers 0 through 16 to the real underlying Broadcom GPIO pin numbers. - /// See the pins page for a table which maps the wiringPi pin number to the Broadcom GPIO pin number to the physical location on the edge connector. - /// This function needs to be called with root privileges. - /// - /// The result code - [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetup", SetLastError = true)] - public static extern int WiringPiSetup(); - - /// - /// This initialises wiringPi but uses the /sys/class/gpio interface rather than accessing the hardware directly. - /// This can be called as a non-root user provided the GPIO pins have been exported before-hand using the gpio program. - /// Pin numbering in this mode is the native Broadcom GPIO numbers – the same as wiringPiSetupGpio() above, - /// so be aware of the differences between Rev 1 and Rev 2 boards. - /// - /// Note: In this mode you can only use the pins which have been exported via the /sys/class/gpio interface before you run your program. - /// You can do this in a separate shell-script, or by using the system() function from inside your program to call the gpio program. - /// Also note that some functions have no effect when using this mode as they’re not currently possible to action unless called with root privileges. - /// (although you can use system() to call gpio to set/change modes if needed) - /// - /// The result code - [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetupSys", SetLastError = true)] - public static extern int WiringPiSetupSys(); - - /// - /// This is identical to wiringPiSetup, however it allows the calling programs to use the Broadcom GPIO - /// pin numbers directly with no re-mapping. - /// As above, this function needs to be called with root privileges, and note that some pins are different - /// from revision 1 to revision 2 boards. - /// - /// The result code - [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetupGpio", SetLastError = true)] - public static extern int WiringPiSetupGpio(); - - /// - /// Identical to wiringPiSetup, however it allows the calling programs to use the physical pin numbers on the P1 connector only. - /// This function needs to be called with root privileges. - /// - /// The result code - [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetupPhys", SetLastError = true)] - public static extern int WiringPiSetupPhys(); - - /// - /// This function is undocumented - /// - /// The pin. - /// The mode. - [DllImport(WiringPiLibrary, EntryPoint = "pinModeAlt", SetLastError = true)] - public static extern void PinModeAlt(int pin, int mode); - - /// - /// This sets the mode of a pin to either INPUT, OUTPUT, PWM_OUTPUT or GPIO_CLOCK. - /// Note that only wiringPi pin 1 (BCM_GPIO 18) supports PWM output and only wiringPi pin 7 (BCM_GPIO 4) - /// supports CLOCK output modes. - /// - /// This function has no effect when in Sys mode. If you need to change the pin mode, then you can - /// do it with the gpio program in a script before you start your program. - /// - /// The pin. - /// The mode. - [DllImport(WiringPiLibrary, EntryPoint = "pinMode", SetLastError = true)] - public static extern void PinMode(int pin, int mode); - - /// - /// This sets the pull-up or pull-down resistor mode on the given pin, which should be set as an input. - /// Unlike the Arduino, the BCM2835 has both pull-up an down internal resistors. The parameter pud should be; PUD_OFF, - /// (no pull up/down), PUD_DOWN (pull to ground) or PUD_UP (pull to 3.3v) The internal pull up/down resistors - /// have a value of approximately 50KΩ on the Raspberry Pi. - /// - /// This function has no effect on the Raspberry Pi’s GPIO pins when in Sys mode. - /// If you need to activate a pull-up/pull-down, then you can do it with the gpio program in a script before you start your program. - /// - /// The pin. - /// The pud. - [DllImport(WiringPiLibrary, EntryPoint = "pullUpDnControl", SetLastError = true)] - public static extern void PullUpDnControl(int pin, int pud); - - /// - /// This function returns the value read at the given pin. It will be HIGH or LOW (1 or 0) depending on the logic level at the pin. - /// - /// The pin. - /// The result code - [DllImport(WiringPiLibrary, EntryPoint = "digitalRead", SetLastError = true)] - public static extern int DigitalRead(int pin); - - /// - /// Writes the value HIGH or LOW (1 or 0) to the given pin which must have been previously set as an output. - /// WiringPi treats any non-zero number as HIGH, however 0 is the only representation of LOW. - /// - /// The pin. - /// The value. - [DllImport(WiringPiLibrary, EntryPoint = "digitalWrite", SetLastError = true)] - public static extern void DigitalWrite(int pin, int value); - - /// - /// Writes the value to the PWM register for the given pin. The Raspberry Pi has one - /// on-board PWM pin, pin 1 (BMC_GPIO 18, Phys 12) and the range is 0-1024. - /// Other PWM devices may have other PWM ranges. - /// This function is not able to control the Pi’s on-board PWM when in Sys mode. - /// - /// The pin. - /// The value. - [DllImport(WiringPiLibrary, EntryPoint = "pwmWrite", SetLastError = true)] - public static extern void PwmWrite(int pin, int value); - - /// - /// This returns the value read on the supplied analog input pin. You will need to - /// register additional analog modules to enable this function for devices such as the Gertboard, quick2Wire analog board, etc. - /// - /// The pin. - /// The result code - [DllImport(WiringPiLibrary, EntryPoint = "analogRead", SetLastError = true)] - public static extern int AnalogRead(int pin); - - /// - /// This writes the given value to the supplied analog pin. You will need to register additional - /// analog modules to enable this function for devices such as the Gertboard. - /// - /// The pin. - /// The value. - [DllImport(WiringPiLibrary, EntryPoint = "analogWrite", SetLastError = true)] - public static extern void AnalogWrite(int pin, int value); - - /// - /// This returns the board revision of the Raspberry Pi. It will be either 1 or 2. Some of the BCM_GPIO pins changed number and - /// function when moving from board revision 1 to 2, so if you are using BCM_GPIO pin numbers, then you need to be aware of the differences. - /// - /// The result code - [DllImport(WiringPiLibrary, EntryPoint = "piBoardRev", SetLastError = true)] - public static extern int PiBoardRev(); - - /// - /// This function is undocumented - /// - /// The model. - /// The memory. - /// The maker. - /// The over volted. - /// The result code - [DllImport(WiringPiLibrary, EntryPoint = "piBoardId", SetLastError = true)] - public static extern int PiBoardId(ref int model, ref int mem, ref int maker, ref int overVolted); - - /// - /// This returns the BCM_GPIO pin number of the supplied wiringPi pin. It takes the board revision into account. - /// - /// The w pi pin. - /// The result code - [DllImport(WiringPiLibrary, EntryPoint = "wpiPinToGpio", SetLastError = true)] - public static extern int WpiPinToGpio(int wPiPin); - - /// - /// This returns the BCM_GPIO pin number of the supplied physical pin on the P1 connector. - /// - /// The physical pin. - /// The result code - [DllImport(WiringPiLibrary, EntryPoint = "physPinToGpio", SetLastError = true)] - public static extern int PhysPinToGpio(int physPin); - - /// - /// This sets the “strength” of the pad drivers for a particular group of pins. - /// There are 3 groups of pins and the drive strength is from 0 to 7. Do not use this unless you know what you are doing. - /// - /// The group. - /// The value. - /// The result code - [DllImport(WiringPiLibrary, EntryPoint = "setPadDrive", SetLastError = true)] - public static extern int SetPadDrive(int group, int value); - - /// - /// Undocumented function - /// - /// The pin. - /// The result code - [DllImport(WiringPiLibrary, EntryPoint = "getAlt", SetLastError = true)] - public static extern int GetAlt(int pin); - - /// - /// Undocumented function - /// - /// The pin. - /// The freq. - /// The result code - [DllImport(WiringPiLibrary, EntryPoint = "pwmToneWrite", SetLastError = true)] - public static extern int PwmToneWrite(int pin, int freq); - - /// - /// This writes the 8-bit byte supplied to the first 8 GPIO pins. - /// It’s the fastest way to set all 8 bits at once to a particular value, although it still takes two write operations to the Pi’s GPIO hardware. - /// - /// The value. - [DllImport(WiringPiLibrary, EntryPoint = "digitalWriteByte", SetLastError = true)] - public static extern void DigitalWriteByte(int value); - - /// - /// This writes the 8-bit byte supplied to the first 8 GPIO pins. - /// It’s the fastest way to set all 8 bits at once to a particular value, although it still takes two write operations to the Pi’s GPIO hardware. - /// - /// The value. - [DllImport(WiringPiLibrary, EntryPoint = "digitalWriteByte2", SetLastError = true)] - public static extern void DigitalWriteByte2(int value); - - /// - /// Undocumented function - /// This reads the 8-bit byte supplied to the first 8 GPIO pins. - /// It’s the fastest way to get all 8 bits at once to a particular value. - /// - /// The result code - [DllImport(WiringPiLibrary, EntryPoint = "digitalReadByte", SetLastError = true)] - public static extern uint DigitalReadByte(); - - /// - /// Undocumented function - /// This reads the 8-bit byte supplied to the first 8 GPIO pins. - /// It’s the fastest way to get all 8 bits at once to a particular value. - /// - /// The result code - [DllImport(WiringPiLibrary, EntryPoint = "digitalReadByte2", SetLastError = true)] - public static extern uint DigitalReadByte2(); - - /// - /// The PWM generator can run in 2 modes – “balanced” and “mark:space”. The mark:space mode is traditional, - /// however the default mode in the Pi is “balanced”. You can switch modes by supplying the parameter: PWM_MODE_BAL or PWM_MODE_MS. - /// - /// The mode. - [DllImport(WiringPiLibrary, EntryPoint = "pwmSetMode", SetLastError = true)] - public static extern void PwmSetMode(int mode); - - /// - /// This sets the range register in the PWM generator. The default is 1024. - /// - /// The range. - [DllImport(WiringPiLibrary, EntryPoint = "pwmSetRange", SetLastError = true)] - public static extern void PwmSetRange(uint range); - - /// - /// This sets the divisor for the PWM clock. - /// Note: The PWM control functions can not be used when in Sys mode. - /// To understand more about the PWM system, you’ll need to read the Broadcom ARM peripherals manual. - /// - /// The divisor. - [DllImport(WiringPiLibrary, EntryPoint = "pwmSetClock", SetLastError = true)] - public static extern void PwmSetClock(int divisor); - - /// - /// Undocumented function - /// - /// The pin. - /// The freq. - [DllImport(WiringPiLibrary, EntryPoint = "gpioClockSet", SetLastError = true)] - public static extern void GpioClockSet(int pin, int freq); - - /// - /// Note: Jan 2013: The waitForInterrupt() function is deprecated – you should use the newer and easier to use wiringPiISR() function below. - /// When called, it will wait for an interrupt event to happen on that pin and your program will be stalled. The timeOut parameter is given in milliseconds, - /// or can be -1 which means to wait forever. - /// The return value is -1 if an error occurred (and errno will be set appropriately), 0 if it timed out, or 1 on a successful interrupt event. - /// Before you call waitForInterrupt, you must first initialise the GPIO pin and at present the only way to do this is to use the gpio program, either - /// in a script, or using the system() call from inside your program. - /// e.g. We want to wait for a falling-edge interrupt on GPIO pin 0, so to setup the hardware, we need to run: gpio edge 0 falling - /// before running the program. - /// - /// The pin. - /// The timeout. - /// The result code - [Obsolete] - [DllImport(WiringPiLibrary, EntryPoint = "waitForInterrupt", SetLastError = true)] - public static extern int WaitForInterrupt(int pin, int timeout); - - /// - /// This function registers a function to received interrupts on the specified pin. - /// The edgeType parameter is either INT_EDGE_FALLING, INT_EDGE_RISING, INT_EDGE_BOTH or INT_EDGE_SETUP. - /// If it is INT_EDGE_SETUP then no initialisation of the pin will happen – it’s assumed that you have already setup the pin elsewhere - /// (e.g. with the gpio program), but if you specify one of the other types, then the pin will be exported and initialised as specified. - /// This is accomplished via a suitable call to the gpio utility program, so it need to be available. - /// The pin number is supplied in the current mode – native wiringPi, BCM_GPIO, physical or Sys modes. - /// This function will work in any mode, and does not need root privileges to work. - /// The function will be called when the interrupt triggers. When it is triggered, it’s cleared in the dispatcher before calling your function, - /// so if a subsequent interrupt fires before you finish your handler, then it won’t be missed. (However it can only track one more interrupt, - /// if more than one interrupt fires while one is being handled then they will be ignored) - /// This function is run at a high priority (if the program is run using sudo, or as root) and executes concurrently with the main program. - /// It has full access to all the global variables, open file handles and so on. - /// - /// The pin. - /// The mode. - /// The method. - /// The result code - [DllImport(WiringPiLibrary, EntryPoint = "wiringPiISR", SetLastError = true)] - public static extern int WiringPiISR(int pin, int mode, InterruptServiceRoutineCallback method); - - /// - /// This function creates a thread which is another function in your program previously declared using the PI_THREAD declaration. - /// This function is then run concurrently with your main program. An example may be to have this function wait for an interrupt while - /// your program carries on doing other tasks. The thread can indicate an event, or action by using global variables to - /// communicate back to the main program, or other threads. - /// - /// The method. - /// The result code - [DllImport(WiringPiLibrary, EntryPoint = "piThreadCreate", SetLastError = true)] - public static extern int PiThreadCreate(ThreadWorker method); - - /// - /// These allow you to synchronise variable updates from your main program to any threads running in your program. keyNum is a number from 0 to 3 and represents a key. - /// When another process tries to lock the same key, it will be stalled until the first process has unlocked the same key. - /// You may need to use these functions to ensure that you get valid data when exchanging data between your main program and a thread - /// – otherwise it’s possible that the thread could wake-up halfway during your data copy and change the data – - /// so the data you end up copying is incomplete, or invalid. See the wfi.c program in the examples directory for an example. - /// - /// The key. - [DllImport(WiringPiLibrary, EntryPoint = "piLock", SetLastError = true)] - public static extern void PiLock(int key); - - /// - /// These allow you to synchronise variable updates from your main program to any threads running in your program. keyNum is a number from 0 to 3 and represents a key. - /// When another process tries to lock the same key, it will be stalled until the first process has unlocked the same key. - /// You may need to use these functions to ensure that you get valid data when exchanging data between your main program and a thread - /// – otherwise it’s possible that the thread could wake-up halfway during your data copy and change the data – - /// so the data you end up copying is incomplete, or invalid. See the wfi.c program in the examples directory for an example. - /// - /// The key. - [DllImport(WiringPiLibrary, EntryPoint = "piUnlock", SetLastError = true)] - public static extern void PiUnlock(int key); - - /// - /// This attempts to shift your program (or thread in a multi-threaded program) to a higher priority - /// and enables a real-time scheduling. The priority parameter should be from 0 (the default) to 99 (the maximum). - /// This won’t make your program go any faster, but it will give it a bigger slice of time when other programs are running. - /// The priority parameter works relative to others – so you can make one program priority 1 and another priority 2 - /// and it will have the same effect as setting one to 10 and the other to 90 (as long as no other - /// programs are running with elevated priorities) - /// The return value is 0 for success and -1 for error. If an error is returned, the program should then consult the errno global variable, as per the usual conventions. - /// Note: Only programs running as root can change their priority. If called from a non-root program then nothing happens. - /// - /// The priority. - /// The result code - [DllImport(WiringPiLibrary, EntryPoint = "piHiPri", SetLastError = true)] - public static extern int PiHiPri(int priority); - - /// - /// This causes program execution to pause for at least howLong milliseconds. - /// Due to the multi-tasking nature of Linux it could be longer. - /// Note that the maximum delay is an unsigned 32-bit integer or approximately 49 days. - /// - /// The how long. - [DllImport(WiringPiLibrary, EntryPoint = "delay", SetLastError = true)] - public static extern void Delay(uint howLong); - - /// - /// This causes program execution to pause for at least howLong microseconds. - /// Due to the multi-tasking nature of Linux it could be longer. - /// Note that the maximum delay is an unsigned 32-bit integer microseconds or approximately 71 minutes. - /// Delays under 100 microseconds are timed using a hard-coded loop continually polling the system time, - /// Delays over 100 microseconds are done using the system nanosleep() function – You may need to consider the implications - /// of very short delays on the overall performance of the system, especially if using threads. - /// - /// The how long. - [DllImport(WiringPiLibrary, EntryPoint = "delayMicroseconds", SetLastError = true)] - public static extern void DelayMicroseconds(uint howLong); - - /// - /// This returns a number representing the number of milliseconds since your program called one of the wiringPiSetup functions. - /// It returns an unsigned 32-bit number which wraps after 49 days. - /// - /// The result code - [DllImport(WiringPiLibrary, EntryPoint = "millis", SetLastError = true)] - public static extern uint Millis(); - - /// - /// This returns a number representing the number of microseconds since your program called one of - /// the wiringPiSetup functions. It returns an unsigned 32-bit number which wraps after approximately 71 minutes. - /// - /// The result code - [DllImport(WiringPiLibrary, EntryPoint = "micros", SetLastError = true)] - public static extern uint Micros(); - - #endregion - } + /// The result code + [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetup", SetLastError = true)] + public static extern Int32 WiringPiSetup(); + + /// + /// This initialises wiringPi but uses the /sys/class/gpio interface rather than accessing the hardware directly. + /// This can be called as a non-root user provided the GPIO pins have been exported before-hand using the gpio program. + /// Pin numbering in this mode is the native Broadcom GPIO numbers – the same as wiringPiSetupGpio() above, + /// so be aware of the differences between Rev 1 and Rev 2 boards. + /// + /// Note: In this mode you can only use the pins which have been exported via the /sys/class/gpio interface before you run your program. + /// You can do this in a separate shell-script, or by using the system() function from inside your program to call the gpio program. + /// Also note that some functions have no effect when using this mode as they’re not currently possible to action unless called with root privileges. + /// (although you can use system() to call gpio to set/change modes if needed) + /// + /// The result code + [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetupSys", SetLastError = true)] + public static extern Int32 WiringPiSetupSys(); + + /// + /// This is identical to wiringPiSetup, however it allows the calling programs to use the Broadcom GPIO + /// pin numbers directly with no re-mapping. + /// As above, this function needs to be called with root privileges, and note that some pins are different + /// from revision 1 to revision 2 boards. + /// + /// The result code + [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetupGpio", SetLastError = true)] + public static extern Int32 WiringPiSetupGpio(); + + /// + /// Identical to wiringPiSetup, however it allows the calling programs to use the physical pin numbers on the P1 connector only. + /// This function needs to be called with root privileges. + /// + /// The result code + [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetupPhys", SetLastError = true)] + public static extern Int32 WiringPiSetupPhys(); + + /// + /// This function is undocumented + /// + /// The pin. + /// The mode. + [DllImport(WiringPiLibrary, EntryPoint = "pinModeAlt", SetLastError = true)] + public static extern void PinModeAlt(Int32 pin, Int32 mode); + + /// + /// This sets the mode of a pin to either INPUT, OUTPUT, PWM_OUTPUT or GPIO_CLOCK. + /// Note that only wiringPi pin 1 (BCM_GPIO 18) supports PWM output and only wiringPi pin 7 (BCM_GPIO 4) + /// supports CLOCK output modes. + /// + /// This function has no effect when in Sys mode. If you need to change the pin mode, then you can + /// do it with the gpio program in a script before you start your program. + /// + /// The pin. + /// The mode. + [DllImport(WiringPiLibrary, EntryPoint = "pinMode", SetLastError = true)] + public static extern void PinMode(Int32 pin, Int32 mode); + + /// + /// This sets the pull-up or pull-down resistor mode on the given pin, which should be set as an input. + /// Unlike the Arduino, the BCM2835 has both pull-up an down internal resistors. The parameter pud should be; PUD_OFF, + /// (no pull up/down), PUD_DOWN (pull to ground) or PUD_UP (pull to 3.3v) The internal pull up/down resistors + /// have a value of approximately 50KΩ on the Raspberry Pi. + /// + /// This function has no effect on the Raspberry Pi’s GPIO pins when in Sys mode. + /// If you need to activate a pull-up/pull-down, then you can do it with the gpio program in a script before you start your program. + /// + /// The pin. + /// The pud. + [DllImport(WiringPiLibrary, EntryPoint = "pullUpDnControl", SetLastError = true)] + public static extern void PullUpDnControl(Int32 pin, Int32 pud); + + /// + /// This function returns the value read at the given pin. It will be HIGH or LOW (1 or 0) depending on the logic level at the pin. + /// + /// The pin. + /// The result code + [DllImport(WiringPiLibrary, EntryPoint = "digitalRead", SetLastError = true)] + public static extern Int32 DigitalRead(Int32 pin); + + /// + /// Writes the value HIGH or LOW (1 or 0) to the given pin which must have been previously set as an output. + /// WiringPi treats any non-zero number as HIGH, however 0 is the only representation of LOW. + /// + /// The pin. + /// The value. + [DllImport(WiringPiLibrary, EntryPoint = "digitalWrite", SetLastError = true)] + public static extern void DigitalWrite(Int32 pin, Int32 value); + + /// + /// Writes the value to the PWM register for the given pin. The Raspberry Pi has one + /// on-board PWM pin, pin 1 (BMC_GPIO 18, Phys 12) and the range is 0-1024. + /// Other PWM devices may have other PWM ranges. + /// This function is not able to control the Pi’s on-board PWM when in Sys mode. + /// + /// The pin. + /// The value. + [DllImport(WiringPiLibrary, EntryPoint = "pwmWrite", SetLastError = true)] + public static extern void PwmWrite(Int32 pin, Int32 value); + + /// + /// This returns the value read on the supplied analog input pin. You will need to + /// register additional analog modules to enable this function for devices such as the Gertboard, quick2Wire analog board, etc. + /// + /// The pin. + /// The result code + [DllImport(WiringPiLibrary, EntryPoint = "analogRead", SetLastError = true)] + public static extern Int32 AnalogRead(Int32 pin); + + /// + /// This writes the given value to the supplied analog pin. You will need to register additional + /// analog modules to enable this function for devices such as the Gertboard. + /// + /// The pin. + /// The value. + [DllImport(WiringPiLibrary, EntryPoint = "analogWrite", SetLastError = true)] + public static extern void AnalogWrite(Int32 pin, Int32 value); + + /// + /// This returns the board revision of the Raspberry Pi. It will be either 1 or 2. Some of the BCM_GPIO pins changed number and + /// function when moving from board revision 1 to 2, so if you are using BCM_GPIO pin numbers, then you need to be aware of the differences. + /// + /// The result code + [DllImport(WiringPiLibrary, EntryPoint = "piBoardRev", SetLastError = true)] + public static extern Int32 PiBoardRev(); + + /// + /// This function is undocumented + /// + /// The model. + /// The memory. + /// The maker. + /// The over volted. + /// The result code + [DllImport(WiringPiLibrary, EntryPoint = "piBoardId", SetLastError = true)] + public static extern Int32 PiBoardId(ref Int32 model, ref Int32 mem, ref Int32 maker, ref Int32 overVolted); + + /// + /// This returns the BCM_GPIO pin number of the supplied wiringPi pin. It takes the board revision into account. + /// + /// The w pi pin. + /// The result code + [DllImport(WiringPiLibrary, EntryPoint = "wpiPinToGpio", SetLastError = true)] + public static extern Int32 WpiPinToGpio(Int32 wPiPin); + + /// + /// This returns the BCM_GPIO pin number of the supplied physical pin on the P1 connector. + /// + /// The physical pin. + /// The result code + [DllImport(WiringPiLibrary, EntryPoint = "physPinToGpio", SetLastError = true)] + public static extern Int32 PhysPinToGpio(Int32 physPin); + + /// + /// This sets the “strength” of the pad drivers for a particular group of pins. + /// There are 3 groups of pins and the drive strength is from 0 to 7. Do not use this unless you know what you are doing. + /// + /// The group. + /// The value. + /// The result code + [DllImport(WiringPiLibrary, EntryPoint = "setPadDrive", SetLastError = true)] + public static extern Int32 SetPadDrive(Int32 group, Int32 value); + + /// + /// Undocumented function + /// + /// The pin. + /// The result code + [DllImport(WiringPiLibrary, EntryPoint = "getAlt", SetLastError = true)] + public static extern Int32 GetAlt(Int32 pin); + + /// + /// Undocumented function + /// + /// The pin. + /// The freq. + /// The result code + [DllImport(WiringPiLibrary, EntryPoint = "pwmToneWrite", SetLastError = true)] + public static extern Int32 PwmToneWrite(Int32 pin, Int32 freq); + + /// + /// This writes the 8-bit byte supplied to the first 8 GPIO pins. + /// It’s the fastest way to set all 8 bits at once to a particular value, although it still takes two write operations to the Pi’s GPIO hardware. + /// + /// The value. + [DllImport(WiringPiLibrary, EntryPoint = "digitalWriteByte", SetLastError = true)] + public static extern void DigitalWriteByte(Int32 value); + + /// + /// This writes the 8-bit byte supplied to the first 8 GPIO pins. + /// It’s the fastest way to set all 8 bits at once to a particular value, although it still takes two write operations to the Pi’s GPIO hardware. + /// + /// The value. + [DllImport(WiringPiLibrary, EntryPoint = "digitalWriteByte2", SetLastError = true)] + public static extern void DigitalWriteByte2(Int32 value); + + /// + /// Undocumented function + /// This reads the 8-bit byte supplied to the first 8 GPIO pins. + /// It’s the fastest way to get all 8 bits at once to a particular value. + /// + /// The result code + [DllImport(WiringPiLibrary, EntryPoint = "digitalReadByte", SetLastError = true)] + public static extern UInt32 DigitalReadByte(); + + /// + /// Undocumented function + /// This reads the 8-bit byte supplied to the first 8 GPIO pins. + /// It’s the fastest way to get all 8 bits at once to a particular value. + /// + /// The result code + [DllImport(WiringPiLibrary, EntryPoint = "digitalReadByte2", SetLastError = true)] + public static extern UInt32 DigitalReadByte2(); + + /// + /// The PWM generator can run in 2 modes – “balanced” and “mark:space”. The mark:space mode is traditional, + /// however the default mode in the Pi is “balanced”. You can switch modes by supplying the parameter: PWM_MODE_BAL or PWM_MODE_MS. + /// + /// The mode. + [DllImport(WiringPiLibrary, EntryPoint = "pwmSetMode", SetLastError = true)] + public static extern void PwmSetMode(Int32 mode); + + /// + /// This sets the range register in the PWM generator. The default is 1024. + /// + /// The range. + [DllImport(WiringPiLibrary, EntryPoint = "pwmSetRange", SetLastError = true)] + public static extern void PwmSetRange(UInt32 range); + + /// + /// This sets the divisor for the PWM clock. + /// Note: The PWM control functions can not be used when in Sys mode. + /// To understand more about the PWM system, you’ll need to read the Broadcom ARM peripherals manual. + /// + /// The divisor. + [DllImport(WiringPiLibrary, EntryPoint = "pwmSetClock", SetLastError = true)] + public static extern void PwmSetClock(Int32 divisor); + + /// + /// Undocumented function + /// + /// The pin. + /// The freq. + [DllImport(WiringPiLibrary, EntryPoint = "gpioClockSet", SetLastError = true)] + public static extern void GpioClockSet(Int32 pin, Int32 freq); + + /// + /// Note: Jan 2013: The waitForInterrupt() function is deprecated – you should use the newer and easier to use wiringPiISR() function below. + /// When called, it will wait for an interrupt event to happen on that pin and your program will be stalled. The timeOut parameter is given in milliseconds, + /// or can be -1 which means to wait forever. + /// The return value is -1 if an error occurred (and errno will be set appropriately), 0 if it timed out, or 1 on a successful interrupt event. + /// Before you call waitForInterrupt, you must first initialise the GPIO pin and at present the only way to do this is to use the gpio program, either + /// in a script, or using the system() call from inside your program. + /// e.g. We want to wait for a falling-edge interrupt on GPIO pin 0, so to setup the hardware, we need to run: gpio edge 0 falling + /// before running the program. + /// + /// The pin. + /// The timeout. + /// The result code + [Obsolete] + [DllImport(WiringPiLibrary, EntryPoint = "waitForInterrupt", SetLastError = true)] + public static extern Int32 WaitForInterrupt(Int32 pin, Int32 timeout); + + /// + /// This function registers a function to received interrupts on the specified pin. + /// The edgeType parameter is either INT_EDGE_FALLING, INT_EDGE_RISING, INT_EDGE_BOTH or INT_EDGE_SETUP. + /// If it is INT_EDGE_SETUP then no initialisation of the pin will happen – it’s assumed that you have already setup the pin elsewhere + /// (e.g. with the gpio program), but if you specify one of the other types, then the pin will be exported and initialised as specified. + /// This is accomplished via a suitable call to the gpio utility program, so it need to be available. + /// The pin number is supplied in the current mode – native wiringPi, BCM_GPIO, physical or Sys modes. + /// This function will work in any mode, and does not need root privileges to work. + /// The function will be called when the interrupt triggers. When it is triggered, it’s cleared in the dispatcher before calling your function, + /// so if a subsequent interrupt fires before you finish your handler, then it won’t be missed. (However it can only track one more interrupt, + /// if more than one interrupt fires while one is being handled then they will be ignored) + /// This function is run at a high priority (if the program is run using sudo, or as root) and executes concurrently with the main program. + /// It has full access to all the global variables, open file handles and so on. + /// + /// The pin. + /// The mode. + /// The method. + /// The result code + [DllImport(WiringPiLibrary, EntryPoint = "wiringPiISR", SetLastError = true)] + public static extern Int32 WiringPiISR(Int32 pin, Int32 mode, InterruptServiceRoutineCallback method); + + /// + /// This function creates a thread which is another function in your program previously declared using the PI_THREAD declaration. + /// This function is then run concurrently with your main program. An example may be to have this function wait for an interrupt while + /// your program carries on doing other tasks. The thread can indicate an event, or action by using global variables to + /// communicate back to the main program, or other threads. + /// + /// The method. + /// The result code + [DllImport(WiringPiLibrary, EntryPoint = "piThreadCreate", SetLastError = true)] + public static extern Int32 PiThreadCreate(ThreadWorker method); + + /// + /// These allow you to synchronise variable updates from your main program to any threads running in your program. keyNum is a number from 0 to 3 and represents a key. + /// When another process tries to lock the same key, it will be stalled until the first process has unlocked the same key. + /// You may need to use these functions to ensure that you get valid data when exchanging data between your main program and a thread + /// – otherwise it’s possible that the thread could wake-up halfway during your data copy and change the data – + /// so the data you end up copying is incomplete, or invalid. See the wfi.c program in the examples directory for an example. + /// + /// The key. + [DllImport(WiringPiLibrary, EntryPoint = "piLock", SetLastError = true)] + public static extern void PiLock(Int32 key); + + /// + /// These allow you to synchronise variable updates from your main program to any threads running in your program. keyNum is a number from 0 to 3 and represents a key. + /// When another process tries to lock the same key, it will be stalled until the first process has unlocked the same key. + /// You may need to use these functions to ensure that you get valid data when exchanging data between your main program and a thread + /// – otherwise it’s possible that the thread could wake-up halfway during your data copy and change the data – + /// so the data you end up copying is incomplete, or invalid. See the wfi.c program in the examples directory for an example. + /// + /// The key. + [DllImport(WiringPiLibrary, EntryPoint = "piUnlock", SetLastError = true)] + public static extern void PiUnlock(Int32 key); + + /// + /// This attempts to shift your program (or thread in a multi-threaded program) to a higher priority + /// and enables a real-time scheduling. The priority parameter should be from 0 (the default) to 99 (the maximum). + /// This won’t make your program go any faster, but it will give it a bigger slice of time when other programs are running. + /// The priority parameter works relative to others – so you can make one program priority 1 and another priority 2 + /// and it will have the same effect as setting one to 10 and the other to 90 (as long as no other + /// programs are running with elevated priorities) + /// The return value is 0 for success and -1 for error. If an error is returned, the program should then consult the errno global variable, as per the usual conventions. + /// Note: Only programs running as root can change their priority. If called from a non-root program then nothing happens. + /// + /// The priority. + /// The result code + [DllImport(WiringPiLibrary, EntryPoint = "piHiPri", SetLastError = true)] + public static extern Int32 PiHiPri(Int32 priority); + + /// + /// This causes program execution to pause for at least howLong milliseconds. + /// Due to the multi-tasking nature of Linux it could be longer. + /// Note that the maximum delay is an unsigned 32-bit integer or approximately 49 days. + /// + /// The how long. + [DllImport(WiringPiLibrary, EntryPoint = "delay", SetLastError = true)] + public static extern void Delay(UInt32 howLong); + + /// + /// This causes program execution to pause for at least howLong microseconds. + /// Due to the multi-tasking nature of Linux it could be longer. + /// Note that the maximum delay is an unsigned 32-bit integer microseconds or approximately 71 minutes. + /// Delays under 100 microseconds are timed using a hard-coded loop continually polling the system time, + /// Delays over 100 microseconds are done using the system nanosleep() function – You may need to consider the implications + /// of very short delays on the overall performance of the system, especially if using threads. + /// + /// The how long. + [DllImport(WiringPiLibrary, EntryPoint = "delayMicroseconds", SetLastError = true)] + public static extern void DelayMicroseconds(UInt32 howLong); + + /// + /// This returns a number representing the number of milliseconds since your program called one of the wiringPiSetup functions. + /// It returns an unsigned 32-bit number which wraps after 49 days. + /// + /// The result code + [DllImport(WiringPiLibrary, EntryPoint = "millis", SetLastError = true)] + public static extern UInt32 Millis(); + + /// + /// This returns a number representing the number of microseconds since your program called one of + /// the wiringPiSetup functions. It returns an unsigned 32-bit number which wraps after approximately 71 minutes. + /// + /// The result code + [DllImport(WiringPiLibrary, EntryPoint = "micros", SetLastError = true)] + public static extern UInt32 Micros(); + + #endregion + } } \ No newline at end of file diff --git a/Unosquare.RaspberryIO/Pi.cs b/Unosquare.RaspberryIO/Pi.cs index c3bebf0..5f1046e 100644 --- a/Unosquare.RaspberryIO/Pi.cs +++ b/Unosquare.RaspberryIO/Pi.cs @@ -1,110 +1,121 @@ -namespace Unosquare.RaspberryIO -{ - using Camera; - using Computer; - using Gpio; - using Native; - using System.Threading.Tasks; - using Swan.Components; - - /// - /// Our main character. Provides access to the Raspberry Pi's GPIO, system and board information and Camera - /// - public static class Pi - { - private static readonly object SyncLock = new object(); - - /// - /// Initializes static members of the class. - /// - static Pi() - { - lock (SyncLock) - { - // Extraction of embedded resources - Resources.EmbeddedResources.ExtractAll(); - - // Instance assignments - Gpio = GpioController.Instance; - Info = SystemInfo.Instance; - Timing = Timing.Instance; - Spi = SpiBus.Instance; - I2C = I2CBus.Instance; - Camera = CameraController.Instance; - PiDisplay = DsiDisplay.Instance; - } - } - - #region Components - - /// - /// Provides access to the Raspberry Pi's GPIO as a collection of GPIO Pins. - /// - public static GpioController Gpio { get; } - - /// - /// Provides information on this Raspberry Pi's CPU and form factor. - /// - public static SystemInfo Info { get; } - - /// - /// Provides access to The PI's Timing and threading API - /// - public static Timing Timing { get; } - - /// - /// Provides access to the 2-channel SPI bus - /// - public static SpiBus Spi { get; } - - /// - /// Provides access to the functionality of the i2c bus. - /// - public static I2CBus I2C { get; } - - /// - /// Provides access to the official Raspberry Pi Camera - /// - public static CameraController Camera { get; } - - /// - /// Provides access to the official Raspberry Pi 7-inch DSI Display - /// - public static DsiDisplay PiDisplay { get; } - - /// - /// Gets the logger source name. - /// - internal static string LoggerSource => typeof(Pi).Namespace; - - #endregion - - #region Methods - - /// - /// Restarts the Pi. Must be running as SU - /// - /// The process result - public static async Task RestartAsync() => await ProcessRunner.GetProcessResultAsync("reboot", null, null); - - /// - /// Restarts the Pi. Must be running as SU - /// - /// The process result - public static ProcessResult Restart() => RestartAsync().GetAwaiter().GetResult(); - - /// - /// Halts the Pi. Must be running as SU - /// - /// The process result - public static async Task ShutdownAsync() => await ProcessRunner.GetProcessResultAsync("halt", null, null); - - /// - /// Halts the Pi. Must be running as SU - /// - /// The process result - public static ProcessResult Shutdown() => ShutdownAsync().GetAwaiter().GetResult(); - - #endregion - } -} +using Unosquare.RaspberryIO.Camera; +using Unosquare.RaspberryIO.Computer; +using Unosquare.RaspberryIO.Gpio; +using Unosquare.RaspberryIO.Native; +using System.Threading.Tasks; +using Unosquare.Swan.Components; +using System; + +namespace Unosquare.RaspberryIO { + /// + /// Our main character. Provides access to the Raspberry Pi's GPIO, system and board information and Camera + /// + public static class Pi { + private static readonly Object SyncLock = new Object(); + + /// + /// Initializes static members of the class. + /// + static Pi() { + lock(SyncLock) { + // Extraction of embedded resources + Resources.EmbeddedResources.ExtractAll(); + + // Instance assignments + Gpio = GpioController.Instance; + Info = SystemInfo.Instance; + Timing = Timing.Instance; + Spi = SpiBus.Instance; + I2C = I2CBus.Instance; + Camera = CameraController.Instance; + PiDisplay = DsiDisplay.Instance; + } + } + + #region Components + + /// + /// Provides access to the Raspberry Pi's GPIO as a collection of GPIO Pins. + /// + public static GpioController Gpio { + get; + } + + /// + /// Provides information on this Raspberry Pi's CPU and form factor. + /// + public static SystemInfo Info { + get; + } + + /// + /// Provides access to The PI's Timing and threading API + /// + public static Timing Timing { + get; + } + + /// + /// Provides access to the 2-channel SPI bus + /// + public static SpiBus Spi { + get; + } + + /// + /// Provides access to the functionality of the i2c bus. + /// + public static I2CBus I2C { + get; + } + + /// + /// Provides access to the official Raspberry Pi Camera + /// + public static CameraController Camera { + get; + } + + /// + /// Provides access to the official Raspberry Pi 7-inch DSI Display + /// + public static DsiDisplay PiDisplay { + get; + } + + /// + /// Gets the logger source name. + /// + internal static String LoggerSource => typeof(Pi).Namespace; + + #endregion + + #region Methods + + /// + /// Restarts the Pi. Must be running as SU + /// + /// The process result + public static async Task RestartAsync() => await ProcessRunner.GetProcessResultAsync("reboot", null, null); + + /// + /// Restarts the Pi. Must be running as SU + /// + /// The process result + public static ProcessResult Restart() => RestartAsync().GetAwaiter().GetResult(); + + /// + /// Halts the Pi. Must be running as SU + /// + /// The process result + public static async Task ShutdownAsync() => await ProcessRunner.GetProcessResultAsync("halt", null, null); + + /// + /// Halts the Pi. Must be running as SU + /// + /// The process result + public static ProcessResult Shutdown() => ShutdownAsync().GetAwaiter().GetResult(); + + #endregion + } +} \ No newline at end of file diff --git a/Unosquare.RaspberryIO/Resources/EmbeddedResources.cs b/Unosquare.RaspberryIO/Resources/EmbeddedResources.cs index 7c8c3b9..ff063ef 100644 --- a/Unosquare.RaspberryIO/Resources/EmbeddedResources.cs +++ b/Unosquare.RaspberryIO/Resources/EmbeddedResources.cs @@ -1,65 +1,55 @@ -namespace Unosquare.RaspberryIO.Resources -{ - using Native; - using Swan; - using System; - using System.Collections.ObjectModel; - using System.IO; - +using Unosquare.RaspberryIO.Native; +using Unosquare.Swan; +using System; +using System.Collections.ObjectModel; +using System.IO; + +namespace Unosquare.RaspberryIO.Resources { + /// + /// Provides access to embedded assembly files + /// + internal static class EmbeddedResources { /// - /// Provides access to embedded assembly files + /// Initializes static members of the class. /// - internal static class EmbeddedResources - { - /// - /// Initializes static members of the class. - /// - static EmbeddedResources() - { - ResourceNames = - new ReadOnlyCollection(typeof(EmbeddedResources).Assembly().GetManifestResourceNames()); - } - - /// - /// Gets the resource names. - /// - /// - /// The resource names. - /// - public static ReadOnlyCollection ResourceNames { get; } - - /// - /// Extracts all the file resources to the specified base path. - /// - public static void ExtractAll() - { - var basePath = Runtime.EntryAssemblyDirectory; - var executablePermissions = Standard.StringToInteger("0777", IntPtr.Zero, 8); - - foreach (var resourceName in ResourceNames) - { - var filename = resourceName.Substring($"{typeof(EmbeddedResources).Namespace}.".Length); - var targetPath = Path.Combine(basePath, filename); - if (File.Exists(targetPath)) return; - - using (var stream = typeof(EmbeddedResources).Assembly() - .GetManifestResourceStream($"{typeof(EmbeddedResources).Namespace}.{filename}")) - { - using (var outputStream = File.OpenWrite(targetPath)) - { - stream?.CopyTo(outputStream); - } - - try - { - Standard.Chmod(targetPath, (uint)executablePermissions); - } - catch - { - /* Ignore */ - } - } - } - } - } + static EmbeddedResources() => ResourceNames = new ReadOnlyCollection(typeof(EmbeddedResources).Assembly().GetManifestResourceNames()); + + /// + /// Gets the resource names. + /// + /// + /// The resource names. + /// + public static ReadOnlyCollection ResourceNames { + get; + } + + /// + /// Extracts all the file resources to the specified base path. + /// + public static void ExtractAll() { + String basePath = Runtime.EntryAssemblyDirectory; + Int32 executablePermissions = Standard.StringToInteger("0777", IntPtr.Zero, 8); + + foreach(String resourceName in ResourceNames) { + String filename = resourceName.Substring($"{typeof(EmbeddedResources).Namespace}.".Length); + String targetPath = Path.Combine(basePath, filename); + if(File.Exists(targetPath)) { + return; + } + + using(Stream stream = typeof(EmbeddedResources).Assembly().GetManifestResourceStream($"{typeof(EmbeddedResources).Namespace}.{filename}")) { + using(FileStream outputStream = File.OpenWrite(targetPath)) { + stream?.CopyTo(outputStream); + } + + try { + _ = Standard.Chmod(targetPath, (UInt32)executablePermissions); + } catch { + /* Ignore */ + } + } + } + } + } } \ No newline at end of file