Vision

For some time, I have been working in small portions on Dispatch as a serial library for the curve tracer. Initially, I thought that the serial library would be limited in scope to that particular project, but I began to see that the library could, potentially, be very useful on many of my past microcontroller projects.

As the C and Python modules have matured into Dispatch (C) and serialdispatch (Python), so, too, has my vision of what this library could potentially achieve in the future with relatively little effort. I will present this vision in this post.

Dispatch and serialdispatch

The Dispatch libraries, as they are, allow the user to define any topic to which data may be published and consumed. This in itself is quite useful. Data may be sent and received easily and simply for any purpose.

What's Next?

The next level will be to create standard topics to which to publish. When data is written to any of these topics, then the module would automatically bring up the appropriate display type and, then, display the data. We wish to turn standard C strings into a sort of scripting command for the PC to interpret and display with no further user intervention!

Why?

Why do this? Couldn't anyone write any program in Python/Ruby/Java/ that would create an appropriate visualization? Well, yes, but - generally - they won't. Most embedded C and C++ programmers know their chosen language quite well and will dabble in other languages as necessary. As a result, they know best how to manipulate C and probably don't want to bother with (or - more likely - don't have the time to bother with) creating the library that would easily provide the visualization.

In essence, the visualization will be controlled by the C code that is publishing to it with the end user not having to write a single line of Python or other in order to see his/her data.

How?

We will create a standard topic format which should - ideally - be extendable and easy to use.

Modifications to serialdispatch

Currently, subscriptions are expecting to find the exact string that is to be sent as the topic, nothing more and nothing less. For instance, data sent to the topic plot will have different subscribers from data sent to the topic plot (there is an extra space in there). As a result, we will need to add a new type of subscriber to the currently existing serialdispatch which will subscribe if the first few characters match a defined pattern. In this case, plot. I suspect that we will call this new subscriber method something like subscribe_to_prefix or subscribe_to_command. This will not affect the functionality of the normal subscribe and will be 100% backwards compatible.

Format

The standard format will be space-delimited (recall that we have already utilized commas and colons) in the dispatch format). We will use the somewhat-familiar command-line syntax of higher-level OSs. Fortunately, we won't have to interpret them in C but in the more dynamic Python code.

DIS_publish("<command> <option1> <option2> ... <optionX>", &dataPtr1, &dataPtr2, ..., &dataPtrX)

Commands

At the moment, I can only think of a couple of commands that would be most useful. This list is sure to expand:

Command Description
plot Plots the data according to its options.
log Log the data according to its options.
csv Save the data to a CSV file.

Plot Options

Option Short Equivalent Description
--name 'name in single quotes' -n 'name in single quotes' Applies the data to a named plot. If this is not present, the named plot will be 'default'. Writing data to the 'default' plot will have the same effect.
--dots -d The data will be plotted using dots.
--lines -l The data will be plotted using lines.
--polar -p The plot will be a polar plot. If the plot is currently a different type of plot with existing data on it, then that plot will be wiped and the new dataset will be applied.
--keep-data -k Append the data contained herein to the existing data set.
--xLabel 'x-axis label in single quotes' -x 'x-axis label in single quotes' The label for the x-axis.
--yLabel 'y-axis label in single quotes' -y 'y-axis label in single quotes' The label for the y-axis.

Examples

We will begin with the most basic example and expand on that. If you haven't read the Dispatch introductory material on Dispatch, you may experience some confusion as this material will not be explained in detail here.

Basic Plot

These examples are provided as a vision of capability. As happened with Dispatch, this vision of the implementation will likely morph in the future, leaving this blog post behind, but the goal will remain the same.

uint8_t xData = 1;
uint16_t yData = 2;

DIS_publish("plot,u8,u16", &xData, &yData);
//                            ^       ^ data addresses
//                ^   ^ format specifiers
//            ^ command

The simplest plot type. When no additional parameters are supplied, then the first data point is plotted on x and the second data point is plotted on y. If data continues to be published in this manner, the points will continually be added to the plot. If you don't understand the 'u8' and 'u16' notation, go back to the vision posts on Dispatch for a review.

If the plot type is not specified using the --dots, --lines, or --polar notation, then the plot type is assumed to be --dots.

Plotting Multiple Parameters

The above works great when only one plot is required... but what if there are multiple parameters that need to be plotted?

uint8_t xData = 1;
uint16_t y1Data = 2;
uint16_t y2Data = 3;

DIS_publish("plot,u8,u16,u16", &xData, &y1Data, &y2Data);
//                                ^       ^        ^ data addresses
//                ^   ^   ^ format specifiers

It is always assumed that all scatter data has the first element along the x axis and all addional elements on the y axis.

Plotting Multi-Dimensional Data

The primary reason for creating Dispatch in the first place and not simply using the Telemetry library was so that we could send multi-dimensional data of arbitrary length. In other words, we want to send arrays of data, not just single data points:

uint8_t xData[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
uint16_t y1Data[10] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
uint16_t y2Data[10] = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29};

DIS_publish("plot:10,u8,u16,u16", xData, y1Data, y2Data);
//                                  ^  ^   ^ data addresses
//                 ^ number of elements in each array

Note that this is basically the same command with the :10 attached and the & removed from the data addresses since the addresses are now arrays (if you don't understand this, search 'C arrays pointers', enjoy the reading).

Labeling the Plot Axes

In some cases, it is useful to name the data appropriately. In this case, we will use single-quotes to surround the data label.

uint8_t xData[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
uint16_t y1Data[10] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
uint16_t y2Data[10] = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29};

DIS_publish("plot --x 'time' --y 'adc':10,u8,u16,u16", xData, y1Data, y2Data);
//                        ^          ^ axis labels
//                  ^           ^ axis label option

The --x and --y stand for '--xLabel' and '--yLabel`, but the shortened notation allows for somewhat shorter transmissions. Both are valid.

Naming the Plot

We can use the --name option to name the plot. If more than one named plot has data sent to it, then each named plot will have its own plot window allocated to it.

uint8_t time[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
uint16_t adc1[10] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};

uint8_t samples[5] = {0, 1, 2, 3, 4};
uint16_t adc2[5] = {5, 6, 7, 8, 9};

DIS_publish("plot --name 'adc vs. time'", time, adc1);
DIS_publish("plot --name 'adc vs. samples'", samples, adc2);
//                            ^ plot name in single quotes
//                   ^ plot name option

Keeping Old Plot Data

The default behavior is that every time the new data is received for a particular plot, the old data is deleted/overwritten. Using the --keep-data option will ensure that old data is retained for display. It may be useful to have a 'clear' button on the GUI to clear the screen when necessary.

Logging the Data to the Console

At times, the user may wish to simply log the data.

uint8_t xData[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
uint16_t y1Data[10] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
uint16_t y2Data[10] = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29};

DIS_publish("log:10,u8,u16,u16", xData, y1Data, y2Data);
//            ^ log command

The default behavior for log is to simply display the data to a console as a series of comma- separated values.

Saving Data to CSV File

Sometimes it is most efficient to simply log data directly to a CSV file for later processing. This is particularly true for higher-volume data. For this, we can supply a relative path for the data:

uint8_t xData[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
uint16_t y1Data[10] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
uint16_t y2Data[10] = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29};

DIS_publish("csv:10,u8,u16,u16", xData, y1Data, y2Data);
//            ^ csv command

Summing Up

As you can see, we are basically attempting to turn our micro-controller into a plot-scripter for the PC. I'm sure that getting all of this accomplished will take some real time, but some of the effort is already in-progress within the curve-tracer project. With a nice debugging tool like this, bringing up ADCs and troubleshooting waveforms should become much easier for the embedded engineer. I will start work on this project as the curve-tracer becomes a more mature platform, particularly as it will become a development platform for this effort.

Until next time!



© by Jason R. Jones 2016
My thanks to the Pelican and Python Communities.