Skip to content

USB Protocol Reference

Technical reference for communicating with NanoVNA-H over USB. Use this guide to build PC applications, automation scripts, or understand the device interface.

FieldValueDescription
idVendor0x0483STMicroelectronics
idProduct0x5740Virtual COM Port
bcdUSB0x0110USB 1.1
bcdDevice0x0200Device version 2.00
bMaxPacketSize064Control endpoint packet size
IndexStringNotes
1nanovna.comManufacturer
2NanoVNA-H or NanoVNA-H4Product (target-dependent)
3xyz or unique IDSerial number

NanoVNA-H implements the CDC ACM (Communications Device Class - Abstract Control Model) profile.

Configuration Descriptor (67 bytes)
├── Interface 0: CDC Control (Communications Class)
│ ├── Header Functional Descriptor
│ ├── Call Management Functional Descriptor
│ ├── ACM Functional Descriptor
│ ├── Union Functional Descriptor
│ └── Endpoint 2 IN: Interrupt (8 bytes, interval 255ms)
└── Interface 1: CDC Data (Data Class)
├── Endpoint 1 OUT: Bulk (64 bytes)
└── Endpoint 1 IN: Bulk (64 bytes)
EndpointDirectionTypeSizePurpose
EP1OUTBulk64 bytesCommands from host
EP1INBulk64 bytesResponses to host
EP2INInterrupt8 bytesCDC notifications

Device path: /dev/ttyACM0 (may increment if multiple devices)

Terminal window
# Using screen
screen /dev/ttyACM0
# Using minicom
minicom -D /dev/ttyACM0
# Using Python
python -c "import serial; s=serial.Serial('/dev/ttyACM0'); print(s.read_until(b'ch> '))"

Permissions: Add user to dialout group:

Terminal window
sudo usermod -aG dialout $USER
# Log out and back in

ParameterValue
Promptch>
Line terminator\r\n (CRLF)
Max line length64 characters
Max arguments4
Baud rateN/A (USB is packet-based)
command [arg1] [arg2] [arg3] [arg4]\r\n

Arguments are space-separated. Command names are case-sensitive.

Frequencies can use multiplier suffixes:

SuffixMultiplierExampleValue
(none)110000001 MHz
k1,0001000k1 MHz
M1,000,0001M1 MHz
G1,000,000,0001.5G1.5 GHz
ch> info
Board: NanoVNA-H
2019-2025 Copyright NanoVNA.com
Version: 1.2.44
ch> sweep 1M 100M 101
ch> scan 7
1000000 0.001234 0.005678 0.000123 -0.000456
2000000 0.001245 0.005689 0.000134 -0.000467
...
ch>

ASCII output with space-separated floating-point values:

[frequency] [S11_re S11_im] [S21_re S21_im]\r\n

Example:

1000000 0.998234 -0.023456 0.001234 0.005678
2000000 0.997123 -0.024567 0.001345 0.005789
  • Values are IEEE 754 single-precision floats formatted as ASCII decimal
  • Each line terminated with \r\n
  • Fields separated by single space

Enabled by setting bit 7 (SCAN_MASK_BINARY) in the scan mask.

Header:

uint16_t mask; // Output mask (same as argument)
uint16_t points; // Number of sweep points

Per-point data (based on mask bits):

// if SCAN_MASK_OUT_FREQ (bit 0):
uint32_t frequency; // 4 bytes, little-endian
// if SCAN_MASK_OUT_DATA0 (bit 1):
float s11_re; // 4 bytes, IEEE 754 LE
float s11_im; // 4 bytes, IEEE 754 LE
// if SCAN_MASK_OUT_DATA1 (bit 2):
float s21_re; // 4 bytes, IEEE 754 LE
float s21_im; // 4 bytes, IEEE 754 LE

The scan command accepts a bitmask argument controlling output:

BitMaskNameDescription
00x01SCAN_MASK_OUT_FREQInclude frequency in output
10x02SCAN_MASK_OUT_DATA0Include S11 (CH0) data
20x04SCAN_MASK_OUT_DATA1Include S21 (CH1) data
30x08SCAN_MASK_NO_CALIBRATIONSkip calibration correction
40x10SCAN_MASK_NO_EDELAYSkip electrical delay
50x20SCAN_MASK_NO_S21OFFSSkip S21 offset
70x80SCAN_MASK_BINARYBinary output mode
MaskHexOutput
30x03Freq + S11 (text)
70x07Freq + S11 + S21 (text)
1350x87Freq + S11 + S21 (binary)
150x0FFreq + S11 + S21, no cal (text)
Terminal window
# Freq + S11 only (text)
scan 3
# Freq + S11 + S21 (text, most common)
scan 7
# Freq + S11 + S21 (binary, for programmatic use)
scan 135
# S11 + S21 without cal correction (raw ADC values)
scan 15
# Custom range: 1 MHz to 100 MHz, 201 points, output S11 only
scan 1M 100M 201 3

┌──────────────────────────────────────────────────────────────┐
│ Header (4 bytes) │
├─────────────────┬────────────────────────────────────────────┤
│ mask (uint16) │ points (uint16) │
├─────────────────┴────────────────────────────────────────────┤
│ Point 0 Data │
├──────────────────────────────────────────────────────────────┤
│ [freq uint32] [s11_re float] [s11_im float] │
│ [s21_re float] [s21_im float] │
├──────────────────────────────────────────────────────────────┤
│ Point 1 Data ... Point N-1 Data │
└──────────────────────────────────────────────────────────────┘

Byte order: Little-endian (ARM Cortex-M native)

import struct
import serial
def scan_binary(port, start_hz, stop_hz, points):
"""Perform binary scan and return parsed data."""
ser = serial.Serial(port, timeout=5)
# Send scan command with binary flag (0x87 = freq + S11 + S21 + binary)
cmd = f"scan {start_hz} {stop_hz} {points} 135\r\n"
ser.write(cmd.encode())
# Read header
header = ser.read(4)
mask, num_points = struct.unpack('<HH', header)
# Calculate bytes per point
bytes_per_point = 0
if mask & 0x01: bytes_per_point += 4 # freq
if mask & 0x02: bytes_per_point += 8 # S11
if mask & 0x04: bytes_per_point += 8 # S21
# Read all point data
data = ser.read(bytes_per_point * num_points)
# Parse points
results = []
offset = 0
for i in range(num_points):
point = {}
if mask & 0x01:
point['freq'] = struct.unpack('<I', data[offset:offset+4])[0]
offset += 4
if mask & 0x02:
point['s11'] = complex(*struct.unpack('<ff', data[offset:offset+8]))
offset += 8
if mask & 0x04:
point['s21'] = complex(*struct.unpack('<ff', data[offset:offset+8]))
offset += 8
results.append(point)
# Read past prompt
ser.read_until(b'ch> ')
ser.close()
return results

Alternative to USB, available on exposed pins.

SignalPin (F072)Pin (F303)
TXPA9PA9
RXPA10PA10
GNDGNDGND
ParameterDefaultRange
Baud rate3840019200 - 3000000
Data bits8-
Stop bits1-
ParityNone-
Flow controlNone-
19200, 38400, 57600, 115200, 230400, 460800, 921600, 1843200, 2000000, 3000000

Configure via menu: Config → EXPERT SETTINGS → SERIAL SPEED Or shell command: usart_cfg {baudrate}

Terminal window
# Enable serial console (disables USB console)
usart 1
# Disable serial console (enables USB console)
usart 0

The VNA_MODE_CONNECTION flag controls which console is active.


If the device doesn’t respond:

  1. Check device enumeration:

    • Linux: lsusb | grep 0483:5740
    • macOS: system_profiler SPUSBDataType | grep -A5 NanoVNA
    • Windows: Check Device Manager
  2. Check permissions:

    • Linux: User in dialout group?
    • macOS: May need explicit terminal permissions
    • Windows: Correct COM port selected?
  3. Send empty line: Send \r\n to get a fresh ch> prompt

ErrorCause
(no output)Unknown command
usage: ...Missing or invalid arguments
too many arguments, max 4More than 4 arguments

If binary data appears corrupted:

  • Ensure no other application is accessing the port
  • Check byte order (little-endian)
  • Verify mask matches expected fields
  • Account for all bytes in header before point data

ComponentFileLines
USB descriptorsusbcfg.c39-137
String descriptorsusbcfg.c150-199
Unique ID generationusbcfg.c201-228
Shell protocol constantsmain.c43-50
Scan mask definitionsmain.c1371-1377
Binary output logicmain.c1434-1441
Text output logicmain.c1442-1448
Command tablemain.c2912-3033
USART configurationmain.c3064-3111