Hello. In my project I need to activate a fan each day, at a specific time and for a specific time interval. I don't have enough program storage memory and free pins on my Arduino Nano, how would you do it without an RTC clock module? Thank you
Until you post your code and project specifics, Iād say a wristwatch and a switch.
Retailers sell time switches.
Then I guess there is no solution, except for reducing your usage of program storage memory and free pins. There are almost always ways to achieve those things. Please post your schematic and code according to forum guide.
This highly depends on the details of your project.
So you really should post a schematic and your complete sketch.
-
Are you using an LCD ? Yes? / No?
-
if you use an LCD does the LCD have parallel or I2C-interface?
-
For what purposes do you use your IO-pins?
-
PWM ?
-
switching on / off ? what?
-
reading in sensors?
. what kind of sensors?
from all these questions you can see that
you really should post a schematic a project-description and your complete sketch.
The project is a meat-dryer machine, using a refrigerator, a humidifier, dehumidifier and light bulb as heating source.
The code is reading the values of temperature and humidity from a BME280 I2C sensor and it is turning on/off 4 relays (3 SSR 2A relays + 1 30A relay module) that are controlling the on/off state of the heating/cooling/humidifying/dehumidifying devices, according to the desidered values of temperature and humidity that are defined in the sketch as "temp_set" and "hum_set". There's also a SSD1306 OLED display with a menu through which the set/current temperature and humidity are shown and can be modified with the aim of four push buttons.
I'd like to add another relay (pin 8: RELAY_FAN_EXT), controlling the on/off state of a 12V PC fan at specific time or, at least, once every specific amount of time for a specific time interval (for example 10 minutes every 24 hours, starting from when the project is powered).
The sketch is already using 88% of internal storage memory of Arduino Nano and 89% of dynamic memory.
One idea I had was to define custom fonts for the OLED display with just the specific characters I'm using, but I don't know how to do that in the u8g2 library.
This is the sketch:
#include <Wire.h> /* I2C Library */
#include <BME280I2C.h> /* Temperature/Humidity sensor: Connect to SDA/SCL */
#include <U8g2lib.h> /* Oled display 128x64: Connect to SDA/SCL */
#define ON true
#define OFF false
#define RELAY_BUTTON_UMI 2
#define RELAY_AC_DEU 12
#define RELAY_AC_COOL 11
#define RELAY_AC_HEAT 10
#define RELAY_AC_UMI 9
#define RELAY_FAN_EXT 8
#define BUTTON_1 3
#define BUTTON_2 4
#define BUTTON_3 5
#define BUTTON_4 6
float p, t, h;
// Define 4 state variables for the devices: 0 = "OFF", 1 = "ON"
unsigned short int umi_state = 0;
unsigned short int deu_state = 0;
unsigned short int cool_state = 0;
unsigned short int heat_state = 0;
int screen_state = 10;
// Set temperature and humidity: default values and deltas (They can be modified by the user, later on)
int temp_set = 20;
int delta_t_set = 3;
int hum_set = 62;
int delta_h_set = 3;
BME280I2C bme;
//U8GLIB_SSD1306_128X64 oled(U8G_I2C_OPT_NONE);
U8G2_SSD1306_128X64_NONAME_F_HW_I2C oled(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
struct arguments {
float t_current;
float h_current;
float t_set;
float h_set;
float dt;
float dh;
};
void setup() {
pinMode(RELAY_BUTTON_UMI, OUTPUT);
pinMode(RELAY_AC_UMI, OUTPUT);
pinMode(BUTTON_1, INPUT);
pinMode(BUTTON_2, INPUT);
pinMode(BUTTON_3, INPUT);
pinMode(BUTTON_4, INPUT);
digitalWrite(RELAY_BUTTON_UMI, LOW);
digitalWrite(RELAY_AC_UMI, LOW);
digitalWrite(RELAY_AC_DEU, LOW);
digitalWrite(RELAY_AC_COOL, LOW);
digitalWrite(RELAY_AC_HEAT, LOW);
oled.begin();
bme.begin();
delay(500);
}
void loop() {
//Read (pressure), temperature and humidity with BME280 sensor and write them in p, t, h global variables
bme.read(p, t, h);
arguments func_args = {t, h, temp_set, hum_set, delta_t_set, delta_h_set};
unsigned short int button_state_1 = digitalRead(BUTTON_1); // Set T/H e SET
unsigned short int button_state_2 = digitalRead(BUTTON_2); // -
unsigned short int button_state_3 = digitalRead(BUTTON_3); // +
unsigned short int button_state_4 = digitalRead(BUTTON_4); // Info e Home
screen_state = state_selection(button_state_1, button_state_2, button_state_3, button_state_4, screen_state); // I use the function <state_selection> to select the value of screen_state
switch (screen_state) {
case 10:
oled.clearBuffer();
default_page(func_args);
oled.sendBuffer();
break;
case 20:
oled.clearBuffer();
set_temp_page(func_args);
oled.sendBuffer();
if (button_state_2 == HIGH) {
temp_set -= 1;
} else if (button_state_3 == HIGH){
temp_set += 1;
}
break;
case 30:
oled.clearBuffer();
info_page(func_args);
oled.sendBuffer();
break;
case 21:
oled.clearBuffer();
set_dt_page(func_args);
oled.sendBuffer();
if (button_state_2 == HIGH) {
delta_t_set -= 1;
} else if (button_state_3 == HIGH){
delta_t_set += 1;
}
break;
case 22:
oled.clearBuffer();
set_hum_page(func_args);
oled.sendBuffer();
if (button_state_2 == HIGH) {
hum_set -= 1;
} else if (button_state_3 == HIGH){
hum_set += 1;
}
break;
case 23:
oled.clearBuffer();
set_dh_page(func_args);
oled.sendBuffer();
if (button_state_2 == HIGH) {
delta_h_set -= 1;
} else if (button_state_3 == HIGH){
delta_h_set += 1;
}
break;
}
// Call for the functions that are keeping T and H constant.
keep_T_const(t, temp_set, delta_t_set);
keep_H_const(h, hum_set, delta_h_set);
delay(300);
}
// ______________________________________________ Functions __________________________________________________
int state_selection(unsigned short int button_state_1, unsigned short int button_state_2, unsigned short int button_state_3, unsigned short int button_state_4, int screen_state){
if (button_state_1 == HIGH && screen_state == 10){ // If you are on HOME and you press the first button, you go to T/H selection page.
screen_state = 20;
} else if (button_state_4 == HIGH && screen_state == 10){ // Homepage to Info
screen_state = 30;
} else if (button_state_4 == HIGH && screen_state == 20){
screen_state = 21;
} else if (button_state_4 == HIGH && screen_state == 21) {
screen_state = 22;
} else if (button_state_4 == HIGH && screen_state == 22) {
screen_state = 23;
} else if (button_state_4 == HIGH && screen_state == 23) {
screen_state = 10;
} else if (button_state_1 == HIGH && (screen_state == 20 || screen_state == 21 || screen_state == 22 || screen_state == 23 || screen_state == 30)) {
screen_state = 10;
}
return screen_state;
}
// OLED menu drawing functions:
void default_page(arguments& args) {
oled.setFont(u8g2_font_8x13_tf);
// oled.setFont(u8g2_font_fur11);
oled.setCursor(7, 14);
oled.print("T:");
oled.setCursor(7, 30);
oled.print("H:");
// Print current temperature and humidity values (showing 1 decimal).
oled.setCursor(25, 14);
oled.print(args.t_current, 1);
oled.print(char(176));
oled.print("C");
oled.setCursor(25, 30);
oled.print(args.h_current, 1);
oled.print("%");
// Write T+ T- H+ H- on top of humi/deum cooler/heater state boxes
oled.setFont(u8g2_font_6x10_tr);
oled.drawStr(92, 8, "T+");
oled.drawStr(112,8, "T-");
oled.drawStr(92,37, "H+");
oled.drawStr(112,37, "H-");
// Write T_set, H_set, dt, dh values
oled.setCursor(7, 43);
oled.print("T");
oled.setFont(u8g2_font_4x6_tr);
oled.print("set");
oled.setFont(u8g2_font_6x10_tf);
oled.print(": ");
oled.print(args.t_set, 0);
oled.print(char(177));
oled.print(args.dt, 0);
oled.print(char(176));
oled.print("C");
oled.setCursor(7, 54);
oled.print("H");
oled.setFont(u8g2_font_4x6_tr);
oled.print("set");
oled.setFont(u8g2_font_6x10_tf);
oled.print(": ");
oled.print(args.h_set, 0);
oled.print(char(177));
oled.print(args.dh, 0);
oled.print("%");
// Screen selection white bar + vertical line
oled.drawLine(83, 0, 83, 70);
oled.drawBox(0, 57, 130, 7);
oled.setCursor(3, 63);
oled.setFont(u8g2_font_4x6_tr);
oled.setColorIndex(0);
oled.print("Set T/H");
oled.setCursor(110, 63);
oled.print("Info");
oled.setColorIndex(1);
// Check for the ON/OFF state of the devices and draw EMPTY/FULL boxes accordingly + On/Off in each box.
if (umi_state == 1) {
oled.drawBox(89, 40, 15, 15); // H+ ON == WHITE BOX
oled.setColorIndex(0); // Imposta il colore a nero
oled.drawStr(93, 50, "On"); // Scrive il testo all'interno del rettangolo
oled.setColorIndex(1); // Imposta il colore a bianco
} else {
oled.drawFrame(89, 40, 15, 15); // H+ ON == BLACK BOX
oled.setColorIndex(1); // Imposta il colore a nero
oled.drawStr(91, 50, "Off"); // Scrive il testo all'interno del rettangolo
oled.setColorIndex(1); // Imposta il colore a bianco
}
if (deu_state == 1) {
oled.drawBox(109, 40, 15, 15); // H- ON == WHITE BOX
oled.setColorIndex(0); // Imposta il colore a nero
oled.drawStr(113, 50, "On"); // Scrive il testo all'interno del rettangolo
oled.setColorIndex(1); // Imposta il colore a bianco
} else {
oled.drawFrame(109, 40, 15, 15); // H- ON == BLACK BOX
oled.setColorIndex(1); // Imposta il colore a nero
oled.drawStr(111, 50, "Off"); // Scrive il testo all'interno del rettangolo
oled.setColorIndex(1); // Imposta il colore a bianco
}
if (cool_state == 1) {
oled.drawBox(109, 11, 15, 15); // T- ON == WHITE BOX
oled.setColorIndex(0); // Imposta il colore a nero
oled.drawStr(113, 21, "On"); // Scrive il testo all'interno del rettangolo
oled.setColorIndex(1); // Imposta il colore a bianco
} else {
oled.drawFrame(109, 11, 15, 15); // T- ON == BLACK BOX
oled.setColorIndex(1); // Imposta il colore a nero
oled.drawStr(111, 21, "Off"); // Scrive il testo all'interno del rettangolo
oled.setColorIndex(1); // Imposta il colore a bianco
}
if (heat_state == 1) {
oled.drawBox(89, 11, 15, 15); // T+ ON == WHITE BOX
oled.setColorIndex(0); // Imposta il colore a nero
oled.drawStr(93, 21, "On"); // Scrive il testo all'interno del rettangolo
oled.setColorIndex(1); // Imposta il colore a bianco
} else {
oled.drawFrame(89, 11, 15, 15); // T+ ON == BLACK BOX
oled.setColorIndex(1); // Imposta il colore a nero
oled.drawStr(91, 21, "Off"); // Scrive il testo all'interno del rettangolo
oled.setColorIndex(1); // Imposta il colore a bianco
}
}
void info_page(arguments& args) {
oled.setFont(u8g2_font_4x6_tr);
oled.setCursor(1, 8);
oled.print("Fase: Stagionatura");
oled.setCursor(1, 17);
oled.print("Prodotto: Salame");
oled.setCursor(1, 26);
oled.print("Inizio: 24/4/24");
oled.setCursor(1, 35);
oled.print("Temperatura:");
oled.print(args.t_set, 0);
oled.setCursor(80, 35);
oled.print("Delta:");
oled.setCursor(110, 35);
oled.print(args.dt, 0);
oled.setCursor(1, 44);
oled.print("Umidita':");
oled.print(args.h_set, 0);
oled.setCursor(80, 44);
oled.print("Delta:");
oled.setCursor(110, 44);
oled.print(args.dh, 0);
// Screen selection white bar
oled.drawBox(0, 57, 130, 7);
oled.setCursor(3, 63);
oled.setFont(u8g2_font_4x6_tr);
oled.setColorIndex(0);
oled.print("Home");
oled.setCursor(110, 63);
//oled.print("Info");
oled.setColorIndex(1);
}
void set_temp_page(arguments& args) {
//Draw a box around the quantity you are changing: TEMPERATURE
oled.drawBox(8, 9, 45, 13);
oled.setColorIndex(0);
oled.setFont(u8g2_font_8x13_tr);
oled.setCursor(10, 20);
oled.print("T:");
oled.setCursor(31, 20);
oled.print(args.t_set, 0);
oled.setColorIndex(1);
oled.setCursor(10, 40);
oled.print("dT:");
oled.setCursor(35, 40);
oled.print(args.dt, 0);
oled.setCursor(70, 20);
oled.print("H:");
oled.setCursor(70, 40);
oled.print("dH:");
oled.setCursor(91, 20);
oled.print(args.h_set, 0);
oled.setCursor(95, 40);
oled.print(args.dh, 0);
// Screen selection white bar
oled.drawBox(0, 57, 130, 7);
oled.setCursor(3, 63);
oled.setFont(u8g2_font_4x6_tr);
oled.setColorIndex(0);
oled.print("Home");
oled.setCursor(46, 63);
oled.print("+");
oled.setCursor(80, 63);
oled.print("-");
oled.setCursor(110, 63);
oled.print("Next");
oled.setColorIndex(1);
}
void set_dt_page(arguments& args) {
//Draw a box around the quantity you are changing: TEMPERATURE
oled.setFont(u8g2_font_8x13_tr);
oled.setCursor(10, 20);
oled.print("T:");
oled.setCursor(31, 20);
oled.print(args.t_set, 0);
oled.drawBox(8, 29, 45, 13);
oled.setColorIndex(0);
oled.setCursor(10, 40);
oled.print("dT:");
oled.setCursor(35, 40);
oled.print(args.dt, 0);
oled.setColorIndex(1);
oled.setCursor(70, 20);
oled.print("H:");
oled.setCursor(70, 40);
oled.print("dH:");
oled.setCursor(91, 20);
oled.print(args.h_set, 0);
oled.setCursor(95, 40);
oled.print(args.dh, 0);
// Screen selection white bar
oled.drawBox(0, 57, 130, 7);
oled.setCursor(3, 63);
oled.setFont(u8g2_font_4x6_tr);
oled.setColorIndex(0);
oled.print("Home");
oled.setCursor(46, 63);
oled.print("+");
oled.setCursor(80, 63);
oled.print("-");
oled.setCursor(110, 63);
oled.print("Next");
oled.setColorIndex(1);
}
void set_hum_page(arguments& args) {
//Draw a box around the quantity you are changing: TEMPERATURE
oled.setFont(u8g2_font_8x13_tr);
oled.setCursor(10, 20);
oled.print("T:");
oled.setCursor(31, 20);
oled.print(args.t_set, 0);
oled.setCursor(10, 40);
oled.print("dT:");
oled.setCursor(35, 40);
oled.print(args.dt, 0);
oled.drawBox(68, 9, 45, 13);
oled.setColorIndex(0);
oled.setCursor(70, 20);
oled.print("H:");
oled.setCursor(91, 20);
oled.print(args.h_set, 0);
oled.setColorIndex(1);
oled.setCursor(70, 40);
oled.print("dH:");
oled.setCursor(95, 40);
oled.print(args.dh, 0);
// Screen selection white bar
oled.drawBox(0, 57, 130, 7);
oled.setCursor(3, 63);
oled.setFont(u8g2_font_4x6_tr);
oled.setColorIndex(0);
oled.print("Home");
oled.setCursor(46, 63);
oled.print("+");
oled.setCursor(80, 63);
oled.print("-");
oled.setCursor(110, 63);
oled.print("Next");
oled.setColorIndex(1);
}
void set_dh_page(arguments& args) {
//Draw a box around the quantity you are changing: Delta_Humidity
oled.setFont(u8g2_font_8x13_tr);
oled.setCursor(10, 20);
oled.print("T:");
oled.setCursor(31, 20);
oled.print(args.t_set, 0);
oled.setCursor(10, 40);
oled.print("dT:");
oled.setCursor(35, 40);
oled.print(args.dt, 0);
oled.setCursor(70, 20);
oled.print("H:");
oled.setCursor(91, 20);
oled.print(args.h_set, 0);
oled.drawBox(68, 29, 45, 13);
oled.setColorIndex(0);
oled.setCursor(70, 40);
oled.print("dH:");
oled.setCursor(95, 40);
oled.print(args.dh, 0);
oled.setColorIndex(1);
// Screen selection white bar
oled.drawBox(0, 57, 130, 7);
oled.setCursor(3, 63);
oled.setFont(u8g2_font_4x6_tr);
oled.setColorIndex(0);
oled.print("Home");
oled.setCursor(46, 63);
oled.print("+");
oled.setCursor(80, 63);
oled.print("-");
oled.setCursor(110, 63);
oled.print("Exit");
oled.setColorIndex(1);
}
// TEMPERATURE control with REFRIGERATOR and LIGHT BULB
void keep_T_const(float t_current, float temp, float dt) {
if( t_current > temp + dt && cool_state == 0){
cool_control(ON);
cool_state = 1;
} else if ( t_current < temp && cool_state == 1){
cool_control(OFF);
cool_state = 0;
} else if ( t_current < temp - dt && heat_state == 0){
heat_control(ON);
heat_state = 1;
} else if ( t_current > temp && heat_state == 1){
heat_control(OFF);
heat_state = 0;
} else {
//Nothing to do
}
}
// HUMIDITY control with HUMIDIFIER and DEHUMIDIFIER
void keep_H_const(float h_current, float hum, float dh) {
if( h_current > hum + dh && deu_state == 0){
deu_control(ON);
deu_state = 1;
} else if ( h_current < hum && deu_state == 1){
deu_control(OFF);
deu_state = 0;
} else if ( h_current < hum - dh && umi_state == 0){
umi_control(ON);
umi_state = 1;
} else if ( h_current > hum && umi_state == 1){
umi_control(OFF);
umi_state = 0;
} else {
//Nothing to do
}
}
// Definition of the 4 functions for the device control: used to turn them ON or OFF.
void umi_control(bool command) {
if (command == ON) {
// Turn ON the humidifier AC outlet
digitalWrite(RELAY_AC_UMI, HIGH);
delay(500);
// Double short press on humidifier button to turn it ON (with continuous working flow)
digitalWrite(RELAY_BUTTON_UMI, HIGH);
delay(200);
digitalWrite(RELAY_BUTTON_UMI, LOW);
delay(200);
digitalWrite(RELAY_BUTTON_UMI, HIGH);
delay(200);
digitalWrite(RELAY_BUTTON_UMI, LOW);
} else if (command == OFF) {
// Long press on humidifier button to turn it OFF
digitalWrite(RELAY_BUTTON_UMI, HIGH);
delay(2100);
digitalWrite(RELAY_BUTTON_UMI, LOW);
delay(200);
// Turn OFF the humidifier AC outlet
digitalWrite(RELAY_AC_UMI, LOW);
} else {
return;
}
}
void cool_control(bool command) {
if (command == ON) {
// Turn ON the refrigerator AC outlet
digitalWrite(RELAY_AC_COOL, HIGH);
} else if (command == OFF) {
// Turn OFF the refrigerator AC outlet
digitalWrite(RELAY_AC_COOL, LOW);
} else {
return;
}
}
void deu_control(bool command) {
if (command == ON) {
// Turn ON the dehumidifier AC outlet
digitalWrite(RELAY_AC_DEU, HIGH);
} else if (command == OFF) {
// Turn OFF the dehumidifier AC outlet
digitalWrite(RELAY_AC_DEU, LOW);
} else {
return;
}
}
void heat_control(bool command) {
if (command == ON) {
// Turn ON the heater AC outlet
digitalWrite(RELAY_AC_HEAT, HIGH);
} else if (command == OFF) {
// Turn OFF the heater AC outlet
digitalWrite(RELAY_AC_HEAT, LOW);
} else {
return;
}
}
you can use a different SSD1306-library that uses less RAM
You are using a lot of string-constants. They can be moved into flash-memory by using the F-macro. 1690 bytes instead of 1792 bytes RAM usage
#include <Wire.h> /* I2C Library */
#include <BME280I2C.h> /* Temperature/Humidity sensor: Connect to SDA/SCL */
#include <U8g2lib.h> /* Oled display 128x64: Connect to SDA/SCL */
#define ON true
#define OFF false
#define RELAY_BUTTON_UMI 2
#define RELAY_AC_DEU 12
#define RELAY_AC_COOL 11
#define RELAY_AC_HEAT 10
#define RELAY_AC_UMI 9
#define RELAY_FAN_EXT 8
#define BUTTON_1 3
#define BUTTON_2 4
#define BUTTON_3 5
#define BUTTON_4 6
float p, t, h;
// Define 4 state variables for the devices: 0 = "OFF", 1 = "ON"
unsigned short int umi_state = 0;
unsigned short int deu_state = 0;
unsigned short int cool_state = 0;
unsigned short int heat_state = 0;
int screen_state = 10;
// Set temperature and humidity: default values and deltas (They can be modified by the user, later on)
int temp_set = 20;
int delta_t_set = 3;
int hum_set = 62;
int delta_h_set = 3;
BME280I2C bme;
//U8GLIB_SSD1306_128X64 oled(U8G_I2C_OPT_NONE);
U8G2_SSD1306_128X64_NONAME_F_HW_I2C oled(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
struct arguments {
float t_current;
float h_current;
float t_set;
float h_set;
float dt;
float dh;
};
void setup() {
pinMode(RELAY_BUTTON_UMI, OUTPUT);
pinMode(RELAY_AC_UMI, OUTPUT);
pinMode(BUTTON_1, INPUT);
pinMode(BUTTON_2, INPUT);
pinMode(BUTTON_3, INPUT);
pinMode(BUTTON_4, INPUT);
digitalWrite(RELAY_BUTTON_UMI, LOW);
digitalWrite(RELAY_AC_UMI, LOW);
digitalWrite(RELAY_AC_DEU, LOW);
digitalWrite(RELAY_AC_COOL, LOW);
digitalWrite(RELAY_AC_HEAT, LOW);
oled.begin();
bme.begin();
delay(500);
}
void loop() {
//Read (pressure), temperature and humidity with BME280 sensor and write them in p, t, h global variables
bme.read(p, t, h);
arguments func_args = {t, h, temp_set, hum_set, delta_t_set, delta_h_set};
unsigned short int button_state_1 = digitalRead(BUTTON_1); // Set T/H e SET
unsigned short int button_state_2 = digitalRead(BUTTON_2); // -
unsigned short int button_state_3 = digitalRead(BUTTON_3); // +
unsigned short int button_state_4 = digitalRead(BUTTON_4); // Info e Home
screen_state = state_selection(button_state_1, button_state_2, button_state_3, button_state_4, screen_state); // I use the function <state_selection> to select the value of screen_state
switch (screen_state) {
case 10:
oled.clearBuffer();
default_page(func_args);
oled.sendBuffer();
break;
case 20:
oled.clearBuffer();
set_temp_page(func_args);
oled.sendBuffer();
if (button_state_2 == HIGH) {
temp_set -= 1;
} else if (button_state_3 == HIGH) {
temp_set += 1;
}
break;
case 30:
oled.clearBuffer();
info_page(func_args);
oled.sendBuffer();
break;
case 21:
oled.clearBuffer();
set_dt_page(func_args);
oled.sendBuffer();
if (button_state_2 == HIGH) {
delta_t_set -= 1;
} else if (button_state_3 == HIGH) {
delta_t_set += 1;
}
break;
case 22:
oled.clearBuffer();
set_hum_page(func_args);
oled.sendBuffer();
if (button_state_2 == HIGH) {
hum_set -= 1;
} else if (button_state_3 == HIGH) {
hum_set += 1;
}
break;
case 23:
oled.clearBuffer();
set_dh_page(func_args);
oled.sendBuffer();
if (button_state_2 == HIGH) {
delta_h_set -= 1;
} else if (button_state_3 == HIGH) {
delta_h_set += 1;
}
break;
}
// Call for the functions that are keeping T and H constant.
keep_T_const(t, temp_set, delta_t_set);
keep_H_const(h, hum_set, delta_h_set);
delay(300);
}
// ______________________________________________ Functions __________________________________________________
int state_selection(unsigned short int button_state_1, unsigned short int button_state_2, unsigned short int button_state_3, unsigned short int button_state_4, int screen_state) {
if (button_state_1 == HIGH && screen_state == 10) { // If you are on HOME and you press the first button, you go to T/H selection page.
screen_state = 20;
} else if (button_state_4 == HIGH && screen_state == 10) { // Homepage to Info
screen_state = 30;
} else if (button_state_4 == HIGH && screen_state == 20) {
screen_state = 21;
} else if (button_state_4 == HIGH && screen_state == 21) {
screen_state = 22;
} else if (button_state_4 == HIGH && screen_state == 22) {
screen_state = 23;
} else if (button_state_4 == HIGH && screen_state == 23) {
screen_state = 10;
} else if (button_state_1 == HIGH && (screen_state == 20 || screen_state == 21 || screen_state == 22 || screen_state == 23 || screen_state == 30)) {
screen_state = 10;
}
return screen_state;
}
// OLED menu drawing functions:
void default_page(arguments& args) {
oled.setFont(u8g2_font_8x13_tf);
// oled.setFont(u8g2_font_fur11);
oled.setCursor(7, 14);
oled.print("T:");
oled.setCursor(7, 30);
oled.print("H:");
// Print current temperature and humidity values (showing 1 decimal).
oled.setCursor(25, 14);
oled.print(args.t_current, 1);
oled.print(char(176));
oled.print("C");
oled.setCursor(25, 30);
oled.print(args.h_current, 1);
oled.print("%");
// Write T+ T- H+ H- on top of humi/deum cooler/heater state boxes
oled.setFont(u8g2_font_6x10_tr);
oled.drawStr(92, 8, "T+");
oled.drawStr(112, 8, "T-");
oled.drawStr(92, 37, "H+");
oled.drawStr(112, 37, "H-");
// Write T_set, H_set, dt, dh values
oled.setCursor(7, 43);
oled.print( F("T") );
oled.setFont(u8g2_font_4x6_tr);
oled.print( F("set") );
oled.setFont(u8g2_font_6x10_tf);
oled.print( F(": ") );
oled.print(args.t_set, 0);
oled.print(char(177));
oled.print(args.dt, 0);
oled.print(char(176));
oled.print(F("C") );
oled.setCursor(7, 54);
oled.print( F("H") );
oled.setFont(u8g2_font_4x6_tr);
oled.print( F("set") );
oled.setFont(u8g2_font_6x10_tf);
oled.print( F(": ") );
oled.print(args.h_set, 0);
oled.print(char(177));
oled.print(args.dh, 0);
oled.print( F("%") );
// Screen selection white bar + vertical line
oled.drawLine(83, 0, 83, 70);
oled.drawBox(0, 57, 130, 7);
oled.setCursor(3, 63);
oled.setFont(u8g2_font_4x6_tr);
oled.setColorIndex(0);
oled.print( F("Set T/H") );
oled.setCursor(110, 63);
oled.print( F("Info") );
oled.setColorIndex(1);
char OnStr[] = "On";
char OffStr[] = "Off";
// Check for the ON/OFF state of the devices and draw EMPTY/FULL boxes accordingly + On/Off in each box.
if (umi_state == 1) {
oled.drawBox(89, 40, 15, 15); // H+ ON == WHITE BOX
oled.setColorIndex(0); // Imposta il colore a nero
oled.drawStr(93, 50, OnStr); // Scrive il testo all'interno del rettangolo
oled.setColorIndex(1); // Imposta il colore a bianco
} else {
oled.drawFrame(89, 40, 15, 15); // H+ ON == BLACK BOX
oled.setColorIndex(1); // Imposta il colore a nero
oled.drawStr(91, 50, OffStr); // Scrive il testo all'interno del rettangolo
oled.setColorIndex(1); // Imposta il colore a bianco
}
if (deu_state == 1) {
oled.drawBox(109, 40, 15, 15); // H- ON == WHITE BOX
oled.setColorIndex(0); // Imposta il colore a nero
oled.drawStr(113, 50, OnStr); // Scrive il testo all'interno del rettangolo
oled.setColorIndex(1); // Imposta il colore a bianco
} else {
oled.drawFrame(109, 40, 15, 15); // H- ON == BLACK BOX
oled.setColorIndex(1); // Imposta il colore a nero
oled.drawStr(111, 50, OffStr); // Scrive il testo all'interno del rettangolo
oled.setColorIndex(1); // Imposta il colore a bianco
}
if (cool_state == 1) {
oled.drawBox(109, 11, 15, 15); // T- ON == WHITE BOX
oled.setColorIndex(0); // Imposta il colore a nero
oled.drawStr(113, 21, OnStr); // Scrive il testo all'interno del rettangolo
oled.setColorIndex(1); // Imposta il colore a bianco
} else {
oled.drawFrame(109, 11, 15, 15); // T- ON == BLACK BOX
oled.setColorIndex(1); // Imposta il colore a nero
oled.drawStr(111, 21, OffStr); // Scrive il testo all'interno del rettangolo
oled.setColorIndex(1); // Imposta il colore a bianco
}
if (heat_state == 1) {
oled.drawBox(89, 11, 15, 15); // T+ ON == WHITE BOX
oled.setColorIndex(0); // Imposta il colore a nero
oled.drawStr(93, 21, OnStr); // Scrive il testo all'interno del rettangolo
oled.setColorIndex(1); // Imposta il colore a bianco
} else {
oled.drawFrame(89, 11, 15, 15); // T+ ON == BLACK BOX
oled.setColorIndex(1); // Imposta il colore a nero
oled.drawStr(91, 21, OffStr); // Scrive il testo all'interno del rettangolo
oled.setColorIndex(1); // Imposta il colore a bianco
}
}
void info_page(arguments& args) {
oled.setFont(u8g2_font_4x6_tr);
oled.setCursor(1, 8);
oled.print( F("Fase: Stagionatura") );
oled.setCursor(1, 17);
oled.print( F("Prodotto: Salame") );
oled.setCursor(1, 26);
oled.print( F("Inizio: 24/4/24") );
oled.setCursor(1, 35);
oled.print( F("Temperatura:") );
oled.print(args.t_set, 0);
oled.setCursor(80, 35);
oled.print( F("Delta:") );
oled.setCursor(110, 35);
oled.print(args.dt, 0);
oled.setCursor(1, 44);
oled.print(F("Umidita':") );
oled.print(args.h_set, 0);
oled.setCursor(80, 44);
oled.print( F("Delta:") );
oled.setCursor(110, 44);
oled.print(args.dh, 0);
// Screen selection white bar
oled.drawBox(0, 57, 130, 7);
oled.setCursor(3, 63);
oled.setFont(u8g2_font_4x6_tr);
oled.setColorIndex(0);
oled.print( F("Home") );
oled.setCursor(110, 63);
//oled.print("Info");
oled.setColorIndex(1);
}
void set_temp_page(arguments& args) {
//Draw a box around the quantity you are changing: TEMPERATURE
oled.drawBox(8, 9, 45, 13);
oled.setColorIndex(0);
oled.setFont(u8g2_font_8x13_tr);
oled.setCursor(10, 20);
oled.print( F("T:") );
oled.setCursor(31, 20);
oled.print(args.t_set, 0);
oled.setColorIndex(1);
oled.setCursor(10, 40);
oled.print( F("dT:") );
oled.setCursor(35, 40);
oled.print(args.dt, 0);
oled.setCursor(70, 20);
oled.print( F("H:") );
oled.setCursor(70, 40);
oled.print( F("dH:") );
oled.setCursor(91, 20);
oled.print(args.h_set, 0);
oled.setCursor(95, 40);
oled.print(args.dh, 0);
// Screen selection white bar
oled.drawBox(0, 57, 130, 7);
oled.setCursor(3, 63);
oled.setFont(u8g2_font_4x6_tr);
oled.setColorIndex(0);
oled.print(F("Home") );
oled.setCursor(46, 63);
oled.print( F("+") );
oled.setCursor(80, 63);
oled.print( F("-") );
oled.setCursor(110, 63);
oled.print( F("Next") );
oled.setColorIndex(1);
}
void set_dt_page(arguments& args) {
//Draw a box around the quantity you are changing: TEMPERATURE
oled.setFont(u8g2_font_8x13_tr);
oled.setCursor(10, 20);
oled.print( F("T:") );
oled.setCursor(31, 20);
oled.print(args.t_set, 0);
oled.drawBox(8, 29, 45, 13);
oled.setColorIndex(0);
oled.setCursor(10, 40);
oled.print( F("dT:") );
oled.setCursor(35, 40);
oled.print(args.dt, 0);
oled.setColorIndex(1);
oled.setCursor(70, 20);
oled.print( F("H:") );
oled.setCursor(70, 40);
oled.print( F("dH:") );
oled.setCursor(91, 20);
oled.print(args.h_set, 0);
oled.setCursor(95, 40);
oled.print(args.dh, 0);
// Screen selection white bar
oled.drawBox(0, 57, 130, 7);
oled.setCursor(3, 63);
oled.setFont(u8g2_font_4x6_tr);
oled.setColorIndex(0);
oled.print( F("Home" ) );
oled.setCursor(46, 63);
oled.print( F("+") );
oled.setCursor(80, 63);
oled.print( F("-") );
oled.setCursor(110, 63);
oled.print( F("Next") );
oled.setColorIndex(1);
}
void set_hum_page(arguments& args) {
//Draw a box around the quantity you are changing: TEMPERATURE
oled.setFont(u8g2_font_8x13_tr);
oled.setCursor(10, 20);
oled.print( F("T:") );
oled.setCursor(31, 20);
oled.print(args.t_set, 0);
oled.setCursor(10, 40);
oled.print( F("dT:") );
oled.setCursor(35, 40);
oled.print(args.dt, 0);
oled.drawBox(68, 9, 45, 13);
oled.setColorIndex(0);
oled.setCursor(70, 20);
oled.print( F("H:") );
oled.setCursor(91, 20);
oled.print(args.h_set, 0);
oled.setColorIndex(1);
oled.setCursor(70, 40);
oled.print( F("dH:") );
oled.setCursor(95, 40);
oled.print(args.dh, 0);
// Screen selection white bar
oled.drawBox(0, 57, 130, 7);
oled.setCursor(3, 63);
oled.setFont(u8g2_font_4x6_tr);
oled.setColorIndex(0);
oled.print( F("Home") );
oled.setCursor(46, 63);
oled.print( F("+") );
oled.setCursor(80, 63);
oled.print( F("-") );
oled.setCursor(110, 63);
oled.print( F("Next") );
oled.setColorIndex(1);
}
void set_dh_page(arguments& args) {
//Draw a box around the quantity you are changing: Delta_Humidity
oled.setFont(u8g2_font_8x13_tr);
oled.setCursor(10, 20);
oled.print( F("T:") );
oled.setCursor(31, 20);
oled.print(args.t_set, 0);
oled.setCursor(10, 40);
oled.print( F("dT:") );
oled.setCursor(35, 40);
oled.print(args.dt, 0);
oled.setCursor(70, 20);
oled.print( F("H:") );
oled.setCursor(91, 20);
oled.print(args.h_set, 0);
oled.drawBox(68, 29, 45, 13);
oled.setColorIndex(0);
oled.setCursor(70, 40);
oled.print( F("dH:") );
oled.setCursor(95, 40);
oled.print(args.dh, 0);
oled.setColorIndex(1);
// Screen selection white bar
oled.drawBox(0, 57, 130, 7);
oled.setCursor(3, 63);
oled.setFont(u8g2_font_4x6_tr);
oled.setColorIndex(0);
oled.print( F("Home") );
oled.setCursor(46, 63);
oled.print( F("+") );
oled.setCursor(80, 63);
oled.print( F("-") );
oled.setCursor(110, 63);
oled.print( F("Exit") );
oled.setColorIndex(1);
}
// TEMPERATURE control with REFRIGERATOR and LIGHT BULB
void keep_T_const(float t_current, float temp, float dt) {
if ( t_current > temp + dt && cool_state == 0) {
cool_control(ON);
cool_state = 1;
} else if ( t_current < temp && cool_state == 1) {
cool_control(OFF);
cool_state = 0;
} else if ( t_current < temp - dt && heat_state == 0) {
heat_control(ON);
heat_state = 1;
} else if ( t_current > temp && heat_state == 1) {
heat_control(OFF);
heat_state = 0;
} else {
//Nothing to do
}
}
// HUMIDITY control with HUMIDIFIER and DEHUMIDIFIER
void keep_H_const(float h_current, float hum, float dh) {
if ( h_current > hum + dh && deu_state == 0) {
deu_control(ON);
deu_state = 1;
} else if ( h_current < hum && deu_state == 1) {
deu_control(OFF);
deu_state = 0;
} else if ( h_current < hum - dh && umi_state == 0) {
umi_control(ON);
umi_state = 1;
} else if ( h_current > hum && umi_state == 1) {
umi_control(OFF);
umi_state = 0;
} else {
//Nothing to do
}
}
// Definition of the 4 functions for the device control: used to turn them ON or OFF.
void umi_control(bool command) {
if (command == ON) {
// Turn ON the humidifier AC outlet
digitalWrite(RELAY_AC_UMI, HIGH);
delay(500);
// Double short press on humidifier button to turn it ON (with continuous working flow)
digitalWrite(RELAY_BUTTON_UMI, HIGH);
delay(200);
digitalWrite(RELAY_BUTTON_UMI, LOW);
delay(200);
digitalWrite(RELAY_BUTTON_UMI, HIGH);
delay(200);
digitalWrite(RELAY_BUTTON_UMI, LOW);
} else if (command == OFF) {
// Long press on humidifier button to turn it OFF
digitalWrite(RELAY_BUTTON_UMI, HIGH);
delay(2100);
digitalWrite(RELAY_BUTTON_UMI, LOW);
delay(200);
// Turn OFF the humidifier AC outlet
digitalWrite(RELAY_AC_UMI, LOW);
} else {
return;
}
}
void cool_control(bool command) {
if (command == ON) {
// Turn ON the refrigerator AC outlet
digitalWrite(RELAY_AC_COOL, HIGH);
} else if (command == OFF) {
// Turn OFF the refrigerator AC outlet
digitalWrite(RELAY_AC_COOL, LOW);
} else {
return;
}
}
void deu_control(bool command) {
if (command == ON) {
// Turn ON the dehumidifier AC outlet
digitalWrite(RELAY_AC_DEU, HIGH);
} else if (command == OFF) {
// Turn OFF the dehumidifier AC outlet
digitalWrite(RELAY_AC_DEU, LOW);
} else {
return;
}
}
void heat_control(bool command) {
if (command == ON) {
// Turn ON the heater AC outlet
digitalWrite(RELAY_AC_HEAT, HIGH);
} else if (command == OFF) {
// Turn OFF the heater AC outlet
digitalWrite(RELAY_AC_HEAT, LOW);
} else {
return;
}
}
If you want fancy graphics on a OLED-display use a microcontroller with more memory.
For example an Seeeduino XIAO SAMD21 has 32kB of RAM compared to 2 kB of an arduino nano
Can be programmed with Arduino-IDE just like a nano
or use a ESP32-board with even more RAM and WiFi on board
which would enable to send data by UDP or TCP to log the data on a Raspberry Pi or a PC
for enough IO-pins use a MCP23017 I2C-io-expander
Thanks a lot. I'll probably move to an ESP32 board.
Using the Arduino Time library <TimeLib.h>. It won't be terribly accurate without an external time reference, and might lose or gain a minute or two a day.
Clearly you don't need any more pins to add an RTC, you are already using i2c for sensor & display.
Your code has many opportunities to reduce the size by removing repetitive parts using functions.
I think this should be perfectly possible with Nano. There is no need to upgrade.
I don't know how to reduce the size of my sketch any further. What would you suggest?
Look for groups of code lines that are very similar and repeated often. Make a function with parameters to do what those lines do. Replace all the repeated code lines with calls to the function. Keep repeating the process until you can't find any more opportunities.
For example, these lines, or very similar lines, are repeated many, many times:
oled.setCursor(7, 14);
oled.print("T:");
Make a function to replace them:
void printText(int x, int y, char *t) {
oled.setCursor(x, y);
oled.print(t);
}
then replace the original lines with calls to the function:
printText(7, 14, "T:");
EDIT: I see there already exists a function to do the above, which you are already using elsewhere: drawStr().
I suspect this one change will reduce your code by many lines. There are many similar changes you can make.
If you can see some repetition but can't figure out how to make a function to remove it, ask on this topic.
I've tried to do these things and now the code is lighter. But with the Rtc clock now it's using 96% of memory of Arduino Nano. What can I do to reduce the size even more? Maybe create custom fonts with just the characters i'm using.. how can I do that?
Now my code looks like this:
#include <Wire.h> /* I2C Library */
#include <BME280I2C.h> /* Temperature/Humidity sensor: Connect to SDA/SCL */
#include <U8g2lib.h> /* Oled display 128x64: Connect to SDA/SCL */
#include <EEPROM.h>
#include <ThreeWire.h>
#include <RtcDS1302.h>
#define ON true
#define OFF false
#define RELAY_BUTTON_UMI 2
#define RELAY_AC_DEU 12
#define RELAY_AC_COOL 11
#define RELAY_AC_HEAT 10
#define RELAY_AC_UMI 9
#define RELAY_FAN_EXT 8
#define BUTTON_1 3
#define BUTTON_2 4
#define BUTTON_3 5
#define BUTTON_4 6
#define DAT 4
#define CLK 5
#define RST 2
float p, t, h;
// Define 4 state variables for the devices: 0 = "OFF", 1 = "ON"
bool umi_state = 0;
bool deu_state = 0;
bool cool_state = 0;
bool heat_state = 0;
byte screen_state = 10;
// Set temperature and humidity: default values and deltas (They can be modified by the user, later on)
byte temp_set;
byte delta_t_set;
byte hum_set;
byte delta_h_set;
BME280I2C bme;
U8G2_SSD1306_128X64_NONAME_F_HW_I2C oled(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
ThreeWire myWire(DAT, CLK, RST); // DAT, CLK, RST pins
RtcDS1302<ThreeWire> Rtc(myWire);
struct arguments {
float t_current;
float h_current;
byte t_set;
byte h_set;
byte dt;
byte dh;
};
void setup() {
pinMode(RELAY_BUTTON_UMI, OUTPUT);
pinMode(RELAY_AC_UMI, OUTPUT);
pinMode(RELAY_AC_DEU, OUTPUT);
pinMode(RELAY_AC_COOL, OUTPUT);
pinMode(RELAY_AC_HEAT, OUTPUT);
pinMode(RELAY_FAN_EXT, OUTPUT);
pinMode(BUTTON_1, INPUT);
pinMode(BUTTON_2, INPUT);
pinMode(BUTTON_3, INPUT);
pinMode(BUTTON_4, INPUT);
digitalWrite(RELAY_BUTTON_UMI, LOW);
digitalWrite(RELAY_AC_UMI, LOW);
digitalWrite(RELAY_AC_DEU, LOW);
digitalWrite(RELAY_AC_COOL, LOW);
digitalWrite(RELAY_AC_HEAT, LOW);
digitalWrite(RELAY_FAN_EXT, LOW);
oled.begin();
bme.begin();
Rtc.Begin();
RtcDateTime currentTime = RtcDateTime(__DATE__, __TIME__);
Rtc.SetDateTime(currentTime);
EEPROM.get(0, temp_set);
EEPROM.get(2, delta_t_set);
EEPROM.get(4, hum_set);
EEPROM.get(6, delta_h_set);
delay(500);
}
void loop() {
RtcDateTime now = Rtc.GetDateTime();
ventilation(now, 10, 20, 30); // From 10.20 to 10.30 the fan is turned ON for ventilation
//Read (pressure), temperature and humidity with BME280 sensor and write them in p, t, h global variables
bme.read(p, t, h);
arguments func_args = {t, h, temp_set, hum_set, delta_t_set, delta_h_set};
bool button_state_1 = digitalRead(BUTTON_1); // Set T/H e SET
bool button_state_2 = digitalRead(BUTTON_2); // -
bool button_state_3 = digitalRead(BUTTON_3); // +
bool button_state_4 = digitalRead(BUTTON_4); // Info e Home
screen_state = state_selection(button_state_1, button_state_2, button_state_3, button_state_4, screen_state); // I use the function <state_selection> to select the value of screen_state
switch (screen_state) {
case 10:
default_page(func_args, screen_state);
break;
case 20:
set_parameter_page(func_args, screen_state);
if (button_state_2 == HIGH) {
temp_set -= 1;
EEPROM.put(0, temp_set);
} else if (button_state_3 == HIGH){
temp_set += 1;
EEPROM.put(0, temp_set);
}
break;
case 30:
info_page(func_args, screen_state);
break;
case 21:
set_parameter_page(func_args, screen_state);
if (button_state_2 == HIGH) {
delta_t_set -= 1;
EEPROM.put(2, delta_t_set);
} else if (button_state_3 == HIGH){
delta_t_set += 1;
EEPROM.put(2, delta_t_set);
}
break;
case 22:
set_parameter_page(func_args, screen_state);
if (button_state_2 == HIGH) {
hum_set -= 1;
EEPROM.put(4, hum_set);
} else if (button_state_3 == HIGH){
hum_set += 1;
EEPROM.put(4, hum_set);
}
break;
case 23:
set_parameter_page(func_args, screen_state);
if (button_state_2 == HIGH) {
delta_h_set -= 1;
EEPROM.put(6, delta_h_set);
} else if (button_state_3 == HIGH){
delta_h_set += 1;
EEPROM.put(6, delta_h_set);
}
break;
}
// Call for the functions that are keeping T and H constant.
keep_T_const(t, temp_set, delta_t_set);
keep_H_const(h, hum_set, delta_h_set);
delay(300);
}
// ______________________________________________ Functions __________________________________________________
int state_selection(bool button_state_1, bool button_state_2, bool button_state_3, bool button_state_4, byte screen_state){
if (button_state_1 == HIGH && screen_state == 10){ // If you are on HOME and you press the first button, you go to T/H selection page.
screen_state = 20;
} else if (button_state_4 == HIGH && screen_state == 10){ // Homepage to Info
screen_state = 30;
} else if (button_state_4 == HIGH && screen_state == 20){
screen_state = 21;
} else if (button_state_4 == HIGH && screen_state == 21) {
screen_state = 22;
} else if (button_state_4 == HIGH && screen_state == 22) {
screen_state = 23;
} else if (button_state_4 == HIGH && screen_state == 23) {
screen_state = 10;
} else if (button_state_1 == HIGH && (screen_state == 20 || screen_state == 21 || screen_state == 22 || screen_state == 23 || screen_state == 30)) {
screen_state = 10;
}
return screen_state;
}
// OLED menu drawing functions:
void default_page(arguments& args, byte screen_state) {
oled.clearBuffer();
oled.setFont(u8g2_font_8x13_tf);
oled.drawStr(7, 14, "T:");
oled.drawStr(7, 30, "H:");
// Print current temperature and humidity values (showing 1 decimal).
oled.setCursor(25, 14);
oled.print(args.t_current, 1);
oled.print(char(176));
oled.print(F("C"));
oled.setCursor(25, 30);
oled.print(args.h_current, 1);
oled.print(F("%"));
// Write T+ T- H+ H- on top of humi/deum cooler/heater state boxes
oled.setFont(u8g2_font_6x10_tr);
oled.drawStr(92, 8, "T+");
oled.drawStr(112, 8, "T-");
oled.drawStr(92, 37, "H+");
oled.drawStr(112, 37, "H-");
// Write T_set, H_set, dt, dh values
oled.setCursor(7, 43);
oled.print( F("T") );
oled.setFont(u8g2_font_4x6_tr);
oled.print( F("set") );
oled.setFont(u8g2_font_6x10_tf);
oled.print( F(": ") );
oled.print(args.t_set, 0);
oled.print(char(177));
oled.print(args.dt, 0);
oled.print(char(176));
oled.print(F("C") );
oled.setCursor(7, 54);
oled.print( F("H") );
oled.setFont(u8g2_font_4x6_tr);
oled.print( F("set") );
oled.setFont(u8g2_font_6x10_tf);
oled.print( F(": ") );
oled.print(args.h_set, 0);
oled.print(char(177));
oled.print(args.dh, 0);
oled.print( F("%") );
// Screen selection white bar + vertical line
oled.drawLine(83, 0, 83, 70);
drawBar(screen_state);
drawStateBox(89, 11, heat_state, "T+");
drawStateBox(109, 11, cool_state, "T-");
drawStateBox(89, 40, umi_state, "H+");
drawStateBox(109, 40, deu_state, "H-");
oled.sendBuffer();
}
void info_page(arguments& args, byte screen_state) {
oled.clearBuffer();
oled.setFont(u8g2_font_4x6_tr);
oled.setCursor(1, 8);
oled.print( F("Fase: Stagionatura") );
oled.setCursor(1, 17);
oled.print( F("Prodotto: Salame") );
oled.setCursor(1, 26);
oled.print( F("Inizio: 24/4/24") );
oled.setCursor(1, 35);
oled.print( F("Temperatura:") );
oled.print(args.t_set, 0);
oled.setCursor(80, 35);
oled.print( F("Delta:") );
oled.setCursor(110, 35);
oled.print(args.dt, 0);
oled.setCursor(1, 44);
oled.print(F("Umidita':") );
oled.print(args.h_set, 0);
oled.setCursor(80, 44);
oled.print( F("Delta:") );
oled.setCursor(110, 44);
oled.print(args.dh, 0);
drawBar(screen_state);
oled.sendBuffer();
}
void set_parameter_page(arguments& args, byte screen_state) {
oled.clearBuffer();
oled.setFont(u8g2_font_8x13_tr);
switch (screen_state) {
case 20:
oled.drawBox(8, 9, 45, 13);
oled.setColorIndex(0);
oled.drawStr(10, 20, "T:");
oled.setCursor(31, 20);
oled.print(args.t_set, 0);
oled.setColorIndex(1);
oled.drawStr(10, 40, "dT:");
oled.drawStr(70, 20, "H:");
oled.drawStr(70, 40, "dH:");
oled.setCursor(35, 40);
oled.print(args.dt, 0);
oled.setCursor(91, 20);
oled.print(args.h_set, 0);
oled.setCursor(95, 40);
oled.print(args.dh, 0);
break;
case 21:
oled.drawBox(8, 29, 45, 13);
oled.setColorIndex(0);
oled.drawStr(10, 40, "dT:");
oled.setCursor(35, 40);
oled.print(args.dt, 0);
oled.setColorIndex(1);
oled.drawStr(10, 20, "T:");
oled.drawStr(70, 20, "H:");
oled.drawStr(70, 40, "dH:");
oled.setCursor(31, 20);
oled.print(args.t_set, 0);
oled.setCursor(91, 20);
oled.print(args.h_set, 0);
oled.setCursor(95, 40);
oled.print(args.dh, 0);
break;
case 22:
oled.drawBox(68, 9, 45, 13);
oled.setColorIndex(0);
oled.drawStr(70, 20, "H:");
oled.setCursor(91, 20);
oled.print(args.h_set, 0);
oled.setColorIndex(1);
oled.drawStr(10, 40, "dT:");
oled.drawStr(10, 20, "T:");
oled.drawStr(70, 40, "dH:");
oled.setCursor(35, 40);
oled.print(args.dt, 0);
oled.setCursor(31, 20);
oled.print(args.t_set, 0);
oled.setCursor(95, 40);
oled.print(args.dh, 0);
break;
case 23:
oled.drawBox(68, 29, 45, 13);
oled.setColorIndex(0);
oled.drawStr(70, 40, "dH:");
oled.setCursor(95, 40);
oled.print(args.dh, 0);
oled.setColorIndex(1);
oled.drawStr(10, 40, "dT:");
oled.drawStr(70, 20, "H:");
oled.drawStr(10, 20, "T:");
oled.setCursor(35, 40);
oled.print(args.dt, 0);
oled.setCursor(91, 20);
oled.print(args.h_set, 0);
oled.setCursor(31, 20);
oled.print(args.t_set, 0);
break;
}
//Draw a box around the quantity you are changing: TEMPERATURE
drawBar(screen_state);
oled.sendBuffer();
}
void drawBar(byte screen_state){
oled.drawBox(0, 57, 130, 7);
oled.setCursor(3, 63);
oled.setFont(u8g2_font_4x6_tr);
oled.setColorIndex(0);
switch (screen_state) {
case 10:
oled.print( F("Set T/H") );
oled.setCursor(110, 63);
oled.print( F("Info") );
break;
case 20:
case 21:
case 22:{
oled.print(F("Home") );
oled.setCursor(46, 63);
oled.print( F("-") );
oled.setCursor(80, 63);
oled.print( F("+") );
oled.setCursor(110, 63);
oled.print( F("Next") );
break;
}
case 23:
oled.print(F("Home") );
oled.setCursor(46, 63);
oled.print( F("-") );
oled.setCursor(80, 63);
oled.print( F("+") );
oled.setCursor(110, 63);
oled.print( F("Exit") );
break;
case 30:
oled.print( F("Home") );
oled.setCursor(110, 63);
break;
}
oled.setColorIndex(1);
}
void drawStateBox(int x, int y, bool device_state, char* symbol ) {
oled.setFont(u8g2_font_4x6_tr);
char* OnStr = "On";
char* OffStr = "Off";
const unsigned int dim = 15;
if (device_state == 1) {
oled.drawBox(x, y, dim, dim); // H+ ON == WHITE BOX
oled.setColorIndex(0); // Imposta il colore a nero
oled.drawStr(x+4, y+10, OnStr); // Scrive il testo all'interno del rettangolo
oled.setColorIndex(1); // Imposta il colore a bianco
} else {
oled.drawFrame(x, y, dim, dim); // T+ ON == BLACK BOX
oled.drawStr(x+2, y+10, OffStr); // Scrive il testo all'interno del rettangolo
}
// Write T+ T- H+ H- on top of humi/deum cooler/heater state boxes
oled.setFont(u8g2_font_6x10_tr);
oled.drawStr(x+3, y-3, symbol);
}
// TEMPERATURE control with REFRIGERATOR and LIGHT BULB
void keep_T_const(float t_current, byte temp, byte dt) {
if( t_current > temp + dt && cool_state == 0){
cool_control(ON);
cool_state = 1;
} else if ( t_current < temp && cool_state == 1){
cool_control(OFF);
cool_state = 0;
} else if ( t_current < temp - dt && heat_state == 0){
heat_control(ON);
heat_state = 1;
} else if ( t_current > temp && heat_state == 1){
heat_control(OFF);
heat_state = 0;
} else {
//Nothing to do
}
}
// HUMIDITY control with HUMIDIFIER and DEHUMIDIFIER
void keep_H_const(float h_current, byte hum, byte dh) {
if( h_current > hum + dh && deu_state == 0){
deu_control(ON);
deu_state = 1;
} else if ( h_current < hum && deu_state == 1){
deu_control(OFF);
deu_state = 0;
} else if ( h_current < hum - dh && umi_state == 0){
umi_control(ON);
umi_state = 1;
} else if ( h_current > hum && umi_state == 1){
umi_control(OFF);
umi_state = 0;
} else {
//Nothing to do
}
}
void ventilation(RtcDateTime now, byte hour, byte min_start, byte min_stop){
if (now.Hour() == hour && (now.Minute() > min_start && now.Minute() < min_stop)) {
digitalWrite(RELAY_FAN_EXT, HIGH);
} else {
digitalWrite(RELAY_FAN_EXT, LOW);
}
}
// Definition of the 4 functions for the device control: used to turn them ON or OFF.
void umi_control(bool command) {
if (command == ON) {
// Turn ON the humidifier AC outlet
digitalWrite(RELAY_AC_UMI, HIGH);
delay(500);
// Double short press on humidifier button to turn it ON (with continuous working flow)
digitalWrite(RELAY_BUTTON_UMI, HIGH);
delay(200);
digitalWrite(RELAY_BUTTON_UMI, LOW);
delay(200);
digitalWrite(RELAY_BUTTON_UMI, HIGH);
delay(200);
digitalWrite(RELAY_BUTTON_UMI, LOW);
} else if (command == OFF) {
// Long press on humidifier button to turn it OFF
digitalWrite(RELAY_BUTTON_UMI, HIGH);
delay(2100);
digitalWrite(RELAY_BUTTON_UMI, LOW);
delay(200);
// Turn OFF the humidifier AC outlet
digitalWrite(RELAY_AC_UMI, LOW);
} else {
return;
}
}
void cool_control(bool command) {
if (command == ON) {
// Turn ON the refrigerator AC outlet
digitalWrite(RELAY_AC_COOL, HIGH);
} else if (command == OFF) {
// Turn OFF the refrigerator AC outlet
digitalWrite(RELAY_AC_COOL, LOW);
} else {
return;
}
}
void deu_control(bool command) {
if (command == ON) {
// Turn ON the dehumidifier AC outlet
digitalWrite(RELAY_AC_DEU, HIGH);
} else if (command == OFF) {
// Turn OFF the dehumidifier AC outlet
digitalWrite(RELAY_AC_DEU, LOW);
} else {
return;
}
}
void heat_control(bool command) {
if (command == ON) {
// Turn ON the heater AC outlet
digitalWrite(RELAY_AC_HEAT, HIGH);
} else if (command == OFF) {
// Turn OFF the heater AC outlet
digitalWrite(RELAY_AC_HEAT, LOW);
} else {
return;
}
}
You've made a good start, but I still see so much repetition, in almost every part of your code.
For example, these 3 functions are basically just repetition:
void cool_control(bool command) {
if (command == ON) {
// Turn ON the refrigerator AC outlet
digitalWrite(RELAY_AC_COOL, HIGH);
} else if (command == OFF) {
// Turn OFF the refrigerator AC outlet
digitalWrite(RELAY_AC_COOL, LOW);
} else {
return;
}
}
void deu_control(bool command) {
if (command == ON) {
// Turn ON the dehumidifier AC outlet
digitalWrite(RELAY_AC_DEU, HIGH);
} else if (command == OFF) {
// Turn OFF the dehumidifier AC outlet
digitalWrite(RELAY_AC_DEU, LOW);
} else {
return;
}
}
void heat_control(bool command) {
if (command == ON) {
// Turn ON the heater AC outlet
digitalWrite(RELAY_AC_HEAT, HIGH);
} else if (command == OFF) {
// Turn OFF the heater AC outlet
digitalWrite(RELAY_AC_HEAT, LOW);
} else {
return;
}
}
Another way to reduce the size of your code would be to use an RTC that shares the same library and pins as your temp sensor and OLED. Use a DS1307 or DS3231 instead of the DS1302. Then you would not need this library:
#include <ThreeWire.h>
and you would save these pins:
#define DAT 4
#define CLK 5
#define RST 2
This would save around 10% of Flash space:
Sketch uses 26642 bytes (86%) of program storage space. Maximum is 30720 bytes.
Global variables use 1699 bytes (82%) of dynamic memory, leaving 349 bytes for local variables. Maximum is 2048 bytes.
Low memory available, stability problems may occur.
The code would be (untested):
#include <Wire.h> /* I2C Library */
#include <BME280I2C.h> /* Temperature/Humidity sensor: Connect to SDA/SCL */
#include <U8g2lib.h> /* Oled display 128x64: Connect to SDA/SCL */
#include <EEPROM.h>
//#include <ThreeWire.h>
//#include <RtcDS1302.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
#define ON true
#define OFF false
#define RELAY_BUTTON_UMI 2
#define RELAY_AC_DEU 12
#define RELAY_AC_COOL 11
#define RELAY_AC_HEAT 10
#define RELAY_AC_UMI 9
#define RELAY_FAN_EXT 8
#define BUTTON_1 3
#define BUTTON_2 4
#define BUTTON_3 5
#define BUTTON_4 6
//#define DAT 4
//#define CLK 5
//#define RST 2
float p, t, h;
// Define 4 state variables for the devices: 0 = "OFF", 1 = "ON"
bool umi_state = 0;
bool deu_state = 0;
bool cool_state = 0;
bool heat_state = 0;
byte screen_state = 10;
// Set temperature and humidity: default values and deltas (They can be modified by the user, later on)
byte temp_set;
byte delta_t_set;
byte hum_set;
byte delta_h_set;
BME280I2C bme;
U8G2_SSD1306_128X64_NONAME_F_HW_I2C oled(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
//ThreeWire myWire(DAT, CLK, RST); // DAT, CLK, RST pins
//RtcDS1302<ThreeWire> Rtc(myWire);
struct arguments {
float t_current;
float h_current;
byte t_set;
byte h_set;
byte dt;
byte dh;
};
void setup() {
pinMode(RELAY_BUTTON_UMI, OUTPUT);
pinMode(RELAY_AC_UMI, OUTPUT);
pinMode(RELAY_AC_DEU, OUTPUT);
pinMode(RELAY_AC_COOL, OUTPUT);
pinMode(RELAY_AC_HEAT, OUTPUT);
pinMode(RELAY_FAN_EXT, OUTPUT);
pinMode(BUTTON_1, INPUT);
pinMode(BUTTON_2, INPUT);
pinMode(BUTTON_3, INPUT);
pinMode(BUTTON_4, INPUT);
digitalWrite(RELAY_BUTTON_UMI, LOW);
digitalWrite(RELAY_AC_UMI, LOW);
digitalWrite(RELAY_AC_DEU, LOW);
digitalWrite(RELAY_AC_COOL, LOW);
digitalWrite(RELAY_AC_HEAT, LOW);
digitalWrite(RELAY_FAN_EXT, LOW);
oled.begin();
bme.begin();
//Rtc.Begin();
//RtcDateTime currentTime = RtcDateTime(__DATE__, __TIME__);
//Rtc.SetDateTime(currentTime);
tmElements_t tm;
tm.Year = 2024;
tm.Month = 3;
tm.Day = 15;
tm.Hour = 12;
tm.Minute = 30;
tm.Second = 0;
RTC.write(tm);
EEPROM.get(0, temp_set);
EEPROM.get(2, delta_t_set);
EEPROM.get(4, hum_set);
EEPROM.get(6, delta_h_set);
delay(500);
}
void loop() {
//RtcDateTime now = Rtc.GetDateTime();
tmElements_t tm;
RTC.read(tm);
ventilation(tm, 10, 20, 30); // From 10.20 to 10.30 the fan is turned ON for ventilation
//Read (pressure), temperature and humidity with BME280 sensor and write them in p, t, h global variables
bme.read(p, t, h);
arguments func_args = {t, h, temp_set, hum_set, delta_t_set, delta_h_set};
bool button_state_1 = digitalRead(BUTTON_1); // Set T/H e SET
bool button_state_2 = digitalRead(BUTTON_2); // -
bool button_state_3 = digitalRead(BUTTON_3); // +
bool button_state_4 = digitalRead(BUTTON_4); // Info e Home
screen_state = state_selection(button_state_1, button_state_2, button_state_3, button_state_4, screen_state); // I use the function <state_selection> to select the value of screen_state
switch (screen_state) {
case 10:
default_page(func_args, screen_state);
break;
case 20:
set_parameter_page(func_args, screen_state);
if (button_state_2 == HIGH) {
temp_set -= 1;
EEPROM.put(0, temp_set);
} else if (button_state_3 == HIGH){
temp_set += 1;
EEPROM.put(0, temp_set);
}
break;
case 30:
info_page(func_args, screen_state);
break;
case 21:
set_parameter_page(func_args, screen_state);
if (button_state_2 == HIGH) {
delta_t_set -= 1;
EEPROM.put(2, delta_t_set);
} else if (button_state_3 == HIGH){
delta_t_set += 1;
EEPROM.put(2, delta_t_set);
}
break;
case 22:
set_parameter_page(func_args, screen_state);
if (button_state_2 == HIGH) {
hum_set -= 1;
EEPROM.put(4, hum_set);
} else if (button_state_3 == HIGH){
hum_set += 1;
EEPROM.put(4, hum_set);
}
break;
case 23:
set_parameter_page(func_args, screen_state);
if (button_state_2 == HIGH) {
delta_h_set -= 1;
EEPROM.put(6, delta_h_set);
} else if (button_state_3 == HIGH){
delta_h_set += 1;
EEPROM.put(6, delta_h_set);
}
break;
}
// Call for the functions that are keeping T and H constant.
keep_T_const(t, temp_set, delta_t_set);
keep_H_const(h, hum_set, delta_h_set);
delay(300);
}
// ______________________________________________ Functions __________________________________________________
int state_selection(bool button_state_1, bool button_state_2, bool button_state_3, bool button_state_4, byte screen_state){
if (button_state_1 == HIGH && screen_state == 10){ // If you are on HOME and you press the first button, you go to T/H selection page.
screen_state = 20;
} else if (button_state_4 == HIGH && screen_state == 10){ // Homepage to Info
screen_state = 30;
} else if (button_state_4 == HIGH && screen_state == 20){
screen_state = 21;
} else if (button_state_4 == HIGH && screen_state == 21) {
screen_state = 22;
} else if (button_state_4 == HIGH && screen_state == 22) {
screen_state = 23;
} else if (button_state_4 == HIGH && screen_state == 23) {
screen_state = 10;
} else if (button_state_1 == HIGH && (screen_state == 20 || screen_state == 21 || screen_state == 22 || screen_state == 23 || screen_state == 30)) {
screen_state = 10;
}
return screen_state;
}
// OLED menu drawing functions:
void default_page(arguments& args, byte screen_state) {
oled.clearBuffer();
oled.setFont(u8g2_font_8x13_tf);
oled.drawStr(7, 14, "T:");
oled.drawStr(7, 30, "H:");
// Print current temperature and humidity values (showing 1 decimal).
oled.setCursor(25, 14);
oled.print(args.t_current, 1);
oled.print(char(176));
oled.print(F("C"));
oled.setCursor(25, 30);
oled.print(args.h_current, 1);
oled.print(F("%"));
// Write T+ T- H+ H- on top of humi/deum cooler/heater state boxes
oled.setFont(u8g2_font_6x10_tr);
oled.drawStr(92, 8, "T+");
oled.drawStr(112, 8, "T-");
oled.drawStr(92, 37, "H+");
oled.drawStr(112, 37, "H-");
// Write T_set, H_set, dt, dh values
oled.setCursor(7, 43);
oled.print( F("T") );
oled.setFont(u8g2_font_4x6_tr);
oled.print( F("set") );
oled.setFont(u8g2_font_6x10_tf);
oled.print( F(": ") );
oled.print(args.t_set, 0);
oled.print(char(177));
oled.print(args.dt, 0);
oled.print(char(176));
oled.print(F("C") );
oled.setCursor(7, 54);
oled.print( F("H") );
oled.setFont(u8g2_font_4x6_tr);
oled.print( F("set") );
oled.setFont(u8g2_font_6x10_tf);
oled.print( F(": ") );
oled.print(args.h_set, 0);
oled.print(char(177));
oled.print(args.dh, 0);
oled.print( F("%") );
// Screen selection white bar + vertical line
oled.drawLine(83, 0, 83, 70);
drawBar(screen_state);
drawStateBox(89, 11, heat_state, "T+");
drawStateBox(109, 11, cool_state, "T-");
drawStateBox(89, 40, umi_state, "H+");
drawStateBox(109, 40, deu_state, "H-");
oled.sendBuffer();
}
void info_page(arguments& args, byte screen_state) {
oled.clearBuffer();
oled.setFont(u8g2_font_4x6_tr);
oled.setCursor(1, 8);
oled.print( F("Fase: Stagionatura") );
oled.setCursor(1, 17);
oled.print( F("Prodotto: Salame") );
oled.setCursor(1, 26);
oled.print( F("Inizio: 24/4/24") );
oled.setCursor(1, 35);
oled.print( F("Temperatura:") );
oled.print(args.t_set, 0);
oled.setCursor(80, 35);
oled.print( F("Delta:") );
oled.setCursor(110, 35);
oled.print(args.dt, 0);
oled.setCursor(1, 44);
oled.print(F("Umidita':") );
oled.print(args.h_set, 0);
oled.setCursor(80, 44);
oled.print( F("Delta:") );
oled.setCursor(110, 44);
oled.print(args.dh, 0);
drawBar(screen_state);
oled.sendBuffer();
}
void set_parameter_page(arguments& args, byte screen_state) {
oled.clearBuffer();
oled.setFont(u8g2_font_8x13_tr);
switch (screen_state) {
case 20:
oled.drawBox(8, 9, 45, 13);
oled.setColorIndex(0);
oled.drawStr(10, 20, "T:");
oled.setCursor(31, 20);
oled.print(args.t_set, 0);
oled.setColorIndex(1);
oled.drawStr(10, 40, "dT:");
oled.drawStr(70, 20, "H:");
oled.drawStr(70, 40, "dH:");
oled.setCursor(35, 40);
oled.print(args.dt, 0);
oled.setCursor(91, 20);
oled.print(args.h_set, 0);
oled.setCursor(95, 40);
oled.print(args.dh, 0);
break;
case 21:
oled.drawBox(8, 29, 45, 13);
oled.setColorIndex(0);
oled.drawStr(10, 40, "dT:");
oled.setCursor(35, 40);
oled.print(args.dt, 0);
oled.setColorIndex(1);
oled.drawStr(10, 20, "T:");
oled.drawStr(70, 20, "H:");
oled.drawStr(70, 40, "dH:");
oled.setCursor(31, 20);
oled.print(args.t_set, 0);
oled.setCursor(91, 20);
oled.print(args.h_set, 0);
oled.setCursor(95, 40);
oled.print(args.dh, 0);
break;
case 22:
oled.drawBox(68, 9, 45, 13);
oled.setColorIndex(0);
oled.drawStr(70, 20, "H:");
oled.setCursor(91, 20);
oled.print(args.h_set, 0);
oled.setColorIndex(1);
oled.drawStr(10, 40, "dT:");
oled.drawStr(10, 20, "T:");
oled.drawStr(70, 40, "dH:");
oled.setCursor(35, 40);
oled.print(args.dt, 0);
oled.setCursor(31, 20);
oled.print(args.t_set, 0);
oled.setCursor(95, 40);
oled.print(args.dh, 0);
break;
case 23:
oled.drawBox(68, 29, 45, 13);
oled.setColorIndex(0);
oled.drawStr(70, 40, "dH:");
oled.setCursor(95, 40);
oled.print(args.dh, 0);
oled.setColorIndex(1);
oled.drawStr(10, 40, "dT:");
oled.drawStr(70, 20, "H:");
oled.drawStr(10, 20, "T:");
oled.setCursor(35, 40);
oled.print(args.dt, 0);
oled.setCursor(91, 20);
oled.print(args.h_set, 0);
oled.setCursor(31, 20);
oled.print(args.t_set, 0);
break;
}
//Draw a box around the quantity you are changing: TEMPERATURE
drawBar(screen_state);
oled.sendBuffer();
}
void drawBar(byte screen_state){
oled.drawBox(0, 57, 130, 7);
oled.setCursor(3, 63);
oled.setFont(u8g2_font_4x6_tr);
oled.setColorIndex(0);
switch (screen_state) {
case 10:
oled.print( F("Set T/H") );
oled.setCursor(110, 63);
oled.print( F("Info") );
break;
case 20:
case 21:
case 22:{
oled.print(F("Home") );
oled.setCursor(46, 63);
oled.print( F("-") );
oled.setCursor(80, 63);
oled.print( F("+") );
oled.setCursor(110, 63);
oled.print( F("Next") );
break;
}
case 23:
oled.print(F("Home") );
oled.setCursor(46, 63);
oled.print( F("-") );
oled.setCursor(80, 63);
oled.print( F("+") );
oled.setCursor(110, 63);
oled.print( F("Exit") );
break;
case 30:
oled.print( F("Home") );
oled.setCursor(110, 63);
break;
}
oled.setColorIndex(1);
}
void drawStateBox(int x, int y, bool device_state, char* symbol ) {
oled.setFont(u8g2_font_4x6_tr);
char* OnStr = "On";
char* OffStr = "Off";
const unsigned int dim = 15;
if (device_state == 1) {
oled.drawBox(x, y, dim, dim); // H+ ON == WHITE BOX
oled.setColorIndex(0); // Imposta il colore a nero
oled.drawStr(x+4, y+10, OnStr); // Scrive il testo all'interno del rettangolo
oled.setColorIndex(1); // Imposta il colore a bianco
} else {
oled.drawFrame(x, y, dim, dim); // T+ ON == BLACK BOX
oled.drawStr(x+2, y+10, OffStr); // Scrive il testo all'interno del rettangolo
}
// Write T+ T- H+ H- on top of humi/deum cooler/heater state boxes
oled.setFont(u8g2_font_6x10_tr);
oled.drawStr(x+3, y-3, symbol);
}
// TEMPERATURE control with REFRIGERATOR and LIGHT BULB
void keep_T_const(float t_current, byte temp, byte dt) {
if( t_current > temp + dt && cool_state == 0){
cool_control(ON);
cool_state = 1;
} else if ( t_current < temp && cool_state == 1){
cool_control(OFF);
cool_state = 0;
} else if ( t_current < temp - dt && heat_state == 0){
heat_control(ON);
heat_state = 1;
} else if ( t_current > temp && heat_state == 1){
heat_control(OFF);
heat_state = 0;
} else {
//Nothing to do
}
}
// HUMIDITY control with HUMIDIFIER and DEHUMIDIFIER
void keep_H_const(float h_current, byte hum, byte dh) {
if( h_current > hum + dh && deu_state == 0){
deu_control(ON);
deu_state = 1;
} else if ( h_current < hum && deu_state == 1){
deu_control(OFF);
deu_state = 0;
} else if ( h_current < hum - dh && umi_state == 0){
umi_control(ON);
umi_state = 1;
} else if ( h_current > hum && umi_state == 1){
umi_control(OFF);
umi_state = 0;
} else {
//Nothing to do
}
}
void ventilation(tmElements_t now, byte hour, byte min_start, byte min_stop){
if (now.Hour == hour && (now.Minute > min_start && now.Minute < min_stop)) {
digitalWrite(RELAY_FAN_EXT, HIGH);
} else {
digitalWrite(RELAY_FAN_EXT, LOW);
}
}
// Definition of the 4 functions for the device control: used to turn them ON or OFF.
void umi_control(bool command) {
if (command == ON) {
// Turn ON the humidifier AC outlet
digitalWrite(RELAY_AC_UMI, HIGH);
delay(500);
// Double short press on humidifier button to turn it ON (with continuous working flow)
digitalWrite(RELAY_BUTTON_UMI, HIGH);
delay(200);
digitalWrite(RELAY_BUTTON_UMI, LOW);
delay(200);
digitalWrite(RELAY_BUTTON_UMI, HIGH);
delay(200);
digitalWrite(RELAY_BUTTON_UMI, LOW);
} else if (command == OFF) {
// Long press on humidifier button to turn it OFF
digitalWrite(RELAY_BUTTON_UMI, HIGH);
delay(2100);
digitalWrite(RELAY_BUTTON_UMI, LOW);
delay(200);
// Turn OFF the humidifier AC outlet
digitalWrite(RELAY_AC_UMI, LOW);
} else {
return;
}
}
void cool_control(bool command) {
if (command == ON) {
// Turn ON the refrigerator AC outlet
digitalWrite(RELAY_AC_COOL, HIGH);
} else if (command == OFF) {
// Turn OFF the refrigerator AC outlet
digitalWrite(RELAY_AC_COOL, LOW);
} else {
return;
}
}
void deu_control(bool command) {
if (command == ON) {
// Turn ON the dehumidifier AC outlet
digitalWrite(RELAY_AC_DEU, HIGH);
} else if (command == OFF) {
// Turn OFF the dehumidifier AC outlet
digitalWrite(RELAY_AC_DEU, LOW);
} else {
return;
}
}
void heat_control(bool command) {
if (command == ON) {
// Turn ON the heater AC outlet
digitalWrite(RELAY_AC_HEAT, HIGH);
} else if (command == OFF) {
// Turn OFF the heater AC outlet
digitalWrite(RELAY_AC_HEAT, LOW);
} else {
return;
}
}
For reference:
My reference? Thanks, but wouldn't @nico_fiaba benefit more? ![]()
Just showing what OP has tried to this point.
That was about one year ago. Up to now the project is almost finished and it changed in many ways. Those were just the first steps. This is the project up to now:
This will reduce the dynamic memory used, taking it below the danger zone:
#include <Wire.h> /* I2C Library */
#include <BME280I2C.h> /* Temperature/Humidity sensor: Connect to SDA/SCL */
#include <U8g2lib.h> /* Oled display 128x64: Connect to SDA/SCL */
#include <EEPROM.h>
#include <ThreeWire.h>
#include <RtcDS1302.h>
#define ON true
#define OFF false
#define RELAY_BUTTON_UMI 2
#define RELAY_AC_DEU 12
#define RELAY_AC_COOL 11
#define RELAY_AC_HEAT 10
#define RELAY_AC_UMI 9
#define RELAY_FAN_EXT 8
#define BUTTON_1 3
#define BUTTON_2 4
#define BUTTON_3 5
#define BUTTON_4 6
#define DAT 4
#define CLK 5
#define RST 2
float p, t, h;
// Define 4 state variables for the devices: 0 = "OFF", 1 = "ON"
bool umi_state = 0;
bool deu_state = 0;
bool cool_state = 0;
bool heat_state = 0;
byte screen_state = 10;
// Set temperature and humidity: default values and deltas (They can be modified by the user, later on)
byte temp_set;
byte delta_t_set;
byte hum_set;
byte delta_h_set;
BME280I2C bme;
//U8G2_SSD1306_128X64_NONAME_F_HW_I2C oled(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
U8G2_SSD1306_128X64_NONAME_1_HW_I2C oled(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
ThreeWire myWire(DAT, CLK, RST); // DAT, CLK, RST pins
RtcDS1302<ThreeWire> Rtc(myWire);
struct arguments {
float t_current;
float h_current;
byte t_set;
byte h_set;
byte dt;
byte dh;
};
void setup() {
pinMode(RELAY_BUTTON_UMI, OUTPUT);
pinMode(RELAY_AC_UMI, OUTPUT);
pinMode(RELAY_AC_DEU, OUTPUT);
pinMode(RELAY_AC_COOL, OUTPUT);
pinMode(RELAY_AC_HEAT, OUTPUT);
pinMode(RELAY_FAN_EXT, OUTPUT);
pinMode(BUTTON_1, INPUT);
pinMode(BUTTON_2, INPUT);
pinMode(BUTTON_3, INPUT);
pinMode(BUTTON_4, INPUT);
digitalWrite(RELAY_BUTTON_UMI, LOW);
digitalWrite(RELAY_AC_UMI, LOW);
digitalWrite(RELAY_AC_DEU, LOW);
digitalWrite(RELAY_AC_COOL, LOW);
digitalWrite(RELAY_AC_HEAT, LOW);
digitalWrite(RELAY_FAN_EXT, LOW);
oled.begin();
bme.begin();
Rtc.Begin();
RtcDateTime currentTime = RtcDateTime(__DATE__, __TIME__);
Rtc.SetDateTime(currentTime);
EEPROM.get(0, temp_set);
EEPROM.get(2, delta_t_set);
EEPROM.get(4, hum_set);
EEPROM.get(6, delta_h_set);
delay(500);
}
void loop() {
RtcDateTime now = Rtc.GetDateTime();
ventilation(now, 10, 20, 30); // From 10.20 to 10.30 the fan is turned ON for ventilation
//Read (pressure), temperature and humidity with BME280 sensor and write them in p, t, h global variables
bme.read(p, t, h);
arguments func_args = {t, h, temp_set, hum_set, delta_t_set, delta_h_set};
bool button_state_1 = digitalRead(BUTTON_1); // Set T/H e SET
bool button_state_2 = digitalRead(BUTTON_2); // -
bool button_state_3 = digitalRead(BUTTON_3); // +
bool button_state_4 = digitalRead(BUTTON_4); // Info e Home
screen_state = state_selection(button_state_1, button_state_2, button_state_3, button_state_4, screen_state); // I use the function <state_selection> to select the value of screen_state
switch (screen_state) {
case 10:
default_page(func_args, screen_state);
break;
case 20:
set_parameter_page(func_args, screen_state);
if (button_state_2 == HIGH) {
temp_set -= 1;
EEPROM.put(0, temp_set);
} else if (button_state_3 == HIGH) {
temp_set += 1;
EEPROM.put(0, temp_set);
}
break;
case 30:
info_page(func_args, screen_state);
break;
case 21:
set_parameter_page(func_args, screen_state);
if (button_state_2 == HIGH) {
delta_t_set -= 1;
EEPROM.put(2, delta_t_set);
} else if (button_state_3 == HIGH) {
delta_t_set += 1;
EEPROM.put(2, delta_t_set);
}
break;
case 22:
set_parameter_page(func_args, screen_state);
if (button_state_2 == HIGH) {
hum_set -= 1;
EEPROM.put(4, hum_set);
} else if (button_state_3 == HIGH) {
hum_set += 1;
EEPROM.put(4, hum_set);
}
break;
case 23:
set_parameter_page(func_args, screen_state);
if (button_state_2 == HIGH) {
delta_h_set -= 1;
EEPROM.put(6, delta_h_set);
} else if (button_state_3 == HIGH) {
delta_h_set += 1;
EEPROM.put(6, delta_h_set);
}
break;
}
// Call for the functions that are keeping T and H constant.
keep_T_const(t, temp_set, delta_t_set);
keep_H_const(h, hum_set, delta_h_set);
delay(300);
}
// ______________________________________________ Functions __________________________________________________
int state_selection(bool button_state_1, bool button_state_2, bool button_state_3, bool button_state_4, byte screen_state) {
if (button_state_1 == HIGH && screen_state == 10) { // If you are on HOME and you press the first button, you go to T/H selection page.
screen_state = 20;
} else if (button_state_4 == HIGH && screen_state == 10) { // Homepage to Info
screen_state = 30;
} else if (button_state_4 == HIGH && screen_state == 20) {
screen_state = 21;
} else if (button_state_4 == HIGH && screen_state == 21) {
screen_state = 22;
} else if (button_state_4 == HIGH && screen_state == 22) {
screen_state = 23;
} else if (button_state_4 == HIGH && screen_state == 23) {
screen_state = 10;
} else if (button_state_1 == HIGH && (screen_state == 20 || screen_state == 21 || screen_state == 22 || screen_state == 23 || screen_state == 30)) {
screen_state = 10;
}
return screen_state;
}
// OLED menu drawing functions:
void default_page(arguments& args, byte screen_state) {
//oled.clearBuffer();
oled.firstPage();
do {
oled.setFont(u8g2_font_8x13_tf);
oled.drawStr(7, 14, "T:");
oled.drawStr(7, 30, "H:");
// Print current temperature and humidity values (showing 1 decimal).
oled.setCursor(25, 14);
oled.print(args.t_current, 1);
oled.print(char(176));
oled.print(F("C"));
oled.setCursor(25, 30);
oled.print(args.h_current, 1);
oled.print(F("%"));
// Write T+ T- H+ H- on top of humi/deum cooler/heater state boxes
oled.setFont(u8g2_font_6x10_tr);
oled.drawStr(92, 8, "T+");
oled.drawStr(112, 8, "T-");
oled.drawStr(92, 37, "H+");
oled.drawStr(112, 37, "H-");
// Write T_set, H_set, dt, dh values
oled.setCursor(7, 43);
oled.print( F("T") );
oled.setFont(u8g2_font_4x6_tr);
oled.print( F("set") );
oled.setFont(u8g2_font_6x10_tf);
oled.print( F(": ") );
oled.print(args.t_set, 0);
oled.print(char(177));
oled.print(args.dt, 0);
oled.print(char(176));
oled.print(F("C") );
oled.setCursor(7, 54);
oled.print( F("H") );
oled.setFont(u8g2_font_4x6_tr);
oled.print( F("set") );
oled.setFont(u8g2_font_6x10_tf);
oled.print( F(": ") );
oled.print(args.h_set, 0);
oled.print(char(177));
oled.print(args.dh, 0);
oled.print( F("%") );
// Screen selection white bar + vertical line
oled.drawLine(83, 0, 83, 70);
drawBar(screen_state);
drawStateBox(89, 11, heat_state, "T+");
drawStateBox(109, 11, cool_state, "T-");
drawStateBox(89, 40, umi_state, "H+");
drawStateBox(109, 40, deu_state, "H-");
//oled.sendBuffer();
} while ( oled.nextPage() );
}
void info_page(arguments& args, byte screen_state) {
//oled.clearBuffer();
oled.firstPage();
do {
oled.setFont(u8g2_font_4x6_tr);
oled.setCursor(1, 8);
oled.print( F("Fase: Stagionatura") );
oled.setCursor(1, 17);
oled.print( F("Prodotto: Salame") );
oled.setCursor(1, 26);
oled.print( F("Inizio: 24/4/24") );
oled.setCursor(1, 35);
oled.print( F("Temperatura:") );
oled.print(args.t_set, 0);
oled.setCursor(80, 35);
oled.print( F("Delta:") );
oled.setCursor(110, 35);
oled.print(args.dt, 0);
oled.setCursor(1, 44);
oled.print(F("Umidita':") );
oled.print(args.h_set, 0);
oled.setCursor(80, 44);
oled.print( F("Delta:") );
oled.setCursor(110, 44);
oled.print(args.dh, 0);
drawBar(screen_state);
//oled.sendBuffer();
} while ( oled.nextPage() );
}
void set_parameter_page(arguments& args, byte screen_state) {
//oled.clearBuffer();
oled.firstPage();
do {
oled.setFont(u8g2_font_8x13_tr);
switch (screen_state) {
case 20:
oled.drawBox(8, 9, 45, 13);
oled.setColorIndex(0);
oled.drawStr(10, 20, "T:");
oled.setCursor(31, 20);
oled.print(args.t_set, 0);
oled.setColorIndex(1);
oled.drawStr(10, 40, "dT:");
oled.drawStr(70, 20, "H:");
oled.drawStr(70, 40, "dH:");
oled.setCursor(35, 40);
oled.print(args.dt, 0);
oled.setCursor(91, 20);
oled.print(args.h_set, 0);
oled.setCursor(95, 40);
oled.print(args.dh, 0);
break;
case 21:
oled.drawBox(8, 29, 45, 13);
oled.setColorIndex(0);
oled.drawStr(10, 40, "dT:");
oled.setCursor(35, 40);
oled.print(args.dt, 0);
oled.setColorIndex(1);
oled.drawStr(10, 20, "T:");
oled.drawStr(70, 20, "H:");
oled.drawStr(70, 40, "dH:");
oled.setCursor(31, 20);
oled.print(args.t_set, 0);
oled.setCursor(91, 20);
oled.print(args.h_set, 0);
oled.setCursor(95, 40);
oled.print(args.dh, 0);
break;
case 22:
oled.drawBox(68, 9, 45, 13);
oled.setColorIndex(0);
oled.drawStr(70, 20, "H:");
oled.setCursor(91, 20);
oled.print(args.h_set, 0);
oled.setColorIndex(1);
oled.drawStr(10, 40, "dT:");
oled.drawStr(10, 20, "T:");
oled.drawStr(70, 40, "dH:");
oled.setCursor(35, 40);
oled.print(args.dt, 0);
oled.setCursor(31, 20);
oled.print(args.t_set, 0);
oled.setCursor(95, 40);
oled.print(args.dh, 0);
break;
case 23:
oled.drawBox(68, 29, 45, 13);
oled.setColorIndex(0);
oled.drawStr(70, 40, "dH:");
oled.setCursor(95, 40);
oled.print(args.dh, 0);
oled.setColorIndex(1);
oled.drawStr(10, 40, "dT:");
oled.drawStr(70, 20, "H:");
oled.drawStr(10, 20, "T:");
oled.setCursor(35, 40);
oled.print(args.dt, 0);
oled.setCursor(91, 20);
oled.print(args.h_set, 0);
oled.setCursor(31, 20);
oled.print(args.t_set, 0);
break;
}
//Draw a box around the quantity you are changing: TEMPERATURE
drawBar(screen_state);
//oled.sendBuffer();
} while ( oled.nextPage() );
}
void drawBar(byte screen_state) {
oled.drawBox(0, 57, 130, 7);
oled.setCursor(3, 63);
oled.setFont(u8g2_font_4x6_tr);
oled.setColorIndex(0);
switch (screen_state) {
case 10:
oled.print( F("Set T/H") );
oled.setCursor(110, 63);
oled.print( F("Info") );
break;
case 20:
case 21:
case 22: {
oled.print(F("Home") );
oled.setCursor(46, 63);
oled.print( F("-") );
oled.setCursor(80, 63);
oled.print( F("+") );
oled.setCursor(110, 63);
oled.print( F("Next") );
break;
}
case 23:
oled.print(F("Home") );
oled.setCursor(46, 63);
oled.print( F("-") );
oled.setCursor(80, 63);
oled.print( F("+") );
oled.setCursor(110, 63);
oled.print( F("Exit") );
break;
case 30:
oled.print( F("Home") );
oled.setCursor(110, 63);
break;
}
oled.setColorIndex(1);
}
void drawStateBox(int x, int y, bool device_state, char* symbol ) {
oled.setFont(u8g2_font_4x6_tr);
char* OnStr = "On";
char* OffStr = "Off";
const unsigned int dim = 15;
if (device_state == 1) {
oled.drawBox(x, y, dim, dim); // H+ ON == WHITE BOX
oled.setColorIndex(0); // Imposta il colore a nero
oled.drawStr(x + 4, y + 10, OnStr); // Scrive il testo all'interno del rettangolo
oled.setColorIndex(1); // Imposta il colore a bianco
} else {
oled.drawFrame(x, y, dim, dim); // T+ ON == BLACK BOX
oled.drawStr(x + 2, y + 10, OffStr); // Scrive il testo all'interno del rettangolo
}
// Write T+ T- H+ H- on top of humi/deum cooler/heater state boxes
oled.setFont(u8g2_font_6x10_tr);
oled.drawStr(x + 3, y - 3, symbol);
}
// TEMPERATURE control with REFRIGERATOR and LIGHT BULB
void keep_T_const(float t_current, byte temp, byte dt) {
if ( t_current > temp + dt && cool_state == 0) {
cool_control(ON);
cool_state = 1;
} else if ( t_current < temp && cool_state == 1) {
cool_control(OFF);
cool_state = 0;
} else if ( t_current < temp - dt && heat_state == 0) {
heat_control(ON);
heat_state = 1;
} else if ( t_current > temp && heat_state == 1) {
heat_control(OFF);
heat_state = 0;
} else {
//Nothing to do
}
}
// HUMIDITY control with HUMIDIFIER and DEHUMIDIFIER
void keep_H_const(float h_current, byte hum, byte dh) {
if ( h_current > hum + dh && deu_state == 0) {
deu_control(ON);
deu_state = 1;
} else if ( h_current < hum && deu_state == 1) {
deu_control(OFF);
deu_state = 0;
} else if ( h_current < hum - dh && umi_state == 0) {
umi_control(ON);
umi_state = 1;
} else if ( h_current > hum && umi_state == 1) {
umi_control(OFF);
umi_state = 0;
} else {
//Nothing to do
}
}
void ventilation(RtcDateTime now, byte hour, byte min_start, byte min_stop) {
if (now.Hour() == hour && (now.Minute() > min_start && now.Minute() < min_stop)) {
digitalWrite(RELAY_FAN_EXT, HIGH);
} else {
digitalWrite(RELAY_FAN_EXT, LOW);
}
}
// Definition of the 4 functions for the device control: used to turn them ON or OFF.
void umi_control(bool command) {
if (command == ON) {
// Turn ON the humidifier AC outlet
digitalWrite(RELAY_AC_UMI, HIGH);
delay(500);
// Double short press on humidifier button to turn it ON (with continuous working flow)
digitalWrite(RELAY_BUTTON_UMI, HIGH);
delay(200);
digitalWrite(RELAY_BUTTON_UMI, LOW);
delay(200);
digitalWrite(RELAY_BUTTON_UMI, HIGH);
delay(200);
digitalWrite(RELAY_BUTTON_UMI, LOW);
} else if (command == OFF) {
// Long press on humidifier button to turn it OFF
digitalWrite(RELAY_BUTTON_UMI, HIGH);
delay(2100);
digitalWrite(RELAY_BUTTON_UMI, LOW);
delay(200);
// Turn OFF the humidifier AC outlet
digitalWrite(RELAY_AC_UMI, LOW);
} else {
return;
}
}
void cool_control(bool command) {
if (command == ON) {
// Turn ON the refrigerator AC outlet
digitalWrite(RELAY_AC_COOL, HIGH);
} else if (command == OFF) {
// Turn OFF the refrigerator AC outlet
digitalWrite(RELAY_AC_COOL, LOW);
} else {
return;
}
}
void deu_control(bool command) {
if (command == ON) {
// Turn ON the dehumidifier AC outlet
digitalWrite(RELAY_AC_DEU, HIGH);
} else if (command == OFF) {
// Turn OFF the dehumidifier AC outlet
digitalWrite(RELAY_AC_DEU, LOW);
} else {
return;
}
}
void heat_control(bool command) {
if (command == ON) {
// Turn ON the heater AC outlet
digitalWrite(RELAY_AC_HEAT, HIGH);
} else if (command == OFF) {
// Turn OFF the heater AC outlet
digitalWrite(RELAY_AC_HEAT, LOW);
} else {
return;
}
}
However, Flash memory is still very high:
Sketch uses 29574 bytes (96%) of program storage space. Maximum is 30720 bytes.
Global variables use 838 bytes (40%) of dynamic memory, leaving 1210 bytes for local variables. Maximum is 2048 bytes.
One reason for the high Flash memory is that you are using 5 different fonts. This may be consuming 10~15% of the memory. Could you reduce the number of fonts?
These are the fonts your code uses:
u8g2_font_8x13_tr
u8g2_font_8x13_tf
u8g2_font_6x10_tr
u8g2_font_6x10_tf
u8g2_font_4x6_tr
I think the fonts ending "f" could take twice as much as the fonts ending in "r", and all the symbols in the "r" fonts are also in the "f" fonts, so perhaps you could replace u8g2_font_8x13_tr with u8g2_font_8x13_tf and u8g2_font_6x10_tr with u8g2_font_6x10_tf?




