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/e2c10fda6d1edcf66d65cfe3b4e5a4ae to your computer and use it in GitHub Desktop.
Save Electronza/e2c10fda6d1edcf66d65cfe3b4e5a4ae to your computer and use it in GitHub Desktop.
Lightning detection using AS3935 and PIC18F4520
# Project page
# https://medium.com/electronza/lightning-detection-using-as3935-and-pic18f4520-microcontroller-695bbfb9dfc5
/*
* Project name:
Thunder_Test (Demonstration of the AS3935 Thunder Click)
* Revision History:
20150416:
- alpha release;
* Description:
This code demonstrates how to use AS3935 Thunder Click
using a PICDEM 2 Plus board w/ PIC18F4520 microcontroller.
* Test configuration:
MCU: PIC18F4520
http://ww1.microchip.com/downloads/en/DeviceDoc/39631E.pdf
dev.board: PICDEM 2 Plus
http://ww1.microchip.com/downloads/en/DeviceDoc/41584B.pdf
Oscillator: External 4MHz canned oscillator
Ext. Modules: Character Lcd 2x16
Thunder Click board from Mikroelektronika
SW: mikroC PRO for PIC
http://www.mikroe.com/mikroc/pic/
* NOTES:
- On PICDEM 2 Plus the LCD is enabled by setting pin D7 high
- THE LCD R/W pin, which is connected to pin D5, must be set low
*/
// Lcd module connections
sbit LCD_D4 at LATD0_bit;
sbit LCD_D5 at LATD1_bit;
sbit LCD_D6 at LATD2_bit;
sbit LCD_D7 at LATD3_bit;
sbit LCD_RS at LATD4_bit;
sbit LCD_RW at LATD5_bit;
sbit LCD_EN at LATD6_bit;
sbit LCD_PW at LATD7_bit;
sbit LCD_D4_Direction at TRISD0_bit;
sbit LCD_D5_Direction at TRISD1_bit;
sbit LCD_D6_Direction at TRISD2_bit;
sbit LCD_D7_Direction at TRISD3_bit;
sbit LCD_RS_Direction at TRISD4_bit;
sbit LCD_RW_Direction at TRISD5_bit;
sbit LCD_EN_Direction at TRISD6_bit;
sbit LCD_PW_Direction at TRISD7_bit;
// End LCD module connections
#define INDOOR 0x12
#define OUTDOOR 0x0E
//long int value;
//short int value1;
unsigned short int tmp1;
long int tmp2;
int message_count = 0;
char msg_str[4];
char Out_Text[11];
int Total_Lightings_Detected = 0;
unsigned short interrupt_detected = 0;
unsigned short interrupt_source, buffer;
char i;
// SPI module conections
sbit Thunder_CS at LATC2_bit;
sbit Thunder_CS_Direction at TRISC2_bit;
// Interrupt pin
// sbit Thunder_INT at LATB0_bit;
sbit Thunder_INT_Direction at TRISB0_bit;
// Now we set the SPI communication
// NB! chip uses SPI MODE1
// SPI.setDataMode(SPI_MODE1);
// NB! max SPI clock speed that chip supports is 2MHz,
// but never use 500kHz, because that will cause interference
// to lightning detection circuit
/*******************************************************************************
* Interrupt External INT1
*******************************************************************************/
void interrupt(){ // Interrupt rutine
if(INT0IF_bit == 1 ) { // Checks Receive Interrupt Flag bit
interrupt_detected = 1; // Set local interrupt flag
INT0IF_bit = 0; // Clear Interrupt Flag
}
}
// Those are the same functions from the PIC32 example from Mikroe
/*******************************************************************************
* Function Thunder_Write(unsigned short address, unsigned short data1)
* ------------------------------------------------------------------------------
* Overview: Function writes desired byte into specified register address
* Input: register address, byte
* Output: Nothing
*******************************************************************************/
void Thunder_Write(unsigned short address, unsigned short data1) {
address.B7 = 0;
address.B6 = 0;
Thunder_CS = 0;
SPI_Write(address);
SPI_Write(data1);
Thunder_CS = 1;
}
/*******************************************************************************
* 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 address) {
unsigned short tmp = 0;
address.B7 = 0;
address.B6 = 1;
Thunder_CS = 0;
SPI_Write(address);
tmp = SPI_Read(buffer);
Thunder_CS = 1;
return tmp;
}
/*******************************************************************************
* Function Thunder_Init()
* ------------------------------------------------------------------------------
* Overview: Function Initializes Thunder chip
* Input: register address, data
* Output: Nothing
*******************************************************************************/
void Thunder_Init(void) {
unsigned short temp;
Thunder_CS_Direction = 0; // Set CS directions as output
Thunder_CS = 1; // Set CS to idle
Thunder_Write(0x3C, 0x96); // set all registers in default mode
Delay_ms(2);
Thunder_Write(0x3D, 0x96); // calibrate internal oscillator
Delay_ms(2);
temp = Thunder_Read(0x00) & 0xC1;
Thunder_Write(0x00, ((INDOOR << 1) | temp)); // set to indoor
Delay_ms(2);
temp = Thunder_Read(0x01) & 0x80;
Thunder_Write(0x01, 0x21 | temp); // set NFL and WDTreshold (was 44)
Delay_ms(2);
temp = Thunder_Read(0x02) & 0x80; // clear statistics, min number of ligtning, spike rejection
Thunder_Write(0x02, 0x42 | temp);
}
/*******************************************************************************
* Function Thunder_Calibrate()
* ------------------------------------------------------------------------------
* Overview: Function aclibrates Thunder chip
* Input: Nothing
* Output: returns 1 if calibrated, 0 otherwise
*******************************************************************************/
void Thunder_Calibrate(void) {
// The ideea is to output the LCO frequency on IRQ pin
// It should be as close as possible to 500KHz (within +/- 3.5%)
// Otherwise sensitivity is reduced
// declaration of some variables to be used within the function
unsigned short functmp;
long int functmp1;
long int bestcal;
long int current_cal;
unsigned short bestcap;
// needed for debug purposes (LCD output of calibration information)
char cal_str[4];
char cal_str1[8];
// Frequency Division Ratio for the Antenna Tuning is 1:16
// this means we have to set REG0x03[7] = 0, REG0x03[6] = 0
// see page 23 of AS3935 datasheet
// To do this we first read the actual value in REG0x00
// bitmask REG0x03[7] = 0, REG0x03[6] = 0
// then write back values
functmp = Thunder_Read(0x03) & 0x3F;
Thunder_Write(0x03, functmp);
Delay_ms(2);
// Here we start
Lcd_Cmd(_LCD_CLEAR); // Clear display
Lcd_Out(1,1, "Calibrating...");
Delay_ms(500); // enough time to see the message
// For each value of the calibration capacitors we measure the frequency on INT pin
// over 100ms period of time. The closest to 3125, the better.
bestcal = 110; // we set a value out of range
for(i=0; i<16; i++) { // We swa
functmp = 0x80 + i;
Thunder_Write(0x08, functmp);
Delay_ms(50); // wait to stabilize
// We print the capacitor value on lcd
Lcd_Cmd(_LCD_CLEAR); // Clear display
Lcd_Out(1,1, "C = ");
IntToStr(i, cal_str) ;
Lcd_Out(1,4, cal_str);
// Now we measure the frequency
functmp1 = 0; // we use functmp1 to count the pulses
// We set Timer1 to measure for 100ms
// see http://www.libstock.com/projects/view/398/timer-calculator
// Expected pulse count value is 3125 (500000 HZ: 16 = 31250 pulses/sec)
INTCON.GIE = 1; // enable interrupts
INTCON.INT0IE = 1; // enable INT1 interrupt
T1CON = 0x11; // 1;2 prescaler
T1CON.T1RUN = 1;
TMR1H = 0x3C;
TMR1L = 0xB0;
TMR1IF_bit = 0; // interrupt cleared, timer started
while(TMR1IF_bit == 0) {
if (interrupt_detected ==1) {
functmp1++;
interrupt_detected = 0;
}
}
INTCON.GIE = 0; // disable interrupts de ce am facut asta???
// Nice print of frequency
Lcd_Out(2,1, "f = ");
IntToStr(functmp1, cal_str1) ;
Lcd_Out(2,4, cal_str1);
delay_ms(100); // time to see what is on the LCD
current_cal = abs (functmp1 - 3125);
if (current_cal < bestcal) {
bestcal = current_cal;
bestcap = i;
}
}
// write best value in the capacitor register
// we also stop the LCO frequency output and go to normal mode
Thunder_Write(0x08, bestcap);
// exit flag to stop the program if AS3935 if it's not calibrated
if (bestcal > 109) { // error greater than 3.5%
Lcd_Cmd(_LCD_CLEAR); // Clear display
Lcd_Out(1,1, "Calibration");
Lcd_Out(2,1, "error...");
while(1){
// stop here if not calibrated
};
}
else {
Lcd_Cmd(_LCD_CLEAR); // Clear display
Lcd_Out(1,1, "Calibrated");
ShortToStr(bestcap, cal_str);
Lcd_Out(2,1, "C = ");
Lcd_Out(2,4, cal_str);
delay_ms(500);
Lcd_Cmd(_LCD_CLEAR); // Clear display
Thunder_Write(0x03, 0x00);
delay_ms(2) ;
bestcap = Thunder_Read(0x03);
delay_ms(2) ;
}
}
/*******************************************************************************
* Function Thunder_Read_Energy()
* ------------------------------------------------------------------------------
* Overview: Function reads energy of detected thunder
* Input: Nothing
* Output: Measured result
*******************************************************************************/
long int Thunder_Read_Energy() {
unsigned short low_byte, mid_byte;
long int Out_thunder_energy;
Out_thunder_energy = Thunder_Read(0x06) & 0x1F;
mid_byte = Thunder_Read(0x05);
low_byte = Thunder_Read(0x04);
Out_thunder_energy = (Out_thunder_energy << 8);
Out_thunder_energy = (Out_thunder_energy | mid_byte);
Out_thunder_energy = (Out_thunder_energy << 8);
Out_thunder_energy = (Out_thunder_energy | low_byte);
return Out_thunder_energy;
}
/*******************************************************************************
* Function Thunder_Read_distance()
* ------------------------------------------------------------------------------
* Overview: Function reads distance from detected thunder
* Input: Nothing
* Output: Measured result
*******************************************************************************/
unsigned int Thunder_Read_distance() {
int Out_thunder_distance;
Out_thunder_distance = Thunder_Read(0x07) & 0x3F;
return Out_thunder_distance;
}
void main(){
Thunder_INT_Direction = 1; // B0 is input
// LCD inintialize
LCD_PW_Direction = 0;
LCD_RW_Direction = 0;
LCD_RW = 0;
LCD_PW = 1;
Delay_ms(200);
// We must init the LCD before performing AS3935 calibrartion as the
// calibration routine uses LCD.
Lcd_Init();
Lcd_Cmd(_LCD_CLEAR); // Clear display
Lcd_Cmd(_LCD_CURSOR_OFF); // Cursor off
// LCD is now ready
// Initializing SPI communication
SPI1_Init_Advanced(_SPI_MASTER_OSC_DIV4, // Initialize PIC as master
_SPI_DATA_SAMPLE_END, // Data sampled at end
_SPI_CLK_IDLE_LOW,
_SPI_HIGH_2_LOW);
// SPI Clock is 1MHz, SPI MODE 1
// Maximum SPI clock for AS3935 is 2MHz. Avoid using 500KHz clock as it
// will intefere with the lightning detection circuits
Thunder_Init();
Thunder_Calibrate ();
// Power up the sensor
// not needed anymore, sensor is powered up by the Thunder_init() routine
// Thanks Exploit for pointing it out, see comments below
// tmp1 = Thunder_Read(0x00) & 0xFE;
// Thunder_Write(0x00, tmp1);
// Delay_ms(2);
// Now we show some working parameters
// Disturbers enabled, or not?
tmp1 = Thunder_Read(0x00) & 0x20;
Lcd_Cmd(_LCD_CLEAR);
//Delay_ms(10);
Lcd_Out(1,1, "Disturbers");
//ShortToStr(tmp1, msg_str);
if (tmp1 == 0x20)
Lcd_Out(2,1, "enabled");
else
Lcd_Out(2,1, "disabled");
delay_ms(1000);
//Are we in indoor, or outdoor mode
tmp1 = Thunder_Read(0x00) & 0x3E;
Lcd_Cmd(_LCD_CLEAR);
Lcd_Out(1,1, "Sensor is set to");
tmp1 = tmp1>>1;
if (tmp1 == INDOOR)
Lcd_Out(2,1, "indoor mode");
else
Lcd_Out(2,1, "outdoor mode");
delay_ms(1000);
// Change noise level to 3 ( I want
tmp1 = Thunder_Read(0x01) & 0x8F;
tmp1 = tmp1 | 0x20;
Thunder_Write(0x01, tmp1);
//Showing noise level
tmp1 = Thunder_Read(0x01) & 0x70;
Lcd_Cmd(_LCD_CLEAR);
Lcd_Out(1,1, "Noise level");
ShortToStr(tmp1>>4, msg_str);
Lcd_Out(2,1, msg_str);
delay_ms(1000);
//Show watchdog treshold
tmp1 = Thunder_Read(0x01) & 0x0F;
Lcd_Cmd(_LCD_CLEAR);
Lcd_Out(1,1, "Watchdog set to");
ShortToStr(tmp1, msg_str);
Lcd_Out(2,1, msg_str);
delay_ms(1000);
//Show spike rejection
tmp1 = Thunder_Read(0x02) & 0x0F;
Lcd_Cmd(_LCD_CLEAR);
Lcd_Out(1,1, "Spike rejection");
ShortToStr(tmp1, msg_str);
Lcd_Out(2,1, msg_str);
delay_ms(1000);
// We are now ready to go with the main routine
// We reenable the INT0 interrupt
INTCON.GIE = 1; // enable interrupts
INTCON.INT0IE = 1; // enable INT0 interrupt
INTCON2.INTEDG0 = 1; // interrupt on raising edge (mandatory)
// This will clear any AS3935 interrupts
// Don't worry. AS3935 issues a lot of interrupts
delay_ms(2);
interrupt_source = Thunder_Read(0x03); // reading reg 0x03 clears interrupt
interrupt_detected = 0; // this will clear any leftover interrupts
while(1){
// this prints a nice message counter
ShortToStr(message_count, msg_str);
Lcd_Cmd(_LCD_CLEAR);
Lcd_Out(1,1, msg_str);
message_count++;
if (message_count == 100)
message_count=0;
if (interrupt_detected == 0){ // nothing happens
Lcd_Out(1,6, "Waiting...");
Delay_ms(150);
}
else {
// We read the AS3935 registers to see what caused the interrupt
delay_ms(2); // 2ms delay between interrupt and reading of register 0x03
// Time to read the 0x03 register which stores interupt codes
interrupt_source = Thunder_Read(0x03) & 0x0F;
if (interrupt_source = 0x01) // is noise
Lcd_Out(1,6, "Just noise");
if (interrupt_source = 0x04) // is disturber
Lcd_Out(1,6, "Disturbers");
if (interrupt_source = 0x08){ // is lightning
Lcd_Out(1,6, "Lightning ");
// now we get the distance to the lightning event
Lcd_Out(2,1, "Dist:");
tmp1 = Thunder_Read_distance();
ShortToStr(tmp1, msg_str);
Lcd_Out(2,6, msg_str);
}
interrupt_detected = 0;
Delay_ms(500);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment