├── .gitignore
├── README.md
├── SCRIPTS
├── TELEMETRY
│ └── ledvtx.lua
└── TOOLS
│ ├── ledvtx.lua
│ └── ledvtx
│ ├── com.lua
│ ├── config.lua
│ ├── config.txt
│ ├── gui.lua
│ └── msp.lua
├── screenshot.png
└── screenshot_color.png
/.gitignore:
--------------------------------------------------------------------------------
1 | *.luac
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Lua LED & VTX Switch
2 |
3 | Скрипт для переключения каналов видеопередатчика и цвета светодиодов с экрана аппаратуры.
4 |
5 |
6 |
7 | ### Установка скрипта OpenTX
8 |
9 | 1) Скачать [zip-архив](https://github.com/alexeystn/lua-vtx-switch/archive/refs/heads/master.zip) и распаковать.
10 | 2) Скопировать содержимое папки `SCRIPTS/TOOLS` из архива в папку `SCRIPTS/TOOLS` на SD карте.
11 | 3) На аппаратуре открыть меню `TOOLS` (долгим нажатием кнопки `Menu`) и выбрать `LED & VTX setup`
12 | 4) *(Опционально)*. Для быстрого доступа к скрипту на экране телеметрии
13 | 1) Положить `ledvtx.lua` из папки `SCRIPTS/TELEMETRY` из архива в папку `SCRIPTS/TELEMETRY` на SD-карте.
14 | 2) В настройках модели на странице `DISPLAY` выбрать `Script: ledvtx` для любого из экранов.
15 |
16 | ### Настройка Betaflight
17 |
18 | 1) Настроить режим светодиодов `set ledstrip_profile = STATUS`.
19 | 2) *(Опционально)*. Для более точных оттенков и равномерной яркости свечения можно использовать таблицу цветов:
20 |
21 |
22 | Таблица
23 |
24 | ```
25 | color 1 30,100,120
26 | color 2 0,0,240
27 | color 3 10,0,220
28 | color 4 30,0,180
29 | color 5 90,0,180
30 | color 6 120,0,240
31 | color 7 150,0,180
32 | color 8 180,0,120
33 | color 9 210,0,180
34 | color 10 240,0,240
35 | color 11 270,0,180
36 | color 12 300,0,120
37 | color 13 330,0,180
38 | save
39 | ```
40 |
41 |
42 |
43 | ### Возможные проблемы
44 |
45 | * Если изменяется цвет только первого светодиода, поменяйте в файле `SCRIPTS/TOOLS/ledvtx.lua` параметр `ledCount = 1` на нужное количество диодов в ленте.
46 | * Если используется Betaflight 4.4 или более ранняя версия, установите в файле `SCRIPTS/TOOLS/ledvtx.lua` для параметра `mspApiVersion` значение `45` вместо `46`.
47 | * При совместном использовании с ELRS VTX Administrator при установке видеоканала в скрипте следует выбирать вариант `* * * *`.
48 |
--------------------------------------------------------------------------------
/SCRIPTS/TELEMETRY/ledvtx.lua:
--------------------------------------------------------------------------------
1 | local script = assert(loadScript("/SCRIPTS/TOOLS/ledvtx.lua"))()
2 |
3 | return { run=script.run, init=script.init}
4 |
--------------------------------------------------------------------------------
/SCRIPTS/TOOLS/ledvtx.lua:
--------------------------------------------------------------------------------
1 | chdir("/SCRIPTS/TOOLS/LEDVTX")
2 |
3 | local toolName = "TNS|LED & VTX setup|TNE"
4 |
5 | local gui = assert(loadScript("gui.lua"))()
6 | local config = assert(loadScript("config.lua"))()
7 | local com = assert(loadScript("com.lua"))()
8 |
9 | local ledCount = 1 -- Default: 1.
10 | local mspApiVersion = 46 -- Default: 46. Set 45 if using BF4.4 or earlier.
11 |
12 | local colorNames = { "Red", "Orange", "Yellow", "Green", "Cyan", "Blue", "Violet", "White", "Black", " * * * *" }
13 | local colorIds = { 2, 3, 4, 6, 8, 10, 13, 1, 0, nil }
14 |
15 | local bandNames = { "Raceband", "Fatshark", "Lowband", " * * * *" }
16 | local bandIds = { 5, 4, 6, nil }
17 |
18 | local ledColor = 1
19 | local vtxBand = 1
20 | local vtxChannel = 1
21 |
22 | local menuPosition = 1
23 | local menuLength = 3
24 | local isItemActive = false
25 |
26 | local ITEM_LED = 1
27 | local ITEM_VTX = 2
28 | local ITEM_SAVE = 3
29 |
30 | local IDLE=1
31 | local BUSY=2
32 | local DONE=3
33 | local FAIL=4
34 |
35 | local state = IDLE
36 |
37 |
38 | local function itemIncrease()
39 | if menuPosition == ITEM_LED then
40 | if ledColor < #colorNames then
41 | ledColor = ledColor + 1
42 | end
43 | end
44 | if menuPosition == ITEM_VTX then
45 | if vtxChannel < 8 then
46 | vtxChannel = vtxChannel + 1
47 | elseif vtxBand < #bandNames then
48 | vtxBand = vtxBand + 1
49 | vtxChannel = 1
50 | end
51 | if bandIds[vtxBand] == nil then
52 | vtxChannel = 1
53 | end
54 | end
55 | end
56 |
57 |
58 | local function itemDecrease()
59 | if menuPosition == ITEM_LED then
60 | if ledColor > 1 then
61 | ledColor = ledColor - 1
62 | end
63 | end
64 | if menuPosition == ITEM_VTX then
65 | if vtxChannel > 1 then
66 | vtxChannel = vtxChannel - 1
67 | elseif vtxBand > 1 then
68 | vtxBand = vtxBand - 1
69 | vtxChannel = 8
70 | end
71 | end
72 | end
73 |
74 |
75 | local function menuMoveDown()
76 | if menuPosition < menuLength then
77 | menuPosition = menuPosition + 1
78 | end
79 | end
80 |
81 |
82 | local function menuMoveUp()
83 | if menuPosition > 1 then
84 | menuPosition = menuPosition - 1
85 | end
86 | end
87 |
88 |
89 | local function drawDisplay()
90 | lcd.clear()
91 | gui.drawSelector(1, colorNames[ledColor], menuPosition==1, isItemActive)
92 | if bandIds[vtxBand] then
93 | gui.drawSelector(2, bandNames[vtxBand] .. " " .. tostring(vtxChannel), menuPosition==2, isItemActive)
94 | else
95 | gui.drawSelector(2, bandNames[vtxBand], menuPosition==2, isItemActive)
96 | end
97 | local text, event
98 | text, event = com.getStatus()
99 | sel = (not text) and (menuPosition == ITEM_SAVE)
100 | if not text then
101 | if event == 1 then
102 | state = DONE
103 | elseif event == -1 then
104 | state = FAIL
105 | end
106 | if state == DONE then
107 | text = "Done"
108 | elseif state == FAIL then
109 | text = "Failed"
110 | else
111 | text = "Save"
112 | end
113 | else
114 | state = BUSY
115 | end
116 | gui.drawButton(text, sel)
117 | gui.drawStatus()
118 | end
119 |
120 |
121 | local function processEnterPress()
122 | if menuPosition < menuLength then
123 | isItemActive = not isItemActive
124 | else
125 | state = BUSY
126 | config.save(ledColor, vtxBand, vtxChannel)
127 | com.sendLedVtxConfig(colorIds[ledColor], bandIds[vtxBand], vtxChannel, ledCount, mspApiVersion)
128 | end
129 | end
130 |
131 |
132 | local function run_func(event)
133 | com.mainLoop()
134 | if state ~= BUSY then
135 | if isItemActive then
136 | if event == EVT_ROT_RIGHT or event == EVT_PLUS_FIRST or event == EVT_PLUS_REPT then
137 | itemIncrease()
138 | end
139 | if event == EVT_ROT_LEFT or event == EVT_MINUS_FIRST or event == EVT_MINUS_REPT then
140 | itemDecrease()
141 | end
142 | if event == EVT_EXIT_BREAK then
143 | isItemActive = false
144 | end
145 | else
146 | if event == EVT_ROT_RIGHT or event == EVT_MINUS_FIRST or event == EVT_MINUS_REPT then
147 | menuMoveDown()
148 | end
149 | if event == EVT_ROT_LEFT or event == EVT_PLUS_FIRST or event == EVT_PLUS_REPT then
150 | menuMoveUp()
151 | end
152 | if event == EVT_EXIT_BREAK then
153 | return -1
154 | end
155 | end
156 | end
157 | if event == EVT_ENTER_BREAK then
158 | processEnterPress()
159 | end
160 | if event == EVT_MENU_BREAK then
161 | com.setDebug()
162 | end
163 | if event == EVT_EXIT_BREAK then
164 | com.cancel()
165 | state = IDLE
166 | end
167 | if ((state == DONE) or (state == FAIL)) and (event == EVT_ROT_LEFT or event == EVT_ROT_RIGHT) then
168 | state = IDLE
169 | end
170 | drawDisplay()
171 | return 0
172 | end
173 |
174 |
175 | local function bg_func()
176 | com.bgLoop()
177 | end
178 |
179 |
180 | local function init_func()
181 | ledColor, vtxBand, vtxChannel = config.load_(#colorNames, #bandNames)
182 | end
183 |
184 |
185 | return { run=run_func, background=bg_func, init=init_func}
186 |
--------------------------------------------------------------------------------
/SCRIPTS/TOOLS/ledvtx/com.lua:
--------------------------------------------------------------------------------
1 | local msp = assert(loadScript("msp.lua"))()
2 |
3 | local MSP_VTX_SET_CONFIG = 89
4 | local MSP_EEPROM_WRITE = 250
5 | local MSP_SET_LED_STRIP = 49
6 | local MSP_SET_RTC = 246
7 |
8 | local isBusy = false
9 | local retryCount = 0
10 | local maxRetries = 4
11 | local retryTimeout = 200
12 | local nextTryTime = 0
13 | local nextRtcTime = 0
14 |
15 | local successFlag = false
16 | local failedFlag = false
17 |
18 | local commandSequence = {}
19 | local commandPointer = 0
20 | local currentCommand = {}
21 |
22 | local debugButtonState = false
23 |
24 | local function getDebugButtonState()
25 | if debugButtonState then
26 | debugButtonState = false
27 | return true
28 | else
29 | return false
30 | end
31 | end
32 |
33 | local function setDebugButtonState()
34 | debugButtonState = true
35 | end
36 |
37 |
38 | local function sendCurrentCommand()
39 | retryCount = retryCount + 1
40 | if retryCount > maxRetries then
41 | isBusy = false
42 | failedFlag = true
43 | end
44 | if currentCommand.write then
45 | msp.write(currentCommand.header, currentCommand.payload)
46 | else
47 | msp.read(currentCommand.header, currentCommand.payload)
48 | end
49 | nextTryTime = getTime() + retryTimeout
50 | print(currentCommand.text)
51 | end
52 |
53 |
54 | local function gotoNextCommand()
55 | if commandPointer < #commandSequence then
56 | commandPointer = commandPointer + 1
57 | currentCommand = commandSequence[commandPointer]
58 | retryCount = 0
59 | sendCurrentCommand()
60 | else
61 | successFlag = true
62 | currentCommand = nil
63 | isBusy = false
64 | end
65 | end
66 |
67 |
68 | function processMspReply(cmd, rx_buf)
69 | local key = getDebugButtonState()
70 | if (cmd == nil or rx_buf == nil) and not key then
71 | return
72 | end
73 | if isBusy and (key or (cmd == currentCommand.header)) then
74 | gotoNextCommand()
75 | end
76 | end
77 |
78 |
79 | local function startTransmission(commands)
80 | commandSequence = commands
81 | commandPointer = 0
82 | isBusy = true
83 | gotoNextCommand()
84 | end
85 |
86 |
87 | local function prepareLedCommand(color, n, version)
88 | local cmd = {}
89 | cmd.header = MSP_SET_LED_STRIP
90 | enableLarsonScanner = 0
91 |
92 | if version < 46 then
93 | cmd.payload = { n-1, (n-1)*16, 16*enableLarsonScanner, color*4, 0 }
94 | else
95 | cmd.payload = { n-1, (n-1)*16, 16*enableLarsonScanner, bit32.lshift(bit32.band(color, 0x03), 6), bit32.rshift(color, 2)}
96 | end
97 | cmd.write = true
98 | cmd.text = "Switching LED " .. tostring(n)
99 | return cmd
100 | end
101 |
102 |
103 | local function prepareVtxCommand(band, channel)
104 | local cmd = {}
105 | cmd.header = MSP_VTX_SET_CONFIG
106 | cmd.payload = { (band-1)*8 + (channel-1), 0, 1, 0 }
107 | cmd.write = true
108 | cmd.text = "Switching VTX"
109 | return cmd
110 | end
111 |
112 |
113 | local function prepareSaveCommand()
114 | local cmd = {}
115 | cmd.header = MSP_EEPROM_WRITE
116 | cmd.payload = nil
117 | cmd.write = false
118 | cmd.text = "Saving"
119 | return cmd
120 | end
121 |
122 |
123 | local function prepareRtcCommand()
124 | local now = getRtcTime()
125 | local values = {}
126 | for i = 1, 4 do
127 | values[i] = bit32.band(now, 0xFF)
128 | now = bit32.rshift(now, 8)
129 | end
130 | values[5] = 0
131 | values[6] = 0
132 | cmd = {}
133 | cmd.header = MSP_SET_RTC
134 | cmd.payload = values
135 | cmd.write = true
136 | cmd.text = "RTC"
137 | return cmd
138 | end
139 |
140 |
141 | local function sendLedVtxConfig(color, band, channel, count, version)
142 | retryCount = 0
143 | local cmd = {}
144 | if band then
145 | cmd[#cmd+1] = prepareVtxCommand(band, channel)
146 | end
147 | if color then
148 | for i = 1, count do
149 | cmd[#cmd+1] = prepareLedCommand(color, i, version)
150 | end
151 | end
152 | cmd[#cmd+1] = prepareSaveCommand()
153 | startTransmission(cmd)
154 | end
155 |
156 |
157 | local function getStatus()
158 | local text = nil
159 | local flag = 0
160 | if isBusy then
161 | if currentCommand then
162 | text = currentCommand.text .. " (" .. tostring(retryCount) .. ")"
163 | end
164 | end
165 | if successFlag then
166 | flag = 1
167 | successFlag = false
168 | end
169 | if failedFlag then
170 | flag = -1
171 | failedFlag = false
172 | end
173 | return text, flag
174 | end
175 |
176 |
177 | function comMainLoop()
178 | if isBusy then
179 | currentTime = getTime()
180 | if currentTime > nextTryTime then
181 | sendCurrentCommand()
182 | end
183 | end
184 | msp.processTxQ()
185 | processMspReply(msp.pollReply())
186 | end
187 |
188 |
189 | function cancel()
190 | isBusy = false
191 | end
192 |
193 |
194 | function comBgLoop()
195 | if getTime() > nextRtcTime then
196 | nextRtcTime = getTime() + 500
197 | rtcCommand = prepareRtcCommand()
198 | msp.write(rtcCommand.header, rtcCommand.payload)
199 | print(rtcCommand.text)
200 | end
201 | msp.processTxQ()
202 | processMspReply(msp.pollReply())
203 | end
204 |
205 |
206 | return { sendLedVtxConfig = sendLedVtxConfig, mainLoop = comMainLoop, getStatus=getStatus,
207 | cancel=cancel, bgLoop=comBgLoop, setDebug=setDebugButtonState}
208 |
--------------------------------------------------------------------------------
/SCRIPTS/TOOLS/ledvtx/config.lua:
--------------------------------------------------------------------------------
1 | local configPath = "config.txt"
2 |
3 |
4 | local function checkLimits(value, maxValue)
5 | if not value then
6 | return 1
7 | elseif value > maxValue then
8 | return maxValue
9 | elseif value < 1 then
10 | return 1
11 | else
12 | return value
13 | end
14 | end
15 |
16 |
17 | local function loadConfig(colorsCount, bandsCount)
18 | local f = io.open(configPath, "r")
19 | if f then
20 | local savedColor = tonumber(io.read(f, 2))
21 | io.read(f, 1)
22 | local savedBand = tonumber(io.read(f, 1))
23 | io.read(f, 1)
24 | local savedChannel = tonumber(io.read(f, 1))
25 | savedColor = checkLimits(savedColor, colorsCount)
26 | savedBand = checkLimits(savedBand, bandsCount)
27 | savedChannel = checkLimits(savedChannel, 8)
28 | return savedColor, savedBand, savedChannel
29 | end
30 | return 1, 1, 1
31 | end
32 |
33 |
34 | local function saveConfig(color, band, channel)
35 | local f = io.open(configPath, "w")
36 | io.write(f, string.format("%2d %1d %1d",color, band, channel))
37 | io.close(f)
38 | end
39 |
40 |
41 | return { save=saveConfig, load_=loadConfig }
42 |
--------------------------------------------------------------------------------
/SCRIPTS/TOOLS/ledvtx/config.txt:
--------------------------------------------------------------------------------
1 | 1 1 1
--------------------------------------------------------------------------------
/SCRIPTS/TOOLS/ledvtx/gui.lua:
--------------------------------------------------------------------------------
1 | local LCD_C = LCD_W / 2 + 1
2 |
3 | local w = 0 -- large screen flag
4 | if LCD_H > 96 then w = 1 end
5 |
6 |
7 | local function drawArrow(x, y, dir)
8 | for i = 0, 4+w*6 do
9 | lcd.drawLine(x+i*dir, y-i, x+i*dir, y+i, SOLID, 0)
10 | end
11 | end
12 |
13 |
14 | local function drawSelector(pos, text, isSelected, isActive)
15 | local flags = 0
16 | local offset = 0
17 | if w == 0 then
18 | flags = MIDSIZE
19 | offset = math.floor(string.len(text) * 7 / 2) + 1
20 | if isSelected then
21 | flags = flags + INVERS
22 | lcd.drawFilledRectangle(LCD_C-50, 18*(pos-1)+5, 100, 17, SOLID)
23 | end
24 | else
25 | flags = DBLSIZE + CENTER
26 | if isSelected then
27 | lcd.drawRectangle(LCD_C-120, 50*(pos-1)+60, 240, 44, SOLID, 2)
28 | end
29 | end
30 | if isActive and isSelected then
31 | drawArrow(LCD_C-57-83*w, (18+32*w)*(pos-1)+13+68*w, 1)
32 | drawArrow(LCD_C+56+83*w, (18+32*w)*(pos-1)+13+68*w, -1)
33 | end
34 | lcd.drawText(LCD_C-offset, (pos-1)*(18+32*w)+7+57*w, text, flags)
35 | end
36 |
37 |
38 | local function drawButton(text, isSelected)
39 | local flags = 0
40 | local offset = 0
41 | if w == 0 then
42 | offset = math.floor((string.len(text)*5)/2)
43 | if isSelected then
44 | flags = INVERS
45 | lcd.drawFilledRectangle(LCD_C-30, 46, 60, 12, SOLID)
46 | end
47 | else
48 | flags = MIDSIZE + CENTER
49 | if isSelected then
50 | lcd.drawRectangle(LCD_C-50, 169, 100, 32, SOLID, 2)
51 | end
52 | end
53 | lcd.drawText(LCD_C-offset, 48+122*w, text, flags)
54 | end
55 |
56 | local function drawStatus()
57 | if getRSSI() > 0 then
58 | for i = 0, 3 do
59 | x = LCD_W - 7 + i*2
60 | lcd.drawLine(x, 7, x, 7 - i*2, SOLID, 0)
61 | end
62 | end
63 | end
64 |
65 | return { drawSelector = drawSelector, drawButton = drawButton, drawStatus = drawStatus }
66 |
--------------------------------------------------------------------------------
/SCRIPTS/TOOLS/ledvtx/msp.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | The following code is the part of Betaflight TX Lua Scripts:
3 | https://github.com/betaflight/betaflight-tx-lua-scripts
4 | ]]
5 |
6 |
7 | -- Protocol version
8 | local MSP_VERSION = bit32.lshift(1,5)
9 | local MSP_STARTFLAG = bit32.lshift(1,4)
10 |
11 | -- Sequence number for next MSP packet
12 | local mspSeq = 0
13 | local mspRemoteSeq = 0
14 | local mspRxBuf = {}
15 | local mspRxSize = 0
16 | local mspRxCRC = 0
17 | local mspRxReq = 0
18 | local mspStarted = false
19 | local mspLastReq = 0
20 | local mspTxBuf = {}
21 | local mspTxIdx = 1
22 | local mspTxCRC = 0
23 |
24 | local maxTxBufferSize = 8
25 | local maxRxBufferSize = 58
26 |
27 | function mspProcessTxQ()
28 | if (#(mspTxBuf) == 0) then
29 | return false
30 | end
31 | if not crossfireTelemetryPush() then
32 | return true
33 | end
34 | local payload = {}
35 | payload[1] = mspSeq + MSP_VERSION
36 | mspSeq = bit32.band(mspSeq + 1, 0x0F)
37 | if mspTxIdx == 1 then
38 | -- start flag
39 | payload[1] = payload[1] + MSP_STARTFLAG
40 | end
41 | local i = 2
42 | while (i <= maxTxBufferSize) and mspTxIdx <= #mspTxBuf do
43 | payload[i] = mspTxBuf[mspTxIdx]
44 | mspTxIdx = mspTxIdx + 1
45 | mspTxCRC = bit32.bxor(mspTxCRC,payload[i])
46 | i = i + 1
47 | end
48 | if i <= maxTxBufferSize then
49 | payload[i] = mspTxCRC
50 | mspSend(payload)
51 | mspTxBuf = {}
52 | mspTxIdx = 1
53 | mspTxCRC = 0
54 | return false
55 | end
56 | mspSend(payload)
57 | return true
58 | end
59 |
60 | function mspSendRequest(cmd, payload)
61 | -- busy
62 | if #(mspTxBuf) ~= 0 or not cmd then
63 | return nil
64 | end
65 | mspTxBuf[1] = #(payload)
66 | mspTxBuf[2] = bit32.band(cmd,0xFF) -- MSP command
67 | for i=1,#(payload) do
68 | mspTxBuf[i+2] = bit32.band(payload[i],0xFF)
69 | end
70 | mspLastReq = cmd
71 | return mspProcessTxQ()
72 | end
73 |
74 | function mspReceivedReply(payload)
75 | local idx = 1
76 | local status = payload[idx]
77 | local err = bit32.btest(status, 0x80)
78 | local version = bit32.rshift(bit32.band(status, 0x60), 5)
79 | local start = bit32.btest(status, 0x10)
80 | local seq = bit32.band(status, 0x0F)
81 | idx = idx + 1
82 | if err then
83 | mspStarted = false
84 | return nil
85 | end
86 | if start then
87 | mspRxBuf = {}
88 | mspRxSize = payload[idx]
89 | mspRxReq = mspLastReq
90 | idx = idx + 1
91 | if version == 1 then
92 | mspRxReq = payload[idx]
93 | idx = idx + 1
94 | end
95 | mspRxCRC = bit32.bxor(mspRxSize, mspRxReq)
96 | if mspRxReq == mspLastReq then
97 | mspStarted = true
98 | end
99 | elseif not mspStarted then
100 | return nil
101 | elseif bit32.band(mspRemoteSeq + 1, 0x0F) ~= seq then
102 | mspStarted = false
103 | return nil
104 | end
105 | while (idx <= maxRxBufferSize) and (#mspRxBuf < mspRxSize) do
106 | mspRxBuf[#mspRxBuf + 1] = payload[idx]
107 | mspRxCRC = bit32.bxor(mspRxCRC, payload[idx])
108 | idx = idx + 1
109 | end
110 | if idx > maxRxBufferSize then
111 | mspRemoteSeq = seq
112 | return true
113 | end
114 | mspStarted = false
115 | -- check CRC
116 | if mspRxCRC ~= payload[idx] and version == 0 then
117 | return nil
118 | end
119 | return mspRxBuf
120 | end
121 |
122 | function mspPollReply()
123 | while true do
124 | local ret = mspPoll()
125 | if type(ret) == "table" then
126 | mspLastReq = 0
127 | return mspRxReq, ret
128 | else
129 | break
130 | end
131 | end
132 | return nil
133 | end
134 |
135 |
136 | -- CRSF Devices
137 | local CRSF_ADDRESS_BETAFLIGHT = 0xC8
138 | local CRSF_ADDRESS_RADIO_TRANSMITTER = 0xEA
139 | -- CRSF Frame Types
140 | local CRSF_FRAMETYPE_MSP_REQ = 0x7A -- response request using msp sequence as command
141 | local CRSF_FRAMETYPE_MSP_RESP = 0x7B -- reply with 60 byte chunked binary
142 | local CRSF_FRAMETYPE_MSP_WRITE = 0x7C -- write with 60 byte chunked binary
143 |
144 | crsfMspCmd = 0
145 |
146 | function mspSend(payload)
147 | local payloadOut = { CRSF_ADDRESS_BETAFLIGHT, CRSF_ADDRESS_RADIO_TRANSMITTER }
148 | for i=1, #(payload) do
149 | payloadOut[i+2] = payload[i]
150 | end
151 | return crossfireTelemetryPush(crsfMspCmd, payloadOut)
152 | end
153 |
154 | function mspRead(cmd)
155 | crsfMspCmd = CRSF_FRAMETYPE_MSP_REQ
156 | return mspSendRequest(cmd, {})
157 | end
158 |
159 | function mspWrite(cmd, payload)
160 | crsfMspCmd = CRSF_FRAMETYPE_MSP_WRITE
161 | return mspSendRequest(cmd, payload)
162 | end
163 |
164 | function mspPoll()
165 | local command, data = crossfireTelemetryPop()
166 | if command == CRSF_FRAMETYPE_MSP_RESP then
167 | if data[1] == CRSF_ADDRESS_RADIO_TRANSMITTER and data[2] == CRSF_ADDRESS_BETAFLIGHT then
168 | local mspData = {}
169 | for i=3, #(data) do
170 | mspData[i-2] = data[i]
171 | end
172 | return mspReceivedReply(mspData)
173 | end
174 | end
175 | return nil
176 | end
177 |
178 |
179 | return { processTxQ = mspProcessTxQ, pollReply = mspPollReply, write = mspWrite, read = mspRead }
180 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexeystn/lua-vtx-switch/646db58eda1224dc2b7e93a49a2c6e2e59d97f51/screenshot.png
--------------------------------------------------------------------------------
/screenshot_color.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexeystn/lua-vtx-switch/646db58eda1224dc2b7e93a49a2c6e2e59d97f51/screenshot_color.png
--------------------------------------------------------------------------------