iNavFlight之MSP DJI协议飞控端请求应答

MSP DJI协议是用于DJI天空端与飞控端之间的通信协议,其工作模式符合C/S经典设计。

这里我们重点介绍下天空端请求报文格式和命令。

1. 报文格式

  +---+---+--------+---------+--------+------+---------+------------------------------+-------------+
  |                            Multiwii Serial Protocol V2                length = 9 + payload size |
  +---+---+--------+---------+--------+------+---------+------------------------------+-------------+
  | $ | X | !   >  | flag(1) | cmd(2)        | size(2) | payload(16bit len)           | checksum_v2 |
  +---+---+--------+---------+--------+------+---------+------------------------------+-------------+
  • ‘$’:表示SOF(Start Of a frame)
  • ‘X’:表示V2
  • ‘>’:表示response
  • ‘!’:表示error

2. 报文标志(flag)

请求报文:MSP_RESULT_ACK or MSP_RESULT_ERROR //截止发稿日

// return positive for ACK, negative on error, zero for no reply
typedef enum {
    MSP_RESULT_ACK = 1,
    MSP_RESULT_ERROR = -1,
    MSP_RESULT_NO_REPLY = 0
} mspResult_e;

3. 报文命令(cmd)

#define DJI_MSP_API_VERSION             1    // 
#define DJI_MSP_FC_VARIANT              2    // 
#define DJI_MSP_FC_VERSION              3    // 
#define DJI_MSP_NAME                    10   // For OSD 'Craft Name'
#define DJI_MSP_OSD_CONFIG              84   // OSD item count + positions
#define DJI_MSP_FILTER_CONFIG           92   //
#define DJI_MSP_PID_ADVANCED            94   //
#define DJI_MSP_STATUS                  101  // For OSD ‘armingTime’, Flight controller arming status
#define DJI_MSP_RC                      105  //
#define DJI_MSP_RAW_GPS                 106  // For OSD ‘GPS Sats’ + coordinates
#define DJI_MSP_COMP_GPS                107  // GPS direction to home & distance to home
#define DJI_MSP_ATTITUDE                108  // For OSD ‘Angle: roll & pitch’
#define DJI_MSP_ALTITUDE                109  // For OSD ‘Numerical Vario’
#define DJI_MSP_ANALOG                  110  // For OSD ‘RSSI Value’, For OSD ‘Battery voltage’ etc
#define DJI_MSP_RC_TUNING               111  //
#define DJI_MSP_PID                     112  // For OSD ‘PID roll, yaw, pitch'
#define DJI_MSP_BATTERY_STATE           130  // For OSD ‘Battery current mAh drawn’ etc
#define DJI_MSP_ESC_SENSOR_DATA         134  // For OSD ‘ESC temperature’
#define DJI_MSP_STATUS_EX               150  // For OSD ‘Fly mode', For OSD ‘Disarmed’
#define DJI_MSP_RTC                     247  // For OSD ‘RTC date time’

4. 请求应答 & 反馈报文

这里主要将payload内容整理出来。

4.1 DJI_MSP_API_VERSION

反馈内容:API版本,三个字节
示例显示:“1.42”

        case DJI_MSP_API_VERSION:
            sbufWriteU8(dst, MSP_PROTOCOL_VERSION);
            sbufWriteU8(dst, DJI_API_VERSION_MAJOR);
            sbufWriteU8(dst, DJI_API_VERSION_MINOR);
            break;

4.2 DJI_MSP_FC_VARIANT

反馈内容:飞控版本,字符串
示例显示:“INAV”

        case DJI_MSP_FC_VARIANT:
            {
                const char * const flightControllerIdentifier = INAV_IDENTIFIER;
                sbufWriteData(dst, flightControllerIdentifier, FLIGHT_CONTROLLER_IDENTIFIER_LENGTH);
            }
            break;

4.3 DJI_MSP_FC_VERSION

反馈内容:飞控版本,三字节
示例显示:“4.1.0”

        case DJI_MSP_FC_VERSION:
            sbufWriteU8(dst, 4);
            sbufWriteU8(dst, 1);
            sbufWriteU8(dst, 0);
            break;

4.4 DJI_MSP_NAME

反馈内容:模型名称,字符串
示例显示:“AocodaRC F7DUAL”

        case DJI_MSP_NAME:
            {
#if defined(USE_OSD)
                if (djiOsdConfig()->use_name_for_messages)  {
                    djiSerializeCraftNameOverride(dst);
                } else {
#endif
                    sbufWriteData(dst, systemConfig()->name, (int)strlen(systemConfig()->name));
#if defined(USE_OSD)
                }
#endif

                break;
            }
            break;

4.5 DJI_MSP_STATUS & DJI_MSP_STATUS_EX

反馈内容:

【1】cycleTime(4B)
注:PID loop time in micro second
示例显示:TBD

【2】Reserved(4B)
示例显示:TBD

【3】SensorStatus(4B)
bit00: SENSOR_ACC
bit01: SENSOR_BARO
bit02: SENSOR_MAG
bit03: SENSOR_GPS
bit04: SENSOR_RANGEFINDER
bit05: SENSOR_OPFLOW
bit06: SENSOR_PITOT
bit07: SENSOR_TEMP
bit15: Hardware failure
示例显示:TBD

【4】FlightMode(4B)
bit00: ARMED
bit01: FLM_ANGLE
bit02: FLM_HORIZON
bit03: FLM_CRUISE //HeadFree
bit04: FLM_FAILSAFE
bit05: FLM_RTH
示例显示:TBD

【5】ConfigProfile(1B)
示例显示:1 //当前使用的Profile ID

【6】SystemLoadPercent(4B)
示例显示:65 //65% CPU使用率

【7】DJI_MSP_STATUS:GyroCycleTime(4B) or DJI_MSP_STATUS_EX:PID_PROFILE_COUNT(3B) & RateProfileIndex(1B)
注:DJI_MSP_STATUS和DJI_MSP_STATUS_EX主要差异点

  1. 示例显示(DJI_MSP_STATUS):PID loop time in micro second --> 125
  2. 示例显示(DJI_MSP_STATUS_EX):PID_PROFILE_COUNT(3B) & RateProfileIndex(1B) --> 3,1

【8】Reserved(1B)
示例显示:TBD

【9】DJI_ARMING_DISABLE_FLAGS_COUNT(1B)
示例显示:25

【10】djiPackArmingDisabledFlags(4B)
bit24: ARMING_DISABLED_ARM_SWITCH
示例显示:0x1000000

【11】Reserved(1B)
示例显示:TBD

        case DJI_MSP_STATUS:
        case DJI_MSP_STATUS_EX:
            {
                // DJI OSD relies on a statically defined bit order and doesn't use MSP_BOXIDS
                // to get actual BOX order. We need a special packBoxModeFlags()
                boxBitmask_t flightModeBitmask;
                djiPackBoxModeBitmask(&flightModeBitmask);

                sbufWriteU16(dst, (uint16_t)cycleTime);
                sbufWriteU16(dst, 0);
                sbufWriteU16(dst, packSensorStatus());
                sbufWriteData(dst, &flightModeBitmask, 4);        // unconditional part of flags, first 32 bits
                sbufWriteU8(dst, getConfigProfile());

                sbufWriteU16(dst, constrain(averageSystemLoadPercent, 0, 100));
                if (cmd->cmd == MSP_STATUS_EX) {
                    sbufWriteU8(dst, 3);            // PID_PROFILE_COUNT
                    sbufWriteU8(dst, 1);            // getCurrentControlRateProfileIndex()
                } else {
                    sbufWriteU16(dst, cycleTime);   // gyro cycle time
                }

                // Cap BoxModeFlags to 32 bits
                // write flightModeFlags header. Lowest 4 bits contain number of bytes that follow
                sbufWriteU8(dst, 0);
                // sbufWriteData(dst, ((uint8_t*)&flightModeBitmask) + 4, byteCount);

                // Write arming disable flags
                sbufWriteU8(dst, DJI_ARMING_DISABLE_FLAGS_COUNT);
                sbufWriteU32(dst, djiPackArmingDisabledFlags());

                // Extra flags
                sbufWriteU8(dst, 0);
            }
            break;
uint16_t packSensorStatus(void)
{
    // Sensor bits
    uint16_t sensorStatus =
            IS_ENABLED(sensors(SENSOR_ACC))         << 0 |
            IS_ENABLED(sensors(SENSOR_BARO))        << 1 |
            IS_ENABLED(sensors(SENSOR_MAG))         << 2 |
            IS_ENABLED(sensors(SENSOR_GPS))         << 3 |
            IS_ENABLED(sensors(SENSOR_RANGEFINDER)) << 4 |
            IS_ENABLED(sensors(SENSOR_OPFLOW))      << 5 |
            IS_ENABLED(sensors(SENSOR_PITOT))       << 6 |
            IS_ENABLED(sensors(SENSOR_TEMP))        << 7;

    // Hardware failure indication bit
    if (!isHardwareHealthy()) {
        sensorStatus |= 1 << 15;        // Bit 15 of sensor bit field indicates hardware failure
    }

    return sensorStatus;
}
static void djiPackBoxModeBitmask(boxBitmask_t * flightModeBitmask)
{
    memset(flightModeBitmask, 0, sizeof(boxBitmask_t));

    // Map flight modes to DJI-supported bits
    switch(getFlightModeForTelemetry()) {
        case FLM_MANUAL:
        case FLM_ACRO:
        case FLM_ACRO_AIR:
            // DJI: No bits set = ACRO
            break;
        case FLM_ANGLE:
            bitArraySet(flightModeBitmask->bits, 1);    // DJI: 1 << 1 : ANGLE
            break;
        case FLM_HORIZON:
            bitArraySet(flightModeBitmask->bits, 2);    // DJI: 1 << 2
            break;
        case FLM_RTH:
            bitArraySet(flightModeBitmask->bits, 5);    // DJI: 1 << 5 : GPS_RESQUE
            break;
        case FLM_CRUISE:
            bitArraySet(flightModeBitmask->bits, 3);    // DJI: 1 << 3 : technically HEADFREE
            break;
        case FLM_FAILSAFE:
            bitArraySet(flightModeBitmask->bits, 4);    // DJI: 1 << 4
            break;
        case FLM_LAUNCH:
        case FLM_ALTITUDE_HOLD:
        case FLM_POSITION_HOLD:
        case FLM_MISSION:
        default:
            // Unsupported ATM, keep at ANGLE
            bitArraySet(flightModeBitmask->bits, 1);    // DJI: 1 << 1 : ANGLE
    }

    // Set ARMED mode
    if (ARMING_FLAG(ARMED)) {
        bitArraySet(flightModeBitmask->bits, 0);        // DJI: 1 << 0 : ARMED
    }
}
static uint32_t djiPackArmingDisabledFlags(void)
{
    // TODO: Map INAV arming disabled flags to DJI/BF ones
    // https://github.com/betaflight/betaflight/blob/c6e5882dd91fa20d246b8f8af10cf6c92876bc3d/src/main/fc/runtime_config.h#L42
    // For now hide everything in ARMING_DISABLED_ARM_SWITCH (bit 24)

    return isArmingDisabled() ? (1 << 24) : 0;
}

4.6 DJI_MSP_RC

反馈内容:channel_1(1B) + channel_2(1B) + channel_3(1B) + channel_4(1B)
示例显示:横滚(1500), 俯仰(1500), 方向(1500), 油门(1200)

        case DJI_MSP_RC:
            // Only send sticks (first 4 channels)
            for (int i = 0; i < STICK_CHANNEL_COUNT; i++) {
                sbufWriteU16(dst, rxGetChannelValue(i));
            }
            break;

4.7 DJI_MSP_RAW_GPS

反馈内容:

fixType(1B) + numSat(1B) + lat(4B) + lon(4B) + alt(2B)/100 + speed(2B) + groundCourse(2B)

示例显示:TBD

        case DJI_MSP_RAW_GPS:
            sbufWriteU8(dst, gpsSol.fixType);
            sbufWriteU8(dst, gpsSol.numSat);
            sbufWriteU32(dst, gpsSol.llh.lat);
            sbufWriteU32(dst, gpsSol.llh.lon);
            sbufWriteU16(dst, gpsSol.llh.alt / 100);
            sbufWriteU16(dst, osdGetSpeedFromSelectedSource());
            sbufWriteU16(dst, gpsSol.groundCourse);
            break;

4.8 DJI_MSP_COMP_GPS

反馈内容:

distanceToHome(2B) + directionToHome(2B) + gpsHeartbeat(1B)

示例显示:TBD

        case DJI_MSP_COMP_GPS:
            sbufWriteU16(dst, GPS_distanceToHome);
            sbufWriteU16(dst, GPS_directionToHome);
            sbufWriteU8(dst, gpsSol.flags.gpsHeartbeat ? 1 : 0);
            break;

4.9 DJI_MSP_ATTITUDE

反馈内容:

roll(2B, DECIDEGREES) + pitch(2B, DECIDEGREES) + yaw(2B, DEGREES)

示例显示:TBD

        case DJI_MSP_ATTITUDE:
            sbufWriteU16(dst, attitude.values.roll);
            sbufWriteU16(dst, attitude.values.pitch);
            sbufWriteU16(dst, DECIDEGREES_TO_DEGREES(attitude.values.yaw));
            break;

4.10 DJI_MSP_ALTITUDE

反馈内容:

EstimatedActualPosition(4B) + EstimatedActualVelocity(2B)

示例显示:TBD

        case DJI_MSP_ALTITUDE:
            sbufWriteU32(dst, lrintf(getEstimatedActualPosition(Z)));
            sbufWriteU16(dst, lrintf(getEstimatedActualVelocity(Z)));
            break;

4.11 DJI_MSP_ANALOG

反馈内容:

BatteryVoltage/10(1B, 0.1V steps) + MAhDrawn(2B, milliamp hours) + RSSI(2B) + Amperage(2B, 0.01A steps) + BatteryVoltage(2B)

示例显示:TBD

        case DJI_MSP_ANALOG:
            sbufWriteU8(dst,  constrain(getBatteryVoltage() / 10, 0, 255));
            sbufWriteU16(dst, constrain(getMAhDrawn(), 0, 0xFFFF)); // milliamp hours drawn from battery
#ifdef USE_SERIALRX_CRSF
            // Range of RSSI field: 0-99: 99 = 150 hz , 0 - 98 50 hz / 4 hz
            if (djiOsdConfig()->rssi_source == DJI_CRSF_LQ) {
                uint16_t scaledLq = 0;
                if (rxLinkStatistics.rfMode >= 2) {
                    scaledLq = RSSI_MAX_VALUE;
                } else {
                    scaledLq = scaleRange(constrain(rxLinkStatistics.uplinkLQ, 0, 100), 0, 100, 0, RSSI_BOUNDARY(98));
                }
                sbufWriteU16(dst, scaledLq);
            } else {
#endif
                sbufWriteU16(dst, getRSSI());
#ifdef USE_SERIALRX_CRSF
            }
#endif
            sbufWriteU16(dst, constrain(getAmperage(), -0x8000, 0x7FFF)); // send amperage in 0.01 A steps, range is -320A to 320A
            sbufWriteU16(dst, getBatteryVoltage());
            break;

4.12 DJI_MSP_PID

反馈内容:

P(1B) + I(1B) + D(1B)

示例显示:TBD

        case DJI_MSP_PID:
            for (unsigned i = 0; i < ARRAYLEN(djiPidIndexMap); i++) {
                sbufWriteU8(dst, pidBank()->pid[djiPidIndexMap[i]].P);
                sbufWriteU8(dst, pidBank()->pid[djiPidIndexMap[i]].I);
                sbufWriteU8(dst, pidBank()->pid[djiPidIndexMap[i]].D);
            }
            break;

4.13 DJI_MSP_BATTERY_STATE

反馈内容:

BatteryCellCount(1B) + capacity(2B) + BatteryVoltage/10(1B, 0.1V steps) + MAhDrawn(2B) + Amperage(2B) + BatteryState(1B) + BatteryVoltage(2B)

示例显示:TBD

        case DJI_MSP_BATTERY_STATE:
            // Battery characteristics
            sbufWriteU8(dst, constrain(getBatteryCellCount(), 0, 255));
            sbufWriteU16(dst, currentBatteryProfile->capacity.value);

            // Battery state
            sbufWriteU8(dst, constrain(getBatteryVoltage() / 10, 0, 255)); // in 0.1V steps
            sbufWriteU16(dst, constrain(getMAhDrawn(), 0, 0xFFFF));
            sbufWriteU16(dst, constrain(getAmperage(), -0x8000, 0x7FFF));

            // Battery alerts - used values match Betaflight's/DJI's
            sbufWriteU8(dst,  getBatteryState());

            // Additional battery voltage field (in 0.01V steps)
            sbufWriteU16(dst, getBatteryVoltage());
            break;

4.14 DJI_MSP_RTC

反馈内容:

year(2B) + month(1B) + day(1B) + hours(1B) + minutes(1B) + seconds(1B) + millis(1B)

示例显示:TBD

        case DJI_MSP_RTC:
            {
                dateTime_t datetime;

                // We don't care about validity here - dt will be always set to a sane value
                // rtcGetDateTime() will call rtcGetDefaultDateTime() internally
                rtcGetDateTime(&datetime);

                sbufWriteU16(dst, datetime.year);
                sbufWriteU8(dst, datetime.month);
                sbufWriteU8(dst, datetime.day);
                sbufWriteU8(dst, datetime.hours);
                sbufWriteU8(dst, datetime.minutes);
                sbufWriteU8(dst, datetime.seconds);
                sbufWriteU16(dst, datetime.millis);
            }
            break;

4.15 DJI_MSP_ESC_SENSOR_DATA

反馈内容:

DJI_OSD_USE_NON_STANDARD_MSP_ESC_SENSOR_DATA: Temp(1B) + Rpm(2B)
STANDARD: N x (Temp(1B) + Rpm(2B))

示例显示:TBD

        case DJI_MSP_ESC_SENSOR_DATA:
            if (djiOsdConfig()->proto_workarounds & DJI_OSD_USE_NON_STANDARD_MSP_ESC_SENSOR_DATA) {
                // Version 1.00.06 of DJI firmware is not using the standard MSP_ESC_SENSOR_DATA
                uint16_t protoRpm = 0;
                int16_t protoTemp = 0;

#if defined(USE_ESC_SENSOR)
                if (STATE(ESC_SENSOR_ENABLED) && getMotorCount() > 0) {
                    uint32_t motorRpmAcc = 0;
                    int32_t motorTempAcc = 0;

                    for (int i = 0; i < getMotorCount(); i++) {
                        const escSensorData_t * escSensor = getEscTelemetry(i);
                        motorRpmAcc += escSensor->rpm;
                        motorTempAcc += escSensor->temperature;
                    }

                    protoRpm = motorRpmAcc / getMotorCount();
                    protoTemp = motorTempAcc / getMotorCount();
                }
#endif

                switch (djiOsdConfig()->esc_temperature_source) {
                    // This is ESC temperature (as intended)
                    case DJI_OSD_TEMP_ESC:
                        // No-op, temperature is already set to ESC
                        break;

                    // Re-purpose the field for core temperature
                    case DJI_OSD_TEMP_CORE:
                        getIMUTemperature(&protoTemp);
                        protoTemp = protoTemp / 10;
                        break;

                    // Re-purpose the field for baro temperature
                    case DJI_OSD_TEMP_BARO:
                        getBaroTemperature(&protoTemp);
                        protoTemp = protoTemp / 10;
                        break;
                }

                // No motor count, just raw temp and RPM data
                sbufWriteU8(dst, protoTemp);
                sbufWriteU16(dst, protoRpm);
            }
            else {
                // Use standard MSP_ESC_SENSOR_DATA message
                sbufWriteU8(dst, getMotorCount());
                for (int i = 0; i < getMotorCount(); i++) {
                    uint16_t motorRpm = 0;
                    int16_t motorTemp = 0;

                    // If ESC_SENSOR is enabled, pull the telemetry data and get motor RPM
#if defined(USE_ESC_SENSOR)
                    if (STATE(ESC_SENSOR_ENABLED)) {
                        const escSensorData_t * escSensor = getEscTelemetry(i);
                        motorRpm = escSensor->rpm;
                        motorTemp = escSensor->temperature;
                    }
#endif

                    // Now populate temperature field (which we may override for different purposes)
                    switch (djiOsdConfig()->esc_temperature_source) {
                        // This is ESC temperature (as intended)
                        case DJI_OSD_TEMP_ESC:
                            // No-op, temperature is already set to ESC
                            break;

                        // Re-purpose the field for core temperature
                        case DJI_OSD_TEMP_CORE:
                            getIMUTemperature(&motorTemp);
                            motorTemp = motorTemp / 10;
                            break;

                        // Re-purpose the field for baro temperature
                        case DJI_OSD_TEMP_BARO:
                            getBaroTemperature(&motorTemp);
                            motorTemp = motorTemp / 10;
                            break;
                    }

                    // Add data for this motor to the packet
                    sbufWriteU8(dst, motorTemp);
                    sbufWriteU16(dst, motorRpm);
                }
            }
            break;

4.16 DJI_MSP_OSD_CONFIG

反馈内容:

1B: 1 //supported flag, DJI_OSD_FLAGS_OSD_FEATURE
1B: video_system //7456 video system, AUTO/PAL/NTSC
1B: units //Configuration
3B: rssi_alarm(1B) + capacity.warning(2B) //Alarms
2B: 0(1B) + ARRAYLEN(djiOSDItemIndexMap)(1B) //OSD_ITEM_COUNT
2B: alt_alarm //Altitude alarm
2nB: (nx2B) //OSD element position and visibility, n=ARRAYLEN(djiOSDItemIndexMap)
1 + nB: n(1B) + 0(nB) //Post flight statistics待修正代码, n=ARRAYLEN(djiOSDStatisticsMap)
5B: 2(1B) + 0, 0 (2x2=4B) //Timers
2B: 0(2B) //djiEncodeOSDEnabledWarnings, 尚未实现
5B: 16(1B) + 0(4B) //djiEncodeOSDEnabledWarnings, 尚未实现
2B: 1(1B) + 1(1B) //DJI OSD expects 1 OSD profile
1B: 0(1B) //No OSD stick overlay

示例显示:TBD

#if defined(USE_OSD)
            // This involved some serious magic, better contain in a separate function for readability
            djiSerializeOSDConfigReply(dst);
#else
            sbufWriteU8(dst, 0);
#endif
            break;
static void djiSerializeOSDConfigReply(sbuf_t *dst)
{
    // Only send supported flag - always
    sbufWriteU8(dst, DJI_OSD_FLAGS_OSD_FEATURE);

    // 7456 video system (AUTO/PAL/NTSC)
    sbufWriteU8(dst, osdConfig()->video_system);

    // Configuration
    sbufWriteU8(dst, osdConfig()->units);

    // Alarms
    sbufWriteU8(dst, osdConfig()->rssi_alarm);
    sbufWriteU16(dst, currentBatteryProfile->capacity.warning);

    // OSD_ITEM_COUNT (previously was timer alarm)
    sbufWriteU8(dst, 0);
    sbufWriteU8(dst, ARRAYLEN(djiOSDItemIndexMap));

    // Altitude alarm
    sbufWriteU16(dst, osdConfig()->alt_alarm);

    // OSD element position and visibility
    for (unsigned i = 0; i < ARRAYLEN(djiOSDItemIndexMap); i++) {
        const int inavOSDIdx = djiOSDItemIndexMap[i].itemIndex;

        // We call OSD item supported if it doesn't have dependencies or all feature-dependencies are satistied
        const bool itemIsSupported = ((djiOSDItemIndexMap[i].depFeature == 0) || feature(djiOSDItemIndexMap[i].depFeature));

        if (inavOSDIdx >= 0 && itemIsSupported) {
            // Position & visibility are encoded in 16 bits, and is the same between BF/DJI.
            // However INAV supports co-ords of 0-63 and has 3 layouts, while BF has co-ords 0-31 and visibility profiles.
            // Re-encode for co-ords of 0-31 and map the layout to all three BF profiles.
            uint16_t itemPos = osdLayoutsConfig()->item_pos[0][inavOSDIdx];
            uint16_t itemPosSD = OSD_POS_SD(OSD_X(itemPos), OSD_Y(itemPos));

            // Workarounds for certain OSD element positions
            // INAV calculates these dynamically, while DJI expects the config to have defined coordinates
            switch(inavOSDIdx) {
                case OSD_CROSSHAIRS:
                    itemPosSD = OSD_POS_SD(15, 8);
                    break;

                case OSD_ARTIFICIAL_HORIZON:
                    itemPosSD = OSD_POS_SD(9, 8);
                    break;

                case OSD_HORIZON_SIDEBARS:
                    itemPosSD = OSD_POS_SD(16, 7);
                    break;
            }

            // Enforce visibility in 3 BF OSD profiles
            if (OSD_VISIBLE(itemPos)) {
            	itemPosSD |= (0x3000 | OSD_VISIBLE_FLAG_SD);
            }

            sbufWriteU16(dst, itemPosSD);
        }
        else {
            // Hide OSD elements unsupported by INAV
            sbufWriteU16(dst, 0);
        }
    }

    // Post flight statistics
    sbufWriteU8(dst, ARRAYLEN(djiOSDStatisticsMap));
    for (unsigned i = 0; i < ARRAYLEN(djiOSDStatisticsMap); i++ ) {
        if (djiOSDStatisticsMap[i] >= 0) {
            // FIXME: Map post-flight statistics from INAV to BF/DJI
            sbufWriteU8(dst, 0);
        }
        else {
            sbufWriteU8(dst, 0);
        }
    }

    // Timers
    sbufWriteU8(dst, DJI_OSD_TIMER_COUNT);
    for (int i = 0; i < DJI_OSD_TIMER_COUNT; i++) {
        // STUB: We don't support BF's OSD timers
        sbufWriteU16(dst, 0);
    }

    // Enabled warnings
    // API < 1.41 stub, kept for compatibility
    sbufWriteU16(dst, djiEncodeOSDEnabledWarnings() & 0xFFFF);

    // API >= 1.41
    // Send the warnings count and 32bit enabled warnings flags.
    sbufWriteU8(dst, DJI_OSD_WARNING_COUNT);
    sbufWriteU32(dst, djiEncodeOSDEnabledWarnings());

    // DJI OSD expects 1 OSD profile
    sbufWriteU8(dst, 1);
    sbufWriteU8(dst, 1);

    // No OSD stick overlay
    sbufWriteU8(dst, 0);

    // API >= 1.43
    // Camera frame element width/height - magic numbers taken from Betaflight source
    //sbufWriteU8(dst, DJI_OSD_SCREEN_WIDTH); // osdConfig()->camera_frame_width
    //sbufWriteU8(dst, DJI_OSD_SCREEN_HEIGHT); // osdConfig()->camera_frame_height
}

4.17 DJI_MSP_FILTER_CONFIG

反馈内容:略
示例显示:TBD

        case DJI_MSP_FILTER_CONFIG:
            sbufWriteU8(dst, gyroConfig()->gyro_main_lpf_hz);           // BF: gyroConfig()->gyro_lowpass_hz
            sbufWriteU16(dst, pidProfile()->dterm_lpf_hz);              // BF: currentPidProfile->dterm_lowpass_hz
            sbufWriteU16(dst, pidProfile()->yaw_lpf_hz);                // BF: currentPidProfile->yaw_lowpass_hz
            sbufWriteU16(dst, 0);                                       // BF: gyroConfig()->gyro_soft_notch_hz_1
            sbufWriteU16(dst, 1);                                       // BF: gyroConfig()->gyro_soft_notch_cutoff_1
            sbufWriteU16(dst, 0);                                       // BF: currentPidProfile->dterm_notch_hz
            sbufWriteU16(dst, 1);                                       // BF: currentPidProfile->dterm_notch_cutoff
            sbufWriteU16(dst, 0);                                       // BF: gyroConfig()->gyro_soft_notch_hz_2
            sbufWriteU16(dst, 1);                                       // BF: gyroConfig()->gyro_soft_notch_cutoff_2
            sbufWriteU8(dst, 0);                                        // BF: currentPidProfile->dterm_filter_type
            sbufWriteU8(dst, gyroConfig()->gyro_lpf);                   // BF: gyroConfig()->gyro_hardware_lpf);
            sbufWriteU8(dst, 0);                                        // BF: DEPRECATED: gyro_32khz_hardware_lpf
            sbufWriteU16(dst, gyroConfig()->gyro_main_lpf_hz);          // BF: gyroConfig()->gyro_lowpass_hz);
            sbufWriteU16(dst, 0);                                       // BF: gyroConfig()->gyro_lowpass2_hz);
            sbufWriteU8(dst, 0);                                        // BF: gyroConfig()->gyro_lowpass_type);
            sbufWriteU8(dst, 0);                                        // BF: gyroConfig()->gyro_lowpass2_type);
            sbufWriteU16(dst, 0);                                       // BF: currentPidProfile->dterm_lowpass2_hz);
            sbufWriteU8(dst, 0);                                        // BF: currentPidProfile->dterm_filter2_type);
            break;

4.18 DJI_MSP_RC_TUNING

反馈内容:

1B: 100 // INAV doesn’t use rcRate
1B: stabilized.rcExpo8
3B: R,P,Y rates
1B: throttle.dynPID
1B: throttle.rcMid8
1B: throttle.rcExpo8
2B: throttle.pa_breakpoint
1B: stabilized.rcYawExpo8
1B: 100 // INAV doesn’t use rcRate
1B: 100 // INAV doesn’t use rcRate
1B: stabilized.rcExpo8
1B: 0
1B: throttle.dynPID

示例显示:TBD

        case DJI_MSP_RC_TUNING:
            sbufWriteU8(dst, 100);                                      // INAV doesn't use rcRate
            sbufWriteU8(dst, currentControlRateProfile->stabilized.rcExpo8);
            for (int i = 0 ; i < 3; i++) {
                // R,P,Y rates see flight_dynamics_index_t
                sbufWriteU8(dst, currentControlRateProfile->stabilized.rates[i]);
            }
            sbufWriteU8(dst, currentControlRateProfile->throttle.dynPID);
            sbufWriteU8(dst, currentControlRateProfile->throttle.rcMid8);
            sbufWriteU8(dst, currentControlRateProfile->throttle.rcExpo8);
            sbufWriteU16(dst, currentControlRateProfile->throttle.pa_breakpoint);
            sbufWriteU8(dst, currentControlRateProfile->stabilized.rcYawExpo8);
            sbufWriteU8(dst, 100);                                      // INAV doesn't use rcRate
            sbufWriteU8(dst, 100);                                      // INAV doesn't use rcRate
            sbufWriteU8(dst, currentControlRateProfile->stabilized.rcExpo8);

            // added in 1.41
            sbufWriteU8(dst, 0);
            sbufWriteU8(dst, currentControlRateProfile->throttle.dynPID);
            break;

4.19 DJI_MSP_SET_PID

反馈内容:成功或者失败,payload为空

根据DJI协议设置PID,接受报文内容:P(1B) + I(1B) + D(1B)

示例显示:TBD

        case DJI_MSP_SET_PID:
            // Check if we have enough data for all PID coefficients
            if ((unsigned)sbufBytesRemaining(src) >= ARRAYLEN(djiPidIndexMap) * 3) {
                for (unsigned i = 0; i < ARRAYLEN(djiPidIndexMap); i++) {
                    pidBankMutable()->pid[djiPidIndexMap[i]].P = sbufReadU8(src);
                    pidBankMutable()->pid[djiPidIndexMap[i]].I = sbufReadU8(src);
                    pidBankMutable()->pid[djiPidIndexMap[i]].D = sbufReadU8(src);
                }
                schedulePidGainsUpdate();
            }
            else {
                reply->result = MSP_RESULT_ERROR;
            }
            break;

4.20 Unsupported

暂不支持命令,截止发稿日。

  • DJI_MSP_PID_ADVANCED
  • DJI_MSP_SET_FILTER_CONFIG
  • DJI_MSP_SET_PID_ADVANCED
  • DJI_MSP_SET_RC_TUNING

5. 参考资料

【1】iNavFlight之MSP DJI协议分析
【2】BetaFlight模块设计之三十二:MSP协议模块分析
【3】iNavFlight之MSP DJI协议天空端请求报文

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值