/***********************************************************************************
Mega2560
use the BREAKOUT BOARD only and use these 8bit data lines to the LCD,
pin usage:
LCD_CS LCD_CD LCD_WR LCD_RD LCD_RST SD_SS SD_DI SD_DO SD_SCK
Arduino Uno A3 A2 A1 A0 A4 10 11 12 13
Arduino Mega2560 A3 A2 A1 A0 A4 10 11 12 13
LCD_D0 LCD_D1 LCD_D2 LCD_D3 LCD_D4 LCD_D5 LCD_D6 LCD_D7
Arduino Uno 8 9 2 3 4 5 6 7
Arduino Mega2560 8 9 2 3 4 5 6 7
**********************************************************************************/
// Donc toutes les I/O logiques de l'UNO sont prises -> on utilisera un Mega2560
/**
RAPPEL ATmega 2560 :
– 256KBytes of In-System Self-Programmable Flash
– 4Kbytes EEPROM
– 8Kbytes Internal SRAM
**/
#include "Arduino.h"
#include <stdint.h>
#include "Station_meteo.h"
/** STATION METEO **/
#define version "16.4"
#define portPIN_switch0 PINL
#define pin_switch0 0b00000100
int sram;
uint32_t bmp_offset = 0;
uint16_t s_width = TFT480.Get_Display_Width();
uint16_t s_heigh = TFT480.Get_Display_Height();
#define NOIR 0x0000
#define ROUGE 0xF800
#define ORANGE 0xFBC0
#define JAUNE 0xFFE0
#define JAUNE_PALE 0xF7F4
#define VERT 0x07E0
#define CYAN 0x07FF
#define BLEU_CLAIR 0x455F
#define BLEU 0x001F
#define MAGENTA 0xF81F
#define VIOLET 0x781A
#define GRIS_TRES_CLAIR 0xDEFB
#define GRIS_CLAIR 0xA534
#define GRIS 0x8410
#define GRIS_FONCE 0x5ACB
#define BLANC 0xFFFF
Etiq3 Etiq_1;
Etiq3 Etiq_2;
//Etiq3 Etiq_3;
Etiq3 Etiq_4;
Etiq3 Etiq_5;
//Etiq3 Etiq_6;
//LED Led1;
LED Led2;
//LED Led3;
//LED Led4;
Scope Scope_1;
uint16_t aff_y;
/**--------------------------------------------------------
déclarations RECEPTION & DECODAGE SONDE EXTERNE
--------------------------------------------------------**/
/**
Rappels :
Scope_1.dx = 450
lst_records[500]
**/
record_meteo lst_records[460]; // voir la définition de la struct 'record_meteo' dans le .h
uint16_t n_record=0;
uint16_t n_record_max=450;
const int INPUT_PIN = 22;
uint8_t SDcardOk=0;
byte etat = 0;
byte memo_etat;
uint32_t memo_micros1 = 0;
uint32_t memo_micros2 = 0;
uint32_t temps_ecoule;
uint32_t pulseWidth;
uint16_t i_buff;
uint16_t TT1;
uint16_t nb_acqui433=0;
uint16_t nb_wr_SD=0;
uint8_t todo_init_record=0;
uint8_t todo_wr_scope_on_sd=0;
uint16_t annee_actuelle;
uint8_t mois_actuel;
uint8_t jour_actuel;
uint8_t jour_de_la_semaine; // 0=dimanche
uint8_t heure_actuelle;
uint8_t minute_actuelle;
uint8_t seconde_actuelle;
float age_Lune;
//uint8_t premiere_passe;
//uint8_t stable;
float echelle_T=1; // pour l'affichage des températures sur le Scope
float echelle_P=1; // pour l'affichage des pressions sur le Scope
int16_t gradu_minT, gradu_maxT;
// int16_t gradu_minP, gradu_maxP;
uint8_t num_acquisition;
uint8_t acqui_valide =0;
uint8_t scope_dat_ok =0;
int16_t T_EXT_lue[5]={NOIR,0,0}; // temperature extérieure (la sonde envoie des salves de 5 messages identiques)
uint8_t confiance[5]; //indice de confiance = nb d'occurences identiques pour chaque valeur reçue
int16_t T_EXT_retenue; // après comparaison des 5 valeurs reçues
int16_t Tmin, Tmax;
int16_t Tmoy; // pour 1 enregistrement, moyenne entre le jour et la nuit
int16_t moyenne_T_sur_toutes; // température mohenne sur l'ensemble des enregistrments affichés
uint16_t T_INTE_lue; // temperature intérieure
uint16_t Pression_lue;
uint16_t Pmin, Pmax;
uint16_t Pmoy;
uint8_t H_in_lue; // humidite
uint8_t memo_H_in;
uint8_t H_out_lue; // humidite
uint8_t memo_H_out;
// String dataString = "";
tmElements_t tm;
const int MAX_BUFFER = 120;
String message;
char buffer[MAX_BUFFER];
void changed() { }
void init_ports()
{
// 0 = entree, 1=sortie ; les 1 sur les pins en entrees activent les R de Pull Up (tirage à VCC)
DDRL = 0b11111011;
PORTL = 0b00000100;
}
/** -------------------------------------------------------
SD card
-----------------------------------------------------------
ADAPT SD -> mega2560 pins
CS -> 53
MISO -> 50
MOSI -> 51
SCK -> 52
**/
int freeRam()
{
extern int __heap_start, *__brkval;
int v;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}
void Init_SDcard()
{
Serial.print("Init SDcard...");
pinMode(53, OUTPUT);
if (!SD.begin(53)) //ici l'appel (et test) de la fonction 'begin' effectue l'initialisation (si possible)
{
Serial.println("init failed ! (pas de SDcard ?)");
// Led3.setCouleur(NOIR);
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(VIOLET);
TFT480.Set_Text_Size(2);
TFT480.Print_String("init failed ! (pas de SDcard ?)", 50, 250);
delay(1000);
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(ORANGE);
TFT480.Set_Text_Size(2);
TFT480.Print_String("demande de RAZ scope.dat", 80, 200);
TFT480.Print_String("elle se fera sur la minute", 70, 230);
todo_init_record =1;
return;
}
else
{
SDcardOk=1;
}
Serial.println("init Ok.");
if (SD.exists("data.txt")) {Serial.println("data.txt existe.");} else { Serial.println("pas de fichier data.txt");}
if (SD.exists("scope.dat"))
{
Serial.println("scope.dat existe.");
scope_dat_ok=1;
}
else
{
scope_dat_ok=0;
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(ORANGE);
TFT480.Set_Text_Size(1);
TFT480.Print_String("pas de fichier scope.dat, je vais le creer", 10, 260);
Serial.println("pas de fichier scope.dat sur la SDcard, je vais le creer");
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(ORANGE);
TFT480.Set_Text_Size(2);
TFT480.Print_String("demande de RAZ scope.dat", 80, 200);
TFT480.Print_String("elle se fera sur la minute", 70, 230);
todo_init_record =1;
todo_wr_scope_on_sd=1;
}
}
/** -------------------------------------------------------
Sensor local BMP280 (Pression & température)
--------------------------------------------------------**/
Adafruit_BMP280 bmp; // use I2C interface
Adafruit_Sensor *bmp_temp = bmp.getTemperatureSensor();
Adafruit_Sensor *bmp_pressure = bmp.getPressureSensor();
void init_BMP280()
{
Serial.println(F("BMP280 Sensor event test"));
if (!bmp.begin())
{
Serial.println(F("Could not find BMP280 sensor !"));
while (1) delay(10);
}
else {Serial.println(F("BMP280 sensor OK!")); }
/* Default settings from datasheet. */
bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */
Adafruit_BMP280::SAMPLING_X2, /* Temp. oversampling */
Adafruit_BMP280::SAMPLING_X16, /* Pressure oversampling */
Adafruit_BMP280::FILTER_X16, /* Filtering. */
Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */
bmp_temp->printSensorDetails();
}
void acqui_PressionTemperature()
{
Serial.println(" "); Serial.println("acqui_PressionTemperature()");
/**
pression athmosphérique normale au niveau de la mer = 1013 hPa
0m ->1013 hPa
1000m-> 899 hPa
-soit 1016-899 = 114 hPa / 1000 m (attention la variation n'est pas linéaire, au dessus de 1000 m, ce n'est plus exact)
-soit 11.4 hPa / 100 m
vu que mon capteur est situé à 100 m d'altitude, je vais rajouter 11.4 hPa à la mesure
afin d'obtenir des valeurs cohérentes par rapport aux information météorologiques
*/
sensors_event_t temp_event, pressure_event;
bmp_temp->getEvent(&temp_event);
bmp_pressure->getEvent(&pressure_event);
Serial.println();
Serial.print(F("Temperature IN = "));
T_INTE_lue =(int16_t)(10*temp_event.temperature);
T_INTE_lue-=50; //c.a.d 5°C de moins, pour compenser le fait que la sonde interne soit chauffée par sa proximité avec l'afficheur etc...
// une autre solution consisterait à l'aloigner du reste de l'électronique.
Serial.print(T_INTE_lue);
Serial.println(" *C");
Serial.print(F("Pression = "));
float pression_acq = pressure_event.pressure;
pression_acq += 11.4; // mon capteur est situé à 100 m d'altitude, voir commentaire ci-dessus
Pression_lue = (int16_t)pression_acq;
Serial.print(Pression_lue);
Serial.println(" hPa");
Serial.println();
}
/**--------------------------------------------------------
fonctions AFFICHAGE TFT
--------------------------------------------------------**/
void clear_screen()
{
TFT480.Fill_Screen(40,40,40);
}
void dessine_triangles()
{
int i = 0;
uint16_t L, H;
L=TFT480.Get_Display_Width();
H=TFT480.Get_Display_Height();
for(i=0; i<H/2; i+=5)
{
TFT480.Set_Draw_color(0,i+64,i+64);
TFT480.Draw_Triangle(L/2-1,H/2-1-i,L/2-1-i,H/2-1+i,L/2-1+i,H/2-1+i);
}
}
void dessine_degrade(uint16_t x, uint16_t y, uint8_t longueur, uint8_t dy, uint8_t RR1, uint8_t RR2, uint8_t GG1, uint8_t GG2, uint8_t BB1, uint8_t BB2)
{
// dessine un dégr&dé horizontal de couleurs (R1,G1,B1) -> (R2,G2,B2)
uint16_t i;
float R,G,B;
if(longueur==0) {return;}
i=0;
R=RR1;
G=GG1;
B=BB1;
while(i<longueur)
{
x++;
R+=(RR2-RR1)/longueur;
G+=(GG2-GG1)/longueur;
B+=(BB2-BB1)/longueur;
TFT480.Set_Draw_color((uint8_t)R,(uint8_t)G,(uint8_t)B);
TFT480.Draw_Line(x, y, x, y+dy);
i++;
}
}
void dessine_arc_en_ciel()
{
int16_t i;
float xr,xv,xb;
float r,v,b;
for (i=0; i<63; i++) // 10 * 2 pi radians
{
xr=(i+12)/10.0; r=140.0+120.0*sin(xr)-i; if (r<0) {r=0;} if (r>255) {r=255;}
xv=(i+54)/10.0; v=130.0+150.0*sin(xv); if (v<0) {v=0;} if (v>255) {v=255;};
xb=(i+38)/10.0; b=100.0+150.0*sin(xb); if (b<0) {b=0;} if (b>255) {b=255;};
/*
//pour test du déphasage des courbes sinus...
TFT480.Set_Draw_color(255, 255, 255);
TFT480.Draw_Pixel(200+i, 150-(uint8_t)(r/10.0));
TFT480.Draw_Pixel(200+i, 200-(uint8_t)(v/10.0));
TFT480.Draw_Pixel(200+i, 250-(uint8_t)(b/10.0));
*/
TFT480.Set_Draw_color((uint8_t)r,(uint8_t)v,(uint8_t)b);
TFT480.Draw_Circle_Helper(400, 280, 400-i, 1);
}
// while (1) {;}
}
void dessine_cercle(uint16_t x0, uint16_t y0, uint16_t r, uint16_t couleur)
{
uint16_t n;
float i;
float x,y;
TFT480.Set_Draw_color(couleur);
i=0;
while(i<2*M_PI)
{
x=x0+r*cos(i);
y=y0+r*sin(i);
TFT480.Draw_Pixel(x,y);
i+=0.01; // radians
}
}
void Draw_arc_elliptique(uint16_t x0, uint16_t y0, int16_t dx, int16_t dy, float alpha1, float alpha2, uint16_t couleur) // alpha1 et alpha2 en radians
{
/*
REMARQUES :
-cette fonction permet également de dessiner un arc de cercle (si dx=dy), voire le cercle complet
- dx et dy sont du type int (et pas uint) et peuvent êtres négafifs, ou nuls.
-alpha1 et alpha2 sont les angles (en radians) des azimuts des extrémités de l'arc
*/
uint16_t n;
float i;
float x,y;
TFT480.Set_Draw_color(couleur);
i=alpha1;
while(i<alpha2)
{
x=x0+dx*cos(i);
y=y0+dy*cos(i+M_PI/2.0);
TFT480.Draw_Pixel(x,y);
i+=0.01; // radians
}
}
void Fill_croissant_elliptique(uint16_t x0, uint16_t y0, int16_t dx1, int16_t dx2, int16_t dy, float alpha1, float alpha2, uint16_t couleur)
{
// dx1 et dx2 sont les extremums (en pixels) horizontaux d'un croissant vertical
if (dx2<dx1) {return;}
int16_t dx;
dx = dx1;
while(dx<dx2)
{
Draw_arc_elliptique(x0, y0, dx, dy, alpha1, alpha2, couleur);
dx++;
}
}
void Dessine_Lune2(uint16_t x0, uint16_t y0, float age)
{
//Remarque : l'age admet des nombres réels (avec décimales)
affi_img(y0-24, x0-24, "/bmp/lune5.bmp"); // image 48x48px
uint8_t r=22;
int16_t T1, T2; //Terminators gauche et droite, limites entre partie éclairée et partie dans l'ombre
if (age<1)
{
TFT480.Set_Draw_color(NOIR);
TFT480.Fill_Rectangle(x0-r, y0-r, x0+r, y0+r); // efface l'image
dessine_cercle(x0, y0, r, GRIS_FONCE); // Nouvelle Lune
}
// on procède par masquage (sur l'image bmp affichée) de la partie dans l'ombre
if (age <= 15) // premier croissant... pleine
{
T1=-r;
T2=r-2*r*age/15;
}
if ((age > 15) && (age <=30)) // pleine... dernier croissant
{
T1=r-2*r*(age-15)/15;
T2=r+1;
}
if(((uint8_t)age)==0)
{
dessine_cercle(x0, y0, r, GRIS);
}
else
{
dessine_cercle(x0, y0, r, GRIS_FONCE);
Fill_croissant_elliptique(x0, y0, T1, T2, r, -M_PI/2, M_PI/2, NOIR);
}
}
float Decimale(float valeur)
{
float d;
d=valeur-floor(valeur);
return d;
}
float GetPhase(int Y, int M, int D)
{
// return "age de le Lune" (nombre allant de 0.00 à 29.xx)
float AG, IP;
uint32_t Y2, M2, K1, K2, K3, JD;
Y2=Y-floor((12-M)/10);
M2=M+9;
if (M2>=12) {M2-=12;}
K1= floor(365.25 * (Y2 + 4712));
K2= floor(30.6 * M2+0.5);
K3= floor(floor((Y2/100) + 49)*0.75)-38;
JD= K1+K2+D+59;
if (JD > 2299160) {JD=JD-K3;}
IP= Decimale((JD-2451550.1)/29.53058);
AG= IP*29.53;
return AG;
}
void calcul_moyenne_T_sur_toutes()
{
// calcul sur l'ensemble des temperature affichée
uint16_t i;
int32_t sum_T =0;
for (i=2; i<= n_record_max ; i++)
{
sum_T += lst_records[i].T;
}
moyenne_T_sur_toutes = sum_T / n_record_max;
Serial.print(" moyenne_T_sur_toutes= "); Serial.println(moyenne_T_sur_toutes);
}
void determine_Tmin_Tmax()
{
uint16_t i;
int16_t t1,t2;
Tmin=500;
Tmax=-500;
for (i=2; i<= n_record_max ; i++) // attention départ=2, pas 0 pour éviter de prendre en compte l'acquisition en cours
{
if (lst_records[i].heure != 0xFFFF)
{
t1=lst_records[i].T;
t2=lst_records[i-1].T;
/*
Serial.print(i);
Serial.print(" t1=");
Serial.println(t1);
*/
if ((t1 !=0) && (t1 > -250) && (t1 < 480)) // -25°C à +48°C
{
if (t1 < (moyenne_T_sur_toutes + 100) ) // pour éliminer les glitches dans le calcul
{
if (t1 < Tmin) {Tmin= t1;}
if (t1 > Tmax) {Tmax= t1;}
}
}
}
}
if ((Tmax - Tmin) < 40) // soit 4°C
{
// dans ce cas on impose des valeurs permettant de calculer une échelle fonctionnelle
//Tmin-=10; // c.à.d -=1°C
//Tmax+=10; // c.à.d +=1°C
}
//Tmin=150;
//Tmax=450;
Tmoy=(Tmin+Tmax)/2;
// TFT480.Set_Text_Back_colour(NOIR);
// TFT480.Set_Text_colour(JAUNE);
// TFT480.Set_Text_Size(1);
// TFT480.Print_Number_Float(Tmax/10, 1, 350, 15, '.', 3, ' ');
// TFT480.Print_Number_Float(Tmin/10, 1, 350, 25, '.', 3, ' ');
/*
TFT480.Print_Number_Int(Tmin,400,5,3,' ',10);
TFT480.Print_Number_Int(Tmax,400,15,3,' ',10);
*/
}
void fixe_Tmin_tmax()
{
// pour l'echelle verticale
// en remplacement éventuel de la fonction précédente
Tmin=180; // 18°C
Tmax=320; // 32°C
Tmoy=(Tmin+Tmax)/2;
}
void efface_glitch_P()
{
int16_t p0,p1,p2;
uint16_t i,n;
for(n=0; n<2; n++)
{
for (i=n_record_max-2; i>1 ; i--)
{
p0=lst_records[i].P;
p1=lst_records[i-1].P;
p2=lst_records[i-2].P;
if ((abs(p1-p2)) > 5)
{
lst_records[i-1].P=p0;
lst_records[i-2].P=p0;
i-=2;
}
}
}
}
void efface_glitch_T()
{
Serial.println("efface_glitch_T()");
int16_t T0,T1,T2;
uint16_t i,n;
for(n=0; n<5; n++)
{
for (i=2; i<n_record_max ; i++)
{
T0=lst_records[i].T;
T1=lst_records[i+1].T;
T2=lst_records[i+2].T;
if ((abs(T1-T2)) > 30)
{
Serial.print("ici i="); Serial.println(i);
lst_records[i+1].T=T0;
lst_records[i+2].T=T0;
i-=2;
}
}
}
}
void verifie_data_scope()
{
Serial.println(" ");
Serial.println("verifie_data_scope() ");
if(! SDcardOk){return;}
int16_t T,P,H;
uint16_t heure1, heure2;
uint16_t i;
uint8_t h, mn;
uint8_t compte;
for (i=1; i<n_record_max-1; i++)
{
T=lst_records[i].T;
P=lst_records[i].P;
H=lst_records[i].H;
heure1=lst_records[i].heure;
heure2=lst_records[i+1].heure;
h= heure1 / 60;
mn = heure1 % 60;
Serial.print(i);
Serial.print(" Heure=");
Serial.print(h);
Serial.print(":");
Serial.print(mn);
Serial.print(" T=");
Serial.print(T);
Serial.print(" P=");
Serial.print(P);
Serial.print(" Hum=");
Serial.print(H);
Serial.println(" ");
}
}
void calcul_Gradu_ech_T()
{
//calcul de la grd min & grad max
gradu_minT = -10+(Tmin/10)*10; // division entière ce qui donne une valeur arrondie
gradu_maxT = 10+(Tmax/10)*10;
// gradu_maxT+=10;
echelle_T =(float) (gradu_maxT - gradu_minT)/(Scope_1.dy);
if (echelle_T==0) {echelle_T=1;} // cas où la température est totalement constante - évite des /0 plus tard
/*
TFT480.Set_Text_colour(BLANC);
TFT480.Set_Text_Size(1);
TFT480.Print_Number_Int(gradu_minT,400,30,3,' ',10);
TFT480.Print_Number_Int(gradu_maxT,400,40,3,' ',10);
*/
// TFT480.Set_Text_colour(ROUGE[0],ROUGE[1],ROUGE[2]);
// TFT480.Print_Number_Float(echelle_T, 1, 400, 50, '.', 3, ' ');
}
void determine_Pmin_Pmax()
{
uint16_t i, p1;
Pmin=2000;
Pmax=0;
for (i=0; i< n_record_max ; i++) // attention départ=2, pas 0 pour éviter de prendre en copta l'acquisition en cours
{
if (lst_records[i].heure != 0xFFFF)
{
p1=lst_records[i].P;
if (p1 !=0)
{
if ((p1 >800) && (p1 < Pmin)) {Pmin= p1;}
if (p1 > Pmax) {Pmax= p1;}
}
}
}
if ((Pmax - Pmin) < 20)
{
// dans ce cas on impose des valeurs permettant de calculer une échelle fonctionnelle
Pmin-=2; // hPa
Pmax+=2;
}
Pmoy=(Pmin+Pmax)/2;
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(VERT);
TFT480.Set_Text_Size(1);
// TFT480.Print_Number_Int(Pmax,160,85,3,' ',10);
// TFT480.Print_Number_Int(Pmin,160,95,3,' ',10);
// TFT480.Set_Text_colour(BLANC);
// TFT480.Print_Number_Int(Pmoy,390,25,3,' ',10);
}
void calcul_ech_P()
{
echelle_P =(float) (Pmax -Pmin)/(Scope_1.dy-20);
if (echelle_P==0) {echelle_P=1;}
// TFT480.Set_Text_colour(ROUGE[0],ROUGE[1],ROUGE[2]);
// TFT480.Print_Number_Float(echelle_P, 3, 390, 35, '.', 3, ' ');
}
uint16_t nb_acq_heures()
{
uint16_t v1=0;
uint16_t i;
for (i=0; i<n_record_max; i++)
{
if (lst_records[i].heure != 0xFFFF) {v1++;}
}
return v1;
}
void affiche_courbeT() // Température extérieure & la graduation temporelle -> sur le scope
{
Serial.println(" "); Serial.println("affiche_courbeT()");
//sram = freeRam(); Serial.print("5-freeRam="); Serial.println(sram);
// Ymax du scope = 142
//142 * 3 = 426 -> 42.6°C max
uint16_t i,j, k1;
uint16_t x, xt;
uint8_t nb_ech=3; // pour le lissage de la courbe
float Ti1, Ti2; // =316 pour 31.6°C
float y1, y2;
uint16_t heure1, heure2;
uint8_t h1, h2, mn1, mn2;
uint8_t RR1, RR2, GG1, GG2, BB1, BB2;
x=Scope_1.dx;
for (i=n_record_max-1; i>nb_ech ; i--)
{
heure1=lst_records[i].heure;
heure2=lst_records[i-1].heure;
h1= heure1 / 60;
mn1 = heure1 % 60;
h2= heure2 / 60;
mn2 = heure2 % 60;
Ti1=lst_records[i-1].T;
Ti2=lst_records[i].T;
/*
if ((abs(Ti1 - memo_Ti1)) > 30.0) {Ti1 = memo_Ti1;} // détection valeur impossible, et correction
else (memo_Ti1 = Ti1); // si OK
if ((abs(Ti2 - memo_Ti2)) > 30.0) {Ti1 = memo_Ti1;} // détection valeur impossible, et correction
else (memo_Ti2 = Ti2); // si OK
*/
// if ((heure==0) && (Ti1==0) &&(Ti2==0)) {return;}
if ((heure1 != 0xFFFF) && (mn1 % 5) == 0) // l'enregistrement doit être valide et être centré sur les multiples de 5mn
{
//Serial.print("affiche_courbeT() ");
//Serial.print(h); Serial.print(":"); Serial.println(mn);
if(nb_acq_heures() > nb_ech) // lissage
{
// lissage de la courbe : filtre passe bas par moyenne glissante
Ti1=0; Ti2=0;
for (j=0; j<nb_ech; j++)
{
k1=i-j;
if (k1>1)
{
Ti1 += lst_records[k1-1].T;
Ti2 += lst_records[k1].T;
}
}
Ti1/=nb_ech; Ti2/=nb_ech;
}
else
{
Ti1=lst_records[i-1].T;
Ti2=lst_records[i].T;
}
y1 = Scope_1.dy/2.0 + (Ti1-Tmoy) / echelle_T;
if (y1>=Scope_1.dy){y1=Scope_1.dy;} // rabote ce qui dépace du cadre
if (y1<0) {y1=0;}
y2 = Scope_1.dy/2.0 + (Ti2-Tmoy) / echelle_T;
if (y2>=Scope_1.dy){y2=Scope_1.dy;} // rabote ce qui dépace du cadre
if (y2<0) {y2=0;}
if ((y1 >1) && (y2 >1))
{
if (mn1 ==0) // tracé de lignes temporelles verticales toutes les heures 'pile'
{
Scope_1.setCouleurTrace(GRIS_FONCE);
if (h1 == 0) {Scope_1.setCouleurTrace(GRIS_CLAIR);}
if (h1 == 12) {Scope_1.setCouleurTrace(ROUGE);}
if (heure1 !=(heure2 + 5) && (heure1 != 0))
{
// signale un manque de data par un tiret violet en bas
Scope_1.Tiret(x, 30, x, Scope_1.dy-2);
Scope_1.setCouleurTrace(VIOLET);
Scope_1.Tiret(x, 20, x, 30);
}
else
{
// mode normal
Scope_1.Tiret(x, 20, x, Scope_1.dy-2);
}
if (h1 == 0)
{
TFT480.Set_Text_Size(1);
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(BLANC);
TFT480.Print_String("00", x+12, Scope_1.y0+Scope_1.dy/2);
affi_img(Scope_1.y0+5, x+8, "/bmp/lune3b.bmp");
}
if (h1 == 5)
{
xt=x+15;
// DEGRADE DE COULEUR pour marquer la matinée
// bleu -> jaune
RR1=0; RR2=255;
GG1=0; GG2=255;
BB1=200; BB2=0;
dessine_degrade(xt, Scope_1.y0+15, 60, 4, RR1, RR2, GG1, GG2, BB1, BB2);
}
if (h1 == 12)
{
TFT480.Set_Text_Size(1);
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(BLANC);
TFT480.Print_String("12h", x+12, Scope_1.y0+Scope_1.dy/2);
// ********* DEGRADES DE COULEUR pour marquer la période diurne **************
xt=x+15;
//Serial.print("xt="); Serial.println(xt); // if ((xt+64)< 470)
{
// jaune -> orange
RR1=255; RR2=100;
GG1=255; GG2=60;
BB1=0; BB2=0;
dessine_degrade(xt, Scope_1.y0+15, 80, 4, RR1, RR2, GG1, GG2, BB1, BB2);
// bleu -> jaune
RR1=0; RR2=255;
GG1=0; GG2=255;
BB1=200; BB2=0;
dessine_degrade(xt-70, Scope_1.y0+15, 60, 4, RR1, RR2, GG1, GG2, BB1, BB2);
}
//icone soleil:
affi_img(Scope_1.y0+5, x+7, "/bmp/soleil.bmp");
}
}
Scope_1.setCouleurTrace(JAUNE);
Scope_1.Tiret(x-1, (uint16_t)y1, x, (uint16_t)y2); // tracé de la courbe
Scope_1.Tiret(x-1, (uint16_t)y1+1, x, (uint16_t)y2+1); // pour épaissir le trait
}
}
else
{ // si enregistrement non valide
//Scope_1.setCouleurTrace(ROUGE);
//Scope_1.Tiret(x-1, 99, x, 101);
}
x--;
}
TFT480.Set_Draw_color(NOIR);
TFT480.Fill_Rectangle(470, 140, 479, 318);
}
void affiche_courbeP() // pression - sur le scope
{
Serial.println(" "); Serial.println("affiche_courbeP()");
/**PNM minimum : 870 hPa, au large des Philippines, près du centre du typhon Tip, le 12 octobre 1979...
Ouragan2 de classe 5 : pression au centre inférieure à 920 hPa
**/
uint8_t lissage;
uint16_t i, j, k1;
uint16_t x;
uint8_t nb_ech=20;
float p1, p2;
float Pi1, Pi2;
/*
Serial.println("affiche_courbeP()");
Serial.print("Pmoy=");
Serial.println(Pmoy);
Serial.print("echelle_P=");
Serial.println(echelle_P);
*/
Scope_1.setCouleurTrace(VERT);
x=Scope_1.dx;
for (i=n_record_max-1; i>nb_ech ; i--)
{
if (lst_records[i].heure != 0xFFFF)
{
lissage=0;
if ((acqui_valide ==1)||(scope_dat_ok==1)){lissage =1;}
if(lissage==1)
{
// lissage de la courbe : filtre passe bas par moyenne glissante
p1=0; p2=0;
for (j=0; j<nb_ech; j++)
{
k1=i-j;
if (k1>1)
{
p1 += lst_records[k1-1].P;
p2 += lst_records[k1].P;
}
}
p1/=nb_ech; p2/=nb_ech;
}
else
{
p1 = lst_records[i-1].P;
p2 = lst_records[i].P;
}
/*
Serial.print("p1=");
Serial.println(p1);
Serial.print("p2=");
Serial.println(p2);
*/
if ((p1>800)&&(p2>800))
{
Pi1 = Scope_1.dy/2.0 + (p1-Pmoy) / echelle_P;
if (Pi1>=Scope_1.dy){Pi1=Scope_1.dy-1;}
if (Pi1<0) {Pi1=0;}
Pi2 = Scope_1.dy/2.0 +(p2-Pmoy) / echelle_P;
if (Pi2>=Scope_1.dy){Pi2=Scope_1.dy-1;}
if (Pi2<0) {Pi2=0;}
}
if ((Pi1 > 0) && (Pi2 > 0))
{
Scope_1.Tiret(x-1, (uint16_t)Pi1, x, (uint16_t)Pi2);
}
}
x--;
}
}
void affiche_courbeH() // Humidité - sur le scope
{
Serial.println(" "); Serial.println("affiche_courbeH()");
uint16_t i,j, k1;
uint8_t nb_ech=20;
float Hi1, Hi2;
float h1, h2;
float ech_Hum;
ech_Hum = (Scope_1.dy) / 100.0;
Scope_1.setCouleurTrace(BLEU);
for (i=n_record_max-1; i>nb_ech ; i--)
{
if (lst_records[i].heure != 0xFFFF)
{
// lissage de la courbe : filtre passe bas par moyenne glissante
h1=0; h2=0;
for (j=0; j<nb_ech; j++)
{
k1=i-j;
if (k1>1)
{
h1 += lst_records[k1-1].H;
h2 += lst_records[k1].H;
}
}
h1/=nb_ech; h2/=nb_ech;
Hi1=h1 * ech_Hum;
if (Hi1>=Scope_1.dy){Hi1=Scope_1.dy-1;}
if (Hi1<1) {Hi1=1;}
Hi2=h2 * ech_Hum;
if (Hi2>=Scope_1.dy){Hi2=Scope_1.dy-1;}
if (Hi2<1) {Hi2=1;}
uint16_t H1_int, H2_int;
H1_int = (uint16_t)Hi1;
H2_int = (uint16_t)Hi2;
if ((H1_int > 1) && (H2_int > 1) && (abs((H1_int-H2_int)) < 20))
{
Scope_1.Tiret(i-1, H1_int, i, H2_int);
}
}
}
TFT480.Set_Text_colour(BLEU_CLAIR);
TFT480.Set_Text_Size(1);
TFT480.Print_String("100%", 390, 150);
TFT480.Print_String(" 0%", 390, 305);
}
void affiche_ligne_ref()
{
uint8_t R=300/echelle_T; // 300 -> 30°C
Scope_1.setCouleurTrace(ROUGE);
Scope_1.Tiret(0, R, Scope_1.dx, R);
}
void affiche_Lune()
{
age_Lune = GetPhase(annee_actuelle, mois_actuel, jour_actuel);
// Affichage de 'l'age' de la Lune [0..29 j]
TFT480.Set_Text_Size(1);
uint16_t x0=436;
uint16_t y0=8;
TFT480.Set_Text_colour(GRIS);
TFT480.Print_String(" AGE:", x0, y0) ;
TFT480.Set_Text_Size(2);
TFT480.Print_Number_Int(age_Lune,x0,y0+10,3,' ',10);
TFT480.Set_Text_colour(BLANC);
switch (((int8_t)age_Lune))
{
case 0:
{
TFT480.Print_String("NL", x0, y0+27);
}
break;
case 8:
{
TFT480.Print_String("PQ", x0, y0+27);
}
break;
case 15:
{
TFT480.Print_String("PL", x0, y0+27);
}
break;
case 22:
{
TFT480.Print_String("DQ", x0, y0+27);
}
break;
default:
{
TFT480.Print_String("--", x0, y0+27);
}
break;
}
Dessine_Lune2(407, 32, age_Lune);
}
void affiche_SCOPE() // vide
{
Scope_1.init(18,145, n_record_max,172); // 1px toutes les 5mn; 12px = 1h
Scope_1.setCouleurCadre(BLANC);
Scope_1.traceCadre();
// Scope_1.traceReticule(); // et efface_surface au préalable
// affiche_ligne_ref();
}
void affiche_pannel()
{
// dessine_triangles();
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(BLANC);
TFT480.Set_Text_Size(1);
TFT480.Print_String("STATION METEO v:", 250, 0);
TFT480.Print_String(version, 250 +100, 0);
uint16_t hauteur = 0;
hauteur += 6;
Etiq_1.setCouleurTxt(ORANGE);
Etiq_1.setCouleurCadre(BLEU_CLAIR);
Etiq_1.setCouleurNom(BLANC);
Etiq_1.init(5, hauteur, 180, 60, "IN");
//RAPPEL : void affiche_float(float valeur, uint8_t nb_chiffres, uint8_t nb_dec, char txt_unite_i[3]);
Etiq_1.affiche_float(0, 4, 1, " C",0,0);
TFT480.Set_Draw_color(Etiq_1.couleur_txt);
TFT480.Draw_Circle(Etiq_1.x0+157, Etiq_1.y0+Etiq_1.dy-24, 3); // affiche le signe "°"
Etiq_2.setCouleurTxt(JAUNE);
Etiq_2.setCouleurCadre(BLEU_CLAIR);
Etiq_2.setCouleurNom(BLANC);
Etiq_2.init(200, hauteur, 180, 60, "OUT");
Etiq_2.affiche_float(0, 4, 1, " C",0,0);
TFT480.Set_Draw_color(Etiq_2.couleur_txt);
TFT480.Draw_Circle(Etiq_2.x0+157, Etiq_2.y0+Etiq_2.dy-24, 3); // affiche le signe "°"
hauteur += 65;
TFT480.Set_Text_colour(BLEU_CLAIR);
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_Size(1);
TFT480.Print_String("PRESSION", 60, hauteur);
TFT480.Set_Text_colour(BLANC);
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_Size(1);
TFT480.Print_String("HUMIDITE (%)", 200, hauteur);
hauteur += 6;
Etiq_5.setCouleurTxt(VERT);
Etiq_5.setCouleurCadre(BLEU_CLAIR);
Etiq_5.setCouleurNom(BLANC);
Etiq_5.init(5, hauteur, 180, 60, "");
//rappel affiche_int(uint32_t valeur, uint8_t nb_chiffres, char txt_unite_i[3])
Etiq_5.affiche_int(0, 4, "hPa",0,0);
Etiq_4.setCouleurTxt(BLEU);
Etiq_4.setCouleurCadre(BLEU_CLAIR);
Etiq_4.setCouleurNom(BLANC);
Etiq_4.init(200, hauteur, 80, 60, "");
Etiq_4.affiche_int(0, 2, "%",0,0);
TFT480.Print_String("%", Etiq_4.x0+60,Etiq_4.y0+35);
TFT480.Set_Text_Size(1);
TFT480.Set_Text_colour(BLANC);
// TFT480.Print_Number_Int(nb_acqui433,330,70,6,' ',10);
// TFT480.Print_Number_Int(nb_wr_SD,440,70,6,' ',10);
}
void affiche_LEDS()
{
TFT480.Set_Text_Size(1);
TFT480.Set_Text_colour(BLANC);
// TFT480.Print_String("WR SDcard", 300, 70);
// Led1.init(390,68, 10,10);
// Led1.setCouleur(NOIR);
Led2.init(410,60, 10,10);
TFT480.Print_String("RX", Led2.x0-20, Led2.y0);
Led2.setCouleur(NOIR);
// TFT480.Print_String("RAZ data", 300, 96);
// Led3.init(390,96, 10,10);
// Led3.setCouleur(NOIR);
// Led4.init(390,10, 20,20);
// Led4.setCouleur(NOIR);
}
/**--------------------------------------------------------
fonctions RECEPTION & DECODAGE SONDE EXTERNE 433MHz
--------------------------------------------------------**/
void init_lst_records()
{
Serial.println(" "); Serial.println("init_lst_records()");
uint16_t i;
uint8_t h;
uint8_t mn;
uint8_t mn5;
float Tn,Pn,Hn;
h= heure_actuelle;
mn=5*(minute_actuelle/5);
//mn=5*mn5;
Serial.print("heure actuelle ponderee="); Serial.print(h); Serial.print(":");Serial.println(mn);
//on part de l'heure actuelle puis on la décrémente par pas de 5mn
for (i=n_record_max; i>0 ; i--)
{
Serial.print(i); Serial.print(" "); Serial.print(h); Serial.print(":");Serial.println(mn);
// lst_records[i].T=0;
// lst_records[i].P=0;
// lst_records[i].H=0;
// sinusoiïdes centrées sur la dernière valeur acquise par les capteurs
Tn = T_EXT_retenue + 10*sin(i/20.0); // sinusoïde (pour affichage echelle consistante)
Tn = T_EXT_retenue;
lst_records[n_record_max-i].T= (int16_t)Tn;
//Pn = Pression_lue + (i/20)*sin(i/27.0); // sinusoïde (pour affichage echelle consistante)
Pn = Pression_lue;
lst_records[n_record_max-i].P= (int16_t)Pn;
//Hn = H_out_lue + (i/10)*sin(i/43.0); // sinusoïde (pour affichage echelle consistante)
Hn = H_out_lue;
lst_records[n_record_max-i].H= (uint16_t)Hn;
//
if (mn>=5){mn-=5;}
else
{
mn=55;
if (h>0){h--;} else {h=23;}
}
lst_records[i].heure=mn+60*h;
}
/**
for (i=0; i<n_record_max ; i++)
{
// sinusoiïdes centrées sur la dernière valeur acquise par les capteurs
Tn = T_EXT_retenue + 10.0*sin(i/40.0); // sinusoïde (pour affichage echelle consistante)
lst_records[n_record_max-i].T= (int16_t)Tn;
Pn = Pression_lue + 5.0*sin(i/40.0); // sinusoïde (pour affichage echelle consistante)
lst_records[n_record_max-i].P= (int16_t)Pn;
Hn = H_out_lue + 5.0*sin(i/40.0); // sinusoïde (pour affichage echelle consistante)
lst_records[n_record_max-i].H= (uint16_t)Hn;
// ---------------------------------------------------------------------
}
**/
}
void efface_buffer()
{
for(int i=0; i<120; i++)
{
buffer[i]=' ';
}
}
void record_enr_actuel()
{
Serial.println( "record_enr_actuel()");
// enregistre les data actuels en fin de liste avant décalage des enregistrements (par la fonction suivante)
lst_records[n_record_max].T= T_EXT_retenue; // format 325 -> 32.5°C
lst_records[n_record_max].P= Pression_lue;
lst_records[n_record_max].H= H_out_lue;
lst_records[n_record_max].heure= heure_actuelle*60 + minute_actuelle;
}
void decale_enregistrements(uint16_t p)
{
Serial.println( "decale_enregistrements");
//décalage vers la gauche depuis la position p
uint16_t i;
TFT480.Set_Text_Back_colour(BLANC);
TFT480.Set_Text_colour(NOIR);
TFT480.Set_Text_Size(2);
TFT480.Print_String("<", 440, 170);
for (i=0; i<p; i++)
{
// decalage
lst_records[i].T=lst_records[i+1].T; // ->remarque : l'enr 'p' est pris par le 'i+1' et est dupliqué vers la gauche
lst_records[i].P=lst_records[i+1].P;
lst_records[i].H=lst_records[i+1].H;
lst_records[i].heure=lst_records[i+1].heure;
}
}
int16_t strbinToInt(String str1, uint8_t nb_bits)
{
uint8_t i;
int16_t result=0;
bool negatif = false;
int tableau1[10];
for (i=0; i<nb_bits; i++) { if(str1[i] == '0') {tableau1[i]=0;} else {tableau1[i]=1;} }
if (tableau1[0] == 1) {negatif=true;}
if (negatif) { for (i=0; i<nb_bits; i++) {tableau1[i] = 1-tableau1[i]; } }
for (i=0; i<nb_bits; i++) { if (tableau1[(nb_bits-1)-i]==1) {result+= 1<<i;} }
if (negatif) { result+=1; result = -result; }
return result;
}
void calcul_confiance()
{
// les 5 valeurs reçues à l'issue d'une salve ne sont pas toujours toutes identiques, dû à des aléas de transmission
// ici on affecte à chaque valeur le nombre d'occurences (qui va de 1 si valeur "exotique" à 5 si toutes identiques)
// ces nombres d'occurences sont inscrites dans un tableau semblable à celui des valeurs lues (mêmes indices).
uint8_t i,j;
uint16_t Ti;
for(i=0; i<5; i++) {confiance[i]=0;} //RAZ
for(i=0; i<5; i++)
{
Ti=T_EXT_lue[i];
for(j=0; j<5; j++)
{
if (T_EXT_lue[j] == Ti) {confiance[i]++;}
}
}
}
uint16_t meilleure_valeur()
{
// trouver la valeur ayant obtenu le meilleur score de présence dans la salve (= le plus grand nombre d'occurences)
// à l'issue de la fonction "calcul_confiance()"
uint16_t meilleure;
uint16_t i;
meilleure = T_EXT_lue[0];
for(i=0; i<4; i++)
{
if (confiance[i+1] > confiance[i]) {meilleure = T_EXT_lue[i+1];}
}
return (meilleure);
}
void lit_temperature() // de la sonde ext 433MHz
// source = message
//(message a été constitué par la fonction "decodeBuffer()")
// destination -> tableau "T_EXT_lue[5]" des 5 valeurs lues (correspondant à une salve)
{
uint8_t p; // position de la séquence de référence fixe
uint8_t decal;
uint8_t nbBits;
int16_t valeur_lue;
String s1;
p = message.indexOf("100000",6); // séquence apparemment fixe > à la 6eme position... (
//le début du message change à chaque changement des piles !)
nbBits=10; // nb de bits à lire
decal=6; // décalage depuis la position p (ici les 6 bits de la séquence fixe)
if (p>0)
{
s1=message.substring(p+decal, p+decal+nbBits); // TFT480.Print_String(s1, 100, 100); // en binaire
//ici, par exemple s1 = "0011111011" -> 251 décimal -> 25.1 °C
// ou bien s1 = "1111100101" -> TEMPERATURE NEGATIVE "1111100101" -> -27 decimal -> -2.7°C
//s1 = "1100010110"; //-234 decimal -> -23.4°C POUR TEST valeurs négatives
valeur_lue=strbinToInt(s1, nbBits);// =316 pour 31.6°C //TFT480.Print_Number_Int(T1,220,100,3,' ',10);
Serial.print(heure_actuelle); Serial.print(":"); Serial.println(minute_actuelle);
Serial.print("Temperature EXT= ");
float temp_f = valeur_lue /10.0;
Serial.println(temp_f);
// todo // expérimenter avec des températures négatives (sonde au frigo !!)
if ((valeur_lue != 0) )
{
T_EXT_lue[num_acquisition] = valeur_lue; // on renregistre la nouvelle valeur (parmi 5 qui constituent une salve)
}
}
}
void lit_humidite()
{
// de la sonde ext 433MHz
uint8_t p; // position de la séquence de référence fixe
uint8_t decal;
uint8_t nbBits;
uint16_t valeur_precedente, valeur_lue;
String s1;
p = message.indexOf("100000",6); // séquence apparemment fixe
nbBits=7; // nb de bits à lire
decal=6+10+5; // décalage depuis la position p
if (p>0)
{
s1=message.substring(p+decal, p+decal+nbBits);
memo_H_out = H_out_lue;
valeur_lue = strbinToInt(s1,nbBits); //TFT480.Print_Number_Int(T1,220,100,3,' ',10);
valeur_precedente = memo_H_out;
if ((valeur_lue != 0) && (valeur_lue < 100) ) // 100% max !
{
if ( abs(valeur_lue - valeur_precedente) < 10 ) // delta de 10% max
{
H_out_lue = valeur_lue; // on renregistre la nouvelle valeur
}
else // au delà -> anti-glich
{
if ( valeur_lue > valeur_precedente) {H_out_lue = valeur_precedente+5;}
if ( valeur_lue < valeur_precedente) {H_out_lue = valeur_precedente-5;}
}
}
}
}
void decodeBuffer()
// transcode les 'A' et 'B' et 'C' qui représentent des durées en '0' et '1' -> destination = message
// pour la suite du traitement voir fonction "lit_temperature()"
{
Serial.println(" "); Serial.println("reception signal 433MHz sonde externe :");
int k;
char car1;
char car2;
k=0;
while (k<=73)
{
car1=buffer[k];
car2=buffer[k+1];
if ((car1=='A') &&(car2=='B'))
{
message += "0";
//TFT480.Print_String("0", 4*k, 10*aff_y);
Serial.print(0);
}
else if ((car1=='A') &&(car2=='C'))
{
message += "1";
//TFT480.Print_String("1", 4*k, 10*aff_y);
Serial.print(1);
}
k++;
}
Serial.println(' ');
efface_buffer();
aff_y++;
if (aff_y>50) {aff_y = 1;}
nb_acqui433++;
TFT480.Set_Text_Size(1);
TFT480.Set_Text_colour(BLANC);
TFT480.Print_Number_Int(nb_acqui433, Led2.x0+25, Led2.y0, 6,' ',10);
Serial.println("-----------------------------------");
}
/**
RESULTS
2020-09-07 PM
ABABACACACACABACACABABABABABABABACACACACACACACACACACACACABABACABACABABACA-␍␊
001111011000000011111111111100101001-␍␊
2020-09-08 AM
ABACABACABABABACACABABABABABABABACACACABACACABACACACACACABABACABACACACABA-␍␊
010100011000000011101101111100101110-␍␊
ABACABABABACACABABABABABABABACACACACACACACABACACACACABABACACABABACACA-␍␊
0100011000000011111110111100110011-␍␊
ABACABABABABABABACABABABABABABABACACACACACACABABACACACACABABACABACACABABA-␍␊
010000001000000011111100111100101100-␍␊
ABACABABABACABACACABABABABABABABACACACACABACACACACACACACABABACABACACABACA-␍␊
010001011000000011110111111100101101-␍␊
ABABABACABACACABABABABABABABACACACACABACACABACACACACABABACABACACABACA-␍␊
010001011000000011110110111100101101-␍␊
ABACABABABACABACACABABABABABABABACACACACABACABABACACACACABABACABACACABACA-␍␊
010001011000000011110100111100101101-␍␊
00111000 100000 0100000111 111100 110011-␍␊
01000000 100000 0011111111 111100 110010
01000001 100000 0011111110 111100 110001
01000001 100000 0100000000 111100 101111-␍␊
01000001 100000 0100000000 111100 10111-␍␊
AVEC temperature variable (R ajustable en // sur la thermistance) :
00111101 100000 0011111111 111100 101001-␍␊
01001000 100000 0011111011 11110 0101010-␍␊ 251 (sans la R ajustable en //) -> 25,1 °C ?
01001000 100000 0100011001 11110 0100110-␍␊ 281 (sans la R mais chauffée avec le doigt) -> 28,1 °C ?
01100110 100000 0110101100 11110 0011111-␍␊ 428
10101110 100000 0111001100 11110 0100010-␍␊ 460
10100101 100000 0111011100 11110 0100001-␍␊ 476
10011100 100000 0111101110 11110 0100000-␍␊ 494
10001101 100000 1000001110 11110 0011101-␍␊ 526
10000100 100000 1000100001 11110 0011100-␍␊ 545
01110100 100000 1001001011 11110 0011011-␍␊ 587
01100101 100000 1001110111 11110 0011001-␍␊ 631
01011000 100000 1010100010 11110 0011011-␍␊ 674
01000110 100000 1011000010 11110 0011011-␍␊ 706
00011111 100000 1011011010 11110 0011010-␍␊ 730
10000001 100000 0010000111 11110 0111101
11000101 100000 0000000001 11110 0111100 ␍␊
11000101 100000 0000000010 11110 0111100
11000101 100011 1111111011 11110 0111100 ␍␊ <- TEMPERATURE NEGATIVE
11000101 100011 1111100101 11110 0111100 ␍␊ <- TEMPERATURE NEGATIVE "1111100101" -> -27 decimal -> -2.7°C
11000101 100011 1111100011 11110 1000000
11000101 100000 0000010101 11110 0111011 ␍␊
11000101 100000 0000010101 11110 0111100
ANALYSE "colonne" 3 (= température):
0110101100 = 428
0111001100 = 460
0111011100 = 476
0111101110 = 494
1000001110 = 526
1000100001 = 545
1001001011 = 587
1001110111 = 631
1010100010 = 674
1011000010 = 706
1011011010 = 730
Humidité (colonne 5):
**/
/**--------------------------------------------------------
partie COMMUNE
--------------------------------------------------------**/
uint8_t decToBcd( int val )
{
return (uint8_t) ((val / 10 * 16) + (val % 10));
}
void setDateTime()
{
// mise à l'heure exacte
// cette fonction ne doit être appelée qu'une fois pour mettre l'horloge RTC à l'heure, ou en cas de changement de sa pile
// il faut la parametrer correctement au préalable en fonction de la date et de l'heure actuelle
#define DS1307_ADDRESS 0x68
byte zero = 0x00;
byte second = 25; //0-59
byte minute = 54; //0-59
byte hour = 14; //0-23
byte weekDay = 5; //1-7
byte monthDay = 20; //1-31
byte month = 7; //1-12
byte year = 23; //0-99
Wire.beginTransmission(DS1307_ADDRESS);
Wire.write(zero);
Wire.write(decToBcd(second));
Wire.write(decToBcd(minute));
Wire.write(decToBcd(hour));
Wire.write(decToBcd(weekDay));
Wire.write(decToBcd(monthDay));
Wire.write(decToBcd(month));
Wire.write(decToBcd(year));
Wire.write(zero); //start
Wire.endTransmission();
}
double MoonPhase(int Year,int Month,int Day)
{
double M;
int XYear,Century;
if(Month<=2)
{
Year--;
Month+=12;
}
Month-=3;
XYear=Year % 100;
Century=(int(Year / 100)*146097) >> 2;
XYear=(XYear*1461) >> 2;
M=(floor(((((Month*153)+2)/ 5)+Day)+1721119+XYear+Century)+4.867)/29.53058;
return fabs(2*(M-floor(M))-1);
}
void setup()
{
// premiere_passe=1;
init_ports();
Serial.begin(115200);
pinMode(INPUT_PIN, INPUT);
attachInterrupt((INPUT_PIN), changed, CHANGE);
Serial.println("Bonjour");
sram = freeRam(); Serial.print("01-freeRam="); Serial.println(sram); //=1552 sans les fonctions SD, =518 avec
//RAPPEL: ATmega2560 SRAM = 8KB...
i_buff=0;
efface_buffer();
TFT480.Init_LCD();
clear_screen();
delay(100);
TFT480.Set_Rotation(1);
Init_SDcard();
/** pour tests
uint8_t r=123;
uint8_t g=228;
uint8_t b=17;
uint16_t couleur1;
couleur1 = TFT480.Color_To_565(r, g, b);
RGB565_to_888(couleur1, &r, &g, &b);
Serial.print("r="); Serial.println(r);
Serial.print("g="); Serial.println(g);
Serial.print("b="); Serial.println(b);
**/
/* TESTS affichage d'images lues sur la SDcard
affi_img(100, 200, "/bmp/lune5.bmp");
delay(300);
affi_img(200,10, "/bmp/skippy.bmp");
delay(300);
affi_img(300,50, "/bmp/soleil.bmp");
delay(300);
affi_img(0,0, "/bmp/montagne.bmp");
delay(300);
*/
sram = freeRam(); Serial.print("ici -> freeRam="); Serial.println(sram); //468
/* TEST AFFICHAGE DE NOMBREUSES ICONES*/
/*
uint16_t x,y;
x=0;
y=0;
for(uint16_t i=0; i<300; i++)
{
affi_img(x,y, "/bmp/soleil.bmp"); // ici c'est ok; plus loin dans une fonction, -> crash de stack
Serial.println(i);
sram = freeRam(); Serial.print("freeRam="); Serial.println(sram); //410
x+=20;
if (x>450){x=0; y+=20;}
}
*/
/* ********************************** */
//dessine_arc_en_ciel();
//delay(2000);
/*
clear_screen();
*/
aff_y=0;
T_INTE_lue=0;
T_EXT_retenue=0;
Pression_lue=1013; //hPa
H_in_lue = 50; // 50%
H_out_lue = 50; // 50%
affiche_meteo();
num_acquisition=0;
affiche_pannel();
affiche_heure(); //et initialise les variables : annee_actuelle, mois_actuel, jour_actuel, heure_actuelle, minute_actuelle
affiche_date(); // attention : executer la ligne précedente au moins 1 fois au préalable pour initialiser les variables
affiche_LEDS();
affiche_SCOPE();
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(VIOLET);
TFT480.Set_Text_Size(2);
TFT480.Print_String("Initialisations...", 30, 160);
/**
Rappels :
Scope_1.dx = 450
lst_records[500]
**/
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(BLANC);
TFT480.Set_Text_Size(1);
init_BMP280();
// setDateTime(); // à décommenter (une fois, à recommenter ensuite) pour mise à l'heure (paramétrer correctement la fonction au préalable !)
TT1=0;
// TT2=50;
// TT3=0;
// TT4=0;
/** --------------------------------------------
les 3 lignes ci-dessous sont à commenter, sauf pour initaliser éventuellement les enregistrements sur la SDcard, pour test
si elles ne sont pas commentées, les enregistrements seront perdus à chaque redémarrage de la carte Arduino **/
// charge_enregistrements(); // pour afficher rapidement (sans attendre les acquisitions) des sinusoïdes...
if (0) // =1 à la 1ere utilisation
{
init_lst_records();
trace_sur_Scope();
write_scope_on_SDcard();
}
/** --------------------------------------------**/
read_scope_on_SDcard();
trace_sur_Scope();
// TFT480.Set_Text_Back_colour(NOIR);
// TFT480.Set_Text_colour(ORANGE[0],ORANGE[1],ORANGE[2]);
// TFT480.Set_Text_Size(1);
// TFT480.Print_String("en attente de donnees reçues", 150, 260);
affiche_Lune();
sram = freeRam(); Serial.print("02-freeRam="); Serial.println(sram); // 1067
}
void trace_sur_Scope() // affiche les courbes
{
Serial.println(" "); Serial.println("trace_sur_Scope()");
verifie_data_scope(); // pour visu sur le port série (USB)
//efface_glitch_T();
calcul_moyenne_T_sur_toutes();
determine_Tmin_Tmax();
//fixe_Tmin_tmax();
calcul_Gradu_ech_T();
efface_glitch_P();
determine_Pmin_Pmax();
calcul_ech_P();
if ((acqui_valide ==1) || (scope_dat_ok==1))
{
Scope_1.traceGraduation(); // ce qui efface tout au préalable
affiche_courbeH();
affiche_courbeP();
affiche_courbeT();
}
}
void affiche_meteo()
{
// sur l'écran TFT
Serial.println("affiche_meteo()");
float T_in_f = T_INTE_lue / 10.0; // float
Etiq_1.affiche_float(T_in_f, 4, 1, " C", 0, 0);
TFT480.Set_Draw_color(Etiq_1.couleur_txt);
TFT480.Draw_Circle(Etiq_1.x0+157, Etiq_1.y0+Etiq_1.dy-24, 3); // affiche le signe "°"
/**
// utile lors de la phase de mise au point...
// Affichage de 5 valeurs reçues, constituant une salve, et de leur indice de confiance
TFT480.Set_Draw_color(NOIR);
TFT480.Fill_Rectangle(434, 4, 476, 54); // efface tout pour crééer une "animation" de l'affichage même si valeurs inchangées
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(BLANC);
TFT480.Set_Draw_color(GRIS);
TFT480.Draw_Fast_VLine(430, 4, 55);
// Affichage des détails de la salve reçue en petits chiffres, à droite
TFT480.Set_Text_Size(1);
uint16_t x0=434;
uint16_t y0=0;
y0+= 5; TFT480.Print_Number_Int(T_EXT_lue[0],x0,y0,3,' ',10); TFT480.Print_Number_Int(confiance[0],x0+30,y0,1,' ',10);
y0+=10; TFT480.Print_Number_Int(T_EXT_lue[1],x0,y0,3,' ',10); TFT480.Print_Number_Int(confiance[1],x0+30,y0,1,' ',10);
y0+=10; TFT480.Print_Number_Int(T_EXT_lue[2],x0,y0,3,' ',10); TFT480.Print_Number_Int(confiance[2],x0+30,y0,1,' ',10);
y0+=10; TFT480.Print_Number_Int(T_EXT_lue[3],x0,y0,3,' ',10); TFT480.Print_Number_Int(confiance[3],x0+30,y0,1,' ',10);
y0+=10; TFT480.Print_Number_Int(T_EXT_lue[4],x0,y0,3,' ',10); TFT480.Print_Number_Int(confiance[4],x0+30,y0,1,' ',10);
// Affichage en VERT de la valeur retenue
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(GRIS);
TFT480.Print_String("ok->", 410,57);
TFT480.Set_Text_colour(VERT);
y0+=12; TFT480.Print_Number_Int(meilleure_valeur(),x0,y0,3,' ',10);
**/
float T_out_f = T_EXT_retenue / 10.0; // float
Etiq_2.affiche_float(T_out_f, 4, 1, " C", Tmin/10, Tmax/10);
TFT480.Set_Draw_color(Etiq_2.couleur_txt);
TFT480.Draw_Circle(Etiq_2.x0+157, Etiq_2.y0+Etiq_2.dy-24, 3); // affiche le signe "°"
Etiq_5.affiche_int(Pression_lue, 4, "hPa", Pmin, Pmax);
// if (stable==1) {Led3.setCouleur(VERT);} else {Led3.setCouleur(ORANGE);}
Etiq_4.affiche_int(H_out_lue, 2, "", 0, 0);
TFT480.Set_Text_Size(2);
TFT480.Print_String("%", Etiq_4.x0+60,Etiq_4.y0+35);
}
void analyse_signal_primaire()
{
char lettre_i;
pulseWidth = micros() - memo_micros1; // VOIR la doc micros() -> nb de microsecondes depuis le démarrage de la carte
memo_micros1 = micros();
//Serial.print('\n');
//Serial.print(pulseWidth);
/**
Une analyse des signaux fait apparaitre ceci:
4 durées caractéristiques 490us ; 95us ; 1940us; 3700us
- 490us c'est la durée des tops HIGHT (tous identiques)
- 950ums ; 1940ms ; 3700ms sont les différentes durées à l'état LOW qui séparent les tops ()
appelons ces durées par une lettre :
A -> 490us
B -> 950us
C -> 1940us
D -> 3700us
**/
if ( (pulseWidth < 460) )
{
return;
}
if ( (pulseWidth >= 460) && (pulseWidth < 530) )
{
//Serial.print('A');
lettre_i = 'A';
buffer[i_buff]= lettre_i; //memorisation de la lettre
i_buff++;
}
if ( (pulseWidth >= 910) && (pulseWidth < 1100) )
{
//Serial.print('B');
lettre_i = 'B';
buffer[i_buff]= lettre_i;
i_buff++;
}
if ( (pulseWidth >= 1900) && (pulseWidth < 1970) )
{
//Serial.print('C');
lettre_i = 'C';
buffer[i_buff]= lettre_i;
i_buff++;
}
if (pulseWidth >= 3000) // sonne la fin de la séquence...
{
if (i_buff>=72)
{
Led2.setCouleur(JAUNE);
Serial.println(' ');
decodeBuffer();
Serial.print(" ");
lit_temperature(); // de la sonde ext 433MHz
lit_humidite(); // de la sonde ext 433MHz
message="";
//memo_micros2 = micros();
efface_buffer();
// premiere_passe==0;
num_acquisition++;
if (num_acquisition >=5)
{
num_acquisition=0;
acqui_valide =1;
calcul_confiance();
T_EXT_retenue = meilleure_valeur();
//determine_Tmin_Tmax();
fixe_Tmin_tmax();
calcul_Gradu_ech_T();
acqui_PressionTemperature();
affiche_meteo();
}
}
i_buff=0;
}
/**
résultat :
AB AC AB AB AC AB AC AC AC AB AB AB AB AB AB AB AC AC AC A
0 1 0 0 1 0 1 1 1 0 0 0 0 0 0 0 1 1 1
Les motifs qui apparaissent sont AB et AC
Il est vraissemblable que AB code pour 0 et AC pour 1 (ou l'inverse))
**/
}
void calcul_jour_de_la_semaine()
{
// d'après l'Algorithme de Mike Keith
uint16_t d, m, y, z, jds;
d=jour_actuel;
m=mois_actuel;
y=annee_actuelle;
if (m>=3)
{
jds = ( ((23*m)/9) + d + 4 + y + (y/4) - (y/100) + (y/400) - 2 ) % 7;
}
else
{
z = y-1;
jds = ( ((23*m)/9) + d + 4 + y + (z/4) - (z/100) + (z/400) ) % 7;
}
jour_de_la_semaine = jds;
}
String conv_time(uint8_t t)
{
String r;
r=String(t);
if (t<10) {r="0"+r;}
return r;
}
/*
RAPPEL :
tm.Day // uint8_t
tm.Month
tmYearToCalendar(tm.Year)
tm.Hour)
tm.Minute)
tm.Second)
*/
void affiche_heure()
{
//Serial.println("affiche_heure()");
String heure;
String heure_sec;
if (RTC.read(tm))
{
annee_actuelle = tmYearToCalendar(tm.Year);
mois_actuel = tm.Month;
jour_actuel = tm.Day;
heure_actuelle = tm.Hour;
minute_actuelle = tm.Minute;
seconde_actuelle = tm.Second;
/*
Serial.print(" annee="); Serial.println(annee_actuelle);
Serial.print(" mois="); Serial.println(mois_actuel);
Serial.print(" jour="); Serial.println(jour_actuel);
Serial.print(" h="); Serial.println(heure_actuelle);
Serial.print(" mn=");Serial.println(minute_actuelle);
Serial.println("------");
*/
heure = conv_time(tm.Hour)+":"+conv_time(tm.Minute);
heure_sec = heure+":"+conv_time(tm.Second)+" ";
TFT480.Set_Text_Size(4);
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(BLANC);
TFT480.Print_String(heure_sec, 290, 105);
if (seconde_actuelle == 0){ tt_les_1mn(); }
if (((minute_actuelle %5) == 0) && (seconde_actuelle == 10)) { tt_les_5mn(); }
if (((minute_actuelle %30) == 0) && (seconde_actuelle == 20)) { tt_les_30mn();}
}
}
void affiche_date()
{
String date;
date = String(conv_time(jour_actuel))+" ";
switch (mois_actuel)
{
case 1: {date+="JAN"; } break;
case 2: {date+="FEV"; } break;
case 3: {date+="MARS";} break;
case 4: {date+="AVR"; } break;
case 5: {date+="MAI"; } break;
case 6: {date+="JUIN";} break;
case 7: {date+="JUIL";} break;
case 8: {date+="AOUT";} break;
case 9: {date+="SEPT";} break;
case 10: {date+="OCT"; } break;
case 11: {date+="NOV"; } break;
case 12: {date+="DEC"; } break;
}
date += " "+String(annee_actuelle);
//date;
uint16_t x0=290, y0=75;
TFT480.Set_Text_Size(2);
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(GRIS_CLAIR);
TFT480.Print_String(date, x0+45, y0);
calcul_jour_de_la_semaine();
switch (jour_de_la_semaine)
{
case 0: { TFT480.Print_String("DIM", x0, y0);} break;
case 1: { TFT480.Print_String("LUN", x0, y0);} break;
case 2: { TFT480.Print_String("MAR", x0, y0);} break;
case 3: { TFT480.Print_String("MER", x0, y0);} break;
case 4: { TFT480.Print_String("JEU", x0, y0);} break;
case 5: { TFT480.Print_String("VEN", x0, y0);} break;
case 6: { TFT480.Print_String("SAM", x0, y0);} break;
}
}
void print_meteo_on_SDcard()
{
String date;
String heure;
String Str1;
// Led1.setCouleur(VERT);
// pour éviter les problèmes avec les températures en °C négatives (< 0°C l'hiver), on passe en kelvin
uint16_t T_EXT_kelvin = T_EXT_retenue +2730; // en prenant T0abs= -273°C (et pas -273,15 °C...)
uint16_t T_INTE_kelvin = T_INTE_lue +2730;
date = String(annee_actuelle)+"-"+conv_time(mois_actuel)+"-"+conv_time(jour_actuel);
heure = conv_time(heure_actuelle)+":"+conv_time(minute_actuelle);
Str1 = date +" [ "+ heure + " ]";
Str1 += " ; T(ext)=" + String(T_EXT_kelvin)+ " ; T(int)=" + String(T_INTE_kelvin);
Str1 +=" ; H=" +String(H_out_lue)+ " ; P=" +String(Pression_lue);
Serial.println(" ");
Serial.println("ecriture data sur SDcard :");
Serial.print(Str1); // sur le port USB, pour info, à lire avec un terminal série (CuteCom...)
Serial.println(" ");
File dataFile = SD.open("data.txt", FILE_WRITE); // ouVERTure du fichier en écriture
if (dataFile)
{
dataFile.println(Str1); // écriture 1 ligne à la fin du fichier "data.txt" sur la micro SDcard
dataFile.close(); // referme le fichier
// résultat sur la SDcard : ajout d'une ligne comme celle-ci :
// 2020-09-19 [ 15:17 ] ; T(ext)=3004 ; T(int)=3018 ; H=62 ; P=1003
//Note : pour lire les températures il faut retrancher 2730 puis /10
}
else { Serial.println("error opening data.txt");} // pour info
delay(100);
// Led1.setCouleur(NOIR);
nb_wr_SD++;
/*
TFT480.Set_Text_Size(1);
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(BLANC);
TFT480.Print_Number_Int(nb_wr_SD,440,70,6,' ',10);
*/
}
void write_scope_on_SDcard() // enregistre en binaire la courbe de T°C affichée sur le scope
{
todo_wr_scope_on_sd=0;
if (SDcardOk==0) {return;}
Serial.println("write_scope_on_SDcard()");
// enregistre le contenu du "scope" afin de le réafficher après une coupure de l'alimentation
// Led1.setCouleur(BLEU_CLAIR);
if(SD.exists("scope.dat")) {SD.remove("scope.dat");} // efface le fichier précédent s'il existe
File binFile1 = SD.open("scope.dat", FILE_WRITE); //re-création et ouverture du fichier binaire (vierge) en écriture
if (binFile1)
{
binFile1.write((const uint8_t *)&lst_records, sizeof(lst_records));
binFile1.close(); // referme le fichier
}
// Led1.setCouleur(NOIR);
}
void read_scope_on_SDcard()
{
if (SDcardOk==0) {return;}
Serial.println(" ");
Serial.println("lecture du fichier scope.dat sur SDcard");
Serial.println(" ");
File binFile1 = SD.open("scope.dat", FILE_READ); //ouverture du fichier en lecture
if (binFile1)
{
binFile1.read((uint8_t *)&lst_records, sizeof(lst_records));
binFile1.close(); // referme le fichier
}
}
void RAZ_data()
{
Serial.println(" "); Serial.println("RAZ_data()");
todo_init_record =0;
// TFT480.Set_Draw_color(NOIR);
// TFT480.Fill_Rectangle(19,146, 467,316);
Scope_1.efface_surface();
// Led3.setCouleur(NOIR);
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(VIOLET);
TFT480.Set_Text_Size(3);
TFT480.Print_String("RAZ SCOPE DATA", 120, 250);
delay(1000);
Scope_1.efface_surface();
init_lst_records();
write_scope_on_SDcard();
read_scope_on_SDcard();
trace_sur_Scope();
}
void tt_les_1mn()
{
Serial.println("----------------------------") ;
Serial.print("tt_les_1mn() ") ;
Serial.print(heure_actuelle); Serial.print(":"); Serial.println(minute_actuelle);
Serial.print("acqui_valide=");Serial.println(acqui_valide);
// TT2=0;
// TT3+=1;
// TFT480.Set_Text_Size(1);
// TFT480.Print_Number_Int(TT3,390,15,3,' ',10);
num_acquisition=0;
calcul_confiance();
T_EXT_retenue = meilleure_valeur();
//determine_Tmin_Tmax();
fixe_Tmin_tmax();
calcul_Gradu_ech_T();
acqui_PressionTemperature();
affiche_meteo(); // dans les cadres du pannel, mais PAS dans le "scope"
if ((todo_init_record==1)&&(acqui_valide)) {RAZ_data();}
if (todo_wr_scope_on_sd==1) {write_scope_on_SDcard();}
//trace_sur_Scope(); // ne pas décommenter ! sauf pour test
}
void tt_les_5mn()
{
uint16_t x2;
uint16_t y2;
Serial.println("----------------------------") ;
Serial.print("tt_les_5mn() ");
Serial.print(heure_actuelle); Serial.print(":"); Serial.println(minute_actuelle);
//Led4.setCouleur(BLANC);
record_enr_actuel();
decale_enregistrements(n_record_max);
trace_sur_Scope();
affiche_date();
/*
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(BLANC);
TFT480.Set_Text_Size(1);
x2 = 300;
y2 = 70;
TFT480.Print_String("WR SDcard", x2, y2);
TFT480.Print_Number_Int(10-TT3,x2+55,y2,3,' ',10); //affiche le temps restant avant enregistrement sur la SDcard
TFT480.Print_String("mn", x2+70, y2);
Led4.setCouleur(NOIR);
*/
}
void tt_les_30mn()
{
Serial.println("----------------------------") ;
Serial.print("tt_les_30mn() ");
Serial.print(heure_actuelle); Serial.print(":"); Serial.println(minute_actuelle);
print_meteo_on_SDcard(); // en ASCII dans le fichier "data.txt" (ajout à la fin du fichier, en conservant la totalité des acquis)
delay(500);
write_scope_on_SDcard(); // en binaire dans le fichier "scope.dat" (remplace le fichier - ne mémorise que 36h au total)
delay(500);
affiche_Lune();
}
void loop()
{
memo_etat = etat;
etat = digitalRead(INPUT_PIN);
if (etat != memo_etat)
{
analyse_signal_primaire();
}
if ((portPIN_switch0 & pin_switch0) == 0)
{
write_TFT_on_SDcard();
/*
todo_init_record =1;
// Led3.setCouleur(ROUGE);
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(BLANC);
TFT480.Set_Text_Size(2);
TFT480.Print_String("demande de RAZ scope.dat", 80, 150);
TFT480.Print_String("elle se fera sur la minute", 70, 170);
TFT480.Print_String("suivant acquisition valide", 70, 190);
Serial.println("demande de RAZ scope.dat");
while ((portPIN_switch0 & pin_switch0) == 0 ) {;} // attente relachement du bouton
delay(1000);
*/
}
TT1+=1;
if (TT1 >= 1000) // tous les 1000 passages dans la boucle pour ne pas trop charger le processeur
{
TT1=0;
temps_ecoule = micros() - memo_micros2;
if (temps_ecoule >= 1E6) // (>=) et pas strictement égal (==) parce qu'alors on rate l'instant en question
// toutefois cette façon de procéder introduit une petite imprécision sur la chronologie des évenements de l'ordre de +1s/mn
// les enregistrement sur la SDcard restent toutefois datés exactement par le module RTC
// l'affichage sur le scope, lui, est un peu imprécis.
{
// ici toutes les 1s
memo_micros2 = micros();
//TT2+=1;
//TFT480.Set_Text_Back_colour(NOIR);
//TFT480.Set_Text_colour(BLANC);
//TFT480.Set_Text_Size(1);
//TFT480.Print_Number_Int(60-TT2,370,84,3,' ',10); // petits chiffres à droite ()chronomètre)
affiche_heure();
Led2.setCouleur(NOIR);
}
}
}
/** ***********************************************************************************
AFFICHAGE IMAGE.bmp
***************************************************************************************/
/**
Rappel et décryptage de la fonction Color_To_565 : (elle se trouve dans le fichier LCDWIKI_KBV.cpp)
//Pass 8-bit (each) R,G,B, get back 16-bit packed color
uint16_t Color_To_565(uint8_t r, uint8_t g, uint8_t b)
{
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3);
}
0xF8 = 11111000
0xFC = 11111100
(r & 0xF8) -> 5 bit de gauche de r (on ignore donc les 3 bits de poids faible)
(g & 0xFC) -> 6 bit de gauche de g (on ignore donc les 2 bits de poids faible)
(b & 0xF8) -> 5 bit de gauche de b (on ignore donc les 3 bits de poids faible)
rrrrr---
gggggg--
bbbbb---
après les décallages on obtient les 16 bits suivants:
rrrrr---========
gggggg--===
===bbbbb
soit après le ou :
rrrrrggggggbbbbb
calcul de la Fonction inverse :
RGB565_to_888
**/
void RGB565_to_888(uint16_t color565, uint8_t *R, uint8_t *G, uint8_t *B)
{
*R=(color565 & 0xFFFFF800) >> 8;
*G=(color565 & 0x7E0) >> 3;
*B=(color565 & 0x1F) << 3 ;
}
uint32_t bmp_width;
uint32_t bmp_heigh;
uint16_t read_16(File fp)
{
uint8_t low;
uint16_t high;
low = fp.read();
high = fp.read();
return (high<<8)|low;
}
uint32_t read_32(File fp)
{
uint16_t low;
uint32_t high;
low = read_16(fp);
high = read_16(fp);
return (high<<8)|low;
}
void write_16(uint16_t v16, File fp)
{
uint8_t low, high;
low = v16 & 0xFF;
high= v16 >>8;
fp.write(low);
fp.write(high);
}
void write_32(uint32_t v32, File fp)
{
uint16_t low, high;
low = v32 & 0xFFFF;
high= v32 >>16;
write_16(low, fp);
write_16(high, fp);
}
bool teste_bmp_header(File fp)
{
if(read_16(fp) != 0x4D42) { return false; } // (2 bytes) The header field used to identify the BMP
read_32(fp); // (4 bytes) get bmp size (nombre total d'octets)
read_32(fp); // (4 bytes) get creator information
bmp_offset = read_32(fp); // (4 bytes) get offset information
read_32(fp);//get DIB information
bmp_width = read_32(fp); //get width and heigh information
bmp_heigh = read_32(fp);
if(read_16(fp)!= 1) {return false;}
read_16(fp);
if(read_32(fp)!= 0) {return false;}
return true;
}
void draw_bmp(uint16_t x0, uint16_t y0, File* fp)
{
//sram = freeRam(); Serial.print("03-freeRam="); Serial.println(sram);
uint16_t i,j,k,p,m=0;
uint8_t bmp_data[2*3]={0};
uint16_t bmp_color[2];
fp->seek(bmp_offset);
for(i=0; i<bmp_heigh; i++)
{
for(j=0; j<(bmp_width/2); j++)
{
m=0;
fp->read(bmp_data,2*3);
for(k=0; k<2; k++)
{
bmp_color[k]= TFT480.Color_To_565(bmp_data[m+2], bmp_data[m+1], bmp_data[m+0]);
m+=3;
}
for(p=0; p<2; p++)
{
TFT480.Set_Draw_color(bmp_color[p]);
TFT480.Draw_Pixel(y0+j*2+p, x0+i);
}
}
}
}
void affi_img(uint16_t x0, uint16_t y0, const char* filename1)
{
File bmp_file;
bmp_file = SD.open(filename1);
if(!bmp_file)
{
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(BLANC);
TFT480.Set_Text_Size(2);
TFT480.Print_String("didnt find bmp",0,10);
delay(2000);
}
if(!teste_bmp_header(bmp_file))
{
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(BLANC);
TFT480.Set_Text_Size(2);
TFT480.Print_String("bad bmp !",0,0);
delay(2000);
return;
}
draw_bmp(x0, y0, &bmp_file);
bmp_file.close();
// delay(1000);
}
/** ***********************************************************************************
CAPTURE D'ECRAN vers SDcard
***************************************************************************************/
void write_TFT_on_SDcard() // enregistre
{
if (SDcardOk==0) {return;}
uint16_t x, y;
uint16_t color565;
uint16_t bmp_color;
uint8_t R, G, B;
if( ! SD.exists("/bmp/capture2.bmp")) {return;}
File File1 = SD.open("/bmp/capture2.bmp", FILE_WRITE); // ouverture du fichier binaire (vierge) en écriture
if (File1)
{
/*
Les images en couleurs réelles BMP888 utilisent 24 bits par pixel:
Il faut 3 octets pour coder chaque pixel, en respectant l'ordre de l'alternance bleu, vert et rouge.
*/
uint16_t bmp_offset = 138;
File1.seek(bmp_offset);
TFT480.Set_Draw_color(NOIR);
TFT480.Set_Text_colour(BLANC);
TFT480.Set_Text_Size(1);
for (y=320; y>0; y--)
{
for (x=0; x<480; x++)
{
color565=TFT480.Read_Pixel(x, y);
RGB565_to_888(color565, &R, &G, &B);
File1.write(B); //G
File1.write(G); //R
File1.write(R); //B
}
TFT480.Print_Number_Int(y/10, 50, 300, 3, '0', 10); // affiche compte à rebour
}
File1.close(); // referme le fichier
TFT480.Fill_Rectangle(50, 300, 65, 310); // efface le compte à rebour
}
}
/** ***************************************************************************************
CLASS Etiq3 // affiche un nombre ou un petit texte dans un rectangle
ainsi que (en plus petit) deux valeurs supplémentaires, par ex: les valeurs mini et maxi
********************************************************************************************/
// Constructeur
Etiq3::Etiq3()
{
}
void Etiq3::init(uint16_t xi, uint16_t yi, uint16_t dxi, uint16_t dyi, char nom_i[12])
{
x0 = xi;
y0 = yi;
dx = dxi;
dy = dyi;
for (int i=0; i<12; i++) {nom[i]=nom_i[i];}
nom[12]='\0'; // zero terminal
TFT480.Set_Text_Back_colour(0, 0, 0);
TFT480.Set_Text_colour(10, 10, 5);
traceCadre();
affi_nom();
}
void Etiq3::traceCadre()
{
TFT480.Set_Draw_color(couleur_cadre);
TFT480.Draw_Rectangle(x0, y0+5, x0+dx, y0+dy);
}
void Etiq3::efface()
{
TFT480.Set_Draw_color(couleur_fond);
TFT480.Fill_Rectangle(x0+1, y0+6, x0+dx-1, y0+dy-1);
affi_nom();
}
void Etiq3::affi_nom()
{
// sur le coin en haut à gauche
TFT480.Set_Text_Back_colour(couleur_fond);
TFT480.Set_Text_colour(couleur_nom);
TFT480.Set_Text_Size(2);
TFT480.Print_String(nom, x0, y0);
}
void Etiq3::setCouleurTxt(uint16_t couleur_i)
{
couleur_txt = couleur_i;
}
void Etiq3::setCouleurCadre(uint16_t couleur_i)
{
couleur_cadre = couleur_i;
}
void Etiq3::setCouleurFond(uint16_t couleur_i)
{
couleur_fond = couleur_i;
}
void Etiq3::setCouleurNom(uint16_t couleur_i)
{
couleur_nom = couleur_i;
}
void Etiq3::flashFond(uint16_t couleur_i)
{
couleur_fond = couleur_i;
TFT480.Set_Text_colour(couleur_fond);
TFT480.Fill_Rectangle(x0, y0, x0+dx, y0+dy);
delay(10);
TFT480.Set_Text_colour(NOIR);
TFT480.Fill_Rectangle(x0, y0, x0+dx, y0+dy);
traceCadre();
affi_nom();
}
void Etiq3::affiche_int(uint32_t valeur, uint8_t nb_chiffres, char txt_unite_i[3], uint32_t v_min, uint32_t v_max)
{
// 'valeur' sera afficée en gros, 'v_min' et 'v_max' en plus petit dans le coin du cadre (facultativement)
// pour ne pas afficher 'v_min' et 'v_max', il faut que toutes les deux soient ==0
uint16_t z;
for (int i=0; i<3; i++) {txt_unite[i]=txt_unite_i[i];}
txt_unite[3]='\0'; // zero terminal
efface(); // efface le contenu précédent
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(couleur_txt);
TFT480.Set_Text_Size(4);
z=0; if ((valeur<1000)&&(valeur>100)) {z=20;}
TFT480.Print_Number_Int(valeur, x0+10+z, y0+18, nb_chiffres, ' ', 10);
TFT480.Set_Text_colour(couleur_txt);
TFT480.Set_Text_Size(2);
TFT480.Print_String(txt_unite, x0+140,y0+35); // ex : mm, kHz, etc...
if((v_min !=0 )&&(v_max !=0 ))
{
// affiche valeurs min et max en haut à droite
TFT480.Set_Text_colour(GRIS_CLAIR);
TFT480.Set_Text_Size(1);
z=0; if (v_max<1000) {z=5;}
TFT480.Print_Number_Int(v_max,x0+140+z,y0+9,3,' ',10);
z=0; if (v_min<1000) {z=5;}
TFT480.Print_Number_Int(v_min,x0+140+z,y0+17,3,' ',10);
}
}
void Etiq3::affiche_float(float valeur, uint8_t nb_chiffres, uint8_t nb_dec, char txt_unite_i[3], float v_min, float v_max)
{
for (int i=0; i<3; i++) {txt_unite[i]=txt_unite_i[i];}
txt_unite[3]='\0'; // zero terminal
efface(); // efface le contenu précédent
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(couleur_txt);
TFT480.Set_Text_Size(4);
//RAPPEL: Print_Number_Float(double num, uint8_t dec, int16_t x, int16_t y, uint8_t divider, int16_t length, uint8_t filler);
TFT480.Print_Number_Float(valeur, nb_dec, x0+20, y0+18, '.', nb_chiffres, ' ');
TFT480.Set_Text_colour(couleur_txt);
TFT480.Set_Text_Size(2);
TFT480.Print_String(txt_unite, x0+140,y0+35); // ex : deg, hPa, mm, kHz ...
if((v_min !=0 )||(v_max !=0 ))
{
uint16_t z;
// affiche valeurs min et max en haut à droite
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(GRIS_CLAIR);
TFT480.Set_Text_Size(1);
z=0; if (v_max<10) {z=6;}
TFT480.Print_Number_Float(v_max, 1, x0+140+z, y0+9, '.', 3, ' ');
z=0; if (v_min<10) {z=6;}
TFT480.Print_Number_Float(v_min, 1, x0+140+z, y0+17, '.', 3, ' ');
}
}
void Etiq3::affiche_HEXA(uint32_t valeur)
{
// affiche un nombre en representation hexadécimale
// 16 nb_signes hexa max
uint8_t r;
uint8_t i;
char tbl[9];
char signes[17] = "0123456789ABCDEF";
for (i=0; i<8; i++)
{
r= valeur % 16; // modulo (reste de la division)
valeur /= 16; // quotient
tbl[7-i]=signes[r];
};
tbl[8]='\0';
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(CYAN);
// TFT480.setFont(SmallFont);
TFT480.Print_String(tbl, x0+10,y0+15);
}
void Etiq3::affiche_texte(char txt_i[10])
{
for (int i=0; i<10; i++) {texte[i]=txt_i[i];}
texte[10]='\0'; // zero terminal
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(BLANC);
// TFT480.setFont(BigFont);
TFT480.Set_Text_Size(3);
TFT480.Print_String(texte, x0+5,y0+18);
}
void Etiq3::affiche_string(String txt_i)
{
TFT480.Set_Text_Back_colour(0, 0, 0);
TFT480.Set_Text_colour(BLANC);
// TFT480.setFont(BigFont);
TFT480.Set_Text_Size(3);
TFT480.Print_String(txt_i, x0+8,y0+18);
}
/** ***********************************************************************************
CLASS Scope // affiche des courbes dans une surface rectangulaire
***************************************************************************************/
// Constructeur
Scope::Scope()
{
}
void Scope::init(uint16_t xi, uint16_t yi, uint16_t dxi, uint16_t dyi)
{
x0 = xi;
y0 = yi;
dx = dxi;
dy = dyi;
TFT480.Set_Draw_color(couleur_cadre);
TFT480.Draw_Rectangle(x0, y0, x0+dx, y0+dy);
}
void Scope::efface_surface()
{
Serial.println("Scope::efface_surface()");
TFT480.Set_Draw_color(NOIR);
TFT480.Fill_Rectangle(x0+1, y0+1, x0+dx-2, y0+dy-2);
// Rappel : Scope_1.init(18,145, 450,172);
// TFT480.Fill_Rectangle(19,146, 467,316); // ok
}
void Scope::setCouleurCadre(uint16_t couleur_i)
{
couleur_cadre = couleur_i;
}
void Scope::setCouleurTrace(uint16_t couleur_i)
{
couleur_trace = couleur_i;
}
void Scope::traceCadre()
{
TFT480.Set_Draw_color(couleur_cadre);
TFT480.Draw_Rectangle(x0, y0, x0+dx, y0+dy);
TFT480.Set_Draw_color(NOIR);
TFT480.Fill_Rectangle(x0+1, y0+1, x0+dx-2, y0+dy-2);
}
void Scope::efface_libel_gradu()
{
TFT480.Set_Draw_color(NOIR);
TFT480.Fill_Rectangle(x0-20, y0+1, x0-2, y0+dy-2);
}
void Scope::traceGraduation()
{
int16_t n, nombre;
float y1,Ti;
int16_t y2;
uint16_t xi;
uint16_t i;
efface_surface();
efface_libel_gradu();
setCouleurTrace(GRIS);
TFT480.Set_Text_Back_colour(NOIR);
TFT480.Set_Text_colour(JAUNE);
TFT480.Set_Text_Size(1);
for (n=gradu_minT; n<=gradu_maxT; n+=20) // 500->50°C
{
Ti = (float)n;
Serial.print("n=");
Serial.println(n);
//lignes horizontales :
y1 = Scope_1.dy/2.0 + (Ti-Tmoy) / echelle_T;
Tiret(1, (uint16_t)y1, dx-1, (uint16_t)y1); // trace la ligne horizontale
nombre = n/10;
y2 = y0+dy-3 -y1;
if (y2>(y0-5)&& // pour ne pas afficher plus haut que le haut du scope
(y2 <= (y0+dy-5)))// pour ne pas afficher trop bas (du cadre du scope et par conséquent du TFT))
{
TFT480.Print_Number_Int(nombre ,x0-15, y2, 3, ' ', 10); // graduation : petits chiffres à gauche
}
}
//lignes VERTicales :
i=0;
for (xi=450; xi>12; xi-=12) // 1 trait de graduation VERTical tous les 12 px
{
// s=0;
// if ( (i%12)==0) {s=10;} //pour un tiret de graduation +grand toutes les 12h
// if ( (i%24)==0) {s=40;}
/*
echelle_T horizontale (temporelle) :
1 mesure(=1px)toutes les 5mn -> 450px*5 = 2250mn ;
2250mn/60 =37.5h
450px / 37.5h = 12px/h
*/
// setCouleurTrace(GRIS_fonce); if ((i%12) == 0) {setCouleurTrace(GRIS_clair);}
// Tiret(xi-1, 20, xi-1, dy-2);
i++;
}
/*
TFT480.Set_Text_Size(1);
TFT480.Set_Text_colour(BLANC);
TFT480.Print_String("-12h", x0+300, y0+dy-10);
TFT480.Print_String("-24h", x0+156, y0+dy-10);
TFT480.Print_String("-36h", x0+12, y0+dy-10);
*/
}
void Scope::Plot(uint16_t x, uint16_t y)
{
TFT480.Set_Draw_color(couleur_trace);
TFT480.Draw_Pixel(x0+x, y0+y);
}
void Scope::Cercle(uint16_t x1, uint16_t y1, uint16_t r1, uint16_t couleur_i)
{
if (x1>(dx-r1)) {return;} // pour ne pas déborder du cadre
TFT480.Set_Draw_color(couleur_i);
TFT480.Draw_Circle(x0+x1, y0+dy-y1, r1);
}
void Scope::Tiret(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
if (x1==0) {x1=1;}
if (x2==0) {x2=1;}
if (x1>=dx-1) {x1=dx-2;}
if (x2>=dx-1) {x2=dx-2;}
TFT480.Set_Draw_color(couleur_trace);
TFT480.Draw_Line(x0+x1, y0+dy-y1, x0+x2, y0+dy-y2);
}
/** ***********************************************************************************
CLASS LED // affiche une petite LED
***************************************************************************************/
// Constructeur
LED::LED()
{
}
void LED::init(uint16_t xi, uint16_t yi, uint16_t dxi, uint16_t dyi)
{
x0 = xi;
y0 = yi;
dx = dxi;
dy = dyi;
TFT480.Set_Draw_color(BLANC);
TFT480.Draw_Rectangle(x0, y0, x0+dx, y0+dy);
TFT480.Set_Draw_color(0, 0, 0);
TFT480.Fill_Rectangle(x0+1, y0+1, x0+dx-1, y0+dy-1);
}
void LED::setCouleur(uint16_t couleur_i)
{
TFT480.Set_Draw_color(couleur_i);
TFT480.Fill_Rectangle(x0+1, y0+1, x0+dx-1, y0+dy-1);
}