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:
53
firmware/lib/ota_updater/ota_updater.cpp
Normal file
53
firmware/lib/ota_updater/ota_updater.cpp
Normal 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
|
||||
27
firmware/lib/ota_updater/ota_updater.h
Normal file
27
firmware/lib/ota_updater/ota_updater.h
Normal 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]);
|
||||
44
firmware/test/test_ota/test_version.cpp
Normal file
44
firmware/test/test_ota/test_version.cpp
Normal 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();
|
||||
}
|
||||
Reference in New Issue
Block a user