Earlier, we had covered setting up an AS3935 lightning detector module. This detector picks up radio emissions, then analyzes them to determine if they are a lightning strike or some other radio source. After collecting some data, it outputs the estimated distance to the incoming storm front.
But that only gets you halfway there. The device detects many non-lightning events, and the bare circuit board is lacking in pizzazz. Today I fix that by digging into the detector’s datasheet, and taking a quick trip to the dollar store buy a suitable housing. The result? A plastic plant that dances when it’s going to rain!
In the last article, I had covered detecting events from the device and then reading back the type of event detected from the device memory. However, we had received a whole lot of type ‘4’ (0100) events output to the terminal, indicating the detection of a lot of non-lightning events. These are called ‘disturbers’ and are radio signals that the chip detects but does not consider lighting. By default it raises the interrupt pin high for these and transmits them anyway.
Our first step will be to filter these out so that we have one less thing to worry about. We could just ignore them, but that’s inelegant – it would be better if there was no data sent at all for these as we’re not interested in them at this time. Thankfully, the datasheet for the AS3935 (PDF) describes a way to do exactly this: bit 5 of memory register 0x03 (MASK_DIST) controls whether the chip transmits disturber events. We can do that by setting that memory address to 0x20 (decimal 32, binary 00100000) before polling the interrupt pin:
pin = 8 light = 2 spi.setup(1, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, 8, 256); gpio.mode(pin, gpio.OUTPUT) gpio.mode(light, gpio.INPUT) function reason() gpio.write(pin, gpio.HIGH) tmr.delay(30) gpio.write(pin, gpio.LOW) tmr.delay(30) spi.send(1, 0x43) tmr.delay(30) read = spi.recv(1, 8) print(string.byte(read)) gpio.write(pin, gpio.HIGH) tmr.delay(30) gpio.write(pin, gpio.LOW) tmr.delay(30) gpio.write(pin, gpio.HIGH) tmr.delay(30) end spi.send(1, 0x03) tmr.delay(2) spi.send(1, 0x20) tmr.alarm(1, 100, 1, function() poll(0) end) function poll() x = gpio.read(light) if x == 1 then reason() end end
When we reset the device, we no longer see any disturber events. Now we see nothing unless I touch the antenna, at which point we read decimal 33 (binary 100001). The MSB (most significant bit) is just the MASK_DIST register that we wrote to, so we can just subtract 32 from whatever result we get, resulting the in the interrupt code. Having done that, the device will output 1 if the interrupt reason is an excessive noise level, and 8 if it is a lightning event.
This is where something a little embarrassing happened. I was sitting in a café at this stage, showing the device to a friend, and it started recording lightning events. It’s dry season in Vietnam right now and it was a clear day, so I explained I had a few bugs to iron out, then got on my motorbike and took the highway home. I proceeded to get caught in the first storm of the year, it was too violent to drive, and spent half an hour hiding under a small bush laughing at myself. Success!?
Returning to the device, we will ignore high noise level events. For a commercial device you would want to let the user know that the noise level is too high, but that’s not interesting to us right now. What would be fun to know is how far away a storm is. The chip needs to collect a little data to be able to estimate stormfront distance, so we’re going to tell the chip not to alert us unless it detects a minimum of lightning strikes within 15 minutes. Bits 4 and 5 of memory register 0x02 control that feature according to the following table:
The default value of the entire 8-bit memory register is 11000010, which we will change to 11010010 (0xD2). This sets a minimum of 5 detected lightning strikes per minute before the chip will report lightning. After the 5th strike, it will report every lightning strike by raising the interrupt pin high:
spi.send(1,0x02) tmr.delay(2) spi.send(1,0xD2)
Once it detects 5 lightning strikes, we’ll want the device to output the distance to the storm as it approaches. To do that, we just read register 0x07 and clear bits 6 and 7 in the result. While I’ve only ever seen those bits set to 0, they are reserved by the device and we are not interested in their state. To finish up, we check the event types and distances produced and make the output a little more human-readable. Our final(ish) code is here.
This works and is fine, but it’s not fun enough yet. You know those little plastic toys that dance when a small solar panel is exposed to sunlight? Well, it turns out that the 3.3v output from a NodeMCU is a very effective surrogate to their amorphous silicon solar panel. A quick pulse from one of the output pins converts a device that dances when exposed to sunlight, to one that dances in the rain.
The only modification needed to the code was to raise a pin high for a short while on lightning detection events, connected to the solar panel positive output (NodeMCU ground to the negative output):
gpio.write(quiver, gpio.HIGH) tmr.alarm(2, 4777, 1, function() dequiver(0) end)
I considered a few models of toy for this part. Monkeys and chickens both sound good with the prefix ‘thunder’. Finally, I settled on a plastic plant because there’s something somehow nightmarish about a plant that quivers in anticipation of rain.
My frivolous use of parts aside, these detectors would be a great addition to many projects like an automatically deploying lightning rod, or a lamp that graphically displays the current weather.