Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,24 @@ node_modules/
__pycache__
data/w
.codebuddy
*.orig
src/version.h
src/display/ui/default/lvgl/project.info

# pioarduino build artifacts (regenerated each build)
sdkconfig.display
sdkconfig.display-headless
sdkconfig.display-headless-8m
sdkconfig.controller
sdkconfig.defaults
managed_components/
dependencies.lock
# pioarduino dual-framework hybrid-compile scaffold (auto-generated)
.dummy/

# Serial/device monitor captures
logs/

# Editor / agent workspaces
.serena/
.claude/
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.16.0)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(gaggimate)
54 changes: 0 additions & 54 deletions boards/esp32-s3-supermini.json

This file was deleted.

1 change: 0 additions & 1 deletion lib/GaggiMateController/library.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
"ble_ota_dfu": "file://lib/ble_ota_dfu",
"NanoPbComm": "file://lib/NanoPbComm",
"nanopb/Nanopb": "^0.4.9",
"h2zero/NimBLE-Arduino": "^1.4.0",
"robtillaart/MAX31855": "^0.6.1",
"bblanchon/ArduinoJson": "^7.2.1",
"PSM": "https://github.com/gaggimate/PSM.Library.git",
Expand Down
4 changes: 2 additions & 2 deletions lib/GaggiMateController/src/peripherals/DigitalInput.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ class DigitalInput {
uint8_t _stable_counter = 0;
int _current_state = 2;
int _last_state = 2;
xTaskHandle taskHandle;
TaskHandle_t taskHandle;
input_callback_t _callback;

const char *LOG_TAG = "Heater";
const char *LOG_TAG = "DigitalInput";
static void loopTask(void *arg);
};

Expand Down
2 changes: 1 addition & 1 deletion lib/GaggiMateController/src/peripherals/DimmedPump.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class DimmedPump : public Pump {
PSM _psm;
PressureSensor *_pressureSensor;
PressureController _pressureController;
xTaskHandle taskHandle;
TaskHandle_t taskHandle;

ControlMode _mode = ControlMode::POWER;
float _power = 0.0f;
Expand Down
2 changes: 1 addition & 1 deletion lib/GaggiMateController/src/peripherals/DistanceSensor.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class DistanceSensor {

TwoWire *i2c;
VL53L0X *tof;
xTaskHandle taskHandle;
TaskHandle_t taskHandle;
distance_callback_t _callback;
int measurements = 0;
int currentMillis = 0;
Expand Down
2 changes: 1 addition & 1 deletion lib/GaggiMateController/src/peripherals/Heater.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class Heater {
float calculateSafetyScaling(float tempError);
TemperatureSensor *sensor;
uint8_t heaterPin;
xTaskHandle taskHandle;
TaskHandle_t taskHandle;
SimplePID *simplePid = nullptr;
Autotune *autotuner = nullptr;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class Max31855Thermocouple : public TemperatureSensor {

private:
MAX31855 *max31855;
xTaskHandle taskHandle;
TaskHandle_t taskHandle;

int errorCount = 0;
std::array<int, MAX31855_ERROR_WINDOW> resultBuffer{};
Expand All @@ -44,7 +44,7 @@ class Max31855Thermocouple : public TemperatureSensor {
temperature_error_callback_t error_callback;

const char *LOG_TAG = "Max31855Thermocouple";
static void monitorTask(void *arg);
[[noreturn]] static void monitorTask(void *arg);
};

#endif // MAX31855THERMOCOUPLE_H
4 changes: 2 additions & 2 deletions lib/GaggiMateController/src/peripherals/PressureSensor.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ class PressureSensor {
int16_t _adc_floor;
ADS1115 *ads = nullptr;
pressure_callback_t _callback;
xTaskHandle taskHandle;
TaskHandle_t taskHandle;

const char *LOG_TAG = "PressureSensor";
static void loopTask(void *arg);
[[noreturn]] static void loopTask(void *arg);
};

#endif // PRESSURESENSOR_H
6 changes: 3 additions & 3 deletions lib/GaggiMateController/src/peripherals/Pump.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ class Pump {
public:
virtual ~Pump() = default;

virtual void setup();
virtual void loop();
virtual void setPower(float setpoint);
virtual void setup() = 0;
virtual void loop() = 0;
virtual void setPower(float setpoint) = 0;
};

#endif // PUMP_H
2 changes: 1 addition & 1 deletion lib/GaggiMateController/src/peripherals/SimplePump.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class SimplePump : public Pump {
float _windowSize = 5000.0f;
unsigned long windowStartTime = 0;
unsigned long nextSwitchTime = 0;
xTaskHandle taskHandle;
TaskHandle_t taskHandle;

const char *LOG_TAG = "SimplePump";
static void loopTask(void *arg);
Expand Down
6 changes: 4 additions & 2 deletions lib/GaggiMateController/src/peripherals/TemperatureSensor.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

class TemperatureSensor {
public:
virtual float read();
virtual bool isErrorState();
virtual ~TemperatureSensor() = default;

virtual float read() = 0;
virtual bool isErrorState() = 0;
};

#endif // TEMPERATURESENSOR_H
3 changes: 1 addition & 2 deletions lib/NanoPbComm/library.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
"frameworks": "arduino",
"platforms": ["espressif32"],
"dependencies": {
"nanopb/Nanopb": "^0.4.9",
"h2zero/NimBLE-Arduino": "^1.4.0"
"nanopb/Nanopb": "^0.4.9"
},
"build": {
"flags": ["-I src"]
Expand Down
41 changes: 26 additions & 15 deletions lib/NanoPbComm/src/ble/BleClientTransport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

void BleClientTransport::init(const String &deviceName) {
NimBLEDevice::init(deviceName.c_str());
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
// +9 dBm — preserves master's setPower(ESP_PWR_LVL_P9), which was +9 dBm
// under NimBLE 1.4.x. 2.x setPower takes int8_t dBm, so pass 9 directly
// (2.x quantizes 9 -> ESP_PWR_LVL_P9). Do NOT pass the enum: ESP_PWR_LVL_P9
// is value 11 on ESP32-S3 and would round up to +12 dBm.
NimBLEDevice::setPower(9);
NimBLEDevice::setMTU(256);
_client = NimBLEDevice::createClient();
_scanner = NimBLEDevice::getScan();
Expand All @@ -16,8 +20,12 @@ void BleClientTransport::init(const String &deviceName) {

void BleClientTransport::scan() {
_readyForConnection = false;
_scanner->clearDuplicateCache();
_scanner->setAdvertisedDeviceCallbacks(this, true);
_scanner->clearResults(); // esp-nimble-cpp 2.x has no clearDuplicateCache(); results vector is unused (setMaxResults(0))
// 1.x setAdvertisedDeviceCallbacks(cb, wantDuplicates=true) -> 2.x
// setScanCallbacks(cb, wantDuplicates). wantDuplicates=true keeps duplicate
// adverts flowing (internally setDuplicateFilter(false)) — same as the explicit
// setDuplicateFilter(false) below, preserving the pre-2.x rediscovery behaviour.
_scanner->setScanCallbacks(this, true);
// BLE and Wi-Fi share the single 2.4 GHz radio. A low-duty passive scan
// (1.25% duty, listen-only) keeps Wi-Fi RTT responsive; discovery is a touch
// slower but still reliable. (Carried over from the previous transport.)
Expand All @@ -26,7 +34,7 @@ void BleClientTransport::scan() {
_scanner->setMaxResults(0);
_scanner->setDuplicateFilter(false);
_scanner->setActiveScan(false);
_scanner->start(0, nullptr, false); // 0 = continuous
_scanner->start(0, false, false); // 2.x: start(duration=0 continuous, isContinue, restart)
}

void BleClientTransport::maintain() {
Expand All @@ -42,7 +50,7 @@ bool BleClientTransport::connectToServer() {
if (!_haveServerAddress)
return false;

ESP_LOGI(LOG_TAG, "Connecting to advertised device");
ESP_LOGI(LOG_TAG, "Connecting to advertised device: %s", _serverAddress.toString().c_str());
unsigned int tries = 0;
do {
if (tries >= MAX_CONNECT_RETRIES) {
Expand All @@ -51,7 +59,8 @@ bool BleClientTransport::connectToServer() {
return false;
}
if (!_client->connect(_serverAddress)) {
ESP_LOGW(LOG_TAG, "Connect failed, retrying");
int error = _client->getLastError();
ESP_LOGW(LOG_TAG, "Connect failed: %d, retrying", error);
delay(500);
}
tries++;
Expand Down Expand Up @@ -137,22 +146,25 @@ bool BleClientTransport::send(const uint8_t *data, size_t length) {

bool BleClientTransport::isConnected() const { return _client != nullptr && _client->isConnected(); }

void BleClientTransport::onResult(NimBLEAdvertisedDevice *advertisedDevice) {
void BleClientTransport::onResult(const NimBLEAdvertisedDevice *advertisedDevice) {
if (!advertisedDevice->haveServiceUUID())
return;
if (advertisedDevice->isAdvertisingService(NimBLEUUID(gm_proto::SERVICE_UUID))) {
ESP_LOGI(LOG_TAG, "Found controller, ready to connect");
_scanner->stop();
// Take a value copy of the address now -- the device object is freed as
// soon as this callback returns (see _serverAddress note in the header).
// Copy everything we need off advertisedDevice BEFORE stopping the scan.
// With setMaxResults(0), NimBLEScan::stop() calls clearResults(), which
// deletes the very advertisedDevice handed to this callback -- so reading
// it after stop() is a use-after-free that returns a garbage peer address
// (the connect then fails with BLE_HS_EINVAL).
_serverAddress = advertisedDevice->getAddress();
_haveServerAddress = true;
ESP_LOGI(LOG_TAG, "Found controller at address %s with name %s, ready to connect",
_serverAddress.toString().c_str(), advertisedDevice->getName().c_str());
_scanner->stop();
_readyForConnection = true;
}
}

void BleClientTransport::onDisconnect(NimBLEClient *client) {
(void)client;
void BleClientTransport::onDisconnect(NimBLEClient *, int) {
ESP_LOGI(LOG_TAG, "Disconnected, will rescan");
_writeChar = nullptr;
_notifyChar = nullptr;
Expand All @@ -161,7 +173,6 @@ void BleClientTransport::onDisconnect(NimBLEClient *client) {
scan();
}

void BleClientTransport::notifyCallback(NimBLERemoteCharacteristic *characteristic, uint8_t *data, size_t length, bool) {
(void)characteristic;
void BleClientTransport::notifyCallback(NimBLERemoteCharacteristic *, uint8_t *data, size_t length, bool) {
emitData(data, length);
}
18 changes: 10 additions & 8 deletions lib/NanoPbComm/src/ble/BleClientTransport.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* client controller, including the low-duty passive scan tuned for Wi-Fi
* coexistence.
*/
class BleClientTransport : public Transport, public NimBLEAdvertisedDeviceCallbacks, public NimBLEClientCallbacks {
class BleClientTransport : public Transport, public NimBLEScanCallbacks, public NimBLEClientCallbacks {
public:
BleClientTransport() = default;

Expand Down Expand Up @@ -48,11 +48,13 @@ class BleClientTransport : public Transport, public NimBLEAdvertisedDeviceCallba
NimBLEScan *_scanner = nullptr;
// Copy of the controller's address taken in onResult(). We must NOT keep the
// NimBLEAdvertisedDevice* itself: with setMaxResults(0) NimBLE deletes that
// object the moment onResult() returns (NimBLEScan erase()), so dereferencing
// it later in connectToServer() -- which runs on the main loop task -- is a
// use-after-free that reads whatever string now occupies the freed heap slot
// back as a bogus peer address. NimBLEAddress is a value type, so copying it
// while the device is still alive is safe and survives the deletion.
// object out from under us -- both when onResult() returns (NimBLEScan erase())
// and, crucially, the instant we call _scanner->stop() inside the callback
// (stop() -> clearResults() -> delete). So the value copy must be taken BEFORE
// stop(); dereferencing the device after that -- or later in connectToServer()
// on the loop task -- is a use-after-free that reads whatever now occupies the
// freed heap slot back as a bogus peer address. NimBLEAddress is a value type,
// so the copy survives the deletion.
NimBLEAddress _serverAddress{};
bool _haveServerAddress = false;
NimBLERemoteCharacteristic *_writeChar = nullptr; // to server (RX_CHAR_UUID)
Expand All @@ -72,8 +74,8 @@ class BleClientTransport : public Transport, public NimBLEAdvertisedDeviceCallba
static constexpr uint16_t CONN_LATENCY = 0;
static constexpr uint16_t CONN_TIMEOUT = 400; // 4 s

void onResult(NimBLEAdvertisedDevice *advertisedDevice) override;
void onDisconnect(NimBLEClient *client) override;
void onResult(const NimBLEAdvertisedDevice *advertisedDevice) override;
void onDisconnect(NimBLEClient *client, int reason) override;
void notifyCallback(NimBLERemoteCharacteristic *characteristic, uint8_t *data, size_t length, bool isNotify);

static constexpr const char *LOG_TAG = "BleClientTransport";
Expand Down
Loading
Loading