Merge pull request #4050 from codecae/msp_shared_crsf_smartport

Decoupled MSP functionality from S.Port.  Implemented MSP telemetry in CRSF.
This commit is contained in:
Andrey Mironov 2017-09-10 13:37:02 +03:00 committed by GitHub
commit 6bf755d0bd
17 changed files with 738 additions and 229 deletions

View File

@ -159,6 +159,7 @@ FC_SRC = \
telemetry/smartport.c \ telemetry/smartport.c \
telemetry/ltm.c \ telemetry/ltm.c \
telemetry/mavlink.c \ telemetry/mavlink.c \
telemetry/msp_shared.c \
telemetry/ibus.c \ telemetry/ibus.c \
telemetry/ibus_shared.c \ telemetry/ibus_shared.c \
sensors/esc_sensor.c \ sensors/esc_sensor.c \

View File

@ -33,22 +33,29 @@
#include "drivers/serial.h" #include "drivers/serial.h"
#include "drivers/serial_uart.h" #include "drivers/serial_uart.h"
#include "drivers/system.h"
#include "drivers/time.h" #include "drivers/time.h"
#include "io/serial.h" #include "io/serial.h"
#include "msp/msp.h"
#include "rx/rx.h" #include "rx/rx.h"
#include "rx/crsf.h" #include "rx/crsf.h"
#include "telemetry/crsf.h"
#include "telemetry/msp_shared.h"
#define CRSF_TIME_NEEDED_PER_FRAME_US 1000 #define CRSF_TIME_NEEDED_PER_FRAME_US 1000
#define CRSF_TIME_BETWEEN_FRAMES_US 4000 // a frame is sent by the transmitter every 4 milliseconds #define CRSF_TIME_BETWEEN_FRAMES_US 4000 // a frame is sent by the transmitter every 4 milliseconds
#define CRSF_DIGITAL_CHANNEL_MIN 172 #define CRSF_DIGITAL_CHANNEL_MIN 172
#define CRSF_DIGITAL_CHANNEL_MAX 1811 #define CRSF_DIGITAL_CHANNEL_MAX 1811
#define CRSF_PAYLOAD_OFFSET offsetof(crsfFrameDef_t, type)
STATIC_UNIT_TESTED bool crsfFrameDone = false; STATIC_UNIT_TESTED bool crsfFrameDone = false;
STATIC_UNIT_TESTED crsfFrame_t crsfFrame; STATIC_UNIT_TESTED crsfFrame_t crsfFrame;
STATIC_UNIT_TESTED uint32_t crsfChannelData[CRSF_MAX_CHANNEL]; STATIC_UNIT_TESTED uint32_t crsfChannelData[CRSF_MAX_CHANNEL];
static serialPort_t *serialPort; static serialPort_t *serialPort;
@ -56,7 +63,6 @@ static uint32_t crsfFrameStartAt = 0;
static uint8_t telemetryBuf[CRSF_FRAME_SIZE_MAX]; static uint8_t telemetryBuf[CRSF_FRAME_SIZE_MAX];
static uint8_t telemetryBufLen = 0; static uint8_t telemetryBufLen = 0;
/* /*
* CRSF protocol * CRSF protocol
* *
@ -133,6 +139,9 @@ STATIC_UNIT_TESTED void crsfDataReceive(uint16_t c)
if (crsfFramePosition < fullFrameLength) { if (crsfFramePosition < fullFrameLength) {
crsfFrame.bytes[crsfFramePosition++] = (uint8_t)c; crsfFrame.bytes[crsfFramePosition++] = (uint8_t)c;
crsfFrameDone = crsfFramePosition < fullFrameLength ? false : true; crsfFrameDone = crsfFramePosition < fullFrameLength ? false : true;
if (crsfFrameDone) {
crsfFramePosition = 0;
}
} }
} }
@ -176,6 +185,20 @@ STATIC_UNIT_TESTED uint8_t crsfFrameStatus(void)
crsfChannelData[14] = rcChannels->chan14; crsfChannelData[14] = rcChannels->chan14;
crsfChannelData[15] = rcChannels->chan15; crsfChannelData[15] = rcChannels->chan15;
return RX_FRAME_COMPLETE; return RX_FRAME_COMPLETE;
} else {
if (crsfFrame.frame.type == CRSF_FRAMETYPE_DEVICE_PING) {
// TODO: CRC CHECK
scheduleDeviceInfoResponse();
return RX_FRAME_COMPLETE;
} else if (crsfFrame.frame.type == CRSF_FRAMETYPE_MSP_REQ || crsfFrame.frame.type == CRSF_FRAMETYPE_MSP_WRITE) {
// TODO: CRC CHECK
uint8_t *frameStart = (uint8_t *)&crsfFrame.frame.payload + 2;
uint8_t *frameEnd = (uint8_t *)&crsfFrame.frame.payload + 2 + CRSF_FRAME_RX_MSP_PAYLOAD_SIZE;
if(handleMspFrame(frameStart, frameEnd)) {
scheduleMspResponse();
}
return RX_FRAME_COMPLETE;
}
} }
} }
return RX_FRAME_PENDING; return RX_FRAME_PENDING;
@ -222,6 +245,7 @@ void crsfRxSendTelemetryData(void)
bool crsfRxInit(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig) bool crsfRxInit(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig)
{ {
for (int ii = 0; ii < CRSF_MAX_CHANNEL; ++ii) { for (int ii = 0; ii < CRSF_MAX_CHANNEL; ++ii) {
crsfChannelData[ii] = (16 * rxConfig->midrc) / 10 - 1408; crsfChannelData[ii] = (16 * rxConfig->midrc) / 10 - 1408;
} }

View File

@ -22,6 +22,8 @@
#define CRSF_PORT_MODE MODE_RXTX #define CRSF_PORT_MODE MODE_RXTX
#define CRSF_MAX_CHANNEL 16 #define CRSF_MAX_CHANNEL 16
#define CRSF_MSP_RX_BUF_SIZE 128
#define CRSF_MSP_TX_BUF_SIZE 128
typedef enum { typedef enum {
CRSF_FRAMETYPE_GPS = 0x02, CRSF_FRAMETYPE_GPS = 0x02,
@ -29,7 +31,12 @@ typedef enum {
CRSF_FRAMETYPE_LINK_STATISTICS = 0x14, CRSF_FRAMETYPE_LINK_STATISTICS = 0x14,
CRSF_FRAMETYPE_RC_CHANNELS_PACKED = 0x16, CRSF_FRAMETYPE_RC_CHANNELS_PACKED = 0x16,
CRSF_FRAMETYPE_ATTITUDE = 0x1E, CRSF_FRAMETYPE_ATTITUDE = 0x1E,
CRSF_FRAMETYPE_FLIGHT_MODE = 0x21 CRSF_FRAMETYPE_FLIGHT_MODE = 0x21,
CRSF_FRAMETYPE_DEVICE_PING = 0x28,
CRSF_FRAMETYPE_DEVICE_INFO = 0x29,
CRSF_FRAMETYPE_MSP_REQ = 0x7A, // response request using msp sequence as command
CRSF_FRAMETYPE_MSP_RESP = 0x7B, // reply with 58 byte chunked binary
CRSF_FRAMETYPE_MSP_WRITE = 0x7C // write with 8 byte chunked binary (OpenTX outbound telemetry buffer limit)
} crsfFrameType_e; } crsfFrameType_e;
enum { enum {
@ -38,11 +45,14 @@ enum {
CRSF_FRAME_LINK_STATISTICS_PAYLOAD_SIZE = 10, CRSF_FRAME_LINK_STATISTICS_PAYLOAD_SIZE = 10,
CRSF_FRAME_RC_CHANNELS_PAYLOAD_SIZE = 22, // 11 bits per channel * 16 channels = 22 bytes. CRSF_FRAME_RC_CHANNELS_PAYLOAD_SIZE = 22, // 11 bits per channel * 16 channels = 22 bytes.
CRSF_FRAME_ATTITUDE_PAYLOAD_SIZE = 6, CRSF_FRAME_ATTITUDE_PAYLOAD_SIZE = 6,
CRSF_FRAME_TX_MSP_PAYLOAD_SIZE = 58,
CRSF_FRAME_RX_MSP_PAYLOAD_SIZE = 8,
CRSF_FRAME_LENGTH_ADDRESS = 1, // length of ADDRESS field CRSF_FRAME_LENGTH_ADDRESS = 1, // length of ADDRESS field
CRSF_FRAME_LENGTH_FRAMELENGTH = 1, // length of FRAMELENGTH field CRSF_FRAME_LENGTH_FRAMELENGTH = 1, // length of FRAMELENGTH field
CRSF_FRAME_LENGTH_TYPE = 1, // length of TYPE field CRSF_FRAME_LENGTH_TYPE = 1, // length of TYPE field
CRSF_FRAME_LENGTH_CRC = 1, // length of CRC field CRSF_FRAME_LENGTH_CRC = 1, // length of CRC field
CRSF_FRAME_LENGTH_TYPE_CRC = 2 // length of TYPE and CRC fields combined CRSF_FRAME_LENGTH_TYPE_CRC = 2, // length of TYPE and CRC fields combined
CRSF_FRAME_LENGTH_EXT_TYPE_CRC = 4 // length of Extended Dest/Origin, TYPE and CRC fields combined
}; };
enum { enum {
@ -51,7 +61,7 @@ enum {
CRSF_ADDRESS_RESERVED1 = 0x8A, CRSF_ADDRESS_RESERVED1 = 0x8A,
CRSF_ADDRESS_CURRENT_SENSOR = 0xC0, CRSF_ADDRESS_CURRENT_SENSOR = 0xC0,
CRSF_ADDRESS_TBS_BLACKBOX = 0xC4, CRSF_ADDRESS_TBS_BLACKBOX = 0xC4,
CRSF_ADDRESS_COLIBRI_RACE_FC = 0xC8, CRSF_ADDRESS_BETAFLIGHT = 0xC8,
CRSF_ADDRESS_RESERVED2 = 0xCA, CRSF_ADDRESS_RESERVED2 = 0xCA,
CRSF_ADDRESS_RACE_TAG = 0xCC, CRSF_ADDRESS_RACE_TAG = 0xCC,
CRSF_ADDRESS_RADIO_TRANSMITTER = 0xEA, CRSF_ADDRESS_RADIO_TRANSMITTER = 0xEA,
@ -59,7 +69,7 @@ enum {
CRSF_ADDRESS_CRSF_TRANSMITTER = 0xEE CRSF_ADDRESS_CRSF_TRANSMITTER = 0xEE
}; };
#define CRSF_PAYLOAD_SIZE_MAX 32 // !!TODO needs checking #define CRSF_PAYLOAD_SIZE_MAX 60 // Size confirmed by Remo
#define CRSF_FRAME_SIZE_MAX (CRSF_PAYLOAD_SIZE_MAX + 4) #define CRSF_FRAME_SIZE_MAX (CRSF_PAYLOAD_SIZE_MAX + 4)
typedef struct crsfFrameDef_s { typedef struct crsfFrameDef_s {
@ -74,7 +84,6 @@ typedef union crsfFrame_u {
crsfFrameDef_t frame; crsfFrameDef_t frame;
} crsfFrame_t; } crsfFrame_t;
void crsfRxWriteTelemetryData(const void *data, int len); void crsfRxWriteTelemetryData(const void *data, int len);
void crsfRxSendTelemetryData(void); void crsfRxSendTelemetryData(void);

View File

@ -189,3 +189,5 @@
#else #else
#define USED_TIMERS (TIM_N(1) | TIM_N(2) | TIM_N(3) | TIM_N(8) | TIM_N(15) | TIM_N(16)) #define USED_TIMERS (TIM_N(1) | TIM_N(2) | TIM_N(3) | TIM_N(8) | TIM_N(15) | TIM_N(16))
#endif #endif
#undef USE_DASHBOARD

View File

@ -129,12 +129,12 @@
#define USE_BARO_BMP280 #define USE_BARO_BMP280
#define USE_BARO_MS5611 #define USE_BARO_MS5611
#define OSD //#define OSD
#define USE_MAX7456 #define USE_MAX7456
#define MAX7456_SPI_INSTANCE SPI2 #define MAX7456_SPI_INSTANCE SPI2
#define MAX7456_SPI_CS_PIN SPI2_NSS_PIN #define MAX7456_SPI_CS_PIN SPI2_NSS_PIN
#define CMS //#define CMS
//#define USE_SDCARD //#define USE_SDCARD
// //

View File

@ -102,7 +102,6 @@
#define TELEMETRY_LTM #define TELEMETRY_LTM
#define TELEMETRY_SMARTPORT #define TELEMETRY_SMARTPORT
#define USE_RESOURCE_MGMT #define USE_RESOURCE_MGMT
#define USE_SERVOS
#endif #endif
#if (FLASH_SIZE > 128) #if (FLASH_SIZE > 128)
@ -118,6 +117,7 @@
#define USE_RX_MSP #define USE_RX_MSP
#define USE_SERIALRX_JETIEXBUS #define USE_SERIALRX_JETIEXBUS
#define USE_SENSOR_NAMES #define USE_SENSOR_NAMES
#define USE_SERVOS
#define USE_VIRTUAL_CURRENT_METER #define USE_VIRTUAL_CURRENT_METER
#define VTX_COMMON #define VTX_COMMON
#define VTX_CONTROL #define VTX_CONTROL

View File

@ -24,6 +24,7 @@
#ifdef TELEMETRY #ifdef TELEMETRY
#include "config/feature.h" #include "config/feature.h"
#include "build/build_config.h"
#include "build/version.h" #include "build/version.h"
#include "config/parameter_group.h" #include "config/parameter_group.h"
@ -31,6 +32,7 @@
#include "common/crc.h" #include "common/crc.h"
#include "common/maths.h" #include "common/maths.h"
#include "common/printf.h"
#include "common/streambuf.h" #include "common/streambuf.h"
#include "common/utils.h" #include "common/utils.h"
@ -51,12 +53,17 @@
#include "telemetry/telemetry.h" #include "telemetry/telemetry.h"
#include "telemetry/crsf.h" #include "telemetry/crsf.h"
#include "telemetry/msp_shared.h"
#include "fc/config.h" #include "fc/config.h"
#define CRSF_CYCLETIME_US 100000 // 100ms, 10 Hz #define CRSF_CYCLETIME_US 100000 // 100ms, 10 Hz
#define CRSF_DEVICEINFO_VERSION 0x01
#define CRSF_DEVICEINFO_PARAMETER_COUNT 255
static bool crsfTelemetryEnabled; static bool crsfTelemetryEnabled;
static bool deviceInfoReplyPending;
static bool mspReplyPending;
static uint8_t crsfFrame[CRSF_FRAME_SIZE_MAX]; static uint8_t crsfFrame[CRSF_FRAME_SIZE_MAX];
static void crsfInitializeFrame(sbuf_t *dst) static void crsfInitializeFrame(sbuf_t *dst)
@ -223,6 +230,41 @@ void crsfFrameFlightMode(sbuf_t *dst)
*lengthPtr = sbufPtr(dst) - lengthPtr; *lengthPtr = sbufPtr(dst) - lengthPtr;
} }
void scheduleDeviceInfoResponse() {
deviceInfoReplyPending = true;
}
/*
0x29 Device Info
Payload:
uint8_t Destination
uint8_t Origin
char[] Device Name ( Null terminated string )
uint32_t Null Bytes
uint32_t Null Bytes
uint32_t Null Bytes
uint8_t 255 (Max MSP Parameter)
uint8_t 0x01 (Parameter version 1)
*/
void crsfFrameDeviceInfo(sbuf_t *dst) {
char buff[30];
tfp_sprintf(buff, "%s %s: %s", FC_FIRMWARE_NAME, FC_VERSION_STRING, systemConfig()->boardIdentifier);
uint8_t *lengthPtr = sbufPtr(dst);
sbufWriteU8(dst, 0);
sbufWriteU8(dst, CRSF_FRAMETYPE_DEVICE_INFO);
sbufWriteU8(dst, CRSF_ADDRESS_RADIO_TRANSMITTER);
sbufWriteU8(dst, CRSF_ADDRESS_BETAFLIGHT);
sbufWriteStringWithZeroTerminator(dst, buff);
for (unsigned int ii=0; ii<12; ii++) {
sbufWriteU8(dst, 0x00);
}
sbufWriteU8(dst, CRSF_DEVICEINFO_PARAMETER_COUNT);
sbufWriteU8(dst, CRSF_DEVICEINFO_VERSION);
*lengthPtr = sbufPtr(dst) - lengthPtr;
}
#define BV(x) (1 << (x)) // bit value #define BV(x) (1 << (x)) // bit value
// schedule array to decide how often each type of frame is sent // schedule array to decide how often each type of frame is sent
@ -238,6 +280,25 @@ typedef enum {
static uint8_t crsfScheduleCount; static uint8_t crsfScheduleCount;
static uint8_t crsfSchedule[CRSF_SCHEDULE_COUNT_MAX]; static uint8_t crsfSchedule[CRSF_SCHEDULE_COUNT_MAX];
void scheduleMspResponse() {
if (!mspReplyPending) {
mspReplyPending = true;
}
}
void crsfSendMspResponse(uint8_t *payload)
{
sbuf_t crsfPayloadBuf;
sbuf_t *dst = &crsfPayloadBuf;
crsfInitializeFrame(dst);
sbufWriteU8(dst, CRSF_FRAME_TX_MSP_PAYLOAD_SIZE + CRSF_FRAME_LENGTH_EXT_TYPE_CRC);
sbufWriteU8(dst, CRSF_FRAMETYPE_MSP_RESP);
sbufWriteU8(dst, CRSF_ADDRESS_RADIO_TRANSMITTER);
sbufWriteU8(dst, CRSF_ADDRESS_BETAFLIGHT);
sbufWriteData(dst, payload, CRSF_FRAME_TX_MSP_PAYLOAD_SIZE);
crsfFinalize(dst);
}
static void processCrsf(void) static void processCrsf(void)
{ {
@ -257,6 +318,7 @@ static void processCrsf(void)
crsfFrameBatterySensor(dst); crsfFrameBatterySensor(dst);
crsfFinalize(dst); crsfFinalize(dst);
} }
if (currentSchedule & BV(CRSF_FRAME_FLIGHT_MODE_INDEX)) { if (currentSchedule & BV(CRSF_FRAME_FLIGHT_MODE_INDEX)) {
crsfInitializeFrame(dst); crsfInitializeFrame(dst);
crsfFrameFlightMode(dst); crsfFrameFlightMode(dst);
@ -277,6 +339,10 @@ void initCrsfTelemetry(void)
// check if there is a serial port open for CRSF telemetry (ie opened by the CRSF RX) // check if there is a serial port open for CRSF telemetry (ie opened by the CRSF RX)
// and feature is enabled, if so, set CRSF telemetry enabled // and feature is enabled, if so, set CRSF telemetry enabled
crsfTelemetryEnabled = crsfRxIsActive(); crsfTelemetryEnabled = crsfRxIsActive();
deviceInfoReplyPending = false;
mspReplyPending = false;
int index = 0; int index = 0;
crsfSchedule[index++] = BV(CRSF_FRAME_ATTITUDE_INDEX); crsfSchedule[index++] = BV(CRSF_FRAME_ATTITUDE_INDEX);
crsfSchedule[index++] = BV(CRSF_FRAME_BATTERY_SENSOR_INDEX); crsfSchedule[index++] = BV(CRSF_FRAME_BATTERY_SENSOR_INDEX);
@ -311,8 +377,19 @@ void handleCrsfTelemetry(timeUs_t currentTimeUs)
// Actual telemetry data only needs to be sent at a low frequency, ie 10Hz // Actual telemetry data only needs to be sent at a low frequency, ie 10Hz
if (currentTimeUs >= crsfLastCycleTime + CRSF_CYCLETIME_US) { if (currentTimeUs >= crsfLastCycleTime + CRSF_CYCLETIME_US) {
crsfLastCycleTime = currentTimeUs; crsfLastCycleTime = currentTimeUs;
if (deviceInfoReplyPending) {
sbuf_t crsfPayloadBuf;
sbuf_t *dst = &crsfPayloadBuf;
crsfInitializeFrame(dst);
crsfFrameDeviceInfo(dst);
crsfFinalize(dst);
deviceInfoReplyPending = false;
} else if (mspReplyPending) {
mspReplyPending = sendMspReply(CRSF_FRAME_TX_MSP_PAYLOAD_SIZE, &crsfSendMspResponse);
} else {
processCrsf(); processCrsf();
} }
}
} }
int getCrsfFrame(uint8_t *frame, crsfFrameType_e frameType) int getCrsfFrame(uint8_t *frame, crsfFrameType_e frameType)

View File

@ -19,9 +19,12 @@
#include "common/time.h" #include "common/time.h"
#include "rx/crsf.h" #include "rx/crsf.h"
#include "telemetry/msp_shared.h"
void initCrsfTelemetry(void); void initCrsfTelemetry(void);
bool checkCrsfTelemetryState(void); bool checkCrsfTelemetryState(void);
void handleCrsfTelemetry(timeUs_t currentTimeUs); void handleCrsfTelemetry(timeUs_t currentTimeUs);
void scheduleDeviceInfoResponse();
void scheduleMspResponse();
int getCrsfFrame(uint8_t *frame, crsfFrameType_e frameType); int getCrsfFrame(uint8_t *frame, crsfFrameType_e frameType);

View File

@ -0,0 +1,231 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include "platform.h"
#ifdef TELEMETRY
#include "build/build_config.h"
#include "common/utils.h"
#include "fc/fc_msp.h"
#include "msp/msp.h"
#include "rx/crsf.h"
#include "rx/msp.h"
#include "telemetry/msp_shared.h"
#include "telemetry/smartport.h"
#define TELEMETRY_MSP_VERSION 1
#define TELEMETRY_MSP_VER_SHIFT 5
#define TELEMETRY_MSP_VER_MASK (0x7 << TELEMETRY_MSP_VER_SHIFT)
#define TELEMETRY_MSP_ERROR_FLAG (1 << 5)
#define TELEMETRY_MSP_START_FLAG (1 << 4)
#define TELEMETRY_MSP_SEQ_MASK 0x0F
#define TELEMETRY_MSP_RES_ERROR (-10)
enum {
TELEMETRY_MSP_VER_MISMATCH=0,
TELEMETRY_MSP_CRC_ERROR=1,
TELEMETRY_MSP_ERROR=2
};
#define REQUEST_BUFFER_SIZE 64
#define RESPONSE_BUFFER_SIZE 64
STATIC_UNIT_TESTED uint8_t checksum = 0;
STATIC_UNIT_TESTED mspPackage_t mspPackage;
static mspRxBuffer_t mspRxBuffer;
static mspTxBuffer_t mspTxBuffer;
static mspPacket_t mspRxPacket;
static mspPacket_t mspTxPacket;
void initSharedMsp() {
mspPackage.requestBuffer = (uint8_t *)&mspRxBuffer;
mspPackage.requestPacket = &mspRxPacket;
mspPackage.requestPacket->buf.ptr = mspPackage.requestBuffer;
mspPackage.requestPacket->buf.end = mspPackage.requestBuffer;
mspPackage.responseBuffer = (uint8_t *)&mspTxBuffer;
mspPackage.responsePacket = &mspTxPacket;
mspPackage.responsePacket->buf.ptr = mspPackage.responseBuffer;
mspPackage.responsePacket->buf.end = mspPackage.responseBuffer;
}
static void processMspPacket()
{
mspPackage.responsePacket->cmd = 0;
mspPackage.responsePacket->result = 0;
mspPackage.responsePacket->buf.end = mspPackage.responseBuffer;
mspPostProcessFnPtr mspPostProcessFn = NULL;
if (mspFcProcessCommand(mspPackage.requestPacket, mspPackage.responsePacket, &mspPostProcessFn) == MSP_RESULT_ERROR) {
sbufWriteU8(&mspPackage.responsePacket->buf, TELEMETRY_MSP_ERROR);
}
if (mspPostProcessFn) {
mspPostProcessFn(NULL);
}
sbufSwitchToReader(&mspPackage.responsePacket->buf, mspPackage.responseBuffer);
}
void sendMspErrorResponse(uint8_t error, int16_t cmd)
{
mspPackage.responsePacket->cmd = cmd;
mspPackage.responsePacket->result = 0;
mspPackage.responsePacket->buf.end = mspPackage.responseBuffer;
sbufWriteU8(&mspPackage.responsePacket->buf, error);
mspPackage.responsePacket->result = TELEMETRY_MSP_RES_ERROR;
sbufSwitchToReader(&mspPackage.responsePacket->buf, mspPackage.responseBuffer);
}
bool handleMspFrame(uint8_t *frameStart, uint8_t *frameEnd)
{
static uint8_t mspStarted = 0;
static uint8_t lastSeq = 0;
if (sbufBytesRemaining(&mspPackage.responsePacket->buf) > 0) {
return false;
}
if (mspStarted == 0) {
initSharedMsp();
}
mspPacket_t *packet = mspPackage.requestPacket;
sbuf_t *frameBuf = sbufInit(&mspPackage.requestFrame, frameStart, frameEnd);
sbuf_t *rxBuf = &mspPackage.requestPacket->buf;
uint8_t header = sbufReadU8(frameBuf);
uint8_t seqNumber = header & TELEMETRY_MSP_SEQ_MASK;
uint8_t version = (header & TELEMETRY_MSP_VER_MASK) >> TELEMETRY_MSP_VER_SHIFT;
if (version != TELEMETRY_MSP_VERSION) {
sendMspErrorResponse(TELEMETRY_MSP_VER_MISMATCH, 0);
return true;
}
if (header & TELEMETRY_MSP_START_FLAG) {
// first packet in sequence
uint8_t mspPayloadSize = sbufReadU8(frameBuf);
packet->cmd = sbufReadU8(frameBuf);
packet->result = 0;
packet->buf.ptr = mspPackage.requestBuffer;
packet->buf.end = mspPackage.requestBuffer + mspPayloadSize;
checksum = mspPayloadSize ^ packet->cmd;
mspStarted = 1;
} else if (!mspStarted) {
// no start packet yet, throw this one away
return false;
} else if (((lastSeq + 1) & TELEMETRY_MSP_SEQ_MASK) != seqNumber) {
// packet loss detected!
mspStarted = 0;
return false;
}
uint8_t bufferBytesRemaining = sbufBytesRemaining(rxBuf);
uint8_t frameBytesRemaining = sbufBytesRemaining(frameBuf);
uint8_t payload[frameBytesRemaining];
if (bufferBytesRemaining >= frameBytesRemaining) {
sbufReadData(frameBuf, payload, frameBytesRemaining);
sbufAdvance(frameBuf, frameBytesRemaining);
sbufWriteData(rxBuf, payload, frameBytesRemaining);
lastSeq = seqNumber;
return false;
} else {
sbufReadData(frameBuf, payload, bufferBytesRemaining);
sbufAdvance(frameBuf, bufferBytesRemaining);
sbufWriteData(rxBuf, payload, bufferBytesRemaining);
sbufSwitchToReader(rxBuf, mspPackage.requestBuffer);
while (sbufBytesRemaining(rxBuf)) {
checksum ^= sbufReadU8(rxBuf);
}
if (checksum != *frameBuf->ptr) {
mspStarted = 0;
sendMspErrorResponse(TELEMETRY_MSP_CRC_ERROR, packet->cmd);
return true;
}
}
mspStarted = 0;
sbufSwitchToReader(rxBuf, mspPackage.requestBuffer);
processMspPacket();
return true;
}
bool sendMspReply(uint8_t payloadSize, mspResponseFnPtr responseFn)
{
static uint8_t checksum = 0;
static uint8_t seq = 0;
uint8_t payloadOut[payloadSize];
sbuf_t payload;
sbuf_t *payloadBuf = sbufInit(&payload, payloadOut, payloadOut + payloadSize);
sbuf_t *txBuf = &mspPackage.responsePacket->buf;
// detect first reply packet
if (txBuf->ptr == mspPackage.responseBuffer) {
// header
uint8_t head = TELEMETRY_MSP_START_FLAG | (seq++ & TELEMETRY_MSP_SEQ_MASK);
if (mspPackage.responsePacket->result < 0) {
head |= TELEMETRY_MSP_ERROR_FLAG;
}
sbufWriteU8(payloadBuf, head);
uint8_t size = sbufBytesRemaining(txBuf);
sbufWriteU8(payloadBuf, size);
}
else {
// header
sbufWriteU8(payloadBuf, (seq++ & TELEMETRY_MSP_SEQ_MASK));
}
uint8_t bufferBytesRemaining = sbufBytesRemaining(txBuf);
uint8_t payloadBytesRemaining = sbufBytesRemaining(payloadBuf);
uint8_t frame[payloadBytesRemaining];
if (bufferBytesRemaining >= payloadBytesRemaining) {
sbufReadData(txBuf, frame, payloadBytesRemaining);
sbufAdvance(txBuf, payloadBytesRemaining);
sbufWriteData(payloadBuf, frame, payloadBytesRemaining);
responseFn(payloadOut);
return true;
} else {
sbufReadData(txBuf, frame, bufferBytesRemaining);
sbufAdvance(txBuf, bufferBytesRemaining);
sbufWriteData(payloadBuf, frame, bufferBytesRemaining);
sbufSwitchToReader(txBuf, mspPackage.responseBuffer);
checksum = sbufBytesRemaining(txBuf) ^ mspPackage.responsePacket->cmd;
while (sbufBytesRemaining(txBuf)) {
checksum ^= sbufReadU8(txBuf);
}
sbufWriteU8(payloadBuf, checksum);
while (sbufBytesRemaining(payloadBuf)>1) {
sbufWriteU8(payloadBuf, 0);
}
}
responseFn(payloadOut);
return false;
}
#endif

View File

@ -0,0 +1,29 @@
#pragma once
#include "msp/msp.h"
#include "rx/crsf.h"
#include "telemetry/smartport.h"
typedef void (*mspResponseFnPtr)(uint8_t *payload);
typedef struct mspPackage_s {
sbuf_t requestFrame;
uint8_t *requestBuffer;
uint8_t *responseBuffer;
mspPacket_t *requestPacket;
mspPacket_t *responsePacket;
} mspPackage_t;
typedef union mspRxBuffer_u {
uint8_t smartPortMspRxBuffer[SMARTPORT_MSP_RX_BUF_SIZE];
uint8_t crsfMspRxBuffer[CRSF_MSP_RX_BUF_SIZE];
} mspRxBuffer_t;
typedef union mspTxBuffer_u {
uint8_t smartPortMspTxBuffer[SMARTPORT_MSP_TX_BUF_SIZE];
uint8_t crsfMspTxBuffer[CRSF_MSP_TX_BUF_SIZE];
} mspTxBuffer_t;
void initSharedMsp();
bool handleMspFrame(uint8_t *frameStart, uint8_t *frameEnd);
bool sendMspReply(uint8_t payloadSize, mspResponseFnPtr responseFn);

View File

@ -43,8 +43,6 @@
#include "io/gps.h" #include "io/gps.h"
#include "io/serial.h" #include "io/serial.h"
#include "msp/msp.h"
#include "sensors/boardalignment.h" #include "sensors/boardalignment.h"
#include "sensors/sensors.h" #include "sensors/sensors.h"
#include "sensors/battery.h" #include "sensors/battery.h"
@ -54,10 +52,10 @@
#include "sensors/gyro.h" #include "sensors/gyro.h"
#include "rx/rx.h" #include "rx/rx.h"
#include "rx/msp.h"
#include "telemetry/telemetry.h" #include "telemetry/telemetry.h"
#include "telemetry/smartport.h" #include "telemetry/smartport.h"
#include "telemetry/msp_shared.h"
enum enum
{ {
@ -164,7 +162,6 @@ typedef struct smartPortFrame_s {
} __attribute__((packed)) smartPortFrame_t; } __attribute__((packed)) smartPortFrame_t;
#define SMARTPORT_FRAME_SIZE sizeof(smartPortFrame_t) #define SMARTPORT_FRAME_SIZE sizeof(smartPortFrame_t)
#define SMARTPORT_TX_BUF_SIZE 256
#define SMARTPORT_PAYLOAD_OFFSET offsetof(smartPortFrame_t, valueId) #define SMARTPORT_PAYLOAD_OFFSET offsetof(smartPortFrame_t, valueId)
#define SMARTPORT_PAYLOAD_SIZE (SMARTPORT_FRAME_SIZE - SMARTPORT_PAYLOAD_OFFSET - 1) #define SMARTPORT_PAYLOAD_SIZE (SMARTPORT_FRAME_SIZE - SMARTPORT_PAYLOAD_OFFSET - 1)
@ -172,30 +169,8 @@ typedef struct smartPortFrame_s {
static smartPortFrame_t smartPortRxBuffer; static smartPortFrame_t smartPortRxBuffer;
static uint8_t smartPortRxBytes = 0; static uint8_t smartPortRxBytes = 0;
static bool smartPortFrameReceived = false; static bool smartPortFrameReceived = false;
#define SMARTPORT_MSP_VERSION 1
#define SMARTPORT_MSP_VER_SHIFT 5
#define SMARTPORT_MSP_VER_MASK (0x7 << SMARTPORT_MSP_VER_SHIFT)
#define SMARTPORT_MSP_VERSION_S (SMARTPORT_MSP_VERSION << SMARTPORT_MSP_VER_SHIFT)
#define SMARTPORT_MSP_ERROR_FLAG (1 << 5)
#define SMARTPORT_MSP_START_FLAG (1 << 4)
#define SMARTPORT_MSP_SEQ_MASK 0x0F
#define SMARTPORT_MSP_RX_BUF_SIZE 64
static uint8_t smartPortMspTxBuffer[SMARTPORT_TX_BUF_SIZE];
static mspPacket_t smartPortMspReply;
static bool smartPortMspReplyPending = false; static bool smartPortMspReplyPending = false;
#define SMARTPORT_MSP_RES_ERROR (-10)
enum {
SMARTPORT_MSP_VER_MISMATCH=0,
SMARTPORT_MSP_CRC_ERROR=1,
SMARTPORT_MSP_ERROR=2
};
static void smartPortDataReceive(uint16_t c) static void smartPortDataReceive(uint16_t c)
{ {
static bool skipUntilStart = true; static bool skipUntilStart = true;
@ -352,194 +327,8 @@ void checkSmartPortTelemetryState(void)
freeSmartPortTelemetryPort(); freeSmartPortTelemetryPort();
} }
static void initSmartPortMspReply(int16_t cmd) void smartPortSendMspResponse(uint8_t *payload) {
{ smartPortSendPackageEx(FSSP_MSPS_FRAME, payload);
smartPortMspReply.buf.ptr = smartPortMspTxBuffer;
smartPortMspReply.buf.end = ARRAYEND(smartPortMspTxBuffer);
smartPortMspReply.cmd = cmd;
smartPortMspReply.result = 0;
}
static void processMspPacket(mspPacket_t* packet)
{
initSmartPortMspReply(0);
if (mspFcProcessCommand(packet, &smartPortMspReply, NULL) == MSP_RESULT_ERROR) {
sbufWriteU8(&smartPortMspReply.buf, SMARTPORT_MSP_ERROR);
}
// change streambuf direction
sbufSwitchToReader(&smartPortMspReply.buf, smartPortMspTxBuffer);
smartPortMspReplyPending = true;
}
/**
* Request frame format:
* - Header: 1 byte
* - Reserved: 2 bits (future use)
* - Error-flag: 1 bit
* - Start-flag: 1 bit
* - CSeq: 4 bits
*
* - MSP payload:
* - if Error-flag == 0:
* - size: 1 byte
* - payload
* - CRC (request type included)
* - if Error-flag == 1:
* - size: 1 byte (== 1)
* - error: 1 Byte
* - 0: Version mismatch (type=0)
* - 1: Sequence number error
* - 2: MSP error
* - CRC (request type included)
*/
bool smartPortSendMspReply()
{
static uint8_t checksum = 0;
static uint8_t seq = 0;
uint8_t packet[SMARTPORT_PAYLOAD_SIZE];
uint8_t* p = packet;
uint8_t* end = p + SMARTPORT_PAYLOAD_SIZE;
sbuf_t* txBuf = &smartPortMspReply.buf;
// detect first reply packet
if (txBuf->ptr == smartPortMspTxBuffer) {
// header
uint8_t head = SMARTPORT_MSP_START_FLAG | (seq++ & SMARTPORT_MSP_SEQ_MASK);
if (smartPortMspReply.result < 0) {
head |= SMARTPORT_MSP_ERROR_FLAG;
}
*p++ = head;
uint8_t size = sbufBytesRemaining(txBuf);
*p++ = size;
checksum = size ^ smartPortMspReply.cmd;
}
else {
// header
*p++ = (seq++ & SMARTPORT_MSP_SEQ_MASK);
}
while ((p < end) && (sbufBytesRemaining(txBuf) > 0)) {
*p = sbufReadU8(txBuf);
checksum ^= *p++; // MSP checksum
}
// to be continued...
if (p == end) {
smartPortSendPackageEx(FSSP_MSPS_FRAME,packet);
return true;
}
// nothing left in txBuf,
// append the MSP checksum
*p++ = checksum;
// pad with zeros
while (p < end)
*p++ = 0;
smartPortSendPackageEx(FSSP_MSPS_FRAME,packet);
return false;
}
void smartPortSendErrorReply(uint8_t error, int16_t cmd)
{
initSmartPortMspReply(cmd);
sbufWriteU8(&smartPortMspReply.buf,error);
smartPortMspReply.result = SMARTPORT_MSP_RES_ERROR;
sbufSwitchToReader(&smartPortMspReply.buf, smartPortMspTxBuffer);
smartPortMspReplyPending = true;
}
/**
* Request frame format:
* - Header: 1 byte
* - Version: 3 bits
* - Start-flag: 1 bit
* - CSeq: 4 bits
*
* - MSP payload:
* - Size: 1 Byte
* - Type: 1 Byte
* - payload...
* - CRC
*/
void handleSmartPortMspFrame(smartPortFrame_t* sp_frame)
{
static uint8_t mspBuffer[SMARTPORT_MSP_RX_BUF_SIZE];
static uint8_t mspStarted = 0;
static uint8_t lastSeq = 0;
static uint8_t checksum = 0;
static mspPacket_t cmd;
// re-assemble MSP frame & forward to MSP port when complete
uint8_t* p = ((uint8_t*)sp_frame) + SMARTPORT_PAYLOAD_OFFSET;
uint8_t* end = p + SMARTPORT_PAYLOAD_SIZE;
uint8_t head = *p++;
uint8_t seq = head & SMARTPORT_MSP_SEQ_MASK;
uint8_t version = (head & SMARTPORT_MSP_VER_MASK) >> SMARTPORT_MSP_VER_SHIFT;
if (version != SMARTPORT_MSP_VERSION) {
mspStarted = 0;
smartPortSendErrorReply(SMARTPORT_MSP_VER_MISMATCH,0);
return;
}
// check start-flag
if (head & SMARTPORT_MSP_START_FLAG) {
//TODO: if (p_size > SMARTPORT_MSP_RX_BUF_SIZE) error!
uint8_t p_size = *p++;
cmd.cmd = *p++;
cmd.result = 0;
cmd.buf.ptr = mspBuffer;
cmd.buf.end = mspBuffer + p_size;
checksum = p_size ^ cmd.cmd;
mspStarted = 1;
} else if (!mspStarted) {
// no start packet yet, throw this one away
return;
} else if (((lastSeq + 1) & SMARTPORT_MSP_SEQ_MASK) != seq) {
// packet loss detected!
mspStarted = 0;
return;
}
// copy payload bytes
while ((p < end) && sbufBytesRemaining(&cmd.buf)) {
checksum ^= *p;
sbufWriteU8(&cmd.buf,*p++);
}
// reached end of smart port frame
if (p == end) {
lastSeq = seq;
return;
}
// last byte must be the checksum
if (checksum != *p) {
mspStarted = 0;
smartPortSendErrorReply(SMARTPORT_MSP_CRC_ERROR,cmd.cmd);
return;
}
// end of MSP packet reached
mspStarted = 0;
sbufSwitchToReader(&cmd.buf,mspBuffer);
processMspPacket(&cmd);
} }
void handleSmartPortTelemetry(void) void handleSmartPortTelemetry(void)
@ -566,7 +355,11 @@ void handleSmartPortTelemetry(void)
if (smartPortRxBuffer.frameId == FSSP_MSPC_FRAME) { if (smartPortRxBuffer.frameId == FSSP_MSPC_FRAME) {
// Pass only the payload: skip sensorId & frameId // Pass only the payload: skip sensorId & frameId
handleSmartPortMspFrame(&smartPortRxBuffer); if (!smartPortMspReplyPending) {
uint8_t *frameStart = (uint8_t *)&smartPortRxBuffer + SMARTPORT_PAYLOAD_OFFSET;
uint8_t *frameEnd = (uint8_t *)&smartPortRxBuffer + SMARTPORT_PAYLOAD_OFFSET + SMARTPORT_PAYLOAD_SIZE;
smartPortMspReplyPending = handleMspFrame(frameStart, frameEnd);
}
} }
} }
@ -578,7 +371,7 @@ void handleSmartPortTelemetry(void)
} }
if (smartPortMspReplyPending) { if (smartPortMspReplyPending) {
smartPortMspReplyPending = smartPortSendMspReply(); smartPortMspReplyPending = sendMspReply(SMARTPORT_PAYLOAD_SIZE, &smartPortSendMspResponse);
smartPortHasRequest = 0; smartPortHasRequest = 0;
return; return;
} }

View File

@ -7,6 +7,9 @@
#pragma once #pragma once
#define SMARTPORT_MSP_TX_BUF_SIZE 256
#define SMARTPORT_MSP_RX_BUF_SIZE 64
void initSmartPortTelemetry(void); void initSmartPortTelemetry(void);
void handleSmartPortTelemetry(void); void handleSmartPortTelemetry(void);

View File

@ -52,6 +52,7 @@
#include "telemetry/crsf.h" #include "telemetry/crsf.h"
#include "telemetry/srxl.h" #include "telemetry/srxl.h"
#include "telemetry/ibus.h" #include "telemetry/ibus.h"
#include "telemetry/msp_shared.h"
PG_REGISTER_WITH_RESET_TEMPLATE(telemetryConfig_t, telemetryConfig, PG_TELEMETRY_CONFIG, 0); PG_REGISTER_WITH_RESET_TEMPLATE(telemetryConfig_t, telemetryConfig, PG_TELEMETRY_CONFIG, 0);
@ -100,6 +101,7 @@ void telemetryInit(void)
#ifdef TELEMETRY_IBUS #ifdef TELEMETRY_IBUS
initIbusTelemetry(); initIbusTelemetry();
#endif #endif
initSharedMsp();
telemetryCheckState(); telemetryCheckState();
} }

View File

@ -172,7 +172,10 @@ rc_controls_unittest_SRC := \
rx_crsf_unittest_SRC := \ rx_crsf_unittest_SRC := \
$(USER_DIR)/rx/crsf.c \ $(USER_DIR)/rx/crsf.c \
$(USER_DIR)/common/crc.c \ $(USER_DIR)/common/crc.c \
$(USER_DIR)/common/streambuf.c $(USER_DIR)/common/printf.c \
$(USER_DIR)/common/typeconversion.c \
$(USER_DIR)/common/streambuf.c \
$(USER_DIR)/drivers/serial.c
rx_ibus_unittest_SRC := \ rx_ibus_unittest_SRC := \
@ -207,8 +210,24 @@ telemetry_crsf_unittest_SRC := \
$(USER_DIR)/common/maths.c \ $(USER_DIR)/common/maths.c \
$(USER_DIR)/common/streambuf.c \ $(USER_DIR)/common/streambuf.c \
$(USER_DIR)/common/gps_conversion.c \ $(USER_DIR)/common/gps_conversion.c \
$(USER_DIR)/common/printf.c \
$(USER_DIR)/common/typeconversion.c \
$(USER_DIR)/fc/runtime_config.c $(USER_DIR)/fc/runtime_config.c
telemetry_crsf_msp_unittest_SRC := \
$(USER_DIR)/rx/crsf.c \
$(USER_DIR)/common/crc.c \
$(USER_DIR)/common/streambuf.c \
$(USER_DIR)/common/printf.c \
$(USER_DIR)/common/streambuf.c \
$(USER_DIR)/drivers/serial.c \
$(USER_DIR)/common/typeconversion.c \
$(USER_DIR)/telemetry/crsf.c \
$(USER_DIR)/common/gps_conversion.c \
$(USER_DIR)/telemetry/msp_shared.c \
$(USER_DIR)/fc/runtime_config.c
telemetry_crsf_unittest_DEFINES := \ telemetry_crsf_unittest_DEFINES := \
FLASH_SIZE=128 \ FLASH_SIZE=128 \
STM32F10X_MD \ STM32F10X_MD \
@ -254,6 +273,9 @@ huffman_unittest_SRC := \
huffman_unittest_DEFINES := \ huffman_unittest_DEFINES := \
USE_HUFFMAN USE_HUFFMAN
ringbuffer_unittest_SRC := \
$(USER_DIR)/common/ringbuffer.c
# Please tweak the following variable definitions as needed by your # Please tweak the following variable definitions as needed by your
# project, except GTEST_HEADERS, which you can use in your own targets # project, except GTEST_HEADERS, which you can use in your own targets
# but shouldn't modify. # but shouldn't modify.

View File

@ -29,11 +29,14 @@ extern "C" {
#include "common/crc.h" #include "common/crc.h"
#include "common/utils.h" #include "common/utils.h"
#include "drivers/serial.h"
#include "io/serial.h" #include "io/serial.h"
#include "rx/rx.h" #include "rx/rx.h"
#include "rx/crsf.h" #include "rx/crsf.h"
#include "telemetry/msp_shared.h"
void crsfDataReceive(uint16_t c); void crsfDataReceive(uint16_t c);
uint8_t crsfFrameCRC(void); uint8_t crsfFrameCRC(void);
uint8_t crsfFrameStatus(void); uint8_t crsfFrameStatus(void);
@ -277,7 +280,9 @@ int16_t debug[DEBUG16_VALUE_COUNT];
uint32_t micros(void) {return dummyTimeUs;} uint32_t micros(void) {return dummyTimeUs;}
serialPort_t *openSerialPort(serialPortIdentifier_e, serialPortFunction_e, serialReceiveCallbackPtr, uint32_t, portMode_e, portOptions_e) {return NULL;} serialPort_t *openSerialPort(serialPortIdentifier_e, serialPortFunction_e, serialReceiveCallbackPtr, uint32_t, portMode_e, portOptions_e) {return NULL;}
serialPortConfig_t *findSerialPortConfig(serialPortFunction_e ) {return NULL;} serialPortConfig_t *findSerialPortConfig(serialPortFunction_e ) {return NULL;}
void serialWriteBuf(serialPort_t *, const uint8_t *, int) {}
bool telemetryCheckRxPortShared(const serialPortConfig_t *) {return false;} bool telemetryCheckRxPortShared(const serialPortConfig_t *) {return false;}
serialPort_t *telemetrySharedPort = NULL; serialPort_t *telemetrySharedPort = NULL;
void scheduleDeviceInfoResponse(void) {};
void scheduleMspResponse(mspPackage_t *package) { UNUSED(package); };
bool handleMspFrame(uint8_t *, uint8_t *) { return false; }
} }

View File

@ -0,0 +1,299 @@
/*
* This file is part of Cleanflight.
*
* Cleanflight 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.
*
* Cleanflight 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 Cleanflight. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <limits.h>
#include <algorithm>
extern "C" {
#include <platform.h>
#include "build/debug.h"
#include "common/crc.h"
#include "common/utils.h"
#include "common/printf.h"
#include "common/gps_conversion.h"
#include "common/streambuf.h"
#include "common/typeconversion.h"
#include "config/parameter_group.h"
#include "config/parameter_group_ids.h"
#include "drivers/serial.h"
#include "drivers/system.h"
#include "fc/runtime_config.h"
#include "fc/config.h"
#include "flight/imu.h"
#include "fc/fc_msp.h"
#include "io/serial.h"
#include "io/gps.h"
#include "msp/msp.h"
#include "rx/rx.h"
#include "rx/crsf.h"
#include "sensors/battery.h"
#include "sensors/sensors.h"
#include "telemetry/telemetry.h"
#include "telemetry/msp_shared.h"
#include "telemetry/smartport.h"
bool handleMspFrame(uint8_t *frameStart, uint8_t *frameEnd);
bool sendMspReply(uint8_t payloadSize, mspResponseFnPtr responseFn);
uint8_t sbufReadU8(sbuf_t *src);
int sbufBytesRemaining(sbuf_t *buf);
void initSharedMsp();
uint16_t testBatteryVoltage = 0;
int32_t testAmperage = 0;
uint8_t mspTxData[64]; //max frame size
sbuf_t mspTxDataBuf;
uint8_t crsfFrameOut[CRSF_FRAME_SIZE_MAX];
uint8_t payloadOutput[64];
sbuf_t payloadOutputBuf;
int32_t testmAhDrawn = 0;
PG_REGISTER(batteryConfig_t, batteryConfig, PG_BATTERY_CONFIG, 0);
PG_REGISTER(telemetryConfig_t, telemetryConfig, PG_TELEMETRY_CONFIG, 0);
PG_REGISTER(systemConfig_t, systemConfig, PG_SYSTEM_CONFIG, 0);
extern bool crsfFrameDone;
extern crsfFrame_t crsfFrame;
extern mspPackage_t mspPackage;
extern uint8_t checksum;
uint32_t dummyTimeUs;
}
#include "unittest_macros.h"
#include "gtest/gtest.h"
typedef struct crsfMspFrame_s {
uint8_t deviceAddress;
uint8_t frameLength;
uint8_t type;
uint8_t destination;
uint8_t origin;
uint8_t payload[CRSF_FRAME_RX_MSP_PAYLOAD_SIZE + CRSF_FRAME_LENGTH_CRC];
} crsfMspFrame_t;
const uint8_t crsfPidRequest[] = {
0x00,0x0D,0x7A,0xC8,0xEA,0x30,0x00,0x70,0x70,0x00,0x00,0x00,0x00,0x69
};
TEST(CrossFireMSPTest, RequestBufferTest)
{
initSharedMsp();
const crsfMspFrame_t *framePtr = (const crsfMspFrame_t*)crsfPidRequest;
crsfFrame = *(const crsfFrame_t*)framePtr;
crsfFrameDone = true;
EXPECT_EQ(CRSF_ADDRESS_BROADCAST, crsfFrame.frame.deviceAddress);
EXPECT_EQ(CRSF_FRAME_LENGTH_ADDRESS + CRSF_FRAME_LENGTH_EXT_TYPE_CRC + CRSF_FRAME_RX_MSP_PAYLOAD_SIZE, crsfFrame.frame.frameLength);
EXPECT_EQ(CRSF_FRAMETYPE_MSP_REQ, crsfFrame.frame.type);
uint8_t *destination = (uint8_t *)&crsfFrame.frame.payload;
uint8_t *origin = (uint8_t *)&crsfFrame.frame.payload + 1;
uint8_t *frameStart = (uint8_t *)&crsfFrame.frame.payload + 2;
uint8_t *frameEnd = (uint8_t *)&crsfFrame.frame.payload + CRSF_FRAME_RX_MSP_PAYLOAD_SIZE + 2;
EXPECT_EQ(0xC8, *destination);
EXPECT_EQ(0xEA, *origin);
EXPECT_EQ(0x30, *frameStart);
EXPECT_EQ(0x69, *frameEnd);
}
TEST(CrossFireMSPTest, ResponsePacketTest)
{
initSharedMsp();
const crsfMspFrame_t *framePtr = (const crsfMspFrame_t*)crsfPidRequest;
crsfFrame = *(const crsfFrame_t*)framePtr;
crsfFrameDone = true;
uint8_t *frameStart = (uint8_t *)&crsfFrame.frame.payload + 2;
uint8_t *frameEnd = (uint8_t *)&crsfFrame.frame.payload + CRSF_FRAME_RX_MSP_PAYLOAD_SIZE + 2;
handleMspFrame(frameStart, frameEnd);
for (unsigned int ii=1; ii<30; ii++) {
EXPECT_EQ(ii, sbufReadU8(&mspPackage.responsePacket->buf));
}
sbufSwitchToReader(&mspPackage.responsePacket->buf, mspPackage.responseBuffer);
}
const uint8_t crsfPidWrite1[] = {0x00,0x0D,0x7C,0xC8,0xEA,0x31,0x1E,0xCA,0x29,0x28,0x1E,0x3A,0x32};
const uint8_t crsfPidWrite2[] = {0x00,0x0D,0x7C,0xC8,0xEA,0x22,0x23,0x46,0x2D,0x14,0x32,0x00,0x00};
const uint8_t crsfPidWrite3[] = {0x00,0x0D,0x7C,0xC8,0xEA,0x23,0x0F,0x00,0x00,0x22,0x0E,0x35,0x19};
const uint8_t crsfPidWrite4[] = {0x00,0x0D,0x7C,0xC8,0xEA,0x24,0x21,0x53,0x32,0x32,0x4B,0x28,0x00};
const uint8_t crsfPidWrite5[] = {0x00,0x0D,0x7C,0xC8,0xEA,0x25,0x00,0x37,0x37,0x4B,0xF8,0x00,0x00};
TEST(CrossFireMSPTest, WriteResponseTest)
{
initSharedMsp();
const crsfMspFrame_t *framePtr1 = (const crsfMspFrame_t*)crsfPidWrite1;
crsfFrame = *(const crsfFrame_t*)framePtr1;
crsfFrameDone = true;
uint8_t *frameStart = (uint8_t *)&crsfFrame.frame.payload + 2;
uint8_t *frameEnd = (uint8_t *)&crsfFrame.frame.payload + CRSF_FRAME_RX_MSP_PAYLOAD_SIZE + 2;
bool pending1 = handleMspFrame(frameStart, frameEnd);
EXPECT_FALSE(pending1); // not done yet*/
EXPECT_EQ(0x29, mspPackage.requestBuffer[0]);
EXPECT_EQ(0x28, mspPackage.requestBuffer[1]);
EXPECT_EQ(0x1E, mspPackage.requestBuffer[2]);
EXPECT_EQ(0x3A, mspPackage.requestBuffer[3]);
EXPECT_EQ(0x32, mspPackage.requestBuffer[4]);
const crsfMspFrame_t *framePtr2 = (const crsfMspFrame_t*)crsfPidWrite2;
crsfFrame = *(const crsfFrame_t*)framePtr2;
crsfFrameDone = true;
uint8_t *frameStart2 = (uint8_t *)&crsfFrame.frame.payload + 2;
uint8_t *frameEnd2 = (uint8_t *)&crsfFrame.frame.payload + CRSF_FRAME_RX_MSP_PAYLOAD_SIZE + 2;
bool pending2 = handleMspFrame(frameStart2, frameEnd2);
EXPECT_FALSE(pending2); // not done yet
EXPECT_EQ(0x23, mspPackage.requestBuffer[5]);
EXPECT_EQ(0x46, mspPackage.requestBuffer[6]);
EXPECT_EQ(0x2D, mspPackage.requestBuffer[7]);
EXPECT_EQ(0x14, mspPackage.requestBuffer[8]);
EXPECT_EQ(0x32, mspPackage.requestBuffer[9]);
EXPECT_EQ(0x00, mspPackage.requestBuffer[10]);
EXPECT_EQ(0x00, mspPackage.requestBuffer[11]);
const crsfMspFrame_t *framePtr3 = (const crsfMspFrame_t*)crsfPidWrite3;
crsfFrame = *(const crsfFrame_t*)framePtr3;
crsfFrameDone = true;
uint8_t *frameStart3 = (uint8_t *)&crsfFrame.frame.payload + 2;
uint8_t *frameEnd3 = frameStart3 + CRSF_FRAME_RX_MSP_PAYLOAD_SIZE;
bool pending3 = handleMspFrame(frameStart3, frameEnd3);
EXPECT_FALSE(pending3); // not done yet
EXPECT_EQ(0x0F, mspPackage.requestBuffer[12]);
EXPECT_EQ(0x00, mspPackage.requestBuffer[13]);
EXPECT_EQ(0x00, mspPackage.requestBuffer[14]);
EXPECT_EQ(0x22, mspPackage.requestBuffer[15]);
EXPECT_EQ(0x0E, mspPackage.requestBuffer[16]);
EXPECT_EQ(0x35, mspPackage.requestBuffer[17]);
EXPECT_EQ(0x19, mspPackage.requestBuffer[18]);
const crsfMspFrame_t *framePtr4 = (const crsfMspFrame_t*)crsfPidWrite4;
crsfFrame = *(const crsfFrame_t*)framePtr4;
crsfFrameDone = true;
uint8_t *frameStart4 = (uint8_t *)&crsfFrame.frame.payload + 2;
uint8_t *frameEnd4 = frameStart4 + CRSF_FRAME_RX_MSP_PAYLOAD_SIZE;
bool pending4 = handleMspFrame(frameStart4, frameEnd4);
EXPECT_FALSE(pending4); // not done yet
EXPECT_EQ(0x21, mspPackage.requestBuffer[19]);
EXPECT_EQ(0x53, mspPackage.requestBuffer[20]);
EXPECT_EQ(0x32, mspPackage.requestBuffer[21]);
EXPECT_EQ(0x32, mspPackage.requestBuffer[22]);
EXPECT_EQ(0x4B, mspPackage.requestBuffer[23]);
EXPECT_EQ(0x28, mspPackage.requestBuffer[24]);
EXPECT_EQ(0x00, mspPackage.requestBuffer[25]);
//EXPECT_EQ(0xB3,checksum);
const crsfMspFrame_t *framePtr5 = (const crsfMspFrame_t*)crsfPidWrite5;
crsfFrame = *(const crsfFrame_t*)framePtr5;
crsfFrameDone = true;
uint8_t *frameStart5 = (uint8_t *)&crsfFrame.frame.payload + 2;
uint8_t *frameEnd5 = frameStart2 + CRSF_FRAME_RX_MSP_PAYLOAD_SIZE;
bool pending5 = handleMspFrame(frameStart5, frameEnd5);
EXPECT_TRUE(pending5); // not done yet
EXPECT_EQ(0x00, mspPackage.requestBuffer[26]);
EXPECT_EQ(0x37, mspPackage.requestBuffer[27]);
EXPECT_EQ(0x37, mspPackage.requestBuffer[28]);
EXPECT_EQ(0x4B, mspPackage.requestBuffer[29]);
EXPECT_EQ(0xF8,checksum);
}
void testSendMspResponse(uint8_t *payload) {
sbuf_t *plOut = sbufInit(&payloadOutputBuf, payloadOutput, payloadOutput + 64);
sbufWriteData(plOut, payload, *payload + 64);
sbufSwitchToReader(&payloadOutputBuf, payloadOutput);
}
TEST(CrossFireMSPTest, SendMspReply) {
initSharedMsp();
const crsfMspFrame_t *framePtr = (const crsfMspFrame_t*)crsfPidRequest;
crsfFrame = *(const crsfFrame_t*)framePtr;
crsfFrameDone = true;
uint8_t *frameStart = (uint8_t *)&crsfFrame.frame.payload + 2;
uint8_t *frameEnd = (uint8_t *)&crsfFrame.frame.payload + CRSF_FRAME_RX_MSP_PAYLOAD_SIZE + 2;
bool handled = handleMspFrame(frameStart, frameEnd);
EXPECT_TRUE(handled);
bool replyPending = sendMspReply(64, &testSendMspResponse);
EXPECT_FALSE(replyPending);
EXPECT_EQ(0x10, sbufReadU8(&payloadOutputBuf));
EXPECT_EQ(0x1E, sbufReadU8(&payloadOutputBuf));
for (unsigned int ii=1; ii<=30; ii++) {
EXPECT_EQ(ii, sbufReadU8(&payloadOutputBuf));
}
EXPECT_EQ(0x71, sbufReadU8(&payloadOutputBuf)); // CRC
}
// STUBS
extern "C" {
gpsSolutionData_t gpsSol;
attitudeEulerAngles_t attitude = { { 0, 0, 0 } };
uint32_t micros(void) {return dummyTimeUs;}
serialPort_t *openSerialPort(serialPortIdentifier_e, serialPortFunction_e, serialReceiveCallbackPtr, uint32_t, portMode_e, portOptions_e) {return NULL;}
serialPortConfig_t *findSerialPortConfig(serialPortFunction_e ) {return NULL;}
uint16_t getBatteryVoltage(void) {
return testBatteryVoltage;
}
int32_t getAmperage(void) {
return testAmperage;
}
uint8_t calculateBatteryPercentageRemaining(void) {
return 67;
}
bool feature(uint32_t) {return false;}
bool isAirmodeActive(void) {return true;}
mspResult_e mspFcProcessCommand(mspPacket_t *cmd, mspPacket_t *reply, mspPostProcessFnPtr *mspPostProcessFn) {
UNUSED(mspPostProcessFn);
sbuf_t *dst = &reply->buf;
//sbuf_t *src = &cmd->buf;
const uint8_t cmdMSP = cmd->cmd;
reply->cmd = cmd->cmd;
if (cmdMSP == 0x70) {
for (unsigned int ii=1; ii<=30; ii++) {
sbufWriteU8(dst, ii);
}
} else if (cmdMSP == 0xCA) {
return MSP_RESULT_ACK;
}
return MSP_RESULT_ACK;
}
void beeperConfirmationBeeps(uint8_t ) {}
int32_t getMAhDrawn(void) {
return testmAhDrawn;
}
}

View File

@ -31,6 +31,8 @@ extern "C" {
#include "common/filter.h" #include "common/filter.h"
#include "common/gps_conversion.h" #include "common/gps_conversion.h"
#include "common/maths.h" #include "common/maths.h"
#include "common/printf.h"
#include "common/typeconversion.h"
#include "config/parameter_group.h" #include "config/parameter_group.h"
#include "config/parameter_group_ids.h" #include "config/parameter_group_ids.h"
@ -38,6 +40,7 @@ extern "C" {
#include "drivers/serial.h" #include "drivers/serial.h"
#include "drivers/system.h" #include "drivers/system.h"
#include "fc/config.h"
#include "fc/runtime_config.h" #include "fc/runtime_config.h"
#include "flight/pid.h" #include "flight/pid.h"
@ -53,6 +56,7 @@ extern "C" {
#include "telemetry/crsf.h" #include "telemetry/crsf.h"
#include "telemetry/telemetry.h" #include "telemetry/telemetry.h"
#include "telemetry/msp_shared.h"
bool airMode; bool airMode;
@ -63,6 +67,7 @@ extern "C" {
serialPort_t *telemetrySharedPort; serialPort_t *telemetrySharedPort;
PG_REGISTER(batteryConfig_t, batteryConfig, PG_BATTERY_CONFIG, 0); PG_REGISTER(batteryConfig_t, batteryConfig, PG_BATTERY_CONFIG, 0);
PG_REGISTER(telemetryConfig_t, telemetryConfig, PG_TELEMETRY_CONFIG, 0); PG_REGISTER(telemetryConfig_t, telemetryConfig, PG_TELEMETRY_CONFIG, 0);
PG_REGISTER(systemConfig_t, systemConfig, PG_SYSTEM_CONFIG, 0);
} }
#include "unittest_macros.h" #include "unittest_macros.h"
@ -293,6 +298,7 @@ void serialWriteBuf(serialPort_t *, const uint8_t *, int) {}
void serialSetMode(serialPort_t *, portMode_e) {} void serialSetMode(serialPort_t *, portMode_e) {}
serialPort_t *openSerialPort(serialPortIdentifier_e, serialPortFunction_e, serialReceiveCallbackPtr, uint32_t, portMode_e, portOptions_e) {return NULL;} serialPort_t *openSerialPort(serialPortIdentifier_e, serialPortFunction_e, serialReceiveCallbackPtr, uint32_t, portMode_e, portOptions_e) {return NULL;}
void closeSerialPort(serialPort_t *) {} void closeSerialPort(serialPort_t *) {}
bool isSerialTransmitBufferEmpty(const serialPort_t *) { return true; }
serialPortConfig_t *findSerialPortConfig(serialPortFunction_e) {return NULL;} serialPortConfig_t *findSerialPortConfig(serialPortFunction_e) {return NULL;}
@ -323,4 +329,7 @@ int32_t getMAhDrawn(void){
return testmAhDrawn; return testmAhDrawn;
} }
bool sendMspReply(uint8_t, mspResponseFnPtr) { return false; }
bool handleMspFrame(uint8_t *, uint8_t *) { return false; }
} }