1
0
Fork 0
mirror of https://github.com/betaflight/betaflight.git synced 2025-07-16 04:45:24 +03:00
betaflight/src/main/rx/expresslrs_telemetry.c
2022-09-07 14:35:00 +02:00

376 lines
11 KiB
C

/*
* This file is part of Cleanflight and Betaflight.
*
* Cleanflight and Betaflight are free software. You can redistribute
* this software and/or modify this software 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 and Betaflight are distributed in the hope that they
* 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 this software.
*
* If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Based on https://github.com/ExpressLRS/ExpressLRS
* Thanks to AlessandroAU, original creator of the ExpressLRS project.
*
* Authors:
* Phobos- - Original port.
*/
#include <string.h>
#include "platform.h"
#ifdef USE_RX_EXPRESSLRS
#include "common/maths.h"
#include "config/feature.h"
#include "fc/runtime_config.h"
#include "msp/msp_protocol.h"
#include "rx/crsf_protocol.h"
#include "rx/expresslrs_telemetry.h"
#include "telemetry/crsf.h"
#include "telemetry/telemetry.h"
#include "sensors/battery.h"
#include "sensors/sensors.h"
static uint8_t tlmBuffer[CRSF_FRAME_SIZE_MAX];
typedef enum {
CRSF_FRAME_GPS_INDEX = 0,
CRSF_FRAME_BATTERY_SENSOR_INDEX,
CRSF_FRAME_ATTITUDE_INDEX,
CRSF_FRAME_FLIGHT_MODE_INDEX,
CRSF_FRAME_PAYLOAD_TYPES_COUNT //should be last
} frameTypeIndex_e;
static crsfFrameType_e payloadTypes[] = {
CRSF_FRAMETYPE_GPS,
CRSF_FRAMETYPE_BATTERY_SENSOR,
CRSF_FRAMETYPE_ATTITUDE,
CRSF_FRAMETYPE_FLIGHT_MODE
};
STATIC_UNIT_TESTED uint8_t tlmSensors = 0;
STATIC_UNIT_TESTED uint8_t currentPayloadIndex;
static uint8_t *data = NULL;
static uint8_t length = 0;
static uint8_t currentOffset;
static uint8_t bytesLastPayload;
static uint8_t currentPackage;
static bool waitUntilTelemetryConfirm;
static uint16_t waitCount;
static uint16_t maxWaitCount;
static volatile stubbornSenderState_e senderState;
static void telemetrySenderResetState(void)
{
bytesLastPayload = 0;
currentOffset = 0;
currentPackage = 1;
waitUntilTelemetryConfirm = true;
waitCount = 0;
// 80 corresponds to UpdateTelemetryRate(ANY, 2, 1), which is what the TX uses in boost mode
maxWaitCount = 80;
senderState = ELRS_SENDER_IDLE;
}
/***
* Queues a message to send, will abort the current message if one is currently being transmitted
***/
void setTelemetryDataToTransmit(const uint8_t lengthToTransmit, uint8_t* dataToTransmit)
{
length = lengthToTransmit;
data = dataToTransmit;
currentOffset = 0;
currentPackage = 1;
waitCount = 0;
senderState = (senderState == ELRS_SENDER_IDLE) ? ELRS_SENDING : ELRS_RESYNC_THEN_SEND;
}
bool isTelemetrySenderActive(void)
{
return senderState != ELRS_SENDER_IDLE;
}
/***
* Copy up to maxLen bytes from the current package to outData
* packageIndex
***/
uint8_t getCurrentTelemetryPayload(uint8_t *outData)
{
uint8_t packageIndex;
bytesLastPayload = 0;
switch (senderState) {
case ELRS_RESYNC:
case ELRS_RESYNC_THEN_SEND:
packageIndex = ELRS_TELEMETRY_MAX_PACKAGES;
break;
case ELRS_SENDING:
bytesLastPayload = MIN((uint8_t)(length - currentOffset), ELRS_TELEMETRY_BYTES_PER_CALL);
// If this is the last data chunk, and there has been at least one other packet
// skip the blank packet needed for WAIT_UNTIL_NEXT_CONFIRM
if (currentPackage > 1 && (currentOffset + bytesLastPayload) >= length) {
packageIndex = 0;
} else {
packageIndex = currentPackage;
}
memcpy(outData, &data[currentOffset], bytesLastPayload);
break;
default:
packageIndex = 0;
}
return packageIndex;
}
void confirmCurrentTelemetryPayload(const bool telemetryConfirmValue)
{
stubbornSenderState_e nextSenderState = senderState;
switch (senderState) {
case ELRS_SENDING:
if (telemetryConfirmValue != waitUntilTelemetryConfirm) {
waitCount++;
if (waitCount > maxWaitCount) {
waitUntilTelemetryConfirm = !telemetryConfirmValue;
nextSenderState = ELRS_RESYNC;
}
break;
}
currentOffset += bytesLastPayload;
if (currentOffset >= length) {
// A 0th packet is always requred so the reciver can
// differentiate a new send from a resend, if this is
// the first packet acked, send another, else IDLE
if (currentPackage == 1) {
nextSenderState = ELRS_WAIT_UNTIL_NEXT_CONFIRM;
} else {
nextSenderState = ELRS_SENDER_IDLE;
}
}
currentPackage++;
waitUntilTelemetryConfirm = !waitUntilTelemetryConfirm;
waitCount = 0;
break;
case ELRS_RESYNC:
case ELRS_RESYNC_THEN_SEND:
case ELRS_WAIT_UNTIL_NEXT_CONFIRM:
if (telemetryConfirmValue == waitUntilTelemetryConfirm) {
nextSenderState = (senderState == ELRS_RESYNC_THEN_SEND) ? ELRS_SENDING : ELRS_SENDER_IDLE;
waitUntilTelemetryConfirm = !telemetryConfirmValue;
} else if (senderState == ELRS_WAIT_UNTIL_NEXT_CONFIRM) { // switch to resync if tx does not confirm value fast enough
waitCount++;
if (waitCount > maxWaitCount) {
waitUntilTelemetryConfirm = !telemetryConfirmValue;
nextSenderState = ELRS_RESYNC;
}
}
break;
case ELRS_SENDER_IDLE:
break;
}
senderState = nextSenderState;
}
#ifdef USE_MSP_OVER_TELEMETRY
static uint8_t *mspData = NULL;
static volatile bool finishedData;
static volatile uint8_t mspLength = 0;
static volatile uint8_t mspCurrentOffset;
static volatile uint8_t mspCurrentPackage;
static volatile bool mspConfirm;
STATIC_UNIT_TESTED volatile bool mspReplyPending;
STATIC_UNIT_TESTED volatile bool deviceInfoReplyPending;
void mspReceiverResetState(void) {
mspCurrentOffset = 0;
mspCurrentPackage = 1;
mspConfirm = false;
mspReplyPending = false;
deviceInfoReplyPending = false;
}
bool getCurrentMspConfirm(void)
{
return mspConfirm;
}
void setMspDataToReceive(const uint8_t maxLength, uint8_t* dataToReceive)
{
mspLength = maxLength;
mspData = dataToReceive;
mspCurrentPackage = 1;
mspCurrentOffset = 0;
finishedData = false;
}
void receiveMspData(const uint8_t packageIndex, const volatile uint8_t* const receiveData)
{
// Resync
if (packageIndex == ELRS_MSP_MAX_PACKAGES) {
mspConfirm = !mspConfirm;
mspCurrentPackage = 1;
mspCurrentOffset = 0;
finishedData = false;
return;
}
if (finishedData) {
return;
}
bool acceptData = false;
if (packageIndex == 0 && mspCurrentPackage > 1) {
// PackageIndex 0 (the final packet) can also contain data
acceptData = true;
finishedData = true;
} else if (packageIndex == mspCurrentPackage) {
acceptData = true;
mspCurrentPackage++;
}
if (acceptData && (receiveData != NULL)) {
uint8_t len = MIN((uint8_t)(mspLength - mspCurrentOffset), ELRS_MSP_BYTES_PER_CALL);
memcpy(&mspData[mspCurrentOffset], (const uint8_t*) receiveData, len);
mspCurrentOffset += len;
mspConfirm = !mspConfirm;
}
}
bool hasFinishedMspData(void)
{
return finishedData;
}
void mspReceiverUnlock(void)
{
if (finishedData) {
mspCurrentPackage = 1;
mspCurrentOffset = 0;
finishedData = false;
}
}
static uint8_t mspFrameSize = 0;
static void bufferMspResponse(uint8_t *payload, const uint8_t payloadSize)
{
mspFrameSize = getCrsfMspFrame(tlmBuffer, payload, payloadSize);
}
void processMspPacket(uint8_t *packet)
{
switch (packet[2]) {
case CRSF_FRAMETYPE_DEVICE_PING:
deviceInfoReplyPending = true;
break;
case CRSF_FRAMETYPE_MSP_REQ:
FALLTHROUGH;
case CRSF_FRAMETYPE_MSP_WRITE:
if (bufferCrsfMspFrame(&packet[ELRS_MSP_PACKET_OFFSET], CRSF_FRAME_RX_MSP_FRAME_SIZE)) {
handleCrsfMspFrameBuffer(&bufferMspResponse);
mspReplyPending = true;
}
break;
default:
break;
}
}
#endif
/*
* Called when the telemetry ratio or air rate changes, calculate
* the new threshold for how many times the telemetryConfirmValue
* can be wrong in a row before giving up and going to RESYNC
*/
void updateTelemetryRate(const uint16_t airRate, const uint8_t tlmRatio, const uint8_t tlmBurst)
{
// consipicuously unused airRate parameter, the wait count is strictly based on number
// of packets, not time between the telemetry packets, or a wall clock timeout
UNUSED(airRate);
// The expected number of packet periods between telemetry packets
uint32_t packsBetween = tlmRatio * (1 + tlmBurst) / tlmBurst;
maxWaitCount = packsBetween * ELRS_TELEMETRY_MAX_MISSED_PACKETS;
}
void initTelemetry(void)
{
if (!featureIsEnabled(FEATURE_TELEMETRY)) {
return;
}
if (sensors(SENSOR_ACC) && telemetryIsSensorEnabled(SENSOR_PITCH | SENSOR_ROLL | SENSOR_HEADING)) {
tlmSensors |= BIT(CRSF_FRAME_ATTITUDE_INDEX);
}
if ((isBatteryVoltageConfigured() && telemetryIsSensorEnabled(SENSOR_VOLTAGE))
|| (isAmperageConfigured() && telemetryIsSensorEnabled(SENSOR_CURRENT | SENSOR_FUEL))) {
tlmSensors |= BIT(CRSF_FRAME_BATTERY_SENSOR_INDEX);
}
if (telemetryIsSensorEnabled(SENSOR_MODE)) {
tlmSensors |= BIT(CRSF_FRAME_FLIGHT_MODE_INDEX);
}
#ifdef USE_GPS
if (featureIsEnabled(FEATURE_GPS)
&& telemetryIsSensorEnabled(SENSOR_ALTITUDE | SENSOR_LAT_LONG | SENSOR_GROUND_SPEED | SENSOR_HEADING)) {
tlmSensors |= BIT(CRSF_FRAME_GPS_INDEX);
}
#endif
telemetrySenderResetState();
#ifdef USE_MSP_OVER_TELEMETRY
mspReceiverResetState();
#endif
}
bool getNextTelemetryPayload(uint8_t *nextPayloadSize, uint8_t **payloadData)
{
#ifdef USE_MSP_OVER_TELEMETRY
if (deviceInfoReplyPending) {
*nextPayloadSize = getCrsfFrame(tlmBuffer, CRSF_FRAMETYPE_DEVICE_INFO);
*payloadData = tlmBuffer;
deviceInfoReplyPending = false;
return true;
} else if (mspReplyPending) {
*nextPayloadSize = mspFrameSize;
*payloadData = tlmBuffer;
mspReplyPending = false;
return true;
} else
#endif
if (tlmSensors & BIT(currentPayloadIndex)) {
*nextPayloadSize = getCrsfFrame(tlmBuffer, payloadTypes[currentPayloadIndex]);
*payloadData = tlmBuffer;
currentPayloadIndex = (currentPayloadIndex + 1) % CRSF_FRAME_PAYLOAD_TYPES_COUNT;
return true;
} else {
currentPayloadIndex = (currentPayloadIndex + 1) % CRSF_FRAME_PAYLOAD_TYPES_COUNT;
*nextPayloadSize = 0;
*payloadData = 0;
return false;
}
}
#endif