The snippet I've posted has been kept as simple as possible on purpose because I don't want to force anyone to put a lot of effort into it. My reasoning is that I've made an error in logic. Perhaps easy to spot for someone with experience, where my lack thereof blinds me.
It seems I have achieved the opposite. Or at least as far as you are concerned.
Here is the larger part of the code:
void climateSettings() {
//adjust settings for seasons with multiMap arrays.
//date[] is the 'x-axis' and the xModifier is the 'y-axis' for a graph to calculate all points based on weather data from Costa Rica
float date[13] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; //months. 13 is added so month 12 can have 'days'
float tempertureModifier[13] = {0.96, 0.92, 0.92, 0.92, 0.92, 0.96, 1.12, 1.04, 1, 0.96, 0.92, 0.96, 0.96}; //based on 25 degrees celsius
float humidityModifier[13] = {1.2, 1.15, 1.09, 1.04, 1.01, 1, 0.97, 1, 1.13, 1.2, 1.07, 1.13, 1.2}; //based on 75% RH --- depending on sensor positions you might need to change the base
float today = getDay() * (1/32) + getMonth(); //today is number of the month plus a fraction for each day, filling out the date[] graph axis
humidityVariation = multiMap(today, date, humidityModifier, 13);
temperatureVariation = multiMap(today, date, tempertureModifier, 13);
//adjust settings for daytime
if (getLocalHour() == 13 || getLocalHour() == 14) { //midday
daytimeHumVariation = 0.9;
daytimeTempVariation = 1.1;
} else if (getLocalHour() >= 23 || getLocalHour() <= 7) { //night
daytimeHumVariation = 1.1;
daytimeTempVariation = 0.8;
} else {
daytimeHumVariation = 1;
daytimeTempVariation = 1;
}
//adjust settings for weather outside
if (rainyDay) {
weatherVariation = 1.1; //10% more humidity
} else {
weatherVariation = 1;
}
humModifier = humidityVariation * daytimeHumVariation * weatherVariation;
tempModifier = temperatureVariation * daytimeTempVariation;
//everything times 10 for better resolution
minAirTemp = EEPROM.readByte(60) * 10;
maxAirTemp = EEPROM.readByte(61) * 10;
minHumidity = EEPROM.readByte(62) * 10;
maxHumidity = EEPROM.readByte(63) * 10;
dayWaterTemp = EEPROM.readByte(dayTemperature) * 10;
nightWaterTemp = EEPROM.readByte(nightTemperature) * 10;
desiredAirTemp = (EEPROM.readByte(desiredTemp) * 10) * tempModifier;
desiredHumidity = (EEPROM.readByte(desiredHumi) * 10) * humModifier;
if (desiredAirTemp < (waterTemp - hysteresis / 4)) { //cannot cool lower that outside temperature. For now waterTemp = external airTemp
desiredAirTemp = waterTemp - hysteresis / 4; //compensate for hysteresis upper limit
}
airHigh = desiredAirTemp + (hysteresis / 2);
airLow = desiredAirTemp - (hysteresis / 2);
humidityHigh = desiredHumidity + hysteresis;
humidityLow = desiredHumidity - hysteresis;
/*
outputDebug("Temperature modifier: ");
outputDebugln(tempModifier);
outputDebug("Humidity modifier: ");
outputDebugln(humModifier);
*/
}
void climateControl() {
airTemp = airTempAverage * 10; //all average readings have 1 decimal number. Scale up by 10 to work with ints
humidity = humidityAverage * 10;
waterTemp = waterTempAverage * 10;
if (dayTime) { //day schedule
hold = false;
if (humidity >= maxHumidity) { //disable sprinklers
noRain = true;
} else { noRain = false; }
if (waterTemp - 10 < dayWaterTemp) {
digitalWrite(relay[5], HIGH); //land heat on
}
if (waterTemp >= dayWaterTemp) {
digitalWrite(relay[5], LOW); //land heat off
}
//if all conditions are within half the hysteresis margins, do nothing
if (airTemp <= (desiredAirTemp + hysteresis / 4) && airTemp >= (desiredAirTemp - hysteresis / 4) && humidity <= (desiredHumidity + hysteresis / 2) && humidity >= (desiredHumidity - hysteresis / 2)) {
climateState = CASE0;
}
if (humidityMonitoring) {
if (airTemp > airHigh && humidity > humidityHigh) {
climateState = CASE1;
}
else if (airTemp < airLow && humidity < humidityLow) {
hold = true;
climateState = CASE2;
}
else if (airTemp > airHigh && humidity < humidityLow) {
hold = true; // prevent other singular trigger
climateState = CASE3;
}
else if (airTemp < airLow && humidity > humidityHigh) {
hold = true;
climateState = CASE4;
}
}
if (!hold) {
if (humidity > humidityHigh && humidityMonitoring) {
climateState = CASE5;
}
else if (airTemp > airHigh) {
climateState = CASE6;
}
else if (humidity < humidityLow && humidityMonitoring) {
climateState = CASE7;
}
else if (airTemp < airLow) {
climateState = CASE8;
}
}
}
if (!dayTime) { //night schedule
if (waterTemp - 10 < nightWaterTemp) {
digitalWrite(relay[5], HIGH); //land heat on
}
if (waterTemp >= nightWaterTemp) {
digitalWrite(relay[5], LOW); //land heat off
}
if (humidity >= humidityLow && humidity <= humidityHigh) {
climateState = CASE0;
}
else if (airTemp > airHigh) {
//climateState = CASE9; //pushes humidity too low in summer
}
else if (humidity < humidityLow) {
climateState = CASE10;
}
else if (airTemp < minAirTemp) {
climateState = CASE11;
}
else if (humidity > (970)) {
climateState = CASE12;
}
}
}
void climateChange() {
airTemp = airTempAverage * 10; //all average readings have 1 decimal number
humidity = humidityAverage * 10;
waterTemp = waterTempAverage * 10;
if (desiredAirTemp != oldAirTemp) { //If the program sets a new target temperature, start to transition gradually. Step keeps track of the increments
desiredAirTemp = smoothTransition(oldAirTemp, desiredAirTemp, 5, 10000, step); //In this case 5 intervals of 1 minute
} else {
step = 0; //Reset step when target is reached and update oldAirTemp
oldAirTemp = desiredAirTemp;
}
/*
if (desiredHumidity != oldHumidity) {
desiredHumidity = smoothTransition(oldHumidity, desiredHumidity, 5, 10000, humStep, 0);
} else {
humStep = 0;
oldHumidity = desiredHumidity;
}
*/
if (climateState != previousClimateState) {
switch (climateState) {
case CASE0: //default do nothing
digitalWrite(relay[1], LOW); //fans off
pwmVal1 = 0;
analogWrite(pwmPin_1, pwmVal1);
digitalWrite(relay[6], LOW); //window heating off --- ??? maybe not?
if (humidityMonitoring) {
digitalWrite(relay[4], LOW); //fogger off
}
outputDebugln("CASE 0");
break;
case CASE1: //decrease temperature and decrease humidity
digitalWrite(relay[1], HIGH); //fans on
digitalWrite(relay[6], LOW); //window heating off
digitalWrite(relay[4], LOW); //fogger off
outputDebugln("CASE 1 airTemp high & humidity high --- fans on max");
startup = 0;
startDelay = millis();
break;
case CASE2: //increase temperature and increase humidity
digitalWrite(relay[1], LOW); //fans off
pwmVal1 = 0;
analogWrite(pwmPin_1, pwmVal1);
digitalWrite(relay[4], HIGH); //fogger on
digitalWrite(relay[6], HIGH); //window heating on
outputDebugln("CASE 2 airTemp low & humidity low --- heating on & fogger on");
break;
case CASE3: //decrease temperature and increase humidity
digitalWrite(relay[6], LOW); //window heating off
//running sequence below
outputDebugln("CASE 3 airTemp high & humidity low --- fans off & sprinklers on");
break;
case CASE4: //increase temperature and decrease humidity
digitalWrite(relay[6], HIGH); //heating on
digitalWrite(relay[1], HIGH); //fans low
pwmVal1 = 60; //low state --- needs testing --- 23% is lowest setting for current fans to start moving
analogWrite(pwmPin_1, pwmVal1);
outputDebugln("CASE 4 airTemp low && humidity high --- heating on & fans low");
break;
//single states
case CASE5: //decrease humidity
if (humidityMonitoring) {
digitalWrite(relay[4], LOW); //fogger off
digitalWrite(relay[1], HIGH);//fans on
startup = 0;
startDelay = millis();
}
outputDebugln("CASE 5 humidity high --- fans on");
break;
case CASE6: //decrease temperature
digitalWrite(relay[6], LOW); //window heating off
digitalWrite(relay[1], HIGH); //fans on
outputDebugln("CASE 6 airTemp high --- fans on");
startup = 0;
startDelay = millis();
break;
case CASE7: //increase humidity
digitalWrite(relay[1], LOW); //fans off
pwmVal1 = 0;
analogWrite(pwmPin_1, pwmVal1);
digitalWrite(relay[4], HIGH); //fogger on
outputDebugln("CASE 7 humidity low --- fogger on");
break;
case CASE8: //increase temperature
digitalWrite(relay[1], LOW); //fans off
pwmVal1 = 0;
analogWrite(pwmPin_1, pwmVal1);
digitalWrite(relay[6], HIGH); //window heat on
outputDebugln("CASE 8 airTemp low --- heat on");
break;
//night
case CASE9: //decrease temperature
digitalWrite(relay[6], LOW); //window heating off
digitalWrite(relay[1], HIGH); //fans on
outputDebugln("CASE 9 airTemp high --- fans on");
startup = 0;
startDelay = millis();
break;
case CASE10: //increase humidity
digitalWrite(relay[1], LOW); //fans off
pwmVal1 = 0;
analogWrite(pwmPin_1, pwmVal1);
digitalWrite(relay[4], HIGH); //fogger on
outputDebugln("CASE 10 humidity low --- fogger on");
break;
case CASE11: //increase temperature
digitalWrite(relay[1], LOW); //fans off
pwmVal1 = 0;
analogWrite(pwmPin_1, pwmVal1);
digitalWrite(relay[6], HIGH); //window heat on
outputDebugln("CASE 11 airTemp low --- heat on");
break;
case CASE12: //decrease humidity
digitalWrite(relay[4], LOW); //fogger off
digitalWrite(relay[1], HIGH); //fans on
outputDebugln("CASE 12 humidity max --- fans on");
startup = 0;
startDelay = millis();
break;
}
previousClimateState = climateState;
}
if (climateState == CASE1) { //decrease temperature and decrease humidity
fanSpeedController(2);
}
else if (climateState == CASE3) { //decrease temperature and increase humidity
//turn on sprinklers and wait a bit for endothermic evaporation, then turn on fans again. This event is unlikely to happen.
static byte state = 0; //at start go to state 0 with ++
if (millis() - intervalMark >= sequenceInterval[state]) {
// go to the next state
state++;
state = state % 5;
outputDebug(F("state = "));
outputDebugln(state);
// act according to state
switch (state) {
case 0: //fans off and wait 2 seconds
digitalWrite(relay[1], LOW);
pwmVal1 = 0;
analogWrite(pwmPin_1, pwmVal1);
break;
case 1: //sprinklers for 3 seconds
digitalWrite(relay[2], HIGH);
break;
case 2: //sprinklers off and wait another 30 seconds
digitalWrite(relay[2], LOW);
break;
case 3: //fans back on for 5 minutes
digitalWrite(relay[1], HIGH);
if (airTemp > maxAirTemp) { airTemp = maxAirTemp; }
mappedTemp = map(airTemp, desiredAirTemp, (desiredAirTemp + 50), 0, 100);
pwmVal1 = multiMap<float>(mappedTemp, input, output, 11);
analogWrite(pwmPin_1, pwmVal1);
break;
case 4: //same as 3 because of max lenght difficulty
if (airTemp > maxAirTemp) { airTemp = maxAirTemp; }
mappedTemp = map(airTemp, desiredAirTemp, (desiredAirTemp + 50), 0, 100);
pwmVal1 = multiMap<float>(mappedTemp, input, output, 11);
analogWrite(pwmPin_1, pwmVal1);
break;
}
intervalMark = millis();
}
}
else if (climateState == CASE5) { //decrease humidity
fanSpeedController(1);
}
else if (climateState == CASE6) { //decrease temperature
fanSpeedController(2);
}
else if (climateState == CASE8) { //increase temperature
//digitalWrite(relay[1], LOW); //fans_2 to be made icm with front window ventilation
//mappedTemp = map(airTemp, desiredAirTemp, maxAirTemp, 0, 100);
//pwmVal2 = multiMap<float>(mappedTemp, input, output, 11);
}
else if (climateState == CASE9) { //decrease temperature
fanSpeedController(2);
}
else if (climateState == CASE12) { //decrease humidity
fanSpeedController(1);
}
pwm1Speed = map(pwmVal1, 0, 255, 0, 100);
}
void fanSpeedController(int type) {
float data, targetValue, maxValue, rangeValue;
if (type == 1) {
data = humidity;
maxValue = maxHumidity;
targetValue = desiredHumidity;
rangeValue = targetValue + (2 * hysteresis);
}
if (type == 2) {
data = airTemp;
maxValue = maxAirTemp;
targetValue = desiredAirTemp;
rangeValue = targetValue + hysteresis;
}
if (data > maxValue) { data = maxValue; }
switch (startup) { //set PWM to minimum value to start the fan moving
case 0:
pwmVal1 = 60;
analogWrite(pwmPin_1, pwmVal1);
if (millis() - startDelay >= 1000) { startup++; }
break;
case 1: //switch to automatic variable control
mappedTemp = map(data, targetValue, rangeValue, 0, 100);
pwmVal1 = multiMap<float>(mappedTemp, input, output, 11);
analogWrite(pwmPin_1, pwmVal1);
break;
}
}
float smoothTransition(float start, float end, uint8_t intervals, unsigned long intervalTime, uint8_t step) {
float delta = (end - start) / intervals;
if (millis() - lastUpdateTime >= intervalTime && step < intervals) {
start += delta;
step++;
}
lastUpdateTime = millis();
outputDebug("Transitioning with new value: ");
outputDebugln(start);
return start;
}