Fix doc path
[opentx.git] / radio / sdcard / taranis-x9 / SxR / SxR.lua
blob2a2fb4ef15004b380afb2a2b7440972a3a380b19
1 ---- #########################################################################
2 ---- # #
3 ---- # Copyright (C) OpenTX #
4 -----# #
5 ---- # License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html #
6 ---- # #
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. #
10 ---- # #
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. #
15 ---- # #
16 ---- #########################################################################
17 local VALUE = 0
18 local COMBO = 1
20 local COLUMN_2 = 150
22 local edit = false
23 local page = 1
24 local current = 1
25 local refreshState = 0
26 local refreshIndex = 0
27 local pageOffset = 0
28 local pages = {}
29 local fields = {}
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]
67 local min, max
68 if field[2] == VALUE then
69 min = field[5]
70 max = field[6]
71 elseif field[2] == COMBO then
72 min = 0
73 max = #(field[5]) - 1
74 end
75 if (step < 0 and field[4] > min) or (step > 0 and field[4] < max) then
76 field[4] = field[4] + step
77 end
78 end
80 -- Select the next or previous page
81 local function selectPage(step)
82 page = 1 + ((page + step - 1 + #pages) % #pages)
83 refreshIndex = 0
84 pageOffset = 0
85 end
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
94 end
95 end
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()
105 lcd.clear()
106 lcd.drawScreenTitle("SxR", page, #pages)
108 if refreshIndex < #fields then
109 drawProgressBar()
112 for index = 1, 7, 1 do
113 local field = fields[pageOffset+index]
114 if field == nil then
115 break
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)
124 else
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
153 refreshState = 1
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)
170 value = b1*256 + b2
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
176 value = index - 1
177 break
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
185 refreshState = 0
187 elseif getTime() > telemetryPopTimeout then
188 fields[refreshIndex + 1][4] = nil
189 refreshIndex = refreshIndex + 1
190 refreshState = 0
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 }
205 -- Main
206 local function runFieldsPage(event)
207 if event == EVT_EXIT_BREAK then -- exit script
208 return 2
209 elseif event == EVT_ENTER_BREAK then -- toggle editing/selecting current field
210 if fields[current][4] ~= nil then
211 edit = not edit
212 if edit == false then
213 updateField(fields[current])
216 elseif edit then
217 if event == EVT_PLUS_FIRST or event == EVT_ROT_RIGHT or event == EVT_PLUS_REPT then
218 addField(1)
219 elseif event == EVT_MINUS_FIRST or event == EVT_ROT_LEFT or event == EVT_MINUS_REPT then
220 addField(-1)
222 else
223 if event == EVT_MINUS_FIRST or event == EVT_ROT_RIGHT then
224 selectField(1)
225 elseif event == EVT_PLUS_FIRST or event == EVT_ROT_LEFT then
226 selectField(-1)
229 redrawFieldsPage()
230 return 0
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]])
245 return result
248 local function runSettingsPage(event)
249 fields = settingsFields
250 return runFieldsPage(event)
253 -- Init
254 local function init()
255 current, edit, refreshState, refreshIndex = 1, false, 0, 0
256 pages = {
257 runConfigPage,
258 runSettingsPage,
262 -- Main
263 local function run(event)
264 if event == nil then
265 error("Cannot be run as a model script!")
266 return 2
267 elseif event == EVT_PAGE_BREAK then
268 selectPage(1)
269 elseif event == EVT_PAGE_LONG then
270 killEvents(event);
271 selectPage(-1)
274 local result = pages[page](event)
275 refreshNext()
277 return result
280 return { init=init, run=run }