/* * 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 * */ /* * LIBRARIES */ //Protocols #include // library used with I2C protocol #include // SPI protocol //Teensy3.x Real Time Clock #include //SD card #include // Sensors #include // Generic #include // BME280 #include //GPS //#include // Adafruit Ultimate GPS #include //Builtin GPS lib /* * DEFINE * & * DECLARE */ //Special characteres #define DEGREE (char)176 //degree symbol in ISO 8859-1 #define DEGREE_LCD (char)222 //degree symbol for lcd display // Pins used #define RX1_PIN 0 // Raspberry Pi2 serial comm. - Hard wired #define TX1_PIN 1 // Raspberry Pi2 serial comm. - Hard wired #define RPI_PWR_PIN 2 // Raspberry Pi power control - Hard wired #define AS3935_CS_PIN 4 // SPI SD CS - Hard wired #define GPS_EN_PIN 6 // GPS ENable #define RX3_PIN 7 // GPS serial comm. #define TX3_PIN 8 // GPS serial comm. #define AS3935_IRQ_PIN 9 // Interrupts from AS3935 lightning sensor #define SD_CS_PIN 10 // SPI CS AS3935 lightning sensor #define SPI_DI_PIN 11 // SPI MOSI AS3935 lightning sensor #define SPI_DO_PIN 12 // SPI MISO AS3935 lightning sensor #define SPI_CK_PIN 13 // SPI clock SD + AS3935 lightning sensor + built-in LED #define BATT_VOLT_PIN 16 // Battery voltage monitoring (Analog Input) #define LOW_BATT_PIN 17 // Low Battery signal from charger (digital input) #define I2C_SDA_PIN 18 // I2C SDA BME280 sensor #define I2C_SCL_PIN 19 // I2C SCL BME280 sensor #define GPS_BUT_PIN 20 // GPS switch power control (unused) //I2C addresses //#define TCN75A_ADDR 0x00 //Sensor I2C bus address //#define BMP180_ADDR 0x77 //Serial over USB communication #define SERIAL_PORT Serial #define SERIAL_BAUD_RATE 9600 //Raspberry Pi Serial Comm. #define RPI_SERIAL_PORT Serial1 #define RPI_SERIAL_BAUD_RATE 9600 //GPS #define GPS_SERIAL_PORT Serial3 #define GPS_SERIAL_BAUD_RATE 9600 //Sensors Adafruit_BME280 bme; float seaLevelPressure = SENSORS_PRESSURE_SEALEVELHPA; float bme280_press; float bme280_temp; float bme280_alti; float bme280_humi; // AS3935 Lightning sensor #define AS3935_INDOORS 0 #define AS3935_OUTDOORS 1 #define AS3935_DIST_DIS 0 #define AS3935_DIST_EN 1 #define AS3935_CAPACITANCE 72 // 72pF for THIS board (from seller) 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 = true; int batt_voltage; bool low_battery_flag = false; // Tasks timers elapsedMillis since_bme280; elapsedMillis since_gps; elapsedMillis since_display; elapsedMillis since_serial_send; elapsedMillis since_sd_log; elapsedMillis since_batt_chk; void BootMessage(String s) { char c[13]; // char buffer for conversion String->char s.toCharArray(c, sizeof(s)); SERIAL_PORT.printf("%-12s", c); } void BootOK() { SERIAL_PORT.println("OK"); } void BootError() { SERIAL_PORT.println("EE"); } void sendDataOnSerial() { //Print date & hour on serial port SERIAL_PORT.printf("%04d/%02d/%02d_%02d:%02d:%02d\n", year(), month(), day(), hour(), minute(), second()); //if (bme280_event.pressure) { SERIAL_PORT.printf("Temperature:%8.2f %cC | Pressure: %8.2f hPa | Altitude:%8.2f m\n", bme280_temp, DEGREE, bme280_press, bme280_alti); // } // else { // SERIAL_PORT.printf("BMP180 Sensor error\n"); // } SERIAL_PORT.printf("Lightning strikes (from start) : %d | Perturb.: %d | Noise : %d | Unknown detect.: %d\n", lightning_nb_total, as3935_perturb_total, as3935_noise_total, as3935_unknown_total); //SERIAL_PORT.printf("Temperature:%8.2f %cC\n", tcn75a_temp, DEGREE); SERIAL_PORT.printf("GPS data: %04d/%02d/%02d_%02d:%02d:%02d (%d)\n", gps_year, gps_month, gps_day, gps_hour, gps_minutes, gps_second, gps_fix_age); SERIAL_PORT.printf(" Latitude: %11.8f | Longitude: %11.8f | Altitude: %5.2f m\n", gps_latitude, gps_longitude, gps_altitude); SERIAL_PORT.printf(" Speed: %4.1f km/h | Course : %4.1f %c\n", gps_speed, gps_course, DEGREE); SERIAL_PORT.printf(" Chars: %11d | Sentences: %11d | Failed cheksum: %4d\n", gps_chars, gps_sentences, gps_failed_checksum); SERIAL_PORT.printf("Battery : %10d mV | Low Battery : %d\n", batt_voltage, low_battery_flag); } bool isStartedPI() { //Return true if the R-Pi respond to an simple request return rpi_status; } void startRPI() { //Start the Raspberry Pi via the MOSFET (grid connected to the RPI_PWR_PIN) if (!isStartedPI()) { pinMode(RPI_PWR_PIN, OUTPUT); digitalWrite(RPI_PWR_PIN, LOW); rpi_status = true; } } void stopRPI() { //Stop the Raspberry Pi via the MOSFET (grid connected to the RPI_PWR_PIN) pinMode(RPI_PWR_PIN, INPUT); //Eteint via le MOSFET rpi_status = false; } void sendDataToRPI() { //if (isStartedPI()) { //Print date & hour on serial port RPI_SERIAL_PORT.printf("%04d/%02d/%02d_%02d:%02d:%02d\n", year(), month(), day(), hour(), minute(), second()); //if (bme280_event.pressure) { RPI_SERIAL_PORT.printf("Temperature:%8.2f %cC | Pressure: %8.2f hPa | Altitude:%8.2f m\n", bme280_temp, DEGREE, bme280_press, bme280_alti); //} //else { // RPI_SERIAL_PORT.printf("BMP180 Sensor error\n"); //} RPI_SERIAL_PORT.printf("Lightning strikes (from start) : %d | Perturb.: %d | Noise : %d | Unknown detect.: %d\n", lightning_nb_total, as3935_perturb_total, as3935_noise_total, as3935_unknown_total); RPI_SERIAL_PORT.printf("GPS data: %04d/%02d/%02d_%02d:%02d:%02d (%d)\n", gps_year, gps_month, gps_day, gps_hour, gps_minutes, gps_second, gps_fix_age); RPI_SERIAL_PORT.printf(" Latitude: %11.8f | Longitude: %11.8f | Altitude: %5.2f m\n", gps_latitude, gps_longitude, gps_altitude); RPI_SERIAL_PORT.printf(" Speed: %4.1f km/h | Course : %4.1f %c\n", gps_speed, gps_course, DEGREE); RPI_SERIAL_PORT.printf(" Chars: %11d | Sentences: %11d | Failed cheksum: %4d\n", gps_chars, gps_sentences, gps_failed_checksum); RPI_SERIAL_PORT.printf("Battery : %10d mV | Low Battery : %d\n", batt_voltage, low_battery_flag); // } // else { // //error RPI is not started // } } //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"); RPI_SERIAL_PORT.begin(RPI_SERIAL_BAUD_RATE); delay(1000); //while (!RPI_SERIAL_PORT) { }; //Wait for serial port to start RPI_SERIAL_PORT.printf("Raspberry's 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("BME280 (Pressure, Humidity 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_OUTDOORS, 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_bme280 >= bme280_delay) { // since_bme280 = since_bme280 - bme280_delay; // // bme280_press = bme.readPressure() / 100.0F; // bme280_alti = bme.readAltitude(seaLevelPressure); // bme280_temp = bme.readTemperature(); // bme280_humi = bme.readHumidity(); // } 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(); sendDataToRPI(); } } /* * 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