diff --git a/src/devices/Shtc3/README.md b/src/devices/Shtc3/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ec1df011d70eff6a52db871a2ed4375da392f3de --- /dev/null +++ b/src/devices/Shtc3/README.md @@ -0,0 +1,28 @@ +# 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 diff --git a/src/devices/Shtc3/Register.cs b/src/devices/Shtc3/Register.cs new file mode 100644 index 0000000000000000000000000000000000000000..b02a9e95d44f0af28fe521bf85992181626e7552 --- /dev/null +++ b/src/devices/Shtc3/Register.cs @@ -0,0 +1,57 @@ +// 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 +{ + /// + /// Shtc3 Register + /// + internal enum Register : ushort + { + SHTC3_ID = 0xEFC8, + SHTC3_RESET = 0x805D, + SHTC3_SLEEP = 0xB098, + SHTC3_WAKEUP = 0x3517, + + /// + /// Temperature and Humidity with Clock stretching disable in normal power mode + /// + SHTC3_MEAS_T_RH_POLLING_NPM = 0x7866, + + /// + /// Temperature and Humidity with Clock Stretching enable and normal power mode + /// + SHTC3_MEAS_T_RH_CLOCKSTR_NPM = 0x7CA2, + + /// + /// Temperature and Humidity with Clock stretching disable in low power mode + /// + SHTC3_MEAS_T_RH_POLLING_LPM = 0x609C, + + /// + /// Temperature and Humidity with Clock stretching enable in low power mode + /// + SHTC3_MEAS_T_RH_CLOCKSTR_LPM = 0x6458, + + /// + /// Humidity and Temperature with Clock stretching disable in normal power mode + /// + SHTC3_MEAS_RH_T_POLLING_NPM = 0x58E0, + + /// + /// Humidity and Temperature with Clock stretching enable in normal power mode + /// + SHTC3_MEAS_RH_T_CLOCKSTR_NPM = 0x5C24, + + /// + /// Humidity and Temperature with Clock stretching disable in low power mode + /// + SHTC3_MEAS_RH_T_POLLING_LPM = 0x401A, + + /// + /// Humidity and Temperature with Clock stretching enable in low power mode + /// + SHTC3_MEAS_RH_T_CLOCKSTR_LPM = 0x44DE, + } +} diff --git a/src/devices/Shtc3/Shtc3.cs b/src/devices/Shtc3/Shtc3.cs new file mode 100644 index 0000000000000000000000000000000000000000..de89c04730abc3878097fe56d1330bb10e5e79d5 --- /dev/null +++ b/src/devices/Shtc3/Shtc3.cs @@ -0,0 +1,285 @@ +// 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 +{ + /// + /// Temperature and humidity sensor Shtc3 + /// + public class Shtc3 : IDisposable + { + // CRC const + private const byte CRC_POLYNOMIAL = 0x31; + private const byte CRC_INIT = 0xFF; + + /// + /// Default I2C address + /// + public const int DefaultI2cAddress = 0x70; + + private I2cDevice _i2cDevice; + private int? _id = null; + + /// + /// Initializes a new instance of the class. + /// + /// The I2C device used for communication. + public Shtc3(I2cDevice i2cDevice) + { + if (i2cDevice == null) + { + throw new ArgumentNullException(nameof(i2cDevice)); + } + + _i2cDevice = i2cDevice; + + Wakeup(); + _status = Status.Idle; + + Reset(); + } + + private Status _status; + + /// + /// Set Shtc3 state + /// + public Status Status + { + get + { + return _status; + } + } + + /// + /// Try read Temperature and Humidity + /// + /// Temperature returned by sensor + /// Humidity return by sensor + /// "true" measured in low power mode, "false"(default) measured in normal power mode + /// "true" allow clock stretching, "false" (default) without clock stretching + /// True if operation was successful + 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 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; + } + } + } + + /// + /// SHTC3 Sleep + /// + public void Sleep() + { + if (Status == Status.Sleep) + { + return; + } + + Write(Register.SHTC3_SLEEP); + } + + /// + /// SHTC3 Wakeup + /// + private void Wakeup() + { + Write(Register.SHTC3_WAKEUP); + } + + /// + /// SHTC3 Soft Reset + /// + public void Reset() + { + if (Status == Status.Sleep) + { + Wakeup(); + } + + Write(Register.SHTC3_RESET); + } + + /// + /// Sensor Id + /// + public int? Id + { + get + { + return _id = _id ?? ReadId(); + } + } + + /// + /// Read Id + /// + private int? ReadId() + { + if (Status == Status.Sleep) + { + Wakeup(); + } + + Write(Register.SHTC3_ID); + + Span 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; + } + + /// + /// 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 + /// + /// Id to test + /// + private static bool ValidShtc3Id(int id) + { + return (id & 0b_0000_1000_0011_1111) == 0b_0000_1000_0000_0111; + } + + /// + /// 8-bit CRC Checksum Calculation + /// + /// Raw Data + /// Raw CRC8 + /// Checksum is true or false + private static bool CheckCrc8(ReadOnlySpan 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 writeBuff = stackalloc byte[2]; + BinaryPrimitives.WriteUInt16BigEndian(writeBuff, (ushort)register); + + _i2cDevice.Write(writeBuff); + + // wait SCL free + DelayHelper.DelayMilliseconds(20, false); + } + + /// + /// Cleanup + /// + public void Dispose() + { + _i2cDevice?.Dispose(); + _i2cDevice = null; + } + } +} diff --git a/src/devices/Shtc3/Shtc3.csproj b/src/devices/Shtc3/Shtc3.csproj new file mode 100644 index 0000000000000000000000000000000000000000..153981be0ef53fbe6c061bb758458223d9c4f841 --- /dev/null +++ b/src/devices/Shtc3/Shtc3.csproj @@ -0,0 +1,16 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + diff --git a/src/devices/Shtc3/Shtc3.sln b/src/devices/Shtc3/Shtc3.sln new file mode 100644 index 0000000000000000000000000000000000000000..d522280748b9e7a7308cee4c6061ded22da695ff --- /dev/null +++ b/src/devices/Shtc3/Shtc3.sln @@ -0,0 +1,48 @@ + +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 diff --git a/src/devices/Shtc3/Status.cs b/src/devices/Shtc3/Status.cs new file mode 100644 index 0000000000000000000000000000000000000000..79415e8f696e5128c18ec4a09911581e17bcb25e --- /dev/null +++ b/src/devices/Shtc3/Status.cs @@ -0,0 +1,22 @@ +// 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 +{ + /// + /// Shtc3 Status + /// + internal enum Status + { + /// + /// Sensor ready to use + /// + Idle, + + /// + /// Sensor in sleep mode + /// + Sleep + } +} diff --git a/src/devices/Shtc3/category.txt b/src/devices/Shtc3/category.txt new file mode 100644 index 0000000000000000000000000000000000000000..28100afeb99f6b4d96865a9034a64b19d2e6f984 --- /dev/null +++ b/src/devices/Shtc3/category.txt @@ -0,0 +1,2 @@ +thermometer +hygrometer diff --git a/src/devices/Shtc3/samples/Program.cs b/src/devices/Shtc3/samples/Program.cs new file mode 100644 index 0000000000000000000000000000000000000000..8c54a609eeaec87faf14cc987d326b37502af449 --- /dev/null +++ b/src/devices/Shtc3/samples/Program.cs @@ -0,0 +1,58 @@ +// 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"); + } + + } +} diff --git a/src/devices/Shtc3/samples/README.md b/src/devices/Shtc3/samples/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c49feee43e985eb8d6714e6a0b9f347b0049da48 --- /dev/null +++ b/src/devices/Shtc3/samples/README.md @@ -0,0 +1,44 @@ +# 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) diff --git a/src/devices/Shtc3/samples/RunningResult.JPG b/src/devices/Shtc3/samples/RunningResult.JPG new file mode 100644 index 0000000000000000000000000000000000000000..baa4ecabe820208b8f22205c19e710eb344d6d3c Binary files /dev/null and b/src/devices/Shtc3/samples/RunningResult.JPG differ diff --git a/src/devices/Shtc3/samples/Shtc3.Samples.csproj b/src/devices/Shtc3/samples/Shtc3.Samples.csproj new file mode 100644 index 0000000000000000000000000000000000000000..297f665d6f225d1466265e2c8400708794e21b22 --- /dev/null +++ b/src/devices/Shtc3/samples/Shtc3.Samples.csproj @@ -0,0 +1,19 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + + +