Skip to content

Commit

Permalink
Add Board as base class of existing ArduinoBoard
Browse files Browse the repository at this point in the history
  • Loading branch information
pgrawehr committed Apr 11, 2021
1 parent 789a961 commit 54a33be
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 42 deletions.
1 change: 1 addition & 0 deletions src/devices/Arduino/Arduino.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

<ItemGroup>
<ProjectReference Include="$(MainLibraryPath)System.Device.Gpio.csproj" />
<ProjectReference Include="..\Board\Board.csproj" />
<ProjectReference Include="..\Common\CommonHelpers.csproj" />
<PackageReference Include="System.IO.Ports" Version="$(SystemIOPortsPackageVersion)" />
<PackageReference Include="System.Memory" Version="$(SystemMemoryPackageVersion)" />
Expand Down
6 changes: 6 additions & 0 deletions src/devices/Arduino/Arduino.sln
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arduino.Tests", "tests\Ardu
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HardwareMonitor", "..\HardwareMonitor\HardwareMonitor.csproj", "{D1C467D8-E6E3-4811-826B-216DCB80A16E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Board", "..\Board\Board.csproj", "{0413C8F5-20E0-48B2-8C35-AED67219B7C6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -73,6 +75,10 @@ Global
{D1C467D8-E6E3-4811-826B-216DCB80A16E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D1C467D8-E6E3-4811-826B-216DCB80A16E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D1C467D8-E6E3-4811-826B-216DCB80A16E}.Release|Any CPU.Build.0 = Release|Any CPU
{0413C8F5-20E0-48B2-8C35-AED67219B7C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0413C8F5-20E0-48B2-8C35-AED67219B7C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0413C8F5-20E0-48B2-8C35-AED67219B7C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0413C8F5-20E0-48B2-8C35-AED67219B7C6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
150 changes: 118 additions & 32 deletions src/devices/Arduino/ArduinoBoard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using System.Threading;
using System.Linq;
using Iot.Device.Common;
using Iot.Device.Board;
using Microsoft.Extensions.Logging;
using UnitsNet;

Expand All @@ -29,7 +30,7 @@ namespace Iot.Device.Arduino
/// Note that the program will run on the PC, so you cannot disconnect the
/// Arduino while this driver is connected.
/// </summary>
public class ArduinoBoard : IDisposable
public class ArduinoBoard : Board.Board, IDisposable
{
private SerialPort? _serialPort;
private Stream? _dataStream;
Expand All @@ -56,6 +57,7 @@ public class ArduinoBoard : IDisposable
/// </remarks>
/// <param name="serialPortStream">A stream to an Arduino/Firmata device</param>
public ArduinoBoard(Stream serialPortStream)
: base(PinNumberingScheme.Logical)
{
_dataStream = serialPortStream ?? throw new ArgumentNullException(nameof(serialPortStream));
_logger = this.GetCurrentClassLogger();
Expand All @@ -69,6 +71,7 @@ public ArduinoBoard(Stream serialPortStream)
/// On Linux, possible values include "/dev/ttyAMA0", "/dev/serial0", "/dev/ttyUSB1", etc.</param>
/// <param name="baudRate">Baudrate to use. It is recommended to use at least 115200 Baud.</param>
public ArduinoBoard(string portName, int baudRate)
: base(PinNumberingScheme.Logical)
{
_dataStream = null;
_serialPort = new SerialPort(portName, baudRate);
Expand Down Expand Up @@ -158,13 +161,73 @@ public static List<int> CommonBaudRates()
};
}

/// <inheritdoc />
public override int ConvertPinNumber(int pinNumber, PinNumberingScheme inputScheme, PinNumberingScheme outputScheme)
{
if (inputScheme == outputScheme && inputScheme == PinNumberingScheme.Logical)
{
return pinNumber;
}

throw new NotSupportedException("The Arduino uses logical pin numbering only");
}

/// <summary>
/// Returns the current assignment of the given pin
/// </summary>
/// <param name="pinNumber">Pin number to query</param>
/// <returns>A value of the <see cref="PinUsage"/> enumeration</returns>
public override PinUsage DetermineCurrentPinUsage(int pinNumber)
{
SupportedMode mode = Firmata.GetPinMode(pinNumber);
switch (mode)
{
case SupportedMode.AnalogInput:
return PinUsage.AnalogIn;
case SupportedMode.DigitalInput:
return PinUsage.Gpio;
case SupportedMode.DigitalOutput:
return PinUsage.Gpio;
case SupportedMode.Pwm:
return PinUsage.Pwm;
case SupportedMode.Servo:
break;
case SupportedMode.Shift:
break;
case SupportedMode.I2c:
return PinUsage.I2c;
case SupportedMode.OneWire:
break;
case SupportedMode.Stepper:
break;
case SupportedMode.Encoder:
break;
case SupportedMode.Serial:
return PinUsage.Uart;
case SupportedMode.InputPullup:
return PinUsage.Gpio;
case SupportedMode.Spi:
return PinUsage.Spi;
case SupportedMode.Sonar:
break;
case SupportedMode.Tone:
break;
case SupportedMode.Dht:
return PinUsage.Gpio;
}

return PinUsage.Unknown;
}

/// <summary>
/// Initialize the board connection. This must be called before any other methods of this class.
/// </summary>
/// <exception cref="NotSupportedException">The Firmata firmware on the connected board is too old.</exception>
/// <exception cref="TimeoutException">There was no answer from the board</exception>
protected virtual void Initialize()
protected override void Initialize()
{
base.Initialize();

// Shortcut, so we do not need to take the lock
if (_initialized)
{
Expand Down Expand Up @@ -313,45 +376,40 @@ private void FirmataOnError(string message, Exception? exception)
/// Creates a GPIO Controller instance for the board. This allows working with digital input/output pins.
/// </summary>
/// <returns>An instance of GpioController, using an Arduino-Enabled driver</returns>
public GpioController CreateGpioController()
public override GpioController CreateGpioController()
{
Initialize();

return new GpioController(PinNumberingScheme.Logical, new ArduinoGpioControllerDriver(this, _supportedPinConfigurations));
}

/// <summary>
/// Creates an I2c device with the given connection settings.
/// </summary>
/// <param name="connectionSettings">I2c connection settings. Only Bus 0 is supported.</param>
/// <returns>An <see cref="I2cDevice"/> instance</returns>
/// <exception cref="NotSupportedException">The firmware reports that no pins are available for I2C. Check whether the I2C module is enabled in Firmata.
/// Or: An invalid Bus Id or device Id was specified</exception>
public virtual I2cDevice CreateI2cDevice(I2cConnectionSettings connectionSettings)
/// <inheritdoc />
protected override I2cBusManager CreateI2cBusCore(int busNumber, int[]? pins)
{
Initialize();

if (connectionSettings == null)
{
throw new ArgumentNullException(nameof(connectionSettings));
}

if (!SupportedPinConfigurations.Any(x => x.PinModes.Contains(SupportedMode.I2c)))
{
throw new NotSupportedException("No Pins with I2c support found. Is the I2c module loaded?");
}

return new ArduinoI2cDevice(this, connectionSettings);
return new I2cBusManager(this, busNumber, pins, new ArduinoI2cBus(this, busNumber));
}

/// <inheritdoc />
public override int GetDefaultI2cBusNumber()
{
return 0;
}

/// <summary>
/// Connect to a device connected to the primary SPI bus on the Arduino
/// Firmata's default implementation has no SPI support, so this first checks whether it's available at all.
/// </summary>
/// <param name="settings">Spi Connection settings</param>
/// <param name="pins">The pins to use.</param>
/// <returns>An <see cref="SpiDevice"/> instance.</returns>
/// <exception cref="NotSupportedException">The Bus number is not 0, or the SPI component has not been enabled in the firmware.</exception>
public virtual SpiDevice CreateSpiDevice(SpiConnectionSettings settings)
protected override SpiDevice CreateSimpleSpiDevice(SpiConnectionSettings settings, int[] pins)
{
Initialize();

Expand Down Expand Up @@ -381,17 +439,54 @@ public virtual SpiDevice CreateSpiDevice(SpiConnectionSettings settings)
/// <param name="frequency">This value is ignored</param>
/// <param name="dutyCyclePercentage">The duty cycle as a fraction.</param>
/// <returns></returns>
public virtual PwmChannel CreatePwmChannel(
protected override PwmChannel CreateSimplePwmChannel(
int chip,
int channel,
int frequency = 400,
double dutyCyclePercentage = 0.5)
int frequency,
double dutyCyclePercentage)
{
Initialize();

return new ArduinoPwmChannel(this, chip, channel, frequency, dutyCyclePercentage);
}

/// <inheritdoc />
public override int GetDefaultPinAssignmentForPwm(int chip, int channel)
{
if (chip == 0)
{
return channel;
}

throw new NotSupportedException($"Unknown chip numbe {chip}");
}

/// <inheritdoc />
public override int[] GetDefaultPinAssignmentForI2c(int busId)
{
if (busId != 0)
{
throw new NotSupportedException("Only bus number 0 is currently supported");
}

var pins = _supportedPinConfigurations.Where(x => x.PinModes.Contains(SupportedMode.I2c)).Select(y => y.Pin);

return pins.ToArray();
}

/// <inheritdoc />
public override int[] GetDefaultPinAssignmentForSpi(SpiConnectionSettings connectionSettings)
{
if (connectionSettings.BusId != 0)
{
throw new NotSupportedException("Only bus number 0 is currently supported");
}

var pins = _supportedPinConfigurations.Where(x => x.PinModes.Contains(SupportedMode.Spi)).Select(y => y.Pin);

return pins.ToArray();
}

/// <summary>
/// Creates an anlog controller for this board.
/// </summary>
Expand Down Expand Up @@ -440,7 +535,7 @@ public bool TryReadDht(int pinNumber, int dhtType, out Temperature temperature,
/// <summary>
/// Standard dispose pattern
/// </summary>
protected virtual void Dispose(bool disposing)
protected override void Dispose(bool disposing)
{
_isDisposed = true;
// Do this first, to force any blocking read operations to end
Expand All @@ -465,15 +560,6 @@ protected virtual void Dispose(bool disposing)
_initialized = false;
}

/// <summary>
/// Dispose this instance
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

internal void EnableSpi()
{
_spiEnabled++;
Expand Down
41 changes: 41 additions & 0 deletions src/devices/Arduino/ArduinoI2cBus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Device.I2c;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Iot.Device.Arduino
{
internal class ArduinoI2cBus : I2cBus
{
private readonly ArduinoBoard _board;
private readonly int _busId;
private readonly HashSet<int> _usedAddresses;

public ArduinoI2cBus(ArduinoBoard board, int busId)
{
_board = board;
_busId = busId;
_usedAddresses = new HashSet<int>();
}

/// <inheritdoc />
public override I2cDevice CreateDevice(int deviceAddress)
{
if (_usedAddresses.Contains(deviceAddress))
{
throw new InvalidOperationException($"Device number {deviceAddress} is already in use");
}

var device = new ArduinoI2cDevice(_board, this, new I2cConnectionSettings(_busId, deviceAddress));
_usedAddresses.Add(deviceAddress);
return device;
}

public override void RemoveDevice(int deviceAddress)
{
_usedAddresses.Remove(deviceAddress);
}
}
}
18 changes: 17 additions & 1 deletion src/devices/Arduino/ArduinoI2cDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ namespace Iot.Device.Arduino
internal class ArduinoI2cDevice : I2cDevice
{
private readonly ArduinoBoard _board;
private ArduinoI2cBus? _bus;

public ArduinoI2cDevice(ArduinoBoard board, I2cConnectionSettings connectionSettings)
public ArduinoI2cDevice(ArduinoBoard board, ArduinoI2cBus bus, I2cConnectionSettings connectionSettings)
{
_board = board ?? throw new ArgumentNullException(nameof(board));
_bus = bus ?? throw new ArgumentNullException(nameof(bus));

if (connectionSettings == null)
{
Expand Down Expand Up @@ -98,5 +100,19 @@ public override void WriteRead(ReadOnlySpan<byte> writeBuffer, Span<byte> readBu
{
_board.Firmata.WriteReadI2cData(ConnectionSettings.DeviceAddress, writeBuffer, readBuffer);
}

protected override void Dispose(bool disposing)
{
if (disposing)
{
if (_bus != null)
{
_bus.RemoveDevice(ConnectionSettings.DeviceAddress);
_bus = null;
}
}

base.Dispose(disposing);
}
}
}
Loading

0 comments on commit 54a33be

Please sign in to comment.