Ubxlib GNSS Sample
Overview
A sample application demonstrating how to integrate a u-blox GNSS device into a Zephyr application. The example code is rigidly configured to work with an u-blox M10 device [1]. It was tested with the SparkFun MAX-M10S Breakout [2].
Requirements
The u-blox GNSS device must be connected via UART, and a Devicetree alias
ubxlib-uart0
must exist that point towards this UART port. Also a
single GPIO line for the module reset signal have to define. To fulfil this
requirement, this sample application comes with a generic application overlay
app.overlay
that should be usable with all boards that provies an
Arduino UNO R3 edge connector. For ubxlib-uart0 = /* … */
the
standard Arduino UART on D0 (RX) and D1 (TX) will be used
(ubxlib-uart0 = &arduino_serial
;). The reset signal will be controlled
by D8 (IO8) and is mapped in gpios = <&arduino_header 14 /* … */>
.
To ensure to use the correct GPIO line for the module reset signal, the example
provides the only locally usable Devicetree binding reset-switch.yaml
.
This specifies the Devicetree compatibility string "reset-switch"
(see
line compatible: "reset-switch"
) with the required gpios:
property.
Default application overlay |
Binding for the module reset signal |
---|---|
/ {
reset_switch: reset_switch {
compatible = "reset-switch";
gpios = <&arduino_header 14 GPIO_ACTIVE_LOW>;
};
aliases {
ubxlib-uart0 = &arduino_serial;
};
};
&arduino_serial {
status = "okay";
current-speed = <9600>;
};
|
description: GPIO pin to cause a reset on an external device
compatible: "reset-switch"
properties:
gpios:
type: phandle-array
required: true
description: |
The GPIO pin connected to a reset switch.
|
Regardless of this, it is still free and open to use a specific board overlay to adapt these default settings to other hardware conditions.
Building and Running
Build and flash for different boards
Nordic nRF9160 DK (nRF9160)
west build -b nrf9160dk/nrf9160 -p -d build/nrf9160dk_nrf9160-ubx_gnss bridle/samples/ubx_gnss
west flash -d build/nrf9160dk_nrf9160-ubx_gnss
Nordic nRF52840 DK (nRF52840)
west build -b nrf52840dk/nrf52840 -p -d build/nrf52840dk_nrf52840-ubx_gnss bridle/samples/ubx_gnss
west flash -d build/nrf52840dk_nrf52840-ubx_gnss
ST Nucleo L496ZG
west build -b nucleo_l496zg -p -d build/nucleo_l496zg-ubx_gnss bridle/samples/ubx_gnss
west flash -d build/nucleo_l496zg-ubx_gnss
ST Nucleo F413ZH
west build -b nucleo_f413zh -p -d build/nucleo_f413zh-ubx_gnss bridle/samples/ubx_gnss
west flash -d build/nucleo_f413zh-ubx_gnss
ST Nucleo F767ZI
west build -b nucleo_f767zi -p -d build/nucleo_f767zi-ubx_gnss bridle/samples/ubx_gnss
west flash -d build/nucleo_f767zi-ubx_gnss
NXP MIMXRT1170-EVKB (CM7)
west build -b mimxrt1170_evk@B/mimxrt1176/cm7 -p -d build/mimxrt1170_evkb_cm7-ubx_gnss bridle/samples/ubx_gnss
west flash -r pyocd -d build/mimxrt1170_evkb_cm7-ubx_gnss
NXP MIMXRT1060-EVK
west build -b mimxrt1060_evk -p -d build/mimxrt1060_evk-ubx_gnss bridle/samples/ubx_gnss
west flash -r pyocd -d build/mimxrt1060_evk-ubx_gnss
NXP MIMXRT1010-EVK (experimental)
west build -b mimxrt1010_evk -p -S usb-console -d build/mimxrt1010_evk-ubx_gnss bridle/samples/ubx_gnss -- -DCONFIG_LOG=n
west flash -r pyocd -d build/mimxrt1010_evk-ubx_gnss
It is more luck than sense that this example works on this extremely poorly equipped board. The word “works” should also not be overrated. This board requires special care when using and maintaining the code base.
Insufficient UART interfaces
First of all, there is a lack of sufficient UART interfaces. The user must decide whether he wants to use the one available LPUART1 as a console via the on-board debug adapter (the factory default) or whether he needs it for his own purposes on the Arduino edge connector. For this example, the later is the case and it is extremely important that the two jumpers JP31 for TX and JP32 for RX are removed so that there is no longer an active connection to the on-board debug adapter (isolation). This also removes the channel for the standard console and the on-board USB device at J9 must be used as an alternative. This in turn means that Zephyr needs the USB device software stack with the USB-CDC/ACM class driver for VCOM access to the shell enabled.
Note: the west build parameter
-S usb-console
.Low on-board memory
The
ubxlib
software stack is extremely memory-intensive and requires at least 16 kB RAM for the memory heap (CONFIG_HEAP_MEM_POOL_SIZE
). That alone is already 25% of the available RAM in this system. Together with the necessary USB device software stack and the USB-CDC/ACM class driver, there is hardly anything left for additional functions. This means that the Zephyr shell can only be used in the absolute minimum configuration (CONFIG_SHELL_MINIMAL
=y
) and the Zephyr logging system must be omitted completely (CONFIG_LOG
=n
).Note: the CMake parameter
-DCONFIG_LOG=n
must be considered for this when calling west build.Disabled runtime stacks
As a result of the limited memory capacity, important other runtime stacks must also be reduced. That are in summary:
Board specific configuration
Context and meaning
CONFIG_HEAP_MEM_POOL_SIZE=16384 CONFIG_MAIN_STACK_SIZE=3456 CONFIG_ISR_STACK_SIZE=1024 CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=512 CONFIG_USB_WORKQUEUE_STACK_SIZE=512 CONFIG_USB_CDC_ACM_RINGBUF_SIZE=512
- Dynamic Memory Pool:
left on 16384- Main Context:
from 4096 to 3456- Interrupt Serive Routines:
from 2048 to 1024- System Worker Queue:
from 1024 to 512- USB-CDC/ACM Worker Queue:
from 1024 to 512- USB-CDC/ACM Ring Buffer:
from 1024 to 512With this heuristically determined memory configuration, the main functions of this “simple” example can be used. One exception is the shell command
gnss single
. The subsequent function call stack may grow to a point where the reduced ISR or main stack overflows and, in the absence of further Zephyr functionality, the CPU simply stops in a critical exception – with no visible notification to the user. This is a very dynamic effect and difficult to predict, but it happens very often.
Example console session
After power-on or hard reset, the GNSS module will be initialized automatically. There is a simple Shell command for some standard evaluation steps:
*** Booting Zephyr OS build v4.0.99… ***
[00:00:02.021,000] <inf> main: GNSS Device is ready!
uart:~$ gnss -h
gnss - GNSS related commands
Subcommands:
single :Get a one-shot position estimate
stream :Start or stop streaming of position estimates
reset :Reset GNSS module
ttff :Measure TTFF
Reset GNSS module:
uart:~$ gnss reset
The on-module LED for PPS signaling goes off and comes back to blink after TTFF.
Measure TTFF:
uart:~$ gnss ttff
Run 1 of 1: Acquired fix after 32.26s
---------------
Avg. TTFF: 32.26
The on-module LED for PPS signaling goes off and comes back to blink after TTFF.
It is also possible to run several TTFF measurements sequentially. If there is also a good receiving range and a reliable position already exists, the TTFF will be correspondingly low:
uart:~$ gnss ttff 10
Run 1 of 10: Acquired fix after 0.23s
Run 2 of 10: Acquired fix after 0.79s
Run 3 of 10: Acquired fix after 1.00s
Run 4 of 10: Acquired fix after 0.59s
Run 5 of 10: Acquired fix after 0.81s
Run 6 of 10: Acquired fix after 0.79s
Run 7 of 10: Acquired fix after 0.80s
Run 8 of 10: Acquired fix after 1.01s
Run 9 of 10: Acquired fix after 0.58s
Run 10 of 10: Acquired fix after 0.81s
---------------
Avg. TTFF: 0.74
Get a one-shot position estimate:
uart:~$ gnss single
Found position estimate after 0.8s: (lat, lon): (50.922432, 11.600015), alt: 192.05m, radius: 1.48m (15 SV used)
Start or stop streaming of position estimates:
uart:~$ gnss stream start
[00:01:15.687,000] <inf> main: Found position estimate: (lat, lon): (50.922447, 11.600006), alt: 192.64m, radius: 1.45m (17 SV used)
[00:01:16.692,000] <inf> main: Found position estimate: (lat, lon): (50.922451, 11.600005), alt: 192.53m, radius: 1.45m (18 SV used)
[00:01:17.697,000] <inf> main: Found position estimate: (lat, lon): (50.922451, 11.600004), alt: 192.63m, radius: 1.45m (18 SV used)
[00:01:18.904,000] <inf> main: Found position estimate: (lat, lon): (50.922455, 11.600004), alt: 192.71m, radius: 1.46m (17 SV used)
[00:01:19.658,000] <inf> main: Found position estimate: (lat, lon): (50.922455, 11.600004), alt: 192.80m, radius: 1.46m (18 SV used)
[00:01:20.663,000] <inf> main: Found position estimate: (lat, lon): (50.922455, 11.600004), alt: 192.96m, radius: 1.46m (18 SV used)
[00:01:21.667,000] <inf> main: Found position estimate: (lat, lon): (50.922455, 11.600003), alt: 192.89m, radius: 1.46m (18 SV used)
[00:01:22.722,000] <inf> main: Found position estimate: (lat, lon): (50.922459, 11.600002), alt: 192.79m, radius: 1.47m (17 SV used)
[00:01:23.929,000] <inf> main: Found position estimate: (lat, lon): (50.922459, 11.600001), alt: 192.92m, radius: 1.47m (18 SV used)
[00:01:24.683,000] <inf> main: Found position estimate: (lat, lon): (50.922462, 11.600000), alt: 192.89m, radius: 1.48m (17 SV used)
[00:01:25.688,000] <inf> main: Found position estimate: (lat, lon): (50.922462, 11.599999), alt: 192.77m, radius: 1.48m (18 SV used)
[00:01:26.693,000] <inf> main: Found position estimate: (lat, lon): (50.922466, 11.599998), alt: 192.69m, radius: 1.48m (18 SV used)
[00:01:27.697,000] <inf> main: Found position estimate: (lat, lon): (50.922466, 11.599996), alt: 192.49m, radius: 1.50m (18 SV used)
uart:~$ gnss stream stop
[00:01:28.905,000] <inf> main: Found position estimate: (lat, lon): (50.922470, 11.599995), alt: 192.22m, radius: 1.50m (18 SV used)
[00:01:29.709,000] <inf> main: Found position estimate: (lat, lon): (50.922470, 11.599994), alt: 192.12m, radius: 1.50m (18 SV used)