2 ******************************************************************************
3 * @addtogroup OpenPilotModules OpenPilot Modules
5 * @addtogroup UAVOMSPBridge UAVO to MSP Bridge Module
8 * @file uavomspbridge.c
9 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2016.
10 * Tau Labs, http://taulabs.org, Copyright (C) 2015
11 * dRonin, http://dronin.org Copyright (C) 2015-2016
12 * @brief Bridges selected UAVObjects to MSP for MWOSD and the like.
14 * @see The GNU Public License (GPL) Version 3
16 *****************************************************************************/
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 3 of the License, or
21 * (at your option) any later version.
23 * This program is distributed in the hope that it will be useful, but
24 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
25 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
28 * You should have received a copy of the GNU General Public License along
29 * with this program; if not, write to the Free Software Foundation, Inc.,
30 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 * Additional note on redistribution: The copyright and license notices above
33 * must be maintained in each individual source file that is a derivative work
34 * of this source file; otherwise redistribution is prohibited.
37 #include "openpilot.h"
38 #include "receiverstatus.h"
39 #include "hwsettings.h"
40 #include "flightmodesettings.h"
41 #include "flightbatterysettings.h"
42 #include "flightbatterystate.h"
43 #include "gpspositionsensor.h"
44 #include "manualcontrolcommand.h"
45 #include "manualcontrolsettings.h"
46 #include "oplinkstatus.h"
47 #include "accessorydesired.h"
48 #include "attitudestate.h"
49 #include "airspeedstate.h"
50 #include "actuatorsettings.h"
51 #include "actuatordesired.h"
52 #include "flightstatus.h"
53 #include "systemstats.h"
54 #include "systemalarms.h"
55 #include "takeofflocation.h"
56 #include "homelocation.h"
57 #include "positionstate.h"
58 #include "velocitystate.h"
59 #include "stabilizationdesired.h"
61 #include "stabilizationsettings.h"
62 #include "stabilizationbank.h"
63 #include "stabilizationsettingsbank1.h"
64 #include "stabilizationsettingsbank2.h"
65 #include "stabilizationsettingsbank3.h"
67 #include "objectpersistence.h"
69 #include "pios_sensors.h"
72 #define PIOS_INCLUDE_MSP_BRIDGE
74 #if defined(PIOS_INCLUDE_MSP_BRIDGE)
76 // oplink rssi - absolute low : -127 dBm noise floor (set by software when link is completely lost)
77 // in reality : around -110 dBm
78 // max: various articles found on web quote -10 dBm as maximum received signal power.
80 #define OPLINK_LOW_RSSI -110
81 #define OPLINK_HIGH_RSSI -10
83 #define MSP_SENSOR_ACC (1 << 0)
84 #define MSP_SENSOR_BARO (1 << 1)
85 #define MSP_SENSOR_MAG (1 << 2)
86 #define MSP_SENSOR_GPS (1 << 3)
87 #define MSP_SENSOR_SONAR (1 << 4)
89 // Magic numbers copied from mwosd
90 #define MSP_IDENT 100 // multitype + multiwii version + protocol version + capability variable
91 #define MSP_STATUS 101 // cycletime & errors_count & sensor present & box activation & current setting number
92 #define MSP_RAW_IMU 102 // 9 DOF
93 #define MSP_SERVO 103 // 8 servos
94 #define MSP_MOTOR 104 // 8 motors
95 #define MSP_RC 105 // 8 rc chan and more
96 #define MSP_RAW_GPS 106 // fix, numsat, lat, lon, alt, speed, ground course
97 #define MSP_COMP_GPS 107 // distance home, direction home
98 #define MSP_ATTITUDE 108 // 2 angles 1 heading
99 #define MSP_ALTITUDE 109 // altitude, variometer
100 #define MSP_ANALOG 110 // vbat, powermetersum, rssi if available on RX
101 #define MSP_RC_TUNING 111 // rc rate, rc expo, rollpitch rate, yaw rate, dyn throttle PID
102 #define MSP_PID 112 // P I D coeff (10 are used currently)
103 #define MSP_BOX 113 // BOX setup (number is dependant of your setup)
104 #define MSP_MISC 114 // powermeter trig
105 #define MSP_MOTOR_PINS 115 // which pins are in use for motors & servos, for GUI
106 #define MSP_BOXNAMES 116 // the aux switch names
107 #define MSP_PIDNAMES 117 // the PID names
108 #define MSP_BOXIDS 119 // get the permanent IDs associated to BOXes
109 #define MSP_NAV_STATUS 121 // Returns navigation status
110 #define MSP_CELLS 130 // FrSky SPort Telemtry
111 #define MSP_ALARMS 242 // Alarm request
113 #define MSP_SET_PID 202 // set P I D coeff
117 MSP_BOX_ID_ANGLE
, // mode.stable (attitude??) - [sensor icon] [ fligth mode icon ]
118 MSP_BOX_ID_HORIZON
, // [sensor icon] [ flight mode icon ]
119 MSP_BOX_ID_BARO
, // [sensor icon]
121 MSP_BOX_ID_MAG
, // [sensor icon]
124 MSP_BOX_ID_CAMSTAB
, // [gimbal icon]
126 MSP_BOX_ID_GPSHOME
, // [ flight mode icon ]
127 MSP_BOX_ID_GPSHOLD
, // [ flight mode icon ]
128 MSP_BOX_ID_PASSTHRU
, // [ flight mode icon ]
132 MSP_BOX_ID_LLIGHTS
, // landing lights
135 MSP_BOX_ID_OSD_SWITCH
, // OSD on or off, maybe.
136 MSP_BOX_ID_GPSMISSION
, // [ flight mode icon ]
137 MSP_BOX_ID_GPSLAND
, // [ flight mode icon ]
139 MSP_BOX_ID_AIR
= 28, // this box will add AIRMODE icon to flight mode.
140 MSP_BOX_ID_ACROPLUS
= 29, // this will add PLUS icon to ACRO. ACRO = !ANGLE && !HORIZON
145 MSP_STATUS_FLIGHTMODE_STABILIZED
,
146 MSP_STATUS_FLIGHTMODE_HORIZON
,
147 MSP_STATUS_SENSOR_BARO
,
148 MSP_STATUS_SENSOR_MAG
,
149 MSP_STATUS_MISC_GIMBAL
,
150 MSP_STATUS_FLIGHTMODE_GPSHOME
,
151 MSP_STATUS_FLIGHTMODE_GPSHOLD
,
152 MSP_STATUS_FLIGHTMODE_GPSMISSION
,
153 MSP_STATUS_FLIGHTMODE_GPSLAND
,
154 MSP_STATUS_FLIGHTMODE_PASSTHRU
,
155 MSP_STATUS_OSD_SWITCH
,
156 MSP_STATUS_ICON_AIRMODE
,
157 MSP_STATUS_ICON_ACROPLUS
,
160 static const uint8_t msp_boxes
[] = {
161 [MSP_STATUS_ARMED
] = MSP_BOX_ID_ARM
,
162 [MSP_STATUS_FLIGHTMODE_STABILIZED
] = MSP_BOX_ID_ANGLE
, // flight mode
163 [MSP_STATUS_FLIGHTMODE_HORIZON
] = MSP_BOX_ID_HORIZON
, // flight mode
164 [MSP_STATUS_SENSOR_BARO
] = MSP_BOX_ID_BARO
, // sensor icon only
165 [MSP_STATUS_SENSOR_MAG
] = MSP_BOX_ID_MAG
, // sensor icon only
166 [MSP_STATUS_MISC_GIMBAL
] = MSP_BOX_ID_CAMSTAB
, // gimbal icon only
167 [MSP_STATUS_FLIGHTMODE_GPSHOME
] = MSP_BOX_ID_GPSHOME
, // flight mode
168 [MSP_STATUS_FLIGHTMODE_GPSHOLD
] = MSP_BOX_ID_GPSHOLD
, // flight mode
169 [MSP_STATUS_FLIGHTMODE_GPSMISSION
] = MSP_BOX_ID_GPSMISSION
, // flight mode
170 [MSP_STATUS_FLIGHTMODE_GPSLAND
] = MSP_BOX_ID_GPSLAND
, // flight mode
171 [MSP_STATUS_FLIGHTMODE_PASSTHRU
] = MSP_BOX_ID_PASSTHRU
, // flight mode
172 [MSP_STATUS_OSD_SWITCH
] = MSP_BOX_ID_OSD_SWITCH
, // switch OSD mode
173 [MSP_STATUS_ICON_AIRMODE
] = MSP_BOX_ID_AIR
,
174 [MSP_STATUS_ICON_ACROPLUS
] = MSP_BOX_ID_ACROPLUS
,
189 MSP_MAYBE_UAVTALK_SLOW2
,
190 MSP_MAYBE_UAVTALK_SLOW3
,
191 MSP_MAYBE_UAVTALK_SLOW4
,
192 MSP_MAYBE_UAVTALK_SLOW5
,
193 MSP_MAYBE_UAVTALK_SLOW6
197 typedef struct __attribute__((packed
)) {
206 PIDAROLL
, // (PIDALT) use this for Attitude ROLL
207 PIDAPITCH
, // (PIDPOS) use this for Attitude PITCH
208 PIDPOSR
, // skipped by MWOSD
209 PIDNAVR
, // skipped by MWOSD
210 PIDAYAW
, // (PIDLEVEL) use this for Attitude YAW
211 PIDMAG
, // unused for now
212 PIDVEL
, // skipped by MWOSD
216 static const char msp_pidnames
[] = "ROLL;"
227 #define MSP_ANALOG_VOLTAGE (1 << 0)
228 #define MSP_ANALOG_CURRENT (1 << 1)
236 UAVObjHandle current_pid_bank
;
245 // Specific packed data structures go here.
246 msp_pid_t piditems
[PID_ITEM_COUNT
];
250 #if defined(PIOS_MSP_STACK_SIZE)
251 #define STACK_SIZE_BYTES PIOS_MSP_STACK_SIZE
253 #define STACK_SIZE_BYTES 768
255 #define TASK_PRIORITY (tskIDLE_PRIORITY)
257 #define MAX_ALARM_LEN 30
259 #define BOOT_DISPLAY_TIME_MS (10 * 1000)
261 static bool module_enabled
= false;
262 static struct msp_bridge
*msp
;
263 static int32_t uavoMSPBridgeInitialize(void);
264 static void uavoMSPBridgeTask(void *parameters
);
266 static void msp_send(struct msp_bridge
*m
, uint8_t cmd
, const uint8_t *data
, size_t len
)
269 uint8_t cs
= (uint8_t)(len
) ^ cmd
;
274 buf
[3] = (uint8_t)(len
);
277 PIOS_COM_SendBuffer(m
->com
, buf
, sizeof(buf
));
280 PIOS_COM_SendBuffer(m
->com
, data
, len
);
282 for (unsigned i
= 0; i
< len
; i
++) {
290 PIOS_COM_SendBuffer(m
->com
, buf
, 1);
293 static msp_state
msp_state_size(struct msp_bridge
*m
, uint8_t b
)
297 return MSP_HEADER_CMD
;
300 static msp_state
msp_state_cmd(struct msp_bridge
*m
, uint8_t b
)
304 m
->checksum
^= m
->cmd_id
;
306 if (m
->cmd_size
> sizeof(m
->cmd_data
)) {
307 // Too large a body. Let's ignore it.
311 return m
->cmd_size
== 0 ? MSP_CHECKSUM
: MSP_FILLBUF
;
314 static msp_state
msp_state_fill_buf(struct msp_bridge
*m
, uint8_t b
)
316 m
->cmd_data
.data
[m
->cmd_i
++] = b
;
318 return m
->cmd_i
== m
->cmd_size
? MSP_CHECKSUM
: MSP_FILLBUF
;
321 static void msp_send_attitude(struct msp_bridge
*m
)
331 AttitudeStateData attState
;
333 AttitudeStateGet(&attState
);
335 // Roll and Pitch are in 10ths of a degree.
336 data
.att
.x
= attState
.Roll
* 10;
337 data
.att
.y
= attState
.Pitch
* -10;
338 // Yaw is just -180 -> 180
339 data
.att
.h
= attState
.Yaw
;
341 msp_send(m
, MSP_ATTITUDE
, data
.buf
, sizeof(data
));
344 #define IS_STAB_MODE(d, m) (((d).Roll == (m)) && ((d).Pitch == (m)))
346 static void msp_send_status(struct msp_bridge
*m
)
356 } __attribute__((packed
)) status
;
358 // TODO: https://github.com/TauLabs/TauLabs/blob/next/shared/uavobjectdefinition/actuatordesired.xml#L8
359 data
.status
.cycleTime
= 0;
360 data
.status
.i2cErrors
= 0;
362 data
.status
.sensors
= m
->sensors
;
364 if (GPSPositionSensorHandle() != NULL
) {
365 GPSPositionSensorStatusOptions gpsStatus
;
366 GPSPositionSensorStatusGet(&gpsStatus
);
368 if (gpsStatus
!= GPSPOSITIONSENSOR_STATUS_NOGPS
) {
369 data
.status
.sensors
|= MSP_SENSOR_GPS
;
373 data
.status
.flags
= 0;
374 data
.status
.setting
= 0;
376 FlightStatusData flightStatus
;
377 FlightStatusGet(&flightStatus
);
379 StabilizationDesiredStabilizationModeData stabModeData
;
380 StabilizationDesiredStabilizationModeGet(&stabModeData
);
382 if (flightStatus
.Armed
== FLIGHTSTATUS_ARMED_ARMED
) {
383 data
.status
.flags
|= (1 << MSP_STATUS_ARMED
);
388 if (flightStatus
.FlightMode
== FLIGHTSTATUS_FLIGHTMODE_POSITIONHOLD
) {
389 data
.status
.flags
|= (1 << MSP_STATUS_FLIGHTMODE_GPSHOLD
);
390 } else if (flightStatus
.FlightMode
== FLIGHTSTATUS_FLIGHTMODE_RETURNTOBASE
) {
391 data
.status
.flags
|= (1 << MSP_STATUS_FLIGHTMODE_GPSHOME
);
392 } else if (flightStatus
.FlightMode
== FLIGHTSTATUS_FLIGHTMODE_PATHPLANNER
) {
393 data
.status
.flags
|= (1 << MSP_STATUS_FLIGHTMODE_GPSMISSION
);
394 } else if (flightStatus
.FlightMode
== FLIGHTSTATUS_FLIGHTMODE_LAND
) {
395 data
.status
.flags
|= (1 << MSP_STATUS_FLIGHTMODE_GPSLAND
);
396 } else if (flightStatus
.FlightMode
== FLIGHTSTATUS_FLIGHTMODE_MANUAL
) {
397 data
.status
.flags
|= (1 << MSP_STATUS_FLIGHTMODE_PASSTHRU
);
399 // if Roll(Rate) && Pitch(Rate) => Acro
400 // if Roll(Acro+) && Pitch(Acro+) => Acro+
401 // if Roll(Rattitude) && Pitch(Rattitude) => Horizon
402 // else => Stabilized
404 if (IS_STAB_MODE(stabModeData
, STABILIZATIONDESIRED_STABILIZATIONMODE_RATE
)
405 || IS_STAB_MODE(stabModeData
, STABILIZATIONDESIRED_STABILIZATIONMODE_RATETRAINER
)) {
406 // data.status.flags should not set any mode flags
407 } else if (IS_STAB_MODE(stabModeData
, STABILIZATIONDESIRED_STABILIZATIONMODE_ACRO
)) { // this is Acro+ mode
408 data
.status
.flags
|= (1 << MSP_STATUS_ICON_ACROPLUS
);
409 } else if (IS_STAB_MODE(stabModeData
, STABILIZATIONDESIRED_STABILIZATIONMODE_RATTITUDE
)) {
410 data
.status
.flags
|= (1 << MSP_STATUS_FLIGHTMODE_HORIZON
);
411 } else { // looks like stabilized mode, whichever it is..
412 data
.status
.flags
|= (1 << MSP_STATUS_FLIGHTMODE_STABILIZED
);
417 // flight mode HORIZON or STABILIZED will turn on Accelerometer icon.
420 if ((stabModeData
.Thrust
== STABILIZATIONDESIRED_STABILIZATIONMODE_ALTITUDEHOLD
)
421 || (stabModeData
.Thrust
== STABILIZATIONDESIRED_STABILIZATIONMODE_ALTITUDEVARIO
)) {
422 data
.status
.flags
|= (1 << MSP_STATUS_SENSOR_BARO
);
425 #ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
426 if (MagStateHandle() != NULL
) {
427 MagStateSourceOptions magSource
;
428 MagStateSourceGet(&magSource
);
430 if (magSource
!= MAGSTATE_SOURCE_INVALID
) {
431 data
.status
.flags
|= (1 << MSP_STATUS_SENSOR_MAG
);
434 #endif /* PIOS_EXCLUDE_ADVANCED_FEATURES */
436 if (flightStatus
.AlwaysStabilizeWhenArmed
== FLIGHTSTATUS_ALWAYSSTABILIZEWHENARMED_TRUE
) {
437 data
.status
.flags
|= (1 << MSP_STATUS_ICON_AIRMODE
);
440 msp_send(m
, MSP_STATUS
, data
.buf
, sizeof(data
));
443 static void msp_send_analog(struct msp_bridge
*m
)
449 uint16_t powerMeterSum
;
452 } __attribute__((packed
)) status
;
455 memset(&data
, 0, sizeof(data
));
457 #ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
458 if (FlightBatteryStateHandle() != NULL
) {
459 FlightBatteryStateData batState
;
460 FlightBatteryStateGet(&batState
);
462 if (m
->analog
& MSP_ANALOG_VOLTAGE
) {
463 data
.status
.vbat
= (uint8_t)lroundf(batState
.Voltage
* 10);
465 if (m
->analog
& MSP_ANALOG_CURRENT
) {
466 data
.status
.current
= lroundf(batState
.Current
* 100);
467 data
.status
.powerMeterSum
= lroundf(batState
.ConsumedEnergy
);
471 // OPLink supports RSSI reported in dBm, but updates different value in ReceiverStatus.Quality (link_quality - function of lost, repaired and good packet count).
472 // We use same method as in Receiver module to decide if oplink is used for control:
473 // Check for Throttle channel group, if this belongs to oplink, we will use oplink rssi instead of ReceiverStatus.Quality.
475 ManualControlSettingsChannelGroupsData channelGroups
;
476 ManualControlSettingsChannelGroupsGet(&channelGroups
);
478 #ifdef PIOS_INCLUDE_OPLINKRCVR
479 if (channelGroups
.Throttle
== MANUALCONTROLSETTINGS_CHANNELGROUPS_OPLINK
) {
481 OPLinkStatusRSSIGet(&rssi
);
483 // MSP values have no units, and OSD rssi display requires calibration anyway, so we will translate OPLINK_LOW_RSSI to OPLINK_HIGH_RSSI -> 0-1023
484 if (rssi
< OPLINK_LOW_RSSI
) {
485 rssi
= OPLINK_LOW_RSSI
;
486 } else if (rssi
> OPLINK_HIGH_RSSI
) {
487 rssi
= OPLINK_HIGH_RSSI
;
490 data
.status
.rssi
= ((rssi
- OPLINK_LOW_RSSI
) * 1023) / (OPLINK_HIGH_RSSI
- OPLINK_LOW_RSSI
);
492 #endif /* PIOS_INCLUDE_OPLINKRCVR */
494 ReceiverStatusQualityGet(&quality
);
496 // MSP RSSI's range is 0-1023
497 data
.status
.rssi
= (quality
* 1023) / 100;
498 #ifdef PIOS_INCLUDE_OPLINKRCVR
500 #endif /* PIOS_INCLUDE_OPLINKRCVR */
502 if (data
.status
.rssi
> 1023) {
503 data
.status
.rssi
= 1023;
506 msp_send(m
, MSP_ANALOG
, data
.buf
, sizeof(data
));
509 static void msp_send_raw_gps(struct msp_bridge
*m
)
514 uint8_t fix
; // 0 or 1
516 int32_t lat
; // 1 / 10 000 000 deg
517 int32_t lon
; // 1 / 10 000 000 deg
518 uint16_t alt
; // meter
519 uint16_t speed
; // cm/s
520 int16_t ground_course
; // degree * 10
521 } __attribute__((packed
)) raw_gps
;
524 GPSPositionSensorData gps_data
;
526 if (GPSPositionSensorHandle() != NULL
) {
527 GPSPositionSensorGet(&gps_data
);
529 data
.raw_gps
.fix
= (gps_data
.Status
>= GPSPOSITIONSENSOR_STATUS_FIX2D
? 1 : 0); // Data will display on OSD if 2D fix or better
530 data
.raw_gps
.num_sat
= gps_data
.Satellites
;
531 data
.raw_gps
.lat
= gps_data
.Latitude
;
532 data
.raw_gps
.lon
= gps_data
.Longitude
;
533 data
.raw_gps
.alt
= (uint16_t)gps_data
.Altitude
;
534 data
.raw_gps
.speed
= (uint16_t)gps_data
.Groundspeed
;
535 data
.raw_gps
.ground_course
= (int16_t)(gps_data
.Heading
* 10.0f
);
537 msp_send(m
, MSP_RAW_GPS
, data
.buf
, sizeof(data
));
541 #ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
543 static inline float Sq(float x
)
548 static void msp_send_comp_gps(struct msp_bridge
*m
)
553 uint16_t distance_to_home
; // meter
554 int16_t direction_to_home
; // degree [-180:180]
555 uint8_t home_position_valid
; // 0 = Invalid, Dronin MSP specific
556 } __attribute__((packed
)) comp_gps
;
559 data
.comp_gps
.distance_to_home
= 0;
560 data
.comp_gps
.direction_to_home
= 0;
561 data
.comp_gps
.home_position_valid
= 0; // Home distance and direction will not display on OSD
563 if (PositionStateHandle() && TakeOffLocationHandle()) {
564 TakeOffLocationData takeOffLocation
;
565 TakeOffLocationGet(&takeOffLocation
);
567 if (takeOffLocation
.Status
== TAKEOFFLOCATION_STATUS_VALID
) {
568 PositionStateData positionState
;
569 PositionStateGet(&positionState
);
571 data
.comp_gps
.distance_to_home
= (uint16_t)sqrtf(Sq(takeOffLocation
.East
- positionState
.East
) + Sq(takeOffLocation
.North
- positionState
.North
));
572 data
.comp_gps
.direction_to_home
= RAD2DEG(atan2f(takeOffLocation
.East
- positionState
.East
, takeOffLocation
.North
- positionState
.North
));
574 data
.comp_gps
.home_position_valid
= 1;
578 // here, CC3D implementation could use raw gps data (GPSPositionSensorData) and locally cached GPSPositionSensorData at arming time as TakeOffLocation.
581 msp_send(m
, MSP_COMP_GPS
, data
.buf
, sizeof(data
));
584 static void msp_send_altitude(struct msp_bridge
*m
)
589 int32_t estimatedAltitude
; // cm
590 int16_t vario
; // cm/s
591 } __attribute__((packed
)) altitude
;
595 if (PositionStateHandle() != NULL
) {
596 PositionStateData positionState
;
597 PositionStateGet(&positionState
);
599 data
.altitude
.estimatedAltitude
= (int32_t)roundf(positionState
.Down
* -100.0f
);
601 data
.altitude
.estimatedAltitude
= 0;
604 if (VelocityStateHandle() != NULL
) {
605 VelocityStateData velocityState
;
606 VelocityStateGet(&velocityState
);
608 data
.altitude
.vario
= (int16_t)roundf(velocityState
.Down
* -100.0f
);
610 data
.altitude
.vario
= 0;
613 msp_send(m
, MSP_ALTITUDE
, data
.buf
, sizeof(data
));
615 #endif /* PIOS_EXCLUDE_ADVANCED_FEATURES */
618 // Scale stick values whose input range is -1 to 1 to MSP's expected
619 // PWM range of 1000-2000.
620 static uint16_t msp_scale_rc(float percent
)
622 return percent
* 500 + 1500;
625 // Throttle maps to 1100-1900 and a bit differently as -1 == 1000 and
626 // then jumps immediately to 0 -> 1 for the rest of the range. MWOSD
627 // can learn ranges as wide as they are sent, but defaults to
628 // 1100-1900 so the throttle indicator will vary for the same stick
629 // position unless we send it what it wants right away.
630 static uint16_t msp_scale_rc_thr(float percent
)
632 return percent
<= 0 ? 1100 : percent
* 800 + 1100;
635 // MSP RC order is Roll/Pitch/Yaw/Throttle/AUX1/AUX2/AUX3/AUX4
636 static void msp_send_channels(struct msp_bridge
*m
)
638 AccessoryDesiredData acc0
, acc1
, acc2
, acc3
;
639 ManualControlCommandData manualState
;
641 ManualControlCommandGet(&manualState
);
642 AccessoryDesiredInstGet(0, &acc0
);
643 AccessoryDesiredInstGet(1, &acc1
);
644 AccessoryDesiredInstGet(2, &acc2
);
645 AccessoryDesiredInstGet(3, &acc3
);
649 uint16_t channels
[8];
652 msp_scale_rc(manualState
.Roll
),
653 msp_scale_rc(manualState
.Pitch
* -1), // TL pitch is backwards
654 msp_scale_rc(manualState
.Yaw
),
655 msp_scale_rc_thr(manualState
.Throttle
),
656 msp_scale_rc(acc0
.AccessoryVal
),
657 msp_scale_rc(acc1
.AccessoryVal
),
658 msp_scale_rc(acc2
.AccessoryVal
),
659 msp_scale_rc(acc3
.AccessoryVal
),
663 msp_send(m
, MSP_RC
, data
.buf
, sizeof(data
));
666 static void msp_send_boxids(struct msp_bridge
*m
) // This is actually sending a map of MSP_STATUS.flag bits to BOX ids.
668 msp_send(m
, MSP_BOXIDS
, msp_boxes
, sizeof(msp_boxes
));
671 static void msp_send_pidnames(struct msp_bridge
*m
)
673 msp_send(m
, MSP_PIDNAMES
, (const uint8_t *)msp_pidnames
, sizeof(msp_pidnames
) - 1); // without terminating 0
676 static void pid_native2msp(const float *native
, msp_pid_t
*piditem
, float scale
, unsigned numelem
)
678 for (unsigned i
= 0; i
< numelem
; ++i
) {
679 // Handle Dterm scale
680 float s
= (i
== 2) ? scale
* 100 : scale
;
681 piditem
->values
[i
] = lroundf(native
[i
] * s
);
685 static void pid_msp2native(const msp_pid_t
*piditem
, float *native
, float scale
, unsigned numelem
)
687 for (unsigned i
= 0; i
< numelem
; ++i
) {
688 // Handle Dterm scale
689 float s
= (i
== 2) ? scale
* 100 : scale
;
690 native
[i
] = (float)piditem
->values
[i
] / s
;
694 static UAVObjHandle
get_current_pid_bank_handle()
698 ManualControlCommandFlightModeSwitchPositionGet(&fm
);
700 if (fm
>= FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_NUMELEM
) {
701 return STABILIZATIONSETTINGS_FLIGHTMODEMAP_BANK1
;
704 StabilizationSettingsFlightModeMapOptions flightModeMap
[STABILIZATIONSETTINGS_FLIGHTMODEMAP_NUMELEM
];
705 StabilizationSettingsFlightModeMapGet(flightModeMap
);
707 switch (flightModeMap
[fm
]) {
708 case STABILIZATIONSETTINGS_FLIGHTMODEMAP_BANK1
:
709 return StabilizationSettingsBank1Handle();
711 case STABILIZATIONSETTINGS_FLIGHTMODEMAP_BANK2
:
712 return StabilizationSettingsBank2Handle();
714 case STABILIZATIONSETTINGS_FLIGHTMODEMAP_BANK3
:
715 return StabilizationSettingsBank3Handle();
721 static void msp_send_pid(struct msp_bridge
*m
)
723 m
->current_pid_bank
= get_current_pid_bank_handle();
725 StabilizationBankData bankData
;
726 UAVObjGetData(m
->current_pid_bank
, &bankData
);
728 msp_pid_t piditems
[PID_ITEM_COUNT
];
730 memset(piditems
, 0, sizeof(piditems
));
732 pid_native2msp((float *)&bankData
.RollRatePID
, &piditems
[PIDROLL
], 10000, 3);
733 pid_native2msp((float *)&bankData
.PitchRatePID
, &piditems
[PIDPITCH
], 10000, 3);
734 pid_native2msp((float *)&bankData
.YawRatePID
, &piditems
[PIDYAW
], 10000, 3);
736 pid_native2msp((float *)&bankData
.RollPI
, &piditems
[PIDAROLL
], 10, 2);
737 pid_native2msp((float *)&bankData
.PitchPI
, &piditems
[PIDAPITCH
], 10, 2);
738 pid_native2msp((float *)&bankData
.YawPI
, &piditems
[PIDAYAW
], 10, 2);
740 msp_send(m
, MSP_PID
, (const uint8_t *)piditems
, sizeof(piditems
));
743 static void msp_set_pid(struct msp_bridge
*m
)
745 if (m
->current_pid_bank
== 0) {
749 StabilizationBankData bankData
;
750 UAVObjGetData(m
->current_pid_bank
, &bankData
);
752 pid_msp2native(&m
->cmd_data
.piditems
[PIDROLL
], (float *)&bankData
.RollRatePID
, 10000, 3);
753 pid_msp2native(&m
->cmd_data
.piditems
[PIDPITCH
], (float *)&bankData
.PitchRatePID
, 10000, 3);
754 pid_msp2native(&m
->cmd_data
.piditems
[PIDYAW
], (float *)&bankData
.YawRatePID
, 10000, 3);
756 pid_msp2native(&m
->cmd_data
.piditems
[PIDAROLL
], (float *)&bankData
.RollPI
, 10, 2);
757 pid_msp2native(&m
->cmd_data
.piditems
[PIDAPITCH
], (float *)&bankData
.PitchPI
, 10, 2);
758 pid_msp2native(&m
->cmd_data
.piditems
[PIDAYAW
], (float *)&bankData
.YawPI
, 10, 2);
760 UAVObjSetData(m
->current_pid_bank
, &bankData
);
762 bool needSave
= true;
765 FlightStatusArmedOptions armed
;
766 FlightStatusArmedGet(&armed
);
768 if (armed
== FLIGHTSTATUS_ARMED_DISARMED
) {
769 ObjectPersistenceData op
;
771 op
.ObjectID
= UAVObjGetID(m
->current_pid_bank
);
773 op
.Selection
= OBJECTPERSISTENCE_SELECTION_SINGLEOBJECT
;
774 op
.Operation
= OBJECTPERSISTENCE_OPERATION_SAVE
;
776 ObjectPersistenceSet(&op
);
780 msp_send(m
, MSP_SET_PID
, 0, 0); // send ack.
785 #define ALARM_ERROR 2
788 #define MS2TICKS(m) ((m) / (portTICK_RATE_MS))
790 static void msp_send_alarms(__attribute__((unused
)) struct msp_bridge
*m
)
796 char msg
[MAX_ALARM_LEN
];
797 } __attribute__((packed
)) alarm
;
800 SystemAlarmsData alarm
;
801 SystemAlarmsGet(&alarm
);
803 // Special case early boot times -- just report boot reason
806 if (xTaskGetTickCount() < MS2TICKS(10 * 1000)) {
807 data
.alarm
.state
= ALARM_CRIT
;
808 const char *boot_reason
= AlarmBootReason(alarm
.RebootCause
);
809 strncpy((char *)data
.alarm
.msg
, boot_reason
, MAX_ALARM_LEN
);
810 data
.alarm
.msg
[MAX_ALARM_LEN
- 1] = '\0';
811 msp_send(m
, MSP_ALARMS
, data
.buf
, strlen((char *)data
.alarm
.msg
) + 1);
817 data
.alarm
.state
= ALARM_OK
;
818 int32_t len
= AlarmString(&alarm
, data
.alarm
.msg
,
819 sizeof(data
.alarm
.msg
), SYSTEMALARMS_ALARM_CRITICAL
, &state
); // Include only CRITICAL and ERROR
821 // NOTE: LP alarm severity levels and MSP levels do not match. ERROR and CRITICAL are swapped.
822 // So far, MW-OSD code (MSP consumer) does not make difference between ALARM_ERROR and ALARM_CRITICAL.
823 // ALARM_WARN should be blinking if thats the highest severity level at the moment.
824 // There might be other types of MSP consumers.
827 case SYSTEMALARMS_ALARM_WARNING
:
828 data
.alarm
.state
= ALARM_WARN
;
830 case SYSTEMALARMS_ALARM_ERROR
:
831 data
.alarm
.state
= ALARM_CRIT
;
833 case SYSTEMALARMS_ALARM_CRITICAL
:
834 data
.alarm
.state
= ALARM_ERROR
;
838 msp_send(m
, MSP_ALARMS
, data
.buf
, len
+ 1);
841 static msp_state
msp_state_checksum(struct msp_bridge
*m
, uint8_t b
)
843 if ((m
->checksum
^ b
) != 0) {
847 // Respond to interesting things.
852 #ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
854 msp_send_comp_gps(m
);
857 msp_send_altitude(m
);
859 #endif /* PIOS_EXCLUDE_ADVANCED_FEATURES */
861 msp_send_attitude(m
);
870 msp_send_channels(m
);
885 msp_send_pidnames(m
);
892 static msp_state
msp_state_discard(struct msp_bridge
*m
, __attribute__((unused
)) uint8_t b
)
894 return m
->cmd_i
++ == m
->cmd_size
? MSP_IDLE
: MSP_DISCARD
;
898 * Process incoming bytes from an MSP query thing.
899 * @param[in] b received byte
900 * @return true if we should continue processing bytes
902 static bool msp_receive_byte(struct msp_bridge
*m
, uint8_t b
)
907 case 0xe0: // uavtalk matching first part of 0x3c @ 57600 baud
908 m
->state
= MSP_MAYBE_UAVTALK_SLOW2
;
910 case '<': // uavtalk matching with 0x3c 0x2x 0xxx 0x0x
911 m
->state
= MSP_MAYBE_UAVTALK2
;
914 m
->state
= MSP_HEADER_START
;
920 case MSP_HEADER_START
:
921 m
->state
= b
== 'M' ? MSP_HEADER_M
: MSP_IDLE
;
924 m
->state
= b
== '<' ? MSP_HEADER_SIZE
: MSP_IDLE
;
926 case MSP_HEADER_SIZE
:
927 m
->state
= msp_state_size(m
, b
);
930 m
->state
= msp_state_cmd(m
, b
);
933 m
->state
= msp_state_fill_buf(m
, b
);
936 m
->state
= msp_state_checksum(m
, b
);
939 m
->state
= msp_state_discard(m
, b
);
941 case MSP_MAYBE_UAVTALK2
:
943 // second possible uavtalk byte
944 m
->state
= (b
& 0xf0) == 0x20 ? MSP_MAYBE_UAVTALK3
: MSP_IDLE
;
946 case MSP_MAYBE_UAVTALK3
:
947 // third possible uavtalk byte can be anything
948 m
->state
= MSP_MAYBE_UAVTALK4
;
950 case MSP_MAYBE_UAVTALK4
:
952 // If this looks like the fourth possible uavtalk byte, we're done
953 if ((b
& 0xf0) == 0) {
954 PIOS_COM_TELEM_RF
= m
->com
;
958 case MSP_MAYBE_UAVTALK_SLOW2
:
959 m
->state
= b
== 0x18 ? MSP_MAYBE_UAVTALK_SLOW3
: MSP_IDLE
;
961 case MSP_MAYBE_UAVTALK_SLOW3
:
962 m
->state
= b
== 0x98 ? MSP_MAYBE_UAVTALK_SLOW4
: MSP_IDLE
;
964 case MSP_MAYBE_UAVTALK_SLOW4
:
965 m
->state
= b
== 0x7e ? MSP_MAYBE_UAVTALK_SLOW5
: MSP_IDLE
;
967 case MSP_MAYBE_UAVTALK_SLOW5
:
968 m
->state
= b
== 0x00 ? MSP_MAYBE_UAVTALK_SLOW6
: MSP_IDLE
;
970 case MSP_MAYBE_UAVTALK_SLOW6
:
972 // If this looks like the sixth possible 57600 baud uavtalk byte, we're done
974 PIOS_COM_ChangeBaud(m
->com
, 57600);
975 PIOS_COM_TELEM_RF
= m
->com
;
985 * Module start routine automatically called after initialization routine
986 * @return 0 when was successful
988 static int32_t uavoMSPBridgeStart(void)
990 if (!module_enabled
) {
991 // give port to telemetry if it doesn't have one
992 // stops board getting stuck in condition where it can't be connected to gcs
993 if (!PIOS_COM_TELEM_RF
) {
994 PIOS_COM_TELEM_RF
= pios_com_msp_id
;
1000 xTaskHandle taskHandle
;
1002 xTaskCreate(uavoMSPBridgeTask
, "uavoMSPBridge", STACK_SIZE_BYTES
/ 4, NULL
, TASK_PRIORITY
, &taskHandle
);
1003 PIOS_TASK_MONITOR_RegisterTask(TASKINFO_RUNNING_UAVOMSPBRIDGE
, taskHandle
);
1008 static uint32_t hwsettings_mspspeed_enum_to_baud(uint8_t baud
)
1011 case HWSETTINGS_MSPSPEED_2400
:
1014 case HWSETTINGS_MSPSPEED_4800
:
1017 case HWSETTINGS_MSPSPEED_9600
:
1020 case HWSETTINGS_MSPSPEED_19200
:
1023 case HWSETTINGS_MSPSPEED_38400
:
1026 case HWSETTINGS_MSPSPEED_57600
:
1030 case HWSETTINGS_MSPSPEED_115200
:
1037 * Module initialization routine
1038 * @return 0 when initialization was successful
1040 static int32_t uavoMSPBridgeInitialize(void)
1042 if (pios_com_msp_id
) {
1043 msp
= pios_malloc(sizeof(*msp
));
1045 memset(msp
, 0x00, sizeof(*msp
));
1047 msp
->com
= pios_com_msp_id
;
1049 // now figure out enabled features: registered sensors, ADC routing, GPS
1051 #ifdef PIOS_EXCLUDE_ADVANCED_FEATURES
1052 msp
->sensors
|= MSP_SENSOR_ACC
;
1055 if (PIOS_SENSORS_GetInstanceByType(0, PIOS_SENSORS_TYPE_3AXIS_ACCEL
)) {
1056 msp
->sensors
|= MSP_SENSOR_ACC
;
1058 if (PIOS_SENSORS_GetInstanceByType(0, PIOS_SENSORS_TYPE_1AXIS_BARO
)) {
1059 msp
->sensors
|= MSP_SENSOR_BARO
;
1061 if (PIOS_SENSORS_GetInstanceByType(0, PIOS_SENSORS_TYPE_3AXIS_MAG
| PIOS_SENSORS_TYPE_3AXIS_AUXMAG
)) {
1062 msp
->sensors
|= MSP_SENSOR_MAG
;
1064 #ifdef PIOS_SENSORS_TYPE_1AXIS_SONAR
1065 if (PIOS_SENSORS_GetInstanceByType(0, PIOS_SENSORS_TYPE_1AXIS_SONAR
)) {
1066 msp
->sensors
|= MSP_SENSOR_SONAR
;
1068 #endif /* PIOS_SENSORS_TYPE_1AXIS_SONAR */
1069 #endif /* PIOS_EXCLUDE_ADVANCED_FEATURES */
1071 // MAP_SENSOR_GPS is hot-pluggable type
1073 HwSettingsADCRoutingDataArray adcRoutingDataArray
;
1074 HwSettingsADCRoutingArrayGet(adcRoutingDataArray
.array
);
1076 for (unsigned i
= 0; i
< sizeof(adcRoutingDataArray
.array
) / sizeof(adcRoutingDataArray
.array
[0]); ++i
) {
1077 switch (adcRoutingDataArray
.array
[i
]) {
1078 case HWSETTINGS_ADCROUTING_BATTERYVOLTAGE
:
1079 msp
->analog
|= MSP_ANALOG_VOLTAGE
;
1081 case HWSETTINGS_ADCROUTING_BATTERYCURRENT
:
1082 msp
->analog
|= MSP_ANALOG_CURRENT
;
1089 HwSettingsMSPSpeedOptions mspSpeed
;
1090 HwSettingsMSPSpeedGet(&mspSpeed
);
1092 PIOS_COM_ChangeBaud(pios_com_msp_id
, hwsettings_mspspeed_enum_to_baud(mspSpeed
));
1094 module_enabled
= true;
1101 MODULE_INITCALL(uavoMSPBridgeInitialize
, uavoMSPBridgeStart
);
1105 * @param[in] parameters parameter given by PIOS_Thread_Create()
1107 static void uavoMSPBridgeTask(__attribute__((unused
)) void *parameters
)
1111 uint16_t count
= PIOS_COM_ReceiveBuffer(msp
->com
, &b
, 1, ~0);
1113 if (!msp_receive_byte(msp
, b
)) {
1114 // Returning is considered risky here as
1115 // that's unusual and this is an edge case.
1117 PIOS_DELAY_WaitmS(60 * 1000);
1124 #endif // PIOS_INCLUDE_MSP_BRIDGE