Skip to content

Commit f4c5fce

Browse files
torbaczEllerbach
andauthored
Sdc4x (#1124)
Co-authored-by: Laurent Ellerbach <[email protected]>
1 parent f17a9f3 commit f4c5fce

17 files changed

+898
-0
lines changed

Diff for: devices/Scd4x/Properties/AssemblyInfo.cs

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
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.Reflection;
5+
using System.Runtime.CompilerServices;
6+
using System.Runtime.InteropServices;
7+
8+
[assembly: AssemblyTitle("Iot.Device.Scd4x")]
9+
[assembly: AssemblyCompany("nanoFramework Contributors")]
10+
[assembly: AssemblyCopyright("Copyright(c).NET Foundation and Contributors")]
11+
12+
[assembly: ComVisible(false)]

Diff for: devices/Scd4x/README.md

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# SCD4x - Temperature & Humidity & CO2 Sensor
2+
3+
The SCD4x is Sensirion's next generation miniature CO2 sensor. This sensor builds on the photoacoustic sensing principle and Sensirion's patented PAsens(r) and CMOSens(r) technology to offer high accuracy at an unmatched price and smallest form factor.
4+
5+
## Documentation
6+
7+
- SCD4X [datasheet](https://cdn.sparkfun.com/assets/d/4/9/a/d/Sensirion_CO2_Sensors_SCD4x_Datasheet.pdf)
8+
9+
## Usage
10+
11+
### Hardware Required
12+
13+
- SCD4X
14+
- Male/Female Jumper Wires
15+
16+
### Circuit
17+
18+
- SCL - SCL
19+
- SDA - SDA
20+
- VCC - 3.3V
21+
- GND - GND
22+
23+
### Code
24+
25+
**Important**: make sure you properly setup the I2C pins especially for ESP32 before creating the `I2cDevice`, make sure you install the `nanoFramework.Hardware.ESP32 nuget`:
26+
27+
```csharp
28+
//////////////////////////////////////////////////////////////////////
29+
// when connecting to an ESP32 device, need to configure the I2C GPIOs
30+
// used for the bus
31+
Configuration.SetPinFunction(23, DeviceFunction.I2C1_DATA);
32+
Configuration.SetPinFunction(22, DeviceFunction.I2C1_CLOCK);
33+
```
34+
35+
For other devices like STM32, please make sure you're using the preset pins for the I2C bus you want to use.
36+
37+
```csharp
38+
I2cConnectionSettings settings = new(1, Scd4x.I2cDefaultAddress);
39+
using I2cDevice device = I2cDevice.Create(settings);
40+
using Scd4x sensor = new(device);
41+
sensor.StopPeriodicMeasurement();
42+
var serialNumber = sensor.GetSerialNumber();
43+
Console.WriteLine($"Serial number: {serialNumber}");
44+
var offset = sensor.GetTemperatureOffset();
45+
Console.WriteLine($"Temperature offset: {offset.DegreesCelsius}");
46+
sensor.SetTemperatureOffset(Temperature.FromDegreesCelsius(4));
47+
offset = sensor.GetTemperatureOffset();
48+
Console.WriteLine($"New temperature offset: {offset.DegreesCelsius}");
49+
50+
sensor.StartPeriodicMeasurement();
51+
while (true)
52+
{
53+
if (!sensor.IsDataReady())
54+
{
55+
Thread.Sleep(1000);
56+
continue;
57+
}
58+
59+
var data = sensor.ReadData();
60+
Console.WriteLine($"Temperature: {data.Temperature.DegreesCelsius} \u00B0C");
61+
Console.WriteLine($"Relative humidity: {data.RelativeHumidity.Percent} %RH");
62+
Console.WriteLine($"CO2: {data.CO2} PPM");
63+
}
64+
65+
```

Diff for: devices/Scd4x/Scd4x.cs

+206
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
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.Buffers.Binary;
6+
using System.Device.I2c;
7+
using System.Threading;
8+
using UnitsNet;
9+
10+
namespace Iot.Device.Scd4x
11+
{
12+
/// <summary>
13+
/// Humidity and Temperature Sensor Scd4x.
14+
/// </summary>
15+
public class Scd4x : IDisposable
16+
{
17+
private const byte Crc8PolyNominal = 0x31;
18+
private const byte Crc8 = 0xFF;
19+
20+
/// <summary>
21+
/// Default I2C address of the Scd4x sensor.
22+
/// </summary>
23+
public const byte I2cDefaultAddress = 0x62;
24+
25+
/// <summary>
26+
/// Represents an I2C device used for communication with the Scd4x sensor.
27+
/// </summary>
28+
private I2cDevice _i2CDevice;
29+
30+
/// <summary>
31+
/// Initializes a new instance of the <see cref="Scd4x" /> class.
32+
/// </summary>
33+
/// <param name="i2CDevice">The I2C device used for communication.</param>
34+
public Scd4x(I2cDevice i2CDevice)
35+
{
36+
_i2CDevice = i2CDevice ?? throw new ArgumentNullException();
37+
}
38+
39+
/// <inheritdoc/>
40+
public void Dispose()
41+
{
42+
_i2CDevice?.Dispose();
43+
_i2CDevice = null;
44+
}
45+
46+
/// <summary>
47+
/// Retrieves the serial number from the SDC device.
48+
/// </summary>
49+
/// <returns>Serial number.</returns>
50+
public string GetSerialNumber()
51+
{
52+
var result = _i2CDevice.Write(new byte[] { 0x36, 0x82 });
53+
CheckI2cResultOrThrow(result);
54+
55+
var buffer = new byte[9];
56+
result = _i2CDevice.Read(buffer);
57+
CheckI2cResultOrThrow(result);
58+
59+
var serial1 = ReadUInt16BigEndian(buffer, 0);
60+
var serial2 = ReadUInt16BigEndian(buffer, 2);
61+
var serial3 = ReadUInt16BigEndian(buffer, 4);
62+
return $"0x{serial1:X4}{serial2:X4}{serial3:X4}";
63+
}
64+
65+
/// <summary>
66+
/// Starts the periodic measurment.
67+
/// </summary>
68+
public void StartPeriodicMeasurement()
69+
{
70+
var result = _i2CDevice.Write(new byte[] { 0x21, 0xB1 });
71+
CheckI2cResultOrThrow(result);
72+
}
73+
74+
/// <summary>
75+
/// Reads the data.
76+
/// </summary>
77+
/// <returns>Sensor data.</returns>
78+
public Scd4xSensorData ReadData()
79+
{
80+
var result = _i2CDevice.Write(new byte[] { 0xEC, 0x05 });
81+
CheckI2cResultOrThrow(result);
82+
83+
Thread.Sleep(1);
84+
var buffer = new byte[9];
85+
result = _i2CDevice.Read(buffer);
86+
CheckI2cResultOrThrow(result);
87+
88+
var co2 = ReadUInt16BigEndian(buffer, 0);
89+
var tempRaw = ReadUInt16BigEndian(buffer, 3);
90+
var humRaw = ReadUInt16BigEndian(buffer, 6);
91+
92+
var temp = (tempRaw * 175.0 / 65535.0) - 45.0;
93+
var hum = humRaw * 100 / 65535.0;
94+
return new Scd4xSensorData(Temperature.FromDegreesCelsius(temp), RelativeHumidity.FromPercent(hum), VolumeConcentration.FromPartsPerMillion(co2));
95+
}
96+
97+
/// <summary>
98+
/// Stops the periodic measurment.
99+
/// </summary>
100+
public void StopPeriodicMeasurement()
101+
{
102+
var result = _i2CDevice.Write(new byte[] { 0x3F, 0x86 });
103+
CheckI2cResultOrThrow(result);
104+
}
105+
106+
/// <summary>
107+
/// Gets the temperature offset of sensor.
108+
/// </summary>
109+
/// <returns>Temperature offset.</returns>
110+
public Temperature GetTemperatureOffset()
111+
{
112+
var result = _i2CDevice.Write(new byte[] { 0x23, 0x18 });
113+
CheckI2cResultOrThrow(result);
114+
115+
Thread.Sleep(1);
116+
var buffer = new byte[3];
117+
result = _i2CDevice.Read(buffer);
118+
CheckI2cResultOrThrow(result);
119+
120+
var tempRaw = ReadUInt16BigEndian(buffer, 0);
121+
var temp = tempRaw * 175.0 / 65535.0;
122+
return Temperature.FromDegreesCelsius(temp);
123+
}
124+
125+
/// <summary>
126+
/// Sets the temperature offset. Doesn't affects CO2 measurments.
127+
/// </summary>
128+
/// <param name="offset">New offset.</param>
129+
public void SetTemperatureOffset(Temperature offset)
130+
{
131+
var asTicks = (offset.DegreesCelsius * 65536.0 / 175.0) + 0.5f;
132+
var tOffset = (ushort)asTicks;
133+
var crc = GenerateCRC(tOffset);
134+
var buffer = new byte[5];
135+
buffer[0] = 0x24;
136+
buffer[1] = 0x1D;
137+
buffer[2] = (byte)(tOffset >> 8);
138+
buffer[3] = (byte)(tOffset & 0x00FF);
139+
buffer[4] = crc;
140+
141+
var result = _i2CDevice.Write(buffer);
142+
CheckI2cResultOrThrow(result);
143+
}
144+
145+
/// <summary>
146+
/// Gets the status of data.
147+
/// </summary>
148+
/// <returns>True if data is ready to read.</returns>
149+
public bool IsDataReady()
150+
{
151+
var result = _i2CDevice.Write(new byte[] { 0xE4, 0xB8 });
152+
CheckI2cResultOrThrow(result);
153+
154+
Thread.Sleep(1);
155+
var buffer = new byte[3];
156+
result = _i2CDevice.Read(buffer);
157+
CheckI2cResultOrThrow(result);
158+
159+
var word = ReadUInt16BigEndian(buffer, 0);
160+
return (word & 0x07FF) != 0;
161+
}
162+
163+
private static byte GenerateCRC(byte[] data)
164+
{
165+
byte crc = Crc8;
166+
167+
foreach (var currentByte in data)
168+
{
169+
crc ^= currentByte;
170+
for (int crcBit = 0; crcBit < 8; crcBit++)
171+
{
172+
if ((crc & 0x80) != 0)
173+
{
174+
crc = (byte)((crc << 1) ^ Crc8PolyNominal);
175+
}
176+
else
177+
{
178+
crc = (byte)(crc << 1);
179+
}
180+
}
181+
}
182+
183+
return crc;
184+
}
185+
186+
private static byte GenerateCRC(ushort data)
187+
{
188+
var highByte = (byte)(data >> 8);
189+
var lowByte = (byte)(data & 0xFF);
190+
return GenerateCRC(new byte[] { highByte, lowByte });
191+
}
192+
193+
private void CheckI2cResultOrThrow(I2cTransferResult status)
194+
{
195+
if (status.Status != I2cTransferStatus.FullTransfer)
196+
{
197+
throw new Exception();
198+
}
199+
}
200+
201+
private ushort ReadUInt16BigEndian(byte[] buffer, int startIndex)
202+
{
203+
return (ushort)((buffer[startIndex] << 8) | buffer[startIndex + 1]);
204+
}
205+
}
206+
}

Diff for: devices/Scd4x/Scd4x.nfproj

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="Current" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<Import Project="packages\Nerdbank.GitVersioning.3.6.141\build\Nerdbank.GitVersioning.props" Condition="Exists('packages\Nerdbank.GitVersioning.3.6.141\build\Nerdbank.GitVersioning.props')" />
4+
<PropertyGroup Label="Globals">
5+
<NanoFrameworkProjectSystemPath>$(MSBuildExtensionsPath)\nanoFramework\v1.0\</NanoFrameworkProjectSystemPath>
6+
</PropertyGroup>
7+
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.Default.props" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.Default.props')" />
8+
<PropertyGroup>
9+
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
10+
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
11+
<ProjectTypeGuids>{11A8DD76-328B-46DF-9F39-F559912D0360};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
12+
<ProjectGuid>{5CAA85D0-961D-442A-91B2-526722EE4432}</ProjectGuid>
13+
<OutputType>Library</OutputType>
14+
<AppDesignerFolder>Properties</AppDesignerFolder>
15+
<FileAlignment>512</FileAlignment>
16+
<RootNamespace>Iot.Device.Scd4x</RootNamespace>
17+
<AssemblyName>Iot.Device.Scd4x</AssemblyName>
18+
<TargetFrameworkVersion>v1.0</TargetFrameworkVersion>
19+
<DocumentationFile>bin\$(Configuration)\Iot.Device.Scd4x.xml</DocumentationFile>
20+
<LangVersion>9.0</LangVersion>
21+
<StyleCopTreatErrorsAsWarnings>false</StyleCopTreatErrorsAsWarnings>
22+
</PropertyGroup>
23+
<PropertyGroup>
24+
<SignAssembly>true</SignAssembly>
25+
</PropertyGroup>
26+
<PropertyGroup>
27+
<AssemblyOriginatorKeyFile>..\key.snk</AssemblyOriginatorKeyFile>
28+
</PropertyGroup>
29+
<PropertyGroup>
30+
<DelaySign>false</DelaySign>
31+
</PropertyGroup>
32+
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.props" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.props')" />
33+
<ItemGroup>
34+
<Reference Include="mscorlib, Version=1.15.6.0, Culture=neutral, PublicKeyToken=c07d481e9758c731">
35+
<HintPath>packages\nanoFramework.CoreLibrary.1.15.5\lib\mscorlib.dll</HintPath>
36+
</Reference>
37+
<Reference Include="System.Buffers.Binary.BinaryPrimitives">
38+
<HintPath>packages\nanoFramework.System.Buffers.Binary.BinaryPrimitives.1.2.628\lib\System.Buffers.Binary.BinaryPrimitives.dll</HintPath>
39+
</Reference>
40+
<Reference Include="System.Device.I2c, Version=1.1.16.0, Culture=neutral, PublicKeyToken=c07d481e9758c731">
41+
<HintPath>packages\nanoFramework.System.Device.I2c.1.1.16\lib\System.Device.I2c.dll</HintPath>
42+
</Reference>
43+
<Reference Include="UnitsNet.RelativeHumidity, Version=5.56.0.0, Culture=neutral, PublicKeyToken=null">
44+
<HintPath>packages\UnitsNet.nanoFramework.RelativeHumidity.5.56.0\lib\UnitsNet.RelativeHumidity.dll</HintPath>
45+
</Reference>
46+
<Reference Include="UnitsNet.Temperature, Version=5.56.0.0, Culture=neutral, PublicKeyToken=null">
47+
<HintPath>packages\UnitsNet.nanoFramework.Temperature.5.56.0\lib\UnitsNet.Temperature.dll</HintPath>
48+
</Reference>
49+
<Reference Include="UnitsNet.VolumeConcentration">
50+
<HintPath>packages\UnitsNet.nanoFramework.VolumeConcentration.5.56.0\lib\UnitsNet.VolumeConcentration.dll</HintPath>
51+
</Reference>
52+
</ItemGroup>
53+
<ItemGroup>
54+
<Compile Include="Scd4xSensorData.cs" />
55+
<None Include="packages.config" />
56+
<Compile Include="Scd4x.cs" />
57+
</ItemGroup>
58+
<ItemGroup>
59+
<Compile Include="Properties\AssemblyInfo.cs" />
60+
<None Include="*.md" />
61+
</ItemGroup>
62+
<ItemGroup>
63+
<Content Include="packages.lock.json" />
64+
</ItemGroup>
65+
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.CSharp.targets" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.CSharp.targets')" />
66+
<ProjectExtensions>
67+
<ProjectCapabilities>
68+
<ProjectConfigurationsDeclaredAsItems />
69+
</ProjectCapabilities>
70+
</ProjectExtensions>
71+
<Import Project="packages\StyleCop.MSBuild.6.2.0\build\StyleCop.MSBuild.targets" Condition="Exists('packages\StyleCop.MSBuild.6.2.0\build\StyleCop.MSBuild.targets')" />
72+
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
73+
<PropertyGroup>
74+
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
75+
</PropertyGroup>
76+
<Error Condition="!Exists('packages\StyleCop.MSBuild.6.2.0\build\StyleCop.MSBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\StyleCop.MSBuild.6.2.0\build\StyleCop.MSBuild.targets'))" />
77+
<Error Condition="!Exists('packages\Nerdbank.GitVersioning.3.6.141\build\Nerdbank.GitVersioning.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Nerdbank.GitVersioning.3.6.141\build\Nerdbank.GitVersioning.props'))" />
78+
<Error Condition="!Exists('packages\Nerdbank.GitVersioning.3.6.141\build\Nerdbank.GitVersioning.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Nerdbank.GitVersioning.3.6.141\build\Nerdbank.GitVersioning.targets'))" />
79+
</Target>
80+
<Import Project="packages\Nerdbank.GitVersioning.3.6.141\build\Nerdbank.GitVersioning.targets" Condition="Exists('packages\Nerdbank.GitVersioning.3.6.141\build\Nerdbank.GitVersioning.targets')" />
81+
</Project>

0 commit comments

Comments
 (0)