#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <SPI.h>
#include <EEPROM.h>
// Screen dimensions
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 128 // Change this to 96 for 1.27" OLED.
// Pins for the screen Pin 13 is CLK and Pin 11 is DIN
#define DC_PIN 9
#define CS_PIN 10
// Variables for the rotary encoder
#define CLK 3
#define DT 4
#define SW 5
// '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
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 6 // Close actuator
#define relayPinO A4 // Open actuator
#define limit_switch 8 // Limit switch that will detect if lid is inserted
#define lid_button 2 // Button to open or close lid manually
const unsigned long period_led = 499; // Time to open/close the lid
// Variables for the pressure readout
const uint8_t analogInputPin = A7;
int voltage;
int cylinder_gauge = 1;
// 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 = 8; // time required for lid to open/close
uint8_t nipple_pos = 0; // 0 = nipple is not inserted
unsigned long start_time_lid = 0;
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
uint8_t display = 1; // 1 = controls what gets displayed
// Variales for the valve
#define relayValve 7
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;
const unsigned long valve_on = 499; // Time the valve stays open
const unsigned long valve_off = 499; // Time the valve stays closed
// Variables for the RGB lights
#define rgbPinsR A0
#define rgbPinsG A1
#define rgbPinsB A2
uint8_t led_show = 0; //To turn on and off the LED's
unsigned long start_time_led;
// Variables for the fan
#define relayFan A3 // Fan output pin for activation
// Variables for the button interrupt (open/close of the lid)
const int btn_lid = 2;
volatile unsigned long bounceTime = 0; // Variable to prevent bouncing on button press
volatile int user_interrupt = 0;
// Color definitions
#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF
Adafruit_ST7735 tft = Adafruit_ST7735(CS_PIN, DC_PIN, 99);
void updateDisplay() {
if (logo == 1) {
tft.fillScreen(BLACK);
tft.drawBitmap(0, 50, ulogo, 128, 23, WHITE);
delay(2000);
logo = 0;
display = 1;
tft.fillScreen(BLACK);
updateDisplay();
}
// Displays message while lid is closing
if (display == 2) {
tft.fillScreen(BLACK);
tft.setCursor(15, 10);
tft.print("Fermeture.");
tft.setCursor(15, 30);
tft.print("Attention");
tft.setCursor(20, 50);
tft.print("aux");
tft.setCursor(15, 70);
tft.print("obstacles");
}
// Display to allow user to set the timer
if (display == 1) {
tft.drawBitmap(0, 0, bitmap_freeze_delay, 34, 32, WHITE);
tft.setCursor(40, 65);
tft.setCursor(30, 6);
tft.print("Temps de");
tft.setCursor(35, 35);
tft.println("gel");
tft.setTextSize(3);
tft.drawRect(40, 100, 55, 25, BLACK);
tft.fillRect(40, 100, 55, 25, BLACK);
tft.setCursor(40, 100);
tft.print(timer);
tft.setTextSize(2);
}
// Display while the machine is freezing
if (display == 5) {
tft.drawBitmap(0, 0, bitmap_freeze, 42, 32, WHITE);
tft.setTextColor(WHITE, BLACK);
tft.setTextSize(3);
tft.setCursor(40, 65);
tft.drawRect(40, 100, 55, 25, BLACK);
tft.fillRect(40, 100, 55, 25, BLACK);
tft.setCursor(40, 100);
tft.print(timer);
tft.setTextSize(2);
}
// Displays warning message if lid is not inserted
if (display == 6) {
nipple_msg = 0;
tft.fillScreen(BLACK);
tft.setCursor(0, 10);
tft.print("Inserer");
tft.setCursor(0, 30);
tft.print("la plaque");
tft.setCursor(0, 50);
tft.print("de gel");/*
tft.setCursor(0, 70);
tft.print("refroidissement"); */
led_show = 3;
LED();
led_show = 0;
LED();
tft.fillScreen(BLACK);
display = 1;
cylinder_gauge = 1;
updateDisplay();
}
// Displays message while the lid is opening
if (display == 4) {
tft.fillScreen(BLACK);
cylinder_gauge = 0;
tft.setCursor(15, 30);
tft.print("Veuillez");
tft.setCursor(15, 50);
tft.print("patienter");
}
// Draw Cylinder gauge
if (cylinder_gauge == 1) {
tft.setTextSize(2);
tft.setTextColor(WHITE, BLACK);
tft.drawRect(2, 55, 55, 25, BLACK);
tft.fillRect(2, 55, 55, 25, BLACK);
voltage = map(constrain(analogRead(analogInputPin), 670, 1024), 670, 1023, 0, 100);
if (voltage >= 90) {
tft.drawBitmap(2, 55, cylinder, 32, 7, GREEN);
}
if (voltage >= 75 && voltage < 90) {
tft.drawBitmap(2, 55, cylinder, 32, 7, YELLOW);
}
if (voltage < 75) {
tft.drawBitmap(2, 55, cylinder, 32, 7, RED);
}
tft.setCursor(3, 65);
tft.print(voltage);
}
}
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() {
while (lid_mvt == 1) {
time_now_lid = millis();
if (time_now_lid - start_time_lid > 999 && lid_mvt == 1) {
//If the lid is in the open position, CLOSE it
if (lid_pos == 1 && time_lid == 8) {
close_lid_warning = 1;
cylinder_gauge = 0;
led_show = 1;
LED();
display = 2;
updateDisplay();
digitalWrite(relayPinC, 0);
}
//If the lid is in the close position, OPEN it
if (lid_pos == 0 && time_lid == 8) {
close_lid_warning = 0;
cylinder_gauge = 0;
led_show = 2;
LED();
display = 4;
updateDisplay();
digitalWrite(relayPinO, 0);
}
// Controls LED's while the lid is moving
if (time_lid > 0 && time_lid < 8) {
if (lid_pos == 1) {
led_show = 1;
LED();
}
else {
led_show = 2;
LED();
}
}
// Reinitializes the variables when lid movement is finished
if (time_lid == 0) {
digitalWrite(relayPinC, 1);
digitalWrite(relayPinO, 1);
digitalWrite(relayFan, 1);
digitalWrite(relayValve, 1);
timer = counter * 5;
led_show = 0;
LED();
lid_pos = ! lid_pos;
lid_mvt = ! lid_mvt;
// 1 seconds more than original because program will substract 1 as it exits the function
time_lid = 9;
cylinder_gauge = 1;
display = 1;
updateDisplay();
}
start_time_lid = time_now_lid;
time_lid--;
}
}
}
void control() {
// If lid is open, close it
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 > 999) {
timer--;
start_time = millis();
display = 5;
updateDisplay();
}
// LED for when freezing is occuring
if (time_now - start_time_led > 499) {
led_show = 0;
LED();
start_time_led = millis();
}
// Turns OFF valve after determined on time
if (time_now - start_time_valve > valve_off && valve_state == 1) {
digitalWrite(relayValve, 1);
start_time_valve = millis();
valve_state = 0;
}
// Turns ON valve after determined on time
if (time_now - start_time_valve > valve_on && valve_state == 0) {
digitalWrite(relayValve, 0);
valve_state = 1;
start_time_valve = millis();
}
if (timer == 0) {
logo = 1;
lid_mvt = 1;
led_show = 0;
LED();
digitalWrite(relayValve, 1);
}
control();
}
}
/* void interrupt_button() {
if (millis() - bounceTime > 250) {
user_interrupt = 1;
Serial.println("ADSDFSDGFDAGDSGSDAGSDAADSDFSDGFDAGDSGSDAGSDAADSDFSDGFDAGDSGSDAGSDA");
bounceTime = millis();
}
} */
/* void interrupt() {
Serial.println("hi");
digitalWrite(relayPinC, 1);
digitalWrite(relayPinO, 1);
digitalWrite(relayFan, 1);
lid_mvt = 1;
time_lid = 8;
timer_start = 0;
lid_operation();
} */
void setup(void) {
Serial.begin(9600);
// Initialize screen
tft.initB();
tft.fillScreen(BLACK);
tft.setTextSize(2);
tft.setRotation(2);
// 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);
// Initialize the button for interrupt, trigger when button is pressed
pinMode(btn_lid, INPUT_PULLUP);
//attachInterrupt(digitalPinToInterrupt(btn_lid), interrupt_button, RISING);
}
void loop() {
/* if (user_interrupt == 1) {
user_interrupt = 0;
interrupt();
} */
// 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 > 50) {
if (digitalRead(limit_switch) == 0) {
timer_start = 1;
if (lid_pos == 1) {
lid_mvt = 1;
}
control();
}
else {
cylinder_gauge = 0;
nipple_msg = 1;
display = 6;
updateDisplay();
}
}
// Remember last button press event
lastButtonPress = millis();
}
// Put in a slight delay to help debounce the reading
delay(1);
}