1 ---- #########################################################################
3 ---- # Copyright (C) OpenTX #
5 ---- # License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html #
7 ---- # This program is free software; you can redistribute it and/or modify #
8 ---- # it under the terms of the GNU General Public License version 2 as #
9 ---- # published by the Free Software Foundation. #
11 ---- # This program is distributed in the hope that it will be useful #
12 ---- # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 ---- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 ---- # GNU General Public License for more details. #
16 ---- #########################################################################
25 local refreshState
= 0
26 local refreshIndex
= 0
30 local modifications
= {}
32 local configFields
= {
33 {"Wing type:", COMBO
, 0x80, nil, { "Normal", "Delta", "VTail" } },
34 {"Mounting type:", COMBO
, 0x81, nil, { "Horz", "Horz rev.", "Vert", "Vert rev." } },
37 local settingsFields
= {
38 {"SxR functions:", COMBO
, 0x9C, nil, { "Disable", "Enable" } },
39 {"Quick Mode:", COMBO
, 0xAA, nil, { "Disable", "Enable" } },
40 {"CH5 mode:", COMBO
, 0xA8, nil, { "AIL2", "AUX1" } },
41 {"CH6 mode:", COMBO
, 0xA9, nil, { "ELE2", "AUX2" } },
42 {"AIL direction:", COMBO
, 0x82, nil, { "Normal", "Invers" }, { 255, 0 } },
43 {"ELE direction:", COMBO
, 0x83, nil, { "Normal", "Invers" }, { 255, 0 } },
44 {"RUD direction:", COMBO
, 0x84, nil, { "Normal", "Invers" }, { 255, 0 } },
45 {"AIL2 direction:", COMBO
, 0x9A, nil, { "Normal", "Invers" }, { 255, 0 } },
46 {"ELE2 direction:", COMBO
, 0x9B, nil, { "Normal", "Invers" }, { 255, 0 } },
47 {"AIL stabilize gain:", VALUE
, 0x85, nil, 0, 200, "%"},
48 {"ELE stabilize gain:", VALUE
, 0x86, nil, 0, 200, "%"},
49 {"RUD stabilize gain:", VALUE
, 0x87, nil, 0, 200, "%"},
50 {"AIL auto level gain:", VALUE
, 0x88, nil, 0, 200, "%"},
51 {"ELE auto level gain:", VALUE
, 0x89, nil, 0, 200, "%"},
52 {"ELE upright gain:", VALUE
, 0x8C, nil, 0, 200, "%"},
53 {"RUD upright gain:", VALUE
, 0x8D, nil, 0, 200, "%"},
54 {"AIL crab gain:", VALUE
, 0x8E, nil, 0, 200, "%"},
55 {"RUD crab gain:", VALUE
, 0x90, nil, 0, 200, "%"},
56 {"AIL auto angle offset:", VALUE
, 0x91, nil, -20, 20, "%", 0x6C},
57 {"ELE auto angle offset:", VALUE
, 0x92, nil, -20, 20, "%", 0x6C},
58 {"ELE upright angle offset:", VALUE
, 0x95, nil, -20, 20, "%", 0x6C},
59 {"RUD upright angle offset:", VALUE
, 0x96, nil, -20, 20, "%", 0x6C},
60 {"AIL crab angle offset:", VALUE
, 0x97, nil, -20, 20, "%", 0x6C},
61 {"RUD crab angle offset:", VALUE
, 0x99, nil, -20, 20, "%", 0x6C},
64 -- Change display attribute to current field
65 local function addField(step
)
66 local field
= fields
[current
]
68 if field
[2] == VALUE
then
71 elseif field
[2] == COMBO
then
75 if (step
< 0 and field
[4] > min) or (step
> 0 and field
[4] < max) then
76 field
[4] = field
[4] + step
80 -- Select the next or previous page
81 local function selectPage(step
)
82 page
= 1 + ((page
+ step
- 1 + #pages
) % #pages
)
87 -- Select the next or previous editable field
88 local function selectField(step
)
89 current
= 1 + ((current
+ step
- 1 + #fields
) % #fields
)
90 if current
> 7 + pageOffset
then
91 pageOffset
= current
- 7
92 elseif current
<= pageOffset
then
93 pageOffset
= current
- 1
97 local function drawProgressBar()
98 local width
= (140 * refreshIndex
) / #fields
99 lcd
.drawRectangle(30, 1, 144, 6)
100 lcd
.drawFilledRectangle(32, 3, width
, 2);
103 -- Redraw the current page
104 local function redrawFieldsPage()
106 lcd
.drawScreenTitle("SxR", page
, #pages
)
108 if refreshIndex
< #fields
then
112 for index
= 1, 7, 1 do
113 local field
= fields
[pageOffset
+index
]
118 local attr
= current
== (pageOffset
+index
) and ((edit
== true and BLINK
or 0) + INVERS
) or 0
120 lcd
.drawText(0, 1+8*index
, field
[1])
122 if field
[4] == nil then
123 lcd
.drawText(COLUMN_2
, 1+8*index
, "---", attr
)
125 if field
[2] == VALUE
then
126 lcd
.drawNumber(COLUMN_2
, 1+8*index
, field
[4], LEFT
+ attr
)
127 elseif field
[2] == COMBO
then
128 if field
[4] >= 0 and field
[4] < #(field
[5]) then
129 lcd
.drawText(COLUMN_2
, 1+8*index
, field
[5][1+field
[4]]
, attr
)
136 local function telemetryRead(field
)
137 return sportTelemetryPush(0x17, 0x30, 0x0C30, field
)
140 local function telemetryWrite(field
, value
)
141 return sportTelemetryPush(0x17, 0x31, 0x0C30, field
+ value
*256)
144 local telemetryPopTimeout
= 0
145 local function refreshNext()
146 if refreshState
== 0 then
147 if #modifications
> 0 then
148 telemetryWrite(modifications
[1][1], modifications
[1][2])
149 modifications
[1] = nil
150 elseif refreshIndex
< #fields
then
151 local field
= fields
[refreshIndex
+ 1]
152 if telemetryRead(field
[3]) == true then
154 telemetryPopTimeout
= getTime() + 120 -- normal delay is 500ms
157 elseif refreshState
== 1 then
158 local physicalId
, primId
, dataId
, value
= sportTelemetryPop()
159 if physicalId
== 0x1A and primId
== 0x32 and dataId
== 0x0C30 then
160 local fieldId
= value
% 256
161 local field
= fields
[refreshIndex
+ 1]
162 if fieldId
== field
[3] then
163 local value
= math
.floor(value
/ 256)
164 if field
[3] == 0xAA then
165 value
= bit32
.band(value
, 0x0001)
167 if field
[3] >= 0x9E and field
[3] <= 0xA0 then
168 local b1
= value
% 256
169 local b2
= math
.floor(value
/ 256)
171 value
= value
- bit32
.band(value
, 0x8000) * 2
173 if field
[2] == COMBO
and #field
== 6 then
174 for index
= 1, #(field
[6]), 1 do
175 if value
== field
[6][index
] then
180 elseif field
[2] == VALUE
and #field
== 8 then
181 value
= value
- field
[8] + field
[5]
183 fields
[refreshIndex
+ 1][4] = value
184 refreshIndex
= refreshIndex
+ 1
187 elseif getTime() > telemetryPopTimeout
then
188 fields
[refreshIndex
+ 1][4] = nil
189 refreshIndex
= refreshIndex
+ 1
195 local function updateField(field
)
196 local value
= field
[4]
197 if field
[2] == COMBO
and #field
== 6 then
198 value
= field
[6][1+value
]
199 elseif field
[2] == VALUE
and #field
== 8 then
200 value
= value
+ field
[8] - field
[5]
202 modifications
[#modifications
+1] = { field
[3], value
}
206 local function runFieldsPage(event
)
207 if event
== EVT_EXIT_BREAK
then -- exit script
209 elseif event
== EVT_ENTER_BREAK
then -- toggle editing/selecting current field
210 if fields
[current
][4] ~= nil then
212 if edit
== false then
213 updateField(fields
[current
])
217 if event
== EVT_PLUS_FIRST
or event
== EVT_ROT_RIGHT
or event
== EVT_PLUS_REPT
then
219 elseif event
== EVT_MINUS_FIRST
or event
== EVT_ROT_LEFT
or event
== EVT_MINUS_REPT
then
223 if event
== EVT_MINUS_FIRST
or event
== EVT_ROT_RIGHT
then
225 elseif event
== EVT_PLUS_FIRST
or event
== EVT_ROT_LEFT
then
233 local wingBitmaps
= { "bmp/plane.bmp", "bmp/delta.bmp", "bmp/vtail.bmp" }
234 local mountBitmaps
= { "bmp/horz.bmp", "bmp/horz-r.bmp", "bmp/vert.bmp", "bmp/vert-r.bmp" }
236 local function runConfigPage(event
)
237 fields
= configFields
238 local result
= runFieldsPage(event
)
239 if fields
[1][4] ~= nil then
240 lcd
.drawPixmap(20, 28, wingBitmaps
[1 + fields
[1][4]]
)
242 if fields
[2][4] ~= nil then
243 lcd
.drawPixmap(128, 28, mountBitmaps
[1 + fields
[2][4]]
)
248 local function runSettingsPage(event
)
249 fields
= settingsFields
250 return runFieldsPage(event
)
254 local function init()
255 current
, edit
, refreshState
, refreshIndex
= 1, false, 0, 0
263 local function run(event
)
265 error("Cannot be run as a model script!")
267 elseif event
== EVT_PAGE_BREAK
then
269 elseif event
== EVT_PAGE_LONG
then
274 local result
= pages
[page
](event
)
280 return { init
=init
, run
=run
}