/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
/*
implementation of MSP and BLHeli-4way protocols for pass-through ESC
calibration and firmware update
With thanks to betaflight for a great reference implementation
*/
#pragma once
#include
#include
#if HAL_SUPPORT_RCOUT_SERIAL
#define HAVE_AP_BLHELI_SUPPORT
#include
#include "msp_protocol.h"
#include "blheli_4way_protocol.h"
#define AP_BLHELI_MAX_ESCS 8
class AP_BLHeli {
public:
AP_BLHeli();
void update(void);
void update_telemetry(void);
bool process_input(uint8_t b);
static const struct AP_Param::GroupInfo var_info[];
struct telem_data {
uint8_t temperature; // degrees C
uint16_t voltage; // volts * 100
uint16_t current; // amps * 100
uint16_t consumption;// mAh
uint16_t rpm; // eRPM
uint16_t count;
uint32_t timestamp_ms;
};
// get the most recent telemetry data packet for a motor
bool get_telem_data(uint8_t esc_index, struct telem_data &td);
static AP_BLHeli *get_singleton(void) {
return _singleton;
}
// send ESC telemetry messages over MAVLink
void send_esc_telemetry_mavlink(uint8_t mav_chan);
private:
static AP_BLHeli *_singleton;
// mask of channels to use for BLHeli protocol
AP_Int32 channel_mask;
AP_Int32 channel_reversible_mask;
AP_Int8 channel_auto;
AP_Int8 run_test;
AP_Int16 timeout_sec;
AP_Int16 telem_rate;
AP_Int8 debug_level;
AP_Int8 output_type;
AP_Int8 control_port;
AP_Int8 motor_poles;
enum mspState {
MSP_IDLE=0,
MSP_HEADER_START,
MSP_HEADER_M,
MSP_HEADER_ARROW,
MSP_HEADER_SIZE,
MSP_HEADER_CMD,
MSP_COMMAND_RECEIVED
};
enum mspPacketType {
MSP_PACKET_COMMAND,
MSP_PACKET_REPLY
};
enum escProtocol {
PROTOCOL_SIMONK = 0,
PROTOCOL_BLHELI = 1,
PROTOCOL_KISS = 2,
PROTOCOL_KISSALL = 3,
PROTOCOL_CASTLE = 4,
PROTOCOL_MAX = 5,
PROTOCOL_NONE = 0xfe,
PROTOCOL_4WAY = 0xff
};
enum motorPwmProtocol {
PWM_TYPE_STANDARD = 0,
PWM_TYPE_ONESHOT125,
PWM_TYPE_ONESHOT42,
PWM_TYPE_MULTISHOT,
PWM_TYPE_BRUSHED,
PWM_TYPE_DSHOT150,
PWM_TYPE_DSHOT300,
PWM_TYPE_DSHOT600,
PWM_TYPE_DSHOT1200,
PWM_TYPE_PROSHOT1000,
};
enum MSPFeatures {
FEATURE_RX_PPM = 1 << 0,
FEATURE_INFLIGHT_ACC_CAL = 1 << 2,
FEATURE_RX_SERIAL = 1 << 3,
FEATURE_MOTOR_STOP = 1 << 4,
FEATURE_SERVO_TILT = 1 << 5,
FEATURE_SOFTSERIAL = 1 << 6,
FEATURE_GPS = 1 << 7,
FEATURE_RANGEFINDER = 1 << 9,
FEATURE_TELEMETRY = 1 << 10,
FEATURE_3D = 1 << 12,
FEATURE_RX_PARALLEL_PWM = 1 << 13,
FEATURE_RX_MSP = 1 << 14,
FEATURE_RSSI_ADC = 1 << 15,
FEATURE_LED_STRIP = 1 << 16,
FEATURE_DASHBOARD = 1 << 17,
FEATURE_OSD = 1 << 18,
FEATURE_CHANNEL_FORWARDING = 1 << 20,
FEATURE_TRANSPONDER = 1 << 21,
FEATURE_AIRMODE = 1 << 22,
FEATURE_RX_SPI = 1 << 25,
FEATURE_SOFTSPI = 1 << 26,
FEATURE_ESC_SENSOR = 1 << 27,
FEATURE_ANTI_GRAVITY = 1 << 28,
FEATURE_DYNAMIC_FILTER = 1 << 29,
};
/*
state of MSP command processing
*/
struct {
enum mspState state;
enum mspPacketType packetType;
uint8_t offset;
uint8_t dataSize;
uint8_t checksum;
uint8_t buf[192];
uint8_t cmdMSP;
enum escProtocol escMode;
uint8_t portIndex;
} msp;
enum blheliState {
BLHELI_IDLE=0,
BLHELI_HEADER_START,
BLHELI_HEADER_CMD,
BLHELI_HEADER_ADDR_LOW,
BLHELI_HEADER_ADDR_HIGH,
BLHELI_HEADER_LEN,
BLHELI_CRC1,
BLHELI_CRC2,
BLHELI_COMMAND_RECEIVED
};
/*
state of blheli 4way protocol handling
*/
struct {
enum blheliState state;
uint8_t command;
uint16_t address;
uint16_t param_len;
uint16_t offset;
uint8_t buf[256+3+8];
uint8_t crc1;
uint16_t crc;
bool connected[AP_BLHELI_MAX_ESCS];
uint8_t interface_mode[AP_BLHELI_MAX_ESCS];
uint8_t deviceInfo[AP_BLHELI_MAX_ESCS][4];
uint8_t chan;
uint8_t ack;
} blheli;
const uint16_t esc_status_addr = 0xEB00;
// protocol reported by ESC in esc_status
enum esc_protocol {
ESC_PROTOCOL_NONE=0,
ESC_PROTOCOL_NORMAL=1,
ESC_PROTOCOL_ONESHOT125=2,
ESC_PROTOCOL_DSHOT=5,
};
// ESC status structure at address 0xEB00
struct PACKED esc_status {
uint8_t unknown[3];
enum esc_protocol protocol;
uint32_t good_frames;
uint32_t bad_frames;
uint32_t unknown2;
};
AP_HAL::UARTDriver *uart;
AP_HAL::UARTDriver *debug_uart;
AP_HAL::UARTDriver *telem_uart;
static const uint8_t max_motors = AP_BLHELI_MAX_ESCS;
uint8_t num_motors;
struct telem_data last_telem[max_motors];
// have we initialised the interface?
bool initialised;
// last valid packet timestamp
uint32_t last_valid_ms;
// when did we start the serial ESC output?
uint32_t serial_start_ms;
// have we disabled motor outputs?
bool motors_disabled;
// have we locked the UART?
bool uart_locked;
// mapping from BLHeli motor numbers to RC output channels
uint8_t motor_map[max_motors];
uint16_t motor_mask;
// when did we last request telemetry?
uint32_t last_telem_request_us;
uint8_t last_telem_esc;
static const uint8_t telem_packet_size = 10;
bool telem_uart_started;
uint32_t last_telem_byte_read_us;
int8_t last_control_port;
bool msp_process_byte(uint8_t c);
void blheli_crc_update(uint8_t c);
bool blheli_4way_process_byte(uint8_t c);
void msp_send_reply(uint8_t cmd, const uint8_t *buf, uint8_t len);
void putU16(uint8_t *b, uint16_t v);
uint16_t getU16(const uint8_t *b);
void putU32(uint8_t *b, uint32_t v);
void putU16_BE(uint8_t *b, uint16_t v);
void msp_process_command(void);
void blheli_send_reply(const uint8_t *buf, uint16_t len);
uint16_t BL_CRC(const uint8_t *buf, uint16_t len);
bool isMcuConnected(void);
void setDisconnected(void);
bool BL_SendBuf(const uint8_t *buf, uint16_t len);
bool BL_ReadBuf(uint8_t *buf, uint16_t len);
uint8_t BL_GetACK(uint16_t timeout_ms=2);
bool BL_SendCMDSetAddress();
bool BL_ReadA(uint8_t cmd, uint8_t *buf, uint16_t n);
bool BL_ConnectEx(void);
bool BL_SendCMDKeepAlive(void);
bool BL_PageErase(void);
void BL_SendCMDRunRestartBootloader(void);
uint8_t BL_SendCMDSetBuffer(const uint8_t *buf, uint16_t nbytes);
bool BL_WriteA(uint8_t cmd, const uint8_t *buf, uint16_t nbytes, uint32_t timeout);
uint8_t BL_WriteFlash(const uint8_t *buf, uint16_t n);
bool BL_VerifyFlash(const uint8_t *buf, uint16_t n);
void blheli_process_command(void);
void run_connection_test(uint8_t chan);
uint8_t telem_crc8(uint8_t crc, uint8_t crc_seed) const;
void read_telemetry_packet(void);
// protocol handler hook
bool protocol_handler(uint8_t , AP_HAL::UARTDriver *);
};
#endif // HAL_SUPPORT_RCOUT_SERIAL