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.
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.
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:
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.
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.
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!
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.