commit e9c20be21ceef0dbaf712015614e8d350e559d76 Author: Pierrick C Date: Sat Jul 8 18:08:54 2017 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..426d034 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +raspberry/data/* +raspberry/pictures/* +*.fcstd1 +*.fcstd2 +boitier/old* +.directory diff --git a/arduino/SDcard/config.txt b/arduino/SDcard/config.txt new file mode 100755 index 0000000..4f30564 --- /dev/null +++ b/arduino/SDcard/config.txt @@ -0,0 +1,4 @@ +#Fichier de configuration du contrôleur du projet +# Camétéo +time_step=2000 +data_file=datalog3.csv \ No newline at end of file diff --git a/arduino/cameteo-teensy-bug/cameteo-teensy/RaspBerryPi_COM.cpp b/arduino/cameteo-teensy-bug/cameteo-teensy/RaspBerryPi_COM.cpp new file mode 100755 index 0000000..08976cc --- /dev/null +++ b/arduino/cameteo-teensy-bug/cameteo-teensy/RaspBerryPi_COM.cpp @@ -0,0 +1,42 @@ + +#include "cameteo_teensy.h" + +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; +} + +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 sendDataToSerial(String data) { + if (isStartedPI()) { + char c[100]; // char buffer for conversion String->char + data.toCharArray(c, sizeof(data)); //convert data string to char array + +// //start serial comm. if needed +// if(!SERIAL_PORT) { +// SERIAL_PORT.begin(SERIAL_BAUD_RATE); +// //while(!SERIAL_PORT); +// } + + SERIAL_PORT.print(c); // send data on serial port + + } + else { + //error RPI is not started + } +} diff --git a/arduino/cameteo-teensy-bug/cameteo-teensy/SerialMessages.cpp b/arduino/cameteo-teensy-bug/cameteo-teensy/SerialMessages.cpp new file mode 100755 index 0000000..f70c4a2 --- /dev/null +++ b/arduino/cameteo-teensy-bug/cameteo-teensy/SerialMessages.cpp @@ -0,0 +1,55 @@ + +#include "cameteo_teensy.h" + +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 (isnan(dht22_event_hum.relative_humidity) || +// isnan(dht22_event_temp.temperature)) { +// SERIAL_PORT.printf("Failed to read from DHT sensor!\n"); +// } +// else { +// SERIAL_PORT.printf("Temperature:%8.2f %cC | Humidity:%8.0f %%\n", +// dht22_event_temp.temperature, DEGREE, dht22_event_hum.relative_humidity); +// } + +// if (bme280_event.pressure) { + SERIAL_PORT.printf("Temperature:%8.2f %cC | Humidity: %8.2f % | Pressure: %8.2f hPa | Altitude:%8.2f m\n", + bme280_temp, DEGREE, bme280_press, bme280_alti); +// } +// else { +// SERIAL_PORT.printf("BME280 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); +} diff --git a/arduino/cameteo-teensy-bug/cameteo-teensy/cameteo-teensy.ino b/arduino/cameteo-teensy-bug/cameteo-teensy/cameteo-teensy.ino new file mode 100755 index 0000000..3bf0cf6 --- /dev/null +++ b/arduino/cameteo-teensy-bug/cameteo-teensy/cameteo-teensy.ino @@ -0,0 +1,36 @@ +/* + * 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 + * + */ + +//Protocols +#include // library used with I2C protocol +#include // SPI protocol + +//Teensy3.x Real Time Clock +#include + +//SD card +#include + +// Sensors +#include // Generic +//#include // DHT22 +//#include // DHT22 unified +//#include // BMP180 +#include // BME280 +#include + +//GPS +//#include // Adafruit Ultimate GPS +#include //Builtin GPS lib + diff --git a/arduino/cameteo-teensy-bug/cameteo-teensy/cameteo.cpp b/arduino/cameteo-teensy-bug/cameteo-teensy/cameteo.cpp new file mode 100755 index 0000000..6ca1aca --- /dev/null +++ b/arduino/cameteo-teensy-bug/cameteo-teensy/cameteo.cpp @@ -0,0 +1,399 @@ +/* + * 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 + +#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 // library used with I2C protocol +#include // SPI protocol + +//Teensy3.x Real Time Clock +#include + +//SD card +#include + +// Sensors +#include // Generic +//#include // DHT22 +//#include // DHT22 unified +//#include // BMP180 +#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 SD_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 AS3935_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 DHT_PIN 14 // Humidity sensor data +#define GPS_BUT_PIN 16 // GPS switch power control + +#define I2C_SDA_PIN 18 // I2C SDA +#define I2C_SCL_PIN 19 // I2C SCL + +#define BATT_VOLT_PIN 22 // Battery voltage monitoring (Analog Input) +#define LOW_BATT_PIN 23 // Low Battery signal from charger (digital input) + +//I2C addresses +//#define TCN75A_ADDR 0x00 //Sensor I2C bus address +#define BMP180_ADDR 0x77 +//#define BME280_ADDR 0x00 // ??? + +//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 115200 + +//GPS +#define GPS_SERIAL_PORT Serial3 +#define GPS_SERIAL_BAUD_RATE 9600 + +// 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) + + diff --git a/arduino/cameteo-teensy/cameteo-teensy.ino b/arduino/cameteo-teensy/cameteo-teensy.ino new file mode 100755 index 0000000..f64c225 --- /dev/null +++ b/arduino/cameteo-teensy/cameteo-teensy.ino @@ -0,0 +1,553 @@ +/* + * 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/connections RPi-Arduino.fzz b/doc/connections RPi-Arduino.fzz new file mode 100755 index 0000000..d8ce885 Binary files /dev/null and b/doc/connections RPi-Arduino.fzz differ diff --git a/doc/connections RPi-teensy.fzz b/doc/connections RPi-teensy.fzz new file mode 100755 index 0000000..a149e9a Binary files /dev/null and b/doc/connections RPi-teensy.fzz differ diff --git a/raspberry/__pycache__/datacam.cpython-32.pyc b/raspberry/__pycache__/datacam.cpython-32.pyc new file mode 100755 index 0000000..0602429 Binary files /dev/null and b/raspberry/__pycache__/datacam.cpython-32.pyc differ diff --git a/raspberry/cameteo-rpi.py b/raspberry/cameteo-rpi.py new file mode 100755 index 0000000..cc91f9f --- /dev/null +++ b/raspberry/cameteo-rpi.py @@ -0,0 +1,361 @@ +#!/usr/bin/python3 +# -*- coding: UTF8 -*- + +"""datacam + +datacam is design to take a picture with the PiCamera and collect environnemental +data from the PiSense HAT, (almost) at the same time. + +""" + +################# +# Configuration # +################# + +#General +version = "v0.4" +#time_lapse = 20 # Time between photos et data captures +verbose = True + +# Pictures +ajust_time = 1 #Time waiting for the camera to ajust before taking the photo (in seconds) +x_res = 2592 #X resolution (max = 2592) +y_res = 1944 #Y resolution (max = 1944) +photo_dir = "pictures" #Photo directory +photo_file = "Test_%Y-%m-%d_%H%M%S" # Picture files name (strftime() compatible) +photo_rotation = "180" # Image rotation in degree +#camera_LED = True # Set to False to disable red camera LED during capture, need root privileges +sense_LED_flash = True + +# Data + +#Data to collect : +# - 'temperature_h' : temperature from humidity sensor +# - 'temperature_p' : temperature from pressure sensor +# - 'temperature_cpu' : temperature from CPU +# - 'pressure' : pressure +# - 'humidity' : relative humidity +data_collection = ['temperature_p', + 'pressure', + 'temperature_h', + 'humidity', + 'temperature_cpu', + 'blabla', + ] +data_display = ['pressure', + 'temperature_h', + 'humidity', + 'blabla', + ] +data_dir = 'data' +data_log = ['date', 'value', 'quality'] + +# Fonts +fonts_dir = '/usr/share/fonts/truetype/freefont/' +font_bold = 'FreeMonoBold.ttf' +font_basic = 'FreeMono.ttf' +font_italic = 'FreeMonoOblique.ttf' +font_bold_italic = 'FreeMonoBoldOblique.ttf' +font_default = font_basic + + +################# +# Imports # +################# + +from os import path, popen +from time import sleep, strftime +import csv + +# Picture management with the Pi Cam +from picamera import PiCamera + +#Pi Sense HAT module (sensors) +from sense_hat import SenseHat + +# Traitement d'images +import PIL +from PIL import ImageFont +from PIL import Image +from PIL import ImageDraw + +# For picture metadata (EXIF, IPTC...) +import piexif + +#Divers +from pprint import pprint + +######################### +# Déclarations globales # +######################### + +sense = SenseHat() +data = {} +data_dir = path.join('/home/pi/datacam', version, data_dir) +photo_dir = path.join('/home/pi/datacam', version, photo_dir) + +############# +# Fonctions # +############# + +#Get one data from one sensors and return the value in a dict object with some metadata +# like time/date, description, unit... + +class Raw_Data: + """Data class. + """ + dtype = '' + date = '' + value = '' + quality = -1 # -1 : default value/data type unknown, 0 : OK, 1 : non-available + metadata = {'desc' : 'Unknow', + 'unit' : '', + } + + def __init__(self, data_type): + "Initialize new data object of the defined type." + self.dtype = data_type + if self.dtype == 'temperature_p': + self.metadata = {'desc' : 'Air temperature (pressure sensors)', + 'unit' : '°C', + 'short' : 'Temperature', + 'category' : 'Environnement/Temperature', + } + elif self.dtype == 'temperature_h': + self.metadata = {'desc' : 'Air temperature (pressure sensors)', + 'unit' : '°C', + 'short' : 'Temperature', + 'category' : 'Environnement/Temperature', + } + elif self.dtype == 'pressure': + self.metadata = {'desc' : 'Atmospheric Pressure', + 'unit' : 'mbar', + 'short' : 'Pression', + 'category' : 'Environnement/Pressure', + } + elif self.dtype == 'humidity': + self.metadata = {'desc' : 'Relative air humidity', + 'unit' : '%', + 'short' : 'Humidity', + 'category' : 'Environnement/Humidity', + } + elif self.dtype == 'temperature_cpu': + self.metadata = {'desc' : 'CPU temperature', + 'unit' : '°C', + 'short' : 'Temperature', + 'category' : 'System/Temperature', + } + else: + print(data_type + " is unknown.") + + def get_raw(self): + "Get raw data from sensors and update information about this data." + + self.date = strftime('%Y-%m-%d_%H:%M:%S') + + if self.dtype == 'temperature_p': + try: + self.value = float(sense.get_temperature_from_pressure()) + self.quality = 0 + except: + self.value = None + self.quality = 1 + elif self.dtype == 'temperature_h': + try: + self.value = float(sense.get_temperature_from_humidity()) + self.quality = 0 + except: + self.value = None + self.quality = 1 + elif self.dtype == 'pressure': + try: + self.value = float(sense.get_pressure()) + self.quality = 0 + except: + self.value = None + self.quality = 1 + elif self.dtype == 'humidity': + try: + self.value = float(sense.get_humidity()) + self.quality = 0 + except: + self.value = None + self.quality = 1 + elif self.dtype == 'temperature_cpu': + try: + temp = popen('/opt/vc/bin/vcgencmd measure_temp').readline() + self.value = float(temp.replace('temp=','').replace("'C\n","")) + self.quality = 0 + except: + self.value = None + self.quality = 1 + else: + print(data_type + " is unknown.") + self.metadata.update({'desc' : 'Unknown'}) + self.value = None + self.quality = -1 + + def write_csv(self, file_path): + "Write data in CSV file. Creates CSV file and metadata text file if they do not exist." + + if path.isfile(file_path): + with open(file_path, 'a') as csvfile: + datawriter = csv.DictWriter(csvfile, fieldnames=data_log, delimiter=';') + datawriter.writerow({n : v for n, v in vars(data[data_type]).items() if n in data_log }) + + else: + #Creation of metadata file + if data[data_type]['desc'] != 'Unknown': + with open(path.join(data_dir, data_type + '_desc.txt'), 'w') as metadata_file: + for desc, value in data[data_type].items(): + if desc not in data_log: + metadata_file.write(desc + " : " + str(value) + '\n') + #Creation of data file + with open(csvfile_path, 'w') as csvfile: + datawriter = csv.DictWriter(csvfile, fieldnames=data_log, delimiter=';') + datawriter.writeheader() + datawriter.writerow({n : v for n, v in vars(data[data_type]).items() if n in data_log}) + + def __repr__(self): + "For debug" + if self.metadata['desc'] == 'Unknown': + return ''.format(d=self.metadata['desc'], + t=self.dtype, + dt=self.date, + ) + elif type(self.value) == float: + return ''.format(d=self.metadata['desc'], + v=self.value, + u=self.metadata['unit'], + q=self.quality, + dt=self.date, + ) + elif self.value == None: + return ''.format(d=self.metadata['desc'], + v=self.value, + q=self.quality, + dt=self.date, + ) + else: + return ''.format(d=self.metadata['desc'], + v=self.value, + u=self.metadata['unit'], + q=self.quality, + dt=self.date, + ) + return + + def __str__(self): + "For print (to user)" + if self.metadata['desc'] == 'Unknown': + return '{data_type:} = {desc:}'.format(desc=self.metadata['desc'], + data_type=self.dtype, + ) + elif type(self.value) == float: + return '{d:} = {v:.1f}{u}'.format(d=self.metadata['desc'], + v=self.value, + u=self.metadata['unit'], + ) + elif self.value == None: + return '{d:} = {v}'.format(d=self.metadata['desc'], + v='NA', + ) + else: + return '{d:} = {v} {u}'.format(d=self.metadata['desc'], + v=self.value, + u=self.metadata['unit'], + ) + + def __call__(self): + return self.value + +# Take a picture +def get_pict(): + with PiCamera() as camera: + camera.resolution = (x_res, y_res) + camera.rotation = photo_rotation + #camera.led = camera_LED # !!! need root privileges !!! + if sense_LED_flash: + sense.show_message(text_string="", + back_colour=[255, 255, 255], + ) + camera.start_preview() + sleep(ajust_time) + sense.set_rotation(r=180, redraw=True) + #sense.show_message(text_string=":-)", + #scroll_speed=0.1 , + #text_colour=[255, 0, 0], + #back_colour=[0, 0, 255]) + + pict_path = path.join(photo_dir, strftime(photo_file) + '.jpg') + camera.capture(pict_path) + sense.clear() + #camera.led = True # !!! need root privileges !!! + + return pict_path + +######## +# MAIN # +######## + +# Collect asked data from sensors +for data_type in data_collection: + data[data_type] = Raw_Data(data_type) + + data[data_type].get_raw() + + if verbose : + print(data[data_type].__repr__()) + print(data[data_type]()) + + #Save data in CSV file + + csvfile_path = path.join(data_dir, data_type + '.csv') + + data[data_type].write_csv(csvfile_path) + +#Take a picture +#picture_path = get_pict() + +## Write data on picture in new picture file +#pict = Image.open(picture_path) + +#draw = ImageDraw.Draw(pict) + ## Text line height (=font size) and line space calculation +#line_height = int(y_res / 40) +#line_space = int(line_height / 10) + +#text = '' +#l = line_space + +## Add a line with data for each data to display +#for d in data_display: + #text = str(data[d]) #.__str__() + ## Select color and font to match data quality + #if data[d].quality == 0: + #text_color = (0, 255, 0) # Quality OK : text in green + #font = font_default + #elif data[d].quality < 0: + #text_color = (100, 100, 100) # Unknown in grey + #font = font_italic + #elif data[d].quality == 1: + #text_color = (255, 255, 0) # Sensor unavailable : yellow + #font = font_italic + #else: + #text_color = (255, 0, 0) # Other = error in red + #font = font_bold + + #img_font = ImageFont.truetype(path.join(fonts_dir, font), line_height) + #draw.text((5,l), text, text_color, img_font) + #draw = ImageDraw.Draw(pict) + #l = l + line_height + line_space + +##Get EXIF metadata and delete embedded thumbnail +#pict_exif = piexif.load(pict.info['exif']) +#del(pict_exif['thumbnail']) + +#pict.save(picture_path, 'jpeg', exif=piexif.dump(pict_exif) ) + +#if verbose : + #print('Picture = ' + picture_path) + diff --git a/raspberry/datacam.py b/raspberry/datacam.py new file mode 100755 index 0000000..42b1f36 --- /dev/null +++ b/raspberry/datacam.py @@ -0,0 +1,362 @@ +#!/usr/bin/python3 +# -*- coding: UTF8 -*- + +"""datacam +v0.3 + +datacam is design to take a picture with the PiCamera and collect environnemental +data from the PiSense HAT, (almost) at the same time. + +""" + +################# +# Configuration # +################# + +#General +version = "v0.3" +#time_lapse = 20 # Time between photos et data captures +verbose = True + +# Pictures +ajust_time = 1 #Time waiting for the camera to ajust before taking the photo (in seconds) +x_res = 2592 #X resolution (max = 2592) +y_res = 1944 #Y resolution (max = 1944) +photo_dir = "pictures" #Photo directory +photo_file = "Test_%Y-%m-%d_%H%M%S" # Picture files name (strftime() compatible) +photo_rotation = "180" # Image rotation in degree +#camera_LED = True # Set to False to disable red camera LED during capture, need root privileges +sense_LED_flash = True + +# Data + +#Data to collect : +# - 'temperature_h' : temperature from humidity sensor +# - 'temperature_p' : temperature from pressure sensor +# - 'temperature_cpu' : temperature from CPU +# - 'pressure' : pressure +# - 'humidity' : relative humidity +data_collection = ['temperature_p', + 'pressure', + 'temperature_h', + 'humidity', + 'temperature_cpu', + 'blabla', + ] +data_display = ['pressure', + 'temperature_h', + 'humidity', + 'blabla', + ] +data_dir = 'data' +data_log = ['date', 'value', 'quality'] + +# Fonts +fonts_dir = '/usr/share/fonts/truetype/freefont/' +font_bold = 'FreeMonoBold.ttf' +font_basic = 'FreeMono.ttf' +font_italic = 'FreeMonoOblique.ttf' +font_bold_italic = 'FreeMonoBoldOblique.ttf' +font_default = font_basic + + +################# +# Imports # +################# + +from os import path, popen +from time import sleep, strftime +import csv + +# Picture management with the Pi Cam +from picamera import PiCamera + +#Pi Sense HAT module (sensors) +from sense_hat import SenseHat + +# Traitement d'images +import PIL +from PIL import ImageFont +from PIL import Image +from PIL import ImageDraw + +# For picture metadata (EXIF, IPTC...) +import piexif + +#Divers +from pprint import pprint + +######################### +# Déclarations globales # +######################### + +sense = SenseHat() +data = {} +data_dir = path.join('/home/pi/datacam', version, data_dir) +photo_dir = path.join('/home/pi/datacam', version, photo_dir) + +############# +# Fonctions # +############# + +#Get one data from one sensors and return the value in a dict object with some metadata +# like time/date, description, unit... + +class Raw_Data: + """Data class. + """ + dtype = '' + date = '' + value = '' + quality = -1 # -1 : default value/data type unknown, 0 : OK, 1 : non-available + metadata = {'desc' : 'Unknow', + 'unit' : '', + } + + def __init__(self, data_type): + "Initialize new data object of the defined type." + self.dtype = data_type + if self.dtype == 'temperature_p': + self.metadata = {'desc' : 'Air temperature (pressure sensors)', + 'unit' : '°C', + 'short' : 'Temperature', + 'category' : 'Environnement/Temperature', + } + elif self.dtype == 'temperature_h': + self.metadata = {'desc' : 'Air temperature (pressure sensors)', + 'unit' : '°C', + 'short' : 'Temperature', + 'category' : 'Environnement/Temperature', + } + elif self.dtype == 'pressure': + self.metadata = {'desc' : 'Atmospheric Pressure', + 'unit' : 'mbar', + 'short' : 'Pression', + 'category' : 'Environnement/Pressure', + } + elif self.dtype == 'humidity': + self.metadata = {'desc' : 'Relative air humidity', + 'unit' : '%', + 'short' : 'Humidity', + 'category' : 'Environnement/Humidity', + } + elif self.dtype == 'temperature_cpu': + self.metadata = {'desc' : 'CPU temperature', + 'unit' : '°C', + 'short' : 'Temperature', + 'category' : 'System/Temperature', + } + else: + print(data_type + " is unknown.") + + def get_raw(self): + "Get raw data from sensors and update information about this data." + + self.date = strftime('%Y-%m-%d_%H:%M:%S') + + if self.dtype == 'temperature_p': + try: + self.value = float(sense.get_temperature_from_pressure()) + self.quality = 0 + except: + self.value = None + self.quality = 1 + elif self.dtype == 'temperature_h': + try: + self.value = float(sense.get_temperature_from_humidity()) + self.quality = 0 + except: + self.value = None + self.quality = 1 + elif self.dtype == 'pressure': + try: + self.value = float(sense.get_pressure()) + self.quality = 0 + except: + self.value = None + self.quality = 1 + elif self.dtype == 'humidity': + try: + self.value = float(sense.get_humidity()) + self.quality = 0 + except: + self.value = None + self.quality = 1 + elif self.dtype == 'temperature_cpu': + try: + temp = popen('/opt/vc/bin/vcgencmd measure_temp').readline() + self.value = float(temp.replace('temp=','').replace("'C\n","")) + self.quality = 0 + except: + self.value = None + self.quality = 1 + else: + print(data_type + " is unknown.") + self.metadata.update({'desc' : 'Unknown'}) + self.value = None + self.quality = -1 + + def write_csv(self, file_path): + "Write data in CSV file. Creates CSV file and metadata text file if they do not exist." + + if path.isfile(file_path): + with open(file_path, 'a') as csvfile: + datawriter = csv.DictWriter(csvfile, fieldnames=data_log, delimiter=';') + datawriter.writerow({n : v for n, v in vars(data[data_type]).items() if n in data_log }) + + else: + #Creation of metadata file + if data[data_type]['desc'] != 'Unknown': + with open(path.join(data_dir, data_type + '_desc.txt'), 'w') as metadata_file: + for desc, value in data[data_type].items(): + if desc not in data_log: + metadata_file.write(desc + " : " + str(value) + '\n') + #Creation of data file + with open(csvfile_path, 'w') as csvfile: + datawriter = csv.DictWriter(csvfile, fieldnames=data_log, delimiter=';') + datawriter.writeheader() + datawriter.writerow({n : v for n, v in vars(data[data_type]).items() if n in data_log}) + + def __repr__(self): + "For debug" + if self.metadata['desc'] == 'Unknown': + return ''.format(d=self.metadata['desc'], + t=self.dtype, + dt=self.date, + ) + elif type(self.value) == float: + return ''.format(d=self.metadata['desc'], + v=self.value, + u=self.metadata['unit'], + q=self.quality, + dt=self.date, + ) + elif self.value == None: + return ''.format(d=self.metadata['desc'], + v=self.value, + q=self.quality, + dt=self.date, + ) + else: + return ''.format(d=self.metadata['desc'], + v=self.value, + u=self.metadata['unit'], + q=self.quality, + dt=self.date, + ) + return + + def __str__(self): + "For print (to user)" + if self.metadata['desc'] == 'Unknown': + return '{data_type:} = {desc:}'.format(desc=self.metadata['desc'], + data_type=self.dtype, + ) + elif type(self.value) == float: + return '{d:} = {v:.1f}{u}'.format(d=self.metadata['desc'], + v=self.value, + u=self.metadata['unit'], + ) + elif self.value == None: + return '{d:} = {v}'.format(d=self.metadata['desc'], + v='NA', + ) + else: + return '{d:} = {v} {u}'.format(d=self.metadata['desc'], + v=self.value, + u=self.metadata['unit'], + ) + + def __call__(self): + return self.value + +# Take a picture +def get_pict(): + with PiCamera() as camera: + camera.resolution = (x_res, y_res) + camera.rotation = photo_rotation + #camera.led = camera_LED # !!! need root privileges !!! + if sense_LED_flash: + sense.show_message(text_string="", + back_colour=[255, 255, 255], + ) + camera.start_preview() + sleep(ajust_time) + sense.set_rotation(r=180, redraw=True) + #sense.show_message(text_string=":-)", + #scroll_speed=0.1 , + #text_colour=[255, 0, 0], + #back_colour=[0, 0, 255]) + + pict_path = path.join(photo_dir, strftime(photo_file) + '.jpg') + camera.capture(pict_path) + sense.clear() + #camera.led = True # !!! need root privileges !!! + + return pict_path + +######## +# MAIN # +######## + +# Collect asked data from sensors +for data_type in data_collection: + data[data_type] = Raw_Data(data_type) + + data[data_type].get_raw() + + if verbose : + print(data[data_type].__repr__()) + print(data[data_type]()) + + #Save data in CSV file + + csvfile_path = path.join(data_dir, data_type + '.csv') + + data[data_type].write_csv(csvfile_path) + +#Take a picture +picture_path = get_pict() + +# Write data on picture in new picture file +pict = Image.open(picture_path) + +draw = ImageDraw.Draw(pict) + # Text line height (=font size) and line space calculation +line_height = int(y_res / 40) +line_space = int(line_height / 10) + +text = '' +l = line_space + +# Add a line with data for each data to display +for d in data_display: + text = str(data[d]) #.__str__() + # Select color and font to match data quality + if data[d].quality == 0: + text_color = (0, 255, 0) # Quality OK : text in green + font = font_default + elif data[d].quality < 0: + text_color = (100, 100, 100) # Unknown in grey + font = font_italic + elif data[d].quality == 1: + text_color = (255, 255, 0) # Sensor unavailable : yellow + font = font_italic + else: + text_color = (255, 0, 0) # Other = error in red + font = font_bold + + img_font = ImageFont.truetype(path.join(fonts_dir, font), line_height) + draw.text((5,l), text, text_color, img_font) + draw = ImageDraw.Draw(pict) + l = l + line_height + line_space + +#Get EXIF metadata and delete embedded thumbnail +pict_exif = piexif.load(pict.info['exif']) +del(pict_exif['thumbnail']) + +pict.save(picture_path, 'jpeg', exif=piexif.dump(pict_exif) ) + +if verbose : + print('Picture = ' + picture_path) + diff --git a/raspberry/pi_dev_sync.sh b/raspberry/pi_dev_sync.sh new file mode 100755 index 0000000..0e7e39a --- /dev/null +++ b/raspberry/pi_dev_sync.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +#Synchronise le répertoire de développement de datacam vers aro-W840 + +rsync --archive --compress --delete --verbose -e ssh pi@aro-pi:~/datacam/* ~/Dev/Python/datacam/ diff --git a/raspberry/serial_module.py b/raspberry/serial_module.py new file mode 100755 index 0000000..1e9daca --- /dev/null +++ b/raspberry/serial_module.py @@ -0,0 +1,69 @@ +#!/usr/bin/python3 +# -*- coding: UTF8 -*- + +# Reception de données depuis un arduino via port série + +version = "0.1" + +#Configuration du port série +port_serie = "/dev/ttyACM1" +#port_serie_alt = "/dev/ttyACM1" + +baud_rate = 115200 + +############# +import serial +from pprint import pprint + +str_line = [] #une ligne de donnée lue sur le port série et prétraitée +data = dict() #une donnée d'un capteur +data_package = dict() #un paquet de donnée à un moment +data_set = dict() #ensemble des paquets de données + +with serial.Serial(port_serie, baud_rate) as comm: #, timeout = 0 + + print("Initialization du microcontroleur en cours...") + + while True: + line = comm.readline() + if line == b'###Init_end###\r\n' : + break + else: + print(".") + + while True: + line = comm.readline() + if line == b'###Data_start###\r\n' : + break + else: + print("En attente d'un nouveau paquet de données...") + + while True: + line = comm.readline() + if line == b'###Data_start###\r\n' : + print("Nouveau paquet de données !") + #break + elif line == b'###Data_end###\r\n' : + print("Fin du paquet de données !") + else: + #Nettoyage des caractères spéciaux inutile (retour chariot...) et + # conversion en liste de chaînes de caractères + str_line = line.decode("ascii").strip().split('\t') + #Mise en tableau de données + data['type'] = str_line[0].strip() + data['sensor'] = str_line[1].strip() + data['value'] = str_line[2].strip() + if len(str_line) > 3 and str_line[3].strip() != "-": + data['unit'] = str_line[3].strip() + else: + data['unit'] = None + + if len(str_line) > 4: + data['comment'] = str_line[4] + else: + data['comment'] = None + + data_package[data['type'] + "_" + data['sensor']] = data + data = {} + + pprint(data_package) \ No newline at end of file