Convert direct reference to string and freq table to vtxCommonXXX services
[betaflight.git] / src / main / io / vtx_tramp.c
blob5dabda3c20c6641e0f411bbd9115c67e742e1f2f
1 /*
2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
8 * any later version.
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
21 /* Created by jflyper */
23 #include <stdbool.h>
24 #include <stdint.h>
25 #include <ctype.h>
26 #include <string.h>
28 #include "platform.h"
30 #if defined(USE_VTX_TRAMP) && defined(USE_VTX_CONTROL)
32 #include "build/debug.h"
34 #include "common/maths.h"
35 #include "common/utils.h"
37 #include "cms/cms_menu_vtx_tramp.h"
39 #include "drivers/vtx_common.h"
41 #include "io/serial.h"
42 #include "io/vtx_tramp.h"
43 #include "io/vtx_control.h"
44 #include "io/vtx.h"
45 #include "io/vtx_string.h"
47 #if defined(USE_CMS) || defined(USE_VTX_COMMON)
48 const uint16_t trampPowerTable[VTX_TRAMP_POWER_COUNT] = {
49 25, 100, 200, 400, 600
52 const char * trampPowerNames[VTX_TRAMP_POWER_COUNT+1] = {
53 "---", "25 ", "100", "200", "400", "600"
55 #endif
57 #if defined(USE_VTX_COMMON)
58 static const vtxVTable_t trampVTable; // forward
59 static vtxDevice_t vtxTramp = {
60 .vTable = &trampVTable,
62 #endif
64 static serialPort_t *trampSerialPort = NULL;
66 static uint8_t trampReqBuffer[16];
67 static uint8_t trampRespBuffer[16];
69 typedef enum {
70 TRAMP_STATUS_BAD_DEVICE = -1,
71 TRAMP_STATUS_OFFLINE = 0,
72 TRAMP_STATUS_ONLINE,
73 TRAMP_STATUS_SET_FREQ_PW,
74 TRAMP_STATUS_CHECK_FREQ_PW
75 } trampStatus_e;
77 trampStatus_e trampStatus = TRAMP_STATUS_OFFLINE;
79 uint32_t trampRFFreqMin;
80 uint32_t trampRFFreqMax;
81 uint32_t trampRFPowerMax;
83 bool trampSetByFreqFlag = false; //false = set via band/channel
84 uint32_t trampCurFreq = 0;
85 uint8_t trampBand = 0;
86 uint8_t trampChannel = 0;
87 uint16_t trampPower = 0; // Actual transmitting power
88 uint16_t trampConfiguredPower = 0; // Configured transmitting power
89 int16_t trampTemperature = 0;
90 uint8_t trampPitMode = 0;
92 // Maximum number of requests sent to try a config change
93 #define TRAMP_MAX_RETRIES 2
95 uint32_t trampConfFreq = 0;
96 uint8_t trampFreqRetries = 0;
98 uint16_t trampConfPower = 0;
99 uint8_t trampPowerRetries = 0;
101 static void trampWriteBuf(uint8_t *buf)
103 serialWriteBuf(trampSerialPort, buf, 16);
106 static uint8_t trampChecksum(uint8_t *trampBuf)
108 uint8_t cksum = 0;
110 for (int i = 1 ; i < 14 ; i++) {
111 cksum += trampBuf[i];
114 return cksum;
117 static void trampCmdU16(uint8_t cmd, uint16_t param)
119 if (!trampSerialPort || IS_RC_MODE_ACTIVE(BOXVTXCONTROLDISABLE)) {
120 return;
123 memset(trampReqBuffer, 0, ARRAYLEN(trampReqBuffer));
124 trampReqBuffer[0] = 15;
125 trampReqBuffer[1] = cmd;
126 trampReqBuffer[2] = param & 0xff;
127 trampReqBuffer[3] = (param >> 8) & 0xff;
128 trampReqBuffer[14] = trampChecksum(trampReqBuffer);
129 trampWriteBuf(trampReqBuffer);
132 static bool trampValidateFreq(uint16_t freq)
134 return (freq >= VTX_TRAMP_MIN_FREQUENCY_MHZ && freq <= VTX_TRAMP_MAX_FREQUENCY_MHZ);
137 static void trampDevSetFreq(uint16_t freq)
139 trampConfFreq = freq;
140 if (trampConfFreq != trampCurFreq) {
141 trampFreqRetries = TRAMP_MAX_RETRIES;
145 void trampSetFreq(uint16_t freq)
147 trampSetByFreqFlag = true; //set freq via MHz value
148 trampDevSetFreq(freq);
151 void trampSendFreq(uint16_t freq)
153 trampCmdU16('F', freq);
156 static bool trampValidateBandAndChannel(uint8_t band, uint8_t channel)
158 return (band >= VTX_TRAMP_MIN_BAND && band <= VTX_TRAMP_MAX_BAND &&
159 channel >= VTX_TRAMP_MIN_CHANNEL && channel <= VTX_TRAMP_MAX_CHANNEL);
162 static void trampDevSetBandAndChannel(uint8_t band, uint8_t channel)
164 trampDevSetFreq(vtxCommonLookupFrequency(vtxCommonDevice(), band, channel));
167 void trampSetBandAndChannel(uint8_t band, uint8_t channel)
169 trampSetByFreqFlag = false; //set freq via band/channel
170 trampDevSetBandAndChannel(band, channel);
173 void trampSetRFPower(uint16_t level)
175 trampConfPower = level;
176 if (trampConfPower != trampPower) {
177 trampPowerRetries = TRAMP_MAX_RETRIES;
181 void trampSendRFPower(uint16_t level)
183 trampCmdU16('P', level);
186 // return false if error
187 bool trampCommitChanges(void)
189 if (trampStatus != TRAMP_STATUS_ONLINE) {
190 return false;
193 trampStatus = TRAMP_STATUS_SET_FREQ_PW;
194 return true;
197 // return false if index out of range
198 static bool trampDevSetPowerByIndex(uint8_t index)
200 if (index > 0 && index <= sizeof(trampPowerTable)) {
201 trampSetRFPower(trampPowerTable[index - 1]);
202 trampCommitChanges();
203 return true;
205 return false;
208 void trampSetPitMode(uint8_t onoff)
210 trampCmdU16('I', onoff ? 0 : 1);
213 // returns completed response code
214 static char trampHandleResponse(void)
216 const uint8_t respCode = trampRespBuffer[1];
218 switch (respCode) {
219 case 'r':
221 const uint16_t min_freq = trampRespBuffer[2]|(trampRespBuffer[3] << 8);
222 if (min_freq != 0) {
223 trampRFFreqMin = min_freq;
224 trampRFFreqMax = trampRespBuffer[4]|(trampRespBuffer[5] << 8);
225 trampRFPowerMax = trampRespBuffer[6]|(trampRespBuffer[7] << 8);
226 return 'r';
229 // throw bytes echoed from tx to rx in bidirectional mode away
231 break;
233 case 'v':
235 const uint16_t freq = trampRespBuffer[2]|(trampRespBuffer[3] << 8);
236 if (freq != 0) {
237 trampCurFreq = freq;
238 trampConfiguredPower = trampRespBuffer[4]|(trampRespBuffer[5] << 8);
239 trampPitMode = trampRespBuffer[7];
240 trampPower = trampRespBuffer[8]|(trampRespBuffer[9] << 8);
242 // if no band/chan match then make sure set-by-freq mode is flagged
243 if (!vtxCommonLookupBandChan(vtxCommonDevice(), trampCurFreq, &trampBand, &trampChannel)) {
244 trampSetByFreqFlag = true;
247 if (trampConfFreq == 0) trampConfFreq = trampCurFreq;
248 if (trampConfPower == 0) trampConfPower = trampPower;
249 return 'v';
252 // throw bytes echoed from tx to rx in bidirectional mode away
254 break;
256 case 's':
258 const uint16_t temp = (int16_t)(trampRespBuffer[6]|(trampRespBuffer[7] << 8));
259 if (temp != 0) {
260 trampTemperature = temp;
261 return 's';
264 break;
267 return 0;
270 typedef enum {
271 S_WAIT_LEN = 0, // Waiting for a packet len
272 S_WAIT_CODE, // Waiting for a response code
273 S_DATA, // Waiting for rest of the packet.
274 } trampReceiveState_e;
276 static trampReceiveState_e trampReceiveState = S_WAIT_LEN;
277 static int trampReceivePos = 0;
279 static void trampResetReceiver(void)
281 trampReceiveState = S_WAIT_LEN;
282 trampReceivePos = 0;
285 static bool trampIsValidResponseCode(uint8_t code)
287 if (code == 'r' || code == 'v' || code == 's') {
288 return true;
289 } else {
290 return false;
294 // returns completed response code or 0
295 static char trampReceive(uint32_t currentTimeUs)
297 UNUSED(currentTimeUs);
299 if (!trampSerialPort) {
300 return 0;
303 while (serialRxBytesWaiting(trampSerialPort)) {
304 const uint8_t c = serialRead(trampSerialPort);
305 trampRespBuffer[trampReceivePos++] = c;
307 switch (trampReceiveState) {
308 case S_WAIT_LEN:
309 if (c == 0x0F) {
310 trampReceiveState = S_WAIT_CODE;
311 } else {
312 trampReceivePos = 0;
314 break;
316 case S_WAIT_CODE:
317 if (trampIsValidResponseCode(c)) {
318 trampReceiveState = S_DATA;
319 } else {
320 trampResetReceiver();
322 break;
324 case S_DATA:
325 if (trampReceivePos == 16) {
326 uint8_t cksum = trampChecksum(trampRespBuffer);
328 trampResetReceiver();
330 if ((trampRespBuffer[14] == cksum) && (trampRespBuffer[15] == 0) && !IS_RC_MODE_ACTIVE(BOXVTXCONTROLDISABLE)) {
331 return trampHandleResponse();
334 break;
336 default:
337 trampResetReceiver();
338 break;
342 return 0;
345 void trampQuery(uint8_t cmd)
347 trampResetReceiver();
348 trampCmdU16(cmd, 0);
351 void trampQueryR(void)
353 trampQuery('r');
356 void trampQueryV(void)
358 trampQuery('v');
361 void trampQueryS(void)
363 trampQuery('s');
366 static void vtxTrampProcess(vtxDevice_t *vtxDevice, timeUs_t currentTimeUs)
368 UNUSED(vtxDevice);
370 static timeUs_t lastQueryTimeUs = 0;
371 static bool initSettingsDoneFlag = false;
373 #ifdef TRAMP_DEBUG
374 static uint16_t debugFreqReqCounter = 0;
375 static uint16_t debugPowReqCounter = 0;
376 #endif
378 if (trampStatus == TRAMP_STATUS_BAD_DEVICE) {
379 return;
382 const char replyCode = trampReceive(currentTimeUs);
384 #ifdef TRAMP_DEBUG
385 debug[0] = trampStatus;
386 #endif
388 switch (replyCode) {
389 case 'r':
390 if (trampStatus <= TRAMP_STATUS_OFFLINE) {
391 trampStatus = TRAMP_STATUS_ONLINE;
393 // once device is ready enter vtx settings
394 if (!initSettingsDoneFlag) {
395 initSettingsDoneFlag = true;
396 // if vtx_band!=0 then enter 'vtx_band/chan' values (and power)
399 break;
401 case 'v':
402 if (trampStatus == TRAMP_STATUS_CHECK_FREQ_PW) {
403 trampStatus = TRAMP_STATUS_SET_FREQ_PW;
405 break;
408 switch (trampStatus) {
410 case TRAMP_STATUS_OFFLINE:
411 case TRAMP_STATUS_ONLINE:
412 if (cmp32(currentTimeUs, lastQueryTimeUs) > 1000 * 1000) { // 1s
414 if (trampStatus == TRAMP_STATUS_OFFLINE) {
415 trampQueryR();
416 } else {
417 static unsigned int cnt = 0;
418 if (((cnt++) & 1) == 0) {
419 trampQueryV();
420 } else {
421 trampQueryS();
425 lastQueryTimeUs = currentTimeUs;
427 break;
429 case TRAMP_STATUS_SET_FREQ_PW:
431 bool done = true;
432 if (trampConfFreq && trampFreqRetries && (trampConfFreq != trampCurFreq)) {
433 trampSendFreq(trampConfFreq);
434 trampFreqRetries--;
435 #ifdef TRAMP_DEBUG
436 debugFreqReqCounter++;
437 #endif
438 done = false;
439 } else if (trampConfPower && trampPowerRetries && (trampConfPower != trampConfiguredPower)) {
440 trampSendRFPower(trampConfPower);
441 trampPowerRetries--;
442 #ifdef TRAMP_DEBUG
443 debugPowReqCounter++;
444 #endif
445 done = false;
448 if (!done) {
449 trampStatus = TRAMP_STATUS_CHECK_FREQ_PW;
451 // delay next status query by 300ms
452 lastQueryTimeUs = currentTimeUs + 300 * 1000;
453 } else {
454 // everything has been done, let's return to original state
455 trampStatus = TRAMP_STATUS_ONLINE;
456 // reset configuration value in case it failed (no more retries)
457 trampConfFreq = trampCurFreq;
458 trampConfPower = trampPower;
459 trampFreqRetries = trampPowerRetries = 0;
462 break;
464 case TRAMP_STATUS_CHECK_FREQ_PW:
465 if (cmp32(currentTimeUs, lastQueryTimeUs) > 200 * 1000) {
466 trampQueryV();
467 lastQueryTimeUs = currentTimeUs;
469 break;
471 default:
472 break;
475 #ifdef TRAMP_DEBUG
476 debug[1] = debugFreqReqCounter;
477 debug[2] = debugPowReqCounter;
478 debug[3] = 0;
479 #endif
481 #ifdef USE_CMS
482 trampCmsUpdateStatusString();
483 #endif
487 #ifdef USE_VTX_COMMON
489 // Interface to common VTX API
491 static vtxDevType_e vtxTrampGetDeviceType(const vtxDevice_t *vtxDevice)
493 UNUSED(vtxDevice);
494 return VTXDEV_TRAMP;
497 static bool vtxTrampIsReady(const vtxDevice_t *vtxDevice)
499 return vtxDevice!=NULL && trampStatus > TRAMP_STATUS_OFFLINE;
502 static void vtxTrampSetBandAndChannel(vtxDevice_t *vtxDevice, uint8_t band, uint8_t channel)
504 UNUSED(vtxDevice);
505 if (trampValidateBandAndChannel(band, channel)) {
506 trampSetBandAndChannel(band, channel);
507 trampCommitChanges();
511 static void vtxTrampSetPowerByIndex(vtxDevice_t *vtxDevice, uint8_t index)
513 UNUSED(vtxDevice);
514 trampDevSetPowerByIndex(index);
517 static void vtxTrampSetPitMode(vtxDevice_t *vtxDevice, uint8_t onoff)
519 UNUSED(vtxDevice);
520 trampSetPitMode(onoff);
523 static void vtxTrampSetFreq(vtxDevice_t *vtxDevice, uint16_t freq)
525 UNUSED(vtxDevice);
526 if (trampValidateFreq(freq)) {
527 trampSetFreq(freq);
528 trampCommitChanges();
532 static bool vtxTrampGetBandAndChannel(const vtxDevice_t *vtxDevice, uint8_t *pBand, uint8_t *pChannel)
534 if (!vtxTrampIsReady(vtxDevice)) {
535 return false;
538 // if in user-freq mode then report band as zero
539 *pBand = trampSetByFreqFlag ? 0 : trampBand;
540 *pChannel = trampChannel;
541 return true;
544 static bool vtxTrampGetPowerIndex(const vtxDevice_t *vtxDevice, uint8_t *pIndex)
546 if (!vtxTrampIsReady(vtxDevice)) {
547 return false;
550 if (trampConfiguredPower > 0) {
551 for (uint8_t i = 0; i < sizeof(trampPowerTable); i++) {
552 if (trampConfiguredPower <= trampPowerTable[i]) {
553 *pIndex = i + 1;
554 break;
559 return true;
562 static bool vtxTrampGetPitMode(const vtxDevice_t *vtxDevice, uint8_t *pOnOff)
564 if (!vtxTrampIsReady(vtxDevice)) {
565 return false;
568 *pOnOff = trampPitMode;
569 return true;
572 static bool vtxTrampGetFreq(const vtxDevice_t *vtxDevice, uint16_t *pFreq)
574 if (!vtxTrampIsReady(vtxDevice)) {
575 return false;
578 *pFreq = trampCurFreq;
579 return true;
582 static const vtxVTable_t trampVTable = {
583 .process = vtxTrampProcess,
584 .getDeviceType = vtxTrampGetDeviceType,
585 .isReady = vtxTrampIsReady,
586 .setBandAndChannel = vtxTrampSetBandAndChannel,
587 .setPowerByIndex = vtxTrampSetPowerByIndex,
588 .setPitMode = vtxTrampSetPitMode,
589 .setFrequency = vtxTrampSetFreq,
590 .getBandAndChannel = vtxTrampGetBandAndChannel,
591 .getPowerIndex = vtxTrampGetPowerIndex,
592 .getPitMode = vtxTrampGetPitMode,
593 .getFrequency = vtxTrampGetFreq,
596 #endif
598 bool vtxTrampInit(void)
600 serialPortConfig_t *portConfig = findSerialPortConfig(FUNCTION_VTX_TRAMP);
602 if (portConfig) {
603 portOptions_e portOptions = 0;
604 #if defined(USE_VTX_COMMON)
605 portOptions = portOptions | (vtxConfig()->halfDuplex ? SERIAL_BIDIR : SERIAL_UNIDIR);
606 #else
607 portOptions = SERIAL_BIDIR;
608 #endif
610 trampSerialPort = openSerialPort(portConfig->identifier, FUNCTION_VTX_TRAMP, NULL, NULL, 9600, MODE_RXTX, portOptions);
613 if (!trampSerialPort) {
614 return false;
617 #if defined(USE_VTX_COMMON)
618 vtxTramp.capability.bandCount = VTX_TRAMP_BAND_COUNT;
619 vtxTramp.capability.channelCount = VTX_TRAMP_CHANNEL_COUNT;
620 vtxTramp.capability.powerCount = sizeof(trampPowerTable),
621 vtxTramp.frequencyTable = vtxStringFrequencyTable();
622 vtxTramp.bandNames = vtxStringBandNames();
623 vtxTramp.bandLetters = vtxStringBandLetters();
624 vtxTramp.channelNames = vtxStringChannelNames();
625 vtxTramp.powerNames = trampPowerNames;
626 vtxTramp.powerValues = trampPowerTable;
628 vtxCommonSetDevice(&vtxTramp);
629 #endif
631 return true;
634 #endif // VTX_TRAMP