mirror of
https://github.com/pascallanger/DIY-Multiprotocol-TX-Module.git
synced 2025-02-04 21:48:12 +00:00
814 lines
31 KiB
Lua
814 lines
31 KiB
Lua
---- #########################################################################
|
|
---- # #
|
|
---- # Copyright (C) OpenTX/EdgeTx #
|
|
-----# #
|
|
---- # License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html #
|
|
---- # #
|
|
---- # This program is free software; you can redistribute it and/or modify #
|
|
---- # it under the terms of the GNU General Public License version 2 as #
|
|
---- # published by the Free Software Foundation. #
|
|
---- # #
|
|
---- # This program is distributed in the hope that it will be useful #
|
|
---- # but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
|
---- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
|
---- # GNU General Public License for more details. #
|
|
---- # #
|
|
---- #########################################################################
|
|
|
|
------------------------------------------------------------------------------
|
|
-- This script library is a rewrite of the original DSM forward programming Lua
|
|
-- Script. The goal is to make it easier to understand, mantain, and to
|
|
-- separate the GUI from the DSM Forward programming engine/logic
|
|
-- in this way, GUIs can evolve independent. OpenTX Gui, EdgeTx GUI, Small Radios, etc.
|
|
|
|
-- Code is based on the code/work by: Pascal Langer (Author of the Multi-Module)
|
|
-- Rewrite/Enhancements By: Francisco Arzu
|
|
--
|
|
------------------------------------------------------------------------------
|
|
--###############################################################################
|
|
-- Multi buffer for DSM description
|
|
-- Multi_Buffer[0..2]=="DSM" -> Lua script is running
|
|
-- Multi_Buffer[3]==0x70+len -> TX to RX data ready to be sent
|
|
-- Multi_Buffer[4..9]=6 bytes of TX to RX data
|
|
-- Multi_Buffer[10..25]=16 bytes of RX to TX data
|
|
--
|
|
-- To start operation:
|
|
-- Write 0x00 at address 3
|
|
-- Write 0x00 at address 10
|
|
-- Write "DSM" at address 0..2
|
|
--###############################################################################
|
|
|
|
|
|
local Log, menuLib, modelLib, DEBUG_ON = ... -- Get Debug_ON from parameters. -- 0=NO DEBUG, 1=HIGH LEVEL 2=MORE DETAILS
|
|
local LIB_VERSION = "0.55"
|
|
local MSG_FILE = "/SCRIPTS/TOOLS/DSMLIB/msg_fwdp_en.txt"
|
|
|
|
local PHASE = menuLib.PHASE
|
|
local LINE_TYPE = menuLib.LINE_TYPE
|
|
|
|
local CH_TYPE = modelLib.CH_TYPE
|
|
local CH_MIX_TYPE = modelLib.CH_MIX_TYPE
|
|
|
|
local DISP_ATTR = menuLib.DISP_ATTR
|
|
local DSM_Context = menuLib.DSM_Context
|
|
local MODEL = modelLib.MODEL
|
|
|
|
local MAX_MENU_LINES = menuLib.MAX_MENU_LINES
|
|
local BACK_BUTTON = menuLib.BACK_BUTTON
|
|
local NEXT_BUTTON = menuLib.NEXT_BUTTON
|
|
local PREV_BUTTON = menuLib.PREV_BUTTON
|
|
|
|
|
|
local SEND_TIMEOUT = 2000 / 10 -- Home many 10ms intervals to wait on sending data to tx to keep connection open (2s)
|
|
local TXInactivityTime = 0 -- Next time to do heartbeat after inactivity
|
|
local RXInactivityTime = 0 -- To check RX disconnection
|
|
|
|
|
|
local TxInfo_Type = 0
|
|
local TxInfo_Step = 0
|
|
local Change_Step = 0
|
|
|
|
local IS_EDGETX = false
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
|
|
local function multiBuffer2String() -- used for debug
|
|
local i
|
|
local rxAnswer = "RX:"
|
|
for i = 10, 25 do
|
|
rxAnswer = rxAnswer .. string.format(" %02X", multiBuffer(i))
|
|
end
|
|
return rxAnswer
|
|
end
|
|
|
|
---------------- DSM Values <-> Int16 Manipulation --------------------------------------------------------
|
|
|
|
local function int16_LSB(number) -- Less Significat byte
|
|
local r,x = bit32.band(number, 0xFF)
|
|
return r
|
|
end
|
|
|
|
local function int16_MSB(number) -- Most signifcant byte
|
|
return bit32.rshift(number, 8)
|
|
end
|
|
|
|
local function Dsm_to_Int16(lsb, msb) -- Componse an Int16 value
|
|
return bit32.lshift(msb, 8) + lsb
|
|
end
|
|
|
|
local function Dsm_to_SInt16(lsb,msb) -- Componse a SIGNED Int16 value
|
|
local value = bit32.lshift(msb, 8) + lsb
|
|
if value >= 0x8000 then -- Negative value??
|
|
return value - 0x10000
|
|
end
|
|
return value
|
|
end
|
|
|
|
local function sInt16ToDsm(value) -- Convent to SIGNED DSM Value
|
|
if value < 0 then
|
|
value = 0x10000 + value
|
|
end
|
|
return value
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
local function DSM_send(...)
|
|
local arg = { ... }
|
|
|
|
for i = 1, #arg do
|
|
multiBuffer(3 + i, arg[i])
|
|
end
|
|
multiBuffer(3, 0x70 + #arg)
|
|
|
|
|
|
if (DEBUG_ON > 1) then
|
|
local str = ""
|
|
for i = 1, #arg do
|
|
str = str .. string.format("%02X ", arg[i])
|
|
end
|
|
LOG_write("DSM_SEND: [%s]\n", str)
|
|
end
|
|
end
|
|
|
|
|
|
|
|
-------------------------------------------------------------------------------------------------
|
|
local function DSM_StartConnection()
|
|
if (DEBUG_ON) then Log.LOG_write("DSM_StartConnection()\n") end
|
|
|
|
--Set protocol to talk to
|
|
multiBuffer( 0, string.byte('D') )
|
|
--test if value has been written
|
|
if multiBuffer( 0 ) ~= string.byte('D') then
|
|
if (DEBUG_ON) then Log.LOG_write("Not Enouth memory\n") end
|
|
error("Not enough memory!")
|
|
return 2
|
|
end
|
|
--Init TX buffer
|
|
multiBuffer( 3, 0x00 )
|
|
--Init RX buffer
|
|
multiBuffer( 10, 0x00 )
|
|
--Init telemetry
|
|
multiBuffer( 0, string.byte('D') )
|
|
multiBuffer( 1, string.byte('S') )
|
|
multiBuffer( 2, string.byte('M') )
|
|
|
|
return 0
|
|
end
|
|
|
|
local function DSM_ReleaseConnection()
|
|
if (DEBUG_ON) then Log.LOG_write("DSM_ReleaseConnection()\n") end
|
|
multiBuffer(0, 0)
|
|
end
|
|
|
|
--------------------------------------------------------------------------------------------------------
|
|
-- REEQUEST Messages to RX
|
|
|
|
local function DSM_sendHeartbeat()
|
|
-- keep connection open
|
|
if (DEBUG_ON) then Log.LOG_write("SEND DSM_sendHeartbeat()\n") end
|
|
DSM_send(0x00, 0x04, 0x00, 0x00)
|
|
end
|
|
|
|
local function DSM_getRxVerson()
|
|
local TX_CAP=0x14 -- Capabilities??
|
|
local TX_MAX_CH = modelLib.TX_CHANNELS - 6 --number of channels after 6 (6Ch=0, 10ch=4, etc)
|
|
|
|
if (DEBUG_ON) then Log.LOG_write("SEND DSM_getRxVersion(Ch:0x%X,Cap:0x%X)\n",TX_MAX_CH,TX_CAP) end
|
|
DSM_send(0x11, 0x06, TX_MAX_CH, TX_CAP, 0x00, 0x00)
|
|
end
|
|
|
|
local function DSM_getMainMenu()
|
|
local TX_CAP=0x14 -- Capabilities??
|
|
local TX_MAX_CH = modelLib.TX_CHANNELS - 6 --number of channels after 6 (6Ch=0, 10ch=4, etc)
|
|
|
|
if (DEBUG_ON) then Log.LOG_write("SEND DSM_getMainMenu(Ch:0x%X,Cap:0x%X) -- TX_Channels=%d\n",TX_MAX_CH,TX_CAP,TX_MAX_CH+6) end
|
|
DSM_send(0x12, 0x06, TX_MAX_CH, TX_CAP, 0x00, 0x00) -- first menu only
|
|
end
|
|
|
|
local function DSM_getMenu(menuId, latSelLine)
|
|
if (DEBUG_ON) then Log.LOG_write("SEND DSM_getMenu(MenuId=0x%X LastSelectedLine=%s)\n", menuId, latSelLine) end
|
|
DSM_send(0x16, 0x06, int16_MSB(menuId), int16_LSB(menuId), 0x00, latSelLine)
|
|
end
|
|
|
|
local function DSM_getFirstMenuLine(menuId)
|
|
if (DEBUG_ON) then Log.LOG_write("SEND DSM_getFirstMenuLine(MenuId=0x%X)\n", menuId) end
|
|
DSM_send(0x13, 0x04, int16_MSB(menuId), int16_LSB(menuId)) -- line 0
|
|
end
|
|
|
|
local function DSM_getNextMenuLine(menuId, curLine)
|
|
if (DEBUG_ON) then Log.LOG_write("SEND DSM_getNextLine(MenuId=0x%X,LastLine=%s)\n", menuId, curLine) end
|
|
DSM_send(0x14, 0x06, int16_MSB(menuId), int16_LSB(menuId), 0x00, curLine) -- line X
|
|
end
|
|
|
|
local function DSM_getNextMenuValue(menuId, valId, text)
|
|
if (DEBUG_ON) then Log.LOG_write("SEND DSM_getNextMenuValue(MenuId=0x%X, LastValueId=0x%X) Extra: Text=\"%s\"\n", menuId, valId,
|
|
text)
|
|
end
|
|
DSM_send(0x15, 0x06, int16_MSB(menuId), int16_LSB(menuId), int16_MSB(valId), int16_LSB(valId)) -- line X
|
|
end
|
|
|
|
local function DSM_updateMenuValue(valId, val, text, line)
|
|
local value = sInt16ToDsm(val)
|
|
if (DEBUG_ON) then Log.LOG_write("SEND DSM_updateValue(ValueId=0x%X,val=%d) Extra: Text=\"%s\" Value=%s\n", valId, val, text, menuLib.lineValue2String(line)) end
|
|
DSM_send(0x18, 0x06, int16_MSB(valId), int16_LSB(valId), int16_MSB(value), int16_LSB(value)) -- send current value
|
|
end
|
|
|
|
local function DSM_validateMenuValue(valId, text, line)
|
|
if (DEBUG_ON) then Log.LOG_write("SEND DSM_validateValue(ValueId=0x%X) Extra: Text=\"%s\" Value=%s\n", valId, text, menuLib.lineValue2String(line)) end
|
|
DSM_send(0x19, 0x04, int16_MSB(valId), int16_LSB(valId))
|
|
end
|
|
|
|
local function DSM_editingValue(lineNum, text, line)
|
|
if (DEBUG_ON) then Log.LOG_write("SEND DSM_editingValue(lineNo=0x%X) Extra: Text=\"%s\" Val=%s\n", lineNum, text, menuLib.lineValue2String(line)) end
|
|
DSM_send(0x1A, 0x04, int16_MSB(lineNum), int16_LSB(lineNum))
|
|
end
|
|
|
|
local function DSM_editingValueEnd(lineNum, text, line)
|
|
if (DEBUG_ON) then Log.LOG_write("SEND DSM_editingValueEnd(lineNo=0x%X) Extra: Text=\"%s\" Value=%s\n", lineNum, text, menuLib.lineValue2String(line)) end
|
|
DSM_send(0x1B, 0x04, int16_MSB(lineNum), int16_LSB(lineNum))
|
|
end
|
|
|
|
-- Send the functionality of the RX channel Port (channel)
|
|
local function DSM_sendTxChInfo_20(portNo)
|
|
local b1,b2 = MODEL.DSM_ChannelInfo[portNo][0] or 0, MODEL.DSM_ChannelInfo[portNo][1] or 0
|
|
|
|
if (DEBUG_ON) then Log.LOG_write("CALL DSM_TxChInfo(#%d DATA= %02X %02X %02X %02X) %s\n", portNo,
|
|
portNo, portNo, b1, b2, modelLib.channelType2String(b1,b2)) -- DATA part
|
|
end
|
|
DSM_send(0x20, 0x06, portNo, portNo, b1, b2)
|
|
end
|
|
|
|
local function DSM_sendTxSubtrim_21(portNo)
|
|
local usage = MODEL.DSM_ChannelInfo[portNo][1] or 0
|
|
local leftTravel = math.abs(math.floor(MODEL.modelOutputChannel[portNo].min/10))
|
|
local rightTravel = math.abs(math.floor(MODEL.modelOutputChannel[portNo].max/10))
|
|
local subTrim = math.floor(MODEL.modelOutputChannel[portNo].offset/10)
|
|
|
|
-- Center at 100%: 142-1906 (0 8E 07 72)
|
|
local left = 142
|
|
local right = 1906
|
|
|
|
if (bit32.band(usage,CH_TYPE.THR)>0) then
|
|
left = 0
|
|
right = 2047
|
|
end
|
|
|
|
left = math.floor (left - (leftTravel -100) *8.8 + subTrim*2)
|
|
right = math.floor (right + (rightTravel -100) *8.8 + subTrim*2)
|
|
|
|
if (left<0) then left=0 end
|
|
if (right>2027) then right=2047 end
|
|
|
|
local b1,b2,b3,b4 = int16_MSB(left), int16_LSB(left), int16_MSB(right), int16_LSB(right)
|
|
|
|
if (DEBUG_ON) then Log.LOG_write("CALL DSM_TxSubtrim(#%d DATA=%02X %02X %02X %02X) Range(%d - %d) ER L/R:(%d - %d)x8 ST:(%d)x2\n", portNo,
|
|
b1,b2,b3,b4, left, right, leftTravel-100, rightTravel-100, subTrim) -- DATA part
|
|
end
|
|
DSM_send(0x21, 0x06, b1,b2,b3,b4) -- Port is not send anywhere, since the previous 0x20 type message have it.
|
|
end
|
|
|
|
local function DSM_sendTxServoTravel_23(portNo)
|
|
local leftTravel = math.abs(math.floor(MODEL.modelOutputChannel[portNo].min/10))
|
|
local rightTravel = math.abs(math.floor(MODEL.modelOutputChannel[portNo].max/10))
|
|
local debugInfo = string.format("Travel L/R (%d - %d)",leftTravel,rightTravel)
|
|
|
|
if (DEBUG_ON) then Log.LOG_write("CALL DSM_TxServoTravel(#%d DATA= %02X %02X %02X %02X) %s\n", portNo,
|
|
0x00, leftTravel, 0x00, rightTravel, debugInfo) -- DATA part
|
|
end
|
|
DSM_send(0x23, 0x06, 0x00, leftTravel, 0x00, rightTravel)
|
|
end
|
|
|
|
local function DSM_sentTxInfo(menuId,portNo)
|
|
-- TxInfo_Type=0 : AR636B Main Menu (Send port/Channel info + SubTrim + Travel)
|
|
-- TxInfo_Type=1 : AR630-637 Famly Main Menu (Only Send Port/Channel usage Msg 0x20)
|
|
-- TxInfo_Type=1F : AR630-637 Initial Setup/Relearn Servo Settings (Send port/Channel info + SubTrim + Travel +0x24/Unknown)
|
|
|
|
if (TxInfo_Step == 0) then
|
|
-- AR630 family: Both TxInfo_Type (ManinMenu=0x1, Other First Time Configuration = 0x1F)
|
|
DSM_sendTxChInfo_20(portNo)
|
|
|
|
if (TxInfo_Type == 0x1F) then
|
|
TxInfo_Step = 1
|
|
end
|
|
if (TxInfo_Type == 0x00) then
|
|
TxInfo_Step = 2
|
|
end
|
|
elseif (TxInfo_Step == 1) then
|
|
DSM_sendTxServoTravel_23(portNo)
|
|
TxInfo_Step = 2
|
|
elseif (TxInfo_Step == 2) then
|
|
DSM_sendTxSubtrim_21(portNo)
|
|
|
|
if (TxInfo_Type == 0x00) then
|
|
TxInfo_Step = 5 -- End Step
|
|
else
|
|
TxInfo_Step = 3
|
|
end
|
|
elseif (TxInfo_Step == 3) then
|
|
-- 24,6: 0 83 5A B5
|
|
if (DEBUG_ON) then Log.LOG_write("CALL DSM_TxInfo24(#%d DATA=0x24 0x06 %02X %02X %02X %02X)\n", portNo,
|
|
0x00, 0x83, 0x5A, 0xB5) -- DATA part
|
|
end
|
|
DSM_send(0x24, 0x06, 0x00, 0x83, 0x5A, 0xB5) -- Still Uknown
|
|
TxInfo_Step = 4
|
|
|
|
elseif (TxInfo_Step == 4) then
|
|
-- 24,6: 6 80 25 4B
|
|
if (DEBUG_ON) then Log.LOG_write("CALL DSM_TxInfo24(#%d DATA=0x24 0x06 %02X %02X %02X %02X)\n", portNo,
|
|
0x06, 0x80, 0x25, 0x4B) -- DATA part
|
|
end
|
|
DSM_send(0x24, 0x06, 0x06, 0x80, 0x25, 0x4B) -- Still Uknown
|
|
TxInfo_Step = 5
|
|
elseif (TxInfo_Step == 5) then
|
|
-- 22,4: 0 0
|
|
if (DEBUG_ON) then Log.LOG_write("CALL DSM_TxInfo_End(#%d)\n", portNo)
|
|
end
|
|
DSM_send(0x22, 0x04, 0x00, 0x00)
|
|
TxInfo_Step = 0
|
|
end
|
|
|
|
if (TxInfo_Step > 0) then
|
|
DSM_Context.SendDataToRX = 1 -- keep Transmitig
|
|
end
|
|
end
|
|
|
|
-----------------------------------------------------------------------------------------------------------
|
|
|
|
local function DSM_sendRequest()
|
|
-- Send the proper Request message depending on the Phase
|
|
|
|
local ctx = DSM_Context
|
|
if (DEBUG_ON) then Log.LOG_write("%3.3f %s: ", menuLib.getElapsedTime(), menuLib.phase2String(ctx.Phase)) end
|
|
|
|
if ctx.Phase == PHASE.RX_VERSION then -- request RX version
|
|
DSM_getRxVerson()
|
|
|
|
elseif ctx.Phase == PHASE.WAIT_CMD then -- keep connection open
|
|
DSM_sendHeartbeat()
|
|
|
|
elseif ctx.Phase == PHASE.MENU_TITLE then -- request menu title
|
|
if ctx.Menu.MenuId == 0 then -- First time loading a menu ?
|
|
DSM_getMainMenu()
|
|
else
|
|
DSM_getMenu(ctx.Menu.MenuId, ctx.SelLine)
|
|
|
|
if (ctx.Menu.MenuId == 0x0001) then -- Executed the Reset Menu??
|
|
if (DEBUG_ON) then Log.LOG_write("RX Reset!!!\n") end
|
|
-- Start again retriving RX info
|
|
ctx.Menu.MenuId = 0
|
|
ctx.isReset = true
|
|
ctx.Phase = PHASE.RX_VERSION
|
|
end
|
|
end
|
|
|
|
elseif ctx.Phase == PHASE.MENU_REQ_TX_INFO then
|
|
DSM_sentTxInfo(ctx.Menu.MenuId, ctx.CurLine)
|
|
|
|
elseif ctx.Phase == PHASE.MENU_LINES then -- request next menu lines
|
|
if ctx.CurLine == -1 then -- No previous menu line loaded ?
|
|
DSM_getFirstMenuLine(ctx.Menu.MenuId)
|
|
else
|
|
DSM_getNextMenuLine(ctx.Menu.MenuId, ctx.CurLine)
|
|
end
|
|
|
|
elseif ctx.Phase == PHASE.MENU_VALUES then -- request menu values
|
|
local line = ctx.MenuLines[ctx.CurLine]
|
|
DSM_getNextMenuValue(ctx.Menu.MenuId, line.ValId, line.Text)
|
|
|
|
elseif ctx.Phase == PHASE.VALUE_CHANGING then -- send value
|
|
local line = ctx.MenuLines[ctx.SelLine] -- Updated Value of SELECTED line
|
|
|
|
if (Change_Step==0) then
|
|
DSM_updateMenuValue(line.ValId, line.Val, line.Text, line)
|
|
|
|
if line.Type == menuLib.LINE_TYPE.LIST_MENU then -- Validation on every Step??
|
|
Change_Step=1; ctx.SendDataToRX=1 -- Send inmediatly after
|
|
else
|
|
ctx.Phase = PHASE.VALUE_CHANGING_WAIT
|
|
end
|
|
else
|
|
DSM_validateMenuValue(line.ValId, line.Text, line)
|
|
Change_Step=0
|
|
ctx.Phase = PHASE.VALUE_CHANGING_WAIT
|
|
end
|
|
|
|
elseif ctx.Phase == PHASE.VALUE_CHANGING_WAIT then
|
|
local line = ctx.MenuLines[ctx.SelLine]
|
|
DSM_editingValue(line.lineNum, line.Text, line)
|
|
elseif ctx.Phase == PHASE.VALUE_CHANGE_END then -- send value
|
|
local line = ctx.MenuLines[ctx.SelLine] -- Update Value of SELECTED line
|
|
|
|
if (Change_Step==0) then
|
|
DSM_updateMenuValue(line.ValId, line.Val, line.Text, line)
|
|
Change_Step=1; ctx.SendDataToRX=1 -- Send inmediatly after
|
|
elseif (Change_Step==1) then
|
|
DSM_validateMenuValue(line.ValId, line.Text, line)
|
|
Change_Step=2; ctx.SendDataToRX=1 -- Send inmediatly after
|
|
else
|
|
DSM_editingValueEnd(line.lineNum, line.Text, line)
|
|
Change_Step=0
|
|
ctx.Phase = PHASE.WAIT_CMD
|
|
end
|
|
elseif ctx.Phase == PHASE.EXIT then
|
|
if (DEBUG_ON) then Log.LOG_write("CALL DSM_TX_Exit()\n") end
|
|
DSM_send(0x1F, 0x02, 0xFF, 0xFF) -- 0xAA
|
|
end
|
|
end
|
|
|
|
-----------------------------------------------------------------------------------------------------------
|
|
-- Parsing Responses
|
|
|
|
local function DSM_parseRxVersion()
|
|
--ex: 0x09 0x01 0x00 0x15 0x02 0x22 0x01 0x00 0x14 v2.22.1 00 14?? 8ch SAFE
|
|
-- 0x09 0x01 0x00 0x18 0x05 0x06 0x34 0x00 0x12 v5.6.52 00 12??? 6ch FC6250
|
|
local rxId = multiBuffer(13)
|
|
DSM_Context.RX.Id = rxId
|
|
DSM_Context.RX.Name = menuLib.Get_RxName(rxId)
|
|
DSM_Context.RX.Version = multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16)
|
|
if (DEBUG_ON) then Log.LOG_write("RESPONSE Receiver=%s Version %s Cap:0x%02X\n",
|
|
DSM_Context.RX.Name, DSM_Context.RX.Version, multiBuffer(18)) end
|
|
end
|
|
|
|
local function DSM_parseMenu()
|
|
--ex: 0x09 0x02 0x4F 0x10 0xA5 0x00 0x00 0x00 0x50 0x10 0x10 0x10 0x00 0x00 0x00 0x00
|
|
-- MenuID TextID PrevID NextID BackID
|
|
local ctx = DSM_Context
|
|
local menu = ctx.Menu
|
|
menu.MenuId = Dsm_to_Int16(multiBuffer(12), multiBuffer(13))
|
|
menu.TextId = Dsm_to_Int16(multiBuffer(14), multiBuffer(15))
|
|
menu.Text = menuLib.Get_Text(menu.TextId)
|
|
menu.PrevId = Dsm_to_Int16(multiBuffer(16), multiBuffer(17))
|
|
menu.NextId = Dsm_to_Int16(multiBuffer(18), multiBuffer(19))
|
|
menu.BackId = Dsm_to_Int16(multiBuffer(20), multiBuffer(21))
|
|
for i = 0, MAX_MENU_LINES do -- clear menu
|
|
ctx.MenuLines[i] = { MenuId = 0, lineNum = 0, Type = 0, Text = "", TextId = 0, ValId = 0, Min=0, Max=0, Def=0, Val=nil }
|
|
end
|
|
ctx.CurLine = -1
|
|
|
|
menuLib.MenuPostProcessing(menu)
|
|
|
|
if (DEBUG_ON) then Log.LOG_write("RESPONSE Menu: %s\n", menuLib.menu2String(menu)) end
|
|
return menu
|
|
end
|
|
|
|
|
|
local function DSM_parseMenuLine()
|
|
--ex: 0x09 0x03 0x00 0x10 0x00 0x1C 0xF9 0x00 0x10 0x10 0x00 0x00 0x00 0x00 0x03 0x00
|
|
--ex: 0x09 0x03 0x61 0x10 0x00 0x6C 0x50 0x00 0x00 0x10 0x36 0x00 0x49 0x00 0x36 0x00
|
|
--ex: 0x09 0x03 0x65 0x10 0x00 0x0C 0x51 0x00 0x00 0x10 0x00 0x00 0xF4 0x00 0x2E 0x00
|
|
-- MenuLSB MenuMSB line Type TextID NextLSB NextMSB Val_Min Val_Max Val_Def
|
|
|
|
local ctx = DSM_Context
|
|
local i = multiBuffer(14)
|
|
local type = multiBuffer(15)
|
|
local line = ctx.MenuLines[i]
|
|
|
|
-- are we trying to override existing line
|
|
if (line.Type > 0 and type == 0) then
|
|
if (DEBUG_ON) then Log.LOG_write("RESPONSE MenuLine: ERROR. Trying to ZERO Override: %s\n", menuLib.menuLine2String(line)) end
|
|
return ctx.MenuLines[ctx.CurLine]
|
|
end
|
|
|
|
ctx.CurLine = i
|
|
|
|
line.lineNum = i
|
|
line.MenuId = Dsm_to_Int16(multiBuffer(12), multiBuffer(13))
|
|
line.Type = type
|
|
line.TextId = Dsm_to_Int16(multiBuffer(16), multiBuffer(17))
|
|
line.Text = nil -- Fill at Post processing
|
|
line.ValId = Dsm_to_Int16(multiBuffer(18), multiBuffer(19))
|
|
|
|
-- Singed int values
|
|
line.Min = Dsm_to_SInt16(multiBuffer(20), multiBuffer(21))
|
|
line.Max = Dsm_to_SInt16(multiBuffer(22), multiBuffer(23))
|
|
line.Def = Dsm_to_SInt16(multiBuffer(24), multiBuffer(25))
|
|
|
|
line.Val=nil
|
|
|
|
menuLib.MenuLinePostProcessing(line)
|
|
|
|
if (DEBUG_ON) then Log.LOG_write("RESPONSE MenuLine: %s\n", menuLib.menuLine2String(line)) end
|
|
|
|
if (line.MenuId~=ctx.Menu.MenuId) then
|
|
-- Going Back too fast: Stil receiving lines from previous menu
|
|
ctx.Menu.MenuId = line.MenuId
|
|
Log.LOG_write("WARNING: Overriding current Menu from Line\n")
|
|
end
|
|
|
|
return line
|
|
end
|
|
|
|
local function DSM_parseMenuValue()
|
|
--ex: 0x09 0x04 0x53 0x10 0x00 0x10 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
|
--ex: 0x09 0x04 0x61 0x10 0x02 0x10 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
|
-- MenuLSB MenuMSB ValLSB ValMSB V_LSB V_MSB
|
|
|
|
-- Identify the line and update the value
|
|
local ctx = DSM_Context
|
|
local menuId = Dsm_to_Int16(multiBuffer(12), multiBuffer(13))
|
|
local valId = Dsm_to_Int16(multiBuffer(14), multiBuffer(15))
|
|
local value = Dsm_to_SInt16(multiBuffer(16), multiBuffer(17)) --Signed int
|
|
|
|
local updatedLine = nil
|
|
for i = 0, MAX_MENU_LINES do -- Find the menu line for this value
|
|
local line = ctx.MenuLines[i]
|
|
if line ~= nil and line.Type ~= 0 then
|
|
if line.Type ~= LINE_TYPE.MENU and line.ValId == valId then -- identifier of ValueId stored in the line
|
|
line.Val = value
|
|
ctx.CurLine = i
|
|
updatedLine = line
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
if (updatedLine == nil) then
|
|
if (DEBUG_ON) then Log.LOG_write("RESPONSE MenuValue: ERROR, Cant find Menu Line with MenuId=%X, ValID=%X to update\n", menuId, valId) end
|
|
else
|
|
if (DEBUG_ON) then Log.LOG_write("RESPONSE MenuValue: UPDATED: %s\n", menuLib.menuLine2String(updatedLine))
|
|
end
|
|
end
|
|
return updatedLine ~= nil
|
|
end
|
|
|
|
local function DSM_parseReqTxInfo()
|
|
local portNo = multiBuffer(12)
|
|
TxInfo_Type = multiBuffer(13)
|
|
if (DEBUG_ON) then Log.LOG_write("RESPONSE ReqTXChannelInfo(#%d %s InfoType=0x%0X)\n",
|
|
portNo, MODEL.PORT_TEXT[portNo], TxInfo_Type) end
|
|
|
|
TxInfo_Step = 0
|
|
|
|
return portNo
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
local function DSM_processResponse()
|
|
local ctx = DSM_Context
|
|
local cmd = multiBuffer(11) -- Response Command
|
|
|
|
if (DEBUG_ON > 1) then Log.LOG_write("%s: RESPONSE %s \n", menuLib.phase2String(ctx.Phase), multiBuffer2String()) end
|
|
if (DEBUG_ON and cmd > 0x00) then Log.LOG_write("%3.3f %s: ", menuLib.getElapsedTime(), menuLib.phase2String(ctx.Phase)) end
|
|
|
|
if cmd == 0x01 then -- read version
|
|
DSM_parseRxVersion()
|
|
--Lib.Init_Text(DSM_Context.RX.Id)
|
|
ctx.isReset = false -- no longer resetting
|
|
ctx.Phase = PHASE.MENU_TITLE
|
|
ctx.Menu.MenuId = 0
|
|
|
|
elseif cmd == 0x02 then -- read menu title
|
|
local menu = DSM_parseMenu()
|
|
|
|
-- Update Selected Line navigation
|
|
if menu.NextId ~= 0 then
|
|
ctx.SelLine = NEXT_BUTTON -- highlight Next
|
|
else
|
|
ctx.SelLine = BACK_BUTTON -- highlight Back
|
|
end
|
|
|
|
if (ctx.Menu.MenuId == 0x0001) then -- Still in RESETTING MENU???
|
|
-- Star from Start
|
|
if (DEBUG_ON) then Log.LOG_write("RX Reset: Still not done, restart again!!!\n") end
|
|
ctx.Menu.MenuId = 0
|
|
ctx.Phase = PHASE.RX_VERSION
|
|
else
|
|
ctx.Phase = PHASE.MENU_LINES
|
|
end
|
|
|
|
|
|
elseif cmd == 0x03 then -- menu lines
|
|
local line = DSM_parseMenuLine()
|
|
|
|
-- Update Selected line navigation
|
|
if (ctx.SelLine == BACK_BUTTON or ctx.SelLine == NEXT_BUTTON or ctx.SelLine == PREV_BUTTON)
|
|
and menuLib.isSelectableLine(line) then -- Auto select the current line
|
|
ctx.SelLine = line.lineNum
|
|
end
|
|
|
|
ctx.Phase = PHASE.MENU_LINES
|
|
|
|
elseif cmd == 0x04 then -- read menu values
|
|
if DSM_parseMenuValue() then
|
|
ctx.Phase = PHASE.MENU_VALUES
|
|
else
|
|
ctx.Phase = PHASE.WAIT_CMD
|
|
end
|
|
|
|
elseif cmd == 0x05 then -- Request TX Info
|
|
local portNo = DSM_parseReqTxInfo()
|
|
ctx.CurLine = portNo
|
|
ctx.Phase = PHASE.MENU_REQ_TX_INFO
|
|
|
|
elseif cmd == 0xA7 then -- answer to EXIT command
|
|
if (DEBUG_ON) then Log.LOG_write("RESPONSE RX Exit\n") end
|
|
if (ctx.Phase==PHASE.EXIT) then -- Expected RX Exit
|
|
ctx.Phase = PHASE.EXIT_DONE
|
|
else -- UnExpected RX Exit
|
|
DSM_ReleaseConnection()
|
|
error("RX Connection Drop")
|
|
end
|
|
|
|
elseif cmd == 0x00 then -- NULL response (or RX heartbeat)
|
|
-- 09 00 01 00 00 00 00 00 00 00 00 00 00 00 00
|
|
-- 09 00 7E 00 20 9E 28 00 20 9E 28 00 20 8D 7E : After TX Heartbeat one of this (FC6250)
|
|
-- 09 00 18 00 20 08 00 00 00 08 00 00 00 98 AE AR8360T
|
|
if (DEBUG_ON) then Log.LOG_write("%3.3f %s: RESPONSE RX Heartbeat --Context: 0x%02X\n",
|
|
menuLib.getElapsedTime(), menuLib.phase2String(ctx.Phase), multiBuffer(12)) end
|
|
else
|
|
if (DEBUG_ON) then Log.LOG_write("RESPONSE Unknown Command (0x%X) DATA=%s\n", cmd, multiBuffer2String()) end
|
|
end
|
|
|
|
return cmd
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
local function DSM_Send_Receive()
|
|
local ctx = DSM_Context
|
|
|
|
-- Receive part: Process incoming messages if there is nothing to send
|
|
if ctx.SendDataToRX == 0 and multiBuffer(10) == 0x09 then
|
|
local cmd = DSM_processResponse()
|
|
|
|
multiBuffer(10, 0x00) -- Clear Response Buffer to know that we are done with the response
|
|
RXInactivityTime = getTime() + SEND_TIMEOUT*4 -- Reset RX Inactivity timeout
|
|
|
|
if (cmd > 0x00) then -- RX HeartBeat ??
|
|
-- Only change to SEND mode if we received a valid response (Ignore heartbeat)
|
|
ctx.SendDataToRX = 1
|
|
ctx.Refresh_Display = true
|
|
end
|
|
else
|
|
if (getTime() > RXInactivityTime and ctx.Phase ~= PHASE.RX_VERSION and ctx.Phase ~= PHASE.EXIT_DONE) then
|
|
if (ctx.isEditing()) then -- If Editing, Extend Timeout
|
|
RXInactivityTime = getTime() + SEND_TIMEOUT*4
|
|
else
|
|
if (DEBUG_ON) then Log.LOG_write("%3.3f %s: RX INACTIVITY TIMEOUT\n", menuLib.getElapsedTime(), menuLib.phase2String(ctx.Phase)) end
|
|
DSM_ReleaseConnection()
|
|
error("RX Disconnected")
|
|
end
|
|
end
|
|
end
|
|
|
|
-----TX Part --------------------------------------
|
|
if ctx.SendDataToRX == 1 then -- Need to send a request
|
|
ctx.SendDataToRX = 0
|
|
DSM_sendRequest()
|
|
TXInactivityTime = getTime() + SEND_TIMEOUT -- Reset Inactivity timeout
|
|
else
|
|
-- Check if enouth time has passed from last transmit/receive activity
|
|
if getTime() > TXInactivityTime then
|
|
ctx.SendDataToRX = 1 -- Switch to Send mode to send heartbeat
|
|
ctx.Refresh_Display = true
|
|
|
|
-- Only change to WAIT_CMD if we are NOT wating for RX version
|
|
if ctx.Phase ~= PHASE.RX_VERSION then
|
|
-- Phase = If IsEditing then VALUE_CHANGING_WAIT else WAIT_CMD
|
|
ctx.Phase = (ctx.isEditing() and PHASE.VALUE_CHANGING_WAIT) or PHASE.WAIT_CMD
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Init
|
|
local function DSM_Init(toolName)
|
|
local dateTime = getDateTime()
|
|
local dateStr = dateTime.year.."-"..dateTime.mon.."-"..dateTime.day.." "..dateTime.hour..":"..dateTime.min
|
|
|
|
local ver, radio, maj, minor, rev, osname = getVersion()
|
|
|
|
if (osname==nil) then osname = "OpenTX" end -- OTX 2.3.14 and below returns nil
|
|
|
|
IS_EDGETX = string.sub(osname,1,1) == 'E'
|
|
|
|
if (DEBUG_ON) then
|
|
Log.LOG_write("---------------DSM New Session %s ----------------\n", toolName, dateStr)
|
|
Log.LOG_write("Radio Info: %s\n", radio .. " " .. (osname or "OpenTx") .. " " .. ver)
|
|
Log.LOG_write("Date : %s\n", dateStr)
|
|
Log.LOG_write("DsmLib Version : %s\n", LIB_VERSION)
|
|
end
|
|
end
|
|
|
|
local function DSM_Init_Text_Exceptions()
|
|
-- Apply special restrictions to some Values
|
|
|
|
local function getTxChText(ch)
|
|
return " ("..(MODEL.TX_CH_TEXT[ch] or "--")..")"
|
|
end
|
|
|
|
local List_Values = menuLib.List_Values
|
|
local List_Text = menuLib.List_Text
|
|
local Text = menuLib.Text
|
|
|
|
Log.LOG_write("Initializing TEXT Exception\n")
|
|
|
|
-- Channel selection for SAFE MODE and GAINS on FC6250HX
|
|
--List_Text[0x000C] = "Inhibit?" --?
|
|
for i = 0, 7 do
|
|
List_Text[0x000D + i] = "Ch"..(i+5) ..getTxChText(i+4)
|
|
end -- Aux channels (Ch5 and Greater)
|
|
|
|
-- Servo Output values..
|
|
local servoOutputValues = {0x0003,0x002D,0x002E,0x002F} --Inh (GAP), 5.5ms, 11ms, 22ms. Fixing L_m1 with 0..244 range!
|
|
--List_Text[0x002D] = "5.5ms"
|
|
--[0x002E] = "11ms"
|
|
--List_Text[0x002F] = "22ms"
|
|
|
|
-- Gain Values
|
|
local gainValues = {0x0032,0x0033,0x0034} -- 1X, 2X, 4X -- Fixing L_m1 with 0..244 range!
|
|
--List_Text[0x0032] = "1 X"
|
|
--[0x0033] = "2 X"
|
|
-- List_Text[0x0034] = "4 X"
|
|
|
|
-- List of Channels for Safe, Gains, Panic, except FC6250HX that uses other range (0x00C..0x015)
|
|
-- the valid range Starts with GEAR if enabled (Thr,Ail,Ele,Rud are not valid, the RX reject them )
|
|
-- Valid Values: Inhibit? (GAP), Gear,Aux1..Aux7,X-Plus-1..XPlus-8
|
|
local channelValues = {0x0035,0x0036,0x0037,0x0038,0x0039,0x003A,0x003B,0x003C,0x003D,0x003E,0x003F,
|
|
0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049}
|
|
|
|
--List_Text[0x0035] = "Inhibit?"
|
|
for i = 0, 11 do
|
|
List_Text[0x0036 + i] = "Ch"..(i+1) .. getTxChText(i)
|
|
end -- Channels on AR637T
|
|
|
|
for i = 1, 8 do -- 41..49
|
|
List_Text[0x0041 + i] = "Ch"..(i+12)
|
|
end
|
|
|
|
-- Flight mode channel selection
|
|
--Text[0x0078] = "FM Channel"
|
|
List_Values[0x0078]=channelValues
|
|
|
|
-- Gain channel selection
|
|
--Text[0x0089] = "Gain Channel"
|
|
List_Values[0x0089]=channelValues
|
|
|
|
-- Gain Sensitivity selection
|
|
--Text[0x008A] = "Gain Sensitivity/r";
|
|
List_Values[0x008A]=gainValues -- Right Alight, (L_M1 was wide open range 0->244)
|
|
|
|
-- Safe mode options, Ihnibit + this values
|
|
local safeModeOptions = {0x0003,0x00B0,0x00B1} -- inh (gap), "Self-Level/Angle Dem, Envelope
|
|
--List_Text[0x00B0] = "Self-Level/Angle Dem"
|
|
--List_Text[0x00B1] = "Envelope"
|
|
|
|
--Text[0x00D2] = "Panic Channel"
|
|
List_Values[0x00D2]=channelValues
|
|
|
|
--Inh, Self-Level/Angle Dem, Envelope -- (L_M was wide open range 0->244)
|
|
--Text[0x01F8] = "Safe Mode";
|
|
List_Values[0x01F8]=safeModeOptions
|
|
end
|
|
|
|
-- Initial Setup
|
|
local function FP_Init(toolname)
|
|
DSM_Context.Phase = PHASE.INIT
|
|
|
|
DSM_Init(toolname)
|
|
menuLib.clearAllText()
|
|
end
|
|
|
|
local initStep=0
|
|
local FileState = {}
|
|
|
|
local function Message_Init()
|
|
lcd.clear()
|
|
if (initStep == 0) then
|
|
if (IS_EDGETX) then
|
|
-- Load all messages at once
|
|
menuLib.LoadTextFromFile(MSG_FILE,13)
|
|
initStep=1
|
|
|
|
else
|
|
-- load messages incrementally to avoid "CPU Limit"
|
|
lcd.drawText(30, 50, "Loading Msg file: "..(FileState.lineNo or 0))
|
|
if (menuLib.INC_LoadTextFromFile(MSG_FILE, FileState)==1) then
|
|
initStep=1
|
|
end
|
|
end
|
|
elseif (initStep == 1) then
|
|
DSM_Init_Text_Exceptions()
|
|
|
|
DSM_Context.Phase = PHASE.RX_VERSION
|
|
DSM_StartConnection()
|
|
end
|
|
end
|
|
|
|
local function FP_Run()
|
|
if (DSM_Context.Phase==PHASE.INIT) then
|
|
Message_Init()
|
|
return 0
|
|
end
|
|
|
|
return DSM_Send_Receive()
|
|
end
|
|
|
|
local function FP_Done()
|
|
local ctx = menuLib.DSM_Context
|
|
ctx.Phase = PHASE.EXIT_DONE
|
|
DSM_ReleaseConnection()
|
|
end
|
|
|
|
return { init=FP_Init, run=FP_Run, done=FP_Done }
|