Temperature Monitor / Alarm
June 2017

 

Temperature Monitor / Alarm

Summary

The monitor is powered with a 1000mAh rechargeable lithium battery and can be left running for several weeks before it needs recharging via its own internal USB/5v lithium charger.

It incorporates an internal digital temperature sensor but an external sensor can be plugged into a 3.5mm jack socket to provide remote temperature measuring. Two alarm setpoints can be set with either an audible or a visual alarm if the measured temperature goes out of the set limits.

The interval between temperature measurements can be adjusted from once every couple of seconds to once every four minutes. The longer the delay, the longer the battery will last between charges. Temperature can be measured & displayed in Fahrenheit or Celcius; changing between scales will automatically re-calculate the two setpoint temperatures.

 

The Main Screen

Switching the unit on displays the main screen shown on the left.

The current temperature is shown in a larger font at the top of the screen, the up arrow indicating that the last change in temperature was rising (a down arrow would indicate a falling temperature). If the current temperature reading is the same as the previous one, the up (or down) arrow will still be shown but an '=' sign will also be displayed.

This is useful when the temperature changes only very slowly but it's important to know if it's rising or falling (measuring the temperature inside a fridge, for example).

The maximum and minimum measured temperatures are also shown (Not to be confused with the High and Low alarm setpoints described below).

The battery voltage is shown at the bottom left of the screen. To protect the battery from over-discharging, the software will shut the monitor down so that it takes only microAmps of current when the voltage falls below 3.1 volts.

The monitor 'sleeps' in very low power mode for much of its time, waking briefly once a second to update the countdown timer shown at the bottom right of the screen. When the countdown timer reaches zero seconds, the monitor will measure the temperature and update the full display. The sleep time can be adjusted in the Setup menu (see below) from 240 seconds (4 minutes) to always awake, in 1-second increments.

To take a temperature measurement immediately, the DOWN button can be pressed, which will over-ride the sleep timer, wake up the monitor and update the display immediately.


The Alarm Setpoints Screen

Two alarm setpoints can be set. Each setpoint can be enabled and disabled individually (by setting the 'Y' or 'N'). The alarm can be either a buzzer or the display backlight can flash. This option is set in the Setup Menu shown below.

To display the Alarm Setpoints screen, the centre of the 5-way navigation switch is pushed once. The arrow at the left of the screen indicates whether the High or Low setpoint is to be adjusted. The UP and DOWN buttons on the navigation switch selects High, Low or Save & Exit.

The Navigation switch's RIGHT and LEFT buttons adjust the appropriate setpoint value up and down respectively. The values change in 0.5 °C increments.

PUSH the centre button on the navigation switch to toggle the setpoint alarm between Enabled (Y) and Disabled (N). When an alarm is sounding (or the screen backlight is flashing), pressing the PUSH button while the main screen is displayed will silence the alarm but an asterisk (*) will appear to the right of the Max or Min value on the main screen (to show which value has been exceeded) until the value is once again within the setpoint limits. The setpoint alarm will still be enabled so the alarm will sound again the next time the temperature goes out of bounds.


The Setup Screen

The Setup menu is accessed by holding down the 5-way navigation switch's centre PUSH button while turning on the power. Here the contrast of the display, the delay between temperature readings and the type of setpoint alarm can be selelcted.

Select the option using the UP and DOWN buttons and change the value of an option with the RIGHT and LEFT buttons. The Sleep setting can be adjusted from 0 to 240 seconds. If set to 0, the display will update as fast as the temperature can be read - approximately every 2 seconds - but the monitor will never go into power-saving sleep mode (except in the case of a low battery voltage as noted above). With settings greater than 0, the unit enters a low power, sleep, mode between each reading so don't take more frequent readings than necessary so as to preserve battery life.

The display can be switched between Fahrenheit and Celcius (using the RIGHT and LEFT buttons) and automatically converts the alarm setpoints to the appropriate scale. As the values are rounded to the nearest 0.5°, there will be some accumulated error in the values if the Scale is switched back and forth.

When displaying either the Setup menu or the Alarm Setpoint menu, the monitor cannot go into power-saving mode so it will automatically switch to the main screen if no button is operated for 60 seconds.


The Circuit

The circuit is fairly straightforward. The 5-way navigation switch is connected to the ATmega328 digital pins D2, D3, D4, D7 and D8. Those pins were mostly chosen for convenience in the printed circuit board layout except that PUSH and DOWN must be connected to digital pins D2 and D3 respectively. As the software puts the ATmega328 into low power sleep mode for much of the time, the alarm setpoint menu, the audible alarm 'accept' signal and the LCD backlight on/off have to be accessed using interrupts to wake the ATmega328 from its sleep.

The PUSH button, connected to digital pin D2, invokes interrupt 0 (INT 0) (to display the setpoint menu or silence the alarm) and the DOWN button on digital pin D3 invokes interrupt 1 (INT 1) to toggle the LCD backlight and to take an immediate temperature reading.

The audible alarm buzzer is connected to digital pin D6. I used a 6 volt miniature buzzer as it provided good volume. A miniature speaker of the type used to provide the beeps as a PC powers up can be used instead of the buzzer as D6 is a PWM-capable pin and the software switches the pin's output with a 50% mark-space output. However, I found the speaker's volume to be much less than the buzzer.

I used software SPI to connect the Nokia 5110 LCD display, mainly for convenience in laying out the PCB.

A Maxim (Dallas) DS18B20 temperature sensor is connected to the ATmega328 digital pin D10. The external sensor connects via a 3.5mm audio jack. When the jack plug is removed an internal sensor is connected instead.

The DS18B20 sensors are wired in parasitic mode so only two wires (Data and Ground) are needed to the external probe.

There is enough space in the enclosure that I used for a 1000mAh lithium-polymer rechageable battery so I included a 03962A charger module. This module (supposedly) protects the battery from excessive discharge but it's cut-off voltage is 2.4 volts -- way below the recommended 3.1 volts for lithium cells so, as noted above, the software switches to very low power mode when the voltage falls below 3.1 volts. The battery I used actually has its own internal protection hardware so that may well cut in anyway, ahead of the software protection.

Normally, it's not good practice to simultaneously charge a lithium battery and draw current from it because the charger IC won't stop charging while it's supplying current - it has no way of knowing whether it's charging the battery or supplying current to the downstream circuit.

Typically, charger ICs expect the charge current to drop to less than 10% of the rated charge current before terminating the charge. In the 03962A charger module, the rated current is 1000mA and the cut-off current is around 100mA. As this circuit draws less than 8mA when it's reading the temperature and less than 1mA when it's asleep, there's no risk of the charger failing to cut out when the battery is fully charged.

These modules are generally listed on eBay as "TP4056 Battery protection LIPO Charger Module" or "TE420." There's an in-depth review of the 03962A charger module on Julian Ilett's You Tube page.

The power on/off switch disconnects the battery from both the main circuit and the 03962A charger module. This was more convenient for the PCB layout but has the advantage that the switch isolates the battery completely from all the electronics. As with all my projects which use a lithium battery, I included a resettable polyfuse.

The Printed Circuit Board

Note: There are several different pinouts on the Nokia 5110 LCD displays. This PCB is designed for the type with parallel tracks as shown here. Although most pins are accessed through the software and are, therefore, interchangeable to some extent, Vcc, GND and the LED backlight's series resistor are hardware-connected and the PCB would need some modification to accomodate other Nokia 5110 displays.

  

 

Download PCB artwork in PCB Wizard format.

Download PCB artwork in PDF format.

 

Components

ATmega328 with Arduino bootloaderHobbytronics
Miniature slide switch Hobbytronics
104x64x28mm enclosureeBay (Eltop Electronics)
TP4056 (03962A) Lithium battery charger moduleeBay
RVFM 6V Buzzer (Low Profile)Rapid Electronics

 

Required Additional Arduino Libraries

Low-Power Library

Arduino-Temperature-Control-Library

Adafruit-GFX-Library

Adafruit-PCD8544-Nokia-5110-LCD-library

 

Arduino Sketch


#include "LowPower.h"

#include <EEPROM.h>

#include <OneWire.h>
#include <DallasTemperature.h>

#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
#include <Fonts/FreeSansBold9pt7b.h>


#define CE A4                          // Define pins used for software SPI for LCD
#define RST A3 
#define DC A2
#define DIN A1 
#define CLK A0 

Adafruit_PCD8544 lcd = Adafruit_PCD8544(CLK, DIN, DC, CE, RST);


#define ONE_WIRE_BUS 10                 // Dallas temperature sensor on pin D10
 
OneWire oneWire(ONE_WIRE_BUS);
 
DallasTemperature sensors(&oneWire);

#define PUSH 2                         // Define pins for 5-way navigation switch
#define DOWN 3
#define LEFT 4
#define UP   7

#define RIGHT 8

#define SOUNDER 6                      // Output pin for alarm sounder
#define LED 9                          // LCD backlight

#define chargingThreshold 4150         // Battery voltage when charging

float maxTemp = -99.0;                 // Starting values for temperature Max and Min.
float minTemp = 99.0;

float highSetpoint = 100.0;            // Starting values for High and Low alarm setpoints.       
float lowSetpoint = 0.0;

boolean degreesC = true;
boolean highAlarmEnabled = false;
boolean lowAlarmEnabled = false;
boolean alarmSounding = false;
boolean alarmReset = false;
boolean lowAlarm = false;
boolean highAlarm = false;
boolean alarmTypeBuzzer = true;  

boolean firstReading = true;
 
volatile boolean resetAlarm = false;             
volatile byte light = HIGH;
volatile byte lowPowerCounter = 0;
volatile unsigned long lightTimer;
volatile boolean isMenu = false;

char buff[6];

float temp;
float lastTemp = 0.0;
byte lastChange = 0;

int contrast = 55;                     // Default contrast. Can be changed by holding PUSH down during power-up


int menuMode = 15;
int setupMode = 0;

byte sleepTime = 10;
byte lightCounter;


void lightISR() {                         // Interrupt for backlight on/off (INT 1)
  if (!isMenu) {                          // If setpoint menu is NOT being displayed,
    light = !light;                       // toggle display backlight on/off. 
    if (light) {
      lightTimer = millis();              // Set timer to turn backlight off automatically.
    }
    digitalWrite(LED, light);
  }
  lowPowerCounter = sleepTime;            // Bring the ATmega328 out of sleep mode.
}

void menuISR() {                          // Interrupt for menu and alarm reset  (INT 0)

  if (alarmSounding ) {                   // If alarm is sounding, reset BUZZER and LED.
    resetAlarm = true;
    
   } else {                               // If alarm is NOT sounding...
    lowPowerCounter = sleepTime;          // Tell main loop to end low power sleep time and..
    isMenu = true;                        // Tell main loop to display setpoint menu
  }  
  
}

void setup(void) {
  CLKPR = 0x80;     // Prepare to change the internal clock
  CLKPR = 0x01;     // 8 MHz clock

  pinMode(RST, OUTPUT);                   // Nokia 5100 LCD pins
  pinMode(CE, OUTPUT);        
  pinMode(DC, OUTPUT);         
  pinMode(DIN, OUTPUT);       
  pinMode(CLK, OUTPUT);

  pinMode(SOUNDER, OUTPUT);
  pinMode(LED, OUTPUT);

  pinMode(10, OUTPUT);
  
  pinMode(PUSH, INPUT_PULLUP);    // INT 0
  pinMode(DOWN, INPUT_PULLUP);    // INT 1
  pinMode(UP, INPUT_PULLUP);
  pinMode(LEFT, INPUT_PULLUP);
  pinMode(RIGHT, INPUT_PULLUP);

  /* This code checks two EEPROM locations to check if 'unique' values
   *  have previously been saved. If they have, system parameters are
   *  retrieved from EEPROM. Otherwise, default values are used, saved
   *  to EEPROM and the two EEPROM check locations updated with 
   *  the 'unique' values.
   */

  if ((EEPROM.read(20) == 55) && (EEPROM.read(21) == 0)) {

    highSetpoint = constrain(eepromReadFloat(0), -127, 255);
    lowSetpoint = constrain(eepromReadFloat(4), -127, 255);

    if (isnan(highSetpoint)) highSetpoint = 100.0;
    if (isnan(lowSetpoint)) lowSetpoint = 0.0;
  
    highAlarmEnabled = constrain(EEPROM.read(8), 0, 1);
    lowAlarmEnabled = constrain(EEPROM.read(9), 0, 1);

    contrast = constrain(EEPROM.read(10), 30, 70);
    sleepTime = constrain(EEPROM.read(11), 0, 240);
    degreesC = constrain(EEPROM.read(12), 0, 1);
    alarmTypeBuzzer = constrain(EEPROM.read(13), 0, 1);
  } else {
    
    highSetpoint = 100.0;
    lowSetpoint = 0.0;
    highAlarmEnabled = false;
    lowAlarmEnabled = false;
    contrast = 55;
    sleepTime = 10;
    degreesC = true;
    alarmTypeBuzzer = true;
    
    eepromWriteFloat(0, highSetpoint);  
    eepromWriteFloat(4, lowSetpoint);
    EEPROM.write(8, highAlarmEnabled);
    EEPROM.write(9, lowAlarmEnabled);
    EEPROM.write(10, contrast);       
    EEPROM.write(11, sleepTime);
    EEPROM.write(12, degreesC);
    EEPROM.write(13, alarmTypeBuzzer);  
    EEPROM.write(20, 55);
    EEPROM.write(21, 0);
  }


  lcd.begin(contrast);             // set LCD contrast
  lcd.clearDisplay();
  lcd.display();

  if (digitalRead(PUSH) == LOW) {   // If PUSH is held down during power-up, 
    adjustSetup();                  // display Setup menu.
    lcd.clearDisplay();
    lcd.display();
    light = HIGH;
    isMenu = false;
    while(digitalRead(PUSH) == LOW);
    delay(200);
  }

  isMenu = false;
  attachInterrupt(digitalPinToInterrupt(2), menuISR, FALLING);
  attachInterrupt(digitalPinToInterrupt(3), lightISR, RISING);  

  lcd.print(F("Starting.."));
  lcd.display();
  sensors.begin();
}

void adjustSetup() {
  setupMode = 0;
  unsigned long cutoff = millis();
  while (setupMode < 40)  {                    // while cursor is not on 4th row.
    displaySetupMenu();
    if (setupMode == 0) {                      // Cursor on top row. Adjust contrast
      long unsigned int timer = millis();
      if (digitalRead(LEFT) == LOW) {
        if (contrast > 30) contrast--;
        delay(100);
        cutoff = millis();
      }
      if (digitalRead(RIGHT) == LOW) {
        if (contrast < 70) contrast++;
        delay(100);
        cutoff = millis();
      } 
    }

    if (setupMode == 10) {                      // Cursor on 2nd row. Adjust sleep timer
       if (digitalRead(LEFT) == LOW) {
        if (sleepTime > 0) sleepTime--;
        delay(100);
        cutoff = millis();
      }
      if (digitalRead(RIGHT) == LOW) {
        if (sleepTime < 240) sleepTime++;
         delay(100);
         cutoff = millis();
      }      
    }

    if (setupMode == 20) {                      // Cursor on 3rd row. Alarm auto reset, yes/no.
       if (digitalRead(LEFT) == LOW) {
        alarmTypeBuzzer = !alarmTypeBuzzer;
        while (digitalRead(LEFT) == LOW);
        cutoff = millis();
      }
      if (digitalRead(RIGHT) == LOW) {
        alarmTypeBuzzer = !alarmTypeBuzzer;
         while (digitalRead(RIGHT) == LOW);
         cutoff = millis();
      }            
    }

    if (setupMode == 30) {                      // Cursor on 3rd row. Alarm auto reset, yes/no.
       if (digitalRead(LEFT) == LOW) {
        degreesC = !degreesC;
        while (digitalRead(LEFT) == LOW);
        cutoff = millis();
      }
      if (digitalRead(RIGHT) == LOW) {
        degreesC = !degreesC;
         while (digitalRead(RIGHT) == LOW);
         cutoff = millis();
      }            
    }    

    if (digitalRead(DOWN) == LOW) {                       // Up/down buttons to move cursor vertically
      if (setupMode < 40)  setupMode = setupMode + 10;
      while (digitalRead(DOWN) == LOW);
      cutoff = millis();
    }
    if (digitalRead(UP) == LOW) {
      if (setupMode > 0) setupMode = setupMode - 10;
      while (digitalRead(UP) == LOW);
      cutoff = millis();
    }

    if ((millis() - cutoff) > 30000) setupMode = 40;     // Quit after 60 seconds
  }

  displaySetupMenu();
  if (EEPROM.read(10) != contrast) EEPROM.write(10, contrast);       // Save any changed values.
  if (EEPROM.read(11) != sleepTime) EEPROM.write(11, sleepTime);
  
  if (EEPROM.read(12) != degreesC) {              // If display (degrees C or degrees F) has been
    EEPROM.write(12, degreesC);                   // changed, save new C or F flag and
    if (degreesC) {                               // convert high and low setpoint values to show the
      highSetpoint = F2C(highSetpoint);           // new scale.
      lowSetpoint = F2C(lowSetpoint);
    } else {
      highSetpoint = C2F(highSetpoint);
      lowSetpoint = C2F(lowSetpoint);
    }
    if (highSetpoint != eepromReadFloat(0)) eepromWriteFloat(0, highSetpoint);  // Save the changes in EEPROM
    if (lowSetpoint != eepromReadFloat(4)) eepromWriteFloat(4, lowSetpoint);    
  } 
  if (EEPROM.read(13) != alarmTypeBuzzer) EEPROM.write(13, alarmTypeBuzzer);
  
  if (digitalRead(PUSH) == LOW);
 
}

float F2C(float t) {                    // Convert Fahrenheit to Celcius
  float v = (t - 32) * 0.55;
  return (round(v * 2) / 2 );           // Round value to nearest 0.5 degrees
}

float C2F(float t) {                    // Convert Celcius to Fahrenheit
  float v = (t * 1.8) + 32;
  return (round(v * 2) / 2);            // Round value to nearest 0.5 degrees.
}

void displaySetupMenu() {               // Display setup menu
  lcd.setContrast(contrast);
  lcd.clearDisplay();
  
  lcd.setCursor(7, 0);
  lcd.print("Contrast: ");
  lcd.print(contrast);
  
  lcd.setCursor(7, 10);
  lcd.print("Sleep: ");
  sprintf(buff,"%3ds", sleepTime);
  lcd.print(buff);  

  lcd.setCursor(7, 20);
  lcd.print("Alarm: ");
  alarmTypeBuzzer ? lcd.print("BUZZ") : lcd.print("LED");

  lcd.setCursor(7, 30);
  lcd.print("Scale: ");
  lcd.write(247);
  degreesC ? lcd.print("C") : lcd.print("F");
  
  lcd.setCursor(7, 40);
  lcd.print("Save & Exit");
  
  lcd.setCursor(0, setupMode);
  lcd.write(26);
  lcd.display();  
      
}

void adjustSetpoints() {
  delay(50);

  while(digitalRead(PUSH) == LOW);
  menuMode = 15;
  unsigned long cutoff = millis();
  while (menuMode < 35) {
    displaySetpointsMenu();

    if (menuMode == 15) {
      if (digitalRead(RIGHT) == LOW) {
        highSetpoint = highSetpoint + 0.5;
        delay(50);
        cutoff = millis();
      }
      if (digitalRead(LEFT) == LOW) {
        highSetpoint = highSetpoint - 0.5;
        delay(50);
        cutoff = millis();
      } 

      if (digitalRead(PUSH) == LOW) {
        highAlarmEnabled = !highAlarmEnabled;
        alarmReset = true;
        highAlarm = false;
        while(digitalRead(PUSH) == LOW);
        cutoff = millis();
      }
    }
    
    if (menuMode == 25) {
     if (digitalRead(RIGHT) == LOW) {
        lowSetpoint= lowSetpoint + 0.5;
        delay(50);
        cutoff = millis();
      }
      if (digitalRead(LEFT) == LOW) {
        lowSetpoint = lowSetpoint - 0.5;
        delay(50);
        cutoff = millis();
      }  
      if (digitalRead(PUSH) == LOW) {
        lowAlarmEnabled = !lowAlarmEnabled;
        alarmReset = true;
        lowAlarm = false;
        while(digitalRead(PUSH) == LOW);
        cutoff = millis();
      }
    }    

    if (digitalRead(DOWN) == LOW) {
      if (menuMode < 35) menuMode = menuMode + 10;               // Don't allow exit if
      if ((lowSetpoint >= highSetpoint) &&  (menuMode == 35)){   // low setpoint is above
        menuMode = 25;                                           // high setpoint.
      }
      while (digitalRead(DOWN) == LOW);
      cutoff = millis();
    }
    
    if (digitalRead(UP) == LOW) {
      if (menuMode > 15) menuMode = menuMode - 10;
      while (digitalRead(UP) == LOW);
      cutoff = millis();
    }
    if ((millis() - cutoff) > 30000) menuMode = 35;     // Quit after 60 seconds
  }
  
  displaySetpointsMenu();
  
  if (highSetpoint != eepromReadFloat(0)) eepromWriteFloat(0, highSetpoint);
  if (lowSetpoint != eepromReadFloat(4)) eepromWriteFloat(4, lowSetpoint);
  if (highAlarmEnabled != EEPROM.read(8)) EEPROM.write(8, highAlarmEnabled);
  if (lowAlarmEnabled != EEPROM.read(9)) EEPROM.write(9, lowAlarmEnabled);
  
  if (digitalRead(PUSH) == LOW);
  delay(250);
  isMenu = false;
}


void displaySetpointsMenu() {
  lcd.clearDisplay();
  lcd.print("Alarms ");
  lcd.write(247);
  degreesC ? lcd.print("C"): lcd.print("F");
  lcd.drawLine(0,9,80,9, BLACK);

  lcd.setCursor(7, 15);
  lcd.print("High: ");
  dtostrf(highSetpoint, 5, 1, buff);
  lcd.print(buff);
  lcd.setCursor(77, 15);
  highAlarmEnabled? lcd.print("Y") : lcd.print("N");
  
  lcd.setCursor(7, 25);
  lcd.print("Low:  ");
  dtostrf(lowSetpoint, 5, 1, buff);
  lcd.print(buff);
  lcd.setCursor(77, 25);
  lowAlarmEnabled? lcd.print("Y") : lcd.print("N");
  
  lcd.setCursor(7, 35);
  if (lowSetpoint >= highSetpoint) lcd.print("!");
  lcd.print("Save & Exit");
    
  lcd.setCursor(0, menuMode);
  lcd.write(26);
  lcd.display();  
}
 
void loop(void) {

  if (isMenu) {
    adjustSetpoints();
  }
  
  if (resetAlarm) {             // resetAlarm is a flag set in PUSH button interrupt
    alarmReset = true;          // service routine when (only when) alarm is sounding.
    alarmSounding = false;

    isMenu = false;
    alarmTypeBuzzer ? digitalWrite(SOUNDER, LOW) : digitalWrite(LED, LOW);
    delay(1000);
    while (digitalRead(PUSH) == LOW); 
  }
  
  resetAlarm = false;
  
  lcd.clearDisplay();
  
  float volts = readVcc();
  if (volts <= 3100) {                          // Get battery volts. If below 3.1v,
    lcd.println("Charge");                      // Display "Charge Battery" and
    lcd.print("Battery");                       // switch to low power. Shut down
    lcd.display();                              // ATmega328 until power switch off/on.
    light = LOW;
    digitalWrite(LED, light);
    digitalWrite(SOUNDER, LOW);
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); 
  }
 
  sensors.requestTemperatures(); // Send the command to get temperatures

  if (degreesC) {
    temp = sensors.getTempCByIndex(0); 
  } else {
    temp =  sensors.getTempFByIndex(0);
  }
  maxTemp = max(temp, maxTemp);
  minTemp = min(temp, minTemp);

  if ((temp < highSetpoint) && (temp > lowSetpoint)) {
    alarmReset = false;
  }

  if ( (highAlarmEnabled && (temp >= highSetpoint))  ) {
    highAlarm = true;
  } else {
    highAlarm = false;
  }

  if ( (lowAlarmEnabled && (temp  <= lowSetpoint))  ) {
    lowAlarm = true;
  } else {
    lowAlarm = false;
  }

  if (!alarmReset && (highAlarm || lowAlarm)) {
     alarmSounding = true;
     if (alarmTypeBuzzer ) { 
       analogWrite(SOUNDER, 127);
     } else {
       digitalWrite(LED, !digitalRead(LED));
     }
  }
  

  if (!highAlarm && !lowAlarm && !light) {
    alarmSounding = false;
    alarmTypeBuzzer ? digitalWrite(SOUNDER, LOW) : digitalWrite(LED, LOW);
  }

  lcd.setFont(&FreeSansBold9pt7b);      // Use larger font
  lcd.setCursor(17, 12);
  dtostrf(temp, 5, 1, buff);            // Right-justify temperature value
  lcd.print(buff);

  lcd.setFont();                        // Restore default font
  lcd.setTextSize(1);                   // Restore small font size
  
  lcd.setCursor(60, 0);
  lcd.write(247);
  degreesC ? lcd.print("C") : lcd.print("F");

  if (temp > lastTemp) lastChange = 1;  // Increasing temp.
  if (temp < lastTemp) lastChange = 2;  // Decreasing temp.

  if (!firstReading) {
  
    lcd.setCursor(77, 0);
    if (lastChange == 1) {
       lcd.write(24);                     // Print up arrow
    }
    if (lastChange == 2) {
       lcd.write(25);                      // Print down arrow
    }
    if (temp == lastTemp) {
      lcd.setCursor(77, 8);
      lcd.print("=");                     // temperature not changed
    }                                     // print == sign.
  }

  firstReading = false;
  lastTemp = temp;                      // remember current temperature
   
  lcd.setCursor(0, 19);
  lcd.print("Max: ");
  dtostrf(maxTemp, 5, 1, buff);         // Print max temperature measured,
  lcd.print(buff);                      // Right-justified
             
  lcd.write(247);
  degreesC ? lcd.print("C") : lcd.print("F");
  if  (highAlarm) {
    lcd.setCursor(74, 19);
    lcd.print("*");                     // Print '*' if temp is above alarm
  }                                     // high setpoint
  lcd.setCursor(0, 29);
  lcd.print("Min: ");
  dtostrf(minTemp, 5, 1, buff);         // Print minimum temperature measured,
  lcd.print(buff);                      // Right-justified.

  lcd.write(247);
  degreesC ? lcd.print("C") : lcd.print("F");
  if  (lowAlarm) {
    lcd.setCursor(74, 29);              // Print '*' if temp is below alarm
    lcd.print("*");                     // low setpoint
  }
  
  
  lcd.drawLine(0,38,80,38, BLACK);      
  lcd.setCursor(0,40);
  lcd.print(volts/1000, 2);
  lcd.print("v");
  lcd.fillRect(55,40, 30,7, WHITE);
  lcd.display();
   
  if (!isMenu && !alarmSounding && 
     (sleepTime > 0) & (volts < chargingThreshold) ) {         // Put ATmega328 to sleep if it's not
    lowPowerCounter = 0;                                       // displaying a menu and the alarm is not
    while (lowPowerCounter < sleepTime) {                      // sounding.  Sleep for 1 second and
      LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_OFF);          // increment the lowPowerCounter until
      lowPowerCounter++;                                       // it matches the desired sleepTime.
      
      lcd.fillRect(55,40, 30,7, WHITE);                        // Clear a rectangle to stop text
      lcd.setCursor(55,40);                                    // overwriting itself and print
      sprintf(buff,"%3ds", sleepTime - lowPowerCounter+1);     // seconds left in sleep mode.
      lcd.print(buff);
      lcd.display();

      checkTimedLight();
      if (readVcc() >= chargingThreshold) {
        lowPowerCounter = sleepTime;
      }
    }      
  } else {
    if (volts >= chargingThreshold) {
      lcd.fillRect(35,40, 45,7, WHITE);                       
      lcd.setCursor(35, 40);
      lcd.print("Charging");
      lcd.display();
    }
  }
}


void checkTimedLight() {
      if (light) {
        lightCounter++;                                        // If the LED backlight is lit, increment
        if (lightCounter >=10) {                               // the lightCounter and count 10 x 1 second
          light = LOW;                                         // sleeps then turn off the light.
          digitalWrite(LED, light);
          lightCounter = 0;
        }
      }     
}

// Get Battery Voltage 

long readVcc() {
  long result;
  // Read 1.1V reference against AVcc
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA,ADSC));
  result = ADCL;
  result |= ADCH<<8;
  result = 1113000L / result; // Back-calculate AVcc in mV
  return result;
}


float eepromReadFloat(int address){                            // Read/Write floating point numbers to EEPROM
  union u_tag {
    byte b[4];
    float fval;
   } u;   
   u.b[0] = EEPROM.read(address);
   u.b[1] = EEPROM.read(address+1);
   u.b[2] = EEPROM.read(address+2);
   u.b[3] = EEPROM.read(address+3);
   return u.fval;
}

void eepromWriteFloat(int address, float value){
   union u_tag {
     byte b[4];
     float fval;
   } u;
   u.fval=value;
 
   EEPROM.write(address  , u.b[0]);
   EEPROM.write(address+1, u.b[1]);
   EEPROM.write(address+2, u.b[2]);
   EEPROM.write(address+3, u.b[3]);
}



 

Back to Index

 


This site and its contents are © Copyright 2015 - All Rights Reserved.