├── .gitignore ├── images ├── setup-1.png ├── screen-1.png ├── screen-2.png └── screen-3.png ├── RESOURCEPOOL ├── images │ ├── batt.bmp │ ├── batt-2.bmp │ ├── gauge-1.bmp │ ├── gauge-2.bmp │ ├── gauge-3.bmp │ ├── gauge-4.bmp │ ├── timer.bmp │ ├── arrow-bwd.bmp │ ├── arrow-fwd.bmp │ ├── arrow-left.bmp │ ├── arrow-right.bmp │ ├── batt-empty.bmp │ ├── gauge-empty.bmp │ └── gauge-full.bmp └── widgets │ ├── flightmode-x9.lua │ ├── flightmode-x7.lua │ ├── flighttime.lua │ ├── flighttime-x9.lua │ ├── heading.lua │ ├── flightmode.lua │ ├── flighttime-x7.lua │ ├── rssi.lua │ ├── rssi-x7.lua │ ├── rssi-x9.lua │ ├── battery.lua │ ├── heading-x7.lua │ ├── heading-x9.lua │ ├── horizon.lua │ ├── horizon-x9.lua │ ├── horizon-x7.lua │ ├── battery-x9.lua │ └── battery-x7.lua ├── LICENSE ├── README.md └── Racer.lua /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.luac 3 | -------------------------------------------------------------------------------- /images/setup-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resourcepool/open-tm/HEAD/images/setup-1.png -------------------------------------------------------------------------------- /images/screen-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resourcepool/open-tm/HEAD/images/screen-1.png -------------------------------------------------------------------------------- /images/screen-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resourcepool/open-tm/HEAD/images/screen-2.png -------------------------------------------------------------------------------- /images/screen-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resourcepool/open-tm/HEAD/images/screen-3.png -------------------------------------------------------------------------------- /RESOURCEPOOL/images/batt.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resourcepool/open-tm/HEAD/RESOURCEPOOL/images/batt.bmp -------------------------------------------------------------------------------- /RESOURCEPOOL/images/batt-2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resourcepool/open-tm/HEAD/RESOURCEPOOL/images/batt-2.bmp -------------------------------------------------------------------------------- /RESOURCEPOOL/images/gauge-1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resourcepool/open-tm/HEAD/RESOURCEPOOL/images/gauge-1.bmp -------------------------------------------------------------------------------- /RESOURCEPOOL/images/gauge-2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resourcepool/open-tm/HEAD/RESOURCEPOOL/images/gauge-2.bmp -------------------------------------------------------------------------------- /RESOURCEPOOL/images/gauge-3.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resourcepool/open-tm/HEAD/RESOURCEPOOL/images/gauge-3.bmp -------------------------------------------------------------------------------- /RESOURCEPOOL/images/gauge-4.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resourcepool/open-tm/HEAD/RESOURCEPOOL/images/gauge-4.bmp -------------------------------------------------------------------------------- /RESOURCEPOOL/images/timer.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resourcepool/open-tm/HEAD/RESOURCEPOOL/images/timer.bmp -------------------------------------------------------------------------------- /RESOURCEPOOL/images/arrow-bwd.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resourcepool/open-tm/HEAD/RESOURCEPOOL/images/arrow-bwd.bmp -------------------------------------------------------------------------------- /RESOURCEPOOL/images/arrow-fwd.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resourcepool/open-tm/HEAD/RESOURCEPOOL/images/arrow-fwd.bmp -------------------------------------------------------------------------------- /RESOURCEPOOL/images/arrow-left.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resourcepool/open-tm/HEAD/RESOURCEPOOL/images/arrow-left.bmp -------------------------------------------------------------------------------- /RESOURCEPOOL/images/arrow-right.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resourcepool/open-tm/HEAD/RESOURCEPOOL/images/arrow-right.bmp -------------------------------------------------------------------------------- /RESOURCEPOOL/images/batt-empty.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resourcepool/open-tm/HEAD/RESOURCEPOOL/images/batt-empty.bmp -------------------------------------------------------------------------------- /RESOURCEPOOL/images/gauge-empty.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resourcepool/open-tm/HEAD/RESOURCEPOOL/images/gauge-empty.bmp -------------------------------------------------------------------------------- /RESOURCEPOOL/images/gauge-full.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resourcepool/open-tm/HEAD/RESOURCEPOOL/images/gauge-full.bmp -------------------------------------------------------------------------------- /RESOURCEPOOL/widgets/flightmode-x9.lua: -------------------------------------------------------------------------------- 1 | --------------------------------- 2 | -- GLOBAL VARIABLES 3 | --------------------------------- 4 | local WIDGET_START_X = 173 5 | local WIDGET_START_Y = 43 6 | local WIDGET_WIDTH = 40 7 | local WIDGET_HEIGHT = 24 8 | 9 | local function layout() 10 | lcd.drawFilledRectangle(WIDGET_START_X, WIDGET_START_Y, WIDGET_WIDTH, WIDGET_HEIGHT, ERASE) 11 | lcd.drawRectangle(WIDGET_START_X, WIDGET_START_Y, WIDGET_WIDTH, WIDGET_HEIGHT, GREY_DEFAULT) 12 | end 13 | 14 | local function redraw(flightMode) 15 | -- draw flight mode 16 | lcd.drawText(WIDGET_START_X + 8, WIDGET_START_Y + 7, flightMode) 17 | end 18 | 19 | 20 | return { layout = layout, redraw = redraw } 21 | -------------------------------------------------------------------------------- /RESOURCEPOOL/widgets/flightmode-x7.lua: -------------------------------------------------------------------------------- 1 | --------------------------------- 2 | -- GLOBAL VARIABLES 3 | --------------------------------- 4 | local WIDGET_START_X = 107 5 | local WIDGET_START_Y = 55 6 | local WIDGET_WIDTH = 21 7 | local WIDGET_HEIGHT = 9 8 | 9 | local function layout() 10 | lcd.drawFilledRectangle(WIDGET_START_X, WIDGET_START_Y, WIDGET_WIDTH, WIDGET_HEIGHT, ERASE) 11 | lcd.drawRectangle(WIDGET_START_X, WIDGET_START_Y, WIDGET_WIDTH, WIDGET_HEIGHT, GREY_DEFAULT) 12 | end 13 | 14 | local function redraw(flightMode) 15 | -- draw flight mode 16 | lcd.drawText(WIDGET_START_X + 2, WIDGET_START_Y + 2, flightMode, SMLSIZE) 17 | end 18 | 19 | 20 | return { layout = layout, redraw = redraw } 21 | -------------------------------------------------------------------------------- /RESOURCEPOOL/widgets/flighttime.lua: -------------------------------------------------------------------------------- 1 | --------------------------------- 2 | -- VARIABLES 3 | --------------------------------- 4 | local timerNumber 5 | 6 | local function init(radio) 7 | timerNumber = FLIGHT_TIMER 8 | layoutEngine = loadScript("/SCRIPTS/TELEMETRY/RESOURCEPOOL/widgets/flighttime-" .. radio .. ".lua")() 9 | end 10 | 11 | local function layout() 12 | layoutEngine.layout() 13 | end 14 | 15 | local function shouldRefresh(lastTimeSinceRedraw) 16 | if lastTimeSinceRedraw > 1000 then 17 | return true 18 | end 19 | return false 20 | end 21 | 22 | local function redraw() 23 | layoutEngine.redraw(timerNumber) 24 | end 25 | 26 | return { tag = "flighttime", init = init, layout = layout, redraw = redraw, shouldRefresh = shouldRefresh } 27 | -------------------------------------------------------------------------------- /RESOURCEPOOL/widgets/flighttime-x9.lua: -------------------------------------------------------------------------------- 1 | --------------------------------- 2 | -- GLOBAL VARIABLES 3 | --------------------------------- 4 | local WIDGET_START_X = 0 5 | local WIDGET_START_Y = 43 6 | local WIDGET_WIDTH = 40 7 | local WIDGET_HEIGHT = 24 8 | --------------------------------- 9 | -- VARIABLES 10 | --------------------------------- 11 | local timer 12 | 13 | local function layout() 14 | lcd.drawFilledRectangle(WIDGET_START_X, WIDGET_START_Y, WIDGET_WIDTH, WIDGET_HEIGHT, ERASE) 15 | lcd.drawPixmap(WIDGET_START_X + 3, WIDGET_START_Y + 5, "/SCRIPTS/TELEMETRY/RESOURCEPOOL/images/timer.bmp") 16 | lcd.drawRectangle(WIDGET_START_X, WIDGET_START_Y, WIDGET_WIDTH, WIDGET_HEIGHT, GREY_DEFAULT) 17 | end 18 | 19 | local function redraw(timerNumber) 20 | -- Refresh timer 21 | timer = model.getTimer(timerNumber) 22 | lcd.drawTimer(WIDGET_START_X + 16, WIDGET_START_Y + 7, timer.value, 0) 23 | end 24 | 25 | return { layout = layout, redraw = redraw } 26 | -------------------------------------------------------------------------------- /RESOURCEPOOL/widgets/heading.lua: -------------------------------------------------------------------------------- 1 | --------------------------------- 2 | -- GLOBAL VARIABLES 3 | --------------------------------- 4 | local MIN_REFRESH_HEADING = 2 5 | 6 | --------------------------------- 7 | -- VARIABLES 8 | --------------------------------- 9 | local heading 10 | local layoutEngine 11 | 12 | local function init(radio) 13 | heading = 0 14 | layoutEngine = loadScript("/SCRIPTS/TELEMETRY/RESOURCEPOOL/widgets/heading-" .. radio .. ".lua")() 15 | end 16 | 17 | local function shouldRefresh(lastTimeSinceRedraw) 18 | -- Refresh Heading 19 | local newHeading = getValue("Hdg") 20 | if math.abs(heading - newHeading) > MIN_REFRESH_HEADING then 21 | heading = newHeading 22 | return true 23 | end 24 | return false 25 | end 26 | 27 | local function layout() 28 | layoutEngine.layout() 29 | end 30 | 31 | local function redraw() 32 | layoutEngine.redraw(heading) 33 | end 34 | 35 | return { tag = "heading", init = init, layout = layout, redraw = redraw, shouldRefresh = shouldRefresh } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 resourcepool 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /RESOURCEPOOL/widgets/flightmode.lua: -------------------------------------------------------------------------------- 1 | --------------------------------- 2 | -- GLOBAL VARIABLES 3 | --------------------------------- 4 | local REFRESH_FREQUENCY_2S = 200 5 | local flightMode 6 | --------------------------------- 7 | -- VARIABLES 8 | --------------------------------- 9 | local flightMode 10 | local layoutEngine 11 | 12 | local function init(radio) 13 | flightMode = 0 14 | layoutEngine = loadScript("/SCRIPTS/TELEMETRY/RESOURCEPOOL/widgets/flightmode-" .. radio .. ".lua")() 15 | end 16 | 17 | local function shouldRefresh(lastTimeSinceRedraw) 18 | 19 | -- Redraw only every 2 seconds 20 | if lastTimeSinceRedraw < REFRESH_FREQUENCY_2S then 21 | return false 22 | end 23 | -- Refresh Flight Mode 24 | local _, newFlightMode = getFlightMode() 25 | 26 | if newFlightMode == flightMode then 27 | return false 28 | end 29 | flightMode = newFlightMode 30 | return true 31 | end 32 | 33 | local function layout() 34 | layoutEngine.layout() 35 | end 36 | 37 | local function redraw() 38 | layoutEngine.redraw(flightMode) 39 | end 40 | 41 | 42 | return { tag = "flightMode", init = init, layout = layout, redraw = redraw, shouldRefresh = shouldRefresh } 43 | -------------------------------------------------------------------------------- /RESOURCEPOOL/widgets/flighttime-x7.lua: -------------------------------------------------------------------------------- 1 | --------------------------------- 2 | -- GLOBAL VARIABLES 3 | --------------------------------- 4 | local WIDGET_START_X = 0 5 | local WIDGET_START_Y = 51 6 | local WIDGET_WIDTH = 34 7 | local WIDGET_HEIGHT = 13 8 | --------------------------------- 9 | -- VARIABLES 10 | --------------------------------- 11 | local timer 12 | 13 | local function layout() 14 | lcd.drawFilledRectangle(WIDGET_START_X, WIDGET_START_Y, WIDGET_WIDTH, WIDGET_HEIGHT, ERASE) 15 | lcd.drawText(WIDGET_START_X + 1, WIDGET_START_Y, "o", MIDSIZE) 16 | lcd.drawLine(WIDGET_START_X + 4, WIDGET_START_Y + 7, WIDGET_START_X + 5, WIDGET_START_Y + 7, SOLID, FORCE) 17 | lcd.drawLine(WIDGET_START_X + 4, WIDGET_START_Y + 7, WIDGET_START_X + 4, WIDGET_START_Y + 4, SOLID, FORCE) 18 | --lcd.drawPixmap(WIDGET_START_X + 3, WIDGET_START_Y + 5, "/SCRIPTS/TELEMETRY/RESOURCEPOOL/images/timer.bmp") 19 | lcd.drawRectangle(WIDGET_START_X, WIDGET_START_Y, WIDGET_WIDTH, WIDGET_HEIGHT, GREY_DEFAULT) 20 | end 21 | 22 | local function redraw(timerNumber) 23 | -- Refresh timer 24 | timer = model.getTimer(timerNumber) 25 | lcd.drawTimer(WIDGET_START_X + 10, WIDGET_START_Y + 4, timer.value, 0) 26 | end 27 | 28 | return { layout = layout, redraw = redraw } 29 | -------------------------------------------------------------------------------- /RESOURCEPOOL/widgets/rssi.lua: -------------------------------------------------------------------------------- 1 | --------------------------------- 2 | -- GLOBAL VARIABLES 3 | --------------------------------- 4 | 5 | local REFRESH_FREQUENCY_500MS = 50 6 | local SETTINGS = getGeneralSettings() 7 | local _,LOW_RSSI = getRSSI() 8 | local HIGH_RSSI = 90 9 | --------------------------------- 10 | -- VARIABLES 11 | --------------------------------- 12 | local rssi 13 | local rssiPercent 14 | 15 | local layoutEngine 16 | 17 | local function init(radio) 18 | rssi = 0 19 | rssiPercent = 0 20 | layoutEngine = loadScript("/SCRIPTS/TELEMETRY/RESOURCEPOOL/widgets/rssi-" .. radio .. ".lua")() 21 | end 22 | 23 | local function shouldRefresh(lastTimeSinceRedraw) 24 | 25 | -- Redraw only every 500 ms 26 | if lastTimeSinceRedraw < REFRESH_FREQUENCY_500MS then 27 | return false 28 | end 29 | -- Refresh RSSI 30 | local newRssi = getRSSI() 31 | if newRssi == rssi then 32 | return false 33 | end 34 | rssi = newRssi 35 | rssiPercent = math.max(0, (rssi - LOW_RSSI)) * 100 / (HIGH_RSSI - LOW_RSSI) 36 | return true 37 | end 38 | 39 | local function layout() 40 | layoutEngine.layout() 41 | end 42 | 43 | local function redraw() 44 | layoutEngine.redraw(rssi, rssiPercent) 45 | end 46 | 47 | 48 | return { tag = "rssi", init = init, layout = layout, redraw = redraw, shouldRefresh = shouldRefresh } 49 | -------------------------------------------------------------------------------- /RESOURCEPOOL/widgets/rssi-x7.lua: -------------------------------------------------------------------------------- 1 | --------------------------------- 2 | -- GLOBAL VARIABLES 3 | --------------------------------- 4 | local WIDGET_START_X = 0 5 | local WIDGET_START_Y = 0 6 | local WIDGET_START_GAUGEX = WIDGET_START_X + 8 7 | local WIDGET_START_GAUGEY = WIDGET_START_Y + 12 8 | local WIDGET_WIDTH = 34 9 | local WIDGET_HEIGHT = 27 10 | local OFFSET = (WIDGET_WIDTH - 4) / 5 -- one spacing between each bar + 2px offset on each side 11 | local BAR_WIDTH = OFFSET - 1 12 | 13 | local function layout() 14 | lcd.drawFilledRectangle(WIDGET_START_X, WIDGET_START_Y, WIDGET_WIDTH, WIDGET_HEIGHT, ERASE) 15 | lcd.drawRectangle(WIDGET_START_X, WIDGET_START_Y, WIDGET_WIDTH, WIDGET_HEIGHT) 16 | end 17 | 18 | local function redraw(rssi, rssiPercent) 19 | lcd.drawNumber(WIDGET_START_GAUGEX, WIDGET_START_Y + 3, rssi, SMLSIZE) 20 | lcd.drawText(WIDGET_START_GAUGEX + 10, WIDGET_START_Y + 3, "db", SMLSIZE) 21 | for i = 0,4 do 22 | if (rssiPercent >= 20 * i and rssiPercent > 10) then 23 | lcd.drawFilledRectangle(WIDGET_START_X + 2 + OFFSET * i, WIDGET_START_Y + 10 + 15 * (4 - i) / 5, BAR_WIDTH, 15 * (i + 1) / 5) 24 | else 25 | lcd.drawRectangle(WIDGET_START_X + 2 + OFFSET * i, WIDGET_START_Y + 10 + 15 * (4 - i) / 5, BAR_WIDTH, 15 * (i + 1) / 5) 26 | end 27 | end 28 | if rssiPercent < 20 then 29 | lcd.drawText(WIDGET_START_X + WIDGET_WIDTH / 2 + 2, WIDGET_START_Y + 19, "low", SMLSIZE + INVERS) 30 | end 31 | end 32 | 33 | return { layout = layout, redraw = redraw } 34 | -------------------------------------------------------------------------------- /RESOURCEPOOL/widgets/rssi-x9.lua: -------------------------------------------------------------------------------- 1 | --------------------------------- 2 | -- GLOBAL VARIABLES 3 | --------------------------------- 4 | local WIDGET_START_X = 173 5 | local WIDGET_START_Y = 0 6 | local WIDGET_START_GAUGEX = WIDGET_START_X + 8 7 | local WIDGET_START_GAUGEY = WIDGET_START_Y + 12 8 | local WIDGET_WIDTH = 43 9 | local WIDGET_HEIGHT = 44 10 | 11 | local function layout() 12 | lcd.drawFilledRectangle(WIDGET_START_X, WIDGET_START_Y, WIDGET_WIDTH, WIDGET_HEIGHT, ERASE) 13 | lcd.drawRectangle(WIDGET_START_X, WIDGET_START_Y, WIDGET_WIDTH, WIDGET_HEIGHT, GREY_DEFAULT) 14 | end 15 | 16 | local function redraw(rssi, rssiPercent) 17 | lcd.drawNumber(WIDGET_START_GAUGEX + 2, WIDGET_START_Y + 3, rssi) 18 | lcd.drawText(WIDGET_START_GAUGEX + 13, WIDGET_START_Y + 3, "db") 19 | if rssiPercent == 0 then 20 | lcd.drawPixmap(WIDGET_START_GAUGEX, WIDGET_START_GAUGEY, "/SCRIPTS/TELEMETRY/RESOURCEPOOL/images/gauge-empty.bmp") 21 | elseif rssiPercent < 20 then 22 | lcd.drawPixmap(WIDGET_START_GAUGEX, WIDGET_START_GAUGEY, "/SCRIPTS/TELEMETRY/RESOURCEPOOL/images/gauge-1.bmp") 23 | elseif rssiPercent < 40 then 24 | lcd.drawPixmap(WIDGET_START_GAUGEX, WIDGET_START_GAUGEY, "/SCRIPTS/TELEMETRY/RESOURCEPOOL/images/gauge-2.bmp") 25 | elseif rssiPercent < 60 then 26 | lcd.drawPixmap(WIDGET_START_GAUGEX, WIDGET_START_GAUGEY, "/SCRIPTS/TELEMETRY/RESOURCEPOOL/images/gauge-3.bmp") 27 | elseif rssiPercent < 80 then 28 | lcd.drawPixmap(WIDGET_START_GAUGEX, WIDGET_START_GAUGEY, "/SCRIPTS/TELEMETRY/RESOURCEPOOL/images/gauge-4.bmp") 29 | else 30 | lcd.drawPixmap(WIDGET_START_GAUGEX, WIDGET_START_GAUGEY, "/SCRIPTS/TELEMETRY/RESOURCEPOOL/images/gauge-full.bmp") 31 | end 32 | end 33 | 34 | 35 | return { layout = layout, redraw = redraw } 36 | -------------------------------------------------------------------------------- /RESOURCEPOOL/widgets/battery.lua: -------------------------------------------------------------------------------- 1 | --------------------------------- 2 | -- GLOBAL VARIABLES 3 | --------------------------------- 4 | local MIN_REFRESH_VBATT = 0.2 5 | 6 | local REFRESH_FREQUENCY_2S = 200 7 | local LIPO_CELL = 3.7 8 | local LIPO_CELL_LOW = 3.5 9 | local LIPO_CELL_MAX = 4.2 10 | local LIPO_DELTA = LIPO_CELL_MAX - LIPO_CELL_LOW 11 | 12 | --------------------------------- 13 | -- VARIABLES 14 | --------------------------------- 15 | local batt 16 | local battVolt 17 | local showBattVoltage 18 | local cellCount 19 | local layoutEngine 20 | 21 | local function init(radio) 22 | batt = 0 23 | battVolt = 0 24 | showBattVoltage = false 25 | cellCount = 0 26 | layoutEngine = loadScript("/SCRIPTS/TELEMETRY/RESOURCEPOOL/widgets/battery-" .. radio .. ".lua")() 27 | end 28 | 29 | -- Detect Lipo Cell count 30 | local function detectCellCount(vfas) 31 | local limit = LIPO_CELL_MAX 32 | if not vfas then 33 | return 0 34 | end 35 | if vfas < limit + 0.5 then 36 | return 1 37 | end 38 | if vfas < limit * 2 + 0.5 then 39 | return 2 40 | end 41 | if vfas < limit * 3 + 0.5 then 42 | return 3 43 | end 44 | if vfas < limit * 4 + 0.5 then 45 | return 4 46 | end 47 | if vfas < limit * 5 + 0.5 then 48 | return 5 49 | end 50 | if vfas < limit * 6 + 0.5 then 51 | return 6 52 | end 53 | return 0 54 | end 55 | 56 | local function shouldRefresh(lastTimeSinceRedraw) 57 | -- Redraw only every 2 seconds 58 | if lastTimeSinceRedraw < REFRESH_FREQUENCY_2S then 59 | return false 60 | end 61 | local newBattVolt = getValue("VFAS") 62 | -- Refresh Battery Level 63 | if showBattVoltage then 64 | showBattVoltage = false 65 | else 66 | showBattVoltage = true 67 | end 68 | battVolt = newBattVolt 69 | cellCount = detectCellCount(battVolt) 70 | batt = 0 71 | if cellCount > 0 then 72 | batt = math.max(0, (battVolt - LIPO_CELL_LOW * cellCount) * 100 / (LIPO_DELTA * cellCount)) 73 | end 74 | return true 75 | end 76 | 77 | local function layout() 78 | layoutEngine.layout(batt) 79 | end 80 | 81 | local function redraw() 82 | layoutEngine.redraw(showBattVoltage, batt, battVolt) 83 | end 84 | 85 | return { tag = "battery", init = init, layout = layout, redraw = redraw, shouldRefresh = shouldRefresh } 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Open-Tm 2 | 3 | Current version: `1.5` 4 | 5 | **Open-Tm supports opentx >=2.2. Don't forget to upgrade** 6 | 7 | Looking for OpenTx Flight Logs to give you more awesomeness! Tweet if you have some to share :) 8 | 9 | ## What it is 10 | Open-Tm is a set of awesome telemetry screens for your favorite OpenTX Radio. 11 | Right now we support both Taranis X7 & Taranis X9D/X9D+ 12 | **X9D/X9D+:** 13 | ![Telemetry Example](images/screen-1.png) 14 | ![Telemetry Example 2](images/screen-2.png) 15 | 16 | **X7:** 17 | ![Telemetry Example 3](images/screen-3.png) 18 | 19 | ## What it does 20 | Open-Tm pulls the telemetry data and displays it in cool widgets. 21 | Really, it's so cool it looks like your OSD. 22 | 23 | The following sensors are supported: 24 | * Hdg : Heading 25 | * VFAS : Battery level 26 | * AccX-AccY-AccZ: Accelerometer 27 | * Flight Mode 28 | * Flight Timer 29 | * RSSI 30 | 31 | ## Howto 32 | 33 | ### 1. Place files on SD Card 34 | Download this repository and unzip its content into the /SCRIPTS/TELEMETRY folder of your Taranis. 35 | 36 | The folder should look similar to this: 37 | ![SD Card content](images/setup-1.png) 38 | 39 | ### 2. Configure Flight Modes 40 | As you may know, your Radio needs to know about your flight modes. 41 | Configure your flight modes in order for the Script to show the current flight mode. 42 | 43 | ### 3. Configure Flight Timer 44 | In order to show the flight time, the script uses the values given by a Timer. 45 | Configure your timer in order to reflect your flight time on your Radio. 46 | 47 | ### 4. Configure Script 48 | Open the Racer.lua file with any text editor and change the first lines according to your configuration. 49 | By default, the script assumes that you have a Taranis X9D/X9D+, a compass on your drone and that the flight timer is timer-0 (the first timer) 50 | 51 | ### 5. Enable Script as telemetry screen 52 | Go to your Taranis settings and enable your telemetry screen. Congrats! 53 | 54 | 55 | # Any issues? 56 | Having trouble on your setup? Here's a few things to check before creating an issue: 57 | * Check whether you have the latest version of the OpenTX Firmware (2.2) 58 | * Make sure you have the "luac" option enabled on your firmware. Otherwise flash a new firmware on OpenTX Companion 59 | * Make sure you have transferred the right files in the specific directories on your SD card 60 | 61 | 62 | # Contribute 63 | Any ideas? Feature requests? Tell us about it in the "Issues" section! 64 | 65 | Contact: Twitter @loicortola 66 | -------------------------------------------------------------------------------- /RESOURCEPOOL/widgets/heading-x7.lua: -------------------------------------------------------------------------------- 1 | --------------------------------- 2 | -- GLOBAL VARIABLES 3 | --------------------------------- 4 | local WIDGET_START_X = 33 5 | local WIDGET_START_Y = 0 6 | local WIDGET_WIDTH = 95 7 | local WIDGET_HEIGHT = 17 8 | 9 | --------------------------------- 10 | -- VARIABLES 11 | --------------------------------- 12 | local x0 = WIDGET_START_X + 5 13 | local y0 = WIDGET_START_Y + 10 14 | local width = WIDGET_WIDTH - 10 15 | local center = x0 + width / 2 16 | local x1 = x0 + width 17 | 18 | local function layout() 19 | lcd.drawFilledRectangle(WIDGET_START_X, WIDGET_START_Y, WIDGET_WIDTH, WIDGET_HEIGHT, ERASE) 20 | lcd.drawLine(x0, y0 + 2, x1, y0 + 2, SOLID, SOLID) 21 | lcd.drawText(center - 2, y0, "^") 22 | lcd.drawRectangle(WIDGET_START_X, WIDGET_START_Y, WIDGET_WIDTH, WIDGET_HEIGHT, GREY_DEFAULT) 23 | end 24 | 25 | local function redraw(heading) 26 | local offset = heading * width / 270 27 | -- angular projections 28 | local pn = (3 * width / 6 - offset) % (width * 8 / 6) 29 | local pe = (5 * width / 6 - offset) % (width * 8 / 6) 30 | local ps = (7 * width / 6 - offset) % (width * 8 / 6) 31 | local pw = (9 * width / 6 - offset) % (width * 8 / 6) 32 | 33 | -- text heights 34 | local upperLineY = y0 - 7 35 | local smallDivY = y0 + 3 36 | local largeDivY = y0 + 4 37 | 38 | if REAL_HEADING then 39 | -- assuming north is 0, then going counter-clockwise 40 | if pw <= width then 41 | lcd.drawText(x0 - 2 + pw, upperLineY, "W", SMLSIZE) 42 | lcd.drawLine(x0 + pw, y0, x0 + pw, smallDivY, SOLID, 0) 43 | end 44 | if pn <= width then 45 | lcd.drawText(x0 - 2 + pn, upperLineY, "N", SMLSIZE) 46 | lcd.drawLine(x0 + pn, y0, x0 + pn, largeDivY, SOLID, 0) 47 | end 48 | if pe <= width then 49 | lcd.drawText(x0 - 2 + pe, upperLineY, "E", SMLSIZE) 50 | lcd.drawLine(x0 + pe, y0, x0 + pe, smallDivY, SOLID, 0) 51 | end 52 | if ps <= width then 53 | lcd.drawText(x0 - 2 + ps, upperLineY, "S", SMLSIZE) 54 | lcd.drawLine(x0 + ps, y0, x0 + ps, largeDivY, SOLID, 0) 55 | end 56 | else 57 | -- assuming north is 0, then going counter-clockwise 58 | if pw <= width then 59 | lcd.drawText(x0 - 2 + pw, upperLineY + 1, "<") 60 | lcd.drawLine(x0 + pw, y0, x0 + pw, smallDivY, SOLID, 0) 61 | end 62 | if pn <= width then 63 | lcd.drawText(x0 - 2 + pn, upperLineY + 1, "^") 64 | lcd.drawLine(x0 + pn, y0, x0 + pn, largeDivY, SOLID, 0) 65 | end 66 | if pe <= width then 67 | lcd.drawText(x0 - 2 + pe, upperLineY, ">") 68 | lcd.drawLine(x0 + pe, y0, x0 + pe, smallDivY, SOLID, 0) 69 | end 70 | if ps <= width then 71 | lcd.drawText(x0 - 2 + ps, upperLineY - 1, "v") 72 | lcd.drawLine(x0 + ps, y0, x0 + ps, largeDivY, SOLID, 0) 73 | end 74 | end 75 | end 76 | 77 | return { layout = layout, redraw = redraw } 78 | -------------------------------------------------------------------------------- /RESOURCEPOOL/widgets/heading-x9.lua: -------------------------------------------------------------------------------- 1 | --------------------------------- 2 | -- GLOBAL VARIABLES 3 | --------------------------------- 4 | local WIDGET_START_X = 40 5 | local WIDGET_START_Y = 0 6 | local WIDGET_WIDTH = 133 7 | local WIDGET_HEIGHT = 20 8 | 9 | --------------------------------- 10 | -- VARIABLES 11 | --------------------------------- 12 | local x0 = WIDGET_START_X + 5 13 | local y0 = WIDGET_START_Y + 11 14 | local width = WIDGET_WIDTH - 10 15 | local center = x0 + width / 2 16 | local x1 = x0 + width 17 | 18 | local function layout() 19 | lcd.drawFilledRectangle(WIDGET_START_X, WIDGET_START_Y, WIDGET_WIDTH, WIDGET_HEIGHT, ERASE) 20 | lcd.drawLine(x0, y0 + 2, x1, y0 + 2, SOLID, GREY_DEFAULT) 21 | lcd.drawPixmap(center - 2, y0, "/SCRIPTS/TELEMETRY/RESOURCEPOOL/images/arrow-fwd.bmp") 22 | lcd.drawRectangle(WIDGET_START_X, WIDGET_START_Y, WIDGET_WIDTH, WIDGET_HEIGHT, GREY_DEFAULT) 23 | end 24 | 25 | local function redraw(heading) 26 | local offset = heading * width / 270 27 | -- angular projections 28 | local pn = (3 * width / 6 - offset) % (width * 8 / 6) 29 | local pe = (5 * width / 6 - offset) % (width * 8 / 6) 30 | local ps = (7 * width / 6 - offset) % (width * 8 / 6) 31 | local pw = (9 * width / 6 - offset) % (width * 8 / 6) 32 | 33 | -- text heights 34 | local upperLineY = y0 - 7 35 | local smallDivY = y0 + 3 36 | local largeDivY = y0 + 4 37 | 38 | if REAL_HEADING then 39 | -- assuming north is 0, then going counter-clockwise 40 | if pw <= width then 41 | lcd.drawText(x0 - 2 + pw, upperLineY, "W", SMLSIZE) 42 | lcd.drawLine(x0 + pw, y0, x0 + pw, smallDivY, SOLID, 0) 43 | end 44 | if pn <= width then 45 | lcd.drawText(x0 - 2 + pn, upperLineY, "N", SMLSIZE) 46 | lcd.drawLine(x0 + pn, y0, x0 + pn, largeDivY, SOLID, 0) 47 | end 48 | if pe <= width then 49 | lcd.drawText(x0 - 2 + pe, upperLineY, "E", SMLSIZE) 50 | lcd.drawLine(x0 + pe, y0, x0 + pe, smallDivY, SOLID, 0) 51 | end 52 | if ps <= width then 53 | lcd.drawText(x0 - 2 + ps, upperLineY, "S", SMLSIZE) 54 | lcd.drawLine(x0 + ps, y0, x0 + ps, largeDivY, SOLID, 0) 55 | end 56 | else 57 | -- assuming north is 0, then going counter-clockwise 58 | if pw <= width then 59 | lcd.drawPixmap(x0 - 2 + pw, upperLineY, "/SCRIPTS/TELEMETRY/RESOURCEPOOL/images/arrow-left.bmp") 60 | lcd.drawLine(x0 + pw, y0, x0 + pw, smallDivY, SOLID, 0) 61 | end 62 | if pn <= width then 63 | lcd.drawPixmap(x0 - 2 + pn, upperLineY, "/SCRIPTS/TELEMETRY/RESOURCEPOOL/images/arrow-fwd.bmp") 64 | lcd.drawLine(x0 + pn, y0, x0 + pn, largeDivY, SOLID, 0) 65 | end 66 | if pe <= width then 67 | lcd.drawPixmap(x0 - 2 + pe, upperLineY, "/SCRIPTS/TELEMETRY/RESOURCEPOOL/images/arrow-right.bmp") 68 | lcd.drawLine(x0 + pe, y0, x0 + pe, smallDivY, SOLID, 0) 69 | end 70 | if ps <= width then 71 | lcd.drawPixmap(x0 - 2 + ps, upperLineY, "/SCRIPTS/TELEMETRY/RESOURCEPOOL/images/arrow-bwd.bmp") 72 | lcd.drawLine(x0 + ps, y0, x0 + ps, largeDivY, SOLID, 0) 73 | end 74 | end 75 | end 76 | 77 | return { layout = layout, redraw = redraw } 78 | -------------------------------------------------------------------------------- /RESOURCEPOOL/widgets/horizon.lua: -------------------------------------------------------------------------------- 1 | --------------------------------- 2 | -- VARIABLES 3 | --------------------------------- 4 | 5 | local accX 6 | local accY 7 | local accZ 8 | 9 | local pitchRad 10 | local pitchDeg 11 | 12 | local rollDeg 13 | local rollRad 14 | 15 | local horizonPitchDelta 16 | local horizonWidth 17 | local horizonHeight 18 | 19 | local layoutEngine 20 | 21 | local function init(radio) 22 | layoutEngine = loadScript("/SCRIPTS/TELEMETRY/RESOURCEPOOL/widgets/horizon-" .. radio .. ".lua")() 23 | accX = 0 24 | accY = 0 25 | accZ = 0 26 | pitchDeg = 0 27 | pitchRad = 0 28 | rollDeg = 0 29 | rollRad = 0 30 | horizonWidth = (layoutEngine.width - 60) / 2 31 | horizonHeight = 0 32 | horizonPitchDelta = 0 33 | 34 | end 35 | 36 | 37 | -- Round Acc data when close to bounds 38 | local function roundData(acc) 39 | if math.abs(acc) <= 0.02 then 40 | return 0 41 | end 42 | if acc > 0.98 then 43 | return 1 44 | end 45 | if acc < -0.98 then 46 | return -1 47 | end 48 | return acc 49 | end 50 | 51 | -- Check whether values changed significantly 52 | local function notSignificantlyDifferent(x, y, z) 53 | return (math.abs(accX - x) < 0.1) and (math.abs(accY - y) < 0.1) and (math.abs(accZ - z) < 0.1) 54 | end 55 | 56 | local function shouldRefresh(lastTimeSinceRedraw) 57 | -- Refresh Accelerometer values 58 | local newAccX = roundData(getValue("AccX")) 59 | local newAccY = roundData(getValue("AccY")) 60 | local newAccZ = roundData(getValue("AccZ")) 61 | 62 | if notSignificantlyDifferent(newAccX, newAccY, newAccZ) then 63 | return false 64 | end 65 | 66 | accX = newAccX 67 | accY = newAccY 68 | accZ = newAccZ 69 | 70 | -- Compute horizon from accelerometer values 71 | if (accX == 0) and (accY == 0) and (accZ == 0) then 72 | -- When no value, set to 0 73 | pitchDeg = 0 74 | pitchRad = 0 75 | rollDeg = 0 76 | rollRad = 0 77 | horizonWidth = (layoutEngine.width - 60) / 2 78 | horizonHeight = 0 79 | horizonPitchDelta = 0 80 | else 81 | -- When value, compute Horizon 82 | local div = math.sqrt(accY * accY + accZ * accZ) 83 | pitchRad = math.atan2(accX, div) 84 | pitchDeg = math.floor(math.deg(pitchRad)) 85 | rollRad = math.atan2(accY, accZ) 86 | rollDeg = math.floor(math.deg(rollRad)) 87 | 88 | -- Size of horizon bar depends on Roll and Pitch 89 | local scale = 2 - math.min(1, math.abs(pitchDeg) / 45) -- Scale goes from 2 (when pitch < 45°) to 1 when pitch > 45° 90 | local r1 = (layoutEngine.width - 60) / 2 91 | local r2 = scale * layoutEngine.height / 4 92 | local sinRoll = math.sin(rollRad) 93 | local cosRoll = math.cos(rollRad) 94 | div = math.sqrt(r2*r2*cosRoll*cosRoll + r1*r1*sinRoll*sinRoll) 95 | horizonWidth = math.floor((r2 / scale) * r1 * cosRoll / div) 96 | horizonHeight = math.floor(r1 * r2 * sinRoll / div) 97 | horizonPitchDelta = math.floor(2 * r2 / scale * pitchDeg / 90) 98 | end 99 | return true 100 | end 101 | 102 | local function layout() 103 | layoutEngine.layout() 104 | end 105 | 106 | local function redraw() 107 | layoutEngine.redraw(horizonWidth, horizonHeight, horizonPitchDelta, pitchDeg, rollDeg) 108 | end 109 | 110 | return { tag = "horizon", init = init, layout = layout, redraw = redraw, shouldRefresh = shouldRefresh } 111 | -------------------------------------------------------------------------------- /RESOURCEPOOL/widgets/horizon-x9.lua: -------------------------------------------------------------------------------- 1 | --------------------------------- 2 | -- GLOBAL VARIABLES 3 | --------------------------------- 4 | local WIDGET_START_X = 40 5 | local WIDGET_START_Y = 20 6 | local WIDGET_WIDTH = 133 7 | local WIDGET_HEIGHT = 45 8 | local HORIZON_WIDTH = 123 9 | local HORIZON_HEIGHT = 39 10 | 11 | local x0 = WIDGET_START_X + 5 12 | local y0 = WIDGET_START_Y + 3 13 | local centerX = x0 + HORIZON_WIDTH / 2 14 | local centerY = WIDGET_START_Y + WIDGET_HEIGHT / 2 15 | local x1 = x0 + HORIZON_WIDTH 16 | 17 | -- Draw degree value for sidebar 18 | local function drawDegLine(cx, cy, offsetY, isTen, value) 19 | if isTen then 20 | lcd.drawLine(cx - 6, cy + offsetY, cx, cy + offsetY, SOLID, 0) 21 | if value < 0 then 22 | lcd.drawText(cx - 21, cy + offsetY - 2, value, INVERS + SMLSIZE) 23 | elseif value < 10 then 24 | lcd.drawText(cx - 11, cy + offsetY - 2, value, INVERS + SMLSIZE) 25 | else 26 | lcd.drawText(cx - 16, cy + offsetY - 2, value, INVERS + SMLSIZE) 27 | end 28 | else 29 | lcd.drawLine(cx - 4, cy + offsetY, cx, cy + offsetY, SOLID, 0) 30 | end 31 | end 32 | 33 | -- Draw the side bar 34 | local function drawSideBar(cx, cy, ay0, up) 35 | -- number of pixels between lines 36 | local offset5 = 4 37 | local missingOffset, realOffset, inc, s 38 | -- compute remaining degrees before next multiple of 5 39 | if up then 40 | s = -1 41 | missingOffset = 5 - (ay0 % 5) 42 | realOffset = offset5 * missingOffset / 5 43 | inc = 5 44 | else 45 | s = 1 46 | missingOffset = ay0 % 5 47 | realOffset = offset5 * missingOffset / 5 48 | inc = -5 49 | end 50 | local n = ay0 - s * missingOffset 51 | local isTen = (n % 10) == 0 52 | for i = 0,3 do 53 | drawDegLine(cx, cy, s * (realOffset + i * offset5), isTen, n + i * inc) 54 | isTen = not isTen 55 | end 56 | end 57 | 58 | local function layout() 59 | -- fill region in grey 60 | lcd.drawFilledRectangle(WIDGET_START_X, WIDGET_START_Y, WIDGET_WIDTH, WIDGET_HEIGHT, ERASE) 61 | lcd.drawFilledRectangle(x0, y0, HORIZON_WIDTH, HORIZON_HEIGHT, SOLID + GREY_DEFAULT) 62 | -- left wing 63 | lcd.drawLine(x0 + 40, centerY, x0 + 50, centerY, SOLID, 0) 64 | lcd.drawLine(x0 + 50, centerY, x0 + 50, centerY + 4, SOLID, 0) 65 | -- right wing 66 | lcd.drawLine(x1 - 40, centerY, x1 - 50, centerY, SOLID, 0) 67 | lcd.drawLine(x1 - 50, centerY, x1 - 50, centerY + 4, SOLID, 0) 68 | -- cockpit center 69 | lcd.drawLine(centerX - 3, centerY, centerX + 3, centerY, SOLID, 0) 70 | lcd.drawLine(centerX, centerY - 2, centerX, centerY + 2, SOLID, 0) 71 | -- contour 72 | lcd.drawRectangle(WIDGET_START_X, WIDGET_START_Y, WIDGET_WIDTH, WIDGET_HEIGHT, GREY_DEFAULT) 73 | end 74 | 75 | local function redraw(horizonWidth, horizonHeight, horizonPitchDelta, pitchDeg, rollDeg) 76 | -- up lines 77 | drawSideBar(x0 + HORIZON_WIDTH - 1, centerY, pitchDeg, true) 78 | -- down lines 79 | drawSideBar(x0 + HORIZON_WIDTH - 1, centerY, pitchDeg, false) 80 | -- horizon dashes 81 | lcd.drawLine(centerX - horizonWidth, centerY + horizonPitchDelta + horizonHeight, centerX + horizonWidth , centerY + horizonPitchDelta - horizonHeight, DOTTED, 0) 82 | if DEBUG then 83 | lcd.drawText(46, 40, "roll" .. math.floor(rollDeg), SMLSIZE) 84 | lcd.drawText(46, 48, "ptch" .. math.floor(pitchDeg), SMLSIZE) 85 | end 86 | end 87 | 88 | return { layout = layout, redraw = redraw, width = 40, height = 40 } 89 | -------------------------------------------------------------------------------- /RESOURCEPOOL/widgets/horizon-x7.lua: -------------------------------------------------------------------------------- 1 | --------------------------------- 2 | -- GLOBAL VARIABLES 3 | --------------------------------- 4 | local WIDGET_START_X = 33 5 | local WIDGET_START_Y = 16 6 | local WIDGET_WIDTH = 95 7 | local WIDGET_HEIGHT = 48 8 | local HORIZON_WIDTH = 86 9 | local HORIZON_HEIGHT = 43 10 | 11 | local x0 = WIDGET_START_X + 5 12 | local y0 = WIDGET_START_Y + 3 13 | local centerX = x0 + HORIZON_WIDTH / 2 14 | local centerY = WIDGET_START_Y + WIDGET_HEIGHT / 2 15 | local x1 = x0 + HORIZON_WIDTH 16 | 17 | -- Draw degree value for sidebar 18 | local function drawDegLine(cx, cy, offsetY, isTen, value) 19 | if isTen then 20 | lcd.drawLine(cx - 6, cy + offsetY, cx, cy + offsetY, SOLID, 0) 21 | if value < 0 then 22 | lcd.drawText(cx - 21, cy + offsetY - 2, value, INVERS + SMLSIZE) 23 | elseif value < 10 then 24 | lcd.drawText(cx - 11, cy + offsetY - 2, value, INVERS + SMLSIZE) 25 | else 26 | lcd.drawText(cx - 16, cy + offsetY - 2, value, INVERS + SMLSIZE) 27 | end 28 | else 29 | lcd.drawLine(cx - 4, cy + offsetY, cx, cy + offsetY, SOLID, 0) 30 | end 31 | end 32 | 33 | -- Draw the side bar 34 | local function drawSideBar(cx, cy, ay0, up) 35 | -- number of pixels between lines 36 | local offset5 = 4 37 | local missingOffset, realOffset, inc, s 38 | -- compute remaining degrees before next multiple of 5 39 | if up then 40 | s = -1 41 | missingOffset = 5 - (ay0 % 5) 42 | realOffset = offset5 * missingOffset / 5 43 | inc = 5 44 | else 45 | s = 1 46 | missingOffset = ay0 % 5 47 | realOffset = offset5 * missingOffset / 5 48 | inc = -5 49 | end 50 | local n = ay0 - s * missingOffset 51 | local isTen = (n % 10) == 0 52 | for i = 0,3 do 53 | drawDegLine(cx, cy, s * (realOffset + i * offset5), isTen, n + i * inc) 54 | isTen = not isTen 55 | end 56 | end 57 | 58 | local function layout() 59 | -- fill region in grey 60 | lcd.drawFilledRectangle(WIDGET_START_X, WIDGET_START_Y, WIDGET_WIDTH, WIDGET_HEIGHT, ERASE) 61 | lcd.drawRectangle(x0, y0, HORIZON_WIDTH, HORIZON_HEIGHT, GREY_DEFAULT) 62 | -- left wing 63 | lcd.drawLine(centerX + 7, centerY, centerX + 14, centerY, SOLID, 0) 64 | lcd.drawLine(centerX + 7, centerY, centerX + 7, centerY + 4, SOLID, 0) 65 | -- right wing 66 | lcd.drawLine(centerX - 7, centerY, centerX - 14, centerY, SOLID, 0) 67 | lcd.drawLine(centerX - 7, centerY, centerX - 7, centerY + 4, SOLID, 0) 68 | -- cockpit center 69 | lcd.drawLine(centerX - 3, centerY, centerX + 3, centerY, SOLID, 0) 70 | lcd.drawLine(centerX, centerY - 2, centerX, centerY + 2, SOLID, 0) 71 | -- contour 72 | lcd.drawRectangle(WIDGET_START_X, WIDGET_START_Y, WIDGET_WIDTH, WIDGET_HEIGHT, GREY_DEFAULT) 73 | end 74 | 75 | local function redraw(horizonWidth, horizonHeight, horizonPitchDelta, pitchDeg, rollDeg) 76 | -- up lines 77 | drawSideBar(x0 + HORIZON_WIDTH - 1, centerY, pitchDeg, true) 78 | -- down lines 79 | drawSideBar(x0 + HORIZON_WIDTH - 1, centerY, pitchDeg, false) 80 | -- horizon dashes 81 | lcd.drawLine(centerX - horizonWidth, centerY + horizonPitchDelta + horizonHeight, centerX + horizonWidth , centerY + horizonPitchDelta - horizonHeight, DOTTED, 0) 82 | if DEBUG then 83 | lcd.drawText(46, 40, "roll" .. math.floor(rollDeg), SMLSIZE) 84 | lcd.drawText(46, 48, "ptch" .. math.floor(pitchDeg), SMLSIZE) 85 | end 86 | end 87 | 88 | return { layout = layout, redraw = redraw, width = 40, height = 40 } 89 | -------------------------------------------------------------------------------- /RESOURCEPOOL/widgets/battery-x9.lua: -------------------------------------------------------------------------------- 1 | --------------------------------- 2 | -- GLOBAL VARIABLES 3 | --------------------------------- 4 | local WIDGET_START_X = 0 5 | local WIDGET_START_BATTX = 10 6 | local WIDGET_START_Y = 0 7 | local WIDGET_START_BATTY = 13 8 | local WIDGET_WIDTH = 40 9 | local WIDGET_HEIGHT = 44 10 | 11 | -- Redraw battery value in volts 12 | local function redrawVoltage(battPercent, battVolt) 13 | if battPercent< 10 then 14 | if battVolt < 10 then 15 | lcd.drawNumber(WIDGET_START_BATTX + 3, WIDGET_START_Y + 3, battVolt * 10, PREC1) 16 | lcd.drawText(WIDGET_START_BATTX + 16, WIDGET_START_Y + 3, "V") 17 | else 18 | lcd.drawNumber(WIDGET_START_BATTX - 2, WIDGET_START_Y + 3, battVolt * 10, PREC1) 19 | lcd.drawText(WIDGET_START_BATTX + 16, WIDGET_START_Y + 3, "V") 20 | end 21 | else 22 | if battVolt < 10 then 23 | lcd.drawNumber(WIDGET_START_BATTX + 3, WIDGET_START_Y + 3, battVolt * 10, PREC1) 24 | lcd.drawText(WIDGET_START_BATTX + 16, WIDGET_START_Y + 3, "V", PREC1) 25 | else 26 | lcd.drawNumber(WIDGET_START_BATTX - 2, WIDGET_START_Y + 3, battVolt * 10, PREC1) 27 | lcd.drawText(WIDGET_START_BATTX + 16, WIDGET_START_Y + 3, "V", PREC1) 28 | end 29 | end 30 | end 31 | 32 | -- Redraw battery value in percent 33 | local function redrawPercent(battPercent) 34 | if battPercent<= 2 then 35 | lcd.drawText(WIDGET_START_BATTX - 3, WIDGET_START_Y + 3, "Empty") 36 | elseif battPercent< 10 then 37 | lcd.drawNumber(WIDGET_START_BATTX + 7, WIDGET_START_Y + 3, batt) 38 | lcd.drawText(WIDGET_START_BATTX + 13, WIDGET_START_Y + 3, "%") 39 | elseif battPercent< 98 then 40 | lcd.drawNumber(WIDGET_START_BATTX + 2, WIDGET_START_Y + 3, batt) 41 | lcd.drawText(WIDGET_START_BATTX + 13, WIDGET_START_Y + 3, "%") 42 | else 43 | lcd.drawText(WIDGET_START_BATTX, WIDGET_START_Y + 3, "Full") 44 | end 45 | end 46 | 47 | -- Redraw battery level container 48 | local function redrawBattery(battPercent) 49 | if battPercent>= 20 then 50 | if battPercent< 26 then 51 | lcd.drawFilledRectangle(WIDGET_START_BATTX + 3, WIDGET_START_BATTY + 21, 14, 4) 52 | elseif battPercent< 40 then 53 | lcd.drawFilledRectangle(WIDGET_START_BATTX + 3, WIDGET_START_BATTY + 17, 14, 8) 54 | elseif battPercent< 60 then 55 | lcd.drawFilledRectangle(WIDGET_START_BATTX + 3, WIDGET_START_BATTY + 13, 14, 12) 56 | elseif battPercent< 80 then 57 | lcd.drawFilledRectangle(WIDGET_START_BATTX + 3, WIDGET_START_BATTY + 9, 14, 16) 58 | else 59 | lcd.drawFilledRectangle(WIDGET_START_BATTX + 3, WIDGET_START_BATTY + 5, 14, 20) 60 | end 61 | for i = 0,4 do 62 | lcd.drawLine(WIDGET_START_BATTX + 3, WIDGET_START_BATTY + 5 + i * 4, WIDGET_START_BATTX + 16, WIDGET_START_BATTY + 5 + i * 4, SOLID, GREY_DEFAULT) 63 | end 64 | end 65 | end 66 | 67 | -- Layout battery widget container 68 | local function layout(battPercent) 69 | lcd.drawFilledRectangle(WIDGET_START_X, WIDGET_START_Y, WIDGET_WIDTH, WIDGET_HEIGHT, ERASE) 70 | if battPercent < 20 then 71 | lcd.drawPixmap(WIDGET_START_BATTX, WIDGET_START_BATTY, "/SCRIPTS/TELEMETRY/RESOURCEPOOL/images/batt-empty.bmp") 72 | else 73 | lcd.drawPixmap(WIDGET_START_BATTX, WIDGET_START_BATTY, "/SCRIPTS/TELEMETRY/RESOURCEPOOL/images/batt.bmp") 74 | end 75 | lcd.drawRectangle(WIDGET_START_X, WIDGET_START_Y, WIDGET_WIDTH, WIDGET_HEIGHT, GREY_DEFAULT) 76 | end 77 | 78 | -- Redraw battery widget 79 | local function redraw(showBattVoltage, battPercent, battVolt) 80 | if showBattVoltage then 81 | redrawVoltage(battPercent, battVolt) 82 | else 83 | redrawPercent(battPercent) 84 | end 85 | redrawBattery(battPercent) 86 | end 87 | 88 | 89 | return { layout = layout, redraw = redraw } 90 | -------------------------------------------------------------------------------- /RESOURCEPOOL/widgets/battery-x7.lua: -------------------------------------------------------------------------------- 1 | --------------------------------- 2 | -- GLOBAL VARIABLES 3 | --------------------------------- 4 | local WIDGET_START_X = 0 5 | local WIDGET_START_BATTX = 5 6 | local WIDGET_START_Y = 26 7 | local WIDGET_START_BATTY = 36 8 | local WIDGET_WIDTH = 34 9 | local WIDGET_HEIGHT = 27 10 | local BATT_WIDTH = 25 11 | local BATT_HEIGHT = 15 12 | -- Redraw battery value in volts 13 | local function redrawVoltage(battPercent, battVolt) 14 | if battPercent< 10 then 15 | if battVolt < 10 then 16 | lcd.drawNumber(WIDGET_START_BATTX + 6, WIDGET_START_Y + 2, battVolt * 10, PREC1 + SMLSIZE) 17 | lcd.drawText(WIDGET_START_BATTX + 19, WIDGET_START_Y + 2, "V", SMLSIZE) 18 | else 19 | lcd.drawNumber(WIDGET_START_BATTX + 2, WIDGET_START_Y + 2, battVolt * 10, PREC1 + SMLSIZE) 20 | lcd.drawText(WIDGET_START_BATTX + 19, WIDGET_START_Y + 2, "V", SMLSIZE) 21 | end 22 | else 23 | if battVolt < 10 then 24 | lcd.drawNumber(WIDGET_START_BATTX + 6, WIDGET_START_Y + 2, battVolt * 10, PREC1 + SMLSIZE) 25 | lcd.drawText(WIDGET_START_BATTX + 19, WIDGET_START_Y + 2, "V", PREC1 + SMLSIZE) 26 | else 27 | lcd.drawNumber(WIDGET_START_BATTX + 2, WIDGET_START_Y + 2, battVolt * 10, PREC1 + SMLSIZE) 28 | lcd.drawText(WIDGET_START_BATTX + 19, WIDGET_START_Y + 2, "V", PREC1 + SMLSIZE) 29 | end 30 | end 31 | end 32 | 33 | -- Redraw battery value in percent 34 | local function redrawPercent(battPercent) 35 | if battPercent<= 2 then 36 | lcd.drawText(WIDGET_START_BATTX, WIDGET_START_Y + 2, "Empty", SMLSIZE) 37 | elseif battPercent< 10 then 38 | lcd.drawNumber(WIDGET_START_BATTX + 7, WIDGET_START_Y + 2, battPercent, SMLSIZE) 39 | lcd.drawText(WIDGET_START_BATTX + 13, WIDGET_START_Y + 2, "%", SMLSIZE) 40 | elseif battPercent< 98 then 41 | lcd.drawNumber(WIDGET_START_BATTX + 2, WIDGET_START_Y + 2, battPercent, SMLSIZE) 42 | lcd.drawText(WIDGET_START_BATTX + 13, WIDGET_START_Y + 2, "%", SMLSIZE) 43 | else 44 | lcd.drawText(WIDGET_START_BATTX + 4, WIDGET_START_Y + 2, "Full", SMLSIZE) 45 | end 46 | end 47 | 48 | -- Redraw battery level container 49 | local function redrawBattery(battPercent) 50 | if battPercent>= 20 then 51 | if battPercent< 26 then 52 | lcd.drawFilledRectangle(WIDGET_START_BATTX + 1, WIDGET_START_BATTY + 2, 4, BATT_HEIGHT - 4) 53 | elseif battPercent< 40 then 54 | lcd.drawFilledRectangle(WIDGET_START_BATTX + 1, WIDGET_START_BATTY + 2, 8, BATT_HEIGHT - 4) 55 | elseif battPercent< 60 then 56 | lcd.drawFilledRectangle(WIDGET_START_BATTX + 1, WIDGET_START_BATTY + 2, 12, BATT_HEIGHT - 4) 57 | elseif battPercent< 80 then 58 | lcd.drawFilledRectangle(WIDGET_START_BATTX + 1, WIDGET_START_BATTY + 2, 16, BATT_HEIGHT - 4) 59 | else 60 | lcd.drawFilledRectangle(WIDGET_START_BATTX + 1, WIDGET_START_BATTY + 2, 20, BATT_HEIGHT - 4) 61 | end 62 | end 63 | for i = 0,4 do 64 | lcd.drawLine(WIDGET_START_BATTX + 1 + i * 4, WIDGET_START_BATTY + 1, WIDGET_START_BATTX + 1 + i * 4, WIDGET_START_BATTY + BATT_HEIGHT - 2, SOLID, ERASE) 65 | end 66 | if battPercent < 20 then 67 | lcd.drawText(WIDGET_START_BATTX + 3, WIDGET_START_BATTY + 5, "LOW!", SMLSIZE) 68 | end 69 | end 70 | 71 | -- Layout battery widget container 72 | local function layout(battPercent) 73 | lcd.drawFilledRectangle(WIDGET_START_X, WIDGET_START_Y, WIDGET_WIDTH, WIDGET_HEIGHT, ERASE) 74 | lcd.drawRectangle(WIDGET_START_X, WIDGET_START_Y, WIDGET_WIDTH, WIDGET_HEIGHT, GREY_DEFAULT) 75 | lcd.drawRectangle(WIDGET_START_BATTX, WIDGET_START_BATTY, BATT_WIDTH - 2, BATT_HEIGHT, GREY_DEFAULT) 76 | lcd.drawFilledRectangle(WIDGET_START_BATTX + BATT_WIDTH - 2, WIDGET_START_BATTY + BATT_HEIGHT / 2 - 3, 2, 6, FORCE) 77 | end 78 | 79 | -- Redraw battery widget 80 | local function redraw(showBattVoltage, battPercent, battVolt) 81 | if showBattVoltage then 82 | redrawVoltage(battPercent, battVolt) 83 | else 84 | redrawPercent(battPercent) 85 | end 86 | redrawBattery(battPercent) 87 | end 88 | 89 | 90 | return { layout = layout, redraw = redraw } 91 | -------------------------------------------------------------------------------- /Racer.lua: -------------------------------------------------------------------------------- 1 | --------------------------------- 2 | -- BEGIN OF CONFIGURATION 3 | --------------------------------- 4 | -- This is your configuration. Modify it accordingly. 5 | -- Tell us which radio you are using. It can be either x7 (Taranis QX7) or x9 (Taranis X9D / X9D+) 6 | RADIO = "x9" -- Use this line if you are using Taranis X9D / X9D + 7 | -- RADIO = "x7" -- Use this line if you are using Taranis QX7 8 | 9 | -- This represents the timer of your flight time. First timer has index 0, second 1, etc... 10 | -- You need to setup the timer yourself in your model. See docs for more details. 11 | FLIGHT_TIMER = 0 12 | -- Enable Real Heading if your drone has a compass. It will replace the arrows in the heading area by "North, West, South, East" 13 | REAL_HEADING = true 14 | -- Enable debug trace (will show debug data) 15 | DEBUG = false 16 | --------------------------------- 17 | -- END OF CONFIGURATION 18 | --------------------------------- 19 | 20 | --------------------------------- 21 | -- GLOBAL VARIABLES 22 | --------------------------------- 23 | -- Screen is 212x64 pixels 24 | local REFRESH_FREQUENCY_30MS = 3 25 | -- Always redraw each timeout seconds (3 seconds approx.) 26 | local INVALIDATE_TIMEOUT = 300 27 | --------------------------------- 28 | -- VARIABLES 29 | --------------------------------- 30 | local lastTime 31 | local lastRun 32 | 33 | local widgets 34 | local timerMatrix 35 | local refreshMatrix 36 | local firstDraw 37 | 38 | -- Load widget from file 39 | local function loadWidget(path) 40 | local w = loadScript(path)() 41 | w.init(RADIO) 42 | timerMatrix[w.tag] = 0 43 | refreshMatrix[w.tag] = false 44 | return w 45 | end 46 | 47 | -- Check whether widget should invalidate layout and redraw or not 48 | local function checkRefreshFlag(widget) 49 | if widget.shouldRefresh(lastTime - timerMatrix[widget.tag]) then 50 | timerMatrix[widget.tag] = lastTime 51 | refreshMatrix[widget.tag] = true 52 | end 53 | end 54 | 55 | -- Perform screen update of widget 56 | local function refreshWidget(widget, force) 57 | if refreshMatrix[widget.tag] or force then 58 | widget.layout() 59 | widget.redraw() 60 | if DEBUG then 61 | lcd.drawText(46, 32, "drw" .. math.floor(lastTime / 10) % 90 + 10, SMLSIZE) 62 | end 63 | timerMatrix[widget.tag] = lastTime 64 | refreshMatrix[widget.tag] = false 65 | end 66 | end 67 | 68 | -- Init telemetry screen 69 | local function init() 70 | firstDraw = true 71 | lastTime = 0 72 | lastRun = 0 73 | timerMatrix = {} 74 | refreshMatrix = {} 75 | widgets = { 76 | loadWidget("/SCRIPTS/TELEMETRY/RESOURCEPOOL/widgets/heading.lua"), 77 | loadWidget("/SCRIPTS/TELEMETRY/RESOURCEPOOL/widgets/flighttime.lua"), 78 | loadWidget("/SCRIPTS/TELEMETRY/RESOURCEPOOL/widgets/battery.lua"), 79 | loadWidget("/SCRIPTS/TELEMETRY/RESOURCEPOOL/widgets/horizon.lua"), 80 | loadWidget("/SCRIPTS/TELEMETRY/RESOURCEPOOL/widgets/rssi.lua"), 81 | loadWidget("/SCRIPTS/TELEMETRY/RESOURCEPOOL/widgets/flightmode.lua") 82 | } 83 | end 84 | 85 | local function background() 86 | local currentTime = getTime() 87 | -- Refresh at specific frequency 88 | if currentTime > lastTime + REFRESH_FREQUENCY_30MS then 89 | lastTime = currentTime 90 | for _, w in ipairs(widgets) do 91 | checkRefreshFlag(w) 92 | end 93 | end 94 | -- Force complete refresh at specific intervals 95 | if lastTime - lastRun > INVALIDATE_TIMEOUT then 96 | firstDraw = true 97 | end 98 | end 99 | 100 | local function run(event) 101 | if firstDraw then 102 | lcd.clear() 103 | for _, w in ipairs(widgets) do 104 | refreshWidget(w, true) 105 | end 106 | firstDraw = false 107 | lastRun = lastTime 108 | return 109 | end 110 | if DEBUG then 111 | lcd.drawText(46, 24, "cmp" .. math.floor(lastTime / 10) % 90 + 10, SMLSIZE) 112 | end 113 | for _, w in ipairs(widgets) do 114 | refreshWidget(w) 115 | end 116 | end 117 | 118 | return { init = init, background = background, run = run } 119 | --------------------------------------------------------------------------------