Created
May 6, 2020 06:22
-
-
Save Electronza/293092bbfd0fed651f2275512009d575 to your computer and use it in GitHub Desktop.
AS3935: Arduino lightning datalogger
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Project page | |
# |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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