11 novembre 2021
Version 1.0 de l’application
/*
Projet: Solution de l'examen 2021.11.10
Référence: http://ve2cuy.com/420-1c4/index.php/ex01-2021-11-10-_9abc/
Auteur: Alain Boudreault, aka VE2CUY
Date: 2021.11.10
Version: 1.0
-------------------------------------------------------------------------
Description: Varier la couleur des pixels en fonction de la température,
d'une température de référence variable
et d'une valeur de palier variable.
NOTE: J'ai utilisé une technique qui n'a pas été présentée
en classe pour l'affichage des traces dans la console.
Au lieu d'utiliser un delai dans la fonction loop() et
de ralentir l'application, j'ai plutôt utilisé un des
'timer' du Atmega328 pour exécuter une fonction d'affichage
des traces à toutes les 3 secondes.
ATTENTION: La vitesse de console série est fixée à 115200
-------------------------------------------------------------------------
Mise à jour:
2021.11.11 - A.B. Ajout d'une fonction TIMER pour le débogage.
2021.11.11 - A.B. Amélioration de la documentation du code.
2021.11.12 - A.B. Gestion du bouton et lecture du potentiomètre.
2021.11.12 - A.B. Affichage des données sur l'écran LCD.
2021.11.12 - A.B. Publication de la version 1.0 de l'application
*/
// *************************************
// Définition des fichiers d'entête
// *************************************
#include <Wire.h> // Inclure la librairie pour I2C (le capteur SHT31 est de type I2C)
#include "SHT31.h" // Inclure la librairie pour le capteur de température et d'humidité SHT31
#include <Adafruit_NeoPixel.h> // Inclure la librairie pour la barre de pixels
#include "rgb_lcd.h" // Inclure la librairie pour le LCD
#include "Streaming.h" // Au besoin, pour les traces sur la console série:
// *************************************
// Définition des MACROS
// *************************************
#define DEBUG
//#define ARDUINO_PROF
#define SERIAL_BAUD_RATE 115200
#define PIN_BOUTON 2 // GPIO de connexion du bouton
#define PIN_NEOPIXEL 4 // GPIO de connexion de la barre de pixels
#ifdef ARDUINO_PROF
#define PIN_POT A2 // GPIO de connexion du potentiomètre pour l'Arduino du prof
#else
#define PIN_POT A3 // GPIO de connexion du potentiomètre pour l'examen
#endif
#define NUMPIXELS 10 // NB de pixels sur la barre
#define PIXEL_ROUGE 200,22,33 // La combinaison RGB de la couleur rouge
#define PIXEL_BLEU 25,50,150 // La combinaison RGB de la couleur bleue
#define TEMP_REF_DEPART 22 // Détermine la valeur de départ de la température de référence
#define TEMP_REF_MINIMUM 19 // Détermine la valeur minimum de la température de référence
#define TEMP_REF_MAXIMUM 33 // Détermine la valeur maximum de la température de référence
#define PALIER_MINIMUM 1 // Détermine la valeur minimum * 10, des paliers de la température
#define PALIER_MAXIMUM 10 // Détermine la valeur maximum * 10, des paliers de la température
#define NB_CARACTERES_ECRAN_LCD 16
#define NB_LIGNES_ECRAN_LCD 2
// Ce qui suit sert au débogage du programme
#ifdef DEBUG
#include <avr/io.h>
#include <avr/interrupt.h>
#define TIMER_TICK_PAR_SECONDE 15624
#define DELAI_TIMER TIMER_TICK_PAR_SECONDE * 3
#endif
// *************************************
// Définition des variables globales
// *************************************
unsigned int valeurDuPotentiometre = 0;
int temperatureDeReference = TEMP_REF_DEPART;
float temperatureDePalier = 0.5; // Fixer la valeur de départ du palier au centre du MIN/MAX
int nbPixelsRouges = 0; // Le nom de la variable dit tout ;-)
float temperature = 0.0; // Sert à stocker la température du capteur
unsigned long int accumulateurClicBouton = 0; // Cumule les utilisations du bouton poussoir
// Servira à déterminer si le pot ajuste la tempDeref ou le palier
char lcdLigne1[NB_CARACTERES_ECRAN_LCD + 1]; // Servira à construire la chaine à afficher sur la ligne 1 du LCD
char lcdLigne2[NB_CARACTERES_ECRAN_LCD + 1]; // +1 -> la fonction sprintf ajoute un 0 à la fin de la chaine
SHT31 capteurTemperature; // Création d'un objet pour le contrôle du capteur de température/humidité
rgb_lcd ecran; // Création d'un objet pour le contrôle de l'écran LCD
Adafruit_NeoPixel pixels(NUMPIXELS, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800); // Création d'un objet pour le contrôle de la barre de pixels.
/* --------------------------- Début du code ----------------------------- */
// ******************************************************
// Initialisation des objets et des paramètres de départ.
// ******************************************************
void setup() {
Serial.begin(SERIAL_BAUD_RATE); // Pour le débogage
pinMode(PIN_BOUTON, INPUT); // Programmer la broche de connexion du bouton en mode lecture
capteurTemperature.begin(); // Me semble que c'est clair ;)
ecran.begin(NB_CARACTERES_ECRAN_LCD,
NB_LIGNES_ECRAN_LCD); // Initialiser l'écran LCD
#ifdef DEBUG
init_timer(); // Initialise un Timer pour l'exécution automatique d'une fonction à toutes les DELAI_TIMER
enteteDepart(); // Afficher les informations de départ
#endif
pixels.begin(); // Me semble que c'est clair ;)
pixels.clear(); // Effacer tous les PIXELS
pixels.setBrightness(50); // Pour ne pas faire sauter le port USB sur mon précieux MACBOOK PRO
for(int i=0; i<NUMPIXELS; i++) { // Au départ, allumer tous les pixels en bleu, parce que c'est beau ;)-
pixels.setPixelColor(i,
pixels.Color(PIXEL_BLEU));
} // for
pixels.show(); // Actualiser l'affichage sur le module NOEPIXEL sinon, NO SHOW!
} // setup
// ******************************************************
// Fonction principale de l'application
// ******************************************************
void loop() {
uint32_t couleur; // Servira à renseigner la couleur du pixel [r|b]
temperature = capteurTemperature.getTemperature(); // Lire la température actuelle
nbPixelsRouges = determinerLeNombreDePixelsRouges(
temperature,
temperatureDeReference,
temperatureDePalier);
// ***************************************************************************
// Renseigner la couleur des pixels en fonction des paliers de la température
// ***************************************************************************
for (int pixelCourant = NUMPIXELS; pixelCourant >= 0; pixelCourant--) {
// Oui je sais, un peu complexe mais pas trop ...
// Déterminer s'il faut renseigner un pixel rouge ou un pixel bleu en vérifiant si nous avons épuisé tous les pixels bleus.
couleur = pixelCourant < (NUMPIXELS - nbPixelsRouges) ? pixels.Color(PIXEL_BLEU) : pixels.Color(PIXEL_ROUGE);
pixels.setPixelColor(pixelCourant, couleur); // Wow, rouge ou bleu, c'est selon ....
} // for pixelCourant
pixels.show(); // Actualiser l'affichage
// ********************************************************************
// Traiter la lecture du potentiomètre en fonction de l'état du bouton
// ********************************************************************
valeurDuPotentiometre = analogRead(PIN_POT);
if ( accumulateurClicBouton % 2 ) { // Ajustement de la température de référence
temperatureDeReference = map(valeurDuPotentiometre, 1023, 0, TEMP_REF_MINIMUM, TEMP_REF_MAXIMUM);
} else { // Ajustement de la valeur du palier
temperatureDePalier = map(valeurDuPotentiometre, 1023, 0, PALIER_MINIMUM, PALIER_MAXIMUM) / 10.0;
} // if accumulateurClicBouton % 2
// ******************
// Gestion du bouton
// ******************
if (digitalRead(PIN_BOUTON)) {
accumulateurClicBouton++; // Le bouton fût appuyé alors, incrémenter son accumulateur
while (digitalRead(PIN_BOUTON)); // Attendre que le bouton soit relaché
}
// *************************************
// Afficher les valeurs sur l'écran LCD
// *************************************
int intTemperature = round(temperature); // Convertir la valeur de la température en valeur entière
int intPalier = temperatureDePalier * 10; // Convertir la valeur de palier en valeur entière entre 1 et 10
// Note: J'ai ajouté la car '*' après la valeur de palier ou de référence en fonction de l'état du potentiomètre.
sprintf(lcdLigne1, "R:%02d%c P:%02d%c T:%02d",
temperatureDeReference,
accumulateurClicBouton % 2 ? '*' : ' ', // Afficher un '*' après la valeur de référence si le pot est en mode 'tempRéférence'
intPalier,
accumulateurClicBouton % 2 ? ' ' : '*', // Afficher un '*' après la valeur de palier si le pot est en mode 'palier'
intTemperature);
sprintf(lcdLigne2, "BL : %02d RG : %02d",NUMPIXELS - nbPixelsRouges, nbPixelsRouges);
ecran.setCursor(0,0); ecran.print(lcdLigne1);
ecran.setCursor(0,1); ecran.print(lcdLigne2);
} // loop
/* *********************************************************
Cette fonction programme une intérruption matérielle sur
le minuteur (timer) numéro 2 de la puce Atmega328.
Cela permet d'éxécuter une fonction automatiquement à
chaque temps imparti du minuteur.
Ici, j'ai fixé le minuteur à 3 sec.
Au bout du délai, la fonction ISR(TIMER1_COMPA_vect) sera
exécutée puis le minuteur sera redemarré à nouveau.
C'est technique permet de ne pas avoir à insérer des
délais inutiles dans notre boucle principale.
*********************************************************** */
void init_timer() {
// INITIALIZE TIMER INTERRUPTS
cli(); // disable global interrupts
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
OCR1A = DELAI_TIMER; // set compare match register to desired timer count. 16 MHz with 1024 prescaler = 15624 counts/s
TCCR1B |= (1 << WGM12); // turn on CTC mode. clear timer on compare match
TCCR1B |= (1 << CS10); // Set CS10 and CS12 bits for 1024 prescaler
TCCR1B |= (1 << CS12);
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
sei(); // enable global interrupts
} // init_timer()
// ********************************************************************
// Vecteur du TIMER, cette fonction sera exécutée à chaque DELAI_TIMER.
// ********************************************************************
ISR(TIMER1_COMPA_vect)
{
static unsigned long int nbTrace = 1;
Serial << "\n\n================================================================================\n";
Serial << "Trace numero ----> " << ++nbTrace << endl;
Serial << "Le potentiomètre renseigne " << ( accumulateurClicBouton % 2 ? "la temperature de reference" : "la valeur du palier")
<< ", valeur du POT: " << valeurDuPotentiometre << endl;
Serial << "La temperature est " << ((temperature < temperatureDeReference) ? "inferieure": "superieure") << " a la temperature de reference" << endl;
Serial << "Temperature: " << temperature << ", reference: " << temperatureDeReference << ", tempPaliers: " << temperatureDePalier << endl;
Serial << "Delta de temperature: " << abs(temperature - temperatureDeReference) << endl;
Serial << "Calcul du nombre de paliers: \n";
Serial << " (temperature - temperatureDeReference) / temperatureDePalier = " << abs(temperature - temperatureDeReference) / temperatureDePalier << endl;
Serial << "NB pixels rouges = " << (nbPixelsRouges) << endl;
Serial << "--------------------------------------------------------------------------------\n";
Serial << "Trace du contenu LCD:\n";
Serial << " Ligne 1: '" << lcdLigne1 <<"'\n";
Serial << " Ligne 2: '" << lcdLigne2 <<"'\n";
Serial << "================================================================================\n";
} // ISR(TIMER1_COMPA_vect)
// ************************************************************************
// Affiche dans la console de débogage le texte de départ de l'application
// ************************************************************************
void enteteDepart() {
Serial << "Solution de l'examen d'objets connectés version 2021.11.12\n";
Serial << "Application par Alain Boudreault\nVersion 1.0\n";
Serial << "----------------------------------------------------------\n";
Serial << "Temperature de depart: " << capteurTemperature.getTemperature() << endl;
Serial << "----------------------------------------------------------\n\n";
} // enteteDepart()
// *******************************************************************************************************
// Calculer le nombre de paliers de température supérieure à la température de base.
// Le nombre de paliers détermine le nombre de pixels rouges à allumer.
// Note: Élimination des valeurs négatives avec abs() et arrondir le résultat de la division avec round()
// Si la température est > temperatureDeReference alors calculer les paliers sinon, retourner 0.
// *******************************************************************************************************
int determinerLeNombreDePixelsRouges(float _temperature, int _temperatureDeReference, float _temperatureDePalier) {
int nbPixelsRed = _temperature > _temperatureDeReference ? round(abs(_temperatureDeReference - _temperature) / _temperatureDePalier) : 0;
// Des fois que nbPixelsRouges > NUMPIXELS
nbPixelsRed = nbPixelsRed > NUMPIXELS ? NUMPIXELS : nbPixelsRed;
return nbPixelsRed;
} // determinerLeNombreDePixelsRouges
// ############################ FIN DU FICHIER ###############################