Development / Protocol |
/NGCTRL2 |
Contents
The NGCTRL2 UAV Communication Protocol
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
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.
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:
NGCTRL2 Packet
COBS Encoded
Framing
Data Set 1
Data Set 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); }
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); }
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);
Make sure to link the resulting object file with the NG client library libng.so.
