-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMonitor.lua
646 lines (603 loc) · 17.2 KB
/
Monitor.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
--[[
This APP has the following tasks
1. measure the BEC voltage
give alarm if the voltage is lower the threshold for 0.6 seconds to filter spikes
it is still recommanded to have a direct BEC alarm with a littelbit lower threshold
2. measure the batterie voltage and current
compensate the voltage drop caused by the current
set alarm if the compensate voltage is lower than the threshold
(Ubatt / NumberCell + Ibatt * Ri < alUbat
3. announce the used cpacity 20%, 40%, 60%, 75%
4. monitor the rx signal quality and strength (always anounced as A1)
5. monitor the rpm depending on the flight mode
tollerate a rampup time and a hysterese of 15 rpm
during the rampup the rpm has to grow at least by 1 rpm per main loop (~25ms)
engine off : rpm = 0 t= rampup + 10s
autorotation: rpm = 0 t= rampup + 5s
FM : rmp = low, mid, high t= rampup
Abriviations
al: Alarm
ch: changed
cur: current
id: Index
init: Initialisation
li: List
p: Pointer
par: Parameter
rep: repeat
sl: selected
--]]
----------------------------------------------------------------------
-- Locals for the application
local appName ="Heli Monitor"
-- Selected Parameter
local rxBound
local alTimeSensor
local alSenText
local alUbec
local alUbat
local slAlPeriod
local slCellNum, slRi
local slIdUbat, slParUbat
local slIdIbat, slParIbat
local slAlFile
local slIdUbec, slParUbec
-- Filter Time in sec
local alTimeUbec, alSpikeUbec
local alTimeUbat, alSpikeUbatt
--capa
local slIdCapa, slParCapa
local slCapa
local capaTime
local alCntCapa
local capaLevel
-- RX monitoring
local alTimeRxs
local alTimeRxq
local pRxConf
local rxQRef
local rxRssiRef
local rssiConvert = {0, 2, 4, 7, 10, 13, 17, 21, 26, 32}
local rxQout
local rxRout
local rxDetected = {}
-- RPM monitor
local alTimeRpm
local slIdRpm, slParRpm
local slRpmTime
local swEngine, swAuto, swFmLow, swFmMid
local fmAlt
local fmTimeRef
local fmActive
local rpmRef
local rpmFm = {} -- engine off, auto rot, rmp low, rpm mid, rpm high
local senRpmHys = 20 -- engine rampup drop
-- alarm flags
local alarm = {}
--
----------------------------------------------------------------------
-- Draw the main form (Application inteface)
local function initForm()
-- Pointer to Sensor in List
local pLiUbat=0
local pLiIbat=0
local pLiUbec=0
local pLiCapa=0
local pLiRpm=0
-- Liste Sensor
local liSenLab = {"..."}
local liSenId = {"..."}
local liSenPar = {"..."}
-- Read available sensors to select from
local liSen = system.getSensors()
for i,sensor in ipairs(liSen) do
if (sensor.label ~= "") then
table.insert(liSenLab, string.format("%s", sensor.label))
table.insert(liSenId, string.format("%s", sensor.id))
table.insert(liSenPar, string.format("%s", sensor.param))
end
end
-- init sensor pionters
for i, p in ipairs(liSenPar) do
if (p==slParUbat and liSenId[i]==slIdUbat) then
pLiUbat=i
end
if (p==slParIbat and liSenId[i]==slIdIbat) then
pLiIbat=i
end
if (p==slParUbec and liSenId[i]==slIdUbec) then
pLiUbec=i
end
if (p==slParCapa and liSenId[i]==slIdCapa) then
pLiCapa=i
end
if (p==slParRpm and liSenId[i]==slIdRpm) then
pLiRpm=i
end
end
-- U-BEC Sensor
form.addRow(2)
form.addLabel({label="U-BEC sensor"})
form.addSelectbox(liSenLab,pLiUbec,true,function(value)
pLiUbec = value
slIdUbec = string.format("%s", liSenId[value])
slParUbec = string.format("%s", liSenPar[value])
if (slIdUbec == "...") then
slIdUbec = 0
slParUbec = 0
end
system.pSave("slIdUbec", slIdUbec)
system.pSave("slParUbec", slParUbec)
end)
-- Alarm Voltage U-BEC
form.addRow(2)
form.addLabel({label="Alarm U-BEC min"})
form.addIntbox(alUbec, 300, 840,770,2,1,function(value) alUbec=value; system.pSave("alUbec",value); end)
-- U-Battery Sensor
form.addRow(2)
form.addLabel({label="U-Battery sensor"})
form.addSelectbox(liSenLab,pLiUbat,true,function(value)
pLiUbat = value
slIdUbat = string.format("%s", liSenId[value])
slParUbat = string.format("%s", liSenPar[value])
if (slIdUbat == "...") then
slIdUbat = 0
slParUbat = 0
end
system.pSave("slIdUbat", slIdUbat)
system.pSave("slParUbat", slParUbat)
system.pSave("pLiUbat", pLiUbat)
end)
-- I-Battery Sensor
form.addRow(2)
form.addLabel({label="I-Battery sensor"})
form.addSelectbox(liSenLab,pLiIbat,true,function(value)
pLiIbat=value
slIdIbat = string.format("%s", liSenId[value])
slParIbat = string.format("%s", liSenPar[value])
if (slIdIbat == "...") then
slIdIbat = 0
slParIbat = 0
end
system.pSave("slIdIbat", slIdIbat)
system.pSave("slParIbat", slParIbat)
end)
--capa
form.addRow(2)
form.addLabel({label="Capacity sensor"})
form.addSelectbox(liSenLab,pLiCapa,true,function(value)
pLiCapa = value
slIdCapa = string.format("%s", liSenId[value])
slParCapa = string.format("%s", liSenPar[value])
if (slIdCapa == "...") then
slIdCapa = 0
slParCapa = 0
end
system.pSave("slIdCapa", slIdCapa)
system.pSave("slParCapa", slParCapa)
end)
form.addRow(2)
form.addLabel({label="Akku Capacity"})
form.addIntbox(slCapa, 10, 20000,5000,0,10,function(value) slCapa=value; system.pSave("slCapa",value); end)
-- Number of Cells
form.addRow(2)
form.addLabel({label="Number Cells"})
form.addIntbox(slCellNum,1,20,6,0,1,function(value) slCellNum=value; system.pSave("slCellNum",value); end)
-- Total Battery Ri
form.addRow(2)
form.addLabel({label="Battery Ri /cell"})
form.addIntbox(slRi,0,1000,25,1,1,function(value) slRi=value; system.pSave("slRi",value); end)
-- Alarm Voltage per Cell
form.addRow(2)
form.addLabel({label="Alarm Umin /cell"})
form.addIntbox(alUbat, 290, 400,320,2,1,function(value) alUbat=value; system.pSave("alUbat",value); end)
-- Audio alarm File
form.addRow(2)
form.addLabel({label="selAudio"})
form.addAudioFilebox(slAlFile,function(value) slAlFile=value; system.pSave("slAlFile",value); end)
-- number of secounds to repeat the alarn
form.addRow(2)
form.addLabel({label="Repeat Alarm"})
form.addIntbox(slAlPeriod, 4, 60,8,0,1,function(value) slAlPeriod=value; system.pSave("slAlPeriod",value); end)
-- FM switches
form.addRow(2)
form.addLabel({label="SW: Engine Off"})
form.addInputbox(swEngine,true, function(value) swEngine=value;system.pSave("swEngine",value); end )
form.addRow(2)
form.addLabel({label="SW: Autorotation"})
form.addInputbox(swAuto,true, function(value) swAuto=value;system.pSave("swAuto",value); end )
form.addRow(2)
form.addLabel({label="SW: FM low RPM"})
form.addInputbox(swFmLow,true, function(value) swFmLow=value;system.pSave("swFmLow",value); end )
form.addRow(2)
form.addLabel({label="SW: FM mid RPM"})
form.addInputbox(swFmMid,true, function(value) swFmMid=value;system.pSave("swFmMid",value); end )
-- Rpm Sensor
form.addRow(2)
form.addLabel({label="RPM sensor"})
form.addSelectbox(liSenLab,pLiRpm,true, function(value)
pLiRpm=value
slIdRpm = string.format("%s", liSenId[value])
slParRpm = string.format("%s", liSenPar[value])
if (slIdRpm == "...") then
slIdRpm = 0
slParRpm = 0
end
system.pSave("slIdRpm", slIdRpm)
system.pSave("slParRpm", slParRpm)
end)
--rpm rampup time
form.addRow(2)
form.addLabel({label="RPM time"})
form.addIntbox(slRpmTime,1,20,0,0,1,function(value) slRpmTime=value; system.pSave("slRpmTime",value); end)
-- rpm alarm level
form.addRow(2)
form.addLabel({label="RPM low"})
form.addIntbox(rpmFm[3],0,4000,1400,0,10, function(value) rpmFm[3]=value; system.pSave("rmpLow",value); end)
form.addRow(2)
form.addLabel({label="RPM mid"})
form.addIntbox(rpmFm[4],0,4000,1800,0,10, function(value) rpmFm[4]=value; system.pSave("rmpMid",value); end)
form.addRow(2)
form.addLabel({label="RPM high"})
form.addIntbox(rpmFm[5],0,4000,2300,0,10, function(value) rpmFm[5]=value; system.pSave("rmpHigh",value); end)
-- rx configuration
form.addRow(2)
form.addLabel({label="RX Quality"})
form.addIntbox(rxQRef,0,100,70,0,1, function(value) rxQRef=value; system.pSave("rxQRef",value); end)
local trssi = 9
for i,v in pairs(rssiConvert) do
if (rxRssiRef == v) then
trssi = i-1
break
end
end
form.addRow(2)
form.addLabel({label="RX Strength"})
form.addIntbox(trssi,0,9,4,0,1, function(value) rxRssiRef=rssiConvert[value+1]; system.pSave("rxRssiRef",rxRssiRef); end)
local liRxConf = {"RX1", "RX1, RX2", "RX1, RXB", "RX1, RX2, RXB"}
form.addRow(2)
form.addLabel({label="RX configuration"})
form.addSelectbox(liRxConf,pRxConf,true,function(value) pRxConf=value; system.pSave("pRxConf",value);
rxDetected[1] = true
rxDetected[2] = false
rxDetected[3] = false
rxBound = 0
if (value == 2 or value == 4) then
rxDetected[2] = true
end
if (value == 3 or value == 4) then
rxDetected[3] = true
end
end)
end
----------------------------------------------------------------------
-- Runtime functions
local function loop()
local curTime = system.getTimeCounter()
alarm.sensor = false
alSenText = ""
-- rx bound or timeout
local txTel = system.getTxTelemetry ()
for i= 1, 6 do
if (txTel.RSSI[i] > 0) then
rxBound = rxBound +1
rxTimeout = 200
end
end
if (rxTimeout > 0) then
rxTimeout = rxTimeout -1
else
rxBound = 0
end
if (rxBound > 200) then
-- U-Batt/cell + ri * I
local senUbat = system.getSensorByID(slIdUbat, slParUbat)
local senIbat = system.getSensorByID(slIdIbat, slParIbat)
if(senUbat and senIbat) then
if(senUbat.valid and senIbat.valid) then
if (senUbat.value / slCellNum + (senIbat.value * slRi) /10000 < alUbat/100) then
if (curTime - alSpikeUbatt > 260) then
alarm.ubat=true
end
else
alSpikeUbatt = curTime
end
else
alarm.sensor = true
alSenText = alSenText .. "U - I "
end
end
-- U-BEC or U-RX
local senUbec = system.getSensorByID(slIdUbec, slParUbec)
local calUbec = txTel.rx1Voltage
if(senUbec) then
if (senUbec.valid) then
if (senUbec.value < calUbec) then
calUbec = senUbec.value
end
else
alarm.sensor = true
alSenText = alSenText .. "Ubec "
end
end
if (calUbec < (alUbec/100)) then
if (curTime - alSpikeUbec > 600) then
alarm.ubec = true
end
else
alSpikeUbec = curTime
end
--Capa
local senCapa = system.getSensorByID(slIdCapa, slParCapa)
if(senCapa) then
if (senCapa.valid) then
if (senCapa.value > slCapa*0.75) then
if (capaLevel < 5) then
alCntCapa = 10
capaLevel = 5
end
elseif (senCapa.value > slCapa*0.7) then
if (capaLevel < 4) then
alCntCapa = 4
capaLevel = 4
end
elseif (senCapa.value > slCapa*0.6) then
if (capaLevel < 3) then
alCntCapa = 1
capaLevel = 3
end
elseif (senCapa.value > slCapa*0.4) then
if (capaLevel < 2) then
alCntCapa = 1
capaLevel = 2
end
elseif (senCapa.value > slCapa*0.2) then
if (capaLevel < 1) then
alCntCapa = 1
capaLevel = 1
end
elseif (senCapa.value < slCapa*0.1) then
if (capaLevel > 0) then
-- und Spannung + Ri * I > 4,1V Akku wirklich voll?
capaLevel = 0
alCntCapa = 0
end
end
else
alarm.sensor = true
alSenText = alSenText .. "Capacity "
end
end
-- RX signal quality
alarm.rx_q = true
alarm.rx_s = true
if (txTel.rx1Percent >= rxQRef) then
alarm.rx_q = false
else
rxQout = txTel.rx1Percent
end
if (txTel.RSSI[1] >= rxRssiRef or txTel.RSSI[2] >= rxRssiRef) then
alarm.rx_s = false
else
rxRout = math.max(txTel.RSSI[1], txTel.RSSI[2])
end
if (rxDetected[2]) then
if (txTel.rx2Percent >= rxQRef) then
alarm.rx_q = false
else
rxQout = math.max(rxQout, txTel.rx2Percent)
end
if (txTel.RSSI[3] >= rxRssiRef or txTel.RSSI[4] >= rxRssiRef) then
alarm.rx_s = false
else
rxRout = math.max(rxRout, txTel.RSSI[3], txTel.RSSI[4])
end
end
if (rxDetected[3]) then
if (txTel.rxBPercent >= rxQRef) then
alarm.rx_q = false
else
rxQout = math.max(rxQout, txTel.rxBPercent)
end
if (txTel.RSSI[5] >= rxRssiRef or txTel.RSSI[6] >= rxRssiRef) then
alarm.rx_s = false
else
rxRout = math.max(rxRout, txTel.RSSI[5], txTel.RSSI[6])
end
end
-- RPM
local enVal, autoVal, fmLow, fmMid = system.getInputsVal(swEngine, swAuto, swFmLow, swFmMid)
local fm = 5
if(enVal and enVal==1) then
fm=1
alarm.rpm = false
elseif (autoVal and autoVal==1) then
fm=2
alarm.rpm = false
elseif(fmLow and fmLow==1) then
fm=3
elseif (fmMid and fmMid==1) then
fm=4
end
local senrpm = system.getSensorByID(slIdRpm, slParRpm)
if (senrpm) then
if (senrpm.valid) then
local senRpmLow = math.max(senrpm.value - senRpmHys, 0)
if (fm ~= fmAlt) then
if (senRpmLow >= rpmFm[fm]) then
fmActive = true
rpmRef = rpmFm[fm]
else
fmActive = false
fmTimeRef = curTime
if (fmAlt == 1) then
fmTimeRef = fmTimeRef + 10000 -- additional 10s time to start after engine off
elseif (fmAlt == 2) then
fmTimeRef = fmTimeRef + 5000 -- additional 5s time to start after autorotation
end
end
end
if (not fmActive) then
if (senRpmLow >= rpmFm[fm] or fmTimeRef + slRpmTime*1000 < curTime) then
fmActive = true
rpmRef = rpmFm[fm]
else
if (senrpm.value > rpmRef) then
rpmRef = senrpm.value
else
rpmRef = rpmRef + 0.5
end
end
end
if (senrpm.value + senRpmHys < rpmRef) then
alarm.rpm = true
end
else
alarm.sensor = true
alSenText = alSenText .. "RPM"
end
end
fmAlt = fm
-- prioritize alarm output and do timining
if (alarm.rpm) then
alarm.rpm = false
if (curTime - alTimeRpm >2000) then
alTimeRpm = curTime
system.playNumber(senrpm.value, 0, "", "Revolution")
system.messageBox("low RPM",2)
end
elseif (alarm.rx_q) then
alarm.rx_q = false
if (curTime - alTimeRxq >2000) then
alTimeRxq = curTime
system.playNumber(rxQout, 0, "Q")
system.messageBox("RX quality",2)
end
elseif (alarm.rx_s) then
alarm.rx_s = false
if (curTime - alTimeRxs >2000) then
alTimeRxs = curTime
local trssi = 9
for i,v in pairs(rssiConvert) do
if (rxRout < v) then
trssi = i-2
break
end
end
system.playNumber(trssi, 0, "A1")
system.messageBox("RX strength",2)
end
elseif (alarm.ubat) then
alarm.ubat = false
if (alTimeUbat < curTime) then
alTimeUbat = curTime + slAlPeriod*1000
if (slAlFile) then
system.playFile(slAlFile,AUDIO_AUDIO_QUEUE)
end
system.playNumber ((senUbat.value / slCellNum), 2, "V")
system.messageBox("UBatt Alarm",2)
end
elseif (alarm.ubec) then
alarm.ubec = false
if (alTimeUbec < curTime) then
alTimeUbec = curTime + slAlPeriod*1000
system.playNumber (calUbec, 2, "V")
system.messageBox("U-BEC Alarm",2)
end
elseif (alCntCapa > 0) then
if (capaTime < curTime) then
capaTime = curTime + slAlPeriod*1000
alCntCapa = alCntCapa -1
system.playNumber (senCapa.value, 0, "mAh")
if (alCntCapa > 0) then
alCntCapa = alCntCapa -1
system.messageBox("Capa",2)
end
end
elseif (alarm.sensor) then
if (curTime - alTimeSensor > slAlPeriod*1000) then
alTimeSensor = curTime
system.playNumber(-1, 0,"","SensErr")
system.messageBox("SEN: " .. alSenText,2)
end
end
-- rx timeout
else
alCntCapa = 0
capaLevel = 0
alarm.ubat = false
alarm.ubec = false
alarm.rx_q = false
alarm.rx_s = false
alarm.rpm = false
end
--
end
----------------------------------------------------------------------
-- Application initialization
local function init()
system.registerForm(1,MENU_APPS,appName,initForm)
slIdUbec = system.pLoad("slIdUbec",0)
slParUbec = system.pLoad("slParUbec",0)
alUbec = system.pLoad("alUbec",780)
slIdUbat = system.pLoad("slIdUbat",0)
slParUbat = system.pLoad("slParUbat",0)
slIdIbat = system.pLoad("slIdIbat",0)
slParIbat = system.pLoad("slParIbat",0)
slIdRpm = system.pLoad("slIdRpm",0)
slParRpm = system.pLoad("slParRpm",0)
slRpmTime = system.pLoad("slRpmTime",6)
swEngine = system.pLoad("swEngine")
swAuto = system.pLoad("swAuto")
swFmLow = system.pLoad("swFmLow")
swFmMid = system.pLoad("swFmMid")
rpmFm[1] = 0
rpmFm[2] = 0
rpmFm[3] = system.pLoad("rmpLow",0)
rpmFm[4] = system.pLoad("rmpMid",0)
rpmFm[5] = system.pLoad("rmpHigh",0)
fmAlt = 0
slCellNum = system.pLoad("slCellNum",6)
slRi = system.pLoad("slRi",25)
alUbat = system.pLoad("alUbat",365)
slAlFile = system.pLoad("slAlFile",0)
slAlPeriod = system.pLoad("slAlPeriod",12)
slIdCapa = system.pLoad("slIdCapa",0)
slParCapa = system.pLoad("slParCapa",0)
slCapa = system.pLoad("slCapa",5000)
rxQRef = system.pLoad("rxQRef",60)
rxRssiRef = system.pLoad("rxRssiRef",7)
pRxConf = system.pLoad("pRxConf",1)
rxDetected[1] = true
rxDetected[2] = false
rxDetected[3] = false
if (pRxConf == 2 or pRxConf == 4) then
rxDetected[2] = true
end
if (pRxConf == 3 or pRxConf == 4) then
rxDetected[3] = true
end
rxBound = 0
rxTimeout = 200
alTimeUbat = system.getTimeCounter()
alSpikeUbatt = alTimeUbat
alTimeUbec = alTimeUbat
alSpikeUbec = alTimeUbat
capaTime = alTimeUbat
fmTimeRef = alTimeUbat
alTimeRxs = alTimeUbat
alTimeRxq = alTimeUbat
alTimeRpm = alTimeUbat
alTimeSensor = alTimeUbat
alCntCapa = 0
capaLevel = 0
alarm.ubat = false
alarm.ubec = false
alarm.rx_q = false
alarm.rx_s = false
alarm.rpm = false
fmActive = false
rpmRef = 0
end
----------------------------------------------------------------------
return {init=init, loop=loop, author="Andre", version="2.17", name=appName}