2 -- An FRSKY S.Port <passthrough protocol> based Telemetry script for Taranis X7
4 -- Copyright (C) 2018. Alessandro Apostoli
5 -- https://github.com/yaapu
7 -- This program is free software; you can redistribute it and/or modify
8 -- it under the terms of the GNU General Public License as published by
9 -- the Free Software Foundation; either version 3 of the License, or
10 -- (at your option) any later version.
12 -- This program is distributed in the hope that it will be useful,
13 -- but WITHOUT ANY WARRANTY, without even the implied warranty of
14 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 -- GNU General Public License for more details.
17 -- You should have received a copy of the GNU General Public License
18 -- along with this program; if not, see <http://www.gnu.org/licenses>.
20 -- Passthrough protocol reference:
21 -- https://cdn.rawgit.com/ArduPilot/ardupilot_wiki/33cd0c2c/images/FrSky_Passthrough_protocol.xlsx
23 -- Borrowed some code from the LI-xx BATTCHECK v3.30 script
24 -- http://frskytaranis.forumactif.org/t2800-lua-download-un-testeur-de-batterie-sur-la-radio
27 local cellfull
, cellempty
= 4.2, 3.00
28 local cell
= {0, 0, 0, 0, 0 ,0}
29 local cellsumfull
, cellsumempty
, cellsumtype
, cellsum
= 0, 0, 0, 0
31 local i
, cellmin
, cellresult
= 0, cellfull
, 0, 0
35 local flightModes
= {}
37 flightModes
[1]="Stabilize"
39 flightModes
[3]="AltHold"
41 flightModes
[5]="Guided"
42 flightModes
[6]="Loiter"
44 flightModes
[8]="Circle"
46 flightModes
[10]="Land"
48 flightModes
[12]="Drift"
50 flightModes
[14]="Sport"
51 flightModes
[15]="Flip"
52 flightModes
[16]="AutoTune"
53 flightModes
[17]="PosHold"
54 flightModes
[18]="Brake"
55 flightModes
[19]="Throw"
56 flightModes
[20]="Avoid"
57 flightModes
[21]="GuiNGPS"
59 local gpsStatuses
= {}
60 gpsStatuses
[0]="NoGPS"
61 gpsStatuses
[1]="NoLock"
62 gpsStatuses
[2]="2DFIX"
63 gpsStatuses
[3]="3DFix"
65 local mavSeverity
= {}
75 local soundFileBasePath
= "/SOUNDS/yaapu0/en"
78 soundFiles
["bat5"] = "bat5.wav"
79 soundFiles
["bat10"] = "bat10.wav"
80 soundFiles
["bat15"] = "bat15.wav"
81 soundFiles
["bat20"] = "bat20.wav"
82 soundFiles
["bat25"] = "bat25.wav"
83 soundFiles
["bat30"] = "bat30.wav"
84 soundFiles
["bat40"] = "bat40.wav"
85 soundFiles
["bat50"] = "bat50.wav"
86 soundFiles
["bat60"] = "bat60.wav"
87 soundFiles
["bat75"] = "bat75.wav"
89 soundFiles
["gpsfix"] = "gpsfix.wav"
90 soundFiles
["gpsnofix"] = "gpsnofix.wav"
92 soundFiles
["lowbat"] = "lowbat.wav"
93 soundFiles
["ekf"] = "ekf.wav"
95 soundFiles
["yaapu"] = "yaapu.wav"
96 soundFiles
["landing"] = "landing.wav"
97 soundFiles
["armed"] = "armed.wav"
98 soundFiles
["disarmed"] = "disarmed.wav"
100 soundFiles
["land"] = "land.wav"
101 soundFiles
["auto"] = "auto.wav"
102 soundFiles
["stabilize"] = "stabilize.wav"
103 soundFiles
["althold"] = "althold.wav"
104 soundFiles
["poshold"] = "poshold.wav"
105 soundFiles
["loiter"] = "loiter.wav"
106 soundFiles
["autotune"] = "autotune.wav"
107 soundFiles
["rtl"] = "rtl.wav"
108 soundFiles
["autotune"] = "autotune.wav"
113 local landComplete
= 0
114 local statusArmed
= 0
115 local batteryFailsafe
= 0
116 local lastBatteryFailsafe
= 0
117 local ekfFailsafe
= 0
118 local lastEkfFailsafe
= 0
127 local battCurrent
= 0
131 local battCurrent2
= 0
137 -- MESSAGES max 10 lines
138 local messageBuffer
= ""
139 local messageHistory
= {}
142 local messageDuplicate
= 1
143 local lastMessage
= ""
144 local lastMessageValue
= 0
145 local lastMessageTime
= 0
154 local SENSOR_ID
,FRAME_ID
,DATA_ID
,VALUE
158 local paramId
,paramValue
160 local battFailsafeVoltage
= 0
161 local battFailsafeCapacity
= 0
162 local battCapacity
= 0
169 local noTelemetryData
= 1
171 local function playSound(soundFile
)
172 playFile(soundFileBasePath
.. "/" .. soundFiles
[soundFile
])
175 local function getValueOrDefault(value
)
176 local tmp
= getValue(value
)
183 local function pushMessage(severity
, msg
)
184 if ( severity
< 4) then
190 if msg
== lastMessage
then
191 messageDuplicate
= messageDuplicate
+ 1
192 if messageDuplicate
> 1 then
193 if string.len(mm
) > 19 then
194 mm
= string.sub(mm
,1,19)
195 messageHistory
[messageIdx
- 1] = string.format("%d.%s %-20s (x%d)", messageIdx
- 1, mavSeverity
[severity
], mm
, messageDuplicate
)
197 messageHistory
[messageIdx
- 1] = string.format("%d.%s %s (x%d)", messageIdx
- 1, mavSeverity
[severity
], msg
, messageDuplicate
)
201 messageHistory
[messageIdx
] = string.format("%d.%s %s", messageIdx
, mavSeverity
[severity
], msg
)
202 messageIdx
= messageIdx
+ 1
206 lastMessageTime
= getTime() -- valore in secondi
210 local lastTimerStart
= 0
212 local function startTimer()
213 lastTimerStart
= getTime()/100
216 local function stopTimer()
217 seconds
= seconds
+ getTime()/100 - lastTimerStart
221 local function symTimer()
222 thrOut
= getValue("thr")
223 if (thrOut
> -500 ) then
230 local function symGPS()
231 thrOut
= getValue("thr")
232 if (thrOut
> 0 ) then
239 elseif thrOut
> -500 then
256 local function symBatt()
257 thrOut
= getValue("thr")
258 if (thrOut
> 0 ) then
259 LIPObatt
= 1350 + ((thrOut
)*0.01 * 30)
260 LIPOcelm
= LIPObatt
/4
261 battCurrent
= 100 + ((thrOut
)*0.01 * 30)
262 battVolt
= LIPObatt
*0.1
268 homeDist
= math
.abs(thrOut
)*2
272 -- simulates attitude by using channel 1 for roll, channel 2 for pitch and channel 4 for yaw
273 local function symAttitude()
277 -- roll [-1024,1024] ==> [-180,180]
278 rollCh
= getValue("ch1") * 0.175
279 -- pitch [1024,-1024] ==> [-90,90]
280 pitchCh
= getValue("ch2") * 0.0878
281 -- yaw [-1024,1024] ==> [0,360]
282 yawCh
= getValue("ch10")
283 if ( yawCh
>= 0) then
284 yawCh
= yawCh
* 0.175
286 yawCh
= 360 + (yawCh
* 0.175)
293 local function symHome()
296 -- home angle in deg [0-360]
297 S2Ch
= getValue("ch12")
298 yawCh
= getValue("ch4")
301 if ( yawCh
>= 0) then
302 yawCh
= yawCh
* 0.175
304 yawCh
= 360 + (yawCh
* 0.175)
309 S2Ch
= 360 + (S2Ch
* 0.175)
311 if (thrOut
> 0 ) then
319 local function processTelemetry()
320 SENSOR_ID
,FRAME_ID
,DATA_ID
,VALUE
= sportTelemetryPop()
330 if ( FRAME_ID
== 0x10) then
332 if ( DATA_ID
== 0x5006) then -- ROLLPITCH
333 -- roll [0,1800] ==> [-180,180]
334 roll
= (bit32
.extract(VALUE
,0,11) - 900) * 0.2
335 -- pitch [0,900] ==> [-90,90]
336 pitch
= (bit32
.extract(VALUE
,11,10) - 450) * 0.2
337 elseif ( DATA_ID
== 0x5005) then -- VELANDYAW
338 vSpeed
= bit32
.extract(VALUE
,1,7) * (10^bit32
.extract(VALUE
,0,1))
339 if (bit32
.extract(VALUE
,8,1) == 1) then
342 hSpeed
= bit32
.extract(VALUE
,10,7) * (10^bit32
.extract(VALUE
,9,1))
343 yaw
= bit32
.extract(VALUE
,17,11) * 0.2
344 elseif ( DATA_ID
== 0x5001) then -- AP STATUS
345 flightMode
= bit32
.extract(VALUE
,0,5)
346 simpleMode
= bit32
.extract(VALUE
,5,2)
347 landComplete
= bit32
.extract(VALUE
,7,1)
348 statusArmed
= bit32
.extract(VALUE
,8,1)
349 batteryFailsafe
= bit32
.extract(VALUE
,9,1)
350 ekfFailsafe
= bit32
.extract(VALUE
,10,2)
351 elseif ( DATA_ID
== 0x5002) then -- GPS STATUS
352 numSats
= bit32
.extract(VALUE
,0,4)
353 gpsStatus
= bit32
.extract(VALUE
,4,2)
354 gpsHdopC
= bit32
.extract(VALUE
,7,7) * (10^bit32
.extract(VALUE
,6,1)) -- dm
355 gpsAlt
= bit32
.extract(VALUE
,24,7) * (10^bit32
.extract(VALUE
,22,2)) -- dm
356 if (bit32
.extract(VALUE
,31,1) == 1) then
359 elseif ( DATA_ID
== 0x5003) then -- BATT
360 battVolt
= bit32
.extract(VALUE
,0,9)
361 battCurrent
= bit32
.extract(VALUE
,10,7) * (10^bit32
.extract(VALUE
,9,1))
362 battMah
= bit32
.extract(VALUE
,17,15)
363 elseif ( DATA_ID
== 0x5008) then -- BATT2
364 battVolt2
= bit32
.extract(VALUE
,0,9)
365 battCurrent2
= bit32
.extract(VALUE
,10,7) * (10^bit32
.extract(VALUE
,9,1))
366 battMah2
= bit32
.extract(VALUE
,17,15)
367 elseif ( DATA_ID
== 0x5004) then -- HOME
368 homeDist
= bit32
.extract(VALUE
,2,10) * (10^bit32
.extract(VALUE
,0,2))
369 homeAlt
= bit32
.extract(VALUE
,14,10) * (10^bit32
.extract(VALUE
,12,2)) * 0.1
370 if (bit32
.extract(VALUE
,24,1) == 1) then
371 homeAlt
= homeAlt
* -1
373 homeAngle
= bit32
.extract(VALUE
, 25, 7) * 3
374 elseif ( DATA_ID
== 0x5000) then -- MESSAGES
375 if (VALUE
~= lastMessageValue
) then
376 lastMessageValue
= VALUE
377 c1
= bit32
.extract(VALUE
,0,7)
378 c2
= bit32
.extract(VALUE
,8,7)
379 c3
= bit32
.extract(VALUE
,16,7)
380 c4
= bit32
.extract(VALUE
,24,7)
381 messageBuffer
= messageBuffer
.. string.char(c4
)
382 messageBuffer
= messageBuffer
.. string.char(c3
)
383 messageBuffer
= messageBuffer
.. string.char(c2
)
384 messageBuffer
= messageBuffer
.. string.char(c1
)
385 if (c1
== 0 or c2
== 0 or c3
== 0 or c4
== 0) then
386 severity
= (bit32
.extract(VALUE
,15,1) * 4) + (bit32
.extract(VALUE
,23,1) * 2) + (bit32
.extract(VALUE
,30,1) * 1)
387 pushMessage( severity
, messageBuffer
)
391 elseif ( DATA_ID
== 0x5007) then -- PARAMS
392 paramId
= bit32
.extract(VALUE
,24,4)
393 paramValue
= bit32
.extract(VALUE
,0,24)
395 frameType
= paramValue
396 elseif paramId
== 2 then
397 battFailsafeVoltage
= paramValue
398 elseif paramId
== 3 then
399 battFailsafeCapacity
= paramValue
400 elseif paramId
== 4 then
401 battCapacity
= paramValue
407 local function telemetryEnabled()
408 if getValue("RxBt") == 0 then
412 return noTelemetryData
== 0
415 local battSource
= "na"
417 local function calcBattery()
422 cellResult
= getValue("Cels")
424 if type(cellResult
) == "table" then
427 for i
= 1, #cell
do cell
[i
] = 0 end
428 cellsumtype
= #cellResult
429 for i
, v
in pairs(cellResult
) do
430 cellsum
= cellsum
+ v
437 -- cels is not defined let's check if A2 is defined
439 battA2
= getValue("A2")
445 elseif battA2
> 17 then
447 elseif battA2
> 13 then
453 cellmin
= battA2
/cellCount
456 -- A2 is not defined, last chance is battVolt
459 cellsum
= battVolt
*0.1
462 elseif cellsum
> 17 then
464 elseif cellsum
> 13 then
470 cellmin
= cellsum
/cellCount
475 LIPOcelm
= cellmin
*100
476 LIPObatt
= cellsum
*100
479 local function drawBattery()
480 if (battCapacity
> 0) then
481 LIPOperc
= (1 - (battMah
/battCapacity
))*100
485 lcd
.drawRectangle(maxX
+1,17,28,16,SOLID
)
486 lcd
.drawFilledRectangle(maxX
+1,17,28,16,SOLID
)
487 -- display battery voltage
488 lcd
.drawText(maxX
+ 28, 19, "v", SMLSIZE
+RIGHT
+INVERS
)
489 lcd
.drawNumber(lcd
.getLastLeftPos(), 18, LIPObatt
, PREC2
+RIGHT
+INVERS
)
490 -- display battery current
491 lcd
.drawText(maxX
+ 28, 26, "A", SMLSIZE
+RIGHT
+INVERS
)
492 lcd
.drawNumber(lcd
.getLastLeftPos(), 26, battCurrent
, SMLSIZE
+PREC1
+RIGHT
+INVERS
)
494 -- display lowest cell voltage
495 if LIPOcelm
< 350 then
496 lcd
.drawNumber(maxX
+ 30, 16, LIPOcelm
, DBLSIZE
+BLINK
+PREC2
)
498 lcd
.drawNumber(maxX
+ 30, 16, LIPOcelm
, DBLSIZE
+PREC2
)
500 lcd
.drawText(lcd
.getLastRightPos(), 17, "v", SMLSIZE
)
501 lcd
.drawText(128, 25, battSource
, SMLSIZE
+RIGHT
)
503 -- display capacity %
505 lcd
.drawText(128, yy
, "Ah", SMLSIZE
+RIGHT
)
506 lcd
.drawNumber(lcd
.getLastLeftPos(), yy
, battCapacity
/100, SMLSIZE
+PREC1
+RIGHT
)
507 lcd
.drawText(lcd
.getLastLeftPos(), yy
, "/", SMLSIZE
+RIGHT
)
508 lcd
.drawNumber(lcd
.getLastLeftPos(), yy
, battMah
/100, SMLSIZE
+PREC1
+RIGHT
)
510 lcd
.drawRectangle(maxX
+1,yy
-2,17,12,SOLID
)
511 lcd
.drawFilledRectangle(maxX
+1,yy
-2,17,12,SOLID
)
513 lcd
.drawText(maxX
+17, yy
, "%", SMLSIZE
+INVERS
+RIGHT
)
514 lcd
.drawNumber(lcd
.getLastLeftPos(), yy
-1, LIPOperc
, INVERS
+RIGHT
)
517 local function drawFlightMode()
518 lcd
.drawFilledRectangle(0,0, 128, 9, SOLID
)
519 lcd
.drawRectangle(0, 0, 128, 9, SOLID
)
521 if (not telemetryEnabled()) then
522 lcd
.drawFilledRectangle((128-100)/2,18, 100, 30, SOLID
)
523 lcd
.drawText(17, 29, "no telemetry data", INVERS
)
527 local strMode
= flightModes
[flightMode
]
529 lcd
.drawText(1, 1, strMode
, SMLSIZE
+INVERS
)
531 if ( simpleMode
== 1) then
532 lcd
.drawText(lcd
.getLastRightPos(), 1, "(S)", SMLSIZE
+INVERS
)
535 if (statusArmed
== 1) then
536 lcd
.drawText(18, 47, "ARMED", SMLSIZE
+INVERS
)
538 lcd
.drawText(18, 47, "DISARMED", SMLSIZE
+INVERS
+BLINK
)
542 local function drawHome()
546 lcd
.drawLine(xx
,13,xx
+ 8,13, SOLID
, 0)
548 lcd
.drawLine(xx
+1,12,xx
+ 2,11, SOLID
, 0)
549 lcd
.drawLine(xx
+1,14,xx
+ 2,15, SOLID
, 0)
551 lcd
.drawLine(xx
+7,12,xx
+ 6,11, SOLID
, 0)
552 lcd
.drawLine(xx
+7,14,xx
+ 6,15, SOLID
, 0)
554 lcd
.drawNumber(xx
+ 11, yy
, homeDist
, SMLSIZE
)
555 lcd
.drawText(lcd
.getLastRightPos(), yy
, "m",SMLSIZE
)
558 local function drawMessage()
559 lcd
.drawFilledRectangle(0,55, 212, 9, SOLID
)
560 lcd
.drawRectangle(0, 55, 212, 9, SOLID
)
561 local now
= getTime()
562 if (now
- lastMessageTime
) > 300 then
563 lcd
.drawText(1, 56, messageHistory
[messageIdx
-1],SMLSIZE
+INVERS
)
565 lcd
.drawText(1, 56, messageHistory
[messageIdx
-1],SMLSIZE
+INVERS
+BLINK
)
569 local function drawAllMessages()
571 if (messageIdx
<= 6) then
572 for i
= 1, messageIdx
- 1 do
573 lcd
.drawText(1, 1+10*(idx
- 1), messageHistory
[i
],SMLSIZE
)
577 for i
= messageIdx
- 6,messageIdx
- 1 do
578 lcd
.drawText(1, 1+10*(idx
-1), messageHistory
[i
],SMLSIZE
)
584 local function drawGPSStatus()
588 lcd
.drawRectangle(xx
,yy
- 1,50,15,SOLID
)
590 local strStatus
= gpsStatuses
[gpsStatus
]
593 if gpsStatus
> 2 then
594 lcd
.drawFilledRectangle(xx
,yy
- 1,50,15,SOLID
)
595 if homeAngle
~= -1 then
599 lcd
.drawText(xx
+ 2, yy
, strStatus
, SMLSIZE
+INVERS
)
600 lcd
.drawNumber(128, yy
, numSats
, SMLSIZE
+INVERS
+RIGHT
)
601 lcd
.drawText(xx
+ 2, yy
+ 7, "Hdop ", SMLSIZE
+INVERS
)
603 if gpsHdopC
> 100 then
604 lcd
.drawText(128, yy
+ 7, "10+", SMLSIZE
+INVERS
+RIGHT
+flags
)
606 lcd
.drawNumber(128, yy
+ 7, gpsHdopC
, SMLSIZE
+INVERS
+RIGHT
+PREC1
+flags
)
609 lcd
.drawText(xx
+ 5, yy
+ 3, strStatus
, BLINK
)
613 local function drawGrid()
614 lcd
.drawLine(maxX
, 0, maxX
, 63, SOLID
, 0)
617 local timerRunning
= 0
619 local function checkLandingStatus()
620 if ( timerRunning
== 0 and landComplete
== 1 and lastTimerStart
== 0) then
623 if (timerRunning
== 1 and landComplete
== 0 and lastTimerStart
~= 0) then
627 timerRunning
= landComplete
631 local function calcFlightTime()
633 if ( lastTimerStart
~= 0) then
634 elapsed
= getTime()/100 - lastTimerStart
636 flightTime
= elapsed
+ seconds
639 -- draws a line centered at ox,oy with given angle and length W/O CROPPING
640 local function drawLine(ox
,oy
,angle
,len
,style
,maxX
,maxY
)
641 local xx
= math
.cos(math
.rad(angle
)) * len
* 0.5
642 local yy
= math
.sin(math
.rad(angle
)) * len
* 0.5
649 lcd
.drawLine(x1
,y1
,x2
,y2
, style
,0)
652 -- draws a line centered at ox,oy with given angle and length WITH CROPPING
653 local function drawCroppedLine(ox
,oy
,angle
,len
,style
,maxX
,maxY
)
655 local xx
= math
.cos(math
.rad(angle
)) * len
* 0.5
656 local yy
= math
.sin(math
.rad(angle
)) * len
* 0.5
663 if (x1
>= maxX
and x2
>= maxX
) then
667 y1
= y1
- math
.tan(math
.rad(angle
)) * (maxX
- x1
)
671 y2
= y2
+ math
.tan(math
.rad(angle
)) * (maxX
- x2
)
674 lcd
.drawLine(x1
,y1
,x2
,y2
, style
,0)
677 local function drawFailsafe()
678 if ekfFailsafe
> 0 then
679 if lastEkfFailsafe
== 0 then
682 lcd
.drawText(maxX
/2 - 28, 47, "EKF FAILSAFE", SMLSIZE
+INVERS
+BLINK
)
684 if batteryFailsafe
> 0 then
685 if lastBatteryFailsafe
== 0 then
688 lcd
.drawText(maxX
/2 - 28, 47, "BAT FAILSAFE", SMLSIZE
+INVERS
+BLINK
)
690 lastEkfFailsafe
= ekfFailsafe
691 lastBatteryFailsafe
= batteryFailsafe
694 local function drawPitch()
697 -- horizon min max +/- 30°
703 if (pitch
< -30) then
707 -- y normalized at 32 +/-20 (0.75 = 20/32)
709 -- horizon, lower half of HUD filled in grey
710 --lcd.drawFilledRectangle(minX,y,maxX-minX,maxY-y + 1,GREY_DEFAULT)
712 -- center indicators for vSpeed and alt
714 lcd
.drawLine(minX
,32 - 5,minX
+ width
,32 - 5, SOLID
, 0)
715 lcd
.drawLine(minX
,32 + 4,minX
+ width
,32 + 4, SOLID
, 0)
716 lcd
.drawLine(minX
+ width
+ 1,32 + 4,minX
+ width
+ 5,32, SOLID
, 0)
717 lcd
.drawLine(minX
+ width
+ 1,32 - 4,minX
+ width
+ 5,32, SOLID
, 0)
718 lcd
.drawPoint(minX
+ width
+ 5,32)
720 lcd
.drawLine(maxX
- width
- 1,32 - 5,maxX
- 1,32 - 5,SOLID
,0)
721 lcd
.drawLine(maxX
- width
- 1,32 + 4,maxX
- 1,32 + 4,SOLID
,0)
722 lcd
.drawLine(maxX
- width
- 2,32 + 4,maxX
- width
- 6,32, SOLID
, 0)
723 lcd
.drawLine(maxX
- width
- 2,32 - 4,maxX
- width
- 6,32, SOLID
, 0)
724 lcd
.drawPoint(maxX
- width
- 6,32)
725 -- center value based on char count
727 if homeAlt
< 10 then -- 2 digits with decimal
728 lcd
.drawNumber(maxX
- 1,32 - 3,homeAlt
* 10,SMLSIZE
+PREC1
+RIGHT
)
730 lcd
.drawNumber(maxX
- 1 + 2,32 - 3,homeAlt
,SMLSIZE
+RIGHT
)
733 if homeAlt
> -10 then -- 1 digit with sign
734 lcd
.drawNumber(maxX
- 1,32 - 3,homeAlt
* 10,SMLSIZE
+PREC1
+RIGHT
)
735 else -- 3 digits with sign
736 lcd
.drawNumber(maxX
- 1,32 - 3,homeAlt
,SMLSIZE
+RIGHT
)
740 if (vSpeed
> 999) then
741 lcd
.drawNumber(minX
+ 1,32 - 3,vSpeed
*0.1,SMLSIZE
)
742 elseif (vSpeed
< -99) then
743 lcd
.drawNumber(minX
+ 1,32 - 3,vSpeed
* 0.1,SMLSIZE
)
745 lcd
.drawNumber(minX
+ 1,32 - 3,vSpeed
,SMLSIZE
+PREC1
)
747 -- up pointing center arrow
748 local x
= math
.floor(maxX
/2)
749 lcd
.drawLine(x
-10,34 + 5,x
,34 ,SOLID
,0)
750 lcd
.drawLine(x
+1 ,34 ,x
+ 10, 34 + 5,SOLID
,0)
753 local function drawRoll()
755 local r2
= 10 --vertical distance between roll horiz segments
756 local cx
,cy
,dx
,dy
,ccx
,ccy
,cccx
,cccy
757 -- no roll ==> segments are vertical, offsets are multiples of r2
768 -- center line offsets
769 dx
= math
.cos(math
.rad(90 - r
)) * -pitch
770 dy
= math
.sin(math
.rad(90 - r
)) * pitch
772 cx
= math
.cos(math
.rad(90 - r
)) * r2
773 cy
= math
.sin(math
.rad(90 - r
)) * r2
775 ccx
= math
.cos(math
.rad(90 - r
)) * 2 * r2
776 ccy
= math
.sin(math
.rad(90 - r
)) * 2 * r2
778 cccx
= math
.cos(math
.rad(90 - r
)) * 3 * r2
779 cccy
= math
.sin(math
.rad(90 - r
)) * 3 * r2
781 local x
= math
.floor(maxX
/2)
782 drawCroppedLine(dx
+ x
- cccx
,dy
+ 32 + cccy
,r
,4,DOTTED
,maxX
,maxY
)
783 drawCroppedLine(dx
+ x
- ccx
,dy
+ 32 + ccy
,r
,6,DOTTED
,maxX
,maxY
)
784 drawCroppedLine(dx
+ x
- cx
,dy
+ 32 + cy
,r
,9,DOTTED
,maxX
,maxY
)
785 drawCroppedLine(dx
+ x
,dy
+ 32,r
,16,SOLID
,maxX
,maxY
)
786 drawCroppedLine(dx
+ x
+ cx
,dy
+ 32 - cy
,r
,9,DOTTED
,maxX
,maxY
)
787 drawCroppedLine(dx
+ x
+ ccx
,dy
+ 32 - ccy
,r
,6,DOTTED
,maxX
,maxY
)
788 drawCroppedLine(dx
+ x
+ cccx
,dy
+ 32 - cccy
,r
,4,DOTTED
,maxX
,maxY
)
791 local function roundTo(val
,int
)
792 return math
.floor(val
/int
) * int
795 local function drawYaw()
796 local northX
= math
.floor(maxX
/2) - 2
797 local offset
= northX
- 4
798 local yawRounded
= math
.floor(yaw
)
799 local yawWindow
= roundTo(yaw
,10)
800 local maxXHalf
= math
.floor(maxX
/2)
801 local yawWindowOn
= false
802 local flags
= SMLSIZE
803 if (not(yawWindow
== 0 or yawWindow
== 90 or yawWindow
== 180 or yawWindow
== 270 or yawWindow
== 360)) then
807 if (yawRounded
== 0 or yawRounded
== 360) then
808 lcd
.drawText(northX
- offset
, minY
+1, "W", SMLSIZE
)
809 lcd
.drawText(northX
, minY
+1, "N", flags
)
810 lcd
.drawText(northX
+ offset
, minY
+1, "E", SMLSIZE
)
811 elseif (yawRounded
== 90) then
812 lcd
.drawText(northX
- offset
, minY
+1, "N", SMLSIZE
)
813 lcd
.drawText(northX
, minY
+1, "E", flags
)
814 lcd
.drawText(northX
+ offset
, minY
+1, "S", SMLSIZE
)
815 elseif (yawRounded
== 180) then
816 lcd
.drawText(northX
- offset
, minY
+1, "W", SMLSIZE
)
817 lcd
.drawText(northX
, minY
+1, "S", flags
)
818 lcd
.drawText(northX
+ offset
, minY
+1, "E", SMLSIZE
)
819 elseif (yawRounded
== 270) then
820 lcd
.drawText(northX
- offset
, minY
+1, "S", SMLSIZE
)
821 lcd
.drawText(northX
, minY
+1, "W", flags
)
822 lcd
.drawText(northX
+ offset
, minY
+1, "N", SMLSIZE
)
823 elseif ( yaw
> 0 and yaw
< 90) then
824 northX
= (maxXHalf
- 2) - 0.3*yaw
825 lcd
.drawText(northX
, minY
+1, "N", flags
)
826 lcd
.drawText(northX
+ offset
, minY
+1, "E", SMLSIZE
)
828 lcd
.drawText(northX
+ offset
+ offset
, minY
+1, "S", SMLSIZE
)
830 elseif ( yaw
> 90 and yaw
< 180) then
831 northX
= (maxXHalf
- 2) - 0.3*(yaw
- 180)
832 lcd
.drawText(northX
- offset
, minY
+1, "E", SMLSIZE
)
833 lcd
.drawText(northX
, minY
+1, "S", flags
)
835 lcd
.drawText(northX
+ offset
, minY
+1, "W", SMLSIZE
)
837 elseif ( yaw
> 180 and yaw
< 270) then
838 northX
= (maxXHalf
- 2) - 0.3*(yaw
- 270)
839 lcd
.drawText(northX
, minY
+1, "W", SMLSIZE
)
840 lcd
.drawText(northX
- offset
, minY
+1, "S", flags
)
842 lcd
.drawText(northX
- offset
- offset
, minY
+1, "E", SMLSIZE
)
844 elseif ( yaw
> 270 and yaw
< 360) then
845 northX
= (maxXHalf
- 2) - 0.3*(yaw
- 360)
846 lcd
.drawText(northX
, minY
+1, "N", SMLSIZE
)
847 lcd
.drawText(northX
- offset
, minY
+1, "W", flags
)
849 lcd
.drawText(northX
- offset
- offset
, minY
+1, "S", SMLSIZE
)
852 lcd
.drawText(northX
+ offset
, minY
+1, "E", SMLSIZE
)
855 lcd
.drawLine(minX
, minY
+7, maxX
, minY
+7, SOLID
, 0)
860 elseif (yaw
< 100) then
865 lcd
.drawNumber(minX
+ offset
+ xx
, minY
, yaw
, MIDSIZE
+INVERS
)
868 local function drawHud()
874 local function drawHomeDirection()
877 local angle
= math
.floor( yaw
- homeAngle
)
878 if ( math
.abs(angle
) > 45 and math
.abs(angle
) < 315) then
884 local x1
= ox
+ r1
* math
.cos(math
.rad(angle
- 90))
885 local y1
= oy
+ r1
* math
.sin(math
.rad(angle
- 90))
886 local x2
= ox
+ r2
* math
.cos(math
.rad(angle
- 90 + 120))
887 local y2
= oy
+ r2
* math
.sin(math
.rad(angle
- 90 + 120))
888 local x3
= ox
+ r2
* math
.cos(math
.rad(angle
- 90 - 120))
889 local y3
= oy
+ r2
* math
.sin(math
.rad(angle
- 90 - 120))
891 lcd
.drawLine(x1
,y1
,x2
,y2
,SOLID
,1)
892 lcd
.drawLine(x1
,y1
,x3
,y3
,SOLID
,1)
893 lcd
.drawLine(ox
,oy
,x2
,y2
,SOLID
,1)
894 lcd
.drawLine(ox
,oy
,x3
,y3
,SOLID
,1)
897 local function drawFlightTime()
898 lcd
.drawText(maxX
, 1, "T:", SMLSIZE
+INVERS
)
899 lcd
.drawTimer(lcd
.getLastRightPos(), 1, flightTime
, SMLSIZE
+INVERS
)
902 local function drawRSSI()
903 local rssi
= getRSSI()
904 lcd
.drawText(128, 1, rssi
, SMLSIZE
+INVERS
+RIGHT
)
905 lcd
.drawText(lcd
.getLastLeftPos(), 1, "Rssi:", SMLSIZE
+INVERS
+RIGHT
)
908 local function drawTxVoltage()
909 txVId
=getFieldInfo("tx-voltage").id
910 txV
= getValue(txVId
)*10
912 lcd
.drawText(128, 10, "v", SMLSIZE
+RIGHT
)
913 lcd
.drawNumber(lcd
.getLastLeftPos(), 10, txV
, SMLSIZE
+PREC1
+RIGHT
)
914 lcd
.drawText(lcd
.getLastLeftPos(), 10, "Tx:", SMLSIZE
+RIGHT
)
918 local lastStatusArmed
= 0
919 local lastGpsStatus
= 0
920 local lastFlightMode
= 0
921 local lastBattLevel
= 0
936 local function checkSoundEvents()
937 if (battCapacity
> 0) then
938 batLevel
= (1 - (battMah
/battCapacity
))*100
943 if batLevel
< (batLevels
[lastBattLevel
] + 1) and lastBattLevel
<= 9 then
944 playSound("bat"..batLevels
[lastBattLevel
])
945 lastBattLevel
= lastBattLevel
+ 1
948 if statusArmed
== 1 and lastStatusArmed
== 0 then
949 lastStatusArmed
= statusArmed
951 elseif statusArmed
== 0 and lastStatusArmed
== 1 then
952 lastStatusArmed
= statusArmed
953 playSound("disarmed")
956 if gpsStatus
> 2 and lastGpsStatus
<= 2 then
957 lastGpsStatus
= gpsStatus
959 elseif gpsStatus
<= 2 and lastGpsStatus
> 2 then
960 lastGpsStatus
= gpsStatus
961 playSound("gpsnofix")
964 if flightMode
~= lastFlightMode
then
965 lastFlightMode
= flightMode
966 if flightMode
== 1 then
967 playSound("stabilize")
968 elseif flightMode
== 3 then
970 elseif flightMode
== 4 then
972 elseif flightMode
== 6 then
974 elseif flightMode
== 7 then
976 elseif flightMode
== 10 then
978 elseif flightMode
== 16 then
979 playSound("autotune")
980 elseif flightMode
== 17 then
985 --------------------------------------------------------------------------------
987 --------------------------------------------------------------------------------
988 local showMessages
= false
989 local showConfig
= false
991 local function background()
997 local function symMode()
1005 local function run(event
)
1007 if event
== EVT_ENTER_BREAK
then
1008 showMessages
= not showMessages
1011 if (clock % 8 == 0) then
1018 if showMessages
then
1031 checkLandingStatus()
1045 local function init()
1046 pushMessage(6,"Yaapu X7 script v1.0")
1049 --------------------------------------------------------------------------------
1051 --------------------------------------------------------------------------------
1052 return {run
=run
, background
=background
, init
=init
}