local toolName = "TNS|DSM Forward Programming|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. # ---- # # ---- ######################################################################### --############################################################################### -- 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 RX_VERSION, WAIT_CMD, MENU_TITLE, MENU_LINES, MENU_VALUES, VALUE_CHANGING, VALUE_CHANGING_WAIT, VALUE_CHANGED = 0, 1, 2, 3, 4, 5, 6, 7 local MENU, LIST_MENU_NOCHANGING, LIST_MENU2, PERCENTAGE_VALUE = 0x1C, 0x6C, 0x4C, 0xC0 local Phase = RX_VERSION local Waiting_RX = 0 local Text = {} local Retry=100 local Blink = 0 local Value_Changed=0 local Menu = { Cur=nil, Id=nil, Title="", Prev=nil, PrevId=nil, Next=nil, NextId=nil, Back=nil, BackId=nil, CurLine=nil, SelLine=nil, EditLine=nil } local Line = {} local RX = { Name="", Version="" } local function conv_int16(number) if number >= 0x8000 then return number - 0x10000 end return number end local function Get_Text(index) out = Text[index] if out == nil then -- unknown... out = "Unknown_"..string.format("%X",index) end return out 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 local function Value_Add(dir) local line=Line[Menu.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 if Line[Menu.SelLine].Type ~= LIST_MENU_NOCHANGING then Phase = VALUE_CHANGING Waiting_RX = 0 end end local function DSM_Menu(event) local Speed = 0 if event == EVT_VIRTUAL_NEXT then if Menu.EditLine == nil then -- not changing a value if Menu.SelLine ~= nil then if Menu.SelLine < 7 then local num = Menu.SelLine for i = Menu.SelLine + 1, 6, 1 do if Line[i].Type ~= nil and Line[i].Next ~= nil then Menu.SelLine=i break end end if num == Menu.SelLine then if Menu.Next ~= 0 then -- Next Menu.SelLine = 7 elseif Menu.Prev ~= 0 then -- Prev Menu.SelLine = 8 end end elseif Menu.Prev ~= 0 then -- Prev Menu.SelLine = 8 end end else -- need to inc the value Value_Add(1) end elseif event == EVT_VIRTUAL_PREV then if Menu.EditLine == nil then if Menu.SelLine ~= nil then if Menu.SelLine == 8 and Menu.Next ~= 0 then Menu.SelLine = 7 elseif Menu.SelLine > 0 then if Menu.SelLine > 6 then Menu.SelLine = 7 end local num = Menu.SelLine for i = Menu.SelLine-1, 0, -1 do if Line[i].Type ~= nil and Line[i].Next ~= nil then Menu.SelLine=i break end end if num == Menu.SelLine then -- Back Menu.SelLine = -1 end else Menu.SelLine = -1 -- Back end end else -- need to dec the value Value_Add(-1) end elseif event == EVT_VIRTUAL_ENTER then if Menu.SelLine == -1 then -- Back Menu.Cur = Menu.Back Menu.Id = Menu.BackId Menu.SelLine = 0 Phase = MENU_TITLE Waiting_RX = 0 elseif Menu.SelLine == 7 then -- Next Menu.Cur = Menu.Next Menu.Id = Menu.NextId Menu.SelLine = 0 Phase = MENU_TITLE Waiting_RX = 0 elseif Menu.SelLine == 8 then -- Prev Menu.Cur = Menu.Prev Menu.Id = Menu.PrevId Menu.SelLine = 0 Phase = MENU_TITLE Waiting_RX = 0 elseif Menu.SelLine ~= nil and Line[Menu.SelLine].Next ~= nil then if Line[Menu.SelLine].Type == MENU then -- Next menu exist Menu.Cur = Line[Menu.SelLine].Next Menu.Id = Line[Menu.SelLine].NextId Phase = MENU_TITLE Waiting_RX = 0 else -- value entry if Menu.EditLine == Menu.SelLine then Menu.EditLine = nil Value_Changed = 0 Phase = VALUE_CHANGED Waiting_RX = 0 else Menu.EditLine = Menu.SelLine end end end end end local function DSM_Send_Receive() if Waiting_RX == 0 then Waiting_RX = 1 -- Need to send a request if Phase == RX_VERSION then -- request RX version DSM_Send(0x11,0x06,0x00,0x14,0x00,0x00) elseif Phase == WAIT_CMD then -- keep connection open DSM_Send(0x00,0x04,0x00,0x00) elseif Phase == MENU_TITLE then -- request menu title if Menu.Cur == nil then DSM_Send(0x12,0x06,0x00,0x14,0x00,0x00) -- first menu only Menu.Cur = 0 else DSM_Send(0x16,0x06,Menu.Id,Menu.Cur,0x00,Menu.SelLine) end elseif Phase == MENU_LINES then -- request menu lines if Menu.CurLine == nil then DSM_Send(0x13,0x04,Menu.Id,Menu.Cur) -- line 0 elseif Menu.CurLine >= 0x80 then local last_byte={0x40,0x01,0x02,0x04,0x00,0x00} -- unknown... DSM_Send(0x20,0x06,Menu.CurLine-0x80,Menu.CurLine-0x80,0x00,last_byte[Menu.CurLine-0x80+1]) -- line X else DSM_Send(0x14,0x06,Menu.Id,Menu.Cur,0x00,Menu.CurLine) -- line X end elseif Phase == MENU_VALUES then -- request menu values DSM_Send(0x15,0x06,Menu.Id,Menu.Cur,Line[Menu.CurLine].ValId,Menu.CurLine) -- line X elseif Phase == VALUE_CHANGING then -- send value local value=Line[Menu.SelLine].Val if value < 0 then value = 0x10000 + value end DSM_Send(0x18,0x06,Line[Menu.SelLine].ValId,Menu.SelLine,bit32.rshift(value,8),bit32.band(value,0xFF)) -- send current value Phase = VALUE_CHANGING_WAIT elseif Phase == VALUE_CHANGED then -- send value if Value_Changed == 0 then local value=Line[Menu.SelLine].Val if value < 0 then value = 0x10000 + value end DSM_Send(0x18,0x06,Line[Menu.SelLine].ValId,Menu.SelLine,bit32.rshift(value,8),bit32.band(value,0xFF)) -- send current value Value_Changed = Value_Changed + 1 Waiting_RX = 0 elseif Value_Changed == 1 then DSM_Send(0x19,0x06,Line[Menu.SelLine].ValId,Menu.SelLine) -- validate -- Value_Changed = Value_Changed + 1 -- Waiting_RX = 0 --elseif Value_Changed == 2 then -- DSM_Send(0x1B,0x06,0x10,Menu.SelLine) -- validate again? -- Value_Changed = Value_Changed + 1 end elseif Phase == VALUE_CHANGING_WAIT then DSM_Send(0x1A,0x06,Line[Menu.SelLine].ValId,Menu.SelLine) end multiBuffer(10,0x00); Retry = 50 elseif multiBuffer(10) == 0x09 then -- Answer received --if multiBuffer(11) == 0x00 then -- waiting for commands? if multiBuffer(11) == 0x01 then -- read version --ex: 0x09 0x01 0x00 0x15 0x02 0x22 0x01 0x00 0x14 0x00 0x00 0x00 0x00 0x00 0x00 0x00 RX.Name = Get_Text(multiBuffer(13)) RX.Version = multiBuffer(14).."."..multiBuffer(15).."."..multiBuffer(16) Phase = MENU_TITLE elseif multiBuffer(11) == 0x02 then -- read menu title --ex: 0x09 0x02 0x4F 0x10 0xA5 0x00 0x00 0x00 0x50 0x10 0x10 0x10 0x00 0x00 0x00 0x00 Menu.Cur = multiBuffer(12) Menu.Id = multiBuffer(13) Menu.Title = Get_Text(multiBuffer(14)+multiBuffer(15)*256) Menu.Prev = multiBuffer(16) Menu.PrevId = multiBuffer(17) Menu.Next = multiBuffer(18) Menu.NextId = multiBuffer(19) Menu.Back = multiBuffer(20) Menu.BackId = multiBuffer(21) for i = 0, 6 do -- clear menu Line[i] = { Menu = nil, Id = nil, Type = nil, Text="", Next = nil, NextId = nil, ValLine = nil, ValId = nil, Min, Max, Def, Val, Unit, Step } end Menu.CurLine = nil if Menu.Next ~= 0 then Menu.SelLine = 7 -- highlight Next else Menu.SelLine = -1 -- highlight Back end Blink = 0 Phase = MENU_LINES elseif multiBuffer(11) == 0x03 then -- read menu lines --ex: 0x09 0x03 0x00 0x10 0x00 0x1C 0xF9 0x00 0x10 0x10 0x00 0x00 0x00 0x00 0x03 0x00 -- Menu Id line Type Text_idx Next V_Id Val_Min Val_Max Val_Def --ex: 0x09 0x03 0x61 0x10 0x00 0x6C 0x50 0x00 0x00 0x10 0x36 0x00 0x49 0x00 0x36 0x00 Menu.CurLine = multiBuffer(14) local line = Line[Menu.CurLine] line.Menu = multiBuffer(12) line.Id = multiBuffer(13) -- not quite sure yet line.Type = multiBuffer(15) -- not quite sure yet: 1C is text menu only, 4C/6C is text followed by text list, C0 is text followed by percentage value line.Text = Get_Text(multiBuffer(16)+multiBuffer(17)*256) if multiBuffer(18) == Menu.Cur then line.Next = nil else line.Next = multiBuffer(18) -- not quite sure yet: 1C=text menu=>next menu, others=>line number of the value end if Menu.SelLine == -1 and line.Next ~= nil then -- Auto select first line of the menu Menu.SelLine = Menu.CurLine end line.NextId = multiBuffer(19) -- not quite sure yet line.ValLine = multiBuffer(18) -- not quite sure yet line.ValId = multiBuffer(19) -- not quite sure yet line.Min = conv_int16(multiBuffer(20)+multiBuffer(21)*256) line.Max = conv_int16(multiBuffer(22)+multiBuffer(23)*256) line.Def = conv_int16(multiBuffer(24)+multiBuffer(25)*256) if line.Type == MENU then -- nothing to do on menu entries elseif line.Type == LIST_MENU_NOCHANGING or line.Type == LIST_MENU2 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 end if line.Type ~= 0x1C then -- value to follow line.Text = line.Text..":" end Phase = MENU_LINES elseif multiBuffer(11) == 0x04 then -- read menu values --ex: 0x09 0x04 0x53 0x10 0x00 0x10 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 -- Menu MeId line VaId Value --ex: 0x09 0x04 0x61 0x10 0x02 0x10 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 Menu.CurLine = multiBuffer(14) Line[Menu.CurLine].Val = conv_int16(multiBuffer(16)+multiBuffer(17)*256) Phase = MENU_VALUES elseif multiBuffer(11) == 0x05 then -- unknown... need to get through the lines... Menu.CurLine = 0x80 + multiBuffer(12) Phase = MENU_LINES elseif multiBuffer(11) == 0x00 and Phase == VALUE_CHANGING then Phase = VALUE_CHANGING_WAIT end -- Data processed Waiting_RX = 0 multiBuffer(10,0x00) Retry = 50 else Retry = Retry - 1 if Retry <= 0 then -- Retry the RX request Retry = 50 Waiting_RX = 0 if Phase ~= RX_VERSION and Phase ~= VALUE_CHANGING_WAIT then Phase = WAIT_CMD end end end end local function DSM_Display() lcd.clear() if LCD_W == 480 then --Draw title lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) lcd.drawText(1, 5, "DSM Forward Programming", MENU_TITLE_COLOR) --Draw RX Menu if Phase == RX_VERSION then lcd.drawText(10,50,"No compatible DSM RX...", BLINK) else if Menu.Title ~= nil then local attrib=0; lcd.drawText(80,32,Menu.Title,MIDSIZE) for i = 0, 6 do if i == Menu.SelLine then attrib = INVERS else attrib = 0 end if Line[i] ~= nil and Line[i].Type ~= nil then if Line[i].Type ~= MENU then -- list/value if Line[i].Val ~= nil then local text="" if Line[i].Type == LIST_MENU_NOCHANGING or Line[i].Type == LIST_MENU2 then text = Get_Text(Line[i].Val+Line[i].Def) elseif Line[i].Type == PERCENTAGE_VALUE then text = Line[i].Val.." %" else text = Line[i].Val end if Menu.EditLine == Menu.SelLine then -- blink edited entry Blink = Blink + 1 if Blink > 25 then attrib = 0 if Blink > 50 then Blink = 0 end end end lcd.drawText(240,32+20*(i+2), text, attrib) -- display value end attrib = 0 end lcd.drawText(10,32+20*(i+2), Line[i].Text, attrib) -- display text end end if Menu.SelLine == -1 then lcd.drawText(437,32, "Back", INVERS) else lcd.drawText(437,32, "Back", 0) end lcd.drawRectangle(437-5, 32-2, 47, 25) if Menu.Next ~= 0 then if Menu.SelLine == 7 then lcd.drawText(437,220, "Next",INVERS) else lcd.drawText(437,220, "Next") end lcd.drawRectangle(437-5, 220-2, 47, 25) end if Menu.Prev ~= 0 then if Menu.SelLine == 8 then lcd.drawText(5,220, "Prev",INVERS) else lcd.drawText(5,220, "Prev") end lcd.drawRectangle(5-5, 220-2, 47, 25) end end lcd.drawText(170,252, "RX "..RX.Name.." v"..RX.Version) -- display RX info end else -- --Draw RX Menu on LCD_W=128 -- if multiBuffer( 4 ) == 0xFF then -- lcd.drawText(2,17,"No compatible DSM RX...",SMLSIZE) -- else -- if Retry_128 ~= 0 then -- --Intro page -- Retry_128 = Retry_128 - 1 -- lcd.drawScreenTitle("DSM Forward Programming",0,0) -- lcd.drawText(2,17,"Press Prev Page for previous Menu" ,SMLSIZE) -- else -- --Menu page -- for line = 0, 7, 1 do -- for i = 0, 21-1, 1 do -- value=multiBuffer( line*21+6+i ) -- if value > 0x80 then -- value = value - 0x80 -- lcd.drawText(2+i*6,1+8*line,string.char(value).." ",SMLSIZE+INVERS) -- else -- lcd.drawText(2+i*6,1+8*line,string.char(value),SMLSIZE) -- end -- end -- end -- end -- end end end -- Init local function DSM_Init() --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') ) --Text to be displayed -> need to use a file instead? --RX names-- Text[0x0014]="SPM4651T" Text[0x0015]="AR637T" --Lists-- Text[0x0036]="Throttle" Text[0x0037]="Aileron" Text[0x0038]="Elevator" Text[0x0039]="Rudder" Text[0x003A]="Gear" --****** --This part is strange since the AR637T needs for i=1,7 do -- 3B..41 Text[0x003A+i]="Aux"..i end for i=1,8 do -- 41..49 Text[0x0041+i]="XPlus-"..i end --But FOTO-PETE reports that it should be: Text[0x0040]="Roll" Text[0x0041]="Pitch" Text[0x0042]="Yaw" Text[0x0046]="Priority" --****** Text[0x004A]="Failsafe" Text[0x004B]="Main Menu" Text[0x004E]="Position" Text[0x0050]="Outputs" Text[0x0051]="Output Channel 1" Text[0x0052]="Output Channel 2" Text[0x0053]="Output Channel 3" Text[0x0054]="Output Channel 4" Text[0x0055]="Output Channel 5" Text[0x0056]="Output Channel 6" --Text[0x005E]="Inhibit" Text[0x005F]="Hold Last" Text[0x0060]="Preset" --Text[0x0061]="Custom" --Messages-- Text[0x0078]="FM Channel" Text[0x0080]="Orientation" Text[0x0085]="Frame Rate" Text[0x0086]="System Setup" Text[0x0087]="F-Mode Setup" Text[0x0088]="Enabled F-Modes" Text[0x008A]="Gain Sensitivity" Text[0x0090]="Apply" Text[0x0093]="Complete" Text[0x0094]="Done" Text[0x0097]="Factory Reset" Text[0x009A]="Capture Failsafe Positions" Text[0x009C]="Custom Failsafe" Text[0x00A5]="First Time Setup" Text[0x00AD]="Gain Channel Select" Text[0x00B6]="FM1" Text[0x00B7]="FM2" Text[0x00B8]="FM3" Text[0x00B9]="FM4" Text[0x00BA]="FM5" Text[0x00BB]="FM6" Text[0x00BC]="FM7" Text[0x00BD]="FM8" Text[0x00BE]="FM9" Text[0x00BF]="FM10" Text[0x00F9]="Gyro settings" Text[0x00FE]="Stick Priority" Text[0x0100]="Make sure the model has been" Text[0x0101]="configured, including wing type," Text[0x0102]="reversing, travel, trimmed, etc." Text[0x0103]="before continuing setup." Text[0x0104]="0104" Text[0x0105]="0105" Text[0x0106]="Any wing type, channel assignment," Text[0x0107]="subtrim, or servo reversing changes" Text[0x0108]="require running through initial" Text[0x0109]="setup again." Text[0x010A]="010A" Text[0x010B]="010B" Text[0x0190]="Relearn Servo Settings" Text[0x019C]="Enter Receiver Bind Mode" Text[0x01DC]="AS3X" Text[0x01DD]="AS3X Settings" Text[0x01DE]="AS3X Gains" Text[0x01E0]="Rate Gains" Text[0x020A]="Restore from Backup" Text[0x0209]="Save to Backup" Text[0x020D]="First Time SAFE Setup" Text[0x021A]="Set the model level," Text[0x021B]="and press Continue." Text[0x021C]="021C" Text[0x021D]="021D" Text[0x021F]="Set the model on its nose," Text[0x0220]="and press Continue. If the" Text[0x0221]="orientation on the next" Text[0x0222]="screen is wrong go back" Text[0x0223]="and try again." Text[0x0224]="Continue" Text[0x0229]="Set Orientation Manually" Text[0x0227]="Other settings" Text[0x022B]="WARNING!" Text[0x022C]="This will reset the" Text[0x022D]="configuration to factory" Text[0x022E]="defaults. This does not" Text[0x022F]="affect the backup config." Text[0x0230]="0230" Text[0x0231]="This will overwrite the" Text[0x0232]="backup memory with your" Text[0x0233]="current configuartion." Text[0x0234]="0234" Text[0x0235]="0235" Text[0x0236]="This will overwrite the" Text[0x0237]="current config with" Text[0x0238]="that which is in" Text[0x0239]="the backup memory." Text[0x023A]="023A" Text[0x023D]="Copy Flight Mode Settings" Text[0x0240]="Utilities" Text[0x8001]="Flight Mode 1" Text[0x8002]="Flight Mode 2" Text[0x8003]="Flight Mode 3" end -- Main local function DSM_Run(event) if event == nil then error("Cannot be run as a model script!") return 2 elseif event == EVT_VIRTUAL_EXIT then DSM_Release() return 2 else DSM_Menu(event) DSM_Send_Receive() DSM_Display() return 0 end end return { init=DSM_Init, run=DSM_Run }