diff --git a/README.md b/README.md index 285f02ee..e896cc35 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ -# Taranis VTx Lua Script +# VTx Lua Script - v1.1 -This stripped-down fork of [betaflight-tx-lua-scripts](https://github.com/betaflight/betaflight-tx-lua-scripts) includes only VTx configuration for Taranis transmitters only. Taranis VTx was created to save memory on your transmitter so other Lua scripts (for example [Lua Telemetry](https://github.com/iNavFlight/LuaTelemetry)) can run on the same model. This script should work with [Betaflight](https://github.com/betaflight/betaflight) as well as [INAV](https://github.com/iNavFlight/inav). +This stripped-down fork of [betaflight-tx-lua-scripts](https://github.com/betaflight/betaflight-tx-lua-scripts) includes only VTx configuration on FrSky transmitters. VTx lua script was created to save memory on your transmitter so other Lua scripts (for example [Lua Telemetry](https://github.com/iNavFlight/LuaTelemetry)) can run on the same model. This script should work with [Betaflight](https://github.com/betaflight/betaflight) as well as [INAV](https://github.com/iNavFlight/inav). ### Features * Uses less memory than [betaflight-tx-lua-scripts](https://github.com/betaflight/betaflight-tx-lua-scripts) * Allows for multiple Lua scripts to be run on the same model (for example [Lua Telemetry](https://github.com/iNavFlight/LuaTelemetry)) * Only allows for VTx control (no PID adjustments) -* Works only on Taranis transmitters running SmartPort or F.Port telemetry -* Works with [INAV](https://github.com/iNavFlight/inav) and [Betaflight](https://github.com/betaflight/betaflight) +* Works on Taranis and Horus transmitters running SmartPort or F.Port telemetry +* Works with [INAV](https://github.com/iNavFlight/inav) and probably [Betaflight](https://github.com/betaflight/betaflight) (untested) ### Installation instructions diff --git a/SCRIPTS/TELEMETRY/VTx.lua b/SCRIPTS/TELEMETRY/VTx.lua index 996c9e6e..53aae30e 100644 --- a/SCRIPTS/TELEMETRY/VTx.lua +++ b/SCRIPTS/TELEMETRY/VTx.lua @@ -5,56 +5,58 @@ SMARTPORT_REMOTE_SENSOR_ID = 0x1B FPORT_REMOTE_SENSOR_ID = 0x00 REQUEST_FRAME_ID = 0x30 REPLY_FRAME_ID = 0x32 +HORUS = LCD_W >= 480 +local fa = HORUS and 2 or 1 -MenuBox = { x=15, y=12, w=100, x_offset=36, h_line=8, h_offset=3 } -SaveBox = { x=15, y=12, w=100, x_offset=4, h=30, h_offset=5 } -NoTelem = { 30, 55, "No Telemetry", BLINK } +MenuBox = { x=15 * fa, y=12 * fa, w=100 * fa, x_offset=36 * fa, h_line=8 * (fa * 1.5), h_offset=3 * fa } +SaveBox = { x=15 * fa, y=12 * fa, w=100 * fa, x_offset=4 * fa, h=30 * fa, h_offset=5 * fa } +NoTelem = { 30 * fa, 55 * fa, "No Telemetry", BLINK } protocol = { - rssi = function() return getValue("RSSI") end, - exitFunc = function() return 0 end, - stateSensor = "Tmp1", - push = sportTelemetryPush, - maxTxBufferSize = 6, - maxRxBufferSize = 6, - saveMaxRetries = 2, - saveTimeout = 300 + rssi = function() return getValue("RSSI") end, + exitFunc = function() return 0 end, + stateSensor = "Tmp1", + push = sportTelemetryPush, + maxTxBufferSize = 6, + maxRxBufferSize = 6, + saveMaxRetries = 2, + saveTimeout = 300 } protocol.mspSend = function(payload) - local dataId = 0 - dataId = payload[1] + bit32.lshift(payload[2],8) - local value = 0 - value = payload[3] + bit32.lshift(payload[4],8) - + bit32.lshift(payload[5],16) + bit32.lshift(payload[6],24) - return sportTelemetryPush(LOCAL_SENSOR_ID, REQUEST_FRAME_ID, dataId, value) + local dataId = 0 + dataId = payload[1] + bit32.lshift(payload[2],8) + local value = 0 + value = payload[3] + bit32.lshift(payload[4],8) + + bit32.lshift(payload[5],16) + bit32.lshift(payload[6],24) + return sportTelemetryPush(LOCAL_SENSOR_ID, REQUEST_FRAME_ID, dataId, value) end protocol.mspRead = function(cmd) - return mspSendRequest(cmd, {}) + return mspSendRequest(cmd, {}) end protocol.mspWrite = function(cmd, payload) - return mspSendRequest(cmd, payload) + return mspSendRequest(cmd, payload) end protocol.mspPoll = function() - local sensorId, frameId, dataId, value = sportTelemetryPop() - if (sensorId == SMARTPORT_REMOTE_SENSOR_ID or sensorId == FPORT_REMOTE_SENSOR_ID) and frameId == REPLY_FRAME_ID then - local payload = {} - payload[1] = bit32.band(dataId,0xFF) - dataId = bit32.rshift(dataId,8) - payload[2] = bit32.band(dataId,0xFF) - payload[3] = bit32.band(value,0xFF) - value = bit32.rshift(value,8) - payload[4] = bit32.band(value,0xFF) - value = bit32.rshift(value,8) - payload[5] = bit32.band(value,0xFF) - value = bit32.rshift(value,8) - payload[6] = bit32.band(value,0xFF) - return mspReceivedReply(payload) - end - return nil + local sensorId, frameId, dataId, value = sportTelemetryPop() + if (sensorId == SMARTPORT_REMOTE_SENSOR_ID or sensorId == FPORT_REMOTE_SENSOR_ID) and frameId == REPLY_FRAME_ID then + local payload = {} + payload[1] = bit32.band(dataId,0xFF) + dataId = bit32.rshift(dataId,8) + payload[2] = bit32.band(dataId,0xFF) + payload[3] = bit32.band(value,0xFF) + value = bit32.rshift(value,8) + payload[4] = bit32.band(value,0xFF) + value = bit32.rshift(value,8) + payload[5] = bit32.band(value,0xFF) + value = bit32.rshift(value,8) + payload[6] = bit32.band(value,0xFF) + return mspReceivedReply(payload) + end + return nil end assert(loadScript(SCRIPT_HOME.."/common.luac", "T"))() @@ -70,16 +72,22 @@ local MENU_TIMESLICE = 100 local lastMenuEvent = 0 function run(event) - lastMenuEvent = getTime() - collectgarbage() - run_ui(event) + lastMenuEvent = getTime() + collectgarbage() + run_ui(event) + return 0 end function run_bg() - if lastMenuEvent + MENU_TIMESLICE < getTime() then - background.run_bg() - collectgarbage() - end + if lastMenuEvent + MENU_TIMESLICE < getTime() then + background.run_bg() + collectgarbage() + end + return 0 +end + +function init() + return 0 end return { init=background.init, run=run, background=run_bg } diff --git a/SCRIPTS/TELEMETRY/VTx/background.lua b/SCRIPTS/TELEMETRY/VTx/background.lua index 58b1ae05..7f37ba2c 100644 --- a/SCRIPTS/TELEMETRY/VTx/background.lua +++ b/SCRIPTS/TELEMETRY/VTx/background.lua @@ -8,86 +8,86 @@ local timeIsSet = false local mspMsgQueued = false local function getSensorValue() - if sensorId == -1 then - local sensor = getFieldInfo(protocol.stateSensor) - if type(sensor) == "table" then - sensorId = sensor['id'] or -1 - end - end - return getValue(sensorId) + if sensorId == -1 then + local sensor = getFieldInfo(protocol.stateSensor) + if type(sensor) == "table" then + sensorId = sensor['id'] or -1 + end + end + return getValue(sensorId) end local function modelActive(sensorValue) - return type(sensorValue) == "number" and sensorValue > 0 + return type(sensorValue) == "number" and sensorValue > 0 end local function init() - lastRunTS = 0 + lastRunTS = 0 end local function run_bg() - -- run in intervals - - if lastRunTS == 0 or lastRunTS + INTERVAL < getTime() then - mspMsgQueued = false - -- ------------------------------------ - -- SYNC DATE AND TIME - -- ------------------------------------ - local sensorValue = getSensorValue() - - if not timeIsSet and modelActive(sensorValue) then - -- Send datetime when the telemetry connection is available - -- assuming when sensor value higher than 0 there is an telemetry connection - -- only send datetime one time after telemetry connection became available - -- or when connection is restored after e.g. lipo refresh - local now = getDateTime() - local year = now.year; - - values = {} - values[1] = bit32.band(year, 0xFF) - year = bit32.rshift(year, 8) - values[2] = bit32.band(year, 0xFF) - values[3] = now.mon - values[4] = now.day - values[5] = now.hour - values[6] = now.min - values[7] = now.sec - - -- send msp message - protocol.mspWrite(MSP_SET_RTC, values) - mspMsgQueued = true - - timeIsSet = true - elseif not modelActive(sensorValue) then - timeIsSet = false - end - - - -- ------------------------------------ - -- SEND RSSI VALUE - -- ------------------------------------ - - if mspMsgQueued == false then - local rssi, alarm_low, alarm_crit = getRSSI() - -- Scale the [0, 85] (empirical) RSSI values to [0, 255] - rssi = rssi * 3 - if rssi > 255 then - rssi = 255 - end - - values = {} - values[1] = rssi - - -- send msp message - protocol.mspWrite(MSP_TX_INFO, values) - mspMsgQueued = true - end - - lastRunTS = getTime() - end - - -- process queue - mspProcessTxQ() + -- run in intervals + + if lastRunTS == 0 or lastRunTS + INTERVAL < getTime() then + mspMsgQueued = false + -- ------------------------------------ + -- SYNC DATE AND TIME + -- ------------------------------------ + local sensorValue = getSensorValue() + + if not timeIsSet and modelActive(sensorValue) then + -- Send datetime when the telemetry connection is available + -- assuming when sensor value higher than 0 there is an telemetry connection + -- only send datetime one time after telemetry connection became available + -- or when connection is restored after e.g. lipo refresh + local now = getDateTime() + local year = now.year; + + values = {} + values[1] = bit32.band(year, 0xFF) + year = bit32.rshift(year, 8) + values[2] = bit32.band(year, 0xFF) + values[3] = now.mon + values[4] = now.day + values[5] = now.hour + values[6] = now.min + values[7] = now.sec + + -- send msp message + protocol.mspWrite(MSP_SET_RTC, values) + mspMsgQueued = true + + timeIsSet = true + elseif not modelActive(sensorValue) then + timeIsSet = false + end + + + -- ------------------------------------ + -- SEND RSSI VALUE + -- ------------------------------------ + + if mspMsgQueued == false then + local rssi, alarm_low, alarm_crit = getRSSI() + -- Scale the [0, 85] (empirical) RSSI values to [0, 255] + rssi = rssi * 3 + if rssi > 255 then + rssi = 255 + end + + values = {} + values[1] = rssi + + -- send msp message + protocol.mspWrite(MSP_TX_INFO, values) + mspMsgQueued = true + end + + lastRunTS = getTime() + end + + -- process queue + mspProcessTxQ() end diff --git a/SCRIPTS/TELEMETRY/VTx/common.lua b/SCRIPTS/TELEMETRY/VTx/common.lua index 21c22572..f4b66711 100644 --- a/SCRIPTS/TELEMETRY/VTx/common.lua +++ b/SCRIPTS/TELEMETRY/VTx/common.lua @@ -20,119 +20,121 @@ local mspTxPk = 0 mspPendingRequest = false function mspProcessTxQ() - if (#(mspTxBuf) == 0) then - return false - end - if not protocol.push() then - return true - end - local payload = {} - payload[1] = mspSeq + MSP_VERSION - mspSeq = bit32.band(mspSeq + 1, 0x0F) - if mspTxIdx == 1 then - -- start flag - payload[1] = payload[1] + MSP_STARTFLAG - end - local i = 2 - while (i <= protocol.maxTxBufferSize) do - if mspTxIdx > #(mspTxBuf) then - break - end - payload[i] = mspTxBuf[mspTxIdx] - mspTxIdx = mspTxIdx + 1 - mspTxCRC = bit32.bxor(mspTxCRC,payload[i]) - i = i + 1 - end - if i <= protocol.maxTxBufferSize then - payload[i] = mspTxCRC - i = i + 1 - -- zero fill - while i <= protocol.maxTxBufferSize do - payload[i] = 0 - i = i + 1 - end - if protocol.mspSend(payload) then - mspTxPk = mspTxPk + 1 - end - mspTxBuf = {} - mspTxIdx = 1 - mspTxCRC = 0 - return false - end - if protocol.mspSend(payload) then - mspTxPk = mspTxPk + 1 - end - return true + if (#(mspTxBuf) == 0) then + return false + end + if not protocol.push() then + return true + end + local payload = {} + payload[1] = mspSeq + MSP_VERSION + mspSeq = bit32.band(mspSeq + 1, 0x0F) + if mspTxIdx == 1 then + -- start flag + payload[1] = payload[1] + MSP_STARTFLAG + end + local i = 2 + while (i <= protocol.maxTxBufferSize) do + if mspTxIdx > #(mspTxBuf) then + break + end + payload[i] = mspTxBuf[mspTxIdx] + mspTxIdx = mspTxIdx + 1 + mspTxCRC = bit32.bxor(mspTxCRC,payload[i]) + i = i + 1 + end + if i <= protocol.maxTxBufferSize then + payload[i] = mspTxCRC + i = i + 1 + -- zero fill + while i <= protocol.maxTxBufferSize do + payload[i] = 0 + i = i + 1 + end + if protocol.mspSend(payload) then + mspTxPk = mspTxPk + 1 + end + mspTxBuf = {} + mspTxIdx = 1 + mspTxCRC = 0 + return false + end + if protocol.mspSend(payload) then + mspTxPk = mspTxPk + 1 + end + return true end function mspSendRequest(cmd, payload) - -- busy - if #(mspTxBuf) ~= 0 or not cmd then - return nil - end - mspTxBuf[1] = #(payload) - mspTxBuf[2] = bit32.band(cmd,0xFF) -- MSP command - for i=1,#(payload) do - mspTxBuf[i+2] = bit32.band(payload[i],0xFF) - end - mspLastReq = cmd - return mspProcessTxQ() + -- busy + if #(mspTxBuf) ~= 0 or not cmd then + return nil + end + mspTxBuf[1] = #(payload) + mspTxBuf[2] = bit32.band(cmd,0xFF) -- MSP command + for i=1,#(payload) do + mspTxBuf[i+2] = bit32.band(payload[i],0xFF) + end + mspLastReq = cmd + return mspProcessTxQ() end function mspReceivedReply(payload) - local idx = 1 - local head = payload[idx] - local err_flag = (bit32.band(head,0x20) ~= 0) - idx = idx + 1 - if err_flag then - -- error flag set - mspStarted = false - return nil - end - local start = (bit32.band(head,0x10) ~= 0) - local seq = bit32.band(head,0x0F) - if start then - -- start flag set - mspRxIdx = 1 - mspRxBuf = {} - mspRxSize = payload[idx] - mspRxCRC = bit32.bxor(mspRxSize,mspLastReq) - mspRxReq = mspLastReq - idx = idx + 1 - mspStarted = true - elseif not mspStarted then - return nil - elseif bit32.band(mspRemoteSeq + 1, 0x0F) ~= seq then - mspStarted = false - return nil - end - while (idx <= protocol.maxRxBufferSize) and (mspRxIdx <= mspRxSize) do - mspRxBuf[mspRxIdx] = payload[idx] - mspRxCRC = bit32.bxor(mspRxCRC,payload[idx]) - mspRxIdx = mspRxIdx + 1 - idx = idx + 1 - end - if idx > protocol.maxRxBufferSize then - mspRemoteSeq = seq - return true - end - -- check CRC - if mspRxCRC ~= payload[idx] then - mspStarted = false - return nil - end - mspStarted = false - return mspRxBuf + local idx = 1 + local head = payload[idx] + local err_flag = (bit32.band(head,0x20) ~= 0) + idx = idx + 1 + if err_flag then + -- error flag set + mspStarted = false + return nil + end + local start = (bit32.band(head,0x10) ~= 0) + local seq = bit32.band(head,0x0F) + if start then + -- start flag set + mspRxIdx = 1 + mspRxBuf = {} + mspRxSize = payload[idx] + mspRxCRC = bit32.bxor(mspRxSize,mspLastReq) + mspRxReq = mspLastReq + idx = idx + 1 + mspStarted = true + elseif not mspStarted then + return nil + elseif bit32.band(mspRemoteSeq + 1, 0x0F) ~= seq then + mspStarted = false + return nil + end + while (idx <= protocol.maxRxBufferSize) and (mspRxIdx <= mspRxSize) do + mspRxBuf[mspRxIdx] = payload[idx] + mspRxCRC = bit32.bxor(mspRxCRC,payload[idx]) + mspRxIdx = mspRxIdx + 1 + idx = idx + 1 + end + if idx > protocol.maxRxBufferSize then + mspRemoteSeq = seq + return true + end + -- check CRC + if mspRxCRC ~= payload[idx] then + mspStarted = false + return nil + end + mspStarted = false + return mspRxBuf end function mspPollReply() - while true do - ret = protocol.mspPoll() - if type(ret) == "table" then - return mspRxReq, ret - else - break - end - end - return nil + while true do + ret = protocol.mspPoll() + if type(ret) == "table" then + return mspRxReq, ret + else + break + end + end + return nil end + +return 0 \ No newline at end of file diff --git a/SCRIPTS/TELEMETRY/VTx/common.luac b/SCRIPTS/TELEMETRY/VTx/common.luac index 68ae4a64..91e8bcdc 100644 Binary files a/SCRIPTS/TELEMETRY/VTx/common.luac and b/SCRIPTS/TELEMETRY/VTx/common.luac differ diff --git a/SCRIPTS/TELEMETRY/VTx/ui.lua b/SCRIPTS/TELEMETRY/VTx/ui.lua index bc789749..a93fe0ed 100644 --- a/SCRIPTS/TELEMETRY/VTx/ui.lua +++ b/SCRIPTS/TELEMETRY/VTx/ui.lua @@ -1,43 +1,41 @@ local userEvent = { - press = { - minus = EVT_MINUS_FIRST, - plus = EVT_PLUS_FIRST, - pageDown = EVT_PAGEDN_FIRST, - pageUp = EVT_PAGEUP_FIRST or EVT_LEFT_BREAK - }, - longPress = { - enter = EVT_ENTER_LONG, - menu = EVT_MENU_LONG or EVT_RIGHT_LONG - }, - repeatPress = { - minus = EVT_MINUS_REPT, - plus = EVT_PLUS_REPT - }, - release = { - enter = EVT_ENTER_BREAK, - exit = EVT_EXIT_BREAK, - menu = EVT_MENU_BREAK or EVT_RIGHT_BREAK, - minus = EVT_MINUS_BREAK, - plus = EVT_PLUS_BREAK - }, - dial = { - left = EVT_ROT_LEFT or EVT_UP_BREAK, - right = EVT_ROT_RIGHT or EVT_DOWN_BREAK - } + press = { + minus = EVT_MINUS_FIRST, + plus = EVT_PLUS_FIRST, + pageDown = EVT_PAGEDN_FIRST, + pageUp = EVT_PAGEUP_FIRST or EVT_LEFT_BREAK + }, + longPress = { + enter = EVT_ENTER_LONG, + menu = EVT_MENU_LONG or EVT_RIGHT_LONG + }, + repeatPress = { + minus = EVT_MINUS_REPT, + plus = EVT_PLUS_REPT + }, + release = { + enter = EVT_ENTER_BREAK, + exit = EVT_EXIT_BREAK, + menu = EVT_MENU_BREAK or EVT_RIGHT_BREAK, + minus = EVT_MINUS_BREAK, + plus = EVT_PLUS_BREAK + }, + dial = { + left = EVT_ROT_LEFT or EVT_UP_BREAK, + right = EVT_ROT_RIGHT or EVT_DOWN_BREAK + } } -local pageStatus = -{ - display = 2, - editing = 3, - saving = 4, - displayMenu = 5, +local pageStatus = { + display = 2, + editing = 3, + saving = 4, + displayMenu = 5, } -local uiMsp = -{ - reboot = 68, - eepromWrite = 250 +local uiMsp = { + reboot = 68, + eepromWrite = 250 } local currentState = pageStatus.display @@ -56,107 +54,107 @@ local killEnterBreak = 0 Page = nil -backgroundFill = backgroundFill or ERASE -foregroundColor = foregroundColor or SOLID +backgroundFill = HORUS and MENU_TITLE_BGCOLOR or (backgroundFill or ERASE) +foregroundColor = HORUS and MENU_TITLE_COLOR or (foregroundColor or SOLID) globalTextOptions = globalTextOptions or 0 local function saveSettings(new) - if Page.values then - if Page.preSave then - payload = Page.preSave(Page) - else - payload = Page.values - end - protocol.mspWrite(Page.write, payload) - saveTS = getTime() - if currentState == pageStatus.saving then - saveRetries = saveRetries + 1 - else - currentState = pageStatus.saving - saveRetries = 0 - saveMaxRetries = protocol.saveMaxRetries or 2 -- default 2 - saveTimeout = protocol.saveTimeout or 150 -- default 1.5s - end - end + if Page.values then + if Page.preSave then + payload = Page.preSave(Page) + else + payload = Page.values + end + protocol.mspWrite(Page.write, payload) + saveTS = getTime() + if currentState == pageStatus.saving then + saveRetries = saveRetries + 1 + else + currentState = pageStatus.saving + saveRetries = 0 + saveMaxRetries = protocol.saveMaxRetries or 2 -- default 2 + saveTimeout = protocol.saveTimeout or 150 -- default 1.5s + end + end end local function invalidatePages() - Page = nil - currentState = pageStatus.display - saveTS = 0 + Page = nil + currentState = pageStatus.display + saveTS = 0 end local function rebootFc() - protocol.mspRead(uiMsp.reboot) - invalidatePages() + protocol.mspRead(uiMsp.reboot) + invalidatePages() end local function eepromWrite() - protocol.mspRead(uiMsp.eepromWrite) + protocol.mspRead(uiMsp.eepromWrite) end local menuList = { - { - t = "save page", - f = saveSettings - }, - { - t = "reload", - f = invalidatePages - }, - { - t = "reboot", - f = rebootFc - } + { + t = "save page", + f = saveSettings + }, + { + t = "reload", + f = invalidatePages + }, + { + t = "reboot", + f = rebootFc + } } local function processMspReply(cmd,rx_buf) - if cmd == nil or rx_buf == nil then - return - end - if cmd == Page.write then - if Page.eepromWrite then - eepromWrite() - else - invalidatePages() - end - pageRequested = false - return - end - if cmd == uiMsp.eepromWrite then - if Page.reboot then - rebootFc() - end - invalidatePages() - return - end - if cmd ~= Page.read then - return - end - if #(rx_buf) > 0 then - Page.values = {} - for i=1,#(rx_buf) do - Page.values[i] = rx_buf[i] - end + if cmd == nil or rx_buf == nil then + return + end + if cmd == Page.write then + if Page.eepromWrite then + eepromWrite() + else + invalidatePages() + end + pageRequested = false + return + end + if cmd == uiMsp.eepromWrite then + if Page.reboot then + rebootFc() + end + invalidatePages() + return + end + if cmd ~= Page.read then + return + end + if #(rx_buf) > 0 then + Page.values = {} + for i=1,#(rx_buf) do + Page.values[i] = rx_buf[i] + end - for i=1,#(Page.fields) do - if (#(Page.values) or 0) >= Page.minBytes then - local f = Page.fields[i] - if f.vals then - f.value = 0; - for idx=1, #(f.vals) do - local raw_val = (Page.values[f.vals[idx]] or 0) - raw_val = bit32.lshift(raw_val, (idx-1)*8) - f.value = bit32.bor(f.value, raw_val) - end - f.value = f.value/(f.scale or 1) - end - end - end - if Page.postLoad then - Page.postLoad(Page) - end - end + for i=1,#(Page.fields) do + if (#(Page.values) or 0) >= Page.minBytes then + local f = Page.fields[i] + if f.vals then + f.value = 0; + for idx=1, #(f.vals) do + local raw_val = (Page.values[f.vals[idx]] or 0) + raw_val = bit32.lshift(raw_val, (idx-1)*8) + f.value = bit32.bor(f.value, raw_val) + end + f.value = f.value/(f.scale or 1) + end + end + end + if Page.postLoad then + Page.postLoad(Page) + end + end end local function incMax(val, inc, base) @@ -172,202 +170,207 @@ local function incMenu(inc) end local function requestPage() - if Page.read and ((Page.reqTS == nil) or (Page.reqTS + requestTimeout <= getTime())) then - Page.reqTS = getTime() - protocol.mspRead(Page.read) - end + if Page.read and ((Page.reqTS == nil) or (Page.reqTS + requestTimeout <= getTime())) then + Page.reqTS = getTime() + protocol.mspRead(Page.read) + end end function drawScreenTitle(screen_title) - lcd.drawFilledRectangle(0, 0, LCD_W, 10) - lcd.drawText(1,1,screen_title,INVERS) + if HORUS then + lcd.drawFilledRectangle(0, 0, LCD_W, 20, TEXT_INVERTED_BGCOLOR) + else + lcd.drawFilledRectangle(0, 0, LCD_W, 10) + end + lcd.drawText(1,HORUS and 0 or 1,screen_title,INVERS) end local function drawScreen() - local screen_title = Page.title - drawScreenTitle(screen_title) - for i=1,#(Page.text) do - local f = Page.text[i] - local textOptions = (f.to or 0) + globalTextOptions - lcd.drawText(f.x, f.y, f.t, textOptions) - end - local val = "---" - for i=1,#(Page.fields) do - local f = Page.fields[i] - local text_options = (f.to or 0) + globalTextOptions - local heading_options = text_options - local value_options = text_options - if i == currentLine then - value_options = text_options + INVERS - if currentState == pageStatus.editing then - value_options = value_options + BLINK - end - end - local spacing = 20 - if f.t ~= nil then - lcd.drawText(f.x, f.y, f.t, heading_options) - if f.sp ~= nil then - spacing = f.sp - end - else - spacing = 0 - end - if f.value then - if f.upd and Page.values then - f.upd(Page) - end - val = f.value - if f.table and f.table[f.value] then - val = f.table[f.value] - end - end - lcd.drawText(f.x + spacing, f.y, val, value_options) - end + local screen_title = Page.title + drawScreenTitle(screen_title) + for i=1,#(Page.text) do + local f = Page.text[i] + local textOptions = (f.to or 0) + globalTextOptions + lcd.drawText(f.x, f.y, f.t, textOptions) + end + local val = "---" + for i=1,#(Page.fields) do + local f = Page.fields[i] + local text_options = (f.to or 0) + globalTextOptions + local heading_options = text_options + local value_options = text_options + if i == currentLine then + value_options = text_options + INVERS + if currentState == pageStatus.editing then + value_options = value_options + BLINK + end + end + local spacing = 20 + if f.t ~= nil then + lcd.drawText(f.x, f.y, f.t, heading_options) + if f.sp ~= nil then + spacing = f.sp + end + else + spacing = 0 + end + if f.value then + if f.upd and Page.values then + f.upd(Page) + end + val = f.value + if f.table and f.table[f.value] then + val = f.table[f.value] + end + end + lcd.drawText(f.x + spacing, f.y, val, value_options) + end end function clipValue(val,min,max) - if val < min then - val = min - elseif val > max then - val = max - end - return val + if val < min then + val = min + elseif val > max then + val = max + end + return val end local function getCurrentField() - return Page.fields[currentLine] + return Page.fields[currentLine] end local function incValue(inc) - local f = Page.fields[currentLine] - local idx = f.i or currentLine - local scale = (f.scale or 1) - f.value = clipValue(f.value + ((inc*(f.mult or 1))/scale), (f.min/scale) or 0, (f.max/scale) or 255) - for idx=1, #(f.vals) do - Page.values[f.vals[idx]] = bit32.rshift(f.value * scale, (idx-1)*8) - end - if f.upd and Page.values then - f.upd(Page) - end + local f = Page.fields[currentLine] + local idx = f.i or currentLine + local scale = (f.scale or 1) + f.value = clipValue(f.value + ((inc*(f.mult or 1))/scale), (f.min/scale) or 0, (f.max/scale) or 255) + for idx=1, #(f.vals) do + Page.values[f.vals[idx]] = bit32.rshift(f.value * scale, (idx-1)*8) + end + if f.upd and Page.values then + f.upd(Page) + end end local function drawMenu() - local x = MenuBox.x - local y = MenuBox.y - local w = MenuBox.w - local h_line = MenuBox.h_line - local h_offset = MenuBox.h_offset - local h = #(menuList) * h_line + h_offset*2 + local x = MenuBox.x + local y = MenuBox.y + local w = MenuBox.w + local h_line = MenuBox.h_line + local h_offset = MenuBox.h_offset + local h = #(menuList) * h_line + h_offset*2 - lcd.drawFilledRectangle(x,y,w,h,backgroundFill) - lcd.drawRectangle(x,y,w-1,h-1,foregroundColor) - lcd.drawText(x+h_line/2,y+h_offset,"Menu:",globalTextOptions) + lcd.drawFilledRectangle(x,y,w,h,backgroundFill) + lcd.drawRectangle(x,y,w-1,h-1,foregroundColor) + lcd.drawText(x+h_line/2,y+h_offset,"Menu:",globalTextOptions) - for i,e in ipairs(menuList) do - local text_options = globalTextOptions - if menuActive == i then - text_options = text_options + INVERS - end - lcd.drawText(x+MenuBox.x_offset,y+(i-1)*h_line+h_offset,e.t,text_options) - end + for i,e in ipairs(menuList) do + local text_options = globalTextOptions + if menuActive == i then + text_options = text_options + INVERS + end + lcd.drawText(x+MenuBox.x_offset,y+(i-1)*h_line+h_offset,e.t,text_options) + end end function run_ui(event) - local now = getTime() - -- if lastRunTS old than 500ms - if lastRunTS + 50 < now then - invalidatePages() - end - lastRunTS = now - if (currentState == pageStatus.saving) then - if (saveTS + saveTimeout < now) then - if saveRetries < saveMaxRetries then - saveSettings() - else - -- max retries reached - currentState = pageStatus.display - invalidatePages() - end - end - end - -- process send queue - mspProcessTxQ() - -- navigation - if (event == userEvent.longPress.menu) then -- Taranis QX7 / X9 - menuActive = 1 - currentState = pageStatus.displayMenu - elseif userEvent.press.pageDown and (event == userEvent.longPress.enter) then -- Horus - menuActive = 1 - killEnterBreak = 1 - currentState = pageStatus.displayMenu - -- menu is currently displayed - elseif currentState == pageStatus.displayMenu then - if event == userEvent.release.exit then - currentState = pageStatus.display - elseif event == userEvent.release.plus or event == userEvent.dial.left then - incMenu(-1) - elseif event == userEvent.release.minus or event == userEvent.dial.right then - incMenu(1) - elseif event == userEvent.release.enter then - if killEnterBreak == 1 then - killEnterBreak = 0 - else - currentState = pageStatus.display - menuList[menuActive].f() - end - end - -- normal page viewing - elseif currentState <= pageStatus.display then - if event == userEvent.release.plus or event == userEvent.dial.left then - incLine(-1) - elseif event == userEvent.release.minus or event == userEvent.dial.right then - incLine(1) - elseif event == userEvent.release.enter then - local field = Page.fields[currentLine] - local idx = field.i or currentLine - if Page.values and Page.values[idx] and (field.ro ~= true) then - currentState = pageStatus.editing - end - elseif event == userEvent.release.exit then - return protocol.exitFunc(); - end - -- editing value - elseif currentState == pageStatus.editing then - if (event == userEvent.release.exit) or (event == userEvent.release.enter) then - currentState = pageStatus.display - elseif event == userEvent.press.plus or event == userEvent.repeatPress.plus or event == userEvent.dial.right then - incValue(1) - elseif event == userEvent.press.minus or event == userEvent.repeatPress.minus or event == userEvent.dial.left then - incValue(-1) - end - end - if Page == nil then - Page = assert(loadScript(SCRIPT_HOME .. "/VTx.luac", "T"))() - end - if not Page.values and currentState == pageStatus.display then - requestPage() - end - lcd.clear() - if TEXT_BGCOLOR then - lcd.drawFilledRectangle(0, 0, LCD_W, LCD_H, TEXT_BGCOLOR) - end - drawScreen() - if protocol.rssi() == 0 then - lcd.drawText(NoTelem[1],NoTelem[2],NoTelem[3],NoTelem[4]) - end - if currentState == pageStatus.displayMenu then - drawMenu() - elseif currentState == pageStatus.saving then - lcd.drawFilledRectangle(SaveBox.x,SaveBox.y,SaveBox.w,SaveBox.h,backgroundFill) - lcd.drawRectangle(SaveBox.x,SaveBox.y,SaveBox.w,SaveBox.h,SOLID) - if saveRetries <= 0 then - lcd.drawText(SaveBox.x+SaveBox.x_offset,SaveBox.y+SaveBox.h_offset,"Saving...",DBLSIZE + BLINK + (globalTextOptions)) - else - lcd.drawText(SaveBox.x+SaveBox.x_offset,SaveBox.y+SaveBox.h_offset,"Retrying",DBLSIZE + (globalTextOptions)) - end - end - processMspReply(mspPollReply()) - return 0 + local now = getTime() + -- if lastRunTS old than 500ms + if lastRunTS + 50 < now then + invalidatePages() + end + lastRunTS = now + if (currentState == pageStatus.saving) then + if (saveTS + saveTimeout < now) then + if saveRetries < saveMaxRetries then + saveSettings() + else + -- max retries reached + currentState = pageStatus.display + invalidatePages() + end + end + end + -- process send queue + mspProcessTxQ() + -- navigation + if (event == userEvent.longPress.menu) then -- Taranis QX7 / X9 + menuActive = 1 + currentState = pageStatus.displayMenu + elseif userEvent.press.pageDown and (event == userEvent.longPress.enter) then -- Horus + menuActive = 1 + killEnterBreak = 1 + currentState = pageStatus.displayMenu + -- menu is currently displayed + elseif currentState == pageStatus.displayMenu then + if event == userEvent.release.exit then + currentState = pageStatus.display + elseif event == userEvent.release.plus or event == userEvent.dial.left then + incMenu(-1) + elseif event == userEvent.release.minus or event == userEvent.dial.right then + incMenu(1) + elseif event == userEvent.release.enter then + if killEnterBreak == 1 then + killEnterBreak = 0 + else + currentState = pageStatus.display + menuList[menuActive].f() + end + end + -- normal page viewing + elseif currentState <= pageStatus.display then + if event == userEvent.release.plus or event == userEvent.dial.left then + incLine(-1) + elseif event == userEvent.release.minus or event == userEvent.dial.right then + incLine(1) + elseif event == userEvent.release.enter then + local field = Page.fields[currentLine] + local idx = field.i or currentLine + if Page.values and Page.values[idx] and (field.ro ~= true) then + currentState = pageStatus.editing + end + elseif event == userEvent.release.exit then + return protocol.exitFunc(); + end + -- editing value + elseif currentState == pageStatus.editing then + if (event == userEvent.release.exit) or (event == userEvent.release.enter) then + currentState = pageStatus.display + elseif event == userEvent.press.plus or event == userEvent.repeatPress.plus or event == userEvent.dial.right then + incValue(1) + elseif event == userEvent.press.minus or event == userEvent.repeatPress.minus or event == userEvent.dial.left then + incValue(-1) + end + end + if Page == nil then + Page = assert(loadScript(SCRIPT_HOME .. "/VTx.luac", "T"))() + end + if not Page.values and currentState == pageStatus.display then + requestPage() + end + if HORUS then + lcd.clear(TEXT_BGCOLOR) + else + lcd.clear() + end + drawScreen() + if protocol.rssi() == 0 then + lcd.drawText(NoTelem[1],NoTelem[2],NoTelem[3],NoTelem[4]) + end + if currentState == pageStatus.displayMenu then + drawMenu() + elseif currentState == pageStatus.saving then + lcd.drawFilledRectangle(SaveBox.x,SaveBox.y,SaveBox.w,SaveBox.h,backgroundFill) + lcd.drawRectangle(SaveBox.x,SaveBox.y,SaveBox.w,SaveBox.h,SOLID) + if saveRetries <= 0 then + lcd.drawText(SaveBox.x+SaveBox.x_offset,SaveBox.y+SaveBox.h_offset,"Saving...",DBLSIZE + BLINK + (globalTextOptions)) + else + lcd.drawText(SaveBox.x+SaveBox.x_offset,SaveBox.y+SaveBox.h_offset,"Retrying",DBLSIZE + (globalTextOptions)) + end + end + processMspReply(mspPollReply()) + return 0 end return run_ui diff --git a/SCRIPTS/TELEMETRY/VTx/ui.luac b/SCRIPTS/TELEMETRY/VTx/ui.luac index a8445b43..3607c8dc 100644 Binary files a/SCRIPTS/TELEMETRY/VTx/ui.luac and b/SCRIPTS/TELEMETRY/VTx/ui.luac differ diff --git a/SCRIPTS/TELEMETRY/VTx/vtx.lua b/SCRIPTS/TELEMETRY/VTx/vtx.lua index 139ba155..a2f61b86 100644 --- a/SCRIPTS/TELEMETRY/VTx/vtx.lua +++ b/SCRIPTS/TELEMETRY/VTx/vtx.lua @@ -1,242 +1,244 @@ +local fa = HORUS and 2 or 1 +local fs = HORUS and 0 or SMLSIZE return { - read = 88, -- MSP_VTX_CONFIG - write = 89, -- MSP_VTX_SET_CONFIG - eepromWrite = true, - reboot = false, - title = "Taranis VTX", - minBytes = 5, - prevBandVal = 0, - prevChanVal = 0, - prevFreqVal = 0, - lastFreqUpdTS = 0, - freqModCounter = 0, - text = {}, - fields = { - { t = "Band", x = 10, y = 14, sp = 30, min=0, max=5, vals = { 2 }, to = SMLSIZE, table = { [0]="U", "A", "B", "E", "F", "R" }, upd = function(self) self.handleBandChanUpdate(self) end }, - { t = "Chan", x = 10, y = 24, sp = 30, min=1, max=8, vals = { 3 }, to = SMLSIZE, upd = function(self) self.handleBandChanUpdate(self) end }, - { t = "Power", x = 10, y = 34, sp = 30, min=1, vals = { 4 }, to = SMLSIZE, upd = function(self) self.updatePowerTable(self) end }, - { t = "Pit", x = 10, y = 44, sp = 30, min=0, max=1, vals = { 5 }, to = SMLSIZE, table = { [0]="OFF", "ON" } }, - { t = "Dev", x = 70, y = 14, sp = 25, write = false, ro = true, vals = { 1 }, to = SMLSIZE , table = { [1]="6705",[3]="SA",[4]="Tramp",[255]="None"} }, - { t = "Freq", x = 70, y = 24, sp = 25, min = 5000, max = 5999, vals = { 6 }, to = SMLSIZE, upd = function(self) self.handleFreqValUpdate(self) end }, - }, - freqLookup = { - { 5865, 5845, 5825, 5805, 5785, 5765, 5745, 5725 }, -- Boscam A - { 5733, 5752, 5771, 5790, 5809, 5828, 5847, 5866 }, -- Boscam B - { 5705, 5685, 5665, 5645, 5885, 5905, 5925, 5945 }, -- Boscam E - { 5740, 5760, 5780, 5800, 5820, 5840, 5860, 5880 }, -- FatShark - { 5658, 5695, 5732, 5769, 5806, 5843, 5880, 5917 }, -- RaceBand - }, - postLoad = function (self) - if (self.values[2] or 0) < 0 or (self.values[3] or 0) == 0 or (self.values[4] or 0) == 0 then - self.values = {} - else - self.prevBandVal = 0 -- reset value trackers - self.prevChanVal = 0 - self.prevFreqVal = 0 - local rFreq - if (self.values[7] or 0) > 0 then - rFreq = math.floor(self.values[6] + (self.values[7] * 256)) - else - rFreq = 0 - end - if (self.values[2] or 0) > 0 then -- band != 0 - if rFreq > 0 then - self.prevFreqVal = rFreq - self.prevBandVal = self.values[2] - self.prevChanVal = self.values[3] - self.fields[1].min = 0 -- make sure 'U' band allowed - self.eepromWrite = true - self.fields[6].value = rFreq - self.values[6] = rFreq - else -- if user freq not supported then - self.fields[1].min = 1 -- don't allow 'U' band - self.eepromWrite = false -- don't write EEPROM on older Betaflight versions - end - else -- band == 0 - if rFreq > 0 then - self.prevFreqVal = rFreq - self.fields[1].min = 0 -- make sure 'U' band allowed - self.eepromWrite = true - self.fields[6].value = rFreq - self.values[6] = rFreq - -- set chan via freq / 100 - self.prevChanVal = clipValue(math.floor((rFreq - 5100) / 100), - self.fields[2].min, self.fields[2].max) - self.fields[2].value = self.prevChanVal - self.values[3] = self.prevChanVal - else - self.values = {} - end - end - end - end, - preSave = function(self) - local valsTemp = {} - if self.values then - local channel - if self.values[2] > 0 then -- band != 0 - channel = (self.values[2]-1)*8 + self.values[3]-1 - elseif self.fields[6].value then -- band == 0 - channel = self.fields[6].value - else - channel = 24 - end - valsTemp[1] = bit32.band(channel,0xFF) - valsTemp[2] = bit32.rshift(channel,8) - valsTemp[3] = self.values[4] - valsTemp[4] = self.values[5] - end - return valsTemp - end, - -- find closest value in freq table that is above/below given freq - findNextInFreqTable = function(self, newFreq) - local startBand - local endBand - local incFlag -- freq increasing or decreasing - if newFreq > self.prevFreqVal then - incFlag = 1 - startBand = 1 - endBand = self.fields[1].max - else - incFlag = -1 - startBand = self.fields[1].max - endBand = 1 - end - local curBand = self.values[2] - local curChan = self.values[3] - local selBand = 0 - local selChan = 0 - local selFreq = 0 - local diffVal = 9999 - local fVal - local minChan = self.fields[2].min - local maxChan = self.fields[2].max - -- need to scan bands in same "direction" as 'incFlag' - -- so same-freq selections will be handled properly (F8 & R7) - for band=startBand,endBand,incFlag do - for chan=minChan,maxChan do - if band ~= curBand or chan ~= curChan then -- don't reselect same band/chan - fVal = self.freqLookup[band][chan] - if incFlag > 0 then - if fVal >= self.prevFreqVal and fVal - self.prevFreqVal < diffVal then - -- if same freq then only select if "next" band: - if fVal ~= self.prevFreqVal or band > curBand then - selBand = band - selChan = chan - selFreq = fVal - diffVal = fVal - self.prevFreqVal - end - end - else - if fVal <= self.prevFreqVal and self.prevFreqVal - fVal < diffVal then - -- if same freq then only select if "previous" band: - if fVal ~= self.prevFreqVal or band < curBand then - selBand = band - selChan = chan - selFreq = fVal - diffVal = self.prevFreqVal - fVal - end - end - end - end - end - end - return selFreq, selBand, selChan - end, - -- returns the next user-frequency value in MHz; implements an - -- "exponential" modification rate so dialing in values is faster - getNextUserFreqValue = function(self, newFreq) - local now = getTime() -- track rate of change for possible mod speedup - if now < self.lastFreqUpdTS + 15 then - self.freqModCounter = self.freqModCounter + (15-(self.lastFreqUpdTS-now)) -- increase counter for mod speedup - else - self.freqModCounter = 0 -- no mod speedup - end - local uFreq - if self.freqModCounter > 65 then -- rate is fast enough; do mod speedup - if newFreq > self.prevFreqVal then - uFreq = clipValue(newFreq + math.floor(self.freqModCounter / 65), - self.fields[6].min, self.fields[6].max) - else - uFreq = clipValue(newFreq - math.floor(self.freqModCounter / 65), - self.fields[6].min, self.fields[6].max) - end - else - uFreq = newFreq - end - self.lastFreqUpdTS = now - return uFreq - end, - updatePowerTable = function(self) - if self.values and not self.fields[3].table then - if self.values[1] == 1 then -- RTC6705 - self.fields[3].table = { 25, 200 } - self.fields[3].max = 2 - self.fields[4].t = nil -- don't display Pit field - self.fields[4].table = { [0]="", "" } - elseif self.values[1] == 3 then -- SmartAudio - self.fields[3].table = { 25, 200, 500, 800 } - self.fields[3].max = 4 - elseif self.values[1] == 4 then -- Tramp - self.fields[3].table = { 25, 100, 200, 400, 600 } - self.fields[3].max = 5 - end - end - end, - handleBandChanUpdate = function(self) - if (#(self.values) or 0) >= self.minBytes then - if (self.values[3] or 0) > 0 then - if self.values[2] ~= self.prevBandVal or self.values[3] ~= self.prevChanVal then - if self.values[2] > 0 then -- band != 0 - self.prevFreqVal = self.freqLookup[self.values[2]][self.values[3]] - else -- band == 0; set freq via channel*100 - self.prevFreqVal = math.floor(5100 + (self.values[3] * 100)) - end - self.fields[6].value = self.prevFreqVal - self.values[6] = self.prevFreqVal - self.prevBandVal = self.values[2] - self.prevChanVal = self.values[3] - end - end - end - end, - handleFreqValUpdate = function(self) - if (#(self.values) or 0) >= self.minBytes and (self.fields[6].value or 0) > 0 then - local newFreq = self.fields[6].value - if newFreq ~= self.prevFreqVal then - if self.values[2] == 0 then - -- band == 0 - local uFreq = self.getNextUserFreqValue(self, newFreq) - self.prevFreqVal = uFreq - if uFreq ~= newFreq then - self.fields[6].value = uFreq - self.values[6] = uFreq - end - -- set channel value via freq/100 - self.prevChanVal = clipValue(math.floor((self.prevFreqVal - 5100) / 100), - self.fields[2].min, self.fields[2].max) - self.fields[2].value = self.prevChanVal - self.values[3] = self.prevChanVal - else - -- band != 0; find closest freq in table that is above/below dialed freq - local selFreq, selBand, selChan = self.findNextInFreqTable(self, newFreq) - if selFreq > 0 then - self.prevFreqVal = selFreq - self.prevBandVal = selBand - self.prevChanVal = selChan - self.fields[6].value = selFreq -- using new freq from table - self.values[6] = selFreq - self.fields[1].value = self.prevBandVal -- set band value for freq - self.values[2] = self.prevBandVal - self.fields[2].value = self.prevChanVal -- set channel value for freq - self.values[3] = self.prevChanVal - else - self.fields[6].value = self.prevFreqVal -- if no match then revert freq - self.values[6] = self.prevFreqVal - end - end - end - end - end + read = 88, -- MSP_VTX_CONFIG + write = 89, -- MSP_VTX_SET_CONFIG + eepromWrite = true, + reboot = false, + title = "Taranis/Horus VTX", + minBytes = 5, + prevBandVal = 0, + prevChanVal = 0, + prevFreqVal = 0, + lastFreqUpdTS = 0, + freqModCounter = 0, + text = {}, + fields = { + { t = "Band", x = 10, y = 14 * fa, sp = 30 * fa, min=0, max=5, vals = { 2 }, to = fs, table = { [0]="U", "A", "B", "E", "F", "R" }, upd = function(self) self.handleBandChanUpdate(self) end }, + { t = "Chan", x = 10, y = 24 * fa, sp = 30 * fa, min=1, max=8, vals = { 3 }, to = fs, upd = function(self) self.handleBandChanUpdate(self) end }, + { t = "Power", x = 10, y = 34 * fa, sp = 30 * fa, min=1, vals = { 4 }, to = fs, upd = function(self) self.updatePowerTable(self) end }, + { t = "Pit", x = 10, y = 44 * fa, sp = 30 * fa, min=0, max=1, vals = { 5 }, to = fs, table = { [0]="OFF", "ON" } }, + { t = "Dev", x = 70 * fa, y = 14 * fa, sp = 25 * fa, write = false, ro = true, vals = { 1 }, to = fs , table = { [1]="6705",[3]="SA",[4]="Tramp",[255]="None"} }, + { t = "Freq", x = 70 * fa, y = 24 * fa, sp = 25 * fa, min = 5000, max = 5999, vals = { 6 }, to = fs, upd = function(self) self.handleFreqValUpdate(self) end }, + }, + freqLookup = { + { 5865, 5845, 5825, 5805, 5785, 5765, 5745, 5725 }, -- Boscam A + { 5733, 5752, 5771, 5790, 5809, 5828, 5847, 5866 }, -- Boscam B + { 5705, 5685, 5665, 5645, 5885, 5905, 5925, 5945 }, -- Boscam E + { 5740, 5760, 5780, 5800, 5820, 5840, 5860, 5880 }, -- FatShark + { 5658, 5695, 5732, 5769, 5806, 5843, 5880, 5917 }, -- RaceBand + }, + postLoad = function (self) + if (self.values[2] or 0) < 0 or (self.values[3] or 0) == 0 or (self.values[4] or 0) == 0 then + self.values = {} + else + self.prevBandVal = 0 -- reset value trackers + self.prevChanVal = 0 + self.prevFreqVal = 0 + local rFreq + if (self.values[7] or 0) > 0 then + rFreq = math.floor(self.values[6] + (self.values[7] * 256)) + else + rFreq = 0 + end + if (self.values[2] or 0) > 0 then -- band != 0 + if rFreq > 0 then + self.prevFreqVal = rFreq + self.prevBandVal = self.values[2] + self.prevChanVal = self.values[3] + self.fields[1].min = 0 -- make sure 'U' band allowed + self.eepromWrite = true + self.fields[6].value = rFreq + self.values[6] = rFreq + else -- if user freq not supported then + self.fields[1].min = 1 -- don't allow 'U' band + self.eepromWrite = false -- don't write EEPROM on older Betaflight versions + end + else -- band == 0 + if rFreq > 0 then + self.prevFreqVal = rFreq + self.fields[1].min = 0 -- make sure 'U' band allowed + self.eepromWrite = true + self.fields[6].value = rFreq + self.values[6] = rFreq + -- set chan via freq / 100 + self.prevChanVal = clipValue(math.floor((rFreq - 5100) / 100), + self.fields[2].min, self.fields[2].max) + self.fields[2].value = self.prevChanVal + self.values[3] = self.prevChanVal + else + self.values = {} + end + end + end + end, + preSave = function(self) + local valsTemp = {} + if self.values then + local channel + if self.values[2] > 0 then -- band != 0 + channel = (self.values[2]-1)*8 + self.values[3]-1 + elseif self.fields[6].value then -- band == 0 + channel = self.fields[6].value + else + channel = 24 + end + valsTemp[1] = bit32.band(channel,0xFF) + valsTemp[2] = bit32.rshift(channel,8) + valsTemp[3] = self.values[4] + valsTemp[4] = self.values[5] + end + return valsTemp + end, + -- find closest value in freq table that is above/below given freq + findNextInFreqTable = function(self, newFreq) + local startBand + local endBand + local incFlag -- freq increasing or decreasing + if newFreq > self.prevFreqVal then + incFlag = 1 + startBand = 1 + endBand = self.fields[1].max + else + incFlag = -1 + startBand = self.fields[1].max + endBand = 1 + end + local curBand = self.values[2] + local curChan = self.values[3] + local selBand = 0 + local selChan = 0 + local selFreq = 0 + local diffVal = 9999 + local fVal + local minChan = self.fields[2].min + local maxChan = self.fields[2].max + -- need to scan bands in same "direction" as 'incFlag' + -- so same-freq selections will be handled properly (F8 & R7) + for band=startBand,endBand,incFlag do + for chan=minChan,maxChan do + if band ~= curBand or chan ~= curChan then -- don't reselect same band/chan + fVal = self.freqLookup[band][chan] + if incFlag > 0 then + if fVal >= self.prevFreqVal and fVal - self.prevFreqVal < diffVal then + -- if same freq then only select if "next" band: + if fVal ~= self.prevFreqVal or band > curBand then + selBand = band + selChan = chan + selFreq = fVal + diffVal = fVal - self.prevFreqVal + end + end + else + if fVal <= self.prevFreqVal and self.prevFreqVal - fVal < diffVal then + -- if same freq then only select if "previous" band: + if fVal ~= self.prevFreqVal or band < curBand then + selBand = band + selChan = chan + selFreq = fVal + diffVal = self.prevFreqVal - fVal + end + end + end + end + end + end + return selFreq, selBand, selChan + end, + -- returns the next user-frequency value in MHz; implements an + -- "exponential" modification rate so dialing in values is faster + getNextUserFreqValue = function(self, newFreq) + local now = getTime() -- track rate of change for possible mod speedup + if now < self.lastFreqUpdTS + 15 then + self.freqModCounter = self.freqModCounter + (15-(self.lastFreqUpdTS-now)) -- increase counter for mod speedup + else + self.freqModCounter = 0 -- no mod speedup + end + local uFreq + if self.freqModCounter > 65 then -- rate is fast enough; do mod speedup + if newFreq > self.prevFreqVal then + uFreq = clipValue(newFreq + math.floor(self.freqModCounter / 65), + self.fields[6].min, self.fields[6].max) + else + uFreq = clipValue(newFreq - math.floor(self.freqModCounter / 65), + self.fields[6].min, self.fields[6].max) + end + else + uFreq = newFreq + end + self.lastFreqUpdTS = now + return uFreq + end, + updatePowerTable = function(self) + if self.values and not self.fields[3].table then + if self.values[1] == 1 then -- RTC6705 + self.fields[3].table = { 25, 200 } + self.fields[3].max = 2 + self.fields[4].t = nil -- don't display Pit field + self.fields[4].table = { [0]="", "" } + elseif self.values[1] == 3 then -- SmartAudio + self.fields[3].table = { 25, 200, 500, 800 } + self.fields[3].max = 4 + elseif self.values[1] == 4 then -- Tramp + self.fields[3].table = { 25, 100, 200, 400, 600 } + self.fields[3].max = 5 + end + end + end, + handleBandChanUpdate = function(self) + if (#(self.values) or 0) >= self.minBytes then + if (self.values[3] or 0) > 0 then + if self.values[2] ~= self.prevBandVal or self.values[3] ~= self.prevChanVal then + if self.values[2] > 0 then -- band != 0 + self.prevFreqVal = self.freqLookup[self.values[2]][self.values[3]] + else -- band == 0; set freq via channel*100 + self.prevFreqVal = math.floor(5100 + (self.values[3] * 100)) + end + self.fields[6].value = self.prevFreqVal + self.values[6] = self.prevFreqVal + self.prevBandVal = self.values[2] + self.prevChanVal = self.values[3] + end + end + end + end, + handleFreqValUpdate = function(self) + if (#(self.values) or 0) >= self.minBytes and (self.fields[6].value or 0) > 0 then + local newFreq = self.fields[6].value + if newFreq ~= self.prevFreqVal then + if self.values[2] == 0 then + -- band == 0 + local uFreq = self.getNextUserFreqValue(self, newFreq) + self.prevFreqVal = uFreq + if uFreq ~= newFreq then + self.fields[6].value = uFreq + self.values[6] = uFreq + end + -- set channel value via freq/100 + self.prevChanVal = clipValue(math.floor((self.prevFreqVal - 5100) / 100), + self.fields[2].min, self.fields[2].max) + self.fields[2].value = self.prevChanVal + self.values[3] = self.prevChanVal + else + -- band != 0; find closest freq in table that is above/below dialed freq + local selFreq, selBand, selChan = self.findNextInFreqTable(self, newFreq) + if selFreq > 0 then + self.prevFreqVal = selFreq + self.prevBandVal = selBand + self.prevChanVal = selChan + self.fields[6].value = selFreq -- using new freq from table + self.values[6] = selFreq + self.fields[1].value = self.prevBandVal -- set band value for freq + self.values[2] = self.prevBandVal + self.fields[2].value = self.prevChanVal -- set channel value for freq + self.values[3] = self.prevChanVal + else + self.fields[6].value = self.prevFreqVal -- if no match then revert freq + self.values[6] = self.prevFreqVal + end + end + end + end + end } \ No newline at end of file diff --git a/SCRIPTS/TELEMETRY/VTx/vtx.luac b/SCRIPTS/TELEMETRY/VTx/vtx.luac index 45bcd6f3..40e9a3ed 100644 Binary files a/SCRIPTS/TELEMETRY/VTx/vtx.luac and b/SCRIPTS/TELEMETRY/VTx/vtx.luac differ