2 #include "ardupilot_protocol.h"
4 void convert_mavlink_to_crsf_telem(uint8_t *CRSFinBuffer
, uint8_t count
, Handset
*handset
)
6 // Store the relative altitude for GPS altitude
7 static int32_t relative_alt
= 0;
9 for (uint8_t i
= 0; i
< count
; i
++)
11 mavlink_message_t msg
;
12 mavlink_status_t status
;
13 bool have_message
= mavlink_frame_char(MAVLINK_COMM_0
, CRSFinBuffer
[CRSF_FRAME_NOT_COUNTED_BYTES
+ i
], &msg
, &status
);
14 // convert mavlink messages to CRSF messages
17 // Only parse heartbeats from the autopilot (not GCS)
18 if (msg
.compid
!= MAV_COMP_ID_AUTOPILOT1
)
24 case MAVLINK_MSG_ID_BATTERY_STATUS
: {
25 mavlink_battery_status_t battery_status
;
26 mavlink_msg_battery_status_decode(&msg
, &battery_status
);
27 if (battery_status
.id
!= 0) {
30 CRSF_MK_FRAME_T(crsf_sensor_battery_t
)
33 crsfbatt
.p
.voltage
= htobe16(battery_status
.voltages
[0] / 100);
35 crsfbatt
.p
.current
= htobe16(battery_status
.current_battery
/ 10);
36 crsfbatt
.p
.capacity
= htobe32(battery_status
.current_consumed
) & 0x0FFF;
37 crsfbatt
.p
.remaining
= battery_status
.battery_remaining
;
38 CRSF::SetHeaderAndCrc((uint8_t *)&crsfbatt
, CRSF_FRAMETYPE_BATTERY_SENSOR
, CRSF_FRAME_SIZE(sizeof(crsf_sensor_battery_t
)), CRSF_ADDRESS_CRSF_TRANSMITTER
);
39 handset
->sendTelemetryToTX((uint8_t *)&crsfbatt
);
42 case MAVLINK_MSG_ID_GPS_RAW_INT
: {
43 mavlink_gps_raw_int_t gps_int
;
44 mavlink_msg_gps_raw_int_decode(&msg
, &gps_int
);
45 CRSF_MK_FRAME_T(crsf_sensor_gps_t
)
47 // We use altitude relative to home for GPS altitude, by default, but we can also use GPS altitude if USE_MAVLINK_GPS_ALTITUDE is defined
48 #if defined(USE_MAVLINK_GPS_ALTITUDE)
49 // mm -> meters + 1000
50 crsfgps
.p
.altitude
= htobe16(gps_int
.alt
/ 1000 + 1000);
52 crsfgps
.p
.altitude
= htobe16((uint16_t)(relative_alt
/ 1000 + 1000));
55 crsfgps
.p
.groundspeed
= htobe16(gps_int
.vel
* 36 / 100);
56 crsfgps
.p
.latitude
= htobe32(gps_int
.lat
);
57 crsfgps
.p
.longitude
= htobe32(gps_int
.lon
);
58 crsfgps
.p
.gps_heading
= htobe16(gps_int
.cog
);
59 crsfgps
.p
.satellites_in_use
= gps_int
.satellites_visible
;
60 CRSF::SetHeaderAndCrc((uint8_t *)&crsfgps
, CRSF_FRAMETYPE_GPS
, CRSF_FRAME_SIZE(sizeof(crsf_sensor_gps_t
)), CRSF_ADDRESS_CRSF_TRANSMITTER
);
61 handset
->sendTelemetryToTX((uint8_t *)&crsfgps
);
64 case MAVLINK_MSG_ID_GLOBAL_POSITION_INT
: {
65 mavlink_global_position_int_t global_pos
;
66 mavlink_msg_global_position_int_decode(&msg
, &global_pos
);
67 CRSF_MK_FRAME_T(crsf_sensor_vario_t
)
69 // store relative altitude for GPS Alt so we don't have 2 Alt sensors
70 relative_alt
= global_pos
.relative_alt
;
71 crsfvario
.p
.verticalspd
= htobe16(-global_pos
.vz
); // MAVLink vz is positive down
72 CRSF::SetHeaderAndCrc((uint8_t *)&crsfvario
, CRSF_FRAMETYPE_VARIO
, CRSF_FRAME_SIZE(sizeof(crsf_sensor_vario_t
)), CRSF_ADDRESS_CRSF_TRANSMITTER
);
73 handset
->sendTelemetryToTX((uint8_t *)&crsfvario
);
76 case MAVLINK_MSG_ID_ATTITUDE
: {
77 mavlink_attitude_t attitude
;
78 mavlink_msg_attitude_decode(&msg
, &attitude
);
79 CRSF_MK_FRAME_T(crsf_sensor_attitude_t
)
81 crsfatt
.p
.pitch
= htobe16(attitude
.pitch
* 10000); // in Betaflight & INAV, CRSF positive pitch is nose down, but in Ardupilot, it's nose up - we follow Ardupilot
82 crsfatt
.p
.roll
= htobe16(attitude
.roll
* 10000);
83 crsfatt
.p
.yaw
= htobe16(attitude
.yaw
* 10000);
84 CRSF::SetHeaderAndCrc((uint8_t *)&crsfatt
, CRSF_FRAMETYPE_ATTITUDE
, CRSF_FRAME_SIZE(sizeof(crsf_sensor_attitude_t
)), CRSF_ADDRESS_CRSF_TRANSMITTER
);
85 handset
->sendTelemetryToTX((uint8_t *)&crsfatt
);
88 case MAVLINK_MSG_ID_HEARTBEAT
: {
89 mavlink_heartbeat_t heartbeat
;
90 mavlink_msg_heartbeat_decode(&msg
, &heartbeat
);
91 CRSF_MK_FRAME_T(crsf_flight_mode_t
)
93 ap_flight_mode_name4(crsffm
.p
.flight_mode
, ap_vehicle_from_mavtype(heartbeat
.type
), heartbeat
.custom_mode
);
94 // if we have a good flight mode, and we're armed, suffix the flight mode with a * - see Ardupilot's AP_CRSF_Telem::calc_flight_mode()
95 size_t len
= strnlen(crsffm
.p
.flight_mode
, sizeof(crsffm
.p
.flight_mode
));
96 if (len
> 0 && (len
+ 1 < sizeof(crsffm
.p
.flight_mode
)) && (heartbeat
.base_mode
& MAV_MODE_FLAG_SAFETY_ARMED
)) {
97 crsffm
.p
.flight_mode
[len
] = '*';
98 crsffm
.p
.flight_mode
[len
+ 1] = '\0';
100 CRSF::SetHeaderAndCrc((uint8_t *)&crsffm
, CRSF_FRAMETYPE_FLIGHT_MODE
, CRSF_FRAME_SIZE(sizeof(crsffm
)), CRSF_ADDRESS_CRSF_TRANSMITTER
);
101 handset
->sendTelemetryToTX((uint8_t *)&crsffm
);
109 bool isThisAMavPacket(uint8_t *buffer
, uint16_t bufferSize
)
111 for (uint8_t i
= 0; i
< bufferSize
; ++i
)
113 uint8_t c
= buffer
[i
];
115 mavlink_message_t msg
;
116 mavlink_status_t status
;
118 // Try parse a mavlink message
119 if (mavlink_frame_char(MAVLINK_COMM_0
, c
, &msg
, &status
))
121 // Message decoded successfully
128 uint16_t buildMAVLinkELRSModeChange(uint8_t mode
, uint8_t *buffer
)
130 constexpr uint8_t ELRS_MODE_CHANGE
= 0x8;
131 mavlink_command_int_t commandMsg
;
132 commandMsg
.target_system
= 255;
133 commandMsg
.target_component
= MAV_COMP_ID_UDP_BRIDGE
;
134 commandMsg
.command
= MAV_CMD_USER_1
;
135 commandMsg
.param1
= ELRS_MODE_CHANGE
;
136 commandMsg
.param2
= mode
;
137 mavlink_message_t msg
;
138 mavlink_msg_command_int_encode(255, MAV_COMP_ID_TELEMETRY_RADIO
, &msg
, &commandMsg
);
139 uint16_t len
= mavlink_msg_to_send_buffer(buffer
, &msg
);