@betaflight/api
Version:
A high-level API to read data from betaflight flight controllers
718 lines • 29 kB
JavaScript
import semver from "semver";
import { execute, apiVersion, WriteBuffer, isOpen, } from "@betaflight/msp";
import codes from "./codes";
import { DisarmFlags, RebootTypes, Beepers, Sensors, SpiRxProtocols, RcInterpolations, RcSmoothingChannels, RcSmoothingTypes, RcSmoothingInputTypes, RcSmoothingDerivativeTypes, GpsProtocols, GpsSbasTypes, } from "./types";
import { availableFeatures, disarmFlagBits, sensorBits, beeperBits, escProtocols, serialRxProviders, spiRxProtocols, rcInterpolations, rcSmoothingChannels, rcSmoothingTypes, rcSmoothingInputTypes, rcSmoothingDerivativeTypes, channelLetters, gpsProtocols, blackboxDevices, targetCapabilities, } from "./features";
import { times, unpackValues, packValues, toIdentifier, fromIdentifier, partialWriteFunc, sleep, } from "./utils";
import { huffmanDecodeBuffer } from "./huffman";
export * from "./osd";
export * from "./power";
export * from "./pid";
export * from "./motors";
export * from "./serial";
export * from "./modes";
export * from "./vtx";
export * from "./types";
export { apiVersion, open, close, isOpen, bytesRead, bytesWritten, packetErrors, ports, baudRate, initialiseSerialBackend, } from "@betaflight/msp";
export { escProtocols, serialRxProviders, spiRxProtocols, rcInterpolations, rcSmoothingChannels, rcSmoothingTypes, rcSmoothingInputTypes, rcSmoothingDerivativeTypes, MCU_GROUPS, mcuGroupFromId, MIXER_LIST, availableFeatures, channelLetters, gpsProtocols, blackboxDevices, } from "./features";
export { mergeDeep } from "./utils";
export const readBoardInfo = async (port) => {
const api = apiVersion(port);
const data = await execute(port, { code: codes.MSP_BOARD_INFO });
return {
boardIdentifier: String.fromCharCode(...times(() => data.readU8(), 4)),
boardVersion: data.readU16(),
boardType: semver.gte(api, "1.35.0") ? data.readU8() : 0,
targetCapabilities: unpackValues(semver.gte(api, "1.37.0") ? data.readU8() : 0, targetCapabilities()),
targetName: semver.gte(api, "1.37.0")
? String.fromCharCode(...times(() => data.readU8(), data.readU8()))
: "",
boardName: semver.gte(api, "1.41.0")
? String.fromCharCode(...times(() => data.readU8(), data.readU8()))
: "",
manufacturerId: semver.gte(api, "1.41.0")
? String.fromCharCode(...times(() => data.readU8(), data.readU8()))
: "",
signature: semver.gte(api, "1.41.0") ? times(() => data.readU8(), 32) : [],
mcuTypeId: semver.gte(api, "1.41.0") ? data.readU8() : 255,
configurationState: semver.gte(api, "1.42.0") ? data.readU8() : undefined,
sampleRateHz: semver.gte(api, "1.43.0") ? data.readU16() : undefined,
};
};
export const readFcVariant = async (port) => {
const data = await execute(port, { code: codes.MSP_FC_VARIANT });
return String.fromCharCode(...times(() => data.readU8(), 4));
};
export const readName = async (port) => {
const data = await execute(port, { code: codes.MSP_NAME });
return String.fromCharCode(...times(() => data.readU8(), data.byteLength));
};
const MAX_NAME_BUFFER_SIZE = 64;
export const writeName = async (port, name) => {
const buffer = Buffer.from(name.slice(0, MAX_NAME_BUFFER_SIZE));
await execute(port, { code: codes.MSP_SET_NAME, data: buffer });
};
export const readUID = async (port) => {
const data = await execute(port, { code: codes.MSP_UID });
return times(() => (data.readU32() + 16 ** 6).toString(16).substr(-6), 3).join("");
};
export const readAnalogValues = async (port) => {
const data = await execute(port, { code: codes.MSP_ANALOG });
const values = {
voltage: data.readU8() / 10.0,
mahDrawn: data.readU16(),
rssi: Math.round((data.readU16() / 1023) * 100),
amperage: data.read16() / 100, // A
};
if (semver.gte(apiVersion(port), "1.41.0")) {
values.voltage = data.readU16() / 100;
}
return values;
};
export const readRawGPS = async (port) => {
const data = await execute(port, { code: codes.MSP_RAW_GPS });
return {
fix: !!data.readU8(),
numSat: data.readU8(),
lat: data.read32(),
lon: data.read32(),
alt: data.readU16(),
speed: data.readU16(),
groundCourse: data.readU16(),
};
};
export const readIMUData = async (port) => {
const data = await execute(port, { code: codes.MSP_RAW_IMU });
const accUnit = () => data.read16() / 512;
const gyroUnit = () => data.read16() * (4 / 16.4);
const mangetUnit = () => data.read16() / 1090;
return {
accelerometer: times(accUnit, 3),
gyroscope: times(gyroUnit, 3),
magnetometer: times(mangetUnit, 3),
};
};
export const readAttitude = async (port) => {
const data = await execute(port, { code: codes.MSP_ATTITUDE });
return {
roll: data.read16() / 10.0,
pitch: data.read16() / 10.0,
yaw: data.read16(), // z
};
};
const extractStatus = (data) => ({
cycleTime: data.readU16(),
i2cError: data.readU16(),
sensors: unpackValues(data.readU16(), sensorBits()),
mode: data.readU32(),
profile: data.readU8(),
});
export const readStatus = async (port) => {
const data = await execute(port, { code: codes.MSP_STATUS });
return extractStatus(data);
};
export const readExtendedStatus = async (port) => {
const data = await execute(port, { code: codes.MSP_STATUS_EX });
const api = apiVersion(port);
const status = {
...extractStatus(data),
cpuload: data.readU16(),
numProfiles: 0,
rateProfile: 0,
armingDisabledFlags: [],
};
if (semver.gte(api, "1.16.0")) {
status.numProfiles = data.readU8();
status.rateProfile = data.readU8();
}
if (semver.gte(api, "1.36.0")) {
// skip all this data for some reason
times(() => data.readU8(), data.readU8());
const flags = disarmFlagBits(api);
// Read arming disable flags
const numFlags = data.readU8();
const flagBits = data.readU32();
// read the enabled disarm flags from the mask
status.armingDisabledFlags = times((i) => { var _a; return (flagBits & (1 << i)) !== 0 && ((_a = flags[i]) !== null && _a !== void 0 ? _a : DisarmFlags.UNKNOWN); }, numFlags).filter((flag) => typeof flag === "number");
}
return status;
};
export const readEnabledFeatures = async (port) => {
const schema = availableFeatures(apiVersion(port));
const data = await execute(port, { code: codes.MSP_FEATURE_CONFIG });
const featureMask = data.readU32();
return Object.entries(schema)
.filter(([bit]) => (featureMask >> Number(bit)) % 2 !== 0)
.map(([, feature]) => feature);
};
export const writeEnabledFeatures = async (port, features) => {
const schema = availableFeatures(apiVersion(port));
const buffer = new WriteBuffer();
buffer.push32(Object.entries(schema)
.filter(([, feature]) => features.includes(feature))
.reduce((acc, [bit]) => acc | (1 << Number(bit)), 0));
await execute(port, {
code: codes.MSP_SET_FEATURE_CONFIG,
data: buffer,
});
};
export const readRcValues = async (port) => {
const data = await execute(port, { code: codes.MSP_RC });
return new Array(data.byteLength / 2).fill(0).map(() => data.readU16());
};
export const readRCTuning = async (port) => {
const version = apiVersion(port);
const data = await execute(port, { code: codes.MSP_RC_TUNING });
const tuning = {
rcRate: 0,
rcExpo: 0,
rollPitchRate: 0,
pitchRate: 0,
rollRate: 0,
yawRate: 0,
dynamicThrottlePid: 0,
throttleMid: 0,
throttleExpo: 0,
dynamicThrottleBreakpoint: 0,
rcYawExpo: 0,
rcYawRate: 0,
rcPitchRate: 0,
rcPitchExpo: 0,
throttleLimitType: 0,
throttleLimitPercent: 0,
rollRateLimit: 0,
pitchRateLimit: 0,
yawRateLimit: 0,
};
tuning.rcRate = parseFloat((data.readU8() / 100).toFixed(2));
tuning.rcExpo = parseFloat((data.readU8() / 100).toFixed(2));
if (semver.lt(version, "1.7.0")) {
tuning.rollPitchRate = parseFloat((data.readU8() / 100).toFixed(2));
}
else {
tuning.rollRate = parseFloat((data.readU8() / 100).toFixed(2));
tuning.pitchRate = parseFloat((data.readU8() / 100).toFixed(2));
}
tuning.yawRate = parseFloat((data.readU8() / 100).toFixed(2));
tuning.dynamicThrottlePid = parseFloat((data.readU8() / 100).toFixed(2));
tuning.throttleMid = parseFloat((data.readU8() / 100).toFixed(2));
tuning.throttleExpo = parseFloat((data.readU8() / 100).toFixed(2));
tuning.dynamicThrottleBreakpoint = semver.gte(version, "1.7.0")
? data.readU16()
: 0;
if (semver.gte(version, "1.10.0")) {
tuning.rcYawExpo = parseFloat((data.readU8() / 100).toFixed(2));
if (semver.gte(version, "1.16.0")) {
tuning.rcYawRate = parseFloat((data.readU8() / 100).toFixed(2));
}
}
if (semver.gte(version, "1.37.0")) {
tuning.rcPitchRate = parseFloat((data.readU8() / 100).toFixed(2));
tuning.rcPitchExpo = parseFloat((data.readU8() / 100).toFixed(2));
}
if (semver.gte(version, "1.41.0")) {
tuning.throttleLimitType = data.readU8();
tuning.throttleLimitPercent = data.readU8();
}
if (semver.gte(version, "1.42.0")) {
tuning.rollRateLimit = data.readU16();
tuning.pitchRateLimit = data.readU16();
tuning.yawRateLimit = data.readU16();
}
return tuning;
};
export const readRCDeadband = async (port) => {
const data = await execute(port, { code: codes.MSP_RC_DEADBAND });
return {
deadband: data.readU8(),
yawDeadband: data.readU8(),
altHoldDeadhand: data.readU8(),
deadband3dThrottle: semver.gte(apiVersion(port), "1.17.0")
? data.readU16()
: 0,
};
};
export const writeArming = async (port, { armingDisabled, runawayTakeoffPreventionDisabled, }) => {
const data = new WriteBuffer();
data.push8(Number(armingDisabled));
data.push8(Number(runawayTakeoffPreventionDisabled));
await execute(port, { code: codes.MSP_ARMING_DISABLE, data });
};
export const calibrateAccelerometer = async (port) => {
await execute(port, { code: codes.MSP_ACC_CALIBRATION });
// This command executes on the device, but doesn't actually produce anything
// for about 2 seconds, so resolve after 2 seconds
await sleep(2000);
};
export const resetConfig = async (port) => {
await execute(port, { code: codes.MSP_RESET_CONF });
// This command executes on the device, but doesn't actually produce anything
// for about 2 seconds, so resolve after 2 seconds
await sleep(2000);
};
/**
* Set the device to reboot, returning true if successful
*/
export const reboot = async (port, type) => {
const api = apiVersion(port);
const data = await execute(port, {
code: codes.MSP_SET_REBOOT,
data: type ? [type] : undefined,
timeout: 3000,
}).catch((e) => {
if (!isOpen(port)) {
return undefined;
}
throw e;
});
if (data && semver.gte(api, "1.40.0")) {
const rebootType = data.read8();
if (rebootType === RebootTypes.MSC || rebootType === RebootTypes.MSC_UTC) {
if (data.read8() === 0) {
return false;
}
}
}
return true;
};
export const commit = async (port) => {
await execute(port, { code: codes.MSP_EEPROM_WRITE });
};
export const readBoardAlignmentConfig = async (port) => {
const data = await execute(port, { code: codes.MSP_BOARD_ALIGNMENT_CONFIG });
return {
roll: data.read16(),
pitch: data.read16(),
yaw: data.read16(),
};
};
export const writeBoardAlignmentConfig = async (port, { roll, pitch, yaw }) => {
await execute(port, {
code: codes.MSP_SET_BOARD_ALIGNMENT_CONFIG,
data: new WriteBuffer().push16(roll).push16(pitch).push16(yaw),
});
};
export const readAdvancedConfig = async (port) => {
const api = apiVersion(port);
const data = await execute(port, { code: codes.MSP_ADVANCED_CONFIG });
const config = {
gyroSyncDenom: 0,
pidProcessDenom: 0,
useUnsyncedPwm: false,
fastPwmProtocol: 0,
gyroToUse: 0,
motorPwmRate: 0,
digitalIdlePercent: 0,
gyroUse32kHz: false,
motorPwmInversion: 0,
gyroHighFsr: 0,
gyroMovementCalibThreshold: 0,
gyroCalibDuration: 0,
gyroOffsetYaw: 0,
gyroCheckOverflow: 0,
debugMode: 0,
debugModeCount: 0,
};
config.gyroSyncDenom = data.readU8();
config.pidProcessDenom = data.readU8();
config.useUnsyncedPwm = data.readU8() !== 0;
config.fastPwmProtocol = toIdentifier(escProtocols(api), data.readU8());
config.motorPwmRate = data.readU16();
if (semver.gte(api, "1.24.0")) {
config.digitalIdlePercent = data.readU16() / 100;
}
if (semver.gte(api, "1.25.0")) {
const gyroUse32kHz = data.readU8();
if (semver.lt(api, "1.41.0")) {
config.gyroUse32kHz = gyroUse32kHz !== 0;
}
}
if (semver.gte(api, "1.42.0")) {
config.motorPwmInversion = data.readU8();
// gyroToUse (read by MSP_SENSOR_ALIGNMENT)
config.gyroToUse = data.readU8();
config.gyroHighFsr = data.readU8();
config.gyroMovementCalibThreshold = data.readU8();
config.gyroCalibDuration = data.readU16();
config.gyroOffsetYaw = data.readU16();
config.gyroCheckOverflow = data.readU8();
config.debugMode = data.readU8();
config.debugModeCount = data.readU8();
}
return config;
};
export const writeAdvancedConfig = async (port, config) => {
var _a;
const api = apiVersion(port);
const buffer = new WriteBuffer();
buffer
.push8(config.gyroSyncDenom)
.push8(config.pidProcessDenom)
.push8(config.useUnsyncedPwm ? 1 : 0)
.push8((_a = fromIdentifier(escProtocols(api), config.fastPwmProtocol)) !== null && _a !== void 0 ? _a : config.fastPwmProtocol)
.push16(config.motorPwmRate);
if (semver.gte(api, "1.24.0")) {
buffer.push16(config.digitalIdlePercent * 100);
}
if (semver.gte(api, "1.25.0")) {
let gyroUse32kHz = 0;
if (semver.lt(api, "1.41.0")) {
gyroUse32kHz = config.gyroUse32kHz ? 1 : 0;
}
buffer.push8(gyroUse32kHz);
}
if (semver.gte(api, "1.42.0")) {
buffer
.push8(config.motorPwmInversion)
.push8(config.gyroToUse)
.push8(config.gyroHighFsr)
.push8(config.gyroMovementCalibThreshold)
.push16(config.gyroCalibDuration)
.push16(config.gyroOffsetYaw)
.push8(config.gyroCheckOverflow)
.push8(config.debugMode);
}
await execute(port, { code: codes.MSP_SET_ADVANCED_CONFIG, data: buffer });
};
export const writePartialAdvancedConfig = partialWriteFunc(readAdvancedConfig, writeAdvancedConfig);
const ALLOWED_DSHOT_CONDITIONS = [Beepers.RX_SET, Beepers.RX_LOST];
export const readBeeperConfig = async (port) => {
const data = await execute(port, { code: codes.MSP_BEEPER_CONFIG });
const api = apiVersion(port);
const beeperSchema = beeperBits(api);
return {
// For some reason, the flag bits are actually inverted for both
// read and write
conditions: unpackValues(data.readU32(), beeperSchema, { inverted: true }),
dshot: {
tone: semver.gte(api, "1.37.0") ? data.readU8() : 0,
conditions: semver.gte(api, "1.39.0")
? unpackValues(data.readU32(), beeperSchema, {
inverted: true,
}).filter((beeper) => ALLOWED_DSHOT_CONDITIONS.includes(beeper))
: [],
},
};
};
export const writeBeeperConfig = async (port, config) => {
const api = apiVersion(port);
const beeperSchema = beeperBits(api);
const buffer = new WriteBuffer();
buffer.push32(packValues(config.conditions, beeperSchema, {
inverted: true,
}));
if (semver.gte(api, "1.37.0")) {
buffer.push8(config.dshot.tone);
}
if (semver.gte(api, "1.39.0")) {
buffer.push32(packValues(config.dshot.conditions.filter((con) => ALLOWED_DSHOT_CONDITIONS.includes(con)), beeperSchema, { inverted: true }));
}
await execute(port, { code: codes.MSP_SET_BEEPER_CONFIG, data: buffer });
};
export const writePartialBeeperConfig = partialWriteFunc(readBeeperConfig, writeBeeperConfig);
export const readDisabledSensors = async (port) => {
const data = await execute(port, { code: codes.MSP_SENSOR_CONFIG });
const schema = sensorBits();
const disabled = [];
schema.forEach((sensor) => {
if (data.remaining() > 0 && data.read8() === 1) {
disabled.push(sensor);
}
});
return disabled;
};
export const writeDisabledSensors = async (port, disabledSensors) => {
const buffer = new WriteBuffer();
[Sensors.ACCELEROMETER, Sensors.BAROMETER, Sensors.MAGNETOMETER].forEach((sensor) => {
buffer.push8(disabledSensors.includes(sensor) ? 1 : 0);
});
await execute(port, { code: codes.MSP_SET_SENSOR_CONFIG, data: buffer });
};
export const readRxConfig = async (port) => {
const api = apiVersion(port);
const data = await execute(port, { code: codes.MSP_RX_CONFIG });
const initial = {
serialProvider: toIdentifier(serialRxProviders(api), data.readU8()),
stick: {
max: data.readU16(),
center: data.readU16(),
min: data.readU16(),
},
spektrumSatBind: data.readU8(),
rxMinUsec: data.readU16(),
rxMaxUsec: data.readU16(),
...(semver.gte(api, "1.20.0")
? {
interpolation: toIdentifier(rcInterpolations(), data.readU8()),
interpolationInterval: data.readU8(),
airModeActivateThreshold: data.readU16(),
}
: {
interpolation: RcInterpolations.AUTO,
interpolationInterval: 0,
airModeActivateThreshold: 0,
}),
...(semver.gte(api, "1.31.0")
? {
spi: {
protocol: toIdentifier(spiRxProtocols(api), data.readU8()),
id: data.readU32(),
rfChannelCount: data.readU8(),
},
fpvCamAngleDegrees: data.readU8(),
}
: {
spi: {
protocol: SpiRxProtocols.NRF24_V202_250K,
id: 0,
rfChannelCount: 0,
},
fpvCamAngleDegrees: 0,
}),
};
const smoothing = semver.gte(api, "1.40.0")
? {
channels: toIdentifier(rcSmoothingChannels(), data.readU8()),
type: toIdentifier(rcSmoothingTypes(), data.readU8()),
inputCutoff: data.readU8(),
derivativeCutoff: data.readU8(),
inputType: toIdentifier(rcSmoothingInputTypes(), data.readU8()),
derivativeType: toIdentifier(rcSmoothingDerivativeTypes(api), data.readU8()),
}
: {
channels: RcSmoothingChannels.RP,
type: RcSmoothingTypes.INTERPOLATION,
inputCutoff: 0,
derivativeCutoff: 0,
inputType: RcSmoothingInputTypes.PT1,
derivativeType: RcSmoothingDerivativeTypes.AUTO,
};
return {
...initial,
...(semver.gte(api, "1.42.0")
? { usbCdcHidType: data.readU8() }
: { usbCdcHidType: 0 }),
rcSmoothing: {
...smoothing,
...(semver.gte(api, "1.42.0")
? {
autoSmoothness: data.readU8(),
}
: { autoSmoothness: 0 }),
},
};
};
export const writeRxConfig = async (port, config) => {
var _a, _b, _c, _d, _e, _f, _g;
const api = apiVersion(port);
const buffer = new WriteBuffer();
buffer
.push8((_a = fromIdentifier(serialRxProviders(api), config.serialProvider)) !== null && _a !== void 0 ? _a : config.serialProvider)
.push16(config.stick.max)
.push16(config.stick.center)
.push16(config.stick.center)
.push8(config.spektrumSatBind)
.push16(config.rxMinUsec)
.push16(config.rxMaxUsec);
if (semver.gte(api, "1.20.0")) {
buffer
.push8((_b = fromIdentifier(rcInterpolations(), config.interpolation)) !== null && _b !== void 0 ? _b : config.interpolation)
.push8(config.interpolationInterval)
.push16(config.airModeActivateThreshold);
}
if (semver.gte(api, "1.31.0")) {
buffer
.push8((_c = fromIdentifier(spiRxProtocols(api), config.spi.protocol)) !== null && _c !== void 0 ? _c : config.spi.protocol)
.push32(config.spi.id)
.push8(config.spi.rfChannelCount)
.push8(config.fpvCamAngleDegrees);
}
if (semver.gte(api, "1.40.0")) {
buffer
.push8((_d = fromIdentifier(rcSmoothingChannels(), config.rcSmoothing.channels)) !== null && _d !== void 0 ? _d : config.rcSmoothing.channels)
.push8((_e = fromIdentifier(rcSmoothingTypes(), config.rcSmoothing.type)) !== null && _e !== void 0 ? _e : config.rcSmoothing.type)
.push8(config.rcSmoothing.inputCutoff)
.push8(config.rcSmoothing.derivativeCutoff)
.push8((_f = fromIdentifier(rcSmoothingInputTypes(), config.rcSmoothing.inputType)) !== null && _f !== void 0 ? _f : config.rcSmoothing.inputType)
.push8((_g = fromIdentifier(rcSmoothingDerivativeTypes(api), config.rcSmoothing.derivativeType)) !== null && _g !== void 0 ? _g : config.rcSmoothing.derivativeType);
}
if (semver.gte(api, "1.42.0")) {
buffer.push8(config.usbCdcHidType).push8(config.rcSmoothing.autoSmoothness);
}
await execute(port, { code: codes.MSP_SET_RX_CONFIG, data: buffer });
};
export const writePartialRxConfig = partialWriteFunc(readRxConfig, writeRxConfig);
export const readRxMap = async (port) => {
const data = await execute(port, { code: codes.MSP_RX_MAP });
const schema = channelLetters();
const map = new Array(8).fill("A");
schema.forEach((letter) => {
map[data.readU8()] = letter;
});
return map;
};
export const writeRxMap = async (port, map) => {
const buffer = new WriteBuffer();
const schema = channelLetters();
schema.forEach((letter) => {
const value = typeof letter !== "number" ? map.indexOf(letter) : letter;
buffer.push8(value);
});
await execute(port, { code: codes.MSP_SET_RX_MAP, data: buffer });
};
export const readRssiConfig = async (port) => {
const data = await execute(port, { code: codes.MSP_RSSI_CONFIG });
return {
channel: data.readU8(),
};
};
export const writeRssiConfig = async (port, config) => {
const buffer = new WriteBuffer();
buffer.push8(config.channel);
await execute(port, { code: codes.MSP_SET_RSSI_CONFIG, data: buffer });
};
export const readGpsConfig = async (port) => {
const api = apiVersion(port);
const data = await execute(port, { code: codes.MSP_GPS_CONFIG });
// gps not enabled, not exactly good api from the FC
if (data.byteLength === 0) {
return {
provider: GpsProtocols.NMEA,
ubloxSbas: GpsSbasTypes.AUTO,
autoConfig: false,
autoBaud: false,
homePointOnce: false,
ubloxUseGalileo: false,
};
}
return {
provider: toIdentifier(gpsProtocols(api), data.readU8()),
ubloxSbas: data.readU8(),
autoConfig: semver.gte(api, "1.34.0") ? data.readU8() === 1 : false,
autoBaud: semver.gte(api, "1.34.0") ? data.readU8() === 1 : false,
homePointOnce: semver.gte(api, "1.43.0") ? data.readU8() === 1 : false,
ubloxUseGalileo: semver.gte(api, "1.43.0") ? data.readU8() === 1 : false,
};
};
export const writeGpsConfig = async (port, config) => {
var _a;
const api = apiVersion(port);
const buffer = new WriteBuffer();
buffer
.push8((_a = fromIdentifier(gpsProtocols(api), config.provider)) !== null && _a !== void 0 ? _a : config.provider)
.push8(config.ubloxSbas);
if (semver.gte(api, "1.34.0")) {
buffer.push8(config.autoConfig ? 1 : 0).push8(config.autoBaud ? 1 : 0);
}
if (semver.gte(api, "1.43.0")) {
buffer
.push8(config.homePointOnce ? 1 : 0)
.push8(config.ubloxUseGalileo ? 1 : 0);
}
await execute(port, { code: codes.MSP_SET_GPS_CONFIG, data: buffer });
};
export const readBlackboxConfig = async (port) => {
const api = apiVersion(port);
const data = await execute(port, { code: codes.MSP_BLACKBOX_CONFIG });
return {
supported: (data.readU8() & 1) !== 0,
device: toIdentifier(blackboxDevices(api), data.readU8()),
rateNum: data.readU8(),
rateDenom: data.readU8(),
pDenom: semver.gte(api, "1.36.0") ? data.readU16() : 0,
sampleRate: semver.gte(api, "1.44.0") ? data.readU8() : 0,
};
};
export const writeBlackboxConfig = async (port, config) => {
var _a;
const api = apiVersion(port);
const buffer = new WriteBuffer();
buffer
.push8((_a = fromIdentifier(blackboxDevices(api), config.device)) !== null && _a !== void 0 ? _a : config.device)
.push8(config.rateNum)
.push8(config.rateDenom);
if (semver.gte(api, "1.36.0")) {
buffer.push16(config.pDenom);
}
if (semver.gte(api, "1.44.0")) {
buffer.push8(config.sampleRate);
}
await execute(port, { code: codes.MSP_SET_BLACKBOX_CONFIG, data: buffer });
};
export const writePartialBlackboxConfig = partialWriteFunc(readBlackboxConfig, writeBlackboxConfig);
/**
* Return the base 64 encoded result of the blackbox config
*/
export const readDataFlashChunk = async (port, address, blockSize) => {
const api = apiVersion(port);
const args = new WriteBuffer();
args.push32(address);
if (semver.gte(api, "1.31.0")) {
args.push16(blockSize);
}
if (semver.gte(api, "1.36.0")) {
args.push8(1);
}
const data = await execute(port, {
code: codes.MSP_DATAFLASH_READ,
data: args,
timeout: 1000,
match: (response) => response.readU32() === address,
});
// ignore the address as this was
// checked by the executor
data.readU32();
const headerSize = semver.gte(api, "1.31.0") ? 7 : 4;
const dataSize = semver.gte(api, "1.31.0")
? data.readU16()
: data.buffer.byteLength - headerSize;
const compressed = semver.gte(api, "1.31.0") && data.readU8() === 1;
/* Strip that address off the front of the reply and deliver it separately so the caller doesn't have to
* figure out the reply format:
*/
if (!compressed) {
return Buffer.from(new Uint8Array(data.buffer, data.byteOffset + headerSize, dataSize));
}
// Read compressed char count to avoid decoding stray bit sequences as bytes
const compressedCharCount = data.readU16();
// Compressed format uses 2 additional bytes as a pseudo-header to denote the number of uncompressed bytes
const compressedArray = Buffer.from(new Uint8Array(data.buffer, data.byteOffset + headerSize + 2, dataSize - 2));
const decompressedArray = huffmanDecodeBuffer(compressedArray, compressedCharCount);
return decompressedArray;
};
export const readDataFlashSummary = async (port) => {
const data = await execute(port, { code: codes.MSP_DATAFLASH_SUMMARY });
if (data.byteLength >= 13) {
const flags = data.readU8();
return {
ready: (flags & 1) !== 0,
supported: (flags & 2) !== 0,
sectors: data.readU32(),
totalSize: data.readU32(),
usedSize: data.readU32(),
};
}
// Firmware version too old to support MSP_DATAFLASH_SUMMARY
return {
ready: false,
supported: false,
sectors: 0,
totalSize: 0,
usedSize: 0,
};
};
export const readSdCardSummary = async (port) => {
const data = await execute(port, { code: codes.MSP_SDCARD_SUMMARY });
const flags = data.readU8();
return {
supported: (flags & 0x01) !== 0,
state: data.readU8(),
filesystemLastError: data.readU8(),
freeSizeKB: data.readU32(),
totalSizeKB: data.readU32(),
};
};
export const eraseDataFlash = async (port) => {
await execute(port, { code: codes.MSP_DATAFLASH_ERASE });
};
//# sourceMappingURL=index.js.map