// -*- 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 #include #include "WProgram.h" /// /// @class BinComm /// @brief Class providing protocol en/decoding services for the ArduPilot /// Mega binary telemetry protocol. /// /// The protocol definition, including structures describing /// messages, MessageID values and helper functions for sending /// and unpacking messages are automatically generated. /// /// See protocol/protocol.def for a description of the message /// definitions, and protocol/protocol.h for the generated /// definitions. /// /// Protocol messages are sent using the send_* functions defined in /// protocol/protocol.h, and handled on reception by functions defined /// in the handlerTable array passed to the constructor. /// class BinComm { public: struct MessageHandler; ////////////////////////////////////////////////////////////////////// /// 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. A single handler /// may be registered for more than one message, /// as the message ID is passed to the handler /// when it is received. /// /// @param interface The stream that will be used /// for telemetry communications. /// /// @param rxBuffSize Size of receive buffer allocated by interface. /// This is used to warn for buffer overflow. /// /// BinComm(const MessageHandler *handlerTable, Stream *interface = NULL); /// /// Optional initialiser. /// /// If the interface stream isn't known at construction time, it /// can be set here instead. /// /// @param interface The stream that will be used for telemetry /// communications. /// void init(Stream *interface); private: /// OTA message header struct MessageHeader { uint8_t length; uint8_t messageID; uint8_t messageVersion; }; /// Incoming header/packet buffer /// XXX we could make this smaller union { uint8_t bytes[0]; MessageHeader header; uint8_t payload[256]; } _decodeBuf; ////////////////////////////////////////////////////////////////////// /// @name Message pack/unpack utility functions /// //@{ /// Emit any scalar type. /// /// @param x Value to emit. /// template inline void _emit(const T x) { _send(&x, sizeof(T)); } /// Emit an array of any scalar type. /// /// @param x Array to emit. /// @param count Array size. /// template inline void _emit(const T *values, uint8_t count) { _send(values, count * sizeof(T)); } /// Emit a fixed-size string, from a NUL-terminated buffer. /// /// The string is NUL-padded if the buffer is larger than the string. /// /// @param msg The NUL-terminated string to emit. /// @param size The maximum length of the string to emit. /// inline void _emit(const char *msg, uint8_t size) { while (size--) { char c = *msg; _emit(c); if (0 != c) msg++; } } /// Unpack any scalar type. /// /// @param buf Buffer pointer. /// @param x Unpacked result. /// template inline void _unpack(uint8_t *&ptr, T &x) { x = *(T *)ptr; ptr += sizeof(T); } /// Unpack an array of any scalar type. /// /// @param buf Buffer pointer. /// @param values Array to receive the unpacked values. /// template inline void _unpack(uint8_t *&ptr, T *values, uint8_t count) { memcpy(values, ptr, count * sizeof(T)); ptr += count * sizeof(T); } /// Unpack a string from a fixed-size buffer. /// /// @param ptr Buffer pointer. /// @param msg Pointer to the result buffer. /// @param size The size of the buffer. /// inline void _unpack(uint8_t *&ptr, char *msg, uint8_t size) { strncpy(msg, (char *)ptr, size); msg[size-1] = '\0'; ptr += size; } //@} public: ////////////////////////////////////////////////////////////////////// /// @name Protocol definition /// //@{ #include "protocol/protocol.h" //@} ////////////////////////////////////////////////////////////////////// /// @name Protocol magic numbers /// /// @note The MessageID enum is automatically generated and thus not described here. /// //@{ /// Message serverities enum severities { SEVERITY_LOW = 1, SEVERITY_MEDIUM = 2, SEVERITY_HIGH = 3, SEVERITY_CRITICAL = 4, }; /// 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 }; ////////////////////////////////////////////////////////////////////// /// @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: const MessageHandler *_handlerTable; ///< callout table Stream *_interface; ///< Serial port we send/receive using. /// Various magic numbers enum MagicNumbers { MSG_PREAMBLE_1 = 0x34, MSG_PREAMBLE_2 = 0x44, MSG_VERSION_1 = 1, MSG_VARIABLE_LENGTH = 0xff }; ////////////////////////////////////////////////////////////////////// /// @name Decoder state //@{ 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 _decoderSumA; ///< sum of incoming bytes uint8_t _decoderSumB; ///< 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 //@} /// Decoder state machine. /// /// @param inByte The byte to process. /// void _decode(uint8_t inByte); ////////////////////////////////////////////////////////////////////// /// @name Encoder state //@{ uint8_t _encoderSumA; ///< sum of outgoing bytes uint8_t _encoderSumB; ///< sum of _sumA values //@} /// Start transmitting a message. /// /// @param messageId The ID of the message to be sent /// @param messageLength The protocol-defined length of the message in bytes /// @param messageVersion The message version (optional) /// void _startMessage(uint8_t messageId, uint8_t messageLength, uint8_t messageVersion = 1); /// Send bytes as part of a message. /// /// @param bytes Pointer to the byte(s) to send. /// @param count Count of bytes to send. void _send(const void *bytes, uint8_t count); /// Finalise message transmission. /// void _endMessage(void); }; #endif // BinComm_h