Splitting long data fields over multiple lines in Data Window


I’m simply wondering if there is a way to insert a “line feed” character into a test string to be displayed in the Data Window. This would be used to display a larger number of bytes in a way that doesn’t require the Data Window to be expanded so that it takes up the entire screen.
In the following example, the ‘data’ field contains 32 bytes of data, but is displayed in one long line in the Data Window:

Is it possible to display these 32 bytes of data on (say) 4 lines of 8 bytes? Note that this is all from the same HLA frame.

Is there an escape character that would accomplish this? I have tried the usual suspects and nothing has worked.

Thanks and regards,


Hi Steve,

Yes, HLAs make formatting the terminal pretty easy.

Any time you call print() from an HLA, the results will appear in the terminal.

I would recommend switching off the terminal for any other analyzers you have, so you don’t get a mix of results from different sources.

Then, in your HLA, you could customize exactly how print is called to include periodic line breaks at an interval you choose.

print(‘\n’) should work. Here is a quick example:

Note, I turned off “stream to terminal” for the async analyzer, so I wouldn’t get a mix of data from the two analyzers.


You mentioned the same “HLA Frame”. I assume you’re producing this from a HLA, but if you mean you’re using FrameV2 from a C++ LLA, then this won’t work.

The terminal functionality for LLAs is limited. Serial, I2C, and SPI have hard-coded special terminal functionality, but all other C++ LLAs get a generic implementation where the results from the LLA’s GenerateFrameTabularText function in ASCII mode are directly printed to the terminal. Note, ‘\n’ characters should still work here. Also, returning more than 1 string from GenerateFrameTabularText will cause all strings to be joined with newline characters. Sorry this isn’t documented! The LLA API is in poor shape. We eventually want to do a huge overhaul, but due to the large amount of legacy support required, it’s going to be a huge project so I don’t want to start on it until the company is a little larger.

Hi Mark,

Thanks very much for your explanation and example – they certainly clear a lot of things up!!

Just to be clear, however, I was wondering if it were possible to display a field using multiple lines in the Data Table Window, not the Terminal Window. (I just realized, by the way, that the Timing Windows DOES display the data in the order I want – if there were only a way to have the Data Table Columns re-ordered, as well…)

To be fair, the Terminal can definitely be used to give me the kind of output I’m looking for. I think the only disadvantage is that I lose the ability to “Go To” a specific transaction if I’m looking at the Terminal Window (unless there’s another magic function that I’m not aware of… :wink:)

Thanks again for taking the time to provide such a detailed answer, with an example to boot!!!

Kudos to you and the entire development team – The software is intuitive and powerful, and provides a lot of nice features that make it a pleasure to use!

Now you just need to get your hardware team to provide more complicated triggers in the hardware (e.g. sequences of events or more complicated conditional triggers :blush:)



I’ll reply twice here to address both points.

First off - I’d love to hear more about your specific trigger needs. Could you describe the use cases you have for more advanced triggers, and what you’re looking for?
This has been a big limitation of our product for some time, but we really want to make sure we understand what users are trying to do before making the trigger more powerful. The why is the most important part, which allows us to consider a wider field of solutions for the problem.

Lastly, if you have used any tools that you think had an amazing trigger system, please let me know!

Ah sorry about that!

The data table display needs a lot of fine tuning. One thing we think a lot about is allowing it to be popped out into a new window, since the data is often very wide. I had not thought about wrapping the cell contents to solve this, which seems like a great idea.

In the short term, the only solution would be to break the data into more frames. HLAs can return an array of frames at once instead of a single frame, or it can return None if no frame should be omitted. This might not make sense for your application though.


Thanks for clarifying this. I’m intrigued by the idea of multiple frames, but am a bit unclear as to exactly how these would be displayed in the Data Table Window. I find that documentation on this aspect is a bit sparse, and that examples go a long way towards helping crystalize understanding of how things work. A simple example that illustrates how multiple frames could be returned and how the Data Table Window is affected would be quite helpful.

I’m also curious to know, for example, how the columns in the Data Table Window can be modified and ordered according to requirements. Let’s say I’m using an I2C Analyzer, but am not interested in the “Start” or “Duration” columns of the I2C analyzer, or if I wanted to replace the “Duration” column by a “Device” column that would contain a decoded device address (So that the order of the columns was now { “Type”, “Device”, “address”, “data”, “count” }). Is this even possible? The order of the columns doesn’t seem to be affected by the AnalyzerFrame() call (based on some tests that I’ve tried), and I’m just wondering if there is a mechanism for “playing with” the displayed columns and their order… Again, an example here could be quite useful.

I’m sorry if I’m asking questions that may be addressed in the documentation - I just haven’t found any clear references to these topics, and it’s easier to ask a question than dig through documentation (particularly when I have other things I need to be doing and this is more of a “research” investigation).

In closing, let me repeat how much I (and many other people here) appreciate both your hardware and software. Since we acquired our first Saleae unit we’ve purchased close to a dozen others – although the USB transfer speed imposes some limitations on the capture bandwidth when dealing with multiple channels, the compactness of the unit and the performance of the software are truly remarkable, and for many applications the Saleae Pros we have are quite invaluable… Some of our employees have even taken advantage of your enthusiast program to purchase their own personal units!

Best regards,


1 Like

Hi Steve,

First we don’t offer the ability to control the order of the columns yet, or the ability to order them in-app. It’s on our backlog, but unfortunately it will be a while before we can do another pass on the data table.

That said, you can hide columns you don’t want to see in the UI by using the right-click menu:

Second, about multiple frames. Here is a quick example showing an HLA producing multiple frames from a single input frame.

Here you can see the new frames in the output. Each of those 3 frames was produced from the single frame below it.

Here you can see them in the sidebar. For HLAs, there is exactly 1 row per AnalyzerFrame returned by your HLA.

Note - the frames you produce from your HLA must always be returned in time order, sorted by their start times. That is true both between calls to decode() as well as between frames returned by decode(), when returning more than one frame at a time, as shown here. Otherwise we’ll get undefined behavior in the app.

You can see the source code below. the decode() function can return one of thee possible types:

  • None indicating no frame should be produced
  • AnalyzerFrame, indicating one frame is produced
  • List[AnalyzerFrame] indicating multiple frames are produced, as shown below.
from saleae.analyzers import HighLevelAnalyzer, AnalyzerFrame, StringSetting, NumberSetting, ChoicesSetting

class Hla(HighLevelAnalyzer):

    result_types = {
        'mytype': {
            'format': '{{data.original}} [{{data.subframe}}]'

    def __init__(self):

    def decode(self, frame: AnalyzerFrame):
        gap = (frame.end_time - frame.start_time) / 10
        width = (frame.end_time - frame.start_time)/3

        time1 = frame.start_time
        time2 = time1 + width
        time3 = time2 + width
        time4 = time3 + width

        # Return the data frame itself
        frame1 = AnalyzerFrame('mytype', time1, time2 - gap, {
            'original': frame.data['data'],
            'subframe': 1

        frame2 = AnalyzerFrame('mytype', time2, time3 - gap,  {
            'original': frame.data['data'],
            'subframe': 2

        frame3 = AnalyzerFrame('mytype', time3, time4,  {
            'original': frame.data['data'],
            'subframe': 3

        return [frame1, frame2, frame3]