// -*- 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.cpp /// @brief Implementation of the ArduPilot Mega binary communications /// library. #include "APM_BinComm.h" #include "WProgram.h" /// @name decoder state machine phases //@{ #define DEC_WAIT_P1 0 #define DEC_WAIT_P2 1 #define DEC_WAIT_HEADER 2 #define DEC_WAIT_MESSAGE 3 #define DEC_WAIT_SUM_A 4 #define DEC_WAIT_SUM_B 5 //@} /// inter-byte timeout for decode (ms) #define DEC_MESSAGE_TIMEOUT 1000 BinComm::BinComm(const BinComm::MessageHandler *handlerTable, Stream *interface) : _handlerTable(handlerTable), _decodePhase(DEC_WAIT_P1), _lastReceived(millis()) { init(interface); }; void BinComm::init(Stream *interface) { _interface = interface; } void BinComm::_startMessage(uint8_t messageId, uint8_t messageLength, uint8_t messageVersion) { // send the preamble first _interface->write((uint8_t)MSG_PREAMBLE_1); _interface->write((uint8_t)MSG_PREAMBLE_2); // initialise the checksum accumulators _encoderSumA = _encoderSumB = 0; // send the header _emit(messageLength); _emit(messageId); _emit(messageVersion); } void BinComm::_send(const void *bytes, uint8_t count) { const uint8_t *p = (const uint8_t *)bytes; uint8_t c; while (count--) { c = *p++; _encoderSumA += c; _encoderSumB += _encoderSumA; _interface->write(c); } } void BinComm::_endMessage(void) { _interface->write(_encoderSumA); _interface->write(_encoderSumB); } 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; _lastReceived = millis(); // 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(MessageHeader); // intialise the checksum accumulators _decoderSumA = _decoderSumB = 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 _decoderSumA += inByte; _decoderSumB += _decoderSumA; // 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_MESSAGE: // do checksum accumulation _decoderSumA += inByte; _decoderSumB += _decoderSumA; // 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 != _decoderSumA) { badMessagesReceived++; _decodePhase = DEC_WAIT_P1; } else { _decodePhase++; } break; case DEC_WAIT_SUM_B: if (inByte == _decoderSumB) { // if we got this far, we have a message messagesReceived++; // call any handler interested in this message for (tableIndex = 0; MSG_NULL != _handlerTable[tableIndex].messageID; tableIndex++) { if(_handlerTable[tableIndex].messageID == MSG_ANY || _handlerTable[tableIndex].messageID == _messageID ) { _handlerTable[tableIndex].handler(_handlerTable[tableIndex].arg, _messageID, _messageVersion, &_decodeBuf); // dont' acknowledge an acknowledgement, will echo infinetly if (_messageID != MSG_ACKNOWLEDGE) send_msg_acknowledge(_messageID, _decoderSumA, _decoderSumB); } else { // XXX should send a NAK of some sort here } } } else { badMessagesReceived++; } // FALLTHROUGH default: _decodePhase = DEC_WAIT_P1; break; } }