# KE0WPC — dedicated 2 m CW beacon firmware for the Quansheng UV-K5

A purpose-built firmware that turns a UV-K5 into a locked-down CW beacon
controller and transmitter. It is **not** a modified F4HWN build, it is a
separate build target that replaces the entire radio application. There is
no receiver, no VFO, no menu, no keypad entry, and no way to change
frequency or callsign without recompiling.

* **Frequency:** 144.2770 MHz, fixed at compile time
* **Callsign:** YOURCALL (default KE0WPC, PLEASE CHANGE!), fixed at compile time
* **Sends:** `YOURCALL/B EN14` in CW at 12 WPM, then a 10 s steady carrier
* **Target:** UV-K5 / K5(8) / K6 with the **DP32G030** MCU

\---

## Hardware warning — read first

This builds for the **DP32G030**, the MCU in the original UV-K5, K5(8) and
K6. The newer **UV-K1 and UV-K5 V3 use a PY32F071** and are a completely
different target. Flashing this to a V3 will brick it.

If you are unsure, the safe check is to read your radio's existing firmware
version with `uvtools` before flashing anything.

\---

## Files

|File|What it is|
|-|-|
|`beacon\_config.h`|Every tunable. Callsign, frequency, speed, timing, power.|
|`beacon.c`|Main loop, transmitter control, keying envelope, display.|
|`morse.c` / `morse.h`|ITU Morse encoder with correct element weighting.|
|`stubs.c`|Handful of globals the stock drivers expect.|
|`../Makefile.beacon`|Build target. Lives in the repo root.|

The stock driver files are linked **completely unmodified**. Nothing outside
`beacon/` and `Makefile.beacon` is touched, so rebasing onto a newer
upstream is a matter of copying two things across.

\---

## Building

```bash
git clone https://github.com/armel/uv-k5-firmware-custom.git
cd uv-k5-firmware-custom
# drop in beacon/ and Makefile.beacon
sudo apt install gcc-arm-none-eabi python3-pip
pip3 install crcmod
make -f Makefile.beacon
```

Produces:

* `uvk5-beacon.bin` — raw image, 8748 bytes (14% of the 60 KB flash)
* `uvk5-beacon.packed.bin` — obfuscated + CRC'd, **this is the one you flash**

## Flashing

1. Back up your calibration data first, with
[uvtools](https://egzumer.github.io/uvtools/). This firmware does not
write to the calibration area, but a backup costs nothing and an
unrecoverable radio costs a radio.
2. Power the radio off. Hold **PTT** and power on — the flashlight LED
lights steady, indicating bootloader mode.
3. Flash `uvk5-beacon.packed.bin` with uvtools or `k5prog`.

To go back to a normal radio, flash any standard UV-K5 firmware the same
way. Nothing here is permanent.

\---

## What it does at power-on

```
ARMING 10 ... 1     10 s countdown. Press any key to abort. Last chance
                    to notice a disconnected antenna.
ID                  KE0WPC/B EN14 in CW, 12 WPM, \~15.4 s
CARRIER 10          Steady unmodulated carrier, 10 s
WAIT 30 ... 1       Silent gap
                    (repeat forever)
```

The display shows state, cycle count and battery voltage. Backlight goes
off after arming. The red LED follows the key.

Pressing **any key** halts the beacon permanently until you power cycle —
this is deliberate, so a halt is unambiguous rather than something you might
accidentally resume.

It also refuses to transmit below 6.60 V and halts with `Battery low`.

\---

## How the CW is generated

The BK4819 has no CW mode. The transmitter is brought up in FM with the
microphone path cut — `GPIOC\_PIN\_AUDIO\_PATH` cleared plus
`BK4819\_EnterTxMute()` — which yields a clean unmodulated carrier. That
carrier is then keyed by ramping the PA bias register (REG\_36) up and down.

The result is true A1A. A distant station copies it on a CW or SSB receiver
as a tone at whatever offset they are tuned to. On an FM receiver it sounds
like the squelch opening and closing, which is not useful — that is expected
and correct for this part of the band.

**The ramp matters.** Slamming the bias from zero to full in a single
register write gives a hard-edged envelope and audible key clicks spread
either side of the carrier. Sixteen steps over 4 ms rounds the edges enough
to be polite to the weak-signal operators who live in this segment. The
4 ms is charged against the element length rather than added to it, so
dit/dah weighting stays correct.

PA bias is read from **your radio's own factory calibration** at EEPROM
`0x1ED0` and interpolated across the band exactly as the stock firmware
does, so output power is correct rather than guessed.

\---

## Duty cycle 

A UV-K5 is a handheld. Its PA is sized for the intermittent duty an operator
produces, not for beacon service, and there is no meaningful heatsink.

With the shipped defaults:

|||
|-|-|
|CW identification|15.4 s, of which 8.6 s is key-down|
|Steady carrier|10.0 s key-down|
|Silent gap|30 s|
|**Full cycle**|**55.4 s**|
|**Actual key-down duty**|**34 %**|

At `BEACON\_POWER\_LEVEL 0` (low) this is workable but the radio will run
noticeably warmer in sustained operation. Feel the back of the case after the
first half hour. If it is too hot to hold comfortably, lengthen
`BEACON\_GAP\_SEC` or add a heatsink before leaving it unattended. If you raise power,
lengthen the carrier or shorten the gap, you are into territory the radio
was not designed for. In that case:

* bolt a heatsink to the back of the case
* run it from a bench supply or battery eliminator, not the pack
* put a thermometer on it and watch the first few hours

\---

## Things worth knowing before you deploy

**Frequency accuracy.** The UV-K5 reference is a plain crystal, not a TCXO.
Expect to be a couple of ppm off — at 144 MHz that is a few hundred hertz —
and to drift with temperature. For a propagation beacon that is usually
tolerable, but measure against a known reference and correct with
`BEACON\_FREQ\_TRIM\_10HZ` (units of 10 Hz) if it matters to you.

**No SWR protection.** There is none in the radio and none added here. Do
not key it into a bad load, and do not let the arming countdown run out
before the antenna is connected.

**Band plan.** 144.275–144.300 MHz is the propagation beacon segment, which
is why the build refuses outright to compile a frequency outside it — a
`\_Static\_assert` in `beacon\_config.h` fails the build rather than producing
an image that transmits somewhere it should not.

**Regulatory.** Under Part 97 a beacon may run under automatic control in
this segment. The station still has to be identified, which the CW ID does
every cycle, well inside the ten-minute requirement. Power is capped at
100 W for beacons and this radio does about 5 W at most, so that is not a
concern. Worth confirming there is not already an active beacon on your
chosen frequency within your coverage area before you leave it running.

\---

## Changing things

Everything lives in `beacon\_config.h`. Edit, `make -f Makefile.beacon`,
reflash. Some of the more useful knobs:

```c
#define BEACON\_ID\_TEXT   "YOURCALL/B GRID"  // sent every cycle
#define BEACON\_WPM       12               // 12–15 is typical for a beacon
#define BEACON\_CARRIER\_SEC 10             // 0 disables the steady carrier
#define BEACON\_GAP\_SEC   30               // your duty cycle control
#define BEACON\_POWER\_LEVEL 0              // 0 low, 1 mid, 2 high
#define BEACON\_BIAS\_CLAMP 255             // hard ceiling on PA bias
```

Grid square is part of `BEACON\_ID\_TEXT`. 

\---

Built on the driver layer from DualTachyon, egzumer and F4HWN.
Apache License, Version 2.0.

