An essential tool especially developing larger applications or distributed firmware is to use logging. This article presents an open source logging framework I’m using. It is small and easy to use and can log to a console, to a file on the host or even to a file on an embedded file system as FatFS.
Outline
While it is possible to monitor with the debugger multiple targets, it gets very difficult if the system is distributed or runs for days and weeks continuously as in this system. If something goes wrong, logs will be invaluable. While logging framework for host development are common, I have not found much suitable for embedded system. What I have found close to what I wanted was https://github.com/rxi/log.c by rxi or uLog (https://github.com/rdpoor/ulog) by Robert Poor. But I needed something more integrated and ready to use for my applications:
- Different log levels (trace, warning, information, error, fatal)
- Ability to set a log level (only log above a level)
- Color coding based on log level
- Open argument list (printf() style)
- Different ways to log: UART/Terminal, file on the host, embedded file system
- Command line/Shell interface
- Small footprint, depending on the features used
- Configurable, up to the point to completely disable it
- Reentrant and integrated with FreeRTOS
- Automatic and configurable date/timestamping
- Easy to integrate, written in C
Source Files
The McuLog consists of three files (see links to GitHub at the end of this article) only:
- McuLogconfig.h: configuration header file
- McuLog.h: interface file
- McuLog.c: imlementation file
The configuration header file is used to configure the log format (with or without date, colors, if file system or RTT file logging shall be supported, etc). At the time of this article the following configuration items are available:
/* * Copyright (c) 2020, Erich Styger * * SPDX-License-Identifier: BSD-3-Clause */ #ifndef MCULOGCONFIG_H_ #define MCULOGCONFIG_H_ #include "McuLib.h" #ifndef MCULOG_CONFIG_IS_ENABLED #define MCULOG_CONFIG_IS_ENABLED (1) /* 1: Logging is enabled; 0: Logging is disabled, not adding anything to the application code */ #endif #ifndef MCULOG_CONFIG_USE_MUTEX #define MCULOG_CONFIG_USE_MUTEX (1 && McuLib_CONFIG_SDK_USE_FREERTOS) /* 1: use a RTOS mutex for the logging module; 0: do not use a mutex */ #endif #ifndef MCULOG_CONFIG_USE_COLOR #define MCULOG_CONFIG_USE_COLOR (1) /* 1: use use ANSI color for terminal, 0: do not use color */ #endif #ifndef MCULOG_CONFIG_USE_FILE #define MCULOG_CONFIG_USE_FILE (0) /* 1: use use file for logging, 0: do not use file */ #endif #ifndef MCULOG_CONFIG_LOG_TIMESTAMP_DATE #define MCULOG_CONFIG_LOG_TIMESTAMP_DATE (1) /* 1: add date to timestamp, 0: do not date for timestamp */ #endif #ifndef MCULOG_CONFIG_USE_RTT_LOGGER #define MCULOG_CONFIG_USE_RTT_LOGGER (1) /* use use SEGGER RTT Logger (Channel 1), 0: do not use SEGGER RTT Logger */ #endif #ifndef MCULOG_CONFIG_PARSE_COMMAND_ENABLED #define MCULOG_CONFIG_PARSE_COMMAND_ENABLED (1 && MCULOG_CONFIG_IS_ENABLED) /* 1: shell command line parser enabled; 0: not enabled */ #endif #endif /* MCULOGCONFIG_H_ */
Settings can be turned on/off at runtime too, e.g.
void McuLog_set_color(bool enable);
can be used to turn on/off color coding.
Usage
To use the McuLog module, include its interface:
#include "McuLog.h"
Before using it, call its initialization routine:
McuLog_Init();
Next, register one or more logger for the output, for example:
McuLog_open_logfile("0:\log.txt"); /* logging to a file */ McuLog_set_console(&UART_stdio); /* logging to a console */ McuLog_set_rtt_logger(true); /* log to a file on the host using RTT Data Logging */
After that, you can log messages with one of the available logging functions, e.g.
McuLog_trace("This is a trace message"); McuLog_debug("Function called returned error code %d", returnValue); McuLog_info("Application started"); McuLog_warn("Log on memory, available bytes %d", HeapNofBytes); McuLog_error("Failed creating task '%s', error code %d", taskName, res); McuLog_fatal("Watchdog timed out, waiting for reset...");
Command Line Shell
McuLog includes an optional command line shell which is used to check the status and to configure it:
Logging to a Console
If logging to a console (or UART), it will print the messages on that connection. If the console is able to display colors (e.g. PuTTY or Segger RTT Console/Viewer), the messages are shown with different colors:
Logging to a File on the Host
Logging to a file on the host is implemented using RTT.
Start Data Logging on the host:
Then select the file where to log the data. Data is logged in a text file:
Logging to a file can be stopped any time.
Logging to a file on the target
The logger includes logging to an embedded file system using FatFS. It is possible to open and manage the file on the application side.
Or simply create or open the file with the McuLog:
McuLog_open_logfile("0:\myLogFile.txt");
The file is ‘synced’ for each log entry. To stop logging call
McuLog_close_logfile();
The messages will be present in the file on the embedded file system:
Summary
With the McuLog I have a small and versatile logging module which can log to console, host file system or to an embedded file system with small overhead. The module requires about 6 KByte flash (mostly because of printf() style), this without any code optimization. It uses simple callbacks and hooks, so if you need logging over I2C, USB, SPI, … or any other communication channel, this can be easily added or extended.
I hope you find this McuLog module as useful as I do.
Happy Logging 🙂
Links
- McuLog Source Files on GitHub:
- Implementation in McuOnEclipse McuLib: https://github.com/ErichStyger/McuOnEclipseLibrary
- Example project on GitHub using McuLog and logging toFatFS (SD Card): https://github.com/ErichStyger/mcuoneclipse/tree/master/Examples/MCUXpresso/FRDM-K22F/FRDM-K22F_FatFS_SD_Card, see “FatFS, MinIni, Shell and FreeRTOS for the NXP K22FN512“
- uLog by Robert Poor: https://github.com/rdpoor/ulog
- log.c by rxi: https://github.com/rxi/log.c
- Segger RTT: Using Segger Real Time Terminal (RTT) with Eclipse
What would be the requirements to run McuLog? For example, the cpu must be capable of running FreeRTOS and have 6KB of program space.. what about RAM? SEGGER RTT Logger?
LikeLike
You don’t need FreeRTOS, you can run it bare metal too.
As for RAM, see the struct ‘L’ around line 50 in https://github.com/ErichStyger/McuOnEclipseLibrary/blob/master/lib/src/McuLog.c.
With Console and RTT it needs 24 bytes only (for bare metal without the need for a lock/mutex it would be even lower).
So really not much at all (not counting in the normal RAM/Codes size used for the UART connection which I assume is used anyway), otherwise you have to add this too.
LikeLike
Hi Erich
Some of the code snippets seem to have issues with extraneous & etc.?
Cheers
Tommy
LikeLike
Hi Tommy,
thanks for the note: WordPress sometimes converts code in snippets into HTML code :-(. I have fixed it now, hopefully it stays like this.
Erich
LikeLike
Another C/C++ log library EasyLogger (https://github.com/armink/EasyLogger) may be also a good choice for MCU. :>
LikeLike
Indeed! Thanks for sharing!
LikeLike
I like this as a small simple logging module- thanks Erich.
I’ve extended the ‘trace’ logic to be enableable, via a bitmask- mainly because I have only a single UART to get any information over. This way I can group messages by logical function:
Trace_feature(TraceMaskBitUART, “A UART trace\n”);
This is only displayed when the TraceMaskBitUART bit is set, which I’d do with
traceon 400
for instance (also, ‘traceoff %x’)
I’ve found this really useful to be able to debug modules by logical function without excessive ‘noise’. In fact, it has been my primary debugging method for several years. Hopefully it’ll help someone out.
Cheers
LikeLike
Hi Rhys,
I really like that idea of having such a bit mask, and that you can turn on/off logging for a group of things! I think I have to add something like this too :-).
LikeLike
Glad if you can make some use of it. Happy to send you my source as a reference if you like, I’d just rather not post it verbatim publicly. Can you contact me offline? (and not post this comment 😛 )
LikeLike
Hi Rhys,
thank you for that offer, but I think it is not necessary, I guess I will find my way 😛.
Erich
LikeLike
Hi Erich,
I am looking for a simple embedded log which should:
1. Store the log in SRAM or flash memory, and the log has only 100 entries of 32 or 64-byte each, and organized as circular buffer manner.
2. No any specific SW needed but a terminal SW such as TeraTerm to access the log via a serial port, and it will display the log content only when a command is received.
Not sure if your log or the logs you mentioned can be the modified and configured with the requirements above.
Thanks
LikeLike
Hi Zhiqun,
1. is something you can be easily add/implement, and
2. is implemented already
Erich
LikeLike
Pingback: How to get Data off an Embedded System: FatFS with USB MSD Host and FreeRTOS Direct Task Notification | MCU on Eclipse
Pingback: How to Use Eclipse CDT Environment Variables in C/C++ Code | MCU on Eclipse
Hello @Erich, seems a very interesting library indeed! Allow me for a probably noob question, but this library is only implemented in MCULib (so I’d guess it only works for NXP Processor Expert projects), so if I would repurpose it for a STM32Cube project, I’d need to reimplement it?
Thank you for your attention
LikeLike
No, it can work with any microcontroller, as the library and McuLib is implemented in C. I’m mainly using it with NXP parts, but as well with microcontrollers from other vendors (see the list of processors in McuLibConfig.h).
LikeLike
Pingback: assert(), __FILE__, Path and other cool GNU gcc Tricks to be aware of | MCU on Eclipse