#751 DSM Enhancements

#751 DSM Forward Programming Enhancements (New GUI, etc)
This commit is contained in:
Frankie Arzu 2022-11-16 00:46:25 -06:00
parent 2d469d074e
commit 68eab7796a
7 changed files with 3447 additions and 0 deletions

View File

@ -0,0 +1,560 @@
local toolName = "TNS|DSM Forward Prog v0.5 (EdgeTx) |TNE"
local VERSION = "v0.5"
---- #########################################################################
---- # #
---- # 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. #
---- # #
---- #########################################################################
-- Code is based on the code/work by: Pascal Langer (Author of the Multi-Module)
-- Rewrite/Enhancements By: Francisco Arzu
local SIMULATION_ON = false -- FALSE: use real communication to DSM RX (DEFAULT), TRUE: use a simulated version of RX
local DEBUG_ON = 1 -- 0=NO DEBUG, 1=HIGH LEVEL 2=LOW LEVEL (Debug logged into the /LOGS/dsm.log)
local DEBUG_ON_LCD = false -- Interactive Information on LCD of Menu data from RX
local dsmLib
if (SIMULATION_ON) then
-- library with SIMILATION VERSION. Works really well in Companion for GUI development
dsmLib = loadScript("/SCRIPTS/TOOLS/DSMLIB/DsmFwPrgSIMLib.lua")(DEBUG_ON)
else
dsmLib = loadScript("/SCRIPTS/TOOLS/DSMLIB/DsmFwPrgLib.lua")(DEBUG_ON)
end
local PHASE = dsmLib.PHASE
local LINE_TYPE = dsmLib.LINE_TYPE
local DISP_ATTR = dsmLib.DISP_ATTR
local DSM_Context = dsmLib.DSM_Context
local LCD_X_LINE_MENU = 30
local LCD_X_LINE_TITLE = 30
local LCD_X_LINE_VALUE = 230
local LCD_X_LINE_DEBUG = 390
local LCD_Y_MENU_TITLE = 20
local LCD_Y_LINE_START = LCD_Y_MENU_TITLE + 30
local LCD_Y_LINE_HEIGHT = (DEBUG_ON_LCD and 23) or 27 -- if DEBUG 23 else 27
local LCD_Y_LOWER_BUTTONS = LCD_Y_LINE_START + 3 + (7 * LCD_Y_LINE_HEIGHT)
local lastRefresh=0 -- Last time the screen was refreshed
local REFRESH_GUI_MS = 300/10 -- 300ms.. Screen Refresh Rate.. to not waste CPU time (in 10ms units to be compatible with getTime())
local originalValue = nil
local touchButtonArea = {}
local EDIT_BUTTON = { DEFAULT=1001, DEC_10=1002, DEC_1=1003, INC_1=1004, INC_10=5, OK=1006, ESC=1007 }
local function GUI_SwitchSimulationOFF()
dsmLib.ReleaseConnection()
dsmLib.LOG_close()
SIMULATION_ON = false
dsmLib = loadScript("/SCRIPTS/TOOLS/DSMLIB/DsmFwPrgLib.lua")(DEBUG_ON)
DSM_Context = dsmLib.DSM_Context
dsmLib.Init(toolName) -- Initialize Library
dsmLib.StartConnection()
DSM_Context.Refresh_Display = true
end
--------------------- Toucch Button Helpers ------------------------------------------------------------
local function GUI_addTouchButton(x,y,w,h,line)
-- Add new button info to end of the array
touchButtonArea[#touchButtonArea+1] = {x=x, y=y, w=w, h=h, line=line}
end
local function GUI_getTouchButton(x,y)
for i = 1, #touchButtonArea do
local button = touchButtonArea[i]
-- is the coordinate inside the button area??
if (x >= button.x and x <= (button.x+button.w) and y >= button.y and (y <= button.y+button.h)) then
return button.line
end
end
return nil
end
local function GUI_clearTouchButtons()
touchButtonArea = {}
end
---------- Return Color to display Menu Lines ----------------------------------------------------------------
local function GUI_GetTextColor(lineNum)
local ctx = DSM_Context
local txtColor = BLACK
-- Gray Out any other line except the one been edited
if (ctx.isEditing() and ctx.EditLine~=lineNum) then txtColor=LIGHTGREY end
return txtColor
end
local function GUI_GetFrameColor(lineNum) -- Frame Color for Value/Menu Boxes
local ctx = DSM_Context
local txtColor = LIGHTGREY --ORANGE
-- Gray Out any other line except the one been edited
if (ctx.isEditing() and ctx.EditLine~=lineNum) then txtColor=LIGHTGREY end
return txtColor
end
--------------------------------------------------------------------------------------------------------
-- Display Text inside a Rectangle. Inv: true means solid rectangle, false=only perimeter
local function GUI_Display_Boxed_Text(lineNum,x,y,w,h,text,inv)
local txtColor = GUI_GetTextColor(lineNum)
local frameColor = GUI_GetFrameColor(lineNum)
if (inv) then
txtColor = WHITE
lcd.drawFilledRectangle(x-5, y-2, w, h, ORANGE)
else
lcd.drawRectangle(x-5, y-2, w, h, frameColor)
end
lcd.drawText(x , y, text, txtColor)
end
------ Display Pre/Next/Back buttons
local function GUI_Diplay_Button(x,y,w,h,text,selected)
GUI_Display_Boxed_Text(-1,x,y,w,h,text,selected)
end
------ Display MENU type of lines (Navigation, SubHeaders, and plain text comments)
local function GUI_Display_Line_Menu(lineNum,line,selected)
-- Menu Lines can be navidation to other Menus (if Selectable)
-- Or SubHeaders or Messages
local txtColor = GUI_GetTextColor(lineNum)
local y = LCD_Y_LINE_START+(LCD_Y_LINE_HEIGHT*lineNum)
local x = LCD_X_LINE_MENU
if dsmLib.isSelectableLine(line) then -- Draw Selectable Menus in Boxes
GUI_Display_Boxed_Text(lineNum,x, y, 350, LCD_Y_LINE_HEIGHT, line.Text,selected)
GUI_addTouchButton(x, y, 350, LCD_Y_LINE_HEIGHT,lineNum)
else
-- Non Selectable Menu Lines, plain text
-- Can be use for sub headers or just regular text lines (like warnings)
local bold = (dsmLib.isDisplayAttr(line.TextAttr,DISP_ATTR.BOLD) and BOLD) or 0
if dsmLib.isDisplayAttr(line.TextAttr,DISP_ATTR.RIGHT) then -- Right Align???
local tw = lcd.sizeText(line.Text)+4
x = LCD_X_LINE_VALUE - tw -- Right
elseif dsmLib.isDisplayAttr(line.TextAttr,DISP_ATTR.CENTER) then -- Center??
local tw = lcd.sizeText(line.Text)
x = x + (LCD_X_LINE_VALUE - LCD_X_LINE_MENU)/2 - tw/2 -- Center - 1/2 Text
end
lcd.drawText(x, y, line.Text, txtColor + bold)
end
end
------ Display NAME : VALUES type of lines
local function GUI_Display_Line_Value(lineNum, line, value, selected, editing)
-- This Displays Name and Value Pairs
local txtColor = GUI_GetTextColor(lineNum)
local bold = 0
local y = LCD_Y_LINE_START+(LCD_Y_LINE_HEIGHT*lineNum)
local x = LCD_X_LINE_TITLE
--if (editing) then -- Any Special color/effect when editing??
-- value = "["..value .. "]"
--end
---------- NAME Part
local header = line.Text
-- ONLY do this for Flight Mode (Right Align or Centered)
if (dsmLib.isFlightModeText(line.TextId)) then
-- Display Header + Value together
header = header .. " " .. value
-- Bold Text???
bold = (dsmLib.isDisplayAttr(line.TextAttr,DISP_ATTR.BOLD) and BOLD) or 0
if dsmLib.isDisplayAttr(line.TextAttr,DISP_ATTR.RIGHT) then -- Right Align
local tw = lcd.sizeText(header)+4
x = LCD_X_LINE_VALUE - tw -- Right
elseif dsmLib.isDisplayAttr(line.TextAttr,DISP_ATTR.CENTER) then -- Centered
local tw = (lcd.sizeText(header))
x = x + (LCD_X_LINE_VALUE - LCD_X_LINE_TITLE)/2 - tw/2 -- Center - 1/2 Text
end
else
-- No Flight Mode, no effects here
header = header .. ":"
end
lcd.drawText(x, y, header, txtColor + bold) -- display Line Header
--------- VALUE PART, Skip for Flight Mode since already show the value
if not dsmLib.isFlightModeText(line.TextId) then
value = value .. (line.Format or "") -- Append % if needed
if dsmLib.isSelectableLine(line) then
-- Can select/edit value, Box it
local tw = lcd.sizeText(value)+10 -- Width of the Text in the lcd
GUI_Display_Boxed_Text(lineNum,LCD_X_LINE_VALUE,y,tw,LCD_Y_LINE_HEIGHT,value,selected)
GUI_addTouchButton(LCD_X_LINE_VALUE,y,tw,LCD_Y_LINE_HEIGHT,lineNum)
else -- Not Editable, Plain Text
lcd.drawText(LCD_X_LINE_VALUE, y, value, txtColor)
end
end
-- Debug info for line Value RANGE when Debug on LCD
if (DEBUG_ON_LCD) then lcd.drawText(LCD_X_LINE_DEBUG, y, line.MinMaxDebug or "", SMLSIZE + BLUE) end -- display debug Min/Max
end
local function GUI_Display_Menu(menu)
local ctx = DSM_Context
local w= LCD_W-100 -- usable Width for the Menu/Lines
-- Center Header
local tw = lcd.sizeText(menu.Text)
local x = w/2 - tw/2 -- Center of Screen - Center of Text
lcd.drawFilledRectangle(0, LCD_Y_MENU_TITLE-2, w, LCD_Y_LINE_HEIGHT-2, DARKGREY)
lcd.drawText(x,LCD_Y_MENU_TITLE,menu.Text, WHITE + BOLD) -- orig MIDSIZE
-- Back Button
if menu.BackId ~= 0 then
GUI_Diplay_Button(437-5,LCD_Y_MENU_TITLE+3,47,LCD_Y_LINE_HEIGHT,"Back",ctx.SelLine == dsmLib.BACK_BUTTON)
GUI_addTouchButton(437-5,LCD_Y_MENU_TITLE+3,47,LCD_Y_LINE_HEIGHT,dsmLib.BACK_BUTTON)
end
-- Next Button
if menu.NextId ~= 0 then
GUI_Diplay_Button(437-5,LCD_Y_LOWER_BUTTONS,47,LCD_Y_LINE_HEIGHT,"Next",ctx.SelLine == dsmLib.NEXT_BUTTON)
GUI_addTouchButton(437-5,LCD_Y_LOWER_BUTTONS,47,LCD_Y_LINE_HEIGHT,dsmLib.NEXT_BUTTON)
end
-- Prev Button
if menu.PrevId ~= 0 then
GUI_Diplay_Button(10,LCD_Y_LOWER_BUTTONS,47,LCD_Y_LINE_HEIGHT,"Prev",ctx.SelLine == dsmLib.PREV_BUTTON)
GUI_addTouchButton(10,LCD_Y_LOWER_BUTTONS,47,LCD_Y_LINE_HEIGHT,dsmLib.PREV_BUTTON)
end
-- Debug on LCD, Show the menu Indo and Phase we are on
if (DEBUG_ON_LCD) then lcd.drawText(0,LCD_Y_MENU_TITLE,dsmLib.phase2String(ctx.Phase),SMLSIZE+BLUE) end -- Phase we are in
if (DEBUG_ON_LCD) then lcd.drawText(0,240,dsmLib.menu2String(menu),SMLSIZE+BLUE) end -- Menu Info
end
------------------------------------------------------------------------------------------------------------
-- Display the EDIT mode buttons when editing a value
local function GUI_Display_Edit_Buttons(line)
GUI_clearTouchButtons() -- Only this buttons can be touched
local x = 15 -- Inittial X position
local w = 55 -- Width of the buttons
local showPrev = line.Val > line.Min
local showNext = line.Val < line.Max
GUI_Diplay_Button(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT,"ESC",true)
GUI_addTouchButton(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT,EDIT_BUTTON.ESC)
x=x+w+10
GUI_Diplay_Button(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT," Def",true)
GUI_addTouchButton(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT,EDIT_BUTTON.DEFAULT)
x=x+w+10
if (not dsmLib.isListLine(line)) then
GUI_Diplay_Button(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT," << ",showPrev)
GUI_addTouchButton(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT,EDIT_BUTTON.DEC_10)
end
x=x+w+10
GUI_Diplay_Button(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT," <",showPrev)
GUI_addTouchButton(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT,EDIT_BUTTON.DEC_1)
x=x+w+10
GUI_Diplay_Button(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT," >",showNext)
GUI_addTouchButton(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT,EDIT_BUTTON.INC_1)
x=x+w+10
if (not dsmLib.isListLine(line)) then
GUI_Diplay_Button(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT," >>",showNext)
GUI_addTouchButton(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT,EDIT_BUTTON.INC_10)
end
x=x+w+10
GUI_Diplay_Button(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT," OK",true)
GUI_addTouchButton(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT,EDIT_BUTTON.OK)
end
------------------------------------------------------------------------------------------------------------
local function GUI_Display()
local ctx = DSM_Context
lcd.clear()
GUI_clearTouchButtons()
if LCD_W == 480 then
local header = "DSM Forward Programming "..VERSION.." "
if ctx.Phase ~= PHASE.RX_VERSION then
header = header .. "RX "..ctx.RX.Name.." v"..ctx.RX.Version
end
--Draw title
lcd.drawFilledRectangle(0, 0, LCD_W, 17, TITLE_BGCOLOR)
lcd.drawText(5, 0, header, MENU_TITLE_COLOR + SMLSIZE)
--Draw RX Menu
if ctx.Phase == PHASE.RX_VERSION then
lcd.drawText(LCD_X_LINE_TITLE,100,"No compatible DSM RX...", BLINK)
else
local menu = ctx.Menu
if menu.Text ~= nil then
GUI_Display_Menu(menu)
for i = 0, dsmLib.MAX_MENU_LINES do
local line = ctx.MenuLines[i]
if i == ctx.SelLine then
-- DEBUG: Display Selected Line info for ON SCREEN Debugging
if (DEBUG_ON_LCD) then lcd.drawText(0,255,dsmLib.menuLine2String(line),SMLSIZE+BLUE) end
end
if line ~= nil and line.Type ~= 0 then
if line.Type == LINE_TYPE.MENU then
GUI_Display_Line_Menu(i, line, i == ctx.SelLine)
else
-- list/value line
local value, imgValue = line.Val, nil
if line.Val ~= nil then
if dsmLib.isListLine(line) then -- for Lists of Strings, get the text
value = dsmLib.Get_Text(line.Val + line.TextStart) -- TextStart is the initial offset for text
imgValue = dsmLib.Get_Text_Img(line.Val + line.TextStart) -- Complentary IMAGE for this value to Display??
if (imgValue) then -- Optional Image for a Value
--TODO: Pending feature.. create images and put bitmap instead of a message
--Display the image/Alternate Text
lcd.drawText(LCD_X_LINE_TITLE, LCD_Y_LINE_START+LCD_Y_LINE_HEIGHT, "Img:"..imgValue)
end
end
GUI_Display_Line_Value(i, line, value, i == ctx.SelLine, i == ctx.EditLine)
end
end -- if ~MENU
end -- if Line[i]~=nil
end -- for
if ctx.isEditing() then
GUI_Display_Edit_Buttons(ctx.MenuLines[ctx.EditLine])
end
end
end
else
-- Different Resolution.. Maybe just adjusting some of the constants will work, adjust it in DSM_Init??
-- LCD_X_LINE_TITLE, LCD_Y_LINE_START, etc
end
end
-------------------------------------------------------------------------------------------------------------
local function GUI_RotEncVal(dir) -- return encoder speed to inc or dec values
local inc = 0
local Speed = getRotEncSpeed()
if Speed == ROTENC_MIDSPEED then inc = (5 * dir)
elseif Speed == ROTENC_HIGHSPEED then inc = (15 * dir)
else inc = dir end
return inc
end
------------------------------------------------------------------------------------
-- Translate Tap/Touch of EDIT buttons to equivalent Key events
local function GUI_Translate_Edit_Buttons(button)
local event = EVT_TOUCH_TAP
local editInc = nil
if (button==EDIT_BUTTON.ESC) then -- ESC
event = EVT_VIRTUAL_EXIT
elseif (button==EDIT_BUTTON.DEFAULT) then -- Default
event = EVT_VIRTUAL_ENTER_LONG
elseif (button==EDIT_BUTTON.DEC_10) then -- -10
event = EVT_VIRTUAL_PREV
editInc = -10
elseif (button==EDIT_BUTTON.DEC_1) then -- -1
event = EVT_VIRTUAL_PREV
editInc = -1
elseif (button==EDIT_BUTTON.INC_1) then -- +1
event = EVT_VIRTUAL_NEXT
editInc = 1
elseif (button==EDIT_BUTTON.INC_10) then -- + 10
event = EVT_VIRTUAL_NEXT
editInc = 10
elseif (button==EDIT_BUTTON.OK) then -- OK
event = EVT_VIRTUAL_ENTER
else
end
return event, editInc
end
------------------------------------------------------------------------------------------------------------
-- Handle Events comming from the GUI
local function GUI_HandleEvent(event, touchState)
local ctx = DSM_Context
local menu = ctx.Menu
local menuLines = ctx.MenuLines
local editInc = nil
if (event == EVT_TOUCH_TAP and ctx.isEditing()) then -- Touch and Editing
local button = GUI_getTouchButton(touchState.x, touchState.y)
if (button) then
event, editInc = GUI_Translate_Edit_Buttons(button)
end
end
if (event == EVT_TOUCH_TAP or event == EVT_TOUCH_FIRST) and not ctx.isEditing() then -- Touch and NOT editing
if (DEBUG_ON) then dsmLib.LOG_write("%s: EVT_TOUCH_TAP %d,%d\n",dsmLib.phase2String(ctx.Phase),touchState.x, touchState.y) end
local button = GUI_getTouchButton(touchState.x, touchState.y)
if button then
-- Found a valid line
ctx.SelLine = button
ctx.Refresh_Display=true
if event == EVT_TOUCH_TAP then -- EVT_TOUCH_FIRST only move focus
event = EVT_VIRTUAL_ENTER
end
end
end
if event == EVT_VIRTUAL_EXIT then
ctx.Refresh_Display=true
if (DEBUG_ON) then dsmLib.LOG_write("%s: EVT_VIRTUAL_EXIT\n",dsmLib.phase2String(ctx.Phase)) end
if ctx.Phase == PHASE.RX_VERSION then
dsmLib.ReleaseConnection() -- Just Exit the Script
else
if ctx.isEditing() then -- Editing a Line, need to restore original value
ctx.MenuLines[ctx.EditLine].Val = originalValue
dsmLib.ChangePhase(PHASE.VALUE_CHANGE_END) -- Update + Validate value in RX
ctx.EditLine = nil -- Exit Edit Mode (By clearing the line editing)
else
dsmLib.ChangePhase(PHASE.EXIT)
end
end
return
end
if event == EVT_VIRTUAL_NEXT then
ctx.Refresh_Display=true
if (DEBUG_ON) then dsmLib.LOG_write("%s: EVT_VIRTUAL_NEXT\n",dsmLib.phase2String(ctx.Phase)) end
if ctx.isEditing() then -- Editing a Line, need to inc the value
local line=ctx.MenuLines[ctx.EditLine]
dsmLib.Value_Add(line, editInc or GUI_RotEncVal(1))
else -- not editing, move selected line to NEXT
dsmLib.MoveSelectionLine(1)
end
return
end
if event == EVT_VIRTUAL_PREV then
ctx.Refresh_Display=true
if (DEBUG_ON) then dsmLib.LOG_write("%s: EVT_VIRTUAL_PREV\n",dsmLib.phase2String(ctx.Phase)) end
if ctx.isEditing() then -- Editiing a line, need to dec the value
local line=ctx.MenuLines[ctx.EditLine]
dsmLib.Value_Add(line, editInc or GUI_RotEncVal(-1))
else -- not editing, move selected line to PREV
dsmLib.MoveSelectionLine(-1)
end
return
end
if event == EVT_VIRTUAL_ENTER_LONG then
ctx.Refresh_Display=true
if (DEBUG_ON) then dsmLib.LOG_write("%s: EVT_VIRTUAL_ENTER_LONG\n",dsmLib.phase2String(ctx.Phase)) end
if ctx.isEditing() then
-- reset the value to default
dsmLib.Value_Default(menuLines[ctx.EditLine]) -- Update value in RX if needed
end
return
end
if event == EVT_VIRTUAL_ENTER then
ctx.Refresh_Display=true
if (DEBUG_ON) then dsmLib.LOG_write("%s: EVT_VIRTUAL_ENTER, SelLine=%d\n",dsmLib.phase2String(ctx.Phase), ctx.SelLine) end
if ctx.SelLine == dsmLib.BACK_BUTTON then -- Back
dsmLib.GotoMenu(menu.BackId)
elseif ctx.SelLine == dsmLib.NEXT_BUTTON then -- Next
dsmLib.GotoMenu(menu.NextId)
elseif ctx.SelLine == dsmLib.PREV_BUTTON then -- Prev
dsmLib.GotoMenu(menu.PrevId)
elseif menuLines[ctx.SelLine].ValId ~= 0 then -- Menu or Value
if menuLines[ctx.SelLine].Type == LINE_TYPE.MENU then -- Navigate to Menu
if (SIMULATION_ON and menuLines[ctx.SelLine].ValId==0xFFFF) then
-- SPECIAL Simulation menu to Exit Simulation
GUI_SwitchSimulationOFF()
else
dsmLib.GotoMenu(menuLines[ctx.SelLine].ValId) -- ValId is the MenuId to navigate to
end
else -- Enter on a Value
if ctx.isEditing() then -- already editing a Line????
ctx.EditLine = nil -- Exit Edit Mode (By clearing the line editing)
dsmLib.ChangePhase(PHASE.VALUE_CHANGE_END) -- Request change the value on RX
else -- Edit the current value
ctx.EditLine = ctx.SelLine
originalValue = menuLines[ctx.SelLine].Val
end
end
end
end
end
------------------------------------------------------------------------------------------------------------
-- Init
local function DSM_Init()
dsmLib.Init(toolName) -- Initialize Library
return dsmLib.StartConnection()
end
------------------------------------------------------------------------------------------------------------
-- Main
local function DSM_Run(event,touchState)
local ctx = DSM_Context
if event == nil then
error("Cannot be run as a model script!")
dsmLib.LOG_close()
return 2
end
GUI_HandleEvent(event,touchState)
dsmLib.Send_Receive() -- Handle Send and Receive DSM Forward Programming Messages
local refreshInterval = REFRESH_GUI_MS
-- When using LCD BLINK attribute, we need faster refresh for BLINK to SHOW on LCD
if (ctx.Phase == PHASE.RX_VERSION) then -- Requesting RX Message Version usea BLINK?
ctx.Refresh_Display=true
refreshInterval = 20 -- 200ms
end
-- Refresh display only if needed and no faster than 300ms, utilize more CPU to speedup DSM communications
if (ctx.Refresh_Display and (getTime()-lastRefresh) > refreshInterval) then --300ms from last refresh
GUI_Display()
ctx.Refresh_Display=false
lastRefresh=getTime()
end
if ctx.Phase == PHASE.EXIT_DONE then
dsmLib.LOG_close()
return 2
else
return 0
end
end
return { init=DSM_Init, run=DSM_Run }

View File

@ -0,0 +1,393 @@
local toolName = "TNS|DSM Forward Prog v0.5 (OTX B&W) |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 SIMULATION_ON = true -- FALSE: use real communication to DSM RX (DEFAULT), TRUE: use a simulated version of RX
local DEBUG_ON = 1 -- 0=NO DEBUG, 1=HIGH LEVEL 2=LOW LEVEL (Debug logged into the /LOGS/dsm.log)
local DEBUG_ON_LCD = false -- Interactive Information on LCD of Menu data from RX
local dsmLib
if (SIMULATION_ON) then
-- library with SIMILATION VERSION. Works really well in Companion for GUI development
dsmLib = loadScript("/SCRIPTS/TOOLS/DSMLIB/DsmFwPrgSIMLib.lua")(DEBUG_ON)
else
dsmLib = loadScript("/SCRIPTS/TOOLS/DSMLIB/DsmFwPrgLib.lua")(DEBUG_ON)
end
local PHASE = dsmLib.PHASE
local LINE_TYPE = dsmLib.LINE_TYPE
local DISP_ATTR = dsmLib.DISP_ATTR
local DSM_Context = dsmLib.DSM_Context
local LCD_X_LINE_MENU = 10
local LCD_X_LINE_TITLE = 10
local LCD_X_LINE_VALUE = 230
local LCD_X_LINE_DEBUG = 390
local LCD_Y_MENU_TITLE = 20
local LCD_Y_LINE_START = LCD_Y_MENU_TITLE + 30
local LCD_Y_LINE_HEIGHT = (DEBUG_ON_LCD and 23) or 27 -- if DEBUG 23 else 27
local LCD_Y_LOWER_BUTTONS = LCD_Y_LINE_START + 7 * LCD_Y_LINE_HEIGHT
local lastRefresh=0 -- Last time the screen was refreshed
local REFRESH_GUI_MS = 500/10 -- 500ms.. Screen Refresh Rate.. to not use unneded CPU time (in 10ms units to be compatible with getTime())
local originalValue = nil
------------------------------------------------------------------------------------------------------------
local function GUI_SwitchSimulationOFF()
dsmLib.ReleaseConnection()
dsmLib.LOG_close()
SIMULATION_ON = false
dsmLib = loadScript("/SCRIPTS/TOOLS/DSMLIB/DsmFwPrgLib.lua")(DEBUG_ON)
DSM_Context = dsmLib.DSM_Context
dsmLib.Init(toolName) -- Initialize Library
dsmLib.StartConnection()
DSM_Context.Refresh_Display = true
end
local function openTx_lcd_sizeText(s)
return string.len(s)*5
end
local function GUI_Diplay_Button(x,y,w,h,text,selected)
local attr = (selected) and INVERS or 0 -- INVERS if line Selected
lcd.drawText(x+5,y+2, text, attr)
lcd.drawRectangle(x, y, w, h, LIGHTGREY)
end
local function GUI_Display_Menu(menu)
local ctx = DSM_Context
local w= LCD_W-100 -- usable Width for the Menu/Lines
-- Center Header
local tw = openTx_lcd_sizeText(menu.Text)
local x = w/2 - tw/2 -- Center of Screen - Center of Text
lcd.drawText(x,LCD_Y_MENU_TITLE,menu.Text,BOLD) -- orig MIDSIZE
-- Back
if menu.BackId ~= 0 then
GUI_Diplay_Button(437-5,LCD_Y_MENU_TITLE,47,25,"Back",ctx.SelLine == dsmLib.BACK_BUTTON)
end
-- Next ?
if menu.NextId ~= 0 then
GUI_Diplay_Button(437-5,LCD_Y_LOWER_BUTTONS,47,25,"Next",ctx.SelLine == dsmLib.NEXT_BUTTON)
end
-- Prev?
if menu.PrevId ~= 0 then
GUI_Diplay_Button(0,LCD_Y_LOWER_BUTTONS,47,25,"Prev",ctx.SelLine == dsmLib.PREV_BUTTON)
end
-- Debug into LCD
if (DEBUG_ON_LCD) then lcd.drawText(0,LCD_Y_MENU_TITLE,dsmLib.phase2String(ctx.Phase),SMLSIZE + BLUE) end -- Phase we are in
if (DEBUG_ON_LCD) then lcd.drawText(LCD_X_LINE_MENU,240,dsmLib.menu2String(menu),SMLSIZE + BLUE) end -- Menu Info
end
local function GUI_Display_Line_Menu(x,y,w,h,line,selected)
local attr = (selected) and INVERS or 0 -- INVERS if line Selected
local bold = 0
local text = line.Text
if dsmLib.isSelectableLine(line) then
-- Menu Line
text = text .. " -->" --OPENTX
else -- SubHeaders and plain text lines
bold = (dsmLib.isDisplayAttr(line.TextAttr,DISP_ATTR.BOLD) and BOLD) or 0
if dsmLib.isDisplayAttr(line.TextAttr,DISP_ATTR.RIGHT) then -- Right Align???
local tw = openTx_lcd_sizeText(line.Text)+4
x = LCD_X_LINE_VALUE - tw -- Right
elseif dsmLib.isDisplayAttr(line.TextAttr,DISP_ATTR.CENTER) then -- Center??
local tw = openTx_lcd_sizeText(line.Text)
x = x + (LCD_X_LINE_VALUE - LCD_X_LINE_MENU)/2 - tw/2 -- Center - 1/2 Text
end
end
lcd.drawText(x,y, text, attr + bold)
end
------------------------------------------------------------------------------------------------------------
local function GUI_Display_Line_Value(lineNum, line, value, selected, editing)
local bold = 0
local y = LCD_Y_LINE_START+(LCD_Y_LINE_HEIGHT*lineNum)
local x = LCD_X_LINE_TITLE
---------- NAME Part
local header = line.Text
-- ONLY do this for Flight Mode (Right Align or Centered)
if (dsmLib.isFlightModeText(line.TextId)) then
-- Display Header + Value together
header = header .. " " .. value
-- Flight mode display attributes
bold = (dsmLib.isDisplayAttr(line.TextAttr,DISP_ATTR.BOLD) and BOLD) or 0
if dsmLib.isDisplayAttr(line.TextAttr,DISP_ATTR.RIGHT) then -- Right Align
local tw = openTx_lcd_sizeText(header)+4
x = LCD_X_LINE_VALUE - tw -- Right
elseif dsmLib.isDisplayAttr(line.TextAttr,DISP_ATTR.CENTER) then -- Centered
local tw = openTx_lcd_sizeText(header)
x = x + (LCD_X_LINE_VALUE - LCD_X_LINE_TITLE)/2 - tw/2 -- Center - 1/2 Text
end
else
-- No Flight Mode, no effects here
header = header .. ":"
end
lcd.drawText(x, y, header, bold) -- display Line Header
--------- VALUE PART, Skip for Flight Mode since already show the value
if not dsmLib.isFlightModeText(line.TextId) then
local attrib = 0
value = value .. (line.Format or "") -- Append % if needed
if selected then
attrib = INVERS
if editing then -- blink editing entry
attrib = attrib + BLINK
value = "[ " .. value .. " ]"
end
end
lcd.drawText(LCD_X_LINE_VALUE,y, value, attrib) -- display value
end
if (DEBUG_ON_LCD) then lcd.drawText(LCD_X_LINE_DEBUG,y, line.MinMaxDebug or "", SMLSIZE) end -- display debug
end
------------------------------------------------------------------------------------------------------------
local function GUI_Display()
local ctx = DSM_Context
lcd.clear()
if LCD_W == 480 then
local header = "DSM Fwrd Programming "
if ctx.Phase ~= PHASE.RX_VERSION then
header = header .. "RX "..ctx.RX.Name.." v"..ctx.RX.Version
end
--Draw title
lcd.drawFilledRectangle(0, 0, LCD_W, 20, TITLE_BGCOLOR)
lcd.drawText(5, 0, header, MENU_TITLE_COLOR)
--Draw RX Menu
if ctx.Phase == PHASE.RX_VERSION then
lcd.drawText(LCD_X_LINE_TITLE,100,"No compatible DSM RX...", BLINK)
else
local menu = ctx.Menu
if menu.Text ~= nil then
GUI_Display_Menu(menu)
for i = 0, dsmLib.MAX_MENU_LINES do
local line = ctx.MenuLines[i]
if i == ctx.SelLine then
-- DEBUG: Display Selected Line info for ON SCREEN Debugging
if (DEBUG_ON_LCD) then lcd.drawText(LCD_X_LINE_TITLE,255,dsmLib.menuLine2String(line),SMLSIZE+BLUE) end
end
if line ~= nil and line.Type ~= 0 then
if line.Type == LINE_TYPE.MENU then
-- Menu Line
GUI_Display_Line_Menu(LCD_X_LINE_MENU,LCD_Y_LINE_START+(LCD_Y_LINE_HEIGHT*i), 350, LCD_Y_LINE_HEIGHT, line, i == ctx.SelLine)
else
-- list/value line
local value = line.Val
if line.Val ~= nil then
if dsmLib.isListLine(line) then -- for Lists of Strings, get the text
value = dsmLib.Get_Text(line.Val + line.TextStart) -- TextStart is the initial offset for text
local imgValue = dsmLib.Get_Text_Img(line.Val + line.TextStart) -- Complentary IMAGE for this value to Display??
if (imgValue) then -- Optional Image for a Value
--TODO: Pending feature.. create images and put bitmap instead of a message
--Display the image/Alternate Text
lcd.drawText(LCD_X_LINE_TITLE, LCD_Y_LINE_START+LCD_Y_LINE_HEIGHT, "Img:"..imgValue)
end
end
GUI_Display_Line_Value(i, line, value, i == ctx.SelLine, i == ctx.EditLine)
end
end -- if ~MENU
end -- if Line[i]~=nil
end -- for
end
end
else
-- Different Resolution
end
end
-------------------------------------------------------------------------------------------------------------
local function GUI_RotEncVal(dir) -- return encoder speed to inc or dec values
local inc = 0
local Speed = getRotEncSpeed()
if Speed == ROTENC_MIDSPEED then
inc = (5 * dir)
elseif Speed == ROTENC_HIGHSPEED then
inc = (15 * dir)
else
inc = dir
end
return inc
end
------------------------------------------------------------------------------------------------------------
local function GUI_HandleEvent(event, touchState)
local ctx = DSM_Context
local menu = ctx.Menu
local menuLines = ctx.MenuLines
if event == EVT_VIRTUAL_EXIT then
ctx.Refresh_Display=true
if (DEBUG_ON) then dsmLib.LOG_write("%s: EVT_VIRTUAL_EXIT\n",dsmLib.phase2String(ctx.Phase)) end
if ctx.Phase == PHASE.RX_VERSION then
dsmLib.ReleaseConnection()
else
if ctx.isEditing() then -- Editing a Line, need to restore original value
ctx.MenuLines[ctx.EditLine].Val = originalValue
dsmLib.ChangePhase(PHASE.VALUE_CHANGE_END) -- Update+Validate value in RX
ctx.EditLine = nil -- Exit Edit Mode (By clearing the line editing)
else
dsmLib.ChangePhase(PHASE.EXIT)
end
end
return
end
if event == EVT_VIRTUAL_NEXT then
ctx.Refresh_Display=true
if (DEBUG_ON) then dsmLib.LOG_write("%s: EVT_VIRTUAL_NEXT\n",dsmLib.phase2String(ctx.Phase)) end
if ctx.isEditing() then -- Editing a Line, need to inc the value
local line=ctx.MenuLines[ctx.EditLine]
dsmLib.Value_Add(line, GUI_RotEncVal(1))
else -- not editing, move selected line to NEXT
dsmLib.MoveSelectionLine(1)
end
return
end
if event == EVT_VIRTUAL_PREV then
ctx.Refresh_Display=true
if (DEBUG_ON) then dsmLib.LOG_write("%s: EVT_VIRTUAL_PREV\n",dsmLib.phase2String(ctx.Phase)) end
if ctx.isEditing() then -- Editiing a line, need to dec the value
local line=ctx.MenuLines[ctx.EditLine]
dsmLib.Value_Add(line, GUI_RotEncVal(-1))
else -- not editing, move selected line to PREV
dsmLib.MoveSelectionLine(-1)
end
return
end
if event == EVT_VIRTUAL_ENTER_LONG then
ctx.Refresh_Display=true
if (DEBUG_ON) then dsmLib.LOG_write("%s: EVT_VIRTUAL_ENTER_LONG\n",dsmLib.phase2String(ctx.Phase)) end
if ctx.isEditing() then
-- reset the value to default
dsmLib.Value_Default( menuLines[ctx.EditLine]) -- Update RX value as needed
end
return
end
if event == EVT_VIRTUAL_ENTER then
ctx.Refresh_Display=true
if (DEBUG_ON) then dsmLib.LOG_write("%s: EVT_VIRTUAL_ENTER\n",dsmLib.phase2String(ctx.Phase)) end
if ctx.SelLine == dsmLib.BACK_BUTTON then -- Back
dsmLib.GotoMenu(menu.BackId)
elseif ctx.SelLine == dsmLib.NEXT_BUTTON then -- Next
dsmLib.GotoMenu(menu.NextId)
elseif ctx.SelLine == dsmLib.PREV_BUTTON then -- Prev
dsmLib.GotoMenu(menu.PrevId)
elseif menuLines[ctx.SelLine].ValId ~= 0 then
if menuLines[ctx.SelLine].Type == LINE_TYPE.MENU then -- Next menu exist
if (SIMULATION_ON and menuLines[ctx.SelLine].ValId==0xFFFF) then
-- SPECIAL Simulation menu to Exit Simulation and
-- comunicate with Real RX
GUI_SwitchSimulationOFF()
else
dsmLib.GotoMenu(menuLines[ctx.SelLine].ValId) -- ValId is the MenuId to navigate to
end
else
-- Editing a Line????
if ctx.isEditing() then
-- Change the Value and exit edit
ctx.EditLine = nil
dsmLib.ChangePhase(PHASE.VALUE_CHANGE_END)
else
-- enter Edit the current line
ctx.EditLine = ctx.SelLine
originalValue = menuLines[ctx.SelLine].Val
end
end
end
end
end
------------------------------------------------------------------------------------------------------------
-- Init
local function DSM_Init()
dsmLib.Init(toolName) -- Initialize Library
return dsmLib.StartConnection()
end
------------------------------------------------------------------------------------------------------------
-- Main
local function DSM_Run(event)
local ctx = DSM_Context
if event == nil then
error("Cannot be run as a model script!")
dsmLib.LOG_close()
return 2
end
GUI_HandleEvent(event)
dsmLib.Send_Receive() -- Handle Send and Receive DSM Forward Programming Messages
local refreshInterval = REFRESH_GUI_MS
-- When using LCD BLINK attribute, we need faster refresh for BLINK to SHOW on LCD
if (ctx.EditLine or (ctx.Phase == PHASE.RX_VERSION)) then -- Editing or Requesting RX Version?
ctx.Refresh_Display=true
refreshInterval = 20 -- 200ms
end
-- Refresh display only if needed and no faster than 500ms, utilize more CPU to speedup DSM communications
if (ctx.Refresh_Display and (getTime()-lastRefresh) > refreshInterval) then --300ms from last refresh
GUI_Display()
ctx.Refresh_Display=false
lastRefresh=getTime()
end
if ctx.Phase == PHASE.EXIT_DONE then
dsmLib.LOG_close()
return 2
else
return 0
end
end
return { init=DSM_Init, run=DSM_Run }

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,45 @@
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, etc.
Code is based on the code/work by: Pascal Langer (Author of the Multi-Module)
Rewrite/Enhancements By: Francisco Arzu
Deployment:
/SCRIPTS/TOOLS/Dsm FwdPrg_OTX_05.lua -- OpenTX version, no touch (black/white radios)
/SCRIPTS/TOOLS/Dsm FwdPrg_ETX_05.lua -- EdgeTX version, touch
/SCRIPTS/TOOLS/DSMLIB/ -- Libraries (ALL CAPITALS) to hide them from the Tools menu
/SCRIPTS/TOOLS/DSMLIB/DsmFwPrgLib.lub -- DSM Protocol Message and Menu engine
/SCRIPTS/TOOLS/DSMLIB/DsmFwPrgSIMLib.lub -- Simulation of AR631 for GUI Development in Companion
/LOGS/dsm_log.txt -- Readable log of the last RX session, usefull for debuging new RX
Version 0.05
- Make the code more readable and understadable
- Separate the DSM Forwards Programing logic from the GUI
- Log the comunnication with the RX on a /LOGS/dsm_log.txt to allow to debug it easier
and see the exchange of data between the RX/TX
- Created a black/white version with only Key/Roller Inputs (OTX)
- Created a nicer GUI for EdgeTX touchscreen color Radios
Known Problems:
1. When trying to Factory Reset an RX, even that navigation to menus seems OK, it did not reset.
Maybe another message needs to be sent to RX when reaching that page
2. When initially setting a new RX, there is a point where a menu navigates to MenuID=0x0001, this seems like a
special Save/Restart type of menu.. but it does not reset the RX. maybe another meesage needs to be send
3. Some Menu List line types (LINE_TYPE.LIST_MENU1), the range (min/max) seems to be incorrect, but cannot see in the data how to fix it
Some of the valid values are not even sequential, very spread apart. There has to be a list of valid options somewhere (in RX or config for each field).
Currenly adding valid values inside the scrip to override the RX
4. The RX return unknow lines when requesting the Lines for a menu. Realy don't understand what they are for.
in some menus, seems to stay stuck in the same return line or no response to the request, making the RX reset/close the connection.
Did a fix to stop requesting the same menu line if the response is the same. This gives an empty menu, but does not reset the connection.
Version 0.02
Original Version from Pascal Langer