Compare commits

..

No commits in common. "main" and "v0.10.0" have entirely different histories.

14 changed files with 36 additions and 177 deletions

View file

@ -9,18 +9,6 @@ single running list until the first versioned release.
_Nothing yet._ _Nothing yet._
## 0.11.0
- **CRT-style shut-off animation** — when powering off, the current screen
collapses towards the middle (squeezed to a line, then a dot) like an old CRT
switching off, replacing the "Power Off" message. (Colour displays only.)
## 0.10.1
- **Fix:** the muted **"M"** indicator in the status bar overlapped the GPS text
when GPS was enabled. The header icon is removed; the muted state is shown by
the flashing red LED alone.
## 0.10.0 ## 0.10.0
- **SMS recipient from contacts** — when composing a message, press **GREEN** in - **SMS recipient from contacts** — when composing a message, press **GREEN** in

View file

@ -59,11 +59,6 @@ Optionally dims the backlight after inactivity to **100 / 50 / 30 / 15 %** of th
configured brightness (only in **Auto** backlight mode). Any activity restores full configured brightness (only in **Auto** backlight mode). Any activity restores full
brightness. Set it in **Menu → Options → Display Options → Screen Dim**. brightness. Set it in **Menu → Options → Display Options → Screen Dim**.
### Shut-off animation — 🟢 [FreeTRX]
Powering off collapses whatever is on screen towards the middle — squeezed to a
line, then a dot — like an old CRT switching off, in place of the "Power Off"
message. (Once the animation starts, the power-off is committed.)
### Theme presets — 🟢 [FreeTRX] ### Theme presets — 🟢 [FreeTRX]
Pick a colour theme on the radio at **Menu → Options → Display Options → "Theme"** Pick a colour theme on the radio at **Menu → Options → Display Options → "Theme"**
(◀/▶ to change; the screen updates live): (◀/▶ to change; the screen updates live):
@ -85,7 +80,8 @@ Editing colours in the CPS only affects **Custom**; the built-in presets are
fixed. Every element (S-meter, frequencies, menus) follows the selected theme. fixed. Every element (S-meter, frequencies, menus) follows the selected theme.
### Mute indicator — 🟢 [FreeTRX] ### Mute indicator — 🟢 [FreeTRX]
While muted, the **red LED flashes** (0.5 s every 2 s). See *Mute* below. While muted, a bold **"M"** appears in the status bar (just left of the battery)
and the **red LED flashes** (0.5 s every 2 s). See *Mute* below.
--- ---
@ -118,21 +114,8 @@ A real-time signal-strength-vs-frequency graph centred on the VFO frequency.
- The scope is **VFO-only** (not available in Channel mode). - The scope is **VFO-only** (not available in Channel mode).
### Scanning — ⚪ [OpenGD77] ### Scanning — ⚪ [OpenGD77]
**There are no scan lists.** Unlike the vendor CPS, OpenGD77 scans **the channels Channel and VFO scanning with hold/pause behaviour, per the upstream OpenGD77
of the currently selected zone** — so a zone *is* the scan list. To scan a controls.
particular set of channels, put them in a zone and select it. The **All Channels**
virtual zone scans every channel in the codeplug.
- **Start/stop:** long-press **UP** on the Channel or VFO screen (**RED** stops).
- **Exclude a channel:** **Menu → Channel details** (or **SK2 + green** on the
Channel screen) → **Zone Skip** (skipped when scanning a normal zone) and
**All Skip** (skipped when scanning *All Channels*).
- **Settings:** **Menu → Options → Radio options****Scan delay**,
**Scan dwell**, **Scan mode** (Hold / Pause / Stop) and **Scan On Boot**.
Note: in the codeplug these two skip flags reuse the old "Auto Scan" and "Lone
Worker" bits, so a vendor CPS may label them differently — set them on the radio
to avoid confusion.
### Quick menu — ⚪ [OpenGD77] (with 🟢 [FreeTRX] additions) ### Quick menu — ⚪ [OpenGD77] (with 🟢 [FreeTRX] additions)
Press the **top / orange** button to open the quick menu for the current screen Press the **top / orange** button to open the quick menu for the current screen
@ -176,11 +159,6 @@ Talkgroups/contacts, timeslots, colour codes, RX groups, and hotspot mode, all p
upstream OpenGD77. The AMBE voice codec is merged in at flash time (see upstream OpenGD77. The AMBE voice codec is merged in at flash time (see
[`TOOLS.md`](TOOLS.md)). [`TOOLS.md`](TOOLS.md)).
### Contacts list — 🟢 [FreeTRX]
**Menu → Contacts** lists all contact types together under the title "Contacts"
(instead of defaulting to the group filter). Press **`#`** to cycle the filter:
all → groups → private → all-call → all.
### DMR reliability fix — 🟢 [FreeTRX] ### DMR reliability fix — 🟢 [FreeTRX]
FreeTRX resolves a hard-fault (freeze needing a battery pull) on the DMR path that FreeTRX resolves a hard-fault (freeze needing a battery pull) on the DMR path that
affected recent self-built OpenGD77 revisions — the codec is called through C affected recent self-built OpenGD77 revisions — the codec is called through C
@ -223,10 +201,9 @@ second or two a message is on air; hotspot mode is unaffected.
## About & versioning — 🟢 [FreeTRX] ## About & versioning — 🟢 [FreeTRX]
**Menu → Firmware info** shows the **FreeTRX version** (semantic versioning, **Menu → Firmware info** shows the **FreeTRX version** (semantic versioning, e.g.
`FreeTRX vMAJOR.MINOR.PATCH`) alongside the git commit ID. Pre-1.0 builds are `FreeTRX v0.4.0`) alongside the git commit ID. Pre-1.0 builds are published as
published as pre-releases. See [`CHANGELOG.md`](CHANGELOG.md) for the full pre-releases. See [`CHANGELOG.md`](CHANGELOG.md) for the full history.
history.
--- ---

View file

@ -5,9 +5,9 @@
#define _FREETRX_VERSION_H_ #define _FREETRX_VERSION_H_
#define FREETRX_VERSION_MAJOR 0 #define FREETRX_VERSION_MAJOR 0
#define FREETRX_VERSION_MINOR 11 #define FREETRX_VERSION_MINOR 10
#define FREETRX_VERSION_PATCH 0 #define FREETRX_VERSION_PATCH 0
#define FREETRX_VERSION "0.11.0" #define FREETRX_VERSION "0.10.0"
#endif // _FREETRX_VERSION_H_ #endif // _FREETRX_VERSION_H_

View file

@ -267,9 +267,6 @@ bool uiNotificationHasTimedOut(void);
bool uiNotificationIsVisible(void); bool uiNotificationIsVisible(void);
void uiNotificationHide(bool immediateRender); void uiNotificationHide(bool immediateRender);
uiNotificationID_t uiNotificationGetId(void); uiNotificationID_t uiNotificationGetId(void);
#if defined(HAS_COLOURS)
uint16_t *uiNotificationGetOffscreenBuffer(void); // full-screen scratch (borrowed by the shutdown animation)
#endif
#if defined(HAS_GPS) #if defined(HAS_GPS)

View file

@ -351,15 +351,6 @@ uiNotificationID_t uiNotificationGetId(void)
return notificationData.id; return notificationData.id;
} }
#if defined(HAS_COLOURS)
// Expose the full-screen offscreen buffer so the shutdown animation can borrow
// it as scratch (no notification is active while powering off).
uint16_t *uiNotificationGetOffscreenBuffer(void)
{
return screenNotificationBufData;
}
#endif
static void displayMessage(void) static void displayMessage(void)
{ {
char msg[SCREEN_LINE_BUFFER_SIZE]; char msg[SCREEN_LINE_BUFFER_SIZE];

View file

@ -25,25 +25,18 @@
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
*/ */
#include <string.h>
#include "user_interface/uiGlobals.h" #include "user_interface/uiGlobals.h"
#include "user_interface/menuSystem.h" #include "user_interface/menuSystem.h"
#include "user_interface/uiLocalisation.h" #include "user_interface/uiLocalisation.h"
#include "functions/ticks.h"
#if defined(PLATFORM_MD9600) || defined(PLATFORM_MD380) || defined(PLATFORM_MDUV380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017) #if defined(PLATFORM_MD9600) || defined(PLATFORM_MD380) || defined(PLATFORM_MDUV380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
#include "interfaces/batteryAndPowerManagement.h" #include "interfaces/batteryAndPowerManagement.h"
#include "hardware/radioHardwareInterface.h" #include "hardware/radioHardwareInterface.h"
#endif #endif
const uint32_t POWEROFF_DURATION_MILLISECONDS = 500;
#if defined(HAS_COLOURS)
static void crtShutdownAnimation(void);
#else
static uint32_t initialEventTime;
static void updateScreen(void); static void updateScreen(void);
static void handleEvent(uiEvent_t *ev); static void handleEvent(uiEvent_t *ev);
#endif static uint32_t initialEventTime;
const uint32_t POWEROFF_DURATION_MILLISECONDS = 500;
menuStatus_t uiPowerOff(uiEvent_t *ev, bool isFirstRun) menuStatus_t uiPowerOff(uiEvent_t *ev, bool isFirstRun)
{ {
@ -56,103 +49,23 @@ menuStatus_t uiPowerOff(uiEvent_t *ev, bool isFirstRun)
} }
#endif #endif
#if defined(HAS_COLOURS)
// Old-CRT-style collapse of whatever is currently on screen; replaces
// the "Power Off" / 73 message, then shuts down.
crtShutdownAnimation();
powerOffFinalStage(false, false);
#else
updateScreen(); updateScreen();
initialEventTime = ev->time; initialEventTime = ev->time;
#endif
} }
else else
{ {
#if !defined(HAS_COLOURS)
handleEvent(ev); handleEvent(ev);
#endif
} }
return MENU_STATUS_SUCCESS; return MENU_STATUS_SUCCESS;
} }
#if defined(HAS_COLOURS)
static void crtShutdownAnimation(void)
{
const int W = DISPLAY_SIZE_X;
const int H = DISPLAY_SIZE_Y;
const int cx = W / 2;
const int cy = H / 2;
const int VSTEPS = 12;
const int HSTEPS = 12;
const uint16_t white = 0xFFFF;
displayRestorePrimaryScreenBuffer();
uint16_t *fb = displayGetPrimaryScreenBuffer();
uint16_t *snap = uiNotificationGetOffscreenBuffer();
// snapshot the screen as it is right now
memcpy(snap, fb, W * H * sizeof(uint16_t));
// Phase 1: squeeze the whole image vertically into a thin central band.
for (int step = 1; step <= VSTEPS; step++)
{
int bandH = H - ((H - 2) * step) / VSTEPS; // 128 -> 2
int top = cy - (bandH / 2);
memset(fb, 0, W * H * sizeof(uint16_t));
for (int dy = 0; dy < bandH; dy++)
{
int srcY = (dy * H) / bandH;
if (srcY >= H)
{
srcY = H - 1;
}
memcpy(&fb[(top + dy) * W], &snap[srcY * W], W * sizeof(uint16_t));
}
displayRender();
vTaskDelay((15 / portTICK_PERIOD_MS));
}
// Phase 2: the band becomes a bright line that collapses horizontally.
for (int step = 1; step <= HSTEPS; step++)
{
int lineW = W - ((W - 2) * step) / HSTEPS; // 160 -> 2
int left = cx - (lineW / 2);
memset(fb, 0, W * H * sizeof(uint16_t));
for (int dx = 0; dx < lineW; dx++)
{
fb[((cy - 1) * W) + (left + dx)] = white;
fb[(cy * W) + (left + dx)] = white;
}
displayRender();
vTaskDelay((15 / portTICK_PERIOD_MS));
}
// Phase 3: a brief central dot, then black.
memset(fb, 0, W * H * sizeof(uint16_t));
fb[((cy - 1) * W) + (cx - 1)] = white;
fb[((cy - 1) * W) + cx] = white;
fb[(cy * W) + (cx - 1)] = white;
fb[(cy * W) + cx] = white;
displayRender();
vTaskDelay((70 / portTICK_PERIOD_MS));
memset(fb, 0, W * H * sizeof(uint16_t));
displayRender();
}
#endif
#if !defined(HAS_COLOURS)
static void updateScreen(void) static void updateScreen(void)
{ {
#if defined(HAS_COLOURS)
bool dblHeight = settingsIsOptionBitSet(BIT_UI_USES_DOUBLE_HEIGHT);
#else
const bool dblHeight = false; const bool dblHeight = false;
#endif
uint16_t fontHeight = (FONT_SIZE_3_HEIGHT * (dblHeight ? 2 : 1)); uint16_t fontHeight = (FONT_SIZE_3_HEIGHT * (dblHeight ? 2 : 1));
displayClearBuf(); displayClearBuf();
@ -191,4 +104,3 @@ static void handleEvent(uiEvent_t *ev)
} }
} }
#endif

View file

@ -1853,12 +1853,12 @@ void uiUtilityRenderQSOData(void)
displayThemeResetToDefault(); displayThemeResetToDefault();
} }
// Toggle the audio mute (speaker) state. The flashing red LED (see the main // Toggle the audio mute (speaker) state. The header shows a mute icon and the LED
// loop) indicates the muted state. // flashes amber (see the main loop) while muted.
void toggleAudioMute(void) void toggleAudioMute(void)
{ {
audioAmpMute(!audioAmpIsMuted()); audioAmpMute(!audioAmpIsMuted());
headerRowIsDirty = true; headerRowIsDirty = true; // refresh the header so the mute icon appears/clears
} }
void uiUtilityRenderHeader(bool isVFODualWatchScanning, bool isVFOSweepScanning, bool forceBatteryDisplay) void uiUtilityRenderHeader(bool isVFODualWatchScanning, bool isVFOSweepScanning, bool forceBatteryDisplay)
@ -1878,6 +1878,8 @@ void uiUtilityRenderHeader(bool isVFODualWatchScanning, bool isVFOSweepScanning,
static uint32_t blinkTime = 0; static uint32_t blinkTime = 0;
int radioMode = trxGetMode(); int radioMode = trxGetMode();
uint8_t powerLevel = trxGetPowerLevel(); uint8_t powerLevel = trxGetPowerLevel();
// Muted either explicitly (SK2) or by the volume knob turned fully down (-99).
bool audioIsMuted = (audioAmpIsMuted() || (lastVolume == -99));
bool isPerChannelPower = (currentChannelData->libreDMR_Power != 0x00); bool isPerChannelPower = (currentChannelData->libreDMR_Power != 0x00);
bool scanIsActive = (uiDataGlobal.Scan.active || uiDataGlobal.Scan.toneActive); bool scanIsActive = (uiDataGlobal.Scan.active || uiDataGlobal.Scan.toneActive);
bool batteryIsLow = batteryIsLowWarning(); bool batteryIsLow = batteryIsLowWarning();
@ -1998,7 +2000,7 @@ void uiUtilityRenderHeader(bool isVFODualWatchScanning, bool isVFOSweepScanning,
displayPrintCore(MODE_TEXT_X_OFFSET, DISPLAY_Y_POS_HEADER, buffer, displayPrintCore(MODE_TEXT_X_OFFSET, DISPLAY_Y_POS_HEADER, buffer,
(((nonVolatileSettings.hotspotType != HOTSPOT_TYPE_OFF) && (uiDataGlobal.dmrDisabled == false)) ? FONT_SIZE_1_BOLD : FONT_SIZE_1), TEXT_ALIGN_LEFT, isInverted); (((nonVolatileSettings.hotspotType != HOTSPOT_TYPE_OFF) && (uiDataGlobal.dmrDisabled == false)) ? FONT_SIZE_1_BOLD : FONT_SIZE_1), TEXT_ALIGN_LEFT, isInverted);
// (mute is indicated by the flashing red LED, not a strike-through) // (mute is shown by the header "M" indicator, not a strike-through)
} }
if ((aprsIsTransmittingOnSameQRG == false) && (monitorModeData.isEnabled == false) && (isVFOSweepScanning == false) && (isVFODualWatchScanning == false) && if ((aprsIsTransmittingOnSameQRG == false) && (monitorModeData.isEnabled == false) && (isVFOSweepScanning == false) && (isVFODualWatchScanning == false) &&
@ -2066,7 +2068,7 @@ void uiUtilityRenderHeader(bool isVFODualWatchScanning, bool isVFOSweepScanning,
displayPrintCore(MODE_TEXT_X_OFFSET, DISPLAY_Y_POS_HEADER, (isVFODualWatchScanning ? "[DW]" : "DMR"), ((nonVolatileSettings.hotspotType != HOTSPOT_TYPE_OFF) ? FONT_SIZE_1_BOLD : FONT_SIZE_1), TEXT_ALIGN_LEFT, isInverted); displayPrintCore(MODE_TEXT_X_OFFSET, DISPLAY_Y_POS_HEADER, (isVFODualWatchScanning ? "[DW]" : "DMR"), ((nonVolatileSettings.hotspotType != HOTSPOT_TYPE_OFF) ? FONT_SIZE_1_BOLD : FONT_SIZE_1), TEXT_ALIGN_LEFT, isInverted);
// (mute is indicated by the flashing red LED, not a strike-through) // (mute is shown by the header "M" indicator, not a strike-through)
} }
if (isVFODualWatchScanning == false) if (isVFODualWatchScanning == false)
@ -2178,8 +2180,13 @@ void uiUtilityRenderHeader(bool isVFODualWatchScanning, bool isVFOSweepScanning,
bool displayTime = (settingsIsOptionBitSet(BIT_DISPLAY_TIME_IN_HEADER) && (forceBatteryDisplay == false) && (batteryIsLow == false)); bool displayTime = (settingsIsOptionBitSet(BIT_DISPLAY_TIME_IN_HEADER) && (forceBatteryDisplay == false) && (batteryIsLow == false));
#endif #endif
#endif #endif
// Mute state is indicated by the flashing red LED only; no header icon // Mute indicator: bold "M" just left of the battery indicator.
// (it collided with the GPS text in the status bar). if (audioIsMuted)
{
displayThemeApply(THEME_ITEM_FG_HEADER_TEXT, THEME_ITEM_BG_HEADER_TEXT);
displayPrintCore((DISPLAY_SIZE_X - 40), DISPLAY_Y_POS_HEADER, "M", FONT_SIZE_1_BOLD, TEXT_ALIGN_LEFT, false);
displayThemeResetToDefault();
}
// Display battery percentage/voltage // Display battery percentage/voltage
bool apoEnabled = (nonVolatileSettings.apo > 0); bool apoEnabled = (nonVolatileSettings.apo > 0);

View file

@ -15,15 +15,9 @@ tagged 🟢 **[FreeTRX]** (new/changed here) or ⚪ **[OpenGD77]** (inherited).
## Screenshots ## Screenshots
| Segmented S-meter & DMR call | 7-segment frequency display | Main menu | | Segmented S-meter & DMR call | 7-segment frequency display |
| --- | --- | --- |
| ![DMR channel showing the talkgroup, repeater name and the block S-meter](docs/images/screenshot-dmr.png) | ![VFO with the 7-segment frequency font](docs/images/screenshot-vfo.png) | ![Main menu with the Messages entry highlighted](docs/images/screenshot-menu.png) |
| DMR text messages | Reading a received message |
| --- | --- | | --- | --- |
| ![Messages menu: quick text, options, new message, inbox and sent](docs/images/screenshot-messages.png) | ![A received weather-report message from a BrandMeister service](docs/images/screenshot-message-view.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) |
*Screens shown with the **Neon 80s** theme.*
## What's different from OpenGD77 ## What's different from OpenGD77
@ -31,8 +25,8 @@ 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 - **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 screen toggles the speaker mute on and off. Turning the **volume knob fully
down** counts as muted too. While muted, the **red LED flashes** (0.5 s every down** counts as muted too. While muted, a bold **"M"** appears in the status
2 s). SK2's 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 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 monitor/squelch-off, and — while a DMR QSO is on screen — a tap still sets the
TX talkgroup to the incoming one. TX talkgroup to the incoming one.
@ -42,18 +36,15 @@ FreeTRX keeps OpenGD77's core (DMR/FM, hotspot, CPS compatibility) and adds:
- **Screen dimming** — optionally dim the backlight to 100 / 50 / 30 / 15 % after - **Screen dimming** — optionally dim the backlight to 100 / 50 / 30 / 15 % after
inactivity (Auto backlight mode); new setting in Display Options. inactivity (Auto backlight mode); new setting in Display Options.
- **DMR text messages (SMS)** — send and receive text messages on the radio, - **DMR text messages (SMS)** — send and receive text messages on the radio,
compatible with BrandMeister, hotspots and OEM radios. Compose on the keypad compatible with BrandMeister, hotspots and OEM radios. Compose on the keypad,
(or pick the recipient from your contacts), inbox/sent boxes, quick-text inbox/sent boxes, quick-text templates, delivery confirmation and an
templates, delivery confirmation and an incoming-message popup (see the manual incoming-message popup (see the manual for details).
for details).
- **Theme presets** — pick a colour theme on the radio: **Custom** (CPS-editable, - **Theme presets** — pick a colour theme on the radio: **Custom** (CPS-editable,
default), **BASIC 80s** (Commodore 64), **Terminal** (green-on-black), default), **BASIC 80s** (Commodore 64), **Terminal** (green-on-black),
**Vertexic** (amber/orange), **Red**, **Neon 80s** (multi-colour synthwave), **Vertexic** (amber/orange), **Red**, **Neon 80s** (multi-colour synthwave),
or **Pride** (rainbow background). CPS edits only affect Custom. or **Pride** (rainbow background). CPS edits only affect Custom.
- **On-radio channel management** — create and delete channels directly from the - **On-radio channel management** — create and delete channels directly from the
Channel quick menu, no PC/CPS required (see [Managing channels](#managing-channels-on-the-radio)). Channel quick menu, no PC/CPS required (see [Managing channels](#managing-channels-on-the-radio)).
- **CRT-style shut-off animation** — the current screen collapses to a line and
then a dot, like an old CRT switching off (replaces the "Power Off" message).
- **DMR reliability fix** — resolves a hard-fault (freeze needing a battery pull) - **DMR reliability fix** — resolves a hard-fault (freeze needing a battery pull)
on the DMR path that affected recent self-built OpenGD77 revisions. on the DMR path that affected recent self-built OpenGD77 revisions.

View file

@ -34,10 +34,6 @@ Output: `MDUV380_firmware/build/Open<platform>.bin` (the flashable, FM-only imag
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. so building one platform overwrites the other's output.
Each build finishes with a **memory-usage summary** — used, size and free bytes for the
FLASH (code), RAM and CCMRAM regions — so the remaining headroom for new features is
visible at a glance.
--- ---
## 3. Flashing ## 3. Flashing

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB