-
Notifications
You must be signed in to change notification settings - Fork 27
Tutorial (C API)
This tutorial will guide you on how to exchange data between an embedded board and an host computer using Telemetry
.
- Sending data from the embedded board is called publishing.
- Receiving data from the host computer is done by subscribing. You can subscribe a variable to a given data topic. Everytime new data is received on this topic, the variable will be updated automatically.
In the main.c file, start by initializing the library.
If you are on a supported platform:
void main()
{
init_telemetry();
On non officially supported platforms (see Setup on a new platform)
void main()
{
TM_transport transport;
// Define your transport callback functions
transport.read = myRead;
transport.write = myWrite;
transport.readable = myReadable;
transport.writeable = myWriteable;
//
init_telemetry(&transport);
After initialization, publishing regularly on topic "foo" is done like this
for( ; ; )
{
publish_i32("foo",123);
sleep(0.1); // sleep to avoid overflowing UART
}
If you connect to the device with pytelemetrycli, you can run a few commands to observe the result desktop-side: todo : animated ttty gif
>: ls
foo
>: print foo
123
Overview of the complete script
To update variables on the embedded board remotely, Telemetry lets you subscribe a variable to a topic.
After initalization, attach the variable foo
to the topic myFooVar
(you can give use name you want, but it's recommended to keep names small):
uin32_t foo = 0;
transport.attach_u32_to("myFooVar", &foo);
Then, in your main loop call:
transport.update();
Each time data is received on myFooVar
topic, the attached variable foo
will be updated with this new value.
You can also attach a function to a topic. See this example :
// Fonction you want to attach to a topic
void myBarFunction(void)
{
// Lot of funny things to do here
}
// In the main, attach you fonction 'myBarFunction' to the topic 'myBarFun'
transport.attach("myBarFun", myBarFunction);
That's it ! Below is the documentation for the older and deprecated API, which should be removed in a near future and should not interested you.
At this point, you know how to send the data from the device to the computer. But what about the other way? What about sending data from the computer so you can control the device at distance?
telemetry
can notify you by calling one of your own custom function every time a frame is received.
This is called subscribing. Here is how you do it.
First, declare a function with the following signature. It must return void
, and has two arguments TM_state*
and TM_msg*
. You will understand later the reason behind it and the flexibility it provides.
void myCustomFunction(TM_state * state, TM_msg * msg)
{
// For now, do nothing
}
As said previously, in the main function, myCustomFunction
must be subscribed. This way, each time data is received from the computer, telemetry
will notify you by calling myCustomFunction
.
Let's see a practical example with the following code.
static uint8_t myInc;
void myCustomFunction(TM_state * state, TM_msg * msg)
{
myInc++;
}
void main()
{
myInc = 0;
init_telemetry();
subscribe(myCustomFunction, NULL); // Subscribe our function to be notified. Put NULL to indicate no user data
for( ; ; )
{
update_telemetry(0.0);
}
}
In this code, each time a new frame is received, myCustomFunction
is called during update_telemetry
and the variable myInc
is incremented.
Incrementing a variable for any received frame is fine but not that much interesting. Let's spice things up a little bit.
Let's increment the variable myInc
only if a topic "foobar" was received.
You can get the topic associated with any frame using the second argument TM_msg * msg
of myCustomFunction
.
TM_msg
is a data structure, defined by telemetry
, that contains all the informations about a received frame (its topic, the payload, etc). TM_msg
has the following definition.
struct TM_msg {
TM_type type;
char * topic;
void * buffer;
uint32_t size;
};
Let's modify just our myCustomFunction
, to read the topic contained in TM_msg
and do something with it.
void myCustomFunction(TM_state * state, TM_msg * msg)
{
// If both string match strcmp will return 0
// See http://www.cplusplus.com/reference/cstring/strcmp/
if(strcmp("foobar",msg->topic) == 0)
{
myInc++; // myInc is now only incremented if received topic is foobar !
}
}
// no change in main
This code is pretty much self-explanatory. We are using the standard strcmp
function to compare two strings, to see if received topic matches with foobar
.
Now that you know how to filter by topic, let's filter by received data type.
Telemetry supports all standard portable datatypes and strings.
We will increment myInc
only if:
- the data was send under topic
foobar
(you already know how) - the received data was of type
uint8_t
.
The data type of a frame is also contained in TM_msg
, precisely under msg->type
.
All supported datatypes are enumerated by telemetry
in TM_type
:
enum TM_type {
TM_float32 = 0, // float
TM_uint8 = 1, // unsigned char number
TM_uint16 = 2, // unsigned short number
TM_uint32 = 3, // unsigned int number
TM_int8 = 4, // signed char number
TM_int16 = 5, // signed short number
TM_int32 = 6, // signed int number
TM_string = 7 // string
};
Again, just a couple of modifications are required to myCustomFunction
.
#include "string.h"
static uint8_t myInc;
void myCustomFunction(TM_state * state, TM_msg * msg)
{
if(strcmp("footer",msg->topic) == 0)
{
if(msg->type == TM_uint8) // Filter by datatype
{
myInc++;
}
}
}
// no change in main()
Now comes the interesting part. We will no longer increment a variable ; instead, we are going to write in myInc
the received data.
We will still only do this if:
- the data was send under topic
foobar
- the received data was of type
uint8_t
The TM_msg
data provides direct access to the data, but in a direct, undecoded form. To transform this raw data into something that can be written in a variable, we need to use the emplace
functions. There are different variants of the emplace functions:
src/telemetry/headers/telemetry.h
uint32_t emplace(TM_msg * m, char * buf, size_t bufSize);
uint32_t emplace_u8(TM_msg * m, uint8_t * dst);
uint32_t emplace_u16(TM_msg * m, uint16_t * dst);
uint32_t emplace_u32(TM_msg * m, uint32_t * dst);
uint32_t emplace_i8(TM_msg * m, int8_t * dst);
uint32_t emplace_i16(TM_msg * m, int16_t * dst);
uint32_t emplace_i32(TM_msg * m, int32_t * dst);
uint32_t emplace_f32(TM_msg * m, float * dst);
```
Because our `myInc` variable is of type `uint8_t`, we are going to use `emplace_u8`. `myCustomFunction` will only require a couple of modifications so that `myInc` can be written with the received data.
```c
static uint8_t myInc;
void myCustomFunction(TM_state * state, TM_msg * msg)
{
if(strcmp("foofoo",msg->topic) == 0)
{
if(msg->type == TM_uint8)
{
if(emplace_u8(msg, &myInc)
{
// Success ! myInc now still contains the received data
}
}
}
}
```
### Conclusion and going further
This way of specifying behavior is extremely flexible. You build your application with the protocol, and it doesn't get in the way.
What if, on topic `booyaa` you wanted to start a function, but on topic `PID` you need to register values somewhere ? Using `telemetry`, there is nothing preventing you to do so.
Now, you may have noticed that, to store received values in a variable, a global variable (`myInc`) was used. There is a better way to make data accessible both from `myCustomFunction` and the `main` function. This is covered in the next tutorial called [Advanced usage](https://github.com/Overdrivr/Telemetry/wiki/Advanced-usage).
You may also want to check at this point the [list of examples](https://github.com/Overdrivr/Telemetry/wiki/Projects-using-telemetry).
Back Wiki home
- Fast data visualization with the command line interface (todo)
- Fast prototyping remote program control with python (todo)
- Overview of the library
- Protocol description
- All the good stuff inside Telemetry
- List of supported platforms
- Good practices (Must-read !) in writing
- Frequently Asked Questions todo
- List of official examples
- List of projects using telemetry