Ok. The code is below. Again, the suspected issue is a memory overrun.
/* ----------------------------------------------------------
Differduino Attic Fan Control ver 6.0
Works with the PCB Differduino ver 0.91
Uses Dallas 1-wire DS18B20 temperature sensors.
Decides when to turn on attic fan based on outside vs attic temperature.
Turns on attic fan via digital output hooked to a relay circuit.
Posts data to the IoT platform via a wiz812mj ethernet module
using the standard Arduino ethernet library and the official ThingSpeak library.
All temperatures are in fahrenheit
-------------------------------------------------------------
*/
#define ThingHTTPapi "(redacted)" // twilio acct SID: (redacted)
#define WadesCell "+1(redacted)"
//===========================================================================================================================
//===========================================================================================================================
const bool use_ThingSpeak = true; // true=>output data to ThingSpeak, false=>don't output data to ThingSpeak
//DEBUG const bool dbg = true; // general debug messages
//DEBUGmem const bool dbgmem = true; // memory usage debug messages
//===========================================================================================================================
//===========================================================================================================================
/* BOF preprocessor bug prevent
* insert me on top of your arduino-code
* per http://a-control.de/arduino-fehler/?lang=en#english
*/
#define nop() __asm volatile ("nop")
#if 1
nop();
#endif
/* EOF preprocessor bug prevent */
//#include <MemoryFree.h>
//#include <pgmStrToRam.h>
#include <DallasTemperature.h>
#include <SPI.h>
#include <OneWire.h>
#include <Ethernet.h>
#include "C:\Users\wade\Documents\Arduino\libraries\HttpClient\HttpClient.h"
#include <ThingSpeak.h>
//#include <LiquidCrystal.h>
//define pins used by lcd display.
//LiquidCrystal lcd (3, 4, 5, 6, 7, 8 );
const int ONE_WIRE_BUS= 9; //define input pin for 1-wire bus. The Differduino PCB uses digital pin 9
// constants for logging data using ThingSpeak
#define APIKEYdatawrite "(redacted)" //Data write API key
#define APIKEYdataread "(redacted)" // Data read API key
unsigned long dataChannelID = 535098; //
#define APIKEYctrlwrite "(redacted)" // Control write API key
#define APIKEYctrlread "(redacted)" // Control read API key
unsigned long ctrlChannelID = 535544;
#define thingSpeakAddress "api.thingspeak.com"
//ThingSpeak Channel Fields
//dataChannelID: field 1=attic temp; field 2=outside temp; field 3=Fan state; field 4=sensorsOK; field 5=SMSbalance
//ctrlChannelID: field 1=maxFanTimeOn; field 2=fanRestTime; field 3=vacation mode; field 4=maxSummerDiff; field 5= minSummerDiff; field 6=SMSlevel
// SMS level 0=no SMS notifications; 1=bad sensors reads only; 2=1 plus fan state changes
//GET https://api.thingspeak.com/update?api_key=(redacted)&field1=0
//Get a Channel Feed
//GET https://api.thingspeak.com/channels/535544/feeds.json?api_key=(redacted)&results=2
//Get a Channel Field
//GET https://api.thingspeak.com/channels/535544/fields/1.json?api_key=(redacted)&results=2
//Get Channel Status Updates
//GET https://api.thingspeak.com/channels/535544/status.json?api_key=(redacted)
//......................................
// String types must be sent to ThingSpeak, therefore a conversion is required.
// The light value straight from the A0 pin and the previously calculated temperature are cast to strings.
// String light = String(analogRead(A0),DEC);
// String temp = String(tempVal,DEC);
//......................................
OneWire oneWire(ONE_WIRE_BUS); // Setup a oneWire instance to communicate with any OneWire devices
DallasTemperature sensors(&oneWire); // Pass our oneWire reference to Dallas Temperature.
//########################################################################################
//########################## VARIABLES ###############################################
int ret; // return code for gets and puts
bool winter;
bool summer;
bool maxExceeded; // max attic-outside before fan is triggered
bool minAchieved; // when attic reaches outside+minAchieved, fan will turn off
//****************************************************************************************
//************************** CONTROL VARIABLES ***************************************
//****************************************************************************************
//These variables are where the adjustments are made that effect when system will cycle.
//These are the defaults that are used when the differential controller first powers up
//If a ThingSpeak 'control' feed is used, these variables can be adjusted through the ThingSpeak interface,
//but system defaults to the values below upon restart/reset of the controller.
int maxfanMinutesOn = 30; // 30 minutes = maximum time fan can be on before shutting off
int fanRestMinutes = 10; // 10 minutes = minimum time the fan is off after being on
int vacationMode = 0; // control variable indicating whether the user has enabled vacation mode or not (0=off; 1=on)
int maxSummerDiff = 15; // attic/outside temp diff at which fan gets turned on in the summertime, also a CONTROL var
int minSummerDiff = 10; // attic/outside temp diff at which fan gets turned off in the summertime, also a CONTROL var
int SMSlevel = 0; // level of SMS messaging (see comments above)
int ShutDown = 0; // 1-> stop recording data and sending SMSs; 0-> record data and send SMSs acc. to SMSlevel
//****************************************************************************************
const int setWinterMax = 33; // upper bound on outside temp that defines wintertime
const int setSummerMin = 75; // lowerbound on outside temp that defines summertime
int numberOfDevices; // Number of temperature devices (sensors) found
unsigned long ThingSpeakPutDelay = 120000UL; // 'puts' to ThingSpeak every 120 secs - must never be < ~20 secs due to free TS acct constraints
unsigned long ThingSpeakPutLast = 0UL; // time (in millis) of last ThingSpeakPut
unsigned long readingDelay = 60000UL; // 60 sec interval between sensor reading attempts
unsigned long readingLast = 0UL; // time of most recent reading of sensors
unsigned long ThingSpeakGetDelay = 60000UL; // 'get' new control values from ThingSpeak every 60 seconds
unsigned long ThingSpeakGetLast = 0UL; //time of most recent 'get' of control values from ThingSpeak in millisecs
static bool putOK; // did the put to the IoT platform occur without error
static bool getOK; // did the get from the IoT platform occur without error
unsigned int fanMinutesOn = 0; // millisecs that the fan has been on
unsigned int fanMinutesOff = 60; // minutes that the fan has been off -- initialize large
unsigned long fanStartTime = 0UL; // time (in ms) that the fan has been on since last being turned on
unsigned long fanStopTime = fanRestMinutes * 1000 * 60; // time (in ms) that the fan has been off since last being turned on
static bool fanOn;
static bool fanStateChanged;
static bool sensorsOK;
int invalidCount=0;
float SMSbalance = 14.4700; //Twilio acct balance
//SLOPE float slope; // most recent rate of temp. decline in attic when fan is on
//variables to hold temperature values
float attic; // attic temp
float outside; // outside temp
float differential; //delta between attic and outside
// prevAttic =attic temp (Tmp) readings and timestamps (Tim) prior to current reading
// initialize these arrays
//SLOPE float prevAtticTmp [6]={999.0,999.0,999.0,999.0,999.0,999.0};
//SLOPE float prevAtticTim [6]; // should not need to initialize global vars to zero ={0,0,0,0,0,0};
//SLOPE int nAtticTemps; //=0; // number of most recent attic temps & tiemstamps contained in prevAtticTmp and prevAtticTim
/* logic rationale
Winter: if attic temp > 32 & outside temp <32, then turn fan on UNTIL
a) maxfanMinutesOn is reached, or
b) attic temp <= 32, or
c) fan's ability to reduce attic temp has reached point of diminishing returns (i.e.,
attic temp rate of reduction ("slope") during most recent period (maxfanMinutesOn/6)
will not achieve attic temp goal of 32 if it were applied over two maxfanMinutesOn durations)
Summer: if (attic temp)- (outside temp) > maxSummerDiff, then turn fan on UNTIL
a) maxfanMinutesOn is reached, or
b) (attic temp)- (outside temp) < minSummerDiff, or
c) fan's ability to reduce attic temp has reached point of diminishing returns (i.e.,
attic temp rate of reduction ("slope") during most recent period (maxfanMinutesOn/6)
will not achieve attic temp goal of (outside temp + minSummerDiff) if it were applied over two maxfanMinutesOn durations)
*/
//SET YOUR THERMOMETER ADDRESSES!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//The 1-wire thermometers are identified by their unique hexidecimal identifier
//use the example sketch 'tester' provided with the DallasTemperature library
//to get each device address.
//These must be specific to my sensor IDs.
byte atticSensor[8] = {
0x28, 0x12, 0xDD, 0x57, 0x05, 0x0, 0x0, 0xF2};
byte outsideSensor[8] = {
0x28, 0x80, 0xD1, 0x4E, 0x05, 0x0, 0x0, 0xA7}; // 2880D14E050000A7
////physical mac address of ethernet, may be printed on shield. The wiz812mj does not come with an assigned mac address!
//Give it any mac (hexidecimal) you wish, AS LONG AS device is connected to your lan, NOT directly to the internet.
//if you wish to connect directly to the internet, with a public IP address, you must find a valid
//assigned mac address, for example, you could use a mac address from some other broken ethernet devise.
byte mac[] = {
0x00, 0x24, 0xd6, 0x7f, 0xa2, 0x54 }; // from Wade's Vixar laptop -- Charlie, you can use this MAC address too
//assign IP address manually for now. Pick ip not in your routers dhcp pool, and not otherwise in use.
//Comment out causes dhcp but is not properly implemented into this code yet.
//ie, waits for dhcp indefinetly if not plugged in, thus controller never starts.
//TODO implement DHCP. Controller should start even if can't get address, and periodically check again for dhcp address.
//
//WVC Wade's wifi router DHCP range is 192.168.1.100 to 192.168.1.199, and the following ip is reserved in the router for use with the Arduino's MAC address
byte ip[] = {
192, 168, 0, 199 };
//----------------------------------------------------------------------------------------------
//Setup Ethernet client
EthernetClient client; //Creates a client which can connect to a specified internet IP address and port (defined in the client.connect() function)
//define pin for fan relay
const int fan = A0;
// temp vars for integer GETs and float GETs
int intGET;
float floatGET;
//##################################################################################
//############################## SETUP #######################################
//##################################################################################
//##################################################################################
//this part of the code runs once to get everything setup and started.
void setup ()
{
//start serial port and print information for debugging
Serial.begin(9600L);//DEBUG
delay (4000); //give user time to start serial monitor after compile & upload//DEBUG
// printState(F("setup"));
//DEBUG Serial.println (F("Waduino Ver 5.1"));
//DEBUG Serial.println (F("Attic Fan Diff Cntrlr"));
//DEBUG Serial.println();
// block of statements used only for testing
//pretends that summertime=outside temp >50, and diff threshholds are 5 & 2 def F
//TEST maxfanMinutesOn = 2; // statement for testing only
//TEST fanRestMinutes = 1; // statement for testing only
//TEST maxSummerDiff = 5; // statement for testing only
//TEST minSummerDiff = 4; // statement for testing only
//TEST setSummerMin = 50; // statement for testing only
// start the ethernet connection
//DEBUG if(dbg) Serial.println (F("starting E/N"));
Ethernet.begin(mac, ip); //Initializes the ethernet library and network settings
// Start up the 1-wire library
//DEBUG Serial.println (F("starting 1-wire sensors"));
sensors.begin();
// start up TS
ThingSpeak.begin(client);
// locate devices on the one wire bus
//DEBUG if(dbg) {
//DEBUG Serial.println(F(""));
//DEBUG Serial.print(F("Locating 1-wire devices..."));
//DEBUG Serial.println (F(""));
//DEBUG Serial.print(F("Found "));
//DEBUG Serial.print(sensors.getDeviceCount(), DEC);
//DEBUG Serial.println(F(" devices."));
//DEBUG }
//// report parasite power requirements
//Serial.print(F("Parasite power is: "));
//if (sensors.isParasitePowerMode()) Serial.println(F("ON"));
//else Serial.println(F("OFF"));
//Serial.println (F(""));
//Set the sensor resolution
//Resolution can be set from 9 to 12 bit. The higher the resolution, the slower the read time
//12 bit resolution is still plenty fast for most applications. We're talking about milliseconds differences
sensors.setResolution(atticSensor, 12);
sensors.setResolution(outsideSensor, 12);
//set pins to output
pinMode (fan, OUTPUT);
//so that the initial state is known, turn off fan and set fanOn to false
digitalWrite (fan, LOW);
fanOn =false;
fanStateChanged = false; // set default setting
readingLast = 0 - readingDelay; // to force early read
ThingSpeakGetLast= 0-ThingSpeakGetDelay; // to force early GET
//DEBUGmem if (dbgmem) {
//DEBUGmem Serial.println(F("*Free RAM = ")); //F function does the same and is now a built in library, in IDE > 1.0.0
//DEBUGmem Serial.println(freeMemory(), DEC); // print how much RAM is available.
//DEBUGmem }
//#######################################################################################
//########################### get SMS acct balance #########################
//#######################################################################################
floatGET = ThingSpeak.readFloatField(dataChannelID,5U,APIKEYdataread); // SMSbalance
if (ThingSpeak.getLastReadStatus() != 200)
{Serial.print(F("Failed to get SMSbalance: "));
Serial.println(ThingSpeak.getLastReadStatus());
}
else {
Serial.print(F("Got SMSbalance="));
Serial.println(floatGET);
if (floatGET > 15)
{Serial.print(F("Invalid SMSbalance="));
Serial.println(floatGET);
}
else if (floatGET>0)
{SMSbalance = floatGET;}
else { // assume value was never written to the data field
ThingSpeak.setField(5,SMSbalance);
}
}
//#######################################################################################
//Serial.println(F("starting loop "));
}
//##################################################################################
//############################# LOOP #######################################
//##################################################################################
void loop ()
{
/*
*
/*Serial.print(F("starting loop "));
Serial.print(millis());
Serial.print(F(" "));
Serial.print(ThingSpeakGetLast);
Serial.print(F(" "));
Serial.println(ThingSpeakGetDelay);
*/
/*Serial.print(ThingSpeakGetLast);
Serial.print(F("---"));
Serial.print(ThingSpeakGetDelay);
Serial.print(F("---"));
Serial.println(ShutDown);
*/
//######################################################################################
//############################ GET CONTROL VARIABLES FROM ThingSpeak ################
//######################################################################################
// int readIntField (unsigned long channelNumber, unsigned int field, const char *readAPIKey)
// intGET = ThingSpeak.readIntField(ctrlChannelID,1U,APIKEYctrlread); //maxfanMinutesOn
//ctrlChannelID: field 1=maxFanTimeOn; field 2=fanRestTime;
// field 3=vacationMode;
// field 4=maxSummerDiff; field 5= minSummerDiff;
// field 6=SMSlevel
getOK=true; // initialize -- a GET failure on any of the control variables will set this to false=>don't reset GET timer
if (millis() - ThingSpeakGetLast >= ThingSpeakGetDelay)
{
intGET = ThingSpeak.readIntField(ctrlChannelID,1U,APIKEYctrlread); // maxfanMinutesOn
if (ThingSpeak.getLastReadStatus() != 200)
{getOK=false;}
else {
Serial.print(F("Got maxfanMinutesOn="));
Serial.println(intGET);
if (intGET <5 || intGET > 60)
{Serial.println(F("Invalid maxfanMinutesOn")); }
else
{maxfanMinutesOn = intGET;}
}
intGET = ThingSpeak.readIntField(ctrlChannelID,2U,APIKEYctrlread); // fanRestMinutes
if (ThingSpeak.getLastReadStatus() != 200)
{getOK=false;}
else {
Serial.print(F("Got fanRestMinutes="));
Serial.println(intGET);
if (intGET < 5 || intGET > 60)
{Serial.println(F("Invalid fanRestMinutes "));}
else
{fanRestMinutes = intGET;}
}
intGET = ThingSpeak.readIntField(ctrlChannelID,3U,APIKEYctrlread); // vacationMode
if (ThingSpeak.getLastReadStatus() != 200)
{getOK=false;}
else {
Serial.print(F("Got vacationMode="));
Serial.println(intGET);
if (intGET != 0 && intGET != 1)
{Serial.println(F("Invalid vacationMode " ));}
else
{vacationMode = intGET;}
}
intGET = ThingSpeak.readIntField(ctrlChannelID,4U,APIKEYctrlread); // maxSummerDiff
if (ThingSpeak.getLastReadStatus() != 200)
{getOK=false;}
else
{
Serial.print(F("Got maxSummerDiff="));
Serial.println(intGET);
if (intGET < 10)
{
Serial.println(F("Invalid maxSummerDiff - setting to 10 " ));
maxSummerDiff=10;
}
else
{maxSummerDiff = intGET;}
}
intGET = ThingSpeak.readIntField(ctrlChannelID,5U,APIKEYctrlread); // minSummerDiff
if (ThingSpeak.getLastReadStatus() != 200)
{getOK=false;}
else {
Serial.print(F("Got minSummerDiff="));
Serial.println(intGET);
if (maxSummerDiff-intGET<5 || intGET<5)
{
minSummerDiff=maxSummerDiff-5;
Serial.print(F("Invalid minSummerDiff - setting to maxSummerDiff-5 = " ));
Serial.println(minSummerDiff);
}
else
{minSummerDiff = intGET;}
}
intGET = ThingSpeak.readIntField(ctrlChannelID,6U,APIKEYctrlread); // SMSlevel
if (ThingSpeak.getLastReadStatus() != 200)
{getOK=false;}
else {
Serial.print(F("Got SMSlevel="));
Serial.println(intGET);
if (intGET<0 || intGET>2)
{
SMSlevel=1;
Serial.println(F("Invalid SMSlevel - setting to 1 = " ));
}
else
{SMSlevel = intGET;}
}
intGET = ThingSpeak.readIntField(ctrlChannelID,7U,APIKEYctrlread); // ShutDown
if (ThingSpeak.getLastReadStatus() != 200)
{getOK=false;}
else {
Serial.print(F("Got ShutDown="));
Serial.println(intGET);
if (intGET<0 || intGET>1)
{
ShutDown=1;
Serial.println(F("Invalid ShutDown - setting to 1 = " ));
}
else
{ShutDown = intGET;}
}
if (getOK == true) // indicates the 'gets' occured without a hitch
{
ThingSpeakGetLast = millis();
}
} // if (millis() - ThingSpeakGetLast >= ThingSpeakGetDelay)
//#############################################################################################
//############################ end of GET CONTROL VARIABLES FROM ThingSpeak ################
//#############################################################################################
if (maxSummerDiff-minSummerDiff < 5) {maxSummerDiff=minSummerDiff+5;}
/*
HTTP status codes. Negative values indicate an error generated by the library. Possible response codes:
200: OK / Success
400: Bad Request (e.g., malformed request syntax, size too large, invalid request message framing, or deceptive request routing)
404: Incorrect API key (or invalid ThingSpeak server address)
-101: Value is out of range or string is too long (> 255 characters)
-201: Invalid field number specified
-210: setField() was not called before writeFields()
-301: Failed to connect to ThingSpeak
-302: Unexpected failure during write to ThingSpeak
-303: Unable to parse response
-304: Timeout waiting for server to respond
-401: Point was not inserted (most probable cause is the rate limit of once every 15 seconds)
*/
//##################################################################################
//########################### READ SENSORS ################################
if ((millis() - readingLast >= readingDelay) && ShutDown == 0) // millis() returns an unsigned long integer
{
Serial.print(F("Reading sensors at millis = ")); //hang
Serial.println(millis()); //hang
//get the values from the DS8B20's
sensors.requestTemperatures();
//assign value to variables
attic = (sensorValue(atticSensor));
outside = (sensorValue(outsideSensor));
//here is where we decide if the reading from the DS18B20 is valid. I use simple comparisons here.
//If thermometer is hooked up wrong, or wires cross etc. , they can return a temp of exactly
//185.0 or 32.0 degrees F. This would indicate a bad sensor reading.
//TODO improve. The method below may be superfluous
//DEBUG if (dbg) Serial.print (F("Validating sensor readings..."));
if ( attic == 185.0 || attic == 32.0 ) {
sensorsOK = false;
}
else if (outside < -30 || outside > 120 || outside == 185.0 || outside == 32.0) {
sensorsOK = false;
}
else {
sensorsOK = true;
}
//printState(F("1"));
//if sensor readings are invalid, print to serial and/or send SMS
if (!sensorsOK) {
invalidCount++; //increment the invalid counter
if (invalidCount > 20) // must get 20 bad reads in a row before the system is shut down.
{
if (SMSlevel>0) {sendSMS(WadesCell,URLEncode(F("TOO MANY BAD READS -- SHUTTING DOWN")));}
Serial.println (F("TOO MANY BAD READS -- SHUTTING DOWN")); //hang
turnFanOff();
ShutDown = 1;
}
}
else { // sensor readings are valid, so make fan decisions
//##################################################################################
//########################### GATHER FAN DECISION INFO ##########################
//##################################################################################
//DEBUG if (dbg) Serial.println (F("Valid"));
readingLast = millis();
if (invalidCount>20) {
if (SMSlevel>0) {sendSMS(WadesCell, URLEncode(F("Finally got a good sensor read -- STARTING UP")));}
ShutDown = 0;
}
invalidCount = 0; //we got a good read, so invalid count is reset to zero.
differential= attic-outside;
if (differential < 0) {differential = 0;} // differential should never be negative
//SLOPE addAtticTemp(); // adds most recent attic temp & temstamp to arrays 'prevAtticTmp' & 'prevAtticTim'
// 'prevAtticTmp and prevAtticTim contain 6 most recent attic temp readings & timestamps,
// with prevAtticXXX[5] being the oldest and prevAtticXXX[0] being the most recent
// get slope of most recent attic temps
//SLOPE calcSlope();
//Decide if it is summertime, wintertime, or neither
if(outside < setWinterMax) {
winter = true;
summer = false;
}
else if(outside > setSummerMin) {
summer = true;
winter = false;
minAchieved = differential < float(minSummerDiff);
maxExceeded = differential > float(maxSummerDiff);
}
else {
summer=false;
winter=false;
}
if (((vacationMode == 1 && summer) || (!summer && !winter)) && fanOn) // should continue to operate in winter even when no one is home
{
//DEBUG if (dbg) Serial.print(F("ON VACATION!"));
turnFanOff();
}
else // determine what to do with fan
{
// How long has fan been in its current state?
if (fanOn) {
fanMinutesOn = (millis() - fanStartTime)/1000/60; // unsigned result should roll over
}
else {
fanMinutesOff = (millis() - fanStopTime)/1000/60; // unsigned result should roll over
}
//printState(F("B4 ctl logic"));
//DEBUG if (dbg) printState();
//####################################################################################################
//################ CONTROL LOGIC that decides when to turn on and off the fan ####################
//####################################################################################################
//DEBUG if (dbg) {
Serial.println (F("Entering CTRL LOGIC: ")); //hang
//DEBUG Serial.print (F(" milli = "));
//DEBUG Serial.print (millis());
//DEBUG Serial.println (F(" #######################################"));
//DEBUG }
if (summer) {
if(!fanOn) {
if(maxExceeded) {
if(fanMinutesOff > fanRestMinutes) { //fan is off, max threshold exceeded, & fan has rested long enough
//DEBUG if (dbg) Serial.print (F("Turn fan on" ));
turnFanOn(); // so turn it off
}
else { // fan is off, max threshold exceeded, but fan not rested enough
//DEBUG if (dbg) Serial.print (F("Leave fan off" ));
} // so leave it off
}
else { // fan is off, and max threshold not reached
} // so leave it off
} // (end of "if(!fanOn)"
else { // fan is on
if(fanMinutesOn > maxfanMinutesOn) { //fan has been on too long
//DEBUG if (dbg) Serial.print(F("FAN EXCEEDED MAX TIME ON!!"));
turnFanOff(); // so turn it off
}
else if (minAchieved) { //fan on, but not too long yet, but min threshold achieved
//DEBUG if (dbg) Serial.print(F("MIN. DIFF. ACHIEVED"));
turnFanOff(); // so turn it off
}
else { //fan on, but not too long yet, and min threshold not yet achieved
//...so determine if slope warrants leaving it on
} // so leave it on
} // ############################################################################################(end of "if fan is on")
} // (end of "if summer")
else if (winter) {
if(!fanOn) {
if(attic > 32 && outside < 32) {
if(fanMinutesOff > fanRestMinutes) { //fan is off, ice dam conditions exist, & fan has rested long enough
//DEBUG if (dbg) Serial.print (F("Turn fan on" ));
turnFanOn(); // so turn it off
}
else { // fan is off, ice dam conditions exist, but fan not rested enough
//DEBUG if (dbg) Serial.print (F("Leave fan off" ));
} // so leave it off
}
else { // fan is off, and ice dam conditions do not exist
} // so leave it off
} // (end of "if(!fanOn)"
else { // fan is on
if(fanMinutesOn > maxfanMinutesOn) { //fan has been on too long
//DEBUG if (dbg) Serial.print(F("FAN EXCEEDED MAX TIME ON!!"));
turnFanOff(); // so turn it off
}
else if (attic <= 32 && outside >= 31) { //fan on, but not too long yet, but ice dam conditions do not exist
//DEBUG if (dbg) Serial.print(F("ICE DAM CONDITIONS NO LONGER EXIST"));
turnFanOff(); // so turn it off
}
else if (attic < 30 && fanMinutesOn > 10) { //fan on, but not too long yet, and ice dam conditions still exist
//...so determine if slope warrants leaving it on
turnFanOff();
} // so leave it on
} // ############################################################################################(end of "if fan is on")
} // (end of "if winter")
else { //neither summer or winter
if (fanOn) {
//DEBUG if (dbg) Serial.print(F("Neither summer or winter-"));
turnFanOff();
}
}
} // "if (((vacationMode == 1 && summer) || (!summer && !winter)) && fanOn)"
//DEBUG if (dbg) {
Serial.println (F("Leaving CNTL LOGIC")); //hang
//DEBUG Serial.println (F(" #######################################"));
//DEBUG }
} // sensor readings are ok
} // if (millis() - readingLast >= readingDelay), i.e. its time to read sensors
//####################################################################################
//############################ put data to ThingSpeak ############################
//####################################################################################
if (((millis() - ThingSpeakPutLast >= ThingSpeakPutDelay) || (fanStateChanged && (millis()-ThingSpeakPutLast > 15000))) && ShutDown == 0)
{
Serial.print(F("PUT attempt to dataChannelID="));
Serial.print(dataChannelID);
Serial.print(F("; at millis = "));
Serial.println(millis());
// Serial.println(attic);
// Serial.println(outside);
//post to ThingSpeak feed
//if (sensorsOK)
//{
//the datastreams number corresponds to the order of the datastreams array.
if (use_ThingSpeak) {
ThingSpeak.setField(1,attic);
ThingSpeak.setField(2,outside);
ThingSpeak.setField(3,fanOn);
ThingSpeak.setField(4,sensorsOK);
ThingSpeak.setField(5,SMSbalance);
/*
ThingSpeak.setField(5,int(fanMinutesOn));
ThingSpeak.setField(6,int(fanMinutesOff));
*/
ret = ThingSpeak.writeFields(dataChannelID,APIKEYdatawrite);
//DEBUG Serial.println(F("Uploading to ThingSpeak"));
if (ret != 200)
{
Serial.print(F(" -- ThingSpeak PUT failed, ret= "));
Serial.println(ret);
putOK = false;
}
else
{
Serial.println(F(" -- ThingSpeak PUT succeeded "));
putOK = true;
}
//DEBUG if (dbg) {
//DEBUG Serial.print(F("ThingSpeak put ret= "));
//DEBUG Serial.print(ret);
//DEBUG if (ret =200)
//DEBUG {
//DEBUG Serial.print(F(" which means OK"));
//DEBUG }
//DEBUG else
//DEBUG {
//DEBUG Serial.print(F(" WHICH MEANS ERROR OCCURED!"));
//DEBUG }
//DEBUG Serial.println(F(""));
//DEBUG Serial.println(F(""));
//DEBUG }
}
//}
ThingSpeakPutLast = millis();
}
//DEBUGmem if (dbgmem) {
//DEBUGmem Serial.println(F("Free RAM = ")); //F function does the same and is now a built in library, in IDE > 1.0.0
//DEBUGmem Serial.println(freeMemory(), DEC); // print how much RAM is available.
//DEBUGmem }
} //And thats the end of the loop.
//##################################################################################
//##################################################################################
//##################################################################################
//##################################################################################
//Here are a couple simple functions used in the code to make it a little cleaner.
//##################################################################################
//sensorValue function---------------------------------------------------------
//reads temp from sensors and returns as floating point value in F.
//To use celsius instead, the last line should be changed to "return tempC".
float sensorValue (byte deviceAddress[])
{
float tempC = sensors.getTempC (deviceAddress);
float tempF = (DallasTemperature::toFahrenheit(tempC));
return tempF;
}
//##################################################################################
// function to maintain array of most recent attic temp readings
//SLOPE void addAtticTemp()
//SLOPE {
//SLOPE for (int i = 5; i>0;i--) { // 0th is the most recent, 5th is the oldest
//SLOPE prevAtticTmp[i]=prevAtticTmp[i-1];
//SLOPE prevAtticTim[i]=prevAtticTim[i-1];
//SLOPE }
//SLOPE if (nAtticTemps <6)
//SLOPE {
//SLOPE nAtticTemps=nAtticTemps + 1;
//SLOPE }
//SLOPE }
//##################################################################################
// function to calc slope of most recent attic temps
//SLOPE void calcSlope()
//SLOPE {
//SLOPE
//SLOPE }
//##################################################################################
// function to turn fan off-----------------------------------------------------
void turnFanOff ()
{
// Serial.println (F("TURNING FAN OFF"));
fanStartTime=0UL;
fanStopTime=millis();
fanMinutesOn=0;
digitalWrite(fan,LOW);
if (fanOn)
{
/*
Serial.print(F("TURNING FAN OFF at "));
Serial.println(fanStopTime);
*/
fanStateChanged = true;
// printState(F("turnFanOff"));
}
fanOn=false;
if (SMSlevel==2) {sendSMS(WadesCell, URLEncode(F("Fan Turned Off")));}
//DEBUG if (dbg) printState();
}
//##################################################################################
// function to turn fan on-------------------------------------------------------
void turnFanOn ()
{
// Serial.println (F("TURNING FAN ON"));
fanStartTime=millis();
fanStopTime=0UL;
fanMinutesOff=0;
digitalWrite(fan,HIGH);
if (!fanOn)
{
/* Serial.print(F("TURNING FAN ON at "));
Serial.println(fanStartTime); */
fanStateChanged = true;
// printState(F("turnFanOn"));
}
fanOn=true;
if (SMSlevel==2) {sendSMS(WadesCell, URLEncode(F("Fan Turned On")));}
//DEBUG if(dbg) printState();
}
//##################################################################################
void sendSMS(String number,String message)
//this function will send the sms
//the first argument is the number to send to, formatted like this +12345678901
//the second argument is the body of the text message, which must be within URLEncode()
//sendSMS(WadesCell, URLEncode(F("Hello World!")));
{
// Make a TCP connection to remote host
Serial.println(F("Sending SMS"));
if (client.connect(thingSpeakAddress, 80))
{
//should look like this...
//api.thingspeak.com/apps/thinghttp/send_request?api_key={api key}&number={send to number}&message={text body}
client.print(F("GET /apps/thinghttp/send_request?api_key="));
client.print(ThingHTTPapi);
client.print(F("&number="));
client.print(number);
client.print(F("&message="));
client.print(message);
client.println(F(" HTTP/1.1"));
client.print(F("Host: "));
client.println(thingSpeakAddress);
client.println(F("Connection: close"));
client.println();
SMSbalance=SMSbalance-0.0075;
}
else
{
Serial.print(F("Connection failed to "));
Serial.println(thingSpeakAddress);
}
// Check for a response from the server, and route it
// out the serial port.
while (client.connected())
{
if ( client.available() )
{
char c = client.read();
Serial.print(c);
}
}
Serial.println();
Serial.println(F("******* end of SMS response ********"));
client.stop();
}
//##################################################################################
String URLEncode(String str)
{
String encodedString="";
char c;
char code0;
char code1;
char code2;
for (int i =0; i < str.length(); i++){
c=str.charAt(i);
if (c == ' '){
encodedString+= '+';
} else if (isalnum(c)){
encodedString+=c;
} else{
code1=(c & 0xf)+'0';
if ((c & 0xf) >9){
code1=(c & 0xf) - 10 + 'A';
}
c=(c>>4)&0xf;
code0=c+'0';
if (c > 9){
code0=c - 10 + 'A';
}
code2='\0';
encodedString+='%';
encodedString+=code0;
encodedString+=code1;
//encodedString+=code2;
}
yield();
}
return encodedString;
}
/*
//##################################################################################
void printState (String location) { //--------------------------------------------------------
//prints values to serial port and lcd
Serial.println();
Serial.print(location);
Serial.print(F(" STATE============ millis = "));
Serial.println(millis());
Serial.print (F("Vacation? "));
Serial.print (vacationMode);
Serial.print (F(" ; sensorsOK "));
Serial.print (sensorsOK);
Serial.print (F(" ; Summ? "));
Serial.print (summer);
Serial.print (F(" ; Wint? "));
Serial.println (winter);
Serial.print (F("maxSummerDiff: "));
Serial.print (maxSummerDiff);
Serial.print (F(" ; minSummerDiff: "));
Serial.println (minSummerDiff);
Serial.print (F("attic "));
Serial.print (attic);
Serial.print (F("; outside "));
Serial.print (outside);
Serial.print (F("; differential "));
Serial.println (differential);
Serial.print (F("Max Diff exceeded? "));
Serial.print (maxExceeded);
Serial.print (F("; Min Diff achieved? "));
Serial.println (minAchieved);
Serial.print (F("fan on? "));
Serial.print (fanOn);
Serial.print (F("; fanStartTime "));
Serial.print (fanStartTime);
Serial.print (F("; fanMinutesOn "));
Serial.print (fanMinutesOn);
Serial.print (F("; fanStopTime "));
Serial.print (fanStopTime);
Serial.print (F("; fanMinutesOff "));
Serial.print (fanMinutesOff);
Serial.print (F(" ; maxfanMinutesOn "));
Serial.print (maxfanMinutesOn);
Serial.print (F(" ; fanRestMinutes "));
Serial.println (fanRestMinutes);
Serial.println (F("=================="));
//WVC printLCD();
}
*/