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