README differences + flashing sections; universal release script

- README: add 'What's different from OpenGD77' (incl. the new SK2 mute) and a
  dedicated 'Flashing' section (DFU steps, per-model flash.sh, donor note).
- scripts/forgejo-release.sh: build/attach a single platform or all, reuse an
  existing release, replace same-named assets, and warn if freetrxVersion.h
  doesn't match the tag.
- TOOLS.md: document the platform build argument.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marcus Kida 2026-07-07 08:11:43 +02:00
parent a838f9636c
commit e50993b98f
4 changed files with 116 additions and 25 deletions

View file

@ -10,6 +10,10 @@ single running list until the first versioned release.
- **DM-1701 build support** — the Docker build is now parameterized by platform; - **DM-1701 build support** — the Docker build is now parameterized by platform;
`./build-firmware.sh DM1701` produces `OpenDM1701.bin` (Baofeng DM-1701 / `./build-firmware.sh DM1701` produces `OpenDM1701.bin` (Baofeng DM-1701 /
Retevis RT-84). Not officially tested yet. Retevis RT-84). Not officially tested yet.
- **Release script** (`scripts/forgejo-release.sh`) now builds and attaches a
single platform or all of them, and reuses an existing release.
- README: added "What's different from OpenGD77" and a dedicated "Flashing"
section.
## 0.1.0 ## 0.1.0

View file

@ -15,6 +15,29 @@ icons, and more). See [`CHANGELOG.md`](CHANGELOG.md) for the full list.
| --- | --- | | --- | --- |
| ![DMR call with the block S-meter](docs/images/screenshot-dmr.png) | ![VFO with the 7-segment frequency font](docs/images/screenshot-vfo.png) | | ![DMR call with the block S-meter](docs/images/screenshot-dmr.png) | ![VFO with the 7-segment frequency font](docs/images/screenshot-vfo.png) |
## What's different from OpenGD77
FreeTRX keeps OpenGD77's core (DMR/FM, hotspot, CPS compatibility) and adds:
- **Audio mute — new SK2 short-tap.** A short tap of **SK2** on the VFO/Channel
screen toggles the speaker mute on and off. Turning the **volume knob fully
down** counts as muted too. While muted, a bold **"M"** appears in the status
bar (left of the battery) and the **red LED flashes** (0.5 s every 2 s). SK2's
existing roles are unchanged: held as a modifier, extra-long-press for
monitor/squelch-off, and — while a DMR QSO is on screen — a tap still sets the
TX talkgroup to the incoming one.
- **Segmented block S-meter** with one block per S-level (S1S9 green, S9+ red)
and a scale label row (S1 S3 S5 S7 S9 / +20 +40 +60).
- **7-segment "LED" frequency font** with faintly-lit inactive segments, on the
VFO and the SK1-held frequency view.
- **Screen dimming** — optionally dim the backlight to 100 / 50 / 30 % after
inactivity (Auto backlight mode); new setting in Display Options.
- **Main-menu icons** next to each top-level entry.
- **DMR reliability fix** — resolves a hard-fault (freeze needing a battery pull)
on the DMR path that affected recent self-built OpenGD77 revisions.
See [`CHANGELOG.md`](CHANGELOG.md) for the complete list.
## Supported radios ## Supported radios
This first iteration is developed for and **tested only on the TYT MD-UV380 and This first iteration is developed for and **tested only on the TYT MD-UV380 and
@ -40,10 +63,35 @@ As with OpenGD77, the source does **not** contain the AMBE codec required for DM
audio. It is merged in from an official manufacturer firmware (V26.45) at flash audio. It is merged in from an official manufacturer firmware (V26.45) at flash
time by the firmware loader. See [`TOOLS.md`](TOOLS.md). time by the firmware loader. See [`TOOLS.md`](TOOLS.md).
## Building, flashing & tools ## Flashing
See [`TOOLS.md`](TOOLS.md) for building (Docker + Arm GNU Toolchain 14), flashing Pre-built firmware is attached to each
(`./flash.sh`), grabbing screenshots, and building language files. [release](https://git.dk1da.de/DK1DA/FreeTRX/releases): `OpenMDUV380.bin` for the
MD-UV380 / RT-3S, `OpenDM1701.bin` for the DM-1701 / RT-84. (Or build your own —
see below.)
1. **Enter firmware-update (DFU) mode:** power the radio **off**, then power it
**on while holding PTT + SK1** (the button above PTT). The screen stays blank
and the top LED glows steadily. Connect the USB cable.
2. **Flash** with the helper (it applies the DMR codec donor and, optionally, a
secondary language):
```bash
./flash.sh -m MD-UV380 -f MDUV380_firmware/build/OpenMDUV380.bin
# DM-1701 / RT-84:
./flash.sh -m DM-1701 -f MDUV380_firmware/build/OpenDM1701.bin
```
**DMR note:** the firmware image is FM-only until the **AMBE codec donor** (the
official V26.45 manufacturer firmware) is merged in at flash time. Register it
once with `./flash.sh -s /path/to/official_V26.45.bin`; the path is then
remembered. Full options, the DFU device list and the underlying loader are
documented in [`TOOLS.md`](TOOLS.md).
## Building & tools
See [`TOOLS.md`](TOOLS.md) for building in Docker
(`./build-firmware.sh [MDUV380|DM1701]`, Arm GNU Toolchain 14), grabbing
screenshots, and building language files.
## License ## License

View file

@ -26,11 +26,13 @@ the official OpenGD77 firmware uses. (Ubuntu's stock GCC 10 miscompiles the DMR
path; see the git history.) path; see the git history.)
```bash ```bash
./build-firmware.sh ./build-firmware.sh # MD-UV380 / RT-3S -> build/OpenMDUV380.bin (default)
./build-firmware.sh DM1701 # DM-1701 / RT-84 -> build/OpenDM1701.bin
``` ```
Output: `MDUV380_firmware/build/OpenMDUV380.bin` (the flashable, FM-only image; the DMR Output: `MDUV380_firmware/build/Open<platform>.bin` (the flashable, FM-only image; the DMR
codec is merged in at flash time — see below). The build is always clean (`rm -rf build`). codec is merged in at flash time — see below). The build is always clean (`rm -rf build`),
so building one platform overwrites the other's output.
--- ---

View file

@ -1,37 +1,74 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# #
# Create a Forgejo release for a tag and attach the built firmware binary. # Build and publish a FreeTRX release on Forgejo, attaching the firmware binaries.
# #
# Requires a Forgejo API token in the FORGEJO_TOKEN env var # Requires a Forgejo API token in FORGEJO_TOKEN
# (Forgejo -> Settings -> Applications -> Generate New Token, scope: write:repository). # (Forgejo -> Settings -> Applications -> Generate New Token, scope: write:repository).
# The release notes are taken from the matching "## <version>" section of CHANGELOG.md. # Release notes are taken from the matching "## <version>" section of CHANGELOG.md.
# If the release already exists it is reused; assets of the same name are replaced.
# #
# Usage: FORGEJO_TOKEN=xxxxx scripts/forgejo-release.sh v0.1.0 # Usage: FORGEJO_TOKEN=xxx scripts/forgejo-release.sh <tag> [platform ...]
# platform: MDUV380 | DM1701 | all (default: all)
# Examples:
# scripts/forgejo-release.sh v0.1.0 # build + attach both binaries
# scripts/forgejo-release.sh v0.1.0 MDUV380 # just the MD-UV380 binary
# #
set -euo pipefail set -euo pipefail
TAG="${1:?usage: FORGEJO_TOKEN=... $0 <tag, e.g. v0.1.0>}" TAG="${1:?usage: FORGEJO_TOKEN=... $0 <tag> [MDUV380|DM1701|all]}"; shift || true
VER="${TAG#v}" VER="${TAG#v}"
API="https://git.dk1da.de/api/v1/repos/DK1DA/FreeTRX" API="https://git.dk1da.de/api/v1/repos/DK1DA/FreeTRX"
BIN="MDUV380_firmware/build/OpenMDUV380.bin"
: "${FORGEJO_TOKEN:?set FORGEJO_TOKEN (Forgejo -> Settings -> Applications, scope write:repository)}" : "${FORGEJO_TOKEN:?set FORGEJO_TOKEN (Forgejo -> Settings -> Applications, scope write:repository)}"
cd "$(dirname "${BASH_SOURCE[0]}")/.." cd "$(dirname "${BASH_SOURCE[0]}")/.."
[ -f "$BIN" ] || { echo "Missing $BIN - run ./build-firmware.sh first"; exit 1; }
# Release body = the CHANGELOG section for this version. # Resolve requested platforms (default / "all" -> both).
PLATFORMS=("$@")
if [ ${#PLATFORMS[@]} -eq 0 ] || [ "${PLATFORMS[0]}" = "all" ]; then
PLATFORMS=(MDUV380 DM1701)
fi
bin_for() { case "$1" in MDUV380) echo OpenMDUV380 ;; DM1701) echo OpenDM1701 ;; *) return 1 ;; esac; }
# Sanity check: the on-screen version should match the tag.
FW_VER="$(grep -oE '"[0-9]+\.[0-9]+\.[0-9]+"' MDUV380_firmware/application/include/freetrxVersion.h | head -1 | tr -d '"')"
[ "$FW_VER" = "$VER" ] || echo "WARNING: freetrxVersion.h is $FW_VER but tag is $VER — bump it before releasing?"
# Build each platform and stage its binary (clean builds overwrite build/).
STAGE="$(mktemp -d)"; trap 'rm -rf "$STAGE"' EXIT
for P in "${PLATFORMS[@]}"; do
OUT="$(bin_for "$P")" || { echo "Unknown platform '$P' (use MDUV380, DM1701 or all)"; exit 1; }
echo "==> Building $P"
./build-firmware.sh "$P" >/dev/null
cp "MDUV380_firmware/build/${OUT}.bin" "$STAGE/${OUT}.bin"
done
# Release notes = the CHANGELOG section for this version.
NOTES="$(awk -v v="## ${VER}" '$0==v{f=1} f&&/^## /&&$0!=v{exit} f{print}' CHANGELOG.md)" NOTES="$(awk -v v="## ${VER}" '$0==v{f=1} f&&/^## /&&$0!=v{exit} f{print}' CHANGELOG.md)"
[ -n "$NOTES" ] || NOTES="FreeTRX ${TAG}" [ -n "$NOTES" ] || NOTES="FreeTRX ${TAG}"
# Create the release (JSON body built safely via python). # Create the release, or reuse it if it already exists.
ID="$(curl -sS "$API/releases/tags/$TAG" -H "Authorization: token $FORGEJO_TOKEN" | python3 -c '
import json,sys
try:
d=json.load(sys.stdin); print(d.get("id","") if isinstance(d,dict) else "")
except Exception:
print("")')"
if [ -z "$ID" ]; then
BODY="$(python3 -c 'import json,sys; print(json.dumps({"tag_name":sys.argv[1],"name":"FreeTRX "+sys.argv[1],"body":sys.argv[2],"draft":False,"prerelease":False}))' "$TAG" "$NOTES")" BODY="$(python3 -c 'import json,sys; print(json.dumps({"tag_name":sys.argv[1],"name":"FreeTRX "+sys.argv[1],"body":sys.argv[2],"draft":False,"prerelease":False}))' "$TAG" "$NOTES")"
RESP="$(curl -fsS -X POST "$API/releases" -H "Authorization: token $FORGEJO_TOKEN" -H "Content-Type: application/json" -d "$BODY")" ID="$(curl -fsS -X POST "$API/releases" -H "Authorization: token $FORGEJO_TOKEN" -H "Content-Type: application/json" -d "$BODY" | python3 -c 'import json,sys;print(json.load(sys.stdin)["id"])')"
ID="$(printf '%s' "$RESP" | python3 -c 'import json,sys; print(json.load(sys.stdin).get("id",""))')" echo "==> Created release $TAG (id=$ID)"
[ -n "$ID" ] || { echo "Failed to create release:"; echo "$RESP"; exit 1; } else
echo "Created release $TAG (id=$ID)" echo "==> Reusing existing release $TAG (id=$ID)"
fi
# Attach the firmware binary. # Attach each staged binary, replacing an existing asset of the same name.
curl -fsS -X POST "$API/releases/$ID/assets?name=OpenMDUV380.bin" \ ASSETS="$(curl -fsS "$API/releases/$ID/assets" -H "Authorization: token $FORGEJO_TOKEN")"
-H "Authorization: token $FORGEJO_TOKEN" -F "attachment=@$BIN" >/dev/null for f in "$STAGE"/*.bin; do
echo "Attached $BIN" name="$(basename "$f")"
echo "Done -> https://git.dk1da.de/DK1DA/FreeTRX/releases/tag/$TAG" aid="$(printf '%s' "$ASSETS" | python3 -c 'import json,sys; n=sys.argv[1]; print(next((str(a["id"]) for a in json.load(sys.stdin) if a.get("name")==n),""))' "$name")"
[ -z "$aid" ] || curl -fsS -X DELETE "$API/releases/$ID/assets/$aid" -H "Authorization: token $FORGEJO_TOKEN" >/dev/null
curl -fsS -X POST "$API/releases/$ID/assets?name=$name" -H "Authorization: token $FORGEJO_TOKEN" -F "attachment=@$f" >/dev/null
echo "==> Attached $name"
done
echo "==> Done -> https://git.dk1da.de/DK1DA/FreeTRX/releases/tag/$TAG"