Initial Commit
This commit is contained in:
commit
b70e14c028
19 changed files with 613 additions and 0 deletions
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
bin/
|
||||||
|
obj/
|
||||||
|
/packages/
|
||||||
|
riderModule.iml
|
||||||
|
/_ReSharper.Caches/
|
||||||
|
notes.md
|
13
.idea/.idea.PMS5003/.idea/.gitignore
vendored
Normal file
13
.idea/.idea.PMS5003/.idea/.gitignore
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Rider ignored files
|
||||||
|
/contentModel.xml
|
||||||
|
/modules.xml
|
||||||
|
/projectSettingsUpdater.xml
|
||||||
|
/.idea.PMS5003.iml
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/../../../../../../../:\Projects\Rider\PMS5003\.idea\.idea.PMS5003\.idea/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
4
.idea/.idea.PMS5003/.idea/encodings.xml
Normal file
4
.idea/.idea.PMS5003/.idea/encodings.xml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||||
|
</project>
|
8
.idea/.idea.PMS5003/.idea/indexLayout.xml
Normal file
8
.idea/.idea.PMS5003/.idea/indexLayout.xml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ContentModelUserStore">
|
||||||
|
<attachedFolders />
|
||||||
|
<explicitIncludes />
|
||||||
|
<explicitExcludes />
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/.idea.PMS5003/.idea/vcs.xml
Normal file
6
.idea/.idea.PMS5003/.idea/vcs.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
22
PMS5003.sln
Normal file
22
PMS5003.sln
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PMS5003", "PMS5003\PMS5003.csproj", "{918D79E6-9AE8-4FA8-8400-BD3A26BB4A60}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PMS5003Tests", "PMS5003Tests\PMS5003Tests.csproj", "{AF7541BD-C0B1-42FE-AEFB-1A1D9CEAC6B9}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{918D79E6-9AE8-4FA8-8400-BD3A26BB4A60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{918D79E6-9AE8-4FA8-8400-BD3A26BB4A60}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{918D79E6-9AE8-4FA8-8400-BD3A26BB4A60}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{918D79E6-9AE8-4FA8-8400-BD3A26BB4A60}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{AF7541BD-C0B1-42FE-AEFB-1A1D9CEAC6B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{AF7541BD-C0B1-42FE-AEFB-1A1D9CEAC6B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{AF7541BD-C0B1-42FE-AEFB-1A1D9CEAC6B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{AF7541BD-C0B1-42FE-AEFB-1A1D9CEAC6B9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
6
PMS5003.sln.DotSettings.user
Normal file
6
PMS5003.sln.DotSettings.user
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=3358b775_002D8738_002D4b8b_002D978a_002D13b28217715f/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" Name="Pms5003DataUnitTest" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||||
|
<TestAncestor>
|
||||||
|
<TestId>xUnit::AF7541BD-C0B1-42FE-AEFB-1A1D9CEAC6B9::.NETCoreApp,Version=v3.1::PMS5003Tests.Pms5003DataUnitTest</TestId>
|
||||||
|
</TestAncestor>
|
||||||
|
</SessionState></s:String></wpf:ResourceDictionary>
|
16
PMS5003/Exceptions/BufferUnderflowExceptions.cs
Normal file
16
PMS5003/Exceptions/BufferUnderflowExceptions.cs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PMS5003.Exceptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// BufferUnderflowExceptions is thrown when the PMS5003 data buffer is incomplete.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public class BufferUnderflowException : Exception
|
||||||
|
{
|
||||||
|
public BufferUnderflowException() : base("The PMS5003 data buffer is underrun, not enough bytes read!")
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
PMS5003/Exceptions/ChecksumMismatchException.cs
Normal file
14
PMS5003/Exceptions/ChecksumMismatchException.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PMS5003.Exceptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ChecksumMismatchException when the frame contents of PMS5003 doesn't match the checksum.
|
||||||
|
/// </summary>
|
||||||
|
public class ChecksumMismatchException : Exception
|
||||||
|
{
|
||||||
|
public ChecksumMismatchException() : base("Checksum mismatch.")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
PMS5003/Exceptions/InvalidStartByteException.cs
Normal file
16
PMS5003/Exceptions/InvalidStartByteException.cs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PMS5003.Exceptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Exception that is thrown by the PMS5003 sensor when the read contains an invalid start sequence.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public class InvalidStartByteException : Exception
|
||||||
|
{
|
||||||
|
public InvalidStartByteException(short firstByte, short secondByte) : base(
|
||||||
|
$"Invalid start characters, expected 0x42 0x4d got [{firstByte:X}, {secondByte:X}]")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
PMS5003/Exceptions/ReadFailedException.cs
Normal file
14
PMS5003/Exceptions/ReadFailedException.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PMS5003.Exceptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Thrown the the read has failed.
|
||||||
|
/// </summary>
|
||||||
|
public class ReadFailedException : Exception
|
||||||
|
{
|
||||||
|
public ReadFailedException() : base("PMS5003 read failed, max retries exceeded")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
151
PMS5003/PMS5003.cs
Normal file
151
PMS5003/PMS5003.cs
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
using System;
|
||||||
|
using System.Device.Gpio;
|
||||||
|
using System.IO.Ports;
|
||||||
|
using System.Threading;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using PMS5003.Exceptions;
|
||||||
|
|
||||||
|
namespace PMS5003
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// PMS5003 digital universal particle concentration sensor.
|
||||||
|
/// Datasheet: https://www.aqmd.gov/docs/default-source/aq-spec/resources-page/plantower-pms5003-manual_v2-3.pdf
|
||||||
|
/// </summary>
|
||||||
|
public class Pms5003
|
||||||
|
{
|
||||||
|
public static Logger<Pms5003> Logger;
|
||||||
|
private readonly GpioController _gpioController;
|
||||||
|
private readonly SerialPort _serialPort;
|
||||||
|
private readonly short _pinSet;
|
||||||
|
private readonly short _pinReset;
|
||||||
|
private bool _isSleeping;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the <see cref="Pms5003"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="portSerialName">The name of the serial port.</param>
|
||||||
|
/// <param name="pinSet">The number of the SET pin.</param>
|
||||||
|
/// <param name="pinReset">The number if the RESET pin.</param>
|
||||||
|
public Pms5003(string portSerialName, short pinSet, short pinReset)
|
||||||
|
{
|
||||||
|
_serialPort = new SerialPort(portSerialName, Pms5003Constants.DefaultBaudRate);
|
||||||
|
_pinReset = pinReset;
|
||||||
|
_pinSet = pinSet;
|
||||||
|
|
||||||
|
_serialPort.Open();
|
||||||
|
if (Environment.OSVersion.Platform == PlatformID.Unix)
|
||||||
|
{
|
||||||
|
_gpioController = new GpioController();
|
||||||
|
if (pinSet >= 0)
|
||||||
|
{
|
||||||
|
_gpioController.OpenPin(_pinSet, PinMode.Output);
|
||||||
|
_gpioController.Write(_pinSet, PinValue.High);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pinReset >= 0)
|
||||||
|
{
|
||||||
|
_gpioController.OpenPin(_pinReset, PinMode.Output);
|
||||||
|
_gpioController.Write(_pinReset, PinValue.High);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger?.LogWarning("Not running under Unix platform, skipping GPIO configuration.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the <see cref="Pms5003"/> using /dev/ttyAMA0 serial port name.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pinSet"></param>
|
||||||
|
/// <param name="pinReset"></param>
|
||||||
|
public Pms5003(short pinSet, short pinReset) : this("/dev/ttyAMA0", pinSet, pinReset)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the <see cref="Pms5003"/> using /dev/ttyAMA0 serial port name
|
||||||
|
/// and no GPIO pins for SET and RESET.
|
||||||
|
/// </summary>
|
||||||
|
public Pms5003() : this("/dev/ttyAMA0", -1, -1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the data from the sensor.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns cref="Pms5003Data">The data.</returns>
|
||||||
|
/// <exception cref="ReadFailedException">Thrown when the read fails.</exception>
|
||||||
|
public Pms5003Data ReadData()
|
||||||
|
{
|
||||||
|
var currentTry = 0;
|
||||||
|
const int maxRetries = 5;
|
||||||
|
|
||||||
|
while (currentTry < maxRetries)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var buffer = new byte[32];
|
||||||
|
_serialPort.Read(buffer, 0, 32);
|
||||||
|
return Pms5003Data.FromBytes(buffer);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger?.LogWarning(e.ToString());
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
currentTry += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ReadFailedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resets the PMS5003 Sensor.
|
||||||
|
/// </summary>
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
if (_gpioController == null || _pinReset < 0) return;
|
||||||
|
_gpioController.Write(_pinReset, PinValue.Low);
|
||||||
|
Thread.Sleep(200);
|
||||||
|
_gpioController.Write(_pinReset, PinValue.High);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enables Sleep Mode for the PMS5003 Sensor.
|
||||||
|
/// </summary>
|
||||||
|
public void Sleep()
|
||||||
|
{
|
||||||
|
if (_gpioController == null || _pinSet < 0) return;
|
||||||
|
_isSleeping = true;
|
||||||
|
_gpioController.Write(_pinSet, PinValue.Low);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disables Sleep Mode for the PMS5003 Sensor.
|
||||||
|
/// </summary>
|
||||||
|
public void WakeUp()
|
||||||
|
{
|
||||||
|
if (_gpioController == null || _pinSet < 0) return;
|
||||||
|
_isSleeping = false;
|
||||||
|
_gpioController.Write(_pinSet, PinValue.High);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if Sleep Mode is enabled.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if sensor is in Sleep Mode, false otherwise.</returns>
|
||||||
|
public bool IsSleeping()
|
||||||
|
{
|
||||||
|
return _isSleeping;
|
||||||
|
}
|
||||||
|
|
||||||
|
~Pms5003()
|
||||||
|
{
|
||||||
|
_serialPort.Close();
|
||||||
|
_gpioController?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
PMS5003/PMS5003.csproj
Normal file
13
PMS5003/PMS5003.csproj
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Iot.Device.Bindings" Version="1.4.0" />
|
||||||
|
<PackageReference Include="System.Device.Gpio" Version="1.4.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
15
PMS5003/Pms5003Constants.cs
Normal file
15
PMS5003/Pms5003Constants.cs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
namespace PMS5003
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The Pms5003Constants defines constants required by the PMS5003 communication protocol.
|
||||||
|
/// </summary>
|
||||||
|
public class Pms5003Constants
|
||||||
|
{
|
||||||
|
public static readonly int DefaultBaudRate = 9600;
|
||||||
|
public static readonly int StartByte1 = 0x42;
|
||||||
|
public static readonly int StartByte2 = 0x4d;
|
||||||
|
public static readonly int CommandReadInPassive = 0xe2;
|
||||||
|
public static readonly int CommandChangeMode = 0xe1;
|
||||||
|
public static readonly int CommandSleep = 0xe4;
|
||||||
|
}
|
||||||
|
}
|
102
PMS5003/Pms5003Data.cs
Normal file
102
PMS5003/Pms5003Data.cs
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
using System.Text;
|
||||||
|
using PMS5003.Exceptions;
|
||||||
|
|
||||||
|
namespace PMS5003
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Pms5003Measurement is an abstraction over the PMS5003 data.
|
||||||
|
/// </summary>
|
||||||
|
public class Pms5003Data
|
||||||
|
{
|
||||||
|
private readonly uint[] _data;
|
||||||
|
public uint Pm1Standard => _data[0];
|
||||||
|
public uint Pm2Dot5Standard => _data[1];
|
||||||
|
public uint Pm10Standard => _data[2];
|
||||||
|
public uint Pm1Atmospheric => _data[3];
|
||||||
|
public uint Pm2Dot5Atmospheric => _data[4];
|
||||||
|
public uint Pm10Atmospheric => _data[5];
|
||||||
|
public uint ParticlesDiameterBeyond0Dot3 => _data[6];
|
||||||
|
public uint ParticlesDiameterBeyond0Dot5 => _data[7];
|
||||||
|
public uint ParticlesDiameterBeyond1Dot0 => _data[8];
|
||||||
|
public uint ParticlesDiameterBeyond2Dot5 => _data[9];
|
||||||
|
public uint ParticlesDiameterBeyond5Dot0 => _data[10];
|
||||||
|
public uint ParticlesDiameterBeyond10Dot0 => _data[11];
|
||||||
|
private uint Reserved => _data[12];
|
||||||
|
private uint Checksum => _data[13];
|
||||||
|
|
||||||
|
private Pms5003Data()
|
||||||
|
{
|
||||||
|
_data = new uint[14];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Instantiates a new instance from the given byte buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buffer">The data buffer</param>
|
||||||
|
/// <returns>A new instance.</returns>
|
||||||
|
public static Pms5003Data FromBytes(byte[] buffer)
|
||||||
|
{
|
||||||
|
var pms5003Measurement = new Pms5003Data();
|
||||||
|
|
||||||
|
if (buffer.Length < 4)
|
||||||
|
{
|
||||||
|
throw new BufferUnderflowException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer[0] != Pms5003Constants.StartByte1 || buffer[1] != Pms5003Constants.StartByte2)
|
||||||
|
{
|
||||||
|
throw new InvalidStartByteException(buffer[0], buffer[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var frameLength = Utils.CombineBytes(buffer[2], buffer[3]);
|
||||||
|
if (frameLength > 0)
|
||||||
|
{
|
||||||
|
var currentDataPoint = 0;
|
||||||
|
for (var i = 4; i < frameLength + 4; i += 2)
|
||||||
|
{
|
||||||
|
pms5003Measurement._data[currentDataPoint] = Utils.CombineBytes(buffer[i], buffer[i + 1]);
|
||||||
|
currentDataPoint += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var checkSum = 0;
|
||||||
|
for (var i = 0; i < frameLength + 2; i++)
|
||||||
|
{
|
||||||
|
checkSum += buffer[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pms5003Measurement.Checksum != checkSum)
|
||||||
|
{
|
||||||
|
throw new ChecksumMismatchException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return pms5003Measurement;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a string representation of the Pms5003Data.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>String with all the fields and values.</returns>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
var buffer = new StringBuilder();
|
||||||
|
buffer.AppendLine("Pms5003Data[");
|
||||||
|
buffer.AppendLine($"Pm1Standard={Pm1Standard},");
|
||||||
|
buffer.AppendLine($"Pm2Dot5Standard={Pm2Dot5Standard},");
|
||||||
|
buffer.AppendLine($"Pm10Standard={Pm10Standard},");
|
||||||
|
buffer.AppendLine($"Pm1Atmospheric={Pm1Atmospheric},");
|
||||||
|
buffer.AppendLine($"Pm2Dot5Atmospheric={Pm2Dot5Atmospheric},");
|
||||||
|
buffer.AppendLine($"Pm10Atmospheric={Pm10Atmospheric},");
|
||||||
|
buffer.AppendLine($"ParticlesDiameterBeyond0Dot3={ParticlesDiameterBeyond0Dot3},");
|
||||||
|
buffer.AppendLine($"ParticlesDiameterBeyond0Dot5={ParticlesDiameterBeyond0Dot5},");
|
||||||
|
buffer.AppendLine($"ParticlesDiameterBeyond1Dot0={ParticlesDiameterBeyond1Dot0},");
|
||||||
|
buffer.AppendLine($"ParticlesDiameterBeyond2Dot5={ParticlesDiameterBeyond2Dot5},");
|
||||||
|
buffer.AppendLine($"ParticlesDiameterBeyond5Dot0={ParticlesDiameterBeyond5Dot0},");
|
||||||
|
buffer.AppendLine($"ParticlesDiameterBeyond10Dot0={ParticlesDiameterBeyond10Dot0},");
|
||||||
|
buffer.AppendLine($"Reserved={Reserved},");
|
||||||
|
buffer.AppendLine($"Checksum={Checksum}");
|
||||||
|
buffer.AppendLine("]");
|
||||||
|
return buffer.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
PMS5003/Utils.cs
Normal file
16
PMS5003/Utils.cs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
namespace PMS5003
|
||||||
|
{
|
||||||
|
public class Utils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Combines two bytes and returns the results ((High RSHIFT 8) | Low)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="high">The high byte.</param>
|
||||||
|
/// <param name="low">The low byte.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static uint CombineBytes(byte high, byte low)
|
||||||
|
{
|
||||||
|
return (uint)(low | (high << 8));;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
PMS5003/readme.md
Normal file
36
PMS5003/readme.md
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
C# Library for interfacing with the PMS5003 Particulate Matter Sensor.
|
||||||
|
|
||||||
|
For wiring the Sensor consult the [datasheet](https://www.aqmd.gov/docs/default-source/aq-spec/resources-page/plantower-pms5003-manual_v2-3.pdf).
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Application
|
||||||
|
{
|
||||||
|
class Example
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
var pms = new Pms5003();
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var data = pms.ReadData();
|
||||||
|
Console.WriteLine(data);
|
||||||
|
Thread.Sleep(2000);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
20
PMS5003Tests/PMS5003Tests.csproj
Normal file
20
PMS5003Tests/PMS5003Tests.csproj
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
||||||
|
<PackageReference Include="xunit" Version="2.4.0" />
|
||||||
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
|
||||||
|
<PackageReference Include="coverlet.collector" Version="1.2.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\PMS5003\PMS5003.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
135
PMS5003Tests/Pms5003DataUnitTest.cs
Normal file
135
PMS5003Tests/Pms5003DataUnitTest.cs
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
using System;
|
||||||
|
using PMS5003;
|
||||||
|
using PMS5003.Exceptions;
|
||||||
|
using Xunit;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
|
namespace PMS5003Tests
|
||||||
|
{
|
||||||
|
public class Pms5003DataUnitTest
|
||||||
|
{
|
||||||
|
private readonly ITestOutputHelper _testOutputHelper;
|
||||||
|
|
||||||
|
public Pms5003DataUnitTest(ITestOutputHelper testOutputHelper)
|
||||||
|
{
|
||||||
|
_testOutputHelper = testOutputHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Test_FromBytes_Ok()
|
||||||
|
{
|
||||||
|
var tests = new byte[][]
|
||||||
|
{
|
||||||
|
new byte[]
|
||||||
|
{
|
||||||
|
66, 77, 0, 28, 0, 8, 0, 13, 0, 15, 0, 8, 0, 13, 0, 15, 7, 17, 1, 249, 0, 82, 0, 6, 0, 2, 0, 2, 151,
|
||||||
|
0, 2, 248
|
||||||
|
},
|
||||||
|
new byte[]
|
||||||
|
{
|
||||||
|
66, 77, 0, 28, 0, 9, 0, 15, 0, 16, 0, 9, 0, 15, 0, 16, 6, 246, 2, 11, 0, 104, 0, 4, 0, 2, 0, 2, 151,
|
||||||
|
0,
|
||||||
|
3, 11
|
||||||
|
},
|
||||||
|
new byte[]
|
||||||
|
{
|
||||||
|
66, 77, 0, 28, 0, 7, 0, 12, 0, 13, 0, 7, 0, 12, 0, 13, 5, 226, 1, 152, 0, 66, 0, 12, 0, 2, 0, 2,
|
||||||
|
151, 0,
|
||||||
|
3, 84
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Assert that no exception is thrown
|
||||||
|
foreach (var subTest in tests)
|
||||||
|
{
|
||||||
|
Pms5003Data.FromBytes(subTest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Test_FromBytes_ChecksumMismatchException()
|
||||||
|
{
|
||||||
|
var tests = new byte[][]
|
||||||
|
{
|
||||||
|
new byte[]
|
||||||
|
{
|
||||||
|
66, 77, 0, 28, 0, 8, 0, 13, 0, 16, 0, 8, 0, 13, 0, 16, 6, 45, 1, 167, 0, 101, 0, 10, 0, 8, 0, 151,
|
||||||
|
0, 144, 255, 0
|
||||||
|
},
|
||||||
|
new byte[]
|
||||||
|
{
|
||||||
|
66, 77, 0, 28, 0, 7, 0, 11, 0, 13, 0, 7, 0, 11, 0, 13, 5, 193, 1, 155, 8, 1, 6, 0, 2, 0, 2, 151, 0,
|
||||||
|
3, 44, 0
|
||||||
|
},
|
||||||
|
new byte[]
|
||||||
|
{
|
||||||
|
66, 77, 0, 28, 0, 8, 0, 14, 0, 18, 0, 8, 0, 14, 0, 18, 6, 186, 1, 208, 0, 100, 0, 8, 0, 6, 0, 6,
|
||||||
|
151, 3, 155, 0
|
||||||
|
},
|
||||||
|
new byte[]
|
||||||
|
{
|
||||||
|
66, 77, 0, 28, 0, 7, 0, 10, 0, 10, 232, 0, 10, 0, 33, 208, 10, 165, 0, 70, 0, 4, 0, 0, 0, 0, 151, 0,
|
||||||
|
2, 161, 0, 0,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var subTest in tests)
|
||||||
|
{
|
||||||
|
Assert.Throws<ChecksumMismatchException>(() => Pms5003Data.FromBytes(subTest));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Test_FromBytes_InvalidStartByteException()
|
||||||
|
{
|
||||||
|
var tests = new byte[][]
|
||||||
|
{
|
||||||
|
new byte[]
|
||||||
|
{
|
||||||
|
66, 00, 0, 28, 0, 8, 0, 13, 0, 16, 0, 8, 0, 13, 0, 16, 6, 45, 1, 167, 0, 101, 0, 10, 0, 8, 0, 151,
|
||||||
|
0, 144, 255, 0
|
||||||
|
},
|
||||||
|
new byte[]
|
||||||
|
{
|
||||||
|
00, 77, 0, 28, 0, 7, 0, 11, 0, 13, 0, 7, 0, 11, 0, 13, 5, 193, 1, 155, 8, 1, 6, 0, 2, 0, 2, 151, 0,
|
||||||
|
3, 44, 0
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var subTest in tests)
|
||||||
|
{
|
||||||
|
Assert.Throws<InvalidStartByteException>(() => Pms5003Data.FromBytes(subTest));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Test_ToString()
|
||||||
|
{
|
||||||
|
var pmsData = Pms5003Data.FromBytes(new byte[]
|
||||||
|
{
|
||||||
|
66, 77, 0, 28, 0, 7, 0, 12, 0, 13, 0, 7, 0, 12, 0, 13, 5, 226, 1, 152, 0, 66, 0, 12, 0, 2, 0, 2,
|
||||||
|
151, 0,
|
||||||
|
3, 84
|
||||||
|
});
|
||||||
|
|
||||||
|
_testOutputHelper.WriteLine(pmsData.ToString());
|
||||||
|
Assert.Equal(@"Pms5003Data[
|
||||||
|
Pm1Standard=7,
|
||||||
|
Pm2Dot5Standard=12,
|
||||||
|
Pm10Standard=13,
|
||||||
|
Pm1Atmospheric=7,
|
||||||
|
Pm2Dot5Atmospheric=12,
|
||||||
|
Pm10Atmospheric=13,
|
||||||
|
ParticlesDiameterBeyond0Dot3=1506,
|
||||||
|
ParticlesDiameterBeyond0Dot5=408,
|
||||||
|
ParticlesDiameterBeyond1Dot0=66,
|
||||||
|
ParticlesDiameterBeyond2Dot5=12,
|
||||||
|
ParticlesDiameterBeyond5Dot0=2,
|
||||||
|
ParticlesDiameterBeyond10Dot0=2,
|
||||||
|
Reserved=38656,
|
||||||
|
Checksum=852
|
||||||
|
]
|
||||||
|
", pmsData.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue