Skip to content

Commit cbf35d2

Browse files
authored
Add support for Firmata 2.7 (dotnet#2093)
* Add support for Firmata 2.7 * Update CompatibilitySuppressions.xml
1 parent bd3eb2d commit cbf35d2

17 files changed

+686
-153
lines changed

src/Iot.Device.Bindings/CompatibilitySuppressions.xml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,20 @@
582582
<Right>lib/net6.0/Iot.Device.Bindings.dll</Right>
583583
<IsBaselineSuppression>true</IsBaselineSuppression>
584584
</Suppression>
585+
<Suppression>
586+
<DiagnosticId>CP0002</DiagnosticId>
587+
<Target>M:System.Device.Analog.AnalogController.ConvertLogicalNumberingSchemeToPinNumber(System.Int32)</Target>
588+
<Left>lib/net6.0/Iot.Device.Bindings.dll</Left>
589+
<Right>lib/net6.0/Iot.Device.Bindings.dll</Right>
590+
<IsBaselineSuppression>true</IsBaselineSuppression>
591+
</Suppression>
592+
<Suppression>
593+
<DiagnosticId>CP0002</DiagnosticId>
594+
<Target>M:System.Device.Analog.AnalogController.ConvertPinNumberToLogicalNumberingScheme(System.Int32)</Target>
595+
<Left>lib/net6.0/Iot.Device.Bindings.dll</Left>
596+
<Right>lib/net6.0/Iot.Device.Bindings.dll</Right>
597+
<IsBaselineSuppression>true</IsBaselineSuppression>
598+
</Suppression>
585599
<Suppression>
586600
<DiagnosticId>CP0002</DiagnosticId>
587601
<Target>F:Iot.Device.Imu.Mpu6500.DefaultI2cAddress</Target>
@@ -869,6 +883,20 @@
869883
<Right>lib/netstandard2.0/Iot.Device.Bindings.dll</Right>
870884
<IsBaselineSuppression>true</IsBaselineSuppression>
871885
</Suppression>
886+
<Suppression>
887+
<DiagnosticId>CP0002</DiagnosticId>
888+
<Target>M:System.Device.Analog.AnalogController.ConvertLogicalNumberingSchemeToPinNumber(System.Int32)</Target>
889+
<Left>lib/netcoreapp3.1/Iot.Device.Bindings.dll</Left>
890+
<Right>lib/netstandard2.0/Iot.Device.Bindings.dll</Right>
891+
<IsBaselineSuppression>true</IsBaselineSuppression>
892+
</Suppression>
893+
<Suppression>
894+
<DiagnosticId>CP0002</DiagnosticId>
895+
<Target>M:System.Device.Analog.AnalogController.ConvertPinNumberToLogicalNumberingScheme(System.Int32)</Target>
896+
<Left>lib/netcoreapp3.1/Iot.Device.Bindings.dll</Left>
897+
<Right>lib/netstandard2.0/Iot.Device.Bindings.dll</Right>
898+
<IsBaselineSuppression>true</IsBaselineSuppression>
899+
</Suppression>
872900
<Suppression>
873901
<DiagnosticId>CP0002</DiagnosticId>
874902
<Target>F:Iot.Device.Imu.Mpu6500.DefaultI2cAddress</Target>
@@ -1149,6 +1177,20 @@
11491177
<Right>lib/netstandard2.0/Iot.Device.Bindings.dll</Right>
11501178
<IsBaselineSuppression>true</IsBaselineSuppression>
11511179
</Suppression>
1180+
<Suppression>
1181+
<DiagnosticId>CP0002</DiagnosticId>
1182+
<Target>M:System.Device.Analog.AnalogController.ConvertLogicalNumberingSchemeToPinNumber(System.Int32)</Target>
1183+
<Left>lib/netstandard2.0/Iot.Device.Bindings.dll</Left>
1184+
<Right>lib/netstandard2.0/Iot.Device.Bindings.dll</Right>
1185+
<IsBaselineSuppression>true</IsBaselineSuppression>
1186+
</Suppression>
1187+
<Suppression>
1188+
<DiagnosticId>CP0002</DiagnosticId>
1189+
<Target>M:System.Device.Analog.AnalogController.ConvertPinNumberToLogicalNumberingScheme(System.Int32)</Target>
1190+
<Left>lib/netstandard2.0/Iot.Device.Bindings.dll</Left>
1191+
<Right>lib/netstandard2.0/Iot.Device.Bindings.dll</Right>
1192+
<IsBaselineSuppression>true</IsBaselineSuppression>
1193+
</Suppression>
11521194
<Suppression>
11531195
<DiagnosticId>CP0005</DiagnosticId>
11541196
<Target>M:Iot.Device.Card.CardTransceiver.get_MaximumReadSize</Target>

src/devices/Arduino/ArduinoAnalogController.cs

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -35,60 +35,58 @@ public override int PinCount
3535
get;
3636
}
3737

38-
public override int ConvertPinNumberToLogicalNumberingScheme(int pinNumber)
38+
/// <inheritdoc />
39+
public override int ConvertPinNumberToAnalogChannelNumber(int pinNumber)
3940
{
40-
int numberAnalogPinsFound = 0;
41-
for (int i = 0; i < _supportedPinConfigurations.Count; i++)
41+
if (pinNumber >= 0 && pinNumber < _supportedPinConfigurations.Count)
4242
{
43-
if (_supportedPinConfigurations[i].PinModes.Contains(SupportedMode.AnalogInput))
43+
int channel = _supportedPinConfigurations[pinNumber].AnalogPinNumber;
44+
if (channel != 127)
4445
{
45-
numberAnalogPinsFound++;
46-
if (pinNumber == i)
47-
{
48-
return numberAnalogPinsFound - 1;
49-
}
46+
return channel;
5047
}
48+
49+
return -1;
5150
}
5251

53-
throw new InvalidOperationException($"Pin {pinNumber} is not a valid analog input pin.");
52+
throw new InvalidOperationException($"Pin {pinNumber} is not a valid input pin.");
5453
}
5554

56-
public override int ConvertLogicalNumberingSchemeToPinNumber(int logicalPinNumber)
55+
/// <inheritdoc />
56+
public override int ConvertAnalogChannelNumberToPinNumber(int analogChannelNumber)
5757
{
58-
int numberAnalogPinsFound = 0;
5958
for (int i = 0; i < _supportedPinConfigurations.Count; i++)
6059
{
61-
if (_supportedPinConfigurations[i].PinModes.Contains(SupportedMode.AnalogInput))
60+
if (_supportedPinConfigurations[i].AnalogPinNumber == analogChannelNumber)
6261
{
63-
numberAnalogPinsFound++;
64-
if (logicalPinNumber == numberAnalogPinsFound - 1)
65-
{
66-
return i;
67-
}
62+
return i;
6863
}
6964
}
7065

71-
throw new InvalidOperationException($"Pin A{logicalPinNumber} does not exist");
66+
return -1;
7267
}
7368

7469
public override bool SupportsAnalogInput(int pinNumber)
7570
{
71+
if (pinNumber >= _supportedPinConfigurations.Count || pinNumber < 0)
72+
{
73+
return false;
74+
}
75+
7676
return _supportedPinConfigurations[pinNumber].PinModes.Contains(SupportedMode.AnalogInput);
7777
}
7878

7979
protected override AnalogInputPin OpenPinCore(int pinNumber)
8080
{
81-
// This method is called with the logical pin numbering (input pin A0 is 0, A1 is 1, etc)
82-
// but the SetPinMode method operates on the global numbers
83-
int fullNumber = ConvertLogicalNumberingSchemeToPinNumber(pinNumber);
84-
_board.Firmata.SetPinMode(fullNumber, SupportedMode.AnalogInput);
85-
_board.Firmata.EnableAnalogReporting(pinNumber);
86-
return new ArduinoAnalogInputPin(_board, this, _supportedPinConfigurations[fullNumber], pinNumber, VoltageReference);
81+
_board.Firmata.SetPinMode(pinNumber, SupportedMode.AnalogInput);
82+
_board.Firmata.EnableAnalogReporting(pinNumber, ConvertPinNumberToAnalogChannelNumber(pinNumber));
83+
return new ArduinoAnalogInputPin(_board, this, _supportedPinConfigurations[pinNumber], pinNumber, VoltageReference);
8784
}
8885

8986
public override void ClosePin(AnalogInputPin pin)
9087
{
91-
_board.Firmata.DisableAnalogReporting(pin.PinNumber);
88+
base.ClosePin(pin);
89+
_board.Firmata.DisableAnalogReporting(pin.PinNumber, ConvertPinNumberToAnalogChannelNumber(pin.PinNumber));
9290
}
9391
}
9492
}

src/devices/Arduino/ArduinoAnalogInputPin.cs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,11 @@ public override void DisableAnalogValueChangedEvent()
4949
}
5050
}
5151

52-
private void FirmataOnAnalogPinValueUpdated(int pin, uint rawvalue)
52+
private void FirmataOnAnalogPinValueUpdated(int channel, uint rawvalue)
5353
{
5454
if (_autoReportingReferenceCount > 0)
5555
{
56-
int physicalPin = Controller.ConvertLogicalNumberingSchemeToPinNumber(pin);
56+
int physicalPin = Controller.ConvertAnalogChannelNumberToPinNumber(channel);
5757
ElectricPotential voltage = ConvertToVoltage(rawvalue);
5858
var message = new ValueChangedEventArgs(rawvalue, voltage, physicalPin, TriggerReason.Timed);
5959
FireValueChanged(message);
@@ -65,13 +65,22 @@ private void FirmataOnAnalogPinValueUpdated(int pin, uint rawvalue)
6565
/// <summary>
6666
/// The arduino would theoretically allow for an external analog reference, but firmata currently doesn't support that.
6767
/// </summary>
68-
public override ElectricPotential MaxVoltage => ElectricPotential.FromVolts(5);
68+
public override ElectricPotential MaxVoltage => VoltageReference;
6969

7070
/// <summary>
71-
/// Similar here: Some boards support more than 10 bit resolution, but we'd have to extend the firmware for that.
71+
/// The ADC resolution for this pin, as reported by the firmware.
7272
/// </summary>
73-
public override int AdcResolutionBits => 10;
73+
public override int AdcResolutionBits => _configuration.AnalogInputResolutionBits;
7474

75+
/// <summary>
76+
/// Reads the analog raw value from a given pin.
77+
/// </summary>
78+
/// <returns>The analog raw value of the given pin</returns>
79+
/// <remarks>
80+
/// This returns the last cached value from the board. The board sends regular updates when a value changes,
81+
/// but this does not request an update before returning a value, so that the read value might be incorrect
82+
/// if the analog pin has just been opened.
83+
/// </remarks>
7584
public override uint ReadRaw()
7685
{
7786
return _board.Firmata.GetAnalogRawValue(PinNumber);

src/devices/Arduino/ArduinoBoard.cs

Lines changed: 89 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,19 @@ protected override void Initialize()
448448

449449
_firmata.EnableDigitalReporting();
450450

451+
string? result = _firmata.CheckSystemVariablesSupported();
452+
if (result != null)
453+
{
454+
Logger.LogInformation($"System variable support not available in firmware. Error: {result}");
455+
}
456+
else
457+
{
458+
Logger.LogInformation("System variable support detected");
459+
GetSystemVariable(SystemVariable.MaxSysexSize, -1, out int bufferSize);
460+
// Should be excluding the SYSEX byte itself and the terminator, but see https://github.com/firmata/ConfigurableFirmata/issues/136
461+
Logger.LogInformation($"Maximum SYSEX message size: {bufferSize}");
462+
}
463+
451464
foreach (ExtendedCommandHandler e in _extendedCommandHandlers)
452465
{
453466
e.Registered(_firmata, this);
@@ -458,41 +471,91 @@ protected override void Initialize()
458471
}
459472
}
460473

474+
/// <summary>
475+
/// Queries the given system variable.
476+
/// </summary>
477+
/// <param name="variableId">The variable to query</param>
478+
/// <param name="value">Receives the value</param>
479+
/// <returns>True on success, false otherwise (value not supported, etc. Check the log output)</returns>
480+
/// <exception cref="IOException">There was an error sending the command</exception>
481+
public bool GetSystemVariable(SystemVariable variableId, out int value)
482+
{
483+
value = 0;
484+
return Firmata.GetOrSetSystemVariable(variableId, -1, true, ref value);
485+
}
486+
487+
/// <summary>
488+
/// Queries the given system variable.
489+
/// </summary>
490+
/// <param name="variableId">The variable to query</param>
491+
/// <param name="pinNumber">The pin number to use (-1 if not applicable for the given parameter)</param>
492+
/// <param name="value">Receives the value</param>
493+
/// <returns>True on success, false otherwise (value not supported, etc. Check the log output)</returns>
494+
/// <exception cref="IOException">There was an error sending the command</exception>
495+
public bool GetSystemVariable(SystemVariable variableId, int pinNumber, out int value)
496+
{
497+
value = 0;
498+
return Firmata.GetOrSetSystemVariable(variableId, pinNumber, true, ref value);
499+
}
500+
501+
/// <summary>
502+
/// Update the given system variable.
503+
/// </summary>
504+
/// <param name="variableId">The variable to update</param>
505+
/// <param name="value">The new value</param>
506+
/// <returns>True on success, false otherwise (check the log output)</returns>
507+
/// <exception cref="IOException">There was a communication error</exception>
508+
public bool SetSystemVariable(SystemVariable variableId, int value)
509+
{
510+
return Firmata.GetOrSetSystemVariable(variableId, -1, false, ref value);
511+
}
512+
513+
/// <summary>
514+
/// Update the given system variable.
515+
/// </summary>
516+
/// <param name="variableId">The variable to update</param>
517+
/// <param name="pinNumber">The pin number to use, or -1 if not relevant</param>
518+
/// <param name="value">The new value</param>
519+
/// <returns>True on success, false otherwise (check the log output)</returns>
520+
/// <exception cref="IOException">There was a communication error</exception>
521+
public bool SetSystemVariable(SystemVariable variableId, int pinNumber, int value)
522+
{
523+
return Firmata.GetOrSetSystemVariable(variableId, pinNumber, false, ref value);
524+
}
525+
461526
private void RegisterCommandHandlers()
462527
{
463-
lock (_commandHandlersLock)
464-
{
465-
_extendedCommandHandlers.Add(new DhtSensor());
466-
_extendedCommandHandlers.Add(new FrequencySensor());
467-
}
528+
_commandHandlersLock.EnterWriteLock();
529+
_extendedCommandHandlers.Add(new DhtSensor());
530+
_extendedCommandHandlers.Add(new FrequencySensor());
531+
_commandHandlersLock.ExitWriteLock();
468532
}
469533

470534
/// <summary>
471535
/// Registers the known supported modes. Should only be called once from Initialize.
472536
/// </summary>
473537
private void RegisterKnownSupportedModes()
474538
{
475-
lock (_commandHandlersLock)
476-
{
477-
// We add all known modes to the list, even though we don't really support them all in the core
478-
_knownSupportedModes.Add(SupportedMode.DigitalInput);
479-
_knownSupportedModes.Add(SupportedMode.DigitalOutput);
480-
_knownSupportedModes.Add(SupportedMode.AnalogInput);
481-
_knownSupportedModes.Add(SupportedMode.Pwm);
482-
_knownSupportedModes.Add(SupportedMode.Servo);
483-
_knownSupportedModes.Add(SupportedMode.Shift);
484-
_knownSupportedModes.Add(SupportedMode.I2c);
485-
_knownSupportedModes.Add(SupportedMode.OneWire);
486-
_knownSupportedModes.Add(SupportedMode.Stepper);
487-
_knownSupportedModes.Add(SupportedMode.Encoder);
488-
_knownSupportedModes.Add(SupportedMode.Serial);
489-
_knownSupportedModes.Add(SupportedMode.InputPullup);
490-
_knownSupportedModes.Add(SupportedMode.Spi);
491-
_knownSupportedModes.Add(SupportedMode.Sonar);
492-
_knownSupportedModes.Add(SupportedMode.Tone);
493-
_knownSupportedModes.Add(SupportedMode.Dht);
494-
_knownSupportedModes.Add(SupportedMode.Frequency);
495-
}
539+
_commandHandlersLock.EnterWriteLock();
540+
// We add all known modes to the list, even though we don't really support them all in the core
541+
_knownSupportedModes.Add(SupportedMode.DigitalInput);
542+
_knownSupportedModes.Add(SupportedMode.DigitalOutput);
543+
_knownSupportedModes.Add(SupportedMode.AnalogInput);
544+
_knownSupportedModes.Add(SupportedMode.Pwm);
545+
_knownSupportedModes.Add(SupportedMode.Servo);
546+
_knownSupportedModes.Add(SupportedMode.Shift);
547+
_knownSupportedModes.Add(SupportedMode.I2c);
548+
_knownSupportedModes.Add(SupportedMode.OneWire);
549+
_knownSupportedModes.Add(SupportedMode.Stepper);
550+
_knownSupportedModes.Add(SupportedMode.Encoder);
551+
_knownSupportedModes.Add(SupportedMode.Serial);
552+
_knownSupportedModes.Add(SupportedMode.InputPullup);
553+
_knownSupportedModes.Add(SupportedMode.Spi);
554+
_knownSupportedModes.Add(SupportedMode.Sonar);
555+
_knownSupportedModes.Add(SupportedMode.Tone);
556+
_knownSupportedModes.Add(SupportedMode.Dht);
557+
_knownSupportedModes.Add(SupportedMode.Frequency);
558+
_commandHandlersLock.ExitWriteLock();
496559
}
497560

498561
/// <summary>

src/devices/Arduino/FirmataCommandSequence.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,16 @@ public void WriteBytesAsTwo7bitBytes(ReadOnlySpan<byte> values)
208208
}
209209
}
210210

211+
/// <summary>
212+
/// Write a packed Int14 to the stream. This is used to write an integer of up to 14 bits.
213+
/// </summary>
214+
/// <param name="value">The value to write. Only the 14 least significant bits are transmitted</param>
215+
public void SendInt14(int value)
216+
{
217+
WriteByte((byte)(value & 0x7F));
218+
WriteByte((byte)((value >> 7) & 0x7F));
219+
}
220+
211221
/// <inheritdoc/>
212222
public override string ToString()
213223
{

0 commit comments

Comments
 (0)