Skip to content

Commit a94a4a5

Browse files
committed
Add ramp PWM backend for dewpoint and target temp
1 parent b62670f commit a94a4a5

3 files changed

Lines changed: 59 additions & 25 deletions

File tree

src/heater.cpp

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,17 @@ struct APB::Heater::Private {
2222
std::optional<float> temperature;
2323
float targetTemperature;
2424
float dewpointOffset;
25+
float rampOffset = 0;
2526

2627
Task loopTask;
2728
char log_scope[20];
2829
uint8_t index;
2930
Heater::GetTargetTemperature getTargetTemperature;
3031

31-
void setup();
32+
void privateSetup();
3233
void loop();
3334
void readTemperature();
34-
void setDuty(float pwm);
35+
void writePinDuty(float pwm);
3536
float getDuty() const;
3637

3738
#ifdef APB_HEATER_TEMPERATURE_SENSOR_THERMISTOR
@@ -69,7 +70,7 @@ void APB::Heater::setup(uint8_t index, Scheduler &scheduler) {
6970
d->index = index;
7071
sprintf(d->log_scope, "Heater[%d] -", index);
7172

72-
d->setup();
73+
d->privateSetup();
7374

7475
d->loopTask.set(APB_HEATER_UPDATE_INTERVAL_SECONDS * 1000, TASK_FOREVER, std::bind(&Heater::Private::loop, d));
7576
scheduler.addTask(d->loopTask);
@@ -121,19 +122,20 @@ uint8_t APB::Heater::index() const {
121122
}
122123

123124

124-
bool APB::Heater::setTemperature(float targetTemperature, float maxDuty) {
125+
bool APB::Heater::setTemperature(float targetTemperature, float maxDuty, float rampOffset) {
125126
if(!this->temperature().has_value()) {
126127
Log.warningln(TEMPERATURE_NOT_FOUND_WARNING_LOG, d->log_scope);
127128
return false;
128129
}
129130
d->targetTemperature = targetTemperature;
130131
d->pwm = maxDuty;
131132
d->mode = Heater::Mode::target_temperature;
133+
d->rampOffset = rampOffset >= 0 ? rampOffset : 0;
132134
d->loop();
133135
return true;
134136
}
135137

136-
bool APB::Heater::setDewpoint(float offset, float maxDuty) {
138+
bool APB::Heater::setDewpoint(float offset, float maxDuty, float rampOffset) {
137139
if(!this->temperature().has_value()) {
138140
Log.warningln(TEMPERATURE_NOT_FOUND_WARNING_LOG, d->log_scope);
139141
return false;
@@ -143,12 +145,14 @@ bool APB::Heater::setDewpoint(float offset, float maxDuty) {
143145
return false;
144146
}
145147
d->dewpointOffset = offset;
148+
d->rampOffset = rampOffset >= 0 ? rampOffset : 0;
146149
d->pwm = maxDuty;
147150
d->mode = Heater::Mode::dewpoint;
148151
d->loop();
149152
return true;
150153
}
151154

155+
152156
std::optional<float> APB::Heater::targetTemperature() const {
153157
if(d->mode != Heater::Mode::target_temperature) {
154158
return {};
@@ -163,6 +167,13 @@ std::optional<float> APB::Heater::dewpointOffset() const {
163167
return {d->dewpointOffset};
164168
}
165169

170+
std::optional<float> APB::Heater::rampOffset() const {
171+
if(d->mode != Mode::dewpoint && d->mode != Mode::target_temperature) {
172+
return {};
173+
}
174+
return {d->rampOffset};
175+
}
176+
166177
std::optional<float> APB::Heater::temperature() const {
167178
return d->temperature;
168179
}
@@ -187,11 +198,11 @@ void APB::Heater::Private::loop()
187198
}
188199

189200
if(mode == Heater::Mode::fixed) {
190-
setDuty(pwm);
201+
writePinDuty(pwm);
191202
return;
192203
}
193204
if(mode == Heater::Mode::off) {
194-
setDuty(0);
205+
writePinDuty(0);
195206
return;
196207
}
197208
// From nmow on we require a temperature sensor on the heater
@@ -201,40 +212,50 @@ void APB::Heater::Private::loop()
201212
return;
202213
}
203214

204-
float targetTemperature;
215+
float dynamicTargetTemperature;
205216
if(mode == Heater::Mode::target_temperature) {
206-
targetTemperature = this->targetTemperature;
217+
dynamicTargetTemperature = this->targetTemperature;
207218
}
208219
if(mode == Heater::Mode::dewpoint) {
209220
if(!Ambient::Instance.reading()) {
210221
Log.warningln("%s Unable to set target temperature, ambient sensor not found.", log_scope);
211222
q->setDuty(0);
212223
return;
213224
}
214-
targetTemperature = dewpointOffset + Ambient::Instance.reading()->dewpoint();
225+
dynamicTargetTemperature = dewpointOffset + Ambient::Instance.reading()->dewpoint();
215226
}
216227

217228
float currentTemperature = temperature.value();
218-
Log.traceln("%s Got target temperature=`%F`", log_scope, targetTemperature);
229+
Log.traceln("%s Got target temperature=`%F`", log_scope, dynamicTargetTemperature);
219230
Log.traceln("%s current temperature=`%F`", log_scope, currentTemperature);
220-
if(currentTemperature < targetTemperature) {
221-
Log.infoln("%s - temperature `%F` lower than target temperature `%F`, setting PWM to `%F`", log_scope, currentTemperature, targetTemperature, pwm);
222-
setDuty(pwm);
231+
if(currentTemperature < dynamicTargetTemperature) {
232+
float rampFactor = rampOffset > 0 ? (dynamicTargetTemperature - currentTemperature)/rampOffset : 1;
233+
float targetPWM = std::max(0.f, std::min(1.f, rampFactor));
234+
Log.infoln("%s - temperature `%F` lower than target temperature `%F`, ramp=`%F` and max PWM is `%F`, ramp factor=`%F`, setting PWM to `%F`",
235+
log_scope,
236+
currentTemperature,
237+
dynamicTargetTemperature,
238+
rampOffset,
239+
pwm,
240+
rampFactor,
241+
targetPWM
242+
);
243+
writePinDuty(targetPWM);
223244
} else {
224-
Log.infoln("%s - temperature `%F` reached target temperature `%F`, setting PWM to 0", log_scope, currentTemperature, targetTemperature);
225-
setDuty(0);
245+
Log.infoln("%s - temperature `%F` reached target temperature `%F`, setting PWM to 0", log_scope, currentTemperature, dynamicTargetTemperature);
246+
writePinDuty(0);
226247
}
227248
}
228249

229250
#ifdef APB_HEATER_TEMPERATURE_SENSOR_THERMISTOR
230251
#define ANALOG_READ_RES 12
231252
#define MAX_PWM 255.0
232-
void APB::Heater::Private::setup() {
253+
void APB::Heater::Private::privateSetup() {
233254
static const float analogReadMax = std::pow(2, ANALOG_READ_RES) - 1;
234255
pinout = &heaters_pinout[index];
235256
Log.traceln("%s Configuring PWM thermistor heater: Thermistor pin=%d, PWM pin=%d, analogReadMax=%F", log_scope, pinout->thermistor, pinout->pwm, analogReadMax);
236257
analogReadResolution(ANALOG_READ_RES);
237-
setDuty(0);
258+
writePinDuty(0);
238259
if(pinout->thermistor != -1) {
239260
smoothThermistor = std::make_unique<SmoothThermistor>(
240261
pinout->thermistor,
@@ -263,7 +284,7 @@ float APB::Heater::Private::getDuty() const {
263284
return this->pwmValue/MAX_PWM;
264285
}
265286

266-
void APB::Heater::Private::setDuty(float pwm) {
287+
void APB::Heater::Private::writePinDuty(float pwm) {
267288
int16_t newPWMValue = MAX_PWM * pwm;
268289
if(newPWMValue != pwmValue) {
269290
pwmValue = newPWMValue;

src/heater.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,12 @@ class Heater {
2929

3030
float duty() const;
3131
void setDuty(float duty);
32-
bool setTemperature(float targetTemperature, float maxDuty=1);
33-
bool setDewpoint(float offset, float maxDuty=1);
32+
bool setTemperature(float targetTemperature, float maxDuty=1, float rampOffset=0);
33+
bool setDewpoint(float offset, float maxDuty=1, float rampOffset=0);
3434
std::optional<float> temperature() const;
3535
std::optional<float> targetTemperature() const;
3636
std::optional<float> dewpointOffset() const;
37+
std::optional<float> rampOffset() const;
3738
bool active() const;
3839

3940
Mode mode() const;

src/webserver.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -296,17 +296,29 @@ void APB::WebServer::onPostSetHeater(AsyncWebServerRequest *request, JsonVariant
296296
heater.setDuty(json["duty"]);
297297
}
298298
if(mode == Heater::Mode::dewpoint) {
299-
if(validation.range("dewpoint_offset", {-30}, {30}).required<float>("dewpoint_offset").invalid()) return;
299+
if(validation
300+
.range("dewpoint_offset", {-30}, {30})
301+
.required<float>("dewpoint_offset")
302+
.range("ramp_offset", 0, 20)
303+
.invalid()
304+
) return;
300305
float dewpointOffset = json["dewpoint_offset"];
301-
if(!heater.setDewpoint(dewpointOffset, duty)) {
306+
float rampOffset = json["ramp_offset"].is<float>() ? json["ramp_offset"] : 0;
307+
if(!heater.setDewpoint(dewpointOffset, duty, rampOffset)) {
302308
JsonResponse::error(500, dewpointTemperatureErrorMessage, request);
303309
return;
304310
}
305311
}
306312
if(mode == Heater::Mode::target_temperature) {
307-
if(validation.range("target_temperature", {-50}, {50}).required<float>("target_temperature").invalid()) return;
313+
if(validation
314+
.range("target_temperature", {-50}, {50})
315+
.required<float>("target_temperature")
316+
.range("ramp_offset", 0, 20)
317+
.invalid()
318+
) return;
308319
float targetTemperature = json["target_temperature"];
309-
if(!heater.setTemperature(targetTemperature, duty)) {
320+
float rampOffset = json["ramp_offset"].is<float>() ? json["ramp_offset"] : 0;
321+
if(!heater.setTemperature(targetTemperature, duty, rampOffset)) {
310322
JsonResponse::error(500, temperatureErrorMessage, request);
311323
return;
312324
}

0 commit comments

Comments
 (0)