Ok but it's so big
Main program
/* Rotary Encoder
Based on Oleg Mazurov's code for rotary encoder interrupt service routines for AVR micros
here https://chome.nerpa.tech/mcu/reading-rotary-encoder-on-arduino/
and using interrupts https://chome.nerpa.tech/mcu/rotary-encoder-interrupt-service-routine-for-avr-micros/
*/
#include <Wire.h>
#include <SPI.h>
#include <TFT_eSPI.h>
#include <Preferences.h>
#include <nvs_flash.h>
Preferences prefs;
// define rotary encoder pins
#define SWITCH 4
#define ENC_A 17
#define ENC_B 16
#define ENC_BT 26
// define anti bump time
#define T_ANTI_BUMP 100
//
#define IN_PRESS !digitalRead(SWITCH)
#define NO_PRESS digitalRead(SWITCH)
#define COD_OUT_LONG 1
#define COD_IN_LONG 2
#define COD_IN 3
#define COD_OUT 4
#define PRESS_CONFIG 700 // ms of pressure to enter the configuration
#define RW_MODE false // for Preference library
#define RO_MODE true // for Preference library
// Variables declaration
long ms_press, _new, old;
unsigned char val_press;
volatile int counter = 0;
unsigned int menuSel, sub_menuSel, min_val, max_val, used_val, stato, phaseSel, pDuration, Temperature, Humidity, Ventilation, vEvery, Sterilization;
unsigned int SP_days, ST_temp, STC_temp, STD_temp, SH_hum, SHC_hum, SHD_hum, SFT_fan, SFE_fan, SUT_uv, SUE_uv;
bool FIRST = true;
// variable definition for Name Space definitions for Preferences library
String phaseID;
String ssid;
String password;
bool existNS; // check the precence of Name Space
// | 0 1 3 | 4 5 6 | 6 7 8 | 9 10 11 | 12 13 14 | 15 16 17 |
// | Val | Val | Val | Val | Val | Val|
// | days In | C In | % In | h In | h In | days In |
// | phDur Use | Temp Use | Humid Use | Vent Use | vEve Use | UV Use| // L , H , U
// | L H U | L H U | L H U | L H U | L H U | L H U | // Min-Val, Max-Val, Used-Val
int16_t defPhase1[] = { 1, 3, 2, 10, 30, 25, 40, 80, 70, 10, 60, 50, 1, 5, 3, 3, 24, 10 }; // Name Space=> defPhase1 STEWING phase_1 following default values
int16_t defPhase2[] = { 4, 15, 5, 5, 15, 10, 30, 80, 65, 10, 60, 50, 1, 5, 3, 3, 24, 10 }; // Name Space=> defPhase2 DRYING phase_2 following default values
int16_t defPhase3[] = {10, 365, 60, 5, 15, 8, 30, 80, 65, 10, 60, 50, 1, 5, 3, 3, 24, 10 }; // Name Space=> defPhase3 SEASONING phase_3 following default values
int16_t usedPhase[18]; // Name Space=> usedPhase in use charged with used phase
TFT_eSPI tft = TFT_eSPI();
// include external routines
#include "M_data.h" // Record and stup stored data
#include "M_encoder.h" // Managing rotary_encoder
#include "D_menu.h" // Menu and display management
//************************************* START SETUP ***********************
//
void setup() {
// Start the serial monitor to show output
Serial.begin(115200); // Change to 9600 for Nano, 115200 for ESP32
delay(500); // Wait for serial to start
Serial.println("Start");
M_init(); // Init all Name Space with default values
M_reading(); // Reading default values
tft.init();
tft.fillScreen(TFT_BLACK);
tft.setRotation(1); //Landscape
pinMode(ENC_A, INPUT_PULLUP);
pinMode(ENC_B, INPUT_PULLUP);
pinMode(SWITCH, INPUT_PULLUP);
}
//************************************* START LOOP ************************
//
void loop() {
val_press = check_press();
if (val_press == COD_IN) {
counter = 0;
MainMenu();
}
anti_bump();
}
Menu management
/*
DISPLAY MANAGEMENT
Phases 1,2,3
Phase duration in days
Temperature in Centigrade Degrees
Humidity in %
Air exchange ventilation in minutes
Sterilization in minutes
*/
//************************************* PHASE MENU ************************
//
void M_phase() { // Menu 0
static int lastCounter = 0;
min_val = 1;
max_val = 3;
used_val = phaseSel;
Serial.println("phaseSel");
Serial.println("counter = "+String(counter));
Serial.println("min_val = "+String(min_val));
Serial.println("max_val = "+String(max_val));
Serial.println("used_val = "+String(used_val));
counter = used_val;
do {
if (counter != lastCounter) {
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_YELLOW);
tft.drawString("Phase 1-2-3", 55, 5, 4);
tft.setTextColor(TFT_GREEN);
tft.drawString(String(counter), 115, 50, 6);
switch (phaseSel) {
case 1:
tft.setTextColor(TFT_YELLOW);
tft.drawString("STEWING", 70, 110, 4);
break;
case 2:
tft.setTextColor(TFT_YELLOW);
tft.drawString("DRYING", 80, 110, 4);
break;
case 3:
tft.setTextColor(TFT_YELLOW);
tft.drawString("SEASONING", 55, 110, 4);
break;
}
}
lastCounter = counter;
read_encoder();
val_press = check_press();
if (val_press == COD_IN) {
anti_bump();
if (used_val != phaseSel){
phaseID = String(phaseSel);
}
stato = 2;
}
} while (stato != 2);
phaseSel = counter;
Serial.println("Exit phaseSel = "+String(phaseSel));
}
//************************************* PHASE DURATION ********************
// in Days
void M_pDuration() { // Menu 1
static int lastCounter = 0;
min_val = usedPhase[0];
max_val = usedPhase[1];
used_val = usedPhase[2];
Serial.println("phaseSel");
Serial.println("counter = "+String(counter));
Serial.println("min_val = "+String(min_val));
Serial.println("max_val = "+String(max_val));
Serial.println("used_val = "+String(used_val));
counter = used_val;
do {
if (counter != lastCounter) {
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_YELLOW);
tft.drawString("Phase Duration", 35, 5, 4);
tft.setTextColor(TFT_GREEN);
tft.drawString(String(counter), 115, 50, 6);
tft.setTextColor(TFT_YELLOW);
tft.drawString("in Days", 90, 110, 4);
Serial.println(counter);
}
lastCounter = counter;
read_encoder();
val_press = check_press();
if (val_press == COD_IN) {
anti_bump();
if (used_val != lastCounter){
usedPhase[2] = lastCounter;
}
stato = 2;
}
} while (stato != 2);
}
//************************************* TEMPERATURE ********************
// in Degrees C
void M_Temperature() { // Menu 2
static int lastCounter = 0;
min_val = usedPhase[3];
max_val = usedPhase[4];
used_val = usedPhase[5];
Serial.println("Temperature");
Serial.println("phaseSel = "+String(phaseSel));
Serial.println("counter = "+String(counter));
Serial.println("min_val = "+String(min_val));
Serial.println("max_val = "+String(max_val));
Serial.println("used_val = "+String(used_val));
counter = used_val;
do {
if (counter != lastCounter) {
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_YELLOW);
tft.drawString("Temperature", 55, 5, 4);
tft.setTextColor(TFT_GREEN);
tft.drawString(String(counter), 115, 50, 6);
tft.setTextColor(TFT_YELLOW);
tft.drawString("Degrees C", 70, 110, 4);
}
lastCounter = counter;
read_encoder();
val_press = check_press();
if (val_press == COD_IN) {
anti_bump();
if (used_val != lastCounter){
usedPhase[5] = lastCounter;
}
stato = 2;
}
} while (stato != 2);
}
//************************************* HUMIDITY ********************
// in Percent
void M_Humidity() { // Menu 3
static int lastCounter = 0;
min_val = usedPhase[6];
max_val = usedPhase[7];
used_val = usedPhase[8];
Serial.println("Humidity");
Serial.println("phaseSel = "+String(phaseSel));
Serial.println("counter = "+String(counter));
Serial.println("min_val = "+String(min_val));
Serial.println("max_val = "+String(max_val));
Serial.println("used_val = "+String(used_val));
counter = used_val;
do {
if (counter != lastCounter) {
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_YELLOW);
tft.drawString("Humidity", 55, 5, 4);
tft.setTextColor(TFT_GREEN);
tft.drawString(String(used_val), 115, 50, 6);
tft.setTextColor(TFT_YELLOW);
tft.drawString("Percent %", 70, 110, 4);
Serial.println(counter);
}
lastCounter = counter;
read_encoder();
val_press = check_press();
if (val_press == COD_IN) {
anti_bump();
if (used_val != lastCounter){
usedPhase[8] = lastCounter;
}
stato = 2;
}
} while (stato != 2);
}
//************************************* VENTILATION ********************
// in Minutes
void M_Ventilation() { // Menu 4
static int lastCounter = 0;
min_val = usedPhase[9];
max_val = usedPhase[10];
used_val = usedPhase[11];
Serial.println("Ventilation");
Serial.println("phaseSel = "+String(phaseSel));
Serial.println("counter = "+String(counter));
Serial.println("min_val = "+String(min_val));
Serial.println("max_val = "+String(max_val));
Serial.println("used_val = "+String(used_val));
counter = used_val;
do {
if (counter != lastCounter) {
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_YELLOW);
tft.drawString("Ventilation", 55, 5, 4);
tft.setTextColor(TFT_GREEN);
tft.drawString(String(counter), 115, 50, 6);
tft.setTextColor(TFT_YELLOW);
tft.drawString("Ventilation min.", 50, 110, 4);
Serial.println(counter);
}
lastCounter = counter;
read_encoder();
val_press = check_press();
if (val_press == COD_IN) {
anti_bump();
if (used_val != lastCounter){
usedPhase[11] = lastCounter;
}
stato = 2;
}
} while (stato != 2);
}
//************************************* VENTILATION EVERY ***************
// in Hours
void M_vEvery() { // Menu 5
static int lastCounter = 0;
min_val = usedPhase[12];
max_val = usedPhase[13];
used_val = usedPhase[14];
Serial.println("M_vEvery");
Serial.println("phaseSel = "+String(phaseSel));
Serial.println("counter = "+String(counter));
Serial.println("min_val = "+String(min_val));
Serial.println("max_val = "+String(max_val));
Serial.println("used_val = "+String(used_val));
counter = used_val;
do {
if (counter != lastCounter) {
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_YELLOW);
tft.drawString("Ventilation", 50, 5, 4);
tft.setTextColor(TFT_GREEN);
tft.drawString(String(counter), 115, 50, 6);
tft.setTextColor(TFT_YELLOW);
tft.drawString("Every # Hours", 50, 110, 4);
Serial.println(counter);
}
lastCounter = counter;
read_encoder();
val_press = check_press();
if (val_press == COD_IN) {
anti_bump();
if (used_val != lastCounter){
usedPhase[14] = lastCounter;
}
stato = 2;
}
} while (stato != 2);
}
//************************************* STERILIZATION ***************
// in hours
void M_Sterilization() { // Menu 6
static int lastCounter = 0;
min_val = usedPhase[15];
max_val = usedPhase[16];
used_val = usedPhase[17];
Serial.println("M_Sterilization");
Serial.println("phaseSel = "+String(phaseSel));
Serial.println("counter = "+String(counter));
Serial.println("min_val = "+String(min_val));
Serial.println("max_val = "+String(max_val));
Serial.println("used_val = "+String(used_val));
counter = used_val;
do {
if (counter != lastCounter) {
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_YELLOW);
tft.drawString("Sterilization", 35, 5, 4);
tft.setTextColor(TFT_GREEN);
tft.drawString(String(counter), 115, 50, 6);
tft.setTextColor(TFT_YELLOW);
tft.drawString("Hours", 50, 110, 4);
Serial.println(counter);
}
lastCounter = counter;
read_encoder();
val_press = check_press();
if (val_press == COD_IN) {
anti_bump();
if (used_val != lastCounter){
usedPhase[17] = lastCounter;
}
stato = 2;
}
} while (stato != 2);
}
//************************************* MENU ******************************
//
void MainMenu() {
Serial.println("Menu");
menuSel = 0;
stato = 0;
counter = 0;
static int lastCounter = 0;
bool exitMenu = false;
val_press = 0;
min_val = 0;
max_val = 8;
do { // If count has changed print the new values to display
if (counter != lastCounter) {
menuSel = counter; // charge menuSel ect with counter
switch (menuSel) {
case 0:
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE);
tft.drawString("Menu", 95, 5, 4);
tft.drawString("Phase 1-2-3", 20, 35, 4);
break;
case 1:
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE);
tft.drawString("Menu", 95, 5, 4);
tft.drawString("Phase Duration", 20, 35, 4);
break;
case 2:
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE);
tft.drawString("Menu", 95, 5, 4);
tft.drawString("Temperature", 20, 35, 4);
break;
case 3:
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE);
tft.drawString("Menu", 95, 5, 4);
tft.drawString("Humidity", 20, 35, 4);
break;
case 4:
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE);
tft.drawString("Menu", 95, 5, 4);
tft.drawString("Ventilation", 20, 35, 4);
break;
case 5:
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE);
tft.drawString("Menu", 95, 5, 4);
tft.drawString("Vent. Every", 20, 35, 4);
break;
case 6:
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE);
tft.drawString("Menu", 95, 5, 4);
tft.drawString("Sterilization", 20, 35, 4);
break;
case 7:
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE);
tft.drawString("Menu", 95, 5, 4);
tft.drawString("EXIT Menu", 20, 35, 4);
break;
case 8:
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_YELLOW);
tft.drawString("Menu", 95, 5, 4);
tft.drawString("RESET ALL DATA", 20, 35, 4);
tft.drawString("SYSTEM RESTART", 20, 75, 4);
break;
}
}
val_press = check_press();
if (val_press == COD_IN) {
anti_bump();
Serial.print("Val_press= ");Serial.println(val_press);
Serial.print("counter= ");Serial.println(counter);
Serial.print("menuSel= ");Serial.println(menuSel);
switch (menuSel) {
case 0: // Phase type 1=Stewing 2=Drying 3=Seasoning
M_phase();
stato = 0;
Serial.print("phaseSel = ");
Serial.println(phaseSel);
lastCounter = 1;
counter = 0;
exitMenu = true;
break;
case 1: // Phase duration in days
M_pDuration();
stato = 0;
Serial.print("Phase Duration = ");
Serial.println(counter);
lastCounter = 0;
counter = 1;
exitMenu = true;
break;
case 2: // Temperature in C°
M_Temperature();
stato = 0;
Serial.print("Temperature");
Serial.println(counter);
lastCounter = 0;
counter = 2;
exitMenu = true;
break;
case 3: // Humidity in %
M_Humidity();
stato = 0;
Serial.print("Temperature");
Serial.println(counter);
lastCounter = 0;
counter = 3;
exitMenu = true;
break;
case 4: // Ventilation in minute
M_Ventilation();
stato = 0;
Serial.print("Ventilation");
Serial.println(counter);
lastCounter = 0;
counter = 4;
exitMenu = true;
break;
case 5:
M_vEvery();
stato = 0;
Serial.print("Ventilation Every");
Serial.println(counter);
lastCounter = 0;
counter = 5;
break;
case 6: // Sterilization in hours (run with Ventilation)
M_Sterilization();
stato = 0;
Serial.print("Sterilization");
Serial.println(counter);
lastCounter = 0;
counter = 6;
break;
case 7: // Exit Menu
stato = 2;
M_writing(); // before write all modified data
M_reading(); // reload all written data
lastCounter = 0;
counter = 7;
exitMenu = true;
break;
case 8:
// then reset and reboot
Serial.println("Restarting in 10 seconds");
delay(10000); /*ESP32 Reset after every 10 sec*/
M_deleteAll();
ESP.restart(); /*ESP restart function*/
break;
}
min_val = 0;
max_val = 8;
}
if (!exitMenu){
lastCounter = counter;
}
exitMenu = false;
read_encoder();
} while (stato != 2);
anti_bump();
Serial.println("Exit Menu");
tft.fillScreen(TFT_BLACK);
}
Encoder routine
////////////////////////////////////////////////////////////////////////////
//
// ENCODER ROUTINES
//
////////////////////////////////////////////////////////////////////////////
//************************************* START ANTI BUMP *******************
//
void anti_bump(void) {
while (!NO_PRESS) continue;
delay(T_ANTI_BUMP);
}
//*********************************** START CHECK BUTTON ****************
// Returned 3 for Normal Push and 2 for Long Push
//
unsigned char check_press(void) {
unsigned char exit;
exit = 0;
if (IN_PRESS)
{
exit = COD_IN;
ms_press = millis();
delay(50);
do {
if ((millis() - ms_press) >= PRESS_CONFIG) exit = COD_IN_LONG;
} while (IN_PRESS && exit!= COD_IN_LONG);
Serial.println(exit, DEC);
return exit;
}
return exit;
}
//************************************* START READ ENCODER ******************
// return CW 1, 2, 3 - CCW -1, -2, -3
//
void read_encoder() {
static uint8_t old_AB = 3; // Lookup table index
static int8_t encval = 0; // Encoder value
static const int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0}; // Lookup table
old_AB <<= 2; // Remember previous state
if (digitalRead(ENC_A)) old_AB |= 0x02; // Add current state of pin A
if (digitalRead(ENC_B)) old_AB |= 0x01; // Add current state of pin B
encval += enc_states[( old_AB & 0x0f )];
// Update counter if encoder has rotated a full indent, that is at least 4 steps
if( encval > 3 ) { // Four steps forward
counter++; // Increase counter
if (counter >= max_val) {counter = max_val;}
encval = 0;
}
else if( encval < -3 ) { // Four steps backwards
counter--; // Decrease counter
if (counter <= min_val) {counter = min_val;}
encval = 0;
}
}
EEPROM management
//
// allData => Name Space=>"allData" // Reference for all stored data
// phaseID => Name Space=>"phaseID" // 1 byte String to int
// ssid => Name Space=>"ssid" // 20 bytes
// password => Name Space=>"password" // 20 bytes
// int16_t defphase1[18] = { 1, 3, 0, 10, 30, 0, 40, 80, 0, 10, 60, 0, 1, 5, 0, 3, 24, 0 }; // Name Space=> defPhase1 STEWING phase_1 following standard values
// int16_t defphase2[18] = { 4, 15, 0, 5, 15, 0, 30, 80, 0, 10, 60, 0, 1, 5, 0, 3, 24, 0 }; // Name Space=> defPhase2 DRYING phase_2 following standard values
// int16_t defphase3[18] = {10, 365, 0, 5, 15, 0, 30, 80, 0, 10, 60, 0, 1, 5, 0, 3, 24, 0 }; // Name Space=> defPhase3 SEASONING phase_3 following standard values
// int16_t usedPhase[18]; // Name Space=> usedPhase in use
//
// Preferences prefs;
//************************************* INIT EEPROM ******************************
//
void M_init() {
delay(500);
prefs.begin("allData", RO_MODE);
bool existNS = prefs.isKey("allData");
Serial.println("existNS= "+String(existNS));
if ( existNS == false ) {
prefs.end();
prefs.begin("allData", RW_MODE);
phaseID = "3";
ssid = "MYHOME";
password = "password";
prefs.putString("phaseID", phaseID);
prefs.putString("ssid", ssid);
prefs.putString("password", password);
prefs.putBytes("defPhase1", defPhase1, sizeof(defPhase1));
prefs.putBytes("defPhase2", defPhase2, sizeof(defPhase2));
prefs.putBytes("defPhase3", defPhase3, sizeof(defPhase3));
prefs.end();
Serial.println("Created all Data Space Name");
}
phaseSel = phaseID.toInt();
}
//************************************* READING EEPROM ******************************
//
void M_reading() {
delay(500);
prefs.begin("allData", RO_MODE);
phaseSel = ( prefs.getString("phaseID", phaseID) ).toInt();
ssid = prefs.getString("ssid", ssid);
password = prefs.getString("password", password);
if (phaseID == "1"){
prefs.getBytes("defPhase1", usedPhase, prefs.getBytesLength("defPhase1"));
}
if (phaseID == "2"){
prefs.getBytes("defPhase2", usedPhase, prefs.getBytesLength("defPhase2"));
}
if (phaseID == "3"){
prefs.getBytes("defPhase3", usedPhase, prefs.getBytesLength("defPhase3"));
}
prefs.end();
Serial.print("phaseSel= ");
Serial.println(phaseSel);
Serial.println("ssid= "+String(ssid));
Serial.println("password= "+String(password));
for (int i = 0; i < 18; i++){
Serial.print("usedPhase[" + String(i) + "]= ");
Serial.println(usedPhase[i]);
}
}
//************************************* WRITING EEPROM ******************************
//
void M_writing() {
delay(500);
prefs.begin("allData", RW_MODE);
prefs.putString("phaseID", phaseID);
prefs.putString("ssid", ssid);
prefs.putString("password", password);
if (phaseID == "1"){
prefs.putBytes("defPhase1", usedPhase, prefs.getBytesLength("defPhase1"));
}
if (phaseID == "2"){
prefs.putBytes("defPhase2", usedPhase, prefs.getBytesLength("defPhase2"));
}
if (phaseID == "3"){
prefs.putBytes("defPhase3", usedPhase, prefs.getBytesLength("defPhase3"));
}
prefs.end();
}
//************************************* DELETE EEPROM ******************************
//
void M_deleteAll() {
Serial.println("Delete all Name Spaces");
nvs_flash_erase(); // erase the NVS partition and...
nvs_flash_init(); // initialize the NVS partition.
while (true);
delay(1000);
}
That is all