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
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
raspberry/data/*
raspberry/pictures/*
*.fcstd1
*.fcstd2
boitier/old*
.directory

4
arduino/SDcard/config.txt Executable file
View File

@ -0,0 +1,4 @@
#Fichier de configuration du contrôleur du projet
# Camétéo
time_step=2000
data_file=datalog3.csv

View File

@ -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
}
}

View File

@ -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);
}

View File

@ -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

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);
}

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)

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.

BIN
arduino/libraries/TCN75A.zip Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
boitier/camera+BME280.fcstd Executable file

Binary file not shown.

Binary file not shown.

BIN
boitier/hdmicase_bottom.stl Executable file

Binary file not shown.

BIN
boitier/hdmicase_top.stl Executable file

Binary file not shown.

BIN
boitier/structure.fcstd Executable file

Binary file not shown.

BIN
doc/cablage-teensy.ods Executable file

Binary file not shown.

170
doc/cameteo.mm Executable file
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
doc/connections RPi-Arduino.fzz Executable file

Binary file not shown.

BIN
doc/connections RPi-teensy.fzz Executable file

Binary file not shown.

Binary file not shown.

361
raspberry/cameteo-rpi.py Executable file
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
raspberry/datacam.py Executable file
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
raspberry/pi_dev_sync.sh Executable file
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
raspberry/serial_module.py Executable file
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)