ESPHome  2022.12.8
adc_sensor.cpp
Go to the documentation of this file.
1 #include "adc_sensor.h"
2 #include "esphome/core/log.h"
3 #include "esphome/core/helpers.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 #include <hardware/adc.h>
16 #endif
17 
18 namespace esphome {
19 namespace adc {
20 
21 static const char *const TAG = "adc";
22 
23 // 13bit for S2, and 12bit for all other esp32 variants
24 #ifdef USE_ESP32
25 static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast<adc_bits_width_t>(ADC_WIDTH_MAX - 1);
26 
27 #ifndef SOC_ADC_RTC_MAX_BITWIDTH
28 #if USE_ESP32_VARIANT_ESP32S2
29 static const int SOC_ADC_RTC_MAX_BITWIDTH = 13;
30 #else
31 static const int SOC_ADC_RTC_MAX_BITWIDTH = 12;
32 #endif
33 #endif
34 
35 static const int ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1; // 4095 (12 bit) or 8191 (13 bit)
36 static const int ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1; // 2048 (12 bit) or 4096 (13 bit)
37 #endif
38 
39 #ifdef USE_RP2040
40 extern "C"
41 #endif
42  void
44  ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
45 #if !defined(USE_ADC_SENSOR_VCC) && !defined(USE_RP2040)
46  pin_->setup();
47 #endif
48 
49 #ifdef USE_ESP32
50  adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
51  if (!autorange_) {
52  adc1_config_channel_atten(channel_, attenuation_);
53  }
54 
55  // load characteristics for each attenuation
56  for (int i = 0; i < (int) ADC_ATTEN_MAX; i++) {
57  auto cal_value = esp_adc_cal_characterize(ADC_UNIT_1, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS,
58  1100, // default vref
60  switch (cal_value) {
61  case ESP_ADC_CAL_VAL_EFUSE_VREF:
62  ESP_LOGV(TAG, "Using eFuse Vref for calibration");
63  break;
64  case ESP_ADC_CAL_VAL_EFUSE_TP:
65  ESP_LOGV(TAG, "Using two-point eFuse Vref for calibration");
66  break;
67  case ESP_ADC_CAL_VAL_DEFAULT_VREF:
68  default:
69  break;
70  }
71  }
72 
73 #endif // USE_ESP32
74 
75 #ifdef USE_RP2040
76  static bool initialized = false;
77  if (!initialized) {
78  adc_init();
79  initialized = true;
80  }
81 #endif
82 
83  ESP_LOGCONFIG(TAG, "ADC '%s' setup finished!", this->get_name().c_str());
84 }
85 
87  LOG_SENSOR("", "ADC Sensor", this);
88 #ifdef USE_ESP8266
89 #ifdef USE_ADC_SENSOR_VCC
90  ESP_LOGCONFIG(TAG, " Pin: VCC");
91 #else
92  LOG_PIN(" Pin: ", pin_);
93 #endif
94 #endif // USE_ESP8266
95 
96 #ifdef USE_ESP32
97  LOG_PIN(" Pin: ", pin_);
98  if (autorange_) {
99  ESP_LOGCONFIG(TAG, " Attenuation: auto");
100  } else {
101  switch (this->attenuation_) {
102  case ADC_ATTEN_DB_0:
103  ESP_LOGCONFIG(TAG, " Attenuation: 0db");
104  break;
105  case ADC_ATTEN_DB_2_5:
106  ESP_LOGCONFIG(TAG, " Attenuation: 2.5db");
107  break;
108  case ADC_ATTEN_DB_6:
109  ESP_LOGCONFIG(TAG, " Attenuation: 6db");
110  break;
111  case ADC_ATTEN_DB_11:
112  ESP_LOGCONFIG(TAG, " Attenuation: 11db");
113  break;
114  default: // This is to satisfy the unused ADC_ATTEN_MAX
115  break;
116  }
117  }
118 #endif // USE_ESP32
119 #ifdef USE_RP2040
120  if (this->is_temperature_) {
121  ESP_LOGCONFIG(TAG, " Pin: Temperature");
122  } else {
123  LOG_PIN(" Pin: ", pin_);
124  }
125 #endif
126  LOG_UPDATE_INTERVAL(this);
127 }
128 
129 float ADCSensor::get_setup_priority() const { return setup_priority::DATA; }
130 void ADCSensor::update() {
131  float value_v = this->sample();
132  ESP_LOGV(TAG, "'%s': Got voltage=%.4fV", this->get_name().c_str(), value_v);
133  this->publish_state(value_v);
134 }
135 
136 #ifdef USE_ESP8266
137 float ADCSensor::sample() {
138 #ifdef USE_ADC_SENSOR_VCC
139  int raw = ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance)
140 #else
141  int raw = analogRead(this->pin_->get_pin()); // NOLINT
142 #endif
143  if (output_raw_) {
144  return raw;
145  }
146  return raw / 1024.0f;
147 }
148 #endif
149 
150 #ifdef USE_ESP32
151 float ADCSensor::sample() {
152  if (!autorange_) {
153  int raw = adc1_get_raw(channel_);
154  if (raw == -1) {
155  return NAN;
156  }
157  if (output_raw_) {
158  return raw;
159  }
160  uint32_t mv = esp_adc_cal_raw_to_voltage(raw, &cal_characteristics_[(int) attenuation_]);
161  return mv / 1000.0f;
162  }
163 
164  int raw11, raw6 = ADC_MAX, raw2 = ADC_MAX, raw0 = ADC_MAX;
165  adc1_config_channel_atten(channel_, ADC_ATTEN_DB_11);
166  raw11 = adc1_get_raw(channel_);
167  if (raw11 < ADC_MAX) {
168  adc1_config_channel_atten(channel_, ADC_ATTEN_DB_6);
169  raw6 = adc1_get_raw(channel_);
170  if (raw6 < ADC_MAX) {
171  adc1_config_channel_atten(channel_, ADC_ATTEN_DB_2_5);
172  raw2 = adc1_get_raw(channel_);
173  if (raw2 < ADC_MAX) {
174  adc1_config_channel_atten(channel_, ADC_ATTEN_DB_0);
175  raw0 = adc1_get_raw(channel_);
176  }
177  }
178  }
179 
180  if (raw0 == -1 || raw2 == -1 || raw6 == -1 || raw11 == -1) {
181  return NAN;
182  }
183 
184  uint32_t mv11 = esp_adc_cal_raw_to_voltage(raw11, &cal_characteristics_[(int) ADC_ATTEN_DB_11]);
185  uint32_t mv6 = esp_adc_cal_raw_to_voltage(raw6, &cal_characteristics_[(int) ADC_ATTEN_DB_6]);
186  uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &cal_characteristics_[(int) ADC_ATTEN_DB_2_5]);
187  uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &cal_characteristics_[(int) ADC_ATTEN_DB_0]);
188 
189  // Contribution of each value, in range 0-2048 (12 bit ADC) or 0-4096 (13 bit ADC)
190  uint32_t c11 = std::min(raw11, ADC_HALF);
191  uint32_t c6 = ADC_HALF - std::abs(raw6 - ADC_HALF);
192  uint32_t c2 = ADC_HALF - std::abs(raw2 - ADC_HALF);
193  uint32_t c0 = std::min(ADC_MAX - raw0, ADC_HALF);
194  // max theoretical csum value is 4096*4 = 16384
195  uint32_t csum = c11 + c6 + c2 + c0;
196 
197  // each mv is max 3900; so max value is 3900*4096*4, fits in unsigned32
198  uint32_t mv_scaled = (mv11 * c11) + (mv6 * c6) + (mv2 * c2) + (mv0 * c0);
199  return mv_scaled / (float) (csum * 1000U);
200 }
201 #endif // USE_ESP32
202 
203 #ifdef USE_RP2040
204 float ADCSensor::sample() {
205  if (this->is_temperature_) {
206  adc_set_temp_sensor_enabled(true);
207  delay(1);
208  adc_select_input(4);
209  } else {
210  uint8_t pin = this->pin_->get_pin();
211  adc_gpio_init(pin);
212  adc_select_input(pin - 26);
213  }
214 
215  int raw = adc_read();
216  if (this->is_temperature_) {
217  adc_set_temp_sensor_enabled(false);
218  }
219  if (output_raw_) {
220  return raw;
221  }
222  return raw * 3.3f / 4096.0f;
223 }
224 #endif
225 
226 #ifdef USE_ESP8266
227 std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }
228 #endif
229 
230 } // namespace adc
231 } // namespace esphome
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:18
uint8_t raw[35]
Definition: bl0939.h:19
adc_atten_t attenuation_
Definition: adc_sensor.h:54
const std::string & get_name() const
Definition: entity_base.cpp:11
virtual void setup()=0
ADC_MODE(ADC_VCC) namespace esphome
Definition: adc_sensor.cpp:8
void setup() override
Setup ADc.
esp_adc_cal_characteristics_t cal_characteristics_[(int) ADC_ATTEN_MAX]
Definition: adc_sensor.h:57
virtual uint8_t get_pin() const =0
InternalGPIOPin * pin_
Definition: adc_sensor.h:46
std::string get_mac_address()
Get the device MAC address as a string, in lowercase hex notation.
Definition: helpers.cpp:442
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:72
std::string unique_id() override
float sample() override
void update() override
Update adc values.
Definition: a4988.cpp:4
adc1_channel_t channel_
Definition: adc_sensor.h:55
float get_setup_priority() const override
HARDWARE_LATE setup priority.
void dump_config() override
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:27