Initial commit

This commit is contained in:
Pierrick C
2017-07-08 18:08:54 +02:00
commit e9c20be21c
31 changed files with 2159 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
raspberry/data/*
raspberry/pictures/*
*.fcstd1
*.fcstd2
boitier/old*
.directory
+4
View File
@@ -0,0 +1,4 @@
#Fichier de configuration du contrôleur du projet
# Camétéo
time_step=2000
data_file=datalog3.csv
@@ -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
}
}
@@ -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);
}
@@ -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 <Wire.h> // library used with I2C protocol
#include <SPI.h> // SPI protocol
//Teensy3.x Real Time Clock
#include <TimeLib.h>
//SD card
#include <SD.h>
// Sensors
#include <Adafruit_Sensor.h> // Generic
//#include <DHT.h> // DHT22
//#include <DHT_U.h> // DHT22 unified
//#include <Adafruit_BMP085_U.h> // BMP180
#include <Adafruit_BME280.h> // BME280
#include <PWFusion_AS3935.h>
//GPS
//#include <Adafruit_GPS.h> // Adafruit Ultimate GPS
#include <TinyGPS.h> //Builtin GPS lib
+399
View File
@@ -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 <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);
}
+97
View File
@@ -0,0 +1,97 @@
/*
* 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 <Wire.h> // library used with I2C protocol
#include <SPI.h> // SPI protocol
//Teensy3.x Real Time Clock
#include <TimeLib.h>
//SD card
#include <SD.h>
// Sensors
#include <Adafruit_Sensor.h> // Generic
//#include <DHT.h> // DHT22
//#include <DHT_U.h> // DHT22 unified
//#include <Adafruit_BMP085_U.h> // BMP180
#include <Adafruit_BME280.h> // BME280
#include <PWFusion_AS3935.h>
//GPS
//#include <Adafruit_GPS.h> // Adafruit Ultimate GPS
#include <TinyGPS.h> //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)
+553
View File
@@ -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 <Wire.h> // library used with I2C protocol
#include <SPI.h> // SPI protocol
//Teensy3.x Real Time Clock
#include <TimeLib.h>
//SD card
#include <SD.h>
// Sensors
#include <Adafruit_Sensor.h> // Generic
#include <Adafruit_BME280.h> // BME280
#include <PWFusion_AS3935.h>
//GPS
//#include <Adafruit_GPS.h> // Adafruit Ultimate GPS
#include <TinyGPS.h> //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<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);BME280_Temperature(degC);BME280_Humidity(%);BME280_Altitude(m)");
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;%.0f;%.1f", bme280_press, bme280_temp, bme280_humi, bme280_alti);
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_Humidty(%);BME280_Altitude(m)");
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;%.0f;%.1f", bme280_press, bme280_temp, bme280_humi, bme280_alti);
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);
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Executable
+170
View File
@@ -0,0 +1,170 @@
<map version="freeplane 1.2.0">
<!--To view this file, download free mind mapping software Freeplane from http://freeplane.sourceforge.net -->
<node TEXT="datacam" ID="ID_940180243" CREATED="1445275007181" MODIFIED="1445275113016"><hook NAME="MapStyle">
<map_styles>
<stylenode LOCALIZED_TEXT="styles.root_node">
<stylenode LOCALIZED_TEXT="styles.predefined" POSITION="right">
<stylenode LOCALIZED_TEXT="default" MAX_WIDTH="600" COLOR="#000000" STYLE="as_parent">
<font NAME="SansSerif" SIZE="10" BOLD="false" ITALIC="false"/>
</stylenode>
<stylenode LOCALIZED_TEXT="defaultstyle.details"/>
<stylenode LOCALIZED_TEXT="defaultstyle.note"/>
<stylenode LOCALIZED_TEXT="defaultstyle.floating">
<edge STYLE="hide_edge"/>
<cloud COLOR="#f0f0f0" SHAPE="ROUND_RECT"/>
</stylenode>
</stylenode>
<stylenode LOCALIZED_TEXT="styles.user-defined" POSITION="right">
<stylenode LOCALIZED_TEXT="styles.topic" COLOR="#18898b" STYLE="fork">
<font NAME="Liberation Sans" SIZE="10" BOLD="true"/>
</stylenode>
<stylenode LOCALIZED_TEXT="styles.subtopic" COLOR="#cc3300" STYLE="fork">
<font NAME="Liberation Sans" SIZE="10" BOLD="true"/>
</stylenode>
<stylenode LOCALIZED_TEXT="styles.subsubtopic" COLOR="#669900">
<font NAME="Liberation Sans" SIZE="10" BOLD="true"/>
</stylenode>
<stylenode LOCALIZED_TEXT="styles.important">
<icon BUILTIN="yes"/>
</stylenode>
</stylenode>
<stylenode LOCALIZED_TEXT="styles.AutomaticLayout" POSITION="right">
<stylenode LOCALIZED_TEXT="AutomaticLayout.level.root" COLOR="#000000">
<font SIZE="18"/>
</stylenode>
<stylenode LOCALIZED_TEXT="AutomaticLayout.level,1" COLOR="#0033ff">
<font SIZE="16"/>
</stylenode>
<stylenode LOCALIZED_TEXT="AutomaticLayout.level,2" COLOR="#00b439">
<font SIZE="14"/>
</stylenode>
<stylenode LOCALIZED_TEXT="AutomaticLayout.level,3" COLOR="#990000">
<font SIZE="12"/>
</stylenode>
<stylenode LOCALIZED_TEXT="AutomaticLayout.level,4" COLOR="#111111">
<font SIZE="10"/>
</stylenode>
</stylenode>
</stylenode>
</map_styles>
</hook>
<node TEXT="Traitement donn&#xe9;es" POSITION="right" ID="ID_1847430113" CREATED="1445275240369" MODIFIED="1445275249403">
<node TEXT="Python (Rasp. Pi)" ID="ID_270658498" CREATED="1445275265758" MODIFIED="1447080486986">
<node TEXT="class Raw_Data" ID="ID_1595052898" CREATED="1445275279482" MODIFIED="1447080748503">
<icon BUILTIN="button_ok"/>
</node>
<node TEXT="class Calculated_Data" ID="ID_718485900" CREATED="1445275812612" MODIFIED="1447080463621"/>
<node TEXT="Fonction : Ajout des donn&#xe9;es &#xe0; une photo" ID="ID_627725476" CREATED="1445275595946" MODIFIED="1445275873150">
<node TEXT="Incrustation" ID="ID_733470856" CREATED="1445275875550" MODIFIED="1445275883425">
<node TEXT="choix ordre" ID="ID_1297866337" CREATED="1445275719545" MODIFIED="1445275726053"/>
<node TEXT="choix donn&#xe9;es affich&#xe9;e" ID="ID_971249184" CREATED="1445275726924" MODIFIED="1445275736408"/>
</node>
<node TEXT="Metadonn&#xe9;e EXIF et/ou XMP" ID="ID_908814226" CREATED="1445275885056" MODIFIED="1445275898061"/>
</node>
<node TEXT="Croisement avec donn&#xe9;es publiques" ID="ID_816020813" CREATED="1447081168096" MODIFIED="1447081190356">
<node TEXT="Sources" ID="ID_1534956111" CREATED="1447081192190" MODIFIED="1447081202255">
<node TEXT="M&#xe9;t&#xe9;oFrance" ID="ID_960146793" CREATED="1447081205560" MODIFIED="1447081210611"/>
<node TEXT="NOAA (GFS / WRF)" ID="ID_484693743" CREATED="1447081217776" MODIFIED="1447081234298"/>
</node>
<node TEXT="Traitements" ID="ID_1239862080" CREATED="1447081274440" MODIFIED="1447081284372">
<node TEXT="Recalcul des altitudes (croisement pressions mesur&#xe9;e par datacam/pression organisme m&#xe9;t&#xe9;o/ coordonn&#xe9;es GPS)" ID="ID_3490593" CREATED="1447081287034" MODIFIED="1447081341959"/>
<node TEXT="Avertissement en cas de divergence importantes avec pr&#xe9;visions" ID="ID_1887237474" CREATED="1447081368574" MODIFIED="1447081389978"/>
</node>
</node>
</node>
<node TEXT="Embarqu&#xe9; Arduino" ID="ID_785409314" CREATED="1447080488757" MODIFIED="1447080501360">
<node TEXT="Sur p&#xe9;riode" ID="ID_1086659337" CREATED="1447080504005" MODIFIED="1447080515713">
<node TEXT="Moyenne" ID="ID_1592586385" CREATED="1447080517032" MODIFIED="1447080519825"/>
<node TEXT="Ecart-type" ID="ID_592958817" CREATED="1447080521647" MODIFIED="1447080531113"/>
<node TEXT="Quantiles" ID="ID_1704132109" CREATED="1447080532182" MODIFIED="1447080704522"/>
</node>
</node>
</node>
<node TEXT="Acquisition photo" POSITION="left" ID="ID_205007013" CREATED="1445275114354" MODIFIED="1445275124232">
<node TEXT="PiCam" ID="ID_471792515" CREATED="1445275165918" MODIFIED="1445275170779">
<node TEXT="Probl&#xe8;me 1 : cable plat tr&#xe8;s peu pratique" ID="ID_279048394" CREATED="1447417532604" MODIFIED="1447417604312"/>
<node TEXT="Probl&#xe8;me 2 : connection tr&#xe8;s sensible (facilement des probl&#xe8;mes de d&#xe9;tection" ID="ID_179270345" CREATED="1447417551524" MODIFIED="1447417586481"/>
</node>
<node TEXT="gPhoto2" ID="ID_714902196" CREATED="1445275175392" MODIFIED="1445275366515">
<icon BUILTIN="help"/>
</node>
<node TEXT="webcam USB" ID="ID_598071482" CREATED="1445703078725" MODIFIED="1445703092031">
<icon BUILTIN="help"/>
</node>
</node>
<node TEXT="Autonomie" POSITION="right" ID="ID_1591208209" CREATED="1445275213800" MODIFIED="1445275219743">
<node TEXT="UI" ID="ID_1359812832" CREATED="1445275221705" MODIFIED="1445275225795"/>
<node TEXT="Energie" ID="ID_1154122970" CREATED="1445275227314" MODIFIED="1445275235842">
<node TEXT="Allumage R-Pi par Arduino" ID="ID_317304726" CREATED="1445275454078" MODIFIED="1445275473445"/>
<node TEXT="Batterie tampon" ID="ID_266523548" CREATED="1445275481734" MODIFIED="1445275494432"/>
<node TEXT="Alimentation" ID="ID_1650279501" CREATED="1445275498672" MODIFIED="1445275508689">
<node TEXT="Panneau solaire" ID="ID_62589468" CREATED="1445275510537" MODIFIED="1445275517145"/>
<node TEXT="Dynamo v&#xe9;lo" ID="ID_187725160" CREATED="1445275518067" MODIFIED="1445275524512"/>
<node TEXT="Batterie grosse capacit&#xe9;" ID="ID_538264726" CREATED="1445275525409" MODIFIED="1445275544191"/>
</node>
</node>
<node TEXT="Boitier" ID="ID_378357561" CREATED="1445275554462" MODIFIED="1445275968197">
<node TEXT="r&#xe9;sistance &#xe0; l&apos;eau" ID="ID_1281039465" CREATED="1445275970340" MODIFIED="1445703138475">
<node TEXT="Id&#xe9;e 1 : impression 3D et/ou d&#xe9;coupe laser" ID="ID_77132726" CREATED="1447417185297" MODIFIED="1447417208635"/>
<node TEXT="Id&#xe9;e 2 : format bidon de cycliste (recyclage)" ID="ID_304162178" CREATED="1447417209434" MODIFIED="1447417236747"/>
</node>
<node TEXT="connectique" ID="ID_1602685639" CREATED="1445275980897" MODIFIED="1445275986634">
<node TEXT="alimentation" ID="ID_1585946812" CREATED="1445703179449" MODIFIED="1445703189353">
<node TEXT="panneau solaire" ID="ID_721407548" CREATED="1447417254583" MODIFIED="1447417273699"/>
<node TEXT="dynamo-v&#xe9;lo" ID="ID_629164198" CREATED="1447417278160" MODIFIED="1447417349222">
<node TEXT="prise USB classique (version &#xe9;tanche ???)" ID="ID_761396973" CREATED="1447417354551" MODIFIED="1447417370095"/>
<node TEXT="adaptateur direct vers la prise &#xe9;tanche existante" ID="ID_632440803" CREATED="1447417370816" MODIFIED="1447417410266"/>
</node>
</node>
<node TEXT="d&#xe9;port capteurs" ID="ID_574169682" CREATED="1445703190116" MODIFIED="1445703200861"/>
<node TEXT="d&#xe9;port acquisition photo" ID="ID_990319349" CREATED="1445703201468" MODIFIED="1445703213407"/>
</node>
<node TEXT="fixation" ID="ID_1152348868" CREATED="1445275993576" MODIFIED="1445275996969">
<node TEXT="v&#xe9;lo" ID="ID_311023012" CREATED="1445275998224" MODIFIED="1445276000394"/>
<node TEXT="rando" ID="ID_1679144491" CREATED="1445276001346" MODIFIED="1445276003720"/>
<node TEXT="tr&#xe9;pied photo" ID="ID_1795302368" CREATED="1445276004510" MODIFIED="1445276016077"/>
</node>
</node>
</node>
<node TEXT="Acquisition donn&#xe9;es" POSITION="left" ID="ID_1804667401" CREATED="1445275093295" MODIFIED="1445275570673">
<node TEXT="PiSense" ID="ID_684279078" CREATED="1445275152767" MODIFIED="1447080440543">
<icon BUILTIN="button_ok"/>
</node>
<node TEXT="Arduino" ID="ID_1198550772" CREATED="1445275194375" MODIFIED="1445275205644">
<node TEXT="Formation" ID="ID_1233017322" CREATED="1445275253081" MODIFIED="1447080380438">
<icon BUILTIN="button_ok"/>
</node>
<node TEXT="Mat&#xe9;riel" ID="ID_926052951" CREATED="1445275297254" MODIFIED="1445275304641">
<node TEXT="RTC" ID="ID_641589009" CREATED="1445275304644" MODIFIED="1447080383273">
<icon BUILTIN="button_ok"/>
</node>
<node TEXT="microSD" ID="ID_1775616220" CREATED="1445275316525" MODIFIED="1447080406240">
<icon BUILTIN="button_ok"/>
</node>
<node TEXT="Temperature" ID="ID_1702799092" CREATED="1445275374067" MODIFIED="1447080408256">
<icon BUILTIN="button_ok"/>
</node>
<node TEXT="Pression" ID="ID_1448376352" CREATED="1445275384406" MODIFIED="1447080411064">
<icon BUILTIN="button_ok"/>
</node>
<node TEXT="Humidit&#xe9;" ID="ID_1888552872" CREATED="1445275387795" MODIFIED="1447080412960">
<icon BUILTIN="button_ok"/>
</node>
<node TEXT="GPS" ID="ID_1146102034" CREATED="1445275903847" MODIFIED="1451130798717">
<icon BUILTIN="button_ok"/>
</node>
<node TEXT="foudre" ID="ID_1917227809" CREATED="1445703116512" MODIFIED="1445703124790"/>
<node TEXT="Autre ?" ID="ID_1708926810" CREATED="1445275394108" MODIFIED="1445275397833"/>
</node>
<node TEXT="Logiciel" ID="ID_521818033" CREATED="1447080606285" MODIFIED="1447080611344">
<node TEXT="Donn&#xe9;es calcul&#xe9;es" ID="ID_238205261" CREATED="1447080619816" MODIFIED="1447080630924">
<node TEXT="Altitudes/Pression au niveau de la mer" ID="ID_145976984" CREATED="1447080633632" MODIFIED="1447080658177"/>
<node TEXT="Humidit&#xe9; / Point de ros&#xe9;e / Contenu en eau" ID="ID_253996014" CREATED="1447080661740" MODIFIED="1447080683496"/>
</node>
<node TEXT="Unification des capteur avec lib. Adafruit Sensors" ID="ID_1043083145" CREATED="1447080689744" MODIFIED="1447080736111"/>
</node>
</node>
</node>
</node>
</map>
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
+361
View File
@@ -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 '<Data {t:} = {d:} at {dt}>'.format(d=self.metadata['desc'],
t=self.dtype,
dt=self.date,
)
elif type(self.value) == float:
return '<Data {d:} = {v:.1f}{u} ({q}) at {dt}>'.format(d=self.metadata['desc'],
v=self.value,
u=self.metadata['unit'],
q=self.quality,
dt=self.date,
)
elif self.value == None:
return '<Data {d:} = {v} ({q}) at {dt}>'.format(d=self.metadata['desc'],
v=self.value,
q=self.quality,
dt=self.date,
)
else:
return '<Data {d:} = {v} {u} ({q}) at {dt}>'.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)
+362
View File
@@ -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 '<Data {t:} = {d:} at {dt}>'.format(d=self.metadata['desc'],
t=self.dtype,
dt=self.date,
)
elif type(self.value) == float:
return '<Data {d:} = {v:.1f}{u} ({q}) at {dt}>'.format(d=self.metadata['desc'],
v=self.value,
u=self.metadata['unit'],
q=self.quality,
dt=self.date,
)
elif self.value == None:
return '<Data {d:} = {v} ({q}) at {dt}>'.format(d=self.metadata['desc'],
v=self.value,
q=self.quality,
dt=self.date,
)
else:
return '<Data {d:} = {v} {u} ({q}) at {dt}>'.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)
+5
View File
@@ -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/
+69
View File
@@ -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)