Traxxas TQ 1st gen: try 5
[DIY-Multiprotocol-TX-Module.git] / Lua_scripts / DSM_SmartRX_Tel.lua
blob716f308c5294dacbeb85375e34f3ae904b6ec34d
1 local toolName = "TNS|DSM Smart RX Telemetry|TNE"
2 ---- ######################################################################### #
3 ---- # License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html #
4 ---- # #
5 ---- # This program is free software; you can redistribute it and/or modify #
6 ---- # it under the terms of the GNU General Public License version 2 as #
7 ---- # published by the Free Software Foundation. #
8 ---- # #
9 ---- # This program is distributed in the hope that it will be useful #
10 ---- # but WITHOUT ANY WARRANTY; without even the implied warranty of #
11 ---- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
12 ---- # GNU General Public License for more details. #
13 ---- # #
14 ---- #########################################################################
16 ------------------------------------------------------------------------------
17 -- Developer: Francisco Arzu
20 local DEBUG_ON = false
23 local TEXT_SIZE = 0 -- NORMAL
24 local X_COL1_HEADER = 6
25 local X_COL1_DATA = 60
26 local X_COL2_HEADER = 170
27 local X_COL2_DATA = 220
28 local Y_LINE_HEIGHT = 20
29 local Y_HEADER = 0
30 local Y_DATA = Y_HEADER + Y_LINE_HEIGHT*2
31 local X_DATA_LEN = 80
32 local X_DATA_SPACE = 5
37 local function getPage(iParam)
38 -- get page from 0-based index
39 -- {0,1,2,3}: cyclic (1), {4,5,6,7}: tail (2)
40 local res = (math.floor(iParam/4)==0) and 0 or 1
41 return res
42 end
44 local function round(v)
45 -- round float
46 local factor = 100
47 return math.floor(v * factor + 0.5) / factor
48 end
51 local function readValue(sensor)
52 -- read from sensor, round and return
53 local v = getValue(sensor)
54 --v = round(v)
55 return v
56 end
58 local function readValueById(sensor)
59 local i = getFieldInfo(sensor)
60 if (i==nil) then return nil end
62 local v = getValue(i.id)
63 return v
64 end
68 local function readBatValue(sensor)
69 -- read from sensor, round and return
70 local v = getValue(sensor)
71 if (v==nil) then return v end
73 return string.format("%2.2f",v)
74 end
76 local function readActiveParamValue(sensor)
77 -- read and return a validated active parameter value
78 local v = getValue(sensor)
79 if (v<1 or v>8) then
80 return -1
81 end
82 return v
83 end
87 local function drawFlightLogScreen(event)
88 -- draw labels and params on screen
89 local h = getValue("Hold")
90 local activeParam = h-1 -- H
92 lcd.clear()
93 lcd.drawText (X_COL1_HEADER, Y_HEADER, "Flight Log", TEXT_SIZE + INVERS)
95 -- read and return parameters
96 local a = getValue("FdeA")
97 local b = getValue("FdeB")
98 local l = getValue("FdeL")
99 local r = getValue("FdeR")
100 local f = getValue("FLss")
102 local titles = {[0]="A:", "B:", "L:", "R:", "F:", "H:"}
103 local values = {[0]=a,b,l,r,f,h}
105 local y = Y_LINE_HEIGHT+Y_DATA
107 for iParam=0,3 do -- A,B,L,R
108 -- highlight selected parameter (rund)
109 local attr = ((activeParam%4)==iParam) and INVERS or 0
110 -- labels
111 local x = X_COL1_HEADER
112 local val = titles[iParam]
113 lcd.drawText (x, y, val, TEXT_SIZE)
115 -- Values
116 val = values[iParam]
117 x = X_COL1_DATA + X_DATA_LEN
118 if (val~=16384) then -- Active value
119 lcd.drawText (x, y, val, attr + TEXT_SIZE + RIGHT)
122 y = y + Y_LINE_HEIGHT
125 y = Y_LINE_HEIGHT+Y_DATA
126 for iParam=4,5 do -- F, H
127 -- labels
128 local x = X_COL2_HEADER
129 local val = titles[iParam]
130 lcd.drawText (x, y, val, TEXT_SIZE + BOLD)
132 -- Values
133 val = values[iParam]
134 x = X_COL2_DATA + X_DATA_LEN
135 lcd.drawText (x, y, val, TEXT_SIZE + RIGHT + BOLD)
137 y = y + Y_LINE_HEIGHT
140 -- Bat
141 y = y + Y_LINE_HEIGHT
142 local bat = readBatValue("A2") or "--"
143 lcd.drawText (X_COL2_HEADER, y, "Bat:", TEXT_SIZE)
144 lcd.drawText (X_COL2_DATA + X_DATA_LEN, y, bat, TEXT_SIZE + RIGHT)
145 lcd.drawText (X_COL2_DATA + X_DATA_LEN + X_DATA_SPACE, y, "v", TEXT_SIZE)
155 local function Unsigned_to_SInt16(value)
156 if value >= 0x8000 then -- Negative value??
157 return value - 0x10000
159 return value
162 local function getDegreesValue(sensor)
163 local i = getFieldInfo(sensor)
164 if (i==nil) then return "-unk-" end
166 local v = getValue(i.id)
167 if v==nil then return "---" end
168 local vs = Unsigned_to_SInt16(v)
170 return string.format("%0.1f o",vs/10)
174 local function getDecHexValue(sensor)
175 local i = getFieldInfo(sensor)
176 if (i==nil) then return "-unk-" end
178 local v = getValue(i.id)
179 if v==nil then return "---" end
180 local vs = Unsigned_to_SInt16(v)
182 return string.format("%d (0x%04X)",vs,v)
185 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}
186 local function drawAS3XSettings(event, page)
187 local s0500 = getDecHexValue("0500")
188 local s0502 = getDecHexValue("0502")
189 local s0504 = getDecHexValue("0504")
190 local s0506 = getDecHexValue("0506")
191 local s0508 = getDecHexValue("0508")
192 local s050B = getDecHexValue("050B")
193 local s050D = getDecHexValue("050D")
195 local d0500 = readValueById("0500") or 0
196 local flags = bit32.rshift(d0500,8)
197 local state = bit32.band(d0500,0xFF)
199 local flagsMsg=""
200 -- flags bits: Safe Envelop, ?, Angle Demand, Stab
201 if (bit32.band(flags,0x1)~=0) then flagsMsg=flagsMsg.."AS3X Stab" end
202 -- This one, only one should show
203 if (bit32.band(flags,0x2)~=0) then flagsMsg=flagsMsg..", Angle Demand"
204 elseif (bit32.band(flags,0x8)~=0) then flagsMsg=flagsMsg..", Safe Envelope"
205 elseif (bit32.band(flags,0x4)~=0) then flagsMsg=flagsMsg..", AS3X Heading" end
207 local d0502 = readValueById("0502") or 0 -- 0x?F?S
208 local fm = bit32.band(bit32.rshift(d0502,8),0xF) -- 0,1,2
210 local axis = bit32.band(d0502,0xF) -- 0=Gains,1=Headings,2=Angle Limits (cointinus iterating to provide all values)
212 local d0504 = readValueById("0504") or 0
213 local d0506 = readValueById("0506") or 0
214 local d0508 = readValueById("0508") or 0
216 local d0 = bit32.rshift(d0504,8)
217 local d1 = bit32.band(d0504,0xFF)
218 local d2 = bit32.rshift(d0506,8)
219 local d3 = bit32.band(d0506,0xFF)
220 local d4 = bit32.rshift(d0508,8)
221 local d5 = bit32.band(d0508,0xFF)
223 --axis: 0=Gains+Headings (RG,PG,YG,RH,PH,YH), 1=Safe Gains (R,P,Y),2=Angle Limits(L,R,U,D)
224 --Constantly changing from 0..2 to represent different data, thats why we have to store the values
225 --in a script/global variable, and not local to the function
226 local s = axis*6
227 as3xData[s+0] = d0
228 as3xData[s+1] = d1
229 as3xData[s+2] = d2
230 as3xData[s+3] = d3
231 as3xData[s+4] = d4
232 as3xData[s+5] = d5
235 lcd.clear()
236 lcd.drawText (0,0, "AS3X/SAFE Settings", TEXT_SIZE + INVERS)
238 local y = Y_DATA
239 -- Flight Mode
240 lcd.drawText (X_COL1_HEADER,y, "FM: "..(fm+1), TEXT_SIZE)
241 lcd.drawText (X_COL1_DATA+X_DATA_LEN*0.3,y, "Flags: "..flags, TEXT_SIZE)
242 lcd.drawText (X_COL2_HEADER+X_DATA_LEN*0.3,y, "State: "..state, TEXT_SIZE)
244 y = y + Y_LINE_HEIGHT
245 lcd.drawText (X_COL1_HEADER,y, flagsMsg, TEXT_SIZE)
247 y = y + Y_LINE_HEIGHT
249 if (page==1) then
250 lcd.drawText (X_COL1_HEADER+X_DATA_LEN*0.3,y, "AS3X Gains", TEXT_SIZE+BOLD)
251 lcd.drawText (X_COL2_HEADER+X_DATA_LEN*0.3,y, "AS3X Headings", TEXT_SIZE+BOLD)
253 y = y + Y_LINE_HEIGHT
254 lcd.drawText (X_COL1_HEADER,y, "Roll:", TEXT_SIZE)
255 lcd.drawText (X_COL1_DATA+X_DATA_LEN,y, as3xData[0], TEXT_SIZE + RIGHT) -- Roll G
256 lcd.drawText (X_COL2_DATA+X_DATA_LEN,y, as3xData[3], TEXT_SIZE + RIGHT) -- Roll H
258 y = y + Y_LINE_HEIGHT
259 lcd.drawText (X_COL1_HEADER,y, "Pitch:", TEXT_SIZE)
260 lcd.drawText (X_COL1_DATA+X_DATA_LEN,y,as3xData[1], TEXT_SIZE + RIGHT) -- Pitch G
261 lcd.drawText (X_COL2_DATA+X_DATA_LEN,y, as3xData[4], TEXT_SIZE + RIGHT) -- Pitch H
263 y = y + Y_LINE_HEIGHT
264 lcd.drawText (X_COL1_HEADER,y, "Yaw:", TEXT_SIZE)
265 lcd.drawText (X_COL1_DATA+X_DATA_LEN,y, as3xData[2], TEXT_SIZE + RIGHT) -- Yaw G
266 lcd.drawText (X_COL2_DATA+X_DATA_LEN,y, as3xData[5], TEXT_SIZE + RIGHT) -- Yaw H
270 if (page==2) then
271 local x_data1 = X_COL1_DATA+X_DATA_LEN
272 local x_data2 = X_COL2_HEADER+X_DATA_LEN*1.6
274 lcd.drawText (X_COL1_HEADER+X_DATA_LEN*0.3,y, "SAFE Gains", TEXT_SIZE+BOLD)
275 lcd.drawText (X_COL2_HEADER+X_DATA_LEN*0.1,y, "Angle Limits", TEXT_SIZE+BOLD)
277 y = y + Y_LINE_HEIGHT
278 lcd.drawText (X_COL1_HEADER,y, "Roll:", TEXT_SIZE)
279 lcd.drawText (x_data1,y, as3xData[6], TEXT_SIZE + RIGHT)
281 lcd.drawText (X_COL2_HEADER,y, "Roll R:", TEXT_SIZE)
282 lcd.drawText (x_data2,y, as3xData[12], TEXT_SIZE + RIGHT)
285 y = y + Y_LINE_HEIGHT
286 lcd.drawText (X_COL1_HEADER,y, "Pitch:", TEXT_SIZE)
287 lcd.drawText (x_data1,y,as3xData[7], TEXT_SIZE + RIGHT)
289 lcd.drawText (X_COL2_HEADER,y, "Roll L:", TEXT_SIZE)
290 lcd.drawText (x_data2,y,as3xData[13], TEXT_SIZE + RIGHT)
293 y = y + Y_LINE_HEIGHT
294 lcd.drawText (X_COL1_HEADER,y, "Yaw:", TEXT_SIZE)
295 lcd.drawText (x_data1,y, as3xData[8], TEXT_SIZE + RIGHT)
297 lcd.drawText (X_COL2_HEADER,y, "Pitch U:", TEXT_SIZE)
298 lcd.drawText (x_data2,y, as3xData[14], TEXT_SIZE + RIGHT)
300 y = y + Y_LINE_HEIGHT
301 lcd.drawText (X_COL2_HEADER,y, "Pitch D:", TEXT_SIZE)
302 lcd.drawText (x_data2,y, as3xData[15], TEXT_SIZE + RIGHT)
305 -- Debug Values
306 if (DEBUG_ON) then
307 local titles = {[0]=
308 "0500","0502","0504","0506","0508","050B","050D"}
310 local values = {[0]=
311 s0500,s0502,s0504,s0506,s0508,s050B,s050D }
313 y = Y_LINE_HEIGHT*2 + Y_HEADER
314 for iParam=0,#titles do -- ??
315 -- labels
316 local x = X_COL1_HEADER+250
317 local val = titles[iParam]
318 lcd.drawText (x, y, val, TEXT_SIZE)
320 val = values[iParam] or "--"
321 x = X_COL1_DATA+250
322 lcd.drawText (x, y, val, TEXT_SIZE)
324 y = y + Y_LINE_HEIGHT
326 end
329 local function drawAS3XSettingsP1(event)
330 drawAS3XSettings(event, 1)
333 local function drawAS3XSettingsP2(event)
334 drawAS3XSettings(event, 2)
338 local function doFloat(v)
339 if v==nil then return 0.0 end
341 local vs = string.format("%1.2f",v)
343 return vs + 0.0
347 local ESC_Title={[0]="","RPM:","Volts:","Motor:","Mot Out:","Throttle:","FET Temp:", "BEC V:", "BEC T:", "BEC A:"}
348 local ESC_uom={[0]="","","V","A","%","%","C", "V","C","A"}
349 local ESC_Status={[0]=0,0,0,0,0,0,0,0,0,0,0}
350 local ESC_Min={[0]=0,0,0,0,0,0,0,0,0,0,0}
351 local ESC_Max={[0]=0,0,0,0,0,0,0,0,0,0,0}
353 local function drawESCStatus(event)
354 lcd.clear()
355 ESC_Status[1] = getValue("Erpm") -- RPM
356 ESC_Status[2] = doFloat(getValue("EVIN")) -- Volts
357 ESC_Status[3] = doFloat(getValue("ECUR")) -- Current
358 ESC_Status[4] = doFloat(getValue("EOUT")) -- % Output
359 ESC_Status[5] = doFloat(getValue("ETHR")) -- Throttle % (EOUT)
360 ESC_Status[6] = getValue("TFET") -- Temp FET
362 ESC_Status[7] = doFloat(getValue("VBEC")) -- Volts BEC
363 ESC_Status[8] = getValue("TBEC") -- Temp BEC
364 ESC_Status[9] = doFloat(getValue("CBEC")) -- Current BEC
366 for i=1,9 do
367 if (ESC_Status~=nil) then
368 if (ESC_Min[i]==0) then
369 ESC_Min[i]=ESC_Status[i]
370 else
371 ESC_Min[i] = math.min(ESC_Min[i],ESC_Status[i])
374 ESC_Max[i] = math.max(ESC_Max[i],ESC_Status[i])
378 lcd.drawText (0,0, "ESC", TEXT_SIZE+INVERS)
380 local y = 0
381 local x_data = X_COL1_DATA+X_DATA_LEN*1.5
382 local x_data2 = X_COL2_DATA+X_DATA_LEN*0.5
383 local x_data3 = x_data2 + X_DATA_LEN*0.8
386 lcd.drawText (x_data,y , "Status", TEXT_SIZE+BOLD+RIGHT)
387 lcd.drawText (x_data2,y, "Min", TEXT_SIZE+BOLD+RIGHT)
388 lcd.drawText (x_data3,y, "Max", TEXT_SIZE+BOLD+RIGHT)
390 y = Y_DATA
391 for i=1,9 do
392 lcd.drawText (X_COL1_HEADER,y, ESC_Title[i], TEXT_SIZE + BOLD)
394 lcd.drawText (x_data,y, ESC_Status[i] or "--", TEXT_SIZE + RIGHT)
395 lcd.drawText (x_data + X_DATA_SPACE,y, ESC_uom[i], TEXT_SIZE)
397 lcd.drawText (x_data2,y, ESC_Min[i] or "--", TEXT_SIZE + RIGHT)
398 lcd.drawText (x_data3,y, ESC_Max[i] or "--", TEXT_SIZE + RIGHT)
399 y = y + Y_LINE_HEIGHT
404 local function drawBATStatus(event)
405 local Title={[0]="","Bat:","Temp:","Rem :","Curr:","Used:","Imbal:","Cycles:", "RX:", "BCpT?:"}
406 local uom={[0]="","V","C","%","mAh","mAh","mV","", "V",""}
407 local Values={[0]=0,0,0,0,0,0,0,0,0,0,0}
408 local CellValues={[0]=0,0,0,0,0,0,0,0,0,0,0}
410 lcd.clear()
412 local ESC_Volts = getValue("EVIN") or 0 -- Volts
413 local ESC_Current = getValue("ECUR") or 0 -- Current
415 Values[1] = 0 -- compute later
416 Values[2] = getValue("BTmp") -- Current (C)
417 Values[3] = nil -- Remaining???
418 Values[4] = getValue("BCur") -- Current (mAh)
419 Values[5] = getValue("BUse") -- Current Used (mAh)
420 Values[6] = getValue("CLMa") -- 0.0 (mV) Imbalance
421 Values[7] = getValue("Cycl") -- Cycles
422 Values[8] = readBatValue("A2") -- v
423 Values[9] = getValue("BCpT") -- Current (mAh) ????
426 --- Total Voltange Calculation
427 local VTotal=0
428 for i=1,10 do
429 CellValues[i] = getValue("Cel"..i)
430 VTotal = VTotal + CellValues[i]
433 if (VTotal==0) then -- No Inteligent Battery,use intelligent ESC if any
434 VTotal = ESC_Volts
435 Values[4] = string.format("%d",ESC_Current * 1000)
438 Values[1] = string.format("%2.2f",VTotal)
440 --- SCREEN
442 lcd.drawText (X_COL1_HEADER,0, "Battery Stats", TEXT_SIZE+INVERS)
446 local y = Y_DATA
447 local x_data = X_COL1_DATA+X_DATA_LEN+X_DATA_SPACE*3
448 for i=2,9 do
449 lcd.drawText (X_COL1_HEADER, y, Title[i], TEXT_SIZE + BOLD)
450 lcd.drawText (x_data, y, Values[i] or "--", TEXT_SIZE + RIGHT)
451 lcd.drawText (x_data+X_DATA_SPACE, y, uom[i], TEXT_SIZE)
452 y = y + Y_LINE_HEIGHT
455 y = Y_DATA
456 x_data = X_COL2_DATA+X_DATA_LEN+X_DATA_SPACE*5
457 for i=1,8 do
458 if ((CellValues[i] or 0) > 0) then
459 lcd.drawText (X_COL2_HEADER+X_DATA_LEN/2,y, "Cel "..i..":", TEXT_SIZE + BOLD)
460 lcd.drawText (x_data,y, string.format("%2.2f",CellValues[i] or 0), TEXT_SIZE + RIGHT)
461 lcd.drawText (x_data+X_DATA_SPACE,y, "v", TEXT_SIZE)
463 y = y + Y_LINE_HEIGHT
466 lcd.drawText (X_COL2_HEADER+X_DATA_LEN/2,0, Title[1], TEXT_SIZE + INVERS + BOLD)
467 lcd.drawText (x_data,0, string.format("%2.2f",Values[1] or 0), TEXT_SIZE + INVERS+ RIGHT)
468 lcd.drawText (x_data+X_DATA_SPACE, 0, uom[1], TEXT_SIZE + INVERS)
474 local function openTelemetryRaw(i2cId)
475 --Init telemetry (Spectrun Telemetry Raw STR)
476 multiBuffer( 0, string.byte('S') )
477 multiBuffer( 1, string.byte('T') )
478 multiBuffer( 2, string.byte('R') )
479 multiBuffer( 3, i2cId ) -- Monitor this teemetry data
480 multiBuffer( 4, 0 ) -- Allow to get Data
483 local function closeTelemetryRaw()
484 multiBuffer(0, 0) -- Destroy the STR header
485 multiBuffer(3, 0) -- Not requesting any Telementry ID
488 local lineText = {nil}
489 local I2C_TEXT_GEN = 0x0C
490 local function drawTextGen(event)
491 if (multiBuffer(0)~=string.byte('S')) then -- First time run???
492 openTelemetryRaw(I2C_TEXT_GEN) -- I2C_ID for TEXT_GEN
493 lineText = {nil}
496 -- Proces TEXT GEN Telementry message
497 if multiBuffer( 4 ) == I2C_TEXT_GEN then -- Specktrum Telemetry ID of data received
498 local instanceNo = multiBuffer( 5 )
499 local lineNo = multiBuffer( 6 )
501 local line = ""
502 for i=0,13 do
503 line = line .. string.char(multiBuffer( 7 + i ))
506 multiBuffer( 4, 0 ) -- Clear Semaphore, to notify that we fully process the current message
507 lineText[lineNo]=line
510 lcd.clear()
511 -- Header
512 if (lineText[0]) then
513 lcd.drawText (X_COL1_HEADER,0, " "..lineText[0].." ", TEXT_SIZE + BOLD + INVERS)
514 else
515 lcd.drawText (X_COL1_HEADER,0, "TextGen", TEXT_SIZE+INVERS)
518 -- Menu lines
519 local y = Y_DATA
520 for i=1,8 do
521 if (lineText[i]) then
522 lcd.drawText (X_COL1_HEADER,y, lineText[i], TEXT_SIZE)
524 y = y + Y_LINE_HEIGHT
527 if event == EVT_VIRTUAL_EXIT then -- Exit?? Clear menu data
528 closeTelemetryRaw()
533 local telPage = 1
534 local telPageSelected = 0
535 local pageTitle = {[0]="Main", "AS3X Settings", "SAFE Settings", "ESC Status", "Battery Status","TextGen","Flight Log"}
537 local function drawMainScreen(event)
538 lcd.clear()
539 lcd.drawText (X_COL1_HEADER, Y_HEADER, "Main Telemetry (Smart RXs)", TEXT_SIZE + INVERS)
541 for iParam=1,#pageTitle do
542 -- highlight selected parameter
543 local attr = (telPage==iParam) and INVERS or 0
545 -- set y draw coord
546 local y = (iParam)*Y_LINE_HEIGHT+Y_DATA
548 -- labels
549 local x = X_COL1_HEADER
550 local val = pageTitle[iParam]
551 lcd.drawText (x, y, val, attr + TEXT_SIZE)
554 if event == EVT_VIRTUAL_PREV then
555 if (telPage>1) then telPage = telPage - 1 end
556 elseif event == EVT_VIRTUAL_NEXT then
557 if (telPage<#pageTitle) then telPage = telPage + 1 end
558 elseif event == EVT_VIRTUAL_ENTER then
559 telPageSelected = telPage
564 local pageDraw = {[0]=drawMainScreen, drawAS3XSettingsP1, drawAS3XSettingsP2, drawESCStatus, drawBATStatus, drawTextGen, drawFlightLogScreen}
566 local function run_func(event)
567 if event == nil then
568 error("Cannot be run as a model script!")
569 return 2
572 -- draw specific page
573 pageDraw[telPageSelected](event)
575 if event == EVT_VIRTUAL_EXIT then
576 if (telPageSelected==0) then return 1 end -- on Main?? Exit Script
577 telPageSelected = 0 -- any page, return to Main
580 return 0
583 local function init_func()
585 if (LCD_W <= 128 or LCD_H <=64) then -- Smaller Screens
586 TEXT_SIZE = SMLSIZE
587 X_COL1_HEADER = 0
588 X_COL1_DATA = 20
590 X_COL2_HEADER = 60
591 X_COL2_DATA = 90
593 X_DATA_LEN = 28
594 X_DATA_SPACE = 1
597 Y_LINE_HEIGHT = 8
598 Y_DATA = Y_HEADER + Y_LINE_HEIGHT
603 return { run=run_func, init=init_func }