Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

print ubxlib output to usb on nrf52840 dongle using Zephyr #215

Open
alehanderoo opened this issue Mar 19, 2024 · 30 comments
Open

print ubxlib output to usb on nrf52840 dongle using Zephyr #215

alehanderoo opened this issue Mar 19, 2024 · 30 comments

Comments

@alehanderoo
Copy link

alehanderoo commented Mar 19, 2024

Hi there,

I have a nrf52840 dongle and I'm using the usb console example to print something to the terminal. ->This works fine.

When I import ubxlib to my project as explained here it compiles and flashes, but the com-port that is enabled by the usb-cdc-acm-console seems to be lost.

Does ubxlib require a specific hardware block to write the output on?

I'm fairly new to Zephyr and ubxlib, so any help/improvements would be highly appreciated.

app.overlay:
`
/ {
chosen {
zephyr,console = &cdc_acm_uart0;
};
};

&zephyr_udc0 {
cdc_acm_uart0: cdc_acm_uart0 {
compatible = "zephyr,cdc-acm-uart";
};
};

&i2c0 {
compatible = "nordic,nrf-twim";
status = "okay";
pinctrl-0 = <&i2c0_default>;
pinctrl-1 = <&i2c0_sleep>;
pinctrl-names = "default", "sleep";
mysensor: mysensor@42{
compatible = "i2c-device";
reg = < 0x42 >;
label = "MYSENSOR";
};
};

&pinctrl {
i2c0_default: i2c0_default {
group1 {
psels = <NRF_PSEL(TWIM_SDA, 0, 31)>,
<NRF_PSEL(TWIM_SCL, 0, 29)>;
};
};

i2c0_sleep: i2c0_sleep {
group1 {
psels = <NRF_PSEL(TWIM_SDA, 0, 31)>,
<NRF_PSEL(TWIM_SCL, 0, 29)>;
low-power-enable;
};
};
};`

prj.conf:
`CONFIG_USB_DEVICE_STACK=y
CONFIG_USB_DEVICE_PRODUCT="Zephyr USB console sample"
CONFIG_USB_DEVICE_PID=0x0004
CONFIG_USB_DEVICE_INITIALIZE_AT_BOOT=n

CONFIG_SERIAL=y
CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y
CONFIG_UART_LINE_CTRL=y

CONFIG_GPIO=y
CONFIG_I2C=y

CONFIG_GNSS=y
CONFIG_GNSS_SATELLITES=y
CONFIG_GNSS_NMEA0183=y
CONFIG_UBXLIB=y
CONFIG_UBXLIB_GNSS=y
CONFIG_DEBUG=y
CONFIG_ASSERT=y

CONFIG_INIT_STACKS=y
CONFIG_THREAD_STACK_INFO=y
CONFIG_THREAD_NAME=y
CONFIG_KERNEL_MEM_POOL=y
CONFIG_HEAP_MEM_POOL_SIZE=20000
CONFIG_MINIMAL_LIBC_MALLOC=n`

@RobMeades
Copy link
Contributor

RobMeades commented Mar 19, 2024

Hi, and thanks for posting. So that I have some context, you say that you are using the nrf52840 dongle: I guess this means that you are compiling Zephyr for nRF52840, running the compiled Zephyr code on that nrf52840 dongle, and when you say "the com-port that is enabled by the usb-cdc-acm-console seems to be lost" that you were receiving debug output from Zephyr at a PC or some such over a USB-serial interface and that, once you added the "ubxlib" and "Need the following for U-Blox as well" CONFIG_ parameters you have listed above, that debug stream is no longer present. I assume also, since you've not mentioned any, that this is without calling any ubxlib code yet, so there is no run-time difference, purely a compile-time difference.

ubxlib, as far as logging is concerned, just calls printf(), nothing fancy or HW-related there. Nothing in what you've posted above stands out to me like it would affect Zephyr's logging interface but it is worth checking the start of your Zephyr build output (maybe do a clean build to make sure that it is performing the device tree configuration step) to make sure that it is not warning about one CONFIG_ item overriding another.

Since you are not running any ubxlib code yet, you could try removing CONFIG_ items until your debug output begins working, to try to narrow it down.

@alehanderoo
Copy link
Author

alehanderoo commented Mar 19, 2024

Hi @RobMeades,

Thank you for your prompt reply!
I'm on a steep learning curve from Arduino to Zephyr. Any help is appreciated!
Since the issue arrose from importing ubxlib, I thought about starting here.

I've started from a clean console printing 'hello world' example project. -> compiles and runs

I'm including the library in CmakeLists.txt with

set(ENV{UBXLIB_DIR} ${CMAKE_CURRENT_LIST_DIR}/ubxlib)
include($ENV{UBXLIB_DIR}/zephyr/ubxlib.cmake)

were receiving debug output from Zephyr at a PC or some such over a USB-serial interface

-printk output to be exact.

so there is no run-time difference, purely a compile-time difference.

-Seems like it, yes. The only code I have in main is this.

When setting CONFIG_UBXLIB=y
I get the following compile error:

`c:/ncs/toolchains/c57af46cb7/opt/zephyr-sdk/arm-zephyr-eabi/bin/../lib/gcc/arm-zephyr-eabi/12.2.0/../../../../arm-zephyr-eabi/bin/ld.bfd.exe: app/libapp.a(u_port.c.obj): in function `ubxlib_preinit':
C:/Users/ ... /Zephyr/m8q_print/ubxlib/port/platform/zephyr/src/u_port.c:208: undefined reference to `k_thread_system_pool_assign'`

So I also enabled the following from the default.conf.

CONFIG_INIT_STACKS=y
CONFIG_THREAD_STACK_INFO=y
CONFIG_THREAD_NAME=y
CONFIG_HEAP_MEM_POOL_SIZE=20000

This does compile but then the com-port does not pop up when the code is flashed to the nRF52840.

Am I understanding correctly that the conf of default.conf is used for compiling when I include it in CmakeLists?
If that is the case, why do I need to set CONFIG_UBXLIB=y?

During compiling I see the following message.
UBXLIB_FEATURES will be "u_lib;short_range;cell;gnss"
I only need GNSS for my application.
Could you maybe point me how I can configure to only compile gnss specific parts?

Thanks in advance for your help!

@RobMeades
Copy link
Contributor

I don't use Zephyr on a day to day basis myself, so I also find it very difficult to follow; it is extremely complicated because it is trying to support pretty much everything there is and remain small and remain flexible; an impossible task without accidentally appending "and extremely complicated" on the end :-(.

Am I understanding correctly that the conf of default.conf is used for compiling when I include it in CmakeLists?

The line include($ENV{UBXLIB_DIR}/zephyr/ubxlib.cmake), which you have added to your CMakeLists.txt, brings the file ubxlib.cmake into your CMakeLists.txt file (assuming, of course, that you have an environment variable UBXLIB_DIR pointing to the ubxlib directory). The file ubxlib.cmake adds ubxlib to ZEPHYR_EXTRA_MODULES and, unless you have deliberately defined UBXLIB_NO_DEF_CONF yourself, it should append port/platform/zephyr/default.conf to your configuration variables. But that default.conf file adds CONFIG_UBXLIB=y and all the other things already, you should have no need to add them yourself, so it plainly isn't doing what it apparently should be doing. It may depend upon where you have put the include($ENV{UBXLIB_DIR}/zephyr/ubxlib.cmake) line in your CMakeLists.txt file; order is important in CMake, things take effect from the moment they are added and for subsequent lines only and it is not always clear what any one line is doing.

Adding ubxlib to ZEPHYR_EXTRA_MODULES is the critical thing though, because that brings in the file module.yml (more abstraction I'm afraid) which the Zephyr west build tool understands, where the most important lines are:

  cmake: port/platform/zephyr/
  kconfig: port/platform/zephyr/Kconfig

Those lines enable west to find the ubxlib Zephyr CMakeLists.txt and KConfig files, which do most of the useful stuff.

When setting CONFIG_UBXLIB=y I get the following compile error...

Yes, just adding CONFIG_UBXLIB=y alone isn't sufficient: Zephyr does not, by default, allow task stacks to be allocated at task creation, only statically at compile time, hence we need those extra things (CONFIG_INIT_STACKS=y, CONFIG_THREAD_STACK_INFO=y etc.) to make it do that.

Could you maybe point me how I can configure to only compile gnss specific parts?

If you look at the detail of the ubxlib Zephyr CMakeLists.txt you will see that it adds ubxlib .c files based on the CMake variable UBXLIB_FEATURES. If you don't define UBXLIB_FEATURES yourself then it brings all of them in, so to chose a subset you should set UBXLIB_FEATURES in your CMakeLists.txt file before you do the include($ENV{UBXLIB_DIR}/zephyr/ubxlib.cmake) thang (order again). If all you need is gnss then you might do something like set(UBXLIB_FEATURES gnss).

@RobMeades
Copy link
Contributor

Just to add, I don't see how any of the above could relate to an nRF52 dongle's USB interface suddenly not working. You say "then the com-port does not pop up". Not "popping up" I guess means that the USB interface is not enumerating. I was going to say that this sounds like the nRF52 is not booting but the nRF52 doesn't have a USB interface, there must be some other chip on the dongle which provides that interface, and that must be booting, otherwise you would not be able to re-program the nRF52. Why would the USB interface chip suddenly not enumerate its output?

All I can suggest is backing-out changes until things work, so that you can pin-point what is going wrong.

Can you confirm that the ubxlib files are actually being built into your image (they should be listed in the build output)? It may or may not make a difference but, for the system thread pool stuff to work, SYS_INIT() must be called, so it might be if, for some reason, the ubxlib files aren't being built into your image then SYS_INIT() would not be being called; not sure what effect that would have.

@alehanderoo
Copy link
Author

alehanderoo commented Mar 19, 2024

Hi @RobMeades,

Thanks for helping!

This is my CmakeLists.txt.

cmake_minimum_required(VERSION 3.20.0)

# Include UBXLIB
set(UBXLIB_FEATURES gnss)
set(ENV{UBXLIB_DIR} ${CMAKE_CURRENT_LIST_DIR}/ubxlib)
include($ENV{UBXLIB_DIR}/zephyr/ubxlib.cmake)
set(ENV{ZEPHYR_EXTRA_MODULES} "${CMAKE_CURRENT_LIST_DIR}/ubxlib")

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(m8q_print)

include(${ZEPHYR_BASE}/samples/subsys/usb/common/common.cmake)
FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})

Adding ubxlib to ZEPHYR_EXTRA_MODULES is working.

ubxlib files are actually being built into your image

-Yes that seems to be done:
image

This is when I include the lines for ubxlib in CmakeLists.txt and include the ubxlib lines in proj.conf.
However, when I include these lines, the com-port is not opened after flashing.
This is without including the ubxlib header or using any of it's functions. I'm using the example main

When I remove the ubxlib lines in proj.conf, the com-port opens and serial data is being written to my terminal.

For completeness, I'm using this dongle.
There is communication over USB, but no debugging unfortunately.

@RobMeades
Copy link
Contributor

RobMeades commented Mar 19, 2024

Where is the picture you show above from? I agree that it shows the ubxlib files but the important thing is that the Zephyr build process shows the ubxlib files: it might be an idea to post that build output, from a clean build, as an attachment here so that we can be completely sure.

From what you say I understand that the USB com port over which debug arrives is enumerating but nothing is being sent over it, which takes us back to the possibility that, for whatever reason the nRF52 is not booting. Is there any other way you could tell whether the nRF52 was booting? For instance, could you light an LED or some such so that you could determine if it has entered your main application? With no debug output, LED's are the thing I'm afraid, we've all been there :-).

As indicated above, since you are using no ubxlib code whatsoever so far, taking out the additional CONFIG_ variables you have added until something works may be another way to divide the problem. The thing I don't understand, though, is that all of the CONFIG_ variables you have added should already have been added because you have included ubxlib.cmake, see these lines:

# Add the UBXLIB default configuration variables
list(APPEND CONF_FILE "${UBXLIB_BASE}/port/platform/zephyr/default.conf")

Not that it should make any difference but, FYI, you have:

include($ENV{UBXLIB_DIR}/zephyr/ubxlib.cmake)
set(ENV{ZEPHYR_EXTRA_MODULES} "${CMAKE_CURRENT_LIST_DIR}/ubxlib")

...but including the ubxlib file ubxlib.cmake will already add ubxlib to ZEPHYR_EXTRA_MODULES, so you don't need the second line (doing it twice should do no harm though).

@RobMeades
Copy link
Contributor

To be clear, we build/run our regression tests on nRF52840 constantly, so there is nothing fundamentally wrong here, which is what makes it all the more strange.

@RobMeades
Copy link
Contributor

RobMeades commented Mar 20, 2024

I understand from one of my colleagues that this board in fact uses a USB block inside the nRF52 itself, for which driver code needs to be included etc., which I am sure the sample you are using is doing. He's going to see if he can dig up one of these boards himself to see what happens (he will dive in here with any input when he does).

I don't know how long that might take though so I would encourage you to (a) get an LED on the board (assuming it has one) operating so that you can flash it in the working case and then (b) flash that LED at entry to your application in the non-working case to see if the code is getting that far.

@plerup
Copy link
Contributor

plerup commented Mar 20, 2024

@alehanderoo
Copy link
Author

alehanderoo commented Mar 20, 2024

@RobMeades, I will check with some LED sample code to see if the application starts.
I think that could be the reason the usb port is not listed, since it starts it during runtime.

@plerup, my journey started at this post. I'm using the one from Zephyr. The only difference from the Zephyr example is that they also include a usbd_next.

the Zephyr build process shows the ubxlib

-Yes they seem to be included when building.
build_logs.txt

taking out the additional CONFIG_ variables you have added until something works may be another way to divide the problem

-When I do that, the "Hello World! arm" message is printed. Here I have included ubxlib in the CmakeLists.txt. Then it does not show in the build logs or in the build source files in vscode (like the image included before).

@RobMeades
Copy link
Contributor

RobMeades commented Mar 20, 2024

Agreed, your build_logs.txt file does contain the expected files and, in particular u_port.c so that should mean that the SYS_INIT() macro is included.

When I do that, the "Hello World! arm" message is printed. Here I have included ubxlib in the CMakeLists.txt. Then it does not show in the build logs or in the build source files in vscode (like the image included before).

Not sure I'm following this: could you maybe rephrase? Are you saying that, in the success case, so with the "Hello World! arm" message being printed, you are including ubxlib in the CMakeLists.txt, i.e. you do, in the success case, have the line include($ENV{UBXLIB_DIR}/zephyr/ubxlib.cmake) in your CMakeLists.txt, it is only explicitly adding certain CONFIG_ lines directly in your CMakeLists.txt file that makes the problem occur?

@alehanderoo
Copy link
Author

@RobMeades,

The succes case: (printing to the terminal over USB)
This is the build log with including ubxlib in the CMakeLists.txt, without including it in the CONFIG_.
build_logs_no_CONF.txt
As you can see, ubxlib is not included in the build. You mention it should so there is something else going wrong here.

The non-succes case: (not printing to the terminal over USB)
This is the build log with including ubxlib in the CMakeLists.txt, with including it in the CONFIG_.
build_logs_with_CONF.txt

I"m including ubxlib in CMakeLists.txt as follows:

cmake_minimum_required(VERSION 3.20.0)

# Include UBXLIB
set(UBXLIB_FEATURES gnss)
set(ENV{UBXLIB_DIR} ${CMAKE_CURRENT_LIST_DIR}/ubxlib)
include($ENV{UBXLIB_DIR}/zephyr/ubxlib.cmake)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(console)

include(${ZEPHYR_BASE}/samples/subsys/usb/common/common.cmake)
FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})

it is only explicitly adding certain CONFIG_ lines directly in your CMakeLists.txt file that makes the problem occur?

-Well, I'm including ubxlib in the CONFIG_ as follows (is this what you mean?):

# re-directing uart output to usb (from console example)
CONFIG_USB_DEVICE_STACK=y
CONFIG_USB_DEVICE_PRODUCT="Zephyr USB console sample"
CONFIG_USB_DEVICE_PID=0x0004
CONFIG_USB_DEVICE_INITIALIZE_AT_BOOT=n

CONFIG_SERIAL=y
CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y
CONFIG_UART_LINE_CTRL=y

# ubxlib
CONFIG_UBXLIB=y

# Required for uPortTaskStackMinFree() to work
CONFIG_INIT_STACKS=y
CONFIG_THREAD_STACK_INFO=y
CONFIG_THREAD_NAME=y
CONFIG_HEAP_MEM_POOL_SIZE=20000

@RobMeades
Copy link
Contributor

Thanks for the clarification. The line:

include($ENV{UBXLIB_DIR}/zephyr/ubxlib.cmake)

...should have the effect of bringing in all of the ubxlib stuff and making it a Zephyr module. If you look inside the file port/platform/zephyr/CMakeLists.txt, which will be brought in by the inclusion, you will see that the whole thing is gated on CONFIG_UBXLIB being set.

Can you try just defining CONFIG_UBXLIB=y for your build, i.e. include none of the other defines that you are adding on behalf of ubxlib? That should bring in the ubxlib stuff in the way that I would expect, if nothing else, so the ubxlib files should appear in your build output.

@RobMeades
Copy link
Contributor

RobMeades commented Mar 20, 2024

Sorry, I see you did this originally and that caused the compilation error:

:/Users/ ... /Zephyr/m8q_print/ubxlib/port/platform/zephyr/src/u_port.c:208: undefined reference to `k_thread_system_pool_assign'`

I don't understand any of this, let me try doing exactly what you are doing myself; I don't have one of those boards but I can try to see why the right definitions aren't getting to the right places.

@alehanderoo
Copy link
Author

@RobMeades,

I've included a GPIO led in my main. It is blinking when I do not include ubxlib in the CONFIG_.
When I do include ubxlib there, the LED does not start blinking, nor do I see the com-port.

I've added my code in the zip below.
source.zip
Many thanks for the help so far!

@RobMeades
Copy link
Contributor

Got it, so one or more of the CONFIG_ variables (since that's all that is really being added in this case, given that no ubxlib code is being called) prevents this particular USB-interfacing nRF52 board from booting. That is nice and clear. Next question is, which one? Lemme think...

@RobMeades
Copy link
Contributor

RobMeades commented Mar 20, 2024

From a blank environment, to reproduce your build locally, I have done the following:

  • Set a few environment variables, so that west knows where Zephyr and my copy of gcc are (obviously just I need to do this, you will already have this set up):
ZEPHYR_BASE=C:\Users\rmea\.ubxlibpkg\nrfconnectsdk-v2.5.0\zephyr
ZEPHYR_TOOLCHAIN_VARIANT=gnuarmemb
GNUARMEMB_TOOLCHAIN_PATH=\Users\rmea\.ubxlibpkg\arm_embedded_gcc-10-2020-q4-major
  • Copied the sample you are using from samples/subsys/usb/console into a directory I can play with, C:\TEMP\console.
  • Inserted into the CMakeLists.txt file of this copy of the sample, after the cmake_minimum_required(VERSION 3.20.0) line, the additional lines:
set(UBXLIB_FEATURES gnss)
set(ENV{UBXLIB_DIR} /projects/ubxlib_priv)
include($ENV{UBXLIB_DIR}/zephyr/ubxlib.cmake)
  • Appended to the end of the prj.conf file of this copy of the sample the single line:
CONFIG_UBXLIB=y
  • While in the C:\TEMP\console directory, executed the following command:
west build -p auto -b nrf52840dongle_nrf52840 --build-opt=-v

[The command on the end to show the verbose build output].

This built to completion, output from west attached. It seems to have done the kind of thing I was expecting, built the bits of ubxlib I was expecting, didn't throw a compiler error.

Attached find the zephyr.elf file it built (renamed to .txt so that Github will allow me to attach it here). This will, of course, be an awfully long-winded way of debugging the problem, but it might be worth you downloading this file, renaming it to zephyr.elf and loading it onto your board to see if it, at least, boots.

@alehanderoo
Copy link
Author

Hi @RobMeades,

Strange that it does want to compile just with CONFIG_UBXLIB=y.
Could you add the hex instead of the elf file?

@RobMeades
Copy link
Contributor

RobMeades commented Mar 20, 2024

Yes, I can't explain any of this; zephyr.hex attached, renamed with .txt on the end.

What version of nRFConnect are you using? We test with 2.5.0, which contains Zephyr 3.4.99, but we also test with native Zephyr (i.e. outside nRFConnect) 3.6.0 as well, so there's plenty of coverage here.

@RobMeades
Copy link
Contributor

Checking the end of your build_logs_with_CONF.txt against the build output I get, they are quite different. You get:

Memory region         Used Size  Region Size  %age Used
           FLASH:       43072 B      1020 KB      4.12%
             RAM:       37280 B       256 KB     14.22%
        IDT_LIST:          0 GB        32 KB      0.00%

I get:

Memory region         Used Size  Region Size  %age Used
           FLASH:       76584 B      1020 KB      7.33%
             RAM:      157632 B       256 KB     60.13%
        IDT_LIST:          0 GB         2 KB      0.00%

Now I would expect the RAM to be quite different, as you are only defining a small heap, but the flash size is half in your build versus mine, and we're using the same optimisation settings etc. as far as I know...?

@alehanderoo
Copy link
Author

alehanderoo commented Mar 20, 2024

Hi @RobMeades,

That worked! It's printing 'Hello World! arm'.

I'm using nrfConnect v2.5.2
With Zephyr v3.5.99

I'm using the project's default optimization. This is mentioned in the build_logs_with_CONF.txt:
but the optimization flag was set to '-Os'.

@RobMeades
Copy link
Contributor

Well, I suppose that is kind of good news; I am using the default settings in this "console" example, so should all be the same as yours. Let me go grab nRFConnect 2.5.2 and see if that is any different.

@RobMeades
Copy link
Contributor

RobMeades commented Mar 20, 2024

I have now repeated the procedure, only difference being that I have pointed the build at the version of Zephyr that comes with nRFConnect version 2.5.2:

ZEPHYR_BASE=C:\Users\rmea\.ubxlibpkg\nrfconnectsdk-v2.5.2\zephyr

...and I have freshly copied samples/subsys/usb/console from there, applying the same changes as above to CMakeLists.txt and prj.conf. Outcome was the same, new build output and zephyr.hex (the latter renamed to have .txt on the end) attached: let me know what happens when you run that one.

One thing to think about is, is there enough heap? You have set it 20ish kbytes: is it clear that is sufficient for whatever the build is doing? You might try a bigger number. It is also strange that your build output complains that both "Debug" and -Os have been applied; I don't know whether that's a real issue or not, I would guess not, since it is a warning rather than an error.

@alehanderoo
Copy link
Author

alehanderoo commented Mar 22, 2024

@RobMeades,

Succes! I uninstalled everything and installed the toolchain through the nrf extension pack in VScode.

The code compiles and runs now. Thank you so much for your help!
I'm including the package as normal: including the folder and setting CONFIG_UBXLIB=y.

However..I am now getting a -5 error when opening the device.

Hello World! arm
Opened device with return code -5.
Unable to open GNSS!
Done.

Do you know what it could be?
There is not much description here..

@RobMeades
Copy link
Contributor

Excellent! If uDeviceOpen() is returning U_ERROR_COMMON_INVALID_PARAMETER then that will mean that one or more of the parameters you passed into it, in the configuration structure, is invalid. Can you paste here what you have passed in?

@alehanderoo
Copy link
Author

alehanderoo commented Mar 22, 2024

@RobMeades,

I currently have the following:

static const uDeviceCfg_t gDeviceCfg = {
    .deviceType = U_DEVICE_TYPE_GNSS,
    .deviceCfg = {
        .cfgGnss = {
            .moduleType = U_GNSS_MODULE_TYPE_M8,
            .pinEnablePower = U_CFG_APP_PIN_GNSS_ENABLE_POWER,
            .pinDataReady = -1, // Not used
	 },
    },
    .transportType = U_DEVICE_TRANSPORT_TYPE_I2C,
    .transportCfg = {
        .cfgI2c = {
            .i2c = U_CFG_APP_GNSS_I2C,
            .pinSda = U_CFG_APP_PIN_GNSS_SDA,
            .pinScl = U_CFG_APP_PIN_GNSS_SCL
        },
     },
};

sda and scl pins are defined in u_cfg_app_platform_specific.h
I also set U_CFG_APP_GNSS_I2C 1

@RobMeades
Copy link
Contributor

RobMeades commented Mar 22, 2024

Thanks, a few comments/questions:

  • in the device tree fragment you posted at the top of this issue you seem to be setting up i2c0 as the I2C bus where the GNSS device is connected: I guess you have changed this to use i2c1 if you are setting .transportCfg.cfgI2c.i2c to 1?
  • as explained here, Zephyr, unlike all the other platforms we support, doesn't allow I2C (or UART or SPI) pins to be passed-in by an application, the application must define them in the Zephyr tree, so do make sure that the I2C pins .pinSda and .pinScl are set to -1.

Otherwise the structure looks fine. During device open the ubxlib code will be communicating with the GNSS device, of course: and by default you should see debug prints of that communication process: could you paste those here?

@alehanderoo
Copy link
Author

alehanderoo commented Mar 22, 2024

@RobMeades,

I see, thank you for the support. This is my current overlay (maybe it's usefull for other people in the future):

/ {
	chosen {
		zephyr,console = &cdc_acm_uart0;
	};
};

&zephyr_udc0 {
	cdc_acm_uart0: cdc_acm_uart0 {
		compatible = "zephyr,cdc-acm-uart";
	};
};

&i2c0 {
	status = "okay";
	/* Otherwise board default settings are good. */
};

&pinctrl {
  i2c0_default: i2c0_default {
	  group1 {
		  psels = <NRF_PSEL(TWIM_SDA, 0, 31)>,
			  <NRF_PSEL(TWIM_SCL, 0, 29)>;
	  };
  };

  i2c0_sleep: i2c0_sleep {
	  group1 {
		  psels = <NRF_PSEL(TWIM_SDA, 0, 31)>,
			  <NRF_PSEL(TWIM_SCL, 0, 29)>;
		  low-power-enable;
	  };
  };
};

u_cfg_app_platform_specific.h

U_CFG_APP_GNSS_I2C                        0
U_CFG_APP_PIN_GNSS_SDA               -1
U_CFG_APP_PIN_GNSS_SCL                -1

This is what I get printend in the terminal:

---- Opened the serial port COM9 ----
USB init and terminal opened
U_GNSS: initialising with ENABLE_POWER pin not connected, transport type I2C.
U_GNSS: sent command b5 62 0a 06 00 00 10 3a.
U_GNSS: decoded UBX response 0x0a 0x06: 11 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [body 120 byte(s)].
U_GNSS: sent command b5 62 06 04 04 00 00 00 09 00 17 76.
U_GNSS: sent command b5 62 0a 06 00 00 10 3a.
U_GNSS: decoded UBX response 0x0a 0x06: 13 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [body 120 byte(s)].
U_GNSS: sent command b5 62 0a 28 00 00 32 a0.
U_GNSS: decoded UBX response 0x0a 0x28: 00 0f 03 03 03 00 00 00 [body 8 byte(s)].
Opened device with return code 0.
Starting position stream.
U_GNSS: sent command b5 62 06 08 00 00 0e 30.
U_GNSS: decoded UBX response 0x06 0x08: e8 03 01 00 01 00 [body 6 byte(s)].
U_GNSS: sent command b5 62 06 01 02 00 01 07 11 3a.
U_GNSS: decoded UBX response 0x06 0x01: 01 07 00 00 00 00 00 00 [body 8 byte(s)].
U_GNSS: sent command b5 62 06 01 08 00 01 07 01 00 00 00 00 00 18 e2.
U_GNSS: decoded UBX response 0x05 0x01: 06 01 [body 2 byte(s)].
Waiting up to 60 seconds for 5 position fixes.
U_GNSS: sent command b5 62 06 01 08 00 01 07 00 00 00 00 00 00 17 dc.
U_GNSS: decoded UBX response 0x05 0x01: 06 01 [body 2 byte(s)].
Done.

I did expect the callback to print out the position. Is that correct?
This is (part of) my main:

static const uDeviceCfg_t gDeviceCfg = {
    .deviceType = U_DEVICE_TYPE_GNSS,
    .deviceCfg = {
        .cfgGnss = {
            .moduleType = U_GNSS_MODULE_TYPE_M8,
            .pinEnablePower = U_CFG_APP_PIN_GNSS_ENABLE_POWER,
            .pinDataReady = -1, // Not used
				},
		},
		.transportType = U_DEVICE_TRANSPORT_TYPE_I2C,
    .transportCfg = {
        .cfgI2c = {
            .i2c = U_CFG_APP_GNSS_I2C,
            .pinSda = U_CFG_APP_PIN_GNSS_SDA,
            .pinScl = U_CFG_APP_PIN_GNSS_SCL
				},
		},
};

typedef struct {
    char *pSomething;
    int32_t somethingElse;
} MyContext_t;

// Count of the number of position fixes received
static size_t gPositionCount = 0;

static char latLongToBits(int32_t thingX1e7,
                          int32_t *pWhole,
                          int32_t *pFraction)
{
    char prefix = '+';

    // Deal with the sign
    if (thingX1e7 < 0) {
        thingX1e7 = -thingX1e7;
        prefix = '-';
    }
    *pWhole = thingX1e7 / 10000000;
    *pFraction = thingX1e7 % 10000000;

    return prefix;
}

// Callback for position reception.
static void callback(uDeviceHandle_t gnssHandle,
                     int32_t errorCode,
                     int32_t latitudeX1e7,
                     int32_t longitudeX1e7,
                     int32_t altitudeMillimetres,
                     int32_t radiusMillimetres,
                     int32_t speedMillimetresPerSecond,
                     int32_t svs,
                     int64_t timeUtc)
{
    char prefix[2] = {0};
    int32_t whole[2] = {0};
    int32_t fraction[2] = {0};

    // Pick up our user context
    MyContext_t *pContext = (MyContext_t *) pUDeviceGetUserContext(gnssHandle);

    // Not using these, just keep the compiler happy
    (void) gnssHandle;
    (void) altitudeMillimetres;
    (void) radiusMillimetres;
    (void) speedMillimetresPerSecond;
    (void) svs;
    (void) timeUtc;
    // We don't actually use the user context here, but
    // of course _you_ could
    (void) pContext;

    if (errorCode == 0) {
        prefix[0] = latLongToBits(longitudeX1e7, &(whole[0]), &(fraction[0]));
        prefix[1] = latLongToBits(latitudeX1e7, &(whole[1]), &(fraction[1]));
        uPortLog("I am here: https://maps.google.com/?q=%c%d.%07d,%c%d.%07d\n",
                 prefix[1], whole[1], fraction[1], prefix[0], whole[0], fraction[0]);
        gPositionCount++;
    }
}


int main(void)
{
	const struct device *const dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
	uint32_t dtr = 0;

#if defined(CONFIG_USB_DEVICE_STACK_NEXT)
	if (enable_usb_device_next()) {
		return 0;
	}
#else
	if (usb_enable(NULL)) {
		return 0;
	}
#endif

	/* Poll if the DTR flag was set */
	while (!dtr) {
		uart_line_ctrl_get(dev, UART_LINE_CTRL_DTR, &dtr);
		/* Give CPU resources to low priority threads. */
		k_sleep(K_MSEC(100));
	}
	printk("USB init and terminal opened\n");

	uDeviceHandle_t devHandle = NULL;
	int32_t returnCode;
	int32_t guardCount = 0;
	MyContext_t context = {0};

	// Initialise the APIs we will need
	uPortInit();
	uPortI2cInit(); // You only need this if an I2C interface is used
	uDeviceInit();

	// Open the device
	returnCode = uDeviceOpen(&gDeviceCfg, &devHandle);
	uPortLog("Opened device with return code %d.\n", returnCode);

	if (returnCode == 0) {
			// Since we are not using the common APIs we do not need
			// to call uNetworkInteraceUp()/uNetworkInteraceDown().

			// If you need to pass context data to
			// uGnssPosGetStreamedStart(), which does not include
			// a user parameter in its function signature, set
			// a user context for ourselves in the device, which
			// we can pick up in callback()
			uDeviceSetUserContext(devHandle, (void *) &context);

			// Start to get position
			uPortLog("Starting position stream.\n");
			returnCode = uGnssPosGetStreamedStart(devHandle,
																						U_GNSS_POS_STREAMED_PERIOD_DEFAULT_MS,
																						callback);
			if (returnCode == 0) {

					uPortLog("Waiting up to 60 seconds for 5 position fixes.\n");
					while ((gPositionCount < 5) && (guardCount < 60)) {
							uPortTaskBlock(1000);
							guardCount++;
					}
					// Stop getting position
					uGnssPosGetStreamedStop(devHandle);

			} else {
					uPortLog("Unable to start position stream!\n");
			}

			// Close the device
			// Note: we don't power the device down here in order
			// to speed up testing; you may prefer to power it off
			// by setting the second parameter to true.
			uDeviceClose(devHandle, false);

	} else {
			uPortLog("Unable to open GNSS!\n");
	}

	// Tidy up
	uDeviceDeinit();
	uPortI2cDeinit(); // You only need this if an I2C interface is used
	uPortDeinit();

	uPortLog("Done.\n");
}

EDIT::
I added a print statement with the returncode in the callback.
in the callback with code: -9
So I'm getting a timeout error.

@RobMeades
Copy link
Contributor

RobMeades commented Mar 22, 2024

From a quick check that code looks OK. You are calling uGnssPosGetStreamedStart() and, I guess, giving it your callback() function, then the code is waiting for a minute for the callback() function to be called before it eventually calls uGnssPosGetStreamedStop().

As explained above the function signature for uGnssPosGetStreamedStart():

* @param[in] pCallback a callback that will be called when navigation
* messages arrive. The parameters to the callback
* are as described in uGnssPosGetStart().
* Note: don't call back into this API from your
* pCallback, it could lead to recursion.
* IMPORTANT: you should check the value of
* errorCode before treating the parameters:
* a value of zero means that a position fix
* has been achieved but a value of
* #U_ERROR_COMMON_TIMEOUT may be used to
* indicate that a message has arrived from the
* GNSS device giving no position fix or a
* time-only fix. Where no fix is achieved the
* variables will be populated with out of range
* values (i.e. INT_MIN or -1 as appopriate).

...you will get a timeout error if the GNSS device has not been able to establish position.

While doing streamed position we switch off printing-out of the messages arriving from the GNSS device as they would get in the way but, if you wish to see them temporarily to confirm that they really are arriving, after your call to uGnssPosGetStreamedStart() you could call uGnssSetUbxMessagePrint(devHandle, true) to switch message printing back on again.

@RobMeades
Copy link
Contributor

Hi @alehanderoo: were you able to get a GNSS fix in the end?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants