From a09b0e5c70f956a9206592170ffa4c8d0d436359 Mon Sep 17 00:00:00 2001 From: Shine Date: Thu, 8 Oct 2026 16:13:18 +0800 Subject: [PATCH 1/5] test(firmware): validate archived Intel HEX checksums and EOF --- scripts/check_hex_snapshot.py | 50 +++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 scripts/check_hex_snapshot.py diff --git a/scripts/check_hex_snapshot.py b/scripts/check_hex_snapshot.py new file mode 100644 index 0000000..d509b3d --- /dev/null +++ b/scripts/check_hex_snapshot.py @@ -0,0 +1,50 @@ +"""Validate Intel HEX framing and checksums of the preserved firmware image. + +This checks file integrity only, not compatibility with actual hardware. +""" +import hashlib +from pathlib import Path + + +def validate_hex(path): + records = 0 + data_bytes = 0 + eof = False + for lineno, raw in enumerate(path.read_text(encoding="ascii").splitlines(), 1): + line = raw.strip() + if not line: + continue + if eof: + raise ValueError("Line {}: trailing records after EOF".format(lineno)) + if not line.startswith(":"): + raise ValueError("Line {}: not an Intel HEX record".format(lineno)) + try: + payload = bytes.fromhex(line[1:]) + except ValueError as exc: + raise ValueError("Line {}: invalid hexadecimal bytes".format(lineno)) from exc + if len(payload) < 5 or len(payload) != payload[0] + 5: + raise ValueError("Line {}: invalid record length".format(lineno)) + if sum(payload) % 256: + raise ValueError("Line {}: checksum mismatch".format(lineno)) + typ = payload[3] + if typ not in (0, 1, 2, 3, 4, 5): + raise ValueError("Line {}: unsupported record type".format(lineno)) + if typ == 0: + data_bytes += payload[0] + if typ == 1: + if payload[0] != 0 or payload[1] != 0 or payload[2] != 0: + raise ValueError("Line {}: malformed EOF".format(lineno)) + eof = True + if typ == 4 and payload[0] != 2: + raise ValueError("Line {}: malformed extended linear address".format(lineno)) + records += 1 + if not eof or not data_bytes: + raise ValueError("No data records or terminal EOF record found") + return records, data_bytes + + +if __name__ == "__main__": + target = Path(__file__).resolve().parents[1] / "firmware/prebuilt/Template.hex" + records, count = validate_hex(target) + print("Intel HEX: {} valid records, {} data bytes".format(records, count)) + print("SHA-256:", hashlib.sha256(target.read_bytes()).hexdigest()) From b2b86d6f3b5313aa1a89f4838b10d0047c2c7fa2 Mon Sep 17 00:00:00 2001 From: Shine Date: Thu, 8 Oct 2026 16:13:27 +0800 Subject: [PATCH 2/5] ci(firmware): verify retained HEX format alongside project paths --- .github/workflows/keil-path-check.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/keil-path-check.yml b/.github/workflows/keil-path-check.yml index 737017b..84a3a5f 100644 --- a/.github/workflows/keil-path-check.yml +++ b/.github/workflows/keil-path-check.yml @@ -15,3 +15,4 @@ jobs: with: python-version: '3.11' - run: python scripts/check_keil_paths.py + - run: python scripts/check_hex_snapshot.py From 12eaa469843c124cf9e62d24cb699b65d31f9daf Mon Sep 17 00:00:00 2001 From: Shine Date: Thu, 8 Oct 2026 16:13:49 +0800 Subject: [PATCH 3/5] docs(firmware): separate artifact checks from hardware validation --- docs/FIRMWARE-VERIFICATION.md | 38 +++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 docs/FIRMWARE-VERIFICATION.md diff --git a/docs/FIRMWARE-VERIFICATION.md b/docs/FIRMWARE-VERIFICATION.md new file mode 100644 index 0000000..7d21d8a --- /dev/null +++ b/docs/FIRMWARE-VERIFICATION.md @@ -0,0 +1,38 @@ +# PulseBand — Firmware verification + +This is an **educational STM32F103 prototype**, not a validated medical or consumer wearable product. A clean repository and a valid Intel HEX file are only initial checks. + +## Source-of-truth + +- Keil µVision project: `firmware/USER/Template.uvprojx` +- Core firmware / peripheral drivers: `firmware/CORE/`, `firmware/FWLIB/`, `firmware/HARDWAR/`, `firmware/SYSTEM/` +- Historical compiled firmware snapshot: `firmware/prebuilt/Template.hex` +- Build output (ignored by Git): `firmware/OBJ/` and `firmware/USER/Listings/` + +## Automated checks + +The repository's pull request workflow runs: + +```shell +python scripts/check_keil_paths.py +python scripts/check_hex_snapshot.py +``` + +The first command checks source/include relative paths referenced by the Keil project. The second verifies Intel HEX record lengths, per-record checksums, end-of-file framing and prints the snapshot's SHA-256 checksum. + +**Passing these checks is not an MCU compilation or functional board test.** The prebuilt HEX snapshot should not be assumed suitable for every STM32F103 board. + +## Build on a development workstation + +1. Install a suitably licensed Keil MDK / µVision toolchain and the compatible STM32F1 device support pack. +2. Open `firmware/USER/Template.uvprojx`. +3. Review chip selection, compiler settings, peripheral pin mappings, clock frequency and flash configuration for the actual hardware revision. +4. Select **Project → Build Target** and review errors/warnings. Rebuild the HEX from current sources if possible. +5. Compare the generated binary and its SHA-256 checksum to the reference only if toolchain, configuration and firmware revision are known to match. +6. Before flashing through ST-Link, confirm supply voltage and pin connections; preserve a backup of the existing firmware where permitted. + +## Hardware acceptance checklist + +Verify sensor communication, display, wireless protocol, alarm logic and power consumption on actual hardware. Document board revision, test equipment, firmware hash and observed results. Claims about SpO2, heart-rate precision, step-count accuracy and battery life require independent reference measurements. + +**No clinical or diagnostic validity is claimed.** From e46b97e228be87827554c9f7a20629a0112c280f Mon Sep 17 00:00:00 2001 From: Shine Date: Thu, 8 Oct 2026 16:14:03 +0800 Subject: [PATCH 4/5] docs: link firmware validation limitations from bilingual READMEs --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 135c182..d4d72ca 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,8 @@ For detailed hardware list and connection instructions, please refer to [hardwar **Build outputs:** Keil writes generated objects to `firmware/OBJ/` and listings to `firmware/USER/Listings/`. These build artifacts are intentionally ignored. A previously committed `Template.hex` snapshot is preserved under [`firmware/prebuilt/`](firmware/prebuilt/); its hardware compatibility has not been independently verified. For a reproducible firmware image, rebuild from source in Keil. +For validation and device-build limitations, see [Firmware verification](docs/FIRMWARE-VERIFICATION.md). The retained `firmware/prebuilt/Template.hex` is a historical snapshot, **not a hardware-tested release**. + ### Mobile APP Usage 1. **Install APP** From 0450aa956d32d47c35a25e27628e0a76d7358759 Mon Sep 17 00:00:00 2001 From: Shine Date: Thu, 8 Oct 2026 16:14:07 +0800 Subject: [PATCH 5/5] docs: link firmware validation limitations from bilingual READMEs --- README_zh-CN.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README_zh-CN.md b/README_zh-CN.md index 5ed9d0d..acc1228 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -94,6 +94,8 @@ **构建产物管理:** Keil 生成的对象文件位于 `firmware/OBJ/`,Listing 文件位于 `firmware/USER/Listings/`,均不再纳入版本控制。原仓库中的 `Template.hex` 快照已保存在 [`firmware/prebuilt/`](firmware/prebuilt/),**尚未对其硬件兼容性进行独立验证**;建议使用 Keil 从源码重新编译固件。 +固件路径、HEX 快照完整性及实物测试说明详见 [固件验证文档](docs/FIRMWARE-VERIFICATION.md)。`firmware/prebuilt/Template.hex` 仅为历史快照,**不代表已验证可用于所有开发板**。 + ### 手机 APP 使用 1. **安装 APP**