一、项目背景
SHT43 DemoBoard 是一块 Sensirion 的温湿度评估板,上面跑一颗 STM32WB55 做 BLE 通信, 把 SHT43 传感器读数通过 两条路暴露出去。
板卡目标芯片:STM32WB55(Cortex-M4 @ 128MHz + Cortex-M0,dual-core,CubeWPA BLE 协议栈) 主机:Ubuntu 24.04 x86_64,无 sudo 权限本文档记录了从 0 到 1 在受限主机上把整个 BLE 固件编译通过的完整路径。
BLE 广播(Manufacturer Specific Data,AD type 0xFF)—— 不连也能空口监听
BLE GATT(Temperature/Humidity 服务)—— 需连接,读 float32 特征值
固件是 C + C++ 混合,用 CMake + Ninja 构建,依赖 STM32 HAL(CubeWB)+ ST 官方 BLE 协议栈 (CubeWPA,已内置在仓库 lib/ 目录,不需要单独下载)。
仓库里没有提供现成的编译环境,README 默认你已装好 arm-none-eabi-gcc、CMake、Ninja, 或直接用他们给的 docker 镜像。本文档走主机本地、免 root 的路线。

二、编译产物
编译成功后在 build/release/ 下生成三个文件:
文件用途
| SHT43_DB-<ver>.elf | 带符号,给 GDB / 调试器用 |
| SHT43_DB-<ver>.bin | 裸二进制,给 OpenOCD / ST-Link 烧录用(CM4 核,0x08000000) |
| SHT43_DB-<ver>.hex | Intel HEX,给一些烧录工具用 |
实测占用:FLASH 13.8%(68 KB / 484 KB),RAM1 7.3%。
三、编译环境
使用Hermes主理的主机没有 sudo,所有工具都走 apt-get download + dpkg-deb -x 解包到用户目录的路线, 最终集中在 /home/genvex/.armtool/ 下,由一个 env.sh 统一导出。
3.1 目录布局
/home/genvex/.armtool/ ├── env.sh # 一键 source, 导出 PATH/LD_LIBRARY_PATH/CPATH ├── staging/ # apt 包解包后的统一根 (usr/bin, usr/lib, usr/include) │ ├── usr/bin/arm-none-eabi-* │ ├── usr/lib/... │ └── usr/include/newlib/ # string.h/ctype.h 等 target 头 (来自 libnewlib-dev) └── cmake-official/ # Kitware 官方 cmake (完整 Modules, 不是 apt 裁剪版)
3.2 需要哪些包
包作用大小
| gcc-arm-none-eabi | ARM GCC 编译器(13.2) | 57 MB |
| binutils-arm-none-eabi | ld / objcopy / ar | 3 MB |
| libnewlib-arm-none-eabi | newlib C 运行时库 | 49 MB |
| libstdc++-arm-none-eabi-newlib | C++ 标准库(newlib 版) | 463 MB |
| libnewlib-dev | newlib 头文件(string.h 等) | 140 KB |
| cmake(apt 版,其实没用) | — | 11 MB |
| ninja-build | 构建工具 | 129 KB |
3.3 安装步骤(可复现)
# 1. 拉包 (清华源已配好) mkdir -p ~/armtool/armtool && cd ~/armtool/armtool DEBIAN_FRONTEND=noninteractive apt-get download \ gcc-arm-none-eabi binutils-arm-none-eabi \ libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib \ libnewlib-dev ninja-build # 2. 解包到统一 staging mkdir -p /home/genvex/.armtool/staging for d in *.deb; do dpkg-deb -x "$d" /home/genvex/.armtool/staging; done # 3. 补 cmake 运行依赖 (librhash / libjsoncpp) DEBIAN_FRONTEND=noninteractive apt-get download librhash0 libjsoncpp25 for d in librhash0*.deb libjsoncpp25*.deb; do dpkg-deb -x "$d" /home/genvex/.armtool/staging done
3.4 cmake 必须换官方 prebuilt
apt 的 cmake_3.28.3-1build7_amd64.deb 是个裁剪版——只装 debtoolchainfilegen, 没有真正的 cmake 二进制 + Modules/ 目录。cmake --version 能跑但一执行项目配置就报Could not find CMAKE_ROOT。
解决:用 Kitware 官方 release 包(走 gh-proxy,本机直连 GitHub 会被拦):
cd /home/genvex/.hermes/cache/scratch # 任意临时目录 curl -fL -C - -o cmake_official.tar.gz \ "https://gh-proxy.com/https://github.com/Kitware/CMake/releases/download/v3.28.3/cmake-3.28.3-linux-x86_64.tar.gz" tar xzf cmake_official.tar.gz -C /home/genvex/.armtool/cmake-official --strip-components=1 # 验证 /home/genvex/.armtool/cmake-official/bin/cmake --version # 应显示 3.28.3 + 无 CMAKE_ROOT 报错
3.5 一键环境脚本 env.sh
# /home/genvex/.armtool/env.sh export ARMTOOL=/home/genvex/.armtool/staging export PATH="$ARMTOOL/usr/bin:$PATH" export LD_LIBRARY_PATH="$ARMTOOL/usr/lib/x86_64-linux-gnu:$ARMTOOL/usr/lib:$LD_LIBRARY_PATH" # 官方 cmake 优先 (PATH 顺序靠后但放前面, 压过 apt 的裁剪版) export PATH="/home/genvex/.armtool/cmake-official/bin:$PATH" # newlib 标准头 (string.h/ctype.h/...) 喂给交叉 gcc export CPATH="$ARMTOOL/usr/include/newlib:$CPATH"
四、本地适应编译改造
坑 #1:没有 ARM 工具链,没有 sudo
→ 全走 apt-get download + dpkg-deb -x 解包到用户目录。见 3.3。
坑 #2:apt 的 cmake 是裁剪版,缺 Modules
→ 换 Kitware 官方 prebuilt cmake。见 3.4。
坑 #3:newlib 头文件 + multi-arch 路径错位(最隐蔽)
解完包后编译直接报 fatal error: string.h: No such file or directory。 两层原因:
a. 头文件缺失。 libnewlib-arm-none-eabi 只带 .a 库,不带 .h。 newlib 头文件在单独的 libnewlib-dev 包里(路径 usr/include/newlib/)。 要额外 apt-get download libnewlib-dev 并解包,然后用 CPATH 指给它(见 env.sh)。
b. gcc 的 multi-arch 目录 ≠ newlib 的 multi-arch 目录。
arm-none-eabi-gcc -print-multi-directory 对 cortex-m4 + hard-float 期望的是thumb/v7e-m+fp/hard
但 newlib 实际把库放在 arm-none-eabi/newlib/thumb/v8-m.main+fp/hard/
两套子目录布局不一致,导致链接阶段 ld 找不到裸的 crt0.o 和 -lc_nano。 症状:ld: cannot find crt0.o / cannot find -lc_nano。
解法:把 newlib 的 hard-float 库软链进 gcc 期望的 multi-dir:
GD=/home/genvex/.armtool/staging/usr/lib/gcc/arm-none-eabi/13.2.1/thumb/v7e-m+fp/hard ND=/home/genvex/.armtool/staging/usr/lib/arm-none-eabi/newlib/thumb/v8-m.main+fp/hard for f in crt0.o libc_nano.a libc.a libm.a libnosys.a libstdc++_nano.a; do [ -f "$ND/$f" ] && ln -sf "$ND/$f" "$GD/$f" done
冒烟验证(hello world 交叉编译,bare-metal 下 undefined _exit/_isatty 是良性的):
echo 'int main(){return 0;}' > /tmp/h.c
arm-none-eabi-gcc -mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 \
-specs=/home/genvex/.armtool/staging/usr/lib/arm-none-eabi/newlib/nano.specs \
/tmp/h.c -o /tmp/h坑 #4:_sbrk 重复定义,链接失败
工程在 st_app/sysmem.c 里强定义了自己的 _sbrk(heap 分配器), 而 newlib 的 libnosys.a 里也有一个弱定义。默认 CMakeLists 链接行带 -lnosys, 于是两个定义都进链接,报 multiple definition of _sbrk。
解法:把 CMakeLists 里的 -lnosys 去掉(让工程自己的 sysmem.c 提供 _sbrk)。 见下节 5.2。
五、项目本身需要的两处 CMakeLists 改动
仓库 CMakeLists.txt 有两处默认值在"非 root + 自定义 staging"环境下不适用, 必须改(已改好并保留在仓库里):
5.1 -specs=nano.specs → 绝对路径
原来写的是相对引用 -specs=nano.specs,gcc 在自己的默认搜索路径找不到。 改成指向 staging 里 newlib 的全路径:
# CMakeLists.txt (linker options) -specs=/home/genvex/.armtool/staging/usr/lib/arm-none-eabi/newlib/nano.specs
5.2 去掉 -lnosys
# 原来: -lc -lm -lnosys # 改为: -lc -lm # 原因: st_app/sysmem.c 提供 _sbrk, 无需 libnosys; 且 libnosys.a 的弱 _sbrk 会撞 -lc -lm
六、编译方法
cd /home/genvex/sht43-demoboard-ble-firmware source /home/genvex/.armtool/env.sh # configure (README 推荐配方) cmake -B build/release \ -DCMAKE_MAKE_PROGRAM=ninja \ -DCMAKE_BUILD_TYPE=release \ -G Ninja # 全量编译 cmake --build build/release
成功输出(关键几行):
[109/109] Linking C executable SHT43_DB-1_0_1-develop.elf Memory region Used Size Region Size %age Used FLASH: 68479 B 484 KB 13.82% RAM1: 14312 B 196600 B 7.28% BUILD rc=0
增量 / 干净重编
# 增量: 只重编改动的文件 cmake --build build/release # 改过 CMakeLists 后 (链接行有缓存): 删 build 目录重来 rm -rf build/release cmake -B build/release -DCMAKE_MAKE_PROGRAM=ninja -DCMAKE_BUILD_TYPE=release -G Ninja cmake --build build/release
debug 配置
cmake -B build/debug -DCMAKE_MAKE_PROGRAM=ninja -DCMAKE_BUILD_TYPE=debug -G Ninja cmake --build build/debug
七、烧录
固件要同时烧两个核(STM32WB55 是双核,CM4 跑应用,CM0 跑 radio 协处理器)。 本仓库构建只产出 CM4 主核 的 .bin。本次只是理论性探讨,还没找到烧录的入口。

用 OpenOCD + ST-Link(CM4 @ 0x08000000)
openocd -f interface/stlink.cfg -f target/stm32wb55.cfg \ -c "program build/release/SHT43_DB-1_0_1-develop.bin 0x08000000 verify reset exit"
用 ST 官方 STM32_Programmer_CLI
STM32_Programmer_CLI -c "connect" -w build/release/SHT43_DB-1_0_1-develop.bin 0x08000000
用 Sensirion 官方 docker (难度也不一定小)
# 见仓库 README "Local build using docker" 一节 docker pull sensirion/sht43-demoboard-ble-firmware-build:latest # docker 里工具链全齐, 直接一条命令出 bin
八、为什么选 CMake+Ninja 而不是别的
仓库只支持 CMake 构建(没有 Makefile、没有 PlatformIO)。 Ninja 比 make 快,且 README 明确推荐 -G Ninja。 STM32 的 CubeWPA 协议栈是预编译好的 .a 大库(lib/ 目录里), 所以编译时间主要花在工程自身的 ~60 个 .c 文件上,整体 1-2 分钟。
参考
固件仓:github.com/Sensirion/sht43-demoboard-ble-firmware
硬件仓(可选):github.com/Sensirion/sht43-demoboard-hardware
官方构建文档:仓库 README.md § "Building the application"
官方 docker:仓库 README.md § "Local build using docker"
我要赚赏金
