Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
cfca629
feat: update float ESP-NOW runtime flow
Skeitt May 29, 2026
c081f83
docs: removed espA_pool dependencies
Skeitt Jun 1, 2026
c4160e8
fix(motion): correct motor geometry convention to match homing
Davide-Colabella May 29, 2026
0a9ecd0
feat(diagnostics): persistent flash log + emergency-stop forensics
Davide-Colabella Jun 2, 2026
e670094
tune(pid): pool-test gains + softer descent kick-start
Davide-Colabella Jun 2, 2026
c0ea7b5
tune(pid): set default gains to Kp=1.7 Ki=0.1 Kd=0.3
Davide-Colabella Jun 2, 2026
8bcb5dc
feat(manual-keyboard): jog relativo non clampato per recupero pistone
Davide-Colabella Jun 6, 2026
34422bd
tune(tof): ricalibra soglie di sicurezza sulla finestra misurata in p…
Davide-Colabella Jun 6, 2026
3f0f21e
refactor(motion): guardia TOF asimmetrica + flash log dell'homing
Davide-Colabella Jun 6, 2026
a700d67
feat(espA): supervisione TOF nelle routine syringe/PID
Davide-Colabella Jun 6, 2026
db036f2
refactor(profile): rinomina parametri profilo con schema per fase (de…
Davide-Colabella Jun 6, 2026
cf0a799
docs: aggiorna riferimenti repo a PoliTOcean/Float
Davide-Colabella Jun 8, 2026
315a45d
fix(comms): proteggi buffer ESPNOW ricevuto da race tra task
Davide-Colabella Jun 8, 2026
d1b819b
feat: added pid tuner pynb
Davide-Colabella Jun 9, 2026
acd52ae
fix(tools): parsing robusto notebook PID (URL Colab, virgola decimale…
Davide-Colabella Jun 9, 2026
64cee19
test(espb): allinea test_parser ai nomi parametri profilo per fase
Davide-Colabella Jun 10, 2026
ca3cff4
feat(profile): valida timeout vs hold e logga inizio/uscita fase su f…
Davide-Colabella Jun 10, 2026
2979fb5
fix(tools): notebook PID legge il dump GUI e ricostruisce la profondi…
Davide-Colabella Jun 10, 2026
711a521
feat(tools): notebook PID analizza discesa e salita con riconosciment…
Davide-Colabella Jun 11, 2026
98ff8d3
tune(config): kick discesa 0.15, velocità motore 1400, MAC ESPA
Davide-Colabella Jun 16, 2026
29e6b90
feat(float): quota da cima, sosta finale e simulatore PID da banco
Davide-Colabella Jun 22, 2026
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
strategy:
fail-fast: false
matrix:
environment: [espA, espB, espA_pool]
environment: [espA, espB]

steps:
- name: Checkout
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
strategy:
fail-fast: true
matrix:
environment: [espA, espB, espA_pool]
environment: [espA, espB]

steps:
- name: Checkout
Expand Down
10 changes: 4 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,25 +57,24 @@ Subject in imperative ("add X", not "added X" / "adds X"), <72 chars. Body expla
### Pull request expectations

- The branch must be **rebased on `master`** before merging (or at least up-to-date) — the PR UI will warn if not.
- All [CI checks](.github/workflows/ci.yml) must be green: `build (espA)`, `build (espB)`, `build (espA_pool)`.
- All [CI checks](.github/workflows/ci.yml) must be green: `build (espA)`, `build (espB)`.
- At least **one approving review** from another maintainer.
- Description should state *what* changes and *why*, plus a manual test plan if the change touches motion/PID/comms (CI does not run hardware tests).

## CI

`.github/workflows/ci.yml` runs on every push to any branch and on every pull request to `master`. It compiles all three PlatformIO environments in parallel:
`.github/workflows/ci.yml` runs on every push to any branch and on every pull request to `master`. It compiles both PlatformIO environments in parallel:

- `espA` — float controller firmware
- `espB` — communication bridge firmware
- `espA_pool` — float controller with shallow-pool profile

Caching of PlatformIO core and build artifacts keeps a typical run under 2 minutes after the first warm-up.

**Hardware tests are not run by CI.** The `test/unit_hw/` and `test/integration/` suites need real ESP32 boards plus motor / TOF / Bar02. Run them locally on a bench setup — see the *Development and Testing* section in [README.md](README.md).

### Releases

Push a tag like `v11.3.0` and the [release workflow](.github/workflows/release.yml) builds all three environments and publishes a GitHub Release with the `firmware.bin` and `firmware.elf` for each one attached.
Push a tag like `v11.3.0` and the [release workflow](.github/workflows/release.yml) builds both environments and publishes a GitHub Release with the `firmware.bin` and `firmware.elf` for each one attached.

```bash
# After the change is merged to master:
Expand All @@ -99,7 +98,7 @@ Enable:
- Dismiss stale pull request approvals when new commits are pushed
- Require status checks to pass before merging
- Require branches to be up to date before merging
- Status checks: `Build espA`, `Build espB`, `Build espA_pool`
- Status checks: `Build espA`, `Build espB`
- Do not allow bypassing the above settings (recommended)
- Restrict who can push to matching branches (admin only — for emergency hotfixes)

Expand All @@ -110,7 +109,6 @@ gh api -X PUT repos/:owner/:repo/branches/master/protection \
-F required_status_checks.strict=true \
-F 'required_status_checks.contexts[]=Build espA' \
-F 'required_status_checks.contexts[]=Build espB' \
-F 'required_status_checks.contexts[]=Build espA_pool' \
-F enforce_admins=false \
-F required_pull_request_reviews.required_approving_review_count=1 \
-F required_pull_request_reviews.dismiss_stale_reviews=true \
Expand Down
139 changes: 89 additions & 50 deletions README.md

Large diffs are not rendered by default.

165 changes: 118 additions & 47 deletions include/config.h

Large diffs are not rendered by default.

153 changes: 124 additions & 29 deletions include/float_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define FLOAT_COMMON_H

#include <Arduino.h>
#include <string.h>

/*
*******************************************************************************
Expand All @@ -28,17 +29,23 @@ enum FloatCommand : uint8_t {
CMD_AUTO_MODE = 5,
CMD_SEND_PACKAGE = 6,
CMD_OTA = 7,
CMD_UPDATE_PID = 8,
CMD_SET_SPEED = 9,
CMD_PID_CONFIG_SET = 8,
CMD_RESERVED_9 = 9,
CMD_TEST_STEPS = 10,
CMD_DEBUG_MODE = 11,
CMD_HOME = 12,
CMD_STOP = 13,
CMD_UPDATE_PID_EXT = 14,
CMD_PID_CONFIG_GET = 14,
CMD_SYRINGE_SET = 15,
CMD_PID_HOLD = 16,
CMD_PID_STEP = 17,
CMD_SET_SURFACE_OFFSET = 18,
CMD_PROFILE_SET = 19,
CMD_PROFILE_GET = 20,
CMD_BALANCE_CONFIG_SET = 21,
CMD_BALANCE_CONFIG_GET = 22,
CMD_MOTOR_CONFIG_SET = 23,
CMD_MOTOR_CONFIG_GET = 24,
};

// List of messages for the ESPA acknowledgements: CS has to be aware of these
Expand All @@ -49,17 +56,22 @@ enum FloatCommand : uint8_t {
#define CMD4_ACK "CMD4_RECVD"
#define CMD5_ACK "SWITCH_AM_RECVD"
#define CMD7_ACK "TRY_UPLOAD_RECVD"
#define CMD8_ACK "CHNG_PARMS_RECVD"
#define CMD9_ACK "TEST_FREQ_RECVD"
#define CMD8_ACK "PID_CONFIG_SET_RECVD"
#define CMD8_ERR "PID_CONFIG_SET_ERR"
#define CMD10_ACK "TEST_STEPS_RECVD"
#define CMD11_ACK "DEBUG_MODE_RECVD"
#define CMD12_ACK "HOME_RECVD"
#define CMD13_ACK "STOP_RECVD"
#define CMD14_ACK "CHNG_PID_EXT_RECVD"
#define CMD15_ACK "SYRINGE_SET_RECVD"
#define CMD16_ACK "PID_HOLD_RECVD"
#define CMD17_ACK "PID_STEP_RECVD"
#define CMD18_ACK "SURFACE_OFF_RECVD"
#define CMD19_ACK "PROFILE_SET_RECVD"
#define CMD19_ERR "PROFILE_SET_ERR"
#define CMD21_ACK "BALANCE_CONFIG_SET_RECVD"
#define CMD21_ERR "BALANCE_CONFIG_SET_ERR"
#define CMD23_ACK "MOTOR_CONFIG_SET_RECVD"
#define CMD23_ERR "MOTOR_CONFIG_SET_ERR"

// Sensor data structure
typedef struct sensor_data {
Expand All @@ -74,32 +86,115 @@ typedef struct input_message {
char message[OUTPUT_LEN];
} input_message;

typedef struct output_message {
float params[3];
struct EmptyPayload {
uint8_t reserved;
};

struct PidConfigPayload {
float kp;
float ki;
float kd;
float periodMs;
float alphaD;
float integralLimit;
float minRetargetFrac;
float uNeutral;
};

struct BalanceConfigPayload {
uint32_t holdMs;
float stopPressureDeltaKpa;
uint8_t stopPressureSamples;
uint16_t samplePeriodMs;
};

struct MotorConfigPayload {
uint32_t maxSpeed;
uint32_t maxAcceleration;
uint32_t homingSpeed;
uint32_t testSpeed;
};

struct TestStepsPayload {
int32_t steps;
uint16_t freq;
uint8_t command = CMD_IDLE;
};

struct SyringeSetPayload {
float uNorm;
float durationS;
};

struct PidHoldPayload {
float depthM;
float durationS;
};

struct PidStepPayload {
float depthM;
};

struct SurfaceOffsetPayload {
float meters;
};

// Ordine dei campi invariato rispetto alla versione precedente (solo rinominati):
// il layout binario e il contratto di serializzazione con la GUI restano compatibili.
// [essenziale] = si imposta per ogni prova (vedi regolamento MATE Task 4.1).
// [avanzato] = safety/calibrazione, cambia di rado (nella GUI sta in "Avanzate").
struct ProfileSetPayload {
uint8_t profileCount; // [avanzato] n. immersioni complete
float descentTargetM; // [essenziale] target discesa, riferito al FONDO del float
float ascentTargetM; // [essenziale] target risalita, riferito al TOP del float
float depthToleranceM; // [essenziale] banda +/- attorno al target
float holdTimeS; // [essenziale] tempo di mantenimento al target
float descentTimeoutS; // [avanzato] timeout assoluto fase discesa (hold incluso)
float ascentTimeoutS; // [avanzato] timeout assoluto fase risalita (hold incluso)
float surfaceRestOffsetM; // [avanzato] quanto il top del float resta sotto pelo a riposo
};

union FloatCommandPayload {
EmptyPayload empty;
PidConfigPayload pidConfig;
BalanceConfigPayload balanceConfig;
MotorConfigPayload motorConfig;
TestStepsPayload testSteps;
SyringeSetPayload syringeSet;
PidHoldPayload pidHold;
PidStepPayload pidStep;
SurfaceOffsetPayload surfaceOffset;
ProfileSetPayload profileSet;
};

typedef struct output_message {
FloatCommand command = CMD_IDLE;
FloatCommandPayload payload;
} output_message;

// MAC addresses - UPDATE THESE TO YOUR ACTUAL MAC ADDRESSES
extern uint8_t espA_mac[6];
extern uint8_t espB_mac[6];

// LED States for better status indication
enum FloatLEDState {
LED_OFF,
LED_INIT, // Green solid - Initializing
LED_IDLE, // Green solid - Ready/Idle
LED_IDLE_DATA, // Green fast blink - Idle with data
LED_LOW_BATTERY, // Red solid - Low battery
LED_ERROR, // Red fast blink - Error state
LED_PROFILE, // Blue blink - Running profile
LED_AUTO_MODE, // Yellow blink - Auto mode active
LED_HOMING, // Purple blink - Motor homing
LED_MOTOR_MOVING, // Purple solid - Motor moving
LED_PID_CONTROL, // Cyan blink - PID active
LED_COMMUNICATION, // White blink - Communicating
LED_OTA_MODE // Orange blink - OTA update mode
inline output_message makeOutputMessage(FloatCommand command) {
output_message message;
memset(&message, 0, sizeof(message));
message.command = command;
return message;
}

static_assert(sizeof(output_message) <= 250, "output_message must fit in one ESP-NOW packet");

// Shared logical LED states. ESPA maps them to the RGB LED; ESPB maps the
// subset it can represent on the built-in single-colour LED.
enum class LEDState : uint8_t {
OFF, // Off / disabled
INIT, // Green solid or boot blinks - Initializing
IDLE, // Green solid / ESPB solid on - Ready/idle
IDLE_WITH_DATA, // Green fast blink - Idle with stored profile data
LOW_BATTERY, // Red solid - Battery voltage below threshold
ERROR, // Red fast blink / ESPB fast blink - Error state
PROFILE, // Blue solid - Running non-PID profile phase
AUTO_MODE, // Yellow blink - Auto mode active
HOMING, // Purple blink - Motor homing
MOTOR_MOVING, // Purple solid - Motor moving
PID_CONTROL, // Cyan blink - PID depth control active
COMMUNICATION, // White solid - Communicating
OTA_MODE, // Orange blink - OTA update mode
};

#endif
2 changes: 1 addition & 1 deletion lib/DebugSerial/library.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
],
"repository": {
"type": "git",
"url": "https://github.com/politocean/Float_2025.git"
"url": "https://github.com/politocean/Float.git"
},
"frameworks": ["arduino"],
"platforms": ["espressif32"],
Expand Down
12 changes: 9 additions & 3 deletions lib/comms/include/comms.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ class CommsManager {
// Returns true if the peer acknowledged delivery.
bool sendMessage(const char* message, uint32_t timeoutMs = 1000);

// Access the last received command (set by the ESP-NOW receive callback)
const output_message& lastCommand() const { return _received; }
void clearCommand() { _received.command = CMD_IDLE; }
// Access the last received command (set by the ESP-NOW receive callback).
// Returns a snapshot by value: the receive callback runs in the WiFi task,
// so the read/write must be guarded to avoid a torn struct.
output_message lastCommand() const;
void clearCommand();

// Outgoing packet — callers fill status_to_send.charge before calling sendMessage()
input_message status_to_send;
Expand All @@ -43,6 +45,10 @@ class CommsManager {
output_message _received;
volatile int8_t _sendResult = -1; // -1=pending, 0=fail, 1=success

// Guards _received against concurrent access between the WiFi-task receive
// callback (writer) and the main loop (reader).
mutable portMUX_TYPE _recvMux = portMUX_INITIALIZER_UNLOCKED;

void _initEspNow();
void _deInitEspNow();
void _reInitEspNow();
Expand Down
21 changes: 20 additions & 1 deletion lib/comms/src/comms.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ void setEspNowChannel() {
CommsManager::CommsManager() {
_instance = this;
memset(&status_to_send, 0, sizeof(status_to_send));
memset(&_received, 0, sizeof(_received));
_received = makeOutputMessage(CMD_IDLE);
}

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -90,9 +90,26 @@ void CommsManager::_reInitEspNow() {
Serial.println("ESP-NOW re-initialised");
}

// ---------------------------------------------------------------------------
output_message CommsManager::lastCommand() const {
portENTER_CRITICAL(&_recvMux);
const output_message snapshot = _received;
portEXIT_CRITICAL(&_recvMux);
return snapshot;
}

// ---------------------------------------------------------------------------
void CommsManager::clearCommand() {
const output_message idle = makeOutputMessage(CMD_IDLE);
portENTER_CRITICAL(&_recvMux);
_received = idle;
portEXIT_CRITICAL(&_recvMux);
}

// ---------------------------------------------------------------------------
bool CommsManager::sendMessage(const char* message, uint32_t timeoutMs) {
strncpy(status_to_send.message, message, sizeof(status_to_send.message) - 1);
status_to_send.message[sizeof(status_to_send.message) - 1] = '\0';

_sendResult = -1;
esp_err_t err = esp_now_send(
Expand Down Expand Up @@ -175,6 +192,8 @@ void CommsManager::_onDataSent(const uint8_t* /*mac*/, esp_now_send_status_t sta

void CommsManager::_onDataRecv(const uint8_t* /*mac*/, const uint8_t* data, int len) {
if (_instance && len == sizeof(output_message)) {
portENTER_CRITICAL(&_instance->_recvMux);
memcpy(&_instance->_received, data, sizeof(output_message));
portEXIT_CRITICAL(&_instance->_recvMux);
}
}
2 changes: 1 addition & 1 deletion lib/espb_bridge_core/include/espb_bridge_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ struct EspbBridgeState {

struct EspbProtocolCommand {
const char* commandText;
uint8_t commandCode;
FloatCommand commandCode;
const char* expectedAck;
};

Expand Down
Loading
Loading