Updated Wizard: reduced mixers name to 6 caracters.
[opentx.git] / radio / sdcard / taranis-x7 / SCRIPTS / WIZARD / plane.lua
blob6454d76108c3ac1789171f8eb015b7c2f9062438
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 -- Plane Wizard pages
18 local ENGINE_PAGE = 0
19 local AILERONS_PAGE = 1
20 local FLAPERONS_PAGE = 2
21 local BRAKES_PAGE = 3
22 local TAIL_PAGE = 4
23 local CONFIRMATION_PAGE = 5
25 -- Navigation variables
26 local page = ENGINE_PAGE
27 local dirty = true
28 local edit = false
29 local field = 0
30 local fieldsMax = 0
32 -- Model settings
33 local engineMode = 1
34 local thrCH1 = 0
35 local aileronsMode = 1
36 local ailCH1 = 0
37 local ailCH2 = 5
38 local flapsMode = 0
39 local flapsCH1 = 6
40 local flapsCH2 = 7
41 local brakesMode = 0
42 local brakesCH1 = 8
43 local brakesCH2 = 9
44 local tailMode = 1
45 local eleCH1 = 0
46 local eleCH2 = 4
47 local rudCH1 = 0
48 local servoPage = nil
50 -- Common functions
51 local lastBlink = 0
52 local function blinkChanged()
53 local time = getTime() % 128
54 local blink = (time - time % 64) / 64
55 if blink ~= lastBlink then
56 lastBlink = blink
57 return true
58 else
59 return false
60 end
61 end
63 local function fieldIncDec(event, value, max, force)
64 if edit or force==true then
65 if event == EVT_ROT_LEFT then
66 value = (value + max)
67 dirty = true
68 elseif event == EVT_ROT_RIGHT then
69 value = (value + max + 2)
70 dirty = true
71 end
72 value = (value % (max+1))
73 end
74 return value
75 end
77 local function valueIncDec(event, value, min, max)
78 if edit then
79 if event == EVT_ROT_LEFT or event == EVT_ROT_LEFT then
80 if value < max then
81 value = (value + 1)
82 dirty = true
83 end
84 elseif event == EVT_ROT_RIGHT or event == EVT_ROT_RIGHT then
85 if value > min then
86 value = (value - 1)
87 dirty = true
88 end
89 end
90 end
91 return value
92 end
94 local function navigate(event, fieldMax, prevPage, nextPage)
95 if event == EVT_ENTER_BREAK then
96 edit = not edit
97 dirty = true
98 elseif edit then
99 if event == EVT_EXIT_BREAK then
100 edit = false
101 dirty = true
102 elseif not dirty then
103 dirty = blinkChanged()
105 else
106 if event == EVT_PAGE_BREAK then
107 page = nextPage
108 field = 0
109 dirty = true
110 elseif event == EVT_PAGE_LONG then
111 page = prevPage
112 field = 0
113 killEvents(event);
114 dirty = true
115 else
116 field = fieldIncDec(event, field, fieldMax, true)
121 local function getFieldFlags(position)
122 flags = 0
123 if field == position then
124 flags = INVERS
125 if edit then
126 flags = INVERS + BLINK
129 return flags
132 local function channelIncDec(event, value)
133 if not edit and event==EVT_MENU_BREAK then
134 servoPage = value
135 dirty = true
136 else
137 value = valueIncDec(event, value, 0, 15)
139 return value
142 -- Init function
143 local function init()
144 rudCH1 = defaultChannel(0)
145 eleCH1 = defaultChannel(1)
146 thrCH1 = defaultChannel(2)
147 ailCH1 = defaultChannel(3)
150 -- Engine Menu
151 local engineModeItems = {"No", "Yes"}
152 local function drawEngineMenu()
153 lcd.clear()
154 if engineMode == 1 then
155 -- 1 channel
156 lcd.drawText(5, 30, "Assign channel", 0);
157 lcd.drawText(5, 40, ">>>", 0);
158 lcd.drawSource(25, 40, MIXSRC_CH1+thrCH1, getFieldFlags(1))
159 fieldsMax = 1
160 else
161 -- No engine
162 fieldsMax = 0
164 lcd.drawText(1, 0, "Got an engine?", 0)
165 lcd.drawFilledRectangle(0, 0, LCD_W, 8, FILL_WHITE)
166 lcd.drawCombobox(0, 8, LCD_W, engineModeItems, engineMode, getFieldFlags(0))
169 local function engineMenu(event)
170 if dirty then
171 dirty = false
172 drawEngineMenu()
175 navigate(event, fieldsMax, page, page+1)
177 if field==0 then
178 engineMode = fieldIncDec(event, engineMode, 1)
179 elseif field==1 then
180 thrCH1 = channelIncDec(event, thrCH1)
184 -- Ailerons Menu
185 local aileronsModeItems = {"No", "Yes", "Yes, 2 channels"}
186 local function drawAileronsMenu()
187 lcd.clear()
188 if aileronsMode == 2 then
189 -- 2 channels
190 lcd.drawText(5, 30, "Assign channels", 0);
191 lcd.drawText(30, 40, "L", 0);
192 lcd.drawText(65, 40, "R", 0);
193 lcd.drawText(5, 50, ">>>", 0);
194 lcd.drawSource(25, 50, MIXSRC_CH1+ailCH1, getFieldFlags(1))
195 lcd.drawSource(60, 50, MIXSRC_CH1+ailCH2, getFieldFlags(2))
196 fieldsMax = 2
197 elseif aileronsMode == 1 then
198 -- 1 channel
199 lcd.drawText(5, 30, "Assign channel", 0);
200 lcd.drawText(5, 40, ">>>", 0);
201 lcd.drawSource(25, 40, MIXSRC_CH1+ailCH1, getFieldFlags(1))
202 fieldsMax = 1
203 else
204 -- No ailerons
205 fieldsMax = 0
207 lcd.drawText(1, 0, "Got ailerons?", 0)
208 lcd.drawFilledRectangle(0, 0, LCD_W, 8, FILL_WHITE)
209 lcd.drawCombobox(0, 8, LCD_W, aileronsModeItems, aileronsMode, getFieldFlags(0))
212 local function aileronsMenu(event)
213 if dirty then
214 dirty = false
215 drawAileronsMenu()
218 navigate(event, fieldsMax, page-1, page+1)
220 if field==0 then
221 aileronsMode = fieldIncDec(event, aileronsMode, 2)
222 elseif field==1 then
223 ailCH1 = channelIncDec(event, ailCH1)
224 elseif field==2 then
225 ailCH2 = channelIncDec(event, ailCH2)
229 -- Flaps Menu
230 local flapsModeItems = {"No", "Yes", "Yes, 2 channels"}
231 local function drawFlapsMenu()
232 lcd.clear()
233 if flapsMode == 0 then
234 -- no flaps
235 fieldsMax = 0
236 elseif flapsMode == 1 then
237 -- 1 channel
238 lcd.drawText(5, 30, "Assign channel", 0);
239 lcd.drawText(5, 40, ">>>", 0);
240 lcd.drawSource(25, 40, MIXSRC_CH1+flapsCH1, getFieldFlags(1))
241 fieldsMax = 1
242 elseif flapsMode == 2 then
243 -- 2 channels
244 lcd.drawText(5, 30, "Assign channels", 0);
245 lcd.drawText(30, 40, "L", 0);
246 lcd.drawText(65, 40, "R", 0);
247 lcd.drawText(5, 50, ">>>", 0);
248 lcd.drawSource(25, 50, MIXSRC_CH1+flapsCH1, getFieldFlags(1))
249 lcd.drawSource(60, 50, MIXSRC_CH1+flapsCH2, getFieldFlags(2))
250 fieldsMax = 2
252 lcd.drawText(1, 0, "Got flaps?", 0)
253 lcd.drawFilledRectangle(0, 0, LCD_W, 8, FILL_WHITE)
254 lcd.drawCombobox(0, 8, LCD_W, flapsModeItems, flapsMode, getFieldFlags(0))
257 local function flapsMenu(event)
258 if dirty then
259 dirty = false
260 drawFlapsMenu()
263 navigate(event, fieldsMax, page-1, page+1)
265 if field==0 then
266 flapsMode = fieldIncDec(event, flapsMode, 2)
267 elseif field==1 then
268 flapsCH1 = channelIncDec(event, flapsCH1)
269 elseif field==2 then
270 flapsCH2 = channelIncDec(event, flapsCH2)
274 -- Airbrakes Menu
275 local brakesModeItems = {"No", "Yes", "Yes, 2 channels"}
276 local function drawBrakesMenu()
277 lcd.clear()
278 if brakesMode == 0 then
279 -- no brakes
280 fieldsMax = 0
281 elseif brakesMode == 1 then
282 -- 1 channel
283 lcd.drawText(5, 30, "Assign channel", 0);
284 lcd.drawText(5, 40, ">>>", 0);
285 lcd.drawSource(25, 40, MIXSRC_CH1+brakesCH1, getFieldFlags(1))
286 fieldsMax = 1
287 elseif brakesMode == 2 then
288 -- 2 channels
289 lcd.drawText(5, 30, "Assign channels", 0);
290 lcd.drawText(30, 40, "L", 0);
291 lcd.drawText(65, 40, "R", 0);
292 lcd.drawText(5, 50, ">>>", 0);
293 lcd.drawSource(25, 50, MIXSRC_CH1+brakesCH1, getFieldFlags(1))
294 lcd.drawSource(60, 50, MIXSRC_CH1+brakesCH2, getFieldFlags(2))
295 fieldsMax = 2
297 lcd.drawText(1, 0, "Got air brakes?", 0)
298 lcd.drawFilledRectangle(0, 0, LCD_W, 8, FILL_WHITE)
299 lcd.drawCombobox(0, 8, LCD_W, brakesModeItems, brakesMode, getFieldFlags(0))
302 local function brakesMenu(event)
303 if dirty then
304 dirty = false
305 drawBrakesMenu()
308 navigate(event, fieldsMax, page-1, page+1)
310 if field==0 then
311 brakesMode = fieldIncDec(event, brakesMode, 2)
312 elseif field==1 then
313 brakesCH1 = channelIncDec(event, brakesCH1)
314 elseif field==2 then
315 brakesCH2 = channelIncDec(event, brakesCH2)
316 end
319 -- Tail Menu
320 local tailModeItems = {"Ele(1)", "Ele(1) + Ruder(1)", "Ele(2) + Ruder(1)", "V-Tail(2)"}
321 local function drawTailMenu()
322 lcd.clear()
323 if tailMode == 0 then
324 -- Elevator(1ch), no rudder...
325 lcd.drawText(5, 30, "Assign channel", 0);
326 lcd.drawText(5, 40, ">>>", 0);
327 lcd.drawSource(25, 40, MIXSRC_CH1+eleCH1, getFieldFlags(1))
328 fieldsMax = 1
329 elseif tailMode == 1 then
330 -- Elevator(1ch) + rudder...
331 lcd.drawText(5, 30, "Assign channels", 0);
332 lcd.drawText(25, 40, "Ele", 0);
333 lcd.drawText(60, 40, "Rud", 0);
334 lcd.drawText(5, 50, ">>>", 0);
335 lcd.drawSource(25, 50, MIXSRC_CH1+eleCH1, getFieldFlags(1))
336 lcd.drawSource(60, 50, MIXSRC_CH1+rudCH1, getFieldFlags(2))
337 fieldsMax = 2
338 elseif tailMode == 2 then
339 -- Elevator(2ch) + rudder...
340 lcd.drawText(5, 30, "Assign channels", 0);
341 lcd.drawText(25, 40, "EleL", 0);
342 lcd.drawText(60, 40, "EleR", 0);
343 lcd.drawText(95, 40, "Rud", 0);
344 lcd.drawText(5, 50, ">>>", 0);
345 lcd.drawSource(25, 50, MIXSRC_CH1+eleCH1, getFieldFlags(1))
346 lcd.drawSource(60, 50, MIXSRC_CH1+eleCH2, getFieldFlags(2))
347 lcd.drawSource(95, 50, MIXSRC_CH1+rudCH1, getFieldFlags(3))
348 fieldsMax = 3
349 else
350 -- V-Tail...
351 lcd.drawText(5, 30, "Assign channels", 0);
352 lcd.drawText(25, 40, "VtaL", 0);
353 lcd.drawText(60, 40, "VtaR", 0);
354 lcd.drawText(5, 50, ">>>", 0);
355 lcd.drawSource(25, 50, MIXSRC_CH1+eleCH1, getFieldFlags(1))
356 lcd.drawSource(60, 50, MIXSRC_CH1+eleCH2, getFieldFlags(2))
357 fieldsMax = 2
359 lcd.drawText(1, 0, "Tail config", 0)
360 lcd.drawFilledRectangle(0, 0, LCD_W, 8, FILL_WHITE)
361 lcd.drawCombobox(0, 8, LCD_W, tailModeItems, tailMode, getFieldFlags(0))
364 local function tailMenu(event)
365 if dirty then
366 dirty = false
367 drawTailMenu()
370 navigate(event, fieldsMax, page-1, page+1)
372 if field==0 then
373 tailMode = fieldIncDec(event, tailMode, 3)
374 elseif field==1 then
375 eleCH1 = channelIncDec(event, eleCH1)
376 elseif (field==2 and tailMode==1) or field==3 then
377 rudCH1 = channelIncDec(event, rudCH1)
378 elseif field==2 then
379 eleCH2 = channelIncDec(event, eleCH2)
380 end
383 -- Servo (limits) Menu
384 local function drawServoMenu(limits)
385 lcd.clear()
386 lcd.drawSource(1, 0, MIXSRC_CH1+servoPage, 0)
387 lcd.drawText(25, 0, "servo min/max/center/direction?", 0)
388 lcd.drawFilledRectangle(0, 0, LCD_W, 8, FILL_WHITE)
389 lcd.drawLine(LCD_W/2-1, 8, LCD_W/2-1, LCD_H, DOTTED, 0)
390 lcd.drawText(LCD_W/2-19, LCD_H-8, ">>>", 0);
391 lcd.drawNumber(140, 35, limits.min, PREC1+getFieldFlags(0));
392 lcd.drawNumber(205, 35, limits.max, PREC1+getFieldFlags(1));
393 lcd.drawNumber(170, 9, limits.offset, PREC1+getFieldFlags(2));
394 if limits.revert == 0 then
395 lcd.drawText(129, 50, "\126", getFieldFlags(3));
396 else
397 lcd.drawText(129, 50, "\127", getFieldFlags(3));
399 fieldsMax = 3
402 local function servoMenu(event)
403 local limits = model.getOutput(servoPage)
405 if dirty then
406 dirty = false
407 drawServoMenu(limits)
410 navigate(event, fieldsMax, page, page)
412 if edit then
413 if field==0 then
414 limits.min = valueIncDec(event, limits.min, -1000, 0)
415 elseif field==1 then
416 limits.max = valueIncDec(event, limits.max, 0, 1000)
417 elseif field==2 then
418 limits.offset = valueIncDec(event, limits.offset, -1000, 1000)
419 elseif field==3 then
420 limits.revert = fieldIncDec(event, limits.revert, 1)
422 model.setOutput(servoPage, limits)
423 elseif event == EVT_EXIT_BREAK then
424 servoPage = nil
425 dirty = true
429 -- Confirmation Menu
430 local function drawNextLine(x, y, label, channel)
431 lcd.drawText(x, y, label, 0);
432 lcd.drawSource(x+30, y, MIXSRC_CH1+channel, 0)
433 y = y + 8
434 if y > 50 then
435 y = 12
436 x = 70
438 return x, y
441 local function drawConfirmationMenu()
442 local x = 5
443 local y = 12
444 lcd.clear()
445 lcd.drawText(0, 1, "Ready to go?", 0);
446 lcd.drawFilledRectangle(0, 0, LCD_W, 9, 0)
447 if engineMode == 1 then
448 x, y = drawNextLine(x, y, "Thr", thrCH1)
450 if aileronsMode == 1 then
451 x, y = drawNextLine(x, y, "Ail", ailCH1)
452 elseif aileronsMode == 2 then
453 x, y = drawNextLine(x, y, "AilL", ailCH1)
454 x, y = drawNextLine(x, y, "AilR", ailCH2)
456 if flapsMode == 1 then
457 x, y = drawNextLine(x, y, "Flap", flapsCH1)
458 elseif flapsMode == 2 then
459 x, y = drawNextLine(x, y, "FlpL", flapsCH1)
460 x, y = drawNextLine(x, y, "FlpR", flapsCH2)
462 if brakesMode == 1 then
463 x, y = drawNextLine(x, y, "Brak", brakesCH1)
464 elseif brakesMode == 2 then
465 x, y = drawNextLine(x, y, "BrkL", brakesCH1)
466 x, y = drawNextLine(x, y, "BrkR", brakesCH2)
468 if tailMode == 3 then
469 x, y = drawNextLine(x, y, "VtaL", eleCH1)
470 x, y = drawNextLine(x, y, "VtaR", eleCH2)
471 else
472 x, y = drawNextLine(x, y, "Rud", rudCH1)
473 if tailMode == 1 then
474 x, y = drawNextLine(x, y, "Elev", eleCH1)
475 elseif tailMode == 2 then
476 x, y = drawNextLine(x, y, "EleL", eleCH1)
477 x, y = drawNextLine(x, y, "EleR", eleCH2)
480 lcd.drawText(0, LCD_H-8, "[Enter Long] to confirm", 0);
481 lcd.drawFilledRectangle(0, LCD_H-9, LCD_W, 9, 0)
482 fieldsMax = 0
485 local function addMix(channel, input, name, weight, index)
486 local mix = { source=input, name=name }
487 if weight ~= nil then
488 mix.weight = weight
490 if index == nil then
491 index = 0
493 model.insertMix(channel, index, mix)
496 local function applySettings()
497 model.defaultInputs()
498 model.deleteMixes()
499 if engineMode > 0 then
500 addMix(thrCH1, MIXSRC_FIRST_INPUT+defaultChannel(2), "Engine")
502 if aileronsMode == 1 then
503 addMix(ailCH1, MIXSRC_FIRST_INPUT+defaultChannel(3), "Ail")
504 elseif aileronsMode == 2 then
505 addMix(ailCH1, MIXSRC_FIRST_INPUT+defaultChannel(3), "AilL")
506 addMix(ailCH2, MIXSRC_FIRST_INPUT+defaultChannel(3), "AilR", -100)
508 if flapsMode == 1 then
509 addMix(flapsCH1, MIXSRC_SA, "Flap")
510 elseif flapsMode == 2 then
511 addMix(flapsCH1, MIXSRC_SA, "FlapL")
512 addMix(flapsCH2, MIXSRC_SA, "FlapR")
514 if brakesMode == 1 then
515 addMix(brakesCH1, MIXSRC_SD, "Brake")
516 elseif brakesMode == 2 then
517 addMix(brakesCH1, MIXSRC_SD, "BrakeL")
518 addMix(brakesCH2, MIXSRC_SD, "BrakeR")
520 if tailMode == 3 then
521 addMix(eleCH1, MIXSRC_FIRST_INPUT+defaultChannel(1), "V-EleL", 50)
522 addMix(eleCH1, MIXSRC_FIRST_INPUT+defaultChannel(0), "V-RudL", 50, 1)
523 addMix(eleCH2, MIXSRC_FIRST_INPUT+defaultChannel(1), "V-EleR", 50)
524 addMix(eleCH2, MIXSRC_FIRST_INPUT+defaultChannel(0), "V-RudR", -50, 1)
525 else
526 if tailMode > 0 then
527 addMix(rudCH1, MIXSRC_FIRST_INPUT+defaultChannel(0), "Rudder")
529 if tailMode == 1 then
530 addMix(eleCH1, MIXSRC_FIRST_INPUT+defaultChannel(1), "Elev")
531 elseif tailMode == 2 then
532 addMix(eleCH1, MIXSRC_FIRST_INPUT+defaultChannel(1), "ElevG")
533 addMix(eleCH2, MIXSRC_FIRST_INPUT+defaultChannel(1), "ElevD")
538 local function confirmationMenu(event)
539 if dirty then
540 dirty = false
541 drawConfirmationMenu()
544 navigate(event, fieldsMax, TAIL_PAGE, page)
546 if event == EVT_EXIT_BREAK then
547 return 2
548 elseif event == EVT_ENTER_LONG then
549 killEvents(event)
550 applySettings()
551 return 2
552 else
553 return 0
557 -- Main
558 local function run(event)
559 if event == nil then
560 error("Cannot be run as a model script!")
563 if servoPage ~= nil then
564 servoMenu(event)
565 elseif page == ENGINE_PAGE then
566 engineMenu(event)
567 elseif page == AILERONS_PAGE then
568 aileronsMenu(event)
569 elseif page == FLAPERONS_PAGE then
570 flapsMenu(event)
571 elseif page == BRAKES_PAGE then
572 brakesMenu(event)
573 elseif page == TAIL_PAGE then
574 tailMenu(event)
575 elseif page == CONFIRMATION_PAGE then
576 return confirmationMenu(event)
578 return 0
581 return { init=init, run=run }