Program Listing for File dji_serial.hpp

Return to documentation for file (src/tap/communication/serial/dji_serial.hpp)

/*
 * Copyright (c) 2020-2021 Advanced Robotics at the University of Washington <robomstr@uw.edu>
 *
 * This file is part of Taproot.
 *
 * Taproot is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Taproot is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Taproot.  If not, see <https://www.gnu.org/licenses/>.
 */

#ifndef TAPROOT_DJI_SERIAL_HPP_
#define TAPROOT_DJI_SERIAL_HPP_

#include <cstdint>

#include "tap/algorithms/crc.hpp"
#include "tap/communication/serial/uart.hpp"
#include "tap/util_macros.hpp"

namespace tap
{
class Drivers;
}

namespace tap::communication::serial
{
class DJISerial
{
public:
    struct FrameHeader
    {
        uint8_t headByte;
        uint16_t dataLength;
        uint8_t seq;
        uint8_t CRC8;
    } modm_packed;

    template <int DATA_SIZE>
    struct SerialMessage
    {
        explicit SerialMessage(uint8_t seq = 0)
        {
            header.headByte = 0xa5;
            header.dataLength = sizeof(data);
            header.seq = seq;
            header.CRC8 = tap::algorithms::calculateCRC8(
                reinterpret_cast<uint8_t *>(&header),
                sizeof(header) - 1);
        }

        void setCRC16()
        {
            CRC16 = tap::algorithms::calculateCRC16(
                reinterpret_cast<uint8_t *>(this),
                sizeof(*this) - 2);
        }

        FrameHeader header;
        uint16_t messageType;
        uint8_t data[DATA_SIZE];
        uint16_t CRC16;
    } modm_packed;

    static const uint16_t SERIAL_RX_BUFF_SIZE = 1024;
    static const uint16_t SERIAL_HEAD_BYTE = 0xA5;

    using ReceivedSerialMessage = SerialMessage<SERIAL_RX_BUFF_SIZE>;

    DJISerial(Drivers *drivers, Uart::UartPort port, bool isRxCRCEnforcementEnabled = true);
    DISALLOW_COPY_AND_ASSIGN(DJISerial)
    mockable ~DJISerial() = default;

    mockable void initialize();

    mockable void updateSerial();

    virtual void messageReceiveCallback(const ReceivedSerialMessage &completeMessage) = 0;

private:
    enum SerialRxState
    {
        SERIAL_HEADER_SEARCH,
        PROCESS_FRAME_HEADER,
        PROCESS_FRAME_DATA
    };

    Uart::UartPort port;

    SerialRxState djiSerialRxState;

    ReceivedSerialMessage newMessage;

    ReceivedSerialMessage mostRecentMessage;

    uint16_t frameCurrReadByte;

    bool rxCrcEnabled;

    inline bool verifyCRC8(uint8_t *message, uint32_t messageLength, uint8_t expectedCRC8)
    {
        return tap::algorithms::calculateCRC8(message, messageLength) == expectedCRC8;
    }

protected:
    Drivers *drivers;
};

}  // namespace tap::communication::serial

#endif  // TAPROOT_DJI_SERIAL_HPP_