Last active
December 21, 2017 11:14
-
-
Save Koepel/873e3bdf5c2171a3fd3dc012f8057e4b to your computer and use it in GitHub Desktop.
Arduino sketch to test the Arduino board with freezing temperatures.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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