ESPHome  2024.4.1
adc_sensor.cpp
Go to the documentation of this file.
1 #include "adc_sensor.h"
2 #include "esphome/core/helpers.h"
3 #include "esphome/core/log.h"
4 
5 #ifdef USE_ESP8266
6 #ifdef USE_ADC_SENSOR_VCC
7 #include <Esp.h>
8 ADC_MODE(ADC_VCC)
9 #else
10 #include <Arduino.h>
11 #endif
12 #endif
13 
14 #ifdef USE_RP2040
15 #ifdef CYW43_USES_VSYS_PIN
16 #include "pico/cyw43_arch.h"
17 #endif
18 #include <hardware/adc.h>
19 #endif
20 
21 namespace esphome {
22 namespace adc {
23 
24 static const char *const TAG = "adc";
25 
26 // 13-bit for S2, 12-bit for all other ESP32 variants
27 #ifdef USE_ESP32
28 static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast<adc_bits_width_t>(ADC_WIDTH_MAX - 1);
29 
30 #ifndef SOC_ADC_RTC_MAX_BITWIDTH
31 #if USE_ESP32_VARIANT_ESP32S2
32 static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 13;
33 #else
34 static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 12;
35 #endif
36 #endif
37 
38 static const int ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1; // 4095 (12 bit) or 8191 (13 bit)
39 static const int ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1; // 2048 (12 bit) or 4096 (13 bit)
40 #endif
41 
42 #ifdef USE_RP2040
43 extern "C"
44 #endif
45  void
47  ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
48 #if !defined(USE_ADC_SENSOR_VCC) && !defined(USE_RP2040)
49  pin_->setup();
50 #endif
51 
52 #ifdef USE_ESP32
53  if (channel1_ != ADC1_CHANNEL_MAX) {
54  adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
55  if (!autorange_) {
56  adc1_config_channel_atten(channel1_, attenuation_);
57  }
58  } else if (channel2_ != ADC2_CHANNEL_MAX) {
59  if (!autorange_) {
60  adc2_config_channel_atten(channel2_, attenuation_);
61  }
62  }
63 
64  // load characteristics for each attenuation
65  for (int32_t i = 0; i <= ADC_ATTEN_DB_11; i++) {
66  auto adc_unit = channel1_ != ADC1_CHANNEL_MAX ? ADC_UNIT_1 : ADC_UNIT_2;
67  auto cal_value = esp_adc_cal_characterize(adc_unit, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS,
68  1100, // default vref
70  switch (cal_value) {
71  case ESP_ADC_CAL_VAL_EFUSE_VREF:
72  ESP_LOGV(TAG, "Using eFuse Vref for calibration");
73  break;
74  case ESP_ADC_CAL_VAL_EFUSE_TP:
75  ESP_LOGV(TAG, "Using two-point eFuse Vref for calibration");
76  break;
77  case ESP_ADC_CAL_VAL_DEFAULT_VREF:
78  default:
79  break;
80  }
81  }
82 
83 #endif // USE_ESP32
84 
85 #ifdef USE_RP2040
86  static bool initialized = false;
87  if (!initialized) {
88  adc_init();
89  initialized = true;
90  }
91 #endif
92 
93  ESP_LOGCONFIG(TAG, "ADC '%s' setup finished!", this->get_name().c_str());
94 }
95 
97  LOG_SENSOR("", "ADC Sensor", this);
98 #if defined(USE_ESP8266) || defined(USE_LIBRETINY)
99 #ifdef USE_ADC_SENSOR_VCC
100  ESP_LOGCONFIG(TAG, " Pin: VCC");
101 #else
102  LOG_PIN(" Pin: ", pin_);
103 #endif
104 #endif // USE_ESP8266 || USE_LIBRETINY
105 
106 #ifdef USE_ESP32
107  LOG_PIN(" Pin: ", pin_);
108  if (autorange_) {
109  ESP_LOGCONFIG(TAG, " Attenuation: auto");
110  } else {
111  switch (this->attenuation_) {
112  case ADC_ATTEN_DB_0:
113  ESP_LOGCONFIG(TAG, " Attenuation: 0db");
114  break;
115  case ADC_ATTEN_DB_2_5:
116  ESP_LOGCONFIG(TAG, " Attenuation: 2.5db");
117  break;
118  case ADC_ATTEN_DB_6:
119  ESP_LOGCONFIG(TAG, " Attenuation: 6db");
120  break;
121  case ADC_ATTEN_DB_11:
122  ESP_LOGCONFIG(TAG, " Attenuation: 11db");
123  break;
124  default: // This is to satisfy the unused ADC_ATTEN_MAX
125  break;
126  }
127  }
128 #endif // USE_ESP32
129 
130 #ifdef USE_RP2040
131  if (this->is_temperature_) {
132  ESP_LOGCONFIG(TAG, " Pin: Temperature");
133  } else {
134 #ifdef USE_ADC_SENSOR_VCC
135  ESP_LOGCONFIG(TAG, " Pin: VCC");
136 #else
137  LOG_PIN(" Pin: ", pin_);
138 #endif // USE_ADC_SENSOR_VCC
139  }
140 #endif // USE_RP2040
141 
142  LOG_UPDATE_INTERVAL(this);
143 }
144 
145 float ADCSensor::get_setup_priority() const { return setup_priority::DATA; }
146 void ADCSensor::update() {
147  float value_v = this->sample();
148  ESP_LOGV(TAG, "'%s': Got voltage=%.4fV", this->get_name().c_str(), value_v);
149  this->publish_state(value_v);
150 }
151 
152 #ifdef USE_ESP8266
153 float ADCSensor::sample() {
154 #ifdef USE_ADC_SENSOR_VCC
155  int32_t raw = ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance)
156 #else
157  int32_t raw = analogRead(this->pin_->get_pin()); // NOLINT
158 #endif
159  if (output_raw_) {
160  return raw;
161  }
162  return raw / 1024.0f;
163 }
164 #endif
165 
166 #ifdef USE_ESP32
167 float ADCSensor::sample() {
168  if (!autorange_) {
169  int raw = -1;
170  if (channel1_ != ADC1_CHANNEL_MAX) {
171  raw = adc1_get_raw(channel1_);
172  } else if (channel2_ != ADC2_CHANNEL_MAX) {
173  adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw);
174  }
175 
176  if (raw == -1) {
177  return NAN;
178  }
179  if (output_raw_) {
180  return raw;
181  }
182  uint32_t mv = esp_adc_cal_raw_to_voltage(raw, &cal_characteristics_[(int32_t) attenuation_]);
183  return mv / 1000.0f;
184  }
185 
186  int raw11 = ADC_MAX, raw6 = ADC_MAX, raw2 = ADC_MAX, raw0 = ADC_MAX;
187 
188  if (channel1_ != ADC1_CHANNEL_MAX) {
189  adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_11);
190  raw11 = adc1_get_raw(channel1_);
191  if (raw11 < ADC_MAX) {
192  adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_6);
193  raw6 = adc1_get_raw(channel1_);
194  if (raw6 < ADC_MAX) {
195  adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_2_5);
196  raw2 = adc1_get_raw(channel1_);
197  if (raw2 < ADC_MAX) {
198  adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_0);
199  raw0 = adc1_get_raw(channel1_);
200  }
201  }
202  }
203  } else if (channel2_ != ADC2_CHANNEL_MAX) {
204  adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_11);
205  adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw11);
206  if (raw11 < ADC_MAX) {
207  adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_6);
208  adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw6);
209  if (raw6 < ADC_MAX) {
210  adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_2_5);
211  adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw2);
212  if (raw2 < ADC_MAX) {
213  adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_0);
214  adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw0);
215  }
216  }
217  }
218  }
219 
220  if (raw0 == -1 || raw2 == -1 || raw6 == -1 || raw11 == -1) {
221  return NAN;
222  }
223 
224  uint32_t mv11 = esp_adc_cal_raw_to_voltage(raw11, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_11]);
225  uint32_t mv6 = esp_adc_cal_raw_to_voltage(raw6, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_6]);
226  uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_2_5]);
227  uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_0]);
228 
229  // Contribution of each value, in range 0-2048 (12 bit ADC) or 0-4096 (13 bit ADC)
230  uint32_t c11 = std::min(raw11, ADC_HALF);
231  uint32_t c6 = ADC_HALF - std::abs(raw6 - ADC_HALF);
232  uint32_t c2 = ADC_HALF - std::abs(raw2 - ADC_HALF);
233  uint32_t c0 = std::min(ADC_MAX - raw0, ADC_HALF);
234  // max theoretical csum value is 4096*4 = 16384
235  uint32_t csum = c11 + c6 + c2 + c0;
236 
237  // each mv is max 3900; so max value is 3900*4096*4, fits in unsigned32
238  uint32_t mv_scaled = (mv11 * c11) + (mv6 * c6) + (mv2 * c2) + (mv0 * c0);
239  return mv_scaled / (float) (csum * 1000U);
240 }
241 #endif // USE_ESP32
242 
243 #ifdef USE_RP2040
244 float ADCSensor::sample() {
245  if (this->is_temperature_) {
246  adc_set_temp_sensor_enabled(true);
247  delay(1);
248  adc_select_input(4);
249 
250  int32_t raw = adc_read();
251  adc_set_temp_sensor_enabled(false);
252  if (this->output_raw_) {
253  return raw;
254  }
255  return raw * 3.3f / 4096.0f;
256  } else {
257  uint8_t pin = this->pin_->get_pin();
258 #ifdef CYW43_USES_VSYS_PIN
259  if (pin == PICO_VSYS_PIN) {
260  // Measuring VSYS on Raspberry Pico W needs to be wrapped with
261  // `cyw43_thread_enter()`/`cyw43_thread_exit()` as discussed in
262  // https://github.com/raspberrypi/pico-sdk/issues/1222, since Wifi chip and
263  // VSYS ADC both share GPIO29
264  cyw43_thread_enter();
265  }
266 #endif // CYW43_USES_VSYS_PIN
267 
268  adc_gpio_init(pin);
269  adc_select_input(pin - 26);
270 
271  int32_t raw = adc_read();
272 
273 #ifdef CYW43_USES_VSYS_PIN
274  if (pin == PICO_VSYS_PIN) {
275  cyw43_thread_exit();
276  }
277 #endif // CYW43_USES_VSYS_PIN
278 
279  if (output_raw_) {
280  return raw;
281  }
282  float coeff = pin == PICO_VSYS_PIN ? 3.0 : 1.0;
283  return raw * 3.3f / 4096.0f * coeff;
284  }
285 }
286 #endif
287 
288 #ifdef USE_LIBRETINY
289 float ADCSensor::sample() {
290  if (output_raw_) {
291  return analogRead(this->pin_->get_pin()); // NOLINT
292  }
293  return analogReadVoltage(this->pin_->get_pin()) / 1000.0f; // NOLINT
294 }
295 #endif // USE_LIBRETINY
296 
297 #ifdef USE_ESP8266
298 std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }
299 #endif
300 
301 } // namespace adc
302 } // namespace esphome
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:19
uint8_t raw[35]
Definition: bl0939.h:19
adc_atten_t attenuation_
Definition: adc_sensor.h:61
virtual void setup()=0
ADC_MODE(ADC_VCC) namespace esphome
Definition: adc_sensor.cpp:8
void setup() override
Setup ADC.
virtual uint8_t get_pin() const =0
InternalGPIOPin * pin_
Definition: adc_sensor.h:53
std::string get_mac_address()
Get the device MAC address as a string, in lowercase hex notation.
Definition: helpers.cpp:587
adc2_channel_t channel2_
Definition: adc_sensor.h:63
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
std::string unique_id() override
float sample() override
esp_adc_cal_characteristics_t cal_characteristics_[SOC_ADC_ATTEN_NUM]
Definition: adc_sensor.h:66
void update() override
Update ADC values.
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
float get_setup_priority() const override
HARDWARE_LATE setup priority
void dump_config() override
const StringRef & get_name() const
Definition: entity_base.cpp:10
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26
adc1_channel_t channel1_
Definition: adc_sensor.h:62