ESPHome  2024.10.0
sm300d2.cpp
Go to the documentation of this file.
1 #include "sm300d2.h"
2 #include "esphome/core/log.h"
3 
4 namespace esphome {
5 namespace sm300d2 {
6 
7 static const char *const TAG = "sm300d2";
8 static const uint8_t SM300D2_RESPONSE_LENGTH = 17;
9 
11  uint8_t response[SM300D2_RESPONSE_LENGTH];
12  uint8_t peeked;
13 
14  while (this->available() > 0 && this->peek_byte(&peeked) && peeked != 0x3C)
15  this->read();
16 
17  bool read_success = read_array(response, SM300D2_RESPONSE_LENGTH);
18 
19  if (!read_success) {
20  ESP_LOGW(TAG, "Reading data from SM300D2 failed!");
22  return;
23  }
24 
25  if (response[0] != 0x3C || response[1] != 0x02) {
26  ESP_LOGW(TAG, "Invalid preamble for SM300D2 response!");
27  this->status_set_warning();
28  return;
29  }
30 
31  uint16_t calculated_checksum = this->sm300d2_checksum_(response);
32  // Occasionally the checksum has a +/- 0x80 offset. Negative temperatures are
33  // responsible for some of these. The rest are unknown/undocumented.
34  if ((calculated_checksum != response[SM300D2_RESPONSE_LENGTH - 1]) &&
35  (calculated_checksum - 0x80 != response[SM300D2_RESPONSE_LENGTH - 1]) &&
36  (calculated_checksum + 0x80 != response[SM300D2_RESPONSE_LENGTH - 1])) {
37  ESP_LOGW(TAG, "SM300D2 Checksum doesn't match: 0x%02X!=0x%02X", response[SM300D2_RESPONSE_LENGTH - 1],
38  calculated_checksum);
39  this->status_set_warning();
40  return;
41  }
42 
43  this->status_clear_warning();
44 
45  ESP_LOGD(TAG, "Successfully read SM300D2 data");
46 
47  const uint16_t co2 = (response[2] * 256) + response[3];
48  const uint16_t formaldehyde = (response[4] * 256) + response[5];
49  const uint16_t tvoc = (response[6] * 256) + response[7];
50  const uint16_t pm_2_5 = (response[8] * 256) + response[9];
51  const uint16_t pm_10_0 = (response[10] * 256) + response[11];
52  // A negative value is indicated by adding 0x80 (128) to the temperature value
53  const float temperature = ((response[12] + (response[13] * 0.1f)) > 128)
54  ? (((response[12] + (response[13] * 0.1f)) - 128) * -1)
55  : response[12] + (response[13] * 0.1f);
56  const float humidity = response[14] + (response[15] * 0.1f);
57 
58  ESP_LOGD(TAG, "Received CO₂: %u ppm", co2);
59  if (this->co2_sensor_ != nullptr)
60  this->co2_sensor_->publish_state(co2);
61 
62  ESP_LOGD(TAG, "Received Formaldehyde: %u µg/m³", formaldehyde);
63  if (this->formaldehyde_sensor_ != nullptr)
64  this->formaldehyde_sensor_->publish_state(formaldehyde);
65 
66  ESP_LOGD(TAG, "Received TVOC: %u µg/m³", tvoc);
67  if (this->tvoc_sensor_ != nullptr)
68  this->tvoc_sensor_->publish_state(tvoc);
69 
70  ESP_LOGD(TAG, "Received PM2.5: %u µg/m³", pm_2_5);
71  if (this->pm_2_5_sensor_ != nullptr)
72  this->pm_2_5_sensor_->publish_state(pm_2_5);
73 
74  ESP_LOGD(TAG, "Received PM10: %u µg/m³", pm_10_0);
75  if (this->pm_10_0_sensor_ != nullptr)
76  this->pm_10_0_sensor_->publish_state(pm_10_0);
77 
78  ESP_LOGD(TAG, "Received Temperature: %.2f °C", temperature);
79  if (this->temperature_sensor_ != nullptr)
80  this->temperature_sensor_->publish_state(temperature);
81 
82  ESP_LOGD(TAG, "Received Humidity: %.2f percent", humidity);
83  if (this->humidity_sensor_ != nullptr)
84  this->humidity_sensor_->publish_state(humidity);
85 }
86 
87 uint16_t SM300D2Sensor::sm300d2_checksum_(uint8_t *ptr) {
88  uint8_t sum = 0;
89  for (int i = 0; i < (SM300D2_RESPONSE_LENGTH - 1); i++) {
90  sum += *ptr++;
91  }
92  return sum;
93 }
94 
96  ESP_LOGCONFIG(TAG, "SM300D2:");
97  LOG_SENSOR(" ", "CO2", this->co2_sensor_);
98  LOG_SENSOR(" ", "Formaldehyde", this->formaldehyde_sensor_);
99  LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_);
100  LOG_SENSOR(" ", "PM2.5", this->pm_2_5_sensor_);
101  LOG_SENSOR(" ", "PM10", this->pm_10_0_sensor_);
102  LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
103  LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
104  this->check_uart_settings(9600);
105 }
106 
107 } // namespace sm300d2
108 } // namespace esphome
sensor::Sensor * pm_10_0_sensor_
Definition: sm300d2.h:32
sensor::Sensor * humidity_sensor_
Definition: sm300d2.h:34
bool peek_byte(uint8_t *data)
Definition: uart.h:30
sensor::Sensor * co2_sensor_
Definition: sm300d2.h:28
sensor::Sensor * pm_2_5_sensor_
Definition: sm300d2.h:31
optional< std::array< uint8_t, N > > read_array()
Definition: uart.h:33
void status_set_warning(const char *message="unspecified")
Definition: component.cpp:151
sensor::Sensor * formaldehyde_sensor_
Definition: sm300d2.h:29
void check_uart_settings(uint32_t baud_rate, uint8_t stop_bits=1, UARTParityOptions parity=UART_CONFIG_PARITY_NONE, uint8_t data_bits=8)
Check that the configuration of the UART bus matches the provided values and otherwise print a warnin...
Definition: uart.cpp:13
void status_clear_warning()
Definition: component.cpp:166
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
uint16_t temperature
Definition: sun_gtil2.cpp:26
uint16_t sm300d2_checksum_(uint8_t *ptr)
Definition: sm300d2.cpp:87
void dump_config() override
Definition: sm300d2.cpp:95
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
sensor::Sensor * temperature_sensor_
Definition: sm300d2.h:33
sensor::Sensor * tvoc_sensor_
Definition: sm300d2.h:30