Skip to content

Commit 0ba2ec6

Browse files
authored
feat: Add pressure gauge, optimize communication (#205)
* feat: Add pressure display * feat: Merge sensor and control data * feat: Adjust UI depending on board version
1 parent bffd1ee commit 0ba2ec6

45 files changed

Lines changed: 44452 additions & 16135 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

lib/GaggiMateController/src/ControllerConfig.h

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -131,33 +131,33 @@ const ControllerConfig GM_PRO_REV_1x = {.name = "GaggiMate Pro Rev 1.x",
131131
}};
132132

133133
const ControllerConfig GM_PRO_LEGO = {.name = "GaggiMate Pro Lego Build",
134-
.autodetectValue = 3,
135-
.heaterPin = 14,
136-
.pumpPin = 9,
137-
.pumpSensePin = 21,
138-
.pumpOn = 1,
139-
.valvePin = 10,
140-
.valveOn = 1,
141-
.altPin = 47,
142-
.altOn = 1,
143-
.pressureScl = 41,
144-
.pressureSda = 42,
145-
.maxSckPin = 6,
146-
.maxCsPin = 7,
147-
.maxMisoPin = 4,
148-
.brewButtonPin = 38,
149-
.steamButtonPin = 48,
150-
.scaleSclPin = 17,
151-
.scaleSdaPin = 18,
152-
.scaleSda1Pin = 39,
153-
.ext1Pin = 1,
154-
.ext2Pin = 2,
155-
.ext3Pin = 8,
156-
.ext4Pin = 12,
157-
.ext5Pin = 13,
158-
.capabilites = {
159-
.dimming = true,
160-
.pressure = true,
161-
}};
134+
.autodetectValue = 3,
135+
.heaterPin = 14,
136+
.pumpPin = 9,
137+
.pumpSensePin = 21,
138+
.pumpOn = 1,
139+
.valvePin = 10,
140+
.valveOn = 1,
141+
.altPin = 47,
142+
.altOn = 1,
143+
.pressureScl = 41,
144+
.pressureSda = 42,
145+
.maxSckPin = 6,
146+
.maxCsPin = 7,
147+
.maxMisoPin = 4,
148+
.brewButtonPin = 38,
149+
.steamButtonPin = 48,
150+
.scaleSclPin = 17,
151+
.scaleSdaPin = 18,
152+
.scaleSda1Pin = 39,
153+
.ext1Pin = 1,
154+
.ext2Pin = 2,
155+
.ext3Pin = 8,
156+
.ext4Pin = 12,
157+
.ext5Pin = 13,
158+
.capabilites = {
159+
.dimming = true,
160+
.pressure = true,
161+
}};
162162

163163
#endif // CONTROLLERCONFIG_H

lib/GaggiMateController/src/GaggiMateController.cpp

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,21 @@ void GaggiMateController::setup() {
2020
_ble.initServer(systemInfo);
2121

2222
this->thermocouple = new Max31855Thermocouple(
23-
_config.maxCsPin, _config.maxMisoPin, _config.maxSckPin, [this](float temperature) { _ble.sendTemperature(temperature); },
23+
_config.maxCsPin, _config.maxMisoPin, _config.maxSckPin, [this](float temperature) { /* noop */ },
2424
[this]() { thermalRunawayShutdown(); });
2525
this->heater = new Heater(
2626
this->thermocouple, _config.heaterPin, [this]() { thermalRunawayShutdown(); },
2727
[this](float Kp, float Ki, float Kd) { _ble.sendAutotuneResult(Kp, Ki, Kd); });
2828
this->valve = new SimpleRelay(_config.valvePin, _config.valveOn);
2929
this->alt = new SimpleRelay(_config.altPin, _config.altOn);
30+
if (_config.capabilites.pressure) {
31+
pressureSensor = new PressureSensor(_config.pressureSda, _config.pressureScl, [this](float pressure) { /* noop */ });
32+
}
3033
if (_config.capabilites.dimming) {
3134
pump = new DimmedPump(_config.pumpPin, _config.pumpSensePin);
3235
} else {
3336
pump = new SimplePump(_config.pumpPin, _config.pumpOn);
3437
}
35-
if (_config.capabilites.pressure) {
36-
pressureSensor =
37-
new PressureSensor(_config.pressureSda, _config.pressureScl, [this](float pressure) { _ble.sendPressure(pressure); });
38-
}
3938
this->brewBtn = new DigitalInput(_config.brewButtonPin, [this](const bool state) { _ble.sendBrewBtnState(state); });
4039
this->steamBtn = new DigitalInput(_config.steamButtonPin, [this](const bool state) { _ble.sendSteamBtnState(state); });
4140
this->thermocouple->setup();
@@ -47,14 +46,17 @@ void GaggiMateController::setup() {
4746
this->steamBtn->setup();
4847
if (_config.capabilites.pressure) {
4948
pressureSensor->setup();
49+
_ble.registerPressureScaleCallback([this](float scale) { this->pressureSensor->setScale(scale); });
5050
}
5151

5252
// Initialize last ping time
5353
lastPingTime = millis();
5454

55-
_ble.registerTempControlCallback([this](float temperature) { this->heater->setSetpoint(temperature); });
56-
_ble.registerPumpControlCallback([this](float setpoint) { this->pump->setPower(setpoint); });
57-
_ble.registerValveControlCallback([this](bool state) { this->valve->set(state); });
55+
_ble.registerOutputControlCallback([this](bool valve, float pumpSetpoint, float heaterSetpoint) {
56+
this->pump->setPower(pumpSetpoint);
57+
this->valve->set(valve);
58+
this->heater->setSetpoint(heaterSetpoint);
59+
});
5860
_ble.registerAltControlCallback([this](bool state) { this->alt->set(state); });
5961
_ble.registerPidControlCallback([this](float Kp, float Ki, float Kd) { this->heater->setTunings(Kp, Ki, Kd); });
6062
_ble.registerPingCallback([this]() {
@@ -71,7 +73,8 @@ void GaggiMateController::loop() {
7173
if ((now - lastPingTime) / 1000 > PING_TIMEOUT_SECONDS) {
7274
handlePingTimeout();
7375
}
74-
delay(1000);
76+
sendSensorData();
77+
delay(250);
7578
}
7679

7780
void GaggiMateController::registerBoardConfig(ControllerConfig config) { configs.push_back(config); }
@@ -119,3 +122,11 @@ void GaggiMateController::thermalRunawayShutdown() {
119122
this->alt->set(false);
120123
_ble.sendError(ERROR_CODE_RUNAWAY);
121124
}
125+
126+
void GaggiMateController::sendSensorData() {
127+
if (_config.capabilites.pressure) {
128+
_ble.sendSensorData(this->thermocouple->read(), this->pressureSensor->getPressure());
129+
} else {
130+
_ble.sendSensorData(this->thermocouple->read(), 0.0f);
131+
}
132+
}

lib/GaggiMateController/src/GaggiMateController.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class GaggiMateController {
3232
void thermalRunawayShutdown(void);
3333
void startPidAutotune(void);
3434
void stopPidAutotune(void);
35+
void sendSensorData(void);
3536

3637
ControllerConfig _config = ControllerConfig{};
3738
NimBLEServerController _ble;

lib/GaggiMateController/src/peripherals/Heater.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ void Heater::setupAutotune(int tuningTemp, int samples) {
3737
}
3838

3939
void Heater::loop() {
40-
if (temperature <= 0 || setpoint <= 0) {
40+
if (temperature <= 0.0f || setpoint <= 0.0f) {
4141
pid->SetMode(QuickPID::Control::manual);
4242
digitalWrite(heaterPin, LOW);
4343
relayStatus = false;

lib/GaggiMateController/src/peripherals/Max31855Thermocouple.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ void Max31855Thermocouple::setup() {
2727
}
2828

2929
void Max31855Thermocouple::loop() {
30+
errors = max(0.0f, errors - 0.1f);
3031
int status = max31855->read();
3132
if (status != STATUS_OK) {
3233
ESP_LOGE(LOG_TAG, "Failed to read temperature: %d\n", status);

lib/GaggiMateController/src/peripherals/Max31855Thermocouple.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class Max31855Thermocouple : public TemperatureSensor {
2727
MAX31855 *max31855;
2828
xTaskHandle taskHandle;
2929

30-
int errors = 0;
30+
float errors = .0f;
3131
float temperature = .0f;
3232

3333
int csPin = 0;

lib/GaggiMateController/src/peripherals/PressureSensor.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
#include "PressureSensor.h"
22
#include "Wire.h"
33

4-
PressureSensor::PressureSensor(uint8_t sda_pin, uint8_t scl_pin, const pressure_callback_t &callback, float pressure_range,
4+
PressureSensor::PressureSensor(uint8_t sda_pin, uint8_t scl_pin, const pressure_callback_t &callback, float pressure_scale,
55
float voltage_floor, float voltage_ceil)
6-
: _sda_pin(sda_pin), _scl_pin(scl_pin), _pressure_range(pressure_range), _callback(callback), taskHandle(nullptr) {
6+
: _sda_pin(sda_pin), _scl_pin(scl_pin), _pressure_scale(pressure_scale), _callback(callback), taskHandle(nullptr) {
77
_adc_floor = static_cast<int16_t>(voltage_floor / ADC_STEP);
8-
float pressureAdcRange = (voltage_ceil - voltage_floor) / ADC_STEP;
9-
_pressure_step = pressure_range / pressureAdcRange;
8+
_pressure_adc_range = (voltage_ceil - voltage_floor) / ADC_STEP;
9+
_pressure_step = pressure_scale / _pressure_adc_range;
1010
}
1111

1212
void PressureSensor::setup() {
@@ -27,15 +27,21 @@ void PressureSensor::setup() {
2727
void PressureSensor::loop() {
2828
if (ads->isConnected()) {
2929
int16_t reading = ads->readADC();
30-
// Subtract the voltage floor from the reading in case of a 0.5-4.5V sensor
3130
reading = reading - _adc_floor;
3231
float pressure = reading * _pressure_step;
33-
ESP_LOGV(LOG_TAG, "ADC Reading: %d, Pressure Reading: %f, Pressure Step: %f, Floor: %d", reading, pressure,
32+
_pressure = 0.1f * pressure + 0.9f * _pressure;
33+
_pressure = std::clamp(_pressure, 0.0f, _pressure_scale);
34+
ESP_LOGV(LOG_TAG, "ADC Reading: %d, Pressure Reading: %f, Pressure Step: %f, Floor: %d", reading, _pressure,
3435
_pressure_step, _adc_floor);
35-
_callback(pressure);
36+
_callback(_pressure);
3637
}
3738
}
3839

40+
void PressureSensor::setScale(float pressure_scale) {
41+
_pressure_scale = pressure_scale;
42+
_pressure_step = pressure_scale / _pressure_adc_range;
43+
}
44+
3945
[[noreturn]] void PressureSensor::loopTask(void *arg) {
4046
auto *sensor = static_cast<PressureSensor *>(arg);
4147
while (true) {

lib/GaggiMateController/src/peripherals/PressureSensor.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,28 @@
44
#include <ADS1X15.h>
55
#include <Arduino.h>
66

7-
constexpr int PRESSURE_READ_INTERVAL_MS = 500;
7+
constexpr int PRESSURE_READ_INTERVAL_MS = 50;
88
constexpr float ADC_STEP = 6.144f / 32767.0f;
99

1010
using pressure_callback_t = std::function<void(float)>;
1111

1212
class PressureSensor {
1313
public:
14-
PressureSensor(uint8_t sda_pin, uint8_t scl_pin, const pressure_callback_t &callback, float pressure_range = 20.6843f,
14+
PressureSensor(uint8_t sda_pin, uint8_t scl_pin, const pressure_callback_t &callback, float pressure_scale = 16.0f,
1515
float voltage_floor = 0.5, float voltage_ceil = 4.5);
1616
~PressureSensor() = default;
1717

1818
void setup();
1919
void loop();
20+
inline float getPressure() const { return _pressure; };
21+
void setScale(float pressure_scale);
2022

2123
private:
2224
uint8_t _sda_pin;
2325
uint8_t _scl_pin;
24-
float _pressure_range;
26+
float _pressure = 0.0f;
27+
float _pressure_adc_range;
28+
float _pressure_scale;
2529
float _pressure_step;
2630
int16_t _adc_floor;
2731
ADS1115 *ads = nullptr;

lib/NimBLEComm/src/NimBLEClientController.cpp

Lines changed: 33 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@
33
constexpr size_t MAX_CONNECT_RETRIES = 3;
44
constexpr size_t BLE_SCAN_DURATION_SECONDS = 10;
55

6-
NimBLEClientController::NimBLEClientController()
7-
: client(nullptr), tempControlChar(nullptr), pumpControlChar(nullptr), valveControlChar(nullptr), altControlChar(nullptr),
8-
tempReadChar(nullptr), pingChar(nullptr), pidControlChar(nullptr), errorChar(nullptr), autotuneChar(nullptr),
9-
brewBtnChar(nullptr), steamBtnChar(nullptr), serverDevice(nullptr) {}
6+
NimBLEClientController::NimBLEClientController() : client(nullptr) {}
107

118
void NimBLEClientController::initClient() {
129
NimBLEDevice::init("GPBLC");
@@ -29,16 +26,13 @@ void NimBLEClientController::scan() {
2926
pBLEScan->start(BLE_SCAN_DURATION_SECONDS, nullptr, false);
3027
}
3128

32-
void NimBLEClientController::registerTempReadCallback(const temp_read_callback_t &callback) { tempReadCallback = callback; }
33-
3429
void NimBLEClientController::registerRemoteErrorCallback(const remote_err_callback_t &callback) {
3530
remoteErrorCallback = callback;
3631
}
37-
3832
void NimBLEClientController::registerBrewBtnCallback(const brew_callback_t &callback) { brewBtnCallback = callback; }
3933
void NimBLEClientController::registerSteamBtnCallback(const brew_callback_t &callback) { steamBtnCallback = callback; }
4034

41-
void NimBLEClientController::registerPressureCallback(const pressure_read_callback_t &callback) { pressureCallback = callback; }
35+
void NimBLEClientController::registerSensorCallback(const sensor_read_callback_t &callback) { sensorCallback = callback; }
4236

4337
void NimBLEClientController::registerAutotuneResultCallback(const pid_control_callback_t &callback) {
4438
autotuneResultCallback = callback;
@@ -81,21 +75,15 @@ bool NimBLEClientController::connectToServer() {
8175
}
8276

8377
// Obtain the remote write characteristics
84-
tempControlChar = pRemoteService->getCharacteristic(NimBLEUUID(TEMP_CONTROL_CHAR_UUID));
85-
pumpControlChar = pRemoteService->getCharacteristic(NimBLEUUID(PUMP_CONTROL_CHAR_UUID));
86-
valveControlChar = pRemoteService->getCharacteristic(NimBLEUUID(VALVE_CONTROL_CHAR_UUID));
78+
outputControlChar = pRemoteService->getCharacteristic(NimBLEUUID(OUTPUT_CONTROL_UUID));
8779
altControlChar = pRemoteService->getCharacteristic(NimBLEUUID(ALT_CONTROL_CHAR_UUID));
8880
autotuneChar = pRemoteService->getCharacteristic(NimBLEUUID(AUTOTUNE_CHAR_UUID));
8981
pingChar = pRemoteService->getCharacteristic(NimBLEUUID(PING_CHAR_UUID));
9082
pidControlChar = pRemoteService->getCharacteristic(NimBLEUUID(PID_CONTROL_CHAR_UUID));
9183
infoChar = pRemoteService->getCharacteristic(NimBLEUUID(INFO_UUID));
84+
pressureScaleChar = pRemoteService->getCharacteristic(NimBLEUUID(PRESSURE_SCALE_UUID));
9285

9386
// Obtain the remote notify characteristic and subscribe to it
94-
tempReadChar = pRemoteService->getCharacteristic(NimBLEUUID(TEMP_READ_CHAR_UUID));
95-
if (tempReadChar->canNotify()) {
96-
tempReadChar->subscribe(true, std::bind(&NimBLEClientController::notifyCallback, this, std::placeholders::_1,
97-
std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
98-
}
9987

10088
errorChar = pRemoteService->getCharacteristic(NimBLEUUID(ERROR_CHAR_UUID));
10189
if (errorChar->canNotify()) {
@@ -121,17 +109,25 @@ bool NimBLEClientController::connectToServer() {
121109
std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
122110
}
123111

112+
sensorChar = pRemoteService->getCharacteristic(NimBLEUUID(SENSOR_DATA_UUID));
113+
if (sensorChar != nullptr && sensorChar->canNotify()) {
114+
sensorChar->subscribe(true, std::bind(&NimBLEClientController::notifyCallback, this, std::placeholders::_1,
115+
std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
116+
}
117+
124118
delay(500);
125119

126120
readyForConnection = false;
127121
return true;
128122
}
129123

130-
void NimBLEClientController::sendTemperatureControl(float setpoint) {
131-
if (tempControlChar != nullptr && client->isConnected()) {
132-
char tempStr[8];
133-
snprintf(tempStr, sizeof(tempStr), "%.2f", setpoint);
134-
tempControlChar->writeValue(tempStr);
124+
void NimBLEClientController::sendOutputControl(bool valve, float pumpSetpoint, float boilerSetpoint) {
125+
if (client->isConnected() && outputControlChar != nullptr) {
126+
char str[30];
127+
snprintf(str, sizeof(str), "%d,%d,%.1f,%.1f", 0, valve ? 1 : 0, pumpSetpoint, boilerSetpoint);
128+
if (outputControlChar->getValue().c_str() != str) {
129+
outputControlChar->writeValue(str, false);
130+
}
135131
}
136132
}
137133

@@ -141,17 +137,12 @@ void NimBLEClientController::sendPidSettings(const String &pid) {
141137
}
142138
}
143139

144-
void NimBLEClientController::sendPumpControl(float setpoint) {
145-
if (pumpControlChar != nullptr && client->isConnected()) {
146-
char pumpStr[8];
147-
snprintf(pumpStr, sizeof(pumpStr), "%.2f", setpoint);
148-
pumpControlChar->writeValue(pumpStr);
149-
}
150-
}
151-
152-
void NimBLEClientController::sendValveControl(bool pinState) {
153-
if (valveControlChar != nullptr && client->isConnected()) {
154-
valveControlChar->writeValue(pinState ? "1" : "0");
140+
void NimBLEClientController::setPressureScale(float scale) {
141+
if (client->isConnected() && pressureScaleChar != nullptr) {
142+
constexpr size_t bufferSize = sizeof(float);
143+
char buffer[bufferSize];
144+
std::memcpy(buffer + 0, &scale, sizeof(scale));
145+
pressureScaleChar->writeValue(buffer);
155146
}
156147
}
157148

@@ -203,20 +194,6 @@ void NimBLEClientController::onDisconnect(NimBLEClient *pServer) {
203194
// Notification callback
204195
void NimBLEClientController::notifyCallback(NimBLERemoteCharacteristic *pRemoteCharacteristic, uint8_t *pData, size_t,
205196
bool) const {
206-
if (pRemoteCharacteristic->getUUID().equals(NimBLEUUID(TEMP_READ_CHAR_UUID))) {
207-
float temperature = atof((char *)pData);
208-
ESP_LOGV(LOG_TAG, "Temperature read: %.2f", temperature);
209-
if (tempReadCallback != nullptr) {
210-
tempReadCallback(temperature);
211-
}
212-
}
213-
if (pRemoteCharacteristic->getUUID().equals(NimBLEUUID(PRESSURE_UUID))) {
214-
float pressure = atof((char *)pData);
215-
ESP_LOGV(LOG_TAG, "Pressure read: %.2f", pressure);
216-
if (pressureCallback != nullptr) {
217-
pressureCallback(pressure);
218-
}
219-
}
220197
if (pRemoteCharacteristic->getUUID().equals(NimBLEUUID(ERROR_CHAR_UUID))) {
221198
int errorCode = atoi((char *)pData);
222199
ESP_LOGV(LOG_TAG, "Error read: %d", errorCode);
@@ -238,6 +215,16 @@ void NimBLEClientController::notifyCallback(NimBLERemoteCharacteristic *pRemoteC
238215
steamBtnCallback(steamButtonStatus);
239216
}
240217
}
218+
if (pRemoteCharacteristic->getUUID().equals(NimBLEUUID(SENSOR_DATA_UUID))) {
219+
String data = String((char *)pData);
220+
float temperature = get_token(data, 0, ',').toFloat();
221+
float pressure = get_token(data, 1, ',').toFloat();
222+
223+
ESP_LOGV(LOG_TAG, "Received sensor data: temperature=%.1f, pressure=%.1f", temperature, pressure);
224+
if (sensorCallback != nullptr) {
225+
sensorCallback(temperature, pressure);
226+
}
227+
}
241228
if (pRemoteCharacteristic->getUUID().equals(NimBLEUUID(AUTOTUNE_RESULT_UUID))) {
242229
String settings = String((char *)pData);
243230
ESP_LOGV(LOG_TAG, "autotune result: %s", settings.c_str());

0 commit comments

Comments
 (0)