Program Listing for File ref_serial_transmitter.cpp
↰ Return to documentation for file (src/tap/communication/serial/ref_serial_transmitter.cpp
)
/*
* Copyright (c) 2022 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/>.
*/
#include "ref_serial_transmitter.hpp"
#include <cstring>
#include "tap/drivers.hpp"
#include "tap/errors/create_errors.hpp"
#include "ref_serial.hpp"
namespace tap::communication::serial
{
RefSerialTransmitter::RefSerialTransmitter(Drivers* drivers) : drivers(drivers) {}
void RefSerialTransmitter::configGraphicGenerics(
Tx::GraphicData* graphicData,
const uint8_t* name,
Tx::GraphicOperation operation,
uint8_t layer,
Tx::GraphicColor color)
{
memcpy(graphicData->name, name, 3);
graphicData->operation = operation;
graphicData->layer = layer;
graphicData->color = static_cast<uint8_t>(color);
}
void RefSerialTransmitter::configLine(
uint16_t width,
uint16_t startX,
uint16_t startY,
uint16_t endX,
uint16_t endY,
Tx::GraphicData* sharedData)
{
sharedData->type = static_cast<uint8_t>(Tx::GraphicType::STRAIGHT_LINE);
sharedData->lineWidth = width;
sharedData->startX = startX;
sharedData->startY = startY;
sharedData->endX = endX;
sharedData->endY = endY;
}
void RefSerialTransmitter::configRectangle(
uint16_t width,
uint16_t startX,
uint16_t startY,
uint16_t endX,
uint16_t endY,
Tx::GraphicData* sharedData)
{
sharedData->type = static_cast<uint8_t>(Tx::GraphicType::RECTANGLE);
sharedData->lineWidth = width;
sharedData->startX = startX;
sharedData->startY = startY;
sharedData->endX = endX;
sharedData->endY = endY;
}
void RefSerialTransmitter::configCircle(
uint16_t width,
uint16_t centerX,
uint16_t centerY,
uint16_t radius,
Tx::GraphicData* sharedData)
{
sharedData->type = static_cast<uint8_t>(Tx::GraphicType::CIRCLE);
sharedData->lineWidth = width;
sharedData->startX = centerX;
sharedData->startY = centerY;
sharedData->radius = radius;
}
void RefSerialTransmitter::configEllipse(
uint16_t width,
uint16_t centerX,
uint16_t centerY,
uint16_t xLen,
uint16_t yLen,
Tx::GraphicData* sharedData)
{
sharedData->type = static_cast<uint8_t>(Tx::GraphicType::ELLIPSE);
sharedData->lineWidth = width;
sharedData->startX = centerX;
sharedData->startY = centerY;
sharedData->endX = xLen;
sharedData->endY = yLen;
}
void RefSerialTransmitter::configArc(
uint16_t startAngle,
uint16_t endAngle,
uint16_t width,
uint16_t centerX,
uint16_t centerY,
uint16_t xLen,
uint16_t yLen,
Tx::GraphicData* sharedData)
{
sharedData->type = static_cast<uint8_t>(Tx::GraphicType::ARC);
sharedData->startAngle = startAngle;
sharedData->endAngle = endAngle;
sharedData->lineWidth = width;
sharedData->startX = centerX;
sharedData->startY = centerY;
sharedData->endX = xLen;
sharedData->endY = yLen;
}
void RefSerialTransmitter::configFloatingNumber(
uint16_t fontSize,
uint16_t decimalPrecision,
uint16_t width,
uint16_t startX,
uint16_t startY,
float value,
Tx::GraphicData* sharedData)
{
sharedData->type = static_cast<uint8_t>(Tx::GraphicType::FLOATING_NUM);
sharedData->startAngle = fontSize;
sharedData->endAngle = decimalPrecision;
sharedData->lineWidth = width;
sharedData->startX = startX;
sharedData->startY = startY;
// Store floating point value in fixed point with 3 decimal points precision
sharedData->value = 1000 * value;
}
void RefSerialTransmitter::configInteger(
uint16_t fontSize,
uint16_t width,
uint16_t startX,
uint16_t startY,
int32_t value,
Tx::GraphicData* sharedData)
{
sharedData->type = static_cast<uint8_t>(Tx::GraphicType::INTEGER);
sharedData->startAngle = fontSize;
sharedData->lineWidth = width;
sharedData->startX = startX;
sharedData->startY = startY;
sharedData->value = value;
}
void RefSerialTransmitter::configCharacterMsg(
uint16_t fontSize,
uint16_t width,
uint16_t startX,
uint16_t startY,
const char* dataToPrint,
Tx::GraphicCharacterMessage* sharedData)
{
sharedData->graphicData.type = static_cast<uint8_t>(Tx::GraphicType::CHARACTER);
sharedData->graphicData.startAngle = fontSize;
sharedData->graphicData.endAngle = strlen(dataToPrint);
sharedData->graphicData.lineWidth = width;
sharedData->graphicData.startX = startX;
sharedData->graphicData.startY = startY;
strncpy(sharedData->msg, dataToPrint, MODM_ARRAY_SIZE(sharedData->msg) - 1);
}
static uint16_t getRobotClientID(RefSerialTransmitter::RobotId robotId)
{
return 0x100 + static_cast<uint16_t>(robotId);
}
modm::ResumableResult<void> RefSerialTransmitter::deleteGraphicLayer(
Tx::DeleteGraphicOperation graphicOperation,
uint8_t graphicLayer)
{
RF_BEGIN(0);
if (drivers->refSerial.getRobotData().robotId == RefSerialTransmitter::RobotId::INVALID)
{
RF_RETURN();
}
deleteGraphicLayerMessage.deleteOperation = graphicOperation;
deleteGraphicLayerMessage.layer = graphicLayer;
configFrameHeader(
&deleteGraphicLayerMessage.frameHeader,
sizeof(deleteGraphicLayerMessage.interactiveHeader) +
sizeof(deleteGraphicLayerMessage.deleteOperation) +
sizeof(deleteGraphicLayerMessage.layer));
deleteGraphicLayerMessage.cmdId = RefSerial::REF_MESSAGE_TYPE_CUSTOM_DATA;
configInteractiveHeader(
&deleteGraphicLayerMessage.interactiveHeader,
0x100,
drivers->refSerial.getRobotData().robotId,
getRobotClientID(drivers->refSerial.getRobotData().robotId));
deleteGraphicLayerMessage.crc16 = algorithms::calculateCRC16(
reinterpret_cast<uint8_t*>(&deleteGraphicLayerMessage),
sizeof(Tx::DeleteGraphicLayerMessage) - sizeof(deleteGraphicLayerMessage.crc16));
RF_WAIT_UNTIL(drivers->refSerial.acquireTransmissionSemaphore());
drivers->uart.write(
bound_ports::REF_SERIAL_UART_PORT,
reinterpret_cast<uint8_t*>(&deleteGraphicLayerMessage),
sizeof(Tx::DeleteGraphicLayerMessage));
drivers->refSerial.releaseTransmissionSemaphore(sizeof(Tx::DeleteGraphicLayerMessage));
RF_END();
}
template <typename GRAPHIC>
modm::ResumableResult<void> RefSerialTransmitter::sendGraphic_(
GRAPHIC* graphicMsg,
uint16_t messageId,
bool configMsgHeader,
bool sendMsg,
RefSerialTransmitter::RobotId robotId,
tap::Drivers* drivers,
uint8_t extraDataLength)
{
RF_BEGIN(1);
if (robotId == RefSerialTransmitter::RobotId::INVALID)
{
RF_RETURN();
}
if (configMsgHeader)
{
RefSerialTransmitter::configFrameHeader(
&graphicMsg->frameHeader,
sizeof(graphicMsg->graphicData) + sizeof(graphicMsg->interactiveHeader) +
extraDataLength);
graphicMsg->cmdId = RefSerial::REF_MESSAGE_TYPE_CUSTOM_DATA;
RefSerialTransmitter::configInteractiveHeader(
&graphicMsg->interactiveHeader,
messageId,
robotId,
getRobotClientID(robotId));
graphicMsg->crc16 = algorithms::calculateCRC16(
reinterpret_cast<uint8_t*>(graphicMsg),
sizeof(*graphicMsg) - sizeof(graphicMsg->crc16));
}
if (sendMsg)
{
RF_WAIT_UNTIL(drivers->refSerial.acquireTransmissionSemaphore());
drivers->uart.write(
bound_ports::REF_SERIAL_UART_PORT,
reinterpret_cast<uint8_t*>(graphicMsg),
sizeof(*graphicMsg));
drivers->refSerial.releaseTransmissionSemaphore(sizeof(GRAPHIC));
}
RF_END();
}
modm::ResumableResult<void> RefSerialTransmitter::sendGraphic(
Tx::Graphic1Message* graphicMsg,
bool configMsgHeader,
bool sendMsg)
{
RF_BEGIN(2);
RF_RETURN_CALL(sendGraphic_<Tx::Graphic1Message>(
graphicMsg,
0x101,
configMsgHeader,
sendMsg,
drivers->refSerial.getRobotData().robotId,
drivers,
0));
RF_END();
}
modm::ResumableResult<void> RefSerialTransmitter::sendGraphic(
Tx::Graphic2Message* graphicMsg,
bool configMsgHeader,
bool sendMsg)
{
RF_BEGIN(3);
RF_RETURN_CALL(sendGraphic_<Tx::Graphic2Message>(
graphicMsg,
0x102,
configMsgHeader,
sendMsg,
drivers->refSerial.getRobotData().robotId,
drivers,
0));
RF_END();
}
modm::ResumableResult<void> RefSerialTransmitter::sendGraphic(
Tx::Graphic5Message* graphicMsg,
bool configMsgHeader,
bool sendMsg)
{
RF_BEGIN(4);
RF_RETURN_CALL(sendGraphic_<Tx::Graphic5Message>(
graphicMsg,
0x103,
configMsgHeader,
sendMsg,
drivers->refSerial.getRobotData().robotId,
drivers,
0));
RF_END();
}
modm::ResumableResult<void> RefSerialTransmitter::sendGraphic(
Tx::Graphic7Message* graphicMsg,
bool configMsgHeader,
bool sendMsg)
{
RF_BEGIN(5);
RF_RETURN_CALL(sendGraphic_<Tx::Graphic7Message>(
graphicMsg,
0x104,
configMsgHeader,
sendMsg,
drivers->refSerial.getRobotData().robotId,
drivers,
0));
RF_END();
}
modm::ResumableResult<void> RefSerialTransmitter::sendGraphic(
Tx::GraphicCharacterMessage* graphicMsg,
bool configMsgHeader,
bool sendMsg)
{
RF_BEGIN(6);
RF_RETURN_CALL(sendGraphic_<Tx::GraphicCharacterMessage>(
graphicMsg,
0x110,
configMsgHeader,
sendMsg,
drivers->refSerial.getRobotData().robotId,
drivers,
MODM_ARRAY_SIZE(graphicMsg->msg)));
RF_END();
}
modm::ResumableResult<void> RefSerialTransmitter::sendRobotToRobotMsg(
Tx::RobotToRobotMessage* robotToRobotMsg,
uint16_t msgId,
RobotId receiverId,
uint16_t msgLen)
{
RF_BEGIN(7);
if (msgLen == 1)
{
RAISE_ERROR(drivers, "message length cannot be 1 byte")
}
if (msgId < 0x0200 || msgId >= 0x02ff)
{
RAISE_ERROR(drivers, "invalid msgId not between [0x200, 0x2ff)");
RF_RETURN();
}
if (msgLen > 113)
{
RAISE_ERROR(drivers, "message length > 113-char maximum");
RF_RETURN();
}
if (drivers->refSerial.getRobotData().robotId == RobotId::INVALID)
{
RF_RETURN();
}
configFrameHeader(
&robotToRobotMsg->frameHeader,
sizeof(robotToRobotMsg->interactiveHeader) + msgLen);
robotToRobotMsg->cmdId = RefSerial::REF_MESSAGE_TYPE_CUSTOM_DATA;
configInteractiveHeader(
&robotToRobotMsg->interactiveHeader,
msgId,
drivers->refSerial.getRobotData().robotId,
static_cast<uint16_t>(receiverId));
static constexpr int FULL_MSG_SIZE_LESS_MSGLEN = sizeof(robotToRobotMsg->frameHeader) +
sizeof(robotToRobotMsg->cmdId) +
sizeof(robotToRobotMsg->interactiveHeader);
*reinterpret_cast<uint16_t*>(robotToRobotMsg->dataAndCRC16 + msgLen) =
algorithms::calculateCRC16(
reinterpret_cast<uint8_t*>(robotToRobotMsg),
FULL_MSG_SIZE_LESS_MSGLEN + msgLen);
RF_WAIT_UNTIL(drivers->refSerial.acquireTransmissionSemaphore());
drivers->uart.write(
bound_ports::REF_SERIAL_UART_PORT,
reinterpret_cast<uint8_t*>(robotToRobotMsg),
FULL_MSG_SIZE_LESS_MSGLEN + msgLen + sizeof(uint16_t));
drivers->refSerial.releaseTransmissionSemaphore(
FULL_MSG_SIZE_LESS_MSGLEN + msgLen + sizeof(uint16_t));
RF_END();
}
void RefSerialTransmitter::configFrameHeader(DJISerial::FrameHeader* header, uint16_t msgLen)
{
header->headByte = 0xa5;
header->dataLength = msgLen;
header->seq = 0;
header->CRC8 = algorithms::calculateCRC8(
reinterpret_cast<const uint8_t*>(header),
sizeof(DJISerial::FrameHeader) - sizeof(header->CRC8));
}
void RefSerialTransmitter::configInteractiveHeader(
Tx::InteractiveHeader* header,
uint16_t cmdId,
RobotId senderId,
uint16_t receiverId)
{
header->dataCmdId = cmdId;
header->senderId = static_cast<uint16_t>(senderId);
header->receiverId = receiverId;
}
} // namespace tap::communication::serial