Keep the colleague's microbu-esp32c5 tree in this repository

obu-firmware builds against vanetza-idf from microbu-esp32c5/external, but
that tree was gitignored, so a clone of this repository could not build the
firmware it ships. It is now committed here as ordinary files in its own
folder, microbu-esp32c5/: the colleague's commit cf4b99f plus the V2X2MAP
bridge's signature verification (--trust) used on the bench. Nothing is
fetched from or pushed to the colleague's repository; this repository and
its remotes carry everything. The folder's own .gitignore keeps build output,
downloaded components and private key material out, as it did there; the
committed file set is identical to that repository's tracked files.

The ESP32-C5 is still flashed from obu-firmware/, which only takes
vanetza-idf from microbu-esp32c5/, so the two stay separate folders.
FLASHING.md says how to take a newer version of the colleague's tree (copy
it over the folder, rebuild, test, commit).
This commit is contained in:
Ashin Walpola
2026-09-23 17:46:40 +02:00
parent 2f60623e18
commit 0e9525162d
9881 changed files with 1582523 additions and 17 deletions
+126
View File
@@ -0,0 +1,126 @@
// micrOBU firmware: the ESP32-C5 half of the VRU ITS-S on vanetza-idf.
// One station task owns stack, security entity and radio; the serial reader task only
// enqueues frames. Link messages: implementation/station-link/README.md.
#include "link_protocol.hpp"
#include "link_service.hpp"
#include "board_controls.hpp"
#include "simple_ble.hpp"
#include "serial_link.hpp"
#include "station.hpp"
#if CONFIG_MICROBU_TEST_CHANNEL
#include "test_channel.hpp"
#endif
#include <esp_log.h>
#include <esp_task_wdt.h>
#include <nvs_flash.h>
#include <freertos/FreeRTOS.h>
#include <freertos/queue.h>
#include <freertos/task.h>
#include <cstdio>
#include <new>
namespace {
const char* TAG = "microbu";
/// @brief Represents one frame received from a transport, to be queued for the station task.
struct Incoming {
microbu::LinkTransport transport;
microbu::serial::Frame frame;
};
QueueHandle_t frames = nullptr; // FIFO of Incoming* (8 bytes each); frame bytes never enter the queue itself
/// @brief Attempts to heap-allocate an Incoming so it can be pushed onto the frames queue.
/// @param transport Transport the frame arrived on.
/// @param frame Decoded frame to take ownership of.
/// @return Owning pointer to push onto the queue, or nullptr if allocation failed.
Incoming* try_new_incoming(microbu::LinkTransport transport, microbu::serial::Frame frame) noexcept {
try {
// std::move here steals frame's payload buffer into the heap Incoming (no byte copy);
// it has nothing to do with the queue below, which only ever transports the resulting pointer.
return new Incoming {transport, std::move(frame)};
} catch (const std::bad_alloc&) {
return nullptr;
}
}
/// @brief Dispatches one queued frame to the link service (or the test channel); logs and drops it on failure.
void handle_incoming(microbu::Station& station, microbu::LinkService& link, const Incoming& incoming) {
try {
if (incoming.frame.type == microbu::serial::FrameType::LINK)
link.handle(incoming.frame.payload, incoming.transport);
#if CONFIG_MICROBU_TEST_CHANNEL
else if (incoming.transport == microbu::LinkTransport::serial &&
incoming.frame.type == microbu::serial::FrameType::TEST)
microbu::serial::write(microbu::serial::FrameType::TEST,
microbu::test_channel_execute(station, incoming.frame.payload));
#endif
} catch (const std::bad_alloc&) {
ESP_LOGE(TAG, "out of memory while handling a frame");
} catch (const std::exception& e) {
ESP_LOGE(TAG, "frame handling failed: %s", e.what());
}
}
/// @brief FreeRTOS task body: owns the station, link service and board controls. Drains the incoming queue.
void station_task(void*) {
microbu::Station station;
microbu::LinkService link(station);
microbu::BoardControls controls;
station.on_disseminated([&controls] { controls.blink(); });
station.on_received([&controls] { controls.blink(); });
ESP_LOGI(TAG, "station task ready; the phone/PC configures the station over BLE or USB Serial-JTAG");
for (;;) { /// main loop: drain the incoming queue, tick the station and link service, tick the controls
Incoming* incoming = nullptr;
if (xQueueReceive(frames, &incoming, pdMS_TO_TICKS(5)) == pdTRUE && incoming) {
handle_incoming(station, link, *incoming);
delete incoming;
}
station.tick();
link.tick();
controls.tick();
}
}
}
/// @brief Firmware entry point: initializes NVS, watchdog, serial/BLE transports, and starts the station task.
extern "C" void app_main() {
// NVS: the credential store (and the Wi-Fi driver's calibration data)
esp_err_t nvs = nvs_flash_init();
if (nvs == ESP_ERR_NVS_NO_FREE_PAGES || nvs == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
nvs = nvs_flash_init();
}
ESP_ERROR_CHECK(nvs);
// Signing and verification take tens of milliseconds each on the C5. This keeps the idle
// watchdog from reporting a busy station task (5 s default) while a credential bundle applies.
esp_task_wdt_config_t watchdog = {};
watchdog.timeout_ms = 30000;
watchdog.idle_core_mask = (1 << portNUM_PROCESSORS) - 1;
watchdog.trigger_panic = false; // log, don't reboot, on timeout
esp_task_wdt_reconfigure(&watchdog);
frames = xQueueCreate(32, sizeof(Incoming*));
microbu::serial::start([](microbu::serial::Frame frame) {
auto* copy = try_new_incoming(microbu::LinkTransport::serial, std::move(frame));
if (!copy) return;
// xQueueSend copies the 8-byte pointer value into the queue, not *copy itself.
// station_task then pops pointers FIFO and owns/deletes whatever it receives.
if (xQueueSend(frames, &copy, 0) != pdTRUE) delete copy; // the phone retries on a missing RESULT
});
if (!microbu::ble::start([](microbu::link::Bytes message) {
microbu::serial::Frame frame {microbu::serial::FrameType::LINK, std::move(message)};
auto* copy = try_new_incoming(microbu::LinkTransport::ble, std::move(frame));
if (!copy) return;
if (xQueueSend(frames, &copy, 0) != pdTRUE) delete copy; // same hand-off as the serial callback above
})) {
ESP_LOGE(TAG, "BLE station link failed to start. USB remains available");
}
ESP_LOGI(TAG, "micrOBU firmware on vanetza-idf, link protocol version 1%s",
#if CONFIG_MICROBU_TEST_CHANNEL
", test channel enabled");
#else
"");
#endif
xTaskCreate(station_task, "station", 12288, nullptr, 10, nullptr);
}