diff --git a/Doxyfile b/Doxyfile index b8c499e..e530180 100644 --- a/Doxyfile +++ b/Doxyfile @@ -48,13 +48,13 @@ PROJECT_NAME = "HELIA" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = v0.1.0 +PROJECT_NUMBER = v0.1.1 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = "REXUS/BEXUS CubeSat payload flight software" +PROJECT_BRIEF = "REXUS/BEXUS Experiment On Board Flight Software documentation" # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 diff --git a/src/Master/HAL/esp32/eth_w5500.c b/src/Master/HAL/esp32/eth_w5500.c index cda0e64..04cbc65 100644 --- a/src/Master/HAL/esp32/eth_w5500.c +++ b/src/Master/HAL/esp32/eth_w5500.c @@ -1,7 +1,8 @@ -/* W5500 Ethernet bring-up. This is a thin driver shim, same kind of thing +/** @brief W5500 Ethernet thingymabob! + * This is a thin driver shim, same kind of thing * as can_bus_twai.c: it brings up a peripheral and reports link/IP state, * owns no task and no queue of its own. Lives in Master's HAL because only - * Master has Ethernet - it isn't a reusable lib. */ + * Master has Ethernet so it isn't reusable */ #include "eth_w5500.h" #include diff --git a/src/Master/HAL/esp32/eth_w5500.h b/src/Master/HAL/esp32/eth_w5500.h index 0b7e139..c03214e 100644 --- a/src/Master/HAL/esp32/eth_w5500.h +++ b/src/Master/HAL/esp32/eth_w5500.h @@ -5,14 +5,13 @@ #include "esp_err.h" #include "freertos/FreeRTOS.h" -/* Bring up SPI + W5500 + esp_netif and start the driver. Non-blocking: - * link/IP arrive later via events. */ +/* Bring up SPI + W5500 + esp_netif and start the driver */ esp_err_t ttc_eth_init(void); -/* True once the cable is up AND the interface has an IP. */ +/* True once the cable is up AND the interface has an IP */ bool ttc_eth_ready(void); -/* Block until ready or timeout. Returns ttc_eth_ready(). */ +/* Block until ready or timeout. Returns ttc_eth_ready() */ bool ttc_eth_wait_ready(TickType_t timeout); #endif \ No newline at end of file diff --git a/src/Master/HAL/esp32/pins.h b/src/Master/HAL/esp32/pins.h index 5dc4cc9..c564931 100644 --- a/src/Master/HAL/esp32/pins.h +++ b/src/Master/HAL/esp32/pins.h @@ -1,4 +1,7 @@ -/** @file pins.h @brief Master board wiring. Only the ESP32 HAL includes this. */ +/** @file pins.h + * @brief Master board wiring + * + */ #ifndef HELIA_MASTER_PINS_H #define HELIA_MASTER_PINS_H diff --git a/src/Master/HAL/sim/hal_sim.c b/src/Master/HAL/sim/hal_sim.c index c6ca6c6..33b2792 100644 --- a/src/Master/HAL/sim/hal_sim.c +++ b/src/Master/HAL/sim/hal_sim.c @@ -1,10 +1,9 @@ -/* Master HAL for running on a laptop. */ +/* Master HAL for running on ma laptop */ #include "hal.h" #include #include bool hal_init(void) { - /* The simulated CAN bus is added with the simulator step. */ return true; } diff --git a/src/Master/control.c b/src/Master/control.c index 1f6170b..6dd5829 100644 --- a/src/Master/control.c +++ b/src/Master/control.c @@ -1,16 +1,13 @@ -/* Master control core: owns the flight-phase state machine (flight_phase.h +/** @brief Master control core: owns the flight-phase state machine (flight_phase.h * does the actual FSM logic; this file just synchronises access to one * instance across cores and hooks it up to CAN + logging). * * Two callers touch the flight state from two different cores: - * - master_control_task (this file, core 1): periodic no-motion tick. + * - master_control_task (this file, core 1): periodic no-motion tick * - master_control_set_phase (called from ttc.c's uplink handler, core - * 0): ground-commanded phase changes. + * 0): ground-commanded phase changes * Hence the mutex - see master_control_init()'s doc comment in control.h * for why it's created eagerly from app_main() rather than lazily here. - * - * Guarded by ESP_PLATFORM so this compiles to nothing on the host (same - * as before) - flight_phase.c underneath it is what's host-testable. */ #include "control.h" @@ -34,10 +31,7 @@ static uint32_t now_ms(void) { return (uint32_t)pdTICKS_TO_MS(xTaskGetTickCount()); } -/* Tells every other node the phase changed - CAN_CMD_FLIGHT_PHASE, per the - * SED's Broadcast CAN Message Dictionary (source BROADCAST, type 0x03). - * Payload: 1 byte, the new master_phase_t value. Called with s_flight_mutex - * already held so the logged phase name matches what was just applied. */ +/** @brief Tells every other node the phase changed */ static void broadcast_phase(master_phase_t phase, master_flight_result_t result) { can_frame_t f = { .id = can_make_id(CAN_PRIO_IMPORTANT, CAN_SRC_BROADCAST, CAN_CMD_FLIGHT_PHASE), diff --git a/src/Master/control.h b/src/Master/control.h index 06e6923..d1de110 100644 --- a/src/Master/control.h +++ b/src/Master/control.h @@ -11,38 +11,20 @@ extern "C" { * @brief Create the flight-phase state and its mutex. MUST be called once * from app_main(), before any task that might call * master_control_set_phase()/master_control_get_phase() is - * created (i.e. before ttc_uplink_task) - this avoids a boot-time - * race where a ground command could arrive before the mutex - * exists. master_control_task() itself only ticks; it doesn't - * create state, so its own creation order relative to this - * doesn't matter, only relative to ttc_uplink_task's. + * created (i.e. before ttc_uplink_task) other we will have a race condition */ void master_control_init(void); /** @brief Control core task: periodically checks the DESCENDING -> LANDED - * no-motion timeout. Pinned to core 1 by setup.c. ESP32-only (see - * control.c); declared unconditionally here since only ESP32-only - * callers reference it. Requires master_control_init() to have - * run first. */ + * no-motion timeout */ void master_control_task(void *arg); /** - * @brief Thread-safe: apply a ground-commanded phase change. Called from - * ttc.c's uplink handler (core 0) when a CAN_CMD_FLIGHT_PHASE - * uplink command arrives - this is the flight manager's gate - * between ground commands and the rest of the system for that - * command. Broadcasts CAN_CMD_FLIGHT_PHASE to the bus and logs the - * result internally; the caller just needs the result to ACK - * ground correctly. - * - * Safe to call even before master_control_task's first loop - * iteration, but NOT before it has been created (the mutex it uses - * is created at task start) - setup.c creates control before ttc's - * uplink task, so this is naturally satisfied. + * @brief Thread-safe: apply a ground-commanded phase chang */ master_flight_result_t master_control_set_phase(master_phase_t next); -/** @brief Thread-safe read of the current flight phase. */ +/** @brief Thread-safe read of the current flight phase */ master_phase_t master_control_get_phase(void); #ifdef __cplusplus diff --git a/src/Master/flight_phase.c b/src/Master/flight_phase.c index d992917..cc9adbb 100644 --- a/src/Master/flight_phase.c +++ b/src/Master/flight_phase.c @@ -6,7 +6,7 @@ static const char *PHASE_NAMES[MASTER_PHASE_COUNT] = { [MASTER_PHASE_ASCENDING] = "ASCENDING", [MASTER_PHASE_FLOAT] = "FLOAT", [MASTER_PHASE_DESCENDING] = "DESCENDING", - [MASTER_PHASE_LANDED] = "LANDED", + [MASTER_PHASE_LANDED] = "LANDED", // This probably won't ever be sent over TT&C because radio connection will be lost }; void master_flight_init(master_flight_t *f, uint32_t now_ms) { diff --git a/src/Master/flight_phase.h b/src/Master/flight_phase.h index 5b34dcb..ff6b5e8 100644 --- a/src/Master/flight_phase.h +++ b/src/Master/flight_phase.h @@ -53,9 +53,9 @@ typedef enum { } master_flight_result_t; /** @brief Auto-transition DESCENDING -> LANDED after this long with no - * motion noted via master_flight_note_motion(). Placeholder value - * (5 minutes) - nothing feeds real motion data yet (see ttc.h/.c - * and control.c), so tune this once it does. */ + * motion noted via master_flight_note_motion() + * Currently this is set to 5 minutes... It might be a little bit longer lmao (5 mins for testing) + * */ #define MASTER_LANDING_NO_MOTION_MS (5u * 60u * 1000u) typedef struct { @@ -64,35 +64,21 @@ typedef struct { uint32_t last_motion_ms; /**< now_ms() at last noted motion (or phase entry) */ } master_flight_t; -/** @brief Starts in MASTER_PHASE_TESTING. */ +/** @brief Starts in MASTER_PHASE_TESTING */ void master_flight_init(master_flight_t *f, uint32_t now_ms); master_phase_t master_flight_phase(const master_flight_t *f); -/** @brief Human-readable phase name for logging. Never NULL. */ +/** @brief Human-readable phase name for logging */ const char *master_phase_name(master_phase_t p); -/** - * @brief Apply a ground-commanded (or otherwise externally requested) - * phase change. See the "override" note above for how out-of-order - * requests are handled. - */ -master_flight_result_t master_flight_set_phase(master_flight_t *f, master_phase_t next, uint32_t now_ms); -/** - * @brief Call periodically (e.g. once per control-loop tick). Checks the - * DESCENDING -> LANDED no-motion timeout and applies it internally - * if due. No-op in any other phase. - */ +master_flight_result_t master_flight_set_phase(master_flight_t *f, master_phase_t next, uint32_t now_ms); void master_flight_tick(master_flight_t *f, uint32_t now_ms); /** - * @brief Call whenever motion is detected, to feed the no-motion timeout. - * TODO: nothing calls this yet - wire it to real Instrumentation - * CAN data (accel/altitude-rate) once that message exists. Until - * then DESCENDING always times out to LANDED - * MASTER_LANDING_NO_MOTION_MS after entry, regardless of actual - * motion. + * @brief Feeds the motion time out for landing detection. + * TODO: Nothing calls this, need to use CAN frames from instrumentation to update this (blocked) */ void master_flight_note_motion(master_flight_t *f, uint32_t now_ms); diff --git a/src/Master/setup.c b/src/Master/setup.c index 0650f9b..9db6ed6 100644 --- a/src/Master/setup.c +++ b/src/Master/setup.c @@ -1,19 +1,10 @@ /* Master boot and task loop. * * Two cores, matching EPS/Instrumentation's comms/control split: - * core 0 (comms_task + ttc_uplink_task): CAN bus and the TT&C link. - * core 1 (master_control_task): flight manager. Placeholder for now. + * core 0 (comms_task + ttc_uplink_task): CAN bus and the TT&C link + * core 1 (master_control_task): flight manager (will be extended in the future) * - * TT&C is deliberately minimal right now - the goal is proving Master and - * a ground script can talk at all, not the full flight design: - * - comms_task mirrors every CAN frame it sees down to ground over UDP - - * both what Master itself sends (its heartbeat, ACKs, etc, from - * master_comms_on_tick()/on_frame()) and every frame it receives from - * other nodes on the bus (rx, via can_bus_recv()). No separate task, - * no queue, no filtering - just a socket call next to the existing CAN - * send/recv, so ground sees the whole bus, not just Master's traffic. - * - ttc_uplink_task (in ttc.c) logs and ACKs whatever ground sends over - * TCP. Nothing is forwarded to CAN yet. + * TT&C is deliberately minimal right now, just enough to mirror CAN traffic down to ground for bench testing */ #include #include "freertos/FreeRTOS.h" @@ -46,12 +37,12 @@ static void send_all(const can_frame_t *tx, size_t n) { if (!can_bus_send(&tx[i], TX_TIMEOUT_MS)) { ESP_LOGW(TAG, "send failed, id=0x%03lX", (unsigned long)tx[i].id); } else if (can_id_type(tx[i].id) != CAN_MSG_HEARTBEAT || can_id_source(tx[i].id) != CAN_SRC_MASTER) { - ESP_LOGI(TAG, "sent: %s", master_describe_frame(&tx[i], text, sizeof text)); /* don't log our own heartbeat */ + ESP_LOGI(TAG, "sent: %s", master_describe_frame(&tx[i], text, sizeof text)); /* don't log our own heartbeat ;-; */ } } } -/* "EPS online" / "EPS LOST" whenever a node appears or goes silent */ +/* "EPS online" / "EPS LOST" whenever a node appears or goes silent (good for debugging restarting) */ static void log_presence_changes(uint8_t before, uint8_t after) { for (unsigned i = 0; i < MASTER_NODE_SLOTS; i++) { uint8_t bit = (uint8_t)(1u << i); @@ -63,11 +54,7 @@ static void log_presence_changes(uint8_t before, uint8_t after) { } } -/* Packs and mirrors one CAN frame down to ground over UDP. No-ops quietly - * if the socket isn't up or the link isn't ready yet - same "just drop it, - * same as ground not being switched on" philosophy as before. Pulled out - * to a helper because it's now called for every frame Master sees on the - * bus, not just its own outgoing ones - see comms_task. */ +/* Packs and mirrors one CAN frame down to ground over UDP. This will fail silently for now (TODO) */ static void downlink_frame(int sock, const struct sockaddr_in *ground, uint8_t *seq, const can_frame_t *f) { if (sock < 0 || !ttc_eth_ready()) return; @@ -85,33 +72,23 @@ static void comms_task(void *arg) { ESP_LOGI(TAG, "state %s, auto-unlock %s", fsm_state_name(comms.state), MASTER_AUTO_UNLOCK ? "ON" : "OFF"); /* One UDP socket, opened once, used to mirror CAN traffic down to - * ground - both frames Master itself sends AND every frame it - * receives from other nodes, which is what gives ground full-bus - * visibility rather than just Master's own heartbeat. No retries - * beyond what sendto() does on its own - if the link is down the - * packet is just dropped, same as the ground station not being - * switched on. */ + * ground */ int dl_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); struct sockaddr_in ground = { .sin_family = AF_INET, .sin_port = htons(CONFIG_TTC_UDP_DOWNLINK_PORT), .sin_addr.s_addr = inet_addr(CONFIG_TTC_GROUND_IP), }; - uint8_t dl_seq = 0; /* one running counter across everything mirrored down, - * so ground can spot drops in the combined stream */ + uint8_t dl_seq = 0; - for (;;) { + while (1) { can_frame_t rx, tx[MASTER_COMMS_MAX_TX]; char text[96]; uint8_t alive_before = comms.alive_mask; if (can_bus_recv(&rx, RX_TIMEOUT_MS)) { ESP_LOGI(TAG, "rx: %s", master_describe_frame(&rx, text, sizeof text)); - /* CAN controllers don't loop a node's own transmitted frames - * back to its own RX, so this is the ONLY path that captures - * traffic from every other node - EPS/Instrumentation/ - * Photonics/etc - Master's own tx frames are mirrored - * separately below. */ + downlink_frame(dl_sock, &ground, &dl_seq, &rx); send_all(tx, master_comms_on_frame(&comms, now_ms(), &rx, tx)); } @@ -137,9 +114,7 @@ void app_main(void) { if (!hal_init()) ESP_LOGE(TAG, "CAN init failed"); - /* Must run before ttc_uplink_task is created: that task can call - * master_control_set_phase() as soon as a ground command arrives, and - * this is what creates the mutex it needs. See control.h. */ + /* Must run before ttc_uplink_task is created */ master_control_init(); xTaskCreatePinnedToCore(comms_task, "master_comms", 4096, NULL, 10, NULL, 0); diff --git a/src/Master/ttc.c b/src/Master/ttc.c index 49ce6bf..8ea8de5 100644 --- a/src/Master/ttc.c +++ b/src/Master/ttc.c @@ -2,8 +2,6 @@ #include -/* Wire format constants (SED "Downlink (UDP)" proposal). Needed on host too, - * since ttc_pack_downlink() is pure byte-packing and is built there. */ #define TTC_DL_HDR_LEN 4 #define TTC_DL_FRAME_LEN 11 @@ -24,10 +22,10 @@ size_t ttc_pack_downlink(uint8_t *pkt, size_t pkt_len, uint8_t seq, const can_fr return TTC_DL_HDR_LEN + TTC_DL_FRAME_LEN; } -/* Everything below owns a task and sockets, so it's ESP-IDF-only - guarded - * the same way as control.c, so the host node_Master target (linked against - * HAL/sim/) builds this file down to just ttc_pack_downlink() above, - * host-testable like any other pure helper. */ +/* Everything below owns a task and sockets, so it's ESP-IDF-only + * This is very close to what the old ttc_uplink_task() did, but now it's a separate file + * I'm very close to putting this in the HAL layer (TODO?) +*/ #ifdef ESP_PLATFORM #include @@ -45,7 +43,6 @@ static const char *TAG = "master_ttc"; #define RX_BUF_LEN 64 -/* Wire format constants (SED "Uplink (TCP)" proposal - see ttc_ground.py). */ #define TTC_UL_SYNC 0xA5 #define TTC_UL_HDR_LEN 4 #define TTC_UL_MAX_PAYLOAD 8 @@ -82,16 +79,9 @@ static void send_ack(int sock, uint8_t cmd, uint8_t flags, uint8_t status, send_all(sock, ack, TTC_ACK_HDR_LEN + len); } -/* No general CAN forwarding yet - see ttc.h. Two commands are handled for - * real so far: - * - TTC_TEST_PING (payload echoed back) so a round-trip time can be - * measured with nothing else in the loop. - * - CAN_CMD_FLIGHT_PHASE: the first command actually gated through the - * flight manager (control.c) before anything happens, rather than - * just being logged and ACKed. This is the pattern every other - * command will eventually follow. - * Everything else is still just logged and ACKed OK, which is enough to - * prove ground's command reached the board. */ +/* No general CAN forwarding yet - see ttc.h + * This is just a proof of concept for the EAR demo, full TT&C still need to be tested + */ static void handle_command(int sock, uint8_t cmd, uint8_t flags, const uint8_t *p, uint8_t len) { if ((flags & TTC_UL_FLAG_TEST) && cmd == TTC_TEST_PING) { send_ack(sock, cmd, flags, TTC_ACK_OK, p, len); diff --git a/src/Master/ttc.h b/src/Master/ttc.h index ac5da68..eed7278 100644 --- a/src/Master/ttc.h +++ b/src/Master/ttc.h @@ -1,15 +1,14 @@ /** * @file ttc.h * @brief Master's TT&C link handling: bare minimum to prove Master and a - * ground script can talk. Deliberately simple - no queues, no CAN + * ground script can talk. Deliberately simple: no queues, no CAN * forwarding, no flight-manager gating yet: * - ttc_pack_downlink() is a pure helper comms_task uses to mirror - * the CAN heartbeat it already sends, over UDP. + * the CAN heartbeat it already sends, over UDP * - ttc_uplink_task() is a TCP listener that logs and ACKs - * whatever ground sends. It doesn't forward anything to CAN. + * whatever ground sends. It doesn't forward anything to CAN... :3 * - * Lives here (not libs/) because it owns a task and sockets - that's - * active behaviour specific to Master, not a reusable pure utility. + * Lives here (not libs/) because it owns a task and sockets, and isn't just a helper function */ #ifndef HELIA_MASTER_TTC_H #define HELIA_MASTER_TTC_H @@ -23,12 +22,13 @@ extern "C" { #endif /** @brief Exact size of a packed downlink packet (4-byte header + 11-byte - * frame). Size the buffer passed to ttc_pack_downlink() with this. */ + * frame). + * Size the buffer passed to ttc_pack_downlink() with this */ #define TTC_DL_PACKET_LEN 15u /** * @brief Pack one CAN frame into the SED UDP downlink wire format - * (header + 11-byte frame). Pure byte-packing, no I/O. + * (header + 11-byte frame). Pure byte-packing, no I/O * @return Bytes written to @p pkt (TTC_DL_PACKET_LEN), or 0 if @p pkt_len * is too small. */ @@ -37,7 +37,7 @@ size_t ttc_pack_downlink(uint8_t *pkt, size_t pkt_len, uint8_t seq, const can_fr /** * @brief TCP uplink listener. Accepts one ground connection at a time, * logs and ACKs each command it parses. Pinned to core 0 by - * setup.c, alongside comms_task. + * setup.c, alongside comms_task */ void ttc_uplink_task(void *arg);