sq2ips/m20-custom-firmware
GitHub: sq2ips/m20-custom-firmware
为Meteomodem M20无线电探空仪提供开源自定义固件,支持GPS、遥测发射与多协议通信,解决业余HAB设备在极端环境下的数据回传与定位需求。
Stars: 20 | Forks: 3
# m20-custom-firmware
这个项目的目标是反向工程 [Meteomodem M20](https://www.meteomodem.com/m20) 无线电探空仪,并为它在气象气球的业余无线电应用中使用构建自定义的自由开源固件。
# 代码
代码使用 C 语言编写,基于 STM32CubeMX(不要与 STM32CubeIDE 混淆)生成的项目以及底层(LL)库,使用 arm-none-eabi 工具链(使用版本 15.1.0)编译。现在它适配于原来的 STM32L051R6T6 芯片。
编译和烧录说明位于本文件更后面的部分。
# 阶段
在此阶段,代码运行良好并在多次飞行中测试过。感谢 SP9AOB 和 SP6MPL 进行测试飞行。它已被用作一些 HAB 任务和浮空气球的追踪器。我也与 SP2ZIE 业余无线电俱乐部一起进行了几次飞行。
尽管如此,其中仍可能存在错误和漏洞,它以“原样”分发,不提供任何担保,请参见[许可证](#LICENSE)。
如果你使用此固件进行飞行,请告知我,这真的有助于发现错误和测试代码。
# 功能实现
- GPS(NMEA)::heavy_check_mark:
- GPS(XM1110)::heavy_check_mark:
- 电台(Horus V2)::heavy_check_mark:
- 电台(Horus V3)::heavy_check_mark:
- 电台(APRS)::heavy_check_mark:
- LPS22 气压计 + 温度传感器::heavy_check_mark:
- 串口(UART)::heavy_check_mark:
- 外部温度传感器::heavy_check_mark:
- 湿度传感器:在 [湿度分支](https://github.com/sq2ips/m20-custom-firmware/tree/humidity) 上(需要校准)
# 功能列表
当前实现的功能包括:
- GPS 时间、位置、海拔、速度(XM1110 上不工作)、上升速率以及卫星数量(NMEA 和 XM1110)
- 将 u-blox 模块模式切换为空中模式,以允许更高海拔的飞行
- 使用 Horus Binary V2/V3 协议和/或 APRS 协议通过电台发送数据
- 获取电池电压
- 获取温度和气压
- 获取外部温度
- 看门狗定时器
- GPS 看门狗(无定位时重启,适用于 GPS 干扰/欺骗区域)
- 发射频率切换
- 额外的 ADC 用于负载 / PV 电压
# 计划工作与代码问题
请参见[当前问题列表](https://github.com/sq2ips/m20-custom-firmware/issues)
# Horus 4FSK 音调间距
由于硬件限制(系统时钟 PLL 设置选项)无法生成可被 9 整除的电台模块时钟频率。这导致无法实现 RS41ng 项目标准化的 270Hz 音调间距。音调间距设置为 244Hz,通过 8MHz 时钟信号获得。该限制与实现 FSK 的方法直接相关,似乎没有无需硬件干预的解决方法。结果是接收站必须设置不同于标准音调间距的参数,否则信噪比会非常低,例如对比:


# 许可证
参见 LICENSE 文件。
# 图像



# 原理图
[joyel24](https://github.com/joyel24/M20-radiosonde-firmware-alt) 完成了出色的 PCB 反向工程,[PDF 链接](https://www.egimoto.com/dwld/17528ed1858138.pdf)(尽管其中存在一些错误)
# 使用的库
- NMEA 解析基于 https://github.com/sztvka/stm32-nmea-gps-hal
- LPS22 实现基于 https://github.com/KitSprout/KSDK/tree/master/firmwareSTM32/KSSTM_Module_LPS22HB/Program/modules
- 电台模块实现基于 https://github.com/adamgreig/wombat
- Horus Binary V2 编码器基于 https://github.com/projecthorus/horusdemodlib/blob/master/src%2Fhorus_l2.c
- Horus Binary V3 编码器基于 Mark VK5QI 的 RS41-nfw 分支:https://github.com/darksidelemm/rs41-nfw/tree/main/fw/fw-files/rs41-nfw
# GPS
存在两种 GPS 模块变种,均已支持。
## 新款 GPS(NMEA)
在较新的 M20 探空仪中使用 u-blox [MAX-M10M](https://content.u-blox.com/sites/default/files/documents/MAX-M10M_DataSheet_UBX-22028884.pdf),采用 NMEA 协议。波特率为 9600。
该模块具有正常的高度限制(12000 米),因此需要进行模式切换以实现平流层飞行(大约 30000 米)。启动后会向模块发送命令以更改其模式。随后高度限制变为 80000 米,以换取较低的(不需要的)最大加速度。

## 旧款 GPS(XM1110)
在较旧的 M20 探空仪中使用 XM1110 GPS 模块。它通过 UART 传输数据,但使用自定义固件仅传输二进制协议数据。波特率为 38400。
该模块似乎没有低于 30000 米的硬高度限制。
由于帧格式特殊,垂直速度尚未实现。

### 数据格式

### 实际数据帧示例
#### 未捕获任何卫星
```
AA AA AA 03 | 01 | 05 5D 4A 7F | 00 00 00 00 | 00 3A 98 | 00 00 | 00 00 | 00 00 | 05 45 DC | 00 | 13 | 12 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 75 6D
offset | 0 | 1 | 5 | 9 | 12 | 14 | 16 | 18 | 21 | 22 | 23 | 24 | 40 | 56
preambule | fix | latitude | longitude | alt | lat dir | lon dir | alt dir | gps time | ? | ? | 12 | satelites s/n ratio | satelites names (numbers) | checksum
```
#### 捕获两颗卫星
```
AA AA AA 03 | 01 | 05 5D 4A 7F | 00 00 00 00 | 00 3A 98 | 00 00 | 00 00 | 00 00 | 05 45 B8 | 00 | 13 | 12 | 28 2A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 12 19 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 9A 80
offset | 0 | 1 | 5 | 9 | 12 | 14 | 16 | 18 | 21 | 22 | 23 | 24 | 40 | 56
preambule | fix | latitude | longitude | alt | lat dir | lon dir | alt dir | gps time | ? | ? | 12 | satelites s/n ratio | satelites names (numbers) | checksum
```
#### 捕获十颗卫星
```
AA AA AA 03 | 03 | 03 40 58 28 | 01 1A FA 13 | 00 1A 31 | FF E6 | FF BC | 00 01 | 07 AE D0 | 01 | 57 | 12 | 11 16 22 28 1C 24 20 20 14 1F 00 00 00 00 00 00 | 04 05 10 12 15 19 1A 1C 1D 1F 00 00 00 00 00 00 | 24 1C
offset | 0 | 1 | 5 | 9 | 12 | 14 | 16 | 18 | 21 | 22 | 23 | 24 | 40 | 56
preambule | fix | latitude | longitude | alt | lat dir | lon dir | alt dir | gps time | ? | ? | 12 | satelites s/n ratio | satelites names (numbers) | checksum
```
## GPS 看门狗
这是一个用于在 GPS 因干扰/欺骗而停止工作时重启模块的功能。https://gpsjam.org/ 主要实现原因是波兰北部存在高水平的干扰。
如果获取初始定位后定位消失,则会在设定时间后重启模块。
# 气压计和温度传感器
使用 LPS22HB 传感器,通过 SPI 接口,提供气压数据以及附加的温度数据。
# 外部温度传感器
使用 NTC 进行外部温度测量,配合附加电阻,原理图如下所示:

# 电池电压读取
电池直接连接到 ADC 引脚之一,不使用电阻分压器。ADC 的参考电压为 3.3V,因此无法测量高于该值的电压。
# 电台
使用的 [ADF7012B](https://www.analog.com/media/en/technical-documentation/data-sheets/ADF7012.pdf) 电台模块。板上没有 TCXO,因此发射频率受温度影响非常不稳定且会漂移。
## Horus Binary V2
[Horus Binary V2](https://github.com/projecthorus/horusdemodlib/wiki/5-Customising-a-Horus-Binary-v2-Packet) 已实现,使用默认自定义格式以便与 [amateur Sondehub](https://amateur.sondehub.org/) 基础设施兼容。
发送的数据(在 [`horus.h`](./m20/Core/Inc/horus.h) 中定义):
| 字节编号 | 数据类型 | 描述 |
|-|-|-|
| 0-1 | uint16 | 负载 ID(0-65535) |
| 2-3 | uint16 | 序列号 |
| 4 | uint8 | 时间(小时) |
| 5 | uint8 | 时间(分钟) |
| 6 | uint8 | 时间(秒) |
| 7-10 | float | 纬度 |
| 11-14 | float | 经度 |
| 15-16 | uint16 | 海拔(米) |
| 17 | uint8 | 速度(公里/小时) |
| 18 | uint8 | 卫星数量 |
| 19 | int8 | 温度(摄氏度)来自 [LPS22HB](#barometer-and-temperature-sensor) 传感器 |
| 20 | uint8 | 电池电压,来自 [电池 ADC](#battery-voltage-reading) |
| 21-22 | int16 | 上升速率(海拔变化速度) |
| 23-24 | int16 | 外部温度,来自 [NTC](#external-temperature-sensor) 传感器 |
| 25 | uint8 | 湿度实现 |
| 26-27 | uint16 | 来自 [LPS22HB](#barometer-and-temperature-sensor) 传感器的气压数据 |
| 28 | uint8 | GPS 看门狗重启计数 |
| 29 | uint8 | 附加 ADC 测量值,参见 [PV / 负载电压 ADC](#pv--payload-voltage-adc) |
| 30-31 | uint16 | CRC16-CCITT 校验和 |
## Horus Binary V3
[Horus Binary V3](https://github.com/xssfox/horusbinaryv3) 是一种为 HAB 飞行设计的新无线电协议。它基于 V2 协议,并额外使用抽象语法符号 1(ASN.1)来描述数据格式。它高度可定制,且不需要像 V2 那样请求负载 ID。
**注意:由于在 V3 中有效载荷呼号直接编码在帧中,因此其长度取决于呼号长度,所以如果不必要,不建议使用后缀。**
数据包中的字段如下:
| 字段名 | 约束 | 描述 |
|-|-|-|
| `payloadCallsign` | 1 到 15 个字符:`a-z`、`A-Z`、`0-9`、`-` | 有效载荷呼号 |
| `sequenceNumber` | 0 - 65535 | 帧序列号 |
| `timeOfDaySeconds` | -1 - 86400 | 自午夜 UTC 以来的时间(秒),由 GPS 模块提供 |
| `latitude` | -9000000 - 9000000 | 当前 GPS 纬度(乘以 100000 的定点数) |
| `longitude` | -18000000..18000000 | 当前 GPS 经度(乘以 100000 的定点数) |
| `altitudeMeters` | -1000 - 50000 | GPS 海拔 |
| `extraSensor`: `type` | 字符串 | 仅作探测器标识符,默认为 `M20` |
| `extraSensor`: `gps` | uint8 | GPS 看门狗重启次数 |
| `velocityHorizontalKilometersPerHour` | 0-512 | 来自 GPS 模块的水平速度(公里/小时) |
| `ascentRateCentimetersPerSecond` | -32767 - 32767 | 上升速率(厘米/秒) |
| `gnssSatellitesVisible` | 0 - 31 | GPS 模块可见卫星数量 |
| `humidityPercentage` | 0 - 100 | 湿度百分比,尚未实现 |
| `pressurehPa` | 0 - 1200 | 来自 [LPS22HB](#barometer-and-temperature-sensor) 传感器的大气压力(百帕) |
| `temperatureCelsius`: `internal` | -1023 - 1023 | 内部温度传感器值(乘以 10),来自 [LPS22HB](#barometer-and-temperature-sensor) 传感器 |
| `temperatureCelsius`: `external` | -1023 - 1023 | 外部温度传感器值(乘以 10),来自 [NTC](#external-temperature-sensor) 传感器 |
| `milliVolts`: `battery` | 0 - 16383 | [电池 ADC](#battery-voltage-reading) 电压(毫伏) |
| `milliVolts`: `solar` | 0 - 16383 | 附加 ADC 测量值(毫伏),参见 [PV / 负载电压 ADC](#pv--payload-voltage-adc) |
部分传感器字段可通过 `config.h` 中的对应使能值启用。
## 关于 Horus Binary
根据 [horusdemodlib 维基](https://github.com/projecthorus/horusdemodlib/wiki):
## APRS
自动分组报告系统(APRS)也常用于气象气球飞行,因为它拥有配合 [amateur Sondehub](https://amateur.sondehub.org/) 基础设施的互联网转发地面站网络。帧从 APRS-IS 收集,由 [sondehub-aprs-gateway](https://github.com/projecthorus/sondehub-aprs-gateway) 解析并发送到地图。
APRS 使用的 AFSK 调制方式实现与 [Trackduino](https://github.com/trackuino/trackuino/blob/1.52/trackuino/afsk.cpp) 类似。
由于无线电模块不支持 AFSK 贝尔 202 音调,因此需要采用其他方法。该方法通过使用直接模式实现,将 TX_DATA 引脚输入一个高频正弦波 PWM 信号,相位根据对应贝尔 202 音调的值递增,从而创建 1 位 DAC。这种方法还允许连续相位变化,这对 AFSK 至关重要。更多实现细节请参见 [`afsk.c`](./m20/Core/Src/afsk.c) 中的注释。基于 AFSK AX.25,使用 APRS 标准进行路由、格式化、位置压缩并添加注释字段。其中插入遥测数据,供 [sondehub-aprs-gateway](https://github.com/projecthorus/sondehub-aprs-gateway) 解析。实现位于 [`aprs.c`](./m20/Core/Src/aprs.c)。
## 关于模式
由于 APRS 调制特性且没有前向纠错,效率较低,且自动接收站点数量远少于 Horus。因此,如果进行飞行,建议使用 Horus 或两者同时使用,而不仅限于 APRS。
# 运行固件
## 所需硬件
- 一个可用的 M20 探空仪 :)
- 一个 ST-Link v2 编程器 USB 狗,如下所示:

- 5 根公对母杜邦线
- 一台 Linux 或 Windows 电脑
## 建议的硬件修改
## 负载电阻
如果你的探空仪使用新款 GPS 模块,额外并联了一个 62 欧姆的电阻在电压转换器输出端,它的作用是抽取约 53mA 的电流并将其转化为热量。我不知道为什么要添加,移除它不会使电源不稳定或类似问题,也许是为了更快排空电池或加热电路板。你可以安全地移除这个电阻以节省电池能量。
这是该电阻:

热成像图:

(图片来自 SP9AOB)
## PV / 负载电压 ADC
PA0 引脚可用于测量额外电压,例如来自其他负载电池或太阳能电池板(PV)。它使用电阻分压器来允许高于 ADC 参考电压 3.3V 的电压。要启用它,请将 `PV_ADC_ENABLE` 设置为 1。
这是连接示意图,绿色为 PA0 引脚,黑色为地(也可使用其他 GND):

R1 和 R2 的值需要在配置中设置(`PV_ADC_R1`、`PV_ADC_R2`)。
例如当 R1 = 1k 且 R2 = 2k 时,最大电压为 4.95V。
在配置中设置数值时,如果可能可以减少数值比例(例如 1000R 和 2000R 可设为 1 和 2)。
~~注意:Horus 电压字段是一个无符号字节,对应 0 到 5V 的电压范围,因此无法表示更高值。
要使此功能与 Horus 解码器配合使用,你需要向 [Horus 自定义字段列表](https://github.com/projecthorus/horusdemodlib/blob/master/custom_field_list.json) 提交一个 PR,将你的呼号添加到 `"SP2ZIE"` 下的 `"other_payloads"` 列表中,例如当表格为 `["SQ2IPS"]` 时,添加 SP0ABC 为:`["SQ2IPS", "SP0ABC"]`。~~
从 Horus Binary V3 开始,以上操作已不再必要。
## 下载代码
首先你需要获取代码,可以使用 `git`:
```
git clone https://github.com/sq2ips/m20-custom-firmware.git
```
也可以从 GitHub 网站点击“code”按钮,或直接从 [这里](https://github.com/sq2ips/m20-custom-firmware/archive/refs/heads/main.zip) 获取,然后解压文件。
# 配置
在编译固件之前,你需要先配置位于 [`config.h`](./m20/Core/Inc/config.h) 文件中的参数。需要修改的最重要参数已加粗。
参数列表:
| 参数 | 类型(单位) | 描述 |
|------||------|
| **`TIME_PERIOD`** | uint(秒) | 帧传输间隔时间。不应低于 4。 |
| `HORUS_ENABLE` | uint | 0 禁用,2 为 Horus Binary V2,3 为 Horus Binary V3。 |
| **`QRG_FSK4`** | float[](Hz) | Horus 4-FSK 发射频率数组,循环切换,可在大括号内添加新频率。请参考 [Horus 维基](https://github.com/projecthorus/horusdemodlib/wiki#commonly-used-frequencies) 获取常用频率。 |
| **`FSK4_POWER`** | uint | 0 到 63 的数值。参见下表。 |
| **`HORUS_V2_PAYLOAD_ID`** | uint16 | Horus Binary V2 帧中传输的负载 ID。为进行飞行,你需要为你的呼号申请一个 ID,更多信息请参见[协议文档](https://github.com/projecthorus/horusdemodlib/wiki#how-do-i-transmit-it)。测试时使用的 ID 为 256。 |
| **`HORUS_V3_PAYLOAD_CALLSIGN`** | string | Horus Binary V3 的负载呼号字符串,长度 1 到 15 个字符:`a-z`、`A-Z`、`0-9`、`-`。 |
| `FSK4_WAIT_TIME` | uint(ms) | 在实际调制之前保持恒定 TX 频率的时间(可能有助于接收机锁定)。 |
| `TX_PAUSE` | uint(ms) | Horus 与 APRS 之间的延迟时间。 |
| `APRS_ENABLE` | bool | 启用 APRS AFSK 传输。 |
| **`QRG_AFSK`** | float[](Hz) | 与 `QRG_4FSK` 类似,欧洲常用 432.500MHz。 |
| **`AFSK_POWER`** | uint | 与 `FSK4_POWER` 类似,默认为 `FSK4_POWER`,可单独设置。 |
| **`APRS_CALLSIGN`** | string | 探空仪呼号,请在此填写你的呼号(最多 6 位数字)。 |
| `APRS_SSID` | uint | 探空仪呼号的 SSID,11 表示“气球、飞行器、航天器等”,请参考 https://www.aprs.org/aprs11/SSIDs.txt。 |
| `APRS_DESTINATION` | string | 目的地地址,用于标识 M20 发射机(最多 6 位数字)。 |
| `APRS_DESTINATION_SSID` | uint | 目的地地址的 SSID。 |
| `APRS_PATH` | string | APRS 路径 1,请参考 https://blog.aprs.fi/2020/02/how-aprs-paths-work.html(最多 6 位数字)。 |
| `APRS_PATH_SSID` | string | APRS 路径 1 的 SSID,注意例如 WIDE2-1 应将 `APRS_PATH` 设为 `WIDE2`,`APRS_PATH_SSID` 设为 `1`。[推荐的气球路径](https://www.aprs.org/balloons.html)。 |
| `APRS_SYMBOL` | string(2 个字符) | 第一个字符为符号表 ID(`/` 或 `\`),第二个字符为符号。`/O` 表示气球,[所有符号](https://www.aprs.org/symbols.html)。要在 Sondehub 上显示,请使用 `/O`。 |
| `APRS_COMMENT_TELEMETRY` | bool | 在 APRS 注释字段中启用遥测。 |
| **`APRS_COMMENT_TEXT`** | string | 注释字段中的附加文本。 |
| `LED_MODE` | uint | 0 - 禁用,1 - 获取数据前 LED 常亮,2 - 定位类型指示(1 次闪表示无定位,2 次闪表示 2D 定位,3 次闪表示 3D 定位)。 |
| `LED_PERIOD` | uint | 仅适用于 `LED_MODE` 为 2 时,定位指示的间隔时间。 |
| `LED_DISABLE_ALT` | uint(米) | 仅适用于 `LED_MODE` 为 2 时,达到指定高度后禁用 LED。 |
| `LED_MODE_2_BLINK_TIME` | uint(ms) | 仅适用于 `LED_MODE` 为 2 时,闪烁持续时间。 |
| `LED_MODE_2_BLINK_PAUSE` | uint(ms) | 仅适用于 `LED_MODE` 为 2 时,闪烁间隔时间。 |
| `RF_BOOST` | bool | 射频发射增强状态,可将信号放大约 15dB。(开启时增强,关闭时衰减;若需降低输出功率,建议减小 `PA_FSK4` 而非关闭此选项。) |
| `ADF_FREQ_CORRECTION` | uint(244Hz 的倍数) | 发射信号频率校正。 |
| `ADF_FSK_DEVIATION` | uint(244Hz 的倍数) | AFSK 调制器的偏移参数,不建议随意更改。 |
| **`GPS_TYPE`** | uint | GPS 模块类型,1 表示 u-blox MAX-M10M,2 表示 XM1110 模块。模块识别方法请参见[GPS 章节](#gps)。 |
| `GPS_WATCHDOG` | bool | 启用 [GPS 看门狗](#gps-watchdog)。 |
| `GPS_WATCHDOG_ARC` | uint | 无定位后触发重启的主循环迭代次数(仅在前一次有定位时有效)。 |
| `AscentRateTime` | uint | 上升速率测量时间。 |
| `LPS22_ENABLE` | bool | 启用 LPS22 传感器。 |
| `NTC_ENABLE` | bool | 启用 NTC 外部温度传感器。 |
| `BAT_ADC_ENABLE` | bool | 启用电池电压测量。 |
| `PV_ADC_ENABLE` | bool | 启用 PA0 引脚的额外 ADC 测量。 |
| `PV_ADC_R1` | uint | 分压电阻 R1 的值。参见 [PV / 负载电压 ADC](#pv--payload-voltage-adc)。 |
| `PV_ADC_R2` | uint | 分压电阻 R2 的值。 |
| `DEBUG` | bool | 通过串口输出调试信息。 |
| `GPS_DEBUG` | bool | 启用 GPS 数据调试输出。 |
| `GPS_RAW_DEBUG` | bool | 启用 GPS 原始帧调试输出。 |
## 电源设置
在 437.600 MHz 处直接测量,`RF_BOOST` 开启时的功率设置:
| 功率设置 | 功率 |
|----------|------|
| 8 | 0.005W |
| 10 | 0.008W |
| 15 | 0.016W |
| 20 | 0.027W |
| 25 | 0.038W |
| 30 | 0.052W |
| 35 | 0.062W |
| 40 | 0.075W |
| 45 | 0.088W |
| 50 | 0.101W |
| 55 | 0.112W |
| 60 | 0.122W |
| 63 | 0.130W |
# 编译固件
在刷写固件之前,你需要先进行编译。根据平台不同,有几种编译方式:
## 在 Linux 上直接编译
要在 Linux 上直接编译,你需要安装 arm-none-eabi 工具链,具体安装方式取决于你的 Linux 发行版。
例如,在 Debian 上可以这样安装:
```
sudo apt install gcc-arm-none-eabi
```
### 注意:在不同发行版中工具链版本可能不同,可能导致生成文件过大
为了避免这个问题,可以从 [这里](https://developer.arm.com/-/media/Files/downloads/gnu/14.2.rel1/binrel/arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi.tar.xz) 下载工具链。
下载后需要解压:
```
tar -xvf arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi.tar.xz
```
然后将二进制文件安装到系统中:
```
sudo cp arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi/bin/* /usr/local/bin/
```
你还需要安装 `make` 工具:
```
sudo apt install make
```
安装完成后,进入下载代码的目录,然后:
```
cd m20
```
现在可以执行 `make` 命令编译固件:
编译成功后,你应该会看到类似如下的内存使用表:

## 使用 Docker 在 Linux 上编译
首先需要安装 Docker,你可以根据你的 Linux 发行版从包管理器安装。
例如,在 Debian 上可以这样安装:
```
sudo apt install docker
```
然后将你的用户添加到 docker 用户组:
```
sudo groupadd docker && sudo usermod -aG docker $USER
```
现在重启:
```
sudo reboot
```
现在需要启动 Docker 守护进程:
```
sudo systemctl start docker
```
但你需要在每次重启电脑后重新启动它,可以设置为开机自启:
```
sudo systemctl enable docker
```
现在你应该进入下载代码的目录,然后:
```
cd m20
```
现在构建 Docker 镜像:
```
docker build -t m20 .
```
它需要一些时间来下载并安装依赖包,完成后运行:
```
docker run --rm -v $(pwd):/opt/m20 m20:latest
```
它会编译代码,完成后你应该看到内存使用表,编译好的二进制文件应该在 `build/` 目录中。
如果后续再次构建,只需要运行最后一条命令即可。
## 使用 MinGW 在 Windows 上编译
首先 [下载 MSYS2](https://www.msys2.org/),然后安装,安装完成后你应该会看到一个终端。
现在安装 `make`:
```
pacman -Sy make
```
确认安装并等待软件包下载安装完成。
TODO
## 使用 Docker 在 Windows 上编译
首先 [下载 Docker Desktop](https://www.docker.com/products/docker-desktop/) 并安装,然后运行 Docker 引擎。
打开 Windows PowerShell 并进入项目目录下的 `m20/`,然后运行:
```
docker build -t m20 .
```
它需要一些时间来下载并安装依赖包,完成后运行:
```
docker run --rm -v .:/opt/m20 m20:latest
```
它会编译代码,完成后你应该看到内存使用表,编译好的二进制文件应该在 `build/` 目录中。
# 刷写固件
## 连接
在刷写固件之前,你需要先将探空仪连接到电脑,使用 ST-Link 编程器。
你可以使用 5 根金针杜邦线完成连接。这是探空仪的引脚定义:

按照该定义连接编程器印在外壳上的引脚。
连接完成后,将编程器插入电脑的 USB 端口,你现在就可以刷写固件了。
你不需要焊接导线,可以直接插入连接器,应该能提供足够的接触以供固件刷写。
## 在 Linux 上刷写
刷写需要 OpenOCD,你可以根据你的 Linux 发行版从包管理器安装。
例如,在 Debian 上可以这样安装:
```
sudo apt install openocd
```
安装完成后,确保你在 `m20` 目录中,然后检查 ST-Link 是否连接,然后可以移除写保护(仅在首次刷写前需要):
```
make protect
```
或者如果你没有 `make` 或想直接运行命令:
```
openocd -s ./openocd/ -f ./openocd/openocd_m20.cfg -c "init; halt; flash protect 0 0 7 off; exit"
```
完成后可以刷写已编译的固件:
```
make flash
```
或者直接
```
openocd -s ./openocd/ -f ./openocd/openocd_m20.cfg -c "program build/m20.elf verify reset exit"
```
完成后,你的探空仪应该可以使用新固件运行。
## 在 Windows 上刷写
首先 [下载 OpenOCD](https://github.com/xpack-dev-tools/openocd-xpack/releases/latest),选择以 `win32-x64.zip` 结尾的文件,然后解压。
进入项目目录下的 `m20/`。确保 ST-Link 已连接,然后移除写保护(仅在首次刷写前需要):
```
\bin\openocd.exe -s openocd -f openocd\openocd_m20.cfg -c "init; halt; flash protect 0 0 7 off; exit"
```
将 `` 替换为解压后的程序路径。
完成后可以刷写已编译的固件:
```
\bin\openocd.exe -s openocd -f openocd\openocd_m20.cfg -c "program build/m20.elf verify reset exit"
```
完成后,你的探空仪应该可以使用新固件运行。
## 在 Windows 上使用 ST-Link 工具刷写
首先 [下载该工具](https://www.st.com/en/development-tools/stsw-link004.html#get-software),解压、安装并运行。连接 ST-Link 并打开编译生成的 `.bin` 文件。然后选择 Target -> Flash & Verify,确保地址设置为 0x80000000 并进行刷写。
# 调试(TODO)
标签:APRS, GPS NMEA, GPS XM1110, Horus协议, STM32, STM32L051, UART, u-blox, 业余无线电, 云资产清单, 固件开发, 客户端加密, 嵌入式系统, 开源固件, 无线电追踪, 气压计, 气象探空仪, 温度传感器, 湿度传感器, 看门狗, 硬件黑客, 裸机开发, 请求拦截, 逆向工程, 项目实战, 高空气球