optimise mavlink SS packet size (#3029)
[ExpressLRS.git] / src / lib / LUA / tx_devLUA.cpp
blob99620a5e89df4fe04edf3e3442c49435c0900cca
1 #ifdef TARGET_TX
3 #include "rxtx_devLua.h"
4 #include "CRSF.h"
5 #include "CRSFHandset.h"
6 #include "OTA.h"
7 #include "FHSS.h"
8 #include "helpers.h"
10 #define STR_LUA_ALLAUX "AUX1;AUX2;AUX3;AUX4;AUX5;AUX6;AUX7;AUX8;AUX9;AUX10"
12 #define STR_LUA_ALLAUX_UPDOWN "AUX1" LUASYM_ARROW_UP ";AUX1" LUASYM_ARROW_DN ";AUX2" LUASYM_ARROW_UP ";AUX2" LUASYM_ARROW_DN \
13 ";AUX3" LUASYM_ARROW_UP ";AUX3" LUASYM_ARROW_DN ";AUX4" LUASYM_ARROW_UP ";AUX4" LUASYM_ARROW_DN \
14 ";AUX5" LUASYM_ARROW_UP ";AUX5" LUASYM_ARROW_DN ";AUX6" LUASYM_ARROW_UP ";AUX6" LUASYM_ARROW_DN \
15 ";AUX7" LUASYM_ARROW_UP ";AUX7" LUASYM_ARROW_DN ";AUX8" LUASYM_ARROW_UP ";AUX8" LUASYM_ARROW_DN \
16 ";AUX9" LUASYM_ARROW_UP ";AUX9" LUASYM_ARROW_DN ";AUX10" LUASYM_ARROW_UP ";AUX10" LUASYM_ARROW_DN
18 #if defined(RADIO_SX127X)
19 #define STR_LUA_PACKETRATES \
20 "D50Hz(-112dBm);25Hz(-123dBm);50Hz(-120dBm);100Hz(-117dBm);100Hz Full(-112dBm);200Hz(-112dBm)"
21 #elif defined(RADIO_LR1121)
22 #define STR_LUA_PACKETRATES \
23 "X100F;X150;" \
24 "50 2.4G;100F 2.4G;150 2.4G;250 2.4G;333F 2.4G;500 2.4G;DK250 2.4G;DK500 2.4G;K1000 2.4G;" \
25 "D50 Low;25 Low;50 Low;100 Low;100F Low;200 Low;200F Low;250 Low;K1000F Low"
26 #elif defined(RADIO_SX128X)
27 #define STR_LUA_PACKETRATES \
28 "50Hz(-115dBm);100Hz Full(-112dBm);150Hz(-112dBm);250Hz(-108dBm);333Hz Full(-105dBm);500Hz(-105dBm);" \
29 "D250(-104dBm);D500(-104dBm);F500(-104dBm);F1000(-104dBm)"
30 #else
31 #error Invalid radio configuration!
32 #endif
34 extern char backpackVersion[];
36 static char version_domain[20+1+6+1];
37 char pwrFolderDynamicName[] = "TX Power (1000 Dynamic)";
38 char vtxFolderDynamicName[] = "VTX Admin (OFF:C:1 Aux11 )";
39 static char modelMatchUnit[] = " (ID: 00)";
40 static char tlmBandwidth[] = " (xxxxxbps)";
41 static const char folderNameSeparator[2] = {' ',':'};
42 static const char tlmRatios[] = "Std;Off;1:128;1:64;1:32;1:16;1:8;1:4;1:2;Race";
43 static const char tlmRatiosMav[] = ";;;;;;;;1:2;";
44 static const char switchmodeOpts4ch[] = "Wide;Hybrid";
45 static const char switchmodeOpts4chMav[] = ";Hybrid";
46 static const char switchmodeOpts8ch[] = "8ch;16ch Rate/2;12ch Mixed";
47 static const char switchmodeOpts8chMav[] = ";16ch Rate/2;";
48 static const char antennamodeOpts[] = "Gemini;Ant 1;Ant 2;Switch";
49 static const char linkModeOpts[] = "Normal;MAVLink";
50 static const char luastrDvrAux[] = "Off;" STR_LUA_ALLAUX_UPDOWN;
51 static const char luastrDvrDelay[] = "0s;5s;15s;30s;45s;1min;2min";
52 static const char luastrHeadTrackingEnable[] = "Off;On;" STR_LUA_ALLAUX_UPDOWN;
53 static const char luastrHeadTrackingStart[] = STR_LUA_ALLAUX;
54 static const char luastrOffOn[] = "Off;On";
55 static char luastrPacketRates[] = STR_LUA_PACKETRATES;
57 #define HAS_RADIO (GPIO_PIN_SCK != UNDEF_PIN)
59 static struct luaItem_selection luaAirRate = {
60 {"Packet Rate", CRSF_TEXT_SELECTION},
61 0, // value
62 luastrPacketRates,
63 STR_EMPTYSPACE
66 static struct luaItem_selection luaTlmRate = {
67 {"Telem Ratio", CRSF_TEXT_SELECTION},
68 0, // value
69 tlmRatios,
70 tlmBandwidth
73 //----------------------------POWER------------------
74 static struct luaItem_folder luaPowerFolder = {
75 {"TX Power", CRSF_FOLDER},pwrFolderDynamicName
78 static struct luaItem_selection luaPower = {
79 {"Max Power", CRSF_TEXT_SELECTION},
80 0, // value
81 strPowerLevels,
82 "mW"
85 static struct luaItem_selection luaDynamicPower = {
86 {"Dynamic", CRSF_TEXT_SELECTION},
87 0, // value
88 "Off;Dyn;AUX9;AUX10;AUX11;AUX12",
89 STR_EMPTYSPACE
92 static struct luaItem_selection luaFanThreshold = {
93 {"Fan Thresh", CRSF_TEXT_SELECTION},
94 0, // value
95 "10mW;25mW;50mW;100mW;250mW;500mW;1000mW;2000mW;Never",
96 STR_EMPTYSPACE // units embedded so it won't display "NevermW"
99 #if defined(Regulatory_Domain_EU_CE_2400)
100 static struct luaItem_string luaCELimit = {
101 {"100mW CE LIMIT", CRSF_INFO},
102 STR_EMPTYSPACE
104 #endif
106 //----------------------------POWER------------------
108 static struct luaItem_selection luaSwitch = {
109 {"Switch Mode", CRSF_TEXT_SELECTION},
110 0, // value
111 switchmodeOpts4ch,
112 STR_EMPTYSPACE
115 static struct luaItem_selection luaAntenna = {
116 {"Antenna Mode", CRSF_TEXT_SELECTION},
117 0, // value
118 antennamodeOpts,
119 STR_EMPTYSPACE
122 static struct luaItem_selection luaLinkMode = {
123 {"Link Mode", CRSF_TEXT_SELECTION},
124 0, // value
125 linkModeOpts,
126 STR_EMPTYSPACE
129 static struct luaItem_selection luaModelMatch = {
130 {"Model Match", CRSF_TEXT_SELECTION},
131 0, // value
132 luastrOffOn,
133 modelMatchUnit
136 static struct luaItem_command luaBind = {
137 {"Bind", CRSF_COMMAND},
138 lcsIdle, // step
139 STR_EMPTYSPACE
142 static struct luaItem_string luaInfo = {
143 {"Bad/Good", (crsf_value_type_e)(CRSF_INFO | CRSF_FIELD_ELRS_HIDDEN)},
144 STR_EMPTYSPACE
147 static struct luaItem_string luaELRSversion = {
148 {version_domain, CRSF_INFO},
149 commit
152 //---------------------------- WiFi -----------------------------
153 static struct luaItem_folder luaWiFiFolder = {
154 {"WiFi Connectivity", CRSF_FOLDER}
157 static struct luaItem_command luaWebUpdate = {
158 {"Enable WiFi", CRSF_COMMAND},
159 lcsIdle, // step
160 STR_EMPTYSPACE
163 static struct luaItem_command luaRxWebUpdate = {
164 {"Enable Rx WiFi", CRSF_COMMAND},
165 lcsIdle, // step
166 STR_EMPTYSPACE
169 static struct luaItem_command luaTxBackpackUpdate = {
170 {"Enable Backpack WiFi", CRSF_COMMAND},
171 lcsIdle, // step
172 STR_EMPTYSPACE
175 static struct luaItem_command luaVRxBackpackUpdate = {
176 {"Enable VRx WiFi", CRSF_COMMAND},
177 lcsIdle, // step
178 STR_EMPTYSPACE
180 //---------------------------- WiFi -----------------------------
182 #if defined(PLATFORM_ESP32)
183 static struct luaItem_command luaBLEJoystick = {
184 {"BLE Joystick", CRSF_COMMAND},
185 lcsIdle, // step
186 STR_EMPTYSPACE
188 #endif
190 //----------------------------VTX ADMINISTRATOR------------------
191 static struct luaItem_folder luaVtxFolder = {
192 {"VTX Administrator", CRSF_FOLDER},vtxFolderDynamicName
195 static struct luaItem_selection luaVtxBand = {
196 {"Band", CRSF_TEXT_SELECTION},
197 0, // value
198 "Off;A;B;E;F;R;L",
199 STR_EMPTYSPACE
202 static struct luaItem_int8 luaVtxChannel = {
203 {"Channel", CRSF_UINT8},
204 0, // value
205 1, // min
206 8, // max
207 STR_EMPTYSPACE
210 static struct luaItem_selection luaVtxPwr = {
211 {"Pwr Lvl", CRSF_TEXT_SELECTION},
212 0, // value
213 "-;1;2;3;4;5;6;7;8",
214 STR_EMPTYSPACE
217 static struct luaItem_selection luaVtxPit = {
218 {"Pitmode", CRSF_TEXT_SELECTION},
219 0, // value
220 "Off;On;" STR_LUA_ALLAUX_UPDOWN,
221 STR_EMPTYSPACE
224 static struct luaItem_command luaVtxSend = {
225 {"Send VTx", CRSF_COMMAND},
226 lcsIdle, // step
227 STR_EMPTYSPACE
229 //----------------------------VTX ADMINISTRATOR------------------
231 //---------------------------- BACKPACK ------------------
232 static struct luaItem_folder luaBackpackFolder = {
233 {"Backpack", CRSF_FOLDER},
236 static struct luaItem_selection luaBackpackEnable = {
237 {"Backpack", CRSF_TEXT_SELECTION},
238 0, // value
239 luastrOffOn,
240 STR_EMPTYSPACE};
242 static struct luaItem_selection luaDvrAux = {
243 {"DVR Rec", CRSF_TEXT_SELECTION},
244 0, // value
245 luastrDvrAux,
246 STR_EMPTYSPACE};
248 static struct luaItem_selection luaDvrStartDelay = {
249 {"DVR Srt Dly", CRSF_TEXT_SELECTION},
250 0, // value
251 luastrDvrDelay,
252 STR_EMPTYSPACE};
254 static struct luaItem_selection luaDvrStopDelay = {
255 {"DVR Stp Dly", CRSF_TEXT_SELECTION},
256 0, // value
257 luastrDvrDelay,
258 STR_EMPTYSPACE};
260 static struct luaItem_selection luaHeadTrackingEnableChannel = {
261 {"HT Enable", CRSF_TEXT_SELECTION},
262 0, // value
263 luastrHeadTrackingEnable,
264 STR_EMPTYSPACE};
266 static struct luaItem_selection luaHeadTrackingStartChannel = {
267 {"HT Start Channel", CRSF_TEXT_SELECTION},
268 0, // value
269 luastrHeadTrackingStart,
270 STR_EMPTYSPACE};
272 static struct luaItem_selection luaBackpackTelemetry = {
273 {"Telemetry", CRSF_TEXT_SELECTION},
274 0, // value
275 "Off;ESPNOW;WiFi",
276 STR_EMPTYSPACE};
278 static struct luaItem_string luaBackpackVersion = {
279 {"Version", CRSF_INFO},
280 backpackVersion};
282 //---------------------------- BACKPACK ------------------
284 static char luaBadGoodString[10];
285 static int event();
287 extern TxConfig config;
288 extern void VtxTriggerSend();
289 extern void ResetPower();
290 extern uint8_t adjustPacketRateForBaud(uint8_t rate);
291 extern void SetSyncSpam();
292 extern bool RxWiFiReadyToSend;
293 extern bool BackpackTelemReadyToSend;
294 extern bool TxBackpackWiFiReadyToSend;
295 extern bool VRxBackpackWiFiReadyToSend;
296 extern unsigned long rebootTime;
297 extern void setWifiUpdateMode();
299 static void luadevUpdateModelID() {
300 itoa(CRSFHandset::getModelID(), modelMatchUnit+6, 10);
301 strcat(modelMatchUnit, ")");
304 static void luadevUpdateTlmBandwidth()
306 expresslrs_tlm_ratio_e eRatio = (expresslrs_tlm_ratio_e)config.GetTlm();
307 // TLM_RATIO_STD / TLM_RATIO_DISARMED
308 if (eRatio == TLM_RATIO_STD || eRatio == TLM_RATIO_DISARMED)
310 // For Standard ratio, display the ratio instead of bps
311 strcpy(tlmBandwidth, " (1:");
312 uint8_t ratioDiv = TLMratioEnumToValue(ExpressLRS_currAirRate_Modparams->TLMinterval);
313 itoa(ratioDiv, &tlmBandwidth[4], 10);
314 strcat(tlmBandwidth, ")");
317 // TLM_RATIO_NO_TLM
318 else if (eRatio == TLM_RATIO_NO_TLM)
320 tlmBandwidth[0] = '\0';
323 // All normal ratios
324 else
326 tlmBandwidth[0] = ' ';
328 uint16_t hz = 1000000 / ExpressLRS_currAirRate_Modparams->interval;
329 uint8_t ratiodiv = TLMratioEnumToValue(eRatio);
330 uint8_t burst = TLMBurstMaxForRateRatio(hz, ratiodiv);
331 uint8_t bytesPerCall = OtaIsFullRes ? ELRS8_TELEMETRY_BYTES_PER_CALL : ELRS4_TELEMETRY_BYTES_PER_CALL;
332 uint32_t bandwidthValue = bytesPerCall * 8U * burst * hz / ratiodiv / (burst + 1);
333 if (OtaIsFullRes)
335 // Due to fullres also packing telemetry into the LinkStats packet, there is at least
336 // N bytes more data for every rate except 100Hz 1:128, and 2*N bytes more for many
337 // rates. The calculation is a more complex though, so just approximate some of the
338 // extra bandwidth
339 bandwidthValue += 8U * (ELRS8_TELEMETRY_BYTES_PER_CALL - sizeof(OTA_LinkStats_s));
342 itoa(bandwidthValue, &tlmBandwidth[2], 10);
343 strcat(tlmBandwidth, "bps)");
347 static void luadevUpdateBackpackOpts()
349 if (config.GetBackpackDisable())
351 // If backpack is disabled, set all the Backpack select options to "Disabled"
352 LUA_FIELD_HIDE(luaDvrAux);
353 LUA_FIELD_HIDE(luaDvrStartDelay);
354 LUA_FIELD_HIDE(luaDvrStopDelay);
355 LUA_FIELD_HIDE(luaHeadTrackingEnableChannel);
356 LUA_FIELD_HIDE(luaHeadTrackingStartChannel);
357 LUA_FIELD_HIDE(luaBackpackTelemetry);
358 LUA_FIELD_HIDE(luaBackpackVersion);
360 else
362 LUA_FIELD_SHOW(luaDvrAux);
363 LUA_FIELD_SHOW(luaDvrStartDelay);
364 LUA_FIELD_SHOW(luaDvrStopDelay);
365 LUA_FIELD_SHOW(luaHeadTrackingEnableChannel);
366 LUA_FIELD_SHOW(luaHeadTrackingStartChannel);
367 LUA_FIELD_SHOW(luaBackpackTelemetry);
368 LUA_FIELD_SHOW(luaBackpackVersion);
372 static void setBleJoystickMode()
374 connectionState = bleJoystick;
377 static void luahandWifiBle(struct luaPropertiesCommon *item, uint8_t arg)
379 struct luaItem_command *cmd = (struct luaItem_command *)item;
380 void (*setTargetState)();
381 connectionState_e targetState;
382 const char *textConfirm;
383 const char *textRunning;
384 if ((void *)item == (void *)&luaWebUpdate)
386 setTargetState = &setWifiUpdateMode;
387 textConfirm = "Enter WiFi Update?";
388 textRunning = "WiFi Running...";
389 targetState = wifiUpdate;
391 else
393 setTargetState = &setBleJoystickMode;
394 textConfirm = "Start BLE Joystick?";
395 textRunning = "Joystick Running...";
396 targetState = bleJoystick;
399 switch ((luaCmdStep_e)arg)
401 case lcsClick:
402 if (connectionState == connected)
404 sendLuaCommandResponse(cmd, lcsAskConfirm, textConfirm);
405 return;
407 // fallthrough (clicking while not connected goes right to exectute)
409 case lcsConfirmed:
410 sendLuaCommandResponse(cmd, lcsExecuting, textRunning);
411 setTargetState();
412 break;
414 case lcsCancel:
415 sendLuaCommandResponse(cmd, lcsIdle, STR_EMPTYSPACE);
416 if (connectionState == targetState)
418 rebootTime = millis() + 400;
420 break;
422 default: // LUACMDSTEP_NONE on load, LUACMDSTEP_EXECUTING (our lua) or LUACMDSTEP_QUERY (Crossfire Config)
423 sendLuaCommandResponse(cmd, cmd->step, cmd->info);
424 break;
428 static void luahandSimpleSendCmd(struct luaPropertiesCommon *item, uint8_t arg)
430 const char *msg = "Sending...";
431 static uint32_t lastLcsPoll;
432 if (arg < lcsCancel)
434 lastLcsPoll = millis();
435 if ((void *)item == (void *)&luaBind)
437 msg = "Binding...";
438 EnterBindingModeSafely();
440 else if ((void *)item == (void *)&luaVtxSend)
442 VtxTriggerSend();
444 else if ((void *)item == (void *)&luaRxWebUpdate)
446 RxWiFiReadyToSend = true;
448 else if ((void *)item == (void *)&luaTxBackpackUpdate && OPT_USE_TX_BACKPACK)
450 TxBackpackWiFiReadyToSend = true;
452 else if ((void *)item == (void *)&luaVRxBackpackUpdate && OPT_USE_TX_BACKPACK)
454 VRxBackpackWiFiReadyToSend = true;
456 sendLuaCommandResponse((struct luaItem_command *)item, lcsExecuting, msg);
457 } /* if doExecute */
458 else if(arg == lcsCancel || ((millis() - lastLcsPoll)> 2000))
460 sendLuaCommandResponse((struct luaItem_command *)item, lcsIdle, STR_EMPTYSPACE);
464 static void updateFolderName_TxPower()
466 uint8_t txPwrDyn = config.GetDynamicPower() ? config.GetBoostChannel() + 1 : 0;
467 uint8_t pwrFolderLabelOffset = 10; // start writing after "TX Power ("
469 // Power Level
470 pwrFolderLabelOffset += findLuaSelectionLabel(&luaPower, &pwrFolderDynamicName[pwrFolderLabelOffset], config.GetPower() - MinPower);
472 // Dynamic Power
473 if (txPwrDyn)
475 pwrFolderDynamicName[pwrFolderLabelOffset++] = folderNameSeparator[0];
476 pwrFolderLabelOffset += findLuaSelectionLabel(&luaDynamicPower, &pwrFolderDynamicName[pwrFolderLabelOffset], txPwrDyn);
479 pwrFolderDynamicName[pwrFolderLabelOffset++] = ')';
480 pwrFolderDynamicName[pwrFolderLabelOffset] = '\0';
483 static void updateFolderName_VtxAdmin()
485 uint8_t vtxBand = config.GetVtxBand();
486 if (vtxBand)
488 luaVtxFolder.dyn_name = vtxFolderDynamicName;
489 uint8_t vtxFolderLabelOffset = 11; // start writing after "VTX Admin ("
491 // Band
492 vtxFolderLabelOffset += findLuaSelectionLabel(&luaVtxBand, &vtxFolderDynamicName[vtxFolderLabelOffset], vtxBand);
493 vtxFolderDynamicName[vtxFolderLabelOffset++] = folderNameSeparator[1];
495 // Channel
496 vtxFolderDynamicName[vtxFolderLabelOffset++] = '1' + config.GetVtxChannel();
498 // VTX Power
499 uint8_t vtxPwr = config.GetVtxPower();
500 //if power is no-change (-), don't show, also hide pitmode
501 if (vtxPwr)
503 vtxFolderDynamicName[vtxFolderLabelOffset++] = folderNameSeparator[1];
504 vtxFolderLabelOffset += findLuaSelectionLabel(&luaVtxPwr, &vtxFolderDynamicName[vtxFolderLabelOffset], vtxPwr);
506 // Pit Mode
507 uint8_t vtxPit = config.GetVtxPitmode();
508 //if pitmode is off, don't show
509 //show pitmode AuxSwitch or show P if not OFF
510 if (vtxPit != 0)
512 if (vtxPit != 1)
514 vtxFolderDynamicName[vtxFolderLabelOffset++] = folderNameSeparator[1];
515 vtxFolderLabelOffset += findLuaSelectionLabel(&luaVtxPit, &vtxFolderDynamicName[vtxFolderLabelOffset], vtxPit);
517 else
519 vtxFolderDynamicName[vtxFolderLabelOffset++] = folderNameSeparator[1];
520 vtxFolderDynamicName[vtxFolderLabelOffset++] = 'P';
524 vtxFolderDynamicName[vtxFolderLabelOffset++] = ')';
525 vtxFolderDynamicName[vtxFolderLabelOffset] = '\0';
527 else
529 //don't show vtx settings if band is OFF
530 luaVtxFolder.dyn_name = NULL;
534 /***
535 * @brief: Update the luaBadGoodString with the current bad/good count
536 * This item is hidden on our Lua and only displayed in other systems that don't poll our status
537 * Called from luaRegisterDevicePingCallback
538 ****/
539 static void luadevUpdateBadGood()
541 itoa(CRSFHandset::BadPktsCountResult, luaBadGoodString, 10);
542 strcat(luaBadGoodString, "/");
543 itoa(CRSFHandset::GoodPktsCountResult, luaBadGoodString + strlen(luaBadGoodString), 10);
546 /***
547 * @brief: Update the dynamic strings used for folder names and labels
548 ***/
549 void luadevUpdateFolderNames()
551 updateFolderName_TxPower();
552 updateFolderName_VtxAdmin();
554 // These aren't folder names, just string labels slapped in the units field generally
555 luadevUpdateTlmBandwidth();
556 luadevUpdateBackpackOpts();
559 static void recalculatePacketRateOptions(int minInterval)
561 const char *allRates = STR_LUA_PACKETRATES;
562 const char *pos = allRates;
563 luastrPacketRates[0] = 0;
564 for (int i=0 ; i < RATE_MAX ; i++)
566 uint8_t rate = i;
567 rate = RATE_MAX - 1 - rate;
568 bool rateAllowed = (get_elrs_airRateConfig(rate)->interval * get_elrs_airRateConfig(rate)->numOfSends) >= minInterval;
570 // Skip unsupported modes for hardware with only a single LR1121 or with a single RF path
571 rateAllowed &= isSupportedRFRate(rate);
573 const char *semi = strchrnul(pos, ';');
574 if (rateAllowed)
576 strncat(luastrPacketRates, pos, semi - pos);
578 pos = semi;
579 if (*semi == ';')
581 strcat(luastrPacketRates, ";");
582 pos = semi+1;
586 // trim off trailing semicolons (assumes luastrPacketRates has at least 1 non-semicolon)
587 for (auto lastPos = strlen(luastrPacketRates)-1; luastrPacketRates[lastPos] == ';'; lastPos--)
589 luastrPacketRates[lastPos] = '\0';
593 uint8_t adjustSwitchModeForAirRate(OtaSwitchMode_e eSwitchMode, uint8_t packetSize)
595 // Only the fullres modes have 3 switch modes, so reset the switch mode if outside the
596 // range for 4ch mode
597 if (packetSize == OTA4_PACKET_SIZE)
599 if (eSwitchMode > smHybridOr16ch)
600 return smWideOr8ch;
603 return eSwitchMode;
606 static void registerLuaParameters()
608 if (HAS_RADIO) {
609 registerLUAParameter(&luaAirRate, [](struct luaPropertiesCommon *item, uint8_t arg) {
610 if (arg < RATE_MAX)
612 uint8_t selectedRate = RATE_MAX - 1 - arg;
613 uint8_t actualRate = adjustPacketRateForBaud(selectedRate);
614 uint8_t newSwitchMode = adjustSwitchModeForAirRate(
615 (OtaSwitchMode_e)config.GetSwitchMode(), get_elrs_airRateConfig(actualRate)->PayloadLength);
616 // If the switch mode is going to change, block the change while connected
617 bool isDisconnected = connectionState == disconnected;
618 // Don't allow the switch mode to change if the TX is in mavlink mode
619 // Wide switchmode is not compatible with mavlink, and the switchmode is
620 // auto configuredwhen entering mavlink mode
621 bool isMavlinkMode = config.GetLinkMode() == TX_MAVLINK_MODE;
622 if (newSwitchMode == OtaSwitchModeCurrent || (isDisconnected && !isMavlinkMode))
624 config.SetRate(actualRate);
625 config.SetSwitchMode(newSwitchMode);
626 if (actualRate != selectedRate)
628 setLuaWarningFlag(LUA_FLAG_ERROR_BAUDRATE, true);
631 else
632 setLuaWarningFlag(LUA_FLAG_ERROR_CONNECTED, true);
635 registerLUAParameter(&luaTlmRate, [](struct luaPropertiesCommon *item, uint8_t arg) {
636 expresslrs_tlm_ratio_e eRatio = (expresslrs_tlm_ratio_e)arg;
637 if (eRatio <= TLM_RATIO_DISARMED)
639 bool isMavlinkMode = config.GetLinkMode() == TX_MAVLINK_MODE;
640 // Don't allow TLM ratio changes if using AIRPORT or Mavlink
641 if (!firmwareOptions.is_airport && !isMavlinkMode)
643 config.SetTlm(eRatio);
647 if (!firmwareOptions.is_airport)
649 registerLUAParameter(&luaSwitch, [](struct luaPropertiesCommon *item, uint8_t arg) {
650 // Only allow changing switch mode when disconnected since we need to guarantee
651 // the pack and unpack functions are matched
652 bool isDisconnected = connectionState == disconnected;
653 // Don't allow the switch mode to change if the TX is in mavlink mode
654 // Wide switchmode is not compatible with mavlink, and the switchmode is
655 // auto configuredwhen entering mavlink mode
656 bool isMavlinkMode = config.GetLinkMode() == TX_MAVLINK_MODE;
657 if (isDisconnected && !isMavlinkMode)
659 config.SetSwitchMode(arg);
660 OtaUpdateSerializers((OtaSwitchMode_e)arg, ExpressLRS_currAirRate_Modparams->PayloadLength);
662 else if (!isMavlinkMode) // No need to display warning as no switch change can be made while in Mavlink mode.
664 setLuaWarningFlag(LUA_FLAG_ERROR_CONNECTED, true);
668 if (isDualRadio())
670 registerLUAParameter(&luaAntenna, [](struct luaPropertiesCommon *item, uint8_t arg) {
671 config.SetAntennaMode(arg);
674 registerLUAParameter(&luaLinkMode, [](struct luaPropertiesCommon *item, uint8_t arg) {
675 // Only allow changing when disconnected since we need to guarantee
676 // the switch pack and unpack functions are matched on the tx and rx.
677 bool isDisconnected = connectionState == disconnected;
678 if (isDisconnected)
680 config.SetLinkMode(arg);
682 else
684 setLuaWarningFlag(LUA_FLAG_ERROR_CONNECTED, true);
687 if (!firmwareOptions.is_airport)
689 registerLUAParameter(&luaModelMatch, [](struct luaPropertiesCommon *item, uint8_t arg) {
690 bool newModelMatch = arg;
691 config.SetModelMatch(newModelMatch);
692 if (connectionState == connected)
694 mspPacket_t msp;
695 msp.reset();
696 msp.makeCommand();
697 msp.function = MSP_SET_RX_CONFIG;
698 msp.addByte(MSP_ELRS_MODEL_ID);
699 msp.addByte(newModelMatch ? CRSFHandset::getModelID() : 0xff);
700 CRSF::AddMspMessage(&msp, CRSF_ADDRESS_CRSF_RECEIVER);
702 luadevUpdateModelID();
706 // POWER folder
707 registerLUAParameter(&luaPowerFolder);
708 luadevGeneratePowerOpts(&luaPower);
709 registerLUAParameter(&luaPower, [](struct luaPropertiesCommon *item, uint8_t arg) {
710 config.SetPower((PowerLevels_e)constrain(arg + POWERMGNT::getMinPower(), POWERMGNT::getMinPower(), POWERMGNT::getMaxPower()));
711 if (!config.IsModified())
713 ResetPower();
715 }, luaPowerFolder.common.id);
716 registerLUAParameter(&luaDynamicPower, [](struct luaPropertiesCommon *item, uint8_t arg) {
717 config.SetDynamicPower(arg > 0);
718 config.SetBoostChannel((arg - 1) > 0 ? arg - 1 : 0);
719 }, luaPowerFolder.common.id);
721 if (GPIO_PIN_FAN_EN != UNDEF_PIN || GPIO_PIN_FAN_PWM != UNDEF_PIN) {
722 registerLUAParameter(&luaFanThreshold, [](struct luaPropertiesCommon *item, uint8_t arg){
723 config.SetPowerFanThreshold(arg);
724 }, luaPowerFolder.common.id);
726 #if defined(Regulatory_Domain_EU_CE_2400)
727 if (HAS_RADIO) {
728 registerLUAParameter(&luaCELimit, NULL, luaPowerFolder.common.id);
730 #endif
731 if ((HAS_RADIO || OPT_USE_TX_BACKPACK) && !firmwareOptions.is_airport) {
732 // VTX folder
733 registerLUAParameter(&luaVtxFolder);
734 registerLUAParameter(&luaVtxBand, [](struct luaPropertiesCommon *item, uint8_t arg) {
735 config.SetVtxBand(arg);
736 }, luaVtxFolder.common.id);
737 registerLUAParameter(&luaVtxChannel, [](struct luaPropertiesCommon *item, uint8_t arg) {
738 config.SetVtxChannel(arg - 1);
739 }, luaVtxFolder.common.id);
740 registerLUAParameter(&luaVtxPwr, [](struct luaPropertiesCommon *item, uint8_t arg) {
741 config.SetVtxPower(arg);
742 }, luaVtxFolder.common.id);
743 registerLUAParameter(&luaVtxPit, [](struct luaPropertiesCommon *item, uint8_t arg) {
744 config.SetVtxPitmode(arg);
745 }, luaVtxFolder.common.id);
746 registerLUAParameter(&luaVtxSend, &luahandSimpleSendCmd, luaVtxFolder.common.id);
749 // WIFI folder
750 registerLUAParameter(&luaWiFiFolder);
751 registerLUAParameter(&luaWebUpdate, &luahandWifiBle, luaWiFiFolder.common.id);
752 if (HAS_RADIO) {
753 registerLUAParameter(&luaRxWebUpdate, &luahandSimpleSendCmd, luaWiFiFolder.common.id);
755 if (OPT_USE_TX_BACKPACK) {
756 registerLUAParameter(&luaTxBackpackUpdate, &luahandSimpleSendCmd, luaWiFiFolder.common.id);
757 registerLUAParameter(&luaVRxBackpackUpdate, &luahandSimpleSendCmd, luaWiFiFolder.common.id);
758 // Backpack folder
759 registerLUAParameter(&luaBackpackFolder);
760 if (GPIO_PIN_BACKPACK_EN != UNDEF_PIN)
762 registerLUAParameter(
763 &luaBackpackEnable, [](luaPropertiesCommon *item, uint8_t arg) {
764 // option is Off/On (enable) and config storage is On/Off (disable)
765 config.SetBackpackDisable(arg == 0);
766 }, luaBackpackFolder.common.id);
768 registerLUAParameter(
769 &luaDvrAux, [](luaPropertiesCommon *item, uint8_t arg) {
770 if (config.GetBackpackDisable() == false)
771 config.SetDvrAux(arg);
773 luaBackpackFolder.common.id);
774 registerLUAParameter(
775 &luaDvrStartDelay, [](luaPropertiesCommon *item, uint8_t arg) {
776 if (config.GetBackpackDisable() == false)
777 config.SetDvrStartDelay(arg);
779 luaBackpackFolder.common.id);
780 registerLUAParameter(
781 &luaDvrStopDelay, [](luaPropertiesCommon *item, uint8_t arg) {
782 if (config.GetBackpackDisable() == false)
783 config.SetDvrStopDelay(arg);
785 luaBackpackFolder.common.id);
786 registerLUAParameter(
787 &luaHeadTrackingEnableChannel, [](luaPropertiesCommon *item, uint8_t arg) {
788 config.SetPTREnableChannel(arg);
790 luaBackpackFolder.common.id);
791 registerLUAParameter(
792 &luaHeadTrackingStartChannel, [](luaPropertiesCommon *item, uint8_t arg) {
793 config.SetPTRStartChannel(arg);
795 luaBackpackFolder.common.id);
796 registerLUAParameter(
797 &luaBackpackTelemetry, [](luaPropertiesCommon *item, uint8_t arg) {
798 config.SetBackpackTlmMode(arg);
799 BackpackTelemReadyToSend = true;
800 }, luaBackpackFolder.common.id);
802 registerLUAParameter(&luaBackpackVersion, nullptr, luaBackpackFolder.common.id);
806 #if defined(PLATFORM_ESP32)
807 registerLUAParameter(&luaBLEJoystick, &luahandWifiBle);
808 #endif
810 if (HAS_RADIO) {
811 registerLUAParameter(&luaBind, &luahandSimpleSendCmd);
814 registerLUAParameter(&luaInfo);
815 if (strlen(version) < 21) {
816 strlcpy(version_domain, version, 21);
817 strlcat(version_domain, " ", sizeof(version_domain));
818 } else {
819 strlcpy(version_domain, version, 18);
820 strlcat(version_domain, "... ", sizeof(version_domain));
822 strlcat(version_domain, FHSSconfig->domain, sizeof(version_domain));
823 registerLUAParameter(&luaELRSversion);
824 registerLUAParameter(NULL);
827 static int event()
829 if (connectionState > FAILURE_STATES)
831 return DURATION_NEVER;
834 bool isMavlinkMode = config.GetLinkMode() == TX_MAVLINK_MODE;
835 uint8_t currentRate = adjustPacketRateForBaud(config.GetRate());
836 recalculatePacketRateOptions(handset->getMinPacketInterval());
837 setLuaTextSelectionValue(&luaAirRate, RATE_MAX - 1 - currentRate);
839 setLuaTextSelectionValue(&luaTlmRate, config.GetTlm());
840 luaTlmRate.options = isMavlinkMode ? tlmRatiosMav : tlmRatios;
842 setLuaTextSelectionValue(&luaSwitch, config.GetSwitchMode());
843 if (isMavlinkMode)
845 luaSwitch.options = OtaIsFullRes ? switchmodeOpts8chMav : switchmodeOpts4chMav;
847 else
849 luaSwitch.options = OtaIsFullRes ? switchmodeOpts8ch : switchmodeOpts4ch;
852 if (isDualRadio())
854 setLuaTextSelectionValue(&luaAntenna, config.GetAntennaMode());
856 setLuaTextSelectionValue(&luaLinkMode, config.GetLinkMode());
857 luadevUpdateModelID();
858 setLuaTextSelectionValue(&luaModelMatch, (uint8_t)config.GetModelMatch());
859 setLuaTextSelectionValue(&luaPower, config.GetPower() - MinPower);
860 if (GPIO_PIN_FAN_EN != UNDEF_PIN || GPIO_PIN_FAN_PWM != UNDEF_PIN)
862 setLuaTextSelectionValue(&luaFanThreshold, config.GetPowerFanThreshold());
865 uint8_t dynamic = config.GetDynamicPower() ? config.GetBoostChannel() + 1 : 0;
866 setLuaTextSelectionValue(&luaDynamicPower, dynamic);
868 setLuaTextSelectionValue(&luaVtxBand, config.GetVtxBand());
869 setLuaUint8Value(&luaVtxChannel, config.GetVtxChannel() + 1);
870 setLuaTextSelectionValue(&luaVtxPwr, config.GetVtxPower());
871 setLuaTextSelectionValue(&luaVtxPit, config.GetVtxPitmode());
872 if (OPT_USE_TX_BACKPACK)
874 setLuaTextSelectionValue(&luaBackpackEnable, config.GetBackpackDisable() ? 0 : 1);
875 setLuaTextSelectionValue(&luaDvrAux, config.GetBackpackDisable() ? 0 : config.GetDvrAux());
876 setLuaTextSelectionValue(&luaDvrStartDelay, config.GetBackpackDisable() ? 0 : config.GetDvrStartDelay());
877 setLuaTextSelectionValue(&luaDvrStopDelay, config.GetBackpackDisable() ? 0 : config.GetDvrStopDelay());
878 setLuaTextSelectionValue(&luaHeadTrackingEnableChannel, config.GetBackpackDisable() ? 0 : config.GetPTREnableChannel());
879 setLuaTextSelectionValue(&luaHeadTrackingStartChannel, config.GetBackpackDisable() ? 0 : config.GetPTRStartChannel());
880 setLuaTextSelectionValue(&luaBackpackTelemetry, config.GetBackpackDisable() ? 0 : config.GetBackpackTlmMode());
881 setLuaStringValue(&luaBackpackVersion, backpackVersion);
883 luadevUpdateFolderNames();
884 return DURATION_IMMEDIATELY;
887 static int timeout()
889 if (luaHandleUpdateParameter())
891 SetSyncSpam();
893 return DURATION_IMMEDIATELY;
896 static int start()
898 if (connectionState > FAILURE_STATES)
900 return DURATION_NEVER;
902 handset->registerParameterUpdateCallback(luaParamUpdateReq);
903 registerLuaParameters();
905 setLuaStringValue(&luaInfo, luaBadGoodString);
906 luaRegisterDevicePingCallback(&luadevUpdateBadGood);
908 event();
909 return DURATION_IMMEDIATELY;
912 device_t LUA_device = {
913 .initialize = NULL,
914 .start = start,
915 .event = event,
916 .timeout = timeout
919 #endif