Initial Commit

This commit is contained in:
Annika Merris 2025-06-26 07:54:22 -04:00
commit 933eee69e4
216 changed files with 20588 additions and 0 deletions

View file

@ -0,0 +1,813 @@
---- #########################################################################
---- # #
---- # 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 }

View file

@ -0,0 +1,33 @@
local LogLib = { }
local LOG_FILE = "/LOGS/dsm_log.txt"
local logFile = nil
local logCount=0
function LogLib.LOG_open()
logFile = io.open(LOG_FILE, "w") -- Truncate Log File
end
function LogLib.LOG_write(...)
if (logFile==nil) then LogLib.LOG_open() end
local str = string.format(...)
if (str==nil) then return end
io.write(logFile, str)
str = string.gsub(str,"\n"," ") -- Elimitate return from line, since print will do it
print(str)
if (logCount > 10) then -- Close an re-open the file
io.close(logFile)
logFile = io.open(LOG_FILE, "a")
logCount =0
end
end
function LogLib.LOG_close()
if (logFile~=nil) then io.close(logFile) end
end
return LogLib

View file

@ -0,0 +1,167 @@
local MODEL, M_DATA, LOG_write = ...
--[[
local MODEL = {
modelName = "", -- The name of the model comming from OTX/ETX
hashName = "",
modelOutputChannel = {}, -- Output information from OTX/ETX
TX_CH_TEXT= { [0]=""},
PORT_TEXT = { [0]=""},
DSM_ChannelInfo = {} -- Data Created by DSM Configuration Script
}
-- MENU DATA Management
local M_DATA = {} -- Store the variables used in the Menus.
--]]
local DATA_PATH = "/MODELS/DSMDATA"
local TX_CHANNELS = 12
local MV_DATA_END = 1040
local function hashName(mName)
local c=10000;
local prefix = string.gsub(mName,"%.","_") -- Change any "." to "_"
prefix = string.gsub(prefix,"% ","_") -- Change any space to "_"
prefix = string.sub(prefix,1,5) -- Take the first 5 characters
-- Simple Hash of the Model Name adding each character
for i = 1, #mName do
local ch = string.byte(mName,i,i)
c=c+ch
end
return (prefix .. c) -- Return Prefix + Hash
end
-- Load Menu Data from a file
local function ST_LoadFileData()
local fname = hashName(MODEL.modelName)..".txt"
MODEL.hashName = fname
-- Clear Menu Data
for i = 0, MV_DATA_END do
M_DATA[i]=nil
end
print("Loading File:"..fname)
local dataFile = io.open(DATA_PATH .. "/".. fname, "r") -- read File
-- cannot read file???
if (dataFile==nil) then return 0 end
local line = io.read(dataFile, 5000)
io.close(dataFile)
if #line == 0 then return 0 end -- No data??
-- Process the input, each line is "Var_Id : Value" format
-- Store it into MANU_DATA
local i=0
for k, v in string.gmatch(line, "(%d+):(%d+)") do
M_DATA[k+0]=v+0 -- do aritmentic to convert string to number
i=i+1
end
-- Return 0 if no lines processed, 1 otherwise
if (i > 0) then return 1 else return 0 end
end
local function getModuleChannelOrder(num)
--Determine fist 4 channels order
local ch_n={}
local st_n = {[0]= "R", "E", "T", "A" }
local c_ord=num -- ch order
if (c_ord == -1) then
ch_n[0] = st_n[3]
ch_n[1] = st_n[1]
ch_n[2] = st_n[2]
ch_n[3] = st_n[0]
else
ch_n[bit32.band(c_ord,3)] = st_n[3]
c_ord = math.floor(c_ord/4)
ch_n[bit32.band(c_ord,3)] = st_n[1]
c_ord = math.floor(c_ord/4)
ch_n[bit32.band(c_ord,3)] = st_n[2]
c_ord = math.floor(c_ord/4)
ch_n[bit32.band(c_ord,3)] = st_n[0]
end
local s = ""
for i=0,3 do
s=s..ch_n[i]
end
return s
end
local function ReadTxModelData()
local TRANSLATE_AETR_TO_TAER=false
local table = model.getInfo() -- Get the model name
MODEL.modelName = table.name
local module = model.getModule(0) -- Internal
if (module==nil or module.Type~=6) then module = model.getModule(1) end -- External
if (module~=nil) then
if (module.Type==6 ) then -- MULTI-MODULE
local chOrder = module.channelsOrder
local s = getModuleChannelOrder(chOrder)
LOG_write("MultiChannel Ch Order: [%s] %s\n",chOrder,s)
if (s=="AETR") then TRANSLATE_AETR_TO_TAER=true
else TRANSLATE_AETR_TO_TAER=false
end
end
end
-- Read Ch1 to Ch10
local i= 0
for i = 0, TX_CHANNELS-1 do
local ch = model.getOutput(i) -- Zero base
if (ch~=nil) then
MODEL.modelOutputChannel[i] = ch
if (string.len(ch.name)==0) then
ch.formatCh = string.format("TX:Ch%i",i+1)
else
ch.formatCh = string.format("TX:Ch%i/%s",i+1,ch.name or "--")
end
end
end
-- Translate AETR to TAER
if (TRANSLATE_AETR_TO_TAER) then
LOG_write("Applying AETR -> TAER translation\n")
local ail = MODEL.modelOutputChannel[0]
local elv = MODEL.modelOutputChannel[1]
local thr = MODEL.modelOutputChannel[2]
MODEL.modelOutputChannel[0] = thr
MODEL.modelOutputChannel[1] = ail
MODEL.modelOutputChannel[2] = elv
end
-- Create the Port Text to be used
LOG_write("Ports/Channels:\n")
for i = 0, TX_CHANNELS-1 do
local ch = MODEL.modelOutputChannel[i]
if (ch~=nil) then
MODEL.TX_CH_TEXT[i] = ch.formatCh
MODEL.PORT_TEXT[i] = string.format("P%i (%s) ",i+1,MODEL.TX_CH_TEXT[i])
LOG_write("Port%d %s [%d,%d] Rev=%d, Off=%d, ppmC=%d, syn=%d\n",i+1,MODEL.TX_CH_TEXT[i],math.floor(ch.min/10),math.floor(ch.max/10), ch.revert, ch.offset, ch.ppmCenter, ch.symetrical)
end
end
end
-- Main Program
LOG_write("Reading Model Info\n")
ReadTxModelData()
local r = ST_LoadFileData()
return r

View file

@ -0,0 +1,282 @@
local MODEL, M_DATA, LOG_write = ...
--[[
local MODEL = {
modelName = "", -- The name of the model comming from OTX/ETX
hashName = "",
modelOutputChannel = {}, -- Output information from OTX/ETX
TX_CH_TEXT= { [0]=""},
PORT_TEXT = { [0]=""},
DSM_ChannelInfo = {} -- Data Created by DSM Configuration Script
}
-- MENU DATA Management
local M_DATA = {} -- Store the variables used in the Menus.
--]]
local TX_CHANNELS = 12
local AT_PLANE = 0
local aircraft_type_text = {[0]="Plane","Heli","Glider","Drone"}
--[[
local WT_A1 = 0
local WT_A2 = 1
local WT_FLPR = 2
local WT_A1_F1 = 3
local WT_A2_F1 = 4
local WT_A2_F2 = 5
--]]
local WT_ELEVON_A = 6
local WT_ELEVON_B = 7
local wing_type_text = {[0]="Normal","Dual Ail","Flapperon", "Ail + Flp","Dual Ail + Flp","Dual Ail/Flp","Elevon A","Elevon B"}
--[[
local TT_R1 = 0
local TT_R1_E1 = 1
local TT_R1_E2 = 2
local TT_R2_E1 = 3
local TT_R2_E2 = 4
--]]
local TT_VT_A = 5
local TT_VT_B = 6
local TT_TLRN_A = 7
local TT_TLRN_B = 8
local TT_TLRN_A_R2 = 9
local TT_TLRN_B_R2 = 10
local tail_type_text = {[0]="Rud Only","Normal","Rud + Dual Ele","Dual Rud + Elv","Dual Rud/Ele",
"VTail A","VTail B","Taileron A","Taileron B","Taileron A + Dual Rud","Taileron B + Dual Rud"}
local MV_AIRCRAFT_TYPE = 1001
local MV_WING_TYPE = 1002
local MV_TAIL_TYPE = 1003
local MV_CH_BASE = 1010
local MV_CH_THR = 1010
local MV_CH_L_AIL = 1011
local MV_CH_R_AIL = 1012
local MV_CH_L_FLP = 1013
local MV_CH_R_FLP = 1014
local MV_CH_L_RUD = 1015
local MV_CH_R_RUD = 1016
local MV_CH_L_ELE = 1017
local MV_CH_R_ELE = 1018
local MV_PORT_BASE = 1020
local MV_DATA_END = 1040
--Channel Types --
local CT_NONE = 0x00
local CT_AIL = 0x01
local CT_ELE = 0x02
local CT_RUD = 0x04
local CT_REVERSE = 0x20
local CT_THR = 0x40
local CT_SLAVE = 0x80
-- Seems like Reverse Mix is complement of the 3 bits
local CMT_NORM = 0x00 -- 0000
local CMT_AIL = 0x10 -- 0001 Taileron
local CMT_ELE = 0x20 -- 0010 For VTIAL and Delta-ELEVON
local CMT_RUD = 0x30 -- 0011 For VTIAL
local CMT_RUD_REV = 0x40 -- 0100 For VTIAL
local CMT_ELE_REV = 0x50 -- 0101 For VTIAL and Delta-ELEVON A
local CMT_AIL_REV = 0x60 -- 0110 Taileron
local CMT_NORM_REV = 0x70 -- 0111
local MT_NORMAL = 0
local MT_REVERSE = 1
local function channelType2String(byte1, byte2)
local s = ""
if (byte2==0) then return s end;
if (bit32.band(byte2,CT_AIL)>0) then s=s.."Ail" end
if (bit32.band(byte2,CT_ELE)>0) then s=s.."Ele" end
if (bit32.band(byte2,CT_RUD)>0) then s=s.."Rud" end
if (bit32.band(byte2,CT_THR)>0) then s=s.."Thr" end
if (bit32.band(byte2,CT_REVERSE)>0) then s=s.."-" end
if (bit32.band(byte2,CT_SLAVE)>0) then s=s.." Slv" end
if (byte1==CMT_NORM) then s=s.." "
elseif (byte1==CMT_AIL) then s=s.." M_Ail"
elseif (byte1==CMT_ELE) then s=s.." M_Ele"
elseif (byte1==CMT_RUD) then s=s.." M_Rud"
elseif (byte1==CMT_RUD_REV) then s=s.." M_Rud-"
elseif (byte1==CMT_ELE_REV) then s=s.." M_Ele-"
elseif (byte1==CMT_AIL_REV) then s=s.." M_Ail-"
elseif (byte1==CMT_NORM_REV) then s=s.." M-"
end
return s;
end
-- This Creates the Servo Settings that will be used to pass to
-- Forward programming
local function CreateDSMPortChannelInfo()
local function ApplyWingMixA(b2)
-- ELEVON
if (b2==CT_AIL+CT_ELE) then return CMT_ELE end; -- 0x03
if (b2==CT_AIL+CT_ELE+CT_SLAVE) then return CMT_NORM end; -- 0x83
end
local function ApplyWingMixB(b2)
-- ELEVON
if (b2==CT_AIL+CT_ELE) then return CMT_NORM end; -- 0x03
if (b2==CT_AIL+CT_ELE+CT_SLAVE) then return CMT_ELE end; -- 0x83
end
local function ApplyTailMixA(b2)
-- VTAIL
-- Default normal/reverse behaviour
if (b2==CT_RUD+CT_ELE) then return CMT_NORM end; -- 0x06
if (b2==CT_RUD+CT_ELE+CT_SLAVE) then return CMT_ELE end; -- 0x86
--TAILERON
-- Default normal/reverse behaviour
if (b2==CT_AIL+CT_ELE) then return CMT_NORM end; -- 0x03
if (b2==CT_AIL+CT_ELE+CT_SLAVE) then return CMT_AIL end; -- 0x83
end
local function ApplyTailMixB(b2)
-- VTAIL
-- Default normal/reverse behaviour
if (b2==CT_RUD+CT_ELE) then return CMT_NORM end; -- 0x06
if (b2==CT_RUD+CT_ELE+CT_SLAVE) then return CMT_RUD end; -- 0x86
--TAILERON
if (b2==CT_AIL+CT_ELE) then return CMT_AIL end; -- 0x03
if (b2==CT_AIL+CT_ELE+CT_SLAVE) then return CMT_NORM end; -- 0x83
end
local function reverseMix(b)
if (b==CMT_NORM) then return CMT_NORM_REV end;
if (b==CMT_AIL) then return CMT_AIL_REV end;
if (b==CMT_ELE) then return CMT_ELE_REV end;
if (b==CMT_RUD) then return CMT_RUD_REV end;
return b
end
local DSM_Ch = MODEL.DSM_ChannelInfo
for i=0, TX_CHANNELS-1 do
DSM_Ch[i] = {[0]= CMT_NORM, CT_NONE, nil} -- Initialize with no special function
end
--local aircraftType = M_DATA[MV_AIRCRAFT_TYPE]
local wingType = M_DATA[MV_WING_TYPE]
local tailType = M_DATA[MV_TAIL_TYPE]
local thrCh = M_DATA[MV_CH_THR]
local lAilCh = M_DATA[MV_CH_L_AIL]
local rAilCh = M_DATA[MV_CH_R_AIL]
local lElevCh = M_DATA[MV_CH_L_ELE]
local rElevCh = M_DATA[MV_CH_R_ELE]
local lRudCh = M_DATA[MV_CH_L_RUD]
local rRudCh = M_DATA[MV_CH_R_RUD]
-- Channels in menu vars are Zero base, Channel info is 1 based
-- THR
if (thrCh~=nil and thrCh < 10) then DSM_Ch[thrCh][1]= CT_THR end
-- AIL (Left and Right)
if (lAilCh~=nil) then DSM_Ch[lAilCh][1] = CT_AIL end
if (rAilCh~=nil) then DSM_Ch[rAilCh][1] = CT_AIL+CT_SLAVE end
-- ELE (Left and Right)
if (lElevCh~=nil) then DSM_Ch[lElevCh][1] = CT_ELE end
if (rElevCh~=nil) then DSM_Ch[rElevCh][1] = CT_ELE+CT_SLAVE end
-- RUD (Left and Right)
if (lRudCh~=nil) then DSM_Ch[lRudCh][1] = CT_RUD end
if (rRudCh~=nil) then DSM_Ch[rRudCh][1] = CT_RUD+CT_SLAVE end
-- VTAIL: RUD + ELE
if (tailType==TT_VT_A) then
DSM_Ch[lElevCh][1] = CT_RUD+CT_ELE
DSM_Ch[rElevCh][1] = CT_RUD+CT_ELE+CT_SLAVE
elseif (tailType==TT_VT_B) then
DSM_Ch[lElevCh][1] = CT_RUD+CT_ELE+CT_SLAVE
DSM_Ch[rElevCh][1] = CT_RUD+CT_ELE
end
-- TAILERRON: 2-ELE + AIL
if (tailType==TT_TLRN_A or tailType==TT_TLRN_A_R2) then
DSM_Ch[lElevCh][1] = CT_AIL+CT_ELE
DSM_Ch[rElevCh][1] = CT_AIL+CT_ELE+CT_SLAVE
elseif (tailType==TT_TLRN_B or tailType==TT_TLRN_B_R2) then
DSM_Ch[lElevCh][1] = CT_AIL+CT_ELE+CT_SLAVE
DSM_Ch[rElevCh][1] = CT_AIL+CT_ELE
end
---- ELEVON : AIL + ELE
if (wingType==WT_ELEVON_A) then
DSM_Ch[lAilCh][1] = CT_AIL+CT_ELE
DSM_Ch[rAilCh][1] = CT_AIL+CT_ELE+CT_SLAVE
elseif (wingType==WT_ELEVON_B) then
DSM_Ch[lAilCh][1] = CT_AIL+CT_ELE+CT_SLAVE
DSM_Ch[rAilCh][1] = CT_AIL+CT_ELE
end
------MIXES ---------
-- TAIL Mixes (Elevator and VTail)
if (tailType==TT_VT_A or tailType==TT_TLRN_A or tailType==TT_TLRN_A_R2) then
DSM_Ch[lElevCh][0] = ApplyTailMixA(DSM_Ch[lElevCh][1])
DSM_Ch[rElevCh][0] = ApplyTailMixA(DSM_Ch[rElevCh][1])
elseif (tailType==TT_VT_B or tailType==TT_TLRN_B or tailType==TT_TLRN_B_R2) then
DSM_Ch[lElevCh][0] = ApplyTailMixB(DSM_Ch[lElevCh][1])
DSM_Ch[rElevCh][0] = ApplyTailMixB(DSM_Ch[rElevCh][1])
end
---- ELEVON : AIL + ELE
if (wingType==WT_ELEVON_A) then
DSM_Ch[lAilCh][0] = ApplyWingMixA(DSM_Ch[lAilCh][1])
DSM_Ch[rAilCh][0] = ApplyWingMixA(DSM_Ch[rAilCh][1])
elseif (wingType==WT_ELEVON_B) then
DSM_Ch[lAilCh][0] = ApplyWingMixB(DSM_Ch[lAilCh][1])
DSM_Ch[rAilCh][0] = ApplyWingMixB(DSM_Ch[rAilCh][1])
end
-- Apply Gyro Reverse as needed for each channel as long as it is used
for i=0, TX_CHANNELS-1 do
if (M_DATA[MV_PORT_BASE+i]==MT_REVERSE and DSM_Ch[i][1]>0) then
DSM_Ch[i][0]=reverseMix(DSM_Ch[i][0])
DSM_Ch[i][1]=DSM_Ch[i][1]+CT_REVERSE
end
end
-- Show how it looks
for i=0, 9 do
local b1,b2 = DSM_Ch[i][0], DSM_Ch[i][1]
local s1 = channelType2String(b1,b2)
local s = string.format("%s (%02X %02X) %s\n", MODEL.PORT_TEXT[i],
b1, b2,s1)
DSM_Ch[i][2]=s1
LOG_write(s)
end
--MODEL.AirWingTailDesc = string.format("Aircraft(%s) Wing(%s) Tail(%s)",aircraft_type_text[aircraftType],wing_type_text[wingType],tail_type_text[tailType])
end
-- Main Program
LOG_write("Creating DSMPort Info\n")
CreateDSMPortChannelInfo()

View file

@ -0,0 +1,90 @@
local Log, menuLib, modelLib, DEBUG_ON, SIMULATION_ON = ... -- Get DebugON from parameters
local MAIN_MENU_LIB_VERSION = "0.56"
local MODEL = modelLib.MODEL
local PHASE = menuLib.PHASE
local LINE_TYPE = menuLib.LINE_TYPE
local lastGoodMenu=0
-- Creates the menus to Render with the GUI
local function ST_LoadMenu(menuId)
local ctx = menuLib.DSM_Context
menuLib.clearMenuLines()
if (menuId==0x1000) then -- MAIN MENU
ctx.Menu = { MenuId = 0x1000, Text = "Main Menu ("..MODEL.modelName..")", PrevId = 0, NextId = 0, BackId = 0, TextId=0 }
ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, Text = "Model Setup", ValId = 0xFFF3,TextId=0 }
if (SIMULATION_ON) then
ctx.MenuLines[4] = { Type = LINE_TYPE.MENU, Text = "RX Simulator (GUI dev only)", ValId = 0xFFF1, TextId=0 } -- Menu 0xFFF2 to SIMULATOR
end
ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, Text = "Forward Programming RX", ValId = 0xFFF2, TextId=0 } -- Menu 0xFFF2 to Real RX
ctx.SelLine = 6
lastGoodMenu = menuId
else
--print("NOT IMPLEMENTED")
ctx.Menu = { MenuId = 0x0002, Text = "NOT IMPLEMENTED", TextId = 0, PrevId = 0, NextId = 0, BackId = lastGoodMenu }
ctx.SelLine = menuLib.BACK_BUTTON
end
menuLib.PostProcessMenu()
end
local function Main_Send_Receive()
local ctx = menuLib.DSM_Context
if ctx.Phase == PHASE.RX_VERSION then -- Just Init RX Version
ctx.RX.Name = "Main Menu"
ctx.RX.Version = MAIN_MENU_LIB_VERSION
ctx.Phase = PHASE.MENU_TITLE
ctx.Menu.MenuId = 0
ctx.Refresh_Display = true
elseif ctx.Phase == PHASE.WAIT_CMD then
elseif ctx.Phase == PHASE.MENU_TITLE then -- request menu title
if ctx.Menu.MenuId == 0 then -- First time loading a menu ?
ST_LoadMenu(0x01000)
else
ST_LoadMenu(ctx.Menu.MenuId)
end
ctx.Phase = PHASE.WAIT_CMD
ctx.Refresh_Display = true
elseif ctx.Phase == PHASE.VALUE_CHANGING then -- send value
local line = ctx.MenuLines[ctx.SelLine] -- Updated Value of SELECTED line
--if (DEBUG_ON) then Log.LOG_write("%3.3f %s: ", menuLib.getElapsedTime(), menuLib.phase2String(ctx.Phase)) end
--if (DEBUG_ON) then Log.LOG_write("SEND SIM_updateMenuValue(ValueId=0x%X Text=\"%s\" Value=%s)\n", line.ValId, line.Text, menuLib.lineValue2String(line)) end
ctx.Phase = PHASE.VALUE_CHANGING_WAIT
elseif ctx.Phase == PHASE.VALUE_CHANGING_WAIT then
local line = ctx.MenuLines[ctx.SelLine]
elseif ctx.Phase == PHASE.VALUE_CHANGE_END then -- send value
local line = ctx.MenuLines[ctx.SelLine] -- Updated Value of SELECTED line
--if (DEBUG_ON) then Log.LOG_write("%3.3f %s: ", menuLib.getElapsedTime(), menuLib.phase2String(ctx.Phase)) end
--if (DEBUG_ON) then Log.LOG_write("SEND SIM_updateMenuValue(ValueId=0x%X Text=\"%s\" Value=%s)\n", line.ValId, line.Text, menuLib.lineValue2String(line)) end
--if (DEBUG_ON) then Log.LOG_write("SEND SIM_validateMenuValue(ValueId=0x%X Text=\"%s\" Value=%s)\n", line.ValId, line.Text, menuLib.lineValue2String(line)) end
ctx.Phase = PHASE.WAIT_CMD
elseif ctx.Phase == PHASE.EXIT then
ctx.Phase=PHASE.EXIT_DONE
return 1
end
return 0
end
local function Main_Init()
local ctx = menuLib.DSM_Context
ctx.Phase = PHASE.RX_VERSION
end
local function Main_Done()
local ctx = menuLib.DSM_Context
ctx.Phase = PHASE.EXIT_DONE
end
return { init=Main_Init, run=Main_Send_Receive, done=Main_Done }

View file

@ -0,0 +1,788 @@
--- #########################################################################
---- # #
---- # 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
local Log, DEBUG_ON = ... -- Parameters
local MenuLib = { }
local PHASE = {
INIT = 0,
RX_VERSION = 1,
WAIT_CMD = 2,
MENU_TITLE = 3,
MENU_REQ_TX_INFO = 4,
MENU_LINES = 5,
MENU_VALUES = 6,
VALUE_CHANGING = 7,
VALUE_CHANGING_WAIT = 8,
VALUE_CHANGE_END = 9,
EXIT = 10,
EXIT_DONE = 11
}
local LINE_TYPE = {
MENU = 0x1C,
LIST_MENU = 0x0C, -- List: INC Change + Validate
LIST_MENU_NC = 0x6C, -- List: No Incremental Change
LIST_MENU_NC2 = 0x6D, -- List: No Incremental Change (Frame Rate Herz)
LIST_MENU_TOG = 0x4C, -- List: Incremental Change, sometimes bolean/Toggle menu (if only 2 values)
LIST_MENU_ORI = 0xCC, -- List: Incremental Change, Orientation Heli
VALUE_NUM_I8_NC = 0x60, -- 8 bit number, no incremental change
VALUE_PERCENT = 0xC0, -- 8 bit number, Signed, percent
VALUE_DEGREES = 0xE0, -- 8 bit number, Signed, Degress
VALUE_NUM_I8 = 0x40, -- 8 bit number
VALUE_NUM_I16 = 0x41, -- 16 Bit number
VALUE_NUM_SI16 = 0xC1, -- 16 bit number, Signed
LT_EMPTY = 0x00
}
-- Bug in Lua compiler, confusing with global BOLD and RIGHT
local DISP_ATTR = {
_BOLD = 0x01, _RIGHT=0x02, _CENTER=0x04, PERCENT = 0x10, DEGREES=0x20, FORCED_MENU = 0x40
}
--RX IDs--
local RX = {
AR636B = 0x0001,
SPM4651T = 0x0014,
AR637T = 0x0015,
AR637TA = 0x0016,
FC6250HX = 0x0018,
AR630 = 0x0019,
AR8360T = 0x001A,
AR10360T = 0x001C,
AR631 = 0x001E
}
local DSM_Context = {
Phase = PHASE.INIT,
Menu = { MenuId = 0, Text = "", TextId = 0, PrevId = 0, NextId = 0, BackId = 0 },
MenuLines = {},
RX = { Id=0, Name = "", Version = "" },
Refresh_Display = true,
SendDataToRX = 1,
SelLine = 0, -- Current Selected Line
EditLine = nil, -- Current Editing Line
CurLine = -1, -- Current Line Requested/Parsed via h message protocol
isReset = false -- false when starting from scracts, true when starting from Reset
}
function DSM_Context.isEditing() return DSM_Context.EditLine~=nil end
local MAX_MENU_LINES = 6
local BACK_BUTTON = -1 -- Tread it as a display line #-1
local NEXT_BUTTON = MAX_MENU_LINES + 1 -- Tread it as a display line #7
local PREV_BUTTON = MAX_MENU_LINES + 2 -- Tread it as a display line #7
-- Text Arrays for Display Text and Debuging
local PhaseText = {}
local LineTypeText = {}
local Text = {} -- Text for Menu and Menu Lines (Headers only)
local List_Text = {} -- Messages for List Options (values only)
local List_Text_Img = {} -- If the Text has Attached Images
local List_Values = {} -- Additiona restrictions on List Values when non contiguos (L_M1 lines has this problem)
local Flight_Mode = {[0]="Fligh Mode"}
local RxName = {}
local StartTime = 0
------------------------------------------------------------------------------------------------------------
-- Get Elapsed Time since we started running the Script. Return a float in format: Seconds.Milliseconds
function MenuLib.getElapsedTime()
local t = getTime()
if (StartTime == 0) then StartTime = t end
return ((t - StartTime) * 10) / 1000
end
------------- Line Type helper functions ------------------------------------------------------------------
-- Check if the text are Flight modes, who will be treated different for Display
function MenuLib.isFlightModeLine(line)
return (line.TextId >= 0x8000 and line.TextId <= 0x8003)
end
function MenuLib.isSelectableLine(line) -- is the display line Selectable??
-- values who are not selectable
if (line.Type == 0) then return false end -- Empty Line
if (line.Type == LINE_TYPE.MENU and line.ValId == line.MenuId and bit32.band(line.TextAttr, DISP_ATTR.FORCED_MENU)==0) then return false end -- Menu that navigates to Itself?
if (line.Min==0 and line.Max==0 and line.Def==0) then return false end -- Values with no Range are only for display
if (line.Type == LINE_TYPE.VALUE_NUM_I8_NC and MenuLib.isFlightModeLine(line)) then return false end -- Flight mode is not Selectable
return true
end
function MenuLib.isEditableLine(line) -- is the display line editable??
-- values who are not editable
if (line.Type == 0 or line.Type == LINE_TYPE.MENU) then return false end -- Menus are not editable
if (line.Min==0 and line.Max==0 and line.Def==0) then return false end -- Values with no Range are only for display
if (line.Type == LINE_TYPE.VALUE_NUM_I8_NC and MenuLib.isFlightModeLine(line)) then return false end -- Flight mode is not Editable
-- any other is Editable
return true
end
function MenuLib.isListLine(line) -- is it a List of options??
if (line.Type == LINE_TYPE.LIST_MENU_NC or line.Type == LINE_TYPE.LIST_MENU or
line.Type == LINE_TYPE.LIST_MENU_TOG or line.Type == LINE_TYPE.LIST_MENU_NC2 or
line.Type == LINE_TYPE.LIST_MENU_ORI) then return true end
return false
end
function MenuLib.isPercentValueLineByMinMax(line)
return
(line.Min == 0 and line.Max == 100) or ( line.Min == -100 and line.Max == 100) or
( line.Min == 0 and line.Max == 150) or ( line.Min == -150 and line.Max == 150)
end
function MenuLib.isPercentValueLine(line) -- is it a Percent value??
if (line.Type == LINE_TYPE.VALUE_PERCENT) then return true end
return false
end
function MenuLib.isNumberValueLine(line) -- is it a number ??
if (MenuLib.isListLine(line) or line.Type == LINE_TYPE.MENU or line.Type == 0) then return false
else return true end
end
function MenuLib.isIncrementalValueUpdate(line)
if (line.Type == LINE_TYPE.LIST_MENU_NC or line.Type == LINE_TYPE.LIST_MENU_NC2 or
line.Type == LINE_TYPE.VALUE_NUM_I8_NC or line.Type == LINE_TYPE.VALUE_DEGREES) then return false end
return true
end
------------------------------------------------------------------------------------------------------------
function MenuLib.Get_Text(index)
if (index >= 0x8000) then
return Flight_Mode[0]
end
local out = Text[index] -- Find in regular header first
if out== nil then
out = List_Text[index] -- Try list values, don't think is necesary, but just playing Safe
end
if out == nil then -- unknown...
out = "Unknown_" .. string.format("%X", index)
end
return out
end
function MenuLib.Get_List_Text(index)
local out = List_Text[index] -- Try to find the message in List_Text
if out == nil then
out = Text[index] -- Try list headers, don't think is necesary, but just playing Safe
end
if out == nil then -- unknown...
out = "UnknownLT_" .. string.format("%X", index)
end
return out
end
function MenuLib.Get_List_Text_Img(index)
local out = List_Text_Img[index]
return out
end
function MenuLib.Get_List_Values(index)
local out = List_Values[index]
return out
end
function MenuLib.Get_RxName(index)
local out = RxName[index]
return out or ("RX_" .. string.format("%X", index))
end
----------- Debugging 2-String functions -------------------------------------------------------------------
function MenuLib.phase2String(index)
local out = PhaseText[index]
return out or ("Phase_" .. string.format("%X", index))
end
function MenuLib.lineType2String(index)
local out = LineTypeText[index]
return out or ("LT_" .. string.format("%X", index or 0xFF))
end
function MenuLib.lineValue2String(l)
if (DEBUG_ON == 0) then
return ""
end
if (l ~= nil and l.Val ~= nil) then
local value = l.Val
if MenuLib.isListLine(l) then
value = value .. "|\"" .. MenuLib.Get_List_Text(l.Val + l.TextStart) .. "\""
else
value = value..(l.Format or "")
end
return value
end
return "nil"
end
function MenuLib.menu2String(m)
local txt = "Menu[]"
if (m ~= nil) then
txt = string.format("M[Id=0x%X P=0x%X N=0x%X B=0x%X Text=\"%s\"[0x%X]]",
m.MenuId, m.PrevId, m.NextId, m.BackId, m.Text, m.TextId)
end
return txt
end
function MenuLib.menuLine2String(l)
local txt = "Line[]"
if (l ~= nil) then
local value = ""
local range = ""
if l.Type~=LINE_TYPE.MENU then
value = "Val="..MenuLib.lineValue2String(l)
if MenuLib.isListLine(l) then
range = string.format("NL=(%s->%s,%s,S=%s) ",l.Min, l.Max, l.Def, l.TextStart )
range = range .. (l.MinMaxOrig or "")
else
range = string.format("[%s->%s,%s]",l.Min, l.Max, l.Def)
end
end
txt = string.format("L[#%s T=%s VId=0x%X Text=\"%s\"[0x%X] %s %s MId=0x%X A=0x%X]",
l.lineNum, MenuLib.lineType2String(l.Type), l.ValId,
l.Text, l.TextId,
value,
range,
l.MenuId,
l.TextAttr
)
end
return txt
end
-----------------------------------------------------------------------------------------------------------
-- Post Procssing Line from Raw values receive by RX or Simulation
function MenuLib.isDisplayAttr(attr, bit)
return (bit32.band(attr,bit)>0)
end
function MenuLib.ExtractDisplayAttr(text1, attr)
local text = text1, pos;
for i=1,2 do
text, pos = string.gsub(text, "/c$", "")
if (pos>0) then -- CENTER
attr = bit32.bor(attr, DISP_ATTR._CENTER)
end
text, pos = string.gsub(text, "/r$", "")
if (pos>0) then -- RIGHT
attr = bit32.bor(attr, DISP_ATTR._RIGHT)
end
text, pos = string.gsub(text, "/p$", "")
if (pos>0) then -- Percent TEXT
attr = bit32.bor(attr, DISP_ATTR.PERCENT)
end
text, pos = string.gsub(text, "/b$", "")
if (pos>0) then -- BOLD TEXT
attr = bit32.bor(attr, DISP_ATTR._BOLD)
end
text, pos = string.gsub(text, "/m$", "")
if (pos>0) then -- FORCED MENU Button
attr = bit32.bor(attr, DISP_ATTR.FORCED_MENU)
end
end
return text, attr
end
function MenuLib.MenuPostProcessing(menu)
menu.Text, menu.TextAttr = MenuLib.ExtractDisplayAttr(menu.Text,menu.TextAttr or 0)
end
function MenuLib.MenuLinePostProcessing(line)
if (line.Text==nil) then
line.Text = MenuLib.Get_Text(line.TextId) -- Get Textual Line headeing text
end
-- Text formatting options
line.Text, line.TextAttr = MenuLib.ExtractDisplayAttr(line.Text,line.TextAttr or 0)
if line.Type == LINE_TYPE.MENU then
-- nothing to do on menu entries
line.Val=nil
elseif MenuLib.isListLine(line) then
-- Original Range for Debugging
line.MinMaxOrig = "[" .. line.Min .. "->" .. line.Max .. "," .. line.Def .. "]"
-- Normalize Min/Max to be relative to Zero
line.TextStart = line.Min
line.Def = line.Def - line.Min -- normalize default value
line.Max = line.Max - line.Min -- normalize max index
line.Min = 0 -- min index
else -- default to numerical value
if MenuLib.isPercentValueLine(line) or MenuLib.isPercentValueLineByMinMax(line) then
-- either explicit Percent or NO-Change value, but range is %Percent
line.Format ="%"
line.TextAttr = bit32.bor(line.TextAttr,DISP_ATTR.PERCENT)
elseif (line.Type == LINE_TYPE.VALUE_DEGREES) then
line.Format ="o"
line.TextAttr = bit32.bor(line.TextAttr,DISP_ATTR.DEGREES)
end
end
line.MinMaxDebug = MenuLib.lineType2String(line.Type).." "..(line.MinMaxOrig or "")
end
function MenuLib.ChangePhase(newPhase)
DSM_Context.Phase = newPhase
DSM_Context.SendDataToRX = 1
end
function MenuLib.Value_Add(line, inc)
if (DEBUG_ON) then Log.LOG_write("%3.3f %s: DSM_Value_Add(%s,%s)\n",
MenuLib.getElapsedTime(), MenuLib.phase2String(DSM_Context.Phase), inc, MenuLib.menuLine2String(line)) end
local skipIncrement = false
local values = nil
local origVal = line.Val
-- Use local validation for LIST_MENU1 when the range is wide open
-- Also use if for some LIST_MENU0 that the Range seems incorrect
if (MenuLib.isListLine(line)) then -- and line.Type==LINE_TYPE.LIST_MENU1 and line.Min==0 and line.Max==244) then
values = MenuLib.Get_List_Values(line.TextId)
end
if (values~=nil) then -- Inc/Dec based on a list of predefined Values Local to Script (values not contiguous),
-- locate current value in values array
-- Values are Zero normalized to the Start of the List (line.TextStart)
for i = 1, #values do
if ((values[i]-line.TextStart)==origVal) then
skipIncrement = true
if (inc==-1 and i > 1) then -- PREV
line.Val = values[i-1]-line.TextStart
elseif (inc==1 and i < #values) then -- NEXT
line.Val = values[i+1]-line.TextStart
end
break
end
end
end
if not skipIncrement then
-- Do it Sequentially
line.Val = line.Val + inc
if line.Val > line.Max then
line.Val = line.Max
elseif line.Val < line.Min then
line.Val = line.Min
end
end
if (origVal~=line.Val and MenuLib.isIncrementalValueUpdate(line)) then
-- Update RX value on every change
MenuLib.ChangePhase(PHASE.VALUE_CHANGING)
end
end
function MenuLib.Value_Default(line)
local origVal = line.Val
if (DEBUG_ON) then Log.LOG_write("%3.3f %s: DSM_Value_Default(%s)\n",
MenuLib.getElapsedTime(), MenuLib.phase2String(DSM_Context.Phase), MenuLib.menuLine2String(line)) end
line.Val = line.Def
if (origVal~=line.Val and MenuLib.isIncrementalValueUpdate(line)) then
-- Update RX value on every change
MenuLib.ChangePhase(PHASE.VALUE_CHANGING)
end
end
function MenuLib.Value_Write_Validate(line)
if (DEBUG_ON) then Log.LOG_write("%3.3f %s: DSM_Value_Write_Validate(%s)\n",
MenuLib.getElapsedTime(), MenuLib.phase2String(DSM_Context.Phase), MenuLib.menuLine2String(line)) end
MenuLib.ChangePhase(PHASE.VALUE_CHANGE_END) -- Update + Validate value in RX
DSM_Context.EditLine = nil -- Exit Edit Mode (By clearing the line editing)
end
function MenuLib.GotoMenu(menuId, lastSelectedLine)
if (DEBUG_ON) then Log.LOG_write("%3.3f %s: DSM_GotoMenu(0x%X,LastSelectedLine=%d)\n",
MenuLib.getElapsedTime(), MenuLib.phase2String(DSM_Context.Phase), menuId, lastSelectedLine) end
DSM_Context.Menu.MenuId = menuId
DSM_Context.SelLine = lastSelectedLine
-- Request to load the menu Again
MenuLib.ChangePhase(PHASE.MENU_TITLE)
end
function MenuLib.MoveSelectionLine(dir)
local ctx = DSM_Context
local menu = ctx.Menu
local menuLines = ctx.MenuLines
if (dir == 1) then -- NEXT
if ctx.SelLine <= MAX_MENU_LINES then
local num = ctx.SelLine
for i = ctx.SelLine + 1, MAX_MENU_LINES, 1 do
if MenuLib.isSelectableLine(menuLines[i]) then
ctx.SelLine = i
break
end
end
if num == ctx.SelLine then
if menu.NextId ~= 0 then -- Next
ctx.SelLine = NEXT_BUTTON
elseif menu.PrevId ~= 0 then -- Prev
ctx.SelLine = PREV_BUTTON
end
end
elseif menu.PrevId ~= 0 then -- Prev
ctx.SelLine = PREV_BUTTON
end
return
end
if (dir == -1) then -- PREV
if ctx.SelLine == PREV_BUTTON and menu.NextId ~= 0 then
ctx.SelLine = NEXT_BUTTON
elseif ctx.SelLine > 0 then
if ctx.SelLine > MAX_MENU_LINES then
ctx.SelLine = NEXT_BUTTON
end
local num = ctx.SelLine
for i = ctx.SelLine - 1, 0, -1 do
if MenuLib.isSelectableLine(menuLines[i]) then
ctx.SelLine = i
break
end
end
if num == ctx.SelLine then -- can't find previous selectable line, then SELECT Back
if (menu.BackId ~= 0) then ctx.SelLine = BACK_BUTTON end
end
else
if (menu.BackId ~= 0) then ctx.SelLine = BACK_BUTTON end -- Back
end
end
end
-- Clear each line of the menu
function MenuLib.clearMenuLines()
local ctx = DSM_Context
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, TextStart=0, Val=nil }
end
end
-- Post processing needed for each menu
function MenuLib.PostProcessMenu()
local ctx = DSM_Context
if (ctx.Menu.Text==nil) then
ctx.Menu.Text = MenuLib.Get_Text(ctx.Menu.TextId)
MenuLib.MenuPostProcessing (ctx.Menu)
end
--if (DEBUG_ON) then Log.LOG_write("SIM RESPONSE Menu: %s\n", MenuLib.menu2String(ctx.Menu)) end
for i = 0, MenuLib.MAX_MENU_LINES do -- clear menu
local line = ctx.MenuLines[i]
if (line.Type~=0) then
line.MenuId = ctx.Menu.MenuId
line.lineNum = i
MenuLib.MenuLinePostProcessing(line) -- Do the same post processing as if they come from the RX
--if (DEBUG_ON) then Log.LOG_write("SIM RESPONSE MenuLine: %s\n", MenuLib.menuLine2String(line)) end
end
end
end
function MenuLib.GetFlightModeValue(line)
local ret = line.Text.." "
local val = line.Val
if (val==nil) then return ret.."--" end
-- Adjust the displayed value for Flight mode line as needed
if (DSM_Context.RX.Id == RX.FC6250HX) then
-- Helicopter Flights modes
if (val==0) then ret = ret .. "1 / HOLD"
elseif (val==1) then ret = ret .. "2 / Normal"
elseif (val==2) then ret = ret .. "3 / Stunt 1"
elseif (val==3) then ret = ret .. "4 / Stunt 2"
elseif (val==4) then ret = ret .. "5 / Panic"
else
ret = ret .. " " .. (val + 1)
end
else
-- No adjustment needed
if (val==190) then
ret=ret.."Err:Out of Range"
else
ret=ret..(val + 1)
end
end
return ret
end
function MenuLib.Init()
print("MenuLib.Init()")
-- Phase Names
PhaseText[PHASE.INIT] = "INIT"
PhaseText[PHASE.RX_VERSION] = "RX_VERSION"
PhaseText[PHASE.WAIT_CMD] = "WAIT_CMD"
PhaseText[PHASE.MENU_TITLE] = "MENU_TITLE"
PhaseText[PHASE.MENU_REQ_TX_INFO] = "MENU_REQ_TX_INFO"
PhaseText[PHASE.MENU_LINES] = "MENU_LINES"
PhaseText[PHASE.MENU_VALUES] = "MENU_VALUES"
PhaseText[PHASE.VALUE_CHANGING] = "VALUE_CHANGING"
PhaseText[PHASE.VALUE_CHANGING_WAIT] = "VALUE_EDITING"
PhaseText[PHASE.VALUE_CHANGE_END] = "VALUE_CHANGE_END"
PhaseText[PHASE.EXIT] = "EXIT"
PhaseText[PHASE.EXIT_DONE] = "EXIT_DONE"
-- Line Types
LineTypeText[LINE_TYPE.MENU] = "M"
LineTypeText[LINE_TYPE.LIST_MENU_NC] = "LM_nc"
LineTypeText[LINE_TYPE.LIST_MENU] = "LM"
LineTypeText[LINE_TYPE.LIST_MENU_TOG] = "LM_tog"
LineTypeText[LINE_TYPE.LIST_MENU_NC2] = "LM_nc2"
LineTypeText[LINE_TYPE.LIST_MENU_ORI] = "LM_ori"
LineTypeText[LINE_TYPE.VALUE_NUM_I8_NC] = "V_nc"
LineTypeText[LINE_TYPE.VALUE_PERCENT] = "V_%"
LineTypeText[LINE_TYPE.VALUE_DEGREES] = "V_de"
LineTypeText[LINE_TYPE.VALUE_NUM_I8] = "V_i8"
LineTypeText[LINE_TYPE.VALUE_NUM_I16] = "V_i16"
LineTypeText[LINE_TYPE.VALUE_NUM_SI16] = "V_s16"
LineTypeText[LINE_TYPE.LT_EMPTY] = "Z"
DSM_Context.Phase = PHASE.RX_VERSION
end
function MenuLib.clearAllText()
local function clearTable(t)
for i, v in ipairs(t) do t[i] = nil end
end
clearTable(Text)
clearTable(List_Text)
clearTable(List_Text_Img)
clearTable(List_Values)
end
function MenuLib.LoadTextFromFile(fileName, mem)
local function rtrim(s)
local n = string.len(s)
while n > 0 and string.find(s, "^%s", n) do n = n - 1 end
return string.sub(s, 1, n)
end
--print(string.format("Loading messages from [%s]",fileName))
local dataFile = io.open(fileName, "r") -- read File
-- cannot read file???
assert(dataFile, "Cannot load Message file:" .. fileName)
local data = io.read(dataFile, mem * 1024) -- read up to 10k characters (newline char also counts!)
io.close(dataFile)
collectgarbage("collect")
local lineNo = 0
for line in string.gmatch(data, "[^\r\n]+") do
lineNo = lineNo + 1
--print(string.format("Line [%d]: %s",lineNo,line))
-- Remove Comments
local s = string.find(line, "--", 1, true)
if (s ~= nil) then
line = string.sub(line, 1, s - 1)
end
line = rtrim(line)
if (string.len(line) > 0) then
local a, b, c = string.match(line, "%s*(%a*)%s*|%s*(%w*)%s*|(.*)%s*")
--print(string.format("[%s] [%s] [%s]",a,b,c))
if (a ~= nil) then
local index = tonumber(b)
if (index == nil) then
assert(false, string.format("%s:%d: Invalid Hex num [%s]", fileName, lineNo, b))
elseif (a == "T") then
Text[index] = c
elseif (a == "LT") then
List_Text[index] = c
elseif (a == "LI") then
List_Text_Img[index] = c
elseif (a == "FM") then
Flight_Mode[0] = c
elseif (a == "RX") then
RxName[index] = c
else
assert(false, string.format("%s:%d: Invalid Line Type [%s]", fileName, lineNo, a))
end
end
end
if (lineNo % 50 == 0) then
collectgarbage("collect")
end
end -- For
--print(string.format("Loaded [%d] messages",lineNo))
data = nil
end
function MenuLib.INC_LoadTextFromFile(fileName, FileState)
-----------------------
local function rtrim(s)
local n = string.len(s)
while n > 0 and string.find(s, "^%s", n) do n = n - 1 end
return string.sub(s, 1, n)
end
local function GetTextInfoFromFile(pos)
local dataFile = io.open(fileName, "r")
io.seek(dataFile,pos)
local buff = io.read(dataFile, 100)
io.close(dataFile)
local line=""
local index=""
local type=""
local pipe=0
local comment=0
local newPos = pos
-- Parse the line:
-- Format: TT|0x999|Text -- Comment
for i=1,#buff do
newPos=newPos+1
local ch = string.sub(buff,i,i)
if (pipe < 2 and ch=="|") then pipe=pipe+1 -- Count pipes pos (Type | Index | .....)
elseif (ch=="\r") then -- Ignore CR
elseif (ch=="\n") then break -- LF, end of line
elseif (ch=="-") then -- March comments
comment=comment+1
if (comment==2) then pipe=6 end -- Comment part of line
else
-- regular char
comment=0
if (pipe==0) then type=type..ch -- in TT (Type)
elseif (pipe==1) then index=index..ch -- in Index
elseif (pipe<6) then line=line..ch end -- in Text
end -- Regular char
end -- For
return type, index, rtrim(line), newPos
end
-----------------------------------------------------------
if (FileState.state==nil) then -- Initial State
FileState.state=1
FileState.lineNo=0
FileState.filePos=0
end
if FileState.state==1 then
for l=1,10 do -- do 10 lines at a time
local type, sIndex, text
local lineStart = FileState.filePos
type, sIndex, text, FileState.filePos = GetTextInfoFromFile(FileState.filePos)
--print(string.format("T=%s, I=%s, T=%s LS=%d, FP=%d",type,sIndex,text,lineStart, FileState.filePos))
if (lineStart==FileState.filePos) then -- EOF
FileState.state=2 --EOF State
return 1
end
FileState.lineNo = FileState.lineNo + 1
type = rtrim(type)
if (string.len(type) > 0 and string.len(sIndex) > 0) then
local index = tonumber(sIndex)
if (index == nil) then
assert(false, string.format("%s:%d: Invalid Hex num [%s]", fileName, FileState.lineNo, sIndex))
elseif (type == "T") then
Text[index] = text
elseif (type == "LT") then
List_Text[index] = text
elseif (type == "LI") then
List_Text_Img[index] = text
elseif (type == "FM") then
Flight_Mode[0] = text
elseif (type == "RX") then
RxName[index] = text
else
assert(false, string.format("%s:%d: Invalid Line Type [%s]", fileName, FileState.lineNo, type))
end
end
end -- for
end -- if
return 0
end
-- Export some Constants and Variables
MenuLib.PHASE = PHASE
MenuLib.LINE_TYPE = LINE_TYPE
MenuLib.DISP_ATTR = DISP_ATTR
MenuLib.RX = RX
MenuLib.MAX_MENU_LINES = MAX_MENU_LINES
MenuLib.BACK_BUTTON = BACK_BUTTON
MenuLib.NEXT_BUTTON = NEXT_BUTTON
MenuLib.PREV_BUTTON = PREV_BUTTON
MenuLib.DSM_Context = DSM_Context
MenuLib.Text = Text
MenuLib.List_Text = List_Text
MenuLib.List_Text_Img = List_Text_Img
MenuLib.List_Values = List_Values
MenuLib.LOG_open = Log.LOG_open
MenuLib.LOG_write = Log.LOG_write
MenuLib.LOG_Close = Log.LOG_close
return MenuLib

View file

@ -0,0 +1,789 @@
local Log, DEBUG_ON = ...
local DATA_PATH = "/MODELS/DSMDATA" -- Path to store model settings files
local TX_CHANNELS = 12
-- MODEL information from ETX/OTX
local ModelLib = {}
local MODEL = {
modelName = "", -- The name of the model comming from OTX/ETX
modelOutputChannel = {}, -- Output information from OTX/ETX
AirWingTailDesc = "",
TX_CH_TEXT = {},
PORT_TEXT = {},
DSM_ChannelInfo = {} -- Data Created by DSM Configuration Script
}
--Channel Types --
local CH_TYPE = {
NONE = 0x00,
AIL = 0x01,
ELE = 0x02,
RUD = 0x04,
REVERSE = 0x20,
THR = 0x40,
SLAVE = 0x80,
}
-- Seems like Reverse Mix is complement of the 3 bits
local CH_MIX_TYPE = {
MIX_NORM = 0x00, -- 0000
MIX_AIL = 0x10, -- 0001 Taileron
MIX_ELE = 0x20, -- 0010 For VTIAL and Delta-ELEVON
MIX_RUD = 0x30, -- 0011 For VTIAL
MIX_RUD_REV = 0x40, -- 0100 For VTIAL
MIX_ELE_REV = 0x50, -- 0101 For VTIAL and Delta-ELEVON A
MIX_AIL_REV = 0x60, -- 0110 Taileron
MIX_NORM_REV = 0x70 -- 0111
}
local AIRCRAFT_TYPE = {
PLANE = 0,
HELI = 1,
GLIDER = 2,
DRONE = 3
}
local aircraft_type_text = {[0]="Plane","Heli","Glider","Drone"}
local WING_TYPE = {
AIL_1 = 0, --1
AIL_2 = 1, --2
FLAPERON = 2, --2
AIL_1_FLP_1 = 3, --2
AIL_2_FLP_1 = 4, --3
AIL_2_FLP_2 = 5, --4
ELEVON_A = 6, --2
ELEVON_B = 7 --2
}
local wing_type_text = {[0]="Normal","Dual Ail","Flapperon", "Ail + Flp","Dual Ail + Flp","Dual Ail/Flp","Elevon A","Elevon B"}
local TAIL_TYPE = {
RUD_1 = 0, -- 1
RUD_1_ELEV_1 = 1, -- 2
RUD_1_ELEV_2 = 2, -- 3
RUD_2_ELEV_1 = 3, -- 3
RUD_2_ELEV_2 = 4, -- 4
VTAIL_A = 5, -- 2
VTAIL_B = 6, -- 2
TRAILERON_A = 7, -- 3
TRAILERON_B = 8, -- 3
TRAILERON_A_R2 = 9, -- 3
TRAILERON_B_R2 = 10 -- 3
}
local tail_type_text = {[0]="Rud Only","Normal","Rud + Dual Ele","Dual Rud + Elv","Dual Rud/Ele",
"VTail A","VTail B","Taileron A","Taileron B","Taileron A + 2x Rud","Taileron B + 2x Rud"}
local CH_MODE_TYPE = {
NORMAL = 0,
REVERSE = 1,
USE_TX = 3
}
local PORT = {
PORT1 = 0,
PORT2 = 1,
PORT3 = 2,
PORT4 = 3,
PORT5 = 4,
PORT6 = 5,
PORT7 = 6,
PORT8 = 7,
PORT9 = 8,
PORT10 = 9,
PORT11 = 10,
PORT12 = 11
}
local MEMU_VAR = {
AIRCRAFT_TYPE = 1001,
WING_TYPE = 1002,
TAIL_TYPE = 1003,
CH_BASE = 1010,
CH_THR = 1010,
CH_L_AIL = 1011,
CH_R_AIL = 1012,
CH_L_FLP = 1013,
CH_R_FLP = 1014,
CH_L_RUD = 1015,
CH_R_RUD = 1016,
CH_L_ELE = 1017,
CH_R_ELE = 1018,
PORT_BASE = 1020,
PORT1_MODE = 1020,
PORT2_MODE = 1021,
PORT3_MODE = 1022,
PORT4_MODE = 1023,
PORT5_MODE = 1024,
PORT6_MODE = 1025,
PORT7_MODE = 1026,
PORT8_MODE = 1027,
PORT9_MODE = 1028,
PORT10_MODE = 1029,
PORT11_MODE = 1030,
PORT12_MODE = 1031,
DATA_END = 1040
}
-- MENU DATA Management
local MENU_DATA = {} -- Store the variables used in the Menus.
---- DSM_ChannelInfo ---------------------------------
-- First byte describe Special Mixing (Vtail/Elevon = 0x20)
--VTAIL
--(0x00 0x06) CH_TYPE.ELE+CH_TYPE.RUD (0x02+0x04 = 0x06)
--(0x20 0x86) CH_TYPE.ELE+CH_TYPE.RUD+CH_TYPE.SLAVE (0x02+0x04+0x80 = 0x86)
-- The 2nd byte describes the functionality of the port
--
-- Single Example: CH_TYPE.AIL (0x01) Aileron
-- Reverse Example: CH_TYPE.AIL+CH_TYPE.REVERSE (0x01+0x20=0x21) Reverse Aileron
-- Slave Example: CH_TYPE.AIL+CH_TYPE.SLAVE (0x01+0x80) -- 2nd servo Aileron
-- Elevon Example: CH_TYPE.AIL+CH_TYPE.ELE (0x01+0x02 = 0x03) -- Elevon
-- Elevon Example: CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE (0x01+0x02+0x80 = 0x83) -- Slave Elevon
-- RudElv (VTail) Example: CH_TYPE.ELE+CH_TYPE.RUD (0x02+0x04 = 0x06) -- Rudevator
-- RudElv (VTail) Example: CH_TYPE.ELE+CH_TYPE.RUD+CH_TYPE.SLAVE (0x02+0x04+0x80 = 0x86) -- Rudevator Slave
-- DEFAULT Simple Plane Port configuration (The Configuration tool will overrride this)
MODEL.DSM_ChannelInfo= {[0]= -- Start array at position 0
{[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.THR}, -- Ch1 Thr (0x40)
{[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.AIL}, -- Ch2 Ail (0x01)
{[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.ELE}, -- Ch2 ElE (0x02)
{[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.RUD}, -- Ch4 Rud (0x04)
{[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE}, -- Ch5 Gear (0x00)
{[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE}, -- Ch6 Aux1 (0x00)
{[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE}, -- Ch7 Aux2 (0x00)
{[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE}, -- Ch8 Aux3 (0x00)
{[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE}, -- Ch9 Aux4 (0x00)
{[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE}, -- Ch10 Aux5 (0x00)
{[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE}, -- Ch11 Aux6 (0x00)
{[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE} -- Ch12 Aux7 (0x00)
}
function ModelLib.printChannelSummary(a,w,t)
-- Summary
print("CHANNEL INFORMATION")
print("Aircraft:".. (aircraft_type_text[MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE]] or "--"))
print("Wing Type:".. (wing_type_text[MENU_DATA[MEMU_VAR.WING_TYPE]] or "--"))
print("Tail Type:".. (tail_type_text[MENU_DATA[MEMU_VAR.TAIL_TYPE]] or "--"))
print("Thr:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_THR] or 30)] or "--")) -- use fake ch30 for non existing channels
print("LAil:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_L_AIL] or 30)] or "--"))
print("RAil:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_R_AIL] or 30)] or "--"))
print("LFlp:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_L_FLP] or 30)] or "--"))
print("RFlp:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_R_FLP] or 30)] or "--"))
print("LEle:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_L_ELE] or 30)] or "--"))
print("REle:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_R_ELE] or 30)] or "--"))
print("LRud:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_L_RUD] or 30)] or "--"))
print("RRud:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_R_RUD] or 30)] or "--"))
end
function ModelLib.printServoReverseInfo()
print("SERVO Normal/Reverse INFORMATION")
for i=0, TX_CHANNELS-1 do
local s="--"
if (MENU_DATA[MEMU_VAR.PORT1_MODE+i] or 0) == 0 then s="NORMAL" else s="REVERSE" end
print(string.format("Port%d: %s", i+1, s))
end
end
function ModelLib.channelType2String(byte1, byte2)
local s = ""
if (byte2==0) then return s end;
if (bit32.band(byte2,CH_TYPE.AIL)>0) then s=s.."Ail" end
if (bit32.band(byte2,CH_TYPE.ELE)>0) then s=s.."Ele" end
if (bit32.band(byte2,CH_TYPE.RUD)>0) then s=s.."Rud" end
if (bit32.band(byte2,CH_TYPE.THR)>0) then s=s.."Thr" end
if (bit32.band(byte2,CH_TYPE.REVERSE)>0) then s=s.."-" end
if (bit32.band(byte2,CH_TYPE.SLAVE)>0) then s=s.." Slv" end
if (byte1==CH_MIX_TYPE.MIX_NORM) then s=s.." "
elseif (byte1==CH_MIX_TYPE.MIX_AIL) then s=s.." M_Ail"
elseif (byte1==CH_MIX_TYPE.MIX_ELE) then s=s.." M_Ele"
elseif (byte1==CH_MIX_TYPE.MIX_RUD) then s=s.." M_Rud"
elseif (byte1==CH_MIX_TYPE.MIX_RUD_REV) then s=s.." M_Rud-"
elseif (byte1==CH_MIX_TYPE.MIX_ELE_REV) then s=s.." M_Ele-"
elseif (byte1==CH_MIX_TYPE.MIX_AIL_REV) then s=s.." M_Ail-"
elseif (byte1==CH_MIX_TYPE.MIX_NORM_REV) then s=s.." M-"
end
return s;
end
-------------------------------------------------------------------------------------------------
-- Read the model information from OTX/ETX
local function getModuleChannelOrder(num)
--Determine fist 4 channels order
local channel_names={}
local stick_names = {[0]= "R", "E", "T", "A" }
local ch_order=num
if (ch_order == -1) then
channel_names[0] = stick_names[3]
channel_names[1] = stick_names[1]
channel_names[2] = stick_names[2]
channel_names[3] = stick_names[0]
else
channel_names[bit32.band(ch_order,3)] = stick_names[3]
ch_order = math.floor(ch_order/4)
channel_names[bit32.band(ch_order,3)] = stick_names[1]
ch_order = math.floor(ch_order/4)
channel_names[bit32.band(ch_order,3)] = stick_names[2]
ch_order = math.floor(ch_order/4)
channel_names[bit32.band(ch_order,3)] = stick_names[0]
end
local s = ""
for i=0,3 do
s=s..channel_names[i]
end
return s
end
function ModelLib.ReadTxModelData()
local TRANSLATE_AETR_TO_TAER=false
local table = model.getInfo() -- Get the model name
MODEL.modelName = table.name
local module = model.getModule(0) -- Internal
if (module==nil or module.Type~=6) then module = model.getModule(1) end -- External
if (module~=nil) then
if (module.Type==6 ) then -- MULTI-MODULE
local chOrder = module.channelsOrder
local s = getModuleChannelOrder(chOrder)
Log.LOG_write("MultiChannel Ch Order: [%s] %s\n",chOrder,s)
if (s=="AETR") then TRANSLATE_AETR_TO_TAER=true
else TRANSLATE_AETR_TO_TAER=false
end
end
end
Log.LOG_write("MODEL NAME = %s\n",MODEL.modelName)
-- Read Ch1 to Ch10
local i= 0
for i = 0, TX_CHANNELS-1 do
local ch = model.getOutput(i) -- Zero base
if (ch~=nil) then
MODEL.modelOutputChannel[i] = ch
if (string.len(ch.name)==0) then
ch.formatCh = string.format("TX:Ch%i",i+1)
else
ch.formatCh = string.format("TX:Ch%i/%s",i+1,ch.name or "--")
end
end
end
-- Translate AETR to TAER
-- TODO: Check if there is a way to know how to the TX is configured, since if it is
-- already TAER, is not needed
if (TRANSLATE_AETR_TO_TAER) then
Log.LOG_write("Applying AETR -> TAER translation\n")
local ail = MODEL.modelOutputChannel[0]
local elv = MODEL.modelOutputChannel[1]
local thr = MODEL.modelOutputChannel[2]
MODEL.modelOutputChannel[0] = thr
MODEL.modelOutputChannel[1] = ail
MODEL.modelOutputChannel[2] = elv
end
-- Create the Port Text to be used
Log.LOG_write("Ports/Channels:\n")
for i = 0, TX_CHANNELS-1 do
local ch = MODEL.modelOutputChannel[i]
if (ch~=nil) then
MODEL.TX_CH_TEXT[i] = ch.formatCh
MODEL.PORT_TEXT[i] = string.format("P%i (%s) ",i+1,MODEL.TX_CH_TEXT[i])
Log.LOG_write("Port%d %s [%d,%d] Rev=%d, Off=%d, ppmC=%d, syn=%d\n",i+1,MODEL.TX_CH_TEXT[i],math.floor(ch.min/10),math.floor(ch.max/10), ch.revert, ch.offset, ch.ppmCenter, ch.symetrical)
end
end
end
----------------------- FILE MANAGEMENT ---------------------------------------------
-- Create a fairly unique name for a model..combination of name and a hash
-- TODO: Check with ETX why we can't get the filename used to store the model info
-- Improvement request??
function ModelLib.hashName(mName)
local c=10000;
local prefix = string.gsub(mName,"%.","_") -- Change any "." to "_"
prefix = string.gsub(prefix,"% ","_") -- Change any space to "_"
prefix = string.sub(prefix,1,5) -- Take the first 5 characters
-- Simple Hash of the Model Name adding each character
for i = 1, #mName do
local ch = string.byte(mName,i,i)
c=c+ch
end
return (prefix .. c) -- Return Prefix + Hash
end
-- Load Menu Data from a file
function ModelLib.ST_LoadFileData()
local fname = ModelLib.hashName(MODEL.modelName)..".txt"
-- Clear Menu Data
for i = 0, MEMU_VAR.DATA_END do
MENU_DATA[i]=nil
end
print("Loading File:"..fname)
local dataFile = io.open(DATA_PATH .. "/".. fname, "r") -- read File
-- cannot read file???
if (dataFile==nil) then return 0 end
local line = io.read(dataFile, 5000)
io.close(dataFile)
if #line == 0 then return 0 end -- No data??
-- Process the input, each line is "Var_Id : Value" format
-- Store it into MANU_DATA
local i=0
for k, v in string.gmatch(line, "(%d+):(%d+)") do
--print(string.format("Read MENU_DATA[%d]:[%d]",k, v))
MENU_DATA[k+0]=v+0 -- do aritmentic to convert string to number
i=i+1
end
local currAircraftType = MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE]
local currWingType = MENU_DATA[MEMU_VAR.WING_TYPE]
local currTailType = MENU_DATA[MEMU_VAR.TAIL_TYPE]
print("Validation")
print(string.format("AIRCRAFT_TYPE(%d)=%s", MEMU_VAR.AIRCRAFT_TYPE,aircraft_type_text[currAircraftType]))
print(string.format("WING_TYPE(%d)=%s", MEMU_VAR.WING_TYPE, wing_type_text[currWingType]))
print(string.format("TAIL_TYPE(%d)=%s", MEMU_VAR.TAIL_TYPE, tail_type_text[currTailType]))
ModelLib.printChannelSummary()
ModelLib.printServoReverseInfo()
-- Return 0 if no lines processed, 1 otherwise
if (i > 0) then return 1 else return 0 end
end
-- Saves MENU_DATA to a file
function ModelLib.ST_SaveFileData()
local fname = ModelLib.hashName(MODEL.modelName)..".txt"
print("Saving File:"..fname)
local dataFile = assert(io.open(DATA_PATH .. "/" .. fname, "w"),"Please create "..DATA_PATH.." folder") -- write File
-- Foreach MENU_DATA with a value write Var_Id:Value into file
for i = 0, MEMU_VAR.DATA_END do
if (MENU_DATA[i]~=nil) then
--print(string.format("Write MENU_DATA[%s] : %s",i,MENU_DATA[i]))
io.write(dataFile,string.format("%s:%s\n",i,MENU_DATA[i]))
end
end
io.close(dataFile)
end
-- This Creates the Servo Settings that will be used to pass to
-- Forward programming
function ModelLib.CreateDSMPortChannelInfo()
local function ApplyWingMixA(b2)
-- ELEVON
if (b2==CH_TYPE.AIL+CH_TYPE.ELE) then return CH_MIX_TYPE.MIX_ELE end; -- 0x03
if (b2==CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE) then return CH_MIX_TYPE.MIX_NORM end; -- 0x83
end
local function ApplyWingMixB(b2)
-- ELEVON
if (b2==CH_TYPE.AIL+CH_TYPE.ELE) then return CH_MIX_TYPE.MIX_NORM end; -- 0x03
if (b2==CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE) then return CH_MIX_TYPE.MIX_ELE end; -- 0x83
end
local function ApplyTailMixA(b2)
-- VTAIL
-- Default normal/reverse behaviour
if (b2==CH_TYPE.RUD+CH_TYPE.ELE) then return CH_MIX_TYPE.MIX_NORM end; -- 0x06
if (b2==CH_TYPE.RUD+CH_TYPE.ELE+CH_TYPE.SLAVE) then return CH_MIX_TYPE.MIX_ELE end; -- 0x86
--TAILERON
-- Default normal/reverse behaviour
if (b2==CH_TYPE.AIL+CH_TYPE.ELE) then return CH_MIX_TYPE.MIX_NORM end; -- 0x03
if (b2==CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE) then return CH_MIX_TYPE.MIX_AIL end; -- 0x83
end
local function ApplyTailMixB(b2)
-- VTAIL
-- Default normal/reverse behaviour
if (b2==CH_TYPE.RUD+CH_TYPE.ELE) then return CH_MIX_TYPE.MIX_NORM end; -- 0x06
if (b2==CH_TYPE.RUD+CH_TYPE.ELE+CH_TYPE.SLAVE) then return CH_MIX_TYPE.MIX_RUD end; -- 0x86
--TAILERON
if (b2==CH_TYPE.AIL+CH_TYPE.ELE) then return CH_MIX_TYPE.MIX_AIL end; -- 0x03
if (b2==CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE) then return CH_MIX_TYPE.MIX_NORM end; -- 0x83
end
local function reverseMix(b)
if (b==CH_MIX_TYPE.MIX_NORM) then return CH_MIX_TYPE.MIX_NORM_REV end;
if (b==CH_MIX_TYPE.MIX_AIL) then return CH_MIX_TYPE.MIX_AIL_REV end;
if (b==CH_MIX_TYPE.MIX_ELE) then return CH_MIX_TYPE.MIX_ELE_REV end;
if (b==CH_MIX_TYPE.MIX_RUD) then return CH_MIX_TYPE.MIX_RUD_REV end;
return b
end
local DSM_ChannelInfo = MODEL.DSM_ChannelInfo
for i=0, TX_CHANNELS-1 do
DSM_ChannelInfo[i] = {[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE} -- Initialize with no special function
end
local aircraftType = MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE]
local wingType = MENU_DATA[MEMU_VAR.WING_TYPE]
local tailType = MENU_DATA[MEMU_VAR.TAIL_TYPE]
local thrCh = MENU_DATA[MEMU_VAR.CH_THR]
local lAilCh = MENU_DATA[MEMU_VAR.CH_L_AIL]
local rAilCh = MENU_DATA[MEMU_VAR.CH_R_AIL]
local lflapCh = MENU_DATA[MEMU_VAR.CH_L_FLP]
local rflapCh = MENU_DATA[MEMU_VAR.CH_R_FLP]
local lElevCh = MENU_DATA[MEMU_VAR.CH_L_ELE]
local rElevCh = MENU_DATA[MEMU_VAR.CH_R_ELE]
local lRudCh = MENU_DATA[MEMU_VAR.CH_L_RUD]
local rRudCh = MENU_DATA[MEMU_VAR.CH_R_RUD]
-- Channels in menu vars are Zero base, Channel info is 1 based
-- THR
if (thrCh~=nil and thrCh < 10) then DSM_ChannelInfo[thrCh][1]= CH_TYPE.THR end
-- AIL (Left and Right)
if (lAilCh~=nil) then DSM_ChannelInfo[lAilCh][1] = CH_TYPE.AIL end
if (rAilCh~=nil) then DSM_ChannelInfo[rAilCh][1] = CH_TYPE.AIL+CH_TYPE.SLAVE end
-- ELE (Left and Right)
if (lElevCh~=nil) then DSM_ChannelInfo[lElevCh][1] = CH_TYPE.ELE end
if (rElevCh~=nil) then DSM_ChannelInfo[rElevCh][1] = CH_TYPE.ELE+CH_TYPE.SLAVE end
-- RUD (Left and Right)
if (lRudCh~=nil) then DSM_ChannelInfo[lRudCh][1] = CH_TYPE.RUD end
if (rRudCh~=nil) then DSM_ChannelInfo[rRudCh][1] = CH_TYPE.RUD+CH_TYPE.SLAVE end
-- VTAIL: RUD + ELE
if (tailType==TAIL_TYPE.VTAIL_A or tailType==TAIL_TYPE.VTAIL_B) then
DSM_ChannelInfo[lElevCh][1] = CH_TYPE.RUD+CH_TYPE.ELE
DSM_ChannelInfo[rElevCh][1] = CH_TYPE.RUD+CH_TYPE.ELE+CH_TYPE.SLAVE
end
-- TAILERRON: 2-ELE + AIL
if (tailType==TAIL_TYPE.TRAILERON_A or tailType==TAIL_TYPE.TRAILERON_A_R2 or
tailType==TAIL_TYPE.TRAILERON_B or tailType==TAIL_TYPE.TRAILERON_B_R2) then
DSM_ChannelInfo[lElevCh][1] = CH_TYPE.AIL+CH_TYPE.ELE
DSM_ChannelInfo[rElevCh][1] = CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE
end
---- ELEVON : AIL + ELE
if (wingType==WING_TYPE.ELEVON_A or wingType==WING_TYPE.ELEVON_B) then
DSM_ChannelInfo[lAilCh][1] = CH_TYPE.AIL+CH_TYPE.ELE
DSM_ChannelInfo[rAilCh][1] = CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE
end
------MIXES ---------
-- TAIL Mixes (Elevator and VTail)
if (tailType==TAIL_TYPE.VTAIL_A or tailType==TAIL_TYPE.TRAILERON_A or tailType==TAIL_TYPE.TRAILERON_A_R2) then
DSM_ChannelInfo[lElevCh][0] = ApplyTailMixA(DSM_ChannelInfo[lElevCh][1])
DSM_ChannelInfo[rElevCh][0] = ApplyTailMixA(DSM_ChannelInfo[rElevCh][1])
elseif (tailType==TAIL_TYPE.VTAIL_B or tailType==TAIL_TYPE.TRAILERON_B or tailType==TAIL_TYPE.TRAILERON_B_R2) then
DSM_ChannelInfo[lElevCh][0] = ApplyTailMixA(DSM_ChannelInfo[lElevCh][1])
DSM_ChannelInfo[rElevCh][0] = ApplyTailMixA(DSM_ChannelInfo[rElevCh][1])
end
---- Wing Mixes
if (wingType==WING_TYPE.ELEVON_A) then
DSM_ChannelInfo[lAilCh][0] = ApplyWingMixA(DSM_ChannelInfo[lAilCh][1])
DSM_ChannelInfo[rAilCh][0] = ApplyWingMixA(DSM_ChannelInfo[rAilCh][1])
elseif (wingType==WING_TYPE.ELEVON_B) then
DSM_ChannelInfo[lAilCh][0] = ApplyWingMixB(DSM_ChannelInfo[lAilCh][1])
DSM_ChannelInfo[rAilCh][0] = ApplyWingMixB(DSM_ChannelInfo[rAilCh][1])
end
-- Apply Gyro Reverse as needed for each channel as long as it is used
for i=0, TX_CHANNELS-1 do
if (MENU_DATA[MEMU_VAR.PORT_BASE+i]==CH_MODE_TYPE.REVERSE and DSM_ChannelInfo[i][1]>0) then
DSM_ChannelInfo[i][0]=reverseMix(DSM_ChannelInfo[i][0])
DSM_ChannelInfo[i][1]=DSM_ChannelInfo[i][1]+CH_TYPE.REVERSE
end
end
-- Show how it looks
for i=0, 9 do
local b1,b2 = DSM_ChannelInfo[i][0], DSM_ChannelInfo[i][1]
print(string.format("%s (%02X %02X) %s", MODEL.PORT_TEXT[i],
b1, b2, ModelLib.channelType2String(b1,b2)))
end
MODEL.AirWingTailDesc = string.format("Aircraft(%s) Wing(%s) Tail(%s)",aircraft_type_text[aircraftType],wing_type_text[wingType],tail_type_text[tailType])
end
function ModelLib.ST_PlaneWingInit(wingType)
print("Change Plane WingType:"..wing_type_text[wingType])
MENU_DATA[MEMU_VAR.WING_TYPE] = wingType
-- Clear all Wing Data
MENU_DATA[MEMU_VAR.CH_L_AIL] = nil
MENU_DATA[MEMU_VAR.CH_R_AIL] = nil
MENU_DATA[MEMU_VAR.CH_L_FLP] = nil
MENU_DATA[MEMU_VAR.CH_R_FLP] = nil
MENU_DATA[MEMU_VAR.CH_THR] = PORT.PORT1
-- Default Channel Assisgments for each Wing type
if (wingType==WING_TYPE.AIL_1) then
MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT2
elseif (wingType==WING_TYPE.AIL_2) then
MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT6
MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2
elseif (wingType==WING_TYPE.FLAPERON) then
MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT6
MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2
elseif (wingType==WING_TYPE.AIL_1_FLP_1) then
MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT2
MENU_DATA[MEMU_VAR.CH_L_FLP] = PORT.PORT6
elseif (wingType==WING_TYPE.AIL_2_FLP_1) then
MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT6
MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2
MENU_DATA[MEMU_VAR.CH_L_FLP] = PORT.PORT5
elseif (wingType==WING_TYPE.AIL_2_FLP_2) then
MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT6
MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2
MENU_DATA[MEMU_VAR.CH_R_FLP] = PORT.PORT5
MENU_DATA[MEMU_VAR.CH_L_FLP] = PORT.PORT7
elseif (wingType==WING_TYPE.ELEVON_A) then
MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT2
MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT3
elseif (wingType==WING_TYPE.ELEVON_B) then
MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT3
MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2
else -- Assume normal
print("ERROR: Invalid Wing Type")
end
ModelLib.printChannelSummary()
end
function ModelLib.ST_PlaneTailInit(tailType)
if (MENU_DATA[MEMU_VAR.WING_TYPE]==WING_TYPE.ELEVON_A or
MENU_DATA[MEMU_VAR.WING_TYPE]==WING_TYPE.ELEVON_B) then
tailType = TAIL_TYPE.RUD_1 -- Delta only have ruder
end
print("Change Plane Tail Type:"..tail_type_text[tailType])
-- Clear all data for Tail
MENU_DATA[MEMU_VAR.TAIL_TYPE] = tailType
MENU_DATA[MEMU_VAR.CH_L_ELE] = nil
MENU_DATA[MEMU_VAR.CH_R_ELE] = nil
MENU_DATA[MEMU_VAR.CH_L_RUD] = nil
MENU_DATA[MEMU_VAR.CH_R_RUD] = nil
-- Setup Channels for different Tail types
if (tailType == TAIL_TYPE.RUD_1) then
MENU_DATA[MEMU_VAR.CH_L_RUD] = PORT.PORT4
elseif (tailType == TAIL_TYPE.RUD_1_ELEV_1) then
MENU_DATA[MEMU_VAR.CH_L_ELE] = PORT.PORT3
MENU_DATA[MEMU_VAR.CH_L_RUD] = PORT.PORT4
elseif (tailType == TAIL_TYPE.RUD_1_ELEV_2) then
MENU_DATA[MEMU_VAR.CH_L_ELE] = PORT.PORT5
MENU_DATA[MEMU_VAR.CH_R_ELE] = PORT.PORT3
MENU_DATA[MEMU_VAR.CH_L_RUD] = PORT.PORT4
elseif (tailType == TAIL_TYPE.RUD_2_ELEV_1) then
MENU_DATA[MEMU_VAR.CH_L_ELE] = PORT.PORT3
MENU_DATA[MEMU_VAR.CH_L_RUD] = PORT.PORT4
MENU_DATA[MEMU_VAR.CH_R_RUD] = PORT.PORT5
elseif (tailType == TAIL_TYPE.RUD_2_ELEV_2) then
MENU_DATA[MEMU_VAR.CH_L_ELE] = PORT.PORT5
MENU_DATA[MEMU_VAR.CH_R_ELE] = PORT.PORT3
MENU_DATA[MEMU_VAR.CH_L_RUD] = PORT.PORT4
MENU_DATA[MEMU_VAR.CH_R_RUD] = PORT.PORT6
elseif (tailType == TAIL_TYPE.VTAIL_A or tailType == TAIL_TYPE.VTAIL_B) then
MENU_DATA[MEMU_VAR.CH_L_ELE] = PORT.PORT4
MENU_DATA[MEMU_VAR.CH_R_ELE] = PORT.PORT3
elseif (tailType == TAIL_TYPE.TRAILERON_A or tailType==TAIL_TYPE.TRAILERON_A_R2 or
tailType == TAIL_TYPE.TRAILERON_B or tailType==TAIL_TYPE.TRAILERON_B_R2) then
MENU_DATA[MEMU_VAR.CH_L_RUD] = PORT.PORT4
MENU_DATA[MEMU_VAR.CH_L_ELE] = PORT.PORT5
MENU_DATA[MEMU_VAR.CH_R_ELE] = PORT.PORT3
else -- Assume Normal
print("ERROR:invalid Tail Type")
end
if (tailType == TAIL_TYPE.TRAILERON_A_R2 or tailType==TAIL_TYPE.TRAILERON_B_R2) then
MENU_DATA[MEMU_VAR.CH_R_RUD] = PORT.PORT7
end
ModelLib.printChannelSummary()
end
function ModelLib.ST_GliderWingInit(wingType)
print("Change Glider WingType:"..wing_type_text[wingType])
MENU_DATA[MEMU_VAR.WING_TYPE] = wingType
-- Clear all Wing Data
MENU_DATA[MEMU_VAR.CH_L_AIL] = nil
MENU_DATA[MEMU_VAR.CH_R_AIL] = nil
MENU_DATA[MEMU_VAR.CH_L_FLP] = nil
MENU_DATA[MEMU_VAR.CH_R_FLP] = nil
MENU_DATA[MEMU_VAR.CH_THR] = PORT.PORT6
-- Default Channel Assisgments for each Wing type
if (wingType==WING_TYPE.AIL_1) then
MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT1
elseif (wingType==WING_TYPE.AIL_2) then
MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT1
MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2
elseif (wingType==WING_TYPE.AIL_2_FLP_1) then
MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT1
MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2
MENU_DATA[MEMU_VAR.CH_L_FLP] = PORT.PORT5
elseif (wingType==WING_TYPE.AIL_2_FLP_2) then
MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT1
MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2
MENU_DATA[MEMU_VAR.CH_L_FLP] = PORT.PORT5
MENU_DATA[MEMU_VAR.CH_R_FLP] = PORT.PORT6
MENU_DATA[MEMU_VAR.CH_THR] = PORT.PORT7
elseif (wingType==WING_TYPE.ELEVON_A) then
MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT1
MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2
elseif (wingType==WING_TYPE.ELEVON_B) then
MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT2
MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT1
else -- Assume normal
print("ERROR: Invalid Wing Type")
end
ModelLib.printChannelSummary()
end
function ModelLib.ST_GliderTailInit(tailType)
if (MENU_DATA[MEMU_VAR.WING_TYPE]==WING_TYPE.ELEVON_A) then
tailType = TAIL_TYPE.RUD_1 -- Delta only have ruder
end
print("Change Glider Tail Type:"..tail_type_text[tailType])
-- Clear all data for Tail
MENU_DATA[MEMU_VAR.TAIL_TYPE] = tailType
MENU_DATA[MEMU_VAR.CH_L_ELE] = nil
MENU_DATA[MEMU_VAR.CH_R_ELE] = nil
MENU_DATA[MEMU_VAR.CH_L_RUD] = nil
MENU_DATA[MEMU_VAR.CH_R_RUD] = nil
-- Setup Channels for different Tail types
if (tailType == TAIL_TYPE.RUD_1) then
MENU_DATA[MEMU_VAR.CH_L_RUD] = PORT.PORT4
elseif (tailType == TAIL_TYPE.RUD_1_ELEV_1) then
MENU_DATA[MEMU_VAR.CH_L_ELE] = PORT.PORT3
MENU_DATA[MEMU_VAR.CH_L_RUD] = PORT.PORT4
elseif (tailType == TAIL_TYPE.VTAIL_A) then
MENU_DATA[MEMU_VAR.CH_L_ELE] = PORT.PORT4
MENU_DATA[MEMU_VAR.CH_R_ELE] = PORT.PORT3
elseif (tailType == TAIL_TYPE.VTAIL_B) then
MENU_DATA[MEMU_VAR.CH_L_ELE] = PORT.PORT3
MENU_DATA[MEMU_VAR.CH_R_ELE] = PORT.PORT4
else -- Assume Normal
print("ERROR: Invalid Tail Type")
end
ModelLib.printChannelSummary()
end
function ModelLib.ST_AircraftInit(aircraftType)
MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE] = aircraftType
print("Change Aircraft:".. aircraft_type_text[aircraftType])
-- Setup Default Aircraft Wing/Tail
if (aircraftType==AIRCRAFT_TYPE.PLANE) then
ModelLib.ST_PlaneWingInit(WING_TYPE.AIL_1)
ModelLib.ST_PlaneTailInit(TAIL_TYPE.RUD_1_ELEV_1)
elseif (aircraftType==AIRCRAFT_TYPE.GLIDER) then
ModelLib.ST_GliderWingInit(WING_TYPE.AIL_1)
ModelLib.ST_GliderTailInit(TAIL_TYPE.RUD_1_ELEV_1)
else
ModelLib.ST_PlaneWingInit(WING_TYPE.AIL_1)
ModelLib.ST_PlaneTailInit(TAIL_TYPE.RUD_1_ELEV_1)
end
end
-- Setup Initial Default Data for the Menus
function ModelLib.ST_Default_Data()
print("Initializing Menu DATA")
ModelLib.ST_AircraftInit(AIRCRAFT_TYPE.PLANE)
print("Initializing Servo Reverse from TX output settings")
MENU_DATA[MEMU_VAR.PORT1_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT1].revert
MENU_DATA[MEMU_VAR.PORT2_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT2].revert
MENU_DATA[MEMU_VAR.PORT3_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT3].revert
MENU_DATA[MEMU_VAR.PORT4_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT4].revert
MENU_DATA[MEMU_VAR.PORT5_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT5].revert
MENU_DATA[MEMU_VAR.PORT6_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT6].revert
MENU_DATA[MEMU_VAR.PORT7_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT7].revert
MENU_DATA[MEMU_VAR.PORT8_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT8].revert
MENU_DATA[MEMU_VAR.PORT9_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT9].revert
MENU_DATA[MEMU_VAR.PORT10_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT10].revert
ModelLib.printServoReverseInfo()
end
ModelLib.TX_CHANNELS = TX_CHANNELS
ModelLib.MODEL = MODEL
ModelLib.CH_TYPE = CH_TYPE
ModelLib.CH_MODE_TYPE = CH_MODE_TYPE
ModelLib.AIRCRAFT_TYPE = AIRCRAFT_TYPE
ModelLib.WING_TYPE = WING_TYPE
ModelLib.TAIL_TYPE = TAIL_TYPE
ModelLib.MENU_DATA = MENU_DATA
ModelLib.MEMU_VAR = MEMU_VAR
ModelLib.PORT = PORT
ModelLib.DATA_PATH = DATA_PATH
ModelLib.aircraft_type_text = aircraft_type_text
ModelLib.wing_type_text = wing_type_text
ModelLib.tail_type_text = tail_type_text
return ModelLib

View file

@ -0,0 +1,467 @@
---- #########################################################################
---- # #
---- # Copyright (C) OpenTX #
-----# #
---- # 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 scrip does the airplane Setup similar to how a a Spektrum radio does
-- it. You can select the plane type, the Wing type, etc.
-- This settings are needed for ForwardProgramming to send the TX aircraft
-- configuration to the RX when in Initial Setup
-- Author: Francisco Arzu
------------------------------------------------------------------------------
local Log, menuLib, modelLib, DEBUG_ON, SIMULATION_ON = ... -- Get DebugON from parameters
local SETUP_LIB_VERSION = "0.56"
local DATA_PATH = modelLib.DATA_PATH
local PHASE = menuLib.PHASE
local LINE_TYPE = menuLib.LINE_TYPE
local MODEL = modelLib.MODEL
local AIRCRAFT_TYPE = modelLib.AIRCRAFT_TYPE
local WING_TYPE = modelLib.WING_TYPE
local TAIL_TYPE = modelLib.TAIL_TYPE
local CH_MODE_TYPE = modelLib.CH_MODE_TYPE
local PORT = modelLib.PORT
local MEMU_VAR = modelLib.MEMU_VAR
local MENU_DATA = modelLib.MENU_DATA
local SetupLib = {}
local lastGoodMenu=0
------------------- Model Setup Helper functions ----------------------
local currAircraftType = -1 -- Current AircraftType selected, and to detect change
local currTailType = -1 -- Current WingType selected, and to detect change
local currWingType = -1 -- Current TailType selected, and to detect change
local menuDataChanged = false -- Flag to notify if any data has changed
local function tailTypeCompatible(a,b)
local function normalize(tt)
if (tt==TAIL_TYPE.TRAILERON_A or tt==TAIL_TYPE.TRAILERON_B) then
return TAIL_TYPE.TRAILERON_A
elseif (tt==TAIL_TYPE.TRAILERON_A_R2 or tt==TAIL_TYPE.TRAILERON_B_R2) then
return TAIL_TYPE.TRAILERON_A_R2
elseif (tt==TAIL_TYPE.VTAIL_A or tt==TAIL_TYPE.VTAIL_B) then
return TAIL_TYPE.VTAIL_A
else
return tt
end
end
return (normalize(a)==normalize(b))
end
-- Creates the menus to Render with the GUI
local function ST_LoadMenu(menuId)
local ctx = menuLib.DSM_Context
local function portUse(p)
local out = ""
if p==MENU_DATA[MEMU_VAR.CH_THR] then out = "Thr"
elseif p == MENU_DATA[MEMU_VAR.CH_L_AIL] then
out=(MENU_DATA[MEMU_VAR.CH_R_AIL] and "Ail_L") or "Ail"
elseif p == MENU_DATA[MEMU_VAR.CH_R_AIL] then out="Ail_R"
elseif p == MENU_DATA[MEMU_VAR.CH_L_ELE] then
out=(MENU_DATA[MEMU_VAR.CH_R_ELE] and "Ele_L") or "Ele"
elseif p == MENU_DATA[MEMU_VAR.CH_R_ELE] then out="Ele_R"
elseif p == MENU_DATA[MEMU_VAR.CH_L_RUD] then
out=(MENU_DATA[MEMU_VAR.CH_R_RUD] and "Rud_L") or "Rud"
elseif p == MENU_DATA[MEMU_VAR.CH_R_RUD] then out="Rud_R"
elseif p == MENU_DATA[MEMU_VAR.CH_L_FLP] then
out=(MENU_DATA[MEMU_VAR.CH_R_FLP] and "Flp_L") or "Flp"
elseif p == MENU_DATA[MEMU_VAR.CH_R_FLP] then out="Flp_R"
end
return out
end
local function formatTXRevert(port)
local out = " " .. modelLib.channelType2String(MODEL.DSM_ChannelInfo[port][0], MODEL.DSM_ChannelInfo[port][1]);
return out
end
local function Header(p)
return MODEL.PORT_TEXT[p].." "..portUse(p)
end
menuLib.clearMenuLines()
if (menuId==0x1000) then -- MAIN MENU
ctx.Menu = { MenuId = 0x1000, Text = "Save-Exit ("..MODEL.modelName..")", PrevId = 0, NextId = 0, BackId = 0, TextId=0 }
if (true) then
ctx.MenuLines[4] = { Type = LINE_TYPE.MENU, Text="Save Changes", TextId = 0, ValId = 0x1005 }
ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, Text="Discard Changes", TextId = 0, ValId = 0x1006 }
ctx.SelLine = 4
end
lastGoodMenu = menuId
elseif (menuId==0x1001) then -- MODEL SETUP
local backId = 0xFFF9 -- No changes, just exit
local title = "Model Setup ("..MODEL.modelName..")"
if (menuDataChanged) then
backId = 0x1000 -- Go to Save menu
title = title.." *"
end
ctx.Menu = { MenuId = 0x1001, Text = title, PrevId = 0, NextId = 0, BackId = backId, TextId=0 }
ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, Text = "Aircraft Type Setup", ValId = 0x1010,TextId=0 }
ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, Text = "Wing & Tail Channels ", ValId = 0x1020, TextId=0 }
ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, Text = "Gyro Channel Reverse", ValId = 0x1030, TextId=0 }
ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, Text = "WARNING: Changing of Aircraft or Wing will", ValId = 0x1001, TextId=0 }
ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, Text = "delete previous Channel/Port assigments.", ValId = 0x1001, TextId=0 }
ctx.SelLine = 0
lastGoodMenu = menuId
elseif (menuId==0x1005) then
modelLib.printChannelSummary()
modelLib.ST_SaveFileData()
menuDataChanged = false
local msg1 = "Data saved to: "
local msg2 = " "..DATA_PATH.."/"..modelLib.hashName(MODEL.modelName)..".txt"
ctx.Menu = { MenuId = 0x1005, Text = "Config Saved", PrevId = 0, NextId = 0, BackId = 0, TextId=0 }
ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, Text=msg1, TextId = 0, ValId = 0x1005 }
ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, Text=msg2, TextId = 0, ValId = 0x1005 }
ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, Text="Complete", TextId = 0, ValId = 0xFFF9 }
ctx.SelLine = 6
lastGoodMenu = menuId
elseif (menuId==0x1006) then
modelLib.ST_LoadFileData()
menuDataChanged = false
currAircraftType = MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE]
currWingType = MENU_DATA[MEMU_VAR.WING_TYPE]
currTailType = MENU_DATA[MEMU_VAR.TAIL_TYPE]
local msg1 = "Data restored from: "
local msg2 = " "..DATA_PATH.."/"..modelLib.hashName(MODEL.modelName)..".txt"
ctx.Menu = { MenuId = 0x1006, Text = "Discart Changes", PrevId = 0, NextId = 0, BackId = 0, TextId=0 }
ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, Text=msg1, TextId = 0, ValId = 0x1006 }
ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, Text=msg2, TextId = 0, ValId = 0x1006 }
ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, Text="Complete", TextId = 0, ValId = 0xFFF9 }
ctx.SelLine = 6
lastGoodMenu = menuId
elseif (menuId==0x1010) then
modelLib.printChannelSummary()
ctx.Menu = { MenuId = 0x1010, Text = "Aircraft Type", PrevId = 0, NextId = 0x1011, BackId = 0x1001, TextId=0 }
ctx.MenuLines[5] = { Type = LINE_TYPE.LIST_MENU_NC, Text="Aircraft Type", TextId = 0, ValId = MEMU_VAR.AIRCRAFT_TYPE, Min=50, Max=53, Def=50, Val=MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE] }
ctx.SelLine = 5
lastGoodMenu = menuId
elseif (menuId==0x1011) then
ctx.Menu = { MenuId = 0x1011, Text = "Model Type:"..modelLib.aircraft_type_text[currAircraftType], PrevId = 0, NextId = 0x1020, BackId = 0x1010, TextId=0 }
ctx.MenuLines[5] = { Type = LINE_TYPE.LIST_MENU_NC, Text="Wing Type", TextId = 0, ValId = MEMU_VAR.WING_TYPE, Min=100, Max=107, Def=100, Val=MENU_DATA[MEMU_VAR.WING_TYPE] }
ctx.MenuLines[6] = { Type = LINE_TYPE.LIST_MENU_NC, Text="Tail Type", TextId = 0, ValId = MEMU_VAR.TAIL_TYPE, Min=200, Max=210, Def=200, Val=MENU_DATA[MEMU_VAR.TAIL_TYPE] }
ctx.SelLine = 5
lastGoodMenu = menuId
elseif (menuId==0x1020) then
------ WING SETUP -------
local thr = MENU_DATA[MEMU_VAR.CH_THR]
local leftAil = MENU_DATA[MEMU_VAR.CH_L_AIL]
local rightAil = MENU_DATA[MEMU_VAR.CH_R_AIL]
local leftFlap = MENU_DATA[MEMU_VAR.CH_L_FLP]
local rightFlap = MENU_DATA[MEMU_VAR.CH_R_FLP]
local thrText = "Thr"
local leftAilText = "Left Aileron"
local rightAilText = "Right Aileron"
local leftFlapText = "Left Flap"
local rightFlapText = "Right Flap"
if (rightAil==nil) then leftAilText = "Aileron" end
if (rightFlap==nil) then leftFlapText = "Flap" end
local title = modelLib.aircraft_type_text[currAircraftType].." Wing:"..modelLib.wing_type_text[currWingType]
ctx.Menu = { MenuId = 0x1020, Text = title, PrevId = 0, NextId = 0x1021, BackId = 0x1011, TextId=0 }
ctx.MenuLines[0] = { Type = LINE_TYPE.LIST_MENU_NC, Text=thrText, TextId = 0, ValId = MEMU_VAR.CH_THR, Min=0, Max=10, Def=0, Val= thr }
ctx.MenuLines[2] = { Type = LINE_TYPE.LIST_MENU_NC, Text=leftAilText, TextId = 0, ValId = MEMU_VAR.CH_L_AIL, Min=0, Max=9, Def=0, Val= leftAil }
if (rightAil~=nil) then
ctx.MenuLines[3] = { Type = LINE_TYPE.LIST_MENU_NC, Text=rightAilText, TextId = 0, ValId = MEMU_VAR.CH_R_AIL, Min=0, Max=9, Def=0, Val= rightAil }
end
if (leftFlap~=nil) then
ctx.MenuLines[4] = { Type = LINE_TYPE.LIST_MENU_NC, Text=leftFlapText, TextId = 0, ValId = MEMU_VAR.CH_L_FLP, Min=0, Max=9, Def=0, Val= leftFlap }
end
if (rightFlap~=nil) then
ctx.MenuLines[5] = { Type = LINE_TYPE.LIST_MENU_NC, Text=rightFlapText, TextId = 0, ValId = MEMU_VAR.CH_R_FLP, Min=0, Max=9, Def=0, Val= rightFlap }
end
ctx.SelLine = 0
lastGoodMenu = menuId
elseif (menuId==0x1021) then
------ TAIL SETUP -------
local leftRud = MENU_DATA[MEMU_VAR.CH_L_RUD]
local rightRud = MENU_DATA[MEMU_VAR.CH_R_RUD]
local leftEle = MENU_DATA[MEMU_VAR.CH_L_ELE]
local rightEle = MENU_DATA[MEMU_VAR.CH_R_ELE]
local leftRudText = "Left Rudder"
local rightRudText = "Right Rudder"
local leftElvText = "Left Elevator"
local rightElvText = "Right Elevator"
if (rightRud==nil) then leftRudText = "Rudder" end
if (rightEle==nil) then leftElvText = "Elevator" end
local title = modelLib.aircraft_type_text[currAircraftType].." Tail:"..modelLib.tail_type_text[currTailType]
ctx.Menu = { MenuId = 0x1021, Text = title, PrevId = 0, NextId = 0x1001, BackId = 0x1020, TextId=0 }
if (leftRud~=nil) then
ctx.MenuLines[1] = { Type = LINE_TYPE.LIST_MENU_NC, Text=leftRudText, TextId = 0, ValId = MEMU_VAR.CH_L_RUD, Min=0, Max=9, Def=0, Val= leftRud}
end
if (rightRud~=nil) then
ctx.MenuLines[2] = { Type = LINE_TYPE.LIST_MENU_NC, Text=rightRudText, TextId = 0, ValId = MEMU_VAR.CH_R_RUD, Min=0, Max=9, Def=0, Val=rightRud }
end
if (leftEle~=nil) then
ctx.MenuLines[4] = { Type = LINE_TYPE.LIST_MENU_NC, Text=leftElvText, TextId = 0, ValId = MEMU_VAR.CH_L_ELE, Min=0, Max=9, Def=0, Val=leftEle }
end
if (rightEle~=nil) then
ctx.MenuLines[5] = { Type = LINE_TYPE.LIST_MENU_NC, Text=rightElvText, TextId = 0, ValId = MEMU_VAR.CH_R_ELE, Min=0, Max=9, Def=0, Val=rightEle }
end
ctx.SelLine = 1
lastGoodMenu = menuId
elseif (menuId==0x1030) then
modelLib.CreateDSMPortChannelInfo()
modelLib.printChannelSummary()
ctx.Menu = { MenuId = 0x1030, Text = "Gyro Channel Reverse (Port 1-5)", PrevId = 0, NextId = 0x1031, BackId = 0x1001, TextId=0 }
ctx.MenuLines[0] = { Type = LINE_TYPE.LIST_MENU_NC, Text=Header(PORT.PORT1), TextId = 0, ValId = MEMU_VAR.PORT1_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT1_MODE], Format = formatTXRevert(PORT.PORT1) }
ctx.MenuLines[1] = { Type = LINE_TYPE.LIST_MENU_NC, Text=Header(PORT.PORT2), TextId = 0, ValId = MEMU_VAR.PORT2_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT2_MODE], Format = formatTXRevert(PORT.PORT2) }
ctx.MenuLines[2] = { Type = LINE_TYPE.LIST_MENU_NC, Text=Header(PORT.PORT3), TextId = 0, ValId = MEMU_VAR.PORT3_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT3_MODE], Format = formatTXRevert(PORT.PORT3) }
ctx.MenuLines[3] = { Type = LINE_TYPE.LIST_MENU_NC, Text=Header(PORT.PORT4), TextId = 0, ValId = MEMU_VAR.PORT4_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT4_MODE], Format = formatTXRevert(PORT.PORT4) }
ctx.MenuLines[4] = { Type = LINE_TYPE.LIST_MENU_NC, Text=Header(PORT.PORT5), TextId = 0, ValId = MEMU_VAR.PORT5_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT5_MODE], Format = formatTXRevert(PORT.PORT5) }
ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, Text="Only Thr/Ail/Rud/Ele. This affects AS3X/SAFE reaction dir./b", TextId = 0, ValId = 0x1030 }
ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, Text="Any changes, use RX 'Relearn Servo Settings'/b", TextId = 0, ValId = 0x1030 }
ctx.SelLine = 0
lastGoodMenu = menuId
elseif (menuId==0x1031) then
modelLib.CreateDSMPortChannelInfo()
modelLib.printChannelSummary()
ctx.Menu = { MenuId = 0x1031, Text = "Gyro Channel Reverse (Port 6-10)", PrevId = 0x1030, NextId = 0, BackId = 0x1001, TextId=0 }
ctx.MenuLines[0] = { Type = LINE_TYPE.LIST_MENU_NC, Text=Header(PORT.PORT6), TextId = 0, ValId = MEMU_VAR.PORT6_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT6_MODE], Format = formatTXRevert(PORT.PORT6) }
ctx.MenuLines[1] = { Type = LINE_TYPE.LIST_MENU_NC, Text=Header(PORT.PORT7), TextId = 0, ValId = MEMU_VAR.PORT7_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT7_MODE], Format = formatTXRevert(PORT.PORT7) }
ctx.MenuLines[2] = { Type = LINE_TYPE.LIST_MENU_NC, Text=Header(PORT.PORT8), TextId = 0, ValId = MEMU_VAR.PORT8_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT8_MODE], Format = formatTXRevert(PORT.PORT8) }
ctx.MenuLines[3] = { Type = LINE_TYPE.LIST_MENU_NC, Text=Header(PORT.PORT9), TextId = 0, ValId = MEMU_VAR.PORT9_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT9_MODE], Format = formatTXRevert(PORT.PORT9) }
ctx.MenuLines[4] = { Type = LINE_TYPE.LIST_MENU_NC, Text=Header(PORT.PORT10), TextId = 0, ValId = MEMU_VAR.PORT10_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT10_MODE], Format = formatTXRevert(PORT.PORT10) }
ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, Text="Only Thr/Ail/Rud/Ele. This affects AS3X/SAFE reaction dir./b", TextId = 0, ValId = 0x1031 }
ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, Text="Any changes, use RX 'Relearn Servo Settings'/b", TextId = 0, ValId = 0x1031 }
ctx.SelLine = 0
lastGoodMenu = menuId
else
print("NOT IMPLEMENTED")
ctx.Menu = { MenuId = 0x0002, Text = "NOT IMPLEMENTED", TextId = 0, PrevId = 0, NextId = 0, BackId = lastGoodMenu }
ctx.SelLine = menuLib.BACK_BUTTON
end
menuLib.PostProcessMenu()
end
-- ST_SendReceive
-- Main state machine for the Setup menu
local function ST_SendReceive()
local ctx = menuLib.DSM_Context
--if (DEBUG_ON>1) then Log.LOG_write("%3.3f %s: ", menuLib.getElapsedTime(), menuLib.phase2String(ctx.Phase)) end
if ctx.Phase == PHASE.RX_VERSION then -- request RX version
ctx.RX.Name = "MODEL SETUP"
ctx.RX.Version = SETUP_LIB_VERSION
ctx.Phase = PHASE.MENU_TITLE
ctx.Menu.MenuId = 0x01001
ctx.Refresh_Display = true
elseif ctx.Phase == PHASE.WAIT_CMD then
elseif ctx.Phase == PHASE.MENU_TITLE then -- request menu title
ST_LoadMenu(ctx.Menu.MenuId)
ctx.Phase = PHASE.WAIT_CMD
ctx.Refresh_Display = true
elseif ctx.Phase == PHASE.VALUE_CHANGING then -- send value
local line = ctx.MenuLines[ctx.SelLine] -- Updated Value of SELECTED line
if (MENU_DATA[line.ValId] ~= line.Val ) then
MENU_DATA[line.ValId] = line.Val
print(string.format("MENU_DATA[%d/%s]=%d",line.ValId,line.Text, line.Val))
menuDataChanged=true
end
ctx.Phase = PHASE.VALUE_CHANGING_WAIT
elseif ctx.Phase == PHASE.VALUE_CHANGING_WAIT then
local line = ctx.MenuLines[ctx.SelLine]
elseif ctx.Phase == PHASE.VALUE_CHANGE_END then -- send value
local line = ctx.MenuLines[ctx.SelLine] -- Updated Value of SELECTED line
-- Update the menu data from the line
if (MENU_DATA[line.ValId] ~= line.Val ) then
MENU_DATA[line.ValId] = line.Val
print(string.format("MENU_DATA[%d/%s]=%d",line.ValId,line.Text, line.Val))
menuDataChanged=true
end
-- Did the aircraft type change?
if (currAircraftType ~= MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE]) then
currAircraftType = MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE]
modelLib.ST_AircraftInit(currAircraftType)
currWingType = MENU_DATA[MEMU_VAR.WING_TYPE]
currTailType = MENU_DATA[MEMU_VAR.TAIL_TYPE]
end
-- Did the Wing type change?
if (currWingType ~= MENU_DATA[MEMU_VAR.WING_TYPE]) then
if (currAircraftType==AIRCRAFT_TYPE.GLIDER) then
currWingType = MENU_DATA[MEMU_VAR.WING_TYPE]
modelLib.ST_GliderWingInit(currWingType)
else
currWingType = MENU_DATA[MEMU_VAR.WING_TYPE]
modelLib.ST_PlaneWingInit(currWingType)
end
-- DELTA has only RUDER
if ((currWingType==WING_TYPE.ELEVON_A or currWingType==WING_TYPE.ELEVON_B) and TAIL_TYPE~=TAIL_TYPE.RUD_1) then
MENU_DATA[MEMU_VAR.TAIL_TYPE] = TAIL_TYPE.RUD_1
end
end
--- Did the tail changed?
local ntt = MENU_DATA[MEMU_VAR.TAIL_TYPE]
if (currTailType ~= ntt) then
if (currAircraftType==AIRCRAFT_TYPE.GLIDER) then
currTailType = ntt
modelLib.ST_GliderTailInit(currTailType)
else
if (not tailTypeCompatible(currTailType,ntt)) then
modelLib.ST_PlaneTailInit(ntt)
end
currTailType = ntt
end
end
ctx.Phase = PHASE.WAIT_CMD
elseif ctx.Phase == PHASE.EXIT then
ctx.Phase=PHASE.EXIT_DONE
end
end
------------------------------------------------------------------------------------------------------------
-- Inital List and Image Text for this menus
local function ST_Init_Text(rxId)
menuLib.clearAllText()
local List_Values = menuLib.List_Values
local List_Text = menuLib.List_Text
local Text = menuLib.Text
local List_Text_Img = menuLib.List_Text_Img
-- Channel Names use the Port Text Retrived from OTX/ETX
for i = 0, 9 do List_Text[i] = MODEL.PORT_TEXT[i] end
List_Text[10]="--"
-- Aircraft Type
List_Text[50+AIRCRAFT_TYPE.PLANE] = "Airplane"; --List_Text_Img[50+AIRCRAFT_TYPE.PLANE] = "at_plane.png|Airplane"
List_Text[50+AIRCRAFT_TYPE.GLIDER] = "Glider (Partial work)"; --List_Text_Img[50+AIRCRAFT_TYPE.GLIDER] = "at_glider.png|Glider"
List_Text[50+AIRCRAFT_TYPE.HELI] = "Helicopter (Not done)"; --List_Text_Img[50+AIRCRAFT_TYPE.HELI] = "at_heli.png|Helicopter"
List_Text[50+AIRCRAFT_TYPE.DRONE] = "Drone (not done)"; --List_Text_Img[50+AIRCRAFT_TYPE.DRONE] = "at_drone.png|Drone"
-- Wing Types
List_Text[100+WING_TYPE.AIL_1] = "Single Ail"; List_Text_Img[100+WING_TYPE.AIL_1] = "wt_1ail.png|Single Aileron"
List_Text[100+WING_TYPE.AIL_2] = "Dual Ail"; List_Text_Img[100+WING_TYPE.AIL_2] = "wt_2ail.png|Dual Aileron"
List_Text[100+WING_TYPE.FLAPERON] = "Flaperon"; List_Text_Img[100+WING_TYPE.FLAPERON] = "wt_flaperon.png|Flaperon"
List_Text[100+WING_TYPE.AIL_1_FLP_1] = "Ail + Flap"; List_Text_Img[100+WING_TYPE.AIL_1_FLP_1] = "wt_1ail_1flp.png|Aileron + Flap"
List_Text[100+WING_TYPE.AIL_2_FLP_1] = "Dual Ail + Flap"; List_Text_Img[100+WING_TYPE.AIL_2_FLP_1] = "wt_2ail_1flp.png|Dual Aileron + Flap"
List_Text[100+WING_TYPE.AIL_2_FLP_2] = "Dual Ail + Dual Flap"; List_Text_Img[100+WING_TYPE.AIL_2_FLP_2] = "wt_2ail_2flp.png|Dual Aileron + Dual Flap"
List_Text[100+WING_TYPE.ELEVON_A] = "Delta/Elevon A"; List_Text_Img[100+WING_TYPE.ELEVON_A] = "wt_elevon.png|Delta/Elevon A"
List_Text[100+WING_TYPE.ELEVON_B] = "Delta/Elevon B"; List_Text_Img[100+WING_TYPE.ELEVON_B] = "wt_elevon.png|Delta/Elevon B"
-- Tail Types
List_Text[200+TAIL_TYPE.RUD_1] = "Rudder Only"; List_Text_Img[200+TAIL_TYPE.RUD_1] = "tt_1rud.png|Rudder Only"
List_Text[200+TAIL_TYPE.RUD_1_ELEV_1] = "Rud + Ele"; List_Text_Img[200+TAIL_TYPE.RUD_1_ELEV_1] = "tt_1rud_1ele.png|Tail Normal"
List_Text[200+TAIL_TYPE.RUD_1_ELEV_2] = "Rud + Dual Ele"; List_Text_Img[200+TAIL_TYPE.RUD_1_ELEV_2] = "tt_1rud_2ele.png|Rud + Dual Elev"
List_Text[200+TAIL_TYPE.RUD_2_ELEV_1] = "Dual Rud + Ele"; List_Text_Img[200+TAIL_TYPE.RUD_2_ELEV_1] = "tt_2rud_1ele.png|Dual Rud + Elev"
List_Text[200+TAIL_TYPE.RUD_2_ELEV_2] = "Dual Rud + Dual Ele"; List_Text_Img[200+TAIL_TYPE.RUD_2_ELEV_2] = "tt_2rud_2ele.png|Dual Rud + Dual Elev"
List_Text[200+TAIL_TYPE.VTAIL_A] = "V-Tail A"; List_Text_Img[200+TAIL_TYPE.VTAIL_A] = "tt_vtail.png|V-Tail A"
List_Text[200+TAIL_TYPE.VTAIL_B] = "V-Tail B"; List_Text_Img[200+TAIL_TYPE.VTAIL_B] = "tt_vtail.png|V-Tail B"
List_Text[200+TAIL_TYPE.TRAILERON_A] = "Taileron A"; List_Text_Img[200+TAIL_TYPE.TRAILERON_A] = "tt_taileron.png|Taileron A"
List_Text[200+TAIL_TYPE.TRAILERON_B] = "Taileron B"; List_Text_Img[200+TAIL_TYPE.TRAILERON_B] = "tt_taileron.png|Taileron B"
List_Text[200+TAIL_TYPE.TRAILERON_A_R2] = "Taileron A + 2x Rud"; List_Text_Img[200+TAIL_TYPE.TRAILERON_A_R2] = "tt_taileron2.png|Taileron A + Dual Rud"
List_Text[200+TAIL_TYPE.TRAILERON_B_R2] = "Taileron B + 2x Rud"; List_Text_Img[200+TAIL_TYPE.TRAILERON_B_R2] = "tt_taileron2.png|Taileron B + Dual Rud"
-- Servo Reverse
if (LCD_W > 128) then
List_Text[300+CH_MODE_TYPE.NORMAL] = "Normal "
List_Text[300+CH_MODE_TYPE.REVERSE] = "Reverse"
else
List_Text[300+CH_MODE_TYPE.NORMAL] = "Nor"
List_Text[300+CH_MODE_TYPE.REVERSE] = "Rev"
end
end
-- Initial Setup
local function ST_Init()
-- Initialize text (use RX_ID 0)
ST_Init_Text(0)
-- Setup default Data, and load a file if exist
--modelLib.ST_Default_Data()
if (modelLib.ST_LoadFileData()==0) then -- Did not load a file
modelLib.ST_Default_Data()
modelLib.ST_SaveFileData() -- Save Defaults
end
menuDataChanged = false
currAircraftType = MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE]
currWingType = MENU_DATA[MEMU_VAR.WING_TYPE]
currTailType = MENU_DATA[MEMU_VAR.TAIL_TYPE]
local ctx = menuLib.DSM_Context
ctx.Phase = PHASE.RX_VERSION
end
local function ST_Done()
local ctx = menuLib.DSM_Context
ctx.Phase = PHASE.EXIT_DONE
end
return { init=ST_Init, run=ST_SendReceive, done=ST_Done }

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,10 @@
-- OVERRIDES Messges for MIN 128x64 screns
-- FORMAT <LineType>|<Msg#>|<Text>
-- Line Type: Text for Menus (T), List_Text Options (LT), List_Text_Image (LI), Flight Mode (FM), RX Name (RX)
-- IMPORTANT: NO EMPTY LINES
--
T |0x0097|DONT USE: Factory Reset
T |0x0098|DONT USE: Factory Reset
T |0x00A5|DONT USE: First Time Setup
T |0x0190|DONT USE: Relearn Servo Settings
T |0x020D|DONT USE: First Time SAFE Setup

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 866 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 880 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 863 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 803 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 755 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 817 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

View file

@ -0,0 +1,437 @@
-- FORMAT <LineType>|<Msg#>|<Text>
-- Line Type: Text for Menus (T), List_Text Options (LT), List_Text_Image (LI), Flight Mode (FM), RX Name (RX)
-- NO EMPTY LINES
-- Formmatting at end of line: /c=Center, /r=Right, /b=Bold, /m=menu
LT|0x0001|Off
LT|0x0002|On
-- Ihn/Act List Options
LT|0x0003|Inh
LT|0x0004|Act
--
-- Channel selection for SAFE MODE and GAINS on FC6250HX
LT|0x000C|Inh
LT|0x000D|Ch5
LT|0x000E|Ch6
LT|0x000F|Ch7
LT|0x0010|Ch8
LT|0x0011|Ch9
LT|0x0012|Ch10
LT|0x0013|Ch11
LT|0x0014|Ch12
--
-- Servo Output values
LT|0x002D|5.5ms
LT|0x002E|11ms
LT|0x002F|22ms
--
-- Gain Multiplier Values
LT|0x0032|1 X
LT|0x0033|2 X
LT|0x0034|4 X
--
LT|0x0035|Inh
LT|0x0036|Thr
LT|0x0037|Ail
LT|0x0038|Ele
LT|0x0039|Rud
LT|0x003A|Ch5
LT|0x003B|Ch6
LT|0x003C|Ch7
LT|0x003D|Ch8
LT|0x003E|Ch9
LT|0x003F|Ch10
LT|0x0040|Ch11
LT|0x0041|Ch12
LT|0x0042|Ch13
LT|0x0043|Ch14
LT|0x0044|Ch15
LT|0x0045|Ch16
LT|0x0046|Ch17
LT|0x0047|Ch18
LT|0x0048|Ch19
LT|0x0049|Ch20
--
T |0x0040|Roll
T |0x0041|Pitch
T |0x0042|Yaw
T |0x0043|Gain/c/b
T |0x0045|Differential
T |0x0046|Priority
T |0x0049|Output Setup
T |0x004A|Failsafe
T |0x004B|Main Menu
T |0x004E|Position
--
T |0x0050|Outputs
T |0x0051|Output Channel 1
T |0x0052|Output Channel 2
T |0x0053|Output Channel 3
T |0x0054|Output Channel 4
T |0x0055|Output Channel 5
T |0x0056|Output Channel 6
T |0x0057|Output Channel 7
T |0x0058|Output Channel 8
T |0x0059|Output Channel 9
T |0x005A|Output Channel 10
--
-- FailSafe Options
--LT|0x005E|Inh
LT|0x005F|Hold Last
LT|0x0060|Preset
--LT|0x0061|Custom
--
T |0x0071|Proportional
T |0x0072|Integral
T |0x0073|Derivate
T |0x0078|FM Channel
T |0x007F|Attitude Gain
--
T |0x0080|Orientation
T |0x0082|Heading
T |0x0085|Frame Rate
T |0x0086|System Setup
T |0x0087|F-Mode Setup
T |0x0088|Enabled F-Modes
T |0x0089|Gain Channel
T |0x008A|Gain Sensitivity/r -- Right Align
T |0x008B|Panic
T |0x008E|Panic Delay
--
LT|0x0187|No Freq --???? unset Freq
LT|0x0088|70hz
LT|0x0089|90hz
LT|0x008A|200hz
LT|0x008B|333hz
LT|0x008C|490hz
LT|0x008D|560hz
LT|0x008E|Normal
LT|0x008F|Reversed
-- FC6250HX: Callibration
T |0x0090|Apply
T |0x0091|Begin
T |0x0092|Start
T |0x0093|Complete
T |0x0094|Done
--
-- FC6250HX: Swashplate Type
--
LT|0x0090|Normal
LI|0x0090|h_swp_norm.png|Normal
LT|0x0091|3 Servos 120 Y
LI|0x0091|h_swp_3_120.png|3 Servos 120 Y
LT|0x0092|3 Servos 120 Y-Inv
LI|0x0092|h_swp_3_120inv.png|3 Servos 120 Y-Inv
LT|0x0093|3 Servos 135 Y
LI|0x0093|h_swp_3_135.png|3 Servos 135 Y
LT|0x0094|3 Servos 135 Y-Inv
LI|0x0094|h_swp_3_135inv.png|3 Servos 135 Y-Inv
LT|0x0095|3 Servos 140 Y
LI|0x0095|h_swp_3_140.png|3 Servos 140 Y
LT|0x0096|3 Servos 140 Y-Inv
LI|0x0096|h_swp_3_140inv.png|3 Servos 140 Y-Inv
LT|0x0097|3 Servos 90 T
LI|0x0097|h_swp_3_90.png|3 Servos 90 T
LT|0x0098|3 Servos 90 T-Inv
LI|0x0098|h_swp_3_90inv.png|3 Servos 90 T-Inv
--
T |0x0097|Factory Reset
T |0x0098|Factory Reset
T |0x0099|Advanced Setup
T |0x009A|Capture Failsafe Positions
T |0x009C|Custom Failsafe
--
T |0x009F|Save Settings -- Save & Reboot RX
--
T |0x00A5|First Time Setup
T |0x00AA|Capture Gyro Gains
T |0x00AD|Gain Channel Select
T |0x00AF|Dynamic
LT|0x00B0|Self-Level
LT|0x00B1|Envelope
--
-- Flight Modes List Options
LT|0x00B5|Inh
LT|0x00B6|FM1
LT|0x00B7|FM2
LT|0x00B8|FM3
LT|0x00B9|FM4
LT|0x00BA|FM5
LT|0x00BB|FM6
LT|0x00BC|FM7
LT|0x00BD|FM8
LT|0x00BE|FM9
LT|0x00BF|FM10
--
T |0x00BE|Unknown_BE -- Used in Reset menu (0x0001) while the RX is rebooting
--
T |0x00C7|Calibrate Sensor
T |0x00C8|Sensor is Calibrating.. Wait
T |0x00CA|SAFE & Panic Mode Setup
--
T |0x00CD|Level model & capt attitude/m -- SPECIAL MENU to itself who is not a comment
T |0x00CE|Error TX Conf
T |0x00CF|Invalid TX Ch Conf 1
T |0x00D0|Invalid TX Ch Conf 2
--
-- RX Orientations for AR631/AR637, Optionally attach an Image + Alt Text to display
LT|0x00CB|Pos 1
LI|0x00CB|rx_pos_1.png|Pilot View: RX Label Up, Pins Back
LT|0x00CC|Pos 2
LI|0x00CC|rx_pos_2.png|Pilot View: RX Label Left, Pins Back
LT|0x00CD|Pos 3
LI|0x00CD|rx_pos_3.png|Pilot View: RX Label Down, Pins Back
LT|0x00CE|Pos 4
LI|0x00CE|rx_pos_4.png|Pilot View: RX Label Right, Pins Back
LT|0x00CF|Pos 5
LI|0x00CF|rx_pos_5.png|Pilot View: RX Label UP, Pins to Front
LT|0x00D0|Pos 6
LI|0x00D0|rx_pos_6.png|Pilot View: RX Label Left, Pins Front
LT|0x00D1|Pos 7
LI|0x00D1|rx_pos_7.png|Pilot View: RX Label Down, Pins Front
LT|0x00D2|Pos 8
LI|0x00D2|rx_pos_8.png|Pilot View: RX Label Right, Pins Front
LT|0x00D3|Pos 9
LI|0x00D3|rx_pos_9.png|Pilot View: RX Label Up, Pins Left
LT|0x00D4|Pos 10
LI|0x00D4|rx_pos_10.png|Pilot View: RX Label Back, Pins Left
LT|0x00D5|Pos 11
LI|0x00D5|rx_pos_11.png|Pilot View: RX Label Down, Pins Left
LT|0x00D6|Pos 12
LI|0x00D6|rx_pos_12.png|Pilot View: RX Label Front, Pins Left
LT|0x00D7|Pos 13
LI|0x00D7|rx_pos_13.png|Pilot View: RX Label Up, Pins Right
LT|0x00D8|Pos 14
LI|0x00D8|rx_pos_14.png|Pilot View: RX Label Back, Pins Right
LT|0x00D9|Pos 15
LI|0x00D9|rx_pos_15.png|Pilot View: RX Label Down, Pins Right
LT|0x00DA|Pos 16
LI|0x00DA|rx_pos_16.png|Pilot View: RX Label Front, Pins Right
LT|0x00DB|Pos 17
LI|0x00DB|rx_pos_17.png|Pilot View: RX Label Back, Pins Down
LT|0x00DC|Pos 18
LI|0x00DC|rx_pos_18.png|Pilot View: RX Label Left, Pins Down
LT|0x00DD|Pos 19
LI|0x00DD|rx_pos_19.png|Pilot View: RX Label Front, Pins Down
LT|0x00DE|Pos 20
LI|0x00DE|rx_pos_20.png|Pilot View: RX Label Right, Pins Down
LT|0x00DF|Pos 21
LI|0x00DF|rx_pos_21.png|Pilot View: RX Label Back, Pins Up
LT|0x00E0|Pos 22
LI|0x00E0|rx_pos_22.png|Pilot View: RX Label Left, Pins Up
LT|0x00E1|Pos 23
LI|0x00E1|rx_pos_23.png|Pilot View: RX Label Front, Pins Up
LT|0x00E2|Pos 24
LI|0x00E2|rx_pos_24.png|Pilot View: RX Label Right, Pins Up
LT|0x00E3|Pos Invalid
LI|0x00E3|rx_pos_25.png|Cannot detect orientation of RX
--
-- RX Orientations images for FC5250 (HACK add 0x100 internally to differenciate for helis)
LI|0x01CB|h_rx_pos_1.png|Pilot View: RX Label Up, Pins Front
LI|0x01CC|h_rx_pos_2.png|Pilot View: RX Label Left, Pins Front
LI|0x01CD|h_rx_pos_3.png|Pilot View: RX Label Down, Pins Front
LI|0x01CE|h_rx_pos_4.png|Pilot View: RX Label Right, Pins Front
LI|0x01CF|h_rx_pos_5.png|Pilot View: RX Label UP, Pins to Back
LI|0x01D0|h_rx_pos_6.png|Pilot View: RX Label Left, Pins Back
LI|0x01D1|h_rx_pos_7.png|Pilot View: RX Label Down, Pins Back
LI|0x01D2|h_rx_pos_8.png|Pilot View: RX Label Right, Pins Back
--
T |0x00D1|Receiver will Reboot/b
T |0x00D2|Panic Channel
T |0x00D3|Swashplate
T |0x00D5|Agility
T |0x00D8|Stop
T |0x00DA|SAFE/c/b -- Center + Bold
T |0x00DB|Stability
T |0x00DC|@ per sec
T |0x00DD|Tail rotor
T |0x00DE|Setup
T |0x00DF|AFR
T |0x00E0|Collective
T |0x00E1|Subtrim
T |0x00E2|Phasing
T |0x00E3|Pre-Comp
T |0x00E4|E-Ring
T |0x00E5|Swash Type
T |0x00E6|Travel
T |0x00E7|Left
T |0x00E8|Right
T |0x00EA|Low Throttle
--
T |0x00F2|Governor
T |0x00F4|Soft Start
--
LT|0x00F2|Fixed
LT|0x00F3|Adjustable
LT|0x00F4|Inh
LT|0x00F5|Nitro
--
T |0x00F6|Direction
T |0x00F8|Settings -- ?? validate on a Spektrum radio
T |0x00F9|Gyro settings
T |0x00FE|Stick Priority/c/b
--
T |0x0100|Make sure the model has been
T |0x0101|configured, including wing type,
T |0x0102|reversing, travel, trimmed, etc.
T |0x0103|before continuing setup.
T |0x0104|
T |0x0105| -- Blank
--
T |0x0106|Any wing type, channel assignment,
T |0x0107|subtrim, or servo reversing changes
T |0x0108|require running through initial
T |0x0109|setup again.
T |0x010A|
T |0x010B|
--
T |0x0190|Relearn Servo Settings
T |0x019C|Enter Receiver Bind Mode
T |0x01AA|Offset
T |0x01D7|SAFE Select Channel
T |0x01DC|AS3X/c/b -- Center + Bold
T |0x01DD|AS3X Settings
T |0x01DE|AS3X Gains
T |0x01E0|Rate Gains/c/b
T |0x01E2|SAFE Settings
T |0x01E3|SAFE Gains
T |0x01E6|Attitude Trim/c/b
T |0x01E7|Envelope
T |0x01E9|Roll Right
T |0x01EA|Roll Left
T |0x01EB|Pitch Down
T |0x01EC|Pitch Up
T |0x01EE|Thr to Pitch
T |0x01EF|Low Thr to Pitch/c/b
T |0x01F0|High Thr to Pitch/c/b
T |0x01F1|Filter
T |0x01F3|Threshold
T |0x01F4|Angle
T |0x01F6|Failsafe Angles/c/b
T |0x01F8|Safe Mode
T |0x01F9|SAFE Select
T |0x01FC|Panic F-Mode
T |0x01FD|FailSafe Flight Mode -- Safe Flight Mode
T |0x0201|Throttle
T |0x0204|Hover
T |0x0208|Decay
T |0x0209|Save to Backup
T |0x020A|Restore from Backup
T |0x020D|First Time SAFE Setup
--
-- First time safe setup Page 3 :
T |0x020E|AS3X gains must be tuned
T |0x020F|and active in SAFE Flight Modes
T |0x0210|to help reduce wobble.
T |0x0211|
T |0x0212|
T |0x0213| -- Blank
--
-- AS3X orientation Setting menu (Level)
T |0x021A|Set the model level,
T |0x021B|and press Continue.
T |0x021C|
T |0x021D| -- Blank
--
-- AS3X orientation Setting menu (Nose down)
T |0x021F|Set the model on its nose,
T |0x0220|and press Continue. If the
T |0x0221|orientation on the next
T |0x0222|screen is wrong go back
T |0x0223|and try again.
T |0x0224|Continue
--
T |0x0226|Angle Limits/c/b
T |0x0227|Other settings
T |0x0229|Set Orientation Manually
--
-- Factory Default Warning
T |0x022B|WARNING!
T |0x022C|This will reset the
T |0x022D|configuration to factory
T |0x022E|defaults. This does not
T |0x022F|affect the backup config.
T |0x0230| -- Blank
--
-- Backup Warning
T |0x0231|This will overwrite the
T |0x0232|backup memory with your
T |0x0233|current configuartion.
T |0x0234|
T |0x0235| -- Blank
--
-- Restore from Backup Warning
T |0x0236|This will overwrite the
T |0x0237|current config with
T |0x0238|that which is in
T |0x0239|the backup memory.
T |0x023A| -- Blank
--
-- Utilities Copy flight modes
T |0x023D|Copy F-Mode Settings
T |0x023E|Source F-Mode
T |0x023F|Target F-Mode
--
T |0x0240|Utilities
--
-- Gain Capture Page
T |0x024C|Gains will be captured on
T |0x024D|Captured gains will be
T |0x024E|Gains on
T |0x024F|were captured and changed
T |0x0250|from Adjustable to Fixed
--
-- Utilities, Copy flight mode (Copy Confirmation, oveerriding FM)
T |0x0251|Are you sure you want to ovewrite the "Target"
T |0x0252|with the "Source" ?
T |0x0253| -- Blank
--
T |0x0254|Pos = Up, Neg = Down
--
-- First time safe setup Page 1 (maybe ask to select Flight Mode cannel)
T |0x0255|Before setting up SAFE
T |0x0256|a Flight Mode channel
T |0x0257|most be configured.
--
-- First time safe setup Page 2 (something related for flight mode)
T |0x025A|Select the desired flight mode
T |0x025B|switch position to adjust settings
T |0x025C|for each flight mode
T |0x025D|
T |0x025E| -- Blank
--
-- Utilities, Copy flight mode (Confirm)
T |0x0259|YES
T |0x0260|WARNING: "Target"
T |0x0261|F-Mode will be overwritten
T |0x0262|by "Source"
--
T |0x0263|Fixed/Adjustable Gains/c/b
T |0x0266|Heading Gain/c/b
T |0x0267|Pos = Nose Up/Roll Right
T |0x0268|Neg = Nose Down/Roll Left
T |0x0269|SAFE - Thr to Pitch
T |0x026A|Use CAUTION for Yaw gain!/b
--
T |0x026B|Head Speed
T |0x026C|Pinion
T |0x026D|Main Gear
T |0x026F|RPM Sensor
T |0x0272|Show Advanced Menus
--
T |0x0300|No compatible DSM RX...
T |0x0301|Waiting for RX to Restart
--
FM|0x8000|Flight Mode/c/b
--
RX|0x0001|AR636
RX|0x0014|SPM4651T
RX|0x0015|AR637T
RX|0x0016|AR637TA
RX|0x0018|FC6250HX
RX|0x0019|AR630
RX|0x001A|AR8360T
RX|0x001B|AR8020T
RX|0x001C|AR10360T
RX|0x001E|AR631