Skip to the content.

Getting Started with Trice

Contents

1. Get it

2. Install It

3. Try it

#include "trice.h"

int tryIt( void ){
    trice( "Hello! 👋🙂\a\n" ); // A message with sound and without target timestamp.
}

You can also edit any of your existing project files accordingly. Just replace any printf with trice. (Handle float or double numbers and runtime-generated strings, according to Trice Similarities and Differences to printf Usage. The file _test/testdata/triceCheck.c shows many usage examples. The uppercase Trice macros are inlining the complete Trice code and the lowercase Trice macros are function calls, so most probably you want use trice to keep the overall code size smaller.

For new projects, use Bind:

touch til.json li.json # Only on the first setup; retain these files afterward.
trice bind -src . -genDir build/sidecars # Also creates the generated directory.

Add your configuration and build/sidecars to the compiler include path, followed by src and src/default_conf. Run Bind before compilation whenever log sources change. Bind adds generated includes; your Trice statements keep their original form. Keep til.json in version control and retain the local li.json; generated sidecars are build artifacts. The F030 RTT and UART examples show the setup.

The legacy Insert/Clean workflow remains available:

With Insert/Clean, use trice insert before compilation and optionally trice clean afterward to remove inline IDs. Choose one workflow for a source tree; do not run Insert over Bind-owned files. See switching workflows.

The optional Trice cache technique avoids un-edited file changes at all, which means no Trice-related build speed disadvantages.

See Trice Cache for Compilation Speed for more details and examples/G1B1_inst/build.sh as example.

A quick setup is possible when using RTT as output channel. Otherwise you need to setup a serial port for Trice data transmission. Other output paths possible too using the auxiliary interface.

4. Use It

(back to top)

5. Fork It (get a contributor)

If you wish to get a contributor please fork the Trice repository.

5.1. ✅ What “forking” means

Forking creates your own copy of someone else’s repository under your account.
You can then:

5.2. 🧭 How to Fork (GitHub)

1. Go to the repository you want to fork

Example: https://github.com/rokath/trice

2. Click the **“Fork” button (top-right)**

You’ll be taken to a Create Fork page.

3. Choose options (usually leave defaults)

Click Create Fork.

4. Clone your fork locally

git clone https://github.com/YOUR_USERNAME/trice.git && cd trice

5. (Optional but recommended) Add the original repo as upstream

This lets you pull updates later.

git remote add upstream https://github.com/rokath/trice.git

Check remotes:

git remote -v

6. Keep your fork updated

git fetch upstream git merge upstream/main

Or:

git pull upstream main

6. Clone It

1. Make sure Git is installed

Check with:

git --version

If not installed, download from https://git-scm.com

2. Clone the repository

Run this command in your terminal or command prompt:

git clone https://github.com/rokath/trice.git

This creates a local folder named trice with the full project history.

3. (Optional) Enter the project folder

cd trice

7. Build It

See Build Trice tool from Go sources.

8. Modify It

If for example you wish to change the logging capabilities, like changing/extending CLI switches, thanks to Go this is very easy also if you are not familiar with Go. See this example.

9. Port it

Trice should be usable on any MCU with any compiler. On ARM MCUs the easiest way is to use SEGGER J-Link with RTT as output. Setting up UART transmission as alternative or additionally is also no big deal.

Compare folders of one of these folder groups:

Without Instrumentation With Trice Instrumentation Remarks
./examples/F030_bare ./examples/F030_inst no RTOS
./examples/G0B1_bare ./examples/G0B1_inst FreeRTOS
./examples/L432_bare ./examples/L432_inst FreeRTOS

This way you see in a quick way any needed adaptations for your target project to port trice to it.

The chapter Example Projects without and with Trice Instrumentation contains further helpful information.

9.1. Target Macros

The easiest and mostly sufficient way to use Trice on the target side is the Trice macro

trice("Hello world!"); // without     timestamp
Trice("Hello world!"); // with 16-bit timestamp
TRice("Hello world!"); // with 32-bit timestamp

which you can mostly use as a printf replacement in legacy code. See Trice Similarities and differences to printf usage for more details. Is uses the TRICE_DEFAULT_PARAMETER_BIT_WIDTH value (usually 32), which is equal for all values.

The additional macros

are always usable and the number 8, 16, 32, 64 specifies the parameter width, which is equal for all values within one macro. Trice macros are partially disabled, when the value TRICE_SINGLE_MAX_SIZE is defined to be smaller than 104. For example with TRICE_SINGLE_MAX_SIZE == 8, TRice32 can have no parameter value (4 byte Trice header, 4 byte stamp) and trice8 can have up to 4 parameter values (4 byte Trice header, 4 byte values) That’s mainly to get compiler errors rather than runtime errors.

More examples:

Trice Header Stamp max. Values Trice Size
trice8 4 0 0 *1 byte 4
… … … … …
trice8 4 0 12 *1 byte 16
Trice8 4 2 0 *1 byte 6
… … … … …
Trice8 4 2 12 *1 byte 18
TRice8 4 4 0 *1 byte 8
… … … … …
TRice8 4 4 12 *1 byte 20
trice16 4 0 2 *2 byte 8
Trice16 4 2 1 *2 byte 8
trice32 4 0 1 *4 byte 8
Trice32 4 2 1 *4 byte 10
TRice32 4 4 2 *4 byte 16
trice64 4 0 1 *8 byte 12
TRice64 4 4 1 *8 byte 16
… … … … …
TRice64 4 4 12 *8 byte 104

The value TRICE_DEFAULT_PARAMETER_BIT_WIDTH is the parameter bit with for the macros trice, Trice, TRice (without number). It can make sense to set this value to 16 on smaller machines.

The full uppercase macro Trice is a Trice macro only using inline code. Because the main design aim was speed, this was the original design. Then it became clear, that several hundred of Trice macros increase the needed code amount too much and that it is better to have just a function call instead of having inline macros. If speed matters use TRICE(id(0), TRICE(Id(0), TRICE(ID(0) else use trice(iD(0), Trice(iD(0), TRice(iD(0) or mix usage as you like. The lower case macros internally use Trice like code but each is only a function call and therefore needs less space.

9.2. Target Trice Stamps

Hint: I usually have the 32-bit timestamp as millisecond counter and the 16-bit timestamp as systick counter to measure short execution times.

9.3. Trice Checks

9.4. Communication Ports

9.5. Target Code Overview

File description
trice.h trice runtime lib user interface, #include trice.h in project files, where to use Trice macros. Add ./src to your compiler include path.
triceConfig.h Create this file to overwrite triceDefaultConfig.h as needed.

See Trice Project Image Size Optimization for compiling src/*.c and removing unused library sections from the final image.

File description
cobs.h message packaging, alternatively for tcobs
cobsEncode.c message encoding, alternatively for tcobs
cobsDecode.c message decoding, normally not needed
trice.c trice core lib
trice8McuOrder.h trice MCU endianness lib
trice8McuReverse.h trice MCU reverse endianness lib
trice16McuOrder.h trice MCU endianness lib
trice16McuReverse.h trice MCU reverse endianness lib
trice32McuOrder.h trice MCU endianness lib
trice32McuReverse.h trice MCU reverse endianness lib
trice64McuOrder.h trice MCU endianness lib
trice64McuReverse.h trice MCU reverse endianness lib
SEGGER_RTT.h Segger RTT code interface
SEGGER_RTT.c Segger RTT code
SEGGER_RTT_ConfDefaults.h SEGGER defaults for settings not supplied by the project
SEGGER_RTT_Conf.h Small Trice fallback configuration; project headers take precedence
SEGGER_RTT_LICENSE.md SEGGER license to retain when redistributing its target sources
tcobs.h message compression and packaging interface
tcobsv1Encode.c message encoding and packaging
tcobsv1Decode.c message decoding and packaging, normally not needed
tcobsv1Internal.h message decoding and packaging internal interface
trice8.h 8-bit trice code interface
trice8.c 8-bit trice code
trice16.h 16-bit trice code interface
trice16.c 16-bit trice code
trice32.h 32-bit trice code interface
trice32.c 32-bit trice code
trice64.h 64-bit trice code interface
trice64.c 64-bit trice code
triceAuxiliary.c trice code for auxiliary interfaces
triceDefaultConfig.h This file contains the most probably settings and serves also as a reference for tuning your project triceConfig.h
triceDoubleBuffer.c trice runtime lib extension needed for fastest deferred mode
triceStackBuffer.c trice runtime lib extension needed for direct mode
triceRingBuffer.c trice runtime lib extension needed for recommended deferred mode
xtea.h XTEA message encryption/decryption interface
xtea.c XTEA message encryption/decryption code

(back to top)

9.6. User Code Adaptation

The Trice macros are designed for maximal execution speed and therefore we have to pay the price for their limited capabilities.

9.7. Limitations

You should be aware that these parameter strings go into the target and slow down the execution. So, whenever a string is known at compile time it should be part of the Trice format string.

The Trice source code parser has very limited capabilities, so it cannot handle C-preprocessor string concatenation.

9.8. Trice (Time) Stamps

9.9. Trice Parameter Bit Widths

Hint: With the default TCOBS framing 8-bit values as 32-bit parameters typically occupy only 2-bytes during transmission.

10. Avoid it

10.1. Parser Limitation

Because the implemented source code parser for trice insert and trice clean is only a simple one, there is one important limitation:

trice( "hi 0" );
// An "allowed" example comment.
trice( "hi 1");
// An \" allowed example comment.
trice( "hi 2");
// A " NOT allowed example comment. This disrupts the parsing.
trice( "hi 3");
// A " NOT allowed example comment. This enables the parsing after a disruption.
trice( "hi 4");

10.2. Trice macros in header files

10.3. Trice macros inside other macros

There is nothing wrong, when putting Trice macros into other macros. But: When running the self made macro, the location information of the inner trice macro will point to the self made macro definition and not to its execution location.

Example: When Functions fnA and fnB are executed, the MY_MESSAGE location information points to file.h and not into the appropriate lines inside file.c.

file.h:

#define MY_MESSAGE trice("msg:Hi\n"); // self made macro

file.c:

void fnA( void ){
  ...
  MY_MESSAGE
  ...
}

void fnB( void ){
  ...
  MY_MESSAGE
  ...
}

10.4. Upper case only TRICE macros should be written with id(0), Id(0) or ID(0)

The stamp size 0, 16 or 32 is usually controlled by writing trice, Trice or TRICE or for upper case only Trice macros by using id(0), Id(0) or ID(0). When writing TRICE("hi"); for example, the Trice CLI switch -defaultStampSize controls the ID insertion, but this is then equal for all new TRICE messages.

(back to top)