-
Notifications
You must be signed in to change notification settings - Fork 68
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #618 from WildernessLabs/feature/next-pm
tera netxPM particulate sensor
- Loading branch information
Showing
15 changed files
with
666 additions
and
6 deletions.
There are no files selected for viewing
Binary file added
BIN
+820 KB
....Foundation.Peripherals/Sensors.Environmental.NextPm/Datasheet/NextPM-User-Guide_v3.5.pdf
Binary file not shown.
Binary file added
BIN
+457 KB
...eadow.Foundation.Peripherals/Sensors.Environmental.NextPm/Datasheet/NextPM_RS485_v2.1.pdf
Binary file not shown.
54 changes: 54 additions & 0 deletions
54
Source/Meadow.Foundation.Peripherals/Sensors.Environmental.NextPm/Driver/CommandManager.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
using System; | ||
using System.Linq; | ||
|
||
namespace Meadow.Foundation.Sensors.Environmental | ||
{ | ||
internal static class CommandManager | ||
{ | ||
public static (int commandLength, int expectedResponseLength) GenerateCommand(NextPm.CommandByte command, byte[] buffer, params byte[] payload) | ||
{ | ||
buffer[0] = NextPm.SensorAddress; | ||
buffer[1] = (byte)command; | ||
|
||
var payloadLength = 2; | ||
|
||
if (payload != null && payload.Length > 0) | ||
{ | ||
Array.Copy(payload, 0, buffer, 2, payload.Length); | ||
payloadLength += payload.Length; | ||
} | ||
|
||
buffer[payloadLength] = CalculateChecksum(buffer, 0, payloadLength); | ||
|
||
switch (command) | ||
{ | ||
case NextPm.CommandByte.ReadFirmware: | ||
return (3, 6); | ||
case NextPm.CommandByte.ReadTempAndHumidity: | ||
return (3, 8); | ||
case NextPm.CommandByte.ReadWriteFanSpeed: | ||
return (payloadLength + 1, 6); | ||
case NextPm.CommandByte.SetPowerMode: | ||
return (payloadLength + 1, 4); | ||
case NextPm.CommandByte.ReadSensorState: | ||
return (3, 4); | ||
case NextPm.CommandByte.Read10sConcentrations: | ||
case NextPm.CommandByte.Read60sConcentrations: | ||
case NextPm.CommandByte.Read900sConcentrations: | ||
return (3, 16); | ||
default: throw new NotImplementedException(); | ||
} | ||
} | ||
|
||
public static byte CalculateChecksum(byte[] data, int start, int length) | ||
{ | ||
return (byte)(0x100 - (data.Skip(start).Take(length).Sum(d => d) % 256)); | ||
} | ||
|
||
public static bool ValidateChecksum(byte[] data, int start, int length) | ||
{ | ||
var expected = CalculateChecksum(data, start, length - 1); | ||
return expected == data[start + length - 1]; | ||
} | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
Source/Meadow.Foundation.Peripherals/Sensors.Environmental.NextPm/Driver/NextPm.Constants.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace Meadow.Foundation.Sensors.Environmental | ||
{ | ||
public partial class NextPm | ||
{ | ||
internal const byte SensorAddress = 0x81; // fixed address, per the data sheet | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
Source/Meadow.Foundation.Peripherals/Sensors.Environmental.NextPm/Driver/NextPm.Enums.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
using System; | ||
|
||
namespace Meadow.Foundation.Sensors.Environmental | ||
{ | ||
public partial class NextPm | ||
{ | ||
internal enum CommandByte : byte | ||
{ | ||
Read10sConcentrations = 0x11, | ||
Read60sConcentrations = 0x12, | ||
Read900sConcentrations = 0x13, | ||
ReadTempAndHumidity = 0x14, | ||
SetPowerMode = 0x15, | ||
ReadSensorState = 0x16, | ||
ReadFirmware = 0x17, | ||
ReadWriteFanSpeed = 0x21, | ||
ReadWriteModbusAddress = 0x22 | ||
} | ||
|
||
[Flags] | ||
internal enum SensorStatus : byte | ||
{ | ||
Sleep = 1 << 0, | ||
Degraded = 1 << 1, | ||
NotReady = 1 << 2, | ||
HeaterError = 1 << 3, | ||
TempHumidyError = 1 << 4, | ||
FanError = 1 << 5, | ||
MemoryError = 1 << 6, | ||
LaserError = 1 << 7, | ||
} | ||
} | ||
} |
83 changes: 83 additions & 0 deletions
83
Source/Meadow.Foundation.Peripherals/Sensors.Environmental.NextPm/Driver/NextPm.Serial.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
using Meadow.Hardware; | ||
using System; | ||
using System.Threading.Tasks; | ||
|
||
namespace Meadow.Foundation.Sensors.Environmental | ||
{ | ||
/// <summary> | ||
/// Represents a TERA Sensor NextPM particulate matter sensor | ||
/// </summary> | ||
public partial class NextPm | ||
{ | ||
private ISerialPort _port; | ||
// dev note: these common buffers are used to minimize heap allocations during serial communications with the sensor | ||
private byte[] _readBuffer = new byte[16]; | ||
private byte[] _writeBuffer = new byte[16]; | ||
|
||
private ISerialPort InitializePort(ISerialPort port) | ||
{ | ||
if (port.IsOpen) | ||
{ | ||
port.Close(); | ||
} | ||
|
||
port.BaudRate = 115200; | ||
port.DataBits = 8; | ||
port.Parity = Parity.Even; | ||
port.StopBits = StopBits.One; | ||
|
||
return port; | ||
} | ||
|
||
private async Task SendCommand(CommandByte command, params byte[] payload) | ||
{ | ||
var parameters = CommandManager.GenerateCommand(command, _writeBuffer, payload); | ||
|
||
if (!_port.IsOpen) | ||
{ | ||
_port.Open(); | ||
} | ||
|
||
_port.Write(_writeBuffer, 0, parameters.commandLength); | ||
|
||
var read = 0; | ||
var expected = parameters.expectedResponseLength; | ||
var timeoutMs = 3000; | ||
var readDelay = 500; | ||
|
||
await Task.Delay(100); // initial device response delay | ||
|
||
Array.Clear(_readBuffer, 0, _readBuffer.Length); | ||
|
||
do | ||
{ | ||
read += _port.Read(_readBuffer, read, _readBuffer.Length - read); | ||
if (read >= expected) break; | ||
|
||
if (read > 2 && _readBuffer[1] != (byte)command && _readBuffer[1] == 0x16) | ||
{ | ||
var status = (SensorStatus)_readBuffer[2]; | ||
// typically a "fail" due to the device being in sleep state | ||
throw new TeraException($"Device status: {status}"); | ||
} | ||
|
||
await Task.Delay(readDelay); | ||
timeoutMs -= readDelay; | ||
} while (timeoutMs > 0); | ||
|
||
if (read < expected) | ||
{ | ||
throw new TeraException("Serial timeout"); | ||
} | ||
else | ||
{ | ||
var checksumIsValid = CommandManager.ValidateChecksum(_readBuffer, 0, parameters.expectedResponseLength); | ||
|
||
if (!checksumIsValid) | ||
{ | ||
throw new TeraException("Invalid response checksum"); | ||
} | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.