ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
sht3x.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file sht3x.cpp
6 * @brief Sensirion SHT3x temperature / humidity codec - implementation. See sht3x.h.
7 */
8
10#include "protocore_config.h"
11#include "services/system/clock.h" // pcdelay
12#include "shared_primitives/crc.h" // PC_CRC8_NRSC5
13
14#if PC_ENABLE_SHT3X
15
16#if defined(ARDUINO)
18#include <Arduino.h>
19#include <Wire.h>
20#endif
21uint8_t pc_sht3x_crc8(const uint8_t *data, size_t len)
22{
23 // The Sensirion CRC-8 is the cataloge's CRC-8/NRSC-5 (poly 0x31, init 0xFF, no reflection, no
24 // final XOR). The loop that used to live here is gone, not reworded: test_crc diffs the shared
25 // engine against that exact loop over every length 0..64, so this is byte-identical to it.
26 return (uint8_t)pc_crc(&PC_CRC8_NRSC5, data, len);
27}
28
29int32_t pc_sht3x_temp_mc(uint16_t raw)
30{
31 // T[C] = -45 + 175 * raw / 65535, in milli-degrees (64-bit to avoid overflow).
32 return (int32_t)(-45000 + (int64_t)175000 * raw / 65535);
33}
34
35int32_t pc_sht3x_rh_mpct(uint16_t raw)
36{
37 int32_t v = (int32_t)((int64_t)100000 * raw / 65535); // RH[%] = 100 * raw / 65535
38 return v > 100000 ? 100000 : v;
39}
40
41bool pc_sht3x_parse(const uint8_t resp[6], int32_t *temp_mc, int32_t *rh_mpct)
42{
43 if (!resp || pc_sht3x_crc8(resp, 2) != resp[2] || pc_sht3x_crc8(resp + 3, 2) != resp[5])
44 {
45 return false;
46 }
47 uint16_t traw = (uint16_t)(((uint16_t)resp[0] << 8) | resp[1]);
48 uint16_t hraw = (uint16_t)(((uint16_t)resp[3] << 8) | resp[4]);
49 if (temp_mc)
50 {
51 *temp_mc = pc_sht3x_temp_mc(traw);
52 }
53 if (rh_mpct)
54 {
55 *rh_mpct = pc_sht3x_rh_mpct(hraw);
56 }
57 return true;
58}
59
60// ---------------------------------------------------------------------------
61// I2C binding
62// ---------------------------------------------------------------------------
63
64#if defined(ARDUINO)
65
66namespace
67{
68// All SHT3x I2C-binding state, owned by one instance (internal linkage): the device address,
69// so it is one named owner, unreachable from any other translation unit.
70struct Sht3xCtx
71{
72 uint8_t addr = PC_SHT3X_I2C_ADDR;
73};
74Sht3xCtx s_sht;
75
76bool send_cmd(uint16_t cmd)
77{
78 Wire.beginTransmission(s_sht.addr);
79 Wire.write((uint8_t)(cmd >> 8));
80 Wire.write((uint8_t)(cmd & 0xFF));
81 return Wire.endTransmission() == 0;
82}
83} // namespace
84
85bool pc_sht3x_begin(uint8_t addr)
86{
87 s_sht.addr = addr ? addr : (uint8_t)PC_SHT3X_I2C_ADDR;
89 bool ok = send_cmd(SHT3X_CMD_SOFT_RESET);
90 pcdelay(2); // soft reset completes in < 1.5 ms
91 return ok;
92}
93
94bool pc_sht3x_read(int32_t *temp_mc, int32_t *rh_mpct)
95{
96 if (!send_cmd(SHT3X_CMD_SINGLE_HIGH))
97 {
98 return false;
99 }
100 pcdelay(20); // a high-repeatability measurement completes in < 15 ms
101 if (Wire.requestFrom((int)s_sht.addr, 6) != 6)
102 {
103 return false;
104 }
105 uint8_t r[6];
106 for (int i = 0; i < 6; i++)
107 {
108 r[i] = (uint8_t)Wire.read();
109 }
110 return pc_sht3x_parse(r, temp_mc, rh_mpct);
111}
112
113#else // host build: no I2C. The CRC + conversion above are host-tested.
114
115bool pc_sht3x_begin(uint8_t)
116{
117 return false;
118}
119bool pc_sht3x_read(int32_t *, int32_t *)
120{
121 return false;
122}
123
124#endif // ARDUINO
125
126#endif // PC_ENABLE_SHT3X
Pluggable monotonic clock for all library timing.
void pcdelay(uint32_t ms)
Block for at least ms milliseconds - the library's single delay primitive.
Definition clock.h:89
Parameterized CRC engine - one source of truth for every cyclic redundancy check.
constexpr pc_crc_params PC_CRC8_NRSC5
CRC-8/NRSC-5 - the Sensirion sensor CRC. check = 0xF7. Used by services/sht3x.
Definition crc.h:177
uint32_t pc_crc(const pc_crc_params *p, const uint8_t *data, size_t len)
One-shot CRC of len octets at data.
Definition crc.h:158
The one owner of the shared I2C bus bring-up for the peripheral drivers.
void pc_i2c_begin()
Bring up the shared I2C bus on PC_I2C_SDA_PIN / PC_I2C_SCL_PIN (-1 = default).
Definition i2c.h:33
User-facing configuration for ProtoCore.
#define PC_SHT3X_I2C_ADDR
I2C address of the SHT3x (0x44 with ADDR low; 0x45 with ADDR high).
Sensirion SHT3x temperature / humidity sensor codec (PC_ENABLE_SHT3X).
uint8_t pc_sht3x_crc8(const uint8_t *data, size_t len)
Sensirion CRC-8 (poly 0x31, init 0xFF) over len bytes.
bool pc_sht3x_parse(const uint8_t resp[6], int32_t *temp_mc, int32_t *rh_mpct)
Decode a six-byte single-shot response (T msb/lsb/crc, RH msb/lsb/crc). Verifies both CRC-8 words,...
int32_t pc_sht3x_temp_mc(uint16_t raw)
Convert a raw 16-bit temperature tick to milli-degrees Celsius.
bool pc_sht3x_begin(uint8_t addr)
Soft-reset the SHT3x at addr over I2C.
#define SHT3X_CMD_SOFT_RESET
soft reset
Definition sht3x.h:36
int32_t pc_sht3x_rh_mpct(uint16_t raw)
Convert a raw 16-bit humidity tick to milli-percent relative humidity (0..100000).
bool pc_sht3x_read(int32_t *temp_mc, int32_t *rh_mpct)
Trigger a single-shot high-repeatability measurement, read + verify the six bytes,...
#define SHT3X_CMD_SINGLE_HIGH
high repeatability, no clock stretching
Definition sht3x.h:33