/*
ESP32 + ILI9341 LCD Basic Example
https://wokwi.com/projects/325324981270479442
*/
#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
#include "Fonts/FreeSans9pt7b.h"
#include "Fonts/FreeSansBold9pt7b.h"
#include "Fonts/FreeSansBold12pt7b.h"
#include "Fonts/FreeSansBold18pt7b.h"
#define TFT_DC 2
#define TFT_CS 15
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);
#define CHRONOMED_COLOR 0x6E98
#define CONTOUR_COLOR 0x5d94
bool firstTime_progression = true;
void setup() {
tft.begin();
tft.setRotation(1);
testFillScreen();
}
void loop() { delay(10); }
unsigned long testFillScreen()
{
/*ecran(1, 1, 1);
delay(3000);
ecran(2, 1, 1);
delay(3000);
ecran(2, 2, 1);
delay(3000);
ecran(2, 3, 1);
delay(3000);
ecran(3, 1, 1);
delay(3000);
ecran(4, 1, 1);
delay(3000);
ecran(5, 1, 1);
delay(3000);
ecran(5, 2, 1);
delay(3000);
ecran(5, 3, 1);
delay(3000);
ecran(5, 4, 1);
delay(3000);
ecran(5, 5, 1);
delay(3000);
ecran(5, 6, 1);
delay(3000);
ecran(5, 7, 1);
delay(3000);
ecran(5, 8, 1);
delay(3000);
ecran(5, 9, 1);
delay(3000);
ecran(6, 1, 1);
delay(3000);
ecran(7, 1, 1);
delay(3000);
ecran(8, 1, 1);
delay(3000);*/
ecran(9, 1, 1);
delay(3000);
ecran(10, 1, 1);
delay(3000);
ecran(11, 1, 1);
delay(3000);
ecran(12, 1, 1);
delay(3000);
ecran(13, 1, 1);
delay(3000);
ecran(14, 1, 1);
delay(3000);
ecran(15, 1, 1);
delay(3000);
ecran(16, 1, 1);
delay(3000);
ecran(17, 1, 1);
delay(3000);
ecran(18, 1, 1);
delay(3000);
ecran(19, 1, 1);
delay(3000);
ecran(20, 1, 1);
delay(3000);
ecran(21, 1, 1);
delay(3000);
ecran(99, 1, 1);
delay(3000);
ecran(100, 1, 1);
delay(3000);
}
void ecran(int etat_machine, int procedure_ecran, int x)
{
// Conversion du int x en string (x_str)
char buffer[8];
char *intStr = __itoa(x, buffer, 10);
String x_str = String(intStr);
/*
if (nom.length() == 0)
{
// nom_ecran2(get_patient());
}
*/
switch (etat_machine)
{
case 1: //Waiting to get a cassette (1:Distribution)
WriteCenteredText2("En attente,", "Envoyer une commande", 110, 1, 1, 1);
firstTime_progression = true;
break;
case 2: //When functionning (1:Distribution)
if (firstTime_progression)
{
WriteCenteredText(" ", 80, 1, 1, 1);
ecrire_nom();
if (procedure_ecran == 1)
{
// partFillScreen(210, 150, 150, 30);
WriteTextXY("Progression: ", 60, 190, 1);
WriteTextXY(x_str, 210, 190, 1);
WriteTextXY("%", 250, 190, 1);
}
firstTime_progression = false;
}
else // Éviter que l'écran réaffiche le tout et qu'il réaffiche seulement la progression
{
if (procedure_ecran == 1)
{
partFillScreen(210, 170, 170, 30);
WriteTextXY("Progression: ", 60, 190, 1);
WriteTextXY(x_str, 210, 190, 1);
WriteTextXY("%", 260, 190, 1);
}
else if (procedure_ecran == 2)
{
partFillScreen(210, 190, 30, 30);
WriteTextXY("Progression: ", 60, 190, 1);
WriteTextXY("100%", 210, 190, 1);
}
else if (procedure_ecran == 3)
{
// partFillScreen(60, 120, 150, 30);
WriteTextXY("Cassette no. :", 60, 215, 1);
partFillScreen(210, 190, 250, 30);
WriteTextXY(x_str, 210, 215, 1);
//bucketId_memory = x;
//Serial.println(bucketId_memory);
}
}
break;
case 3: //End of cycle (3:Fin du cycle)
WriteCenteredText("Ouvrir le tiroir", 110, 1, 3, 1);
if (x>0) //Toujours à 0?
{
WriteTextXY("Rx manquants:", 60, 180, 1);
WriteTextXY(x_str, 235, 180, 1);
}
firstTime_progression = true;
break;
case 4: //Waiting for drawer to be closed (1:Distribution)
WriteCenteredText2("Tiroir ouvert,", "Refermer le tiroir", 100, 1, 1, 1);
break;
case 5: //While in function (2:Remplissage)
// WriteCenteredText("Changement cassette", 80, 2, ILI9341_WHITE, ILI9341_BLACK, true);
if (procedure_ecran == 1)
{
WriteCenteredText("Ouvrir la porte", 140, 2, 2, 1);
// alternance_LED = 0; // pour la LED de la porte
}
else if (procedure_ecran == 2)
{
WriteCenteredText2("Prendre", "la cassette",110, 2, 2, 1);
}
else if (procedure_ecran == 3)
{
WriteCenteredText("Ouvrir les pinces", 140, 2, 2, 1);
}
else if (procedure_ecran == 4)
{
WriteCenteredText("Fermer les pinces", 140, 2, 2, 1);
}
else if (procedure_ecran == 5)
{
WriteCenteredText2("Charger", "la cassette",110, 2, 2, 1);
}
else if (procedure_ecran == 6)
{
WriteCenteredText2("Chargement", "en cours",110, 2, 2, 1);
}
else if (procedure_ecran == 7)
{
WriteCenteredText("Fermer la porte", 140, 2, 2, 1);
// delay(100);
}
else if (procedure_ecran == 8)
{
WriteCenteredText2("Livraison", "devant la porte", 110, 2, 2, 1);
}
else if (procedure_ecran == 9)
{
WriteCenteredText("Merci", 140, 2, 2, 1);
}
break;
case 6: //End of cycle (4:Annulation)
WriteCenteredText2("Annulation", "du cycle", 110, 2, 4, 1);
firstTime_progression = true;
break;
case 7: //Pas utiliser?
WriteCenteredText3("Verifier l'etiquette", "ou le positionnement", "de la cassette", 110, 1, 99, 1);
break;
case 8: //Pas sur de comprendre? (1:Distribution)
WriteCenteredText("Cycle en attente :", 70, 1, 1, 1);
WriteCenteredText("Appuyez sur le bouton vert", 180, 1, 1, 0);
WriteCenteredText("pour démarrer", 205, 1, 1, 0);
ecrire_nom();
break;
case 9: //Cancellation in process (4.Annulation)
WriteCenteredText("En cours", 130, 2, 4, 1);
firstTime_progression = true;
break;
case 10: //Analysing (99.Erreur)
if (firstTime_progression)
{
if (procedure_ecran == 1)
{
WriteCenteredText("ANALYSE ELECTRIQUE", 120, 1, 99, 1);
}
else
{
WriteCenteredText("ANALYSE MACHINE", 120, 1, 99, 1);
}
WriteCenteredText2("Température haute!", "Refroidissement...", 110, 1, 99, 1);
firstTime_progression = false;
}
// tft.print(char(read_temperature(2)));
break;
case 11: //Different condition of case 4 (3.Fin du cycle)
WriteCenteredText("Complet", 80, 2, 3, 1);
ecrire_nom();
/*if (nom.length() > 2 && prenom.length() > 2)
{
ecrire_nom();
}*/
break;
case 12: //Problem with the cassette (99.Erreur)
WriteCenteredText("BLOCAGE", 90, 1, 99, 1);
WriteCenteredText("DISQUE/CHUTE", 110, 1, 99, 0);
WriteCenteredText3("Ouvrir la porte,", "Vérifier chute,", "brasser cassette", 150, 1, 99, 0);
firstTime_progression = true; // important pour ecran(2,1,0) après blocage critique
break;
case 13: //Problem with the machine (99.Erreur)
WriteCenteredText("Repartir la machine,", 120, 1, 99, 1);
WriteTextXY("Erreur no. cassette:", 30, 150, 1);
WriteTextXY(x_str, 260, 150, 1);
break;
case 14: //Not enough drugs (99.Erreur)
WriteCenteredText("QTE DE MEDICAMENT", 90, 1, 99, 1);
WriteCenteredText("INSUFFISANTE", 110, 1, 99, 0);
WriteCenteredText3("Ouvrir la porte", "Effectuer remplissage", "avec l'application", 150, 1, 99, 0);
firstTime_progression = true; // important pour ecran(2,1,0) après blocage critique
delay(4000);
break;
case 15: //Plate not associated (99.Erreur)
WriteCenteredText2("PLATEAU NON-ASSOCIE", "VERS UNE COMMANDE", 100, 1, 99, 1);
delay(4000);
break;
case 16: //Not used?
WriteCenteredText("", 160, 1, 99, 1);
break;
case 17: //Peggy did not take the cassette correctly (99.Erreur)
WriteCenteredText("CASSETTE MAL PRISE.", 140, 1, 99, 1);
break;
case 18: //Cassette problem (99.Erreur)
WriteCenteredText3("CASSETTE INSTABLE.", "Ouvrir la porte,", "Replacer cassette.", 110, 1, 99, 1);
firstTime_progression = true; // important pour ecran(2,1,0) après blocage critique
break;
case 19: //Scan problem (99.Erreur)
WriteCenteredText3("Mauvais scan cassette,", "Ouvrir la porte,", "Bonne cassette", 110, 1, 99, 1);
firstTime_progression = true; // important pour ecran(2,1,0) après blocage critique
break;
case 20: //Old order, New order (1.Preparation)
WriteCenteredText("COMMANDE", 130, 1, 1, 1);
WriteCenteredText("PLUS RECENTE", 150, 1, 1, 0);
delay(4000);
break;
case 21: //Bringback did not work (99.Erreur)
WriteCenteredText3("ARRET OBLIGATOIRE", "Redemarrage requis", "Sous approbation", 100, 1, 99, 1);
WriteTextXY("Code no.: ", 80, 200, 1);
partFillScreen(210, 180, 250, 30);
WriteTextXY(x_str, 210, 200, 1);
firstTime_progression = true;
break;
case 99: //Logo showing at boot
tft.setFont(&FreeSansBold18pt7b);
tft.fillScreen(ILI9341_WHITE);
tft.setCursor(35, 120);
tft.setTextColor(ILI9341_BLACK);
tft.setTextSize(1);
tft.print("CHRONO");
tft.setTextColor(CHRONOMED_COLOR);
tft.setCursor(192, 120);
tft.print("MED");
break;
case 100: //Setup boot (1.Preparation)
WriteCenteredText("Bonjour!", 120, 1, 3, 1);
break;
default:
break;
}
}
/**
* Write one lines on the screen, centered in the middle. Also shows the cycle and the logo
*
*
* @param textBuf : First line of text.
* @param y : height of the line from the top of the screen.
* @param textSize : Size of the text.
* @param cycle : number representing the cycle in the machine (remplissage, dechargement, etc.)
* @return size: return the horizontal length of the text.
*
* @see related logoShowTopRight() and cycleShowTopLeft()
*/
uint16_t *WriteCenteredText(String textBuf, uint16_t y, uint8_t textSize, uint16_t cycle, bool fill)
{
textBuf = replace_FRchar(textBuf);
int16_t x_offset;
int16_t x1, y1;
static uint16_t size[2] = {0, 0}; // {w, h}
tft.setFont(&FreeSansBold12pt7b);
tft.setTextSize(1);
//Corner information
cycleShowTopLeft(cycle, fill);
//Corner logo
logoShowTopRight();
//Parameters for textBuf
tft.setTextColor(ILI9341_BLACK);
if(textSize == 2){
tft.setFont(&FreeSansBold18pt7b);
}
else{
tft.setFont(&FreeSansBold12pt7b);
}
tft.getTextBounds(textBuf, 0, y, &x1, &y1, &size[0], &size[1]); // calc width of new string
x_offset = 160 - (size[0] / 2);
tft.setCursor(x_offset, y);
tft.print(textBuf);
/*
Serial.print("x_offset : ");Serial.println(x_offset);
Serial.print("x1 : ");Serial.println(x1);
Serial.print("y1 : ");Serial.println(y1);
Serial.print("size[0] (w) : ");Serial.println(size[0]);
Serial.print("size[1] (h) : ");Serial.println(size[1]);
*/
return size;
}
/**
* Replaces odd character in the string
*
* @param text : string that will get verified.
* @return texg: return the cleaned text.
*
*/
String replace_FRchar(String text)
{
String FR_array[11];
FR_array[0] = "é";
FR_array[1] = "É";
FR_array[2] = "è";
FR_array[3] = "È";
FR_array[4] = "ê";
FR_array[5] = "ë";
FR_array[6] = "à";
FR_array[7] = "ç";
FR_array[8] = "ô";
FR_array[9] = "î";
FR_array[10] = "ï";
for (int i = 0; i < 6; i++)
{
text.replace(FR_array[i], "e");
}
for (int i = 9; i < 11; i++)
{
text.replace(FR_array[i], "i");
}
text.replace(FR_array[6], "a");
text.replace(FR_array[7], "c");
text.replace(FR_array[8], "o");
return text;
}
/**
* Write two lines on the screen, centered in the middle and spaced verticaly. Also shows the cycle and the logo
*
*
* @param textBuf1 : First line of text.
* @param textBuf2 : Second line of text.
* @param y : height of the first line from the top of the screen.
* @param textSize : Size of the text.
* @param cycle : number representing the cycle in the machine (remplissage, dechargement, etc.)
* @return size: return the horizontal length of the text.
*
* @see related logoShowTopRight() and cycleShowTopLeft()
*/
uint16_t *WriteCenteredText2(String textBuf1, String textBuf2, uint16_t y, uint8_t textSize, uint16_t cycle, bool fill)
{
textBuf1 = replace_FRchar(textBuf1);
textBuf2 = replace_FRchar(textBuf2);
int16_t x_offset;
int16_t x1, y1;
static uint16_t size[2] = {0, 0}; // {w, h}
tft.setFont(&FreeSansBold12pt7b);
tft.setTextSize(1);
//Corner information
cycleShowTopLeft(cycle, fill);
//Corner logo
logoShowTopRight();
//Parameters for textBuf
tft.setTextColor(ILI9341_BLACK);
if(textSize == 2){
tft.setFont(&FreeSansBold18pt7b);
}
else{
tft.setFont(&FreeSansBold12pt7b);
}
// textBuf1
tft.getTextBounds(textBuf1, 0, y, &x1, &y1, &size[0], &size[1]); // calc width of new string and stores it in size[0]
x_offset = 160 - (size[0] / 2);
tft.setCursor(x_offset, y);
tft.print(textBuf1);
// textBuf2
tft.getTextBounds(textBuf2, 0, y, &x1, &y1, &size[0], &size[1]); // calc width of new string and stores it in size[0]
x_offset = 160 - (size[0] / 2);
tft.setCursor(x_offset, y + 60);
tft.print(textBuf2);
return size;
}
uint16_t *WriteCenteredText3(String textBuf1, String textBuf2, String textBuf3, uint16_t y, uint8_t textSize, uint16_t cycle, bool fill)
{
textBuf1 = replace_FRchar(textBuf1);
textBuf2 = replace_FRchar(textBuf2);
textBuf3 = replace_FRchar(textBuf3);
int16_t x_offset;
int16_t x1, y1;
static uint16_t size[2] = {0, 0}; // {w, h}
tft.setFont(&FreeSansBold12pt7b);
tft.setTextSize(1);
//Corner information
cycleShowTopLeft(cycle, fill);
//Corner logo
logoShowTopRight();
//Parameters for textBuf
tft.setTextColor(ILI9341_BLACK);
if(textSize == 2){
tft.setFont(&FreeSansBold18pt7b);
}
else{
tft.setFont(&FreeSansBold12pt7b);
}
// textBuf1
tft.getTextBounds(textBuf1, 0, y, &x1, &y1, &size[0], &size[1]); // calc width of new string and stores it in size[0]
x_offset = 160 - (size[0] / 2);
tft.setCursor(x_offset, y);
tft.print(textBuf1);
// textBuf2
tft.getTextBounds(textBuf2, 0, y, &x1, &y1, &size[0], &size[1]); // calc width of new string and stores it in size[0]
x_offset = 160 - (size[0] / 2);
tft.setCursor(x_offset, y + 30);
tft.print(textBuf2);
// textBuf3
tft.getTextBounds(textBuf3, 0, y, &x1, &y1, &size[0], &size[1]); // calc width of new string and stores it in size[0]
x_offset = 160 - (size[0] / 2);
tft.setCursor(x_offset, y + 60);
tft.print(textBuf3);
return size;
}
/**
* Prints the CM (ChronoMed) logo in the top left right corner of the screen with the good color
*
*/
void logoShowTopRight(){
tft.setTextColor(ILI9341_BLACK);
tft.setFont(&FreeSansBold12pt7b);
tft.setCursor(274, 28);
tft.setTextSize(1);
tft.print("C");
tft.setTextColor(CHRONOMED_COLOR);
tft.setCursor(291, 28);
tft.print("M");
}
/**
* Checks the string (cycle) and then prints it in the top left corner. Aesthetics (background) included.
*
* @param cycle : number corresponding to the cycle in the machine (1. Dechargement, 2. Remplissage, etc.)
*
*/
void cycleShowTopLeft(uint16_t cycle, bool fill){
String textCycle; //String to be displayed of the screen
switch (cycle)
{
case 1:
textCycle = "Distribution";
break;
case 2:
textCycle = "Remplissage";
break;
case 3:
textCycle = "Fin du cycle";
break;
case 4:
textCycle = "Annulation";
break;
case 99:
textCycle = "Erreur!";
break;
}
tft.setTextColor(0x39c7);
tft.setFont(&FreeSansBold12pt7b);
if(fill == 1){
tft.fillScreen(ILI9341_WHITE);
}
tft.fillRect(0, 0, 230, 40, CHRONOMED_COLOR);
tft.fillTriangle(230, 40, 230, 0, 270, 0, CHRONOMED_COLOR);
tft.fillRect(0, 38, 230, 3, CONTOUR_COLOR);
tft.drawLine(230, 40, 270, 0, CONTOUR_COLOR);
tft.drawLine(230, 39, 269, 0, CONTOUR_COLOR);
tft.drawLine(230, 38, 268, 0, CONTOUR_COLOR);
tft.drawLine(230, 37, 267, 0, CONTOUR_COLOR);
if(cycle==99){ //Error, shows red instead
tft.fillRect(0, 0, 230, 41, ILI9341_RED);
tft.fillTriangle(230, 40, 230, 0, 270, 0, ILI9341_RED);
}
else if(cycle==5){ //Cancellation, shows orange instead
tft.fillRect(0, 0, 230, 41, 0xfcc8);
tft.fillTriangle(230, 40, 230, 0, 270, 0, 0xfcc8);
}
tft.setCursor(10, 26);
tft.print(textCycle);
}
/** Write the textBuf string at the position X and Y
*
*
* @param textBuf : string to write.
* @param x : horizontal position of the text from the left.
* @param y : vertical position of the text from the top.
* @param textSize : Size of the displayed text.
*
*/
bool WriteTextXY(String textBuf, uint16_t x, uint16_t y, uint8_t textSize)
{
textBuf = replace_FRchar(textBuf);
tft.setTextSize(textSize);
tft.setTextColor(ILI9341_BLACK);
tft.setTextSize(textSize);
tft.setFont(&FreeSansBold12pt7b);
tft.setCursor(x, y);
tft.print(textBuf);
return true;
}
void partFillScreen(int16_t rectX, int16_t rectY, int16_t rectW, int16_t rectH)
{
tft.fillRect(rectX, rectY, rectW, rectH, ILI9341_WHITE);
}
void ecrire_nom()
{
WriteTextXY("nom", 100, 115, 1);
WriteTextXY("prenom", 100, 135, 1);
}