Here is what I dug out from years ago. I am a hack so don’t trash the code.
/*--------------------------------------------------------------
Program: eth_websrv_SD
Description: Arduino webserver that shows a webpage with the measured heat index data displayed in a series of google charts.
The heat index is calculated by measuring the temperature and humidity and then performing a calculation.
The displayed webpage will show the current data with a quick visual indicator and basic configuration information.
There will be three charts on the webpage.
One will be the current day with five minute intervals. The highest value during the five minute interval will be used.
The second will be the past 7 days worth of data, with 30 minute intervals. The highest value during the interval will be used.
The third chart will show the past 30 days worth of data, with 90 minute intervales. The highest value during the interval will be used.
TODO:
1. Have to figure out how to send e-mails.
2. Make statement about further data being kept on SD card.
2. Must figure out how to get the time set.
a. email_sd_test.ino has a thing that samples time from the pachube server
b. There is also a way to set the date/time via the url parameters
Hardware: Arduino Uno and official Arduino Ethernet
shield. Should work with other Arduinos and
compatible Ethernet shields.
2Gb micro SD card formatted FAT16
Software: Developed using Arduino 1.0.3 software
Should be compatible with Arduino 1.0 +
SD card contains web page called index.htm
References: - WebServer example by David A. Mellis and
modified by Tom Igoe
- SD card examples by David A. Mellis and
Tom Igoe
- Ethernet library documentation:
http://arduino.cc/en/Reference/Ethernet
- SD Card library documentation:
http://arduino.cc/en/Reference/SD
Date: 10 January 2013
Author: W.A. Smith, http://startingelectronics.com
-------------------------------------------------------------- */
// The defines & includes required to get all of the required functionality /////////////////////////////////////////////////////////////////////////
#include <SPI.h>
#include <Ethernet.h> // This is the Ethernet library
#include <SD.h> // This is the SD Card library
#include <DHT22.h> // This is the temperature sensor library
#include <Time.h> // This is the time library
// Defines for the DHT22 Instance ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define DHT22_PIN A2 // Data wire is plugged into port 0 on the Arduino
DHT22 myDHT22(DHT22_PIN); // Setup a DHT22 instance
char* errorValue = "E4.htm"; // The error value returned by the sensor
float farenheitTemp = 0; // The farenheit temperature calculated from the celcius temp
float humidity = 0; // The directly read humidity value
float heatIndexTemp = 0; // The heat index value calculated from temp and humidity
float delayHeatIndexTemp = 80; // The heat index value calculated from temp and humidity
const float decay = 0.006738; // The decay coefficient. This came from e^(-1/0.2)
const float LPF = 0.04; // The Low Pass Filter coefficient
// The email program flow values ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
char* levelNow = "L4.htm"; // Current temp threshold level
char* levelPrev = "L4.htm"; // Temp threshold level the system was at on previous scan
// Ethernet configuration stuff here ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
byte mac[] = {0x90, 0xA2, 0xDA, 0x0D, 0x65, 0xC0}; // This is the MAC address from the sticker on the board - Ethernet Shield
IPAddress ip(192, 168, 0, 200); // IP address, may need to change depending on network
EthernetServer server(80); // create a server at port 80
// SD card configuration stuff here /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
File webFile;
// Time control variables /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
char* j = "0";
char* newHour = "00";
char* newMinute = "00";
char* newDay = "00";
char* newMonth = "00";
char* newYear = "0000";
// General program flow values here ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int currMin = 0; // This is the current minute value that is to be used later for decision making.
int prevMin = 0; // This is the previous minute value that is to be used later for decision making.
int currHour = 0; // This is the current hour value that is to be used later for decision making.
boolean dayData = false; // This is used to know that the 5 min interval data has been written.
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//
// ARDUINO REQUIRED FUNCTIONS
//
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// This routine is required by the Arduino sketch. It is a one time execution
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup()
{
Ethernet.begin(mac, ip); // initialize Ethernet device
server.begin(); // start to listen for clients
Serial.begin(9600); // for debugging
// initialize SD card
//Serial.println("Initializing SD card...");
if (!SD.begin(4)) {
//Serial.println("ERROR - SD card initialization failed!");
return; // init failed
}
//Serial.println("SUCCESS - SD card initialized.");
// check for index.htm file
if (!SD.exists("one.htm")) {
//Serial.println("ERROR - Can't find one.htm file!");
return; // can't find index file
}
//Serial.println("SUCCESS - Found files.");
// Set the time
setTime(21,11,00,8,1,2013);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// This routine is required by the Arduino sketch. This is the main() function in regular C.
// It constantly loops through, executing the code as it does
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void loop()
{
boolean bailOut = false; // Used to know when the first line of the POSTed webpage has been received
boolean sawFlag = false; // Used to know if the '?' symbol has been seen
char input[4]; // The variable used to store the POSTed data into, limited to 100
int i = 0; // Just a loop counter
int j = 0; // Just a loop counter
currMin = minute(); // Get the current minute value for looping determination
if (currMin != prevMin){ // THIS BIT WILL OUTPUT THE TEMP DATA FOR THE GRAPHS IF IT IS A NEW MINUTE
readSensor(); // Read the temp and humidity sensor and calculate the heat index temp
if ((currMin % 5) == 0){
exportData("day.htm",false); // Export weekly data
currHour = (hour() % 3); // Get current hour, and divide by three.
if ((currMin == 0) || (currMin == 30)){ //This was a switch statement but eliminated it to reduce sketch size
exportData("week.htm",true); // Export weekly data
if (currHour == 0){ // Remainder of hour/3 is zero so hour is 0,3,6,9,12,15,18,21 which is when output should be made when minute = 0.
exportData("month.htm",true); // Export monthly data
}
}
}
prevMin = currMin;
}
// THIS BIT IS THE WEBSERVER SECTION LISTENING FOR REQUESTS AND SENDING DATA
EthernetClient client = server.available(); // try to get client
if (client) { // got client?
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) { // client data available to read
char c = client.read(); // read 1 byte (character) from client
if (c == '?'){ // If it sees the ? set a flag to store input
if (sawFlag == true){
sawFlag = false;
}
else{
sawFlag = true;
}
}
if ((!bailOut) && (sawFlag) && (c!='?')){ // If no line feed and a ? mark seen, read the input
// This should parse the input html string to get the date and time from the string.
// if (i==2){
// newMonth[0] = input[i-2];
// newMonth[1] = input[i-1];
// Serial.print(newHour);
// }
// if (i==5){
// newMinute[0] = input[i-2];
// newMinute[1] = input[i-1];
// Serial.print(newMinute);
//}
// if (i==8){
// newMonth[0] = input[i-2];
// newMonth[1] = input[i-1];
// Serial.print(newMonth);
// }
// if (i==11){
// newDay[0] = input[i-2];
// newDay[1] = input[i-1];
// Serial.print(newDay);
//}
Serial.print(c);
input[i] = c; // Store into the character array
i++; // Increment the character array position
}
if (c == '\n' && currentLineIsBlank) { // respond to client only after last line received
outputClient("One.htm", client); // The first static section of the webpage is opened includes a a standard http response header
client.print(farenheitTemp); // The measured farenheit temperature is output to the client
outputClient("Two.htm", client); // This is the transition betwnee the temp and humidity
client.print(humidity); // The measured humidity is output to the client
outputClient("Three.htm", client); // This is the transition between humidty and the feels like temperature
client.print(heatIndexTemp); // The heat index temperature is output to the client
outputClient(errorValue, client); // Now evaluate the warning condition and determine what file to open
outputClient(levelNow, client); // Now evaluate the warning condition and determine what file to open
outputClient("email.htm", client); // Send the email data information
outputClient("Four.htm", client); // Send the header for the daily chart
outputClient("day.htm", client); // Now send the data for the weekly chart.
outputClient("Five.htm", client); // Now send the transition between the daily and weekly charts.
outputClient("week.htm", client); // Now send the data for the weekly chart.
outputClient("Six.htm", client); // Now send the transition between the weekly and monthly charts.
outputClient("month.htm", client); // Now send the data for the monthly chart.
outputClient("Seven.htm", client); // Send the end of the page page file
break;
}
} // end if (client.available())
} // end while (client.connected())
delay(1); // give the web browser time to receive the data
client.stop(); // close the connection
} // end if (client)
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//
// WEBSERVER AND FILE MANIPULATION VARIABLES
//
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// This routine will write the sensor data to the data file used to generate the weekly data graph.
// TODO: Build error handling into the routine to catch bad card writes
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void exportData(char* currFile, boolean writeDate)
{
// open the file. note that only one file can be open at a time,
webFile = SD.open(currFile, FILE_WRITE); // open web data page file
if (webFile) {
//webFile.println(",");
webFile.print(",['");
if (writeDate==true){
webFile.print(month());
webFile.print("-");
webFile.print(day());
webFile.print("-");
webFile.print(year());
webFile.print(" ");
}
webFile.print(hour());
webFile.print(":");
if (minute()<10){
webFile.print('0');
}
webFile.print(minute());
webFile.print("',");
webFile.print(delayHeatIndexTemp);
webFile.println(", 90, 105, 120]");
webFile.close();
}
//Serial.println("Called write to file.");
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// This routine will open the specified file, and output it if it is available
// TODO: Build error handling into the routine to catch bad card writes
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void outputClient(char* currFile, EthernetClient client)
{
webFile = SD.open(currFile); // open the file containing the data for the chart with the past 24 hours shown
if (webFile) {
if (webFile.size() > 5000){ // See if there is a days worth of data to show TODO: set size properly
webFile.seek(webFile.size() - 5000); // Back up to the starting point
while(webFile.peek()!=91){ // See if a '[' is the file position
if (webFile.position() > 1){ // Don't backup past the file starting position
webFile.seek(webFile.position()-1); // Backup one file position
}
else{
webFile.seek(0); // It was at the 1st of the file, go to the starting point
break; // Exit the while loop
}
}
}
while(webFile.available()) {
client.write(webFile.read()); // Send the chart data to the client
}
webFile.close();
}
//Serial.println("Called write to file.");
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//
// TEMPERATURE FUNCTIONS
//
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// This routine will read information from the temp/humidity sensor. It is the Sparkfun SEN-10167
// The routine will read from the sensor, and if it is a valid read will calculate the heat index and the
// Temperature warning level using the filtered heat index value
// If the data is not read, then the temperature will not be changed. An error flag will be set.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void readSensor(){
delay(2000); // The sensor can only be read from every 1-2s, and requires a minimum 2 second warm-up after power-on.
DHT22_ERROR_t errorCode;
errorCode = myDHT22.readData();
switch(errorCode){
case DHT_ERROR_NONE:
errorValue = "E0.htm";
//errorDescription = "Data Read OK";
farenheitTemp = myDHT22.getTemperatureC();
farenheitTemp = (((9.0/5.0)*farenheitTemp)+32);
humidity = myDHT22.getHumidity();
heatIndex(farenheitTemp, myDHT22.getHumidity());
break;
case DHT_ERROR_CHECKSUM:
errorValue = "E1.htm";
//errorDescription = "Checksum Error, maybe bad values";
farenheitTemp = myDHT22.getTemperatureC();
farenheitTemp = (((9.0/5.0)*farenheitTemp)+32);
humidity = myDHT22.getHumidity();
heatIndex(farenheitTemp, myDHT22.getHumidity());
break;
case DHT_BUS_HUNG:
errorValue = "E2.htm";
//errorDescription = "BUS Hung, no values read";
break;
default:
errorValue = "E3.htm";
// case DHT_ERROR_NOT_PRESENT:
// errorValue = "E3.htm";
// //errorDescription = "Sensor not present.";
// break;
// case DHT_ERROR_ACK_TOO_LONG:
// errorValue = "E3.htm";
// //errorDescription = "Data Timeout, no values read";
// break;
// case DHT_ERROR_SYNC_TIMEOUT:
// errorValue = "E3.htm";
// //errorDescription = "Sync Timeout, no values read";
// break;
// case DHT_ERROR_DATA_TIMEOUT:
// errorValue = "E3.htm";
// //errorDescription = "Data Timeout, no values read";
// break;
// case DHT_ERROR_TOOQUICK:
// errorValue = "E3.htm";
// //errorDescription = "Polled too quick, no values read";
// break;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// This routine will calculate the heat index temperature. Equation is from Wikipedia as of April 2012. Calculated values match
// those of NOAA and other websites and the readily available heat index charts.
// Per accepted practice, heat index calclulation is only performed if the temperature is above 80 degrees, or the
// relative humidity is above 40%
// If neither are true then heat index temperature will be equal to measured temperature.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void heatIndex(float temp, float RH){
// If temp is above 80 and humidity is above 40, compute the heat index temp.
if (temp>80.0){
if (RH>=40.0){
heatIndexTemp=16.923+((1.85212*(0.1))*temp);
heatIndexTemp = heatIndexTemp + (5.37941*RH)-((1.00254*(0.1))*temp*RH);
heatIndexTemp = heatIndexTemp + (9.41695*(0.001)*(temp*temp))+(7.28898*(0.001)*(RH*RH));
heatIndexTemp = heatIndexTemp + (3.45372*(0.0001)*(temp*temp)*RH)-(8.14971*(0.0001)*temp*(RH*RH));
heatIndexTemp = heatIndexTemp + (1.02102*(0.00001)*(temp*temp)* (RH*RH))-(3.8646*(0.00001)*(temp*temp*temp));
heatIndexTemp = heatIndexTemp + (2.91583*(0.00001)*(RH*RH*RH))+(1.42721*(0.000001)*(temp*temp*temp)*RH);
heatIndexTemp = heatIndexTemp + (1.97483*(0.0000001)*temp*(RH*RH*RH));
heatIndexTemp = heatIndexTemp - (2.18429*(0.00000001)*(temp*temp*temp)*(RH*RH));
heatIndexTemp = heatIndexTemp + (8.43296*(0.0000000001)* (temp*temp)*(RH*RH*RH))-(4.81975* (0.00000000001)*(temp*temp*temp)*(RH*RH*RH));
}
else
{
heatIndexTemp = temp;
}
}
else
{
heatIndexTemp = temp;
}
// Calculate a delayed decaying value, then run it through a LPF to create a smoothed value determine warning levels.
// The equation is: Previous + LPFCoefficient * ((Actual + Previous*DecayCoeffient)-Previous)
// the value is calculated incrementally to avoid issues with computation
// Calculate the decay, which is: (Actual + Previous*DecayCoeffient)
temp = (delayHeatIndexTemp*decay); //The multiplactive part of the decay or Previous*DecayCoeffient
temp = heatIndexTemp + temp; //The final part and the resulting decayed signal which is (Actual + Previous*DecayCoeffient)
// Calculate the LPF result of the decayed signal, which is:
temp = (temp-delayHeatIndexTemp); // The summing portion of the LPF, which is ((Actual + Previous*DecayCoeffient) - Previous)
temp = LPF*temp; // The LPF part of the signal, which is LPFCoefficient * ((Actual + Previous*DecayCoeffient)-Previous)
// Calculate the final decayed, filtered result
delayHeatIndexTemp = delayHeatIndexTemp + temp; // The full equation result, Previous + LPFCoefficient * ((Actual + Previous*DecayCoeffient)-Previous)
// Now, using the smoothed heat index temperatue, determine what the current warning level state is.
if ((delayHeatIndexTemp>90) && (delayHeatIndexTemp<=105)){
levelNow = "L3.htm"; // Above 90 and less than 105, so at level 3, as set by Corporate
}
else if ((delayHeatIndexTemp>105) && (delayHeatIndexTemp<=120)){
levelNow = "L2.htm"; // Above 105 and less than 120, so at level 2, as set by Corporate
}
else if (delayHeatIndexTemp>120){
levelNow = "L1.htm"; // Above 120, so at level 1, as set by Corporate
}
else{
levelNow = "L4.htm"; // Below 90, so at level 4, as set by Corporate
}
}