From 933eee69e49ea54f9e80c1614d9fbcb0e2a8ddea Mon Sep 17 00:00:00 2001 From: Annika Merris Date: Thu, 26 Jun 2025 07:54:22 -0400 Subject: [PATCH] Initial Commit --- .gitignore | 3 + BACKUP/CAR2-2023-10-06.yml | 682 +++++++ FIRMWARE/readme.txt | 2 + LOGS/readme.txt | 1 + MODELS/README.txt | 3 + MODELS/model00.yml | 525 ++++++ MODELS/model01.yml | 395 ++++ MODELS/model02.yml | 550 ++++++ MODELS/model03.yml | 251 +++ MODELS/model04.yml | 232 +++ MODELS/model05.yml | 383 ++++ MODELS/model06.yml | 383 ++++ RADIO/README.txt | 1 + RADIO/radio.yml | 183 ++ SCREENSHOTS/readme.txt | 1 + SCRIPTS/FUNCTIONS/readme.txt | 1 + SCRIPTS/MIXES/readme.txt | 1 + SCRIPTS/RGBLED/Bback.lua | 68 + SCRIPTS/RGBLED/Bfwrd.lua | 68 + SCRIPTS/RGBLED/Pback.lua | 68 + SCRIPTS/RGBLED/Pback.luac | Bin 0 -> 1200 bytes SCRIPTS/RGBLED/Pfwrd.lua | 68 + SCRIPTS/RGBLED/Pfwrd.luac | Bin 0 -> 1052 bytes SCRIPTS/RGBLED/README.md | 1 + SCRIPTS/RGBLED/blue.lua | 16 + SCRIPTS/RGBLED/blue.luac | Bin 0 -> 425 bytes SCRIPTS/RGBLED/flow.lua | 52 + SCRIPTS/RGBLED/green.lua | 16 + SCRIPTS/RGBLED/green.luac | Bin 0 -> 385 bytes SCRIPTS/RGBLED/off.lua | 16 + SCRIPTS/RGBLED/orange.lua | 16 + SCRIPTS/RGBLED/orange.luac | Bin 0 -> 390 bytes SCRIPTS/RGBLED/police.lua | 26 + SCRIPTS/RGBLED/police.luac | Bin 0 -> 588 bytes SCRIPTS/RGBLED/purple.lua | 16 + SCRIPTS/RGBLED/rainbw.lua | 52 + SCRIPTS/RGBLED/rainbw.luac | Bin 0 -> 793 bytes SCRIPTS/RGBLED/red.lua | 16 + SCRIPTS/RGBLED/red.luac | Bin 0 -> 385 bytes SCRIPTS/RGBLED/rgbLop.lua | 53 + SCRIPTS/RGBLED/rgbLop.luac | Bin 0 -> 902 bytes SCRIPTS/RGBLED/runner.lua | 77 + SCRIPTS/RGBLED/runner.luac | Bin 0 -> 1173 bytes SCRIPTS/RGBLED/sapp.lua | 16 + SCRIPTS/RGBLED/white.lua | 16 + SCRIPTS/RGBLED/yellow.lua | 16 + SCRIPTS/RGBLED/yellow.luac | Bin 0 -> 385 bytes SCRIPTS/TELEMETRY/gplusl.lua | 63 + SCRIPTS/TELEMETRY/readme.txt | 2 + SCRIPTS/TOOLS/BREAKOUT/gfx/gover1.bmp | Bin 0 -> 574 bytes SCRIPTS/TOOLS/BREAKOUT/gfx/gover2.bmp | Bin 0 -> 574 bytes SCRIPTS/TOOLS/BREAKOUT/gfx/splash1.bmp | Bin 0 -> 574 bytes SCRIPTS/TOOLS/BREAKOUT/gfx/splash2.bmp | Bin 0 -> 574 bytes SCRIPTS/TOOLS/BREAKOUT/gfx/up1.bmp | Bin 0 -> 574 bytes SCRIPTS/TOOLS/BREAKOUT/gfx/up2.bmp | Bin 0 -> 574 bytes SCRIPTS/TOOLS/DSM FwdPrg_55_MIN.lua | 1046 +++++++++++ SCRIPTS/TOOLS/DSM FwdPrg_56_Color.lua | 790 ++++++++ SCRIPTS/TOOLS/DSM FwdPrg_56_MIN.lua | 1088 +++++++++++ SCRIPTS/TOOLS/DSM FwdPrg_56_STUP.lua | 951 ++++++++++ SCRIPTS/TOOLS/DSMLIB/DsmFwPrgLib.lua | 813 +++++++++ SCRIPTS/TOOLS/DSMLIB/DsmLogLib.lua | 33 + SCRIPTS/TOOLS/DSMLIB/DsmMIN_P1.lua | 167 ++ SCRIPTS/TOOLS/DSMLIB/DsmMIN_P2.lua | 282 +++ SCRIPTS/TOOLS/DSMLIB/DsmMainMenuLib.lua | 90 + SCRIPTS/TOOLS/DSMLIB/DsmMenuLib.lua | 788 ++++++++ SCRIPTS/TOOLS/DSMLIB/DsmModelLib.lua | 789 ++++++++ SCRIPTS/TOOLS/DSMLIB/DsmSetupMenuLib.lua | 467 +++++ SCRIPTS/TOOLS/DSMLIB/DsmSimMenuLib.lua | 1596 +++++++++++++++++ SCRIPTS/TOOLS/DSMLIB/MIN_msg_fwdp_en.txt | 10 + SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_1.png | Bin 0 -> 1408 bytes SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_2.png | Bin 0 -> 862 bytes SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_3.png | Bin 0 -> 1162 bytes SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_4.png | Bin 0 -> 866 bytes SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_5.png | Bin 0 -> 1464 bytes SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_6.png | Bin 0 -> 880 bytes SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_7.png | Bin 0 -> 1213 bytes SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_8.png | Bin 0 -> 863 bytes SCRIPTS/TOOLS/DSMLIB/img/h_swp_3_120.png | Bin 0 -> 1361 bytes SCRIPTS/TOOLS/DSMLIB/img/h_swp_3_120inv.png | Bin 0 -> 1374 bytes SCRIPTS/TOOLS/DSMLIB/img/h_swp_3_135.png | Bin 0 -> 1350 bytes SCRIPTS/TOOLS/DSMLIB/img/h_swp_3_135inv.png | Bin 0 -> 1361 bytes SCRIPTS/TOOLS/DSMLIB/img/h_swp_3_140.png | Bin 0 -> 1332 bytes SCRIPTS/TOOLS/DSMLIB/img/h_swp_3_140inv.png | Bin 0 -> 1341 bytes SCRIPTS/TOOLS/DSMLIB/img/h_swp_3_90.png | Bin 0 -> 1251 bytes SCRIPTS/TOOLS/DSMLIB/img/h_swp_3_90inv.png | Bin 0 -> 1272 bytes SCRIPTS/TOOLS/DSMLIB/img/h_swp_norm.png | Bin 0 -> 1254 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_1.png | Bin 0 -> 2725 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_10.png | Bin 0 -> 2805 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_11.png | Bin 0 -> 3042 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_12.png | Bin 0 -> 2781 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_13.png | Bin 0 -> 2677 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_14.png | Bin 0 -> 2811 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_15.png | Bin 0 -> 2999 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_16.png | Bin 0 -> 2776 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_17.png | Bin 0 -> 2629 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_18.png | Bin 0 -> 2619 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_19.png | Bin 0 -> 2622 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_2.png | Bin 0 -> 2536 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_20.png | Bin 0 -> 2674 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_21.png | Bin 0 -> 2735 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_22.png | Bin 0 -> 2698 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_23.png | Bin 0 -> 2743 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_24.png | Bin 0 -> 2721 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_25.png | Bin 0 -> 2696 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_3.png | Bin 0 -> 2992 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_4.png | Bin 0 -> 2540 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_5.png | Bin 0 -> 2575 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_6.png | Bin 0 -> 2548 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_7.png | Bin 0 -> 2995 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_8.png | Bin 0 -> 2547 bytes SCRIPTS/TOOLS/DSMLIB/img/rx_pos_9.png | Bin 0 -> 2834 bytes SCRIPTS/TOOLS/DSMLIB/img/tt_1rud.png | Bin 0 -> 728 bytes SCRIPTS/TOOLS/DSMLIB/img/tt_1rud_1ele.png | Bin 0 -> 1229 bytes SCRIPTS/TOOLS/DSMLIB/img/tt_1rud_2ele.png | Bin 0 -> 1115 bytes SCRIPTS/TOOLS/DSMLIB/img/tt_2rud_1ele.png | Bin 0 -> 1401 bytes SCRIPTS/TOOLS/DSMLIB/img/tt_2rud_2ele.png | Bin 0 -> 1359 bytes SCRIPTS/TOOLS/DSMLIB/img/tt_taileron.png | Bin 0 -> 1126 bytes SCRIPTS/TOOLS/DSMLIB/img/tt_taileron2.png | Bin 0 -> 1382 bytes SCRIPTS/TOOLS/DSMLIB/img/tt_vtail.png | Bin 0 -> 1293 bytes SCRIPTS/TOOLS/DSMLIB/img/wt_1ail.png | Bin 0 -> 721 bytes SCRIPTS/TOOLS/DSMLIB/img/wt_1ail_1flp.png | Bin 0 -> 803 bytes SCRIPTS/TOOLS/DSMLIB/img/wt_2ail.png | Bin 0 -> 679 bytes SCRIPTS/TOOLS/DSMLIB/img/wt_2ail_1flp.png | Bin 0 -> 755 bytes SCRIPTS/TOOLS/DSMLIB/img/wt_2ail_2flp.png | Bin 0 -> 817 bytes SCRIPTS/TOOLS/DSMLIB/img/wt_elevon.png | Bin 0 -> 1055 bytes SCRIPTS/TOOLS/DSMLIB/img/wt_flaperon.png | Bin 0 -> 679 bytes SCRIPTS/TOOLS/DSMLIB/msg_fwdp_en.txt | 437 +++++ SCRIPTS/TOOLS/DSM_AR636_Tel.lua | 683 +++++++ SCRIPTS/TOOLS/DSM_SmartRX_Tel.lua | 603 +++++++ SCRIPTS/TOOLS/Game-Breakout.lua | 508 ++++++ SCRIPTS/TOOLS/Game-Breakout.luac | Bin 0 -> 10238 bytes SCRIPTS/TOOLS/Graupner HoTT Model Locator.lua | 167 ++ SCRIPTS/TOOLS/Graupner HoTT.lua | 171 ++ SCRIPTS/TOOLS/ModelLocator.lua | 157 ++ SCRIPTS/TOOLS/ModelLocator.luac | Bin 0 -> 1738 bytes SCRIPTS/TOOLS/MultiChan.txt | 233 +++ SCRIPTS/TOOLS/MultiChannelsUpdater.lua | 327 ++++ SCRIPTS/TOOLS/MultiConfig.lua | 533 ++++++ SCRIPTS/TOOLS/MultiLOLI.lua | 221 +++ SCRIPTS/TOOLS/TBSAGENTLITE/LIB/error.luac | Bin 0 -> 1023 bytes SCRIPTS/TOOLS/TBSAGENTLITE/LIB/file.luac | Bin 0 -> 1324 bytes SCRIPTS/TOOLS/TBSAGENTLITE/LIB/gui.luac | Bin 0 -> 1198 bytes SCRIPTS/TOOLS/TBSAGENTLITE/LIB/radio.luac | Bin 0 -> 741 bytes SCRIPTS/TOOLS/TBSAGENTLITE/LIB/winBW.luac | Bin 0 -> 4963 bytes SCRIPTS/TOOLS/TBSAGENTLITE/LIB/winC.luac | Bin 0 -> 4909 bytes .../TOOLS/TBSAGENTLITE/SETUP/GFX/C/Fusion.png | Bin 0 -> 8032 bytes .../TBSAGENTLITE/SETUP/GFX/C/TBSAgentLite.png | Bin 0 -> 13931 bytes .../TBSAGENTLITE/SETUP/GFX/C/TBSwifi.png | Bin 0 -> 4590 bytes .../TOOLS/TBSAGENTLITE/SETUP/GFX/C/TRrxm.png | Bin 0 -> 9162 bytes .../TOOLS/TBSAGENTLITE/SETUP/GFX/C/TRtxm.png | Bin 0 -> 6739 bytes .../TBSAGENTLITE/SETUP/GFX/C/XFTango2.png | Bin 0 -> 9540 bytes .../TOOLS/TBSAGENTLITE/SETUP/GFX/C/XFardu.png | Bin 0 -> 10209 bytes .../TOOLS/TBSAGENTLITE/SETUP/GFX/C/XFkiss.png | Bin 0 -> 4638 bytes .../TOOLS/TBSAGENTLITE/SETUP/GFX/C/XFrxm.png | Bin 0 -> 10723 bytes .../TOOLS/TBSAGENTLITE/SETUP/GFX/C/XFrxmd.png | Bin 0 -> 10640 bytes .../TOOLS/TBSAGENTLITE/SETUP/GFX/C/XFtxf.png | Bin 0 -> 9540 bytes .../TOOLS/TBSAGENTLITE/SETUP/GFX/C/XFtxm.png | Bin 0 -> 5561 bytes .../TBSAGENTLITE/SETUP/GFX/C/XFu32hv.png | Bin 0 -> 13017 bytes .../TOOLS/TBSAGENTLITE/SETUP/GFX/C/XFu32n.png | Bin 0 -> 13539 bytes .../TOOLS/TBSAGENTLITE/SETUP/GFX/C/XFuevo.png | Bin 0 -> 13483 bytes .../TOOLS/TBSAGENTLITE/SETUP/GFX/C/cloud.png | Bin 0 -> 239 bytes .../TOOLS/TBSAGENTLITE/SETUP/GFX/C/frame.png | Bin 0 -> 764 bytes .../TOOLS/TBSAGENTLITE/SETUP/GFX/C/txmod.png | Bin 0 -> 315 bytes .../TBSAGENTLITE/SETUP/GFX/C/wgtlogo.png | Bin 0 -> 1314 bytes .../TBSAGENTLITE/SETUP/GFX/C/wgtlogoOFF.png | Bin 0 -> 295 bytes .../TBSAGENTLITE/SETUP/GFX/C/wgtlogoON.png | Bin 0 -> 291 bytes .../TOOLS/TBSAGENTLITE/SETUP/GFX/GS/cloud.bmp | Bin 0 -> 232 bytes .../SETUP/GFX/GS/tbs_splashGS1.bmp | Bin 0 -> 3704 bytes .../SETUP/GFX/GS/tbs_splashGS2.bmp | Bin 0 -> 3704 bytes .../TOOLS/TBSAGENTLITE/SETUP/GFX/GS/tr.bmp | Bin 0 -> 232 bytes .../TOOLS/TBSAGENTLITE/SETUP/GFX/GS/wifi.bmp | Bin 0 -> 232 bytes .../TOOLS/TBSAGENTLITE/SETUP/GFX/GS/xf.bmp | Bin 0 -> 232 bytes SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/0.luac | Bin 0 -> 1559 bytes SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/10.luac | Bin 0 -> 275 bytes SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/11.luac | Bin 0 -> 316 bytes SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/13.luac | Bin 0 -> 367 bytes SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/9.luac | Bin 0 -> 617 bytes SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/dev.luac | Bin 0 -> 2164 bytes SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/fld.luac | Bin 0 -> 1900 bytes SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/main.luac | Bin 0 -> 3954 bytes .../TOOLS/TBSAGENTLITE/SETUP/UI/cuiBW.luac | Bin 0 -> 3516 bytes SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/cuiC.luac | Bin 0 -> 5699 bytes .../TOOLS/TBSAGENTLITE/SETUP/UI/cuiGS.luac | Bin 0 -> 4050 bytes SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/main.luac | Bin 0 -> 4838 bytes .../TOOLS/TBSAGENTLITE/SETUP/UI/popBW10.luac | Bin 0 -> 4572 bytes .../TOOLS/TBSAGENTLITE/SETUP/UI/popBW13.luac | Bin 0 -> 2490 bytes .../TOOLS/TBSAGENTLITE/SETUP/UI/popBW8.luac | Bin 0 -> 2402 bytes .../TOOLS/TBSAGENTLITE/SETUP/UI/popC10.luac | Bin 0 -> 4942 bytes .../TOOLS/TBSAGENTLITE/SETUP/UI/popC13.luac | Bin 0 -> 2651 bytes .../TOOLS/TBSAGENTLITE/SETUP/UI/popC8.luac | Bin 0 -> 2797 bytes .../TOOLS/TBSAGENTLITE/SETUP/UI/popGS10.luac | Bin 0 -> 4489 bytes .../TOOLS/TBSAGENTLITE/SETUP/UI/popGS13.luac | Bin 0 -> 2192 bytes .../TOOLS/TBSAGENTLITE/SETUP/UI/popGS8.luac | Bin 0 -> 2307 bytes SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/setup.luac | Bin 0 -> 644 bytes SCRIPTS/TOOLS/TBSAGENTLITE/loader.luac | Bin 0 -> 2570 bytes SCRIPTS/TOOLS/TBSAGENTLITE/splash.luac | Bin 0 -> 1843 bytes SCRIPTS/TOOLS/TBSAgentLite.lua | 8 + SCRIPTS/TOOLS/TBSAgentLite.luac | Bin 0 -> 211 bytes SCRIPTS/TOOLS/WizardLoader.lua | 11 + SCRIPTS/TOOLS/WizardLoader.luac | Bin 0 -> 296 bytes SCRIPTS/TOOLS/elrsV3.lua | 945 ++++++++++ SCRIPTS/TOOLS/elrsV3.luac | Bin 0 -> 15774 bytes SCRIPTS/TOOLS/pidDsm.lua | 127 ++ SCRIPTS/TOOLS/readme.txt | 1 + SCRIPTS/WIZARD/delta.lua | 383 ++++ SCRIPTS/WIZARD/heli.lua | 533 ++++++ SCRIPTS/WIZARD/multi.lua | 440 +++++ SCRIPTS/WIZARD/plane.lua | 582 ++++++ SCRIPTS/WIZARD/wizard.lua | 87 + SCRIPTS/WIZARD/wizard.luac | Bin 0 -> 1349 bytes SCRIPTS/simulator.txt | 1 + SCRIPTS/snake.lua | 176 ++ SOUNDS/README.txt | 2 + SOUNDS/edgetx.sounds.version | 1 + edgetx.sdcard.target | 1 + edgetx.sdcard.version | 1 + 216 files changed, 20588 insertions(+) create mode 100644 .gitignore create mode 100755 BACKUP/CAR2-2023-10-06.yml create mode 100755 FIRMWARE/readme.txt create mode 100755 LOGS/readme.txt create mode 100755 MODELS/README.txt create mode 100644 MODELS/model00.yml create mode 100644 MODELS/model01.yml create mode 100644 MODELS/model02.yml create mode 100644 MODELS/model03.yml create mode 100644 MODELS/model04.yml create mode 100644 MODELS/model05.yml create mode 100755 MODELS/model06.yml create mode 100755 RADIO/README.txt create mode 100755 RADIO/radio.yml create mode 100755 SCREENSHOTS/readme.txt create mode 100755 SCRIPTS/FUNCTIONS/readme.txt create mode 100755 SCRIPTS/MIXES/readme.txt create mode 100755 SCRIPTS/RGBLED/Bback.lua create mode 100755 SCRIPTS/RGBLED/Bfwrd.lua create mode 100755 SCRIPTS/RGBLED/Pback.lua create mode 100755 SCRIPTS/RGBLED/Pback.luac create mode 100755 SCRIPTS/RGBLED/Pfwrd.lua create mode 100755 SCRIPTS/RGBLED/Pfwrd.luac create mode 100755 SCRIPTS/RGBLED/README.md create mode 100755 SCRIPTS/RGBLED/blue.lua create mode 100755 SCRIPTS/RGBLED/blue.luac create mode 100755 SCRIPTS/RGBLED/flow.lua create mode 100755 SCRIPTS/RGBLED/green.lua create mode 100755 SCRIPTS/RGBLED/green.luac create mode 100755 SCRIPTS/RGBLED/off.lua create mode 100755 SCRIPTS/RGBLED/orange.lua create mode 100644 SCRIPTS/RGBLED/orange.luac create mode 100755 SCRIPTS/RGBLED/police.lua create mode 100755 SCRIPTS/RGBLED/police.luac create mode 100755 SCRIPTS/RGBLED/purple.lua create mode 100755 SCRIPTS/RGBLED/rainbw.lua create mode 100755 SCRIPTS/RGBLED/rainbw.luac create mode 100755 SCRIPTS/RGBLED/red.lua create mode 100755 SCRIPTS/RGBLED/red.luac create mode 100755 SCRIPTS/RGBLED/rgbLop.lua create mode 100644 SCRIPTS/RGBLED/rgbLop.luac create mode 100755 SCRIPTS/RGBLED/runner.lua create mode 100755 SCRIPTS/RGBLED/runner.luac create mode 100755 SCRIPTS/RGBLED/sapp.lua create mode 100755 SCRIPTS/RGBLED/white.lua create mode 100755 SCRIPTS/RGBLED/yellow.lua create mode 100644 SCRIPTS/RGBLED/yellow.luac create mode 100755 SCRIPTS/TELEMETRY/gplusl.lua create mode 100755 SCRIPTS/TELEMETRY/readme.txt create mode 100755 SCRIPTS/TOOLS/BREAKOUT/gfx/gover1.bmp create mode 100755 SCRIPTS/TOOLS/BREAKOUT/gfx/gover2.bmp create mode 100755 SCRIPTS/TOOLS/BREAKOUT/gfx/splash1.bmp create mode 100755 SCRIPTS/TOOLS/BREAKOUT/gfx/splash2.bmp create mode 100755 SCRIPTS/TOOLS/BREAKOUT/gfx/up1.bmp create mode 100755 SCRIPTS/TOOLS/BREAKOUT/gfx/up2.bmp create mode 100644 SCRIPTS/TOOLS/DSM FwdPrg_55_MIN.lua create mode 100644 SCRIPTS/TOOLS/DSM FwdPrg_56_Color.lua create mode 100644 SCRIPTS/TOOLS/DSM FwdPrg_56_MIN.lua create mode 100644 SCRIPTS/TOOLS/DSM FwdPrg_56_STUP.lua create mode 100644 SCRIPTS/TOOLS/DSMLIB/DsmFwPrgLib.lua create mode 100644 SCRIPTS/TOOLS/DSMLIB/DsmLogLib.lua create mode 100644 SCRIPTS/TOOLS/DSMLIB/DsmMIN_P1.lua create mode 100644 SCRIPTS/TOOLS/DSMLIB/DsmMIN_P2.lua create mode 100644 SCRIPTS/TOOLS/DSMLIB/DsmMainMenuLib.lua create mode 100644 SCRIPTS/TOOLS/DSMLIB/DsmMenuLib.lua create mode 100644 SCRIPTS/TOOLS/DSMLIB/DsmModelLib.lua create mode 100644 SCRIPTS/TOOLS/DSMLIB/DsmSetupMenuLib.lua create mode 100644 SCRIPTS/TOOLS/DSMLIB/DsmSimMenuLib.lua create mode 100644 SCRIPTS/TOOLS/DSMLIB/MIN_msg_fwdp_en.txt create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_1.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_2.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_3.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_4.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_5.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_6.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_7.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_8.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/h_swp_3_120.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/h_swp_3_120inv.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/h_swp_3_135.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/h_swp_3_135inv.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/h_swp_3_140.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/h_swp_3_140inv.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/h_swp_3_90.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/h_swp_3_90inv.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/h_swp_norm.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_1.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_10.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_11.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_12.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_13.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_14.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_15.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_16.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_17.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_18.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_19.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_2.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_20.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_21.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_22.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_23.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_24.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_25.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_3.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_4.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_5.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_6.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_7.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_8.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/rx_pos_9.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/tt_1rud.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/tt_1rud_1ele.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/tt_1rud_2ele.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/tt_2rud_1ele.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/tt_2rud_2ele.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/tt_taileron.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/tt_taileron2.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/tt_vtail.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/wt_1ail.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/wt_1ail_1flp.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/wt_2ail.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/wt_2ail_1flp.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/wt_2ail_2flp.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/wt_elevon.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/img/wt_flaperon.png create mode 100644 SCRIPTS/TOOLS/DSMLIB/msg_fwdp_en.txt create mode 100644 SCRIPTS/TOOLS/DSM_AR636_Tel.lua create mode 100644 SCRIPTS/TOOLS/DSM_SmartRX_Tel.lua create mode 100755 SCRIPTS/TOOLS/Game-Breakout.lua create mode 100755 SCRIPTS/TOOLS/Game-Breakout.luac create mode 100644 SCRIPTS/TOOLS/Graupner HoTT Model Locator.lua create mode 100644 SCRIPTS/TOOLS/Graupner HoTT.lua create mode 100755 SCRIPTS/TOOLS/ModelLocator.lua create mode 100755 SCRIPTS/TOOLS/ModelLocator.luac create mode 100644 SCRIPTS/TOOLS/MultiChan.txt create mode 100644 SCRIPTS/TOOLS/MultiChannelsUpdater.lua create mode 100644 SCRIPTS/TOOLS/MultiConfig.lua create mode 100644 SCRIPTS/TOOLS/MultiLOLI.lua create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/LIB/error.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/LIB/file.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/LIB/gui.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/LIB/radio.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/LIB/winBW.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/LIB/winC.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/Fusion.png create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/TBSAgentLite.png create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/TBSwifi.png create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/TRrxm.png create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/TRtxm.png create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/XFTango2.png create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/XFardu.png create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/XFkiss.png create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/XFrxm.png create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/XFrxmd.png create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/XFtxf.png create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/XFtxm.png create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/XFu32hv.png create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/XFu32n.png create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/XFuevo.png create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/cloud.png create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/frame.png create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/txmod.png create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/wgtlogo.png create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/wgtlogoOFF.png create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/wgtlogoON.png create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/GS/cloud.bmp create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/GS/tbs_splashGS1.bmp create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/GS/tbs_splashGS2.bmp create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/GS/tr.bmp create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/GS/wifi.bmp create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/GS/xf.bmp create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/0.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/10.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/11.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/13.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/9.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/dev.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/fld.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/main.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/cuiBW.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/cuiC.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/cuiGS.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/main.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/popBW10.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/popBW13.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/popBW8.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/popC10.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/popC13.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/popC8.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/popGS10.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/popGS13.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/popGS8.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/setup.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/loader.luac create mode 100755 SCRIPTS/TOOLS/TBSAGENTLITE/splash.luac create mode 100755 SCRIPTS/TOOLS/TBSAgentLite.lua create mode 100755 SCRIPTS/TOOLS/TBSAgentLite.luac create mode 100755 SCRIPTS/TOOLS/WizardLoader.lua create mode 100755 SCRIPTS/TOOLS/WizardLoader.luac create mode 100755 SCRIPTS/TOOLS/elrsV3.lua create mode 100755 SCRIPTS/TOOLS/elrsV3.luac create mode 100644 SCRIPTS/TOOLS/pidDsm.lua create mode 100755 SCRIPTS/TOOLS/readme.txt create mode 100755 SCRIPTS/WIZARD/delta.lua create mode 100755 SCRIPTS/WIZARD/heli.lua create mode 100755 SCRIPTS/WIZARD/multi.lua create mode 100755 SCRIPTS/WIZARD/plane.lua create mode 100755 SCRIPTS/WIZARD/wizard.lua create mode 100755 SCRIPTS/WIZARD/wizard.luac create mode 100755 SCRIPTS/simulator.txt create mode 100755 SCRIPTS/snake.lua create mode 100755 SOUNDS/README.txt create mode 100755 SOUNDS/edgetx.sounds.version create mode 100755 edgetx.sdcard.target create mode 100755 edgetx.sdcard.version diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6c7c974 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/FIRMWARE/*.bin +*.wav +.DS_Store diff --git a/BACKUP/CAR2-2023-10-06.yml b/BACKUP/CAR2-2023-10-06.yml new file mode 100755 index 0000000..f66dbd2 --- /dev/null +++ b/BACKUP/CAR2-2023-10-06.yml @@ -0,0 +1,682 @@ +semver: 2.10.0 +header: + name: "CAR2" +timers: + 0: + start: 300 + swtch: "L1" + value: 187 + mode: ON + countdownBeep: 2 + minuteBeep: 1 + persistent: 2 + countdownStart: 1 + showElapsed: 0 + extraHaptic: 0 + name: "Tsb" +telemetryProtocol: 0 +thrTrim: 0 +noGlobalFunctions: 0 +displayTrims: 2 +ignoreSensorIds: 0 +trimInc: 0 +disableThrottleWarning: 0 +displayChecklist: 0 +extendedLimits: 1 +extendedTrims: 0 +throttleReversed: 0 +enableCustomThrottleWarning: 0 +disableTelemetryWarning: 0 +showInstanceIds: 0 +customThrottleWarningPosition: 0 +beepANACenter: 0 +mixData: + - + weight: 100 + destCh: 0 + srcRaw: I0 + carryTrim: 0 + mixWarn: 0 + mltpx: ADD + offset: 0 + swtch: "NONE" + flightModes: 000000000 + delayUp: 0 + delayDown: 0 + speedUp: 0 + speedDown: 0 + name: "" + - + weight: 100 + destCh: 1 + srcRaw: I1 + carryTrim: 0 + mixWarn: 0 + mltpx: ADD + offset: 0 + swtch: "NONE" + flightModes: 000000000 + delayUp: 0 + delayDown: 0 + speedUp: 0 + speedDown: 0 + name: "" + - + weight: 100 + destCh: 2 + srcRaw: I2 + carryTrim: 0 + mixWarn: 0 + mltpx: ADD + offset: 0 + swtch: "NONE" + flightModes: 000000000 + delayUp: 0 + delayDown: 0 + speedUp: 0 + speedDown: 0 + name: "" + - + weight: 100 + destCh: 3 + srcRaw: SB + carryTrim: 0 + mixWarn: 0 + mltpx: ADD + offset: 0 + swtch: "NONE" + flightModes: 000000000 + delayUp: 0 + delayDown: 0 + speedUp: 0 + speedDown: 0 + name: "" + - + weight: 100 + destCh: 4 + srcRaw: I4 + carryTrim: 0 + mixWarn: 0 + mltpx: ADD + offset: 0 + swtch: "NONE" + flightModes: 000000000 + delayUp: 0 + delayDown: 0 + speedUp: 0 + speedDown: 0 + name: "" + - + weight: 100 + destCh: 5 + srcRaw: I5 + carryTrim: 0 + mixWarn: 0 + mltpx: ADD + offset: 0 + swtch: "NONE" + flightModes: 000000000 + delayUp: 0 + delayDown: 0 + speedUp: 0 + speedDown: 0 + name: "" + - + weight: 100 + destCh: 6 + srcRaw: SC + carryTrim: 0 + mixWarn: 0 + mltpx: ADD + offset: 0 + swtch: "NONE" + flightModes: 000000000 + delayUp: 0 + delayDown: 0 + speedUp: 0 + speedDown: 0 + name: "" + - + weight: 100 + destCh: 7 + srcRaw: SD + carryTrim: 0 + mixWarn: 0 + mltpx: ADD + offset: 0 + swtch: "NONE" + flightModes: 000000000 + delayUp: 0 + delayDown: 0 + speedUp: 0 + speedDown: 0 + name: "" +limitData: + 0: + min: 0 + max: 0 + ppmCenter: 0 + offset: 0 + symetrical: 0 + revert: 1 + curve: 0 + name: "" +expoData: + - + mode: 3 + scale: 0 + trimSource: 0 + srcRaw: TH + chn: 0 + swtch: "NONE" + flightModes: 000000000 + weight: 100 + name: "" + offset: 0 + curve: + type: 1 + value: 0 + - + mode: 3 + scale: 0 + trimSource: 0 + srcRaw: ST + chn: 1 + swtch: "NONE" + flightModes: 000000000 + weight: 100 + name: "" + offset: 0 + curve: + type: 1 + value: 0 + - + mode: 3 + scale: 0 + trimSource: 0 + srcRaw: SA + chn: 2 + swtch: "NONE" + flightModes: 000000000 + weight: 100 + name: "" + offset: 0 + curve: + type: 1 + value: 0 + - + mode: 3 + scale: 0 + trimSource: 0 + srcRaw: SB + chn: 3 + swtch: "NONE" + flightModes: 000000000 + weight: 100 + name: "" + offset: 0 + curve: + type: 1 + value: 0 + - + mode: 3 + scale: 0 + trimSource: 0 + srcRaw: P1 + chn: 4 + swtch: "NONE" + flightModes: 000000000 + weight: 100 + name: "" + offset: 0 + curve: + type: 1 + value: 0 + - + mode: 3 + scale: 0 + trimSource: 0 + srcRaw: P2 + chn: 5 + swtch: "NONE" + flightModes: 000000000 + weight: 100 + name: "" + offset: 0 + curve: + type: 1 + value: 0 + - + mode: 3 + scale: 0 + trimSource: 0 + srcRaw: SC + chn: 6 + swtch: "NONE" + flightModes: 000000000 + weight: 100 + name: "" + offset: 0 + curve: + type: 1 + value: 0 + - + mode: 3 + scale: 0 + trimSource: 0 + srcRaw: SD + chn: 7 + swtch: "NONE" + flightModes: 000000000 + weight: 100 + name: "" + offset: 0 + curve: + type: 1 + value: 0 +curves: + 31: + type: 1 + smooth: 0 + points: -1 + name: "CNT" +points: + 155: + val: -100 + 158: + val: 100 + 159: + val: -1 + 160: + val: 1 +logicalSw: + 0: + func: FUNC_STICKY + def: "SB0,SB0" + andsw: "NONE" + delay: 0 + duration: 0 + 1: + func: FUNC_VNEG + def: "I0,-5" + andsw: "NONE" + delay: 0 + duration: 0 + 2: + func: FUNC_VPOS + def: "TH,5" + andsw: "NONE" + delay: 0 + duration: 0 + 3: + func: FUNC_VNEG + def: "I0,-75" + andsw: "NONE" + delay: 0 + duration: 0 + 4: + func: FUNC_AND + def: "!L3,!L2" + andsw: "NONE" + delay: 0 + duration: 0 +customFn: + 0: + swtch: "L1" + func: PLAY_TRACK + def: "start,1x" + 1: + swtch: "!L1" + func: PLAY_TRACK + def: "stop,1x" + 2: + swtch: "L2" + func: RGB_LED + def: "red" + 3: + swtch: "L3" + func: RGB_LED + def: "blue" + 4: + swtch: "L4" + func: RGB_LED + def: "police" + 5: + swtch: "L5" + func: RGB_LED + def: "green" +thrTraceSrc: TH +switchWarningState: AuBuCuDu +rssiSource: none +rfAlarms: + warning: 45 + critical: 42 +thrTrimSw: 0 +potsWarnMode: WARN_OFF +jitterFilter: GLOBAL +moduleData: + 0: + type: TYPE_CROSSFIRE + subType: 0 + channelsStart: 0 + channelsCount: 16 + failsafeMode: NOT_SET + mod: + crsf: + telemetryBaudrate: 0 +trainerData: + mode: SLAVE + channelsStart: 0 + channelsCount: 0 + frameLength: 0 + delay: 0 + pulsePol: 0 +inputNames: + 0: + val: "Th" + 1: + val: "St" + 2: + val: "Sa" + 3: + val: "Sb" +potsWarnEnabled: 0 +telemetrySensors: + 0: + id1: + id: 20 + id2: + instance: 0 + label: "1RSS" + subId: 0 + type: TYPE_CUSTOM + unit: 17 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 1: + id1: + id: 20 + id2: + instance: 1 + label: "2RSS" + subId: 0 + type: TYPE_CUSTOM + unit: 17 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 2: + id1: + id: 20 + id2: + instance: 2 + label: "RQly" + subId: 0 + type: TYPE_CUSTOM + unit: 13 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 3: + id1: + id: 20 + id2: + instance: 3 + label: "RSNR" + subId: 0 + type: TYPE_CUSTOM + unit: 17 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 4: + id1: + id: 20 + id2: + instance: 4 + label: "ANT" + subId: 0 + type: TYPE_CUSTOM + unit: 0 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 5: + id1: + id: 20 + id2: + instance: 5 + label: "RFMD" + subId: 0 + type: TYPE_CUSTOM + unit: 0 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 6: + id1: + id: 20 + id2: + instance: 6 + label: "TPWR" + subId: 0 + type: TYPE_CUSTOM + unit: 16 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 7: + id1: + id: 20 + id2: + instance: 7 + label: "TRSS" + subId: 0 + type: TYPE_CUSTOM + unit: 17 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 8: + id1: + id: 20 + id2: + instance: 8 + label: "TQly" + subId: 0 + type: TYPE_CUSTOM + unit: 13 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 9: + id1: + id: 20 + id2: + instance: 9 + label: "TSNR" + subId: 0 + type: TYPE_CUSTOM + unit: 17 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 10: + id1: + id: 8 + id2: + instance: 0 + label: "RxBt" + subId: 0 + type: TYPE_CUSTOM + unit: 1 + prec: 1 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 11: + id1: + id: 8 + id2: + instance: 1 + label: "Curr" + subId: 0 + type: TYPE_CUSTOM + unit: 2 + prec: 1 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 12: + id1: + id: 8 + id2: + instance: 2 + label: "Capa" + subId: 0 + type: TYPE_CUSTOM + unit: 14 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 13: + id1: + id: 8 + id2: + instance: 3 + label: "Bat%" + subId: 0 + type: TYPE_CUSTOM + unit: 13 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 +screens: + 0: + type: VALUES + u: + lines: + 0: + sources: + 0: + val: tele(10) + 1: + val: tele(2) + 1: + sources: + 0: + val: Tmr1 + 1: + val: tele(6) +view: 0 +modelRegistrationID: "*\x15*\x15!\x03%\x07" +usbJoystickExtMode: 0 +usbJoystickIfMode: JOYSTICK +usbJoystickCircularCut: 0 +radioGFDisabled: GLOBAL +radioTrainerDisabled: GLOBAL +modelHeliDisabled: GLOBAL +modelFMDisabled: GLOBAL +modelCurvesDisabled: GLOBAL +modelGVDisabled: GLOBAL +modelLSDisabled: GLOBAL +modelSFDisabled: GLOBAL +modelCustomScriptsDisabled: GLOBAL +modelTelemetryDisabled: GLOBAL diff --git a/FIRMWARE/readme.txt b/FIRMWARE/readme.txt new file mode 100755 index 0000000..13e3c64 --- /dev/null +++ b/FIRMWARE/readme.txt @@ -0,0 +1,2 @@ +You can put firmware for your modules or radio in this folder. +Files in this folder are NOT automatically applied. diff --git a/LOGS/readme.txt b/LOGS/readme.txt new file mode 100755 index 0000000..fb26ce7 --- /dev/null +++ b/LOGS/readme.txt @@ -0,0 +1 @@ +Logs files created by "SD Logs" special function will be stored in this directory. diff --git a/MODELS/README.txt b/MODELS/README.txt new file mode 100755 index 0000000..9ae65e5 --- /dev/null +++ b/MODELS/README.txt @@ -0,0 +1,3 @@ +Model configuration files go in this directory. On colorlcd radios, there is +also a `models.yml` file which controls which models are shown, and in what +order they are shown. diff --git a/MODELS/model00.yml b/MODELS/model00.yml new file mode 100644 index 0000000..eef3ee0 --- /dev/null +++ b/MODELS/model00.yml @@ -0,0 +1,525 @@ +semver: 2.11.1 +header: + name: KRATON + bitmap: "" + labels: "" + modelId: + 0: + val: 0 +timers: + 0: + swtch: L1 + mode: OFF + name: "" + minuteBeep: 0 + countdownBeep: 0 + start: 0 + persistent: 0 + countdownStart: 0 + value: 297 + showElapsed: 0 + extraHaptic: 0 +noGlobalFunctions: 0 +thrTrim: 0 +trimInc: 0 +displayTrims: 2 +ignoreSensorIds: 0 +showInstanceIds: 0 +disableThrottleWarning: 0 +enableCustomThrottleWarning: 0 +customThrottleWarningPosition: 0 +beepANACenter: 0 +extendedLimits: 1 +extendedTrims: 0 +throttleReversed: 0 +checklistInteractive: 0 +flightModeData: + 0: + trim: + 3: + value: 2 + mode: 0 + name: 100pct + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 100 + 1: + swtch: "!L2" + name: Disarmed + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 0 + 2: + swtch: SA1 + name: 60pct + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 60 + 3: + swtch: SA2 + name: 30pct + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 30 +mixData: + - destCh: 0 + srcRaw: I0 + weight: 100 + swtch: NONE + curve: + type: 0 + value: 0 + delayPrec: 0 + delayUp: 0 + delayDown: 0 + speedPrec: 0 + speedUp: 0 + speedDown: 0 + carryTrim: 0 + mltpx: ADD + mixWarn: 0 + flightModes: 000000000 + offset: 0 + name: "" + - destCh: 1 + srcRaw: I1 + weight: gv(0) + swtch: NONE + curve: + type: 0 + value: 0 + delayPrec: 0 + delayUp: 0 + delayDown: 0 + speedPrec: 0 + speedUp: 0 + speedDown: 0 + carryTrim: 0 + mltpx: ADD + mixWarn: 0 + flightModes: 000000000 + offset: 0 + name: "" +expoData: + - srcRaw: ST + scale: 0 + mode: 3 + chn: 0 + swtch: NONE + flightModes: 000000000 + weight: 100 + offset: 0 + curve: + type: 1 + value: 0 + trimSource: 0 + name: "" + - srcRaw: TH + scale: 0 + mode: 3 + chn: 1 + swtch: NONE + flightModes: 000000000 + weight: 100 + offset: 0 + curve: + type: 1 + value: 0 + trimSource: 0 + name: "" +inputNames: + 0: + val: St + 1: + val: Th +curves: + 31: + type: 1 + smooth: 0 + points: -1 + name: CNT +points: + 155: + val: -100 + 156: + val: 0 + 157: + val: 0 + 158: + val: 100 + 159: + val: -1 + 160: + val: 1 +logicalSw: + 0: + func: FUNC_EDGE + def: SC2,0,- + delay: 0 + duration: 0 + andsw: NONE + lsPersist: 0 + lsState: 0 + 1: + func: FUNC_STICKY + def: L1,L1 + delay: 0 + duration: 0 + andsw: NONE + lsPersist: 0 + lsState: 0 +customFn: + 0: + swtch: "!L2" + func: RGB_LED + def: rgbLop,1,On + 1: + swtch: SA0 + func: RGB_LED + def: green,1,On + 2: + swtch: SA1 + func: RGB_LED + def: yellow,1,On + 3: + swtch: SA2 + func: RGB_LED + def: orange,1,On +thrTraceSrc: TH +thrTrimSw: 0 +potsWarnMode: WARN_OFF +potsWarnEnabled: 0 +jitterFilter: GLOBAL +displayChecklist: 0 +gvars: + 0: + name: TH + min: 0 + max: 0 + popup: 0 + prec: 0 + unit: 0 +telemetryProtocol: 0 +varioData: + source: none + centerSilent: 0 + centerMax: 0 + centerMin: 0 + min: 0 + max: 0 +rssiSource: none +rfAlarms: + warning: 45 + critical: 42 +disableTelemetryWarning: 0 +moduleData: + 0: + type: TYPE_MULTIMODULE + subType: 6,4 + channelsStart: 0 + channelsCount: 16 + failsafeMode: NOT_SET + mod: + multi: + disableTelemetry: 0 + disableMapping: 0 + autoBindMode: 1 + lowPowerMode: 0 + optionValue: 0 +trainerData: + mode: OFF + channelsStart: 0 + channelsCount: -8 + frameLength: 0 + delay: 0 + pulsePol: 0 +telemetrySensors: + 0: + type: TYPE_CUSTOM + id1: + id: 20 + subId: 0 + id2: + instance: 0 + label: 1RSS + unit: 17 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 1: + type: TYPE_CUSTOM + id1: + id: 20 + subId: 0 + id2: + instance: 1 + label: 2RSS + unit: 17 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 2: + type: TYPE_CUSTOM + id1: + id: 20 + subId: 0 + id2: + instance: 2 + label: RQly + unit: 13 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 3: + type: TYPE_CUSTOM + id1: + id: 20 + subId: 0 + id2: + instance: 3 + label: RSNR + unit: 17 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 4: + type: TYPE_CUSTOM + id1: + id: 20 + subId: 0 + id2: + instance: 4 + label: ANT + unit: 0 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 5: + type: TYPE_CUSTOM + id1: + id: 20 + subId: 0 + id2: + instance: 5 + label: RFMD + unit: 0 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 6: + type: TYPE_CUSTOM + id1: + id: 20 + subId: 0 + id2: + instance: 6 + label: TPWR + unit: 16 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 7: + type: TYPE_CUSTOM + id1: + id: 20 + subId: 0 + id2: + instance: 7 + label: TRSS + unit: 17 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 8: + type: TYPE_CUSTOM + id1: + id: 20 + subId: 0 + id2: + instance: 8 + label: TQly + unit: 13 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 9: + type: TYPE_CUSTOM + id1: + id: 20 + subId: 0 + id2: + instance: 9 + label: TSNR + unit: 17 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 10: + type: TYPE_CUSTOM + id1: + id: 8 + subId: 0 + id2: + instance: 0 + label: RxBt + unit: 1 + prec: 1 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 11: + type: TYPE_CUSTOM + id1: + id: 8 + subId: 0 + id2: + instance: 1 + label: Curr + unit: 2 + prec: 1 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 12: + type: TYPE_CUSTOM + id1: + id: 8 + subId: 0 + id2: + instance: 2 + label: Capa + unit: 14 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 13: + type: TYPE_CUSTOM + id1: + id: 8 + subId: 0 + id2: + instance: 3 + label: Bat + unit: 13 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 +modelRegistrationID: "*\x15*\x15!\x03%\x07" +hatsMode: GLOBAL +usbJoystickExtMode: 0 +usbJoystickIfMode: JOYSTICK +usbJoystickCircularCut: 0 +radioGFDisabled: GLOBAL +radioTrainerDisabled: GLOBAL +modelHeliDisabled: GLOBAL +modelFMDisabled: GLOBAL +modelCurvesDisabled: GLOBAL +modelGVDisabled: GLOBAL +modelLSDisabled: GLOBAL +modelSFDisabled: GLOBAL +modelCustomScriptsDisabled: GLOBAL +modelTelemetryDisabled: GLOBAL \ No newline at end of file diff --git a/MODELS/model01.yml b/MODELS/model01.yml new file mode 100644 index 0000000..9d392ff --- /dev/null +++ b/MODELS/model01.yml @@ -0,0 +1,395 @@ +semver: 2.11.1 +header: + name: DT4.18 + bitmap: "" + labels: "" + modelId: + 0: + val: 0 +noGlobalFunctions: 0 +thrTrim: 0 +trimInc: 0 +displayTrims: 2 +ignoreSensorIds: 0 +showInstanceIds: 0 +disableThrottleWarning: 0 +enableCustomThrottleWarning: 0 +customThrottleWarningPosition: 0 +beepANACenter: 0 +extendedLimits: 0 +extendedTrims: 0 +throttleReversed: 1 +checklistInteractive: 0 +flightModeData: + 0: + trim: + 0: + value: 6 + mode: 0 + 4: + value: -2 + mode: 0 + name: 100pct + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 100 + 1: + val: 100 + 1: + swtch: "!L2" + name: Disarm + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 0 + 1: + val: 0 + 2: + swtch: SA1 + name: 60pct + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 60 + 3: + swtch: SA2 + name: 30pct + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 30 +mixData: + - destCh: 0 + srcRaw: I1 + weight: 100 + swtch: NONE + curve: + type: 0 + value: 0 + delayPrec: 0 + delayUp: 0 + delayDown: 0 + speedPrec: 0 + speedUp: 0 + speedDown: 0 + carryTrim: 0 + mltpx: ADD + mixWarn: 0 + flightModes: 000000000 + offset: 0 + name: "" + - destCh: 1 + srcRaw: I0 + weight: gv(0) + swtch: NONE + curve: + type: 0 + value: 0 + delayPrec: 0 + delayUp: 0 + delayDown: 0 + speedPrec: 0 + speedUp: 0 + speedDown: 0 + carryTrim: 0 + mltpx: ADD + mixWarn: 0 + flightModes: 000000000 + offset: 0 + name: "" + - destCh: 7 + srcRaw: P2 + weight: 100 + swtch: NONE + curve: + type: 0 + value: 0 + delayPrec: 0 + delayUp: 0 + delayDown: 0 + speedPrec: 0 + speedUp: 0 + speedDown: 0 + carryTrim: 0 + mltpx: ADD + mixWarn: 0 + flightModes: 000000000 + offset: 0 + name: Gyro +expoData: + - srcRaw: TH + scale: 0 + mode: 3 + chn: 0 + swtch: NONE + flightModes: 000000000 + weight: 100 + offset: 0 + curve: + type: 1 + value: 0 + trimSource: 0 + name: "" + - srcRaw: ST + scale: 0 + mode: 3 + chn: 1 + swtch: NONE + flightModes: 000000000 + weight: 100 + offset: 0 + curve: + type: 1 + value: 0 + trimSource: 0 + name: "" +inputNames: + 0: + val: TH + 1: + val: ST +logicalSw: + 0: + func: FUNC_EDGE + def: SC2,0,- + delay: 0 + duration: 0 + andsw: NONE + lsPersist: 0 + lsState: 0 + 1: + func: FUNC_STICKY + def: L1,L1 + delay: 0 + duration: 0 + andsw: NONE + lsPersist: 0 + lsState: 0 +customFn: + 0: + swtch: "!L2" + func: RGB_LED + def: rgbLop,1,On + 1: + swtch: SA0 + func: RGB_LED + def: green,1,On + 2: + swtch: SA1 + func: RGB_LED + def: yellow,1,On + 3: + swtch: SA2 + func: RGB_LED + def: orange,1,On +thrTraceSrc: TH +thrTrimSw: 0 +potsWarnMode: WARN_OFF +potsWarnEnabled: 0 +jitterFilter: GLOBAL +displayChecklist: 0 +gvars: + 0: + name: THR + min: 0 + max: 0 + popup: 0 + prec: 0 + unit: 0 + 1: + name: ST + min: 0 + max: 0 + popup: 0 + prec: 0 + unit: 0 +telemetryProtocol: 0 +screens: + 0: + type: BARS + u: + bars: + 0: + source: tele(2) + barMin: 0 + barMax: 0 + 1: + source: tele(3) + barMin: 0 + barMax: 0 + 2: + source: tele(4) + barMin: 0 + barMax: 0 + 3: + source: tele(5) + barMin: 0 + barMax: 0 +varioData: + source: none + centerSilent: 0 + centerMax: 0 + centerMin: 0 + min: 0 + max: 0 +rssiSource: none +rfAlarms: + warning: 45 + critical: 42 +disableTelemetryWarning: 0 +moduleData: + 0: + type: TYPE_MULTIMODULE + subType: 74,0 + channelsStart: 0 + channelsCount: 16 + failsafeMode: NOT_SET + mod: + multi: + disableTelemetry: 0 + disableMapping: 0 + autoBindMode: 0 + lowPowerMode: 0 + optionValue: 0 +trainerData: + mode: OFF + channelsStart: 0 + channelsCount: -8 + frameLength: 0 + delay: 0 + pulsePol: 0 +telemetrySensors: + 0: + type: TYPE_CUSTOM + id1: + id: 241 + subId: 0 + id2: + instance: 0 + label: A1 + unit: 1 + prec: 1 + autoOffset: 0 + filter: 1 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 132 + offset: 0 + 1: + type: TYPE_CUSTOM + id1: + id: 242 + subId: 0 + id2: + instance: 0 + label: A2 + unit: 1 + prec: 1 + autoOffset: 0 + filter: 1 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 132 + offset: 0 + 2: + type: TYPE_CUSTOM + id1: + id: 240 + subId: 0 + id2: + instance: 0 + label: RSSI + unit: 0 + prec: 0 + autoOffset: 0 + filter: 1 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 3: + type: TYPE_CUSTOM + id1: + id: 65534 + subId: 0 + id2: + instance: 0 + label: TRSS + unit: 17 + prec: 0 + autoOffset: 0 + filter: 1 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 4: + type: TYPE_CUSTOM + id1: + id: 65532 + subId: 0 + id2: + instance: 0 + label: RQly + unit: 0 + prec: 0 + autoOffset: 0 + filter: 1 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 5: + type: TYPE_CUSTOM + id1: + id: 65533 + subId: 0 + id2: + instance: 0 + label: TQly + unit: 0 + prec: 0 + autoOffset: 0 + filter: 1 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 +modelRegistrationID: ANNIKA +hatsMode: GLOBAL +usbJoystickExtMode: 0 +usbJoystickIfMode: JOYSTICK +usbJoystickCircularCut: 0 +radioGFDisabled: GLOBAL +radioTrainerDisabled: GLOBAL +modelHeliDisabled: GLOBAL +modelFMDisabled: GLOBAL +modelCurvesDisabled: GLOBAL +modelGVDisabled: GLOBAL +modelLSDisabled: GLOBAL +modelSFDisabled: GLOBAL +modelCustomScriptsDisabled: GLOBAL +modelTelemetryDisabled: GLOBAL \ No newline at end of file diff --git a/MODELS/model02.yml b/MODELS/model02.yml new file mode 100644 index 0000000..c6edb5d --- /dev/null +++ b/MODELS/model02.yml @@ -0,0 +1,550 @@ +semver: 2.11.1 +header: + name: ARX100 + bitmap: "" + labels: "" + modelId: + 0: + val: 0 +timers: + 0: + swtch: L1 + mode: OFF + name: "" + minuteBeep: 0 + countdownBeep: 0 + start: 0 + persistent: 0 + countdownStart: 0 + value: 297 + showElapsed: 0 + extraHaptic: 0 +noGlobalFunctions: 0 +thrTrim: 0 +trimInc: 0 +displayTrims: 2 +ignoreSensorIds: 0 +showInstanceIds: 0 +disableThrottleWarning: 0 +enableCustomThrottleWarning: 0 +customThrottleWarningPosition: 0 +beepANACenter: 0 +extendedLimits: 1 +extendedTrims: 0 +throttleReversed: 0 +checklistInteractive: 0 +flightModeData: + 0: + trim: + 0: + value: -82 + mode: 0 + 1: + value: -22 + mode: 0 + 4: + value: 6 + mode: 0 + name: 100pct + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 100 + 1: + swtch: "!L2" + name: Disarm + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 0 + 2: + swtch: SA1 + name: 60pct + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 60 + 3: + swtch: SA2 + name: 30pct + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 30 +mixData: + - destCh: 0 + srcRaw: I0 + weight: gv(0) + swtch: NONE + curve: + type: 0 + value: 0 + delayPrec: 0 + delayUp: 0 + delayDown: 0 + speedPrec: 0 + speedUp: 0 + speedDown: 0 + carryTrim: 0 + mltpx: ADD + mixWarn: 0 + flightModes: 000000000 + offset: 0 + name: "" + - destCh: 1 + srcRaw: I1 + weight: 100 + swtch: NONE + curve: + type: 0 + value: 0 + delayPrec: 0 + delayUp: 0 + delayDown: 0 + speedPrec: 0 + speedUp: 0 + speedDown: 0 + carryTrim: 0 + mltpx: ADD + mixWarn: 0 + flightModes: 000000000 + offset: 0 + name: "" +limitData: + 0: + min: 0 + max: 0 + revert: 0 + offset: 0 + ppmCenter: 0 + symetrical: 0 + name: "" + curve: 0 + 1: + min: 0 + max: 0 + revert: 0 + offset: 0 + ppmCenter: 0 + symetrical: 0 + name: "" + curve: 0 +expoData: + - srcRaw: TH + scale: 0 + mode: 3 + chn: 0 + swtch: NONE + flightModes: 000000000 + weight: 100 + offset: 0 + curve: + type: 0 + value: 0 + trimSource: 0 + name: "" + - srcRaw: ST + scale: 0 + mode: 3 + chn: 1 + swtch: NONE + flightModes: 000000000 + weight: 100 + offset: 0 + curve: + type: 0 + value: 0 + trimSource: 0 + name: "" +inputNames: + 0: + val: TH + 1: + val: ST +curves: + 31: + type: 1 + smooth: 0 + points: -1 + name: CNT +points: + 155: + val: -100 + 156: + val: 0 + 157: + val: 0 + 158: + val: 100 + 159: + val: -1 + 160: + val: 1 +logicalSw: + 0: + func: FUNC_EDGE + def: SC2,0,- + delay: 0 + duration: 0 + andsw: NONE + lsPersist: 0 + lsState: 0 + 1: + func: FUNC_STICKY + def: L1,L1 + delay: 0 + duration: 0 + andsw: NONE + lsPersist: 0 + lsState: 0 +customFn: + 0: + swtch: "!L2" + func: RGB_LED + def: rgbLop,1,On + 1: + swtch: SA0 + func: RGB_LED + def: green,1,On + 2: + swtch: SA1 + func: RGB_LED + def: yellow,1,On + 3: + swtch: SA2 + func: RGB_LED + def: orange,1,On +thrTraceSrc: TH +thrTrimSw: 0 +potsWarnMode: WARN_OFF +potsWarnEnabled: 0 +jitterFilter: GLOBAL +displayChecklist: 0 +gvars: + 0: + name: THR + min: 0 + max: 0 + popup: 0 + prec: 0 + unit: 0 +telemetryProtocol: 0 +varioData: + source: none + centerSilent: 0 + centerMax: 0 + centerMin: 0 + min: 0 + max: 0 +rssiSource: none +rfAlarms: + warning: 45 + critical: 42 +disableTelemetryWarning: 0 +moduleData: + 0: + type: TYPE_MULTIMODULE + subType: 1,0 + channelsStart: 0 + channelsCount: 16 + failsafeMode: NOT_SET + mod: + multi: + disableTelemetry: 0 + disableMapping: 0 + autoBindMode: 0 + lowPowerMode: 0 + optionValue: 0 +trainerData: + mode: OFF + channelsStart: 0 + channelsCount: -8 + frameLength: 0 + delay: 0 + pulsePol: 0 +telemetrySensors: + 0: + type: TYPE_CUSTOM + id1: + id: 20 + subId: 0 + id2: + instance: 0 + label: 1RSS + unit: 17 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 1: + type: TYPE_CUSTOM + id1: + id: 20 + subId: 0 + id2: + instance: 1 + label: 2RSS + unit: 17 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 2: + type: TYPE_CUSTOM + id1: + id: 20 + subId: 0 + id2: + instance: 2 + label: RQly + unit: 13 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 3: + type: TYPE_CUSTOM + id1: + id: 20 + subId: 0 + id2: + instance: 3 + label: RSNR + unit: 17 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 4: + type: TYPE_CUSTOM + id1: + id: 20 + subId: 0 + id2: + instance: 4 + label: ANT + unit: 0 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 5: + type: TYPE_CUSTOM + id1: + id: 20 + subId: 0 + id2: + instance: 5 + label: RFMD + unit: 0 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 6: + type: TYPE_CUSTOM + id1: + id: 20 + subId: 0 + id2: + instance: 6 + label: TPWR + unit: 16 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 7: + type: TYPE_CUSTOM + id1: + id: 20 + subId: 0 + id2: + instance: 7 + label: TRSS + unit: 17 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 8: + type: TYPE_CUSTOM + id1: + id: 20 + subId: 0 + id2: + instance: 8 + label: TQly + unit: 13 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 9: + type: TYPE_CUSTOM + id1: + id: 20 + subId: 0 + id2: + instance: 9 + label: TSNR + unit: 17 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 10: + type: TYPE_CUSTOM + id1: + id: 8 + subId: 0 + id2: + instance: 0 + label: RxBt + unit: 1 + prec: 1 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 11: + type: TYPE_CUSTOM + id1: + id: 8 + subId: 0 + id2: + instance: 1 + label: Curr + unit: 2 + prec: 1 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 12: + type: TYPE_CUSTOM + id1: + id: 8 + subId: 0 + id2: + instance: 2 + label: Capa + unit: 14 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 13: + type: TYPE_CUSTOM + id1: + id: 8 + subId: 0 + id2: + instance: 3 + label: Bat + unit: 13 + prec: 0 + autoOffset: 0 + filter: 0 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 +modelRegistrationID: "*\x15*\x15!\x03%\x07" +hatsMode: GLOBAL +usbJoystickExtMode: 0 +usbJoystickIfMode: JOYSTICK +usbJoystickCircularCut: 0 +radioGFDisabled: GLOBAL +radioTrainerDisabled: GLOBAL +modelHeliDisabled: GLOBAL +modelFMDisabled: GLOBAL +modelCurvesDisabled: GLOBAL +modelGVDisabled: GLOBAL +modelLSDisabled: GLOBAL +modelSFDisabled: GLOBAL +modelCustomScriptsDisabled: GLOBAL +modelTelemetryDisabled: GLOBAL \ No newline at end of file diff --git a/MODELS/model03.yml b/MODELS/model03.yml new file mode 100644 index 0000000..3a70801 --- /dev/null +++ b/MODELS/model03.yml @@ -0,0 +1,251 @@ +semver: 2.11.1 +header: + name: MOJAVE + bitmap: "" + labels: "" + modelId: + 0: + val: 0 +noGlobalFunctions: 0 +thrTrim: 0 +trimInc: 0 +displayTrims: 0 +ignoreSensorIds: 0 +showInstanceIds: 0 +disableThrottleWarning: 0 +enableCustomThrottleWarning: 0 +customThrottleWarningPosition: 0 +beepANACenter: 0 +extendedLimits: 0 +extendedTrims: 0 +throttleReversed: 0 +checklistInteractive: 0 +flightModeData: + 0: + name: 100pct + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 100 + 1: + swtch: "!L2" + name: Disarm + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 0 + 2: + swtch: SA1 + name: 60pct + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 60 + 3: + swtch: SA2 + name: 30pct + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 30 +mixData: + - destCh: 0 + srcRaw: I1 + weight: 100 + swtch: NONE + curve: + type: 0 + value: 0 + delayPrec: 0 + delayUp: 0 + delayDown: 0 + speedPrec: 0 + speedUp: 0 + speedDown: 0 + carryTrim: 0 + mltpx: ADD + mixWarn: 0 + flightModes: 000000000 + offset: 0 + name: "" + - destCh: 1 + srcRaw: I0 + weight: gv(0) + swtch: NONE + curve: + type: 0 + value: 0 + delayPrec: 0 + delayUp: 0 + delayDown: 0 + speedPrec: 0 + speedUp: 0 + speedDown: 0 + carryTrim: 0 + mltpx: ADD + mixWarn: 0 + flightModes: 000000000 + offset: 0 + name: "" + - destCh: 3 + srcRaw: P2 + weight: 25 + swtch: NONE + curve: + type: 0 + value: 0 + delayPrec: 0 + delayUp: 0 + delayDown: 0 + speedPrec: 0 + speedUp: 0 + speedDown: 0 + carryTrim: 0 + mltpx: ADD + mixWarn: 0 + flightModes: 000000000 + offset: -10 + name: DSC +limitData: + 3: + min: 0 + max: 0 + revert: 0 + offset: 0 + ppmCenter: 0 + symetrical: 0 + name: "" + curve: 0 +expoData: + - srcRaw: TH + scale: 0 + mode: 3 + chn: 0 + swtch: NONE + flightModes: 000000000 + weight: 100 + offset: 0 + curve: + type: 1 + value: 0 + trimSource: 0 + name: "" + - srcRaw: ST + scale: 0 + mode: 3 + chn: 1 + swtch: NONE + flightModes: 000000000 + weight: 100 + offset: 0 + curve: + type: 1 + value: 0 + trimSource: 0 + name: "" +inputNames: + 0: + val: TH + 1: + val: ST +logicalSw: + 0: + func: FUNC_EDGE + def: SC2,0,- + delay: 0 + duration: 0 + andsw: NONE + lsPersist: 0 + lsState: 0 + 1: + func: FUNC_STICKY + def: L1,L1 + delay: 0 + duration: 0 + andsw: NONE + lsPersist: 0 + lsState: 0 +customFn: + 0: + swtch: L2 + func: RGB_LED + def: rgbLop,1,On + 1: + swtch: SA0 + func: RGB_LED + def: green,1,On + 2: + swtch: SA1 + func: RGB_LED + def: yellow,1,On + 3: + swtch: SA2 + func: RGB_LED + def: orange,1,On +thrTraceSrc: TH +thrTrimSw: 0 +potsWarnMode: WARN_OFF +potsWarnEnabled: 0 +jitterFilter: GLOBAL +displayChecklist: 0 +gvars: + 0: + name: TH + min: 0 + max: 0 + popup: 0 + prec: 0 + unit: 0 +telemetryProtocol: 0 +varioData: + source: none + centerSilent: 0 + centerMax: 0 + centerMin: 0 + min: 0 + max: 0 +rssiSource: none +rfAlarms: + warning: 45 + critical: 42 +disableTelemetryWarning: 0 +moduleData: + 0: + type: TYPE_MULTIMODULE + subType: 11,5 + channelsStart: 0 + channelsCount: 16 + failsafeMode: NOT_SET + mod: + multi: + disableTelemetry: 0 + disableMapping: 0 + autoBindMode: 0 + lowPowerMode: 0 + optionValue: 0 +trainerData: + mode: OFF + channelsStart: 0 + channelsCount: -8 + frameLength: 0 + delay: 0 + pulsePol: 0 +modelRegistrationID: MOJAVE +hatsMode: GLOBAL +usbJoystickExtMode: 0 +usbJoystickIfMode: JOYSTICK +usbJoystickCircularCut: 0 +radioGFDisabled: GLOBAL +radioTrainerDisabled: GLOBAL +modelHeliDisabled: GLOBAL +modelFMDisabled: GLOBAL +modelCurvesDisabled: GLOBAL +modelGVDisabled: GLOBAL +modelLSDisabled: GLOBAL +modelSFDisabled: GLOBAL +modelCustomScriptsDisabled: GLOBAL +modelTelemetryDisabled: GLOBAL \ No newline at end of file diff --git a/MODELS/model04.yml b/MODELS/model04.yml new file mode 100644 index 0000000..11a83e6 --- /dev/null +++ b/MODELS/model04.yml @@ -0,0 +1,232 @@ +semver: 2.11.1 +header: + name: GRANITE + bitmap: "" + labels: "" + modelId: + 0: + val: 0 +noGlobalFunctions: 0 +thrTrim: 0 +trimInc: 0 +displayTrims: 0 +ignoreSensorIds: 0 +showInstanceIds: 0 +disableThrottleWarning: 0 +enableCustomThrottleWarning: 0 +customThrottleWarningPosition: 0 +beepANACenter: 0 +extendedLimits: 0 +extendedTrims: 0 +throttleReversed: 0 +checklistInteractive: 0 +flightModeData: + 0: + name: 100pct + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 100 + 1: + swtch: "!L2" + name: Disarm + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 0 + 2: + swtch: SA1 + name: 60pct + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 60 + 3: + swtch: SA2 + name: 30pct + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 30 +mixData: + - destCh: 0 + srcRaw: I0 + weight: gv(0) + swtch: NONE + curve: + type: 0 + value: 0 + delayPrec: 0 + delayUp: 0 + delayDown: 0 + speedPrec: 0 + speedUp: 0 + speedDown: 0 + carryTrim: 0 + mltpx: ADD + mixWarn: 0 + flightModes: 000000000 + offset: 0 + name: "" + - destCh: 1 + srcRaw: I1 + weight: 100 + swtch: NONE + curve: + type: 0 + value: 0 + delayPrec: 0 + delayUp: 0 + delayDown: 0 + speedPrec: 0 + speedUp: 0 + speedDown: 0 + carryTrim: 0 + mltpx: ADD + mixWarn: 0 + flightModes: 000000000 + offset: 0 + name: "" +limitData: + 1: + min: 0 + max: 0 + revert: 1 + offset: 0 + ppmCenter: 0 + symetrical: 0 + name: "" + curve: 0 +expoData: + - srcRaw: TH + scale: 0 + mode: 3 + chn: 0 + swtch: NONE + flightModes: 000000000 + weight: 100 + offset: 0 + curve: + type: 1 + value: 0 + trimSource: 0 + name: "" + - srcRaw: ST + scale: 0 + mode: 3 + chn: 1 + swtch: NONE + flightModes: 000000000 + weight: 100 + offset: 0 + curve: + type: 1 + value: 0 + trimSource: 0 + name: "" +inputNames: + 0: + val: TH + 1: + val: ST +logicalSw: + 0: + func: FUNC_EDGE + def: SC2,0,- + delay: 0 + duration: 0 + andsw: NONE + lsPersist: 0 + lsState: 0 + 1: + func: FUNC_STICKY + def: L1,L1 + delay: 0 + duration: 0 + andsw: NONE + lsPersist: 0 + lsState: 0 +customFn: + 0: + swtch: L2 + func: RGB_LED + def: rgbLop,1,On + 1: + swtch: SA0 + func: RGB_LED + def: green,1,On + 2: + swtch: SA1 + func: RGB_LED + def: yellow,1,On + 3: + swtch: SA2 + func: RGB_LED + def: orange,1,On +thrTraceSrc: TH +thrTrimSw: 0 +potsWarnMode: WARN_OFF +potsWarnEnabled: 0 +jitterFilter: GLOBAL +displayChecklist: 0 +gvars: + 0: + name: TH + min: 0 + max: 0 + popup: 0 + prec: 0 + unit: 0 +telemetryProtocol: 0 +varioData: + source: none + centerSilent: 0 + centerMax: 0 + centerMin: 0 + min: 0 + max: 0 +rssiSource: none +rfAlarms: + warning: 45 + critical: 42 +disableTelemetryWarning: 0 +moduleData: + 0: + type: TYPE_MULTIMODULE + subType: 1,0 + channelsStart: 0 + channelsCount: 16 + failsafeMode: NOT_SET + mod: + multi: + disableTelemetry: 0 + disableMapping: 0 + autoBindMode: 0 + lowPowerMode: 0 + optionValue: 0 +trainerData: + mode: OFF + channelsStart: 0 + channelsCount: -8 + frameLength: 0 + delay: 0 + pulsePol: 0 +modelRegistrationID: ANNIKA +hatsMode: GLOBAL +usbJoystickExtMode: 0 +usbJoystickIfMode: JOYSTICK +usbJoystickCircularCut: 0 +radioGFDisabled: GLOBAL +radioTrainerDisabled: GLOBAL +modelHeliDisabled: GLOBAL +modelFMDisabled: GLOBAL +modelCurvesDisabled: GLOBAL +modelGVDisabled: GLOBAL +modelLSDisabled: GLOBAL +modelSFDisabled: GLOBAL +modelCustomScriptsDisabled: GLOBAL +modelTelemetryDisabled: GLOBAL \ No newline at end of file diff --git a/MODELS/model05.yml b/MODELS/model05.yml new file mode 100644 index 0000000..e25e028 --- /dev/null +++ b/MODELS/model05.yml @@ -0,0 +1,383 @@ +semver: 2.11.1 +header: + name: RAIDER + bitmap: "" + labels: "" + modelId: + 0: + val: 0 +noGlobalFunctions: 0 +thrTrim: 0 +trimInc: 0 +displayTrims: 2 +ignoreSensorIds: 0 +showInstanceIds: 0 +disableThrottleWarning: 0 +enableCustomThrottleWarning: 0 +customThrottleWarningPosition: 0 +beepANACenter: 0 +extendedLimits: 0 +extendedTrims: 0 +throttleReversed: 0 +checklistInteractive: 0 +flightModeData: + 0: + trim: + 0: + value: -64 + mode: 0 + name: 100pct + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 100 + 1: + swtch: "!L2" + name: Disarm + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 0 + 1: + val: 0 + 2: + swtch: SA1 + name: 60pct + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 60 + 3: + swtch: SA2 + name: 30pct + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 30 +mixData: + - destCh: 0 + srcRaw: I1 + weight: 100 + swtch: NONE + curve: + type: 0 + value: 0 + delayPrec: 0 + delayUp: 0 + delayDown: 0 + speedPrec: 0 + speedUp: 0 + speedDown: 0 + carryTrim: 0 + mltpx: ADD + mixWarn: 0 + flightModes: 000000000 + offset: 0 + name: "" + - destCh: 1 + srcRaw: I0 + weight: gv(0) + swtch: NONE + curve: + type: 0 + value: 0 + delayPrec: 0 + delayUp: 0 + delayDown: 0 + speedPrec: 0 + speedUp: 0 + speedDown: 0 + carryTrim: 0 + mltpx: ADD + mixWarn: 0 + flightModes: 000000000 + offset: 0 + name: "" + - destCh: 7 + srcRaw: P2 + weight: 100 + swtch: NONE + curve: + type: 0 + value: 0 + delayPrec: 0 + delayUp: 0 + delayDown: 0 + speedPrec: 0 + speedUp: 0 + speedDown: 0 + carryTrim: 0 + mltpx: ADD + mixWarn: 0 + flightModes: 000000000 + offset: 0 + name: Gyro +expoData: + - srcRaw: TH + scale: 0 + mode: 3 + chn: 0 + swtch: NONE + flightModes: 000000000 + weight: 100 + offset: 0 + curve: + type: 1 + value: 0 + trimSource: 0 + name: "" + - srcRaw: ST + scale: 0 + mode: 3 + chn: 1 + swtch: NONE + flightModes: 000000000 + weight: 100 + offset: 0 + curve: + type: 1 + value: 0 + trimSource: 0 + name: "" +inputNames: + 0: + val: TH + 1: + val: ST +logicalSw: + 0: + func: FUNC_EDGE + def: SC2,0,- + delay: 0 + duration: 0 + andsw: NONE + lsPersist: 0 + lsState: 0 + 1: + func: FUNC_STICKY + def: L1,L1 + delay: 0 + duration: 0 + andsw: NONE + lsPersist: 0 + lsState: 1 +customFn: + 0: + swtch: "!L2" + func: RGB_LED + def: rgbLop,1,On + 1: + swtch: SA0 + func: RGB_LED + def: green,1,On + 2: + swtch: SA1 + func: RGB_LED + def: yellow,1,On + 3: + swtch: SA2 + func: RGB_LED + def: orange,1,On +thrTraceSrc: TH +thrTrimSw: 0 +potsWarnMode: WARN_OFF +potsWarnEnabled: 0 +jitterFilter: GLOBAL +displayChecklist: 0 +gvars: + 0: + name: THR + min: 0 + max: 0 + popup: 0 + prec: 0 + unit: 0 +telemetryProtocol: 0 +screens: + 0: + type: BARS + u: + bars: + 0: + source: tele(2) + barMin: 0 + barMax: 0 + 1: + source: tele(3) + barMin: 0 + barMax: 0 + 2: + source: tele(4) + barMin: 0 + barMax: 0 + 3: + source: tele(5) + barMin: 0 + barMax: 0 +varioData: + source: none + centerSilent: 0 + centerMax: 0 + centerMin: 0 + min: 0 + max: 0 +rssiSource: none +rfAlarms: + warning: 45 + critical: 42 +disableTelemetryWarning: 0 +moduleData: + 0: + type: TYPE_MULTIMODULE + subType: 74,0 + channelsStart: 0 + channelsCount: 16 + failsafeMode: NOT_SET + mod: + multi: + disableTelemetry: 0 + disableMapping: 0 + autoBindMode: 0 + lowPowerMode: 0 + optionValue: 0 +trainerData: + mode: OFF + channelsStart: 0 + channelsCount: -8 + frameLength: 0 + delay: 0 + pulsePol: 0 +telemetrySensors: + 0: + type: TYPE_CUSTOM + id1: + id: 241 + subId: 0 + id2: + instance: 0 + label: A1 + unit: 1 + prec: 1 + autoOffset: 0 + filter: 1 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 132 + offset: 0 + 1: + type: TYPE_CUSTOM + id1: + id: 242 + subId: 0 + id2: + instance: 0 + label: A2 + unit: 1 + prec: 1 + autoOffset: 0 + filter: 1 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 132 + offset: 0 + 2: + type: TYPE_CUSTOM + id1: + id: 240 + subId: 0 + id2: + instance: 0 + label: RSSI + unit: 0 + prec: 0 + autoOffset: 0 + filter: 1 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 3: + type: TYPE_CUSTOM + id1: + id: 65534 + subId: 0 + id2: + instance: 0 + label: TRSS + unit: 17 + prec: 0 + autoOffset: 0 + filter: 1 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 4: + type: TYPE_CUSTOM + id1: + id: 65532 + subId: 0 + id2: + instance: 0 + label: RQly + unit: 0 + prec: 0 + autoOffset: 0 + filter: 1 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 5: + type: TYPE_CUSTOM + id1: + id: 65533 + subId: 0 + id2: + instance: 0 + label: TQly + unit: 0 + prec: 0 + autoOffset: 0 + filter: 1 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 +modelRegistrationID: ANNIKA +hatsMode: GLOBAL +usbJoystickExtMode: 0 +usbJoystickIfMode: JOYSTICK +usbJoystickCircularCut: 0 +radioGFDisabled: GLOBAL +radioTrainerDisabled: GLOBAL +modelHeliDisabled: GLOBAL +modelFMDisabled: GLOBAL +modelCurvesDisabled: GLOBAL +modelGVDisabled: GLOBAL +modelLSDisabled: GLOBAL +modelSFDisabled: GLOBAL +modelCustomScriptsDisabled: GLOBAL +modelTelemetryDisabled: GLOBAL \ No newline at end of file diff --git a/MODELS/model06.yml b/MODELS/model06.yml new file mode 100755 index 0000000..e888d01 --- /dev/null +++ b/MODELS/model06.yml @@ -0,0 +1,383 @@ +semver: 2.11.1 +header: + name: "RAIDER" +telemetryProtocol: 0 +thrTrim: 0 +noGlobalFunctions: 0 +displayTrims: 2 +ignoreSensorIds: 0 +trimInc: 0 +disableThrottleWarning: 0 +displayChecklist: 0 +extendedLimits: 0 +extendedTrims: 0 +throttleReversed: 0 +enableCustomThrottleWarning: 0 +disableTelemetryWarning: 0 +showInstanceIds: 0 +checklistInteractive: 0 +customThrottleWarningPosition: 0 +beepANACenter: 0 +mixData: + - + destCh: 0 + srcRaw: "I1" + carryTrim: 0 + mixWarn: 0 + mltpx: ADD + delayPrec: 0 + speedPrec: 0 + flightModes: 000000000 + weight: 100 + offset: 0 + swtch: "NONE" + delayUp: 0 + delayDown: 0 + speedUp: 0 + speedDown: 0 + name: "" + - + destCh: 1 + srcRaw: "I0" + carryTrim: 0 + mixWarn: 0 + mltpx: ADD + delayPrec: 0 + speedPrec: 0 + flightModes: 000000000 + weight: 100 + offset: 0 + swtch: "NONE" + delayUp: 0 + delayDown: 0 + speedUp: 0 + speedDown: 0 + name: "" + - + destCh: 7 + srcRaw: "P2" + carryTrim: 0 + mixWarn: 0 + mltpx: ADD + delayPrec: 0 + speedPrec: 0 + flightModes: 000000000 + weight: 100 + offset: 0 + swtch: "NONE" + delayUp: 0 + delayDown: 0 + speedUp: 0 + speedDown: 0 + name: "Gyro" +expoData: + - + mode: 3 + scale: 0 + trimSource: 0 + srcRaw: "TH" + weight: "gv(0)" + offset: 0 + swtch: "NONE" + curve: + type: 1 + value: 0 + chn: 0 + flightModes: 000000000 + name: "" + - + mode: 3 + scale: 0 + trimSource: 0 + srcRaw: "ST" + weight: "gv(1)" + offset: 0 + swtch: "NONE" + curve: + type: 1 + value: 0 + chn: 1 + flightModes: 000000000 + name: "" +logicalSw: + 0: + func: FUNC_EDGE + def: "SC2,0,-" + andsw: "NONE" + lsPersist: 0 + lsState: 0 + delay: 0 + duration: 0 + 1: + func: FUNC_STICKY + def: "L1,L1" + andsw: "NONE" + lsPersist: 0 + lsState: 1 + delay: 0 + duration: 0 +flightModeData: + 0: + trim: + 0: + value: -64 + mode: 0 + name: "No Lim" + swtch: "NONE" + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 100 + 1: + val: 100 + 2: + val: 0 + 3: + val: 0 + 4: + val: 0 + 5: + val: 0 + 6: + val: 0 + 7: + val: 0 + 8: + val: 0 + 1: + name: "Disarm" + swtch: "!L2" + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 0 + 1: + val: 0 + 2: + name: "60 Lim" + swtch: "SA1" + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 60 + 3: + name: "30 Lim" + swtch: "SA2" + fadeIn: 0 + fadeOut: 0 + gvars: + 0: + val: 30 +thrTraceSrc: TH +switchWarning: + SA: + pos: up + FL1: + pos: up + FL2: + pos: up +gvars: + 0: + name: "THR" + min: 0 + max: 0 + popup: 0 + prec: 0 + unit: 0 + 1: + name: "ST" + min: 0 + max: 0 + popup: 0 + prec: 0 + unit: 0 +rssiSource: none +rfAlarms: + warning: 45 + critical: 42 +thrTrimSw: 0 +potsWarnMode: WARN_OFF +jitterFilter: GLOBAL +moduleData: + 0: + type: TYPE_MULTIMODULE + subType: 74,0 + channelsStart: 0 + channelsCount: 16 + failsafeMode: NOT_SET + mod: + multi: + disableTelemetry: 0 + disableMapping: 0 + autoBindMode: 0 + lowPowerMode: 0 + receiverTelemetryOff: 0 + receiverHigherChannels: 0 + optionValue: 0 +trainerData: + mode: OFF + channelsStart: 0 + channelsCount: -8 + frameLength: 0 + delay: 0 + pulsePol: 0 +inputNames: + 0: + val: "TH" + 1: + val: "ST" +potsWarnEnabled: 0 +telemetrySensors: + 0: + id1: + id: 241 + id2: + instance: 0 + label: "A1" + subId: 0 + type: TYPE_CUSTOM + unit: 1 + prec: 1 + autoOffset: 0 + filter: 1 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 132 + offset: 0 + 1: + id1: + id: 242 + id2: + instance: 0 + label: "A2" + subId: 0 + type: TYPE_CUSTOM + unit: 1 + prec: 1 + autoOffset: 0 + filter: 1 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 132 + offset: 0 + 2: + id1: + id: 240 + id2: + instance: 0 + label: "RSSI" + subId: 0 + type: TYPE_CUSTOM + unit: 0 + prec: 0 + autoOffset: 0 + filter: 1 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 3: + id1: + id: 65534 + id2: + instance: 0 + label: "TRSS" + subId: 0 + type: TYPE_CUSTOM + unit: 17 + prec: 0 + autoOffset: 0 + filter: 1 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 4: + id1: + id: 65532 + id2: + instance: 0 + label: "RQly" + subId: 0 + type: TYPE_CUSTOM + unit: 0 + prec: 0 + autoOffset: 0 + filter: 1 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 + 5: + id1: + id: 65533 + id2: + instance: 0 + label: "TQly" + subId: 0 + type: TYPE_CUSTOM + unit: 0 + prec: 0 + autoOffset: 0 + filter: 1 + logs: 1 + persistent: 0 + onlyPositive: 0 + cfg: + custom: + ratio: 0 + offset: 0 +screens: + 0: + type: BARS + u: + bars: + 0: + source: tele(2) + barMin: 0 + barMax: 0 + 1: + source: tele(3) + barMin: 0 + barMax: 0 + 2: + source: tele(4) + barMin: 0 + barMax: 0 + 3: + source: tele(5) + barMin: 0 + barMax: 0 +view: 0 +modelRegistrationID: "ANNIKA" +usbJoystickExtMode: 0 +usbJoystickIfMode: JOYSTICK +usbJoystickCircularCut: 0 +radioGFDisabled: GLOBAL +radioTrainerDisabled: GLOBAL +modelHeliDisabled: GLOBAL +modelFMDisabled: GLOBAL +modelCurvesDisabled: GLOBAL +modelGVDisabled: GLOBAL +modelLSDisabled: GLOBAL +modelSFDisabled: GLOBAL +modelCustomScriptsDisabled: GLOBAL +modelTelemetryDisabled: GLOBAL diff --git a/RADIO/README.txt b/RADIO/README.txt new file mode 100755 index 0000000..7da16a9 --- /dev/null +++ b/RADIO/README.txt @@ -0,0 +1 @@ +Radio specific configuration files are stored in this directory. diff --git a/RADIO/radio.yml b/RADIO/radio.yml new file mode 100755 index 0000000..2ba9719 --- /dev/null +++ b/RADIO/radio.yml @@ -0,0 +1,183 @@ +checksum: 12551 +semver: 2.11.1 +board: mt12 +calib: + ST: + mid: 1052 + spanNeg: 703 + spanPos: 743 + TH: + mid: 764 + spanNeg: 560 + spanPos: 1012 + P1: + mid: 1023 + spanNeg: 1005 + spanPos: 1007 + P2: + mid: 1026 + spanNeg: 1008 + spanPos: 1004 + P3: + mid: 935 + spanNeg: 664 + spanPos: 543 + P4: + mid: 1099 + spanNeg: 631 + spanPos: 561 +currModelFilename: model1.yml +vBatWarn: 66 +txVoltageCalibration: -7 +vBatMin: 64 +vBatMax: 84 +backlightColor: 0 +contrast: 18 +currModel: 0 +backlightMode: backlight_mode_all +trainer: + calib: + 0: + val: 0 + 1: + val: 0 + 2: + val: 0 + 3: + val: 0 + mix: + 0: + srcChn: 3 + mode: REPL + studWeight: 100 + 1: + srcChn: 1 + mode: REPL + studWeight: 100 + 2: + srcChn: 2 + mode: REPL + studWeight: 100 + 3: + srcChn: 0 + mode: REPL + studWeight: 100 +PPM_Multiplier: 0 +view: 1 +fai: 0 +disableMemoryWarning: 0 +beepMode: mode_all +alarmsFlash: 0 +disableAlarmWarning: 0 +disableRssiPoweroffAlarm: 0 +disableTrainerPoweroffAlarm: 0 +USBMode: 0 +hatsMode: SWITCHABLE +stickDeadZone: 0 +jackMode: 0 +hapticMode: mode_nokeys +stickMode: 1 +timezone: -5 +timezoneMinutes: 0 +adjustRTC: 1 +inactivityTimer: 30 +internalModuleBaudrate: 5 +internalModule: TYPE_MULTIMODULE +splashMode: 2 +lightAutoOff: 12 +templateSetup: 21 +hapticLength: 1 +speakerPitch: 0 +hapticStrength: 2 +beepLength: 2 +gpsFormat: 0 +speakerVolume: 4 +backlightBright: 0 +blOffBright: 0 +switchesDelay: 0 +globalTimer: 43671 +bluetoothName: 94832Q- +bluetoothBaudrate: 0 +bluetoothMode: OFF +countryCode: 0 +noJitterFilter: 0 +disableRtcWarning: 1 +audioMuteEnable: 0 +keysBacklight: 0 +rotEncMode: 0 +imperial: 0 +ppmunit: 0 +ttsLanguage: en +beepVolume: 4 +wavVolume: 4 +varioVolume: 2 +varioPitch: 0 +varioRange: 0 +varioRepeat: 0 +backgroundVolume: 3 +dontPlayHello: 0 +invertLCD: 0 +serialPort: + VCP: + mode: CLI + power: 0 +antennaMode: MODE_PER_MODEL +pwrOnSpeed: 1 +pwrOffSpeed: 1 +pwrOffIfInactive: 0 +disablePwrOnOffHaptic: 0 +sticksConfig: + 0: + name: "" + 1: + name: "" +potsConfig: + P1: + name: "" + type: with_detent + inv: 0 + P2: + name: "" + type: with_detent + inv: 0 + P3: + name: "" + type: axis_x + inv: 0 + P4: + name: "" + type: axis_y + inv: 0 +switchConfig: + SA: + type: 3pos + name: "" + SB: + type: toggle + name: "" + SC: + type: toggle + name: "" + SD: + type: toggle + name: "" + FL1: + type: 2pos + name: "" + FL2: + type: 2pos + name: "" +ownerRegistrationID: ANNIKA +imuMax: 0 +imuOffset: 0 +uartSampleMode: 0 +radioGFDisabled: 0 +radioTrainerDisabled: 0 +modelHeliDisabled: 0 +modelFMDisabled: 0 +modelCurvesDisabled: 0 +modelGVDisabled: 0 +modelLSDisabled: 0 +modelSFDisabled: 0 +modelCustomScriptsDisabled: 0 +modelTelemetryDisabled: 0 \ No newline at end of file diff --git a/SCREENSHOTS/readme.txt b/SCREENSHOTS/readme.txt new file mode 100755 index 0000000..59e528e --- /dev/null +++ b/SCREENSHOTS/readme.txt @@ -0,0 +1 @@ +Screenshots created by "Screenshot" special function will be stored in this directory. diff --git a/SCRIPTS/FUNCTIONS/readme.txt b/SCRIPTS/FUNCTIONS/readme.txt new file mode 100755 index 0000000..cabcbeb --- /dev/null +++ b/SCRIPTS/FUNCTIONS/readme.txt @@ -0,0 +1 @@ +This directory is for Lua functions scripts. diff --git a/SCRIPTS/MIXES/readme.txt b/SCRIPTS/MIXES/readme.txt new file mode 100755 index 0000000..2f3fd0a --- /dev/null +++ b/SCRIPTS/MIXES/readme.txt @@ -0,0 +1 @@ +This directory is for Lua mixer scripts. diff --git a/SCRIPTS/RGBLED/Bback.lua b/SCRIPTS/RGBLED/Bback.lua new file mode 100755 index 0000000..e39510a --- /dev/null +++ b/SCRIPTS/RGBLED/Bback.lua @@ -0,0 +1,68 @@ +local function init() + colorChangeTime = getTime() -- Initialize time + phase = 0 + currentLed = 0 -- Current lit LED position + scroll_oldtime = getTime() -- Initialize scroll_oldtime + scroll_cycle = 0 -- Initialize scroll_cycle +end + +-- Function to generate smooth cyclic colors +local function getColor(phase, length) + local position = (phase % length) / length + local r, g, b = 0, 0, 0 + local maxBrightness = 255 -- Maximum brightness value + + -- RGB color transition: red -> green -> blue -> red + if position < 1/3 then + -- From red to green + r = maxBrightness * (1 - 3 * position) + g = maxBrightness * (3 * position) + elseif position < 2/3 then + -- From green to blue + position = position - 1/3 + g = maxBrightness * (1 - 3 * position) + b = maxBrightness * (3 * position) + else + -- From blue to red + position = position - 2/3 + b = maxBrightness * (1 - 3 * position) + r = maxBrightness * (3 * position) + end + + -- Skip colors that are close to the background color + local bg_r, bg_g, bg_b = 0, 0, 72 -- Background color + local threshold = 30 -- Color difference threshold + if math.abs(r - bg_r) < threshold and math.abs(g - bg_g) < threshold and math.abs(b - bg_b) < threshold then + return getColor((phase + 1) % length, length) -- Skip this color and get the next one + end + + return r, g, b +end + +local colorPhase = 0 -- Initialize color phase + +local function run() + for i=LED_STRIP_LENGTH - 1, 0, -1 do -- Reverse iteration + if (i == scroll_cycle) then + local r, g, b = getColor(colorPhase, 255) + setRGBLedColor(i, r, g, b) + else + setRGBLedColor(i, 0, 0, 72) + end + end + if ((getTime() - scroll_oldtime) > 8) then + scroll_oldtime = getTime() + scroll_cycle = scroll_cycle - 1 -- Decrement scroll_cycle + if (scroll_cycle < 0) then + scroll_cycle = LED_STRIP_LENGTH - 1 -- Reset scroll_cycle to the end of the strip + end + end + colorPhase = (colorPhase + 1) % 255 -- Update color phase + applyRGBLedColors() +end + +local function background() + -- Called periodically while the Special Function switch is off +end + +return { run=run, background=background, init=init } \ No newline at end of file diff --git a/SCRIPTS/RGBLED/Bfwrd.lua b/SCRIPTS/RGBLED/Bfwrd.lua new file mode 100755 index 0000000..6e66669 --- /dev/null +++ b/SCRIPTS/RGBLED/Bfwrd.lua @@ -0,0 +1,68 @@ +local function init() + colorChangeTime = getTime() -- Initialize time + phase = 0 + currentLed = 0 -- Current lit LED position + scroll_oldtime = getTime() -- Initialize scroll_oldtime + scroll_cycle = 0 -- Initialize scroll_cycle +end + +-- Function to generate smooth cyclic colors +local function getColor(phase, length) + local position = (phase % length) / length + local r, g, b = 0, 0, 0 + local maxBrightness = 255 -- Maximum brightness value + + -- RGB color transition: red -> green -> blue -> red + if position < 1/3 then + -- From red to green + r = maxBrightness * (1 - 3 * position) + g = maxBrightness * (3 * position) + elseif position < 2/3 then + -- From green to blue + position = position - 1/3 + g = maxBrightness * (1 - 3 * position) + b = maxBrightness * (3 * position) + else + -- From blue to red + position = position - 2/3 + b = maxBrightness * (1 - 3 * position) + r = maxBrightness * (3 * position) + end + + -- Skip colors that are close to the background color + local bg_r, bg_g, bg_b = 0, 0, 72 -- Background color + local threshold = 30 -- Color difference threshold + if math.abs(r - bg_r) < threshold and math.abs(g - bg_g) < threshold and math.abs(b - bg_b) < threshold then + return getColor((phase + 1) % length, length) -- Skip this color and get the next one + end + + return r, g, b +end + +local colorPhase = 0 -- Initialize color phase + +local function run() + for i=0, LED_STRIP_LENGTH - 1, 1 do + if (i == scroll_cycle) then + local r, g, b = getColor(colorPhase, 255) + setRGBLedColor(i, r, g, b) + else + setRGBLedColor(i, 0, 0, 72) + end + end + if ((getTime() - scroll_oldtime) > 8) then + scroll_oldtime = getTime() + scroll_cycle = scroll_cycle + 1 + if (scroll_cycle >= LED_STRIP_LENGTH) then + scroll_cycle = 0 + end + end + colorPhase = (colorPhase + 1) % 255 -- Update color phase + applyRGBLedColors() +end + +local function background() + -- Called periodically while the Special Function switch is off +end + +return { run=run, background=background, init=init } \ No newline at end of file diff --git a/SCRIPTS/RGBLED/Pback.lua b/SCRIPTS/RGBLED/Pback.lua new file mode 100755 index 0000000..54d21c4 --- /dev/null +++ b/SCRIPTS/RGBLED/Pback.lua @@ -0,0 +1,68 @@ +local function init() + colorChangeTime = getTime() -- Initialize time + phase = 0 + currentLed = 0 -- Current lit LED position + scroll_oldtime = getTime() -- Initialize scroll_oldtime + scroll_cycle = 0 -- Initialize scroll_cycle +end + +-- Function to generate smooth cyclic colors +local function getColor(phase, length) + local position = (phase % length) / length + local r, g, b = 0, 0, 0 + local maxBrightness = 255 -- Maximum brightness value + + -- RGB color transition: red -> green -> blue -> red + if position < 1/3 then + -- From red to green + r = maxBrightness * (1 - 3 * position) + g = maxBrightness * (3 * position) + elseif position < 2/3 then + -- From green to blue + position = position - 1/3 + g = maxBrightness * (1 - 3 * position) + b = maxBrightness * (3 * position) + else + -- From blue to red + position = position - 2/3 + b = maxBrightness * (1 - 3 * position) + r = maxBrightness * (3 * position) + end + + -- Skip colors that are close to the background color + local bg_r, bg_g, bg_b = 72, 0, 72 -- Background color + local threshold = 30 -- Color difference threshold + if math.abs(r - bg_r) < threshold and math.abs(g - bg_g) < threshold and math.abs(b - bg_b) < threshold then + return getColor((phase + 1) % length, length) -- Skip this color and get the next one + end + + return r, g, b +end + +local colorPhase = 0 -- Initialize color phase + +local function run() + for i=LED_STRIP_LENGTH - 1, 0, -1 do -- Reverse iteration + if (i == scroll_cycle) then + local r, g, b = getColor(colorPhase, 255) + setRGBLedColor(i, r, g, b) + else + setRGBLedColor(i, 72, 0, 72) + end + end + if ((getTime() - scroll_oldtime) > 8) then + scroll_oldtime = getTime() + scroll_cycle = scroll_cycle - 1 -- Decrement scroll_cycle + if (scroll_cycle < 0) then + scroll_cycle = LED_STRIP_LENGTH - 1 -- Reset scroll_cycle to the end of the strip + end + end + colorPhase = (colorPhase + 1) % 255 -- Update color phase + applyRGBLedColors() +end + +local function background() + -- Called periodically while the Special Function switch is off +end + +return { run=run, background=background, init=init } \ No newline at end of file diff --git a/SCRIPTS/RGBLED/Pback.luac b/SCRIPTS/RGBLED/Pback.luac new file mode 100755 index 0000000000000000000000000000000000000000..463db5d6e864f25350963cdc76fe79be8d377003 GIT binary patch literal 1200 zcmaJ=&rcIk5T1G4t`#YWms$-G4<;VGd55>Hh+?Cr;<+hh1H{sj(jGWDZ`CW&_y@U* z5pQzxhb>&@1y&0crV*&ts-wmaMXG@hYc zN)H$#{Mt}ne+YtZY3oqjDuJG0RDTD@+&-RQPA`e~Bl*w)%_wL_>x+UiBpuJ3{k%`D>g z!4kc<#DItxCjzhwh{|HQWLb`#xNiwZ$1!^<&nE1Z#FMfOxFjqGh|6Nsr}8t6!Px`E zWIna2ta%cL0T|SZnA{=Lk*Uzv%3*%yv*VM9xGYh=WOH(6_l!BS$7XD=Mq@kbeKA5s zFQB<9=HdzcFv34k2S=DZGO!uwMhBuWoXvhH{Oa2L%apz^ja&sg5`Hg@T&J0~az$84L7k*~P!)Qj>HGNjWNF4)3IxcOqI}hK2{CVwDC0*5rwt{+?FwwhG zY$UT6NZ$Gsq`hD%BbBiQR1!kWut&865iX+NIUCSr$bO8xR!C8uCfsKZpW5<+#*4N3 zIM>NI}-jp)SC;!6s4qceXaM{CteWilkuj<>$GS%(m)A`4<7*$xX{xwu= j?(Vkt&m|fT$xC}-)_t5L3$>D(jE+#R_dn7Y green -> blue -> red + if position < 1/3 then + -- From red to green + r = maxBrightness * (1 - 3 * position) + g = maxBrightness * (3 * position) + elseif position < 2/3 then + -- From green to blue + position = position - 1/3 + g = maxBrightness * (1 - 3 * position) + b = maxBrightness * (3 * position) + else + -- From blue to red + position = position - 2/3 + b = maxBrightness * (1 - 3 * position) + r = maxBrightness * (3 * position) + end + + -- Skip colors that are close to the background color + local bg_r, bg_g, bg_b = 72, 0, 72 -- Background color + local threshold = 30 -- Color difference threshold + if math.abs(r - bg_r) < threshold and math.abs(g - bg_g) < threshold and math.abs(b - bg_b) < threshold then + return getColor((phase + 1) % length, length) -- Skip this color and get the next one + end + + return r, g, b +end + +local colorPhase = 0 -- Initialize color phase + +local function run() + for i=0, LED_STRIP_LENGTH - 1, 1 do + if (i == scroll_cycle) then + local r, g, b = getColor(colorPhase, 255) + setRGBLedColor(i, r, g, b) + else + setRGBLedColor(i, 72, 0, 72) + end + end + if ((getTime() - scroll_oldtime) > 8) then + scroll_oldtime = getTime() + scroll_cycle = scroll_cycle + 1 + if (scroll_cycle >= LED_STRIP_LENGTH) then + scroll_cycle = 0 + end + end + colorPhase = (colorPhase + 1) % 255 -- Update color phase + applyRGBLedColors() +end + +local function background() + -- Called periodically while the Special Function switch is off +end + +return { run=run, background=background, init=init } \ No newline at end of file diff --git a/SCRIPTS/RGBLED/Pfwrd.luac b/SCRIPTS/RGBLED/Pfwrd.luac new file mode 100755 index 0000000000000000000000000000000000000000..43f021c531789c280e9bf44cc399ef6dd4d41053 GIT binary patch literal 1052 zcmah|%}x|S5U%d&9anGx-IJQEm~7M#Pd)(l?8=Y0K?B4q8J2M&4!dN35;<8tawbOK z0-wO!3`V@kOQ0v8z>9u0{HziaE2-|P>ZIR1*P1AV)sIkxDD*Gb}L%h+=@)G z74>t&-`)s&(a8v-%|W*twfl`|-Hi8|-A){@cH;HE6qHW==1wz?WR!o1jCBvHX>1zX zTTArLlFDE!lmt*05KXdG6|4BjvHO~-}Qs4kppa+3yNt5C^)pLVm%Ci zImWaY563#yj#}{`uNZ1FC{xvs9xoi)SO=mHuaO5O3E{ zEtS}UgzUtYxo!gjd>R)ibp^DP^9c&*GHjn~RJBY3ll&?tyt~?%d9=E`vh?`bYGY<` zZe>14>Tk;BSb~*Ne`&4;g4Tu5IgUdvU*K#R6Oy?WZg0mse-!oPEij7`o?H+Mzm~@| M(8$Nkz5gt~0figaG5`Po literal 0 HcmV?d00001 diff --git a/SCRIPTS/RGBLED/README.md b/SCRIPTS/RGBLED/README.md new file mode 100755 index 0000000..c6a84d3 --- /dev/null +++ b/SCRIPTS/RGBLED/README.md @@ -0,0 +1 @@ +This folder contains sample RGB Lua scripts, for radios which support this capability. diff --git a/SCRIPTS/RGBLED/blue.lua b/SCRIPTS/RGBLED/blue.lua new file mode 100755 index 0000000..5c7f6a5 --- /dev/null +++ b/SCRIPTS/RGBLED/blue.lua @@ -0,0 +1,16 @@ +local function init() +end + +local function run() + for i=0, LED_STRIP_LENGTH - 1, 1 + do + setRGBLedColor(i, 0, 0, 128) + end + applyRGBLedColors() +end + +local function background() + -- Called periodically while the Special Function switch is off +end + +return { run=run, background=background, init=init } diff --git a/SCRIPTS/RGBLED/blue.luac b/SCRIPTS/RGBLED/blue.luac new file mode 100755 index 0000000000000000000000000000000000000000..946f8002ba18dc00e6651c029bec71cc1b8ea3be GIT binary patch literal 425 zcma)1T}lH%4F0B$!PY vTQ-enVdo- green -> blue -> red + if position < 1/3 then + -- From red to green + r = maxBrightness * (1 - 32 * position) + g = maxBrightness * (32 * position) + elseif position < 2/3 then + -- From green to blue + position = position - 1/3 + g = maxBrightness * (1 - 32 * position) + b = maxBrightness * (32 * position) + else + -- From blue to red + position = position - 2/3 + b = maxBrightness * (1 - 32 * position) + r = maxBrightness * (32 * position) + end + + return r, g, b +end + +local colorChangeTime = 0 -- The time of the last color change + +local function run() + if ((getTime() - colorChangeTime) > 1) then -- Use an interval of 2 time units + colorChangeTime = getTime() + phase = (phase + 1) % 255 -- Update color phase + + for i = 0, LED_STRIP_LENGTH - 1, 1 do + local r, g, b = getColor(phase + i * 64, 255) -- Increase phase offset for each LED + setRGBLedColor(i, r, g, b) + end + applyRGBLedColors() + end +end + +local function background() + -- Called periodically while the Special Function switch is off +end + +return { run=run, background=background, init=init } \ No newline at end of file diff --git a/SCRIPTS/RGBLED/green.lua b/SCRIPTS/RGBLED/green.lua new file mode 100755 index 0000000..feb5310 --- /dev/null +++ b/SCRIPTS/RGBLED/green.lua @@ -0,0 +1,16 @@ +local function init() +end + +local function run() + for i=0, LED_STRIP_LENGTH - 1, 1 + do + setRGBLedColor(i, 0, 255, 0) + end + applyRGBLedColors() +end + +local function background() + -- Called periodically while the Special Function switch is off +end + +return { run=run, background=background, init=init } diff --git a/SCRIPTS/RGBLED/green.luac b/SCRIPTS/RGBLED/green.luac new file mode 100755 index 0000000000000000000000000000000000000000..3f5b5faab5b71a02c2fb1b9b144451f0346a091d GIT binary patch literal 385 zcmah^%}&B#5S$O!Ce$CIJ*iis9(;&JL@;Rx0pgWZA%@trq|ii9Zl8hI@CCfVwY-Oq zq3+jk;DBM0{bn;evzzb2PlF!L=11j^w|VQ2hV?j%6YtlGr@>?rzHi7=>)nDw)s72&_pdhMfBY~t+5i9m literal 0 HcmV?d00001 diff --git a/SCRIPTS/RGBLED/off.lua b/SCRIPTS/RGBLED/off.lua new file mode 100755 index 0000000..ec63dcc --- /dev/null +++ b/SCRIPTS/RGBLED/off.lua @@ -0,0 +1,16 @@ +local function init() +end + +local function run() + for i=0, LED_STRIP_LENGTH - 1, 1 + do + setRGBLedColor(i, 0, 0, 0) + end + applyRGBLedColors() +end + +local function background() + -- Called periodically while the Special Function switch is off +end + +return { run=run, background=background, init=init } diff --git a/SCRIPTS/RGBLED/orange.lua b/SCRIPTS/RGBLED/orange.lua new file mode 100755 index 0000000..cf1650e --- /dev/null +++ b/SCRIPTS/RGBLED/orange.lua @@ -0,0 +1,16 @@ +local function init() +end + +local function run() + for i=0, LED_STRIP_LENGTH - 1, 1 + do + setRGBLedColor(i, 255, 100, 0) -- Change RGB values to represent orange color + end + applyRGBLedColors() +end + +local function background() + -- Called periodically while the Special Function switch is off +end + +return { run=run, background=background, init=init } diff --git a/SCRIPTS/RGBLED/orange.luac b/SCRIPTS/RGBLED/orange.luac new file mode 100644 index 0000000000000000000000000000000000000000..36c5d956ab09d0933549f0fa7dc968261f6b0e4c GIT binary patch literal 390 zcmah^%}&B#5S)ci4E2X-Pw17X2Or|Ah-lIf0>mq+mKb8wl0p+bxqSv-!x!)d*ZLkl zhPvBu;Gl7m{bsW>vzzbO&w?SEuY1msV-!!f0PK64gw`Nam+o9DvcMYyA4{OH;DVV8 zApZpD1dOV9j7hWD?A!(Sj}K{*#i7}n#?yuFqtYM*RHWq9R)c@Sh~kuLy&h7wpPxznuU7wT<`>YydXX literal 0 HcmV?d00001 diff --git a/SCRIPTS/RGBLED/police.lua b/SCRIPTS/RGBLED/police.lua new file mode 100755 index 0000000..07dea0d --- /dev/null +++ b/SCRIPTS/RGBLED/police.lua @@ -0,0 +1,26 @@ +local function init() + police_oldtime = getTime() + police_cycle = 0 +end + +local function run() + for i=0, LED_STRIP_LENGTH - 1, 1 + do + if (i % 2 == police_cycle) then + setRGBLedColor(i, 0, 0, 50) + else + setRGBLedColor(i, 50, 0, 0) + end + end + if ((getTime() - police_oldtime) > 8) then + police_oldtime = getTime() + police_cycle = 1 - police_cycle + end + applyRGBLedColors() +end + +local function background() + -- Called periodically while the Special Function switch is off +end + +return { run=run, background=background, init=init } diff --git a/SCRIPTS/RGBLED/police.luac b/SCRIPTS/RGBLED/police.luac new file mode 100755 index 0000000000000000000000000000000000000000..86fb644ec477937820dd6fcc2b63f377a81c3b28 GIT binary patch literal 588 zcma)2OHKko5Pdy16FvddF~(1VE(p4C%lL;;Eh zYN`uqSf&S_+iN>b&+YiVM@L4xj^EHuas{*}ZP&4Mp^a(v_ghSM_QFpr$40YC+^tFh zrUaZr()J<{C1K-42_r*-OxuPMB&JMWu!@;L)+Qsv!8OKUh#3g6Gxhmw1~&8-CFGG( zz$s`R=B(np-Q0wY6rYRznMrwM6+v&C;JLyz~riOj2oJvNBoa`0X&Ij AhyVZp literal 0 HcmV?d00001 diff --git a/SCRIPTS/RGBLED/purple.lua b/SCRIPTS/RGBLED/purple.lua new file mode 100755 index 0000000..99c6e19 --- /dev/null +++ b/SCRIPTS/RGBLED/purple.lua @@ -0,0 +1,16 @@ +local function init() +end + +local function run() + for i=0, LED_STRIP_LENGTH - 1, 1 + do + setRGBLedColor(i, 255, 0, 255) -- Set to purple color + end + applyRGBLedColors() +end + +local function background() + -- Called periodically while the Special Function switch is off +end + +return { run=run, background=background, init=init } \ No newline at end of file diff --git a/SCRIPTS/RGBLED/rainbw.lua b/SCRIPTS/RGBLED/rainbw.lua new file mode 100755 index 0000000..5248ec7 --- /dev/null +++ b/SCRIPTS/RGBLED/rainbw.lua @@ -0,0 +1,52 @@ +local function init() + colorChangeTime = getTime() -- Initialize time + phase = 0 +end + +local minBrightness = 0 -- Minimum brightness value +local maxBrightness = 255 -- Maximum brightness value + +local function getColor(phase, length) + local position = (phase % length) / length + local r, g, b = minBrightness, minBrightness, minBrightness + + -- RGB color transition: red -> green -> blue -> red + if position < 1/3 then + -- From red to green + r = maxBrightness * (1 - 3 * position) + g = maxBrightness * (3 * position) + elseif position < 2/3 then + -- From green to blue + position = position - 1/3 + g = maxBrightness * (1 - 3 * position) + b = maxBrightness * (3 * position) + else + -- From blue to red + position = position - 2/3 + b = maxBrightness * (1 - 3 * position) + r = maxBrightness * (3 * position) + end + + return r, g, b +end + +local colorChangeTime = 0 -- The time of the last color change + +local function run() + if ((getTime() - colorChangeTime) > 2) then -- Use an interval of 4 time units + colorChangeTime = getTime() + phase = (phase + 1) % 255 -- Update color phase + + for i = 0, LED_STRIP_LENGTH - 1, 1 do + local r, g, b = getColor(phase + i * 64, 255) -- Increase phase offset for each LED + setRGBLedColor(i, r, g, b) + end + applyRGBLedColors() + end +end + +local function background() + -- Called periodically while the Special Function switch is off +end + +return { run=run, background=background, init=init } \ No newline at end of file diff --git a/SCRIPTS/RGBLED/rainbw.luac b/SCRIPTS/RGBLED/rainbw.luac new file mode 100755 index 0000000000000000000000000000000000000000..e6b218fedb3423618112fe7c5d420f6d84fb1223 GIT binary patch literal 793 zcma)4&1zFo5dP-myPJhBBG{J@S3ZKEGq<^-;KG5q^Ua*^%$c8iwl$5maQaQTbf#ogo;(2HJ~T+o zLa0y{bVal#>K*W1l!Vdq!222SS#(|_Klk3x3D3U~=pq<-;fJCKf6B+&(X?-mJ&bl9 zbw-0}f7j-_{q98afMAuTFaaafk+%A7qp!W!4G zktFa5a49h?bD=fa`^(vk?s{*$?7m8#K2crf$mG(h*(3(JuQfKS{Nq{R>Zm$~_EC~r zey=Dr>Mi!9Ev5fPx-omQ)x5R6)!w{wf4kM(Sa07BosyTv@nmzo-imh{x_5gj8V-9; c|A>rrJ~9(aM~5;q(CphEom< z4W}9!8cs1VG(KZsU{qshU|fIb182lOzFf=wWFlab1Hn1IZU;t8#jf{*82Z0J0 zfo7{XHZZ9({HtekaAZ(%XaL*62GlDI#0)HgKCUkD!68AO0r5Vre(oV2!XR^5_={6Z qg4~^aQd6AsbMlLXjX;7zi3J5Yl}N(HAhlQk4^WH`EKb;0NZJ9bQ#B0$ literal 0 HcmV?d00001 diff --git a/SCRIPTS/RGBLED/rgbLop.lua b/SCRIPTS/RGBLED/rgbLop.lua new file mode 100755 index 0000000..3c70083 --- /dev/null +++ b/SCRIPTS/RGBLED/rgbLop.lua @@ -0,0 +1,53 @@ +local function init() + cycleTime = getTime() -- Initialize time + phase = 0 +end + +-- Function to generate smooth cyclic colors +local function getColor(phase, length) + local position = (phase % length) / length + local r, g, b = 5, 5, 5 + local maxBrightness = 255 -- Maximum brightness value + local minBrightness = 0 -- Minimum brightness value + + -- RGB color transition: red -> green -> blue -> red + if position < 1/3 then + -- From red to green + r = minBrightness + (maxBrightness - minBrightness) * (1 - 3 * position) + g = maxBrightness * (3 * position) + b = minBrightness + elseif position < 2/3 then + -- From green to blue + position = position - 1/3 + g = minBrightness + (maxBrightness - minBrightness) * (1 - 3 * position) + b = maxBrightness * (3 * position) + r = minBrightness + else + -- From blue to red + position = position - 2/3 + b = minBrightness + (maxBrightness - minBrightness) * (1 - 3 * position) + r = maxBrightness * (3 * position) + g = minBrightness + end + + return math.max(0, math.min(r, maxBrightness)), math.max(0, math.min(g, maxBrightness)), math.max(0, math.min(b, maxBrightness)) +end + +local function run() + if ((getTime() - cycleTime) > 2) then -- Use an interval of 8 time units + cycleTime = getTime() + phase = phase + 1 -- Update color phase + end + + for i = 0, LED_STRIP_LENGTH - 1, 1 do + local r, g, b = getColor(phase, 255) + setRGBLedColor(i, r, g, b) + end + applyRGBLedColors() +end + +local function background() + -- Called periodically while the Special Function switch is off +end + +return { run=run, background=background, init=init } \ No newline at end of file diff --git a/SCRIPTS/RGBLED/rgbLop.luac b/SCRIPTS/RGBLED/rgbLop.luac new file mode 100644 index 0000000000000000000000000000000000000000..c2bde1fc849894ce850a8981e53b43128acf99d7 GIT binary patch literal 902 zcma)4PiqrV5dY2Fx7)C_4Vse*F-V9af{1s0yKNJN*h(9`mNgA+FiAuHKznjF2fc~t zSLmtw1-$J>s3-ADc=RiX&fBI&N)OI5Gru?QH)~XQ50(CgiIbOeo|I#Vog!Q= zaX>|*_^sre=P%JIe?#~QEMIuSKhJwQ<9&y6Aa%WIfCUxmU;?}r{3ZEX?`yw@0LeDs?SU6>e=|}+;tI}CJ&Vp@w z;kPxSa6TR%KP=eU6{I=#c;}IJyU}P*>uz))rrom_7f4xR_p green -> blue -> red + if position < 1/3 then + -- From red to green + r = maxBrightness * (1 - 3 * position) + g = maxBrightness * (3 * position) + elseif position < 2/3 then + -- From green to blue + position = position - 1/3 + g = maxBrightness * (1 - 3 * position) + b = maxBrightness * (3 * position) + else + -- From blue to red + position = position - 2/3 + b = maxBrightness * (1 - 3 * position) + r = maxBrightness * (3 * position) + end + + return r, g, b +end + +local maxBackgroundBrightness = 255 -- Maximum brightness value for the background + +local function run() + if ((getTime() - colorChangeTime) > 2) then -- Use an interval of 4 time units + colorChangeTime = getTime() + phase = phase + 1 -- Update color phase + currentLed = (currentLed + 1) % LED_STRIP_LENGTH -- Move to the next LED + end + + for i = 0, LED_STRIP_LENGTH - 1, 1 do + local r, g, b = getColor(phase, 255) + if i <= currentLed then + setRGBLedColor(i, r, g, b) + else + -- Set the background color to the opposite of the main color in the RGB color space + local bg_r = (r + 128) % 256 + local bg_g = (g + 128) % 256 + local bg_b = (b + 128) % 256 + + -- Ensure the brightness of the background color does not exceed 72 + bg_r = math.min(bg_r, maxBackgroundBrightness) + bg_g = math.min(bg_g, maxBackgroundBrightness) + bg_b = math.min(bg_b, maxBackgroundBrightness) + + -- Ensure at least one color channel is always off + if bg_r > bg_g and bg_r > bg_b then + bg_r = 0 + elseif bg_g > bg_r and bg_g > bg_b then + bg_g = 0 + else + bg_b = 0 + end + + setRGBLedColor(i, bg_r, bg_g, bg_b) + end + end + applyRGBLedColors() +end + +local function background() + -- Called periodically while the Special Function switch is off +end + +return { run=run, background=background, init=init } \ No newline at end of file diff --git a/SCRIPTS/RGBLED/runner.luac b/SCRIPTS/RGBLED/runner.luac new file mode 100755 index 0000000000000000000000000000000000000000..524108686ed889f379b006c0a35ae96377701695 GIT binary patch literal 1173 zcmaJ=&rcIU6#m}qk5o)SyljdgVxsZS=^6}DMu`WHMlam)Kk&`AD^!gyc{}fY`^~%`^X6K;-&g^;u3JLoZFyp50)y!iehdC(F`hOyo^5s8 z{njRYOKG;6Jy-^b4)-WuOkZovGNS}=Db+?aDPs6ngs{=xX?JVejn-DY*4&NZ3lm#$ zZ|K+~I@^uC_&lLJ{6@dqja$8XyeWiX(A*gN*j~(uDqOlP%Nq^6)}Rk!dy?k7Xa$;r zl=vkvP7L$sjLb<&PT@~w`AK@G7Tdc%#xv_FvLu1 zD3H32!btLC(s^;LQU&K*$SU9o{$wKcB^x;9WT2)8k$XRhoJWI!yRT|^71g{{vEw%!I9)R4Fm@`lyyIfAaMx} zJKBNE4!egDt2zDih0it2YKFa@DM&xZ2J>H|JGw4t|F5w|7D1=~mf literal 0 HcmV?d00001 diff --git a/SCRIPTS/RGBLED/sapp.lua b/SCRIPTS/RGBLED/sapp.lua new file mode 100755 index 0000000..151af46 --- /dev/null +++ b/SCRIPTS/RGBLED/sapp.lua @@ -0,0 +1,16 @@ +local function init() +end + +local function run() + for i=0, LED_STRIP_LENGTH - 1, 1 + do + setRGBLedColor(i, 0, 255, 255) -- Set to sapphire color + end + applyRGBLedColors() +end + +local function background() + -- Called periodically while the Special Function switch is off +end + +return { run=run, background=background, init=init } \ No newline at end of file diff --git a/SCRIPTS/RGBLED/white.lua b/SCRIPTS/RGBLED/white.lua new file mode 100755 index 0000000..78e27c4 --- /dev/null +++ b/SCRIPTS/RGBLED/white.lua @@ -0,0 +1,16 @@ +local function init() +end + +local function run() + for i=0, LED_STRIP_LENGTH - 1, 1 + do + setRGBLedColor(i, 255, 255, 255) -- Set to white color + end + applyRGBLedColors() +end + +local function background() + -- Called periodically while the Special Function switch is off +end + +return { run=run, background=background, init=init } \ No newline at end of file diff --git a/SCRIPTS/RGBLED/yellow.lua b/SCRIPTS/RGBLED/yellow.lua new file mode 100755 index 0000000..f0176e7 --- /dev/null +++ b/SCRIPTS/RGBLED/yellow.lua @@ -0,0 +1,16 @@ +local function init() +end + +local function run() + for i=0, LED_STRIP_LENGTH - 1, 1 + do + setRGBLedColor(i, 255, 255, 0) + end + applyRGBLedColors() +end + +local function background() + -- Called periodically while the Special Function switch is off +end + +return { run=run, background=background, init=init } diff --git a/SCRIPTS/RGBLED/yellow.luac b/SCRIPTS/RGBLED/yellow.luac new file mode 100644 index 0000000000000000000000000000000000000000..e3d0a97c11e6a07e93347b902b9c89851c79b6b8 GIT binary patch literal 385 zcmah^%}&B#5S$O!Ce$CIJ*iis9(;&JL@;Rx0pgWZA%@trq|ii9Zl8hI@CCfVwY-Oq zq3+jk;DBM0`DQaayPNOAPlF!L=11j9%d>l_ oG9Asj&0BvotjA%Tc)!*>4JMQDeZ!vG>=qoVcU<_pf3-3G2mItU+W-In literal 0 HcmV?d00001 diff --git a/SCRIPTS/TELEMETRY/gplusl.lua b/SCRIPTS/TELEMETRY/gplusl.lua new file mode 100755 index 0000000..4129315 --- /dev/null +++ b/SCRIPTS/TELEMETRY/gplusl.lua @@ -0,0 +1,63 @@ +-- This code was originally written by Miami Mike to calculate the Open Location Code (Google+Code) +-- It was altered by ChrisOhara to permanently store the last received GPS coordinates by background function. +-- So that it can display the last position as Google+Code even after connection to receiver is lost. +-- Works on taranis + +local mid = LCD_W / 2 +local map = {[0] = + "2", "3", "4", "5", "6", "7", "8", "9", "C", "F", + "G", "H", "J", "M", "P", "Q", "R", "V", "W", "X"} + +local my_gpsId +local latitude, longitude = 0.0, 0.0 +local lat = 0 +local lon = 0 +local pluscode = "" + + +local function init_func() + my_gpsId = getFieldInfo("GPS") and getFieldInfo("GPS").id or nil; +end + + +local function getcode(lat, lon) + local int = math.floor(lat) + local codepair = map[int] + lat = 20 * (lat - int) + int = math.floor(lon) + codepair = codepair .. map[int] + lon = 20 * (lon - int) + return lat, lon, codepair +end + + +local function bg_func() + if getValue(my_gpsId) ~= 0 then + local gps = getValue(my_gpsId) + latitude, longitude = gps.lat, gps.lon + end +end + + +local function run_func() + lat = (latitude + 90) / 20 + lon = (longitude + 180) / 20 + pluscode = "" + for i = 1, 4 do + lat, lon, codepair = getcode(lat, lon) + pluscode = pluscode .. codepair + end + pluscode = pluscode .. "+" + lat, lon, codepair = getcode(lat, lon) + pluscode = pluscode .. codepair + pluscode = pluscode .. map[4 * math.floor(lat / 5) + math.floor(lon / 4)] + lcd.clear() + lcd.drawText(mid - 53, 5, "GPS coordinates are") + lcd.drawText(mid - 44, 15, latitude.. ", " .. longitude) + lcd.drawText(mid- 49, 35, "Google Plus Code is") + lcd.drawText(mid - 32, 45, pluscode) -- full 12 characters + --- lcd.drawText(mid - 20, 45, string.sub(pluscode, 5, 12)) -- shortened version + return 0 +end + +return {run=run_func, init=init_func, background=bg_func} \ No newline at end of file diff --git a/SCRIPTS/TELEMETRY/readme.txt b/SCRIPTS/TELEMETRY/readme.txt new file mode 100755 index 0000000..ea20c78 --- /dev/null +++ b/SCRIPTS/TELEMETRY/readme.txt @@ -0,0 +1,2 @@ +This directory is for Lua telemetry scripts. +Those scripts can be selected using DISPLAY screen in MODEL SETUP. diff --git a/SCRIPTS/TOOLS/BREAKOUT/gfx/gover1.bmp b/SCRIPTS/TOOLS/BREAKOUT/gfx/gover1.bmp new file mode 100755 index 0000000000000000000000000000000000000000..e35f4cde406650b8d617626a71b5691823fe99b8 GIT binary patch literal 574 zcmXYt&1(};6vfYcz)UM-W>UmW=}d0pyU-2~L!Dejg{0bM4nkR!O;%;*h3z=BJ@D`TVM5$5z| z_YqX+*i|hVWr;bJ=Y)A^$#dkV1GDx5mM+M$Xif4fJPsuo$uv1hpxVhS`JKRDLSC#1 zU_vht@2TK7+cotv%EnT{w5NNE^d)>ZD!3tZVDhE45kKAw6&$ zbB**9B;D3Vqa(RkG*bR(YGNLS|K%srJ{Cvgud2|eJK?(@j#{{I#GZVBZx;P>hN-5S zBu`JmWw}D>{0Xt4kj_F~DhX*~4`sFh+_HzBSO>1yB0V=x3Oe(mG`k2s%KaOAK_A@A!OIglQ?YY??e#3RP7jQm+yX#i_ g@qW;$*7i8R2i9z{`1OEqX0zqo;3m{!pL729FM~In!TCzRZ z(2tDjH?B`ywqCX~kUhC$Jrbf4U)|byD!hVM%FMdf%i|B3nJ-x{S8LCfORi;w{&HqP zh|^rBT*_K@C9KZKu}%$hn_I4BAMZY#&x*?a0T!~ZRruBJY?g%(`_KLPn|Cd{5e*Rj zqitWCz!(@sk=zZea@I9i@O}^yl`hYL4Rz%xjZ@}KPj&CVf#YcEZ}SZ3X=*s;od0Ag zAZqd+OI@V*ptP9MGh?ups*H5NX{(2SjVgFw4PpZ@BlY_z6~X8ii($$Kd$lwbsX+HF z{;d5V1*31gh9+Fw6Jbn}`c)~6WUfsZttMQ*cdjv-TTeer=$ONVLt{8C^XEkMBgxd? z=@d#Dk{8;8+ma{REn|a}f4-*al4oqgsDA=putTFxbx#RQ$$T+L2Qsc7ncFn@+K)Mi wyN#6&rE%iR7pV3NzNEE>f#ktBFM;;Tz?aO&O$oF-c@*Gda#zDnLhuRjANN*Ood5s; literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/BREAKOUT/gfx/splash1.bmp b/SCRIPTS/TOOLS/BREAKOUT/gfx/splash1.bmp new file mode 100755 index 0000000000000000000000000000000000000000..1f66160a6c9ae9167d5e6b21266407c37e6d9aa3 GIT binary patch literal 574 zcmXv}&ubGw7=5!DIvX1{KZ;TzZ8qx^-M!{w5YpWwMG_He*g@%~_3Xuy;K`Mwr1fGG zl0!hmf?hTRy{OIt8No zo@O`NX*Ku#2b>`&^k}7q8sSk^pyJ)gAam{AI*`@_G9C2*Hy)Sr4p9S@D3_A_LZn_w z$5<&e&&Nxd(#>e!8S2q;Epkx%^Xq${H8Xq^UzwNR)Rg?iqN?y;DN2Th#gYYMj$v_G z15=tsEWxM|WRYeJx%M##V?nMtP0+vyn0qB*2Fn%Z7F)P#RN9qkm3qcAX3pHaYnW_n zX$uc#9Na+3CH?U3_2H1MhB9RM0u=D9y-55^5yyW`tz@Q0aicyHd!Jm$g`Z3G6=gr5rN-%R_%Tu4W3uiL-TT}103tAv- z`RA9~)5&CIHB{X|Qp=H_+FfS{hLIri*K zpqR5>H2OQU`h3=L(0y&z8Vek;JGcJQ?uc5&TxU*9K7c&j;vszkD8%^q=~qBwlqsVV(ST2LG!?G!|dc@>+z-x8IZJJVfQD<(O^JMbG}+V_xr$^Wo31D{uJ08-Z9p zB4h6q#;787_9c&iTLGjh0d_~?J#|tRpJC4w;*oz<6yy9cAU~9_p#==mB;nO^G)lfj z!WH-M7-@w@w-oa(vB#x-@n)2j57LN_4c@wD fTlo1RrrieOcL|$Cv8LZLS}0Z~R7(wj+z0*xSLrA> literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/BREAKOUT/gfx/up1.bmp b/SCRIPTS/TOOLS/BREAKOUT/gfx/up1.bmp new file mode 100755 index 0000000000000000000000000000000000000000..2479a9765335c3ede07f2594cede6f732d6296b5 GIT binary patch literal 574 zcmXX?&ubGw7=1H4x|4dyW~H>ZY?lo*>%sq^-QA{;U4PJq9S@%RM~L7lvzkEKQxnpA z5DH~Wj_T1B(X$5;@nXaZ>d9Xqg0Oyxeelhj?|W~CHxu31;8i53ZtM@;SSi$k`4u7 zGZBiYU1u}8N6I>T1ra+Zb@okaa8aecX1A|b*;~??sAiuktrTfbdvM;cM^-1~th0rp)5MRw5u{HmOF2a>Lm%^g7gf*U0Qp(j93zn?4 zVD8ztNb^8P3$`mu?4|LufYV^5QHEZhv5!U>z)G`U+AO?`=?}#4QvG$_4_R>b>^HsN zFaCsO;T>w{N6;1~MC INGbK?AIQm?K>z>% literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/BREAKOUT/gfx/up2.bmp b/SCRIPTS/TOOLS/BREAKOUT/gfx/up2.bmp new file mode 100755 index 0000000000000000000000000000000000000000..7b71ea99378bb11815df8970bdc1d2735a205383 GIT binary patch literal 574 zcmXxf!Asjv90%~{GI)o90q0 zWK66?#P`RK(vn9<#|cDUPZ_UVw~;*D-CuD%%d6%m9b@CXoS!(*y~1&KvQ~8rqt6%e zGp^e%^lDYzFdKb;LSE|~Bl~IBG0gMBm(#l2I61|P?ig09+xuDLckam>e)~iln9V5s zb6~^bN5QuK?%CRU6n%7-GR@$voqN8NX?zR&+Vxjv^$XB$v*Ya|+Gjaw|7}yXA0BIshDl&dXUJwST_s*v?-6OMdpI)Xad)h1&vvQ?fQ*p0y zrNz>nf8@i+vLfcQUnDhVrQsYiNH4c(RFRg6wv(EZ#<^G%pd_2fVbc_U4gBD;1OAQ0 irA8g0^mI^^pt~A!X)y^T`0dF@0UZ9&ra9#PnfMPtZ&0rQ literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSM FwdPrg_55_MIN.lua b/SCRIPTS/TOOLS/DSM FwdPrg_55_MIN.lua new file mode 100644 index 0000000..62aa215 --- /dev/null +++ b/SCRIPTS/TOOLS/DSM FwdPrg_55_MIN.lua @@ -0,0 +1,1046 @@ +local toolName = "TNS|DSM Frwd Prog v0.55a (MIN)|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. # +---- # # +---- ######################################################################### + + +local VERSION = "v0.55a-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" +local MSG_MIN_FILE_OFFSET = 20000 + +-- Phase +local PH_INIT = 0 +local PH_RX_VER, PH_TITLE, PH_TX_INFO, PH_LINES, PH_VALUES = 1, 2, 3, 4, 5 +local PH_VAL_CHANGING, PH_VAL_EDITING, PH_VAL_EDIT_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_NC, LT_LIST_NC2, LT_LIST, LT_LIST_ORI, LT_LIST_TOG = 0x6C, 0x6D, 0x0C, 0xCC, 0x4C +local LT_VALUE_NC = 0x60 +local LT_VALUE_PERCENT, LT_VALUE_DEGREES = 0xC0, 0xE0 + +local Phase = PH_INIT +local SendDataToRX = 1 -- Initiate Sending Data + +local Text = {} +local List_Text = {} +local List_Text_Img = {} +local Flight_Mode = "Flight Mode" +local RxName = {} + +local TXInactivityTime = 0 +local RXInactivityTime = 0 +local TX_Info_Step = 0 +local TX_Info_Type = 0 +local Change_Step = 0 +local originalValue = 0 + +local TX_MAX_CH = 12 - 6 -- Number of Channels after Ch6 + +--local ctx = { +local ctx_SelLine = 0 -- Current Selected Line +local ctx_EditLine = nil -- Current Editing Line +local ctx_CurLine = -1 -- Current Line Requested/Parsed via h message protocol +local ctx_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 = "", + + 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 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 + +----- Line Type +local function isIncrementalValueUpdate(line) + if (line.Type == LT_LIST_NC or line.Type == LT_LIST_NC2 or + line.Type == LT_VALUE_NC or line.Type == LT_VALUE_DEGREES) then return false end + return true +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_NC or line.Type==LT_LIST_NC2 or + line.Type == LT_LIST or line.Type == LT_LIST_ORI or line.Type == LT_LIST_TOG +end + +local function isEditing() + return ctx_EditLine ~= nil +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 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 + +local function GetTextInfoFromFile(pos) + local f=MSG_FILE + if (pos >= MSG_MIN_FILE_OFFSET) then -- Depending on offset, use the main, or MIN version + f = MSG_FILE_MIN + pos = pos - MSG_MIN_FILE_OFFSET + end + + -- open and read File + local dataFile = io.open(f, "r") + io.seek(dataFile,pos) + local buff = io.read(dataFile, 100) + io.close(dataFile) + + local line="" + local index="" + local type="" + + local pipe=0 + local comment=0 + local newPos = pos + + -- Parse the line: + -- Format: TT|0x999|Text -- Comment + for i=1,#buff do + newPos=newPos+1 + local ch = string.sub(buff,i,i) + + if (pipe < 2 and ch=="|") then pipe=pipe+1 -- Count pipes pos (Type | Index | .....) + elseif (ch=="/") then pipe=6 -- far right end, like comment + elseif (ch=="\r") then -- Ignore CR + elseif (ch=="\n") then break -- LF, end of line + elseif (ch=="-") then -- March comments + comment=comment+1 + if (comment==2) then pipe=6 end -- Comment part of line + else + -- regular char + comment=0 + if (pipe==0) then type=type..ch -- in TT (Type) + elseif (pipe==1) then index=index..ch -- in Index + elseif (pipe<6) then line=line..ch end -- in Text + end -- Regular char + end -- Fpr + + return type, index, rtrim(line), newPos +end + +local function GetTextFromFile(pos) + if (pos==nil) then return nil end + local t,i,l, p = GetTextInfoFromFile(pos) + return l +end + +----------------------- +local function Get_Text(index) + local pos = Text[index] + local out = "" + + if (pos == nil) then + out = string.format("Unknown_%X", index) + else + out = GetTextFromFile(pos) + end + + --local out = Text[index] or string.format("Unknown_%X", index) + if (index >= 0x8000) then + out = Flight_Mode + end + return out +end + +local function Get_Text_Value(index) + local pos = List_Text[index] + local out = "" + + if (pos == nil) then + out = Get_Text(index) + else + out = GetTextFromFile(pos) + end + + --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_Connect() + --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')) +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 +--------------------- + +function ChangePhase(newPhase) + Phase = newPhase + SendDataToRX = 1 +end + +local function Value_Add(dir) + local line = MenuLines[ctx_SelLine] + local origVal = line.Val + local inc = dir + + if (not isListLine(line)) then -- List do slow inc + local Speed = getRotEncSpeed() + if Speed == ROTENC_MIDSPEED then + inc = (5 * dir) + elseif Speed == ROTENC_HIGHSPEED then + inc = (15 * dir) + end + end + + line.Val = line.Val + inc + + if line.Val > line.Max then + line.Val = line.Max + elseif line.Val < line.Min then + line.Val = line.Min + end + + if (origVal~=line.Val and isIncrementalValueUpdate(line)) then + -- Update RX value on every change, otherwise, just at the end + ChangePhase(PH_VAL_CHANGING) + end +end +-------------- + +local function GotoMenu(menuId, lastSelectedLine) + Menu.MenuId = menuId + ctx_SelLine = lastSelectedLine + -- Request to load the menu Again + ChangePhase(PH_TITLE) +end + +local function DSM_HandleEvent(event) + if event == EVT_VIRTUAL_EXIT then + if Phase == PH_RX_VER then + Phase = PH_EXIT_DONE -- Exit program + else + if isEditing() then -- Editing a Line, need to restore original value + MenuLines[ctx_EditLine].Val = originalValue + event = EVT_VIRTUAL_ENTER + else + if (Menu.BackId > 0 ) then -- Back?? + ctx_SelLine = -1 --Back Button + event = EVT_VIRTUAL_ENTER + else + ChangePhase(PH_EXIT_REQ) + end + end + end + end -- Exit + + if Phase == PH_RX_VER then return end -- nothing else to do + + if event == EVT_VIRTUAL_NEXT then + if isEditing() then -- Editting? + Value_Add(1) + else + 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 + return + end + + if event == EVT_VIRTUAL_PREV then + if isEditing() 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 + return + end + + if event == EVT_VIRTUAL_ENTER_LONG then + if isEditing() then + MenuLines[ctx_SelLine].Val = MenuLines[ctx_SelLine].Def + ChangePhase(PH_VAL_CHANGING) + 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 isEditing() then + ctx_EditLine = nil -- Done Editting + ChangePhase(PH_VAL_EDIT_END) + else -- Start Editing + ctx_EditLine = ctx_SelLine + originalValue = MenuLines[ctx_SelLine].Val + ChangePhase(PH_VAL_EDITING) + 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("TX:DSM_TxInfo_20(Port=#%d, Port Use)\n", portNo) + + if (TX_Info_Type == 0x1F) then -- SmartRx + TX_Info_Step = 1 + elseif (TX_Info_Type == 0x00) then -- AR636 + 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("TX:DSM_TxInfo_Travel(Port=#%d,(L=%d - R=%d))\n", portNo,leftTravel,rightTravel) + + TX_Info_Step = 2 + elseif (TX_Info_Step == 2) then + -- Subtrim + local b1,b2,b3,b4 = 0x00, 0x8E, 0x07, 0x72 -- (192-1904) + if (portNo==0) then -- Thr + b1,b2,b3,b4 = 0x00, 0x00, 0x07, 0xFF -- (0-2047) + end + + DSM_Send(0x21, 0x06, b1,b2,b3,b4) -- Port is not send anywhere, since the previous 0x20 type message have it. + LOG_write("TX:DSM_TxInfo_SubTrim(Port=#%d)\n", portNo) + + if (TX_Info_Type == 0x00) then -- AR636 + TX_Info_Step = 5 -- End Step + else + TX_Info_Step = 3 + end + elseif (TX_Info_Step == 3) then + LOG_write("TX: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("TX: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("TX:DSM_TxInfo_END(Port=#%d)\n", portNo) + DSM_Send(0x22, 0x04, 0x00, 0x00) + TX_Info_Step = 0 -- Done!! + end + + if (TX_Info_Step > 0) then + SendDataToRX = 1 -- keep Transmitig + end +end + +local function DSM_SendUpdate(line) + local valId = line.ValId + local value = sInt16ToDsm(line.Val) + + LOG_write("TX:ChangeValue(VId=0x%04X,Val=%d)\n", valId, line.Val) + DSM_Send(0x18, 0x06, + int16_MSB(valId), int16_LSB(valId), + int16_MSB(value), int16_LSB(value)) -- send current values +end + +local function DSM_SendValidate(line) + local valId = line.ValId + LOG_write("TX:ValidateValue(VId=0x%04X)\n", valId) + DSM_Send(0x19, 0x04, int16_MSB(valId), int16_LSB(valId)) +end + +local function DSM_SendRequest() + --LOG_write("DSM_SendRequest Phase=%d\n",Phase) + -- Need to send a request + local menuId = Menu.MenuId + local menuLSB = int16_LSB(menuId) + local menuMSB = int16_MSB(menuId) + + if Phase == PH_RX_VER then -- request RX version + DSM_Send(0x11, 0x06, TX_MAX_CH, 0x14, 0x00, 0x00) + LOG_write("TX:GetVersion(TX_MAX_CH=%d)\n",TX_MAX_CH+6) + + elseif Phase == PH_WAIT_CMD then -- keep connection open + DSM_Send(0x00, 0x04, 0x00, 0x00) + LOG_write("TX:TxHb\n") + + elseif Phase == PH_TITLE then -- request menu title + if menuId == 0 then + -- very first menu only + DSM_Send(0x12, 0x06, TX_MAX_CH, 0x14, 0x00, 0x00) + else + -- Any other menu + DSM_Send(0x16, 0x06, menuMSB, menuLSB, 0x00, ctx_SelLine) + if (menuId == 0x0001) then -- Executed Save&Reset menu + Phase = PH_RX_VER + ctx_isReset = true + end + end + LOG_write("TX: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 + if ctx_CurLine == -1 then + DSM_Send(0x13, 0x04, menuMSB, menuLSB) -- GetFirstLine + else + DSM_Send(0x14, 0x06, menuMSB, menuLSB, 0x00, ctx_CurLine) -- line X + end + LOG_write("TX:GetNextLine(LastLine=%d)\n", ctx_CurLine) + + elseif Phase == PH_VALUES then -- request menu values + local valId = MenuLines[ctx_CurLine].ValId + DSM_Send(0x15, 0x06, + menuMSB, menuLSB, + int16_MSB(valId), int16_LSB(valId)) + LOG_write("TX:GetNextValue(LastVId=0x%04X)\n", valId) + + elseif Phase == PH_VAL_EDITING then -- Editing a line (like a HB) + local line = MenuLines[ctx_SelLine] + DSM_Send(0x1A, 0x04, 0x00, ctx_SelLine) + LOG_write("TX:EditingValueLine(L=%d)\n", ctx_SelLine) + + elseif Phase == PH_VAL_CHANGING then -- change value during editing + local line = MenuLines[ctx_SelLine] + if (Change_Step==0) then + DSM_SendUpdate(line) + if line.Type == LT_LIST then -- Incremental Validation?? + Change_Step=1 + end + else -- Change_Step==1 + DSM_SendValidate(line) + Change_Step=0 + end + if (Change_Step==0) then Phase=PH_VAL_EDITING else SendDataToRX=1 end -- Done with change? + + elseif Phase == PH_VAL_EDIT_END then -- Done Editing line + local line = MenuLines[ctx_SelLine] + + if (Change_Step==0) then + DSM_SendUpdate(line) + Change_Step=1 + elseif (Change_Step==1) then + DSM_SendValidate(line) + Change_Step=2 + else -- Change_Step==3 + LOG_write("TX:EditValueEnd(L=%d)\n", ctx_SelLine) + DSM_Send(0x1B, 0x04, 0x00, ctx_SelLine) + Change_Step=0 + end + if (Change_Step==0) then Phase = PH_WAIT_CMD else SendDataToRX=1 end -- Done with change? + + elseif Phase == PH_EXIT_REQ then -- EXIT Request + DSM_Send(0x1F, 0x02, 0xAA) + LOG_write("TX:TX Exit Request\n") + end +end + +local function DSM_ProcessResponse() + local cmd = multiBuffer(11) + if cmd == 0x01 then -- read version + RX.Name = Get_RxName(multiBuffer(13)) + RX.Version = multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16) + + Menu.MenuId = 0 + Phase = PH_TITLE + LOG_write("RX: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 } + end + ctx_CurLine = -1 + ctx_SelLine = -1 -- highlight Back + + LOG_write("RX:Menu: Mid=0x%04X \"%s\"\n", menu.MenuId, menu.Text) + + if (menu.MenuId == 0x0001) then -- Still in RESETTING MENU??? + 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)) + + -- Signed 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("RX:Line: #%d Vid=0x%04X T=0x%02X \"%s\"\n", i, line.ValId, type, line.Text) + + if (line.MenuId~=Menu.MenuId) then -- Going Back too fast: Stil receiving lines from previous menu + Menu.MenuId = line.MenuId + end + + 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("RX: Value Updated: #%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("RX:TXInfoReq: Port=%d T=0x%02X\n", ctx_CurLine, TX_Info_Type) + + elseif cmd == 0xA7 then -- RX EXIT Request + if Phase == PH_EXIT_REQ then -- Response to our EXIT req + Phase = PH_EXIT_DONE + else -- Unexpected RX Exit + DSM_Release() + error("RX Connection Drop") + end + + elseif cmd == 0x00 then -- RX Heartbeat + LOG_write("RX:RxHb\n") + end + + return cmd +end + + +local function DSM_Send_Receive() + + -- Receive part: Process incoming messages if there is nothing to send + if SendDataToRX==0 and multiBuffer(10) == 0x09 then + local cmd = DSM_ProcessResponse() + -- Data processed + multiBuffer(10, 0x00) + RXInactivityTime = getTime() + 800 -- Reset Inactivity timeout (8s) + + if (cmd > 0x00) then -- RX-HeartBeat?? + -- Only change to SEND mode if we received a valid response (Ignore heartbeat) + SendDataToRX = 1 + end + else + -- Check if enouth time has passed from last Received activity + if (getTime() > RXInactivityTime and Phase~=PH_RX_VER and Phase~= PH_EXIT_DONE) then + if (isEditing()) then -- If Editing, extend time + RXInactivityTime = getTime() + 400 + else + DSM_Release() + error("RX Disconnected") + end + end + end + + -- Sending part -- + if SendDataToRX == 1 then + SendDataToRX = 0 + DSM_SendRequest() + TXInactivityTime = getTime() + 200 -- Reset Inactivity timeout (2s) + else + -- Check if enouth time has passed from last transmit activity + if getTime() > TXInactivityTime then + SendDataToRX = 1 -- Switch to Send mode to send heartbeat + + -- Change to Idle/HB mode if we are wating for RX version + if Phase ~= PH_RX_VER then + -- Phase = If IsEditing then PH_VAL_EDITING else PH_WAIT_CMD + Phase = (isEditing() and PH_VAL_EDITING) or 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(1, 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 < 70) then + msg = RX.Name .. " v" .. RX.Version + ver_rx_count = ver_rx_count + 1 + else + msg = "FProg "..VERSION + ver_rx_count = ver_rx_count + 1 + if (ver_rx_count > 140) then ver_rx_count=0 end + end + lcd.drawText(40, 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) + + if (Phase == PH_TX_INFO) then + lcd.drawText(1, 3 * LCD_Y_LINE_HEIGHT, "Sending CH"..(ctx_CurLine+1), TEXT_ATTR) + end + + 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 .. " " + if (line.Val==nil) then heading = heading .. "--" else heading = heading .. ((line.Val or 0) + 1) end + else + local text = nil + 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 offset = 0 + if (line.Type==LT_LIST_ORI) then offset = offset + 0x100 end --FH6250 hack + local imgDesc = GetTextFromFile(List_Text_Img[textId+offset]) + + if (imgDesc and i == ctx_SelLine) then -- Optional Image and Msg for selected value + showBitmap(1, 20, imgDesc) + end + 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 or "--", attrib + RIGHT) -- display value + attrib = TEXT_ATTR + end + end -- not Flight mode + lcd.drawText(1, 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(1, LCD_Y_LOWER_BUTTONS, "Prev", ctx_SelLine == 8) + end +end + +----------------------------------------------------------------------------------------- + +local function load_msg_from_file(fileName, offset, FileState) + + if (FileState.state==nil) then -- Initial State + FileState.state=1 + FileState.lineNo=0 + FileState.filePos=0 + end + + if FileState.state==1 then + for l=1,10 do -- do 10 lines at a time + local type, sIndex, text + local lineStart = FileState.filePos + + type, sIndex, text, FileState.filePos = GetTextInfoFromFile(FileState.filePos+offset) + + --print(string.format("T=%s, I=%s, T=%s LS=%d, FP=%d",type,sIndex,text,lineStart, FileState.filePos)) + + if (lineStart==FileState.filePos) then -- EOF + FileState.state=2 --EOF State + return 1 + end + FileState.lineNo = FileState.lineNo + 1 + + type = rtrim(type) + + if (string.len(type) > 0 and string.len(sIndex) > 0) then + local index = tonumber(sIndex) + local filePos = lineStart + offset + + if (index == nil) then + assert(false, string.format("%s:%d: Invalid Hex num [%s]", fileName, FileState.lineNo, sIndex)) + elseif (type == "T") then + Text[index] = filePos + elseif (type == "LT") then + List_Text[index] = filePos + elseif (type == "LI") then + List_Text_Img[index] = filePos + elseif (type == "FM") then + Flight_Mode = text + elseif (type == "RX") then + RxName[index] = text + else + assert(false, string.format("%s:%d: Invalid Line Type [%s]", fileName, FileState.lineNo, type)) + end + end + end -- for + end -- if + + collectgarbage("collect") + return 0 +end + +local function DSM_Init_Model() + MODEL.DSM_ChannelInfo={} + MODEL.modelOutputChannel={} + + local ChInfo = MODEL.DSM_ChannelInfo + local OutCh = MODEL.modelOutputChannel + + for i=0, 11 do + ChInfo[i] = {[0]= 0x00, 0x00} + OutCh[i] = {min=1000, max=1000} + end + + ChInfo[0][1] = 0x40 -- Ch1 Thr (0x40) + ChInfo[1][1] = 0x01 -- Ch2 Ail (0x01) + ChInfo[2][1] = 0x02 -- Ch2 ElE (0x02) + ChInfo[3][1] = 0x04 -- Ch4 Rud (0x04) +end + +------------------------------------------------------------------------------------------------------------ +-- Init +local function DSM_Init() + LOG_open() + LOG_write("--- NEW SESSION\n") + + DSM_Init_Model() + 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 + + 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 + + Phase = PH_INIT +end + + +----------------------------------------------------------------------------------------------------------- +local initStep=0 +local FileState = { lineNo=0} + +local function Inc_Init() + lcd.clear() + + if (initStep == 0) then + lcd.drawText(1, 0, "Loading Msg file: "..(FileState.lineNo or 0)) + if (load_msg_from_file(MSG_FILE, 0, FileState)==1) then + initStep=1 + FileState = {} + collectgarbage("collect") + end + return 0 + elseif (initStep == 1) then + lcd.drawText(1, 0, "Loading Msg file: "..(FileState.lineNo or 0)) + if (load_msg_from_file(MSG_FILE_MIN, MSG_MIN_FILE_OFFSET, FileState)==1) then + initStep=2 + FileState ={} + collectgarbage("collect") + end + return 0 + else -- initStep==2 + --initStep=3 + Phase = PH_RX_VER -- Done Init + DSM_Connect() + end +end + +------------------------------------------------------------------------------------------------------------ +-- Main + +local function DSM_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + end + + if (Phase == PH_INIT) then + Inc_Init() -- Incremental initialization + return 0 + end + + DSM_Display() + DSM_HandleEvent(event) + DSM_Send_Receive() + collectgarbage("collect") + + if Phase == PH_EXIT_DONE then + DSM_Release() + LOG_close() + return 2 + else + return 0 + end +end + +return { init = DSM_Init, run = DSM_Run } diff --git a/SCRIPTS/TOOLS/DSM FwdPrg_56_Color.lua b/SCRIPTS/TOOLS/DSM FwdPrg_56_Color.lua new file mode 100644 index 0000000..b631963 --- /dev/null +++ b/SCRIPTS/TOOLS/DSM FwdPrg_56_Color.lua @@ -0,0 +1,790 @@ +local toolName = "TNS|DSM Forward Prog v0.56 (Color) |TNE" +local VERSION = "v0.56" + +---- ######################################################################### +---- # # +---- # 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. # +---- # # +---- ######################################################################### +------------------------------------------------------------------------------ +-- This script library is a rewrite of the original DSM forward programming Lua +-- Script. The goal is to make it easier to understand, mantain, and to +-- separate the GUI from the DSM Forward programming engine/logic +-- in this way, GUIs can evolve independent. OpenTX Gui, EdgeTx GUI, Small Radios, etc. + +-- Code is based on the code/work by: Pascal Langer (Author of the Multi-Module) +-- Rewrite/Enhancements By: Francisco Arzu +------------------------------------------------------------------------------ + +local SIMULATION_ON = true -- false: dont show simulation menu, TRUE: show simulation menu +local DEBUG_ON = 1 -- 0=NO DEBUG, 1=HIGH LEVEL 2=LOW LEVEL (Debug logged into the /LOGS/dsm.log) +local USE_SPECKTRUM_COLORS = true -- true: Use spectrum colors, false: use theme colors (default on OpenTX) +local DSMLIB_PATH = "/SCRIPTS/TOOLS/DSMLIB/" +local IMAGE_PATH = DSMLIB_PATH .. "img/" + +local Log = assert(loadScript(DSMLIB_PATH.."DsmLogLib.lua"), "Not-Found: DSMLIB/DsmLogLib.lua")() +local menuLib = assert(loadScript(DSMLIB_PATH.."DsmMenuLib.lua"), "Not-Found: DSMLIB/DsmMenuLib.lua")(Log, DEBUG_ON) +local modelLib = assert(loadScript(DSMLIB_PATH.."DsmModelLib.lua"), "Not-Found: DSMLIB/DsmModelLib.lua")(Log, DEBUG_ON) +local menuProcessor = assert(loadScript(DSMLIB_PATH.."DsmMainMenuLib.lua"), "Not-Found: DSMLIB/DsmMainMenuLib.lua")(Log, menuLib, modelLib, DEBUG_ON, SIMULATION_ON) + +local PHASE = menuLib.PHASE +local LINE_TYPE = menuLib.LINE_TYPE +local DISP_ATTR = menuLib.DISP_ATTR +local DSM_Context = menuLib.DSM_Context + + +local lastRefresh=0 -- Last time the screen was refreshed +local REFRESH_GUI_MS = 300/10 -- 300ms.. Screen Refresh Rate.. to not waste CPU time (in 10ms units to be compatible with getTime()) +local originalValue = nil + +local touchButtonArea = {} +local EDIT_BUTTON = { DEFAULT=1001, DEC_10=1002, DEC_1=1003, INC_1=1004, INC_10=5, OK=1006, ESC=1007 } + +local IS_EDGETX = false -- DEFAULT until Init changed it + +local LCD_Y_MENU_TITLE = 20 +local LCD_W_MENU_TITLE = LCD_W-100 + +local LCD_X_LINE_MENU = 30 +local LCD_W_LINE_MENU = 350 + +local LCD_X_LINE_TITLE = 30 +local LCD_X_LINE_VALUE = 230 +local LCD_X_LINE_DEBUG = 390 + + +local LCD_Y_LINE_START = LCD_Y_MENU_TITLE + 30 +local LCD_Y_LINE_HEIGHT = 27 + +local LCD_Y_LOWER_BUTTONS = LCD_Y_LINE_START + 3 + (7 * LCD_Y_LINE_HEIGHT) + +-- TOOL BG COLOR +local LCD_TOOL_BGCOLOR = TEXT_BGCOLOR +-- TOOL HEADER +local LCD_TOOL_HDR_COLOR = MENU_TITLE_COLOR +local LCD_TOOL_HDR_BGCOLOR = TITLE_BGCOLOR +-- MENU HEADER +local LCD_MENU_COLOR = MENU_TITLE_COLOR +local LCD_MENU_BGCOLOR = MENU_TITLE_BGCOLOR +-- LINE SELECTED +local LCD_SELECTED_COLOR = TEXT_INVERTED_COLOR +local LCD_SELECTED_BGCOLOR = TEXT_INVERTED_BGCOLOR +local LCD_EDIT_BGCOLOR = MENU_TITLE_BGCOLOR -- WARNING_COLOR +-- NORMAL TEXT +local LCD_NORMAL_COLOR = TEXT_COLOR +local LCD_DISABLE_COLOR = TEXT_DISABLE_COLOR +local LCD_DEBUG_COLOR = LINE_COLOR +-- NORMAL BOX FRAME COLOR +local LCD_BOX_COLOR = TEXT_DISABLE_COLOR + + +local warningScreenON = true + + +--------------------- lcd.sizeText replacement ------------------------------------------------- +-- EdgeTx dont have lcd.sizeText, so we do an equivalent one using the string length and 5px per character +local function my_lcd_sizeText(s) + if (s==nil) then return 20 end + -- return: If IS_EDGETX then lcd.sizeText() else string.len() + return (IS_EDGETX and lcd.sizeText(s)) or (string.len(s)*10) +end + + +local function GUI_SwitchToRX() + -- Force to refresh DSM Info in MODEL (dsmLib pointing to the setup Script) + -- local dsmChannelInfo, description = modelLib.CreateDSMPortChannelInfo() + + menuProcessor.done() + Log.LOG_close() -- Reset the log + Log.LOG_open() + + menuProcessor = nil + collectgarbage("collect") + + modelLib.ReadTxModelData() + modelLib.ST_LoadFileData() + modelLib.CreateDSMPortChannelInfo() + + local dsmLib = assert(loadScript(DSMLIB_PATH.."DsmFwPrgLib.lua"),"Not-Found: DSMLIB/DsmFwPrgLib.lua") + menuProcessor = dsmLib(Log, menuLib, modelLib, DEBUG_ON) + + dsmLib = nil + collectgarbage("collect") + + menuProcessor.init(toolName) -- Initialize Library + DSM_Context.Refresh_Display = true +end + +local function GUI_SwitchToSIM() + menuProcessor.done() + Log.LOG_close() + + menuProcessor = nil + collectgarbage("collect") + + local simLib = assert(loadScript(DSMLIB_PATH.."DsmSimMenuLib.lua"), "Not-Found: DSMLIB/DsmSimMenuLib.lua") + menuProcessor = simLib(Log, menuLib, modelLib, DEBUG_ON) + + simLib = nil + collectgarbage("collect") + + menuProcessor.init(toolName) -- Initialize Library + DSM_Context.Refresh_Display = true +end + +local function GUI_SwitchToSetupMenu() + menuProcessor.done() + + menuProcessor = nil + collectgarbage("collect") + + local setupLib = assert(loadScript(DSMLIB_PATH.."DsmSetupMenuLib.lua"), "Not-Found: DSMLIB/DsmSetupMenuLib.lua") + menuProcessor = setupLib(Log, menuLib, modelLib, DEBUG_ON, SIMULATION_ON) + + setupLib = nil + collectgarbage("collect") + + menuProcessor.init(toolName) -- Initialize Library + DSM_Context.Refresh_Display = true +end + +local function GUI_SwitchToMainMenu() + print("SWITCHING TO MAIN MENU") + menuProcessor.done() + + menuProcessor = nil + collectgarbage("collect") + + local mainMenuLib = assert(loadScript(DSMLIB_PATH.."DsmMainMenuLib.lua"), "Not-Found: DSMLIB/DsmMainMenuLib.lua") + menuProcessor = mainMenuLib(Log, menuLib, modelLib, DEBUG_ON, SIMULATION_ON) + + mainMenuLib = nil + collectgarbage("collect") + + menuProcessor.init(toolName) -- Initialize Library + DSM_Context.Refresh_Display = true +end + + +--------------------- Toucch Button Helpers ------------------------------------------------------------ +local function GUI_addTouchButton(x,y,w,h,line) + -- Add new button info to end of the array + touchButtonArea[#touchButtonArea+1] = {x=x, y=y, w=w, h=h, line=line} +end + +local function GUI_getTouchButton(x,y) + for i = 1, #touchButtonArea do + local button = touchButtonArea[i] + -- is the coordinate inside the button area?? + if (x >= button.x and x <= (button.x+button.w) and y >= button.y and (y <= button.y+button.h)) then + return button.line + end + end + return nil +end + +local function GUI_clearTouchButtons() + touchButtonArea = {} +end + +---------- Return Color to display Menu Lines ---------------------------------------------------------------- +local function GUI_GetTextColor(lineNum) + local ctx = DSM_Context + local txtColor = LCD_NORMAL_COLOR + -- Gray Out any other line except the one been edited + if (ctx.isEditing() and ctx.EditLine~=lineNum) then txtColor=LCD_DISABLE_COLOR end + return txtColor +end + +local function GUI_GetFrameColor(lineNum) -- Frame Color for Value/Menu Boxes + local ctx = DSM_Context + local txtColor = LCD_BOX_COLOR + -- Gray Out any other line except the one been edited + if (ctx.EditLine~=lineNum) then txtColor=LCD_DISABLE_COLOR end + return txtColor +end + +-------------------------------------------------------------------------------------------------------- +-- Display Text inside a Rectangle. Inv: true means solid rectangle, false=only perimeter +local function GUI_Display_Boxed_Text(lineNum,x,y,w,h,text,inv, isNumber) + local ctx = DSM_Context + local txtColor = GUI_GetTextColor(lineNum) + local frameColor = GUI_GetFrameColor(lineNum) + -- If editing this lineNum, chose EDIT Color, else SELECTED Color + local selectedBGColor = (ctx.EditLine==lineNum and LCD_EDIT_BGCOLOR) or LCD_SELECTED_BGCOLOR + + if (inv) then + txtColor = LCD_SELECTED_COLOR + lcd.drawFilledRectangle(x-5, y-2, w, h, selectedBGColor) + else + lcd.drawRectangle(x-5, y-2, w, h, frameColor) + end + if (isNumber) then + lcd.drawNumber(x+w-10 , y, text, txtColor + RIGHT) + else + lcd.drawText(x , y, text, txtColor) + end +end + +------ Display Pre/Next/Back buttons +local function GUI_Diplay_Button(x,y,w,h,text,selected) + GUI_Display_Boxed_Text(-1,x,y,w,h,text,selected, false) +end + +------ Display MENU type of lines (Navigation, SubHeaders, and plain text comments) +local function GUI_Display_Line_Menu(lineNum,line,selected) + -- Menu Lines can be navidation to other Menus (if Selectable) + -- Or SubHeaders or Messages + + local txtColor = GUI_GetTextColor(lineNum) + local y = LCD_Y_LINE_START+(LCD_Y_LINE_HEIGHT*lineNum) + local x = LCD_X_LINE_MENU + + if menuLib.isSelectableLine(line) then -- Draw Selectable Menus in Boxes + GUI_Display_Boxed_Text(lineNum,x, y, LCD_W_LINE_MENU, LCD_Y_LINE_HEIGHT, line.Text,selected, false) + GUI_addTouchButton(x, y, LCD_W_LINE_MENU, LCD_Y_LINE_HEIGHT,lineNum) + else + -- Non Selectable Menu Lines, plain text + -- Can be use for sub headers or just regular text lines (like warnings) + + local bold = (menuLib.isDisplayAttr(line.TextAttr,DISP_ATTR._BOLD) and BOLD) or 0 + + if menuLib.isDisplayAttr(line.TextAttr,DISP_ATTR._RIGHT) then -- Right Align??? + local tw = my_lcd_sizeText(line.Text)+4 + x = LCD_X_LINE_VALUE - tw -- Right + elseif menuLib.isDisplayAttr(line.TextAttr,DISP_ATTR._CENTER) then -- Center?? + local tw = my_lcd_sizeText(line.Text) + x = x + (LCD_X_LINE_VALUE - LCD_X_LINE_MENU)/2 - tw/2 -- Center - 1/2 Text + end + + lcd.drawText(x, y, line.Text, txtColor + bold) + end +end + +------ Display NAME : VALUES type of lines +local function GUI_Display_Line_Value(lineNum, line, value, selected, editing) + -- This Displays Name and Value Pairs + local txtColor = GUI_GetTextColor(lineNum) + local bold = 0 + local y = LCD_Y_LINE_START+(LCD_Y_LINE_HEIGHT*lineNum) + local x = LCD_X_LINE_TITLE + + ---------- NAME Part + local header = line.Text + -- ONLY do this for Flight Mode (Right Align or Centered) + if (menuLib.isFlightModeLine(line)) then + -- Display Header + Value together + header = menuLib.GetFlightModeValue(line) + + -- Bold Text??? + bold = (menuLib.isDisplayAttr(line.TextAttr,DISP_ATTR._BOLD) and BOLD) or 0 + + if menuLib.isDisplayAttr(line.TextAttr,DISP_ATTR._RIGHT) then -- Right Align + local tw = my_lcd_sizeText(header)+4 + x = LCD_X_LINE_VALUE - tw -- Right + elseif menuLib.isDisplayAttr(line.TextAttr,DISP_ATTR._CENTER) then -- Centered + local tw = my_lcd_sizeText(header) + x = x + (LCD_X_LINE_VALUE - LCD_X_LINE_TITLE)/2 - tw/2 -- Center - 1/2 Text + end + else + -- No Flight Mode, no effects here + header = header .. ":" + end + + lcd.drawText(x, y, header, txtColor + bold) -- display Line Header + + + --------- VALUE PART, Skip for Flight Mode since already show the value + if (value==nil) then return end + + if not menuLib.isFlightModeLine(line) then + if menuLib.isSelectableLine(line) then + --if (editing) then -- Any Special color/effect when editing?? + -- value = "["..value .. "]" + --end + -- Can select/edit value, Box it + local tw = math.max(my_lcd_sizeText(value)+10,45) -- Width of the Text in the lcd + GUI_Display_Boxed_Text(lineNum,LCD_X_LINE_VALUE,y,tw,LCD_Y_LINE_HEIGHT,value,selected, not menuLib.isListLine(line)) + GUI_addTouchButton(LCD_X_LINE_VALUE,y,tw,LCD_Y_LINE_HEIGHT,lineNum) + + lcd.drawText(LCD_X_LINE_VALUE+tw+5, y, (line.Format or ""), txtColor + bold) + else -- Not Editable, Plain Text + lcd.drawText(LCD_X_LINE_VALUE, y, value, txtColor) + end + end + +end + +local function GUI_Display_Menu(menu) + local ctx = DSM_Context + local w= LCD_W_MENU_TITLE + + -- Center Header + local tw = my_lcd_sizeText(menu.Text) + local x = w/2 - tw/2 -- Center of Screen - Center of Text + + lcd.drawFilledRectangle(0, LCD_Y_MENU_TITLE-2, w, LCD_Y_LINE_HEIGHT-2, LCD_MENU_BGCOLOR) + lcd.drawText(x,LCD_Y_MENU_TITLE,menu.Text, LCD_MENU_COLOR + BOLD) + + -- Back Button + if menu.BackId ~= 0 then + GUI_Diplay_Button(437-5,LCD_Y_MENU_TITLE+3,47,LCD_Y_LINE_HEIGHT,"Back",ctx.SelLine == menuLib.BACK_BUTTON) + GUI_addTouchButton(437-5,LCD_Y_MENU_TITLE+3,47,LCD_Y_LINE_HEIGHT,menuLib.BACK_BUTTON) + end + -- Next Button + if menu.NextId ~= 0 then + GUI_Diplay_Button(437-5,LCD_Y_LOWER_BUTTONS,47,LCD_Y_LINE_HEIGHT,"Next",ctx.SelLine == menuLib.NEXT_BUTTON) + GUI_addTouchButton(437-5,LCD_Y_LOWER_BUTTONS,47,LCD_Y_LINE_HEIGHT,menuLib.NEXT_BUTTON) + end + -- Prev Button + if menu.PrevId ~= 0 then + GUI_Diplay_Button(10,LCD_Y_LOWER_BUTTONS,47,LCD_Y_LINE_HEIGHT,"Prev",ctx.SelLine == menuLib.PREV_BUTTON) + GUI_addTouchButton(10,LCD_Y_LOWER_BUTTONS,47,LCD_Y_LINE_HEIGHT,menuLib.PREV_BUTTON) + end +end + +------------------------------------------------------------------------------------------------------------ +-- Display the EDIT mode buttons when editing a value + +local function GUI_Display_Edit_Buttons(line) + GUI_clearTouchButtons() -- Only this buttons can be touched + local x = 15 -- Inittial X position + local w = 55 -- Width of the buttons + + local showPrev = line.Val > line.Min + local showNext = line.Val < line.Max + + GUI_Diplay_Button(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT,"ESC",true) + GUI_addTouchButton(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT,EDIT_BUTTON.ESC) + + x=x+w+10 + GUI_Diplay_Button(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT," Def",true) + GUI_addTouchButton(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT,EDIT_BUTTON.DEFAULT) + + x=x+w+10 + if (not menuLib.isListLine(line)) then + GUI_Diplay_Button(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT," << ",showPrev) + GUI_addTouchButton(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT,EDIT_BUTTON.DEC_10) + end + + x=x+w+10 + GUI_Diplay_Button(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT," <",showPrev) + GUI_addTouchButton(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT,EDIT_BUTTON.DEC_1) + + x=x+w+10 + GUI_Diplay_Button(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT," >",showNext) + GUI_addTouchButton(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT,EDIT_BUTTON.INC_1) + + x=x+w+10 + if (not menuLib.isListLine(line)) then + GUI_Diplay_Button(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT," >>",showNext) + GUI_addTouchButton(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT,EDIT_BUTTON.INC_10) + end + + x=x+w+10 + GUI_Diplay_Button(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT," OK",true) + GUI_addTouchButton(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT,EDIT_BUTTON.OK) + +end + +local function GUI_ShowBitmap(x,y,imgData) + -- imgData format "bitmap.png|alt message" + local f = string.gmatch(imgData, '([^%|]+)') -- Iterator over values split by '|' + local imgName, imgMsg = f(), f() + + lcd.drawText(x, y, imgMsg or "") -- Alternate Image MSG + + local imgPath = IMAGE_PATH .. (imgName or "") + local bitmap = Bitmap.open(imgPath) + if (bitmap~=nil) then + lcd.drawBitmap(bitmap, x,y+20) + end +end + +------------------------------------------------------------------------------------------------------------ +local function GUI_Display() + local ctx = DSM_Context + lcd.clear(LCD_TOOL_BGCOLOR) + GUI_clearTouchButtons() + + if LCD_W ~= 480 then + -- Different Resolution.. Maybe just adjusting some of the constants will work, adjust it in DSM_Init?? + -- LCD_X_LINE_TITLE, LCD_Y_LINE_START, etc + lcd.drawText(LCD_X_LINE_TITLE,100,"Only supported in Color Radios of 480 resolution", BLINK) + return + end + + local header = "DSM Forward Programming "..VERSION.." " + if ctx.Phase ~= PHASE.RX_VERSION then + header = header .. ctx.RX.Name.." v"..ctx.RX.Version + end + + --Draw title + lcd.drawFilledRectangle(0, 0, LCD_W, 17, LCD_TOOL_HDR_BGCOLOR) + lcd.drawText(5, 0, header, LCD_TOOL_HDR_COLOR + SMLSIZE) + + -- Getting RX Version + if ctx.Phase == PHASE.RX_VERSION then + if (ctx.isReset) then + lcd.drawText(LCD_X_LINE_TITLE,100, menuLib.Get_Text(0x301), BLINK) -- Resetting... + else + lcd.drawText(LCD_X_LINE_TITLE,100,menuLib.Get_Text(0x300), BLINK) -- Not valid RX + end + return + end + + + local menu = ctx.Menu + if menu.Text == nil then return end + + ----- Draw RX Menu --------- + GUI_Display_Menu(menu) + + -- Sending TX Information??? + if (ctx.Phase==PHASE.MENU_REQ_TX_INFO) then + --lcd.drawFilledRectangle(x-5, y-2, w, h, selectedBGColor) + --lcd.drawRectangle(x-5, y-2, w, h, frameColor) + lcd.drawText(LCD_X_LINE_TITLE,100, "Sending CH"..(ctx.CurLine+1)) -- Channel Info + return + end + + for i = 0, menuLib.MAX_MENU_LINES do + local line = ctx.MenuLines[i] + + if line ~= nil and line.Type ~= 0 then + if line.Type == LINE_TYPE.MENU then + GUI_Display_Line_Menu(i, line, i == ctx.SelLine) + else + local value = nil + if line.Val ~= nil then + value = line.Val + if menuLib.isListLine(line) then -- for Lists of Strings, get the text + value = menuLib.Get_List_Text(line.Val + line.TextStart) -- TextStart is the initial offset for text + -- Complentary IMAGE for this value to Display?? + local offset = 0 + if (line.Type==LINE_TYPE.LIST_MENU_ORI) then offset = offset + 0x100 end --FH6250 hack + + local imgData = menuLib.Get_List_Text_Img(line.Val + line.TextStart + offset) + + if (imgData and i == ctx.SelLine) then -- Optional Image and Msg for selected value + GUI_ShowBitmap(LCD_X_LINE_TITLE,LCD_Y_LINE_START, imgData) + end + end + end -- if Line[i]~=nil + GUI_Display_Line_Value(i, line, value, i == ctx.SelLine, i == ctx.EditLine) + end + end -- if ~MENU + end -- for + + if IS_EDGETX and ctx.isEditing() then + -- Display Touch button for Editing values + GUI_Display_Edit_Buttons(ctx.MenuLines[ctx.EditLine]) + end +end + +------------------------------------------------------------------------------------------------------------- +local function GUI_RotEncVal(line, dir) -- return encoder speed to inc or dec values + + if menuLib.isListLine(line) then return dir end + + local inc = 0 + local Speed = getRotEncSpeed() + + if Speed == ROTENC_MIDSPEED then inc = (5 * dir) + elseif Speed == ROTENC_HIGHSPEED then inc = (15 * dir) + else inc = dir end + + return inc +end + +------------------------------------------------------------------------------------ +-- Translate Tap/Touch of EDIT buttons to equivalent Key events +local function GUI_Translate_Edit_Buttons(button) + local event = EVT_TOUCH_TAP + local editInc = nil + + if (button==EDIT_BUTTON.ESC) then -- ESC + event = EVT_VIRTUAL_EXIT + elseif (button==EDIT_BUTTON.DEFAULT) then -- Default + event = EVT_VIRTUAL_ENTER_LONG + elseif (button==EDIT_BUTTON.DEC_10) then -- -10 + event = EVT_VIRTUAL_PREV + editInc = -10 + elseif (button==EDIT_BUTTON.DEC_1) then -- -1 + event = EVT_VIRTUAL_PREV + editInc = -1 + elseif (button==EDIT_BUTTON.INC_1) then -- +1 + event = EVT_VIRTUAL_NEXT + editInc = 1 + elseif (button==EDIT_BUTTON.INC_10) then -- + 10 + event = EVT_VIRTUAL_NEXT + editInc = 10 + elseif (button==EDIT_BUTTON.OK) then -- OK + event = EVT_VIRTUAL_ENTER + else + + end + + return event, editInc +end + +------------------------------------------------------------------------------------------------------------ +-- Handle Events comming from the GUI +local function GUI_HandleEvent(event, touchState) + local ctx = DSM_Context + local menu = ctx.Menu + local menuLines = ctx.MenuLines + local editInc = nil + + if (IS_EDGETX) then + if (event == EVT_TOUCH_TAP and ctx.isEditing()) then -- Touch and Editing + local button = GUI_getTouchButton(touchState.x, touchState.y) + if (button) then + event, editInc = GUI_Translate_Edit_Buttons(button) + end + end + + if (event == EVT_TOUCH_TAP or event == EVT_TOUCH_FIRST) and not ctx.isEditing() then -- Touch and NOT editing + if (DEBUG_ON) then Log.LOG_write("%s: EVT_TOUCH_TAP %d,%d\n",menuLib.phase2String(ctx.Phase),touchState.x, touchState.y) end + local button = GUI_getTouchButton(touchState.x, touchState.y) + if button then + -- Found a valid line + ctx.SelLine = button + ctx.Refresh_Display=true + if event == EVT_TOUCH_TAP then -- EVT_TOUCH_FIRST only move focus + event = EVT_VIRTUAL_ENTER + end + end + end + end -- IS_EDGETX + + if event == EVT_VIRTUAL_EXIT then + ctx.Refresh_Display=true + if (DEBUG_ON) then Log.LOG_write("%s: EVT_VIRTUAL_EXIT\n",menuLib.phase2String(ctx.Phase)) end + if ctx.Phase == PHASE.RX_VERSION then + menuLib.ChangePhase(PHASE.EXIT_DONE) -- Just Exit the Script + else + if ctx.isEditing() then -- Editing a Line, need to restore original value + local line = ctx.MenuLines[ctx.EditLine] + line.Val = originalValue + menuLib.Value_Write_Validate(line) + elseif (menu.BackId > 0 ) then -- Back?? + ctx.SelLine = menuLib.BACK_BUTTON + event = EVT_VIRTUAL_ENTER + else + menuLib.ChangePhase(PHASE.EXIT) + end + end + end + + if ctx.Phase == PHASE.RX_VERSION then return end + + if event == EVT_VIRTUAL_NEXT then + ctx.Refresh_Display=true + if (DEBUG_ON) then Log.LOG_write("%s: EVT_VIRTUAL_NEXT\n",menuLib.phase2String(ctx.Phase)) end + if ctx.isEditing() then -- Editing a Line, need to inc the value + local line=ctx.MenuLines[ctx.EditLine] + menuLib.Value_Add(line, editInc or GUI_RotEncVal(line, 1)) + else -- not editing, move selected line to NEXT + menuLib.MoveSelectionLine(1) + end + return + end + + if event == EVT_VIRTUAL_PREV then + ctx.Refresh_Display=true + if (DEBUG_ON) then Log.LOG_write("%s: EVT_VIRTUAL_PREV\n",menuLib.phase2String(ctx.Phase)) end + if ctx.isEditing() then -- Editiing a line, need to dec the value + local line=ctx.MenuLines[ctx.EditLine] + menuLib.Value_Add(line, editInc or GUI_RotEncVal(line, -1)) + else -- not editing, move selected line to PREV + menuLib.MoveSelectionLine(-1) + end + return + end + + if event == EVT_VIRTUAL_ENTER_LONG then + ctx.Refresh_Display=true + if (DEBUG_ON) then Log.LOG_write("%s: EVT_VIRTUAL_ENTER_LONG\n",menuLib.phase2String(ctx.Phase)) end + if ctx.isEditing() then + -- reset the value to default + menuLib.Value_Default(menuLines[ctx.EditLine]) -- Update value in RX if needed + end + return + end + + if event == EVT_VIRTUAL_ENTER then + ctx.Refresh_Display=true + if (DEBUG_ON) then Log.LOG_write("%s: EVT_VIRTUAL_ENTER, SelLine=%d\n",menuLib.phase2String(ctx.Phase), ctx.SelLine) end + if ctx.SelLine == menuLib.BACK_BUTTON then -- Back + if (menu.BackId==0xFFF9) then + -- SPECIAL Main Menu + GUI_SwitchToMainMenu() + else + menuLib.GotoMenu(menu.BackId,0x80) + end + elseif ctx.SelLine == menuLib.NEXT_BUTTON then -- Next + menuLib.GotoMenu(menu.NextId,0x82) + elseif ctx.SelLine == menuLib.PREV_BUTTON then -- Prev + menuLib.GotoMenu(menu.PrevId,0x81) + elseif menuLines[ctx.SelLine].ValId ~= 0 then -- Menu or Value + + if menuLines[ctx.SelLine].Type == LINE_TYPE.MENU then -- Navigate to Menu + if (menuLines[ctx.SelLine].ValId==0xFFF1) then + -- SPECIAL Simulation menu to Simulator + GUI_SwitchToSIM() + elseif (menuLines[ctx.SelLine].ValId==0xFFF2) then + -- SPECIAL Simulation menu to go to RX + GUI_SwitchToRX() + elseif (menuLines[ctx.SelLine].ValId==0xFFF3) then + -- SPECIAL Settup Menu + GUI_SwitchToSetupMenu() + elseif (menuLines[ctx.SelLine].ValId==0xFFF9) then + -- SPECIAL Settup Menu + GUI_SwitchToMainMenu() + else + menuLib.GotoMenu(menuLines[ctx.SelLine].ValId, ctx.SelLine) -- ValId is the MenuId to navigate to + end + else -- Enter on a Value + if ctx.isEditing() then -- already editing a Line???? + menuLib.Value_Write_Validate(menuLines[ctx.SelLine]) + else -- Edit the current value + ctx.EditLine = ctx.SelLine + originalValue = menuLines[ctx.SelLine].Val + menuLib.ChangePhase(PHASE.VALUE_CHANGING_WAIT) + end + end + end + end +end + +local function init_colors() + -- osName in OpenTX is nil, otherwise is EDGETX + local ver, radio, maj, minor, rev, osname = getVersion() + if (osname==nil) then osname = "OpenTX" end -- OTX 2.3.14 and below returns nil + + IS_EDGETX = string.sub(osname,1,1) == 'E' + + if (IS_EDGETX and USE_SPECKTRUM_COLORS) then + -- SPECKTRUM COLORS (only works on EDGETX) + LCD_TOOL_BGCOLOR = LIGHTWHITE + -- TOOL HEADER + LCD_TOOL_HDR_COLOR = WHITE + LCD_TOOL_HDR_BGCOLOR = DARKBLUE + -- MENU HEADER + LCD_MENU_COLOR = WHITE + LCD_MENU_BGCOLOR = DARKGREY + -- LINE SELECTED + LCD_SELECTED_COLOR = WHITE + LCD_SELECTED_BGCOLOR = ORANGE + LCD_EDIT_BGCOLOR = RED + -- NORMAL TEXT + LCD_NORMAL_COLOR = BLACK + LCD_DISABLE_COLOR = LIGHTGREY + LCD_DEBUG_COLOR = BLUE + -- NORMAL BOX FRAME COLOR + LCD_BOX_COLOR = LIGHTGREY + end +end + +local function GUI_Warning(event,touchState) + lcd.clear(LCD_TOOL_BGCOLOR) + local header = "DSM Forward Programming "..VERSION.." " + --Draw title + lcd.drawFilledRectangle(0, 0, LCD_W, 17, LCD_TOOL_HDR_BGCOLOR) + lcd.drawText(5, 0, header, LCD_TOOL_HDR_COLOR + SMLSIZE) + + lcd.drawText(100,20,"INFO", BOLD) + lcd.drawText(5,40,"DSM Forward programing shares TX Servo/Output settings", 0) + lcd.drawText(5,60,"with the RX. Make sure you setup your plane first in ", 0) + lcd.drawText(5,80,"the TX before your start Fwrd programming your RX.", 0) + lcd.drawText(5,100,"Wing & Tail type can be configured using this tool.", 0) + + lcd.drawText(5,150,"TX Gyro Servo settings are sent to the RX during 'Initial Setup'", 0) + lcd.drawText(5,170,"as well as when using RX 'Relearn Servo Settings'", 0) + lcd.drawText(5,200,"ALWAYS TEST Gyro reactions after this conditions before flying.", BOLD) + + lcd.drawText(100,250," OK ", INVERS + BOLD) + + if event == EVT_VIRTUAL_EXIT or event == EVT_VIRTUAL_ENTER or event == EVT_TOUCH_TAP then + warningScreenON = false + end + + return 0 +end + +------------------------------------------------------------------------------------------------------------ +-- Init +local function DSM_Init() + Log.LOG_open() + + init_colors() + modelLib.ReadTxModelData() + modelLib.ST_LoadFileData() + modelLib.CreateDSMPortChannelInfo() + + menuLib.Init() + menuProcessor.init() + return 0 +end + +------------------------------------------------------------------------------------------------------------ +-- Main +local function DSM_Run(event,touchState) + local ctx = DSM_Context + + if event == nil then + error("Cannot be run as a model script!") + Log.LOG_close() + return 2 + end + + if (warningScreenON) then + return GUI_Warning(event,touchState) + end + + GUI_HandleEvent(event,touchState) + + local ret = menuProcessor.run() -- Handle Send and Receive DSM Forward Programming Messages + + if ctx.Phase == PHASE.INIT then return 0 end + + + local refreshInterval = REFRESH_GUI_MS + + -- When using LCD BLINK attribute, we need faster refresh for BLINK to SHOW on LCD + if (ctx.Phase == PHASE.RX_VERSION or ctx.Phase==PHASE.MENU_REQ_TX_INFO) then -- Requesting RX Message Version usea BLINK? + ctx.Refresh_Display=true + refreshInterval = 20 -- 200ms + end + + if (not IS_EDGETX) then -- OPENTX NEEDS REFRESH ON EVERY CYCLE + GUI_Display() + -- Refresh display only if needed and no faster than 300ms, utilize more CPU to speedup DSM communications + elseif (ctx.Refresh_Display and (getTime()-lastRefresh) > refreshInterval) then --300ms from last refresh + GUI_Display() + ctx.Refresh_Display=false + lastRefresh=getTime() + end + + if ctx.Phase == PHASE.EXIT_DONE then + Log.LOG_close() + return 2 + else + return 0 + end +end + + +return { init=DSM_Init, run=DSM_Run } \ No newline at end of file diff --git a/SCRIPTS/TOOLS/DSM FwdPrg_56_MIN.lua b/SCRIPTS/TOOLS/DSM FwdPrg_56_MIN.lua new file mode 100644 index 0000000..9e99e70 --- /dev/null +++ b/SCRIPTS/TOOLS/DSM FwdPrg_56_MIN.lua @@ -0,0 +1,1088 @@ +local toolName = "TNS|DSM Frwd Prog v0.56 (MIN)|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. # +---- # # +---- ######################################################################### + + +local VERSION = "v0.56" +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" +local MSG_MIN_FILE_OFFSET = 20000 + +-- Phase +local PH_INIT = 0 +local PH_RX_VER, PH_TITLE, PH_TX_INFO, PH_LINES, PH_VALUES = 1, 2, 3, 4, 5 +local PH_VAL_CHANGING, PH_VAL_EDITING, PH_VAL_EDIT_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_NC, LT_LIST_NC2, LT_LIST, LT_LIST_ORI, LT_LIST_TOG = 0x6C, 0x6D, 0x0C, 0xCC, 0x4C +local LT_VALUE_NC = 0x60 +local LT_VALUE_PERCENT, LT_VALUE_DEGREES = 0xC0, 0xE0 + +local Phase = PH_INIT +local SendDataToRX = 1 -- Initiate Sending Data + +local Text = {} +local List_Text = {} +local List_Text_Img = {} +local Flight_Mode = "Flight Mode" +local RxName = {} + +local TXInactivityTime = 0 +local RXInactivityTime = 0 +local TX_Info_Step = 0 +local TX_Info_Type = 0 +local Change_Step = 0 +local originalValue = 0 + +local TX_MAX_CH = 12 - 6 -- Number of Channels after Ch6 + +--local ctx = { +local ctx_SelLine = 0 -- Current Selected Line +local ctx_EditLine = nil -- Current Editing Line +local ctx_CurLine = -1 -- Current Line Requested/Parsed via h message protocol +local ctx_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 + + 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 = "" +local RX_Version = "" + +local logFile = nil + +--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 gc() + collectgarbage("collect") +end + +--[[ +local function gcTable(t) + if type(t)=="table" then + for i,v in pairs(t) do + if type(v) == "table" then + gcTable(v) + end + t[i] = nil + end + end + gc() + return t +end +--]] + +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 + +----- Line Type +local function isIncrementalValueUpdate(line) + if (line.Type == LT_LIST_NC or line.Type == LT_LIST_NC2 or + line.Type == LT_VALUE_NC or line.Type == LT_VALUE_DEGREES) then return false end + return true +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_NC or line.Type==LT_LIST_NC2 or + line.Type == LT_LIST or line.Type == LT_LIST_ORI or line.Type == LT_LIST_TOG +end + +local function isEditing() + return ctx_EditLine ~= nil +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 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 + +local function GetTextInfoFromFile(pos) + local f=MSG_FILE + if (pos >= MSG_MIN_FILE_OFFSET) then -- Depending on offset, use the main, or MIN version + f = MSG_FILE_MIN + pos = pos - MSG_MIN_FILE_OFFSET + end + + -- open and read File + local dataFile = io.open(f, "r") + io.seek(dataFile,pos) + local buff = io.read(dataFile, 100) + io.close(dataFile) + + local line="" + local index="" + local type="" + + local pipe=0 + local comment=0 + local newPos = pos + + -- Parse the line: + -- Format: TT|0x999|Text -- Comment + for i=1,#buff do + newPos=newPos+1 + local ch = string.sub(buff,i,i) + + if (pipe < 2 and ch=="|") then pipe=pipe+1 -- Count pipes pos (Type | Index | .....) + elseif (ch=="/") then pipe=6 -- far right end, like comment + elseif (ch=="\r") then -- Ignore CR + elseif (ch=="\n") then break -- LF, end of line + elseif (ch=="-") then -- March comments + comment=comment+1 + if (comment==2) then pipe=6 end -- Comment part of line + else + -- regular char + comment=0 + if (pipe==0) then type=type..ch -- in TT (Type) + elseif (pipe==1) then index=index..ch -- in Index + elseif (pipe<6) then line=line..ch end -- in Text + end -- Regular char + end -- Fpr + gc() + return type, index, rtrim(line), newPos +end + +local function GetTextFromFile(pos) + if (pos==nil) then return nil end + local t,i,l, p = GetTextInfoFromFile(pos) + return l +end + +local function getTxChText(index) + local ch = nil + local out = nil + + if (index >= 0x000D and index <= 0x000D+7) then ch = index - 0x000D + 5 -- ch5 + elseif (index >= 0x0036 and index <= 0x0036+11) then ch = index - 0x0036 end + + if (ch ~= nil) then + out = "Ch"..(ch+1) .. " ("..(MODEL.TX_CH_TEXT[ch] or "--")..")" + --out = MODEL.PORT_TEXT[ch] or "--" + end + + return out +end + +----------------------- +local function Get_Text(index) + local pos = Text[index] + local out = nil + + if (pos == nil) then + out = string.format("Unknown_%X", index) + else + out = GetTextFromFile(pos) + end + + if (index >= 0x8000) then + out = Flight_Mode + end + return out +end + +local function Get_Text_Value(index) + local out = getTxChText(index) + if (out) then return out end + + local pos = List_Text[index] + if (pos == nil) then + out = Get_Text(index) + else + out = GetTextFromFile(pos) + end + + return out +end +--------------------- +local function Get_RxName(index) + local out = RxName[index] or string.format("Unknown_%X", index) + return out +end +-------------------- + +local function updateValText(line) + line.ValText = line.Val + if (isListLine(line)) then + line.ValText = Get_Text_Value(line.TextStart + line.Val) + end +end + +local function DSM_Connect() + --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')) +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 +--------------------- + +function ChangePhase(newPhase) + Phase = newPhase + SendDataToRX = 1 +end + +local function Value_Add(dir) + local line = MenuLines[ctx_SelLine] + local origVal = line.Val + local inc = dir + + if (not isListLine(line)) then -- List do slow inc + local Speed = getRotEncSpeed() + if Speed == ROTENC_MIDSPEED then + inc = (5 * dir) + elseif Speed == ROTENC_HIGHSPEED then + inc = (15 * dir) + end + end + + line.Val = line.Val + inc + + if line.Val > line.Max then + line.Val = line.Max + elseif line.Val < line.Min then + line.Val = line.Min + end + + if (origVal~=line.Val) then -- Any changes? + if (isIncrementalValueUpdate(line)) then + -- Update RX value on every change, otherwise, just at the end + ChangePhase(PH_VAL_CHANGING) + end + updateValText(line) + end +end +-------------- + +local function GotoMenu(menuId, lastSelectedLine) + Menu.MenuId = menuId + ctx_SelLine = lastSelectedLine + -- Request to load the menu Again + ChangePhase(PH_TITLE) +end + +local function DSM_HandleEvent(event) + if event == EVT_VIRTUAL_EXIT then + if Phase == PH_RX_VER then + Phase = PH_EXIT_DONE -- Exit program + else + if isEditing() then -- Editing a Line, need to restore original value + MenuLines[ctx_EditLine].Val = originalValue + event = EVT_VIRTUAL_ENTER + else + if (Menu.BackId > 0 ) then -- Back?? + ctx_SelLine = -1 --Back Button + event = EVT_VIRTUAL_ENTER + else + ChangePhase(PH_EXIT_REQ) + end + end + end + end -- Exit + + if Phase == PH_RX_VER then return end -- nothing else to do + + if event == EVT_VIRTUAL_NEXT then + if isEditing() then -- Editting? + Value_Add(1) + else + 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 + return + end + + if event == EVT_VIRTUAL_PREV then + if isEditing() 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 + return + end + + if event == EVT_VIRTUAL_ENTER_LONG then + if isEditing() then + MenuLines[ctx_SelLine].Val = MenuLines[ctx_SelLine].Def + ChangePhase(PH_VAL_CHANGING) + 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 isEditing() then + ctx_EditLine = nil -- Done Editting + ChangePhase(PH_VAL_EDIT_END) + else -- Start Editing + ctx_EditLine = ctx_SelLine + originalValue = MenuLines[ctx_SelLine].Val + ChangePhase(PH_VAL_EDITING) + 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] + local b0, b1, b2 = info[0], info[1], info[2] + DSM_Send(0x20, 0x06, portNo, portNo, b0,b1) + LOG_write("TX:DSM_TxInfo_20(Port=#%d, (%02x, %02x) %s)\n", portNo, b0,b1,b2 or "") + + if (TX_Info_Type == 0x1F) then -- SmartRx + TX_Info_Step = 1 + elseif (TX_Info_Type == 0x00) then -- AR636 + 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("TX:DSM_TxInfo_Travel(Port=#%d,(L=%d - R=%d))\n", portNo,leftTravel,rightTravel) + + TX_Info_Step = 2 + elseif (TX_Info_Step == 2) then + -- Subtrim + local b1,b2,b3,b4 = 0x00, 0x8E, 0x07, 0x72 -- (192-1904) + if (portNo==0) then -- Thr + b1,b2,b3,b4 = 0x00, 0x00, 0x07, 0xFF -- (0-2047) + end + + DSM_Send(0x21, 0x06, b1,b2,b3,b4) -- Port is not send anywhere, since the previous 0x20 type message have it. + LOG_write("TX:DSM_TxInfo_SubTrim(Port=#%d)\n", portNo) + + if (TX_Info_Type == 0x00) then -- AR636 + TX_Info_Step = 5 -- End Step + else + TX_Info_Step = 3 + end + elseif (TX_Info_Step == 3) then + LOG_write("TX: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("TX: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("TX:DSM_TxInfo_END(Port=#%d)\n", portNo) + DSM_Send(0x22, 0x04, 0x00, 0x00) + TX_Info_Step = 0 -- Done!! + end + + if (TX_Info_Step > 0) then + SendDataToRX = 1 -- keep Transmitig + end +end + +local function DSM_SendUpdate(line) + local valId = line.ValId + local value = sInt16ToDsm(line.Val) + + LOG_write("TX:ChangeValue(VId=0x%04X,Val=%d)\n", valId, line.Val) + DSM_Send(0x18, 0x06, + int16_MSB(valId), int16_LSB(valId), + int16_MSB(value), int16_LSB(value)) -- send current values +end + +local function DSM_SendValidate(line) + local valId = line.ValId + LOG_write("TX:ValidateValue(VId=0x%04X)\n", valId) + DSM_Send(0x19, 0x04, int16_MSB(valId), int16_LSB(valId)) +end + +local function DSM_SendRequest() + --LOG_write("DSM_SendRequest Phase=%d\n",Phase) + -- Need to send a request + local menuId = Menu.MenuId + local menuLSB = int16_LSB(menuId) + local menuMSB = int16_MSB(menuId) + + if Phase == PH_RX_VER then -- request RX version + DSM_Send(0x11, 0x06, TX_MAX_CH, 0x14, 0x00, 0x00) + LOG_write("TX:GetVersion(TX_MAX_CH=%d)\n",TX_MAX_CH+6) + + elseif Phase == PH_WAIT_CMD then -- keep connection open + DSM_Send(0x00, 0x04, 0x00, 0x00) + --LOG_write("TX:TxHb\n") + + elseif Phase == PH_TITLE then -- request menu title + if menuId == 0 then + -- very first menu only + DSM_Send(0x12, 0x06, TX_MAX_CH, 0x14, 0x00, 0x00) + else + -- Any other menu + DSM_Send(0x16, 0x06, menuMSB, menuLSB, 0x00, ctx_SelLine) + if (menuId == 0x0001) then -- Executed Save&Reset menu + Phase = PH_RX_VER + ctx_isReset = true + end + end + LOG_write("TX: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 + if ctx_CurLine == -1 then + DSM_Send(0x13, 0x04, menuMSB, menuLSB) -- GetFirstLine + else + DSM_Send(0x14, 0x06, menuMSB, menuLSB, 0x00, ctx_CurLine) -- line X + end + LOG_write("TX:GetNextLine(LastLine=%d)\n", ctx_CurLine) + + elseif Phase == PH_VALUES then -- request menu values + local valId = MenuLines[ctx_CurLine].ValId + DSM_Send(0x15, 0x06, + menuMSB, menuLSB, + int16_MSB(valId), int16_LSB(valId)) + LOG_write("TX:GetNextValue(LastVId=0x%04X)\n", valId) + + elseif Phase == PH_VAL_EDITING then -- Editing a line (like a HB) + local line = MenuLines[ctx_SelLine] + DSM_Send(0x1A, 0x04, 0x00, ctx_SelLine) + LOG_write("TX:EditingValueLine(L=%d)\n", ctx_SelLine) + + elseif Phase == PH_VAL_CHANGING then -- change value during editing + local line = MenuLines[ctx_SelLine] + if (Change_Step==0) then + DSM_SendUpdate(line) + if line.Type == LT_LIST then -- Incremental Validation?? + Change_Step=1 + end + else -- Change_Step==1 + DSM_SendValidate(line) + Change_Step=0 + end + if (Change_Step==0) then Phase=PH_VAL_EDITING else SendDataToRX=1 end -- Done with change? + + elseif Phase == PH_VAL_EDIT_END then -- Done Editing line + local line = MenuLines[ctx_SelLine] + + if (Change_Step==0) then + DSM_SendUpdate(line) + Change_Step=1 + elseif (Change_Step==1) then + DSM_SendValidate(line) + Change_Step=2 + else -- Change_Step==3 + LOG_write("TX:EditValueEnd(L=%d)\n", ctx_SelLine) + DSM_Send(0x1B, 0x04, 0x00, ctx_SelLine) + Change_Step=0 + end + if (Change_Step==0) then Phase = PH_WAIT_CMD else SendDataToRX=1 end -- Done with change? + + elseif Phase == PH_EXIT_REQ then -- EXIT Request + DSM_Send(0x1F, 0x02, 0xAA) + LOG_write("TX:TX Exit Request\n") + end +end + +local function DSM_ProcessResponse() + local cmd = multiBuffer(11) + if cmd == 0x01 then -- read version + RX_Name = Get_RxName(multiBuffer(13)) + RX_Version = multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16) + + Menu.MenuId = 0 + Phase = PH_TITLE + LOG_write("RX: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] = { Type = 0 } + end + ctx_CurLine = -1 + ctx_SelLine = -1 -- highlight Back + + LOG_write("RX:Menu: Mid=0x%04X \"%s\"\n", menu.MenuId, menu.Text) + + if (menu.MenuId == 0x0001) then -- Still in RESETTING MENU??? + 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)) + + -- Signed 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.TextStart = line.Min + line.Def = line.Def - line.Min -- normalize default value + line.Max = line.Max - line.Min -- normalize 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("RX:Line: #%d Vid=0x%04X T=0x%02X \"%s\"\n", i, line.ValId, type, line.Text) + + if (line.MenuId~=Menu.MenuId) then -- Going Back too fast: Stil receiving lines from previous menu + Menu.MenuId = line.MenuId + end + + 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 + + updateValText(line) + + local debugTxt = line.Val + if isListLine(line) then + debugTxt = line.ValText .. " [" .. value .. "]" + end + + LOG_write("RX: Value Updated: #%d VId=0x%04X Value=%s\n", i, valId, debugTxt) + 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("RX:TXInfoReq: Port=%d T=0x%02X\n", ctx_CurLine, TX_Info_Type) + + elseif cmd == 0xA7 then -- RX EXIT Request + if Phase == PH_EXIT_REQ then -- Response to our EXIT req + Phase = PH_EXIT_DONE + else -- Unexpected RX Exit + DSM_Release() + error("RX Connection Drop") + end + + elseif cmd == 0x00 then -- RX Heartbeat + LOG_write("RX:RxHb\n") + end + + return cmd +end + + +local function DSM_Send_Receive() + + -- Receive part: Process incoming messages if there is nothing to send + if SendDataToRX==0 and multiBuffer(10) == 0x09 then + local cmd = DSM_ProcessResponse() + -- Data processed + multiBuffer(10, 0x00) + RXInactivityTime = getTime() + 800 -- Reset Inactivity timeout (8s) + + if (cmd > 0x00) then -- RX-HeartBeat?? + -- Only change to SEND mode if we received a valid response (Ignore heartbeat) + SendDataToRX = 1 + end + else + -- Check if enouth time has passed from last Received activity + if (getTime() > RXInactivityTime and Phase~=PH_RX_VER and Phase~= PH_EXIT_DONE) then + if (isEditing()) then -- If Editing, extend time + RXInactivityTime = getTime() + 400 + else + DSM_Release() + error("RX Disconnected") + end + end + end + + -- Sending part -- + if SendDataToRX == 1 then + SendDataToRX = 0 + DSM_SendRequest() + TXInactivityTime = getTime() + 200 -- Reset Inactivity timeout (2s) + else + -- Check if enouth time has passed from last transmit activity + if getTime() > TXInactivityTime then + SendDataToRX = 1 -- Switch to Send mode to send heartbeat + + -- Change to Idle/HB mode if we are wating for RX version + if Phase ~= PH_RX_VER then + -- Phase = If IsEditing then PH_VAL_EDITING else PH_WAIT_CMD + Phase = (isEditing() and PH_VAL_EDITING) or 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(1, 3 * LCD_Y_LINE_HEIGHT, Get_Text(msgId), BLINK) + return + end + + -- display Program version or RX version + local msg = RX_Name .. " v" .. RX_Version + ver_rx_count = ver_rx_count + 1 + if (ver_rx_count > 50) then + msg = "FProg "..VERSION + if (ver_rx_count > 100) then ver_rx_count=0 end + end + lcd.drawText(40, 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) + + if (Phase == PH_TX_INFO) then + lcd.drawText(1, 3 * LCD_Y_LINE_HEIGHT, "Sending CH"..(ctx_CurLine+1), TEXT_ATTR) + end + + 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.Type ~= 0 then + local heading = line.Text + + if (line.TextId >= 0x8000) then -- Flight mode + heading = " " .. Flight_Mode .. " " + if (line.Val==nil) then heading = heading .. "--" else heading = heading .. ((line.Val or 0) + 1) end + else + local text = nil + if line.Type ~= LT_MENU then -- list/value + if line.Val ~= nil then -- Value to display?? + -- text = line.Val + text = line.ValText + + if isListLine(line) then + local textId = line.Val + line.TextStart + --text = Get_Text_Value(textId) + + -- image?? + local offset = 0 + if (line.Type==LT_LIST_ORI) then offset = offset + 0x100 end --FH6250 hack + local imgDesc = GetTextFromFile(List_Text_Img[textId+offset]) + + if (imgDesc and i == ctx_SelLine) then -- Optional Image and Msg for selected value + showBitmap(1, 20, imgDesc) + end + end + end -- Value + + if (ctx_EditLine == i) then -- Editing a Line + attrib = BLINK + INVERS + TEXT_ATTR + end + lcd.drawText(LCD_X_MAX, y, text or "--", attrib + RIGHT) -- display value + attrib = TEXT_ATTR + end -- Line with value/list + end -- not Flight mode + lcd.drawText(1, 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(1, LCD_Y_LOWER_BUTTONS, "Prev", ctx_SelLine == 8) + end +end + +----------------------------------------------------------------------------------------- + +local function load_msg_from_file(fileName, offset, FileState) + + if (FileState.state==nil) then -- Initial State + FileState.state=1 + FileState.lineNo=0 + FileState.filePos=0 + end + + if FileState.state==1 then + for l=1,10 do -- do 10 lines at a time + local type, sIndex, text + local lineStart = FileState.filePos + + type, sIndex, text, FileState.filePos = GetTextInfoFromFile(FileState.filePos+offset) + + --print(string.format("T=%s, I=%s, T=%s LS=%d, FP=%d",type,sIndex,text,lineStart, FileState.filePos)) + + if (lineStart==FileState.filePos) then -- EOF + FileState.state=2 --EOF State + return 1 + end + FileState.lineNo = FileState.lineNo + 1 + + type = rtrim(type) + + if (string.len(type) > 0 and string.len(sIndex) > 0) then + local index = tonumber(sIndex) + local filePos = lineStart + offset + + if (index == nil) then + assert(false, string.format("%s:%d: Invalid Hex num [%s]", fileName, FileState.lineNo, sIndex)) + elseif (type == "T") then + Text[index] = filePos + elseif (type == "LT") then + List_Text[index] = filePos + elseif (type == "LI") then + List_Text_Img[index] = filePos + elseif (type == "FM") then + Flight_Mode = text + elseif (type == "RX") then + RxName[index] = text + else + assert(false, string.format("%s:%d: Invalid Line Type [%s]", fileName, FileState.lineNo, type)) + end + end + gc() + end -- for + end -- if + + return 0 +end + +------------------------------------------------------------------------------------------------------------ +-- Init +local function DSM_Init() + --LOG_open() + --LOG_write("--- NEW SESSION\n") + + --DSM_Init_Model() + + --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 + + 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 + + Phase = PH_INIT +end + + +----------------------------------------------------------------------------------------------------------- +local initStep=0 +local FileState = { lineNo=0 } + +local function Inc_Init() + lcd.clear() + + lcd.drawText(1, 0, "Loading Msg file: "..(FileState.lineNo or 0)) + if (initStep == 0) then + if (load_msg_from_file(MSG_FILE, 0, FileState)==1) then + initStep=1 + FileState = {} + end + else + Phase = PH_RX_VER -- Done Init + DSM_Connect() + end +end + +------------------------------------------------------------------------------------------------------------ +-- Main + +local function DSM_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + end + + if (Phase == PH_INIT) then + Inc_Init() -- Incremental initialization + return 0 + end + + DSM_Display() + DSM_HandleEvent(event) + DSM_Send_Receive() + gc() + + if Phase == PH_EXIT_DONE then + DSM_Release() + LOG_close() + return 2 + else + return 0 + end +end + +--- + gc() + local M_DATA = {} + + -- Load Model Configuration + local r = assert(loadScript(DSMLIB_PATH.."DsmMIN_P1.lua"), "Mising: DsmMIN_P1.lua") + (MODEL, M_DATA, LOG_write) + gc() + if (r==1) then + -- Translate model Configuration to DSMDATA + local r = assert(loadScript(DSMLIB_PATH.."DsmMIN_P2.lua"), "Missing DsmMIN_P2.lua") + (MODEL, M_DATA, LOG_write) + + MODEL.PORT_TEXT = nil + --MODEL.TX_CH_TEXT = {} + gc() + else + error("Cannot load Model Config") + end + M_DATA = nil + gc() +--- + +return { init = DSM_Init, run = DSM_Run } diff --git a/SCRIPTS/TOOLS/DSM FwdPrg_56_STUP.lua b/SCRIPTS/TOOLS/DSM FwdPrg_56_STUP.lua new file mode 100644 index 0000000..efadc83 --- /dev/null +++ b/SCRIPTS/TOOLS/DSM FwdPrg_56_STUP.lua @@ -0,0 +1,951 @@ +local toolName = "TNS|DSM Frwd Prog v0.56a (MIN-SETUP)|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. # +---- # # +---- ######################################################################### + + +local VERSION = "v0.56" +local DSMLIB_PATH = "/SCRIPTS/TOOLS/DSMLIB/" +local DATA_PATH = "/MODELS/DSMDATA" + +local LOG_FILE = "/LOGS/dsm_min_log.txt" + +-- Phase +local PH_INIT = 0 +local PH_RX_VER, PH_TITLE = 1, 2 +local PH_VAL_CHANGING, PH_VAL_EDITING, PH_VAL_EDIT_END = 6, 7, 8 +local PH_WAIT_CMD, PH_EXIT_REQ, PH_EXIT_DONE = 9, 10, 11 + +-- Line Types +local LT_MENU, LT_LIST_NC = 0x1C, 0x6C + +local Phase = PH_INIT + +local Text = {} +local List_Text = {} +local List_Text_Img = {} + +local originalValue = 0 + +local ctx_SelLine = 0 -- Current Selected Line +local ctx_EditLine = nil -- Current Editing Line + +local Menu = { MenuId = 0, Text = "", TextId = 0, PrevId = 0, NextId = 0, BackId = 0 } +local MenuLines = {} + +local logFile = nil + +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 TX_CHANNELS = 12 + +local AT_PLANE = 0 + +local aircraft_type_text = {[0]="Plane","Heli","Glider","Drone"} + +local WT_A1 = 0 +local WT_A2 = 1 +local WT_FLPR = 2 +local WT_A1_F1 = 3 +local WT_A2_F1 = 4 +local WT_A2_F2 = 5 +local WT_ELEVON_A = 6 +local WT_ELEVON_B = 7 + +local wing_type_text = {[0]="Normal","Dual Ail","Flapperon", "Ail + Flp","Dual Ail + Flp","Dual Ail/Flp","Elevon A","Elevon B"} + +local TT_R1 = 0 +local TT_R1_E1 = 1 +local TT_R1_E2 = 2 +local TT_R2_E1 = 3 +local TT_R2_E2 = 4 +local TT_VT_A = 5 +local TT_VT_B = 6 +local TT_TLRN_A = 7 +local TT_TLRN_B = 8 +local TT_TLRN_A_R2 = 9 +local TT_TLRN_B_R2 = 10 + +local tail_type_text = {[0]="Rud Only","Normal","Rud + Dual Ele","Dual Rud + Elv","Dual Rud/Ele", + "VTail A","VTail B","Taileron A","Taileron B", + "Taileron A + Dual Rud","Taileron B + Dual Rud" + } + +local MT_NORMAL = 0 +local MT_REVERSE = 1 + +local P1 = 0 +local P2 = 1 +local P3 = 2 +local P4 = 3 +local P5 = 4 +local P6 = 5 +local P7 = 6 +local P8 = 7 +--local P9 = 8 +--local P10 = 9 + +local MV_AIRCRAFT_TYPE = 1001 +local MV_WING_TYPE = 1002 +local MV_TAIL_TYPE = 1003 + +local MV_CH_BASE = 1010 +local MV_CH_THR = 1010 + +local MV_CH_L_AIL = 1011 +local MV_CH_R_AIL = 1012 +local MV_CH_L_FLP = 1013 +local MV_CH_R_FLP = 1014 + +local MV_CH_L_RUD = 1015 +local MV_CH_R_RUD = 1016 +local MV_CH_L_ELE = 1017 +local MV_CH_R_ELE = 1018 + +local MV_PORT_BASE = 1020 +local MV_P1_MODE = 1020 +--local MV_P2_MODE = 1021 +--local MV_P3_MODE = 1022 +--local MV_P4_MODE = 1023 +--local MV_P5_MODE = 1024 +local MV_P6_MODE = 1025 +--local MV_P7_MODE = 1026 +--local MV_P8_MODE = 1027 +--local MV_P9_MODE = 1028 +--local MV_P10_MODE = 1029 + +local MV_DATA_END = 1040 + +-- MENU DATA Management +local M_DB = {} -- Store the variables used in the Menus. + +local lastGoodMenu=0 + +local currATyp = -1 +local currTTyp = -1 +local currWTyp = -1 + +local menuDataChanged = false + +local MODEL = { + modelName = "", -- The name of the model comming from OTX/ETX + hashName = nil, + modelOutputChannel = {}, -- Output information from OTX/ETX + + TX_CH_TEXT= { }, + PORT_TEXT = { }, + + DSM_ChannelInfo = {} -- Data Created by DSM Configuration Script +} + +local function gc() + collectgarbage("collect") +end + +--[[ +local function gcTable(t) + if type(t)=="table" then + for i,v in pairs(t) do + if type(v) == "table" then + gcTable(v) + end + t[i] = nil + end + end + gc() + return t +end +--]] + +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 + +-- Saves MENU_DATA to a file +local function ST_SaveFileData() + local fname = MODEL.hashName + + print("Saving File:"..fname) + local dataFile = assert(io.open(DATA_PATH .. "/" .. fname, "w"),"Please create "..DATA_PATH.." folder") -- write File + + -- Foreach MENU_DATA with a value write Var_Id:Value into file + for i = 0, MV_DATA_END do + if (M_DB[i]~=nil) then + io.write(dataFile,string.format("%s:%s\n",i,M_DB[i])) + end + end + io.close(dataFile) +end + +local function tailTypeCompatible(a,b) + + local function normalize(tt) + if (tt==TT_TLRN_A or tt==TT_TLRN_B) then + return TT_TLRN_A + elseif (tt==TT_TLRN_A_R2 or tt==TT_TLRN_B_R2) then + return TT_TLRN_A_R2 + elseif (tt==TT_VT_A or tt==TT_VT_B) then + return TT_VT_A + else + return tt + end + end + + return (normalize(a)==normalize(b)) +end + +local function ST_PlaneWingInit(wingType) + --print("Change Plane WingType:"..wing_type_text[wingType]) + + M_DB[MV_WING_TYPE] = wingType + + -- Clear all Wing Data + M_DB[MV_CH_L_AIL] = nil + M_DB[MV_CH_R_AIL] = nil + M_DB[MV_CH_L_FLP] = nil + M_DB[MV_CH_R_FLP] = nil + + M_DB[MV_CH_THR] = P1 + + -- Default Channel Assisgments for each Wing type + + if (wingType==WT_A1) then + M_DB[MV_CH_L_AIL] = P2 + elseif (wingType==WT_A2 or wingType==WT_FLPR) then + M_DB[MV_CH_L_AIL] = P6 + M_DB[MV_CH_R_AIL] = P2 + elseif (wingType==WT_A1_F1) then + M_DB[MV_CH_L_AIL] = P2 + M_DB[MV_CH_L_FLP] = P6 + elseif (wingType==WT_A2_F1) then + M_DB[MV_CH_L_AIL] = P6 + M_DB[MV_CH_R_AIL] = P2 + M_DB[MV_CH_L_FLP] = P5 + elseif (wingType==WT_A2_F2) then + M_DB[MV_CH_L_AIL] = P6 + M_DB[MV_CH_R_AIL] = P2 + M_DB[MV_CH_R_FLP] = P5 + M_DB[MV_CH_L_FLP] = P7 + elseif (wingType==WT_ELEVON_A) then + M_DB[MV_CH_L_AIL] = P2 + M_DB[MV_CH_R_AIL] = P3 + elseif (wingType==WT_ELEVON_B) then + M_DB[MV_CH_L_AIL] = P3 + M_DB[MV_CH_R_AIL] = P2 + else -- Assume normal + print("ERROR: Invalid Wing Type") + end +end + +local function ST_PlaneTailInit(tailType) + if (M_DB[MV_WING_TYPE]==WT_ELEVON_A or + M_DB[MV_WING_TYPE]==WT_ELEVON_B) then + tailType = TT_R1 -- Delta only have ruder + end + + --print("Change Plane Tail Type:"..tail_type_text[tailType]) + + -- Clear all data for Tail + M_DB[MV_TAIL_TYPE] = tailType + M_DB[MV_CH_L_ELE] = nil + M_DB[MV_CH_R_ELE] = nil + M_DB[MV_CH_L_RUD] = nil + M_DB[MV_CH_R_RUD] = nil + + -- Setup Channels for different Tail types + if (tailType == TT_R1) then + M_DB[MV_CH_L_RUD] = P4 + elseif (tailType == TT_R1_E1) then + M_DB[MV_CH_L_ELE] = P3 + M_DB[MV_CH_L_RUD] = P4 + elseif (tailType == TT_R1_E2) then + M_DB[MV_CH_L_ELE] = P5 + M_DB[MV_CH_R_ELE] = P3 + M_DB[MV_CH_L_RUD] = P4 + elseif (tailType == TT_R2_E1) then + M_DB[MV_CH_L_ELE] = P3 + M_DB[MV_CH_L_RUD] = P4 + M_DB[MV_CH_R_RUD] = P5 + elseif (tailType == TT_R2_E2) then + M_DB[MV_CH_L_ELE] = P5 + M_DB[MV_CH_R_ELE] = P3 + M_DB[MV_CH_L_RUD] = P4 + M_DB[MV_CH_R_RUD] = P6 + elseif (tailType == TT_VT_A or tailType == TT_VT_B) then + M_DB[MV_CH_L_ELE] = P3 + M_DB[MV_CH_R_ELE] = P4 + elseif (tailType == TT_TLRN_A or tailType == TT_TLRN_B or + tailType == TT_TLRN_A_R2 or tailType == TT_TLRN_B_R2) then + M_DB[MV_CH_L_RUD] = P4 + M_DB[MV_CH_L_ELE] = P5 + M_DB[MV_CH_R_ELE] = P3 + else -- Assume Normal + print("ERROR:invalid Tail Type") + end + + if (tailType == TT_TLRN_A_R2 or tailType == TT_TLRN_B_R2) then + M_DB[MV_CH_R_RUD] = P8 + end +end + +local function ST_AircraftInit(aircraftType) + M_DB[MV_AIRCRAFT_TYPE] = aircraftType + ST_PlaneWingInit(WT_A1) + ST_PlaneTailInit(TT_R1_E1) +end + + +-- Setup Initial Default Data for the Menus +local function ST_Default_Data() + ST_AircraftInit(AT_PLANE) + + for i=0,9 do + M_DB[MV_P1_MODE+i] = MT_NORMAL + MODEL.modelOutputChannel[P1+i].revert + end +end + +local function MenuLinePostProcessing(line) + line.MenuId = Menu.MenuId + + if line.Type == LT_MENU then + -- nothing to do on menu entries + line.Val=nil + elseif line.Type == LT_LIST_NC then + -- Normalize Min/Max to be relative to Zero + line.TextStart = line.Min + line.Def = line.Def - line.Min -- normalize default value + line.Max = line.Max - line.Min -- normalize max index + line.Min = 0 -- min index + end +end + + +local function portUse(p) + local out = nil + if p==M_DB[MV_CH_THR] then out = "Thr" + elseif p == M_DB[MV_CH_L_AIL] then + out=(M_DB[MV_CH_R_AIL] and "Ail_L") or "Ail" + elseif p == M_DB[MV_CH_R_AIL] then out="Ail_R" + elseif p == M_DB[MV_CH_L_ELE] then + out=(M_DB[MV_CH_R_ELE] and "Ele_L") or "Ele" + elseif p == M_DB[MV_CH_R_ELE] then out="Ele_R" + elseif p == M_DB[MV_CH_L_RUD] then + out=(M_DB[MV_CH_R_RUD] and "Rud_L") or "Rud" + elseif p == M_DB[MV_CH_R_RUD] then out="Rud-R" + elseif p == M_DB[MV_CH_L_FLP] then + out=(M_DB[MV_CH_R_FLP] and "Flp_L") or "Flp" + elseif p == M_DB[MV_CH_R_FLP] then out="Flp_R" + else + out = "" + end + return out +end + +-- Creates the menus to Render with the GUI +local function ST_LoadMenu(menuId) + + local function Header(p) + return MODEL.PORT_TEXT[p].." "..portUse(p) + end + + local function generateGyroReverse(menuId, P_BASE, V_BASE) + for i=0,4 do + MenuLines[i] = { Type = LT_LIST_NC, Text=Header(P_BASE+i), TextId = 0, ValId = V_BASE+i, Min=45, Max=46, Def=45, Val=M_DB[V_BASE+i] } + end + + MenuLines[5] = { Type = LT_MENU, Text="Only TAER affects AS3X/SAFE react dir", TextId = 0, ValId = menuId } + MenuLines[6] = { Type = LT_MENU, Text="If changes, RX 'Relearn Servo'", TextId = 0, ValId = menuId } + + ctx_SelLine = 0 + end + + -- Begin + for i = 0, 6 do -- clear menu + MenuLines[i] = { MenuId = 0, lineNum = 0, Type = 0 } + end + + + if (menuId==0x1000) then -- MAIN MENU + Menu = { MenuId = 0x1000, Text = "Save-Exit ("..MODEL.modelName..")", PrevId = 0, NextId = 0, BackId = 0, TextId=0 } + + if (true) then + MenuLines[4] = { Type = LT_MENU, Text="Save Changes", TextId = 0, ValId = 0x1005 } + MenuLines[5] = { Type = LT_MENU, Text="Discard Changes", TextId = 0, ValId = 0xFFF9 } + ctx_SelLine = 4 + end + lastGoodMenu = menuId + elseif (menuId==0x1001) then -- MODEL SETUP + local backId = 0xFFF9 -- No changes, just exit + local title = "Setup ("..MODEL.modelName..")" + if (menuDataChanged) then + backId = 0x1000 -- Go to Save menu + title = title.." *" + end + Menu = { MenuId = 0x1001, Text = title, PrevId = 0, NextId = 0, BackId = backId, TextId=0 } + MenuLines[0] = { Type = LT_MENU, Text = "Aircraft Setup", ValId = 0x1010,TextId=0 } + MenuLines[1] = { Type = LT_MENU, Text = "Wing & Tail Channels ", ValId = 0x1020, TextId=0 } + MenuLines[3] = { Type = LT_MENU, Text = "Gyro Channel Reverse", ValId = 0x1030, TextId=0 } + MenuLines[5] = { Type = LT_MENU, Text = "WARNING: Changing of Aircraft", ValId = 0x1001, TextId=0 } + MenuLines[6] = { Type = LT_MENU, Text = "deletes prev Ch/Port assgmt.", ValId = 0x1001, TextId=0 } + + ctx_SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1005) then + ST_SaveFileData() + menuDataChanged = false + + local msg1 = "Data saved to: " + local msg2 = " ../DSMLIB/"..MODEL.hashName + + Menu = { MenuId = 0x1005, Text = "Config Saved", PrevId = 0, NextId = 0, BackId = 0, TextId=0 } + MenuLines[2] = { Type = LT_MENU, Text=msg1, TextId = 0, ValId = 0x1005 } + MenuLines[3] = { Type = LT_MENU, Text=msg2, TextId = 0, ValId = 0x1005 } + MenuLines[6] = { Type = LT_MENU, Text="Complete", TextId = 0, ValId = 0xFFF9 } + ctx_SelLine = 6 + lastGoodMenu = menuId + elseif (menuId==0x1010) then + Menu = { MenuId = 0x1010, Text = "Aircraft", PrevId = 0, NextId = 0x1011, BackId = 0x1001, TextId=0 } + MenuLines[5] = { Type = LT_LIST_NC, Text="Aircraft Type", TextId = 0, ValId = MV_AIRCRAFT_TYPE, Min=15, Max=15, Def=15, Val=M_DB[MV_AIRCRAFT_TYPE] } + ctx_SelLine = 5 + lastGoodMenu = menuId + elseif (menuId==0x1011) then + Menu = { MenuId = 0x1011, Text = "Model Type: "..aircraft_type_text[currATyp], PrevId = 0, NextId = 0x1020, BackId = 0x1010, TextId=0 } + MenuLines[5] = { Type = LT_LIST_NC, Text="Wing Type", TextId = 0, ValId = MV_WING_TYPE, Min=20, Max=27, Def=20, Val=M_DB[MV_WING_TYPE] } + MenuLines[6] = { Type = LT_LIST_NC, Text="Tail Type", TextId = 0, ValId = MV_TAIL_TYPE, Min=30, Max=40, Def=30, Val=M_DB[MV_TAIL_TYPE] } + ctx_SelLine = 5 + lastGoodMenu = menuId + elseif (menuId==0x1020) then + ------ WING SETUP ------- + local thr = M_DB[MV_CH_THR] + local leftAil = M_DB[MV_CH_L_AIL] + local rightAil = M_DB[MV_CH_R_AIL] + local leftFlap = M_DB[MV_CH_L_FLP] + local rightFlap = M_DB[MV_CH_R_FLP] + + local thrText = "Thr" + local leftAilText = "Left Ail" + local rightAilText = "Right Ail" + local leftFlapText = "Left Flap" + local rightFlapText = "Right Flap" + + if (rightAil==nil) then leftAilText = "Aileron" end + if (rightFlap==nil) then leftFlapText = "Flap" end + + local title = aircraft_type_text[currATyp].." Wing:"..wing_type_text[currWTyp] + + Menu = { MenuId = 0x1020, Text = title, PrevId = 0, NextId = 0x1021, BackId = 0x1011, TextId=0 } + + MenuLines[0] = { Type = LT_LIST_NC, Text=thrText, TextId = 0, ValId = MV_CH_THR, Min=0, Max=10, Def=0, Val= thr } + + MenuLines[2] = { Type = LT_LIST_NC, Text=leftAilText, TextId = 0, ValId = MV_CH_L_AIL, Min=0, Max=9, Def=0, Val= leftAil } + + if (rightAil) then + MenuLines[3] = { Type = LT_LIST_NC, Text=rightAilText, TextId = 0, ValId = MV_CH_R_AIL, Min=0, Max=9, Def=0, Val= rightAil } + end + + if (leftFlap) then + MenuLines[4] = { Type = LT_LIST_NC, Text=leftFlapText, TextId = 0, ValId = MV_CH_L_FLP, Min=0, Max=9, Def=0, Val= leftFlap } + end + if (rightFlap) then + MenuLines[5] = { Type = LT_LIST_NC, Text=rightFlapText, TextId = 0, ValId = MV_CH_R_FLP, Min=0, Max=9, Def=0, Val= rightFlap } + end + + ctx_SelLine = 0 + lastGoodMenu = menuId + + elseif (menuId==0x1021) then + ------ TAIL SETUP ------- + local leftRud = M_DB[MV_CH_L_RUD] + local rightRud = M_DB[MV_CH_R_RUD] + local leftEle = M_DB[MV_CH_L_ELE] + local rightEle = M_DB[MV_CH_R_ELE] + + local leftRudText = "Left Rud" + local rightRudText = "Right Rud" + + local leftElvText = "Left Ele" + local rightElvText = "Right Ele" + + if (rightRud==nil) then leftRudText = "Rud" end + if (rightEle==nil) then leftElvText = "Ele" end + + local title = aircraft_type_text[currATyp].." Tail:"..tail_type_text[currTTyp] + + Menu = { MenuId = 0x1021, Text = title, PrevId = 0, NextId = 0x1001, BackId = 0x1020, TextId=0 } + if (leftRud) then + MenuLines[1] = { Type = LT_LIST_NC, Text=leftRudText, TextId = 0, ValId = MV_CH_L_RUD, Min=0, Max=9, Def=0, Val= leftRud} + end + + if (rightRud) then + MenuLines[2] = { Type = LT_LIST_NC, Text=rightRudText, TextId = 0, ValId = MV_CH_R_RUD, Min=0, Max=9, Def=0, Val=rightRud } + end + + if (leftEle) then + MenuLines[4] = { Type = LT_LIST_NC, Text=leftElvText, TextId = 0, ValId = MV_CH_L_ELE, Min=0, Max=9, Def=0, Val=leftEle } + end + + if (rightEle) then + MenuLines[5] = { Type = LT_LIST_NC, Text=rightElvText, TextId = 0, ValId = MV_CH_R_ELE, Min=0, Max=9, Def=0, Val=rightEle } + end + + ctx_SelLine = 1 + lastGoodMenu = menuId + + elseif (menuId==0x1030) then + Menu = { MenuId = 0x1030, Text = "Gyro Reverse (Port 1-5)", PrevId = 0, NextId = 0x1031, BackId = 0x1001, TextId=0 } + generateGyroReverse(menuId,P1,MV_P1_MODE) + lastGoodMenu = menuId + elseif (menuId==0x1031) then + Menu = { MenuId = 0x1031, Text = "Gyro Reverse (Port 6-10)", PrevId = 0x1030, NextId = 0, BackId = 0x1001, TextId=0 } + generateGyroReverse(menuId,P6,MV_P6_MODE) + lastGoodMenu = menuId + elseif (menuId==0xFFF9) then + ChangePhase(PH_EXIT_DONE) + return + else + Menu = { MenuId = 0x0002, Text = "NOT IMPLEMENTED", TextId = 0, PrevId = 0, NextId = 0, BackId = lastGoodMenu } + ctx_SelLine = -1 -- BACK BUTTON + end + + for i = 0, 6 do + if (MenuLines[i].Type > 0) then + MenuLinePostProcessing(MenuLines[i]) + end + end + gc() +end + +-- Inital List and Image Text for this menus +local function ST_Init_Text(rxId) + -- Channel Names use the Port Text Retrived from OTX/ETX + local p = 0 + + for i = 0, 9 do List_Text[i] = MODEL.PORT_TEXT[i] end + List_Text[10] = "--" + + -- Aircraft Type + List_Text[15+AT_PLANE] = "Airplane"; + + -- Wing Types + p = 20+WT_A1; List_Text[p] = "Single Ail"; --List_Text_Img[p] = "x.png|Single Aileron" + p = 20+WT_A2; List_Text[p] = "Dual Ail"; --List_Text_Img[p] = "x.png|Dual Aileron" + p = 20+WT_FLPR; List_Text[p] = "Flaperon"; --List_Text_Img[p] = "x.png|Flaperon" + p = 20+WT_A1_F1; List_Text[p] = "Ail + Flap"; --List_Text_Img[p] = "x.png|Aileron + Flap" + p = 20+WT_A2_F1; List_Text[p] = "Dual Ail + Flap"; --List_Text_Img[p] = "x.png|Dual Aileron + Flap" + p = 20+WT_A2_F2; List_Text[p] = "Dual Ail + Dual Flap"; --List_Text_Img[p] = "x.png|Dual Aileron + Dual Flap" + p = 20+WT_ELEVON_A; List_Text[p] = "Delta A"; --List_Text_Img[p] = "x.png|Delta/Elevon A" + p = 20+WT_ELEVON_B; List_Text[p] = "Delta B"; --List_Text_Img[p] = "x.png|Delta/Elevon B" + + -- Tail Types + p = 30+TT_R1; List_Text[p] = "Rudder Only"; --List_Text_Img[p] = "x.png|Rudder Only" + p = 30+TT_R1_E1; List_Text[p] = "Rud + Ele"; --List_Text_Img[p] = "x.png|Tail Normal" + p = 30+TT_R1_E2; List_Text[p] = "Rud + Dual Ele"; --List_Text_Img[p] = "x.png|Rud + Dual Ele" + p = 30+TT_R2_E1; List_Text[p] = "Dual Rud + Ele"; --List_Text_Img[p] = "x.png|Dual Rud + Ele" + p = 30+TT_R2_E2; List_Text[p] = "Dual Rud + Dual Ele"; --List_Text_Img[p] = "x.png|Dual Rud + Dual Elev" + p = 30+TT_VT_A; List_Text[p] = "V-Tail A"; --List_Text_Img[p] = "x.png|V-Tail A" + p = 30+TT_VT_B; List_Text[p] = "V-Tail B"; --List_Text_Img[p] = "x.png|V-Tail B" + p = 30+TT_TLRN_A; List_Text[p] = "Taileron A"; --List_Text_Img[p] = "x.png|Taileron A" + p = 30+TT_TLRN_B; List_Text[p] = "Taileron B"; --List_Text_Img[p] = "x.png|Taileron B" + p = 30+TT_TLRN_A_R2; List_Text[p] = "Taileron A + 2x Rud"; --List_Text_Img[p] = "x.png|Taileron A + Dual Rud" + p = 30+TT_TLRN_B_R2; List_Text[p] = "Taileron B + 2x Rud"; --List_Text_Img[p] = "x.png|Taileron B + Dual Rud" + + -- Servo Reverse + List_Text[45+MT_NORMAL] = "Normal" + List_Text[45+MT_REVERSE] = "Reverse" +end + +-- Initial Setup +local function ST_Init() + ST_Init_Text(0) + gc() + + -- Setup default Data if no data loaded + menuDataChanged = false + if (M_DB[MV_AIRCRAFT_TYPE]==nil) then + ST_Default_Data() + menuDataChanged = true + end + + currATyp = M_DB[MV_AIRCRAFT_TYPE] + currWTyp = M_DB[MV_WING_TYPE] + currTTyp = M_DB[MV_TAIL_TYPE] + + Phase = PH_RX_VER +end + +----- Line Type + +local function isSelectable(line) + 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_NC +end + +local function isEditing() + return ctx_EditLine ~= nil +end + +----------------------- +local function Get_Text(index) + local out = Text[index] or string.format("Unknown_%X", index) + return out +end + +local function Get_Text_Value(index) + local out = List_Text[index] or Get_Text(index) + return out +end + +function ChangePhase(newPhase) + Phase = newPhase +end + +local function Value_Add(dir) + local line = MenuLines[ctx_SelLine] + local inc = dir + + line.Val = line.Val + inc + + if line.Val > line.Max then + line.Val = line.Max + elseif line.Val < line.Min then + line.Val = line.Min + end +end +-------------- + +local function GotoMenu(menuId, lastSelectedLine) + Menu.MenuId = menuId + ctx_SelLine = lastSelectedLine + ChangePhase(PH_TITLE) +end + +local function DSM_HandleEvent(event) + if event == EVT_VIRTUAL_EXIT then + if Phase == PH_RX_VER then + Phase = PH_EXIT_DONE -- Exit program + else + if isEditing() then -- Editing a Line, need to restore original value + MenuLines[ctx_EditLine].Val = originalValue + event = EVT_VIRTUAL_ENTER + else + if (Menu.BackId > 0 ) then -- Back?? + ctx_SelLine = -1 --Back Button + event = EVT_VIRTUAL_ENTER + else + ChangePhase(PH_EXIT_REQ) + end + end + end + end -- Exit + + if Phase == PH_RX_VER then return end -- nothing else to do + + if event == EVT_VIRTUAL_NEXT then + if isEditing() then -- Editting? + Value_Add(1) + else + 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 + return + end + + if event == EVT_VIRTUAL_PREV then + if isEditing() 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 + return + end + + + if 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 isEditing() then + ctx_EditLine = nil -- Done Editting + ChangePhase(PH_VAL_EDIT_END) + else -- Start Editing + ctx_EditLine = ctx_SelLine + originalValue = MenuLines[ctx_SelLine].Val + ChangePhase(PH_VAL_EDITING) + end + end + end +end + +local function DSM_Send_Receive() + + if Phase == PH_RX_VER then -- request RX version + Phase = PH_TITLE + Menu.MenuId = 0x01001 + Refresh_Display = true + elseif Phase == PH_WAIT_CMD then + + elseif Phase == PH_TITLE then -- request menu title + ST_LoadMenu(Menu.MenuId) + if (Phase~=PH_EXIT_DONE) then + Phase = PH_WAIT_CMD + end + Refresh_Display = true + elseif Phase == PH_VAL_EDIT_END then -- send value + local line = MenuLines[ctx_SelLine] -- Updated Value of SELECTED line + + -- Update the menu data from the line + if (M_DB[line.ValId] ~= line.Val ) then + M_DB[line.ValId] = line.Val + print(string.format("MENU_DATA[%d/%s]=%d",line.ValId,line.Text, line.Val)) + menuDataChanged=true + end + + -- Did the Wing type change? + local wt = M_DB[MV_WING_TYPE] + if (currWTyp ~= wt) then + currWTyp = wt + ST_PlaneWingInit(currWTyp) + + -- DELTA has only RUDER + if (currWTyp==WT_ELEVON_A or currWTyp==WT_ELEVON_B) then + M_DB[MV_TAIL_TYPE] = TT_R1 + end + end + + --- Did the tail changed? + local tt = M_DB[MV_TAIL_TYPE] + if (currTTyp ~= tt) then + if (not tailTypeCompatible(currTTyp,tt)) then + ST_PlaneTailInit(tt) + end + currTTyp = tt + end + + Phase = PH_WAIT_CMD + elseif Phase == PH_EXIT_REQ then + Phase=PH_EXIT_DONE + 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 + gc() +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 function DSM_Display() + lcd.clear() + --Draw RX Menu + if Phase == PH_RX_VER then + return + end + + -- display Program version or RX version + local msg = "FProg "..VERSION + lcd.drawText(40, 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 = line.Text + + local text = nil + if line.Type ~= LT_MENU then -- list/value + if line.Val ~= nil then + if isListLine(line) then + local textId = line.Val + line.TextStart + 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(1, 20, imgDesc) + --end + 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 or "--", attrib + RIGHT) -- display value + --lcd.drawText(LCD_X_MAX, y, line.Format or "", TEXT_ATTR + RIGHT) -- display Format + attrib = TEXT_ATTR + end + + lcd.drawText(1, 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(1, LCD_Y_LOWER_BUTTONS, "Prev", ctx_SelLine == 8) + end +end + +------------------------------------------------------------------------------------------------------------ +-- Init +local function DSM_Init() + --LOG_open() + ST_Init() + gc() + + 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 + + Phase = PH_RX_VER +end + +-- Main + +local function DSM_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + end + + DSM_Display() + DSM_HandleEvent(event) + DSM_Send_Receive() + gc() + + if Phase == PH_EXIT_DONE then + LOG_close() + return 2 + else + return 0 + end +end + +--- +-- Load Model Config +gc() +local r = assert(loadScript(DSMLIB_PATH.."DsmMIN_P1.lua"), "Not-Found: DSMLIB/DsmMIN_P1.lua") + (MODEL,M_DB, LOG_write) +gc() +---- +return { init = DSM_Init, run = DSM_Run } diff --git a/SCRIPTS/TOOLS/DSMLIB/DsmFwPrgLib.lua b/SCRIPTS/TOOLS/DSMLIB/DsmFwPrgLib.lua new file mode 100644 index 0000000..e81759f --- /dev/null +++ b/SCRIPTS/TOOLS/DSMLIB/DsmFwPrgLib.lua @@ -0,0 +1,813 @@ +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX/EdgeTx # +-----# # +---- # 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. # +---- # # +---- ######################################################################### + +------------------------------------------------------------------------------ +-- This script library is a rewrite of the original DSM forward programming Lua +-- Script. The goal is to make it easier to understand, mantain, and to +-- separate the GUI from the DSM Forward programming engine/logic +-- in this way, GUIs can evolve independent. OpenTX Gui, EdgeTx GUI, Small Radios, etc. + +-- Code is based on the code/work by: Pascal Langer (Author of the Multi-Module) +-- Rewrite/Enhancements By: Francisco Arzu +-- +------------------------------------------------------------------------------ + --############################################################################### + -- 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 Log, menuLib, modelLib, DEBUG_ON = ... -- Get Debug_ON from parameters. -- 0=NO DEBUG, 1=HIGH LEVEL 2=MORE DETAILS +local LIB_VERSION = "0.55" +local MSG_FILE = "/SCRIPTS/TOOLS/DSMLIB/msg_fwdp_en.txt" + +local PHASE = menuLib.PHASE +local LINE_TYPE = menuLib.LINE_TYPE + +local CH_TYPE = modelLib.CH_TYPE +local CH_MIX_TYPE = modelLib.CH_MIX_TYPE + +local DISP_ATTR = menuLib.DISP_ATTR +local DSM_Context = menuLib.DSM_Context +local MODEL = modelLib.MODEL + +local MAX_MENU_LINES = menuLib.MAX_MENU_LINES +local BACK_BUTTON = menuLib.BACK_BUTTON +local NEXT_BUTTON = menuLib.NEXT_BUTTON +local PREV_BUTTON = menuLib.PREV_BUTTON + + +local SEND_TIMEOUT = 2000 / 10 -- Home many 10ms intervals to wait on sending data to tx to keep connection open (2s) +local TXInactivityTime = 0 -- Next time to do heartbeat after inactivity +local RXInactivityTime = 0 -- To check RX disconnection + + +local TxInfo_Type = 0 +local TxInfo_Step = 0 +local Change_Step = 0 + +local IS_EDGETX = false + +------------------------------------------------------------------------------------------------------------ + +local function multiBuffer2String() -- used for debug + local i + local rxAnswer = "RX:" + for i = 10, 25 do + rxAnswer = rxAnswer .. string.format(" %02X", multiBuffer(i)) + end + return rxAnswer +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 DSM_send(...) + local arg = { ... } + + for i = 1, #arg do + multiBuffer(3 + i, arg[i]) + end + multiBuffer(3, 0x70 + #arg) + + + if (DEBUG_ON > 1) then + local str = "" + for i = 1, #arg do + str = str .. string.format("%02X ", arg[i]) + end + LOG_write("DSM_SEND: [%s]\n", str) + end +end + + + +------------------------------------------------------------------------------------------------- +local function DSM_StartConnection() + if (DEBUG_ON) then Log.LOG_write("DSM_StartConnection()\n") end + + --Set protocol to talk to + multiBuffer( 0, string.byte('D') ) + --test if value has been written + if multiBuffer( 0 ) ~= string.byte('D') then + if (DEBUG_ON) then Log.LOG_write("Not Enouth memory\n") end + 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') ) + + return 0 +end + +local function DSM_ReleaseConnection() + if (DEBUG_ON) then Log.LOG_write("DSM_ReleaseConnection()\n") end + multiBuffer(0, 0) +end + +-------------------------------------------------------------------------------------------------------- +-- REEQUEST Messages to RX + +local function DSM_sendHeartbeat() + -- keep connection open + if (DEBUG_ON) then Log.LOG_write("SEND DSM_sendHeartbeat()\n") end + DSM_send(0x00, 0x04, 0x00, 0x00) +end + +local function DSM_getRxVerson() + local TX_CAP=0x14 -- Capabilities?? + local TX_MAX_CH = modelLib.TX_CHANNELS - 6 --number of channels after 6 (6Ch=0, 10ch=4, etc) + + if (DEBUG_ON) then Log.LOG_write("SEND DSM_getRxVersion(Ch:0x%X,Cap:0x%X)\n",TX_MAX_CH,TX_CAP) end + DSM_send(0x11, 0x06, TX_MAX_CH, TX_CAP, 0x00, 0x00) +end + +local function DSM_getMainMenu() + local TX_CAP=0x14 -- Capabilities?? + local TX_MAX_CH = modelLib.TX_CHANNELS - 6 --number of channels after 6 (6Ch=0, 10ch=4, etc) + + if (DEBUG_ON) then Log.LOG_write("SEND DSM_getMainMenu(Ch:0x%X,Cap:0x%X) -- TX_Channels=%d\n",TX_MAX_CH,TX_CAP,TX_MAX_CH+6) end + DSM_send(0x12, 0x06, TX_MAX_CH, TX_CAP, 0x00, 0x00) -- first menu only +end + +local function DSM_getMenu(menuId, latSelLine) + if (DEBUG_ON) then Log.LOG_write("SEND DSM_getMenu(MenuId=0x%X LastSelectedLine=%s)\n", menuId, latSelLine) end + DSM_send(0x16, 0x06, int16_MSB(menuId), int16_LSB(menuId), 0x00, latSelLine) +end + +local function DSM_getFirstMenuLine(menuId) + if (DEBUG_ON) then Log.LOG_write("SEND DSM_getFirstMenuLine(MenuId=0x%X)\n", menuId) end + DSM_send(0x13, 0x04, int16_MSB(menuId), int16_LSB(menuId)) -- line 0 +end + +local function DSM_getNextMenuLine(menuId, curLine) + if (DEBUG_ON) then Log.LOG_write("SEND DSM_getNextLine(MenuId=0x%X,LastLine=%s)\n", menuId, curLine) end + DSM_send(0x14, 0x06, int16_MSB(menuId), int16_LSB(menuId), 0x00, curLine) -- line X +end + +local function DSM_getNextMenuValue(menuId, valId, text) + if (DEBUG_ON) then Log.LOG_write("SEND DSM_getNextMenuValue(MenuId=0x%X, LastValueId=0x%X) Extra: Text=\"%s\"\n", menuId, valId, + text) + end + DSM_send(0x15, 0x06, int16_MSB(menuId), int16_LSB(menuId), int16_MSB(valId), int16_LSB(valId)) -- line X +end + +local function DSM_updateMenuValue(valId, val, text, line) + local value = sInt16ToDsm(val) + if (DEBUG_ON) then Log.LOG_write("SEND DSM_updateValue(ValueId=0x%X,val=%d) Extra: Text=\"%s\" Value=%s\n", valId, val, text, menuLib.lineValue2String(line)) end + DSM_send(0x18, 0x06, int16_MSB(valId), int16_LSB(valId), int16_MSB(value), int16_LSB(value)) -- send current value +end + +local function DSM_validateMenuValue(valId, text, line) + if (DEBUG_ON) then Log.LOG_write("SEND DSM_validateValue(ValueId=0x%X) Extra: Text=\"%s\" Value=%s\n", valId, text, menuLib.lineValue2String(line)) end + DSM_send(0x19, 0x04, int16_MSB(valId), int16_LSB(valId)) +end + +local function DSM_editingValue(lineNum, text, line) + if (DEBUG_ON) then Log.LOG_write("SEND DSM_editingValue(lineNo=0x%X) Extra: Text=\"%s\" Val=%s\n", lineNum, text, menuLib.lineValue2String(line)) end + DSM_send(0x1A, 0x04, int16_MSB(lineNum), int16_LSB(lineNum)) +end + +local function DSM_editingValueEnd(lineNum, text, line) + if (DEBUG_ON) then Log.LOG_write("SEND DSM_editingValueEnd(lineNo=0x%X) Extra: Text=\"%s\" Value=%s\n", lineNum, text, menuLib.lineValue2String(line)) end + DSM_send(0x1B, 0x04, int16_MSB(lineNum), int16_LSB(lineNum)) +end + +-- Send the functionality of the RX channel Port (channel) +local function DSM_sendTxChInfo_20(portNo) + local b1,b2 = MODEL.DSM_ChannelInfo[portNo][0] or 0, MODEL.DSM_ChannelInfo[portNo][1] or 0 + + if (DEBUG_ON) then Log.LOG_write("CALL DSM_TxChInfo(#%d DATA= %02X %02X %02X %02X) %s\n", portNo, + portNo, portNo, b1, b2, modelLib.channelType2String(b1,b2)) -- DATA part + end + DSM_send(0x20, 0x06, portNo, portNo, b1, b2) +end + +local function DSM_sendTxSubtrim_21(portNo) + local usage = MODEL.DSM_ChannelInfo[portNo][1] or 0 + local leftTravel = math.abs(math.floor(MODEL.modelOutputChannel[portNo].min/10)) + local rightTravel = math.abs(math.floor(MODEL.modelOutputChannel[portNo].max/10)) + local subTrim = math.floor(MODEL.modelOutputChannel[portNo].offset/10) + + -- Center at 100%: 142-1906 (0 8E 07 72) + local left = 142 + local right = 1906 + + if (bit32.band(usage,CH_TYPE.THR)>0) then + left = 0 + right = 2047 + end + + left = math.floor (left - (leftTravel -100) *8.8 + subTrim*2) + right = math.floor (right + (rightTravel -100) *8.8 + subTrim*2) + + if (left<0) then left=0 end + if (right>2027) then right=2047 end + + local b1,b2,b3,b4 = int16_MSB(left), int16_LSB(left), int16_MSB(right), int16_LSB(right) + + if (DEBUG_ON) then Log.LOG_write("CALL DSM_TxSubtrim(#%d DATA=%02X %02X %02X %02X) Range(%d - %d) ER L/R:(%d - %d)x8 ST:(%d)x2\n", portNo, + b1,b2,b3,b4, left, right, leftTravel-100, rightTravel-100, subTrim) -- DATA part + end + DSM_send(0x21, 0x06, b1,b2,b3,b4) -- Port is not send anywhere, since the previous 0x20 type message have it. +end + +local function DSM_sendTxServoTravel_23(portNo) + local leftTravel = math.abs(math.floor(MODEL.modelOutputChannel[portNo].min/10)) + local rightTravel = math.abs(math.floor(MODEL.modelOutputChannel[portNo].max/10)) + local debugInfo = string.format("Travel L/R (%d - %d)",leftTravel,rightTravel) + + if (DEBUG_ON) then Log.LOG_write("CALL DSM_TxServoTravel(#%d DATA= %02X %02X %02X %02X) %s\n", portNo, + 0x00, leftTravel, 0x00, rightTravel, debugInfo) -- DATA part + end + DSM_send(0x23, 0x06, 0x00, leftTravel, 0x00, rightTravel) +end + +local function DSM_sentTxInfo(menuId,portNo) + -- TxInfo_Type=0 : AR636B 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 (TxInfo_Step == 0) then + -- AR630 family: Both TxInfo_Type (ManinMenu=0x1, Other First Time Configuration = 0x1F) + DSM_sendTxChInfo_20(portNo) + + if (TxInfo_Type == 0x1F) then + TxInfo_Step = 1 + end + if (TxInfo_Type == 0x00) then + TxInfo_Step = 2 + end + elseif (TxInfo_Step == 1) then + DSM_sendTxServoTravel_23(portNo) + TxInfo_Step = 2 + elseif (TxInfo_Step == 2) then + DSM_sendTxSubtrim_21(portNo) + + if (TxInfo_Type == 0x00) then + TxInfo_Step = 5 -- End Step + else + TxInfo_Step = 3 + end + elseif (TxInfo_Step == 3) then + -- 24,6: 0 83 5A B5 + if (DEBUG_ON) then Log.LOG_write("CALL DSM_TxInfo24(#%d DATA=0x24 0x06 %02X %02X %02X %02X)\n", portNo, + 0x00, 0x83, 0x5A, 0xB5) -- DATA part + end + DSM_send(0x24, 0x06, 0x00, 0x83, 0x5A, 0xB5) -- Still Uknown + TxInfo_Step = 4 + + elseif (TxInfo_Step == 4) then + -- 24,6: 6 80 25 4B + if (DEBUG_ON) then Log.LOG_write("CALL DSM_TxInfo24(#%d DATA=0x24 0x06 %02X %02X %02X %02X)\n", portNo, + 0x06, 0x80, 0x25, 0x4B) -- DATA part + end + DSM_send(0x24, 0x06, 0x06, 0x80, 0x25, 0x4B) -- Still Uknown + TxInfo_Step = 5 + elseif (TxInfo_Step == 5) then + -- 22,4: 0 0 + if (DEBUG_ON) then Log.LOG_write("CALL DSM_TxInfo_End(#%d)\n", portNo) + end + DSM_send(0x22, 0x04, 0x00, 0x00) + TxInfo_Step = 0 + end + + if (TxInfo_Step > 0) then + DSM_Context.SendDataToRX = 1 -- keep Transmitig + end +end + +----------------------------------------------------------------------------------------------------------- + +local function DSM_sendRequest() + -- Send the proper Request message depending on the Phase + + local ctx = DSM_Context + if (DEBUG_ON) then Log.LOG_write("%3.3f %s: ", menuLib.getElapsedTime(), menuLib.phase2String(ctx.Phase)) end + + if ctx.Phase == PHASE.RX_VERSION then -- request RX version + DSM_getRxVerson() + + elseif ctx.Phase == PHASE.WAIT_CMD then -- keep connection open + DSM_sendHeartbeat() + + elseif ctx.Phase == PHASE.MENU_TITLE then -- request menu title + if ctx.Menu.MenuId == 0 then -- First time loading a menu ? + DSM_getMainMenu() + else + DSM_getMenu(ctx.Menu.MenuId, ctx.SelLine) + + if (ctx.Menu.MenuId == 0x0001) then -- Executed the Reset Menu?? + if (DEBUG_ON) then Log.LOG_write("RX Reset!!!\n") end + -- Start again retriving RX info + ctx.Menu.MenuId = 0 + ctx.isReset = true + ctx.Phase = PHASE.RX_VERSION + end + end + + elseif ctx.Phase == PHASE.MENU_REQ_TX_INFO then + DSM_sentTxInfo(ctx.Menu.MenuId, ctx.CurLine) + + elseif ctx.Phase == PHASE.MENU_LINES then -- request next menu lines + if ctx.CurLine == -1 then -- No previous menu line loaded ? + DSM_getFirstMenuLine(ctx.Menu.MenuId) + else + DSM_getNextMenuLine(ctx.Menu.MenuId, ctx.CurLine) + end + + elseif ctx.Phase == PHASE.MENU_VALUES then -- request menu values + local line = ctx.MenuLines[ctx.CurLine] + DSM_getNextMenuValue(ctx.Menu.MenuId, line.ValId, line.Text) + + elseif ctx.Phase == PHASE.VALUE_CHANGING then -- send value + local line = ctx.MenuLines[ctx.SelLine] -- Updated Value of SELECTED line + + if (Change_Step==0) then + DSM_updateMenuValue(line.ValId, line.Val, line.Text, line) + + if line.Type == menuLib.LINE_TYPE.LIST_MENU then -- Validation on every Step?? + Change_Step=1; ctx.SendDataToRX=1 -- Send inmediatly after + else + ctx.Phase = PHASE.VALUE_CHANGING_WAIT + end + else + DSM_validateMenuValue(line.ValId, line.Text, line) + Change_Step=0 + ctx.Phase = PHASE.VALUE_CHANGING_WAIT + end + + elseif ctx.Phase == PHASE.VALUE_CHANGING_WAIT then + local line = ctx.MenuLines[ctx.SelLine] + DSM_editingValue(line.lineNum, line.Text, line) + elseif ctx.Phase == PHASE.VALUE_CHANGE_END then -- send value + local line = ctx.MenuLines[ctx.SelLine] -- Update Value of SELECTED line + + if (Change_Step==0) then + DSM_updateMenuValue(line.ValId, line.Val, line.Text, line) + Change_Step=1; ctx.SendDataToRX=1 -- Send inmediatly after + elseif (Change_Step==1) then + DSM_validateMenuValue(line.ValId, line.Text, line) + Change_Step=2; ctx.SendDataToRX=1 -- Send inmediatly after + else + DSM_editingValueEnd(line.lineNum, line.Text, line) + Change_Step=0 + ctx.Phase = PHASE.WAIT_CMD + end + elseif ctx.Phase == PHASE.EXIT then + if (DEBUG_ON) then Log.LOG_write("CALL DSM_TX_Exit()\n") end + DSM_send(0x1F, 0x02, 0xFF, 0xFF) -- 0xAA + end +end + +----------------------------------------------------------------------------------------------------------- +-- Parsing Responses + +local function DSM_parseRxVersion() + --ex: 0x09 0x01 0x00 0x15 0x02 0x22 0x01 0x00 0x14 v2.22.1 00 14?? 8ch SAFE + -- 0x09 0x01 0x00 0x18 0x05 0x06 0x34 0x00 0x12 v5.6.52 00 12??? 6ch FC6250 + local rxId = multiBuffer(13) + DSM_Context.RX.Id = rxId + DSM_Context.RX.Name = menuLib.Get_RxName(rxId) + DSM_Context.RX.Version = multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16) + if (DEBUG_ON) then Log.LOG_write("RESPONSE Receiver=%s Version %s Cap:0x%02X\n", + DSM_Context.RX.Name, DSM_Context.RX.Version, multiBuffer(18)) end +end + +local function DSM_parseMenu() + --ex: 0x09 0x02 0x4F 0x10 0xA5 0x00 0x00 0x00 0x50 0x10 0x10 0x10 0x00 0x00 0x00 0x00 + -- MenuID TextID PrevID NextID BackID + local ctx = DSM_Context + local menu = ctx.Menu + menu.MenuId = Dsm_to_Int16(multiBuffer(12), multiBuffer(13)) + menu.TextId = Dsm_to_Int16(multiBuffer(14), multiBuffer(15)) + menu.Text = menuLib.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, MAX_MENU_LINES do -- clear menu + ctx.MenuLines[i] = { MenuId = 0, lineNum = 0, Type = 0, Text = "", TextId = 0, ValId = 0, Min=0, Max=0, Def=0, Val=nil } + end + ctx.CurLine = -1 + + menuLib.MenuPostProcessing(menu) + + if (DEBUG_ON) then Log.LOG_write("RESPONSE Menu: %s\n", menuLib.menu2String(menu)) end + return menu +end + + +local function DSM_parseMenuLine() + --ex: 0x09 0x03 0x00 0x10 0x00 0x1C 0xF9 0x00 0x10 0x10 0x00 0x00 0x00 0x00 0x03 0x00 + --ex: 0x09 0x03 0x61 0x10 0x00 0x6C 0x50 0x00 0x00 0x10 0x36 0x00 0x49 0x00 0x36 0x00 + --ex: 0x09 0x03 0x65 0x10 0x00 0x0C 0x51 0x00 0x00 0x10 0x00 0x00 0xF4 0x00 0x2E 0x00 + -- MenuLSB MenuMSB line Type TextID NextLSB NextMSB Val_Min Val_Max Val_Def + + local ctx = DSM_Context + local i = multiBuffer(14) + local type = multiBuffer(15) + local line = ctx.MenuLines[i] + + -- are we trying to override existing line + if (line.Type > 0 and type == 0) then + if (DEBUG_ON) then Log.LOG_write("RESPONSE MenuLine: ERROR. Trying to ZERO Override: %s\n", menuLib.menuLine2String(line)) end + return ctx.MenuLines[ctx.CurLine] + end + + 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 = nil -- Fill at Post processing + 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)) + + line.Val=nil + + menuLib.MenuLinePostProcessing(line) + + if (DEBUG_ON) then Log.LOG_write("RESPONSE MenuLine: %s\n", menuLib.menuLine2String(line)) end + + if (line.MenuId~=ctx.Menu.MenuId) then + -- Going Back too fast: Stil receiving lines from previous menu + ctx.Menu.MenuId = line.MenuId + Log.LOG_write("WARNING: Overriding current Menu from Line\n") + end + + return line +end + +local function DSM_parseMenuValue() + --ex: 0x09 0x04 0x53 0x10 0x00 0x10 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + --ex: 0x09 0x04 0x61 0x10 0x02 0x10 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + -- MenuLSB MenuMSB ValLSB ValMSB V_LSB V_MSB + + -- Identify the line and update the value + local ctx = DSM_Context + local menuId = Dsm_to_Int16(multiBuffer(12), multiBuffer(13)) + 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, MAX_MENU_LINES do -- Find the menu line for this value + local line = ctx.MenuLines[i] + if line ~= nil and line.Type ~= 0 then + if line.Type ~= LINE_TYPE.MENU and line.ValId == valId then -- identifier of ValueId stored in the line + line.Val = value + ctx.CurLine = i + updatedLine = line + break + end + end + end + + if (updatedLine == nil) then + if (DEBUG_ON) then Log.LOG_write("RESPONSE MenuValue: ERROR, Cant find Menu Line with MenuId=%X, ValID=%X to update\n", menuId, valId) end + else + if (DEBUG_ON) then Log.LOG_write("RESPONSE MenuValue: UPDATED: %s\n", menuLib.menuLine2String(updatedLine)) + end + end + return updatedLine ~= nil +end + +local function DSM_parseReqTxInfo() + local portNo = multiBuffer(12) + TxInfo_Type = multiBuffer(13) + if (DEBUG_ON) then Log.LOG_write("RESPONSE ReqTXChannelInfo(#%d %s InfoType=0x%0X)\n", + portNo, MODEL.PORT_TEXT[portNo], TxInfo_Type) end + + TxInfo_Step = 0 + + return portNo +end + +------------------------------------------------------------------------------------------------------------ +local function DSM_processResponse() + local ctx = DSM_Context + local cmd = multiBuffer(11) -- Response Command + + if (DEBUG_ON > 1) then Log.LOG_write("%s: RESPONSE %s \n", menuLib.phase2String(ctx.Phase), multiBuffer2String()) end + if (DEBUG_ON and cmd > 0x00) then Log.LOG_write("%3.3f %s: ", menuLib.getElapsedTime(), menuLib.phase2String(ctx.Phase)) end + + if cmd == 0x01 then -- read version + DSM_parseRxVersion() + --Lib.Init_Text(DSM_Context.RX.Id) + ctx.isReset = false -- no longer resetting + ctx.Phase = PHASE.MENU_TITLE + ctx.Menu.MenuId = 0 + + elseif cmd == 0x02 then -- read menu title + local menu = DSM_parseMenu() + + -- Update Selected Line navigation + if menu.NextId ~= 0 then + ctx.SelLine = NEXT_BUTTON -- highlight Next + else + ctx.SelLine = BACK_BUTTON -- highlight Back + end + + if (ctx.Menu.MenuId == 0x0001) then -- Still in RESETTING MENU??? + -- Star from Start + if (DEBUG_ON) then Log.LOG_write("RX Reset: Still not done, restart again!!!\n") end + ctx.Menu.MenuId = 0 + ctx.Phase = PHASE.RX_VERSION + else + ctx.Phase = PHASE.MENU_LINES + end + + + elseif cmd == 0x03 then -- menu lines + local line = DSM_parseMenuLine() + + -- Update Selected line navigation + if (ctx.SelLine == BACK_BUTTON or ctx.SelLine == NEXT_BUTTON or ctx.SelLine == PREV_BUTTON) + and menuLib.isSelectableLine(line) then -- Auto select the current line + ctx.SelLine = line.lineNum + end + + ctx.Phase = PHASE.MENU_LINES + + elseif cmd == 0x04 then -- read menu values + if DSM_parseMenuValue() then + ctx.Phase = PHASE.MENU_VALUES + else + ctx.Phase = PHASE.WAIT_CMD + end + + elseif cmd == 0x05 then -- Request TX Info + local portNo = DSM_parseReqTxInfo() + ctx.CurLine = portNo + ctx.Phase = PHASE.MENU_REQ_TX_INFO + + elseif cmd == 0xA7 then -- answer to EXIT command + if (DEBUG_ON) then Log.LOG_write("RESPONSE RX Exit\n") end + if (ctx.Phase==PHASE.EXIT) then -- Expected RX Exit + ctx.Phase = PHASE.EXIT_DONE + else -- UnExpected RX Exit + DSM_ReleaseConnection() + error("RX Connection Drop") + end + + elseif cmd == 0x00 then -- NULL response (or RX heartbeat) + -- 09 00 01 00 00 00 00 00 00 00 00 00 00 00 00 + -- 09 00 7E 00 20 9E 28 00 20 9E 28 00 20 8D 7E : After TX Heartbeat one of this (FC6250) + -- 09 00 18 00 20 08 00 00 00 08 00 00 00 98 AE AR8360T + if (DEBUG_ON) then Log.LOG_write("%3.3f %s: RESPONSE RX Heartbeat --Context: 0x%02X\n", + menuLib.getElapsedTime(), menuLib.phase2String(ctx.Phase), multiBuffer(12)) end + else + if (DEBUG_ON) then Log.LOG_write("RESPONSE Unknown Command (0x%X) DATA=%s\n", cmd, multiBuffer2String()) end + end + + return cmd +end + +------------------------------------------------------------------------------------------------------------ +local function DSM_Send_Receive() + local ctx = DSM_Context + + -- Receive part: Process incoming messages if there is nothing to send + if ctx.SendDataToRX == 0 and multiBuffer(10) == 0x09 then + local cmd = DSM_processResponse() + + multiBuffer(10, 0x00) -- Clear Response Buffer to know that we are done with the response + RXInactivityTime = getTime() + SEND_TIMEOUT*4 -- Reset RX Inactivity timeout + + if (cmd > 0x00) then -- RX HeartBeat ?? + -- Only change to SEND mode if we received a valid response (Ignore heartbeat) + ctx.SendDataToRX = 1 + ctx.Refresh_Display = true + end + else + if (getTime() > RXInactivityTime and ctx.Phase ~= PHASE.RX_VERSION and ctx.Phase ~= PHASE.EXIT_DONE) then + if (ctx.isEditing()) then -- If Editing, Extend Timeout + RXInactivityTime = getTime() + SEND_TIMEOUT*4 + else + if (DEBUG_ON) then Log.LOG_write("%3.3f %s: RX INACTIVITY TIMEOUT\n", menuLib.getElapsedTime(), menuLib.phase2String(ctx.Phase)) end + DSM_ReleaseConnection() + error("RX Disconnected") + end + end + end + + -----TX Part -------------------------------------- + if ctx.SendDataToRX == 1 then -- Need to send a request + ctx.SendDataToRX = 0 + DSM_sendRequest() + TXInactivityTime = getTime() + SEND_TIMEOUT -- Reset Inactivity timeout + else + -- Check if enouth time has passed from last transmit/receive activity + if getTime() > TXInactivityTime then + ctx.SendDataToRX = 1 -- Switch to Send mode to send heartbeat + ctx.Refresh_Display = true + + -- Only change to WAIT_CMD if we are NOT wating for RX version + if ctx.Phase ~= PHASE.RX_VERSION then + -- Phase = If IsEditing then VALUE_CHANGING_WAIT else WAIT_CMD + ctx.Phase = (ctx.isEditing() and PHASE.VALUE_CHANGING_WAIT) or PHASE.WAIT_CMD + end + end + end +end + +-- Init +local function DSM_Init(toolName) + local dateTime = getDateTime() + local dateStr = dateTime.year.."-"..dateTime.mon.."-"..dateTime.day.." "..dateTime.hour..":"..dateTime.min + + local ver, radio, maj, minor, rev, osname = getVersion() + + if (osname==nil) then osname = "OpenTX" end -- OTX 2.3.14 and below returns nil + + IS_EDGETX = string.sub(osname,1,1) == 'E' + + if (DEBUG_ON) then + Log.LOG_write("---------------DSM New Session %s ----------------\n", toolName, dateStr) + Log.LOG_write("Radio Info: %s\n", radio .. " " .. (osname or "OpenTx") .. " " .. ver) + Log.LOG_write("Date : %s\n", dateStr) + Log.LOG_write("DsmLib Version : %s\n", LIB_VERSION) + end +end + +local function DSM_Init_Text_Exceptions() + -- Apply special restrictions to some Values + + local function getTxChText(ch) + return " ("..(MODEL.TX_CH_TEXT[ch] or "--")..")" + end + + local List_Values = menuLib.List_Values + local List_Text = menuLib.List_Text + local Text = menuLib.Text + + Log.LOG_write("Initializing TEXT Exception\n") + + -- Channel selection for SAFE MODE and GAINS on FC6250HX + --List_Text[0x000C] = "Inhibit?" --? + for i = 0, 7 do + List_Text[0x000D + i] = "Ch"..(i+5) ..getTxChText(i+4) + end -- Aux channels (Ch5 and Greater) + + -- Servo Output values.. + local servoOutputValues = {0x0003,0x002D,0x002E,0x002F} --Inh (GAP), 5.5ms, 11ms, 22ms. Fixing L_m1 with 0..244 range! + --List_Text[0x002D] = "5.5ms" + --[0x002E] = "11ms" + --List_Text[0x002F] = "22ms" + + -- Gain Values + local gainValues = {0x0032,0x0033,0x0034} -- 1X, 2X, 4X -- Fixing L_m1 with 0..244 range! + --List_Text[0x0032] = "1 X" + --[0x0033] = "2 X" + -- List_Text[0x0034] = "4 X" + + -- List of Channels for Safe, Gains, Panic, except FC6250HX that uses other range (0x00C..0x015) + -- the valid range Starts with GEAR if enabled (Thr,Ail,Ele,Rud are not valid, the RX reject them ) + -- Valid Values: Inhibit? (GAP), Gear,Aux1..Aux7,X-Plus-1..XPlus-8 + local channelValues = {0x0035,0x0036,0x0037,0x0038,0x0039,0x003A,0x003B,0x003C,0x003D,0x003E,0x003F, + 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049} + + --List_Text[0x0035] = "Inhibit?" + for i = 0, 11 do + List_Text[0x0036 + i] = "Ch"..(i+1) .. getTxChText(i) + end -- Channels on AR637T + + for i = 1, 8 do -- 41..49 + List_Text[0x0041 + i] = "Ch"..(i+12) + end + + -- Flight mode channel selection + --Text[0x0078] = "FM Channel" + List_Values[0x0078]=channelValues + + -- Gain channel selection + --Text[0x0089] = "Gain Channel" + List_Values[0x0089]=channelValues + + -- Gain Sensitivity selection + --Text[0x008A] = "Gain Sensitivity/r"; + List_Values[0x008A]=gainValues -- Right Alight, (L_M1 was wide open range 0->244) + + -- Safe mode options, Ihnibit + this values + local safeModeOptions = {0x0003,0x00B0,0x00B1} -- inh (gap), "Self-Level/Angle Dem, Envelope + --List_Text[0x00B0] = "Self-Level/Angle Dem" + --List_Text[0x00B1] = "Envelope" + + --Text[0x00D2] = "Panic Channel" + List_Values[0x00D2]=channelValues + + --Inh, Self-Level/Angle Dem, Envelope -- (L_M was wide open range 0->244) + --Text[0x01F8] = "Safe Mode"; + List_Values[0x01F8]=safeModeOptions +end + +-- Initial Setup +local function FP_Init(toolname) + DSM_Context.Phase = PHASE.INIT + + DSM_Init(toolname) + menuLib.clearAllText() +end + +local initStep=0 +local FileState = {} + +local function Message_Init() + lcd.clear() + if (initStep == 0) then + if (IS_EDGETX) then + -- Load all messages at once + menuLib.LoadTextFromFile(MSG_FILE,13) + initStep=1 + + else + -- load messages incrementally to avoid "CPU Limit" + lcd.drawText(30, 50, "Loading Msg file: "..(FileState.lineNo or 0)) + if (menuLib.INC_LoadTextFromFile(MSG_FILE, FileState)==1) then + initStep=1 + end + end + elseif (initStep == 1) then + DSM_Init_Text_Exceptions() + + DSM_Context.Phase = PHASE.RX_VERSION + DSM_StartConnection() + end +end + +local function FP_Run() + if (DSM_Context.Phase==PHASE.INIT) then + Message_Init() + return 0 + end + + return DSM_Send_Receive() +end + +local function FP_Done() + local ctx = menuLib.DSM_Context + ctx.Phase = PHASE.EXIT_DONE + DSM_ReleaseConnection() +end + +return { init=FP_Init, run=FP_Run, done=FP_Done } diff --git a/SCRIPTS/TOOLS/DSMLIB/DsmLogLib.lua b/SCRIPTS/TOOLS/DSMLIB/DsmLogLib.lua new file mode 100644 index 0000000..4d13be2 --- /dev/null +++ b/SCRIPTS/TOOLS/DSMLIB/DsmLogLib.lua @@ -0,0 +1,33 @@ +local LogLib = { } + +local LOG_FILE = "/LOGS/dsm_log.txt" +local logFile = nil +local logCount=0 + +function LogLib.LOG_open() + logFile = io.open(LOG_FILE, "w") -- Truncate Log File +end + +function LogLib.LOG_write(...) + if (logFile==nil) then LogLib.LOG_open() end + local str = string.format(...) + + if (str==nil) then return end + + io.write(logFile, str) + + str = string.gsub(str,"\n"," ") -- Elimitate return from line, since print will do it + print(str) + + if (logCount > 10) then -- Close an re-open the file + io.close(logFile) + logFile = io.open(LOG_FILE, "a") + logCount =0 + end +end + +function LogLib.LOG_close() + if (logFile~=nil) then io.close(logFile) end +end + +return LogLib diff --git a/SCRIPTS/TOOLS/DSMLIB/DsmMIN_P1.lua b/SCRIPTS/TOOLS/DSMLIB/DsmMIN_P1.lua new file mode 100644 index 0000000..b228bd5 --- /dev/null +++ b/SCRIPTS/TOOLS/DSMLIB/DsmMIN_P1.lua @@ -0,0 +1,167 @@ +local MODEL, M_DATA, LOG_write = ... + +--[[ +local MODEL = { + modelName = "", -- The name of the model comming from OTX/ETX + hashName = "", + modelOutputChannel = {}, -- Output information from OTX/ETX + + TX_CH_TEXT= { [0]=""}, + PORT_TEXT = { [0]=""}, + + DSM_ChannelInfo = {} -- Data Created by DSM Configuration Script + } + + -- MENU DATA Management +local M_DATA = {} -- Store the variables used in the Menus. + +--]] + +local DATA_PATH = "/MODELS/DSMDATA" +local TX_CHANNELS = 12 + +local MV_DATA_END = 1040 + + + local function hashName(mName) + local c=10000; + + local prefix = string.gsub(mName,"%.","_") -- Change any "." to "_" + prefix = string.gsub(prefix,"% ","_") -- Change any space to "_" + prefix = string.sub(prefix,1,5) -- Take the first 5 characters + + -- Simple Hash of the Model Name adding each character + for i = 1, #mName do + local ch = string.byte(mName,i,i) + c=c+ch + end + + return (prefix .. c) -- Return Prefix + Hash + end + + -- Load Menu Data from a file + local function ST_LoadFileData() + local fname = hashName(MODEL.modelName)..".txt" + MODEL.hashName = fname + + -- Clear Menu Data + for i = 0, MV_DATA_END do + M_DATA[i]=nil + end + + print("Loading File:"..fname) + + local dataFile = io.open(DATA_PATH .. "/".. fname, "r") -- read File + -- cannot read file??? + if (dataFile==nil) then return 0 end + + local line = io.read(dataFile, 5000) + io.close(dataFile) + + if #line == 0 then return 0 end -- No data?? + + -- Process the input, each line is "Var_Id : Value" format + -- Store it into MANU_DATA + local i=0 + for k, v in string.gmatch(line, "(%d+):(%d+)") do + M_DATA[k+0]=v+0 -- do aritmentic to convert string to number + i=i+1 + end + + -- Return 0 if no lines processed, 1 otherwise + if (i > 0) then return 1 else return 0 end + end + + local function getModuleChannelOrder(num) + --Determine fist 4 channels order + local ch_n={} + local st_n = {[0]= "R", "E", "T", "A" } + local c_ord=num -- ch order + if (c_ord == -1) then + ch_n[0] = st_n[3] + ch_n[1] = st_n[1] + ch_n[2] = st_n[2] + ch_n[3] = st_n[0] + else + ch_n[bit32.band(c_ord,3)] = st_n[3] + c_ord = math.floor(c_ord/4) + ch_n[bit32.band(c_ord,3)] = st_n[1] + c_ord = math.floor(c_ord/4) + ch_n[bit32.band(c_ord,3)] = st_n[2] + c_ord = math.floor(c_ord/4) + ch_n[bit32.band(c_ord,3)] = st_n[0] + end + + local s = "" + for i=0,3 do + s=s..ch_n[i] + end + return s + end + + local function ReadTxModelData() + local TRANSLATE_AETR_TO_TAER=false + local table = model.getInfo() -- Get the model name + MODEL.modelName = table.name + + local module = model.getModule(0) -- Internal + if (module==nil or module.Type~=6) then module = model.getModule(1) end -- External + if (module~=nil) then + if (module.Type==6 ) then -- MULTI-MODULE + local chOrder = module.channelsOrder + local s = getModuleChannelOrder(chOrder) + LOG_write("MultiChannel Ch Order: [%s] %s\n",chOrder,s) + + if (s=="AETR") then TRANSLATE_AETR_TO_TAER=true + else TRANSLATE_AETR_TO_TAER=false + end + end + end + + -- Read Ch1 to Ch10 + local i= 0 + for i = 0, TX_CHANNELS-1 do + local ch = model.getOutput(i) -- Zero base + if (ch~=nil) then + MODEL.modelOutputChannel[i] = ch + if (string.len(ch.name)==0) then + ch.formatCh = string.format("TX:Ch%i",i+1) + else + ch.formatCh = string.format("TX:Ch%i/%s",i+1,ch.name or "--") + end + end + end + + -- Translate AETR to TAER + + if (TRANSLATE_AETR_TO_TAER) then + LOG_write("Applying AETR -> TAER translation\n") + local ail = MODEL.modelOutputChannel[0] + local elv = MODEL.modelOutputChannel[1] + local thr = MODEL.modelOutputChannel[2] + + MODEL.modelOutputChannel[0] = thr + MODEL.modelOutputChannel[1] = ail + MODEL.modelOutputChannel[2] = elv + end + + -- Create the Port Text to be used + LOG_write("Ports/Channels:\n") + for i = 0, TX_CHANNELS-1 do + local ch = MODEL.modelOutputChannel[i] + if (ch~=nil) then + MODEL.TX_CH_TEXT[i] = ch.formatCh + MODEL.PORT_TEXT[i] = string.format("P%i (%s) ",i+1,MODEL.TX_CH_TEXT[i]) + LOG_write("Port%d %s [%d,%d] Rev=%d, Off=%d, ppmC=%d, syn=%d\n",i+1,MODEL.TX_CH_TEXT[i],math.floor(ch.min/10),math.floor(ch.max/10), ch.revert, ch.offset, ch.ppmCenter, ch.symetrical) + end + end + end + + -- Main Program + + LOG_write("Reading Model Info\n") + ReadTxModelData() + local r = ST_LoadFileData() + return r + + diff --git a/SCRIPTS/TOOLS/DSMLIB/DsmMIN_P2.lua b/SCRIPTS/TOOLS/DSMLIB/DsmMIN_P2.lua new file mode 100644 index 0000000..6897b31 --- /dev/null +++ b/SCRIPTS/TOOLS/DSMLIB/DsmMIN_P2.lua @@ -0,0 +1,282 @@ +local MODEL, M_DATA, LOG_write = ... + +--[[ +local MODEL = { + modelName = "", -- The name of the model comming from OTX/ETX + hashName = "", + modelOutputChannel = {}, -- Output information from OTX/ETX + + TX_CH_TEXT= { [0]=""}, + PORT_TEXT = { [0]=""}, + + DSM_ChannelInfo = {} -- Data Created by DSM Configuration Script + } + + -- MENU DATA Management +local M_DATA = {} -- Store the variables used in the Menus. + +--]] + + +local TX_CHANNELS = 12 + +local AT_PLANE = 0 + +local aircraft_type_text = {[0]="Plane","Heli","Glider","Drone"} + +--[[ +local WT_A1 = 0 +local WT_A2 = 1 +local WT_FLPR = 2 +local WT_A1_F1 = 3 +local WT_A2_F1 = 4 +local WT_A2_F2 = 5 +--]] +local WT_ELEVON_A = 6 +local WT_ELEVON_B = 7 + +local wing_type_text = {[0]="Normal","Dual Ail","Flapperon", "Ail + Flp","Dual Ail + Flp","Dual Ail/Flp","Elevon A","Elevon B"} + +--[[ +local TT_R1 = 0 +local TT_R1_E1 = 1 +local TT_R1_E2 = 2 +local TT_R2_E1 = 3 +local TT_R2_E2 = 4 +--]] + +local TT_VT_A = 5 +local TT_VT_B = 6 +local TT_TLRN_A = 7 +local TT_TLRN_B = 8 +local TT_TLRN_A_R2 = 9 +local TT_TLRN_B_R2 = 10 + +local tail_type_text = {[0]="Rud Only","Normal","Rud + Dual Ele","Dual Rud + Elv","Dual Rud/Ele", + "VTail A","VTail B","Taileron A","Taileron B","Taileron A + Dual Rud","Taileron B + Dual Rud"} + + +local MV_AIRCRAFT_TYPE = 1001 +local MV_WING_TYPE = 1002 +local MV_TAIL_TYPE = 1003 + +local MV_CH_BASE = 1010 +local MV_CH_THR = 1010 +local MV_CH_L_AIL = 1011 +local MV_CH_R_AIL = 1012 +local MV_CH_L_FLP = 1013 +local MV_CH_R_FLP = 1014 + +local MV_CH_L_RUD = 1015 +local MV_CH_R_RUD = 1016 +local MV_CH_L_ELE = 1017 +local MV_CH_R_ELE = 1018 + +local MV_PORT_BASE = 1020 + +local MV_DATA_END = 1040 + +--Channel Types -- +local CT_NONE = 0x00 +local CT_AIL = 0x01 +local CT_ELE = 0x02 +local CT_RUD = 0x04 +local CT_REVERSE = 0x20 +local CT_THR = 0x40 +local CT_SLAVE = 0x80 + +-- Seems like Reverse Mix is complement of the 3 bits +local CMT_NORM = 0x00 -- 0000 +local CMT_AIL = 0x10 -- 0001 Taileron +local CMT_ELE = 0x20 -- 0010 For VTIAL and Delta-ELEVON +local CMT_RUD = 0x30 -- 0011 For VTIAL +local CMT_RUD_REV = 0x40 -- 0100 For VTIAL +local CMT_ELE_REV = 0x50 -- 0101 For VTIAL and Delta-ELEVON A +local CMT_AIL_REV = 0x60 -- 0110 Taileron +local CMT_NORM_REV = 0x70 -- 0111 + +local MT_NORMAL = 0 +local MT_REVERSE = 1 + +local function channelType2String(byte1, byte2) + local s = "" + + if (byte2==0) then return s end; + + if (bit32.band(byte2,CT_AIL)>0) then s=s.."Ail" end + if (bit32.band(byte2,CT_ELE)>0) then s=s.."Ele" end + if (bit32.band(byte2,CT_RUD)>0) then s=s.."Rud" end + if (bit32.band(byte2,CT_THR)>0) then s=s.."Thr" end + + if (bit32.band(byte2,CT_REVERSE)>0) then s=s.."-" end + + if (bit32.band(byte2,CT_SLAVE)>0) then s=s.." Slv" end + + if (byte1==CMT_NORM) then s=s.." " + elseif (byte1==CMT_AIL) then s=s.." M_Ail" + elseif (byte1==CMT_ELE) then s=s.." M_Ele" + elseif (byte1==CMT_RUD) then s=s.." M_Rud" + elseif (byte1==CMT_RUD_REV) then s=s.." M_Rud-" + elseif (byte1==CMT_ELE_REV) then s=s.." M_Ele-" + elseif (byte1==CMT_AIL_REV) then s=s.." M_Ail-" + elseif (byte1==CMT_NORM_REV) then s=s.." M-" + end + + return s; + end + + -- This Creates the Servo Settings that will be used to pass to +-- Forward programming +local function CreateDSMPortChannelInfo() + local function ApplyWingMixA(b2) + -- ELEVON + if (b2==CT_AIL+CT_ELE) then return CMT_ELE end; -- 0x03 + if (b2==CT_AIL+CT_ELE+CT_SLAVE) then return CMT_NORM end; -- 0x83 + end + + local function ApplyWingMixB(b2) + -- ELEVON + if (b2==CT_AIL+CT_ELE) then return CMT_NORM end; -- 0x03 + if (b2==CT_AIL+CT_ELE+CT_SLAVE) then return CMT_ELE end; -- 0x83 + end + + local function ApplyTailMixA(b2) + -- VTAIL + -- Default normal/reverse behaviour + if (b2==CT_RUD+CT_ELE) then return CMT_NORM end; -- 0x06 + if (b2==CT_RUD+CT_ELE+CT_SLAVE) then return CMT_ELE end; -- 0x86 + + --TAILERON + -- Default normal/reverse behaviour + if (b2==CT_AIL+CT_ELE) then return CMT_NORM end; -- 0x03 + if (b2==CT_AIL+CT_ELE+CT_SLAVE) then return CMT_AIL end; -- 0x83 + end + + local function ApplyTailMixB(b2) + -- VTAIL + -- Default normal/reverse behaviour + if (b2==CT_RUD+CT_ELE) then return CMT_NORM end; -- 0x06 + if (b2==CT_RUD+CT_ELE+CT_SLAVE) then return CMT_RUD end; -- 0x86 + + --TAILERON + if (b2==CT_AIL+CT_ELE) then return CMT_AIL end; -- 0x03 + if (b2==CT_AIL+CT_ELE+CT_SLAVE) then return CMT_NORM end; -- 0x83 + end + + local function reverseMix(b) + if (b==CMT_NORM) then return CMT_NORM_REV end; + if (b==CMT_AIL) then return CMT_AIL_REV end; + if (b==CMT_ELE) then return CMT_ELE_REV end; + if (b==CMT_RUD) then return CMT_RUD_REV end; + return b + end + + local DSM_Ch = MODEL.DSM_ChannelInfo + + for i=0, TX_CHANNELS-1 do + DSM_Ch[i] = {[0]= CMT_NORM, CT_NONE, nil} -- Initialize with no special function + end + + --local aircraftType = M_DATA[MV_AIRCRAFT_TYPE] + local wingType = M_DATA[MV_WING_TYPE] + local tailType = M_DATA[MV_TAIL_TYPE] + + local thrCh = M_DATA[MV_CH_THR] + local lAilCh = M_DATA[MV_CH_L_AIL] + local rAilCh = M_DATA[MV_CH_R_AIL] + + local lElevCh = M_DATA[MV_CH_L_ELE] + local rElevCh = M_DATA[MV_CH_R_ELE] + + local lRudCh = M_DATA[MV_CH_L_RUD] + local rRudCh = M_DATA[MV_CH_R_RUD] + + -- Channels in menu vars are Zero base, Channel info is 1 based + + -- THR + if (thrCh~=nil and thrCh < 10) then DSM_Ch[thrCh][1]= CT_THR end + + -- AIL (Left and Right) + if (lAilCh~=nil) then DSM_Ch[lAilCh][1] = CT_AIL end + if (rAilCh~=nil) then DSM_Ch[rAilCh][1] = CT_AIL+CT_SLAVE end + -- ELE (Left and Right) + if (lElevCh~=nil) then DSM_Ch[lElevCh][1] = CT_ELE end + if (rElevCh~=nil) then DSM_Ch[rElevCh][1] = CT_ELE+CT_SLAVE end + -- RUD (Left and Right) + if (lRudCh~=nil) then DSM_Ch[lRudCh][1] = CT_RUD end + if (rRudCh~=nil) then DSM_Ch[rRudCh][1] = CT_RUD+CT_SLAVE end + + -- VTAIL: RUD + ELE + if (tailType==TT_VT_A) then + DSM_Ch[lElevCh][1] = CT_RUD+CT_ELE + DSM_Ch[rElevCh][1] = CT_RUD+CT_ELE+CT_SLAVE + elseif (tailType==TT_VT_B) then + DSM_Ch[lElevCh][1] = CT_RUD+CT_ELE+CT_SLAVE + DSM_Ch[rElevCh][1] = CT_RUD+CT_ELE + end + + -- TAILERRON: 2-ELE + AIL + if (tailType==TT_TLRN_A or tailType==TT_TLRN_A_R2) then + DSM_Ch[lElevCh][1] = CT_AIL+CT_ELE + DSM_Ch[rElevCh][1] = CT_AIL+CT_ELE+CT_SLAVE + elseif (tailType==TT_TLRN_B or tailType==TT_TLRN_B_R2) then + DSM_Ch[lElevCh][1] = CT_AIL+CT_ELE+CT_SLAVE + DSM_Ch[rElevCh][1] = CT_AIL+CT_ELE + end + + ---- ELEVON : AIL + ELE + if (wingType==WT_ELEVON_A) then + DSM_Ch[lAilCh][1] = CT_AIL+CT_ELE + DSM_Ch[rAilCh][1] = CT_AIL+CT_ELE+CT_SLAVE + elseif (wingType==WT_ELEVON_B) then + DSM_Ch[lAilCh][1] = CT_AIL+CT_ELE+CT_SLAVE + DSM_Ch[rAilCh][1] = CT_AIL+CT_ELE + end + + ------MIXES --------- + + -- TAIL Mixes (Elevator and VTail) + if (tailType==TT_VT_A or tailType==TT_TLRN_A or tailType==TT_TLRN_A_R2) then + DSM_Ch[lElevCh][0] = ApplyTailMixA(DSM_Ch[lElevCh][1]) + DSM_Ch[rElevCh][0] = ApplyTailMixA(DSM_Ch[rElevCh][1]) + elseif (tailType==TT_VT_B or tailType==TT_TLRN_B or tailType==TT_TLRN_B_R2) then + DSM_Ch[lElevCh][0] = ApplyTailMixB(DSM_Ch[lElevCh][1]) + DSM_Ch[rElevCh][0] = ApplyTailMixB(DSM_Ch[rElevCh][1]) + end + + ---- ELEVON : AIL + ELE + if (wingType==WT_ELEVON_A) then + DSM_Ch[lAilCh][0] = ApplyWingMixA(DSM_Ch[lAilCh][1]) + DSM_Ch[rAilCh][0] = ApplyWingMixA(DSM_Ch[rAilCh][1]) + elseif (wingType==WT_ELEVON_B) then + DSM_Ch[lAilCh][0] = ApplyWingMixB(DSM_Ch[lAilCh][1]) + DSM_Ch[rAilCh][0] = ApplyWingMixB(DSM_Ch[rAilCh][1]) + end + + -- Apply Gyro Reverse as needed for each channel as long as it is used + for i=0, TX_CHANNELS-1 do + if (M_DATA[MV_PORT_BASE+i]==MT_REVERSE and DSM_Ch[i][1]>0) then + DSM_Ch[i][0]=reverseMix(DSM_Ch[i][0]) + DSM_Ch[i][1]=DSM_Ch[i][1]+CT_REVERSE + end + end + + -- Show how it looks + for i=0, 9 do + local b1,b2 = DSM_Ch[i][0], DSM_Ch[i][1] + local s1 = channelType2String(b1,b2) + local s = string.format("%s (%02X %02X) %s\n", MODEL.PORT_TEXT[i], + b1, b2,s1) + DSM_Ch[i][2]=s1 + LOG_write(s) + end + + --MODEL.AirWingTailDesc = string.format("Aircraft(%s) Wing(%s) Tail(%s)",aircraft_type_text[aircraftType],wing_type_text[wingType],tail_type_text[tailType]) +end + + -- Main Program + + LOG_write("Creating DSMPort Info\n") + CreateDSMPortChannelInfo() + + diff --git a/SCRIPTS/TOOLS/DSMLIB/DsmMainMenuLib.lua b/SCRIPTS/TOOLS/DSMLIB/DsmMainMenuLib.lua new file mode 100644 index 0000000..f23bac3 --- /dev/null +++ b/SCRIPTS/TOOLS/DSMLIB/DsmMainMenuLib.lua @@ -0,0 +1,90 @@ +local Log, menuLib, modelLib, DEBUG_ON, SIMULATION_ON = ... -- Get DebugON from parameters +local MAIN_MENU_LIB_VERSION = "0.56" +local MODEL = modelLib.MODEL + +local PHASE = menuLib.PHASE +local LINE_TYPE = menuLib.LINE_TYPE + +local lastGoodMenu=0 + +-- Creates the menus to Render with the GUI +local function ST_LoadMenu(menuId) + local ctx = menuLib.DSM_Context + menuLib.clearMenuLines() + + if (menuId==0x1000) then -- MAIN MENU + ctx.Menu = { MenuId = 0x1000, Text = "Main Menu ("..MODEL.modelName..")", PrevId = 0, NextId = 0, BackId = 0, TextId=0 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, Text = "Model Setup", ValId = 0xFFF3,TextId=0 } + + if (SIMULATION_ON) then + ctx.MenuLines[4] = { Type = LINE_TYPE.MENU, Text = "RX Simulator (GUI dev only)", ValId = 0xFFF1, TextId=0 } -- Menu 0xFFF2 to SIMULATOR + end + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, Text = "Forward Programming RX", ValId = 0xFFF2, TextId=0 } -- Menu 0xFFF2 to Real RX + ctx.SelLine = 6 + + lastGoodMenu = menuId + else + --print("NOT IMPLEMENTED") + ctx.Menu = { MenuId = 0x0002, Text = "NOT IMPLEMENTED", TextId = 0, PrevId = 0, NextId = 0, BackId = lastGoodMenu } + ctx.SelLine = menuLib.BACK_BUTTON + end + + menuLib.PostProcessMenu() +end + +local function Main_Send_Receive() + local ctx = menuLib.DSM_Context + + if ctx.Phase == PHASE.RX_VERSION then -- Just Init RX Version + ctx.RX.Name = "Main Menu" + ctx.RX.Version = MAIN_MENU_LIB_VERSION + ctx.Phase = PHASE.MENU_TITLE + ctx.Menu.MenuId = 0 + + ctx.Refresh_Display = true + elseif ctx.Phase == PHASE.WAIT_CMD then + + elseif ctx.Phase == PHASE.MENU_TITLE then -- request menu title + if ctx.Menu.MenuId == 0 then -- First time loading a menu ? + ST_LoadMenu(0x01000) + else + ST_LoadMenu(ctx.Menu.MenuId) + end + ctx.Phase = PHASE.WAIT_CMD + ctx.Refresh_Display = true + + elseif ctx.Phase == PHASE.VALUE_CHANGING then -- send value + local line = ctx.MenuLines[ctx.SelLine] -- Updated Value of SELECTED line + --if (DEBUG_ON) then Log.LOG_write("%3.3f %s: ", menuLib.getElapsedTime(), menuLib.phase2String(ctx.Phase)) end + --if (DEBUG_ON) then Log.LOG_write("SEND SIM_updateMenuValue(ValueId=0x%X Text=\"%s\" Value=%s)\n", line.ValId, line.Text, menuLib.lineValue2String(line)) end + ctx.Phase = PHASE.VALUE_CHANGING_WAIT + + elseif ctx.Phase == PHASE.VALUE_CHANGING_WAIT then + local line = ctx.MenuLines[ctx.SelLine] + + elseif ctx.Phase == PHASE.VALUE_CHANGE_END then -- send value + local line = ctx.MenuLines[ctx.SelLine] -- Updated Value of SELECTED line + --if (DEBUG_ON) then Log.LOG_write("%3.3f %s: ", menuLib.getElapsedTime(), menuLib.phase2String(ctx.Phase)) end + --if (DEBUG_ON) then Log.LOG_write("SEND SIM_updateMenuValue(ValueId=0x%X Text=\"%s\" Value=%s)\n", line.ValId, line.Text, menuLib.lineValue2String(line)) end + --if (DEBUG_ON) then Log.LOG_write("SEND SIM_validateMenuValue(ValueId=0x%X Text=\"%s\" Value=%s)\n", line.ValId, line.Text, menuLib.lineValue2String(line)) end + ctx.Phase = PHASE.WAIT_CMD + + elseif ctx.Phase == PHASE.EXIT then + ctx.Phase=PHASE.EXIT_DONE + return 1 + end + + return 0 +end + +local function Main_Init() + local ctx = menuLib.DSM_Context + ctx.Phase = PHASE.RX_VERSION +end + +local function Main_Done() + local ctx = menuLib.DSM_Context + ctx.Phase = PHASE.EXIT_DONE +end + +return { init=Main_Init, run=Main_Send_Receive, done=Main_Done } \ No newline at end of file diff --git a/SCRIPTS/TOOLS/DSMLIB/DsmMenuLib.lua b/SCRIPTS/TOOLS/DSMLIB/DsmMenuLib.lua new file mode 100644 index 0000000..3831b60 --- /dev/null +++ b/SCRIPTS/TOOLS/DSMLIB/DsmMenuLib.lua @@ -0,0 +1,788 @@ +--- ######################################################################### +---- # # +---- # Copyright (C) OpenTX/EdgeTx # +-----# # +---- # 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. # +---- # # +---- ######################################################################### + +------------------------------------------------------------------------------ +-- This script library is a rewrite of the original DSM forward programming Lua +-- Script. The goal is to make it easier to understand, mantain, and to +-- separate the GUI from the DSM Forward programming engine/logic +-- in this way, GUIs can evolve independent. OpenTX Gui, EdgeTx GUI, Small Radios, etc. + +-- Code is based on the code/work by: Pascal Langer (Author of the Multi-Module) +-- Rewrite/Enhancements By: Francisco Arzu + +local Log, DEBUG_ON = ... -- Parameters + + +local MenuLib = { } + +local PHASE = { + INIT = 0, + RX_VERSION = 1, + WAIT_CMD = 2, + MENU_TITLE = 3, + MENU_REQ_TX_INFO = 4, + MENU_LINES = 5, + MENU_VALUES = 6, + VALUE_CHANGING = 7, + VALUE_CHANGING_WAIT = 8, + VALUE_CHANGE_END = 9, + EXIT = 10, + EXIT_DONE = 11 +} + +local LINE_TYPE = { + MENU = 0x1C, + LIST_MENU = 0x0C, -- List: INC Change + Validate + LIST_MENU_NC = 0x6C, -- List: No Incremental Change + LIST_MENU_NC2 = 0x6D, -- List: No Incremental Change (Frame Rate Herz) + LIST_MENU_TOG = 0x4C, -- List: Incremental Change, sometimes bolean/Toggle menu (if only 2 values) + LIST_MENU_ORI = 0xCC, -- List: Incremental Change, Orientation Heli + + VALUE_NUM_I8_NC = 0x60, -- 8 bit number, no incremental change + VALUE_PERCENT = 0xC0, -- 8 bit number, Signed, percent + VALUE_DEGREES = 0xE0, -- 8 bit number, Signed, Degress + VALUE_NUM_I8 = 0x40, -- 8 bit number + VALUE_NUM_I16 = 0x41, -- 16 Bit number + VALUE_NUM_SI16 = 0xC1, -- 16 bit number, Signed + + LT_EMPTY = 0x00 +} + +-- Bug in Lua compiler, confusing with global BOLD and RIGHT +local DISP_ATTR = { + _BOLD = 0x01, _RIGHT=0x02, _CENTER=0x04, PERCENT = 0x10, DEGREES=0x20, FORCED_MENU = 0x40 +} + +--RX IDs-- +local RX = { + AR636B = 0x0001, + SPM4651T = 0x0014, + AR637T = 0x0015, + AR637TA = 0x0016, + FC6250HX = 0x0018, + AR630 = 0x0019, + AR8360T = 0x001A, + AR10360T = 0x001C, + AR631 = 0x001E +} + +local DSM_Context = { + Phase = PHASE.INIT, + Menu = { MenuId = 0, Text = "", TextId = 0, PrevId = 0, NextId = 0, BackId = 0 }, + MenuLines = {}, + RX = { Id=0, Name = "", Version = "" }, + Refresh_Display = true, + SendDataToRX = 1, + + 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 +} + +function DSM_Context.isEditing() return DSM_Context.EditLine~=nil end + +local MAX_MENU_LINES = 6 +local BACK_BUTTON = -1 -- Tread it as a display line #-1 +local NEXT_BUTTON = MAX_MENU_LINES + 1 -- Tread it as a display line #7 +local PREV_BUTTON = MAX_MENU_LINES + 2 -- Tread it as a display line #7 + +-- Text Arrays for Display Text and Debuging +local PhaseText = {} +local LineTypeText = {} + +local Text = {} -- Text for Menu and Menu Lines (Headers only) +local List_Text = {} -- Messages for List Options (values only) +local List_Text_Img = {} -- If the Text has Attached Images +local List_Values = {} -- Additiona restrictions on List Values when non contiguos (L_M1 lines has this problem) +local Flight_Mode = {[0]="Fligh Mode"} +local RxName = {} + +local StartTime = 0 + +------------------------------------------------------------------------------------------------------------ +-- Get Elapsed Time since we started running the Script. Return a float in format: Seconds.Milliseconds +function MenuLib.getElapsedTime() + local t = getTime() + if (StartTime == 0) then StartTime = t end + + return ((t - StartTime) * 10) / 1000 +end + +------------- Line Type helper functions ------------------------------------------------------------------ + +-- Check if the text are Flight modes, who will be treated different for Display +function MenuLib.isFlightModeLine(line) + return (line.TextId >= 0x8000 and line.TextId <= 0x8003) +end + +function MenuLib.isSelectableLine(line) -- is the display line Selectable?? + -- values who are not selectable + if (line.Type == 0) then return false end -- Empty Line + if (line.Type == LINE_TYPE.MENU and line.ValId == line.MenuId and bit32.band(line.TextAttr, DISP_ATTR.FORCED_MENU)==0) then return false end -- Menu that navigates to Itself? + if (line.Min==0 and line.Max==0 and line.Def==0) then return false end -- Values with no Range are only for display + if (line.Type == LINE_TYPE.VALUE_NUM_I8_NC and MenuLib.isFlightModeLine(line)) then return false end -- Flight mode is not Selectable + return true +end + +function MenuLib.isEditableLine(line) -- is the display line editable?? + -- values who are not editable + if (line.Type == 0 or line.Type == LINE_TYPE.MENU) then return false end -- Menus are not editable + if (line.Min==0 and line.Max==0 and line.Def==0) then return false end -- Values with no Range are only for display + if (line.Type == LINE_TYPE.VALUE_NUM_I8_NC and MenuLib.isFlightModeLine(line)) then return false end -- Flight mode is not Editable + -- any other is Editable + return true +end + +function MenuLib.isListLine(line) -- is it a List of options?? + if (line.Type == LINE_TYPE.LIST_MENU_NC or line.Type == LINE_TYPE.LIST_MENU or + line.Type == LINE_TYPE.LIST_MENU_TOG or line.Type == LINE_TYPE.LIST_MENU_NC2 or + line.Type == LINE_TYPE.LIST_MENU_ORI) then return true end + return false +end + +function MenuLib.isPercentValueLineByMinMax(line) + return + (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) +end + +function MenuLib.isPercentValueLine(line) -- is it a Percent value?? + if (line.Type == LINE_TYPE.VALUE_PERCENT) then return true end + return false +end + +function MenuLib.isNumberValueLine(line) -- is it a number ?? + if (MenuLib.isListLine(line) or line.Type == LINE_TYPE.MENU or line.Type == 0) then return false + else return true end +end + +function MenuLib.isIncrementalValueUpdate(line) + if (line.Type == LINE_TYPE.LIST_MENU_NC or line.Type == LINE_TYPE.LIST_MENU_NC2 or + line.Type == LINE_TYPE.VALUE_NUM_I8_NC or line.Type == LINE_TYPE.VALUE_DEGREES) then return false end + return true +end + +------------------------------------------------------------------------------------------------------------ +function MenuLib.Get_Text(index) + if (index >= 0x8000) then + return Flight_Mode[0] + end + + local out = Text[index] -- Find in regular header first + if out== nil then + out = List_Text[index] -- Try list values, don't think is necesary, but just playing Safe + end + if out == nil then -- unknown... + out = "Unknown_" .. string.format("%X", index) + end + return out +end + +function MenuLib.Get_List_Text(index) + local out = List_Text[index] -- Try to find the message in List_Text + if out == nil then + out = Text[index] -- Try list headers, don't think is necesary, but just playing Safe + end + if out == nil then -- unknown... + out = "UnknownLT_" .. string.format("%X", index) + end + return out +end + +function MenuLib.Get_List_Text_Img(index) + local out = List_Text_Img[index] + return out +end + +function MenuLib.Get_List_Values(index) + local out = List_Values[index] + return out +end + +function MenuLib.Get_RxName(index) + local out = RxName[index] + return out or ("RX_" .. string.format("%X", index)) +end + +----------- Debugging 2-String functions ------------------------------------------------------------------- + +function MenuLib.phase2String(index) + local out = PhaseText[index] + return out or ("Phase_" .. string.format("%X", index)) +end + +function MenuLib.lineType2String(index) + local out = LineTypeText[index] + return out or ("LT_" .. string.format("%X", index or 0xFF)) +end + +function MenuLib.lineValue2String(l) + if (DEBUG_ON == 0) then + return "" + end + if (l ~= nil and l.Val ~= nil) then + local value = l.Val + if MenuLib.isListLine(l) then + value = value .. "|\"" .. MenuLib.Get_List_Text(l.Val + l.TextStart) .. "\"" + else + value = value..(l.Format or "") + end + return value + end + return "nil" +end + +function MenuLib.menu2String(m) + local txt = "Menu[]" + if (m ~= nil) then + txt = string.format("M[Id=0x%X P=0x%X N=0x%X B=0x%X Text=\"%s\"[0x%X]]", + m.MenuId, m.PrevId, m.NextId, m.BackId, m.Text, m.TextId) + end + return txt +end + +function MenuLib.menuLine2String(l) + local txt = "Line[]" + if (l ~= nil) then + local value = "" + local range = "" + if l.Type~=LINE_TYPE.MENU then + value = "Val="..MenuLib.lineValue2String(l) + if MenuLib.isListLine(l) then + range = string.format("NL=(%s->%s,%s,S=%s) ",l.Min, l.Max, l.Def, l.TextStart ) + range = range .. (l.MinMaxOrig or "") + else + range = string.format("[%s->%s,%s]",l.Min, l.Max, l.Def) + end + end + + txt = string.format("L[#%s T=%s VId=0x%X Text=\"%s\"[0x%X] %s %s MId=0x%X A=0x%X]", + l.lineNum, MenuLib.lineType2String(l.Type), l.ValId, + l.Text, l.TextId, + value, + range, + l.MenuId, + l.TextAttr + ) + end + return txt +end + +----------------------------------------------------------------------------------------------------------- +-- Post Procssing Line from Raw values receive by RX or Simulation + +function MenuLib.isDisplayAttr(attr, bit) + return (bit32.band(attr,bit)>0) +end + +function MenuLib.ExtractDisplayAttr(text1, attr) + local text = text1, pos; + + for i=1,2 do + text, pos = string.gsub(text, "/c$", "") + if (pos>0) then -- CENTER + attr = bit32.bor(attr, DISP_ATTR._CENTER) + end + + text, pos = string.gsub(text, "/r$", "") + if (pos>0) then -- RIGHT + attr = bit32.bor(attr, DISP_ATTR._RIGHT) + end + + text, pos = string.gsub(text, "/p$", "") + if (pos>0) then -- Percent TEXT + attr = bit32.bor(attr, DISP_ATTR.PERCENT) + end + + text, pos = string.gsub(text, "/b$", "") + if (pos>0) then -- BOLD TEXT + attr = bit32.bor(attr, DISP_ATTR._BOLD) + end + + text, pos = string.gsub(text, "/m$", "") + if (pos>0) then -- FORCED MENU Button + attr = bit32.bor(attr, DISP_ATTR.FORCED_MENU) + end + end + + return text, attr +end + +function MenuLib.MenuPostProcessing(menu) + menu.Text, menu.TextAttr = MenuLib.ExtractDisplayAttr(menu.Text,menu.TextAttr or 0) +end + +function MenuLib.MenuLinePostProcessing(line) + if (line.Text==nil) then + line.Text = MenuLib.Get_Text(line.TextId) -- Get Textual Line headeing text + end + + -- Text formatting options + line.Text, line.TextAttr = MenuLib.ExtractDisplayAttr(line.Text,line.TextAttr or 0) + + if line.Type == LINE_TYPE.MENU then + -- nothing to do on menu entries + line.Val=nil + elseif MenuLib.isListLine(line) then + -- Original Range for Debugging + line.MinMaxOrig = "[" .. line.Min .. "->" .. line.Max .. "," .. line.Def .. "]" + + -- Normalize Min/Max to be relative to Zero + line.TextStart = line.Min + line.Def = line.Def - line.Min -- normalize default value + line.Max = line.Max - line.Min -- normalize max index + line.Min = 0 -- min index + else -- default to numerical value + if MenuLib.isPercentValueLine(line) or MenuLib.isPercentValueLineByMinMax(line) then + -- either explicit Percent or NO-Change value, but range is %Percent + line.Format ="%" + line.TextAttr = bit32.bor(line.TextAttr,DISP_ATTR.PERCENT) + elseif (line.Type == LINE_TYPE.VALUE_DEGREES) then + line.Format ="o" + line.TextAttr = bit32.bor(line.TextAttr,DISP_ATTR.DEGREES) + end + end + + line.MinMaxDebug = MenuLib.lineType2String(line.Type).." "..(line.MinMaxOrig or "") +end + + +function MenuLib.ChangePhase(newPhase) + DSM_Context.Phase = newPhase + DSM_Context.SendDataToRX = 1 +end + +function MenuLib.Value_Add(line, inc) + if (DEBUG_ON) then Log.LOG_write("%3.3f %s: DSM_Value_Add(%s,%s)\n", + MenuLib.getElapsedTime(), MenuLib.phase2String(DSM_Context.Phase), inc, MenuLib.menuLine2String(line)) end + + local skipIncrement = false + local values = nil + local origVal = line.Val + + -- Use local validation for LIST_MENU1 when the range is wide open + -- Also use if for some LIST_MENU0 that the Range seems incorrect + if (MenuLib.isListLine(line)) then -- and line.Type==LINE_TYPE.LIST_MENU1 and line.Min==0 and line.Max==244) then + values = MenuLib.Get_List_Values(line.TextId) + end + + + if (values~=nil) then -- Inc/Dec based on a list of predefined Values Local to Script (values not contiguous), + -- locate current value in values array + -- Values are Zero normalized to the Start of the List (line.TextStart) + for i = 1, #values do + if ((values[i]-line.TextStart)==origVal) then + skipIncrement = true + if (inc==-1 and i > 1) then -- PREV + line.Val = values[i-1]-line.TextStart + elseif (inc==1 and i < #values) then -- NEXT + line.Val = values[i+1]-line.TextStart + end + break + end + end + end + + if not skipIncrement then + -- Do it Sequentially + line.Val = line.Val + inc + + if line.Val > line.Max then + line.Val = line.Max + elseif line.Val < line.Min then + line.Val = line.Min + end + end + + if (origVal~=line.Val and MenuLib.isIncrementalValueUpdate(line)) then + -- Update RX value on every change + MenuLib.ChangePhase(PHASE.VALUE_CHANGING) + end +end + +function MenuLib.Value_Default(line) + local origVal = line.Val + if (DEBUG_ON) then Log.LOG_write("%3.3f %s: DSM_Value_Default(%s)\n", + MenuLib.getElapsedTime(), MenuLib.phase2String(DSM_Context.Phase), MenuLib.menuLine2String(line)) end + + line.Val = line.Def + if (origVal~=line.Val and MenuLib.isIncrementalValueUpdate(line)) then + -- Update RX value on every change + MenuLib.ChangePhase(PHASE.VALUE_CHANGING) + end +end + +function MenuLib.Value_Write_Validate(line) + if (DEBUG_ON) then Log.LOG_write("%3.3f %s: DSM_Value_Write_Validate(%s)\n", + MenuLib.getElapsedTime(), MenuLib.phase2String(DSM_Context.Phase), MenuLib.menuLine2String(line)) end + + MenuLib.ChangePhase(PHASE.VALUE_CHANGE_END) -- Update + Validate value in RX + DSM_Context.EditLine = nil -- Exit Edit Mode (By clearing the line editing) +end + +function MenuLib.GotoMenu(menuId, lastSelectedLine) + if (DEBUG_ON) then Log.LOG_write("%3.3f %s: DSM_GotoMenu(0x%X,LastSelectedLine=%d)\n", + MenuLib.getElapsedTime(), MenuLib.phase2String(DSM_Context.Phase), menuId, lastSelectedLine) end + + DSM_Context.Menu.MenuId = menuId + DSM_Context.SelLine = lastSelectedLine + -- Request to load the menu Again + MenuLib.ChangePhase(PHASE.MENU_TITLE) +end + +function MenuLib.MoveSelectionLine(dir) + local ctx = DSM_Context + local menu = ctx.Menu + local menuLines = ctx.MenuLines + + if (dir == 1) then -- NEXT + if ctx.SelLine <= MAX_MENU_LINES then + local num = ctx.SelLine + for i = ctx.SelLine + 1, MAX_MENU_LINES, 1 do + if MenuLib.isSelectableLine(menuLines[i]) then + ctx.SelLine = i + break + end + end + + if num == ctx.SelLine then + if menu.NextId ~= 0 then -- Next + ctx.SelLine = NEXT_BUTTON + elseif menu.PrevId ~= 0 then -- Prev + ctx.SelLine = PREV_BUTTON + end + end + elseif menu.PrevId ~= 0 then -- Prev + ctx.SelLine = PREV_BUTTON + end + return + end + + if (dir == -1) then -- PREV + if ctx.SelLine == PREV_BUTTON and menu.NextId ~= 0 then + ctx.SelLine = NEXT_BUTTON + elseif ctx.SelLine > 0 then + if ctx.SelLine > MAX_MENU_LINES then + ctx.SelLine = NEXT_BUTTON + end + local num = ctx.SelLine + for i = ctx.SelLine - 1, 0, -1 do + if MenuLib.isSelectableLine(menuLines[i]) then + ctx.SelLine = i + break + end + end + if num == ctx.SelLine then -- can't find previous selectable line, then SELECT Back + if (menu.BackId ~= 0) then ctx.SelLine = BACK_BUTTON end + end + else + if (menu.BackId ~= 0) then ctx.SelLine = BACK_BUTTON end -- Back + end + end +end + + +-- Clear each line of the menu +function MenuLib.clearMenuLines() + local ctx = DSM_Context + for i = 0, MAX_MENU_LINES do -- clear menu + ctx.MenuLines[i] = { MenuId = 0, lineNum = 0, Type = 0, Text = "", TextId = 0, ValId = 0, Min=0, Max=0, Def=0, TextStart=0, Val=nil } + end +end + +-- Post processing needed for each menu +function MenuLib.PostProcessMenu() + local ctx = DSM_Context + + if (ctx.Menu.Text==nil) then + ctx.Menu.Text = MenuLib.Get_Text(ctx.Menu.TextId) + MenuLib.MenuPostProcessing (ctx.Menu) + end + + --if (DEBUG_ON) then Log.LOG_write("SIM RESPONSE Menu: %s\n", MenuLib.menu2String(ctx.Menu)) end + + for i = 0, MenuLib.MAX_MENU_LINES do -- clear menu + local line = ctx.MenuLines[i] + if (line.Type~=0) then + line.MenuId = ctx.Menu.MenuId + line.lineNum = i + MenuLib.MenuLinePostProcessing(line) -- Do the same post processing as if they come from the RX + --if (DEBUG_ON) then Log.LOG_write("SIM RESPONSE MenuLine: %s\n", MenuLib.menuLine2String(line)) end + end + + end +end + +function MenuLib.GetFlightModeValue(line) + local ret = line.Text.." " + local val = line.Val + + if (val==nil) then return ret.."--" end + + -- Adjust the displayed value for Flight mode line as needed + if (DSM_Context.RX.Id == RX.FC6250HX) then + -- Helicopter Flights modes + if (val==0) then ret = ret .. "1 / HOLD" + elseif (val==1) then ret = ret .. "2 / Normal" + elseif (val==2) then ret = ret .. "3 / Stunt 1" + elseif (val==3) then ret = ret .. "4 / Stunt 2" + elseif (val==4) then ret = ret .. "5 / Panic" + else + ret = ret .. " " .. (val + 1) + end + else + -- No adjustment needed + if (val==190) then + ret=ret.."Err:Out of Range" + else + ret=ret..(val + 1) + end + + end + return ret +end + +function MenuLib.Init() + print("MenuLib.Init()") + -- Phase Names + PhaseText[PHASE.INIT] = "INIT" + PhaseText[PHASE.RX_VERSION] = "RX_VERSION" + PhaseText[PHASE.WAIT_CMD] = "WAIT_CMD" + PhaseText[PHASE.MENU_TITLE] = "MENU_TITLE" + PhaseText[PHASE.MENU_REQ_TX_INFO] = "MENU_REQ_TX_INFO" + PhaseText[PHASE.MENU_LINES] = "MENU_LINES" + PhaseText[PHASE.MENU_VALUES] = "MENU_VALUES" + PhaseText[PHASE.VALUE_CHANGING] = "VALUE_CHANGING" + PhaseText[PHASE.VALUE_CHANGING_WAIT] = "VALUE_EDITING" + PhaseText[PHASE.VALUE_CHANGE_END] = "VALUE_CHANGE_END" + PhaseText[PHASE.EXIT] = "EXIT" + PhaseText[PHASE.EXIT_DONE] = "EXIT_DONE" + + + -- Line Types + LineTypeText[LINE_TYPE.MENU] = "M" + LineTypeText[LINE_TYPE.LIST_MENU_NC] = "LM_nc" + LineTypeText[LINE_TYPE.LIST_MENU] = "LM" + LineTypeText[LINE_TYPE.LIST_MENU_TOG] = "LM_tog" + LineTypeText[LINE_TYPE.LIST_MENU_NC2] = "LM_nc2" + LineTypeText[LINE_TYPE.LIST_MENU_ORI] = "LM_ori" + LineTypeText[LINE_TYPE.VALUE_NUM_I8_NC] = "V_nc" + LineTypeText[LINE_TYPE.VALUE_PERCENT] = "V_%" + LineTypeText[LINE_TYPE.VALUE_DEGREES] = "V_de" + LineTypeText[LINE_TYPE.VALUE_NUM_I8] = "V_i8" + LineTypeText[LINE_TYPE.VALUE_NUM_I16] = "V_i16" + LineTypeText[LINE_TYPE.VALUE_NUM_SI16] = "V_s16" + LineTypeText[LINE_TYPE.LT_EMPTY] = "Z" + + DSM_Context.Phase = PHASE.RX_VERSION +end + +function MenuLib.clearAllText() + local function clearTable(t) + for i, v in ipairs(t) do t[i] = nil end + end + + clearTable(Text) + clearTable(List_Text) + clearTable(List_Text_Img) + clearTable(List_Values) +end + +function MenuLib.LoadTextFromFile(fileName, mem) + 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 + +function MenuLib.INC_LoadTextFromFile(fileName, FileState) + ----------------------- + 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 + + local function GetTextInfoFromFile(pos) + local dataFile = io.open(fileName, "r") + io.seek(dataFile,pos) + local buff = io.read(dataFile, 100) + io.close(dataFile) + + local line="" + local index="" + local type="" + + local pipe=0 + local comment=0 + local newPos = pos + + -- Parse the line: + -- Format: TT|0x999|Text -- Comment + for i=1,#buff do + newPos=newPos+1 + local ch = string.sub(buff,i,i) + + if (pipe < 2 and ch=="|") then pipe=pipe+1 -- Count pipes pos (Type | Index | .....) + elseif (ch=="\r") then -- Ignore CR + elseif (ch=="\n") then break -- LF, end of line + elseif (ch=="-") then -- March comments + comment=comment+1 + if (comment==2) then pipe=6 end -- Comment part of line + else + -- regular char + comment=0 + if (pipe==0) then type=type..ch -- in TT (Type) + elseif (pipe==1) then index=index..ch -- in Index + elseif (pipe<6) then line=line..ch end -- in Text + end -- Regular char + end -- For + + return type, index, rtrim(line), newPos + end + + ----------------------------------------------------------- + + if (FileState.state==nil) then -- Initial State + FileState.state=1 + FileState.lineNo=0 + FileState.filePos=0 + end + + if FileState.state==1 then + for l=1,10 do -- do 10 lines at a time + local type, sIndex, text + local lineStart = FileState.filePos + + type, sIndex, text, FileState.filePos = GetTextInfoFromFile(FileState.filePos) + + --print(string.format("T=%s, I=%s, T=%s LS=%d, FP=%d",type,sIndex,text,lineStart, FileState.filePos)) + + if (lineStart==FileState.filePos) then -- EOF + FileState.state=2 --EOF State + return 1 + end + FileState.lineNo = FileState.lineNo + 1 + + type = rtrim(type) + + if (string.len(type) > 0 and string.len(sIndex) > 0) then + local index = tonumber(sIndex) + + if (index == nil) then + assert(false, string.format("%s:%d: Invalid Hex num [%s]", fileName, FileState.lineNo, sIndex)) + elseif (type == "T") then + Text[index] = text + elseif (type == "LT") then + List_Text[index] = text + elseif (type == "LI") then + List_Text_Img[index] = text + elseif (type == "FM") then + Flight_Mode[0] = text + elseif (type == "RX") then + RxName[index] = text + else + assert(false, string.format("%s:%d: Invalid Line Type [%s]", fileName, FileState.lineNo, type)) + end + end + end -- for + end -- if + + return 0 + end + + +-- Export some Constants and Variables +MenuLib.PHASE = PHASE +MenuLib.LINE_TYPE = LINE_TYPE +MenuLib.DISP_ATTR = DISP_ATTR +MenuLib.RX = RX + +MenuLib.MAX_MENU_LINES = MAX_MENU_LINES +MenuLib.BACK_BUTTON = BACK_BUTTON +MenuLib.NEXT_BUTTON = NEXT_BUTTON +MenuLib.PREV_BUTTON = PREV_BUTTON + +MenuLib.DSM_Context = DSM_Context + +MenuLib.Text = Text +MenuLib.List_Text = List_Text +MenuLib.List_Text_Img = List_Text_Img +MenuLib.List_Values = List_Values + +MenuLib.LOG_open = Log.LOG_open +MenuLib.LOG_write = Log.LOG_write +MenuLib.LOG_Close = Log.LOG_close + + +return MenuLib \ No newline at end of file diff --git a/SCRIPTS/TOOLS/DSMLIB/DsmModelLib.lua b/SCRIPTS/TOOLS/DSMLIB/DsmModelLib.lua new file mode 100644 index 0000000..357c4a5 --- /dev/null +++ b/SCRIPTS/TOOLS/DSMLIB/DsmModelLib.lua @@ -0,0 +1,789 @@ + +local Log, DEBUG_ON = ... + +local DATA_PATH = "/MODELS/DSMDATA" -- Path to store model settings files +local TX_CHANNELS = 12 + +-- MODEL information from ETX/OTX +local ModelLib = {} + +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 +} + +--Channel Types -- +local CH_TYPE = { + NONE = 0x00, + AIL = 0x01, + ELE = 0x02, + RUD = 0x04, + + REVERSE = 0x20, + THR = 0x40, + SLAVE = 0x80, +} + +-- Seems like Reverse Mix is complement of the 3 bits +local CH_MIX_TYPE = { + MIX_NORM = 0x00, -- 0000 + MIX_AIL = 0x10, -- 0001 Taileron + MIX_ELE = 0x20, -- 0010 For VTIAL and Delta-ELEVON + MIX_RUD = 0x30, -- 0011 For VTIAL + + MIX_RUD_REV = 0x40, -- 0100 For VTIAL + MIX_ELE_REV = 0x50, -- 0101 For VTIAL and Delta-ELEVON A + MIX_AIL_REV = 0x60, -- 0110 Taileron + MIX_NORM_REV = 0x70 -- 0111 +} + +local AIRCRAFT_TYPE = { + PLANE = 0, + HELI = 1, + GLIDER = 2, + DRONE = 3 +} +local aircraft_type_text = {[0]="Plane","Heli","Glider","Drone"} + +local WING_TYPE = { + AIL_1 = 0, --1 + AIL_2 = 1, --2 + FLAPERON = 2, --2 + AIL_1_FLP_1 = 3, --2 + AIL_2_FLP_1 = 4, --3 + AIL_2_FLP_2 = 5, --4 + ELEVON_A = 6, --2 + ELEVON_B = 7 --2 +} + +local wing_type_text = {[0]="Normal","Dual Ail","Flapperon", "Ail + Flp","Dual Ail + Flp","Dual Ail/Flp","Elevon A","Elevon B"} + +local TAIL_TYPE = { + RUD_1 = 0, -- 1 + RUD_1_ELEV_1 = 1, -- 2 + RUD_1_ELEV_2 = 2, -- 3 + RUD_2_ELEV_1 = 3, -- 3 + RUD_2_ELEV_2 = 4, -- 4 + VTAIL_A = 5, -- 2 + VTAIL_B = 6, -- 2 + TRAILERON_A = 7, -- 3 + TRAILERON_B = 8, -- 3 + TRAILERON_A_R2 = 9, -- 3 + TRAILERON_B_R2 = 10 -- 3 +} +local tail_type_text = {[0]="Rud Only","Normal","Rud + Dual Ele","Dual Rud + Elv","Dual Rud/Ele", + "VTail A","VTail B","Taileron A","Taileron B","Taileron A + 2x Rud","Taileron B + 2x Rud"} + +local CH_MODE_TYPE = { + NORMAL = 0, + REVERSE = 1, + USE_TX = 3 +} + +local PORT = { + PORT1 = 0, + PORT2 = 1, + PORT3 = 2, + PORT4 = 3, + PORT5 = 4, + PORT6 = 5, + PORT7 = 6, + PORT8 = 7, + PORT9 = 8, + PORT10 = 9, + PORT11 = 10, + PORT12 = 11 +} + +local MEMU_VAR = { + AIRCRAFT_TYPE = 1001, + WING_TYPE = 1002, + TAIL_TYPE = 1003, + + CH_BASE = 1010, + CH_THR = 1010, + + CH_L_AIL = 1011, + CH_R_AIL = 1012, + CH_L_FLP = 1013, + CH_R_FLP = 1014, + + CH_L_RUD = 1015, + CH_R_RUD = 1016, + CH_L_ELE = 1017, + CH_R_ELE = 1018, + + PORT_BASE = 1020, + PORT1_MODE = 1020, + PORT2_MODE = 1021, + PORT3_MODE = 1022, + PORT4_MODE = 1023, + PORT5_MODE = 1024, + PORT6_MODE = 1025, + PORT7_MODE = 1026, + PORT8_MODE = 1027, + PORT9_MODE = 1028, + PORT10_MODE = 1029, + PORT11_MODE = 1030, + PORT12_MODE = 1031, + + DATA_END = 1040 +} + + +-- MENU DATA Management +local MENU_DATA = {} -- Store the variables used in the Menus. + + +---- DSM_ChannelInfo --------------------------------- +-- First byte describe Special Mixing (Vtail/Elevon = 0x20) +--VTAIL +--(0x00 0x06) CH_TYPE.ELE+CH_TYPE.RUD (0x02+0x04 = 0x06) +--(0x20 0x86) CH_TYPE.ELE+CH_TYPE.RUD+CH_TYPE.SLAVE (0x02+0x04+0x80 = 0x86) + +-- The 2nd byte describes the functionality of the port +-- +-- Single Example: CH_TYPE.AIL (0x01) Aileron +-- Reverse Example: CH_TYPE.AIL+CH_TYPE.REVERSE (0x01+0x20=0x21) Reverse Aileron +-- Slave Example: CH_TYPE.AIL+CH_TYPE.SLAVE (0x01+0x80) -- 2nd servo Aileron + +-- Elevon Example: CH_TYPE.AIL+CH_TYPE.ELE (0x01+0x02 = 0x03) -- Elevon +-- Elevon Example: CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE (0x01+0x02+0x80 = 0x83) -- Slave Elevon + +-- RudElv (VTail) Example: CH_TYPE.ELE+CH_TYPE.RUD (0x02+0x04 = 0x06) -- Rudevator +-- RudElv (VTail) Example: CH_TYPE.ELE+CH_TYPE.RUD+CH_TYPE.SLAVE (0x02+0x04+0x80 = 0x86) -- Rudevator Slave + +-- DEFAULT Simple Plane Port configuration (The Configuration tool will overrride this) +MODEL.DSM_ChannelInfo= {[0]= -- Start array at position 0 + {[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.THR}, -- Ch1 Thr (0x40) + {[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.AIL}, -- Ch2 Ail (0x01) + {[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.ELE}, -- Ch2 ElE (0x02) + {[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.RUD}, -- Ch4 Rud (0x04) + {[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE}, -- Ch5 Gear (0x00) + {[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE}, -- Ch6 Aux1 (0x00) + {[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE}, -- Ch7 Aux2 (0x00) + {[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE}, -- Ch8 Aux3 (0x00) + {[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE}, -- Ch9 Aux4 (0x00) + {[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE}, -- Ch10 Aux5 (0x00) + {[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE}, -- Ch11 Aux6 (0x00) + {[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE} -- Ch12 Aux7 (0x00) + } + +function ModelLib.printChannelSummary(a,w,t) + -- Summary + print("CHANNEL INFORMATION") + print("Aircraft:".. (aircraft_type_text[MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE]] or "--")) + print("Wing Type:".. (wing_type_text[MENU_DATA[MEMU_VAR.WING_TYPE]] or "--")) + print("Tail Type:".. (tail_type_text[MENU_DATA[MEMU_VAR.TAIL_TYPE]] or "--")) + print("Thr:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_THR] or 30)] or "--")) -- use fake ch30 for non existing channels + print("LAil:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_L_AIL] or 30)] or "--")) + print("RAil:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_R_AIL] or 30)] or "--")) + print("LFlp:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_L_FLP] or 30)] or "--")) + print("RFlp:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_R_FLP] or 30)] or "--")) + print("LEle:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_L_ELE] or 30)] or "--")) + print("REle:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_R_ELE] or 30)] or "--")) + print("LRud:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_L_RUD] or 30)] or "--")) + print("RRud:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_R_RUD] or 30)] or "--")) +end + +function ModelLib.printServoReverseInfo() + print("SERVO Normal/Reverse INFORMATION") + for i=0, TX_CHANNELS-1 do + local s="--" + if (MENU_DATA[MEMU_VAR.PORT1_MODE+i] or 0) == 0 then s="NORMAL" else s="REVERSE" end + print(string.format("Port%d: %s", i+1, s)) + end +end + +function ModelLib.channelType2String(byte1, byte2) + local s = "" + + if (byte2==0) then return s end; + + if (bit32.band(byte2,CH_TYPE.AIL)>0) then s=s.."Ail" end + if (bit32.band(byte2,CH_TYPE.ELE)>0) then s=s.."Ele" end + if (bit32.band(byte2,CH_TYPE.RUD)>0) then s=s.."Rud" end + if (bit32.band(byte2,CH_TYPE.THR)>0) then s=s.."Thr" end + + if (bit32.band(byte2,CH_TYPE.REVERSE)>0) then s=s.."-" end + + if (bit32.band(byte2,CH_TYPE.SLAVE)>0) then s=s.." Slv" end + + if (byte1==CH_MIX_TYPE.MIX_NORM) then s=s.." " + elseif (byte1==CH_MIX_TYPE.MIX_AIL) then s=s.." M_Ail" + elseif (byte1==CH_MIX_TYPE.MIX_ELE) then s=s.." M_Ele" + elseif (byte1==CH_MIX_TYPE.MIX_RUD) then s=s.." M_Rud" + elseif (byte1==CH_MIX_TYPE.MIX_RUD_REV) then s=s.." M_Rud-" + elseif (byte1==CH_MIX_TYPE.MIX_ELE_REV) then s=s.." M_Ele-" + elseif (byte1==CH_MIX_TYPE.MIX_AIL_REV) then s=s.." M_Ail-" + elseif (byte1==CH_MIX_TYPE.MIX_NORM_REV) then s=s.." M-" + end + + return s; +end + + +------------------------------------------------------------------------------------------------- +-- Read the model information from OTX/ETX + +local function getModuleChannelOrder(num) + --Determine fist 4 channels order + local channel_names={} + local stick_names = {[0]= "R", "E", "T", "A" } + local ch_order=num + if (ch_order == -1) then + channel_names[0] = stick_names[3] + channel_names[1] = stick_names[1] + channel_names[2] = stick_names[2] + channel_names[3] = stick_names[0] + else + channel_names[bit32.band(ch_order,3)] = stick_names[3] + ch_order = math.floor(ch_order/4) + channel_names[bit32.band(ch_order,3)] = stick_names[1] + ch_order = math.floor(ch_order/4) + channel_names[bit32.band(ch_order,3)] = stick_names[2] + ch_order = math.floor(ch_order/4) + channel_names[bit32.band(ch_order,3)] = stick_names[0] + end + + local s = "" + for i=0,3 do + s=s..channel_names[i] + end + return s +end + +function ModelLib.ReadTxModelData() + local TRANSLATE_AETR_TO_TAER=false + local table = model.getInfo() -- Get the model name + MODEL.modelName = table.name + + local module = model.getModule(0) -- Internal + if (module==nil or module.Type~=6) then module = model.getModule(1) end -- External + if (module~=nil) then + if (module.Type==6 ) then -- MULTI-MODULE + local chOrder = module.channelsOrder + local s = getModuleChannelOrder(chOrder) + Log.LOG_write("MultiChannel Ch Order: [%s] %s\n",chOrder,s) + + if (s=="AETR") then TRANSLATE_AETR_TO_TAER=true + else TRANSLATE_AETR_TO_TAER=false + end + end + end + + Log.LOG_write("MODEL NAME = %s\n",MODEL.modelName) + + -- Read Ch1 to Ch10 + local i= 0 + for i = 0, TX_CHANNELS-1 do + local ch = model.getOutput(i) -- Zero base + if (ch~=nil) then + MODEL.modelOutputChannel[i] = ch + if (string.len(ch.name)==0) then + ch.formatCh = string.format("TX:Ch%i",i+1) + else + ch.formatCh = string.format("TX:Ch%i/%s",i+1,ch.name or "--") + end + end + end + + -- Translate AETR to TAER + -- TODO: Check if there is a way to know how to the TX is configured, since if it is + -- already TAER, is not needed + + if (TRANSLATE_AETR_TO_TAER) then + Log.LOG_write("Applying AETR -> TAER translation\n") + local ail = MODEL.modelOutputChannel[0] + local elv = MODEL.modelOutputChannel[1] + local thr = MODEL.modelOutputChannel[2] + + MODEL.modelOutputChannel[0] = thr + MODEL.modelOutputChannel[1] = ail + MODEL.modelOutputChannel[2] = elv + end + + -- Create the Port Text to be used + Log.LOG_write("Ports/Channels:\n") + for i = 0, TX_CHANNELS-1 do + local ch = MODEL.modelOutputChannel[i] + if (ch~=nil) then + MODEL.TX_CH_TEXT[i] = ch.formatCh + MODEL.PORT_TEXT[i] = string.format("P%i (%s) ",i+1,MODEL.TX_CH_TEXT[i]) + + Log.LOG_write("Port%d %s [%d,%d] Rev=%d, Off=%d, ppmC=%d, syn=%d\n",i+1,MODEL.TX_CH_TEXT[i],math.floor(ch.min/10),math.floor(ch.max/10), ch.revert, ch.offset, ch.ppmCenter, ch.symetrical) + end + end +end + +----------------------- FILE MANAGEMENT --------------------------------------------- +-- Create a fairly unique name for a model..combination of name and a hash +-- TODO: Check with ETX why we can't get the filename used to store the model info +-- Improvement request?? + +function ModelLib.hashName(mName) + local c=10000; + + local prefix = string.gsub(mName,"%.","_") -- Change any "." to "_" + prefix = string.gsub(prefix,"% ","_") -- Change any space to "_" + prefix = string.sub(prefix,1,5) -- Take the first 5 characters + + -- Simple Hash of the Model Name adding each character + for i = 1, #mName do + local ch = string.byte(mName,i,i) + c=c+ch + end + + return (prefix .. c) -- Return Prefix + Hash +end + +-- Load Menu Data from a file +function ModelLib.ST_LoadFileData() + local fname = ModelLib.hashName(MODEL.modelName)..".txt" + + -- Clear Menu Data + for i = 0, MEMU_VAR.DATA_END do + MENU_DATA[i]=nil + end + + print("Loading File:"..fname) + + local dataFile = io.open(DATA_PATH .. "/".. fname, "r") -- read File + -- cannot read file??? + if (dataFile==nil) then return 0 end + + local line = io.read(dataFile, 5000) + io.close(dataFile) + + if #line == 0 then return 0 end -- No data?? + + -- Process the input, each line is "Var_Id : Value" format + -- Store it into MANU_DATA + local i=0 + for k, v in string.gmatch(line, "(%d+):(%d+)") do + --print(string.format("Read MENU_DATA[%d]:[%d]",k, v)) + MENU_DATA[k+0]=v+0 -- do aritmentic to convert string to number + i=i+1 + end + + local currAircraftType = MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE] + local currWingType = MENU_DATA[MEMU_VAR.WING_TYPE] + local currTailType = MENU_DATA[MEMU_VAR.TAIL_TYPE] + + print("Validation") + print(string.format("AIRCRAFT_TYPE(%d)=%s", MEMU_VAR.AIRCRAFT_TYPE,aircraft_type_text[currAircraftType])) + print(string.format("WING_TYPE(%d)=%s", MEMU_VAR.WING_TYPE, wing_type_text[currWingType])) + print(string.format("TAIL_TYPE(%d)=%s", MEMU_VAR.TAIL_TYPE, tail_type_text[currTailType])) + + ModelLib.printChannelSummary() + ModelLib.printServoReverseInfo() + + -- Return 0 if no lines processed, 1 otherwise + if (i > 0) then return 1 else return 0 end +end + +-- Saves MENU_DATA to a file +function ModelLib.ST_SaveFileData() + local fname = ModelLib.hashName(MODEL.modelName)..".txt" + + print("Saving File:"..fname) + local dataFile = assert(io.open(DATA_PATH .. "/" .. fname, "w"),"Please create "..DATA_PATH.." folder") -- write File + + -- Foreach MENU_DATA with a value write Var_Id:Value into file + for i = 0, MEMU_VAR.DATA_END do + if (MENU_DATA[i]~=nil) then + --print(string.format("Write MENU_DATA[%s] : %s",i,MENU_DATA[i])) + io.write(dataFile,string.format("%s:%s\n",i,MENU_DATA[i])) + end + end + io.close(dataFile) +end + +-- This Creates the Servo Settings that will be used to pass to +-- Forward programming +function ModelLib.CreateDSMPortChannelInfo() + local function ApplyWingMixA(b2) + -- ELEVON + if (b2==CH_TYPE.AIL+CH_TYPE.ELE) then return CH_MIX_TYPE.MIX_ELE end; -- 0x03 + if (b2==CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE) then return CH_MIX_TYPE.MIX_NORM end; -- 0x83 + end + + local function ApplyWingMixB(b2) + -- ELEVON + if (b2==CH_TYPE.AIL+CH_TYPE.ELE) then return CH_MIX_TYPE.MIX_NORM end; -- 0x03 + if (b2==CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE) then return CH_MIX_TYPE.MIX_ELE end; -- 0x83 + end + + local function ApplyTailMixA(b2) + -- VTAIL + -- Default normal/reverse behaviour + if (b2==CH_TYPE.RUD+CH_TYPE.ELE) then return CH_MIX_TYPE.MIX_NORM end; -- 0x06 + if (b2==CH_TYPE.RUD+CH_TYPE.ELE+CH_TYPE.SLAVE) then return CH_MIX_TYPE.MIX_ELE end; -- 0x86 + + --TAILERON + -- Default normal/reverse behaviour + if (b2==CH_TYPE.AIL+CH_TYPE.ELE) then return CH_MIX_TYPE.MIX_NORM end; -- 0x03 + if (b2==CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE) then return CH_MIX_TYPE.MIX_AIL end; -- 0x83 + end + + local function ApplyTailMixB(b2) + -- VTAIL + -- Default normal/reverse behaviour + if (b2==CH_TYPE.RUD+CH_TYPE.ELE) then return CH_MIX_TYPE.MIX_NORM end; -- 0x06 + if (b2==CH_TYPE.RUD+CH_TYPE.ELE+CH_TYPE.SLAVE) then return CH_MIX_TYPE.MIX_RUD end; -- 0x86 + + --TAILERON + if (b2==CH_TYPE.AIL+CH_TYPE.ELE) then return CH_MIX_TYPE.MIX_AIL end; -- 0x03 + if (b2==CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE) then return CH_MIX_TYPE.MIX_NORM end; -- 0x83 + end + + local function reverseMix(b) + if (b==CH_MIX_TYPE.MIX_NORM) then return CH_MIX_TYPE.MIX_NORM_REV end; + if (b==CH_MIX_TYPE.MIX_AIL) then return CH_MIX_TYPE.MIX_AIL_REV end; + if (b==CH_MIX_TYPE.MIX_ELE) then return CH_MIX_TYPE.MIX_ELE_REV end; + if (b==CH_MIX_TYPE.MIX_RUD) then return CH_MIX_TYPE.MIX_RUD_REV end; + return b + end + + + + local DSM_ChannelInfo = MODEL.DSM_ChannelInfo + + for i=0, TX_CHANNELS-1 do + DSM_ChannelInfo[i] = {[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE} -- Initialize with no special function + end + + local aircraftType = MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE] + local wingType = MENU_DATA[MEMU_VAR.WING_TYPE] + local tailType = MENU_DATA[MEMU_VAR.TAIL_TYPE] + + local thrCh = MENU_DATA[MEMU_VAR.CH_THR] + local lAilCh = MENU_DATA[MEMU_VAR.CH_L_AIL] + local rAilCh = MENU_DATA[MEMU_VAR.CH_R_AIL] + local lflapCh = MENU_DATA[MEMU_VAR.CH_L_FLP] + local rflapCh = MENU_DATA[MEMU_VAR.CH_R_FLP] + + local lElevCh = MENU_DATA[MEMU_VAR.CH_L_ELE] + local rElevCh = MENU_DATA[MEMU_VAR.CH_R_ELE] + + local lRudCh = MENU_DATA[MEMU_VAR.CH_L_RUD] + local rRudCh = MENU_DATA[MEMU_VAR.CH_R_RUD] + + -- Channels in menu vars are Zero base, Channel info is 1 based + + -- THR + if (thrCh~=nil and thrCh < 10) then DSM_ChannelInfo[thrCh][1]= CH_TYPE.THR end + + -- AIL (Left and Right) + if (lAilCh~=nil) then DSM_ChannelInfo[lAilCh][1] = CH_TYPE.AIL end + if (rAilCh~=nil) then DSM_ChannelInfo[rAilCh][1] = CH_TYPE.AIL+CH_TYPE.SLAVE end + -- ELE (Left and Right) + if (lElevCh~=nil) then DSM_ChannelInfo[lElevCh][1] = CH_TYPE.ELE end + if (rElevCh~=nil) then DSM_ChannelInfo[rElevCh][1] = CH_TYPE.ELE+CH_TYPE.SLAVE end + -- RUD (Left and Right) + if (lRudCh~=nil) then DSM_ChannelInfo[lRudCh][1] = CH_TYPE.RUD end + if (rRudCh~=nil) then DSM_ChannelInfo[rRudCh][1] = CH_TYPE.RUD+CH_TYPE.SLAVE end + + -- VTAIL: RUD + ELE + if (tailType==TAIL_TYPE.VTAIL_A or tailType==TAIL_TYPE.VTAIL_B) then + DSM_ChannelInfo[lElevCh][1] = CH_TYPE.RUD+CH_TYPE.ELE + DSM_ChannelInfo[rElevCh][1] = CH_TYPE.RUD+CH_TYPE.ELE+CH_TYPE.SLAVE + end + + -- TAILERRON: 2-ELE + AIL + if (tailType==TAIL_TYPE.TRAILERON_A or tailType==TAIL_TYPE.TRAILERON_A_R2 or + tailType==TAIL_TYPE.TRAILERON_B or tailType==TAIL_TYPE.TRAILERON_B_R2) then + DSM_ChannelInfo[lElevCh][1] = CH_TYPE.AIL+CH_TYPE.ELE + DSM_ChannelInfo[rElevCh][1] = CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE + end + + ---- ELEVON : AIL + ELE + if (wingType==WING_TYPE.ELEVON_A or wingType==WING_TYPE.ELEVON_B) then + DSM_ChannelInfo[lAilCh][1] = CH_TYPE.AIL+CH_TYPE.ELE + DSM_ChannelInfo[rAilCh][1] = CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE + end + + ------MIXES --------- + + -- TAIL Mixes (Elevator and VTail) + if (tailType==TAIL_TYPE.VTAIL_A or tailType==TAIL_TYPE.TRAILERON_A or tailType==TAIL_TYPE.TRAILERON_A_R2) then + DSM_ChannelInfo[lElevCh][0] = ApplyTailMixA(DSM_ChannelInfo[lElevCh][1]) + DSM_ChannelInfo[rElevCh][0] = ApplyTailMixA(DSM_ChannelInfo[rElevCh][1]) + elseif (tailType==TAIL_TYPE.VTAIL_B or tailType==TAIL_TYPE.TRAILERON_B or tailType==TAIL_TYPE.TRAILERON_B_R2) then + DSM_ChannelInfo[lElevCh][0] = ApplyTailMixA(DSM_ChannelInfo[lElevCh][1]) + DSM_ChannelInfo[rElevCh][0] = ApplyTailMixA(DSM_ChannelInfo[rElevCh][1]) + end + + ---- Wing Mixes + if (wingType==WING_TYPE.ELEVON_A) then + DSM_ChannelInfo[lAilCh][0] = ApplyWingMixA(DSM_ChannelInfo[lAilCh][1]) + DSM_ChannelInfo[rAilCh][0] = ApplyWingMixA(DSM_ChannelInfo[rAilCh][1]) + elseif (wingType==WING_TYPE.ELEVON_B) then + DSM_ChannelInfo[lAilCh][0] = ApplyWingMixB(DSM_ChannelInfo[lAilCh][1]) + DSM_ChannelInfo[rAilCh][0] = ApplyWingMixB(DSM_ChannelInfo[rAilCh][1]) + end + + -- Apply Gyro Reverse as needed for each channel as long as it is used + for i=0, TX_CHANNELS-1 do + if (MENU_DATA[MEMU_VAR.PORT_BASE+i]==CH_MODE_TYPE.REVERSE and DSM_ChannelInfo[i][1]>0) then + DSM_ChannelInfo[i][0]=reverseMix(DSM_ChannelInfo[i][0]) + DSM_ChannelInfo[i][1]=DSM_ChannelInfo[i][1]+CH_TYPE.REVERSE + end + end + + -- Show how it looks + for i=0, 9 do + local b1,b2 = DSM_ChannelInfo[i][0], DSM_ChannelInfo[i][1] + print(string.format("%s (%02X %02X) %s", MODEL.PORT_TEXT[i], + b1, b2, ModelLib.channelType2String(b1,b2))) + end + + MODEL.AirWingTailDesc = string.format("Aircraft(%s) Wing(%s) Tail(%s)",aircraft_type_text[aircraftType],wing_type_text[wingType],tail_type_text[tailType]) +end + +function ModelLib.ST_PlaneWingInit(wingType) + print("Change Plane WingType:"..wing_type_text[wingType]) + + MENU_DATA[MEMU_VAR.WING_TYPE] = wingType + + -- Clear all Wing Data + MENU_DATA[MEMU_VAR.CH_L_AIL] = nil + MENU_DATA[MEMU_VAR.CH_R_AIL] = nil + MENU_DATA[MEMU_VAR.CH_L_FLP] = nil + MENU_DATA[MEMU_VAR.CH_R_FLP] = nil + + MENU_DATA[MEMU_VAR.CH_THR] = PORT.PORT1 + + -- Default Channel Assisgments for each Wing type + + if (wingType==WING_TYPE.AIL_1) then + MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT2 + elseif (wingType==WING_TYPE.AIL_2) then + MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT6 + MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2 + elseif (wingType==WING_TYPE.FLAPERON) then + MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT6 + MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2 + elseif (wingType==WING_TYPE.AIL_1_FLP_1) then + MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT2 + MENU_DATA[MEMU_VAR.CH_L_FLP] = PORT.PORT6 + elseif (wingType==WING_TYPE.AIL_2_FLP_1) then + MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT6 + MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2 + MENU_DATA[MEMU_VAR.CH_L_FLP] = PORT.PORT5 + elseif (wingType==WING_TYPE.AIL_2_FLP_2) then + MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT6 + MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2 + MENU_DATA[MEMU_VAR.CH_R_FLP] = PORT.PORT5 + MENU_DATA[MEMU_VAR.CH_L_FLP] = PORT.PORT7 + elseif (wingType==WING_TYPE.ELEVON_A) then + MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT2 + MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT3 + elseif (wingType==WING_TYPE.ELEVON_B) then + MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT3 + MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2 + else -- Assume normal + print("ERROR: Invalid Wing Type") + end + + + ModelLib.printChannelSummary() +end + +function ModelLib.ST_PlaneTailInit(tailType) + if (MENU_DATA[MEMU_VAR.WING_TYPE]==WING_TYPE.ELEVON_A or + MENU_DATA[MEMU_VAR.WING_TYPE]==WING_TYPE.ELEVON_B) then + tailType = TAIL_TYPE.RUD_1 -- Delta only have ruder + end + + print("Change Plane Tail Type:"..tail_type_text[tailType]) + + -- Clear all data for Tail + MENU_DATA[MEMU_VAR.TAIL_TYPE] = tailType + MENU_DATA[MEMU_VAR.CH_L_ELE] = nil + MENU_DATA[MEMU_VAR.CH_R_ELE] = nil + MENU_DATA[MEMU_VAR.CH_L_RUD] = nil + MENU_DATA[MEMU_VAR.CH_R_RUD] = nil + + -- Setup Channels for different Tail types + if (tailType == TAIL_TYPE.RUD_1) then + MENU_DATA[MEMU_VAR.CH_L_RUD] = PORT.PORT4 + elseif (tailType == TAIL_TYPE.RUD_1_ELEV_1) then + MENU_DATA[MEMU_VAR.CH_L_ELE] = PORT.PORT3 + MENU_DATA[MEMU_VAR.CH_L_RUD] = PORT.PORT4 + elseif (tailType == TAIL_TYPE.RUD_1_ELEV_2) then + MENU_DATA[MEMU_VAR.CH_L_ELE] = PORT.PORT5 + MENU_DATA[MEMU_VAR.CH_R_ELE] = PORT.PORT3 + MENU_DATA[MEMU_VAR.CH_L_RUD] = PORT.PORT4 + elseif (tailType == TAIL_TYPE.RUD_2_ELEV_1) then + MENU_DATA[MEMU_VAR.CH_L_ELE] = PORT.PORT3 + MENU_DATA[MEMU_VAR.CH_L_RUD] = PORT.PORT4 + MENU_DATA[MEMU_VAR.CH_R_RUD] = PORT.PORT5 + elseif (tailType == TAIL_TYPE.RUD_2_ELEV_2) then + MENU_DATA[MEMU_VAR.CH_L_ELE] = PORT.PORT5 + MENU_DATA[MEMU_VAR.CH_R_ELE] = PORT.PORT3 + MENU_DATA[MEMU_VAR.CH_L_RUD] = PORT.PORT4 + MENU_DATA[MEMU_VAR.CH_R_RUD] = PORT.PORT6 + elseif (tailType == TAIL_TYPE.VTAIL_A or tailType == TAIL_TYPE.VTAIL_B) then + MENU_DATA[MEMU_VAR.CH_L_ELE] = PORT.PORT4 + MENU_DATA[MEMU_VAR.CH_R_ELE] = PORT.PORT3 + elseif (tailType == TAIL_TYPE.TRAILERON_A or tailType==TAIL_TYPE.TRAILERON_A_R2 or + tailType == TAIL_TYPE.TRAILERON_B or tailType==TAIL_TYPE.TRAILERON_B_R2) then + MENU_DATA[MEMU_VAR.CH_L_RUD] = PORT.PORT4 + MENU_DATA[MEMU_VAR.CH_L_ELE] = PORT.PORT5 + MENU_DATA[MEMU_VAR.CH_R_ELE] = PORT.PORT3 + else -- Assume Normal + print("ERROR:invalid Tail Type") + end + + if (tailType == TAIL_TYPE.TRAILERON_A_R2 or tailType==TAIL_TYPE.TRAILERON_B_R2) then + MENU_DATA[MEMU_VAR.CH_R_RUD] = PORT.PORT7 + end + + ModelLib.printChannelSummary() +end + +function ModelLib.ST_GliderWingInit(wingType) + print("Change Glider WingType:"..wing_type_text[wingType]) + + MENU_DATA[MEMU_VAR.WING_TYPE] = wingType + + -- Clear all Wing Data + MENU_DATA[MEMU_VAR.CH_L_AIL] = nil + MENU_DATA[MEMU_VAR.CH_R_AIL] = nil + MENU_DATA[MEMU_VAR.CH_L_FLP] = nil + MENU_DATA[MEMU_VAR.CH_R_FLP] = nil + MENU_DATA[MEMU_VAR.CH_THR] = PORT.PORT6 + + -- Default Channel Assisgments for each Wing type + + if (wingType==WING_TYPE.AIL_1) then + MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT1 + elseif (wingType==WING_TYPE.AIL_2) then + MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT1 + MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2 + elseif (wingType==WING_TYPE.AIL_2_FLP_1) then + MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT1 + MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2 + MENU_DATA[MEMU_VAR.CH_L_FLP] = PORT.PORT5 + elseif (wingType==WING_TYPE.AIL_2_FLP_2) then + MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT1 + MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2 + MENU_DATA[MEMU_VAR.CH_L_FLP] = PORT.PORT5 + MENU_DATA[MEMU_VAR.CH_R_FLP] = PORT.PORT6 + MENU_DATA[MEMU_VAR.CH_THR] = PORT.PORT7 + elseif (wingType==WING_TYPE.ELEVON_A) then + MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT1 + MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2 + elseif (wingType==WING_TYPE.ELEVON_B) then + MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT2 + MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT1 + else -- Assume normal + print("ERROR: Invalid Wing Type") + end + + ModelLib.printChannelSummary() +end + +function ModelLib.ST_GliderTailInit(tailType) + if (MENU_DATA[MEMU_VAR.WING_TYPE]==WING_TYPE.ELEVON_A) then + tailType = TAIL_TYPE.RUD_1 -- Delta only have ruder + end + + print("Change Glider Tail Type:"..tail_type_text[tailType]) + + -- Clear all data for Tail + MENU_DATA[MEMU_VAR.TAIL_TYPE] = tailType + MENU_DATA[MEMU_VAR.CH_L_ELE] = nil + MENU_DATA[MEMU_VAR.CH_R_ELE] = nil + MENU_DATA[MEMU_VAR.CH_L_RUD] = nil + MENU_DATA[MEMU_VAR.CH_R_RUD] = nil + + -- Setup Channels for different Tail types + if (tailType == TAIL_TYPE.RUD_1) then + MENU_DATA[MEMU_VAR.CH_L_RUD] = PORT.PORT4 + elseif (tailType == TAIL_TYPE.RUD_1_ELEV_1) then + MENU_DATA[MEMU_VAR.CH_L_ELE] = PORT.PORT3 + MENU_DATA[MEMU_VAR.CH_L_RUD] = PORT.PORT4 + elseif (tailType == TAIL_TYPE.VTAIL_A) then + MENU_DATA[MEMU_VAR.CH_L_ELE] = PORT.PORT4 + MENU_DATA[MEMU_VAR.CH_R_ELE] = PORT.PORT3 + elseif (tailType == TAIL_TYPE.VTAIL_B) then + MENU_DATA[MEMU_VAR.CH_L_ELE] = PORT.PORT3 + MENU_DATA[MEMU_VAR.CH_R_ELE] = PORT.PORT4 + else -- Assume Normal + print("ERROR: Invalid Tail Type") + end + + ModelLib.printChannelSummary() +end + + +function ModelLib.ST_AircraftInit(aircraftType) + MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE] = aircraftType + + print("Change Aircraft:".. aircraft_type_text[aircraftType]) + + -- Setup Default Aircraft Wing/Tail + if (aircraftType==AIRCRAFT_TYPE.PLANE) then + ModelLib.ST_PlaneWingInit(WING_TYPE.AIL_1) + ModelLib.ST_PlaneTailInit(TAIL_TYPE.RUD_1_ELEV_1) + elseif (aircraftType==AIRCRAFT_TYPE.GLIDER) then + ModelLib.ST_GliderWingInit(WING_TYPE.AIL_1) + ModelLib.ST_GliderTailInit(TAIL_TYPE.RUD_1_ELEV_1) + else + ModelLib.ST_PlaneWingInit(WING_TYPE.AIL_1) + ModelLib.ST_PlaneTailInit(TAIL_TYPE.RUD_1_ELEV_1) + end + + +end + + +-- Setup Initial Default Data for the Menus +function ModelLib.ST_Default_Data() + print("Initializing Menu DATA") + ModelLib.ST_AircraftInit(AIRCRAFT_TYPE.PLANE) + + print("Initializing Servo Reverse from TX output settings") + + MENU_DATA[MEMU_VAR.PORT1_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT1].revert + MENU_DATA[MEMU_VAR.PORT2_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT2].revert + MENU_DATA[MEMU_VAR.PORT3_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT3].revert + MENU_DATA[MEMU_VAR.PORT4_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT4].revert + MENU_DATA[MEMU_VAR.PORT5_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT5].revert + MENU_DATA[MEMU_VAR.PORT6_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT6].revert + MENU_DATA[MEMU_VAR.PORT7_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT7].revert + MENU_DATA[MEMU_VAR.PORT8_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT8].revert + MENU_DATA[MEMU_VAR.PORT9_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT9].revert + MENU_DATA[MEMU_VAR.PORT10_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT10].revert + + ModelLib.printServoReverseInfo() + +end + + +ModelLib.TX_CHANNELS = TX_CHANNELS +ModelLib.MODEL = MODEL +ModelLib.CH_TYPE = CH_TYPE +ModelLib.CH_MODE_TYPE = CH_MODE_TYPE +ModelLib.AIRCRAFT_TYPE = AIRCRAFT_TYPE +ModelLib.WING_TYPE = WING_TYPE +ModelLib.TAIL_TYPE = TAIL_TYPE +ModelLib.MENU_DATA = MENU_DATA +ModelLib.MEMU_VAR = MEMU_VAR +ModelLib.PORT = PORT +ModelLib.DATA_PATH = DATA_PATH + +ModelLib.aircraft_type_text = aircraft_type_text +ModelLib.wing_type_text = wing_type_text +ModelLib.tail_type_text = tail_type_text + + +return ModelLib + diff --git a/SCRIPTS/TOOLS/DSMLIB/DsmSetupMenuLib.lua b/SCRIPTS/TOOLS/DSMLIB/DsmSetupMenuLib.lua new file mode 100644 index 0000000..7c998fa --- /dev/null +++ b/SCRIPTS/TOOLS/DSMLIB/DsmSetupMenuLib.lua @@ -0,0 +1,467 @@ +---- ######################################################################### +---- # # +---- # 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. # +---- # # +---- ######################################################################### + +------------------------------------------------------------------------------ +-- This scrip does the airplane Setup similar to how a a Spektrum radio does +-- it. You can select the plane type, the Wing type, etc. +-- This settings are needed for ForwardProgramming to send the TX aircraft +-- configuration to the RX when in Initial Setup +-- Author: Francisco Arzu +------------------------------------------------------------------------------ + +local Log, menuLib, modelLib, DEBUG_ON, SIMULATION_ON = ... -- Get DebugON from parameters +local SETUP_LIB_VERSION = "0.56" + +local DATA_PATH = modelLib.DATA_PATH + +local PHASE = menuLib.PHASE +local LINE_TYPE = menuLib.LINE_TYPE + +local MODEL = modelLib.MODEL + +local AIRCRAFT_TYPE = modelLib.AIRCRAFT_TYPE +local WING_TYPE = modelLib.WING_TYPE +local TAIL_TYPE = modelLib.TAIL_TYPE +local CH_MODE_TYPE = modelLib.CH_MODE_TYPE +local PORT = modelLib.PORT +local MEMU_VAR = modelLib.MEMU_VAR +local MENU_DATA = modelLib.MENU_DATA + +local SetupLib = {} + +local lastGoodMenu=0 + +------------------- Model Setup Helper functions ---------------------- +local currAircraftType = -1 -- Current AircraftType selected, and to detect change +local currTailType = -1 -- Current WingType selected, and to detect change +local currWingType = -1 -- Current TailType selected, and to detect change + +local menuDataChanged = false -- Flag to notify if any data has changed + + +local function tailTypeCompatible(a,b) + + local function normalize(tt) + if (tt==TAIL_TYPE.TRAILERON_A or tt==TAIL_TYPE.TRAILERON_B) then + return TAIL_TYPE.TRAILERON_A + elseif (tt==TAIL_TYPE.TRAILERON_A_R2 or tt==TAIL_TYPE.TRAILERON_B_R2) then + return TAIL_TYPE.TRAILERON_A_R2 + elseif (tt==TAIL_TYPE.VTAIL_A or tt==TAIL_TYPE.VTAIL_B) then + return TAIL_TYPE.VTAIL_A + else + return tt + end + end + + return (normalize(a)==normalize(b)) + end + + +-- Creates the menus to Render with the GUI +local function ST_LoadMenu(menuId) + local ctx = menuLib.DSM_Context + + local function portUse(p) + local out = "" + if p==MENU_DATA[MEMU_VAR.CH_THR] then out = "Thr" + elseif p == MENU_DATA[MEMU_VAR.CH_L_AIL] then + out=(MENU_DATA[MEMU_VAR.CH_R_AIL] and "Ail_L") or "Ail" + elseif p == MENU_DATA[MEMU_VAR.CH_R_AIL] then out="Ail_R" + elseif p == MENU_DATA[MEMU_VAR.CH_L_ELE] then + out=(MENU_DATA[MEMU_VAR.CH_R_ELE] and "Ele_L") or "Ele" + elseif p == MENU_DATA[MEMU_VAR.CH_R_ELE] then out="Ele_R" + elseif p == MENU_DATA[MEMU_VAR.CH_L_RUD] then + out=(MENU_DATA[MEMU_VAR.CH_R_RUD] and "Rud_L") or "Rud" + elseif p == MENU_DATA[MEMU_VAR.CH_R_RUD] then out="Rud_R" + elseif p == MENU_DATA[MEMU_VAR.CH_L_FLP] then + out=(MENU_DATA[MEMU_VAR.CH_R_FLP] and "Flp_L") or "Flp" + elseif p == MENU_DATA[MEMU_VAR.CH_R_FLP] then out="Flp_R" + end + return out + end + + local function formatTXRevert(port) + local out = " " .. modelLib.channelType2String(MODEL.DSM_ChannelInfo[port][0], MODEL.DSM_ChannelInfo[port][1]); + return out + end + + local function Header(p) + return MODEL.PORT_TEXT[p].." "..portUse(p) + end + + menuLib.clearMenuLines() + + + if (menuId==0x1000) then -- MAIN MENU + ctx.Menu = { MenuId = 0x1000, Text = "Save-Exit ("..MODEL.modelName..")", PrevId = 0, NextId = 0, BackId = 0, TextId=0 } + + if (true) then + ctx.MenuLines[4] = { Type = LINE_TYPE.MENU, Text="Save Changes", TextId = 0, ValId = 0x1005 } + ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, Text="Discard Changes", TextId = 0, ValId = 0x1006 } + ctx.SelLine = 4 + end + lastGoodMenu = menuId + elseif (menuId==0x1001) then -- MODEL SETUP + local backId = 0xFFF9 -- No changes, just exit + local title = "Model Setup ("..MODEL.modelName..")" + if (menuDataChanged) then + backId = 0x1000 -- Go to Save menu + title = title.." *" + end + ctx.Menu = { MenuId = 0x1001, Text = title, PrevId = 0, NextId = 0, BackId = backId, TextId=0 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, Text = "Aircraft Type Setup", ValId = 0x1010,TextId=0 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, Text = "Wing & Tail Channels ", ValId = 0x1020, TextId=0 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, Text = "Gyro Channel Reverse", ValId = 0x1030, TextId=0 } + ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, Text = "WARNING: Changing of Aircraft or Wing will", ValId = 0x1001, TextId=0 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, Text = "delete previous Channel/Port assigments.", ValId = 0x1001, TextId=0 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1005) then + modelLib.printChannelSummary() + modelLib.ST_SaveFileData() + menuDataChanged = false + + + local msg1 = "Data saved to: " + local msg2 = " "..DATA_PATH.."/"..modelLib.hashName(MODEL.modelName)..".txt" + + ctx.Menu = { MenuId = 0x1005, Text = "Config Saved", PrevId = 0, NextId = 0, BackId = 0, TextId=0 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, Text=msg1, TextId = 0, ValId = 0x1005 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, Text=msg2, TextId = 0, ValId = 0x1005 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, Text="Complete", TextId = 0, ValId = 0xFFF9 } + ctx.SelLine = 6 + lastGoodMenu = menuId + elseif (menuId==0x1006) then + modelLib.ST_LoadFileData() + menuDataChanged = false + currAircraftType = MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE] + currWingType = MENU_DATA[MEMU_VAR.WING_TYPE] + currTailType = MENU_DATA[MEMU_VAR.TAIL_TYPE] + + local msg1 = "Data restored from: " + local msg2 = " "..DATA_PATH.."/"..modelLib.hashName(MODEL.modelName)..".txt" + + ctx.Menu = { MenuId = 0x1006, Text = "Discart Changes", PrevId = 0, NextId = 0, BackId = 0, TextId=0 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, Text=msg1, TextId = 0, ValId = 0x1006 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, Text=msg2, TextId = 0, ValId = 0x1006 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, Text="Complete", TextId = 0, ValId = 0xFFF9 } + ctx.SelLine = 6 + lastGoodMenu = menuId + elseif (menuId==0x1010) then + modelLib.printChannelSummary() + ctx.Menu = { MenuId = 0x1010, Text = "Aircraft Type", PrevId = 0, NextId = 0x1011, BackId = 0x1001, TextId=0 } + ctx.MenuLines[5] = { Type = LINE_TYPE.LIST_MENU_NC, Text="Aircraft Type", TextId = 0, ValId = MEMU_VAR.AIRCRAFT_TYPE, Min=50, Max=53, Def=50, Val=MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE] } + ctx.SelLine = 5 + lastGoodMenu = menuId + elseif (menuId==0x1011) then + ctx.Menu = { MenuId = 0x1011, Text = "Model Type:"..modelLib.aircraft_type_text[currAircraftType], PrevId = 0, NextId = 0x1020, BackId = 0x1010, TextId=0 } + ctx.MenuLines[5] = { Type = LINE_TYPE.LIST_MENU_NC, Text="Wing Type", TextId = 0, ValId = MEMU_VAR.WING_TYPE, Min=100, Max=107, Def=100, Val=MENU_DATA[MEMU_VAR.WING_TYPE] } + ctx.MenuLines[6] = { Type = LINE_TYPE.LIST_MENU_NC, Text="Tail Type", TextId = 0, ValId = MEMU_VAR.TAIL_TYPE, Min=200, Max=210, Def=200, Val=MENU_DATA[MEMU_VAR.TAIL_TYPE] } + ctx.SelLine = 5 + lastGoodMenu = menuId + elseif (menuId==0x1020) then + ------ WING SETUP ------- + local thr = MENU_DATA[MEMU_VAR.CH_THR] + local leftAil = MENU_DATA[MEMU_VAR.CH_L_AIL] + local rightAil = MENU_DATA[MEMU_VAR.CH_R_AIL] + local leftFlap = MENU_DATA[MEMU_VAR.CH_L_FLP] + local rightFlap = MENU_DATA[MEMU_VAR.CH_R_FLP] + + local thrText = "Thr" + local leftAilText = "Left Aileron" + local rightAilText = "Right Aileron" + local leftFlapText = "Left Flap" + local rightFlapText = "Right Flap" + + if (rightAil==nil) then leftAilText = "Aileron" end + if (rightFlap==nil) then leftFlapText = "Flap" end + + local title = modelLib.aircraft_type_text[currAircraftType].." Wing:"..modelLib.wing_type_text[currWingType] + + ctx.Menu = { MenuId = 0x1020, Text = title, PrevId = 0, NextId = 0x1021, BackId = 0x1011, TextId=0 } + + ctx.MenuLines[0] = { Type = LINE_TYPE.LIST_MENU_NC, Text=thrText, TextId = 0, ValId = MEMU_VAR.CH_THR, Min=0, Max=10, Def=0, Val= thr } + + ctx.MenuLines[2] = { Type = LINE_TYPE.LIST_MENU_NC, Text=leftAilText, TextId = 0, ValId = MEMU_VAR.CH_L_AIL, Min=0, Max=9, Def=0, Val= leftAil } + + if (rightAil~=nil) then + ctx.MenuLines[3] = { Type = LINE_TYPE.LIST_MENU_NC, Text=rightAilText, TextId = 0, ValId = MEMU_VAR.CH_R_AIL, Min=0, Max=9, Def=0, Val= rightAil } + end + + if (leftFlap~=nil) then + ctx.MenuLines[4] = { Type = LINE_TYPE.LIST_MENU_NC, Text=leftFlapText, TextId = 0, ValId = MEMU_VAR.CH_L_FLP, Min=0, Max=9, Def=0, Val= leftFlap } + end + if (rightFlap~=nil) then + ctx.MenuLines[5] = { Type = LINE_TYPE.LIST_MENU_NC, Text=rightFlapText, TextId = 0, ValId = MEMU_VAR.CH_R_FLP, Min=0, Max=9, Def=0, Val= rightFlap } + end + + ctx.SelLine = 0 + lastGoodMenu = menuId + + elseif (menuId==0x1021) then + ------ TAIL SETUP ------- + local leftRud = MENU_DATA[MEMU_VAR.CH_L_RUD] + local rightRud = MENU_DATA[MEMU_VAR.CH_R_RUD] + local leftEle = MENU_DATA[MEMU_VAR.CH_L_ELE] + local rightEle = MENU_DATA[MEMU_VAR.CH_R_ELE] + + local leftRudText = "Left Rudder" + local rightRudText = "Right Rudder" + + local leftElvText = "Left Elevator" + local rightElvText = "Right Elevator" + + if (rightRud==nil) then leftRudText = "Rudder" end + if (rightEle==nil) then leftElvText = "Elevator" end + + local title = modelLib.aircraft_type_text[currAircraftType].." Tail:"..modelLib.tail_type_text[currTailType] + + ctx.Menu = { MenuId = 0x1021, Text = title, PrevId = 0, NextId = 0x1001, BackId = 0x1020, TextId=0 } + if (leftRud~=nil) then + ctx.MenuLines[1] = { Type = LINE_TYPE.LIST_MENU_NC, Text=leftRudText, TextId = 0, ValId = MEMU_VAR.CH_L_RUD, Min=0, Max=9, Def=0, Val= leftRud} + end + + if (rightRud~=nil) then + ctx.MenuLines[2] = { Type = LINE_TYPE.LIST_MENU_NC, Text=rightRudText, TextId = 0, ValId = MEMU_VAR.CH_R_RUD, Min=0, Max=9, Def=0, Val=rightRud } + end + + if (leftEle~=nil) then + ctx.MenuLines[4] = { Type = LINE_TYPE.LIST_MENU_NC, Text=leftElvText, TextId = 0, ValId = MEMU_VAR.CH_L_ELE, Min=0, Max=9, Def=0, Val=leftEle } + end + + if (rightEle~=nil) then + ctx.MenuLines[5] = { Type = LINE_TYPE.LIST_MENU_NC, Text=rightElvText, TextId = 0, ValId = MEMU_VAR.CH_R_ELE, Min=0, Max=9, Def=0, Val=rightEle } + end + + ctx.SelLine = 1 + lastGoodMenu = menuId + + elseif (menuId==0x1030) then + modelLib.CreateDSMPortChannelInfo() + modelLib.printChannelSummary() + + ctx.Menu = { MenuId = 0x1030, Text = "Gyro Channel Reverse (Port 1-5)", PrevId = 0, NextId = 0x1031, BackId = 0x1001, TextId=0 } + ctx.MenuLines[0] = { Type = LINE_TYPE.LIST_MENU_NC, Text=Header(PORT.PORT1), TextId = 0, ValId = MEMU_VAR.PORT1_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT1_MODE], Format = formatTXRevert(PORT.PORT1) } + ctx.MenuLines[1] = { Type = LINE_TYPE.LIST_MENU_NC, Text=Header(PORT.PORT2), TextId = 0, ValId = MEMU_VAR.PORT2_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT2_MODE], Format = formatTXRevert(PORT.PORT2) } + ctx.MenuLines[2] = { Type = LINE_TYPE.LIST_MENU_NC, Text=Header(PORT.PORT3), TextId = 0, ValId = MEMU_VAR.PORT3_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT3_MODE], Format = formatTXRevert(PORT.PORT3) } + ctx.MenuLines[3] = { Type = LINE_TYPE.LIST_MENU_NC, Text=Header(PORT.PORT4), TextId = 0, ValId = MEMU_VAR.PORT4_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT4_MODE], Format = formatTXRevert(PORT.PORT4) } + ctx.MenuLines[4] = { Type = LINE_TYPE.LIST_MENU_NC, Text=Header(PORT.PORT5), TextId = 0, ValId = MEMU_VAR.PORT5_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT5_MODE], Format = formatTXRevert(PORT.PORT5) } + + ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, Text="Only Thr/Ail/Rud/Ele. This affects AS3X/SAFE reaction dir./b", TextId = 0, ValId = 0x1030 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, Text="Any changes, use RX 'Relearn Servo Settings'/b", TextId = 0, ValId = 0x1030 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1031) then + modelLib.CreateDSMPortChannelInfo() + modelLib.printChannelSummary() + ctx.Menu = { MenuId = 0x1031, Text = "Gyro Channel Reverse (Port 6-10)", PrevId = 0x1030, NextId = 0, BackId = 0x1001, TextId=0 } + ctx.MenuLines[0] = { Type = LINE_TYPE.LIST_MENU_NC, Text=Header(PORT.PORT6), TextId = 0, ValId = MEMU_VAR.PORT6_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT6_MODE], Format = formatTXRevert(PORT.PORT6) } + ctx.MenuLines[1] = { Type = LINE_TYPE.LIST_MENU_NC, Text=Header(PORT.PORT7), TextId = 0, ValId = MEMU_VAR.PORT7_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT7_MODE], Format = formatTXRevert(PORT.PORT7) } + ctx.MenuLines[2] = { Type = LINE_TYPE.LIST_MENU_NC, Text=Header(PORT.PORT8), TextId = 0, ValId = MEMU_VAR.PORT8_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT8_MODE], Format = formatTXRevert(PORT.PORT8) } + ctx.MenuLines[3] = { Type = LINE_TYPE.LIST_MENU_NC, Text=Header(PORT.PORT9), TextId = 0, ValId = MEMU_VAR.PORT9_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT9_MODE], Format = formatTXRevert(PORT.PORT9) } + ctx.MenuLines[4] = { Type = LINE_TYPE.LIST_MENU_NC, Text=Header(PORT.PORT10), TextId = 0, ValId = MEMU_VAR.PORT10_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT10_MODE], Format = formatTXRevert(PORT.PORT10) } + + ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, Text="Only Thr/Ail/Rud/Ele. This affects AS3X/SAFE reaction dir./b", TextId = 0, ValId = 0x1031 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, Text="Any changes, use RX 'Relearn Servo Settings'/b", TextId = 0, ValId = 0x1031 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + else + print("NOT IMPLEMENTED") + ctx.Menu = { MenuId = 0x0002, Text = "NOT IMPLEMENTED", TextId = 0, PrevId = 0, NextId = 0, BackId = lastGoodMenu } + ctx.SelLine = menuLib.BACK_BUTTON + end + + menuLib.PostProcessMenu() +end + +-- ST_SendReceive +-- Main state machine for the Setup menu + +local function ST_SendReceive() + local ctx = menuLib.DSM_Context + --if (DEBUG_ON>1) then Log.LOG_write("%3.3f %s: ", menuLib.getElapsedTime(), menuLib.phase2String(ctx.Phase)) end + + if ctx.Phase == PHASE.RX_VERSION then -- request RX version + ctx.RX.Name = "MODEL SETUP" + ctx.RX.Version = SETUP_LIB_VERSION + ctx.Phase = PHASE.MENU_TITLE + ctx.Menu.MenuId = 0x01001 + + ctx.Refresh_Display = true + + + elseif ctx.Phase == PHASE.WAIT_CMD then + + elseif ctx.Phase == PHASE.MENU_TITLE then -- request menu title + ST_LoadMenu(ctx.Menu.MenuId) + ctx.Phase = PHASE.WAIT_CMD + ctx.Refresh_Display = true + + elseif ctx.Phase == PHASE.VALUE_CHANGING then -- send value + local line = ctx.MenuLines[ctx.SelLine] -- Updated Value of SELECTED line + + if (MENU_DATA[line.ValId] ~= line.Val ) then + MENU_DATA[line.ValId] = line.Val + print(string.format("MENU_DATA[%d/%s]=%d",line.ValId,line.Text, line.Val)) + menuDataChanged=true + end + + ctx.Phase = PHASE.VALUE_CHANGING_WAIT + + elseif ctx.Phase == PHASE.VALUE_CHANGING_WAIT then + local line = ctx.MenuLines[ctx.SelLine] + + elseif ctx.Phase == PHASE.VALUE_CHANGE_END then -- send value + local line = ctx.MenuLines[ctx.SelLine] -- Updated Value of SELECTED line + + -- Update the menu data from the line + if (MENU_DATA[line.ValId] ~= line.Val ) then + MENU_DATA[line.ValId] = line.Val + print(string.format("MENU_DATA[%d/%s]=%d",line.ValId,line.Text, line.Val)) + menuDataChanged=true + end + + -- Did the aircraft type change? + if (currAircraftType ~= MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE]) then + currAircraftType = MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE] + modelLib.ST_AircraftInit(currAircraftType) + currWingType = MENU_DATA[MEMU_VAR.WING_TYPE] + currTailType = MENU_DATA[MEMU_VAR.TAIL_TYPE] + end + + -- Did the Wing type change? + if (currWingType ~= MENU_DATA[MEMU_VAR.WING_TYPE]) then + if (currAircraftType==AIRCRAFT_TYPE.GLIDER) then + currWingType = MENU_DATA[MEMU_VAR.WING_TYPE] + modelLib.ST_GliderWingInit(currWingType) + else + currWingType = MENU_DATA[MEMU_VAR.WING_TYPE] + modelLib.ST_PlaneWingInit(currWingType) + + end + + -- DELTA has only RUDER + if ((currWingType==WING_TYPE.ELEVON_A or currWingType==WING_TYPE.ELEVON_B) and TAIL_TYPE~=TAIL_TYPE.RUD_1) then + MENU_DATA[MEMU_VAR.TAIL_TYPE] = TAIL_TYPE.RUD_1 + end + end + + --- Did the tail changed? + local ntt = MENU_DATA[MEMU_VAR.TAIL_TYPE] + if (currTailType ~= ntt) then + if (currAircraftType==AIRCRAFT_TYPE.GLIDER) then + currTailType = ntt + modelLib.ST_GliderTailInit(currTailType) + else + if (not tailTypeCompatible(currTailType,ntt)) then + modelLib.ST_PlaneTailInit(ntt) + end + currTailType = ntt + end + end + + ctx.Phase = PHASE.WAIT_CMD + elseif ctx.Phase == PHASE.EXIT then + ctx.Phase=PHASE.EXIT_DONE + end +end + +------------------------------------------------------------------------------------------------------------ + +-- Inital List and Image Text for this menus +local function ST_Init_Text(rxId) + menuLib.clearAllText() + + local List_Values = menuLib.List_Values + local List_Text = menuLib.List_Text + local Text = menuLib.Text + local List_Text_Img = menuLib.List_Text_Img + + -- Channel Names use the Port Text Retrived from OTX/ETX + for i = 0, 9 do List_Text[i] = MODEL.PORT_TEXT[i] end + List_Text[10]="--" + + -- Aircraft Type + List_Text[50+AIRCRAFT_TYPE.PLANE] = "Airplane"; --List_Text_Img[50+AIRCRAFT_TYPE.PLANE] = "at_plane.png|Airplane" + List_Text[50+AIRCRAFT_TYPE.GLIDER] = "Glider (Partial work)"; --List_Text_Img[50+AIRCRAFT_TYPE.GLIDER] = "at_glider.png|Glider" + List_Text[50+AIRCRAFT_TYPE.HELI] = "Helicopter (Not done)"; --List_Text_Img[50+AIRCRAFT_TYPE.HELI] = "at_heli.png|Helicopter" + List_Text[50+AIRCRAFT_TYPE.DRONE] = "Drone (not done)"; --List_Text_Img[50+AIRCRAFT_TYPE.DRONE] = "at_drone.png|Drone" + + -- Wing Types + List_Text[100+WING_TYPE.AIL_1] = "Single Ail"; List_Text_Img[100+WING_TYPE.AIL_1] = "wt_1ail.png|Single Aileron" + List_Text[100+WING_TYPE.AIL_2] = "Dual Ail"; List_Text_Img[100+WING_TYPE.AIL_2] = "wt_2ail.png|Dual Aileron" + List_Text[100+WING_TYPE.FLAPERON] = "Flaperon"; List_Text_Img[100+WING_TYPE.FLAPERON] = "wt_flaperon.png|Flaperon" + List_Text[100+WING_TYPE.AIL_1_FLP_1] = "Ail + Flap"; List_Text_Img[100+WING_TYPE.AIL_1_FLP_1] = "wt_1ail_1flp.png|Aileron + Flap" + List_Text[100+WING_TYPE.AIL_2_FLP_1] = "Dual Ail + Flap"; List_Text_Img[100+WING_TYPE.AIL_2_FLP_1] = "wt_2ail_1flp.png|Dual Aileron + Flap" + List_Text[100+WING_TYPE.AIL_2_FLP_2] = "Dual Ail + Dual Flap"; List_Text_Img[100+WING_TYPE.AIL_2_FLP_2] = "wt_2ail_2flp.png|Dual Aileron + Dual Flap" + List_Text[100+WING_TYPE.ELEVON_A] = "Delta/Elevon A"; List_Text_Img[100+WING_TYPE.ELEVON_A] = "wt_elevon.png|Delta/Elevon A" + List_Text[100+WING_TYPE.ELEVON_B] = "Delta/Elevon B"; List_Text_Img[100+WING_TYPE.ELEVON_B] = "wt_elevon.png|Delta/Elevon B" + + -- Tail Types + List_Text[200+TAIL_TYPE.RUD_1] = "Rudder Only"; List_Text_Img[200+TAIL_TYPE.RUD_1] = "tt_1rud.png|Rudder Only" + List_Text[200+TAIL_TYPE.RUD_1_ELEV_1] = "Rud + Ele"; List_Text_Img[200+TAIL_TYPE.RUD_1_ELEV_1] = "tt_1rud_1ele.png|Tail Normal" + List_Text[200+TAIL_TYPE.RUD_1_ELEV_2] = "Rud + Dual Ele"; List_Text_Img[200+TAIL_TYPE.RUD_1_ELEV_2] = "tt_1rud_2ele.png|Rud + Dual Elev" + List_Text[200+TAIL_TYPE.RUD_2_ELEV_1] = "Dual Rud + Ele"; List_Text_Img[200+TAIL_TYPE.RUD_2_ELEV_1] = "tt_2rud_1ele.png|Dual Rud + Elev" + List_Text[200+TAIL_TYPE.RUD_2_ELEV_2] = "Dual Rud + Dual Ele"; List_Text_Img[200+TAIL_TYPE.RUD_2_ELEV_2] = "tt_2rud_2ele.png|Dual Rud + Dual Elev" + List_Text[200+TAIL_TYPE.VTAIL_A] = "V-Tail A"; List_Text_Img[200+TAIL_TYPE.VTAIL_A] = "tt_vtail.png|V-Tail A" + List_Text[200+TAIL_TYPE.VTAIL_B] = "V-Tail B"; List_Text_Img[200+TAIL_TYPE.VTAIL_B] = "tt_vtail.png|V-Tail B" + List_Text[200+TAIL_TYPE.TRAILERON_A] = "Taileron A"; List_Text_Img[200+TAIL_TYPE.TRAILERON_A] = "tt_taileron.png|Taileron A" + List_Text[200+TAIL_TYPE.TRAILERON_B] = "Taileron B"; List_Text_Img[200+TAIL_TYPE.TRAILERON_B] = "tt_taileron.png|Taileron B" + List_Text[200+TAIL_TYPE.TRAILERON_A_R2] = "Taileron A + 2x Rud"; List_Text_Img[200+TAIL_TYPE.TRAILERON_A_R2] = "tt_taileron2.png|Taileron A + Dual Rud" + List_Text[200+TAIL_TYPE.TRAILERON_B_R2] = "Taileron B + 2x Rud"; List_Text_Img[200+TAIL_TYPE.TRAILERON_B_R2] = "tt_taileron2.png|Taileron B + Dual Rud" + + + -- Servo Reverse + if (LCD_W > 128) then + List_Text[300+CH_MODE_TYPE.NORMAL] = "Normal " + List_Text[300+CH_MODE_TYPE.REVERSE] = "Reverse" + else + List_Text[300+CH_MODE_TYPE.NORMAL] = "Nor" + List_Text[300+CH_MODE_TYPE.REVERSE] = "Rev" + end +end + +-- Initial Setup +local function ST_Init() + -- Initialize text (use RX_ID 0) + ST_Init_Text(0) + + -- Setup default Data, and load a file if exist + --modelLib.ST_Default_Data() + if (modelLib.ST_LoadFileData()==0) then -- Did not load a file + modelLib.ST_Default_Data() + modelLib.ST_SaveFileData() -- Save Defaults + end + menuDataChanged = false + currAircraftType = MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE] + currWingType = MENU_DATA[MEMU_VAR.WING_TYPE] + currTailType = MENU_DATA[MEMU_VAR.TAIL_TYPE] + + local ctx = menuLib.DSM_Context + ctx.Phase = PHASE.RX_VERSION +end + +local function ST_Done() + local ctx = menuLib.DSM_Context + ctx.Phase = PHASE.EXIT_DONE +end + + +return { init=ST_Init, run=ST_SendReceive, done=ST_Done } \ No newline at end of file diff --git a/SCRIPTS/TOOLS/DSMLIB/DsmSimMenuLib.lua b/SCRIPTS/TOOLS/DSMLIB/DsmSimMenuLib.lua new file mode 100644 index 0000000..43d45d1 --- /dev/null +++ b/SCRIPTS/TOOLS/DSMLIB/DsmSimMenuLib.lua @@ -0,0 +1,1596 @@ +---- ######################################################################### +---- # # +---- # 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. # +---- # # +---- ######################################################################### + +------------------------------------------------------------------------------ +-- This script simulates the Forward programming menus for AR631 and FC6250HX +-- receivers. +-- The intend is to make easier GUI development in Companion since it cannot +-- talk to the receivers +-- +-- Author: Francisco Arzu +------------------------------------------------------------------------------ + + +local Log, menuLib, modelLib, DEBUG_ON = ... -- Get DebugON from parameters +local SIM_LIB_VERSION = "0.56" +local MSG_FILE = "/SCRIPTS/TOOLS/DSMLIB/msg_fwdp_en.txt" + +local PHASE = menuLib.PHASE +local LINE_TYPE = menuLib.LINE_TYPE + +local SimLib = {} + +local lastGoodMenu=0 +local RX_loadMenu = nil +local RX_Initialized = true + +local IS_EDGETX = false + + +local function AR631_loadMenu(menuId) + menuLib.clearMenuLines() + local ctx = menuLib.DSM_Context + + if (menuId==0x1000) then + --M[Id=0x1000 P=0x0 N=0x0 B=0x0 Text="Main Menu"] + --L[#0 T=M VId=0x1010 Text="Gyro settings" MId=0x1000 ] + --L[#1 T=M VId=0x105E Text="Other settings" MId=0x1000 ] + + ctx.Menu = { MenuId = 0x1000, TextId = 0x004B, PrevId = 0, NextId = 0, BackId = 0 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x00F9, ValId = 0x1010 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x0227, ValId = 0x105E } + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1010) then + -- M[Id=0x1010 P=0x0 N=0x0 B=0x1000 Text="Gyro settings"] + + -- NEW + -- L[#5 T=M VId=0x104F val=nil [0->0,3] Text="First Time Setup" MId=0x1010 ] -- NEW ONLY + -- L[#6 T=M VId=0x1055 Text="First Time SAFE Setup"[0x20D] MId=0x1010 ] + + -- Initialize AR637T + -- L[#0 T=M VId=0x1011 Text="AS3X Settings"[0x1DD] MId=0x1010 ] + -- L[#1 T=M VId=0x1019 Text="SAFE Settings"[0x1E2] MId=0x1010 ] + -- L[#2 T=M VId=0x1021 Text="F-Mode Setup"[0x87] MId=0x1010 ] + -- L[#3 T=M VId=0x1022 Text="System Setup"[0x86] MId=0x1010 ] + -- Only on BNF locked receivers + -- L[#4 T=M VId=0x105C Text="SAFE Select "[0x1F9] MId=0x1010 ] + + + ctx.Menu = { MenuId = 0x1010, TextId = 0x00F9, PrevId = 0, NextId = 0, BackId = 0x1000 } + if not RX_Initialized then + ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, TextId = 0x00A5, ValId = 0x104F} + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, TextId = 0x020D, ValId = 0x1055} + ctx.SelLine = 5 + else + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x01DD, ValId = 0x1011 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x01E2, ValId = 0x1019 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x0087, ValId = 0x1021 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x0086, ValId = 0x1022 } + ctx.MenuLines[4] = { Type = LINE_TYPE.MENU, TextId = 0x01F9, ValId = 0x105C } + ctx.SelLine = 0 + end + lastGoodMenu = menuId + elseif (menuId==0x1011) then + -- M[Id=0x1011 P=0x0 N=0x0 B=0x1010 Text="AS3X Settings"[0x1DD]] + -- L[#0 T=M VId=0x1012 Text="AS3X Gains"[0x1DE] MId=0x1011 ] + -- L[#1 T=M VId=0x1013 Text="Priority"[0x46] MId=0x1011 ] + + -- L[#2 T=M VId=0x1015 Text="Heading"[0x82] MId=0x1011 ] + -- L[#4 T=LM VId=0x1004 val=50 [0->244,50,TS=0] Text="Gain Sensitivity"[0x8A] MId=0x1011] + -- L[#5 T=M VId=0x1016 Text="Fixed/Adjustable Gains"[0x263] MId=0x1011 ] + -- L[#6 T=M VId=0x1017 Text="Capture Gyro Gains"[0xAA] MId=0x1011 ] + + ctx.Menu = { MenuId = 0x1011, TextId = 0x1DD, PrevId = 0, NextId = 0, BackId = 0x1010 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x1DE, ValId = 0x1012} + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x46, ValId = 0x1013} + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x82, ValId = 0x1015} + ctx.MenuLines[4] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x8A, ValId = 0x1004, Min=0, Max=244, Def=50, Val=50 } + ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, TextId = 0x263, ValId = 0x1016} + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, TextId = 0xAA, ValId = 0x1017 } + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1012) then + -- M[Id=0x1012 P=0x0 N=0x0 B=0x1011 Text="AS3X Gains"[0x1DE]] + --L[#0 T=V_nc VId=0x1000 Text="Flight Mode 1"[0x8001] val=1 [0->10,0] MId=0x1012 ] + --L[#2 T=M VId=0x1012 Text="Rate Gains"[0x1E0] MId=0x1012 ] + --L[#3 T=V_nc VId=0x1004 Text="Roll"[0x40] val=14 [0->100,40] MId=0x1012 ] + --L[#4 T=V_nc VId=0x1005 Text="Pitch"[0x41] val=29 [0->100,50] MId=0x1012 ] + --L[#5 T=V_nc VId=0x1006 Text="Yaw"[0x42] val=48 [0->100,60] MId=0x1012 ] + + ctx.Menu = { MenuId = 0x1012, TextId = 0x1DE, PrevId = 0, NextId = 0, BackId = 0x1011 } + ctx.MenuLines[0] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x8001, ValId = 0x1000, Min=0, Max=10, Def=0, Val=1 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x1E0, ValId = 0x1012 } + ctx.MenuLines[3] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x40, ValId = 0x1004, Min=0, Max=100, Def=40, Val=40 } + ctx.MenuLines[4] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x41, ValId = 0x1005, Min=0, Max=100, Def=50, Val=50 } + ctx.MenuLines[5] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x42, ValId = 0x1006, Min=0, Max=100, Def=60, Val=60 } + + ctx.SelLine = 3 + lastGoodMenu = menuId + elseif (menuId==0x1013) then + --M[Id=0x1013 P=0x0 N=0x0 B=0x1011 Text="Priority"[0x46]] + --L[#0 T=V_nc VId=0x1000 Text="Flight Mode 1"[0x8001] val=1 [0->10,0] MId=0x1012 ] + --L[#1 T=M VId=0x1013 Text="Stick Priority"[0xFE] MId=0x1013 ] + --L[#3 T=V_nc VId=0x1004 Text="Roll"[0x40] val=14 [0->160,160] MId=0x1012 ] + --L[#4 T=V_nc VId=0x1005 Text="Pitch"[0x41] val=29 [0->160,160] MId=0x1012 ] + --L[#5 T=V_nc VId=0x1006 Text="Yaw"[0x42] val=48 [0->160,160] MId=0x1012 ] + + ctx.Menu = { MenuId = 0x1013, TextId = 0x46, PrevId = 0, NextId = 0, BackId = 0x1011 } + ctx.MenuLines[0] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x8001, ValId = 0x1000, Min=0, Max=10, Def=0, Val=1 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0xFE, ValId = 0x1013 } + ctx.MenuLines[3] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x40, ValId = 0x1004, Min=0, Max=160, Def=100, Val=160 } + ctx.MenuLines[4] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x41, ValId = 0x1005, Min=0, Max=160, Def=100, Val=160 } + ctx.MenuLines[5] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x42, ValId = 0x1006, Min=0, Max=160, Def=100, Val=160 } + + ctx.SelLine = 3 + lastGoodMenu = menuId + elseif (menuId==0x1015) then + -- M[Id=0x1015 P=0x0 N=0x0 B=0x1011 Text="Heading Gain"[0x266]] + -- L[#0T=V_nc VId=0x1000 Text="Flight Mode 1"[0x8001] val=1 [0->10,0] MId=0x1015 ] + -- L[#1 T=M VId=0x1015 Text="Heading Gain"[0x266] MId=0x1015 ] + -- L[#2 T=V_nc VId=0x1004 Text="Roll"[0x40] val=0 [0->100,0] MId=0x1015 ] + -- L[#3 T=V_nc VId=0x1005 Text="Pitch"[0x41] val=0 [0->100,0] MId=0x1015 ] + -- L[#5 T=M VId=0x1015 Text="Use CAUTION for Yaw gain!"[0x26A] MId=0x1015 ] + -- L[#6T=V_nc VId=0x1006 Text="Yaw"[0x42] val=0 [0->100,0] MId=0x1015 ] + + ctx.Menu = { MenuId = 0x1015, TextId = 0x266, PrevId = 0, NextId = 0, BackId = 0x1011 } + ctx.MenuLines[0] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x8001, ValId = 0x1000, Min=0, Max=10, Def=0, Val=1 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x1F9, ValId = 0x1015 } + ctx.MenuLines[2] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x40, ValId = 0x1004, Min=0, Max=100, Def=0, Val=0 } + ctx.MenuLines[3] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x41, ValId = 0x1005, Min=0, Max=100, Def=0, Val=0 } + ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, TextId = 0x26A, ValId = 0x1015 } + ctx.MenuLines[6] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x42, ValId = 0x1006, Min=0, Max=100, Def=0, Val=0 } + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1016) then + -- M[Id=0x1016 P=0x0 N=0x0 B=0x1011 Text="Fixed/Adjustable Gains"[0x263]] + -- L[#0T=V_nc VId=0x1000 Text="Flight Mode 1"[0x8001] val=1 [0->10,0] MId=0x1016 ] + -- L[#1 T=M VId=0x1016 val=nil [0->0,2] Text="Fixed/Adjustable Gains"[0x263] MId=0x1016 ] + -- L[#2 T=LM_nc VId=0x1002 Text="Roll"[0x40] MId=0x1016 val=1 NL=(0->1,24,S=242) [242->243,243] ] + -- L[#3 T=LM_nc VId=0x1003 Text="Pitch"[0x41] MId=0x1016 val=1 NL=(0->1,1,S=242) [242->243,243] ] + -- L[#4 T=LM_nc VId=0x1004 Text="Yaw"[0x42] MId=0x1016 val=1 NL=(0->1,1,S=242) [242->243,243] ] + + ctx.Menu = { MenuId = 0x1016, TextId = 0x263, PrevId = 0, NextId = 0, BackId = 0x1011 } + ctx.MenuLines[0] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x8001, ValId = 0x1000, Min=0, Max=10, Def=0, Val=1 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x1F9, ValId = 0x1016 } + ctx.MenuLines[2] = { Type = LINE_TYPE.LIST_MENU_NC, TextId = 0x40, ValId = 0x1002, Min=242, Max=243, Def=243, Val=1 } + ctx.MenuLines[3] = { Type = LINE_TYPE.LIST_MENU_NC, TextId = 0x41, ValId = 0x1003, Min=242, Max=243, Def=243, Val=1 } + ctx.MenuLines[4] = { Type = LINE_TYPE.LIST_MENU_NC, TextId = 0x42, ValId = 0x1004, Min=242, Max=243, Def=243, Val=1 } + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1017) then + --M[Id=0x1017 P=0x0 N=0x0 B=0x1011 Text="Capture Gyro Gains"[0xAA]] + --L[#0 T=M VId=0x1017 Text="Gains will be captured on"[0x24C] MId=0x1017 ] + --L[#1 T=V_nc VId=0x1000 Text=" Flight Mode 1"[0x8001] Val=1 [0->10,0] MId=0x1017 ] + --L[#2 T=M VId=0x1017 Text="Captured gains will be"[0x24D] MId=0x1017 ] + --L[#3 T=V_i8 VId=0x1004 Text="Roll"[0x40] Val=40 [0->0,0] MId=0x1017 ] + --L[#4 T=V_i8 VId=0x1005 Text="Pitch"[0x41] Val=50 [0->0,0] MId=0x1017 ] + --L[#5 T=V_i8 VId=0x1006 Text="Yaw"[0x42] Val=60 [0->0,0] MId=0x1017 ] + --L[#6 T=M VId=0x1018 Text="Capture Gyro Gains"[0xAA] MId=0x1017 ] + + ctx.Menu = { MenuId = 0x1017, TextId = 0xAA, PrevId = 0, NextId = 0, BackId = 0x1011 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x24C, ValId = 0x1017 } + ctx.MenuLines[1] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x8001, ValId = 0x1000, Min=0, Max=10, Def=0, Val=1 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x24D, ValId = 0x1017 } + ctx.MenuLines[3] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x40, ValId = 0x1004, Min=0, Max=0, Def=0, Val=40 } + ctx.MenuLines[4] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x41, ValId = 0x1005, Min=0, Max=0, Def=0, Val=50 } + ctx.MenuLines[5] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x42, ValId = 0x1006, Min=0, Max=0, Def=0, Val=60 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, TextId = 0xAA, ValId = 0x1018 } + + ctx.SelLine = 6 + lastGoodMenu = menuId + elseif (menuId==0x1018) then + --M[Id=0x1018 P=0x0 N=0x0 B=0x1011 Text="Capture Gyro Gains"[0xAA]] + --L[#0 T=M VId=0x1018 Text="Gains on"[0x24E] MId=0x1018 ] + --L[#1 T=V_nc VId=0x1018 Text=" Flight Mode 1"[0x8001] Val=1 [0->10,0] MId=0x1018 ] + --L[#2 T=M VId=0x1018 Text="were captured and changed"[0x24F] MId=0x1018 + --L[#3 T=M VId=0x1018 Text="from Adjustable to Fixed"[0x250] MId=0x1018 ] + --L[#4 T=V_i8 VId=0x1004 Text="Roll"[0x40] Val=40 [0->0,0] MId=0x1018 ] + --L[#5 T=V_i8 VId=0x1005 Text="Pitch"[0x41] Val=50 [0->0,0] MId=0x1018 ] + --L[#6 T=V_i8 VId=0x1006 Text="Yaw"[0x42] Val=60 [0->0,0] MId=0x1018 ] + + ctx.Menu = { MenuId = 0x1018, TextId = 0xAA, PrevId = 0, NextId = 0, BackId = 0x1011 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x24E, ValId = 0x1018 } + ctx.MenuLines[1] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x8001, ValId = 0x1000, Min=0, Max=10, Def=0, Val=1 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x24F, ValId = 0x1018 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x250, ValId = 0x1018 } + ctx.MenuLines[4] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x40, ValId = 0x1004, Min=0, Max=0, Def=0, Val=40 } + ctx.MenuLines[5] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x41, ValId = 0x1005, Min=0, Max=0, Def=0, Val=50 } + ctx.MenuLines[6] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x42, ValId = 0x1006, Min=0, Max=0, Def=0, Val=60 } + + ctx.SelLine = -1 + lastGoodMenu = menuId + elseif (menuId==0x1019) then + --M[Id=0x1019 P=0x0 N=0x0 B=0x1010 Text="SAFE Settings"[0x1E2]] + --L[#0 T=M VId=0x101A Text="SAFE Gains"[0x1E3] MId=0x1019 ] + --L[#1 T=M VId=0x101B Text="Angle Limits"[0x226] MId=0x1019 ] + --L[#5 T=M VId=0x101E Text="Fixed/Adjustable Gains"[0x263] MId=0x1019 ] + --L[#6 T=M VId=0x101F Text="Capture Gyro Gains"[0xAA] MId=0x1019 ] + + ctx.Menu = { MenuId = 0x1019, TextId = 0x1E2, PrevId = 0, NextId = 0, BackId = 0x1010 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x1E3, ValId = 0x101A } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x226, ValId = 0x101B } + ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, TextId = 0x263, ValId = 0x101E } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, TextId = 0xAA, ValId = 0x101F } + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x101A) then + --M[Id=0x101A P=0x0 N=0x0 B=0x1019 Text="SAFE Gains"[0x1E3]] + --L[#0 T=V_nc VId=0x1000 Text=" Flight Mode 1"[0x8001] Val=nil [0->10,0] MId=0x101A ] + --L[#1 T=M VId=0x101A Text="Gain"[0x43] MId=0x101A ] + --L[#2 T=V_nc VId=0x1002 Text="Roll"[0x40] Val=35 [5->100,35] MId=0x101A ] + --L[#3 T=V_nc VId=0x1003 Text="Pitch"[0x41] Val=35 [5->100,35] MId=0x101A ] + + ctx.Menu = { MenuId = 0x101A, TextId = 0x1E3, PrevId = 0, NextId = 0, BackId = 0x1019 } + ctx.MenuLines[0] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x8001, ValId = 0x1000, Min=0, Max=10, Def=0, Val=1 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x43, ValId = 0x101A } + ctx.MenuLines[2] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x40, ValId = 0x1002, Min=5, Max=100, Def=35, Val=35 } + ctx.MenuLines[3] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x41, ValId = 0x1003, Min=5, Max=100, Def=60, Val=35 } + + ctx.SelLine = -1 + lastGoodMenu = menuId + elseif (menuId==0x101B) then + --M[Id=0x101B P=0x0 N=0x0 B=0x1019 Text="Angle Limits"[0x226]] + --L[#0T=V_nc VId=0x1000 Text=" Flight Mode 1"[0x8001] Val=nil [0->10,0] MId=0x101B ] + --L[#1 T=M VId=0x101B Text="Angle Limits"[0x226] MId=0x101B ] + --L[#2 T=V_nc VId=0x1002 Text="Roll Right"[0x1E9] Val=60 [10->90,60] MId=0x101B ] + --L[#3 T=V_nc VId=0x1003 Text="Roll Left"[0x1EA] Val=60 [10->90,60] MId=0x101B ] + --L[#4 T=V_nc VId=0x1004 Text="Pitch Down"[0x1EB] Val=40 [10->75,40] MId=0x101B ] + --L[#5 T=V_nc VId=0x1005 Text="Pitch Up"[0x1EC] Val=50 [10->75,50] MId=0x101B ] + + ctx.Menu = { MenuId = 0x101B, TextId = 0x226, PrevId = 0, NextId = 0, BackId = 0x1019 } + ctx.MenuLines[0] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x8001, ValId = 0x1000, Min=0, Max=10, Def=0, Val=1 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x226, ValId = 0x101B } + ctx.MenuLines[2] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x1E9, ValId = 0x1002, Min=10, Max=90, Def=60, Val=60 } + ctx.MenuLines[3] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x1EA, ValId = 0x1003, Min=10, Max=90, Def=60, Val=60 } + ctx.MenuLines[4] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x1EB, ValId = 0x1004, Min=10, Max=90, Def=40, Val=40 } + ctx.MenuLines[5] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x1EC, ValId = 0x1005, Min=10, Max=90, Def=50, Val=50 } + + ctx.SelLine = -1 + lastGoodMenu = menuId + + elseif (menuId==0x101E) then + --M[Id=0x101E P=0x0 N=0x0 B=0x1019 Text="Fixed/Adjustable Gains"[0x263]] + --L[#0 T=V_nc VId=0x1000 Text=" Flight Mode 1"[0x8001] Val=nil [0->10,0] MId=0x101E ] + --L[#1 T=M VId=0x101E Text="Fixed/Adjustable Gains"[0x263] MId=0x101E ] + --L[#2 T=LM_nc VId=0x1002 Text="Roll"[0x40] Val=0 N=(0->1,1,S=242) [242->243,243] MId=0x101E ] + --L[#3 T=LM_nc VId=0x1003 Text="Pitch"[0x41] Val=0 N=(0->1,1,S=242) [242->243,243] MId=0x101E ] + + ctx.Menu = { MenuId = 0x101E, TextId = 0x263, PrevId = 0, NextId = 0, BackId = 0x1019 } + ctx.MenuLines[0] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x8001, ValId = 0x1000, Min=0, Max=10, Def=0, Val=1 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x263, ValId = 0x101E } + ctx.MenuLines[2] = { Type = LINE_TYPE.LIST_MENU_NC, TextId = 0x40, ValId = 0x1002, Min=242, Max=243, Def=243, Val=0 } + ctx.MenuLines[3] = { Type = LINE_TYPE.LIST_MENU_NC, TextId = 0x41, ValId = 0x1003, Min=242, Max=243, Def=243, Val=0 } + + ctx.SelLine = -1 + lastGoodMenu = menuId + elseif (menuId==0x101F) then + --M[Id=0x101F P=0x0 N=0x0 B=0x1019 Text="Capture Gyro Gains"[0xAA]] + --L[#0 T=M VId=0x101F Text="Gains will be captured on"[0x24C] MId=0x101F ] + --L[#1 T=V_nc VId=0x1000 Text=" Flight Mode 1"[0x8001] Val=1 [0->10,0] MId=0x101F ] + --L[#2 T=M VId=0x101F Text="Captured gains will be"[0x24D] MId=0x101F ] + --L[#3 T=V_i8 VId=0x1004 Text="Roll"[0x40] Val=40 [0->0,0] MId=0x101F ] + --L[#4 T=V_i8 VId=0x1005 Text="Pitch"[0x41] Val=50 [0->0,0] MId=0x101F ] + --L[#6 T=M VId=0x1020 Text="Capture Gyro Gains"[0xAA] MId=0x101F ] + + ctx.Menu = { MenuId = 0x101F, TextId = 0xAA, PrevId = 0, NextId = 0, BackId = 0x1019 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x24C, ValId = 0x101F } + ctx.MenuLines[1] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x8001, ValId = 0x1000, Min=0, Max=10, Def=0, Val=1 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x24D, ValId = 0x101F } + ctx.MenuLines[3] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x40, ValId = 0x1004, Min=0, Max=0, Def=0, Val=35 } + ctx.MenuLines[4] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x41, ValId = 0x1005, Min=0, Max=0, Def=0, Val=35 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, TextId = 0xAA, ValId = 0x1020 } + + ctx.SelLine = 6 + lastGoodMenu = menuId + elseif (menuId==0x1020) then + --M[Id=0x1020 P=0x0 N=0x0 B=0x101F Text="Capture Gyro Gains"[0xAA]] + --L[#0 T=M VId=0x1020 Text="Gains on"[0x24E] MId=0x1020 ] + --L[#1 T=V_nc VId=0x1018 Text=" Flight Mode 1"[0x8001] Val=1 [0->10,0] MId=0x1020 ] + --L[#2 T=M VId=0x1018 Text="were captured and changed"[0x24F] MId=0x1020 + --L[#3 T=M VId=0x1018 Text="from Adjustable to Fixed"[0x250] MId=0x1020 ] + --L[#4 T=V_i8 VId=0x1004 Text="Roll"[0x40] Val=40 [0->0,0] MId=0x1020 ] + --L[#5 T=V_i8 VId=0x1005 Text="Pitch"[0x41] Val=50 [0->0,0] MId=0x1020 ] + + ctx.Menu = { MenuId = 0x1020, TextId = 0xAA, PrevId = 0, NextId = 0, BackId = 0x1019 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x24E, ValId = 0x1020 } + ctx.MenuLines[1] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x8001, ValId = 0x1000, Min=0, Max=10, Def=0, Val=1 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x24F, ValId = 0x1020 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x250, ValId = 0x1020 } + ctx.MenuLines[4] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x40, ValId = 0x1004, Min=0, Max=0, Def=0, Val=35 } + ctx.MenuLines[5] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x41, ValId = 0x1005, Min=0, Max=0, Def=0, Val=35 } + + ctx.SelLine = -1 + lastGoodMenu = menuId + elseif (menuId==0x1021) then + --M[Id=0x1021 P=0x0 N=0x0 B=0x1010 Text="F-Mode Setup"[0x87]] + --L[#0 T=V_nc VId=0x1000 Text="Flight Mode 1"[0x8001] val=1 [0->10,0] MId=0x1021 ] + --L[#1 T=M VId=0x7CA6 Text="FM Channel"[0x78] MId=0x1021 ] + --L[#2 T=LM VId=0x1002 Text="AS3X"[0x1DC] val=1 (0->1,3,S=3) [3->4|3] MId=0x1021] + + -- Why Jump from Value 3 to 176?? where do we know valid values???? + --L[#3 T=LM VId=0x1003 Text="Safe Mode"[0x1F8] val=3|"Inh" NL=(0->244,0,S=0) [0->244,3] MId=0x1021 ] + --L[#3 T=LM VId=0x1003 Text="Safe Mode"[0x1F8] val=176|"Self-Level/Angle Dem" NL=(0->244,0,S=0) [0->244,3] MId=0x1021 ] + + --L[#4 T=LM VId=0x1004 Text="Panic"[0x8B] val=0 NL=(0->1,3,S=3) [3->4,3] MId=0x1021 ] + --L[#5 T=LM VId=0x1005 Text="High Thr to Pitch"[0x1F0] val=0 NL=(0->1,3,S=3) [3->4,3] MId=0x1021 ] + --L[#6 T=LM VId=0x1006 Text="Low Thr to Pitch"[0x1EF] val=0 NL=(0->1,3,S=3) [3->4,3] MId=0x1021 ] + + ctx.Menu = { MenuId = 0x1021, TextId = 0x87, PrevId = 0, NextId = 0, BackId = 0x1010 } + ctx.MenuLines[0] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x8001, ValId = 0x1000, Min=0, Max=10, Def=0, Val=1 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x78, ValId = 0x7CA6 } + ctx.MenuLines[2] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x1DC, ValId = 0x1002, Min=3, Max=4, Def=3, Val=1 } + ctx.MenuLines[3] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x1F8, ValId = 0x1003, Min=0, Max=244, Def=3, Val=176 } + ctx.MenuLines[4] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x8B, ValId = 0x1004, Min=3, Max=4, Def=3, Val=0 } + ctx.MenuLines[5] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x1F0, ValId = 0x1005, Min=3, Max=4, Def=3, Val=0 } + ctx.MenuLines[6] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x1EF, ValId = 0x1006, Min=3, Max=4, Def=3, Val=0 } + ctx.SelLine = 1 + lastGoodMenu = menuId + elseif (menuId==0x1022) then + --M[Id=0x1022 P=0x0 N=0x0 B=0x1010 Text="System Setup"[0x86]] + --L[#0 T=M VId=0x1023 Text="Relearn Servo Settings"[0x190] MId=0x1022 ] + --L[#1 T=M VId=0x1025 Text="Orientation"[0x80] MId=0x1022 ] + --L[#2 T=M VId=0x1029 Text="Gain Channel Select"[0xAD] MId=0x1022 ] + --L[#3 T=M VId=0x102A Text="SAFE/Panic Mode Setup"[0xCA] MId=0x1022 ] + --L[#4 T=M VId=0x1032 Text="Utilities"[0x240] MId=0x1022 ] + + ctx.Menu = { MenuId = 0x1022, TextId = 0x86, PrevId = 0, NextId = 0, BackId = 0x1010 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x190, ValId = 0x1023 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x80, ValId = 0x1025 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0xAD, ValId = 0x1029 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0xCA, ValId = 0x102A } + ctx.MenuLines[4] = { Type = LINE_TYPE.MENU, TextId = 0x240, ValId = 0x1032 } + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1023) then + --M[Id=0x1023 P=0x0 N=0x0 B=0x1022 Text="Relearn Servo Settings"[0x190]] + --L[#3 T=M VId=0x1024 Text="Apply"[0x90] MId=0x1023 ] + + ctx.Menu = { MenuId = 0x1023, TextId = 0x190, PrevId = 0, NextId = 0, BackId = 0x1022 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x90, ValId = 0x1024 } + + ctx.SelLine = 3 + lastGoodMenu = menuId + elseif (menuId==0x1024) then + --M[Id=0x1024 P=0x0 N=0x0 B=0x0 Text="Relearn Servo Settings"[0x190]] + --L[#3 T=M VId=0x1000 Text="Complete"[0x93] MId=0x1024 ] + + ctx.Menu = { MenuId = 0x1024, TextId = 0x190, PrevId = 0, NextId = 0, BackId = 0 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x93, ValId = 0x1000 } + + ctx.SelLine = 3 + lastGoodMenu = menuId + + elseif (menuId==0x1025) then + --M[Id=0x1025 P=0x0 N=0x0 B=0x1022 Text="Orientation"[0x80]] + --L[#0 T=M VId=0x1025 Text="Set the model level,"[0x21A] MId=0x1025 ] + --L[#1 T=M VId=0x1025 Text="and press Continue."[0x21B] MId=0x1025 ] + --L[#2 T=M VId=0x1025 Text=""[0x21C] MId=0x1025 ] + --L[#3 T=M VId=0x1025 Text=""[0x21D] MId=0x1025 ] + --L[#5 T=M VId=0x1026 Text="Continue"[0x224] MId=0x1025 ] + --L[#6 T=M VId=0x1027 Text="Set Orientation Manually"[0x229] MId=0x1025 ] + + ctx.Menu = { MenuId = 0x1025, TextId = 0x80, PrevId = 0, NextId = 0, BackId = 0x1022 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x21A, ValId = 0x1025 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x21B, ValId = 0x1025 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x21C, ValId = 0x1025 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x21D, ValId = 0x1025 } + ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, TextId = 0x224, ValId = 0x1026 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, TextId = 0x229, ValId = 0x1027 } + ctx.SelLine = 5 + lastGoodMenu = menuId + elseif (menuId==0x1026) then + --M[Id=0x1026 P=0x1025 N=0x0 B=0x1025 Text="Orientation"[0x80]] + --L[#0 T=M VId=0x1026 Text="Set the model on its nose,"[0x21F] MId=0x1026 ] + --L[#1 T=M VId=0x1026 Text="and press Continue. If the"[0x220] MId=0x1026 ] + --L[#2 T=M VId=0x1026 Text="orientation on the next"[0x221] MId=0x1026 ] + --L[#3 T=M VId=0x1026 Text="screen is wrong go back"[0x222] MId=0x1026 ] + --L[#4 T=M VId=0x1026 Text="and try again."[0x223] MId=0x1026 ] + --L[#6 T=M VId=0x1027 Text="Continue"[0x224] MId=0x1026 ] + + ctx.Menu = { MenuId = 0x1026, TextId = 0x80, PrevId = 0x1025, NextId = 0, BackId = 0x1025 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x21F, ValId = 0x1026 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x220, ValId = 0x1026 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x221, ValId = 0x1026 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x222, ValId = 0x1026 } + ctx.MenuLines[4] = { Type = LINE_TYPE.MENU, TextId = 0x223, ValId = 0x1026 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, TextId = 0x224, ValId = 0x1027 } + + ctx.SelLine = 6 + lastGoodMenu = menuId + elseif (menuId==0x1027) then + --M[Id=0x1028 P=0x0 N=0x0 B=0x1028 Text="Orientation"[0x80]] + --L[#5 T=LM_nc VId=0x1000 Text="Orientation"[0x80] Val=4|"RX Pos 5" NL=(0->23,0,S=203) [203->226,203] MId=0x1027 ] + --L[#6 T=M VId=0x1028 Text="Continue"[0x224] MId=0x1027 ] + + ctx.Menu = { MenuId = 0x1027, TextId = 0x80, PrevId = 0x1025, NextId = 0, BackId = 0x1025 } + ctx.MenuLines[5] = { Type = LINE_TYPE.LIST_MENU_NC, TextId = 0x80, ValId = 0x1000, Min=203, Max=226, Def=203, Val=5 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, TextId = 0x224, ValId = 0x1028 } + ctx.SelLine = 5 + lastGoodMenu = menuId + elseif (menuId==0x1028) then + --M[Id=0x1027 P=0x1025 N=0x0 B=0x1025 Text="Orientation"[0x80]] + --L[#2 T=M VId=0x1 Text="Resetting RX... "[0x9F] MId=0x1028 ] + --L[#3 T=M VId=0x1028 Text="RX Pos 7"[0xD1] MId=0x1028 ] + + ctx.Menu = { MenuId = 0x1028, TextId = 0x80, PrevId = 0x1025, NextId = 0, BackId = 0x1025 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x9F, ValId = 0x1 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0xD1, ValId = 0x1028 } + ctx.SelLine = 2 + lastGoodMenu = menuId + + elseif (menuId==0x1029) then + --M[Id=0x1029 P=0x0 N=0x0 B=0x1022 Text="Gain Channel Select"[0xAD]] + --L[#0 T=M VId=0x1029 Text="AS3X"[0x1DC] MId=0x1029 ] + --L[#1 T=LM VId=0x1000 Text="Roll"[0x40] Val=0|"Inhibit?" NL=(0->32,0,S=53) [53->85,53] MId=0x1029 ] + --L[#2 T=LM VId=0x1001 Text="Pitch"[0x41] Val=0|"Inhibit?" NL=(0->32,0,S=53) [53->85,53] MId=0x1029 ] + --L[#3 T=LM VId=0x1002 Text="Yaw"[0x42] Val=0|"Inhibit?" NL=(0->32,0,S=53) [53->85,53] MId=0x1029 ] + --L[#4 T=M VId=0x1029 Text="SAFE"[0xDA] MId=0x1029 ] + --L[#5 T=LM VId=0x1004 Text="Roll"[0x40] Val=0|"Inhibit?" NL=(0->32,0,S=53) [53->85,53] MId=0x1029 ] + --L[#6 T=LM VId=0x1005 Text="Pitch"[0x41] Val=0|"Inhibit?" NL=(0->32,0,S=53) [53->85,53] MId=0x1029 ] + + ctx.Menu = { MenuId = 0x1029, TextId = 0xAD, PrevId = 0, NextId = 0, BackId = 0x1022 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x1DC, ValId = 0x1029 } + + ctx.MenuLines[1] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x40, ValId = 0x1000, Min=53, Max=85, Def=53, Val=0 } + ctx.MenuLines[2] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x41, ValId = 0x1001, Min=53, Max=85, Def=53, Val=0 } + ctx.MenuLines[3] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x42, ValId = 0x1002, Min=53, Max=85, Def=53, Val=0 } + ctx.MenuLines[4] = { Type = LINE_TYPE.MENU, TextId = 0xDA, ValId = 0x1029 } + ctx.MenuLines[5] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x40, ValId = 0x1004, Min=53, Max=85, Def=53, Val=0 } + ctx.MenuLines[6] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x41, ValId = 0x1005, Min=53, Max=85, Def=53, Val=0 } + + ctx.SelLine = 2 + lastGoodMenu = menuId + elseif (menuId==0x1030) then + --M[Id=0x1030 P=0x0 N=0x0 B=0x102A Text="Attitude Trim"[0x1E6]] + --L[#0 T=M VId=0x1030 Text="Level model and capture attiude/M"[0xCD] MId=0x1030 ] FORCED MENU + --L[#1 T=M VId=0x1030 Text="Attitude Trim"[0x1E6] MId=0x1030 ] + --L[#2 T=V_de VId=0x1002 Text="Roll"[0x40] Val=-1 [-45->45,0] MId=0x1030 ] + --L[#3 T=V_de VId=0x1003 Text="Pitch"[0x41] Val=7 [-45->45,0] MId=0x1030 ] + --L[#5 T=M VId=0x1030 Text="Positive = Nose Up/Roll Right"[0x267] MId=0x1030 ] + --L[#6 T=M VId=0x1030 Text="Negative = Nose Down/Roll Left"[0x268] MId=0x1030 ] + + ctx.Menu = { MenuId = 0x1030, TextId = 0x1E6, PrevId = 0, NextId = 0, BackId = 0x102A } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0xCD, ValId = 0x1030 } -- FORCED MENU (/M) + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x1E6, ValId = 0x1030 } + ctx.MenuLines[2] = { Type = LINE_TYPE.VALUE_DEGREES, TextId = 0x40, ValId = 0x1002, Min=-45, Max=45, Def=0, Val=-1 } + ctx.MenuLines[3] = { Type = LINE_TYPE.VALUE_DEGREES, TextId = 0x41, ValId = 0x1003, Min=-45, Max=45, Def=0, Val=7 } + ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, TextId = 0x267, ValId = 0x1030 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, TextId = 0x268, ValId = 0x1030 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1031) then + --M[Id=0x1031 P=0x0 N=0x0 B=0x102A Text="Failsafe Angles"[0x1F6]] + --L[#0 T=M VId=0x1031 Text="Failsafe Angles"[0x1F6] MId=0x1031 ] + --L[#1 T=V_de VId=0x1001 Text="Roll"[0x40] Val=0 [-90->90,0] MId=0x1031 ] + --L[#2 T=V_de VId=0x1002 Text="Pitch"[0x41] Val=0 [-90->90,0] MId=0x1031 ] + --L[#5 T=M VId=0x1031 Text="Positive = Nose Up/Roll Right"[0x267] MId=0x1031 ] + --L[#6 T=M VId=0x1031 Text="Negative = Nose Down/Roll Left"[0x268] MId=0x1031 ] + + ctx.Menu = { MenuId = 0x1031, TextId = 0x1F6, PrevId = 0, NextId = 0, BackId = 0x102A } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x1F6, ValId = 0x1031 } + ctx.MenuLines[1] = { Type = LINE_TYPE.VALUE_DEGREES, TextId = 0x40, ValId = 0x1001, Min=-90, Max=90, Def=0, Val=0 } + ctx.MenuLines[2] = { Type = LINE_TYPE.VALUE_DEGREES, TextId = 0x41, ValId = 0x1002, Min=-90, Max=90, Def=0, Val=0 } + ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, TextId = 0x267, ValId = 0x1031 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, TextId = 0x268, ValId = 0x1031 } + + ctx.SelLine = 1 + lastGoodMenu = menuId + elseif (menuId==0x1032) then + --M[Id=0x1032 P=0x0 N=0x0 B=0x1022 Text="Utilities"[0x240]] + --L[#0 T=M VId=0x1033 Text="Copy Flight Mode Settings"[0x23D] MId=0x1032 ] + --L[#1 T=LM_nc VId=0x1001 Text="Enabled F-Modes"[0x88] Val=2 NL=(0->9,2,S=182) [182->191,184] MId=0x1032 ] + + ctx.Menu = { MenuId = 0x1032, TextId = 0x240, PrevId = 0, NextId = 0, BackId = 0x1022 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x23D, ValId = 0x1033 } + ctx.MenuLines[1] = { Type = LINE_TYPE.LIST_MENU_NC, TextId = 0x88, ValId = 0x1001, Min=182, Max=191, Def=184, Val=2 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + + elseif (menuId==0x1033) then + --M[Id=0x1033 P=0x0 N=0x0 B=0x1032 Text="Copy Flight Mode Settings"[0x23D]] + --L[#0 T=V_nc VId=0x1000 Text="Flight Mode"[0x8001] Val=0 [0->10,0] MId=0x1033 ] + --L[#1 T=M VId=0x1033 Text="WARNING: "Target""[0x260] MId=0x1033 ] + --L[#2 T=M VId=0x1033 Text="flight mode will be overwritten"[0x261] MId=0x1033 ] + --L[#3 T=M VId=0x1033 Text="by "Source""[0x262] MId=0x1033 ] + --L[#4 T=LM_nc VId=0x1004 Text="Source Flight Mode"[0x23E] Val=0|"FM1" NL=(0->9,0,S=182) [182->191,182] MId=0x1033 ] + --L[#5 T=LM_nc VId=0x1005 Text="Target Flight Mode"[0x23F] Val=0|"FM1" NL=(0->9,0,S=182) [182->191,182] MId=0x1033 ] + --L[#6 T=M VId=0x1035 Text="Apply"[0x90] MId=0x1034 ] + + + ctx.Menu = { MenuId = 0x1033, TextId = 0x23D, PrevId = 0, NextId = 0, BackId = 0x1032 } + ctx.MenuLines[0] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x8001, ValId = 0x1000, Min=0, Max=10, Def=0, Val=1 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x260, ValId = 0x1033 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x261, ValId = 0x1033 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x262, ValId = 0x1033 } + ctx.MenuLines[4] = { Type = LINE_TYPE.LIST_MENU_NC, TextId = 0x23E, ValId = 0x1004, Min=182, Max=191, Def=182, Val=0 } + ctx.MenuLines[5] = { Type = LINE_TYPE.LIST_MENU_NC, TextId = 0x23F, ValId = 0x1005, Min=182, Max=191, Def=182, Val=0 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, TextId = 0x90, ValId = 0x1034 } + + ctx.SelLine = 4 + lastGoodMenu = menuId + elseif (menuId==0x1034) then + --M[Id=0x1033 P=0x0 N=0x0 B=0x1032 Text="Copy Flight Mode Settings"[0x23D]] + + --L[#1 T=M VId=0x1033 Text="Are you sure you want to ovewrite the \"Target\""[0x251] MId=0x1033 ] + --L[#2 T=M VId=0x1033 Text="with the \"Source\" ? "[0x252] MId=0x1033 ] + --L[#3 T=M VId=0x1033 Text=""[0x253] MId=0x1033 ] + --L[#4 T=LM_nc VId=0x1004 Text="Source Flight Mode"[0x23E] Val=0|"FM1" NL=(0->0,0,S=182) [182->182,182] MId=0x1033 ] + --L[#5 T=LM_nc VId=0x1005 Text="Target Flight Mode"[0x23F] Val=1|"FM2" NL=(0->0,0,S=182) [182->182,182] MId=0x1033 ] + --L[#6 T=M VId=0x1035 Text="YES"[0x259] MId=0x1034 ] + + + ctx.Menu = { MenuId = 0x1034, TextId = 0x23D, PrevId = 0x1033, NextId = 0, BackId = 0x1033 } + + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x251, ValId = 0x1034 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x252, ValId = 0x1034 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x253, ValId = 0x1034 } + ctx.MenuLines[4] = { Type = LINE_TYPE.LIST_MENU_NC, TextId = 0x23E, ValId = 0x1004, Min=182, Max=182, Def=182, Val=0 } + ctx.MenuLines[5] = { Type = LINE_TYPE.LIST_MENU_NC, TextId = 0x23F, ValId = 0x1005, Min=182, Max=182, Def=182, Val=1 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, TextId = 0x259, ValId = 0x1035 } + + ctx.SelLine = 6 + lastGoodMenu = menuId + elseif (menuId==0x1035) then + --M[Id=0x1035 P=0x0 N=0x0 B=0x1032 Text="Copy Flight Mode Settings"[0x23D]] + --L[#3 T=M VId=0x1032 Text="Complete"[0x93] MId=0x1035 ] + + ctx.Menu = { MenuId = 0x1035, TextId = 0x23D, PrevId = 0, NextId = 0, BackId = 0x1032 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x93, ValId = 0x1032 } + + ctx.SelLine = 3 + lastGoodMenu = menuId + elseif (menuId==0x102A) then + --M[Id=0x102A P=0x0 N=0x0 B=0x1022 Text="SAFE/Panic Mode Setup"[0xCA]] + --L[#2 T=M VId=0x102B Text="Panic"[0x8B] MId=0x102A ] + --L[#3 T=M VId=0x102D Text="Throttle to Pitch"[0x1EE] MId=0x102A ] + --L[#4 T=M VId=0x1030 Text="Attitude Trim"[0x1E6] MId=0x102A ] + --L[#5 T=LM_nc VId=0x1006 Text="SAFE Failsafe FMode"[0x1FD] Val=0|"FM1" NL=(0->10,0,S=181) [181->191,181] MId=0x102A ] + --L[#6 T=M VId=0x1031 Text="Failsafe Angles"[0x1F6] MId=0x102A ] + + ctx.Menu = { MenuId = 0x102A, TextId = 0xCA, PrevId = 0, NextId = 0, BackId = 0x1022 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x8B, ValId = 0x102B } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x1EE, ValId = 0x102D } + ctx.MenuLines[4] = { Type = LINE_TYPE.MENU, TextId = 0x1E6, ValId = 0x1030 } + ctx.MenuLines[5] = { Type = LINE_TYPE.LIST_MENU_NC, TextId = 0x1FD, ValId = 0x1006, Min=182, Max=191, Def=182, Val=0 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, TextId = 0x1F6, ValId = 0x1031 } + + ctx.SelLine = 5 + lastGoodMenu = menuId + elseif (menuId==0x102B) then + --M[Id=0x102B P=0x0 N=0x0 B=0x102A Text="Panic"[0x8B]] + --L[#0 T=LM VId=0x1000 Text="Panic Channel"[0xD2] Val=0|"Inhibit?" NL=(0->32,0,S=53) [53->85,53] MId=0x102B ] + --L[#1 T=LM VId=0x1001 Text="Panic Delay"[0x8E] Val=0|"Inh" NL=(0->1,0,S=3) [3->4,3] MId=0x102B ] + --L[#2 T=LM VId=0x1002 Text="Panic Flight Mode"[0x1FC] Val=0|"FM1" NL=(0->10,0,S=181) [181->191,181] MId=0x102B ] + --L[#3 T=V_nc VId=0x1003 Text="Roll Right"[0x1E9] Val=30 [0->90,30] MId=0x102B ] + --L[#4 T=V_nc VId=0x1004 Text="Roll Left"[0x1EA] Val=30 [0->90,30] MId=0x102B ] + --L[#5 T=V_nc VId=0x1005 Text="Pitch Down"[0x1EB] Val=30 [0->75,30] MId=0x102B ] + --L[#6 T=V_nc VId=0x1006 Text="Pitch Up"[0x1EC] Val=30 [0->75,30] MId=0x102B ] + + ctx.Menu = { MenuId = 0x102B, TextId = 0x8B, PrevId = 0, NextId = 0, BackId = 0x102A } + ctx.MenuLines[0] = { Type = LINE_TYPE.LIST_MENU, TextId = 0xD2, ValId = 0x1000, Min=53, Max=85, Def=53, Val=0 } + ctx.MenuLines[1] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x8E, ValId = 0x1001, Min=3, Max=4, Def=3, Val=0 } + ctx.MenuLines[2] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x1FC, ValId = 0x1002, Min=181, Max=191, Def=181, Val=0 } + ctx.MenuLines[3] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x1E9, ValId = 0x1003, Min=0, Max=90, Def=30, Val=30 } + ctx.MenuLines[4] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x1EA, ValId = 0x1004, Min=0, Max=90, Def=30, Val=30 } + ctx.MenuLines[5] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x1E9, ValId = 0x1005, Min=0, Max=75, Def=30, Val=30 } + ctx.MenuLines[6] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x1EC, ValId = 0x1006, Min=0, Max=75, Def=30, Val=30 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x102D) then + --M[Id=0x102D P=0x0 N=0x0 B=0x102A Text="SAFE - Throttle to Pitch"[0x269]] + --L[#0 T=M VId=0x102D Text="Postive = Up, Negative = Down"[0x254] MId=0x102D ] + --L[#1 T=M VId=0x102D Text="Low Thr to Pitch"[0x1EF] MId=0x102D ] + --L[#2 T=V_nc VId=0x1001 Text="Threshold"[0x1F3] Val=30 [0->50,30] MId=0x102D ] + --L[#3 T=V_de VId=0x1002 Text="Angle"[0x1F4] Val=0 [-45->45,0] MId=0x102D ] + --L[#4 T=M VId=0x102D Text="High Thr to Pitch"[0x1F0] MId=0x102D ] + --L[#5 T=V_nc VId=0x1005 Text="Threshold"[0x1F3] Val=70 [51->100,70] MId=0x102D ] + --L[#6 T=V_de VId=0x1006 Text="Angle"[0x1F4] Val=0 [-45->45,0] MId=0x102D ] + + ctx.Menu = { MenuId = 0x102D, TextId = 0x269, PrevId = 0, NextId = 0, BackId = 0x102A } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x254, ValId = 0x102D } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x1EF, ValId = 0x102D } + ctx.MenuLines[2] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x1F3, ValId = 0x1001, Min=0, Max=50, Def=30, Val=30 } + ctx.MenuLines[3] = { Type = LINE_TYPE.VALUE_DEGREES, TextId = 0x1F4, ValId = 0x1002, Min=-45, Max=45, Def=0, Val=0 } + ctx.MenuLines[4] = { Type = LINE_TYPE.MENU, TextId = 0x1F0, ValId = 0x102D } + ctx.MenuLines[5] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x1F3, ValId = 0x1005, Min=51, Max=100, Def=70, Val=70 } + ctx.MenuLines[6] = { Type = LINE_TYPE.VALUE_DEGREES, TextId = 0x1F4, ValId = 0x1006, Min=-45, Max=45, Def=0, Val=0 } + + ctx.SelLine = 2 + lastGoodMenu = menuId + elseif (menuId==0x104F) then + --M[Id=0x104F P=0x0 N=0x1050 B=0x1010 Text="First Time Setup"] + --L[#0 T=M VId=0x104F Text="Make sure the model has been" MId=0x104F ] + --L[#1 T=M VId=0x104F Text="configured, including wing type," MId=0x104F ] + --L[#2 T=M VId=0x104F Text="reversing, travel, trimmed, etc." MId=0x104F ] + --L[#3 T=M VId=0x104F [0->0,2] Text="before continuing setup." MId=0x104F ] + --L[#4 T=M VId=0x104F [0->0,2] Text="" MId=0x104F ] + --L[#5 T=M VId=0x104F [0->0,2] Text="" MId=0x104F ] + + ctx.Menu = { MenuId = 0x104F, TextId = 0x00F9, PrevId = 0, NextId = 0x1050, BackId = 0x1010 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x0100, ValId = 0x104F } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x0101, ValId = 0x104F } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x0102, ValId = 0x104F } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x0103, ValId = 0x104F } + ctx.MenuLines[4] = { Type = LINE_TYPE.MENU, TextId = 0x0104, ValId = 0x104F } + ctx.SelLine = menuLib.NEXT_BUTTON + lastGoodMenu = menuId + elseif (menuId==0x1050) then + + --M[Id=0x1050 P=0x104F N=0x1051 B=0x1010 Text="First Time Setup"] + --L[#0 T=M VId=0x1050 Text="Any wing type, channel assignment," MId=0x1050 ] + --L[#1 T=M VId=0x1050 Text="subtrim, or servo reversing changes" MId=0x1050 + --L[#2 T=M VId=0x1050 Text="require running through initial" MId=0x1050 ] + --L[#3 T=M VId=0x1050 Text="setup again." MId=0x1050 ] + + ctx.Menu = { MenuId = 0x1050, TextId = 0x00F9, PrevId = 0x104F, NextId = 0x1051, BackId = 0x1010 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x0106, ValId = 0x1050 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x0107, ValId = 0x1050 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x0108, ValId = 0x1050 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x0109, ValId = 0x1050 } + ctx.MenuLines[4] = { Type = LINE_TYPE.MENU, TextId = 0x010A, ValId = 0x1050 } + ctx.SelLine = menuLib.NEXT_BUTTON + lastGoodMenu = menuId + elseif (menuId==0x1051) then + --M[Id=0x1051 P=0x0 N=0x0 B=0x1010 Text="First Time Setup"] + --L[#0 T=M VId=0x1051 Text="Set the model level," MId=0x1051 ] + --L[#1 T=M VId=0x1051 Text="and press Continue." MId=0x1051 ] + --L[#2 T=M VId=0x1051 Text="" MId=0x1051 ] + --L[#5 T=M VId=0x1052 Text="Continue" MId=0x1051 ] + --L[#6 T=M VId=0x1053 Text="Set Orientation Manually" MId=0x1051 ] + + ctx.Menu = { MenuId = 0x1051, TextId = 0x00F9, PrevId = 0, NextId = 0, BackId = 0x1010 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x021A, ValId = 0x1051 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x021B, ValId = 0x1051 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x021C, ValId = 0x1051 } + ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, TextId = 0x0224, ValId = 0x1052 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, TextId = 0x0229, ValId = 0x1053 } + + ctx.SelLine = 5 + lastGoodMenu = menuId + elseif (menuId==0x1052) then + --M[Id=0x1052 P=0x1051 N=0x0 B=0x1010 Text="First Time Setup"[0xA5]] + --L[#0 T=M VId=0x1052 Text="Set the model on its nose,"[0x21F] MId=0x1052 ] + --L[#1 T=M VId=0x1052 Text="and press Continue. If the"[0x220] MId=0x1052 ] + --L[#2 T=M VId=0x1052 Text="orientation on the next"[0x221] MId=0x1052 ] + --L[#3 T=M VId=0x1052 Text="screen is wrong go back"[0x222] MId=0x1052 ] + --L[#4 T=M VId=0x1052 Text="and try again."[0x223] MId=0x1052 ] + --L[#6 T=M VId=0x1053 Text="Continue"[0x224] MId=0x1052 ] + + ctx.Menu = { MenuId = 0x1052, TextId = 0x00A5, PrevId = 0x1051, NextId = 0, BackId = 0x1010 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x21F, ValId = 0x1052 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x220, ValId = 0x1052 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x221, ValId = 0x1052 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x222, ValId = 0x1052 } + ctx.MenuLines[4] = { Type = LINE_TYPE.MENU, TextId = 0x223, ValId = 0x1052 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, TextId = 0x224, ValId = 0x1053 } + ctx.SelLine = 6 + lastGoodMenu = menuId + elseif (menuId==0x1053) then + --M[Id=0x1053 P=0x1051 N=0x0 B=0x1010 Text="First Time Setup"[0xA5]] + --L[#5 T=LM_nc VId=0x1000 Text="Orientation"[0x80] MId=0x1053 val=0 (0->23,0,S=203) [203->226,203] ] + --L[#6 T=M VId=0x1054 Text="Continue"[0x224] MId=0x1053 ] + + ctx.Menu = { MenuId = 0x1053, TextId = 0x00A5, PrevId = 0x1051, NextId = 0, BackId = 0x1010 } + ctx.MenuLines[5] = { Type = LINE_TYPE.LIST_MENU_NC, TextId = 0x80, ValId = 0x1000, Min=203, Max=226, Def=203, Val=0 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, TextId = 0x224, ValId = 0x1054 } + ctx.SelLine = 5 + lastGoodMenu = menuId + elseif (menuId==0x1054) then + --M[Id=0x1054 P=0x1053 N=0x0 B=0x1010 Text="First Time Setup"[0xA5]] + --L[#1 T=M VId=0x7CA5 Text="Gain Channel Select"[0xAD] ] + --L[#6 T=M VId=0x1 Text="Apply"[0x90] MId=0x1054 ] + + ctx.Menu = { MenuId = 0x1054, TextId = 0x00A5, PrevId = 0x1053, NextId = 0, BackId = 0x1010 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0xAD, ValId = 0x7CA5 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, TextId = 0x90, ValId = 0x01 } -- Special save&reboot?? + ctx.SelLine = 1 + lastGoodMenu = menuId + elseif (menuId==0x1055) then + --M[Id=0x1055 P=0x0 N=0x0 B=0x1010 Text="First Time SAFE Setup"[0x20D]] + --L[#0 T=M VId=0x1055 Text="Before setting up SAFE"[0x255] MId=0x1055 ] + --L[#1 T=M VId=0x1055 Text="a Fligt Mode channel"[0x256] MId=0x1055 ] + --L[#2 T=M VId=0x1055 Text="most be configured."[0x257] MId=0x1055 ] + --L[#5 T=M VId=0x7CA7 Text="FM Channel"[0x78] MId=0x1055 ] + --L[#6 T=M VId=0x1056 Text="Continue"[0x224] MId=0x1055 ] + + ctx.Menu = { MenuId = 0x1055, TextId = 0x20D, PrevId = 0x1053, NextId = 0, BackId = 0x1010 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x255, ValId = 0x1055 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x256, ValId = 0x1055 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x257, ValId = 0x1055 } + ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, TextId = 0x78, ValId = 0x7CA7 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, TextId = 0x224, ValId = 0x1056 } + ctx.SelLine = 5 + lastGoodMenu = menuId + elseif (menuId==0x1056) then + --M[Id=0x1056 P=0x1055 N=0x1057 B=0x1010 Text="First Time SAFE Setup"[0x20D]] + --L[#0 T=V_nc VId=0x1000 Text="Flight Mode"[0x8001] Val=1 [0->10,0] MId=0x1056 ] + --L[#1 T=M VId=0x1056 Text="Select the desired flight mode"[0x25A] MId=0x1056 ] + --L[#2 T=M VId=0x1056 Text="switch position to adjust settings"[0x25B] MId=0x1056 ] + --L[#3 T=M VId=0x1056 Text="for each flight mode"[0x25C] MId=0x1056 ] + --L[#4 T=M VId=0x1056 Text=""[0x25D] MId=0x1056 ] + --L[#5 T=M VId=0x1056 Text=""[0x25E] MId=0x1056 ] + + ctx.Menu = { MenuId = 0x1056, TextId = 0x20D, PrevId = 0x1053, NextId = 0x1057, BackId = 0x1010 } + ctx.MenuLines[0] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x8001, ValId = 0x1000, Min=0, Max=10, Def=0, Val=1 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x25A, ValId = 0x1056 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x25B, ValId = 0x1056 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x25C, ValId = 0x1056 } + ctx.MenuLines[4] = { Type = LINE_TYPE.MENU, TextId = 0x25D, ValId = 0x1056 } + ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, TextId = 0x25E, ValId = 0x1056 } + + ctx.SelLine = 7 + lastGoodMenu = menuId + elseif (menuId==0x1057) then + --M[Id=0x1057 P=0x1056 N=0x1059 B=0x1010 Text="First Time SAFE Setup"[0x20D]] + --L[#0 T=M VId=0x1057 Text="AS3X gains must be tuned"[0x20E] MId=0x1057 ] + --L[#1 T=M VId=0x1057 Text="and active i SAFE Flight Modes"[0x20F] MId=0x1057 ] + --L[#2 T=M VId=0x1057 Text="to help reduce wobble."[0x210] MId=0x1057 ] + --L[#3 T=M VId=0x1057 Text=""[0x211] MId=0x1057 ] + --L[#4 T=M VId=0x1057 Text=""[0x212] MId=0x1057 ] + --L[#5 T=M VId=0x1057 Text=""[0x213] MId=0x1057 ] + + ctx.Menu = { MenuId = 0x1057, TextId = 0x20D, PrevId = 0x1056, NextId = 0x1059, BackId = 0x1010 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x20E, ValId = 0x1057 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x20F, ValId = 0x1057 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x210, ValId = 0x1057 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x211, ValId = 0x1057 } + ctx.MenuLines[4] = { Type = LINE_TYPE.MENU, TextId = 0x212, ValId = 0x1057 } + ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, TextId = 0x213, ValId = 0x1057 } + + ctx.SelLine = 7 + lastGoodMenu = menuId + + elseif (menuId==0x1059) then + --M[Id=0x1059 P=0x1057 N=0x105A B=0x1010 Text="First Time SAFE Setup"[0x20D]] + --L[#0 T=M VId=0x1059 Text="Leven model and capture attiude"[0xCD] MId=0x1059 ] + --L[#1 T=M VId=0x1059 Text="Attitude Trim"[0x1E6] MId=0x1059 ] + --L[#2 T=V_de VId=0x1002 Text="Roll"[0x40] Val=13 [-45->45,0] MId=0x1059 ] + --L[#3 T=V_de VId=0x1003 Text="Pitch"[0x41] Val=5 [-45->45,0] MId=0x1059 ] + --L[#5 T=M VId=0x1059 Text="Positive = Nose Up/Roll Right"[0x267] MId=0x1059 ] + --L[#6 T=M VId=0x1059 Text="Negative = Nose Down/Roll Left"[0x268] MId=0x1059 ] + + ctx.Menu = { MenuId = 0x1059, TextId = 0x20D, PrevId = 0x1057, NextId = 0x105A, BackId = 0x1010 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0xCD, ValId = 0x1059 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x1E6, ValId = 0x1059 } + ctx.MenuLines[2] = { Type = LINE_TYPE.VALUE_DEGREES, TextId = 0x40, ValId = 0x1002, Min=-45, Max=45, Def=0, Val=13 } + ctx.MenuLines[3] = { Type = LINE_TYPE.VALUE_DEGREES, TextId = 0x41, ValId = 0x1003, Min=-45, Max=45, Def=0, Val=5 } + ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, TextId = 0x267, ValId = 0x1059 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, TextId = 0x268, ValId = 0x1059 } + + ctx.SelLine = 7 + lastGoodMenu = menuId + elseif (menuId==0x105A) then + --M[Id=0x105A P=0x1059 N=0x105B B=0x1010 Text="First Time SAFE Setup"[0x20D]] + --L[#0 T=V_nc VId=0x1000 Text="Flight Mode"[0x8001] Val=0 [0->10,0] MId=0x105A ] + --L[#1 T=LM VId=0x1001 Text="Safe Mode"[0x1F8] Val=3|"Inh" NL=(0->244,3,S=0) [0->244,3] MId=0x105A ] + --L[#2 T=M VId=0x105A Text="Angle Limits "[0x226] MId=0x105A ] + --L[#3 T=V_nc VId=0x1003 Text="Roll Right"[0x1E9] Val=60 [10->90,60] MId=0x105A ] + --L[#4 T=V_nc VId=0x1004 Text="Roll Left"[0x1EA] Val=60 [10->90,60] MId=0x105A ] + --L[#5 T=V_nc VId=0x1005 Text="Pitch Down"[0x1EB] Val=40 [10->75,40] MId=0x105A ] + --L[#6 T=V_nc VId=0x1006 Text="Pitch Up"[0x1EC] Val=50 [10->75,50] MId=0x105A ] + + ctx.Menu = { MenuId = 0x105A, TextId = 0x1DE, PrevId = 0x1059, NextId = 0x105B, BackId = 0x1010 } + ctx.MenuLines[0] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x8001, ValId = 0x1000, Min=0, Max=10, Def=0, Val=1 } + ctx.MenuLines[1] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x1F8, ValId = 0x1001, Min=0, Max=244, Def=3, Val=3 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x226, ValId = 0x105A } + ctx.MenuLines[3] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x1E9, ValId = 0x1003, Min=10, Max=90, Def=60, Val=60 } + ctx.MenuLines[4] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x1EA, ValId = 0x1004, Min=10, Max=90, Def=60, Val=60 } + ctx.MenuLines[5] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x1EB, ValId = 0x1004, Min=10, Max=75, Def=40, Val=40 } + ctx.MenuLines[6] = { lineNum = 7, Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x1EC, ValId = 0x1004, Min=10, Max=75, Def=50, Val=50 } + + ctx.SelLine = 1 + lastGoodMenu = menuId + elseif (menuId==0x105B) then + --M[Id=0x105B P=0x105A N=0x0 B=0x1000 Text="First Time SAFE Setup"[0x20D]] + --L[#3 T=M VId=0x1 Text="Apply"[0x90] MId=0x1064 ] + + ctx.Menu = { MenuId = 0x105B, TextId = 0x20D, PrevId = 0x105A, NextId = 0, BackId = 0x1000 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x90, ValId = 0x1 } -- reset RX + + ctx.SelLine = 3 + lastGoodMenu = menuId + elseif (menuId==0x105C) then + -- M[Id=0x105C P=0x0 N=0x0 B=0x1010 Text="SAFE Select"[0x1F9]] + --L[#0 T=V_nc VId=0x1000 Text="Flight Mode 1"[0x8001] val=1 [0->10,0] MId=0x105C ] + --L[#1 T=LM VId=0x1001 Text="SAFE Select Channel"[0x1D7] val=5 NL=(0->32,53,S=53) [53->85,53] MId=0x105C] + --L[#2 T=LM VId=0x1002 Text="AS3X"[0x1DC] val=1 NL=(0->1,1,S=1) [1->2,1] MId=0x105C] + --L[#3 T=LM VId=0x1003 Text="SAFE"[0xDA] val=0 NL=(0->0,0,S=1) [1->1,1] MId=0x105C ] + --L[#6 T=LM VId=0x1006 Text="SAFE Select"[0x1F9] val=0 NL=(0->1,1,S=1) [1->2,1] MId=0x105C ] + + ctx.Menu = { MenuId = 0x105C, TextId = 0x01F9, PrevId = 0, NextId = 0, BackId = 0x1010 } + ctx.MenuLines[0] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x8001, ValId = 0x1000, Min=0, Max=10, Def=0, Val=1 } + ctx.MenuLines[1] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x1D7, ValId = 0x1001, Min=53, Max=85, Def=53, Val=5 } + ctx.MenuLines[2] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x1DC, ValId = 0x1002, Min=1, Max=2, Def=1, Val=1 } + ctx.MenuLines[3] = { Type = LINE_TYPE.LIST_MENU, TextId = 0xDA, ValId = 0x1003, Min=1, Max=1, Def=1, Val=0 } + ctx.MenuLines[6] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x1F9, ValId = 0x1004, Min=1, Max=2, Def=1, Val=0 } + + ctx.SelLine = 1 + lastGoodMenu = menuId + elseif (menuId==0x105E) then + -- M[Id=0x105E P=0x0 N=0x0 B=0x1000 Text="Other settings"] + -- L[#1 T=M VId=0x1060 Text="Failsafe" MId=0x105E ] + -- L[#2 T=M VId=0x1064 Text="Enter Receiver Bind Mode" MId=0x105E ] + -- L[#3 T=M VId=0x1065 Text="Frame Rate" MId=0x105E ] + -- L[#4 T=M VId=0x1067 Text="Factory Reset" MId=0x105E ] + -- L[#5 T=M VId=0x1069 Text="Restore from Backup" MId=0x105E ] + -- L[#6 T=M VId=0x106A Text="Save to Backup" MId=0x105E ] + + ctx.Menu = { MenuId = 0x105E, TextId = 0x0227, PrevId = 0, NextId = 0, BackId = 0x1010 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x004A, ValId = 0x1060 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x019C, ValId = 0x1064 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x0085, ValId = 0x1065 } + ctx.MenuLines[4] = { Type = LINE_TYPE.MENU, TextId = 0x0097, ValId = 0x1067 } + ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, TextId = 0x020A, ValId = 0x1069 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, TextId = 0x0209, ValId = 0x106A } + + ctx.SelLine = 1 + lastGoodMenu = menuId + elseif (menuId==0x1060) then + --M[Id=0x1060 P=0x0 N=0x0 B=0x105E Text="Failsafe"] + --L[#0 T=M VId=0x1061 Text="Failsafe" MId=0x1060 ] + --L[#1 T=M VId=0x1062 Text="Capture Failsafe Positions" MId=0x1060 ] + + ctx.Menu = { MenuId = 0x1060, TextId = 0x004A, PrevId = 0, NextId = 0, BackId = 0x105E } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x004A, ValId = 0x1061 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x009A, ValId = 0x1062 } + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1061) then + --M[Id=0x1061 P=0x0 N=0x0 B=0x1060 Text="Failsafe"] + --L[#0 T=LM_nc VId=0x1000 Text="Outputs:" val=0 NL=(0->19,54,S=54) [54->73,54] MId=0x1061 ] + --L[#2 T=LM_tog VId=0x1002 Text="Custom Failsafe:" val=0 NL=(0->1,95,S=95) [95->96,95] MId=0x1061 ] + --L[#3 T=V_% VId=0x1003 Text="Position:" val=-100 [-150->150,0] MId=0x1061 ] + + ctx.Menu = { MenuId = 0x1061, TextId = 0x004A, PrevId = 0, NextId = 0, BackId = 0x1060 } + ctx.MenuLines[0] = { Type = LINE_TYPE.LIST_MENU_NC, TextId = 0x0050, ValId = 0x1000, Min=54, Max=73, Def=54, Val=0 } + ctx.MenuLines[1] = { Type = LINE_TYPE.LIST_MENU_TOG, TextId = 0x009C, ValId = 0x1002, Min=95, Max=96, Def=95, Val=0 } + ctx.MenuLines[2] = { Type = LINE_TYPE.VALUE_PERCENT, TextId = 0x004E, ValId = 0x1002, Min=-150, Max=150, Def=0, Val=-100 } + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1064) then + --M[Id=0x1064 P=0x0 N=0x0 B=0x105E Text="Enter Receiver Bind Mode"[0x19C]] + --L[#3 T=M VId=0x1 Text="Apply"[0x90] MId=0x1064 ] + + ctx.Menu = { MenuId = 0x1064, TextId = 0x19C, PrevId = 0, NextId = 0, BackId = 0x105E } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x90, ValId = 0x1 } -- reset RX + + ctx.SelLine = 3 + lastGoodMenu = menuId + elseif (menuId==0x1065) then + --M[Id=0x1065 P=0x0 N=0x0 B=0x105E Text="Frame Rate"] + --L[#0 T=LM VId=0x1000 Text="Output Channel 1:" val=46 NL=(0->244,46|S=0) [0->244,0] MId=0x1065 ] + --L[#1 T=LM VId=0x1001 Text="Output Channel 2:" val=47 NL=(0->244,46|S=0) [0->244,0] MId=0x1065 ] + --L[#2 T=LM VId=0x1002 Text="Output Channel 3:" val=46 NL=(0->244,46|S=0) [0->244,0] MId=0x1065 ] + --L[#3 T=LM VId=0x1003 Text="Output Channel 4:" val=46 NL=(0->244,46|S=0) [0->244,0] MId=0x1065 ] + --L[#4 T=LM VId=0x1004 Text="Output Channel 5:" val=46 NL=(0->244,46|S=0) [0->244,0] MId=0x1065 ] + --L[#5 T=LM VId=0x1005 Text="Output Channel 6:" val=46 NL=(0->244,46|S=0) [0->244,0] MId=0x1065 ] + + ctx.Menu = { MenuId = 0x1065, TextId = 0x0085, PrevId = 0, NextId = 0, BackId = 0x105E } + ctx.MenuLines[0] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x0051, ValId = 0x1000, Min=0, Max=244, Def=46, Val=46 } + ctx.MenuLines[1] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x0052, ValId = 0x1001, Min=0, Max=244, Def=46, Val=47 } + ctx.MenuLines[2] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x0053, ValId = 0x1002, Min=0, Max=244, Def=46, Val=46 } + ctx.MenuLines[3] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x0054, ValId = 0x1002, Min=0, Max=244, Def=46, Val=46 } + ctx.MenuLines[4] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x0055, ValId = 0x1002, Min=0, Max=244, Def=46, Val=46 } + ctx.MenuLines[5] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x0056, ValId = 0x1002, Min=0, Max=244, Def=46, Val=46 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1067) then + --M[Id=0x1067 P=0x0 N=0x0 B=0x105E Text="WARNING!"[0x22B]] + --L[#0 T=M VId=0x1067 Text="This will reset the"[0x22C] MId=0x1067 ] + --L[#1 T=M VId=0x1067 Text="configuration to factory"[0x22D] MId=0x1067 ] + --L[#2 T=M VId=0x1067 Text="defaults. This does not"[0x22E] MId=0x1067 ] + --L[#3 T=M VId=0x1067 Text="affect the backup config."[0x22F] MId=0x1067 ] + --L[#4 T=M VId=0x1067 Text=""[0x230] MId=0x1067 ] + --L[#6 T=M VId=0x1068 Text="Apply"[0x90] MId=0x1067 ] + + ctx.Menu = { MenuId = 0x1067, TextId = 0x22B, PrevId = 0, NextId = 0, BackId = 0x105E } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x22C, ValId = 0x1067 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x22D, ValId = 0x1067 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x22E, ValId = 0x1067 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x22F, ValId = 0x1067 } + ctx.MenuLines[4] = { Type = LINE_TYPE.MENU, TextId = 0x230, ValId = 0x1067 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, TextId = 0x90, ValId = 0x1068 } + + ctx.SelLine = 6 + lastGoodMenu = menuId + elseif (menuId==0x1068) then + --M[Id=0x1068 P=0x0 N=0x0 B=0x1000 Text="Done"[0x94]] + --L[#3 T=M VId=0x1000 Text="Complete"[0x93] MId=0x1068 ] + + ctx.Menu = { MenuId = 0x1068, TextId = 0x94, PrevId = 0, NextId = 0, BackId = 0x1000 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x93, ValId = 0x1000 } + + ctx.SelLine = 3 + lastGoodMenu = menuId + elseif (menuId==0x1069) then + --M[Id=0x1069 P=0x0 N=0x0 B=0x105E Text="WARNING!"[0x22B]] + --L[#0 T=M VId=0x1069 Text="This will overwrite the"[0x236] MId=0x1069 ] + --L[#1 T=M VId=0x1069 Text="current config with"[0x237] MId=0x1069 ] + --L[#2 T=M VId=0x1069 Text="that which is in"[0x238] MId=0x1069 ] + --L[#3 T=M VId=0x1069 Text="the backup memory."[0x239] MId=0x1069 ] + --L[#4 T=M VId=0x1069 Text=""[0x23A] MId=0x1069 ] + --L[#6 T=M VId=0x1068 Text="Apply"[0x90] MId=0x1069 ] + + ctx.Menu = { MenuId = 0x1069, TextId = 0x22B, PrevId = 0, NextId = 0, BackId = 0x105E } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x236, ValId = 0x1069 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x237, ValId = 0x1069 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x238, ValId = 0x1069 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x239, ValId = 0x1069 } + ctx.MenuLines[4] = { Type = LINE_TYPE.MENU, TextId = 0x23A, ValId = 0x1069 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, TextId = 0x90, ValId = 0x1068 } + + ctx.SelLine = 6 + lastGoodMenu = menuId + elseif (menuId==0x106A) then + --M[Id=0x106A P=0x0 N=0x0 B=0x105E Text="WARNING!"[0x22B]] + --L[#0 T=M VId=0x106A Text="This will overwrite the"[0x231] MId=0x106A ] + --L[#1 T=M VId=0x106A Text="backup memory with your"[0x232] MId=0x106A ] + --L[#2 T=M VId=0x106A Text="current configuartion."[0x233] MId=0x106A ] + --L[#3 T=M VId=0x106A Text=""[0x234] MId=0x106A ] + --L[#4 T=M VId=0x106A Text=""[0x235] MId=0x106A ] + --L[#6 T=M VId=0x1068 Text="Apply"[0x90] MId=0x106A ] + + ctx.Menu = { MenuId = 0x106A, TextId = 0x22B, PrevId = 0, NextId = 0, BackId = 0x105E } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x231, ValId = 0x106A } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x232, ValId = 0x106A } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x233, ValId = 0x106A } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x234, ValId = 0x106A } + ctx.MenuLines[4] = { Type = LINE_TYPE.MENU, TextId = 0x235, ValId = 0x106A } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, TextId = 0x90, ValId = 0x1068 } + + ctx.SelLine = 6 + lastGoodMenu = menuId + elseif (menuId==0x7CA5) then + --M[Id=0x7CA5 P=0x0 N=0x1021 B=0x1021 Text="Gain Channel Select"[0xAD]] + --L[#0 T=LM VId=0x1000 Text="Gain Channel"[0x89] val=7 N=(0->32,53,S=53) [53->85,53] MId=0x7CA5 ] + + ctx.Menu = { MenuId = 0x7CA5, TextId = 0xAD, PrevId = 0, NextId = 0x1054, BackId = 0x1054 } + ctx.MenuLines[0] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x89, ValId = 0x1000, Min=53, Max=85, Def=53, Val=7 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x7CA6) then + --M[Id=0x7CA6 P=0x0 N=0x1021 B=0x1021 Text="FM Channel"[0x78]] + --L[#0 T=LM VId=0x1000 Text="FM Channel"[0x78] val=7 N=(0->32,53,S=53) [53->85,53] MId=0x7CA6 ] + + ctx.Menu = { MenuId = 0x7CA6, TextId = 0x78, PrevId = 0, NextId = 0x1021, BackId = 0x1021 } + ctx.MenuLines[0] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x78, ValId = 0x1000, Min=53, Max=85, Def=53, Val=7 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x7CA7) then + --M[Id=0x7CA7 P=0x0 N=0x1055 B=0x1055 Text="FM Channel"[0x78]] + --L[#0 T=LM VId=0x1000 Text="FM Channel"[0x78] val=7 N=(0->32,53,S=53) [53->85,53] MId=0x7CA6 ] + + ctx.Menu = { MenuId = 0x7CA7, TextId = 0x78, PrevId = 0, NextId = 0x1055, BackId = 0x1055 } + ctx.MenuLines[0] = { Type = LINE_TYPE.LIST_MENU, TextId = 0x78, ValId = 0x1000, Min=53, Max=85, Def=53, Val=0 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x0001) then + -- Save Settings and Reboot + ctx.Menu = { MenuId = 0x0001, TextId = 0x009F, PrevId = 0, NextId = 0, BackId = 0x1000 } + ctx.SelLine = menuLib.BACK_BUTTON + + else + print("NOT IMPLEMENTED") + ctx.Menu = { MenuId = 0x0002, Text = "NOT IMPLEMENTED", TextId = 0, PrevId = 0, NextId = 0, BackId = lastGoodMenu } + ctx.SelLine = menuLib.BACK_BUTTON + end + + menuLib.PostProcessMenu() +end + +local function FC6250HX_loadMenu(menuId) + menuLib.clearMenuLines() + local ctx = menuLib.DSM_Context + + if (menuId==0x1000) then + --M[Id=0x1000 P=0x0 N=0x0 B=0x0 Text="Main Menu"[0x4B]] + --L[#0 T=M VId=0x1100 Text="Swashplate"[0xD3] MId=0x1000 ] + --L[#1 T=M VId=0x1200 [0->0,2] Text="Tail rotor"[0xDD] MId=0x1000 ] + + --L[#2 T=M VId=0x1280 Text="Governor"[0xF2] + + --L[#3 T=M VId=0x1400 [0->0,2] Text="SAFE"[0xDA] MId=0x1000 ] + --L[#5 T=M VId=0x1300 [0->0,2] Text="Setup"[0xDE] MId=0x1000 ] + --L[#6 T=M VId=0x1700 [0->0,2] Text="System Setup"[0x86] MId=0x1000 ] + + ctx.Menu = { MenuId = 0x1000, TextId = 0x004B, PrevId = 0, NextId = 0, BackId = 0 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0xD3, ValId = 0x1100 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0xDD, ValId = 0x1200 } + + if (not RX_Initialized) then + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0xF2, ValId = 0x1280 } + end + + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0xDA, ValId = 0x1400 } + + ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, TextId = 0xDE, ValId = 0x1300 } + ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, TextId = 0x86, ValId = 0x1700 } + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1100) then + --M[Id=0x1100 P=0x0 N=0x0 B=0x1000 Text="Swashplate"[0xD3]] + --L[#0 T=M VId=0x1110 val=nil [0->0,2] Text="Roll"[0x40] MId=0x1100 ] + --L[#1 T=M VId=0x1120 val=nil [0->0,2] Text="Pitch"[0x41] MId=0x1100 ] + --L[#2 T=V_i8 VId=0x1103 Text="Agility"[0xD5] val=100 [0->200,100] MId=0x1100 ] + + ctx.Menu = { MenuId = 0x1100, TextId = 0xD3, PrevId = 0, NextId = 0, BackId = 0x1000 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x40, ValId = 0x1110, Min=0, Max=0, Def=3, Val=nil } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x41, ValId = 0x1120, Min=0, Max=0, Def=2, Val=nil } + ctx.MenuLines[2] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0xD5, ValId = 0x1103, Min=0, Max=200, Def=100, Val=100 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1110) then + --M[Id=0x1110 P=0x0 N=0x0 B=0x1100 Text="Roll"[0x40]] + --L[#0 T=V_i16 VId=0x1111 Text="@ per sec"[0xDC] val=270 [0->900,270] MId=0x1110 ] + --L[#1 T=V_nc VId=0x1112 Text="FLIGHT MODE"[0x8000] val=1 [0->5,0] MId=0x1110 ] + --L[#2 T=V_i8 VId=0x1113 Text="Proportional"[0x71] val=100 [0->255,100] MId=0x1110 ] + --L[#3 T=V_i8 VId=0x1114 Text="Integral"[0x72] val=100 [0->255,100] MId=0x1110 ] + --L[#4 T=V_i8 VId=0x1115 Text="Derivate"[0x73] val=7 [0->255,7] MId=0x1110 ] + + ctx.Menu = { MenuId = 0x1110, TextId = 0x40, PrevId = 0, NextId = 0, BackId = 0x1100 } + ctx.MenuLines[0] = { Type = LINE_TYPE.VALUE_NUM_I16, TextId = 0xDC, ValId = 0x1111, Min=0, Max=900, Def=270, Val=270 } + ctx.MenuLines[1] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x8000, ValId = 0x1112, Min=0, Max=5, Def=0, Val=1 } + ctx.MenuLines[2] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x71, ValId = 0x1113, Min=0, Max=255, Def=100, Val=100 } + ctx.MenuLines[3] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x72, ValId = 0x1114, Min=0, Max=255, Def=100, Val=100 } + ctx.MenuLines[4] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x73, ValId = 0x1115, Min=0, Max=255, Def=7, Val=7 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1120) then + --M[Id=0x1120 P=0x0 N=0x0 B=0x1100 Text="Pitch"[0x41]] + --L[#0 T=V_i16 VId=0x1121 Text="@ per sec"[0xDC] Val=270 [0->900,270] MId=0x1120 ] + --L[#1 T=V_i8 VId=0x1212 Text="Start"[0x92] Val=nil [5->200,25] MId=0x1200 ] + --L[#2 T=V_i8 VId=0x1213 Text="Stop"[0xD8] Val=nil [5->200,25] MId=0x1200 ] + --L[#3 T=V_nc VId=0x1122 Text=" FLIGHT MODE"[0x8000] Val=1 [0->5,0] MId=0x1120 ] + --L[#4 T=V_i8 VId=0x1123 Text="Proportional"[0x71] Val=100 [0->255,100] MId=0x1120 ] + --L[#5 T=V_i8 VId=0x1124 Text="Integral"[0x72] Val=100 [0->255,100] MId=0x1120 ] + --L[#6 T=V_i8 VId=0x1125 Text="Derivate"[0x73] Val=14 [0->255,14] MId=0x1120 ] + + ctx.Menu = { MenuId = 0x1120, TextId = 0x41, PrevId = 0, NextId = 0, BackId = 0x1100 } + ctx.MenuLines[0] = { Type = LINE_TYPE.VALUE_NUM_I16, TextId = 0xDC, ValId = 0x1121, Min=0, Max=900, Def=270, Val=270 } + ctx.MenuLines[1] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x92, ValId = 0x1123, Min=5, Max=200, Def=25, Val=25 } + ctx.MenuLines[2] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0xD8, ValId = 0x1123, Min=5, Max=200, Def=26, Val=100 } + + ctx.MenuLines[3] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x8000, ValId = 0x1122, Min=0, Max=5, Def=0, Val=1 } + + ctx.MenuLines[4] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x71, ValId = 0x1123, Min=0, Max=255, Def=100, Val=100 } + ctx.MenuLines[5] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x72, ValId = 0x1124, Min=0, Max=255, Def=95, Val=95 } + ctx.MenuLines[6] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x73, ValId = 0x1125, Min=0, Max=255, Def=45, Val=45 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1200) then + --M[Id=0x1200 P=0x0 N=0x0 B=0x1000 Text="Tail rotor"[0xDD]] + --L[#0 T=V_i16 VId=0x1211 Text="@ per sec"[0xDC] Val=550 [0->1280,550] MId=0x1200 ] + --L[#1 T=V_i8 VId=0x1212 Text="Start"[0x92] Val=25 [5->200,25] MId=0x1200 ] + --L[#2 T=V_i8 VId=0x1213 Text="Stop"[0xD8] Val=25 [5->200,25] MId=0x1200 ] + --L[#3 T=V_nc VId=0x1214 Text=" FLIGHT MODE"[0x8000] Val=1 [0->5,0] MId=0x1200 ] + --L[#4 T=V_i8 VId=0x1215 Text="Proportional"[0x71] Val=100 [0->255,100] MId=0x1200 ] + --L[#5 T=V_i8 VId=0x1216 Text="Integral"[0x72] Val=95 [0->255,95] MId=0x1200 ] + --L[#6 T=V_i8 VId=0x1217 Text="Derivate"[0x73] Val=45 [0->255,45] MId=0x1200 ] + + ctx.Menu = { MenuId = 0x1200, TextId = 0xDD, PrevId = 0, NextId = 0, BackId = 0x1000 } + ctx.MenuLines[0] = { Type = LINE_TYPE.VALUE_NUM_I16, TextId = 0xDC, ValId = 0x1211, Min=0, Max=1280, Def=550, Val=550 } + ctx.MenuLines[1] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x92, ValId = 0x1212, Min=5, Max=200, Def=25, Val=25 } + ctx.MenuLines[2] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0xD8, ValId = 0x1213, Min=5, Max=200, Def=26, Val=100 } + + ctx.MenuLines[3] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x8000, ValId = 0x1214, Min=0, Max=5, Def=0, Val=1 } + + ctx.MenuLines[4] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x71, ValId = 0x1215, Min=0, Max=255, Def=100, Val=100 } + ctx.MenuLines[5] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x72, ValId = 0x1216, Min=0, Max=255, Def=95, Val=95 } + ctx.MenuLines[6] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x73, ValId = 0x1217, Min=0, Max=255, Def=45, Val=45 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1280) then -- TODO + --M[Id=0x1200 P=0x0 N=0x0 B=0x1000 Text="Governor"[0xF0]] + --L[#4 T=V_i8 VId=0x1215 Text="Proportional"[0x71] Val=100 [0->255,100] MId=0x1200 ] + --L[#5 T=V_i8 VId=0x1216 Text="Integral"[0x72] Val=95 [0->255,95] MId=0x1200 ] + --L[#3 T=V_nc VId=0x1214 Text=" FLIGHT MODE"[0x8000] Val=1 [0->5,0] MId=0x1200 ] + --L[#6 T=V_i8 VId=0x1217 Text="Head Speed"[0x26B] Val=45 [0->255,45] MId=0x1200 ] + + ctx.Menu = { MenuId = 0x1280, TextId = 0xDD, PrevId = 0, NextId = 0, BackId = 0x1000 } + ctx.MenuLines[0] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x92, ValId = 0x1212, Min=5, Max=200, Def=25, Val=25 } + ctx.MenuLines[1] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0xD8, ValId = 0x1213, Min=5, Max=200, Def=26, Val=100 } + + ctx.MenuLines[5] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x8000, ValId = 0x1214, Min=0, Max=5, Def=0, Val=1 } + + ctx.MenuLines[6] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x71, ValId = 0x1215, Min=0, Max=255, Def=100, Val=100 } + + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1300) then + --M[Id=0x1300 P=0x0 N=0x0 B=0x1000 Text="Setup"[0xDE]] + --L[#0 T=M VId=0x1310 Text="Swashplate"[0xD3] MId=0x1300 ] + --L[#1 T=M VId=0x1360 Text="Tail rotor"[0xDD] MId=0x1300 ] + + -- L[#2 T=M VId=0x13C0 Text="Throttle"[0x201] MId=0x1300 A=0x0] + -- L[#3 T=M VId=0x13B0 Text="Gyro settings"[0xF9] MId=0x1300 A=0x0] + + --L[#4 T=LM_nc VId=0x1701 Text="FM Channel"[0x78] val=1 NL=(0->8,1,S=12) [12->20,13] MId=0x1300 ] + --L[#5 T=LM_nc VId=0x1702 Text="Gain Channel"[0x89] val=0 NL=(0->8,1,S=12)] [12->20,13] MId=0x1300 ] + --L[#6 T=LM_nc VId=0x1703 Text="Output Channel 6"[0x56] val=1 NL=(0->12,0,S=53) [53->65,53] MId=0x1300 ] + + ctx.Menu = { MenuId = 0x1300, TextId = 0xDE, PrevId = 0, NextId = 0, BackId = 0x1000 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0xD3, ValId = 0x1310 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0xDD, ValId = 0x1360 } + + if (not RX_Initialized) then + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x201, ValId = 0x13C0 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0xF9, ValId = 0x13B0 } + end + + ctx.MenuLines[4] = { Type = LINE_TYPE.LIST_MENU_NC, TextId = 0x78, ValId = 0x1701, Min=12, Max=20, Def=13, Val=1 } + ctx.MenuLines[5] = { Type = LINE_TYPE.LIST_MENU_NC, TextId = 0x89, ValId = 0x1702, Min=12, Max=20, Def=13, Val=0 } + ctx.MenuLines[6] = { Type = LINE_TYPE.LIST_MENU_NC, TextId = 0x56, ValId = 0x1702, Min=53, Max=65, Def=53, Val=1 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1310) then + --M[Id=0x1310 P=0x0 N=0x0 B=0x1300 Text="Swashplate"[0xD3]] + --L[#0 T=M VId=0x1330 Text="Output Setup"[0x49] MId=0x1310 ] + --L[#1 T=M VId=0x1320 Text="AFR"[0xDF] MId=0x1310 ] + --L[#2 T=V_% VId=0x1311 Text="E-Ring"[0xE4] Val=100 [50->150,100] MId=0x1310 ] + --L[#3 T=V_% VId=0x1312 Text="Phasing"[0xE2] Val=0 [-45->45,0] MId=0x1310 ] + --L[#4 T=V_% VId=0x1313 Text="Decay"[0x208] Val=50 [0->100,50] MId=0x1310 ] + + ctx.Menu = { MenuId = 0x1310, TextId = 0xD3, PrevId = 0, NextId = 0, BackId = 0x1300 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x49, ValId = 0x1330 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0xDF, ValId = 0x1320 } + ctx.MenuLines[4] = { Type = LINE_TYPE.VALUE_PERCENT, TextId = 0xE4, ValId = 0x1311, Min=50, Max=150, Def=100, Val=100 } + ctx.MenuLines[5] = { Type = LINE_TYPE.VALUE_PERCENT, TextId = 0xE2, ValId = 0x1312, Min=-45, Max=45, Def=0, Val=0 } + ctx.MenuLines[6] = { Type = LINE_TYPE.VALUE_PERCENT, TextId = 0x208, ValId = 0x1313, Min=0, Max=100, Def=50, Val=50 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1320) then + --M[Id=0x1320 P=0x0 N=0x0 B=0x1310 Text="AFR"[0xDF]] + --L[#0 T=V_% VId=0x1321 Text="Roll"[0x40] Val=-75 % [-127->127,-75] MId=0x1320 ] + --L[#1 T=V_% VId=0x1322 Text="Pitch"[0x41] Val=-75 % [-127->127,-75] MId=0x1320 ] + --L[#2 T=V_% VId=0x1323 Text="Collective"[0xE0] Val=45 % [5->127,45] MId=0x1320 ] + --L[#3 T=V_% VId=0x1324 Text="Differential"[0x45] Val=0 % [-25->25,0] MId=0x1320 ] + + ctx.Menu = { MenuId = 0x1320, TextId = 0xDF, PrevId = 0, NextId = 0, BackId = 0x1310 } + ctx.MenuLines[0] = { Type = LINE_TYPE.VALUE_PERCENT, TextId = 0x40, ValId = 0x1321, Min=-127, Max=127, Def=-75, Val=-75 } + ctx.MenuLines[1] = { Type = LINE_TYPE.VALUE_PERCENT, TextId = 0x41, ValId = 0x1322, Min=-127, Max=127, Def=-75, Val=-75 } + ctx.MenuLines[2] = { Type = LINE_TYPE.VALUE_PERCENT, TextId = 0xE0, ValId = 0x1323, Min=5, Max=127, Def=45, Val=45 } + ctx.MenuLines[3] = { Type = LINE_TYPE.VALUE_PERCENT, TextId = 0x45, ValId = 0x1324, Min=-25, Max=25, Def=0, Val=0 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1330) then + --M[Id=0x1330 P=0x0 N=0x0 B=0x1310 Text="Output Setup"[0x49]] + + ---- Full version + --L[#0 T=LM_nc2 VId=0x1331 Text="Frame Rate"[0x85] Val=nil NL=(0->5,0,S=136) [136->141,136] MId=0x1330 A=0x0] + --L[#1 T=M VId=0x1334 Text="Swash Type"[0xE5] MId=0x1330 A=0x0] + --L[#2 T=M VId=0x1332 Text="Direction"[0xF6] MId=0x1330 A=0x0] + + --L[#3 T=M VId=0x1331 Text="Subtrim"[0xE1] MId=0x1330 ] + + ctx.Menu = { MenuId = 0x1330, TextId = 0x49, PrevId = 0, NextId = 0, BackId = 0x1310 } + + if (not RX_Initialized) then + ctx.MenuLines[0] = { Type = LINE_TYPE.LIST_MENU_NC, TextId = 0x85, ValId = 0x1331, Min=136, Max=141, Def=136, Val=0 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0xE5, ValId = 0x1334 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0xF6, ValId = 0x1332 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0xE1, ValId = 0x1331 } + ctx.SelLine = 0 + else + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0xE1, ValId = 0x1331 } + ctx.SelLine = 0 + end + + lastGoodMenu = menuId + elseif (menuId==0x1331) then + --M[Id=0x1331 P=0x0 N=0x0 B=0x1330 Text="Subtrim"[0xE1]] + --L[#0 T=V_s16 VId=0x1332 Text="Output Channel 1"[0x51] Val=57 [-82->82,0] MId=0x1331 ] + --L[#1 T=V_s16 VId=0x1333 Text="Output Channel 2"[0x52] Val=-17 [-82->82,0] MId=0x1331 ] + --L[#2 T=V_s16 VId=0x1334 Text="Output Channel 3"[0x53] Val=-53 [-82->82,0] MId=0x1331 ] + + ctx.Menu = { MenuId = 0x1331, TextId = 0xE1, PrevId = 0, NextId = 0, BackId = 0x1330 } + ctx.MenuLines[0] = { Type = LINE_TYPE.VALUE_NUM_SI16, TextId = 0x51, ValId = 0x1332, Min=-82, Max=82, Def=0, Val=57 } + ctx.MenuLines[1] = { Type = LINE_TYPE.VALUE_NUM_SI16, TextId = 0x52, ValId = 0x1333, Min=-82, Max=82, Def=0, Val=-17 } + ctx.MenuLines[2] = { Type = LINE_TYPE.VALUE_NUM_SI16, TextId = 0x53, ValId = 0x1334, Min=-82, Max=82, Def=0, Val=-53 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1332) then + --M[Id=0x1332 P=0x0 N=0x0 B=0x1330 Text="Direction"[0xF6]] + --L[#0 T=LM_tog VId=0x1333 Text="Output Channel 1"[0x51] Val=nil NL=(0->1,0,S=142) [142->143,142] MId=0x1332 A=0x0] + --L[#1 T=LM_tog VId=0x1334 Text="Output Channel 2"[0x52] Val=nil NL=(0->1,0,S=142) [142->143,142] MId=0x1332 A=0x0] + --L[#2 T=LM_tog VId=0x1335 Text="Output Channel 3"[0x53] Val=nil NL=(0->1,0,S=142) [142->143,142] MId=0x1332 A=0x0] + + ctx.Menu = { MenuId = 0x1332, TextId = 0xF6, PrevId = 0, NextId = 0, BackId = 0x1330 } + ctx.MenuLines[0] = { Type = LINE_TYPE.LIST_MENU_TOG, TextId = 0x51, ValId = 0x1333, Min=142, Max=143, Def=142, Val=0 } + ctx.MenuLines[1] = { Type = LINE_TYPE.LIST_MENU_TOG, TextId = 0x52, ValId = 0x1334, Min=142, Max=143, Def=142, Val=0 } + ctx.MenuLines[2] = { Type = LINE_TYPE.LIST_MENU_TOG, TextId = 0x53, ValId = 0x1335, Min=142, Max=143, Def=142, Val=0 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1334) then + -- M[Id=0x1334 P=0x0 N=0x0 B=0x1330 Text="Swashplate"[0xD3]] + -- L[#6 T=LM_tog VId=0x1335 Text="Swash Type"[0xE5] Val=nil NL=(0->8,0,S=144) [144->152,144] MId=0x1334 A=0x0] + + ctx.Menu = { MenuId = 0x1334, TextId = 0xD3, PrevId = 0, NextId = 0, BackId = 0x1330 } + ctx.MenuLines[6] = { Type = LINE_TYPE.LIST_MENU_NC, TextId = 0xE5, ValId = 0x1335, Min=144, Max=152, Def=144, Val=0 } + + ctx.SelLine = 6 + lastGoodMenu = menuId + elseif (menuId==0x1360) then + --M[Id=0x1360 P=0x0 N=0x0 B=0x1300 Text="Tail rotor"[0xDD]] + --L[#0 T=M VId=0x1390 Text="Advanced Setup"[0x99] MId=0x1360 ] + + ctx.Menu = { MenuId = 0x1360, TextId = 0xDD, PrevId = 0, NextId = 0, BackId = 0x1000 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x99, ValId = 0x1390 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1390) then + --M[Id=0x1390 P=0x0 N=0x0 B=0x1360 Text="Phasing"[0xE2]] + --L[#0 T=V_% VId=0x1311 Text="Left"[0xE7] Val=0 % [-45->45,0] MId=0x1390 ] + --L[#1 T=V_% VId=0x1312 Text="Right"[0xE8] Val=0 % [-45->45,0] MId=0x1390 ] + + ctx.Menu = { MenuId = 0x1390, TextId = 0xE2, PrevId = 0, NextId = 0, BackId = 0x1360 } + ctx.MenuLines[0] = { Type = LINE_TYPE.VALUE_PERCENT, TextId = 0xE2, ValId = 0x1311, Min=-45, Max=45, Def=0, Val=0 } + ctx.MenuLines[1] = { Type = LINE_TYPE.VALUE_PERCENT, TextId = 0xE8, ValId = 0x1312,Min=-45, Max=45, Def=0, Val=0 } + + + ctx.SelLine = 0 + lastGoodMenu = menuId + + elseif (menuId==0x13B0) then + -- M[Id=0x13B0 P=0x0 N=0x0 B=0x1300 Text="Gyro settings"[0xF9]] + -- L[#0 T=M VId=0x13B1 Text="Orientation"[0x80] MId=0x13B0 A=0x0] + + ctx.Menu = { MenuId = 0x13B0, TextId = 0xF9, PrevId = 0, NextId = 0, BackId = 0x1300 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x80, ValId = 0x13B1 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x13B1) then + -- M[Id=0x13B1 P=0x0 N=0x0 B=0x13B5 Text="Gyro settings"[0xF9]] + --L[#6 T=LM_ori VId=0x13B2 Text="Orientation"[0x80] Val=nil NL=(0->7,0,S=203) [203->210,203] MId=0x13B1 A=0x0] + + ctx.Menu = { MenuId = 0x13B1, TextId = 0xF9, PrevId = 0, NextId = 0, BackId = 0x13B5 } + ctx.MenuLines[6] = { Type = LINE_TYPE.LIST_MENU_ORI, TextId = 0x80, ValId = 0x13B2, Min=203, Max=210, Def=203, Val=0 } + + ctx.SelLine = 6 + lastGoodMenu = menuId + elseif (menuId==0x13B5) then + --M[Id=0x13B5 P=0x0 N=0x0 B=0x13B0 Text="Calibrate Sensor"[0xC7]] + --L[#3 T=M VId=0x13B6 Text="Begin"[0x91] MId=0x13B5 A=0x0] + + ctx.Menu = { MenuId = 0x13B5, TextId = 0xC7, PrevId = 0, NextId = 0, BackId = 0x13B0 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x91, ValId = 0x13B6 } + + ctx.SelLine = 3 + lastGoodMenu = menuId + elseif (menuId==0x13B6) then + --M[Id=0x13B6 P=0x0 N=0x0 B=0x13B0 Text="Calibrate Sensor"[0xC7]] + --L[#3 T=M VId=0x13B0 Text="Sensor is Calibrating.. Wait"[0xC8] MId=0x13B6 A=0x0] + + ctx.Menu = { MenuId = 0x13B6, TextId = 0xC7, PrevId = 0, NextId = 0, BackId = 0x13B0 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0xC8, ValId = 0x17F1 } + + ctx.SelLine = 3 + lastGoodMenu = menuId + elseif (menuId==0x13C0) then + --M[Id=0x13C0 P=0x0 N=0x0 B=0x1300 Text="Throttle"[0x201]] + --L[#0 T=M VId=0x13C1 Text="Failsafe"[0x4A] MId=0x13C0 A=0x0] + --L[#1 T=V_% VId=0x13C2 Text="Hover"[0x204] Val=nil [0->100,65] MId=0x13C0 A=0x10] + --L[#2 T=M VId=0x13D0 Text="Governor"[0xF2] MId=0x13C0 A=0x0] + --L[#4 T=V_nc VId=0x13C3 Text="Flight Mode"[0x8000] Val=nil [0->5,0] MId=0x13C0 A=0x5] + --L[#5 T=V_% VId=0x13C4 Text="Offset"[0x1AA] Val=nil [-25->25,0] MId=0x13C0 A=0x10] + --L[#6 T=V_i8 VId=0x13C5 Text="Soft Start"[0xF4] Val=nil [0->250,0] MId=0x13C0 A=0x0] + + ctx.Menu = { MenuId = 0x13C0, TextId = 0x201, PrevId = 0, NextId = 0, BackId = 0x1300 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0x4A, ValId = 0x13C1 } + ctx.MenuLines[1] = { Type = LINE_TYPE.VALUE_PERCENT, TextId = 0x204, ValId = 0x13C2, Min=0, Max=100, Def=65, Val=65 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0xF2, ValId = 0x13D0 } + + ctx.MenuLines[4] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x8000, ValId = 0x13C3, Min=0, Max=5, Def=0, Val=1 } + ctx.MenuLines[5] = { Type = LINE_TYPE.VALUE_PERCENT, TextId = 0x1AA, ValId = 0x13C4, Min=-25, Max=25, Def=0, Val=0 } + ctx.MenuLines[6] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0xF4, ValId = 0x13C5, Min=0, Max=250, Def=0, Val=1 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x13C1) then + --M[Id=0x13C1 P=0x0 N=0x0 B=0x13C0 Text="Failsafe"[0x4A]] + --L[#2 T=M VId=0x13C3 Text="Capture Failsafe Positions"[0x9A] MId=0x13C1 A=0x0] + + ctx.Menu = { MenuId = 0x13C1, TextId = 0x4A, PrevId = 0, NextId = 0, BackId = 0x13C0 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x9A, ValId = 0x13C3 } + + ctx.SelLine = 2 + lastGoodMenu = menuId + elseif (menuId==0x13D0) then + --M[Id=0x13D0 P=0x0 N=0x0 B=0x13C0 Text="Governor"[0xF2]] + --L[#0 T=LM_ori VId=0x13D1 Text="Governor"[0xF2] Val=nil NL=(0->1,0,S=244) [244->245,244] MId=0x13D0 A=0x0] + --L[#1 T=V_i8 VId=0x13D2 Text="Main Gear"[0x26D] Val=nil [1->255,170] MId=0x13D0 A=0x0] + --L[#2 T=V_i8 VId=0x13D3 Text="Pinion"[0x26C] Val=nil [1->255,20] MId=0x13D0 A=0x0] + --L[#3 T=V_i8 VId=0x13D5 Text="Low Throttle"[0xEA] Val=nil [1->100,75] MId=0x13D0 A=0x0] + --L[#4 T=V_i8 VId=0x13D6 Text="Filter"[0x1F1] Val=nil [1->65,35] MId=0x13D0 A=0x0] + --L[#5 T=M VId=0x13E0 Text="RPM Sensor"[0x26F] MId=0x13D0 A=0x0] + + + ctx.Menu = { MenuId = 0x13D0, TextId = 0xF2, PrevId = 0, NextId = 0, BackId = 0x13C0 } + ctx.MenuLines[0] = { Type = LINE_TYPE.LIST_MENU_ORI, TextId = 0x0F2, ValId = 0x13D1, Min=244, Max=245, Def=244, Val=0 } + ctx.MenuLines[1] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x26D, ValId = 0x13D2, Min=1, Max=255, Def=170, Val=170 } + ctx.MenuLines[2] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x26C, ValId = 0x13D3, Min=1, Max=255, Def=20, Val=20 } + ctx.MenuLines[3] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x0EA, ValId = 0x13D5, Min=1, Max=100, Def=75, Val=75 } + ctx.MenuLines[4] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x1F1, ValId = 0x13D6, Min=1, Max=65, Def=35, Val=35 } + ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, TextId = 0x26F, ValId = 0x13E0 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x13E0) then + --M[Id=0x13E0 P=0x0 N=0x0 B=0x13D0 Text="Governor"[0xF2]] + --L[#0 T=LM_ori VId=0x13D1 Text="RPM Sensor"[0x26F] Val=nil NL=(0->1,0,S=244) [244->245,244] MId=0x13D0 A=0x0] + + ctx.Menu = { MenuId = 0x13E0, TextId = 0xF2, PrevId = 0, NextId = 0, BackId = 0x13D0 } + ctx.MenuLines[3] = { Type = LINE_TYPE.LIST_MENU_TOG, TextId = 0x26F, ValId = 0x13E3, Min=142, Max=143, Def=142, Val=0 } + + ctx.SelLine = 3 + lastGoodMenu = menuId + elseif (menuId==0x1400) then + --M[Id=0x1400 P=0x0 N=0x0 B=0x1000 Text="SAFE"[0xDA]] + --L[#0 T=M VId=0x1410 Text="Stability"[0xDB] MId=0x1400 ] + --L[#1 T=M VId=0x140 Text="Panic"[0x8B] MId=0x1400 ] + --L[#2 T=M VId=0x1420 Text="Attitude Trim"[0x1E6] MId=0x1400 ] + + ctx.Menu = { MenuId = 0x1400, TextId = 0xDA, PrevId = 0, NextId = 0, BackId = 0x1000 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0xDB, ValId = 0x1410 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x8B, ValId = 0x140 } + ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, TextId = 0x1E6, ValId = 0x1420 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1410) then + -- M[Id=0x1410 P=0x0 N=0x0 B=0x1400 Text="Stability"[0xDB]] + --L[#0 T=V_i8 VId=0x1411 Text="Gain"[0x43] val=50 [5->200,50] MId=0x1410 ] + --L[#1 T=V_i8 VId=0x1412 Text="Envelope"[0x1E7] val=45 [5->90,45] MId=0x1410 ] + --L[#3 T=V_nc VId=0x1413 Text="FLIGHT MODE"[0x8000] val=1 [0->5,0] MId=0x1410 ] + --L[#4 T=LM_tog VId=0x1414 Text="Stability"[0xDB] val=1 NL=(0->1,1,S=1) [1->2,1] MId=0x1410 ] + + ctx.Menu = { MenuId = 0x1410, TextId = 0xDB, PrevId = 0, NextId = 0, BackId = 0x1400 } + ctx.MenuLines[0] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x43, ValId = 0x1411, Min=0, Max=200, Def=50, Val=50 } + ctx.MenuLines[1] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x1E7, ValId = 0x1412, Min=0, Max=90, Def=45, Val=45 } + ctx.MenuLines[3] = { Type = LINE_TYPE.VALUE_NUM_I8_NC, TextId = 0x8000, ValId = 0x1413, Min=0, Max=5, Def=0, Val=1 } + ctx.MenuLines[4] = { Type = LINE_TYPE.LIST_MENU_TOG, TextId = 0xDB, ValId = 0x1414, Min=1, Max=2, Def=1, Val=1 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + + elseif (menuId==0x140) then + --M[Id=0x140 P=0x0 N=0x0 B=0x1400 Text="Panic"[0x8B]] + --L[#0 T=V_i8 VId=0x141 Text="Envelope"[0x1E7] val=30 [5->90,45] MId=0x140 ] + --L[#1 T=V_i8 VId=0x142 Text="Yaw"[0x42] val=30 [25->100,50] MId=0x140 ] + + ctx.Menu = { MenuId = 0x140, TextId = 0x8B, PrevId = 0, NextId = 0, BackId = 0x1400 } + ctx.MenuLines[0] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x1E7, ValId = 0x141, Min=5, Max=90, Def=45, Val=30 } + ctx.MenuLines[1] = { Type = LINE_TYPE.VALUE_NUM_I8, TextId = 0x42, ValId = 0x142, Min=25, Max=100, Def=50, Val=30 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1420) then + --M[Id=0x1420 P=0x0 N=0x0 B=0x1400 Text="Attitude Trim"[0x1E6]] + --L[#0 T=V_s16 VId=0x1421 Text="Roll"[0x40] val=274 [-850->850,450] MId=0x1420 ] + --L[#1 T=V_s16 VId=0x1422 Text="Pitch"[0x41] val=58 [-850->850,0] MId=0x1420 ] + + ctx.Menu = { MenuId = 0x1420, TextId = 0x1E6, PrevId = 0, NextId = 0, BackId = 0x1400 } + ctx.MenuLines[0] = { Type = LINE_TYPE.VALUE_NUM_SI16, TextId = 0x40, ValId = 0x1421, Min=-850, Max=850, Def=450, Val=274 } + ctx.MenuLines[1] = { Type = LINE_TYPE.VALUE_NUM_SI16, TextId = 0x41, ValId = 0x1422, Min=-850, Max=850, Def=0, Val=58 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1700) then + --M[Id=0x1700 P=0x0 N=0x0 B=0x1000 Text="System Setup"[0x86]] + --L[#0 T=M VId=0x17F0 Text="Calibrate Sensor"[0xC7] MId=0x1700 ] + --L[#1 T=M VId=0x17E0 Text="Factory Reset"[0x97] MId=0x1700 ] + + ctx.Menu = { MenuId = 0x1700, TextId = 0x86, PrevId = 0, NextId = 0, BackId = 0x1000 } + ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, TextId = 0xC7, ValId = 0x17F0 } + ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, TextId = 0x97, ValId = 0x17E0 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x17E0) then + --M[Id=0x17E0 P=0x0 N=0x0 B=0x1700 Text="Factory Reset"[0x98]] + --[#3 T=M VId=0x17E1 Text="Apply"[0x90] MId=0x17E0 ] + + ctx.Menu = { MenuId = 0x17E0, TextId = 0x98, PrevId = 0, NextId = 0, BackId = 0x1700 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x90, ValId = 0x17E1 } + + ctx.SelLine = 3 + lastGoodMenu = menuId + elseif (menuId==0x17F0) then + --M[Id=0x17F0 P=0x0 N=0x0 B=0x1700 Text="Calibrate Sensor"[0xC7]] + --L[#3 T=M VId=0x17F1 Text="Begin"[0x91] MId=0x17F0 ] + + ctx.Menu = { MenuId = 0x17F0, TextId = 0xC7, PrevId = 0, NextId = 0, BackId = 0x1700 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x91, ValId = 0x17F1 } + + ctx.SelLine = 3 + lastGoodMenu = menuId + elseif (menuId==0x17F1) then + --M[Id=0x17F1 P=0x0 N=0x0 B=0x1700 Text="Calibrate Sensor"[0xC7]] + --L[#3 T=M VId=0x17F1 Text="Complete"[0x93] MId=0x17F0 ] + + ctx.Menu = { MenuId = 0x17F1, TextId = 0xC7, PrevId = 0, NextId = 0, BackId = 0x1700 } + ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, TextId = 0x93, ValId = 0x1700 } + + ctx.SelLine = 3 + lastGoodMenu = menuId + elseif (menuId==0x0001) then + -- Save Settings and Reboot + ctx.Menu = { MenuId = 0x0001, TextId = 0x009F, PrevId = 0, NextId = 0, BackId = 0x1000 } + ctx.SelLine = menuLib.BACK_BUTTON + else + ctx.Menu = { MenuId = 0x0002, Text = "NOT IMPLEMENTED", TextId = 0, PrevId = 0, NextId = 0, BackId = lastGoodMenu } + ctx.SelLine = menuLib.BACK_BUTTON + end + + menuLib.PostProcessMenu() +end + + +local function loadMenu(menuId) + menuLib.clearMenuLines() + local ctx = menuLib.DSM_Context + + if (menuId==0x1000) then + --M[Id=0x1000 P=0x0 N=0x0 B=0x0 Text="Main Menu"] + --L[#0 T=M VId=0x1010 val=nil [0->0,3] Text="Gyro settings" MId=0x1000 ] + --L[#1 T=M VId=0x105E val=nil [0->0,2] Text="Other settings" MId=0x1000 ] + + ctx.Menu = { MenuId = 0x1000, Text = "RX SIMULATION", PrevId = 0, NextId = 0, BackId = 0, TextId=0 } + ctx.MenuLines[0] = { MenuId = 0x1000, Type = LINE_TYPE.MENU, Text = "AR630/631/637 (NEW)", ValId = 0x1001,TextId=0 } + ctx.MenuLines[1] = { MenuId = 0x1000, Type = LINE_TYPE.MENU, Text = "AR630/631/637 (INITIALIZED)", ValId = 0x1002, TextId=0 } + ctx.MenuLines[4] = { MenuId = 0x1000, Type = LINE_TYPE.MENU, Text = "FC6250HX", ValId = 0x1005, TextId=0 } + ctx.MenuLines[5] = { MenuId = 0x1000, Type = LINE_TYPE.MENU, Text = "FC6250HX (UNLOCKED)", ValId = 0x1006, TextId=0 } + + ctx.SelLine = 0 + lastGoodMenu = menuId + elseif (menuId==0x1001) then + RX_Initialized = false + ctx.RX.Id = menuLib.RX.AR631 + ctx.RX.Name = "AR630/631/637-SIM" + ctx.RX.Version = "2.38.5" + RX_loadMenu = AR631_loadMenu + RX_loadMenu(0x01000) + elseif (menuId==0x1002) then + RX_Initialized = true + ctx.RX.Id = menuLib.RX.AR631 + ctx.RX.Name = "AR630/631/637-SIM" + ctx.RX.Version = "2.38.5" + RX_loadMenu = AR631_loadMenu + RX_loadMenu(0x01000) + elseif (menuId==0x1005) then + RX_Initialized = true + ctx.RX.Id = menuLib.RX.FC6250HX + ctx.RX.Name = "FC6250HX-SIM" + ctx.RX.Version = "5.6.255" + + RX_loadMenu = FC6250HX_loadMenu + RX_loadMenu(0x01000) + elseif (menuId==0x1006) then + RX_Initialized = false + ctx.RX.Id = menuLib.RX.FC6250HX + ctx.RX.Name = "FC6250HX-SIM" + ctx.RX.Version = "5.6.52" + + RX_loadMenu = FC6250HX_loadMenu + RX_loadMenu(0x01000) + end +end + + + + +local function SIM_Send_Receive() + local ctx = menuLib.DSM_Context + --if (DEBUG_ON) then Log.LOG_write("%3.3f %s: ", menuLib.getElapsedTime(), menuLib.phase2String(ctx.Phase)) end + + if ctx.Phase == PHASE.RX_VERSION then -- request RX version + ctx.RX.Name = "SIMULATOR" + ctx.RX.Version = SIM_LIB_VERSION + ctx.Phase = PHASE.MENU_TITLE + ctx.Menu.MenuId=0 + + ctx.Refresh_Display = true + RX_loadMenu = loadMenu + + elseif ctx.Phase == PHASE.WAIT_CMD then + + elseif ctx.Phase == PHASE.MENU_TITLE then -- request menu title + if ctx.Menu.MenuId == 0 then -- First time loading a menu ? + RX_loadMenu(0x01000) + else + RX_loadMenu(ctx.Menu.MenuId) + end + ctx.Phase = PHASE.WAIT_CMD + ctx.Refresh_Display = true + + elseif ctx.Phase == PHASE.VALUE_CHANGING then -- send value + local line = ctx.MenuLines[ctx.SelLine] -- Updated Value of SELECTED line + if (DEBUG_ON) then Log.LOG_write("%3.3f %s: ", menuLib.getElapsedTime(), menuLib.phase2String(ctx.Phase)) end + if (DEBUG_ON) then Log.LOG_write("SEND SIM_updateMenuValue(ValueId=0x%X Text=\"%s\" Value=%s)\n", line.ValId, line.Text, menuLib.lineValue2String(line)) end + ctx.Phase = PHASE.VALUE_CHANGING_WAIT + + elseif ctx.Phase == PHASE.VALUE_CHANGING_WAIT then + local line = ctx.MenuLines[ctx.SelLine] + + elseif ctx.Phase == PHASE.VALUE_CHANGE_END then -- send value + local line = ctx.MenuLines[ctx.SelLine] -- Updated Value of SELECTED line + if (DEBUG_ON) then Log.LOG_write("%3.3f %s: ", menuLib.getElapsedTime(), menuLib.phase2String(ctx.Phase)) end + if (DEBUG_ON) then Log.LOG_write("SEND SIM_updateMenuValue(ValueId=0x%X Text=\"%s\" Value=%s)\n", line.ValId, line.Text, menuLib.lineValue2String(line)) end + if (DEBUG_ON) then Log.LOG_write("SEND SIM_validateMenuValue(ValueId=0x%X Text=\"%s\" Value=%s)\n", line.ValId, line.Text, menuLib.lineValue2String(line)) end + ctx.Phase = PHASE.WAIT_CMD + + elseif ctx.Phase == PHASE.EXIT then + ctx.Phase=PHASE.EXIT_DONE + end +end + +local FileState = {} + +-- Initial Setup +local function Sim_Init() + local ctx = menuLib.DSM_Context + ctx.Phase = PHASE.INIT + + local ver, radio, maj, minor, rev, osname = getVersion() + if (osname==nil) then osname = "OpenTX" end -- OTX 2.3.14 and below returns nil + IS_EDGETX = string.sub(osname,1,1) == 'E' +end + +local function SIM_Done() + local ctx = menuLib.DSM_Context + ctx.Phase = PHASE.EXIT_DONE +end + + +local function SIM_Run() + if (menuLib.DSM_Context.Phase == PHASE.INIT) then + if (IS_EDGETX) then + menuLib.LoadTextFromFile(MSG_FILE,13) + menuLib.DSM_Context.Phase = PHASE.RX_VERSION + else -- Incremental initialization + lcd.clear() + lcd.drawText(30, 50, "Loading Msg file: "..(FileState.lineNo or 0)) + if (menuLib.INC_LoadTextFromFile(MSG_FILE, FileState)==1) then + menuLib.DSM_Context.Phase = PHASE.RX_VERSION + end + return + end + end + + return SIM_Send_Receive() +end + +return { init=Sim_Init, run=SIM_Run, done=SIM_Done } diff --git a/SCRIPTS/TOOLS/DSMLIB/MIN_msg_fwdp_en.txt b/SCRIPTS/TOOLS/DSMLIB/MIN_msg_fwdp_en.txt new file mode 100644 index 0000000..8d83314 --- /dev/null +++ b/SCRIPTS/TOOLS/DSMLIB/MIN_msg_fwdp_en.txt @@ -0,0 +1,10 @@ +-- OVERRIDES Messges for MIN 128x64 screns +-- FORMAT || +-- Line Type: Text for Menus (T), List_Text Options (LT), List_Text_Image (LI), Flight Mode (FM), RX Name (RX) +-- IMPORTANT: NO EMPTY LINES +-- +T |0x0097|DONT USE: Factory Reset +T |0x0098|DONT USE: Factory Reset +T |0x00A5|DONT USE: First Time Setup +T |0x0190|DONT USE: Relearn Servo Settings +T |0x020D|DONT USE: First Time SAFE Setup diff --git a/SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_1.png b/SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_1.png new file mode 100644 index 0000000000000000000000000000000000000000..2fbc84df83b1529b030d0cd87497e3e967011c93 GIT binary patch literal 1408 zcmcJPeKgYx7{`BO2zM_|s_W%$t0*tK(!J7~Vc~8gl9$j*Md)S?F*B^OoJfgVRHoEL zLM$rewR4*`TD6Q>t4-a8Ut?OaI7Yg=_niCx{pbGieZJ@Oob#OL``7nua1a4zW^D!l z0E|TR4bgkQUPo6L>nHCF%Uo}e)DXfhfcwH$r3YvNJ`fK8Eyd1|k0ObFN5?CgKu~`!wv1B2Fi6JtDVoT__TCKiHpB`(l8ahou&e!Uy zO=LGgFrl@*J)!n#3y%24<@S_v0|tiomfLRi!G9bqK_1tv znObJz>xI3w>euLQqOg@TQ(Lm7Cp`-Hl#_W)C=@n7e2g?lB1<^_bDXH5GIA&)ctj!z zk36szlXG|)(h>{C`RzB3+e zH+dZvGsk{nquJ5dPN>F9oJ@%x-&a^73KQMWGlUHn!JiYYsikHGJs)UY!I`WXH~u<` zVW6`(n{q{rBMzRU^5Oy{+EX2HQ_kIF?=+?JN_ZBb^+|Z_$kahPKGkyhaVd3$krziN z`)P_Cf2pWd-SUdrJ~gmYv6@@lb?-*ZcpT!py89uAIwVEiyH6_DrlgoSbg|y_eYyUC zdjL&Wq3lNcXT>J^FRv{xNOliLB?oC4rb3xrL*>Cc)$_XhhaHtZUw`YY^pjk=FQVNT zVWu^y8)aVxt467*=k&=>$aU|gd^MuBe4e|Lb7H3bXz;=W=XuhF(}g#W(5aI8q{$_& zbzV`V^ibXCt6`C5cmW^$L*4{kT0F_e(LOURptp3#DN-h#t9_5>gn@Izl%WWo>TH9@ zdKUA}w|TslXZhZ*X~F*>zn77VQq>+DOl`#{^haLA)Vt<{(Qr`O z#zgl;ZzHeX$kemuaB~A^sPTba;fXriK9SF; zO2nV--FnRg*H2hZM#;hFV9&x!>^nryJk7X=6t?~2rpfn0R%AXWE8RXf#jB^+DP6&B z9Djt8&^tXU2D&_|QfphMyE{Copbxeti^+tUPZs3>1{-}E#faj7F;000f;zBK zP~-y_Oee2qsITDz9)O52IXU{>f2^1X8!$;pNk{4p9svrJR)4BF4H#tpTThomCRxi! VK|W&CN~mv5faDkC%iVRD{TGJUZvX%Q literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_2.png b/SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_2.png new file mode 100644 index 0000000000000000000000000000000000000000..c458f03bf6dfc34d5e591d563b46178f64bc2507 GIT binary patch literal 862 zcmeAS@N?(olHy`uVBq!ia0vp^CxAGGgAGU?ZmZ`8QjEnx?oJHr&dIz4a#+$GeH|GX zHuiJ>Nn{1`ISV`@iy0XBj({-ZRBb+K1_q|jo-U3d6>)FxKFphKAi;VeZU1kD>N9rF z*|TbALHy0D;oGg7t!}*jIU`!{)vnE1wq-0!l8YN9^u>c;pG#Q#*o^&U#LgRs zx$U<5R5LLhe6~GetB79Vw)xW@-)`IaG%4$@mEMS?-@plegWAfB)*|i=Q!5?9P47&zt@6`*o{#X`)L?Hk9q!rpxm^ z{5Qi|la&gl5_@kfviEMT@ZbN+?p5)#Y27PJ_iEP8d42zm+2i-?OYiWrE_vSlaccL5 zX<@(R=HK7>bkREN)z_~7dDx&+`|F3(HoFavCjPTcTK`v!>C(47`g|uAKa{X9?{(bz zJR!4I=gndER)IHS6E@%HD&X0D*0C$mXWmYOHbDp`kiGqGLt)UbW-fLD%l;!UomQ)dLZ{MEo;Mh z!M1aI|Ln2-_v`x)-#4EuJtW_(^vz|i{@`hKTSIhN*i2@@33K=C-ybhsD=aW==4uZ1 zwGzpzS(f-5YR}Dz&6=NE-+%A9-um_Hue;q3<2+GNba&IoUYR9_b{xHR^7H4_{ zW-@*H_^~io_s8FJp935fKD>S>umYMw=T}wzVm4UK-~8LsS{|4u7(8A5T-G@yGywoS Cg>qE@ literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_3.png b/SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_3.png new file mode 100644 index 0000000000000000000000000000000000000000..b9aef9ac7cfcdb5da2656d9d1900de1dcdb31b16 GIT binary patch literal 1162 zcmeAS@N?(olHy`uVBq!ia0vp^CxAGGgAGU?ZmZ`8QjEnx?oJHr&dIz4a#+$GeH|GX zHuiJ>Nn{1`ISV`@iy0XBj({-ZRBb+Kpc;8k7srr_xVLwG{cbxjv^^|rJ<^su#al(a zK~#I&-GiG}ztcJ>I7wBeF*N)s%Uez@G2gy}Y@Cj^o$iG(pDkxf{y*~B?vjR~pUvZs zHFDOB6ZbynX^5KN<<4Nr!J!}^;Gm$;;NZZ}*ucQV$jHLP#KFQMKwXIs`Zt6orbd3$ z@!fZLj^q+|eSQ6-#yq0N9{o8Eef|C2OUrgjs$6^6w|8&tl=Q>PemUPh$#Y5hobO?G zhX+U7cCT;Py=(W1`QB?>zEpZ%QPoV9nJUVN~*?&0`!^Ot1}?}M(cTYtFj4yW4IeY|(`^)^DhYU%K8)4^D)2;bAz z(dW)?sd&H2=Xu)BxWs3dFP-l;n!EmbU*vPfm1RX+S6pA<+J9Zmy4GNN_u1`Nx=*uz zUMkYGdj9OvZGV3K+8JRSxAxwW>%6Zfyh_T~S6%o1)$i9AW73}=d&0QGTDxw`(Q^vR zpS}F;{<_`!efj1v`)6BwS4|Try{&zn-T(CdXZ<^k#M#Y$>=w{fC|&r%X{TM&qs)&f zb>jY0e}6uEQseZx&z0$a7O^am>9L#Id}-TRhnwqVHodK$U_JRzi?E15`GwznZlU6e zH+iP~?~ampHqG%p-Fr^&6Ze|x&&sWuiT($TJKj5%vlqT^-l=?ho!K^Pzw~Ffb0)<* z`TG8xeEVt5tRwC_gX|{kVcE0w+g`=nzdP=x#iwSJWnND*yLpS+IO~*kYxnH|`XN32{Pt`6%u}l6Uly}o`-u=+*L z{i(m#XG;ES+NZIFUGYgzh+RkSoSRO!%|q7Rzq8L&H@ei``{3KRZ?8Sx*OV@B{iEHR zgGH)lx7TgtYs;E%3XHue@urfu5@d=kYL;3xx4bK#QY{d@Gq)q#Cq72{m>tKe+PhnL z>M!P9db>b}eQ9@d$lB9e89SDKwcojxyVHN_#%+_euGg>p8u_AbyPUx3xp(L8I(ScI zE3>pTqve7IlbheaX1F>0`L=tzK3Cz5HwTKF*JrPLr}s)-!M3utHdd5JdU}Ly;X?~6 zt0>!TYr8kUo$#}7c^K2BYX@&EU-@pUv#iAV^XFF|pFDZ;)#Te8CmNWHSXfvLfw}2{ znyi4Eb#sW};sfl)911GN%?x%k92pdMHr(Kx>?yoEiH(uvog+h|B$2sHac0Awy?fVs u@%&*bu>u;@xSySoB@LQu8~7zZ^FJ`&?0tr9mJ+c1VDNPHb6Mw<&;$T*fbYNn literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_4.png b/SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_4.png new file mode 100644 index 0000000000000000000000000000000000000000..942767006517af78e920270cbb306c734de2c6bd GIT binary patch literal 866 zcmeAS@N?(olHy`uVBq!ia0vp^CxAGGgAGU?ZmZ`8QjEnx?oJHr&dIz4a#+$GeH|GX zHuiJ>Nn{1`ISV`@iy0XBj({-ZRBb+K1_q|@o-U3d6>)FxUd&tMAi;KFSNPr+&2N%_ zUASkGx$AbJY9Z&}0Eq;R=PE9NEi)7M{ach<_3QIfR*5gK%eZ+21UOh&m>3%!92CfA zzOwxPZ~GqwCZ@)Q4fABfBtWLk$XlcIW<$~!P7W5PLuF+FiEvvc@b2Be|Ngs+S|DX+ zPvm*F`vdI;itgKY4=noN%U8P_(uqIXxR@Njaur$4&HMf;_)Wy^to)5$1#*^G7T-Q7 zwdZIP#KmFz*=80CzQ2_+OV!@uX0zqzw2KWqb7w^MuHCcW_SLZkufN}@GBn9^H_AR& zyLZPcp56A6Q_F2L-agLF)e-pnHR;91Yg5enw&v%|{`cARSFEmr?wl5}TNf7D_ITG9 z@k`!b_P?gLE}Zj(^4#dlQg2_f{f~_93GVIOw8j10%^PNapJ#um4s(c-`{B-3qv!TNv*S@@EalKal{PWNC+k?;in^FF1!F0x*@mbn$qmuUPES|Go$~$+mw#Uqx zU}=*tw=94EJ~xeV<=yF)y9zT_`ev`Vm>)0Vv?0=A+n<;F(o^reJD!&_w=yk$$$8EA z@9IUJpX|5S>b^R*=JoX(RfYEF+)dK?eu%tgU`birP!ct}=a8drC{xgu0N)A$&Wv>`tzV?5VbV@P%E&IHU6%JiSJKysgo7_Ck!5n=gg literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_5.png b/SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_5.png new file mode 100644 index 0000000000000000000000000000000000000000..2a526c354606698d9e137aa47ced2e1baf8fb7db GIT binary patch literal 1464 zcmchXdpOez7{`B`+s3ihogAUl&e9ND9PFr#xooE)r>JH`+a%3pHr3oOQMp7^7NHC0 zi3~YztvDoYE^*X|Ve%lC9HB6F?5xiJ=g;%U_x(Pf_j$hWfA9CIyQ{Od<}OVD0JMoN z1P`SPm7+n^mGeb%td`P1;U3QR0RQ#((@Ibcz?1O+P<35P?x&_?XsFA{Z~%a{eii5@ z3Z@4DYGfh-f9#^q+^`*MkF!qOLR%JCU*#Cz)D=KxT7?lt3A#xhV`=-DmAtLnNW85Y z1ig2m;4ttt7Bu4?uG9m%BEfWJ^n6;h>`Kd3bc<1(ZgYbkw+n88{iU6zDu&_%3toUR z1X5&Z7?%(SXoA7}{s$PJnwUUrz#JWMfEq}&x#@KM>Z)ar4L2sn3W-F9F=KC%5F7k_ zdo~@+B0wOZ_!fyx^w?r=CROST0yZksDE%0ToXPxy-*OSUs9PtL9zrA^Lt za?r*vUU!4c21asRtp%2~qQ1P;2;*hPU&PB>Dq+#2Ic(O##DHv*0bBI%6l zXE40>zASkbiE+EBv$)IYV0A~>Ip1+>*538@HJ-(rs|^BdouzTx<=MvJ8rkamnR_le z#TRF1c7ega+LgVMI+kQ-WyM5O4c-;IbeTHa1{1fW%h41z(h_^`=lZ8pgCo8IzQVh4 z$hVa;5)}2Q0a%-$keBtTuPCV>MnMZ9qow!fREE2{(q+T9n8XzFBBpt$gy1w7ug9dD zZr4@l+*vyO(Zd8kc||4BwfJe^5z_7aK>PHz~q`9;A!AODCm`CdG`u;$<7_b1AwA;<9eK!SEuHJ?=-J zZl*ADpLQCGF>i86&!`bhX>48KT9^2+nq&cuNclVghebE;k$j0ZpKBhgdE@hXXX6hoWoj*6;|<1<+HcoSxeiPL*!z<=*)bJ0WzsG1~%rrlm@HI z-sA01(9TsD&119Uglapc9c)^^tZt%Hj4D#&2rJK-)M_u>HF>6ax}QnmpeOi=STs5oYrf+1iRLhOb*$70$H8HE+k2b(a=Dd_r|Wc$Fv>r z?h78ix3E3GH+ZTdXo?>4a?TIDX*?TiTc+QR>q}?E~4Q&#r+#wr2)@49P+2Q zhs4>Bq8qiFGE)-eh1(J3l6;2b@68Jj{3qw{Dk6`UF)ni?Dq0azPcsZ8?NW3!NA0B( zuCgh=(8gYK3!Jxj15r*!l`Tt?kJLH(l!z)0WhVGzg&3FJawb#nvBM=Um*wl`<~H1K zvKndEd!1dJxkAQeKtM)@dZ=(*`+kDgp}`dPw227zL&!*0h8kq*Y(OS`!>Z#@yFa!V z3aQkJfr0mtcEWk)yn|K73^=YC11UPK4%(*r?d$Ph+v2~V7%G%V@=zy758!TVD5Im& jL_kw{=HH+vdNTI7*S3Q1KEFM$Y+Hcn=t|(*pHBD(uUC~S literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_6.png b/SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_6.png new file mode 100644 index 0000000000000000000000000000000000000000..0d6e3d6a5daf3fc1069de3fc06f12adc373f1233 GIT binary patch literal 880 zcmeAS@N?(olHy`uVBq!ia0vp^CxAGGgAGU?ZmZ`8QjEnx?oJHr&dIz4a#+$GeH|GX zHuiJ>Nn{1`ISV`@iy0XBj({-ZRBb+K1_owUPZ!6KinzCTFXk=skzjl9OaAKu@lWx~ z#P3d4^DUPR-=ud?yp_?da$XBZi)>rsy}CuWzkGQ8gjeFr{imW53JL-o94t&sjSUWD zGp`i?`G5YC6AKFyW1_8pL?W26Yh%}k7SYB24hjMS+di|1x`7xRAgP-du3x{N+8f3R zw0DzYeZr1fko7q){{H>D$!#?YOVi<(wHwS26*O}(Iez6TvXIfs{;FBFCU@)h2vv*k zb6(whoWOf8$r0k*)%MM2Dm%XC7@eIYKgXuo@^jk72A;b!BImBXb1#nL#C^+4y{_By zOMY(&Ds79s-*d{BKk9eQ`4#n-tyBNsn9(a+>@OWHx$E7(eHY(e_L{m{`qi0RQ7@dk z^Lo8++M9(XAHQ??SXkDXb}h!t+zHoS_?!Rezm+s6&v#1ovCKC!OpjHqioX80Ba(6D z#m1#Mm5-xln{0mP5%%mZrO`OGJEJCs(gj5U5~lbYN|CfF}+d%mj1BpFe+E>YM)Hzwmoulf#4WjtT*e gkQ9|yRsEY+$@fD_jqIo6z+A%M>FVdQ&MBb@0C53w;Q#;t literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_7.png b/SCRIPTS/TOOLS/DSMLIB/img/h_rx_pos_7.png new file mode 100644 index 0000000000000000000000000000000000000000..f905abf673dba566f9403e06b5926df3fc3457aa GIT binary patch literal 1213 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 literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/h_swp_3_135.png b/SCRIPTS/TOOLS/DSMLIB/img/h_swp_3_135.png new file mode 100644 index 0000000000000000000000000000000000000000..8b06374049571c4998b617ae2bf157f252daa09c GIT binary patch literal 1350 zcmeAS@N?(olHy`uVBq!ia0vp^CxAGGgAGU?ZmZ`8QjEnx?oJHr&dIz4a#+$GeH|GX zHuiJ>Nn{1`ISV`@iy0XBj({-ZRBb+KpqksBE{-7;ac}P$=H0dvIsS3Fk7QAA8b=ZH ze1+M|;|?y?zpyb{y?>{ZdcK`wUO+`g^#m4ka6`Ezq3kb3_Nr_ z9VB+_5YgGl;QW#CVUp~;O>8MAzOn~2r7&JJD3K1BtzvI_(Lkp9_P)2vBOWHXIeA@; zS&}WVi`QdqS3`zG#G-@F+f=7*-NVJc|LYvZ_nkF|ejQ!0GVSOYg?v>w4SMdCJ{Ky~@tc*WAgz4A_D^dH zLm4fPZT|df%jBz%R-ZV0_~FOBI{fL64YcoC^{&0W^U&Wa*=8@X3CYLayoyk-EW4xn z>h<|Me`A8yPL6GU(atIT@9VE$w`@M8^E%vJ_kVtKsm#tk#Ut1EbXZwG=jbez*=2j& zutZS6eK}hypW#Cd&vz39CP+$4^-XCmKC$r$kLR|ACw_Ijd3kxW+`oDR9#?#L0vK=0 z{%xq0jmTPlE#b_9E7yHRCl&W6pGw}a;YibLL&dYL4QDQhFc-g(+|!cW;pkAXt(g6V z$iow=*$zc2a@$t2Kff@m>V5r+N9hw*T8q?Wtx@&X=@BayTmRL?-62iosX{NOXYVHV z6c6`nRo7;&-gj%+y5v&@M$HN^yp^>MPt4U{*fyEf#abz6`HSLTPEHoD zu3h^kl=WiQku2*?w>j9o3?m(OHQR)l^RzIlK2(x5@DUO?_iIPU8HE*br(R$4WHNd( zi@B~uAaHf!3`YiLMn)c%lr+8EnNvUgauyUg;NTFj_R8jVB?aRn+ao`6obb6SH%SJ> z2WE^j5oVSMj7IpQ}SboX3~E&9mp`gjIRJoH5(8f4a3ySA#`m zW#!L|r55Tc{N??du0@}HS8p19=_3cwtRKl49(6h9?UGNj@6;}J2c`-o1;4!feEX=( zww5P}JMGRWoZob(esaim?e9G8UPaSyFFe#FQea_WVQKJHYRS&oU-%SoyT7*fuV4S)PJMb@GE|?b!`I>7{rmQ}M0VKC;!hJe5b!2{V*^6|g7=g9u>XQ<7ae5$3M~5=JYD@<);T3K F0RWA6VzvMP literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/h_swp_3_135inv.png b/SCRIPTS/TOOLS/DSMLIB/img/h_swp_3_135inv.png new file mode 100644 index 0000000000000000000000000000000000000000..656ec645734cdd23e6d6a241cdc28a9020e63fa0 GIT binary patch literal 1361 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 literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_10.png b/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_10.png new file mode 100644 index 0000000000000000000000000000000000000000..e755828df5ed8487957fc2cc639cacc1df46d1fe GIT binary patch literal 2805 zcma)8dpwhUAD%HYWp<9q+^-{*6GzxRD#zw2{dSI!YP2N_9KNe~Dm<9OK4 z6Ih=Ebs7Q&uB=Rw6tIX8JsoU8HT~*Sz(za->xu<|==oA>ge|}x8gcjp5d?yDZkEUm z1DF~JB$?}IhxLm0n<)=^;B^YowaAE#GkAaoD}(iw10=v`FjxhxADcw?_44YI1?#72 z#TqE*B07N?O_sFeFZ1{=EhHunl@0NTOQ&HiZOMzdj(lVP=X2MPJ$V}Gt3Rg1_MUkS@_T$5O4*;BxH8XS%>%^| z5d*g$%#=TB|g-@F+ya2@~_<# z&4Xaf2ahznZK&*gy7Sw9+vXHqTiscM!?k`bM_Q9tGao1YjN0m)_hpLUQ$?j%B`DlG zH&}x%R8Mh~l~2BojQl;>IhbDS?Om|pa+lH=mVOCg1OG%Li1o2sE-Yu3|CnKK+fU?5 zH=i3cZankmc7!3b*0n=OzV*pn{I<9}Q*2B(n;7~&Ps4KjsUU4n1CtF(Vx ziU!xE%cW#jhaUECppjht?DEN!xI_HGT0}1^OV;DQdu3k03qscnCLoYXwMzT}x#!z( z9{pzG`BiOn;TdnLOhC~0FE7wL{hAL{ov7Q9i*nGJ(iBQN$!S_;GxlT)^xwX{=ghIc zsN4bkUFIJyC8AkQcMe^~x1OrGrlgnD@}Gyt(uur&8Q)SBTnXLjhJHjo+Bb>Hnh zhritcIFo);sk46{Y1e50+M#1`gFrfj_|2rlV9K5L9dZQ2WvEP=Ovu3*mbw6qBat@PR(F+_ll*ID(TyMy_MDF zITJRFb05~GD_Bw6d%hh&Cd2tUT7>ufs#v{wjWka%A9-f!G!b!SS5R*|?qX3-Q;Xkz z7^%|wLM+C&KGeiPKJM#lk=Xm}&KyNf7`wh>b!jGDYMZgNLz>_zL;l;k9dZ*7NGTE+ zPqqkW{z6Jud`yUSDl*pS(}|o&Qib!ti2V7p@-yJdto(ty_+61>`S|%rO3qg+HQvi2 z+2DeGoJ(DOC1xxa${D${yU2+)^4SW2df^m1Kh&A$A#7kph9?w)=gew5zYJ6&+;$h+ z;Ti@@vJo~ZGj>URzf4P*PZ8QOgL1R4;sBAg7xDJHpNIli0%q@M2t9tGD@o^9# z#fO(9)5feTV%kxu{Zx=h0?SQ0g=mzrDPjjN7}~Ay#{k!S_+MPnYdJ1x3qS?lN3(V^!uhLs~)ZDv!SS0_{EJRTg; zEY?=QxRVVI6z=qg=lwO$5lOwhRs)HhcF>Cg)O>S-VB9pYyPdE5(6415uGY^wp2N(D&>@#1 z&)X#?Y2Ex&$aNsAgvPQkdO#Q6&s=ix+leaAd-z$2$HA{uF()I;mjNqczAQ9KxAub(m~URiF9-&$Y9m#>GmS^+ zjNRbXFpRR{xewI&vc2Ks&PB!@IDZ4wKNMau#zJ_yd({?!U;*LVA_Xh&l&TnbktNAri|Tcai=``&bi z9+hi#6%s54GS>BQ+vx{VMQMof3pufA7!sTxYXvBh$dH81&n%mvnfUHy==UE}M~m0B zaqH!lRwxvz!>y5R*b^N&wg~u6M+)*OU`K$dh|+Rdy@BFQ8UrhPuq%&$W=*IvHx1L` zSz%yJwgB^Kga5H^K1Wd(zr(|jwS48R%`Nsqf29P_fX3Q^mO9*p9NAdQiiKpgPK(^Q z;U~YDTfL{69uVtdm26l=jv;#pX<`O#mJWkf#-GthXG`;Ysjm4y^{<5WcdXk rFhFD#YGkzR0A2iF5{y8(mTrh{3@wz#z$BD`s0wnlceAUp#i#ujJfS<= literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_11.png b/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_11.png new file mode 100644 index 0000000000000000000000000000000000000000..66d1c2bc2e6e7bb91e787c9edd05a3ec56a24622 GIT binary patch literal 3042 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 literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_12.png b/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_12.png new file mode 100644 index 0000000000000000000000000000000000000000..1829cf22508d20e38168c58940f785dfd3f32917 GIT binary patch literal 2781 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 RctA@Eay#mdu0Z*d{|hYUQCR>0 literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_13.png b/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_13.png new file mode 100644 index 0000000000000000000000000000000000000000..8f0bdfd13aef3d045f56a3ce27392f36d0ce993b GIT binary patch literal 2677 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 literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_14.png b/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_14.png new file mode 100644 index 0000000000000000000000000000000000000000..7cc9f4c6d9559400b4bfefb0bace0c3f851af6d0 GIT binary patch literal 2811 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 D%YU)k literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_16.png b/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_16.png new file mode 100644 index 0000000000000000000000000000000000000000..37129718f4f45e8ac2968b23ad080a068b6c0f74 GIT binary patch literal 2776 zcmYLLdpOf=AD_Y7;|_g z3?*6Pd7m6cVN;u9rpYMd{pr1~_xENEJ1WR(%7Q>31!pIW zH?Y42RuEJg_;#lf<$z6s;O&S8)v&Z@fdeE2<%R-*>a*oG0;Pa64DWP~00JrYY%huY zJ&IZ&kZia!26ZYLyI2yEsW313rapElJf}|ArO^bnGE+h}Vf?8ZE2k4#E~9LwlZQ|# z7pdJh(OJ>-Qt@Be{YgA2;zTUc?Ffqek6WJ}2siE>>R>+HU%9|)8(_7We+eMwqM$NdF%Lw7etiPDkqQ(Cy*H91pjMwU}9 zKNLUOav||Wi0L@I?#=oQ9~pUI!7y4N z^w7FoWFxjei~i(bTMk*)<$=WC5ekjw#mn)e(S*lM;p5WTyR5+12FoP0)zxK$^Dmp| zaiYB+TOuyDIOiG(KDH7#PIGY;5a&z)HdQiT=7sKH6aGyD?1C`^V3c#7c9oGLr=F^$ zZ3D8iie0%JJwIG6V3pW$sMKAf8L+O&+hV&LV@B={O7}fQ9$CLI93>uck_0f=7a%rT zs;ZqHAHOr@n7x}(WF1a=0I~KcMW$$3he6Lg^OV5WUywRcJTZ`Mf>L?BZJL3}*IXxu ziC2Zlgu@@(6XHf;StfpeMVhw72rsqxwcfxRly4UcxXQi=v2gNrySMrI##{V>QYB$1 zdqhzO8Gmbj=!FcwFIVOg_}IQ*w36`*0_?{3xzjDd{rB;G_Y66U$>opJ+>B zgIV_p7e*GI#D1T{*Z8&KR>u_7^&PXwQHx_z!fRuI$Wx&sH4N?NRAfS|jV*k}02=uW zPc%#u81g#Z|HSO;+lyL@SQyLH-{4M9bEM?dI^Bw_Qx?Sw;j8tUmN<}o_}GCK0-uF{ zrhPaP`gOe7f=RzB}u@<0H>&^TQ%WgFH!4;l)t+=HBeBbj5I zEvcn%b#JP@D`c5BGjM@!6CFfibQc+huXHb&@|^wqu}nO-2Sy!HJ9zcOt(bx^!4yOA z=8jgNQIl~Rlp4l5N=NQ zFtDx&l@~tMB-;@4kbgCrdX+<%TSKL%<^v*c2pb!J&Ds1Q8-5ChL zmSVayl=Ar3RF*)2sx>Ah!hTe|S z+chW~@9b$a8_7WNkMEpEzf-!}R^gNNwWfgjV4>&@_N`xKPh#SY)KaTK+Ohu<=3rI- zS%gM^Qe!P5!j$>396yKRhMS+64^(;ng+OmIs%(ExiF$o@PrcNU(Xrb*oZK;`mm@2DUxR(2V6~n zfuM^EFzdqB7at=xMAnU%`(~XJrpm@mzN{#Wx>X#zJSK=}iWU-rnN(%oG%8VCTE_WCRuQ=Nh#={%(sQ zPzg)Yb3^?j9Gb+>qY;a!iHd2OPu&Q3RWz-c);8X|yn9{9r?B;trZF~>LC?J_rs~Qe zFuPwK@+EEfW@;Xk<)wR_0B23f-G7(Ar@M;Xeef|?{8JRjD(tG~&5zVlTDhlbSBG${ z{?{72y3~qtwyJ?nwg@W@XGyHN}{0mkV*s5QE4l(J?)^UX8 z&&*uj@lSVW-pNY$N7Qql-TDeFbkeUJ+V{GupG9LI`{oGa8Z$eN4|h1~9k?ex2o1E8 zKht1->KU9#KhBsqGX1f|PbuijyL^1hRM^)uHGWCcTrMta_RwrFj-Ga865=hrTmj0XGhTwkb0@IVyn;u>y2nMFoXA%hJvLU*Q zW3*(8fOh$YW~*9LXopd#Z5DxZf>Nwu1>|bonv7u@T+i9Q6p^jtkjh4Ou_s&iI-NST zBdmMq61y^EzGi@GC8@Sm22n;E+zwGC>lD${`0%VdcT(8&VXWKmMD~CKfhQloGUG*c zHUL@-l42`ymZ|sT>Bf)!4&3!n`07+}Kot-YU16p=`+}hMmj@A}seOOfUv#9-7Zci2 z#XoYKyk#65W8Di6z&3XprlpxMC9bP;SE)LJc#;j{bl3xU=x|k1E0*HcaOpjc_4MWS zNhGzQERBpWiL$5`Z5nUeg3%27_U(%+by2_g71^9ud_BX`s^#Hfek<3-S5FtP9hs;~ z#`{NMbMy9`QJkm?lnm?MS!ScJKYj*-2I@}R;YAz)wO4_6W%{kZ%UpPI>bc4#>-aT+ zE>Fd}ny0OVX384?Io#RibA7ViO<7end7VPr?SclzN#N>P!5$a*vLiKiXlVXO z@K1%SI(6F7rC_f@1C46VZ@%`4#MYuFG-84t0kbzH%@saTa`m653e0oIM@Q0~(S+ZW zvg~){q->8{JtGu)F~48DNLJoFz8yZwQ3`rJOMnXhf9W}TKUve!ahqL_FjhDNyhcIJ L$J{YBXdLN31g%H| literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_17.png b/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_17.png new file mode 100644 index 0000000000000000000000000000000000000000..d3b82cbee3ee03fdc2e93b5c765c34bd11ea893b GIT binary patch literal 2629 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 Kdx~99>VE;wW36xi literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_20.png b/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_20.png new file mode 100644 index 0000000000000000000000000000000000000000..3ab286e7008e79bd1070977175565fce887a94c5 GIT binary patch literal 2674 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 egI)5rtPc@*#f literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_21.png b/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_21.png new file mode 100644 index 0000000000000000000000000000000000000000..2ef1d29c99e80641c043695393cb0ce17ec1a7ff GIT binary patch literal 2735 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 Vda~hzD?pM7I_&J}RPGo=|1WvG42}Q* literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_22.png b/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_22.png new file mode 100644 index 0000000000000000000000000000000000000000..4037dfe14d8870d469acda68f23c81845df8dde7 GIT binary patch literal 2698 zcmZ`*X*iqN7mqcu$3FJdRuxOsSc1|RYNyLos@9-PDWa| zSlW@&X~!B{xNY+{vYmpp8LG#o_p>&zw;CHoUaR-}$FI6t=lhL7-DPoF3N~V@=Wtpm z^}scsa@)-Zh0+*}7w6h-HcWwFkhPo4=J)os?pd==h<3mIl~~rX4A*PLNVoI@jvgZEue|FWoJ%wHb6z$ zdZ?h`Qjt2Z7KtEhLR#8xnN=bi)S0v`<7#i8lGb^Wb5VrE$IpK~#P09y()mT?yPWRo zq&5b=;#M)c0#P;!k+O(g@-x-cb4r@kDUJuh zl7Vj@IV^3hEnvd!?CdDUJp95b!==ZBSW_*MdL9kxIy*NcfVfy&q82+&(VjFZf@=zV4YIo zRG2QNYykoL@35Db$YHcO@7Al`bUK|R(fdQ)32KhCUtzqa+uc^~E;flsFXAbW5n8W&;RfT?1Pk?jMI$B}__DLI z2x$l{AjhykNkY<-arBcg8hNQFO^ALxI%9)1OSz?Bk}f2xCv2EXu+Nt0zG3@rO4F6huXd@Q#OrvHg@e2=FejT3c0W~;rztokO({Ht-~0!k(WC7EUiaN}O?uzt6jD%1Cj+-NDkx zkqK?>Oe4G=irae=*~5ByM{TLxHgnFs=6v@$ZrynMoQRT{RH;D#|I|Y}G1D4F%fz3Q zfBp?hZ4D8U(MeG-3gIn;qeU5QUSCpoi}EBtZ$qAi6R?eHb{Yo*PL(iBYQrQ z@_CgZEXVCubd>MgOuKoB`_?*BsLU){Flbs;TU(If-9V>gP>PMi@I|IyxRQ&@cJtR~ zr^sQaTRUkhY4VQH+X13~e*{<4$hT-8m|=ByzM4UTcblCB*g*{#$fSqW#fFWDNe15~l zh!yPYR)7DL)pj{IhBedOw%xx;X1p#2uY^9|*p|kBc~ZkGj><%ua`0)T4jFKG>e0y0 zC}}FGdQ)kgCWzRt8CsTqAh_WEv~@@OQq{cR*+qdavsWzp96x}C@cdB-e$Qn=s}fku zw<(mt4L?VqDo$}@CgILUPL=aI{t?h0O3d9U6FG9kxN^ErDhfR;^$yo#EcMI%T^sBJ zfNWD$zQPo%1|nrjQb`VZno9tPF{8(NP1r_9rIoF4!Yc^@`hm4){H=U=4<8izxM+_| zWUcJb8U-0@m&(oOH&e06URI;RgU{2w4SX;Yc;z|f9%$pkcGA}B5$CDW0bJ7e#R)?; z@N8^>_SrdUxO1WD!7UVzO|EqvT$T!(e3pvbsP(M#u@qb2U*zd) z0>>3*N7v+!@+}Sz7(wHn-~MpyrZk+t(T;oI2A2P&V}W*evXqAX6AoD)f1&Y`m# z78z;<$;6$xDWiMSfAW^I!9qh+*j4eJf6Ohw2uy_6Yx1vErx7gqiq&s&^ZlRVVE`?p zzY`i_;MTsLHPzD-We{>?uXT4P64#gpUP6;g(G`@60xtn&>P86MvG{>h2`{nH>~eryZgtbrGkq#Ohp zAX&l(?eX_RBjGLC_V?BOBD_XXUq`CJQOq7+7idB!(6uNfc3H{8#U}b*EtfBt{&5P= zd4Vd>8m@eslH)Yw2^uk4(BGGi97;1(x*XK2YJ0QG`lozgEP#F&$j#5^ZA>CGc1C&U zkVuH}^y=3QZn9(EWk#VgIs_Ca(}#*-SMTAYAez@R^CuWGkx2Ny5MF`y;?d2%4Q~f`BI#IV*$l#~zCwis~yM|EEE~ zddG}9sH&;a+3eWybolYe6DP=ZkmQ3rkj9fY_hw7+-7gF3s4qL@jY9l_C>dfZ{(6{q zAw$csUg~iPY3a1Mnwhq^yh9ciiYsEF$D|IfkuIAcR%A*{`Oq&~oo*qBAClg=YB%#;=5w0``|gI#164DK=s;xh-~>8QR2 lxg*aB`oFW7c#g2#o{$_v7@}|L00HJlkb|wOO|7*b{(pce1D^l@ literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_23.png b/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_23.png new file mode 100644 index 0000000000000000000000000000000000000000..d767b2fd479318f0832bc3a16857ebd53d4de8a4 GIT binary patch literal 2743 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 Yi|O%B_aDr|0nbX1tBV)D);W~^Z+62lH~;_u literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_25.png b/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_25.png new file mode 100644 index 0000000000000000000000000000000000000000..ba84f610d3b0603682b0d27a84b6a92321977ad3 GIT binary patch literal 2696 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 literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_3.png b/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_3.png new file mode 100644 index 0000000000000000000000000000000000000000..8510177dac2c9c1ba378d58f6bb144b0b3a98622 GIT binary patch literal 2992 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 literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_5.png b/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_5.png new file mode 100644 index 0000000000000000000000000000000000000000..5af94fe1992db7381c8e6343c58f4423d7fe241e GIT binary patch literal 2575 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 I=pIG>7ZyR*sQ>@~ literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_6.png b/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_6.png new file mode 100644 index 0000000000000000000000000000000000000000..3668db1b1ffc7b4c26a44b634acbeafbe8d84e82 GIT binary patch literal 2548 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 literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_9.png b/SCRIPTS/TOOLS/DSMLIB/img/rx_pos_9.png new file mode 100644 index 0000000000000000000000000000000000000000..948f858b757d08708633944311f157c0c6bff86b GIT binary patch literal 2834 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 literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/tt_1rud_1ele.png b/SCRIPTS/TOOLS/DSMLIB/img/tt_1rud_1ele.png new file mode 100644 index 0000000000000000000000000000000000000000..5546a25d5bda89c5b90e980b2ad83ca520d26b10 GIT binary patch literal 1229 zcmeAS@N?(olHy`uVBq!ia0y~yVB`m~Q#jaw%>>vU1kdX1KCyS_ zlrMt2`=?CWUMyH<+E<}mRbZl8IDLT&(|z-1%d;NaTm*h+JD%Qk`TS<_h6VO|_KZ#~ z>=Tr18bls4F5%!;aIIj_JjCoFDCf}ifguQwRLx`wpE-WZPp^GFrS}}OhY(0ZgLK>5 zlNLWbfM)2;IP~K5^3%P=i8X&e{@bk6|Iz8y`TBn~|9`$a{rz49B03rEi22`@nJ!HdDG){=a^k8E-c@A z(D0RoLkqhPQ^KEs8*4v0npN=mRa}_Ac6YPiF8+s(@maay%dGC~jB6UR5o*QVW#(utsme!*|XIjnELdup39k!rt73}|LMw}`)(3n#kWeSKWmOZ zwBo-lJ}pW}XmMif)W_3KBzsT1Ud+;dcUw=Cwc?ZB_un4xP#0z>R;u^^V*2i;;^E}C zCztVFbkgarS#2s(+xNoo&B=$>{Rc0q&7G66FjrI{a&7C$#KXx|O5f}*K5^mBtT}!^ zx}ay#di|su<-r~vhVs8&)|oNiFW5p-km=BdXLTZwZ$8s-7&dc$x|Y~VeVzi%2j)HryPx{`*}Y8 z|KHl3S3CUblp{ha|7q*3E)Dy0t97UO{11h;kFVR^34Cp1dbg6{HZT>Ra^9x9`1qzkEHup0PwoZkv8UeEHnt3b$%`9vYkPSa;`=ZSj<6@AjKMJ7fFqn#S3? z|9b+x%^xnjv2fiwt9~PgEH#5KD_-(WTv%>wu=Chw*E99Um-D;D8w~8{)-$;X z);Xv=WOosw8WkVq*K+w~$-Q0g=B?at_4Ov(sqzAzKNu&r$QLe4eQ{@A=IZOI(uepv zRQ@!0AgCv5=}J3J&h*x@SD5sHSt;_{q=&c9KkqHS|IaQz=f&#p?lVt8we){{yOAaL z`{j4@4+}sXT~+nN*St3GE`!|F)TinUjqatp--tHkER9W#$^Mdm`9pPe_3wpSm;GM7 zns-|O@3vQ;E$6MX|6uSzBQwuF^YH77g(Y9N{z*N)uvENm`qlf3nWrB#{eDB2?TYC2 zFv;vN`R?C^>+e4-iToM)Z_}|^6K4A+ugaaj(q?<8rQEjbUpJMf1sKj;y>{o_BL4>6 zm3#cYO5F6@{_y*YhNbma6*8T(R(}k>>;6@yVxFSb{?10@eOUoHq6OcD+Kyi;{SnKj z)|_f{Rbhtqy2Y&b*HqP9{hYq{o%a64d`0gr`+YMN-p4!n-Z7`IH&$P7JI}lM&0I;5 z+LeAUvJ$>5smi;Wef&SKVBKQhri-C!Ii><`LAKvdm8KpzmA~k7|I)crvVSczdN?^` zo%h#S>QZWd^w?YY?)#TM+B&)F?2%_1R~7iqSCLrt$++;_*0Q8fb-nGO>sJcMl-#ub z%=f)CFZT7RBTovm*Oz~ik<|O+v#vHK?t9s%my>I2uZf?mH7~#%4Sr^T!A8x^(7s-7EgwVptPW zf4TVfwOuuDJ{3N=p=AGIhxrP>{q_IXGuC!<+MkYJAR778ch%MVBJ;d^Q{S#<{+6?L z>2dpP?`iYCM`r8V?sI?a9P)!xJ3%b`=T^oSu>wWB^^-qOI(=bxnatIKJFjc{9);b1 zzwqzuPy6fsKHjs{&pIsq>gTGI|IwC*WNeyF-2b_>T|^-2r|-3pdaeDll*8)!E8L!z zyJ+p_-#Gu literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/tt_2rud_1ele.png b/SCRIPTS/TOOLS/DSMLIB/img/tt_2rud_1ele.png new file mode 100644 index 0000000000000000000000000000000000000000..2f5042455c4b5c90f5fa9d417604ca621bb4e6a8 GIT binary patch literal 1401 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 MI)V;(=$}~dH;1^eDgXcg literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/tt_taileron.png b/SCRIPTS/TOOLS/DSMLIB/img/tt_taileron.png new file mode 100644 index 0000000000000000000000000000000000000000..e8a406aa9d1662f6c529c9ebb4078668d36bf5d5 GIT binary patch literal 1126 zcmeAS@N?(olHy`uVBq!ia0y~yV6+FalR4OcPnv;&`M0NwV@O5Z+q-XbUz>@rJ=k%%q~lq< z`%8uG)g@E@dAGlqyR27Wbwcgah~wLoCcgj7;KFiagPq4dyPl~(zMS7J-e6!qx1PyG zu+Bl{A-jtZ)u{L=zn04{OYZG@H*e*JtFJfNPL&t%{J}V}MZR!Z>We$`GFM+ul|IDZ zq4KA}13^7eOIO-)a;CSIy~3mq%u12pCOy1;{&{ct{eO1(IWJa!cb|C*s-^$q+l?%_ z-!H$Le^>zG=&Gt8zUH-gcNyfarao0?Xml^#{YJDQXK8F|O!k-j%O9$%tA8)ty6pGr z)x6sRc(=X!Y&mb8{Re{&8ku?anTKCrEG+rD^-t>Yg{9(k)34rN%sl;|>GvDDY*$3D zhe>9K$#?%QTz~&zN#xJSf18fYnlRfpc~$QGl{VW$E#Ex>T*>a{!X7Wp^m zuH57IRpO@K_J`kJG%T&Zs*vfNwfbZ5UH7jt74sCe_IEZK@5>6v5iR&G)OP$*>5o`G zwdPcts|qu;*DYqfzox3@>gV*m@3i+X<|}%4+3%aF@IKzj_l`Mzy|Maw+j-v2Z{|vh z)UNb%70tQkPQuqsQLDci+GC(bmaTXOBGFxT?T+zKX=EPsWAcww5J@s_ShJUB6O5rsSse zXTI;Hd9klg9eGliy}taDjHKQlpLMk{ao@{6y_{TIdrkagt#Qdy^T5B1-hS^-{=f1f z|M0TE&zJ81^YeCPw!pve+x)lo+55PDJa9csZ*SqW^V$9D&+d6R;qR8_huZcHs=zq^ zl6v~y&DXDASH8~ndbfPq5B8WtGM%aKnNHt3+_mb@G&bW2nLj>w*QGPB>t6Ba7Q>p5 z`pd<)ukEUN^QrK`4JG>zJIq)3?XUm8p0T!@)Bbe)0@28yzN@a@7n$eXoBDP=^S7L} zOOM-UdrzD9Ju+L@cAxuW=a3(q+6iLeKesZzh!rT}t)KjP(&-Dk%Ve$=+<9Ho_bBZC z`-OjJf7)O7_wk;se%4{>S3g&!{ExOgBxBQb;{MO2?IHqEKYg!-)NAdZr5sk*U*Yz& z+(m0Y|Hk>>*1i1l(sC->7iF_QYPkuat8EtK&(^E1UedICermqFPPIM9e(po>>sCGg zr_%rZMXLUU*Dt5a-wdh$>oNZqFep-Pa+8+#7EGUB&*-|mB}|wvxdK==FnGH9xvXoL#zu0cPzx2eri>&Xlvybx>`{{Q z8m*G|yYaM}w1;&N@eGZ8gWD$P2DCmrQy{E13bk{#s+X;iSHH<8Fo8cINS_Pu>H4Gb-UV!@fafZS*I_w8Jaf(_BqOblA z$aK;dWp5Ixb;G_vz?;#hel#))jEsF16E zCR*gBxzHz4;_8w!LY+erRUKyc10)TuWrDTqU??AAMX@I{a`%b?lTy7MVpxEKcF=0f9r%0@CokREIQ(0$3 zH8d1@0dM}x;BY4e#S7l0$`g!n!lI}#38vSIO=G%-ZoB)DUoW=IIOGzsSI%z>9NN{r z0(&``Trq!`-UU`&U{4idMJ4h{W9WQgI-(YZXn4AyXeA_9wy1*kw%oOywe_w&z~8ca z$ZxLOVletF4?WWK?ZM|S#wio)Bd`fCN|h$x-<|;Z{K!*;xZ-+iJni#r+?G^J7aXEY z;L#^0#Q9&-W6*OVk5$5%X~fZxPo<_>flu*apkD%k@P74v_=hqAy{0;L^il5#*6qmA zP(pv^LRVj2-=7sLxTH%Rv+%dCSSNko_s&_MSKA6eNvz>AlE``k^E#9ucM~y2Bj=ps zmn0?dL6pdr$l6zucr>9X&1W)4(CzIa!(A}?HYU(VIM;;9|~dJWq^$5#Qd l*2P$D{jU-I?{N6pGiJU!*kL?koHwyv_^q z;5uz@UbuUwdAA40=8MhNca~T1Ik+?^7fvm(JGUnF$KU<)?=f)I?)<|U;8MY$c!-%p zP|l&_1A|iwyMU5SgTO<^77l&|ilyR9Vqg6I`}fld+yAC#bVTIRbr;NgZ!IWyW=%!T z%UFla7O#J|Rd-qU3%Z*8;+wPo`T6tbPcM$Qv$NCduj^b>Ud<=Vu5Dqn?MZ7%>6Qhb znq)%*&NnIF+;{zjRV(}I%x`z6RWIBW9_LUg_&q>MCa|elwDnMOb>n;8c^)78b(VM( zXw;lr^76S&Uzz+)!MpC8p4YlGCts;o{rtPd?i=4)y7v~UE-y}0efZ%<^}_2f z?5fy2{@v0&K2^?JPd~#=VVRv#cb(Q=Z;az>J^rn!{>riI_kL$2<`-;{ z2v2amYu%r+?VYlejQg(Ex0a={YgS}ce0gCP#d_lEy;7};bMFc&T)JO)T)fURVek31 zmg}zY*+?w>b>mX@`^CrR+Uq}(d+g=Met6ZYmg61ol*4~TRW~b_Z@Ez_yQk}&^fKMu z{d-#@g=9>RPo1-_d8#RA|D-1CDs?^H3Caf--gvk@>tSF}gz4`7l=nNmo0P+qEw()u zcUiOU=8a36f1kKovG?sSQL8^|vntM9seQs2bg6X9jpr+5Cm2M1=e<1b9jk|D!IP*G zSu4pn$FCo5h#lK({bO54_T8EDOBK#HEk3$q(d!4IdqbXo^zkg%0%V7p{9Nj9eDCdN z(?6dd-+n5{U;F?2Cx(AlmVBP^@ZGHS3*EyR(~XYaxRmt%u^1!MSChZ7TW>2!817Oz ze{8xH#+`S9&dE5BBzare(U zXmh)I@%(4W-Y<_{-SqoK>azzYmVTWoH^FCf;;rs(`ByLGJtvxP){2wcBtJ9F{*=cXmn(J?8-UXB4(4n@0o zgBSGWC7-_f?cCQl#`2!8&)3G}UKh1IfAP7v<&Vwkrn}qEP3ISmT=n_IRip1#=WSp9 zfx3VDlBV^g1#!-Y7k|0EP4{VQ!TJw*nm>1c(Aw3ndve zYttlnf4qOHGEE}7s#M-HF6TVg&a{R1CtVA!a^QCcr*O&GqP5&+UJr}ZPUp-xccb=< zRMLgRA`cCdzi%{mSXUxD=j?_L9U{@^-zjVI3Rq0eJT@)zyWP9inDq*t&oylqv@d+P z(dqld-Jx^b#Li{RmdKI;Vst0EY8mcK`qY literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/wt_1ail.png b/SCRIPTS/TOOLS/DSMLIB/img/wt_1ail.png new file mode 100644 index 0000000000000000000000000000000000000000..1611053a1388b9b98ef7d09badee63008203f661 GIT binary patch literal 721 zcmeAS@N?(olHy`uVBq!ia0y~yU=#$h(>U0GEqspYBgoIw+o*7(L)M(i=>vEWqgAxoSB~MMymd`FYo8R6W z+GfqO;5(m%n@hu!vi$US_PXk8Hva$jd;j&%25j^8)yCLuJoR_|(-gbB^ajnC=Uypi zwUQtGXE3gcOI;J$8S`pZ{OZo*uU1&r$_iH2DzYS52<}{vyzpj1PyejTKrJc%rk+@M zDNHUsUDaqN@3FrtEN$am?(B+b>F{~+G{)kvjfCm)=lkXgPVkjwyIRdIy?fHfe18^? zXS?QZ{LgrNZekwX=|=u8cTOF+%~$;RtNHZF8-y5z1aYECA`_>-pS9xni~HrT7}v{6 zt53IZ`>(q=qLZPs1B5)QHs3sa_;As++J8SwzyEl8{8OfMd3pKioA!tAUpscSI{SaZ zlOOTPUt}ar|E-0^OUkT{oW1f)*VC?9CZUBhB+OsllzgzQqKxgv9Iz!PU)c&aB(H&K iCnIu^wIpo+&8#7DZNKeW*+gJ6WbkzLb6Mw<&;$U?OCE&) literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/wt_1ail_1flp.png b/SCRIPTS/TOOLS/DSMLIB/img/wt_1ail_1flp.png new file mode 100644 index 0000000000000000000000000000000000000000..b51d1fd7768df82e56282552f181ecdbb84ff3af GIT binary patch literal 803 zcmeAS@N?(olHy`uVBq!ia0y~yVB`m~Q#jawg`Ga zZj2&~rUFUr>lnKhFc<~0Jb7}V@od-S8_DyZ?4E!3^4zcY&h(voEXls_>hil&pML!s zuiY?lUTHtW;tqBJL4E}#IR_V;hK>pb!H*0|j~HE!Fn17^SaQnVA#VNkyP_LHl7kbT zBI&yHs>CC~iG=*PYXmGM=I8m7!tru4= zF0*;xpUN+C@7{m^x;%WhyIkSN#k*(pOWmHfT(tlABZI@UE!>~h@pTo~_gxE<@{OpR6EX9C@AIfC8?HZYu+Hb@`(U{{Q*e?caAFf1khqjok80H*eg$Em6*&*#jP)wbVgySX}D^yaC5|7Ob`m>Jj8&yX(2uRtV< zd{Za)`){2M)4G$vlM|jZsyu?B)&CBbEL_gC$ny1-bIcw`kWk*Utkc1EeDAaPqd)I| zzIL_leu;ZOt~9KjHEaFh8&=D89g1p~>z1!S)_ni%{rbv^nm3kh*Nb^gpI2I%>hE8_ z@OQbsyxjky@0B($=4@kFz5CqU<1U5uA-`OXidU_kFn88;_2*xdUq6T`zP55M`-Bcu zbSK89&D%eid0ST2KECAFZ)_T#q&+gM+3+-;d0Sf6ynmM4PNBIL+2v^Lu-@ZWc>T}G zPXA?F%zpm*m70j^m{+A|5>bO;@$1JQE#ykq+wZ$uTlG2dPo`}9;Y6EXnWw)`ONsP8 z`?feHOZLpn+huYNN=T@v^kc+tFYcU+V%Himy#evnnqO0E5`NdH%12D8VCaOQlS$#x n4Y$*f9RZINybPAzcf;#AuHAZAIHM7mxEMTL{an^LB{Ts5N01Qg literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/wt_2ail_1flp.png b/SCRIPTS/TOOLS/DSMLIB/img/wt_2ail_1flp.png new file mode 100644 index 0000000000000000000000000000000000000000..5b13f6c536ee2d465c86ec427b98370772d8880c GIT binary patch literal 755 zcmeAS@N?(olHy`uVBq!ia0vp^&w)6TgAGU?^fT!IQjEnx?oJHr&dIz4a#+$GeH|GX zHuiJ>Nn{1`ISV`@iy0XBj({-ZRBb+K1_q`MPZ!6KinzCT&lX;GU}#I6$@SozY}WyE zt_2(`8kHZ;kXpdO^&qXQ!Mgl~g5yawxZ#VHn0bmax_LdN@i>8t&%&&?6=nj@g~ zX;az#-z)T%&OD@fZRY8yh=vyyx6`M!GcTL6=a^r0Wc9~gKL57=`t$nPwr`tnANR9r zICFj7){^b}*4ypfwexP#$IU*!USIa}n=?25$G-<}ED9FO33tV2uiW*oY|YhuzqUL! zRc{eOLS22E@7}DkNbowDtz&eqto!H2gWGN?z2@6?^;%f@oLQ%%I8@KEuC)EVy}458 z`JAouqjpN`^jSP#_gCX)NB`uB-e5-q-R(7pJE*#Tv+SA47UGM(iFuV9*gt9sSp0ZR ztedRWuB`9U{rtz@RxDm!_J84G{&YVMRWLIBwf=ps*=(cN@6Ue!o_fv1{$8E#=H(mX z)}MZJZT|iHr*ihqtN(f9&)mdKI;Vst0IWqkiU0rr literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/wt_2ail_2flp.png b/SCRIPTS/TOOLS/DSMLIB/img/wt_2ail_2flp.png new file mode 100644 index 0000000000000000000000000000000000000000..87b384298a8a9cfbc53fb9714770b2a463930241 GIT binary patch literal 817 zcmeAS@N?(olHy`uVBq!ia0vp^&w)6TgAGU?^fT!IQjEnx?oJHr&dIz4a#+$GeH|GX zHuiJ>Nn{1`ISV`@iy0XBj({-ZRBb+K1_q{Mo-U3d6>)Fx?kyB{WN3RREb8FL=enSW zNrNd%;YiCB2G;#M(+CW|NQyPn-|Yzq`Y2ITl@TK&)(g?mNGuF z(b(=FQRt*WCA7lve_!JE+fyYCu5A1=$5L^M1r%L*!Ml)=zrA|3zxBB}0$y_jv_5V6 zcH{X9y`?h`NxeRks+;3bGUu)TwA;+frtCR(ZSR`B6;W#cZ|(hCRU7^L*4yuXRg9ML z?bj}y{k5;^^OIlS?8W0xE!l7P_Q{c(mJjUp&ozqYUHRVf=t9)0b^DpKx30H)`(v)K zQzsl1E53C*5UcF_*0PNKb9dk6MG1K)vvrKlm39AISGX?g_@%y(tf4xG=a246&fZp6!+2z8i=cee@mr4!TLslygdpg7Q`YToZ#_b**S$@Ud=%r6yLzAX zr7QE2%MI*dLAl~$;WaZJKiSx=zt2~eTzw~R>=#&l=gQ*MW&fwSg}?n5>@E)p3Sekt zeTl!nd(R%7&Hv`rRz;@ye*XEh-Dvjly8Zf_@9(Ss9g+6A;>W+znWks^!{47;v){cx ze#*`G<$F@M-p^lV_iSZfVaM(Dr)QK_bgjIWRW2)6Ek73;_UV`6p55(PyNmz$4z)t3 z6`|%W)3$tRyAU$TyyY60k;8G2tpgrR!cM*S*0Q#3jOI=Ux(b4_F0=_4*r)q(s8SPk dTrsg{`RU<>6~JW9;OXk;vd$@?2>|5^SYiMG literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/wt_elevon.png b/SCRIPTS/TOOLS/DSMLIB/img/wt_elevon.png new file mode 100644 index 0000000000000000000000000000000000000000..fa354ca18836c044ce0f2cf0808eb0011bdb4d3e GIT binary patch literal 1055 zcmeAS@N?(olHy`uVBq!ia0y~yVB`m~Q#jaweIILURO@YZ^`EDZ%bu8CgFHU3uMLS@Eg5p6(Vs5Yo<6;*_08mGj6nX>%%!dVH?EogVTDV{#jABc?_B!g zU2!L1eWRhMnyzZ8wP1uC5!CP-UetOaC zmV%9tT~D@qXc%0QG51+%X8mnp zx$p?18Gi4-Z8@BK!+_Pvq+jrsoBf?jYn&_gC>i`x_xQxouYR$y=+&)hf+|Pjo+oU7 zxG?p@v@oN7-e854@UzahA4El+NWI(Mejs6GkAh~^?B#ifm8%Z2vQ5uW)!)U%uiL!# zt)&Mjq|Tc2=Qc;BY?-iV>dqW9n?>SY2^*%~%g>y}#oy?-x{61rTF$}a>)B+_q->ch z8jD*+w$IBy%gnKIQ)Pzq8_(6h6+>Iyzo>BWR9y_3aL2}mg^lMl$E2jSTZ<-th$sqb zVXF>Fo&fTKS*~1Opn|)baOAFaVf_7DnIF$uuV1sm_t_mi-bpWhMd;cn-4A%Obk6+r zH}9vNU;jnd>Bg0qxLGeXCzgi0%v;@M+yCj1Zb8JmJ=318Tj}uvWMtUR+?{Gi)9l~= zZNJ=HFT4L=)jx}GUzfd7ymRVG%)IO8A5Z^&UjBda|Ic3%=6Sy^dS~UCFBh!8Vb6}w zj5*rXf0vr~FZ|$9*>%Z=P3XJ7SHF$>&zeu$Il6t%K7HcTzovPo(Y2Mf{0c4=42rZm zbNBjL+fDoYPP_NF?ejnWV`Aw!qYStyN;VA>eE-?^x3{-nUc96KzP)^1&9Aaw!T;aA zd*|2xFYDpk-8-+%njV+Bwz=o$C7T9;hm0*8aMxt2*?jxr$KA6sQx?TYR8=>hgnw%M x^>b=Xf-s5?CjHCf-oOuZCD0K-9V{|@`x&ExJ(Y|1XoIppgQu&X%Q~loCIH#A-k$&f literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/DSMLIB/img/wt_flaperon.png b/SCRIPTS/TOOLS/DSMLIB/img/wt_flaperon.png new file mode 100644 index 0000000000000000000000000000000000000000..82d2464f1709aee4fb70e3b94145333805272026 GIT binary patch literal 679 zcmeAS@N?(olHy`uVBq!ia0vp^&w)6TgAGU?^fT!IQjEnx?oJHr&dIz4a#+$GeH|GX zHuiJ>Nn{1`ISV`@iy0XBj({-ZRBb+K1_mY@PZ!6KinzCTql<(b85{x&jCXPTEY^H? zcnAOVNAh}3;liqYT_+W0wjbr46MWS*Jh1W91#{z)xZA(w%^c3Hekj%Mfie7UBRQ)=HNEJT&tDNWd+p-yq#dE#S&qSDbWxZ#MFukF{#p z6SMr|| +-- Line Type: Text for Menus (T), List_Text Options (LT), List_Text_Image (LI), Flight Mode (FM), RX Name (RX) +-- NO EMPTY LINES +-- Formmatting at end of line: /c=Center, /r=Right, /b=Bold, /m=menu +LT|0x0001|Off +LT|0x0002|On +-- Ihn/Act List Options +LT|0x0003|Inh +LT|0x0004|Act +-- +-- Channel selection for SAFE MODE and GAINS on FC6250HX +LT|0x000C|Inh +LT|0x000D|Ch5 +LT|0x000E|Ch6 +LT|0x000F|Ch7 +LT|0x0010|Ch8 +LT|0x0011|Ch9 +LT|0x0012|Ch10 +LT|0x0013|Ch11 +LT|0x0014|Ch12 +-- +-- Servo Output values +LT|0x002D|5.5ms +LT|0x002E|11ms +LT|0x002F|22ms +-- +-- Gain Multiplier Values +LT|0x0032|1 X +LT|0x0033|2 X +LT|0x0034|4 X +-- +LT|0x0035|Inh +LT|0x0036|Thr +LT|0x0037|Ail +LT|0x0038|Ele +LT|0x0039|Rud +LT|0x003A|Ch5 +LT|0x003B|Ch6 +LT|0x003C|Ch7 +LT|0x003D|Ch8 +LT|0x003E|Ch9 +LT|0x003F|Ch10 +LT|0x0040|Ch11 +LT|0x0041|Ch12 +LT|0x0042|Ch13 +LT|0x0043|Ch14 +LT|0x0044|Ch15 +LT|0x0045|Ch16 +LT|0x0046|Ch17 +LT|0x0047|Ch18 +LT|0x0048|Ch19 +LT|0x0049|Ch20 +-- +T |0x0040|Roll +T |0x0041|Pitch +T |0x0042|Yaw +T |0x0043|Gain/c/b +T |0x0045|Differential +T |0x0046|Priority +T |0x0049|Output Setup +T |0x004A|Failsafe +T |0x004B|Main Menu +T |0x004E|Position +-- +T |0x0050|Outputs +T |0x0051|Output Channel 1 +T |0x0052|Output Channel 2 +T |0x0053|Output Channel 3 +T |0x0054|Output Channel 4 +T |0x0055|Output Channel 5 +T |0x0056|Output Channel 6 +T |0x0057|Output Channel 7 +T |0x0058|Output Channel 8 +T |0x0059|Output Channel 9 +T |0x005A|Output Channel 10 +-- +-- FailSafe Options +--LT|0x005E|Inh +LT|0x005F|Hold Last +LT|0x0060|Preset +--LT|0x0061|Custom +-- +T |0x0071|Proportional +T |0x0072|Integral +T |0x0073|Derivate +T |0x0078|FM Channel +T |0x007F|Attitude Gain +-- +T |0x0080|Orientation +T |0x0082|Heading +T |0x0085|Frame Rate +T |0x0086|System Setup +T |0x0087|F-Mode Setup +T |0x0088|Enabled F-Modes +T |0x0089|Gain Channel +T |0x008A|Gain Sensitivity/r -- Right Align +T |0x008B|Panic +T |0x008E|Panic Delay +-- +LT|0x0187|No Freq --???? unset Freq +LT|0x0088|70hz +LT|0x0089|90hz +LT|0x008A|200hz +LT|0x008B|333hz +LT|0x008C|490hz +LT|0x008D|560hz +LT|0x008E|Normal +LT|0x008F|Reversed + +-- FC6250HX: Callibration +T |0x0090|Apply +T |0x0091|Begin +T |0x0092|Start +T |0x0093|Complete +T |0x0094|Done +-- +-- FC6250HX: Swashplate Type +-- +LT|0x0090|Normal +LI|0x0090|h_swp_norm.png|Normal +LT|0x0091|3 Servos 120 Y +LI|0x0091|h_swp_3_120.png|3 Servos 120 Y +LT|0x0092|3 Servos 120 Y-Inv +LI|0x0092|h_swp_3_120inv.png|3 Servos 120 Y-Inv +LT|0x0093|3 Servos 135 Y +LI|0x0093|h_swp_3_135.png|3 Servos 135 Y +LT|0x0094|3 Servos 135 Y-Inv +LI|0x0094|h_swp_3_135inv.png|3 Servos 135 Y-Inv +LT|0x0095|3 Servos 140 Y +LI|0x0095|h_swp_3_140.png|3 Servos 140 Y +LT|0x0096|3 Servos 140 Y-Inv +LI|0x0096|h_swp_3_140inv.png|3 Servos 140 Y-Inv +LT|0x0097|3 Servos 90 T +LI|0x0097|h_swp_3_90.png|3 Servos 90 T +LT|0x0098|3 Servos 90 T-Inv +LI|0x0098|h_swp_3_90inv.png|3 Servos 90 T-Inv +-- +T |0x0097|Factory Reset +T |0x0098|Factory Reset +T |0x0099|Advanced Setup +T |0x009A|Capture Failsafe Positions +T |0x009C|Custom Failsafe +-- +T |0x009F|Save Settings -- Save & Reboot RX +-- +T |0x00A5|First Time Setup +T |0x00AA|Capture Gyro Gains +T |0x00AD|Gain Channel Select +T |0x00AF|Dynamic +LT|0x00B0|Self-Level +LT|0x00B1|Envelope +-- +-- Flight Modes List Options +LT|0x00B5|Inh +LT|0x00B6|FM1 +LT|0x00B7|FM2 +LT|0x00B8|FM3 +LT|0x00B9|FM4 +LT|0x00BA|FM5 +LT|0x00BB|FM6 +LT|0x00BC|FM7 +LT|0x00BD|FM8 +LT|0x00BE|FM9 +LT|0x00BF|FM10 +-- +T |0x00BE|Unknown_BE -- Used in Reset menu (0x0001) while the RX is rebooting +-- +T |0x00C7|Calibrate Sensor +T |0x00C8|Sensor is Calibrating.. Wait +T |0x00CA|SAFE & Panic Mode Setup +-- +T |0x00CD|Level model & capt attitude/m -- SPECIAL MENU to itself who is not a comment +T |0x00CE|Error TX Conf +T |0x00CF|Invalid TX Ch Conf 1 +T |0x00D0|Invalid TX Ch Conf 2 +-- +-- RX Orientations for AR631/AR637, Optionally attach an Image + Alt Text to display +LT|0x00CB|Pos 1 +LI|0x00CB|rx_pos_1.png|Pilot View: RX Label Up, Pins Back +LT|0x00CC|Pos 2 +LI|0x00CC|rx_pos_2.png|Pilot View: RX Label Left, Pins Back +LT|0x00CD|Pos 3 +LI|0x00CD|rx_pos_3.png|Pilot View: RX Label Down, Pins Back +LT|0x00CE|Pos 4 +LI|0x00CE|rx_pos_4.png|Pilot View: RX Label Right, Pins Back +LT|0x00CF|Pos 5 +LI|0x00CF|rx_pos_5.png|Pilot View: RX Label UP, Pins to Front +LT|0x00D0|Pos 6 +LI|0x00D0|rx_pos_6.png|Pilot View: RX Label Left, Pins Front +LT|0x00D1|Pos 7 +LI|0x00D1|rx_pos_7.png|Pilot View: RX Label Down, Pins Front +LT|0x00D2|Pos 8 +LI|0x00D2|rx_pos_8.png|Pilot View: RX Label Right, Pins Front +LT|0x00D3|Pos 9 +LI|0x00D3|rx_pos_9.png|Pilot View: RX Label Up, Pins Left +LT|0x00D4|Pos 10 +LI|0x00D4|rx_pos_10.png|Pilot View: RX Label Back, Pins Left +LT|0x00D5|Pos 11 +LI|0x00D5|rx_pos_11.png|Pilot View: RX Label Down, Pins Left +LT|0x00D6|Pos 12 +LI|0x00D6|rx_pos_12.png|Pilot View: RX Label Front, Pins Left +LT|0x00D7|Pos 13 +LI|0x00D7|rx_pos_13.png|Pilot View: RX Label Up, Pins Right +LT|0x00D8|Pos 14 +LI|0x00D8|rx_pos_14.png|Pilot View: RX Label Back, Pins Right +LT|0x00D9|Pos 15 +LI|0x00D9|rx_pos_15.png|Pilot View: RX Label Down, Pins Right +LT|0x00DA|Pos 16 +LI|0x00DA|rx_pos_16.png|Pilot View: RX Label Front, Pins Right +LT|0x00DB|Pos 17 +LI|0x00DB|rx_pos_17.png|Pilot View: RX Label Back, Pins Down +LT|0x00DC|Pos 18 +LI|0x00DC|rx_pos_18.png|Pilot View: RX Label Left, Pins Down +LT|0x00DD|Pos 19 +LI|0x00DD|rx_pos_19.png|Pilot View: RX Label Front, Pins Down +LT|0x00DE|Pos 20 +LI|0x00DE|rx_pos_20.png|Pilot View: RX Label Right, Pins Down +LT|0x00DF|Pos 21 +LI|0x00DF|rx_pos_21.png|Pilot View: RX Label Back, Pins Up +LT|0x00E0|Pos 22 +LI|0x00E0|rx_pos_22.png|Pilot View: RX Label Left, Pins Up +LT|0x00E1|Pos 23 +LI|0x00E1|rx_pos_23.png|Pilot View: RX Label Front, Pins Up +LT|0x00E2|Pos 24 +LI|0x00E2|rx_pos_24.png|Pilot View: RX Label Right, Pins Up +LT|0x00E3|Pos Invalid +LI|0x00E3|rx_pos_25.png|Cannot detect orientation of RX +-- +-- RX Orientations images for FC5250 (HACK add 0x100 internally to differenciate for helis) +LI|0x01CB|h_rx_pos_1.png|Pilot View: RX Label Up, Pins Front +LI|0x01CC|h_rx_pos_2.png|Pilot View: RX Label Left, Pins Front +LI|0x01CD|h_rx_pos_3.png|Pilot View: RX Label Down, Pins Front +LI|0x01CE|h_rx_pos_4.png|Pilot View: RX Label Right, Pins Front +LI|0x01CF|h_rx_pos_5.png|Pilot View: RX Label UP, Pins to Back +LI|0x01D0|h_rx_pos_6.png|Pilot View: RX Label Left, Pins Back +LI|0x01D1|h_rx_pos_7.png|Pilot View: RX Label Down, Pins Back +LI|0x01D2|h_rx_pos_8.png|Pilot View: RX Label Right, Pins Back +-- +T |0x00D1|Receiver will Reboot/b +T |0x00D2|Panic Channel +T |0x00D3|Swashplate +T |0x00D5|Agility +T |0x00D8|Stop +T |0x00DA|SAFE/c/b -- Center + Bold +T |0x00DB|Stability +T |0x00DC|@ per sec +T |0x00DD|Tail rotor +T |0x00DE|Setup +T |0x00DF|AFR +T |0x00E0|Collective +T |0x00E1|Subtrim +T |0x00E2|Phasing +T |0x00E3|Pre-Comp +T |0x00E4|E-Ring +T |0x00E5|Swash Type +T |0x00E6|Travel +T |0x00E7|Left +T |0x00E8|Right +T |0x00EA|Low Throttle +-- +T |0x00F2|Governor +T |0x00F4|Soft Start +-- +LT|0x00F2|Fixed +LT|0x00F3|Adjustable +LT|0x00F4|Inh +LT|0x00F5|Nitro +-- +T |0x00F6|Direction +T |0x00F8|Settings -- ?? validate on a Spektrum radio +T |0x00F9|Gyro settings +T |0x00FE|Stick Priority/c/b +-- +T |0x0100|Make sure the model has been +T |0x0101|configured, including wing type, +T |0x0102|reversing, travel, trimmed, etc. +T |0x0103|before continuing setup. +T |0x0104| +T |0x0105| -- Blank +-- +T |0x0106|Any wing type, channel assignment, +T |0x0107|subtrim, or servo reversing changes +T |0x0108|require running through initial +T |0x0109|setup again. +T |0x010A| +T |0x010B| +-- +T |0x0190|Relearn Servo Settings +T |0x019C|Enter Receiver Bind Mode +T |0x01AA|Offset +T |0x01D7|SAFE Select Channel +T |0x01DC|AS3X/c/b -- Center + Bold +T |0x01DD|AS3X Settings +T |0x01DE|AS3X Gains +T |0x01E0|Rate Gains/c/b +T |0x01E2|SAFE Settings +T |0x01E3|SAFE Gains +T |0x01E6|Attitude Trim/c/b +T |0x01E7|Envelope +T |0x01E9|Roll Right +T |0x01EA|Roll Left +T |0x01EB|Pitch Down +T |0x01EC|Pitch Up +T |0x01EE|Thr to Pitch +T |0x01EF|Low Thr to Pitch/c/b +T |0x01F0|High Thr to Pitch/c/b +T |0x01F1|Filter +T |0x01F3|Threshold +T |0x01F4|Angle +T |0x01F6|Failsafe Angles/c/b +T |0x01F8|Safe Mode +T |0x01F9|SAFE Select +T |0x01FC|Panic F-Mode +T |0x01FD|FailSafe Flight Mode -- Safe Flight Mode +T |0x0201|Throttle +T |0x0204|Hover +T |0x0208|Decay +T |0x0209|Save to Backup +T |0x020A|Restore from Backup +T |0x020D|First Time SAFE Setup +-- +-- First time safe setup Page 3 : +T |0x020E|AS3X gains must be tuned +T |0x020F|and active in SAFE Flight Modes +T |0x0210|to help reduce wobble. +T |0x0211| +T |0x0212| +T |0x0213| -- Blank +-- +-- AS3X orientation Setting menu (Level) +T |0x021A|Set the model level, +T |0x021B|and press Continue. +T |0x021C| +T |0x021D| -- Blank +-- +-- AS3X orientation Setting menu (Nose down) +T |0x021F|Set the model on its nose, +T |0x0220|and press Continue. If the +T |0x0221|orientation on the next +T |0x0222|screen is wrong go back +T |0x0223|and try again. +T |0x0224|Continue +-- +T |0x0226|Angle Limits/c/b +T |0x0227|Other settings +T |0x0229|Set Orientation Manually +-- +-- Factory Default Warning +T |0x022B|WARNING! +T |0x022C|This will reset the +T |0x022D|configuration to factory +T |0x022E|defaults. This does not +T |0x022F|affect the backup config. +T |0x0230| -- Blank +-- +-- Backup Warning +T |0x0231|This will overwrite the +T |0x0232|backup memory with your +T |0x0233|current configuartion. +T |0x0234| +T |0x0235| -- Blank +-- +-- Restore from Backup Warning +T |0x0236|This will overwrite the +T |0x0237|current config with +T |0x0238|that which is in +T |0x0239|the backup memory. +T |0x023A| -- Blank +-- +-- Utilities Copy flight modes +T |0x023D|Copy F-Mode Settings +T |0x023E|Source F-Mode +T |0x023F|Target F-Mode +-- +T |0x0240|Utilities +-- +-- Gain Capture Page +T |0x024C|Gains will be captured on +T |0x024D|Captured gains will be +T |0x024E|Gains on +T |0x024F|were captured and changed +T |0x0250|from Adjustable to Fixed +-- +-- Utilities, Copy flight mode (Copy Confirmation, oveerriding FM) +T |0x0251|Are you sure you want to ovewrite the "Target" +T |0x0252|with the "Source" ? +T |0x0253| -- Blank +-- +T |0x0254|Pos = Up, Neg = Down +-- +-- First time safe setup Page 1 (maybe ask to select Flight Mode cannel) +T |0x0255|Before setting up SAFE +T |0x0256|a Flight Mode channel +T |0x0257|most be configured. +-- +-- First time safe setup Page 2 (something related for flight mode) +T |0x025A|Select the desired flight mode +T |0x025B|switch position to adjust settings +T |0x025C|for each flight mode +T |0x025D| +T |0x025E| -- Blank +-- +-- Utilities, Copy flight mode (Confirm) +T |0x0259|YES +T |0x0260|WARNING: "Target" +T |0x0261|F-Mode will be overwritten +T |0x0262|by "Source" +-- +T |0x0263|Fixed/Adjustable Gains/c/b +T |0x0266|Heading Gain/c/b +T |0x0267|Pos = Nose Up/Roll Right +T |0x0268|Neg = Nose Down/Roll Left +T |0x0269|SAFE - Thr to Pitch +T |0x026A|Use CAUTION for Yaw gain!/b +-- +T |0x026B|Head Speed +T |0x026C|Pinion +T |0x026D|Main Gear +T |0x026F|RPM Sensor +T |0x0272|Show Advanced Menus +-- +T |0x0300|No compatible DSM RX... +T |0x0301|Waiting for RX to Restart +-- +FM|0x8000|Flight Mode/c/b +-- +RX|0x0001|AR636 +RX|0x0014|SPM4651T +RX|0x0015|AR637T +RX|0x0016|AR637TA +RX|0x0018|FC6250HX +RX|0x0019|AR630 +RX|0x001A|AR8360T +RX|0x001B|AR8020T +RX|0x001C|AR10360T +RX|0x001E|AR631 + diff --git a/SCRIPTS/TOOLS/DSM_AR636_Tel.lua b/SCRIPTS/TOOLS/DSM_AR636_Tel.lua new file mode 100644 index 0000000..0ec740d --- /dev/null +++ b/SCRIPTS/TOOLS/DSM_AR636_Tel.lua @@ -0,0 +1,683 @@ +local toolName = "TNS|DSM AR636 Telemetry|TNE" +---- ######################################################################### # +---- # 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. # +---- # # +---- ######################################################################### + +------------------------------------------------------------------------------ +-- Developer: Francisco Arzu +-- Original idea taken from DsmPID.lua.. don't know who is the author +-- + +local DEBUG_ON = false +-- + +local TEXT_SIZE = 0 -- NORMAL +local X_COL1_HEADER = 6 +local X_COL1_DATA = 60 +local X_COL2_HEADER = 170 +local X_COL2_DATA = 220 +local Y_LINE_HEIGHT = 20 +local Y_HEADER = 0 +local Y_DATA = Y_HEADER + Y_LINE_HEIGHT*2 +local X_DATA_LEN = 80 +local X_DATA_SPACE = 5 + + +local function getPage(iParam) + -- get page from 0-based index + -- {0,1,2,3}: cyclic (1), {4,5,6,7}: tail (2) + local res = (math.floor(iParam/4)==0) and 0 or 1 + return res +end + +local function round(v) + -- round float + local factor = 100 + return math.floor(v * factor + 0.5) / factor +end + + +local function readValue(sensor) + -- read from sensor, round and return + local v = getValue(sensor) + --v = round(v) + return v +end + +local function readValueById(sensor) + local i = getFieldInfo(sensor) + if (i==nil) then return nil end + + local v = getValue(i.id) + return v +end + + + +local function readBatValue(sensor) + -- read from sensor, round and return + local v = getValue(sensor) + if (v==nil) then return "--" end + + return string.format("%2.2f",v) +end + +local function readActiveParamValue(sensor) + -- read and return a validated active parameter value + local v = getValue(sensor) + if (v<1 or v>8) then + return -1 + end + return v +end + + +local function drawPIDScreen() + -- draw labels and params on screen + + local pageId = getValue("FLss") + + lcd.clear() + -- if active gain does not validate then assume + -- Gain Adjustment Mode is disabled + if not (pageId==4401 or pageId==4402) then + lcd.drawText(0,0,"BLADE Gain Adjustment", TEXT_SIZE +INVERS) + lcd.drawText(X_COL1_HEADER,Y_LINE_HEIGHT*1,"Enter Gain Adjustment Mode",TEXT_SIZE) + lcd.drawText(X_COL1_HEADER,Y_LINE_HEIGHT*2,"Stk: Low/R + Low/R + Panic (3 sec)",TEXT_SIZE) + lcd.drawText(X_COL1_HEADER,Y_LINE_HEIGHT*4,"Op: Right Stk: Up/Down to select, Left/Right change value",TEXT_SIZE) + lcd.drawText(X_COL1_HEADER,Y_LINE_HEIGHT*5,"Panic to exit",TEXT_SIZE) + return + end + + local activePage = (pageId % 100)-1 --Last 2 digits, make it zero base + + lcd.drawText (X_COL1_HEADER, Y_HEADER, "Cyclic (0-200)", TEXT_SIZE + INVERS) + lcd.drawText (X_COL2_HEADER, Y_HEADER, "Tail (0-200)", TEXT_SIZE + INVERS) + + + + local p = readValue("FdeA") + local i = readValue("FdeB") + local d = readValue("FdeL") + local r = readValue("FdeR") + + local titles = {[0]="P:", "I:", "D:", "Resp:", "P:","I:","D:", "Filt:"} + local values = {[0]=p,i,d,r,p,i,d,r} + + local activeParam = readActiveParamValue("Hold")-1 + + for iParam=0,7 do + -- highlight selected parameter + local attr = (activeParam==iParam) and INVERS or 0 + -- circular index (per page) + local perPageIndx = (iParam % 4) + + -- set y draw coord + local y = (perPageIndx+1)*Y_LINE_HEIGHT+Y_DATA + + -- check if displaying cyclic params. + local isCyclicPage = (getPage(iParam)==0) + + -- labels + local x = isCyclicPage and X_COL1_HEADER or X_COL2_HEADER + -- labels are P,I,D for both pages except for last param + local val = titles[iParam] + lcd.drawText (x, y, val, TEXT_SIZE) + + -- gains + -- set all params for non-active page to '--' rather than 'last value' + val = (getPage(iParam)==activePage) and values[iParam] or '--' + x = isCyclicPage and X_COL1_DATA or X_COL2_DATA + + if (val~=16384) then -- Active value + lcd.drawText (x, y, val, attr + TEXT_SIZE) + end + end +end + + +local function drawFlightLogScreen() + -- draw labels and params on screen + local h = getValue("Hold") + local activeParam = h-1 -- H + + lcd.clear() + lcd.drawText (X_COL1_HEADER, Y_HEADER, "Flight Log", TEXT_SIZE + INVERS) + + -- read and return parameters + local a = getValue("FdeA") + local b = getValue("FdeB") + local l = getValue("FdeL") + local r = getValue("FdeR") + local f = getValue("FLss") + + local titles = {[0]="A:", "B:", "L:", "R:", "F:", "H:"} + local values = {[0]=a,b,l,r,f,h} + + local y = Y_LINE_HEIGHT+Y_DATA + + for iParam=0,3 do -- A,B,L,R + -- highlight selected parameter (rund) + local attr = ((activeParam%4)==iParam) and INVERS or 0 + -- labels + local x = X_COL1_HEADER + local val = titles[iParam] + lcd.drawText (x, y, val, TEXT_SIZE) + + -- Values + val = values[iParam] + x = X_COL1_DATA + X_DATA_LEN + if (val~=16384) then -- Active value + lcd.drawText (x, y, val, attr + TEXT_SIZE + RIGHT) + end + + y = y + Y_LINE_HEIGHT + end + + y = Y_LINE_HEIGHT+Y_DATA + for iParam=4,5 do -- F, H + -- labels + local x = X_COL2_HEADER + local val = titles[iParam] + lcd.drawText (x, y, val, TEXT_SIZE + BOLD) + + -- Values + val = values[iParam] + x = X_COL2_DATA + X_DATA_LEN + lcd.drawText (x, y, val, TEXT_SIZE + RIGHT + BOLD) + + y = y + Y_LINE_HEIGHT + end + + -- Bat + y = y + Y_LINE_HEIGHT + local bat = readBatValue("A2") or "--" + lcd.drawText (X_COL2_HEADER, y, "Bat:", TEXT_SIZE) + lcd.drawText (X_COL2_DATA + X_DATA_LEN, y, bat, TEXT_SIZE + RIGHT) + lcd.drawText (X_COL2_DATA + X_DATA_LEN + X_DATA_SPACE, y, "v", TEXT_SIZE) + +end + +local function servoAdjustScreen() + -- draw labels and params on screen + local pageId = getValue("FLss") -- FLss + local activeParam = getValue("Hold")-1 -- Hold + + lcd.clear() + lcd.drawText (0, Y_HEADER, "BLADE Servo SubTrim", TEXT_SIZE + INVERS) + + if pageId~=1234 then + lcd.drawText(X_COL1_HEADER,Y_LINE_HEIGHT*1,"Enter Servo Adjustment Mode",TEXT_SIZE) + lcd.drawText(X_COL1_HEADER,Y_LINE_HEIGHT*2,"Stk: Low/L + Low/R + Panic (3 sec)",TEXT_SIZE) + lcd.drawText(X_COL1_HEADER,Y_LINE_HEIGHT*4,"Op: R Stk: Up/Down to select, Left/Right change value",TEXT_SIZE) + lcd.drawText(X_COL1_HEADER,Y_LINE_HEIGHT*5,"Panic to exit",TEXT_SIZE) + return + end + + local a = getValue("FdeA") + local b = getValue("FdeB") + local l = getValue("FdeL") + + local titles = {[0]="Servo1:", "Servo2:", "Servo3:"} + local values = {[0]=a,b,l} + + for iParam=0,#values do -- S1,S2,S3 + -- highlight selected parameter + local attr = (activeParam==iParam) and INVERS or 0 + + -- set y draw coord + local y = (iParam+1)*Y_LINE_HEIGHT+Y_HEADER + + -- labels + local x = X_COL1_HEADER + local val = titles[iParam] + lcd.drawText (x, y, val, TEXT_SIZE) + + val = values[iParam] + x = X_COL1_DATA + if (val~=16384) then -- Active value + lcd.drawText (x, y, val, attr + TEXT_SIZE) + end + end +end + +local function Unsigned_to_SInt16(value) + if value >= 0x8000 then -- Negative value?? + return value - 0x10000 + end + return value +end + +local function getDegreesValue(sensor) + local i = getFieldInfo(sensor) + if (i==nil) then return "-unk-" end + + local v = getValue(i.id) + if v==nil then return "---" end + local vs = Unsigned_to_SInt16(v) + + return string.format("%0.1f o",vs/10) +end + + +local function getDecHexValue(sensor) + local i = getFieldInfo(sensor) + if (i==nil) then return "-unk-" end + + local v = getValue(i.id) + if v==nil then return "---" end + local vs = Unsigned_to_SInt16(v) + + return string.format("%d (0x%04X)",vs,v) +end + + + + +local function drawVersionScreen() + local paramV = getValue("FdeA") + local B = getValue("FdeB") + local rxId = getValue("FdeL") + local firmware = getValue("FLss") + local prodId = getValue("Hold") + local bat = readBatValue("A2") + + lcd.clear() + lcd.drawText (0, Y_HEADER, "BLADE Version", TEXT_SIZE + INVERS) + + --Product ID + local val = "ID_".. prodId + + if (prodId==243) then val = "Blade 230 V1" + elseif (prodId==250) then val = "Blade 230 V2 (not Smart)" + elseif (prodId==149) then val = "Blade 250 CFX" + end + + local y = Y_DATA + local x_data1 = X_COL1_DATA+X_DATA_LEN + lcd.drawText (X_COL1_HEADER, y, "Prod:", TEXT_SIZE) + lcd.drawText (x_data1, y, val, TEXT_SIZE) + + -- RX + val = "ID_"..rxId + if (rxId==1) then val = "AR636" + end + + y = y + Y_LINE_HEIGHT + lcd.drawText (X_COL1_HEADER, y, "RX:", TEXT_SIZE) + lcd.drawText (x_data1, y, val, TEXT_SIZE) + + -- Firmware + val = string.format("%0.2f",firmware/100) + y = y + Y_LINE_HEIGHT + lcd.drawText (X_COL1_HEADER, y, "Firmware:", TEXT_SIZE) + lcd.drawText (x_data1, y, val, TEXT_SIZE) + + -- ParamV + y = y + Y_LINE_HEIGHT + lcd.drawText (X_COL1_HEADER, y, "Params:", TEXT_SIZE) + lcd.drawText (x_data1, y, paramV, TEXT_SIZE) + + -- Bat + y = y + Y_LINE_HEIGHT + lcd.drawText (X_COL1_HEADER, y, "Bat:", TEXT_SIZE) + lcd.drawText (x_data1, y, bat, TEXT_SIZE) + + y = y + Y_LINE_HEIGHT + lcd.drawText(X_COL1_HEADER,y,"Press Panic for 3s",TEXT_SIZE) + + y = y + Y_LINE_HEIGHT + lcd.drawText(X_COL1_HEADER,y,"Usually Panic is Ch7 on a switch and Revesed",TEXT_SIZE) + +end + +local function parseFlightMode(v) + -- FlightMode (Hex: MMSGG) MM=Flight Mode, S=Status (0= off, 1=init, 2=Hold, 3=Running) GG=??? + if v==nil then return "---" end + local fm = bit32.rshift(v, 12) + local status = bit32.band(bit32.rshift(v, 8),0xF) + + local res = " "..fm.." " + + if (fm==0) then res = res .. " NORMAL" + elseif (fm==1) then res = res .. " INTERMEDIATE" + elseif (fm==2) then res = res .. " ADVANCED" + elseif (fm==5) then res = res .. " PANIC" + end + + if (status==2) then res=res .. " HOLD" end + + if (DEBUG_ON) then + res = res .. string.format(" (0x%04X)",v) + end + + return res +end + + +local function drawAlpha6Monitor() + lcd.clear() + + local RxStatus = readValueById("2402") -- FlightMode (Hex: MMSGG) MM=Flight Mode, S=Status (0=init, 2=Ready, 3=Sensor Fault) GG=??? + + local ARoll = getDegreesValue("2406") --Att Roll + local APitch = getDegreesValue("2408") --Att Pitch + local AYaw = getDegreesValue("240B") --Att Yaw + + + lcd.drawText (0,0, "BLADE Alpha6 Monitor", TEXT_SIZE+INVERS) + + local y = Y_DATA + local x_data1 = X_COL1_DATA+X_DATA_LEN + local x_data2 = X_COL1_DATA+X_DATA_LEN*2 + local x_data3 = X_COL1_DATA+X_DATA_LEN*3 + + -- Flight Mode + lcd.drawText (0,y, "F-Mode:"..parseFlightMode(RxStatus), TEXT_SIZE) + + y = y + Y_LINE_HEIGHT + lcd.drawText (x_data1,y, "Attitude", TEXT_SIZE+BOLD + RIGHT) + lcd.drawText (x_data2,y, "Gyro", TEXT_SIZE+BOLD + RIGHT) + lcd.drawText (x_data3,y, "Gain", TEXT_SIZE+BOLD + RIGHT) + + y = y + Y_LINE_HEIGHT + lcd.drawText (X_COL1_HEADER,y, "Rol:", TEXT_SIZE) + lcd.drawText (x_data1,y, ARoll, TEXT_SIZE + RIGHT) + lcd.drawText (x_data2,y, "-", TEXT_SIZE + RIGHT) + lcd.drawText (x_data3,y, "-", TEXT_SIZE + RIGHT) + + y = y + Y_LINE_HEIGHT + lcd.drawText (X_COL1_HEADER,y, "Pitch:", TEXT_SIZE) + lcd.drawText (x_data1,y, APitch, TEXT_SIZE + RIGHT) + lcd.drawText (x_data2,y, "-", TEXT_SIZE + RIGHT) + lcd.drawText (x_data3,y, "-", TEXT_SIZE + RIGHT) + + y = y + Y_LINE_HEIGHT + lcd.drawText (X_COL1_HEADER,y, "Yaw:", TEXT_SIZE) + lcd.drawText (x_data1,y, AYaw, TEXT_SIZE + RIGHT) + lcd.drawText (x_data2,y, "-", TEXT_SIZE + RIGHT) + lcd.drawText (x_data3,y, "-", TEXT_SIZE + RIGHT) + + y = y + Y_LINE_HEIGHT + Y_LINE_HEIGHT + lcd.drawText (0,y, "Bat: "..readBatValue("A2").." v", TEXT_SIZE) + + + -- Debug Values + if (DEBUG_ON) then + local s2400 = getDecHexValue("2400") + local s2402 = getDecHexValue("2402") + local s2404 = getDecHexValue("2404") + + local s240D = getDecHexValue("240D") + + local s1G00 = getDecHexValue("1G00") + local s1G02 = getDecHexValue("1G02") + local s1G04 = getDecHexValue("1G04") + local s1G06 = getDecHexValue("1G06") + local s1G08 = getDecHexValue("1G08") + local s1G0B = getDecHexValue("1G0B") + local s1G0D = getDecHexValue("1G0D") + + local titles = {[0]= + "2400","2402/FM-S-?", + "2404","240D", + "1G00","1G02","1G04", + "1G06","1G08","1G0B","1G0D"} + + local values = {[0]= + s2400,s2402,s2404,s240D, + s1G00,s1G02,s1G04, + s1G06,s1G08,s1G0B,s1G0D} + + + -- draw labels and params on screen + + y = Y_LINE_HEIGHT*2 + Y_HEADER + for iParam=0,#titles do -- ?? + -- labels + local x = X_COL1_HEADER+220 + local val = titles[iParam] + lcd.drawText (x, y, val, TEXT_SIZE) + + val = values[iParam] + x = X_COL1_DATA+250 + lcd.drawText (x, y, val, TEXT_SIZE) + + y = y + Y_LINE_HEIGHT + end + end +end + +local function readAlpha3arameters() + +end + + + + +local function drawAS3XMonitor() + lcd.clear() + local s1G00 = getDecHexValue("1G00") + local s1G02 = getDecHexValue("1G02") + local s1G04 = getDecHexValue("1G04") + local s1G06 = getDecHexValue("1G06") + local s1G08 = getDecHexValue("1G08") + local s1G0B = getDecHexValue("1G0B") + local s1G0D = getDecHexValue("1G0D") + + local s6C00 = getDecHexValue("6C00") + local s6C02 = getDecHexValue("6C02") + local s6C04 = getDecHexValue("6C04") + + + + local RRoll = bit32.rshift(getValue("1G00") or 0,8) + local RPitch = bit32.band(getValue("1G00") or 0,0xFF) + local RYaw = bit32.rshift(getValue("1G02") or 0,8) + + local HRoll = bit32.band(getValue("1G02") or 0,0xFF) + local HPitch = bit32.rshift(getValue("1G04") or 0,8) + local HYaw = bit32.band(getValue("1G04") or 0,0xFF) + + local ARoll = bit32.rshift(getValue("1G06") or 0,8) + local APitch = bit32.band(getValue("1G06") or 0,0xFF) + local AYaw = bit32.rshift(getValue("1G08") or 0,8) + + + lcd.drawText (0,0, "Plane AR636 AS3X Gains", TEXT_SIZE+INVERS) + + local y = Y_DATA + local x_data1 = X_COL1_DATA+X_DATA_LEN + local x_data2 = X_COL1_DATA+X_DATA_LEN*2 + local x_data3 = X_COL1_DATA+X_DATA_LEN*3.1 + + -- Flight Mode + --lcd.drawText (0,y, "F-Mode: "..(nil or "--"), TEXT_SIZE) + + y = y + Y_LINE_HEIGHT + lcd.drawText (x_data1,y, "Rate", TEXT_SIZE+BOLD + RIGHT) + lcd.drawText (x_data2,y, "Head", TEXT_SIZE+BOLD + RIGHT) + lcd.drawText (x_data3+X_DATA_SPACE*3,y, "Actual", TEXT_SIZE+BOLD + RIGHT) + + y = y + Y_LINE_HEIGHT + lcd.drawText (X_COL1_HEADER,y, "Roll %:", TEXT_SIZE) + lcd.drawText (x_data1,y, RRoll, TEXT_SIZE + RIGHT) + lcd.drawText (x_data2,y, HRoll, TEXT_SIZE + RIGHT) + lcd.drawText (x_data3,y, ARoll, TEXT_SIZE + RIGHT) + + y = y + Y_LINE_HEIGHT + lcd.drawText (X_COL1_HEADER,y, "Pitch %:", TEXT_SIZE) + lcd.drawText (x_data1,y, RPitch, TEXT_SIZE + RIGHT) + lcd.drawText (x_data2,y, HPitch, TEXT_SIZE + RIGHT) + lcd.drawText (x_data3,y, APitch, TEXT_SIZE + RIGHT) + + y = y + Y_LINE_HEIGHT + lcd.drawText (X_COL1_HEADER,y, "Yaw %:", TEXT_SIZE) + lcd.drawText (x_data1,y, RYaw, TEXT_SIZE + RIGHT) + lcd.drawText (x_data2,y, HYaw, TEXT_SIZE + RIGHT) + lcd.drawText (x_data3,y, AYaw, TEXT_SIZE + RIGHT) + + + -- Debug Values + if (DEBUG_ON) then + local Alpha3Tags = {[0]= + "1G00/RA+RE","1G02/RY+HA","1G04R HP+HY","1G06/AR+AP","1G08/AY+?","1G0B","1G0D","6C00","6C02","6C04"} + + local params = {[0]= + s1G00,s1G02,s1G04,s1G06,s1G08,s1G0B,s1G0D,s6C00,s6C02,s6C04 } + + y = Y_LINE_HEIGHT*2 + Y_HEADER + for iParam=0,#Alpha3Tags do -- ?? + -- labels + local x = X_COL1_HEADER+220 + local val = Alpha3Tags[iParam] + lcd.drawText (x, y, val, TEXT_SIZE) + + val = params[iParam] + x = X_COL1_DATA+250 + lcd.drawText (x, y, val, TEXT_SIZE) + + y = y + Y_LINE_HEIGHT + end + end +end + +local function openTelemetryRaw(i2cId) + --Init telemetry (Spectrun Telemetry Raw STR) + multiBuffer( 0, string.byte('S') ) + multiBuffer( 1, string.byte('T') ) + multiBuffer( 2, string.byte('R') ) + multiBuffer( 3, i2cId ) -- Monitor this teemetry data + multiBuffer( 4, 0 ) -- Allow to get Data +end + +local function closeTelemetryRaw() + multiBuffer(0, 0) -- Destroy the STR header + multiBuffer(3, 0) -- Not requesting any Telementry ID +end + +local lineText = {nil} +local I2C_TEXT_GEN = 0x0C + +local function drawTextGen(event) + if (multiBuffer(0)~=string.byte('S')) then -- First time run??? + openTelemetryRaw(I2C_TEXT_GEN) -- I2C_ID for TEXT_GEN + lineText = {nil} + end + + -- Proces TEXT GEN Telementry message + if multiBuffer( 4 ) == I2C_TEXT_GEN then -- Specktrum Telemetry ID of data received + local instanceNo = multiBuffer( 5 ) + local lineNo = multiBuffer( 6 ) + local line = "" + for i=0,13 do + line = line .. string.char(multiBuffer( 7 + i )) + end + + multiBuffer( 4, 0 ) -- Clear Semaphore, to notify that we fully process the current message + lineText[lineNo]=line + end + + lcd.clear() + -- Header + if (lineText[0]) then + lcd.drawText (X_COL1_HEADER,0, " "..lineText[0].." ", TEXT_SIZE + BOLD + INVERS) + else + lcd.drawText (X_COL1_HEADER,0, "TextGen", TEXT_SIZE+INVERS) + end + + -- Menu lines + local y = Y_DATA + for i=1,8 do + if (lineText[i]) then + lcd.drawText (X_COL1_HEADER,y, lineText[i], TEXT_SIZE) + end + y = y + Y_LINE_HEIGHT + end + + if event == EVT_VIRTUAL_EXIT then -- Exit?? Clear menu data + closeTelemetryRaw() + end +end + +local telPage = 1 +local telPageSelected = 0 +local pageTitle = {[0]="Main", "Blade Version", "Blade Servo Adjust","Blade Gyro Adjust", "Blade Alpha6 Monitor", "Plane AS3X Monitor", "TextGen", "Flight Log"} + +local function drawMainScreen(event) + lcd.clear() + lcd.drawText (X_COL1_HEADER, Y_HEADER, "Main Telemetry (AR636)", TEXT_SIZE + INVERS) + + for iParam=1,#pageTitle do + -- highlight selected parameter + local attr = (telPage==iParam) and INVERS or 0 + + -- set y draw coord + local y = (iParam-1)*Y_LINE_HEIGHT+Y_DATA + + -- labels + local x = X_COL1_HEADER + local val = pageTitle[iParam] + lcd.drawText (x, y, val, attr + TEXT_SIZE) + end + + if event == EVT_VIRTUAL_PREV then + if (telPage>1) then telPage = telPage - 1 end + elseif event == EVT_VIRTUAL_NEXT then + if (telPage<#pageTitle) then telPage = telPage + 1 end + elseif event == EVT_VIRTUAL_ENTER then + telPageSelected = telPage + end +end + + +local pageDraw = {[0]=drawMainScreen, drawVersionScreen, servoAdjustScreen,drawPIDScreen, drawAlpha6Monitor, drawAS3XMonitor, drawTextGen, drawFlightLogScreen} + +local function run_func(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + end + + -- draw specific page + pageDraw[telPageSelected](event) + + if event == EVT_VIRTUAL_EXIT then + if (telPageSelected==0) then return 1 end -- on Main?? Exit Script + telPageSelected = 0 -- any page, return to Main + end + + return 0 +end + +local function init_func() + + if (LCD_W <= 128 or LCD_H <=64) then -- Smaller Screens + TEXT_SIZE = SMLSIZE + X_COL1_HEADER = 0 + X_COL1_DATA = 20 + + X_COL2_HEADER = 60 + X_COL2_DATA = 90 + + X_DATA_LEN = 28 + X_DATA_SPACE = 1 + + + Y_LINE_HEIGHT = 8 + Y_DATA = Y_HEADER + Y_LINE_HEIGHT + + end +end + +return { run=run_func, init=init_func } diff --git a/SCRIPTS/TOOLS/DSM_SmartRX_Tel.lua b/SCRIPTS/TOOLS/DSM_SmartRX_Tel.lua new file mode 100644 index 0000000..716f308 --- /dev/null +++ b/SCRIPTS/TOOLS/DSM_SmartRX_Tel.lua @@ -0,0 +1,603 @@ +local toolName = "TNS|DSM Smart RX Telemetry|TNE" +---- ######################################################################### # +---- # 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. # +---- # # +---- ######################################################################### + +------------------------------------------------------------------------------ +-- Developer: Francisco Arzu + + +local DEBUG_ON = false +-- + +local TEXT_SIZE = 0 -- NORMAL +local X_COL1_HEADER = 6 +local X_COL1_DATA = 60 +local X_COL2_HEADER = 170 +local X_COL2_DATA = 220 +local Y_LINE_HEIGHT = 20 +local Y_HEADER = 0 +local Y_DATA = Y_HEADER + Y_LINE_HEIGHT*2 +local X_DATA_LEN = 80 +local X_DATA_SPACE = 5 + + + + +local function getPage(iParam) + -- get page from 0-based index + -- {0,1,2,3}: cyclic (1), {4,5,6,7}: tail (2) + local res = (math.floor(iParam/4)==0) and 0 or 1 + return res +end + +local function round(v) + -- round float + local factor = 100 + return math.floor(v * factor + 0.5) / factor +end + + +local function readValue(sensor) + -- read from sensor, round and return + local v = getValue(sensor) + --v = round(v) + return v +end + +local function readValueById(sensor) + local i = getFieldInfo(sensor) + if (i==nil) then return nil end + + local v = getValue(i.id) + return v +end + + + +local function readBatValue(sensor) + -- read from sensor, round and return + local v = getValue(sensor) + if (v==nil) then return v end + + return string.format("%2.2f",v) +end + +local function readActiveParamValue(sensor) + -- read and return a validated active parameter value + local v = getValue(sensor) + if (v<1 or v>8) then + return -1 + end + return v +end + + + +local function drawFlightLogScreen(event) + -- draw labels and params on screen + local h = getValue("Hold") + local activeParam = h-1 -- H + + lcd.clear() + lcd.drawText (X_COL1_HEADER, Y_HEADER, "Flight Log", TEXT_SIZE + INVERS) + + -- read and return parameters + local a = getValue("FdeA") + local b = getValue("FdeB") + local l = getValue("FdeL") + local r = getValue("FdeR") + local f = getValue("FLss") + + local titles = {[0]="A:", "B:", "L:", "R:", "F:", "H:"} + local values = {[0]=a,b,l,r,f,h} + + local y = Y_LINE_HEIGHT+Y_DATA + + for iParam=0,3 do -- A,B,L,R + -- highlight selected parameter (rund) + local attr = ((activeParam%4)==iParam) and INVERS or 0 + -- labels + local x = X_COL1_HEADER + local val = titles[iParam] + lcd.drawText (x, y, val, TEXT_SIZE) + + -- Values + val = values[iParam] + x = X_COL1_DATA + X_DATA_LEN + if (val~=16384) then -- Active value + lcd.drawText (x, y, val, attr + TEXT_SIZE + RIGHT) + end + + y = y + Y_LINE_HEIGHT + end + + y = Y_LINE_HEIGHT+Y_DATA + for iParam=4,5 do -- F, H + -- labels + local x = X_COL2_HEADER + local val = titles[iParam] + lcd.drawText (x, y, val, TEXT_SIZE + BOLD) + + -- Values + val = values[iParam] + x = X_COL2_DATA + X_DATA_LEN + lcd.drawText (x, y, val, TEXT_SIZE + RIGHT + BOLD) + + y = y + Y_LINE_HEIGHT + end + + -- Bat + y = y + Y_LINE_HEIGHT + local bat = readBatValue("A2") or "--" + lcd.drawText (X_COL2_HEADER, y, "Bat:", TEXT_SIZE) + lcd.drawText (X_COL2_DATA + X_DATA_LEN, y, bat, TEXT_SIZE + RIGHT) + lcd.drawText (X_COL2_DATA + X_DATA_LEN + X_DATA_SPACE, y, "v", TEXT_SIZE) + + +end + + + + + + +local function Unsigned_to_SInt16(value) + if value >= 0x8000 then -- Negative value?? + return value - 0x10000 + end + return value +end + +local function getDegreesValue(sensor) + local i = getFieldInfo(sensor) + if (i==nil) then return "-unk-" end + + local v = getValue(i.id) + if v==nil then return "---" end + local vs = Unsigned_to_SInt16(v) + + return string.format("%0.1f o",vs/10) +end + + +local function getDecHexValue(sensor) + local i = getFieldInfo(sensor) + if (i==nil) then return "-unk-" end + + local v = getValue(i.id) + if v==nil then return "---" end + local vs = Unsigned_to_SInt16(v) + + return string.format("%d (0x%04X)",vs,v) +end + +local as3xData = {[0]=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} +local function drawAS3XSettings(event, page) + local s0500 = getDecHexValue("0500") + local s0502 = getDecHexValue("0502") + local s0504 = getDecHexValue("0504") + local s0506 = getDecHexValue("0506") + local s0508 = getDecHexValue("0508") + local s050B = getDecHexValue("050B") + local s050D = getDecHexValue("050D") + + local d0500 = readValueById("0500") or 0 + local flags = bit32.rshift(d0500,8) + local state = bit32.band(d0500,0xFF) + + local flagsMsg="" + -- flags bits: Safe Envelop, ?, Angle Demand, Stab + if (bit32.band(flags,0x1)~=0) then flagsMsg=flagsMsg.."AS3X Stab" end + -- This one, only one should show + if (bit32.band(flags,0x2)~=0) then flagsMsg=flagsMsg..", Angle Demand" + elseif (bit32.band(flags,0x8)~=0) then flagsMsg=flagsMsg..", Safe Envelope" + elseif (bit32.band(flags,0x4)~=0) then flagsMsg=flagsMsg..", AS3X Heading" end + + local d0502 = readValueById("0502") or 0 -- 0x?F?S + local fm = bit32.band(bit32.rshift(d0502,8),0xF) -- 0,1,2 + + local axis = bit32.band(d0502,0xF) -- 0=Gains,1=Headings,2=Angle Limits (cointinus iterating to provide all values) + + local d0504 = readValueById("0504") or 0 + local d0506 = readValueById("0506") or 0 + local d0508 = readValueById("0508") or 0 + + local d0 = bit32.rshift(d0504,8) + local d1 = bit32.band(d0504,0xFF) + local d2 = bit32.rshift(d0506,8) + local d3 = bit32.band(d0506,0xFF) + local d4 = bit32.rshift(d0508,8) + local d5 = bit32.band(d0508,0xFF) + + --axis: 0=Gains+Headings (RG,PG,YG,RH,PH,YH), 1=Safe Gains (R,P,Y),2=Angle Limits(L,R,U,D) + --Constantly changing from 0..2 to represent different data, thats why we have to store the values + --in a script/global variable, and not local to the function + local s = axis*6 + as3xData[s+0] = d0 + as3xData[s+1] = d1 + as3xData[s+2] = d2 + as3xData[s+3] = d3 + as3xData[s+4] = d4 + as3xData[s+5] = d5 + + + lcd.clear() + lcd.drawText (0,0, "AS3X/SAFE Settings", TEXT_SIZE + INVERS) + + local y = Y_DATA + -- Flight Mode + lcd.drawText (X_COL1_HEADER,y, "FM: "..(fm+1), TEXT_SIZE) + lcd.drawText (X_COL1_DATA+X_DATA_LEN*0.3,y, "Flags: "..flags, TEXT_SIZE) + lcd.drawText (X_COL2_HEADER+X_DATA_LEN*0.3,y, "State: "..state, TEXT_SIZE) + + y = y + Y_LINE_HEIGHT + lcd.drawText (X_COL1_HEADER,y, flagsMsg, TEXT_SIZE) + + y = y + Y_LINE_HEIGHT + + if (page==1) then + lcd.drawText (X_COL1_HEADER+X_DATA_LEN*0.3,y, "AS3X Gains", TEXT_SIZE+BOLD) + lcd.drawText (X_COL2_HEADER+X_DATA_LEN*0.3,y, "AS3X Headings", TEXT_SIZE+BOLD) + + y = y + Y_LINE_HEIGHT + lcd.drawText (X_COL1_HEADER,y, "Roll:", TEXT_SIZE) + lcd.drawText (X_COL1_DATA+X_DATA_LEN,y, as3xData[0], TEXT_SIZE + RIGHT) -- Roll G + lcd.drawText (X_COL2_DATA+X_DATA_LEN,y, as3xData[3], TEXT_SIZE + RIGHT) -- Roll H + + y = y + Y_LINE_HEIGHT + lcd.drawText (X_COL1_HEADER,y, "Pitch:", TEXT_SIZE) + lcd.drawText (X_COL1_DATA+X_DATA_LEN,y,as3xData[1], TEXT_SIZE + RIGHT) -- Pitch G + lcd.drawText (X_COL2_DATA+X_DATA_LEN,y, as3xData[4], TEXT_SIZE + RIGHT) -- Pitch H + + y = y + Y_LINE_HEIGHT + lcd.drawText (X_COL1_HEADER,y, "Yaw:", TEXT_SIZE) + lcd.drawText (X_COL1_DATA+X_DATA_LEN,y, as3xData[2], TEXT_SIZE + RIGHT) -- Yaw G + lcd.drawText (X_COL2_DATA+X_DATA_LEN,y, as3xData[5], TEXT_SIZE + RIGHT) -- Yaw H + end + + + if (page==2) then + local x_data1 = X_COL1_DATA+X_DATA_LEN + local x_data2 = X_COL2_HEADER+X_DATA_LEN*1.6 + + lcd.drawText (X_COL1_HEADER+X_DATA_LEN*0.3,y, "SAFE Gains", TEXT_SIZE+BOLD) + lcd.drawText (X_COL2_HEADER+X_DATA_LEN*0.1,y, "Angle Limits", TEXT_SIZE+BOLD) + + y = y + Y_LINE_HEIGHT + lcd.drawText (X_COL1_HEADER,y, "Roll:", TEXT_SIZE) + lcd.drawText (x_data1,y, as3xData[6], TEXT_SIZE + RIGHT) + + lcd.drawText (X_COL2_HEADER,y, "Roll R:", TEXT_SIZE) + lcd.drawText (x_data2,y, as3xData[12], TEXT_SIZE + RIGHT) + + + y = y + Y_LINE_HEIGHT + lcd.drawText (X_COL1_HEADER,y, "Pitch:", TEXT_SIZE) + lcd.drawText (x_data1,y,as3xData[7], TEXT_SIZE + RIGHT) + + lcd.drawText (X_COL2_HEADER,y, "Roll L:", TEXT_SIZE) + lcd.drawText (x_data2,y,as3xData[13], TEXT_SIZE + RIGHT) + + + y = y + Y_LINE_HEIGHT + lcd.drawText (X_COL1_HEADER,y, "Yaw:", TEXT_SIZE) + lcd.drawText (x_data1,y, as3xData[8], TEXT_SIZE + RIGHT) + + lcd.drawText (X_COL2_HEADER,y, "Pitch U:", TEXT_SIZE) + lcd.drawText (x_data2,y, as3xData[14], TEXT_SIZE + RIGHT) + + y = y + Y_LINE_HEIGHT + lcd.drawText (X_COL2_HEADER,y, "Pitch D:", TEXT_SIZE) + lcd.drawText (x_data2,y, as3xData[15], TEXT_SIZE + RIGHT) + end + + -- Debug Values + if (DEBUG_ON) then + local titles = {[0]= + "0500","0502","0504","0506","0508","050B","050D"} + + local values = {[0]= + s0500,s0502,s0504,s0506,s0508,s050B,s050D } + + y = Y_LINE_HEIGHT*2 + Y_HEADER + for iParam=0,#titles do -- ?? + -- labels + local x = X_COL1_HEADER+250 + local val = titles[iParam] + lcd.drawText (x, y, val, TEXT_SIZE) + + val = values[iParam] or "--" + x = X_COL1_DATA+250 + lcd.drawText (x, y, val, TEXT_SIZE) + + y = y + Y_LINE_HEIGHT + end + end +end + +local function drawAS3XSettingsP1(event) + drawAS3XSettings(event, 1) +end + +local function drawAS3XSettingsP2(event) + drawAS3XSettings(event, 2) +end + + +local function doFloat(v) + if v==nil then return 0.0 end + + local vs = string.format("%1.2f",v) + + return vs + 0.0 +end + + +local ESC_Title={[0]="","RPM:","Volts:","Motor:","Mot Out:","Throttle:","FET Temp:", "BEC V:", "BEC T:", "BEC A:"} +local ESC_uom={[0]="","","V","A","%","%","C", "V","C","A"} +local ESC_Status={[0]=0,0,0,0,0,0,0,0,0,0,0} +local ESC_Min={[0]=0,0,0,0,0,0,0,0,0,0,0} +local ESC_Max={[0]=0,0,0,0,0,0,0,0,0,0,0} + +local function drawESCStatus(event) + lcd.clear() + ESC_Status[1] = getValue("Erpm") -- RPM + ESC_Status[2] = doFloat(getValue("EVIN")) -- Volts + ESC_Status[3] = doFloat(getValue("ECUR")) -- Current + ESC_Status[4] = doFloat(getValue("EOUT")) -- % Output + ESC_Status[5] = doFloat(getValue("ETHR")) -- Throttle % (EOUT) + ESC_Status[6] = getValue("TFET") -- Temp FET + + ESC_Status[7] = doFloat(getValue("VBEC")) -- Volts BEC + ESC_Status[8] = getValue("TBEC") -- Temp BEC + ESC_Status[9] = doFloat(getValue("CBEC")) -- Current BEC + + for i=1,9 do + if (ESC_Status~=nil) then + if (ESC_Min[i]==0) then + ESC_Min[i]=ESC_Status[i] + else + ESC_Min[i] = math.min(ESC_Min[i],ESC_Status[i]) + end + + ESC_Max[i] = math.max(ESC_Max[i],ESC_Status[i]) + end + end + + lcd.drawText (0,0, "ESC", TEXT_SIZE+INVERS) + + local y = 0 + local x_data = X_COL1_DATA+X_DATA_LEN*1.5 + local x_data2 = X_COL2_DATA+X_DATA_LEN*0.5 + local x_data3 = x_data2 + X_DATA_LEN*0.8 + + + lcd.drawText (x_data,y , "Status", TEXT_SIZE+BOLD+RIGHT) + lcd.drawText (x_data2,y, "Min", TEXT_SIZE+BOLD+RIGHT) + lcd.drawText (x_data3,y, "Max", TEXT_SIZE+BOLD+RIGHT) + + y = Y_DATA + for i=1,9 do + lcd.drawText (X_COL1_HEADER,y, ESC_Title[i], TEXT_SIZE + BOLD) + + lcd.drawText (x_data,y, ESC_Status[i] or "--", TEXT_SIZE + RIGHT) + lcd.drawText (x_data + X_DATA_SPACE,y, ESC_uom[i], TEXT_SIZE) + + lcd.drawText (x_data2,y, ESC_Min[i] or "--", TEXT_SIZE + RIGHT) + lcd.drawText (x_data3,y, ESC_Max[i] or "--", TEXT_SIZE + RIGHT) + y = y + Y_LINE_HEIGHT + end +end + + +local function drawBATStatus(event) + local Title={[0]="","Bat:","Temp:","Rem :","Curr:","Used:","Imbal:","Cycles:", "RX:", "BCpT?:"} + local uom={[0]="","V","C","%","mAh","mAh","mV","", "V",""} + local Values={[0]=0,0,0,0,0,0,0,0,0,0,0} + local CellValues={[0]=0,0,0,0,0,0,0,0,0,0,0} + + lcd.clear() + + local ESC_Volts = getValue("EVIN") or 0 -- Volts + local ESC_Current = getValue("ECUR") or 0 -- Current + + Values[1] = 0 -- compute later + Values[2] = getValue("BTmp") -- Current (C) + Values[3] = nil -- Remaining??? + Values[4] = getValue("BCur") -- Current (mAh) + Values[5] = getValue("BUse") -- Current Used (mAh) + Values[6] = getValue("CLMa") -- 0.0 (mV) Imbalance + Values[7] = getValue("Cycl") -- Cycles + Values[8] = readBatValue("A2") -- v + Values[9] = getValue("BCpT") -- Current (mAh) ???? + + + --- Total Voltange Calculation + local VTotal=0 + for i=1,10 do + CellValues[i] = getValue("Cel"..i) + VTotal = VTotal + CellValues[i] + end + + if (VTotal==0) then -- No Inteligent Battery,use intelligent ESC if any + VTotal = ESC_Volts + Values[4] = string.format("%d",ESC_Current * 1000) + end + + Values[1] = string.format("%2.2f",VTotal) + + --- SCREEN + + lcd.drawText (X_COL1_HEADER,0, "Battery Stats", TEXT_SIZE+INVERS) + + + + local y = Y_DATA + local x_data = X_COL1_DATA+X_DATA_LEN+X_DATA_SPACE*3 + for i=2,9 do + lcd.drawText (X_COL1_HEADER, y, Title[i], TEXT_SIZE + BOLD) + lcd.drawText (x_data, y, Values[i] or "--", TEXT_SIZE + RIGHT) + lcd.drawText (x_data+X_DATA_SPACE, y, uom[i], TEXT_SIZE) + y = y + Y_LINE_HEIGHT + end + + y = Y_DATA + x_data = X_COL2_DATA+X_DATA_LEN+X_DATA_SPACE*5 + for i=1,8 do + if ((CellValues[i] or 0) > 0) then + lcd.drawText (X_COL2_HEADER+X_DATA_LEN/2,y, "Cel "..i..":", TEXT_SIZE + BOLD) + lcd.drawText (x_data,y, string.format("%2.2f",CellValues[i] or 0), TEXT_SIZE + RIGHT) + lcd.drawText (x_data+X_DATA_SPACE,y, "v", TEXT_SIZE) + end + y = y + Y_LINE_HEIGHT + end + + lcd.drawText (X_COL2_HEADER+X_DATA_LEN/2,0, Title[1], TEXT_SIZE + INVERS + BOLD) + lcd.drawText (x_data,0, string.format("%2.2f",Values[1] or 0), TEXT_SIZE + INVERS+ RIGHT) + lcd.drawText (x_data+X_DATA_SPACE, 0, uom[1], TEXT_SIZE + INVERS) +end + + + + +local function openTelemetryRaw(i2cId) + --Init telemetry (Spectrun Telemetry Raw STR) + multiBuffer( 0, string.byte('S') ) + multiBuffer( 1, string.byte('T') ) + multiBuffer( 2, string.byte('R') ) + multiBuffer( 3, i2cId ) -- Monitor this teemetry data + multiBuffer( 4, 0 ) -- Allow to get Data +end + +local function closeTelemetryRaw() + multiBuffer(0, 0) -- Destroy the STR header + multiBuffer(3, 0) -- Not requesting any Telementry ID +end + +local lineText = {nil} +local I2C_TEXT_GEN = 0x0C +local function drawTextGen(event) + if (multiBuffer(0)~=string.byte('S')) then -- First time run??? + openTelemetryRaw(I2C_TEXT_GEN) -- I2C_ID for TEXT_GEN + lineText = {nil} + end + + -- Proces TEXT GEN Telementry message + if multiBuffer( 4 ) == I2C_TEXT_GEN then -- Specktrum Telemetry ID of data received + local instanceNo = multiBuffer( 5 ) + local lineNo = multiBuffer( 6 ) + + local line = "" + for i=0,13 do + line = line .. string.char(multiBuffer( 7 + i )) + end + + multiBuffer( 4, 0 ) -- Clear Semaphore, to notify that we fully process the current message + lineText[lineNo]=line + end + + lcd.clear() + -- Header + if (lineText[0]) then + lcd.drawText (X_COL1_HEADER,0, " "..lineText[0].." ", TEXT_SIZE + BOLD + INVERS) + else + lcd.drawText (X_COL1_HEADER,0, "TextGen", TEXT_SIZE+INVERS) + end + + -- Menu lines + local y = Y_DATA + for i=1,8 do + if (lineText[i]) then + lcd.drawText (X_COL1_HEADER,y, lineText[i], TEXT_SIZE) + end + y = y + Y_LINE_HEIGHT + end + + if event == EVT_VIRTUAL_EXIT then -- Exit?? Clear menu data + closeTelemetryRaw() + end +end + + +local telPage = 1 +local telPageSelected = 0 +local pageTitle = {[0]="Main", "AS3X Settings", "SAFE Settings", "ESC Status", "Battery Status","TextGen","Flight Log"} + +local function drawMainScreen(event) + lcd.clear() + lcd.drawText (X_COL1_HEADER, Y_HEADER, "Main Telemetry (Smart RXs)", TEXT_SIZE + INVERS) + + for iParam=1,#pageTitle do + -- highlight selected parameter + local attr = (telPage==iParam) and INVERS or 0 + + -- set y draw coord + local y = (iParam)*Y_LINE_HEIGHT+Y_DATA + + -- labels + local x = X_COL1_HEADER + local val = pageTitle[iParam] + lcd.drawText (x, y, val, attr + TEXT_SIZE) + end + + if event == EVT_VIRTUAL_PREV then + if (telPage>1) then telPage = telPage - 1 end + elseif event == EVT_VIRTUAL_NEXT then + if (telPage<#pageTitle) then telPage = telPage + 1 end + elseif event == EVT_VIRTUAL_ENTER then + telPageSelected = telPage + end +end + + +local pageDraw = {[0]=drawMainScreen, drawAS3XSettingsP1, drawAS3XSettingsP2, drawESCStatus, drawBATStatus, drawTextGen, drawFlightLogScreen} + +local function run_func(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + end + + -- draw specific page + pageDraw[telPageSelected](event) + + if event == EVT_VIRTUAL_EXIT then + if (telPageSelected==0) then return 1 end -- on Main?? Exit Script + telPageSelected = 0 -- any page, return to Main + end + + return 0 +end + +local function init_func() + + if (LCD_W <= 128 or LCD_H <=64) then -- Smaller Screens + TEXT_SIZE = SMLSIZE + X_COL1_HEADER = 0 + X_COL1_DATA = 20 + + X_COL2_HEADER = 60 + X_COL2_DATA = 90 + + X_DATA_LEN = 28 + X_DATA_SPACE = 1 + + + Y_LINE_HEIGHT = 8 + Y_DATA = Y_HEADER + Y_LINE_HEIGHT + + end +end + +return { run=run_func, init=init_func } diff --git a/SCRIPTS/TOOLS/Game-Breakout.lua b/SCRIPTS/TOOLS/Game-Breakout.lua new file mode 100755 index 0000000..7eac869 --- /dev/null +++ b/SCRIPTS/TOOLS/Game-Breakout.lua @@ -0,0 +1,508 @@ +-------------------------------------------------------- +-- BREAKOUT FOR X-LITE V2.0 +-------------------------------------------------------- +-- Basic script (c)2016 for Taranis by 'travir' +-- Rewritten and enhanced for the X-Lite +-- (c)2018 by Mike Dogan (mike-vom-mars.com) +-------------------------------------------------------- + +local screenWidth = math.floor( LCD_W ) +local screenHeight = math.floor( LCD_H ) + +local score = 0 +local lives = 5 +local level = 1 +local map = 1 +local bricks = {} +local brickWidth = 8 +local brickHeight = 6 +local brickGapWidth = 2 +local brickGapHeight = 2 +local visibleBricks = 0 + +local showSplash = getTime() +local playSplash = true +local showGameOver = 0 +local showLevelUp = 0 +local showStats = false +local playStats = false + +local paddle = {} +paddle.width = 24 +paddle.height = 4 +paddle.x = screenWidth / 2 - paddle.width / 2 +paddle.y = screenHeight - paddle.height +paddle.dx = 1 + +local ball = {} +ball.width = 4 +ball.height = 4 +ball.x = 0 +ball.y = 0 +ball.dx = 0 +ball.dy = 0 +local lx = ball.x +local ly = ball.y + +local oldTime + +local stats = + { + begin = 0, + finish = 0, + kills = 0, + score = 0, + level = 0, + } + +local maps = + { + { + 1,1,1,1,1,0,0,1,1,1,1,1, + 1,1,1,1,1,0,0,1,1,1,1,1, + 1,1,1,1,1,0,0,1,1,1,1,1, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + }, + { + 1,0,1,0,1,0,1,0,1,0,1,0, + 0,1,0,1,0,1,0,1,0,1,0,1, + 1,0,1,0,1,0,1,0,1,0,1,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + }, + { + 2,2,2,2,2,0,0,2,2,2,2,2, + 2,2,2,2,2,0,0,2,2,2,2,2, + 1,1,1,1,1,0,0,1,1,1,1,1, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + }, + { + 2,2,2,0,2,0,2,0,0,2,2,2, + 2,1,2,0,2,0,2,0,0,2,1,2, + 2,1,2,0,2,0,2,0,0,2,1,2, + 2,2,2,0,2,0,2,0,0,2,2,2, + 0,0,0,0,0,0,0,0,0,0,0,0, + }, + { + 3,3,3,3,3,0,0,3,3,3,3,3, + 2,2,2,2,2,0,0,2,2,2,2,2, + 2,2,2,2,2,0,0,2,2,2,2,2, + 3,3,3,3,3,0,0,3,3,3,3,3, + 0,0,0,0,0,0,0,0,0,0,0,0, + }, + { + 1,2,3,1,2,3,1,2,3,1,2,3, + 2,3,1,2,3,1,2,3,1,2,3,1, + 3,1,2,3,1,2,3,1,2,3,1,2, + 1,2,3,1,2,3,1,2,3,1,2,3, + 0,0,0,0,0,0,0,0,0,0,0,0, + }, + { + 3,1,3,1,3,1,1,3,1,3,1,3, + 3,1,3,1,3,1,1,3,1,3,1,3, + 3,1,3,1,3,1,1,3,1,3,1,3, + 3,1,3,1,3,1,1,3,1,3,1,3, + 0,0,0,0,0,0,0,0,0,0,0,0, + }, + { + 0,3,2,3,2,3,3,2,3,2,3,0, + 0,0,3,2,3,2,2,3,2,3,0,0, + 0,0,0,3,2,3,3,2,3,0,0,0, + 0,0,0,0,3,2,2,3,0,0,0,0, + 0,0,0,0,0,3,3,0,0,0,0,0, + }, + { + 3,2,0,3,2,0,3,2,0,3,2,0, + 3,2,0,3,2,0,3,2,0,3,2,0, + 3,2,0,3,2,0,3,2,0,3,2,0, + 3,2,0,3,2,0,3,2,0,3,2,0, + 3,2,0,3,2,0,3,2,0,3,2,0, + }, + { + 1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2, + 1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3, + }, + { + 3,3,3,0,3,3,3,3,0,3,3,3, + 3,2,3,0,0,0,0,0,0,3,2,3, + 3,3,3,0,3,3,3,3,0,3,3,3, + 0,0,0,0,3,2,2,3,0,0,0,0, + 3,3,3,0,3,3,3,3,0,3,3,3, + }, + { + 3,3,3,3,3,3,3,3,3,3,3,3, + 3,1,1,1,1,1,1,1,1,1,1,1, + 3,3,3,3,3,3,3,3,3,3,3,3, + 1,1,1,1,1,1,1,1,1,1,1,3, + 3,3,3,3,3,3,3,3,3,3,3,3, + }, + { + 3,3,3,1,3,3,3,1,3,3,3,1, + 3,1,3,1,3,1,3,1,3,1,3,1, + 3,1,3,1,3,1,3,1,3,1,3,1, + 3,1,3,1,3,1,3,1,3,1,3,1, + 3,1,3,3,3,1,3,3,3,1,3,3, + }, + } + + +--------------------------------------- + +local function createBricks() + local k + local i = 1 + local col = 1 + local row = 1 + visibleBricks = 0 + + for k in pairs (bricks) do bricks[k] = nil end + bricks = {} + + for row = 1,5 do + for col = 1,12 do + local p = (row-1)*12 + col + if maps[map][p] > 0 then + bricks[i] = {} + bricks[i].x = 5 + (col-1) * (brickWidth +brickGapWidth) + bricks[i].y = 9 + (row-1) * (brickHeight+brickGapHeight) + bricks[i].lives = maps[map][p] + i = i + 1 + visibleBricks = visibleBricks + 1 + end + end + end +end + +--------------------------------------- + +local function resetBall() + ball.dx = 0 + ball.dy = 0 +end + +--------------------------------------- + +local function reset(won) + -- NEW GAME? + if won == nil then + stats.begin = getTime() + stats.score = 0 + stats.kills = 0 + stats.level = 1 + -- GAME OVER? + elseif won == false then + stats.level = level + stats.finish = getTime() + showGameOver = getTime() + playFile("/SCRIPTS/TOOLS/BREAKOUT/snd/gover.wav") + end + + -- RESET VARS + lives = won == true and lives or 5 + score = won == true and score or 0 + level = won == true and level + 1 or 1 + map = won == true and map + 1 or 1; if map > #maps then map = 1 end + -- PLACE PADDLE + paddle.x = screenWidth / 2 - paddle.width / 2 + paddle.y = screenHeight - paddle.height + + resetBall() + createBricks() +end + +--------------------------------------- + +local function drawAll() + -- BRICKS + for i, brick in pairs(bricks) do + if brick.lives == 1 then + lcd.drawRectangle(brick.x, brick.y, brickWidth, brickHeight) + elseif brick.lives == 2 then + lcd.drawRectangle(brick.x, brick.y, brickWidth, brickHeight) + lcd.drawFilledRectangle(brick.x+2, brick.y+2, brickWidth-4, brickHeight-4, 0) + elseif brick.lives == 3 then + lcd.drawFilledRectangle(brick.x, brick.y, brickWidth, brickHeight, 0) + end + end + -- BALL & BAT + lcd.drawRectangle(paddle.x, paddle.y, paddle.width, paddle.height, 0) + lcd.drawFilledRectangle(paddle.x+3, paddle.y+1, paddle.width-6, paddle.height-2, 0) + lcd.drawFilledRectangle(ball.x, ball.y, ball.width, ball.height, 0) + -- TEXTS + if ball.dx == 0 and ball.dy == 0 then + lcd.drawText(7, 1, " PULL STICK UP! ", SMLSIZE + INVERS + BLINK) + else + --lcd.drawText(100, 0, visibleBricks, 0) + lcd.drawText(5, 1, "LIVES: " .. lives, SMLSIZE + (lives == 0 and BLINK or 0) ) + lcd.drawText(55, 1, "SCORE: " .. score, SMLSIZE) + end +end + +--------------------------------------- + +local function collisionDetection(aX, aY, aW, aH, bX, bY, bW, bH) + if aY + aH < bY then + return false + elseif aY > bY + bH then + return false + elseif aX + aW < bX then + return false + elseif aX > bX + bW then + return false + else + return true; + end +end + +--------------------------------------- + +local function update(deltaTime) + if collisionDetection(ball.x, ball.y, ball.width, ball.height, + paddle.x, paddle.y, paddle.width, paddle.height) then + + -- RANDOMIZE A BIT + local xr = math.random(90,110)/100 + local yr = math.random(90,110)/100 + + if ball.y > paddle.y - paddle.height then + ball.y = ball.y + paddle.y - ball.y - ball.height + end + + if ball.dx > 0 then -- BALL MOVING TO RIGHT? + if ball.x < paddle.x + ball.width then + ball.dx = -ball.dx * xr + end + elseif ball.dx < 0 then -- BALL MOVING TO LEFT? + if ball.x > paddle.x + paddle.width - ball.width then + ball.dx = -ball.dx * xr + end + end + + ball.dy = -ball.dy * yr + + playFile("/SCRIPTS/TOOLS/BREAKOUT/snd/bat.wav") + end + + for i, brick in pairs(bricks) do + if brick.lives > 0 then + if collisionDetection(ball.x, ball.y, ball.width, ball.height, + brick.x, brick.y, brickWidth, brickHeight) then + + if ball.y < brick.y + brickHeight then -- Hit was below the brick + ball.dy = -ball.dy + elseif ball.y + ball.height > brick.y then -- Hit was above the brick + ball.dy = -ball.dy + end + + if ball.x < brick.x + brickWidth then -- Brick hit on right + ball.dx = -ball.dx + elseif ball.x + ball.width > brick.x then -- Brick hit on left + ball.dx = -ball.dx + end + + ball.x = lx + ball.y = ly + + brick.lives = brick.lives - 1 + if brick.lives == 0 then + stats.kills = stats.kills + 1 + visibleBricks = visibleBricks - 1 + playFile("/SCRIPTS/TOOLS/BREAKOUT/snd/brick.wav") + else + playFile("/SCRIPTS/TOOLS/BREAKOUT/snd/brick.wav") + end + score = score + 10; stats.score = score + if visibleBricks == 0 then + playFile("/SCRIPTS/TOOLS/BREAKOUT/snd/up.wav") + reset(true) + showLevelUp = getTime() + end + end + end + end + + + -- BALL STICKS TO BAT? + if ball.dx == 0 and ball.dy == 0 then + ball.x = paddle.x + paddle.width/2 + ball.y = paddle.y - paddle.height - 2 + -- THROW BALL! + if getValue("ele") > 500 then + playFile("/SCRIPTS/TOOLS/BREAKOUT/snd/throw.wav") + ball.dx = math.random(1,2) == 1 and -0.55 or 0.55 + ball.dy = -0.5 + print(getValue("thr")) + end + -- BALL IS MOVING + else + lx = ball.x + ly = ball.y + ball.x = ball.x + ball.dx * deltaTime + ball.y = ball.y + ball.dy * deltaTime + end + + if getValue('ail') > 150 or getValue('rud') > 150 then + paddle.x = paddle.x + paddle.dx * deltaTime + end + if getValue('ail') < -150 or getValue('rud') < -150 then + paddle.x = paddle.x - paddle.dx * deltaTime + end + + --if getValue('ele') > 999 or getValue('ele') < -999 then + -- playFile("/SCRIPTS/TOOLS/BREAKOUT/snd/up.wav") + -- reset(true) + -- showLevelUp = getTime() + --end + + if ball.x + ball.width > screenWidth then + ball.x = screenWidth - ball.width + ball.dx = -ball.dx + playFile("/SCRIPTS/TOOLS/BREAKOUT/snd/border.wav") + elseif ball.x < 0 then + ball.x = 0 + ball.dx = -ball.dx + playFile("/SCRIPTS/TOOLS/BREAKOUT/snd/border.wav") + end + + if ball.y < 0 then + ball.y = 0 + ball.dy = -ball.dy + playFile("/SCRIPTS/TOOLS/BREAKOUT/snd/border.wav") + -- LOST BALL? + elseif ball.y + ball.height > screenHeight then + resetBall() + lives = lives - 1 + + if lives >= 0 then + playFile("/SCRIPTS/TOOLS/BREAKOUT/snd/killed.wav") + elseif lives < 0 then + reset(false) + end + end + + if paddle.x + paddle.width > screenWidth then + paddle.x = screenWidth - paddle.width + elseif paddle.x < 0 then + paddle.x = 0 + end +end + +--------------------------------------- + +function secsToClock(seconds) + local seconds = tonumber(seconds) + + if seconds <= 0 then + return "00:00:00"; + else + hours = string.format("%02.f", math.floor(seconds/3600)); + mins = string.format("%02.f", math.floor(seconds/60 - (hours*60))); + secs = string.format("%02.f", math.floor(seconds - hours*3600 - mins *60)); + return hours..":"..mins..":"..secs + end +end + +--------------------------------------- + +local function init() + -- lcd.lock() + lcd.clear() + createBricks() + reset() + drawAll() + oldTime = getTime() + +end + +--------------------------------------- + +local function run(event) + if event == EVT_EXIT_BREAK then return 2 end + + local newTime = getTime() + local deltaTime = newTime - oldTime + lcd.clear() + + -- SPLASH SCREEN + if showSplash > 0 then + if newTime < (showSplash+250) then + if playSplash == true then + playFile("/SCRIPTS/TOOLS/BREAKOUT/snd/splash.wav") + playSplash = false + end + lcd.drawPixmap(0,0, "/SCRIPTS/TOOLS/BREAKOUT/gfx/splash1.bmp") + lcd.drawPixmap(64,0, "/SCRIPTS/TOOLS/BREAKOUT/gfx/splash2.bmp") + return 0 + else + showSplash = 0 + end + + -- GAME OVER SCREEN + elseif showGameOver > 0 then + if newTime < (showGameOver+350) then + lcd.drawPixmap(0,0, "/SCRIPTS/TOOLS/BREAKOUT/gfx/gover1.bmp") + lcd.drawPixmap(64,0, "/SCRIPTS/TOOLS/BREAKOUT/gfx/gover2.bmp") + return 0 + else + showGameOver = 0 + showStats = true + playStats = true + return 0 + end + + -- SHOW STATS SCREEN + elseif showStats == true then + if playStats == true then + playFile("/SCRIPTS/TOOLS/BREAKOUT/snd/stats.wav") + playStats = false + end + lcd.drawRectangle(2, 2, screenWidth-4, screenHeight-4) + lcd.drawText(5,6, "Level:", 0) + lcd.drawText(5,16, "Your score:", 0) + lcd.drawText(5,26, "Time played:", 0) + lcd.drawText(5,36, "Bricks killed:", 0) + lcd.drawText(85,6, stats.level, 0) + lcd.drawText(85,16, stats.score, 0) + lcd.drawText(85,26, secsToClock( (stats.finish-stats.begin)/100 ), 0) + lcd.drawText(85,36, stats.kills, 0) + lcd.drawText(6,51, "-STICK UP TO CONTINUE-", INVERS) + -- DISMISS? + if getValue("ele") > 500 then + showStats = false + -- SPLASH SCREEN + showSplash = getTime() + playSplash = true + return 0 + end + + -- LEVEL UP SCREEN + elseif showLevelUp > 0 then + if newTime < (showLevelUp+250) then + lcd.drawPixmap(0,0, "/SCRIPTS/TOOLS/BREAKOUT/gfx/up1.bmp") + lcd.drawPixmap(64,0, "/SCRIPTS/TOOLS/BREAKOUT/gfx/up2.bmp") + return 0 + else + showLevelUp = 0 + end + + -- GAME RUNNING + else + update(deltaTime) + drawAll() + end + -- lcd.lock() + + oldTime = newTime + + + + return 0 +end + + +return {init=init, run=run} diff --git a/SCRIPTS/TOOLS/Game-Breakout.luac b/SCRIPTS/TOOLS/Game-Breakout.luac new file mode 100755 index 0000000000000000000000000000000000000000..aa0dc92a3d102c9f542658814de4f86d43454591 GIT binary patch literal 10238 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 red_value then + if value > green_value then + return lcd.RGB(0, 0xdf, 0) + end + if value < red_value then + return lcd.RGB(0xdf, 0, 0) + end + g = math.floor(0xdf * (value - red_value) / range) + r = 0xdf - g + return lcd.RGB(r, g, 0) + else + if value > green_value then + return lcd.RGB(0, 0xdf, 0) + end + if value < red_value then + return lcd.RGB(0xdf, 0, 0) + end + r = math.floor(0xdf * (value - green_value) / range) + g = 0xdf - r + return lcd.RGB(r, g, 0) + end +end + +local function main(event) + + lcd.clear() + + -- fetch uplink rssi (HoTT sensor Rssi) + local rssi = getValue("Rssi") + + -- calculate a percentage for the color bar (range -115db to -15db) + local rssiP = rssi + + if(rssi ~= 0) then -- rssi < 0 -> telemtry data received + if(rssi >= -15) then -- -15db to -1db => 100% + rssiP = 100 + else + if(rssi >= -115) then -- between -115db and -15db => 0% to 100% + rssiP = 100+rssi+15 + else + rssiP = 0 -- less than -115 => 0% + end + end + end + + lcd.drawBitmap(img, 250, 50, 40) + + -- Title + lcd.drawText(3, 3, "Graupner HoTT Rssi Model Locator", 0) + myColor = getRangeColor(rssi, 0, 100) + lcd.setColor(CUSTOM_COLOR, myColor) + + -- draw current value + local dx = 0 + if rssi < -99 then + dx = -33 + end + + if rssi == 0 then + lcd.drawText(115, 73, "no telemetry", DBLSIZE + CUSTOM_COLOR) + else + lcd.drawNumber(180+dx, 30, rssi, XXLSIZE + CUSTOM_COLOR) + lcd.drawText(275, 73, "db", 0 + CUSTOM_COLOR) + end + + -- draw main bar + lcd.setColor(CUSTOM_COLOR, YELLOW) -- RED / YELLOW + local xMin = 0 + local yMin = 270 + local xMax = 480 + local yMax = 200 + local h = 0 + local rssiAsX = (rssiP * xMax) / 100 + + for xx = xMin, rssiAsX, 20 do + lcd.setColor(CUSTOM_COLOR, getRangeColor(xx, xMin, xMax - 40)) + h = h + 10 + lcd.drawFilledRectangle(xx, yMin - h, 15, h, CUSTOM_COLOR) + end + + -- beep + if getTime() >= nextPlayTime then + playFile("/SCRIPTS/TOOLS/Model Locator (by RSSI).wav") + nextPlayTime = getTime() + delayMillis - rssiP + end + + return 0 +end + +return {init = init,run = main,background = bg} + diff --git a/SCRIPTS/TOOLS/Graupner HoTT.lua b/SCRIPTS/TOOLS/Graupner HoTT.lua new file mode 100644 index 0000000..d0a7d4a --- /dev/null +++ b/SCRIPTS/TOOLS/Graupner HoTT.lua @@ -0,0 +1,171 @@ +---- ######################################################################### +---- # # +---- # 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 HoTT description +-- To start operation: +-- Write "HoTT" at address 0..3 +-- Write 0xFF at address 4 will request the buffer to be cleared +-- Write 0x0F at address 5 +-- Read buffer from address 6 access the RX text for 168 bytes, 21 caracters +-- by 8 lines +-- Write at address 5 sends an order to the RX: 0xXF=start, 0xX7=prev page, +-- 0xXE=next page, 0xX9=enter, 0xXD=next or 0xXB=prev with X being the sensor +-- to request data from 8=RX only, 9=Vario, A=GPS, B=Cust, C=ESC, D=GAM, E=EAM +-- Write at address 4 the value 0xFF will request the buffer to be cleared +-- !! Before exiting the script must write 0 at address 0 for normal operation !! +--############################################################################### + +HoTT_Sensor = 0 +Timer_128 = 100 + +local function HoTT_Release() + multiBuffer( 0, 0 ) +end + +local function HoTT_Send(button) + multiBuffer( 5, 0x80+(HoTT_Sensor*16) + button) +end + +local function HoTT_Sensor_Inc() + local detected_sensors=multiBuffer( 4 ) + local a + if detected_sensors ~= 0xFF then + repeat + HoTT_Sensor=(HoTT_Sensor+1)%7 -- Switch to next sensor + if HoTT_Sensor ~= 0 then + a = math.floor(detected_sensors/ (2^(HoTT_Sensor-1))) -- shift right + end + until HoTT_Sensor==0 or a % 2 == 1 + HoTT_Send( 0x0F ) + end +end + +local function HoTT_Draw_LCD() + local i + local value + local line + local result + local offset=0 + + local sensor_name = { "", "+Vario", "+GPS", "+Cust", "+ESC", "+GAM", "+EAM" } + + lcd.clear() + + if LCD_W == 480 then + --Draw title + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, "Graupner HoTT: config RX" .. sensor_name[HoTT_Sensor+1] .. " - Menu cycle Sensors", MENU_TITLE_COLOR) + --Draw RX Menu + if multiBuffer( 4 ) == 0xFF then + lcd.drawText(10,50,"No HoTT telemetry...", BLINK) + else + 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(10+i*16,32+20*line,string.char(value).." ",INVERS) + else + lcd.drawText(10+i*16,32+20*line,string.char(value)) + end + end + end + end + else + --Draw RX Menu on LCD_W=128 + if multiBuffer( 4 ) == 0xFF then + lcd.drawText(2,17,"No HoTT telemetry...",SMLSIZE) + else + if Timer_128 ~= 0 then + --Intro page + Timer_128 = Timer_128 - 1 + lcd.drawScreenTitle("Graupner Hott",0,0) + lcd.drawText(2,17,"Configuration of RX" .. sensor_name[HoTT_Sensor+1] ,SMLSIZE) + lcd.drawText(2,37,"Press menu to cycle Sensors" ,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 HoTT_Init() + --Set protocol to talk to + multiBuffer( 0, string.byte('H') ) + --test if value has been written + if multiBuffer( 0 ) ~= string.byte('H') then + error("Not enough memory!") + return 2 + end + multiBuffer( 1, string.byte('o') ) + multiBuffer( 2, string.byte('T') ) + multiBuffer( 3, string.byte('T') ) + --Request init of the RX buffer + multiBuffer( 4, 0xFF ) + --Request RX to send the config menu + HoTT_Send( 0x0F ) + HoTT_Sensor = 0; + HoTT_Detected_Sensors=0; + Timer_128 = 100 +end + +-- Main +local function HoTT_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_EXIT then + HoTT_Release() + return 2 + else + if event == EVT_VIRTUAL_PREV_PAGE then + killEvents(event) + HoTT_Send( 0x07 ) + elseif event == EVT_VIRTUAL_ENTER then + HoTT_Send( 0x09 ) + elseif event == EVT_VIRTUAL_PREV then + HoTT_Send( 0x0B ) + elseif event == EVT_VIRTUAL_NEXT then + HoTT_Send( 0x0D ) + elseif event == EVT_VIRTUAL_NEXT_PAGE then + HoTT_Send( 0x0E ) + elseif event == EVT_VIRTUAL_MENU then + Timer_128 = 100 + HoTT_Sensor_Inc() + else + HoTT_Send( 0x0F ) + end + HoTT_Draw_LCD() + return 0 + end +end + +return { init=HoTT_Init, run=HoTT_Run } diff --git a/SCRIPTS/TOOLS/ModelLocator.lua b/SCRIPTS/TOOLS/ModelLocator.lua new file mode 100755 index 0000000..c22a200 --- /dev/null +++ b/SCRIPTS/TOOLS/ModelLocator.lua @@ -0,0 +1,157 @@ +---- TNS|Model Locator|TNE +---- ######################################################################### +---- # # +---- # Telemetry Widget script for b&w 128x64 radios # +---- # Copyright (C) EdgeTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### + +-- Model Locator by RSSI +-- AegisCrusader & Offer Shmuely (based on code from Scott Bauer 6/21/2015) +-- Date: 2022-2024 +-- ver: 0.6 +local app_ver = "0.6" + +-- This widget help to find a lost/crashed model based on the RSSI (if still available) +-- The widget produce audio representation (vario-meter style) of the RSSI from the lost model +-- The widget also displays the RSSI in a visible bar + +-- There are two way to use it +-- 1. The simple way: +-- walk toward the quad/plane that crashed, +-- as you get closer to your model the beeps will become more frequent with higher pitch (and a visual bar graph as well) +-- until you get close enough to find it visually + +-- 2. the more accurate way: +-- turn the antenna straight away (i.e. to point from you, straight away) +-- try to find the weakest signal! (not the highest), i.e. the lowest RSSI you can find, this is the direction to the model. +-- now walk to the side (not toward the model), find again the weakest signal, this is also the direction to your model +-- triangulate the two lines, and it will be :-) + +local delayMillis = 100 +local targetTXPower = 25 +local nextPlayTime = getTime() +local useHaptic = false + +local function getSignalValues() + -- try to get transmitter power + local txPowerField = getFieldInfo("TPWR") + local txPowerValue = nil + if txPowerField then + txPowerValue = getValue("TPWR") + end + + -- try regular Frsky RSSI + local fieldinfo = getFieldInfo("RSSI") + if fieldinfo then + local v = getValue("RSSI") + return v, 0, 100, txPowerValue, "Using signal: Frsky RSSI", nil + end + + -- try expressLRS antenna 1 + local fieldinfo = getFieldInfo("1RSS") + if fieldinfo then + local v = getValue("1RSS") + if v == 0 then + v = -115 + end + return v, -115, 20, txPowerValue, "Using signal: ELRS 1RSS", "Set TX PWR 25mW No Dyn" + end + + -- try expressLRS antenna 2 + local fieldinfo = getFieldInfo("2RSS") + if fieldinfo then + local v = getValue("2RSS") + if v == 0 then + v = -115 + end + return v, -115, 20, txPowerValue, "Using signal: ELRS 2RSS", "Set TX PWR 25mW No Dyn" + end + + return nil, 0, 0 +end + +local function init() + lcd.clear() +end + +local function run(event) + -- exit script + if event == EVT_VIRTUAL_EXIT then + return 2 + end + + lcd.clear() + + local signalValue, signalMin, signalMax, txPower, line1, line2 = getSignalValues() + + lcd.drawText(0, 0, "Model Locator by RSSI", BOLD) + + if signalValue == nil then + lcd.drawText(0, 24, "No signal, expected:", 0 + BLINK) + lcd.drawText(0, 32, "RSSI/1RSS/2RSS", 0 + BLINK) + return 0 + end + + if txPower then + lcd.drawText(0, 16, "Current TX PWR: " .. tostring(txPower) .. "mW") + + if txPower ~= targetTXPower then + lcd.drawText(0, 8, line2 or "", BLINK) + else + lcd.drawText(0, 8, "[ENTER] to toggle haptic") + end + else + lcd.drawText(0, 8, "[ENTER] to toggle haptic") + end + + local signalPercent = 100 * ((signalValue - signalMin) / (signalMax - signalMin)) + + lcd.drawText(0, 24, tostring(signalValue) .. "dB", DBLSIZE) + + -- draw main bar + local xMin = 10 + local yMin = LCD_H - 10 + local xMax = LCD_W + local h = 0 + local rssiAsX = (signalPercent * xMax) / 100 + + for xx = xMin, rssiAsX, 7 do + h = h + 2 + lcd.drawFilledRectangle(xx, yMin - h, 5, h) + end + + lcd.drawFilledRectangle(0, LCD_H - 10, LCD_W, 1) + + -- current signal type + lcd.drawText(0, LCD_H - 8, line1) + + -- toggle haptic + if event == EVT_VIRTUAL_ENTER then + useHaptic = not useHaptic + end + + -- beep + if getTime() >= nextPlayTime then + playFile("/SCRIPTS/TOOLS/modloc.wav") + if useHaptic then + playHaptic(7, 0, 1) + end + nextPlayTime = getTime() + delayMillis - signalPercent + end + + return 0 +end + +return {init = init, run = run} diff --git a/SCRIPTS/TOOLS/ModelLocator.luac b/SCRIPTS/TOOLS/ModelLocator.luac new file mode 100755 index 0000000000000000000000000000000000000000..aa0ca2ceacf2969cafd7d1c763c63988fad7a814 GIT binary patch literal 1738 zcmaJ>L2nyX5T1GO?K(+m3*AzhBDG5t^@JLz1c#h<*Y-9RZd%!HQm9bmW;dyYV@Hld zlM`>(p$8;R98oWTIPnL#d7a{(S|sjVs)!SR0hRAgI=9_Qk&Ai#StJ}>w zO@DKI>fDqvYiEgwyq}8<@z3JP6L>NT-y=#i(Rk?5fz~9UY-9iA6aASG9ZQL(QckBv zNqSKbUnEU7{FHj{(uYojCuiEcPOQiMMr$KhR;$zME7jZXfCV8W{%H=oZXW2h0Dm@l zdEi-1T*CVd_W2>{d)lMSBl-b)2)+kgHavX)j)3um9s|P}Az6^KW}lf7pJ~WYq)a72 zbdj|j!a4q4t@Tc`J@1CStzTw?iz@Cs~-2=#!VN2+{+(qEW3+cx3t?))@5UTnVT_~ZoXvQfNnY0 z3k6|1Tkt%Z^)#6iqMo=a?bS%xtL?bidj-qKfPIV2bml%p2$l<;={IQ~at)SHdvs+u zqzKGqFh|3L#^}RC&Ljs+K6a8ajYmEm_*z_`5&V@tia}pf0Zc%TMrmqrN9;o8J$uof z`AmN>@r>!cE*#}cfged4ixT0Yi-VMyi;C_GX&v|^eu3Z0kVSrPU(7XC^nF0m+`nX` z@_bA#=58Iy2e?F7I1hn?Uuk=c2|kf({*6=lYnFUvr|5$4RbG<-=gsrel;=0F=yx%& z?ENy$23oMNDGWkd6k~NS4Jkt%PYzOfs#vx?NlHtyvwfTH?6ZCp46SeveNYc4w4#Z} z4^qYD0(q9UqLLQtgTKBA2e8YBc7pHCxERu7IWE|HA^Cd+ii%ns+Oy~O!Gi*#h^R;+ z+`odpOVpWkS!%w8k*z?6a)F|OrY}+BcRX1*4~MY0K*?YNoWuruTbkKq9qvPqd^5!7 zJSKSm_1EI+U|=uqC*URF1G6TH%}t#p+|)P9OO2JKO099judbADRvONKFR+@7dd<6y zc&DEar8;x98^vw6+Ffn-yFK^zvtbObRZHZYHKcE1z7FU0T{qs@j92?{G_UNST3P(a zaSh@m=BqMihI7u0n)7;|OL4o`i#yLob>3Bz{q9!3hw)c(W7(N7AXd}2%8QM1?Nhhk z#czGR9lLj$oBh`6;rBvDftn}<)q3TVvNL5|>}s*JvT!Jtoq|!FHfIW3?RFg1kY2L` z7RQ3cG4rZU9jS_m_WW}{x!G>+!ZlXs=IX^-<$9w&*SL1ATA$nKM(ytErMu0~)iDEF U$g{FRh-tQ1uwsdC;s30^0gxVgj{pDw literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/MultiChan.txt b/SCRIPTS/TOOLS/MultiChan.txt new file mode 100644 index 0000000..402b7de --- /dev/null +++ b/SCRIPTS/TOOLS/MultiChan.txt @@ -0,0 +1,233 @@ +24,0,Assan,Std,0,CH5,CH6,CH7,CH8 +14,0,Bayang,Std,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,n-a,n-a,AnAux1,AnAux2 +14,1,Bayang,H8S3D,1,Flip,RTH,Pict,Video,HLess,Invert,Rates +14,2,Bayang,X16_AH,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf +14,3,Bayang,IRDRONE,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop +14,4,Bayang,DHD_D4,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop +14,5,Bayang,QX100,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop +59,0,BayangRX,RX,1,AnAux1,AnAux2,Flip,RTH,Pict,Video +59,1,BayangRX,CPPM,1,AnAux1,AnAux2,Flip,RTH,Pict,Video +41,0,Bugs,3-6-8,0,Arm,Angle,Flip,Pict,Video,LED +42,0,BugsMini,Mini,0,Arm,Angle,Flip,Pict,Video,LED +42,1,BugsMini,3H,0,Arm,Angle,Flip,Pict,Video,LED,AltHol +34,0,Cabell,V3,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +34,1,Cabell,V3Telem,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +13,0,CG023,Std,1,Flip,Light,Pict,Video,HLess +13,1,CG023,YD829,1,Flip,n-a,Pict,Video,HLess +37,0,Corona,COR_V1,0,CH5,CH6,CH7,CH8 +37,1,Corona,COR_V2,0,CH5,CH6,CH7,CH8 +37,2,Corona,FD_V3,0,CH5,CH6,CH7,CH8 +12,0,CX10,Green,1,Flip,Rate +12,1,CX10,Blue,1,Flip,Rate,Pict,Video +12,2,CX10,DM007,1,Flip,Mode,Pict,Video,HLess +12,4,CX10,JC3015_1,1,Flip,Mode,Pict,Video +12,5,CX10,JC3015_2,1,Flip,Mode,LED,DFlip +12,6,CX10,MK33041,1,Flip,Mode,Pict,Video,HLess,RTH +7,0,Devo,8CH,0,CH5,CH6,CH7,CH8 +7,1,Devo,10CH,0,CH5,CH6,CH7,CH8,CH9,CH10 +7,2,Devo,12CH,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 +7,3,Devo,6CH,0,CH5,CH6 +7,4,Devo,7CH,0,CH5,CH6,CH7 +33,0,DM022,Std,1,Flip,LED,Cam1,Cam2,HLess,RTH,RLow +6,0,DSM,2_1F,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill +6,1,DSM,2_2F,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill +6,2,DSM,X_1F,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill +6,3,DSM,X_2F,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill +6,4,DSM,AUTO,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill +6,5,DSM,R_1F,0,AUX3,AUX4,AUX5 +6,6,DSM,2SFC,0 +70,0,DSM_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 +70,1,DSM_RX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 +45,0,E01X,E012,1,n-a,Flip,n-a,HLess,RTH +45,1,E01X,E015,1,Arm,Flip,LED,HLess,RTH +16,0,ESKY,Std,0,Gyro,Pitch +16,1,ESKY,ET4,0,Gyro,Pitch +35,0,ESKY150,4CH,0 +35,1,ESKY150,7CH,0,FMode,Aux6,Aux7 +69,0,ESKY150V2,Std,0,CH5_RA,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +1,0,Flysky,Flysky,0,CH5,CH6,CH7,CH8 +1,1,Flysky,V9x9,1,Flip,Light,Pict,Video +1,2,Flysky,V6x6,1,Flip,Light,Pict,Video,HLess,RTH,XCAL,YCAL +1,3,Flysky,V912,1,BtmBtn,TopBtn +1,4,Flysky,CX20,0,CH5,CH6,CH7 +28,0,Flysky_AFHDS2A,PWM_IBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 +28,1,Flysky_AFHDS2A,PPM_IBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 +28,2,Flysky_AFHDS2A,PWM_SBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 +28,3,Flysky_AFHDS2A,PPM_SBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 +28,4,Flysky_AFHDS2A,PWM_IB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +28,5,Flysky_AFHDS2A,PPM_IB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +28,6,Flysky_AFHDS2A,PWM_SB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +28,7,Flysky_AFHDS2A,PPM_SB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +56,0,Flysky2A_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 +56,1,Flysky2A_RX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 +53,0,Height,5ch,0,Gear +53,1,Height,8ch,0,Gear,Gyro,Flap,Light +25,0,FrSkyV,V8,0,CH5,CH6,CH7,CH8 +3,0,FrSkyD,D8,0,CH5,CH6,CH7,CH8 +3,0,FrSkyD,D8Cloned,0,CH5,CH6,CH7,CH8 +67,0,FrSkyL,LR12,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 +67,1,FrSkyL,LR12_6CH,0,CH5,CH6 +15,0,FrSkyX,D16_FCC,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +15,1,FrSkyX,D16_8CH_FCC,0,CH5,CH6,CH7,CH8 +15,2,FrSkyX,D16_LBT,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +15,3,FrSkyX,D16_8CH_LBT,0,CH5,CH6,CH7,CH8 +15,4,FrSkyX,D16Cloned,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +15,5,FrSkyX,D16Cloned_8CH,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +64,0,FrSkyX2,D16_FCC,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +64,1,FrSkyX2,D16_8CH_FCC,0,CH5,CH6,CH7,CH8 +64,2,FrSkyX2,D16_LBT,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +64,3,FrSkyX2,D16_8CH_LBT,1,CH5,CH6,CH7,CH8 +64,4,FrSkyX2,D16Cloned,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +64,5,FrSkyX2,D16Cloned_8CH,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +65,0,FrSkyR9,R9_915,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +65,1,FrSkyR9,R9_868,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +65,2,FrSkyR9,R9_915_8CH,0,CH5,CH6,CH7,CH8 +65,3,FrSkyR9,R9_968_8CH,0,CH5,CH6,CH7,CH8 +55,0,FrSkyRX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +55,1,FrSkyRX,CloneTX,0 +55,2,FrSkyRX,EraseTX,0 +55,3,FrSkyRX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +58,0,FX,816,1 +58,1,FX,620,1 +58,2,FX,9630,1,Rate,Gyro,TrimR,TrimA,TrimE +58,3,FX,Q560,1,FLIP,Gyro,LEDs +20,0,FY326,FY326,1,Flip,RTH,HLess,Expert,Calib +20,1,FY326,FY319,1,Flip,RTH,HLess,Expert,Calib +23,0,FY326,FY326,1,Flip,RTH,HLess,Expert +47,0,GD00x,V1,1,Trim,LED,Rate +47,1,GD00x,V2,1,Trim,LED,Rate +32,0,GW008,FY326,1,Flip +36,0,H8_3D,Std,1,Flip,Light,Pict,Video,RTH,FlMode,Cal1 +36,1,H8_3D,H20H,1,Flip,Light,Pict,Video,Opt1,Opt2,Cal1,Cal2,Gimbal +36,2,H8_3D,H20,1,Flip,Light,Pict,Video,Opt1,Opt2,Cal1,Cal2,Gimbal +36,3,H8_3D,H30,1,Flip,Light,Pict,Video,Opt1,Opt2,Cal1,Cal2,Gimbal +4,0,Hisky,Std,0,Gear,Pitch,Gyro,CH8 +4,1,Hisky,HK310,0,Aux +39,0,Hitec,Opt_Fw,0,CH5,CH6,CH7,CH8,CH9 +39,1,Hitec,Opt_Hub,0,CH5,CH6,CH7,CH8,CH9 +39,2,Hitec,Minima,0,CH5,CH6,CH7,CH8,CH9 +26,0,Hontai,Std,1,Flip,LED,Pict,Video,HLess,RTH,Calib +26,1,Hontai,JJRCX1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib +26,2,Hontai,X5C1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib +26,3,Hontai,FQ777_951,1,Flip,Arm,Pict,Video,HLess,RTH,Calib +26,4,Hontai,XKK170,1,Rate,Emerg,TakLan,Calib,TrimA,TrimE +57,0,HoTT,Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +57,1,HoTT,No_Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +2,0,Hubsan,H107,1,Flip,Light,Pict,Video,HLess +2,1,Hubsan,H301,0,RTH,Light,Stab,Video +2,2,Hubsan,H501,0,RTH,Light,Pict,Video,HLess,GPS_H,ALT_H,Flip,FModes +22,0,J6Pro,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 +71,0,JJRC345,JJRC345,1,Flip,HLess,RTH,LED,UNK1,UNK2,UNK3 +71,1,JJRC345,SkyTmblr,1,Flip,HLess,RTH,LED,UNK1,UNK2,UNK3 +49,0,KF606,KF606,1,Trim +49,1,KF606,MIG320,1,Trim,LED +49,2,KF606,ZCZ50,1,Trim,UNK +9,0,KN,WLToys,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim,Rtrim,Hover +9,1,KN,Feilun,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim,Rtrim,Hover +73,0,Kyosho,FHSS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 +73,1,Kyosho,Hype,0,CH5,CH6 +18,0,MJXQ,WHL08,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate +18,1,MJXQ,X600,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate +18,2,MJXQ,X800,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate +18,3,MJXQ,H26D,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate +18,4,MJXQ,E010,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate +18,5,MJXQ,H26WH,1,Flip,Arm,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate +18,6,MJXQ,Phoenix,1,Flip,Arm,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate +17,0,MT99XX,Std,1,Flip,LED,Pict,Video,HLess +17,1,MT99XX,H7,1,Flip,LED,Pict,Video,HLess +17,2,MT99XX,YZ,1,Flip,LED,Pict,Video,HLess +17,3,MT99XX,LS,1,Flip,Invert,Pict,Video,HLess +17,4,MT99XX,FY805,1,Flip,n-a,n-a,n-a,HLess +17,5,MT99XX,A180,0,3D_6G +17,6,MT99XX,Dragon,0,Mode,RTH +17,7,MT99XX,F949G,0,6G_3D,Light +44,0,NCC1701,Std,1,Warp +77,0,OMP,M2,0,THold,IdleUp,6G_3D +60,0,Pelikan,PRO_V4,0,CH5,CH6,CH7,CH8 +60,1,Pelikan,LITE_V4,0,CH5,CH6,CH7,CH8 +60,2,Pelikan,SCX24,0 +51,0,Potensic,A20,1,TakLan,Emerg,Mode,HLess +66,0,Propel,74-Z,1,LEDs,RollCW,RolCCW,Fire,Weapon,Calib,AltHol,TakeOf,Land,Train +29,0,Q2x2,Q222,1,Flip,LED,Mod2,Mod1,HLess,RTH,XCal,YCal +29,1,Q2x2,Q242,1,Flip,LED,Pict,Video,HLess,RTH,XCal,YCal +29,2,Q2x2,Q282,1,Flip,LED,Pict,Video,HLess,RTH,XCal,YCal +31,0,Q303,Q303,1,AltHol,Flip,Pict,Video,HLess,RTH,Gimbal +31,1,Q303,C35,1,Arm,VTX,Pict,Video,n-a,RTH,Gimbal +31,2,Q303,CX10D,1,Arm,Flip +31,3,Q303,CX10WD,1,Arm,Flip +72,0,Q90C,Std,0,FMode,VTX+ +74,0,RadioLink,Surface,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8 +74,1,RadioLink,Air,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8 +74,2,RadioLink,DumboRC,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8 +74,3,RadioLink,RC4G,0,CH5,FS_CH1,FS_CH2,FS_CH3,FS_CH4 +76,0,Realacc,Std,1,Flip,Light,Calib,HLess,RTH,ThCut,Rotat +50,0,Redpine,Fast,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16 +50,1,Redpine,Slow,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16 +21,0,Futaba,SFHSS,0,CH5,CH6,CH7,CH8 +19,0,Shenqi,Cycle,1 +68,0,Skyartec,Std,0,CH5,CH6,CH7 +11,0,SLT,V1,0,Gear,Pitch +11,1,SLT,V2,0,CH5,CH6,CH7,CH8 +11,2,SLT,Q100,0,Rates,n-a,CH7,CH8,Mode,Flip,n-a,n-a,Calib +11,3,SLT,Q200,0,Rates,n-a,CH7,CH8,Mode,VidOn,VidOff,Calib +11,4,SLT,MR100,0,Rates,n-a,CH7,CH8,Mode,Flip,Video,Pict +11,5,SLT,V1_4CH,0 +11,6,SLT,RF_SIM,0,CH5,CH6,CH7,CH8,CH9,CH10 +10,0,Symax,Std,1,Flip,Rates,Pict,Video,HLess +10,1,Symax,X5C,1,Flip,Rates,Pict,Video,HLess +43,0,Traxxas,TQ2,0 +43,1,Traxxas,TQ1,0 +5,0,V2x2,Std,1,Flip,Light,Pict,Video,HLess,CalX,CalY +5,1,V2x2,JXD506,1,Flip,Light,Pict,Video,HLess,StaSto,Emerg,Cam_UD +48,0,V761,3CH,0,Gyro,Calib,Flip,RtnAct,Rtn,Beep +48,1,V761,4CH,0,Gyro,Calib,Flip,RtnAct,Rtn,Beep +48,2,V761,TOPRC,0,Gyro,Calib,Flip,RtnAct,Rtn +46,0,V911s,V911s,1,Calib,Rate +46,1,V911s,E119,1,Calib,Rate,6G_3D +22,0,WFLY,WFR0xS,0,CH5,CH6,CH7,CH8,CH9 +30,0,WK2x01,WK2801,0,CH5,CH6,CH7,CH8 +30,1,WK2x01,WK2401,0 +30,2,WK2x01,W6_5_1,0,Gear,Dis,Gyro +30,3,WK2x01,W6_6_1,0,Gear,Col,Gyro +30,4,WK2x01,W6HEL,0,Gear,Col,Gyro +30,5,WK2x01,W6HEL_I,0,Gear,Col,Gyro +62,0,XK,X450,1,FMode,TakeOf,Emerg,3D_6G,Pict,Video +62,1,XK,X420,1,FMode,TakeOf,Emerg,3D_6G,Pict,Video,Flip,Light +62,2,XK,Cars,0,FMode,TakeOf,Emerg,3D_6G,Pict,Video,Flip,Light +99,0,XK2,X4,0,Rate,Mode,Hover,Light +99,1,XK2,P10,0,Rate,Mode,Hover,Light +8,0,YD717,Std,1,Flip,Light,Pict,Video,HLess +8,1,YD717,SkyWlkr,1,Flip,Light,Pict,Video,HLess +8,2,YD717,Simax4,1,Flip,Light,Pict,Video,HLess +8,3,YD717,XinXun,1,Flip,Light,Pict,Video,HLess +8,4,YD717,NiHui,1,Flip,Light,Pict,Video,HLess +52,0,ZSX,280,1,Light +78,0,M-Link,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +79,0,WFLY2,RF20x,0,CH5,CH6,CH7,CH8,CH9,CH10 +80,0,E016Hv2,E016Hv2,1,TakLan,EmStop,Flip,Calib,HLess,RTH +81,0,E010r5,E010r5,1,Flip,LED,CALIB,HLess,RTH,GLIDE +82,0,LOLI,Std,0,CH5,CH6,CH7,CH8,1SwSePpPw,2SwSePw,3SwSe,4SwSe,5SwSeSb,6SwSe,7SwSePw,8SwSe +83,0,E129,E129,1,TakLan,EmStop,TrimA,TrimE,TrimR +83,1,E129,C186,1,TakLan,EmStop,TrimA,TrimE,TrimR,Loop,Flip,Debug +84,0,JOYSWAY,Std,0 +85,0,E016H,Std,1,Stop,Flip,n-a,HLess,RTH +87,0,IKEA +89,0,LOSI +90,0,MouldKg,Analog,0 +90,1,MouldKg,Digit,0 +91,0,Xerall,Tank,0,FlTa,TakLan,Rate,HLess,Photo,Video,TrimR,TrimE,TrimA +92,0,MT99xx2,PA18,0,MODE,FLIP,RTH +92,1,MT99xx2,SU35,0,Mode,LED,LED_FH,Invert,Rate +93,0,Kyosho2,KT-17,0 +94,0,Scorpio +95,0,Bluefly,HP100,0,CH5,CH6,CH7,CH8 +96,0,BumbleB +97,0,SGF22,F22,1,Mode,Flip,LED,Pict,Video,TrRes,Bal,HiBal +97,1,SGF22,F22S,1,Mode,Flip,LED,Pict,Video,TrRes +97,2,SGF22,J20,1,Mode,Flip,LED,Pict,Video +61,0,EazyRC +98,0,Kyosho3,ASF,0 +100,0,YuXiang,Std,0,Lock,Rate,Land,Manual,Flip,Mode,Pitch +102,0,JIABAILE,Std,0,Speed,Light,Flash +102,1,JIABAILE,Gyro,0,Speed,Light,Flash,Tr_ST +103,0,H36,Std,1,Flip,HLess,RTH diff --git a/SCRIPTS/TOOLS/MultiChannelsUpdater.lua b/SCRIPTS/TOOLS/MultiChannelsUpdater.lua new file mode 100644 index 0000000..a905cd8 --- /dev/null +++ b/SCRIPTS/TOOLS/MultiChannelsUpdater.lua @@ -0,0 +1,327 @@ + +local toolName = "TNS|Multi chan namer|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. # +---- # # +---- ######################################################################### + +local protocol_name = "" +local sub_protocol_name = "" +local bind_ch = 0 +local module_conf = {} +local module_pos = "Internal" +local file_ok = 0 +local done = 0 +local protocol = 0 +local sub_protocol = 0 +local f_seek = 0 +local channel_names={} +local num_search = "Searching" + +local function drawScreenTitle(title) + if LCD_W == 480 then + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, title, MENU_TITLE_COLOR) + else + lcd.drawScreenTitle(title, 0, 0) + end +end + +function bitand(a, b) + local result = 0 + local bitval = 1 + while a > 0 and b > 0 do + if a % 2 == 1 and b % 2 == 1 then -- test the rightmost bits + result = result + bitval -- set the current bit + end + bitval = bitval * 2 -- shift left + a = math.floor(a/2) -- shift right + b = math.floor(b/2) + end + return result +end + +local function Multi_Draw_LCD(event) + local line = 0 + + lcd.clear() + drawScreenTitle("Multi channels namer") + + --Display settings + local lcd_opt = 0 + if LCD_W == 480 then + x_pos = 10 + y_pos = 32 + y_inc = 20 + else + x_pos = 0 + y_pos = 9 + y_inc = 8 + lcd_opt = SMLSIZE + end + + --Multi Module detection + if module_conf["Type"] ~= 6 then + if LCD_W == 480 then + lcd.drawText(10,50,"No Multi module configured...", BLINK) + else + --Draw on LCD_W=128 + lcd.drawText(2,17,"No Multi module configured...",SMLSIZE) + end + return + else + lcd.drawText(x_pos, y_pos+y_inc*line,module_pos .. " Multi detected.", lcd_opt) + line = line + 1 + end + + --Channel order + if (ch_order == -1) then + lcd.drawText(x_pos, y_pos+y_inc*line,"Channels order can't be read from Multi...", lcd_opt) + line = line + 1 + end + + --Can't open file MultiChan.txt + if file_ok == 0 then + lcd.drawText(x_pos, y_pos+y_inc*line,"Can't read MultiChan.txt file...", lcd_opt) + return + end + + if ( protocol_name == "" or sub_protocol_name == "" ) and f_seek ~=-1 then + local f = io.open("/SCRIPTS/TOOLS/MultiChan.txt", "r") + if f == nil then return end + lcd.drawText(x_pos, y_pos+y_inc*line,num_search, lcd_opt) + num_search = num_search .. "." + if #num_search > 15 then + num_search = string.sub(num_search,1,9) + end + local proto = 0 + local sub_proto = 0 + local proto_name = "" + local sub_proto_name = "" + local channels = "" + local nbr_try = 0 + local nbr_car = 0 + repeat + io.seek(f, f_seek) + local data = io.read(f, 100) -- read 100 characters + if #data ==0 then + f_seek = -1 -- end of file + break + end + proto, sub_proto, proto_name, sub_proto_name, bind_ch, channels = string.match(data,'(%d+),(%d),([%w-_ ]+),([%w-_ ]+),(%d)(.+)') + if proto ~= nil and sub_proto ~= nil and protocol_name ~= nil and sub_protocol_name ~= nil and bind_ch ~= nil then + if tonumber(proto) == protocol and tonumber(sub_proto) == sub_protocol then + protocol_name = proto_name + sub_protocol_name = sub_proto_name + bind_ch = tonumber(bind_ch) + if channels ~= nil then + --extract channel names + nbr_car = string.find(channels, "\r") + if nbr_car == nil then nbr_car = string.find(channels, "\n") end + if nbr_car ~= nil then + channels = string.sub(channels,1,nbr_car-1) + end + local i = 5 + for k in string.gmatch(channels, ",([%w-_ ]+)") do + channel_names[i] = k + i = i + 1 + end + end + f_seek = -1 -- protocol found + break + end + end + if f_seek ~= -1 then + nbr_car = string.find(data, "\n") + if nbr_car == nil then nbr_car = string.find(data, "\r") end + if nbr_car == nil then + f_seek = -1 -- end of file + break + end + f_seek = f_seek + nbr_car -- seek to next line + nbr_try = nbr_try + 1 + end + until nbr_try > 20 or f_seek == -1 + io.close(f) + end + + if f_seek ~= -1 then + return -- continue searching... + end + + --Protocol & Sub_protocol + if protocol_name == "" or sub_protocol_name == "" then + lcd.drawText(x_pos, y_pos+y_inc*line,"Unknown protocol "..tostring(protocol).."/"..tostring(sub_protocol).." ...", lcd_opt) + return + elseif LCD_W > 128 then + lcd.drawText(x_pos, y_pos+y_inc*line,"Protocol: " .. protocol_name .. " / SubProtocol: " .. sub_protocol_name, lcd_opt) + line = line + 1 + else + lcd.drawText(x_pos, y_pos+y_inc*line,"Protocol: " .. protocol_name, lcd_opt) + line = line + 1 + lcd.drawText(x_pos, y_pos+y_inc*line,"SubProtocol: " .. sub_protocol_name, lcd_opt) + line = line + 1 + end + + text1="" + text2="" + for i,v in ipairs(channel_names) do + if i<=8 then + if i==1 then + text1 = v + else + text1=text1 .. "," .. v + end + else + if i==9 then + text2 = v + else + text2=text2 .. "," .. v + end + end + end + if LCD_W > 128 then + lcd.drawText(x_pos, y_pos+y_inc*line,"Channels: " .. text1, lcd_opt) + line = line + 1 + if text2 ~= "" then + lcd.drawText(x_pos*9, y_pos+y_inc*line,text2, lcd_opt) + line = line + 1 + end + end + + if event ~= EVT_VIRTUAL_ENTER and done == 0 then + lcd.drawText(x_pos, y_pos+y_inc*line," Save", lcd_opt + INVERS + BLINK) + return + end + + lcd.drawText(x_pos, y_pos+y_inc*line,"Setting channel names.", lcd_opt) + line = line + 1 + local output, nbr + if done == 0 then + for i,v in ipairs(channel_names) do + output = model.getOutput(i-1) + output["name"] = v + model.setOutput(i-1,output) + nbr = i + end + for i = nbr, 15 do + output = model.getOutput(i) + output["name"] = "n-a" + model.setOutput(i,output) + end + if bind_ch == 1 then + output = model.getOutput(15) + output["name"] = "BindCH" + model.setOutput(15,output) + end + done = 1 + end + lcd.drawText(x_pos, y_pos+y_inc*line,"Done!", lcd_opt) + line = line + 1 +end + +-- Init +local function Multi_Init() + module_conf = model.getModule(0) + if module_conf["Type"] ~= 6 then + module_pos = "External" + module_conf = model.getModule(1) + if module_conf["Type"] ~= 6 then + return + end + end + + protocol = module_conf["protocol"] + sub_protocol = module_conf["subProtocol"] + + --Exceptions on first 4 channels... + local stick_names = { "Rud", "Ele", "Thr", "Ail" } + if ( protocol == 4 and sub_protocol == 1 ) or protocol == 19 or protocol == 52 then -- Hisky/HK310, Shenqi, ZSX + stick_names[2] = "n-a" + stick_names[4] = "n-a" + elseif protocol == 43 then -- Traxxas + stick_names[2] = "Aux4" + stick_names[4] = "Aux3" + elseif ( protocol == 48 and sub_protocol == 0 ) then -- V761 3CH + stick_names[4] = "n-a" + elseif protocol == 47 or protocol == 49 or ( protocol == 58 and sub_protocol < 2 ) then -- GD00x, KF606, FX816 + stick_names[1] = "n-a" + stick_names[2] = "n-a" + end + + --Determine fist 4 channels order + local ch_order=module_conf["channelsOrder"] + if (ch_order == -1) then + channel_names[1] = stick_names[defaultChannel(0)+1] + channel_names[2] = stick_names[defaultChannel(1)+1] + channel_names[3] = stick_names[defaultChannel(2)+1] + channel_names[4] = stick_names[defaultChannel(3)+1] + else + channel_names[bitand(ch_order,3)+1] = stick_names[4] + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = stick_names[2] + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = stick_names[3] + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = stick_names[1] + end + + --Exceptions on first 4 channels... + if ( protocol == 73 or (protocol == 74 and sub_protocol == 0) or (protocol == 60 and sub_protocol == 2) or protocol == 89) then -- Kyosho or RadioLink Surface or Pelikan/SCX24 or Losi + channel_names[1] = "ST" + channel_names[2] = "THR" + channel_names[3] = "CH3" + if(protocol == 60 and sub_protocol == 2) then + channel_names[4] = "n-a" + else + channel_names[4] = "CH4" + end + end + if ( protocol == 6 and sub_protocol == 5 ) then -- DSMR + channel_names[1] = "ST" + channel_names[2] = "THR" + channel_names[3] = "AUX1" + channel_names[4] = "AUX2" + end + if ( protocol == 90 ) then -- Mould King + channel_names[1] = "A" + channel_names[2] = "B" + channel_names[3] = "C" + channel_names[4] = "D" + end + + --Check MultiChan.txt + local f = io.open("/SCRIPTS/TOOLS/MultiChan.txt", "r") + if f == nil then return end + file_ok = 1 + io.close(f) +end + +-- Main +local function Multi_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + else + Multi_Draw_LCD(event) + if event == EVT_VIRTUAL_EXIT then + return 2 + end + end + return 0 +end + +return { init=Multi_Init, run=Multi_Run } diff --git a/SCRIPTS/TOOLS/MultiConfig.lua b/SCRIPTS/TOOLS/MultiConfig.lua new file mode 100644 index 0000000..01042fe --- /dev/null +++ b/SCRIPTS/TOOLS/MultiConfig.lua @@ -0,0 +1,533 @@ +---- ######################################################################### +---- # # +---- # 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 Config description +-- To start operation: +-- Write 0xFF at address 4 will request the buffer to be cleared +-- Write "Conf" at address 0..3 +-- Read +-- Read at address 12 gives the current config page +-- Read at address 13..172 gives the current data of the page = 8 lines * 20 caracters +-- Write +-- Write at address 5..11 the command +-- Write 0x01 at address 4 will send the command to the module +-- !! Before exiting the script must write 0 at address 0 for normal operation !! +--############################################################################### + +local Version = "v0.2" +local Focus = -1 +local Page = 0 +local Edit = -1 +local Edit_pos = 1 +local Menu = { {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""} } +local Menu_value = {} +local Blink = 0 +local ModuleNumber = 0 +local ModuleType = "" +local Module = {} +local InitialProtocol = 0 +local InitialSubProtocol = 0 + +function bitand(a, b) + local result = 0 + local bitval = 1 + while a > 0 and b > 0 do + if a % 2 == 1 and b % 2 == 1 then -- test the rightmost bits + result = result + bitval -- set the current bit + end + bitval = bitval * 2 -- shift left + a = math.floor(a/2) -- shift right + b = math.floor(b/2) + end + return result +end + +local function Config_Send(page, line, value) + local i + i = (page*16) + line + multiBuffer( 5, i ) + for i = 1 , 6 , 1 do + multiBuffer( 5+i, value[i] ) + end + multiBuffer( 4, 1 ) +end + +local function Config_Release() + --Set the protocol back to what it was + Module.protocol = InitialProtocol + Module.subProtocol = InitialSubProtocol + model.setModule(ModuleNumber, Module) + + --Stop requesting updates + local i + for i = 3 , 0 , -1 do + multiBuffer( i, 0 ) + end +end + +local function Config_Page( ) + Config_Send(Page, 0, { 0, 0, 0, 0, 0, 0 }) +end + +local function Config_Draw_Edit( event ) + local i + local text + + if Menu[Focus].field_type == 0xD0 then + -- Editable Hex value + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 1 + Blink = 0 + for i = 1, Menu[Focus].field_len, 1 do + Menu_value[i] = Menu[Focus].field_value[i] + end + end + if Edit == 0 then + -- Not editing value + if event == EVT_VIRTUAL_ENTER then + if Edit_pos > Menu[Focus].field_len then + -- Save or Cancel + Edit = -1 + if Edit_pos == Menu[Focus].field_len + 1 then + -- Save + Config_Send(Page, Focus, Menu_value) + end + return + else + -- Switch to edit mode + Edit = 1 + end + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Move cursor + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < Menu[Focus].field_len + 2 then + -- Move cursor + Edit_pos = Edit_pos + 1 + end + else + -- Editing value + if event == EVT_VIRTUAL_ENTER then + -- End edit + Edit = 0 + elseif event == EVT_VIRTUAL_PREV then + -- Change value + if Menu_value[Edit_pos] > 0 then + Menu_value[Edit_pos] = Menu_value[Edit_pos] - 1 + end + elseif event == EVT_VIRTUAL_NEXT then + -- Change value + if Menu_value[Edit_pos] < 255 then + Menu_value[Edit_pos] = Menu_value[Edit_pos] + 1 + end + end + --Blink + Blink = Blink + 1 + if Blink > 30 then + Blink = 0 + end + end + --Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + for i = 1, Menu[Focus].field_len, 1 do + if i==Edit_pos and (Edit ~= 1 or Blink > 15) then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170+12*2*(i-1), 110, string.format('%02X', Menu_value[i]), attrib) + else + lcd.drawText(17+6*2*(i-1), 10, string.format('%02X', Menu_value[i]), attrib + SMLSIZE) + end + end + if Edit_pos == Menu[Focus].field_len + 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Save", attrib) + else + lcd.drawText(17, 30, "Save", attrib + SMLSIZE) + end + if Edit_pos == Menu[Focus].field_len + 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "Cancel", attrib) + else + lcd.drawText(77, 30, "Cancel", attrib + SMLSIZE) + end + + elseif Menu[Focus].field_type == 0x90 then + -- Action text + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 2 + end + if event == EVT_VIRTUAL_ENTER then + -- Exit + Edit = -1 + if Edit_pos == 1 then + -- Yes + Config_Send(Page, Focus, { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA } ) + end + return + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Switch to Yes + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < 2 then + -- Switch to No + Edit_pos = Edit_pos + 1 + end + -- Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + if LCD_W == 480 then + lcd.drawText(170, 110, Menu[Focus].field_text .. "?") + else + lcd.drawText(17, 10, Menu[Focus].field_text .. "?", SMLSIZE) + end + if Edit_pos == 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Yes", attrib) + else + lcd.drawText(17, 30, "Yes", attrib + SMLSIZE) + end + if Edit_pos == 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "No", attrib) + else + lcd.drawText(77, 30, "No", attrib) + end + end +end + +local function Config_Next_Prev( event ) +-- Next Prev on main menu + local line + if event == EVT_VIRTUAL_PREV then + for line = Focus - 1, 1, -1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + elseif event == EVT_VIRTUAL_NEXT then + for line = Focus + 1, 7, 1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + end +end + +local function Config_Draw_Menu() +-- Main menu + local i + local value + local line + local length + local text + + lcd.clear() + + if LCD_W == 480 then + --Draw title + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, "Multi Config " .. Version, MENU_TITLE_COLOR) + if multiBuffer(13) == 0x00 then + lcd.drawText(10,50,"No Config telemetry...", BLINK) + end + else + --Draw on LCD_W=128 + lcd.drawText(1, 0, "Multi Config " .. Version, SMLSIZE) + if multiBuffer(13) == 0x00 then + lcd.drawText(2,17,"No Config telemetry...",SMLSIZE) + end + end + + if multiBuffer(13) ~= 0x00 then + if LCD_W == 480 then + --Draw firmware version and channels order + local ch_order = multiBuffer(17) + local channel_names = {} + channel_names[bitand(ch_order,3)+1] = "A" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "E" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "T" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "R" + lcd.drawText(150, 5, ModuleType.." v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16) .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4], MENU_TITLE_COLOR) + else + lcd.drawText(76, 0, "/Fw" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16),SMLSIZE) -- .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4]) + end + + --Draw Menu + for line = 1, 7, 1 do + --Clear line info + Menu[line].text = "" + Menu[line].field_type = 0 + Menu[line].field_len = 0 + for i = 1, 7, 1 do + Menu[line].field_value[i] = 0 + end + Menu[line].field_text = "" + length = 0 + --Read line from buffer + for i = 0, 20-1, 1 do + value=multiBuffer( line*20+13+i ) + if value > 0x80 and Menu[line].field_type == 0 then + -- Read field type + Menu[line].field_type = bitand(value, 0xF0) + Menu[line].field_len = bitand(value, 0x0F) + length = Menu[line].field_len + if Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 and Focus == -1 then + -- First actionnable field if nothing was selected + Focus = line; + end + else + if Menu[line].field_type == 0 then + -- Text + if value == 0 then + break -- end of line + end + Menu[line].text = Menu[line].text .. string.char(value) + else + -- Menu specific fields + length = length - 1 + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + Menu[line].field_text = Menu[line].field_text .. string.char(value) + else + Menu[line].field_value[Menu[line].field_len-length] = value + end + if length == 0 then + -- End of fields + break + end + end + end + end + -- Display menu text + if Menu[line].text ~= "" then + if Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 or Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + Menu[line].text = Menu[line].text .. ":" + end + if LCD_W == 480 then + lcd.drawText(10,32+20*line,Menu[line].text ) + else + lcd.drawText(2,1+8*line,Menu[line].text,SMLSIZE) + end + end + -- Display specific fields + if line == Focus then + attrib = INVERS + else + attrib = 0 + end + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + -- Text + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, Menu[line].field_text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, Menu[line].field_text, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 then + -- Decimal value + value = 0 + for i = 1, Menu[line].field_len, 1 do + value = value*256 + value + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, value, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, value, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + -- Hex value + text="" + for i = 1, Menu[line].field_len, 1 do + text = text .. string.format('%02X ', Menu[line].field_value[i]) + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, text, SMLSIZE + attrib) + end + end + end + end +end + +-- Init +local function Config_Init() + --Find Multi module + Module_int = model.getModule(0) + Module_ext = model.getModule(1) + if Module_int["Type"] ~= 6 and Module_ext["Type"] ~= 6 then + error("No Multi module detected...") + return 2 + end + if Module_int["Type"] == 6 and Module_ext["Type"] == 6 then + error("Two Multi modules detected, turn on only the one to be configured.") + return 2 + end + if Module_int["Type"] == 6 then + ModuleNumber = 0 + ModuleType = "Internal" + else + ModuleNumber = 1 + ModuleType = "External" + end + --Get Module settings and set it to config protocol + Module = model.getModule(ModuleNumber) + InitialProtocol = Module.protocol + InitialSubProtocol = Module.subProtocol + Module.protocol = 86 + Module.subProtocol = 0 + model.setModule(ModuleNumber, Module) + --pause while waiting for the module to switch to config + for i = 0, 10, 1 do end + + --Set protocol to talk to + multiBuffer( 0, string.byte('C') ) + --test if value has been written + if multiBuffer( 0 ) ~= string.byte('C') then + error("Not enough memory!") + return 2 + end + + --Request init of the buffer + multiBuffer( 4, 0xFF ) + multiBuffer(13, 0x00 ) + + --Continue buffer init + multiBuffer( 1, string.byte('o') ) + multiBuffer( 2, string.byte('n') ) + multiBuffer( 3, string.byte('f') ) + + -- Test set + -- multiBuffer( 12, 0 ) + -- multiBuffer( 13, 1 ) + -- multiBuffer( 14, 3 ) + -- multiBuffer( 15, 2 ) + -- multiBuffer( 16, 62 ) + -- multiBuffer( 17, 0 + 1*4 + 2*16 + 3*64) + + -- multiBuffer( 33, string.byte('G') ) + -- multiBuffer( 34, string.byte('l') ) + -- multiBuffer( 35, string.byte('o') ) + -- multiBuffer( 36, string.byte('b') ) + -- multiBuffer( 37, string.byte('a') ) + -- multiBuffer( 38, string.byte('l') ) + -- multiBuffer( 39, string.byte(' ') ) + -- multiBuffer( 40, string.byte('I') ) + -- multiBuffer( 41, string.byte('D') ) + -- multiBuffer( 42, 0xD0 + 4 ) + -- multiBuffer( 43, 0x12 ) + -- multiBuffer( 44, 0x34 ) + -- multiBuffer( 45, 0x56 ) + -- multiBuffer( 46, 0x78 ) + -- multiBuffer( 47, 0x9A ) + -- multiBuffer( 48, 0xBC ) + + -- multiBuffer( 53, 0x90 + 9 ) + -- multiBuffer( 54, string.byte('R') ) + -- multiBuffer( 55, string.byte('e') ) + -- multiBuffer( 56, string.byte('s') ) + -- multiBuffer( 57, string.byte('e') ) + -- multiBuffer( 58, string.byte('t') ) + -- multiBuffer( 59, string.byte(' ') ) + -- multiBuffer( 60, string.byte('G') ) + -- multiBuffer( 61, string.byte('I') ) + -- multiBuffer( 62, string.byte('D') ) + -- multiBuffer( 63, 0x00 ) +end + +-- Main +local function Config_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_EXIT then + Config_Release() + return 2 + else + Config_Draw_Menu() + if ( event == EVT_VIRTUAL_PREV_PAGE or event == EVT_VIRTUAL_NEXT_PAGE ) and Edit < 1 then + -- Not editing, ok to change page + if event == EVT_VIRTUAL_PREV_PAGE then + killEvents(event) + if Page > 0 then + --Page = Page - 1 + --Config_Page() + end + else + --Page = Page + 1 + --Config_Page() + end + end + if Focus > 0 then + -- At least one line has an action + if Edit >= 0 then + -- Currently editing + Config_Draw_Edit( event ) + elseif event == EVT_VIRTUAL_ENTER then + -- Switch to edit + Config_Draw_Edit( 0 ) + elseif event == EVT_VIRTUAL_PREV or event == EVT_VIRTUAL_NEXT then + -- Main menu selection + Config_Next_Prev( event ) + end + end + return 0 + end +end + +return { init=Config_Init, run=Config_Run } diff --git a/SCRIPTS/TOOLS/MultiLOLI.lua b/SCRIPTS/TOOLS/MultiLOLI.lua new file mode 100644 index 0000000..4d309e3 --- /dev/null +++ b/SCRIPTS/TOOLS/MultiLOLI.lua @@ -0,0 +1,221 @@ + +local toolName = "TNS|Multi LOLI RX config|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. # +---- # # +---- ######################################################################### + +local loli_nok = false +local channels={ { 768, "PWM", 100, 102, "PPM", 50, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH1 + { 768, "PWM", 100, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH2 + { -768, "Servo", 0, -2048, "Switch", -100 }, -- CH3 + { -768, "Servo", 0, -2048, "Switch", -100 }, -- CH4 + { 102, "SBUS", 50, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH5 + { -768, "Servo", 0, -2048, "Switch", -100 }, -- CH6 + { 768, "PWM", 100, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH7 + { -768, "Servo", 0, -2048, "Switch", -100 } } -- CH8 + +local sel = 1 +local edit = false + +local blink = 0 +local BLINK_SPEED = 15 + +local function drawScreenTitle(title) + if LCD_W == 480 then + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, title, MENU_TITLE_COLOR) + else + lcd.drawScreenTitle(title, 0, 0) + end +end + +local function LOLI_Draw_LCD(event) + local line = 0 + + lcd.clear() + + --Display settings + local lcd_opt = 0 + if LCD_W == 480 then + drawScreenTitle("Multi - LOLI RX configuration tool") + x_pos = 152 + x_inc = 90 + y_pos = 40 + y_inc = 20 + else + x_pos = 5 + x_inc = 30 + y_pos = 1 + y_inc = 8 + lcd_opt = SMLSIZE + end + + --Multi Module detection + if loli_nok then + if LCD_W == 480 then + lcd.drawText(10,50,"The LOLI protocol is not selected...", lcd_opt) + else + --Draw on LCD_W=128 + lcd.drawText(2,17,"LOLI protocol not selected...",SMLSIZE) + end + return + end + + --Display current config + if LCD_W == 480 then + line = line + 1 + lcd.drawText(x_pos, y_pos+y_inc*line -2, "Channel", lcd_opt) + lcd.drawText(x_pos+x_inc, y_pos+y_inc*line -2, "Function", lcd_opt) + lcd.drawRectangle(x_pos-4, y_pos+y_inc*line -4 , 2*x_inc +2, 188) + lcd.drawLine(x_pos-4, y_pos+y_inc*line +18, x_pos-4 +2*x_inc +1, y_pos+y_inc*line +18, SOLID, 0) + lcd.drawLine(x_pos+x_inc -5, y_pos+y_inc*line -4, x_pos+x_inc -5, y_pos+y_inc*line -5 +188, SOLID, 0) + line = line + 1 + end + + local out + for i = 1, 8 do + out = getValue("ch"..(i+8)) + lcd.drawText(x_pos, y_pos+y_inc*line, "CH"..i, lcd_opt) + for j = 1, #channels[i], 3 do + if out > channels[i][j] then + if sel == i then + invert = INVERS + if edit == true then + blink = blink + 1 + if blink > BLINK_SPEED then + invert = 0 + if blink > BLINK_SPEED * 2 then + blink = 0 + end + end + end + else + invert = 0 + end + lcd.drawText(x_pos+x_inc, y_pos+y_inc*line, channels[i][j+1], lcd_opt + invert) + break + end + end + line = line + 1 + end +end + +local function LOLI_Change_Value(dir) + local pos = 0 + local out + --look for the current position + out = getValue("ch"..(sel+8)) + for j = 1, #channels[sel], 3 do + if out > channels[sel][j] then + pos = j + break + end + end + + --decrement or increment + if dir < 0 and pos > 1 then + pos = pos - 3 + elseif dir > 0 and pos + 3 < #channels[sel] then + pos = pos + 3 + else + return + end + + --delete all mixers for the selected channel + local num_mix = model.getMixesCount(sel-1 +8) + for i = 1, num_mix do + model.deleteMix(sel-1 +8, 0); + end + + --create new mixer + local source_max = getFieldInfo("cyc1") + + local val = { name = channels[sel][pos+1], + source = source_max.id - 1, -- MAX=100 on TX16S + weight = channels[sel][pos+2], + offset = 0, + switch = 0, + multiplex = 0, + curveType = 0, + curveValue = 0, + flightModes = 0, + carryTrim = false, + mixWarn = 0, + delayUp = 0, + delayDown = 0, + speedUp = 0, + speedDown = 0 } + model.insertMix(sel-1 +8, 0, val) +end + +local function LOLI_Menu(event) + if event == EVT_VIRTUAL_NEXT then + if edit == false then + -- not changing a value + if sel < 8 then + sel = sel + 1 + end + else + -- need to inc the value + LOLI_Change_Value(1) + end + elseif event == EVT_VIRTUAL_PREV then + if edit == false then + -- not changing a value + if sel > 1 then + sel = sel - 1 + end + else + -- need to dec the value + LOLI_Change_Value(-1) + end + elseif event == EVT_VIRTUAL_ENTER then + if edit == false then + edit = true + blink = BLINK_SPEED + else + edit = false + end + end +end + +-- Init +local function LOLI_Init() + local module_conf = model.getModule(0) + if module_conf["Type"] ~= 6 or module_conf["protocol"] ~= 82 then + module_conf = model.getModule(1) + if module_conf["Type"] ~= 6 or module_conf["protocol"] ~= 82 then + loli_nok = true + end + end +end + +-- Main +local function LOLI_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_EXIT then + return 2 + else + LOLI_Menu(event) + LOLI_Draw_LCD(event) + return 0 + end +end + +return { init=LOLI_Init, run=LOLI_Run } diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/LIB/error.luac b/SCRIPTS/TOOLS/TBSAGENTLITE/LIB/error.luac new file mode 100755 index 0000000000000000000000000000000000000000..0ab504e6900d2ae5b0bb9a1d0f9fdd1ee86caa61 GIT binary patch literal 1023 zcmZuw&ubGw6#izi-Gpclf{@k}L=cbSRXjMGv`u4wAlYigf+3Pc12KVQ3q5$4Z6TpW zPvS}ZUv`Vl&7-IO6aEQ)v$LBknCckQSczBgM1}cN1)MVfjr@v+PL#kEF_TNQnByW5sCvX> zp^H(BVxr^R2Mi?~%%awLEAlx4=Zh&nfG9=5Zd}&jaKf zKSSb=%5_=2Q(tc%k!8yxyD%@?vc0%r(fk^57};+!>; zSYgVU`a!<%1UN>9E6rV-&KC3=HP1Kq@-}}zO)q-W%Ipu{!M#aFI~a|Iqw&M=IOL9p z8Z*mZ-xKa?G>OtJI-~3>d8=CMN-Xt6Qx&J(D4VGSA`>-q0U z)!yJa*J`t8CM1dTevSIZi>AHYF6ZK(cgq^OXWyc^_j04<+gI~?n$~w?EC%- z-F&sSdTW&~FAmm_eon;SB7Q@pCrVL48i=Nu()`-tn#^nEqBc#eY+>C8E?01CHru^c z_JXAAG|cjDmZy38xRa4|`?QA-2iY*|cGtnecG&M>rY~lEP!6Pv<#F&Y<;I1puF`jl^3w?tudI+siXCp=*7XMj~Jzf1F zYS#6N{;BE_>bfox1#9M#X%=a&FmBXX!=steYz-dDVt9gn@JJZMPfJhBIHqtMOC|6? zLI;~;&1)K`Rl|5*eLSbGJN_d7Co>eukTGQjQ2 z&PhsAAZzz)r2jnY)yV!skmQo|q+T6n+0Wz(iPTF~wHRb+>k@jTB;KBu)4!5GAIK21 zVLoX0j_Xd3Pjg6n1!&&C#GRtI>ph=z`oj!UCzZRn>RYAD+g-%_7=k|e4q-##*dc-A z9WHYyPK!d(Ia!vGC51&}m=@OJm_6EVl(aJ{{hhM(4dx5X&qrl&?#%<7Fm1H792NX8ma3w2nIZ7xj6F>3-nvUM^ z=16l?1~dUaEt9|k55d49D_ekWF){JFW5;Q#gZn#r}HGVz{4}}fxBngwu*N6 z+?yDDbdZ_RTfs@D5z>VjiK9q!#W6MK2$pa**uq)Wf>k;uSUP$ucaKMtxI$D(k?`IpZhP_VA$@%X|4bk{0QMV%ab(E4%&|7 zOm>>3gPQRL{mnqmOi%lRZi?T5BYt-0t5*GE=(?UhpjLWXm%>nv@~nTd>~(L+Y%iI^ b4CZz||8>xIo^)Ts+Uozcq4V26yh$GbcYgTg literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/LIB/gui.luac b/SCRIPTS/TOOLS/TBSAGENTLITE/LIB/gui.luac new file mode 100755 index 0000000000000000000000000000000000000000..67d1752174b1e1f12914582d351aba8f2d4fc857 GIT binary patch literal 1198 zcmZ`&O-~b16g_VkM!ej* z-skl(pcZ(}1E#GtEDRBY18r1SaD&#@!6P#&DZqRw6Q8@ zV0zZUDeZHN+rvm2(-=j+y|(rxJ}N}-GT#&cZ;YQxrM6iOKf#-%&F1j!Z<1m#+DVJ)^7@jM);;S^^VtOpY8y9%4 z@NB!b$t)5&lL~L)-JrNukD_obUJn|p!FmV@BT1xm1y>x3(3sU0ZKLs1r_2Xd<2^fB zBsOF#u1OiuS~X2frhYxDH&WjI%F=TAWyLR-%B)Wa7fN|wW+bWp_1q+QkFSIAr##v$ zWWYiWpj1x7oV4`FSsv=p01tbAcG}irpLRLRk@Ma`UhQisSaAhtg@)^Mrr`{-l{mM_WMQEb8Y=jnp`l{2}rD9Ev|jc1}|H&9>xpdXT}wyl{t7aG=j5H zeKm*@6#;fn9(AR~4~RbE>%f(@OuEYs{~*ur+Nv)+-w{u`C!No99Sb$apGOv45?^2? zr1<=Lak*SUCI8hTo)CSWXeaLQn9-6D#$z&{a$8#rj7~>x9WyLw(9)RdTc^0o-7K`+ zitbqT9G?Xq8m>nE_m1qY#1V2C0V4#Y;>*SUXJqH(=#n<|Dw~XDVsY{F E7hde>w*UYD literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/LIB/radio.luac b/SCRIPTS/TOOLS/TBSAGENTLITE/LIB/radio.luac new file mode 100755 index 0000000000000000000000000000000000000000..51c9bf9e6bd804da549a7c3a1dba0ddc49798cab GIT binary patch literal 741 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 literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/Fusion.png b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/Fusion.png new file mode 100755 index 0000000000000000000000000000000000000000..79ac095c5deb8410e20fb785716af77f79dabeec GIT binary patch literal 8032 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 Y0{Z$Miri-DnE(I)07*qoM6N<$g4;Z|y8r+H literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/TRrxm.png b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/TRrxm.png new file mode 100755 index 0000000000000000000000000000000000000000..c99bf509cadac7d5d0b7847c88843d09494c043b GIT binary patch literal 9162 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 UBz^Pa5&!@I07*qoM6N<$f^tDJ?*IS* literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/TRtxm.png b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/TRtxm.png new file mode 100755 index 0000000000000000000000000000000000000000..0f4ed33aea47edc6d54855faf6b8aa13ae777c70 GIT binary patch literal 6739 zcmV-Z8m#4sP)BEw*ETl?0X;^`@YE@zyddL9Tf%v$EQyn^^MB-9A{))p5+sD)Mq)v=vX|f5QjlU z1Y|^IkwsxpQ66hp1X&Z7NEVWikgYpOCwcXqM4%z5ajyeZPD5bI-K_ zsKn3YHRSU%6y8O4^KNF(o-L2QzP|AH_lKXKpFH>RsT;S-%1V#VJ(103bH8go??_Zp zQGxREa+H;op|rFVFTVKVPh+AYi(t51E;ks#!NCX!2ylZ^7bytlqaHlpB;Je(Mn?oi z!6+#yL2+@hfbz;KugGT?a0mxJFfb4yAt4A03v+{Fx7%wSI8PvO(0TA&K7RAfH*h*j zaP76%A|SwDZiv_Gh@fzMIPnz>j(Jg0k^6HuAS#*&MtFEQqN1V@8XAhApr9JF-1-An zAb1c&(%iXov1!vLBqVf2NJuDdzkRxV&hINI9gR!K9S(;Bg@uJR{<|9xiYXrC41&?A zQzrq2T%v>PiA*p!^YtK@$;rw1;fEj4y?b{I88Q?nPMpAxKmLeOqecmvauRezP{<+V z4oVPMbg+A*$p!oZ0pMT~jEIN`;TV2a+kly1@R8{A2T7M*b{Qs3nuKq^{TAuz>FCv~ zC%*gcJ6w6?l>)lzEYigchxS4oFOHE)9f~fe(^(S`4vd0AF-2eq1`l#hT|^)_xcnhr zhqIqYWMm|!PoIH9hYrb)a#qle*u42`%$PCF%_Ry(ZJ@U|;{Qgq=;Bx_r(6IC2Ze(} z@kC(sQO)1lNI66Y)6^vy85x)`VLYz7>ME>Rvl?g5W@6BwA=O+m0o}TFlbbV*hK>Y^ zIo3)wxlZajpT#rIK1p zR#5cy*|TRcV88%Onlw=WId|?He0*%!zkfe=@7^mjMD>q?*^$W&h-toF%3%Vb&rR!z zqA)Wv6F1*{Gy3%D1DC55r%#_0HeJ1Xm4LzLD&`t!0}0BH<|sMs=r;H=k29#rrIgr;yw zNhwlOPm6`|<(Hd~lanjP1G&YM=l?T@G>}tF?oc4~IrRqzouVr+FhE{sWn~HXeER99 z?i*4szN2wT4L}-0rv5OwgbG`OlgcGmTyX`)j2Q!`(}9ePbg{v=Z{LB#hYw%CCDxhK zQK-q>V=I>^hp4Tsz6|Yen!$_$R903XH8oYNF69z}*AdNNr6vVL4^xj=MOSuqHb#sX zA zI7BQiT3=P7%hhD})ghI-^{^^<6PWz`eDv(u6W3pVJ<7_easveg`N+!3#)kD9<$N{P!$qd#*2m97rZ$&^rkN|k<)M??I6)QeNSy}l7-|GmV zG#emRF0oo&`keY=?AWoAH(`v6;d%m)n))Nwu3gvR{E}wpklK6`1%;on>Vjr)=gyt0 zeUd|nj*h~H4eP~kp*OE-;r33=3W$PXjQ}f`sO3e!q$+1J31wv^66YePkYhgn_+$6A zDlPS2?U9<&3Jdd5TIxhb##yXdwYs{7tNzvYZH-#IT+j-LHP*!e zX3k{Flqm=bsw(zQNl8KH&M{cC=5w4nbxOoodsXl)&LQxi;H|nOCnra8Cd@7t6cmWw zpg+RzfArCE_chD{z4k(_!XYrsU{h=w!$E04E&LW*hyWOwdnZd0F2xqifTl#3_6vnye zEcWf&m-Q$}PfwHYa!JFgRjUL@t$J)Px}??C0C=*vtj;3MV2UZOyi$|+(4oVKkB`UJ zt=mvkRJF#!tmkU$)T)5!qt)WFatXcn{{073FOp=sw-8@_^%Y)z`4#sy+KVn})m1W9 z1@Fn?A_!co!kh`8$Ha8P?%g|NW!Bqozbz)QCbHUt8Qkj2mQ2xQ_1;ZqktQ%Ri`m&( zBD(Ip^G@NMmtTHa0BA39F0WWeRvV}2q0ZtRcie&U@(TGL-y1u29QN(|9$$aGMb1%Y z5p7s>c?E}{jw%%%G>I9WA2MW!D0n_kPtTAQS>%$>KmS}DNNd4%TO_YpX=!E_J%#5r z8a!jh3=AJW9J#sKNJ`oz=Nvh51k0B%cdu1!%Ut3$BFj9ieu`WYqemwqE$xgbdz!ux5#iXqdp8y@UV@b?SGm)} zKXXVU$ZBy}*DNYXbQUkae4JQSvi{xXgW%vGRKkWo|9K9kO`9%03cqV|iK+0tjl89b@Zlpcb?Q{9oG&eNAS)*iuJUq3ga;!mI0Wq92MQ8jd9F2Qr;%Y zycQ5o7ME#tDVH#L#a2?Wu`%e@y$4E)oCxtt#(;jo7(Mn1*!}$Q*4+P<3?!>p6cipB zg7Kz&ir4o<@&t+k!cIkLX=zxyb}a$}gHY}&$FSjp;4F(oRPEPy~z^udXa@%K+!f(OjglJWA~q&kLig-C zl$5jwPd@pyR6}yinFfCL*=KR^-~q2%Xj*%#I4>cqf;SHi9Gk7NLgnDWgRuGdNQ}#F zXRE?!=@>x~M^HH6>~J@C?pyN5=tMHv<#3eXuYaB6ju!K}ty#&^Cfc}p0!6um{>R|K zgVCc$4>5b`oimie4iQ{X%I^{sHuvn>wX4*19XWDT+74!C=U~BtzhmXf)fZ$P(TWyW zo9U}(1w|jJq**FNpM-!grbP#lRV(BG?T%VfT8iY96geju+?>TVCu&)Z88a4Br`{$-cprZFq5F4Q zGxOMHJ5MSw6i3tzT$|XrbF6TNzdwVEWk^juh4cCOa4`rO7A9SzGc(hXo_ zN^;ox^&4>b@R1t%`Ie?O2Z-rybH;N}*R5NJci(+aUQ<`Z#Ka;lE>=SGtl^@v=8E3~ z2M!2_oI95zmGrdIv|TF$ksp4zM1I4}&$l!vUJXd4r8%>Ca29hwIlKGy>xZ12Y|#NR zF>xp?EJjF37$PG($$%4-W5oypMz00L z90&zKJ#5Zy4nD1>J$sU*4K0_#aqwvdv(aWoMkd0-!o?9hdi03IxY#$Cp?Uf&g@r}J z6?$C=XV~$<2Or3FOwnb%pXMa51jPLF+9{iZ#zzhon=n&6EnN5y%%1%MQc_MLE-qeD zywofFob3ZkN}TZXvkOPW$9F+&Y&=3jL&QI+s3@1umFu)Ja_!pnc;k(~iP+NLG@Gh5 zH+dZ(wSl2qJCFmI6`VP9Cg#nXFa6{hWZbc1yPR|W{CUL1#fdICdGaKDe0)XEFdEEO z9YcqVK#%TyV6#=V_fXNKEo?8p{Bj9c@_jXft(?-FTm==Z;W48Q#4 zWOtOA9LI@ZjSe>#c@-e$EHt@;1Hyshb9&bku9$#f!-vUxX=l!$Q>V`8*|Ue3x&)4q zU7EA>#?PENBiA7)yiQ4}Q?5_lVk&mJGz5F;(vKzXr6on=lFI6S?JeP-cwLc$X<3=O z(C92qNlC_KLxy6)_$#GQE;cS6UAtE8z7P=+T^%y1YCO=pcW)6_4424;0_D-z2!;8Dh>DDob3(%+#7y3@WeX-woFxCkOm1mVv$+D0mkh|Gk3NbuYu3ok zG4!npl!NriC!dI7&fkhzd5&%NRT z-9s*+zMwuik&=R5y?Y}tAh-%JhZETu8RFSj_*NBbS5{OYJ1Y}iyLQ93TmBDMUU{{M zH4?!k_o+kLTvTsB2*i8uy(b*-#v5=W@XN_wO&gxG0*!!XlCOV>${83Q=S)gkOjq z3HAhp`-O{`Dk*oMAin?^zL{9Rew{SMF(-$4Z_U9a^#X+J#;HI#8!0O4B7(&FTOQoV z<$(tt5H4aZ)y2ruA2j7QXSNOm*RC^aJ9<b)AQ9e?E4;nNW zNxS!;Aiod=jy#lyRiJB3H}oj$hH`%!ic5vk@8^f**c3Bn@$N<1SXV zG`CN3Q9#J@Wc`NR1b5OocI+7Ly6Y}?8z!=TeaV^wsjS{mOLygAk=%$8BW0OedU~c< zNyWtulrZsB?2zvb95_&P0RKmyKE1K$KoUCjjz-_8euxcW84KjSr&o-4(BGvCL?!JBaNf8K(i;y`@$^=A0n{n3BGKiwF2P6U;dL^YO>kNSkoVtz-~D{&&YgJct+zyf zP)E@GYzTe-Jy$z@jqTgFdpx_+vX!1$+pD|C(I-%)t+l>YAo+@TyyfOwaPPhMiE+W6 zDEvPC3wrE4r?8+1E=L)*j^BpqxD8qVlYuvq{)Uz3S0S>07~af!9nPsGIGvD+z|uhc zc;>XU8YI{>^SSIn0cj3>;t`N%o_Xei*OdJ)yzqi-5Xo82;9WyN;Xpq2*khtoJdt&2 zn^{82fi(}^1e1fp2FwgDzWL^xc<`YI@ZZlpBTnJjvsv!W5cHcU|4{*y*-B9wRfYug z#D;YnVDq)%hHI`zxvK&<+HOKmTQ3|>Oa}4*Umv@qf+@QBn~N7O7875WW21?=#3LYF za%?45Y0Q~3N2)s#5)wpS^CmBqUVr^{vA8&EJ&~spQsod10zsfS;%n6_45KstGFzzmg1r6v4`9WLPcBgA%?b!3skPD5Pd_a>go=`;EN@T;mf?x2ZSm?R4j{c} z&Sp>Kso1(UF5&wWp)oO?v3Ktt3ArmxnR1J)MV4e6va3(V8=l8+c*G!>i;4__l6=VY9Hnl{{_Dr{TQ&^hJlCsB7AU^SZ&S{r`tK@ z_4wZxEvnM7)^&}Ir5b>+b3$E2XOD`FgT)!ih$^{-6@iNuExIV6Bo?;OgRYHgTW@G` zi7C4HntleYDVpX=TpW7WU3ZH9aEDXQpO;x*7?Fp`bEja)ijjCd{B``twfE!cnEyiQ zKOA`E)5j1wAPS)o!RX$tyL(x-=1pjEv6&jBiM@_(X&XW{0I84Ix&QwA-OmXM?R@Rw zULP(YSJkIYpH*}zC?>ZkmvH7%gmRF~txO+%^mpKR!WgeG6t}eM*gC2z-qv(wLp4U9&QyJfrZLSwc30r$?6lGOr%4Yz^iZ8wAXl0D zzt%>cItEz*;i0ejIW0i0Tc0=YZM^Wp^H{!o38vmQRUA@|mzcq_>q0py%BupGKCwQi z?B&ARookSmo`#rCF*ttgDE1`nLS=;u{(b?HDpn_x(qPe|cQJYLt-_6pLsM`{4L~R# z>q7+Nx4-?Z%%0h^XJg^Qg%W(E(x>Ic_i7_@5yR*8Ax~X{JaLKcyg;$V=x%XwkyJA> z*!a%;caV~DLJH{AGJ@fVN^juRtEG=h-#&e0?K}4~88>bWA|oS^d^}n7CLH({NoCYwOU0Zu{<<%LcPF9HG_k!<6{22YT1GdDak2Fi0_Kqe>Dx)Uv~rI z;}ayw#lh$K%7JDV)uz@RN;O25DPNy|{&@kVF?8R3_sO5Q?Y7&ndiClixoJl=K5%D@5KMXL1h4voHBa!XxV8YH}|~g zmg&={Ng)FP$US#n^oas#Ds63Ru2F`@aEDe)nqyr95Ni#smgJMBPoFL`{Hdp&65Uc? zqR3(nMVpT`2bqBAAJjaEiZQ;=*-qWTtuVCCm)y%G)w(!s&O$W+_`kHmk%x*pQzV(i6|k7yjU}zbq|rd}$ilw|AAIma4$&e2oed=Z z8M3l+iV{6>9(55do0cLI5akFfw`dNWM2k-cTq{}l|IO|FOeeIlTwx_Ghvj`R%v6>C zw{QsQx#yk}wxz;jCXU>p^0YM<(_f6NtDJQ36$noS31;42Il}6BH#HD?k7|;WO9%$* py_7Bh*P)1Fy%CY>aZLXoPp1QC@;2c@Xs|1a-Vt``9X^e(t6_N82eU(o-eOTE~* zR8bcqD98m8goHpKfdJ`cN~X7S=9FFUdfvUilbty;lL?ub1j)PBS!d>yUB35w+w+#A z0dR4U4j9r8vf{v^aPVqwzWHW(4uwL9Mx%&CBJ#JcALQG3lH)kAZ5yU(!Y~Yc^PAr+ z9lfNo$PCM}O3X+mlZeG)C8ivt@gv2IVzG!^E+&nJg%;1_24i1(MTw;h)O=L!8WhH8AYLHH+kw_#&N)FQa5v3!a z&m)`7mTmMBL#U?spk$C4b#-;Z7)l8xWM7FpBS`ONiKB#2I;edxWNk_-O)209$N)+P znNd|$B@)B;4yvs2Auc{8L}?wWu0o+uI`(cVOEHCNii{yM_@Lw*r12vfarml>Ypo<@ zH-^xDkSSD8WQ=-N?(bNMJ3xsbkJ-}Fg4bSqtt4gPa0KB{6wz1&Aw2{=6!N_}o?Aya z6hR~sLnus}2DuJ(4HM~pPqs=WQ-W))sLVJ7FXZiBl=|K;fB8$?efQls_Sj=Z1*TF- zxUL4c;gaX(ErbR=WJA+j=z0j@P{MaIT(s$ zEYpGu6AoJ0$e&c`Pn8A3+91B4J@VHvt*N z*P_uFbX}9r)YsQz&YU?o^w2}g?q#xR+!+$&_O;huD-2n+Y85{8p$~~?L4oM*YR1;B zTSb4WT^($^pHuN1jlyZ!iDV^B^AX@!ef$2pZ@fxanw;qmEF^1(u7EeqRTJ89CzGt2VQvL z1-$1y@4=KQ4e09XLDP;Vw0Cp}6R1+uZjMBvvasYScW^u&lV@=@qMozi?RsywCsRoj ziw2sSnh=kNQP)s|NGJ+jj|%g+=8o;caXjg9$t4%CT}uE8GM>ky5^#*`2(9%QjFn^0ITelWoVA z_i5ZC;RqbpLAYc9J$+x1uE24}9E;l88f@RO713B2sZ<48Tbhs=$jEo0d6HmTjxdGi za$U5b{)w3sEaj&%8)I1hF!X1;e4r(qNbdw(Zz~mZoN$x%5m-pFSO~Yl~g!@9#%j zTRS2V9igxnW5MOZYr+scetI*dwAWYXZ+jjxwI`C^-p} z$&?9Y2yccrb@b6kW8=n+qU))vdwY9j7k77e%impHU8UFg8sDQ=MTsip^BBzKP&5o! zj^(R4pr)o8jg3=L*Vu?vt6#>#MGG)<#&l!{bEvJWLUYS@w6^X+i=K>(=3(|Nd|EbakS7$}|{-ELN;sg^yoy8Eh*nUexKQFGfQ{BdV*b1q$+zhh!;^ ziN`%Of)5qD3{5la592=JHe@tXuIJDjCx*ty)TvWP{5Dhbj7mZ51!i@dMw#Ben9_5H~V94I;Uu`IxFpGhVXVgu621gh)m@#gyV z*tBIcX3m@~h`?iyKQ5me%@z!OeWcG#7QJJW<#bmWH*iPO9z^NdIu=%~T#3HE9#mCV z;Gu^e#EUPkEc@<(O#9V_>^OK3~AXg$q$zTPtVT z->PV=^p2DePb%Vg-}~Mtu@1AGj5WHtyX8G$#-asSv2q0tJ8Zsu=be&ZJ76RvxPui8 zt{`&8Dau7fU~zx?)1RWJrw30z{j?Zvy14wzj2W|V&e`X}GHv-TV;T**fS0jo1e%ZX zs^bl3@RhUi4;6>JT~gi`k=QSO@r(G?uYQG_Zn_EI_{KNnh?ELqV=un=BIeGW>*rch z@_8n_60r*GZ0*3c*ItKMO!q6Dh#1EtN8*BTejcZudMZBq+0Tx+=p(&8k#xW?Wa!N^ zDE!TDeuGCIc?2h)d@_1^d+^)e{#KCQ_Vx~J*suYeot>C7=McflC<&W4Z-Qp(DCR5y zAsGnf^BLH-1>%hPToI;OgyVX7l?NYu5Ic745HNAtv}pqC(B?3Z++Qkg(it*bFi2c$ z)22-Vt6rrLt4 zbRuZKNQhzvlS|8&FBkQ*W$RXfNJ6@fii%3q*Vkd{)J9P;R7Hh+QPALGF^@u_h+?6D zTs|+m-yuxTnt*WFE7R10!F*nj%|r% zlL@3#EYeHK&TnmPMP?um)3i$2LJyr~y4EYekcDE71CsEN1@GUH9!rTTV5-D4T$oW;3^QX5+5=__!(kJa6&4p)L^*}=?{7YhBpx(UHFOjx!h>q_;-hsu-C z&hYPGnUQTP_2E^G^AGNRN(0py)!t1v-6*QYatvs?E5}O3)A)b4{}5}|yn-{&IZLj^ z%R)NPwXlC|+bdfNdH>Vk8)hCq|M|}a*qlFq{;=-yzS5*i2y^Vs&CNLWxMNVrn_g_H zd4`=vJP0<*uJv&!N(qfUKPQIWPxG=Wg^Up?0VGHa>k(MRJaT3Z7hQA_rcRw!b{&zZ zj>|6l6texjh{mEw#FDUzCfeKEr5J;LIkWeTJ$I(k!M3vN1CHwrFi^KP}ZQJGc@y8v5*Ir$X#fwjcWfo!C zZt1+DKAGQl94W+L5npF#25-FarZ9~0&u2dKnGz$$OIjukI$`?ufd?KGAD^k&Ty7AB zVxhzcsd3HcMM`+Xe1^aKXxNo>7>3v6Vq3PCcX#~~e=+zr+S)p>Z259zvRO=~>SB!2+_FQ;gH+`s^}i}umS&?^4B1Q{9)0YOxaR7s@%~d!MrT_) z<{x&bS7pMTPZeht@rg?>#m|5C3vAxJ4Ra2eC&wi-np?K99M6}&-KnyI@Jvcv@<$(i z6wf{P96tQv597V>eebw*2}~$Mlo;Q8@4a~V;fE#Smfi@7m`&shISgjA$mR0L6b4~B zHWINoQqj2JQoKPjgb!vAxkEFVe)yYmSmI}>f@1NQWDzeq?|d{hH=(by1L?}Ns7f6g zGJU<6d+0(edH-TO`pA=5F#m92@XVPrB_FSAAyKEwi(*bcq-!V?OhEu>KYsVS-{Hg) zPsHJeA1=0wHC_8k6Uq?oSnl5Y@4sIzm{lfT^Yoi*%@fjS)WMm1HW7zdrEq4%QgMcG1#i`R9N7XFUASBWP)9k{mo4^5OJZQtQQYE0tMUS%J>ZZmfK91=>5>amO8Z zmTU~AgEeR4K@)D&Uw-*z!JX(f7V-rYO%vIIA!;Zq(vjO`NNpVs;|tS9EE_|*B3)WI z7CZ15=bwL(fQj_1G>!FLo&&%*h3bRnBIrosF23)<2hiTtg$0Wa!>2xVIb1T`^6Izp zJ?%LDxZ`lvSsz44M-Lhsr-@sfw{4JfU2wsL`1gPRH{5*l*YV7=PnKFU zXV0FEuwzG)#0ZM`Jr=x?sn0HZ2Aei- zMptK_#4Owd!jODkq;TQFqtG~|AA{MvI0uwo(=fz6hZIzN5vyNWg=e4pANilx8;wNe zn&_nPx_BSKK92*IA#_zm61c$J?V1k5^3;n03K4+ZxnPbYnLKZcwnW7tTmZfvj_8u6 zC9PV$8vpf+U!b?AXV*IKuM@K8Z^{sDV$&%x6{THsny4N@12e;iR6_-<{Oc4)k<;s=f$}e5I zbe|!W33q!tJ33_14ckJ&VzHZDcG29eD)yl3DlH2B4h|Ce+1%WObUH3A1c_tfxiU80a6w_H8X8F>i!QRk>GlYhiH_zCDH|^TH`}RZAw*1ee zp>~13>;UTXb*QeXDS1)!lDM-gDl0|BFhK0=>B2M1Uc^&RufUES&0=R}%$y+=?=8)3 zvKjg_8HA%@L4P$l77&f-$P8xDp2?uLsu~*mNm$J7HwKZ>vu8{bdttjadi(lu*t|KC z2JY(W6zQm{sKwSTO_)AyCSF>#3a6cR+CBk}6V8x%^XADj8*wb72!qL@a7?@;8eRqB zDWd&gJQC6c%o6WQbxg4*kpU&6WipylGkex7Spdc|1SpR><_P&5RSn%#ibH>2KYIFl zFl+7{n05xPW6ANd*+I-cWF}5uFb7XPu?)893Pj>^SVAlg&9SA=krHMU4a6f6gzZ+jb-qX(`NZ zX>P&v88bv}QB`TKi(Jt}ZT%EfAdkkXBs$PfxdifAF0HE(#y~9zRQO?7|B#L~Cn1y4yNYS62(eF(g_~R#af+ znl&&EsmFyEU4Xmqc^JCpBCbb0`x1_#wss1pPn{`wl60y<;u7ihVh3$3irMq#3gn}Q zBhcIkURw1EHg4F6`3vUD_3Z5wj0tB5iw78Czw*i};u(2xw%5tzh)SS(amqSix%d$+ zgkqs6-B9%R$wb2L+~tWx5~*YgFRXmE?3{qtbEry(jz8g8M8a>PyQ^0gh%p9%#wekX zya6{_gO^@i2g@pmF=v-BU1Qo3rjwT}dA}sKJmgq;aUA-`xhJm(-$-$ab zCDyE6FKuoW>6GNz+0o@)kJp#T+<(sT@-J`$z4(k};ELL0vngHsL^_3K%a-B%^UuZX z*@u*^#~#1mUPO=*w9%Fp-`}-VaB$LQVD`UU>MZV;*W)ZOnwk~%gY$Dm91!d zx%F&kr&Nb5oH`XzEdCU>5w_;S)-~y%axDi_>T7Y> z{P~hUZ)s^pIHdW#iGHyhG*M5HkY}UFR7r}_A+bFskvW#;A`}gyy`>d@e)P|{>hqsJ zFd4#R4tyg=15eM1H>ui@`5NPkV3J1|#An?bUqld+HB(d-nwmV}x@c`_ktjW#NP0PO z&4)H(v67mZ(oipY-!e?G9sJw%AOSg-Y1vr6euJnesyLAj$1UaQy+%}~hC_bN-aiko zedsJP7sCAI%a`HeOD>UUes5?(BXzaFf+?lb66oo&4yNp0UEk$_sL>w5t}8E9>j956 z)T$;*jQ~-jDL80=`4r2w;f8c*kr3!NGthJ888VhYMiXdkA_XF>LeX7>B3y72h2o%8 zG={>QD!~hK;K1^()iq(61(^9PjKUy{d>&T8gzmVdDwSQ{lq+Sc>t0`vKmOrQrF9r< zMsd<2B5J zSDj)zyW$uheo6b2A=E|)#tlIg3O~l7M1@1Tl&rFPQkYyQqO-LP$%?c$+A5h8w_4f6 zr=EIB7&6uXancyV)@b-QMq_J9v770qX-$8kf)_=57|#@+1Y`HhOrs8UQGnrRWa0~%V+MxbSFSa*rkz%`8EDXyoqPnUAg=`LHE|0-{4qek4<$ppDrtjr=4f~${_n4y7stQ!^xJu=O1u5?8?mbXGh2Bk}Xuv2IO6OB4RZ0b=fDQozN%%&djNtj1H{ovQZuSyc6{TtyB{`Ht z!tewA{ZjKKXn=2HsPZZ+YtY-1k$y)+BdBJFa54c-X<;2#CNm%~%${gcdkj_bM!YD^ zOG3Gpg&2)IY=p#~`twD6DPiscw&QtAnkK3upU+BcVA_V4J0YB6TcsJ14Gj%q+@)v4 z4?el`x#&!{GOf&=Peu{uV8I*TBLk>b$pmFr6mt#BL}za|y8HV?qB)j>62@b2a}d3* zsjHNUMrI+8+&~5u@f517>Le$#cI~>d^%#yOwUqP&ANYV&Y7mU8Z>X23*)Eufr=u{v zxqHo^3Sqi}U_hXMeCKW4;nW$QMol?BOlA~A*{n?DTv~`tL$mMeq)vG)| z1k*|)k%*$VyBn!k3N|ZgLSYOJ7V+9^uVe9IZ|u`(G0CL%hY)vwCT z$B`&l-1hO0e;l9uz% zixXmS9ITH#bNEhhEk>eAQ&}q5Q{D!vJ#?a~Aq%FJ_h4-(t6d2ZjhySm4e zOs)uvZ3CL8+m)X0@9je-lNB34my~&s(U=h=o|oPjD=fKj-dew2TH(U+m|O#)nPR>m zpJ9TmtFu#Vk9dkcQ^+98uMtxqsi?PPQmRv!dxsz6j6@^9GtQ(g;@or3m8hL+g2fKv zYBJE%y~x^I+=x5~EXwOX$VVTa#y-O!kzSS_D&073LQ_k#s1!;Svy3cV9V;n>fKtJZ zU--^IzK|~gj64VJQraKKPPN8*V^xvQ&l9g>%wv_%1ju+oGLTH!6HOXJRD8kR^0AM7 zOw@p`dq^qWUkUL1|EY#(+*wPj`$3#MsOBOO6bd;M@;PCsin6J);;}g5iG+AU3+B%s z^EfCBsb0VAvdhE<6{R}g5Y+?UGZQy>Fe@GutLrHpbhWo{-!AtQ6D-XU42*~a`&Xz5 zSR}~#T{uuwR`7;7{gb+uH>dIo6nQqzc%QQcf#n=u4^vVQh9+@Jv6w@#n3Y75S5m5p zK~JUAlJV6vGWJFV+l0CJIv2NX;bgT(^0P#DvvO_RWoYKp3o z>10=wtRd(z%UvT;O}sENZs=MJrTsEmgNyaikA778FJ!?PlQ7SrDS!`Bz@{JzrS8L>oqO2#0)Y< zMfPeVsmJbWcXZlohVX=H*f8}b3*m@UQDoHx{Qmd9FZ*7Q)IxpdJKqtb$N-E`ga>hZ zn3SeQii^1KVvM?3uQb*1^c!>DGH^MD&aSR~Zk$ocGEN)fd=*JLId+L5%6p>fEc$_A zNlJ#Kp@V9d>$S&om%TCq-FfGoew%OzZ@lpazVL-FlsufF4>Dx;wC3)lUMJXi_0?C4 z+o*e4Q-mBK#?l~YQYjeW^l1qUb@`i-Y?SUWCsx}g9L#aCX;7G$w|ymHE2s zu7kQgKL6-PKPvm2qI>SSM_>!$N5d^j@U?G$``c2ByR)@bm@RgJlEDk7N}{+^WpS5j zo*OLx`);uJiwfQ`Szt{jtMb*+ZhS1HzRw2Fv(7rp>!bH=1%Xc)(LsA=8pzW{X6dVM z)NS9WE2*NpPvl_e{ST!*GK4DShU>4#&_ihcfq9|B-8gpxMVa+j{2V)}h7!NW9sPqJ z{GjwhSzhD6XGi$AYVOnm-bzq)Nm-~Lt5A&*SY%t;c#!M=8V-pAmN_zArH&mxh!v!O z4oQJ^%SGt`6EDm5>uw&W^QOrlb&Lp?1rNEyk}P54Hi9veYK1-p-SU1K5D z2BGMohaM7%hQFv@!2}%(8D5XV;2?4X1IP_#ORe2>TKHKGXdarkAC5+32upXKTfV&P z_2HPp6D&P*>4==CxUOBhwxruj-wn)O=gf63BzF@TqKd1@AWEsy>uQmLr9i3*g~w7w zdE-SxZ2^Dp@zqyfl~Lqs)Q4qTrH$0r*Dnz{QDo*p2*rH;>tB}*Ii^ie_KM=ZUEZTT z67iD%_O~Zxmj{UvuObd?Y1_GTx80#qr0|QfIU9?+iZ`YbSE`|3J(M&={VrU^SV|u~ zy@~o|10@lpV~#mS?Bx3O>v7jzcL_qw4NOR%n~wKNr-$9g4?OTdX>|EmXju4t=)#Vb zsJs;jP&2d{(D&CjVBC~t?#&%%oCA-aC7q$Gt&s1+}~FzmOuo9@5= zfwI@R*j(iAfB*aPzxvTd-XNt!8S`B~W3=lhjo2;AA`04~$tJZ)crA1&f&*zrQsJY~ zunZa53|X;qA^1dT=`ohR6| z!$qgZN87+s)Z1>mP3pQh(VFeY|Ac$j?p6YXLpk8}5RQ!h2V<0> zqzonV)-25-#Lv>4VAQNUui@O$cQTbR@a$MEJRyy`)#+qdsll5G%Q2l&w~njFs;qa zyFn^@%ar@>y;oG#4PW|F*?R|R7mdgeX810@;tIcrV0aoUQthe5JOAI*CS{`FGAo1Nss&Im9qVn#+S&b!z$ovsaD>XT)LIIWECK2po zu9!pU*#6NkMZ`8U+6d}*F{vS)M6T8B;xdM)QpjM*BRNNiV<-@K%PqI;aWz>; zeUOGH8MDsTylhr9PjKUE!N8nF2I}|4Sz1r1o06hxqYl!(lbRTzTCT|q{uarw3^_>q ipUT|cgR~zB!2bd{AEqiUT9~f@0000GS2ZC3p2x<-69XP$Ld`fN-E<3EJ|83R~L z_rhmVFJr>@mg$=Q&cwzxcbRpw&^N!Ruh1cNpyr@d-9TNep?|J4XVn+YQ@7Xahniz* zZ-@88csAd&J&qhXGMEF-JoBtH4P)ZJDlo-;wy^F_q=}6WoAjCg&VPcn;O+T6*`Cwh z#&1mFGw(~XK+m<*e`yVdjl<_WoReH1#8`=n;wioc#}sR=|JmEl-+u17z?7^Zk?U0z zm{C`CW=fi&5^KDOSihLqTPBY}gy!{aGTlpkzu1`kd)=1krq`$E$@}5+B}xzG06Axq z8PfM1&{wd1?l&>S#7-R(s;Wj^*THO629MiQQl+2C&)Gx^Ex}sj1YHM4NopH! z!?i27E2EjR<^m-xU~C&$13kW&sbfgrN1ht==!Oh&cPsZ_WiYC1RCR^AssQ7@t2{?b zm*LXIq=ecVWn3+pK+B1yr1%AIC&V&`Qs; zHG6aA1ig1Ip+k$Jh6D|(oK{4WRn>Ls)G=xv=Ot0;H&e9p^L<9Pvyw3tYr*wD-wSCNAl{7s(6(C_Jf^Y@%yxn1xdm1l+ND^K$QL&#huzflFo1O{cB$vK~< zY~z%L~mOSRMX^QL&}X(-FlHnPh-i!mucge!hrKT{S(1pL z70L|f&*<+tBl%}f3X{!>FlWR7)*lhbndLYkVOGVM0)OYB;k4&B7h zyxT!wXw+xR2H^lxNB{{=YHQOs+)gLd;U{Xvb9_v_aAF`Q0JN40=S?G9C<_>emnO-7 zCMe38!N&w6Fk=X5fEJe2ZyMMN`lM$S0R%&m*@zPvpaYJGm_*aRYEO5qe$6e@|AiNzOhMBi4JK*3BJ2R3UGq@(BNT5DkmG-wMzee-*QA#(Xvs$c;z zS*Ctzu>yIpc8L*MgK|ToCCV_ZVB@DH8xETD-0eW)B*WAACXUGFBLs+;C_pb8N@%?l zl*C9Spg>vExL_oP$b~RTViIA66H`zZ+1Xi}-87jyVG2vO*f8>{g+bWkOkn+)FEndh z!9+zOat+|mrJq(>h1|c*b4(7LF*0eZ9Z1MLt21E8&}t81W@ent8b#7#nX^<7Pe8~5 zb{E8E(ley$OQys~!f+<>{^kXRoUl5!;HDVd2Qaip{xJ)Tv<_lC(m?jTNG>L# zXr@@*M72p8l3^ZEz}U<{?sxdJoS?of!Lk%pFiEn7sGE&1f;|RBv=1oGKO+Lce96?v zCHzBTYg2OS@_8bTO#^ZBA`y#3C5fkb19Rh2wOr{ zbFL)y2~qOgQ6?l5 zXedjeQ3<7x&*9&tw#+v3K8k@;a?VnV49$^e28@K&TKZ+5S=(*ySdLZj|2jHP<8dHh z^E!plmIQ^xSjj%j3k*3T$teyi21-Z@(upR*8%bmJ&mTFfv)yv(QOGNE=0STKp0wi$3u*MIKD03b@oU%`tQQ1)nIFbbcQN2}@%oqOfG9l{qbud1YFz z9W6JZ*%AyRaxe|yag|1!I`A=S(Hdo-i#wQsgd=MR4;s^hNiC&0;SNhnzMdeM1Vd|w`so+%p6`3#w#%$W1-V6= zS8P#xdl70ptS4-Es#4~Yn?1ub3h|~-3%T)~0QlX;1WLua5)7tjAJVs$F5yFXii{LV z1YsT(c#!H>C9oJ9PmwgCv@?-<_jGs7CMWpUD4(zIr!`?a}l-+NOo= zJE^S$Fc=CSTp!hF6#B30)Hs-7RfM79Dav9Ow;kB^qUm=*aHa_ZbEAnY_Ba$47gS`D z!s%}%qv;VRp?%xn)?fcM{P;x|0j^vL7H!+YRH2@1HtX<{Z@vKE+P~y$%Oo@nzQFaf zSX=zjhu@1g{ioOa`m?!3!~hN*IfnoHm4CwHfBj`V@i$+=*PnXEkF`*bBW@xSo$139 zPFcXfwikwRh+v4=1QI?a&o83~77+0@u)ahvSqKbMw_KW-1|~uEr8q#>_vpJ8AGqcH z_^}Hw#M;^_x~_L_0d+-FvN(F|IHuE?Opr#K@4o0ei;?N^yT5V?-t@Y4VCgr2_F$Tw zTudP6*`sst!{`1x{O}up2pC_9C%^np_~3{B1YiE@Q&?CW$Lbg%qcXG2gqY{==0@rn zHJO2frq2?OO9Ch9NVhm_mX8R8A&?FWJe4!|gl}7X;1_PerI%fXS<|8I1Hy4deAerU zEASg-r!0)6@s;N2JB+FxzxQjG;`|?Y3GB+_K)c^ddHC1GfAM!4KeyjN_xz*4!4IQ9 z@L`*XuC$thxlRry&j_q_`P4f80Y`M%g~?v z8yK${xh89HMH6It+~`z)_mb&plUB$Kjhk)Hi#Zi5ivZP*v7f ztz-80z{$_yg7d! z_`x@R2fF8<0Jjc6lav0{!Y1Ckpx!pWO#il72LVw$%5u|Xnw4lYZna%L{0y?1`& zmv4C=F1q9r^lgXPbmAen&J~iau1xJ*geROyrmn*nJwEy?KY<^7!@og)@^RGNAz$nq zd`<`!8{gK5qUyeNeK%e+kl7pWcR;m>Q9Z_}{Rg1lg8%-Oe~YGRLJumxmC&Y0x=I>B zRKJr?7Q0&++fyez2H|K+rw6pt&RI{$?~`&y_-CEec4(UhzxckN$3>T4j%GSV)3*NH ziU&rN0?=G$UTZM@7T)}yYyA4h7cgtWdVAXk zWS7>;4)$<~Vl+{a`tV8wOKAidYZw)+=gcO9$#ePzV=!VA?9w4l%HDg^4qS4@6=-KO zKYv#Y?e14qE?Tw}^!b50RQ1^3YMlM{F-wnIcbta{|LZyEPdtgLKNzLG2I}BD9{ile z#~;{-%H>s@1M?;?9Bx=#7~!0kZNN1bpM}@Ha)y_j(fA`8whQgNuk!uPfaY0jsJ7rW zuh@#e{rWMCd;nPq|4DQa83A?8t?f-6sJy*SYm2Cy!Pa?<<_I`N=%iKNfp>WC&KvR8 zcf7+p^R~-lRtk+#<~InQV@B(lF!J^r7+1jezor9Dd=XXuO>d@91vqPagYjr1X3YrC zueMm37_3f#)k%dl_jv*wKHlQVzdw#&xcysLI=X<#WbBz?9MjxUG}}ZHF8H$^RlSaL zzGG7sP!}MnvnI)sfS1@7;*hLGGMeVQdYrH_(920R?h`C?WSL?4EJyN7{<+R0sQ+=} z4fyG+uMWYY9lFz)DsAWhYpbg` zeDEN?`gdQ!@#D{XpW=ft5{kYJb{@eW_-rgeRJ0*5^gT8}+hSxEQ|7mdM61|_m%E&A za(S!&_tY9%yBN%DZ!vNv&cov>pfc0>duN|ohr>8*C!!KqFsD+8PA^g@m$iX!y;AUP zkh8pC8$}W}D<^M*gXP+lCBKE)Sw9N`XU0cC{{mb~+AAAhE zcHQRfy_ak!nFx~T2xQ`Eu3)0wz@r>rfFNxZ0!q{71Fi8~J`t(%{Ra5$+O)mJshdXL zU-Zn61!g#{aL#;&rfbuljEUyEPsGnVg@icg!d@ivB!AagSrS{I_Iea3SIfW zh@lq|IA%2Xxt%-l_N%V;S~6|A1P%OQQ}mduuVdf7CEWF=ci_JJ@5gvF!iEit;aG8$ z@N9B>gyg)s2pO>qL+}GO$31DEN5)7^+u?g&wHXVW{=H{xjS+fV!?vA|T|A>5tELr> z9wz}d>0FZD)cfZJhV0_OrOwS{El`?dvzqXo!rUezwJ2Tg)@(Y(%{OkxTi@|^wC&78 z-4r}|Mh9j*yYCs?b?2XA?<0?3apT5}v?7eGk|6a+0}2vE-$8Q3zV>G*qX0Pg?EX&db&z&T%Mf6nu&IPqxRT8{wNv>(S2ydU*3if}MN^lK<1! zFx$(VXX2!Wu;R#<+H_Kd41D*@`dA?(;2bmFeeE^4>YYF1KleWAXZ-y>jyG9b#lC&} zard2f;h|4Gg2fFRJkz`cJ0z7k%>;<^fzS*kIjxUBJc-hDtw(0gznrn<)EIb%{x+Yo z5wH7?ufzx5^Cq0S;UIA03t_#^X*;CVxm3yoz(%yT!SU4&U;EaI7ns_axfll$e?&;Y zk?cQbgGwSJo_3d5T5tGg$<%d9G204*W;Vt3*ItWl@3|h6>C{DIe$%P;FDfo3%I36G4J)$deH(&pK@b9>4!x zz~{Yr%q|Pq(mBeLNtiSu)1v_bDcDXv#5T+YQ%y0=UIA?JT4# zqJLRiU&sCf2e9Y<`>|*5UW^yUdAumqCffe44XFvT&elpZ>A&{x6Y1kWkBvQ3-~zm! z*Pz=#q;BfJX}ik9Ww#+#n+CSu5WI(*gWsD8X%^TT%SXrf^CyA-{f|e}D@npWI=sM% z_>84ak_jv2viy3D%*=9UoP2`h9E<1Km4Hz{?&DSi-8Gglga8T zmX?RW~8o>#fGfJNT}vvF|YO#jn-){oUWhcr;Ev zC`zjEyGfW;V9l@M7OpJi$&8WCo4(}l(z0p56suT^snd*Weg@ZWyUr&HXKfpxW*>?a zdIL1;>o~Bqj0gAZ!QJ=m#^z0%@P~i!F?|2)&qK%epq;a98ce5CkA{4h*PEpLAs=Q_ zgbcsMYfz(4N>V!k=x7-+O!HPMXphU+);@Nr(YoL4DIVf>E;a8!LDk^o`Up$URe17i zBi#ClXRzJ?qfs4?@MX&&_KTQa2%47|qEo?~%ZC5t577Si;A~-tcKf)0;WXpgZQDGW zazr#~|0DD{TU*D00|)TXgAd`(d+){8&6{xhZM*Q=*S!`;jvVz)*xH!ZFd?f;0UIJR zqt{`O;N;&bzlUxU0%K)Cy^TO4C{owDNM#YTw((=SE#i@nK4;;HuJRiB?1>r&4sXQ6 zUs%I^pE-cWvMLlpVf>{63iRZVm|*$~p{zN9O9?Z}bV{t1qRg$&+Rkvz)mP)%Z9j|Y zWa2SC63PivbB=x4X~qK&dd=9fc{4t;YZt!nymPU>w&tr!+_tPsiBN3fnB%jqZiA`q zJTa1wF+oEJ?z`PZ$}PDBiAl&R`W74F5Xan=b&IDDbbenYw($8X=PRadjn&oOYsQmb z2OfQL2~RJrV7#!H-Z4f1f-{4sp>$$36SCw(7$!~;dX#=~hCKD5^HeM*@wurP-gd?1 zc-M9B_U6T9aX}EZ3%R4UHOGvD*!$2P+`IdJY~FMVcHMRx&OP@$f6qHqM{HAj-_SD? zK+xCkgLFMZRB8*_h6HbjoO`xf#21H!bxdYG{_vqStgKC$KnCq|b2zy^!Lbu9o;kdV z6RRy2Mk6dNEP~FPn1mzj0Mw98FK4~w9?{Tb@f;S5DhC|q@e1XCqIGl(c>9%CV8_ij zqnWLvZGvRyU9%o*lPQ*$m$CQ32XOc9-8gmgX57AO7tTBPdvS7gCFH8AI%t*lV`P@% zUb%_IZ&k_{<=J}e=tKKD*8d{<8LutzZEh>m9uIzgzeiEY#8f7%iPq}szN*TnZ3w>l zbfs0JD)|+gc>@m((Rg?)ny19QhS*rVO1Ylli@<0GZ5MX>-g?F5*nZPaw9U-tr#QT` zF&(kIw1iLY-HUs7@5a{6r{W{KK7#N0zH_mQM)WvHalH>>zj=xEij6TDG=itE-s*y2KdD{_4xe~ z(keviQe!lv{4(sg>1NEDDQ44IaH^Gs&w_WI_a%pxmhtGLe}Q{; z--oSRx8k>c>sGwt72k=co_gAwsVZhj!^A5wA@^dn@xlT&Z9WB+E9`@HYE!)tRGOQ@7H0)t zUYX#BObp=S?rro_(fp>p7V90neD@?d?wHPJCQDcmjX*i$3h8$h7%~U0eTQ~hlrwT! z&S~SBv3e;td=;vi!3 zdGCNeb#05IM~~u1F1R4#au8C$PnuRvRA0f3;`MgswYhmx4XNUj!^p4u4M|^2# zl$@~VF~Py5WqkUvN4=SHrofpVH;&8wFtQ9xC1nW6MD)(i`{zed!YiM7wNy7{E3`h( z70rgr@OELRumeHf@bbwje%9Vl{M7LmFN=y>4m4Iy`2`vY#;NM~q+#kRW_x*Zv5HmU zDc2L*I5grM_?4GkiW_g*iS_k0@4)@-o+R30vc85RhY#V=#~#Cd_uYr_cu?9viFZFc! zu*zgRE`1ONC$ePZ9RxKM`Ng&5-O`?-H7V$ZL-08a;h>2FGk%}!o--A0yYj79 z2ZIKz$U)F@e(EHRboNVL8s6DE8prqchN=t1J&F+gP&#CapSt{Vy!$;j_@rS#Q<=an zP;)zg&P;vwGoQgH?!G%YH*aQShAc}0sWsc~O}mcKq`GPopE~I~pTg@c9qu?{HyWx8Od2MZy(#>_;rJX7w75xz6t6VKY*(b#cW4B-83Z!j_YbtMEDu33{ zBPaJgoiG+Ci3x+Y^EO$kJY5@(wYl#cp!uEO{!M)KAHRX|c%kSgxNp(9@}waz@fuPE zhdZi*i+$bHtictRUV`mAcVcaQ%@6MKK4bzs!f$3ceE0}H^Ov8-9e3V|)3(nSL?wNQnv?Xu07j4Vt$^qU;0f#)>58bU_5K;|_1jZ@cYwyy{ioh2tkz@_-6H&ao7dHneDQ0@$Y4pj-At?~#>>Z{v<> z8#o4ZA+U?Zb|Dp#QyP5C5=?!ex$$<6DW{xr3O@D8hjHtNe#aj!8jZ6t3UvjcnB1GJ zu1gG=yJMK@i2BY4bXQ(_5w5#&2iDd@oZ&jLF^%pfNZ@%!&_%AtHK;on!%&BJ| z^3s=j$LLWNkogZ4o;P=q9LU7^q=(dCo~H7K=oRh)ps=RnjCIu_RtHv$5%dfZ7fSl{ezYEw?vYe;!^6+AaOTaMcbXJC zSKJ4P_93ba0Zw z1Mc^%TJ*5Cmo$Vi$ZEC{BKA$v6q<-Mr*iwY zpdmCdF#9&Q`h;g?(<@}@d7YFjEkH>zgPVhmFvxc~YGNZX(hj6s2ovA*k`ocyLjCce zRcnZt3;P2wU>YE;2-O!efP%8-4*xnrG7aR2^b79u1c*77Jx?yi6c)e7oFxJ) zf+nPsc8w&16NBdLYYT0e^Rj-s6s+WYM%q9(gGz@5+iIzJ(|#DZbhJ1*iPj^V74;qtv=K*L14}-A3nl8=++$t|ljM{aDg|!2o7|I88o(!|chxf=HlMpM zy{%tNktBaw_B(w1dA?=d7y=L^+p7GEGCOHrkU}J5Ga@WQih4^SF>^R$y2PG3c>`^r zQm@M9GnC?o)8iO@fSMFBM81ftJYOvDL-nASN~F-{)2L7ZDG+2RknLRYahWa6 zqlm(D-YpYkM%(O_!&N+|&iB+g8W^Mv@(E-!SOlqEqnML8`#!Yc zTGTkaPRNMII*Q4*@HuPC&@^U(v$Yj2y{2N3Lkh;2L=2=@6Ta}C6A>pUw3P2DM5438 zNgL^pR7xKq$0)#baB0hzcKp;>BR|Sl<-g*iG}~t8S+2Pyjpm=NDV`QqambvwL!CJn z3*-4DQozA0I3zNW+BDP@jqH-Ps`y9tBj52=L^Cu;qY{yY7eEike*uUM<#Uy`bI45% zxX>3FGLRv)xIBrWX5>5U_*)_bdzpVBO(AhSGdTscMBLI5vk17jhVvkd`3D<>+`EW)`lX7V6V!_B0D zD$q<4#!H9zlPZ4V5Lyd{Bx?3|9F?J`ym-~il8MqR5@|0 zB!!y-k~nPz`DDdn8H@(&J4-pwZE;4I>t_=}Qb|cMSDVkU3)3PVVC&TcWPu)EyHVy# zY;+Y~v0G$FhRP*RA~E&QeoKkxl3Ieb$w$~6fbx`$gv~>sKq_QXYMwMunk&vE%Tb!p zViqKF<$`5GI)v6~9_;r4C@C>g{Usu?_F|C(bF@57!0llX;zF7aP8iuvLe6B_wHah> zO41L;2z{UX7oK$F+9J`A%t~<0)bW!h{!rKy+~&_JZ8gld78yd3I?vT>N6iNklQ%5J zC@~_LG$uVcQ6a`ppE={XUuVx7Mfj3tCP%Gy^)z$28KdvAZ$j2)Wj;9rc{(YhzGQPb z1=*VORrqriO6rBF$WBTSoJ_eQLYiz2{Xt1=fFHErhpw=%iu(sE278%6mSs1 z+7?dX=0L`RDod&X*M%=Am=ZcBoFFMD2d&^3d=X?{9`%=h$lAj?_85IYy51O^JQ zk_U&Won?$tXemWnTripc8w#mOwC1*kUiCKRd`hwzW1 zAOCwiGGOE1*-+6yCc4doax+KwGZ@MLPxE8zNrGYNPphg}2l+&lfcpLft_8iUE3JbE zzf7}uay4=$9Y-K~b6IDFa%6XVU$S5I;Y~ZKDjYVwiY*Pqeg`)Z>P20mEk|$6OOoVS zMQmxNhm{@{`ES(<;VPIM-quyc^S|T4*pNo!>LZ|blEt3+Q9f^3Qkq|3XH$W?|I-`p zT`&F(c3I#hL)>8)e`>aOZ!Zep$Le=(KC&n7)u95;YLA_x>)OM`{Cc>Vz1T+Z&g%+K^ zVd)Jy2FlqfO<5|FUx^X&8To&nIz6T4tq+_OL#jiX8UJP(`trx%8QTM^n&fWANMn zib-wN-@QPtDEFOFR`N@F+79|uxwR*22lZBMKj>_goXPW~DW!F(>sQs9u)vF1q?YV$K+$Ewq zn}8j{iR(OFEu_d*jDz1_RC;#ln&r2~c!5n5Dd_w5B)%_Gx*j%^$dPC6v$WFtZL2ei zZfmmAZfoPu&n=u|_KQ~LL{gzRR(V%=qI__W_n_qZ!#rq@+&kMxk{(bNDKB-n@VjBQ z&3Ozv6>P4>&p3pL)F@Xef=b_GweQ!+$Ot6Kp7>FBVpMrldn&8Ur(v%T6nQFiojh+E zDx}V4JdN8!NsUpki*PN7J?lbT#}}FsV)A>PI$p}4&il-W+d|O{g3rQ3v=$O5aO4ss zdkXl%hZCJ(l4lDf6t^4|zCu)@DZ1VHY0YrNWUTl87?A%tnXn2@Ls>wy=tzY~ZP>_| z5p?XApJ`2m{$Na`vE9*U$Q7l$6c?5O=e5#6&+BSp(2I^gBPWDF6aztkcob^8Brx2C>7Mimc&HHw;4d zQI|;U;E>#ln|*ej;O*9^!#=((Wb+=at?~TxH$Q}w0#dqnF=Z5v7$a+Y|ICC{=6M@HtRb=k7mRB?9c69 zB67iIz$X;vA6R?q4JFEGbvs%c81Vr%>jJvMUQ2mzNp#1vibGUlz5{7U_bfIkcSW{( zuIo;`aKxr2E)urn;#s7*^Iq1oNP5_#eZFq)0p&sERbgpqy!^GPTy0W~ezk*6+9q6- z`jnj(b#ooC7%6{qWf(W(x1Nb+#pPZYz)ET z?BZxho|62X%)x)e8ucd-QIe3^R;iAzS>Jhqwu%~2{Mmka&<(+tJ@Us8Rel-A4*5F5 z?`SP;UQAI`$e4d3_T8Udk-oesZ{vk=6DysjGKO6Co>H@F+Q)9wb!9N&quXyad0dm9 z{}yaGn+q8K$|$nZJdZ`l2cuGRa{van06xUm-}ugYBaa&A?BZpAqptDWRQrS^t!Lmp z<4Q%xJel)_Fr_QDdw0-`T=2j!95%J7Fx_GHclp6i3Q7kZq-MTSo} z25V9e(cDsh(=4jL{sN>RZxT~!*By=V0c5CEw2lhAgk=*GTVqp&kdFTAnZ9EzzB9SJ zCdg9!@#ar4w>d;jN^sYAJdOO-Le-zvz81p*T@Oou2iPtiD1uzxnRg%XrizUgygZY2 zygL7<`<>UAsJz-?AiY8Cb>pZbfI4h?Z76#_P24RD;k(iUW}F`8^-lJH{CCZ}CQB@_ zl&JPNF`c`@uKQlf(;f0;u^Ezg-8(15guHxt!m;6av#;CYBWkQA)k$Jxc`V|DKG%b!pl4Mp-bCeKf=Y-XdX!{h`P>9rYIk-_CHw%R5iH62f(@G z3{W=mW~41R*z?(hCOy{SQp(4kjq!MIc6!|B`szaXckU|4;qJeDpl}oy8<%vY71huA zm%vc}x|n}cS8`hS!yL+Y+XHO2X?C@z9EwaXwy5dOqn>0v@Air)IFoTRr(Uub6D}r_ z{YhdoTx+{3y+{kUlTXO@MYe8h3AT=%Q}U{^HS4~z>xiVL-57m0S&`fLJ3DXu@&z7s)U!ffi=D_-{ z9Q_wUyo~?bq=vzoBQ&TX)vuA4w+C5dK2=B_0*Ghqhg;WJ9ujvqdv80$)ws^S=C6X{ zmutGJ;NHn>hF*eia8NtQ`H@e?S2EGkrlkr3t zN{}Z;o`+9Ad71TLkn6FS*M{CiHQ8bfkGBg`sA3qIWTVGwQiaq!LAzHqmPwLD+9!Fx z)@fW=(`{oo_haDif`aDzju~PuSV?K09d{OU6s%EF%(fFwrM&82Vpje8XxgnUtqvef zv&jVjg6r?^MD?N)Lmf~CXhk?(%w;QJ)O-7{cUK$>Usy@V$?8C0sb%ZbvDRbdv-vV} z@d-aElXj8KK!8_|Sq6HV#(3ZeI;NC1BdwFXCVA|v+OSnz0!?%>%g)MjowKMsVq)sC z?_`h8I0AXvuY=;xslrV`bg#!YwKI2%mIFYKt(twxpk#V;a2|6Rr{MwuWCuWRWom=& zbTo2?;%tH3_HAbyMH`1$qr3|OLC+!#J9oE}^5~#~n^fWkX)D>uepoL6@p01h2tBqv z*&5GFWz_bd3+GOiIS$yZozM&zeEYfKUK0sK7)~W6+~$9>r5d-KoFrolS!%F>7N!O* zBCLWgRFdV&bHT```g!3qtY?Z=$JC2grBCpvovMC=p!Z_a&>sR>qQk_B{IPVYB96L8 z&8C&MgMh2*Ov?r0&+CW@_!G~l=hOymfCW;wIK)4eUBLoVOoY0-1kGa^9$g2$%a6-l z8d;d;?3uo*xBb|_`+}A;y@?!ORy14GIjfS^5-gg18s}n1t=EJ&(rkjN71kv}(WFvT zA1`<7wR)!M!OLB--=32zDo>$>q)sl-l!B>w#BRZK+}a5~*|5W?`yw*bSQw+DpOBfhv|2dFk`)?IB z)bW`hlkH}{G4L40ho1M45$N`OyNt4&#-e#@+B_X9eYWLZG{ z-{$9GN3m}EX=@{kF^MwLj|-V+MADH*&w&)A&cV zL{%Y-8t$?7l{M2m3p*O%X5A|YMxyXr4Pv9e zb-vlTm)w&g3(>1T>~%}wH#3ls?;DXH2(6!6S|dCk8HOY_>rWP^bf25X6T_sEgL<4g;zg~?BM6@j1wOaYN*x8 z5DPggE48+dp%=i)m2zbsjt|pkpW!Tn3Eme_$4LeGklVy$J%Pjbwo3xe_NsybLps#R zZx3+meP!Gv5$;4ez~0ko->Tob$OH5(6WCZ_d#YWJg|yJh_g$u`Fv|*S#hJ?XF!Fw( zY3EbrEEzw9@Y}wIxSgq5ttnde1N5^W#OlN{ADB0cro`V3E1aJ0PMZV?S~QJeaF&Fn zPW*%co1s*=fzFa-5s?-NPWO?z*&su;M_=+&?x0_k(^m&qM??LVPNm6T!iFht4Put; ze1!W0jyG(!^5t%>JaKMA1`qiBImqKJ^BnH(Ee8E_%z_k6K z$kctn8SwKpFb;Be#_f};EtP;oF$Vo!cT@I@JR*IWMI63jBNscqNCOpDTm&;@QrV7G zdy;tnunt$}k})Q~SUx$yr0I74!FdAd zUF!l6Z))np15JserAISGNVf#(0lKvyy#syqJ?Si>+c0v} zwq2BjGDdLFX-7Mts& zAs(Xl5}0kXSlZlG&Q>dp9-#3FXcqXTJclwW%~ucmr|A{U$i6tjWQN&%lg@#2&MaEj zI-S{|F8=@SF0}vm)#iUM^N*hy z>#bc`Aw`<<2ubeT_x8c>A_ZgRRUUB`ZuV{<&NMHV@K2m7pu225B%>V}^n4w6h)N#n zxoU(!p~+(j?23hJ8@Qie4(~a?=6{I|@Ckb0cVAtP9UT*o=%zdY3#%&q(tWR)cacdS zFv|vJ&|_2Ur5t~l%)rI#9+&W0`4h`_Nd%?HBgnwhlf6O)(`HRGn({z>;s@+Puse|b z9lyUvJ#e0*wLo4$%w)y(cb@I}%D{kpL*dVYiY>$${2(j$=xBjp#b4R6BXWXzs_q>r zZ2sV0qg=^|2`YNQ*DKTMDLVzaf#QaQZ0Z9p^;Z#^_`hW#-JjfkYNZT1UR02C#IfPI z1`J8D8(5D8+-HeUe#eoUaO5DR>Tf@q_iO`GE8Zz>dv8SUzyrpMDpgB74j>H=4MzUG=xd)gXxdR zM3WKWBcFX7kyYr%4W=TO_)Ci%V*FEb>zE3OIP4DEiNx}mr2p0VyqN+YI)nF<8lkP@ ziyEi;=l>3*{U|R_R$VqXkqMf`&@^GcDtK{LtN+_Gk*gArI!G8+_PYLhI*3}K3Tkum z0*?>AA+H_1J&$n|<@sQpIR)(~j6!?dM|F>ma5B(>J7RO%DxU{Y=sw~rt~|VD*?4V# zvG+sZb#Y`Y6PaoVZYbIrPg8BguikrijK|$$TQTbzN72>$*iLSlf=EcB$Mz`|o7S6+ zgRWrYP5eqnfldbZ4e5qjYK(h?{iB-MHh-xvJU+p6XqgC literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/XFrxm.png b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/XFrxm.png new file mode 100755 index 0000000000000000000000000000000000000000..8d3f3250fe31904b24e1c012eca37e67efffd961 GIT binary patch literal 10723 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 Hu0mjfk+8ma literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/XFu32hv.png b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/XFu32hv.png new file mode 100755 index 0000000000000000000000000000000000000000..dc371a5ccbd0aa946618eae63be13c821084454e GIT binary patch literal 13017 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 b4FLZSH=zYXh z2Y8fK+V_7?nO@Tq0%`On(nPU?G!?N@bZuZ;b?w{q)h%}4-FMfvzH48>Ua)`&h=2%4 zlio{uPbSG^di}0*CSfh0xQoiZANO_5l`_f9^PK;=`~NTSglq} zCewaUj>_SaPB6mZFo8fof^y$|_sQ4)iiU`VH=E7aY&IMY$9_Il;n3WfH+_E$_+Cn&;9319>m;vqEWs2n~KPMi+YCDyuAQ@#R-@E-ycVV(pq zonJe{-=lJPMS$|wGlhJHq5BAV-)S1>^{mqktS?aukpw zOpXF_gvn7rjxag00ZD$jnfd=mWAn%cL{absV^mbtb+m2-EdRHGIl{`gspVEW7Qeox zmicf0oth>OgNBY}?8LLtzLeF*6A78+s{e;73P%ti36jY$ieiyJz5UKSK3_DCVs|6i zt}K4>^D7zp!f39&@^Z4fWiw##P_+Mpt>q&I5K)#^lvEM(zW@F!FFpMf`IS9AB> zzo%DWE(;eeqPjKAmi6oT%?;Bzu3s1Gs>>)V-OijP8+q@8`5Zs=1kO2k0+T17kHhJZ zCM=nZNaLp}=AZiZzPB6^TS!$EP*j$GwwkMFT#qX~gKmXInA4nW*|~{Kx0QzaTDDaO zSiGT}ihX+-Ik+!ogU08p*K)@l_b}@8i9GV~qqMd8Q54gEBpApo36g^{e*AdZ`w-p2 z&c>8zo`~Q5P^^=bc~PVrpEGX}lc!vY-EN{=mwW=jFwuC7WIRqnRWX=tELgCFtvj~y z>)+hQa9+WkJ}fA|rz-+3Q*nwxg~p zDzED&)sSQ&A-}FzwVq2ZnSsq=B|SZjV8D;6>a;ZZNhZXC+p(u*lbvqmrYp~(Z|@>J zo+fU;@mh`_I)KvB?PTY;DCv<$Lt_P3Uw<<<-g+lX7JrJ@>y_{ERWOoAY+mz|Xh_O$ zfU2qN+O?On&ba`CK_@RaNA4E!zGkxtpFe=nY{72Q*}eWVE|_=%lP?%W>E7*FG$T8= zZ)fh}Mf5pl5K(^sRZ)p2lDIR{S+RU2t5&S1wx$k;(}h7d)9Uf!blE#{8sTeVkW~4x z73bSDzymB@_8D)^o=r}6CSGqFTeok<{;|&B!Gm$w z(+LFqlpA7rb9nZ-zf-<*JJ-y(f{LOoWOpgTY__AINv-{< z-OP_;vi6(nIEs>TTbEvTJ0E}i9)r7Qp(GOcLjg2FT{T$B&29y~%s;&*##q(-<(|80zai>?yCMsiB;11qJfjQqI5gdVd&nesYtQkXtVRK64q~7LQ`WiR+|;4!^VdnEx>HaQVGJBHnBB8K#^dpk-?b}my*HQ2S`T~oR1)?F$ji(i5q9v! z)>2ljzKy>;`2>;nCWZ_?j>lhkRoc(@U!TdAE$jGj&H@^mBE<9JEctXH0e=%URl~{6 z>q;mPARK66=Z^JEJof_9GV|p<2=}l2K&FfzCJ#wD3}K6<^KNhBo@-}t>9on*^~ZaO zCqmfl28N8ej3&L9=U!aEgMXRDz~23-udXMln(?-WY4`hay3C|yn6NwStXjE?Ki>Zo zZ@v2ot^Np^c?Gz#^3W|#K3cq-x|Viwb91Syt3%Tb6c=|#x0?Id(@kmFcA}9cLf#5K`EV8$d)Kk>qxoDt{R(z$-^kuQyQr$H zl=t;Rg2Ca{kfe0lLPF@6J8bdu&pyXHbKYTZO)FX1#S9GV$*0>WX z%yzJE*G>wG3Rtst8UOp-yNo^YcupKTlE(TLnmqehI)5=|j5>v!lA?d!bw?BJa8&^g zzsX7}XdTz~>1)sN(C_ajVM{Xhv{UI*at!xB_TQ{rzL2R?E@SK3<>YorXWZ|8hoN{N z&7ZDj`Nm!3<>!;0;lLjVkqBF9544isHG}icJD1tB-=n;;9-B1-gJLEYQAo3>_+n9l z@hDE0n{YTzpv6m8W>>`MmjTRzALYH?(7%% ze9dZJdiH-Q%2YY+_}=Pxbq6Q_UG3#aOkmzy&SWDg6gk- z{i_Uhb8>RX&dw$)D~t5>^!@DjLlF_q36)kGFsSMS%<2b(2W|Zu8<7I2!#T4$sHCKbf-c=iCgONn8;C|DC`w!soBfeE73KBBLm|xO zB!dPI;*MY6#ehB~j6dxp644k}TyZw5mVF{C2D#n4v*h!wBolTrGctMkmDf4-_>o+C z*<}npVFbq>d+a~=;~~_{M93>bU602@V`C!?4Go-g&N=eA!>S?TLc}A~*37LxzlL^^ zKfLz}4y(?lHOpvfsv#JPV^7QHq|-)l`>$@G?~r3@s%@ckZv`JMTuMz{6S-XrP?HML zke^JKi|)mJsI9A~qPz-|!Hkkrh()8sqCxVyb!GT*BWP_6ux!-f6Ve(6M^bglv%G~$n@xt5hlIx1|$=fes*8RBB5;*KS@j#I6+qcnk;0Q{34<{od zol{2+=KS*~;7+%(W6wU~kt7y_i2(z8p(G+ySJYs)*~m;!r?t&TIP4|Ep%M?b6ADD= z(Wf6x9uF<8&6rIlqLC=Ib&XWl*3s7PBNz@7427s`Xk_1>auUe|`2}4FMk3VISAZ6y zt;It^F|%^*Mlv(=IrWUue7}sx)ziW3WnLarXl`kwxupfK*GC`_q_lJ| zTeoc@J1dj!C0+5ix1(afpzD;D?dFX)UT68rHM~3b9c(TWX<1oRS2a`LP|vz8Yl$Y~ z7!4*2x&^!4A&a9QemIX0=FFw3!9$n)uB4~C|G8nmlrrw6hh$^`V1IOOSX+fppg$=z8|y6&WY3B#Z_=TtC!EFrF{=2-FtJ~sIvhx3qSaX zMGF?tr%w?^o6gQ%JMlEN(Xa0i!to?~%PL4}D!E;|6Ib1+)+GK!1H1Q?OFcD;s$(*h zWp~nu#0XhD9wQX-qbe56Ml)uM5tHs@?5I&RG&Hek-EIPYFGa<<3>Z`_|7Yc@r6iLI zUGfXqwY#341wF_v%woa94=@=lB$GxGib^yV!Duqk@0erQw{IU0JopeVzx*2G&m6j5m0opa=N11)9{p56IBf) zR1+Z#l4z0cwli_!KsIgOOlfH)M%{$l?PlV{bEs`Z@FH`DHx#z#pZhNhH6$5A$!Ctb}YZrJ{;Wo40ex#Zx)=ymR=) z?Z1LhBTbDSsw+KgSXa&$J9gr97oeMqm^1HFGVB_?`t-)($|4a~xblh_tX;p0Nf%y7 zanE9wEnkkOu9*S-doy+N6zXegC@(D|9uxC4iwj+C;p)k#wt0ahF^i=2mv^{*3jTSIg3Qw@e?F^mG(w4jDzq zu)DmDSW@TYv13@ZVIxC_4aMbjQCU$>Nl{-cCM#_%Eesr3#I$J>$;`-*CHiDMK|Gc+ zO~Ofw4f5rQ*3R$6hLR;5O_OcmXgn#wiTPWoE8B&?xel9_AlGfdq(q3vV)z3A)~#71 zLGRL~3z`7lXk_W)WmHu+GVNzGXsWBBtV}Gt%D}E}f~_8`HYaC{=*skIL%8kd<2fcj zOeSIG%zB-cde3*R=f9*Ooi{3WV`HO-voDy;zOr(P3wu#lQODrE{m3uMBj9gFi~GrU zr_rNlZ>+i-H6)@GJLX(BJ$m+H!-@@Db;AwZbl2T@17TJ!S<1Zk-=n#y0lUM-_N{ve z2ZCf|WHMy%U`k4QuwvC01cM=rMuRi~!l$U3CL_I0P&&VgYwR>zs?-`yv?@s?k{Fc) zW?e&9bxE}F`C94SyEh|GJb@NZ8(A6YXh|JZgKV4$AKYT38#?LC5KodK`as_7&=T& zU8tO{X~bg*F1h$pcJ1Cx-#&fu)Kt)`IGIK}~d2q;%=`6Y(TLpC7k7oBaHqw6?U6 zm!CyAq!5jOQ5-L#YebV_+I_7k@i>d-y~i~--A1U*&&thPXmxkz;+uzaapUpq-nNw` z?|;On3+M3IYyaTJd!M4_+UX<+cW&u}uI(WMlDg3p(Npixm@6;8j8!Yv(5Kg6BCR39 zz9#Ow_dbe>3+3|`TNk!(-->RC;B>l3Xpo+hj?3AN|GoEN?tJinxbN|2>D7A>ul(aV zX8mI(MY#@oYsb>R|4??9mQ(F%#b&c(cco$04dmwKQe9Vzr`01GEz+Ne3al_;>X&>7 z@C|T;)`aEstA@#Fl;4a0vl>kjAd}HdBobxo&YhUd1{4gK3{JFU9FM0Jv)RZ66UOt! zx;1QAzlBHt@F4M+7cJrE=4*e3i6{@=K3R4^TLu=8?mCAwM_2xI+YMCIH!*PVAYyTG zwLwr049Xz`l5$2#w2p4gO*j35H{O4bzWs(0jmC)U2~4hBUVUXHCUZaf4?PBVx`V$z z_b?ZoGmQbqp2o6ybNPJ1YTEq){`T}t9{S6Z>}&OM-_M$Z<}m(CAFjkLoBp#?BB&B=`>v;772#J#A0!&JuR)R zIP7-3?Oy7?Scl1OPO*$75hG>hf`I_tyXBDOuu`0x!^CsWXW`P-)Hf^myltF)<|JZj zf@j`d%$X(Yyr| z=I3*4-(E!g)!3{-+I^sz@|gABVqTp)pG&5k%lQ*Va_QtN7&-nz`F{gOoW`Kz0zCEf z-?`?VKhYXV@VAE^B0b$mQQtgD`W-_b-9V3$T3((t6NlZ2l8CZq^(sjZ6%IKOPe>CY zf(f;wOreXqmSmA6zH-g+j_3CSA~%#Oxn9!W#N$|PHgu;`+H$c-2CGF{0Zmbb0uoC^ ziF^G_zicWti^gy6`aPpZ9?QL#PGU!O6Q6wiITxKZ5=TxRMMF-fDq!Sqa~4w@a?xCR z6K_2K9CzM%C&wN;jD`j;Hmie#AiE?FT<!!{0 z$jHU)1dGwk?70gmE-dEiE2q=Y+D>g*{bbQ6JpACjC`O$N&c2ei z`bzG+w3z!JoI%K>Q{VJCy@ER!JpLkPz4&()ES$%~zqyvPFPK7EZ3A|DHyLt&n;e>k zsEUm9SFHSq=BhAj)|D~y<)=9BI5(5V1n51)N~6cciGvMHsI&3m(ppyT4iOB)OS5J( zd(LZ2nlzch;_j?mwvKJv_hB^aq+4{7Rs-Q!g8JGBiG)UFO+7EY@fKB0E%91?WKJP8 zR@it&Mb}ih^99>cXtS=RdijYC$1d9fsw#t#L@Y(bb~+r=fmhd7)1|lzS=m|CR@4)W z#w7_a8ji{haQ=DcvUSTUs)B9wEb75M_xyo|79R!O9Ncm5^>{XIW8UMlShQd*wucSm z4!o2VUo7CX5res4+7$$oI!Q&tVsK#44N@y5WAh*ieMo>rLSg>=$A<~n?X2FqkrBu1 zTybRq-TO44hG4_zUe>MbMv!z)Dr)D_2`U$!?WCZug-70OWBV>Ex!GOmYB3N`!2X;*b~GzL|6I~e?RFR8WCBw%MnPT{ z`*v)W$=a*4Ugp0adX(C_Dz2PyHJL_T_Eoy|H*&@WgBUeo0K+c2hWmec6{}XP=1+fq z48>x>8;(iRg*d-QfFVlq8VRE6fk{S(1c=|~WAoNce7gAy$+Jx#+=ZdQAl430bgbP|CW-ar6DBAE&lghN)elqrfu zQvHNPlBjT|B4hUX$S*9wYPHea+$0k;vD3xRW{X*tI3gVY32hVFkmvxY({yF$WMeX! zX|8V|m`uvCh3c9bGP5%nKW-eQd#WhgQbC5*Ng@%ap{a@B$w>oJVGt5xn-;Bg}f@ZEm~gx482134}tVIWsXD zbqR*J?(d=$9RQHd-gvyDRwZ_sg^U~<{XI@>MmJ>}O1XPoDW7~eimR?FV%&vi@SCye zBL0G%pFv?7@tBSG=KI+6d6LPOkL8-_zu@twUZQz#12(gja5#t}xV3^jC-#iUtP>)$ zhDg*e7gNOjnieDE^AZgP@U^z!OD1IfO3BK{YI6{e2c_27Y*yKrYV&%DL_(=hwcRTh zxV@zfzuHbX9wWDFKG9%^kk`lQqfVElmrd)wVCbO1RMzfe-@Xm>EY4zZVK&8iF#>~& zS^MrvZn*uoG}mmRpnERq;Z}C8{g4$ama%cgX6DT+rKGowEo+u>?RD2OaL6!1icY#S z9h1o@!I0)k1%sL-_AP$>TQ#JUVPSDtu_-EPT96l?xtD!=cJuV}Z!vfNd_G#7!Q|Pe zGi`DqXN=E<-aXj0%+CGy*0Ew;6(=0umHQul3^kceMdcIZSEK|lVQDwD^NvxaMv1$ttU8 z>xx6cQuRhfYMIcQwCqfrE*HBh8n9Z^C@SbhO>HC5SRAL@Nz--@-MV${E{6#I6kkvP5j_hVp>Vw!8KacEJ@Rtp1$ z3}?cqVf5*5fry`v7H{PJk98WG+A+JEcze!AWastcXHza`^PVrT7)2CSLnzXY)tV_2 zuTVHhA{LiXkYKvYPG2leESexQqYHMMQ&!Eb@>)c*D4(3%ZZtMD5om9fyx>?Yg4Z9C zPFVy87ORzLEJ`@&m!`|+aLR07guAa=8!dv zNXSn-5+M;w$ex5q@*=??7K4*CR~mj_GrM;S^A;!F;bPyuQnI>~P*YiszoijfQ^?HA zp-=AtvI1OLSt0MCq@)Cc$w)XFr?{j$y1~T0ePwvtJvbc}Yz_yyEe*TFP1&x^6y&%$ zZ^9^oZH?GnM%sdHj5_@cuD#=r^cg;aFIFzX;%{T@#4(IM_iT=zbS3v)eJdY*yn2zZ@8qkd&K|mPGwu=XXNJ=Fnqv3sv64Jx}_3y2XUtp&8U-5 zA)%|7EOxrNbR3QZ?H)g4MvtblzFzLE#^wgBX<78@-HYndeNL=ezK>BMfg5s8G!&d$Ko5+)k+(%M#qQ?WCC%t)3kU&g+T zTbQ?OKF`ej2T$GgOP+h?CeEBPj*7k8Is5GXc-wW}c==hvyOt7(MVUPPdQLy@0)nA{ zj0CL~D+Z$(quGqvWK2bPYU=TQMsH@}c@GKwB+VECX+jyvgC#*7|LaiN>$`UsAVH^`F7@ZrNTYBBuYCi?g9PjjmeqazK2$t0;f$#_zZ!)L04sC z@__01zs$=rceHSIk%%d(?3xKKl2K2UFa%igz(r4V|1ISZ)zkKipWEp`}XZaZEZOQ zEkQv+H>&HJh>0#&Qb$w8WR*f99>Zp}$eR?=nTYpA+%H-MCX*p`{6;b%d*i-95SJ?x zgDJ^rrw*sKqLf#j`v=2MIF1p=_vNkkSCHl|!wo54XLCxLt7b4sQ)}ojRR>Es{$;1G$v;S;om54*rlj(CW9o2z zW85J5xbpC2a{#9^LNLRP-(|z&^^vsMiCFDa`3$s16&hO_$SH55d!Kx!Ty_HENB3pP zqTRgx;cE6YHPGIo%2uqy;U*M`uxQajndzpvv)H($RF-WdD={1)o=nIMVl`W3jY?$s z38KW4sre1L9zv6?R+;Mvf}J2P=ma?9+%qZ8wqT6bam>&GEMB^pVIzlg_4RjARM>~5 ztDYy6_>|Mno6PAKUkqk9cU^Qdd&=s#_3pbdIPB87T5Wa=qMBgRQAM3fQ)Qm@?;OE$ zNVP1<*_Fk-s4QWNT_7f8qY)!&TrfIJ7!7tJkswx+IKbbA)gQpp+=9=ULBJoS&C@_r zTbK%8D|_m_^vtu-t4B7IEn9n^A1YfjMZ+Ej@Mum*{z#)pP%-+R#^@gCP4TC zIi(vGNuW&*iN+JDYt%c2gG9wwkgO#^PGtUiO7jINA{LF%P# z=QHPx`8@jcRLUwg^5V<|JX*Gci>F=4lq<)w?9C@xynHLuZ@Yzz+0iGjFOS)O=Y_RmEYA5%0IFMJ=X8fEFaDlVCF z8C^{=icc-1e}0$(hncl~k}PagcjE7+~;d`=9zWRnKJE^ATLE;KYaRf$GmkT0xj41ZmY!NJ>fV=?_?Kqrn?9DzJ ztIPRpV-;Rs4Bcuc5(pDjT1muoOd`7#0En$`)G#W#B*vLE9iO)yaf(Ssb}r3rtx^ND zFO5s=?|%qjM9kiald8x*h_9^`L(oTl?*aHjg?#kJ>pcC)67_z6LD;gbQ~@x7K<6xVCZO?(J>SAqXY6)iG@Pc*49b~ zCGvxe^bCnoLbsqA4XCCFddw(&jNND^V093*yKuPMNY8S~V=(j<4`U`|vuDRnc5L57 zBo;@trHSSNs>wy%V3OuZkBCN=Dt$vR?5EwYV|Q5a$HFAlxYQt7iy=u&NzJLGXn-jo z1OZzdOyjNeFr=50C!YQ@*WdgHh7@Ho_5AT{`QS}v|Ksm+;PaW~UvS3gN&NYDFEIAp zF`PK=6aoe}7PE%gW|S5{m`Ba1q8ml6I*yuD56h7I_Xb4lzJUV=vTfV8gZ|CiZ@
  • +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 dmj9Cl_fGnq_j6ta2=bHsU_|p16hAD{ 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 Z{C_mG3u$USQi%Wn002ovPDHLkV1j=gi97%R literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/cloud.png b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/cloud.png new file mode 100755 index 0000000000000000000000000000000000000000..f43f82e1d922c6f214a17c2632d7da1a3fc88c50 GIT binary patch literal 239 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|&H|6fVg?3oVGw3ym^DWND7etm z#WBR9cWTgW!2=2$-1CBW{LfkESbxPiPW0U`MP5%Hp@z=?QfKBcaU3|~;PS=ghxZq+ z8)hE$yJkoxRb=(djd1Xsu*3QsM}DHTlkYY8kn@p`X8b&~<&5Jar)w%o(q#|5uG`y6 zw#f*ssoS-cD>8oJ&e@k4(@U?tzu9}G^nc6R(4&3y;9{%9{D){9n0Y9>gTe~DWM4fh!0vn literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/frame.png b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/frame.png new file mode 100755 index 0000000000000000000000000000000000000000..872e5a88c15b8acdda68147beedcf71882ebf006 GIT binary patch literal 764 zcmeAS@N?(olHy`uVBq!ia0vp^DIm8C=m*N?s|={lP>`Tg8}ox~TsOf!7c4CkLu zHk+L)q7{C~Lau+~{r8V+?2ec1mVNg>)!C#%QAeyhVr>|+*R3h5Rw-x(-2Yx_+_G@y z_T1U|a~w-_UJ4zn+S}LO-X5Vh{i4ZP2B)V&Cmt4b+|4Wh-aSu!t?z}vrDp|>J%0R{ zVX^MR6I!$TbaLC%jAHJUPSl-vCXX_m#=@Pxb z+|&2?l!TUv4l81qI?pfm-t0G9=>B{~PSwAusvRYPH%%_zu1s%#fB)&Xj7KwmmH#z) zZY}WZE`uSrz~*5n?Y{{wg*7wCb*P(t3OC=AS?8n(AG=ckXvt zk6({~(Ruix!0*3yuYR4Mu0Gi_B$U;=aSQLEjN4^1Hm0qex!kD3pzrwO56kZ6iC=zc zQeS5y_ka2S+VfvdueaR4cQ5beYp+AtCI(5>-D0aFTU^oy?J`w#*QBh Yp~hd2#6(?_0H$gNPgg&ebxsLQ05@DatN;K2 literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/txmod.png b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/txmod.png new file mode 100755 index 0000000000000000000000000000000000000000..6abf55e5cc5310aac6f7ec5f739b4b56aa60fab0 GIT binary patch literal 315 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|&H|6fVg?3oVGw3ym^DWNDEQXX z#WBR9ckPr!u4V(C)_6A&mc0$%?QhmqFbJ}0@B16+6}-GOr?E-p?DgqaLOh&YCfsPK zzIL14(}PXQwR`E!>U#?QP zeL}1kOXrEnDyvp$t-7&|SCNI?_=1DbWfRBHSf^cQLPSq*vY7EY@batakUf6!=V#`*O572dFOMzD+_3KT zL-oHaPRyzfo7FMrSlz6xU&45=Om}-AtWwMP?^{odU;V*5U-mQXVOgpCOG5Vp(BllA Lu6{1-oD!M<^y7U^ literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/wgtlogo.png b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/wgtlogo.png new file mode 100755 index 0000000000000000000000000000000000000000..b04904458c431ce96ef052757453c2a44f0613a5 GIT binary patch literal 1314 zcmV+-1>O3IP)$O{>-FG$tp>Ow-Av^Z77) zbb2wFnY0Ujn7z*0Yk#x$IcuL2sm#;f11~!Jm3w-61a<)BGXPiycJ%c0xOVN@UE%lNsZ`4T{{9(tpZbyd;t7E23iXrG(b2K4uC9}-bPRX__?6SQlSWkt zxn2ml|Gn1L)m0uH9UW2o)m2r%*VUmdTeeICW2Z@21eS)Xw*b%oPuyG@3v*jSuB9RO zDRG-OZ=O=WR$oE=Y15`nr8*Y42Dq^17Fp2%+=`lc7lvH3&e^zeV^Q52sYjwHTFC77 z1Mn?y1@I}A^Y;&6H*nn?bN2w(16Kl%19t$2fmYz=*(R#rCxGqCmoINu7j$=bPXXuD z#C#c)1MUI#2R#;`x7Go;6}UIR><6v_t_I!$E(h9!s#Z#+QkNzY34tlp7uDC)_s7S_ zKOP$!`zDU#U*b4Eq>igI`F!5a&dzH2hk^CL5}=Q{x3@3Et`BqV?Cf-MafFI*H z-W|vB{y2_*&FAyO>d)aGOh+nF6rrvR|DYvQ@q8*18RLIYdXa2srqucoa(N#0r;+Zq@$ywwt=?+t7idR z9b%3Fc64;8POEF{1#Fo+tYT3VJpmkEv}jTFA1$Ers-K=^S~|om0BCD#1IR~Fbbr0; zCh83gHr&?Mb{%kT7~c*YohANm;DYL)y}cbO=X?Fu$SZ$177tSCivlt55Rl>2?ahR1 zaTw>+lG@)0Tish6Y+OAI^oH@pRC?UNX<`S$wTr{Jw_GkCrE>bzZH)pRjiTuHfq{X} z8s7s{df1^F-=W}ppz0VL9DJ&!rDXy*(9nuaeAT_C{tk@u-*&D6ZURa{Hw9ny`nVO_ z*(h716$JQAJpjBK76*8g%FDoWL01MJA?DR`xjaqfTyM;psNlWo+Y=KLW1QHe^S}*3 z`5OQ9$$dEJ1b?wu{7ZeJiFKOVVt@mQL}H+~w|D#` zeE~``OdbNG7JhK5t^8pODj7mdZNTn%PPncogLn@z`!_6lD Y2Lu{OvzE~zt^fc407*qoM6N<$f-O^X+yDRo literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/wgtlogoOFF.png b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/wgtlogoOFF.png new file mode 100755 index 0000000000000000000000000000000000000000..0ef51989555b03713fa781208faeabc70dccd601 GIT binary patch literal 295 zcmeAS@N?(olHy`uVBq!ia0vp^8bGYT!3HD~)>;8c2F?PH$YKTtZeb8+WSBKa0w{RX z)5S3)qV?^JK)x0Q9+%~5?;rf_f3CW%ZlY7A$lnq|21EGR7tToB0bwyaJ=PpQF2Z|CnSMq{VT zrfpZ7Z)WCwd-}oBf>&W`>X{kyWOSG})s^hcQCY#-@amyql6zUW(vyQs<@>$`NPOV3 zP~h0H$A0On$hd|ZIx+`kCgd$;_$DHJW!|LLxmIVrie^VRc)whsFzX+8*>z=xY`Mka qhkA+)KkOCtj$Zim|L2Y?9~!KW=BxT&__F&m$m^c2elF{r5}E+3igr%` literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/wgtlogoON.png b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/C/wgtlogoON.png new file mode 100755 index 0000000000000000000000000000000000000000..8a3a91c363bc12c23e2aa91884b7c1fa967f1a46 GIT binary patch literal 291 zcmeAS@N?(olHy`uVBq!ia0vp^8bGYT!3HD~)>;8c2F?PH$YKTtZeb8+WSBKa0w{RZ z)5S3)qV;WrBVV%tkN5J->P!FXpLoaYI^5C{<@nZO!>R|;p~82r6*6l+&#`^{F`r4d z#Q#I5&#wrlWmBUv*?dDicsml9qw^z=ExelRsMrxU-Q~(^NB@_*-yCud_^4_%O;sXs zRtR56^Tj;A*PE*4W!e@zN%itO#y-7emE6jsRXtWW`=9KQsutWRvPWu;^!Y@IWgcF8 zWDS?!JE|JMdnsVo%)=gQ7rs#_PRWy(-R9xf>?L4n^>UU>9XtPVRgQO=K6#gw_I00q l6{fSM-sk&yWx>0`f0zOsrGsk<^%Ftf_H^}gS?83{1ON#lZodEk literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/GS/cloud.bmp b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/GS/cloud.bmp new file mode 100755 index 0000000000000000000000000000000000000000..088623d967b9d34fcfb994dd199c5ce1835381b2 GIT binary patch literal 232 zcmX|5I|_qP5S&1|Kw#1)B3K11Qh0-4n=T$8jR!~(uizEDf>*E*dqF(FBW)_-A*50^ z>xT@?EW0zi?Dh7+f|r)Fa=&te?QA?%54(l)U=@<^JP$z-APhr9QG_^-kt7MyG)0zW z$nzXUQJ}7C7-P_N9Yh49Bds$KmQwwzJ?Q*P$GVz%u%X+JHBZ7zYstp4)V#Jw-8XMb ZU#44RDHtkhE9%oPMf%B9+$T}!$3HblSbP8g literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/GS/tbs_splashGS1.bmp b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/GS/tbs_splashGS1.bmp new file mode 100755 index 0000000000000000000000000000000000000000..1cd2a3b268cd99e175e7620cadcd7ac9d912fa53 GIT binary patch literal 3704 zcmbW3&r8%%5WuHJ!b;riFNl|>mKC(oS!}N%2;w1vFjyfEMF+Q=;vp6iymav-L?;g+ zT^BXHW+>gI*h80SUA~z3bElbk-}k=n`+eVCcYE-C-+6E5Gc)ha=yyjRzv%`UGc*s- z^MamXdM-c*%>WtFU%R3o*$Aheo*qz2K`xhrd_E83WzscO4cDdJT zh`7x@{GFa>7CBolPyDX#5x1{;`;Wam_3>gC6Wqo?;U>?^#Nn9g_v@F!ovh>O8q!=Z zGlr+7PnZ`enk2y*WMhd&YAYr4WP8&w_JGzp64E${#ykv7?LJFJ1VB<~;__TICoX#RHnD7&PJ>ZOfg}n{wn8jbr+lEzq<;@<%nzTP<T#w$sj*EVd&G5%O z9FmHSt}X2{N>QXq;*5?tBeN@aEj}H#z#SUKOJEv|bPk0$g*6+*3ZbolT26+;N8&}Y zvwyJ`BSCL^7#4MO_gF60AQ~e7K;Xd!X|U8!o5bcRvm&Sg=O;R zmCCusj%?1?08Gu1ZSDDZ;RkHU{cpS&XBsZ5AZL*}GO^tV2&I_TT_k?@BtX|PF6ZLQ zc%})RyVRc-XUB=??0VvTZ^we3#@%@6F0_2!m55dHe7wo!Z&+~c8@=;s@H{M?3w<_w NO*B(B(NUiP`~$NPiZ}oO literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/GS/tbs_splashGS2.bmp b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/GS/tbs_splashGS2.bmp new file mode 100755 index 0000000000000000000000000000000000000000..300d424b5aeed927e3dd043135192a3bdc46adf2 GIT binary patch literal 3704 zcmeH|&ubf35XYy&O@Abf>;45|SS3>B^c{Td!7RQO6@ei)7adIKMLxy8#pqInP!xRf z7K(!pC2;vf@xNDgAbkW+}(EbB^%WKF+scV%ykOOp+_7e9D!-_Fiw zzBBW7CG(qw^W=}KSksu_V@_gzKqFX*5_tZ_*gFrLC-+Xm>%cdM zuF!kVd`(J9c8o5Im^mqMT4a?|jyvx0h9Lv4G?et1v*H=Act)cHD~9Gc6kfFb@LZJ_>M!_7BhwX8mfn4meL+HLoS123aT`Yuw7INE(iZ1HzONr zNrBU~yP6y72{==gn4fE|qPebR1>D6#THK6T^f^a%z?H)K(!zERd2-eR=b)dc;7>c? zF1cyxFgN9i>N6Uh@r=$PXP^wUMsw3eGxvmNbpv#LseD@Y(SX0qR4&gZD~}5y$M%xW zxs(!@k*8vG017TkBa1qf2xp{$&7STnEZMLx#^Cl1kry@D-yBq{%}2KM$~EcVm)?VZ z^*)^NsP|~ICeId{vR~cwF_~Hlbc4IMUukgr?VTf@@AEwbVOozP;r?^$>+O<~TSOm= z)bW!diwO~J+NGxElSJoSx`t=L-jDgYh$GvyvWE{Jt_R%YQLgePhZqSaa&8hO>5S$` z>veRHH}sBjj==RpEVTiUI@Ltx67pM=JA*fxLJoU>kXEB}eH3?vs9C31z^O`GyOMv^9$`QSfB66^TXaB1T|>MBxU~qU#H7u z&DkANcNgh$1y0%*=f3bA{ZLDelz$94l!a2aaY1kDSdr%^I6#GfeiM>Gp17l861^XC zNtzLzDB3yXX26Xu!lIj(^v|!T9rSW`$UR)Pv3;F4RzzM|)=%=(LY(XCPw^+Vjwg4J z+3RKdj%6)3?^#xuXA;%M81!&C1{M4t#R21GdGdN+$0-iX?g?`z-OsU@^~KFIrd;fE ah@0mrcl=46_T{uM$9Ld(>;Le+5d8xqGmNVM literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/GS/tr.bmp b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/GS/tr.bmp new file mode 100755 index 0000000000000000000000000000000000000000..700d52e41d4fe77dd89edeaaf5951dbcc19cd0f2 GIT binary patch literal 232 zcmXYpu?@p83dIh7fWha$+W)#*rWM2nmsgS?d_AB$~5kVe2(CvOXJsuBA(T literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/GS/wifi.bmp b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/GFX/GS/wifi.bmp new file mode 100755 index 0000000000000000000000000000000000000000..cb4be505248a3dea34cf3eb929d87ddd9ddaf1cd GIT binary patch literal 232 zcmZWhu?+(u5c5mvA_b8VsM51RM4OTo7~qSr0xPfrE3g79(5FIdKn(xgpU$>q`|kbu z;g~PwMLbSyxUFC(Kko;Yagvl0N-1cqVT^&b7S1_%?-4>kj1eg%RCn>lgk=2h`-qc~=*6`{NF<0N*82 A5dZ)H literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/0.luac b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/0.luac new file mode 100755 index 0000000000000000000000000000000000000000..552ceac1c1b6882d9add06665df0bb896ff464b2 GIT binary patch literal 1559 zcmah}&1w`u5dLa*GrQ&o4JvCAq9W)6=)q%pR!Kt8i-PDO=)}zG!X_-)S<#c#Np=$i zPo4vM@I87G*??et0P!920)o}^m!DnDKy_DFeO=xCRn<&AX#0;q#bU9HiBDt0lf%e9 za*Cqf0w|DqSb?vBL87~(GBo|QR`A4cw1ZY`EQ{@~2vB+^!%dMu0bd#D!C+q*nq)&2 zE^)p#b`6dU@Jp~2Lq|SDiBD1DgWy44`7WtcIh;wAa%MrMT*#E`Dh~C=W)!YX{Yrf`Rs6SbZGM-7n%{1O$w|$% z;ANt%_~GmHBKQ5!Pc}8fwZz*BgJz<&SL?F3B!ON9;bZX^M70GsETahXl5^f#Bj&RrWBDm`j#;R1@r}e@^Lxs=y^S;XwK1#HC#>R7*El>8u zlz?10oQa>iR1fdmPVUrby{^~744BeUJNY091jh~%Ihl?|n~jOsQL=q73xzplpjjSc z6n;uUFEUeCMd*p_b0h_

    MWfRvt5_BZuXIHo@nq>Ks7R2ZN0quXa!3z1;zL-puiQ z3a^}AyF2XcP4?bV# dX+1G_xr5Qfm%u{*Fu^B-8)^ literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/11.luac b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/11.luac new file mode 100755 index 0000000000000000000000000000000000000000..1552c7423f7bc11c353468fd7b44394ab565d254 GIT binary patch literal 316 zcmZusJ8l9o6dVV~EEY6rRskeBuE9e>Ns*+9L_}+W5lAaQ3)DQCj;2M*hEW>_iQu%WGs#LTpSE#V*AX*KpuJbgv&0u}hT~I#Q&|yW|lv zdr^qsz|4O9_M4qyd&9y#f{ZZrno8lOBVN~7#G$at#5gwhb+7x7qrMn-_jt z!9-b06EIg=EExB~*q4lfi1v&8XYZWRAIEFO%M5$hjD%PjZvDQr#UXq=4?@XiGXYZR`-1~x7m@`Ya&0nw@Pb6fS~ j%h?L{Qo)N4BlRAx!PV=}i|O#;$XXm50wY-Tls$r34x;pVSXEM49k3%f4tNVovj1ekL5j zT)dw0(C|b0K)`3H3*wRpIVqx^ryK_&FA?<^)iJSj@Ye^=Kj!&uA(2(Z)R4&dp3Ob3 z@Qk*_rsH>EFS4k6{+ZD>>srzEUCv{ffQHws#&LJ?oi7+4@Vy@;_xx_7T~7+NT>s`4 zw)rF5SJ!)$-JQ5dlo@}u)o3Tey+=|CGD@j>HMigNC)AO)@%P(}DO#2ckm4ORXkKQP HVgLCr-CT6Z literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/dev.luac b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/dev.luac new file mode 100755 index 0000000000000000000000000000000000000000..2831dc2f708a3465b4e62f7a2c0c4cbf79fc4c7f GIT binary patch literal 2164 zcmZ`)U2hvj6umRMJI+VbI-+FVR25G}eWF$J!dqu&t4*N|E{Opnfsk7}iA5b7e}wSV z*>x2wjw(P12??Q6sUm-4?Sw?K!~;n1Qt}`A1CZd(&TLZDbfxuPpL_1PGk5OHT)f>W zEdjA@dkijoH#IRk0qH}ijpmyGq=MfG4)P6g)(K!I>7=}{<24)!cpzAc&XA;9+oD(yuvuy=+8`VsUFhA|w{eTVoqa0|#$BuRf5 z_4+x?;J_am}%Ko0;hV159<(XNAWI}=f4znJULqc({c+@Lt;Xy3}k zf#XV$gl51K8E|DzCO8)wj|`3;&0jG5F~fh|@NXKv!Qp&@&&udqmlJKnJO9rWPn%eS zN8?(PXEh$D{0WW6XAS>dlgr?IO6$w$`;%O^G)GK=7fpf-lSb3v^o$w*xY4|AG!Y-3 zLH?M=VbSPJq0SUmff*fx)3X`}M{eQT^{D3@ieD|_6Y#nOeu}VP_3q;1)Q^d-`UxAS?A|t<;7@`0};6qIRz- zo>E2wiORZsZ62M*FjfW+a~T8 zJ&nTBaS4vwAwKruymiJ~)mBTZ6obzbTgiCMawkPgiH&`Y&BoNHpbNXOtQFq6TWc7r zfjwqM!Ob+|&v+8#>s7dSzuv(HP%^)31V>pnEwK<3(Naw;s_k0IsMZ@5oTqBBvs%?3Y45xmDFgJdH1f%nW|;5>0N@Kk+alc&k1EOKy1_ zdLFTQA-8e>mK&23Y=9U?3{R$%_ZTnTpZPxz9-`J`yqndoRNKtd;raDfZxyu{)PtE% Nc~p%~^C;|P{6FlNmR0}& literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/fld.luac b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/fld.luac new file mode 100755 index 0000000000000000000000000000000000000000..90aae4cbf32531c972512b0ff497c08d0c1fd09e GIT binary patch literal 1900 zcma)7U2EJ%6g@N2YGo&;eF)jzx}ngP5b{z;pG!wtdmYCgF|ktud8xcBdxbY^cUKPN zDI>XEw}zHLNhp*;+g~DW6K!m8pbvQp{tx{DrSy(wWhd!N1=8HP_ssn`b7wTYxa-{l zGfmS*?Z>If_a;$1jGH802AEIdq=5VY5{zWaqR)=k>-+h1f@Ul9GEBXqL7@jiAd>?7 z3D|xjm@Ogq;b0^jlp_a?)Ik+PUIC3bdpKv)Eijrke4%jPI5+*ZO z;G6**@jX+?BR4MDN&IaVKgEeF@p}Y(kKYI_#bk7d>slK zxeS+vlG!4DM7BrDHe%0=YVw@+u_QBgh>>$>=y=ynC!RnwoWMy0{)$m9#BJ`6A5!i& z%m&pk$Df-K?Op0lQV!sS-=p-#W7RJTe-ycoGq#}i-lzyJZ8?0BMZ~=$iRT4a!v#3} zJS2y4G3MqhaX>ceQT8Nr7BnvRGyExw7Ew#650vk8@zj>rAu=Q1Z&g>C7}U=+CmdVS ze7j5Li33J0F^;`NxMRcO%P70cW)(5vb&aRIsQyv@q8H>Z)&9~K#EHZ)%M3eVgN)tG z{i&Y*jLw>+&R@^}s?ll9(@xto+vv1rywH2yw7d+IPr|$!bxpuQyaiMVe*XQ|Vv7yt^_eRj`kCy~~rv>XB zirL)mbo^$x>GjsVO<$X-CY80yGJLS(7rwtNe1BL7R|?_dLbxofGA--)n_lw)be+jv zt!xLamfzJO)`RfVYsj&^emmF*$5^~C^0-~^9wlQcQo^3bZTjIV$z$%WYM=eE7j!pu zt{Xv@ilU~%)dGbi_tD>7FEn|g6_dldK_p_}ckTmDAR@3-~5?}yYKXD!ie2c1?=k)V9f-JaJl zuNemS{rS+pr(-C6Uil;M(1?y2Ti segFRoAa>q93$GuO@m^A|zIl|!=Y_)L|2Z|j`KV1+1;70$-GBA}0{l0T9RL6T literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/main.luac b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/P/main.luac new file mode 100755 index 0000000000000000000000000000000000000000..91079fee3456003818ab365f3b671d8769d5dfed GIT binary patch literal 3954 zcma)9&u<&Y6@IfzEg9Kv9iT1cq(a=FaDgJQf%H}s%`7ET32h~6tN;R%z@@~MM3|CT zk}|53-cmN1NU?J$Tog5ov}pA&^c0}GR74drjP%m>5C}aa$l3S8XumhJq(sSXyTEd1 z-n@A~zj^cc;?>pS+oV#dREEy|>df#9!_<4!&?O8n5UDHp7Kpf|HATdrFg?($wuH_U z7AOu295)Np*(h-5)|MuEhS%0J}5-vA7Z=-+UB;Ev$c&q z;N9A0Bln{=bwv+tM~42makdiJaS2kD@EXJK^iJ}AU*nO4KtCf`^7DXX;Kw)pe>M;v* zOQoxmJ(Z7NGaR+x7kMaMvHW)S#yELt2wDgl*BUkQPGJnE!DlTwEtV{o8&fwLmLLps zi#Og5=4(NHkvws>Uc3oE%qUofWiqM-Vxt6Lu2!F)S`}UiubHh>NmeB0Kfvpd_dGr! z-aSn;qa&4#6h}yn7ivn!+L`c)Mnh7+rcg~%Oj&xeE??mx#q>{NlSWZ(BPt{F82{>x zT6uZFnl`sstR03%cuWc`_mSUMF#fSweNNE*4&HN`IID*^4|Fr;?~_tlcDWTs)W!QZ z#qOgxa!)ymhI6Vp+>D#UPG@7d*}1i;n(k4)cC6e@72ckOpfCAf>sg&*KO$<;4z=}| ziZL!~hx3pt`|c@n@5I`Ds1)rh)!Yx`=DzA|?1!CO1<3k2LXyYc0_ECy1n(VK_`2q{ zp!+OQ&tt?Vjaj)`EZ6FNh?(RjAp=}`02IoyL1_x3ix)52&5PwyDOiT@o&)kKna^J> zB!Me|*x>On0;_X&cO_UT&aL%PYvBwzz=1q)@7VJb%*PDP5wQY6K#4OPaR%iCZ!@87 zcu`EFT8=XdPjX5ubj5`#Kz`_^fDrbMEv_!#=#jBUlvHmt3>hKvFr6<472x@s7|yL! zD#2W1p;((OE(9Q0!%vz`l*ihn;V?ThTC<1DP~j@fOZqSNpWbu~M<3iO{D%qTFjpuzWe}BMzbPK8RUXZ0I_n zb3#Wx!^ojMB>G&0^AA4H-=TCk=J>HocU4Y(tbBJgWc4T_=Np*&F=~cN(f5?Y-*%YJ zIJs0lw?h-T&r?>AZfth19UXeV{vf3hw%i~bQFhy4!hC+E2RZ8ZiT$Ppj1 zl(GB~_}Tlf{)&%X+C>f9RcXjLU_@9s^!rHhJ%zhS<=n2qdbsPRoiW1Mb`IcAAyV$| z_{sV`r1=izFqc?UqW3at;lI~gz*4L|SY)r$ET!$bsqdB z)Bbc%>4^4Zt%|!%o@bq}8rFV;*QL{Ce7eBm0W+l((*|NZqBUi8hPx>EUzn1R=58F} zOoYe@vBZA_r9ign!7l62R=i`DV+E**yF;6odFvZ&K>js-Y$s_$X1Z)cKy$K zGTT{BWB)kr>NL+7n?2r($JGq&x^lkFSYsTK9W%!8Wy2@Lg6Y2TAATyAz5q?uBo@Zr zV=)o`kNfnZ*)O*5NIYg!m~o;X6I0M)+7p4n zVgEU*r#p;{Ge3+9R+;|f*;@tY3ZCTV}M61(u5D_D9IXg z&Y8Wq;zQ}1Mxq>LBlKbaQ-?dK! zQ~ClE?yLDX$>e=<%5IptWN;&`2?nOIKatZ!AMz(ErY=c(<#F(yj5^V$p#Mb}l=mI{ zUO+Wykv@!_j_7O95j(^PvBO%Mwd&C%w&qA^W#Zh%)gFPL6qD~O*=NeW7-O8e_QoW6 zIE?7!SL*foaxIt+Dgpi{)z)mEFAh{q$DhJz>dLg;S1ciF;)L{b&vs`YYyU7D-g$gd zNJ-xv*wlhM4f5BC_nhzxxKnF6m{n12G<0*67jCBRQ{ a8%6){c%{H|(&?Xq`fR6-D~99f&wl_^Rv1qJ literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/cuiBW.luac b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/cuiBW.luac new file mode 100755 index 0000000000000000000000000000000000000000..2b4d98c74e611589c321b7a681399449c5142931 GIT binary patch literal 3516 zcmZ`+TW=f36+Sb&TFP`}w}n&54T7ph5EMbt7DkFbw1sDvZ}}4EsFG|Pfg;t`gfE|$&}%w$qy(Dv~PX%A1Ki8%YWFYeaQAy-f3PU-g zLpiNUi(JqfA<;B=gpibkLt4m&au0mhLoLiaAIe;KBqEQ(9gnmmF_0w1o=34)rH?(L zQKicG3Cq7P^BhMHeG26SWzwU4sVT117~>zq>`YKNbQq$*i-93vMzkuRm-6*-d;oj^ znI#%U$q_v3U7Wj90;cqBjCgk3HlDL>MpvRRnxt%Jl8)Nehkg4nJ=>q8Q8gj)lqjC4 zQ})!OQO($VDlu}8+U()%38NqE^^WaFr{Kd=Mz?)=#NJ~vfRW__bHr~B6G9>5TZe(1 z%G{xU9WD0@p3aeTg4w()jy#OafD^6EC`4i~-}*MBrm zi;o@!JR%1slKhL~@#4A3MI_)5xhP*VxtL4LE3Z-2(3D$Q}4|AFLG(|4XsqE7<9QtK>D#VF=)H_JIbDX z1gJ!~5iGp$l4hIv9jhq6kVw6oXFmkX8mm$RQn)N2`VhH16X*_IGt8zOPZXs=_U9 zmyI|}g%QTRC;F7@fgROacA){w35jwJn1yclqxRO#*^TaYkw9=IGry1Q;dYCxsBu;LMl0h;Zq&y4aO%NX2m=q;yKue0KPkXehDq@7QztVd`E>XV`9ccs}b7_T&Psne(S>Y3uWt!F4#IN?jmLwyYw)qxVw1AB(sUs zcJqGHynAz8+ZUZUa>UOa!P$!{!QZ#zLi8J~U*bJmN6s_uR%f=cwa0S=JK#D=YKYYp zvs?ZMLjrHFVw~3JHPh^Q6BRm#^hXLn%mLO9R4)F-I3@H5MN$&T3cThN65)l`m}xOW zseDO8@j{WIPeX&}lzp6QImW*ZTf$t@e@N<49@0TL^iD_6L*ncx_miE>FGsTfTuASe zJo5gn{`UtXSM81EnzQqYoXR8loWw5gKUdX1@)+d%IIAXOj7j)Z_A%~eHSg>EDV>Q( zOv%_^l(G0uB>l5P<^NOcc#mOZ9tLzh!)rAQ=uM1}`zMxH%415hDoup~GI3SlrFRDO z8Ij=Z>=SdgKKRRMkibvRL;y@hXnLWvFfYi$rz`3ieCLgQyrOw7$MYF|U|z!0ByZ+5Hy$Z{mFiNy{19dw6@CWEWgYz3Yfy+LmJ43@#JHu-z^$eC`+K z>Mfcz7tLWx6BPyU@{-{*J=e4Y^y2C=x+vp2kjl5WZDfYr=%f#BA(sP*_51eF&1UbG zExXP3RzWoJ1Yy>VPPe=54m;;GpL9gEAn;Hy5+67ynl+#B(9O{)eauZ=^E;>6YMJI% znBg8AgH?;W-MtOas|)V~?ET*CPw;)+M-X?F5+jX%GaxZN5V}9$LP0?gXx_)gG*aju z!v5RC0b>gf^*X6XnCs-bfYq*DS-VzmT)7%9tS#1-uG;6CGs6hs_Z5F^_(`_~s?F3q IV-COn1Fofl{Qv*} literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/cuiC.luac b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/cuiC.luac new file mode 100755 index 0000000000000000000000000000000000000000..be2c6a73c702b78b45088cf556fe967b8a78e98a GIT binary patch literal 5699 zcmai2UvnJA5%1YO?MgC|O+q+99FuEx$@4Axx7W7sg(Uj1PXp1jg>QpzQ;6s5kgU^@xRc4ckl_jO zB(m*H=I1ajhjF=7_S;O>g$z%KCmHunCiBNIZVcnbQrWwitP5F}Fpg5$-F+F>!bdMaaD?T4xKlkr@j1wly4+?i6fI6Z?E1Cd0b^G{x;{tt)LM4y~W(M^v~F*oWI(CI_Yz3eRQ&|`yZXexkvwFwo$VL zP5SMRUCdGQnYRAy`moLBb%v-nv@ZAcw`xjoLanHmIeurd>dL4E>;d z{<*?)mo_U`YtL@1Una+4=3?oMACr><*;uO@VlnWO38IF~w4P6Sm-l%;NqFkqKJSIp zxwHGc7gJtQ>Zuk>#njr{nO5mcs&&z5RcqHtPrq41I1CBmwb?{Zt;!@Ig@m}cm2#d) ziI=4_fyH55xn65vg*;>(F`#7Z{a1AK7iNn~vu~D+9HClE<&PL=iluWVI%nsWjnb8R z^-7}j$6rcqWNBgE7~#+^&IYHT%AwuYj5dwAOK`mkw}B*T~pYZ<)|uuQaY@%BxZbz<+f<-2r+!86RCp4gD}B z-b{##*b!cmKR?tFqRrU#~HP zoILslp3DA{ce%FSny!q7@Aca+$ z>n2)ev*znt;PYVt5?-$-M}*ztxL&Tcf=2W0N@K2eYm4|WzJzBM57BrXe-24ZpHL{rsjp7a7ttre5uZ}oo23q7 zw;fBnAAU;rkeC<3&{~no;zarb*x65H4A-?rt+Hu&8i5vg1#(!#dGCb;J&R>S@R`&n z2sxNlZ<*$bX>L$*Oy^`~0hyCCZFMn~-G3!fLYV>qe)Id$_9qZspA6)&b2d9WWXAc0V{ONSPSpW8A;TMcHmP9JA;>A z+#7pQS2!N#5(R6@D_LHM7t%0CUxqt$v|Xff7yfp8xlXr4D=2jD!3OW*3?XiO8`jq^ zHJ4CN?(Zp|HH<}e>71mj-RjL5?Fgo>#VNfD%9J13ha%89F&mSm|EQ}sa z#oAtW(4Laoutu3aJxH2C<=W-6kU?c?Fr_XGCszZ-|QPlp26zi(ZX|M|7HtGbz8 zZHwWI5(M_|Xu0fBg!xt^u6$LXAVPToA9{cY^YSv6`FoyrD32VQ`hQ+>-^%(k3VkA# zC!zLj6*F55J5Gp>jX&HtIw`ccR!fq=_s!OF0jV$2q`GJIkwoi)p=;NzbR7Vi8tR={c7lKG&i7q*v9_~l5IRTO}vF> zASLn!a#1X;KzQ_;P}VkHD4z_K2tto8U=ulLUM%P+a>*WrVlpCSYOHC+o9=pDazo!b z4wU7kP)>{wKi~yn*Sf=XB5Xx~_Y)q8e1uDo=kp3y8Ep{utPkRz*oEE~v9kBXjy;Av zFl7~Ue-~~0OK-=1FBUfEGR_-p3!ij4ZoGr4tw)^S0#Tr8w9}$wO;Y$ywjK{3pS*~; z*4hqt0mNN;AGYwaOdp_S{z4pMy^ukV=NZ6L(@L%Tz9`#f&hXtqRHCmCvpm^qph{Q+ z@^?fQuQ~01f%xo2vG__t4{_3Cm_`hDFm^IhcNu6X6tW_XclpK@RBODmS1T=0%(}|O zJ`>dpDX8x>V$#<)%F4E9rIs*kT?|R!XCS;;$D4`;?v2E71$G#5mm1CHW{T0QvKp5O z!d^~^H%v~vnW{dL=1AjBQZF=`sr)4g<3JeyP*x@8bXWhqgZ~fkSTx2(Ne4+}Mlp6Q zd}u|Ttfc5iNI((rLc;KfZ~K^RVLrfe40#E-aj~NG;^k1}agY^Ia literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/cuiGS.luac b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/cuiGS.luac new file mode 100755 index 0000000000000000000000000000000000000000..aa9cec82f96b8c7e19c6662635fbaa60e2105317 GIT binary patch literal 4050 zcmZ`+UvC^q5wD&d&pNi80Lq;mLPUt*gpe+Q6TI*MJ^N?IPS#G?jYA-coV9lxuWs!% z_9k(j=yAdtgn$QLxI0ebfqage*|pbE2)Q?2P{i>K?h7FCtM2Kw_u?CArmMTE>wi`E zoUc5nS4p_8J5J~RJT>w439^rv`47N%h{QFFSBX?tN=is!MbXDni5Os-V44zz_*DGV z=>vDyS3=L@)Jao8!g0tiIHVkq?&8yTD99D*X^v=6ERy%Mh&|-#J!v0%z|(QD$EA4J zrF}XT(<4&(-_+9(d_`&d*JM!z0Zo<&PHr8jv_m;-b zXyDHib!PmiJ|cZG!FG=w*)_^zsLzvG6K5++c*pc+eb|2J4Z3db8qGg>;_--hj)nd| zMq|Zuj#UiX3ls3qvQv79X)Mo(Tas{wNf$=$WtKzb+yD zzll2gF~Qbxe3q7)8;xj(yeUk!qt1HNsdwtjt%%03famj1G5{M~%p1`|@;m@r5y%Y) zU~$fFD9LQYI~gFC?aEmTd|PT3<*HvR8y3O!Y7ro75mw8;SSpnRf3b3tyi-^K|DYMQ z8sstSjr#gfyK&3$^Pg&5;d-THsKVg(3K>n*J*ibMm2Vl`TC=g1;qKkD97t-1)aEO- zxnE{lRp-K4=)iU#ZTYNj*sVgLOh9CrT$A2u5Z9tsw9>f0?TsSKCapnu6& zT#)2=NlJMtkS`rglcLXiX@LS!1P{uweu3;$sQR=>iq1(DPg7p$wWLpXfS*=0oabrZ z&|91oDu9eo?o(Io(_T^%GfL5P;-~=gFu_Y`{mh4HkFABYPv3wY(>T6ZkMmj<_K}1> z3B_>!ASuu*bs#*HW&qpbbc_;|@7t+x?!rDT8sRWu6{0Z>=Et z*`X_~sJ>+)*Vw8*y3yQOuWwRTaUkZ%)oz$^9^*O8Q3_YW+Lg}o_R{udtG>M!)_!n# zd41Ec)=9xK8r2|Uq>9j;pT9*Wy(-1h(yUP+t67$F1L@2?1ZiGE(;$yecudNTW+%#u z6|&dfOf=rjNu$MbL*a#OE1oD5PnS(iW(F9t1`1MJiDG8ywqOC0!yiK5`@$nJFX^@1 zw=mc=43wl9;HH(MJiz%x(p$j8X}g>@LJB(1cyg6c87yo;DAtpGNA=Y)TKV{$)XrQ_AAvFBF&L6aQONzeVX>@Ob^{< zmvHGMm&5Wkg8tTT-09$f_gd|`ZS{pel7=UdYA+~$%6;F`zXerm;tNWBj_L9$ml)zQ1FBc@a_=muSit z;E84t?>nafpY}tR{YNc33I8;pS@`I=@IX@(ieBS4NdInRKE17;!*{u%k8dYDcZTzs z$NUNDFE!XH|rdM!b@nhV&yNKqVlwzQ8^duyv6QQ~Z zR}3FBfEkYtHIO(+g!vif$g!zMdYyD!=)Uq*kd<%UT)H(^y}9UDmgdU~i{_ZLW@sjS RiRXt6KdEhShv#?n`9G+`0kZ%A literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/main.luac b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/main.luac new file mode 100755 index 0000000000000000000000000000000000000000..90f068b699660df6e4865cd208d59b00e6843282 GIT binary patch literal 4838 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 literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/popBW13.luac b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/popBW13.luac new file mode 100755 index 0000000000000000000000000000000000000000..be9dc3ce6f3a7154e82b0459e5bb9352a5972ea7 GIT binary patch literal 2490 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 literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/popC13.luac b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/popC13.luac new file mode 100755 index 0000000000000000000000000000000000000000..6ad86392f10ba2f38fcf653303863b992229d3ae GIT binary patch literal 2651 zcmb_eTW=dh6h3Fx8K;3%1yv-pT+9oiLOfI|;-zY5Y_A=sNs1h|N?W4HjVF;Mdjqjk z+9zg#+6YvxkEs7;H^H`A)cyg~H~QXx0B2@)+uSHcVx`?VbMAZg%y;(W+*Z8`n8{?u z;q_l8Cr(U2dg1s2k?#WF1zKkTbWaF~Kodv9U7_(ijqp3|=mIGb{uF@UMmn^-n0Uw% z9(-kaAp;qwJpHxhNe^V`rFuQ*FSE;5_nqw?-Z!%v!Z6Cf2nH|`16>mJfUNY{lJ=RAqvUW>Iyr_M$|Nt#0m%neE@KYY61l`I^hw^AzDM?+u@7Yy zeof?In1#nkwTZ$X9usdi!XHidM<3V z+x4K4TU0?8I3)$C0to&x?J7a;)6I?bP1WfDKf23#Jy=z(RC!$R<0LC=1Z&OBcD>u& z2y%f^4b=et)_$X>DJANulVgIvy5C91XFE?OT8IIupxNB^LYynm<JorQjO!7mBS(H#ifJ3Oszt|J285MP0ZHoAN`e|`I95o3(GJhpT_pk9ADIL zOnz|&dz5QGmR=w8UO5aIVGPr#;WE)ZIrIO-3TF*++?>5rPGzk{SYYfsC3p50YqAE(ktZ0lsAf^KwMv=>sLWX|HCrv!sH)X& zJy<7PO$?@mRV`ntxF7}R+iVT+77StVwp{hEoKB67iNu;tkh~OkiAKic7`;#>I9XdGp3>zY2dh*Zzf>dsozkf31BiKuwyjBJ8Kpj~trMQj}*?fO}$0r#Yba zLOD#s4#h%!cn)Cs_w`QC$#>BHf6#jkqaFG``3)Zl4d$QV6{??|jQ-`?%H>M6c1g~y zxVEq@`TYJ*yW`3Eq(#R?LeRW5r^XyC!?7Qin_zq5(62=50#Zww#O5D`0{e|-mxi9r i!ycKJh~H2x)m4?QYSRY5993!-dkig^JPhLi6aEH-hB{gR literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/popC8.luac b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/popC8.luac new file mode 100755 index 0000000000000000000000000000000000000000..c4d48e3f9c22ba45f44c542894741f5364e0aef1 GIT binary patch literal 2797 zcmb7G-A@!(6hG(AatBb1ZQ7!xq%^Hfn#Lw3HhnRAXLn~eAWID(K?$KN3`nvdfz|5U zolRwVK~0+Ur7`J?|3Y6ov+T!{t?8@rfACM}@6N{xEHUXMv-jM4K7Qw(ui25w`{ilk zOeWJ$=YJg<{B)4ghfl46{F;cbW1S$d6$>yAJ9E;@w%+>VV$?qvIb~uZ=!@ z2suOlhW_5OkgZW)a}hGdFcd{@0Jjn0cF+8#SV(XSz%BBD@PT{SE;lUtjm06Xre)V~ zmxhzEYq`uwHpXRL^;I|@v z$dbBua3pa}QnAsbqG)9xlQA}&hNOiA50Ru70&&;1Coga|p~ce|wP^oz2^shtc^E@p zWIukEmew~{yoV%*u-Wmd8(y_sE#IklBw>O!NoKHY-PNyyeYE9~A@Gc;NPA-)W^C+ts@vTcbUBgyqpD(fwVDu=ONii3Lg=4T)~)p9wu;p6xm9Vi_lK#fF2{vPqg9|X?__5+{Yw_ zLL~}}a&tnl>xUvLU(Q;KOgyTN+9)M9_Ek2r@waY7HqM|OdTfLz+b}*De1qMia~zj) zk?-%-adId6n(DA`Q4h{>ogl^0{0#9aX&uFCUuU^ZP0>+J?&v1VFyCv-_>7q`pm(VPsQK>n3_y%Ug?1M0~04g zo?6HR0k=TAz%Ti}*hRkTm^W?YD;g)xf%P0d7~Ud$C;+FZ(2A-}3y}|KMJ;F{!F05w zU0~LOoXG|CvF5YB5--U&dEue3AY3NPXPa8AEi=Y;og%e?zmGB127bp0Em;eNZN^GA zmw5wx!^0TGJ>O&7FA68L=`m9#Pe*agXNWE;ri@#Bzp^Ad1uOtxKu)*;M=OwJ!5A$up2MnD5ZK z-0xG>ACw$O#AdyRm?;LOXE1G+H_*st4CUOZSr_xeG_p5G@)TAycYb|m3++x4MbI5P zDjo~jA1CvPLMesq%9P^!j$Is!2gOWja%TLto5<-IB?SyFkThS&#j(7%TE1VY8W1vm zqxK}*+|L8doIza}zm7Z?u=^G-ll%rtRK+TwBBn%Kb^SU$iz-4XuA{r!sE&>bxPuAw zTE$%u!pLV%(h&~RKd4000TY`1t_>ZcVjXT{?N9^WVY)d=*r?nB=%Nz{^Wl7h?&|XJ z@qUiIv12O7T&y<_X~a#T>{Pw2G?Z_zr8N|fb9@=@#_Ahp-kW@ykBE6pC^^bdwC(;} z1a%d!Me+)k1H8t!dGawCNJ~f2IYS4a{eYa1Xw)Gd4o+#w&LKJHsGQHSoEp$FOn4-q z)1X1W@rw)_bQ*t3j4kOL;LnVqH4Q5X`CM$v$V1NNycqPGdtKcl@)l(BvAljKZ?h{# zaNS{r>6X|iU@bBx54XgiMh>Dimkbz|B^ryMtBPGeK4=3nx|V{nI>?Z!;pAm~kZWxZph)$>^Q(C>{^&h5U BHkbea literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/popGS10.luac b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/popGS10.luac new file mode 100755 index 0000000000000000000000000000000000000000..15a9a53f3abc01cdb766c55c5a99011780d3bd12 GIT binary patch literal 4489 zcmaJ_&u<&Y6@IfzEg89T(x5gi2}X2qgCOXkXf6TL)-$AbY0083P(P~JRR!AG$Z93U zu;|w5X@+vC*g<)7bTD7lA?eT44&MC>-&St8ltoD!0dNIKw>%_dwaVw7@5OfkBx zh`vHM6&{gCOtd}vRo84KO2FJsun`-?a=?=ND;^!v+L=vKX4gU9c|@2i+cP{NmXt6X!F0 zzVq4UlWhtv4O75i(Y#liyPyxy--uh)TeiC%Z|Sa{_QOXT7NQ+qf71K|1wSxi?bmX1 z>nTQS97f-czt|@dMj7T z$kR;DJcr-2Jf6jaE5lo!)bTsKYFmCkJDTA8R+w?25qgyewkfCNa-4a!B{D zgI;j2sDyV}2ya!A*8z5)1nU9Y=Lze}gbm}yfIE zu`*{e$}tHoVfXHH?69OEd8#@09A{B-x`KIJON7BW`6#bE*SoI^uqi*EZLQn#vm4nq zUbcz8+j_FOvtdEIPajYgE2tW}SjAN*0FM#WQKI$Z;fM4*^ZX2wUl?iW78>V}c2tLA znZF|gEMSoY<&)*Z3o^X0DZ7pZ=R`C?a<80ta)K)0@MK4C0@}*oodqV*gFL z>;Nssurm3;Q>QF3j#sHn7mxd9Xj})Ipo740Q##mOY^+c|b-=zaxWBK2{})yDVHf9< zq$tJavee7|3tb%S5*I2EC2BU9ds*^EW6k+ko9OZPgw! zP22p@9w-kVHJ{ktgD+b7LBM&SH%(aRJ-4yPHzB-mJGD}u*hHn@;g8!+A&FFiij8$= zsLqiRbe)r*>k~!_q$$VBC=W|hIjP4HpiupD{qT5Mg_EcBY4zgqsM661{WJ+#fPV$* z(QD-BYvkx_7Nj9XETS<~$w=7tI+tDmOp%9FM3$Kll}rXnk3wuu2fBS8A`DS&Il`An zG6Zjg5UKH3m_w0LSwyfAe5fHbBC0Fpt%5hg*c!)=zNY(R^H}hb&1vumS<33oG)v(x zOH1(YUsMy)tBJ3@4*czb@6Rdo55U(kVF@g3nl*J;m7Kxq(E2fKn$X&Mh}_pluRf)t z&8@9?y@9&~&S~6HvV13=b>7c9Q@?eRS#E9tVM8p04*rf)ewcN>-*l20ZZ1N#M)>L4 zbQp=HxkWds;?dJ-EogLtcaUFQny8KX#7<^De^$r=GratNm4$TTL6uY8uxmzu)q7q1q?*Xst zV6JcTLIiBBk`!v)v{3IJ7T%yfE~m;H6-<@+NIv(Dgg*)DJ=#7$9MojY@z=s8ra&Pu z$?fKh#8_g|tEY$F8_bizH$C53Dfe`O{E)H)Irnfh<@Hnz)_ZJ9s);*&T;^3cfGWEv zYt*0wZ#P?yY#wempJgFMsKo`Kwl{ZNkmYxmp-2M4#Ld~LA-V@$8$T@f-_i6CBi9m% z>);jH;|cX~Dd{H(`wqG?-5AJlo*5Z`E0+ZV=dt2m5$9WkehuqZOYPcCaOUZY$uMS2 z32#!bxug^FBU0_$HpjW?cv0oAF!II|^7M7pVU7~fF7t`OJ5}&Lp1=3OaSd@`<3I9{z2U@q33%ug8)1_4dpRkAYnoQ{o?@MQayr{z^pjUO z0GP$`0DR#9$HP;&k&F*(kYqZ|M?J{F%{|zOviS_wIrpDy+ZL;HW)krfyo*2K^5WX^ zOk;6L%&ld0wE*$|O(m03`Hk}UChiVQCyRLkT_Kwk=&U?JjxSo9X7i}mC1_zIT;FWj z8_ur&)D5)h+R}{umc>UZK89ynF}`h2v)u_-z@|T%hiz!-S|gl?>dZInX@>bmh|AY$ t1}(J=*@TY&dB(Q=;tyau`;0zKV%)I*4bby+U)bngczmEF>oR`o`5%a{jFbQX literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/popGS13.luac b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/popGS13.luac new file mode 100755 index 0000000000000000000000000000000000000000..358899100010ae3a67b279d472fb7e0479e24f2d GIT binary patch literal 2192 zcma)7Pj4GV6#vbxGj;++1r<3^L481_MIdp8LuYNTQzveMY$pn>D$9*0k&(Rydjsi- z*#I__BcdmI?DyEUi!D;|2~Z^te1Lv~@@CiT#7T-+Y4^>%|2yyZ-dry2*UMmuL?VT^ zez~yp_7dWU@sDVE6<}*bR{_-*0zMRR6dno1epU=mX-5w%i{T#u_P|$O$P0+aj42P_ zg*?xI6~sLCeaN#+Se_Z{^&@|Y6Sis)+0OBPTvhNM>%5fu>9Noh4{DUl70T~T;`0=J zYPF`*kO$xwXw#E!Te@|(ey=6L6U1rv_OL)Sd^jgkmi5Mc@ONl=$KEbh?`CqXPETg; z*y~lhd^cks$en#x##Ya7gr0MqcDwF0G8O5#;7q%V-3tCH?TSw3%Wh|{D|qTp|wcGA;%O*6VA_@b7F zk+o;dZ*=S5L?^Z<-8Ff?2ZOjenfK`h7Js0R{x3@aeI+pP1@mdoS%9Qv83m#%RN6-h zMnI_8@)i1ifB~{>AhK*(Wm#H4{U#LBge1>5Fh&j&F=9-kf)L;ZCIXfK&*t$=z&w^y z<%0w~wt&D|06(g&)i3?8l6qmLC>gZXa;+e?pE4V^)&#OQIjp|FEn%2v*E7 zyGeQ8#GmZYb4J%&B;7wSrWg}8G$+=O;xKc-R!yIQ zJ*4%?m}l&*rSt8*>^XJ;IK@e_tdR^>jU%vM#~>cnFlnttwe*gRB(-nSThsD~T0Zfg zve=I56L#9i8Uq?Z!_kx9!A)9au7M|s_S|mM*^3&jw|_4ZepW%)XFKgi=@z&~+4V?e zw?$ag_Ps*nu~N0FF=!!p*i~WIwsuHUV^gEH&}_A2qbzsay0b@pJw^Fd5iK%bOcz)c zVPLy#RqR;nVl4b%y7;>pbBZGD?3J?pc`a`jtj*FanN+NEO@zIy(>P8bj8fPr=Vnpf zi#krLRpmk4i0XFi=0g#lpi^(tSkhCga;sD+-nOG-%!QLDS{K*1>~aOufBZDEYdX7~ z=)|%crF<;Ri_;VC`Hjd<(XZ(V(3M{mT8(wG)q{_AdU-4;=A&e&E_s@nQEi%Z^4d$U z{)l%;bWqF#w{O7PY?u!u?)*dD=0>n z$Yl#iD?`o6)l^QGN+ide{0N>G`FJO3HPxxj1wO#x5Wt0D-O$4O0qi jH<~Wp3nA22Hfg-QLi~noNmmwK*{03QA)+yJ1~>Q@H2TSb literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/popGS8.luac b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/UI/popGS8.luac new file mode 100755 index 0000000000000000000000000000000000000000..9128188d6ae89e49417baa5c4cf617f4c3c69989 GIT binary patch literal 2307 zcmZ`)TW=dh6h3FxolVjfi3(~;h#Co6E&`sBsOXICUCU{jR?bC%s#qr8w2_=NiZ{@{ z%|y_IN=QgZy!ElagI(w1M-Z65ZL4%LS=RjU3;#-(!h|E)^C?SI#L(i4rnM7GjOsQh>ttLO!!sn8>qDrC&q2Zn_ ziAhaVObSM(CGxo}wDxEhI=iX%c5T^9Q;(KXD~|U{UJv{p7(Ki1y>$ABA>{)7o7%nK zQ}(8;;VNXRX)3DRh2L)9?{Lo^hrvc{n?JN8k^(y$Bny}}A8nHKK-~C-n zL%OJ0ggTq2lo1j#z6!`UAx+|pMZ-oWM-@+m|fs_K9&{G5?`wzc9u?2gDJKAr2QYK(qi!6Q@%Ay& zUIC8Y!OGohxYlk*t$MT)<7R=y98TY>oVcD7W^u4Hqojm#P}lQ8E^db6pADpj;x*L= ze6=2Y+o%MgUYuRXwbW4S6D8lu<2jC`?osS!OQqId%mz@?FL18U~a66jO$XgsLW? zdsr9E7%=CMlmKZ8NK?FP6d!@^sf2q;!Y@^ZI#+2QpdGn3=_>ATviw^@JY&cXZJ3mH z6U|4Md(>sgs8+3nZJN*x69`exApJVOpI7GS2=p$+*(INBWK z=+(^}Qc!XfTGGQzecd&hg4;s(a-YE9e#-mH;qPNmUtxG8Z(!QTaIr0;e-sTFsTIBF z=ma!cj!w~#G;uLGZzSsza(YzGawez7jZAYM3Fvfb!gha_X_qeGsdBcY-Y4HW+E7tR z$d@x)P9AbTSHy%p*c78*;n`of}~ZOjGB3nbkub9%Q?5AK3>x^L&V@apCnQ+MkD?(MWGx0{(7X#OQ# zt3T;3uOxT%cTq*|WdJ6E)>`cF?5?&C5oe&&{1L_IwyKM@tk7CfJBp)P9Ic-{Ua(xl h8JuDF>bV)%IxCjFlFo1IM8!EA(QA<8pmi2r{{s?ztyBO2 literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/setup.luac b/SCRIPTS/TOOLS/TBSAGENTLITE/SETUP/setup.luac new file mode 100755 index 0000000000000000000000000000000000000000..d87b832807f9e7f1e86481b4932d9a0b6e1f0ef4 GIT binary patch literal 644 zcmZWmO;5r=5Pb`EK@#+0G|0i5SI*vAh!7ABK{%7rHiQIff+ccX)D%CCo;~_M%7^hE zc=NwF(-sq9lkLpBd2c$ildq4>CWNMGY2@Crnd1!BN2JckJq5%C*&3jHT}J>#h{C(B zgdgab!4NftY3M8V)qR8)*?*X+1?E8_(kR8)qmL5n!MQKBgqSDsjj0IhMjU^OV`v*3 zJpFO7qB;wtrb`%e4KwkHnQ@*xoQgz6tnfoMUjjU@`5!!w!fS`RI-z-vD3)pZbUJ<4 zb|#Qn3T_>5=y;}Qb_Wh*k`TKEsgWI6Hsme?BlE81OuRVVgxrhKrek@g+hZjw>}DDA zAkLNhgMnjjFe>e8M6H*vqGsyVVxc$gL++E#x*?6_^~_P%?8WUguA~26HXu{PX%vQL z-;FS?#uK%xG00t%7-c4*7FVXVfSP6#k2V^+5ESO<=IJJ|Bgy!nP3yV{$X{=tUyhaG T)2zE^eaK-R7y5S?*aGnj)(U{E literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/TBSAGENTLITE/loader.luac b/SCRIPTS/TOOLS/TBSAGENTLITE/loader.luac new file mode 100755 index 0000000000000000000000000000000000000000..64651f5a39d4045f881d48986448677a4b2743f5 GIT binary patch literal 2570 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 aT$EW*!om(>rxvj=hg5)^gyc>j4+H@6QYzvA literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/WizardLoader.lua b/SCRIPTS/TOOLS/WizardLoader.lua new file mode 100755 index 0000000..686b1b2 --- /dev/null +++ b/SCRIPTS/TOOLS/WizardLoader.lua @@ -0,0 +1,11 @@ +local toolName = "TNS|Wizard Loader|TNE" + +local function init() +end + +local function run(event) + chdir("/SCRIPTS/WIZARD") + return "/SCRIPTS/WIZARD/wizard.lua" +end + +return {init = init, run = run} diff --git a/SCRIPTS/TOOLS/WizardLoader.luac b/SCRIPTS/TOOLS/WizardLoader.luac new file mode 100755 index 0000000000000000000000000000000000000000..7b4780ab106bfceeb605b402544be59f69220c2e GIT binary patch literal 296 zcmb34DNPJ!ketlRCB?-80u^Bl3=9rCof&}?6fm-I09hb`93Wofz`$_2fq~(aLqo%< z28PCGKq)nb1_oxJ9E(_pUvN!$W>sQQih@smVoGXJO^BZ>3u|UxW(f;RQE48?6h;On zAPLa`VlXg)L_ucY1Z+SVP9SDvU>T775E85(?iuA6^v0H8T5O#lD@ literal 0 HcmV?d00001 diff --git a/SCRIPTS/TOOLS/elrsV3.lua b/SCRIPTS/TOOLS/elrsV3.lua new file mode 100755 index 0000000..649a3e0 --- /dev/null +++ b/SCRIPTS/TOOLS/elrsV3.lua @@ -0,0 +1,945 @@ +-- TNS|ExpressLRS|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. # +---- # # +---- ######################################################################### +local deviceId = 0xEE +local handsetId = 0xEF +local deviceName = "" +local lineIndex = 1 +local pageOffset = 0 +local edit = nil +local charIndex = 1 +local fieldPopup +local fieldTimeout = 0 +local loadQ = {} +local fieldChunk = 0 +local fieldData = {} +local fields = {} +local devices = {} +local goodBadPkt = "?/??? ?" +local elrsFlags = 0 +local elrsFlagsInfo = "" +local fields_count = 0 +local backButtonId = 2 +local exitButtonId = 3 +local devicesRefreshTimeout = 50 +local folderAccess = nil +local commandRunningIndicator = 1 +local expectChunksRemain = -1 +local deviceIsELRS_TX = nil +local linkstatTimeout = 100 +local titleShowWarn = nil +local titleShowWarnTimeout = 100 +local exitscript = 0 + +local COL2 +local maxLineIndex +local textXoffset +local textYoffset +local textSize +local byteToStr + +local function allocateFields() + fields = {} + for i=1, fields_count + 2 + #devices do + fields[i] = { } + end + backButtonId = fields_count + 2 + #devices + fields[backButtonId] = {name="----BACK----", parent = 255, type=14} + if folderAccess ~= nil then + fields[backButtonId].parent = folderAccess + end + exitButtonId = backButtonId + 1 + fields[exitButtonId] = {id = exitButtonId, name="----EXIT----", type=17} +end + +local function reloadAllField() + fieldChunk = 0 + fieldData = {} + -- loadQ is actually a stack + loadQ = {} + for fieldId = fields_count, 1, -1 do + loadQ[#loadQ+1] = fieldId + end +end + +local function getField(line) + local counter = 1 + for i = 1, #fields do + local field = fields[i] + if folderAccess == field.parent and not field.hidden then + if counter < line then + counter = counter + 1 + else + return field + end + end + end +end + +local function constrain(x, low, high) + if x < low then + return low + elseif x > high then + return high + end + return x +end + +-- Change display attribute to current field +local function incrField(step) + local field = getField(lineIndex) + local min, max = 0, 0 + if ((field.type <= 5) or (field.type == 8)) then + min = field.min or 0 + max = field.max or 0 + step = field.step * step + elseif field.type == 9 then + min = 0 + max = #field.values - 1 + end + field.value = constrain(field.value + step, min, max) +end + +-- Select the next or previous editable field +local function selectField(step) + local newLineIndex = lineIndex + local field + repeat + newLineIndex = newLineIndex + step + if newLineIndex <= 0 then + newLineIndex = #fields + elseif newLineIndex == 1 + #fields then + newLineIndex = 1 + pageOffset = 0 + end + field = getField(newLineIndex) + until newLineIndex == lineIndex or (field and field.name) + lineIndex = newLineIndex + if lineIndex > maxLineIndex + pageOffset then + pageOffset = lineIndex - maxLineIndex + elseif lineIndex <= pageOffset then + pageOffset = lineIndex - 1 + end +end + +local function fieldGetSelectOpts(data, offset, last) + if last then + while data[offset] ~= 0 do + offset = offset + 1 + end + return last, offset + 1 + end + + -- Split a table of byte values (string) with ; separator into a table + local r = {} + local opt = '' + local b = data[offset] + while b ~= 0 do + if b == 59 then -- ';' + r[#r+1] = opt + opt = '' + else + opt = opt .. byteToStr(b) + end + offset = offset + 1 + b = data[offset] + end + + r[#r+1] = opt + opt = nil + return r, offset + 1, collectgarbage("collect") +end + +local function fieldGetString(data, offset, last) + if last then + return last, offset + #last + 1 + end + + local result = "" + while data[offset] ~= 0 do + result = result .. byteToStr(data[offset]) + offset = offset + 1 + end + + return result, offset + 1, collectgarbage("collect") +end + +local function getDevice(name) + for i=1, #devices do + if devices[i].name == name then + return devices[i] + end + end +end + +local function fieldGetValue(data, offset, size) + local result = 0 + for i=0, size-1 do + result = bit32.lshift(result, 8) + data[offset + i] + end + return result +end + +local function fieldUnsignedLoad(field, data, offset, size) + field.value = fieldGetValue(data, offset, size) + field.min = fieldGetValue(data, offset+size, size) + field.max = fieldGetValue(data, offset+2*size, size) + --field.default = fieldGetValue(data, offset+3*size, size) + field.unit = fieldGetString(data, offset+4*size, field.unit) + field.step = 1 +end + +local function fieldUnsignedToSigned(field, size) + local bandval = bit32.lshift(0x80, (size-1)*8) + field.value = field.value - bit32.band(field.value, bandval) * 2 + field.min = field.min - bit32.band(field.min, bandval) * 2 + field.max = field.max - bit32.band(field.max, bandval) * 2 + --field.default = field.default - bit32.band(field.default, bandval) * 2 +end + +local function fieldSignedLoad(field, data, offset, size) + fieldUnsignedLoad(field, data, offset, size) + fieldUnsignedToSigned(field, size) +end + +local function fieldIntSave(index, value, size) + local frame = { deviceId, handsetId, index } + for i=size-1, 0, -1 do + frame[#frame + 1] = (bit32.rshift(value, 8*i) % 256) + end + crossfireTelemetryPush(0x2D, frame) +end + +local function fieldUnsignedSave(field, size) + local value = field.value + fieldIntSave(field.id, value, size) +end + +local function fieldSignedSave(field, size) + local value = field.value + if value < 0 then + value = bit32.lshift(0x100, (size-1)*8) + value + end + fieldIntSave(field.id, value, size) +end + +local function fieldIntDisplay(field, y, attr) + lcd.drawText(COL2, y, field.value .. field.unit, attr) +end + +-- UINT8 +local function fieldUint8Load(field, data, offset) + fieldUnsignedLoad(field, data, offset, 1) +end + +local function fieldUint8Save(field) + fieldUnsignedSave(field, 1) +end + +-- INT8 +local function fieldInt8Load(field, data, offset) + fieldSignedLoad(field, data, offset, 1) +end + +local function fieldInt8Save(field) + fieldSignedSave(field, 1) +end + +-- UINT16 +local function fieldUint16Load(field, data, offset) + fieldUnsignedLoad(field, data, offset, 2) +end + +local function fieldUint16Save(field) + fieldUnsignedSave(field, 2) +end + +-- INT16 +local function fieldInt16Load(field, data, offset) + fieldSignedLoad(field, data, offset, 2) +end + +local function fieldInt16Save(field) + fieldSignedSave(field, 2) +end + +-- TEXT SELECTION +local function fieldTextSelectionLoad(field, data, offset) + field.values, offset = fieldGetSelectOpts(data, offset, field.nc == nil and field.values) + field.value = data[offset] + -- min max and default (offset+1 to 3) are not used on selections + -- units never uses cache + field.unit = fieldGetString(data, offset+4) + field.nc = nil -- use cache next time +end + +local function fieldTextSelectionSave(field) + crossfireTelemetryPush(0x2D, { deviceId, handsetId, field.id, field.value }) +end + +local function fieldTextSelectionDisplay_color(field, y, attr) + local val = field.values[field.value+1] or "ERR" + lcd.drawText(COL2, y, val, attr) + local strPix = lcd.sizeText and lcd.sizeText(val) or (10 * #val) + lcd.drawText(COL2 + strPix, y, field.unit, 0) +end + +local function fieldTextSelectionDisplay_bw(field, y, attr) + lcd.drawText(COL2, y, field.values[field.value+1] or "ERR", attr) + lcd.drawText(lcd.getLastPos(), y, field.unit, 0) +end + +-- STRING +local function fieldStringLoad(field, data, offset) + field.value, offset = fieldGetString(data, offset) + if #data >= offset then + field.maxlen = data[offset] + end +end + +local function fieldStringDisplay(field, y, attr) + lcd.drawText(COL2, y, field.value, attr) +end + +local function fieldFolderOpen(field) + folderAccess = field.id + local backFld = fields[backButtonId] + -- Store the lineIndex and pageOffset to return to in the backFld + backFld.li = lineIndex + backFld.po = pageOffset + backFld.parent = folderAccess + + lineIndex = 1 + pageOffset = 0 +end + +local function fieldFolderDeviceOpen(field) + crossfireTelemetryPush(0x28, { 0x00, 0xEA }) --broadcast with standard handset ID to get all node respond correctly + return fieldFolderOpen(field) +end + +local function fieldFolderDisplay(field,y ,attr) + lcd.drawText(textXoffset, y, "> " .. field.name, bit32.bor(attr, BOLD)) +end + +local function fieldCommandLoad(field, data, offset) + field.status = data[offset] + field.timeout = data[offset+1] + field.info = fieldGetString(data, offset+2) + if field.status == 0 then + fieldPopup = nil + end +end + +local function fieldCommandSave(field) + if field.status ~= nil then + if field.status < 4 then + field.status = 1 + crossfireTelemetryPush(0x2D, { deviceId, handsetId, field.id, field.status }) + fieldPopup = field + fieldPopup.lastStatus = 0 + commandRunningIndicator = 1 + fieldTimeout = getTime() + field.timeout + end + end +end + +local function fieldCommandDisplay(field, y, attr) + lcd.drawText(10, y, "[" .. field.name .. "]", bit32.bor(attr, BOLD)) +end + +local function UIbackExec() + local backFld = fields[backButtonId] + lineIndex = backFld.li or 1 + pageOffset = backFld.po or 0 + + backFld.parent = 255 + backFld.li = nil + backFld.po = nil + folderAccess = nil +end + +local function UIexitExec() + exitscript = 1 +end + +local function changeDeviceId(devId) --change to selected device ID + folderAccess = nil + deviceIsELRS_TX = nil + elrsFlags = 0 + --if the selected device ID (target) is a TX Module, we use our Lua ID, so TX Flag that user is using our LUA + if devId == 0xEE then + handsetId = 0xEF + else --else we would act like the legacy lua + handsetId = 0xEA + end + deviceId = devId + fields_count = 0 --set this because next target wouldn't have the same count, and this trigger to request the new count +end + +local function fieldDeviceIdSelect(field) + local device = getDevice(field.name) + changeDeviceId(device.id) + crossfireTelemetryPush(0x28, { 0x00, 0xEA }) +end + +local function createDeviceFields() -- put other devices in the field list + fields[fields_count + 2 + #devices] = fields[backButtonId] + backButtonId = fields_count + 2 + #devices -- move back button to the end of the list, so it will always show up at the bottom. + for i=1, #devices do + if devices[i].id == deviceId then + fields[fields_count+1+i] = {name=devices[i].name, parent = 255, type=15} + else + fields[fields_count+1+i] = {name=devices[i].name, parent = fields_count+1, type=15} + end + end +end + +local function parseDeviceInfoMessage(data) + local offset + local id = data[2] + local newName + newName, offset = fieldGetString(data, 3) + local device = getDevice(newName) + if device == nil then + device = { id = id, name = newName } + devices[#devices + 1] = device + end + if deviceId == id then + deviceName = newName + deviceIsELRS_TX = ((fieldGetValue(data,offset,4) == 0x454C5253) and (deviceId == 0xEE)) or nil -- SerialNumber = 'E L R S' and ID is TX module + local newFieldCount = data[offset+12] + if newFieldCount ~= fields_count or newFieldCount == 0 then + fields_count = newFieldCount + allocateFields() + reloadAllField() + fields[fields_count+1] = {id = fields_count+1, name="Other Devices", parent = 255, type=16} -- add other devices folders + if newFieldCount == 0 then + -- This device has no fields so the Loading code never starts + createDeviceFields() + end + end + end +end + +local functions = { + { load=fieldUint8Load, save=fieldUint8Save, display=fieldIntDisplay }, --1 UINT8(0) + { load=fieldInt8Load, save=fieldInt8Save, display=fieldIntDisplay }, --2 INT8(1) + { load=fieldUint16Load, save=fieldUint16Save, display=fieldIntDisplay }, --3 UINT16(2) + { load=fieldInt16Load, save=fieldInt16Save, display=fieldIntDisplay }, --4 INT16(3) + nil, + nil, + nil, + nil, + nil, --9 FLOAT(8) + { load=fieldTextSelectionLoad, save=fieldTextSelectionSave, display = nil }, --10 SELECT(9) + { load=fieldStringLoad, save=nil, display=fieldStringDisplay }, --11 STRING(10) editing NOTIMPL + { load=nil, save=fieldFolderOpen, display=fieldFolderDisplay }, --12 FOLDER(11) + { load=fieldStringLoad, save=nil, display=fieldStringDisplay }, --13 INFO(12) + { load=fieldCommandLoad, save=fieldCommandSave, display=fieldCommandDisplay }, --14 COMMAND(13) + { load=nil, save=UIbackExec, display=fieldCommandDisplay }, --15 back(14) + { load=nil, save=fieldDeviceIdSelect, display=fieldCommandDisplay }, --16 device(15) + { load=nil, save=fieldFolderDeviceOpen, display=fieldFolderDisplay }, --17 deviceFOLDER(16) + { load=nil, save=UIexitExec, display=fieldCommandDisplay }, --18 exit(17) +} + +local function parseParameterInfoMessage(data) + local fieldId = (fieldPopup and fieldPopup.id) or loadQ[#loadQ] + if data[2] ~= deviceId or data[3] ~= fieldId then + fieldData = {} + fieldChunk = 0 + return + end + local field = fields[fieldId] + local chunksRemain = data[4] + -- If no field or the chunksremain changed when we have data, don't continue + if not field or (chunksRemain ~= expectChunksRemain and #fieldData ~= 0) then + return + end + expectChunksRemain = chunksRemain - 1 + for i=5, #data do + fieldData[#fieldData + 1] = data[i] + end + if chunksRemain > 0 then + fieldChunk = fieldChunk + 1 + else + loadQ[#loadQ] = nil + -- Populate field from fieldData + if #fieldData > 3 then + local offset + field.id = fieldId + field.parent = (fieldData[1] ~= 0) and fieldData[1] or nil + field.type = bit32.band(fieldData[2], 0x7f) + field.hidden = bit32.btest(fieldData[2], 0x80) or nil + field.name, offset = fieldGetString(fieldData, 3, field.name) + if functions[field.type+1].load then + functions[field.type+1].load(field, fieldData, offset) + end + if field.min == 0 then field.min = nil end + if field.max == 0 then field.max = nil end + end + + fieldChunk = 0 + fieldData = {} + + -- Last field loaded, add the list of devices to the end + if #loadQ == 0 then + createDeviceFields() + end + end +end + +local function parseElrsInfoMessage(data) + if data[2] ~= deviceId then + fieldData = {} + fieldChunk = 0 + return + end + + local badPkt = data[3] + local goodPkt = (data[4]*256) + data[5] + local newFlags = data[6] + -- If flags are changing, reset the warning timeout to display/hide message immediately + if newFlags ~= elrsFlags then + elrsFlags = newFlags + titleShowWarnTimeout = 0 + end + elrsFlagsInfo = fieldGetString(data, 7) + + local state = (bit32.btest(elrsFlags, 1) and "C") or "-" + goodBadPkt = string.format("%u/%u %s", badPkt, goodPkt, state) +end + +local function parseElrsV1Message(data) + if (data[1] ~= 0xEA) or (data[2] ~= 0xEE) then + return + end + + -- local badPkt = data[9] + -- local goodPkt = (data[10]*256) + data[11] + -- goodBadPkt = string.format("%u/%u X", badPkt, goodPkt) + fieldPopup = {id = 0, status = 2, timeout = 0xFF, info = "ERROR: 1.x firmware"} + fieldTimeout = getTime() + 0xFFFF +end + +local function refreshNext() + local command, data = crossfireTelemetryPop() + if command == 0x29 then + parseDeviceInfoMessage(data) + elseif command == 0x2B then + parseParameterInfoMessage(data) + if #loadQ > 0 then + fieldTimeout = 0 -- request next chunk immediately + elseif fieldPopup then + fieldTimeout = getTime() + fieldPopup.timeout + end + elseif command == 0x2D then + parseElrsV1Message(data) + elseif command == 0x2E then + parseElrsInfoMessage(data) + end + + local time = getTime() + if fieldPopup then + if time > fieldTimeout and fieldPopup.status ~= 3 then + crossfireTelemetryPush(0x2D, { deviceId, handsetId, fieldPopup.id, 6 }) -- lcsQuery + fieldTimeout = time + fieldPopup.timeout + end + elseif time > devicesRefreshTimeout and fields_count < 1 then + devicesRefreshTimeout = time + 100 -- 1s + crossfireTelemetryPush(0x28, { 0x00, 0xEA }) + elseif time > linkstatTimeout then + if not deviceIsELRS_TX and #loadQ == 0 then + goodBadPkt = "" + else + crossfireTelemetryPush(0x2D, { deviceId, handsetId, 0x0, 0x0 }) --request linkstat + end + linkstatTimeout = time + 100 + elseif time > fieldTimeout and fields_count ~= 0 then + if #loadQ > 0 then + crossfireTelemetryPush(0x2C, { deviceId, handsetId, loadQ[#loadQ], fieldChunk }) + fieldTimeout = time + 50 -- 0.5s + end + end + + if time > titleShowWarnTimeout then + -- if elrsFlags bit set is bit higher than bit 0 and bit 1, it is warning flags + titleShowWarn = (elrsFlags > 3 and not titleShowWarn) or nil + titleShowWarnTimeout = time + 100 + end +end + +local lcd_title -- holds function that is color/bw version +local function lcd_title_color() + lcd.clear() + + local EBLUE = lcd.RGB(0x43, 0x61, 0xAA) + local EGREEN = lcd.RGB(0x9f, 0xc7, 0x6f) + local EGREY1 = lcd.RGB(0x91, 0xb2, 0xc9) + local EGREY2 = lcd.RGB(0x6f, 0x62, 0x7f) + local barHeight = 30 + + -- Field display area (white w/ 2px green border) + lcd.setColor(CUSTOM_COLOR, EGREEN) + lcd.drawRectangle(0, 0, LCD_W, LCD_H, CUSTOM_COLOR) + lcd.drawRectangle(1, 0, LCD_W - 2, LCD_H - 1, CUSTOM_COLOR) + -- title bar + lcd.drawFilledRectangle(0, 0, LCD_W, barHeight, CUSTOM_COLOR) + lcd.setColor(CUSTOM_COLOR, EGREY1) + lcd.drawFilledRectangle(LCD_W - textSize, 0, textSize, barHeight, CUSTOM_COLOR) + lcd.setColor(CUSTOM_COLOR, EGREY2) + lcd.drawRectangle(LCD_W - textSize, 0, textSize, barHeight - 1, CUSTOM_COLOR) + lcd.drawRectangle(LCD_W - textSize, 1 , textSize - 1, barHeight - 2, CUSTOM_COLOR) -- left and bottom line only 1px, make it look bevelled + lcd.setColor(CUSTOM_COLOR, BLACK) + if titleShowWarn then + lcd.drawText(textXoffset + 1, 4, elrsFlagsInfo, CUSTOM_COLOR) + lcd.drawText(LCD_W - textSize - 5, 4, tostring(elrsFlags), RIGHT + BOLD + CUSTOM_COLOR) + else + local title = fields_count > 0 and deviceName or "Loading..." + lcd.drawText(textXoffset + 1, 4, title, CUSTOM_COLOR) + lcd.drawText(LCD_W - 5, 4, goodBadPkt, RIGHT + BOLD + CUSTOM_COLOR) + end + -- progress bar + if #loadQ > 0 and fields_count > 0 then + local barW = (COL2-4) * (fields_count - #loadQ) / fields_count + lcd.setColor(CUSTOM_COLOR, EBLUE) + lcd.drawFilledRectangle(2, 2+20, barW, barHeight-5-20, CUSTOM_COLOR) + lcd.setColor(CUSTOM_COLOR, WHITE) + lcd.drawFilledRectangle(2+barW, 2+20, COL2-2-barW, barHeight-5-20, CUSTOM_COLOR) + end +end + +local function lcd_title_bw() + lcd.clear() + -- B&W screen + local barHeight = 9 + if titleShowWarn then + lcd.drawText(LCD_W, 1, tostring(elrsFlags), RIGHT) + else + lcd.drawText(LCD_W - 1, 1, goodBadPkt, RIGHT) + lcd.drawLine(LCD_W - 10, 0, LCD_W - 10, barHeight-1, SOLID, INVERS) + end + + if #loadQ > 0 and fields_count > 0 then + lcd.drawFilledRectangle(COL2, 0, LCD_W, barHeight, GREY_DEFAULT) + lcd.drawGauge(0, 0, COL2, barHeight, fields_count - #loadQ, fields_count, 0) + else + lcd.drawFilledRectangle(0, 0, LCD_W, barHeight, GREY_DEFAULT) + if titleShowWarn then + lcd.drawText(textXoffset, 1, elrsFlagsInfo, INVERS) + else + local title = fields_count > 0 and deviceName or "Loading..." + lcd.drawText(textXoffset, 1, title, INVERS) + end + end +end + +local function lcd_warn() + lcd.drawText(textXoffset, textSize*2, "Error:") + lcd.drawText(textXoffset, textSize*3, elrsFlagsInfo) + lcd.drawText(LCD_W/2, textSize*5, "[OK]", BLINK + INVERS + CENTER) +end + +local function reloadCurField() + local field = getField(lineIndex) + fieldTimeout = 0 + fieldChunk = 0 + fieldData = {} + loadQ[#loadQ+1] = field.id +end + +local function reloadRelatedFields(field) + -- Reload the parent folder to update the description + if field.parent then + loadQ[#loadQ+1] = field.parent + fields[field.parent].name = nil + end + + -- Reload all editable fields at the same level as well as the parent item + for fieldId = fields_count, 1, -1 do + -- Skip this field, will be added to end + local fldTest = fields[fieldId] + if fieldId ~= field.id + and fldTest.parent == field.parent + and (fldTest.type or 99) < 11 then -- type could be nil if still loading + fldTest.nc = true -- "no cache" the options + loadQ[#loadQ+1] = fieldId + end + end + + -- Reload this field + loadQ[#loadQ+1] = field.id + -- with a short delay to allow the module EEPROM to commit + fieldTimeout = getTime() + 20 +end + +local function handleDevicePageEvent(event) + if #fields == 0 then --if there is no field yet + return + else + if fields[backButtonId].name == nil then --if back button is not assigned yet, means there is no field yet. + return + end + end + + if event == EVT_VIRTUAL_EXIT then -- Cancel edit / go up a folder / reload all + if edit then + edit = nil + reloadCurField(0) + else + if folderAccess == nil and #loadQ == 0 then -- only do reload if we're in the root folder and finished loading + if deviceId ~= 0xEE then + changeDeviceId(0xEE) --change device id clear the fields_count, therefore the next ping will do reloadAllField() + else + reloadAllField() + end + crossfireTelemetryPush(0x28, { 0x00, 0xEA }) + end + UIbackExec() + end + elseif event == EVT_VIRTUAL_ENTER then -- toggle editing/selecting current field + if elrsFlags > 0x1F then + elrsFlags = 0 + crossfireTelemetryPush(0x2D, { deviceId, handsetId, 0x2E, 0x00 }) + else + local field = getField(lineIndex) + if field and field.name then + if field.type < 10 then + edit = not edit + end + if not edit then + if field.type < 10 then + -- Editable fields + reloadRelatedFields(field) + elseif field.type == 13 then + -- Command + reloadCurField() + end + if functions[field.type+1].save then + functions[field.type+1].save(field) + end + end + end + end + elseif edit then + if event == EVT_VIRTUAL_NEXT then + incrField(1) + elseif event == EVT_VIRTUAL_PREV then + incrField(-1) + end + else + if event == EVT_VIRTUAL_NEXT then + selectField(1) + elseif event == EVT_VIRTUAL_PREV then + selectField(-1) + end + end +end + +-- Main +local function runDevicePage(event) + handleDevicePageEvent(event) + + lcd_title() + + if #devices > 1 then -- show other device folder + fields[fields_count+1].parent = nil + end + if elrsFlags > 0x1F then + lcd_warn() + else + for y = 1, maxLineIndex+1 do + local field = getField(pageOffset+y) + if not field then + break + elseif field.name ~= nil then + local attr = lineIndex == (pageOffset+y) + and ((edit and BLINK or 0) + INVERS) + or 0 + if field.type < 11 or field.type == 12 then -- if not folder, command, or back + lcd.drawText(textXoffset, y*textSize+textYoffset, field.name, 0) + end + if functions[field.type+1].display then + functions[field.type+1].display(field, y*textSize+textYoffset, attr) + end + end + end + end +end + +local function popupCompat(t, m, e) + -- Only use 2 of 3 arguments for older platforms + return popupConfirmation(t, e) +end + +local function runPopupPage(event) + if event == EVT_VIRTUAL_EXIT then + crossfireTelemetryPush(0x2D, { deviceId, handsetId, fieldPopup.id, 5 }) -- lcsCancel + fieldTimeout = getTime() + 200 -- 2s + end + + local result + if fieldPopup.status == 0 and fieldPopup.lastStatus ~= 0 then -- stopped + popupCompat(fieldPopup.info, "Stopped!", event) + reloadAllField() + fieldPopup = nil + elseif fieldPopup.status == 3 then -- confirmation required + result = popupCompat(fieldPopup.info, "PRESS [OK] to confirm", event) + fieldPopup.lastStatus = fieldPopup.status + if result == "OK" then + crossfireTelemetryPush(0x2D, { deviceId, handsetId, fieldPopup.id, 4 }) -- lcsConfirmed + fieldTimeout = getTime() + fieldPopup.timeout -- we are expecting an immediate response + fieldPopup.status = 4 + elseif result == "CANCEL" then + fieldPopup = nil + end + elseif fieldPopup.status == 2 then -- running + if fieldChunk == 0 then + commandRunningIndicator = (commandRunningIndicator % 4) + 1 + end + result = popupCompat(fieldPopup.info .. " [" .. string.sub("|/-\\", commandRunningIndicator, commandRunningIndicator) .. "]", "Press [RTN] to exit", event) + fieldPopup.lastStatus = fieldPopup.status + if result == "CANCEL" then + crossfireTelemetryPush(0x2D, { deviceId, handsetId, fieldPopup.id, 5 }) -- lcsCancel + fieldTimeout = getTime() + fieldPopup.timeout -- we are expecting an immediate response + fieldPopup = nil + end + end +end + +local function loadSymbolChars() + -- On firmwares that have constants defined for the arrow chars, use them in place of + -- the \xc0 \xc1 chars (which are OpenTX-en) + if __opentx then + byteToStr = function (b) + -- Use the table to convert the char, else use string.char if not in the table + return ({ + [192] = __opentx.CHAR_UP, + [193] = __opentx.CHAR_DOWN + })[b] or string.char(b) + end + else + byteToStr = string.char + end +end + +local function touch2evt(event, touchState) + -- Convert swipe events to normal events Left/Right/Up/Down -> EXIT/ENTER/PREV/NEXT + -- PREV/NEXT are swapped if editing + -- TAP is converted to ENTER + touchState = touchState or {} + return (touchState.swipeLeft and EVT_VIRTUAL_EXIT) + or (touchState.swipeRight and EVT_VIRTUAL_ENTER) + or (touchState.swipeUp and (edit and EVT_VIRTUAL_NEXT or EVT_VIRTUAL_PREV)) + or (touchState.swipeDown and (edit and EVT_VIRTUAL_PREV or EVT_VIRTUAL_NEXT)) + or (event == EVT_TOUCH_TAP and EVT_VIRTUAL_ENTER) +end + +local function setLCDvar() + -- Set the title function depending on if LCD is color, and free the other function and + -- set textselection unit function, use GetLastPost or sizeText + if (lcd.RGB ~= nil) then + lcd_title = lcd_title_color + functions[10].display=fieldTextSelectionDisplay_color + else + lcd_title = lcd_title_bw + functions[10].display=fieldTextSelectionDisplay_bw + touch2evt = nil + end + lcd_title_color = nil + lcd_title_bw = nil + fieldTextSelectionDisplay_bw = nil + fieldTextSelectionDisplay_color = nil + -- Determine if popupConfirmation takes 3 arguments or 2 + -- if pcall(popupConfirmation, "", "", EVT_VIRTUAL_EXIT) then + -- major 1 is assumed to be FreedomTX + local ver, radio, major = getVersion() + if major ~= 1 then + popupCompat = popupConfirmation + end + if LCD_W == 480 then + COL2 = 240 + maxLineIndex = 10 + textXoffset = 3 + textYoffset = 10 + textSize = 22 --textSize is text Height + elseif LCD_W == 320 then + COL2 = 160 + maxLineIndex = 14 + textXoffset = 3 + textYoffset = 10 + textSize = 22 + else + if LCD_W == 212 then + COL2 = 110 + else + COL2 = 70 + end + maxLineIndex = 6 + textXoffset = 0 + textYoffset = 3 + textSize = 8 + end + loadSymbolChars() + loadSymbolChars = nil +end + +local function setMock() + -- Setup fields to display if running in Simulator + local _, rv = getVersion() + if string.sub(rv, -5) ~= "-simu" then return end + local mock = loadScript("mockup/elrsmock.lua") + if mock == nil then return end + fields, goodBadPkt, deviceName = mock(), "0/500 C", "ExpressLRS TX" + fields_count = #fields - 1 + loadQ = { fields_count } + deviceIsELRS_TX = true + backButtonId = #fields + + fields_count = fields_count + 1 + exitButtonId = fields_count + 1 + fields[exitButtonId] = {id = exitButtonId, name="----EXIT----", type=17} +end + +-- Init +local function init() + setLCDvar() + setMock() + setLCDvar = nil + setMock = nil +end + +-- Main +local function run(event, touchState) + if event == nil then + error("Cannot be run as a model script!") + return 2 + end + + event = (touch2evt and touch2evt(event, touchState)) or event + if fieldPopup ~= nil then + runPopupPage(event) + else + runDevicePage(event) + end + + refreshNext() + + return exitscript +end + +return { init=init, run=run } diff --git a/SCRIPTS/TOOLS/elrsV3.luac b/SCRIPTS/TOOLS/elrsV3.luac new file mode 100755 index 0000000000000000000000000000000000000000..e4dc21388cf956225ed28d123aadd41ca389d6cc GIT binary patch literal 15774 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 then + return nil + end + return v +end + +local function readParameters() + -- read and return parameters + local p = readValue(PSensor) + local i = readValue(ISensor) + local d = readValue(DSensor) + local r = readValue(RSensor) + local a = readActiveParamValue(ActiveParamSensor) + return {p,i,d,r,a} +end + +local function drawParameters() + -- draw labels and params on screen + local params = readParameters() + local activeParam = params[5] + + -- if active gain does not validate then assume + -- Gain Adjustment Mode is disabled + if not activeParam then + lcd.clear() + lcd.drawText(20,30,"Please enter Gain Adjustment Mode") + return + end + + local activePage = getPage(activeParam-1) + for iParam=0,7 do + -- highlight selected parameter + local attr = (activeParam==iParam+1) and 2 or 0 + -- circular index (per page) + local perPageIndx = iParam % 4 + 1 + -- check if displaying cyclic params. + local isCyclicPage = (getPage(iParam)==1) + -- set y draw coord + local y = perPageIndx*10+2 + + -- labels + local x = isCyclicPage and 6 or 120 + -- labels are P,I,D for both pages except for last param + local val = iParam==3 and "Response" or + (iParam==7 and "Filtering" or tags[perPageIndx]) + lcd.drawText (x, y, val, attr) + + -- gains + -- set all params for non-active page to '--' rather than 'last value' + val = (getPage(iParam)==activePage) and params[perPageIndx] or '--' + x = isCyclicPage and 70 or 180 + lcd.drawText (x, y, val, attr) + end +end + + +local function run_func(event) + -- TODO: calling clear() on every function call redrawing all labels is not ideal + lcd.clear() + lcd.drawText (8, 2, "Cyclic (0...200)") + lcd.drawText (114, 2, "Tail (0...200)") + drawParameters() +end + +local function init_func() end +local function bg_func() end + + +return { run=run_func, background=bg_func, init=init_func } diff --git a/SCRIPTS/TOOLS/readme.txt b/SCRIPTS/TOOLS/readme.txt new file mode 100755 index 0000000..9ea1e7c --- /dev/null +++ b/SCRIPTS/TOOLS/readme.txt @@ -0,0 +1 @@ +Scripts that need to be available in TOOLS menu should be in this directory. diff --git a/SCRIPTS/WIZARD/delta.lua b/SCRIPTS/WIZARD/delta.lua new file mode 100755 index 0000000..fd0d7d5 --- /dev/null +++ b/SCRIPTS/WIZARD/delta.lua @@ -0,0 +1,383 @@ +---- ######################################################################### +---- # # +---- # 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. # +---- # # +---- ######################################################################### +-- Delta Wizard pages +local ENGINE_PAGE = 0 +local ELEVONS_PAGE = 1 +local RUDDER_PAGE = 2 +local CONFIRMATION_PAGE = 3 + +-- Navigation variables +local page = ENGINE_PAGE +local dirty = true +local edit = false +local field = 0 +local fieldsMax = 0 + +-- Model settings +local engineMode = 1 +local thrCH1 = 0 +local elevCH1 = 0 +local elevCH2 = 0 +local elevonsMode = 0 +local rudderMode = 0 +local rudCH1 = 0 +local servoPage = nil + +-- Common functions +local lastBlink = 0 +local function blinkChanged() + local time = getTime() % 128 + local blink = (time - time % 64) / 64 + if blink ~= lastBlink then + lastBlink = blink + return true + else + return false + end +end + +local function fieldIncDec(event, value, max, force) + if edit or force==true then + if event == EVT_VIRTUAL_INC then + value = (value + max) + dirty = true + elseif event == EVT_VIRTUAL_DEC then + value = (value + max + 2) + dirty = true + end + value = (value % (max+1)) + end + return value +end + +local function valueIncDec(event, value, min, max) + if edit then + if event == EVT_VIRTUAL_INC or event == EVT_VIRTUAL_INC_REPT then + if value < max then + value = (value + 1) + dirty = true + end + elseif event == EVT_VIRTUAL_DEC or event == EVT_VIRTUAL_DEC_REPT then + if value > min then + value = (value - 1) + dirty = true + end + end + end + return value +end + +local function navigate(event, fieldMax, prevPage, nextPage) + if event == EVT_VIRTUAL_ENTER then + edit = not edit + dirty = true + elseif edit then + if event == EVT_VIRTUAL_EXIT then + edit = false + dirty = true + elseif not dirty then + dirty = blinkChanged() + end + else + if event == EVT_VIRTUAL_NEXT_PAGE then + page = nextPage + field = 0 + dirty = true + elseif event == EVT_VIRTUAL_PREV_PAGE then + page = prevPage + field = 0 + killEvents(event); + dirty = true + else + field = fieldIncDec(event, field, fieldMax, true) + end + end +end + +local function getFieldFlags(position) + flags = 0 + if field == position then + flags = INVERS + if edit then + flags = INVERS + BLINK + end + end + return flags +end + +local function channelIncDec(event, value) + if not edit and event==EVT_VIRTUAL_MENU then + servoPage = value + dirty = true + else + value = valueIncDec(event, value, 0, 15) + end + return value +end + +-- Init function +local function init() + rudCH1 = defaultChannel(0) + thrCH1 = defaultChannel(2) + elevCH1 = defaultChannel(1) + elevCH2 = defaultChannel(3) +end + +-- Engine Menu +local engineModeItems = {"No", "Yes"} +local function drawEngineMenu() + lcd.clear() + if engineMode == 0 then + -- No engine + fieldsMax = 0 + else + -- 1 channel + lcd.drawText(5, 30, "Assign channel", 0); + lcd.drawText(5, 40, ">>>", 0); + lcd.drawSource(25, 40, MIXSRC_CH1+thrCH1, getFieldFlags(1)) + fieldsMax = 1 + end + lcd.drawText(1, 0, "Got an engine?", 0) + lcd.drawFilledRectangle(0, 0, LCD_W, 8, FILL_WHITE) + lcd.drawCombobox(0, 8, LCD_W, engineModeItems, engineMode, getFieldFlags(0)) +end + +local function engineMenu(event) + if dirty then + dirty = false + drawEngineMenu() + end + + navigate(event, fieldsMax, page, page+1) + + if field==0 then + engineMode = fieldIncDec(event, engineMode, 1) + elseif field==1 then + thrCH1 = channelIncDec(event, thrCH1) + end +end + +-- Elevons Menu +local elevonsModeItems = {"2 Channels"} +local function drawElevonsMenu() + lcd.clear() + lcd.drawText(1, 0, "Select elevon channnels", 0) + lcd.drawFilledRectangle(0, 0, LCD_W, 8, FILL_WHITE) + lcd.drawCombobox(0, 8, LCD_W, elevonsModeItems, elevonsMode, 0) + lcd.drawText(5, 30, "Assign channels", 0); + lcd.drawText(30, 40, "L", 0); + lcd.drawText(65, 40, "R", 0); + lcd.drawText(5, 50, ">>>", 0); + lcd.drawSource(25, 50, MIXSRC_CH1+elevCH1, getFieldFlags(0)) + lcd.drawSource(60, 50, MIXSRC_CH1+elevCH2, getFieldFlags(1)) + fieldsMax = 1 +end + +local function elevonsMenu(event) + if dirty then + dirty = false + drawElevonsMenu() + end + + navigate(event, fieldsMax, page-1, page+1) + + if field==0 then + elevCH1 = channelIncDec(event, elevCH1) + elseif field==1 then + elevCH2 = channelIncDec(event, elevCH2) + end +end + +-- Rudder menu +local rudderModeItems = {"No", "Yes"} + +local function drawRudderMenu() + lcd.clear() + if rudderMode == 0 then + -- No rudder + fieldsMax = 0 + else + -- 1 channel + lcd.drawText(5, 30, "Assign channel", 0); + lcd.drawText(5, 40, ">>>", 0); + lcd.drawSource(25, 40, MIXSRC_CH1+rudCH1, getFieldFlags(1)) + fieldsMax = 1 + end + lcd.drawText(1, 0, "Got a rudder?", 0) + lcd.drawFilledRectangle(0, 0, LCD_W, 8, FILL_WHITE) + lcd.drawCombobox(0, 8, LCD_W, rudderModeItems, rudderMode, getFieldFlags(0)) +end + +local function rudderMenu(event) + if dirty then + dirty = false + drawRudderMenu() + end + + navigate(event, fieldsMax, page-1, page+1) + + if field==0 then + rudderMode = fieldIncDec(event, rudderMode, 1) + elseif field==1 then + rudCH1 = channelIncDec(event, rudCH1) + end +end + +-- Servo (limits) Menu +local function drawServoMenu(limits) + lcd.clear() + lcd.drawSource(1, 0, MIXSRC_CH1+servoPage, 0) + lcd.drawText(25, 0, "servo min/max/center/direction?", 0) + lcd.drawFilledRectangle(0, 0, LCD_W, 8, FILL_WHITE) + lcd.drawText(LCD_W/2-19, LCD_H-8, ">>>", 0); + lcd.drawNumber(140, 35, limits.min, PREC1+getFieldFlags(0)); + lcd.drawNumber(205, 35, limits.max, PREC1+getFieldFlags(1)); + lcd.drawNumber(170, 9, limits.offset, PREC1+getFieldFlags(2)); + if limits.revert == 0 then + lcd.drawText(129, 50, "\126", getFieldFlags(3)); + else + lcd.drawText(129, 50, "\127", getFieldFlags(3)); + end + fieldsMax = 3 +end + +local function servoMenu(event) + local limits = model.getOutput(servoPage) + + if dirty then + dirty = false + drawServoMenu(limits) + end + + navigate(event, fieldsMax, page, page) + + if edit then + if field==0 then + limits.min = valueIncDec(event, limits.min, -1000, 0) + elseif field==1 then + limits.max = valueIncDec(event, limits.max, 0, 1000) + elseif field==2 then + limits.offset = valueIncDec(event, limits.offset, -1000, 1000) + elseif field==3 then + limits.revert = fieldIncDec(event, limits.revert, 1) + end + model.setOutput(servoPage, limits) + elseif event == EVT_VIRTUAL_EXIT then + servoPage = nil + dirty = true + end +end + +-- Confirmation Menu +local function addMix(channel, input, name, weight, index) + local mix = { source=input, name=name } + if weight ~= nil then + mix.weight = weight + end + if index == nil then + index = 0 + end + model.insertMix(channel, index, mix) +end + +local function applySettings() + model.defaultInputs() + model.deleteMixes() + if engineMode == 1 then + addMix(thrCH1, MIXSRC_FIRST_INPUT+defaultChannel(2), "Engine") + end + addMix(elevCH1, MIXSRC_FIRST_INPUT+defaultChannel(1), "D-EleL", 50) + addMix(elevCH1, MIXSRC_FIRST_INPUT+defaultChannel(3), "D-AilL", 50, 1) + addMix(elevCH2, MIXSRC_FIRST_INPUT+defaultChannel(1), "D-EleR", 50) + addMix(elevCH2, MIXSRC_FIRST_INPUT+defaultChannel(3), "D-AilR", -50, 1) + if rudderMode == 1 then + addMix(rudCH1, MIXSRC_FIRST_INPUT+defaultChannel(0), "Rudder") + end +end + +local function drawNextLine(x, y, label, channel) + lcd.drawText(x, y, label, 0); + lcd.drawText(x+48, y, ":", 0); + lcd.drawSource(x+52, y, MIXSRC_CH1+channel, 0) + y = y + 8 + if y > 50 then + y = 12 + x = 120 + end + return x, y +end + +local function drawConfirmationMenu() + local x = 22 + local y = 12 + lcd.clear() + lcd.drawText(48, 1, "Ready to go?", 0); + lcd.drawFilledRectangle(0, 0, LCD_W, 9, 0) + if engineMode == 1 then + x, y = drawNextLine(x, y, "Throttle", thrCH1) + end + x, y = drawNextLine(x, y, "Elevon L", elevCH1) + x, y = drawNextLine(x, y, "Elevon R", elevCH2) + if rudderMode == 1 then + drawNextLine(x, y, "Rudder", rudCH1) + end + lcd.drawText(0, LCD_H-8, "[Enter Long] to confirm", 0); + lcd.drawFilledRectangle(0, LCD_H-9, LCD_W, 9, 0) + fieldsMax = 0 +end + +local function confirmationMenu(event) + if dirty then + dirty = false + drawConfirmationMenu() + end + + navigate(event, fieldsMax, RUDDER_PAGE, page) + + if event == EVT_VIRTUAL_EXIT then + return 2 + elseif event == EVT_VIRTUAL_ENTER_LONG then + killEvents(event) + applySettings() + return 2 + else + return 0 + end +end + +-- Main + +local function run(event) + if event == nil then + error("Cannot be run as a model script!") + end + + if servoPage ~= nil then + servoMenu(event) + elseif page == ENGINE_PAGE then + engineMenu(event) + elseif page == ELEVONS_PAGE then + elevonsMenu(event) + elseif page == RUDDER_PAGE then + rudderMenu(event) + elseif page == CONFIRMATION_PAGE then + return confirmationMenu(event) + end + return 0 +end + +return { init=init, run=run } diff --git a/SCRIPTS/WIZARD/heli.lua b/SCRIPTS/WIZARD/heli.lua new file mode 100755 index 0000000..4545da3 --- /dev/null +++ b/SCRIPTS/WIZARD/heli.lua @@ -0,0 +1,533 @@ +---- ######################################################################### +---- # # +---- # Copyright (C) EdgeTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### + +local VALUE = 0 +local COMBO = 1 +local edit = false +local page = 1 +local current = 1 +local pages = {} +local fields = {} +local switches = {"SA", "SB", "SC", "SD", "SF"} +local switchValues = {[0]=2, 5, 8, 11, 14} + +-- Change display attribute to current field +local function addField(step) + local field = fields[current] + local min, max + if field[3] == VALUE then + min = field[6] + max = field[7] + elseif field[3] == COMBO then + min = 0 + max = #(field[6]) - 1 + end + if (step < 0 and field[5] > min) or (step > 0 and field[5] < max) then + field[5] = field[5] + step + end +end + +-- Select the next or previous page +local function selectPage(step) + page = 1 + ((page + step - 1 + #pages) % #pages) + edit = false + current = 1 +end + +-- Select the next or previous editable field +local function selectField(step) + repeat + current = 1 + ((current + step - 1 + #fields) % #fields) + until fields[current][4]==1 +end + +-- Redraw the current page +local function redrawFieldsPage(event) + + for index = 1, 10, 1 do + local field = fields[index] + if field == nil then + break + end + + local attr = current == (index) and ((edit == true and BLINK or 0) + INVERS) or 0 + attr = attr + + if field[4] == 1 then + if field[3] == VALUE then + lcd.drawNumber(field[1], field[2], field[5], LEFT + attr) + elseif field[3] == COMBO then + if field[5] >= 0 and field[5] < #(field[6]) then + lcd.drawText(field[1],field[2], field[6][1+field[5]], attr) + end + end + end + end +end + +local function updateField(field) + local value = field[5] +end + +-- Main +local function runFieldsPage(event) + if event == EVT_VIRTUAL_EXIT then -- exit script + return 2 + elseif event == EVT_VIRTUAL_ENTER then -- toggle editing/selecting current field + if fields[current][5] ~= nil then + edit = not edit + if edit == false then + lcd.clear() + updateField(fields[current]) + end + end + elseif edit then + if event == EVT_VIRTUAL_INC or event == EVT_VIRTUAL_INC_REPT then + addField(1) + elseif event == EVT_VIRTUAL_DEC or event == EVT_VIRTUAL_DEC_REPT then + addField(-1) + end + else + if event == EVT_VIRTUAL_NEXT then + selectField(1) + elseif event == EVT_VIRTUAL_PREV then + selectField(-1) + end + end + redrawFieldsPage(event) + return 0 +end + +-- set visibility flags starting with SECOND field of fields +local function setFieldsVisible(...) + local arg={...} + local cnt = 2 + for i,v in ipairs(arg) do + fields[cnt][4] = v + cnt = cnt + 1 + end +end + +local TypeFields = { + {0, 12, COMBO, 1, 0, {"Flybarless (FBL)", "Flybarred (FB)" } }, + {65, 25, COMBO, 1, 0, {"120", "120X", "140", "90" } }, +} + +local function runTypeConfig(event) + lcd.clear() + fields = TypeFields + lcd.drawScreenTitle("Helicopter Type", 1,9) + fields[2][4] = 0 + if fields[1][5] == 1 then + lcd.drawText(0, 25, "Swash Type") + fields[2][4] = 1 + end + local result = runFieldsPage(event) + return result +end + +local StyleFields = { + {0, 12, COMBO, 1, 0, { "Sport", "Light 3D","Full 3D" } }, +} + +local function runStyleConfig(event) + lcd.clear() + fields = StyleFields + lcd.drawScreenTitle("Your Flying Style",2,9) + fields[1][4]=1 + local result = runFieldsPage(event) + return result +end + +local SwitchFields = { + {75, 12, COMBO, 1, 0, { "SA", "SB", "SC", "SD","SF" } }, + {75, 33, COMBO, 1, 4, { "SA", "SB", "SC", "SD","SF" } }, + {75, 54, COMBO, 1, 2, { "SA", "SB", "SC", "SD", "SF" } }, +} + +local function runSwitchConfig(event) + lcd.clear() + lcd.drawScreenTitle("Assign Switches",3,9) + fields = SwitchFields + lcd.drawText(0, 12, "FM (Idle Up)") + fields[1][4]=1 + lcd.drawText(0, 33, "Throttle Hold") + fields[2][4]=1 + fields[3][4]=0 + if TypeFields[1][5]==1 then + lcd.drawText(0, 54, "Tail Gain") + fields[3][4]=1 + end + local result = runFieldsPage(event) + return result +end + +local ThrFields = { + {0, 12, COMBO, 1, 2, { "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8" } }, +} + +local function runThrConfig(event) + lcd.clear() + fields = ThrFields + lcd.drawScreenTitle("Throttle Channel",3,9) + fields[1][4]=1 + local result = runFieldsPage(event) + return result +end + +local CurveFields = { + {75, 12, COMBO, 1, 0, { "Thr Up", "V Curve","Flat" } }, + {75, 32, COMBO, 1, 0, { "V Curve","Flat" } }, + {75, 54, COMBO, 1, 0, { "V Curve","Flat" } }, +} + +local function runCurveConfig(event) + lcd.clear() + fields = CurveFields + lcd.drawScreenTitle("FM Throttle Curves",4,9) + lcd.drawText(0, 12, "FM0 Curve") + fields[1][4]=1 + lcd.drawText(0, 32, "FM1 Curve") + fields[2][4]=1 + lcd.drawText(0, 54, "FM2 Curve") + fields[3][4]=1 + local result = runFieldsPage(event) + return result +end + +local AilerFields = { + {0, 12, COMBO, 1, 0, { "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8" } }, +} + +local function runAilerConfig(event) + lcd.clear() + fields = AilerFields + lcd.drawScreenTitle("Aileron Channel",5,9) + fields[1][4]=1 + local result = runFieldsPage(event) + return result +end + +local EleFields = { + {0, 12, COMBO, 1, 1, { "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8" } }, +} + +local function runEleConfig(event) + lcd.clear() + fields = EleFields + lcd.drawScreenTitle("Elevator Channel",6,9) + fields[1][4]=1 + local result = runFieldsPage(event) + return result +end + +local RudFields = { + {0, 12, COMBO, 1, 3, { "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8" } }, +} + +local function runRudConfig(event) + lcd.clear() + fields = RudFields + lcd.drawScreenTitle("Rudder (Tail) Channel",7,9) + fields[1][4]=1 + local result = runFieldsPage(event) + return result +end + +local lineIndex + +local function drawNextChanelLine(text, text2) + lcd.drawText(0, lineIndex, text) + lcd.drawText(55, lineIndex, ": CH" .. text2 + 1) + lineIndex = lineIndex + 9 +end + +local function drawNextSwitchLine(text, text2) + lcd.drawText(0, lineIndex, text) + lcd.drawText(55, lineIndex, ": " ..switches[text2 + 1]) + lineIndex = lineIndex + 9 +end + +local function drawNextTextLine(text, text2) + lcd.drawText(0, lineIndex, text) + lcd.drawText(55, lineIndex, ": " ..text2) + lineIndex = lineIndex + 9 +end + +local function switchLine(text) + text=SwitchFields[2][5] + getFieldInfo(text) + swnum=text.id +end + +local SummaryFields = { + {0, 56, {1}}, +} + +local function runSummary(event) + lcd.clear() + lcd.drawScreenTitle("Summary", 8,9) + fields = SummaryFields + lineIndex = 9 + + -- Type + if TypeFields[1][5]==0 then + drawNextTextLine("TYPE","FBL") + else + drawNextTextLine("TYPE","FB") + if TypeFields[2][5]==0 then + lcd.drawText(75,9,"120") + elseif TypeFields[2][5]==1 then + lcd.drawText(75,9,"120X") + elseif TypeFields[2][5]==2 then + lcd.drawText(75,9,"140") + else + lcd.drawText(75,9,"90") + end + end + + -- Style + if StyleFields[1][5]==0 then + drawNextTextLine("Style","Sport") + elseif StyleFields[1][5]==1 then + drawNextTextLine("Style","Light 3D") + else + drawNextTextLine("Style","Full 3D") + end + + -- Switch + drawNextSwitchLine("FM SW",SwitchFields[1][5]) + drawNextSwitchLine("Th Hold SW",SwitchFields[2][5]) + if TypeFields[1][5]==1 then + drawNextSwitchLine("Gyro SW",SwitchFields[3][5]) + end + + -- thr + drawNextChanelLine("Throttle",ThrFields[1][5]) + fields[1][4]=1 + local result = runFieldsPage(event) + return result +end + +local ConfigSummaryFields = { + {10, 50,{1}}, +} + +local function runConfigSummary(event) + lcd.clear() + lcd.drawScreenTitle("Long Enter -> Create", 9,9) + fields = ConfigSummaryFields + lineIndex = 8 + + -- FM0 Curve + if CurveFields[1][5]==0 then + drawNextTextLine("FM0 Curve","Throttle Up") + elseif CurveFields[1][5]==1 then + drawNextTextLine("FM0 Curve","V Style") + else + drawNextTextLine("FM0 Curve","Flat Style") + end + + -- FM1 Curve + if CurveFields[2][5]==0 then + drawNextTextLine("FM1 Curve","V Style") + else + drawNextTextLine("FM1 Curve","Flat Style") + end + + -- FM3 Curve + if CurveFields[3][5]==0 then + drawNextTextLine("FM2 Curve","V Style") + else + drawNextTextLine("FM2 Curve","Flat Style") + end + + -- Ail + drawNextChanelLine("Aileron",AilerFields[1][5]) + + -- Elev + drawNextChanelLine("Elevator",EleFields[1][5]) + + -- Rudder + drawNextChanelLine("Rudder",RudFields[1][5]) + fields[1][4]=1 + local result = runFieldsPage(event) + return result +end + +local function runCreateModel(event) + lcd.clear() + local b = SwitchFields[1][5] + local tUp = switchValues[b] + local i = SwitchFields[2][5] + local hold = switchValues[i] + local f = SwitchFields[3][5] + local gyRate = switchValues[f] + model.defaultInputs() + model.deleteMixes() + + -- Curve Fm0 + if StyleFields[1][5]==0 and CurveFields[1][5]==0 then + model.setCurve(0,{name="TC0",y={-100, 0, 0, 40, 40}}) + elseif StyleFields[1][5]==1 and CurveFields[1][5]==0 then + model.setCurve(0,{name="TC0",y={-100, 0, 35, 50, 50}}) + elseif StyleFields[1][5]==2 and CurveFields[1][5]==0 then + model.setCurve(0,{name="TC0",y={-100, 0, 40, 80, 80}}) + elseif StyleFields[1][5]==0 and CurveFields[1][5]==1 then + model.setCurve(0,{name="TC0",y={50, 40, 50}}) + elseif StyleFields[1][5]==1 and CurveFields[1][5]==1 then + model.setCurve(0,{name="TC0",y={65, 55, 65}}) + elseif StyleFields[1][5]==2 and CurveFields[1][5]==1 then + model.setCurve(0,{name="TC0",y={70, 60, 70}}) + elseif StyleFields[1][5]==0 and CurveFields[1][5]==2 then + model.setCurve(0,{name="TC0",y={60,60,60}}) + elseif StyleFields[1][5]==1 and CurveFields[1][5]==2 then + model.setCurve(0,{name="TC0",y={65,65,65}}) + else + model.setCurve(0,{name="TC0",y={70,70,70}}) + end + + --Curve FM1 + if StyleFields[1][5]==0 and CurveFields[2][5]==0 then + model.setCurve(1,{name="TC1",y={60, 50, 60}}) + elseif StyleFields[1][5]==1 and CurveFields[2][5]==0 then + model.setCurve(1,{name="TC1",y={70, 60, 70}}) + elseif StyleFields[1][5]==2 and CurveFields[2][5]==0 then + model.setCurve(1,{name="TC1",y={85, 75, 85}}) + elseif StyleFields[1][5]==0 and CurveFields[2][5]==1 then + model.setCurve(1,{name="TC1",y={65,65,65}}) + elseif StyleFields[1][5]==1 and CurveFields[2][5]==1 then + model.setCurve(1,{name="TC1",y={70,70,70}}) + else + model.setCurve(1,{name="TC1",y={85 ,85,85}}) + end + + --Curve FM2 + if StyleFields[1][5]>=0 and CurveFields[3][5]==0 then + model.setCurve(2,{name="TC2",y={70, 60, 70}}) + elseif StyleFields[1][5]==1 and CurveFields[3][5]==0 then + model.setCurve(2,{name="TC2",y={85, 70, 85}}) + elseif StyleFields[1][5]==2 and CurveFields[3][5]==0 then + model.setCurve(2,{name="TC2",y={100, 90, 100}}) + elseif StyleFields[1][5]==0 and CurveFields[3][5]==1 then + model.setCurve(2,{name="TC2",y={75 ,75,75}}) + elseif StyleFields[1][5]==1 and CurveFields[3][5]==1 then + model.setCurve(2,{name="TC2",y={85 ,85, 85}}) + else + model.setCurve(2,{name="TC2",y={95 ,95, 95}}) + end + + --Curve TH Hold + model.setCurve(3,{name="THD",y={-100,-100,-100}}) + + -- Throttle + model.insertMix(ThrFields[1][5], 0,{name="Th0",weight=100,curveType=3,curveValue=1}) + model.insertMix(ThrFields[1][5], 1,{name="Th1",weight=100,switch=tUp,multiplex=2,curveType=3,curveValue=2}) + model.insertMix(ThrFields[1][5], 2,{name="Th2",weight=100,switch=tUp-1,multiplex=2,curveType=3,curveValue=3}) + model.insertMix(ThrFields[1][5], 3,{name="Hld",weight=100,offset=-15,switch=hold+1,multiplex=2,curveType=3,curveValue=4}) + model.setOutput(ThrFields[1][5],{name="Thrt"}) + + -- Ail + if TypeFields[1][5] == 0 then + model.insertMix(AilerFields[1][5], 0,{name="Ail",weight=100}) + model.setOutput(AilerFields[1][5],{name="Aile"}) + else + model.insertMix(AilerFields[1][5], 0,{source=83,name="Ail",weight=100}) + model.setOutput(AilerFields[1][5],{name="Aile"}) + end + + -- Elev + if TypeFields[1][5] == 0 then + model.insertMix(EleFields[1][5], 0,{name="Ele",weight=100}) + model.setOutput(EleFields[1][5],{name="Elev"}) + else + model.insertMix(EleFields[1][5], 0,{source=82,name="Ele",weight=100}) + model.setOutput(EleFields[1][5],{name="Elev"}) + end + + -- Rudder + model.insertMix(RudFields[1][5], 0,{name="Rud",weight=100}) + model.setOutput(RudFields[1][5],{name="Rud"}) + + -- Gyro + if TypeFields[1][5] == 0 then + model.insertMix(4, 0,{source=81,name="T.Ga",weight=25}) + model.setOutput(4,{name="T.Ga"}) + else + model.insertMix( 4, 0,{source=81,name="HHold",weight=25}) + model.insertMix( 4, 1,{source=81,name="Rate",weight=-25,switch=gyRate,multiplex=2}) + model.setOutput(4,{name="T.Ga"}) + end + + -- Pitch + if TypeFields[1][5] == 0 then + model.insertMix(5, 0,{source=77,name="Pch",weight=100}) + model.setOutput(5,{name="Ptch"}) + else + model.insertMix(5, 0,{source=84,name="Pch",weight=100}) + model.setOutput(5,{name="Ptch"}) + end + + --Set Swash Parameters + if TypeFields[1][5]==1 and TypeFields[2][5]==0 then + model.setSwashRing({type="1",collectiveSource=77,aileronSource=78,elevatorSource=76,collectiveWeight=60,aileronWeight=60,elevatorWeight=60}) + elseif TypeFields[2][5]==1 then + model.setSwashRing({type="2",collectiveSource=77,aileronSource=78,elevatorSource=76,collectiveWeight=60,aileronWeight=60,elevatorWeight=60}) + elseif TypeFields[2][5]==2 then + model.setSwashRing({type="3",collectiveSource=77,aileronSource=78,elevatorSource=76,collectiveWeight=40,aileronWeight=40,elevatorWeight=60}) + elseif TypeFields[2][5]==3 then + model.setSwashRing({type="4",collectiveSource=77,aileronSource=78,elevatorSource=76,collectiveWeight=35,aileronWeight=35,elevatorWeight=60}) + end +end + +-- Init +local function init() + current, edit = 1, false + pages = { + runTypeConfig, + runStyleConfig, + runSwitchConfig, + runThrConfig, + runCurveConfig, + runAilerConfig, + runEleConfig, + runRudConfig, + runSummary, + runConfigSummary, + } +end + +-- Main +local function run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_NEXT_PAGE and page < #pages then + selectPage(1) + elseif event == EVT_VIRTUAL_ENTER_LONG and page == #pages then + runCreateModel(event) + lcd.drawText(0,15,"Model Sucessfully created !") + return 2 + elseif event == EVT_VIRTUAL_PREV_PAGE and page > 1 then + killEvents(event); + selectPage(-1) + end + local result = pages[page](event) + return result +end + +return { init=init, run=run } diff --git a/SCRIPTS/WIZARD/multi.lua b/SCRIPTS/WIZARD/multi.lua new file mode 100755 index 0000000..8878447 --- /dev/null +++ b/SCRIPTS/WIZARD/multi.lua @@ -0,0 +1,440 @@ +---- ######################################################################### +---- # # +---- # 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. # +---- # # +---- ######################################################################### +-- Multicopter Wizard pages +local THROTTLE_PAGE = 0 +local ROLL_PAGE = 1 +local PITCH_PAGE = 2 +local YAW_PAGE = 3 +local ARM_PAGE = 4 +local MODE_PAGE = 5 +local BEEPER_PAGE = 6 +local CONFIRMATION_PAGE = 7 + +-- Navigation variables +local page = THROTTLE_PAGE +local dirty = true +local edit = false +local field = 0 +local fieldsMax = 0 +local comboBoxMode = 0 -- Scrap variable +local validSwitch = {} + +-- Model settings +local thrCH1 = 0 +local rollCH1 = 0 +local yawCH1 = 0 +local pitchCH1 = 0 +local armSW1 = 1 +local beeperSW1 = 1 +local modeSW1 = 1 +local switches = {} + +-- Common functions +local lastBlink = 0 +local function blinkChanged() + local time = getTime() % 128 + local blink = (time - time % 64) / 64 + if blink ~= lastBlink then + lastBlink = blink + return true + else + return false + end +end + +local function fieldIncDec(event, value, max, force) + if edit or force==true then + if event == EVT_VIRTUAL_INC then + value = (value + max) + dirty = true + elseif event == EVT_VIRTUAL_DEC then + value = (value + max + 2) + dirty = true + end + value = (value % (max+1)) + end + return value +end + +local function valueIncDec(event, value, min, max) + if edit then + if event == EVT_VIRTUAL_INC or event == EVT_VIRTUAL_INC_REPT then + if value < max then + value = (value + 1) + dirty = true + end + elseif event == EVT_VIRTUAL_DEC or event == EVT_VIRTUAL_DEC_REPT then + if value > min then + value = (value - 1) + dirty = true + end + end + end + return value +end + +local function navigate(event, fieldMax, prevPage, nextPage) + if event == EVT_VIRTUAL_ENTER then + edit = not edit + dirty = true + elseif edit then + if event == EVT_VIRTUAL_EXIT then + edit = false + dirty = true + elseif not dirty then + dirty = blinkChanged() + end + else + if event == EVT_VIRTUAL_NEXT_PAGE then + page = nextPage + field = 0 + dirty = true + elseif event == EVT_VIRTUAL_PREV_PAGE then + page = prevPage + field = 0 + killEvents(event); + dirty = true + else + field = fieldIncDec(event, field, fieldMax, true) + end + end +end + +local function getFieldFlags(position) + flags = 0 + if field == position then + flags = INVERS + if edit then + flags = INVERS + BLINK + end + end + return flags +end + +local function channelIncDec(event, value) + if not edit and event==EVT_VIRTUAL_MENU then + servoPage = value + dirty = true + else + value = valueIncDec(event, value, 0, 15) + end + return value +end + +local function switchValueIncDec(event, value, min, max) + if edit then + if event == EVT_VIRTUAL_INC or event == EVT_VIRTUAL_INC_REPT then + if value < max then + value = (value + 1) + dirty = true + end + elseif event == EVT_VIRTUAL_DEC or event == EVT_VIRTUAL_DEC_REPT then + if value > min then + value = (value - 1) + dirty = true + end + end + end + return value +end + +local function switchIncDec(event, value) + if not edit and event== EVT_VIRTUAL_MENU then + servoPage = value + dirty = true + else + value = switchValueIncDec(event, value, 1, #switches) + end + return value +end + +-- Init function +local function init() + thrCH1 = defaultChannel(2) + rollCH1 = defaultChannel(3) + yawCH1 = defaultChannel(0) + pitchCH1 = defaultChannel(1) + local ver, radio, maj, minor, rev = getVersion() + if string.match(radio, "x7") then + switches = {"SA", "SB", "SC", "SD", "SF", "SH"} + elseif string.match(radio, "tx12") then + switches = {"SA", "SB", "SC", "SD", "SE", "SF"} + else + switches = {"SA", "SB", "SC", "SD"} + end +end + +-- Throttle Menu +local function drawThrottleMenu() + lcd.clear() + lcd.drawText(1, 0, "Multicopter", 0) + lcd.drawFilledRectangle(0, 0, LCD_W, 8, FILL_WHITE) + lcd.drawCombobox(0, 8, LCD_W, {"Throttle"}, comboBoxMode, getFieldFlags(1)) + lcd.drawText(5, 30, "Assign channel", 0); + lcd.drawText(5, 40, ">>>", 0); + lcd.drawSource(25, 40, MIXSRC_CH1+thrCH1, getFieldFlags(0)) + fieldsMax = 0 +end + +local function throttleMenu(event) + if dirty then + dirty = false + drawThrottleMenu() + end + navigate(event, fieldsMax, page, page+1) + thrCH1 = channelIncDec(event, thrCH1) +end + +-- Roll Menu +local function drawRollMenu() + lcd.clear() + lcd.drawText(1, 0, "Multicopter", 0) + lcd.drawFilledRectangle(0, 0, LCD_W, 8, FILL_WHITE) + lcd.drawCombobox(0, 8, LCD_W, {"Roll"}, comboBoxMode, getFieldFlags(1)) + lcd.drawText(5, 30, "Assign channel", 0); + lcd.drawText(5, 40, ">>>", 0); + lcd.drawSource(25, 40, MIXSRC_CH1+rollCH1, getFieldFlags(0)) + fieldsMax = 0 +end + +local function rollMenu(event) + if dirty then + dirty = false + drawRollMenu() + end + navigate(event, fieldsMax, page-1, page+1) + rollCH1 = channelIncDec(event, rollCH1) +end + +-- Pitch Menu +local function drawPitchMenu() + lcd.clear() + lcd.drawText(1, 0, "Multicopter", 0) + lcd.drawFilledRectangle(0, 0, LCD_W, 8, FILL_WHITE) + lcd.drawCombobox(0, 8, LCD_W, {"Pitch"}, comboBoxMode, getFieldFlags(1)) + lcd.drawText(5, 30, "Assign channel", 0); + lcd.drawText(5, 40, ">>>", 0); + lcd.drawSource(25, 40, MIXSRC_CH1+pitchCH1, getFieldFlags(0)) + fieldsMax = 0 +end + +local function pitchMenu(event) + if dirty then + dirty = false + drawPitchMenu() + end + navigate(event, fieldsMax, page-1, page+1) + pitchCH1 = channelIncDec(event, pitchCH1) +end + +-- Yaw Menu +local function drawYawMenu() + lcd.clear() + lcd.drawText(1, 0, "Multicopter", 0) + lcd.drawFilledRectangle(0, 0, LCD_W, 8, FILL_WHITE) + lcd.drawCombobox(0, 8, LCD_W, {"Yaw"}, comboBoxMode, getFieldFlags(1)) + lcd.drawText(5, 30, "Assign channel", 0); + lcd.drawText(5, 40, ">>>", 0); + lcd.drawSource(25, 40, MIXSRC_CH1+yawCH1, getFieldFlags(0)) + fieldsMax = 0 +end + +local function yawMenu(event) + if dirty then + dirty = false + drawYawMenu() + end + navigate(event, fieldsMax, page-1, page+1) + yawCH1 = channelIncDec(event, yawCH1) +end + +-- Arm Menu +local function drawArmMenu() + lcd.clear() + lcd.drawText(1, 0, "Multicopter", 0) + lcd.drawFilledRectangle(0, 0, LCD_W, 8, FILL_WHITE) + lcd.drawCombobox(0, 8, LCD_W, {"Arm"}, comboBoxMode, getFieldFlags(1)) + lcd.drawText(5, 30, "Assign AUX1", 0); + lcd.drawText(5, 40, ">>>", 0); + lcd.drawText(25, 40, switches[armSW1], getFieldFlags(0)) + fieldsMax = 0 +end + +local function armMenu(event) + if dirty then + dirty = false + drawArmMenu() + end + navigate(event, fieldsMax, page-1, page+1) + armSW1 = switchIncDec(event, armSW1) +end + +-- Beeper Menu +local function drawbeeperMenu() + lcd.clear() + lcd.drawText(1, 0, "Multicopter", 0) + lcd.drawFilledRectangle(0, 0, LCD_W, 8, FILL_WHITE) + lcd.drawCombobox(0, 8, LCD_W, {"Beeper"}, comboBoxMode, getFieldFlags(1)) + lcd.drawText(5, 30, "Assign AUX2", 0); + lcd.drawText(5, 40, ">>>", 0); + lcd.drawText(25, 40, switches[beeperSW1], getFieldFlags(0)) + fieldsMax = 0 +end + +local function beeperMenu(event) + if dirty then + dirty = false + drawbeeperMenu() + end + navigate(event, fieldsMax, page-1, page+1) + beeperSW1 = switchIncDec(event, beeperSW1) +end + +-- Mode Menu +local function drawmodeMenu() + lcd.clear() + lcd.drawText(1, 0, "Multicopter", 0) + lcd.drawFilledRectangle(0, 0, LCD_W, 8, FILL_WHITE) + lcd.drawCombobox(0, 8, LCD_W, {"Mode"}, comboBoxMode, getFieldFlags(1)) + lcd.drawText(5, 30, "Assign AUX3", 0); + lcd.drawText(5, 40, ">>>", 0); + lcd.drawText(25, 40, switches[modeSW1], getFieldFlags(0)) + fieldsMax = 0 +end + +local function modeMenu(event) + if dirty then + dirty = false + drawmodeMenu() + end + navigate(event, fieldsMax, page-1, page+1) + modeSW1 = switchIncDec(event, modeSW1) +end + +-- Confirmation Menu +local function drawNextLine(x, y, label, channel) + lcd.drawText(x, y, label, 0); + lcd.drawText(x+46, y, ":", 0); + lcd.drawSource(x+50, y, MIXSRC_CH1+channel, 0) + y = y + 8 + if y > 50 then + y = 12 + x = 120 + end + return x, y +end + +local function drawNextSWLine(x, y, label, switch) + lcd.drawText(x, y, label, 0); + lcd.drawText(x+38, y, ":", 0); + lcd.drawText(x+42, y, switches[switch], 0) + y = y + 8 + if y > 50 then + y = 12 + x = 120 + end + return x, y +end + +local function drawConfirmationMenu() + local x = 1 + local y = 12 + lcd.clear() + lcd.drawText(0, 1, "Ready to go?", 0); + lcd.drawFilledRectangle(0, 0, LCD_W, 9, 0) + x, y = drawNextLine(x, y, "Throttle", thrCH1) + x, y = drawNextLine(x, y, "Roll", rollCH1) + x, y = drawNextLine(x, y, "Pitch", pitchCH1) + x, y = drawNextLine(x, y, "Yaw", yawCH1) + local x = 72 + local y = 12 + x, y = drawNextSWLine(x, y, "Arm", armSW1) + x, y = drawNextSWLine(x, y, "Mode", modeSW1) + x, y = drawNextSWLine(x, y, "Beeper", beeperSW1) + lcd.drawText(0, LCD_H-8, "[Enter Long] to confirm", 0); + lcd.drawFilledRectangle(0, LCD_H-9, LCD_W, 9, 0) + fieldsMax = 0 +end + +local function addMix(channel, input, name, weight, index) + local mix = { source=input, name=name } + if weight ~= nil then + mix.weight = weight + end + if index == nil then + index = 0 + end + model.insertMix(channel, index, mix) +end + +local function applySettings() + model.defaultInputs() + model.deleteMixes() + addMix(thrCH1, MIXSRC_FIRST_INPUT+defaultChannel(2), "Throttle") + addMix(rollCH1, MIXSRC_FIRST_INPUT+defaultChannel(3), "Roll") + addMix(yawCH1, MIXSRC_FIRST_INPUT+defaultChannel(0), "Yaw") + addMix(pitchCH1, MIXSRC_FIRST_INPUT+defaultChannel(1), "Pitch") + addMix(4, MIXSRC_SA + armSW1 - 1, "Arm") + addMix(5, MIXSRC_SA + beeperSW1 - 1, "Beeper") + addMix(6, MIXSRC_SA + modeSW1 - 1, "Mode") +end + +local function confirmationMenu(event) + if dirty then + dirty = false + drawConfirmationMenu() + end + + navigate(event, fieldsMax, BEEPER_PAGE, page) + + if event == EVT_VIRTUAL_EXIT then + return 2 + elseif event == EVT_VIRTUAL_ENTER_LONG then + killEvents(event) + applySettings() + return 2 + else + return 0 + end +end + +-- Main +local function run(event) + if event == nil then + error("Cannot be run as a model script!") + end + if page == THROTTLE_PAGE then + throttleMenu(event) + elseif page == ROLL_PAGE then + rollMenu(event) + elseif page == YAW_PAGE then + yawMenu(event) + elseif page == PITCH_PAGE then + pitchMenu(event) + elseif page == ARM_PAGE then + armMenu(event) + elseif page == BEEPER_PAGE then + beeperMenu(event) + elseif page == MODE_PAGE then + modeMenu(event) + elseif page == CONFIRMATION_PAGE then + return confirmationMenu(event) + end + return 0 +end + +return { init=init, run=run } diff --git a/SCRIPTS/WIZARD/plane.lua b/SCRIPTS/WIZARD/plane.lua new file mode 100755 index 0000000..0937bc0 --- /dev/null +++ b/SCRIPTS/WIZARD/plane.lua @@ -0,0 +1,582 @@ +---- ######################################################################### +---- # # +---- # 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. # +---- # # +---- ######################################################################### +-- Plane Wizard pages +local ENGINE_PAGE = 0 +local AILERONS_PAGE = 1 +local FLAPERONS_PAGE = 2 +local BRAKES_PAGE = 3 +local TAIL_PAGE = 4 +local CONFIRMATION_PAGE = 5 + +-- Navigation variables +local page = ENGINE_PAGE +local dirty = true +local edit = false +local field = 0 +local fieldsMax = 0 + +-- Model settings +local engineMode = 1 +local thrCH1 = 0 +local aileronsMode = 1 +local ailCH1 = 0 +local ailCH2 = 5 +local flapsMode = 0 +local flapsCH1 = 6 +local flapsCH2 = 7 +local brakesMode = 0 +local brakesCH1 = 8 +local brakesCH2 = 9 +local tailMode = 1 +local eleCH1 = 0 +local eleCH2 = 4 +local rudCH1 = 0 +local servoPage = nil + +-- Common functions +local lastBlink = 0 +local function blinkChanged() + local time = getTime() % 128 + local blink = (time - time % 64) / 64 + if blink ~= lastBlink then + lastBlink = blink + return true + else + return false + end +end + +local function fieldIncDec(event, value, max, force) + if edit or force==true then + if event == EVT_VIRTUAL_DEC or event == EVT_VIRTUAL_DEC_REPT then + value = (value + max) + dirty = true + elseif event == EVT_VIRTUAL_INC or event == EVT_VIRTUAL_INC_REPT then + value = (value + max + 2) + dirty = true + end + value = (value % (max+1)) + end + return value +end + +local function valueIncDec(event, value, min, max) + if edit then + if event == EVT_VIRTUAL_INC or event == EVT_VIRTUAL_INC_REPT then + if value < max then + value = (value + 1) + dirty = true + end + elseif event == EVT_VIRTUAL_DEC or event == EVT_VIRTUAL_DEC_REPT then + if value > min then + value = (value - 1) + dirty = true + end + end + end + return value +end + +local function navigate(event, fieldMax, prevPage, nextPage) + if event == EVT_VIRTUAL_ENTER then + edit = not edit + dirty = true + elseif edit then + if event == EVT_VIRTUAL_EXIT then + edit = false + dirty = true + elseif not dirty then + dirty = blinkChanged() + end + else + if event == EVT_VIRTUAL_NEXT_PAGE then + page = nextPage + field = 0 + dirty = true + elseif event == EVT_VIRTUAL_PREV_PAGE then + page = prevPage + field = 0 + killEvents(event); + dirty = true + else + field = fieldIncDec(event, field, fieldMax, true) + end + end +end + +local function getFieldFlags(position) + flags = 0 + if field == position then + flags = INVERS + if edit then + flags = INVERS + BLINK + end + end + return flags +end + +local function channelIncDec(event, value) + if not edit and event==EVT_VIRTUAL_MENU then + servoPage = value + dirty = true + else + value = valueIncDec(event, value, 0, 15) + end + return value +end + +-- Init function +local function init() + rudCH1 = defaultChannel(0) + eleCH1 = defaultChannel(1) + thrCH1 = defaultChannel(2) + ailCH1 = defaultChannel(3) +end + +-- Engine Menu +local engineModeItems = {"No", "Yes"} +local function drawEngineMenu() + lcd.clear() + if engineMode == 1 then + -- 1 channel + lcd.drawText(5, 30, "Assign channel", 0); + lcd.drawText(5, 40, ">>>", 0); + lcd.drawSource(25, 40, MIXSRC_CH1+thrCH1, getFieldFlags(1)) + fieldsMax = 1 + else + -- No engine + fieldsMax = 0 + end + lcd.drawText(1, 0, "Got an engine?", 0) + lcd.drawFilledRectangle(0, 0, LCD_W, 8, FILL_WHITE) + lcd.drawCombobox(0, 8, LCD_W, engineModeItems, engineMode, getFieldFlags(0)) +end + +local function engineMenu(event) + if dirty then + dirty = false + drawEngineMenu() + end + + navigate(event, fieldsMax, page, page+1) + + if field==0 then + engineMode = fieldIncDec(event, engineMode, 1) + elseif field==1 then + thrCH1 = channelIncDec(event, thrCH1) + end +end + +-- Ailerons Menu +local aileronsModeItems = {"No", "Yes, 1 channel", "Yes, 2 channels"} +local function drawAileronsMenu() + lcd.clear() + if aileronsMode == 2 then + -- 2 channels + lcd.drawText(5, 30, "Assign channels", 0); + lcd.drawText(30, 40, "L", 0); + lcd.drawText(65, 40, "R", 0); + lcd.drawText(5, 50, ">>>", 0); + lcd.drawSource(25, 50, MIXSRC_CH1+ailCH1, getFieldFlags(1)) + lcd.drawSource(60, 50, MIXSRC_CH1+ailCH2, getFieldFlags(2)) + fieldsMax = 2 + elseif aileronsMode == 1 then + -- 1 channel + lcd.drawText(5, 30, "Assign channel", 0); + lcd.drawText(5, 40, ">>>", 0); + lcd.drawSource(25, 40, MIXSRC_CH1+ailCH1, getFieldFlags(1)) + fieldsMax = 1 + else + -- No ailerons + fieldsMax = 0 + end + lcd.drawText(1, 0, "Got ailerons?", 0) + lcd.drawFilledRectangle(0, 0, LCD_W, 8, FILL_WHITE) + lcd.drawCombobox(0, 8, LCD_W, aileronsModeItems, aileronsMode, getFieldFlags(0)) +end + +local function aileronsMenu(event) + if dirty then + dirty = false + drawAileronsMenu() + end + + navigate(event, fieldsMax, page-1, page+1) + + if field==0 then + aileronsMode = fieldIncDec(event, aileronsMode, 2) + elseif field==1 then + ailCH1 = channelIncDec(event, ailCH1) + elseif field==2 then + ailCH2 = channelIncDec(event, ailCH2) + end +end + +-- Flaps Menu +local flapsModeItems = {"No", "Yes, 1 channel", "Yes, 2 channels"} +local function drawFlapsMenu() + lcd.clear() + if flapsMode == 0 then + -- no flaps + fieldsMax = 0 + elseif flapsMode == 1 then + -- 1 channel + lcd.drawText(5, 30, "Assign channel", 0); + lcd.drawText(5, 40, ">>>", 0); + lcd.drawSource(25, 40, MIXSRC_CH1+flapsCH1, getFieldFlags(1)) + fieldsMax = 1 + elseif flapsMode == 2 then + -- 2 channels + lcd.drawText(5, 30, "Assign channels", 0); + lcd.drawText(30, 40, "L", 0); + lcd.drawText(65, 40, "R", 0); + lcd.drawText(5, 50, ">>>", 0); + lcd.drawSource(25, 50, MIXSRC_CH1+flapsCH1, getFieldFlags(1)) + lcd.drawSource(60, 50, MIXSRC_CH1+flapsCH2, getFieldFlags(2)) + fieldsMax = 2 + end + lcd.drawText(1, 0, "Got flaps?", 0) + lcd.drawFilledRectangle(0, 0, LCD_W, 8, FILL_WHITE) + lcd.drawCombobox(0, 8, LCD_W, flapsModeItems, flapsMode, getFieldFlags(0)) +end + +local function flapsMenu(event) + if dirty then + dirty = false + drawFlapsMenu() + end + + navigate(event, fieldsMax, page-1, page+1) + + if field==0 then + flapsMode = fieldIncDec(event, flapsMode, 2) + elseif field==1 then + flapsCH1 = channelIncDec(event, flapsCH1) + elseif field==2 then + flapsCH2 = channelIncDec(event, flapsCH2) + end +end + +-- Airbrakes Menu +local brakesModeItems = {"No", "Yes, 1 channel", "Yes, 2 channels"} +local function drawBrakesMenu() + lcd.clear() + if brakesMode == 0 then + -- no brakes + fieldsMax = 0 + elseif brakesMode == 1 then + -- 1 channel + lcd.drawText(5, 30, "Assign channel", 0); + lcd.drawText(5, 40, ">>>", 0); + lcd.drawSource(25, 40, MIXSRC_CH1+brakesCH1, getFieldFlags(1)) + fieldsMax = 1 + elseif brakesMode == 2 then + -- 2 channels + lcd.drawText(5, 30, "Assign channels", 0); + lcd.drawText(30, 40, "L", 0); + lcd.drawText(65, 40, "R", 0); + lcd.drawText(5, 50, ">>>", 0); + lcd.drawSource(25, 50, MIXSRC_CH1+brakesCH1, getFieldFlags(1)) + lcd.drawSource(60, 50, MIXSRC_CH1+brakesCH2, getFieldFlags(2)) + fieldsMax = 2 + end + lcd.drawText(1, 0, "Got air brakes?", 0) + lcd.drawFilledRectangle(0, 0, LCD_W, 8, FILL_WHITE) + lcd.drawCombobox(0, 8, LCD_W, brakesModeItems, brakesMode, getFieldFlags(0)) +end + +local function brakesMenu(event) + if dirty then + dirty = false + drawBrakesMenu() + end + + navigate(event, fieldsMax, page-1, page+1) + + if field==0 then + brakesMode = fieldIncDec(event, brakesMode, 2) + elseif field==1 then + brakesCH1 = channelIncDec(event, brakesCH1) + elseif field==2 then + brakesCH2 = channelIncDec(event, brakesCH2) + end +end + +-- Tail Menu +local tailModeItems = {"Ele(1)", "Ele(1) + Ruder(1)", "Ele(2) + Ruder(1)", "V-Tail(2)"} +local function drawTailMenu() + lcd.clear() + if tailMode == 0 then + -- Elevator(1ch), no rudder... + lcd.drawText(5, 30, "Assign channel", 0); + lcd.drawText(5, 40, ">>>", 0); + lcd.drawSource(25, 40, MIXSRC_CH1+eleCH1, getFieldFlags(1)) + fieldsMax = 1 + elseif tailMode == 1 then + -- Elevator(1ch) + rudder... + lcd.drawText(5, 30, "Assign channels", 0); + lcd.drawText(25, 40, "Ele", 0); + lcd.drawText(60, 40, "Rud", 0); + lcd.drawText(5, 50, ">>>", 0); + lcd.drawSource(25, 50, MIXSRC_CH1+eleCH1, getFieldFlags(1)) + lcd.drawSource(60, 50, MIXSRC_CH1+rudCH1, getFieldFlags(2)) + fieldsMax = 2 + elseif tailMode == 2 then + -- Elevator(2ch) + rudder... + lcd.drawText(5, 30, "Assign channels", 0); + lcd.drawText(25, 40, "EleL", 0); + lcd.drawText(60, 40, "EleR", 0); + lcd.drawText(95, 40, "Rud", 0); + lcd.drawText(5, 50, ">>>", 0); + lcd.drawSource(25, 50, MIXSRC_CH1+eleCH1, getFieldFlags(1)) + lcd.drawSource(60, 50, MIXSRC_CH1+eleCH2, getFieldFlags(2)) + lcd.drawSource(95, 50, MIXSRC_CH1+rudCH1, getFieldFlags(3)) + fieldsMax = 3 + else + -- V-Tail... + lcd.drawText(5, 30, "Assign channels", 0); + lcd.drawText(25, 40, "VtaL", 0); + lcd.drawText(60, 40, "VtaR", 0); + lcd.drawText(5, 50, ">>>", 0); + lcd.drawSource(25, 50, MIXSRC_CH1+eleCH1, getFieldFlags(1)) + lcd.drawSource(60, 50, MIXSRC_CH1+eleCH2, getFieldFlags(2)) + fieldsMax = 2 + end + lcd.drawText(1, 0, "Tail config", 0) + lcd.drawFilledRectangle(0, 0, LCD_W, 8, FILL_WHITE) + lcd.drawCombobox(0, 8, LCD_W, tailModeItems, tailMode, getFieldFlags(0)) +end + +local function tailMenu(event) + if dirty then + dirty = false + drawTailMenu() + end + + navigate(event, fieldsMax, page-1, page+1) + + if field==0 then + tailMode = fieldIncDec(event, tailMode, 3) + elseif field==1 then + eleCH1 = channelIncDec(event, eleCH1) + elseif (field==2 and tailMode==1) or field==3 then + rudCH1 = channelIncDec(event, rudCH1) + elseif field==2 then + eleCH2 = channelIncDec(event, eleCH2) + end +end + +-- Servo (limits) Menu +local function drawServoMenu(limits) + lcd.clear() + lcd.drawSource(1, 0, MIXSRC_CH1+servoPage, 0) + lcd.drawText(25, 0, "servo min/max/center/direction?", 0) + lcd.drawFilledRectangle(0, 0, LCD_W, 8, FILL_WHITE) + lcd.drawLine(LCD_W/2-1, 8, LCD_W/2-1, LCD_H, DOTTED, 0) + lcd.drawText(LCD_W/2-19, LCD_H-8, ">>>", 0); + lcd.drawNumber(140, 35, limits.min, PREC1+getFieldFlags(0)); + lcd.drawNumber(205, 35, limits.max, PREC1+getFieldFlags(1)); + lcd.drawNumber(170, 9, limits.offset, PREC1+getFieldFlags(2)); + if limits.revert == 0 then + lcd.drawText(129, 50, "\126", getFieldFlags(3)); + else + lcd.drawText(129, 50, "\127", getFieldFlags(3)); + end + fieldsMax = 3 +end + +local function servoMenu(event) + local limits = model.getOutput(servoPage) + + if dirty then + dirty = false + drawServoMenu(limits) + end + + navigate(event, fieldsMax, page, page) + + if edit then + if field==0 then + limits.min = valueIncDec(event, limits.min, -1000, 0) + elseif field==1 then + limits.max = valueIncDec(event, limits.max, 0, 1000) + elseif field==2 then + limits.offset = valueIncDec(event, limits.offset, -1000, 1000) + elseif field==3 then + limits.revert = fieldIncDec(event, limits.revert, 1) + end + model.setOutput(servoPage, limits) + elseif event == EVT_VIRTUAL_EXIT then + servoPage = nil + dirty = true + end +end + +-- Confirmation Menu +local function drawNextLine(x, y, label, channel) + lcd.drawText(x, y, label, 0); + lcd.drawText(x+26, y, ":", 0); + lcd.drawSource(x+30, y, MIXSRC_CH1+channel, 0) + y = y + 8 + if y > 50 then + y = 12 + x = 70 + end + return x, y +end + +local function drawConfirmationMenu() + local x = 5 + local y = 12 + lcd.clear() + lcd.drawText(0, 1, "Ready to go?", 0); + lcd.drawFilledRectangle(0, 0, LCD_W, 9, 0) + if engineMode == 1 then + x, y = drawNextLine(x, y, "Thr", thrCH1) + end + if aileronsMode == 1 then + x, y = drawNextLine(x, y, "Ail", ailCH1) + elseif aileronsMode == 2 then + x, y = drawNextLine(x, y, "AilL", ailCH1) + x, y = drawNextLine(x, y, "AilR", ailCH2) + end + if flapsMode == 1 then + x, y = drawNextLine(x, y, "Flap", flapsCH1) + elseif flapsMode == 2 then + x, y = drawNextLine(x, y, "FlpL", flapsCH1) + x, y = drawNextLine(x, y, "FlpR", flapsCH2) + end + if brakesMode == 1 then + x, y = drawNextLine(x, y, "Brak", brakesCH1) + elseif brakesMode == 2 then + x, y = drawNextLine(x, y, "BrkL", brakesCH1) + x, y = drawNextLine(x, y, "BrkR", brakesCH2) + end + if tailMode == 3 then + x, y = drawNextLine(x, y, "VtaL", eleCH1) + x, y = drawNextLine(x, y, "VtaR", eleCH2) + else + x, y = drawNextLine(x, y, "Rud", rudCH1) + if tailMode == 1 then + x, y = drawNextLine(x, y, "Elev", eleCH1) + elseif tailMode == 2 then + x, y = drawNextLine(x, y, "EleL", eleCH1) + x, y = drawNextLine(x, y, "EleR", eleCH2) + end + end + lcd.drawText(0, LCD_H-8, "[Enter Long] to confirm", 0); + lcd.drawFilledRectangle(0, LCD_H-9, LCD_W, 9, 0) + fieldsMax = 0 +end + +local function addMix(channel, input, name, weight, index) + local mix = { source=input, name=name } + if weight ~= nil then + mix.weight = weight + end + if index == nil then + index = 0 + end + model.insertMix(channel, index, mix) +end + +local function applySettings() + model.defaultInputs() + model.deleteMixes() + if engineMode > 0 then + addMix(thrCH1, MIXSRC_FIRST_INPUT+defaultChannel(2), "Engine") + end + if aileronsMode == 1 then + addMix(ailCH1, MIXSRC_FIRST_INPUT+defaultChannel(3), "Ail") + elseif aileronsMode == 2 then + addMix(ailCH1, MIXSRC_FIRST_INPUT+defaultChannel(3), "AilL", -100) + addMix(ailCH2, MIXSRC_FIRST_INPUT+defaultChannel(3), "AilR") + end + if flapsMode == 1 then + addMix(flapsCH1, MIXSRC_SA, "Flap") + elseif flapsMode == 2 then + addMix(flapsCH1, MIXSRC_SA, "FlapL") + addMix(flapsCH2, MIXSRC_SA, "FlapR") + end + if brakesMode == 1 then + addMix(brakesCH1, MIXSRC_SD, "Brake") + elseif brakesMode == 2 then + addMix(brakesCH1, MIXSRC_SD, "BrakeL") + addMix(brakesCH2, MIXSRC_SD, "BrakeR") + end + if tailMode == 3 then + addMix(eleCH1, MIXSRC_FIRST_INPUT+defaultChannel(1), "V-EleL", 50) + addMix(eleCH1, MIXSRC_FIRST_INPUT+defaultChannel(0), "V-RudL", -50, 1) + addMix(eleCH2, MIXSRC_FIRST_INPUT+defaultChannel(1), "V-EleR", 50) + addMix(eleCH2, MIXSRC_FIRST_INPUT+defaultChannel(0), "V-RudR", 50, 1) + else + if tailMode > 0 then + addMix(rudCH1, MIXSRC_FIRST_INPUT+defaultChannel(0), "Rudder") + end + if tailMode == 1 then + addMix(eleCH1, MIXSRC_FIRST_INPUT+defaultChannel(1), "Elev") + elseif tailMode == 2 then + addMix(eleCH1, MIXSRC_FIRST_INPUT+defaultChannel(1), "ElevG") + addMix(eleCH2, MIXSRC_FIRST_INPUT+defaultChannel(1), "ElevD") + end + end +end + +local function confirmationMenu(event) + if dirty then + dirty = false + drawConfirmationMenu() + end + + navigate(event, fieldsMax, TAIL_PAGE, page) + + if event == EVT_VIRTUAL_EXIT then + return 2 + elseif event == EVT_VIRTUAL_ENTER_LONG then + killEvents(event) + applySettings() + return 2 + else + return 0 + end +end + +-- Main +local function run(event) + if event == nil then + error("Cannot be run as a model script!") + end + + if servoPage ~= nil then + servoMenu(event) + elseif page == ENGINE_PAGE then + engineMenu(event) + elseif page == AILERONS_PAGE then + aileronsMenu(event) + elseif page == FLAPERONS_PAGE then + flapsMenu(event) + elseif page == BRAKES_PAGE then + brakesMenu(event) + elseif page == TAIL_PAGE then + tailMenu(event) + elseif page == CONFIRMATION_PAGE then + return confirmationMenu(event) + end + return 0 +end + +return { init=init, run=run } diff --git a/SCRIPTS/WIZARD/wizard.lua b/SCRIPTS/WIZARD/wizard.lua new file mode 100755 index 0000000..61b8160 --- /dev/null +++ b/SCRIPTS/WIZARD/wizard.lua @@ -0,0 +1,87 @@ +---- ######################################################################### +---- # # +---- # Copyright (C) EdgeTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### + +-- Model types +local modelType = 0 +local MODELTYPE_PLANE = 0 +local MODELTYPE_DELTA = 1 +local MODELTYPE_QUAD = 2 +local MODELTYPE_HELI = 3 +-- Common functions +local function fieldIncDec(event, value, max) + if event == EVT_VIRTUAL_DEC or event == EVT_VIRTUAL_DEC_REPT then + value = (value - 1) + elseif event == EVT_VIRTUAL_INC or event == EVT_VIRTUAL_INC_REPT then + value = (value + max + 3) + end + value = (value % (max+2)) + return value +end + +-- Model Type Menu +local function modelTypeSurround(index) + if index <= 1 then + lcd.drawFilledRectangle(59*(index%2)+12, 13, 43, 23) + else + lcd.drawFilledRectangle(59*(index%2)+12, 34, 40, 20) + end +end + +local function drawModelChoiceMenu() + lcd.clear() + lcd.drawScreenTitle("Select model type", 0, 0) + lcd.drawText( 20, 20, "Plane") + lcd.drawText( 78, 20, "Delta") + lcd.drawText( 20, 40, "Multi") + lcd.drawText( 78, 40, "Heli") + modelTypeSurround(modelType) + fieldsMax = 0 +end + +local function modelTypeMenu(event) + drawModelChoiceMenu() + if event == EVT_VIRTUAL_ENTER then + if modelType == MODELTYPE_PLANE then + return "plane.lua" + elseif modelType == MODELTYPE_DELTA then + return "delta.lua" + elseif modelType == MODELTYPE_QUAD then + return "multi.lua" + elseif modelType == MODELTYPE_HELI then + return "heli.lua" + end + else + modelType = fieldIncDec(event, modelType, 2) + end + return 0 +end + +-- Main +local function run(event) + if event == nil then + error("Cannot be run as a model script!") + end + + if event == EVT_VIRTUAL_EXIT then + return 2 + end + + + return modelTypeMenu(event) +end + +return { run=run } diff --git a/SCRIPTS/WIZARD/wizard.luac b/SCRIPTS/WIZARD/wizard.luac new file mode 100755 index 0000000000000000000000000000000000000000..aa5a19f31e85b6ffc8f2eebabc8a1b0ebebf1576 GIT binary patch literal 1349 zcmZ`(T~8B16uq;ZE>K00q6SU+VF)owd^0}yG7Gd=0}UyK_|~lLpiP!SmTknhDTzL4 zj3)jEpZo*9?V89N|HL2RxwEZ8g=y~W?Y(F2-gEZu-d*qeTQvW5I(H{0c^&N#k@~SB za2YOfF%b?wnbDhwCQ|$(U?lg2e@_VlWol5_dffhZ@-b3DQ^LCH7`@FIIm#|qv>M;yj zE@E@RW&%tWlu3P~$cr%}IG?~46gerzBMVLcg{8-%L5yzx3J`460 zGz?!%Qu3bF;CYo+2)7+*Ka?}9$5uPky4UPRftIsdI)E^xz5R|3O3~W`ZD&+YF|etR zBCCRQHv_+?^L4K0uIeE2twhq@=m$|(W}oSxD|5SD9dr&i{39L)wBlZ%-V-F&I7wL^ z2XxHOn5q<0QDL-W*D#BBWJJ*zJN6#pyNDIcC`ZoUoEhd~IXd?*7Y}ok8RjTs&taZC zG+=8gft<;p4E@4AgeRzD$f?2SC&d_6(!MscXWx=ay;*6<+yM*oDCqk#2L&TPb@x~n z>rTCfH0-5ghuhF3OrRj9TnI;GgcMnhpIguIZyP2ka~+o=CX7D;{JsnfYeu3P<8z>( z89oOiKH!2Mk;X~%PTadKE?pgl`=Kna_`TkKRC=jP`0kYa!;(LI_=oMVdk~f7jdLtt Q)S9+)2gOj6d%tx30bu0l{Qv*} literal 0 HcmV?d00001 diff --git a/SCRIPTS/simulator.txt b/SCRIPTS/simulator.txt new file mode 100755 index 0000000..d871238 --- /dev/null +++ b/SCRIPTS/simulator.txt @@ -0,0 +1 @@ + 3 \ No newline at end of file diff --git a/SCRIPTS/snake.lua b/SCRIPTS/snake.lua new file mode 100755 index 0000000..b937f04 --- /dev/null +++ b/SCRIPTS/snake.lua @@ -0,0 +1,176 @@ +-------------------------------------------------------------- +-- Classic snake game +-- +-- 2009 Led Lab @PUC-Rio www.eluaproject.net +-- Dado Sutter +-- Ives Negreiros +-- To Benjamin +--------------------------------------------------------------- + +local xMax = math.floor( LCD_W / 6 ) - 1 +local yMax = math.floor( LCD_H / 8 ) - 1 +local game_map = {} + +local Head = {} +local Tail = {} + +local highscore = 0 +local size = 3 +Tail.x = 1 +Tail.y = 1 +Head.x = Tail.x + ( size - 1 ) +Head.y = Tail.y + +local Food = {} +Food.x = false +Food.y = false + +Head.dx = 1 +Head.dy = 0 +Tail.dx = Head.dx +Tail.dy = Head.dy +local direction = "right" +local score = 0 + +local function create_food() + Food.x, Food.y = math.random( xMax - 1), math.random( yMax - 1) + while game_map[ Food.x ][ Food.y ] do + Food.x, Food.y = math.random( xMax - 1 ), math.random( yMax - 1 ) + end + game_map[ Food.x ][ Food.y ] = "food" + lcd.drawText( Food.x * 6, Food.y * 8+2, "@", 0 ) +end + +local function eat_food() + playFile("/SCRIPTS/snake.wav") + lcd.drawText( Head.x * 6, Head.y * 8, " ", 0 ) + game_map[ Head.x ][ Head.y ] = nil + create_food() + score = score + 1 +end + +local function check_collision() + if Head.x < 0 or Head.x > xMax then + return true + elseif Head.y < 0 or Head.y > yMax then + return true + elseif ( ( game_map[ Head.x ][ Head.y ] ) and ( game_map[ Head.x ][ Head.y ] ~= "food" ) ) then + return true + end + return false +end + +local function move() + if game_map[ Tail.x ][ Tail.y ] == "right" then + Tail.dx = 1 + Tail.dy = 0 + elseif game_map[ Tail.x ][ Tail.y ] == "left" then + Tail.dx = -1 + Tail.dy = 0 + elseif game_map[ Tail.x ][ Tail.y ] == "up" then + Tail.dx = 0 + Tail.dy = -1 + elseif game_map[ Tail.x ][ Tail.y ] == "down" then + Tail.dx = 0 + Tail.dy = 1 + end + + game_map[ Head.x ][ Head.y ] = direction + Head.x = Head.x + Head.dx + Head.y = Head.y + Head.dy + + if Head.x < 0 or Head.x > xMax or Head.y < 0 or Head.y > yMax then + return + elseif game_map[ Head.x ][ Head.y ] == "food" then + eat_food() + else + lcd.drawText(Tail.x * 6, Tail.y * 8, " ", 16) + game_map[ Tail.x ][ Tail.y ] = nil + Tail.x = Tail.x + Tail.dx + Tail.y = Tail.y + Tail.dy + end + + lcd.drawText(Head.x * 6, Head.y * 8, "*", 0) +end + +local function init() + food = false + lcd.clear() + size = 3 + score = 0 + Tail.x = 1 + Tail.y = 1 + Head.x = Tail.x + ( size - 1 ) + Head.y = Tail.y + Head.dx = 1 + Head.dy = 0 + Tail.dx = Head.dx + Tail.dy = Head.dy + direction = "right" + + for i = 0, xMax, 1 do + game_map[ i ] = {} + end + + for i = 0, size - 1, 1 do + game_map[ Tail.x + ( i * Tail.dx ) ][ Tail.y + ( i * Tail.dy ) ] = direction + lcd.drawText( ( Tail.x + ( i * Tail.dx ) ) * 6, ( Tail.y + ( i * Tail.dy ) ) * 8, "*", 0 ) + end + + create_food() +end + +local snakeCounter = 0 + +local function run(event) + if event == nil then + raise("Cannot be run as a model script!") + end + + if event == EVT_VIRTUAL_EXIT then + return 2 + end + + snakeCounter = snakeCounter + 1 + if snakeCounter < 30 then + return 0 + end + + snakeCounter = 0 + + local dir = direction + if getValue('rud') > 100 and direction ~= "left" then + dir = "right" + Head.dx = 1 + Head.dy = 0 + end + if getValue('rud') < -100 and direction ~= "right" then + dir = "left" + Head.dx = -1 + Head.dy = 0 + end + if getValue('ele') > 100 and direction ~= "down" then + dir = "up" + Head.dx = 0 + Head.dy = -1 + end + if getValue('ele') < -100 and direction ~= "up" then + dir = "down" + Head.dx = 0 + Head.dy = 1 + end + + direction = dir + move() + + lcd.refresh() + + if check_collision() then + return 1 + end + + return 0 +end + +return { init=init, run=run } + diff --git a/SOUNDS/README.txt b/SOUNDS/README.txt new file mode 100755 index 0000000..bd0837b --- /dev/null +++ b/SOUNDS/README.txt @@ -0,0 +1,2 @@ +Sound packs organised by language are stored in this directory. e.g. en, de, fr +You can also add your own model dependent sounds. diff --git a/SOUNDS/edgetx.sounds.version b/SOUNDS/edgetx.sounds.version new file mode 100755 index 0000000..805579f --- /dev/null +++ b/SOUNDS/edgetx.sounds.version @@ -0,0 +1 @@ +v2.11.0 \ No newline at end of file diff --git a/edgetx.sdcard.target b/edgetx.sdcard.target new file mode 100755 index 0000000..48053ec --- /dev/null +++ b/edgetx.sdcard.target @@ -0,0 +1 @@ +mt12 \ No newline at end of file diff --git a/edgetx.sdcard.version b/edgetx.sdcard.version new file mode 100755 index 0000000..805579f --- /dev/null +++ b/edgetx.sdcard.version @@ -0,0 +1 @@ +v2.11.0 \ No newline at end of file