Skip to content
Rémi Bèges edited this page Dec 4, 2016 · 23 revisions

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.

Publishing

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

Subscribing

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.

Subscribing (old API)

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.

Filtering by topic

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.

Filtering by data type

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()

Writing the received data into the variable

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).

Setup

Get started for embedded platforms

Get started for remote debug and remote control

  • Fast data visualization with the command line interface (todo)
  • Fast prototyping remote program control with python (todo)

General knowledge

Troubleshooting

  • Frequently Asked Questions todo

Examples and projects

Clone this wiki locally