ruuvi.drivers.c  ${PROJECT_VERSION}
Drivers for external sensors and peripherals on embedded systems.
ruuvi_interface_shtcx.c
Go to the documentation of this file.
1 
11 #if RI_SHTCX_ENABLED || DOXYGEN
12 // Ruuvi headers
13 #include "ruuvi_driver_error.h"
14 #include "ruuvi_driver_sensor.h"
15 #include "ruuvi_interface_shtcx.h"
17 #include "ruuvi_interface_gpio.h"
18 #include "ruuvi_interface_i2c.h"
19 #include "ruuvi_interface_shtcx.h"
20 #include "ruuvi_interface_rtc.h"
21 #include "ruuvi_interface_yield.h"
22 
23 #include <string.h>
24 
30 // Sensirion driver.
31 #include "shtc1.h"
32 #define LOW_POWER_SLEEP_MS_MIN (1000U)
33 #define SHTCX_PROBE_RETRIES_MAX (5U)
34 
35 static inline uint32_t US_TO_MS_ROUNDUP (uint32_t us)
36 {
37  return (us / 1000) + 2;
38 }
39 
41 #define RETURN_SUCCESS_ON_VALID(param) do {\
42  if(RD_SENSOR_CFG_DEFAULT == param ||\
43  RD_SENSOR_CFG_MIN == param ||\
44  RD_SENSOR_CFG_MAX == param ||\
45  RD_SENSOR_CFG_NO_CHANGE == param \
46  ) return RD_SUCCESS;\
47  } while(0)
48 
50 #define VERIFY_SENSOR_SLEEPS() do { \
51  uint8_t MACRO_MODE = 0; \
52  ri_shtcx_mode_get(&MACRO_MODE); \
53  if(RD_SENSOR_CFG_SLEEP != MACRO_MODE) { return RD_ERROR_INVALID_STATE; } \
54  } while(0)
55 
56 static uint64_t m_tsample;
57 static bool m_autorefresh;
58 static int32_t m_temperature;
59 static int32_t m_humidity;
60 static bool m_is_init;
61 static const char m_sensor_name[] = "SHTCX";
62 
63 #define STATUS_OK 0
64 #define STATUS_ERR_BAD_DATA (-1)
65 #define STATUS_CRC_FAIL (-2)
66 #define STATUS_UNKNOWN_DEVICE (-3)
67 #define STATUS_WAKEUP_FAILED (-4)
68 #define STATUS_SLEEP_FAILED (-5)
69 
76 static rd_status_t SHTCX_TO_RUUVI_ERROR (const int16_t rslt)
77 {
78  if (STATUS_OK == rslt) { return RD_SUCCESS; }
79 
80  rd_status_t err_code = RD_ERROR_INTERNAL;
81 
82  if (STATUS_UNKNOWN_DEVICE == rslt) { err_code = RD_ERROR_NOT_FOUND; }
83  else if (STATUS_ERR_BAD_DATA == rslt) { err_code = RD_ERROR_INVALID_DATA; }
84  else if (STATUS_CRC_FAIL == rslt) { err_code = RD_ERROR_INVALID_DATA; }
85  else if (STATUS_WAKEUP_FAILED == rslt) { err_code = RD_ERROR_INTERNAL; }
86  else if (STATUS_SLEEP_FAILED == rslt) { err_code = RD_ERROR_INTERNAL; }
87 
88  return err_code;
89 }
90 
91 rd_status_t ri_shtcx_init (rd_sensor_t * sensor, rd_bus_t bus, uint8_t handle)
92 {
93  rd_status_t err_code = RD_SUCCESS;
94 
95  if (NULL == sensor)
96  {
97  err_code |= RD_ERROR_NULL;
98  }
99  else if (rd_sensor_is_init (sensor))
100  {
101  err_code |= RD_ERROR_INVALID_STATE;
102  }
103  else
104  {
105  rd_sensor_initialize (sensor);
106  sensor->name = m_sensor_name;
107  uint8_t retries = 0;
108 
109  switch (bus)
110  {
111  case RD_BUS_I2C:
112  do
113  {
114  err_code = SHTCX_TO_RUUVI_ERROR (shtc1_probe());
115  retries++;
116  } while ( (RD_ERROR_INVALID_DATA == err_code)
117 
118  && (retries < SHTCX_PROBE_RETRIES_MAX));
119 
120  break;
121 
122  default:
123  err_code |= RD_ERROR_INVALID_PARAM;
124  }
125 
126  if (RD_SUCCESS != err_code)
127  {
128  err_code = RD_ERROR_NOT_FOUND;
129  }
130  else
131  {
132  // Sensirion driver delays high-power mode time in any case.
133  // Explicitly entering low-power mode has no effect.
134  shtc1_enable_low_power_mode (0);
135  sensor->init = ri_shtcx_init;
136  sensor->uninit = ri_shtcx_uninit;
141  sensor->scale_set = ri_shtcx_scale_set;
142  sensor->scale_get = ri_shtcx_scale_get;
143  sensor->dsp_set = ri_shtcx_dsp_set;
144  sensor->dsp_get = ri_shtcx_dsp_get;
145  sensor->mode_set = ri_shtcx_mode_set;
146  sensor->mode_get = ri_shtcx_mode_get;
147  sensor->data_get = ri_shtcx_data_get;
150  sensor->provides.datas.temperature_c = 1;
151  sensor->provides.datas.humidity_rh = 1;
152  err_code |= SHTCX_TO_RUUVI_ERROR (shtc1_sleep());
153  m_tsample = RD_UINT64_INVALID;
154  m_is_init = true;
155  }
156  }
157 
158  return err_code;
159 }
160 
162  rd_bus_t bus, uint8_t handle)
163 {
164  if (NULL == sensor) { return RD_ERROR_NULL; }
165 
166  rd_status_t err_code = RD_SUCCESS;
167  shtc1_enable_low_power_mode (1);
168  err_code |= SHTCX_TO_RUUVI_ERROR (shtc1_sleep());
169  rd_sensor_uninitialize (sensor);
170  m_tsample = RD_UINT64_INVALID;
171  m_temperature = RD_INT32_INVALID;
172  m_humidity = RD_INT32_INVALID;
173  m_is_init = false;
174  m_autorefresh = false;
175  return err_code;
176 }
177 
178 rd_status_t ri_shtcx_samplerate_set (uint8_t * samplerate)
179 {
180  if (NULL == samplerate) { return RD_ERROR_NULL; }
181 
183  rd_status_t err_code = RD_SUCCESS;
184 
185  if (RD_SENSOR_CFG_DEFAULT == *samplerate) { *samplerate = RD_SENSOR_CFG_DEFAULT; }
186  else if (RD_SENSOR_CFG_NO_CHANGE == *samplerate) { *samplerate = RD_SENSOR_CFG_DEFAULT; }
187  else if (RD_SENSOR_CFG_MIN == *samplerate) { *samplerate = RD_SENSOR_CFG_DEFAULT; }
188  else if (RD_SENSOR_CFG_MAX == *samplerate) {*samplerate = RD_SENSOR_CFG_DEFAULT; }
189  else { *samplerate = RD_SENSOR_ERR_NOT_SUPPORTED; err_code |= RD_ERROR_NOT_SUPPORTED; }
190 
191  return err_code;
192 }
193 
194 rd_status_t ri_shtcx_samplerate_get (uint8_t * samplerate)
195 {
196  if (NULL == samplerate) { return RD_ERROR_NULL; }
197 
198  *samplerate = RD_SENSOR_CFG_DEFAULT;
199  return RD_SUCCESS;
200 }
201 
202 rd_status_t ri_shtcx_resolution_set (uint8_t * resolution)
203 {
204  if (NULL == resolution) { return RD_ERROR_NULL; }
205 
207  uint8_t original = *resolution;
208  *resolution = RD_SENSOR_CFG_DEFAULT;
209  RETURN_SUCCESS_ON_VALID (original);
210  return RD_ERROR_NOT_SUPPORTED;
211 }
212 
213 rd_status_t ri_shtcx_resolution_get (uint8_t * resolution)
214 {
215  if (NULL == resolution) { return RD_ERROR_NULL; }
216 
217  *resolution = RD_SENSOR_CFG_DEFAULT;
218  return RD_SUCCESS;
219 }
220 
222 {
223  if (NULL == scale) { return RD_ERROR_NULL; }
224 
226  uint8_t original = *scale;
227  *scale = RD_SENSOR_CFG_DEFAULT;
228  RETURN_SUCCESS_ON_VALID (original);
229  return RD_ERROR_NOT_SUPPORTED;
230 }
231 
233 {
234  if (NULL == scale) { return RD_ERROR_NULL; }
235 
236  *scale = RD_SENSOR_CFG_DEFAULT;
237  return RD_SUCCESS;
238 }
239 
240 rd_status_t ri_shtcx_dsp_set (uint8_t * dsp, uint8_t * parameter)
241 {
242  if (NULL == dsp || NULL == parameter) { return RD_ERROR_NULL; }
243 
245 
246  // Validate configuration
247  if ( (RD_SENSOR_CFG_DEFAULT != *parameter
248  && RD_SENSOR_CFG_MIN != *parameter
249  && RD_SENSOR_CFG_MAX != *parameter) ||
250  (RD_SENSOR_DSP_LAST != *dsp))
251  {
252  return RD_ERROR_NOT_SUPPORTED;
253  }
254 
255  return RD_SUCCESS;
256 }
257 
258 rd_status_t ri_shtcx_dsp_get (uint8_t * dsp, uint8_t * parameter)
259 {
260  if (NULL == dsp || NULL == parameter) { return RD_ERROR_NULL; }
261 
262  // Only default is available
263  *dsp = RD_SENSOR_CFG_DEFAULT;
264  *parameter = RD_SENSOR_CFG_DEFAULT;
265  return RD_SUCCESS;
266 }
267 
268 // Start single on command, mark autorefresh with continuous
270 {
271  rd_status_t err_code = RD_SUCCESS;
272 
273  if (NULL == mode)
274  {
275  err_code |= RD_ERROR_NULL;
276  }
277  // Enter sleep by default and by explicit sleep commmand
278  else if ( (RD_SENSOR_CFG_SLEEP == *mode) || (RD_SENSOR_CFG_DEFAULT == *mode))
279  {
280  m_autorefresh = false;
281  *mode = RD_SENSOR_CFG_SLEEP;
282  err_code |= SHTCX_TO_RUUVI_ERROR (shtc1_sleep());
283  }
284  else if (RD_SENSOR_CFG_SINGLE == *mode)
285  {
286  // Do nothing if sensor is in continuous mode
287  uint8_t current_mode;
288  ri_shtcx_mode_get (&current_mode);
289 
290  if (RD_SENSOR_CFG_CONTINUOUS == current_mode)
291  {
292  *mode = RD_SENSOR_CFG_CONTINUOUS;
293  return RD_ERROR_INVALID_STATE;
294  }
295 
296  // Enter sleep after measurement
297  m_autorefresh = false;
298  *mode = RD_SENSOR_CFG_SLEEP;
299  m_tsample = rd_sensor_timestamp_get();
300  err_code |= SHTCX_TO_RUUVI_ERROR (shtc1_wake_up());
302  err_code |= SHTCX_TO_RUUVI_ERROR (shtc1_measure_blocking_read (&m_temperature,
303  &m_humidity));
304  err_code |= SHTCX_TO_RUUVI_ERROR (shtc1_sleep());
305  return err_code;
306  }
307  else if (RD_SENSOR_CFG_CONTINUOUS == *mode)
308  {
309  m_autorefresh = true;
310  err_code |= RD_SUCCESS;
311  }
312  else
313  {
314  err_code |= RD_ERROR_INVALID_PARAM;
315  }
316 
317  return err_code;
318 }
319 
321 {
322  if (NULL == mode) { return RD_ERROR_NULL; }
323 
324  if (m_autorefresh)
325  {
326  *mode = RD_SENSOR_CFG_CONTINUOUS;
327  }
328 
329  if (!m_autorefresh)
330  {
331  *mode = RD_SENSOR_CFG_SLEEP;
332  }
333 
334  return RD_SUCCESS;
335 }
336 
338 {
339  rd_status_t err_code = RD_SUCCESS;
340 
341  if (NULL == p_data)
342  {
343  err_code |= RD_ERROR_NULL;
344  }
345  else
346  {
347  if (m_autorefresh)
348  {
349  // Set autorefresh to false to take a single sample
350  m_autorefresh = false;
351  uint8_t mode = RD_SENSOR_CFG_SINGLE;
352  err_code |= ri_shtcx_mode_set (&mode);
353  // Restore autorefresh
354  m_autorefresh = true;
355  }
356 
357  if ( (RD_SUCCESS == err_code) && (RD_UINT64_INVALID != m_tsample))
358  {
359  rd_sensor_data_t d_environmental;
360  rd_sensor_data_fields_t env_fields = {.bitfield = 0};
361  float env_values[2];
362  env_values[0] = m_humidity / 1000.0f;
363  env_values[1] = m_temperature / 1000.0f;
364  env_fields.datas.humidity_rh = 1;
365  env_fields.datas.temperature_c = 1;
366  d_environmental.data = env_values;
367  d_environmental.valid = env_fields;
368  d_environmental.fields = env_fields;
369  d_environmental.timestamp_ms = m_tsample;
370  rd_sensor_data_populate (p_data,
371  &d_environmental,
372  p_data->fields);
373  }
374  }
375 
376  return err_code;
377 }
378 
379 // Ceedling mocks sensirion functions
380 #ifndef CEEDLING
381 
395 void sensirion_sleep_usec (uint32_t useconds)
396 {
397  if (useconds < LOW_POWER_SLEEP_MS_MIN)
398  {
399  ri_delay_us (useconds);
400  }
401  else
402  {
403  ri_delay_ms (US_TO_MS_ROUNDUP (useconds));
404  }
405 }
406 #endif
407 
410 #endif // RI_SHTCX_ENABLED || DOXYGEN
#define RD_ERROR_INVALID_PARAM
Invalid Parameter.
#define RD_ERROR_NULL
Null Pointer.
uint32_t rd_status_t
bitfield for representing errors
#define RD_ERROR_INVALID_DATA
Invalid Data.
#define RD_INT32_INVALID
Signal that value should not be used.
#define RD_ERROR_NOT_SUPPORTED
Not supported.
#define RD_SUCCESS
Internal Error.
#define RD_ERROR_NOT_FOUND
Not found.
#define RD_UINT64_INVALID
Signal that value should not be used.
#define RD_ERROR_INVALID_STATE
Invalid state, operation disallowed in this state.
#define RD_ERROR_INTERNAL
Internal Error.
#define LOW_POWER_SLEEP_MS_MIN
#define STATUS_OK
SHTC driver ok.
rd_status_t ri_shtcx_uninit(rd_sensor_t *sensor, rd_bus_t bus, uint8_t handle)
rd_sensor_init_fp
rd_status_t ri_shtcx_scale_set(uint8_t *scale)
rd_sensor_setup_fp
rd_status_t ri_shtcx_samplerate_set(uint8_t *samplerate)
rd_sensor_setup_fp
#define RETURN_SUCCESS_ON_VALID(param)
Macro for checking "ignored" parameters NO_CHANGE, MIN, MAX, DEFAULT.
rd_status_t ri_shtcx_dsp_get(uint8_t *dsp, uint8_t *parameter)
#define STATUS_UNKNOWN_DEVICE
Invalid WHOAMI.
#define STATUS_SLEEP_FAILED
Device didn't go to sleep.
void sensirion_sleep_usec(uint32_t useconds)
Implement sleep function for SHTC driver.
rd_status_t ri_shtcx_dsp_set(uint8_t *dsp, uint8_t *parameter)
rd_sensor_setup_fp
#define STATUS_WAKEUP_FAILED
Device didn't wake up.
#define STATUS_CRC_FAIL
SHTC driver CRC error.
rd_status_t ri_shtcx_init(rd_sensor_t *sensor, rd_bus_t bus, uint8_t handle)
rd_sensor_init_fp
rd_status_t ri_shtcx_scale_get(uint8_t *scale)
rd_sensor_setup_fp
rd_status_t ri_shtcx_data_get(rd_sensor_data_t *const p_data)
rd_sensor_data_fp
#define VERIFY_SENSOR_SLEEPS()
Macro for checking that sensor is in sleep mode before configuration.
#define STATUS_ERR_BAD_DATA
SHTC driver data invald.
rd_status_t ri_shtcx_samplerate_get(uint8_t *samplerate)
rd_sensor_setup_fp
rd_status_t ri_shtcx_resolution_get(uint8_t *resolution)
rd_sensor_setup_fp
#define SHTCX_PROBE_RETRIES_MAX
rd_status_t ri_shtcx_mode_get(uint8_t *mode)
rd_sensor_setup_fp
rd_status_t ri_shtcx_mode_set(uint8_t *mode)
rd_sensor_setup_fp
rd_status_t ri_shtcx_resolution_set(uint8_t *resolution)
rd_sensor_setup_fp
#define RD_SENSOR_DSP_LAST
Return last value from sensor. Parameter: No effect. Use default.
#define RD_SENSOR_ERR_NOT_SUPPORTED
Error code, given parameter is not supported by sensor.
bool rd_sensor_is_init(const rd_sensor_t *const sensor)
Check if given sensor structure is already initialized.
#define RD_SENSOR_CFG_SLEEP
Sensor should go to sleep immediately.
#define RD_SENSOR_CFG_MAX
Configure largest supported and implemented value.
#define RD_SENSOR_CFG_DEFAULT
Default value, always valid for the sensor.
void rd_sensor_uninitialize(rd_sensor_t *const p_sensor)
Mark sensor as uninitialized by calling the generic initialization. Will not clear the name of the se...
rd_status_t rd_sensor_configuration_get(const rd_sensor_t *sensor, rd_sensor_configuration_t *config)
Implementation of ref rd_configuration_fp.
void rd_sensor_initialize(rd_sensor_t *const p_sensor)
Initialize sensor struct with non-null pointers which return RD_ERROR_NOT_INITIALIZED.
void rd_sensor_data_populate(rd_sensor_data_t *const target, const rd_sensor_data_t *const provided, const rd_sensor_data_fields_t requested)
Populate given target data with data provided by sensor as requested.
rd_status_t rd_sensor_configuration_set(const rd_sensor_t *sensor, rd_sensor_configuration_t *config)
Implementation of ref rd_configuration_fp.
#define RD_SENSOR_CFG_MIN
Configure smallest supported and implemented value.
#define RD_SENSOR_CFG_CONTINUOUS
Sensor will keep sampling at defined sample rate.
#define RD_SENSOR_CFG_NO_CHANGE
Do not change configured value.
rd_bus_t
Type of bus sensor uses.
uint64_t rd_sensor_timestamp_get(void)
Calls the timestamp function and returns its value.
#define RD_SENSOR_CFG_SINGLE
Sensor should go to sleep after single measurement.
@ RD_BUS_I2C
I2C bus.
Header to enable and disable module compilation.
Ruuvi error codes and error check function.
Ruuvi sensor interface Lifecycle: Beta
Interface for I2C operations.
#define RI_SHTCX_WAKEUP_US
Time from wakeup cmd to rdy.
rd_status_t ri_delay_us(uint32_t time)
Delay a given number of microseconds.
rd_status_t ri_delay_ms(uint32_t time)
Delay a given number of milliseconds.
unsigned int humidity_rh
Relative humidity, %.
unsigned int temperature_c
Temperature, celcius.
Generic sensor data struct.
float * data
Data of sensor. Must contain as many elements as fields has bits set.
uint64_t timestamp_ms
Timestamp of the event, rd_sensor_timestamp_get.
rd_sensor_data_fields_t valid
Listing of valid data in this sample.
rd_sensor_data_fields_t fields
Description of datafields which may be contained in this sample.
Interface to sensor. Some sensors can implement additional functions. The additional functions are de...
rd_sensor_data_fp data_get
rd_sensor_data_fp
rd_configuration_fp configuration_set
rd_configuration_fp
rd_sensor_setup_fp samplerate_get
rd_sensor_setup_fp
rd_sensor_setup_fp resolution_get
rd_sensor_setup_fp
rd_sensor_init_fp uninit
rd_sensor_init_fp
rd_configuration_fp configuration_get
rd_configuration_fp
rd_sensor_dsp_fp dsp_get
rd_sensor_dsp_fp
rd_sensor_data_fields_t provides
Description of data fields the sensor is able to provide.
rd_sensor_dsp_fp dsp_set
rd_sensor_dsp_fp
rd_sensor_setup_fp mode_set
rd_sensor_setup_fp
const char * name
Sensor human-readable name. Should be at most 8 bytes long.
rd_sensor_setup_fp mode_get
rd_sensor_setup_fp
rd_sensor_setup_fp samplerate_set
rd_sensor_setup_fp
rd_sensor_init_fp init
rd_sensor_init_fp
rd_sensor_setup_fp scale_set
rd_sensor_setup_fp
rd_sensor_setup_fp resolution_set
rd_sensor_setup_fp
rd_sensor_setup_fp scale_get
rd_sensor_setup_fp
Union to access sensor data.
uint32_t bitfield
Bitfield used to access sensor data.
rd_sensor_data_bitfield_t datas
Structured data field.