#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <SPI.h>
#include <EEPROM.h>
// Variables for the rotary encoder
#define CLK 5
#define DT 3
#define SW 4
// 'cylinder', 32x7px
const unsigned char cylinder[] PROGMEM = {
0x00, 0x00, 0x00, 0x00,
0x3f, 0xff, 0xf1, 0xf8,
0x7f, 0xff, 0xf1, 0xfc,
0x7f, 0xff, 0xf1, 0xfe,
0x7f, 0xff, 0xf1, 0xfc,
0x3f, 0xff, 0xf1, 0xf8,
0x00, 0x00, 0x00, 0x00
};
//Ufrost logo, 128px x 23px
const unsigned char ulogo[] PROGMEM = {
0xe0, 0x00, 0x61, 0xff, 0xff, 0x3f, 0xfe, 0x00, 0x00, 0x54, 0x00, 0x03, 0xfe, 0x0f, 0xff, 0xff,
0xe0, 0x00, 0x61, 0xff, 0xff, 0x3f, 0xff, 0xc0, 0x02, 0xa5, 0x00, 0x0f, 0xff, 0x8f, 0xff, 0xff,
0xe0, 0x00, 0x61, 0xc0, 0x00, 0x3c, 0x03, 0xe0, 0x0a, 0x42, 0xa0, 0x1e, 0x03, 0xc0, 0x07, 0x00,
0xe0, 0x00, 0x61, 0xc0, 0x00, 0x3c, 0x00, 0xf0, 0x04, 0x10, 0x40, 0x3c, 0x00, 0xc0, 0x07, 0x00,
0xe0, 0x00, 0x61, 0xc0, 0x00, 0x3c, 0x00, 0xf0, 0x28, 0xfc, 0x28, 0x38, 0x00, 0x00, 0x07, 0x00,
0xe0, 0x00, 0x61, 0xc0, 0x00, 0x3c, 0x00, 0x78, 0x20, 0x7c, 0x08, 0x38, 0x00, 0x00, 0x07, 0x00,
0xe0, 0x00, 0x61, 0xc0, 0x00, 0x3c, 0x00, 0x78, 0x46, 0x10, 0x88, 0x3c, 0x00, 0x00, 0x07, 0x00,
0xe0, 0x00, 0x61, 0xc0, 0x00, 0x3c, 0x00, 0x78, 0x9e, 0xfe, 0xa4, 0x3e, 0x00, 0x00, 0x07, 0x00,
0xe0, 0x00, 0x61, 0xc0, 0x00, 0x3c, 0x00, 0xf0, 0x4e, 0xfe, 0xea, 0x3f, 0x80, 0x00, 0x07, 0x00,
0xe0, 0x00, 0x61, 0xc0, 0x00, 0x3c, 0x01, 0xf0, 0x8f, 0xff, 0xe2, 0x1f, 0xf0, 0x00, 0x07, 0x00,
0xe0, 0x00, 0x61, 0xc0, 0x00, 0x3f, 0xff, 0xe1, 0x59, 0xff, 0xe0, 0x0f, 0xfc, 0x00, 0x07, 0x00,
0xe0, 0x00, 0x61, 0xc0, 0x00, 0x3f, 0xff, 0x81, 0xc3, 0xff, 0x86, 0x03, 0xff, 0x00, 0x07, 0x00,
0xe0, 0x00, 0x61, 0xff, 0xf8, 0x3f, 0xfe, 0x01, 0xd3, 0xff, 0x26, 0x00, 0x7f, 0xc0, 0x07, 0x00,
0xe0, 0x00, 0x61, 0xff, 0xf8, 0x3c, 0x0f, 0x01, 0xcf, 0xff, 0x66, 0x00, 0x0f, 0xe0, 0x07, 0x00,
0xe0, 0x00, 0x61, 0xc0, 0x00, 0x3c, 0x07, 0x80, 0xce, 0xff, 0xce, 0x00, 0x03, 0xe0, 0x07, 0x00,
0xe0, 0x00, 0x61, 0xc0, 0x00, 0x3c, 0x07, 0x80, 0xfe, 0xfe, 0xee, 0x00, 0x01, 0xf0, 0x07, 0x00,
0xe0, 0x00, 0x61, 0xc0, 0x00, 0x3c, 0x03, 0xc0, 0xe6, 0x10, 0x8c, 0x00, 0x00, 0xf0, 0x07, 0x00,
0xf0, 0x00, 0xe1, 0xc0, 0x00, 0x3c, 0x03, 0xc0, 0x70, 0x78, 0x1c, 0x00, 0x00, 0xe0, 0x07, 0x00,
0x70, 0x00, 0xc1, 0xc0, 0x00, 0x3c, 0x01, 0xe0, 0x38, 0xfc, 0x38, 0x20, 0x00, 0xe0, 0x07, 0x00,
0x78, 0x03, 0xc1, 0xc0, 0x00, 0x3c, 0x00, 0xf0, 0x3c, 0x30, 0xf0, 0x70, 0x01, 0xc0, 0x07, 0x00,
0x3e, 0x0f, 0x81, 0xc0, 0x00, 0x3c, 0x00, 0xf0, 0x1f, 0x83, 0xe0, 0x3c, 0x03, 0xc0, 0x07, 0x00,
0x1f, 0xfe, 0x01, 0xc0, 0x00, 0x3c, 0x00, 0x78, 0x07, 0xff, 0xc0, 0x1f, 0xff, 0x00, 0x07, 0x00,
0x03, 0xf8, 0x01, 0xc0, 0x00, 0x3c, 0x00, 0x3c, 0x01, 0xff, 0x00, 0x07, 0xfc, 0x00, 0x07, 0x00
};
// 'freeze_delay', 34x32px
const unsigned char bitmap_freeze_delay[] PROGMEM = {
0x00, 0x1f, 0xc0, 0x00, 0x00, 0x00, 0x1f, 0xc0, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00,
0x7f, 0xf0, 0x00, 0x00, 0x0c, 0xe0, 0x3c, 0x00, 0x00, 0x1b, 0x80, 0x07, 0x00, 0x00, 0x36, 0x00,
0x01, 0x80, 0x00, 0x0c, 0x06, 0x00, 0xc0, 0x00, 0x18, 0x36, 0x60, 0x60, 0x00, 0x30, 0x30, 0x6c,
0x20, 0x00, 0x31, 0x80, 0x0c, 0x30, 0x00, 0x61, 0x80, 0x00, 0x10, 0x00, 0x40, 0x60, 0x00, 0x10,
0x00, 0x46, 0x30, 0x03, 0x18, 0x00, 0xc6, 0x1a, 0x03, 0x08, 0x00, 0xc0, 0x0f, 0x00, 0x08, 0x00,
0x8c, 0x07, 0x80, 0x00, 0x00, 0x8c, 0x07, 0x00, 0x00, 0x00, 0x80, 0x02, 0x05, 0x28, 0x00, 0xc6,
0x00, 0x05, 0x28, 0x00, 0xc6, 0x00, 0x0f, 0x3c, 0x00, 0x40, 0x00, 0x03, 0x30, 0x00, 0x41, 0x80,
0x31, 0xe3, 0x00, 0x61, 0x80, 0x19, 0xc6, 0x00, 0x30, 0x00, 0xff, 0xff, 0xc0, 0x10, 0x30, 0x7f,
0xff, 0x80, 0x18, 0x33, 0x19, 0xe6, 0x00, 0x0c, 0x03, 0x31, 0xe3, 0x00, 0x06, 0x00, 0x03, 0x30,
0x00, 0x03, 0x80, 0x0f, 0x3c, 0x00, 0x00, 0xf8, 0x35, 0x28, 0x00, 0x00, 0x0f, 0xe5, 0x28, 0x00
};
// 'freeze', 42x32px
const unsigned char bitmap_freeze[] PROGMEM = {
0x00, 0x08, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x1c, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xee, 0x00,
0x00, 0x00, 0x00, 0x07, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf8, 0x00, 0x00, 0x00, 0x01, 0x01,
0xf0, 0x30, 0x00, 0x00, 0x03, 0x00, 0xe0, 0xfc, 0x00, 0x00, 0x13, 0x80, 0xe1, 0xfe, 0x00, 0x00,
0x3b, 0x80, 0xe1, 0xce, 0x00, 0x00, 0x1f, 0x00, 0xe1, 0x87, 0x0c, 0x00, 0x0f, 0x00, 0xe0, 0xc7,
0x1e, 0x00, 0x1f, 0xe1, 0xe0, 0x0e, 0x3f, 0x00, 0x7f, 0xf3, 0xef, 0xfe, 0x33, 0x80, 0xf8, 0x3f,
0xef, 0xfc, 0x71, 0xc0, 0x60, 0x1f, 0xe7, 0xf0, 0x21, 0xc0, 0x00, 0x1f, 0xe0, 0x00, 0x01, 0xc0,
0x00, 0x1f, 0xef, 0x9f, 0x9f, 0x80, 0x00, 0x1f, 0xef, 0x9f, 0x9f, 0x00, 0x60, 0x1f, 0xef, 0x9f,
0x9e, 0x00, 0xf8, 0x3f, 0xe0, 0x00, 0x00, 0x00, 0x7f, 0xf3, 0xef, 0xff, 0x80, 0x00, 0x1f, 0xc1,
0xef, 0xff, 0xc0, 0x00, 0x0f, 0x00, 0xef, 0xff, 0xe0, 0x00, 0x1f, 0x00, 0xe0, 0x00, 0x70, 0x00,
0x3b, 0x80, 0xe0, 0x0c, 0x70, 0x00, 0x13, 0x80, 0xe0, 0x18, 0x60, 0x00, 0x03, 0x01, 0xf0, 0x1f,
0xe0, 0x00, 0x01, 0x03, 0xf8, 0x0f, 0xc0, 0x00, 0x00, 0x07, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x0e,
0xee, 0x00, 0x00, 0x00, 0x00, 0x1c, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x08, 0xe2, 0x00, 0x00, 0x00
};
// Variable for the inactivity timer
unsigned long inactive_time_start; // Time at bootinactive_time_now
// Variables for the timer control
uint8_t counter = 2;
int timer = counter * 5; // Value of the timer in seconds
unsigned long lastButtonPress = 0;
uint8_t currentStateCLK;
uint8_t lastStateCLK;
uint8_t timer_start = 0; // 0 = timer is not counting down
// Variables for the linear actuator
#define relayPinC 38 // Close actuator
#define relayPinO 6 // Open actuator
#define limit_switch 8 // Limit switch that will detect if lid is inserted
unsigned long period_led = 499UL; // Time to open/close the lid
// Variables for the pressure readout
const uint8_t analogInputPin = A0;
int voltage;
// Variables for the lid operation
uint8_t lid_mvt = 0; // 0 = lid is in movement
uint8_t lid_pos = 1; // 1 = lid is open
uint8_t time_lid = 1; // time required for lid to open/close
uint8_t nipple_pos = 0; // 0 = nipple is not inserted
unsigned long start_time_lid;
unsigned long time_now_lid;
// Variables for display
uint8_t close_lid_warning = 0; // 0 = message for closing the lid is disabled
uint8_t logo = 1; // 1 = display logo on screen
uint8_t nipple_msg = 0; // 1 = display missing nipple msg on screen
#define relayValve 12 // Valve activation
uint8_t valve_state = 0; // 0 = Valve is off
unsigned long time_now;
unsigned long time_now_valve;
unsigned long start_time;
unsigned long start_time_valve;
unsigned long valve_on = 499UL; // Time the valve stays open
unsigned long valve_off = 499UL; // Time the valve stays closed
// Variables for the RGB lights
#define rgbPinsR 11
#define rgbPinsG 10
#define rgbPinsB 9
uint8_t led_show = 0; //To turn on and off the LED's
// Variables for the fan
#define relayFan 13 // Fan output pin for activation
Adafruit_SSD1306 tft(128, 64, &Wire, -1);
void updateDisplay() {
if (logo == 1) {
tft.clearDisplay();
tft.drawBitmap(0, 15, ulogo, 128, 23, WHITE);
tft.display();
delay(2000);
tft.clearDisplay();
logo = 0;
updateDisplay();
}
// Display to allow user to set the timer
if (timer_start == 0) {
tft.drawRect(0, 0, 42, 32, BLACK);
tft.fillRect(0, 0, 42, 32, BLACK);
tft.drawBitmap(0, 0, bitmap_freeze_delay, 34, 32, WHITE);
tft.setCursor(56, 2);
tft.print("Temps de");
tft.setCursor(56, 12);
tft.println("congelation");
tft.setTextSize(2);
tft.setCursor(64, 30);
tft.drawRect(64, 30, 50, 15, BLACK);
tft.fillRect(64, 30, 50, 15, BLACK);
tft.print(timer);
tft.setTextSize(1);
}
// Display while the machine is freezing
if (timer_start == 1) {
tft.drawRect(0, 0, 34, 32, BLACK);
tft.fillRect(0, 0, 34, 32, BLACK);
tft.drawBitmap(0, 0, bitmap_freeze, 42, 32, WHITE);
tft.setTextColor(WHITE, BLACK);
tft.setCursor(64, 30);
tft.setTextSize(2);
tft.drawRect(64, 30, 50, 15, BLACK);
tft.fillRect(64, 30, 50, 15, BLACK);
tft.print(timer);
tft.setTextSize(1);
}
// Always displayed
// Draw Cylinder gauge
tft.setTextColor(WHITE, BLACK);
voltage = map(constrain(analogRead(analogInputPin), 670, 1024), 670, 1023, 0, 100);
if (voltage >= 90) {
tft.drawBitmap(2, 45, cylinder, 32, 7, WHITE);
}
if (voltage >= 75 && voltage < 90) {
tft.drawBitmap(2, 45, cylinder, 32, 7, WHITE);
}
if (voltage < 75) {
tft.drawBitmap(2, 45, cylinder, 32, 7, WHITE);
}
tft.setCursor(5, 55);
tft.print(voltage);
tft.display();
}
void LED() {
// Lights off
if (led_show == 0) {
digitalWrite(rgbPinsR, 1);
digitalWrite(rgbPinsG, 1);
digitalWrite(rgbPinsB, 1);
}
// Lights when closing lid
if (led_show == 1) {
if (digitalRead(rgbPinsR) == 1) {
digitalWrite(rgbPinsB, 1);
digitalWrite(rgbPinsR, 0);
}
else {
digitalWrite(rgbPinsR, 1);
digitalWrite(rgbPinsB, 0);
}
// Lights when opening lid
if (led_show == 2) {
if (digitalRead(rgbPinsG) == 1) {
digitalWrite(rgbPinsB, 1);
digitalWrite(rgbPinsG, 0);
}
else {
digitalWrite(rgbPinsG, 1);
digitalWrite(rgbPinsB, 0);
}
// Lights when a warning message is displayed
if (led_show == 3) {
for (int i = 0; i < 9; i++) {
if ((digitalRead(rgbPinsR)) == 0) {
digitalWrite(rgbPinsR, 1);
}
else {
digitalWrite(rgbPinsR, 0);
}
delay(500);
}
}
}
}
}
void lid_operation() {
lid_mvt = 1;
time_now_lid = millis();
if (time_now_lid - start_time_lid > 999UL && lid_mvt == 1) {
time_lid--;
//If the lid is in the open position, CLOSE it
if (lid_pos == 1 && time_lid > 0) {
led_show = 1;
LED();
updateDisplay();
digitalWrite(relayPinC, 0);
}
//If the lid is in the open position, OPEN it
if (lid_pos == 0 && time_lid > 0) {
led_show = 2;
LED();
updateDisplay();
digitalWrite(relayPinO, 0);
}
if (time_lid == 0) {
digitalWrite(relayPinC, 1);
digitalWrite(relayPinO, 1);
digitalWrite(relayFan, 1);
led_show = 0;
LED();
if (lid_pos == 1) {
lid_pos = 0;
}
else {
lid_pos = 1;
}
lid_mvt = 0;
time_lid = 1;
updateDisplay();
}
start_time_lid = time_now_lid;
}
}
void control() {
// If lid is open, close it
while (lid_mvt == 1) {
lid_operation();
}
// If the lid is closed and not in movement
if (timer_start == 1 && lid_pos == 0 && lid_mvt == 0) {
time_now = millis();
digitalWrite(relayFan, 0);
if (time_now - start_time > 999UL) {
timer--;
start_time = millis();
updateDisplay();
}
// Time for the valve to stay on
if (time_now - start_time_valve > valve_off && valve_state == 1) {
digitalWrite(relayValve, 1);
start_time_valve = millis();
valve_state = 0;
}
// LED for when freezing is occuring
if (time_now - start_time_valve > 499UL && valve_state == 1) {
led_show = 1;
LED();
}
// Time for the valve to stay off
if (time_now - start_time_valve > valve_on && valve_state == 0) {
digitalWrite(relayValve, 0);
valve_state = 1;
start_time_valve = millis();
}
// Section that controls the countdown of the timer
if (timer == 0) {
timer = counter * 5;
timer_start = 0;
logo = 1;
lid_mvt = 1;
led_show = 0;
LED();
digitalWrite(relayValve, 1);
tft.clearDisplay();
updateDisplay();
}
control();
}
}
void setup(void) {
Serial.begin(9600);
// Initialize screen
tft.begin(SSD1306_SWITCHCAPVCC, 0x3C);
tft.clearDisplay();
// Set encoder pins as inputs
pinMode(CLK, INPUT);
pinMode(DT, INPUT);
pinMode(SW, INPUT_PULLUP);
// Set RGB pins as outputs and off at boot
pinMode(rgbPinsR, OUTPUT);
pinMode(rgbPinsG, OUTPUT);
pinMode(rgbPinsB, OUTPUT);
digitalWrite(rgbPinsR, 1);
digitalWrite(rgbPinsG, 1);
digitalWrite(rgbPinsB, 1);
// Init fan pin and set as off at boot
pinMode(relayFan, OUTPUT);
digitalWrite(relayFan, 1);
// Init relay valve pin and set as off at boot
pinMode(relayValve, OUTPUT);
digitalWrite(relayValve, 1);
// Set variable for the pressure sensor as input
pinMode(analogInputPin, INPUT);
// Init relay actuator and fan pins and set as off at boot
pinMode(relayFan, OUTPUT);
pinMode(relayPinC, OUTPUT);
pinMode(relayPinO, OUTPUT);
digitalWrite(relayFan, 1);
digitalWrite(relayPinC, 1);
digitalWrite(relayPinO, 1);
// Read the initial state of CLK
lastStateCLK = digitalRead(CLK);
updateDisplay();
start_time = millis();
pinMode(limit_switch, INPUT_PULLUP);
start_time_lid = millis();
}
void loop() {
// Section that records each turn of the knob
// Read the current state of CLK
currentStateCLK = digitalRead(CLK);
// If last and current state of CLK are different, then pulse occurred
// React to only 1 state change to avoid double count
if (currentStateCLK != lastStateCLK && currentStateCLK == 1) {
// If the DT state is different than the CLK state then
// the encoder is rotating CW so increment
if (digitalRead(DT) != currentStateCLK) {
counter = counter + 1;
if (counter == 101) {
counter = 2;
}
} else {
// Encoder is rotating CCW so decrementCW so increment
counter = counter - 1;
if (counter == 1) {
counter = 100;
}
}
timer = counter * 5;
updateDisplay();
}
// Remember last CLK state
lastStateCLK = currentStateCLK;
// Read the button state
int btnState = digitalRead(SW);
//If we detect LOW signal, button is pressed
if (btnState == LOW) {
//if 50ms have passed since last LOW pulse, it means that the
//button has been pressed, released and pressed again
if (millis() - lastButtonPress > 10) {
if (digitalRead(limit_switch) == 1) {
timer_start = 1;
if (lid_pos == 1) {
lid_mvt = 1;
}
control();
}
else {
nipple_msg = 1;
updateDisplay();
}
}
// Remember last button press event
lastButtonPress = millis();
}
// Put in a slight delay to help debounce the reading
delay(1);
}