NanoVNA-H and NanoVNA-H4 are very tiny handheld Vector Network Analyzers (VNA). They are standalone portable devices with LCD and battery. This project aims to provide improved firmware for this useful instrument for enthusiast.
This repository contains the source code of the improved NanoVNA-H and NanoVNA-H4 firmware.
The documentation describes the build and flash process on a MacOS or a Linux (Debian or Ubuntu) system, other Linux (or even BSD) systems may behave similar.
This is StephenGenusa/NanoVNA-D, a fork of DiSlord/NanoVNA-D carrying fixes and features for open upstream issues. I am new to the NanoVNA and this is my experimental fork.
- Ham band indicators with a region setting (DISPLAY→SCALE→HAM BANDS): band edges drawn on the frequency axis for IARU R1/R2/R3, USA, Canada, UK, Germany, Japan, or Australia (upstream #103/#104). On the H4 the bar is additionally colored by sub-band: CW (orange), narrow digital (blue), phone (green), per the IARU regional band plans.
- SD folder browsing (two levels on the H4, e.g.
CAL/HF/): folders show as/NAMEin the file browser,..goes up a level, the browser's NEW button creates a folder, and SAVE writes into the folder the browser last showed — browse into a folder, press X, then save; the save box shows the full path (manual chapter 7) (#76). Always on for the H4; opt-in for the H (__SD_BROWSER_FOLDERS__innanovna.h, one level, flash headroom). - SWR ANT trace format (DISPLAY→FORMAT→SWR ANT→SWR ANT) — the SWR at the far end of the feedline,
de-embedded from the measured S11 using the one-way cable loss you enter in
DISPLAY→FORMAT→SWR ANT→CABLE LOSS (dB, matched loss at the band in use; from the cable's data sheet or
a MEASURE→CABLE run). Feedline loss attenuates the reflected wave twice, so a lossy coax always
reads a better SWR than the antenna has: |Γ_ant| = |Γ_meas|·10^(L/10). Run SWR and SWR ANT as two
traces to see the difference. The trace is blank and the marker reads
set CABLE LOSSuntil a loss is entered; the value is not saved across power cycles because it is cable- and band-specific. Accuracy is set by how well you know L: ±0.2 dB moves a true 2:1 by ±0.07, a 3:1 by ±0.2, and a 10:1 by several units — the reading is reliable below about 5:1. Use a common-mode choke at the feedpoint; without one the coax shield is part of the antenna being measured. Console:trace 0 swrant. - Coax presets for SWR ANT (H4 only): DISPLAY→FORMAT→SWR ANT→CABLE TYPE cycles MANUAL / LMR‑400 /
RG‑213 / RG‑8X / RG‑58 / RG‑174‑316; with a type selected and CABLE LENGTH (metres) entered, the
loss is computed per sweep point from ARRL Antenna Book Vol. 3 Table 23.4 (interpolated on √f),
so it follows band changes without re-entry; the CABLE LOSS button shows the value at sweep
centre, and typing a loss there returns to MANUAL. The table is for new, dry, name-brand cable —
old, wet or off-brand coax can be markedly worse; measure it (MEASURE→CABLE, antenna disconnected)
when in doubt. Host test:
gcc -Wall -Wextra -Werror -o /tmp/test_coax tests/test_coax.c -lm && /tmp/test_coax. - SWR BW measure (MEASURE→SWR BW (S11)) — bandwidth and Q of an SWR dip. Walks from the
active marker to the nearest minimum (deepest dip in the sweep if no marker is active), reports
f₀ and minimum SWR, the 2:1 and 3:1 edge frequencies and bandwidths (an edge outside the sweep
is flagged, so widen the sweep), and the bandwidth quality factor Q. A narrow, deep dip is a
high-Q, low-loss antenna; a broad, shallow one means something in the system is dissipating —
loss makes an antenna easier to match, so a good SWR curve is not by itself a good antenna.
Q follows Yaghjian & Best (fractional VSWR-s bandwidth = 2√β/Q, β = (s−1)²/4s), generalized
to use the measured R at the dip rather than assuming a 50 Ω match, which makes it exact for
a series-RLC dip at any minimum SWR; 2:1 and 3:1 give the same Q when the dip is RLC-like.
Always on for the H4; opt-in for the H (
__S11_SWR_BW_MEASURE__innanovna.h, ~1.4 KB, which consumes essentially all of the H's remaining flash). Host test:gcc -Wall -Wextra -Werror -o /tmp/test_swr_bw tests/test_swr_bw.c -lm && /tmp/test_swr_bw. - Workflows (experimental)
- TUNE (S11) measure and a stored reference sweep (MEASURE→TUNE (S11); H4 only): a
target-frequency workflow for trimming an antenna. Enter TARGET and, optionally, cycle
ANTENNA (UNKNOWN / DIPOLE / VERTICAL / EFHW); the panel reports an ADD/REMOVE verdict and
how much, from the antenna's own 468/f or 234/f model until something better is available.
STORE REF saves the current sweep (also usable from MEASURE→RESONANCE (S11), which gets the
same STORE REF / CLEAR REF / REPEAT CHECK and a reference Δf0 row); fold the wire rather
than cut it, re-sweep, and enter what changed under WIRE CHANGE (metres, per leg on a
dipole) — the verdict then switches to the antenna's own measured sensitivity, tagged
[measured](or[loaded?]when it implies a loading coil or trap: more than 3× the full-size Hz/m for that frequency). The reference is invalidated by value — sweep points, span, calibration status, or processing (S21 offset, electrical delay, smoothing) — so a stale comparison is always flagged (stale (points|span|cal|proc)), never silently wrong. REPEAT CHECK reports the sweep-to-sweep noise floor (max |ΔΓ| against the reference) in a message box, for judging whether a small measured change is real. See the on-device ant-tune-workflow guide (below) for the full fold-before-you-cut procedure and a calculated kHz/cm sensitivity table. Host test:python3 -m unittest tests.test_workflow. - CHOKE (S21) measure and a fixture null (MEASURE→MORE (S21)→CHOKE (S21); H4 only): a
series-through per-band test for a common-mode choke, since reflection measurements are
poor in the 1–10 kΩ range a good choke sits in. Build a jig with the choke in series between
the ports, THRU-calibrate the span, pick a region under DISPLAY→SCALE→HAM BANDS, then sweep
the jig OPEN and STORE FIXTURE before inserting the choke — every reading has that open
sweep's admittance subtracted first, so the jig's own stray capacitance is not counted as
choke resistance. Each band reports its worst-point series resistance R_S and a verdict
(POOR / WEAK / MARGINAL / GOOD / MEETS / HIGH PWR against a typed target, 5 kΩ by default,
2 kΩ to 100 kΩ) — X is shown but never judged, since a choke's reactance can cancel other
reactance in the common-mode circuit rather than help it, while R_S only ever adds loss. A
JIGreading marks a band where the fixture, not the choke, is the limiting factor. The ohms printed on such a row are the jig's un-nulled ceiling, 1/(4πfC); the verdict itself is taken against a limit some twenty times higher, because de-embedding subtracts the jig's susceptance instead of enduring it and only the repeatability of the null (taken as 5 %) leaks back into R_S — which is what lets a clip-lead jig grade a multi-kΩ choke across the whole HF range instead of only the lowest bands. STORE REF keeps the corrected impedance of the worst band to compare across a rewind (REF: 20m R 3.90kΩ → 6.10kΩ), sharing its storage with TUNE/RESONANCE's reference but as a different, mutually invisible kind. See the on-device choke-measure and choke-recipe guides (below) for the fixture, pass criteria and turns tables.
- TUNE (S11) measure and a stored reference sweep (MEASURE→TUNE (S11); H4 only): a
target-frequency workflow for trimming an antenna. Enter TARGET and, optionally, cycle
ANTENNA (UNKNOWN / DIPOLE / VERTICAL / EFHW); the panel reports an ADD/REMOVE verdict and
how much, from the antenna's own 468/f or 234/f model until something better is available.
STORE REF saves the current sweep (also usable from MEASURE→RESONANCE (S11), which gets the
same STORE REF / CLEAR REF / REPEAT CHECK and a reference Δf0 row); fold the wire rather
than cut it, re-sweep, and enter what changed under WIRE CHANGE (metres, per leg on a
dipole) — the verdict then switches to the antenna's own measured sensitivity, tagged
- On-device guides (SD CARD→LOAD→GUIDE, H4; opt-in for the H): the device shows short
reference pages from the card's
GUIDESfolder — plain markdown with headings, emphasis and auto-laid-out tables, paged with the wheel or a tap. The repository'sGUIDES/pack has 29 of them: antenna tuning and radials, POTA/SOTA rules and field safety, the K9YC choke recipe and how to measure a choke with the S21 series-through formats, coax loss, SWR and formula cards, propagation, and the instrument's own formats, panels, commands and menus. Write your own with any editor;tools/manual/guide.py checktells you what the screen would clip andguide.py rendershows each page pixel for pixel from your PC.
GUIDES/ant-radials.md, page 2, as the NanoVNA-H4 draws it - PNG screenshots on the H4 (SD CARD→IMAGE FORMAT cycles BMP / TIFF / PNG): indexed 8-bit, compressed PNG that opens anywhere: a busy sweep screen is 24 KB as PNG against 307 KB as BMP and 55 KB as TIFF. BMP and TIFF are unchanged on both devices (the H keeps BMP / TIFF), and a saved configuration with TIFF selected keeps saving TIFF. LOAD SCREENSHOT lists all three types. The on-device viewer opens PNGs the device wrote; PNGs made on a PC generally use compression features it does not implement and are rejected with "Unsupported PNG".
.nvsaccepted for command scripts alongside.cmd, which mail/AV filters block (#97).*IDN?console command (SCPI-style identify) for VISA/pyvisa/LabVIEW use (#98).- ZERO marker search alongside MAXIMUM/MINIMUM — finds the trace value closest to zero, e.g. reactance zero crossings (#107).
- Raw S21 phase corrected (hardware THRU polarity inversion undone in firmware, #81). Note: re-do your THRU calibration after flashing — thru data saved by older firmware carries the old sign and would show S21 phase off by 180°. Verified on two NanoVNA‑H4 units (raw S21 phase reads ≈180° on stock firmware, ≈0° with this fix; calibrated results are identical). The NanoVNA‑H has not been checked.
- Optional output mute on pause (STIMULUS→MUTE OUTPUT ON PAUSE, default off): finishes the current scan, then disables the Si5351 outputs while paused (#50).
- Touch double-tap filter — accidental rapid re-taps ignored (100 ms window, #109).
- Consistent stored-file viewing: changing the stimulus while displaying an
.s1p/.s2ploaded from SD resumes live sweep instead of desyncing axes and markers (#101). - Fix sweep hang after ~12 h uptime (32-bit system-time wrap, #110).
CLOCK_GENbuild option for boards with an MS5351/SWC5351 clock chip (see Build below, #54).
A workflow is a MEASURE panel that turns a sweep into a verdict for one job, instead of leaving the numbers to be read off a trace: it tells you what to do next (add or remove wire, which band your choke is weakest on) and keeps a stored reference so the next sweep can be compared with the last one. Two exist so far, both on the NanoVNA‑H4 only:
- TUNE (S11) — trim a wire antenna to a target frequency. Enter the target, read the ADD/REMOVE verdict, store a reference, fold some wire, re-sweep, enter the change, and the verdict switches from a textbook model to the antenna's own measured sensitivity.
- CHOKE (S21) — grade a common-mode choke band by band from a series-through sweep, after nulling the test jig so its stray capacitance is not mistaken for choke resistance.
They are more involved than the other panels — the choke workflow needs a jig, a THRU calibration, a stored fixture sweep and a region setting before it prints a single verdict — and the manual (chapter 5) and the on-device guides walk through each step. They are also new and experimental: the arithmetic is host-tested and the code has been reviewed, but bench time on real antennas and chokes is still limited, and bug fixes should be expected. Treat a verdict as a well-informed suggestion, not an instruction: check that the numbers make physical sense before you cut wire or accept a choke, and if the panel and your own judgment disagree, trust your judgment and report the case.
Read it here: PDF · single-file HTML (both attached to the v1.2.58-sg release).
A manual written from the firmware source lives in docs/manual/:
every menu, trace format, console command and status letter is taken from the code that
implements it and footnoted to it, with the original NanoVNA guide's procedures checked
against the code and reworded. Start at 00-front.md. Chapters:
orientation (screen, status letters, wheel, touch),
trace formats, calibration,
markers, MEASURE panels,
fork features, SD card,
console commands, menu map with a
mockup of every menu on both devices, and firmware update.
GUIDES/ is a pack of 29 reference pages for the SD card — antenna tuning and
radials, POTA/SOTA field rules and safety, choke recipe and measurement, coax loss, SWR and
formula cards, the device's own formats and commands — that the H4 shows on screen via
SD CARD → LOAD → GUIDE. They are plain markdown you can extend (manual chapter 7).
Sweep screens in the chapters are rendered from modelled circuits by
tools/manual/screen.py, which reproduces the firmware's drawing code
pixel for pixel and is regression-tested against real H4 screenshots.
The menu map, console reference and trace-format table are generated by the scripts in
tools/manual/ (make -C docs/manual all) and checked against the source by
python3 -m unittest tests.test_manual_gen, so they cannot drift from the firmware.
make -C docs/manual dist builds the PDF and HTML (pandoc + XeLaTeX).
References for the antenna-measurement features:
- A. D. Yaghjian and S. R. Best, "Impedance, Bandwidth, and Q of Antennas," IEEE Trans. Antennas Propag., vol. 53, no. 4, pp. 1298–1324, Apr. 2005.
- A. D. Yaghjian, "Fundamentals of Antenna Bandwidth and Quality Factor," arXiv:2501.03146 (2025), eq. 7, 12, 21 — open-access restatement of the above.
- M. W. Maxwell, W2DU, Reflections III: Transmission Lines and Antennas, Appendix 6 — the SWR-through-line-loss relation used by SWR ANT (3:1 through 0.5 dB reads 2.61:1).
- ARRL Antenna Book, Vol. 1, "Q of Antennas" — the 2:1 SWR bandwidth convention.
- ARRL Antenna Book, Vol. 3, Table 23.4, "Cable Attenuation (dB per 100 feet)" — the coax preset figures (stored converted to dB per 100 m).
binaries/ holds the current release builds with SHA-256 checksums:
binaries/NanoVNA-H_1.2.58-sg.bin NanoVNA-H (STM32F072)
binaries/NanoVNA-H4_1.2.58-sg.bin NanoVNA-H4 (STM32F303)
.hex versions are alongside for tools that want them. Flash the .bin with dfu-util at
0x08000000 (see Flash Firmware below, or ./2_prog.sh). Releases on GitHub carry the same
files as assets. Re-do calibration after flashing (see the S21 note above).
UPDATE: Recent gcc version works to build NanoVNA, no need to use old version.
Install cross tools and firmware updating tool.
brew tap px4/px4
brew install gcc-arm-none-eabi-80
brew install dfu-util
Download arm cross tools from here.
wget https://developer.arm.com/-/media/Files/downloads/gnu-rm/8-2018q4/gcc-arm-none-eabi-8-2018-q4-major-linux.tar.bz2
sudo tar xfj gcc-arm-none-eabi-8-2018-q4-major-linux.tar.bz2 -C /usr/local
PATH=/usr/local/gcc-arm-none-eabi-8-2018-q4-major/bin:$PATH
sudo apt install -y dfu-util
sudo apt install gcc-arm-none-eabi
sudo apt install -y dfu-util
Do this once to initialize your local clone from GitHub (ChibiOS and FatFS are vendored in-tree — no submodules to fetch):
git clone https://github.com/StephenGenusa/NanoVNA-D.git
cd NanoVNA-D
To get updates from the GitHub repository, go to your NanoVNA-D directory and type:
git pull
Go to your NanoVNA-D directory and type:
export TARGET=F072
make clean
make
Go to your NanoVNA-D directory and type:
export TARGET=F303
make clean
make
For boards populated with an MS5351 or SWC5351 clock generator (e.g. HW version 4.3_MS), bake in the matching default (also switchable at runtime via CONFIG→MODE):
make TARGET=F303 CLOCK_GEN=MS5351
Three scripts in the repository root wrap the common workflow:
./0_backup_firmware.sh # back up the attached device's flash over DFU
./1_build.sh # clean build -> build/H4.bin or build/H.bin (default F303; or pass F072)
./2_prog.sh # flash the built firmware via dfu-util
The two DFU scripts detect which device is attached from the flash layout the STM32 bootloader
reports (detect_target.sh: 64 × 2 KB pages = H, 128 × 2 KB = H4) and act on that; with no
device in DFU mode they stop and say so rather than assume. You can still pass F072 or F303
explicitly — 2_prog.sh then refuses if the attached device is the other one, so the wrong image
cannot be written by mistake. 1_build.sh adds the ARM toolchain to PATH itself if it finds
one under /usr/local or /opt.
When the build of your firmware is finished, you can flash it onto your NanoVNA device. First, let the device enter DFU mode by one of following methods.
- Open the device and jumper
BOOT0pin toVddpin when powering the device. - Select menu Config->DFU (needs recent firmware).
- Press the jog switch on your -H4 when powering the device.
Then, flash the firmware using dfu-util via USB.
Go to your NanoVNA-D directory and type:
dfu-util -d 0483:df11 -a 0 -s 0x08000000:leave -D build/H.bin
Go to your NanoVNA-D directory and type:
dfu-util -d 0483:df11 -a 0 -s 0x08000000:leave -D build/H4.bin
Go to your NanoVNA-D directory and type:
make flash
The low-level tool dfu-util displays a lot of information that is very useful especially for developers, but can confuse the user.
In particular, please ignore the message about corrupt firmware, this is the normal behaviour of the unit before clearing the status.
It is important to note that after clearing the status, there is no longer an error condition present.
...
Determining device status...
DFU state(10) = dfuERROR, status(10) = Device's firmware is corrupt. It cannot return to run-time (non-DFU) operations
Clearing status
Determining device status...
DFU state(2) = dfuIDLE, status(0) = No error condition is present
...
There are several numbers of great companion PC tools from third-party.
tools/vna/— fork-authored PC scripts that drive the device over the USB console, e.g.resonance_log.py(log f0 and R at X = 0 across ground-system changes, with the dB gain of each step); seetools/vna/README.md.- NanoVNA-App software by OneOfEleven
- NanoVNASharp Windows software by hugen79
- NanoVNA WebSerial/WebUSB by cho45
- Android NanoVNA app by cho45
- NanoVNASaver by mihtjel and the members of NanoVNA-Saver
- TAPR VNAR4 supports NanoVNA by erikkaashoek
- The NanoVNA toolbox by Ho-Ro
- see python directory to use NanoVNA with Python and Jupyter Notebook.
- This firmware's user manual — written from the source; see User manual above.
- NanoVNA User Guide(ja) by cho45. (en:google translate)
- NanoVNA user group on groups.io.
- Schematics
- PCB Photo
- Block Diagram
- Kit available from https://ttrf.tk/kit/nanovna
Hardware design material is disclosed to prevent bad quality clone. Please let me know if you would have your own unit.
