feat(firmware): add OTA updater library skeleton with version comparison

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-11 06:59:02 -07:00
parent f37e0d6b07
commit 8b1fd10db7
3 changed files with 124 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
// firmware/lib/ota_updater/ota_updater.cpp
#include "ota_updater.h"
#include <stdio.h>
#include <string.h>
// ── version comparison ─────────────────────────────────────────────────────
bool ota_version_newer(const char* current, const char* remote) {
int ca = 0, cb = 0, cc = 0;
int ra = 0, rb = 0, rc = 0;
if (sscanf(current, "%d.%d.%d", &ca, &cb, &cc) != 3) return false;
if (sscanf(remote, "%d.%d.%d", &ra, &rb, &rc) != 3) return false;
if (ra != ca) return ra > ca;
if (rb != cb) return rb > cb;
return rc > cc;
}
// ── signature verification ─────────────────────────────────────────────────
// (real implementation added in Task 7)
bool ota_verify_signature_with_key(const uint8_t hash32[32], const uint8_t sig64[64],
const uint8_t pubkey65[65]) {
(void)hash32; (void)sig64; (void)pubkey65;
return false; // placeholder — filled in Task 7
}
// ── device-only code ───────────────────────────────────────────────────────
#ifndef NATIVE_TEST
#include "ota_pubkey.h"
#include "version.h"
bool ota_verify_signature(const uint8_t hash32[32], const uint8_t sig64[64]) {
return ota_verify_signature_with_key(hash32, sig64, kOtaPublicKey);
}
static const char* s_server_base = nullptr;
static const char* s_device_id = nullptr;
static const char* s_hmac_secret = nullptr;
static uint32_t s_interval_ms = 21600000UL; // 6 h default
static uint32_t s_last_check_ms = 0;
void ota_updater_init(const char* server_base, const char* device_id,
const char* hmac_secret, uint32_t check_interval_ms) {
s_server_base = server_base;
s_device_id = device_id;
s_hmac_secret = hmac_secret;
s_interval_ms = check_interval_ms;
s_last_check_ms = 0; // force first check on next call
}
bool ota_updater_check_and_apply() { return false; } // filled in Task 8
#endif // NATIVE_TEST

View File

@@ -0,0 +1,27 @@
// firmware/lib/ota_updater/ota_updater.h
#pragma once
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
// One-time init. Call from setup() after WiFi is ready.
// server_base: e.g. "http://logs.research.bike:8000"
// check_interval_ms: milliseconds between polls (e.g. 6*3600*1000 = 21600000)
void ota_updater_init(const char* server_base,
const char* device_id,
const char* hmac_secret,
uint32_t check_interval_ms);
// Polls server; downloads, verifies, and flashes if newer version available.
// Returns true if update was applied (device reboots before returning false path).
// Safe to call from any task; blocks during download.
bool ota_updater_check_and_apply();
// Exposed for unit testing — pass an arbitrary 65-byte uncompressed P-256 pubkey.
bool ota_version_newer(const char* current, const char* remote);
bool ota_verify_signature_with_key(const uint8_t hash32[32], const uint8_t sig64[64],
const uint8_t pubkey65[65]);
// Production wrapper — uses the compiled-in kOtaPublicKey from ota_pubkey.h.
// Not callable from native tests (requires ota_pubkey.h / device build).
bool ota_verify_signature(const uint8_t hash32[32], const uint8_t sig64[64]);

View File

@@ -0,0 +1,44 @@
// firmware/test/test_ota/test_version.cpp
#include <unity.h>
// Pull in the function under test — include .cpp directly for native builds
// so we don't need a separate compilation unit per test.
#define NATIVE_TEST
#include "../../lib/ota_updater/ota_updater.cpp"
void setUp() {}
void tearDown() {}
void test_remote_newer_patch() {
TEST_ASSERT_TRUE(ota_version_newer("1.0.0", "1.0.1"));
}
void test_remote_newer_minor() {
TEST_ASSERT_TRUE(ota_version_newer("1.0.9", "1.1.0"));
}
void test_remote_newer_major() {
TEST_ASSERT_TRUE(ota_version_newer("0.9.9", "1.0.0"));
}
void test_same_version() {
TEST_ASSERT_FALSE(ota_version_newer("1.2.3", "1.2.3"));
}
void test_remote_older() {
TEST_ASSERT_FALSE(ota_version_newer("1.2.3", "1.2.2"));
}
void test_malformed_current() {
TEST_ASSERT_FALSE(ota_version_newer("bad", "1.0.0"));
}
void test_malformed_remote() {
TEST_ASSERT_FALSE(ota_version_newer("1.0.0", "bad"));
}
int main() {
UNITY_BEGIN();
RUN_TEST(test_remote_newer_patch);
RUN_TEST(test_remote_newer_minor);
RUN_TEST(test_remote_newer_major);
RUN_TEST(test_same_version);
RUN_TEST(test_remote_older);
RUN_TEST(test_malformed_current);
RUN_TEST(test_malformed_remote);
return UNITY_END();
}