400 lines
12 KiB
C++
400 lines
12 KiB
C++
|
/*
|
||
|
* CAMETEO project
|
||
|
*
|
||
|
* This is a personnal project of weather station with
|
||
|
* automatic photo taking.
|
||
|
* This code is the weather station part and is meant
|
||
|
* to be run on a PJRC Teensy 3.2 board.
|
||
|
*
|
||
|
* Author : Arofarn
|
||
|
*
|
||
|
* Licence : GPL v3
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include <Arduino.h>
|
||
|
|
||
|
#include "cameteo_teensy.h"
|
||
|
#include "SerialMessages.cpp"
|
||
|
#include "RaspBerryPi_COM.cpp"
|
||
|
|
||
|
//sensors_event_t bme280_event;
|
||
|
float seaLevelPressure = 1015.0;
|
||
|
float bme280_press;
|
||
|
float bme280_temp;
|
||
|
float bme280_alti;
|
||
|
float bme280_hum;
|
||
|
|
||
|
enum strike_sources { UNKNOWN_SRC, LIGHTNING, PERTURBATION, NOISE };
|
||
|
volatile int8_t AS3935_ISR_Trig = 0; // Trigger for AS3935 lightning sensor
|
||
|
|
||
|
void AS3935_ISR() {
|
||
|
AS3935_ISR_Trig = 1;
|
||
|
}
|
||
|
|
||
|
PWF_AS3935 lightning(AS3935_CS_PIN, AS3935_IRQ_PIN, 33);
|
||
|
|
||
|
int as3935_src;
|
||
|
int as3935_distance;
|
||
|
long lightning_nb_total = 0;
|
||
|
int lightning_nb_hour = 0;
|
||
|
int lightning_nb_day = 0;
|
||
|
int as3935_perturb_total = 0;
|
||
|
int as3935_noise_total = 0;
|
||
|
int as3935_unknown_total = 0;
|
||
|
char lightning_log_file[12] = "lghtnng.log";
|
||
|
|
||
|
//GPS
|
||
|
//Adafruit_GPS GPS(&GPS_SERIAL_PORT);
|
||
|
TinyGPS GPS;
|
||
|
float gps_latitude, gps_longitude, gps_altitude; // returns +- latitude/longitude in degrees
|
||
|
float gps_speed, gps_course;
|
||
|
unsigned long gps_time, gps_date, gps_fix_age;
|
||
|
int gps_year;
|
||
|
byte gps_month, gps_day, gps_hour, gps_minutes, gps_second, gps_hundreths;
|
||
|
unsigned long gps_chars, gps_hdop;
|
||
|
unsigned short gps_sentences, gps_failed_checksum, gps_satellites;
|
||
|
|
||
|
//Miscellaneous
|
||
|
bool rpi_status;
|
||
|
rpi_status = false;
|
||
|
int batt_voltage;
|
||
|
bool low_battery_flag = false;
|
||
|
|
||
|
// Tasks timers
|
||
|
elapsedMillis since_bme280;
|
||
|
//elapsedMillis since_dht;
|
||
|
elapsedMillis since_gps;
|
||
|
elapsedMillis since_display;
|
||
|
elapsedMillis since_serial_send;
|
||
|
elapsedMillis since_sd_log;
|
||
|
elapsedMillis since_batt_chk;
|
||
|
|
||
|
//SD Card
|
||
|
#define CONFIGFILE "config.txt"
|
||
|
|
||
|
//Configuration
|
||
|
char data_file[13] = "datalog.csv";
|
||
|
|
||
|
//Delays
|
||
|
|
||
|
unsigned int bme280_delay = 500;
|
||
|
//unsigned int dht_delay = 3000;
|
||
|
unsigned int gps_delay = 100;
|
||
|
unsigned int serial_send_delay = 5000;
|
||
|
unsigned int sd_log_delay = 5000;
|
||
|
unsigned int batt_chk_delay = 5000;
|
||
|
|
||
|
//Date and time
|
||
|
int TZ = 1; //Time zone
|
||
|
|
||
|
/*
|
||
|
* SETUP
|
||
|
*/
|
||
|
|
||
|
void setup() {
|
||
|
|
||
|
//To be sure Raspberry-Pi won't be turned on unexpectedly
|
||
|
stopRPI();
|
||
|
|
||
|
pinMode(LOW_BATT_PIN, INPUT_PULLUP);
|
||
|
low_battery_flag = !digitalRead(LOW_BATT_PIN);
|
||
|
|
||
|
pinMode(GPS_BUT_PIN, INPUT_PULLUP);
|
||
|
pinMode(GPS_EN_PIN, OUTPUT);
|
||
|
gps_power();
|
||
|
|
||
|
SERIAL_PORT.begin(SERIAL_BAUD_RATE);
|
||
|
delay(1000);
|
||
|
//while (!SERIAL_PORT) { }; //Wait for serial port to start
|
||
|
SERIAL_PORT.printf("Serial Comm. OK\nNow booting...\n");
|
||
|
|
||
|
BootMessage("SDcard");
|
||
|
// see if the card is present and can be initialized:
|
||
|
if (!SD.begin(SD_CS_PIN)) {
|
||
|
BootError();
|
||
|
while(1);
|
||
|
}
|
||
|
BootOK();
|
||
|
|
||
|
BootMessage("RT Clock");
|
||
|
// set the Time library to use Teensy 3.0's RTC to keep time
|
||
|
setSyncProvider(getTeensy3Time);
|
||
|
if (timeStatus()!= timeSet) {
|
||
|
BootError();
|
||
|
while(1);
|
||
|
}
|
||
|
BootOK();
|
||
|
|
||
|
// BootMessage("AM2302/DHT22 (Humidity and Temperature)");
|
||
|
// dht.begin();
|
||
|
// //sensor_t sensor;
|
||
|
// BootOK();
|
||
|
|
||
|
BootMessage("BME280 (Pressure and Temperature)");
|
||
|
/* Initialise the sensor */
|
||
|
if(!bme.begin())
|
||
|
{
|
||
|
/* There was a problem detecting the BMP085 ... check your connections */
|
||
|
BootError();
|
||
|
while(1);
|
||
|
}
|
||
|
BootOK();
|
||
|
|
||
|
BootMessage("AS3935 (Lightning)");
|
||
|
SPI.begin();
|
||
|
SPI.setClockDivider(SPI_CLOCK_DIV16); // SPI speed to SPI_CLOCK_DIV16/1MHz (max 2MHz, NEVER 500kHz!)
|
||
|
SPI.setDataMode(SPI_MODE1); // MAX31855 is a Mode 1 device --> clock starts low, read on rising edge
|
||
|
SPI.setBitOrder(MSBFIRST); // data sent to chip MSb first
|
||
|
lightning.AS3935_DefInit(); // set registers to default
|
||
|
// now update sensor cal for your application and power up chip
|
||
|
lightning.AS3935_ManualCal(AS3935_CAPACITANCE, AS3935_INDOORS, AS3935_DIST_EN);
|
||
|
// enable interrupt (hook IRQ pin to Arduino Uno/Mega interrupt input: 0 -> pin 2, 1 -> pin 3 )
|
||
|
attachInterrupt(AS3935_IRQ_PIN, AS3935_ISR, RISING);
|
||
|
BootOK();
|
||
|
|
||
|
BootMessage("GPS");
|
||
|
GPS_SERIAL_PORT.begin(GPS_SERIAL_BAUD_RATE);
|
||
|
while (!GPS_SERIAL_PORT) {} ;
|
||
|
BootOK();
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* LOOP
|
||
|
*/
|
||
|
|
||
|
void loop() {
|
||
|
|
||
|
//Power ON/OFF GPS
|
||
|
gps_power();
|
||
|
|
||
|
//Lightning detection
|
||
|
if (AS3935_ISR_Trig != 0) {
|
||
|
AS3935_ISR_Trig = 0;
|
||
|
time_t t = now();
|
||
|
int distance = -9999;
|
||
|
int energy = -9999;
|
||
|
switch (as3935_src) {
|
||
|
case UNKNOWN_SRC:
|
||
|
//source inconnue
|
||
|
as3935_unknown_total++;
|
||
|
SERIAL_PORT.printf("Interruption (AS3935) : unkown source (not lightning)\n");
|
||
|
break;
|
||
|
case LIGHTNING:
|
||
|
//Foudre !!!
|
||
|
lightning_nb_total++;
|
||
|
distance = lightning.AS3935_GetLightningDistKm();
|
||
|
energy = lightning.AS3935_GetStrikeEnergyRaw();
|
||
|
SERIAL_PORT.printf("Interruption (AS3935) : Lightningbolt !!!\n");
|
||
|
SERIAL_PORT.printf("Distance : %4d km | Energy : %d \n", distance, energy);
|
||
|
break;
|
||
|
case PERTURBATION:
|
||
|
//Perturbation
|
||
|
as3935_perturb_total++;
|
||
|
SERIAL_PORT.printf("Interruption (AS3935) : perturbation...\n");
|
||
|
break;
|
||
|
case NOISE:
|
||
|
//Trop de bruit électromagnétique
|
||
|
as3935_noise_total++;
|
||
|
SERIAL_PORT.printf("Interruption (AS3935) : Too much electromagnetic noise!\n");
|
||
|
break;
|
||
|
}
|
||
|
writeLightningToSD(as3935_src, t, distance, energy);
|
||
|
}
|
||
|
|
||
|
if (since_batt_chk >= batt_chk_delay) {
|
||
|
since_batt_chk -= batt_chk_delay;
|
||
|
//Check battery status and voltage
|
||
|
low_battery_flag = !digitalRead(LOW_BATT_PIN);
|
||
|
batt_voltage = getBatteryVoltage();
|
||
|
}
|
||
|
|
||
|
// if (since_dht >= dht_delay) {
|
||
|
// since_dht = since_dht - dht_delay;
|
||
|
// // Read temperature or humidity
|
||
|
// dht.humidity().getEvent(&dht22_event_hum);
|
||
|
// dht.temperature().getEvent(&dht22_event_temp);
|
||
|
// }
|
||
|
|
||
|
if (since_bme280 >= bme280_delay) {
|
||
|
since_bme280 = since_bme280 - bme280_delay;
|
||
|
/* Get a new sensor event */
|
||
|
bmp.getEvent(&bme280_event);
|
||
|
|
||
|
/* Get the values (barometric pressure is measure in hPa) */
|
||
|
if (bme280_event.pressure)
|
||
|
{
|
||
|
bme280_press = bme280_event.pressure;
|
||
|
bmp.getTemperature(&bme280_temp);
|
||
|
bme280_alti = bmp.pressureToAltitude(seaLevelPressure, bme280_press);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (since_gps >= gps_delay) {
|
||
|
since_gps = since_gps - gps_delay;
|
||
|
while (GPS_SERIAL_PORT.available()) {
|
||
|
char c = GPS_SERIAL_PORT.read();
|
||
|
if (GPS.encode(c)) {
|
||
|
GPS.get_datetime(&gps_date, &gps_time, &gps_fix_age);
|
||
|
GPS.crack_datetime(&gps_year, &gps_month, &gps_day,
|
||
|
&gps_hour, &gps_minutes, &gps_second,
|
||
|
&gps_hundreths, &gps_fix_age);
|
||
|
GPS.f_get_position(&gps_latitude, &gps_longitude, &gps_fix_age);
|
||
|
gps_altitude = GPS.f_altitude();
|
||
|
gps_speed = GPS.f_speed_kmph();
|
||
|
GPS.stats(&gps_chars, &gps_sentences, &gps_failed_checksum);
|
||
|
gps_satellites = GPS.satellites();
|
||
|
gps_hdop = GPS.hdop();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (since_sd_log >= sd_log_delay) {
|
||
|
since_sd_log = since_sd_log - sd_log_delay;
|
||
|
writeDataToSD();
|
||
|
}
|
||
|
|
||
|
if (since_serial_send >= serial_send_delay) {
|
||
|
since_serial_send = since_serial_send - serial_send_delay;
|
||
|
sendDataOnSerial();
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* FUNCTIONS
|
||
|
*/
|
||
|
|
||
|
time_t getTeensy3Time() {
|
||
|
return Teensy3Clock.get();
|
||
|
}
|
||
|
|
||
|
void gps_power() {
|
||
|
if (digitalRead(GPS_BUT_PIN) == 0) { digitalWrite(GPS_EN_PIN, LOW); }
|
||
|
else { digitalWrite(GPS_EN_PIN, HIGH); }
|
||
|
}
|
||
|
|
||
|
int getBatteryVoltage() {
|
||
|
long mean = 0;
|
||
|
int U = 0;
|
||
|
int n = 10;
|
||
|
int res = 12;
|
||
|
|
||
|
analogReadResolution(res);
|
||
|
|
||
|
for (int i=0; i<n; i++) {
|
||
|
mean += analogRead(BATT_VOLT_PIN);
|
||
|
//delay(2);
|
||
|
}
|
||
|
mean /= n;
|
||
|
|
||
|
U = map(mean, 0, pow(2, res)-1, 0, 3300); // Convert data from ADC into input voltage in mV
|
||
|
U *= 2; //Multiple by 2 for the voltage divider
|
||
|
|
||
|
return U;
|
||
|
}
|
||
|
|
||
|
void writeDataToSD() {
|
||
|
|
||
|
char dir[20];
|
||
|
char path[60];
|
||
|
|
||
|
String directory = dayDirectory();
|
||
|
|
||
|
directory.toCharArray(dir, sizeof(directory));
|
||
|
sprintf(path, "%s/%s", dir, data_file);
|
||
|
|
||
|
// Test to know if the file exists before opening it, if it doesn't exist
|
||
|
// we will write first an header
|
||
|
bool no_header = SD.exists(path);
|
||
|
|
||
|
// open the file. note that only one file can be open at a time,
|
||
|
// so you have to close this one before opening another.
|
||
|
File dataFile = SD.open(path, FILE_WRITE);
|
||
|
|
||
|
// if the file is available, write to it:
|
||
|
if (dataFile) {
|
||
|
if (!no_header) {
|
||
|
SERIAL_PORT.printf("Creating file with CSV header : %s\n", path);
|
||
|
dataFile.printf("Date");
|
||
|
dataFile.printf(";bme280_Pressure(hPa);BMP180_Temperature(degC);BMP180_Altitude(m)");
|
||
|
// dataFile.printf(";DHT22_Humidity(%);DHT22_Temperature(degC)");
|
||
|
dataFile.printf(";TotalLightningCount;TotalPerturbationEvents;TotalNoiseDetection;TotalUnknownDetection");
|
||
|
dataFile.printf(";GPS_Latitude;GPS_Longitude;GPS_Altitude;GPS_Satellites;GPS_HDOP;GPS_Date");
|
||
|
dataFile.printf(";Battery_voltage(mV);Low_Battery_Status");
|
||
|
dataFile.printf("\n");
|
||
|
}
|
||
|
dataFile.printf("%04d/%02d/%02d_%02d:%02d:%02d", year(), month(), day(), hour(), minute(), second());
|
||
|
dataFile.printf(";%.2f;%.2f;%.1f", bme280_press, bme280_temp, bme280_alti);
|
||
|
// dataFile.printf(";%.0f;%.2f", dht22_event_hum.relative_humidity, dht22_event_temp.temperature);
|
||
|
dataFile.printf(";%d;%d;%d;%d", lightning_nb_total, as3935_perturb_total, as3935_noise_total, as3935_unknown_total);
|
||
|
dataFile.printf(";%.8f;%.8f;%.2f;%d;%d;%04d/%02d/%02d_%02d:%02d:%02d", gps_latitude, gps_longitude, gps_altitude,
|
||
|
gps_satellites, gps_hdop,
|
||
|
gps_year, gps_month, gps_day,
|
||
|
gps_hour, gps_minutes, gps_second);
|
||
|
dataFile.printf(";%d;%d", batt_voltage, low_battery_flag);
|
||
|
dataFile.printf("\n");
|
||
|
|
||
|
dataFile.close();
|
||
|
}
|
||
|
SERIAL_PORT.printf("Data writen : %s\n", path);
|
||
|
}
|
||
|
|
||
|
void writeLightningToSD(int type, time_t t, int dist, int energy) {
|
||
|
|
||
|
char dir[20];
|
||
|
char path[60];
|
||
|
|
||
|
String directory = dayDirectory();
|
||
|
|
||
|
directory.toCharArray(dir, sizeof(directory));
|
||
|
sprintf(path, "%s/%s", dir, lightning_log_file);
|
||
|
|
||
|
// Test to know if the file exists before opening it, if it doesn't exist
|
||
|
// we will write first an header
|
||
|
bool no_header = SD.exists(path);
|
||
|
|
||
|
// open the file. note that only one file can be open at a time,
|
||
|
// so you have to close this one before opening another.
|
||
|
File dataFile = SD.open(path, FILE_WRITE);
|
||
|
|
||
|
// if the file is available, write to it:
|
||
|
if (dataFile) {
|
||
|
if (!no_header) {
|
||
|
SERIAL_PORT.printf("Creating file with CSV header : %s\n", path);
|
||
|
dataFile.printf("Date");
|
||
|
dataFile.printf(";distance(km);energie_raw");
|
||
|
dataFile.printf(";TotalLightningCount;TotalPerturbationEvents;TotalNoiseDetection;TotalUnknownDetection");
|
||
|
dataFile.printf(";BME280_Pressure(hPa);BME280_Temperature(degC);BME280_Altitude(m)");
|
||
|
// dataFile.printf(";DHT22_Humidity(%);DHT22_Temperature(degC)");
|
||
|
dataFile.printf("\n");
|
||
|
}
|
||
|
dataFile.printf("%04d/%02d/%02d_%02d:%02d:%02d", year(t), month(t), day(t), hour(t), minute(t), second(t));
|
||
|
dataFile.printf(";%d;%d;%d", type, dist, energy);
|
||
|
dataFile.printf(";%d;%d;%d;%d", lightning_nb_total, as3935_perturb_total, as3935_noise_total, as3935_unknown_total);
|
||
|
dataFile.printf(";%.2f;%.2f;%.1f", bme280_press, bme280_temp, bme280_alti);
|
||
|
// dataFile.printf(";%.0f;%.2f", dht22_event_hum.relative_humidity, dht22_event_temp.temperature);
|
||
|
dataFile.printf("\n");
|
||
|
|
||
|
dataFile.close();
|
||
|
}
|
||
|
SERIAL_PORT.printf("Data writen : %s\n", path);
|
||
|
}
|
||
|
|
||
|
String dayDirectory() {
|
||
|
|
||
|
char dir[20] ;
|
||
|
|
||
|
sprintf(dir, "data/%04d/%02d/%02d/", year(), month(), day());
|
||
|
|
||
|
if (!SD.exists(dir))
|
||
|
{
|
||
|
SERIAL_PORT.printf("Creating directory : %s ...", dir);
|
||
|
SD.mkdir(dir);
|
||
|
SERIAL_PORT.printf(" DONE!\n");
|
||
|
}
|
||
|
|
||
|
return String(dir);
|
||
|
}
|