Project - TeensyClimate

From My notepad
Jump to: navigation, search

Contents

Project: TeensyClimate

The primary purpose of this project was to give me a reason to learn how to develop solutions using the Atmel AVR 8-bit microcontrollers.

The secondary purpose of this project is to develop a climate control system for the home.

Current Project Status

30 July 2011:

The code has been cleaned up a little bit (you should have seen versions 0.1-0.4!!!).

While I am using the uthash library, I know I'm not using it properly, but it works. While I wanted to implement a hash table, it's pretty much just creating a linked list for me and I'm iterating the list in various places. I've tried implementing a standard linked list using pointers (in version 0.6) but it's not working properly. The sensor objects are being created with the proper hardware addresses in each location, but the temperature readings are not being stored right, which is just strange. So for now I'm continuing to eat up some extra memory while I use the uthash library.

I'm trying to determine the best relay setup to use to control the attic fan. I guess I just need to get over it and by a $15-20 120-240V relay and be done with it.

To Date min's and max's

  • 7/31/2011 14:11:16 - Max 131.67 @ Attic
  • 8/3/2011 14:58:15 - Max: 135.16 @ Attic

Development Photos

Breadboarded project
  1. Teensy 2.0 Development Board
  2. LCD Contrast Potentiometer (10k)
  3. DS18B20 Programmable Resolution 1-Wire Digital Thermometer: two on board and one in attic
  4. CAT3 extending 1-Wire bus to sensor in attic
  5. HD44780 compatible LCD display
Serial console extended stats
Serial console CLI options
Graphed data from 8 Aug 2011 (dark green is attic temperature, others are inside temperature)

The CLI was updated to export data using a CSV format (millis,now(),each sensors last conversion...). A Python script was written to read the text from the serial interface and write it to a file. LiveGraph was used to read the file in real time and display the graphed data. The graph above begins at about 11:30 PM 7 Aug 2011 and ends about 11:30 PM 8 Aug 2011. One sample is output to the CLI every second. After roughly a 24 hour period the CSV file was 2.69MB in size.

Version

The current version is 0.5c.

Features

As of 0.5c (1 Aug 2011):

  • Global min/max with sensor name and timestamps

As of 0.5b (30 July 2011):

  • Dynamic sensor discovery on device power on
  • Per sensor last/min/max temperatures with timestamps
  • Non-blocking temperature conversions
  • Sensor data display on LCD (currently limited to the first two sensors)
  • Sensor data display on serial console (basic and extended)
  • Serial console control
  • Time update via serial console

To Do

  • Air Conditional Filter Change Reminder
  • Attic fan relay control
  • EEPROM storage of configuration information
  • Ethernet TCP/IP Interface using WizNet WIZ812MJ module
  • Historical statistics other than just current/min/max
    • avg/min/max per day
    • long term five minute interval graphing (via external means, SNMP possible)
  • Keypad for complete device control
  • PCB design
  • Final migration from breadboard to PCB

Source Code

  1. /*
  2. Starting point:
  3. http://tushev.org/articles/electronics/42-how-it-works-ds18b20-and-arduino
  4.  
  5. -= TODO =-
  6.  
  7. A/C Filter Change Reminder
  8.  
  9. -= EEPROM STORAGE MAP =-
  10.  1k bytes EEPROM available
  11.  Page size is 8 bytes
  12.  128 Pages
  13.  
  14.  Reserve the first page for empty (due to slot 0 possiblity of being corrupted)
  15.  
  16.  Reserve pages 2-9 for configuration data
  17.  
  18.  TIMEZONE_OFFSET_HOURS - char
  19.  DST - daylight savings time - byte
  20.  NUM_STORED_SENSORS - byte
  21.  AIRFILTER REPLACEMENT DATE
  22.  AIRFILTER LIFESPAN
  23.  
  24.  Pages 10 and up are reserved for stored sensors
  25.  
  26.  STORED SENSORS:
  27.  ADDR - byte[8] (1 page)
  28.  NAME - char[10] (1 page plus 2 bytes)
  29.  CALIBRATION_OFFSET - float (2 bytes)
  30.  
  31. */
  32.  
  33. //#define DEBUG 0
  34.  
  35. #define ACTIVITY_CHAR_INTERVAL 250
  36. #define BLINK_INTERVAL 250
  37. #define CONSOLE_UPDATE_INTERVAL 1000
  38. #define DEFAULT_MIN_TEMP 999
  39. #define DEFAULT_MAX_TEMP -99
  40. #define DEFAULT_TIME_ZONE_OFFSET -5  // CST during DST
  41. #define DS18B20_ID 0x28
  42. #define DS18B20_CONVERSION_WAIT_TIME 750
  43. #define LCD_CLEAR_INTERVAL 10000
  44. #define LCD_UPDATE_INTERVAL 1000
  45. #define LCD_STRING_BUFFER_LENGTH 21
  46. #define LED_PIN 11
  47.  
  48. #define CLI_STRING_BUFFER_LENGTH 50
  49. #define NUM_CLI_LINES 10
  50.  
  51. #include <avr/pgmspace.h>
  52. #include <MemoryFree.h>
  53. #include <LiquidCrystal.h>
  54. #include <Metro.h>
  55. #include <OneWire.h>
  56. #include <String.h>
  57. #include <Streaming.h>
  58. #include <Time.h>
  59. #include "C:\arduino-0022\projects\temp_03\uthash.h"
  60.  
  61. prog_char cli_line_1[] PROGMEM  = "CLI Options:";
  62. prog_char cli_line_2[] PROGMEM  = "B:     jump to bootloader";
  63. prog_char cli_line_3[] PROGMEM  = "C:     reset sensors stats";
  64. prog_char cli_line_4[] PROGMEM  = "d/D:   toggle lcd display contents";
  65. prog_char cli_line_5[] PROGMEM  = "e/E:   toggle the display of extended stats";
  66. prog_char cli_line_6[] PROGMEM  = "f/F:   show current free memory";
  67. prog_char cli_line_7[] PROGMEM  = "G:     reset global sensor stats";
  68. prog_char cli_line_8[] PROGMEM  = "h/H:   display this help";
  69. prog_char cli_line_9[] PROGMEM = "T:     time sync via console";
  70. prog_char cli_line_10[] PROGMEM = "?:     display this help";
  71.  
  72. PROGMEM const char *cli_string_table[] = 
  73. {
  74.   cli_line_1,
  75.   cli_line_2,
  76.   cli_line_3,
  77.   cli_line_4,
  78.   cli_line_5,
  79.   cli_line_6,
  80.   cli_line_7,
  81.   cli_line_8,
  82.   cli_line_9,
  83.   cli_line_10
  84. };
  85.  
  86. byte tempSensorAttic[8] = {
  87.   0x28, 0xdb, 0x32, 0x5b, 0x03, 0x00, 0x00, 0x8d};
  88. byte tempSensorInside1[8] = {
  89.   0x28, 0xb7, 0x4b, 0x5b, 0x03, 0x00, 0x00, 0xad};
  90. byte tempSensorInside2[8] = {
  91.   0x28, 0x39, 0x44, 0x5b, 0x03, 0x00, 0x00, 0x3b};
  92.  
  93. boolean lcdDisplayAddresses = false;
  94. boolean showExtendedStats = false;
  95.  
  96. unsigned int activityCharIndex = 0;
  97. unsigned int ledStatus = 0;
  98.  
  99. unsigned long loopIteration = 0;
  100. unsigned long loopStartMillis;
  101. unsigned long lastLoopDuration = 0;
  102.  
  103. LiquidCrystal lcd(16, 17, 12, 13, 14, 15);
  104.  
  105. #define NUM_METROS 6
  106.  
  107. Metro activityCharMetro(ACTIVITY_CHAR_INTERVAL, false);
  108. Metro consoleUpdateMetro(CONSOLE_UPDATE_INTERVAL);
  109. Metro ledMetro(BLINK_INTERVAL, false);
  110. Metro lcdUpdateMetro(LCD_UPDATE_INTERVAL);
  111. Metro lcdClearMetro(LCD_CLEAR_INTERVAL);  // counter to clear the lcd every 10 seconds for housekeeping
  112. Metro owBusSearchMetro(600000, false);  //  counter to search the bus every 10 minutes for new devices
  113.  
  114. OneWire ds(10);
  115.  
  116. float globalMin = DEFAULT_MIN_TEMP;
  117. char * globalMinName = NULL;
  118. time_t globalMinTimeStamp = (time_t) 0;
  119. float globalMax = DEFAULT_MAX_TEMP;
  120. char * globalMaxName = NULL;
  121. time_t globalMaxTimeStamp = (time_t) 0;
  122.  
  123. struct DS18B20 {
  124.   byte addr[8];              /* key */
  125.   char name[10];
  126.   boolean active;
  127.   boolean converting;  /* true if a conversion has been requested */
  128.   unsigned int crcerrors;
  129.   unsigned long startConversionLI;  // loop iteration of the start conversion
  130.   unsigned long liLastConversion;    // number of loop iterations for the last conversion
  131.   Metro conversionTimer;
  132.   float lastTemp;
  133.   time_t lastTimeStamp;
  134.   float minTemp;
  135.   time_t minTimeStamp;
  136.   float maxTemp;
  137.   time_t maxTimeStamp;
  138.   UT_hash_handle hh;         /* makes this structure hashable */
  139. };
  140.  
  141. struct DS18B20 *tempSensors = NULL;
  142.  
  143. boolean compareByteArray(byte a1[], byte a2[]) {
  144.   int arraySize = sizeof(a1)/sizeof(byte);
  145.   if (sizeof(a1) != sizeof(a2)) {
  146.     return false;
  147.   }
  148.   for(int i=0; i<arraySize; i++) {
  149.     if (a1[i] != a2[i]) {
  150.       return false;
  151.     }
  152.   }
  153.   return true;
  154. }
  155.  
  156. void add_sensor(byte addr[]) {
  157.   struct DS18B20 *s;
  158.  
  159.   s = (DS18B20 *) malloc(sizeof(struct DS18B20));
  160.   for (int i = 0; i < 8; i++) {
  161.     s->addr[i] = addr[i];
  162.   }
  163.   s->active = false;
  164.   s->converting = false;
  165.   s->crcerrors = 0;
  166.   s->conversionTimer = Metro(DS18B20_CONVERSION_WAIT_TIME, false);
  167.   s->liLastConversion = 0;
  168.   s->lastTemp = 0.0;
  169.   s->lastTimeStamp = now();
  170.   s->minTemp = DEFAULT_MIN_TEMP;
  171.   s->minTimeStamp = s->lastTimeStamp;
  172.   s->maxTemp = DEFAULT_MAX_TEMP;
  173.   s->maxTimeStamp = s->lastTimeStamp;
  174.   if (compareByteArray(s->addr,tempSensorAttic)) {
  175.     strcpy(s->name, "Attic");
  176.   } 
  177.   else if(compareByteArray(s->addr,tempSensorInside1)) {
  178.     strcpy(s->name, "Inside1");
  179.   } 
  180.   else if(compareByteArray(s->addr,tempSensorInside2)) {
  181.     strcpy(s->name, "Inside2");
  182.   } 
  183.   else {
  184.     strcpy(s->name, "Unknown");
  185.   }
  186.   HASH_ADD(hh, tempSensors, addr, sizeof(byte) * 8, s);
  187. }
  188.  
  189. struct DS18B20 *find_sensor(byte addr[]) {
  190.   struct DS18B20 *s;
  191.  
  192. #ifdef DEBUG
  193.   Serial.print("find_sensor(");
  194.   Serial.print(OneWireaddrtostring(addr, false));
  195.   Serial.println(")");
  196. #endif
  197.  
  198.   for (s=tempSensors; s != NULL; s=(DS18B20 *) s->hh.next) {
  199. #ifdef DEBUG
  200.     Serial.println("Comparing:");
  201.     Serial.print(OneWireaddrtostring(s->addr, false));
  202.     Serial.print(" to ");
  203.     Serial.println(OneWireaddrtostring(addr, false));    
  204. #endif
  205.  
  206.     if (compareByteArray(addr, s->addr)) {
  207. #ifdef DEBUG
  208.       Serial.println("  match found... sensor already detected");
  209. #endif
  210.       return s;
  211.     }
  212.   }
  213.  
  214. #ifdef DEBUG
  215.   Serial.println("no match found... returning NULL");
  216. #endif
  217.   return NULL;
  218. }
  219.  
  220. void print_sensors(boolean extended) {
  221.   struct DS18B20 *s;
  222.  
  223.   Serial.println("Current list of sensors:");
  224.   for (s=tempSensors; s != NULL; s=(DS18B20 *) s->hh.next) {
  225.     Serial.print(OneWireaddrtostring(s->addr, false));
  226.     if (s->active) {
  227.       if (!showExtendedStats) {
  228.         // show standard information
  229.         Serial.print(" ");
  230.         Serial.print(s->lastTemp);
  231.         Serial.print("/");
  232.         Serial.print(s->minTemp);
  233.         Serial.print("/");
  234.         Serial.print(s->maxTemp);
  235.         Serial.print(" F, Location: ");
  236.         Serial.println(s->name);
  237.       } 
  238.       else {
  239.         // show extended stats
  240.         Serial << " Location: " << s->name << " (crcerrors: " << s->crcerrors << " ) (lis: " << s->liLastConversion << ")" << endl;
  241.         Serial << "    Last: " << s->lastTemp << " @ ";
  242.         serialPrintDateTime(s->lastTimeStamp);
  243.         Serial << endl;
  244.         Serial << "    Min: " << s->minTemp << " @ ";
  245.         serialPrintDateTime(s->minTimeStamp);
  246.         Serial << endl;
  247.         Serial << "    Max: " << s->maxTemp << " @ ";
  248.         serialPrintDateTime(s->maxTimeStamp);
  249.         Serial << endl;
  250.       }
  251.     } 
  252.     else {
  253.       Serial.println(" is pending first read.");
  254.     }
  255.   }
  256.   Serial.println();
  257. }
  258.  
  259. boolean update_sensor(byte addr[], float temp) {
  260.   struct DS18B20 *s;
  261.  
  262.   s = find_sensor(addr);
  263.   if (s != NULL) {
  264.     if (!s->active) {
  265.       s->active = true;
  266.     }
  267.  
  268.     s->lastTemp = temp;
  269.     s->lastTimeStamp = now();
  270.  
  271.     if (temp < s->minTemp) {
  272.       s->minTemp = temp;
  273.       s->minTimeStamp = now();
  274.     }
  275.  
  276.     if (temp < globalMin) {
  277.       globalMin = temp;
  278.       globalMinName = s->name;
  279.       globalMinTimeStamp = now();
  280.     }
  281.  
  282.     if (temp > s->maxTemp) {
  283.       s->maxTemp = temp;
  284.       s->maxTimeStamp = now();
  285.     }
  286.  
  287.     if (temp > globalMax) {
  288.       globalMax = temp;
  289.       globalMaxName = s->name;
  290.       globalMaxTimeStamp = now();
  291.     }
  292.  
  293.     return true;
  294.   } 
  295.   else {
  296. #ifdef DEBUG
  297.     Serial.print("update_sensor() failed for addr ");
  298.     Serial.print(OneWireaddrtostring(addr, false));
  299.     Serial.print(" ");
  300.     Serial.print(temp);
  301.     Serial.println(" F");
  302. #endif
  303.     return false;
  304.   }
  305. }
  306.  
  307. int count_sensors() {
  308.   struct DS18B20 *s;
  309.   int count = 0;
  310.   for (s=tempSensors; s != NULL; s=(DS18B20 *) s->hh.next, count++) {
  311.   }
  312.   return count;
  313. }
  314.  
  315. void clear_sensor_stats() {
  316.   struct DS18B20 *s;
  317.  
  318.   for (s=tempSensors; s != NULL; s=(DS18B20 *) s->hh.next) {
  319.     s->minTemp = DEFAULT_MIN_TEMP;
  320.     s->minTimeStamp = (time_t) 0;
  321.     s->maxTemp = DEFAULT_MAX_TEMP;
  322.     s->maxTimeStamp = (time_t) 0;
  323.   }
  324. }
  325.  
  326. void clear_global_sensor_stats() {
  327.   globalMin = DEFAULT_MIN_TEMP;
  328.   globalMinName = NULL;
  329.   globalMinTimeStamp = (time_t) 0;
  330.   globalMax = DEFAULT_MAX_TEMP;
  331.   globalMaxName = NULL;
  332.   globalMaxTimeStamp = (time_t) 0;
  333. }
  334.  
  335. boolean findDS18B20Devices(OneWire & ow) {
  336.   byte addr[8];
  337.   unsigned int deviceCount = 0;
  338.  
  339. #ifdef DEBUG
  340.   Serial.println("Searching bus...");
  341. #endif
  342.  
  343.   //find a device
  344.   while (ow.search(addr)) {
  345.     if (OneWire::crc8( addr, 7) != addr[7]) {
  346.       Serial.println("Bad crc!!!");
  347.       continue;
  348.     }
  349.  
  350.     if (addr[0] != DS18B20_ID) {
  351. #ifdef DEBUG
  352.       Serial.print("Unknown device: ");
  353. #endif
  354.       Serial.println(OneWireaddrtostring(addr, false));
  355.       continue;
  356.     }
  357.  
  358. #ifdef DEBUG
  359.     Serial.print("Found a device:");
  360.     Serial.println(OneWireaddrtostring(addr, false));
  361. #endif    
  362.  
  363.     deviceCount++;
  364.     if (find_sensor(addr) == NULL) {
  365. #ifdef DEBUG
  366.       Serial.print("Adding new sensor to list: ");
  367. #endif
  368.       add_sensor(addr);
  369.     } 
  370. #ifdef DEBUG
  371.     else {
  372.       Serial.print("We already know about this sensor: ");
  373.     }
  374. #endif
  375.     Serial.println(OneWireaddrtostring(addr, false));
  376.     Serial.println();
  377.   }
  378.  
  379. #ifdef DEBUG
  380.   Serial.println("No more devices found... resetting search.");
  381.   Serial.println();
  382. #endif
  383.   ow.reset_search();
  384. }
  385.  
  386. boolean requestTemperatureConversion(OneWire ow, DS18B20 *sensor) {
  387.   ow.reset();
  388.   ow.select(sensor->addr);
  389.   ow.write(0x44, 1);
  390.  
  391.   return true;
  392. }
  393.  
  394. float retrieveTemperature(OneWire ow, DS18B20 *sensor) {
  395.   byte data[12];
  396.   float temp;
  397.  
  398.   ow.reset();
  399.   ow.select(sensor->addr);
  400.   ow.write(0xBE);
  401.   for (int i = 0; i < 9; i++) {
  402.     data[i] = ow.read();
  403.   }
  404.  
  405.   temp = ( (data[1] << 8) + data[0] )*0.0625;  // tempc
  406.   temp = (temp * 1.8) + 32;  // tempf
  407.  
  408.   if (OneWire::crc8( data, 8) != data[8]) {
  409.     temp = -9999;
  410.   }
  411.  
  412.   return temp;
  413. }
  414.  
  415. void toggleLED() {
  416.   switch (ledStatus) {
  417.   case 1:
  418.     digitalWrite(LED_PIN, LOW);
  419.     ledStatus = 0;
  420.     break;
  421.   default:
  422.     digitalWrite(LED_PIN, HIGH);
  423.     ledStatus = 1;
  424.     break;
  425.   }
  426. }
  427.  
  428. void showActivityChar() {
  429.   lcd.setCursor(18,0);
  430.   switch (activityCharIndex) {
  431.   case 1:
  432.     activityCharIndex--;
  433.     lcd.print(count_sensors());
  434.     lcd.print(" ");
  435.     break;
  436.   default:
  437.     activityCharIndex++;
  438.     lcd.print(count_sensors());
  439.     lcd.print("*");
  440.     break;
  441.   }
  442. }
  443.  
  444. String OneWireaddrtostring(byte addr[], boolean lcd) {
  445.   String toReturn;
  446.  
  447.   for( int i = 0; i < 8; i++) {
  448.     if (addr[i] < 16) {
  449.       toReturn += "0";
  450.     }
  451.     toReturn += String(addr[i], HEX);
  452.     if (i < 7) {
  453.       if (!lcd) {
  454.         // don't print semicolons on the lcd
  455.         // we don't have enough room
  456.         toReturn += ":";
  457.       }
  458.     }
  459.   }
  460.  
  461.   return toReturn;
  462. }
  463.  
  464. void update_lcd() {
  465.   struct DS18B20 *s;
  466.   unsigned int count;
  467.   unsigned int maxCount = 2;
  468.  
  469.   s = tempSensors;
  470.   if (lcdDisplayAddresses) {
  471.     maxCount = 3;
  472.   }
  473.  
  474.   if (lcdClearMetro.check() == 1) {
  475.     lcd.clear();
  476.     lcdClearMetro.reset();
  477.   }
  478.  
  479.   if (lcdDisplayAddresses) {
  480.     lcd.setCursor(0,0);
  481.     lcd.print("Uptime: ");
  482.     lcd.print(millis() / 1000);
  483.     lcd.print("s ");
  484.   }
  485.  
  486.   for (count = 0; count < maxCount; count++, s=(DS18B20 *) s->hh.next) {
  487.     if (s == NULL) {
  488.       break;
  489.     }
  490.  
  491.     if (!lcdDisplayAddresses) {
  492.       if (!s->active) {
  493.         continue;
  494.       }
  495.       lcd.setCursor(count*10,0);
  496.       lcd.print(s->name);
  497.       lcd.setCursor(count*10,1);
  498.       lcd.print(s->lastTemp);
  499.       lcd.print((char)223);
  500.       lcd.print("F");
  501.       lcd.setCursor(count*10,2);
  502.       lcd.print(s->minTemp);
  503.       lcd.print((char)223);
  504.       lcd.print("F");
  505.       lcd.setCursor(count*10,3);
  506.       lcd.print(s->maxTemp);
  507.       lcd.print((char)223);
  508.       lcd.print("F");
  509.     } 
  510.     else {
  511.       // display addresses instead of temps
  512.       lcd.setCursor(0,count + 1);
  513.       lcd.print("    ");
  514.       lcd.print(OneWireaddrtostring(s->addr, true));
  515.       lcd.setCursor(0,count + 1);
  516.       lcd.print(s->name);
  517.       lcd.print(":");
  518.     }
  519.   }
  520. }
  521.  
  522. void serialPrintDateTime(time_t timeStamp) {
  523.   time_t timeStampAdjusted = timeStamp + (DEFAULT_TIME_ZONE_OFFSET * 60 * 60);
  524.   Serial << month(timeStampAdjusted) << "/" << day(timeStampAdjusted) << "/" << year(timeStampAdjusted) << " " << hour(timeStampAdjusted) << ":";
  525.   if (minute(timeStampAdjusted) < 10) Serial << "0";
  526.   Serial << minute(timeStampAdjusted) << ":";
  527.   if (second(timeStampAdjusted) < 10) Serial << "0";
  528.   Serial << second(timeStampAdjusted);
  529. }
  530.  
  531. void processTimeSyncMessage() {
  532.   int count=0;
  533.   char buf[11];
  534.   boolean status = false;  // did we get good data
  535.   Serial.println("Waiting for time data in @time_t format...");
  536.   Serial.flush();
  537.   while (count < 11) {
  538.     if (Serial.available()) {  // receive all 11 bytes into "buf"
  539.       buf[count++] = Serial.read();
  540.     }
  541.   }
  542.   if (buf[0] == '@') {     
  543.     time_t pctime = 0;
  544.     for(int i=1; i < 11; i++) {   
  545.       char c = buf[i];    
  546.       if (c >= '0' && c <= '9') {   
  547.         pctime = (10 * pctime) + (c - '0') ; // convert digits to a number    
  548.       }
  549.     }
  550.     pctime += 10;   
  551.     setTime(pctime);   // Sync clock to the time received
  552.     status = true;
  553.   }
  554.   if (status) {
  555.     Serial.println("Sync message received and time updated.");
  556.   } 
  557.   else {
  558.     Serial.println("Invalid sync message received.");
  559.   }
  560. }
  561.  
  562. void jumpToBootloader() {
  563.   unsigned int counter = 10;
  564.   Serial << "Jumping to bootloader in " << counter << " seconds..." << endl;
  565.   Serial << "Make sure you close your serial console!!!" << endl;
  566.   Serial << endl;
  567.   Serial << "PRESS ANY KEY TO ABORT!!!" << endl;
  568.   Serial << endl;
  569.   Serial.flush();
  570.   lcd.clear();
  571.   while (counter > 0) {
  572.     lcd.setCursor(0,0);
  573.     lcd.print(counter);
  574.     Serial << counter << " ";
  575.     if (Serial.available()) {
  576.       Serial.flush();
  577.       Serial << endl << endl << "Aborted!" << endl << endl << endl;
  578.       return;
  579.     }
  580.     delay(1000);
  581.     counter--;
  582.   }
  583.   Serial.println("Jumping!");
  584.   cli();
  585.   // disable watchdog, if enabled
  586.   // disable all peripherals
  587.   UDCON = 1;
  588.   USBCON = (1<<FRZCLK);  // disable USB
  589.   UCSR1B = 0;
  590.   delay(5);
  591. #if defined(__AVR_AT90USB162__)                // Teensy 1.0
  592.   EIMSK = 0; 
  593.   PCICR = 0; 
  594.   SPCR = 0; 
  595.   ACSR = 0; 
  596.   EECR = 0;
  597.   TIMSK0 = 0; 
  598.   TIMSK1 = 0; 
  599.   UCSR1B = 0;
  600.   DDRB = 0; 
  601.   DDRC = 0; 
  602.   DDRD = 0;
  603.   PORTB = 0; 
  604.   PORTC = 0; 
  605.   PORTD = 0;
  606.   asm volatile("jmp 0x3E00");
  607. #elif defined(__AVR_ATmega32U4__)              // Teensy 2.0
  608.   EIMSK = 0; 
  609.   PCICR = 0; 
  610.   SPCR = 0; 
  611.   ACSR = 0; 
  612.   EECR = 0; 
  613.   ADCSRA = 0;
  614.   TIMSK0 = 0; 
  615.   TIMSK1 = 0; 
  616.   TIMSK3 = 0; 
  617.   TIMSK4 = 0; 
  618.   UCSR1B = 0; 
  619.   TWCR = 0;
  620.   DDRB = 0; 
  621.   DDRC = 0; 
  622.   DDRD = 0; 
  623.   DDRE = 0; 
  624.   DDRF = 0; 
  625.   TWCR = 0;
  626.   PORTB = 0; 
  627.   PORTC = 0; 
  628.   PORTD = 0; 
  629.   PORTE = 0; 
  630.   PORTF = 0;
  631.   asm volatile("jmp 0x7E00");
  632. #elif defined(__AVR_AT90USB646__)              // Teensy++ 1.0
  633.   EIMSK = 0; 
  634.   PCICR = 0; 
  635.   SPCR = 0; 
  636.   ACSR = 0; 
  637.   EECR = 0; 
  638.   ADCSRA = 0;
  639.   TIMSK0 = 0; 
  640.   TIMSK1 = 0; 
  641.   TIMSK2 = 0; 
  642.   TIMSK3 = 0; 
  643.   UCSR1B = 0; 
  644.   TWCR = 0;
  645.   DDRA = 0; 
  646.   DDRB = 0; 
  647.   DDRC = 0; 
  648.   DDRD = 0; 
  649.   DDRE = 0; 
  650.   DDRF = 0;
  651.   PORTA = 0; 
  652.   PORTB = 0; 
  653.   PORTC = 0; 
  654.   PORTD = 0; 
  655.   PORTE = 0; 
  656.   PORTF = 0;
  657.   asm volatile("jmp 0xFC00");
  658. #elif defined(__AVR_AT90USB1286__)             // Teensy++ 2.0
  659.   EIMSK = 0; 
  660.   PCICR = 0; 
  661.   SPCR = 0; 
  662.   ACSR = 0; 
  663.   EECR = 0; 
  664.   ADCSRA = 0;
  665.   TIMSK0 = 0; 
  666.   TIMSK1 = 0; 
  667.   TIMSK2 = 0; 
  668.   TIMSK3 = 0; 
  669.   UCSR1B = 0; 
  670.   TWCR = 0;
  671.   DDRA = 0; 
  672.   DDRB = 0; 
  673.   DDRC = 0; 
  674.   DDRD = 0; 
  675.   DDRE = 0; 
  676.   DDRF = 0;
  677.   PORTA = 0; 
  678.   PORTB = 0; 
  679.   PORTC = 0; 
  680.   PORTD = 0; 
  681.   PORTE = 0; 
  682.   PORTF = 0;
  683.   asm volatile("jmp 0x1FC00");
  684. #endif 
  685. }
  686.  
  687. void printCLIOptions() {
  688.   char buf[CLI_STRING_BUFFER_LENGTH];
  689.  
  690.   for (int i = 0; i < NUM_CLI_LINES; i++) {
  691.     strcpy_P(buf, (char*)pgm_read_word(&(cli_string_table[i])));
  692.     Serial.println(buf);
  693.   }
  694.  
  695.   Serial.println();
  696. }
  697.  
  698. void setup() {
  699.   // Initialize the display and tell the world we're starting to work
  700.   lcd.begin(20, 4);
  701.   lcd.setCursor(0,0);
  702.   lcd.print("Setting up...");
  703.  
  704.   pinMode(LED_PIN, OUTPUT);
  705.   ledMetro.reset();
  706.   Serial.begin(9600);
  707.  
  708.   // displaying activity char so we know we've started the initial 1-wire search
  709.   showActivityChar();
  710.  
  711. #ifdef DEBUG
  712.   for (int i = 0; i < 10; i++) {
  713.     Serial << i << " ";
  714.     delay(1000);
  715.   }
  716.   Serial << endl;
  717. #endif
  718.  
  719.   findDS18B20Devices(ds);
  720.  
  721. #ifdef DEBUG
  722.   for (int i = 0; i < 10; i++) {
  723.     Serial << i << " ";
  724.     delay(1000);
  725.   }
  726.   Serial << endl;
  727. #endif
  728. } // end setup
  729.  
  730. void loop() {
  731.   loopIteration++;
  732.   loopStartMillis = millis();
  733.  
  734.   float tmpTemp = 0;
  735.   struct DS18B20 *s;
  736.  
  737.   if (activityCharMetro.check() == 1) {
  738.     showActivityChar();
  739.     activityCharMetro.reset();
  740.   }
  741.  
  742.   // manage the sensors
  743.   for (s=tempSensors; s != NULL; s=(DS18B20 *) s->hh.next) {
  744.     if (s->converting) {
  745.       if (s->conversionTimer.check() == 1) {
  746.         tmpTemp = retrieveTemperature(ds, s);
  747.         if (tmpTemp != -9999) {
  748.           update_sensor(s->addr, tmpTemp);
  749.         }
  750.         else {
  751.           s->crcerrors++;
  752.         }
  753.         s->liLastConversion = loopIteration - s->startConversionLI;
  754.         s->converting = false;
  755.         tmpTemp = 0;
  756.       }
  757.     } 
  758.     else {
  759.       requestTemperatureConversion(ds, s);
  760.       s->startConversionLI = loopIteration;
  761.       s->conversionTimer.reset();
  762.       s->converting = true;
  763.     }
  764.  
  765.   } // for tempSensors
  766.  
  767.   // process command line input
  768.   if (Serial.available() > 0) {
  769.     char c = Serial.read();
  770.     switch (c) {
  771.     case 'B':
  772.       jumpToBootloader();
  773.       break;
  774.     case 'C':
  775.       clear_sensor_stats();
  776.       break;
  777.     case 'd':
  778.     case 'D':
  779.       if (lcdDisplayAddresses) {
  780. #ifdef DEBUG
  781.         Serial.println("Switching LCD to display sensor values");
  782. #endif
  783.         lcdDisplayAddresses = false;
  784.       } 
  785.       else {
  786. #ifdef DEBUG
  787.         Serial.println("Switching LCD to display sensor addresses");
  788. #endif
  789.         lcdDisplayAddresses = true;
  790.       }
  791.       Serial.println();
  792.       Serial.flush();
  793.       lcd.clear();
  794.       break;
  795.     case 'f':
  796.     case 'F':
  797.       Serial << "Free memory: " << freeMemory() << " bytes." << endl;
  798. #ifdef DEBUG
  799.       Serial << sizeof(DS18B20) * count_sensors() << " bytes for " << count_sensors() << " DS18B20 sensors" << endl;
  800.       Serial << sizeof(LiquidCrystal) << " bytes for LiquidCrystal object" << endl;
  801.       Serial << sizeof(Metro) * NUM_METROS << " bytes for " << NUM_METROS << " Metro objects" << endl;
  802.       Serial << sizeof(OneWire) << " bytes for OneWire object" << endl;
  803. #endif
  804.       Serial << endl;
  805.       break;
  806.     case 'G':
  807.       clear_global_sensor_stats();
  808.       break;
  809.     case 'e':
  810.     case 'E':
  811.       if (showExtendedStats) {
  812. #ifdef DEBUG
  813.         Serial.println("Disabling extended stats.");
  814. #endif
  815.         showExtendedStats = false;
  816.       } 
  817.       else {
  818. #ifdef DEBUG
  819.         Serial.println("Enabling extended stats.");
  820. #endif
  821.         showExtendedStats = true;
  822.       }
  823.       break;
  824.     case 't':
  825.     case 'T':
  826. #ifdef DEBUG
  827.       Serial.println("Processing time sync request...");
  828. #endif
  829.       processTimeSyncMessage();
  830. #ifdef DEBUG
  831.       Serial.println("Done!");
  832. #endif
  833.       Serial.println();
  834.       break;
  835.     case 'h':
  836.     case 'H':
  837.     case '?':
  838.       printCLIOptions();
  839.       break;
  840.     default:
  841.       Serial.print("Key: 0x");
  842.       Serial.println(c, HEX);
  843.       printCLIOptions();
  844.     } // testing c
  845.   }
  846.  
  847.   if (consoleUpdateMetro.check() == 1) {
  848.     serialPrintDateTime(now());
  849.     Serial << " (Uptime: " << millis() / 1000 << " s) (li: " << loopIteration << ") (lld: " << lastLoopDuration << " ms)" << endl;
  850.     Serial << "Global Min: " << globalMin << " @ ";
  851.     serialPrintDateTime(globalMinTimeStamp);
  852.     Serial << " (" << globalMinName << ")" << endl;
  853.     Serial << "Global Max: " << globalMax << " @ ";
  854.     serialPrintDateTime(globalMaxTimeStamp);
  855.     Serial << " (" << globalMaxName << ")" << endl << endl;
  856.     print_sensors(false);
  857.   }
  858.  
  859.   if (lcdUpdateMetro.check() == 1) {
  860.     update_lcd();
  861.   }
  862.  
  863.   if (ledMetro.check() == 1) {
  864.     toggleLED();
  865.     ledMetro.reset();
  866.   }
  867.  
  868.   lastLoopDuration = millis() - loopStartMillis;
  869. } // end loop
Personal tools