Project Background and Resources
Contents
1. Download and install Trice
Choose a release or a source checkout, then use its matching target sources, host tools, and documentation. An older release binary may not contain features documented by a newer checkout. See firmware and host-tool compatibility.
- GitHub Releases provides published host-tool archives and source archives. Choose the operating system and architecture of your host computer; extract both
triceandtlogand add their directory toPATH. -
For a checkout build, run the existing script from the repository root with Go and Bash installed:
./scripts/buildTriceTool.sh trice --version tlog --versionThe script builds
triceandtlogand prints the installation paths. Use the Go version required by go.mod; see building the host tools for details. - For v2, use a release archive or build a checkout of its tag. Remote
go install github.com/rokath/trice/cmd/trice@v2.0.0is not supported while the module path has no/v2suffix. A local checkout build does not require that suffix. - The macOS Homebrew tap provides stable releases:
brew install rokath/tap/trice. For a beta, use its tagged release archive or build from its source checkout. Confirm the installed version rather than assuming it includes the current development checkout.
For the first hardware-free experiment, follow the User Manual. Its PC examples additionally require a native C compiler. Shell examples use Bash; Windows users can run them in Git Bash with a Windows host compiler. For target integration, choose a firmware quickstart.
2. Alternative projects and related approaches
Trice combines printf-like source calls, ID-based host decoding, and configurable binary transport. Other projects emphasize different languages, runtime environments, tracing models, or integrations. These links are starting points for comparison, not a claim that all projects provide the same features or interchangeable formats.
2.1. Logging and deferred formatting
- defmt: deferred formatting for embedded Rust.
- Zephyr dictionary-based logging.
- Memfault Compact Logs.
- Postform.
- Embedded Logger (elog).
- McuLog.
- uLog.
- NanoLog.
- Diagnostic Log and Trace (DLT).
2.2. Tracing and visualization
- Baical UP7.
- Pigweed tokenized tracing.
- SEGGER SystemView.
- Percepio Tracealyzer.
- QP/Spy.
- MCUViewer.
- RTEdbg.
- Tonbandgerät.
- Traces.
2.3. Other techniques and background reading
- Plain
printfover UART, or a project-specific binary logger. - Hardware trace through Arm ITM/SWO, and Arm Keil Event Recorder.
- SEGGER RTT as a transport; Trice can also use RTT.
- Logging with symbols in the Embedonomicon.
- Call-stack logging through function instrumentation.
- Dynamic printf breakpoints.
- Minimal Structured Logging for Autonomous Vehicles, a talk about another approach.
3. Community and contributions
Use GitHub Discussions for questions, ideas, and integration experience. Search open and closed issues for known problems and previous resolutions. For a reproducible bug, include the tool version, relevant configuration, command, expected result, and actual output.
Examples, documentation improvements, platform recipes, fixes, and reproducible performance measurements are welcome. CONTRIBUTING describes the development workflow, CHANGELOG records release changes, and the MIT License governs the project. AUTHORS lists contributors.
4. Support and sponsoring
If Trice helps your work, you can support it by sharing an example, improving the documentation, reporting a reproducible problem, contributing a fix, or starring the repository. Financial support for continued maintenance is also welcome:
These are the support routes configured by the project in FUNDING.yml. Sponsoring is optional; using Trice does not require a donation.
5. Project history
For the cumulative release changes, see Progress from 1.0 to 2.0.
Developing firmware means to deal also with interrupts and often with timing. How do you check, if an interrupt occurred? OK, increment a counter and display it in a background loop with some printf-like function. What about time measurement? Set a digital output to 1 and 0 and connect a measurement device. Once, developing software for a real-time image processing device, I had no clue where in detail the processing time exploded when the image quality got bad. A spare analog output with a video interrupt synced oscilloscope gave me the needed information, after I changed the analog output on several points in my algorithm. But, hey guys, I want to deal with my programming tasks and do not like all this hassle connecting wires and steer into instruments.
A printf is so cool on a PC, developing software there. But an embedded device often cannot use it for performance reasons. My very first attempt was writing the format string .const offset together with its values in a FIFO during a log statement and to do the printf it in the background. But that is compiler specific. OK the full string address is better but needs buffer space. Zephyr for example does something like that calling it “deferred logging”.
Then, one day I had the idea to compute short checksums for the format strings in a pre-compile step and to use them as ID in a list together with the format strings. That was a step forward but needed to write a supporting PC program. I did that in C++ in the assumption to get it better done that way. Finally, it worked, but I hated my PC code, as I dislike C++ now because of all its nuts and bolts to handle, accompanied by missing libraries on the next PC. The tool usability was also unhandy and therefore error prone and the need became clear for a full automated solution. Also, what is, if 2 different format strings accidentally generate the same short checksum? There was a way around, but an ID based message filtering will never be possible that way.
The need became clear for controllable IDs and management options. And there was Go now, an as-fast-as-C language, easy to learn, promising high programming efficiency and portability. It would be interesting to try it out on a real PC project.
Trying to add tags in form of partial Trice macro names was blowing up the header code amount and was a too rigid design. Which are the right tags? One lucky day I came to the conclusion to handle tags just as format string parts like "debug:Here we are!\n" and getting rid of them in the target code this way also giving the user freedom to invent any tags.
Another point in the design was the question how to re-sync after data stream interruption, because that happens often during firmware development. Several encodings were tried out and a proprietary escape sequence format and an alternative flexible data format with more ID bits were working reliably but with COBS things got satisfactory. A side result of that trials is the Trice tool option to add different decoders if needed. Now the default Trice message framing is TCOBSv1 which includes short message compression and this way allows very low transmit bandwidths and/or saves storage, when binary Trice data are stored in Flash memory.
There was a learning not to reduce the transmit byte count to an absolute minimum, but to focus more on Trice macro speed and universality. That led to a double buffer on the target side as an alternative to the ring buffer solution. The actual binary encoding, allowing alongside user protocols, is the result of the optional target timestamps and location info some users asked for, keeping the target code as light as possible. Float and double number support was implementable for free because this work is done mainly on the host side.
Trice grew, and as it got usable I decided to make it Open Source to say “Thank You” to the community this way.
Learning that Trice is also a baby girl name, our daughter Ida designed the little girl with the pen symbolizing the Trice macro for recording and the eyeglasses standing for the PC tool Trice visualizing the logs.

5.1. Progress from 1.0 to 2.0
ID-based binary logging, Insert/Clean, target buffers, COBS/TCOBS, XTEA and RTT were already available in 1.0. The table separates later improvements from the features introduced with 2.0; use the Changelog for individual release notes.
| Release period | Progress for users | Current reference |
|---|---|---|
| 1.1.x | Macro aliases and user tags, UDP input, additional stamp/version information and more host packages. | Existing logging macros, CLI |
| 1.2.x | More stamp-delta and portability support, shared decoder registration, format-string removal and printf-length corrections. Standalone tlog arrived in 1.2.3; installation packages expanded. |
Stamps, Printf comparison, Build and release |
| 1.2.4 to 1.3.0 | Counted typeX0 payloads, ABC reception and broadcast commands with multi-node examples. ABC was already part of 1.3.0. | typeX0, ABC |
| 2.0 | Bind supplies IDs through generated headers so source calls remain ID-free. Insert/Clean stays available; compiler and wrapper limits are documented. | Bind |
| 2.0 | Named structured fields with text/JSON/key-value output; Context Enrichment adds selected context values with explicit limits for direct and wrapper/rebase sites. | Structured logging, Context Enrichment |
| 2.0 | Weighted tags, filtering, configurable colors, consistent macro-alias identity and updated CLI/custom-tag spellings. | Tags, Alias identity |
| 2.0 | Host-side printf, unwrap and delta expressions for stamps, plus numeric visualization through files or UDP. |
Stamp expressions, Visualization |
| 2.0 | Local deferred text logging on the target, source-selected C metadata, readable dictionaries and Bind reports. | Target implementation, Generation |
| 2.0 | Deterministic ID assignment for identical calls, improved reload/replay and filter behavior, a guided User Manual, a split reference and two release PDFs. | IDs, User Manual, Reference |
Before rebuilding a 1.x project, follow the migration guide and preserve the matching firmware, tool and dictionaries. The unchanged record layout alone does not make old format strings compatible with the new field syntax.
6. Ideas for future development
These are ideas and historical proposals, not release commitments.
The Future Design Ideas appendix preserves the dictionary-schema, dashboard-adapter and C-host-decoder proposals recovered from the former archive.
6.1. Further Context Enrichment Variants
Context Enrichment supports direct Bind log sites and reversible source extensions with insert/clean. Bind rejects selected CE sites in wrapper macros or counter-rebase regions. Use an ordinary function, put direct calls on separate source lines, or use insert/clean as described under bind-limits.
6.2. Improving the Trice Tool Internal Parser (not planned right now)
6.2.1. Trice Internal Log Code Short Description
Trice v1.0 Code
Hint: To follow this explanation with the debugger, open the trice folder in VSCode, click the Run & Debug button or press CTRL-SHIFT-D, select
trice l -p DUMPand set a breakpoint atfunc main()in ./cmd/trice/main.go or directly intranslator.Translatein ./internal/translator/translator.go.
- In file ./internal/args/handler.go function
logLoopcallsreceiver.NewReadWriteCloserand passes the createdrwcobject totranslator.Translate.- There
rwcis used to create an appropriate decode objectdecpassed todecodeAndComposeLoop, which usesfunc (p *trexDec) Read(b []byte) (n int, err error)then, doing the byte interpretation.
- Finally
n += p.sprintTrice(b[n:]) // use param infois called doing the conversion.- Read returns a single Trice conversion result or a single error message in b[:n] or 0 and is called again and again.
- The returned Trice conversion result is the Trice format string with inserted values, but no timestamp, color or location information. The target timestamp, for this single Trice is hold in
decoder.TargetTimestamp. It is used only when a new log line begins, what is the normal case. If a Trice format string ends not with a newline, the following Trice gets part of the same log line and therefore its target timestamp is discarded. Also additional data like the location information only displayed for the first Trice in a log line containing several Trices. Because the color is inherent to the Trice tag and needs no display space it is attached to the following Trices in a log line as well.- The after
Readfollowingemitter.BanOrPickFiltercould remove the Read result.- If something is to write, the location information is generated if a new line starts and passed to the with
sw := emitter.New(w)created objectsw.WriteStringmethod which internally keeps a slice of type[]stringcollecting all parts of an output line.- The line
p.Line = append(p.Line, ts, p.prefix, sx)adds host timestampts, the buildp.prefixand the “printed Trice” (sxis here just the location information) to the line slice.- In the next step the stored target timestamp
decoder.TargetTimestampis printed into a string and added to the Trice line.- Optionally the Trice ID follows, if desired.
- The in a string printed Trice follows now and if the
sw.WriteStringmethod detects a\nat its end, the configured line suffix (usually"") follows andp.completeLine()is called then. It passes the line (slice of strings) top.lw.WriteLine(p.Line), which adds color, prints to the output device and clears the sw.Line slice for the next line.
Disadvantages of Trice v1.0 Implementation
- The Reader can only return a single Trice, because its byte interface cannot distinguish between Trices anymore.
- The field order (prefix, host stamp, location information, target stamp optional ID, format string, suffix) is hardcoded.
- Trices with several newlines inside the format string cannot deal with tags after an (internal) line break (newline)
- Binary parser needs to hold internal location after one Trice was decoded.
- Only one target timestamp value in a global variable.
- Line writing is not straight forward understandable.
Aims for a better implementation
- Read should parse the binary data only and return a Trice struct slice.
- Cycle errors?
- Each Trice struct gets printed in a string.
- The printed string then is split into several strings, which all get the same stamp information or space fields.
- Finally we have a slice of such structs: hs, ts, loc, idString, format string.
- The format string has no newline inside anymore, but has one at the end (usually) or not.
- The struct slice is cyclically passed to a line writer, which writes one line if it can find a format string ending with a newline.
6.3. Using Trice on Servers
A server can ingest and analyze Trice streams from devices. Using Trice as the server application’s own logger is a separate use case and needs evidence of a practical benefit. Claims about speed, energy use, storage, and additional compression require measurements with equivalent retained information.