Project Overview. Parts List. An Open Source Environmental Monitoring System

Project Overview An Open Source Environmental Monitoring System For several years I had the opportunity to run a data center for a small hosting and w...
Author: Damian Eaton
0 downloads 2 Views 530KB Size
Project Overview An Open Source Environmental Monitoring System For several years I had the opportunity to run a data center for a small hosting and web development shop in Salt Lake City Utah. While constantly growing it was nearly impossible to get core elements of our infrastructure to an highly reliable state. Thusly we had to rely on climate monitoring hardware to prevent serious outages and failures due to flooding and air conditioner failure. The problem was that the off the shelf systems were very cost prohibitive and lacked in features. So my solution was to build a comparable system that was 100% open source and totally customizable to the needs of each implementation. We have been successful in its implementation in many datacenters and production environments including Toro, Bowing and Northrop Grumman to name a few. Based on the powerful and flexible Arduino platform anyone from beginner and expert with little programming background can totally customize the system to their needs. From simple climate monitoring and notification to complex networks of many units and master dashboards aggregating xml feeds from each unit the uses are virtually unlimited both inside and out of the datacenter environment.

Parts List

Allied Parts List 1 2 3 4 5 6 11 12 13 14 15 16

Part LED 1, Led 2 C1, C2 R1 - R8 R9-R10 TA1 - TA2 TD1- TD4

Allied Part # 670-1065 & 670-1064 213-1246 895-8729 895-8592 512-7322 742-0084

Description One red and one blue LED Two 0.1U CERAMIC CAP Eight 10K Ohm Resistors Two 330OHM Ohm Resistors Two Screw Terminal Blocks Four Screw Terminal Blocks

CDS1 SW1 SP1 TS1 H1 H2

980-2750 821-0477 623-0027 924-0295 863-0722 863-0733

CD's Cell 90 Degree Push Button Peizo Speaker Single Screw Terminal Block 6 Pin Header 8 Pin Header

Part # SPB-01 Rackmount or wallmount LCD-09067 DEV-09950 DEV-09026

Available Available Available Available Available

Sensor-01

Available at Sproutboard.com

MFG MFG MFG MFG MFG MFG

Four pin 90 degree Pin Block Eight pin 90 degree Pin Block Four pin 90 degree Block Three pin 90 degree Block 6 Pin Terminal 8 Pin Terminal

Other Vendor Parts List 1 2 3 4 5 6 7 8 9 10 17 18

Part Sproutboard Chassis Serial LCD Arduino Ethernet Shield Temp / Humidity Sensor JA J11 J9 J10 T1 T2

Part Part Part Part Part Part

no. 22-28-8022 no.87230-4 no.M20-7890346 no. 2212BR-04G no.2211S-06G no.2211S-08G

at at at at at

Description Sproutboard.com Sproutboard.com Sparkfun.com Sparkfun.com Sparkfun.com

Layout And Circuit

Board Top

Board Bottom

Building A Sprout Board

Step 1. The assembly of the sprout board is very simple and requires very little experience with soldering. There are a few places where adding components can get cramped, but if you take your time it will be very easy. I will however assume that you have soldered components into boards with the write-up below.

Step 2. Our first major step is to mount the resistors. note that R1 8 are 10K ohm (brown, black, orange, gold) and R9 and R10 are 300 ohm resistors(orange, orange, brown, gold)

Step 3. Next we will mount our two 0.1U ceramic capacitors in C1 and C2.

Step 4. Next we will add J9 and J10 to our board. Note the indented tab on the front of the component. This Indent shows you what direction is forward. if you install it backwards it will not have the friction to keep components from falling out.

Step 5. Next we will add our speaker. Note the + sign on the top of the speaker showing polarity.

Step 6. Next we will add our eight pin header for our jumper block.

Step 7. Next we will add our 4 pin header. This can get a little crowded on the bottom side of the board so take your time.

Step 8. Next we will add our push button. The push button has 2 holes for the metal bracket its mounted in. You may need to push a little to get it to snap into place.

Step 9. Next we will add our Serial header. Make sure the holes for the terminal are facing the end of the board.

Step 10. Now we will add our 2 and 4 terminal block sections. An important tip is that the terminal blocks slide and lock together. if you do not lock them together into one segment they will not fit. again make sure the holes for the terminal are facing forward.

Step 11. the 4 terminal block mounted.

Step 12. Next we will mount the pins for the arduino to plug into. a great tip here is to plug the pins into the arduino and then match the holes and solder with the arduino in place. this will insure the pins are straight.

Step 13. Now we will add the headers for the shield accessory board that is next to the arduino. Use the same tip here if you have a shield you can use to match the holes and solder the headers in place. this will insure the pins are straight.

Step 14. So here we can see the finished terminal headers and pins.

Step 15. next we will mount our CDs cell. Bend the cell with a 1/8th inch bend so the cell can poke through the front of the acrylic if you chose to use our chassis. alternatively you can use this as a general header for analog devices ( like a 10K ohm pot).

Step 16. Next we will add our first LED. Note the flat side of the led on the diagram facing the terminal block next to it. Bend your leds leads as shown.

Step 17. Finally add the second led using the same technique

Step 18. Congratulations! you have a finished sprout board!

Step 19. Here we have our glamour shot showing the board with Arduino and Ethernet shield mounted.

Step 20. And finally our close-up shot. Now we need to learn how to use it.

How to use the Sproutboard Pin Assignments

Analog I/O: Analog 0 - CCD Light Sensor (CDS1) Analog 1 - Accessory Terminal Block 1 (TA1) Analog 2 - Accessory Terminal Block 2 (TA2) Analog 3 - Onboard Accessories Socket 1 (Temperature sensor on external board J9) Analog 4 - Onboard Accessories Socket 1 (Humidity sensor on external board J9) Analog 5 - Onboard Accessories Socket 2 (Sound J10) Digital I/O: Digital 0 - Not used Digital 1 - Serial Terminal (TS1) Digital 2 - Onboard Switch (SW1) Digital 3 - Onboard Peizo Speaker (SP1) Digital 4 - LED 1 (LED 1) Digital 5 - LED 2 (LED 2) Digital 6 - Accessory Terminal Block 1 (DA1) Digital 7 - Accessory Terminal Block 2 (DA2) Digital 8 - Accessory Terminal Block 3 (DA3) Digital 9 - Accessory Terminal Block 4 (DA4) Digital 10 - Reserved For Additional Shield (Ethernet shield pins correspond) Digital 11 - Reserved For Additional Shield (Ethernet shield pins correspond) Digital 12 - Reserved For Additional Shield (Ethernet shield pins correspond) Digital 13 - Reserved For Additional Shield (Ethernet shield pins correspond)

Headers

We have provided 6 headers that can be used for expanding the capabilities of the sprout board to add additional sensors, input devices or control external devices. On the left side we have 2 analog terminals and on the right we have 4 Digital terminals. These terminals all have a set of three sockets that all use the same configuration of 5V+ on the left, I/O in the center and Ground on the right. In addition the two analog ports can simply be configured as digital I/O as needed with the arduino pinmode.

Jumpers

We have added these jumpers to our board to allow for very flexible connection of additional circuits through the accessory terminals. This configuration allows for you to setup a 2 wire button for input, have the option of both 5v and GND on an external device, or even ground your floating analog pins for more constant signals. For example: On an analog terminal you may want to use an LDR that has 2 pin. to do this you will need to close the jumper for the terminal you want to use and then simply use the 5V and I/O port for a simple connection. However you may also want to use a Pot in the second terminal. In this case you simply connect the center post on the pot to the I/O socket and the corresponding posts to their respective 5v and GND sockets. In any event we note the red rings in the picture so you know the pin pairs for the jumpers.

Serial Header

We have added a serial header on this board to allow you to connect a LCD screen with ease. As you can see in the picture here we have our 3 terminal block mounted at the end of the board. From left to right we have 5V+, TX, Ground. If you choose to use a spark fun Serial LCD display this allows for the simplest and most user friendly addition you could ever want.

Open Source Server Room Monitor

Parts List The base part list for this project is very short and is focused on typical rack mount server room installations. Below you will find a list of the parts needed to build this project. This represents only the core requirements for this system and even then some components are optional.

Sprout Board: The sprout board is the core to this project. It allows the microcontroller and an accessory shield to dock with it and utilize the onboard features with ease.

Arduino Duemilanove or Uno: The world famous Arduino. A great platform for learning microcontroller basics on and a great platform for real world devices. For this project we will be using an Arduino Duemilanove. A Atmel 168 chip found on older board will work, but you will have limited resources to work with for this sketch.

Ethernet shield 1.0: A brilliant piece of work! While this is optional it is highly recommended as it provides both a real-time interface for monitoring statistics, but as well provides the ability to send alerts if an exception occurs and yup, all the work is already done for you if you add it.

Rack Mount Chassis: So we will be assuming you will be using this in a standard rack mount scenario and thusly we would recommend the 19" rackount chassis for your project. But if your server room is more like a old broom closet then you may prefer the wall mount chassis. Either way it will help protect your project from the stray broom handle.

LCD Screen: So I say this is an optional component... but technically so are many things that we call conveniences. the serial lcd screen uses only 3 wires and provides real time information about the climate it is monitoring. You may want to save the 25.00 and just use the web interface and that's just fine, but you will definitely lose the bling factor.

Audio Board: Ok, Some components are more optional than others and this falls into that category though it is recommended. Most Servers and UPS's have alarms on them for when bad things happen. You may also have a smoke detector or building alarm. This ambient noise is what this really is used to detect. if there is an audio spike then you know there is a problem.

Temperature / Humidity Board: The most important thing to have for your server room monitoring system. This sensor board monitors temperature and humidity using the front accessory socket on the sprout board. Additionally these can be used for external sensors for temperature or humidity individually or combines with ease.

Power Supply: We recommend a 7-9V DC regulated power supply for your project. 12V can be a bit on the high end and may damage your arduino over time.

Configuration and Setup The setup for this project is very easy and all the information you will need is contained in the sketch. The default IP address for the Ethernet real time reporting is 192.168.0.42 and is setup as a variable that is easy to change. We used the Arduino Dev environment found at www.Arduino.cc Simply download, Unzip and load on your arduino to use

Ideas and Accessories

In addition to the features of the base configuration , there are many other devices you may want to add to your project to increase its capabilities. In our pilot installation of our server room monitor we utilized our additional I/O on the front panel to connect several other sensors. The sensors we used were as follows: One additional remote temperature sensor installed at the AC vent. One magnetic reed sensor installed on the door to the server room to act as a entry monitor. One Smoke Detector with a built in alarm I/O port connected to our digital I/O. One simple water sensor plate connected to our analog inputs. One AC powered relay to detect City power Failures. One solid state relay connected to an emergency exhaust blower. In the Near future we will have simple articles detailing how to use these additional devices with your sprout board.

Software Build Example In this example we have a fairly feature rich version of environmental monitoring. With real-time alerts, the ability to sync to network time servers (NTP), save time zone offsets, set work day time ranges for power conservation, customize network settings, save everything in flash memory incase of power failure and a real-time web interface for reporting of sensor values it is a very feature rich version to start development from. There are a library's you will need to download but are available at arduino.cc.

#define COMMUNITY_EDITION #include #include #include #include #include #include #include #include #include "WebServer.h" #include // UDP library from: [email protected] 12/30/2008 #include #include #if UDP_TX_PACKET_MAX_SIZE < 64 || UDP_RX_PACKET_MAX_SIZE < 64 #error : UDP packet size to small - modify UdpBytewise.h to set buffers to 64 bytes #endif /* Server Room Monitor Sproutboard.com Analog I/O: Analog 0 - CCD Light Sensor (CDS1) Analog 1 - Accessory Terminal Block 1 (TA1) unused Analog 2 - Accessory Terminal Block 2 (TA2) unused Analog 3 - Onboard Accessories Socket 1 (Temperature sensor on external board J9) Analog 4 - Onboard Accessories Socket 1 (Humidity sensor on external board J9) Analog 5 - Onboard Accessories Socket 2 (Sound J10) Digital I/O: Digital 0 - Not used Digital 1 - Serial Terminal (TS1) Digital 2 - Onboard Switch (SW1) Digital 3 - Onboard Peizo Speaker (SP1) Digital 4 - LED 1 (LED 1) Digital 5 - LED 2 (LED 2) Digital 6 - Accessory Terminal Block 1 (DA1) unused (reed switch #1) Digital 7 - Accessory Terminal Block 2 (DA2) unused (power #1) Digital 8 - Accessory Terminal Block 3 (DA3) unused (power #2) Digital 9 - Accessory Terminal Block 4 (DA4) unused (power #3) Digital 10 - Reserved For Additional Shield (Ethernet shield pins correspond) Digital 11 - Reserved For Additional Shield (Ethernet shield pins correspond) Digital 12 - Reserved For Additional Shield (Ethernet shield pins correspond) Digital 13 - Reserved For Additional Shield (Ethernet shield pins correspond) */ #define LCD_DELAY 790 // 790 msec. With everything else, should work out to about 1.0 second #define TEMPERATURE_LOW_ALARM 10 #define TEMPERATURE_HIGH_ALARM 300 #define HUMIDITY_LOW_ALARM 200 #define HUMIDITY_HIGH_ALARM 650 #define VERSION_STRING "v0.1.6C 9/14/10" time_t prevDisplay = 0; // when the digital clock was displayed time_t systemStartTime = 0; // const long timeZoneOffset = 32400L; // Hawai'i Time // const long timeZoneOffset = 28800L; // Alaska Time

// const long timeZoneOffset = 25200L; // Pacific Time const long timeZoneOffset = 21600L; // set this to the offset in seconds to your local time. This is Mountain Time. // const long timeZoneOffset = 18000L; // Central Time // const long timeZoneOffset = 14400L; // Eastern Time // const long timeZoneOffset = 10800L; // Atlantic Time // IPv4 settings. You may wish to customize. If you change the IP address through the setCmd() interface, the last // octet of the MAC address will change with it, sort-of ensuring that it is unique on a network with multiple // Sproutboards. byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x2A }; // the mac address, you wont likely need to change this. byte ip[] = { 192, 168, 0, 42 }; // the ip address of the monitor byte gateway[] = { 192, 168, 0, 1 }; byte subnet[] = { 255, 255, 255, 0 }; WebServer webserver( "", 80 ); // NTP server IP addresses... select one. s byte SNTP_server_IP[] = { 192, 43, 244, 18 }; // time.nist.gov //byte SNTP_server_IP[] = { 130, 149, 17, 21 }; // ntps1-0.cs.tu-berlin.de //byte SNTP_server_IP[] = { 192, 53, 103, 108 }; // ptbtime1.ptb.de /* application settings, you may not want to tinker with these.*/ #define analogPinNone 255 // avoid the temptation of using (-1). We have to read/write this to EEPROM which is bytes. #define analogPin0 0 // the pin that the potentiometer is attached to #define analogPin1 1 // the pin that the potentiometer is attached to #define analogPin2 2 // the pin that the potentiometer is attached to #define analogPin3 3 // the pin that the potentiometer is attached to #define analogPin4 4 // the pin that the potentiometer is attached to #define analogPin5 5 // the pin that the potentiometer is attached to const int buttonPin = 2; // the number of the pushbutton pin int buttonState = 0; const int speakerPin = 3; const int ledPin1 = 4; // alarm LED const int ledPin2 = 5; // activity LED ReedSwitches reedSwitches; PowerController powerController1( "A", 7, EighteenHour ); PowerController powerController2( "B", 8, TwelveHour ); PowerController powerController3( "C", 9, AlwaysOn ); int alarmsilent = 0; // shuts up the alarm sound int alarmstate = 0; //0 is no alarm, 1 is alarm char *alarmMessage; int _iteration = 0; Statistic temperature( "temp", TEMPERATURE_LOW_ALARM, TEMPERATURE_HIGH_ALARM, 1, analogPin3 ); Statistic humidity( "humidity", HUMIDITY_LOW_ALARM, HUMIDITY_HIGH_ALARM, 2, analogPin4 ); Statistic soundLevel( "sound", 3, analogPin5 ); Statistic lightLevel( "light", 4, analogPin0 ); void ( *restart )( void ) = 0; //declare reset function @ address 0 char temp[ 100 ]; // universal temp buffer. static const prog_uchar header[] PROGMEM = "\r\n"

"\r\n" "\r\nSproutBoard Status\r\n" "\r\n" "body { font-size: 10pt; font-family: sans-serif }\r\n" "td { font-size: 10pt; font-family: sans-serif }\r\n" "th { font-size: 10pt; font-family: sans-serif }\r\n" "h2 { font-size: 18pt; font-weight: bold; color: #000040; }\r\n" ".footer { font-size: 12pt; font-style: italic; }\r\n" ".name { color: #808080; background-color: #FFE0FF; }\r\n" ".value { color: #004000; background-color: #E0FFE0; font-weight: bold; }\r\n" "\r\n" "\r\n"; static const prog_uchar body[] PROGMEM = "\r\n" "SproutBoard™ Status Report\r\n\r\n" "\r\n"; static const prog_uchar final[] PROGMEM = "

\r\n" "For SproutBoard sales, hardware and software support, visit us at www.sproutboard.com\r\n" "\r\n"; char *TRNameString( char *dest, char *title ) { strcpy( dest, "" ); strcat( dest, title ); strcat( dest, "" ); return dest; } void statusCmd( WebServer &server, WebServer::ConnectionType type, char *tail, bool ) { server.httpSuccess( "text/xml" ); if ( type != WebServer::HEAD ) { server.printP( header ); server.printP( body ); server.print( TRNameString( temp, "Software Rev." ) ); server.print( VERSION_STRING ); server.println( "" ); server.print( TRNameString( temp, "Startup" ) ); temp[ 0 ] = '\0'; StringConverter::AppendTimeToString( temp, systemStartTime, 2 ); server.print( temp ); server.println( "" ); server.print( TRNameString( temp, "System Time" ) ); temp[ 0 ] = '\0'; StringConverter::AppendTimeToString( temp, now(), 2 ); server.print( temp ); server.println( "" ); server.print( TRNameString( temp, "Uptime" ) ); server.print( FormatUpTime( temp, systemStartTime, prevDisplay ) ); server.println( "" ); server.println( powerController1.TRStatus( temp ) ); server.println( powerController2.TRStatus( temp ) );

server.println( powerController3.TRStatus( temp ) ); server.println( temperature.TRStatus( temp, "° C" ) ); server.println( humidity.TRStatus( temp, "%" ) ); server.println( lightLevel.TRStatus( temp, "%" ) ); server.println( soundLevel.TRStatus( temp, "%" ) ); server.println( reedSwitches.TRStatus( temp ) ); server.print( TRNameString( temp, "HTTP Server IP Address" ) ); for ( int i = 0; i < 4; i++ ) { server.print( ( int ) ip[ i ] ); if ( i != 3 ) { server.print( "." ); } } server.println( "" ); server.print( TRNameString( temp, "HTTP Server Gateway" ) ); for ( int i = 0; i < 4; i++ ) { server.print( ( int ) gateway[ i ] ); if ( i != 3 ) { server.print( "." ); } } server.println( "" ); server.printP( final ); } } // Arduino Interface void setup() { mac[ 5 ] = ip[ 3 ]; // hack ensures each unit has its own IP address. reedSwitches.AddPin( 6 ); // set digital pin I/O modes pinMode( powerController1._relay, OUTPUT); pinMode( powerController2._relay, OUTPUT); pinMode( powerController3._relay, OUTPUT); pinMode(ledPin1, OUTPUT); pinMode(ledPin2, OUTPUT); pinMode(speakerPin, OUTPUT); pinMode(buttonPin, INPUT); temperature._samplesPerRead = 8; humidity._samplesPerRead = 8; BlinkLed( ledPin1 ); BlinkLed( ledPin2 ); delay(100); Serial.begin(9600); delay(100); ClearLCD(); Serial.print( VERSION_STRING ); delay( 2000 ); // give user time to reset the Ethernet Shield ClearLCD(); for ( int i = 0; i < 4; i++ ) {

Serial.print( ( int ) ip[ i ] ); if ( i != 3 ) { Serial.print( "." ); } } delay( 2000 ); // give user time to reset the Ethernet Shield // make the ethernet shield work Ethernet.begin( mac, ip, gateway, subnet ); SetupWebserver(); //ClearLCD(); //Serial.print( "Getting NTP Time" ); setSyncProvider( getNtpTime ); while ( timeStatus() == timeNotSet ) { } systemStartTime = now(); ClearLCD(); BlinkLed( ledPin1 ); BlinkLed( ledPin2 ); } void SetupWebserver() { webserver.begin(); webserver.setDefaultCommand( &statusCmd ); } void loop() { reedSwitches.Poll(); ManageMains(); // temp and humidity get eight reads each. temperature.Add( ReadAnalogAverage( temperature ), hour() ); humidity.Add( ReadAnalogAverage( humidity ), hour() ); if ( soundLevel._pin != analogPinNone ) { soundLevel.Add( analogRead( soundLevel._pin ), hour() ); } if ( lightLevel._pin != analogPinNone ) { lightLevel.Add( analogRead( lightLevel._pin ), hour() ); } ReadNTPClock(); BackgroundWork( _iteration ); RefreshLCD( ++_iteration ); } void ReadNTPClock() { if ( now() != prevDisplay ) //update the display only if the time has changed { prevDisplay = now(); } }

char *FormatUpTime( char *buf, time_t start, time_t current ) { long seconds = current - start; if ( seconds > 0 ) { buf[ 0 ] = '\0'; StringConverter::AppendIntToString( buf, ( int ) ( seconds / 3600L ) ); strcat( buf, ":" ); StringConverter::AppendZeroPaddedIntToString( buf, 2, ( int ) ( ( seconds % 3600L ) / 60L ) ); strcat( buf, ":" ); StringConverter::AppendZeroPaddedIntToString( buf, 2, ( int ) ( seconds % 60L ) ); } else { strcpy( buf, "" ); } return buf; } // cycles through display stuff for LCD. void RefreshLCD( int count ) { ClearLCD(); if ( strcmp( alarmMessage, "" ) ) { CenterLineOne( alarmMessage ); Serial.print( alarmMessage ); SelectLineTwo(); } switch ( count % 10 ) { case 0: // show humidity, temperature case 1: if ( temperature._pin != analogPinNone ) { Serial.print( ( int ) temperature._lastValue / 10 ); Serial.print( "C " ); } if ( humidity._pin != analogPinNone ) { Serial.print( "RH: " ); Serial.print( ( int ) humidity._lastValue / 10 ); Serial.print( "%" ); } break; case 2: // light level, sound level case 3: if ( lightLevel._pin != analogPinNone ) { Serial.print( "Light " ); Serial.print( ( int ) lightLevel._lastValue / 10 ); Serial.print( "%" ); } break; case 4: case 5: // noise if ( soundLevel._pin != analogPinNone )

{ Serial.print( "Noise " ); Serial.print( ( int ) soundLevel._lastValue / 10 ); Serial.print( "%" ); } break; case 6: case 7: Serial.print( "PWR " ); Serial.print( powerController1._relayState == On ? "ON " : "off " ); Serial.print( powerController2._relayState == On ? "ON " : "off " ); Serial.print( powerController3._relayState == On ? "ON" : "off" ); break; case 8: case 9: FormatUpTime( temp, systemStartTime, prevDisplay ); strcat( temp, " Up" ); Serial.print( temp ); break; } if ( ! strcmp( alarmMessage, "" ) ) { SelectLineTwo(); temp[ 0 ] = '\0'; StringConverter::AppendTimeToString( temp, now(), 1 ); Serial.print( temp ); } delay( LCD_DELAY ); // wait for a second } // manages the three power mains void ManageMains() { powerController1._relayState = ManageMain( powerController1 ); powerController2._relayState = ManageMain( powerController2 ); powerController3._relayState = ManageMain( powerController3 ); } PowerControllerRelayState ManageMain( PowerController powerController ) { PowerControllerRelayState expectedState = powerController.GetExpectedState( hour(), minute() ); if ( expectedState != powerController._relayState ) { digitalWrite( powerController._relay, ( expectedState == On ) ? HIGH : LOW ); } return expectedState; } void BackgroundWork( int count ) { Alarm(); if ( count % 4 == 0 ) { BlinkLed( ledPin2 ); }

if ( reedSwitches._alert && alarmstate == 0 ) { BlinkLed( ledPin1 ); } ReadButton(); //char buff[ 64 ]; //int len = 64; /* process incoming connections one at a time forever */ //webserver.processConnection( buff, &len ); webserver.processConnection(); } /** * computes the average of a list, striking out the high and low values as outliers. **/ int AverageOf( int *list, int count ) { int accumulator = 0; int high = -1000; int low = 1000; for ( int i = 0; i < count; i++ ) { accumulator += list[ i ]; if ( list[ i ] < low ) { low = list[ i ]; } if ( list[ i ] > high ) { high = list[ i ]; } } accumulator -= ( low + high ); return accumulator / ( count - 2 ); } // reads the temperature. Cost is 80 msec int ReadAnalogAverage( Statistic which ) { if ( which._pin != analogPinNone ) { int samples[ 8 ]; for ( int i = 0; i