ESPHome  2024.5.4
as3935.cpp
Go to the documentation of this file.
1 #include "as3935.h"
2 #include "esphome/core/log.h"
3 
4 namespace esphome {
5 namespace as3935 {
6 
7 static const char *const TAG = "as3935";
8 
10  ESP_LOGCONFIG(TAG, "Setting up AS3935...");
11 
12  this->irq_pin_->setup();
13  LOG_PIN(" IRQ Pin: ", this->irq_pin_);
14 
15  // Write properties to sensor
16  this->write_indoor(this->indoor_);
17  this->write_noise_level(this->noise_level_);
18  this->write_watchdog_threshold(this->watchdog_threshold_);
19  this->write_spike_rejection(this->spike_rejection_);
20  this->write_lightning_threshold(this->lightning_threshold_);
21  this->write_mask_disturber(this->mask_disturber_);
22  this->write_div_ratio(this->div_ratio_);
23  this->write_capacitance(this->capacitance_);
24 
25  // Handle setting up tuning or auto-calibration
26  if (this->tune_antenna_) {
27  ESP_LOGCONFIG(TAG, " Antenna tuning: ENABLED - lightning detection will not function in this mode");
28  this->tune_antenna();
29  } else if (this->calibration_) {
30  this->calibrate_oscillator();
31  }
32 }
33 
34 void AS3935Component::dump_config() {
35  ESP_LOGCONFIG(TAG, "AS3935:");
36  LOG_PIN(" Interrupt Pin: ", this->irq_pin_);
37 #ifdef USE_BINARY_SENSOR
38  LOG_BINARY_SENSOR(" ", "Thunder alert", this->thunder_alert_binary_sensor_);
39 #endif
40 #ifdef USE_SENSOR
41  LOG_SENSOR(" ", "Distance", this->distance_sensor_);
42  LOG_SENSOR(" ", "Lightning energy", this->energy_sensor_);
43 #endif
44 }
45 
46 float AS3935Component::get_setup_priority() const { return setup_priority::DATA; }
47 
48 void AS3935Component::loop() {
49  if (!this->irq_pin_->digital_read())
50  return;
51 
52  uint8_t int_value = this->read_interrupt_register_();
53  if (int_value == NOISE_INT) {
54  ESP_LOGI(TAG, "Noise was detected - try increasing the noise level value!");
55  } else if (int_value == DISTURBER_INT) {
56  ESP_LOGI(TAG, "Disturber was detected - try increasing the spike rejection value!");
57  } else if (int_value == LIGHTNING_INT) {
58  ESP_LOGI(TAG, "Lightning has been detected!");
59 #ifdef USE_BINARY_SENSOR
60  if (this->thunder_alert_binary_sensor_ != nullptr) {
61  this->thunder_alert_binary_sensor_->publish_state(true);
62  this->set_timeout(10, [this]() { this->thunder_alert_binary_sensor_->publish_state(false); });
63  }
64 #endif
65 #ifdef USE_SENSOR
66  uint8_t distance = this->get_distance_to_storm_();
67  if (this->distance_sensor_ != nullptr)
68  this->distance_sensor_->publish_state(distance);
69 
70  uint32_t energy = this->get_lightning_energy_();
71  if (this->energy_sensor_ != nullptr)
72  this->energy_sensor_->publish_state(energy);
73 #endif
74  }
75 }
76 
77 void AS3935Component::write_indoor(bool indoor) {
78  ESP_LOGV(TAG, "Setting indoor to %d", indoor);
79  if (indoor) {
81  } else {
83  }
84 }
85 // REG0x01, bits[3:0], manufacturer default: 0010 (2).
86 // This setting determines the threshold for events that trigger the
87 // IRQ Pin.
88 void AS3935Component::write_watchdog_threshold(uint8_t watchdog_threshold) {
89  ESP_LOGV(TAG, "Setting watchdog sensitivity to %d", watchdog_threshold);
90  if ((watchdog_threshold < 1) || (watchdog_threshold > 10)) // 10 is the max sensitivity setting
91  return;
92  this->write_register(THRESHOLD, THRESH_MASK, watchdog_threshold, 0);
93 }
94 
95 // REG0x01, bits [6:4], manufacturer default: 010 (2).
96 // The noise floor level is compared to a known reference voltage. If this
97 // level is exceeded the chip will issue an interrupt to the IRQ pin,
98 // broadcasting that it can not operate properly due to noise (INT_NH).
99 // Check datasheet for specific noise level tolerances when setting this register.
100 void AS3935Component::write_noise_level(uint8_t noise_level) {
101  ESP_LOGV(TAG, "Setting noise level to %d", noise_level);
102  if ((noise_level < 1) || (noise_level > 7))
103  return;
104 
105  this->write_register(THRESHOLD, NOISE_FLOOR_MASK, noise_level, 4);
106 }
107 // REG0x02, bits [3:0], manufacturer default: 0010 (2).
108 // This setting, like the watchdog threshold, can help determine between false
109 // events and actual lightning. The shape of the spike is analyzed during the
110 // chip's signal validation routine. Increasing this value increases robustness
111 // at the cost of sensitivity to distant events.
112 void AS3935Component::write_spike_rejection(uint8_t spike_rejection) {
113  ESP_LOGV(TAG, "Setting spike rejection to %d", spike_rejection);
114  if ((spike_rejection < 1) || (spike_rejection > 11))
115  return;
116 
117  this->write_register(LIGHTNING_REG, SPIKE_MASK, spike_rejection, 0);
118 }
119 // REG0x02, bits [5:4], manufacturer default: 0 (single lightning strike).
120 // The number of lightning events before IRQ is set high. 15 minutes is The
121 // window of time before the number of detected lightning events is reset.
122 // The number of lightning strikes can be set to 1,5,9, or 16.
123 void AS3935Component::write_lightning_threshold(uint8_t lightning_threshold) {
124  ESP_LOGV(TAG, "Setting lightning threshold to %d", lightning_threshold);
125  switch (lightning_threshold) {
126  case 1:
127  this->write_register(LIGHTNING_REG, ((1 << 5) | (1 << 4)), 0, 4); // Demonstrative
128  break;
129  case 5:
130  this->write_register(LIGHTNING_REG, ((1 << 5) | (1 << 4)), 1, 4);
131  break;
132  case 9:
133  this->write_register(LIGHTNING_REG, ((1 << 5) | (1 << 4)), 1, 5);
134  break;
135  case 16:
136  this->write_register(LIGHTNING_REG, ((1 << 5) | (1 << 4)), 3, 4);
137  break;
138  default:
139  return;
140  }
141 }
142 // REG0x03, bit [5], manufacturer default: 0.
143 // This setting will return whether or not disturbers trigger the IRQ Pin.
144 void AS3935Component::write_mask_disturber(bool enabled) {
145  ESP_LOGV(TAG, "Setting mask disturber to %d", enabled);
146  if (enabled) {
147  this->write_register(INT_MASK_ANT, (1 << 5), 1, 5);
148  } else {
149  this->write_register(INT_MASK_ANT, (1 << 5), 0, 5);
150  }
151 }
152 // REG0x03, bit [7:6], manufacturer default: 0 (16 division ratio).
153 // The antenna is designed to resonate at 500kHz and so can be tuned with the
154 // following setting. The accuracy of the antenna must be within 3.5 percent of
155 // that value for proper signal validation and distance estimation.
156 void AS3935Component::write_div_ratio(uint8_t div_ratio) {
157  ESP_LOGV(TAG, "Setting div ratio to %d", div_ratio);
158  switch (div_ratio) {
159  case 16:
160  this->write_register(INT_MASK_ANT, ((1 << 7) | (1 << 6)), 0, 6);
161  break;
162  case 22:
163  this->write_register(INT_MASK_ANT, ((1 << 7) | (1 << 6)), 1, 6);
164  break;
165  case 64:
166  this->write_register(INT_MASK_ANT, ((1 << 7) | (1 << 6)), 1, 7);
167  break;
168  case 128:
169  this->write_register(INT_MASK_ANT, ((1 << 7) | (1 << 6)), 3, 6);
170  break;
171  default:
172  return;
173  }
174 }
175 // REG0x08, bits [3:0], manufacturer default: 0.
176 // This setting will add capacitance to the series RLC antenna on the product
177 // to help tune its resonance. The datasheet specifies being within 3.5 percent
178 // of 500kHz to get optimal lightning detection and distance sensing.
179 // It's possible to add up to 120pF in steps of 8pF to the antenna.
180 void AS3935Component::write_capacitance(uint8_t capacitance) {
181  ESP_LOGV(TAG, "Setting tune cap to %d pF", capacitance * 8);
182  this->write_register(FREQ_DISP_IRQ, CAP_MASK, capacitance, 0);
183 }
184 
185 // REG0x03, bits [3:0], manufacturer default: 0.
186 // When there is an event that exceeds the watchdog threshold, the register is written
187 // with the type of event. This consists of two messages: INT_D (disturber detected) and
188 // INT_L (Lightning detected). A third interrupt INT_NH (noise level too HIGH)
189 // indicates that the noise level has been exceeded and will persist until the
190 // noise has ended. Events are active HIGH. There is a one second window of time to
191 // read the interrupt register after lightning is detected, and 1.5 after
192 // disturber.
194  // A 2ms delay is added to allow for the memory register to be populated
195  // after the interrupt pin goes HIGH. See "Interrupt Management" in
196  // datasheet.
197  ESP_LOGV(TAG, "Calling read_interrupt_register_");
198  delay(2);
199  return this->read_register_(INT_MASK_ANT, INT_MASK);
200 }
201 
202 // REG0x02, bit [6], manufacturer default: 1.
203 // This register clears the number of lightning strikes that has been read in
204 // the last 15 minute block.
206  // Write high, then low, then high to clear.
207  ESP_LOGV(TAG, "Calling clear_statistics_");
208  this->write_register(LIGHTNING_REG, (1 << 6), 1, 6);
209  this->write_register(LIGHTNING_REG, (1 << 6), 0, 6);
210  this->write_register(LIGHTNING_REG, (1 << 6), 1, 6);
211 }
212 
213 // REG0x07, bit [5:0], manufacturer default: 0.
214 // This register holds the distance to the front of the storm and not the
215 // distance to a lightning strike.
217  ESP_LOGV(TAG, "Calling get_distance_to_storm_");
218  return this->read_register_(DISTANCE, DISTANCE_MASK);
219 }
220 
222  ESP_LOGV(TAG, "Calling get_lightning_energy_");
223  uint32_t pure_light = 0; // Variable for lightning energy which is just a pure number.
224  uint32_t temp = 0;
225  // Temp variable for lightning energy.
227  // Temporary Value is large enough to handle a shift of 16 bits.
228  pure_light = temp << 16;
229  temp = this->read_register(ENERGY_LIGHT_MSB);
230  // Temporary value is large enough to handle a shift of 8 bits.
231  pure_light |= temp << 8;
232  // No shift here, directly OR'ed into pure_light variable.
233  temp = this->read_register(ENERGY_LIGHT_LSB);
234  pure_light |= temp;
235  return pure_light;
236 }
237 
238 // REG0x03, bit [7:6], manufacturer default: 0 (16 division ratio).
239 // This function returns the current division ratio of the resonance frequency.
240 // The antenna resonance frequency should be within 3.5 percent of 500kHz, and
241 // so when modifying the resonance frequency with the internal capacitors
242 // (tuneCap()) it's important to keep in mind that the displayed frequency on
243 // the IRQ pin is divided by this number.
244 uint8_t AS3935Component::read_div_ratio() {
245  ESP_LOGV(TAG, "Calling read_div_ratio");
246  uint8_t reg_val = this->read_register_(INT_MASK_ANT, DIV_MASK);
247  reg_val >>= 6; // Front of the line.
248 
249  if (reg_val == 0) {
250  return 16;
251  } else if (reg_val == 1) {
252  return 32;
253  } else if (reg_val == 2) {
254  return 64;
255  } else if (reg_val == 3) {
256  return 128;
257  }
258  ESP_LOGW(TAG, "Unknown response received for div_ratio");
259  return 0;
260 }
261 
262 uint8_t AS3935Component::read_capacitance() {
263  ESP_LOGV(TAG, "Calling read_capacitance");
264  uint8_t reg_val = this->read_register_(FREQ_DISP_IRQ, CAP_MASK) * 8;
265  return (reg_val);
266 }
267 
268 // REG0x08, bits [5,6,7], manufacturer default: 0.
269 // This will send the frequency of the oscillators to the IRQ pin.
270 // _osc 1, bit[5] = TRCO - System RCO at 32.768kHz
271 // _osc 2, bit[6] = SRCO - Timer RCO Oscillators 1.1MHz
272 // _osc 3, bit[7] = LCO - Frequency of the Antenna
273 void AS3935Component::display_oscillator(bool state, uint8_t osc) {
274  if ((osc < 1) || (osc > 3))
275  return;
276 
277  this->write_register(FREQ_DISP_IRQ, OSC_MASK, state, 4 + osc);
278 }
279 
280 // REG0x3D, bits[7:0]
281 // This function calibrates both internal oscillators The oscillators are tuned
282 // based on the resonance frequency of the antenna and so it should be trimmed
283 // before the calibration is done.
284 bool AS3935Component::calibrate_oscillator() {
285  ESP_LOGI(TAG, "Starting oscillators calibration...");
286  this->write_register(CALIB_RCO, WIPE_ALL, DIRECT_COMMAND, 0); // Send command to calibrate the oscillators
287 
288  this->display_oscillator(true, 2);
289  delay(2); // Give time for the internal oscillators to start up.
290  this->display_oscillator(false, 2);
291 
292  // Check it they were calibrated successfully.
293  uint8_t reg_val_srco = this->read_register_(CALIB_SRCO, CALIB_MASK_NOK);
294  uint8_t reg_val_trco = this->read_register_(CALIB_TRCO, CALIB_MASK_NOK);
295 
296  // reg_val_srco &= CALIB_MASK;
297  // reg_val_srco >>= 6;
298  // reg_val_trco &= CALIB_MASK;
299  // reg_val_trco >>= 6;
300  if (!reg_val_srco && !reg_val_trco) { // Zero upon success
301  ESP_LOGI(TAG, "Calibration was succesful");
302  return true;
303  } else {
304  ESP_LOGW(TAG, "Calibration was NOT succesful");
305  return false;
306  }
307 }
308 
309 void AS3935Component::tune_antenna() {
310  ESP_LOGI(TAG, "Starting antenna tuning...");
311  uint8_t div_ratio = this->read_div_ratio();
312  uint8_t tune_val = this->read_capacitance();
313  ESP_LOGI(TAG, "Division Ratio is set to: %d", div_ratio);
314  ESP_LOGI(TAG, "Internal Capacitor is set to: %d", tune_val);
315  ESP_LOGI(TAG, "Displaying oscillator on INT pin. Measure its frequency - multiply value by Division Ratio");
316  this->display_oscillator(true, ANTFREQ);
317 }
318 
319 uint8_t AS3935Component::read_register_(uint8_t reg, uint8_t mask) {
320  uint8_t value = this->read_register(reg);
321  value &= (~mask);
322  return value;
323 }
324 
325 } // namespace as3935
326 } // namespace esphome
virtual void write_register(uint8_t reg, uint8_t mask, uint8_t bits, uint8_t start_position)=0
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:19
virtual uint8_t read_register(uint8_t reg)=0
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
Definition: component.cpp:69
uint8_t read_register_(uint8_t reg, uint8_t mask)
Definition: as3935.cpp:319
virtual void setup()=0
virtual void setup()
Where the component&#39;s initialization should happen.
Definition: component.cpp:48
virtual bool digital_read()=0
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
bool state
Definition: fan.h:34
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26