Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Documentation/gpio-linux-libgpiod.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ The following table shows which driver supports which library version
| V2 | 2.x |

NOTE: Due to a [breaking change in the values of enums in the libgpiod](
https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/commit/?id=783ff2e3c70788cdd1c65cba9ee0398bda5ebcda), only libgpiod versions 1.1 and later can be expected to function reliably with the V1 driver.
https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/commit/?id=783ff2e3c70788cdd1c65cba9ee0398bda5ebcda), only libgpiod versions 1.1 and later can be expected to function reliably with the V1 driver.
To check what libgpiod packages you have on a deb based system, use: ``` $apt show libgpiod* ```

## Choose LibGpiodDriver Version
Expand Down
2 changes: 1 addition & 1 deletion samples/M5StackRemoteDisplay/RemoteControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public RemoteControl(Chsc6440? touch, Ili9342 screen, M5ToughPowerControl? power
_messageRouter.AddFilterRule(new FilterRule(_messageRouter.InterfaceName, TalkerId.ElectronicChartDisplayAndInformationSystem, SentenceId.Any,
new List<string>(), (source, destination, before) =>
{
_cache.Add(before);
_cache.Add(source, before);
return null;
}, true, false));

Expand Down
25 changes: 22 additions & 3 deletions src/devices/Arduino/ArduinoBoard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ public ArduinoBoard(string portName, int baudRate)
_logger = this.GetCurrentClassLogger();
}

/// <summary>
/// The recommended firmware version to use with this library.
/// When using an older version, some features might not work properly.
/// </summary>
public static Version ExpectedFirmwareVersion => new Version(3, 4);

/// <summary>
/// The board logger.
/// </summary>
Expand Down Expand Up @@ -209,9 +215,7 @@ public static bool TryConnectToNetworkedBoard(IPAddress boardAddress, int port,
/// <param name="board">Returns the board if successful</param>
/// <returns>True on success, false otherwise</returns>
public static bool TryConnectToNetworkedBoard(IPAddress boardAddress, int port, bool useAutoReconnect,
#if NET5_0_OR_GREATER
[NotNullWhen(true)]
#endif
out ArduinoBoard? board)
{
try
Expand Down Expand Up @@ -454,7 +458,7 @@ protected override void Initialize()
_firmataVersion = _firmata.QueryFirmataVersion();
if (_firmataVersion < _firmata.QuerySupportedFirmataVersion())
{
throw new NotSupportedException($"Firmata version on board is {_firmataVersion}. Expected {_firmata.QuerySupportedFirmataVersion()}. They must be equal.");
throw new NotSupportedException($"Firmata version on board is {_firmataVersion}. Expected at least {_firmata.QuerySupportedFirmataVersion()}.");
}

Logger.LogInformation($"Firmata version on board is {_firmataVersion}.");
Expand All @@ -464,6 +468,11 @@ protected override void Initialize()

Logger.LogInformation($"Firmware version on board is {_firmwareVersion}");

if (_firmwareVersion < ExpectedFirmwareVersion)
{
Logger.LogWarning($"Firmware version on board is {_firmwareVersion}. It is recommended to upgrade to {ExpectedFirmwareVersion}.");
}

_firmata.QueryCapabilities();

_supportedPinConfigurations = _firmata.PinConfigurations.AsReadOnly();
Expand Down Expand Up @@ -862,6 +871,16 @@ public void SetAnalogPinSamplingInterval(TimeSpan timeSpan)
Firmata.SetAnalogInputSamplingInterval(timeSpan);
}

/// <summary>
/// Query the current analog input sampling interval.
/// </summary>
/// <returns></returns>
public TimeSpan QueryAnalogPinSamplingInterval()
{
Initialize();
return Firmata.QueryAnalogInputSamplingInterval();
}

/// <summary>
/// Performs a software reset of the Arduino firmware
/// </summary>
Expand Down
15 changes: 15 additions & 0 deletions src/devices/Arduino/FirmataDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1508,6 +1508,21 @@ public void SetAnalogInputSamplingInterval(TimeSpan interval)
SendCommand(seq);
}

public TimeSpan QueryAnalogInputSamplingInterval()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider

Suggested change
public TimeSpan QueryAnalogInputSamplingInterval()
public TimeSpan AnalogInputSamplingInterval => ...

or renaming Query to Get to make it sound simpler

{
FirmataCommandSequence seq = new();
seq.WriteByte((byte)FirmataSysexCommand.SAMPLING_INTERVAL_QUERY);
seq.WriteByte((byte)FirmataCommand.END_SYSEX);
byte[] reply = SendCommandAndWait(seq, TimeSpan.FromSeconds(1), (q, bytes) => bytes[0] == (byte)FirmataSysexCommand.SAMPLING_INTERVAL, out _lastCommandError);
if (_lastCommandError != CommandError.None)
{
throw new IOException($"Command failed with error {_lastCommandError}");
}

int millis = FirmataCommandSequence.DecodeInt14(reply, 1);
return TimeSpan.FromMilliseconds(millis);
}

public void ConfigureSpiDevice(SpiConnectionSettings connectionSettings)
{
if (connectionSettings.ChipSelectLine >= 15)
Expand Down
23 changes: 12 additions & 11 deletions src/devices/Arduino/FirmataSysexCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,16 @@ namespace Iot.Device.Arduino
internal enum FirmataSysexCommand
{
ENCODER_DATA = 0x61,
EXTENDED_REPORT_ANALOG = 0x64,
SYSTEM_VARIABLE = 0x66,
SPI_DATA = 0x68,
ANALOG_MAPPING_QUERY = 0x69,
ANALOG_MAPPING_RESPONSE = 0x6A,
CAPABILITY_QUERY = 0x6B,
CAPABILITY_RESPONSE = 0x6C,
PIN_STATE_QUERY = 0x6D,
PIN_STATE_RESPONSE = 0x6E,
EXTENDED_ANALOG = 0x6F,
SERVO_CONFIG = 0x70,
STRING_DATA = 0x71,
STEPPER_DATA = 0x72,
Expand All @@ -18,21 +27,13 @@ internal enum FirmataSysexCommand
I2C_REQUEST = 0x76,
I2C_REPLY = 0x77,
I2C_CONFIG = 0x78,
EXTENDED_REPORT_ANALOG = 0x64,
EXTENDED_ANALOG = 0x6F,
PIN_STATE_QUERY = 0x6D,
PIN_STATE_RESPONSE = 0x6E,
CAPABILITY_QUERY = 0x6B,
CAPABILITY_RESPONSE = 0x6C,
ANALOG_MAPPING_QUERY = 0x69,
ANALOG_MAPPING_RESPONSE = 0x6A,
DHT_SENSOR_DATA_REQUEST = 0x74,
REPORT_FIRMWARE = 0x79,
SAMPLING_INTERVAL = 0x7A,
SCHEDULER_DATA = 0x7B,
SAMPLING_INTERVAL_QUERY = 0x7C,
FREQUENCY_COMMAND = 0x7D,
SYSEX_NON_REALTIME = 0x7E,
SYSEX_REALTIME = 0x7F,
DHT_SENSOR_DATA_REQUEST = 0x74, // User defined block
FREQUENCY_COMMAND = 0x7D,
SYSTEM_VARIABLE = 0x66,
}
}
19 changes: 19 additions & 0 deletions src/devices/Arduino/FrequencySensor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
Expand Down Expand Up @@ -187,6 +188,24 @@ public void DisableFrequencyReporting(int pinNumber)
_frequencyReportingPin = -1;
}

/// <summary>
/// Sets a debouncing period for the input pin. Useful if the sensor's signal is a bit "dirty".
/// </summary>
/// <param name="pinNumber">The pin to observe</param>
/// <param name="period">The idle time. Any events within this period will be ignored</param>
public void SetDebouncingPeriod(int pinNumber, TimeSpan period)
{
ArgumentOutOfRangeException.ThrowIfNegative(period.TotalSeconds, nameof(period));
uint us = (uint)period.TotalMicroseconds;
FirmataCommandSequence sequence = new();
sequence.WriteByte((byte)FirmataSysexCommand.FREQUENCY_COMMAND);
sequence.WriteByte(3);
sequence.WriteByte((byte)pinNumber);
sequence.SendUInt32(us);
sequence.WriteByte((byte)FirmataCommand.END_SYSEX);
SendCommand(sequence);
}

/// <inheritdoc />
protected override void Dispose(bool disposing)
{
Expand Down
1 change: 1 addition & 0 deletions src/devices/Arduino/samples/ApiChecker/Arduino.sample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ public static void Main(string[] args)
private static void ConnectWithStream(Stream stream)
{
ArduinoBoard board = new ArduinoBoard(stream);
stream.ReadTimeout = 60000;
try
{
Console.WriteLine(
Expand Down
18 changes: 17 additions & 1 deletion src/devices/Arduino/samples/ApiChecker/TestCases.cs
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,22 @@ private void TestGpio()

private void TestAnalogIn()
{
_board.SetAnalogPinSamplingInterval(TimeSpan.FromMilliseconds(20));
TimeSpan diditwork = TimeSpan.Zero;
try
{
diditwork = _board.QueryAnalogPinSamplingInterval();
}
catch (IOException)
{
diditwork = TimeSpan.Zero;
}

if (diditwork != TimeSpan.FromMilliseconds(20))
{
Console.WriteLine("We are not able to query the sampling interval. Ensure the board runs ConfigurableFirmata 3.4 or newer");
}

// Use Pin 6
int gpio = _ledPin;
int analogPin = GetAnalogPin(_board, _analogInputChannel);
Expand All @@ -503,7 +519,7 @@ private void TestAnalogIn()
gpioController.OpenPin(gpio);
gpioController.SetPinMode(gpio, PinMode.Output);

Console.WriteLine("Blinking GPIO6, based on analog input.");
Console.WriteLine($"Blinking GPIO{analogPin}, based on analog input on A{_analogInputChannel}.");
while (!Console.KeyAvailable)
{
ElectricPotential voltage = pin.ReadVoltage();
Expand Down
18 changes: 17 additions & 1 deletion src/devices/Common/Iot/Device/Common/GeographicPosition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,25 @@ public GeographicPosition(GeographicPosition pos)
/// <param name="latitude">Latitude of position, in degrees. Valid values are -90 - +90</param>
/// <param name="longitude">Longitude of position, in degrees. Valid values are -180 to +180 or 0 to 360, depending on application</param>
/// <param name="ellipsoidalHeight">Height over the WGS84 ellipsoid.</param>
/// <remarks>No exception is thrown on denormalized or out-of-range positions.</remarks>
/// <exception cref="ArgumentOutOfRangeException">The position is invalid (latitude or longitude are out of range)</exception>
public GeographicPosition(double latitude, double longitude, double ellipsoidalHeight)
{
if (Double.IsNaN(latitude) || Double.IsInfinity(latitude) || Math.Abs(latitude) > 90.1)
{
throw new ArgumentOutOfRangeException(nameof(latitude), latitude, "Latitude must be a valid number and between -90 and +90");
}

if (Double.IsNaN(longitude) || Double.IsInfinity(longitude))
{
throw new ArgumentOutOfRangeException(nameof(longitude), longitude, "Longitude must be a valid number");
}

if (Double.IsNaN(ellipsoidalHeight) || Double.IsInfinity(ellipsoidalHeight))
{
throw new ArgumentOutOfRangeException(nameof(ellipsoidalHeight), ellipsoidalHeight,
"Height must be a valid number");
}

_latitude = latitude;
_longitude = longitude;
_height = ellipsoidalHeight;
Expand Down
97 changes: 97 additions & 0 deletions src/devices/Common/Iot/Device/Common/HysteresisFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// 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.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Iot.Device.Common
{
/// <summary>
/// This class implements a histeresis filter on a boolean value.
/// The output value will depend on the input and a configurable delay.
/// This is useful to e.g. trigger an error only after an input value
/// is high for at least a certain time.
/// </summary>
public class HysteresisFilter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about the word Filter in the name but I cannot think of anything better... maybe HysteresisFunction?

{
private bool _currentValue;
private bool _lastInputValue;
private Stopwatch _tickCountLastChange;

/// <summary>
/// Creates a new histeresis filter with the given initial value
/// </summary>
/// <param name="initialValue">The initial value of the output</param>
public HysteresisFilter(bool initialValue)
{
_currentValue = initialValue;
_lastInputValue = initialValue;
_tickCountLastChange = Stopwatch.StartNew();
RisingDelayTime = TimeSpan.Zero;
FallingDelayTime = TimeSpan.Zero;
}

/// <summary>
/// The delay time for a raising input
/// </summary>
public TimeSpan RisingDelayTime
{
get;
set;
}

/// <summary>
/// The delay time for a falling input
/// </summary>
public TimeSpan FallingDelayTime
{
get;
set;
}

/// <summary>
/// The current output value. This will not change unless <see cref="Update"/> is called regularly.
/// </summary>
public bool Output => _currentValue;

/// <summary>
/// Updates the input value.
/// This method must be called regularly (typically significantly more often than any of <see cref="FallingDelayTime"/> and
/// <see cref="RisingDelayTime"/>) to update the input. The output will change when the input has changed for more
/// than the delay time in either direction.
/// </summary>
/// <param name="newValue">The new input value</param>
/// <returns>The current output value</returns>
public bool Update(bool newValue)
{
// Reset clock when input value is different from output and we have not already started a wait
if (newValue != _currentValue && newValue != _lastInputValue)
{
_tickCountLastChange.Restart();
}

_lastInputValue = newValue;

if (newValue)
{
if (_tickCountLastChange.Elapsed >= RisingDelayTime)
{
_currentValue = true;
}
}
else
{
if (_tickCountLastChange.Elapsed >= FallingDelayTime)
{
_currentValue = false;
}
}

return _currentValue;
}
}
}
Loading
Loading