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)
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 */
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"
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"
57 #if defined(USE_VTX_COMMON)
58 static const vtxVTable_t trampVTable
; // forward
59 static vtxDevice_t vtxTramp
= {
60 .vTable
= &trampVTable
,
64 static serialPort_t
*trampSerialPort
= NULL
;
66 static uint8_t trampReqBuffer
[16];
67 static uint8_t trampRespBuffer
[16];
70 TRAMP_STATUS_BAD_DEVICE
= -1,
71 TRAMP_STATUS_OFFLINE
= 0,
73 TRAMP_STATUS_SET_FREQ_PW
,
74 TRAMP_STATUS_CHECK_FREQ_PW
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
)
110 for (int i
= 1 ; i
< 14 ; i
++) {
111 cksum
+= trampBuf
[i
];
117 static void trampCmdU16(uint8_t cmd
, uint16_t param
)
119 if (!trampSerialPort
|| IS_RC_MODE_ACTIVE(BOXVTXCONTROLDISABLE
)) {
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
) {
193 trampStatus
= TRAMP_STATUS_SET_FREQ_PW
;
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();
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];
221 const uint16_t min_freq
= trampRespBuffer
[2]|(trampRespBuffer
[3] << 8);
223 trampRFFreqMin
= min_freq
;
224 trampRFFreqMax
= trampRespBuffer
[4]|(trampRespBuffer
[5] << 8);
225 trampRFPowerMax
= trampRespBuffer
[6]|(trampRespBuffer
[7] << 8);
229 // throw bytes echoed from tx to rx in bidirectional mode away
235 const uint16_t freq
= trampRespBuffer
[2]|(trampRespBuffer
[3] << 8);
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
;
252 // throw bytes echoed from tx to rx in bidirectional mode away
258 const uint16_t temp
= (int16_t)(trampRespBuffer
[6]|(trampRespBuffer
[7] << 8));
260 trampTemperature
= temp
;
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
;
285 static bool trampIsValidResponseCode(uint8_t code
)
287 if (code
== 'r' || code
== 'v' || code
== 's') {
294 // returns completed response code or 0
295 static char trampReceive(uint32_t currentTimeUs
)
297 UNUSED(currentTimeUs
);
299 if (!trampSerialPort
) {
303 while (serialRxBytesWaiting(trampSerialPort
)) {
304 const uint8_t c
= serialRead(trampSerialPort
);
305 trampRespBuffer
[trampReceivePos
++] = c
;
307 switch (trampReceiveState
) {
310 trampReceiveState
= S_WAIT_CODE
;
317 if (trampIsValidResponseCode(c
)) {
318 trampReceiveState
= S_DATA
;
320 trampResetReceiver();
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();
337 trampResetReceiver();
345 void trampQuery(uint8_t cmd
)
347 trampResetReceiver();
351 void trampQueryR(void)
356 void trampQueryV(void)
361 void trampQueryS(void)
366 static void vtxTrampProcess(vtxDevice_t
*vtxDevice
, timeUs_t currentTimeUs
)
370 static timeUs_t lastQueryTimeUs
= 0;
371 static bool initSettingsDoneFlag
= false;
374 static uint16_t debugFreqReqCounter
= 0;
375 static uint16_t debugPowReqCounter
= 0;
378 if (trampStatus
== TRAMP_STATUS_BAD_DEVICE
) {
382 const char replyCode
= trampReceive(currentTimeUs
);
385 debug
[0] = trampStatus
;
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)
402 if (trampStatus
== TRAMP_STATUS_CHECK_FREQ_PW
) {
403 trampStatus
= TRAMP_STATUS_SET_FREQ_PW
;
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
) {
417 static unsigned int cnt
= 0;
418 if (((cnt
++) & 1) == 0) {
425 lastQueryTimeUs
= currentTimeUs
;
429 case TRAMP_STATUS_SET_FREQ_PW
:
432 if (trampConfFreq
&& trampFreqRetries
&& (trampConfFreq
!= trampCurFreq
)) {
433 trampSendFreq(trampConfFreq
);
436 debugFreqReqCounter
++;
439 } else if (trampConfPower
&& trampPowerRetries
&& (trampConfPower
!= trampConfiguredPower
)) {
440 trampSendRFPower(trampConfPower
);
443 debugPowReqCounter
++;
449 trampStatus
= TRAMP_STATUS_CHECK_FREQ_PW
;
451 // delay next status query by 300ms
452 lastQueryTimeUs
= currentTimeUs
+ 300 * 1000;
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;
464 case TRAMP_STATUS_CHECK_FREQ_PW
:
465 if (cmp32(currentTimeUs
, lastQueryTimeUs
) > 200 * 1000) {
467 lastQueryTimeUs
= currentTimeUs
;
476 debug
[1] = debugFreqReqCounter
;
477 debug
[2] = debugPowReqCounter
;
482 trampCmsUpdateStatusString();
487 #ifdef USE_VTX_COMMON
489 // Interface to common VTX API
491 static vtxDevType_e
vtxTrampGetDeviceType(const vtxDevice_t
*vtxDevice
)
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
)
505 if (trampValidateBandAndChannel(band
, channel
)) {
506 trampSetBandAndChannel(band
, channel
);
507 trampCommitChanges();
511 static void vtxTrampSetPowerByIndex(vtxDevice_t
*vtxDevice
, uint8_t index
)
514 trampDevSetPowerByIndex(index
);
517 static void vtxTrampSetPitMode(vtxDevice_t
*vtxDevice
, uint8_t onoff
)
520 trampSetPitMode(onoff
);
523 static void vtxTrampSetFreq(vtxDevice_t
*vtxDevice
, uint16_t freq
)
526 if (trampValidateFreq(freq
)) {
528 trampCommitChanges();
532 static bool vtxTrampGetBandAndChannel(const vtxDevice_t
*vtxDevice
, uint8_t *pBand
, uint8_t *pChannel
)
534 if (!vtxTrampIsReady(vtxDevice
)) {
538 // if in user-freq mode then report band as zero
539 *pBand
= trampSetByFreqFlag
? 0 : trampBand
;
540 *pChannel
= trampChannel
;
544 static bool vtxTrampGetPowerIndex(const vtxDevice_t
*vtxDevice
, uint8_t *pIndex
)
546 if (!vtxTrampIsReady(vtxDevice
)) {
550 if (trampConfiguredPower
> 0) {
551 for (uint8_t i
= 0; i
< sizeof(trampPowerTable
); i
++) {
552 if (trampConfiguredPower
<= trampPowerTable
[i
]) {
562 static bool vtxTrampGetPitMode(const vtxDevice_t
*vtxDevice
, uint8_t *pOnOff
)
564 if (!vtxTrampIsReady(vtxDevice
)) {
568 *pOnOff
= trampPitMode
;
572 static bool vtxTrampGetFreq(const vtxDevice_t
*vtxDevice
, uint16_t *pFreq
)
574 if (!vtxTrampIsReady(vtxDevice
)) {
578 *pFreq
= trampCurFreq
;
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
,
598 bool vtxTrampInit(void)
600 serialPortConfig_t
*portConfig
= findSerialPortConfig(FUNCTION_VTX_TRAMP
);
603 portOptions_e portOptions
= 0;
604 #if defined(USE_VTX_COMMON)
605 portOptions
= portOptions
| (vtxConfig()->halfDuplex
? SERIAL_BIDIR
: SERIAL_UNIDIR
);
607 portOptions
= SERIAL_BIDIR
;
610 trampSerialPort
= openSerialPort(portConfig
->identifier
, FUNCTION_VTX_TRAMP
, NULL
, NULL
, 9600, MODE_RXTX
, portOptions
);
613 if (!trampSerialPort
) {
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
);