Historical project documentation. Content is maintained in GitLab.

NGCTRL2

Development / Protocol/NGCTRL2

The NGCTRL2 UAV Communication Protocol

{i} This is work in progress! Please post in the forum when you find errors or mistakes!

General overview

The NGCTRL2 protocol is a simple asynchronous serial protocol used between NGOS and it’s client implementation in libng (available in the source code release). Essentially it’s a packet driven, eg. each message has a message type and messages may arrive asynchronously and out of order.

The client library libng implements everything needed to open a connection to NGOS, request data packets with arbitrary intervals and default parsers for all available packet types. A client application opens a NGCTRL2 connection, requests packet types and intervals and specifies the parser function to process received packets.

There are several parser functions implemented in libng allowing you to process the received data:

  • The printf parser - Outputs receiced data packets in human readable form to the console
  • The fprintf parser - Outputs receiced data packets in human readable form to a file descriptor
  • The struct parser - Outputs received data packets into a pre-defined structure allowing easy access

{i} It’s also easily possible to implement your own parser function and use that.

Usage of the NGCTRL2 protocol in the NGOS shell

The NGOS shell has shell commands which allow you to inspect and manipulate the NGCTRL2 packet dumping.

Make sure to enable the API mode, so that the special API shell commands are available to you:

# mode api

Shell mode: api

You can inspect the available packet types with the command api ngctrl-v2-list-types.

# api ngctrl-v2-list-types

NGCTRL V2 - Packet Dump valid Packet Types:

  0      Gyroscopes
  1      Accelerometer
  2      Attitude
  3      Motor 1-4
  4      Motor 5-8
  5      Motor 9-12
  6      Motor 13-16
  7      Height/Baro
  8      GPS Position
  9      Guessed Position
  10     Debug
  11     Debug Long
  12     Battery
  13     Position Hold
  14     Flight State

You can also inspect the NGCTRL2 packet dumping tables. In this case we are dumping 5 packet types in intervals of 500ms on UART0:

# api ngctrl-v2-list

NGCTRL V2 - Packet Dump Table:

  Terminal      Active

  uart0         NGCTRL V2
  uart1         No
  usb           No

  Terminal      Id      Rate    Cnt     Description

  uart0         0       500     318     Gyroscopes
  uart0         1       500     361     Accelerometer
  uart0         2       500     402     Attitude
  uart0         3       500     444     Motor 1-4
  uart0         4       0       0       Motor 5-8
  uart0         5       0       0       Motor 9-12
  uart0         6       0       0       Motor 13-16
  uart0         7       500     484     Height/Baro
  uart0         8       0       0       GPS Position
  uart0         9       0       0       Guessed Position
  uart0         10      0       0       Debug
  uart0         11      0       0       Debug Long
  uart0         12      0       0       Battery
  uart0         13      0       0       Position Hold
  uart0         14      0       0       Flightstate

It’s also possible to request packet dumping with a shell command. To start dumping packet type 4 (motors 5-8) in an interval of 500ms use the following commands:

# api ngctrl-v2-dump uart0 on

UART0: Started dump...

# api ngctrl-v2-request uart0 4 500

Activating packet type 'Motor 5-8' with intervall 500

Now, NGCTRL2 is active on UART0 and sends packet type 4 all 500ms. To switch it off again, use the following shell commands:

# api ngctrl-v2-request uart0 4 0

Activating packet type 'Motor 5-8' with intervall 0

# api ngctrl-v2-dump uart0 off

UART0: Dump stopped.

The command line tool NGCTRL2

ngctrl2, the command line tool, uses the parsers described on this page to implement simple command and data query access to NGOS.

It’s arguments are quite simple:

ngctrl2 - 0.63

Usage:
        --verbose (-v)
        --help (-h)
        --device (-d)
        --baud (-b)
        --flowctrl (-F)
        --request (-r)
        --log (-l)
        --log-to-file (-L)
        --show-version (-V)
        --execute (-e)

In essence you tell it what communication port to use (–device, –baud, –flowctrl), then you tell it what to request (–request) and choose if you want to print it to your screen (–log) or to a file (–log-to-file).

A sample usage where we request packets of type 2 (attitude) with a 500ms interval:

$ ./ngctrl2 -l -r 2:500

ngctrl - 0.63

Requesting continuous data of packet type 02 in intervall 500.

Dumping NG data ...
... press any key to stop!

Current NG log data:

        ATTITUDE                +42         -19         +19
        ATTITUDE                +40         -19         +19
             ...

As you can see the argument to –request is <packet-type>:<intervall>.

Besides requesting certain packet types, ngctrl2 will also display out-of-order packets received. An out-of-order packet is a packet, which you did not request but which gets sent to you automatically when an event happens.

A sample of an out-of-order packet can be seen here:

$ ./ngctrl2 -l -r 2:1000

ngctrl - 0.63

Requesting continuous data of packet type 02 in intervall 1000.

Dumping NG data ...
... press any key to stop!

Current NG log data:

        ATTITUDE                +42         -19         +19
        ATTITUDE                +40         -19         +19
        ATTITUDE                +45         -19         +19
     FLIGHTSTATE             landed
        ATTITUDE                +31          +0          +0
        ATTITUDE                +36          +0          +0
        ATTITUDE                +41          +0          +0
     FLIGHTSTATE             spinup
        ATTITUDE                +26          +0          +0
        ATTITUDE                +33          +0          +0
        ATTITUDE                +37          +0          +0
     FLIGHTSTATE             flying
        ATTITUDE                +38          +0          +0
        ATTITUDE                +40          +0          +0
             ...

As you can see above, we receive a packet of type DUMP_PKG_FLIGHTSTATE, whenever NGOS changes it’s flight state.

Out-of-order packets can be ignored, or they can be handled by the client application.

Simple NGCTRL2 communications

Available NGCTRL2 packet types

The following are the available packet types in the NGCTRL2 protocol (defined in ngos/src/fc/ctrl/dump.h):

typedef enum
{
  DUMP_PKG_GYROS = 0,           // Gyros
  DUMP_PKG_ACC = 1,             // Acc
  DUMP_PKG_ATTITUDE = 2,        // Attitude
  DUMP_PKG_MOTOR_Q1 = 3,        // Motor 1-4
  DUMP_PKG_MOTOR_Q2 = 4,        // Motor 5-8
  DUMP_PKG_MOTOR_Q3 = 5,        // Motor 9-12
  DUMP_PKG_MOTOR_Q4 = 6,        // Motor 13-16
  DUMP_PKG_HEIGHT = 7,          // Height
  DUMP_PKG_HOME = 8,            // Home position
  DUMP_PKG_GPS = 9,             // GPS position
  DUMP_PKG_POS = 10,            // Guessed position
  DUMP_PKG_TARGET = 11,         // Target position
  DUMP_PKG_COMPASS = 12,        // Compass
  DUMP_PKG_NAVIGATION = 13,     // Navigation data
  DUMP_PKG_DEBUG = 14,          // Debug
  DUMP_PKG_DEBUGLONG = 15,      // Debug
  DUMP_PKG_BATTERY = 16,        // Battery Voltage
  DUMP_PKG_FLIGHTSTATE = 17,    // Flight State
  DUMP_PKG_INT_SCALING = 18,    // Integral Scaling
  DUMP_PKG_WAYPOINTS = 19,      // Waypoints
  DUMP_PKG_NEXT_WAYPOINT = 20,  // Next Waypoint
} dump_pkg_type_t;

These are the packet types you may request together with an interval specifying how often you want to receive that data packet. Please note that packets may arrive out of order.

NGCTRL2 functions in library libng

The following functions are implemented in libng and are defined in ng.h:

  • **int ng_open_uart(char dev, int baud, int flowctrl, bool bluetooth, ng_handle_t ng)

Open serial connection to NGOS using the given communication parameters and the given ng_handle_t which will be used in further libng calls.

  • *int ng_close_uart(ng_handle_t ng)

Close the serial connection using the given ng_handle_t.

  • *int ng_ngctrl2_initialize(ng_handle_t ng)

Initialize the NGCTRL2 protocol. The function needs a ng_handle_t as argument which can be aquired by calling ng_open_uart() earlier.

  • *int ng_ngctrl2_request(ng_handle_t ng, int type, int interval)

Request to receive packet type type in interval interval.

  • *int ng_ngctrl2_shutdown(ng_handle_t ng)

Shutdown the NGCTRL2 protocol on the given ng_handle_t. Don’t forget to close the connection to NGOS with ng_close_uart(ng) afterwards.

  • *int ng_ngctrl2_packet_parser_printf(unsigned char pkg)

The printf parser implementation available for use with ng_ngctrl2_packet_poll().

  • *int ng_ngctrl2_packet_parser_fprintf(unsigned char pkg)

The fprintf parser implementation available for use with ng_ngctrl2_packet_poll().

  • *int ng_ngctrl2_packet_parser_struct_storage(unsigned char pkg)

The struct storage parser implementation available for use with ng_ngctrl2_packet_poll().

  • *int ng_ngctrl2_packet_poll(ng_handle_t ng, ng_ngctrl2_packet_parser_t parser)

The dispatcher function. It registers a NGCTRL2 packet parser function which will be called for every and all NGCTRL2 packets received for a given ng_handler_t. Make sure to register only one parser per ng_handler_t. The library libng implements several parser functions for easy usage but the above function also allows you to specify your own parser function.

The NGCTRL2 protocol packet definition

NGCTRL2 dispatches NGCTRL2 packets between NGOS and PC (or better libng). Each packet may contain an arbitrary count of data-sets. All data-sets sent out within one cycle will be queued up inside a single NGCTRL2 packet and sent out together as one packet.

These packets are COBS (Constant Overhead Byte Stuffing) encoded, which makes sure that no zero byte will show up inside the packet. The NGCTRL2 packet is framed with a zero byte. Each packet contains a simple one byte checksum at the end of the packet.

{i} The COBS algorithm has a constant worst case overhead of 1 byte per 255 bytes - whereas normal byte stuffing algorithms have a non-constant worst case of up to 200% of packet size - making sure our packets will not grow madly with deformed packet data content.

A NGCTRL2 packet consists of a arbitrary length series of data-sets, a checksum and a framing bytes. Each data-set consists of a type byte, specifying the data-set data content and size, and the payload with the implicit size given by the data-set type. These arbitrary count of data-sets are followed by a one byte XOR checksum. The whole packet gets COBS encoded and framed with a zero byte.

Concluding one can specify a NGCTRL2 packet like this:

||<tablewidth=“70%” colspan=“7”>NGCTRL2 Packet|| ||<colspan=“6”>COBS Encoded||Framing|| ||<colspan=“2”>Data Set 1||<colspan=“2”>Data Set 2||…||<colspan=“2”> || ||Type 1||Data 1||Type 2||Data 2||…||checksum||0||

There are a lot of different data-set types available in NGOS.

Here is a non conclusive list of the available data-set types:

  • DUMP_PKG_GYROS
  • DUMP_PKG_ACC
  • DUMP_PKG_ATTITUDE
  • DUMP_PKG_MOTOR_Q1
  • DUMP_PKG_MOTOR_Q2
  • DUMP_PKG_MOTOR_Q3
  • DUMP_PKG_MOTOR_Q4
  • DUMP_PKG_HEIGHT
  • DUMP_PKG_HOME
  • DUMP_PKG_GPS
  • DUMP_PKG_POS
  • DUMP_PKG_TARGET
  • DUMP_PKG_COMPASS
  • DUMP_PKG_NAVIGATION
  • DUMP_PKG_DEBUG
  • DUMP_PKG_DEBUGLONG
  • DUMP_PKG_BATTERY
  • DUMP_PKG_FLIGHTSTATE
  • DUMP_PKG_INT_SCALING
  • DUMP_PKG_WAYPOINTS
  • DUMP_PKG_NEXT_WAYPOINT

Each of the above data-set types can be requested by a client application, either continiously with a arbitrary interval time or just once, for a snapshot of the current state.

The NGCTRL2 packet parsers

The NGCTRL2 printf parser

Requesting data from your NG and output it using the printf parser is a simple thing consisting of a handful lines of code.

#include <ng.h>

int main()
{
  ng_handle_t ngs;                 /* ng handle storage */
  ng_handle_t *ng = &ngs;          /* pointer to ng handle storage */

  int type = DUMP_PKG_GYROS;
  int interval = 100; // [ms]

  char *device = "/dev/ttyS0";     /* open arguments */
  int baud = BAUD115200;
  int flowctrl = 0;

  // open connection to NGOS
  if ((int ret = ng_open_uart(device, baudrate, flowctrl, ng)) < 0)
  {
    // handle error
  }

  // initialize NGCTRL2 protocol
  ng_ngctrl2_initialize(ng);

  // request data packet
  ng_ngctrl2_request(ng, type, interval);

  // while user did not press a key
  while (! ng_char_available(stdin))
  {
    int pkg_type[20];
    int pkg_count;

    // dispatch received packets to printf parser
    pkg_count = ng_ngctrl2_packet_poll(ng, ng_ngctrl2_packet_parser_printf, pkg_type);
  }

  // shutdown NGCTRL2 protocol
  ng_ngctrl2_shutdown(ng);

  // close connection to NGOS
  ng_close_uart(ng);
}

{i} Make sure to link the resulting object file with the NG client library libng.so.

The NGCTRL2 fprintf parser

Requesting data from your NG and output it using the fprintf parser is very similar to using the printf parser. The only difference is that you have to open a file before hand using a special file descriptor exported by libng.

#include <ng.h>

int main()
{
  ng_handle_t ngs;                 /* ng handle storage */
  ng_handle_t *ng = &ngs;          /* pointer to ng handle storage */

  int type = DUMP_PKG_GYROS;
  int interval = 100; // [ms]

  // open connection to NGOS
  if ((int ret = ng_open_uart(device, baudrate, flowctrl, ng)) < 0)
  {
    // handle error
  }

  // open file to dump into and store file descriptor in libng's parser variable
  if ((ng_ngctrl2_packet_parser_fprintf_fd = fopen(file, "w")) == NULL)
  {
    // handle error
  }

  // initialize NGCTRL2 protocol
  ng_ngctrl2_initialize(ng);

  // request data packet
  ng_ngctrl2_request(ng, type, intervall);

  // while user did not press a key
  while (! ng_char_available(stdin))
  {
    int pkg_type[20];
    int pkg_count;

    // dispatch received packets to printf parser
    pkg_count = ng_ngctrl2_packet_poll(ng, ng_ngctrl2_packet_parser_fprintf, pkg_type);
  }

  // shutdown NGCTRL2 protocol
  ng_ngctrl2_shutdown(ng);

  // close file
  fclose(ng_ngctrl2_packet_parser_fprintf_fd);

  // close connection to NGOS
  ng_close_uart(ng);
}

{i} Make sure to link the resulting object file with the NG client library libng.so.

Your own NGCTRL2 packet parser

A NGCTRL2 packet parser is nothing more than a function with defined arguments and return value.

int myown_ngctrl2_packet_parser(unsigned char *pkg)
{
  // fetch packet type
  dump_pkg_type_t type = (dump_pkg_type_t)*pkg;

  // fetch packet data
  ctrl_ngctrl_dump_v2_t *ngdump = (ctrl_ngctrl_dump_v2_t *) (pkg + 1);

  // return value should be number of bytes parsed or -1 for unknown packet
  int ret = -1;

  switch (type)
  {
    case DUMP_PKG_GYROS:
    {
      int wN = ngdump->d[0].i_l16; // wN.v16 = Gyro Nick
      int wR = ngdump->d[0].i_h16; // wR.v16 = Gyro Roll
      int wY = ngdump->d[1].i_l16; // wY.v16 = Gyro Yaw
      ret = 6;

      // process the gyro values
      break;
    }

    // ... handle more packet types
  }

  return ret; // return number of bytes parsed
}

Having defined a parser function like the one above, you can now specify it when you setup the packet dispatcher:

  // dispatch received packets to parser
  pkg_count = ng_ngctrl2_packet_poll(ng, myown_ngctrl2_packet_parser, pkg_type);

{i} Make sure to link the resulting object file with the NG client library libng.so.