30 March 2016

Thermoelectric Drinks-Can Cooler

I am a big fan of Ben Heck. Way back in 2011, Ben made a Super Can Cooler on his YouTube channel and I'd always thought it was a neat idea, something I'd like to build myself one day.
At the time I didn't have an easy way to make the aluminium pieces. Recently though, I had a chance to make my own can-cooler inspired by Ben's version.

The idea with this device is that a drinks can fits snugly in the aluminium 'coldsink' and a pair of peltier modules(TECs) pull heat from the aluminium and dissipate it to the room, cooling down the can in the process.

Of course this is an absurd contraption, wholly impractical and not even particularly useful. But for me there's huge fun to be had in designing and building a 'thing' whatever it is, and *I* think it's a cool idea anyway.

Luckily, my brother is an apprentice tool-maker and as part of an assignment for college, he machined the aluminium pieces and built the sliding base for me.

I could then design the control electronics and make an enclosure for them with my 3D printer.

Overall this is a bit of a novelty project, thermoelectric coolers are not all-that efficient and because I liked the idea of having the big chunk of bare aluminium as a design feature, it wouldn't be well insulated either.

In any case, I'd go a bit overboard on the electronics and use it as a way to improve my nascent Arduino programming 'skills'. (blah-blah, you could do it with a 555 instead, I like Arduino.)

Looking through some of the stuff I've picked up from eBay recently gave me some ideas.
I'd use a 'Nokia' LCD as a screen, Dallas DS18B20 digital thermometers to monitor temperature and a rotary encoder for user input, with a piezo transducer for feedback.

I also had some WS2812 LED strip left-over from a DIY-ambilight that I might add on there too.
Everything will be powered from a 12V supply and I'd use a logic-level MOSFET for the high-current switching.

Luckily I'm not the first person to use these with an Arduino. Some very clever people have already written software libraries for these components to make them easy to use.

Adafruit-PCD8544 library
OneWire library
toneAC library
Dallas Temperature library
Adafruit-NeoPixel library

I am very much an amateur programmer, I more-or-less understand the basics but rely heavily on copying and pasting other peoples code.

Because of this I'll post my sketch here, it's definitely untidy, hacked-together and needs to be cleaned up, but it helped me greatly to see what others had written.

It's not pretty, you have been warned... (expand source if you hate your scrollwheel)
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
#include <Fonts/TomThumb2.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <EEPROM.h>
#include <toneAC.h>
#include <Adafruit_NeoPixel.h>

#define PIN A4
#define NUMPIXELS 14
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

/*
Software SPI for LCD
D13 - Serial clock out (SCLK)
D11 - Serial data out (DIN)
D4 - Data/Command select (D/C)
D5 - LCD chip select (CS)
D8 - LCD reset (RST)

A0 ... Rotary Button
A1 ... Rotary CLK
A2 ... Rotary DT

D2    ... DS18x20 Temperature sensors
D3    ... TEC MOSFET
D6    ... Hall effect sensor for "door close"
D7    ... LCD Backlight EN
D9,10 ... Piezo buzzer
D7   ... can detect switch
A3    ... fan control transistor
*/

Adafruit_PCD8544 display = Adafruit_PCD8544(13, 11, 4, 5, 8);   // LCD pins

#define ONE_WIRE_BUS 2
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);


/*************************************** Bitmap and Sound Data ********************************************
 **********************************************************************************************************/
 
// Always Coca-Cola Jingle
int melody[] = { 1480, 1480, 1480, 1480, 1568, 1480, 1318, 1318, 1760, 1480, 1175 };
int noteDurations[] = { 8, 8, 8, 8, 4, 8, 4, 8, 8, 4, 4 };

// coke bitmap data
static const unsigned char PROGMEM coke[] =
{
  B01111110,
  B11111011,
  B11111011,
  B11111011,
  B11111011,
  B11111101,
  B11111101,
  B11111101,
  B11111101,
  B11111001,
  B11111011,
  B01111110,
};

// title bitmap data
static const unsigned char PROGMEM title[] =
{
  B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000,
  B00111100, B00000000, B00000000, B00000001, B11100000, B00000000, B00000111, B00000000, B00000000, B00000000, B00000000,
  B01111110, B00000000, B00000000, B00000011, B11110000, B00000000, B00000111, B00000000, B00000000, B00000000, B00000000,
  B11100110, B01111100, B11101100, B00000111, B00110001, B11000001, B11000111, B00011110, B00111100, B00000000, B00000000,
  B11100000, B11111110, B11111110, B00000111, B00000011, B11100011, B11100111, B00110111, B01111110, B00000000, B00000000,
  B11100000, B11101110, B11101110, B00000111, B00000111, B01110111, B01110111, B01110111, B01110000, B00000000, B00000000,
  B11100000, B11101110, B11101110, B00000111, B00000111, B01110111, B01110111, B01111111, B01110000, B00011100, B01100000,
  B11100110, B11101110, B11101110, B00000111, B00110111, B01110111, B01110111, B01110000, B01110001, B01010100, B00100000,
  B01111110, B11111110, B11101110, B00000011, B11110011, B11100011, B11100111, B00111111, B01110001, B01010100, B00100000,
  B00111100, B01110110, B11101110, B00000001, B11100001, B11000001, B11000111, B00011110, B01110000, B10011101, B01110000,
  B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000,
  B11111111, B11111111, B11111111, B11111111, B11111111, B11111111, B11111111, B11111111, B11111111, B11111111, B11110000,
};

// large snowflake data
static const unsigned char PROGMEM snow16[] =
{
  B00000110, B00000000,
  B00101111, B01000000,
  B01110110, B11100000,
  B00110110, B11000000,
  B01001001, B00100000,
  B11110110, B11110000,
  B11110110, B11110000,
  B01001001, B00100000,
  B00110110, B11000000,
  B01110110, B11100000,
  B00101111, B01000000,
  B00000110, B00000000,
  B00000000, B00000000,
  B00000000, B00000000,
  B00000000, B00000000,
  B00000000, B00000000,
};


/***********************************************************************************************************************************
 **********************************************************************************************************************************/

volatile byte seqA = 0;
volatile byte seqB = 0;
volatile boolean right = false;
volatile boolean left = false;
volatile boolean button = false;
boolean backlight;
boolean LEDs;
boolean beep;
boolean doorClosed;
boolean canDetect;
boolean start;
boolean doorFlash = 0;
boolean canFlash = 0;
boolean hsFan = 0;
byte menuitem = 1;
byte page = 1;

unsigned long coolStart;
unsigned long coolEnd;

int tempUser;       // user set temperature variable
int tempSet;        // temperature to attempt for cooling
byte contrast;      // screen contrast value
byte contUser;      // user set contrast variable

float tempHeatsink;        // temperature variables
float tempCan;
float tempRoom;


void setup() {

  pixels.begin(); // This initializes the NeoPixel library.

  pinMode(A0, INPUT);     //A0 rotary switch to GND
  pinMode(A1, INPUT);     //A1 & A2 rotary encoder via 27k resistor and 100nF cap for debounce
  pinMode(A2, INPUT);

  pinMode(3, OUTPUT);          // Pin 3 for MOSFET output testing
  pinMode(6, INPUT_PULLUP);    // Pin 6 for Hall effect sensor
  pinMode(12, INPUT_PULLUP);   // Pin 12 for can detect switch
  pinMode(A3, OUTPUT);         // Pin A3 for Fan output

  digitalWrite(3, LOW);     //initial state off
  digitalWrite(A3, LOW);

  // Enable internal pull-up resistors
  digitalWrite(A0, HIGH);
  digitalWrite(A1, HIGH);
  digitalWrite(A2, HIGH);


  // Read stored preferences from EEPROM
  contrast = EEPROM.read(0);
  beep = EEPROM.read(1);
  LEDs = EEPROM.read(2);
  backlight = EEPROM.read(3);
  tempSet = EEPROM.read(4);


  // LCD backlight
  pinMode(12, OUTPUT);
  if (backlight) {
    digitalWrite(12, LOW);
  }
  else {
    digitalWrite(12, HIGH);
  }

  PCICR =  0b00000010;  // 1. PCIE1: Pin Change Interrupt Enable 1
  PCMSK1 = 0b00000111;  // Enable Pin Change Interrupt for A0, A1, A2
  sei();                // Enable Global Interrupts

  // Initialize LCD
  display.setRotation(2);   // Set LCD orientation
  display.begin(contrast);  // Set LCD contrast
  display.clearDisplay();   // Clear display
  display.display();        // Apply changes

  sensors.begin();           // start temperature sensors

  pixels.setBrightness(100); // set global brightness of strip (0-255)
}

void playJingle () {
  if (beep) {
    for (int thisNote = 0; thisNote < 11; thisNote++) {
      int noteDuration = 1000 / noteDurations[thisNote];
      toneAC(melody[thisNote], 10, noteDuration, true);   // Play thisNote at full volume for noteDuration in the background.
      delay(noteDuration * 4 / 3);                        // Wait while the tone plays in the background, plus another 33% delay between notes.
    }
  }
}

void saveData() {   // save user settings in EEPROM
  EEPROM.write(0, contrast);
  EEPROM.write(1, beep);
  EEPROM.write(2, LEDs);
  EEPROM.write(3, backlight);
}

void checkHeatsink() {
  if (hsFan) {       // only check temp sensor if the fan is already on to prevent slowdown in menu
    sensors.requestTemperatures();      // get temperature readings and round to 1dp
    float hstemp = round((sensors.getTempCByIndex(0) + 0.5) * 10) / 10.0;
    if (hstemp < 30) {                   // check heatsink temp and turn fans off if cooler than (30°C)
      digitalWrite(A3, LOW);
      hsFan = 0;
    }
    else {
      hsFan = 1;
    }
  }
}

void doorCheck () {
  if ((digitalRead(6) == LOW)) {      // read hall-effect sensor to check if the door is closed
    doorClosed = true;
  }
  else {
    doorClosed = false;
    start = 0;
  }
  if ((digitalRead(7) == LOW)) {     // read the can switch to check if a can is inserted
    canDetect = true;
  }
  else {
    canDetect = false;
    start = 0;
  }
}

void clickBeep() {
  if (beep) {
    toneAC(4000, 10, 50, true);
  }
}

void menuBeep () {
  if (beep) {                           // check beep setting
    if ((page != 3) && (page != 6)) {   // no scroll beeps on cooling/finished pages
      toneAC(4000, 10, 50, true);
    }
  }
}

void ledsMain() {
  if (LEDs) {
    for (int i = 0; i < NUMPIXELS; i++) {
      pixels.setPixelColor(i, pixels.Color(255, 0, 0));
      pixels.show();
    }
  }
}

void ledsTemp() {
  if (LEDs) {
    for (int i = 0; i < NUMPIXELS; i++) {
      pixels.setPixelColor(i, pixels.Color(0, 0, 255));
      pixels.show();
    }
  }
}

void ledsCont() {
  if (LEDs) {
    for (int i = 0; i < NUMPIXELS; i++) {
      pixels.setPixelColor(i, pixels.Color(255, 125, 0));
      pixels.show();
    }
  }
}

void ledsSett() {
  if (LEDs) {
    for (int i = 0; i < NUMPIXELS; i++) {
      pixels.setPixelColor(i, pixels.Color(0, 255, 0));
      pixels.show();
    }
  }
}

void ledsFin() {
  if (LEDs) {
    for (int i = 0; i < NUMPIXELS; i++) {
      pixels.setPixelColor(i, pixels.Color(255, 0, 0));
      pixels.show();
    }
  }
}


void ledsOff () {
  for (int i = 0; i < NUMPIXELS; i++) {
    pixels.setPixelColor(i, pixels.Color(0, 0, 0));
    pixels.show();
  }
}

void ledsCoolPage () {
  if (LEDs) {
    if (start) {
      for (int i = 0; i < NUMPIXELS; i++) {
        pixels.setPixelColor(i, pixels.Color(0, 205, 255));     // colour when cooling
        pixels.show();
      }
    }
    else {
      for (int i = 0; i < NUMPIXELS; i++) {
        pixels.setPixelColor(i, pixels.Color(255, 0, 255));
        pixels.show();
      }
    }
  }
}

void loop() {

  /*************************************** Menu Pages ***********************************************
   **************************************************************************************************/

  // ************************************* Main-menu  **********************************************
  
  if (page == 1) {

    digitalWrite(3, LOW);     // make sure TECs are off

    ledsMain();

    display.clearDisplay();
    display.setTextColor(BLACK, WHITE);          // (text, background)
    display.drawBitmap(0, 0, title, 88, 12, 1);  // draw title bitmap
    display.drawFastHLine(0, 40, 66, BLACK);     // footer line
    display.setCursor(0, 41);                    // move text to bottom
    display.setFont(&TomThumb);
    display.print("George Gibson 2016");
    display.setFont();
    display.drawBitmap(68, 30, snow16, 16, 16, 1);  // draw snowflake


    display.setTextSize(1);                       // Set text size
    display.setCursor(3, 13);                     // position of top menu option text
    if (menuitem == 1) {
      display.setTextColor(WHITE, BLACK);
    }
    else {
      display.setTextColor(BLACK, WHITE);
    }
    display.print(">Start (");                    // top menu option text
    if ((tempSet < 10) && (tempSet >= 0)) {       //add leading zero to pad display
      display.print("0");
    }
    display.print(tempSet);                        // print the user-set temperature
    display.print("zC)");                          // for ° symbol use "z" instead


    display.setCursor(3, 22);                      // position of middle menu option text
    if (menuitem == 2) {
      display.setTextColor(WHITE, BLACK);
    }
    else {
      display.setTextColor(BLACK, WHITE);
    }
    display.print(">Temperature");                // middle menu option text

    display.setCursor(3, 31);                     // position of bottom menu option text
    if (menuitem == 3) {
      display.setTextColor(WHITE, BLACK);
    }
    else {
      display.setTextColor(BLACK, WHITE);
    }
    display.print(">Settings");                   // bottom menu option text

    display.display();

    checkHeatsink();

  }

  // ************************************* Temperature menu  *********************************************
  
  else if (page == 2) {

    ledsTemp();

    display.setTextSize(1);
    display.clearDisplay();
    display.setTextColor(BLACK, WHITE);
    display.setCursor(6, 0);
    display.print("Set your can");
    display.setCursor(8, 40);
    display.print("temperature");
    display.drawFastHLine(2, 9, 80, BLACK);      // draw a line at the top
    display.drawFastHLine(2, 37, 80, BLACK);     // draw a line at the bottom
    display.setTextSize(3);
    display.setCursor(12, 13);
    if ((tempUser < 10) && (tempUser >= 0)) {
      display.print("0");                        //add leading zero to pad the display
    }
    display.print(tempUser);
    display.setTextSize(2);
    display.print("zC");
    display.setTextSize(1);
    display.display();
  }

  // ************************************* Cooling Page  **********************************************
  
  else if (page == 3) {

    ledsCoolPage();

    sensors.requestTemperatures();
    // get temperature readings and round to 1dp
    tempHeatsink = round((sensors.getTempCByIndex(0) + 0.5) * 10) / 10.0;     // heatsink
    tempCan = round((sensors.getTempCByIndex(1) + 0.5) * 10) / 10.0;          // can
    tempRoom = round((sensors.getTempCByIndex(2) + 0.5) * 10) / 10.0;         // room

    doorCheck();

    display.clearDisplay();
    display.drawFastHLine(1, 0, 82, BLACK);     // draw border lines
    display.drawFastHLine(0, 1, 84, BLACK);
    display.drawFastVLine(0, 1, 17, BLACK);
    display.drawFastVLine(83, 1, 17, BLACK);
    display.drawFastVLine(1, 1, 17, BLACK);
    display.drawFastVLine(82, 1, 17, BLACK);

    display.drawFastHLine(1, 18, 82, BLACK);
    display.drawFastHLine(0, 17, 84, BLACK);
    display.drawFastHLine(0, 33, 84, BLACK);
    display.drawFastHLine(0, 47, 84, BLACK);

    display.setCursor(10, 2);                       // current temperature block
    display.setFont(&TomThumb);
    display.print("Current");
    display.setFont();
    display.setTextSize(1);
    display.setCursor(7, 9);
    display.print(tempCan, 1);
    display.print("zC");

    display.setCursor(55, 2);                      // target temperature block
    display.setFont(&TomThumb);
    display.print("Target");
    display.setFont();
    display.setTextSize(1);
    display.setCursor(56, 9);
    if ((tempSet < 10) && (tempSet >= 0)) {
      display.print("0");                           //add leading zero to pad the display
    }
    display.print(tempSet);
    display.print("zC");

    display.setCursor(6, 19);                       // ambient temperature
    display.setFont(&TomThumb);
    display.print("Room temp    : ");
    display.print(tempRoom, 1);
    display.print(" #c");
    display.setFont();

    display.setCursor(6, 26);                       // heatsink temperature
    display.setFont(&TomThumb);
    display.print("Heatsink      : ");
    display.print(tempHeatsink, 1);
    display.print(" #c");
    display.setFont();

    display.setCursor(8, 34);                        // door closed text block
    display.setFont(&TomThumb);
    if (doorClosed) {

    }
    else {
      if (doorFlash == 0) {                          // flash close door text
        display.print("  *Close the door*");
        display.display();
        doorFlash = 1;
      }
      else {
        doorFlash = 0;
      }
    }
    display.setFont();

    display.setCursor(8, 40);                         // insert can text block
    display.setFont(&TomThumb);
    if (canDetect) {

    }
    else {
      if (canFlash == 0) {                            // flash can text
        display.print("    *Insert a can*");
        display.display();
        canFlash = 1;
      }
      else {
        canFlash = 0;
      }
    }
    display.setFont();


    // *** door closed, can inserted and can temp is lower than user set (ie. can already cold enough) ***
    if ((doorClosed) && (canDetect) && (tempSet > tempCan)) {
      display.setCursor(6, 34);
      display.setFont(&TomThumb);
      display.print("Can is already cold,");
      display.setFont();
      display.setCursor(1, 40);
      display.setFont(&TomThumb);
      display.print("set a new temperature");
      display.setFont();
    }

    // *** door closed, can inserted and can temp is higher than user set (ie. perform cooling) ***
    if ((doorClosed) && (canDetect) && (tempSet < tempCan)) {
      if (start == 0) {
        coolStart = millis();
      }
      start = 1;
      digitalWrite(3, HIGH);            // turn on TECs only under these conditions
      digitalWrite(A3, HIGH);           // turn on fans at same time, but don't turn off until the temperature check on main page has been passed
      hsFan = 1;
      display.setCursor(8, 36);
      display.print("* Cooling *");
    }
    else {
      digitalWrite(3, LOW);
    }

    // *** door closed, can inserted and can temp lower than user set (ie. can has finished cooling) ***
    if ((doorClosed) && (canDetect) && (tempSet >= tempCan) && (start == 1) ) {
      coolEnd = millis();
      display.setCursor(4, 36);
      display.print("* Finished *");
      display.display();
      digitalWrite(3, LOW);
      playJingle();
      start = 0;
      delay(500);
      page = 6;
    }


    display.display();
  }

  // ************************************* Settings menu  **********************************************
  
  else if (page == 4) {

    ledsSett();

    display.clearDisplay();                     // Clear previous screen
    display.drawFastHLine(0, 0, 84, BLACK);     // draw border
    display.drawFastHLine(0, 47, 84, BLACK);
    display.setTextSize(1);                     // Set text size
    display.setCursor(4, 2);                    // position of top menu option text

    // ****** change text colour if highlighted ******
    if (menuitem == 1) {
      display.setTextColor(WHITE, BLACK);
    }
    else {
      display.setTextColor(BLACK, WHITE);
    }
    // ************************************************

    display.print("Set contrast");              // 1st option text
    display.setCursor(4, 11);
    if (menuitem == 2) {
      display.setTextColor(WHITE, BLACK);
    }
    else {
      display.setTextColor(BLACK, WHITE);
    }

    display.print("Backlight:");                // 2nd option text
    if (backlight) {
      display.print("ON"); // toggle backlight
    }
    else {
      display.print("OFF");
    }


    display.setCursor(4, 20);
    if (menuitem == 3) {
      display.setTextColor(WHITE, BLACK);
    }
    else {
      display.setTextColor(BLACK, WHITE);
    }

    display.print("LEDs:");                     // 3rd option text
    if (LEDs) {
      display.print("ON"); // toggle LEDs
    }
    else {
      display.print("OFF");
    }

    display.setCursor(4, 29);
    if (menuitem == 4) {
      display.setTextColor(WHITE, BLACK);
    }
    else {
      display.setTextColor(BLACK, WHITE);
    }

    display.print("Beeps:");                      // 4th option text
    if (beep) {
      display.print("ON");
    }
    else {
      display.print("OFF");
    }

    display.setCursor(4, 38);
    if (menuitem == 5) {
      display.setTextColor(WHITE, BLACK);
    }
    else {
      display.setTextColor(BLACK, WHITE);
    }

    display.print(">main menu");                  // back to main menu

    display.display();
  }


  // ************************************* Contrast menu  **********************************************
  
  else if (page == 5) {

    ledsCont();

    display.setTextSize(1);
    display.clearDisplay();
    display.setTextColor(BLACK, WHITE);
    display.setCursor(6, 0);
    display.print("Set Contrast");
    display.setCursor(6, 40);
    display.print("Values 0-127");
    display.drawFastHLine(2, 9, 80, BLACK);      // draw a line at the top
    display.drawFastHLine(2, 37, 80, BLACK);     // draw a line at the bottom
    display.setTextSize(3);
    display.setCursor(20, 13);
    if ((contUser < 10) && (contUser >= 0)) {
      display.print("0"); //add leading zero to pad the display
    }
    display.print(contUser);
    display.setContrast(contUser);
    display.display();
  }

  // ************************************* Finished Page  **********************************************
  
  else if (page == 6) {

    ledsFin();

    display.clearDisplay();
    display.setCursor(5, 41);
    display.setFont(&TomThumb);
    display.print("Enjoy your cold can!");
    display.setFont();
    display.drawFastHLine(0, 0, 84, BLACK);
    display.drawFastHLine(0, 39, 84, BLACK);
    display.setCursor(0, 2);
    display.setFont(&TomThumb);
    display.print("Can cooled by ");
    float coolTemp = tempRoom - tempCan;     // calculate how much the can was cooled by
    display.print(coolTemp, 1);
    display.print(" #c");
    display.setFont();
    display.setCursor(0, 12);
    display.setFont(&TomThumb);

    int coolTime = (coolEnd - coolStart) / 1000;
    int mins = coolTime / 60;
    int secs = coolTime & 60;
    display.print("It took ");
    display.print(mins);
    display.print("mins ");
    display.print(secs);
    display.print("secs,");
    display.setFont();

    float coolSecs = coolTime;
    float power = coolSecs / 30000;   // conversion factor to kWh, assuming 120W draw
    display.setCursor(0, 20);
    display.setFont(&TomThumb);
    display.print("used approx ");
    display.print(power, 4);
    display.print("kWh");
    display.setFont();

    float cost = power * 0.12;
    display.setCursor(0, 28);
    display.setFont(&TomThumb);
    display.print("& cost about $");
    display.print(cost, 4);
    //display.print("p");
    display.setFont();

    display.drawBitmap(76, 2, coke, 8, 12, 1);  // draw coke can bmp

    display.display();

    digitalWrite(3, LOW);

    checkHeatsink();

  }


  // ************************************* Rotary Encoder  **********************************************

  // Take action if a new command received from the encoder
  if (left) {
    left = false;
    menuitem--;
    if (menuitem == 0) {
      menuitem = 5;           // roll-over to bottom
    }
  }

  if (right) {
    right = false;
    menuitem++;
    if (menuitem == 6) {      // roll-over to top
      menuitem = 1;
    }
  }

  if (button) {
    button = false;
    clickBeep();

    if (page == 1 && menuitem == 3) {         // If on main menu and "option 3" is highlighted and button pressed then:
      menuitem = 5;
      page = 4;
    }
    else if (page == 1 && menuitem == 2) {    // If on main menu and "option 2" is highlighted and button pressed then:
      page = 2;                                 // move to page 2
      tempUser = tempSet;                          // reset the rotary movements since previous user set
    }
    else if (page == 1 && menuitem == 1) {    // If on main menu and "option 1" is highlighted and button pressed
      page = 3;                                 // go to main cooling page
      delay(500);
    }
    else if (page == 3) {                     // If on Cooling page and button is pressed
      if (start == 1) {
        page = 6;                             // if cooling was activated then go to finished page
      }
      else {
        page = 1;                              // if not, go back to main menu
        menuitem = 5;
      }
      coolEnd = millis();                     // reset cooling timer
      start = 0;
    }
    else if (page == 2) {                     // If on Set Temp page and button is pressed
      page = 1;                                 // return to main menu
      menuitem = 1;
      tempSet = tempUser;                          // write user target temp to temp set variable
      EEPROM.write(4, tempSet);                     // write tempSet to EEPROM
    }
    else if (page == 4 && menuitem == 3) {    // toggle LEDs
      if (LEDs) {
        LEDs = false;
        ledsOff();
      }
      else {
        LEDs = true;
      }
    }
    else if (page == 4 && menuitem == 2) {    // toggle backlight
      if (backlight) {
        backlight = false;
        digitalWrite(12, HIGH);
      }
      else {
        backlight = true;
        digitalWrite(12, LOW);
      }

    }
    else if (page == 4 && menuitem == 5) {        // return to main menu
      saveData();
      page = 1;
    }
    else if (page == 4 && menuitem == 4) {        //menu beeps
      if (beep) {
        beep = false;
      }
      else {
        beep = true;
      }

    }
    else if (page == 4 && menuitem == 1) {
      contUser = contrast;
      page = 5;
    }
    else if (page == 5) {
      contrast = contUser;     // write user contrast value to contrast
      page = 4;                // go back to settings page
      menuitem = 1;            // with contrast highlighted
    }
    else if (page == 6) {
      page = 1;
      menuitem = 4;
    }
  }
}


ISR (PCINT1_vect) {

  // If interrupt is triggered by the button
  if (!digitalRead(A0)) {
    button = true;
  }

  // Else if interrupt is triggered by encoder signals
  else {

    // Read A and B signals
    boolean A_val = digitalRead(A1);
    boolean B_val = digitalRead(A2);

    // Record the A and B signals in seperate sequences
    seqA <<= 1;
    seqA |= A_val;

    seqB <<= 1;
    seqB |= B_val;

    // Mask the MSB four bits
    seqA &= 0b00001111;
    seqB &= 0b00001111;

    // Compare the recorded sequence with the expected sequence
    if (seqA == 0b00001001 && seqB == 0b00000011) {
      tempUser--;        //decrement Target Temp when turning knob left
      contUser--;
      left = true;
      menuBeep();
    }

    if (seqA == 0b00000011 && seqB == 0b00001001) {
      tempUser++;        //increment Target Temp when turning knob right
      contUser++;
      right = true;
      menuBeep();
    }
  }

}
The Arduino takes readings from the temperature sensors and compares them to a user-set target. It turns on the peltier coolers if the temperature is above the set-point, the door is closed and a can is detected.

It also measures the heat-sink temperature in order to control the fans and takes an ambient temperature reading to work out how much the can was cooled down by.

The case for the electronics is 3D printed and has cut-outs for all the components to fit. The idea would be to bolt it to the aluminium chassis to cool the MOSFET and to detect when the door is closed with a hall-effect sensor and an embedded magnet.
Since I wanted to be able to upgrade/service the unit, I used JST plugs for signals and 2mm bullet connectors for the high-current supply and sleeved the cables where I could.

I imagined using the LEDs mounted on the bottom as a temperature display. Fading from red to blue according to the temperature.

In the end I couldn't quite get to grips with the Neopixel library that I used to drive the LED strips. Something I'll revisit another time I think, perhaps when I upgrade the heatsinks.

I only used cheap coolers initially, with the intention to upgrade later on. I've also ordered some higher power TECs from China that may improve the cooling performance even more.

As of now it cools a can by about 10ish°C in 10 minutes or so and while that's not ice-cold it's perfectly drinkable and it does make for a cool, if a bit imposing, desk toy.

2 comments:

  1. Really awesome creation! Very creative and logical, something that students from http://essaywriting.education/ will find very useful on their projects.

    ReplyDelete