amespace Ventilation
{
uint8_t EEMEM eeprom_enabled = Default_VentilationEnabled;
uint8_t EEMEM eeprom_powerPercent = Default_VentilationPowerPercent;
uint8_t EEMEM eeprom_phaseControlEnabled = Default_VetilationPhaseControlEnabled;
uint16_t EEMEM eeprom_standbyFeedingVentilationDuration = Default_StandbyFeedingVentilationDuration;
volatile bool zeroCrossFlag = false;
volatile unsigned long zeroCrossTimestamp = 0; // zero cross detection timestamp in µs.
bool isEnabled;
bool isActive;
bool isPhaseControlEnabled;
uint16_t standbyFeedingVentilationDuration;
bool isPowerPercentPrintRequired;
bool isPhaseControlStatusPrintRequired;
bool isStandbyFeedingVentilationDurationPrintRequired;
uint16_t startupTimer;
uint16_t standbyFeedingVentilationTimer;
uint8_t powerPercent;
uint16_t newValue;
uint8_t targetPowerPercent;
uint8_t currentPowerPercent;
void init()
{
if (appState.isAppConfigValid)
{
if defined(MODEL_A)
isEnabled = eeprom_read_byte(&eeprom_enabled);
standbyFeedingVentilationDuration = eeprom_read_word(&eeprom_standbyFeedingVentilationDuration);
elif defined(MODEL_M)
isEnabled = true;
endif
isPhaseControlEnabled = eeprom_read_byte(&eeprom_phaseControlEnabled);
powerPercent = eeprom_read_byte(&eeprom_powerPercent);
}
else
{
if defined(MODEL_A)
eeprom_update_byte(&eeprom_enabled, Default_VentilationEnabled);
isEnabled = Default_VentilationEnabled;
eeprom_update_word(&eeprom_standbyFeedingVentilationDuration, Default_StandbyFeedingVentilationDuration);
standbyFeedingVentilationDuration = Default_StandbyFeedingVentilationDuration;
elif defined(MODEL_M)
isEnabled = true;
endif
eeprom_update_byte(&eeprom_phaseControlEnabled, Default_VetilationPhaseControlEnabled);
isPhaseControlEnabled = Default_VetilationPhaseControlEnabled;
eeprom_update_byte(&eeprom_powerPercent, Default_VentilationPowerPercent);
powerPercent = Default_VentilationPowerPercent;
}
if (powerPercent > 201 || !isPhaseControlEnabled)
powerPercent = Default_VentilationPowerPercent;
else if (powerPercent < UserDefinedMinPowerPercent)
powerPercent = UserDefinedMinPowerPercent;
isActive = false;
startupTimer = VentilationPhaseControlDelay;
targetPowerPercent = 0;
currentPowerPercent = 0;
if defined(MODEL_A)
DDRA |= _BV(DDA1); // set PA1 (Ventilation Motor Control) as output.
elif defined(MODEL_M)
DDRC |= _BV(DDC5); // set PC5 (Ventilation Motor Control) as output.
endif
DDRE &= ~_BV(DDE6); // set PE6 (Zero Cross Interrupts) as input.
EICRB |= _BV(ISC61) | _BV(ISC60); // set INT6 Sense Control for rising edge detection.
// EICRB |= _BV(ISC61); // set INT6 Sense Control for rising edge detection.
// EICRB &= ~_BV(ISC60); // set INT6 Sense Control for rising edge detection.
EIMSK |= _BV(INT6); // enable External Interrupt Request 6.
TCCR1A = 0; // clear TCCR1A (Timer/Counter Control Register 1A).
TCCR1B = 0; // clear TCCR1B (Timer/Counter Control Register 1B).
TCCR1C = 0; // clear TCCR1C (Timer/Counter Control Register 1C).
TCCR1B |= _BV(CS10); // set prescaler to 1 cycle (Clock Select = 001).
TCCR1B |= _BV(WGM12); // set WGM (Waveform Generation Mode) to CTC (Clear Timer on Compare match).
OCR1A = 1999; // set OCR1A (Output Compare Register 1A) to 1999 cycles.
TCNT1 = 0; // reset TCNT1 (Timer Counter 1).
TIMSK |= _BV(OCIE1A); // set OCIE1A (Output Compare A Match Interrupt Enable) for interrupting on OCR1A = TCNT1.
}
void toggleEnabled()
{
isEnabled = !isEnabled;
eeprom_update_byte(&eeprom_enabled, isEnabled);
}
void increasePowerPercent()
{
if (powerPercent < 100)
{
if (powerPercent <= 100 - UserDefinedPowerPercentStep)
powerPercent += UserDefinedPowerPercentStep;
else
powerPercent = 100;
}
else
{
powerPercent = 201;
}
isPowerPercentPrintRequired = true;
eeprom_update_byte(&eeprom_powerPercent, powerPercent);
}
void decreasePowerPercent()
{
if (powerPercent > 100)
powerPercent = 100;
else if (powerPercent <= UserDefinedMinPowerPercent + UserDefinedPowerPercentStep)
powerPercent = UserDefinedMinPowerPercent;
else
powerPercent -= UserDefinedPowerPercentStep;
isPowerPercentPrintRequired = true;
eeprom_update_byte(&eeprom_powerPercent, powerPercent);
}
void print()
{
if (isPowerPercentPrintRequired || UI::isPrintRequired)
{
if (appState.editMode == EditMode::VentilationPowerPercent && (appState.command != Command::Save && appState.command != Command::Discard))
{
if (Blink::command == BlinkCommand::Hide)
{
PRINT(VentilationPowerPercentPos, " ")
}
else
{
if (newValue > 100)
PRINT(VentilationPowerPercentPos, UI::language == 0 ? "OTO " : "AUTO")
else if (newValue == 100)
PRINT(VentilationPowerPercentPos, UI::language == 0 ? "%100" : "100%")
else
PRINTF(VentilationPowerPercentPos, UI::language == 0 ? "%%%02d " : "%02d%% ", newValue)
}
}
else
{
if (powerPercent > 100)
PRINT(VentilationPowerPercentPos, UI::language == 0 ? "OTO " : "AUTO")
else if (powerPercent == 100)
PRINT(VentilationPowerPercentPos, UI::language == 0 ? "%100" : "100%")
else
PRINTF(VentilationPowerPercentPos, UI::language == 0 ? "%%%02d " : "%02d%% ", powerPercent)
}
isPowerPercentPrintRequired = false;
}
if (isPhaseControlStatusPrintRequired || UI::isPrintRequired)
{
if (appState.editMode == EditMode::VentilationPhaseControlMode && (appState.command != Command::Save && appState.command != Command::Discard))
{
if (Blink::command == BlinkCommand::Hide)
{
PRINT(VentilationPhaseControlPos, " ")
}
else
{
if (newValue)
PRINT(VentilationPhaseControlPos, UI::language == 0 ? " FAZ KONTROL " : "PHASE CONTROL")
else
PRINTF(VentilationPhaseControlPos, UI::language == 0 ? " AC / KAPAT " : "TOGGLE ON/OFF")
}
}
else
{
if (isPhaseControlEnabled)
PRINT(VentilationPhaseControlPos, UI::language == 0 ? " FAZ KONTROL " : "PHASE CONTROL")
else
PRINTF(VentilationPhaseControlPos, UI::language == 0 ? " AC / KAPAT " : "TOGGLE ON/OFF")
}
isPhaseControlStatusPrintRequired = false;
}
if (isStandbyFeedingVentilationDurationPrintRequired || UI::isPrintRequired)
{
if (appState.editMode == EditMode::StandbyFeedingVentilationDuration && (appState.command != Command::Save && appState.command != Command::Discard))
{
if (Blink::command == BlinkCommand::Hide)
PRINT(StandbyFeedingVentilationDurationPos, " ")
else
PRINTF(StandbyFeedingVentilationDurationPos, "%02d:%02d", newValue / 60, newValue % 60)
}
else
PRINTF(StandbyFeedingVentilationDurationPos, "%02d:%02d", standbyFeedingVentilationDuration / 60, standbyFeedingVentilationDuration % 60)
isStandbyFeedingVentilationDurationPrintRequired = false;
}
}
void editModeControl()
{
switch (appState.editMode)
{
case EditMode::VentilationPowerPercent:
if (appState.command == Command::Edit)
{
if (powerPercent > 100)
newValue = 201;
else
newValue = powerPercent;
}
else if (appState.command == Command::Save)
{
powerPercent = newValue;
eeprom_update_byte(&eeprom_powerPercent, newValue);
}
if (Input::getButton(Buttons::Up))
{
if (newValue < 100)
{
if (newValue <= 100 - UserDefinedPowerPercentStep)
newValue += UserDefinedPowerPercentStep;
else
newValue = 100;
}
else
{
newValue = 201;
}
}
else if (Input::getButton(Buttons::Down))
{
if (newValue > 100)
newValue = 100;
else if (newValue <= UserDefinedMinPowerPercent + UserDefinedPowerPercentStep)
newValue = UserDefinedMinPowerPercent;
else
newValue -= UserDefinedPowerPercentStep;
}
if (ON_BLINK || ON_EDIT_COMMANDS || Input::getButton(Buttons::Up) || Input::getButton(Buttons::Down))
isPowerPercentPrintRequired = true;
break;
case EditMode::VentilationPhaseControlMode:
if (appState.command == Command::Edit)
{
newValue = isPhaseControlEnabled;
}
else if (appState.command == Command::Save)
{
isPhaseControlEnabled = newValue;
eeprom_update_byte(&eeprom_phaseControlEnabled, newValue);
}
if (Input::getButtonOnce(Buttons::Up) || Input::getButtonOnce(Buttons::Down))
{
newValue = !newValue;
isPhaseControlStatusPrintRequired = true;
}
if (ON_BLINK || ON_EDIT_COMMANDS)
isPhaseControlStatusPrintRequired = true;
break;
case EditMode::StandbyFeedingVentilationDuration:
if (appState.command == Command::Edit)
{
newValue = standbyFeedingVentilationDuration;
}
else if (appState.command == Command::Save)
{
standbyFeedingVentilationDuration = newValue;
eeprom_update_word(&eeprom_standbyFeedingVentilationDuration, newValue);
}
if (Input::getButton(Buttons::Up))
{
if (newValue < MaxStandbyFeedingVentilationDuration)
newValue++;
}
else if (Input::getButton(Buttons::Down))
{
if (newValue > MinStandbyFeedingVentilationDuration)
newValue--;
}
if (ON_BLINK || ON_EDIT_COMMANDS || Input::getButton(Buttons::Up) || Input::getButton(Buttons::Down))
isStandbyFeedingVentilationDurationPrintRequired = true;
break;
default:
break;
}
}
void updatePower()
{
const uint8_t updatePeriod = 100; // in ms
static uint8_t updateTimer = updatePeriod; // in ms
DECREASE_TIMER_MS(updateTimer)
if (updateTimer)
return;
updateTimer = updatePeriod;
if (currentPowerPercent > targetPowerPercent)
currentPowerPercent = targetPowerPercent;
else if (currentPowerPercent < targetPowerPercent)
currentPowerPercent++;
}
void update()
{
if (powerPercent > 100 && isPhaseControlEnabled)
{
int16_t tempDiff = Heating::desiredWaterTemp - Sensor::waterTemp;
if (tempDiff < 1)
powerPercent = 101;
else if (tempDiff > VentilationPowerControlTempRange)
powerPercent = 201;
else
{
powerPercent = tempDiff * (100.0F / (float)VentilationPowerControlTempRange) + 101;
if (powerPercent > 201)
powerPercent = 201;
}
}
if defined(MODEL_A)
if (isEnabled)
{
if (Heating::isActive)
{
standbyFeedingVentilationTimer = 0;
isActive = true;
}
else if (Core::isEnabled)
{
if (standbyFeedingVentilationTimer == 0 && Feeding::isActive && !Feeding::isManualFeedingActive && Feeding::isAutoFeedingEnabled)
{
isActive = true;
standbyFeedingVentilationTimer = standbyFeedingVentilationDuration;
}
else
{
DECREASE_TIMER_S(standbyFeedingVentilationTimer)
if (standbyFeedingVentilationTimer == 0)
isActive = false;
}
}
else
isActive = false;
}
else
isActive = false;
isActive = isActive && !BackfireProtection::isActive;
elif defined(MODEL_M)
isActive = isEnabled && Heating::isActive;
endif
if (isActive)
DECREASE_TIMER_MS(startupTimer)
else
{
startupTimer = VentilationPhaseControlDelay;
standbyFeedingVentilationTimer = 0;
}
if (startupTimer || standbyFeedingVentilationTimer)
targetPowerPercent = 100;
else
{
targetPowerPercent = powerPercent > 100 ? powerPercent - 101 : powerPercent;
if (targetPowerPercent < VentilationMinPowerPercent)
targetPowerPercent = VentilationMinPowerPercent;
}
if (!isActive)
targetPowerPercent = 0;
updatePower();
editModeControl();
print();
}
ISR(INT6_vect)
{
if (!zeroCrossFlag)
{
if (isActive)
{
if (currentPowerPercent == 100)
{
if defined(MODEL_A)
PORTA |= _BV(PA1); // set PA1 (Ventilation Motor Control) to high.
elif defined(MODEL_M)
PORTC |= _BV(PC5); // set PC5 (Ventilation Motor Control) to high.
endif
}
}
else
{
if defined(MODEL_A)
PORTA &= ~_BV(PA1); // set PA1 (Ventilation Motor Control) to low.
elif defined(MODEL_M)
PORTC &= ~_BV(PC5); // set PC5 (Ventilation Motor Control) to low.
endif
}
zeroCrossTimestamp = micros();
zeroCrossFlag = true;
}
}
ISR(TIMER1_COMPA_vect)
{
if (isActive && zeroCrossFlag && currentPowerPercent < 100)
{
unsigned long now = micros();
if (now - zeroCrossTimestamp < (100U - currentPowerPercent) * VentilationMaxPhaseDelay * 10 + VentilationInterruptDelay)
return;
if defined(MODEL_A)
PORTA |= _BV(PA1); // set PA1 (Ventilation Motor Control) to high.
elif defined(MODEL_M)
PORTC |= _BV(PC5); // set PC5 (Ventilation Motor Control) to high.
endif
_delay_us(5);
if defined(MODEL_A)
PORTA &= ~_BV(PA1); // set PA1 (Ventilation Motor Control) to low.
elif defined(MODEL_M)
PORTC &= ~_BV(PC5); // set PC5 (Ventilation Motor Control) to low.
endif
}
zeroCrossFlag = false;
}
}