-
Notifications
You must be signed in to change notification settings - Fork 67
/
mLRS-bw.lua
972 lines (841 loc) · 33.7 KB
/
mLRS-bw.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
--local toolName = "TNS|mLRS Configurator|TNE"
----------------------------------------------------------------------
-- Copyright (c) MLRS project
-- GPL3
-- https://www.gnu.org/licenses/gpl-3.0.de.html
-- OlliW @ www.olliw.eu
-- modify for B/W screen by Jason Wang
----------------------------------------------------------------------
-- Lua TOOLS script
----------------------------------------------------------------------
-- copy script to SCRIPTS\TOOLS folder on OpenTx SD card
-- works with mLRS v0.3.32 and later, mOTX v33
local version = '2023-12-02.00'
local required_tx_mLRS_version_int = 337 -- 'v0.3.37'
local required_rx_mLRS_version_int = 335 -- 'v0.3.35'
-----------------------------custom param list ---------------------------
-- param idxes of options on main page (max 5 params)
local param_idx_list = {}
param_idx_list[0] = 0 -- BindPhrase -- must not be changed
param_idx_list[1] = 1 -- Mode
param_idx_list[2] = 4 -- Param1 = Tx Power
param_idx_list[3] = 14 -- Param2 = Rx Power
param_idx_list[4] = 17 -- Param3 = Rx Out Mode
-- experimental
local paramLoadDeadTime_10ms = 200 -- 150 was a bit too short
local disableParamLoadErrorWarnings = false
----------------------------------------------------------------------
-- black/white helper
----------------------------------------------------------------------
-- idxes of options on main page
-- param (max 5)
local BindPhrase_idx = 0 -- must not be changed
local Mode_idx = 1
local Param1_idx = 2
local Param2_idx = 3
local Param3_idx = 4
-- cmd (max 4)
local Save_idx = 5
local Reload_idx = 6
local Bind_idx = 7
local PAGE_MAIN_CURSOR_IDX_PARAM_MAX = Save_idx - 1
local PAGE_MAIN_CURSOR_IDX_MAX = Bind_idx
-- convert cmd id to param list index
local function cmd_to_list_index(pidx)
for idx = 0, 4 do
if (param_idx_list[idx] == pidx) then return true,idx; end
end
return false
end
-- convert param list index to cmd id
local function list_index_to_cmd(idx)
if (idx <= 4) then return true, param_idx_list[idx]; end
return false
end
--------lines and colors
local CUSTOM_COLOR = INVERS
local TEXT_COLOR = 0
local MENU_TITLE_COLOR = INVERS
local TEXT_DISABLE_COLOR = 0
local function liney(line)
return 8*line;
end
----------------------------------------------------------------------
-- MBridge CRSF emulation
----------------------------------------------------------------------
local MBRIDGE_COMMANDPACKET_STX = 0xA0
local MBRIDGE_COMMANDPACKET_MASK = 0xE0
local MBRIDGE_CMD_TX_LINK_STATS_LEN = 22
local MBRIDGE_CMD_DEVICE_ITEM_LEN = 24
local MBRIDGE_CMD_PARAM_ITEM_LEN = 24
local MBRIDGE_CMD_REQUEST_CMD_LEN = 18
local MBRIDGE_CMD_INFO_LEN = 24
local MBRIDGE_CMD_PARAM_SET_LEN = 7
local MBRIDGE_CMD_MODELID_SET_LEN = 3
local MBRIDGE_PARAM_TYPE_UINT8 = 0
local MBRIDGE_PARAM_TYPE_INT8 = 1
local MBRIDGE_PARAM_TYPE_UINT16 = 2
local MBRIDGE_PARAM_TYPE_INT16 = 3
local MBRIDGE_PARAM_TYPE_LIST = 4
local MBRIDGE_PARAM_TYPE_STR6 = 5
local MBRIDGE_CMD_TX_LINK_STATS = 2
local MBRIDGE_CMD_REQUEST_INFO = 3
local MBRIDGE_CMD_DEVICE_ITEM_TX = 4
local MBRIDGE_CMD_DEVICE_ITEM_RX = 5
local MBRIDGE_CMD_PARAM_REQUEST_LIST = 6
local MBRIDGE_CMD_PARAM_ITEM = 7
local MBRIDGE_CMD_PARAM_ITEM2 = 8
local MBRIDGE_CMD_PARAM_ITEM3 = 9
local MBRIDGE_CMD_REQUEST_CMD = 10
local MBRIDGE_CMD_INFO = 11
local MBRIDGE_CMD_PARAM_SET = 12
local MBRIDGE_CMD_PARAM_STORE = 13
local MBRIDGE_CMD_BIND_START = 14
local MBRIDGE_CMD_BIND_STOP = 15
local MBRIDGE_CMD_MODELID_SET = 16
local MBRIDGE_CMD_SYSTEM_BOOTLOADER = 17
local function mbridgeCmdLen(cmd)
if cmd == MBRIDGE_CMD_TX_LINK_STATS then return MBRIDGE_CMD_TX_LINK_STATS_LEN; end
if cmd == MBRIDGE_CMD_REQUEST_INFO then return 0; end
if cmd == MBRIDGE_CMD_DEVICE_ITEM_TX then return MBRIDGE_CMD_DEVICE_ITEM_LEN; end
if cmd == MBRIDGE_CMD_DEVICE_ITEM_RX then return MBRIDGE_CMD_DEVICE_ITEM_LEN; end
if cmd == MBRIDGE_CMD_PARAM_REQUEST_LIST then return 0; end
if cmd == MBRIDGE_CMD_PARAM_ITEM then return MBRIDGE_CMD_PARAM_ITEM_LEN; end
if cmd == MBRIDGE_CMD_PARAM_ITEM2 then return MBRIDGE_CMD_PARAM_ITEM_LEN; end
if cmd == MBRIDGE_CMD_PARAM_ITEM3 then return MBRIDGE_CMD_PARAM_ITEM_LEN; end
if cmd == MBRIDGE_CMD_REQUEST_CMD then return MBRIDGE_CMD_REQUEST_CMD_LEN; end
if cmd == MBRIDGE_CMD_INFO then return MBRIDGE_CMD_INFO_LEN; end
if cmd == MBRIDGE_CMD_PARAM_SET then return MBRIDGE_CMD_PARAM_SET_LEN; end
if cmd == MBRIDGE_CMD_PARAM_STORE then return 0; end
if cmd == MBRIDGE_CMD_BIND_START then return 0; end
if cmd == MBRIDGE_CMD_BIND_STOP then return 0; end
if cmd == MBRIDGE_CMD_MODELID_SET then return MBRIDGE_CMD_MODELID_SET_LEN; end
if cmd == MBRIDGE_CMD_SYSTEM_BOOTLOADER then return 0; end
return 0;
end
local function isConnected()
if getRSSI() ~= 0 then return true end
return false
end
local function cmdPush(cmd, payload)
-- 'O', 'W', len/cmd, payload bytes
local data = { 79, 87, cmd + MBRIDGE_COMMANDPACKET_STX }
for i=1, mbridgeCmdLen(cmd) do data[#data + 1] = 0 end -- fill with zeros of correct length
for i=1, #payload do data[3 + i] = payload[i] end -- fill in data
-- crossfireTelemetryPush() extends it to
-- 0xEE, len, 129, 'O', 'W', len/cmd, payload bytes, crc8
return crossfireTelemetryPush(129, data)
end
local function cmdPop()
-- crossfireTelemetryPop() is invoked if
-- address = RADIO_ADDRESS (0xEA) or UART_SYNC (0xC8)
-- frame id != normal crsf telemetry sensor id
-- 0xEE, len, 130, len/cmd, payload bytes, crc8
local cmd, data = crossfireTelemetryPop()
-- cmd = 130
-- data = len/cmd, payload bytes
if cmd == nil then return nil end
if data == nil or data[1] == nil then return nil end -- Huston, we have a problem
local command = data[1] - MBRIDGE_COMMANDPACKET_STX
local res = {
cmd = command,
len = mbridgeCmdLen(command),
payload = {}
}
for i=2, #data do res.payload[i-2] = data[i] end
return res
end
----------------------------------------------------------------------
-- Info/Warning box
----------------------------------------------------------------------
local popup = false
local popup_text = ""
local popup_t_end_10ms = -1
local function setPopup(txt)
popup = true
popup_text = txt
popup_t_end_10ms = getTime() + 150
end
local function setPopupWTmo(txt, tmo_10ms)
popup = true
popup_text = txt
popup_t_end_10ms = getTime() + tmo_10ms
end
local function setPopupBlocked(txt)
popup = true
popup_text = txt
popup_t_end_10ms = -1
end
local function isPopupBlocked()
if popup and popup_t_end_10ms < 0 then return true; end
return false;
end
local function clearPopup()
popup = false
end
local function drawPopup()
lcd.drawFilledRectangle(0, liney(1)+2, LCD_W, liney(4)-2, SOLID)
local i = string.find(popup_text, "\n")
local attr = MENU_TITLE_COLOR+MIDSIZE
if i == nil then
lcd.drawText(2, liney(1)+2, popup_text, attr)
else
local t1 = string.sub(popup_text, 1,i-1)
local t2 = string.sub(popup_text, i+1)
lcd.drawText(2, liney(1)+4, t1, attr)
lcd.drawText(2, liney(1)+4+12, t2, attr)
end
end
local function doPopup()
if popup then
drawPopup()
if popup_t_end_10ms > 0 then
local t_10ms = getTime()
if t_10ms > popup_t_end_10ms then clearPopup() end
end
end
end
----------------------------------------------------------------------
-- helper to handle connect
----------------------------------------------------------------------
local connected = false
local connected_has_changed = false
local has_connected = false
local has_disconnected = false
local function doConnected()
local is_connected = isConnected()
connected_has_changed = false
if is_connected ~= connected then connected_has_changed = true end
has_connected = false
if connected_has_changed and is_connected then has_connected = true end
has_disconnected = false
if connected_has_changed and not is_connected then has_disconnected = true end
connected = is_connected
end
----------------------------------------------------------------------
-- variables for mBridge traffic
----------------------------------------------------------------------
local paramloop_t_last = 0
local DEVICE_ITEM_TX = nil
local DEVICE_ITEM_RX = nil
local DEVICE_INFO = nil
local DEVICE_PARAM_LIST = nil
local DEVICE_PARAM_LIST_expected_index = 0
local DEVICE_PARAM_LIST_current_index = -1
local DEVICE_PARAM_LIST_errors = 0
local DEVICE_PARAM_LIST_complete = false
local DEVICE_DOWNLOAD_is_running = true -- we start the script with this
local DEVICE_SAVE_t_last = 0
local function clearParams()
DEVICE_ITEM_TX = nil
DEVICE_ITEM_RX = nil
DEVICE_INFO = nil
DEVICE_PARAM_LIST = nil
DEVICE_PARAM_LIST_expected_index = 0
DEVICE_PARAM_LIST_current_index = -1
DEVICE_PARAM_LIST_errors = 0
DEVICE_PARAM_LIST_complete = false
DEVICE_DOWNLOAD_is_running = true
end
local function paramsError(err)
DEVICE_PARAM_LIST_errors = DEVICE_PARAM_LIST_errors + 1
end
----------------------------------------------------------------------
-- helper to convert command payloads
----------------------------------------------------------------------
local function mb_to_string(payload,pos,len)
local str = ""
for i = 0,len-1 do
if payload[pos+i] == 0 then break end
str = str .. string.char(payload[pos+i])
end
return str
end
local function mb_to_u8(payload, pos)
return payload[pos]
end
local function mb_to_i8(payload, pos)
local v = payload[pos+0]
if v >= 128 then v = v - 256 end
return v
end
local function mb_to_u16(payload, pos)
return payload[pos+0] + payload[pos+1]*256
end
local function mb_to_i16(payload, pos)
local v = payload[pos+0] + payload[pos+1]*256
if v >= 32768 then v = v - 65536 end
return v
end
local function mb_to_u24(payload, pos)
return payload[pos+0] + payload[pos+1]*256 + payload[pos+2]*256*256
end
local function mb_to_u32(payload, pos)
return payload[pos+0] + payload[pos+1]*256 + payload[pos+2]*256*256 + payload[pos+2]*256*256*256
end
local function mb_to_value(payload, pos, typ)
if typ == MBRIDGE_PARAM_TYPE_UINT8 then -- UINT8
return mb_to_u8(payload,pos)
elseif typ == MBRIDGE_PARAM_TYPE_INT8 then -- INT8
return mb_to_i8(payload,pos)
elseif typ == MBRIDGE_PARAM_TYPE_UINT16 then -- UINT16
return mb_to_u16(payload,pos)
elseif typ == MBRIDGE_PARAM_TYPE_INT16 then -- INT16
return mb_to_i16(payload,pos)
elseif typ == MBRIDGE_PARAM_TYPE_LIST then -- LIST
return payload[pos+0]
end
return 0
end
local function mb_to_value_or_str6(payload, pos, typ)
if typ == 5 then --MBRIDGE_PARAM_TYPE_STR6 then
return mb_to_string(payload,pos,6)
else
return mb_to_value(payload,pos,typ)
end
end
--[[
local function mb_to_options(payload, pos, len)
local str = ""
for i = 0,len-1 do
if payload[pos+i] == 0 then break end
str = str .. string.char(payload[pos+i])
end
str = str .. ","
local opt = {};
for s in string.gmatch(str, "([^,]+)") do
table.insert(opt, s)
end
return opt
end
--]]
local function mb_to_options(payload, pos, len)
local r = {}
local idx = 0
local opt = ''
for i = 0,len-1 do
if payload[pos+i] == 0 then break end
if payload[pos+i] == 44 then -- 44 = ','
r[idx] = opt
idx = idx + 1
opt = ''
else
opt = opt .. string.char(payload[pos+i])
end
end
r[idx] = opt
return r
end
local function mb_to_firmware_u16_int(u16)
local major = bit32.rshift(bit32.band(u16, 0xF000), 12)
local minor = bit32.rshift(bit32.band(u16, 0x0FC0), 6)
local patch = bit32.band(u16, 0x003F)
return major * 10000 + minor * 100 + patch
end
local function mb_to_firmware_u16_string(u16)
local major = bit32.rshift(bit32.band(u16, 0xF000), 12)
local minor = bit32.rshift(bit32.band(u16, 0x0FC0), 6)
local patch = bit32.band(u16, 0x003F)
return string.format("v%d.%d.%02d", major, minor, patch)
end
local function mb_to_u8_bits(payload, pos, bitpos, bitmask)
local v = payload[pos]
v = bit32.rshift(v, bitpos)
v = bit32.band(v, bitmask)
return v
end
local function mb_allowed_mask_editable(allowed_mask)
-- if none or only one option allowed -> not editable
if allowed_mask == 0 then return false; end
if allowed_mask == 1 then return false; end
if allowed_mask == 2 then return false; end
if allowed_mask == 4 then return false; end
if allowed_mask == 8 then return false; end
if allowed_mask == 16 then return false; end
if allowed_mask == 32 then return false; end
if allowed_mask == 64 then return false; end
if allowed_mask == 128 then return false; end
if allowed_mask == 256 then return false; end
return true
end
----------------------------------------------------------------------
-- looper to send and read command frames
----------------------------------------------------------------------
local function doParamLoop()
-- trigger getting device items and param items
local t_10ms = getTime()
if t_10ms - paramloop_t_last > 33 then -- was 10 = 100 ms
paramloop_t_last = t_10ms
if t_10ms < DEVICE_SAVE_t_last + paramLoadDeadTime_10ms then
-- skip, we don't send a cmd if the last Save was recent
elseif DEVICE_ITEM_TX == nil then
cmdPush(MBRIDGE_CMD_REQUEST_INFO, {}) -- triggers sending DEVICE_ITEM_TX, DEVICE_ITEM_RX, INFO
--cmdPush(MBRIDGE_CMD_REQUEST_CMD, {MBRIDGE_CMD_REQUEST_INFO)
-- these should have been set when we nil-ed DEVICE_PARAM_LIST
DEVICE_PARAM_LIST_expected_index = 0
DEVICE_PARAM_LIST_current_index = -1
DEVICE_PARAM_LIST_errors = 0
DEVICE_PARAM_LIST_complete = false
elseif DEVICE_PARAM_LIST == nil then
if DEVICE_INFO ~= nil then -- wait for it to be populated
DEVICE_PARAM_LIST = {}
cmdPush(MBRIDGE_CMD_PARAM_REQUEST_LIST, {}) -- triggers sending full list of PARAM_ITEMs
--cmdPush(MBRIDGE_CMD_REQUEST_CMD, {MBRIDGE_CMD_PARAM_REQUEST_LIST})
end
end
end
-- handle received commands
for ijk = 1,6 do -- handle only up to 6 per lua cycle
local cmd = cmdPop()
if cmd == nil then break end
if cmd.cmd == MBRIDGE_CMD_DEVICE_ITEM_TX then
-- MBRIDGE_CMD_DEVICE_ITEM_TX
DEVICE_ITEM_TX = cmd
DEVICE_ITEM_TX.version_u16 = mb_to_u16(cmd.payload, 0)
DEVICE_ITEM_TX.setuplayout = mb_to_u16(cmd.payload, 2)
--DEVICE_ITEM_TX.name = mb_to_string(cmd.payload, 4, 20)
DEVICE_ITEM_TX.version_int = mb_to_firmware_u16_int(DEVICE_ITEM_TX.version_u16)
DEVICE_ITEM_TX.version_str = mb_to_firmware_u16_string(DEVICE_ITEM_TX.version_u16)
elseif cmd.cmd == MBRIDGE_CMD_DEVICE_ITEM_RX then
-- MBRIDGE_CMD_DEVICE_ITEM_RX
DEVICE_ITEM_RX = cmd
DEVICE_ITEM_RX.version_u16 = mb_to_u16(cmd.payload, 0)
DEVICE_ITEM_RX.setuplayout = mb_to_u16(cmd.payload, 2)
--DEVICE_ITEM_RX.name = mb_to_string(cmd.payload, 4, 20)
DEVICE_ITEM_RX.version_int = mb_to_firmware_u16_int(DEVICE_ITEM_RX.version_u16)
DEVICE_ITEM_RX.version_str = mb_to_firmware_u16_string(DEVICE_ITEM_RX.version_u16)
elseif cmd.cmd == MBRIDGE_CMD_INFO then
-- MBRIDGE_CMD_INFO
DEVICE_INFO = cmd
DEVICE_INFO.receiver_sensitivity = mb_to_i16(cmd.payload,0)
DEVICE_INFO.tx_power_dbm = mb_to_i8(cmd.payload,3)
DEVICE_INFO.rx_power_dbm = mb_to_i8(cmd.payload,4)
DEVICE_INFO.rx_available = mb_to_u8_bits(cmd.payload,5,0,0x1)
--DEVICE_INFO.tx_diversity = mb_to_u8_bits(cmd.payload,5,1,0x3)
--DEVICE_INFO.rx_diversity = mb_to_u8_bits(cmd.payload,5,3,0x3)
DEVICE_INFO.tx_config_id = mb_to_u8(cmd.payload,6)
DEVICE_INFO.tx_diversity = mb_to_u8_bits(cmd.payload,7,0,0x0F)
DEVICE_INFO.rx_diversity = mb_to_u8_bits(cmd.payload,7,4,0x0F)
elseif cmd.cmd == MBRIDGE_CMD_PARAM_ITEM then
-- MBRIDGE_CMD_PARAM_ITEM
local index = cmd.payload[0]
if index ~= DEVICE_PARAM_LIST_expected_index and index ~= 255 then
paramsError()
end
DEVICE_PARAM_LIST_current_index = index -- inform potential Item2/3 calls
DEVICE_PARAM_LIST_expected_index = index + 1 -- prepare for next
if DEVICE_PARAM_LIST == nil then
paramsError()
elseif index < 128 then
local valid,index = cmd_to_list_index(index)
if valid then
DEVICE_PARAM_LIST[index] = cmd
DEVICE_PARAM_LIST[index].typ = mb_to_u8(cmd.payload, 1)
DEVICE_PARAM_LIST[index].name = mb_to_string(cmd.payload, 2, 16)
DEVICE_PARAM_LIST[index].value = mb_to_value_or_str6(cmd.payload, 18, DEVICE_PARAM_LIST[index].typ)
DEVICE_PARAM_LIST[index].min = 0
DEVICE_PARAM_LIST[index].max = 0
DEVICE_PARAM_LIST[index].unit = ""
DEVICE_PARAM_LIST[index].options = {}
DEVICE_PARAM_LIST[index].allowed_mask = 65536
DEVICE_PARAM_LIST[index].editable = true
end
elseif index == 255 then -- EOL (end of list :)
if DEVICE_PARAM_LIST_errors == 0 then
DEVICE_PARAM_LIST_complete = true
elseif disableParamLoadErrorWarnings then -- ignore any errors
DEVICE_PARAM_LIST_complete = true
else
-- Huston, we have a proble,
DEVICE_PARAM_LIST_complete = false
setPopupWTmo("Param Upload Errors ("..tostring(DEVICE_PARAM_LIST_errors)..")!\nTry Reload", 200)
end
DEVICE_DOWNLOAD_is_running = false
else
paramsError()
end
elseif cmd.cmd == MBRIDGE_CMD_PARAM_ITEM2 then
-- MBRIDGE_CMD_PARAM_ITEM2
local index = cmd.payload[0]
if index ~= DEVICE_PARAM_LIST_current_index then
paramsError()
elseif DEVICE_PARAM_LIST == nil then
paramsError()
else
local valid,index = cmd_to_list_index(index)
if valid then
if DEVICE_PARAM_LIST[index] == nil then
paramsError()
elseif DEVICE_PARAM_LIST[index].typ < MBRIDGE_PARAM_TYPE_LIST then
DEVICE_PARAM_LIST[index].min = mb_to_value(cmd.payload, 1, DEVICE_PARAM_LIST[index].typ)
DEVICE_PARAM_LIST[index].max = mb_to_value(cmd.payload, 3, DEVICE_PARAM_LIST[index].typ)
DEVICE_PARAM_LIST[index].unit = mb_to_string(cmd.payload, 7, 6)
elseif DEVICE_PARAM_LIST[index].typ == MBRIDGE_PARAM_TYPE_LIST then
DEVICE_PARAM_LIST[index].allowed_mask = mb_to_u16(cmd.payload, 1)
DEVICE_PARAM_LIST[index].options = mb_to_options(cmd.payload, 3, 21)
DEVICE_PARAM_LIST[index].item2payload = cmd.payload
DEVICE_PARAM_LIST[index].min = 0
DEVICE_PARAM_LIST[index].max = #DEVICE_PARAM_LIST[index].options
DEVICE_PARAM_LIST[index].editable = mb_allowed_mask_editable(DEVICE_PARAM_LIST[index].allowed_mask)
elseif DEVICE_PARAM_LIST[index].typ == MBRIDGE_PARAM_TYPE_STR6 then
-- nothing to do, is send but hasn't any content
else
paramsError()
end
end
end
elseif cmd.cmd == MBRIDGE_CMD_PARAM_ITEM3 then
-- MBRIDGE_CMD_PARAM_ITEM3
local index = cmd.payload[0]
local is_item4 = false
if (index >= 128) then -- this is actually ITEM4
index = index - 128;
is_item4 = true
end
if index ~= DEVICE_PARAM_LIST_current_index then
paramsError()
elseif DEVICE_PARAM_LIST == nil then
paramsError()
else
local valid,index = cmd_to_list_index(index)
if valid then
if DEVICE_PARAM_LIST[index] == nil then
paramsError()
elseif DEVICE_PARAM_LIST[index].typ ~= MBRIDGE_PARAM_TYPE_LIST then
paramsError()
elseif DEVICE_PARAM_LIST[index].item2payload == nil then
paramsError()
elseif is_item4 and DEVICE_PARAM_LIST[index].item3payload == nil then
paramsError()
else
local s = DEVICE_PARAM_LIST[index].item2payload
if not is_item4 then
DEVICE_PARAM_LIST[index].item3payload = cmd.payload
for i=1,23 do s[23+i] = cmd.payload[i] end
DEVICE_PARAM_LIST[index].options = mb_to_options(s, 3, 21+23)
DEVICE_PARAM_LIST[index].item2payload = nil
else
local s3 = DEVICE_PARAM_LIST[index].item3payload
for i=1,23 do s[23+i] = s3[i]; s[23+23+i] = cmd.payload[i]; end
DEVICE_PARAM_LIST[index].options = mb_to_options(s, 3, 21+23+23)
DEVICE_PARAM_LIST[index].item2payload = nil
DEVICE_PARAM_LIST[index].item3payload = nil
s3 = nil
end
DEVICE_PARAM_LIST[index].max = #DEVICE_PARAM_LIST[index].options
s = nil
end
end
end
end
cmd = nil
end --for
end
local function sendParamSet(idx)
if not DEVICE_PARAM_LIST_complete then return end -- needed here??
local p = DEVICE_PARAM_LIST[idx]
local valid,idx = list_index_to_cmd(idx)
if not valid then return end
if p.typ < MBRIDGE_PARAM_TYPE_LIST then
cmdPush(MBRIDGE_CMD_PARAM_SET, {idx, p.value})
elseif p.typ == MBRIDGE_PARAM_TYPE_LIST then
cmdPush(MBRIDGE_CMD_PARAM_SET, {idx, p.value})
elseif p.typ == MBRIDGE_PARAM_TYPE_STR6 then
local cmd = {idx}
for i = 1,6 do
cmd[i+1] = string.byte(string.sub(p.value, i,i))
end
cmdPush(MBRIDGE_CMD_PARAM_SET, cmd)
end
end
local function sendParamStore()
if not DEVICE_PARAM_LIST_complete then return end -- needed here??
cmdPush(MBRIDGE_CMD_PARAM_STORE, {})
DEVICE_SAVE_t_last = getTime()
setPopupWTmo("Save Parameters", 250)
end
local function sendBind()
--if not DEVICE_PARAM_LIST_complete then return end -- needed here??
if DEVICE_DOWNLOAD_is_running then return end
cmdPush(MBRIDGE_CMD_BIND_START, {})
setPopupBlocked("Binding")
end
----------------------------------------------------------------------
-- Edit stuff
----------------------------------------------------------------------
local cursor_idx = Param1_idx
local edit = false
local option_value = 0
local cursor_pidx = 0 -- parameter idx which corresponds to the current cursor_idx
local page_param_cnt = 0 -- number of parameters available on page
local cursor_x_idx = 0 -- index into string for string edits
local bindphrase_chars = "abcdefghijklmnopqrstuvwxyz0123456789_#-."
local function cur_attr(idx) -- used in menu
local attr = TEXT_COLOR
if cursor_idx == idx then
attr = attr + INVERS
if edit then attr = attr + BLINK end
end
return attr
end
local function cur_attr_x(idx, x_idx) -- for Bind Phrase character editing
local attr = TEXT_COLOR
if DEVICE_PARAM_LIST_complete and cursor_idx == idx then
if edit then
if cursor_x_idx == x_idx then attr = attr + BLINK + INVERS end
else
attr = attr + INVERS
end
end
return attr
end
local function cur_attr_p(idx, pidx) -- used for parameters
local attr = cur_attr(idx)
return attr
end
local function param_value_inc(idx)
if not DEVICE_PARAM_LIST_complete then return end -- needed here??
local p = DEVICE_PARAM_LIST[idx]
if p.typ < MBRIDGE_PARAM_TYPE_LIST then
p.value = p.value + 1
elseif p.typ == MBRIDGE_PARAM_TYPE_LIST then
local value = p.value
while value <= p.max do
value = value + 1
local m = bit32.lshift(1,value)
if bit32.btest(m, p.allowed_mask) then p.value = value; break end
end
end
if p.value > p.max then p.value = p.max end
DEVICE_PARAM_LIST[idx].value = p.value
end
local function param_value_dec(idx)
if not DEVICE_PARAM_LIST_complete then return end -- needed here??
local p = DEVICE_PARAM_LIST[idx]
if p.typ < MBRIDGE_PARAM_TYPE_LIST then
p.value = p.value - 1
elseif p.typ == MBRIDGE_PARAM_TYPE_LIST then
local value = p.value
while value >= p.min do
value = value - 1
local m = bit32.lshift(1,value)
if bit32.btest(m, p.allowed_mask) then p.value = value; break end
end
end
if p.value < p.min then p.value = p.min end
DEVICE_PARAM_LIST[idx].value = p.value
end
local function param_str6_inc(idx)
if not DEVICE_PARAM_LIST_complete then return end -- needed here??
local p = DEVICE_PARAM_LIST[idx]
if p.typ == MBRIDGE_PARAM_TYPE_STR6 then
local c = string.sub(p.value, cursor_x_idx+1, cursor_x_idx+1)
local i = string.find(bindphrase_chars, c, 1, true) -- true for plain search
i = i + 1
if i > string.len(bindphrase_chars) then i = 1 end
c = string.sub(bindphrase_chars, i,i)
p.value = string.sub(p.value, 1, cursor_x_idx) .. c .. string.sub(p.value, cursor_x_idx+2, string.len(p.value))
end
DEVICE_PARAM_LIST[idx].value = p.value
end
local function param_str6_dec(idx)
if not DEVICE_PARAM_LIST_complete then return end -- needed here??
local p = DEVICE_PARAM_LIST[idx]
if p.typ == MBRIDGE_PARAM_TYPE_STR6 then
local c = string.sub(p.value, cursor_x_idx+1, cursor_x_idx+1)
local i = string.find(bindphrase_chars, c, 1, true) -- true for plain search
i = i - 1
if i < 1 then i = string.len(bindphrase_chars) end
c = string.sub(bindphrase_chars, i,i)
p.value = string.sub(p.value, 1, cursor_x_idx) .. c .. string.sub(p.value, cursor_x_idx+2, string.len(p.value))
end
DEVICE_PARAM_LIST[idx].value = p.value
end
local function param_str6_next(idx)
if not DEVICE_PARAM_LIST_complete then return false end -- needed here??
local p = DEVICE_PARAM_LIST[idx]
if p.typ == MBRIDGE_PARAM_TYPE_STR6 then
cursor_x_idx = cursor_x_idx + 1
if cursor_x_idx >= string.len(p.value) then
return true -- last char
end
end
return false
end
----------------------------------------------------------------------
-- Page Main
----------------------------------------------------------------------
local function drawPageMain()
local x, y;
if DEVICE_DOWNLOAD_is_running then
lcd.drawText(LCD_W/3, LCD_H-24, "MLRS", DBLSIZE+TEXT_COLOR+BLINK+INVERS)
lcd.drawText(12, LCD_H-9, "parameters loading ...", TEXT_COLOR+BLINK+INVERS)
return
end
local version_error = false
if DEVICE_ITEM_TX ~= nil and DEVICE_ITEM_TX.version_int < required_tx_mLRS_version_int then
version_error = true
popup_text = "Tx version not supported\nby this Lua script!"
end
if DEVICE_ITEM_RX ~= nil and connected and DEVICE_ITEM_RX.version_int < required_rx_mLRS_version_int then
version_error = true
popup_text = "Rx version not supported\nby this Lua script!"
end
if version_error then
drawPopup()
return
end
y = liney(0)
if DEVICE_PARAM_LIST_complete then
--lcd.drawText(0, y, DEVICE_PARAM_LIST[0].name, TEXT_COLOR)
lcd.drawText(0, y, "Phrase", TEXT_COLOR)
for i = 1,6 do
local c = string.sub(DEVICE_PARAM_LIST[0].value, i, i) -- param_idx = 0 = BindPhrase
local attr = cur_attr_x(0, i-1)
lcd.drawText(LCD_W*2/3+(i-1)*6, y, c, attr)
end
end
if DEVICE_PARAM_LIST_complete then
for i=1,PAGE_MAIN_CURSOR_IDX_PARAM_MAX do
lcd.drawText(0, liney(i), DEVICE_PARAM_LIST[i].name, TEXT_COLOR)
lcd.drawText(LCD_W*2/3, liney(i), DEVICE_PARAM_LIST[i].options[DEVICE_PARAM_LIST[i].value], cur_attr(i))
end
end
y = liney(5)
lcd.drawText(0, y, "save", cur_attr(Save_idx))
lcd.drawText(LCD_W/4, y, "load", cur_attr(Reload_idx))
lcd.drawText(LCD_W/2, y, "bind", cur_attr(Bind_idx))
y = liney(6)
attr = SMLSIZE + TEXT_COLOR
lcd.drawText(0, y, "TxPwr", attr)
if DEVICE_INFO ~= nil then
lcd.drawText(LCD_W/4, y, tostring(DEVICE_INFO.tx_power_dbm).."dBm", attr)
else
lcd.drawText(LCD_W/4, y, "--dBm", attr)
end
if DEVICE_ITEM_TX ~= nil then
lcd.drawText(LCD_W/2, y, DEVICE_ITEM_TX.version_str, attr)
else
lcd.drawText(LCD_W/2, y, "v-.--.--", attr)
end
y = liney(7)
lcd.drawText(0, y, "RxPwr", attr)
if DEVICE_INFO ~= nil and DEVICE_INFO.rx_available then
lcd.drawText(LCD_W/4, y, tostring(DEVICE_INFO.rx_power_dbm).."dBm", attr)
else
lcd.drawText(LCD_W/4, y, "--dBm", attr)
end
if not connected then
lcd.drawText(LCD_W/2, y, "disconect", attr)
elseif not DEVICE_PARAM_LIST_complete then
lcd.drawText(LCD_W/2, y, "loading..", attr)
elseif DEVICE_ITEM_RX ~= nil then
lcd.drawText(LCD_W/2, y, DEVICE_ITEM_RX.version_str, attr)
else
lcd.drawText(LCD_W/2, y, "v-.--.--", attr)
end
end
local function doPageMain(event)
if not edit then
if event == EVT_VIRTUAL_EXIT then
-- nothing to do
elseif event == EVT_VIRTUAL_ENTER then
if cursor_idx == Save_idx and DEVICE_PARAM_LIST_complete then -- Save pressed
sendParamStore()
clearParams()
elseif cursor_idx == Reload_idx then -- Reload pressed
clearParams()
elseif cursor_idx == Bind_idx then -- Bind pressed
sendBind()
elseif DEVICE_PARAM_LIST_complete then -- edit option
cursor_x_idx = 0
edit = true
end
elseif event == EVT_VIRTUAL_NEXT then -- and DEVICE_PARAM_LIST_complete then
cursor_idx = cursor_idx + 1
if cursor_idx > PAGE_MAIN_CURSOR_IDX_MAX then cursor_idx = PAGE_MAIN_CURSOR_IDX_MAX end
elseif event == EVT_VIRTUAL_PREV then -- and DEVICE_PARAM_LIST_complete then
cursor_idx = cursor_idx - 1
if cursor_idx < 0 then cursor_idx = 0 end
end
else
if event == EVT_VIRTUAL_EXIT then
if cursor_idx <= PAGE_MAIN_CURSOR_IDX_PARAM_MAX then -- BindPhrase, user defined params
sendParamSet(cursor_idx)
end
edit = false
elseif event == EVT_VIRTUAL_ENTER then
if cursor_idx == BindPhrase_idx then -- BindPhrase
if param_str6_next(0) then
sendParamSet(0)
edit = false
end
elseif cursor_idx <= PAGE_MAIN_CURSOR_IDX_PARAM_MAX then -- user defined params
sendParamSet(cursor_idx)
edit = false
else
edit = false
end
elseif event == EVT_VIRTUAL_NEXT then
if cursor_idx == BindPhrase_idx then -- BindPhrase
param_str6_inc(0)
elseif cursor_idx <= PAGE_MAIN_CURSOR_IDX_PARAM_MAX then -- user defined params
param_value_inc(cursor_idx)
end
elseif event == EVT_VIRTUAL_PREV then
if cursor_idx == BindPhrase_idx then -- BindPhrase
param_str6_dec(0)
elseif cursor_idx <= PAGE_MAIN_CURSOR_IDX_PARAM_MAX then -- user defined params
param_value_dec(cursor_idx)
end
end
end
drawPageMain()
end
----------------------------------------------------------------------
----------------------------------------------------------------------
local function Do(event)
lcd.clear()
doConnected()
if has_connected then
clearParams()
if not popup then setPopup("Receiver connected!") end
if isPopupBlocked() then clearPopup() end
end
if has_disconnected then
if not popup then setPopup("Receiver\nhas disconnected!") end
end
doParamLoop()
doPageMain(event)
doPopup()
end
----------------------------------------------------------------------
-- Script OTX Interface
----------------------------------------------------------------------
local function scriptInit()
DEVICE_DOWNLOAD_is_running = true -- we start the script with this
local tnow_10ms = getTime()
if tnow_10ms < 300 then
DEVICE_SAVE_t_last = 300 - tnow_10ms -- treat script start like a Save
else
DEVICE_SAVE_t_last = 0
end
end
local function scriptRun(event)
if event == nil then
error("Cannot be run as a model script!")
return 2
end
if model.getModule(1).Type ~= 5 then
error("mLRS not accessible: CRSF not enabled!")
return 2
end
if not edit then
if event == EVT_VIRTUAL_EXIT then
return 2
end
end
Do(event)
return 0
end
return { init=scriptInit, run=scriptRun }