mail

So, the graphic above looks a lot like something from the 80's. I wanted to put something up since this page was going to be nearly all text, so there ya go.

In Part 1 we described the 'vision' of the protocol that we wish to use, placing a lot of emphasis on the publish methods.

In Part 2 we described how that vision translates into a byte stream.

One item that we have neglected is actually consuming the data. For data consumption, we are going to use a 'subscriber' function and a callback function.

Subscribing to a Topic

The Simple Approach

We must first 'subscribe' to a topic using the 'subscribe' function. Below is the simplest complete subscription that we can execute:

void mySubscriber(void);    // declare your subscriber function

int main(void){
    subscribe("my topic", &mySubscriber);

    while(1){
        process();  // must be called continually for subscriptions to work
    }
}

void mySubscriber(void){
    static int i = 0;
    i++;
}

Going through the above lines, first we must declare our callback function just as we declare any other function. In main, we subscribe to the topic "my topic" using the subscribe function and, as a parameter, the callback function.

The process function must be called continually as it takes care of calling the subscriber when the topic is received. When the topic "my_topic" is received then mySubscriber is called. In this case, mySubscriber will simply increment a variable. That is all there is to subscribing! In its current form, mySubscriber isn't very helpful. It might be useful for toggling a pin on receipt of a particular topic, but not much more than that. We want the subscriber to consume actual data. For that, we must use the getElements function.

The Slightly More Complex Approach

I like the use the task manager for items that need to be executed repeatedly, such as process(). For this project, I would make a small alteration to the above code:

void mySubscriber(void);    // declare your subscriber function

int main(void){
    TASK_init();

    subscribe("my topic", &mySubscriber);

    TASK_add(&process, 10); // execute 'process()' every 10ms

    TASK_manage();
}

void mySubscriber(void){
    static int i = 0;
    i++;
}

This has the same effect as the previous code block with two exceptions:

  1. I can add other tasks with several different timings. Each of these will appear to execute in parallel so long as each doesn't unduly tax the processor.
  2. Task timing is much more deterministic - if a function needs to execute every 10ms, it simply executes when it is ready to execute.

I generally implement a task manager early in each project so that I can use it to its greatest advantage. In many cases, the task manager can take the place of several timer interrupts without dedicating hardware timers to each or having to create a separate global variable to keep track of different time intervals.

Retrieving Data

A subscriber retrieves data using getElements:

void mySubscriber(void){
    /* declare a place for the data to go */
    uint16_t myData0[10];
    int16_t myData1[10];
    uint16_t dataLength;

    dataLength = getElements(0, myData0); // element(s)0 into myData0[]
    getElements(1, myData1);              // element(s)1 into myData1[]
}

In the above sequence, a two-dimensional dataset with up to 10 elements each was retrieved and saved into myData0 and myData1. Recall from previous posts that the array length is the same for both, which is why dataLength was only retrieved on the first call to getElements.

The arrays MUST be long enough to hold the data as there is no protection from overflowing the data destination array. Additionally, the arrays MUST be of the proper type. If the publisher of the data is sending int32 data and the subscriber is retrieving int16 data, then the data retrieved by the getElements will be garbage.

Notes

There are no program-imposed limitations on what functions can use the getElements function; however, the function returned data is only valid within a subscriber. If getElements is called outside a subscriber, it will only return garbage.

As mentioned before, the data format must exactly match between publisher and subscriber. If the formats do not match, then garbage will be returned!

Future of the Protocol

At this time, the protocol is being developed as part of the curve tracer project. I believe that it is quite useful and worth moving into its own repository. I will create similar functionality using Python in order to complete the communications loop between the curve tracer and the PC. Python will also enable the data visualization.

For source code for the protocol as it exists, visit the github repository.



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