If RSSI Channel is set to Disabled when using S.Bus then generate RSS… (#5090)
[betaflight.git] / src / main / rx / fport.c
blobba483df0d036576a13c00d92161d6753cc43b33a
1 /*
2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
18 #include <stdbool.h>
19 #include <stdint.h>
20 #include <stdlib.h>
21 #include <string.h>
23 #include "platform.h"
25 #if defined(USE_SERIAL_RX)
27 #include "build/debug.h"
29 #include "common/maths.h"
30 #include "common/utils.h"
32 #include "drivers/time.h"
34 #include "io/serial.h"
36 #ifdef USE_TELEMETRY
37 #include "telemetry/telemetry.h"
38 #include "telemetry/smartport.h"
39 #endif
41 #include "rx/rx.h"
42 #include "rx/sbus_channels.h"
43 #include "rx/fport.h"
46 #define FPORT_TIME_NEEDED_PER_FRAME_US 3000
47 #define FPORT_MAX_TELEMETRY_RESPONSE_DELAY_US 2000
48 #define FPORT_MIN_TELEMETRY_RESPONSE_DELAY_US 500
49 #define FPORT_MAX_TELEMETRY_AGE_MS 500
52 #define FPORT_FRAME_MARKER 0x7E
54 #define FPORT_ESCAPE_CHAR 0x7D
55 #define FPORT_ESCAPE_MASK 0x20
57 #define FPORT_CRC_VALUE 0xFF
59 #define FPORT_BAUDRATE 115200
61 #define FPORT_PORT_OPTIONS (SERIAL_STOPBITS_1 | SERIAL_PARITY_NO)
63 enum {
64 DEBUG_FPORT_FRAME_INTERVAL = 0,
65 DEBUG_FPORT_FRAME_ERRORS,
66 DEBUG_FPORT_FRAME_LAST_ERROR,
67 DEBUG_FPORT_TELEMETRY_DELAY,
70 enum {
71 DEBUG_FPORT_NO_ERROR = 0,
72 DEBUG_FPORT_ERROR_TIMEOUT,
73 DEBUG_FPORT_ERROR_OVERSIZE,
74 DEBUG_FPORT_ERROR_SIZE,
75 DEBUG_FPORT_ERROR_CHECKSUM,
76 DEBUG_FPORT_ERROR_TYPE,
77 DEBUG_FPORT_ERROR_TYPE_SIZE,
80 enum {
81 FPORT_FRAME_TYPE_CONTROL = 0x00,
82 FPORT_FRAME_TYPE_TELEMETRY_REQUEST = 0x01,
83 FPORT_FRAME_TYPE_TELEMETRY_RESPONSE = 0x81,
87 enum {
88 FPORT_FRAME_ID_NULL = 0x00, // (master/slave)
89 FPORT_FRAME_ID_DATA = 0x10, // (master/slave)
90 FPORT_FRAME_ID_READ = 0x30, // (master)
91 FPORT_FRAME_ID_WRITE = 0x31, // (master)
92 FPORT_FRAME_ID_RESPONSE = 0x32, // (slave)
95 typedef struct fportControlData_s {
96 sbusChannels_t channels;
97 uint8_t rssi;
98 } fportControlData_t;
100 typedef union fportData_s {
101 fportControlData_t controlData;
102 smartPortPayload_t telemetryData;
103 } fportData_t;
105 typedef struct fportFrame_s {
106 uint8_t type;
107 fportData_t data;
108 } fportFrame_t;
110 static const smartPortPayload_t emptySmartPortFrame = { .frameId = 0, .valueId = 0, .data = 0 };
112 #define FPORT_REQUEST_FRAME_LENGTH sizeof(fportFrame_t)
113 #define FPORT_RESPONSE_FRAME_LENGTH (sizeof(uint8_t) + sizeof(smartPortPayload_t))
115 #define FPORT_FRAME_PAYLOAD_LENGTH_CONTROL (sizeof(uint8_t) + sizeof(fportControlData_t))
116 #define FPORT_FRAME_PAYLOAD_LENGTH_TELEMETRY_REQUEST (sizeof(uint8_t) + sizeof(smartPortPayload_t))
118 #define NUM_RX_BUFFERS 3
119 #define BUFFER_SIZE (FPORT_REQUEST_FRAME_LENGTH + 2 * sizeof(uint8_t))
121 typedef struct fportBuffer_s {
122 uint8_t data[BUFFER_SIZE];
123 uint8_t length;
124 } fportBuffer_t;
126 static fportBuffer_t rxBuffer[NUM_RX_BUFFERS];
128 static volatile uint8_t rxBufferWriteIndex = 0;
129 static volatile uint8_t rxBufferReadIndex = 0;
131 static volatile timeUs_t lastTelemetryFrameReceivedUs;
132 static volatile bool clearToSend = false;
134 static volatile uint8_t framePosition = 0;
136 static smartPortPayload_t *mspPayload = NULL;
137 static timeUs_t lastRcFrameReceivedMs = 0;
139 static serialPort_t *fportPort;
140 static bool telemetryEnabled = false;
142 static void reportFrameError(uint8_t errorReason) {
143 static volatile uint16_t frameErrors = 0;
145 DEBUG_SET(DEBUG_FPORT, DEBUG_FPORT_FRAME_ERRORS, ++frameErrors);
146 DEBUG_SET(DEBUG_FPORT, DEBUG_FPORT_FRAME_LAST_ERROR, errorReason);
149 // Receive ISR callback
150 static void fportDataReceive(uint16_t c, void *data)
152 UNUSED(data);
154 static timeUs_t frameStartAt = 0;
155 static bool escapedCharacter = false;
156 static timeUs_t lastFrameReceivedUs = 0;
157 static bool telemetryFrame = false;
159 const timeUs_t currentTimeUs = micros();
161 clearToSend = false;
163 if (framePosition > 1 && cmpTimeUs(currentTimeUs, frameStartAt) > FPORT_TIME_NEEDED_PER_FRAME_US + 500) {
164 reportFrameError(DEBUG_FPORT_ERROR_TIMEOUT);
166 framePosition = 0;
169 uint8_t val = (uint8_t)c;
171 if (val == FPORT_FRAME_MARKER) {
172 if (framePosition > 1) {
173 const uint8_t nextWriteIndex = (rxBufferWriteIndex + 1) % NUM_RX_BUFFERS;
174 if (nextWriteIndex != rxBufferReadIndex) {
175 rxBuffer[rxBufferWriteIndex].length = framePosition - 1;
176 rxBufferWriteIndex = nextWriteIndex;
179 if (telemetryFrame) {
180 clearToSend = true;
181 lastTelemetryFrameReceivedUs = currentTimeUs;
182 telemetryFrame = false;
185 DEBUG_SET(DEBUG_FPORT, DEBUG_FPORT_FRAME_INTERVAL, currentTimeUs - lastFrameReceivedUs);
186 lastFrameReceivedUs = currentTimeUs;
188 escapedCharacter = false;
191 frameStartAt = currentTimeUs;
192 framePosition = 1;
193 } else if (framePosition > 0) {
194 if (framePosition >= BUFFER_SIZE + 1) {
195 framePosition = 0;
197 reportFrameError(DEBUG_FPORT_ERROR_OVERSIZE);
198 } else {
199 if (escapedCharacter) {
200 val = val ^ FPORT_ESCAPE_MASK;
201 escapedCharacter = false;
202 } else if (val == FPORT_ESCAPE_CHAR) {
203 escapedCharacter = true;
205 return;
208 if (framePosition == 2 && val == FPORT_FRAME_TYPE_TELEMETRY_REQUEST) {
209 telemetryFrame = true;
212 rxBuffer[rxBufferWriteIndex].data[framePosition - 1] = val;
213 framePosition = framePosition + 1;
218 #if defined(USE_TELEMETRY_SMARTPORT)
219 static void smartPortWriteFrameFport(const smartPortPayload_t *payload)
221 framePosition = 0;
223 uint16_t checksum = 0;
224 smartPortSendByte(FPORT_RESPONSE_FRAME_LENGTH, &checksum, fportPort);
225 smartPortSendByte(FPORT_FRAME_TYPE_TELEMETRY_RESPONSE, &checksum, fportPort);
226 smartPortWriteFrameSerial(payload, fportPort, checksum);
228 #endif
230 static bool checkChecksum(uint8_t *data, uint8_t length)
232 uint16_t checksum = 0;
233 for (unsigned i = 0; i < length; i++) {
234 checksum = checksum + *(uint8_t *)(data + i);
237 checksum = (checksum & 0xff) + (checksum >> 8);
239 return checksum == FPORT_CRC_VALUE;
242 static uint8_t fportFrameStatus(rxRuntimeConfig_t *rxRuntimeConfig)
244 static smartPortPayload_t payloadBuffer;
245 static bool hasTelemetryRequest = false;
247 uint8_t result = RX_FRAME_PENDING;
249 if (rxBufferReadIndex != rxBufferWriteIndex) {
250 uint8_t bufferLength = rxBuffer[rxBufferReadIndex].length;
251 uint8_t frameLength = rxBuffer[rxBufferReadIndex].data[0];
252 if (frameLength != bufferLength - 2) {
253 reportFrameError(DEBUG_FPORT_ERROR_SIZE);
254 } else {
255 if (!checkChecksum(&rxBuffer[rxBufferReadIndex].data[0], bufferLength)) {
256 reportFrameError(DEBUG_FPORT_ERROR_CHECKSUM);
257 } else {
258 fportFrame_t *frame = (fportFrame_t *)&rxBuffer[rxBufferReadIndex].data[1];
260 switch (frame->type) {
261 case FPORT_FRAME_TYPE_CONTROL:
262 if (frameLength != FPORT_FRAME_PAYLOAD_LENGTH_CONTROL) {
263 reportFrameError(DEBUG_FPORT_ERROR_TYPE_SIZE);
264 } else {
265 result |= sbusChannelsDecode(rxRuntimeConfig, &frame->data.controlData.channels);
267 setRssiUnfiltered(scaleRange(constrain(frame->data.controlData.rssi, 0, 100), 0, 100, 0, 1024), RSSI_SOURCE_RX_PROTOCOL);
269 lastRcFrameReceivedMs = millis();
272 break;
273 case FPORT_FRAME_TYPE_TELEMETRY_REQUEST:
274 if (frameLength != FPORT_FRAME_PAYLOAD_LENGTH_TELEMETRY_REQUEST) {
275 reportFrameError(DEBUG_FPORT_ERROR_TYPE_SIZE);
276 } else {
277 #if defined(USE_TELEMETRY_SMARTPORT)
278 if (!telemetryEnabled) {
279 break;
282 switch(frame->data.telemetryData.frameId) {
283 case FPORT_FRAME_ID_NULL:
284 case FPORT_FRAME_ID_DATA: // never used
285 hasTelemetryRequest = true;
287 break;
288 case FPORT_FRAME_ID_READ:
289 case FPORT_FRAME_ID_WRITE: // never used
290 memcpy(&payloadBuffer, &frame->data.telemetryData, sizeof(smartPortPayload_t));
291 mspPayload = &payloadBuffer;
293 break;
294 default:
296 break;
298 #endif
301 break;
302 default:
303 reportFrameError(DEBUG_FPORT_ERROR_TYPE);
305 break;
311 rxBufferReadIndex = (rxBufferReadIndex + 1) % NUM_RX_BUFFERS;
314 if ((mspPayload || hasTelemetryRequest) && cmpTimeUs(micros(), lastTelemetryFrameReceivedUs) >= FPORT_MIN_TELEMETRY_RESPONSE_DELAY_US) {
315 hasTelemetryRequest = false;
317 result = result | RX_FRAME_PROCESSING_REQUIRED;
320 if (lastRcFrameReceivedMs && ((millis() - lastRcFrameReceivedMs) > FPORT_MAX_TELEMETRY_AGE_MS)) {
321 setRssiFiltered(0, RSSI_SOURCE_RX_PROTOCOL);
322 lastRcFrameReceivedMs = 0;
325 return result;
328 static bool fportProcessFrame(const rxRuntimeConfig_t *rxRuntimeConfig)
330 UNUSED(rxRuntimeConfig);
332 #if defined(USE_TELEMETRY_SMARTPORT)
333 timeUs_t currentTimeUs = micros();
334 if (cmpTimeUs(currentTimeUs, lastTelemetryFrameReceivedUs) > FPORT_MAX_TELEMETRY_RESPONSE_DELAY_US) {
335 clearToSend = false;
338 if (clearToSend) {
339 DEBUG_SET(DEBUG_FPORT, DEBUG_FPORT_TELEMETRY_DELAY, currentTimeUs - lastTelemetryFrameReceivedUs);
341 processSmartPortTelemetry(mspPayload, &clearToSend, NULL);
343 if (clearToSend) {
344 smartPortWriteFrameFport(&emptySmartPortFrame);
346 clearToSend = false;
350 mspPayload = NULL;
351 #endif
353 return true;
356 bool fportRxInit(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig)
358 static uint16_t sbusChannelData[SBUS_MAX_CHANNEL];
359 rxRuntimeConfig->channelData = sbusChannelData;
360 sbusChannelsInit(rxConfig, rxRuntimeConfig);
362 rxRuntimeConfig->channelCount = SBUS_MAX_CHANNEL;
363 rxRuntimeConfig->rxRefreshRate = 11000;
365 rxRuntimeConfig->rcFrameStatusFn = fportFrameStatus;
366 rxRuntimeConfig->rcProcessFrameFn = fportProcessFrame;
368 const serialPortConfig_t *portConfig = findSerialPortConfig(FUNCTION_RX_SERIAL);
369 if (!portConfig) {
370 return false;
373 fportPort = openSerialPort(portConfig->identifier,
374 FUNCTION_RX_SERIAL,
375 fportDataReceive,
376 NULL,
377 FPORT_BAUDRATE,
378 MODE_RXTX,
379 FPORT_PORT_OPTIONS | (rxConfig->serialrx_inverted ? SERIAL_INVERTED : 0) | (rxConfig->halfDuplex ? SERIAL_BIDIR : 0)
382 if (fportPort) {
383 #if defined(USE_TELEMETRY_SMARTPORT)
384 telemetryEnabled = initSmartPortTelemetryExternal(smartPortWriteFrameFport);
385 #endif
387 if (rssiSource == RSSI_SOURCE_NONE) {
388 rssiSource = RSSI_SOURCE_RX_PROTOCOL;
392 return fportPort != NULL;
394 #endif