From e806b446a8dc8550e615289bf9c535153f502fdd Mon Sep 17 00:00:00 2001 From: Offer Shmuely Date: Fri, 27 Feb 2026 20:18:04 +0200 Subject: [PATCH] updates for TX16mk3 800x480 resolution implement flight logic rules for various aircraft types - flight rule for planes with telemetry (1_plane_tlm.lua) - flight rule for planes without telemetry (2_plane_no_tlm.lua) - helicopter flight rule based on RPM (3_heli_rpm.lua) - helicopter arm switch rule (4_heli_arm.lua) - flight rule controlled by a switch (5_by_switch.lua) - DLG (Discus Launch Glider) specific flight rule (6_dlg.lua) --- sdcard/c480x272/WIDGETS/Flights/app.lua | 244 ++++++------------ sdcard/c480x272/WIDGETS/Flights/lib_log.lua | 10 +- .../WIDGETS/Flights/lib_widget_tools.lua | 172 +++++++++--- sdcard/c480x272/WIDGETS/Flights/main.lua | 31 ++- .../WIDGETS/Flights/rules/1_plane_tlm.lua | 96 +++++++ .../WIDGETS/Flights/rules/2_plane_no_tlm.lua | 69 +++++ .../WIDGETS/Flights/rules/3_heli_rpm.lua | 96 +++++++ .../WIDGETS/Flights/rules/4_heli_arm.lua | 82 ++++++ .../WIDGETS/Flights/rules/5_by_switch.lua | 49 ++++ .../c480x272/WIDGETS/Flights/rules/6_dlg.lua | 98 +++++++ .../WIDGETS/Flights/rules/FlightLogicRule.lua | 146 +++++++++++ sdcard/c480x320/WIDGETS/Flights/app.lua | 244 ++++++------------ sdcard/c480x320/WIDGETS/Flights/lib_log.lua | 10 +- .../WIDGETS/Flights/lib_widget_tools.lua | 172 +++++++++--- sdcard/c480x320/WIDGETS/Flights/main.lua | 31 ++- .../WIDGETS/Flights/rules/1_plane_tlm.lua | 96 +++++++ .../WIDGETS/Flights/rules/2_plane_no_tlm.lua | 69 +++++ .../WIDGETS/Flights/rules/3_heli_rpm.lua | 96 +++++++ .../WIDGETS/Flights/rules/4_heli_arm.lua | 82 ++++++ .../WIDGETS/Flights/rules/5_by_switch.lua | 49 ++++ .../c480x320/WIDGETS/Flights/rules/6_dlg.lua | 98 +++++++ .../WIDGETS/Flights/rules/FlightLogicRule.lua | 146 +++++++++++ sdcard/c800x480/WIDGETS/Flights/app.lua | 244 ++++++------------ sdcard/c800x480/WIDGETS/Flights/lib_log.lua | 10 +- .../WIDGETS/Flights/lib_widget_tools.lua | 172 +++++++++--- sdcard/c800x480/WIDGETS/Flights/main.lua | 31 ++- .../WIDGETS/Flights/rules/1_plane_tlm.lua | 96 +++++++ .../WIDGETS/Flights/rules/2_plane_no_tlm.lua | 69 +++++ .../WIDGETS/Flights/rules/3_heli_rpm.lua | 96 +++++++ .../WIDGETS/Flights/rules/4_heli_arm.lua | 82 ++++++ .../WIDGETS/Flights/rules/5_by_switch.lua | 49 ++++ .../c800x480/WIDGETS/Flights/rules/6_dlg.lua | 98 +++++++ .../WIDGETS/Flights/rules/FlightLogicRule.lua | 146 +++++++++++ 33 files changed, 2652 insertions(+), 627 deletions(-) create mode 100644 sdcard/c480x272/WIDGETS/Flights/rules/1_plane_tlm.lua create mode 100644 sdcard/c480x272/WIDGETS/Flights/rules/2_plane_no_tlm.lua create mode 100644 sdcard/c480x272/WIDGETS/Flights/rules/3_heli_rpm.lua create mode 100644 sdcard/c480x272/WIDGETS/Flights/rules/4_heli_arm.lua create mode 100644 sdcard/c480x272/WIDGETS/Flights/rules/5_by_switch.lua create mode 100644 sdcard/c480x272/WIDGETS/Flights/rules/6_dlg.lua create mode 100644 sdcard/c480x272/WIDGETS/Flights/rules/FlightLogicRule.lua create mode 100644 sdcard/c480x320/WIDGETS/Flights/rules/1_plane_tlm.lua create mode 100644 sdcard/c480x320/WIDGETS/Flights/rules/2_plane_no_tlm.lua create mode 100644 sdcard/c480x320/WIDGETS/Flights/rules/3_heli_rpm.lua create mode 100644 sdcard/c480x320/WIDGETS/Flights/rules/4_heli_arm.lua create mode 100644 sdcard/c480x320/WIDGETS/Flights/rules/5_by_switch.lua create mode 100644 sdcard/c480x320/WIDGETS/Flights/rules/6_dlg.lua create mode 100644 sdcard/c480x320/WIDGETS/Flights/rules/FlightLogicRule.lua create mode 100644 sdcard/c800x480/WIDGETS/Flights/rules/1_plane_tlm.lua create mode 100644 sdcard/c800x480/WIDGETS/Flights/rules/2_plane_no_tlm.lua create mode 100644 sdcard/c800x480/WIDGETS/Flights/rules/3_heli_rpm.lua create mode 100644 sdcard/c800x480/WIDGETS/Flights/rules/4_heli_arm.lua create mode 100644 sdcard/c800x480/WIDGETS/Flights/rules/5_by_switch.lua create mode 100644 sdcard/c800x480/WIDGETS/Flights/rules/6_dlg.lua create mode 100644 sdcard/c800x480/WIDGETS/Flights/rules/FlightLogicRule.lua diff --git a/sdcard/c480x272/WIDGETS/Flights/app.lua b/sdcard/c480x272/WIDGETS/Flights/app.lua index 0780fa35..05ddae54 100644 --- a/sdcard/c480x272/WIDGETS/Flights/app.lua +++ b/sdcard/c480x272/WIDGETS/Flights/app.lua @@ -48,21 +48,26 @@ -- or -- flight_ended2.wav-->flight_ended.wav -- for Heli, the motor-switch=arm-switch (same value for both) --- if you need a reversed arm switch (e.g. !SF) you need to do change it (for now) in the script: inverted_arm_switch_logic=0 -- if you prefer different logics, do them on logical switches, and put that logical switch in both Arm & motor channel --- if your recweiver does not have telemetry, use_telemetry-->off +-- if your recweiver does not have telemetry, use: [Plane no Telemetry] ]] +local args = {...} +local triggerTypeDefs = args[1] + local app_name = "Flights" -local app_ver = "1.7" +local app_ver = "2.1" + +local lvSCALE = lvgl.LCD_SCALE or 1 +local is800 = (LCD_W==800) local build_ui = nil ------------------------------------------------------------------------------------------------------------------ -- configuration local default_flight_starting_duration = 30 -- 30 sec to detect flight success local default_flight_ending_duration = 8 -- 8 sec to detect flight ended -local default_min_motor_value = 200 +-- local default_min_motor_value = 200 local enable_count_announcement_on_start = 0 -- 0=no voice, 1=play the count upon increment local enable_count_announcement_on_end = 1 -- 0=no voice, 1=play the count upon end of flight local show_dots = true -- false=do not show dots, true=show dbg dots @@ -72,9 +77,7 @@ local use_flights_history = 1 -- 0=do not write flights-history, -- imports -local img = bitmap.open("/WIDGETS/" .. app_name .. "/logo.png") -local LibLogClass = loadScript("/WIDGETS/" .. app_name .. "/lib_log.lua", "btd") -local m_log = LibLogClass(app_name, "/WIDGETS/" .. app_name) +local m_log = assert(loadScript("/WIDGETS/" .. app_name .. "/lib_log.lua", "btd"))(app_name, "/WIDGETS/" .. app_name) -- better font size names local FS={FONT_38=XXLSIZE,FONT_16=DBLSIZE,FONT_12=MIDSIZE,FONT_8=0,FONT_6=SMLSIZE} @@ -87,21 +90,14 @@ local function update(wgt, options) if (wgt == nil) then return end wgt.options = options - wgt.use_telemetry = wgt.options.use_telemetry + wgt.triggerDesc = triggerTypeDefs.info[wgt.options.triggerType].desc + wgt.triggerFile = triggerTypeDefs.info[wgt.options.triggerType].file wgt.enable_sounds = wgt.options.enable_sounds - wgt.heli_mode = wgt.options.heli_mode == 1 - if (wgt.options.arm_switch_id == wgt.options.motor_channel) then - wgt.heli_mode = true - end -- status wgt.status = {} - wgt.status.switch_on = nil wgt.status.switch_name = nil - wgt.status.tele_is_available = nil - wgt.status.motor_active = nil wgt.status.motor_channel_name = nil - wgt.status.motor_channel_direction_inv = nil wgt.status.flight_state = "GROUND" wgt.status.duration_passed = 0 wgt.status.periodic1 = wgt.tools.periodicInit() @@ -110,11 +106,9 @@ local function update(wgt, options) wgt.status.flight_start_date_time = 0 wgt.status.flight_end_time = 0 wgt.status.flight_duration = 0 - wgt.status.ground_on_switch = false if (wgt.options.min_flight_duration < 0) then wgt.options.min_flight_duration = math.abs(wgt.options.min_flight_duration) - wgt.status.ground_on_switch = true default_flight_ending_duration = 1 end @@ -135,6 +129,19 @@ local function update(wgt, options) wgt.status.motor_channel_name = fi_mot.name end + local t_chunk = assert(loadScript("/WIDGETS/" .. app_name .. "/rules/" .. wgt.triggerFile, "btd"), "Failed to load trigger script: "..wgt.triggerFile) + wgt.rule = t_chunk(m_log, app_name, wgt.status.switch_name, wgt.status.motor_channel_name) + + log("Using trigger type: %s (%s)", wgt.triggerDesc, wgt.triggerFile) + log("info: %s", wgt.rule:info()) + + local override_min_flight_time = wgt.rule:override_min_flight_time() + if (override_min_flight_time ~= nil) then + wgt.options.min_flight_duration = override_min_flight_time + log("Using override min flight duration: %s", wgt.options.min_flight_duration) + end + + -- auto debug mode if widget size 1/2 or 1/1 wgt.options.is_debug = (wgt.options.auto_debug==1 and wgt.zone.h > 140) -- log("auto_debug: %s, is_debug: %s, wgt.zone.h: %s", wgt.options.auto_debug, wgt.options.is_debug, wgt.zone.h) @@ -148,8 +155,8 @@ local function update(wgt, options) end local ver, radio, maj, minor, rev, osname = getVersion() - wgt.is_valid_ver = (maj == 2 and minor >= 11) - + local nVer = maj*1000000 + minor*1000 + rev + wgt.is_valid_ver = (nVer>=2011000) build_ui(wgt) end @@ -157,18 +164,13 @@ end local function create(zone, options) local wgt = { zone = zone, - options = options - } - --wgt.options.use_days = wgt.options.use_days % 2 -- modulo due to bug that cause the value to be other than 0|1 - - -- imports - wgt.ToolsClass = loadScript("/WIDGETS/" .. app_name .. "/lib_widget_tools.lua", "btd") - wgt.tools = wgt.ToolsClass(m_log, app_name) + options = options, - wgt.FlightsHistoryClass = loadScript("/WIDGETS/" .. app_name .. "/lib_flights_history.lua", "btd") - wgt.flightHistory = wgt.FlightsHistoryClass(m_log, app_name) - wgt.FlightsCountClass = loadScript("/WIDGETS/" .. app_name .. "/lib_flights_count.lua", "btd") - wgt.flightCountHWriter = wgt.FlightsCountClass(m_log, app_name, "/flights-count.csv") + -- imports + tools = loadScript("/WIDGETS/" .. app_name .. "/lib_widget_tools.lua", "btd")(m_log, app_name), + flightHistory = loadScript("/WIDGETS/" .. app_name .. "/lib_flights_history.lua", "btd")(m_log, app_name), + -- flightCountHWriter = loadScript("/WIDGETS/" .. app_name .. "/lib_flights_count.lua", "btd")(m_log, app_name, "/flights-count.csv"), + } update(wgt, options) return wgt @@ -201,72 +203,6 @@ local function getFontSize(wgt, txt) return FS.FONT_6 end ---------------------------------------------------------------------------------------------------- - -local function updateTelemetryStatus(wgt) - if wgt.use_telemetry == 0 then - wgt.status.tele_is_available = true - else - wgt.status.tele_is_available = wgt.tools.isTelemetryAvailable() - end -end - -local function updateMotorStatus(wgt) - - -- for heli, if the motor-sw==switch-sw, then ignore motor direction detection - if (wgt.heli_mode == true) then - wgt.status.motor_active = wgt.status.switch_on - return - end - - local motor_value = getValue(wgt.options.motor_channel) - --log(string.format("motor_value (%s): %s", wgt.options.motor_channel, motor_value)) - - ---- if we do not have telemetry, then the battery is not connected yet, so we can detect yet motor channel direction - --if (wgt.status.tele_is_available == nil or wgt.status.tele_is_available == false) then - -- return - --end - - if (wgt.status.motor_channel_direction_inv == nil) then - -- detect motor channel direction - if (motor_value < (-1024 + default_min_motor_value)) then - wgt.status.motor_channel_direction_inv = false - elseif (motor_value > (1024 - default_min_motor_value)) then - wgt.status.motor_channel_direction_inv = true - else - -- still nil - return - end - end - - if (wgt.status.motor_channel_direction_inv == false) then - -- non inverted mixer - if (motor_value > (-1024 + default_min_motor_value)) then - wgt.status.motor_active = true - else - wgt.status.motor_active = false - end - else - -- inverted mixer - if (motor_value < (1024 - default_min_motor_value)) then - wgt.status.motor_active = true - else - wgt.status.motor_active = false - end - end - -end - -local function updateSwitchStatus(wgt) - wgt.status.switch_on = getSwitchValue(wgt.options.arm_switch_id) - - -- if wgt.status.switch_on==true then - -- log(string.format("arm_switch(%s)=ON", wgt.status.switch_name)) - -- else - -- log(string.format("arm_switch(%s)=OFF", wgt.status.switch_name)) - -- end -end - -------------------------------------------------------------------------------------------------------- -- get flight count local function getFlightCount(wgt) @@ -345,29 +281,12 @@ local function stateChange(wgt, newState, timer_sec) end end - -local function is_flight_starting(wgt) - if (wgt.status.motor_active == true) and (wgt.status.switch_on == true) and (wgt.use_telemetry==0 or wgt.status.tele_is_available == true) then - return true - else - return false - end -end - local function background(wgt) - - updateSwitchStatus(wgt) - - updateMotorStatus(wgt) -- always after updateSwitchStatus - - updateTelemetryStatus(wgt) - - --log(string.format("tele_is_available: %s", wgt.status.tele_is_available)) + wgt.rule:background(wgt) -- **** state: GROUND *** if wgt.status.flight_state == "GROUND" then - -- if (wgt.status.motor_active == true) and (wgt.status.switch_on == true) and (wgt.use_telemetry==0 or wgt.status.tele_is_available == true) then - if (is_flight_starting(wgt) == true) then + if (wgt.rule:is_flight_starting() == true) then stateChange(wgt, "FLIGHT_STARTING", wgt.options.min_flight_duration) wgt.status.last_flight_count = getFlightCount(wgt) wgt.status.flight_start_time = getTime() * 10 / 1000 @@ -376,11 +295,9 @@ local function background(wgt) end return - -- **** state: FLIGHT_STARTING *** + -- **** state: FLIGHT_STARTING *** elseif wgt.status.flight_state == "FLIGHT_STARTING" then - - -- if (wgt.status.motor_active == false) or (wgt.status.switch_on == false) or (wgt.use_telemetry==1 and wgt.status.tele_is_available == false) then - if (is_flight_starting(wgt) == false) then + if (wgt.rule:is_flight_starting() == false) then stateChange(wgt, "GROUND", 0) return end @@ -402,10 +319,11 @@ local function background(wgt) end return - -- **** state: FLIGHT_ON *** + -- **** state: FLIGHT_ON *** elseif wgt.status.flight_state == "FLIGHT_ON" then -- record flight duration in case we soon detect end of flight - if (wgt.status.motor_active == true) and (wgt.status.switch_on == true) then + + if wgt.rule:is_still_on_flight() then wgt.status.flight_end_time = getTime() * 10 / 1000 wgt.status.flight_duration = wgt.status.flight_end_time - wgt.status.flight_start_time -- log("flight_start_time: %s", wgt.status.flight_start_time) @@ -413,13 +331,7 @@ local function background(wgt) -- log("flight_duration: %s", wgt.status.flight_duration) end - -- if (wgt.status.ground_on_switch and wgt.status.switch_on == false) then - -- stateChange(wgt, "FLIGHT_ENDING", 0) - if (wgt.use_telemetry==1 and wgt.status.tele_is_available == false) or - (wgt.use_telemetry==0 and wgt.status.switch_on == false) - or - (wgt.status.ground_on_switch and wgt.status.switch_on == false) - then + if wgt.rule:is_flight_ending() then stateChange(wgt, "FLIGHT_ENDING", default_flight_ending_duration) local num_flights = getFlightCount(wgt) @@ -448,8 +360,7 @@ local function background(wgt) doEndOfFlightTasks(wgt) end - if (wgt.use_telemetry==1 and wgt.status.tele_is_available == true and wgt.status.ground_on_switch==false) or - (wgt.use_telemetry==0 and wgt.status.switch_on == true and wgt.status.ground_on_switch==false) then + if wgt.rule:is_flight_ending() == false then stateChange(wgt, "FLIGHT_ON", 0) end @@ -498,15 +409,15 @@ build_ui = function(wgt) local ts_w, ts_h = lcd.sizeText(num_flights, font_size) local dx = (zone_w - ts_w) / 2 - local dyh = 5 + local dyh = 5*lvSCALE local dy -- = header_h - 1 - local is_top_bar = (zone_h < 50) + local is_top_bar = (zone_h < 50*lvSCALE) if is_top_bar then -- force minimal spaces - dyh = -3 + dyh = -3*lvSCALE else - dyh = 5 + dyh = 5*lvSCALE end -- global @@ -519,41 +430,43 @@ build_ui = function(wgt) -- draw count if is_top_bar == true then -- pMain:label({x=zone_w-ts_w -10, y=dy, font=font_size, text=function() return getFlightCount(wgt) end, color=wgt.options.text_color}) - pMain:label({x=10, y=13, font=font_size, text=function() return getFlightCount(wgt) end, color=wgt.options.text_color}) + pMain:label({x=10*lvSCALE, y=13*lvSCALE, font=font_size, text=function() return getFlightCount(wgt) end, color=wgt.options.text_color}) else - pMain:label({x=zone_w-ts_w-5, y=5, font=font_size, text=function() return getFlightCount(wgt) end, color=wgt.options.text_color}) + pMain:label({x=zone_w-ts_w-5*lvSCALE, y=5*lvSCALE, font=font_size, text=function() return getFlightCount(wgt) end, color=wgt.options.text_color}) end - -- enable_dbg_dots + -- enable 3 dots if (show_dots == true) then - local dxc = 7 - pMain:circle({x=5, y=20 + dxc*0, radius=3, filled=true, color=function() return wgt.status.tele_is_available==true and GREEN or GREY end}) - pMain:circle({x=5, y=20 + dxc*1, radius=3, filled=true, color=function() return wgt.status.switch_on==true and GREEN or GREY end}) - pMain:circle({x=5, y=20 + dxc*2, radius=3, filled=true, color=function() return wgt.status.motor_active==true and GREEN or GREY end}) + local dxc = 7*lvSCALE + pMain:circle({x=5*lvSCALE, y=20*lvSCALE + dxc*0, radius=3, filled=true, color=function() return wgt.rule:is_dot_1() and GREEN or GREY end}) + pMain:circle({x=5*lvSCALE, y=20*lvSCALE + dxc*1, radius=3, filled=true, color=function() return wgt.rule:is_dot_2() and GREEN or GREY end}) + pMain:circle({x=5*lvSCALE, y=20*lvSCALE + dxc*2, radius=3, filled=true, color=function() return wgt.rule:is_dot_3() and GREEN or GREY end}) end -- debug local pInfo = lvgl.box({x=0, y=40}) if wgt.options.is_debug == true then - local dx = 15 - pInfo:label({x=dx, y=0, font=FS.FONT_6, text=function() return string.format("%s - telemetry", ternary(wgt.status.tele_is_available)) end}) - pInfo:label({x=dx, y=15, font=FS.FONT_6, text=function() return string.format("%s - arm_switch (%s)", ternary(wgt.status.switch_on), wgt.status.switch_name) end}) - pInfo:label({x=dx, y=30, font=FS.FONT_6, text=function() - if (wgt.heli_mode == false) then - return string.format("%s - throttle (%s) (inv: %s)" , ternary(wgt.status.motor_active), wgt.status.motor_channel_name, wgt.status.motor_channel_direction_inv) - else - return string.format("%s - heli mode arm (ignore throttle)", ternary(wgt.status.motor_active)) - end - end}) - pInfo:label({x=dx, y=45, font=FS.FONT_6, text=function() return string.format("timer: %.1f/%d", wgt.status.duration_passed / 1000, wgt.tools.getDurationMili(wgt.status.periodic1) / 1000) end}) - pInfo:label({x=dx, y=60, font=FS.FONT_6, text=function() return string.format("flight duration: %.1f", wgt.status.flight_duration) end}) - - pInfo:label({x=dx, y=85, font=FS.FONT_6, text="state:"}) - dx = 50 - pInfo:label({x=dx, y=85, font=FS.FONT_6, text="GROUND" , color=function() return getColorByState(wgt, "GROUND") end}) - pInfo:label({x=dx, y=100, font=FS.FONT_6, text="FLIGHT_STARTING", color=function() return getColorByState(wgt, "FLIGHT_STARTING") end}) - pInfo:label({x=dx, y=115, font=FS.FONT_6, text="FLIGHT_ON" , color=function() return getColorByState(wgt, "FLIGHT_ON") end}) - pInfo:label({x=dx, y=130, font=FS.FONT_6, text="FLIGHT_ENDING" , color=function() return getColorByState(wgt, "FLIGHT_ENDING") end}) + local dx = 15*lvSCALE + pInfo:label({x=dx+190*lvSCALE, y= 5*lvSCALE, font=FS.FONT_8, text=function() return string.format("Rule: %s", wgt.triggerDesc) end}) + pInfo:label({x=dx, y= 0*lvSCALE, font=FS.FONT_6, text=function() return wgt.rule:dot_1_txt() end}) + pInfo:label({x=dx, y=15*lvSCALE, font=FS.FONT_6, text=function() return wgt.rule:dot_2_txt() end}) + pInfo:label({x=dx, y=30*lvSCALE, font=FS.FONT_6, text=function() return wgt.rule:dot_3_txt() end}) + pInfo:label({x=dx, y=45*lvSCALE, font=FS.FONT_6, text=function() return string.format("timer: %.1f/%d", wgt.status.duration_passed / 1000, wgt.tools.getDurationMili(wgt.status.periodic1) / 1000) end}) + pInfo:label({x=dx, y=60*lvSCALE, font=FS.FONT_6, text=function() return string.format("flight duration: %.1f", wgt.status.flight_duration) end}) + + pInfo:label({x=dx, y=80*lvSCALE, font=FS.FONT_6, text="state:"}) + dx = 50*lvSCALE + pInfo:label({x=dx, y= 80*lvSCALE, font=FS.FONT_6, text="GROUND" , color=function() return getColorByState(wgt, "GROUND") end}) + pInfo:label({x=dx, y= 95*lvSCALE, font=FS.FONT_6, text="FLIGHT_STARTING", color=function() return getColorByState(wgt, "FLIGHT_STARTING") end}) + pInfo:label({x=dx, y=110*lvSCALE, font=FS.FONT_6, text="FLIGHT_ON" , color=function() return getColorByState(wgt, "FLIGHT_ON") end}) + pInfo:label({x=dx, y=125*lvSCALE, font=FS.FONT_6, text="FLIGHT_ENDING" , color=function() return getColorByState(wgt, "FLIGHT_ENDING") end}) + + pInfo:label({x= 5*lvSCALE, y=145*lvSCALE, font=FS.FONT_6, text=function() return string.format("starting:\n %s", wgt.rule.is_flight_starting()) end}) + pInfo:label({x= 70*lvSCALE, y=145*lvSCALE, font=FS.FONT_6, text=function() return string.format("on_flight:\n %s", wgt.rule.is_still_on_flight()) end}) + pInfo:label({x=140*lvSCALE, y=145*lvSCALE, font=FS.FONT_6, text=function() return string.format("ending:\n %s", wgt.rule.is_flight_ending()) end}) + + pInfo:rectangle({x=dx+160*lvSCALE, y=30*lvSCALE, w=260*lvSCALE, h=140*lvSCALE, color=LIGHTBLUE, filled=true, rounded=5}) + pInfo:label({x=dx+(160+10)*lvSCALE, y=40*lvSCALE, font=FS.FONT_6, text=function() return wgt.rule:info() end, color=WHITE}) end end @@ -561,11 +474,4 @@ local function refresh(wgt, event, touchState) background(wgt) end -return { - name = app_name, - create = create, - update = update, - refresh = refresh, - background = background, - useLvgl=true -} +return {name=app_name, create=create, update=update, refresh=refresh, background=background, useLvgl=true} diff --git a/sdcard/c480x272/WIDGETS/Flights/lib_log.lua b/sdcard/c480x272/WIDGETS/Flights/lib_log.lua index c23e6e8a..7a933a01 100644 --- a/sdcard/c480x272/WIDGETS/Flights/lib_log.lua +++ b/sdcard/c480x272/WIDGETS/Flights/lib_log.lua @@ -2,6 +2,7 @@ local app_name, script_dir = ... local ENABLE_LOG_TO_CONSOLE = true local ENABLE_LOG_TO_FILE = false +local ENABLE_LOG_TO_SERIAL = false local M = {} @@ -17,6 +18,7 @@ local log = { outfile = script_dir .. "/app.log", enable_file = ENABLE_LOG_TO_FILE, enable_console = ENABLE_LOG_TO_CONSOLE and is_simulator(), + enable_serial_dbg = ENABLE_LOG_TO_SERIAL, current_level = nil, -- func @@ -61,7 +63,7 @@ local function tostring(...) end function M.do_log(iLevel, ulevel, fmt, ...) - if log.enable_console == false and log.enable_file == false then + if log.enable_console == false and log.enable_file == false and log.enable_serial_dbg == false then return end @@ -91,6 +93,12 @@ function M.do_log(iLevel, ulevel, fmt, ...) io.write(fp, msg2 .. "\n") io.close(fp) end + + -- Output to log file + if log.enable_serial_dbg == true then + serialWrite(msg2.."\r\n") -- 115200 bps + end + end function M.trace(fmt, ...) diff --git a/sdcard/c480x272/WIDGETS/Flights/lib_widget_tools.lua b/sdcard/c480x272/WIDGETS/Flights/lib_widget_tools.lua index 1e419474..738daade 100644 --- a/sdcard/c480x272/WIDGETS/Flights/lib_widget_tools.lua +++ b/sdcard/c480x272/WIDGETS/Flights/lib_widget_tools.lua @@ -1,4 +1,6 @@ -local m_log, app_name = ... +local args = {...} +local m_log = args[1] +local app_name = args[2] local M = {} M.m_log = m_log @@ -9,18 +11,20 @@ M.tele_src_id = nil local getTime = getTime local lcd = lcd --- better font names -local FONT_38 = XXLSIZE -- 38px -local FONT_16 = DBLSIZE -- 16px -local FONT_12 = MIDSIZE -- 12px -local FONT_8 = 0 -- Default 8px -local FONT_6 = SMLSIZE -- 6px +-- better font size names +local FS={FONT_38=XXLSIZE,FONT_16=DBLSIZE,FONT_12=MIDSIZE,FONT_8=0,FONT_6=SMLSIZE} +M.FS = FS +M.FONT_LIST = {FS.FONT_6, FS.FONT_8, FS.FONT_12, FS.FONT_16, FS.FONT_38} +local lvSCALE = lvgl.LCD_SCALE or 1 -local FONT_LIST = {FONT_6, FONT_8, FONT_12, FONT_16, FONT_38} --------------------------------------------------------------------------------------------------- local function log(fmt, ...) - m_log.info(fmt, ...) + if M.m_log then + M.m_log.info(fmt, ...) + else + print("[" .. M.app_name .. "] " .. string.format(fmt, ...)) + end end --------------------------------------------------------------------------------------------------- @@ -55,9 +59,10 @@ end --------------------------------------------------------------------------------------------------- function M.periodicInit() - local t = {} - t.startTime = -1; - t.durationMili = -1; + local t = { + startTime = -1, + durationMili = -1 + } return t end @@ -66,6 +71,10 @@ function M.periodicStart(t, durationMili) t.durationMili = durationMili; end +function M.periodicStop(t) + t.durationMili = -1; +end + function M.periodicHasPassed(t, show_log) -- not started yet if (t.durationMili <= 0) then @@ -110,6 +119,43 @@ function M.isTelemetryAvailable() return is_telem > 0 end +function M.isTelemetryAvailableOld() + -- select telemetry source + if not M.tele_src_id then + --log("select telemetry source") + local tele_src = getFieldInfo("RSSI") + if not tele_src then tele_src = getFieldInfo("1RSS") end + if not tele_src then tele_src = getFieldInfo("2RSS") end + if not tele_src then tele_src = getFieldInfo("RQly") end + if not tele_src then tele_src = getFieldInfo("VFR%") end + if not tele_src then tele_src = getFieldInfo("VFR") end + if not tele_src then tele_src = getFieldInfo("TRSS") end + if not tele_src then tele_src = getFieldInfo("RxBt") end + if not tele_src then tele_src = getFieldInfo("A1") end + + if tele_src == nil then + --log("no telemetry sensor found") + M.tele_src_id = nil + M.tele_src_name = "---" + return false + else + --log("telemetry sensor found: " .. tele_src.name) + M.tele_src_id = tele_src.id + M.tele_src_name = tele_src.name + end + end + + if M.tele_src_id == nil then + return false + end + + local rx_val = getValue(M.tele_src_id) + if rx_val ~= 0 then + return true + end + return false +end + --------------------------------------------------------------------------------------------------- -- workaround to detect telemetry-reset event, until a proper implementation on the lua interface will be created @@ -120,7 +166,6 @@ end -- on event detection, the function onTelemetryResetEvent() will be trigger -- function M.detectResetEvent(wgt, callback_onTelemetryResetEvent) - local currMinRSSI = getValue('RSSI-') if (currMinRSSI == nil) then log("telemetry reset event: can not be calculated") @@ -160,8 +205,8 @@ function M.getSensorInfoByName(sensorName) s1.type = s2.type --name (string) Name s1.name = s2.name - --unit (number) See list of units in the appendix of the OpenTX Lua Reference Guide - s1.unit = s2.unit + --unit (number->string) See list of units in the appendix of the OpenTX Lua Reference Guide + s1.unit = M.unitIdToString(s2.unit) --prec (number) Number of decimals s1.prec = s2.prec --id (number) Only custom sensors @@ -213,9 +258,10 @@ function M.isSensorExist(sensorName) end --------------------------------------------------------------------------------------------------- --- workaround for bug in getFiledInfo() -- ???? why? +-- workaround for bug in getFiledInfo() why? function M.cleanInvalidCharFromGetFiledInfo(sourceName) - if string.byte(string.sub(sourceName, 1, 1)) > 127 then + + if string.byte(string.sub(sourceName, 1, 1)) > 127 then sourceName = string.sub(sourceName, 2, -1) end if string.byte(string.sub(sourceName, 1, 1)) > 127 then @@ -235,35 +281,97 @@ function M.getSourceNameCleaned(source) end ------------------------------------------------------------------------------------------------------ + function M.getFontSizeRelative(orgFontSize, delta) - for i = 1, #FONT_LIST do - if FONT_LIST[i] == orgFontSize then + for i = 1, #M.FONT_LIST do + if M.FONT_LIST[i] == orgFontSize then local newIndex = i + delta - newIndex = math.min(newIndex, #FONT_LIST) + newIndex = math.min(newIndex, #M.FONT_LIST) newIndex = math.max(newIndex, 1) - return FONT_LIST[newIndex] + return M.FONT_LIST[newIndex] end end return orgFontSize end +function M.getFontIndex(fontSize, defaultFontSize) + for i = 1, #M.FONT_LIST do + -- log("M.FONT_SIZES[%d]: %d (%d)", i, M.FONT_LIST[i], fontSize) + if M.FONT_LIST[i] == fontSize then + return i + end + end + return defaultFontSize +end + ------------------------------------------------------------------------------------------------------ + function M.lcdSizeTextFixed(txt, font_size) local ts_w, ts_h = lcd.sizeText(txt, font_size) local v_offset = 0 - if font_size == FONT_38 then - v_offset = -15 - elseif font_size == FONT_16 then - v_offset = -8 - elseif font_size == FONT_12 then - v_offset = -6 - elseif font_size == FONT_8 then - v_offset = -4 - elseif font_size == FONT_6 then - v_offset = -3 + if font_size == FS.FONT_38 then + v_offset = -6*lvSCALE + ts_h = 52*lvSCALE + ts_w=ts_w-3 + elseif font_size == FS.FONT_16 then + v_offset = -6*lvSCALE + ts_h = 28*lvSCALE + elseif font_size == FS.FONT_12 then + v_offset = -5*lvSCALE + ts_h = 20*lvSCALE + elseif font_size == FS.FONT_8 then + v_offset = -3*lvSCALE + ts_h = 15*lvSCALE + elseif font_size == FS.FONT_6 then + v_offset = -2*lvSCALE + ts_h = 14*lvSCALE + end + return ts_w, ts_h, v_offset +end + +function M.getFontSize(wgt, txt, max_w, max_h, max_font_size) + local maxFontIndex = M.getFontIndex(max_font_size, nil) + --log("getFontSize() [%s] %dx%d (maxIndex: %d)", txt, max_w, max_h, maxFontIndex) + + if maxFontIndex>=5 then + local w, h, v_offset = M.lcdSizeTextFixed(txt, FS.FONT_38) + if w <= max_w and h <= max_h then + log("[%s] FS.FONT_38 %dx%d", txt, w, h) + return FS.FONT_38, w, h, v_offset + else + log("[%s] FS.FONT_38 %dx%d (too small)", txt, w, h) + end + end + + local w, h, v_offset + if maxFontIndex>=4 then + w, h, v_offset = M.lcdSizeTextFixed(txt, FS.FONT_16) + if w <= max_w and h <= max_h then + -- log("[%s] FS.FONT_16 %dx%d", txt, w, h, txt) + return FS.FONT_16, w, h, v_offset + end + end + + if maxFontIndex>=3 then + w, h, v_offset = M.lcdSizeTextFixed(txt, FS.FONT_12) + if w <= max_w and h <= max_h then + -- log("[%s] FS.FONT_12 %dx%d", txt, w, h, txt) + return FS.FONT_12, w, h, v_offset + end end - return ts_w, ts_h +2*v_offset, v_offset + + if maxFontIndex>=2 then + w, h, v_offset = M.lcdSizeTextFixed(txt, FS.FONT_8) + if w <= max_w and h <= max_h then + -- log("[%s] FS.FONT_8 %dx%d", txt, w, h, txt) + return FS.FONT_8, w, h, v_offset + end + end + + w, h, v_offset = M.lcdSizeTextFixed(txt, FS.FONT_6) + -- log("[%s] FS.FONT_6 %dx%d", txt, w, h, txt) + return FS.FONT_6, w, h, v_offset end ------------------------------------------------------------------------------------------------------ diff --git a/sdcard/c480x272/WIDGETS/Flights/main.lua b/sdcard/c480x272/WIDGETS/Flights/main.lua index 92d8f3ec..4051773c 100644 --- a/sdcard/c480x272/WIDGETS/Flights/main.lua +++ b/sdcard/c480x272/WIDGETS/Flights/main.lua @@ -4,6 +4,27 @@ local tool = nil local default_flight_starting_duration = 30 -- 30 sec to detect flight success +local triggerTypeDefs = { + labels = { + "1. Plane with Telemetry", + "2. Plane no Telemetry", + "3. Heli [RPM]", + "4. Heli [Arm]", + "5. By switch", + "6. DLG [Height]", + "7. Glider [Height]", + }, + info = { + {desc = "1=Plane with Telemetry (mot+Arm+Telm)", file = "1_plane_tlm.lua" }, + {desc = "2=Plane no Telemetry (mot+Arm)", file = "2_plane_no_tlm.lua" }, + {desc = "3=Heli [RPM] (RPM+Telm)", file = "3_heli_rpm.lua" }, + {desc = "4=Heli [Arm] (Arm+Telm)", file = "4_heli_arm.lua" }, + {desc = "5=By switch", file = "5_by_switch.lua" }, + {desc = "6=DLG [Vario] (Height)", file = "6_dlg.lua" }, + {desc = "7=Glider [Vario] (mot+Arm+Height)", file = "7_glider.lua" }, + } +} + -- for backward compatibility local function getSwitchIds(key) local OS_SWITCH_ID = { @@ -21,27 +42,23 @@ end local DEFAULT_MOTOR_CHANNEL_ID = getSourceIndex("CH3") or getSourceIndex("thr111") or getSwitchIds("CH3") -- motor_channel=CH3 local options = { + { "triggerType" , CHOICE, 1 , triggerTypeDefs.labels}, { "arm_switch_id" , SWITCH, "SF"..CHAR_UP}, -- CHAR_UP|-|CHAR_DOWN { "motor_channel" , SOURCE, DEFAULT_MOTOR_CHANNEL_ID }, - { "heli_mode" , BOOL, 0}, -- ignore motor direction detection, and throttle position { "text_color" , COLOR, YELLOW},--, COLOR_THEME_PRIMARY2 }, { "min_flight_duration" , VALUE, default_flight_starting_duration, -30, 120 }, { "enable_sounds" , BOOL, 1}, -- 0=no sound, 1=play blip sound on increment & on flight end - { "use_telemetry" , BOOL, 1}, -- 0=do not use telemetry, 1=use telemetry in state machine { "auto_debug" , BOOL, 1}, -- show debug status on screen if widget is large enough - -- { "ground_on_switch" , BOOL, 0}, -- 0=auto detect ground by time, 1=ground on switch is used (not auto) } local function translate(name) local translations = { arm_switch_id="Arm Switch Position", motor_channel="Motor Channel", - heli_mode="Heli mode (ignore motor ch)", min_flight_duration = "Min flight duration (sec)", text_color = "Text color", enable_sounds = "Enable sounds", - use_telemetry = "Use telemetry", - -- ground_on_switch = "Ground on switch", + triggerType = "Type", auto_debug = "Auto debug", } return translations[name] @@ -50,7 +67,7 @@ end local function create(zone, options) -- print(string.format("1111 Flights create: %s", name)) - tool = assert(loadScript("/WIDGETS/"..app_name.."/app.lua", "btd"))() + tool = assert(loadScript("/WIDGETS/"..app_name.."/app.lua", "btd"))(triggerTypeDefs) return tool.create(zone, options) end local function update(wgt, options) return tool.update(wgt, options) end diff --git a/sdcard/c480x272/WIDGETS/Flights/rules/1_plane_tlm.lua b/sdcard/c480x272/WIDGETS/Flights/rules/1_plane_tlm.lua new file mode 100644 index 00000000..a902dd77 --- /dev/null +++ b/sdcard/c480x272/WIDGETS/Flights/rules/1_plane_tlm.lua @@ -0,0 +1,96 @@ +local args = {...} +local m_log = args[1] +local app_name = args[2] +local switch_name = args[3] +local motor_channel_name = args[4] + +local info = [[ +Flight Rules +flight active when: +1. Telemetry available +2. Arm switch ON +3. motor channel not idle +]] + +local FlightLogicRule = assert(loadScript("/WIDGETS/" .. app_name .. "/rules/FlightLogicRule", "btd"), "Failed to load trigger Base Class")(m_log, app_name, switch_name, motor_channel_name) + +local M = FlightLogicRule:new() + +local function log(fmt, ...) + FlightLogicRule:log(fmt, ...) +end + +function M:info() + return info +end + +function M:is_flight_starting() + if (M.status.tele_is_available == false) then + return false + end + if (M.status.switch_on == false) then + return false + end + if (M.status.motor_active == false) then + return false + end + + return true +end + +function M:is_still_on_flight() + if M.status.switch_on == false then + return false + end + if M.status.motor_active == false then + return false + end + return true +end + +function M:is_flight_ending() + return (M.status.tele_is_available == false) +end + +function M:is_dot_1() + return M.status.tele_is_available==true +end +function M:is_dot_2() + return M.status.switch_on==true +end +function M:is_dot_3() + return M.status.motor_active==true +end + +function M:dot_1_txt() + return string.format("%s - telemetry", M:to_on_off(M.status.tele_is_available)) +end + +function M:dot_2_txt() + return string.format("%s - arm_switch (%s)", M:to_on_off(M.status.switch_on), M.status.switch_name) +end + +function M:dot_3_txt() + return string.format("%s - throttle (%s) (inv: %s)" , M:to_on_off(self.status.motor_active), self.status.motor_channel_name, self.status.motor_channel_direction_inv) +end + + +-- function M.background(wgt) +-- -- update switch status +-- status.switch_on = getSwitchValue(wgt.options.arm_switch_id) + +-- self:updateMotorStatus(wgt) -- always after updateSwitchStatus + +-- -- update telemetry status +-- status.tele_is_available = wgt.tools.isTelemetryAvailable() + + +-- -- if status.switch_on==true then +-- -- log(string.format("arm_switch(%s)=ON", status.switch_name)) +-- -- else +-- -- log(string.format("arm_switch(%s)=OFF", status.switch_name)) +-- -- end + +-- end + +return M diff --git a/sdcard/c480x272/WIDGETS/Flights/rules/2_plane_no_tlm.lua b/sdcard/c480x272/WIDGETS/Flights/rules/2_plane_no_tlm.lua new file mode 100644 index 00000000..e8bfa93d --- /dev/null +++ b/sdcard/c480x272/WIDGETS/Flights/rules/2_plane_no_tlm.lua @@ -0,0 +1,69 @@ +local args = {...} +local m_log = args[1] +local app_name = args[2] +local switch_name = args[3] +local motor_channel_name = args[4] + +local info = [[ +Flight Rules +flight active when: +1. Arm switch ON +2. motor channel not idle +Notes: +no Telemetry needed +]] + +local FlightLogicRule = assert(loadScript("/WIDGETS/" .. app_name .. "/rules/FlightLogicRule", "btd"), "Failed to load trigger Base Class")(m_log, app_name, switch_name, motor_channel_name) + +local M = FlightLogicRule:new() + +local function log(fmt, ...) + FlightLogicRule:log(fmt, ...) +end + +function M:info() + return info +end + +function M:is_flight_starting() + if (M.status.switch_on == false) then + return false + end + if (M.status.motor_active == false) then + return false + end + + return true +end + +function M:is_still_on_flight() + if M.status.switch_on == false then + return false + end + if M.status.motor_active == false then + return false + end + return true +end + +function M:is_flight_ending() + return (M.status.switch_on == false) +end + +function M:is_dot_2() + return M.status.switch_on==true +end +function M:is_dot_3() + return M.status.motor_active==true +end + +function M:dot_2_txt() + return string.format("%s - arm_switch (%s)", M:to_on_off(M.status.switch_on), M.status.switch_name) +end + +function M:dot_3_txt() + return string.format("%s - throttle (%s) (inv: %s)" , M:to_on_off(M.status.motor_active), M.status.motor_channel_name, M.status.motor_channel_direction_inv) +end + + +return M diff --git a/sdcard/c480x272/WIDGETS/Flights/rules/3_heli_rpm.lua b/sdcard/c480x272/WIDGETS/Flights/rules/3_heli_rpm.lua new file mode 100644 index 00000000..1074a418 --- /dev/null +++ b/sdcard/c480x272/WIDGETS/Flights/rules/3_heli_rpm.lua @@ -0,0 +1,96 @@ +local args = {...} +local m_log = args[1] +local app_name = args[2] +local switch_name = args[3] +local motor_channel_name = args[4] + +local info = [[ +Flight Rules +flight active when: +1. Telemetry available +2. headspeed is above 500 rpm +Notes: +rpm sensor 'Hspd' is required +]] + +local FlightLogicRule = assert(loadScript("/WIDGETS/" .. app_name .. "/rules/FlightLogicRule", "btd"), "Failed to load trigger Base Class")(m_log, app_name, switch_name, motor_channel_name) + +local M = FlightLogicRule:new() + +local status_me = { + is_rpm_on = false, +} + +local function log(fmt, ...) + FlightLogicRule:log(fmt, ...) +end + +function M:info() + return info +end + +function M:is_flight_starting() + if (M.status.tele_is_available == false) then + return false + end + -- if (M.status.switch_on == false) then + -- return false + -- end + if (status_me.is_rpm_on == false) then + return false + end + + return true +end + +function M:is_still_on_flight() + -- if M.status.switch_on == false then + -- return false + -- end + if status_me.is_rpm_on == false then + return false + end + return true +end + +function M:is_flight_ending() + return (status_me.is_rpm_on == false) +end + +function M:is_dot_1() + return M.status.tele_is_available==true +end +-- function M:is_dot_2() +-- return M.status.switch_on==true +-- end +function M:is_dot_3() + return status_me.is_rpm_on==true +end + +function M:dot_1_txt() + return string.format("%s - telemetry", M:to_on_off(M.status.tele_is_available)) +end + +-- function M:dot_2_txt() +-- return string.format("%s - arm_switch (%s)", M:to_on_off(M.status.switch_on), M.status.switch_name) +-- end + +function M:dot_3_txt() + return string.format("%s - rpm_on (%s)" , M:to_on_off(status_me.is_rpm_on), "tlm: Hspd") +end + +function M:background(wgt) + + local hspd = getValue("Hspd") + -- local rpm = getValue("rpm") + + if hspd ~= nil then + status_me.is_rpm_on = (hspd > 500) + else + status_me.is_rpm_on = false + end + +end + + +return M diff --git a/sdcard/c480x272/WIDGETS/Flights/rules/4_heli_arm.lua b/sdcard/c480x272/WIDGETS/Flights/rules/4_heli_arm.lua new file mode 100644 index 00000000..a571f1fb --- /dev/null +++ b/sdcard/c480x272/WIDGETS/Flights/rules/4_heli_arm.lua @@ -0,0 +1,82 @@ +local args = {...} +local m_log = args[1] +local app_name = args[2] +local switch_name = args[3] +local motor_channel_name = args[4] + +local info = [[ +Flight Rules +flight active when: +1. Telemetry available +2. Arm switch ON +]] + +local FlightLogicRule = assert(loadScript("/WIDGETS/" .. app_name .. "/rules/FlightLogicRule", "btd"), "Failed to load trigger Base Class")(m_log, app_name, switch_name, motor_channel_name) + +local M = FlightLogicRule:new() + +local function log(fmt, ...) + FlightLogicRule:log(fmt, ...) +end + +function M:info() + return info +end + +function M:is_flight_starting() + if (M.status.tele_is_available == false) then + return false + end + if (M.status.switch_on == false) then + return false + end + + return true +end + +function M:is_still_on_flight() + if M.status.switch_on == false then + return false + end + return true +end + +function M:is_flight_ending() + return (M.status.tele_is_available == false) +end + +function M:is_dot_1() + return M.status.tele_is_available==true +end +function M:is_dot_2() + return M.status.switch_on==true +end + +function M:dot_1_txt() + return string.format("%s - telemetry", M:to_on_off(M.status.tele_is_available)) +end + +function M:dot_2_txt() + return string.format("%s - arm_switch (%s)", M:to_on_off(M.status.switch_on), M.status.switch_name) +end + + +-- function M.background(wgt) +-- -- update switch status +-- status.switch_on = getSwitchValue(wgt.options.arm_switch_id) + +-- self:updateMotorStatus(wgt) -- always after updateSwitchStatus + +-- -- update telemetry status +-- status.tele_is_available = wgt.tools.isTelemetryAvailable() + + +-- -- if status.switch_on==true then +-- -- log(string.format("arm_switch(%s)=ON", status.switch_name)) +-- -- else +-- -- log(string.format("arm_switch(%s)=OFF", status.switch_name)) +-- -- end + +-- end + +return M diff --git a/sdcard/c480x272/WIDGETS/Flights/rules/5_by_switch.lua b/sdcard/c480x272/WIDGETS/Flights/rules/5_by_switch.lua new file mode 100644 index 00000000..3c22585a --- /dev/null +++ b/sdcard/c480x272/WIDGETS/Flights/rules/5_by_switch.lua @@ -0,0 +1,49 @@ +local args = {...} +local m_log = args[1] +local app_name = args[2] +local switch_name = args[3] +local motor_channel_name = args[4] + +local info = [[ +Flight Rules +flight active when: +1. Arm switch ON +Notes: +only the switch control the flight +the switch can be physical / logical +]] + +local FlightLogicRule = assert(loadScript("/WIDGETS/" .. app_name .. "/rules/FlightLogicRule", "btd"), "Failed to load trigger Base Class")(m_log, app_name, switch_name, motor_channel_name) + +local M = FlightLogicRule:new() + +local function log(fmt, ...) + FlightLogicRule:log(fmt, ...) +end + +function M:info() + return info +end + +function M:is_flight_starting() + return (M.status.switch_on == true) +end + +function M:is_still_on_flight() + return (M.status.switch_on == true) +end + +function M:is_flight_ending() + return (M.status.switch_on == false) +end + +function M:is_dot_2() + return M.status.switch_on==true +end + +function M:dot_2_txt() + return string.format("%s - arm_switch (%s)", M:to_on_off(M.status.switch_on), M.status.switch_name) +end + + +return M diff --git a/sdcard/c480x272/WIDGETS/Flights/rules/6_dlg.lua b/sdcard/c480x272/WIDGETS/Flights/rules/6_dlg.lua new file mode 100644 index 00000000..7aa8846c --- /dev/null +++ b/sdcard/c480x272/WIDGETS/Flights/rules/6_dlg.lua @@ -0,0 +1,98 @@ +local args = {...} +local m_log = args[1] +local app_name = args[2] +local switch_name = args[3] +local motor_channel_name = args[4] + +local info = [[ +Flight Rules - DLG (Discus Launch Glider) +flight active when: +1. Telemetry available +2. Height increase detected (vario/altitude) +3. Vario Telemetry available +Notes: +- Flight starts when vario detect launch +- Flight ends when height below 3m +]] + +local FlightLogicRule = assert(loadScript("/WIDGETS/" .. app_name .. "/rules/FlightLogicRule", "btd"), "Failed to load trigger Base Class")(m_log, app_name, wgt_status) + +local M = FlightLogicRule:new() + +-- DLG specific variables +-- local height_threshold = 5 -- meters above starting height to consider flight active +-- local landing_threshold = 2 -- meters above starting height to consider landing + +local VSPEED_DETECT_THROW = 8 +-- local VSPEED_DETECT_THROW_DONE = 1 +-- local ALTITUDE_DETECT_ON_AIR = 20 -- meters, threshold to detect if we are on air +local ALTITUDE_DETECT_LANDING = 2 -- meters, threshold to start detect landing + +local status_me = { + is_throwing = false, + is_still_flying = false, + is_landing = false +} + +local function log(fmt, ...) + FlightLogicRule:log(fmt, ...) +end + +function M:info() + return info +end + +function M:is_flight_starting() + if (M.status.tele_is_available == false) then + return false + end + return status_me.is_throwing +end + +function M:is_still_on_flight() + return status_me.is_still_flying +end + +function M:is_flight_ending() + return status_me.is_landing +end + +function M:is_dot_1() + return M.status.tele_is_available==true +end + +function M:is_dot_2() + return self.status.is_throwing or self.status.is_still_flying +end + + +function M:dot_1_txt() + return string.format("%s - telemetry", M:to_on_off(M.status.tele_is_available)) +end + +function M:dot_2_txt() + return string.format("%s - vario", M:to_on_off(status_me.is_throwing or status_me.is_still_flying)) +end + +function M:override_min_flight_time() + return 1.0 +end + +function M:background(wgt) + + if (self.status.tele_is_available == false) then + status_me.is_throwing = false + status_me.is_still_flying = false + status_me.is_landing = false + return + end + + local alt = getValue("Alt") or 0 + local vspd = getValue("VSpd") or 0 + + status_me.is_throwing = (vspd > VSPEED_DETECT_THROW) + status_me.is_still_flying = (alt >= ALTITUDE_DETECT_LANDING) + status_me.is_landing = (alt < ALTITUDE_DETECT_LANDING) +end + +return M diff --git a/sdcard/c480x272/WIDGETS/Flights/rules/FlightLogicRule.lua b/sdcard/c480x272/WIDGETS/Flights/rules/FlightLogicRule.lua new file mode 100644 index 00000000..5cb7a47e --- /dev/null +++ b/sdcard/c480x272/WIDGETS/Flights/rules/FlightLogicRule.lua @@ -0,0 +1,146 @@ +local args = {...} +local m_log = args[1] +local app_name = args[2] +local switch_name = args[3] +local motor_channel_name = args[4] + +local default_min_motor_value = 200 + +local FlightLogicRule = { + m_log = m_log, + app_name = app_name, + + status = { + switch_on = nil, + switch_name = switch_name, + tele_is_available = nil, + motor_active = nil, + motor_channel_name = motor_channel_name, + motor_channel_direction_inv = nil, + } + + +} + +function FlightLogicRule:new (o) + o = o or {} + setmetatable(o, FlightLogicRule) + FlightLogicRule.__index = FlightLogicRule + return o +end + +function FlightLogicRule:log(fmt, ...) + FlightLogicRule.m_log.info(fmt, ...) +end + +FlightLogicRule.info = function() + return "I am trigger class" +end + +function FlightLogicRule:is_flight_starting() + return false +end + +function FlightLogicRule:is_still_on_flight() + return false +end + +function FlightLogicRule:is_flight_ending() + return false +end + +function FlightLogicRule:is_dot_1() + return true +end + +function FlightLogicRule:is_dot_2() + return true +end + +function FlightLogicRule:is_dot_3() + return true +end + +function FlightLogicRule:to_on_off(cond) + if cond then + return "ON" + else + return "OFF" + end +end + +function FlightLogicRule:dot_1_txt() + return "---" +end + +function FlightLogicRule:dot_2_txt() + return "---" +end + +function FlightLogicRule:dot_3_txt() + return "---" +end + +function FlightLogicRule:override_min_flight_time() + return nil +end + +function FlightLogicRule:updateMotorStatus(wgt) + -- -- for heli, if the motor-sw==switch-sw, then ignore motor direction detection + -- if (wgt.heli_mode == true) then + -- status.motor_active = status.switch_on + -- return + -- end + + local motor_value = getValue(wgt.options.motor_channel) + --log(string.format("motor_value (%s): %s", wgt.options.motor_channel, motor_value)) + + if (self.status.motor_channel_direction_inv == nil) then + -- detect motor channel direction + if (motor_value < (-1024 + default_min_motor_value)) then + self.status.motor_channel_direction_inv = false + elseif (motor_value > (1024 - default_min_motor_value)) then + self.status.motor_channel_direction_inv = true + else + -- still nil + return + end + end + + if (FlightLogicRule.status.motor_channel_direction_inv == false) then + -- non inverted mixer + if (motor_value > (-1024 + default_min_motor_value)) then + FlightLogicRule.status.motor_active = true + else + FlightLogicRule.status.motor_active = false + end + else + -- inverted mixer + if (motor_value < (1024 - default_min_motor_value)) then + FlightLogicRule.status.motor_active = true + else + FlightLogicRule.status.motor_active = false + end + end + +end + +function FlightLogicRule:background(wgt) + -- update switch status + FlightLogicRule.status.switch_on = getSwitchValue(wgt.options.arm_switch_id) + + self:updateMotorStatus(wgt) -- always after updateSwitchStatus + + -- update telemetry status + FlightLogicRule.status.tele_is_available = wgt.tools.isTelemetryAvailable() + + + -- if status.switch_on==true then + -- log(string.format("arm_switch(%s)=ON", status.switch_name)) + -- else + -- log(string.format("arm_switch(%s)=OFF", status.switch_name)) + -- end + +end + +return FlightLogicRule diff --git a/sdcard/c480x320/WIDGETS/Flights/app.lua b/sdcard/c480x320/WIDGETS/Flights/app.lua index 0780fa35..05ddae54 100644 --- a/sdcard/c480x320/WIDGETS/Flights/app.lua +++ b/sdcard/c480x320/WIDGETS/Flights/app.lua @@ -48,21 +48,26 @@ -- or -- flight_ended2.wav-->flight_ended.wav -- for Heli, the motor-switch=arm-switch (same value for both) --- if you need a reversed arm switch (e.g. !SF) you need to do change it (for now) in the script: inverted_arm_switch_logic=0 -- if you prefer different logics, do them on logical switches, and put that logical switch in both Arm & motor channel --- if your recweiver does not have telemetry, use_telemetry-->off +-- if your recweiver does not have telemetry, use: [Plane no Telemetry] ]] +local args = {...} +local triggerTypeDefs = args[1] + local app_name = "Flights" -local app_ver = "1.7" +local app_ver = "2.1" + +local lvSCALE = lvgl.LCD_SCALE or 1 +local is800 = (LCD_W==800) local build_ui = nil ------------------------------------------------------------------------------------------------------------------ -- configuration local default_flight_starting_duration = 30 -- 30 sec to detect flight success local default_flight_ending_duration = 8 -- 8 sec to detect flight ended -local default_min_motor_value = 200 +-- local default_min_motor_value = 200 local enable_count_announcement_on_start = 0 -- 0=no voice, 1=play the count upon increment local enable_count_announcement_on_end = 1 -- 0=no voice, 1=play the count upon end of flight local show_dots = true -- false=do not show dots, true=show dbg dots @@ -72,9 +77,7 @@ local use_flights_history = 1 -- 0=do not write flights-history, -- imports -local img = bitmap.open("/WIDGETS/" .. app_name .. "/logo.png") -local LibLogClass = loadScript("/WIDGETS/" .. app_name .. "/lib_log.lua", "btd") -local m_log = LibLogClass(app_name, "/WIDGETS/" .. app_name) +local m_log = assert(loadScript("/WIDGETS/" .. app_name .. "/lib_log.lua", "btd"))(app_name, "/WIDGETS/" .. app_name) -- better font size names local FS={FONT_38=XXLSIZE,FONT_16=DBLSIZE,FONT_12=MIDSIZE,FONT_8=0,FONT_6=SMLSIZE} @@ -87,21 +90,14 @@ local function update(wgt, options) if (wgt == nil) then return end wgt.options = options - wgt.use_telemetry = wgt.options.use_telemetry + wgt.triggerDesc = triggerTypeDefs.info[wgt.options.triggerType].desc + wgt.triggerFile = triggerTypeDefs.info[wgt.options.triggerType].file wgt.enable_sounds = wgt.options.enable_sounds - wgt.heli_mode = wgt.options.heli_mode == 1 - if (wgt.options.arm_switch_id == wgt.options.motor_channel) then - wgt.heli_mode = true - end -- status wgt.status = {} - wgt.status.switch_on = nil wgt.status.switch_name = nil - wgt.status.tele_is_available = nil - wgt.status.motor_active = nil wgt.status.motor_channel_name = nil - wgt.status.motor_channel_direction_inv = nil wgt.status.flight_state = "GROUND" wgt.status.duration_passed = 0 wgt.status.periodic1 = wgt.tools.periodicInit() @@ -110,11 +106,9 @@ local function update(wgt, options) wgt.status.flight_start_date_time = 0 wgt.status.flight_end_time = 0 wgt.status.flight_duration = 0 - wgt.status.ground_on_switch = false if (wgt.options.min_flight_duration < 0) then wgt.options.min_flight_duration = math.abs(wgt.options.min_flight_duration) - wgt.status.ground_on_switch = true default_flight_ending_duration = 1 end @@ -135,6 +129,19 @@ local function update(wgt, options) wgt.status.motor_channel_name = fi_mot.name end + local t_chunk = assert(loadScript("/WIDGETS/" .. app_name .. "/rules/" .. wgt.triggerFile, "btd"), "Failed to load trigger script: "..wgt.triggerFile) + wgt.rule = t_chunk(m_log, app_name, wgt.status.switch_name, wgt.status.motor_channel_name) + + log("Using trigger type: %s (%s)", wgt.triggerDesc, wgt.triggerFile) + log("info: %s", wgt.rule:info()) + + local override_min_flight_time = wgt.rule:override_min_flight_time() + if (override_min_flight_time ~= nil) then + wgt.options.min_flight_duration = override_min_flight_time + log("Using override min flight duration: %s", wgt.options.min_flight_duration) + end + + -- auto debug mode if widget size 1/2 or 1/1 wgt.options.is_debug = (wgt.options.auto_debug==1 and wgt.zone.h > 140) -- log("auto_debug: %s, is_debug: %s, wgt.zone.h: %s", wgt.options.auto_debug, wgt.options.is_debug, wgt.zone.h) @@ -148,8 +155,8 @@ local function update(wgt, options) end local ver, radio, maj, minor, rev, osname = getVersion() - wgt.is_valid_ver = (maj == 2 and minor >= 11) - + local nVer = maj*1000000 + minor*1000 + rev + wgt.is_valid_ver = (nVer>=2011000) build_ui(wgt) end @@ -157,18 +164,13 @@ end local function create(zone, options) local wgt = { zone = zone, - options = options - } - --wgt.options.use_days = wgt.options.use_days % 2 -- modulo due to bug that cause the value to be other than 0|1 - - -- imports - wgt.ToolsClass = loadScript("/WIDGETS/" .. app_name .. "/lib_widget_tools.lua", "btd") - wgt.tools = wgt.ToolsClass(m_log, app_name) + options = options, - wgt.FlightsHistoryClass = loadScript("/WIDGETS/" .. app_name .. "/lib_flights_history.lua", "btd") - wgt.flightHistory = wgt.FlightsHistoryClass(m_log, app_name) - wgt.FlightsCountClass = loadScript("/WIDGETS/" .. app_name .. "/lib_flights_count.lua", "btd") - wgt.flightCountHWriter = wgt.FlightsCountClass(m_log, app_name, "/flights-count.csv") + -- imports + tools = loadScript("/WIDGETS/" .. app_name .. "/lib_widget_tools.lua", "btd")(m_log, app_name), + flightHistory = loadScript("/WIDGETS/" .. app_name .. "/lib_flights_history.lua", "btd")(m_log, app_name), + -- flightCountHWriter = loadScript("/WIDGETS/" .. app_name .. "/lib_flights_count.lua", "btd")(m_log, app_name, "/flights-count.csv"), + } update(wgt, options) return wgt @@ -201,72 +203,6 @@ local function getFontSize(wgt, txt) return FS.FONT_6 end ---------------------------------------------------------------------------------------------------- - -local function updateTelemetryStatus(wgt) - if wgt.use_telemetry == 0 then - wgt.status.tele_is_available = true - else - wgt.status.tele_is_available = wgt.tools.isTelemetryAvailable() - end -end - -local function updateMotorStatus(wgt) - - -- for heli, if the motor-sw==switch-sw, then ignore motor direction detection - if (wgt.heli_mode == true) then - wgt.status.motor_active = wgt.status.switch_on - return - end - - local motor_value = getValue(wgt.options.motor_channel) - --log(string.format("motor_value (%s): %s", wgt.options.motor_channel, motor_value)) - - ---- if we do not have telemetry, then the battery is not connected yet, so we can detect yet motor channel direction - --if (wgt.status.tele_is_available == nil or wgt.status.tele_is_available == false) then - -- return - --end - - if (wgt.status.motor_channel_direction_inv == nil) then - -- detect motor channel direction - if (motor_value < (-1024 + default_min_motor_value)) then - wgt.status.motor_channel_direction_inv = false - elseif (motor_value > (1024 - default_min_motor_value)) then - wgt.status.motor_channel_direction_inv = true - else - -- still nil - return - end - end - - if (wgt.status.motor_channel_direction_inv == false) then - -- non inverted mixer - if (motor_value > (-1024 + default_min_motor_value)) then - wgt.status.motor_active = true - else - wgt.status.motor_active = false - end - else - -- inverted mixer - if (motor_value < (1024 - default_min_motor_value)) then - wgt.status.motor_active = true - else - wgt.status.motor_active = false - end - end - -end - -local function updateSwitchStatus(wgt) - wgt.status.switch_on = getSwitchValue(wgt.options.arm_switch_id) - - -- if wgt.status.switch_on==true then - -- log(string.format("arm_switch(%s)=ON", wgt.status.switch_name)) - -- else - -- log(string.format("arm_switch(%s)=OFF", wgt.status.switch_name)) - -- end -end - -------------------------------------------------------------------------------------------------------- -- get flight count local function getFlightCount(wgt) @@ -345,29 +281,12 @@ local function stateChange(wgt, newState, timer_sec) end end - -local function is_flight_starting(wgt) - if (wgt.status.motor_active == true) and (wgt.status.switch_on == true) and (wgt.use_telemetry==0 or wgt.status.tele_is_available == true) then - return true - else - return false - end -end - local function background(wgt) - - updateSwitchStatus(wgt) - - updateMotorStatus(wgt) -- always after updateSwitchStatus - - updateTelemetryStatus(wgt) - - --log(string.format("tele_is_available: %s", wgt.status.tele_is_available)) + wgt.rule:background(wgt) -- **** state: GROUND *** if wgt.status.flight_state == "GROUND" then - -- if (wgt.status.motor_active == true) and (wgt.status.switch_on == true) and (wgt.use_telemetry==0 or wgt.status.tele_is_available == true) then - if (is_flight_starting(wgt) == true) then + if (wgt.rule:is_flight_starting() == true) then stateChange(wgt, "FLIGHT_STARTING", wgt.options.min_flight_duration) wgt.status.last_flight_count = getFlightCount(wgt) wgt.status.flight_start_time = getTime() * 10 / 1000 @@ -376,11 +295,9 @@ local function background(wgt) end return - -- **** state: FLIGHT_STARTING *** + -- **** state: FLIGHT_STARTING *** elseif wgt.status.flight_state == "FLIGHT_STARTING" then - - -- if (wgt.status.motor_active == false) or (wgt.status.switch_on == false) or (wgt.use_telemetry==1 and wgt.status.tele_is_available == false) then - if (is_flight_starting(wgt) == false) then + if (wgt.rule:is_flight_starting() == false) then stateChange(wgt, "GROUND", 0) return end @@ -402,10 +319,11 @@ local function background(wgt) end return - -- **** state: FLIGHT_ON *** + -- **** state: FLIGHT_ON *** elseif wgt.status.flight_state == "FLIGHT_ON" then -- record flight duration in case we soon detect end of flight - if (wgt.status.motor_active == true) and (wgt.status.switch_on == true) then + + if wgt.rule:is_still_on_flight() then wgt.status.flight_end_time = getTime() * 10 / 1000 wgt.status.flight_duration = wgt.status.flight_end_time - wgt.status.flight_start_time -- log("flight_start_time: %s", wgt.status.flight_start_time) @@ -413,13 +331,7 @@ local function background(wgt) -- log("flight_duration: %s", wgt.status.flight_duration) end - -- if (wgt.status.ground_on_switch and wgt.status.switch_on == false) then - -- stateChange(wgt, "FLIGHT_ENDING", 0) - if (wgt.use_telemetry==1 and wgt.status.tele_is_available == false) or - (wgt.use_telemetry==0 and wgt.status.switch_on == false) - or - (wgt.status.ground_on_switch and wgt.status.switch_on == false) - then + if wgt.rule:is_flight_ending() then stateChange(wgt, "FLIGHT_ENDING", default_flight_ending_duration) local num_flights = getFlightCount(wgt) @@ -448,8 +360,7 @@ local function background(wgt) doEndOfFlightTasks(wgt) end - if (wgt.use_telemetry==1 and wgt.status.tele_is_available == true and wgt.status.ground_on_switch==false) or - (wgt.use_telemetry==0 and wgt.status.switch_on == true and wgt.status.ground_on_switch==false) then + if wgt.rule:is_flight_ending() == false then stateChange(wgt, "FLIGHT_ON", 0) end @@ -498,15 +409,15 @@ build_ui = function(wgt) local ts_w, ts_h = lcd.sizeText(num_flights, font_size) local dx = (zone_w - ts_w) / 2 - local dyh = 5 + local dyh = 5*lvSCALE local dy -- = header_h - 1 - local is_top_bar = (zone_h < 50) + local is_top_bar = (zone_h < 50*lvSCALE) if is_top_bar then -- force minimal spaces - dyh = -3 + dyh = -3*lvSCALE else - dyh = 5 + dyh = 5*lvSCALE end -- global @@ -519,41 +430,43 @@ build_ui = function(wgt) -- draw count if is_top_bar == true then -- pMain:label({x=zone_w-ts_w -10, y=dy, font=font_size, text=function() return getFlightCount(wgt) end, color=wgt.options.text_color}) - pMain:label({x=10, y=13, font=font_size, text=function() return getFlightCount(wgt) end, color=wgt.options.text_color}) + pMain:label({x=10*lvSCALE, y=13*lvSCALE, font=font_size, text=function() return getFlightCount(wgt) end, color=wgt.options.text_color}) else - pMain:label({x=zone_w-ts_w-5, y=5, font=font_size, text=function() return getFlightCount(wgt) end, color=wgt.options.text_color}) + pMain:label({x=zone_w-ts_w-5*lvSCALE, y=5*lvSCALE, font=font_size, text=function() return getFlightCount(wgt) end, color=wgt.options.text_color}) end - -- enable_dbg_dots + -- enable 3 dots if (show_dots == true) then - local dxc = 7 - pMain:circle({x=5, y=20 + dxc*0, radius=3, filled=true, color=function() return wgt.status.tele_is_available==true and GREEN or GREY end}) - pMain:circle({x=5, y=20 + dxc*1, radius=3, filled=true, color=function() return wgt.status.switch_on==true and GREEN or GREY end}) - pMain:circle({x=5, y=20 + dxc*2, radius=3, filled=true, color=function() return wgt.status.motor_active==true and GREEN or GREY end}) + local dxc = 7*lvSCALE + pMain:circle({x=5*lvSCALE, y=20*lvSCALE + dxc*0, radius=3, filled=true, color=function() return wgt.rule:is_dot_1() and GREEN or GREY end}) + pMain:circle({x=5*lvSCALE, y=20*lvSCALE + dxc*1, radius=3, filled=true, color=function() return wgt.rule:is_dot_2() and GREEN or GREY end}) + pMain:circle({x=5*lvSCALE, y=20*lvSCALE + dxc*2, radius=3, filled=true, color=function() return wgt.rule:is_dot_3() and GREEN or GREY end}) end -- debug local pInfo = lvgl.box({x=0, y=40}) if wgt.options.is_debug == true then - local dx = 15 - pInfo:label({x=dx, y=0, font=FS.FONT_6, text=function() return string.format("%s - telemetry", ternary(wgt.status.tele_is_available)) end}) - pInfo:label({x=dx, y=15, font=FS.FONT_6, text=function() return string.format("%s - arm_switch (%s)", ternary(wgt.status.switch_on), wgt.status.switch_name) end}) - pInfo:label({x=dx, y=30, font=FS.FONT_6, text=function() - if (wgt.heli_mode == false) then - return string.format("%s - throttle (%s) (inv: %s)" , ternary(wgt.status.motor_active), wgt.status.motor_channel_name, wgt.status.motor_channel_direction_inv) - else - return string.format("%s - heli mode arm (ignore throttle)", ternary(wgt.status.motor_active)) - end - end}) - pInfo:label({x=dx, y=45, font=FS.FONT_6, text=function() return string.format("timer: %.1f/%d", wgt.status.duration_passed / 1000, wgt.tools.getDurationMili(wgt.status.periodic1) / 1000) end}) - pInfo:label({x=dx, y=60, font=FS.FONT_6, text=function() return string.format("flight duration: %.1f", wgt.status.flight_duration) end}) - - pInfo:label({x=dx, y=85, font=FS.FONT_6, text="state:"}) - dx = 50 - pInfo:label({x=dx, y=85, font=FS.FONT_6, text="GROUND" , color=function() return getColorByState(wgt, "GROUND") end}) - pInfo:label({x=dx, y=100, font=FS.FONT_6, text="FLIGHT_STARTING", color=function() return getColorByState(wgt, "FLIGHT_STARTING") end}) - pInfo:label({x=dx, y=115, font=FS.FONT_6, text="FLIGHT_ON" , color=function() return getColorByState(wgt, "FLIGHT_ON") end}) - pInfo:label({x=dx, y=130, font=FS.FONT_6, text="FLIGHT_ENDING" , color=function() return getColorByState(wgt, "FLIGHT_ENDING") end}) + local dx = 15*lvSCALE + pInfo:label({x=dx+190*lvSCALE, y= 5*lvSCALE, font=FS.FONT_8, text=function() return string.format("Rule: %s", wgt.triggerDesc) end}) + pInfo:label({x=dx, y= 0*lvSCALE, font=FS.FONT_6, text=function() return wgt.rule:dot_1_txt() end}) + pInfo:label({x=dx, y=15*lvSCALE, font=FS.FONT_6, text=function() return wgt.rule:dot_2_txt() end}) + pInfo:label({x=dx, y=30*lvSCALE, font=FS.FONT_6, text=function() return wgt.rule:dot_3_txt() end}) + pInfo:label({x=dx, y=45*lvSCALE, font=FS.FONT_6, text=function() return string.format("timer: %.1f/%d", wgt.status.duration_passed / 1000, wgt.tools.getDurationMili(wgt.status.periodic1) / 1000) end}) + pInfo:label({x=dx, y=60*lvSCALE, font=FS.FONT_6, text=function() return string.format("flight duration: %.1f", wgt.status.flight_duration) end}) + + pInfo:label({x=dx, y=80*lvSCALE, font=FS.FONT_6, text="state:"}) + dx = 50*lvSCALE + pInfo:label({x=dx, y= 80*lvSCALE, font=FS.FONT_6, text="GROUND" , color=function() return getColorByState(wgt, "GROUND") end}) + pInfo:label({x=dx, y= 95*lvSCALE, font=FS.FONT_6, text="FLIGHT_STARTING", color=function() return getColorByState(wgt, "FLIGHT_STARTING") end}) + pInfo:label({x=dx, y=110*lvSCALE, font=FS.FONT_6, text="FLIGHT_ON" , color=function() return getColorByState(wgt, "FLIGHT_ON") end}) + pInfo:label({x=dx, y=125*lvSCALE, font=FS.FONT_6, text="FLIGHT_ENDING" , color=function() return getColorByState(wgt, "FLIGHT_ENDING") end}) + + pInfo:label({x= 5*lvSCALE, y=145*lvSCALE, font=FS.FONT_6, text=function() return string.format("starting:\n %s", wgt.rule.is_flight_starting()) end}) + pInfo:label({x= 70*lvSCALE, y=145*lvSCALE, font=FS.FONT_6, text=function() return string.format("on_flight:\n %s", wgt.rule.is_still_on_flight()) end}) + pInfo:label({x=140*lvSCALE, y=145*lvSCALE, font=FS.FONT_6, text=function() return string.format("ending:\n %s", wgt.rule.is_flight_ending()) end}) + + pInfo:rectangle({x=dx+160*lvSCALE, y=30*lvSCALE, w=260*lvSCALE, h=140*lvSCALE, color=LIGHTBLUE, filled=true, rounded=5}) + pInfo:label({x=dx+(160+10)*lvSCALE, y=40*lvSCALE, font=FS.FONT_6, text=function() return wgt.rule:info() end, color=WHITE}) end end @@ -561,11 +474,4 @@ local function refresh(wgt, event, touchState) background(wgt) end -return { - name = app_name, - create = create, - update = update, - refresh = refresh, - background = background, - useLvgl=true -} +return {name=app_name, create=create, update=update, refresh=refresh, background=background, useLvgl=true} diff --git a/sdcard/c480x320/WIDGETS/Flights/lib_log.lua b/sdcard/c480x320/WIDGETS/Flights/lib_log.lua index c23e6e8a..7a933a01 100644 --- a/sdcard/c480x320/WIDGETS/Flights/lib_log.lua +++ b/sdcard/c480x320/WIDGETS/Flights/lib_log.lua @@ -2,6 +2,7 @@ local app_name, script_dir = ... local ENABLE_LOG_TO_CONSOLE = true local ENABLE_LOG_TO_FILE = false +local ENABLE_LOG_TO_SERIAL = false local M = {} @@ -17,6 +18,7 @@ local log = { outfile = script_dir .. "/app.log", enable_file = ENABLE_LOG_TO_FILE, enable_console = ENABLE_LOG_TO_CONSOLE and is_simulator(), + enable_serial_dbg = ENABLE_LOG_TO_SERIAL, current_level = nil, -- func @@ -61,7 +63,7 @@ local function tostring(...) end function M.do_log(iLevel, ulevel, fmt, ...) - if log.enable_console == false and log.enable_file == false then + if log.enable_console == false and log.enable_file == false and log.enable_serial_dbg == false then return end @@ -91,6 +93,12 @@ function M.do_log(iLevel, ulevel, fmt, ...) io.write(fp, msg2 .. "\n") io.close(fp) end + + -- Output to log file + if log.enable_serial_dbg == true then + serialWrite(msg2.."\r\n") -- 115200 bps + end + end function M.trace(fmt, ...) diff --git a/sdcard/c480x320/WIDGETS/Flights/lib_widget_tools.lua b/sdcard/c480x320/WIDGETS/Flights/lib_widget_tools.lua index 1e419474..738daade 100644 --- a/sdcard/c480x320/WIDGETS/Flights/lib_widget_tools.lua +++ b/sdcard/c480x320/WIDGETS/Flights/lib_widget_tools.lua @@ -1,4 +1,6 @@ -local m_log, app_name = ... +local args = {...} +local m_log = args[1] +local app_name = args[2] local M = {} M.m_log = m_log @@ -9,18 +11,20 @@ M.tele_src_id = nil local getTime = getTime local lcd = lcd --- better font names -local FONT_38 = XXLSIZE -- 38px -local FONT_16 = DBLSIZE -- 16px -local FONT_12 = MIDSIZE -- 12px -local FONT_8 = 0 -- Default 8px -local FONT_6 = SMLSIZE -- 6px +-- better font size names +local FS={FONT_38=XXLSIZE,FONT_16=DBLSIZE,FONT_12=MIDSIZE,FONT_8=0,FONT_6=SMLSIZE} +M.FS = FS +M.FONT_LIST = {FS.FONT_6, FS.FONT_8, FS.FONT_12, FS.FONT_16, FS.FONT_38} +local lvSCALE = lvgl.LCD_SCALE or 1 -local FONT_LIST = {FONT_6, FONT_8, FONT_12, FONT_16, FONT_38} --------------------------------------------------------------------------------------------------- local function log(fmt, ...) - m_log.info(fmt, ...) + if M.m_log then + M.m_log.info(fmt, ...) + else + print("[" .. M.app_name .. "] " .. string.format(fmt, ...)) + end end --------------------------------------------------------------------------------------------------- @@ -55,9 +59,10 @@ end --------------------------------------------------------------------------------------------------- function M.periodicInit() - local t = {} - t.startTime = -1; - t.durationMili = -1; + local t = { + startTime = -1, + durationMili = -1 + } return t end @@ -66,6 +71,10 @@ function M.periodicStart(t, durationMili) t.durationMili = durationMili; end +function M.periodicStop(t) + t.durationMili = -1; +end + function M.periodicHasPassed(t, show_log) -- not started yet if (t.durationMili <= 0) then @@ -110,6 +119,43 @@ function M.isTelemetryAvailable() return is_telem > 0 end +function M.isTelemetryAvailableOld() + -- select telemetry source + if not M.tele_src_id then + --log("select telemetry source") + local tele_src = getFieldInfo("RSSI") + if not tele_src then tele_src = getFieldInfo("1RSS") end + if not tele_src then tele_src = getFieldInfo("2RSS") end + if not tele_src then tele_src = getFieldInfo("RQly") end + if not tele_src then tele_src = getFieldInfo("VFR%") end + if not tele_src then tele_src = getFieldInfo("VFR") end + if not tele_src then tele_src = getFieldInfo("TRSS") end + if not tele_src then tele_src = getFieldInfo("RxBt") end + if not tele_src then tele_src = getFieldInfo("A1") end + + if tele_src == nil then + --log("no telemetry sensor found") + M.tele_src_id = nil + M.tele_src_name = "---" + return false + else + --log("telemetry sensor found: " .. tele_src.name) + M.tele_src_id = tele_src.id + M.tele_src_name = tele_src.name + end + end + + if M.tele_src_id == nil then + return false + end + + local rx_val = getValue(M.tele_src_id) + if rx_val ~= 0 then + return true + end + return false +end + --------------------------------------------------------------------------------------------------- -- workaround to detect telemetry-reset event, until a proper implementation on the lua interface will be created @@ -120,7 +166,6 @@ end -- on event detection, the function onTelemetryResetEvent() will be trigger -- function M.detectResetEvent(wgt, callback_onTelemetryResetEvent) - local currMinRSSI = getValue('RSSI-') if (currMinRSSI == nil) then log("telemetry reset event: can not be calculated") @@ -160,8 +205,8 @@ function M.getSensorInfoByName(sensorName) s1.type = s2.type --name (string) Name s1.name = s2.name - --unit (number) See list of units in the appendix of the OpenTX Lua Reference Guide - s1.unit = s2.unit + --unit (number->string) See list of units in the appendix of the OpenTX Lua Reference Guide + s1.unit = M.unitIdToString(s2.unit) --prec (number) Number of decimals s1.prec = s2.prec --id (number) Only custom sensors @@ -213,9 +258,10 @@ function M.isSensorExist(sensorName) end --------------------------------------------------------------------------------------------------- --- workaround for bug in getFiledInfo() -- ???? why? +-- workaround for bug in getFiledInfo() why? function M.cleanInvalidCharFromGetFiledInfo(sourceName) - if string.byte(string.sub(sourceName, 1, 1)) > 127 then + + if string.byte(string.sub(sourceName, 1, 1)) > 127 then sourceName = string.sub(sourceName, 2, -1) end if string.byte(string.sub(sourceName, 1, 1)) > 127 then @@ -235,35 +281,97 @@ function M.getSourceNameCleaned(source) end ------------------------------------------------------------------------------------------------------ + function M.getFontSizeRelative(orgFontSize, delta) - for i = 1, #FONT_LIST do - if FONT_LIST[i] == orgFontSize then + for i = 1, #M.FONT_LIST do + if M.FONT_LIST[i] == orgFontSize then local newIndex = i + delta - newIndex = math.min(newIndex, #FONT_LIST) + newIndex = math.min(newIndex, #M.FONT_LIST) newIndex = math.max(newIndex, 1) - return FONT_LIST[newIndex] + return M.FONT_LIST[newIndex] end end return orgFontSize end +function M.getFontIndex(fontSize, defaultFontSize) + for i = 1, #M.FONT_LIST do + -- log("M.FONT_SIZES[%d]: %d (%d)", i, M.FONT_LIST[i], fontSize) + if M.FONT_LIST[i] == fontSize then + return i + end + end + return defaultFontSize +end + ------------------------------------------------------------------------------------------------------ + function M.lcdSizeTextFixed(txt, font_size) local ts_w, ts_h = lcd.sizeText(txt, font_size) local v_offset = 0 - if font_size == FONT_38 then - v_offset = -15 - elseif font_size == FONT_16 then - v_offset = -8 - elseif font_size == FONT_12 then - v_offset = -6 - elseif font_size == FONT_8 then - v_offset = -4 - elseif font_size == FONT_6 then - v_offset = -3 + if font_size == FS.FONT_38 then + v_offset = -6*lvSCALE + ts_h = 52*lvSCALE + ts_w=ts_w-3 + elseif font_size == FS.FONT_16 then + v_offset = -6*lvSCALE + ts_h = 28*lvSCALE + elseif font_size == FS.FONT_12 then + v_offset = -5*lvSCALE + ts_h = 20*lvSCALE + elseif font_size == FS.FONT_8 then + v_offset = -3*lvSCALE + ts_h = 15*lvSCALE + elseif font_size == FS.FONT_6 then + v_offset = -2*lvSCALE + ts_h = 14*lvSCALE + end + return ts_w, ts_h, v_offset +end + +function M.getFontSize(wgt, txt, max_w, max_h, max_font_size) + local maxFontIndex = M.getFontIndex(max_font_size, nil) + --log("getFontSize() [%s] %dx%d (maxIndex: %d)", txt, max_w, max_h, maxFontIndex) + + if maxFontIndex>=5 then + local w, h, v_offset = M.lcdSizeTextFixed(txt, FS.FONT_38) + if w <= max_w and h <= max_h then + log("[%s] FS.FONT_38 %dx%d", txt, w, h) + return FS.FONT_38, w, h, v_offset + else + log("[%s] FS.FONT_38 %dx%d (too small)", txt, w, h) + end + end + + local w, h, v_offset + if maxFontIndex>=4 then + w, h, v_offset = M.lcdSizeTextFixed(txt, FS.FONT_16) + if w <= max_w and h <= max_h then + -- log("[%s] FS.FONT_16 %dx%d", txt, w, h, txt) + return FS.FONT_16, w, h, v_offset + end + end + + if maxFontIndex>=3 then + w, h, v_offset = M.lcdSizeTextFixed(txt, FS.FONT_12) + if w <= max_w and h <= max_h then + -- log("[%s] FS.FONT_12 %dx%d", txt, w, h, txt) + return FS.FONT_12, w, h, v_offset + end end - return ts_w, ts_h +2*v_offset, v_offset + + if maxFontIndex>=2 then + w, h, v_offset = M.lcdSizeTextFixed(txt, FS.FONT_8) + if w <= max_w and h <= max_h then + -- log("[%s] FS.FONT_8 %dx%d", txt, w, h, txt) + return FS.FONT_8, w, h, v_offset + end + end + + w, h, v_offset = M.lcdSizeTextFixed(txt, FS.FONT_6) + -- log("[%s] FS.FONT_6 %dx%d", txt, w, h, txt) + return FS.FONT_6, w, h, v_offset end ------------------------------------------------------------------------------------------------------ diff --git a/sdcard/c480x320/WIDGETS/Flights/main.lua b/sdcard/c480x320/WIDGETS/Flights/main.lua index 92d8f3ec..4051773c 100644 --- a/sdcard/c480x320/WIDGETS/Flights/main.lua +++ b/sdcard/c480x320/WIDGETS/Flights/main.lua @@ -4,6 +4,27 @@ local tool = nil local default_flight_starting_duration = 30 -- 30 sec to detect flight success +local triggerTypeDefs = { + labels = { + "1. Plane with Telemetry", + "2. Plane no Telemetry", + "3. Heli [RPM]", + "4. Heli [Arm]", + "5. By switch", + "6. DLG [Height]", + "7. Glider [Height]", + }, + info = { + {desc = "1=Plane with Telemetry (mot+Arm+Telm)", file = "1_plane_tlm.lua" }, + {desc = "2=Plane no Telemetry (mot+Arm)", file = "2_plane_no_tlm.lua" }, + {desc = "3=Heli [RPM] (RPM+Telm)", file = "3_heli_rpm.lua" }, + {desc = "4=Heli [Arm] (Arm+Telm)", file = "4_heli_arm.lua" }, + {desc = "5=By switch", file = "5_by_switch.lua" }, + {desc = "6=DLG [Vario] (Height)", file = "6_dlg.lua" }, + {desc = "7=Glider [Vario] (mot+Arm+Height)", file = "7_glider.lua" }, + } +} + -- for backward compatibility local function getSwitchIds(key) local OS_SWITCH_ID = { @@ -21,27 +42,23 @@ end local DEFAULT_MOTOR_CHANNEL_ID = getSourceIndex("CH3") or getSourceIndex("thr111") or getSwitchIds("CH3") -- motor_channel=CH3 local options = { + { "triggerType" , CHOICE, 1 , triggerTypeDefs.labels}, { "arm_switch_id" , SWITCH, "SF"..CHAR_UP}, -- CHAR_UP|-|CHAR_DOWN { "motor_channel" , SOURCE, DEFAULT_MOTOR_CHANNEL_ID }, - { "heli_mode" , BOOL, 0}, -- ignore motor direction detection, and throttle position { "text_color" , COLOR, YELLOW},--, COLOR_THEME_PRIMARY2 }, { "min_flight_duration" , VALUE, default_flight_starting_duration, -30, 120 }, { "enable_sounds" , BOOL, 1}, -- 0=no sound, 1=play blip sound on increment & on flight end - { "use_telemetry" , BOOL, 1}, -- 0=do not use telemetry, 1=use telemetry in state machine { "auto_debug" , BOOL, 1}, -- show debug status on screen if widget is large enough - -- { "ground_on_switch" , BOOL, 0}, -- 0=auto detect ground by time, 1=ground on switch is used (not auto) } local function translate(name) local translations = { arm_switch_id="Arm Switch Position", motor_channel="Motor Channel", - heli_mode="Heli mode (ignore motor ch)", min_flight_duration = "Min flight duration (sec)", text_color = "Text color", enable_sounds = "Enable sounds", - use_telemetry = "Use telemetry", - -- ground_on_switch = "Ground on switch", + triggerType = "Type", auto_debug = "Auto debug", } return translations[name] @@ -50,7 +67,7 @@ end local function create(zone, options) -- print(string.format("1111 Flights create: %s", name)) - tool = assert(loadScript("/WIDGETS/"..app_name.."/app.lua", "btd"))() + tool = assert(loadScript("/WIDGETS/"..app_name.."/app.lua", "btd"))(triggerTypeDefs) return tool.create(zone, options) end local function update(wgt, options) return tool.update(wgt, options) end diff --git a/sdcard/c480x320/WIDGETS/Flights/rules/1_plane_tlm.lua b/sdcard/c480x320/WIDGETS/Flights/rules/1_plane_tlm.lua new file mode 100644 index 00000000..a902dd77 --- /dev/null +++ b/sdcard/c480x320/WIDGETS/Flights/rules/1_plane_tlm.lua @@ -0,0 +1,96 @@ +local args = {...} +local m_log = args[1] +local app_name = args[2] +local switch_name = args[3] +local motor_channel_name = args[4] + +local info = [[ +Flight Rules +flight active when: +1. Telemetry available +2. Arm switch ON +3. motor channel not idle +]] + +local FlightLogicRule = assert(loadScript("/WIDGETS/" .. app_name .. "/rules/FlightLogicRule", "btd"), "Failed to load trigger Base Class")(m_log, app_name, switch_name, motor_channel_name) + +local M = FlightLogicRule:new() + +local function log(fmt, ...) + FlightLogicRule:log(fmt, ...) +end + +function M:info() + return info +end + +function M:is_flight_starting() + if (M.status.tele_is_available == false) then + return false + end + if (M.status.switch_on == false) then + return false + end + if (M.status.motor_active == false) then + return false + end + + return true +end + +function M:is_still_on_flight() + if M.status.switch_on == false then + return false + end + if M.status.motor_active == false then + return false + end + return true +end + +function M:is_flight_ending() + return (M.status.tele_is_available == false) +end + +function M:is_dot_1() + return M.status.tele_is_available==true +end +function M:is_dot_2() + return M.status.switch_on==true +end +function M:is_dot_3() + return M.status.motor_active==true +end + +function M:dot_1_txt() + return string.format("%s - telemetry", M:to_on_off(M.status.tele_is_available)) +end + +function M:dot_2_txt() + return string.format("%s - arm_switch (%s)", M:to_on_off(M.status.switch_on), M.status.switch_name) +end + +function M:dot_3_txt() + return string.format("%s - throttle (%s) (inv: %s)" , M:to_on_off(self.status.motor_active), self.status.motor_channel_name, self.status.motor_channel_direction_inv) +end + + +-- function M.background(wgt) +-- -- update switch status +-- status.switch_on = getSwitchValue(wgt.options.arm_switch_id) + +-- self:updateMotorStatus(wgt) -- always after updateSwitchStatus + +-- -- update telemetry status +-- status.tele_is_available = wgt.tools.isTelemetryAvailable() + + +-- -- if status.switch_on==true then +-- -- log(string.format("arm_switch(%s)=ON", status.switch_name)) +-- -- else +-- -- log(string.format("arm_switch(%s)=OFF", status.switch_name)) +-- -- end + +-- end + +return M diff --git a/sdcard/c480x320/WIDGETS/Flights/rules/2_plane_no_tlm.lua b/sdcard/c480x320/WIDGETS/Flights/rules/2_plane_no_tlm.lua new file mode 100644 index 00000000..e8bfa93d --- /dev/null +++ b/sdcard/c480x320/WIDGETS/Flights/rules/2_plane_no_tlm.lua @@ -0,0 +1,69 @@ +local args = {...} +local m_log = args[1] +local app_name = args[2] +local switch_name = args[3] +local motor_channel_name = args[4] + +local info = [[ +Flight Rules +flight active when: +1. Arm switch ON +2. motor channel not idle +Notes: +no Telemetry needed +]] + +local FlightLogicRule = assert(loadScript("/WIDGETS/" .. app_name .. "/rules/FlightLogicRule", "btd"), "Failed to load trigger Base Class")(m_log, app_name, switch_name, motor_channel_name) + +local M = FlightLogicRule:new() + +local function log(fmt, ...) + FlightLogicRule:log(fmt, ...) +end + +function M:info() + return info +end + +function M:is_flight_starting() + if (M.status.switch_on == false) then + return false + end + if (M.status.motor_active == false) then + return false + end + + return true +end + +function M:is_still_on_flight() + if M.status.switch_on == false then + return false + end + if M.status.motor_active == false then + return false + end + return true +end + +function M:is_flight_ending() + return (M.status.switch_on == false) +end + +function M:is_dot_2() + return M.status.switch_on==true +end +function M:is_dot_3() + return M.status.motor_active==true +end + +function M:dot_2_txt() + return string.format("%s - arm_switch (%s)", M:to_on_off(M.status.switch_on), M.status.switch_name) +end + +function M:dot_3_txt() + return string.format("%s - throttle (%s) (inv: %s)" , M:to_on_off(M.status.motor_active), M.status.motor_channel_name, M.status.motor_channel_direction_inv) +end + + +return M diff --git a/sdcard/c480x320/WIDGETS/Flights/rules/3_heli_rpm.lua b/sdcard/c480x320/WIDGETS/Flights/rules/3_heli_rpm.lua new file mode 100644 index 00000000..1074a418 --- /dev/null +++ b/sdcard/c480x320/WIDGETS/Flights/rules/3_heli_rpm.lua @@ -0,0 +1,96 @@ +local args = {...} +local m_log = args[1] +local app_name = args[2] +local switch_name = args[3] +local motor_channel_name = args[4] + +local info = [[ +Flight Rules +flight active when: +1. Telemetry available +2. headspeed is above 500 rpm +Notes: +rpm sensor 'Hspd' is required +]] + +local FlightLogicRule = assert(loadScript("/WIDGETS/" .. app_name .. "/rules/FlightLogicRule", "btd"), "Failed to load trigger Base Class")(m_log, app_name, switch_name, motor_channel_name) + +local M = FlightLogicRule:new() + +local status_me = { + is_rpm_on = false, +} + +local function log(fmt, ...) + FlightLogicRule:log(fmt, ...) +end + +function M:info() + return info +end + +function M:is_flight_starting() + if (M.status.tele_is_available == false) then + return false + end + -- if (M.status.switch_on == false) then + -- return false + -- end + if (status_me.is_rpm_on == false) then + return false + end + + return true +end + +function M:is_still_on_flight() + -- if M.status.switch_on == false then + -- return false + -- end + if status_me.is_rpm_on == false then + return false + end + return true +end + +function M:is_flight_ending() + return (status_me.is_rpm_on == false) +end + +function M:is_dot_1() + return M.status.tele_is_available==true +end +-- function M:is_dot_2() +-- return M.status.switch_on==true +-- end +function M:is_dot_3() + return status_me.is_rpm_on==true +end + +function M:dot_1_txt() + return string.format("%s - telemetry", M:to_on_off(M.status.tele_is_available)) +end + +-- function M:dot_2_txt() +-- return string.format("%s - arm_switch (%s)", M:to_on_off(M.status.switch_on), M.status.switch_name) +-- end + +function M:dot_3_txt() + return string.format("%s - rpm_on (%s)" , M:to_on_off(status_me.is_rpm_on), "tlm: Hspd") +end + +function M:background(wgt) + + local hspd = getValue("Hspd") + -- local rpm = getValue("rpm") + + if hspd ~= nil then + status_me.is_rpm_on = (hspd > 500) + else + status_me.is_rpm_on = false + end + +end + + +return M diff --git a/sdcard/c480x320/WIDGETS/Flights/rules/4_heli_arm.lua b/sdcard/c480x320/WIDGETS/Flights/rules/4_heli_arm.lua new file mode 100644 index 00000000..a571f1fb --- /dev/null +++ b/sdcard/c480x320/WIDGETS/Flights/rules/4_heli_arm.lua @@ -0,0 +1,82 @@ +local args = {...} +local m_log = args[1] +local app_name = args[2] +local switch_name = args[3] +local motor_channel_name = args[4] + +local info = [[ +Flight Rules +flight active when: +1. Telemetry available +2. Arm switch ON +]] + +local FlightLogicRule = assert(loadScript("/WIDGETS/" .. app_name .. "/rules/FlightLogicRule", "btd"), "Failed to load trigger Base Class")(m_log, app_name, switch_name, motor_channel_name) + +local M = FlightLogicRule:new() + +local function log(fmt, ...) + FlightLogicRule:log(fmt, ...) +end + +function M:info() + return info +end + +function M:is_flight_starting() + if (M.status.tele_is_available == false) then + return false + end + if (M.status.switch_on == false) then + return false + end + + return true +end + +function M:is_still_on_flight() + if M.status.switch_on == false then + return false + end + return true +end + +function M:is_flight_ending() + return (M.status.tele_is_available == false) +end + +function M:is_dot_1() + return M.status.tele_is_available==true +end +function M:is_dot_2() + return M.status.switch_on==true +end + +function M:dot_1_txt() + return string.format("%s - telemetry", M:to_on_off(M.status.tele_is_available)) +end + +function M:dot_2_txt() + return string.format("%s - arm_switch (%s)", M:to_on_off(M.status.switch_on), M.status.switch_name) +end + + +-- function M.background(wgt) +-- -- update switch status +-- status.switch_on = getSwitchValue(wgt.options.arm_switch_id) + +-- self:updateMotorStatus(wgt) -- always after updateSwitchStatus + +-- -- update telemetry status +-- status.tele_is_available = wgt.tools.isTelemetryAvailable() + + +-- -- if status.switch_on==true then +-- -- log(string.format("arm_switch(%s)=ON", status.switch_name)) +-- -- else +-- -- log(string.format("arm_switch(%s)=OFF", status.switch_name)) +-- -- end + +-- end + +return M diff --git a/sdcard/c480x320/WIDGETS/Flights/rules/5_by_switch.lua b/sdcard/c480x320/WIDGETS/Flights/rules/5_by_switch.lua new file mode 100644 index 00000000..3c22585a --- /dev/null +++ b/sdcard/c480x320/WIDGETS/Flights/rules/5_by_switch.lua @@ -0,0 +1,49 @@ +local args = {...} +local m_log = args[1] +local app_name = args[2] +local switch_name = args[3] +local motor_channel_name = args[4] + +local info = [[ +Flight Rules +flight active when: +1. Arm switch ON +Notes: +only the switch control the flight +the switch can be physical / logical +]] + +local FlightLogicRule = assert(loadScript("/WIDGETS/" .. app_name .. "/rules/FlightLogicRule", "btd"), "Failed to load trigger Base Class")(m_log, app_name, switch_name, motor_channel_name) + +local M = FlightLogicRule:new() + +local function log(fmt, ...) + FlightLogicRule:log(fmt, ...) +end + +function M:info() + return info +end + +function M:is_flight_starting() + return (M.status.switch_on == true) +end + +function M:is_still_on_flight() + return (M.status.switch_on == true) +end + +function M:is_flight_ending() + return (M.status.switch_on == false) +end + +function M:is_dot_2() + return M.status.switch_on==true +end + +function M:dot_2_txt() + return string.format("%s - arm_switch (%s)", M:to_on_off(M.status.switch_on), M.status.switch_name) +end + + +return M diff --git a/sdcard/c480x320/WIDGETS/Flights/rules/6_dlg.lua b/sdcard/c480x320/WIDGETS/Flights/rules/6_dlg.lua new file mode 100644 index 00000000..7aa8846c --- /dev/null +++ b/sdcard/c480x320/WIDGETS/Flights/rules/6_dlg.lua @@ -0,0 +1,98 @@ +local args = {...} +local m_log = args[1] +local app_name = args[2] +local switch_name = args[3] +local motor_channel_name = args[4] + +local info = [[ +Flight Rules - DLG (Discus Launch Glider) +flight active when: +1. Telemetry available +2. Height increase detected (vario/altitude) +3. Vario Telemetry available +Notes: +- Flight starts when vario detect launch +- Flight ends when height below 3m +]] + +local FlightLogicRule = assert(loadScript("/WIDGETS/" .. app_name .. "/rules/FlightLogicRule", "btd"), "Failed to load trigger Base Class")(m_log, app_name, wgt_status) + +local M = FlightLogicRule:new() + +-- DLG specific variables +-- local height_threshold = 5 -- meters above starting height to consider flight active +-- local landing_threshold = 2 -- meters above starting height to consider landing + +local VSPEED_DETECT_THROW = 8 +-- local VSPEED_DETECT_THROW_DONE = 1 +-- local ALTITUDE_DETECT_ON_AIR = 20 -- meters, threshold to detect if we are on air +local ALTITUDE_DETECT_LANDING = 2 -- meters, threshold to start detect landing + +local status_me = { + is_throwing = false, + is_still_flying = false, + is_landing = false +} + +local function log(fmt, ...) + FlightLogicRule:log(fmt, ...) +end + +function M:info() + return info +end + +function M:is_flight_starting() + if (M.status.tele_is_available == false) then + return false + end + return status_me.is_throwing +end + +function M:is_still_on_flight() + return status_me.is_still_flying +end + +function M:is_flight_ending() + return status_me.is_landing +end + +function M:is_dot_1() + return M.status.tele_is_available==true +end + +function M:is_dot_2() + return self.status.is_throwing or self.status.is_still_flying +end + + +function M:dot_1_txt() + return string.format("%s - telemetry", M:to_on_off(M.status.tele_is_available)) +end + +function M:dot_2_txt() + return string.format("%s - vario", M:to_on_off(status_me.is_throwing or status_me.is_still_flying)) +end + +function M:override_min_flight_time() + return 1.0 +end + +function M:background(wgt) + + if (self.status.tele_is_available == false) then + status_me.is_throwing = false + status_me.is_still_flying = false + status_me.is_landing = false + return + end + + local alt = getValue("Alt") or 0 + local vspd = getValue("VSpd") or 0 + + status_me.is_throwing = (vspd > VSPEED_DETECT_THROW) + status_me.is_still_flying = (alt >= ALTITUDE_DETECT_LANDING) + status_me.is_landing = (alt < ALTITUDE_DETECT_LANDING) +end + +return M diff --git a/sdcard/c480x320/WIDGETS/Flights/rules/FlightLogicRule.lua b/sdcard/c480x320/WIDGETS/Flights/rules/FlightLogicRule.lua new file mode 100644 index 00000000..5cb7a47e --- /dev/null +++ b/sdcard/c480x320/WIDGETS/Flights/rules/FlightLogicRule.lua @@ -0,0 +1,146 @@ +local args = {...} +local m_log = args[1] +local app_name = args[2] +local switch_name = args[3] +local motor_channel_name = args[4] + +local default_min_motor_value = 200 + +local FlightLogicRule = { + m_log = m_log, + app_name = app_name, + + status = { + switch_on = nil, + switch_name = switch_name, + tele_is_available = nil, + motor_active = nil, + motor_channel_name = motor_channel_name, + motor_channel_direction_inv = nil, + } + + +} + +function FlightLogicRule:new (o) + o = o or {} + setmetatable(o, FlightLogicRule) + FlightLogicRule.__index = FlightLogicRule + return o +end + +function FlightLogicRule:log(fmt, ...) + FlightLogicRule.m_log.info(fmt, ...) +end + +FlightLogicRule.info = function() + return "I am trigger class" +end + +function FlightLogicRule:is_flight_starting() + return false +end + +function FlightLogicRule:is_still_on_flight() + return false +end + +function FlightLogicRule:is_flight_ending() + return false +end + +function FlightLogicRule:is_dot_1() + return true +end + +function FlightLogicRule:is_dot_2() + return true +end + +function FlightLogicRule:is_dot_3() + return true +end + +function FlightLogicRule:to_on_off(cond) + if cond then + return "ON" + else + return "OFF" + end +end + +function FlightLogicRule:dot_1_txt() + return "---" +end + +function FlightLogicRule:dot_2_txt() + return "---" +end + +function FlightLogicRule:dot_3_txt() + return "---" +end + +function FlightLogicRule:override_min_flight_time() + return nil +end + +function FlightLogicRule:updateMotorStatus(wgt) + -- -- for heli, if the motor-sw==switch-sw, then ignore motor direction detection + -- if (wgt.heli_mode == true) then + -- status.motor_active = status.switch_on + -- return + -- end + + local motor_value = getValue(wgt.options.motor_channel) + --log(string.format("motor_value (%s): %s", wgt.options.motor_channel, motor_value)) + + if (self.status.motor_channel_direction_inv == nil) then + -- detect motor channel direction + if (motor_value < (-1024 + default_min_motor_value)) then + self.status.motor_channel_direction_inv = false + elseif (motor_value > (1024 - default_min_motor_value)) then + self.status.motor_channel_direction_inv = true + else + -- still nil + return + end + end + + if (FlightLogicRule.status.motor_channel_direction_inv == false) then + -- non inverted mixer + if (motor_value > (-1024 + default_min_motor_value)) then + FlightLogicRule.status.motor_active = true + else + FlightLogicRule.status.motor_active = false + end + else + -- inverted mixer + if (motor_value < (1024 - default_min_motor_value)) then + FlightLogicRule.status.motor_active = true + else + FlightLogicRule.status.motor_active = false + end + end + +end + +function FlightLogicRule:background(wgt) + -- update switch status + FlightLogicRule.status.switch_on = getSwitchValue(wgt.options.arm_switch_id) + + self:updateMotorStatus(wgt) -- always after updateSwitchStatus + + -- update telemetry status + FlightLogicRule.status.tele_is_available = wgt.tools.isTelemetryAvailable() + + + -- if status.switch_on==true then + -- log(string.format("arm_switch(%s)=ON", status.switch_name)) + -- else + -- log(string.format("arm_switch(%s)=OFF", status.switch_name)) + -- end + +end + +return FlightLogicRule diff --git a/sdcard/c800x480/WIDGETS/Flights/app.lua b/sdcard/c800x480/WIDGETS/Flights/app.lua index 0780fa35..05ddae54 100644 --- a/sdcard/c800x480/WIDGETS/Flights/app.lua +++ b/sdcard/c800x480/WIDGETS/Flights/app.lua @@ -48,21 +48,26 @@ -- or -- flight_ended2.wav-->flight_ended.wav -- for Heli, the motor-switch=arm-switch (same value for both) --- if you need a reversed arm switch (e.g. !SF) you need to do change it (for now) in the script: inverted_arm_switch_logic=0 -- if you prefer different logics, do them on logical switches, and put that logical switch in both Arm & motor channel --- if your recweiver does not have telemetry, use_telemetry-->off +-- if your recweiver does not have telemetry, use: [Plane no Telemetry] ]] +local args = {...} +local triggerTypeDefs = args[1] + local app_name = "Flights" -local app_ver = "1.7" +local app_ver = "2.1" + +local lvSCALE = lvgl.LCD_SCALE or 1 +local is800 = (LCD_W==800) local build_ui = nil ------------------------------------------------------------------------------------------------------------------ -- configuration local default_flight_starting_duration = 30 -- 30 sec to detect flight success local default_flight_ending_duration = 8 -- 8 sec to detect flight ended -local default_min_motor_value = 200 +-- local default_min_motor_value = 200 local enable_count_announcement_on_start = 0 -- 0=no voice, 1=play the count upon increment local enable_count_announcement_on_end = 1 -- 0=no voice, 1=play the count upon end of flight local show_dots = true -- false=do not show dots, true=show dbg dots @@ -72,9 +77,7 @@ local use_flights_history = 1 -- 0=do not write flights-history, -- imports -local img = bitmap.open("/WIDGETS/" .. app_name .. "/logo.png") -local LibLogClass = loadScript("/WIDGETS/" .. app_name .. "/lib_log.lua", "btd") -local m_log = LibLogClass(app_name, "/WIDGETS/" .. app_name) +local m_log = assert(loadScript("/WIDGETS/" .. app_name .. "/lib_log.lua", "btd"))(app_name, "/WIDGETS/" .. app_name) -- better font size names local FS={FONT_38=XXLSIZE,FONT_16=DBLSIZE,FONT_12=MIDSIZE,FONT_8=0,FONT_6=SMLSIZE} @@ -87,21 +90,14 @@ local function update(wgt, options) if (wgt == nil) then return end wgt.options = options - wgt.use_telemetry = wgt.options.use_telemetry + wgt.triggerDesc = triggerTypeDefs.info[wgt.options.triggerType].desc + wgt.triggerFile = triggerTypeDefs.info[wgt.options.triggerType].file wgt.enable_sounds = wgt.options.enable_sounds - wgt.heli_mode = wgt.options.heli_mode == 1 - if (wgt.options.arm_switch_id == wgt.options.motor_channel) then - wgt.heli_mode = true - end -- status wgt.status = {} - wgt.status.switch_on = nil wgt.status.switch_name = nil - wgt.status.tele_is_available = nil - wgt.status.motor_active = nil wgt.status.motor_channel_name = nil - wgt.status.motor_channel_direction_inv = nil wgt.status.flight_state = "GROUND" wgt.status.duration_passed = 0 wgt.status.periodic1 = wgt.tools.periodicInit() @@ -110,11 +106,9 @@ local function update(wgt, options) wgt.status.flight_start_date_time = 0 wgt.status.flight_end_time = 0 wgt.status.flight_duration = 0 - wgt.status.ground_on_switch = false if (wgt.options.min_flight_duration < 0) then wgt.options.min_flight_duration = math.abs(wgt.options.min_flight_duration) - wgt.status.ground_on_switch = true default_flight_ending_duration = 1 end @@ -135,6 +129,19 @@ local function update(wgt, options) wgt.status.motor_channel_name = fi_mot.name end + local t_chunk = assert(loadScript("/WIDGETS/" .. app_name .. "/rules/" .. wgt.triggerFile, "btd"), "Failed to load trigger script: "..wgt.triggerFile) + wgt.rule = t_chunk(m_log, app_name, wgt.status.switch_name, wgt.status.motor_channel_name) + + log("Using trigger type: %s (%s)", wgt.triggerDesc, wgt.triggerFile) + log("info: %s", wgt.rule:info()) + + local override_min_flight_time = wgt.rule:override_min_flight_time() + if (override_min_flight_time ~= nil) then + wgt.options.min_flight_duration = override_min_flight_time + log("Using override min flight duration: %s", wgt.options.min_flight_duration) + end + + -- auto debug mode if widget size 1/2 or 1/1 wgt.options.is_debug = (wgt.options.auto_debug==1 and wgt.zone.h > 140) -- log("auto_debug: %s, is_debug: %s, wgt.zone.h: %s", wgt.options.auto_debug, wgt.options.is_debug, wgt.zone.h) @@ -148,8 +155,8 @@ local function update(wgt, options) end local ver, radio, maj, minor, rev, osname = getVersion() - wgt.is_valid_ver = (maj == 2 and minor >= 11) - + local nVer = maj*1000000 + minor*1000 + rev + wgt.is_valid_ver = (nVer>=2011000) build_ui(wgt) end @@ -157,18 +164,13 @@ end local function create(zone, options) local wgt = { zone = zone, - options = options - } - --wgt.options.use_days = wgt.options.use_days % 2 -- modulo due to bug that cause the value to be other than 0|1 - - -- imports - wgt.ToolsClass = loadScript("/WIDGETS/" .. app_name .. "/lib_widget_tools.lua", "btd") - wgt.tools = wgt.ToolsClass(m_log, app_name) + options = options, - wgt.FlightsHistoryClass = loadScript("/WIDGETS/" .. app_name .. "/lib_flights_history.lua", "btd") - wgt.flightHistory = wgt.FlightsHistoryClass(m_log, app_name) - wgt.FlightsCountClass = loadScript("/WIDGETS/" .. app_name .. "/lib_flights_count.lua", "btd") - wgt.flightCountHWriter = wgt.FlightsCountClass(m_log, app_name, "/flights-count.csv") + -- imports + tools = loadScript("/WIDGETS/" .. app_name .. "/lib_widget_tools.lua", "btd")(m_log, app_name), + flightHistory = loadScript("/WIDGETS/" .. app_name .. "/lib_flights_history.lua", "btd")(m_log, app_name), + -- flightCountHWriter = loadScript("/WIDGETS/" .. app_name .. "/lib_flights_count.lua", "btd")(m_log, app_name, "/flights-count.csv"), + } update(wgt, options) return wgt @@ -201,72 +203,6 @@ local function getFontSize(wgt, txt) return FS.FONT_6 end ---------------------------------------------------------------------------------------------------- - -local function updateTelemetryStatus(wgt) - if wgt.use_telemetry == 0 then - wgt.status.tele_is_available = true - else - wgt.status.tele_is_available = wgt.tools.isTelemetryAvailable() - end -end - -local function updateMotorStatus(wgt) - - -- for heli, if the motor-sw==switch-sw, then ignore motor direction detection - if (wgt.heli_mode == true) then - wgt.status.motor_active = wgt.status.switch_on - return - end - - local motor_value = getValue(wgt.options.motor_channel) - --log(string.format("motor_value (%s): %s", wgt.options.motor_channel, motor_value)) - - ---- if we do not have telemetry, then the battery is not connected yet, so we can detect yet motor channel direction - --if (wgt.status.tele_is_available == nil or wgt.status.tele_is_available == false) then - -- return - --end - - if (wgt.status.motor_channel_direction_inv == nil) then - -- detect motor channel direction - if (motor_value < (-1024 + default_min_motor_value)) then - wgt.status.motor_channel_direction_inv = false - elseif (motor_value > (1024 - default_min_motor_value)) then - wgt.status.motor_channel_direction_inv = true - else - -- still nil - return - end - end - - if (wgt.status.motor_channel_direction_inv == false) then - -- non inverted mixer - if (motor_value > (-1024 + default_min_motor_value)) then - wgt.status.motor_active = true - else - wgt.status.motor_active = false - end - else - -- inverted mixer - if (motor_value < (1024 - default_min_motor_value)) then - wgt.status.motor_active = true - else - wgt.status.motor_active = false - end - end - -end - -local function updateSwitchStatus(wgt) - wgt.status.switch_on = getSwitchValue(wgt.options.arm_switch_id) - - -- if wgt.status.switch_on==true then - -- log(string.format("arm_switch(%s)=ON", wgt.status.switch_name)) - -- else - -- log(string.format("arm_switch(%s)=OFF", wgt.status.switch_name)) - -- end -end - -------------------------------------------------------------------------------------------------------- -- get flight count local function getFlightCount(wgt) @@ -345,29 +281,12 @@ local function stateChange(wgt, newState, timer_sec) end end - -local function is_flight_starting(wgt) - if (wgt.status.motor_active == true) and (wgt.status.switch_on == true) and (wgt.use_telemetry==0 or wgt.status.tele_is_available == true) then - return true - else - return false - end -end - local function background(wgt) - - updateSwitchStatus(wgt) - - updateMotorStatus(wgt) -- always after updateSwitchStatus - - updateTelemetryStatus(wgt) - - --log(string.format("tele_is_available: %s", wgt.status.tele_is_available)) + wgt.rule:background(wgt) -- **** state: GROUND *** if wgt.status.flight_state == "GROUND" then - -- if (wgt.status.motor_active == true) and (wgt.status.switch_on == true) and (wgt.use_telemetry==0 or wgt.status.tele_is_available == true) then - if (is_flight_starting(wgt) == true) then + if (wgt.rule:is_flight_starting() == true) then stateChange(wgt, "FLIGHT_STARTING", wgt.options.min_flight_duration) wgt.status.last_flight_count = getFlightCount(wgt) wgt.status.flight_start_time = getTime() * 10 / 1000 @@ -376,11 +295,9 @@ local function background(wgt) end return - -- **** state: FLIGHT_STARTING *** + -- **** state: FLIGHT_STARTING *** elseif wgt.status.flight_state == "FLIGHT_STARTING" then - - -- if (wgt.status.motor_active == false) or (wgt.status.switch_on == false) or (wgt.use_telemetry==1 and wgt.status.tele_is_available == false) then - if (is_flight_starting(wgt) == false) then + if (wgt.rule:is_flight_starting() == false) then stateChange(wgt, "GROUND", 0) return end @@ -402,10 +319,11 @@ local function background(wgt) end return - -- **** state: FLIGHT_ON *** + -- **** state: FLIGHT_ON *** elseif wgt.status.flight_state == "FLIGHT_ON" then -- record flight duration in case we soon detect end of flight - if (wgt.status.motor_active == true) and (wgt.status.switch_on == true) then + + if wgt.rule:is_still_on_flight() then wgt.status.flight_end_time = getTime() * 10 / 1000 wgt.status.flight_duration = wgt.status.flight_end_time - wgt.status.flight_start_time -- log("flight_start_time: %s", wgt.status.flight_start_time) @@ -413,13 +331,7 @@ local function background(wgt) -- log("flight_duration: %s", wgt.status.flight_duration) end - -- if (wgt.status.ground_on_switch and wgt.status.switch_on == false) then - -- stateChange(wgt, "FLIGHT_ENDING", 0) - if (wgt.use_telemetry==1 and wgt.status.tele_is_available == false) or - (wgt.use_telemetry==0 and wgt.status.switch_on == false) - or - (wgt.status.ground_on_switch and wgt.status.switch_on == false) - then + if wgt.rule:is_flight_ending() then stateChange(wgt, "FLIGHT_ENDING", default_flight_ending_duration) local num_flights = getFlightCount(wgt) @@ -448,8 +360,7 @@ local function background(wgt) doEndOfFlightTasks(wgt) end - if (wgt.use_telemetry==1 and wgt.status.tele_is_available == true and wgt.status.ground_on_switch==false) or - (wgt.use_telemetry==0 and wgt.status.switch_on == true and wgt.status.ground_on_switch==false) then + if wgt.rule:is_flight_ending() == false then stateChange(wgt, "FLIGHT_ON", 0) end @@ -498,15 +409,15 @@ build_ui = function(wgt) local ts_w, ts_h = lcd.sizeText(num_flights, font_size) local dx = (zone_w - ts_w) / 2 - local dyh = 5 + local dyh = 5*lvSCALE local dy -- = header_h - 1 - local is_top_bar = (zone_h < 50) + local is_top_bar = (zone_h < 50*lvSCALE) if is_top_bar then -- force minimal spaces - dyh = -3 + dyh = -3*lvSCALE else - dyh = 5 + dyh = 5*lvSCALE end -- global @@ -519,41 +430,43 @@ build_ui = function(wgt) -- draw count if is_top_bar == true then -- pMain:label({x=zone_w-ts_w -10, y=dy, font=font_size, text=function() return getFlightCount(wgt) end, color=wgt.options.text_color}) - pMain:label({x=10, y=13, font=font_size, text=function() return getFlightCount(wgt) end, color=wgt.options.text_color}) + pMain:label({x=10*lvSCALE, y=13*lvSCALE, font=font_size, text=function() return getFlightCount(wgt) end, color=wgt.options.text_color}) else - pMain:label({x=zone_w-ts_w-5, y=5, font=font_size, text=function() return getFlightCount(wgt) end, color=wgt.options.text_color}) + pMain:label({x=zone_w-ts_w-5*lvSCALE, y=5*lvSCALE, font=font_size, text=function() return getFlightCount(wgt) end, color=wgt.options.text_color}) end - -- enable_dbg_dots + -- enable 3 dots if (show_dots == true) then - local dxc = 7 - pMain:circle({x=5, y=20 + dxc*0, radius=3, filled=true, color=function() return wgt.status.tele_is_available==true and GREEN or GREY end}) - pMain:circle({x=5, y=20 + dxc*1, radius=3, filled=true, color=function() return wgt.status.switch_on==true and GREEN or GREY end}) - pMain:circle({x=5, y=20 + dxc*2, radius=3, filled=true, color=function() return wgt.status.motor_active==true and GREEN or GREY end}) + local dxc = 7*lvSCALE + pMain:circle({x=5*lvSCALE, y=20*lvSCALE + dxc*0, radius=3, filled=true, color=function() return wgt.rule:is_dot_1() and GREEN or GREY end}) + pMain:circle({x=5*lvSCALE, y=20*lvSCALE + dxc*1, radius=3, filled=true, color=function() return wgt.rule:is_dot_2() and GREEN or GREY end}) + pMain:circle({x=5*lvSCALE, y=20*lvSCALE + dxc*2, radius=3, filled=true, color=function() return wgt.rule:is_dot_3() and GREEN or GREY end}) end -- debug local pInfo = lvgl.box({x=0, y=40}) if wgt.options.is_debug == true then - local dx = 15 - pInfo:label({x=dx, y=0, font=FS.FONT_6, text=function() return string.format("%s - telemetry", ternary(wgt.status.tele_is_available)) end}) - pInfo:label({x=dx, y=15, font=FS.FONT_6, text=function() return string.format("%s - arm_switch (%s)", ternary(wgt.status.switch_on), wgt.status.switch_name) end}) - pInfo:label({x=dx, y=30, font=FS.FONT_6, text=function() - if (wgt.heli_mode == false) then - return string.format("%s - throttle (%s) (inv: %s)" , ternary(wgt.status.motor_active), wgt.status.motor_channel_name, wgt.status.motor_channel_direction_inv) - else - return string.format("%s - heli mode arm (ignore throttle)", ternary(wgt.status.motor_active)) - end - end}) - pInfo:label({x=dx, y=45, font=FS.FONT_6, text=function() return string.format("timer: %.1f/%d", wgt.status.duration_passed / 1000, wgt.tools.getDurationMili(wgt.status.periodic1) / 1000) end}) - pInfo:label({x=dx, y=60, font=FS.FONT_6, text=function() return string.format("flight duration: %.1f", wgt.status.flight_duration) end}) - - pInfo:label({x=dx, y=85, font=FS.FONT_6, text="state:"}) - dx = 50 - pInfo:label({x=dx, y=85, font=FS.FONT_6, text="GROUND" , color=function() return getColorByState(wgt, "GROUND") end}) - pInfo:label({x=dx, y=100, font=FS.FONT_6, text="FLIGHT_STARTING", color=function() return getColorByState(wgt, "FLIGHT_STARTING") end}) - pInfo:label({x=dx, y=115, font=FS.FONT_6, text="FLIGHT_ON" , color=function() return getColorByState(wgt, "FLIGHT_ON") end}) - pInfo:label({x=dx, y=130, font=FS.FONT_6, text="FLIGHT_ENDING" , color=function() return getColorByState(wgt, "FLIGHT_ENDING") end}) + local dx = 15*lvSCALE + pInfo:label({x=dx+190*lvSCALE, y= 5*lvSCALE, font=FS.FONT_8, text=function() return string.format("Rule: %s", wgt.triggerDesc) end}) + pInfo:label({x=dx, y= 0*lvSCALE, font=FS.FONT_6, text=function() return wgt.rule:dot_1_txt() end}) + pInfo:label({x=dx, y=15*lvSCALE, font=FS.FONT_6, text=function() return wgt.rule:dot_2_txt() end}) + pInfo:label({x=dx, y=30*lvSCALE, font=FS.FONT_6, text=function() return wgt.rule:dot_3_txt() end}) + pInfo:label({x=dx, y=45*lvSCALE, font=FS.FONT_6, text=function() return string.format("timer: %.1f/%d", wgt.status.duration_passed / 1000, wgt.tools.getDurationMili(wgt.status.periodic1) / 1000) end}) + pInfo:label({x=dx, y=60*lvSCALE, font=FS.FONT_6, text=function() return string.format("flight duration: %.1f", wgt.status.flight_duration) end}) + + pInfo:label({x=dx, y=80*lvSCALE, font=FS.FONT_6, text="state:"}) + dx = 50*lvSCALE + pInfo:label({x=dx, y= 80*lvSCALE, font=FS.FONT_6, text="GROUND" , color=function() return getColorByState(wgt, "GROUND") end}) + pInfo:label({x=dx, y= 95*lvSCALE, font=FS.FONT_6, text="FLIGHT_STARTING", color=function() return getColorByState(wgt, "FLIGHT_STARTING") end}) + pInfo:label({x=dx, y=110*lvSCALE, font=FS.FONT_6, text="FLIGHT_ON" , color=function() return getColorByState(wgt, "FLIGHT_ON") end}) + pInfo:label({x=dx, y=125*lvSCALE, font=FS.FONT_6, text="FLIGHT_ENDING" , color=function() return getColorByState(wgt, "FLIGHT_ENDING") end}) + + pInfo:label({x= 5*lvSCALE, y=145*lvSCALE, font=FS.FONT_6, text=function() return string.format("starting:\n %s", wgt.rule.is_flight_starting()) end}) + pInfo:label({x= 70*lvSCALE, y=145*lvSCALE, font=FS.FONT_6, text=function() return string.format("on_flight:\n %s", wgt.rule.is_still_on_flight()) end}) + pInfo:label({x=140*lvSCALE, y=145*lvSCALE, font=FS.FONT_6, text=function() return string.format("ending:\n %s", wgt.rule.is_flight_ending()) end}) + + pInfo:rectangle({x=dx+160*lvSCALE, y=30*lvSCALE, w=260*lvSCALE, h=140*lvSCALE, color=LIGHTBLUE, filled=true, rounded=5}) + pInfo:label({x=dx+(160+10)*lvSCALE, y=40*lvSCALE, font=FS.FONT_6, text=function() return wgt.rule:info() end, color=WHITE}) end end @@ -561,11 +474,4 @@ local function refresh(wgt, event, touchState) background(wgt) end -return { - name = app_name, - create = create, - update = update, - refresh = refresh, - background = background, - useLvgl=true -} +return {name=app_name, create=create, update=update, refresh=refresh, background=background, useLvgl=true} diff --git a/sdcard/c800x480/WIDGETS/Flights/lib_log.lua b/sdcard/c800x480/WIDGETS/Flights/lib_log.lua index c23e6e8a..7a933a01 100644 --- a/sdcard/c800x480/WIDGETS/Flights/lib_log.lua +++ b/sdcard/c800x480/WIDGETS/Flights/lib_log.lua @@ -2,6 +2,7 @@ local app_name, script_dir = ... local ENABLE_LOG_TO_CONSOLE = true local ENABLE_LOG_TO_FILE = false +local ENABLE_LOG_TO_SERIAL = false local M = {} @@ -17,6 +18,7 @@ local log = { outfile = script_dir .. "/app.log", enable_file = ENABLE_LOG_TO_FILE, enable_console = ENABLE_LOG_TO_CONSOLE and is_simulator(), + enable_serial_dbg = ENABLE_LOG_TO_SERIAL, current_level = nil, -- func @@ -61,7 +63,7 @@ local function tostring(...) end function M.do_log(iLevel, ulevel, fmt, ...) - if log.enable_console == false and log.enable_file == false then + if log.enable_console == false and log.enable_file == false and log.enable_serial_dbg == false then return end @@ -91,6 +93,12 @@ function M.do_log(iLevel, ulevel, fmt, ...) io.write(fp, msg2 .. "\n") io.close(fp) end + + -- Output to log file + if log.enable_serial_dbg == true then + serialWrite(msg2.."\r\n") -- 115200 bps + end + end function M.trace(fmt, ...) diff --git a/sdcard/c800x480/WIDGETS/Flights/lib_widget_tools.lua b/sdcard/c800x480/WIDGETS/Flights/lib_widget_tools.lua index 1e419474..738daade 100644 --- a/sdcard/c800x480/WIDGETS/Flights/lib_widget_tools.lua +++ b/sdcard/c800x480/WIDGETS/Flights/lib_widget_tools.lua @@ -1,4 +1,6 @@ -local m_log, app_name = ... +local args = {...} +local m_log = args[1] +local app_name = args[2] local M = {} M.m_log = m_log @@ -9,18 +11,20 @@ M.tele_src_id = nil local getTime = getTime local lcd = lcd --- better font names -local FONT_38 = XXLSIZE -- 38px -local FONT_16 = DBLSIZE -- 16px -local FONT_12 = MIDSIZE -- 12px -local FONT_8 = 0 -- Default 8px -local FONT_6 = SMLSIZE -- 6px +-- better font size names +local FS={FONT_38=XXLSIZE,FONT_16=DBLSIZE,FONT_12=MIDSIZE,FONT_8=0,FONT_6=SMLSIZE} +M.FS = FS +M.FONT_LIST = {FS.FONT_6, FS.FONT_8, FS.FONT_12, FS.FONT_16, FS.FONT_38} +local lvSCALE = lvgl.LCD_SCALE or 1 -local FONT_LIST = {FONT_6, FONT_8, FONT_12, FONT_16, FONT_38} --------------------------------------------------------------------------------------------------- local function log(fmt, ...) - m_log.info(fmt, ...) + if M.m_log then + M.m_log.info(fmt, ...) + else + print("[" .. M.app_name .. "] " .. string.format(fmt, ...)) + end end --------------------------------------------------------------------------------------------------- @@ -55,9 +59,10 @@ end --------------------------------------------------------------------------------------------------- function M.periodicInit() - local t = {} - t.startTime = -1; - t.durationMili = -1; + local t = { + startTime = -1, + durationMili = -1 + } return t end @@ -66,6 +71,10 @@ function M.periodicStart(t, durationMili) t.durationMili = durationMili; end +function M.periodicStop(t) + t.durationMili = -1; +end + function M.periodicHasPassed(t, show_log) -- not started yet if (t.durationMili <= 0) then @@ -110,6 +119,43 @@ function M.isTelemetryAvailable() return is_telem > 0 end +function M.isTelemetryAvailableOld() + -- select telemetry source + if not M.tele_src_id then + --log("select telemetry source") + local tele_src = getFieldInfo("RSSI") + if not tele_src then tele_src = getFieldInfo("1RSS") end + if not tele_src then tele_src = getFieldInfo("2RSS") end + if not tele_src then tele_src = getFieldInfo("RQly") end + if not tele_src then tele_src = getFieldInfo("VFR%") end + if not tele_src then tele_src = getFieldInfo("VFR") end + if not tele_src then tele_src = getFieldInfo("TRSS") end + if not tele_src then tele_src = getFieldInfo("RxBt") end + if not tele_src then tele_src = getFieldInfo("A1") end + + if tele_src == nil then + --log("no telemetry sensor found") + M.tele_src_id = nil + M.tele_src_name = "---" + return false + else + --log("telemetry sensor found: " .. tele_src.name) + M.tele_src_id = tele_src.id + M.tele_src_name = tele_src.name + end + end + + if M.tele_src_id == nil then + return false + end + + local rx_val = getValue(M.tele_src_id) + if rx_val ~= 0 then + return true + end + return false +end + --------------------------------------------------------------------------------------------------- -- workaround to detect telemetry-reset event, until a proper implementation on the lua interface will be created @@ -120,7 +166,6 @@ end -- on event detection, the function onTelemetryResetEvent() will be trigger -- function M.detectResetEvent(wgt, callback_onTelemetryResetEvent) - local currMinRSSI = getValue('RSSI-') if (currMinRSSI == nil) then log("telemetry reset event: can not be calculated") @@ -160,8 +205,8 @@ function M.getSensorInfoByName(sensorName) s1.type = s2.type --name (string) Name s1.name = s2.name - --unit (number) See list of units in the appendix of the OpenTX Lua Reference Guide - s1.unit = s2.unit + --unit (number->string) See list of units in the appendix of the OpenTX Lua Reference Guide + s1.unit = M.unitIdToString(s2.unit) --prec (number) Number of decimals s1.prec = s2.prec --id (number) Only custom sensors @@ -213,9 +258,10 @@ function M.isSensorExist(sensorName) end --------------------------------------------------------------------------------------------------- --- workaround for bug in getFiledInfo() -- ???? why? +-- workaround for bug in getFiledInfo() why? function M.cleanInvalidCharFromGetFiledInfo(sourceName) - if string.byte(string.sub(sourceName, 1, 1)) > 127 then + + if string.byte(string.sub(sourceName, 1, 1)) > 127 then sourceName = string.sub(sourceName, 2, -1) end if string.byte(string.sub(sourceName, 1, 1)) > 127 then @@ -235,35 +281,97 @@ function M.getSourceNameCleaned(source) end ------------------------------------------------------------------------------------------------------ + function M.getFontSizeRelative(orgFontSize, delta) - for i = 1, #FONT_LIST do - if FONT_LIST[i] == orgFontSize then + for i = 1, #M.FONT_LIST do + if M.FONT_LIST[i] == orgFontSize then local newIndex = i + delta - newIndex = math.min(newIndex, #FONT_LIST) + newIndex = math.min(newIndex, #M.FONT_LIST) newIndex = math.max(newIndex, 1) - return FONT_LIST[newIndex] + return M.FONT_LIST[newIndex] end end return orgFontSize end +function M.getFontIndex(fontSize, defaultFontSize) + for i = 1, #M.FONT_LIST do + -- log("M.FONT_SIZES[%d]: %d (%d)", i, M.FONT_LIST[i], fontSize) + if M.FONT_LIST[i] == fontSize then + return i + end + end + return defaultFontSize +end + ------------------------------------------------------------------------------------------------------ + function M.lcdSizeTextFixed(txt, font_size) local ts_w, ts_h = lcd.sizeText(txt, font_size) local v_offset = 0 - if font_size == FONT_38 then - v_offset = -15 - elseif font_size == FONT_16 then - v_offset = -8 - elseif font_size == FONT_12 then - v_offset = -6 - elseif font_size == FONT_8 then - v_offset = -4 - elseif font_size == FONT_6 then - v_offset = -3 + if font_size == FS.FONT_38 then + v_offset = -6*lvSCALE + ts_h = 52*lvSCALE + ts_w=ts_w-3 + elseif font_size == FS.FONT_16 then + v_offset = -6*lvSCALE + ts_h = 28*lvSCALE + elseif font_size == FS.FONT_12 then + v_offset = -5*lvSCALE + ts_h = 20*lvSCALE + elseif font_size == FS.FONT_8 then + v_offset = -3*lvSCALE + ts_h = 15*lvSCALE + elseif font_size == FS.FONT_6 then + v_offset = -2*lvSCALE + ts_h = 14*lvSCALE + end + return ts_w, ts_h, v_offset +end + +function M.getFontSize(wgt, txt, max_w, max_h, max_font_size) + local maxFontIndex = M.getFontIndex(max_font_size, nil) + --log("getFontSize() [%s] %dx%d (maxIndex: %d)", txt, max_w, max_h, maxFontIndex) + + if maxFontIndex>=5 then + local w, h, v_offset = M.lcdSizeTextFixed(txt, FS.FONT_38) + if w <= max_w and h <= max_h then + log("[%s] FS.FONT_38 %dx%d", txt, w, h) + return FS.FONT_38, w, h, v_offset + else + log("[%s] FS.FONT_38 %dx%d (too small)", txt, w, h) + end + end + + local w, h, v_offset + if maxFontIndex>=4 then + w, h, v_offset = M.lcdSizeTextFixed(txt, FS.FONT_16) + if w <= max_w and h <= max_h then + -- log("[%s] FS.FONT_16 %dx%d", txt, w, h, txt) + return FS.FONT_16, w, h, v_offset + end + end + + if maxFontIndex>=3 then + w, h, v_offset = M.lcdSizeTextFixed(txt, FS.FONT_12) + if w <= max_w and h <= max_h then + -- log("[%s] FS.FONT_12 %dx%d", txt, w, h, txt) + return FS.FONT_12, w, h, v_offset + end end - return ts_w, ts_h +2*v_offset, v_offset + + if maxFontIndex>=2 then + w, h, v_offset = M.lcdSizeTextFixed(txt, FS.FONT_8) + if w <= max_w and h <= max_h then + -- log("[%s] FS.FONT_8 %dx%d", txt, w, h, txt) + return FS.FONT_8, w, h, v_offset + end + end + + w, h, v_offset = M.lcdSizeTextFixed(txt, FS.FONT_6) + -- log("[%s] FS.FONT_6 %dx%d", txt, w, h, txt) + return FS.FONT_6, w, h, v_offset end ------------------------------------------------------------------------------------------------------ diff --git a/sdcard/c800x480/WIDGETS/Flights/main.lua b/sdcard/c800x480/WIDGETS/Flights/main.lua index 92d8f3ec..4051773c 100644 --- a/sdcard/c800x480/WIDGETS/Flights/main.lua +++ b/sdcard/c800x480/WIDGETS/Flights/main.lua @@ -4,6 +4,27 @@ local tool = nil local default_flight_starting_duration = 30 -- 30 sec to detect flight success +local triggerTypeDefs = { + labels = { + "1. Plane with Telemetry", + "2. Plane no Telemetry", + "3. Heli [RPM]", + "4. Heli [Arm]", + "5. By switch", + "6. DLG [Height]", + "7. Glider [Height]", + }, + info = { + {desc = "1=Plane with Telemetry (mot+Arm+Telm)", file = "1_plane_tlm.lua" }, + {desc = "2=Plane no Telemetry (mot+Arm)", file = "2_plane_no_tlm.lua" }, + {desc = "3=Heli [RPM] (RPM+Telm)", file = "3_heli_rpm.lua" }, + {desc = "4=Heli [Arm] (Arm+Telm)", file = "4_heli_arm.lua" }, + {desc = "5=By switch", file = "5_by_switch.lua" }, + {desc = "6=DLG [Vario] (Height)", file = "6_dlg.lua" }, + {desc = "7=Glider [Vario] (mot+Arm+Height)", file = "7_glider.lua" }, + } +} + -- for backward compatibility local function getSwitchIds(key) local OS_SWITCH_ID = { @@ -21,27 +42,23 @@ end local DEFAULT_MOTOR_CHANNEL_ID = getSourceIndex("CH3") or getSourceIndex("thr111") or getSwitchIds("CH3") -- motor_channel=CH3 local options = { + { "triggerType" , CHOICE, 1 , triggerTypeDefs.labels}, { "arm_switch_id" , SWITCH, "SF"..CHAR_UP}, -- CHAR_UP|-|CHAR_DOWN { "motor_channel" , SOURCE, DEFAULT_MOTOR_CHANNEL_ID }, - { "heli_mode" , BOOL, 0}, -- ignore motor direction detection, and throttle position { "text_color" , COLOR, YELLOW},--, COLOR_THEME_PRIMARY2 }, { "min_flight_duration" , VALUE, default_flight_starting_duration, -30, 120 }, { "enable_sounds" , BOOL, 1}, -- 0=no sound, 1=play blip sound on increment & on flight end - { "use_telemetry" , BOOL, 1}, -- 0=do not use telemetry, 1=use telemetry in state machine { "auto_debug" , BOOL, 1}, -- show debug status on screen if widget is large enough - -- { "ground_on_switch" , BOOL, 0}, -- 0=auto detect ground by time, 1=ground on switch is used (not auto) } local function translate(name) local translations = { arm_switch_id="Arm Switch Position", motor_channel="Motor Channel", - heli_mode="Heli mode (ignore motor ch)", min_flight_duration = "Min flight duration (sec)", text_color = "Text color", enable_sounds = "Enable sounds", - use_telemetry = "Use telemetry", - -- ground_on_switch = "Ground on switch", + triggerType = "Type", auto_debug = "Auto debug", } return translations[name] @@ -50,7 +67,7 @@ end local function create(zone, options) -- print(string.format("1111 Flights create: %s", name)) - tool = assert(loadScript("/WIDGETS/"..app_name.."/app.lua", "btd"))() + tool = assert(loadScript("/WIDGETS/"..app_name.."/app.lua", "btd"))(triggerTypeDefs) return tool.create(zone, options) end local function update(wgt, options) return tool.update(wgt, options) end diff --git a/sdcard/c800x480/WIDGETS/Flights/rules/1_plane_tlm.lua b/sdcard/c800x480/WIDGETS/Flights/rules/1_plane_tlm.lua new file mode 100644 index 00000000..a902dd77 --- /dev/null +++ b/sdcard/c800x480/WIDGETS/Flights/rules/1_plane_tlm.lua @@ -0,0 +1,96 @@ +local args = {...} +local m_log = args[1] +local app_name = args[2] +local switch_name = args[3] +local motor_channel_name = args[4] + +local info = [[ +Flight Rules +flight active when: +1. Telemetry available +2. Arm switch ON +3. motor channel not idle +]] + +local FlightLogicRule = assert(loadScript("/WIDGETS/" .. app_name .. "/rules/FlightLogicRule", "btd"), "Failed to load trigger Base Class")(m_log, app_name, switch_name, motor_channel_name) + +local M = FlightLogicRule:new() + +local function log(fmt, ...) + FlightLogicRule:log(fmt, ...) +end + +function M:info() + return info +end + +function M:is_flight_starting() + if (M.status.tele_is_available == false) then + return false + end + if (M.status.switch_on == false) then + return false + end + if (M.status.motor_active == false) then + return false + end + + return true +end + +function M:is_still_on_flight() + if M.status.switch_on == false then + return false + end + if M.status.motor_active == false then + return false + end + return true +end + +function M:is_flight_ending() + return (M.status.tele_is_available == false) +end + +function M:is_dot_1() + return M.status.tele_is_available==true +end +function M:is_dot_2() + return M.status.switch_on==true +end +function M:is_dot_3() + return M.status.motor_active==true +end + +function M:dot_1_txt() + return string.format("%s - telemetry", M:to_on_off(M.status.tele_is_available)) +end + +function M:dot_2_txt() + return string.format("%s - arm_switch (%s)", M:to_on_off(M.status.switch_on), M.status.switch_name) +end + +function M:dot_3_txt() + return string.format("%s - throttle (%s) (inv: %s)" , M:to_on_off(self.status.motor_active), self.status.motor_channel_name, self.status.motor_channel_direction_inv) +end + + +-- function M.background(wgt) +-- -- update switch status +-- status.switch_on = getSwitchValue(wgt.options.arm_switch_id) + +-- self:updateMotorStatus(wgt) -- always after updateSwitchStatus + +-- -- update telemetry status +-- status.tele_is_available = wgt.tools.isTelemetryAvailable() + + +-- -- if status.switch_on==true then +-- -- log(string.format("arm_switch(%s)=ON", status.switch_name)) +-- -- else +-- -- log(string.format("arm_switch(%s)=OFF", status.switch_name)) +-- -- end + +-- end + +return M diff --git a/sdcard/c800x480/WIDGETS/Flights/rules/2_plane_no_tlm.lua b/sdcard/c800x480/WIDGETS/Flights/rules/2_plane_no_tlm.lua new file mode 100644 index 00000000..e8bfa93d --- /dev/null +++ b/sdcard/c800x480/WIDGETS/Flights/rules/2_plane_no_tlm.lua @@ -0,0 +1,69 @@ +local args = {...} +local m_log = args[1] +local app_name = args[2] +local switch_name = args[3] +local motor_channel_name = args[4] + +local info = [[ +Flight Rules +flight active when: +1. Arm switch ON +2. motor channel not idle +Notes: +no Telemetry needed +]] + +local FlightLogicRule = assert(loadScript("/WIDGETS/" .. app_name .. "/rules/FlightLogicRule", "btd"), "Failed to load trigger Base Class")(m_log, app_name, switch_name, motor_channel_name) + +local M = FlightLogicRule:new() + +local function log(fmt, ...) + FlightLogicRule:log(fmt, ...) +end + +function M:info() + return info +end + +function M:is_flight_starting() + if (M.status.switch_on == false) then + return false + end + if (M.status.motor_active == false) then + return false + end + + return true +end + +function M:is_still_on_flight() + if M.status.switch_on == false then + return false + end + if M.status.motor_active == false then + return false + end + return true +end + +function M:is_flight_ending() + return (M.status.switch_on == false) +end + +function M:is_dot_2() + return M.status.switch_on==true +end +function M:is_dot_3() + return M.status.motor_active==true +end + +function M:dot_2_txt() + return string.format("%s - arm_switch (%s)", M:to_on_off(M.status.switch_on), M.status.switch_name) +end + +function M:dot_3_txt() + return string.format("%s - throttle (%s) (inv: %s)" , M:to_on_off(M.status.motor_active), M.status.motor_channel_name, M.status.motor_channel_direction_inv) +end + + +return M diff --git a/sdcard/c800x480/WIDGETS/Flights/rules/3_heli_rpm.lua b/sdcard/c800x480/WIDGETS/Flights/rules/3_heli_rpm.lua new file mode 100644 index 00000000..1074a418 --- /dev/null +++ b/sdcard/c800x480/WIDGETS/Flights/rules/3_heli_rpm.lua @@ -0,0 +1,96 @@ +local args = {...} +local m_log = args[1] +local app_name = args[2] +local switch_name = args[3] +local motor_channel_name = args[4] + +local info = [[ +Flight Rules +flight active when: +1. Telemetry available +2. headspeed is above 500 rpm +Notes: +rpm sensor 'Hspd' is required +]] + +local FlightLogicRule = assert(loadScript("/WIDGETS/" .. app_name .. "/rules/FlightLogicRule", "btd"), "Failed to load trigger Base Class")(m_log, app_name, switch_name, motor_channel_name) + +local M = FlightLogicRule:new() + +local status_me = { + is_rpm_on = false, +} + +local function log(fmt, ...) + FlightLogicRule:log(fmt, ...) +end + +function M:info() + return info +end + +function M:is_flight_starting() + if (M.status.tele_is_available == false) then + return false + end + -- if (M.status.switch_on == false) then + -- return false + -- end + if (status_me.is_rpm_on == false) then + return false + end + + return true +end + +function M:is_still_on_flight() + -- if M.status.switch_on == false then + -- return false + -- end + if status_me.is_rpm_on == false then + return false + end + return true +end + +function M:is_flight_ending() + return (status_me.is_rpm_on == false) +end + +function M:is_dot_1() + return M.status.tele_is_available==true +end +-- function M:is_dot_2() +-- return M.status.switch_on==true +-- end +function M:is_dot_3() + return status_me.is_rpm_on==true +end + +function M:dot_1_txt() + return string.format("%s - telemetry", M:to_on_off(M.status.tele_is_available)) +end + +-- function M:dot_2_txt() +-- return string.format("%s - arm_switch (%s)", M:to_on_off(M.status.switch_on), M.status.switch_name) +-- end + +function M:dot_3_txt() + return string.format("%s - rpm_on (%s)" , M:to_on_off(status_me.is_rpm_on), "tlm: Hspd") +end + +function M:background(wgt) + + local hspd = getValue("Hspd") + -- local rpm = getValue("rpm") + + if hspd ~= nil then + status_me.is_rpm_on = (hspd > 500) + else + status_me.is_rpm_on = false + end + +end + + +return M diff --git a/sdcard/c800x480/WIDGETS/Flights/rules/4_heli_arm.lua b/sdcard/c800x480/WIDGETS/Flights/rules/4_heli_arm.lua new file mode 100644 index 00000000..a571f1fb --- /dev/null +++ b/sdcard/c800x480/WIDGETS/Flights/rules/4_heli_arm.lua @@ -0,0 +1,82 @@ +local args = {...} +local m_log = args[1] +local app_name = args[2] +local switch_name = args[3] +local motor_channel_name = args[4] + +local info = [[ +Flight Rules +flight active when: +1. Telemetry available +2. Arm switch ON +]] + +local FlightLogicRule = assert(loadScript("/WIDGETS/" .. app_name .. "/rules/FlightLogicRule", "btd"), "Failed to load trigger Base Class")(m_log, app_name, switch_name, motor_channel_name) + +local M = FlightLogicRule:new() + +local function log(fmt, ...) + FlightLogicRule:log(fmt, ...) +end + +function M:info() + return info +end + +function M:is_flight_starting() + if (M.status.tele_is_available == false) then + return false + end + if (M.status.switch_on == false) then + return false + end + + return true +end + +function M:is_still_on_flight() + if M.status.switch_on == false then + return false + end + return true +end + +function M:is_flight_ending() + return (M.status.tele_is_available == false) +end + +function M:is_dot_1() + return M.status.tele_is_available==true +end +function M:is_dot_2() + return M.status.switch_on==true +end + +function M:dot_1_txt() + return string.format("%s - telemetry", M:to_on_off(M.status.tele_is_available)) +end + +function M:dot_2_txt() + return string.format("%s - arm_switch (%s)", M:to_on_off(M.status.switch_on), M.status.switch_name) +end + + +-- function M.background(wgt) +-- -- update switch status +-- status.switch_on = getSwitchValue(wgt.options.arm_switch_id) + +-- self:updateMotorStatus(wgt) -- always after updateSwitchStatus + +-- -- update telemetry status +-- status.tele_is_available = wgt.tools.isTelemetryAvailable() + + +-- -- if status.switch_on==true then +-- -- log(string.format("arm_switch(%s)=ON", status.switch_name)) +-- -- else +-- -- log(string.format("arm_switch(%s)=OFF", status.switch_name)) +-- -- end + +-- end + +return M diff --git a/sdcard/c800x480/WIDGETS/Flights/rules/5_by_switch.lua b/sdcard/c800x480/WIDGETS/Flights/rules/5_by_switch.lua new file mode 100644 index 00000000..3c22585a --- /dev/null +++ b/sdcard/c800x480/WIDGETS/Flights/rules/5_by_switch.lua @@ -0,0 +1,49 @@ +local args = {...} +local m_log = args[1] +local app_name = args[2] +local switch_name = args[3] +local motor_channel_name = args[4] + +local info = [[ +Flight Rules +flight active when: +1. Arm switch ON +Notes: +only the switch control the flight +the switch can be physical / logical +]] + +local FlightLogicRule = assert(loadScript("/WIDGETS/" .. app_name .. "/rules/FlightLogicRule", "btd"), "Failed to load trigger Base Class")(m_log, app_name, switch_name, motor_channel_name) + +local M = FlightLogicRule:new() + +local function log(fmt, ...) + FlightLogicRule:log(fmt, ...) +end + +function M:info() + return info +end + +function M:is_flight_starting() + return (M.status.switch_on == true) +end + +function M:is_still_on_flight() + return (M.status.switch_on == true) +end + +function M:is_flight_ending() + return (M.status.switch_on == false) +end + +function M:is_dot_2() + return M.status.switch_on==true +end + +function M:dot_2_txt() + return string.format("%s - arm_switch (%s)", M:to_on_off(M.status.switch_on), M.status.switch_name) +end + + +return M diff --git a/sdcard/c800x480/WIDGETS/Flights/rules/6_dlg.lua b/sdcard/c800x480/WIDGETS/Flights/rules/6_dlg.lua new file mode 100644 index 00000000..7aa8846c --- /dev/null +++ b/sdcard/c800x480/WIDGETS/Flights/rules/6_dlg.lua @@ -0,0 +1,98 @@ +local args = {...} +local m_log = args[1] +local app_name = args[2] +local switch_name = args[3] +local motor_channel_name = args[4] + +local info = [[ +Flight Rules - DLG (Discus Launch Glider) +flight active when: +1. Telemetry available +2. Height increase detected (vario/altitude) +3. Vario Telemetry available +Notes: +- Flight starts when vario detect launch +- Flight ends when height below 3m +]] + +local FlightLogicRule = assert(loadScript("/WIDGETS/" .. app_name .. "/rules/FlightLogicRule", "btd"), "Failed to load trigger Base Class")(m_log, app_name, wgt_status) + +local M = FlightLogicRule:new() + +-- DLG specific variables +-- local height_threshold = 5 -- meters above starting height to consider flight active +-- local landing_threshold = 2 -- meters above starting height to consider landing + +local VSPEED_DETECT_THROW = 8 +-- local VSPEED_DETECT_THROW_DONE = 1 +-- local ALTITUDE_DETECT_ON_AIR = 20 -- meters, threshold to detect if we are on air +local ALTITUDE_DETECT_LANDING = 2 -- meters, threshold to start detect landing + +local status_me = { + is_throwing = false, + is_still_flying = false, + is_landing = false +} + +local function log(fmt, ...) + FlightLogicRule:log(fmt, ...) +end + +function M:info() + return info +end + +function M:is_flight_starting() + if (M.status.tele_is_available == false) then + return false + end + return status_me.is_throwing +end + +function M:is_still_on_flight() + return status_me.is_still_flying +end + +function M:is_flight_ending() + return status_me.is_landing +end + +function M:is_dot_1() + return M.status.tele_is_available==true +end + +function M:is_dot_2() + return self.status.is_throwing or self.status.is_still_flying +end + + +function M:dot_1_txt() + return string.format("%s - telemetry", M:to_on_off(M.status.tele_is_available)) +end + +function M:dot_2_txt() + return string.format("%s - vario", M:to_on_off(status_me.is_throwing or status_me.is_still_flying)) +end + +function M:override_min_flight_time() + return 1.0 +end + +function M:background(wgt) + + if (self.status.tele_is_available == false) then + status_me.is_throwing = false + status_me.is_still_flying = false + status_me.is_landing = false + return + end + + local alt = getValue("Alt") or 0 + local vspd = getValue("VSpd") or 0 + + status_me.is_throwing = (vspd > VSPEED_DETECT_THROW) + status_me.is_still_flying = (alt >= ALTITUDE_DETECT_LANDING) + status_me.is_landing = (alt < ALTITUDE_DETECT_LANDING) +end + +return M diff --git a/sdcard/c800x480/WIDGETS/Flights/rules/FlightLogicRule.lua b/sdcard/c800x480/WIDGETS/Flights/rules/FlightLogicRule.lua new file mode 100644 index 00000000..5cb7a47e --- /dev/null +++ b/sdcard/c800x480/WIDGETS/Flights/rules/FlightLogicRule.lua @@ -0,0 +1,146 @@ +local args = {...} +local m_log = args[1] +local app_name = args[2] +local switch_name = args[3] +local motor_channel_name = args[4] + +local default_min_motor_value = 200 + +local FlightLogicRule = { + m_log = m_log, + app_name = app_name, + + status = { + switch_on = nil, + switch_name = switch_name, + tele_is_available = nil, + motor_active = nil, + motor_channel_name = motor_channel_name, + motor_channel_direction_inv = nil, + } + + +} + +function FlightLogicRule:new (o) + o = o or {} + setmetatable(o, FlightLogicRule) + FlightLogicRule.__index = FlightLogicRule + return o +end + +function FlightLogicRule:log(fmt, ...) + FlightLogicRule.m_log.info(fmt, ...) +end + +FlightLogicRule.info = function() + return "I am trigger class" +end + +function FlightLogicRule:is_flight_starting() + return false +end + +function FlightLogicRule:is_still_on_flight() + return false +end + +function FlightLogicRule:is_flight_ending() + return false +end + +function FlightLogicRule:is_dot_1() + return true +end + +function FlightLogicRule:is_dot_2() + return true +end + +function FlightLogicRule:is_dot_3() + return true +end + +function FlightLogicRule:to_on_off(cond) + if cond then + return "ON" + else + return "OFF" + end +end + +function FlightLogicRule:dot_1_txt() + return "---" +end + +function FlightLogicRule:dot_2_txt() + return "---" +end + +function FlightLogicRule:dot_3_txt() + return "---" +end + +function FlightLogicRule:override_min_flight_time() + return nil +end + +function FlightLogicRule:updateMotorStatus(wgt) + -- -- for heli, if the motor-sw==switch-sw, then ignore motor direction detection + -- if (wgt.heli_mode == true) then + -- status.motor_active = status.switch_on + -- return + -- end + + local motor_value = getValue(wgt.options.motor_channel) + --log(string.format("motor_value (%s): %s", wgt.options.motor_channel, motor_value)) + + if (self.status.motor_channel_direction_inv == nil) then + -- detect motor channel direction + if (motor_value < (-1024 + default_min_motor_value)) then + self.status.motor_channel_direction_inv = false + elseif (motor_value > (1024 - default_min_motor_value)) then + self.status.motor_channel_direction_inv = true + else + -- still nil + return + end + end + + if (FlightLogicRule.status.motor_channel_direction_inv == false) then + -- non inverted mixer + if (motor_value > (-1024 + default_min_motor_value)) then + FlightLogicRule.status.motor_active = true + else + FlightLogicRule.status.motor_active = false + end + else + -- inverted mixer + if (motor_value < (1024 - default_min_motor_value)) then + FlightLogicRule.status.motor_active = true + else + FlightLogicRule.status.motor_active = false + end + end + +end + +function FlightLogicRule:background(wgt) + -- update switch status + FlightLogicRule.status.switch_on = getSwitchValue(wgt.options.arm_switch_id) + + self:updateMotorStatus(wgt) -- always after updateSwitchStatus + + -- update telemetry status + FlightLogicRule.status.tele_is_available = wgt.tools.isTelemetryAvailable() + + + -- if status.switch_on==true then + -- log(string.format("arm_switch(%s)=ON", status.switch_name)) + -- else + -- log(string.format("arm_switch(%s)=OFF", status.switch_name)) + -- end + +end + +return FlightLogicRule