Skip to content

Commit c2f264f

Browse files
committed
Full support (be and fe) for ramping duty
1 parent 7f98300 commit c2f264f

6 files changed

Lines changed: 52 additions & 7 deletions

File tree

src/heater.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ APB::Heaters::Array &APB::Heaters::Instance = *new APB::Heaters::Array();
1818
struct APB::Heater::Private {
1919
APB::Heater *q;
2020
Heater::Mode mode{Heater::Mode::off};
21-
float pwm;
21+
float maxDuty;
2222
std::optional<float> temperature;
2323
float targetTemperature;
2424
float dewpointOffset;
@@ -99,8 +99,12 @@ const String APB::Heater::modeAsString() const
9999
return Private::modesToString[mode()];
100100
}
101101

102+
float APB::Heater::maxDuty() const {
103+
return d->maxDuty;
104+
}
105+
102106
float APB::Heater::duty() const {
103-
return d->pwm;
107+
return d->getDuty();
104108
}
105109

106110
bool APB::Heater::active() const {
@@ -109,7 +113,7 @@ bool APB::Heater::active() const {
109113

110114
void APB::Heater::setDuty(float duty) {
111115
if(duty > 0) {
112-
d->pwm = duty;
116+
d->maxDuty = duty;
113117
d->mode = Heater::Mode::fixed;
114118
} else {
115119
d->mode = Heater::Mode::off;
@@ -128,7 +132,7 @@ bool APB::Heater::setTemperature(float targetTemperature, float maxDuty, float r
128132
return false;
129133
}
130134
d->targetTemperature = targetTemperature;
131-
d->pwm = maxDuty;
135+
d->maxDuty = maxDuty;
132136
d->mode = Heater::Mode::target_temperature;
133137
d->rampOffset = rampOffset >= 0 ? rampOffset : 0;
134138
d->loop();
@@ -146,7 +150,7 @@ bool APB::Heater::setDewpoint(float offset, float maxDuty, float rampOffset) {
146150
}
147151
d->dewpointOffset = offset;
148152
d->rampOffset = rampOffset >= 0 ? rampOffset : 0;
149-
d->pwm = maxDuty;
153+
d->maxDuty = maxDuty;
150154
d->mode = Heater::Mode::dewpoint;
151155
d->loop();
152156
return true;
@@ -198,7 +202,7 @@ void APB::Heater::Private::loop()
198202
}
199203

200204
if(mode == Heater::Mode::fixed) {
201-
writePinDuty(pwm);
205+
writePinDuty(maxDuty);
202206
return;
203207
}
204208
if(mode == Heater::Mode::off) {
@@ -236,7 +240,7 @@ void APB::Heater::Private::loop()
236240
currentTemperature,
237241
dynamicTargetTemperature,
238242
rampOffset,
239-
pwm,
243+
maxDuty,
240244
rampFactor,
241245
targetPWM
242246
);

src/heater.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class Heater {
2727
enum Mode { off, fixed, target_temperature, dewpoint };
2828
void setup(uint8_t index, Scheduler &scheduler);
2929

30+
float maxDuty() const;
3031
float duty() const;
3132
void setDuty(float duty);
3233
bool setTemperature(float targetTemperature, float maxDuty=1, float rampOffset=0);

src/webserver.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ void APB::WebServer::onGetHeaters(AsyncWebServerRequest *request) {
153153
void APB::WebServer::populateHeatersStatus(JsonArray heatersStatus) {
154154
std::for_each(Heaters::Instance.begin(), Heaters::Instance.end(), [heatersStatus](Heater &heater) {
155155
heatersStatus[heater.index()]["mode"] = heater.modeAsString(),
156+
heatersStatus[heater.index()]["maxDuty"] = heater.maxDuty();
156157
heatersStatus[heater.index()]["duty"] = heater.duty();
157158
heatersStatus[heater.index()]["active"] = heater.active();
158159
heatersStatus[heater.index()]["has_temperature"] = heater.temperature().has_value();
@@ -205,12 +206,19 @@ void APB::WebServer::onGetMetrics(AsyncWebServerRequest *request) {
205206
.gauge("ambient", ambientReading->humidity, MetricsResponse::Labels().unit("%").field("humidity"), nullptr, false)
206207
.gauge("ambient", ambientReading->dewpoint(), MetricsResponse::Labels().unit("°C").field("dewpoint"), nullptr, false);
207208
}
209+
std::for_each(Heaters::Instance.begin(), Heaters::Instance.end(), [index=0, &metricsResponse](const Heater &heater) mutable {
210+
metricsResponse.gauge("heater", heater.maxDuty(), MetricsResponse::Labels()
211+
.add("index", String(heater.index()).c_str())
212+
.field("maxDuty")
213+
.add("mode", heater.modeAsString().c_str()), nullptr, index++==0);
214+
});
208215
std::for_each(Heaters::Instance.begin(), Heaters::Instance.end(), [index=0, &metricsResponse](const Heater &heater) mutable {
209216
metricsResponse.gauge("heater", heater.duty(), MetricsResponse::Labels()
210217
.add("index", String(heater.index()).c_str())
211218
.field("duty")
212219
.add("mode", heater.modeAsString().c_str()), nullptr, index++==0);
213220
});
221+
214222
std::for_each(Heaters::Instance.begin(), Heaters::Instance.end(), [index=0, &metricsResponse](const Heater &heater) mutable {
215223
metricsResponse.gauge("heater", heater.active(), MetricsResponse::Labels()
216224
.add("index", String(heater.index()).c_str())

web/src/features/app/api.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
1+
class FetchError extends Error {
2+
constructor(message, request, response, options) {
3+
super(message, options);
4+
this.request = request;
5+
this.response = response;
6+
}
7+
}
18
const fetchJson = async (path, init) => {
29
let url = path;
310
// console.log(process.env)
411
if(process.env.NODE_ENV === 'development' && 'REACT_APP_ASTROPOWERBOX_API_HOST' in process.env) {
512
url = `${process.env.REACT_APP_ASTROPOWERBOX_API_HOST}${path}`;
613
}
714
const response = await fetch(url, init)
15+
if(!response.ok) {
16+
throw new FetchError(`Response failed for ${path}`, { path, init}, response)
17+
}
818
return await response.json();
919
}
1020
const payloadJson = async (path, method, payload) => {

web/src/features/sensors/heaters/Heaters.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,22 +106,43 @@ const SetHeaterModal = ({heater: originalHeater, show, onClose, index}) => {
106106
<Form.Label>Duty</Form.Label>
107107
<Badge className='float-end'><Number value={heater.duty} formatFunction={formatPercentage} decimals={1} /></Badge>
108108
<Form.Range min={0} max={1} step={0.001} value={heater.duty} onChange={updateHeater('duty', parseFloat)} />
109+
{ ['target_temperature', 'dewpoint'].includes(heater.mode) &&
110+
<Form.Text>When ramp is set to a non zero value, and mode is either <code>Dewpoint offset</code> or <code>Target temperature</code>,
111+
this will be a maximum value rather than the real duty.</Form.Text> }
109112
</Form.Group>
110113
</Collapse>
111114
<Collapse in={heater.mode === 'target_temperature' && originalHeater.has_temperature}>
112115
<Form.Group className='mb-3'>
113116
<Form.Label>Target Temperature</Form.Label>
114117
<Badge className='float-end'><Number value={heater.target_temperature} unit='°C'/></Badge>
115118
<Form.Range min={-20} max={50} value={heater.target_temperature} onChange={updateHeater('target_temperature', parseFloat)}/>
119+
<Form.Text>When the temperature sensor will reach this temperature, the heater will turn off.</Form.Text>
116120
</Form.Group>
117121
</Collapse>
118122
<Collapse in={heater.mode === 'dewpoint' && originalHeater.has_temperature}>
119123
<Form.Group className='mb-3'>
120124
<Form.Label>Dewpoint Offset</Form.Label>
121125
<Badge className='float-end'><Number value={heater.dewpoint_offset} unit='°C' /></Badge>
122126
<Form.Range min={-20} max={20} value={heater.dewpoint_offset} onChange={updateHeater('dewpoint_offset', parseFloat)}/>
127+
<Form.Text>
128+
Offset to the dewpoint temperature (either positive or negative).
129+
For instance, if the dewpoint is <code>10°C</code>, and the offset is set to <code>5°C</code>, the target temperature will be <code>15°C</code>.
130+
</Form.Text>
123131
</Form.Group>
124132
</Collapse>
133+
<Collapse in={['dewpoint', 'target_temperature'].includes(heater.mode) && originalHeater.has_temperature}>
134+
<Form.Group className='mb-3'>
135+
<Form.Label>Ramp Offset</Form.Label>
136+
<Badge className='float-end'><Number value={heater.ramp_offset || 0} unit='°C' /></Badge>
137+
<Form.Range min={0} max={20} value={heater.ramp_offset || 0} onChange={updateHeater('ramp_offset', parseFloat)}/>
138+
<Form.Text>
139+
Set this to a number greater than <code>0</code> to start ramping down the duty proportionally to the difference with the target temperature.
140+
For instance, if set to <code>3°C</code>, with a target temperature of <code>25°C</code>, a current temperature of <code>24°C</code>
141+
and a maximum duty of <code>100%</code>, the actual duty will be <code>(25-24)/3 = 33%</code>.
142+
</Form.Text>
143+
</Form.Group>
144+
145+
</Collapse>
125146
</Form>
126147
</Modal.Body>
127148
<Modal.Footer>

web/src/features/sensors/heaters/heatersSlice.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export const heatersSlice = createSlice({
5656
builder
5757
.addCase(getHeatersAsync.fulfilled, (state, action) => onHeatersReceived(state, action.payload))
5858
.addCase(setHeaterAsync.fulfilled, (state, action) => onHeatersReceived(state, action.payload))
59+
.addCase(setHeaterAsync.rejected, (state, action) => console.log(action.error.response))
5960
.addCase(getHistoryAsync.fulfilled, (state, { payload }) => {
6061
if(!payload) {
6162
return;

0 commit comments

Comments
 (0)