/*
Station_meteo2
pour ESP32 Wroom + afficheur 3.5" TFT 480x320
par Silicium628
Ceci est un client WiFi qui se connecte à tour de rôle à deux serveurs distincts hébergé sur deux autres ESP32
- un serveur de temps réel (lui-même sy,chronisé par GPS)
- un serveur Girouette
*/
/*=====================================================================================================
CONCERNANT L'AFFICHAGE TFT: connexion:
(Pensez à configurer le fichier User_Setup.h de la bibliothèque ~/Arduino/libraries/TFT_eSPI/ )
les lignes qui suivent ne sont qu'un commentaire pour vous indiquer la config à utiliser
placées ici, elle ne sont pas fonctionnelles
Il FAUT modifier le fichier User_Setup.h installé par le système Arduino dans ~/Arduino/libraries/TFT_eSPI/
// ESP32 pins used for the parallel interface TFT
#define TFT_CS 27
#define TFT_DC 14
#define TFT_RST 26
#define TFT_WR 12
#define TFT_RD 13
#define TFT_D0 16
#define TFT_D1 4
#define TFT_D2 2
#define TFT_D3 22
#define TFT_D4 21
#define TFT_D5 15
#define TFT_D6 25
#define TFT_D7 17
=====================================================================================================*/
String version="2.0";
// commenter la ligne suivante si le module Girouette (avec serveur WiFi) est présent et actif
// #define NO_girouette
#include <stdint.h>
#include "Station_meteo2.h"
#include "Couleurs_AEC.h"
#include <TFT_eSPI.h> // Hardware-specific library
#include "Free_Fonts.h"
#include "FS.h"
#include "SD.h"
//#include "time.h"
TFT_eSPI TFT480 = TFT_eSPI(); // Configurer le fichier User_Setup.h de la bibliothèque TFT480_eSPI au préalable
#include <WiFi.h>
#include <HTTPClient.h>
const char* ssid1 = "TPGPS_34";
const char* password1 = "94r6tkJ31";
const char* ssid2 = "WIND_srv";
const char* password2 = "62z4exW58";
//IP address with URL path
const char* srvName_HR = "http://192.168.4.1/HR";
const char* srvName_ANGLE = "http://192.168.4.1/AGL"; //.4.2 ?
#define NOIR 0x0000
#define MARRON 0x9240
#define ROUGE 0xF800
#define ROSE 0xFBDD
#define ORANGE 0xFBC0
#define JAUNE 0xFFE0
#define JAUNE_PALE 0xF7F4
#define VERT 0x07E0
#define VERT_FONCE 0x02E2
#define OLIVE 0x05A3
#define CYAN 0x07FF
#define BLEU_CLAIR 0x455F
#define AZUR 0x1BF9
#define BLEU 0x001F
#define MAGENTA 0xF81F
#define VIOLET1 0x781A
#define VIOLET2 0xECBE
#define GRIS_TRES_CLAIR 0xDEFB
#define GRIS_CLAIR 0xA534
#define GRIS 0x8410
#define GRIS_FONCE 0x5ACB
#define GRIS_TRES_FONCE 0x2124
#define BLANC 0xFFFF
uint8_t WiFi_status=0;
uint8_t mode = 0; // 0 ou 1
String recp_HR = "{}";
String recp_ANGLE = "{}";
int angle=8;
int angl_1 =0;
float vent_du=0;
float vent_vers=180;
uint32_t bmp_offset = 0;
uint32_t bmp_width;
uint32_t bmp_heigh;
uint8_t annee; // attention: =23 pour 2023
uint8_t mois;
uint8_t jour;
uint8_t annee_in=0;
uint8_t mois_in=0;
uint8_t jour_in=0;
uint8_t heures=0;
uint8_t memo_heures;
uint8_t minutes=0;
uint8_t memo_minutes=0;
uint8_t secondes=0;
uint8_t jour_de_la_semaine;
uint8_t heures_in=0; // GPS reçue par WiFi
uint8_t minutes_in=0; // GPS reçue par WiFi
uint8_t secondes_in=0; // GPS reçue par WiFi
float age_Lune;
String annee_txt;
String mois_txt;
String jour_txt;
String date_txt;
String memo_date_txt="---";
uint8_t SDcardOk=0;
Etiquette Etiq_1;
Etiquette Etiq_2;
Etiquette Etiq_3;
//Etiquette Etiq_4;
LED Led2;
LED Led3;
Scope Scope_1;
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;
uint16_t aff_y;
int16_t gradu_minT, gradu_maxT;
int16_t gradu_minP, gradu_maxP;
uint8_t num_acquisition=4;
uint8_t acqui_valide =1;
uint8_t scope_dat_ok =0;
int16_t T_EXT_lue[10]={0,0,0,0,0,0,0,0,0,0}; //temperature extérieure (la sonde envoie des salves de plusieurs messages identiques)
uint8_t confiance[10]; //indice de confiance = nb d'occurences identiques pour chaque valeur reçue
uint8_t nb_de_salves=6;
int16_t T_EXT_retenue=0; // après comparaison des 5 valeurs reçues
int16_t Tmin, Tmax;
int16_t Tmoy=0; // pour 1 enregistrement, moyenne entre le jour et la nuit
int32_t moyenne_T_sur_toutes; // température mohenne sur l'ensemble des enregistrments affichés
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
uint16_t Pression_lue=1013;
uint16_t Pmin, Pmax=1013;
uint16_t Pmoy=1013;
uint8_t Humid_lue=0; // humidite
uint8_t memo_Humid_lue=0;
uint8_t GPIO_SDA = 33;
uint8_t GPIO_SCL = 32;
const int GPIO_SIGNAL = 34; // (34 = point P5 sur la carte universelle; ATTENTION ne PAS câbler la 10K de pullup externe ici)
bool SIGNAL_etat;
bool memo_SIGNAL_etat;
const int GPIO_bouton1 = 36; // attention: le GPIO 36 n'a pas de R de pullup interne, il faut en câbler une (10k) au +3V3
bool bouton1_etat;
bool memo_bouton1_etat;
uint32_t memo_micros1 = 0;
uint32_t memo_micros2 = 0;
uint32_t temps_ecoule;
uint16_t compteur1s =0;
uint32_t pulseWidth;
uint16_t i_buff;
int alerte1=0;
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;
uint8_t todo_wr_ecran_on_sd=0;
uint16_t couleur_txt = BLANC;
uint16_t couleur_fond = GRIS_TRES_FONCE;
uint16_t couleur_fond_txt = NOIR;
const int MAX_BUFFER = 120;
String message;
char buffer[MAX_BUFFER];
/**
Rappels :
Scope_1.dx = 450
lst_records[500]
**/
static void smartdelay(unsigned long ms)
{
unsigned long start = millis();
while (millis() - start < ms) {;}
}
void init_SDcard()
{
uint16_t y=10;
TFT480.setTextColor(VERT, NOIR);
TFT480.drawString("Init SDcard", 10, y);
y+=20;
if(!SD.begin())
{
TFT480.drawString("Card Mount Failed", 0, y);
delay (1000);
TFT480.fillRect(10, 10, 480, 320, NOIR); // efface
return;
}
uint8_t cardType = SD.cardType();
if(cardType == CARD_NONE)
{
TFT480.drawString("No SDcard", 10, y);
delay (1000);
TFT480.fillRect(10, 10, 480, 320, NOIR); // efface
return;
}
SDcardOk=1;
TFT480.drawString("SDcard Type: ", 10, y);
if(cardType == CARD_SD) {TFT480.drawString("SDSC", 150, y);}
else if(cardType == CARD_SDHC) {TFT480.drawString("SDHC", 150, y);}
y+=20;
uint32_t cardSize = SD.cardSize() / (1024 * 1024);
String s1=(String)cardSize + " GB";
TFT480.drawString("SDcard size: ", 10, y);
TFT480.drawString(s1, 150, y);
//Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
//Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));
delay (1000);
TFT480.fillRect(0, 0, 480, 320, NOIR); // efface
}
void connexion_serveur_HR()
{
int compte1=0;
WiFi.persistent(false);
WiFi.begin(ssid1, password1);
delay(100);
while((WiFi.status() != WL_CONNECTED) && compte1 < 20)
{
delay(100);
compte1++;
//TFT480.print(".");
}
}
void connexion_serveur_ANGLE()
{
int compte1=0;
WiFi.persistent(false);
WiFi.begin(ssid2, password2);
delay(100);
while((WiFi.status() != WL_CONNECTED) && compte1 < 20)
{
delay(100);
compte1++;
//TFT480.print(".");
}
if (compte1 >=20)
{
//TFT480.setFreeFont(FF5);
//TFT480.setTextSize(1);
//TFT480.setTextColor(NOIR, BLANC);
//TFT480.drawString("Serveur Girouette HS", 150, 185);
alerte1 =1;
}
else if (alerte1 ==1)
{
//TFT480.fillRect(150, 185, 220, 20, NOIR);
//trace_sur_Scope();
alerte1 =0;
}
}
void httpGetAngle()
{
// Serial.println("envoi req HR");
TFT480.fillCircle(470, 20, 5,BLEU );
delay(200);
HTTPClient http2;
http2.begin(srvName_ANGLE);
int httpResponseCode = http2.GET();
if (httpResponseCode>0)
{
recp_ANGLE = http2.getString();
TFT480.fillCircle(470, 20, 5,VERT );
if(recp_ANGLE.substring(0, 6)=="angle=")
{
String s2=recp_ANGLE.substring(6, 8);
angl_1 = s2.toInt();
}
vent_du = 22.5*angl_1;// La direction depuis laquelle souffle le vent, celle de la tête du "coq"" est celle reçue par WiFi
// C'est celle utilisée dans le langage courant ("vent du Nord..." ) et en aéronautique (vent au 359, dix noeuds...)
// mais sur les cartes météo, les petites flèches (vecteurs) pointent dans la direction dans laquelle souffle le vent
// c'est cette dernière que nous affichons ici, par une flèche bleue.
vent_vers = 180 + vent_du;// direction dans laquelle souffle le vent (c.a.d direction de la QUEUE du coq !!)
if (vent_vers > 360) {vent_vers -= 360;}
}
http2.end();
}
void httpGetHeureDate()
{
//Serial.println("httpGetHeureDate()");
//TFT480.fillCircle(465, 122, 5,BLEU );
Led3.setCouleur(BLEU);
delay(200);
HTTPClient http2;
http2.begin(srvName_HR);
int httpResponseCode = http2.GET();
if (httpResponseCode>0)
{
recp_HR = http2.getString();
}
http2.end();
//TFT480.fillCircle(465, 122, 5,VERT );
Led3.setCouleur(VERT);
}
void ajuste_HR()
{
//Serial.println("ajuste_HR()");
if(recp_HR.length() == 18)
{
WiFi_status =1;
heures_in =(recp_HR.substring(0,2)).toInt();
minutes_in =(recp_HR.substring(3,5)).toInt();
secondes_in =(recp_HR.substring(6,8)).toInt();
//secondes_in++; // pour compenser le temps de traitement
if (heures != heures_in) {heures = heures_in;}
if (minutes != minutes_in) {minutes = minutes_in;}
if (secondes != secondes_in) {secondes = secondes_in;}
jour_in =(recp_HR.substring(9,11)).toInt();
mois_in =(recp_HR.substring(12,14)).toInt();
annee_in =(recp_HR.substring(15,17)).toInt();
if (jour != jour_in) {jour = jour_in;}
if (mois != mois_in) {mois = mois_in;}
if (annee != annee_in) {annee = annee_in;}
}
else {WiFi_status=0;}
}
void calcul_jour_de_la_semaine()
{
// d'après l'Algorithme de Mike Keith
uint16_t d, m, y, z, jds;
d=jour;
m=mois;
y=annee;
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;
}
void affiche_date()
{
//Serial.println("affiche_date()");
date_txt="";
calcul_jour_de_la_semaine();
switch (jour_de_la_semaine)
{
case 0: { date_txt+="Dim ";} break;
case 1: { date_txt+="Lun ";} break;
case 2: { date_txt+="Mar ";} break;
case 3: { date_txt+="Mer ";} break;
case 4: { date_txt+="Jeu ";} break;;
case 5: { date_txt+="Ven ";} break;
case 6: { date_txt+="Sam ";} break;
}
date_txt += String(conv_time(jour))+" ";
switch (mois)
{
case 1: {date_txt+="Janv "; } break;
case 2: {date_txt+="Fev "; } break;
case 3: {date_txt+="Mars "; } break;
case 4: {date_txt+="Avr "; } break;
case 5: {date_txt+="Mai "; } break;
case 6: {date_txt+="Juin "; } break;
case 7: {date_txt+="Juil "; } break;
case 8: {date_txt+="Aout "; } break;
case 9: {date_txt+="Sept "; } break;
case 10: {date_txt+="Oct "; } break;
case 11: {date_txt+="Nov "; } break;
case 12: {date_txt+="Dec "; } break;
}
if (annee_in >0) // pour éviter d'afficher une date fantaisiste au départ
{
uint16_t annee_in2 = annee_in + 2000;
annee_txt = (String)annee_in2;
date_txt += annee_txt;
//memo_date_txt = date_txt;
TFT480.setTextColor(BLANC, NOIR);
TFT480.setFreeFont(FF6);
TFT480.setTextSize(1);
TFT480.drawString(date_txt,200,118);
}
}
void affiche_heure()
{
//Serial.println("affiche_heure()");
String s1;
if (memo_minutes != minutes)
{
memo_minutes = minutes;
TFT480.fillRect(200, 85, 105, 26, BLEU); // efface le fond du rectangle dans lequel s'affiche l'heure
s1="";
if(heures<10){s1+="0";}
s1 += String(heures);
s1 += ":";
if(minutes<10){s1+="0";}
s1 += String(minutes);
//TFT480.setTextColor(couleur_txt, couleur_fond_txt);
TFT480.setFreeFont(FF7);
TFT480.setTextSize(1);
TFT480.setTextColor(BLANC);
TFT480.drawString(s1, 200, 85); // Affiche les chiffres de l'heure par ex: 12:34
affiche_date();
}
TFT480.setTextColor(GRIS, NOIR);
TFT480.setFreeFont(FF7);
TFT480.setTextSize(1);
s1="";
if(secondes<10){s1="0";}
s1 += String(secondes);
TFT480.fillRect(320, 85, 43, 30, NOIR); // efface
TFT480.drawString(s1, 320, 85); // Affiche les secondes
if(WiFi_status == 1) { Led3.setCouleur(VERT);} else { Led3.setCouleur(ROUGE);}
if (secondes == 0){ tt_les_1mn(); }
if (((minutes %5) == 0) && (secondes == 10)) { tt_les_5mn(); }
if (((minutes %30) == 0) && (secondes == 20)) { tt_les_30mn(); }
}
void affi_rayon2(uint16_t x0, uint16_t y0, float r1, float r2, float angle_i, uint16_t couleur_i, bool gras)
{
// trace une portion de rayon de cercle entre les distances r1 et r2 du centre
// angle_i en degrés décimaux - sens trigo
float angle =angle_i/57.3; // (57.3 ~ 180/pi)
int16_t x1, x2;
int16_t y1, y2;
x1=x0+int16_t(r1* cos(angle));
y1=y0-int16_t(r1* sin(angle));
x2=x0+int16_t(r2* cos(angle));
y2=y0-int16_t(r2* sin(angle));
if ((x1>0) && (x2>0) && (y1>0) && (y2>0) && (x1<480) && (x2<480) && (y1<320) && (y2<320) )
{
TFT480.drawLine(x1, y1, x2, y2, couleur_i);
if (gras)
{
TFT480.drawLine(x1, y1-1, x2, y2-1, couleur_i);
TFT480.drawLine(x1, y1+1, x2, y2+1, couleur_i);
}
}
//TFT480.fillCircle(x2, y2, 2, ROUGE);
}
void affi_pointe(uint16_t x0, uint16_t y0, uint16_t r, double angle_i, float taille, uint16_t couleur_i)
{
// trace une pointe de flèche sur un cercle de rayon r
// angle_i en degrés décimaux - sens trigo
float angle =angle_i/57.3; // (57.3 ~ 180/pi)
int16_t x1, x2, x3;
int16_t y1, y2, y3;
x1=x0+r* cos(angle); // pointe
y1=y0-r* sin(angle); // pointe
x2=x0+(r-7)* cos(angle-taille); // base A
y2=y0-(r-7)* sin(angle-taille); // base A
x3=x0+(r-7)* cos(angle+taille); // base B
y3=y0-(r-7)* sin(angle+taille); // base B
TFT480.fillTriangle(x1, y1, x2, y2, x3, y3, couleur_i);
}
void affi_dir_vent(int x0, int y0, int dx, int dy, float angle_i) // en chiffres
{
int a1;
TFT480.setFreeFont(FF0);
TFT480.setTextSize(1);
TFT480.setTextColor(BLANC);
a1=(int)angle_i;
String s1 =String(a1);
TFT480.fillRect(x0+60, y0+5, 25, 8, NOIR); // efface
TFT480.drawString(s1, x0+65, y0+5);
}
void affi_boussole(int x0, int y0, int dx, int dy, float angle_i) // angle_i définit la position de la pointe de la flèche
{
TFT480.drawRect(x0, y0, dx, dy, BLANC);
TFT480.fillRect(x0+1, y0+1, dx-2, dy-2, NOIR); // efface
TFT480.setFreeFont(FF0);
TFT480.setTextSize(1);
TFT480.drawString("N", x0+35, y0+5);
TFT480.setTextColor(BLANC);
TFT480.setFreeFont(FF5);
TFT480.setTextSize(1);
TFT480.fillRect(x0+15, y0+1, 50, 53, NOIR); // efface
//affi_pointe(x0+dx/2, y0+dy/2, (dy/2)-5, 90-angle_i, 2.5, CYAN);
affi_rayon2(x0+dx/2, y0+dy/2, 0, 25, 90-angle_i, JAUNE, 0); // tige de la flèche
affi_pointe(x0+dx/2, y0+dy/2, 25, 90-angle_i, 0.3, JAUNE); // pointe triangulaire en bout de flèche
TFT480.drawCircle(x0+dx/2, y0+dy/2, (dy/2)-2, BLANC);
TFT480.setTextColor(ROUGE);
TFT480.drawString("N", -6+x0+dx/2, y0+5);
TFT480.setTextColor(JAUNE);
TFT480.drawString("W", x0+6, -8+y0+dy/2);
TFT480.setTextColor(JAUNE);
TFT480.drawString("E", x0+dx-14, -8+y0+dy/2);
TFT480.setTextColor(BLEU);
TFT480.drawString("S", -6+x0+dx/2, y0+dy-20);
TFT480.setFreeFont(FF0);
TFT480.setTextSize(1);
TFT480.setTextColor(BLANC);
TFT480.setTextColor(BLANC, NOIR);
TFT480.drawString("VENT", x0, y0-3);
}
void draw_AEC(uint16_t x0, uint16_t y0, uint16_t L, uint8_t sens)
{
// ligne arc-en-ciel
// affiche une ligne de pixels colorés à partir de la variable 'couleurs_aec' mémorisée en PROGMEM (voir fichier Couleurs_AEC.h)
// L = longueur de la ligne
//Serial.println("draw_draw_AEC()");
uint16_t x, i, j;
uint16_t y1;
uint16_t couleur_i;
for (int16_t i=0; i<L; i++)
{
float f = 470.0/L * i; // pour balayer toute l'échelle des couleurs disponibles
j=uint16_t(f);
couleur_i = couleurs_aec[2*j] | couleurs_aec[2*j+1]<<8;
if (sens==0){x=i;} else {x=L-i;}
TFT480.drawPixel(x0+x, y0, couleur_i);
}
}
void draw_bmp(uint16_t x0, uint16_t y0, uint8_t inv_x, File* fp)
{
//Serial.println("draw_bmp()");
uint16_t i, j, j2, k, p, m=0;
uint16_t y1;
uint8_t bmp_data[2*3]={0};
uint16_t bmp_color[2];
uint8_t rot =1;
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]= Color_To_565(bmp_data[m+2], bmp_data[m+1], bmp_data[m+0]);
m+=3;
}
for(p=0; p<2; p++)
{
if (rot==0)
{
y1=y0;
TFT480.drawPixel(x0+i, y0+j*2+p, bmp_color[p]);
}
if (rot==1)
{
if (inv_x == 0) {j2=j;} else { j2=(bmp_width/2)-j; }
y1=y0;
TFT480.drawPixel(x0+j2*2+p,320-(y1+i), bmp_color[p]);
}
}
}
}
}
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
// ici on a lu 16 octets
bmp_width = read_32(fp); //(4 bytes) get width and heigh information
bmp_heigh = read_32(fp); //(4 bytes)
// ici on a lu 24 octets
//if(read_16(fp)!= 1) {return false;}
read_16(fp);
//if(read_32(fp)!= 0) {return false;}
return true;
}
void affi_img(uint16_t x0, uint16_t y0, uint8_t inv_x, const char* filename1)
{
//Serial.println("affi_img()");
File bmp_file;
bmp_file = SD.open(filename1);
if(!bmp_file)
{
//Serial.println("didnt find bmp");
TFT480.setFreeFont(FF1);
TFT480.setTextSize(1);
TFT480.drawString("didnt find bmp",0,10);
delay(2000);
return;
}
if(!teste_bmp_header(bmp_file))
{
//Serial.println("bad bmp !");
TFT480.setFreeFont(FF1);
TFT480.setTextSize(1);
TFT480.drawString("bad bmp !",0,0);
delay(2000);
return;
}
draw_bmp(x0, y0, inv_x, &bmp_file);
bmp_file.close();
// delay(1000);
}
void affi_graduation_temporelle()
{
//Serial.println("affi_graduation_temporelle()");
/*
RAPPEL:
void Scope::init(uint16_t xi, uint16_t yi, uint16_t dxi, uint16_t dyi)
Scope_1.init(18,145, n_record_max,160); // 1px toutes les 5mn; 12px = 1h
uint16_t n_record_max = 450
donc:
Scope => (18, 145, 450, 160)
*/
int16_t h1, mn1;
int16_t j;
int16_t x0, x1;
TFT480.fillRect(Scope_1.x0, Scope_1.y0-18, Scope_1.dx, 17, couleur_fond); // // efface
j=3; // on va tracer 3 jours = 36h = 432x5mn (correspondant à 432 px)
h1=heures;
mn1=minutes;
x0=18+450-12; // px le plus à droite (on va tracer de droite à gauche en remontant dans le passé)
x1=x0;
while (j>0)
{
mn1--;
if (mn1<0)
{
mn1=0;
h1--;
if (h1<0)
{
mn1=59;
h1=23;
j--;
}
}
Scope_1.setCouleurTrace(GRIS_FONCE);
if ((h1 == 6) || (h1==18)) {Scope_1.setCouleurTrace(GRIS);}
if (h1 == 0) {Scope_1.setCouleurTrace(GRIS_CLAIR);}
if (h1 == 12) {Scope_1.setCouleurTrace(ROUGE);}
if (mn1==0) // tracé de lignes temporelles verticales toutes les heures 'pile'
{
x1-=12;
// mode normal
if (x1 > 30) // pour ne pas abimer l'affichage de la graduation de la pression
{
Scope_1.Tiret(x1, 5, x1, Scope_1.dy-2);
}
if (h1 == 0)
{
Scope_1.Tiret(x1+1, 5, x1+1, Scope_1.dy-2); // pour épaissir
for (int n=2; n<10; n++)
{
draw_AEC(x1+8, Scope_1.y0+n, 142, 0); // lignes aux couleurs de l'arc-en-ciel
}
affi_img(x1+8, Scope_1.y0-20, 0, "/bmp/lune3b.bmp");
TFT480.setFreeFont(FF5);
TFT480.setTextColor(BLANC, BLEU);
TFT480.drawString("0", x1+14, Scope_1.y0-18);
}
if (h1 == 6)
{
TFT480.setFreeFont(FF5);
TFT480.setTextColor(BLANC, BLEU);
TFT480.drawString("6", x1+12, Scope_1.y0-18);
}
affiche_degrade(0,0);
if (h1 == 12)
{
Scope_1.Tiret(x1+1, 5, x1+1, Scope_1.dy-2); // pour épaissir
for (int n=2; n<10; n++)
{
draw_AEC(x1+8, Scope_1.y0+n, 142, 1); // lignes aux couleurs de l'arc-en-ciel
}
TFT480.setFreeFont(FF5);
TFT480.setTextColor(BLANC, BLEU);
TFT480.drawString("12", x1+8, Scope_1.y0-18);
affi_img(x1+7, Scope_1.y0-20, 0, "/bmp/soleil.bmp"); //icone soleil:
}
if (h1 == 18)
{
TFT480.setFreeFont(FF5);
TFT480.setTextColor(BLANC, BLEU);
TFT480.drawString("18", x1+7, Scope_1.y0-18);
}
}
}
TFT480.setFreeFont(FF0);
TFT480.setTextColor(BLANC, BLEU);
TFT480.drawString("NOW", Scope_1.x0+Scope_1.dx-10, Scope_1.y0-18);
uint16_t xx1 =Scope_1.x0+Scope_1.dx-6;
uint16_t yy1 =Scope_1.y0-10;
uint16_t xx2 =Scope_1.x0+Scope_1.dx-2;
uint16_t yy2 =Scope_1.y0-2;
uint16_t xx3 =Scope_1.x0+Scope_1.dx+2;
uint16_t yy3 =Scope_1.y0-10;
TFT480.fillTriangle(xx1, yy1, xx2, yy2, xx3, yy3, BLANC);
TFT480.fillRect(468, Scope_1.y0-2, 12, 20, couleur_fond);
}
void init_SCOPE() // vide
{
//Serial.println("init_SCOPE()");
Scope_1.init(18,160, n_record_max,160); // 1px toutes les 5mn; 12px = 1h
Scope_1.setCouleurCadre(BLANC);
Scope_1.traceCadre();
affi_graduation_temporelle();
}
uint8_t decToBcd( int val )
{
return (uint8_t) ((val / 10 * 16) + (val % 10));
}
uint16_t Color_To_565(uint8_t r, uint8_t g, uint8_t b)
{
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3);
}
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 ;
}
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);
}
void setup()
{
pinMode(GPIO_SIGNAL, INPUT);
SIGNAL_etat = digitalRead(GPIO_SIGNAL);
memo_SIGNAL_etat = SIGNAL_etat;
pinMode(GPIO_bouton1, INPUT);
bouton1_etat = digitalRead(GPIO_bouton1);
memo_bouton1_etat = bouton1_etat;
Wire.begin(GPIO_SDA, GPIO_SCL, 100000); // OK (source: https://randomnerdtutorials.com/esp32-i2c-communication-arduino-ide/ )
// en conséquece câbler le capteur BMP280 en i2C sur les GPIO 32 et GPIO 33 de l'ESP32 (à la place de 21, 22 par défaut)
Serial.begin(19200);
TFT480.init();
TFT480.setRotation(1); // 0..3 à voir, suivant le modèle de l'afficheur et sa disposition
TFT480.fillScreen(NOIR);
TFT480.setTextColor(BLANC, NOIR);
TFT480.drawRect(0, 0, 480, 320, BLANC); // ok
delay(300);
init_SDcard();
TFT480.setFreeFont(FF1);
uint16_t y=5;
y+=20;
String s1="version " + version;
TFT480.drawString(s1, 10, y);
y+=20;
TFT480.setTextColor(BLEU, NOIR);
TFT480.drawString("Heure GPS", 10, y);
y+=20;
TFT480.setTextColor(JAUNE, NOIR);
TFT480.drawString("Client WiFi", 10, y);
y+=40;
//affi_img(0, 0, 0, "/bmp/capture1.bmp");
//affi_img(0, 50, 1, "/bmp/capture1.bmp");
//affi_img(0, 110, 1, "/bmp/ligne_aec1.bmp");
draw_AEC(0, 110, 470, 0);
draw_AEC(100, 130, 250, 1);
TFT480.setFreeFont(FF1);
/* **************************************************************************** */
//Serial.println("ici3");
TFT480.fillScreen(couleur_fond);
/*
TFT480.setTextColor(CYAN, couleur_fond);
TFT480.setFreeFont(FF5);
TFT480.setTextSize(1);
TFT480.drawString("GPS Time",0,0);
*/
init_BMP280();
aff_y=0;
num_acquisition=6;
for(int i=0; i<=5; i++) {T_EXT_lue[i]=0;}
T_EXT_retenue=0;
Pression_lue=1013; //hPa
Humid_lue = 50; // 50%
affiche_meteo();
affi_pannel();
/* ***************************** A DECOMMENTER **********************
*/
affiche_heure(); //et initialise les variables : annee, mois, jour, heures, minutes
affiche_date(); // attention : executer la ligne précedente au moins 1 fois au préalable pour initialiser les variables
/* ****************************************************************** */
affi_cadres();
affiche_LEDS();
init_SCOPE();
// --------------------------------------------
//charge_enregistrements(); // pour afficher rapidement (sans attendre les acquisitions)
if (0) // =1 à la 1ere utilisation
{
TFT480.setTextColor(JAUNE, NOIR); TFT480.setFreeFont(FF5);
String s1="init_lst_records()"; TFT480.drawString(s1, 50, 200);
init_lst_records();
serial_out_datas();
//trace_sur_Scope();
write_scope_on_SDcard();
}
// --------------------------------------------
read_scope_on_SDcard();
trace_sur_Scope();
affiche_Lune();
Led2.setCouleur(ROUGE);
WiFi.disconnect();
connexion_serveur_HR();
recp_HR = "{}";
if(WiFi.status()== WL_CONNECTED )
{
httpGetHeureDate();
ajuste_HR();
}
affiche_heure();
// animation de la boussole, pour test
int x0=100; int y0=80; int dx=86; int dy =55;
for(int v1=0; v1<360; v1++)
{
affi_boussole(x0, y0, dx, dy, v1);
delay(10);
}
}
void Draw_arc_elliptique(uint16_t x0, uint16_t y0, int16_t dx, int16_t dy, float alpha1, float alpha2, uint16_t couleur_i) // 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.drawPixel(x,y, couleur_i);
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(x0-24, 258, 0, "/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.fillRect(x0-r, y0-r, 2*r, 2*r, NOIR); // efface l'image
TFT480.drawCircle(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)
{
TFT480.drawCircle(x0, y0, r, GRIS);
}
else
{
TFT480.drawCircle(x0, y0, r, GRIS_FONCE);
Fill_croissant_elliptique(x0, y0, T1, T2, r, -M_PI/2, M_PI/2, NOIR);
}
}
void affiche_Lune()
{
//Serial.println("affiche_Lune()");
//Serial.print("annee="); //Serial.println(annee);
//Serial.print("mois="); //Serial.println(mois);
//Serial.print("jour="); //Serial.println(jour);
age_Lune = GetPhase(2000+annee, mois, jour);
// Affichage de 'l'age' de la Lune [0..29 j]
//TFT480.Set_Text_Size(1);
uint16_t x0=432;
uint16_t y0=15;
TFT480.fillRect(x0, y0+1, 32, 50, NOIR); // efface
TFT480.setFreeFont(FF0);
TFT480.setTextSize(1);
TFT480.setTextColor(GRIS_CLAIR, NOIR);
TFT480.drawString("AGE:", x0, y0) ;
TFT480.setTextColor(BLANC, NOIR);
String s1=String((uint8_t)age_Lune); TFT480.drawString(s1, x0, y0+10);
TFT480.setTextColor(BLANC);
TFT480.setFreeFont(FF1);
switch (((int8_t)age_Lune))
{
case 0: { TFT480.drawString("NL", x0+4, y0+27); } break;
case 8: { TFT480.drawString("PQ", x0+4, y0+27); } break;
case 15: { TFT480.drawString("PL", x0+4, y0+27); } break;
case 22: {TFT480.drawString("DQ", x0+4, y0+27); } break;
default: { TFT480.drawString("--", x0+4, y0+27); } break;
}
Dessine_Lune2(407, 38, age_Lune);
}
void incremente_heure(uint8_t nb_s)
{
for (uint8_t n=0; n<nb_s; n++)
{
if (secondes < 59) {secondes+=1;}
else
{
secondes=0;
if (minutes < 59) {minutes+=1;}
else
{
minutes=0;
if (heures < 23) {heures+=1;}
else
heures=0;
}
}
}
}
void tt_les_30mn()
{
//Serial.println("----------------------------") ;
//Serial.print("tt_les_30mn() ");
//Serial.print(heures); //Serial.print(":"); //Serial.println(minutes);
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 tt_les_5mn()
{
uint16_t x2;
uint16_t y2;
//Serial.println("----------------------------") ;
//Serial.print("tt_les_5mn() ");
//Serial.print(heures); //Serial.print(":"); //Serial.println(minutes);
//Led4.setCouleur(BLANC);
record_enr_actuel();
decale_enregistrements();
trace_sur_Scope();
affiche_date();
/*
TFT480.Set_Text_Back_colour(NOIR);
TFT480.setTextColor(BLANC);
TFT480.Set_Text_Size(1);
x2 = 300;
y2 = 70;
TFT480.drawString("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.drawString("mn", x2+70, y2);
Led4.setCouleur(NOIR);
*/
}
void tt_les_1mn()
{
//Serial.println("----------------------------") ;
//Serial.print("tt_les_1mn() ") ;
//Serial.print(heures); //Serial.print(":"); //Serial.println(minutes);
//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);
#ifndef NO_girouette // voir la direcive au tout début du programme
if(mode == 0)
{
WiFi.disconnect();
connexion_serveur_HR();
recp_HR = "{}";
if(WiFi.status()== WL_CONNECTED )
{
httpGetHeureDate();
ajuste_HR();
}
mode=1;
}
else
{
WiFi.disconnect();
connexion_serveur_ANGLE();
recp_ANGLE = "{}";
if(WiFi.status()== WL_CONNECTED )
{
httpGetAngle();
}
mode=0;
int x0=100; int y0=80; int dx=86; int dy =55;
if (alerte1 ==0)
{
affi_boussole(x0, y0, dx, dy, vent_vers);
// un "vent du Nord" affichera une flèche dirigée dans le sens du vent, donc vers le SUD comme sur les cartes météo
affi_dir_vent(x0, y0, dx, dy, vent_du); //en chiffres, un "vent du Nord" écrira "0", pas "180"
}
else
{
// trace une croix rouge dans le rectangle si pas de signal reçu
TFT480.drawRect(x0, y0, dx, dy, BLANC);
TFT480.drawLine(x0, y0, x0+dx, y0+dy, ROUGE);
TFT480.drawLine(x0+1, y0, x0+dx+1, y0+dy, ROUGE); // pour épaissir le trait
TFT480.drawLine(x0+dx, y0, x0, y0+dy, ROUGE);
TFT480.drawLine(x0+dx+1, y0, x0+1, y0+dy, ROUGE);
}
}
#endif
#ifdef NO_girouette
WiFi.disconnect();
connexion_serveur_HR();
recp_HR = "{}";
if(WiFi.status()== WL_CONNECTED )
{
httpGetHeureDate();
ajuste_HR();
}
#endif
num_acquisition=0;
calcul_confiance();
T_EXT_retenue = meilleure_valeur();
nb_acqui433=0; // RAZ
calcul_Tmin_Tmax();
//fixe_Tmin_tmax();
calcul_Gradu_ech_T();
calcul_Gradu_ech_P();
affiche_meteo(); // en haut, 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();}
//write_TFT_on_SDcard(); // POUR TEST
//trace_sur_Scope(); // POUR TEST
//print_meteo_on_SDcard(); // POUR TEST
}
void toutes_les_20s()
{
}
void toutes_les_5s()
{
if (todo_wr_ecran_on_sd==1) {write_TFT_on_SDcard();}
// POURT TEST
//record_enr_actuel();
//decale_enregistrements();
//trace_sur_Scope();
// FIN TEST
}
void toutes_les_1s()
{
compteur1s++;
if((compteur1s%5)==0)
{
toutes_les_5s();
}
if(compteur1s>20)
{
compteur1s=0;
toutes_les_20s();
}
incremente_heure(1); // +1s
affiche_heure();
}
void loop()
{
temps_ecoule = micros() - memo_micros1;
if (temps_ecoule >= 1E6) // (>=) et pas strictement égal (==) parce qu'alors on rate l'instant en question
{
memo_micros1 = micros();
toutes_les_1s();
}
memo_SIGNAL_etat = SIGNAL_etat;
SIGNAL_etat = digitalRead(GPIO_SIGNAL);
if (SIGNAL_etat != memo_SIGNAL_etat) //hors timing local (les signaux horaires sont émis par la sonde sans requete préalable)
{
analyse_signal_primaire();
}
memo_bouton1_etat = bouton1_etat;
bouton1_etat = digitalRead(GPIO_bouton1);
if (bouton1_etat != memo_bouton1_etat) //hors timing local (les signaux horaires sont émis par la sonde sans requete préalable)
{
if (bouton1_etat == 0)
{
todo_wr_ecran_on_sd =1;
}
}
}
void affiche_meteo() //(en nombres dans la partie haute de l'afficheur)
{
//Serial.println("affiche_meteo()");
/**
// 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.setTextColor(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.setTextColor(GRIS);
TFT480.drawString("ok->", 410,57);
TFT480.setTextColor(VERT);
y0+=12; TFT480.Print_Number_Int(meilleure_valeur(),x0,y0,3,' ',10);
**/
float T_out_f = T_EXT_retenue / 10.0; // float
Etiq_1.affiche_float(T_out_f, 4, 1, " C", Tmin/10, Tmax/10);
TFT480.drawCircle(Etiq_1.x0+132, Etiq_1.y0+Etiq_1.dy-20, 3, Etiq_1.couleur_txt); // affiche le signe "°"
Etiq_3.affiche_int(Pression_lue, 4, "hPa", Pmin, Pmax);
// if (stable==1) {Led3.setCouleur(VERT);} else {Led3.setCouleur(ORANGE);}
Etiq_2.affiche_int(Humid_lue, 2, "", 0, 0);
//TFT480.Set_Text_Size(2);
TFT480.drawString("%", Etiq_2.x0+70,Etiq_2.y0+35);
//Serial.println("fin affiche_meteo()");
}
void affi_pannel() // init vide
{
//Serial.println("affi_pannel()");
uint16_t hauteur = 0;
hauteur += 6;
Etiq_1.setCouleurTxt(JAUNE);
Etiq_1.setCouleurCadre(BLEU_CLAIR);
Etiq_1.setCouleurNom(BLANC);
Etiq_1.init(5, hauteur, 180, 60, "TEMPERATURE");
Etiq_1.affiche_string ("---");
Etiq_3.setCouleurTxt(VERT);
Etiq_3.setCouleurCadre(VERT);
Etiq_3.setCouleurNom(BLANC);
Etiq_3.init(200, hauteur, 170, 60, "PRESSION");
TFT480.setTextColor(Etiq_3.couleur_txt, NOIR);
Etiq_3.affiche_int(0, 4, "hPa",0,0);
hauteur += 70;
Etiq_2.setCouleurTxt(BLEU_CLAIR);
Etiq_2.setCouleurCadre(BLEU_CLAIR);
Etiq_2.setCouleurNom(BLANC);
Etiq_2.init(5, hauteur, 90, 55, "HUMIDITE");
Etiq_2.affiche_string ("---");
//Etiq_4.setCouleurTxt(BLANC);
//Etiq_4.setCouleurCadre(BLANC);
//Etiq_4.setCouleurNom(BLANC);
//Etiq_4.init(100, hauteur, 86, 55, "VENT");
//Etiq_4.affiche_string ("");
}
void affi_cadres()
{
TFT480.drawRect(380, 12, 90, 59, BLANC); // cadre 1 (PHASE LUNE)
TFT480.drawRect(380, 78, 90, 35, GRIS); // cadre 2 (affi nb acquisitions)
TFT480.setFreeFont(FF0);
TFT480.setTextColor(GRIS, NOIR);
String s1="433 MHz";
TFT480.drawString(s1, 425, 82); // dans le cadre 2
s1="GPS (WiFi)";
TFT480.drawString(s1, 400, 97);
}
void affiche_LEDS()
{
//TFT480.Set_Text_Size(1);
//TFT480.setTextColor(BLANC);
Led2.init(385,82, 10,10);
//TFT480.drawString("RX", Led2.x0-10, Led2.y0, BLANC);
Led2.setCouleur(NOIR);
Led3.init(385,97, 10,10);
//TFT480.drawString("RX", Led2.x0-10, Led2.y0, BLANC);
Led3.setCouleur(NOIR);
}
void affiche_degrade(uint16_t x, uint16_t y)
{
// todo : charger une image
}
void calcul_Gradu_ech_T()
{
//Serial.println("calcul_Gradu_ech_T()");
String s1;
//calcul de la gradu min & gradu max
gradu_minT = -10+(Tmin/10)*10;
gradu_maxT = 10+(Tmax/10)*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
}
void calcul_Gradu_ech_P()
{
//Serial.println("calcul_Gradu_ech_P()");
String s1;
//calcul de la gradu min & gradu max
gradu_minP = -2+(Pmin/10)*10;
gradu_maxP = 2+(Pmax/10)*10;
//Serial.print("gradu_minP="); //Serial.println(gradu_minP);
//Serial.print("gradu_maxP="); //Serial.println(gradu_maxP);
echelle_P =(float) (gradu_maxP - gradu_minP)/(Scope_1.dy);
if (echelle_P==0) {echelle_P=1;} // cas où la Pression est totalement constante - évite des /0 plus tard
//Serial.print("echelle_P="); //Serial.println(echelle_P);
}
void calcul_Tmin_Tmax()
{
//Serial.println(" ");
//Serial.println("calcul_Tmin_Tmax()");
uint16_t i;
int16_t T_i, memo_T_i;
Tmin=500;
Tmax=-500;
for (i=0; i< n_record_max-1; i++)
{
memo_T_i = T_i;
T_i=lst_records[i].T;
if (abs(T_i - memo_T_i) < 100) // anti-glitch (si delta T > 10°C en 5mn... -> sans doute un enregistrement corrompu)
{
if ( (T_i != 0) && (T_i > -250) && (T_i < 480)) // -25°C à +48°C
{
if (T_i < Tmin) {Tmin= T_i;}
if (T_i > Tmax) {Tmax= T_i;}
}
}
else {i+=2;}
}
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
}
Tmoy=(Tmin+Tmax)/2;
//Serial.print("Tmin = "); //Serial.println(Tmin);
//Serial.print("Tmax = "); //Serial.println(Tmax);
//Serial.println(" ");
}
void fixe_Tmin_tmax()
{
// pour l'echelle verticale
// en remplacement éventuel de la fonction précédente
Tmin=60; // 6°C
Tmax=220; // 22°C
Tmoy=(Tmin+Tmax)/2;
echelle_T =(float) (gradu_maxT - gradu_minT)/(Scope_1.dy);
}
void fixe_Pmin_Pmax()
{
// pour l'echelle verticale
// en remplacement éventuel de la fonction suivante
Pmin=980;
Pmax=1050;
Pmoy=(Pmin+Pmax)/2;
}
void calcul_Pmin_Pmax()
{
//Serial.println("calcul_Pmin_Pmax()");
uint16_t i, p1;
Pmin=2000;
Pmax=0;
for (i=2; i< n_record_max-1 ; i++) // attention départ=2, pas 0 pour éviter de prendre en compte l'acquisition en cours
{
p1=lst_records[i].P;
if ((p1 !=0) && (p1 != 0xFFFF))
{
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;
/*
//Serial.print("Pmin = "); //Serial.println(Pmin);
//Serial.print("Pmax = "); //Serial.println(Pmax);
//Serial.print("Pmoy = "); //Serial.println(Pmoy);
//Serial.println(" ");
*/
}
void calcul_ech_P()
{
//Serial.println("calcul_ech_P()");
echelle_P =(float) (Pmax -Pmin)/(Scope_1.dy-20);
if (echelle_P==0) {echelle_P=1;}
}
void verifie_data_scope()
{
//Serial.println(" ");
//Serial.println("verifie_data_scope() ");
if(! SDcardOk){return;}
int16_t T,P;
uint8_t H;
uint16_t heure_i;
uint16_t i;
uint8_t h, mn;
for (i=1; i<n_record_max-1; i++)
{
T=lst_records[i].T;
P=lst_records[i].P;
H=lst_records[i].H;
//heure_i=lst_records[i].heure;
//heure2=lst_records[i+1].heure;
h= heure_i / 60;
mn = heure_i % 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 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>2 ; 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-3 ; 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;
}
}
}
}
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 temperatures affichées
uint16_t i;
int32_t somme_T=0;
for (i=0; i< n_record_max-1 ; i++)
{
somme_T += lst_records[i].T;
}
moyenne_T_sur_toutes = somme_T / n_record_max;
//Serial.print(" moyenne_T_sur_toutes = "); //Serial.print(moyenne_T_sur_toutes);
//Serial.print("; c.a.d: "); //Serial.print(moyenne_T_sur_toutes/10.0); //Serial.println("°C");
}
void trace_sur_Scope() // (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();
calcul_Tmin_Tmax();
//fixe_Tmin_tmax(); // pour test
calcul_Gradu_ech_T();
//efface_glitch_P();
//calcul_Pmin_Pmax();
fixe_Pmin_Pmax();
calcul_ech_P();
calcul_Gradu_ech_P();
//if ((acqui_valide ==1) || (scope_dat_ok==1))
{
//Serial.println(" ");
//Serial.println("Affiche courbes");
Scope_1.affi_gradu_horizontale(); // lignes H. efface tout au préalable
affi_graduation_temporelle();
affiche_courbeH();
affiche_courbeP();
affiche_courbeT();
}
}
/*
uint16_t nb_acq_heures()
{
uint16_t v1=0;
uint16_t i;
for (i=0; i<n_record_max-1; 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()");
//TFT480.setTextColor(JAUNE, NOIR); TFT480.setFreeFont(FF5);
//String s1="affiche_courbeT()"; TFT480.drawString(s1, 50, 200);
//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 x0, x1, memo_x1, xt;
uint8_t nb_ech=5; // pour le lissage de la courbe
uint8_t nb1;
float Temperature1, memo_Temperature1; // =316 pour 31.6°C
int16_t y1, memo_y1;
x0=Scope_1.x0 + Scope_1.dx;
/*
1px toutes les 5mn;
12px = 1h
6px = 30mn
x= x=Scope_1.dx LORSQUE heure_i = heures*60 + minutes
RAPPEL du format des enregistrements :
void 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= Humid_lue;
lst_records[n_record_max].heure= heures*60 + minutes;
*/
x1=Scope_1.dx;
Scope_1.setCouleurTrace(JAUNE);
//for (i=0; i<n_record_max; i++)
for (i=n_record_max-1; i>nb_ech+1; i--)
{
memo_x1 =x1;
x1--;
memo_Temperature1 = Temperature1;
// lissage de la courbe (filtre passe bas par moyenne glissante)
Temperature1=0;
nb1=0;
for (j=0; j<nb_ech; j++)
{
k1=i-j;
if ((k1>1) && (k1 < n_record_max-1))
{
if (lst_records[k1].T !=0)
{
Temperature1 += lst_records[k1].T;
nb1++;
}
}
}
if (nb1 != 0)
{
Temperature1 /= nb1;
}
memo_y1=y1;
float yf;
if (echelle_T != 0) {yf = Scope_1.y0/2.0 + (Temperature1 - Tmoy) / echelle_T;}
y1= (int16_t) yf;
Scope_1.Tiret(x1, y1, memo_x1, memo_y1); // tracé de la courbe
Scope_1.Tiret(x1+1, y1, memo_x1+1, memo_y1); // pour épaissir le trait
//Scope_1.Plot(x1, y1); // ponctuel
}
}
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=15; // pour le lissage de la courbe
uint8_t nb1;
float Pression1;
//float Pi1;
int16_t y1, memo_y1;
/*
//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--)
{
Pression1=0;
nb1=0;
// lissage de la courbe (filtre passe bas par moyenne glissante)
for (j=0; j<nb_ech; j++)
{
k1=i-j;
if ((k1>1) && (k1 < n_record_max-1))
{
if (lst_records[k1].P !=0)
{
Pression1 += lst_records[k1].P;
nb1++;
}
}
}
if (nb1 != 0)
{
Pression1 /= nb1;
}
/*
//Serial.print("Pression1=");
//Serial.println(Pression1);
*/
if (Pression1>800)
{
memo_y1 = y1;
y1 = (int16_t) (Scope_1.dy/2.0 + (Pression1-Pmoy) / echelle_P);
}
Scope_1.Tiret(x-1, y1, x, memo_y1);
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--)
{
// 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) && (k1 < n_record_max-1))
{
h1 += lst_records[k1-1].H;
h2 += lst_records[k1].H;
}
}
h1/=nb_ech; h2/=nb_ech;
//h1=80; h2=80; // POUR TEST affichage
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);
}
}
}
/**--------------------------------------------------------
fonctions RECEPTION & DECODAGE SONDE EXTERNE 433MHz
--------------------------------------------------------**/
void serial_out_datas()
{
//Serial.println("serial_out_datas()");
for (int i=0; i<n_record_max; i++)
{
//Serial.print(i);
//Serial.print(" "); //Serial.print(" T°="); //Serial.print(lst_records[i].T);
//Serial.print(" c.a.d: "); //Serial.print(lst_records[i].T / 10.0); //Serial.println("°C");
}
}
void init_lst_records()
{
//Serial.println("init_lst_records()");
uint16_t i;
//uint8_t h;
//uint8_t mn;
//uint8_t mn5;
float Tn,Pn,Hn;
//h= heures;
//mn=5*(minutes/5);
//mn=5*mn5;
for (i=0; i<n_record_max-1; i++)
{
//Serial.print(i); //Serial.print(" "); //Serial.print(h); //Serial.print(":");//Serial.print(mn);
// sinusoïdes :
// température
//Tn = 100.0 + 50.0*sin(i/20.0); // sinusoïde (pour affichage echelle consistante pour TEST) ; format 325 -> 32.5°C
//lst_records[i].T= (int16_t)Tn;
lst_records[i].T = 180; // 18°C
// pression
//Pn = Pression_lue + (i/20)*sin(i/27.0); // sinusoïde (pour affichage echelle consistante pour TEST)
//lst_records[i].P= (int16_t)Pn;
lst_records[i].P = 1013;
// humidité
//Hn = Humid_lue + (i/10)*sin(i/43.0); // sinusoïde (pour affichage echelle consistante pour TEST)
//lst_records[i].H= (uint16_t)Hn;
Hn = 50;
/*
if (mn>=5){mn-=5;}
else
{
mn=55;
if (h>0){h--;} else {h=23;}
}
*/
}
}
void efface_buffer()
{
for(int i=0; i<120; i++)
{
buffer[i]=' ';
}
}
void record_enr_actuel()
{
//Serial.println("record_enr_actuel()");
// cette action est effectuée toutes les 5mn
//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 = Humid_lue;
//lst_records[n_record_max].heure = heures*60 + minutes;
}
void decale_enregistrements()
{
//Serial.println( "decale_enregistrements");
//décalage vers la gauche
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<n_record_max; i++)
{
// decalage
lst_records[i].T=lst_records[i+1].T; // ->remarque : l'enr 'n_record_max' 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;
}
}
void write_scope_on_SDcard() // enregistre en binaire la courbe de T°C affichée sur le scope
{
//Serial.println( "write_scope_on_SDcard()");
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("/data/scope.dat")) {SD.remove("/data/scope.dat");} // efface le fichier précédent s'il existe
File binFile1 = SD.open("/data/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 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 read_scope_on_SDcard()
{
if (SDcardOk==0) {return;}
//Serial.println(" ");
//Serial.println("lecture du fichier /data/scope.dat sur SDcard");
//Serial.println(" ");
//const char* filename1;
//filename1="SCOPE.DAT";
//filename1+='\0';
File file_1;
file_1 = SD.open("/data/scope.dat"); //ouverture du fichier en lecture
if(!file_1)
{
file_1.close();
//Serial.println("file nof found");
return;
}
else
{
//Serial.println("file /data/scope.dat ok");
file_1.read((uint8_t *)&lst_records, sizeof(lst_records));
file_1.close(); // referme le fichier
}
}
void print_meteo_on_SDcard()
{
//Serial.println("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...)
date = String(annee)+"-"+conv_time(mois)+"-"+conv_time(jour);
heure = conv_time(heures)+":"+conv_time(minutes);
Str1 = date +" [ "+ heure + " ]";
Str1 += " ; T(ext)=" + String(T_EXT_kelvin);
Str1 +=" ; H=" +String(Humid_lue)+ " ; P=" + String(Pression_lue);
//Serial.println(" ");
//Serial.println("ecriture data sur SDcard :");
//Serial.print(Str1);
//Serial.println(" ");
File dataFile = SD.open("/data/data.txt", FILE_APPEND); // ouverture du fichier en écriture à la fin du fichier
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/data.txt");} // pour info
delay(100);
// Led1.setCouleur(NOIR);
nb_wr_SD++;
/*
TFT480.Set_Text_Size(1);
TFT480.Set_Text_Back_colour(NOIR);
TFT480.setTextColor(BLANC);
TFT480.Print_Number_Int(nb_wr_SD,440,70,6,' ',10);
*/
}
void analyse_signal_primaire()
{
char lettre_i;
pulseWidth = micros() - memo_micros2; // VOIR la doc micros() -> nb de microsecondes depuis le démarrage de la carte
memo_micros2 = 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) )
{
Led2.setCouleur(VERT);
return;
}
if ( (pulseWidth >= 460) && (pulseWidth < 530) )
{
//Serial.print('A');
lettre_i = 'A';
buffer[i_buff]= lettre_i; //memorisation de la lettre
i_buff++;
if (i_buff>MAX_BUFFER-1) {i_buff=0;}
}
if ( (pulseWidth >= 910) && (pulseWidth < 1100) )
{
Led2.setCouleur(JAUNE);
//Serial.print('B');
lettre_i = 'B';
buffer[i_buff]= lettre_i;
i_buff++;
if (i_buff>MAX_BUFFER-1) {i_buff=0;}
}
if ( (pulseWidth >= 1900) && (pulseWidth < 1970) )
{
//Serial.print('C');
lettre_i = 'C';
buffer[i_buff]= lettre_i;
i_buff++;
if (i_buff>MAX_BUFFER-1) {i_buff=0;}
}
if (pulseWidth >= 2000) // sonne la fin de la séquence...
{
if (i_buff>=72)
{
Led2.setCouleur(ROUGE);
//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 >=6)
{
//Serial.println(" "); //Serial.println("num_acquisition >=6");
num_acquisition=0;
acqui_valide =1;
calcul_confiance();
T_EXT_retenue = meilleure_valeur();
calcul_Tmin_Tmax();
//fixe_Tmin_tmax();
calcul_Gradu_ech_T();
acqui_Pression();
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))
**/
}
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 6 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<nb_de_salves; i++)
{
confiance[i]=0; //RAZ
}
for(i=0; i<nb_de_salves; i++)
{
Ti=T_EXT_lue[i];
for(j=0; j<nb_de_salves; 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<nb_de_salves; 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.drawString(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(heures); //Serial.print(":"); //Serial.println(minutes);
//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 enregistre 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_Humid_lue = Humid_lue;
valeur_lue = strbinToInt(s1,nbBits); //TFT480.Print_Number_Int(T1,220,100,3,' ',10);
valeur_precedente = memo_Humid_lue;
/*
if ((valeur_lue != 0) && (valeur_lue < 100) ) // 100% max !
{
if ( abs(valeur_lue - valeur_precedente) < 20 ) // delta de 20% max
{
Humid_lue = valeur_lue; // on renregistre la nouvelle valeur
}
else // au delà -> anti-glich
{
if ( valeur_lue > valeur_precedente) {Humid_lue = valeur_precedente+5;}
if ( valeur_lue < valeur_precedente) {Humid_lue = valeur_precedente-5;}
}
}
*/
if ((valeur_lue != 0) && (valeur_lue < 100) ) // 100% max !
{
Humid_lue = valeur_lue; // on renregistre la nouvelle valeur
}
}
}
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.drawString("0", 4*k, 10*aff_y);
//Serial.print(0);
}
else if ((car1=='A') &&(car2=='C'))
{
message += "1";
//TFT480.drawString("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.setFreeFont(FF1);
TFT480.setTextColor(BLANC, NOIR);
if (nb_acqui433>2)
{
String s1=String (nb_acqui433);
TFT480.drawString(s1, Led2.x0+15, Led2.y0-1); // dans le cadre 2
}
else {TFT480.fillRect(Led2.x0+14, Led2.y0-1, 25, 14, NOIR);} // efface
//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):
**/
/** -------------------------------------------------------
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(("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_Pression()
{
//Serial.println("acqui_Pression()");
/**
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_pressure->getEvent(&pressure_event);
float pression_acq = pressure_event.pressure;
//Serial.print("pression_acq = ");
//Serial.print(pression_acq);
//Serial.println(" hPa");
if ( isnan (pression_acq) || (pression_acq < 600) || (pression_acq > 1100)) // cas par exemple où le capteur n'est pas connecté...
{
//Serial.println("ERREUR !");
//Serial.println();
Pression_lue = 1013;
return;
}
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();
}
/** ***********************************************************************************
CAPTURE D'ECRAN vers SDcard
***************************************************************************************/
void write_TFT_on_SDcard() // enregistre
{
todo_wr_ecran_on_sd =0;
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.setFreeFont(FF0);
TFT480.setTextSize(1);
TFT480.setTextColor(JAUNE, NOIR);
for (y=320; y>0; y--)
{
for (x=0; x<480; x++)
{
color565=TFT480.readPixel(x, y);
RGB565_to_888(color565, &R, &G, &B);
File1.write(B); //G
File1.write(G); //R
File1.write(R); //B
}
String s1=String(y/10); TFT480.drawString(s1, 450, 118); // affiche compte à rebour
}
File1.close(); // referme le fichier
//TFT480.Fill_Rectangle(50, 300, 65, 310); // efface le compte à rebour
}
}
/** ***************************************************************************************
CLASS Etiquette // 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
Etiquette::Etiquette()
{
}
void Etiquette::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.setTextColor(10, 10, 5);
traceCadre();
efface();
}
void Etiquette::traceCadre()
{
TFT480.drawRect(x0, y0+5, dx, dy, couleur_cadre);
}
void Etiquette::efface()
{
TFT480.fillRect(x0+1, y0+6, dx-2, dy-2, NOIR);
affi_nom();
}
void Etiquette::affi_nom()
{
// sur le coin en haut à gauche
TFT480.setFreeFont(FF0);
TFT480.setTextSize(1);
TFT480.setTextColor(BLANC, NOIR);
TFT480.drawString(nom, x0, y0);
}
void Etiquette::setCouleurTxt(uint16_t couleur_i)
{
couleur_txt = couleur_i;
}
void Etiquette::setCouleurCadre(uint16_t couleur_i)
{
couleur_cadre = couleur_i;
}
void Etiquette::setCouleurFond(uint16_t couleur_i)
{
couleur_fond = couleur_i;
}
void Etiquette::setCouleurNom(uint16_t couleur_i)
{
couleur_nom = couleur_i;
}
void Etiquette::flashFond(uint16_t couleur_i)
{
couleur_fond = couleur_i;
//TFT480.setTextColor(couleur_fond);
TFT480.fillRect(x0, y0, x0+dx, y0+dy, couleur_fond);
delay(10);
//TFT480.setTextColor(NOIR);
TFT480.fillRect(x0, y0, x0+dx, y0+dy, NOIR);
traceCadre();
affi_nom();
}
void Etiquette::affiche_int(uint32_t valeur, uint8_t nb_chiffres, char txt_unite_i[3], uint32_t v_min, uint32_t v_max)
{
String s1;
// '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.setFreeFont(FF7);
TFT480.setTextSize(1);
TFT480.setTextColor(couleur_txt, NOIR);
z=0; if ((valeur<1000)&&(valeur>100)) {z=20;}
s1=String(valeur); TFT480.drawString(s1, x0+20+z, y0+25);
TFT480.setFreeFont(FF2);
TFT480.setTextColor(couleur_txt, NOIR);
TFT480.drawString(txt_unite, x0+120, y0+35); // ex : mm, kHz, etc...
if((v_min !=0 )&&(v_max !=0 ))
{
// affiche valeurs min et max en haut à droite
TFT480.setFreeFont(FF0);
z=0; if (v_max<1000) {z=5;}
s1=String(v_max); TFT480.drawString(s1, x0+140+z,y0+9,3);
z=0; if (v_min<1000) {z=5;}
s1=String(v_min); TFT480.drawString(s1, x0+140+z, y0+17,3);
}
}
void Etiquette::affiche_float(float valeur, uint8_t nb_chiffres, uint8_t nb_dec, char txt_unite_i[3], float v_min, float v_max)
{
String s1;
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.Print_Number_Float(valeur, nb_dec, x0+20, y0+18, '.', nb_chiffres, ' ');
TFT480.setFreeFont(FF7);
TFT480.setTextSize(1);
TFT480.setTextColor(couleur_txt, NOIR);
s1=String(valeur, 1); TFT480.drawString(s1, x0+20, y0+25);
TFT480.setFreeFont(FF2);
TFT480.setTextColor(couleur_txt, NOIR);
TFT480.drawString(txt_unite, x0+110,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.setTextColor(GRIS_CLAIR);
//TFT480.Set_Text_Size(1);
TFT480.setFreeFont(FF0);
TFT480.setTextColor(BLANC, NOIR);
z=0; if (v_max<10) {z=6;}
//TFT480.Print_Number_Float(v_max, 1, x0+140+z, y0+9, '.', 3, ' ');
s1=String (v_max, 3); TFT480.drawString(s1, x0+140+z, y0+9);
z=0; if (v_min<10) {z=6;}
//TFT480.Print_Number_Float(v_min, 1, x0+140+z, y0+17, '.', 3, ' ');
s1=String (v_min, 3); TFT480.drawString(s1, x0+140+z, y0+17);
}
}
void Etiquette::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.setTextColor(CYAN);
// TFT480.setFont(SmallFont);
TFT480.drawString(tbl, x0+10,y0+15);
}
void Etiquette::affiche_texte(char txt_i[10])
{
for (int i=0; i<10; i++) {texte[i]=txt_i[i];}
texte[10]='\0'; // zero terminal
TFT480.setFreeFont(FF7);
TFT480.setTextSize(1);
TFT480.setTextColor(couleur_txt, NOIR);
TFT480.drawString(texte, x0+5,y0+18);
}
void Etiquette::affiche_string(String txt_i)
{
TFT480.setFreeFont(FF7);
TFT480.setTextSize(1);
TFT480.setTextColor(couleur_txt, NOIR);
TFT480.drawString(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.setTextColor(couleur_cadre);
//TFT480.drawRect(x0, y0, dx, dy, couleur_cadre);
traceCadre();
}
void Scope::efface_surface()
{
//TFT480.setTextColor(NOIR);
TFT480.fillRect(x0+1, y0+1, dx-2, dy-2, NOIR);
// Rappel : Scope_1.init(18,145, 450,172);
// TFT480.fillRect(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.drawRect(x0, y0, dx, dy, couleur_cadre);
TFT480.fillRect(x0+1, y0+1,dx-2, dy-2, NOIR);
}
void Scope::efface_libel_gradu()
{
//TFT480.setTextColor(NOIR);
TFT480.fillRect(x0-20, y0+1, x0-2, dy-2, NOIR);
}
void Scope::affi_gradu_horizontale()
{
int16_t n, nombre;
float y1, Ti, Pi;
int16_t y2;
uint16_t xi;
uint16_t i;
efface_surface();
efface_libel_gradu();
setCouleurTrace(GRIS_FONCE);
//TFT480.Set_Text_Back_colour(NOIR);
//TFT480.setTextColor(JAUNE);
for (n=gradu_minT; n<=gradu_maxT; n+=20) // 500->50°C
{
Ti = (float)n;
//Serial.print("n=");
//Serial.println(n);
//lignes horizontales Temperature:
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-20)&& // 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))
{
String s1=String (int16_t(nombre));
TFT480.setFreeFont(FF0);
TFT480.setTextColor(JAUNE, NOIR);
TFT480.drawString(s1, x0-15, y2); // graduation : petits chiffres à gauche
}
}
//Graduation Pression:
int pas1 = (gradu_maxP - gradu_minP) / 5;
if (pas1 <= 2) {pas1 =2;} // en effet si pas1=0 la boucle suivante devient infinie...
// ce qui provoquait un bug lorsque la pression restait constante...
// et dire qu'il y a des volontaires pour s'embarquer pour la planète Mars !!!
for (n=gradu_minP; n<=gradu_maxP+5; n+=pas1)
{
Pi = (float)n;
y1 = dy/2.0 + (Pi-Pmoy) / echelle_P;
y2 = y0+dy-3 -y1;
//if (y2>(y0)&& // 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))
{
String s1=String (int16_t(n));
TFT480.setFreeFont(FF0);
TFT480.setTextColor(VERT);
TFT480.drawString(s1, x0+8, y2); // graduation : petits chiffres à gauche
}
}
//Graduation Humidité:
for (n=0; n<10; n+=2)
{
y1 = -12 + y0 + dy - 15*n;
String s1=String (10*n);
TFT480.setFreeFont(FF0);
TFT480.setTextColor(BLEU);
TFT480.drawString(s1, x0+40, y1); // graduation : petits chiffres à gauche
}
}
void Scope::Plot(uint16_t x, uint16_t y)
{
TFT480.drawPixel(x0+x, y0+y, couleur_trace);
}
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.drawCircle(x0+x1, y0+dy-y1, r1, couleur_i);
}
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;}
if (y1>y0-5) {y1=y0-5;}
if (y2>y0-5) {y2=y0-5;}
if (y1>(y0+dy)) {y1=y0+dy;}
if (y2>(y0+dy)) {y2=y0+dy;}
TFT480.drawLine(x0+x1, y0+dy-y1, x0+x2, y0+dy-y2, couleur_trace);
}
/** ***********************************************************************************
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.drawRect(x0, y0, dx, dy, BLANC);
TFT480.fillRect(x0+1, y0+1, dx-2, dy-2, NOIR);
}
void LED::setCouleur(uint16_t couleur_i)
{
TFT480.fillRect(x0+1, y0+1, dx-2, dy-2, couleur_i);
}