NicolaySerialSFM3x00 1.0.0

dotnet add package NicolaySerialSFM3x00 --version 1.0.0
                    
NuGet\Install-Package NicolaySerialSFM3x00 -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="NicolaySerialSFM3x00" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NicolaySerialSFM3x00" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="NicolaySerialSFM3x00" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add NicolaySerialSFM3x00 --version 1.0.0
                    
#r "nuget: NicolaySerialSFM3x00, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package NicolaySerialSFM3x00@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=NicolaySerialSFM3x00&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=NicolaySerialSFM3x00&version=1.0.0
                    
Install as a Cake Tool

Nicolay Serial SFM3x00

A .NET library for operating Sensirion SFM3x00 flow meters through the Nicolay flow meter connector over an RS232 or RS485 serial interface. The library supports polling, continuous measurement streaming, request-response commands, and the optional AMS5915 pressure sensor available in some connectors.

The manufacturer's protocol specification lists these supported flow meters:

  • SFM3200-AW
  • SFM3300-AW
  • SFM3300-D
  • SFM3400-AW
  • SFM3400-D

Installation

dotnet add package NicolaySerialSFM3x00

The library targets netstandard2.1.

Getting started

using NicolaySerialSFM3x00;

await using var device = new SfmDevice("/dev/ttyUSB0");
await device.Connect();

if (!await device.Check())
{
    throw new InvalidOperationException("The device did not pass its self test");
}

// Flow in standard liters per minute, or null if the sensor cannot be read.
double? flow = await device.GetFlowSlmAsync();

The device address defaults to 1, which is the factory default. On a bus with several devices, pass the address to the constructor: new SfmDevice("/dev/ttyUSB0", address: 2).

Every request-response command accepts an optional timeout and cancellation token. For example: await device.GetFlowSlmAsync(timeoutMs: 500, cancellationToken: token). Requests that go unanswered throw after the timeout. The default timeout is 250 ms and can be changed with device.DefaultTimeoutMs.

Streaming

The round trip to the device limits polling to about 60 measurements per second at the default 115200 baud. Continuous measurement streaming is roughly eighteen times faster:

using var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(10));

await foreach (var sample in device.StreamAsync(cancellation.Token))
{
    Console.WriteLine($"{sample.FlowSlm:F4} slm");
}

Measured on a connector running firmware 0.99a, polling achieves about 62 Hz and streaming about 1100 Hz, which is close to the theoretical limit of the serial link.

The device stops streaming as soon as it receives any byte, so no other command can be sent while a stream is running. Calls to other methods wait until the stream ends. Leaving the loop, canceling the token, or disposing the enumerator stops the stream and returns the device to processing requests. Streamed packets carry no checksum, and the manufacturer recommends streaming only over RS232 or onboard USB.

Packets are buffered until they are read, so a consumer that cannot keep up will cause the buffer to grow.

Pressure

If a pressure sensor is installed, GetPressureAsync returns pressure in the sensor's own unit. The method uses calibration data that it reads from the device and caches:

var sensor = await device.GetPressureSensorInfoAsync();
if (sensor.IsPresent)
{
    Console.WriteLine($"{sensor.Type}: {await device.GetPressureAsync():F3}");
}

GetFlowAndPressureAsync reads both quantities in one request. This command is faster than two separate requests and captures both readings at the same time. To convert a raw count, such as one from a streamed packet, use sensor.ToPressure(raw).

Commands

Method Code Purpose
Check 0x05 Device self test
GetSoftwareVersionAsync 0x01 Board firmware version
GetHardwareVersionAsync 0x02 Board hardware version
GetPressureSensorInfoAsync 0x06 Installed pressure sensor and its calibration
GetRawPressureAsync, GetPressureAsync 0x07 Raw or converted pressure
GetFlowAndPressureAsync 0x09 Flow and pressure in one request
GetSensorArticleNumberAsync 0x0A Sensirion article number
BoardHardwareResetAsync 0x0B Restart the board and sensor
SensorHardResetAsync 0x0C Power cycle the sensor
SensorSoftResetAsync 0x0D Soft reset the sensor
StartFlowSensorAsync 0x0E Restart flow measurement
GetSensorSerialNumberAsync 0x0F Sensor serial number
GetFlowMilliSlmAsync, GetFlowSlmAsync 0x10 Flow calculated by the board
GetRawFlowAsync 0x11 Raw flow measurement
GetFlowScaleAsync, GetFlowOffsetAsync 0x12, 0x13 Flow calibration
GetHeaterStateAsync, SetHeaterStateAsync 0x14 Sensor heater on or off
GetHeaterPowerAsync, SetHeaterPowerAsync 0x15 Sensor heater power
GetTemperatureScaleAsync, GetTemperatureOffsetAsync 0x18, 0x19 Temperature calibration
ForceTemperatureUpdateAsync 0x1B Chip temperature in degrees Celsius
ForceRawTemperatureUpdateAsync 0x1C Raw chip temperature
StreamAsync 0x1E Continuous measurement stream
SetBaudRateAsync 0x22 Change the UART baud rate

Bulk read (0x1D) is not implemented. Streaming covers the same need and is simpler to consume. The protocol also defines a get form of the baud rate command. Its request layout is unspecified, so the library supports only the set form.

Errors

Failures are reported as exceptions deriving from SfmException:

  • SfmDeviceException when the device refuses a request. The exception contains the reason as an SfmExceptionCode, such as Busy or ShutdownHardwareResetRequired.
  • SfmTimeoutException when no response arrives in time.
  • SfmCrcException when a response fails its checksum.

If the device reports that it could not read a sensor, the affected methods return null. This is an expected condition and does not indicate a protocol failure. The datasheet specifies SensorHardResetAsync as the recovery procedure. Methods that return a raw value return it unchanged, with the sentinel documented on the method.

If the read loop stops unexpectedly, for instance because the port is removed, the ReadFault event is raised and any request in flight fails with the same error.

Device behavior

Reading the sensor EEPROM, which GetSensorArticleNumberAsync and GetSensorSerialNumberAsync do, leaves the sensor busy for a few hundred milliseconds. During that interval, a temperature read has been observed to return the temperature offset instead of a measurement. The returned value appears to be roughly 200 degrees Celsius, and the raw temperature is 0xFFFF. Wait a few hundred milliseconds after reading either identifier before reading the temperature.

Building

dotnet build
dotnet test

The test suite covers framing, checksums, and response decoding against the values in the manufacturer's protocol specification, and exercises the device through a fake serial port, so no hardware is needed to run it.

Example/ is a console program that prints the connected device's information and benchmarks polling against streaming:

dotnet run --project Example -- /dev/ttyUSB0

License

MIT, see LICENSE.

Versioning

Version 1.0.0 changes the API exposed by version 0.2.0. GetValue is obsolete in favor of GetRawFlowAsync, which returns the unsigned 16-bit count sent by the device. The byte-level parser is no longer public.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 110 9/10/2026
0.2.0 150 1/30/2026