static void rfSourceNotify(RFSource &r, unsigned char event) { for (unsigned char i=0; i<TEMP_COUNT; ++i) if ((pid.Probes[i]->getProbeType() == PROBETYPE_RF12) && ((rfMap[i] == RFSOURCEID_ANY) || (rfMap[i] == r.getId())) ) { if (event == RFEVENT_Remove) pid.Probes[i]->calcTemp(0); else if (r.isNative()) pid.Probes[i]->setTemperatureC(r.Value / 10.0f); else { unsigned int val = r.Value; unsigned char adcBits = rfmanager.getAdcBits(); // If the remote is lower resolution then shift it up to our resolution if (adcBits < pid.getAdcBits()) val <<= (pid.getAdcBits() - adcBits); pid.Probes[i]->calcTemp(val); } } /* if probe is this source */ if (event & (RFEVENT_Add | RFEVENT_Remove)) outputRfStatus(); }
static void reportFanParams(void) { print_P(PSTR("HMFN" CSV_DELIMITER)); SerialX.print(pid.getMinFanSpeed(), DEC); Serial_csv(); SerialX.print(pid.getMaxFanSpeed(), DEC); Serial_csv(); SerialX.print(pid.getMinServoPos(), DEC); Serial_csv(); SerialX.print(pid.getMaxServoPos(), DEC); Serial_csv(); SerialX.print(pid.getOutputFlags(), DEC); Serial_nl(); }
static void newTempsAvail(void) { static unsigned char pidCycleCount; updateDisplay(); ++pidCycleCount; if ((pidCycleCount % 0x20) == 0) outputRfStatus(); outputCsv(); // We want to report the status before the alarm readout so // receivers can tell what the value was that caused the alarm checkAlarms(); if (g_LogPidInternals) pid.pidStatus(); if ((pidCycleCount % 0x04) == 1) outputAdcStatus(); ledmanager.publish(LEDSTIMULUS_Off, LEDACTION_Off); ledmanager.publish(LEDSTIMULUS_LidOpen, pid.isLidOpen()); ledmanager.publish(LEDSTIMULUS_FanOn, pid.isOutputActive()); ledmanager.publish(LEDSTIMULUS_FanMax, pid.isOutputMaxed()); ledmanager.publish(LEDSTIMULUS_PitTempReached, pid.isPitTempReached()); ledmanager.publish(LEDSTIMULUS_Startup, pid.getPitStartRecover() == PIDSTARTRECOVER_STARTUP); ledmanager.publish(LEDSTIMULUS_Recovery, pid.getPitStartRecover() == PIDSTARTRECOVER_RECOVERY); #ifdef HEATERMETER_RFM12 rfmanager.sendUpdate(pid.getPidOutput()); #endif }
void TempProbe::calcTemp(void) { const float ADCmax = (1 << (10+TEMP_OVERSAMPLE_BITS)) - 1; if (_accumulatedCount != 0) { unsigned int ADCval = _accumulator / _accumulatedCount; _accumulatedCount = 0; // Units 'A' = ADC value if (pid.getUnits() == 'A') { Temperature = ADCval; return; } if (ADCval != 0) // Vout >= MAX is reduced in readTemp() { float R, T; // If you put the fixed resistor on the Vcc side of the thermistor, use the following R = Steinhart[3] / ((ADCmax / (float)ADCval) - 1.0f); // If you put the thermistor on the Vcc side of the fixed resistor use the following //R = Steinhart[3] * ADCmax / (float)Vout - Steinhart[3]; // Units 'R' = resistance, unless this is the pit probe (which should spit out Celsius) if (pid.getUnits() == 'R' && this != pid.Probes[TEMP_PIT]) { Temperature = R; return; }; // Compute degrees K R = log(R); T = 1.0f / ((Steinhart[2] * R * R + Steinhart[1]) * R + Steinhart[0]); setTemperatureC(T - 273.15f); } /* if ADCval */ else Temperature = NAN; } /* if accumulatedcount */ if (hasTemperature()) { calcExpMovingAverage(TEMPPROBE_AVG_SMOOTH, &TemperatureAvg, Temperature); Alarms.updateStatus(Temperature); } else Alarms.silenceAll(); }
void ProbeAlarm::updateStatus(int value) { // Low: Arming point >= Thresh + 1.0f, Trigger point < Thresh // A low alarm set for 100 enables at 101.0 and goes off at 99.9999... if (getLowEnabled()) { if (value >= (getLow() + 1)) Armed[ALARM_IDX_LOW] = true; else if (value < getLow() && Armed[ALARM_IDX_LOW]) Ringing[ALARM_IDX_LOW] = true; } // High: Arming point < Thresh - 1.0f, Trigger point >= Thresh // A high alarm set for 100 enables at 98.9999... and goes off at 100.0 if (getHighEnabled()) { if (value < (getHigh() - 1)) Armed[ALARM_IDX_HIGH] = true; else if (value >= getHigh() && Armed[ALARM_IDX_HIGH]) Ringing[ALARM_IDX_HIGH] = true; } if (pid.isLidOpen()) Ringing[ALARM_IDX_LOW] = Ringing[ALARM_IDX_HIGH] = false; }
static void outputCsv(void) { #ifdef HEATERMETER_SERIAL print_P(PSTR("HMSU" CSV_DELIMITER)); pid.status(); Serial_nl(); #endif /* HEATERMETER_SERIAL */ }
static void reportLidParameters(void) { print_P(PSTR("HMLD" CSV_DELIMITER)); SerialX.print(pid.LidOpenOffset, DEC); Serial_csv(); SerialX.print(pid.getLidOpenDuration(), DEC); Serial_nl(); }
static void storeProbeType(unsigned char probeIndex, unsigned char probeType) { unsigned char ofs = getProbeConfigOffset(probeIndex, offsetof( __eeprom_probe, probeType)); if (ofs != 0) { pid.setProbeType(probeIndex, probeType); econfig_write_byte((unsigned char *)ofs, probeType); } }
void storeSetPoint(int sp) { // If the setpoint is >0 that's an actual setpoint. // 0 or less is a manual fan speed boolean isManualMode; if (sp > 0) { config_store_word(setPoint, sp); pid.setSetPoint(sp); isManualMode = false; } else { pid.setPidOutput(-sp); isManualMode = true; } config_store_byte(manualMode, isManualMode); }
void TempProbe::setTemperatureC(float T) { // Sanity - anything less than -20C (-4F) or greater than 500C (932F) is rejected if (T <= -20.0f || T > 500.0f) Temperature = NAN; else { if (pid.getUnits() == 'F') Temperature = (T * (9.0f / 5.0f)) + 32.0f; else Temperature = T; Temperature += Offset; } }
void storeLidParam(unsigned char idx, int val) { if (val < 0) val = 0; switch (idx) { case 0: pid.LidOpenOffset = val; config_store_byte(lidOpenOffset, val); break; case 1: pid.setLidOpenDuration(val); config_store_word(lidOpenDuration, val); break; case 2: if (val) pid.resetLidOpenResumeCountdown(); else pid.LidOpenResumeCountdown = 0; break; } }
static void storePidParam(char which, float value) { unsigned char k; switch (which) { case 'b': k = 0; break; case 'p': k = 1; break; case 'i': k = 2; break; case 'd': k = 3; break; default: return; } pid.setPidConstant(k, value); unsigned char ofs = offsetof(__eeprom_data, pidConstants[0]); econfig_write_block(&pid.Pid[k], (void *)(ofs + k * sizeof(float)), sizeof(value)); }
void hmcoreLoop(void) { #ifdef HEATERMETER_SERIAL serial_doWork(); #endif /* HEATERMETER_SERIAL */ #ifdef HEATERMETER_RFM12 if (rfmanager.doWork()) ledmanager.publish(LEDSTIMULUS_RfReceive, LEDACTION_OneShot); #endif /* HEATERMETER_RFM12 */ Menus.doWork(); if (pid.doWork()) newTempsAvail(); tone_doWork(); ledmanager.doWork(); }
static void eepromLoadBaseConfig(unsigned char forceDefault) { // The compiler likes to join eepromLoadBaseConfig and eepromLoadProbeConfig s // this union saves stack space by reusing the same memory area for both structs union { struct __eeprom_data base; struct __eeprom_probe probe; } config; econfig_read_block(&config.base, 0, sizeof(__eeprom_data)); forceDefault = forceDefault || config.base.magic != EEPROM_MAGIC; if (forceDefault != 0) { memcpy_P(&config.base, &DEFAULT_CONFIG[forceDefault - 1], sizeof(__eeprom_data)); econfig_write_block(&config.base, 0, sizeof(__eeprom_data)); } pid.setSetPoint(config.base.setPoint); pid.LidOpenOffset = config.base.lidOpenOffset; pid.setLidOpenDuration(config.base.lidOpenDuration); memcpy(pid.Pid, config.base.pidConstants, sizeof(config.base.pidConstants)); if (config.base.manualMode) pid.setPidOutput(0); setLcdBacklight(config.base.lcdBacklight); #ifdef HEATERMETER_RFM12 memcpy(rfMap, config.base.rfMap, sizeof(rfMap)); #endif pid.setUnits(config.base.pidUnits == 'C' ? 'C' : 'F'); pid.setMinFanSpeed(config.base.minFanSpeed); pid.setMaxFanSpeed(config.base.maxFanSpeed); pid.setOutputFlags(config.base.pidOutputFlags); g_HomeDisplayMode = config.base.homeDisplayMode; pid.setMinServoPos(config.base.minServoPos); pid.setMaxServoPos(config.base.maxServoPos); for (unsigned char led = 0; led<LED_COUNT; ++led) ledmanager.setAssignment(led, config.base.ledConf[led]); }
static void reportFanParams(void) { print_P(PSTR("HMFN" CSV_DELIMITER)); SerialX.print(pid.getFanMinSpeed(), DEC); Serial_csv(); SerialX.print(pid.getFanMaxSpeed(), DEC); Serial_csv(); SerialX.print(pid.getServoMinPos(), DEC); Serial_csv(); SerialX.print(pid.getServoMaxPos(), DEC); Serial_csv(); SerialX.print(pid.getOutputFlags(), DEC); Serial_csv(); SerialX.print(pid.getFanMaxStartupSpeed(), DEC); Serial_csv(); SerialX.print(pid.getFanActiveFloor(), DEC); Serial_csv(); SerialX.print(pid.getServoActiveCeil(), DEC); Serial_nl(); }
void hmcoreSetup(void) { pinModeFast(PIN_WIRELESS_LED, OUTPUT); blinkLed(); #ifdef HEATERMETER_SERIAL Serial.begin(HEATERMETER_SERIAL); // don't use SerialX because we don't want any preamble Serial.write('\n'); reportVersion(); #endif /* HEATERMETER_SERIAL */ // Disable Analog Comparator ACSR = bit(ACD); // Disable Digital Input on ADC pins DIDR0 = bit(ADC5D) | bit(ADC4D) | bit(ADC3D) | bit(ADC2D) | bit(ADC1D) | bit(ADC0D); // And other unused units power_twi_disable(); // Switch the pin mode first to INPUT with internal pullup // to take it to 5V before setting the mode to OUTPUT. // If we reverse this, the pin will go OUTPUT,LOW and reboot. // SoftReset and WiShield are mutually exlusive, but it is HIGH/OUTPUT too digitalWriteFast(PIN_SOFTRESET, HIGH); pinModeFast(PIN_SOFTRESET, OUTPUT); tone4khz_init(); pid.Probes[TEMP_PIT] = &probe0; pid.Probes[TEMP_FOOD1] = &probe1; pid.Probes[TEMP_FOOD2] = &probe2; pid.Probes[TEMP_AMB] = &probe3; eepromLoadConfig(0); pid.init(); lcdDefineChars(); #ifdef HEATERMETER_RFM12 checkInitRfManager(); #endif Menus.setState(ST_HOME_NOPROBES); }
static void storeFanActiveFloor(unsigned char fanActiveFloor) { pid.setFanActiveFloor(fanActiveFloor); config_store_byte(fanActiveFloor, pid.getFanActiveFloor()); }
static void storeMaxFanSpeed(unsigned char maxFanSpeed) { maxFanSpeed = constrain(maxFanSpeed, 0, 100); pid.setMaxFanSpeed(maxFanSpeed); config_store_byte(maxFanSpeed, maxFanSpeed); }
static void storePidUnits(char units) { pid.setUnits(units); if (units == 'C' || units == 'F') config_store_byte(pidUnits, units); }
void updateDisplay(void) { // Updates to the temperature can come at any time, only update // if we're in a state that displays them state_t state = Menus.getState(); if (!isMenuHomeState()) return; char buffer[17]; unsigned char probeIdxLow, probeIdxHigh; // Fixed pit area lcd.setCursor(0, 0); if (state == ST_HOME_ALARM) { toneEnable(true); if (ALARM_ID_TO_IDX(g_AlarmId) == ALARM_IDX_LOW) lcdprint_P(PSTR("** ALARM LOW **"), false); else lcdprint_P(PSTR("** ALARM HIGH **"), false); probeIdxLow = probeIdxHigh = ALARM_ID_TO_PROBE(g_AlarmId); } /* if ST_HOME_ALARM */ else { toneEnable(false); /* Big Number probes overwrite the whole display if it has a temperature */ if (g_HomeDisplayMode >= TEMP_PIT && g_HomeDisplayMode <= TEMP_AMB) { TempProbe *probe = pid.Probes[g_HomeDisplayMode]; if (probe->hasTemperature()) { lcdPrintBigNum(probe->Temperature); return; } } /* Default Pit / Fan Speed first line */ int pitTemp; if (pid.Probes[TEMP_CTRL]->hasTemperature()) pitTemp = pid.Probes[TEMP_CTRL]->Temperature; else pitTemp = 0; if (!pid.getManualOutputMode() && !pid.Probes[TEMP_CTRL]->hasTemperature()) memcpy_P(buffer, LCD_LINE1_UNPLUGGED, sizeof(LCD_LINE1_UNPLUGGED)); else if (pid.LidOpenResumeCountdown > 0) snprintf_P(buffer, sizeof(buffer), PSTR("Pit:%3d"DEGREE"%c Lid%3u"), pitTemp, pid.getUnits(), pid.LidOpenResumeCountdown); else { char c1,c2; if (pid.getManualOutputMode()) { c1 = '^'; // LCD_ARROWUP c2 = '^'; // LCD_ARROWDN } else { c1 = '['; c2 = ']'; } snprintf_P(buffer, sizeof(buffer), PSTR("Pit:%3d"DEGREE"%c %c%3u%%%c"), pitTemp, pid.getUnits(), c1, pid.getPidOutput(), c2); } lcd.print(buffer); // Display mode 0xff is 2-line, which only has space for 1 non-pit value if (g_HomeDisplayMode == 0xff) probeIdxLow = probeIdxHigh = state - ST_HOME_FOOD1 + TEMP_FOOD1; else { // Display mode 0xfe is 4 line home, display 3 other temps there probeIdxLow = TEMP_FOOD1; probeIdxHigh = TEMP_AMB; } } /* if !ST_HOME_ALARM */ // Rotating probe display for (unsigned char probeIndex=probeIdxLow; probeIndex<=probeIdxHigh; ++probeIndex) { if (probeIndex < TEMP_COUNT && pid.Probes[probeIndex]->hasTemperature()) { loadProbeName(probeIndex); snprintf_P(buffer, sizeof(buffer), PSTR("%-12s%3d"DEGREE), editString, (int)pid.Probes[probeIndex]->Temperature); } else { // If probeIndex is outside the range (in the case of ST_HOME_NOPROBES) // just fill the bottom line with spaces memset(buffer, ' ', sizeof(buffer)); buffer[sizeof(buffer) - 1] = '\0'; } lcd.setCursor(0, probeIndex - probeIdxLow + 1); lcd.print(buffer); } }
static void storePidOutputFlags(unsigned char pidOutputFlags) { pid.setOutputFlags(pidOutputFlags); config_store_byte(pidOutputFlags, pidOutputFlags); }
static void storeServoMaxPos(unsigned char servoMaxPos) { pid.setServoMaxPos(servoMaxPos); config_store_byte(servoMaxPos, servoMaxPos); }
static void storeMinServoPos(unsigned char minServoPos) { pid.setMinServoPos(minServoPos); config_store_byte(minServoPos, minServoPos); }
static void checkInitRfManager(void) { if (pid.countOfType(PROBETYPE_RF12) != 0) rfmanager.init(HEATERMETER_RFM12); }
void TempProbe::calcTemp(void) { const float ADCmax = (1 << (10+TEMP_OVERSAMPLE_BITS)) - 1; if (_accumulatedCount != 0) { unsigned int ADCval = _accumulator / _accumulatedCount; _accumulatedCount = 0; // Units 'A' = ADC value if (pid.getUnits() == 'A') { Temperature = ADCval; return; } if (ADCval != 0) // Vout >= MAX is reduced in readTemp() { if (_probeType == PROBETYPE_TC_ANALOG) { float mvScale = Steinhart[3]; // Commented out because there's no "divide by zero" exception so // just allow undefined results to save prog space //if (mvScale == 0.0f) // mvScale = 1.0f; // If scale is <100 it is assumed to be mV/C with a 3.3V reference if (mvScale < 100.0f) mvScale = 3300.0f / mvScale; setTemperatureC(ADCval / ADCmax * mvScale); } else { float R, T; // If you put the fixed resistor on the Vcc side of the thermistor, use the following R = Steinhart[3] / ((ADCmax / (float)ADCval) - 1.0f); // If you put the thermistor on the Vcc side of the fixed resistor use the following //R = Steinhart[3] * ADCmax / (float)Vout - Steinhart[3]; // Units 'R' = resistance, unless this is the pit probe (which should spit out Celsius) if (pid.getUnits() == 'R' && this != pid.Probes[TEMP_PIT]) { Temperature = R; return; }; // Compute degrees K R = log(R); T = 1.0f / ((Steinhart[2] * R * R + Steinhart[1]) * R + Steinhart[0]); setTemperatureC(T - 273.15f); } /* if PROBETYPE_INTERNAL */ } /* if ADCval */ else Temperature = NAN; } /* if accumulatedcount */ if (hasTemperature()) { calcExpMovingAverage(TEMPPROBE_AVG_SMOOTH, &TemperatureAvg, Temperature); Alarms.updateStatus(Temperature); } else Alarms.silenceAll(); }
static void storeServoActiveCeil(unsigned char servoActiveCeil) { pid.setServoActiveCeil(servoActiveCeil); config_store_byte(servoActiveCeil, pid.getServoActiveCeil()); }
static void storeFanMaxStartupSpeed(unsigned char fanMaxStartupSpeed) { pid.setFanMaxStartupSpeed(fanMaxStartupSpeed); config_store_byte(fanMaxStartupSpeed, pid.getFanMaxStartupSpeed()); }
static void storeMaxServoPos(unsigned char maxServoPos) { pid.setMaxServoPos(maxServoPos); config_store_byte(maxServoPos, maxServoPos); }