Skip to content

Repository files navigation

BareLog

Bare-metal logging for ARM Cortex-M.

These notes describe what BareLog is, which targets it supports, how to build and integrate it, and what to watch for when something looks wrong. Read the flushing section before shipping firmware that depends on logs.

What is BareLog?

BareLog is a small C11 logging library. It does not use the heap, does not link against stdio, and formats messages with its own printf subset.

Log lines are written into an IRQ-safe ring buffer. A separate flush path moves bytes to a backend (UART DMA, SWO/ITM, SEGGER RTT, or on-chip flash). ISR-safe entry points (LOG_*_ISR) exist alongside the normal macros. Optional FreeRTOS / ThreadX locking covers the flush path. Compile-time and runtime level filters, module whitelist/blacklist, hex dumps, optional DWT timestamps, and optional ANSI colours are available.

Version macros live in include/blog_config.h (BLOG_VERSION_*). See CHANGELOG.md for release history. The current tree is 0.9.1.

License: BSD-3-Clause (LICENSE).

On what hardware does it run?

Originally exercised on STM32F1; STM32F4 is also supported. CMake selects linker script, startup, device header, and clocks from the chip name.

Family Coverage (high level) Flash RAM Max HCLK
STM32F1 F100 / F101 / F102 / F103 / F105 / F107 32–1024K 6–96K 72 MHz
STM32F4 F401…F479 lineup used by the port (40+ parts) 64–2048K 32–384K 84–180 MHz

Other Cortex-M parts are not first-class ports yet. SWO and DWT paths assume ARMv7-M / ARMv7E-M (M3/M4-class).

Building

Host tests (Unity, no ARM toolchain required):

just test

Cross builds need arm-none-eabi-gcc on PATH (or ARM_GCC_PATH):

just build-arm STM32F103xB          # default F1 chip if omitted
just build-arm-f4 STM32F407VG
just build-all                      # all four backends (F1 demo)
just size                           # arm-none-eabi-size per backend
just flash-jlink                    # or: just flash-stlink

CMake examples:

cmake -B build --toolchain toolchain-arm.cmake \
    -DSTM32F1_CHIP=STM32F103xB -DBLOG_BACKEND=BLOG_BACKEND_UART_DMA .

cmake -B build --toolchain toolchain-arm.cmake \
    -DBLOG_TARGET=stm32f4 -DSTM32F4_CHIP=STM32F429VI \
    -DBLOG_BACKEND=BLOG_BACKEND_SWO .

When this repository is the CMake top level, BLOG_BUILD_DEMO defaults to ON and produces BareLog.elf / .hex / .bin. When BareLog is pulled in with add_subdirectory(), the demo defaults to OFF and only the blog static library is built.

cmake ... -DBLOG_BUILD_DEMO=OFF    # library only
cmake ... -DBLOG_BUILD_DEMO=ON     # force demo even as a subdirectory

Integrating into another firmware

set(BLOG_BACKEND BLOG_BACKEND_UART_DMA CACHE STRING "")
set(BLOG_TARGET stm32f1 CACHE STRING "")
set(STM32F1_CHIP STM32F103xB CACHE STRING "")
add_subdirectory(path/to/BareLog)
target_link_libraries(your_app PRIVATE blog)

Port paths resolve via BLOG_ROOT inside port/*/target.cmake, so the tree need not be the CMake source root.

Configuration

Common compile definitions (defaults in include/blog_config.h):

Define Default Meaning
BLOG_BACKEND UART DMA 1 UART DMA, 2 SWO, 3 RTT, 4 Flash
BLOG_LEVEL DEBUG Compile-time minimum level
BLOG_RINGBUF_SIZE 256 Power of two
BLOG_UART_BAUDRATE 115200 UART backend
BLOG_UART_CLOCK_HZ 72e6 / port UART clock used for BRR
BLOG_ENABLE_TIMESTAMP 0 Prefix with DWT CYCCNT
BLOG_ENABLE_COLOR 0 ANSI colour in level tags
BLOG_MODULE 0 Per-translation-unit module id
BLOG_RTOS 0 0 none, 1 FreeRTOS, 2 ThreadX
BLOG_BUILD_DEMO top-level Build demo firmware image

Printf subset: %d %i %u %x %X %p %c %s %%, length modifiers l / ll (including 64-bit for ll), optional width and 0 pad. No floating point.

Using the API

#include "blog.h"

int main(void)
{
	blog_init();

	LOG_INFO("System started, clock=%u", SystemCoreClock);
	LOG_DEBUG("value=0x%08x", reg);
	LOG_WARN("Low memory: %u bytes free", free);
	LOG_ERROR("Sensor %d failed", id);

	uint8_t buf[64];
	LOG_HEXDUMP(BLOG_LEVEL_INFO, "RX", buf, sizeof(buf));

	blog_flush_blocking(BLOG_FLUSH_MAX_ITERATIONS);
	blog_deinit();
}

From interrupt context use the ISR variants; still flush from thread or main context:

void DMA1_Channel4_IRQHandler(void)
{
	LOG_DEBUG_ISR("ISR flag=0x%x", flags);
}

Module filtering (optional):

#define BLOG_MODULE 1u   /* in the producing .c file */

blog_set_module_whitelist(0x03u);
blog_set_module_blacklist(0x04u);

RTOS: define BLOG_RTOS=1 (FreeRTOS) or 2 (ThreadX) on the blog target, and call blog_init() after the scheduler is up so the recursive mutex can be created.

NOTE on flushing

LOG_* only formats into the ring buffer. Nothing is emitted on the wire (or into flash) until blog_flush() or blog_flush_blocking() runs.

Call blog_flush() from the main loop, an idle hook, or a periodic timer. Call blog_flush_blocking(...) before sleep, reboot, or blog_deinit(). If you never flush, the buffer fills and overflow is counted in dropped.

Backends

Backend Typical use Notes
UART DMA General / production Needs UART pins; baud via config
SWO/ITM Debug without UART Probe + SWO pin; ARMv7-M/E-M
SEGGER RTT Fast interactive debug J-Link; needs submodule ext/segger-rtt
Flash Post-mortem / crash log blog_init() resumes; wipe with blog_flash_reset()

Minimal samples live under examples/ (uart_dma, swo, rtt, flash).

Size

Figures below are for the demo BareLog.elf on STM32F103xB with -Os (library + startup + small main), not a stripped consumer link. Remeasure with just build-all && just size.

Backend text (ROM) data+bss (RAM)
UART DMA 4412 B 920 B
SWO 4236 B 920 B
SEGGER RTT 5036 B 1264 B
Flash 4520 B 928 B

Directory layout

include/       Public headers
src/           Core and default backend implementations
port/stm32f1/  F1 port (startup, linker, device, UART/flash hooks)
port/stm32f4/  F4 port
cmsis/         CMSIS core headers shipped for the ports
ext/           SEGGER RTT (git submodule)
tests/         Host Unity tests
tools/         Flash decode, clang_check.py
examples/      Per-backend sketches

If something goes wrong

  • No output: confirm a flush path is running; check backend and probe wiring.
  • Partial output / gaps: ring buffer overflow (dropped); flush more often or enlarge BLOG_RINGBUF_SIZE.
  • Flash log empty after reset: blog_init() does not erase; use blog_flash_reset() when you intentionally wipe. Decode dumps with just flash-decode <file>.
  • IDE noise about unused includes in blog.h: that header is an umbrella API; see .clangd (UnusedIncludes: None).
  • Host just test hangs on older trees: current concurrent tests need the host IRQ shim (src/blog_irq_host.c).

clang syntax pass over compile databases:

python tools/clang_check.py

About

Lightweight, logging library for bare-metal STM32 - zero dependencies, zero dynamic memory, zero HAL.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages