I’ve always been fascinated by both lightning and remote sensing. Giving the rather obvious and ubiquitous nature of the former, it provides the perfect opportunity to do a bit of the latter. Lightning detection and the resulting data — while obviously useful for protecting people, pets, and equipment — can also help with things like storm identification, tracking, and intensity quantification.
Various methods can be used to detect lightning. Commonly it involves sensing the electromagnetic radiation generated by a strike. Most people have probably heard this at some point as static, crackles, and popping sounds on an AM radio. Some detectors are capable of picking up the flashes of light produced within a cloud, even during the day when they usually can’t be seen by the human eye due to sunlight. Occasionally lightning will emit a brief pulse of gamma radiation — something a new instrument aboard the International Space Station is set to study.
In this post I’ll explore using a RaspberryPi to interface the AS3935 Franklin Lightning Sensor IC from ams (Austria Mikro Systeme). The AS3935 is a programmable sensor that can detect lightning activity up to 40km away. It uses a proprietary hardwired algorithm to filter out noise and man-made “disturbers” and estimate the distance to the leading edge of the storm; has programmable detection levels, threshold settings, and antenna tuning; and unlike many previous terrestrial lightning sensors can detect both cloud-to-ground and intra-cloud lightning activity.
If you’d rather skip all the boring details and jump straight to the results, scoll down or click here.
Rather than rounding up the specific components in small quantities, laying out a PCB, and trying to hand-solder the rather small (4x4mm) MLPQ-16 package, I used the MOD-1016 breakout board from Embedded Adventures. The AS3935 requires a supply voltage range of 2.4 – 5.5V, which will work perfectly with the RaspberryPi’s 3.3v GPIO logic levels. It can be interfaced through SPI or I2C. By default the MOD-1016 comes configured to use I2C but can easily be switched to use SPI through a couple of solder jumpers on the board. In my setup I’ll be sticking with the default I2C configuration.
This project served as a nice introduction to the I2C protocol for me. I had used the 1-Wire protocol before to interface some DS18B20 temperature sensors, but I2C is much more widely used and so I was glad to have the opportunity to dive into it. In this post I’ll be covering the details necessary to communicate with the AS3935 from the RaspberryPi over I2C, but if you’d like more information, Byte Paradigm provides a wonderful introduction to I2C and SPI.
If you don’t already have your RaspberryPi configured to use the I2C protocol you’ll have to install a couple packages and load a couple kernel modules. Adafruit has a wonderful guide here, but basically you just need to install the python-smbus and i2c-tools packages, and load the i2c-bcm2708 and i2c-dev kernel modules. Doing this on the Raspbian distro might go something like this:
### Installs the packages ~ $ sudo apt-get install python-smbus ~ $ sudo apt-get install i2c-tools ### Loads the kernel modules ~ $ sudo modprobe i2c-bcm2708 ~ $ sudo modprobe i2c-dev ### Make sure modules load on boot ~ $ echo "i2c-bcm2708 i2c-dev" | sudo tee -a /etc/modules
Wiring up the MOD-1016 to the RaspberryPi is relatively simple. With I2C you only need two wires (SDA & SCL) — besides power and ground, of course — to communicate with multiple devices. The AS3935 issues interrupts to alert the microcontroller to events, so we’ll need one additional wire for that. I used a standard breadboard and a RaspberryPi Cobbler from Adafruit to mock-up the circuit. The MOD-1016 (AS3935) connects to the RaspberryPi as such:
|VCC||3.3v (pin 1)|
|IRQ||GPIO 17 (pin 11)|
|SCL||SCL (pin 5)|
|SDA||SDA (pin 3)|
One thing to note about I2C is that it was designed for inter-chip communication, often between integrated-circuits found on the same PCB. In I2C the lines are held high by pull-up resistors and pulled low to indicate the opposite state. The time it takes to bring the line back up to VCC varies depending on the value of the pull-up resistor and the bus capacitance. The I2C specification limits the maximum capacitance to 400pF, which typically restricts the practical distance to within a few meters. There are ways to extend the max length if needed. The most obvious being an I2C bus extender. Slowing down the clock rate can also help increase max distance. Also, be aware that if using twister-pair cable like CAT-5 for I2C connections not to run the SCL and SDA lines together over the same twisted-pair. Use separate pairs and tie the other line in each pair to ground.
Once everything is wired up you can use the command i2cdetect to see whether I2C is working and can communicate with the sensor. If using an older RaspberryPi (with 256MB ram) you will run the command as shown below with an I2C bus ID of 0. If using a newer RaspberryPi though use 1 instead. In the output below you can see the AS3935 (0x03) and the MS5611 (0x76) has been properly detected.
~ $ sudo i2cdetect -y 0 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: 03 -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- 76 --
Now to actually use the sensor we’re going to need to be able to interact with it. This is done by reading and writing to registers within the chip. Registers are memory locations used to store bits for things like configuration and input/output. In a way they are like digital DIP switches, where bits or series of bits are used to indicate or set various states. For example, with the AS3935 it is possible to clear the statistics built up by the algorithm by toggling the 7th bit (bit 6) of the 3rd register (0x02). Tables and mappings describing the register locations and functionality can be found in a device’s datasheet. For example, the register table from the AS3935 datasheet is shown below.
Most languages and platforms have tools or libraries available for working with I2C. On Linux for instance, the i2c-tools package provides utilities like i2cdetect, i2cget, i2cdump,and i2cset that can be used from the command line. For python, the SMBus module provided by the python-smbus package offers the bindings needed for accessing the I2C bus. Often though there are higher-level libraries available for devices that abstract away the details of working with individual registers. Instead of having to know which bits to read or write to which registers you can often just instantiate a class and call methods to interact with the particular device.
For working with the the AS3935 in python, the RaspberryPi-AS3935 library written by Phil Fenstermacher is available. Installation instructions can be found on the GitHub page. It provides useful methods as well as a nice demo script to get you up and running. To see available methods and their arguments take a look through the RPi_AS3935.py file.
The AS3935 uses a parallel RLC circuit as an antenna and needs to be tuned to a resonant frequency of 500kHz ±3.5%. To compensate for variances there is up to 120pF available internally though tuning capacitors that can be activated in steps of 8pF. Through a register setting the AS3935 can output the resonant frequency on the IRQ pin allowing an external device to measure it and tune the antenna appropriately by activating the necessary tuning capacitors. Luckily the MOD-1016 breakouts from Embedded Adventures comes with the tuning capacitor value displayed on the outside of the anti-static bag. This makes the calibration routine much simpler. When using the above library it is as simple as calling the calibrate() method and passing the tuning capacitor value as an argument. More details on the antenna design can be found in the AS3935 AMS Hardware Design Guide.
The signal from the antenna then feeds into the AS3935 IC to be amplified and demodulated by the the analog front-end (AFE). By default the AFE gain is optimized for an indoor environment. If placing the sensor outdoors you will want to toggle AFE Gain Boost setting to the outdoor value. Another important setting is the noise floor threshold. When the noise floor level crosses the noise floor threshold an interrupt is issued which allows the external device to then raise the threshold. The RaspberryPi-AS3935 library includes methods for easily changing both of these settings.
When an event is detect, the AS3935 notifies the host device by pulling the IRQ pin high momentarily. Handling these interrupts in Python on the RaspberryPi involves registering a callback function that is ran whenever it detects the rising edge of the IRQ pin being pulled high. According to the datasheet you must wait 2 milliseconds after an interrupt is issued before reading register 0x03, bits 3-0 to determine what the event was. The value will indicate one of four types of events.
|0000||Distance estimation has changed|
|0001||Noise level too high|
Although the algorithm used by the AS3935 to qualify and quantify events is proprietary and hardwired, there are some settings available to tweak the process. The first of which involves the “watchdog”. This is a circuit that monitors the output of the AFE and sends the signal onto “signal validation” once it crosses a certain threshold. The watchdog threshold can be adjusted through register 0x01, bits 3-0. Signal validation is the first step of the algorithm and is where “disturbers” are sorted from real lightning. Register 0x02, bits 3-0 allows you to adjust the spike rejection threshold. (At the time of this writing the RaspberryPi-AS3935 library is lacking methods for adjusting the watchdog and spike rejection thresholds. However it shouldn’t be too difficult to add them.) Both of these settings can help cut down on false-positives, however improved disturber rejection comes at the expense of detection efficiency. The graphs below from the datasheet demonstrate this.
By default an interrupt will be issued each time a disturber is detected. This can be useful initially to see that the sensor is working, but in practical use these events tend to clutter up things. The AS3935 has the ability to mask disturbers by setting register 0x03, bit 5 to 1. This will prevent interrupts from being issued for any additional disturbers. Another setting that may be useful for eliminating false positives or controlling output volume is the “minimum number of lighting events” threshold. This will cause an interrupt to be issued only if this minimum number of strikes is detected within a 15 minute interval. The default is 1 strike, but you can raise that to 5, 9, or 16 with register 0x02, bits 5-4.
If an event passes signal validation as a legitimate lightning strike, then calculations are made to estimate the energy of the event and stored along with timing data which then allows the AS3935 to make a statistical distance estimation to the head of the storm from 0 to 40km in 14 steps. The distance is represented directly in register 0x07, bits 5-0 as kilometers (binary encoded). 0x3F (111111) means an event was out of range. 0x01 (000001) means the storm is estimated to be directly overhead.
For the most part I was able to get decent results using the default settings and the demo script adjusted for the particular tune cap value. I was having an issue though where it would seem to go completely deaf and fail to register even nearby lightning strikes. After a few weeks of replacing and testing everything from the power supply to the wiring, I finally determined the issue was probably software related. Adding a few additional milliseconds wait before and after calling the calibration routine — making sure the internal RC oscillators have time to stabilize — seemed to fix the problem. Some online have suggested re-running the calibration routine after any changes to the settings.
I also have occasional false-positives where a lightning strike or two is detected on a perfectly clear day. Adjusting the watchdog and/or spike rejection thresholds should take care of these. Adding the methods to adjust these thresholds to the RaspberryPi-RS3935 library and rewriting the monitoring script will be my next steps. I would also like to put together an API to collect this and other real-time sensor data and display it online. Currently I just have a text log being uploaded via FTP every 15 minutes.
I decided to use a RaspberryPi for this project rather than something like an Arduino or another microcontroller for a couple of reasons. An obvious one being I had a couple of them lying around. The main reason though was that I wanted this to be a web-connected platform so that I could capture and display events remotely. I also wanted it to be able to sit outside and connect wirelessly. For $45 ($35 RaspberryPi + $10 USB WiFi adapter) it’s hard to beat.
To be able to collect other outdoor sensor readings I put together a simple weather-resistant project box using a PVC enclosure, outlet box, gaskets, and weather-proof outlet covers. I ended up using an iPad charger for the power supply and Limefuel 6000mAh battery pack as UPS for the RaspberryPi. A DS18B20 temperature sensor was used to first get an idea of ambient box temps, and later to monitor the temperature of the battery pack.
I’ll update this post or link to updates as I come across additional information or find new ways to use the AS3935 lightning sensor. Something I would like to explorer down the road is the possibility of using multiple devices spaced >1km apart to triangulate thunderstorms.
Feel free to ask questions in the comments below and I’ll do my best to answer them.