From 1d75c09ea49fbd36f3e06033f5f3bee5dfdc8540 Mon Sep 17 00:00:00 2001
From: Pradeep CK <pradeep@carbonix.com.au>
Date: Mon, 25 Nov 2024 12:21:52 +1100
Subject: [PATCH] LUA : restructure - WIP

WIP
---
 .../scripts/cx_built_in_test.lua              | 339 +++++-------------
 .../CarbonixCommon/scripts/cx_modules/esc.lua | 172 +++++++++
 .../scripts/cx_utils/cx_aircraft.lua          |   8 +
 .../scripts/cx_utils/cx_msg.lua               |  47 +++
 .../scripts/cx_modules/esc.lua                |   1 +
 .../scripts/cx_utils/cx_aircraft.lua          |   8 +
 .../scripts/cx_utils/cx_msg.lua               |   1 +
 .../scripts/cx_modules/esc.lua                |   1 +
 .../scripts/cx_utils/cx_aircraft.lua          |   8 +
 .../scripts/cx_utils/cx_msg.lua               |   1 +
 .../scripts/cx_modules/esc.lua                |   1 +
 .../scripts/cx_utils/cx_aircraft.lua          |   8 +
 .../scripts/cx_utils/cx_msg.lua               |   1 +
 .../scripts/cx_modules/esc.lua                |   1 +
 .../scripts/cx_utils/cx_aircraft.lua          |   8 +
 .../scripts/cx_utils/cx_msg.lua               |   1 +
 16 files changed, 363 insertions(+), 243 deletions(-)
 create mode 100644 libraries/AP_HAL_ChibiOS/hwdef/CarbonixCommon/scripts/cx_modules/esc.lua
 create mode 100644 libraries/AP_HAL_ChibiOS/hwdef/CarbonixCommon/scripts/cx_utils/cx_aircraft.lua
 create mode 100644 libraries/AP_HAL_ChibiOS/hwdef/CarbonixCommon/scripts/cx_utils/cx_msg.lua
 create mode 120000 libraries/AP_HAL_ChibiOS/hwdef/CubeOrange-Ottano/scripts/cx_modules/esc.lua
 create mode 100644 libraries/AP_HAL_ChibiOS/hwdef/CubeOrange-Ottano/scripts/cx_utils/cx_aircraft.lua
 create mode 120000 libraries/AP_HAL_ChibiOS/hwdef/CubeOrange-Ottano/scripts/cx_utils/cx_msg.lua
 create mode 120000 libraries/AP_HAL_ChibiOS/hwdef/CubeOrange-Volanti/scripts/cx_modules/esc.lua
 create mode 100644 libraries/AP_HAL_ChibiOS/hwdef/CubeOrange-Volanti/scripts/cx_utils/cx_aircraft.lua
 create mode 120000 libraries/AP_HAL_ChibiOS/hwdef/CubeOrange-Volanti/scripts/cx_utils/cx_msg.lua
 create mode 120000 libraries/AP_HAL_ChibiOS/hwdef/CubeOrangePlus-Ottano/scripts/cx_modules/esc.lua
 create mode 100644 libraries/AP_HAL_ChibiOS/hwdef/CubeOrangePlus-Ottano/scripts/cx_utils/cx_aircraft.lua
 create mode 120000 libraries/AP_HAL_ChibiOS/hwdef/CubeOrangePlus-Ottano/scripts/cx_utils/cx_msg.lua
 create mode 120000 libraries/AP_HAL_ChibiOS/hwdef/CubeOrangePlus-Volanti/scripts/cx_modules/esc.lua
 create mode 100644 libraries/AP_HAL_ChibiOS/hwdef/CubeOrangePlus-Volanti/scripts/cx_utils/cx_aircraft.lua
 create mode 120000 libraries/AP_HAL_ChibiOS/hwdef/CubeOrangePlus-Volanti/scripts/cx_utils/cx_msg.lua

diff --git a/libraries/AP_HAL_ChibiOS/hwdef/CarbonixCommon/scripts/cx_built_in_test.lua b/libraries/AP_HAL_ChibiOS/hwdef/CarbonixCommon/scripts/cx_built_in_test.lua
index 1abb8d649a..d9e93fbce9 100644
--- a/libraries/AP_HAL_ChibiOS/hwdef/CarbonixCommon/scripts/cx_built_in_test.lua
+++ b/libraries/AP_HAL_ChibiOS/hwdef/CarbonixCommon/scripts/cx_built_in_test.lua
@@ -1,243 +1,96 @@
---[[
-    File Name: cx_built_in_test.lua
-    Description: This script performs a continuous built-in test for various functionalities on Carbonix Aircrafts, focusing on ESC status check and fault detection.
-    Owner: [Carbonix - Software Team]
-]]
-
--- ******************* Macros *******************
-
-local SCRIPT_NAME = 'CX_BIT'
-local SCRIPT_VERSION = 1.0 -- Script Version
-
-
---------  MAVLINK/AUTOPILOT 'CONSTANTS'  --------
--- MAVLink Severity Levels
-local MAV_SEVERITY_CRITICAL = 2
-local MAV_SEVERITY_ERROR = 3
-local MAV_SEVERITY_WARNING = 4
-local MAV_SEVERITY_INFO = 6
-
--- Engine Types
-local HIRTH_EFI_TYPE = 8
-
-local ESC_WARMUP_TIME = 3000
-local SERVO_OUT_THRESHOLD = 1010
-local ESC_RPM_THRESHOLD = 10
--- ******************* Variables *******************
-
-local number_of_esc = 5 --default value for Volanti
-
--- Add a new table to store the warm-up end times for each ESC
-local esc_warmup_end_time = {}
-
-local srv_number = {
-    [1] = {"Motor1", 33},
-    [2] = {"Motor2", 34},
-    [3] = {"Motor3", 35},
-    [4] = {"Motor4", 36},
-    [5] = {"Motor5", 70},
-    [6] = {"Motor6", 38},
-    [7] = {"Elevator", 19},
-    [8] = {"Rudder", 21},
-    [9] = {"GPIO", -1},
-    [10] = {"Script1", 94},
-    [11] = {"Aileron", 4}
-}
-
-local srv_prv_telem_ms = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
-local srv_telem_in_err_status  = {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}
-local srv_rpm_in_err_status  = {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}
-
--- ******************* Objects *******************
-
-local auth_id = arming:get_aux_auth_id()
-assert(auth_id, SCRIPT_NAME .. ": could not get a prearm check auth id")
-
-local params = {
-    EFI_TYPE = Parameter()
-}
-
-
--- ******************* Functions *******************
-
--- wrapper for gcs:send_text()
-local function gcs_msg(severity, txt)
-    if type(severity) == 'string' then
-    -- allow just a string to be passed for simple/routine messages
-        txt      = severity
-        severity = MAV_SEVERITY_INFO
-    end
-    gcs:send_text(severity, string.format('%s: %s', SCRIPT_NAME, txt))
-end
-
-local function check_aircraft_type()
-    if params.EFI_TYPE:get() == HIRTH_EFI_TYPE then
-        number_of_esc = 4
-    else
-        number_of_esc = 5
-    end
-    return true
-end
-
-local function init_param()
-    for param_name, param in pairs(params) do
-        if not param:init(param_name) then
-            return false
-        end
-    end
-    return true
-end
-
-local function arming_check_init()
-    -- check param fetch
-    if not init_param() then
-        arming:set_aux_auth_failed(auth_id, "Parameter Init Failed")
-        gcs_msg(MAV_SEVERITY_WARNING, "Parameter Init Failed")
-        return false
-    end
-    -- check aircraft Type
-    if not check_aircraft_type() then
-        arming:set_aux_auth_failed(auth_id, "Aircraft Type Check Failed")
-        gcs_msg(MAV_SEVERITY_WARNING, "Aircraft Type Check Failed")
-        return false
-    end
-    gcs_msg(MAV_SEVERITY_INFO, "Script Version " .. SCRIPT_VERSION .. " Initialized")
-    arming:set_aux_auth_passed(auth_id)
-    return true
-end
-
--- Call this function whenever a motor starts running
-local function esc_is_started(i)
-    -- Set the warm-up end time for this ESC to 3 seconds from now
-    esc_warmup_end_time[i] = millis() + ESC_WARMUP_TIME
-end
-
--- Call this function whenever a motor stops running
-local function esc_is_stopped(i)
-    -- Clear the warm-up end time for this ESC
-    esc_warmup_end_time[i] = nil
-end
-
--- Counters to debounce nil checks on esc rpm and servo output, this is a
--- workaround to avoid giving the pilot a critical warning for an unexplained
--- one-loop dropout we saw recently
-local NIL_WARN_THRESHOLD = 3
-local esc_rpm_nil_counter = {0, 0, 0, 0, 0}
-local servo_out_nil_counter = {0, 0, 0, 0, 0}
-
-local function esc_check_loop()
-    for i = 1, number_of_esc  do
-        local esc_last_telem_data_ms = esc_telem:get_last_telem_data_ms(i-1):toint()
-        local esc_rpm = esc_telem:get_rpm(i-1)
-        local servo_out = SRV_Channels:get_output_pwm(srv_number[i][2])
-        -- Telem data timestamp check
-        if not esc_last_telem_data_ms or esc_last_telem_data_ms == 0 or esc_last_telem_data_ms == srv_prv_telem_ms[i] then
-            if srv_telem_in_err_status[i] == false then
-                gcs_msg(MAV_SEVERITY_CRITICAL, "ESC " .. i .. " Telemetry Lost")
-                srv_telem_in_err_status[i] = true
-            end
-        -- Nil check for RPM reading
-        elseif not esc_rpm then
-            esc_rpm_nil_counter[i] = esc_rpm_nil_counter[i] + 1
-            if esc_rpm_nil_counter[i] < NIL_WARN_THRESHOLD then
-                gcs_msg(MAV_SEVERITY_INFO, "ESC " .. i .. " RPM nil")
-            elseif srv_rpm_in_err_status[i] == false then
-                gcs_msg(MAV_SEVERITY_CRITICAL, "ESC " .. i .. " RPM nil")
-                srv_telem_in_err_status[i] = true
-            end
-        -- Nil check for servo output
-        elseif not servo_out then
-            servo_out_nil_counter[i] = servo_out_nil_counter[i] + 1
-            if servo_out_nil_counter[i] < NIL_WARN_THRESHOLD then
-                gcs_msg(MAV_SEVERITY_INFO, "ESC " .. i .. " Servo Out nil")
-            elseif srv_rpm_in_err_status[i] == false then
-                gcs_msg(MAV_SEVERITY_CRITICAL, "ESC " .. i .. " Servo Out nil")
-                srv_telem_in_err_status[i] = true
-            end
-        -- Telemetry data is fresh and valid
-        else
-            if srv_telem_in_err_status[i] == true then
-                gcs_msg(MAV_SEVERITY_INFO, "ESC " .. i .. " Telemetry Recovered")
-                srv_telem_in_err_status[i] = false
-                servo_out_nil_counter[i] = 0
-                esc_rpm_nil_counter[i] = 0
-            end
-            -- If armed, check that the motor is actually turning when it is commanded to
-            if arming:is_armed() then
-                -- If the PWM is below the threshold, it is okay for the motor to be stopped
-                if servo_out < SERVO_OUT_THRESHOLD then
-                    esc_is_stopped(i)
-                -- If the PWM has just gone above the threshold, start the warm-up timer
-                elseif servo_out > SERVO_OUT_THRESHOLD and not esc_warmup_end_time[i]  then
-                    esc_is_started(i)
-                -- If the motor is running, and the warmup timer has expired, check that the motor is spinning
-                elseif esc_warmup_end_time[i] and millis() > esc_warmup_end_time[i] then
-                    if servo_out > SERVO_OUT_THRESHOLD and esc_rpm < ESC_RPM_THRESHOLD then
-                        if srv_rpm_in_err_status[i] == false then
-                            gcs_msg(MAV_SEVERITY_CRITICAL, "ESC " .. i .. " RPM Drop")
-                            srv_rpm_in_err_status[i] = true
-                        end
-                    else
-                        if srv_rpm_in_err_status[i] == true then
-                            gcs_msg(MAV_SEVERITY_INFO, "ESC " .. i .. " RPM Recovered")
-                            srv_rpm_in_err_status[i] = false
-                        end
-                    end
-                end
-            end
-        end
-        -- Update srv_prv_telem_ms[i] if it had valid data this loop
-        if esc_last_telem_data_ms and esc_last_telem_data_ms ~= 0 then
-            srv_prv_telem_ms[i] = esc_last_telem_data_ms
-        end
-    end
-end
-
-local function arming_checks()
-    -- check for status in srv_telem_in_err_status and also CX_SERVO_ERROR bit status
-    local pre_arm_status = false-- check for status in srv_telem_in_err_status and also CX_SERVO_ERROR bit status
-    arming:set_aux_auth_passed(auth_id)
-
-    for i, status in ipairs(srv_telem_in_err_status) do
-        if status == true then
-            arming:set_aux_auth_failed(auth_id, "Actuator ".. i .. " Telemetry Error")
-            return false
-        end
-    end
-    if pre_arm_status == false then
-        arming:set_aux_auth_passed(auth_id)
-    end
-
-end
-
-local function update()
-    esc_check_loop()
-    arming_checks()
-end
-
--- wrapper around update(). This calls update() and if update faults
--- then an error is displayed, but the script is not stopped
-local function protected_wrapper()
-    local success, err = pcall(update)
-    if not success then
-        gcs_msg(MAV_SEVERITY_ERROR, "Internal Error: " .. err)
-        -- when we fault we run the update function again after 1s, slowing it
-        -- down a bit so we don't flood the console with errors
-        return protected_wrapper, 1000
-    end
-    return protected_wrapper, 200
-end
-
-local function script_exit()
-    -- pre arm failure SCRIPT_NAME not Running
-    arming:set_aux_auth_failed(auth_id, SCRIPT_NAME .. " Not Running")
-    gcs_msg(MAV_SEVERITY_CRITICAL, "LUA SCRIPT EXIT   ... Need Reboot to Reinitialize")
-end
-
-
--- ******************* Main *******************
-if arming_check_init() then
-    return protected_wrapper, 10000
-end
-
-script_exit()
+local cx_msg = require("scripts.cx_utils.cx_msg")
+local cx_esc = require("scripts.cx_modules.esc")
+
+-- Create instances of modules
+cx_msg = cx_msg.new()
+
+-- Add modules that require Built-in-test (implemented in cx_modules)
+-- Each module should have the following functions:
+-- 1. new() - constructor
+-- 2. init() - initialize the module
+-- 3. update() - update the module
+-- 4. check_for_errors() - returns pre-arm checks/errors in the module
+--                       - built in test errors are managed by each module
+local modules = {
+    esc = cx_esc.new()
+}
+
+-- ******************* Functions *******************
+-- initialize function
+local function init()
+    -- initialize all modules
+    for _, module in pairs(modules) do
+        module:init()
+    end
+
+    cx_msg:send(cx_msg.MAV_SEVERITY_INFO, "LUA script initialized (" .. cx_aircraft.type .. ")")
+    return true
+end
+
+-- Pre-arm status check before arming
+local function check_prearm_status()
+    -- Track errors in modules
+    local modules_with_errors = {}
+    local total_errors = 0
+
+    for _, module in pairs(modules) do
+        local error_count = module:check_for_errors()
+        if error_count > 0 then
+            table.insert(modules_with_errors, { name = module.name, errors = error_count })
+            total_errors = total_errors + error_count
+        end
+    end
+
+    -- Handle error cases
+    if #modules_with_errors == 0 then
+        -- No modules have errors, clear error flag
+        cx_msg:clear_prearm_error()
+    elseif #modules_with_errors == 1 then
+        -- Only one module has errors
+        local module = modules_with_errors[1]
+        msg = module.name .. " (" .. module.errors .. " checks fail)"
+        cx_msg:set_prearm_error(msg)
+    else
+        -- More than one module has errors
+        msg = #modules_with_errors .. " modules (" .. total_errors .. " checks) failing. Check messages"
+        cx_msg:set_prearm_error(msg)
+    end
+end
+
+-- update function
+local function update()
+    -- update all modules
+    for _, module in pairs(modules) do
+        module:update()
+    end
+
+    check_prearm_status()
+end
+
+-- wrapper around update(). This calls update() and if update faults
+-- then an error is displayed, but the script is not stopped
+local function protected_wrapper()
+    local success, err = pcall(update)
+    if not success then
+        cx_msg:send(cx_msg.MAV_SEVERITY_ERROR, "Internal Error: " .. err)
+        -- when we fault we run the update function again after 1s, slowing it
+        -- down a bit so we don't flood the console with errors
+        return protected_wrapper, 1000
+    end
+    return protected_wrapper, 200
+end
+
+-- exit function
+local function script_exit()
+    -- pre arm failure SCRIPT_NAME not Running
+    arming:set_aux_auth_failed(auth_id, SCRIPT_NAME .. " Not Running")
+    cx_msg:send(cx_msg.MAV_SEVERITY_CRITICAL, "LUA SCRIPT EXIT   ... Need Reboot to Reinitialize")
+end
+
+
+-- ******************* Main *******************
+if init() then
+    return protected_wrapper, 10000
+end
+
+script_exit()
diff --git a/libraries/AP_HAL_ChibiOS/hwdef/CarbonixCommon/scripts/cx_modules/esc.lua b/libraries/AP_HAL_ChibiOS/hwdef/CarbonixCommon/scripts/cx_modules/esc.lua
new file mode 100644
index 0000000000..d0f8a09415
--- /dev/null
+++ b/libraries/AP_HAL_ChibiOS/hwdef/CarbonixCommon/scripts/cx_modules/esc.lua
@@ -0,0 +1,172 @@
+local cx_msg = require("scripts.cx_utils.cx_msg")
+local cx_aircraft = require("scripts.cx_utils.cx_aircraft")
+
+local ESC = {}
+ESC.__index = ESC
+
+-- Constructor
+function ESC.new()
+    local self = setmetatable({}, ESC)
+    self.name = "ESC"
+    
+    self.number_of_esc = 5 
+
+    -- CONSTANTS
+    self.ESC_WARMUP_TIME = 3000
+    self.ESC_RPM_THRESHOLD = 10
+    self.SERVO_OUT_THRESHOLD = 1010
+
+    -- Add a new table to store the warm-up end times for each ESC
+    self.esc_warmup_end_time = {}
+
+    self.srv_prv_telem_ms = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+    self.srv_telem_in_err_status  = {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}
+    self.srv_rpm_in_err_status  = {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}
+    
+    
+    -- Counters to debounce nil checks on esc rpm and servo output, this is a
+    -- workaround to avoid giving the pilot a critical warning for an unexplained
+    -- one-loop dropout we saw recently
+    self.NIL_WARN_THRESHOLD = 3
+    self.esc_rpm_nil_counter = {0, 0, 0, 0, 0}
+    self.servo_out_nil_counter = {0, 0, 0, 0, 0}
+
+    self.srv_number = {
+        [1] = {"Motor1", 33},
+        [2] = {"Motor2", 34},
+        [3] = {"Motor3", 35},
+        [4] = {"Motor4", 36},
+        [5] = {"Motor5", 70},
+        [6] = {"Motor6", 38},
+        [7] = {"Elevator", 19},
+        [8] = {"Rudder", 21},
+        [9] = {"GPIO", -1},
+        [10] = {"Script1", 94},
+        [11] = {"Aileron", 4}
+    }
+
+    return self
+end
+
+-- Get the number of ESCs based on the aircraft type
+function ESC:get_num_esc()
+    local aircraft_type = cx_aircraft.type
+    if aircraft_type == "Volanti" then
+        return 5
+    elseif aircraft_type == "Ottano" then
+        return 4
+    else
+        return 0
+    end
+end
+
+-- Initialize the ESC module
+function ESC:init()
+    self.number_of_esc = self.get_num_esc()
+    for i = 1, self.number_of_esc do
+        self.esc_warmup_end_time[i] = nil
+        self.srv_prv_telem_ms[i] = 0
+    end
+    cx_msg:send(cx_msg.MAV_SEVERITY_INFO, "ESC init (" .. cx_aircraft.type .. ": " .. self.number_of_esc .. " ESCs)")   
+end
+
+
+-- Call this function whenever a motor starts running
+function ESC:esc_is_started(i)
+    -- Set the warm-up end time for this ESC to 3 seconds from now
+    self.esc_warmup_end_time[i] = millis() + self.ESC_WARMUP_TIME
+end
+
+-- Call this function whenever a motor stops running
+function ESC:esc_is_stopped(i)
+    -- Clear the warm-up end time for this ESC
+    self.esc_warmup_end_time[i] = nil
+end
+
+function ESC:update()
+    for i = 1, self.number_of_esc  do
+        local esc_last_telem_data_ms = esc_telem:get_last_telem_data_ms(i-1):toint()
+        local esc_rpm = esc_telem:get_rpm(i-1)
+        local servo_out = SRV_Channels:get_output_pwm(self.srv_number[i][2])
+        -- Telem data timestamp check
+        if not esc_last_telem_data_ms or esc_last_telem_data_ms == 0 or esc_last_telem_data_ms == self.srv_prv_telem_ms[i] then
+            if self.srv_telem_in_err_status[i] == false then
+                cx_msg:send(cx_msg.MAV_SEVERITY_CRITICAL, "ESC " .. i .. " Telemetry Lost")
+                self.srv_telem_in_err_status[i] = true
+            end
+        -- Nil check for RPM reading
+        elseif not esc_rpm then
+            self.esc_rpm_nil_counter[i] = self.esc_rpm_nil_counter[i] + 1
+            if self.esc_rpm_nil_counter[i] < self.NIL_WARN_THRESHOLD then
+                cx_msg:send(cx_msg.MAV_SEVERITY_INFO, "ESC " .. i .. " RPM nil")
+            elseif self.srv_rpm_in_err_status[i] == false then
+                cx_msg:send(cx_msg.MAV_SEVERITY_CRITICAL, "ESC " .. i .. " RPM nil")
+                self.srv_telem_in_err_status[i] = true
+            end
+        -- Nil check for servo output
+        elseif not servo_out then
+            self.servo_out_nil_counter[i] = self.servo_out_nil_counter[i] + 1
+            if self.servo_out_nil_counter[i] < self.NIL_WARN_THRESHOLD then
+                cx_msg:send(cx_msg.MAV_SEVERITY_INFO, "ESC " .. i .. " Servo Out nil")
+            elseif self.srv_rpm_in_err_status[i] == false then
+                cx_msg:send(cx_msg.MAV_SEVERITY_CRITICAL, "ESC " .. i .. " Servo Out nil")
+                self.srv_telem_in_err_status[i] = true
+            end
+        -- Telemetry data is fresh and valid
+        else
+            if self.srv_telem_in_err_status[i] == true then
+                cx_msg:send(cx_msg.MAV_SEVERITY_INFO, "ESC " .. i .. " Telemetry Recovered")
+                self.srv_telem_in_err_status[i] = false
+                self.servo_out_nil_counter[i] = 0
+                self.esc_rpm_nil_counter[i] = 0
+            end
+            -- If armed, check that the motor is actually turning when it is commanded to
+            if arming:is_armed() then
+                -- If the PWM is below the threshold, it is okay for the motor to be stopped
+                if servo_out < self.SERVO_OUT_THRESHOLD then
+                    self.esc_is_stopped(i)
+                -- If the PWM has just gone above the threshold, start the warm-up timer
+                elseif servo_out > self.SERVO_OUT_THRESHOLD and not self.esc_warmup_end_time[i]  then
+                    self.esc_is_started(i)
+                -- If the motor is running, and the warmup timer has expired, check that the motor is spinning
+                elseif self.esc_warmup_end_time[i] and millis() > self.esc_warmup_end_time[i] then
+                    if servo_out > self.SERVO_OUT_THRESHOLD and esc_rpm < self.ESC_RPM_THRESHOLD then
+                        if self.srv_rpm_in_err_status[i] == false then
+                            cx_msg:send(cx_msg.MAV_SEVERITY_CRITICAL, "ESC " .. i .. " RPM Drop")
+                            self.srv_rpm_in_err_status[i] = true
+                        end
+                    else
+                        if self.srv_rpm_in_err_status[i] == true then
+                            cx_msg:send(cx_msg.MAV_SEVERITY_INFO, "ESC " .. i .. " RPM Recovered")
+                            self.srv_rpm_in_err_status[i] = false
+                        end
+                    end
+                end
+            end
+        end
+        -- Update srv_prv_telem_ms[i] if it had valid data this loop
+        if esc_last_telem_data_ms and esc_last_telem_data_ms ~= 0 then
+            self.srv_prv_telem_ms[i] = esc_last_telem_data_ms
+        end
+    end
+end
+
+-- Return error code
+function ESC:check_for_errors()
+    local error_count = 0
+    for _, status in ipairs(self.srv_telem_in_err_status) do
+        if status then
+            error_count = error_count + 1
+        end
+    end
+    for _, status in ipairs(self.srv_rpm_in_err_status) do
+        if status then
+            error_count = error_count + 1
+        end
+    end
+
+    -- No errors found
+    return error_count
+end
+
+return ESC
diff --git a/libraries/AP_HAL_ChibiOS/hwdef/CarbonixCommon/scripts/cx_utils/cx_aircraft.lua b/libraries/AP_HAL_ChibiOS/hwdef/CarbonixCommon/scripts/cx_utils/cx_aircraft.lua
new file mode 100644
index 0000000000..b46339c7f8
--- /dev/null
+++ b/libraries/AP_HAL_ChibiOS/hwdef/CarbonixCommon/scripts/cx_utils/cx_aircraft.lua
@@ -0,0 +1,8 @@
+-- Define the Aircraft class
+local cx_aircraft = {}
+cx_aircraft.__index = cx_aircraft
+
+-- set the aircraft type
+cx_aircraft.type = "Volanti"
+
+return cx_aircraft
\ No newline at end of file
diff --git a/libraries/AP_HAL_ChibiOS/hwdef/CarbonixCommon/scripts/cx_utils/cx_msg.lua b/libraries/AP_HAL_ChibiOS/hwdef/CarbonixCommon/scripts/cx_utils/cx_msg.lua
new file mode 100644
index 0000000000..5ead9136c1
--- /dev/null
+++ b/libraries/AP_HAL_ChibiOS/hwdef/CarbonixCommon/scripts/cx_utils/cx_msg.lua
@@ -0,0 +1,47 @@
+local cx_msg = {}
+cx_msg.__index = cx_msg
+
+-- MACROS
+SCRIPT_NAME = 'CX_BIT'
+
+-- MAVLink severity level definitions
+cx_msg.MAV_SEVERITY_CRITICAL = 2
+cx_msg.MAV_SEVERITY_ERROR = 3
+cx_msg.MAV_SEVERITY_WARNING = 4
+cx_msg.MAV_SEVERITY_INFO = 6
+
+-- auth id for prearm check
+function cx_msg:new()
+    self.prearm_msg = nil
+    self.auth_id = arming:get_aux_auth_id()
+    assert(self.auth_id, SCRIPT_NAME .. ": could not get prearm check auth id")
+    return self
+end
+
+function cx_msg:set_prearm_error(msg)
+    if self.prearm_msg == nil or self.prearm_msg ~= msg then
+        self.prearm_msg = msg
+        arming:set_aux_auth_failed(self.auth_id, msg)
+        self:send(self.MAV_SEVERITY_WARNING, msg)
+    end
+end
+
+function cx_msg:clear_prearm_error()
+    if self.prearm_msg ~= nil then
+        self.prearm_msg = nil
+        arming:set_aux_auth_passed(self.auth_id)
+        self:send(self.MAV_SEVERITY_INFO, "Prearm check passed")
+    end
+end
+
+-- wrapper for gcs:send_text(). Helps identify cx_bit messages
+function cx_msg:send(severity, txt)
+    if type(severity) == 'string' then
+        -- allow just a string to be passed for simple/routine messages
+        txt      = severity
+        severity = self.MAV_SEVERITY_INFO
+    end
+    gcs:send_text(severity, string.format('%s: %s', SCRIPT_NAME, txt))
+end
+
+return cx_msg
diff --git a/libraries/AP_HAL_ChibiOS/hwdef/CubeOrange-Ottano/scripts/cx_modules/esc.lua b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrange-Ottano/scripts/cx_modules/esc.lua
new file mode 120000
index 0000000000..56e57c74a1
--- /dev/null
+++ b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrange-Ottano/scripts/cx_modules/esc.lua
@@ -0,0 +1 @@
+../../../CarbonixCommon/scripts/cx_modules/esc.lua
\ No newline at end of file
diff --git a/libraries/AP_HAL_ChibiOS/hwdef/CubeOrange-Ottano/scripts/cx_utils/cx_aircraft.lua b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrange-Ottano/scripts/cx_utils/cx_aircraft.lua
new file mode 100644
index 0000000000..d1ae333c68
--- /dev/null
+++ b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrange-Ottano/scripts/cx_utils/cx_aircraft.lua
@@ -0,0 +1,8 @@
+-- Define the Aircraft class
+local cx_aircraft = {}
+cx_aircraft.__index = cx_aircraft
+
+-- set the aircraft type
+cx_aircraft.type = "Ottano"
+
+return cx_aircraft
\ No newline at end of file
diff --git a/libraries/AP_HAL_ChibiOS/hwdef/CubeOrange-Ottano/scripts/cx_utils/cx_msg.lua b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrange-Ottano/scripts/cx_utils/cx_msg.lua
new file mode 120000
index 0000000000..93be9da6bf
--- /dev/null
+++ b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrange-Ottano/scripts/cx_utils/cx_msg.lua
@@ -0,0 +1 @@
+../../../CarbonixCommon/scripts/cx_utils/cx_msg.lua
\ No newline at end of file
diff --git a/libraries/AP_HAL_ChibiOS/hwdef/CubeOrange-Volanti/scripts/cx_modules/esc.lua b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrange-Volanti/scripts/cx_modules/esc.lua
new file mode 120000
index 0000000000..56e57c74a1
--- /dev/null
+++ b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrange-Volanti/scripts/cx_modules/esc.lua
@@ -0,0 +1 @@
+../../../CarbonixCommon/scripts/cx_modules/esc.lua
\ No newline at end of file
diff --git a/libraries/AP_HAL_ChibiOS/hwdef/CubeOrange-Volanti/scripts/cx_utils/cx_aircraft.lua b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrange-Volanti/scripts/cx_utils/cx_aircraft.lua
new file mode 100644
index 0000000000..b46339c7f8
--- /dev/null
+++ b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrange-Volanti/scripts/cx_utils/cx_aircraft.lua
@@ -0,0 +1,8 @@
+-- Define the Aircraft class
+local cx_aircraft = {}
+cx_aircraft.__index = cx_aircraft
+
+-- set the aircraft type
+cx_aircraft.type = "Volanti"
+
+return cx_aircraft
\ No newline at end of file
diff --git a/libraries/AP_HAL_ChibiOS/hwdef/CubeOrange-Volanti/scripts/cx_utils/cx_msg.lua b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrange-Volanti/scripts/cx_utils/cx_msg.lua
new file mode 120000
index 0000000000..93be9da6bf
--- /dev/null
+++ b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrange-Volanti/scripts/cx_utils/cx_msg.lua
@@ -0,0 +1 @@
+../../../CarbonixCommon/scripts/cx_utils/cx_msg.lua
\ No newline at end of file
diff --git a/libraries/AP_HAL_ChibiOS/hwdef/CubeOrangePlus-Ottano/scripts/cx_modules/esc.lua b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrangePlus-Ottano/scripts/cx_modules/esc.lua
new file mode 120000
index 0000000000..56e57c74a1
--- /dev/null
+++ b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrangePlus-Ottano/scripts/cx_modules/esc.lua
@@ -0,0 +1 @@
+../../../CarbonixCommon/scripts/cx_modules/esc.lua
\ No newline at end of file
diff --git a/libraries/AP_HAL_ChibiOS/hwdef/CubeOrangePlus-Ottano/scripts/cx_utils/cx_aircraft.lua b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrangePlus-Ottano/scripts/cx_utils/cx_aircraft.lua
new file mode 100644
index 0000000000..d1ae333c68
--- /dev/null
+++ b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrangePlus-Ottano/scripts/cx_utils/cx_aircraft.lua
@@ -0,0 +1,8 @@
+-- Define the Aircraft class
+local cx_aircraft = {}
+cx_aircraft.__index = cx_aircraft
+
+-- set the aircraft type
+cx_aircraft.type = "Ottano"
+
+return cx_aircraft
\ No newline at end of file
diff --git a/libraries/AP_HAL_ChibiOS/hwdef/CubeOrangePlus-Ottano/scripts/cx_utils/cx_msg.lua b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrangePlus-Ottano/scripts/cx_utils/cx_msg.lua
new file mode 120000
index 0000000000..93be9da6bf
--- /dev/null
+++ b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrangePlus-Ottano/scripts/cx_utils/cx_msg.lua
@@ -0,0 +1 @@
+../../../CarbonixCommon/scripts/cx_utils/cx_msg.lua
\ No newline at end of file
diff --git a/libraries/AP_HAL_ChibiOS/hwdef/CubeOrangePlus-Volanti/scripts/cx_modules/esc.lua b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrangePlus-Volanti/scripts/cx_modules/esc.lua
new file mode 120000
index 0000000000..56e57c74a1
--- /dev/null
+++ b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrangePlus-Volanti/scripts/cx_modules/esc.lua
@@ -0,0 +1 @@
+../../../CarbonixCommon/scripts/cx_modules/esc.lua
\ No newline at end of file
diff --git a/libraries/AP_HAL_ChibiOS/hwdef/CubeOrangePlus-Volanti/scripts/cx_utils/cx_aircraft.lua b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrangePlus-Volanti/scripts/cx_utils/cx_aircraft.lua
new file mode 100644
index 0000000000..b46339c7f8
--- /dev/null
+++ b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrangePlus-Volanti/scripts/cx_utils/cx_aircraft.lua
@@ -0,0 +1,8 @@
+-- Define the Aircraft class
+local cx_aircraft = {}
+cx_aircraft.__index = cx_aircraft
+
+-- set the aircraft type
+cx_aircraft.type = "Volanti"
+
+return cx_aircraft
\ No newline at end of file
diff --git a/libraries/AP_HAL_ChibiOS/hwdef/CubeOrangePlus-Volanti/scripts/cx_utils/cx_msg.lua b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrangePlus-Volanti/scripts/cx_utils/cx_msg.lua
new file mode 120000
index 0000000000..93be9da6bf
--- /dev/null
+++ b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrangePlus-Volanti/scripts/cx_utils/cx_msg.lua
@@ -0,0 +1 @@
+../../../CarbonixCommon/scripts/cx_utils/cx_msg.lua
\ No newline at end of file