Skip to content

Instantly share code, notes, and snippets.

@Koepel
Last active December 21, 2017 11:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Koepel/873e3bdf5c2171a3fd3dc012f8057e4b to your computer and use it in GitHub Desktop.
Save Koepel/873e3bdf5c2171a3fd3dc012f8057e4b to your computer and use it in GitHub Desktop.
Arduino sketch to test the Arduino board with freezing temperatures.
// Freezing Sketch
// ---------------
//
// December 2017
// Initial version.
// Using Arduino IDE 1.8.5
// For an Arduino Uno, Nano, Pro Mini.
// Not fully tested, but I did test it with freezing temperatures.
//
//
// For: http://forum.arduino.cc/index.php?topic=517522
//
// Goal: test the Arduino with low temperatures (in the freezer).
// Using the internal temperature sensor to store data in the EEPROM.
//
// Batteries will stop working or get damaged at very low temperatures.
// When the Arduino is put into a freezer, perhaps two thin wires can
// be used to keep the batteries or power pack outside the freezer.
//
// The internal temperature sensor is very inaccurate.
// A signed 8-bit char is good enough for the temperature.
// Storing that every 10 seconds in a 1024 bytes EEPROM is for a length of 2.8 hours.
//
//
// EEPROM Usage:
// address 0: Commands. For example auto recording at power up.
// address 1: Offset for internal temperature sensor
// address 2 and up: Temperature data
//
//
// TODO:
// Perhaps adding a DS18B20 temperature sensor.
// Sleep mode in case a small battery is used.
//
#include <EEPROM.h>
#define SAMPLE_INTERVAL 10000L // sample interval in ms
#define DATA_EMPTY -128 // -128 is defined as no data
#define CMD_NONE -128
#define CMD_AUTO_RECORD_ONCE -127 // command -127 for auto recording just once at power up
#define CMD_VALID -120 // starting number for a valid command
unsigned long previousMillis;
boolean recording;
int recording_index;
int offset;
void setup()
{
int valid = 0;
Serial.begin( 9600);
Serial.println();
Serial.println(F( "Freezing Sketch"));
Serial.println(F( "---------------"));
Serial.print(F( "EEPROM size = "));
Serial.print( EEPROM.length());
Serial.println(F( " bytes."));
Serial.print(F( "Recording time = "));
Serial.print( ((EEPROM.length() - 2) * SAMPLE_INTERVAL / 1000L) / 60);
Serial.println(F( " minutes."));
pinMode( LED_BUILTIN, OUTPUT); // use the system led to blink when recording.
delay( 500); // extra delay to be sure that this is a valid power up.
if( isEepromDataValid())
{
char c;
EEPROM.get( 0, c);
if( c == CMD_AUTO_RECORD_ONCE)
{
clear(); // clear previous recorded temperature data
recording = true;
recording_index = 2;
char cmd = CMD_NONE;
EEPROM.put(0, cmd); // don't start recording the next power up.
}
EEPROM.get( 1, c);
offset = int( c);
// Search for the number valid temperatures.
for( int i=2; i<EEPROM.length(); i++)
{
char c;
EEPROM.get( i, c);
if( c == DATA_EMPTY)
break;
valid++;
}
}
else
{
clearAll(); // clear eeprom and prepare it for our use.
recording = false;
offset = 0;
}
Serial.print(F( "Current temperature = "));
int t = int( GetTemp()) + offset;
Serial.print( t);
Serial.println(F( " °C"));
if( recording)
{
Serial.println(F( "Auto recording !"));
}
else if( valid > 0)
{
Serial.print(F( "Number of stored temperatures = "));
Serial.println( valid);
}
else
{
Serial.println(F( "No stored temperatures at the moment."));
}
Serial.println(F( "Type 'h' or '?' for help"));
}
void loop()
{
unsigned long currentMillis = millis();
if( Serial.available() > 0)
{
int inByte = Serial.read(); // read a byte, it can be '\n' or '\r' as well.
inByte = tolower( inByte); // make all commands lower case for this sketch
switch( inByte)
{
case 'a':
{ // workaround to allow local variables.
Serial.print(F( "Auto recording at the next power up is enabled."));
char c = CMD_AUTO_RECORD_ONCE;
EEPROM.put( 0, c);
}
break;
case 'c':
Serial.print(F( "Clearing EEPROM... "));
clear();
Serial.println(F( "done."));
break;
case 'd':
// Dump the data.
// To be able to export it to a graph program.
Serial.println(F( "---begin---"));
for( int i=2; i<EEPROM.length(); i++)
{
char c;
EEPROM.get( i, c);
if( c == DATA_EMPTY)
break;
int data = int( c);
Serial.print( data); // print as number, as integer
Serial.print( ',');
}
Serial.println();
Serial.println(F( "---end---"));
break;
case 'f':
// Show the data in a nice format.
// The time with hh:mm:ss
Serial.println(F( "hh:mm:ss °C")); // Let's go nuts and try a UTF-8 character for degrees.
Serial.println(F( "-------------"));
for( int i=2; i<EEPROM.length(); i++)
{
char c;
EEPROM.get( i, c);
if( c == DATA_EMPTY)
break;
int data = int( c);
int totalseconds = int( (long( i) * SAMPLE_INTERVAL) / 1000L);
int ss = totalseconds % 60;
int mm = (totalseconds / 60) % 60;
int hh = totalseconds / (60 * 60);
char buffer[40];
sprintf_P( buffer, PSTR("%02d:%02d:%02d %+d"), hh, mm, ss, data);
Serial.println( buffer);
}
Serial.println(F( "---end---"));
break;
case 'h':
case '?':
Serial.println();
Serial.println(F( "Freezing Sketch"));
Serial.println(F( "---------------"));
Serial.println(F( "Usage:"));
Serial.println(F( " a : Auto recording at the next power up (once)."));
Serial.println(F( " Previous stored temperatures will be erased."));
Serial.println(F( " c : Clear the temperature data."));
Serial.println(F( " d : Dump the temperature data."));
Serial.println(F( " f : Formatted display of the temperature data."));
Serial.println(F( " h : Help information."));
Serial.println(F( " ? : Help information."));
Serial.println(F( " r : Record the temperature for the next 2.8 hours."));
Serial.println(F( " Previous stored temperatures will be erased."));
Serial.println(F( " s : Stop recording."));
Serial.println(F( " t<temperature> : Set temperature to adjust offset."));
Serial.println(F( " Enter the actual temperature after the 't'."));
Serial.println(F( " For example: t21"));
Serial.println(F( " x : Clear entire EEPROM, also the temperature offset."));
break;
case 'r':
Serial.println(F( "Recording"));
Serial.println(F( "You may dump ('d') the data"
" or use the formatted display of the data ('f')"
" while recording."));
Serial.println(F( "You may disconnect the Arduino board from the USB now,"
" but keep it powered with a battery."
" Put the Arduino in a freezer to test it."));
clear(); // clear eeprom, but keep the temperature offset.
recording = true; // enable recording
recording_index = 2; // temperature data starts at eeprom address 2
char c;
EEPROM.get( 1, c); // read the offset
offset = int( c); // keep offset in global variable during recording
previousMillis = currentMillis; // reset the software timer
break;
case 's':
Serial.println(F( "Stopped recording"));
recording = false;
break;
case 't':
{ // use '{' and '}' as workaround to allow local variables.
Serial.println(F( "Temperature offset adjust."));
// Read the offset from the serial monitor,
// and convert everything to normal integers.
long temperature_long = Serial.parseInt(); // use timeout, default 1 second
int temperature = int( temperature_long);
float unadjusted_float = GetTemp();
int unadjusted = int( unadjusted_float);
offset = temperature - unadjusted; // offset is a global variable.
Serial.print(F( "Received current temperature: "));
Serial.println( temperature);
Serial.print(F( "Temperature before offset: "));
Serial.println( unadjusted);
Serial.print(F( "Adjusted temperature: "));
Serial.println( unadjusted + offset);
char c = char( offset);
EEPROM.put( 1, c);
}
break;
case 'x':
Serial.print(F( "Clearing entire EEPROM, also temperature offset... "));
clearAll();
recording = false; // for safety, stop any recording
Serial.println(F( "done."));
break;
case '!':
// Undocumented extras.
delay( 5); // wait for second character to arrive
int x = Serial.read();
x = tolower( x);
switch( x)
{
case 'v':
Serial.print(F( "Undocumented: Erase... "));
erase();
Serial.println(F( "done."));
break;
case 'w':
Serial.println(F( "Undocumented: Software Reset"));
delay( 40); // allow the text to be written to the serial monitor
asm volatile ("jmp 0"); // bad code, don't use it.
break;
}
break;
}
}
if( recording)
{
if( currentMillis - previousMillis >= SAMPLE_INTERVAL)
{
previousMillis += SAMPLE_INTERVAL; // increment with interval to keep in sync with time.
float temperature = GetTemp(); // get the temperature
int data = int( temperature); // convert to integer.
data += offset; // correct with global integer 'offset'
char c = char( data); // convert to signed 8-bits char
EEPROM.put( recording_index, c); // store it
if( data >= 0) // the sprinf format "%+d" puts an '+' in front of '0', let's do the same here.
Serial.print(F( "+"));
Serial.println( data);
recording_index++;
if( recording_index >= EEPROM.length()) // EEPROM full ?
{
recording = false; // stop recording
}
// Blink the system led to show that it is recording
digitalWrite( LED_BUILTIN, HIGH);
delay( 250);
digitalWrite( LED_BUILTIN, LOW);
}
}
}
void clear()
{
// Clear the EEPROM, but only the temperature data.
char c = DATA_EMPTY;
for( int i=2; i<EEPROM.length(); i++) // temperature data starts at 2
{
EEPROM.put( i, c);
}
}
void clearAll()
{
// Clear the EEPROM.
char c = CMD_NONE;
EEPROM.put( 0, c); // command
c = 0;
EEPROM.put( 1, c); // temperature offset
clear(); // clear temperature data.
}
void erase()
{
// Erase the etire EEPROM, all of it.
// Write it with 0xFF, as if it was a new Arduino board.
byte b = 0xFF;
for( int i=0; i<EEPROM.length(); i++)
{
EEPROM.put( i, b);
}
}
boolean isEepromDataValid()
{
// Test the data of the EEPROM, to check if it is valid.
char data[4];
EEPROM.get( 0, data); // read the first 4 bytes.
if(( data[0] == 0xFF && data[1] == 0xFF && data[2] == 0xFF && data[3] == 0xFF) ||
( data[0] > CMD_VALID) ||
( data[1] == -128 || data[1] == 127))
{
// The EEPROM seems to have no valid data.
return( false);
}
return( true);
}
// Get the temperature in Celsius by using the internal temperature sensor.
// Taken from:
// https://playground.arduino.cc/Main/InternalTemperatureSensor
float GetTemp(void)
{
unsigned int wADC;
double t;
// The internal temperature has to be used
// with the internal reference of 1.1V.
// Channel 8 can not be selected with
// the analogRead function yet.
// Set the internal reference and mux.
ADMUX = (_BV(REFS1) | _BV(REFS0) | _BV(MUX3));
ADCSRA |= _BV(ADEN); // enable the ADC
delay(20); // wait for voltages to become stable.
ADCSRA |= _BV(ADSC); // Start the ADC
// Detect end-of-conversion
while (bit_is_set(ADCSRA,ADSC));
// Reading register "ADCW" takes care of how to read ADCL and ADCH.
wADC = ADCW;
// The offset of 324.31 could be wrong. It is just an indication.
t = (wADC - 324.31 ) / 1.22;
// The returned temperature is in degrees Celsius.
return (t);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment