Library implementing the lower layers of the APM binary communications protocol.

Based on work done for the ArduStationM firmware and influenced by Randall Mackay's ArduCopter BinComm code.

This is a work in progress; discussion is welcome.

git-svn-id: https://arducopter.googlecode.com/svn/trunk@318 f9c3cf11-9bcb-44bc-f272-b75c42450872
This commit is contained in:
DrZiplok@gmail.com 2010-08-26 08:12:19 +00:00
parent 884098b74d
commit 71efe28851
6 changed files with 2097 additions and 0 deletions

View File

@ -0,0 +1,206 @@
// -*- Mode: C++; c-basic-offset: 8; indent-tabs-mode: nil -*-
//
// Copyright (c) 2010 Michael Smith. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
/// @file BinComm.cpp
/// @brief Implementation of the ArduPilot Mega binary communications
/// library.
#include "WProgram.h"
#include "APM_BinComm.h"
/// @name decoder state machine phases
//@{
#define DEC_WAIT_P1 0
#define DEC_WAIT_P2 1
#define DEC_WAIT_HEADER 2
#define DEC_WAIT_PACKET 3
#define DEC_WAIT_SUM_A 4
#define DEC_WAIT_SUM_B 5
//@}
/// inter-byte timeout for decode (ms)
#define DEC_MESSAGE_TIMEOUT 100
BinComm::BinComm(const BinComm::BCMessageHandler *handlerTable,
FastSerial *interface) :
_handlerTable(handlerTable),
_interface(interface)
{
};
void
BinComm::_sendMessage(void)
{
uint8_t bytesToSend;
uint8_t sumA, sumB;
const uint8_t *p;
// send the preamble first
_interface->write((uint8_t)MSG_PREAMBLE_1);
_interface->write((uint8_t)MSG_PREAMBLE_2);
// set up to send the payload
bytesToSend = _encodeBuf.header.length + sizeof(_encodeBuf.header);
sumA = sumB = 0;
p = (const uint8_t *)&_encodeBuf;
// send message bytes and compute checksum on the fly
while (bytesToSend--) {
sumA += *p;
sumB += sumA;
_interface->write(*p++);
}
// send the checksum
_interface->write(sumA);
_interface->write(sumB);
}
void
BinComm::update(void)
{
uint8_t count;
// Ensure that we don't spend too long here by only processing
// the bytes that were available when we started.
//
// XXX we might want to further constrain this count
count = _interface->available();
while (count--)
_decode(_interface->read());
}
void
BinComm::_decode(uint8_t inByte)
{
uint8_t tableIndex;
// handle inter-byte timeouts (resync after link loss, etc.)
//
if ((millis() - _lastReceived) > DEC_MESSAGE_TIMEOUT)
_decodePhase = DEC_WAIT_P1;
// run the decode state machine
//
switch (_decodePhase) {
// Preamble detection
//
// Note the fallthrough from P2 to P1 deals with the case where
// we see 0x34, 0x34, 0x44 where the first 0x34 is garbage or
// a SUM_B byte we never looked at.
//
case DEC_WAIT_P2:
if (MSG_PREAMBLE_2 == inByte) {
_decodePhase++;
// prepare for the header
_bytesIn = 0;
_bytesExpected = sizeof(struct msg_packet_header);
// intialise the checksum accumulators
_sumA = _sumB = 0;
break;
}
_decodePhase = DEC_WAIT_P1;
// FALLTHROUGH
case DEC_WAIT_P1:
if (MSG_PREAMBLE_1 == inByte) {
_decodePhase++;
}
break;
// receiving the header
//
case DEC_WAIT_HEADER:
// do checksum accumulation
_sumA += inByte;
_sumB += _sumA;
// store the byte
_decodeBuf.bytes[_bytesIn++] = inByte;
// check for complete header received
if (_bytesIn == _bytesExpected) {
_decodePhase++;
// prepare for the payload
// variable-length data?
_bytesIn = 0;
_bytesExpected = _decodeBuf.header.length;
_messageID = _decodeBuf.header.messageID;
_messageVersion = _decodeBuf.header.messageVersion;
// sanity check to avoid buffer overflow - revert back to waiting
if (_bytesExpected > sizeof(_decodeBuf))
_decodePhase = DEC_WAIT_P1;
}
break;
// receiving payload data
//
case DEC_WAIT_PACKET:
// do checksum accumulation
_sumA += inByte;
_sumB += _sumA;
// store the byte
_decodeBuf.bytes[_bytesIn++] = inByte;
// check for complete payload received
if (_bytesIn == _bytesExpected) {
_decodePhase++;
}
break;
// waiting for the checksum bytes
//
case DEC_WAIT_SUM_A:
if (inByte != _sumA) {
badPacketsReceived++;
_decodePhase = DEC_WAIT_P1;
} else {
_decodePhase++;
}
break;
case DEC_WAIT_SUM_B:
if (inByte == _sumB) {
// if we got this far, we have a packet
packetsReceived++;
// call any handler interested in this message
for (tableIndex = 0; MSG_NULL != _handlerTable[tableIndex].messageID; tableIndex++)
if ((_handlerTable[tableIndex].messageID == _messageID) ||
(_handlerTable[tableIndex].messageID == MSG_ANY))
_handlerTable[tableIndex].handler(_handlerTable[tableIndex].arg, _messageID, _messageVersion, &_decodeBuf);
} else {
badPacketsReceived++;
}
_decodePhase = DEC_WAIT_P1;
break;
}
}

View File

@ -0,0 +1,265 @@
// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: t -*-
//
// Copyright (c) 2010 Michael Smith. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
/// @file BinComm.h
/// @brief Definitions for the ArduPilot Mega binary communications
/// library.
#ifndef APM_BinComm_h
#define APM_BinComm_h
#include "WProgram.h"
#include <string.h>
#include <inttypes.h>
#include <FastSerial.h>
///
/// @class BinComm
/// @brief Class providing protocol en/decoding services for the ArduPilot
/// Mega binary telemetry protocol.
///
class BinComm {
public:
//////////////////////////////////////////////////////////////////////
/// @name Protocol magic numbers
///
/// @note The messageID enum is automatically generated and thus not described here.
///
//@{
/// Variables defined
/// XXX these should probably be handled by the database/MIB?
enum variableID {
MSG_VAR_ROLL_MODE = 0x00,
MSG_VAR_PITCH_MODE = 0x01,
MSG_VAR_THROTTLE_MODE = 0x02,
MSG_VAR_YAW_MODE = 0x03,
MSG_VAR_ELEVON_TRIM_1 = 0x04,
MSG_VAR_ELEVON_TRIM_2 = 0x05,
MSG_VAR_INTEGRATOR_0 = 0x10,
MSG_VAR_INTEGRATOR_1 = 0x11,
MSG_VAR_INTEGRATOR_2 = 0x12,
MSG_VAR_INTEGRATOR_3 = 0x13,
MSG_VAR_INTEGRATOR_4 = 0x14,
MSG_VAR_INTEGRATOR_5 = 0x15,
MSG_VAR_INTEGRATOR_6 = 0x16,
MSG_VAR_INTEGRATOR_7 = 0x17,
MSG_VAR_KFF_0 = 0x1a,
MSG_VAR_KFF_1 = 0x1b,
MSG_VAR_KFF_2 = 0x1c,
MSG_VAR_TARGET_BEARING = 0x20,
MSG_VAR_NAV_BEARING = 0x21,
MSG_VAR_BEARING_ERROR = 0x22,
MSG_VAR_CROSSTRACK_BEARING = 0x23,
MSG_VAR_CROSSTRACK_ERROR = 0x24,
MSG_VAR_ALTITUDE_ERROR = 0x25,
MSG_VAR_WP_RADIUS = 0x26,
MSG_VAR_LOITER_RADIUS = 0x27,
MSG_VAR_WP_MODE = 0x28,
MSG_VAR_LOOP_COMMANDS = 0x29,
MSG_VAR_NAV_GAIN_SCALER = 0x2a,
};
/// PID sets defined
enum PIDSet {
MSG_SERVO_ROLL = 0,
MSG_SERVO_PITCH = 1,
MSG_SERVO_RUDDER = 2,
MSG_SERVO_NAV_ROLL = 3,
MSG_SERVO_NAV_PITCH_ASP = 4,
MSG_SERVO_NAV_PITCH_ALT = 5,
MSG_SERVO_TE_THROTTLE = 6,
MSG_SERVO_ALT_THROTTLE = 7,
MSG_SERVO_ELEVATOR = 8 // Added by Randy
};
//@}
//////////////////////////////////////////////////////////////////////
/// Message reception callout descriptor
///
/// An array of these handlers is passed to the constructor to delegate
/// processing for received messages.
///
struct MessageHandler {
MessageID messageID; ///< messageID for which the handler will be called
void (* handler)(void *arg,
uint8_t messageId,
uint8_t messageVersion,
void *messageData); ///< function to be called
void *arg; ///< argument passed to function
};
//////////////////////////////////////////////////////////////////////
/// Constructor.
///
/// @param handlerTable Array of callout functions to which
/// received messages will be sent. More than
/// one handler for a given messageID may be
/// registered; handlers are called in the order
/// they appear in the table.
///
/// @param interface The FastSerial interface that will be used
/// for telemetry communications.
///
BinComm(const MessageHandler *handlerTable,
FastSerial *interface);
//////////////////////////////////////////////////////////////////////
/// @name Decoder interface
//@{
/// Consume bytes from the interface and feed them to the decoder.
///
/// If a packet is completed, then any callbacks associated
/// with the packet's messageID will be called.
///
/// If no bytes are passed to the decoder for a period determined
/// by DEC_MESSAGE_TIMEOUT, the decode state machine will reset
/// before processing the next byte. This can help re-synchronise
/// after a link loss or in-flight failure.
///
void update(void);
uint32_t messagesReceived; ///< statistics
uint32_t badMessagesReceived; ///< statistics
//@}
//////////////////////////////////////////////////////////////////////
/// @name Encoder interface
///
/// Messages are normally encoded and sent using the
/// send_msg_* functions defined in protocol/protocol.h.
/// For each message type MSG_* there is a corresponding send_msg_*
/// function which will construct and transmit the message.
///
//@{
uint32_t messagesSent; ///< statistics
//@}
private:
/// Serial port we send/receive using.
FastSerial *_interface;
/// Various magic numbers
enum MagicNumbers {
MSG_PREAMBLE_1 = 0x34,
MSG_PREAMBLE_2 = 0x44,
MSG_VERSION_1 = 1,
MSG_VARIABLE_LENGTH = 0xff
};
//////////////////////////////////////////////////////////////////////
/// @name Decoder
//@{
uint8_t _decodePhase; ///< decoder state machine phase
uint8_t _bytesIn; ///< bytes received in the current phase
uint8_t _bytesExpected; ///< bytes expected in the current phase
uint8_t _sumA; ///< sum of incoming bytes
uint8_t _sumB; ///< sum of _sumA values
uint8_t _messageID; ///< messageID from the packet being received
uint8_t _messageVersion;///< messageVersion from the packet being received
unsigned long _lastReceived; ///< timestamp of last byte reception
/// Incoming header/packet buffer
/// XXX we could make this smaller
union {
uint8_t bytes[0];
msg_packet_header header;
uint8_t payload[256];
} _decodeBuf;
const BCMessageHandler *_handlerTable; ///< callout table
/// Decoder state machine
///
/// @param inByte The byte to process.
///
void _decode(uint8_t inByte);
//@}
//////////////////////////////////////////////////////////////////////
/// @name Encoder
//@{
/// Send the packet in the encode buffer
///
void _sendMessage(void);
/// Outgoing header/packet buffer
/// XXX we could make this smaller
struct {
msg_packet_header header;
uint8_t payload[256 - sizeof(msg_packet_header)];
} _encodeBuf;
//////////////////////////////////////////////////////////////////////
/// @name Message pack/unpack utility functions
///
//@{
inline void _pack(uint8_t *&ptr, const uint8_t x) { (uint8_t *)ptr = x; ptr += sizeof(x); };
inline void _pack(uint8_t *&ptr, const uint16 x) { (uint16_t *)ptr = x; ptr += sizeof(x); };
inline void _pack(uint8_t *&ptr, const int16 x) { (int16_t *)ptr = x; ptr += sizeof(x); };
inline void _pack(uint8_t *&ptr, const uint32_t x) { (uint32_t *)ptr = x; ptr += sizeof(x); };
inline void _pack(uint8_t *&ptr, const int32_t x) { (int32_t *)ptr = x; ptr += sizeof(x); };
inline void _pack(uint8_t *&ptr, const char *msg, uint8_t size) { strlcpy(ptr, msg, size); ptr += size; };
inline void _pack(uint8_t *&ptr, const uint16_t *values, uint8_t count) { memcpy(ptr, values, count * 2); ptr += count * 2; };
inline void _unpack(uint8_t *&ptr, uint8_t &x) { x = *(uint8_t *)ptr; ptr += sizeof(x); };
inline void _unpack(uint8_t *&ptr, uint16_t &x) { x = *(uint16_t *)ptr; ptr += sizeof(x); };
inline void _unpack(uint8_t *&ptr, int16_t &x) { x = *(int16_t *)ptr; ptr += sizeof(x); };
inline void _unpack(uint8_t *&ptr, uint32_t &x) { x = *(uint32_t *)ptr; ptr += sizeof(x); };
inline void _unpack(uint8_t *&ptr, int32_t &x) { x = *(int32_t *)ptr; ptr += sizeof(x); };
inline void _unpack(uint8_t *&ptr, char *msg, uint8_t size) { strlcpy(msg, ptr, size); ptr += size; };
inline void _unpack(uint8_t *&ptr, uint16_t *values, uint8_t count) { memcpy(values, ptr, count * 2); ptr += count * 2; };
//@}
public:
//////////////////////////////////////////////////////////////////////
/// @name Protocol definition
///
/// The protocol definition, including structures describing messages,
/// messageID values and helper functions for packing messages are
/// automatically generated.
//@{
#include "protocol/protocol.h"
//@}
};
#endif // BinComm_h

View File

@ -0,0 +1,6 @@
APM_BinComm KEYWORD1
update KEYWORD2
messagesReceived KEYWORD2
badMessagesReceived KEYWORD2
messagesSent KEYWORD2

View File

@ -0,0 +1,215 @@
#
# Message definitions for the ArduPilot Mega binary communications protocol.
#
# Process this file using:
#
# awk -f protogen.awk protocol.def > protocol.h
#
# Messages are declared with
#
# message <MESSAGE_ID> <MESSAGE_NAME>
#
# <MESSAGE_NAME> is a valid member of BinComm::MessageID.
# <MESSAGE_ID> is the message ID byte
#
# Following a message declaration the fields of the message are
# defined in the format:
#
# <TYPE> <NAME> [<COUNT>]
#
# <TYPE> is a C type corresponding to the field. The type must be a single
# word, either an integer from <inttypes.h> or "char".
# <NAME> is the name of the field; it should be unique within the message
# <COUNT> is an optional array count for fields that are arrays
# (note that currently there is only _pack support for character arrays)
#
#
# Acknowledge message
#
message 0x00 MSG_ACKNOWLEDGE
uint8_t msgID
uint16_t msgSum
#
# System heartbeat
#
message 0x01 MSG_HEARTBEAT
uint8_t flightMode
uint16_t timeStamp
uint16_t batteryVoltage
uint16_t commandIndex
#
# Attitude report
#
message 0x02 MSG_ATTITUDE
int16_t roll
int16_t pitch
int16_t yaw
#
# Location report
#
message 0x03 MSG_LOCATION
int32_t latitude
int32_t longitude
int16_t altitude
int16_t groundSpeed
int16_t groundCourse
uint16_t timeOfWeek
#
# Optional pressure-based location report
#
message 0x04 MSG_PRESSURE
uint16_t pressureAltitude
uint16_t airSpeed
#
# Text status message
#
message 0x05 MSG_STATUS_TEXT
uint8_t severity
char text 50
#
# Algorithm performance report
#
message 0x06 MSG_PERF_REPORT
uint32_t interval
uint16_t mainLoopCycles
uint8_t mainLoopTime
uint8_t gyroSaturationCount
uint8_t adcConstraintCount
uint16_t imuHealth
uint16_t gcsMessageCount
#
# System version messages
#
message 0x07 MSG_VERSION_REQUEST
uint8_t systemType
uint8_t systemID
message 0x08 MSG_VERSION
uint8_t systemType
uint8_t systemID
uint8_t firmwareVersion 3
#
# Flight command operations
#
message 0x20 MSG_COMMAND_REQUEST
uint16_t UNSPECIFIED
message 0x21 MSG_COMMAND_UPLOAD
uint8_t action
uint16_t itemNumber
int listLength
uint8_t commandID
uint8_t p1
uint16_t p2
uint32_t p3
uint32_t p4
message 0x22 MSG_COMMAND_LIST
int itemNumber
int listLength
uint8_t commandID
uint8_t p1
uint16_t p2
uint32_t p3
uint32_t p4
message 0x23 MSG_COMMAND_MODE_CHANGE
uint16_t UNSPECIFIED
#
# Parameter operations
#
message 0x30 MSG_VALUE_REQUEST
uint8_t valueID
uint8_t broadcast
message 0x31 MSG_VALUE_SET
uint8_t valueID
uint32_t value
message 0x32 MSG_VALUE
uint8_t valueID
uint32_t value
#
# PID adjustments
#
message 0x40 MSG_PID_REQUEST
uint8_t pidSet
message 0x41 MSG_PID_SET
uint8_t pidSet
int32_t p
int32_t i
int32_t d
int16_t integratorMax
message 0x42 MSG_PID
uint8_t pidSet
int32_t p
int32_t i
int32_t d
int16_t integratorMax
#
# Radio trim settings
#
message 0x50 MSG_TRIM_STARTUP
uint16_t value 8
message 0x51 MSG_TRIM_MIN
uint16_t value 8
message 0x52 MSG_TRIM_MAX
uint16_t value 8
#
# Direct sensor access
#
message 0x60 MSG_SENSOR
uint16_t UNSPECIFIED
#
# Simulation-related messages
#
message 0x70 MSG_SIM
uint16_t UNSPECIFIED
#
# Direct I/O pin control
#
message 0x80 MSG_PIN_REQUEST
uint16_t UNSPECIFIED
message 0x81 MSG_PIN_SET
uint16_t UNSPECIFIED
#
# Dataflash operations
#
message 0x90 MSG_DATAFLASH_REQUEST
uint16_t UNSPECIFIED
message 0x91 MSG_DATAFLASH_SET
uint16_t UNSPECIFIED
#
# EEPROM operations
#
message 0xa0 MSG_EEPROM_REQUEST
uint16_t UNSPECIFIED
message 0xa1 MSG_EEPROM_SET
uint16_t UNSPECIFIED

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,173 @@
#
# Process the protocol specification and emit functions to pack and unpack buffers.
#
# See protocol.def for a description of the definition format.
#
BEGIN {
printf("//\n// THIS FILE WAS AUTOMATICALLY GENERATED - DO NOT EDIT\n//\n")
printf("#pragma pack(1)\n");
currentMessage = ""
}
END {
# finalise the last message definition
EMIT_MESSAGE()
#
# emit the messageID enum
#
# XXX it would be elegant to sort the array here, but not
# everyone has GNU awk.
#
printf("\n//////////////////////////////////////////////////////////////////////\n")
printf("/// Message ID values\n")
printf("enum messageID {\n")
for (opcode in opcodes) {
printf("\t%s = 0x%x,\n", opcodes[opcode], opcode)
}
printf("\tMSG_ANY = 0xfe,\n")
printf("\tMSG_NULL = 0xff\n")
printf("};\n")
printf("#pragma pack(pop)\n")
}
#
# Emit definitions for one message
#
function EMIT_MESSAGE(payloadSize)
{
if (currentMessage != "") {
printf("\n//////////////////////////////////////////////////////////////////////\n")
printf("/// @name %s \n//@{\n\n", currentMessage)
#
# emit a structure defining the message payload
#
printf("/// Structure describing the payload section of the %s message\n", currentMessage)
printf("struct %s {\n", tolower(currentMessage))
for (i = 0; i < fieldCount; i++) {
printf("\t%s %s", types[i], names[i])
if (counts[i])
printf("[%s]", counts[i])
printf(";\n")
}
printf("};\n\n")
#
# emit a routine to pack the message payload from a set of variables and send it
#
printf("/// Send a %s message\n", currentMessage)
printf("inline void\nBinComm::send_%s(\n", tolower(currentMessage))
for (i = 0; i < fieldCount; i++) {
if (counts[i]) {
printf("\tconst %s (&%s)[%d]", types[i], names[i], counts[i])
} else {
printf("\tconst %s %s", types[i], names[i])
}
if (i < (fieldCount -1))
printf(",\n");
}
printf(")\n{\n")
printf("\tuint8_t *p = &_encodeBuf.payload;\n")
payloadSize = 0;
for (i = 0; i < fieldCount; i++) {
if (counts[i]) {
printf("\t_pack(p, %s, %s);\n", names[i], counts[i])
payloadSize += sizes[i] * counts[i]
} else {
printf("\t_pack(p, %s);\n", names[i])
payloadSize += sizes[i]
}
}
printf("\t_encodeBuf.header.length = %s;\n", payloadSize)
printf("\t_encodeBuf.header.messageID = %s;\n", currentMessage)
printf("\t_encodeBuf.header.messageVersion = MSG_VERSION_1;\n")
printf("\t_sendMessage();\n")
printf("};\n\n")
#
# emit a routine to unpack the current message into a set of variables
#
printf("/// Unpack a %s message\n", currentMessage)
printf("inline void\nBinComm::unpack_%s(\n", tolower(currentMessage))
for (i = 0; i < fieldCount; i++) {
if (counts[i]) {
printf("\t%s (&%s)[%d]", types[i], names[i], counts[i])
} else {
printf("\t%s &%s", types[i], names[i])
}
if (i < (fieldCount -1))
printf(",\n");
}
printf(")\n{\n")
printf("\tuint8_t *p = &_decodeBuf.payload;\n")
for (i = 0; i < fieldCount; i++) {
if (counts[i]) {
printf("\t_unpack(p, %s, %s);\n", names[i], counts[i])
payloadSize += sizes[i] * counts[i]
} else {
printf("\t_unpack(p, %s);\n", names[i])
payloadSize += sizes[i]
}
}
printf("};\n")
# close the Doxygen group
printf("//@}\n")
}
}
# skip lines containing comments
$1=="#" {
next
}
#
# process a new message declaration
#
$1=="message" {
# emit any previous message
EMIT_MESSAGE()
# save the current opcode and message name
currentOpcode = $2
currentMessage = $3
opcodes[$2] = $3
# set up for the coming fields
fieldCount = 0
delete types
delete names
delete sizes
delete counts
next
}
#
# process a field inside a message definition
#
NF >= 2 {
# save the field definition
types[fieldCount] = $1
names[fieldCount] = $2
# guess the field size, note that we only support <inttypes.h> and "char"
sizes[fieldCount] = 1
if ($1 ~ ".*16.*")
sizes[fieldCount] = 2
if ($1 ~ ".*32.*")
sizes[fieldCount] = 4
# if an array size was supplied, save it
if (NF >= 3) {
counts[fieldCount] = $3
}
fieldCount++
}