Go Down

Topic: Clock/temperature display for my Father (Read 5 times) previous topic - next topic

pocketscience

First bit:
Code: [Select]

/*
 WeatherStation:
   Created by Gavin Maxwell, 2010

*/

#include <WProgram.h>
#include <LiquidCrystal.h>
#include <EEPROM.h>
#include <Wire.h>
#include <DS1307.h>

// --> START PIN ASSIGNMENTS

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd( 17, 16, 12, 11, 10, 9 ); // modified for better board routing

#define TEMP_PIN 0            // analog IN pin 0
#define TEMP_REF_PIN 1        // analog IN pin 1
#define RESET_MINMAX_PIN  13  // button to reset the stored values in EEPROM

#define INT_0 0  // SQW interrupt from DS1307

// --> END PIN ASSIGNMENTS

int rtc[ DS1307_MAX_ITEMS ];
char* DOW[8] = { "", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };

#define MV_PER_DEGREE 10 // each 10mV is 1C
#define FULLSCALE_MV ((float)(4700)) // 5000 mV = 1024 from the ADC - actually 4700mV is approx what is supplied to LM35
#define FULLSCALE_ADC ((float)(1024)) // ADC returns from 0 - 1023 so 1024 unique values
#define MV_PER_ADC_STEP ((float)( FULLSCALE_MV / FULLSCALE_ADC )) // mV per ADC increment
#define INTER_READ_DELAY 20 // milliseconds between reads
#define UPDATE_INTERVAL  10000 // milliseconds between updates
#define USE_GROUND_REF 1

#define TEMP_SAMPLES 20 // number of samples to average

// EEPROM Addresses
#define MIN_TEMP_ADDRESS  0x00
#define MAX_TEMP_ADDRESS  0x01
#define CLOCK_SET_ADDRESS 0x02
#define CALIBRATION_ADDRESS 0x03  // NOT USED

unsigned long lastUpdate;
long minTemp = 255;
long maxTemp = -255;
long LM35Calibration = -1;  // Rough as nails

boolean showColon = true; // flag to blink the colon seperator

#define DEGREE_CHAR 4
#define STEP1_CHAR 5
#define STEP2_CHAR 6
#define STEP3_CHAR 7
#define ARROW_CHAR 0x7E

byte degree[8] = {
 B01100,
 B10010,
 B10010,
 B01100,
 B00000,
 B00000,
 B00000,
};
byte step1[8] = {
 B00000,
 B01110,
 B10001,
 B10001,
 B10001,
 B01110,
 B00000,
};
byte step2[8] = {
 B00000,
 B01110,
 B11111,
 B11011,
 B11111,
 B01110,
 B00000,
};
byte step3[8] = {
 B00000,
 B01110,
 B11111,
 B11111,
 B11111,
 B01110,
 B00000,
};

volatile boolean updateTime = true;

// DS1307 1Hz pulse ISR - set our flag and get out!
void pulseISR()
{
 updateTime = true;
}

//
// Setup all of our pins, initialise the RTC, serial port and LCD
//
void setup()
{
 int i, ndx;
 pinMode( TEMP_PIN, INPUT );
 pinMode( TEMP_REF_PIN, INPUT );
 
 pinMode( RESET_MINMAX_PIN, INPUT );
 digitalWrite( RESET_MINMAX_PIN, HIGH );  // internal pull-up - switch will pull low to indicate reset
 
 // DS1307 sends a 1Hz pulse - we use this to update the display  
 attachInterrupt( INT_0, pulseISR, FALLING );
 
 Serial.begin( 9600 );

 // read the stored min and max
 // values will be 255 if never written before
 // if max has never been recorded set it super low
 // to force an update first time we measure
 minTemp = EEPROM.read( MIN_TEMP_ADDRESS );
 maxTemp = EEPROM.read( MAX_TEMP_ADDRESS );
 if ( maxTemp == 255 )
   maxTemp = -255;
   
 // EEPROM stores a unsigned byte, so we need to handle
 // negatives. 200 limit OK as sensor goes down to -55 (0xC9, 201)
 if ( minTemp > 200 )
   minTemp = minTemp - 256;
 
 if ( maxTemp > 200 )
   maxTemp = maxTemp - 256;

 // if EEPROM has never been set then use a default date
 if ( EEPROM.read( CLOCK_SET_ADDRESS ) == 255 )
 {
   RTC.stop();
   RTC.set( DS1307_SEC, 1 );
   RTC.set( DS1307_MIN, 17 );
   RTC.set( DS1307_HR, 15 );
   RTC.set( DS1307_DOW, 6 );
   RTC.set( DS1307_DATE, 16 );
   RTC.set( DS1307_MTH, 10 );
   RTC.set( DS1307_YR, 10 );
   RTC.start();
   EEPROM.write( CLOCK_SET_ADDRESS, 1 );
 }
 
 // Define custom characters
 lcd.createChar( DEGREE_CHAR, degree );
 lcd.createChar( STEP1_CHAR, step1 );
 lcd.createChar( STEP2_CHAR, step2 );
 lcd.createChar( STEP3_CHAR, step3 );

 // set up the LCD's number of rows and columns:
 lcd.begin( 16, 2 );
 
 // Get the current time and display Xmas Card if it's Xmas!
 RTC.get( rtc, true );

 // SNIP FOR PRIVACY REASONS
 
 // enable 1Hz square wave output
 RTC.enableOutput();
 RTC.setOutputRate( 0 );
 
 lcd.clear();
 
 lastUpdate = millis() - UPDATE_INTERVAL;
}

//
// Simple formatter of numeric values
//
void BufferPrint( char *buffer, unsigned long value, unsigned long i, int bufLen )
{
 unsigned long temp;
 int bufindex = 0;

 if ( buffer )
 {
   memset( buffer, 0, bufLen );
   while ( i > 0 )
   {
     temp = value / i;
     value -= temp * i;
     buffer[ bufindex++ ] = (char)( '0' + temp );
     i/=10;
   }
 }
}

//
// Retrieves updated time from DS1307, formats it then displays on LCD
//
void UpdateTime()
{
 const int timeBufLen = 8;
 char buf[ timeBufLen ];

 RTC.get( rtc, true );

 lcd.setCursor( 0, 0 );
 BufferPrint( buf, rtc[ DS1307_HR ], 10, timeBufLen );
 lcd.print( buf );
 lcd.print( showColon ? ":" : " " );
 showColon = !showColon;
 BufferPrint( buf, rtc[ DS1307_MIN ], 10, timeBufLen );
 lcd.print( buf );

 lcd.print( " " );

 // Check DOW is a valid value
 if ( rtc[ DS1307_DOW ] <= 0 || rtc[ DS1307_DOW ] > 7 )
   lcd.print( "???" );
 else
   lcd.print( DOW[ rtc[ DS1307_DOW ] ] );
 lcd.print( " " );

 BufferPrint( buf, rtc[ DS1307_DATE ], 10, timeBufLen );
 lcd.print( buf );
 lcd.print( "/" );
 BufferPrint( buf, rtc[ DS1307_MTH ], 10, timeBufLen );
 lcd.print( buf );
 
 // reset flag until next interrupt
 updateTime = false;
}

//
// Decode the entered time and set the DS1307
// Format: THHMMSSWDDMMYY
// where W is 1->7 for Monday->Sunday
//
void HandleTimeSet()
{
 int inByte = Serial.read();
 
 if ( inByte == 't' || inByte == 'T' )
 {
   int b1, b2, hh, mm, ss, wd, dd, mo, yy;
   
   Serial.println( "SETTING THE TIME" );
   b1 = Serial.read();
   b2 = Serial.read();
   hh = ( ( b1 - '0')  * 10 ) + ( b2 - '0' );
   b1 = Serial.read();
   b2 = Serial.read();
   mm = ( ( b1 - '0')  * 10 ) + ( b2 - '0' );
   b1 = Serial.read();
   b2 = Serial.read();
   ss = ( ( b1 - '0')  * 10 ) + ( b2 - '0' );
   b1 = Serial.read();
   wd = ( b1 - '0');
   b1 = Serial.read();
   b2 = Serial.read();
   dd = ( ( b1 - '0')  * 10 ) + ( b2 - '0' );
   b1 = Serial.read();
   b2 = Serial.read();
   mo = ( ( b1 - '0')  * 10 ) + ( b2 - '0' );
   b1 = Serial.read();
   b2 = Serial.read();
   yy = ( ( b1 - '0')  * 10 ) + ( b2 - '0' );

   RTC.stop();
   RTC.set( DS1307_SEC, ss );
   RTC.set( DS1307_MIN, mm );
   RTC.set( DS1307_HR, hh );
   RTC.set( DS1307_DOW, wd );
   RTC.set( DS1307_DATE, dd );
   RTC.set( DS1307_MTH, mo );
   RTC.set( DS1307_YR, yy );
   RTC.start();
 }
 else if ( inByte == 'c' || inByte == 'C' )
 {
   Serial.println( "TEMPERATURE CALIBRATION NOT IMPLEMENTED YET" );
   
 }
}

//
// A press on the rest button over a set duration indicates
// we need to clear the current stored min and max temperatures
//
void CheckForResetMinMax()
{
 // Handle resetting the min and max temp values
 if ( digitalRead( RESET_MINMAX_PIN ) == LOW )
 {
   long press = millis();
   
   // spin whilst the buton is down
   while ( digitalRead( RESET_MINMAX_PIN ) == LOW )
   {
     lcd.setCursor( 0, 1 );
     if ( millis() > press + 1500 )
       lcd.print( "T:<RELEASE>     " );
     else
       lcd.print( "T:<HOLD 2 RESET>" );
   }
   
   // was it down long enough to indicate a real press
   if ( millis() > press + 1500 )
   {
     lcd.setCursor( 0, 1 );
     lcd.print( "T:<RESETTING>   " );

     minTemp = 255;
     maxTemp = -255;
     EEPROM.write( MIN_TEMP_ADDRESS, 255 );
     EEPROM.write( MAX_TEMP_ADDRESS, 255 );
   }

   // force a temp update
   lastUpdate = millis() - UPDATE_INTERVAL;
 }
}
Is life really that serious...??!

pocketscience

Second bit:
Code: [Select]

//
// We loop around here whilst ever we have power!
//
void loop()
{
 long temp;
 
 // Time to update the time?
 if ( updateTime )
   UpdateTime();
 
 // Any input to process?
 if ( Serial.available() )
   HandleTimeSet();

 // Has user pressed the reset button
 CheckForResetMinMax();
 
 // time to update the temp reading?
 if ( ( millis() - lastUpdate ) > UPDATE_INTERVAL )
 {
   temp = LM35Read();
   
   // Keep min and max values stored in EEPROM
   if ( temp < minTemp )
   {
     minTemp = temp;
     EEPROM.write( MIN_TEMP_ADDRESS, minTemp );
   }
   if ( temp > maxTemp )
   {
     maxTemp = temp;
     EEPROM.write( MAX_TEMP_ADDRESS, maxTemp );
   }
   
   lcd.setCursor( 0, 1 );
   lcd.print( "T:");
   lcd.print( temp );
   lcd.write( DEGREE_CHAR );  // custom degree sign
   lcd.print( "C " );
   lcd.print( "[" );
   lcd.print( minTemp );
   lcd.write( ARROW_CHAR );  // right facing arrow
   lcd.print( maxTemp );
   lcd.print( "]   " );
   lastUpdate = millis();
 }
}

//
// Take a reading from the sensor. We actually take multiple and average them.
// The sensor returns a voltage that maps directly to temperature
//
long LM35Read()
{
 int ndx;
 long adcVal = 0, mV;
 
 animateStart();
 for (ndx = 0; ndx < TEMP_SAMPLES; ndx++ )
 {
   mV = analogRead( TEMP_PIN );
   adcVal += mV;
#if ( USE_GROUND_REF )
   adcVal -= analogRead( TEMP_REF_PIN );
#endif
   delay( INTER_READ_DELAY );
   animateStep();
 }
 animateStop();

 // average it
 adcVal = (long)( adcVal / TEMP_SAMPLES );
 
 // convert value from ADC steps (0-1023) to mV
 mV = adcVal * MV_PER_ADC_STEP;

 // now convert mV to degrees C
 return( mV / MV_PER_DEGREE ) + LM35Calibration;
}


Note I started implementing a calibration mechanism but haven't completed that yet.
Is life really that serious...??!

convoluted

Thanks pocketscience, much appreciated!

Go Up