Audience: iOS engineers building a custom app that connects to a Columbus P-70 Ultra RTK GNSS receiver over Bluetooth Low Energy
Platform: iOS 15+ recommended (CoreBluetooth)
Receiver: Columbus P-70 Ultra (BK / Polaris BK166X family — not u-blox)
Status: Field-proven integration notes for GATT connect, NMEA download, RTCM uplink, Rover/Base role HEX, fixed Base ASCII, and Flash save
This document is a neutral third-party integration guide. It describes the BLE interface and the BK / $POLCFG… / NMEA / RTCM traffic needed to use P-70 Ultra from your own iOS app. Official Columbus manuals and the P-70 Ultra interface description remain the primary references for product policy and chip protocol details. This guide explains what to do; implement the CoreBluetooth and protocol details in your own code.
Related guide: The Columbus EX-1 iOS BLE Developer Guide covers the same BLE transport family on a u-blox ZED-F9P receiver. Do not reuse EX-1 UBX (
CFG-TMODE3,CFG-MSG,CFG-CFG,NAV-PVT,NAV-SVIN) on P-70.
Table of contents
- What you can build
- Architecture overview
- Critical: which BLE name to use
- GATT profile
- iOS project setup
- Minimal path: scan → connect → NMEA
- Implementing the BLE client
- Parsing the notify stream
- NMEA position parsing
- RTK Rover: write RTCM3 corrections
- Command framing: BK HEX and
$POLCFG… - Role configuration: Rover / Base
- Permanent save (
$POLCFGSAVE) - Recommended transaction rules
- Accuracy estimates (HPPOSLLH)
- Output rate (1 / 5 / 10 / 20 Hz)
- Pitfalls checklist
- EX-1 vs P-70 quick diff
1. What you can build
| Level | Capability |
|---|---|
| A | Discover P-70 Ultra, connect, receive NMEA, show lat/lon/fix |
| B | Inject RTCM3 corrections over BLE (RTK Rover) |
| C | Switch Rover / Base with official BK HEX sequences; wait for automatic Base lock (GGA quality 7); optionally set a fixed Base with $POLCFGBASE, then optionally $POLCFGSAVE |
You do not send NavStarTool JSON (Config_Rover_*.Json / Config_Base.Json) over BLE. Those files are for the Windows tool over USB / SPP. Live configuration on BLE uses BK binary frames (42 4B …) and ASCII $POLCFG… lines written to the same characteristic that carries NMEA.
2. Architecture overview
iOS App (Central) ←── Notify FFF1: NMEA (+ BK / RTCM) ── P-70 Ultra NTRIP BLE
─── Write FFF1: RTCM3 and/or BK / $POLCFG… ───► Service FFF0 / Char FFF1
- iPhone / iPad = BLE Central (
CBCentralManager) - P-70 Ultra = BLE Peripheral
- Downlink (receiver → app): primarily NMEA 0183 text (v4.11 family); may be interleaved with BK binary (sync
42 4B) and RTCM3 (preambleD3) when Base / raw modes are active - Uplink (app → receiver): RTCM3 binary for corrections, and/or BK HEX /
$POLCFG…for configuration
Baud rate of the internal UART behind the BLE bridge is not something your app sets (factory default on the wired channel is often 230400). Use ATT MTU / maximumWriteValueLength for write chunking.
Chip protocol note: P-70 Ultra uses a BK / Polaris command set. EX-1’s u-blox UBX path does not apply.
3. Critical: which BLE name to use
P-70 Ultra advertises two Bluetooth interfaces:
| Advertised name | Use for your app? | Notes |
|---|---|---|
P-70 Ultra NTRIP BLE |
Yes — always use this | Supports NMEA download and RTCM3 / command write (full RTK workflow) |
P-70 Ultra BLE (no NTRIP) |
No (for RTK / config) | Positioning-only channel; do not rely on it for RTCM uplink or role writes |
Matching rule: connect only when the advertised / peripheral name has prefix P-70 Ultra NTRIP BLE.
Do not identify P-70 only by service UUID FFF0. Several Columbus models share FFF0 / FFF1. Always combine:
- Name prefix
P-70 Ultra NTRIP BLE - Then discover service
FFF0and characteristicFFF1
Official naming length reminder: P-70 Ultra NTRIP BLE is the NTRIP channel; P-70 Ultra BLE is the short non-NTRIP name.
4. GATT profile
| Item | Value |
|---|---|
| Service UUID | 0000FFF0-0000-1000-8000-00805F9B34FB (16-bit FFF0) |
| Characteristic UUID | 0000FFF1-0000-1000-8000-00805F9B34FB (16-bit FFF1) |
| Notify / Indicate | Required for GNSS stream |
| Write | Same characteristic; P-70 documents Notify + Write Without Response |
Documented Columbus behavior for this family: one characteristic for both directions (UART-over-GATT style). The BLE hardware design is in the same family as EX-1; the command language behind the bridge differs.
Connection sequence:
- Scan peripherals (do not filter by service UUID alone).
- Keep only names with prefix
P-70 Ultra NTRIP BLE. - Connect; discover service
FFF0, then characteristicFFF1. - Verify notify or indicate is available; enable notifications.
- Start buffering and parsing notification data.
- Use a connection timeout around 20 seconds.
5. iOS project setup
Info.plist
- Set
NSBluetoothAlwaysUsageDescriptionto a clear user-facing reason (connect to a Columbus P-70 Ultra GNSS receiver). - Optionally add
UIBackgroundModes→bluetooth-centralif you need brief background streaming or reconnect.
Capabilities
- Enable Background Modes → Uses Bluetooth LE accessories only if you need background central reconnect / notify.
- Background scanning is restricted on iOS; plan for foreground discovery and optional restore identifier if you need reconnect after kill/relaunch.
Entitlements / privacy
No special Apple entitlement beyond the standard Bluetooth usage description is required for central mode. Test on a physical iPhone; Simulator is not useful for real P-70 hardware.
6. Minimal path: scan → connect → NMEA
Goal: show a live fix outdoors.
- Power on P-70 Ultra with clear sky view.
- Scan and select
P-70 Ultra NTRIP BLE. - Subscribe to
FFF1notifications. - Buffer bytes into lines ending with CR/LF.
- Parse GGA / RMC sentences (talker may be
GN,GP,GL, etc.). - Display latitude, longitude, altitude, fix quality.
Factory / default Rover-style profiles typically emit RMC / GGA / GSA / GSV. Example address forms: $GNGGA, $GNRMC.
You may also see Polaris proprietary $… / $BKCHIP,… lines; treat them as identity / diagnostics, not as a substitute for GGA.
7. Implementing the BLE client
Build a small CoreBluetooth central that:
- Creates
CBCentralManagerand waits for.poweredOn. - Scans without relying on service UUID filters for discovery (name filter is primary).
- Connects the chosen peripheral and discovers
FFF0/FFF1. - Enables notify on
FFF1and forwards each update to your parsers. - Exposes a write path that chunks payloads with
maximumWriteValueLength(for:)(floor around 20 bytes is a safe minimum if the stack reports a tiny value).
Write type guidance: P-70’s NTRIP BLE characteristic is documented as write without response. Prefer that property when present. If a firmware build also exposes with-response, either is acceptable for short ASCII commands; for continuous RTCM, without-response is the expected path. Always fall back to whichever write property exists.
Cancel an in-flight connect after ~20 seconds if the link never reaches connected + notifying.
After connect, P-70 may need 1–3 seconds before a steady NMEA stream appears — do not treat the first quiet window as an immediate data-silence failure.
8. Parsing the notify stream
Notifications are not guaranteed to be one NMEA sentence per callback. A single callback may contain a partial sentence, multiple sentences, or NMEA mixed with BK frames and/or RTCM3.
Practical demux strategy
- Position-only app: keep printable ASCII plus CR/LF; replace other bytes with a line break so binary traffic does not corrupt UTF-8 line assembly; emit complete lines that start with
$. - Base / command ACK / RTCM verification / accuracy: keep a raw byte buffer and run parallel framers:
- NMEA / ASCII: starts with
$, ends with\r\n(or detect$OK/$FAILtokens even when fragmented) - BK: sync
42 4B, then class/length/payload per the interface description - RTCM3: preamble
D3, 10-bit length, payload, 24-bit CRC - Optional accuracy frames: BK
BK_PNT_NAVand/or a UBX-compatible HPPOSLLH-shaped frame (see §15)
- NMEA / ASCII: starts with
Never force an entire notification through UTF-8 decoding without filtering.
9. NMEA position parsing
Coordinate format
NMEA lat/lon fields are ddmm.mmmm / dddmm.mmmm, not decimal degrees. Convert: degrees = floor(value / 100), minutes = remainder, decimal = degrees + minutes / 60; negate for S/W.
GGA fields (common)
| Index | Meaning |
|---|---|
| 1 | UTC time hhmmss.ss |
| 2–3 | Latitude + N/S |
| 4–5 | Longitude + E/W |
| 6 | Fix quality (0 invalid, 1 GPS, 2 DGPS, 4 RTK fixed, 5 RTK float, 7 Base mode, …) |
| 7 | Satellites |
| 8 | HDOP |
| 9 | Altitude (often orthometric / MSL — check your product notes) |
| 11 | Geoid separation |
P-70-specific: GGA quality 7 means the receiver is in Base mode (automatic survey lock or fixed Base). That is the primary Base-readiness signal — there is no u-blox NAV-SVIN on P-70.
RMC
Use status field A (active). Provides speed (knots) and course.
Talker IDs
Parse by the last three characters of the address field (GGA, RMC, …), not by assuming $GN only.
10. RTK Rover: write RTCM3 corrections
Once notify is active on P-70 Ultra NTRIP BLE:
- Connect your app to an NTRIP caster (TCP) as a client.
- Decode the HTTP response body to a pure RTCM3 byte stream (if the caster uses HTTP chunked encoding, strip chunk framing first).
- Write those bytes to
FFF1, preserving packet order and chunking to the platform writable length.
No special “start RTCM” init command is required before uplink. Ensure the chip is in Rover mode (not Base) before injecting corrections.
Write tips
| Topic | Recommendation |
|---|---|
| Characteristic | Same FFF1 |
| Write type for RTCM | Write without response when that is what the characteristic exposes |
| Chunk size | Use maximumWriteValueLength — never assume 20 forever; official notes say there is no special chunk policy beyond the stack default |
| Ordering | Preserve RTCM packet order |
| During Base / Survey lock | Do not inject Rover corrections into a unit that is still locking Base (GGA quality heading toward / at 7 with RTCM output) |
How you know RTK is working
Watch GGA fix quality: 4 = RTK Fixed, 5 = RTK Float, 1 / 2 = single / DGPS. Also watch age of differential in GGA when present.
11. Command framing: BK HEX and $POLCFG…
P-70 configuration over BLE uses two families:
A. BK binary frames
Sync prefix 42 4B, followed by length / class / payload / checksum fields as defined in the P-70 Ultra interface description (document series such as UG01-BK166X-…).
For App work you normally paste official HEX sequences from Columbus (Common Commands / command table) rather than hand-building every CFG key. Successful apply replies with ASCII $OK; failure replies with $FAIL.
B. ASCII plaintext commands
CRLF-terminated lines, for example:
| Command | Purpose |
|---|---|
$POLCFGBASE,<lat>,<lon>,<height>\r\n |
Set fixed Base coordinate (immediate) |
$POLCFGBASE,0,0,0\r\n |
Clear fixed Base |
$POLCFGSAVE\r\n |
Save current configuration to Flash |
$POLCFGPTVER\r\n / $POLCFGPTSYS\r\n |
Query helpers (firmware-dependent) |
Important: $OK / $FAIL can span BLE notifications. Buffer raw bytes and search for the tokens $OK and $FAIL (prefer detecting $FAIL before treating a bare $OK substring incorrectly if your buffer is overly greedy).
Suggested ACK timeout: 10–15 seconds on BLE; one bounded retry on timeout only.
One command (or one logical HEX step) in flight: wait for $OK / $FAIL (or timeout) before the next exclusive configuration write. Do not interleave RTCM uplink or optional accuracy-enable HEX with a POLCFG transaction.
Equivalent Flash-save HEX (same effect as $POLCFGSAVE):
42 4B CC 17 26 04 00 00
Official Rover/Base HEX tables often append this save frame at the end. For lab / temporary Apply UX, omit the trailing save and only send $POLCFGSAVE after user confirmation (recommended).
12. Role configuration: Rover / Base
12.1 Official HEX sequences (temporary Apply — without trailing save)
Use the same payloads as Columbus Common Commands, but drop the final 42 4B CC 17 26 04 00 00 when you want RAM-only apply.
Rover Mode (1 Hz) — recommended default
42 4B AC 06 26 02 00 0C 55 55 00 01 00 00 00 03 00 00 00 01
42 4B 25 00 26 02 00 0C 55 55 00 01 00 00 00 07 00 00 00 01
42 4B 99 5A 26 02 00 0C 55 55 00 01 00 00 00 0C 00 00 00 04
42 4B 73 8F 26 02 00 0C 55 55 00 01 00 00 00 0D 00 00 00 00
42 4B F4 4A 26 02 00 0C 55 55 00 01 00 00 00 31 00 00 00 00
42 4B C9 D8 26 02 00 0C 55 55 00 01 00 00 00 0E 00 00 00 27
Base Mode (MSM4 — recommended)
42 4B AC 06 26 02 00 0C 55 55 00 01 00 00 00 03 00 00 00 01
42 4B 25 00 26 02 00 0C 55 55 00 01 00 00 00 07 00 00 00 01
42 4B 99 5A 26 02 00 0C 55 55 00 01 00 00 00 0C 00 00 00 04
42 4B B2 03 26 02 00 0C 55 55 00 01 00 00 00 0D 00 00 00 0C
42 4B C4 29 26 02 00 0C 55 55 00 01 00 00 00 31 00 00 00 03
42 4B 46 8E 26 02 00 0C 55 55 00 01 00 00 00 10 00 00 00 01
42 4B A9 1E 26 02 00 0C 55 55 00 01 00 00 00 0E 00 00 00 21
An MSM7 Base variant also exists in official tables. Prefer MSM4 for interoperability unless you control both Base and Rover stacks. Do not mix MSM4 and MSM7 casually on a shared caster.
Send one HEX frame at a time, wait for $OK, then the next.
12.2 Automatic Survey-In (no duration / accuracy knobs)
Unlike EX-1, P-70 Ultra does not expose adjustable Survey-In duration or accuracy limits over BLE.
When BASE Mode HEX is applied:
- The chip evaluates whether the current position is accurate enough.
- When ready, it locks the reference and GGA quality becomes
7. - Coordinates should remain stable while quality stays at
7. - You may then optionally refine with
$POLCFGBASE,….
App readiness policy (practical):
- Wait for GGA quality
7. - Require several consecutive epochs and a short stability window (for example ≥3 epochs over ≥3 seconds with horizontal wander under a few centimetres).
- Base mode floods BLE with RTCM, so NMEA may arrive slower than 1 Hz — use a generous stale timeout (on the order of 15 s).
- Only then start NTRIP server upload of Base RTCM extracted from the notify stream.
12.3 Fixed Base ($POLCFGBASE)
$POLCFGBASE,<lat>,<lon>,<height>\r\n
| Field | Rule |
|---|---|
| Latitude | Decimal degrees; north positive, south negative |
| Longitude | Decimal degrees; east positive, west negative |
| Height | Meters. Marketing / web text may say “above mean sea level”; field verification on P-70 Ultra shows ellipsoidal height semantics for $POLCFGBASE (compare against a known site / EX-1 ellipsoidal reference). Send ellipsoidal meters unless Columbus confirms otherwise for your firmware. |
| Clear | $POLCFGBASE,0,0,0\r\n |
Formatting tips:
- Use POSIX /
en_US_POSIXdecimal points (never locale commas). - Keep high precision (apps often use ~9 decimal places for lat/lon and ~4 for height).
- Always terminate with
\r\n.
$POLCFGBASE takes effect immediately in RAM. Persist only with $POLCFGSAVE (next section).
12.4 Rover temporary apply (recommended order)
- Pause RTCM uplink / NTRIP client if active.
- Send Rover HEX sequence (desired rate) without trailing save — serial
$OKeach. - Confirm GGA quality is not stuck at
7(unless a leftover Flash fixed Base is still locking — clear + save + power-cycle may be required). - Start NTRIP client and write RTCM uplink.
12.5 Base temporary apply (recommended order)
- Pause Rover RTCM uplink.
- If a stale Flash
$POLCFGBASEmay exist and you want receiver-managed / automatic survey, clear it ($POLCFGBASE,0,0,0),$POLCFGSAVE, and often power-cycle once so Flash reload is empty. - Send Base MSM4 HEX without trailing save — serial
$OKeach. - Wait for GGA quality
7+ coordinate stability. - Optionally apply
$POLCFGBASE,…for a known fixed point. - Confirm RTCM3 appears in the notify stream; then start NTRIP server upload.
12.6 Two-step UX (strongly recommended)
- Apply temporarily — HEX role + optional
$POLCFGBASE; verify; do not save yet. - Save permanently — only after verification, on user confirmation (
$POLCFGSAVE).
Temporary settings can be lost on power cycle. That is intentional for lab testing.
13. Permanent save ($POLCFGSAVE)
Send either:
$POLCFGSAVE\r\n
or the HEX equivalent 42 4B CC 17 26 04 00 00.
This writes the receiver’s current configuration to Flash so it survives power-off.
Warnings you should surface to your users:
- Do not auto-save.
- Do not save before temporary apply verification.
$OKmeans the command was accepted — still verify after a power cycle if persistence is a product requirement.- A saved
$POLCFGBASEcan re-lock Base after reboot (GGA quality7at the saved coordinate). Clearing for a fresh automatic survey may require clear → save → power-cycle.
Do not brand this as u-blox “BBR/Flash CFG-CFG”. UI copy such as “Save to P-70” / “Save P-70 Configuration” is clearer.
14. Recommended transaction rules
| Rule | Why |
|---|---|
| One exclusive config write in flight | $OK does not identify which command succeeded if several overlap |
| Serial HEX list with per-frame ACK | Prevents false “all done” |
| Pause RTCM uplink during role / POLCFG transactions | Avoid mixed Base/Rover traffic on the shared pipe |
| Pause optional HPPOSLLH enable during POLCFG | Same shared notify / write pipe |
Name gate: P-70 Ultra NTRIP BLE only |
Wrong interface has no reliable RTCM write |
Wait for GGA quality 7 (+ stability) before Base “ready” |
Automatic survey has no NAV-SVIN |
| No Rover RTCM while locking Base | Contaminates base solution |
Separate Apply vs $POLCFGSAVE |
Official HEX tables often include save; apps should not surprise users |
A full Base apply (many HEX frames + readiness wait) over BLE commonly takes tens of seconds. Show progress (command i of n, then “waiting for Base fix”).
15. Accuracy estimates (HPPOSLLH)
Do not treat NMEA HDOP / GST as centimetre RTK accuracy on P-70 Ultra.
Official guidance for this product family: enable high-precision position / accuracy output and read the horizontal / vertical accuracy estimates from that stream (tools may label the view similarly to u-blox HPPOSLLH even though the chip is BK/Polaris).
Temporary enable (do not save)
42 4B BE A5 26 02 00 0C 55 55 00 01 00 00 00 0F 00 00 08 00
Disable (optional):
42 4B 37 0C 26 02 00 0C 55 55 00 01 00 00 00 0F 00 00 00 00
Leaving this unsaved is intentional: it reverts after power cycle and avoids permanent side effects.
Decoder notes
- Keep a raw buffer; frames may be BK
42 4Bnavigation/accuracy messages and/or a UBX-shapedB5 62HPPOSLLH-compatible layout depending on firmware / bridge behaviour. - Validate checksums; handle BLE fragmentation.
- Until a valid accuracy frame arrives, show “—” / waiting — do not fake RTK hAcc/vAcc from HDOP.
16. Output rate (1 / 5 / 10 / 20 Hz)
P-70 Ultra RTK Rover supports 1 / 5 / 10 / 20 Hz via official Rover HEX variants (same structure as §12.1; rate bytes change). Highest rate is 20 Hz; throughput, log size, and battery cost rise quickly.
| Rate | Notes |
|---|---|
| 1 Hz | Default / highest stability for most apps |
| 5 Hz | Vehicles, robots |
| 10 Hz | Higher dynamics |
| 20 Hz | UAV / high dynamics; large BLE load |
Apply rate by sending the matching Rover HEX sequence without trailing save; verify observed GGA cadence; only then $POLCFGSAVE if persistence is required. Prefer changing rate only while confirmed Rover and without an active NTRIP session.
17. Pitfalls checklist
- Connecting to
P-70 Ultra BLEinstead ofP-70 Ultra NTRIP BLE. - Assuming
FFF0alone means P-70 Ultra. - Sending u-blox UBX (
CFG-TMODE3,CFG-MSG,CFG-CFG, …) — wrong chip. - Treating every notify as UTF-8 text.
- Writing RTCM without removing HTTP chunked framing.
- Parallel HEX /
$POLCFG…writes without waiting for$OK. - Expecting adjustable Survey-In duration/accuracy like EX-1.
- Declaring Base ready before GGA quality
7(and stable coordinates). - Using MSL height for
$POLCFGBASEwhen the unit expects ellipsoidal height (verify on your firmware). - Auto-including the trailing Flash-save HEX from official tables during “temporary apply”.
- Hard-coding ATT payload size to 20 forever.
- Expecting Simulator + real BLE hardware to work.
- Changing UART baud from the app “just in case” — do not.
- Leaving a Flash
$POLCFGBASEin place and wondering why automatic survey never restarts after Base start.
18. EX-1 vs P-70 quick diff
| Topic | EX-1 | P-70 Ultra |
|---|---|---|
| GNSS chip / protocol | u-blox ZED-F9P / UBX | BK / Polaris BK166X |
| BLE name (RTK) | EX-1 NTRIP BLE |
P-70 Ultra NTRIP BLE |
| GATT | FFF0 / FFF1 |
Same |
| Config language | UBX binary | BK HEX (42 4B) + $POLCFG… |
| Command ACK | UBX ACK/NAK (05 01 / 05 00) |
$OK / $FAIL |
| Rover / Base | CFG-TMODE3 + CFG-MSG RTCM rates |
Official HEX sequences |
| Survey-In | Configurable duration + accuracy; NAV-SVIN |
Automatic; GGA quality 7 |
| Fixed Base | UBX TMODE3 Fixed (ellipsoidal) | $POLCFGBASE (use ellipsoidal; verify) |
| Permanent save | CFG-CFG BBR+Flash |
$POLCFGSAVE |
| Accuracy estimate | NAV-PVT poll |
Enable HPPOSLLH / BK accuracy output |
| Rover rates (typical) | Product-specific | 1 / 5 / 10 / 20 Hz |
Official Columbus references (keep bookmarked):
- BLE: https://cbgps.com/p70/support/manual/developer/ble/developer_ble_en.htm
- Common commands: https://cbgps.com/p70/support/manual/developer/command/developer_command_en.htm
- Config / NavStarTool: https://cbgps.com/p70/support/manual/config.htm
End of guide.
