--[[ Lua script to simulate a HL connection over a UART Setup: This script requires 1 serial port: A "Script" serial port to connect directly to the GCS Usage: Use the MAVLink High Latency Control ("link hl on|off" in MAVProxy) to control whether to send or not The script will, however, automatically enable MAVLink High Latency Control upon start Caveats: -This will send HIGH_LATENCY2 packets in place of HEARTBEAT packets -A single HIGH_LATENCY2 packet will be send every 5 sec -MAVLink 1 will be used, as it's slightly more efficient (50 vs 52 bytes for a HL2 message) -The param SCR_VM_I_COUNT may need to be increased in some circumstances Written by Stephen Dade (stephen_dade@hotmail.com) --]] ---@diagnostic disable: need-check-nil ---@diagnostic disable: cast-local-type local port = serial:find_serial(0) if not port then gcs:send_text(0, "No Scripting Serial Port") return end port:begin(19200) port:set_flow_control(0) local time_last_tx = millis():tofloat() * 0.001 -- enable high latency mode from here, instead of having to enable from GCS gcs:enable_high_latency_connections(true) --[[ Lua Object for decoding and encoding MAVLink (V1 only) messages --]] local function MAVLinkProcessor() -- public fields local self = { -- define MAVLink message id's COMMAND_LONG = 76, COMMAND_INT = 75, HIGH_LATENCY2 = 235, MISSION_ITEM_INT = 73, SET_MODE = 11 } -- private fields local _mavbuffer = "" local _mavresult = {} local _payload_len = 0 local _mavdecodestate = 0 -- 0=looking for marker, 1=getting header,2=getting payload,3=getting crc PROTOCOL_MARKER_V1 = 0xFE HEADER_LEN_V1 = 6 local _txseqid = 0 -- AUTOGEN from MAVLink generator local _crc_extra = {} _crc_extra[75] = 0x9e _crc_extra[76] = 0x98 _crc_extra[235] = 0xb3 _crc_extra[73] = 0x26 _crc_extra[11] = 0x59 local _messages = {} _messages[75] = { -- COMMAND_INT {"param1", "> 8) ~ (tmp << 8) ~ (tmp << 3) ~ (tmp >> 4) crc = crc & 0xFFFF end return string.pack(" 263 then _mavbuffer = "" end return false end function self.bytesToString(buf, start, stop) local ret = "" for idx = start, stop do ret = ret .. string.format("0x%x ", buf:byte(idx), 1, -1) .. " " end return ret end function self.createMAVLink(message, msgid) -- generate a mavlink message (V1 only) -- create the payload local message_map = _messages[msgid] if not message_map then -- we don't know how to encode this message, bail on it gcs:send_text(3, "Unknown MAVLink message " .. msgid) return nil end local packString = "<" local packedTable = {} local packedIndex = 1 for i, v in ipairs(message_map) do if v[3] then packString = (packString .. string.rep(string.sub(v[2], 2), v[3])) for j = 1, v[3] do packedTable[packedIndex] = message[message_map[i][1]][j] packedIndex = packedIndex + 1 end else packString = (packString .. string.sub(v[2], 2)) packedTable[packedIndex] = message[message_map[i][1]] packedIndex = packedIndex + 1 end end local payload = string.pack(packString, table.unpack(packedTable)) -- create the header. Assume componentid of 1 local header = string.pack(' 0 do local byte = port:read() if mavlink.parseMAVLink(byte) then break end end -- send HL2 packet every 5 sec if gcs:get_high_latency_status() and (millis():tofloat() * 0.001) - time_last_tx > 5 then -- update HL2 packet hl2.timestamp = millis():tofloat() local position = ahrs:get_location() local wind = ahrs:wind_estimate() if position then hl2.latitude = tonumber(position:lat()) hl2.longitude = tonumber(position:lng()) hl2.altitude = math.floor(tonumber(position:alt()) * 0.01) end if wind then wind_xy = Vector2f() wind_xy:x(wind:x()) wind_xy:y(wind:y()) hl2.windspeed = math.abs(math.floor(wind_xy:length() * 5)) hl2.wind_heading = math.floor(wrap_360(wind_xy:angle()) / 2) end hl2.custom_mode = vehicle:get_mode() if vehicle:get_wp_distance_m() ~= nil then hl2.target_distance = math.floor(vehicle:get_wp_distance_m() / 10) end if mission:get_current_nav_index() ~= nil then hl2.wp_num = mission:get_current_nav_index() end if vehicle:get_wp_bearing_deg() ~= nil then hl2.target_heading = math.floor(wrap_360( vehicle:get_wp_bearing_deg()) / 2) end -- failure flags hl2.failure_flags = 0 if not ahrs:healthy() then hl2.failure_flags = hl2.failure_flags + 4096 -- HL_FAILURE_FLAG_ESTIMATOR end if battery:num_instances() > 0 and not battery:healthy(0) then hl2.failure_flags = hl2.failure_flags + 128 -- HL_FAILURE_FLAG_BATTERY end if gps:num_sensors() > 0 and gps:status(0) <= gps.NO_FIX then hl2.failure_flags = hl2.failure_flags + 1 -- HL_FAILURE_FLAG_GPS end if (FWVersion:type() == 2 or FWVersion:type() == 3) and terrain:status() == terrain.TerrainStatusUnhealthy then -- only for copter and plane hl2.failure_flags = hl2.failure_flags + 64 -- HL_FAILURE_FLAG_TERRAIN end if not rc:has_valid_input() then hl2.failure_flags = hl2.failure_flags + 256 -- HL_FAILURE_FLAG_RC_RECEIVER end hl2.heading = math.floor(wrap_360(math.deg(ahrs:get_yaw())) / 2) hl2.throttle = math.floor(gcs:get_hud_throttle()) if ahrs:airspeed_estimate() ~= nil then hl2.airspeed = math.abs(math.floor(ahrs:airspeed_estimate() * 5)) end -- hl2.airspeed_sp = 0 hl2.groundspeed = math.abs(math.floor( ahrs:groundspeed_vector():length() * 5)) hl2.temperature_air = math.floor(baro:get_external_temperature()) if battery:num_instances() > 0 and battery:capacity_remaining_pct(0) ~= nil then hl2.battery = battery:capacity_remaining_pct(0) else hl2.battery = 0 end -- just sending armed state here for simplicity. Flight mode is in the custom_mode field if arming:is_armed() then hl2.custom0 = 129 -- MAV_MODE_FLAG_SAFETY_ARMED + MAV_MODE_FLAG_CUSTOM_MODE_ENABLED else hl2.custom0 = 1 -- MAV_MODE_FLAG_CUSTOM_MODE_ENABLED end local newpkt = mavlink.createMAVLink(hl2, mavlink.HIGH_LATENCY2) gcs:send_text(3, "Sent HL2 packet, size: " .. tostring(#newpkt) .. ", seq " .. mavlink.getSeqID()) for idx = 1, #newpkt do port:write(newpkt:byte(idx)) end time_last_tx = millis():tofloat() * 0.001 end return HLSatcom, 100 end return HLSatcom, 100