Frankie Arzu 62aa58d32d
Forward Prog v0.56 (#919)
1. NEW Black&White Radios new model setup. Now it can setup completely a plane from zero
2. Color version; simplified code for Tail mixes, and fix Taileron setup
2023-12-17 11:12:14 +01:00

1089 lines
32 KiB
Lua

local toolName = "TNS|DSM Frwd Prog v0.56 (MIN)|TNE"
---- #########################################################################
---- # #
---- # 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. #
---- # #
---- #########################################################################
local VERSION = "v0.56"
local LANGUAGE = "en"
local DSMLIB_PATH = "/SCRIPTS/TOOLS/DSMLIB/"
local LOG_FILE = "/LOGS/dsm_min_log.txt"
local MSG_FILE = DSMLIB_PATH.."msg_fwdp_" .. LANGUAGE .. ".txt"
local MSG_FILE_MIN = DSMLIB_PATH.."MIN_msg_fwdp_" .. LANGUAGE .. ".txt"
local MSG_MIN_FILE_OFFSET = 20000
-- Phase
local PH_INIT = 0
local PH_RX_VER, PH_TITLE, PH_TX_INFO, PH_LINES, PH_VALUES = 1, 2, 3, 4, 5
local PH_VAL_CHANGING, PH_VAL_EDITING, PH_VAL_EDIT_END = 6, 7, 8
local PH_WAIT_CMD, PH_EXIT_REQ, PH_EXIT_DONE = 9, 10, 11
-- Line Types
local LT_MENU = 0x1C
local LT_LIST_NC, LT_LIST_NC2, LT_LIST, LT_LIST_ORI, LT_LIST_TOG = 0x6C, 0x6D, 0x0C, 0xCC, 0x4C
local LT_VALUE_NC = 0x60
local LT_VALUE_PERCENT, LT_VALUE_DEGREES = 0xC0, 0xE0
local Phase = PH_INIT
local SendDataToRX = 1 -- Initiate Sending Data
local Text = {}
local List_Text = {}
local List_Text_Img = {}
local Flight_Mode = "Flight Mode"
local RxName = {}
local TXInactivityTime = 0
local RXInactivityTime = 0
local TX_Info_Step = 0
local TX_Info_Type = 0
local Change_Step = 0
local originalValue = 0
local TX_MAX_CH = 12 - 6 -- Number of Channels after Ch6
--local ctx = {
local ctx_SelLine = 0 -- Current Selected Line
local ctx_EditLine = nil -- Current Editing Line
local ctx_CurLine = -1 -- Current Line Requested/Parsed via h message protocol
local ctx_isReset = false -- false when starting from scracts, true when starting from Reset
--}
local MODEL = {
modelName = "", -- The name of the model comming from OTX/ETX
modelOutputChannel = {}, -- Output information from OTX/ETX
TX_CH_TEXT= { },
PORT_TEXT = { },
DSM_ChannelInfo = {} -- Data Created by DSM Configuration Script
}
local Menu = { MenuId = 0, Text = "", TextId = 0, PrevId = 0, NextId = 0, BackId = 0 }
local MenuLines = {}
local RX_Name = ""
local RX_Version = ""
local logFile = nil
--local LCD_X_LINE_TITLE = 0
--local LCD_X_LINE_VALUE = 75
local LCD_W_BUTTONS = 19
local LCD_H_BUTTONS = 10
local LCD_X_MAX = 128
local LCD_X_RIGHT_BUTTONS = LCD_X_MAX - LCD_W_BUTTONS - 1
local LCD_Y_LINE_HEIGHT = 7
local LCD_Y_LOWER_BUTTONS = (8 * LCD_Y_LINE_HEIGHT) + 2
local TEXT_ATTR = SMLSIZE
local function gc()
collectgarbage("collect")
end
--[[
local function gcTable(t)
if type(t)=="table" then
for i,v in pairs(t) do
if type(v) == "table" then
gcTable(v)
end
t[i] = nil
end
end
gc()
return t
end
--]]
local function LOG_open()
logFile = io.open(LOG_FILE, "w") -- Truncate Log File
end
local function LOG_write(...)
if (logFile == nil) then LOG_open() end
local str = string.format(...)
io.write(logFile, str)
end
local function LOG_close()
if (logFile ~= nil) then io.close(logFile) end
end
----- Line Type
local function isIncrementalValueUpdate(line)
if (line.Type == LT_LIST_NC or line.Type == LT_LIST_NC2 or
line.Type == LT_VALUE_NC or line.Type == LT_VALUE_DEGREES) then return false end
return true
end
local function isSelectable(line)
if (line.TextId == 0x00CD) then return true end -- Exceptiom: Level model and capture attitude
if (line.Type == LT_MENU and line.ValId == line.MenuId) then return false end -- Menu to same page
if (line.Type ~= LT_MENU and line.Max == 0) then return false end -- Read only data line
if (line.Type ~= 0 and line.TextId < 0x8000) then return true end -- Not Flight Mode
return false;
end
local function isListLine(line)
return line.Type==LT_LIST_NC or line.Type==LT_LIST_NC2 or
line.Type == LT_LIST or line.Type == LT_LIST_ORI or line.Type == LT_LIST_TOG
end
local function isEditing()
return ctx_EditLine ~= nil
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 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 f=MSG_FILE
if (pos >= MSG_MIN_FILE_OFFSET) then -- Depending on offset, use the main, or MIN version
f = MSG_FILE_MIN
pos = pos - MSG_MIN_FILE_OFFSET
end
-- open and read File
local dataFile = io.open(f, "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=="/") then pipe=6 -- far right end, like comment
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 -- Fpr
gc()
return type, index, rtrim(line), newPos
end
local function GetTextFromFile(pos)
if (pos==nil) then return nil end
local t,i,l, p = GetTextInfoFromFile(pos)
return l
end
local function getTxChText(index)
local ch = nil
local out = nil
if (index >= 0x000D and index <= 0x000D+7) then ch = index - 0x000D + 5 -- ch5
elseif (index >= 0x0036 and index <= 0x0036+11) then ch = index - 0x0036 end
if (ch ~= nil) then
out = "Ch"..(ch+1) .. " ("..(MODEL.TX_CH_TEXT[ch] or "--")..")"
--out = MODEL.PORT_TEXT[ch] or "--"
end
return out
end
-----------------------
local function Get_Text(index)
local pos = Text[index]
local out = nil
if (pos == nil) then
out = string.format("Unknown_%X", index)
else
out = GetTextFromFile(pos)
end
if (index >= 0x8000) then
out = Flight_Mode
end
return out
end
local function Get_Text_Value(index)
local out = getTxChText(index)
if (out) then return out end
local pos = List_Text[index]
if (pos == nil) then
out = Get_Text(index)
else
out = GetTextFromFile(pos)
end
return out
end
---------------------
local function Get_RxName(index)
local out = RxName[index] or string.format("Unknown_%X", index)
return out
end
--------------------
local function updateValText(line)
line.ValText = line.Val
if (isListLine(line)) then
line.ValText = Get_Text_Value(line.TextStart + line.Val)
end
end
local function DSM_Connect()
--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'))
end
local function DSM_Release()
multiBuffer(0, 0)
end
--------------------
local function DSM_Send(...)
local arg = { ... }
for i = 1, #arg do
multiBuffer(3 + i, arg[i])
end
multiBuffer(3, 0x70 + #arg)
end
---------------------
function ChangePhase(newPhase)
Phase = newPhase
SendDataToRX = 1
end
local function Value_Add(dir)
local line = MenuLines[ctx_SelLine]
local origVal = line.Val
local inc = dir
if (not isListLine(line)) then -- List do slow inc
local Speed = getRotEncSpeed()
if Speed == ROTENC_MIDSPEED then
inc = (5 * dir)
elseif Speed == ROTENC_HIGHSPEED then
inc = (15 * dir)
end
end
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
if (origVal~=line.Val) then -- Any changes?
if (isIncrementalValueUpdate(line)) then
-- Update RX value on every change, otherwise, just at the end
ChangePhase(PH_VAL_CHANGING)
end
updateValText(line)
end
end
--------------
local function GotoMenu(menuId, lastSelectedLine)
Menu.MenuId = menuId
ctx_SelLine = lastSelectedLine
-- Request to load the menu Again
ChangePhase(PH_TITLE)
end
local function DSM_HandleEvent(event)
if event == EVT_VIRTUAL_EXIT then
if Phase == PH_RX_VER then
Phase = PH_EXIT_DONE -- Exit program
else
if isEditing() then -- Editing a Line, need to restore original value
MenuLines[ctx_EditLine].Val = originalValue
event = EVT_VIRTUAL_ENTER
else
if (Menu.BackId > 0 ) then -- Back??
ctx_SelLine = -1 --Back Button
event = EVT_VIRTUAL_ENTER
else
ChangePhase(PH_EXIT_REQ)
end
end
end
end -- Exit
if Phase == PH_RX_VER then return end -- nothing else to do
if event == EVT_VIRTUAL_NEXT then
if isEditing() then -- Editting?
Value_Add(1)
else
if ctx_SelLine < 7 then -- On a regular line
local num = ctx_SelLine -- Find the prev selectable
for i = ctx_SelLine + 1, 6, 1 do
local line = MenuLines[i]
if isSelectable(line) then
ctx_SelLine = i
break
end
end
if num == ctx_SelLine then -- No Selectable Line
if Menu.NextId ~= 0 then
ctx_SelLine = 7 -- Next
elseif Menu.PrevId ~= 0 then
ctx_SelLine = 8 -- Prev
end
end
elseif Menu.PrevId ~= 0 then
ctx_SelLine = 8 -- Prev
end
end
return
end
if event == EVT_VIRTUAL_PREV then
if isEditing() then -- In Edit Mode
Value_Add(-1)
else
if ctx_SelLine == 8 and Menu.NextId ~= 0 then
ctx_SelLine = 7 -- Next
elseif ctx_SelLine > 0 then
if ctx_SelLine > 6 then
ctx_SelLine = 7 --NEXT
end
local num = ctx_SelLine -- Find Prev Selectable line
for i = ctx_SelLine - 1, 0, -1 do
local line = MenuLines[i]
if isSelectable(line) then
ctx_SelLine = i
break
end
end
if num == ctx_SelLine then -- No Selectable Line
if (Menu.BackId > 0) then
ctx_SelLine = -1 -- Back
end
end
else
ctx_SelLine = -1 -- Back
end
end
return
end
if event == EVT_VIRTUAL_ENTER_LONG then
if isEditing() then
MenuLines[ctx_SelLine].Val = MenuLines[ctx_SelLine].Def
ChangePhase(PH_VAL_CHANGING)
end
elseif event == EVT_VIRTUAL_ENTER then
if ctx_SelLine == -1 then -- Back
GotoMenu(Menu.BackId, 0x80)
elseif ctx_SelLine == 7 then -- Next
GotoMenu(Menu.NextId, 0x82)
elseif ctx_SelLine == 8 then -- Prev
GotoMenu(Menu.PrevId, 0x81)
elseif ctx_SelLine >= 0 and MenuLines[ctx_SelLine].Type == LT_MENU then
GotoMenu(MenuLines[ctx_SelLine].ValId, ctx_SelLine) -- ValId is the next menu
else
-- value entry
if isEditing() then
ctx_EditLine = nil -- Done Editting
ChangePhase(PH_VAL_EDIT_END)
else -- Start Editing
ctx_EditLine = ctx_SelLine
originalValue = MenuLines[ctx_SelLine].Val
ChangePhase(PH_VAL_EDITING)
end
end
end
end
------------------------------------------------------------------------------------------------------------
local function SendTxInfo(portNo)
-- TxInfo_Type=0 : AR636 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 (TX_Info_Step == 0) then
-- AR630 family: Both TxInfo_Type (ManinMenu=0x1, Other First Time Configuration = 0x1F)
local info = MODEL.DSM_ChannelInfo[portNo]
local b0, b1, b2 = info[0], info[1], info[2]
DSM_Send(0x20, 0x06, portNo, portNo, b0,b1)
LOG_write("TX:DSM_TxInfo_20(Port=#%d, (%02x, %02x) %s)\n", portNo, b0,b1,b2 or "")
if (TX_Info_Type == 0x1F) then -- SmartRx
TX_Info_Step = 1
elseif (TX_Info_Type == 0x00) then -- AR636
TX_Info_Step = 2
end
elseif (TX_Info_Step == 1) then
local info = MODEL.modelOutputChannel[portNo]
local leftTravel = math.abs(math.floor(info.min/10))
local rightTravel = math.abs(math.floor(info.max/10))
DSM_Send(0x23, 0x06, 0x00, leftTravel, 0x00, rightTravel)
LOG_write("TX:DSM_TxInfo_Travel(Port=#%d,(L=%d - R=%d))\n", portNo,leftTravel,rightTravel)
TX_Info_Step = 2
elseif (TX_Info_Step == 2) then
-- Subtrim
local b1,b2,b3,b4 = 0x00, 0x8E, 0x07, 0x72 -- (192-1904)
if (portNo==0) then -- Thr
b1,b2,b3,b4 = 0x00, 0x00, 0x07, 0xFF -- (0-2047)
end
DSM_Send(0x21, 0x06, b1,b2,b3,b4) -- Port is not send anywhere, since the previous 0x20 type message have it.
LOG_write("TX:DSM_TxInfo_SubTrim(Port=#%d)\n", portNo)
if (TX_Info_Type == 0x00) then -- AR636
TX_Info_Step = 5 -- End Step
else
TX_Info_Step = 3
end
elseif (TX_Info_Step == 3) then
LOG_write("TX:DSM_TxInfo_24?(Port=#%d)\n", portNo)
DSM_Send(0x24, 0x06, 0x00, 0x83, 0x5A, 0xB5) -- Still Uknown
TX_Info_Step = 4
elseif (TX_Info_Step == 4) then
LOG_write("TX:DSM_TxInfo_24?(Port=#%d)\n", portNo)
DSM_Send(0x24, 0x06, 0x06, 0x80, 0x25, 0x4B) -- Still Uknown
TX_Info_Step = 5
elseif (TX_Info_Step == 5) then
LOG_write("TX:DSM_TxInfo_END(Port=#%d)\n", portNo)
DSM_Send(0x22, 0x04, 0x00, 0x00)
TX_Info_Step = 0 -- Done!!
end
if (TX_Info_Step > 0) then
SendDataToRX = 1 -- keep Transmitig
end
end
local function DSM_SendUpdate(line)
local valId = line.ValId
local value = sInt16ToDsm(line.Val)
LOG_write("TX:ChangeValue(VId=0x%04X,Val=%d)\n", valId, line.Val)
DSM_Send(0x18, 0x06,
int16_MSB(valId), int16_LSB(valId),
int16_MSB(value), int16_LSB(value)) -- send current values
end
local function DSM_SendValidate(line)
local valId = line.ValId
LOG_write("TX:ValidateValue(VId=0x%04X)\n", valId)
DSM_Send(0x19, 0x04, int16_MSB(valId), int16_LSB(valId))
end
local function DSM_SendRequest()
--LOG_write("DSM_SendRequest Phase=%d\n",Phase)
-- Need to send a request
local menuId = Menu.MenuId
local menuLSB = int16_LSB(menuId)
local menuMSB = int16_MSB(menuId)
if Phase == PH_RX_VER then -- request RX version
DSM_Send(0x11, 0x06, TX_MAX_CH, 0x14, 0x00, 0x00)
LOG_write("TX:GetVersion(TX_MAX_CH=%d)\n",TX_MAX_CH+6)
elseif Phase == PH_WAIT_CMD then -- keep connection open
DSM_Send(0x00, 0x04, 0x00, 0x00)
--LOG_write("TX:TxHb\n")
elseif Phase == PH_TITLE then -- request menu title
if menuId == 0 then
-- very first menu only
DSM_Send(0x12, 0x06, TX_MAX_CH, 0x14, 0x00, 0x00)
else
-- Any other menu
DSM_Send(0x16, 0x06, menuMSB, menuLSB, 0x00, ctx_SelLine)
if (menuId == 0x0001) then -- Executed Save&Reset menu
Phase = PH_RX_VER
ctx_isReset = true
end
end
LOG_write("TX:GetMenu(M=0x%04X,L=%d)\n", menuId, ctx_SelLine)
elseif Phase == PH_TX_INFO then -- TX Info
SendTxInfo(ctx_CurLine)
elseif Phase == PH_LINES then -- request menu lines
if ctx_CurLine == -1 then
DSM_Send(0x13, 0x04, menuMSB, menuLSB) -- GetFirstLine
else
DSM_Send(0x14, 0x06, menuMSB, menuLSB, 0x00, ctx_CurLine) -- line X
end
LOG_write("TX:GetNextLine(LastLine=%d)\n", ctx_CurLine)
elseif Phase == PH_VALUES then -- request menu values
local valId = MenuLines[ctx_CurLine].ValId
DSM_Send(0x15, 0x06,
menuMSB, menuLSB,
int16_MSB(valId), int16_LSB(valId))
LOG_write("TX:GetNextValue(LastVId=0x%04X)\n", valId)
elseif Phase == PH_VAL_EDITING then -- Editing a line (like a HB)
local line = MenuLines[ctx_SelLine]
DSM_Send(0x1A, 0x04, 0x00, ctx_SelLine)
LOG_write("TX:EditingValueLine(L=%d)\n", ctx_SelLine)
elseif Phase == PH_VAL_CHANGING then -- change value during editing
local line = MenuLines[ctx_SelLine]
if (Change_Step==0) then
DSM_SendUpdate(line)
if line.Type == LT_LIST then -- Incremental Validation??
Change_Step=1
end
else -- Change_Step==1
DSM_SendValidate(line)
Change_Step=0
end
if (Change_Step==0) then Phase=PH_VAL_EDITING else SendDataToRX=1 end -- Done with change?
elseif Phase == PH_VAL_EDIT_END then -- Done Editing line
local line = MenuLines[ctx_SelLine]
if (Change_Step==0) then
DSM_SendUpdate(line)
Change_Step=1
elseif (Change_Step==1) then
DSM_SendValidate(line)
Change_Step=2
else -- Change_Step==3
LOG_write("TX:EditValueEnd(L=%d)\n", ctx_SelLine)
DSM_Send(0x1B, 0x04, 0x00, ctx_SelLine)
Change_Step=0
end
if (Change_Step==0) then Phase = PH_WAIT_CMD else SendDataToRX=1 end -- Done with change?
elseif Phase == PH_EXIT_REQ then -- EXIT Request
DSM_Send(0x1F, 0x02, 0xAA)
LOG_write("TX:TX Exit Request\n")
end
end
local function DSM_ProcessResponse()
local cmd = multiBuffer(11)
if cmd == 0x01 then -- read version
RX_Name = Get_RxName(multiBuffer(13))
RX_Version = multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16)
Menu.MenuId = 0
Phase = PH_TITLE
LOG_write("RX:Version: %s %s\n", RX_Name, RX_Version)
elseif cmd == 0x02 then -- read menu title
local menu = Menu
menu.MenuId = Dsm_to_Int16(multiBuffer(12), multiBuffer(13))
menu.TextId = Dsm_to_Int16(multiBuffer(14), multiBuffer(15))
menu.Text = 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, 6 do -- clear menu
MenuLines[i] = { Type = 0 }
end
ctx_CurLine = -1
ctx_SelLine = -1 -- highlight Back
LOG_write("RX:Menu: Mid=0x%04X \"%s\"\n", menu.MenuId, menu.Text)
if (menu.MenuId == 0x0001) then -- Still in RESETTING MENU???
Phase = PH_RX_VER
else
Phase = PH_LINES
end
elseif cmd == 0x03 then -- read menu lines
local i = multiBuffer(14)
local type = multiBuffer(15)
local line = MenuLines[i]
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 = Get_Text(line.TextId)
line.ValId = Dsm_to_Int16(multiBuffer(18), multiBuffer(19))
-- Signed 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))
if line.Type == LT_MENU then
-- nothing to do on menu entries
elseif isListLine(line) then
line.Val = nil
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
line.Val = nil --line.Def -- use default value not sure if needed
if (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) then
line.Type = LT_VALUE_PERCENT -- Override to Value Percent
end
end
if ctx_SelLine == -1 and isSelectable(line) then -- Auto select first selectable line of the menu
ctx_SelLine = ctx_CurLine
end
LOG_write("RX:Line: #%d Vid=0x%04X T=0x%02X \"%s\"\n", i, line.ValId, type, line.Text)
if (line.MenuId~=Menu.MenuId) then -- Going Back too fast: Stil receiving lines from previous menu
Menu.MenuId = line.MenuId
end
Phase = PH_LINES
elseif cmd == 0x04 then -- read menu values
-- Identify the line and update the value
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, 6 do -- Find the menu line for this value
local line = MenuLines[i]
if line.Type ~= 0 then
if line.Type ~= LT_MENU and line.ValId == valId then -- identifier of ValueId stored in the line
line.Val = value
ctx_CurLine = i
--updatedLine = line
updateValText(line)
local debugTxt = line.Val
if isListLine(line) then
debugTxt = line.ValText .. " [" .. value .. "]"
end
LOG_write("RX: Value Updated: #%d VId=0x%04X Value=%s\n", i, valId, debugTxt)
break
end
end
end
--if (updatedLine == nil) then
-- LOG_write("Cannot Find Line for ValueId=%x\n", valId)
--end
Phase = PH_VALUES
elseif cmd == 0x05 then -- Request TX info
ctx_CurLine = multiBuffer(12)
TX_Info_Type = multiBuffer(13)
TX_Info_Step = 0
Phase = PH_TX_INFO
LOG_write("RX:TXInfoReq: Port=%d T=0x%02X\n", ctx_CurLine, TX_Info_Type)
elseif cmd == 0xA7 then -- RX EXIT Request
if Phase == PH_EXIT_REQ then -- Response to our EXIT req
Phase = PH_EXIT_DONE
else -- Unexpected RX Exit
DSM_Release()
error("RX Connection Drop")
end
elseif cmd == 0x00 then -- RX Heartbeat
LOG_write("RX:RxHb\n")
end
return cmd
end
local function DSM_Send_Receive()
-- Receive part: Process incoming messages if there is nothing to send
if SendDataToRX==0 and multiBuffer(10) == 0x09 then
local cmd = DSM_ProcessResponse()
-- Data processed
multiBuffer(10, 0x00)
RXInactivityTime = getTime() + 800 -- Reset Inactivity timeout (8s)
if (cmd > 0x00) then -- RX-HeartBeat??
-- Only change to SEND mode if we received a valid response (Ignore heartbeat)
SendDataToRX = 1
end
else
-- Check if enouth time has passed from last Received activity
if (getTime() > RXInactivityTime and Phase~=PH_RX_VER and Phase~= PH_EXIT_DONE) then
if (isEditing()) then -- If Editing, extend time
RXInactivityTime = getTime() + 400
else
DSM_Release()
error("RX Disconnected")
end
end
end
-- Sending part --
if SendDataToRX == 1 then
SendDataToRX = 0
DSM_SendRequest()
TXInactivityTime = getTime() + 200 -- Reset Inactivity timeout (2s)
else
-- Check if enouth time has passed from last transmit activity
if getTime() > TXInactivityTime then
SendDataToRX = 1 -- Switch to Send mode to send heartbeat
-- Change to Idle/HB mode if we are wating for RX version
if Phase ~= PH_RX_VER then
-- Phase = If IsEditing then PH_VAL_EDITING else PH_WAIT_CMD
Phase = (isEditing() and PH_VAL_EDITING) or PH_WAIT_CMD
end
end
end
end
-----
local function showBitmap(x, y, imgDesc)
local f = string.gmatch(imgDesc, '([^%|]+)') -- Iterator over values split by '|'
local imgName, imgMsg = f(), f()
f = string.gmatch(imgMsg or "", '([^%:]+)') -- Iterator over values split by ':'
local p1, p2 = f(), f()
lcd.drawText(x, y, p1 or "", TEXT_ATTR) -- Alternate Image MSG
lcd.drawText(x, y + LCD_Y_LINE_HEIGHT, p2 or "", TEXT_ATTR) -- Alternate Image MSG
end
local function drawButton(x, y, text, active)
local attr = TEXT_ATTR
if (active) then attr = attr + INVERS end
lcd.drawText(x, y, text, attr)
end
local ver_rx_count = 0
local function DSM_Display()
lcd.clear()
--Draw RX Menu
if Phase == PH_RX_VER then
lcd.drawText(1, 0, "DSM Frwd Prog "..VERSION, INVERS)
local msgId = 0x300 -- Waiting for RX
if (ctx_isReset) then msgId=0x301 end -- Waiting for Reset
lcd.drawText(1, 3 * LCD_Y_LINE_HEIGHT, Get_Text(msgId), BLINK)
return
end
-- display Program version or RX version
local msg = RX_Name .. " v" .. RX_Version
ver_rx_count = ver_rx_count + 1
if (ver_rx_count > 50) then
msg = "FProg "..VERSION
if (ver_rx_count > 100) then ver_rx_count=0 end
end
lcd.drawText(40, LCD_Y_LOWER_BUTTONS, msg, TEXT_ATTR)
if Menu.MenuId == 0 then return end; -- No Title yet
-- Got a Menu
lcd.drawText(1, 0, Menu.Text, TEXT_ATTR + INVERS)
if (Phase == PH_TX_INFO) then
lcd.drawText(1, 3 * LCD_Y_LINE_HEIGHT, "Sending CH"..(ctx_CurLine+1), TEXT_ATTR)
end
local y = LCD_Y_LINE_HEIGHT + 2
for i = 0, 6 do
local attrib = TEXT_ATTR
if (i == ctx_SelLine) then attrib = attrib + INVERS end -- Selected Line
local line = MenuLines[i]
if line.Type ~= 0 then
local heading = line.Text
if (line.TextId >= 0x8000) then -- Flight mode
heading = " " .. Flight_Mode .. " "
if (line.Val==nil) then heading = heading .. "--" else heading = heading .. ((line.Val or 0) + 1) end
else
local text = nil
if line.Type ~= LT_MENU then -- list/value
if line.Val ~= nil then -- Value to display??
-- text = line.Val
text = line.ValText
if isListLine(line) then
local textId = line.Val + line.TextStart
--text = Get_Text_Value(textId)
-- image??
local offset = 0
if (line.Type==LT_LIST_ORI) then offset = offset + 0x100 end --FH6250 hack
local imgDesc = GetTextFromFile(List_Text_Img[textId+offset])
if (imgDesc and i == ctx_SelLine) then -- Optional Image and Msg for selected value
showBitmap(1, 20, imgDesc)
end
end
end -- Value
if (ctx_EditLine == i) then -- Editing a Line
attrib = BLINK + INVERS + TEXT_ATTR
end
lcd.drawText(LCD_X_MAX, y, text or "--", attrib + RIGHT) -- display value
attrib = TEXT_ATTR
end -- Line with value/list
end -- not Flight mode
lcd.drawText(1, y, heading, attrib) -- display text
end
y = y + LCD_Y_LINE_HEIGHT
end -- for
if Menu.BackId~=0 then
drawButton(LCD_X_RIGHT_BUTTONS, 0, "Back", ctx_SelLine == -1)
end
if Menu.NextId~=0 then
drawButton(LCD_X_RIGHT_BUTTONS, LCD_Y_LOWER_BUTTONS, "Next", ctx_SelLine == 7)
end
if Menu.PrevId~=0 then
drawButton(1, LCD_Y_LOWER_BUTTONS, "Prev", ctx_SelLine == 8)
end
end
-----------------------------------------------------------------------------------------
local function load_msg_from_file(fileName, offset, FileState)
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+offset)
--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)
local filePos = lineStart + offset
if (index == nil) then
assert(false, string.format("%s:%d: Invalid Hex num [%s]", fileName, FileState.lineNo, sIndex))
elseif (type == "T") then
Text[index] = filePos
elseif (type == "LT") then
List_Text[index] = filePos
elseif (type == "LI") then
List_Text_Img[index] = filePos
elseif (type == "FM") then
Flight_Mode = 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
gc()
end -- for
end -- if
return 0
end
------------------------------------------------------------------------------------------------------------
-- Init
local function DSM_Init()
--LOG_open()
--LOG_write("--- NEW SESSION\n")
--DSM_Init_Model()
--Set protocol to talk to
multiBuffer(0, string.byte('D'))
--test if value has been written
if multiBuffer(0) ~= string.byte('D') then
error("Not enough memory!")
return 2
end
if (LCD_W > 128) then
TEXT_ATTR = 0
LCD_Y_LINE_HEIGHT = 25
LCD_X_MAX = 300
LCD_X_RIGHT_BUTTONS = LCD_X_MAX - 30
LCD_Y_LOWER_BUTTONS = (8 * LCD_Y_LINE_HEIGHT) + 2
end
Phase = PH_INIT
end
-----------------------------------------------------------------------------------------------------------
local initStep=0
local FileState = { lineNo=0 }
local function Inc_Init()
lcd.clear()
lcd.drawText(1, 0, "Loading Msg file: "..(FileState.lineNo or 0))
if (initStep == 0) then
if (load_msg_from_file(MSG_FILE, 0, FileState)==1) then
initStep=1
FileState = {}
end
else
Phase = PH_RX_VER -- Done Init
DSM_Connect()
end
end
------------------------------------------------------------------------------------------------------------
-- Main
local function DSM_Run(event)
if event == nil then
error("Cannot be run as a model script!")
return 2
end
if (Phase == PH_INIT) then
Inc_Init() -- Incremental initialization
return 0
end
DSM_Display()
DSM_HandleEvent(event)
DSM_Send_Receive()
gc()
if Phase == PH_EXIT_DONE then
DSM_Release()
LOG_close()
return 2
else
return 0
end
end
---
gc()
local M_DATA = {}
-- Load Model Configuration
local r = assert(loadScript(DSMLIB_PATH.."DsmMIN_P1.lua"), "Mising: DsmMIN_P1.lua")
(MODEL, M_DATA, LOG_write)
gc()
if (r==1) then
-- Translate model Configuration to DSMDATA
local r = assert(loadScript(DSMLIB_PATH.."DsmMIN_P2.lua"), "Missing DsmMIN_P2.lua")
(MODEL, M_DATA, LOG_write)
MODEL.PORT_TEXT = nil
--MODEL.TX_CH_TEXT = {}
gc()
else
error("Cannot load Model Config")
end
M_DATA = nil
gc()
---
return { init = DSM_Init, run = DSM_Run }