#log #nrf #zephyr #ffi #logging #bidnings

no-std logz

Zephyr RTOS logging implementation

3 releases

0.1.2 May 29, 2022
0.1.1 May 28, 2022
0.1.0 May 28, 2022

#189 in No standard library

24 downloads per month

MIT/Apache

13KB
176 lines

LOGZ

This repo provides a crate with a Rust Logger implementation for use on an embedded device with Zephyr RTOS. bindgen and cbindgen are used to bind Rust calls to Zephyr RTOS's log2 implementation. Since this crate is targetting NRF52 and NRF53 deployments, it is no_std and does not allocate.

Build

Include this crate in another crate that needs a logger implementation and make the usual log macro calls like log::trace!("2.0 * 2.0 = {}", 2. * 2.);. With a high initialization priority, the global logger is initialized to call the matching LOG_DBG, LOG_INF, LOG_WRN, or LOG_ERR macro in the bridging C.

Build Tools

In order to build there are several tools to install

  • NRF Connect SDK
    • v1.9.1
  • Rustup and Cargo
    • stable
    • v1.24.3 and v1.61.0
  • Cross compiling targets
    • M4(F): thumbv7em-none-eabihf
    • M33(F): thumbv8m.main-none-eabihf
  • Tools and Subcommands
    • cargo install cargo-make,cbindgen
    • brew install llvm
      • llvm-config needs to be in your PATH
      • echo 'export PATH="/opt/homebrew/opt/llvm/bin:$PATH"' >> ~/.zshrc
      • sudo xcode-select --install

If you have all of that, then cargo make test should work.

Example

In the example, we have a blinky application compiled for NRF52 or NRF53. The CMakeLists.txt of the sample project adds library that needs a logger, but is built as a static library for the main.c of Zephyr RTOS. The example project includes logz as a crate dependency and adds a lib/logz submodule. The submodule holds the C headers and src file to include in the example CMakeLists.txt.

The full example is here: https://github.com/trueb2/logz-example-rs

In the example CMakeLists.txt, we build the project with a static library and the bridge.c of logz

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(blinky)

### EXAMPLE STATIC LIBRARY :: BEGIN ###

# Allow app to include headers from the library including normal or bindgen headers
set(LOGZ_EXAMPLE_LIB_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/logz-example-rs)
set(LOGZ_LIB_SRC_DIR ${LOGZ_EXAMPLE_LIB_SRC_DIR}/lib/logz)
# Cortex-M4F (NRF52)
# set(LOGZ_EXAMPLE_LIB_DIR ${LOGZ_EXAMPLE_LIB_SRC_DIR}/target/thumbv7em-none-eabihf/release)
# Cortex-M33F (NRF53)
set(LOGZ_EXAMPLE_LIB_DIR ${LOGZ_EXAMPLE_LIB_SRC_DIR}/target/thumbv8m.main-none-eabihf/release)
include_directories(AFTER ${LOGZ_EXAMPLE_LIB_SRC_DIR}/include ${LOGZ_EXAMPLE_LIB_SRC_DIR}/include/generated ${LOGZ_LIB_SRC_DIR}/include/generated)
add_library(liblogz_example_rs STATIC IMPORTED GLOBAL)
set_target_properties(liblogz_example_rs PROPERTIES IMPORTED_LOCATION ${LOGZ_EXAMPLE_LIB_DIR}/liblogz_example_rs.a)
# Always let cargo decide if the library should be rebuilt
add_custom_target(
  liblogz_example_rs_target
  ALL
  BYPRODUCTS ${LOGZ_EXAMPLE_LIB_DIR}/liblogz_example_rs.a
  WORKING_DIRECTORY ${LOGZ_EXAMPLE_LIB_SRC_DIR}
  COMMAND cargo make build
  )
add_dependencies(app liblogz_example_rs_target)
# A couple Cortex M math functions get multiple definitions
target_link_libraries(app PRIVATE ${LOGZ_EXAMPLE_LIB_DIR}/liblogz_example_rs.a -Wl,--allow-multiple-definition)

### EXAMPLE STATIC LIBRARY :: END ###


### The logger bindings still need to be compiled for zephyr, which is why we still have to build the static lib and bridge
target_sources(app PRIVATE src/main.c ${LOGZ_LIB_SRC_DIR}/src/bridge.c)

This makes sure that the log macro calls will be piped to an initialized Logger instance that writes its logs to the Zephyr LOG2 implementation as a string formatted in Rust

In Zephyr, we call a Rust library function, example_foo()

void flip_timer(struct k_timer* timer) {
	LOG_INF("LED: %d", (int) on);
	gpio_pin_set(led_dev, PIN, (int)on);
	on = !on;
	example_foo();
}

In Rust, we format the logs and invoke the LOG2 macros

#[no_mangle]
pub extern "C" fn example_foo() {
    log::trace!("Foo");
    log::debug!("Bar");
    log::info!("Fizz");
    log::warn!("Buzz");
    log::error!("Fizzle");
}
void log_inf(const char *restrict msg)
{
    LOG_INF("%s", msg);
}

In the UART, RTT, or whatever you have configured in your Kconfig, we get normal logs.

[00:04:32.166,320] <inf> rs: logz_example_rs: Fizz
[00:04:32.166,351] <wrn> rs: logz_example_rs: Buzz
[00:04:32.166,381] <err> rs: logz_example_rs: Fizzle
[00:04:32.416,198] <inf> blinky: LED: 0
[00:04:32.416,259] <inf> rs: logz_example_rs: Foo
[00:04:32.416,290] <inf> rs: logz_example_rs: Bar
[00:04:32.416,320] <inf> rs: logz_example_rs: Fizz
[00:04:32.416,351] <wrn> rs: logz_example_rs: Buzz
[00:04:32.416,412] <err> rs: logz_example_rs: Fizzle
[00:04:32.666,198] <inf> blinky: LED: 1
[00:04:32.666,259] <inf> rs: logz_example_rs: Foo
[00:04:32.666,290] <inf> rs: logz_example_rs: Bar
[00:04:32.666,351] <inf> rs: logz_example_rs: Fizz
[00:04:32.666,381] <wrn> rs: logz_example_rs: Buzz
[00:04:32.666,412] <err> rs: logz_example_rs: Fizzle
[00:04:32.916,229] <inf> blinky: LED: 0
[00:04:32.916,290] <inf> rs: logz_example_rs: Foo
[00:04:32.916,320] <inf> rs: logz_example_rs: Bar
[00:04:32.916,351] <inf> rs: logz_example_rs: Fizz
[00:04:32.916,381] <wrn> rs: logz_example_rs: Buzz
[00:04:32.916,442] <err> rs: logz_example_rs: Fizzle

Dependencies

~0.6–2.5MB
~48K SLoC