From 16a35c4c98b49914ccf5839ca7caef1fd0fe9704 Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Sat, 23 Jul 2022 17:44:35 +0200 Subject: [PATCH 01/16] Adding GpioPin --- src/System.Device.Gpio.Tests/GpioPinTests.cs | 80 ++++++++++++ .../System/Device/Gpio/GpioController.cs | 13 +- .../System/Device/Gpio/GpioPin.cs | 116 ++++++++++++++++++ 3 files changed, 204 insertions(+), 5 deletions(-) create mode 100644 src/System.Device.Gpio.Tests/GpioPinTests.cs create mode 100644 src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs diff --git a/src/System.Device.Gpio.Tests/GpioPinTests.cs b/src/System.Device.Gpio.Tests/GpioPinTests.cs new file mode 100644 index 0000000000..2827e0abc0 --- /dev/null +++ b/src/System.Device.Gpio.Tests/GpioPinTests.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Text; +using Moq; +using Xunit; + +namespace System.Device.Gpio.Tests +{ + public class GpioPinTests : IDisposable + { + private const int PinNumber = 2; + private Mock _mockedGpioDriver; + + public GpioPinTests() + { + _mockedGpioDriver = new Mock(MockBehavior.Default); + _mockedGpioDriver.CallBase = true; + } + + public void Dispose() + { + _mockedGpioDriver.VerifyAll(); + } + + [Fact] + public void TestOpnPin() + { + // Arrange + var ctrl = new GpioController(PinNumberingScheme.Logical, _mockedGpioDriver.Object); + // Act + GpioPin pin = ctrl.OpenPin(PinNumber, PinMode.Input); + // Assert + Assert.True(pin.PinNumber == PinNumber); + Assert.True(pin.GetPinMode() == PinMode.Input); + } + + [Fact] + public void TestClosePin() + { + // Arrange + var ctrl = new GpioController(PinNumberingScheme.Logical, _mockedGpioDriver.Object); + // Act + GpioPin pin = ctrl.OpenPin(PinNumber, PinMode.Input); + ctrl.ClosePin(PinNumber); + // Assert + Assert.Throws(() => { var ret = pin.Read(); }); + } + + [Fact] + public void TestDisposePin() + { + // Arrange + var ctrl = new GpioController(PinNumberingScheme.Logical, _mockedGpioDriver.Object); + // Act + GpioPin pin = ctrl.OpenPin(PinNumber, PinMode.Input); + pin.Dispose(); + // Assert + Assert.Throws(() => { var ret = pin.Read(); }); + // The pin should be close + Assert.False(ctrl.IsPinOpen(PinNumber)); + } + + [Fact] + public void TestToggleReadWrite() + { + // Arrange + var ctrl = new GpioController(PinNumberingScheme.Logical, _mockedGpioDriver.Object); + // Act + GpioPin pin = ctrl.OpenPin(PinNumber, PinMode.Input); + pin.Write(PinValue.High); + // Assert + Assert.True(pin.Read() == PinValue.High); + pin.Toggle(); + Assert.True(pin.Read() == PinValue.Low); + } + } +} diff --git a/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs b/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs index 88f70d21a4..88ac7b3616 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs @@ -88,7 +88,7 @@ protected virtual int GetLogicalPinNumber(int pinNumber) /// The driver attempts to open the pin without changing its mode or value. /// /// The pin number in the controller's numbering scheme. - public void OpenPin(int pinNumber) + public GpioPin OpenPin(int pinNumber) { if (IsPinOpen(pinNumber)) { @@ -97,6 +97,7 @@ public void OpenPin(int pinNumber) OpenPinCore(pinNumber); _openPins.TryAdd(pinNumber, null); + return new GpioPin(pinNumber, this); } /// @@ -114,10 +115,11 @@ protected virtual void OpenPinCore(int pinNumber) /// /// The pin number in the controller's numbering scheme. /// The mode to be set. - public void OpenPin(int pinNumber, PinMode mode) + public GpioPin OpenPin(int pinNumber, PinMode mode) { - OpenPin(pinNumber); + var pin = OpenPin(pinNumber); SetPinMode(pinNumber, mode); + return pin; } /// @@ -127,13 +129,14 @@ public void OpenPin(int pinNumber, PinMode mode) /// The mode to be set. /// The initial value to be set if the mode is output. The driver will attempt to set the mode without causing glitches to the other value. /// (if is , the pin should not glitch to low during open) - public void OpenPin(int pinNumber, PinMode mode, PinValue initialValue) + public GpioPin OpenPin(int pinNumber, PinMode mode, PinValue initialValue) { - OpenPin(pinNumber); + var pin = OpenPin(pinNumber); // Set the desired initial value _openPins[pinNumber] = initialValue; SetPinMode(pinNumber, mode); + return pin; } /// diff --git a/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs b/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs new file mode 100644 index 0000000000..40a197bbcc --- /dev/null +++ b/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs @@ -0,0 +1,116 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Device.Gpio +{ + /// + /// Represents a general-purpose I/O (GPIO) pin. + /// + public sealed class Gpio​Pin : IDisposable + { + private readonly int _pinNumber; + private readonly GpioController _controller; + private bool _disposedValue; + + internal Gpio​Pin(int pinNumber, GpioController controller) + { + _controller = controller; + _pinNumber = pinNumber; + } + + /// + /// Gets the pin number of the general-purpose I/O (GPIO) pin. + /// + /// + /// The pin number of the GPIO pin. + /// + public int PinNumber + { + get + { + return _pinNumber; + } + } + + /// + /// Gets the current pin mode for the general-purpose I/O (GPIO) pin. The pin mode specifies whether the pin is configured as an input or an output, and determines how values are driven onto the pin. + /// + /// An enumeration value that indicates the current pin mode for the GPIO pin. + /// The pin mode specifies whether the pin is configured as an input or an output, and determines how values are driven onto the pin. + public PinMode GetPinMode() => _controller.GetPinMode(_pinNumber); + + /// + /// Gets whether the general-purpose I/O (GPIO) pin supports the specified pin mode. + /// + /// The pin mode that you want to check for support. + /// + /// if the GPIO pin supports the pin mode that pinMode specifies; otherwise false. + /// If you specify a pin mode for which this method returns when you call , generates an exception. + /// + public bool IsPinModeSupported(PinMode pinMode) => _controller.IsPinModeSupported(_pinNumber, pinMode); + + /// + /// Sets the pin mode of the general-purpose I/O (GPIO) pin. + /// The pin mode specifies whether the pin is configured as an input or an output, and determines how values are driven onto the pin. + /// + /// An enumeration value that specifies pin mode to use for the GPIO pin. + /// The pin mode specifies whether the pin is configured as an input or an output, and determines how values are driven onto the pin. + /// The GPIO pin does not support the specified pin mode. + public void SetPinMode(PinMode value) => _controller.SetPinMode(_pinNumber, value); + + /// + /// Reads the current value of the general-purpose I/O (GPIO) pin. + /// + /// The current value of the GPIO pin. If the pin is configured as an output, this value is the last value written to the pin. + public PinValue Read() => _controller.Read(_pinNumber); + + /// + /// Drives the specified value onto the general purpose I/O (GPIO) pin according to the current pin mode for the pin + /// if the pin is configured as an output, or updates the latched output value for the pin if the pin is configured as an input. + /// + /// The enumeration value to write to the GPIO pin. + /// If the GPIO pin is configured as an output, the method drives the specified value onto the pin according to the current pin mode for the pin. + /// If the GPIO pin is configured as an input, the method updates the latched output value for the pin. The latched output value is driven onto the pin when the configuration for the pin changes to output. + /// + /// This exception will be thrown on an attempt to write to a pin that hasn't been opened or is not configured as output. + public void Write(PinValue value) => _controller.Write(_pinNumber, value); + + /// + /// Occurs when the value of the general-purpose I/O (GPIO) pin changes, either because of an external stimulus when the pin is configured as an input, or when a value is written to the pin when the pin in configured as an output. + /// + public event PinChangeEventHandler ValueChanged + { + add + { + if (_disposedValue) + { + throw new ObjectDisposedException(nameof(GpioPin)); + } + + _controller.RegisterCallbackForPinValueChangedEvent(_pinNumber, PinEventTypes.Falling | PinEventTypes.Rising, value); + } + + remove + { + if (_disposedValue) + { + throw new ObjectDisposedException(nameof(GpioPin)); + } + + _controller.UnregisterCallbackForPinValueChangedEvent(_pinNumber, value); + } + } + + /// + /// Toggles the output of the general purpose I/O (GPIO) pin if the pin is configured as an output. + /// + public void Toggle() => _controller.Write(_pinNumber, !_controller.Read(_pinNumber)); + + /// + public void Dispose() + { + _controller.ClosePin(_pinNumber); + _disposedValue = true; + } + } +} From 13428160b486ca8d1ab61fd08a8f755f7f14eea5 Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Mon, 22 Aug 2022 11:49:36 +0200 Subject: [PATCH 02/16] adjusting based on PR feedback --- src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs b/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs index 40a197bbcc..3f314c8a36 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs @@ -6,7 +6,7 @@ namespace System.Device.Gpio /// /// Represents a general-purpose I/O (GPIO) pin. /// - public sealed class Gpio​Pin : IDisposable + public class Gpio​Pin : IDisposable { private readonly int _pinNumber; private readonly GpioController _controller; @@ -24,13 +24,7 @@ public sealed class Gpio​Pin : IDisposable /// /// The pin number of the GPIO pin. /// - public int PinNumber - { - get - { - return _pinNumber; - } - } + public int PinNumber => _pinNumber; /// /// Gets the current pin mode for the general-purpose I/O (GPIO) pin. The pin mode specifies whether the pin is configured as an input or an output, and determines how values are driven onto the pin. From 357820c1c4a69dd798ac29b4f4ffe29a6a723254 Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Sat, 10 Sep 2022 16:50:55 +0200 Subject: [PATCH 03/16] Adjusting to have natively drivers. --- src/System.Device.Gpio.Tests/GpioPinTests.cs | 18 ++------ .../System/Device/Gpio/GpioController.cs | 2 +- .../System/Device/Gpio/GpioPin.cs | 42 ++++++------------- 3 files changed, 16 insertions(+), 46 deletions(-) diff --git a/src/System.Device.Gpio.Tests/GpioPinTests.cs b/src/System.Device.Gpio.Tests/GpioPinTests.cs index 2827e0abc0..2bbfc40138 100644 --- a/src/System.Device.Gpio.Tests/GpioPinTests.cs +++ b/src/System.Device.Gpio.Tests/GpioPinTests.cs @@ -46,21 +46,9 @@ public void TestClosePin() GpioPin pin = ctrl.OpenPin(PinNumber, PinMode.Input); ctrl.ClosePin(PinNumber); // Assert - Assert.Throws(() => { var ret = pin.Read(); }); - } - - [Fact] - public void TestDisposePin() - { - // Arrange - var ctrl = new GpioController(PinNumberingScheme.Logical, _mockedGpioDriver.Object); - // Act - GpioPin pin = ctrl.OpenPin(PinNumber, PinMode.Input); - pin.Dispose(); - // Assert - Assert.Throws(() => { var ret = pin.Read(); }); - // The pin should be close - Assert.False(ctrl.IsPinOpen(PinNumber)); + // This should work even if the pin is closed in the controller as the driver has no idea + // Of the controller behavior. + var ret = pin.Read(); } [Fact] diff --git a/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs b/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs index 99a7524405..1095f6731e 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs @@ -96,7 +96,7 @@ public GpioPin OpenPin(int pinNumber) OpenPinCore(pinNumber); _openPins.TryAdd(pinNumber, null); - return new GpioPin(pinNumber, this); + return new GpioPin(pinNumber, _driver); } /// diff --git a/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs b/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs index 3f314c8a36..949b8fde68 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs @@ -6,15 +6,14 @@ namespace System.Device.Gpio /// /// Represents a general-purpose I/O (GPIO) pin. /// - public class Gpio​Pin : IDisposable + public class Gpio​Pin { private readonly int _pinNumber; - private readonly GpioController _controller; - private bool _disposedValue; + private readonly GpioDriver _driver; - internal Gpio​Pin(int pinNumber, GpioController controller) + internal Gpio​Pin(int pinNumber, GpioDriver driver) { - _controller = controller; + _driver = driver; _pinNumber = pinNumber; } @@ -31,7 +30,7 @@ public class Gpio​Pin : IDisposable /// /// An enumeration value that indicates the current pin mode for the GPIO pin. /// The pin mode specifies whether the pin is configured as an input or an output, and determines how values are driven onto the pin. - public PinMode GetPinMode() => _controller.GetPinMode(_pinNumber); + public PinMode GetPinMode() => _driver.GetPinMode(_pinNumber); /// /// Gets whether the general-purpose I/O (GPIO) pin supports the specified pin mode. @@ -41,7 +40,7 @@ public class Gpio​Pin : IDisposable /// if the GPIO pin supports the pin mode that pinMode specifies; otherwise false. /// If you specify a pin mode for which this method returns when you call , generates an exception. /// - public bool IsPinModeSupported(PinMode pinMode) => _controller.IsPinModeSupported(_pinNumber, pinMode); + public bool IsPinModeSupported(PinMode pinMode) => _driver.IsPinModeSupported(_pinNumber, pinMode); /// /// Sets the pin mode of the general-purpose I/O (GPIO) pin. @@ -50,13 +49,13 @@ public class Gpio​Pin : IDisposable /// An enumeration value that specifies pin mode to use for the GPIO pin. /// The pin mode specifies whether the pin is configured as an input or an output, and determines how values are driven onto the pin. /// The GPIO pin does not support the specified pin mode. - public void SetPinMode(PinMode value) => _controller.SetPinMode(_pinNumber, value); + public void SetPinMode(PinMode value) => _driver.SetPinMode(_pinNumber, value); /// /// Reads the current value of the general-purpose I/O (GPIO) pin. /// /// The current value of the GPIO pin. If the pin is configured as an output, this value is the last value written to the pin. - public PinValue Read() => _controller.Read(_pinNumber); + public PinValue Read() => _driver.Read(_pinNumber); /// /// Drives the specified value onto the general purpose I/O (GPIO) pin according to the current pin mode for the pin @@ -67,7 +66,7 @@ public class Gpio​Pin : IDisposable /// If the GPIO pin is configured as an input, the method updates the latched output value for the pin. The latched output value is driven onto the pin when the configuration for the pin changes to output. /// /// This exception will be thrown on an attempt to write to a pin that hasn't been opened or is not configured as output. - public void Write(PinValue value) => _controller.Write(_pinNumber, value); + public void Write(PinValue value) => _driver.Write(_pinNumber, value); /// /// Occurs when the value of the general-purpose I/O (GPIO) pin changes, either because of an external stimulus when the pin is configured as an input, or when a value is written to the pin when the pin in configured as an output. @@ -76,35 +75,18 @@ public event PinChangeEventHandler ValueChanged { add { - if (_disposedValue) - { - throw new ObjectDisposedException(nameof(GpioPin)); - } - - _controller.RegisterCallbackForPinValueChangedEvent(_pinNumber, PinEventTypes.Falling | PinEventTypes.Rising, value); + _driver.AddCallbackForPinValueChangedEvent(_pinNumber, PinEventTypes.Falling | PinEventTypes.Rising, value); } remove { - if (_disposedValue) - { - throw new ObjectDisposedException(nameof(GpioPin)); - } - - _controller.UnregisterCallbackForPinValueChangedEvent(_pinNumber, value); + _driver.RemoveCallbackForPinValueChangedEvent(_pinNumber, value); } } /// /// Toggles the output of the general purpose I/O (GPIO) pin if the pin is configured as an output. /// - public void Toggle() => _controller.Write(_pinNumber, !_controller.Read(_pinNumber)); - - /// - public void Dispose() - { - _controller.ClosePin(_pinNumber); - _disposedValue = true; - } + public void Toggle() => _driver.Write(_pinNumber, !_driver.Read(_pinNumber)); } } From 252c22706475a63921bc8998c107daf467d855b3 Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Sun, 11 Sep 2022 22:27:39 +0200 Subject: [PATCH 04/16] make methods virtual --- .../System/Device/Gpio/GpioPin.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs b/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs index 949b8fde68..f5ded82f43 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs @@ -23,14 +23,14 @@ public class Gpio​Pin /// /// The pin number of the GPIO pin. /// - public int PinNumber => _pinNumber; + public virtual int PinNumber => _pinNumber; /// /// Gets the current pin mode for the general-purpose I/O (GPIO) pin. The pin mode specifies whether the pin is configured as an input or an output, and determines how values are driven onto the pin. /// /// An enumeration value that indicates the current pin mode for the GPIO pin. /// The pin mode specifies whether the pin is configured as an input or an output, and determines how values are driven onto the pin. - public PinMode GetPinMode() => _driver.GetPinMode(_pinNumber); + public virtual PinMode GetPinMode() => _driver.GetPinMode(_pinNumber); /// /// Gets whether the general-purpose I/O (GPIO) pin supports the specified pin mode. @@ -40,7 +40,7 @@ public class Gpio​Pin /// if the GPIO pin supports the pin mode that pinMode specifies; otherwise false. /// If you specify a pin mode for which this method returns when you call , generates an exception. /// - public bool IsPinModeSupported(PinMode pinMode) => _driver.IsPinModeSupported(_pinNumber, pinMode); + public virtual bool IsPinModeSupported(PinMode pinMode) => _driver.IsPinModeSupported(_pinNumber, pinMode); /// /// Sets the pin mode of the general-purpose I/O (GPIO) pin. @@ -49,13 +49,13 @@ public class Gpio​Pin /// An enumeration value that specifies pin mode to use for the GPIO pin. /// The pin mode specifies whether the pin is configured as an input or an output, and determines how values are driven onto the pin. /// The GPIO pin does not support the specified pin mode. - public void SetPinMode(PinMode value) => _driver.SetPinMode(_pinNumber, value); + public virtual void SetPinMode(PinMode value) => _driver.SetPinMode(_pinNumber, value); /// /// Reads the current value of the general-purpose I/O (GPIO) pin. /// /// The current value of the GPIO pin. If the pin is configured as an output, this value is the last value written to the pin. - public PinValue Read() => _driver.Read(_pinNumber); + public virtual PinValue Read() => _driver.Read(_pinNumber); /// /// Drives the specified value onto the general purpose I/O (GPIO) pin according to the current pin mode for the pin @@ -66,12 +66,12 @@ public class Gpio​Pin /// If the GPIO pin is configured as an input, the method updates the latched output value for the pin. The latched output value is driven onto the pin when the configuration for the pin changes to output. /// /// This exception will be thrown on an attempt to write to a pin that hasn't been opened or is not configured as output. - public void Write(PinValue value) => _driver.Write(_pinNumber, value); + public virtual void Write(PinValue value) => _driver.Write(_pinNumber, value); /// /// Occurs when the value of the general-purpose I/O (GPIO) pin changes, either because of an external stimulus when the pin is configured as an input, or when a value is written to the pin when the pin in configured as an output. /// - public event PinChangeEventHandler ValueChanged + public virtual event PinChangeEventHandler ValueChanged { add { @@ -87,6 +87,6 @@ public event PinChangeEventHandler ValueChanged /// /// Toggles the output of the general purpose I/O (GPIO) pin if the pin is configured as an output. /// - public void Toggle() => _driver.Write(_pinNumber, !_driver.Read(_pinNumber)); + public virtual void Toggle() => _driver.Write(_pinNumber, !_driver.Read(_pinNumber)); } } From 5cd6c352263a50baa93c58031f890dd55e6a9a97 Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Fri, 11 Nov 2022 15:25:31 +0100 Subject: [PATCH 05/16] adding Toggle to System.Device drivers --- .../Device/Gpio/Drivers/HummingBoardDriver.cs | 3 +++ .../Device/Gpio/Drivers/LibGpiodDriver.cs | 14 +++++++++++ .../Device/Gpio/Drivers/RaspberryPi3Driver.cs | 3 +++ .../Gpio/Drivers/RaspberryPi3LinuxDriver.cs | 7 ++++++ .../System/Device/Gpio/Drivers/SysFsDriver.cs | 10 ++++++++ .../Device/Gpio/Drivers/Windows10Driver.cs | 20 ++++++++++++++-- .../Drivers/Windows10Driver.notSupported.cs | 6 +++++ .../System/Device/Gpio/GpioController.cs | 23 +++++++++++++++++-- .../System/Device/Gpio/GpioDriver.cs | 6 +++++ .../System/Device/Gpio/GpioPin.cs | 2 +- 10 files changed, 89 insertions(+), 5 deletions(-) diff --git a/src/System.Device.Gpio/System/Device/Gpio/Drivers/HummingBoardDriver.cs b/src/System.Device.Gpio/System/Device/Gpio/Drivers/HummingBoardDriver.cs index 28b43a13fe..32172d05da 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/Drivers/HummingBoardDriver.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/Drivers/HummingBoardDriver.cs @@ -82,6 +82,9 @@ protected internal override PinMode GetPinMode(int pinNumber) /// protected internal override PinValue Read(int pinNumber) => _internalDriver.Read(pinNumber); + /// + protected internal override void Toggle(int pinNumber) => _internalDriver.Read(pinNumber); + /// protected internal override void RemoveCallbackForPinValueChangedEvent(int pinNumber, PinChangeEventHandler callback) => _internalDriver.RemoveCallbackForPinValueChangedEvent(pinNumber, callback); diff --git a/src/System.Device.Gpio/System/Device/Gpio/Drivers/LibGpiodDriver.cs b/src/System.Device.Gpio/System/Device/Gpio/Drivers/LibGpiodDriver.cs index 20c09caeda..c33fa16e0d 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/Drivers/LibGpiodDriver.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/Drivers/LibGpiodDriver.cs @@ -6,6 +6,7 @@ using System.Runtime.InteropServices; using System.Collections.Concurrent; using System.Diagnostics; +using System.Net.NetworkInformation; namespace System.Device.Gpio.Drivers; @@ -21,6 +22,7 @@ public class LibGpiodDriver : UnixDriver private readonly ConcurrentDictionary _pinNumberToSafeLineHandle; private readonly ConcurrentDictionary _pinNumberToEventHandler; private readonly int _pinCount; + private readonly ConcurrentDictionary _pinValue; private SafeChipHandle _chip; /// @@ -76,6 +78,7 @@ public LibGpiodDriver(int gpioChip = 0) _pinCount = Interop.libgpiod.gpiod_chip_num_lines(_chip); _pinNumberToEventHandler = new ConcurrentDictionary(); _pinNumberToSafeLineHandle = new ConcurrentDictionary(); + _pinValue = new ConcurrentDictionary(); } catch (DllNotFoundException) { @@ -134,6 +137,7 @@ protected internal override void ClosePin(int pinNumber) pinHandle?.Dispose(); // We know this works _pinNumberToSafeLineHandle.TryRemove(pinNumber, out _); + _pinValue.TryRemove(pinNumber, out _); } } } @@ -211,6 +215,9 @@ protected internal override void OpenPin(int pinNumber) } _pinNumberToSafeLineHandle.TryAdd(pinNumber, pinHandle); + // This is setting up a default value without reading the driver as it's the default behavior. + // If the Setmode with an initial value is used, this is going to be corrected automatically + _pinValue.TryAdd(pinNumber, PinValue.Low); } } @@ -225,12 +232,16 @@ protected internal override PinValue Read(int pinNumber) throw ExceptionHelper.GetIOException(ExceptionResource.ReadPinError, Marshal.GetLastWin32Error(), pinNumber); } + _pinValue[pinNumber] = result; return result; } throw ExceptionHelper.GetInvalidOperationException(ExceptionResource.PinNotOpenedError, pin: pinNumber); } + /// + protected internal override void Toggle(int pinNumber) => Write(pinNumber, !_pinValue[pinNumber]); + /// protected internal override void RemoveCallbackForPinValueChangedEvent(int pinNumber, PinChangeEventHandler callback) { @@ -307,6 +318,7 @@ protected internal override void SetPinMode(int pinNumber, PinMode mode, PinValu pinNumber); } + _pinValue[pinNumber] = initialValue; pinHandle.PinMode = mode; return; } @@ -372,6 +384,7 @@ protected internal override void Write(int pinNumber, PinValue value) } Interop.libgpiod.gpiod_line_set_value(pinHandle, (value == PinValue.High) ? 1 : 0); + _pinValue[pinNumber] = value; } /// @@ -399,6 +412,7 @@ protected override void Dispose(bool disposing) } _pinNumberToSafeLineHandle.Clear(); + _pinValue.Clear(); } _chip?.Dispose(); diff --git a/src/System.Device.Gpio/System/Device/Gpio/Drivers/RaspberryPi3Driver.cs b/src/System.Device.Gpio/System/Device/Gpio/Drivers/RaspberryPi3Driver.cs index b25d7522fa..27fb42c02d 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/Drivers/RaspberryPi3Driver.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/Drivers/RaspberryPi3Driver.cs @@ -198,6 +198,9 @@ protected internal override int ConvertPinNumberToLogicalNumberingScheme(int pin /// protected internal override PinValue Read(int pinNumber) => InternalDriver.Read(pinNumber); + /// + protected internal override void Toggle(int pinNumber) => InternalDriver.Toggle(pinNumber); + /// protected internal override void RemoveCallbackForPinValueChangedEvent(int pinNumber, PinChangeEventHandler callback) => InternalDriver.RemoveCallbackForPinValueChangedEvent(pinNumber, callback); diff --git a/src/System.Device.Gpio/System/Device/Gpio/Drivers/RaspberryPi3LinuxDriver.cs b/src/System.Device.Gpio/System/Device/Gpio/Drivers/RaspberryPi3LinuxDriver.cs index 57e60ba52b..19f443caf0 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/Drivers/RaspberryPi3LinuxDriver.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/Drivers/RaspberryPi3LinuxDriver.cs @@ -175,6 +175,13 @@ protected internal unsafe override PinValue Read(int pinNumber) return Convert.ToBoolean((register >> (pinNumber % 32)) & 1) ? PinValue.High : PinValue.Low; } + /// + protected internal override void Toggle(int pinNumber) + { + ValidatePinNumber(pinNumber); + _interruptDriver!.Toggle(pinNumber); + } + /// /// Removes a handler for a pin value changed event. /// diff --git a/src/System.Device.Gpio/System/Device/Gpio/Drivers/SysFsDriver.cs b/src/System.Device.Gpio/System/Device/Gpio/Drivers/SysFsDriver.cs index 508893d022..4330314f19 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/Drivers/SysFsDriver.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/Drivers/SysFsDriver.cs @@ -28,6 +28,7 @@ public class SysFsDriver : UnixDriver private readonly List _exportedPins = new List(); private readonly Dictionary _devicePins = new Dictionary(); + private readonly Dictionary _pinValues = new Dictionary(); private TimeSpan _statusUpdateSleepTime = TimeSpan.FromMilliseconds(1); private int _pollFileDescriptor = -1; private Thread? _eventDetectionThread; @@ -118,6 +119,8 @@ protected internal override void OpenPin(int pinNumber) SysFsHelpers.EnsureReadWriteAccessToPath(pinPath); _exportedPins.Add(pinNumber); + // Default value is low, otherwise it's the set pin mode with default value that will override this + _pinValues.Add(pinNumber, PinValue.Low); } catch (UnauthorizedAccessException e) { @@ -145,6 +148,7 @@ protected internal override void ClosePin(int pinNumber) { _devicePins[pinNumber].Dispose(); _devicePins.Remove(pinNumber); + _pinValues.Remove(pinNumber); } // If this controller wasn't the one that opened the pin, then Remove will return false, so we don't need to close it. @@ -248,9 +252,13 @@ protected internal override PinValue Read(int pinNumber) throw new InvalidOperationException("There was an attempt to read from a pin that is not open."); } + _pinValues[pinNumber] = result; return result; } + /// + protected internal override void Toggle(int pinNumber) => Write(pinNumber, !_pinValues[pinNumber]); + private PinValue ConvertSysFsValueToPinValue(string value) { return value.Trim() switch @@ -275,6 +283,7 @@ protected internal override void Write(int pinNumber, PinValue value) { string sysFsValue = ConvertPinValueToSysFs(value); File.WriteAllText(valuePath, sysFsValue); + _pinValues[pinNumber] = value; } catch (UnauthorizedAccessException e) { @@ -588,6 +597,7 @@ protected override void Dispose(bool disposing) } _devicePins.Clear(); + _pinValues.Clear(); if (_pollFileDescriptor != -1) { Interop.close(_pollFileDescriptor); diff --git a/src/System.Device.Gpio/System/Device/Gpio/Drivers/Windows10Driver.cs b/src/System.Device.Gpio/System/Device/Gpio/Drivers/Windows10Driver.cs index eded34669b..6a5755a759 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/Drivers/Windows10Driver.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/Drivers/Windows10Driver.cs @@ -14,6 +14,7 @@ public class Windows10Driver : GpioDriver { private static readonly WinGpio.GpioController s_winGpioController = WinGpio.GpioController.GetDefault(); private readonly Dictionary _openPins = new Dictionary(); + private readonly Dictionary _pinValues = new Dictionary(); /// /// Initializes a new instance of the class. @@ -68,6 +69,7 @@ protected internal override void ClosePin(int pinNumber) { pin.ClosePin(); _openPins.Remove(pinNumber); + _pinValues.Remove(pinNumber); } } @@ -113,6 +115,8 @@ protected internal override void OpenPin(int pinNumber) WinGpio.GpioPin windowsPin = s_winGpioController.OpenPin(pinNumber, WinGpio.GpioSharingMode.Exclusive); _openPins[pinNumber] = new Windows10DriverPin(this, windowsPin); + // Default value, override by the set pin mode with another default value + _pinValues[pinNumber] = PinValue.Low; } /// @@ -120,7 +124,15 @@ protected internal override void OpenPin(int pinNumber) /// /// The pin number in the driver's logical numbering scheme. /// The value of the pin. - protected internal override PinValue Read(int pinNumber) => LookupOpenPin(pinNumber).Read(); + protected internal override PinValue Read(int pinNumber) + { + var value = LookupOpenPin(pinNumber).Read(); + _pinValues[pinNumber] = value; + return value; + } + + /// + protected internal override void Toggle(int pinNumber) => Write(pinNumber, !_pinValues[pinNumber]); /// /// Removes a handler for a pin value changed event. @@ -152,7 +164,11 @@ protected internal override WaitForEventResult WaitForEvent(int pinNumber, PinEv /// /// The pin number in the driver's logical numbering scheme. /// The value to be written to the pin. - protected internal override void Write(int pinNumber, PinValue value) => LookupOpenPin(pinNumber).Write(value); + protected internal override void Write(int pinNumber, PinValue value) + { + LookupOpenPin(pinNumber).Write(value); + _pinValues[pinNumber] = value; + } /// /// Lookups an open pin in the driver. diff --git a/src/System.Device.Gpio/System/Device/Gpio/Drivers/Windows10Driver.notSupported.cs b/src/System.Device.Gpio/System/Device/Gpio/Drivers/Windows10Driver.notSupported.cs index cfab9d122d..ca4ef02240 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/Drivers/Windows10Driver.notSupported.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/Drivers/Windows10Driver.notSupported.cs @@ -66,6 +66,12 @@ protected internal override PinValue Read(int pinNumber) throw new PlatformNotSupportedException(); } + /// + protected internal override void Toggle(int pinNumber) + { + throw new PlatformNotSupportedException(); + } + /// protected internal override void RemoveCallbackForPinValueChangedEvent(int pinNumber, PinChangeEventHandler callback) { diff --git a/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs b/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs index 1095f6731e..4d0c598bc5 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs @@ -30,6 +30,7 @@ public class GpioController : IDisposable /// If a pin element exists, that pin is open. Uses current controller's numbering scheme /// private readonly ConcurrentDictionary _openPins; + private readonly ConcurrentDictionary _gpioPins; private GpioDriver _driver; /// @@ -50,6 +51,7 @@ public GpioController(PinNumberingScheme numberingScheme, GpioDriver driver) _driver = driver; NumberingScheme = numberingScheme; _openPins = new ConcurrentDictionary(); + _gpioPins = new ConcurrentDictionary(); } /// @@ -91,12 +93,13 @@ public GpioPin OpenPin(int pinNumber) { if (IsPinOpen(pinNumber)) { - throw new InvalidOperationException($"Pin {pinNumber} is already open."); + return _gpioPins[pinNumber]; } OpenPinCore(pinNumber); _openPins.TryAdd(pinNumber, null); - return new GpioPin(pinNumber, _driver); + _gpioPins[pinNumber] = new GpioPin(pinNumber, _driver); + return _gpioPins[pinNumber]; } /// @@ -162,6 +165,7 @@ protected virtual void ClosePinCore(int pinNumber) { int logicalPinNumber = GetLogicalPinNumber(pinNumber); _driver.ClosePin(logicalPinNumber); + _gpioPins.TryRemove(pinNumber, out _); } /// @@ -246,6 +250,20 @@ public virtual PinValue Read(int pinNumber) return _driver.Read(logicalPinNumber); } + /// + /// Toggle the current value of a pin. + /// + /// The pin number in the controller's numbering scheme. + public virtual void Toggle(int pinNumber) + { + if (!IsPinOpen(pinNumber)) + { + throw new InvalidOperationException($"Can not read from pin {pinNumber} because it is not open."); + } + + _driver.Toggle(pinNumber); + } + /// /// Writes a value to a pin. /// @@ -378,6 +396,7 @@ protected virtual void Dispose(bool disposing) } _openPins.Clear(); + _gpioPins.Clear(); _driver?.Dispose(); _driver = null!; } diff --git a/src/System.Device.Gpio/System/Device/Gpio/GpioDriver.cs b/src/System.Device.Gpio/System/Device/Gpio/GpioDriver.cs index 5fb772c5db..7b2cf762b8 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/GpioDriver.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/GpioDriver.cs @@ -82,6 +82,12 @@ protected internal virtual void SetPinMode(int pinNumber, PinMode mode, PinValue /// The value of the pin. protected internal abstract PinValue Read(int pinNumber); + /// + /// Toggle the current value of a pin. + /// + /// The pin number in the driver's logical numbering scheme. + protected internal abstract void Toggle(int pinNumber); + /// /// Writes a value to a pin. /// diff --git a/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs b/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs index f5ded82f43..2bef40424c 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs @@ -87,6 +87,6 @@ public virtual event PinChangeEventHandler ValueChanged /// /// Toggles the output of the general purpose I/O (GPIO) pin if the pin is configured as an output. /// - public virtual void Toggle() => _driver.Write(_pinNumber, !_driver.Read(_pinNumber)); + public virtual void Toggle() => _driver.Toggle(_pinNumber); } } From 811730d4133a63f385a1d4c78336b89dcee71cb0 Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Fri, 11 Nov 2022 16:40:18 +0100 Subject: [PATCH 06/16] Board and Mcp23xxx adjustments --- src/devices/Board/DummyGpioDriver.cs | 6 +++ src/devices/Board/KeyboardGpioDriver.cs | 48 ++++++++++++++-------- src/devices/Mcp23xxx/Mcp23xxx.cs | 17 +++++++- src/devices/Mcp23xxx/tests/Mcp23xxxTest.cs | 2 + 4 files changed, 55 insertions(+), 18 deletions(-) diff --git a/src/devices/Board/DummyGpioDriver.cs b/src/devices/Board/DummyGpioDriver.cs index 9ff1ec4fcc..f4ecef4b65 100644 --- a/src/devices/Board/DummyGpioDriver.cs +++ b/src/devices/Board/DummyGpioDriver.cs @@ -65,6 +65,12 @@ protected override PinValue Read(int pinNumber) throw new NotSupportedException("No such pin"); } + /// + protected override void Toggle(int pinNumber) + { + throw new NotSupportedException("No such pin"); + } + /// protected override void Write(int pinNumber, PinValue value) { diff --git a/src/devices/Board/KeyboardGpioDriver.cs b/src/devices/Board/KeyboardGpioDriver.cs index b689e53767..5288e1742d 100644 --- a/src/devices/Board/KeyboardGpioDriver.cs +++ b/src/devices/Board/KeyboardGpioDriver.cs @@ -26,6 +26,7 @@ private enum LedKey } private const int SupportedPinCount = 256; + private readonly Dictionary _pinValues = new Dictionary(); private KeyState[] _state; @@ -42,6 +43,10 @@ public KeyboardGpioDriver() _pollThread = null; _terminateThread = true; + foreach (var key in Enum.GetValues(typeof(LedKey))) + { + _pinValues.Add((int)key!, GetLedState((LedKey)key).KeyValue); + } } protected override int PinCount @@ -111,6 +116,27 @@ private bool IsKeyPressed(ConsoleKey key) } private void SetLedState(LedKey key, PinValue state) + { + (int virtualKey, int currentKeyState) = GetLedState(key); + _pinValues[(int)key] = state; + if ((state == PinValue.High && currentKeyState == 0) || + (state == PinValue.Low && currentKeyState != 0)) + { + // Simulate a key press + Interop.keybd_event((byte)virtualKey, + 0x45, + Interop.KEYEVENTF_EXTENDEDKEY | 0, + IntPtr.Zero); + + // Simulate a key release + Interop.keybd_event((byte)virtualKey, + 0x45, + Interop.KEYEVENTF_EXTENDEDKEY | Interop.KEYEVENTF_KEYUP, + IntPtr.Zero); + } + } + + private (int VirtualKey, int KeyValue) GetLedState(LedKey key) { int virtualKey = 0; if (key == LedKey.NumLock) @@ -131,22 +157,7 @@ private void SetLedState(LedKey key, PinValue state) } // Bit 1 indicates whether the LED is currently on or off (or, whether Scroll lock, num lock, caps lock is on) - int currentKeyState = Interop.GetKeyState(virtualKey) & 1; - if ((state == PinValue.High && currentKeyState == 0) || - (state == PinValue.Low && currentKeyState != 0)) - { - // Simulate a key press - Interop.keybd_event((byte)virtualKey, - 0x45, - Interop.KEYEVENTF_EXTENDEDKEY | 0, - IntPtr.Zero); - - // Simulate a key release - Interop.keybd_event((byte)virtualKey, - 0x45, - Interop.KEYEVENTF_EXTENDEDKEY | Interop.KEYEVENTF_KEYUP, - IntPtr.Zero); - } + return (virtualKey, Interop.GetKeyState(virtualKey) & 1); } protected override PinValue Read(int pinNumber) @@ -162,6 +173,8 @@ protected override PinValue Read(int pinNumber) } } + protected override void Toggle(int pinNumber) => Write(pinNumber, !_pinValues[pinNumber]); + protected override void Write(int pinNumber, PinValue value) { if (pinNumber == 0) @@ -192,7 +205,8 @@ protected override WaitForEventResult WaitForEvent(int pinNumber, PinEventTypes { return new WaitForEventResult() { - EventTypes = PinEventTypes.Rising, TimedOut = false + EventTypes = PinEventTypes.Rising, + TimedOut = false }; } else if (eventTypes == PinEventTypes.Falling && newState == PinValue.Low) diff --git a/src/devices/Mcp23xxx/Mcp23xxx.cs b/src/devices/Mcp23xxx/Mcp23xxx.cs index 20c260c330..37a5f73f13 100644 --- a/src/devices/Mcp23xxx/Mcp23xxx.cs +++ b/src/devices/Mcp23xxx/Mcp23xxx.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; using System.Device.Gpio; using System.Runtime.CompilerServices; using System.Threading; @@ -16,6 +17,7 @@ public abstract partial class Mcp23xxx : GpioDriver private readonly int _reset; private readonly int _interruptA; private readonly int _interruptB; + private readonly Dictionary _pinValues = new Dictionary(); private BankStyle _bankStyle; private GpioController? _controller; private bool _shouldDispose; @@ -243,6 +245,7 @@ protected override void Dispose(bool disposing) _controller = null; } + _pinValues.Clear(); _bus?.Dispose(); _bus = null!; base.Dispose(disposing); @@ -359,9 +362,13 @@ protected override PinValue Read(int pinNumber) new PinValuePair(pinNumber, default) }; Read(pinValuePairs); - return pinValuePairs[0].PinValue; + _pinValues[pinNumber] = pinValuePairs[0].PinValue; + return _pinValues[pinNumber]; } + /// + protected override void Toggle(int pinNumber) => Write(pinNumber, !_pinValues[pinNumber]); + /// /// Reads the value of a set of pins /// @@ -394,6 +401,7 @@ protected void Read(Span pinValuePairs) { int pin = pinValuePairs[i].PinNumber; pinValuePairs[i] = new PinValuePair(pin, result & (1 << pin)); + _pinValues[pin] = pinValuePairs[i].PinValue; } } @@ -410,6 +418,7 @@ protected override void Write(int pinNumber, PinValue value) new PinValuePair(pinNumber, value) }; Write(pinValuePairs); + _pinValues[pinNumber] = value; } /// @@ -452,6 +461,10 @@ protected void Write(ReadOnlySpan pinValuePairs) } _gpioCache = newValue; + foreach (PinValuePair pinValuePair in pinValuePairs) + { + _pinValues[pinValuePair.PinNumber] = pinValuePair.PinValue; + } } private ushort SetBits(ushort current, ushort bits, ushort mask) @@ -519,12 +532,14 @@ private byte GetMappedAddress(Register register, Port port = Port.PortA, protected override void OpenPin(int pinNumber) { // No-op + _pinValues.TryAdd(pinNumber, PinValue.Low); } /// protected override void ClosePin(int pinNumber) { // No-op + _pinValues.Remove(pinNumber); } /// diff --git a/src/devices/Mcp23xxx/tests/Mcp23xxxTest.cs b/src/devices/Mcp23xxx/tests/Mcp23xxxTest.cs index 98a48e8d9b..e8f364981b 100644 --- a/src/devices/Mcp23xxx/tests/Mcp23xxxTest.cs +++ b/src/devices/Mcp23xxx/tests/Mcp23xxxTest.cs @@ -214,6 +214,8 @@ protected override PinValue Read(int pinNumber) return PinValue.Low; } + protected override void Toggle(int pinNumber) => Write(pinNumber, !Read(pinNumber)); + public void Read(Span pinValuePairs) { for (int i = 0; i < pinValuePairs.Length; i++) From b524ed4d3fa38743e3bbdd3d0758dddd26f79164 Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Fri, 11 Nov 2022 17:17:45 +0100 Subject: [PATCH 07/16] adding FT4222, FT232H and Pcx857x --- src/devices/Ft232H/Ft232HGpio.cs | 16 +++++++++++-- src/devices/Ft4222/Ft4222Gpio.cs | 13 ++++++++++- src/devices/Pcx857x/Pcx857x.cs | 29 ++++++++++++++++++------ src/devices/Pcx857x/tests/Pcx857xTest.cs | 2 ++ 4 files changed, 50 insertions(+), 10 deletions(-) diff --git a/src/devices/Ft232H/Ft232HGpio.cs b/src/devices/Ft232H/Ft232HGpio.cs index 03b92a649c..7fe5cd3a03 100644 --- a/src/devices/Ft232H/Ft232HGpio.cs +++ b/src/devices/Ft232H/Ft232HGpio.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; using System.Device.Gpio; using System.Linq; using System.Threading; @@ -13,6 +14,8 @@ namespace Iot.Device.Ft232H /// public class Ft232HGpio : GpioDriver { + private readonly Dictionary _pinValues = new Dictionary(); + /// /// Store the FTDI Device Information /// @@ -64,12 +67,14 @@ protected override void OpenPin(int pinNumber) } DeviceInformation.PinOpen[pinNumber] = true; + _pinValues.TryAdd(pinNumber, PinValue.Low); } /// protected override void ClosePin(int pinNumber) { DeviceInformation.PinOpen[pinNumber] = false; + _pinValues.Remove(pinNumber); } /// @@ -130,15 +135,20 @@ protected override PinValue Read(int pinNumber) if (pinNumber < 8) { var val = DeviceInformation.GetGpioValuesLow(); - return (((val >> pinNumber) & 0x01) == 0x01) ? PinValue.High : PinValue.Low; + _pinValues[pinNumber] = (((val >> pinNumber) & 0x01) == 0x01) ? PinValue.High : PinValue.Low; } else { var valhigh = DeviceInformation.GetGpioValuesHigh(); - return (((valhigh >> (pinNumber - 8)) & 0x01) == 0x01) ? PinValue.High : PinValue.Low; + _pinValues[pinNumber] = (((valhigh >> (pinNumber - 8)) & 0x01) == 0x01) ? PinValue.High : PinValue.Low; } + + return _pinValues[pinNumber]; } + /// + protected override void Toggle(int pinNumber) => Write(pinNumber, !_pinValues[pinNumber]); + /// protected override void Write(int pinNumber, PinValue value) { @@ -172,6 +182,8 @@ protected override void Write(int pinNumber, PinValue value) DeviceInformation.SetGpioValuesHigh(); } + + _pinValues[pinNumber] = value; } /// diff --git a/src/devices/Ft4222/Ft4222Gpio.cs b/src/devices/Ft4222/Ft4222Gpio.cs index 7108bdcf57..d7653d420c 100644 --- a/src/devices/Ft4222/Ft4222Gpio.cs +++ b/src/devices/Ft4222/Ft4222Gpio.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; using System.Device.Gpio; using System.IO; using System.Linq; @@ -18,6 +19,7 @@ public class Ft4222Gpio : GpioDriver { private const int PinCountConst = 4; + private readonly Dictionary _pinValues = new Dictionary(); private SafeFtHandle _ftHandle; private GpioPinMode[] _gpioDirections = new GpioPinMode[PinCountConst]; private GpioTrigger[] _gpioTriggers = new GpioTrigger[PinCountConst]; @@ -110,6 +112,7 @@ private void InitializeGpio(Ft4222Device device) /// protected override void ClosePin(int pinNumber) { + _pinValues.Remove(pinNumber); } /// @@ -124,6 +127,7 @@ protected override void ClosePin(int pinNumber) /// protected override void OpenPin(int pinNumber) { + _pinValues.TryAdd(pinNumber, PinValue.Low); } /// @@ -136,9 +140,13 @@ protected override PinValue Read(int pinNumber) throw new IOException($"{nameof(Read)}: failed to write GPIO, status: {status}"); } - return pinVal == GpioPinValue.High ? PinValue.High : PinValue.Low; + _pinValues[pinNumber] = pinVal == GpioPinValue.High ? PinValue.High : PinValue.Low; + return _pinValues[pinNumber]; } + /// + protected override void Toggle(int pinNumber) => Write(pinNumber, !_pinValues[pinNumber]); + /// protected override void AddCallbackForPinValueChangedEvent(int pinNumber, PinEventTypes eventTypes, PinChangeEventHandler callback) { @@ -265,12 +273,15 @@ protected override void Write(int pinNumber, PinValue value) { throw new IOException($"{nameof(Write)}: failed to write GPIO, status: {status}"); } + + _pinValues[pinNumber] = value; } /// protected override void Dispose(bool disposing) { _ftHandle.Dispose(); + _pinValues.Clear(); base.Dispose(disposing); } } diff --git a/src/devices/Pcx857x/Pcx857x.cs b/src/devices/Pcx857x/Pcx857x.cs index bba8caf91b..43acc5d208 100644 --- a/src/devices/Pcx857x/Pcx857x.cs +++ b/src/devices/Pcx857x/Pcx857x.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Device.Gpio; using System.Device.I2c; using System.Runtime.CompilerServices; @@ -22,13 +23,14 @@ public abstract class Pcx857x : GpioDriver protected I2cDevice Device { get; } private readonly GpioController? _controller; private readonly int _interrupt; + private readonly Dictionary _pinValues = new Dictionary(); private bool _shouldDispose; // Pin mode bits- 0 for input, 1 for output to match PinMode private ushort _pinModes; // Pin value bits, 1 for high - private ushort _pinValues; + private ushort _pinValueBits; /// /// Remote I/O expander for I2C-bus with interrupt. @@ -106,6 +108,7 @@ protected void InternalWriteUInt16(ushort value) protected override void ClosePin(int pinNumber) { // No-op + _pinValues.Remove(pinNumber); } /// @@ -117,6 +120,7 @@ protected override void Dispose(bool disposing) } Device.Dispose(); + _pinValues.Clear(); base.Dispose(disposing); } @@ -124,6 +128,7 @@ protected override void Dispose(bool disposing) protected override void OpenPin(int pinNumber) { // No-op + _pinValues.TryAdd(pinNumber, PinValue.Low); } /// @@ -134,9 +139,13 @@ protected override PinValue Read(int pinNumber) new PinValuePair(pinNumber, default) }; Read(values); - return values[0].PinValue; + _pinValues[pinNumber] = values[0].PinValue; + return _pinValues[pinNumber]; } + /// + protected override void Toggle(int pinNumber) => Write(pinNumber, !_pinValues[pinNumber]); + /// /// Reads multiple pins from the device /// @@ -161,6 +170,7 @@ public void Read(Span pinValues) { int pin = pinValues[i].PinNumber; pinValues[i] = new PinValuePair(pin, (data >> pin) & 1); + _pinValues[pin] = pinValues[i].PinValue; } } @@ -198,21 +208,21 @@ protected override void SetPinMode(int pinNumber, PinMode mode) throw new ArgumentOutOfRangeException(nameof(mode), "Only Input and Output modes are supported."); } - WritePins(_pinValues); + WritePins(_pinValueBits); } private void WritePins(ushort value) { // We need to set all input pins to high - _pinValues = (ushort)(value | ~_pinModes); + _pinValueBits = (ushort)(value | ~_pinModes); if (PinCount == 8) { - WriteByte((byte)_pinValues); + WriteByte((byte)_pinValueBits); } else { - InternalWriteUInt16(_pinValues); + InternalWriteUInt16(_pinValueBits); } } @@ -224,6 +234,7 @@ protected override void Write(int pinNumber, PinValue value) new PinValuePair(pinNumber, value) }; Write(values); + _pinValues[pinNumber] = value; } /// @@ -250,7 +261,11 @@ ushort SetBits(ushort current, ushort bits, ushort mask) return current; } - WritePins(SetBits(_pinValues, (ushort)values, (ushort)pins)); + WritePins(SetBits(_pinValueBits, (ushort)values, (ushort)pins)); + foreach (PinValuePair pinValuePair in pinValues) + { + _pinValues[pinValuePair.PinNumber] = pinValuePair.PinValue; + } } /// diff --git a/src/devices/Pcx857x/tests/Pcx857xTest.cs b/src/devices/Pcx857x/tests/Pcx857xTest.cs index e2fd4f05a7..41047f0462 100644 --- a/src/devices/Pcx857x/tests/Pcx857xTest.cs +++ b/src/devices/Pcx857x/tests/Pcx857xTest.cs @@ -161,6 +161,8 @@ protected override PinValue Read(int pinNumber) return PinValue.Low; } + protected override void Toggle(int pinNumber) => Write(pinNumber, !Read(pinNumber)); + public void Read(Span pinValues) { for (int i = 0; i < pinValues.Length; i++) From 8ca6aac3e35af1c5e3d2b08ce56a0215cca2347b Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Fri, 11 Nov 2022 17:35:24 +0100 Subject: [PATCH 08/16] adding Arduino and Seasaw --- src/devices/Arduino/ArduinoGpioControllerDriver.cs | 12 +++++++++++- src/devices/Seesaw/SeesawGpioDriver.cs | 11 ++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/devices/Arduino/ArduinoGpioControllerDriver.cs b/src/devices/Arduino/ArduinoGpioControllerDriver.cs index 3706ff9540..3e3f9242d0 100644 --- a/src/devices/Arduino/ArduinoGpioControllerDriver.cs +++ b/src/devices/Arduino/ArduinoGpioControllerDriver.cs @@ -18,6 +18,7 @@ internal class ArduinoGpioControllerDriver : GpioDriver private readonly IReadOnlyCollection _supportedPinConfigurations; private readonly Dictionary _callbackContainers; private readonly ConcurrentDictionary _pinModes; + private readonly ConcurrentDictionary _pinValues; private readonly object _callbackContainersLock; private readonly AutoResetEvent _waitForEventResetEvent; private readonly ILogger _logger; @@ -31,6 +32,7 @@ internal ArduinoGpioControllerDriver(FirmataDevice device, IReadOnlyCollection(); + _pinValues = new ConcurrentDictionary(); _outputPinValues = new ConcurrentDictionary(); _logger = this.GetCurrentClassLogger(); @@ -54,11 +56,14 @@ protected override void OpenPin(int pinNumber) { throw new ArgumentOutOfRangeException(nameof(pinNumber), $"Pin {pinNumber} is not valid"); } + + _pinValues.TryAdd(pinNumber, PinValue.Low); } protected override void ClosePin(int pinNumber) { _pinModes.TryRemove(pinNumber, out _); + _pinValues.TryRemove(pinNumber, out _); } protected override void SetPinMode(int pinNumber, PinMode mode) @@ -152,9 +157,12 @@ protected override bool IsPinModeSupported(int pinNumber, PinMode mode) protected override PinValue Read(int pinNumber) { - return _device.ReadDigitalPin(pinNumber); + _pinValues[pinNumber] = _device.ReadDigitalPin(pinNumber); + return _pinValues[pinNumber]; } + protected override void Toggle(int pinNumber) => Write(pinNumber, !_pinValues[pinNumber]); + protected override void Write(int pinNumber, PinValue value) { if (_outputPinValues.TryGetValue(pinNumber, out PinValue? existingValue) && existingValue != null) @@ -168,6 +176,7 @@ protected override void Write(int pinNumber, PinValue value) _device.WriteDigitalPin(pinNumber, value); _outputPinValues.AddOrUpdate(pinNumber, x => value, (y, z) => value); + _pinValues[pinNumber] = value; } protected override WaitForEventResult WaitForEvent(int pinNumber, PinEventTypes eventTypes, CancellationToken cancellationToken) @@ -283,6 +292,7 @@ protected override void Dispose(bool disposing) _callbackContainers.Clear(); } + _pinValues.Clear(); _outputPinValues.Clear(); _device.DigitalPortValueUpdated -= FirmataOnDigitalPortValueUpdated; } diff --git a/src/devices/Seesaw/SeesawGpioDriver.cs b/src/devices/Seesaw/SeesawGpioDriver.cs index 30f8741e07..d9e5b3491a 100644 --- a/src/devices/Seesaw/SeesawGpioDriver.cs +++ b/src/devices/Seesaw/SeesawGpioDriver.cs @@ -15,6 +15,7 @@ namespace Iot.Device.Seesaw public class SeesawGpioDriver : GpioDriver { private readonly Dictionary _openPins; + private readonly Dictionary _pinValues; private Seesaw _seesawDevice; @@ -41,6 +42,7 @@ public SeesawGpioDriver(Seesaw seesawDevice) } _openPins = new Dictionary(); + _pinValues = new Dictionary(); } /// @@ -67,6 +69,7 @@ protected override void ClosePin(int pinNumber) } _openPins.Remove(pinNumber); + _pinValues.Remove(pinNumber); } /// @@ -145,6 +148,7 @@ public void OpenPin(int pinNumber, PinMode mode) } _openPins.Add(pinNumber, mode); + _pinValues.Add(pinNumber, PinValue.Low); SetPinMode(pinNumber, mode); } @@ -165,9 +169,13 @@ protected override PinValue Read(int pinNumber) throw new InvalidOperationException("Can not read a value from a pin that is not open."); } - return _seesawDevice.ReadGpioDigital((byte)pinNumber) ? PinValue.High : PinValue.Low; + _pinValues[pinNumber] = _seesawDevice.ReadGpioDigital((byte)pinNumber) ? PinValue.High : PinValue.Low; + return _pinValues[pinNumber]; } + /// + protected override void Toggle(int pinNumber) => Write(pinNumber, !_pinValues[pinNumber]); + /// /// Read the given pins with the given pin numbers. /// @@ -219,6 +227,7 @@ protected override void Write(int pinNumber, PinValue value) } _seesawDevice.WriteGpioDigital((byte)pinNumber, (value == PinValue.High)); + _pinValues[pinNumber] = value; } /// From 9e61fbb4b5789bc9a89230a1857f8585d355a4c1 Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Fri, 11 Nov 2022 17:50:08 +0100 Subject: [PATCH 09/16] adjsting file encoding after conflict merge --- src/devices/Board/KeyboardGpioDriver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/devices/Board/KeyboardGpioDriver.cs b/src/devices/Board/KeyboardGpioDriver.cs index d452599bf9..6dd3551122 100644 --- a/src/devices/Board/KeyboardGpioDriver.cs +++ b/src/devices/Board/KeyboardGpioDriver.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; From 2b21a22526304edb74f92a3c5ba580bcad97d8c9 Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Fri, 11 Nov 2022 19:01:38 +0100 Subject: [PATCH 10/16] Adding missing Toggle implementation in Arduino native --- tools/ArduinoCsCompiler/Hal/ArduinoNativeGpioDriver.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/ArduinoCsCompiler/Hal/ArduinoNativeGpioDriver.cs b/tools/ArduinoCsCompiler/Hal/ArduinoNativeGpioDriver.cs index 8ee0964b96..eb81ce86c4 100644 --- a/tools/ArduinoCsCompiler/Hal/ArduinoNativeGpioDriver.cs +++ b/tools/ArduinoCsCompiler/Hal/ArduinoNativeGpioDriver.cs @@ -65,6 +65,8 @@ protected override PinValue Read(int pinNumber) return _hardwareLevelAccess.ReadPin(pinNumber); } + protected override void Toggle(int pinNumber) => Write(pinNumber, !Read(pinNumber)); + protected override void Write(int pinNumber, PinValue value) { _hardwareLevelAccess.WritePin(pinNumber, value == PinValue.High ? 1 : 0); From 1f4272c6c2529895e97168575220cb56d6df5ce4 Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Fri, 11 Nov 2022 19:15:38 +0100 Subject: [PATCH 11/16] Adjusting test for new pattern with GpioPin --- src/devices/Board/tests/BoardTests.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/devices/Board/tests/BoardTests.cs b/src/devices/Board/tests/BoardTests.cs index 3969a2e497..68e8471f15 100644 --- a/src/devices/Board/tests/BoardTests.cs +++ b/src/devices/Board/tests/BoardTests.cs @@ -57,12 +57,13 @@ public void GpioControllerCreateOpenClosePin() } [Fact] - public void OpenPinAlreadyAssignedThrows() + public void GetSameGpioPinWhenOpen() { var board = CreateBoard(); var ctrl = board.CreateGpioController(); - ctrl.OpenPin(1); - Assert.Throws(() => ctrl.OpenPin(1)); + var firstGpioPin = ctrl.OpenPin(1); + var secondGpioPin = ctrl.OpenPin(1); + Assert.Equal(firstGpioPin, secondGpioPin); } [Fact] From e667d8e4b6278d1d513f5dd1f2205792fb239a35 Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Fri, 18 Nov 2022 10:47:31 +0100 Subject: [PATCH 12/16] adjusting PR feedback --- .../System/Device/Gpio/Drivers/HummingBoardDriver.cs | 3 --- src/System.Device.Gpio/System/Device/Gpio/GpioDriver.cs | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/System.Device.Gpio/System/Device/Gpio/Drivers/HummingBoardDriver.cs b/src/System.Device.Gpio/System/Device/Gpio/Drivers/HummingBoardDriver.cs index 32172d05da..28b43a13fe 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/Drivers/HummingBoardDriver.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/Drivers/HummingBoardDriver.cs @@ -82,9 +82,6 @@ protected internal override PinMode GetPinMode(int pinNumber) /// protected internal override PinValue Read(int pinNumber) => _internalDriver.Read(pinNumber); - /// - protected internal override void Toggle(int pinNumber) => _internalDriver.Read(pinNumber); - /// protected internal override void RemoveCallbackForPinValueChangedEvent(int pinNumber, PinChangeEventHandler callback) => _internalDriver.RemoveCallbackForPinValueChangedEvent(pinNumber, callback); diff --git a/src/System.Device.Gpio/System/Device/Gpio/GpioDriver.cs b/src/System.Device.Gpio/System/Device/Gpio/GpioDriver.cs index 7b2cf762b8..fd07e54681 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/GpioDriver.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/GpioDriver.cs @@ -86,7 +86,7 @@ protected internal virtual void SetPinMode(int pinNumber, PinMode mode, PinValue /// Toggle the current value of a pin. /// /// The pin number in the driver's logical numbering scheme. - protected internal abstract void Toggle(int pinNumber); + protected internal virtual void Toggle(int pinNumber) => Write(pinNumber, !Read(pinNumber)); /// /// Writes a value to a pin. From b2c00e112239b3b46c4073bd1c80f8ed3812d994 Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Fri, 18 Nov 2022 10:49:45 +0100 Subject: [PATCH 13/16] typo in test --- src/System.Device.Gpio.Tests/GpioPinTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Device.Gpio.Tests/GpioPinTests.cs b/src/System.Device.Gpio.Tests/GpioPinTests.cs index 2bbfc40138..278d375c9e 100644 --- a/src/System.Device.Gpio.Tests/GpioPinTests.cs +++ b/src/System.Device.Gpio.Tests/GpioPinTests.cs @@ -26,7 +26,7 @@ public void Dispose() } [Fact] - public void TestOpnPin() + public void TestOpenPin() { // Arrange var ctrl = new GpioController(PinNumberingScheme.Logical, _mockedGpioDriver.Object); From 04a50cb9226ed7cfdc454d3fd33b13d683f65148 Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Tue, 22 Nov 2022 18:49:59 +0100 Subject: [PATCH 14/16] fixing nit --- src/System.Device.Gpio.Tests/GpioPinTests.cs | 8 ++++---- src/devices/Board/KeyboardGpioDriver.cs | 2 +- tools/ArduinoCsCompiler/Hal/ArduinoNativeGpioDriver.cs | 2 -- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/System.Device.Gpio.Tests/GpioPinTests.cs b/src/System.Device.Gpio.Tests/GpioPinTests.cs index 278d375c9e..8d495fef17 100644 --- a/src/System.Device.Gpio.Tests/GpioPinTests.cs +++ b/src/System.Device.Gpio.Tests/GpioPinTests.cs @@ -33,8 +33,8 @@ public void TestOpenPin() // Act GpioPin pin = ctrl.OpenPin(PinNumber, PinMode.Input); // Assert - Assert.True(pin.PinNumber == PinNumber); - Assert.True(pin.GetPinMode() == PinMode.Input); + Assert.Equal(pin.PinNumber, PinNumber); + Assert.Equal(PinMode.Input, pin.GetPinMode()); } [Fact] @@ -60,9 +60,9 @@ public void TestToggleReadWrite() GpioPin pin = ctrl.OpenPin(PinNumber, PinMode.Input); pin.Write(PinValue.High); // Assert - Assert.True(pin.Read() == PinValue.High); + Assert.Equal(PinValue.High, pin.Read()); pin.Toggle(); - Assert.True(pin.Read() == PinValue.Low); + Assert.Equal(PinValue.Low, pin.Read()); } } } diff --git a/src/devices/Board/KeyboardGpioDriver.cs b/src/devices/Board/KeyboardGpioDriver.cs index 6dd3551122..6edb6245d1 100644 --- a/src/devices/Board/KeyboardGpioDriver.cs +++ b/src/devices/Board/KeyboardGpioDriver.cs @@ -143,7 +143,7 @@ private void SetLedState(LedKey key, PinValue state) // Simulate a key press Interop.keybd_event((byte)virtualKey, 0x45, - Interop.KEYEVENTF_EXTENDEDKEY | 0, + Interop.KEYEVENTF_EXTENDEDKEY, IntPtr.Zero); // Simulate a key release diff --git a/tools/ArduinoCsCompiler/Hal/ArduinoNativeGpioDriver.cs b/tools/ArduinoCsCompiler/Hal/ArduinoNativeGpioDriver.cs index eb81ce86c4..8ee0964b96 100644 --- a/tools/ArduinoCsCompiler/Hal/ArduinoNativeGpioDriver.cs +++ b/tools/ArduinoCsCompiler/Hal/ArduinoNativeGpioDriver.cs @@ -65,8 +65,6 @@ protected override PinValue Read(int pinNumber) return _hardwareLevelAccess.ReadPin(pinNumber); } - protected override void Toggle(int pinNumber) => Write(pinNumber, !Read(pinNumber)); - protected override void Write(int pinNumber, PinValue value) { _hardwareLevelAccess.WritePin(pinNumber, value == PinValue.High ? 1 : 0); From f627a91bea6b87a84ea5ee540a4b13fadff47961 Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Wed, 23 Nov 2022 07:54:17 +0100 Subject: [PATCH 15/16] fixing test --- .../GpioControllerSoftwareTests.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/System.Device.Gpio.Tests/GpioControllerSoftwareTests.cs b/src/System.Device.Gpio.Tests/GpioControllerSoftwareTests.cs index c28178287f..0b9d218775 100644 --- a/src/System.Device.Gpio.Tests/GpioControllerSoftwareTests.cs +++ b/src/System.Device.Gpio.Tests/GpioControllerSoftwareTests.cs @@ -30,13 +30,14 @@ public void PinCountReportedCorrectly() } [Fact] - public void OpenTwiceThrows() + public void OpenTwiceGpioPinAndClosedTwiceThrows() { var ctrl = new GpioController(PinNumberingScheme.Logical, _mockedGpioDriver.Object); _mockedGpioDriver.Setup(x => x.OpenPinEx(1)); _mockedGpioDriver.Setup(x => x.ClosePinEx(1)); - ctrl.OpenPin(1); - Assert.Throws(() => ctrl.OpenPin(1)); + GpioPin gpioPin1 = ctrl.OpenPin(1); + GpioPin gpiopin2 = ctrl.OpenPin(1); + Assert.Equal(gpioPin1, gpiopin2); ctrl.ClosePin(1); Assert.Throws(() => ctrl.ClosePin(1)); } From 57fb4077b942bdc93cfade8ae2b1ee7f1a44026d Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Wed, 23 Nov 2022 14:37:46 +0100 Subject: [PATCH 16/16] fixing test --- src/System.Device.Gpio.Tests/GpioPinTests.cs | 21 +++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/System.Device.Gpio.Tests/GpioPinTests.cs b/src/System.Device.Gpio.Tests/GpioPinTests.cs index 8d495fef17..1746551a0d 100644 --- a/src/System.Device.Gpio.Tests/GpioPinTests.cs +++ b/src/System.Device.Gpio.Tests/GpioPinTests.cs @@ -29,6 +29,10 @@ public void Dispose() public void TestOpenPin() { // Arrange + _mockedGpioDriver.Setup(x => x.OpenPinEx(PinNumber)); + _mockedGpioDriver.Setup(x => x.IsPinModeSupportedEx(PinNumber, It.IsAny())).Returns(true); + _mockedGpioDriver.Setup(x => x.SetPinModeEx(PinNumber, It.IsAny())); + _mockedGpioDriver.Setup(x => x.GetPinModeEx(PinNumber)).Returns(PinMode.Input); var ctrl = new GpioController(PinNumberingScheme.Logical, _mockedGpioDriver.Object); // Act GpioPin pin = ctrl.OpenPin(PinNumber, PinMode.Input); @@ -41,6 +45,9 @@ public void TestOpenPin() public void TestClosePin() { // Arrange + _mockedGpioDriver.Setup(x => x.OpenPinEx(PinNumber)); + _mockedGpioDriver.Setup(x => x.IsPinModeSupportedEx(PinNumber, It.IsAny())).Returns(true); + _mockedGpioDriver.Setup(x => x.SetPinModeEx(PinNumber, It.IsAny())); var ctrl = new GpioController(PinNumberingScheme.Logical, _mockedGpioDriver.Object); // Act GpioPin pin = ctrl.OpenPin(PinNumber, PinMode.Input); @@ -55,14 +62,22 @@ public void TestClosePin() public void TestToggleReadWrite() { // Arrange + PinValue pinValue = PinValue.High; + _mockedGpioDriver.Setup(x => x.OpenPinEx(PinNumber)); + _mockedGpioDriver.Setup(x => x.IsPinModeSupportedEx(PinNumber, It.IsAny())).Returns(true); + _mockedGpioDriver.Setup(x => x.SetPinModeEx(PinNumber, It.IsAny())); + _mockedGpioDriver.Setup(x => x.ReadEx(PinNumber)).Returns(pinValue); var ctrl = new GpioController(PinNumberingScheme.Logical, _mockedGpioDriver.Object); // Act GpioPin pin = ctrl.OpenPin(PinNumber, PinMode.Input); - pin.Write(PinValue.High); + pin.Write(pinValue); // Assert - Assert.Equal(PinValue.High, pin.Read()); + Assert.Equal(pinValue, pin.Read()); pin.Toggle(); - Assert.Equal(PinValue.Low, pin.Read()); + // Make sure we setup the drive properly + pinValue = !pinValue; + _mockedGpioDriver.Setup(x => x.ReadEx(PinNumber)).Returns(pinValue); + Assert.Equal(pinValue, pin.Read()); } } }