Skip to content

Commit 2bcfdf1

Browse files
SpotiguspjhenaultjosesimoesEllerbach
authored
Migration Mhz19b and documentation update (#229)
* Migrated Mhz19b sensor to nanoframework, updated fritzing, readme text, and added expected out example. * Update README.md * Update README.md Added fritzing and expected output. * Copied nuspec from existing src. * Updated nuspec dependencies to match project. * Fixed assembly naming in project to match for nuspec. * Removed xml requirement from nuspec. * Fixed nuspec paths per PR comment. * Update devices/Mhz19b/Properties/AssemblyInfo.cs Co-authored-by: José Simões <[email protected]> * Update README.md * Update AssemblyInfo.cs * Update AssemblyInfo.cs * Update Mhz19b.nfproj * Update Mhz19b.nfproj * Update Mhz19b.nuspec Added .xml for documentation * Create Mhz19b.nuspec Added .xml for documentation * Update devices/Mhz19b/Mhz19b.nuspec Co-authored-by: José Simões <[email protected]> * Update Program.cs Removed extra lines. * Update README.md Updated pin naming to avoid confusion. Co-authored-by: Phillip Henault <[email protected]> Co-authored-by: José Simões <[email protected]> Co-authored-by: Laurent Ellerbach <[email protected]>
1 parent b1c5834 commit 2bcfdf1

18 files changed

+656
-0
lines changed

Diff for: devices/Mhz19b/AbmState.cs

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Iot.Device.Mhz19b
5+
{
6+
/// <summary>
7+
/// Defines if automatic baseline correction (ABM) is on or off
8+
/// For details refer to datasheet, rev. 1.0, pg. 8
9+
/// </summary>
10+
public enum AbmState
11+
{
12+
/// <summary>
13+
/// ABM off (value acc. to datasheet)
14+
/// </summary>
15+
Off = 0x00,
16+
17+
/// <summary>
18+
/// ABM on (value acc. to datasheet)
19+
/// </summary>
20+
On = 0xA0
21+
}
22+
}

Diff for: devices/Mhz19b/DetectionRange.cs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Iot.Device.Mhz19b
5+
{
6+
/// <summary>
7+
/// Defines the sensor detection range, which is either 2000 or 5000ppm.
8+
/// </summary>
9+
public enum DetectionRange
10+
{
11+
/// <summary>
12+
/// Detection range 2000ppm
13+
/// </summary>
14+
Range2000 = 2000,
15+
16+
/// <summary>
17+
/// Detection range 5000ppm
18+
/// </summary>
19+
Range5000 = 5000
20+
}
21+
}

Diff for: devices/Mhz19b/ExpectedOutput.png

30.3 KB
Loading

Diff for: devices/Mhz19b/Mhz19B.fzz

35.7 KB
Binary file not shown.

Diff for: devices/Mhz19b/Mhz19B_bb.png

90.8 KB
Loading

Diff for: devices/Mhz19b/Mhz19b.cs

+243
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Device.Model;
6+
using System.IO;
7+
using System.IO.Ports;
8+
using System.Threading;
9+
using UnitsNet;
10+
11+
namespace Iot.Device.Mhz19b
12+
{
13+
/// <summary>
14+
/// MH-Z19B CO2 concentration sensor binding
15+
/// </summary>
16+
[Interface("MH-Z19B CO2 concentration sensor binding")]
17+
public sealed class Mhz19b : IDisposable
18+
{
19+
private const int MessageBytes = 9;
20+
private bool _shouldDispose = false;
21+
private SerialPort? _serialPort;
22+
private Stream _serialPortStream;
23+
24+
/// <summary>
25+
/// Initializes a new instance of the <see cref="Mhz19b"/> class using an existing (serial port) stream.
26+
/// </summary>
27+
/// <param name="stream">Existing stream</param>
28+
/// <param name="shouldDispose">If true, the stream gets disposed when disposing the binding</param>
29+
public Mhz19b(Stream stream, bool shouldDispose)
30+
{
31+
_serialPortStream = stream ?? throw new ArgumentNullException(nameof(stream));
32+
_shouldDispose = shouldDispose;
33+
}
34+
35+
/// <summary>
36+
/// Initializes a new instance of the <see cref="Mhz19b"/> class and creates a new serial port stream.
37+
/// </summary>
38+
/// <param name="uartDevice">Path to the UART device / serial port, e.g. /dev/serial0</param>
39+
/// <exception cref="System.ArgumentException">uartDevice is null or empty</exception>
40+
public Mhz19b(string uartDevice)
41+
{
42+
if (uartDevice is not { Length: > 0 })
43+
{
44+
throw new ArgumentException(nameof(uartDevice));
45+
}
46+
47+
// create serial port using the setting acc. to datasheet, pg. 7, sec. general settings
48+
_serialPort = new SerialPort(uartDevice, 9600, Parity.None, 8, StopBits.One)
49+
{
50+
ReadTimeout = 1000,
51+
WriteTimeout = 1000
52+
};
53+
54+
_serialPort.Open();
55+
_serialPortStream = _serialPort.BaseStream;
56+
_shouldDispose = true;
57+
}
58+
59+
/// <summary>
60+
/// Gets the current CO2 concentration from the sensor.
61+
/// </summary>
62+
/// <returns>CO2 volume concentration</returns>
63+
/// <exception cref="IOException">Communication with sensor failed</exception>
64+
/// <exception cref="TimeoutException">A timeout occurred while communicating with the sensor</exception>
65+
[Telemetry("Co2Concentration")]
66+
public VolumeConcentration GetCo2Reading()
67+
{
68+
// send read command request
69+
byte[] request = CreateRequest(Command.ReadCo2Concentration);
70+
request[(int)MessageFormat.Checksum] = Checksum(request);
71+
_serialPortStream.Write(request, 0, request.Length);
72+
73+
// read complete response (9 bytes expected)
74+
byte[] response = new byte[MessageBytes];
75+
76+
long endTicks = DateTime.UtcNow.AddMilliseconds(250).Ticks;
77+
int bytesRead = 0;
78+
while (DateTime.UtcNow.Ticks < endTicks && bytesRead < MessageBytes)
79+
{
80+
bytesRead += _serialPortStream.Read(response, bytesRead, response.Length - bytesRead);
81+
Thread.Sleep(1);
82+
}
83+
84+
if (bytesRead < MessageBytes)
85+
{
86+
throw new TimeoutException($"Communication with sensor failed.");
87+
}
88+
89+
// check response and return calculated concentration if valid
90+
if (response[(int)MessageFormat.Checksum] == Checksum(response))
91+
{
92+
return VolumeConcentration.FromPartsPerMillion((int)response[(int)MessageFormat.DataHighResponse] * 256 + (int)response[(int)MessageFormat.DataLowResponse]);
93+
}
94+
else
95+
{
96+
throw new IOException("Invalid response message received from sensor");
97+
}
98+
}
99+
100+
/// <summary>
101+
/// Initiates a zero point calibration.
102+
/// </summary>
103+
/// <exception cref="System.IO.IOException">Communication with sensor failed</exception>
104+
[Command]
105+
public void PerformZeroPointCalibration() => SendRequest(CreateRequest(Command.CalibrateZeroPoint));
106+
107+
/// <summary>
108+
/// Initiate a span point calibration.
109+
/// </summary>
110+
/// <param name="span">span value, between 1000[ppm] and 5000[ppm]. The typical value is 2000[ppm].</param>
111+
/// <exception cref="System.ArgumentException">Thrown when span value is out of range</exception>
112+
/// <exception cref="System.IO.IOException">Communication with sensor failed</exception>
113+
[Command]
114+
public void PerformSpanPointCalibration(VolumeConcentration span)
115+
{
116+
if ((span.PartsPerMillion < 1000) || (span.PartsPerMillion > 5000))
117+
{
118+
throw new ArgumentException("Span value out of range (1000-5000[ppm])", nameof(span));
119+
}
120+
121+
byte[] request = CreateRequest(Command.CalibrateSpanPoint);
122+
// set span in request, c. f. datasheet rev. 1.0, pg. 8 for details
123+
request[(int)MessageFormat.DataHighRequest] = (byte)(span.PartsPerMillion / 256);
124+
request[(int)MessageFormat.DataLowRequest] = (byte)(span.PartsPerMillion % 256);
125+
126+
SendRequest(request);
127+
}
128+
129+
/// <summary>
130+
/// Switches the autmatic baseline correction on and off.
131+
/// </summary>
132+
/// <param name="state">State of automatic correction</param>
133+
/// <exception cref="System.IO.IOException">Communication with sensor failed</exception>
134+
[Command]
135+
public void SetAutomaticBaselineCorrection(AbmState state)
136+
{
137+
byte[] request = CreateRequest(Command.AutoCalibrationSwitch);
138+
// set on/off state in request, c. f. datasheet rev. 1.0, pg. 8 for details
139+
request[(int)MessageFormat.DataHighRequest] = (byte)state;
140+
141+
SendRequest(request);
142+
}
143+
144+
/// <summary>
145+
/// Set the sensor detection range.
146+
/// </summary>
147+
/// <param name="detectionRange">Detection range of the sensor</param>
148+
/// <exception cref="System.IO.IOException">Communication with sensor failed</exception>
149+
[Property("DetectionRange")]
150+
public void SetSensorDetectionRange(DetectionRange detectionRange)
151+
{
152+
byte[] request = CreateRequest(Command.DetectionRangeSetting);
153+
// set detection range in request, c. f. datasheet rev. 1.0, pg. 8 for details
154+
request[(int)MessageFormat.DataHighRequest] = (byte)((int)detectionRange / 256);
155+
request[(int)MessageFormat.DataLowRequest] = (byte)((int)detectionRange % 256);
156+
157+
SendRequest(request);
158+
}
159+
160+
private void SendRequest(byte[] request)
161+
{
162+
request[(int)MessageFormat.Checksum] = Checksum(request);
163+
164+
try
165+
{
166+
_serialPortStream.Write(request, 0, request.Length);
167+
}
168+
catch (Exception e)
169+
{
170+
throw new IOException("Sensor communication failed", e);
171+
}
172+
}
173+
174+
private byte[] CreateRequest(Command command) => new byte[]
175+
{
176+
0xff, // start byte,
177+
0x01, // sensor number, always 0x1
178+
(byte)command,
179+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // empty bytes
180+
};
181+
182+
/// <summary>
183+
/// Calculate checksum for requests and responses.
184+
/// For details refer to datasheet rev. 1.0, pg. 8.
185+
/// </summary>
186+
/// <param name="packet">Packet the checksum is calculated for</param>
187+
/// <returns>Cheksum</returns>
188+
private byte Checksum(byte[] packet)
189+
{
190+
byte checksum = 0;
191+
for (int i = 1; i < 8; i++)
192+
{
193+
checksum += packet[i];
194+
}
195+
196+
checksum = (byte)(0xff - checksum);
197+
checksum += 1;
198+
return checksum;
199+
}
200+
201+
/// <inheritdoc cref="IDisposable" />
202+
public void Dispose()
203+
{
204+
if (_shouldDispose)
205+
{
206+
_serialPortStream?.Dispose();
207+
_serialPortStream = null!;
208+
}
209+
210+
if (_serialPort is not null)
211+
{
212+
if (_serialPort.IsOpen)
213+
{
214+
_serialPort.Close();
215+
}
216+
}
217+
218+
_serialPort?.Dispose();
219+
_serialPort = null;
220+
}
221+
222+
private enum Command : byte
223+
{
224+
ReadCo2Concentration = 0x86,
225+
CalibrateZeroPoint = 0x87,
226+
CalibrateSpanPoint = 0x88,
227+
AutoCalibrationSwitch = 0x79,
228+
DetectionRangeSetting = 0x99
229+
}
230+
231+
private enum MessageFormat
232+
{
233+
Start = 0x00,
234+
SensorNum = 0x01,
235+
Command = 0x02,
236+
DataHighRequest = 0x03,
237+
DataLowRequest = 0x04,
238+
DataHighResponse = 0x02,
239+
DataLowResponse = 0x03,
240+
Checksum = 0x08
241+
}
242+
}
243+
}

Diff for: devices/Mhz19b/Mhz19b.nfproj

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="Current" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<PropertyGroup Label="Globals">
4+
<NanoFrameworkProjectSystemPath>$(MSBuildExtensionsPath)\nanoFramework\v1.0\</NanoFrameworkProjectSystemPath>
5+
</PropertyGroup>
6+
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.Default.props" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.Default.props')" />
7+
<PropertyGroup>
8+
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
9+
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
10+
<ProjectTypeGuids>{11A8DD76-328B-46DF-9F39-F559912D0360};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
11+
<ProjectGuid>28c03c4e-a89f-4168-b786-6ec851700358</ProjectGuid>
12+
<OutputType>Library</OutputType>
13+
<AppDesignerFolder>Properties</AppDesignerFolder>
14+
<FileAlignment>512</FileAlignment>
15+
<RootNamespace>Iot.Device.Mhz19b</RootNamespace>
16+
<AssemblyName>Iot.Device.Mhz19b</AssemblyName>
17+
<TargetFrameworkVersion>v1.0</TargetFrameworkVersion>
18+
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
19+
<DocumentationFile>bin\$(Configuration)\Iot.Device.Mhz19b.xml</DocumentationFile>
20+
</PropertyGroup>
21+
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.props" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.props')" />
22+
<ItemGroup>
23+
<Compile Include="AbmState.cs" />
24+
<Compile Include="DetectionRange.cs" />
25+
<Compile Include="Mhz19b.cs" />
26+
<Compile Include="Properties\AssemblyInfo.cs" />
27+
</ItemGroup>
28+
<ItemGroup>
29+
<Reference Include="mscorlib">
30+
<HintPath>packages\nanoFramework.CoreLibrary.1.11.7\lib\mscorlib.dll</HintPath>
31+
</Reference>
32+
<Reference Include="nanoFramework.Runtime.Events">
33+
<HintPath>packages\nanoFramework.Runtime.Events.1.9.2\lib\nanoFramework.Runtime.Events.dll</HintPath>
34+
</Reference>
35+
<Reference Include="System.Device.Model">
36+
<HintPath>packages\nanoFramework.System.Device.Model.1.0.259\lib\System.Device.Model.dll</HintPath>
37+
</Reference>
38+
<Reference Include="System.IO.Ports">
39+
<HintPath>packages\nanoFramework.System.IO.Ports.1.0.2\lib\System.IO.Ports.dll</HintPath>
40+
</Reference>
41+
<Reference Include="UnitsNet.VolumeConcentration">
42+
<HintPath>packages\UnitsNet.nanoFramework.VolumeConcentration.4.112.0\lib\UnitsNet.VolumeConcentration.dll</HintPath>
43+
</Reference>
44+
</ItemGroup>
45+
<ItemGroup>
46+
<None Include="packages.config" />
47+
</ItemGroup>
48+
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.CSharp.targets" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.CSharp.targets')" />
49+
<ProjectExtensions>
50+
<ProjectCapabilities>
51+
<ProjectConfigurationsDeclaredAsItems />
52+
</ProjectCapabilities>
53+
</ProjectExtensions>
54+
<Import Project="packages\Nerdbank.GitVersioning.3.4.194\build\Nerdbank.GitVersioning.targets" Condition="Exists('packages\Nerdbank.GitVersioning.3.4.194\build\Nerdbank.GitVersioning.targets')" />
55+
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
56+
<PropertyGroup>
57+
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.</ErrorText>
58+
</PropertyGroup>
59+
<Error Condition="!Exists('packages\Nerdbank.GitVersioning.3.4.194\build\Nerdbank.GitVersioning.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Nerdbank.GitVersioning.3.4.194\build\Nerdbank.GitVersioning.targets'))" />
60+
</Target>
61+
</Project>

Diff for: devices/Mhz19b/Mhz19b.nuspec

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
3+
<metadata>
4+
<id>nanoFramework.Iot.Device.Mhz19b</id>
5+
<version>$version$</version>
6+
<title>nanoFramework.Iot.Device.Mhz19b</title>
7+
<authors>nanoFramework project contributors</authors>
8+
<owners>nanoFramework,dotnetfoundation</owners>
9+
<requireLicenseAcceptance>false</requireLicenseAcceptance>
10+
<license type="file">LICENSE.md</license>
11+
<releaseNotes>
12+
</releaseNotes>
13+
<readme>docs\README.md</readme>
14+
<developmentDependency>false</developmentDependency>
15+
<projectUrl>https://github.com/nanoframework/nanoFramework.IoT.Device</projectUrl>
16+
<icon>images\nf-logo.png</icon>
17+
<repository type="git" url="https://github.com/nanoframework/nanoFramework.IoT.Device" commit="$commit$" />
18+
<copyright>Copyright (c) .NET Foundation and Contributors</copyright>
19+
<description>This package includes the .NET IoT Core binding Iot.Device.Mhz19b for .NET nanoFramework C# projects.</description>
20+
<tags>nanoFramework C# csharp netmf netnf Iot.Device.Mhz19b</tags>
21+
<dependencies>
22+
<dependency id="nanoFramework.CoreLibrary" version="1.11.7" />
23+
<dependency id="nanoFramework.Runtime.Events" version="1.9.2" />
24+
<dependency id="nanoFramework.System.Device.Model" version="1.0.259" />
25+
<dependency id="nanoFramework.System.IO.Ports" version="1.0.2" />
26+
<dependency id="UnitsNet.nanoFramework.VolumeConcentration" version="4.112.0" />
27+
</dependencies>
28+
</metadata>
29+
<files>
30+
<file src="bin\Release\Iot.Device.Mhz19b.dll" target="lib\Iot.Device.Mhz19b.dll" />
31+
<file src="bin\Release\Iot.Device.Mhz19b.pdb" target="lib\Iot.Device.Mhz19b.pdb" />
32+
<file src="bin\Release\Iot.Device.Mhz19b.pdbx" target="lib\Iot.Device.Mhz19b.pdbx" />
33+
<file src="bin\Release\Iot.Device.Mhz19b.pe" target="lib\Iot.Device.Mhz19b.pe" />
34+
<file src="bin\Release\Iot.Device.Mhz19b.xml" target="lib\Iot.Device.Mhz19b.xml" />
35+
<file src="README.md" target="docs\" />
36+
<file src="..\..\assets\nf-logo.png" target="images" />
37+
<file src="..\..\LICENSE.md" target="" />
38+
</files>
39+
</package>

0 commit comments

Comments
 (0)