未验证 提交 d24b3b20 编写于 作者: F famblard 提交者: GitHub

Added SHTC3 binding (#1067)

* Added SHTC3 binding

* Changes from the code review

* change .csproj

* Changes from the code review

* add use Ratio

* fix readme and use ReadUInt16BigEndian

* remove bad use of ReadUInt16BigEndian the temperature can be negative not humidity

* Update src/devices/Shtc3/Shtc3.cs
Co-authored-by: NKrzysztof Wicher <mordotymoja@gmail.com>

* Update src/devices/Shtc3/Shtc3.cs
Co-authored-by: NKrzysztof Wicher <mordotymoja@gmail.com>

* Update src/devices/Shtc3/Shtc3.cs
Co-authored-by: NKrzysztof Wicher <mordotymoja@gmail.com>

* Update src/devices/Shtc3/Shtc3.cs
Co-authored-by: NKrzysztof Wicher <mordotymoja@gmail.com>

* Update src/devices/Shtc3/Shtc3.cs
Co-authored-by: NKrzysztof Wicher <mordotymoja@gmail.com>

* update after review

* change status to internal
Co-authored-by: NKrzysztof Wicher <mordotymoja@gmail.com>
上级 b76bfd49
# SHTC3 - Temperature & Humidity Sensor
SHTC3 is a digital humidity and temperature sensor designed especially for battery-driven high-volume consumer electronics application.
To reduce power cosumption this project use capability of sensor to allow measurement in low power mode and active sleep mode.
## Usage
```C#
I2cConnectionSettings settings = new I2cConnectionSettings(1, Iot.Device.Shtc3.Shtc3.DefaultI2cAddress);
I2cDevice device = I2cDevice.Create(settings);
using (Shtc3 sensor = new Shtc3(device))
{
if (sensor.TryGetTemperatureAndHumidity(out var temperature, out var relativeHumidity))
{
// temperature (℃)
Console.WriteLine($"Temperature: {temperature.DegreesCelsius:0.#}\u00B0C");
// humidity (%)
Console.WriteLine($"Humidity: {relativeHumidity.Percent:0.#}%");
}
// Make sensor in sleep mode
sensor.Sleep();
}
```
## References
https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/2_Humidity_Sensors/Datasheets/Sensirion_Humidity_Sensors_SHTC3_Datasheet.pdf
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Iot.Device.Shtc3
{
/// <summary>
/// Shtc3 Register
/// </summary>
internal enum Register : ushort
{
SHTC3_ID = 0xEFC8,
SHTC3_RESET = 0x805D,
SHTC3_SLEEP = 0xB098,
SHTC3_WAKEUP = 0x3517,
/// <summary>
/// Temperature and Humidity with Clock stretching disable in normal power mode
/// </summary>
SHTC3_MEAS_T_RH_POLLING_NPM = 0x7866,
/// <summary>
/// Temperature and Humidity with Clock Stretching enable and normal power mode
/// </summary>
SHTC3_MEAS_T_RH_CLOCKSTR_NPM = 0x7CA2,
/// <summary>
/// Temperature and Humidity with Clock stretching disable in low power mode
/// </summary>
SHTC3_MEAS_T_RH_POLLING_LPM = 0x609C,
/// <summary>
/// Temperature and Humidity with Clock stretching enable in low power mode
/// </summary>
SHTC3_MEAS_T_RH_CLOCKSTR_LPM = 0x6458,
/// <summary>
/// Humidity and Temperature with Clock stretching disable in normal power mode
/// </summary>
SHTC3_MEAS_RH_T_POLLING_NPM = 0x58E0,
/// <summary>
/// Humidity and Temperature with Clock stretching enable in normal power mode
/// </summary>
SHTC3_MEAS_RH_T_CLOCKSTR_NPM = 0x5C24,
/// <summary>
/// Humidity and Temperature with Clock stretching disable in low power mode
/// </summary>
SHTC3_MEAS_RH_T_POLLING_LPM = 0x401A,
/// <summary>
/// Humidity and Temperature with Clock stretching enable in low power mode
/// </summary>
SHTC3_MEAS_RH_T_CLOCKSTR_LPM = 0x44DE,
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Buffers.Binary;
using System.Device;
using System.Device.I2c;
using System.Threading;
using UnitsNet;
namespace Iot.Device.Shtc3
{
/// <summary>
/// Temperature and humidity sensor Shtc3
/// </summary>
public class Shtc3 : IDisposable
{
// CRC const
private const byte CRC_POLYNOMIAL = 0x31;
private const byte CRC_INIT = 0xFF;
/// <summary>
/// Default I2C address
/// </summary>
public const int DefaultI2cAddress = 0x70;
private I2cDevice _i2cDevice;
private int? _id = null;
/// <summary>
/// Initializes a new instance of the <see cref="Shtc3"/> class.
/// </summary>
/// <param name="i2cDevice">The I2C device used for communication.</param>
public Shtc3(I2cDevice i2cDevice)
{
if (i2cDevice == null)
{
throw new ArgumentNullException(nameof(i2cDevice));
}
_i2cDevice = i2cDevice;
Wakeup();
_status = Status.Idle;
Reset();
}
private Status _status;
/// <summary>
/// Set Shtc3 state
/// </summary>
public Status Status
{
get
{
return _status;
}
}
/// <summary>
/// Try read Temperature and Humidity
/// </summary>
/// <param name="temperature">Temperature returned by sensor</param>
/// <param name="relativeHumidity">Humidity return by sensor</param>
/// <param name="lowPower">"true" measured in low power mode, "false"(default) measured in normal power mode</param>
/// <param name="clockStretching">"true" allow clock stretching, "false" (default) without clock stretching</param>
/// <returns>True if operation was successful</returns>
public bool TryGetTemperatureAndHumidity(out Temperature temperature, out Ratio relativeHumidity, bool lowPower = false, bool clockStretching = false)
{
if (Status == Status.Sleep)
{
Wakeup();
}
Register cmd = GetMeasurementCmd(lowPower, clockStretching);
if (!TryReadSensorData(cmd, out var st, out var srh))
{
temperature = default(Temperature);
relativeHumidity = default(Ratio);
return false;
}
// Details in the Datasheet P9
temperature = Temperature.FromDegreesCelsius(Math.Round(st * 175 / 65536.0 - 45, 1));
relativeHumidity = Ratio.FromDecimalFractions(srh / 65536.0);
return true;
}
private bool TryReadSensorData(Register cmd, out double temperature, out double humidity)
{
Write(cmd);
Span<byte> readBuff = stackalloc byte[6];
_i2cDevice.Read(readBuff);
// Details in the Datasheet P7
int st = BinaryPrimitives.ReadInt16BigEndian(readBuff.Slice(0, 2)); // Temp
int srh = BinaryPrimitives.ReadUInt16BigEndian(readBuff.Slice(3, 2)); // Humi
// check 8-bit crc
bool tCrc = CheckCrc8(readBuff.Slice(0, 2), readBuff[2]);
bool rhCrc = CheckCrc8(readBuff.Slice(3, 2), readBuff[5]);
if (!tCrc || !rhCrc)
{
temperature = double.NaN;
humidity = double.NaN;
return false;
}
temperature = st;
humidity = srh;
return true;
}
private static Register GetMeasurementCmd(bool lowPower, bool clockStretching)
{
if (lowPower)
{
if (clockStretching)
{
return Register.SHTC3_MEAS_T_RH_CLOCKSTR_LPM;
}
else
{
return Register.SHTC3_MEAS_T_RH_POLLING_LPM;
}
}
else
{
if (clockStretching)
{
return Register.SHTC3_MEAS_T_RH_CLOCKSTR_NPM;
}
else
{
return Register.SHTC3_MEAS_T_RH_POLLING_NPM;
}
}
}
/// <summary>
/// SHTC3 Sleep
/// </summary>
public void Sleep()
{
if (Status == Status.Sleep)
{
return;
}
Write(Register.SHTC3_SLEEP);
}
/// <summary>
/// SHTC3 Wakeup
/// </summary>
private void Wakeup()
{
Write(Register.SHTC3_WAKEUP);
}
/// <summary>
/// SHTC3 Soft Reset
/// </summary>
public void Reset()
{
if (Status == Status.Sleep)
{
Wakeup();
}
Write(Register.SHTC3_RESET);
}
/// <summary>
/// Sensor Id
/// </summary>
public int? Id
{
get
{
return _id = _id ?? ReadId();
}
}
/// <summary>
/// Read Id
/// </summary>
private int? ReadId()
{
if (Status == Status.Sleep)
{
Wakeup();
}
Write(Register.SHTC3_ID);
Span<byte> readBuff = stackalloc byte[3];
_i2cDevice.Read(readBuff);
// check 8-bit crc
if (!CheckCrc8(readBuff.Slice(0, 2), readBuff[2]))
{
return null;
}
int id = BinaryPrimitives.ReadInt16BigEndian(readBuff.Slice(0, 2));
// check the result match to the SHTC3 product code
if (!ValidShtc3Id(id))
{
return null;
}
return id;
}
/// <summary>
/// Check the match to the SHTC3 product code
/// Table 15 while bits 11 and 5:0 contain the SHTC3 specific product code 0b_0000_1000_0000_0111
/// </summary>
/// <param name="id">Id to test</param>
/// <returns></returns>
private static bool ValidShtc3Id(int id)
{
return (id & 0b_0000_1000_0011_1111) == 0b_0000_1000_0000_0111;
}
/// <summary>
/// 8-bit CRC Checksum Calculation
/// </summary>
/// <param name="data">Raw Data</param>
/// <param name="crc8">Raw CRC8</param>
/// <returns>Checksum is true or false</returns>
private static bool CheckCrc8(ReadOnlySpan<byte> data, byte crc8)
{
// Details in the Datasheet table 16 P9
byte crc = CRC_INIT;
for (int i = 0; i < 2; i++)
{
crc ^= data[i];
for (int j = 8; j > 0; j--)
{
if ((crc & 0x80) != 0)
{
crc = (byte)((crc << 1) ^ CRC_POLYNOMIAL);
}
else
{
crc = (byte)(crc << 1);
}
}
}
return crc == crc8;
}
private void Write(Register register)
{
Span<byte> writeBuff = stackalloc byte[2];
BinaryPrimitives.WriteUInt16BigEndian(writeBuff, (ushort)register);
_i2cDevice.Write(writeBuff);
// wait SCL free
DelayHelper.DelayMilliseconds(20, false);
}
/// <summary>
/// Cleanup
/// </summary>
public void Dispose()
{
_i2cDevice?.Dispose();
_i2cDevice = null;
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<!--Disabling default items so samples source won't get build by the main library-->
<EnableDefaultItems>false</EnableDefaultItems>
</PropertyGroup>
<ItemGroup>
<Compile Include="*.cs" />
<Compile Include="..\Common\System\Device\DelayHelper.cs" Link="DelayHelper.cs" />
<None Include="README.md" />
<ProjectReference Include="$(MainLibraryPath)System.Device.Gpio.csproj" />
</ItemGroup>
</Project>

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30002.166
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shtc3", "Shtc3.csproj", "{20BAE1B8-EC5B-4E2E-9876-8DCC4C18532F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{71FB7D89-939F-42FF-914D-60EE6B826694}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shtc3.Samples", "samples\Shtc3.Samples.csproj", "{299AE1A7-B530-4DD3-9384-C7E0A3960041}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Device.Gpio", "..\..\System.Device.Gpio\System.Device.Gpio.csproj", "{A81F4110-9D79-426B-BFD5-77C1CFADD15A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommonHelpers", "..\Common\CommonHelpers.csproj", "{9089BDBB-35CE-4CD6-BD79-484177C5B153}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{20BAE1B8-EC5B-4E2E-9876-8DCC4C18532F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{20BAE1B8-EC5B-4E2E-9876-8DCC4C18532F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{20BAE1B8-EC5B-4E2E-9876-8DCC4C18532F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{20BAE1B8-EC5B-4E2E-9876-8DCC4C18532F}.Release|Any CPU.Build.0 = Release|Any CPU
{299AE1A7-B530-4DD3-9384-C7E0A3960041}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{299AE1A7-B530-4DD3-9384-C7E0A3960041}.Debug|Any CPU.Build.0 = Debug|Any CPU
{299AE1A7-B530-4DD3-9384-C7E0A3960041}.Release|Any CPU.ActiveCfg = Release|Any CPU
{299AE1A7-B530-4DD3-9384-C7E0A3960041}.Release|Any CPU.Build.0 = Release|Any CPU
{A81F4110-9D79-426B-BFD5-77C1CFADD15A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A81F4110-9D79-426B-BFD5-77C1CFADD15A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A81F4110-9D79-426B-BFD5-77C1CFADD15A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A81F4110-9D79-426B-BFD5-77C1CFADD15A}.Release|Any CPU.Build.0 = Release|Any CPU
{9089BDBB-35CE-4CD6-BD79-484177C5B153}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9089BDBB-35CE-4CD6-BD79-484177C5B153}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9089BDBB-35CE-4CD6-BD79-484177C5B153}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9089BDBB-35CE-4CD6-BD79-484177C5B153}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{299AE1A7-B530-4DD3-9384-C7E0A3960041} = {71FB7D89-939F-42FF-914D-60EE6B826694}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A3AA64A6-B047-418A-89EE-F0B0DCEA4766}
EndGlobalSection
EndGlobal
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Iot.Device.Shtc3
{
/// <summary>
/// Shtc3 Status
/// </summary>
internal enum Status
{
/// <summary>
/// Sensor ready to use
/// </summary>
Idle,
/// <summary>
/// Sensor in sleep mode
/// </summary>
Sleep
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Device.I2c;
using System.Threading;
using Iot.Device.Common;
using UnitsNet;
namespace Iot.Device.Shtc3.Samples
{
internal class Program
{
private static void Main(string[] args)
{
I2cConnectionSettings settings = new I2cConnectionSettings(1, Iot.Device.Shtc3.Shtc3.DefaultI2cAddress);
I2cDevice device = I2cDevice.Create(settings);
using (Shtc3 sensor = new Shtc3(device))
{
Console.WriteLine($"Sensor Id: {sensor.Id}");
while (true)
{
// Try sensor measurement in normal power mode
if (sensor.TryGetTemperatureAndHumidity(out var temperature, out var relativeHumidity))
{
Console.WriteLine($"====================In normal power mode===========================");
ConsoleWriteInfo(temperature, relativeHumidity);
}
// Try sensor measurement in low power mode
if (sensor.TryGetTemperatureAndHumidity(out temperature, out relativeHumidity, lowPower: true))
{
Console.WriteLine($"====================In low power mode===========================");
ConsoleWriteInfo(temperature, relativeHumidity);
}
// Set sensor in sleep mode
sensor.Sleep();
Console.WriteLine();
Thread.Sleep(1000);
}
}
}
private static void ConsoleWriteInfo(Temperature temperature, Ratio relativeHumidity)
{
Console.WriteLine($"Temperature: {temperature.DegreesCelsius:0.#}\u00B0C");
Console.WriteLine($"Humidity: {relativeHumidity.Percent:0.#}%");
// WeatherHelper supports more calculations, such as saturated vapor pressure, actual vapor pressure and absolute humidity.
Console.WriteLine($"Heat index: {WeatherHelper.CalculateHeatIndex(temperature, relativeHumidity).DegreesCelsius:0.#}\u00B0C");
Console.WriteLine($"Dew point: {WeatherHelper.CalculateDewPoint(temperature, relativeHumidity).DegreesCelsius:0.#}\u00B0C");
}
}
}
# SHTC3 - Samples
## Hardware Required
* SHTC3
* Male/Female Jumper Wires
## Circuit
* SCL - SCL
* SDA - SDA
* VCC - 3.3V
* GND - GND
## Code
```C#
I2cConnectionSettings settings = new I2cConnectionSettings(1, Shtc3.DefaultI2cAddress);
I2cDevice device = I2cDevice.Create(settings);
using (Shtc3 sensor = new Shtc3(device))
{
Console.WriteLine($"Sensor Id: {sensor.Id}");
while (true)
{
if (sensor.TryGetTemperatureAndHumidity(out var temperature, out var relativeHumidity))
{
Console.WriteLine($"Temperature: {temperature.DegreesCelsius:0.#}\u00B0C");
Console.WriteLine($"Relative humidity: {relativeHumidity.Percent:0.#}%");
// WeatherHelper supports more calculations, such as saturated vapor pressure, actual vapor pressure and absolute humidity.
Console.WriteLine($"Heat index: {WeatherHelper.CalculateHeatIndex(temperature, relativeHumidity).DegreesCelsius:0.#}\u00B0C");
Console.WriteLine($"Dew point: {WeatherHelper.CalculateDewPoint(temperature, relativeHumidity).DegreesCelsius:0.#}\u00B0C");
Console.WriteLine();
}
sensor.Sleep();
Thread.Sleep(1000);
}
}
```
## Result
![](RunningResult.JPG)
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<None Remove="README.md" />
<None Remove="RunningResult.jpg" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\System.Device.Gpio\System.Device.Gpio.csproj" />
<ProjectReference Include="..\..\Common\CommonHelpers.csproj" />
<ProjectReference Include="..\Shtc3.csproj" />
</ItemGroup>
</Project>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册