User Tools

Site Tools


jms_x11:firmware


This is an old revision of the document!


Jump Star GNSS X11 Firmware Analysis

jump back to the main X11 page

This page is what I have worked out from the MCU firmware so far. Almost all of it comes from reading the GD32F303 flash dump in a disassembler. A smaller part comes from live tests over Bluetooth and from a few helper scripts. Where it matters I say which kind of evidence a claim rests on:

  • In the code - I read it in the disassembly. The address is given.
  • Observed - I saw it happen on the live unit.
  • Inferred - my reading of the code. I have not tested it.

Treat the addresses as a map, not as gospel. Some were read by eye and some are still being checked.

Status as of 2026-11-06: the firmware is read and mostly mapped. The Winbond flash has not been read yet. The first read of the config block is scripted and waiting for the unit to be powered.

The dump

Item Value
Chip GigaDevice GD32F303VCT6
Flash dump 262,144 bytes (256 KB). sha256 bf782a78c5abf63e4fef54c54ed8d13134f9c9c041bff3a56114eb34c3530724. Two dumps, identical.
Readout protection Off. Option bytes at 0x1FFFF800 read ffff5aa5; the RDP byte is 0xA5.
Image size The last non-0xFF byte is at 0x31756. About 197 KB of the 256 KB is used.
External flash Winbond W25Q128 (16 MB). The firmware reads it through its own routine. See Winbond flash below.

I dumped the flash over SWD with an RP2040 debug probe. The read was done with the core halted, and nothing was written.

Boot and application layout

Internal flash holds two images:

  • 0x08000000 - 0x08000FFF: a 4 KB bootloader. Reset vector at 0x08000D9C. In the code.
  • 0x08001000 onward: the application. Initial stack pointer 0x2000AE70, reset handler 0x080304D4. In the code.

The bootloader looks at Winbond address 0x030000 for the ASCII string PRGAMOK. If it finds it, the bootloader erases the application area, reads a new image from Winbond address 0x032000, XORs each 32-bit word with 0x4E4E4E4E, programs the result into flash, and then clears the flag. This is almost certainly how a firmware update is installed. In the code (flow read); not exercised. On my unit the staging area at 0x032000 is all 0xFF, so no update image is staged.

Startup of the application runs through 0x080304D4, then SystemInit (0x08001200, inferred), then the C runtime at 0x08030C34. That enables the FPU and calls main at 0x0801CD7C.

Main loop

main is not a set of one-time init calls followed by sleep. It is a cooperative loop. Each pass writes a step number to RAM 0x20007E41, runs one task, and clears the soft-watchdog counter (more on that under Power and resets). The tasks that matter so far:

Step Function What I think it does
8 0x08018590 Parser B. Reads text commands that arrive on the RS232 port (SP…, CK…, #JMS).
9 0x08027C26 Bluetooth/Wi-Fi module open and close, driven by the enable flags.
20 0x0800405C Reads the UM980 receive buffer and drives the GGA logger to the TF card.
12, 15, 21, 22 0x08015A36, 0x08014D8C, 0x0800C024, 0x0800C08A Run only under certain flag values. Not identified yet.

Full table of the other steps is in my working notes, not here.

Serial ports

The MCU has five USART/UART peripherals. The init order gives them channel numbers 0 to 4 (channel 0 = USART0 … channel 4 = UART4). The UM980 and Bluetooth roles below are read from the code and the send-call sites, and the Bluetooth-to-UM980 path has been confirmed live.

Peripheral Role Evidence
USART0 (IRQ 37) MCU RS232 port, 9600 baud, on the 10 pin connector. Text commands, #JMS commands, and the binary factory-test frames all come in here. The boot log comes out here. In the code (ISR 0x08025DAF). Boot log observed.
UART3 (IRQ 52) UM980 link (channel 3). Receive ring 2500 bytes. In the code: all UM980 commands go out on channel 3. Forwarding from Bluetooth to channel 3 observed live.
UART4 (IRQ 53) FSC-BW246 Bluetooth/Wi-Fi module (channel 4). Receive ring 1500 bytes. In the code. Module traffic observed over SPP.
USART1, USART2 Not identified. Possibly the modem or radio. Not yet.

Receive handling on UART4 has limits that shape everything above it:

  • The ISR only stores bytes. The frame handler runs when 10 ms have passed with no new byte. In the code.
  • The frame handler takes at most 500 bytes per frame. It splits longer bursts at arbitrary byte positions, not at RTCM message boundaries. In the code.
  • Frames under 6 bytes are dropped. In the code.
  • A burst longer than 1500 bytes with no 10 ms gap overwrites its own start. In the code, and the overrun was observed in a live buffer dump.

Bluetooth to UM980 forwarding

The frame handler at 0x08026BD0 decides what happens to each Bluetooth frame. Three RAM flags control it:

  • Flag C (0x20007DA0). Set by a frame that starts with +++, cleared by one that starts with —. While it is set, any frame that is not a known command and is at least 6 bytes long goes out to the UM980 unchanged. Observed. A text command sent this way got a reply from the UM980, and RTCM replayed through this path decoded on the UM980's COM2 status log. The flag is RAM only, so a reboot clears it.
  • Flag B (0x20007DE7) is GNSS.PASS. When it is ON, every frame goes to the UM980, including frames that are known JMS commands. In the code. The chain from the JMS SET handler to this flag is confirmed. Not yet tested live.
  • Flag A (0x20007E23) is the base/rover flag. In rover mode the code has a path that sends the payload of a module-framed data packet (+SPPDATA=len,…) to the UM980. That path never fires on this unit, because the module runs in transparent mode and delivers raw bytes. Observed: raw bytes arrived with no header.

So the vendor's own “phone internet” corrections path appears not to work on this hardware. The route that works is +++ followed by raw data. Observed for RTCM at modest rates; see the limits above.

Bluetooth module

At boot the MCU runs a short AT sequence on UART4: AT+VER, AT+ADDR, AT+STAT, AT+NAME, AT+ROLE=1, AT+TPMODE=1. The reply to AT+VER decides the receive mode. BT986 gives mode 1, BW236 gives mode 2, BW246 gives mode 3. This unit reports FSC-BW246,V5.1.3, so it runs in mode 3. In the code and observed in the boot log.

The firmware contains no AT command that configures Wi-Fi. There is no SSID, password, or AP/STA setting anywhere in the image. The only Wi-Fi string is AT+WFSEND=0,. In the code.

Corrections: what the limits did to the test

These are numbers from my own tests, not from the code. Observed.

  • A one-message-per-write relay at 20 messages per second lost between 8% and 71% of messages, depending on type.
  • At 0.15 s spacing about 95% of MSM messages arrived.
  • A live MSM7 stream, paced at one message per write, decoded between 32% and 80% of messages per type. The relay fell behind and the backlog grew.

Those numbers are why the Bluetooth route is not yet a reliable correction path. The UM980 solution also stayed at quality 1 with Bluetooth corrections, while the same corrections over RS232 produced RTK float. That points at the UM980 side rather than the MCU link. See lead A under Open questions.

Config store

Everything that survives a power cycle lives in one 2048-byte block in the Winbond. There are two copies:

  • Primary at Winbond 0x000000.
  • Backup at Winbond 0x390000.

Every setter writes both copies. At boot the loader at 0x0800D14C to 0x0800D67C copies a fixed set of indexes into RAM variables. The accessors are:

Function Address What it does
read byte 0x0800CF78 Read one byte at an index
write byte 0x0800CDDC Write one byte, both copies
write 16 bit 0x0800CE50 Big-endian 16-bit write
write n bytes 0x0800CED4 Write a run of bytes
read n bytes 0x0800D08E Read a run of bytes

Some settings and where they live. Index numbers are byte offsets into the 2048-byte block:

Key Index RAM variable Note
GNSS.MODE 0x6CD 0x20007E23 0 = MOVE (rover), 1 = BASE
GNSS.ENABLE 0x48D 0x20007E29
GPRS.ENABLE 0x6FA 0x20007E25
BLE.ENABLE 0x786 0x20007D9B
WIFI.ENABLE 0x79B 0x20007D9C
WIFI.NAME 0x79C (string)
RADIO.ENABLE 0x7BB 0x20007E19
Backup server record 46
Main server record 55
NTRIP record 1300

The header is the one open discrepancy in this section. The reader scans for A5 5A. The probe read on the laptop at Winbond 0x000000 returned 00 00 AA 55 05 43 4D 49 4F 54, which is the APN record CMIOT. I do not yet know how these two fit together. The full read will settle it.

The $JMS,BASE status line

The unit sends a status line about once a second. In the code it is built at 0x080026EC. The fields I have decoded:

# Value What it is
1 V1.03-250220 Protocol version. Same as the BASE.PROTOCOL key.
2 13900063393 Serial number.
3 FY-X11-GGA-V0.30-251013 Software version.
4 X11-V5-20241228 Hardware version.
5 24 Board temperature in degrees C, one byte from RAM.
6 42 Battery percent: (adc - 325) * 10 / 9, clamped to 0 to 100, where adc is the battery sense reading on PC3. In the code.
7 0 1 when the external DC input is at or above 7.0 V. The sense is on PC4 through an 11:1 divider. This settles the field the wiki page left open. In the code.
8 0 Corrections link flag. Set to 1 when request id 50 returns 1, and cleared by the keep-alive monitor. Which peer it refers to is not identified yet. In the code, partly inferred.
9 to 13 OFF ON ON OFF OFF GPRS, GNSS, BLE, WIFI, and RADIO enable flags, in that order. In the code, matches the key dump.

“BASE” in the line is only the section name. The field does not say whether the unit is a base.

Base and rover

The mode is RAM flag A (0x20007E23): 0 = rover, 1 = base. Setting GNSS.MODE changes it and writes index 0x6CD. The MCU then sends its GNSS setup to the UM980 again. The UM980 keeps its own base/rover configuration, so the MCU's RAM flag is only what the firmware itself remembers about the mode. In the code, inferred for the UM980 side.

The #JMS command parser

The parser at 0x0801C6A0 handles the #JMS frames. The frame layout is:

#JMS,GET,GNSS.MODE*xx
#JMS,SET,GNSS.MODE,MOVE*xx

Each frame is under 201 bytes. The checksum is the XOR of the body, written as two uppercase hex digits. Replies start with @JMS,. A HEART command sets a link-alive byte (0x20007D9A), which is what $JMS,LINK prints. Group dispatch covers BASE, GPRS, GNSS, BLE, WIFI, and RADIO. Anything else returns CMD ERR. In the code.

The same parser reads X11-CONFIG.txt from the TF card (see below). The line syntax is identical.

Reply routing is not settled. For the TF card path my notes say replies go out on USART0. Over Bluetooth and RS232 I have seen replies come back on the link the command arrived on. See Open questions.

TF card slot

The datasheet says the slot is reserved. The firmware uses it, in SPI mode, with FatFs.

  • Hardware: SPI3. SCK PB3, MISO PB4, MOSI PB5, chip select PA15, card detect PC7 (low = card present). Mode 3. The prescaler starts at /256 for card init and then goes to /64 or /2. In the code.
  • On card insertion, after about two seconds, the firmware mounts the card and opens X11-CONFIG.txt in the root directory. It reads the file in 100-byte pieces and sends each piece through the #JMS parser. It runs once per insertion. In the code.
  • The same firmware writes GGA logs to the card as GGA-*.txt files. The writer is at 0x0800E7D0 and is driven from the main loop. What data goes into the log I have not traced. In the code.

Because the card path uses the same parser and the same setters, a correctly checksummed #JMS,SET line in X11-CONFIG.txt would persist exactly like one sent over Bluetooth. Inferred. Not run.

Text commands

The firmware has a family of text commands. They are reachable from the RS232 port, and the code placement suggests from Bluetooth too. Reachable over Bluetooth is in the code; not tested live.

Prefix Password (plain text in image) Role State of decoding
<SPTF…> TFGPS Set JT808 platform parameters, fields as A C I K N P S T X p t x y Fields not decoded
<CKTF> - Report the SPTF values -
<SPGD…> GDGPS Set. Fields E N P X Y Z Purpose not decoded
<SPYC…> YCGPS Set NTRIP client settings. *r:0 selects NTRIP, *r:1 selects QXSDK Partly decoded
<CKYC> - Report the SPYC values -
<CKINFO>, <INFO> - Information report Not decoded
<INS: - Not decoded -
<CK-SIXENTS>, <SIXENTS - Third-party service report and setting Not decoded
<RETTF> - Reboot (“Reset OK!”) Reachable by code placement; not run
<RECTF> - Factory reset Destructive. See below
<EXT - Firmware update entry Do not send
<GNSS_UPDATE, <RADIO_UPDATE - Module firmware pass-through Do not send

The binary frames on RS232 (a $ start byte, a 0xFF end byte, and an XOR checksum) are a factory production-test protocol. Commands 0xED, 0xEE, and 0xEF exist, and 0xED and 0xEF run the factory reset. Do not send them. In the code.

Factory reset

<RECTF> calls 0x0800D67E. The routine:

1. Writes ''AA 55'' to bytes 0 and 1 of the config block.
2. Zeroes config indexes 65 to 2039.
3. Writes the defaults. The APN becomes ''CMNET''. The JT808 identity becomes placeholders (HT-600, 13800180001, plate "Yue A88888"). GNSS.MODE becomes MOVE. The enable flags, including GNSS.ENABLE and BLE.ENABLE, become 0.
4. Writes the block to both copies.
5. Erases Winbond 4 KB sectors at 0x002000, 0x00A000 to 0x011FFF, 0x016000 to 0x01FFFF, and 0x340000 to 0x344FFF (the 0x343000 sector is not in the list).

It does not touch the boot flag at 0x030000 or the staging area at 0x032000, so a reset cannot break the boot path.

Two consequences I have not tested. First, with GNSS.ENABLE and BLE.ENABLE both 0 after a reset, the unit may come up with no Bluetooth and no GNSS output, and would need an RS232 connection to recover. Inferred. Second, the APN CMIOT on this unit is written by a different reset path, one that the binary factory-test commands reach. So the unit was probably provisioned on a bench through that path. Inferred.

Do not send <RECTF> without reading the config block first and keeping a copy.

Power and resets

Three things can restart or power off the MCU. I can name the mechanism for each, but I have not caught all of them in the act.

  • Low battery. The firmware reads the battery sense on PC3. If it is below 3.3 V, the code prints LOW POWER :%d and resets through AIRCR SYSRESETREQ at 0x0802BA9E. In the code. With the battery pack disconnected, PC3 reads about 0.55 V, and this fires about 9 seconds after power-up. Observed on the bench, 2026-11-06.
  • Soft watchdog. A 1 ms tick (TIM2) increments a counter at 0x20007D44. The main loop clears it. When the counter passes the limit at 0x20000922 (10000 on my unit), the firmware prints Soft Watch Dog Time Out! and resets through 0x08015A00. Idle peak was 22 ticks. In the code; limit and idle peak observed. This is the best candidate for the unexplained restarts, but I have not proved it.
  • Remote reset commands. <RETTF> and the JT808 server command both reset the unit. See Text commands and Factory reset.

At boot the firmware prints its reset cause (POR/PDR, software, low-power, PIN, independent watchdog, window watchdog). It clears the RCC reset flags itself, so the cause cannot be read back over SWD after boot. Capture the RS232 port during a test to see the message. In the code.

Winbond flash

The external flash is a W25Q128 on SPI1 (PA5, PA6, PA7), with chip select PA4 driven by software. The firmware's read routine at 0x0801E442 uses only opcodes 0x03 (read) and 0x05 (status). The write and erase routines exist in the image (opcodes 0x06, 0x02, and 0x20), and factory reset uses the 0x20 sector erase. In the code.

Known regions, from the code:

Address Contents
0x000000 Config block, primary copy (2 KB)
0x002000 Data store, erased by factory reset. Contents unknown.
0x030000 Boot flag, PRGAMOK when an update is pending
0x032000 Staged update image, XOR 0x4E4E4E4E
0x00A000 to 0x011FFF Data store, erased by factory reset. Contents unknown.
0x016000 to 0x01FFFF Data store, erased by factory reset. Contents unknown.
0x390000 Config block, backup copy (2 KB)
0x395000 Small record starting AA 55
0x340000 to 0x344FFF Data store, erased by factory reset. Contents unknown.

Observed on my unit: 0x032000 is all 0xFF, so there is no staged update.

The full image has not been read. A capture is scripted (swd_capture.py) to read the two config copies and a few probe ranges in one halted session, because the unit's low-power shutdown gives a window of about nine seconds. The full 16 MB read is not authorised yet.

Open questions

These are the things I could not settle from the code, and the things I think are most useful to check next:

  • Lead A: interfacemode. Nine call sites set UM980 COM1 to either interfacemode com1 compass compass on (six sites) or interfacemode com1 auto auto on (three sites). If “compass” makes COM1 ignore RTCM, that would explain why Bluetooth corrections decode on the UM980 but never change the solution. Not tested. Test by sending the auto form from the COM2 terminal, then repeating the Bluetooth correction test.
  • Lead C: the cellular NTRIP client. The MCU has its own NTRIP and JT808 clients over the modem. They write received corrections to the UM980 through the same kind of path as Bluetooth. Worth comparing that path with the Bluetooth one.
  • GNSS.PASS (flag B). The chain from the SET handler to the flag is confirmed. The live behaviour is not.
  • Field 8 recipient. The corrections-link flag is set by request id 50 through the port table at 0x20000890. The recipient UART is not identified.
  • Config header. The disassembly says A5 5A. The laptop probe shows 00 00 AA 55 at offset 0. Needs the full read.
  • Factory reset sector list. One of my notes lists a sector set at 0xC000. The other lists 0x340000 to 0x344FFF. The second matches the code I read most recently. Check before relying on either.
  • Reply routing. My notes disagree on whether #JMS replies go out on USART0 or on the link the command came in on. Test with one command over each link.
  • Undecoded. The field tables for SPTF, SPGD, CKINFO, INS, and SIXENTS. The 01, 0A, 0D, and 37 arms of the binary test protocol. The driver pointer table at 0x20000208 (open +88, close +96, poll +100) is in a compressed blob in flash, so its values have to be read from RAM.
  • Startup flag 0x2000092C. Its initial value is inside the compressed startup blob. Read it from RAM.

Tools

The analysis tools live with the firmware dump in the project directory:

  • fwtool.py - disassembly walker, cross-references, callers, string search, and literal pool resolution. Subcommands: dis, xref, callers, findstr, str, hex, word, func.
  • fw1.dis - the full listing, generated with objdump.
  • swd_capture.py - the one-halt capture chain for the Winbond config blocks and probes. Written for the low-power shutdown window.
  • winbond_dump.py - the original read-only Winbond reader, which calls the firmware's routine over SWD.
  • ocd_peek.py and wd_watch.py - read-only RAM peeks for the Bluetooth flags, UART ring, and watchdog counter.

The JMS and RTCM test tools (spp2tcp.py, rtcm_relay.py, rtcm_replay.py) are described on the Software page.

jms_x11/firmware.1793976488.txt.gz · Last modified: by millerjs

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki