diff --git a/CHANGELOG.md b/CHANGELOG.md index c14475c..9bbfdde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ single running list until the first versioned release. - **DM-1701 build support** — the Docker build is now parameterized by platform; `./build-firmware.sh DM1701` produces `OpenDM1701.bin` (Baofeng DM-1701 / 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 diff --git a/README.md b/README.md index 982f3d3..1653ef5 100644 --- a/README.md +++ b/README.md @@ -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) | +## 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 (S1–S9 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 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 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 -(`./flash.sh`), grabbing screenshots, and building language files. +Pre-built firmware is attached to each +[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 diff --git a/TOOLS.md b/TOOLS.md index d95d9f3..5e3bc6f 100644 --- a/TOOLS.md +++ b/TOOLS.md @@ -26,11 +26,13 @@ the official OpenGD77 firmware uses. (Ubuntu's stock GCC 10 miscompiles the DMR path; see the git history.) ```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 -codec is merged in at flash time — see below). The build is always clean (`rm -rf build`). +Output: `MDUV380_firmware/build/Open.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`), +so building one platform overwrites the other's output. --- diff --git a/scripts/forgejo-release.sh b/scripts/forgejo-release.sh index ea83752..2cd5c39 100644 --- a/scripts/forgejo-release.sh +++ b/scripts/forgejo-release.sh @@ -1,37 +1,74 @@ #!/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). -# The release notes are taken from the matching "## " section of CHANGELOG.md. +# Release notes are taken from the matching "## " 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 [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 -TAG="${1:?usage: FORGEJO_TOKEN=... $0 }" +TAG="${1:?usage: FORGEJO_TOKEN=... $0 [MDUV380|DM1701|all]}"; shift || true VER="${TAG#v}" 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)}" 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)" [ -n "$NOTES" ] || NOTES="FreeTRX ${TAG}" -# Create the release (JSON body built safely via python). -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="$(printf '%s' "$RESP" | python3 -c 'import json,sys; print(json.load(sys.stdin).get("id",""))')" -[ -n "$ID" ] || { echo "Failed to create release:"; echo "$RESP"; exit 1; } -echo "Created release $TAG (id=$ID)" +# 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")" + 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"])')" + echo "==> Created release $TAG (id=$ID)" +else + echo "==> Reusing existing release $TAG (id=$ID)" +fi -# Attach the firmware binary. -curl -fsS -X POST "$API/releases/$ID/assets?name=OpenMDUV380.bin" \ - -H "Authorization: token $FORGEJO_TOKEN" -F "attachment=@$BIN" >/dev/null -echo "Attached $BIN" -echo "Done -> https://git.dk1da.de/DK1DA/FreeTRX/releases/tag/$TAG" +# Attach each staged binary, replacing an existing asset of the same name. +ASSETS="$(curl -fsS "$API/releases/$ID/assets" -H "Authorization: token $FORGEJO_TOKEN")" +for f in "$STAGE"/*.bin; do + name="$(basename "$f")" + 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"