/** * Prints the temperature information for our sensors onto the touchscreen. * @returns Time it took to run the function */ unsigned long Ohmbrewer::Thermostat::displayThermTemp(Ohmbrewer::Screen *screen) { unsigned long start = micros(); //"Temp °C: 88.0 90.0" // current target // If current == target, we'll default to yellow, 'cause we're golden... uint16_t color = screen->YELLOW; if(getSensor()->getTemp()->c() > getTargetTemp()->c()) { // above target temp color = screen->RED; } else if(getSensor()->getTemp()->c() < getTargetTemp()->c()) { // below target temp color = screen->CYAN; } //Label screen->setTextColor(screen->WHITE, screen->DEFAULT_BG_COLOR); screen->print("Temp "); screen->writeDegree(); screen->print("C:"); //Temps getSensor()->getTemp()->displayTempC(color, screen); screen->print(" "); //margin getTargetTemp()->displayTempC(screen->YELLOW, screen); screen->resetTextColor(); screen->println(""); return micros() - start; }
/** * Controls all the inner workings of the PID functionality * Should be called by _timer * * Controls the heating element Relays manually, overriding the standard relay * functionality * * The pid is designed to Output an analog value, but the relay can only be On/Off. * * "time proportioning control" it's essentially a really slow version of PWM. * first we decide on a window size. Then set the pid to adjust its output between 0 and that window size. * lastly, we add some logic that translates the PID output into "Relay On Time" with the remainder of the * window being "Relay Off Time" * * PID Adaptive Tuning * You can change the tuning parameters. this can be * helpful if we want the controller to be agressive at some * times, and conservative at others. * */ void Ohmbrewer::Thermostat::doPID(){ getSensor()->work(); setPoint = getTargetTemp()->c(); //targetTemp input = getSensor()->getTemp()->c();//currentTemp double gap = abs(setPoint-input); //distance away from target temp //SET TUNING PARAMETERS if (gap<10) { //we're close to targetTemp, use conservative tuning parameters _thermPID->SetTunings(cons.kP(), cons.kI(), cons.kD()); }else {//we're far from targetTemp, use aggressive tuning parameters _thermPID->SetTunings(agg.kP(), agg.kI(), agg.kD()); } //COMPUTATIONS _thermPID->Compute(); if (millis() - windowStartTime>windowSize) { //time to shift the Relay Window windowStartTime += windowSize; } //TURN ON if (getState() && gap!=0) {//if we want to turn on the element (thermostat is ON) //TURN ON state and powerPin if (!(getElement()->getState())) {//if heating element is off getElement()->setState(true);//turn it on if (getElement()->getPowerPin() != -1) { // if powerPin enabled digitalWrite(getElement()->getPowerPin(), HIGH); //turn it on (only once each time you switch state) } } //RELAY MODULATION if (output < millis() - windowStartTime) { digitalWrite(getElement()->getControlPin(), HIGH); } else { digitalWrite(getElement()->getControlPin(), LOW); } } //TURN OFF if (gap == 0 || getTargetTemp()->c() <= getSensor()->getTemp()->c() ) {//once reached target temp getElement()->setState(false); //turn off element getElement()->work(); // if (getElement()->getPowerPin() != -1) { // if powerPin enabled // digitalWrite(getElement()->getPowerPin(), LOW); //turn it off too TODO check this // } // Notify Ohmbrewer that the target temperature has been reached. Publisher pub = Publisher(new String(getStream()), String("msg"), String("Target Temperature Reached.")); pub.add(String("last_read_time"), String(getSensor()->getLastReadTime())); pub.add(String("temperature"), String(getSensor()->getTemp()->c())); pub.publish(); } }
/** Heater control routine. * controlTemp - current control temperature (room or composite of rooms) * heaterTemp - current electric heater temperature to control (ref: O1) * outgoingFluidTemp - current outgoing temperature of the fluid * (oven + electric heater, ref: O2) */ int controlHeater(float controlTemp, float heaterTemp, float outgoingFluidTemp) { /* First check heater is OK (wasn't overheated) */ if (heaterTemp > heaterCutOffTemp) { // -- Current date and time time_t now = time(NULL); // -- Persist failure info FILE *fp; fp = fopen(configuration.heaterFailurePath, "w"); if (NULL != fp) { const char* formatStr = "%s: Heater failure detected t=%4.2f.\r\n"; fprintf(fp, formatStr, ctime(&now), heaterTemp); fclose(fp); printf(formatStr, ctime(&now), heaterTemp); } else printf("Cannot write heater failure status file.\n\r"); setHeater(OFF); setPump(ON); exit(EXIT_FAIL); } if (outgoingFluidTemp > configuration.fluidElectricHeaterOffTemp && outgoingFluidTemp - heaterTemp > configuration.ovenExtraElectricHeaterOffTemp) { // Other heater has created enough temperature, no need to run electricity setHeater(OFF); return OFF; } else if (controlTemp < getTargetTemp()) { setHeater(ON); // setPump(ON); return ON; } else if (controlTemp > getTargetTemp() + configuration.tempDelta) { setHeater(OFF); // Dont stop pump until fluid temp will go down // Other heat sources may still be on return OFF; } // return current state return getHeaterState(); }
int main(int argc, const char** args) { // -- Set confguration.ini & others directories setDirectories(); // -- Check for previous fatal errors if (wasOverheated()) { printf("Previous heater failure detected. Cannot run.\n\r"); return EXIT_FAIL; } // -- Load settings from .ini file loadSettings(); initRoomDescriptors(); // debug stuff // printf("%s", asctime(&configuration.arrive)); // printf("%s", asctime(&configuration.dep)); // time_t tt = getHeatingStartTime(); // // printf("%s", ctime(&tt)); // printf("%f\n", getTargetTemp()); // // printf("%f\n", configuration.tempDelta); // printf("%f\n", configuration.fluidPumpOffTemp); // return; // end of debug stuff // -- Measure current temperatures and set out the target float controlTemp = getControlTemperature(); float outgoingFluidTemp = getT(outputSensor); float ingoingFluidTemp = getT(inputSensor); float electricHeaterTemp = getT(heaterSensor); float kitchenTemp = getT(kitchenSensor); float bathroomTemp = getT(bathRoomSensor); float sashaBedroomTemp = getT(childrenSmallSensor); float targetTemp = getTargetTemp(); // -- Control heater and pump int heaterState = controlHeater(controlTemp, electricHeaterTemp, outgoingFluidTemp); // -- Control sauna floor temp float saunaFloorTemp = getT(saunaFloorSensor); float saunaFloorTargetTemp = configuration.saunaFloorTemp; int saunaFloorHeatingState = controlSaunaFloor(saunaFloorTemp, saunaFloorTargetTemp); // -- Initizlize temp vector (no paritcular order) float tv[10]; int tvc = 0; tv[tvc++] = controlTemp; tv[tvc++] = electricHeaterTemp; tv[tvc++] = ingoingFluidTemp; tv[tvc++] = outgoingFluidTemp; tv[tvc++] = bathroomTemp; tv[tvc++] = kitchenTemp; tv[tvc++] = sashaBedroomTemp; int pumpState = controlPump(tv, tvc); // -- Individual rooms control /* Not tested yet */ int i; for (i=0; i<ROOMS_COUNT; i++) controlRoom(&roomControlDescriptors[i], targetTemp); // -- Dates: now and when to start heating next time by our arrival char nowStr[TBL], onStr[TBL]; getDateTimeStr(nowStr, TBL, time(NULL)); getDateTimeStr(onStr, TBL, getHeatingStartTime()); printf("%s|%4.2f|%4.2f|%4.2f| %4.2f |%4.2f|%4.2f|%4.2f|%4.2f| %4.2f|%4.2f |%4.2f|%d|%d|%4.2f|%d|%c|%c|%4.1f|%4.1f|%s\r\n", nowStr, electricHeaterTemp, ingoingFluidTemp, outgoingFluidTemp, getT(externalSensor), getT(amSensor), getT(bedroomSensor), getT(cabinetSensor), sashaBedroomTemp, kitchenTemp, bathroomTemp, controlTemp, heaterState, pumpState, saunaFloorTemp, saunaFloorHeatingState, (isPresence() ? 'P' : 'S'), (isSaving() ? 'N' : 'D'), targetTemp, saunaFloorTargetTemp, onStr ); return EXIT_OK; }