From 01ed6e1658a9bee515f45612db1485fe8a1b5e39 Mon Sep 17 00:00:00 2001 From: Pierrick C Date: Sat, 28 Jul 2018 18:42:27 +0200 Subject: [PATCH] Start to move from Arduino (C/C++) to MicroPython / CircuitPython (teensy=>Feather M0 express) --- arduino/SDcard/config.txt | 4 - .../cameteo-teensy/RaspBerryPi_COM.cpp | 42 -- .../cameteo-teensy/SerialMessages.cpp | 55 -- .../cameteo-teensy/cameteo-teensy.ino | 36 -- .../cameteo-teensy/cameteo.cpp | 399 ------------- .../cameteo-teensy/cameteo_teensy.h | 97 --- arduino/cameteo-teensy/cameteo-teensy.ino | 553 ------------------ .../mqtt_esp8266_BME280.ino | 306 ---------- circuitpython/boot.py | 17 + circuitpython/lib/adafruit_bme280.mpy | Bin 0 -> 5422 bytes .../lib/adafruit_bus_device/__init__.py | 0 .../lib/adafruit_bus_device/i2c_device.mpy | Bin 0 -> 1103 bytes .../lib/adafruit_bus_device/spi_device.mpy | Bin 0 -> 1250 bytes circuitpython/lib/adafruit_gps.mpy | Bin 0 -> 3874 bytes circuitpython/lib/neopixel.mpy | Bin 0 -> 3645 bytes circuitpython/main.py | 126 ++++ doc/cablage-teensy.ods | Bin 11514 -> 0 bytes doc/connections RPi-Arduino.fzz | Bin 25688 -> 0 bytes doc/connections RPi-teensy.fzz | Bin 19954 -> 0 bytes 19 files changed, 143 insertions(+), 1492 deletions(-) delete mode 100755 arduino/SDcard/config.txt delete mode 100755 arduino/cameteo-teensy-bug/cameteo-teensy/RaspBerryPi_COM.cpp delete mode 100755 arduino/cameteo-teensy-bug/cameteo-teensy/SerialMessages.cpp delete mode 100755 arduino/cameteo-teensy-bug/cameteo-teensy/cameteo-teensy.ino delete mode 100755 arduino/cameteo-teensy-bug/cameteo-teensy/cameteo.cpp delete mode 100755 arduino/cameteo-teensy-bug/cameteo-teensy/cameteo_teensy.h delete mode 100755 arduino/cameteo-teensy/cameteo-teensy.ino delete mode 100644 arduino/mqtt_esp8266_BME280/mqtt_esp8266_BME280.ino create mode 100644 circuitpython/boot.py create mode 100644 circuitpython/lib/adafruit_bme280.mpy create mode 100644 circuitpython/lib/adafruit_bus_device/__init__.py create mode 100644 circuitpython/lib/adafruit_bus_device/i2c_device.mpy create mode 100644 circuitpython/lib/adafruit_bus_device/spi_device.mpy create mode 100644 circuitpython/lib/adafruit_gps.mpy create mode 100644 circuitpython/lib/neopixel.mpy create mode 100644 circuitpython/main.py delete mode 100755 doc/cablage-teensy.ods delete mode 100755 doc/connections RPi-Arduino.fzz delete mode 100755 doc/connections RPi-teensy.fzz diff --git a/arduino/SDcard/config.txt b/arduino/SDcard/config.txt deleted file mode 100755 index 4f30564..0000000 --- a/arduino/SDcard/config.txt +++ /dev/null @@ -1,4 +0,0 @@ -#Fichier de configuration du contrôleur du projet -# Camétéo -time_step=2000 -data_file=datalog3.csv \ No newline at end of file diff --git a/arduino/cameteo-teensy-bug/cameteo-teensy/RaspBerryPi_COM.cpp b/arduino/cameteo-teensy-bug/cameteo-teensy/RaspBerryPi_COM.cpp deleted file mode 100755 index 08976cc..0000000 --- a/arduino/cameteo-teensy-bug/cameteo-teensy/RaspBerryPi_COM.cpp +++ /dev/null @@ -1,42 +0,0 @@ - -#include "cameteo_teensy.h" - -void stopRPI() { - //Stop the Raspberry Pi via the MOSFET (grid connected to the RPI_PWR_PIN) - - pinMode(RPI_PWR_PIN, INPUT); //Eteint via le MOSFET - rpi_status = false; -} - -bool isStartedPI() { - //Return true if the R-Pi respond to an simple request - return rpi_status; -} - -void startRPI() { - //Start the Raspberry Pi via the MOSFET (grid connected to the RPI_PWR_PIN) - if (!isStartedPI()) { - pinMode(RPI_PWR_PIN, OUTPUT); - digitalWrite(RPI_PWR_PIN, LOW); - rpi_status = true; - } -} - -void sendDataToSerial(String data) { - if (isStartedPI()) { - char c[100]; // char buffer for conversion String->char - data.toCharArray(c, sizeof(data)); //convert data string to char array - -// //start serial comm. if needed -// if(!SERIAL_PORT) { -// SERIAL_PORT.begin(SERIAL_BAUD_RATE); -// //while(!SERIAL_PORT); -// } - - SERIAL_PORT.print(c); // send data on serial port - - } - else { - //error RPI is not started - } -} diff --git a/arduino/cameteo-teensy-bug/cameteo-teensy/SerialMessages.cpp b/arduino/cameteo-teensy-bug/cameteo-teensy/SerialMessages.cpp deleted file mode 100755 index f70c4a2..0000000 --- a/arduino/cameteo-teensy-bug/cameteo-teensy/SerialMessages.cpp +++ /dev/null @@ -1,55 +0,0 @@ - -#include "cameteo_teensy.h" - -void BootMessage(String s) { - char c[13]; // char buffer for conversion String->char - s.toCharArray(c, sizeof(s)); - SERIAL_PORT.printf("%-12s", c); -} - -void BootOK() { - SERIAL_PORT.println("OK"); -} - -void BootError() { - SERIAL_PORT.println("EE"); -} - -void sendDataOnSerial() { - - //Print date & hour on serial port - SERIAL_PORT.printf("%04d/%02d/%02d_%02d:%02d:%02d\n", year(), month(), day(), hour(), minute(), second()); - -// if (isnan(dht22_event_hum.relative_humidity) || -// isnan(dht22_event_temp.temperature)) { -// SERIAL_PORT.printf("Failed to read from DHT sensor!\n"); -// } -// else { -// SERIAL_PORT.printf("Temperature:%8.2f %cC | Humidity:%8.0f %%\n", -// dht22_event_temp.temperature, DEGREE, dht22_event_hum.relative_humidity); -// } - -// if (bme280_event.pressure) { - SERIAL_PORT.printf("Temperature:%8.2f %cC | Humidity: %8.2f % | Pressure: %8.2f hPa | Altitude:%8.2f m\n", - bme280_temp, DEGREE, bme280_press, bme280_alti); -// } -// else { -// SERIAL_PORT.printf("BME280 Sensor error\n"); -// } - - SERIAL_PORT.printf("Lightning strikes (from start) : %d | Perturb.: %d | Noise : %d | Unknown detect.: %d\n", lightning_nb_total, - as3935_perturb_total, - as3935_noise_total, - as3935_unknown_total); - - //SERIAL_PORT.printf("Temperature:%8.2f %cC\n", tcn75a_temp, DEGREE); - - SERIAL_PORT.printf("GPS data: %04d/%02d/%02d_%02d:%02d:%02d (%d)\n", gps_year, gps_month, gps_day, - gps_hour, gps_minutes, gps_second, - gps_fix_age); - SERIAL_PORT.printf(" Latitude: %11.8f | Longitude: %11.8f | Altitude: %5.2f m\n", gps_latitude, gps_longitude, gps_altitude); - SERIAL_PORT.printf(" Speed: %4.1f km/h | Course : %4.1f %c\n", gps_speed, gps_course, DEGREE); - SERIAL_PORT.printf(" Chars: %11d | Sentences: %11d | Failed cheksum: %4d\n", gps_chars, gps_sentences, gps_failed_checksum); - - SERIAL_PORT.printf("Battery : %10d mV | Low Battery : %d\n", batt_voltage, low_battery_flag); -} diff --git a/arduino/cameteo-teensy-bug/cameteo-teensy/cameteo-teensy.ino b/arduino/cameteo-teensy-bug/cameteo-teensy/cameteo-teensy.ino deleted file mode 100755 index 3bf0cf6..0000000 --- a/arduino/cameteo-teensy-bug/cameteo-teensy/cameteo-teensy.ino +++ /dev/null @@ -1,36 +0,0 @@ -/* - * CAMETEO project - * - * This is a personnal project of weather station with - * automatic photo taking. - * This code is the weather station part and is meant - * to be run on a PJRC Teensy 3.2 board. - * - * Author : Arofarn - * - * Licence : GPL v3 - * - */ - -//Protocols -#include // library used with I2C protocol -#include // SPI protocol - -//Teensy3.x Real Time Clock -#include - -//SD card -#include - -// Sensors -#include // Generic -//#include // DHT22 -//#include // DHT22 unified -//#include // BMP180 -#include // BME280 -#include - -//GPS -//#include // Adafruit Ultimate GPS -#include //Builtin GPS lib - diff --git a/arduino/cameteo-teensy-bug/cameteo-teensy/cameteo.cpp b/arduino/cameteo-teensy-bug/cameteo-teensy/cameteo.cpp deleted file mode 100755 index 6ca1aca..0000000 --- a/arduino/cameteo-teensy-bug/cameteo-teensy/cameteo.cpp +++ /dev/null @@ -1,399 +0,0 @@ -/* - * CAMETEO project - * - * This is a personnal project of weather station with - * automatic photo taking. - * This code is the weather station part and is meant - * to be run on a PJRC Teensy 3.2 board. - * - * Author : Arofarn - * - * Licence : GPL v3 - * - */ - -#include - -#include "cameteo_teensy.h" -#include "SerialMessages.cpp" -#include "RaspBerryPi_COM.cpp" - -//sensors_event_t bme280_event; -float seaLevelPressure = 1015.0; -float bme280_press; -float bme280_temp; -float bme280_alti; -float bme280_hum; - -enum strike_sources { UNKNOWN_SRC, LIGHTNING, PERTURBATION, NOISE }; -volatile int8_t AS3935_ISR_Trig = 0; // Trigger for AS3935 lightning sensor - -void AS3935_ISR() { - AS3935_ISR_Trig = 1; -} - -PWF_AS3935 lightning(AS3935_CS_PIN, AS3935_IRQ_PIN, 33); - -int as3935_src; -int as3935_distance; -long lightning_nb_total = 0; -int lightning_nb_hour = 0; -int lightning_nb_day = 0; -int as3935_perturb_total = 0; -int as3935_noise_total = 0; -int as3935_unknown_total = 0; -char lightning_log_file[12] = "lghtnng.log"; - -//GPS -//Adafruit_GPS GPS(&GPS_SERIAL_PORT); -TinyGPS GPS; -float gps_latitude, gps_longitude, gps_altitude; // returns +- latitude/longitude in degrees -float gps_speed, gps_course; -unsigned long gps_time, gps_date, gps_fix_age; -int gps_year; -byte gps_month, gps_day, gps_hour, gps_minutes, gps_second, gps_hundreths; -unsigned long gps_chars, gps_hdop; -unsigned short gps_sentences, gps_failed_checksum, gps_satellites; - -//Miscellaneous -bool rpi_status; -rpi_status = false; -int batt_voltage; -bool low_battery_flag = false; - -// Tasks timers -elapsedMillis since_bme280; -//elapsedMillis since_dht; -elapsedMillis since_gps; -elapsedMillis since_display; -elapsedMillis since_serial_send; -elapsedMillis since_sd_log; -elapsedMillis since_batt_chk; - -//SD Card -#define CONFIGFILE "config.txt" - -//Configuration -char data_file[13] = "datalog.csv"; - - //Delays - -unsigned int bme280_delay = 500; -//unsigned int dht_delay = 3000; -unsigned int gps_delay = 100; -unsigned int serial_send_delay = 5000; -unsigned int sd_log_delay = 5000; -unsigned int batt_chk_delay = 5000; - -//Date and time -int TZ = 1; //Time zone - -/* - * SETUP - */ - -void setup() { - - //To be sure Raspberry-Pi won't be turned on unexpectedly - stopRPI(); - - pinMode(LOW_BATT_PIN, INPUT_PULLUP); - low_battery_flag = !digitalRead(LOW_BATT_PIN); - - pinMode(GPS_BUT_PIN, INPUT_PULLUP); - pinMode(GPS_EN_PIN, OUTPUT); - gps_power(); - - SERIAL_PORT.begin(SERIAL_BAUD_RATE); - delay(1000); - //while (!SERIAL_PORT) { }; //Wait for serial port to start - SERIAL_PORT.printf("Serial Comm. OK\nNow booting...\n"); - - BootMessage("SDcard"); - // see if the card is present and can be initialized: - if (!SD.begin(SD_CS_PIN)) { - BootError(); - while(1); - } - BootOK(); - - BootMessage("RT Clock"); - // set the Time library to use Teensy 3.0's RTC to keep time - setSyncProvider(getTeensy3Time); - if (timeStatus()!= timeSet) { - BootError(); - while(1); - } - BootOK(); - -// BootMessage("AM2302/DHT22 (Humidity and Temperature)"); -// dht.begin(); -// //sensor_t sensor; -// BootOK(); - - BootMessage("BME280 (Pressure and Temperature)"); - /* Initialise the sensor */ - if(!bme.begin()) - { - /* There was a problem detecting the BMP085 ... check your connections */ - BootError(); - while(1); - } - BootOK(); - - BootMessage("AS3935 (Lightning)"); - SPI.begin(); - SPI.setClockDivider(SPI_CLOCK_DIV16); // SPI speed to SPI_CLOCK_DIV16/1MHz (max 2MHz, NEVER 500kHz!) - SPI.setDataMode(SPI_MODE1); // MAX31855 is a Mode 1 device --> clock starts low, read on rising edge - SPI.setBitOrder(MSBFIRST); // data sent to chip MSb first - lightning.AS3935_DefInit(); // set registers to default - // now update sensor cal for your application and power up chip - lightning.AS3935_ManualCal(AS3935_CAPACITANCE, AS3935_INDOORS, AS3935_DIST_EN); - // enable interrupt (hook IRQ pin to Arduino Uno/Mega interrupt input: 0 -> pin 2, 1 -> pin 3 ) - attachInterrupt(AS3935_IRQ_PIN, AS3935_ISR, RISING); - BootOK(); - - BootMessage("GPS"); - GPS_SERIAL_PORT.begin(GPS_SERIAL_BAUD_RATE); - while (!GPS_SERIAL_PORT) {} ; - BootOK(); - -} - -/* - * LOOP - */ - -void loop() { - - //Power ON/OFF GPS - gps_power(); - - //Lightning detection - if (AS3935_ISR_Trig != 0) { - AS3935_ISR_Trig = 0; - time_t t = now(); - int distance = -9999; - int energy = -9999; - switch (as3935_src) { - case UNKNOWN_SRC: - //source inconnue - as3935_unknown_total++; - SERIAL_PORT.printf("Interruption (AS3935) : unkown source (not lightning)\n"); - break; - case LIGHTNING: - //Foudre !!! - lightning_nb_total++; - distance = lightning.AS3935_GetLightningDistKm(); - energy = lightning.AS3935_GetStrikeEnergyRaw(); - SERIAL_PORT.printf("Interruption (AS3935) : Lightningbolt !!!\n"); - SERIAL_PORT.printf("Distance : %4d km | Energy : %d \n", distance, energy); - break; - case PERTURBATION: - //Perturbation - as3935_perturb_total++; - SERIAL_PORT.printf("Interruption (AS3935) : perturbation...\n"); - break; - case NOISE: - //Trop de bruit électromagnétique - as3935_noise_total++; - SERIAL_PORT.printf("Interruption (AS3935) : Too much electromagnetic noise!\n"); - break; - } - writeLightningToSD(as3935_src, t, distance, energy); - } - - if (since_batt_chk >= batt_chk_delay) { - since_batt_chk -= batt_chk_delay; - //Check battery status and voltage - low_battery_flag = !digitalRead(LOW_BATT_PIN); - batt_voltage = getBatteryVoltage(); - } - -// if (since_dht >= dht_delay) { -// since_dht = since_dht - dht_delay; -// // Read temperature or humidity -// dht.humidity().getEvent(&dht22_event_hum); -// dht.temperature().getEvent(&dht22_event_temp); -// } - - if (since_bme280 >= bme280_delay) { - since_bme280 = since_bme280 - bme280_delay; - /* Get a new sensor event */ - bmp.getEvent(&bme280_event); - - /* Get the values (barometric pressure is measure in hPa) */ - if (bme280_event.pressure) - { - bme280_press = bme280_event.pressure; - bmp.getTemperature(&bme280_temp); - bme280_alti = bmp.pressureToAltitude(seaLevelPressure, bme280_press); - } - } - - if (since_gps >= gps_delay) { - since_gps = since_gps - gps_delay; - while (GPS_SERIAL_PORT.available()) { - char c = GPS_SERIAL_PORT.read(); - if (GPS.encode(c)) { - GPS.get_datetime(&gps_date, &gps_time, &gps_fix_age); - GPS.crack_datetime(&gps_year, &gps_month, &gps_day, - &gps_hour, &gps_minutes, &gps_second, - &gps_hundreths, &gps_fix_age); - GPS.f_get_position(&gps_latitude, &gps_longitude, &gps_fix_age); - gps_altitude = GPS.f_altitude(); - gps_speed = GPS.f_speed_kmph(); - GPS.stats(&gps_chars, &gps_sentences, &gps_failed_checksum); - gps_satellites = GPS.satellites(); - gps_hdop = GPS.hdop(); - } - } - } - - if (since_sd_log >= sd_log_delay) { - since_sd_log = since_sd_log - sd_log_delay; - writeDataToSD(); - } - - if (since_serial_send >= serial_send_delay) { - since_serial_send = since_serial_send - serial_send_delay; - sendDataOnSerial(); - } - -} - -/* - * FUNCTIONS - */ - -time_t getTeensy3Time() { - return Teensy3Clock.get(); -} - -void gps_power() { - if (digitalRead(GPS_BUT_PIN) == 0) { digitalWrite(GPS_EN_PIN, LOW); } - else { digitalWrite(GPS_EN_PIN, HIGH); } -} - -int getBatteryVoltage() { - long mean = 0; - int U = 0; - int n = 10; - int res = 12; - - analogReadResolution(res); - - for (int i=0; i // library used with I2C protocol -#include // SPI protocol - -//Teensy3.x Real Time Clock -#include - -//SD card -#include - -// Sensors -#include // Generic -//#include // DHT22 -//#include // DHT22 unified -//#include // BMP180 -#include // BME280 -#include - -//GPS -//#include // Adafruit Ultimate GPS -#include //Builtin GPS lib - -/* - * DEFINE - * & - * DECLARE - */ - -//Special characteres -#define DEGREE (char)176 //degree symbol in ISO 8859-1 -#define DEGREE_LCD (char)222 //degree symbol for lcd display - -// Pins used -#define RX1_PIN 0 // Raspberry Pi2 serial comm. - Hard wired -#define TX1_PIN 1 // Raspberry Pi2 serial comm. - Hard wired -#define RPI_PWR_PIN 2 // Raspberry Pi power control - Hard wired -#define SD_CS_PIN 4 // SPI SD CS - Hard wired -#define GPS_EN_PIN 6 // GPS ENable -#define RX3_PIN 7 // GPS serial comm. -#define TX3_PIN 8 // GPS serial comm. -#define AS3935_IRQ_PIN 9 // Interrupts from AS3935 lightning sensor -#define AS3935_CS_PIN 10 // SPI CS AS3935 lightning sensor -#define SPI_DI_PIN 11 // SPI MOSI AS3935 lightning sensor -#define SPI_DO_PIN 12 // SPI MISO AS3935 lightning sensor -#define SPI_CK_PIN 13 // SPI clock SD + AS3935 lightning sensor + built-in LED -//#define DHT_PIN 14 // Humidity sensor data -#define GPS_BUT_PIN 16 // GPS switch power control - -#define I2C_SDA_PIN 18 // I2C SDA -#define I2C_SCL_PIN 19 // I2C SCL - -#define BATT_VOLT_PIN 22 // Battery voltage monitoring (Analog Input) -#define LOW_BATT_PIN 23 // Low Battery signal from charger (digital input) - -//I2C addresses -//#define TCN75A_ADDR 0x00 //Sensor I2C bus address -#define BMP180_ADDR 0x77 -//#define BME280_ADDR 0x00 // ??? - -//Serial over USB communication -#define SERIAL_PORT Serial -#define SERIAL_BAUD_RATE 9600 - -//Raspberry Pi Serial Comm. -#define RPI_SERIAL_PORT Serial1 -#define RPI_SERIAL_BAUD_RATE 115200 - -//GPS -#define GPS_SERIAL_PORT Serial3 -#define GPS_SERIAL_BAUD_RATE 9600 - -// AS3935 Lightning sensor -#define AS3935_INDOORS 0 -#define AS3935_OUTDOORS 1 -#define AS3935_DIST_DIS 0 -#define AS3935_DIST_EN 1 -#define AS3935_CAPACITANCE 72 // 72pF for THIS board (from seller) - - diff --git a/arduino/cameteo-teensy/cameteo-teensy.ino b/arduino/cameteo-teensy/cameteo-teensy.ino deleted file mode 100755 index f64c225..0000000 --- a/arduino/cameteo-teensy/cameteo-teensy.ino +++ /dev/null @@ -1,553 +0,0 @@ -/* - * CAMETEO project - * - * This is a personnal project of weather station with - * automatic photo taking. - * This code is the weather station part and is meant - * to be run on a PJRC Teensy 3.2 board. - * - * Author : Arofarn - * - * Licence : GPL v3 - * - */ - -/* - * LIBRARIES - */ - -//Protocols -#include // library used with I2C protocol -#include // SPI protocol - -//Teensy3.x Real Time Clock -#include - -//SD card -#include - -// Sensors -#include // Generic -#include // BME280 -#include - -//GPS -//#include // Adafruit Ultimate GPS -#include //Builtin GPS lib - -/* - * DEFINE - * & - * DECLARE - */ - -//Special characteres -#define DEGREE (char)176 //degree symbol in ISO 8859-1 -#define DEGREE_LCD (char)222 //degree symbol for lcd display - -// Pins used -#define RX1_PIN 0 // Raspberry Pi2 serial comm. - Hard wired -#define TX1_PIN 1 // Raspberry Pi2 serial comm. - Hard wired -#define RPI_PWR_PIN 2 // Raspberry Pi power control - Hard wired -#define AS3935_CS_PIN 4 // SPI SD CS - Hard wired -#define GPS_EN_PIN 6 // GPS ENable -#define RX3_PIN 7 // GPS serial comm. -#define TX3_PIN 8 // GPS serial comm. -#define AS3935_IRQ_PIN 9 // Interrupts from AS3935 lightning sensor -#define SD_CS_PIN 10 // SPI CS AS3935 lightning sensor -#define SPI_DI_PIN 11 // SPI MOSI AS3935 lightning sensor -#define SPI_DO_PIN 12 // SPI MISO AS3935 lightning sensor -#define SPI_CK_PIN 13 // SPI clock SD + AS3935 lightning sensor + built-in LED -#define BATT_VOLT_PIN 16 // Battery voltage monitoring (Analog Input) -#define LOW_BATT_PIN 17 // Low Battery signal from charger (digital input) -#define I2C_SDA_PIN 18 // I2C SDA BME280 sensor -#define I2C_SCL_PIN 19 // I2C SCL BME280 sensor -#define GPS_BUT_PIN 20 // GPS switch power control (unused) - -//I2C addresses -//#define TCN75A_ADDR 0x00 //Sensor I2C bus address -//#define BMP180_ADDR 0x77 - -//Serial over USB communication -#define SERIAL_PORT Serial -#define SERIAL_BAUD_RATE 9600 - -//Raspberry Pi Serial Comm. -#define RPI_SERIAL_PORT Serial1 -#define RPI_SERIAL_BAUD_RATE 9600 - -//GPS -#define GPS_SERIAL_PORT Serial3 -#define GPS_SERIAL_BAUD_RATE 9600 - -//Sensors -Adafruit_BME280 bme; -float seaLevelPressure = SENSORS_PRESSURE_SEALEVELHPA; -float bme280_press; -float bme280_temp; -float bme280_alti; -float bme280_humi; - -// AS3935 Lightning sensor -#define AS3935_INDOORS 0 -#define AS3935_OUTDOORS 1 -#define AS3935_DIST_DIS 0 -#define AS3935_DIST_EN 1 -#define AS3935_CAPACITANCE 72 // 72pF for THIS board (from seller) -enum strike_sources { UNKNOWN_SRC, LIGHTNING, PERTURBATION, NOISE }; -volatile int8_t AS3935_ISR_Trig = 0; // Trigger for AS3935 lightning sensor - -void AS3935_ISR() { - AS3935_ISR_Trig = 1; -} - -PWF_AS3935 lightning(AS3935_CS_PIN, AS3935_IRQ_PIN, 33); - -int as3935_src; -int as3935_distance; -long lightning_nb_total = 0; -int lightning_nb_hour = 0; -int lightning_nb_day = 0; -int as3935_perturb_total = 0; -int as3935_noise_total = 0; -int as3935_unknown_total = 0; -char lightning_log_file[12] = "lghtnng.log"; - -//GPS -//Adafruit_GPS GPS(&GPS_SERIAL_PORT); -TinyGPS GPS; -float gps_latitude, gps_longitude, gps_altitude; // returns +- latitude/longitude in degrees -float gps_speed, gps_course; -unsigned long gps_time, gps_date, gps_fix_age; -int gps_year; -byte gps_month, gps_day, gps_hour, gps_minutes, gps_second, gps_hundreths; -unsigned long gps_chars, gps_hdop; -unsigned short gps_sentences, gps_failed_checksum, gps_satellites; - -//Miscellaneous -bool rpi_status = true; -int batt_voltage; -bool low_battery_flag = false; - -// Tasks timers -elapsedMillis since_bme280; -elapsedMillis since_gps; -elapsedMillis since_display; -elapsedMillis since_serial_send; -elapsedMillis since_sd_log; -elapsedMillis since_batt_chk; - - -void BootMessage(String s) { - char c[13]; // char buffer for conversion String->char - s.toCharArray(c, sizeof(s)); - SERIAL_PORT.printf("%-12s", c); -} - -void BootOK() { - SERIAL_PORT.println("OK"); -} - -void BootError() { - SERIAL_PORT.println("EE"); -} - -void sendDataOnSerial() { - - //Print date & hour on serial port - SERIAL_PORT.printf("%04d/%02d/%02d_%02d:%02d:%02d\n", year(), month(), day(), hour(), minute(), second()); - - //if (bme280_event.pressure) { - SERIAL_PORT.printf("Temperature:%8.2f %cC | Pressure: %8.2f hPa | Altitude:%8.2f m\n", - bme280_temp, DEGREE, bme280_press, bme280_alti); -// } -// else { -// SERIAL_PORT.printf("BMP180 Sensor error\n"); -// } - - SERIAL_PORT.printf("Lightning strikes (from start) : %d | Perturb.: %d | Noise : %d | Unknown detect.: %d\n", lightning_nb_total, - as3935_perturb_total, - as3935_noise_total, - as3935_unknown_total); - - //SERIAL_PORT.printf("Temperature:%8.2f %cC\n", tcn75a_temp, DEGREE); - - SERIAL_PORT.printf("GPS data: %04d/%02d/%02d_%02d:%02d:%02d (%d)\n", gps_year, gps_month, gps_day, - gps_hour, gps_minutes, gps_second, - gps_fix_age); - SERIAL_PORT.printf(" Latitude: %11.8f | Longitude: %11.8f | Altitude: %5.2f m\n", gps_latitude, gps_longitude, gps_altitude); - SERIAL_PORT.printf(" Speed: %4.1f km/h | Course : %4.1f %c\n", gps_speed, gps_course, DEGREE); - SERIAL_PORT.printf(" Chars: %11d | Sentences: %11d | Failed cheksum: %4d\n", gps_chars, gps_sentences, gps_failed_checksum); - - SERIAL_PORT.printf("Battery : %10d mV | Low Battery : %d\n", batt_voltage, low_battery_flag); -} - - -bool isStartedPI() { - //Return true if the R-Pi respond to an simple request - return rpi_status; -} - -void startRPI() { - //Start the Raspberry Pi via the MOSFET (grid connected to the RPI_PWR_PIN) - if (!isStartedPI()) { - pinMode(RPI_PWR_PIN, OUTPUT); - digitalWrite(RPI_PWR_PIN, LOW); - rpi_status = true; - } -} - -void stopRPI() { - //Stop the Raspberry Pi via the MOSFET (grid connected to the RPI_PWR_PIN) - - pinMode(RPI_PWR_PIN, INPUT); //Eteint via le MOSFET - rpi_status = false; -} - -void sendDataToRPI() { - //if (isStartedPI()) { - //Print date & hour on serial port - RPI_SERIAL_PORT.printf("%04d/%02d/%02d_%02d:%02d:%02d\n", year(), month(), day(), hour(), minute(), second()); - - //if (bme280_event.pressure) { - RPI_SERIAL_PORT.printf("Temperature:%8.2f %cC | Pressure: %8.2f hPa | Altitude:%8.2f m\n", - bme280_temp, DEGREE, bme280_press, bme280_alti); - //} - //else { - // RPI_SERIAL_PORT.printf("BMP180 Sensor error\n"); - //} - - RPI_SERIAL_PORT.printf("Lightning strikes (from start) : %d | Perturb.: %d | Noise : %d | Unknown detect.: %d\n", lightning_nb_total, - as3935_perturb_total, - as3935_noise_total, - as3935_unknown_total); - - RPI_SERIAL_PORT.printf("GPS data: %04d/%02d/%02d_%02d:%02d:%02d (%d)\n", gps_year, gps_month, gps_day, - gps_hour, gps_minutes, gps_second, - gps_fix_age); - RPI_SERIAL_PORT.printf(" Latitude: %11.8f | Longitude: %11.8f | Altitude: %5.2f m\n", gps_latitude, gps_longitude, gps_altitude); - RPI_SERIAL_PORT.printf(" Speed: %4.1f km/h | Course : %4.1f %c\n", gps_speed, gps_course, DEGREE); - RPI_SERIAL_PORT.printf(" Chars: %11d | Sentences: %11d | Failed cheksum: %4d\n", gps_chars, gps_sentences, gps_failed_checksum); - - RPI_SERIAL_PORT.printf("Battery : %10d mV | Low Battery : %d\n", batt_voltage, low_battery_flag); -// } -// else { -// //error RPI is not started -// } -} - - -//SD Card -#define CONFIGFILE "config.txt" - -//Configuration -char data_file[13] = "datalog.csv"; - - //Delays - -unsigned int bme280_delay = 500; -//unsigned int dht_delay = 3000; -unsigned int gps_delay = 100; -unsigned int serial_send_delay = 5000; -unsigned int sd_log_delay = 5000; -unsigned int batt_chk_delay = 5000; - -//Date and time -int TZ = 1; //Time zone - -/* - * SETUP - */ - -void setup() { - - //To be sure Raspberry-Pi won't be turned on unexpectedly - stopRPI(); - - pinMode(LOW_BATT_PIN, INPUT_PULLUP); - low_battery_flag = !digitalRead(LOW_BATT_PIN); - - pinMode(GPS_BUT_PIN, INPUT_PULLUP); - pinMode(GPS_EN_PIN, OUTPUT); - gps_power(); - - SERIAL_PORT.begin(SERIAL_BAUD_RATE); - delay(1000); - //while (!SERIAL_PORT) { }; //Wait for serial port to start - SERIAL_PORT.printf("Serial Comm. OK\nNow booting...\n"); - - RPI_SERIAL_PORT.begin(RPI_SERIAL_BAUD_RATE); - delay(1000); - //while (!RPI_SERIAL_PORT) { }; //Wait for serial port to start - RPI_SERIAL_PORT.printf("Raspberry's Serial Comm. OK\nNow booting...\n"); - - BootMessage("SDcard"); - // see if the card is present and can be initialized: - if (!SD.begin(SD_CS_PIN)) { - BootError(); - while(1); - } - BootOK(); - - BootMessage("RT Clock"); - // set the Time library to use Teensy 3.0's RTC to keep time - setSyncProvider(getTeensy3Time); - if (timeStatus()!= timeSet) { - BootError(); - while(1); - } - BootOK(); - - - BootMessage("BME280 (Pressure, Humidity and Temperature)"); - /* Initialise the sensor */ -// if(!bme.begin()) -// { -// /* There was a problem detecting the BMP085 ... check your connections */ -// BootError(); -// while(1); -// } - BootOK(); - - BootMessage("AS3935 (Lightning)"); - SPI.begin(); - SPI.setClockDivider(SPI_CLOCK_DIV16); // SPI speed to SPI_CLOCK_DIV16/1MHz (max 2MHz, NEVER 500kHz!) - SPI.setDataMode(SPI_MODE1); // MAX31855 is a Mode 1 device --> clock starts low, read on rising edge - SPI.setBitOrder(MSBFIRST); // data sent to chip MSb first - lightning.AS3935_DefInit(); // set registers to default - // now update sensor cal for your application and power up chip - lightning.AS3935_ManualCal(AS3935_CAPACITANCE, AS3935_OUTDOORS, AS3935_DIST_EN); - // enable interrupt (hook IRQ pin to Arduino Uno/Mega interrupt input: 0 -> pin 2, 1 -> pin 3 ) - attachInterrupt(AS3935_IRQ_PIN, AS3935_ISR, RISING); - BootOK(); - - BootMessage("GPS"); - GPS_SERIAL_PORT.begin(GPS_SERIAL_BAUD_RATE); - while (!GPS_SERIAL_PORT) {} ; - BootOK(); - -} - -/* - * LOOP - */ - -void loop() { - - //Power ON/OFF GPS - gps_power(); - - //Lightning detection - if (AS3935_ISR_Trig != 0) { - AS3935_ISR_Trig = 0; - time_t t = now(); - int distance = -9999; - int energy = -9999; - switch (as3935_src) { - case UNKNOWN_SRC: - //source inconnue - as3935_unknown_total++; - SERIAL_PORT.printf("Interruption (AS3935) : unkown source (not lightning)\n"); - break; - case LIGHTNING: - //Foudre !!! - lightning_nb_total++; - distance = lightning.AS3935_GetLightningDistKm(); - energy = lightning.AS3935_GetStrikeEnergyRaw(); - SERIAL_PORT.printf("Interruption (AS3935) : Lightningbolt !!!\n"); - SERIAL_PORT.printf("Distance : %4d km | Energy : %d \n", distance, energy); - break; - case PERTURBATION: - //Perturbation - as3935_perturb_total++; - SERIAL_PORT.printf("Interruption (AS3935) : perturbation...\n"); - break; - case NOISE: - //Trop de bruit électromagnétique - as3935_noise_total++; - SERIAL_PORT.printf("Interruption (AS3935) : Too much electromagnetic noise!\n"); - break; - } - writeLightningToSD(as3935_src, t, distance, energy); - } - - if (since_batt_chk >= batt_chk_delay) { - since_batt_chk -= batt_chk_delay; - //Check battery status and voltage - low_battery_flag = !digitalRead(LOW_BATT_PIN); - batt_voltage = getBatteryVoltage(); - } - -// if (since_bme280 >= bme280_delay) { -// since_bme280 = since_bme280 - bme280_delay; -// -// bme280_press = bme.readPressure() / 100.0F; -// bme280_alti = bme.readAltitude(seaLevelPressure); -// bme280_temp = bme.readTemperature(); -// bme280_humi = bme.readHumidity(); -// } - - if (since_gps >= gps_delay) { - since_gps = since_gps - gps_delay; - while (GPS_SERIAL_PORT.available()) { - char c = GPS_SERIAL_PORT.read(); - if (GPS.encode(c)) { - GPS.get_datetime(&gps_date, &gps_time, &gps_fix_age); - GPS.crack_datetime(&gps_year, &gps_month, &gps_day, - &gps_hour, &gps_minutes, &gps_second, - &gps_hundreths, &gps_fix_age); - GPS.f_get_position(&gps_latitude, &gps_longitude, &gps_fix_age); - gps_altitude = GPS.f_altitude(); - gps_speed = GPS.f_speed_kmph(); - GPS.stats(&gps_chars, &gps_sentences, &gps_failed_checksum); - gps_satellites = GPS.satellites(); - gps_hdop = GPS.hdop(); - } - } - } - - if (since_sd_log >= sd_log_delay) { - since_sd_log = since_sd_log - sd_log_delay; - writeDataToSD(); - } - - if (since_serial_send >= serial_send_delay) { - since_serial_send = since_serial_send - serial_send_delay; - sendDataOnSerial(); - sendDataToRPI(); - } - -} - -/* - * FUNCTIONS - */ - -time_t getTeensy3Time() { - return Teensy3Clock.get(); -} - -void gps_power() { - if (digitalRead(GPS_BUT_PIN) == 0) { digitalWrite(GPS_EN_PIN, LOW); } - else { digitalWrite(GPS_EN_PIN, HIGH); } -} - -int getBatteryVoltage() { - long mean = 0; - int U = 0; - int n = 10; - int res = 12; - - analogReadResolution(res); - - for (int i=0; i -#include -#include -#include -#include -#include -#include -#include - -/************************* WiFi Access Point *********************************/ - -#define WLAN_SSID "arowifi2" -#define WLAN_PASS "nAGjywhCQ4iUBcbmf0PBn_srrghpOur4-aIzcJ_8Uxm1b58dH1c4Vy-LEMLd" - -/************************* MQTT broker Setup *********************************/ - -#define MQTT_SERVER "192.168.0.18" -#define MQTT_SERVERPORT 1883 // use 8883 for SSL -#define MQTT_USERNAME "arofarn" -#define MQTT_KEY "WaKaW9XMGUZ3rRJD" -#define MQTT_ID "huzzah0" -#define MQTT_PUB_INTERVAL 15000 // milliseconds - -/************************* Time and NTP Setup *********************************/ - -#define NTP_SERVER "fr.pool.ntp.org" -#define TIMEZONE 0 // UTC+0 -#define DAYLIGHT false -#define NTP_REFRESH_INTERVAL 3600 - -/************************* Atmospheric Pressure Mode *********************************/ - -#define MODE_STATION true -#define DEFAULT_ALTITUDE 140.0 -#define DEFAULT_MSLP 1013.25 //Mean Atmospheric Pressure at Mean Sea-Level - -// Create an ESP8266 WiFiClient class to connect to the MQTT server. -WiFiClient wifi_client; -MQTTClient mqtt_client; - -//Create a class for the sensor -Adafruit_BME280 bme; // I2C - -bool mode_station = MODE_STATION; -float altitude = DEFAULT_ALTITUDE; -float atm_press_sea_level = DEFAULT_MSLP; - -bool led0_status = true ; -unsigned long previousPub = 0; - -/*************************** Sketch Code ************************************/ -// Bug workaround for Arduino 1.6.6, it seems to need a function declaration -// for some reason (only affects ESP8266, likely an arduino-builder bug). -void MQTT_connect(); - -void setup() { - bool wstatus=false; - - Serial.begin(115200); - delay(10); - - pinMode(LED_BUILTIN, OUTPUT); - pinMode(2, OUTPUT); - - digitalWrite(2, wstatus); - digitalWrite(LED_BUILTIN, led0_status); - - // Connect to WiFi access point. - Serial.println(); Serial.println(); - Serial.print("Connecting to "); - Serial.println(WLAN_SSID); - - WiFi.begin(WLAN_SSID, WLAN_PASS); - while (WiFi.status() != WL_CONNECTED) { - wstatus = !wstatus; - delay(500); - Serial.print("."); - digitalWrite(2, wstatus); - } - Serial.println(); - - digitalWrite(2, LOW); - Serial.println("WiFi connected"); - Serial.println("IP address: "); Serial.println(WiFi.localIP()); - - delay(500); - - NTP.begin(NTP_SERVER, TIMEZONE, DAYLIGHT); //Local french NTP server, timezone, summertime= false - NTP.setInterval(NTP_REFRESH_INTERVAL); - NTP.onNTPSyncEvent([](NTPSyncEvent_t error) { - if (error) { - Serial.print("Time Sync error: "); - if (error == noResponse) - Serial.println("NTP server not reachable"); - else if (error == invalidAddress) - Serial.println("Invalid NTP server address"); - } - else { - Serial.print("Got NTP time: "); - Serial.println(NTP.getTimeDateString(NTP.getLastNTPSync())); - } - }); - - time_t nowNTP; - nowNTP = NTP.getTime(); - Serial.println(nowNTP); - - // default settings - bool status; - status = bme.begin(); - if (!status) { - Serial.println("Could not find a valid BME280 sensor, check wiring!"); - while (1); - } - - mqtt_client.begin(MQTT_SERVER, MQTT_SERVERPORT, wifi_client); //MQTT_SERVERPORT, MQTT_USERNAME, MQTT_KEY); - //client.onMessage(messageReceived); - - MQTT_connect(); - mqtt_client.onMessage(messageReceived); - - previousPub = millis(); -} - -uint32_t x=0; - -void loop() { - mqtt_client.loop(); - - if (!mqtt_client.connected()) { - MQTT_connect(); - } - - unsigned long currentMillis = millis(); - - if(currentMillis - previousPub > MQTT_PUB_INTERVAL) { - // save the last time you blinked the LED - previousPub = currentMillis; - - float pressure_val = -99.9; - float temperature_val = -99.9; - float humidity_val = -99.9; - String date_str = ""; - char date_val[20] = "00:00:00 00-00-0000"; - char tosend[120]; - - pressure_val = bme.readPressure() / 100.0F; - temperature_val = bme.readTemperature(); - humidity_val = bme.readHumidity(); - - date_str = NTP.getTimeDateString(); - date_str.toCharArray(date_val,20); - - // Now we can publish stuff! - Serial.print(F("Sending pressure value ")); - Serial.print(pressure_val); - Serial.print("..."); - val2json(pressure_val, date_str, "hPa", "AP").toCharArray(tosend, 120); - if (! mqtt_client.publish("huzzah0/AdaBME280_1/pressure", tosend, true, 2) ) { - Serial.println(F("Failed")); - } else { - Serial.println(F("OK!")); - } - if(mode_station) { - //Sea-level atmospheric pressure calculus - atm_press_sea_level = bme.seaLevelForAltitude(altitude, pressure_val); - } else { - altitude = bme.readAltitude(atm_press_sea_level); - } - - Serial.print(F("Sending sea-level pressure value ")); - Serial.print(atm_press_sea_level); - Serial.print("..."); - val2json(atm_press_sea_level, date_str, "hPa", "MSLP").toCharArray(tosend, 120); - if (! mqtt_client.publish("huzzah0/AdaBME280_1/sea_level_pressure", tosend, true, 2) ) { - Serial.println(F("Failed")); - } else { - Serial.println(F("OK!")); - } - Serial.print(F("Sending altitude value ")); - Serial.print(altitude); - Serial.print("..."); - val2json(altitude, date_str, "m", "ALTI").toCharArray(tosend, 120); - if (! mqtt_client.publish("huzzah0/AdaBME280_1/altitude", tosend, true, 2) ) { - Serial.println(F("Failed")); - } else { - Serial.println(F("OK!")); - } - - // Now we can publish stuff! - Serial.print(F("Sending humity value ")); - Serial.print(humidity_val); - Serial.print("..."); - val2json(humidity_val, date_str, "%", "RH").toCharArray(tosend, 120); - if (! mqtt_client.publish("huzzah0/AdaBME280_1/humidity", tosend, true, 2)) { - Serial.println(F("Failed")); - } else { - Serial.println(F("OK!")); - } - - // Now we can publish stuff! - Serial.print(F("Sending temperature value ")); - Serial.print(temperature_val); - Serial.print("..."); - val2json(temperature_val, date_str, "degC", "AT").toCharArray(tosend, 120); - if (! mqtt_client.publish("huzzah0/AdaBME280_1/temperature", tosend, true, 2)) { - Serial.println(F("Failed")); - } else { - Serial.println(F("OK!")); - } - - // __LAST__ thing to send : Date and Time - Serial.print(F("Sending date and time ")); - Serial.print(date_val); - Serial.print("..."); - if (! mqtt_client.publish("huzzah0/NTP/date", date_val, true, 2)) { - Serial.println(F("Failed")); - } else { - Serial.println(F("OK!")); - } - } - - delay(10); -} - -void MQTT_connect() { - Serial.print("checking wifi..."); - while (WiFi.status() != WL_CONNECTED) { - Serial.print("."); - delay(1000); - } - - Serial.print("\nconnecting..."); - while (!mqtt_client.connect(MQTT_ID, MQTT_USERNAME, MQTT_KEY)) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - mqtt_client.subscribe("huzzah0/led0", 1); - mqtt_client.subscribe("huzzah0/mode_station", 1); - if(mode_station) { - mqtt_client.unsubscribe("huzzah0/sea_level_pressure"); - mqtt_client.subscribe("huzzah0/altitude", 2); - } - else { - mqtt_client.unsubscribe("huzzah0/altitude"); - mqtt_client.subscribe("huzzah0/sea_level_pressure", 1); - } -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); - - if(topic == "huzzah0/led0") { - if(payload == "0") led0_status = HIGH; - if(payload == "1") led0_status = LOW; - digitalWrite(LED_BUILTIN, led0_status); - } - if(topic == "huzzah0/mode_station") { - if(payload == "0") { - mode_station = false; - mqtt_client.unsubscribe("huzzah0/altitude"); - mqtt_client.subscribe("huzzah0/sea_level_pressure", 1); - } - if(payload == "1") { - mode_station = true; - mqtt_client.unsubscribe("huzzah0/sea_level_pressure"); - mqtt_client.subscribe("huzzah0/altitude", 1); - } - } - if(topic == "huzzah0/altitude") { - String pl = payload; - altitude = pl.toFloat(); - Serial.print("New altitude :"); - Serial.println(altitude); - } -} - -String int2str(int a) { - String ret = ""; - if (a < 10) { - ret +="0"; - } - if (a > 99) - a = 99; - ret += a; - return ret; -} - -String val2json (float val, String date, String unit, String type) { - String json = "{\"date\": \""; - json += date; - json += "\", \"value\": \""; - json += val; - json += "\", \"unit\": \""; - json += unit; - json += "\", \"type\": \""; - json += type; - json += "\" }"; - //Serial.println(json); - return json; -} - diff --git a/circuitpython/boot.py b/circuitpython/boot.py new file mode 100644 index 0000000..5d5b19c --- /dev/null +++ b/circuitpython/boot.py @@ -0,0 +1,17 @@ +# Selectively setting readonly to False on boot + +import board +import digitalio +import storage + +switch = digitalio.DigitalInOut(board.D5) +switch.direction = digitalio.Direction.INPUT +switch.pull = digitalio.Pull.UP +led = digitalio.DigitalInOut(board.D13) +led.direction = digitalio.Direction.OUTPUT + +# If the D0 is connected to ground with a wire +# CircuitPython can write to the drive +storage.remount("/", switch.value) +print("Readonly : {}".format(switch.value)) +led.value = switch.value \ No newline at end of file diff --git a/circuitpython/lib/adafruit_bme280.mpy b/circuitpython/lib/adafruit_bme280.mpy new file mode 100644 index 0000000000000000000000000000000000000000..252e9be6fe4b167f461ab842d6bf851ce0f85170 GIT binary patch literal 5422 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 literal 0 HcmV?d00001 diff --git a/circuitpython/lib/adafruit_bus_device/spi_device.mpy b/circuitpython/lib/adafruit_bus_device/spi_device.mpy new file mode 100644 index 0000000000000000000000000000000000000000..2a447832e7d047fb83636044d1628f3ed5cd4575 GIT binary patch literal 1250 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 literal 0 HcmV?d00001 diff --git a/circuitpython/main.py b/circuitpython/main.py new file mode 100644 index 0000000..b768ab6 --- /dev/null +++ b/circuitpython/main.py @@ -0,0 +1,126 @@ +# Simple weather and GPS logger + +import board +from busio import I2C, UART +from time import sleep +from analogio import AnalogIn +import microcontroller +import gc +import micropython +import os + +from adafruit_bme280 import Adafruit_BME280_I2C +from adafruit_gps import GPS +import neopixel + +######### +# Setup # +######### + +# BME280 sensors (I2C) +i2c = I2C(board.SCL, board.SDA) +bme280 = Adafruit_BME280_I2C(i2c) + +# Integrated Neopixel +pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) + +# Battery voltage +vbat = AnalogIn(board.D9, ) + +# GPS on Feather board +gps_uart = UART(board.TX, board.RX, baudrate=9600, timeout=3000) +gps = GPS(gps_uart) +# Turn on the basic GGA and RMC info +gps.send_command('PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0') +gps.send_command('PMTK220,2000') # 1000 ms refresh + +gc.collect() +micropython.mem_info() + +temp, hum, press = 0.0, 0.0, 0.0 +rouge, vert, bleu = 0, 0, 0 + +# Check if data directory exists +if 'data' not in os.listdir(): + os.mkdir('data') + os.mkdir('data/hourly') + os.mkdir('data/daily') + +############# +# Main loop # +############# +while True: + sleep(5) + + gc.collect() + # micropython.mem_info(1) + # print('Memory free: {} allocated: {}'.format(gc.mem_free(), gc.mem_alloc())) + + temp = bme280.temperature + hum = bme280.humidity + press = bme280.pressure + + print("Temperature: {:>+.1f} degC | Humidite: {:>.1f} % | Pression: {:>.1f} hPa".format(temp, hum, press)) + print("Tension batterie : {:>.2f} V | CPU Temp: {:>+.1f} degC".format((vbat.value*2*3.3/65536), microcontroller.cpu.temperature)) + # 0.00644531 = 2*3.3/1024 : + # 2 : voltage is divided by 2 + # 3.3 : Vref = 3.3V + # 1024 : 10bit ADC + + # Conversion des donn?es en couleur + # ROUGE => temp?rature : max = 35?C, min =10?C soit une amplitude de 25?C + rouge = int((temp-10)*255/25) + if rouge > 255: + rouge = 255 + if rouge < 0: + rouge = 0 + + # BLEU => humidit? : max= 100%, mini=0% + bleu = int(hum*255/100) + + # VERT => Pression : mini=960hPa, maxi = 1030hPa soit une amplitude 70hPa + vert = int((press-960)*255/70) + if vert > 255: + vert = 255 + if vert < 0: + vert = 0 + + rvb = (rouge, vert, bleu) + print("Couleur : {}".format(rvb)) + pixel[0] = rvb + + gps.update() + if not gps.has_fix: + # Try again if we don't have a fix yet. + print('Waiting for fix... {} - {}'.format(gps.has_fix, gps.satellites)) + continue + + print('Fix timestamp: {}/{}/{} {:02}:{:02}:{:02}'.format( + gps.timestamp_utc.tm_mon, # Grab parts of the time from the + gps.timestamp_utc.tm_mday, # struct_time object that holds + gps.timestamp_utc.tm_year, # the fix time. Note you might + gps.timestamp_utc.tm_hour, # not get all data like year, day, + gps.timestamp_utc.tm_min, # month! + gps.timestamp_utc.tm_sec)) + if gps.altitude_m is not None: + print('Latitude: {} deg | Longitude: {} deg | Altitude: {} m'.format(gps.latitude, + gps.longitude, + gps.altitude_m)) + else: + print('Latitude: {} deg | Longitude: {} deg'.format(gps.latitude, + gps.longitude)) + + print('Fix quality: {}'.format(gps.fix_quality)) + # Some attributes beyond latitude, longitude and timestamp are optional + # and might not be present. Check if they're None before trying to use! + if gps.satellites is not None: + print('# satellites: {}'.format(gps.satellites)) + if gps.track_angle_deg is not None: + print('Speed: {} knots'.format(gps.speed_knots)) + if gps.track_angle_deg is not None: + print('Track angle: {} degrees'.format(gps.track_angle_deg)) + if gps.horizontal_dilution is not None: + print('Horizontal dilution: {}'.format(gps.horizontal_dilution)) + if gps.height_geoid is not None: + print('Height geo ID: {} meters'.format(gps.height_geoid)) + diff --git a/doc/cablage-teensy.ods b/doc/cablage-teensy.ods deleted file mode 100755 index 2a105d72aba98310296da17c69ce23cdf8710483..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11514 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 diff --git a/doc/connections RPi-Arduino.fzz b/doc/connections RPi-Arduino.fzz deleted file mode 100755 index d8ce88514add831945bd69e8e8a4b8282129ff4e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25688 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 diff --git a/doc/connections RPi-teensy.fzz b/doc/connections RPi-teensy.fzz deleted file mode 100755 index a149e9acb5f2aeb5151a67adb1e791102d43d1b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19954 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