From 0b048b4b42a3a2e3b3a73da98a2485406fe0acdb Mon Sep 17 00:00:00 2001 From: Stephen Dade Date: Mon, 20 Feb 2023 16:04:19 +1100 Subject: [PATCH] AP_Scripting: Add Mavlink example --- libraries/AP_Scripting/examples/MAVLinkHL.lua | 460 ++++++++++++++++++ 1 file changed, 460 insertions(+) create mode 100644 libraries/AP_Scripting/examples/MAVLinkHL.lua diff --git a/libraries/AP_Scripting/examples/MAVLinkHL.lua b/libraries/AP_Scripting/examples/MAVLinkHL.lua new file mode 100644 index 0000000000..ebe9c6262a --- /dev/null +++ b/libraries/AP_Scripting/examples/MAVLinkHL.lua @@ -0,0 +1,460 @@ +--[[ +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) +--]] +local port = serial:find_serial(0) + +if not port or baud == 0 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 + } + + -- 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 + _crc_extra = {} + _crc_extra[75] = 0x9e + _crc_extra[76] = 0x98 + _crc_extra[235] = 0xb3 + _crc_extra[73] = 0x26 + + _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()) + hl2.battery = battery:capacity_remaining_pct(0) + + -- 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 +