Stap 9: Maak de knoppen iets doen.
Elke knop heeft een functie en het doel worden toegewezen. In ons geval, zijn de eerste en tweede knoppen voor vergroten of verkleinen van de knoppen respectievelijk, en de derde knop is voor schakelen tussen uitvoeren of modus te houden. Ik heb ook een vierde knop, maar ik weet niet of haar nodig. Voor nu laat ik het in, maar we zullen zien.
De definitieve Code:
Voor de toepassing van dit Instructable is dit de definitieve code
/* /* This is the application that I will be using to control the furnace. The concept is this: I have 3 zones I will be monitoring. Each zone will have at least one zone damper which will be controlled by the arduino application. Each zone will have a single thermometer which will report back to the controller. Depending on the thermometer reading and the thermostat setting, the dampers will open or shut and the central furnace will be turned on. Once an acceptable temperature has been reached in all zones, the furnace will turn off. In addition to general heating cycles, the system will be programmable. At this time, however, the programming cycle will be handled here, not via the thermostat. IMPORTANT TEST CASES: (to be added as I think of them/come across them * Need to ensure that the furnace is always off if all 3 dampers are closed. * dampers should be open unless they specifically need to be closed. * if system is set to "hold", all zones are set to open and the programming does not run. * if system is overridden but not held, the override stops once the next programmed interval comes up * can only override to explicit upper and lower bounds. */ #include <LiquidCrystal.h> //This is a library needed for the LCD display #include <OneWire.h> //This is a library needed for the thermometer readings #include <DallasTemperature.h> //This is a temperature library #include <Wire.h> #include "RTClib.h" // Connections: // rs (LCD pin 4) to Arduino pin 12 // rw (LCD pin 5) to Arduino pin 11 // enable (LCD pin 6) to Arduino pin 10 // LCD pin 15 to Arduino pin 13 // LCD pins d4, d5, d6, d7 to Arduino pins 5, 4, 3, 2 LiquidCrystal lcd(12, 11, 10, 5, 4, 3, 2); byte deg[8] = { B01000, B10100, B01000, B00111, B00100, B00110, B00100, B00100 }; // const int backlight = 13; //controls backlight #define THERM_BUS 6 // all temperature readings come in via this single pin. OneWire oneWire(THERM_BUS); DallasTemperature sensors(&oneWire); DeviceAddress zone1Therm = { 0x28, 0x48, 0x39, 0x11, 0x04, 0x00, 0x00, 0x94}; DeviceAddress zone2Therm = { 0x28, 0xF2, 0x1F, 0x11, 0x04, 0x00, 0x00, 0x38}; DeviceAddress zone3Therm = { 0x28, 0xBA, 0xF3, 0x10, 0x04, 0x00, 0x00, 0x98}; RTC_DS1307 RTC; // START PROGRAMMING // Times are 6 AM, 8 AM, 3PM, and 9PM int* PROGRAMMED_VALUES[7]; int SUNDAY_VALUES[4] = { 67, 67, 67, 60}; int MONDAY_VALUES[4] = { 67, 67, 67, 60}; int TUESDAY_VALUES[4] = { 67, 67, 67, 60}; int WEDNESDAY_VALUES[4] = { 67, 67, 67, 60}; int THURSDAY_VALUES[4] = { 67, 67, 67, 60}; int FRIDAY_VALUES[4] = { 67, 67, 90, 75}; int SATURDAY_VALUES[4] = { 67, 67, 67, 60}; //END PROGRAMMING float zone1Temp = 0; float zone2Temp = 0; float zone3Temp = 0; boolean z1Check = 0; boolean z2Check = 0; boolean z3Check = 0; boolean isOverridden = false; boolean furnaceState = false; //with furnaceState, false means it's currently off, true means its currently on. String thermState = "run"; int overrideValue = 0; int currentMode = 0; const int furnacePin = 7; int zoneCounter = 0; int a = 0; int loopCounter = 0; int z1Pin = 8; int z2Pin = 9; int z3Pin = 13; //scavenging backlight pin for z3 void setup() { Serial.begin(57600); // This connects the arduino to the computer //TEMPERATURE SENSOR SETUP sensors.begin(); sensors.setResolution(zone1Therm, 10); lcd.createChar(0, deg); // RTC SETUP. Wire.begin(); RTC.begin(); if(!RTC.isrunning()){ log("ERROR", "RTC is NOT running!"); //RTC.adjust(DateTime(__DATE__, __TIME__)); //THIS ONLY NEEDS TO BE UNCOMMENTED IF YOU ARE SETTING UP YOUR RTC FOR THE FIRST TIME! ONCE IT IS SET, THIS MUST BE DISABLED! } //SETUP OUTPUTS pinMode(furnacePin, OUTPUT); pinMode(z1Pin, OUTPUT); pinMode(z2Pin, OUTPUT); pinMode(z3Pin, OUTPUT); // eventually fit backlight control back into arduino. For now we'll just set it to constant. // digitalWrite(backlight, HIGH); //SETUP INPUTS pinMode(A0, INPUT_PULLUP); //SETTING UP THE WEEKLY PROGRAM PROGRAMMED_VALUES[0] = SUNDAY_VALUES; PROGRAMMED_VALUES[1] = MONDAY_VALUES; PROGRAMMED_VALUES[2] = TUESDAY_VALUES; PROGRAMMED_VALUES[3] = WEDNESDAY_VALUES; PROGRAMMED_VALUES[4] = THURSDAY_VALUES; PROGRAMMED_VALUES[5] = FRIDAY_VALUES; PROGRAMMED_VALUES[6] = SATURDAY_VALUES; } // this runs the continual loop void loop() { delay(100); //DELAY CURRENTLY SET TO .1 SECONDS. WILL ADJUST AS NEEDED //DATE/TIME LOGIC // if(loopCounter % 2 == 0){ // log("TIME", "Loop initiated at " + getDateAndTime()); // } //TEMPERATURE LOGIC // logTemperatureData(); if(loopCounter % 30 == 0) { log("info", (String)loopCounter); //changes output every 3 seconds outputToScreen(); } // The following allows us to only check furnace state every 60 seconds while reading the button changes in effective realtime if(loopCounter % 60 == 0) { log("info", (String)loopCounter); sensors.requestTemperatures(); zone1Temp = getTemperature(zone1Therm); zone2Temp = getTemperature(zone2Therm); zone3Temp = getTemperature(zone3Therm); z1Check = checkZoneTemp(zone1Temp); z2Check = checkZoneTemp(zone2Temp); z3Check = checkZoneTemp(zone3Temp); loopCounter = 0; if(z1Check || z2Check || z3Check){ startFurnace(); } else { stopFurnace(); } if(z1Check && furnaceState) { closeDamper(z1Pin); } else { openDamper(z1Pin); } if(z2Check && furnaceState) { closeDamper(z2Pin); } else { openDamper(z2Pin); } if(z3Check && furnaceState) { closeDamper(z3Pin); } else { openDamper(z3Pin); } } a = analogRead(0); //THESE NUMBERS CHANGE DEPENDING ON YOUR RESISTORS. int targetTemp = getProgrammedTargetTemperature(); if(a < 20) { //This means temperature up. isOverridden = true; if(overrideValue != targetTemp && overrideValue != 0){ overrideValue++; } else { overrideValue = targetTemp + 1; } if(overrideValue > 80){ overrideValue = 80; } log("info", "temperature up to " + (String)overrideValue); loopCounter = -1; //THIS RESETS THE LOOP COUNTER, MEANING EVERYTHING WILL BE RUN AGAIN } if(a < 30 && a > 20) { //This means temperature down. isOverridden = true; if(overrideValue != targetTemp && overrideValue > 0){ overrideValue--; } else { overrideValue = targetTemp - 1; } if(overrideValue < 50) { overrideValue = 50; } log("info", "temperature down to " + (String)overrideValue); loopCounter = -1; //THIS RESETS THE LOOP COUNTER, MEANING EVERYTHING WILL BE RUN AGAIN } if(a < 45 && a > 30){ //This is a toggle for hold/run if(thermState == "hold") { thermState = "run"; isOverridden = false; log("info", "setting thermState to run"); } else { thermState = "hold"; isOverridden = true; log("info", "setting thermState to hold"); } isOverridden = true; loopCounter = -1; //THIS RESETS THE LOOP COUNTER, MEANING EVERYTHING WILL BE RUN AGAIN } if(a < 60 && a > 45){ //DO I NEED A FOURTH BUTTON? } loopCounter++; } boolean checkZoneTemp(int temperature) { int temp = getTargetTemperature(); if(temperature > temp){ return false; } return true; } void logTemperatureData() { log("Zone1Temp", (String)(int)zone1Temp); log("Zone2Temp", (String)(int)zone2Temp); log("Zone3Temp", (String)(int)zone3Temp); } void outputToScreen() { zoneCounter++; if(zoneCounter == 1){ String val = "Zone1: " + (String)(int)zone1Temp; val += " (" + (String)getTargetTemperature(); val += (String)")"; toScreen(val, "Mode: " + getModeString()); } else if (zoneCounter == 2){ String val = "Zone2: " + (String)(int)zone2Temp; val += " (" + (String)getTargetTemperature(); val += (String)")"; toScreen(val, "Furnace: " + getFurnaceState()); } else { zoneCounter = 0; String val = "Zone3: " + (String)(int)zone3Temp; val += " (" + (String)getTargetTemperature(); val += (String)")"; toScreen(val, "Open: " + getActiveZonesForScreen()); } } void checkMode() { int mode = getMode(); if(mode != currentMode) { currentMode = mode; if(isOverridden) { if(thermState == "run") { //This logic simply overturns the override value and resumes the program if the thermstate is not held. Otherwise, the overrideValue is used. isOverridden = false; } } } } int getTargetTemperature(){ checkMode(); if(isOverridden) { return overrideValue; } else { return getProgrammedTargetTemperature(); } } int getProgrammedTargetTemperature() { int mode = getMode(); DateTime now = RTC.now(); int date = now.dayOfWeek(); int* values = { 0 }; values = PROGRAMMED_VALUES[date]; // log("info", "getting programmed target temp: " + (String)values[mode]); return values[mode]; } String getActiveZonesForScreen(){ String retVal = ""; if(z1Check && !furnaceState){ retVal += "1,"; } if(z2Check && !furnaceState) { retVal += "2,"; } if(z3Check && !furnaceState){ retVal += "3"; } if(retVal.length() == 0) { retVal += "None"; } return retVal; } String getFurnaceState(){ if(furnaceState){ return "On"; } else { return "Off"; } } String getModeString() { if(isOverridden) { if(thermState == "run") { return "Override"; } else { return "HOLD"; } } int mode = getMode(); switch (mode) { case 0: return "Wake"; case 1: return "Away"; case 2: return "Home"; case 3: return "Sleep"; case -1: return "Error"; default: return "N/A"; } } int getMode() { DateTime now = RTC.now(); int hour = now.hour(); if(hour < 6 || hour >= 21) { return 3; } if(hour >= 6 && hour < 8){ return 0; } if(hour >=8 && hour < 15){ return 1; } if(hour >= 15 && hour < 21){ return 2; } return -1; } float getTemperature(DeviceAddress deviceAddress) { float tempC = sensors.getTempC(deviceAddress); //Serial.println(tempC); if(tempC == -127){ Serial.println("ERROR getting temperature."); } return DallasTemperature::toFahrenheit(tempC); } //convenience function. Handles all the boilerplate for writing to the LCDScreen void toScreen(String line1Value, String line2Value) { lcd.begin(16,2); lcd.clear(); lcd.setCursor(0,0); lcd.print(line1Value); lcd.setCursor(0,1); lcd.print(line2Value); } String getDateAndTime() { DateTime now = RTC.now(); return String(now.year()) + "/" + String(now.month()) + "/" + String(now.day()) + " " + String(now.hour()) + ":" + String(now.minute()) + ":" + String(now.second()); } void openDamper(int damper) { if(damper == z1Pin || damper == z2Pin || damper == z3Pin) { digitalWrite(damper, HIGH); // log("info", "Opening damper" + damper); } else { log("error", "Problem opening damper. Damper not found. Was expecting 1, 2, or 3. Got " + damper); } } void closeDamper(int damper) { if(damper == z1Pin || damper == z2Pin || damper == z3Pin) { digitalWrite(damper, LOW); // log("info", "Closing damper" + damper); } else { log("error", "Problem closing damper. Damper not found. Was expecting 1, 2, or 3. Got " + damper); } } //function for logging messages to the console. //Eventually plan to write to logfile on RPi void log(String code, String message) { Serial.println(code + ": " + message); } void recordData(String type, String object, String value) { // TODO - output to RPi database } void startFurnace(){ //Send appropriate signal to the furnace to start up if(!furnaceState) { furnaceState = !furnaceState; log("INFO", "STARTING FURNACE"); digitalWrite(furnacePin, HIGH); } } void stopFurnace(){ //Send appropriate signal to the furnace to stop if(furnaceState){ furnaceState = !furnaceState; log("INFO", "STOPPING FURNACE"); digitalWrite(furnacePin, LOW); } }