mirror of
https://github.com/pascallanger/DIY-Multiprotocol-TX-Module.git
synced 2025-02-04 16:38:12 +00:00
b2f8f482bb
* #751 DSM Enhancements #751 DSM Forward Programming Enhancements (New GUI, etc) * Make both work on EdgeTx and OpenTX * #751 Turn OFF simulation by default Distribution code with RX simulation OFF Simulation should be only for Development * #751 Update Readme Documentation Updated the Readme.txt documentation and removed compiled luac file that was check in by mistake * #751 Fix problems With Reset RX 1. Fix problem when trying to Factory Reset. Enter Bind Mode. Save backup, Restore Backup 2. Found a way to advance on the Gyro initial Setup menus.. a bit of a hack, but works. 3. Handle RX resets properly. It needed after initial setup * #751 Cosmetic and Show Orientation Images #751 1. Fix problems when text contradictions between Menu/Line Headers and List Values 2. Show Images of RX orientations 3. Able to Hack getting into Initial Setup and other menus who was failing before 4. Custumize the way Flight Mode reports the Value on Screen * #751 add check for required libraries Add check that the required files in DSMLIB exist * #751 Write documentation about the protocol so that we don't forget later what we know, and enable others to understand the logs and maybe help solve problems. * #766 Change the way to detect that the files exist. now works on both ETX and OTX * #766 Strange Flickering in OTX Strange Flickering happening on OTX. Refreshing the screen on every cycle fixed the problem * #766 Change way of dectecting EdgeTX Change way of detecting OTX in multiple versions: OTX 2.3.14 and 2.3.15 * #766 make editable Gain Values Gains and other settings should be editable even when they are VALUE_NOCHANGING. Flight Mode is an exception that is handled properly. Right align numbers. * #766 More enhacements Added AR630 Make numbers right justified Cleanup some log messages and line types. Updated DSM FWD prog documentation * #751 more cosmetic things 1. Added AR10360T, 2. Simplify way to configured the hack for more receivers. 3. Change some texts on menus to march spektrum 4. Background color in Spektrum theme to match * #751 A few final changes 1. Update channel names to include channel number. i.e: Ch5 (Gear) 2, Fix flight mode display for Heli Receiver 3. i think the unknown lines are to request info about the TX settings * #751 1. Added Warning Screen 2. Correct handling of Unknown lines in Gyro Settings->Initial Setup * #751 New v0.51 version. - Added new menus to configure Model/Wing type. Without it, the initial setup will not work properly. * #751 More fixes on mixers and servo reverse -- Fix problem reversing servos when using vtail/delta mix -- Properly detect ch order of multimodule * #751 Updated channel naming and docs Updated readme documentation Consistent naming of Ch across the code. * #751 Fix message displaying data path * #751 More improvements 1. Much easier to select channels > Ch6 for FMode, Gain and Panic channels 2. B&W version for smaller screens (128x64).. Memory footprint still a problem. 3. Fix a lot typos/misspell/grammar in the documentation * Create DSM_AR636_TextGen.lua Script to show Telemetry TextGen screens for AR636 Receiver. Really useful for BLADE helis using the AR636 Could replace dsmPID.lua Still needs to be ported to smaller screens. * #751 Enhancements for Lua Script tools Enhancements * Delete DSM_AR636_TextGen.lua The TextGen functionality is included in DSM_AR636_Tel.lua. No longer needed * Version 0.54 1. Fix problem with "Attitude Trim" Menu 2. New "MINimalistic" version for radios with very low memory 3. Externalized menu messages shared by all versions. the idea is to allow to translate it into other languages. 4. Correction of TextGen tools to work on black&white radios (some Lua functional differences). TextGen will be working on EdgeTx 2,8.3 --------- Co-authored-by: pascallanger <pascal_langer@yahoo.fr>
933 lines
31 KiB
Lua
933 lines
31 KiB
Lua
local toolName = "TNS|DSM Frwd Prog v0.54-beta (MIN)|TNE"
|
|
|
|
--local ModelParam = ...
|
|
|
|
---- #########################################################################
|
|
---- # #
|
|
---- # 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. #
|
|
---- # #
|
|
---- #########################################################################
|
|
|
|
|
|
--###############################################################################
|
|
-- 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 VERSION = "v0.54-MIN"
|
|
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"
|
|
|
|
-- Phase
|
|
local PH_RX_VER, PH_TITLE, PH_TX_INFO, PH_LINES, PH_VALUES = 1, 2, 3, 4, 5
|
|
local PH_VAL_CHANGING, PH_VAL_WAIT, PH_VAL_CHNG_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, LT_LIST_VALIDATE, LT_LIST_TOG = 0x6C, 0x0C, 0x4C
|
|
local LT_VALUE_NOCHANGING = 0x60
|
|
local LT_VALUE_PERCENT, LT_VALUE_DEGREES = 0xC0, 0xE0
|
|
|
|
local Phase = PH_RX_VER
|
|
local Waiting_RX = 0
|
|
|
|
local Text = {}
|
|
local List_Text = {}
|
|
local List_Text_Img = {}
|
|
local Flight_Mode = { [0] = "Flight Mode" }
|
|
local RxName = {}
|
|
|
|
local InactivityTime = 0
|
|
local Value_Change_Step = 0
|
|
local TX_Info_Step = 0
|
|
local TX_Info_Type = 0
|
|
local originalValue = 0
|
|
|
|
local ctx = {
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
local Menu = { MenuId = 0, Text = "", TextId = 0, PrevId = 0, NextId = 0, BackId = 0 }
|
|
local MenuLines = {}
|
|
local RX = { Name = "", Version = "" }
|
|
|
|
local logFile = nil
|
|
local logCount = 0
|
|
|
|
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 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
|
|
|
|
---------------- 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 Get_Text(index)
|
|
local out = Text[index] or string.format("Unknown_%X", index)
|
|
if (index >= 0x8000) then
|
|
out = Flight_Mode[0]
|
|
end
|
|
return out
|
|
end
|
|
|
|
local function Get_Text_Value(index)
|
|
local out = List_Text[index] or Get_Text(index)
|
|
return out
|
|
end
|
|
------------------------------------------------------------------------------------------------------------
|
|
local function Get_RxName(index)
|
|
local out = RxName[index] or string.format("Unknown_%X", index)
|
|
return out
|
|
end
|
|
------------------------------------------------------------------------------------------------------------
|
|
local function DSM_Release()
|
|
multiBuffer(0, 0)
|
|
Phase = PH_EXIT_DONE
|
|
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
|
|
Waiting_RX = 0
|
|
end
|
|
|
|
local function Value_Add(dir)
|
|
local line = MenuLines[ctx.SelLine]
|
|
|
|
Speed = getRotEncSpeed()
|
|
if Speed == ROTENC_MIDSPEED then
|
|
line.Val = line.Val + (5 * dir)
|
|
elseif Speed == ROTENC_HIGHSPEED then
|
|
line.Val = line.Val + (15 * dir)
|
|
else
|
|
line.Val = line.Val + dir
|
|
end
|
|
|
|
if line.Val > line.Max then
|
|
line.Val = line.Max
|
|
elseif line.Val < line.Min then
|
|
line.Val = line.Min
|
|
end
|
|
|
|
ChangePhase(PH_VAL_CHANGING)
|
|
Value_Change_Step = 0
|
|
end
|
|
------------------------------------------------------------------------------------------------------------
|
|
|
|
local function GotoMenu(menuId, lastSelectedLine)
|
|
Menu.MenuId = menuId
|
|
ctx.SelLine = lastSelectedLine
|
|
-- Request to load the menu Again
|
|
ChangePhase(PH_TITLE)
|
|
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 or line.Type == LT_LIST_VALIDATE or line.Type == LT_LIST_TOG
|
|
end
|
|
|
|
local function DSM_Menu(event)
|
|
if event == EVT_VIRTUAL_EXIT then
|
|
if Phase == PH_RX_VER then
|
|
DSM_Release() -- Exit program
|
|
else
|
|
if ctx.EditLine ~= nil then -- Editing a Line, need to restore original value
|
|
MenuLines[ctx.EditLine].Val = originalValue
|
|
event = EVT_VIRTUAL_ENTER
|
|
else
|
|
ChangePhase(PH_EXIT_REQ)
|
|
end
|
|
end
|
|
end
|
|
|
|
if Phase == PH_RX_VER then return end -- nothing else to do
|
|
|
|
if event == EVT_VIRTUAL_NEXT then
|
|
if ctx.EditLine ~= nil then
|
|
Value_Add(1)
|
|
else
|
|
-- not changing a value
|
|
|
|
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
|
|
elseif event == EVT_VIRTUAL_PREV then
|
|
if ctx.EditLine ~= nil 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
|
|
elseif event == EVT_VIRTUAL_ENTER_LONG then
|
|
if ctx.EditLine ~= nil then
|
|
-- reset the value to default
|
|
--if MenuLines[ctx.SelLine].Type ~= LIST_MENU_NOCHANGING then
|
|
MenuLines[ctx.SelLine].Val = MenuLines[ctx.SelLine].Def
|
|
ChangePhase(PH_VAL_CHANGING)
|
|
Value_Change_Step = 0
|
|
--end
|
|
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 ctx.EditLine ~= nil then
|
|
ctx.EditLine = nil -- Done Editting
|
|
Value_Change_Step = 0
|
|
ChangePhase(PH_VAL_CHNG_END)
|
|
else -- Start Editing
|
|
ctx.EditLine = ctx.SelLine
|
|
originalValue = MenuLines[ctx.SelLine].Val
|
|
ChangePhase(PH_VAL_WAIT)
|
|
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]
|
|
DSM_Send(0x20, 0x06, portNo, portNo, info[0],info[1])
|
|
LOG_write("DSM_TxInfo_20(Port=#%d, Port Use)\n", portNo)
|
|
|
|
if (TX_Info_Type == 0x1F) then
|
|
TX_Info_Step = 1
|
|
elseif (TX_Info_Type == 0x00) then
|
|
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("DSM_TxInfo_23(Port=#%d,ServoTravel(L=%d - R=%d))\n", portNo,leftTravel,rightTravel)
|
|
|
|
TX_Info_Step = 2
|
|
elseif (TX_Info_Step == 2) then
|
|
local data = {[0]= -- Start at 0
|
|
{[0]= 0x0, 0x00, 0x07, 0xFF }, -- Ch1 Thr: 0 00 07 FF Subtrim ??
|
|
{[0]= 0x0, 0x8E, 0x07, 0x72 }, -- Ch2 Ail: 0 8E 07 72 Subtrim 0
|
|
{[0]= 0x0, 0x8E, 0x07, 0x72 }, -- Ch3 Elev: 0 8E 07 72 Subtrim 0
|
|
{[0]= 0x0, 0x8E, 0x07, 0x72 }, -- Ch4 Rud: 0 8E 07 72 Subtrim 0
|
|
{[0]= 0x0, 0x8E, 0x07, 0x72 }, -- Ch5 Gear: 0 8E 07 72 Subtrim 0
|
|
{[0]= 0x0, 0x8E, 0x07, 0x72 }, -- Ch6 Aux1: 0 8E 07 72 Subtrim 0
|
|
{[0]= 0x0, 0x8E, 0x07, 0x72 }, -- Ch7 Aux2: 0 8E 07 72 Subtrim 0
|
|
{[0]= 0x0, 0x8E, 0x07, 0x72 }, -- Ch8 Aux3: 0 8E 07 72 Subtrim 0
|
|
{[0]= 0x0, 0x8E, 0x07, 0x72 }, -- Ch9 Aux4: 0 8E 07 72 Subtrim 0
|
|
{[0]= 0x0, 0x8E, 0x07, 0x72 }, -- Ch10 Aux5: 0 8E 07 72 Subtrim 0
|
|
}
|
|
local info = data[portNo]
|
|
local b1,b2,b3,b4 = info[0], info[1], info[2], info[3]
|
|
|
|
DSM_Send(0x21, 0x06, b1,b2,b3,b4) -- Port is not send anywhere, since the previous 0x20 type message have it.
|
|
LOG_write("DSM_TxInfo_21(Port=#%d, SubTrim)\n", portNo)
|
|
|
|
if (TX_Info_Type == 0x00) then
|
|
TX_Info_Step = 5 -- End Step
|
|
else
|
|
TX_Info_Step = 3
|
|
end
|
|
elseif (TX_Info_Step == 3) then
|
|
LOG_write("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("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("DSM_TxInfo_22(Port=#%d, END of Data)\n", portNo)
|
|
DSM_Send(0x22, 0x04, 0x00, 0x00)
|
|
TX_Info_Step = 0 -- Done!!
|
|
end
|
|
|
|
if (TX_Info_Step > 0) then
|
|
Waiting_RX = 0 -- keep Transmitig
|
|
end
|
|
end
|
|
|
|
local function DSM_SendRequest()
|
|
--LOG_write("DSM_SendRequest Phase=%d\n",Phase)
|
|
-- Need to send a request
|
|
if Phase == PH_RX_VER then -- request RX version
|
|
DSM_Send(0x11, 0x06, 0x00, 0x14, 0x00, 0x00)
|
|
LOG_write("GetVersion()\n")
|
|
elseif Phase == PH_WAIT_CMD then -- keep connection open
|
|
DSM_Send(0x00, 0x04, 0x00, 0x00)
|
|
elseif Phase == PH_TITLE then -- request menu title
|
|
local menuId = Menu.MenuId
|
|
if menuId == 0 then
|
|
DSM_Send(0x12, 0x06, 0x00, 0x14, 0x00, 0x00) -- first menu only
|
|
else
|
|
DSM_Send(0x16, 0x06, int16_MSB(menuId), int16_LSB(menuId), 0x00, ctx.SelLine)
|
|
if (menuId == 0x0001) then -- Executed Save&Reset menu
|
|
Phase = PH_RX_VER
|
|
ctx.isReset = true
|
|
end
|
|
end
|
|
LOG_write("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
|
|
local menuId = Menu.MenuId
|
|
if ctx.CurLine == -1 then
|
|
DSM_Send(0x13, 0x04, int16_MSB(menuId), int16_LSB(menuId)) -- GetFirstLine
|
|
else
|
|
DSM_Send(0x14, 0x06, int16_MSB(menuId), int16_LSB(menuId), 0x00, ctx.CurLine) -- line X
|
|
end
|
|
LOG_write("GetLines(LastLine=%d)\n", ctx.CurLine)
|
|
elseif Phase == PH_VALUES then -- request menu values
|
|
local menuId = Menu.MenuId
|
|
local valId = MenuLines[ctx.CurLine].ValId
|
|
DSM_Send(0x15, 0x06,
|
|
int16_MSB(menuId), int16_LSB(menuId),
|
|
int16_MSB(valId), int16_LSB(valId))
|
|
LOG_write("GetValues(LastVId=0x%04X)\n", valId)
|
|
elseif Phase == PH_VAL_CHANGING then -- send new value: Two steps, Update & Validate
|
|
local line = MenuLines[ctx.SelLine]
|
|
local valId = line.ValId
|
|
if Value_Change_Step == 0 then
|
|
local value = sInt16ToDsm(line.Val)
|
|
DSM_Send(0x18, 0x06,
|
|
int16_MSB(valId), int16_LSB(valId),
|
|
int16_MSB(value), int16_LSB(value)) -- send current values
|
|
LOG_write("ChangeValue(VId=0x%04X,Val=%d)\n", valId, value)
|
|
|
|
if line.Type == LT_LIST_VALIDATE then -- Incremental Validation??
|
|
Value_Change_Step = 1
|
|
Waiting_RX = 0 -- Do SEND in the next step
|
|
end
|
|
else
|
|
-- Validate Value
|
|
DSM_Send(0x19, 0x04, int16_MSB(valId), int16_LSB(valId))
|
|
Value_Change_Step = 0
|
|
Phase = PH_VAL_WAIT
|
|
LOG_write("ValidateValue(VId=0x%04X)\n", valId)
|
|
end
|
|
elseif Phase == PH_VAL_CHNG_END then
|
|
DSM_Send(0x1B, 0x04, 0x00, int16_LSB(ctx.SelLine))
|
|
Value_Change_Step = 0
|
|
Phase = PH_WAIT_CMD
|
|
LOG_write("ValueChangeEnd(L=%d)\n", ctx.SelLine)
|
|
elseif Phase == PH_VAL_WAIT then
|
|
-- Value Changing Wait
|
|
DSM_Send(0x1A, 0x04, 0x00, int16_LSB(ctx.SelLine))
|
|
LOG_write("ValueChangeWait(L=%d)\n", ctx.SelLine)
|
|
elseif Phase == PH_EXIT_REQ then -- EXIT Request
|
|
DSM_Send(0x1F, 0x02, 0xAA)
|
|
end
|
|
end
|
|
|
|
local function DSM_ProcessResponse()
|
|
local cmd = multiBuffer(11)
|
|
-- LOG_write("DSM_ProcessResponse BEGIN: Cmd=%x\n",cmd)
|
|
if cmd == 0x01 then -- read version
|
|
RX.Name = Get_RxName(multiBuffer(13))
|
|
RX.Version = multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16)
|
|
--ctx.isReset = false -- no longer resetting
|
|
Menu.MenuId = 0
|
|
Phase = PH_TITLE
|
|
LOG_write("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] = { MenuId = 0, Type = 0, TextId = 0, ValId = 0, Min = 0, Max = 0, Def = 0, Val = nil, Unit, Step }
|
|
end
|
|
ctx.CurLine = -1
|
|
ctx.SelLine = -1 -- highlight Back
|
|
|
|
LOG_write("Menu: Mid=0x%04X \"%s\"\n", menu.MenuId, menu.Text)
|
|
|
|
if (menu.MenuId == 0x0001) then -- Still in RESETTING MENU???
|
|
--menu.MenuId = 0
|
|
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))
|
|
|
|
-- 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))
|
|
|
|
if line.Type == LT_MENU then
|
|
-- nothing to do on menu entries
|
|
elseif isListLine(line) then
|
|
line.Val = nil --line.Def - line.Min -- use default value not sure if needed
|
|
line.Def = line.Min -- pointer to the start of the list in Text
|
|
line.Max = line.Max - line.Min -- 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("Line: #%d Vid=0x%04X T=0x%02X \"%s\"\n", i, line.ValId, type, line.Text)
|
|
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
|
|
|
|
local valueTxt = value
|
|
if isListLine(line) then
|
|
valueTxt = Get_Text_Value(line.Def + value) .. " [" .. value .. "]"
|
|
end
|
|
|
|
LOG_write("Update Value: #%d VId=0x%04X Value=%s\n", i, valId, valueTxt)
|
|
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("TXInfoReq: Port=%d T=0x%02X\n", ctx.CurLine, TX_Info_Type)
|
|
elseif cmd == 0xA7 then -- answer to EXIT command
|
|
DSM_Release()
|
|
elseif cmd == 0x00 and Phase == PH_VAL_CHANGING then
|
|
Phase = PH_VAL_WAIT
|
|
end
|
|
|
|
--LOG_write("DSM_ProcessResponse END: Cmd=%x\n",cmd)
|
|
return cmd
|
|
end
|
|
|
|
|
|
local function DSM_Send_Receive()
|
|
if Waiting_RX == 0 then
|
|
Waiting_RX = 1
|
|
DSM_SendRequest()
|
|
multiBuffer(10, 0x00);
|
|
InactivityTime = getTime() + 200 -- Reset Inactivity timeout
|
|
-- -- -- -- -- -- -- -- -- -- -- -- receive part -- -- -- -- -- -- -- -- -- -- -- -- --
|
|
elseif multiBuffer(10) == 0x09 then
|
|
local cmd = DSM_ProcessResponse()
|
|
-- Data processed
|
|
multiBuffer(10, 0x00)
|
|
if (cmd > 0x00) then -- Any non NULL response
|
|
-- Only change to SEND mode if we received a valid response (Ignore NULL Responses, that are really heartbeat i most cases)
|
|
Waiting_RX = 0
|
|
InactivityTime = getTime() + 200 -- Reset Inactivity timeout
|
|
end
|
|
else -- No Send or Receive,
|
|
-- Check if enouth time has passed from last transmit/receive activity
|
|
if getTime() > InactivityTime then
|
|
InactivityTime = getTime() + 200
|
|
Waiting_RX = 0 -- Switch to Send mode to send heartbeat
|
|
|
|
if Phase == PH_EXIT_REQ then
|
|
DSM_Release()
|
|
end
|
|
|
|
if Phase ~= PH_RX_VER and Phase ~= PH_VAL_WAIT then
|
|
Phase = 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(0, 3 * LCD_Y_LINE_HEIGHT, Get_Text(msgId), BLINK)
|
|
return
|
|
end
|
|
|
|
-- display Program version or RX version
|
|
local msg = RX.Name .. " v" .. RX.Version
|
|
if (ver_rx_count < 100) then
|
|
msg = RX.Name .. " v" .. RX.Version
|
|
ver_rx_count = ver_rx_count + 1
|
|
else
|
|
msg = "Frwd Prog "..VERSION
|
|
ver_rx_count = ver_rx_count + 1
|
|
if (ver_rx_count > 200) then ver_rx_count=0 end
|
|
end
|
|
lcd.drawText(30, 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)
|
|
|
|
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 ~= nil and line.Type ~= 0 then
|
|
local heading = Get_Text(line.TextId)
|
|
|
|
if (line.TextId >= 0x8000) then -- Flight mode
|
|
heading = " " .. Flight_Mode[0] .. " "
|
|
if (line.Val==nil) then heading = heading .. "--" else heading = heading .. ((line.Val or 0) + 1) end
|
|
else
|
|
local text = "-"
|
|
if line.Type ~= LT_MENU then -- list/value
|
|
if line.Val ~= nil then
|
|
if isListLine(line) then
|
|
local textId = line.Val + line.Def
|
|
text = Get_Text_Value(textId)
|
|
|
|
local imgDesc = List_Text_Img[textId]
|
|
|
|
if (imgDesc and i == ctx.SelLine) then -- Optional Image and Msg for selected value
|
|
showBitmap(0, 20, imgDesc)
|
|
end
|
|
elseif (line.Type == LT_VALUE_PERCENT) then
|
|
text = line.Val .. " %"
|
|
elseif (line.Type == LT_VALUE_DEGREES) then
|
|
text = line.Val .. " @"
|
|
else
|
|
text = line.Val
|
|
end
|
|
end -- if is Value
|
|
|
|
if (ctx.EditLine == i) then -- Editing a Line
|
|
attrib = BLINK + INVERS + TEXT_ATTR
|
|
end
|
|
lcd.drawText(LCD_X_MAX, y, text, attrib + RIGHT) -- display value
|
|
attrib = TEXT_ATTR
|
|
end
|
|
end -- Flight mode
|
|
lcd.drawText(0, 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(0, LCD_Y_LOWER_BUTTONS, "Prev", ctx.SelLine == 8)
|
|
end
|
|
|
|
end
|
|
|
|
|
|
local function load_msg_from_file(fileName, mem, Text, List_Text, List_Text_Img, RxName, Flight_Mode)
|
|
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
|
|
|
|
|
|
local function clean_msg(Text, Flight_Mode)
|
|
local function clean_line(c)
|
|
if (c==nil) then return c end
|
|
local pos
|
|
c, pos = string.gsub(c, "/b$", "")
|
|
c, pos = string.gsub(c, "/c$", "")
|
|
c, pos = string.gsub(c, "/r$", "")
|
|
c, pos = string.gsub(c, "/p$", "")
|
|
c, pos = string.gsub(c, "/m$", "")
|
|
return c
|
|
end
|
|
|
|
-- Clean the line of special markers that are only used in color vesion
|
|
for i = 0, 0x0300 do
|
|
Text[i] = clean_line(Text[i])
|
|
collectgarbage("collect")
|
|
end
|
|
|
|
for i = 0, #Flight_Mode do
|
|
-- Clean the line of special markers that are only used in color vesion
|
|
Flight_Mode[i] = clean_line(Flight_Mode[i])
|
|
end
|
|
end
|
|
|
|
|
|
local function DSM_Init_Model()
|
|
MODEL.DSM_ChannelInfo= {[0]= -- Start array at position 0
|
|
{[0]= 0x00, 0x40}, -- Ch1 Thr (0x40)
|
|
{[0]= 0x00, 0x01}, -- Ch2 Ail (0x01)
|
|
{[0]= 0x00, 0x02}, -- Ch2 ElE (0x02)
|
|
{[0]= 0x00, 0x04}, -- Ch4 Rud (0x04)
|
|
{[0]= 0x00, 0x00}, -- Ch5 Gear (0x00)
|
|
{[0]= 0x00, 0x00}, -- Ch6 Aux1 (0x00)
|
|
{[0]= 0x00, 0x00}, -- Ch7 Aux2 (0x00)
|
|
{[0]= 0x00, 0x00}, -- Ch8 Aux3 (0x00)
|
|
{[0]= 0x00, 0x00}, -- Ch9 Aux4 (0x00)
|
|
{[0]= 0x00, 0x00} -- Ch10 Aux5 (0x00)
|
|
}
|
|
|
|
MODEL.modelOutputChannel = {[0]=
|
|
{min=1000, max=1000}, -- Ch1
|
|
{min=1000, max=1000}, -- Ch2
|
|
{min=1000, max=1000}, -- Ch3
|
|
{min=1000, max=1000}, -- Ch4
|
|
{min=1000, max=1000}, -- Ch5
|
|
{min=1000, max=1000}, -- Ch6
|
|
{min=1000, max=1000}, -- Ch7
|
|
{min=1000, max=1000}, -- Ch8
|
|
{min=1000, max=1000}, -- Ch9
|
|
{min=1000, max=1000} -- Ch10
|
|
}
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
-- Init
|
|
local function DSM_Init()
|
|
LOG_open()
|
|
LOG_write("-------- NEW SESSION --------------------\n")
|
|
|
|
DSM_Init_Model()
|
|
|
|
--[[
|
|
if (ModelParam~=nil) then
|
|
LOG_write("Got MODEL PARAMETER... copying\n")
|
|
MODEL.DSM_ChannelInfo = ModelParam.DSM_ChannelInfo
|
|
else
|
|
LOG_write("NO-PARMETER --- Create DEFAULT")
|
|
end
|
|
--]]
|
|
|
|
collectgarbage("collect")
|
|
LOG_write("Mem before msg =%d\n",collectgarbage("count"))
|
|
load_msg_from_file(MSG_FILE, 10, Text, List_Text, List_Text_Img, RxName, Flight_Mode)
|
|
collectgarbage("collect")
|
|
LOG_write("Mem after msg =%d\n",collectgarbage("count"))
|
|
load_msg_from_file(MSG_FILE_MIN, 4, Text, List_Text, List_Text_Img, RxName, Flight_Mode)
|
|
collectgarbage("collect")
|
|
LOG_write("Mem after msg2 =%d\n",collectgarbage("count"))
|
|
clean_msg(Text,Flight_Mode)
|
|
collectgarbage("collect")
|
|
|
|
--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
|
|
|
|
--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'))
|
|
|
|
|
|
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
|
|
|
|
|
|
end
|
|
------------------------------------------------------------------------------------------------------------
|
|
-- Main
|
|
local function DSM_Run(event)
|
|
if event == nil then
|
|
error("Cannot be run as a model script!")
|
|
return 2
|
|
else
|
|
DSM_Display()
|
|
DSM_Menu(event)
|
|
DSM_Send_Receive()
|
|
end
|
|
if Phase == PH_EXIT_DONE then
|
|
LOG_close()
|
|
return 2
|
|
else
|
|
return 0
|
|
end
|
|
end
|
|
|
|
return { init = DSM_Init, run = DSM_Run }
|