From e68662764fcaecd9d1578297e470d72eaebc5dc1 Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 22 May 2020 19:55:04 +0200 Subject: [PATCH] Battcheck fixes (#7602) Battcheck reviewed --- CMakeLists.txt | 2 +- radio/sdcard/horus/WIDGETS/BattCheck/main.lua | 232 +++++++++++------- 2 files changed, 148 insertions(+), 86 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f0011a7aa..e9bca79b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ set(VERSION_REVISION "8") set(VERSION_SUFFIX $ENV{OPENTX_VERSION_SUFFIX}) set(VERSION_FAMILY ${VERSION_MAJOR}.${VERSION_MINOR}) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}${VERSION_SUFFIX}) -set(SDCARD_REVISION "0027") +set(SDCARD_REVISION "0028") set(SDCARD_VERSION ${VERSION_MAJOR}.${VERSION_MINOR}V${SDCARD_REVISION}) cmake_minimum_required(VERSION 2.8) diff --git a/radio/sdcard/horus/WIDGETS/BattCheck/main.lua b/radio/sdcard/horus/WIDGETS/BattCheck/main.lua index 215d589ac..58790445a 100755 --- a/radio/sdcard/horus/WIDGETS/BattCheck/main.lua +++ b/radio/sdcard/horus/WIDGETS/BattCheck/main.lua @@ -19,15 +19,50 @@ -- Horus Widget to display the levels of lipo battery with per cell indication -- 3djc & Offer Shmuely -- Date: 2020 --- ver: 0.4 +-- ver: 0.5 +local version = "v0.5" local _options = { - { "Sensor", SOURCE, 0 }, -- default to 'Cels' - { "Color", COLOR, WHITE }, - { "Shadow", BOOL, 0 }, - { "LowestCell", BOOL, 1 } -- 0=main voltage display shows all-cell-voltage, 1=main voltage display shows lowest-cell + { "Sensor", SOURCE, 0 }, -- default to 'Cels' + { "Color", COLOR, WHITE }, + { "Shadow", BOOL, 0 }, + { "LowestCell", BOOL, 1 } -- 0=main voltage display shows all-cell-voltage, 1=main voltage display shows lowest-cell } +-- Data gathered from commercial lipo sensors + +local _lipoPercentListSplit = { + { { 3, 0 }, { 3.093, 1 }, { 3.196, 2 }, { 3.301, 3 }, { 3.401, 4 }, { 3.477, 5 }, { 3.544, 6 }, { 3.601, 7 }, { 3.637, 8 }, { 3.664, 9 }, { 3.679, 10 }, { 3.683, 11 }, { 3.689, 12 }, { 3.692, 13 } }, + { { 3.705, 14 }, { 3.71, 15 }, { 3.713, 16 }, { 3.715, 17 }, { 3.72, 18 }, { 3.731, 19 }, { 3.735, 20 }, { 3.744, 21 }, { 3.753, 22 }, { 3.756, 23 }, { 3.758, 24 }, { 3.762, 25 }, { 3.767, 26 } }, + { { 3.774, 27 }, { 3.78, 28 }, { 3.783, 29 }, { 3.786, 30 }, { 3.789, 31 }, { 3.794, 32 }, { 3.797, 33 }, { 3.8, 34 }, { 3.802, 35 }, { 3.805, 36 }, { 3.808, 37 }, { 3.811, 38 }, { 3.815, 39 } }, + { { 3.818, 40 }, { 3.822, 41 }, { 3.825, 42 }, { 3.829, 43 }, { 3.833, 44 }, { 3.836, 45 }, { 3.84, 46 }, { 3.843, 47 }, { 3.847, 48 }, { 3.85, 49 }, { 3.854, 50 }, { 3.857, 51 }, { 3.86, 52 } }, + { { 3.863, 53 }, { 3.866, 54 }, { 3.87, 55 }, { 3.874, 56 }, { 3.879, 57 }, { 3.888, 58 }, { 3.893, 59 }, { 3.897, 60 }, { 3.902, 61 }, { 3.906, 62 }, { 3.911, 63 }, { 3.918, 64 } }, + { { 3.923, 65 }, { 3.928, 66 }, { 3.939, 67 }, { 3.943, 68 }, { 3.949, 69 }, { 3.955, 70 }, { 3.961, 71 }, { 3.968, 72 }, { 3.974, 73 }, { 3.981, 74 }, { 3.987, 75 }, { 3.994, 76 } }, + { { 4.001, 77 }, { 4.007, 78 }, { 4.014, 79 }, { 4.021, 80 }, { 4.029, 81 }, { 4.036, 82 }, { 4.044, 83 }, { 4.052, 84 }, { 4.062, 85 }, { 4.074, 86 }, { 4.085, 87 }, { 4.095, 88 } }, + { { 4.105, 89 }, { 4.111, 90 }, { 4.116, 91 }, { 4.12, 92 }, { 4.125, 93 }, { 4.129, 94 }, { 4.135, 95 }, { 4.145, 96 }, { 4.176, 97 }, { 4.179, 98 }, { 4.193, 99 }, { 4.2, 100 } }, +} + +local function periodicInit(t, durationMili) + t.startTime = getTime(); + t.durationMili = durationMili; +end +local function periodicReset(t) + t.startTime = getTime(); +end +local function periodicHasPassed(t) + local elapsed = getTime() - t.startTime; + local elapsedMili = elapsed * 10; + if (elapsedMili < t.durationMili) then + return false; + end + return true; +end +local function periodicGetElapsedTime(t) + local elapsed = getTime() - t.startTime; + local elapsedMili = elapsed * 10; + return elapsedMili; +end + -- This function is run once at the creation of the widget local function create(zone, options) local wgt = { @@ -40,8 +75,8 @@ local function create(zone, options) telemResetLowestMinRSSI = 101, no_telem_blink = 0, isDataAvailable = 0, - cellDataLive = {0,0,0,0,0,0}, - cellDataHistoryLowest = {5,5,5,5,5,5}, + cellDataLive = { 0, 0, 0, 0, 0, 0 }, + cellDataHistoryLowest = { 5, 5, 5, 5, 5, 5 }, cellDataHistoryCellLowest = 5, cellMax = 0, cellMin = 0, @@ -50,7 +85,10 @@ local function create(zone, options) cellCount = 0, cellSum = 0, mainValue = 0, - secondaryValue = 0 + secondaryValue = 0, + periodic1 = { startTime = getTime(), durationMili = 1000 }, + periodicProfiler = { startTime = getTime(), durationMili = 5000 }, + profTimes = {}, } -- use default if user did not set, So widget is operational on "select widget" @@ -65,7 +103,9 @@ end -- This function allow updates when you change widgets settings local function update(wgt, options) - if (wgt == nil) then return end + if (wgt == nil) then + return + end wgt.options = options @@ -83,8 +123,8 @@ end local function onTelemetryResetEvent(wgt) wgt.telemResetCount = wgt.telemResetCount + 1 - wgt.cellDataLive = {0,0,0,0,0,0} - wgt.cellDataHistoryLowest = {5,5,5,5,5,5} + wgt.cellDataLive = { 0, 0, 0, 0, 0, 0 } + wgt.cellDataHistoryLowest = { 5, 5, 5, 5, 5, 5 } wgt.cellDataHistoryCellLowest = 5 end @@ -99,9 +139,12 @@ end local function detectResetEvent(wgt) local currMinRSSI = getValue('RSSI-') - if (currMinRSSI == nil) then return + if (currMinRSSI == nil) then + return + end + if (currMinRSSI == wgt.telemResetLowestMinRSSI) then + return end - if (currMinRSSI == wgt.telemResetLowestMinRSSI) then return end if (currMinRSSI < wgt.telemResetLowestMinRSSI) then -- rssi just got lower, record it @@ -118,28 +161,28 @@ local function detectResetEvent(wgt) end - --- This function return the percentage remaining in a single Lipo cel -local function getCellPercent(cellValue) +--- since running on long array found to be very intensive to hrous cpu, we are splitting the list to small lists +local function getCellPercent(wgt, cellValue) if cellValue == nil then return 0 end + local result = 0; - -- in case somehow voltage is higher, don't return nil - if (cellValue > 4.2) then - return 100 - end - - -- Data gathered from commercial lipo sensors - local myArrayPercentList = { {3,0},{3.093,1},{3.196,2},{3.301,3},{3.401,4},{3.477,5},{3.544,6},{3.601,7},{3.637,8},{3.664,9},{3.679,10},{3.683,11},{3.689,12},{3.692,13},{3.705,14},{3.71,15},{3.713,16},{3.715,17},{3.72,18},{3.731,19},{3.735,20},{3.744,21},{3.753,22},{3.756,23},{3.758,24},{3.762,25},{3.767,26},{3.774,27},{3.78,28},{3.783,29},{3.786,30},{3.789,31},{3.794,32},{3.797,33},{3.8,34},{3.802,35},{3.805,36},{3.808,37},{3.811,38},{3.815,39},{3.818,40},{3.822,41},{3.825,42},{3.829,43},{3.833,44},{3.836,45},{3.84,46},{3.843,47},{3.847,48},{3.85,49},{3.854,50},{3.857,51},{3.86,52},{3.863,53},{3.866,54},{3.87,55},{3.874,56},{3.879,57},{3.888,58},{3.893,59},{3.897,60},{3.902,61},{3.906,62},{3.911,63},{3.918,64},{3.923,65},{3.928,66},{3.939,67},{3.943,68},{3.949,69},{3.955,70},{3.961,71},{3.968,72},{3.974,73},{3.981,74},{3.987,75},{3.994,76},{4.001,77},{4.007,78},{4.014,79},{4.021,80},{4.029,81},{4.036,82},{4.044,83},{4.052,84},{4.062,85},{4.074,86},{4.085,87},{4.095,88},{4.105,89},{4.111,90},{4.116,91},{4.12,92},{4.125,93},{4.129,94},{4.135,95},{4.145,96},{4.176,97},{4.179,98},{4.193,99},{4.2,100} } - - for i, v in ipairs(myArrayPercentList) do - if v[1] >= cellValue then - result = v[2] - break + for i1, v1 in ipairs(_lipoPercentListSplit) do + --is the cellVal < last-value-on-sub-list? (first-val:v1[1], last-val:v1[#v1]) + if (cellValue <= v1[#v1][1]) then + -- cellVal is in this sub-list, find the exact value + for i2, v2 in ipairs(v1) do + if v2[1] >= cellValue then + result = v2[2] + return result + end + end end end - return result + -- in case somehow voltage is too high (>4.2), don't return nil + return 100 end --- This function returns a table with cels values @@ -152,20 +195,13 @@ local function calculateBatteryData(wgt) return end - -- this is necessary for simu where cell-count can change - if #wgt.cellDataHistoryLowest ~= #newCellData then - wgt.cellDataHistoryLowest = {} - for k, v in pairs(newCellData) do - wgt.cellDataHistoryLowest[k] = 5 -- invalid reading, set high value so the min() will update it soon - end - end - local cellMax = 0 local cellMin = 5 local cellSum = 0 for k, v in pairs(newCellData) do -- stores the lowest cell values in historical table - if v > 1 and v < wgt.cellDataHistoryLowest[k] then -- min 1v to consider a valid reading + if v > 1 and v < wgt.cellDataHistoryLowest[k] then + -- min 1v to consider a valid reading wgt.cellDataHistoryLowest[k] = v --- calc history lowest of all cells @@ -181,7 +217,8 @@ local function calculateBatteryData(wgt) end --- calc lowest of all cells - if v < cellMin and v > 1 then -- min 1v to consider a valid reading + if v < cellMin and v > 1 then + -- min 1v to consider a valid reading cellMin = v end --- sum of all cells @@ -197,7 +234,6 @@ local function calculateBatteryData(wgt) --- average of all cells wgt.cellAvg = wgt.cellSum / wgt.cellCount - wgt.cellPercent = getCellPercent(wgt.cellMin) wgt.cellDataLive = newCellData @@ -219,13 +255,19 @@ local function calculateBatteryData(wgt) wgt.secondaryValue = "-2" end - wgt.isDataAvailable = true + -- calculate intensive CPU data + if (periodicHasPassed(wgt.periodic1) or wgt.cellPercent == 0) then + + --wgt.cellPercent = getCellPercent(wgt, wgt.cellMin) -- use batt percentage by lowest cell voltage + wgt.cellPercent = getCellPercent(wgt, wgt.cellAvg) -- use batt percentage by average cell voltage + + periodicReset(wgt.periodic1) + end end - -- color for battery -- This function returns green at 100%, red bellow 30% and graduate in between local function getPercentColor(percent) @@ -242,34 +284,46 @@ end -- This function returns green at gvalue, red at rvalue and graduate in between local function getRangeColor(value, green_value, red_value) local range = math.abs(green_value - red_value) - if range==0 then return lcd.RGB(0, 0xdf, 0) end + if range == 0 then + return lcd.RGB(0, 0xdf, 0) + end + if value == nil then + return lcd.RGB(0, 0xdf, 0) + end if green_value > red_value then - if value > green_value then return lcd.RGB(0, 0xdf, 0) end - if value < red_value then return lcd.RGB(0xdf, 0, 0) end + if value > green_value then + return lcd.RGB(0, 0xdf, 0) + end + if value < red_value then + return lcd.RGB(0xdf, 0, 0) + end g = math.floor(0xdf * (value - red_value) / range) r = 0xdf - g return lcd.RGB(r, g, 0) else - if value > green_value then return lcd.RGB(0, 0xdf, 0) end - if value < red_value then return lcd.RGB(0xdf, 0, 0) end + if value > green_value then + return lcd.RGB(0, 0xdf, 0) + end + if value < red_value then + return lcd.RGB(0xdf, 0, 0) + end r = math.floor(0xdf * (value - green_value) / range) g = 0xdf - r return lcd.RGB(r, g, 0) end end - --- Zone size: 70x39 1/8th top bar local function refreshZoneTiny(wgt) local myString = string.format("%2.1fV", wgt.mainValue) - lcd.drawText(wgt.zone.x + wgt.zone.w -25, wgt.zone.y + 5, wgt.cellPercent .. "%", RIGHT + SMLSIZE + CUSTOM_COLOR + wgt.no_telem_blink) - lcd.drawText(wgt.zone.x + wgt.zone.w -25, wgt.zone.y + 20, myString, RIGHT + SMLSIZE + CUSTOM_COLOR + wgt.no_telem_blink) + lcd.drawText(wgt.zone.x + wgt.zone.w - 25, wgt.zone.y + 5, wgt.cellPercent .. "%", RIGHT + SMLSIZE + CUSTOM_COLOR + wgt.no_telem_blink) + lcd.drawText(wgt.zone.x + wgt.zone.w - 25, wgt.zone.y + 20, myString, RIGHT + SMLSIZE + CUSTOM_COLOR + wgt.no_telem_blink) -- draw batt lcd.drawRectangle(wgt.zone.x + 50, wgt.zone.y + 9, 16, 25, CUSTOM_COLOR, 2) - lcd.drawFilledRectangle(wgt.zone.x +50 + 4, wgt.zone.y + 7, 6, 3, CUSTOM_COLOR) + lcd.drawFilledRectangle(wgt.zone.x + 50 + 4, wgt.zone.y + 7, 6, 3, CUSTOM_COLOR) local rect_h = math.floor(25 * wgt.cellPercent / 100) - lcd.drawFilledRectangle(wgt.zone.x +50, wgt.zone.y + 9 + 25 - rect_h, 16, rect_h, CUSTOM_COLOR + wgt.no_telem_blink) + lcd.drawFilledRectangle(wgt.zone.x + 50, wgt.zone.y + 9 + 25 - rect_h, 16, rect_h, CUSTOM_COLOR + wgt.no_telem_blink) end --- Zone size: 160x32 1/8th @@ -307,13 +361,13 @@ local function refreshZoneMedium(wgt) end -- draw values - lcd.drawText(wgt.zone.x, wgt.zone.y + 35, string.format("%2.1fV", wgt.mainValue), DBLSIZE + CUSTOM_COLOR + wgt.shadowed + wgt.no_telem_blink) + lcd.drawText(wgt.zone.x + wgt.zone.w, wgt.zone.y, string.format("%2.1fV", wgt.mainValue), DBLSIZE + CUSTOM_COLOR + RIGHT + wgt.shadowed + wgt.no_telem_blink) -- more info if 1/4 is high enough (without trim & slider) if wgt.zone.h > 80 then - lcd.drawText(wgt.zone.x , wgt.zone.y + 70, string.format("%2.2fV" , wgt.secondaryValue), SMLSIZE + CUSTOM_COLOR + wgt.no_telem_blink) - lcd.drawText(wgt.zone.x + 50, wgt.zone.y + 70, string.format("dV %2.2fV", wgt.cellMax - wgt.cellMin), SMLSIZE + CUSTOM_COLOR + wgt.no_telem_blink) - lcd.drawText(wgt.zone.x , wgt.zone.y + 84, string.format("Min %2.2fV", wgt.cellDataHistoryCellLowest), SMLSIZE + CUSTOM_COLOR + wgt.no_telem_blink) + --lcd.drawText(wgt.zone.x + 50 , wgt.zone.y + 70, string.format("%2.2fV" , wgt.secondaryValue), SMLSIZE + CUSTOM_COLOR + wgt.no_telem_blink) + lcd.drawText(wgt.zone.x, wgt.zone.y + 70, string.format("dV %2.2fV", wgt.cellMax - wgt.cellMin), SMLSIZE + CUSTOM_COLOR + wgt.no_telem_blink) + lcd.drawText(wgt.zone.x, wgt.zone.y + 84, string.format("Min %2.2fV", wgt.cellDataHistoryCellLowest), SMLSIZE + CUSTOM_COLOR + wgt.no_telem_blink) end -- fill batt @@ -321,28 +375,21 @@ local function refreshZoneMedium(wgt) lcd.drawGauge(wgt.zone.x + myBatt.x, wgt.zone.y + myBatt.y, myBatt.w, myBatt.h, wgt.cellPercent, 100, CUSTOM_COLOR) -- draw cells - local cellH = wgt.zone.h / wgt.cellCount - if cellH > 20 then cellH =20 end + local cellH = 19 local cellX = 118 - local cellW = 58 + local cellW = wgt.zone.w / 3 + + local pos = { { x = 0, y = 38 }, { x = cellW, y = 38 }, { x = 2 * cellW, y = 38 }, { x = 0, y = 57 }, { x = cellW, y = 57 }, { x = 2 * cellW, y = 57 } } for i = 1, wgt.cellCount, 1 do - local cellY = wgt.zone.y + (i-1)* (cellH -1) + local cellY = wgt.zone.y + (i - 1) * (cellH - 1) -- fill current cell - lcd.setColor(CUSTOM_COLOR, getRangeColor(wgt.cellDataLive[i], wgt.cellMax, wgt.cellMax - 0.2)) --lcd.drawFilledRectangle(wgt.zone.x + cellX , cellY, 58, cellH, CUSTOM_COLOR) - local percentCurrent = getCellPercent(wgt.cellDataLive[i]) - local percentMin = getCellPercent(wgt.cellDataHistoryLowest[i]) - - lcd.drawFilledRectangle(wgt.zone.x + cellX , cellY, cellW * percentCurrent / 100, cellH, CUSTOM_COLOR) - - -- fill min - lcd.setColor(CUSTOM_COLOR, getRangeColor(wgt.cellDataHistoryLowest[i], wgt.cellMax, wgt.cellMax - 0.2)) - lcd.drawFilledRectangle(wgt.zone.x + cellX + ((percentCurrent - percentMin) / 100) , cellY, cellW - (cellW * (percentCurrent - percentMin) / 100), cellH, CUSTOM_COLOR) - + lcd.setColor(CUSTOM_COLOR, getRangeColor(wgt.cellDataLive[i], wgt.cellMax, wgt.cellMax - 0.2)) + lcd.drawFilledRectangle(wgt.zone.x + pos[i].x, wgt.zone.y + pos[i].y, cellW - 1, 20, CUSTOM_COLOR) lcd.setColor(CUSTOM_COLOR, WHITE) - lcd.drawText (wgt.zone.x + cellX + 10, cellY, string.format("%.2f", wgt.cellDataLive[i]), SMLSIZE + CUSTOM_COLOR + wgt.shadowed + wgt.no_telem_blink) - lcd.drawRectangle (wgt.zone.x + cellX , cellY, 59, cellH, CUSTOM_COLOR , 1) + lcd.drawText(wgt.zone.x + pos[i].x + 10, wgt.zone.y + pos[i].y, string.format("%.2f", wgt.cellDataLive[i]), CUSTOM_COLOR + wgt.shadowed) + lcd.drawRectangle(wgt.zone.x + pos[i].x, wgt.zone.y + pos[i].y, cellW, 20, CUSTOM_COLOR, 1) end -- draws bat @@ -447,7 +494,9 @@ end -- This function allow recording of lowest cells when widget is in background local function background(wgt) - if (wgt == nil) then return end + if (wgt == nil) then + return + end detectResetEvent(wgt) @@ -456,11 +505,21 @@ local function background(wgt) end local function refresh(wgt) - - if (wgt == nil) then return end - if (wgt.options == nil) then return end - if (wgt.zone == nil) then return end - if (wgt.options.LowestCell == nil) then return end + if (wgt == nil) then + return + end + if type(wgt) ~= "table" then + return + end + if (wgt.options == nil) then + return + end + if (wgt.zone == nil) then + return + end + if (wgt.options.LowestCell == nil) then + return + end if wgt.options.Shadow == 1 then wgt.shadowed = SHADOWED @@ -478,14 +537,17 @@ local function refresh(wgt) wgt.no_telem_blink = INVERS + BLINK end - - if wgt.zone.w > 380 and wgt.zone.h > 165 then refreshZoneXLarge(wgt) - elseif wgt.zone.w > 180 and wgt.zone.h > 145 then refreshZoneLarge(wgt) - elseif wgt.zone.w > 170 and wgt.zone.h > 65 then refreshZoneMedium(wgt) - elseif wgt.zone.w > 150 and wgt.zone.h > 28 then refreshZoneSmall(wgt) - elseif wgt.zone.w > 65 and wgt.zone.h > 35 then refreshZoneTiny(wgt) + if wgt.zone.w > 380 and wgt.zone.h > 165 then + refreshZoneXLarge(wgt) + elseif wgt.zone.w > 180 and wgt.zone.h > 145 then + refreshZoneLarge(wgt) + elseif wgt.zone.w > 170 and wgt.zone.h > 65 then + refreshZoneMedium(wgt) + elseif wgt.zone.w > 150 and wgt.zone.h > 28 then + refreshZoneSmall(wgt) + elseif wgt.zone.w > 65 and wgt.zone.h > 35 then + refreshZoneTiny(wgt) end - end -return { name="BattCheck", options=_options, create=create, update=update, background=background, refresh=refresh } +return { name = "BattCheck", options = _options, create = create, update = update, background = background, refresh = refresh } \ No newline at end of file