forked from amkovkov/GranuSightSoftware2
Первый коммит
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
using System.Device.I2c;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace GSS2.Core.Hardware;
|
||||
|
||||
public class TemperatureHumidityService : IDisposable
|
||||
{
|
||||
public class TemperatureHumidityServiceConfiguration
|
||||
{
|
||||
public static TemperatureHumidityServiceConfiguration Default => new TemperatureHumidityServiceConfiguration
|
||||
{
|
||||
Bus = 0,
|
||||
Address = 0x44
|
||||
};
|
||||
|
||||
public int Bus { get; set; }
|
||||
public int Address { get; set; }
|
||||
}
|
||||
private bool _disposedValue;
|
||||
private readonly ILogger<TemperatureHumidityService> _logger;
|
||||
private readonly TemperatureHumidityServiceConfiguration _configuration;
|
||||
|
||||
private readonly I2cDevice _sensor;
|
||||
|
||||
public TemperatureHumidityService(ILogger<TemperatureHumidityService> logger, TemperatureHumidityServiceConfiguration? configuration = null)
|
||||
{
|
||||
_logger = logger;
|
||||
|
||||
_logger.LogInformation("Temperature and humidity sensor initialization");
|
||||
|
||||
if (configuration is null)
|
||||
{
|
||||
_logger.LogWarning("Temperature and humidity sensor configuration not set. Using default configuration.");
|
||||
_configuration = TemperatureHumidityServiceConfiguration.Default;
|
||||
}
|
||||
else
|
||||
{
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
_logger.LogDebug(@$"Temperature and humidity sensor use following configuration:
|
||||
Bus: {_configuration.Bus}
|
||||
Address: {_configuration.Address}
|
||||
");
|
||||
|
||||
try
|
||||
{
|
||||
_sensor = I2cDevice.Create(new I2cConnectionSettings(_configuration.Bus, _configuration.Address));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogCritical(e, "An exception occurred while temperature and humidity sensor initialization");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(double temperature, double relativeHumidity)> MeasureAsync(int measurementsCount = 1, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// В соответствии с даташитом на SHT45-AD1B-R2 псевдокод измерения с высокой точностью следующий:
|
||||
// i2c_write(i2c_addr=0x44, tx_bytes=[0xFD])
|
||||
// wait_seconds(0.01)
|
||||
// rx_bytes = i2c_read(i2c_addr=0x44, number_of_bytes=6)
|
||||
// t_ticks = rx_bytes[0] * 256 + rx_bytes[1]
|
||||
// checksum_t = rx_bytes[2]
|
||||
// rh_ticks = rx_bytes[3] * 256 + rx_bytes[4]
|
||||
// checksum_rh = rx_bytes[5]
|
||||
// t_degC = -45 + 175 * t_ticks/65535
|
||||
// rh_pRH = -6 + 125 * rh_ticks/65535
|
||||
// if (rh_pRH > 100):
|
||||
// rh_pRH = 100
|
||||
// if (rh_pRH < 0):
|
||||
// rh_pRH = 0
|
||||
|
||||
if (measurementsCount <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(measurementsCount));
|
||||
|
||||
double sumTemperature = 0;
|
||||
double sumRelativeHumidity = 0;
|
||||
int successMeasurementsCount = 0;
|
||||
|
||||
Span<byte> command = stackalloc byte[] { 0xFD };
|
||||
Span<byte> data = stackalloc byte[6];
|
||||
|
||||
for (int i = 0; i < measurementsCount; i++)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
// Отправляем команду
|
||||
_sensor.Write(command);
|
||||
// 10 мс ожидания
|
||||
await Task.Delay(10, cancellationToken);
|
||||
// Читаем данные
|
||||
_sensor.Read(data);
|
||||
|
||||
// Распаковываем данные [2 * 8-bit T-data; 8-bit CRC; 2 * 8-bit RH-data; 8-bit CRC]
|
||||
ushort temperatureTicks = BitConverter.ToUInt16(data.ToArray(), 0);
|
||||
byte temperatureChecksum = data[2];
|
||||
int relativeHumidityTicks = BitConverter.ToUInt16(data.ToArray(), 3);
|
||||
byte relativeHumidityChecksum = data[5];
|
||||
|
||||
// Проверка по CRC
|
||||
if (!CheckCrc(data.Slice(0, 2), temperatureChecksum) ||
|
||||
!CheckCrc(data.Slice(3, 2), relativeHumidityChecksum))
|
||||
{
|
||||
_logger.LogWarning("Temperature and humidity sensor CRC error");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Пересчёт
|
||||
double temperature = -45 + 175 * (temperatureTicks / 65535.0);
|
||||
double relativeHumidity = -6 + 125 * (relativeHumidityTicks / 65535.0);
|
||||
relativeHumidity = Math.Clamp(relativeHumidity, 0, 100);
|
||||
|
||||
// Сумма для усреднения
|
||||
sumTemperature += temperature;
|
||||
sumRelativeHumidity += relativeHumidity;
|
||||
successMeasurementsCount++;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogWarning(e, "Temperature and humidity sensor not available on I2C bus");
|
||||
}
|
||||
}
|
||||
|
||||
if (successMeasurementsCount == 0)
|
||||
{
|
||||
_logger.LogError("Temperature and humidity sensor: no successful measurements");
|
||||
return (double.NaN, double.NaN);
|
||||
}
|
||||
|
||||
return (sumTemperature / successMeasurementsCount, sumRelativeHumidity / successMeasurementsCount);
|
||||
}
|
||||
|
||||
private static bool CheckCrc(Span<byte> data, byte expectedCrc)
|
||||
{
|
||||
byte crc = 0xFF;
|
||||
foreach (var b in data)
|
||||
{
|
||||
crc ^= b;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if ((crc & 0x80) != 0)
|
||||
crc = (byte)((crc << 1) ^ 0x31);
|
||||
else
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
return crc == expectedCrc;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
try
|
||||
{
|
||||
_sensor?.Dispose();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
_disposedValue = true;
|
||||
}
|
||||
}
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user