Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Electronza/293092bbfd0fed651f2275512009d575 to your computer and use it in GitHub Desktop.
Save Electronza/293092bbfd0fed651f2275512009d575 to your computer and use it in GitHub Desktop.
AS3935: Arduino lightning datalogger
#include <SPI.h>
#include <SD.h>
// set up variables using the SD utility library functions:
Sd2Card card;
SdVolume volume;
SdFile root;
const int SD_CS = 8;
// AS3935 connections
// INT is pin 2
// CS is pin 10
// SCK is pin 13
// MISO is pin 12
// MOSI is pin 11
#define Thunder_CS 10
#define Thunder_INT 2
#define LED_PIN 5
// Initial noise, spike rejection and watchdog levels
// all set to middle of the range
// Noise level is 3 bits (0-7)
// default is 2, see page 12 of datasheet
unsigned short noise_lvl = 2;
// Watchdog level is 4 bits (0-15)
// default is 1, see page 12 of datasheet
unsigned short wdt_lvl = 1;
// Spike rejection level is 4 bits (0-15)
// default is 2, see page 12 of datasheet
unsigned short srej_lvl = 2; //
// Number of lightnings is 3 bits (0-7)
// Set to 1 lightning, see page 22 of datasheet
unsigned short min_num_ligh = 0;
// Indoor or outdoor. See page 19 of datasheet
#define indoor 0x24
#define outdoor 0x1C
// Used by interrupt routine
unsigned short AS3935_INT_Triggered = 0;
// A temporary variable
unsigned short tmp;
// This stores energy of lightning events
unsigned long L_energy;
unsigned long en_tmp;
// Used for timekeeping
unsigned long current_millis;
void setup() {
// Used by the debugging LED
pinMode(LED_PIN, OUTPUT);
// put your setup code here, to run once:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
// SPI: first begin, then set parameters
SPI.begin();
Serial.print("Initializing SD card...");
// see if the card is present and can be initialized:
if (!SD.begin(SD_CS)) {
Serial.println("Card failed, or not present");
// don't do anything more:
while(1);
}
// Flash the led if card is initialized
digitalWrite(LED_PIN,HIGH);
delay(500);
digitalWrite(LED_PIN,LOW);
Serial.println("card initialized.");
// Load AS3935 defaults
// This is done by writing 0x96 in registers 0x3C and 0x3D
// See page 16 of datasheet
Thunder_Write(0x3C, tmp);
delay(2);
Thunder_Write(0x3D, tmp);
delay(2);
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
digitalWrite(LED_PIN,HIGH); //LED will stay on while we access the SD card
File dataFile = SD.open("messages.txt", FILE_WRITE);
// Dump AS3935 registers and print their content
Serial.println("----------------------------------------------------------------");
Serial.println("Intitial register dump");
dataFile.println("----------------------------------------------------------------");
dataFile.println("Intitial register dump");
for (int myregister = 0; myregister < 9 ; myregister++) {
tmp = Thunder_Read (myregister);
Serial.print("Register: ");
Serial.print(myregister,HEX);
Serial.print(" value is ");
Serial.print(tmp,HEX);
Serial.print(" in binary ");
Serial.println(tmp,BIN);
dataFile.print("Register: ");
dataFile.print(myregister,HEX);
dataFile.print(" value is ");
dataFile.print(tmp,HEX);
dataFile.print(" in binary ");
dataFile.println(tmp,BIN);
}
Serial.println(" ");
dataFile.println(" ");
dataFile.close();
// Calibrate the sensor
// Set INT pin as input
pinMode(Thunder_INT, INPUT);
Thunder_Calibrate();
// Now we set out AS39354 sensor
// Setting noise level and watchdog (they are in register 0x01);
tmp = wdt_lvl | (noise_lvl << 4);
Thunder_Write(0x01, tmp);
// Setting spike rejection, minimum number of lightnings and clear stats
// These are in register 0x02
tmp = srej_lvl | (min_num_ligh << 4);
Thunder_Write(0x02, tmp);
// Setting to indoor mode
tmp = Thunder_Read (0x00);
tmp = tmp & 0xC1; // mask AFE Setting bits
tmp = tmp | indoor;
Thunder_Write(0x00, tmp);
// Dump AS3935 registers after settings
File dataFile1 = SD.open("messages.txt", FILE_WRITE);
Serial.println("----------------------------------------------------------------");
Serial.println("Final register dump");
dataFile1.println("----------------------------------------------------------------");
dataFile1.println("Final register dump");
for (int myregister = 0; myregister < 9 ; myregister++) {
tmp = Thunder_Read (myregister);
Serial.print("Register: ");
Serial.print(myregister,HEX);
Serial.print(" value is ");
Serial.print(tmp,HEX);
Serial.print(" in binary ");
Serial.println(tmp,BIN);
dataFile1.print("Register: ");
dataFile1.print(myregister,HEX);
dataFile1.print(" value is ");
dataFile1.print(tmp,HEX);
dataFile1.print(" in binary ");
dataFile1.println(tmp,BIN);
}
Serial.println("----------------------------------------------------------------");
Serial.println(" ");
Serial.println("Lightning detection started");
Serial.println(" ");
dataFile1.println("----------------------------------------------------------------");
dataFile1.println(" ");
dataFile1.println("Lightning detection started");
dataFile1.println(" ");
dataFile1.close();
digitalWrite(LED_PIN,LOW);
attachInterrupt(0, AS3935_IRQ, RISING);
tmp = digitalRead(0x03); // dummy read, clears any existing interrupts
} // end of void setup() part of the Arduino code
void loop() {
// put your main code here, to run repeatedly:
// If Thunder_INT pin is high then we have an event
if(AS3935_INT_Triggered) {
current_millis = millis(); // Record timestamp
AS3935_INT_Triggered = 0; // Clear interrupt flag
delay(2); // wait 2ms before reading the event (see datasheet page 22)
tmp = Thunder_Read(0x03); // Read event register. This also clears Thunder_INT
tmp = tmp & 0x0F; // Mask interrupt bits
String logString = "";
logString += String(current_millis);
logString += ',';
if (tmp == 0x00){ // Statistics changed
logString += String("New statistics,0,NaN,NaN");
}
if (tmp == 0x01){ // Noise
logString += String("Noise detected,1,NaN,NaN");
}
if (tmp == 0x04){ // Man-made disturber
logString += String("Disturber detected,2,NaN,NaN");
}
if (tmp == 0x08){ // Lightning
logString += String("Lightning detected,3,");
// Get distance to the lightning event
// this means bits [5:0] of register 0x07
tmp = Thunder_Read(0x07);
tmp = tmp & 0x3F; // mask bits 7:6
logString += String(tmp); // add distance to the string
logString += String(","); // separator for csv format
// Get energy of lightning event
// this means bits [4:0] of register 0x06
// and content of registers 0x04 and 0x05
en_tmp = Thunder_Read(0x06);
en_tmp = en_tmp & 0x3F; // mask bits 7:6
L_energy = L_energy | ( en_tmp << 16 );
en_tmp = Thunder_Read(0x05);
L_energy = L_energy | ( en_tmp << 8 );
en_tmp = Thunder_Read(0x04);
L_energy = L_energy | en_tmp;
logString += String(L_energy);
}
// write data to sd card
File dataFile3 = SD.open("datalog.csv", FILE_WRITE);
if (dataFile3) {
dataFile3.println(logString);
dataFile3.close();
// print to the serial port too:
Serial.println(logString);
}
}
}
/*******************************************************************************
* Function Thunder_Write(unsigned short address, unsigned short data1)
* ------------------------------------------------------------------------------
* Overview: Function writes desired byte into specified register address
* Input: register address, byte
* Output: Nothing
*******************************************************************************/
unsigned short Thunder_Write(unsigned short reg_address, unsigned short reg_value) {
reg_address = reg_address & 0x3F; // mask bits 6 and 7 (write)
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE1));
digitalWrite(Thunder_CS, 0);
SPI.transfer(reg_address);
SPI.transfer(reg_value);
digitalWrite(Thunder_CS, 1);
SPI.endTransaction();
}
/*******************************************************************************
* Function Thunder_Read(unsigned short address)
* ------------------------------------------------------------------------------
* Overview: Function reads byte from specified address
* Input: register address
* Output: desired byte
*******************************************************************************/
unsigned short Thunder_Read(unsigned short reg_address) {
// Set SPI to work with AS3935
unsigned short tmp;
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE1));
reg_address = reg_address & 0x3F; // mask bits 6 and 7
reg_address = reg_address | 0x40; // set bit 6 (read cycle)
digitalWrite(Thunder_CS, 0);
SPI.transfer(reg_address);
tmp = SPI.transfer(0);
digitalWrite(Thunder_CS, 1);
SPI.endTransaction();
return tmp;
}
/*******************************************************************************
* Function Thunder_Calibrate(calibrate)
* ------------------------------------------------------------------------------
* Overview: Function calibrates the LCO
* Input: nothing
* Output: true if calibration is sucessful
*******************************************************************************/
void Thunder_Calibrate(void)
{
File dataFile2 = SD.open("messages.txt", FILE_WRITE); // dump data on SD card
// Internal variables
int target = 3125, currentcount = 0, bestdiff = 32767, currdiff = 0;
byte bestTune = 0, currTune = 0;
unsigned long setUpTime;
int currIrq, prevIrq;
unsigned short tmp;
// set lco_fdiv divider to 0, which translates to 16
// so we are looking for 31250Hz on irq pin
// and since we are counting for 100ms that translates to number 3125
// each capacitor changes second least significant digit
// using this timing so this is probably the best way to go
// LCO_FDIV is bits [7:6] of register 0x03
// A value of 00 signifies division by 16
// 500000 / 16 = 31250
tmp = Thunder_Read (0x03); // read register 0x03
tmp = tmp & 0x3F; // set bits 7:6 to 0
Thunder_Write(0x03, tmp); // write it back
// Now we output the signal on the INT pin
// This is done by setting bit7 (DISP_LCO) in register 0x08
tmp = Thunder_Read (0x08); // read register 0x08
tmp = tmp & 0x0F; // clear bits 7:4
tmp = tmp | 0x80; // set bit 7
Thunder_Write(0x08, tmp); // write it back
// At this point we should have the frequency of 31250Hz on INT pin
// Let's fine tune it
// We check frequency output for each value of tuning capacitor
for (currTune = 0; currTune <= 0x0F ; currTune++)
{
// Write the tuning capacitor value
tmp = Thunder_Read (0x08); // read register 0x08
tmp = tmp & 0xF0; // clear bits 3:0
tmp = tmp | currTune; // set bit 7
Thunder_Write(0x08, tmp); // write it back
// wait to settle
delay(10);
currentcount = 0;
prevIrq = digitalRead(Thunder_INT);
setUpTime = millis() + 100;
while((long)(millis() - setUpTime) < 0)
{
currIrq = digitalRead(Thunder_INT);
if (currIrq > prevIrq)
{
currentcount++;
}
prevIrq = currIrq;
}
currdiff = target - currentcount;
// don't look at me, abs() misbehaves
if(currdiff < 0)
currdiff = -currdiff;
if(bestdiff > currdiff)
{
bestdiff = currdiff;
bestTune = currTune;
}
// uncomment for debugging
dataFile2.print("Current tuning capacitor is ");
dataFile2.print(currTune);
dataFile2.print(" best value is ");
dataFile2.println(bestTune);
}
// Write the best tuning capacitor value
// and turn off LCO
Thunder_Write(0x08, bestTune);
// Power-up
tmp = Thunder_Read (0x00); // read register 0x00
tmp = tmp & 0xFE; // clear bit 0
Thunder_Write(0x00, tmp); // write it back
// if error is over 109, we are outside allowed tuning range of +/-3.5%
if (bestdiff > 109)
{
dataFile2.println("Calibration failed");
// blocking
while(1);
}
else
{
dataFile2.println("Calibration successfull");
}
dataFile2.close();
}
/*******************************************************************************
* ISR routine
* ------------------------------------------------------------------------------
* This sets the AS3935_INT_Triggered flag
*******************************************************************************/
void AS3935_IRQ()
{
AS3935_INT_Triggered = 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment