1215 lines
37 KiB
C++
1215 lines
37 KiB
C++
/*
|
|
* Copyright (C) EdgeTX
|
|
*
|
|
* Based on code named
|
|
* opentx - https://github.com/opentx/opentx
|
|
* th9x - http://code.google.com/p/th9x
|
|
* er9x - http://code.google.com/p/er9x
|
|
* gruvin9x - http://code.google.com/p/gruvin9x
|
|
*
|
|
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include "afhds3.h"
|
|
#include "afhds3_transport.h"
|
|
#include "afhds3_config.h"
|
|
|
|
#include "pulses.h"
|
|
|
|
#include "../debug.h"
|
|
#include "../definitions.h"
|
|
|
|
#include "telemetry/telemetry.h"
|
|
#include "mixer_scheduler.h"
|
|
#include "hal/module_driver.h"
|
|
#include "hal/module_port.h"
|
|
|
|
#define SET_DIRTY() storageDirty(EE_MODEL)
|
|
|
|
#define checkDirtyFlag(dirtyCmd) (cfg->others.dirtyFlag & ((uint32_t) 1 << dirtyCmd))
|
|
#define clearDirtyFlag(dirtyCmd) (cfg->others.dirtyFlag &= ~((uint32_t) 1 << dirtyCmd))
|
|
|
|
#define FAILSAFE_HOLD 1
|
|
#define FAILSAFE_CUSTOM 2
|
|
|
|
#define FAILSAFE_HOLD_VALUE 0x8000
|
|
#define FAILSAFE_NOPULSES_VALUE 0x8001
|
|
|
|
#define MAX_NO_OF_MODELS 20
|
|
|
|
//get channel value outside of afhds3 namespace
|
|
int32_t getChannelValue(uint8_t channel);
|
|
void processFlySkyAFHDS3Sensor(const uint8_t * packet, uint8_t type);
|
|
void processFlySkySensor(const uint8_t * packet, uint8_t type);
|
|
|
|
namespace afhds3
|
|
{
|
|
|
|
static uint8_t _phyMode_channels[] = {
|
|
18, // CLASSIC_FLCR1_18CH
|
|
10, // CLASSIC_FLCR6_10CH
|
|
18, // ROUTINE_FLCR1_18CH
|
|
8, // ROUTINE_FLCR6_8CH
|
|
12, // ROUTINE_LORA_12CH
|
|
};
|
|
|
|
// enum COMMAND_DIRECTION
|
|
// {
|
|
// RADIO_TO_MODULE = 0,
|
|
// MODULE_TO_RADIO = 1
|
|
// };
|
|
|
|
// enum DATA_TYPE
|
|
// {
|
|
// READY_DT, // 8 bytes 0x01 Not ready 0x02 Ready
|
|
// STATE_DT, // See MODULE_STATE
|
|
// MODE_DT,
|
|
// MOD_CONFIG_DT,
|
|
// CHANNELS_DT,
|
|
// TELEMETRY_DT,
|
|
// MODULE_POWER_DT,
|
|
// MODULE_VERSION_DT,
|
|
// EMPTY_DT,
|
|
// };
|
|
|
|
//Product number
|
|
#define PN_FRM301 ( (0x1234<<16) | 0x0003 )
|
|
#define PN_FRM004 ( (0x1234<<16) | 0x0007 )
|
|
#define PN_FRM005 ( (0x1234<<16) | 0x0009 )
|
|
|
|
#define PN_FTR10 ( (0x1234<<16) | 0x0002 )
|
|
#define PN_FGR4 ( (0x1234<<16) | 0x0004 )
|
|
|
|
#define PN_FTR4 ( (0x1234<<16) | 0x0006 )
|
|
#define PN_FGR4S ( (0x1234<<16) | 0x0006 )
|
|
#define PN_FGR4P ( (0x1234<<16) | 0x0006 )
|
|
|
|
#define PN_FTR16S ( (0x0001<<16) | 0x0103 )
|
|
//#define PN_MINIZ ( (0x0001<<16) | 0x0104 )
|
|
|
|
#define PN_FTR8B ( (0x0001<<16) | 0x0105 )
|
|
#define PN_FTR12B ( (0x0001<<16) | 0x0106 )
|
|
#define PN_FGR8B ( (0x0001<<16) | 0x0107 )
|
|
#define PN_FGR12B ( (0x0001<<16) | 0x0108 )
|
|
#define PN_GMR ( (0x0001<<16) | 0x0109 )
|
|
#define PN_TMR ( (0x0001<<16) | 0x010A )
|
|
#define PN_INR4_GYB ( (0x0001<<16) | 0x010B )
|
|
#define PN_INR6_HS ( (0x0001<<16) | 0x010C )
|
|
#define PN_FTR4B ( (0x0001<<16) | 0x010D )
|
|
#define PN_FGR4B ( (0x0001<<16) | 0x010E )
|
|
#define PN_FBR12 ( (0x0001<<16) | 0x010F )
|
|
#define PN_INR6_FC ( (0x0001<<16) | 0x0110 )
|
|
#define PN_TR8B ( (0x0001<<16) | 0x0111 )
|
|
#define PN_FBR8 ( (0x0001<<16) | 0x0112 )
|
|
#define PN_FBR4 ( (0x0001<<16) | 0x0113 )
|
|
|
|
// enum used by command response -> translate to ModuleState
|
|
enum MODULE_READY_E {
|
|
MODULE_STATUS_UNKNOWN = 0x00,
|
|
MODULE_STATUS_NOT_READY = 0x01,
|
|
MODULE_STATUS_READY = 0x02
|
|
};
|
|
|
|
enum ModuleState {
|
|
STATE_NOT_READY = 0x00, // virtual, module not ready
|
|
STATE_HW_ERROR = 0x01,
|
|
STATE_BINDING = 0x02,
|
|
STATE_SYNC_RUNNING = 0x03, // sync state, ready to sync settings, receiver not connected
|
|
STATE_SYNC_DONE = 0x04, // sync state, ready to sync settings, receiver connected
|
|
STATE_STANDBY = 0x05, // standby state, ready to update modelID, and switch to RUN mode
|
|
STATE_UPDATING_WAIT = 0x06,
|
|
STATE_UPDATING_MOD = 0x07,
|
|
STATE_UPDATING_RX = 0x08,
|
|
STATE_UPDATING_RX_FAILED = 0x09,
|
|
STATE_RF_TESTING = 0x0a,
|
|
STATE_READY = 0x0b, // virtual, module ready awaiting to query module state
|
|
STATE_HW_TEST = 0xff,
|
|
};
|
|
|
|
// used for set command
|
|
enum MODULE_MODE_E {
|
|
STANDBY = 0x01,
|
|
BIND = 0x02, // after bind module will enter run mode
|
|
RUN = 0x03,
|
|
RX_UPDATE = 0x04, // after successful update module will enter standby mode,
|
|
// otherwise hw error will be raised
|
|
MODULE_MODE_UNKNOWN = 0xFF
|
|
};
|
|
|
|
enum CMD_RESULT {
|
|
FAILURE = 0x01,
|
|
SUCCESS = 0x02,
|
|
};
|
|
|
|
enum CHANNELS_DATA_MODE {
|
|
CHANNELS = 0x01,
|
|
FAIL_SAFE = 0x02,
|
|
};
|
|
|
|
PACK(struct ChannelsData {
|
|
uint8_t mode;
|
|
uint8_t channelsNumber;
|
|
int16_t data[AFHDS3_MAX_CHANNELS];
|
|
});
|
|
|
|
union ChannelsData_u {
|
|
ChannelsData data;
|
|
uint8_t buffer[sizeof(ChannelsData)];
|
|
};
|
|
|
|
PACK(struct TelemetryData {
|
|
uint8_t sensorType;
|
|
uint8_t length;
|
|
uint8_t type;
|
|
uint8_t semsorID;
|
|
uint8_t data[8];
|
|
});
|
|
|
|
enum MODULE_POWER_SOURCE {
|
|
INTERNAL = 0x01,
|
|
EXTERNAL = 0x02,
|
|
};
|
|
|
|
enum DeviceAddress {
|
|
TRANSMITTER = 0x01,
|
|
FRM303 = 0x04,
|
|
IRM301 = 0x05,
|
|
};
|
|
|
|
PACK(struct ModuleVersion
|
|
{
|
|
uint16_t companyNumber;
|
|
uint32_t txID;
|
|
uint32_t rxID;
|
|
uint32_t productNumber;
|
|
uint32_t hardwareVersion;
|
|
uint32_t bootloaderVersion;
|
|
uint32_t firmwareVersion;
|
|
uint32_t rfVersion;
|
|
});
|
|
|
|
|
|
PACK(struct ReceiverVersion
|
|
{
|
|
uint32_t ProductNumber;
|
|
uint16_t MainboardVersion;
|
|
uint16_t RFModuleVersion;
|
|
uint16_t BootloaderVersion;
|
|
uint16_t FirmwareVersion;
|
|
uint16_t RFLibraryVersion;
|
|
} );
|
|
|
|
PACK(struct CommandResult_s {
|
|
uint16_t command;
|
|
uint8_t result;
|
|
uint8_t respLen;
|
|
});
|
|
|
|
union AfhdsFrameData {
|
|
uint8_t value;
|
|
// Config_s Config;
|
|
ChannelsData Channels;
|
|
TelemetryData Telemetry;
|
|
ModuleVersion Version;
|
|
CommandResult_s CommandResult;
|
|
};
|
|
|
|
#define FRM302_STATUS 0x56
|
|
|
|
uint8_t receiver_type( unsigned long productnumber );
|
|
class ProtoState
|
|
{
|
|
public:
|
|
/**
|
|
* Initialize class for operation
|
|
* @param moduleIndex index of module one of INTERNAL_MODULE, EXTERNAL_MODULE
|
|
* @param resetFrameCount flag if current frame count should be reseted
|
|
*/
|
|
void init(uint8_t moduleIndex, void* buffer, etx_module_state_t* mod_st,
|
|
uint8_t fAddr);
|
|
|
|
/**
|
|
* Fills DMA buffers with frame to be send depending on actual state
|
|
*/
|
|
void setupFrame();
|
|
|
|
/**
|
|
* Sends prepared buffers
|
|
*/
|
|
void sendFrame() { trsp.sendBuffer(); }
|
|
|
|
/**
|
|
* Gets actual module status into provided buffer
|
|
* @param statusText target buffer for status
|
|
*/
|
|
void getStatusString(char* statusText) const;
|
|
|
|
/**
|
|
* Sends stop command to prevent any further module operations
|
|
*/
|
|
void stop();
|
|
|
|
Config_u* getConfig() { return &cfg; }
|
|
|
|
void applyConfigFromModel();
|
|
|
|
protected:
|
|
|
|
void resetConfig(uint8_t version);
|
|
|
|
private:
|
|
//friendship declaration - use for passing telemetry
|
|
friend void processTelemetryData(void* ctx, uint8_t data, uint8_t* buffer, uint8_t* len);
|
|
|
|
void processTelemetryData(uint8_t data, uint8_t* buffer, uint8_t* len);
|
|
|
|
void parseData(uint8_t* rxBuffer, uint8_t rxBufferCount);
|
|
|
|
void setState(ModuleState state);
|
|
|
|
bool syncSettings();
|
|
|
|
// void requestInfoAndRun(bool send = false);
|
|
|
|
uint8_t setFailSafe(int16_t* target, uint8_t rfchannelcount=AFHDS3_MAX_CHANNELS);
|
|
|
|
inline int16_t convert(int channelValue);
|
|
|
|
void sendChannelsData();
|
|
|
|
void clearFrameData();
|
|
|
|
bool isConnected();
|
|
bool hasTelemetry();
|
|
|
|
Transport trsp;
|
|
|
|
/**
|
|
* Index of the module
|
|
*/
|
|
uint8_t module_index;
|
|
|
|
/**
|
|
* Reported state of the HF module
|
|
*/
|
|
ModuleState state;
|
|
|
|
//bool modelIDSet;
|
|
//bool modelcfgGet;
|
|
uint8_t modelID;
|
|
uint32_t cmd_flg;
|
|
/**
|
|
* Command count used for counting actual number of commands sent in run mode
|
|
*/
|
|
uint32_t cmdCount;
|
|
|
|
/**
|
|
* Command index of command to be send when cmdCount reached necessary value
|
|
*/
|
|
uint32_t cmdIndex;
|
|
|
|
/**
|
|
* Pointer to module config - it is making operations easier and faster
|
|
*/
|
|
ModuleData* moduleData;
|
|
|
|
/**
|
|
* Actual module configuration - must be requested from module
|
|
*/
|
|
Config_u cfg;
|
|
|
|
/**
|
|
* Actual module version - must be requested from module
|
|
*/
|
|
ModuleVersion version;
|
|
ReceiverVersion rx_version;
|
|
};
|
|
|
|
static const char* const moduleStateText[] =
|
|
{
|
|
"Not ready",
|
|
"HW Error",
|
|
"Binding",
|
|
"Disconnected",
|
|
"Connected",
|
|
"Standby",
|
|
"Waiting for update",
|
|
"Updating",
|
|
"Updating RX",
|
|
"Updating RX failed",
|
|
"Testing",
|
|
"Ready",
|
|
"HW test"
|
|
};
|
|
|
|
static const COMMAND periodicRequestCommands[] =
|
|
{
|
|
COMMAND::MODULE_STATE,
|
|
// COMMAND::MODULE_GET_CONFIG,
|
|
COMMAND::VIRTUAL_FAILSAFE // One way failsafe
|
|
};
|
|
|
|
static const uint16_t AFHDS3_POWER[] = {56, 68, 80, 96, 108, 120, 132};
|
|
|
|
//Static collection of afhds3 object instances by module
|
|
static ProtoState protoState[MAX_MODULES];
|
|
|
|
void getStatusString(uint8_t module, char* buffer)
|
|
{
|
|
return protoState[module].getStatusString(buffer);
|
|
}
|
|
|
|
//friends function that can access telemetry parsing method
|
|
void processTelemetryData(void* ctx, uint8_t data, uint8_t* buffer, uint8_t* len)
|
|
{
|
|
auto mod_st = (etx_module_state_t*)ctx;
|
|
auto p_state = (ProtoState*)mod_st->user_data;
|
|
p_state->processTelemetryData(data, buffer, len);
|
|
}
|
|
|
|
void ProtoState::getStatusString(char* buffer) const
|
|
{
|
|
strcpy(buffer, state <= ModuleState::STATE_READY ? moduleStateText[state]
|
|
: "Unknown");
|
|
}
|
|
|
|
void ProtoState::processTelemetryData(uint8_t byte, uint8_t* buffer, uint8_t* len)
|
|
{
|
|
uint8_t maxSize = TELEMETRY_RX_PACKET_SIZE;
|
|
if (!trsp.processTelemetryData(byte, buffer, *len, maxSize))
|
|
return;
|
|
|
|
parseData(buffer, *len);
|
|
*len = 0;
|
|
}
|
|
|
|
bool ProtoState::isConnected()
|
|
{
|
|
return this->state == ModuleState::STATE_SYNC_DONE;
|
|
}
|
|
|
|
bool ProtoState::hasTelemetry()
|
|
{
|
|
if (cfg.version == 0)
|
|
return cfg.v0.IsTwoWay;
|
|
else
|
|
return cfg.v1.IsTwoWay;
|
|
}
|
|
|
|
void ProtoState::setupFrame()
|
|
{
|
|
bool trsp_error = false;
|
|
if (trsp.handleRetransmissions(trsp_error)) return;
|
|
|
|
if (trsp_error) {
|
|
this->state = ModuleState::STATE_NOT_READY;
|
|
clearFrameData();
|
|
}
|
|
|
|
if (this->state == ModuleState::STATE_NOT_READY) {
|
|
TRACE("AFHDS3 [GET MODULE READY]");
|
|
trsp.putFrame(COMMAND::MODULE_READY, FRAME_TYPE::REQUEST_GET_DATA);
|
|
return;
|
|
}
|
|
|
|
// Process backlog, not check states
|
|
if (trsp.processQueue()) return;
|
|
|
|
::ModuleSettingsMode moduleMode = getModuleMode(module_index);
|
|
|
|
if (moduleMode == ::ModuleSettingsMode::MODULE_MODE_BIND) {
|
|
if (state != STATE_BINDING) {
|
|
TRACE("AFHDS3 [BIND]");
|
|
applyConfigFromModel();
|
|
|
|
trsp.putFrame(COMMAND::MODULE_SET_CONFIG,
|
|
FRAME_TYPE::REQUEST_SET_EXPECT_DATA, cfg.buffer,
|
|
cfg.version == 0 ? sizeof(cfg.v0) : sizeof(cfg.v1));
|
|
|
|
trsp.enqueue(COMMAND::MODULE_MODE, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, true,
|
|
(uint8_t)MODULE_MODE_E::BIND);
|
|
return;
|
|
}
|
|
}
|
|
else if (moduleMode == ::ModuleSettingsMode::MODULE_MODE_RANGECHECK) {
|
|
TRACE("AFHDS3 [RANGE CHECK] not supported");
|
|
}
|
|
else if (moduleMode == ::ModuleSettingsMode::MODULE_MODE_NORMAL) {
|
|
|
|
// If module is ready but not started
|
|
if (this->state == ModuleState::STATE_READY) {
|
|
trsp.putFrame(MODULE_STATE, FRAME_TYPE::REQUEST_GET_DATA);
|
|
return;
|
|
}
|
|
|
|
// ModelID change detection, if changed => STATE_STANDBY => Set ModelID => RUN mode => STATE_SYNC_XXX
|
|
uint8_t newModelID = g_model.header.modelId[module_index] % MAX_NO_OF_MODELS; // Rotate the model ID when exceed no. of stored models
|
|
if (modelID != newModelID)
|
|
{
|
|
if (this->state != ModuleState::STATE_STANDBY) {
|
|
TRACE("AFHDS3 [Model ID Changed] Switch to STATE_STANDBY");
|
|
auto mode = (uint8_t)MODULE_MODE_E::STANDBY;
|
|
trsp.putFrame(COMMAND::MODULE_MODE, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, &mode, 1);
|
|
return;
|
|
} else {
|
|
TRACE("AFHDS3 [Model ID Changed] Set ModelID to %d", newModelID);
|
|
modelID = newModelID;
|
|
trsp.putFrame(COMMAND::MODEL_ID, FRAME_TYPE::REQUEST_SET_EXPECT_DATA,
|
|
&modelID, 1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If standby or exit bind => RUN mode => STATE_SYNC_XXX
|
|
if (this->state == ModuleState::STATE_STANDBY || this->state == ModuleState::STATE_BINDING) {
|
|
cmdCount = 0;
|
|
// requestInfoAndRun(true);
|
|
auto mode = (uint8_t)MODULE_MODE_E::RUN;
|
|
trsp.putFrame(COMMAND::MODULE_MODE, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, &mode, 1);
|
|
return;
|
|
}
|
|
|
|
// Exit bind
|
|
/* if (this->state == STATE_BINDING) {
|
|
TRACE("AFHDS3 [EXIT BIND]");
|
|
// modelcfgGet = true;
|
|
auto mode = (uint8_t)MODULE_MODE_E::RUN;
|
|
trsp.putFrame(COMMAND::MODULE_MODE, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, &mode, 1);
|
|
trsp.enqueue(COMMAND::MODULE_GET_CONFIG, FRAME_TYPE::REQUEST_GET_DATA);
|
|
return;
|
|
} */
|
|
}
|
|
|
|
/* if (modelcfgGet) {
|
|
trsp.enqueue(COMMAND::MODULE_GET_CONFIG, FRAME_TYPE::REQUEST_GET_DATA);
|
|
return;
|
|
}*/
|
|
|
|
if (cmdCount++ >= 150) {
|
|
|
|
cmdCount = 0;
|
|
if (cmdIndex >= sizeof(periodicRequestCommands)) {
|
|
cmdIndex = 0;
|
|
}
|
|
COMMAND cmd = periodicRequestCommands[cmdIndex++];
|
|
|
|
if (cmd == COMMAND::VIRTUAL_FAILSAFE) {
|
|
Config_u* cfg = this->getConfig();
|
|
uint8_t len =_phyMode_channels[cfg->v0.PhyMode];
|
|
if (!hasTelemetry()) {
|
|
uint16_t failSafe[AFHDS3_MAX_CHANNELS + 1] = {
|
|
((AFHDS3_MAX_CHANNELS << 8) | CHANNELS_DATA_MODE::FAIL_SAFE), 0};
|
|
setFailSafe((int16_t*)(&failSafe[1]), len);
|
|
TRACE("AFHDS ONE WAY FAILSAFE");
|
|
trsp.putFrame(COMMAND::CHANNELS_FAILSAFE_DATA,
|
|
FRAME_TYPE::REQUEST_SET_NO_RESP, (uint8_t*)failSafe,
|
|
AFHDS3_MAX_CHANNELS * 2 + 2);
|
|
}
|
|
else if( isConnected() ){
|
|
uint8_t data[AFHDS3_MAX_CHANNELS*2 + 3] = { (uint8_t)(RX_CMD_FAILSAFE_VALUE&0xFF), (uint8_t)((RX_CMD_FAILSAFE_VALUE>>8)&0xFF), (uint8_t)(2*len)};
|
|
int16_t failSafe[18];
|
|
setFailSafe(&failSafe[0], len);
|
|
std::memcpy( &data[3], failSafe, 2*len );
|
|
trsp.putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, data, 2*len+3);
|
|
}
|
|
} else {
|
|
trsp.putFrame(cmd, FRAME_TYPE::REQUEST_GET_DATA);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (isConnected()) {
|
|
// Sync config, with commands
|
|
if (syncSettings()) { return; }
|
|
|
|
// Send channels data
|
|
sendChannelsData();
|
|
} else {
|
|
//default frame - request state
|
|
trsp.putFrame(MODULE_STATE, FRAME_TYPE::REQUEST_GET_DATA);
|
|
}
|
|
}
|
|
|
|
|
|
void ProtoState::init(uint8_t moduleIndex, void* buffer,
|
|
etx_module_state_t* mod_st, uint8_t fAddr)
|
|
{
|
|
module_index = moduleIndex;
|
|
trsp.init(buffer, mod_st, fAddr);
|
|
|
|
//clear local vars because it is member of union
|
|
moduleData = &g_model.moduleData[module_index];
|
|
state = ModuleState::STATE_NOT_READY;
|
|
modelID = 0xff; // Valid range 0-19
|
|
// modelIDSet = false;
|
|
clearFrameData();
|
|
}
|
|
|
|
void ProtoState::clearFrameData()
|
|
{
|
|
TRACE("AFHDS3 clearFrameData");
|
|
trsp.clear();
|
|
|
|
cmdCount = 0;
|
|
cmdIndex = 0;
|
|
}
|
|
|
|
bool containsData(enum FRAME_TYPE frameType)
|
|
{
|
|
return (frameType == FRAME_TYPE::RESPONSE_DATA ||
|
|
frameType == FRAME_TYPE::REQUEST_SET_EXPECT_DATA ||
|
|
frameType == FRAME_TYPE::REQUEST_SET_EXPECT_ACK ||
|
|
frameType == FRAME_TYPE::REQUEST_SET_EXPECT_DATA ||
|
|
frameType == FRAME_TYPE::REQUEST_SET_NO_RESP);
|
|
}
|
|
|
|
void ProtoState::setState(ModuleState state)
|
|
{
|
|
if (state == this->state) {
|
|
return;
|
|
}
|
|
|
|
uint8_t oldState = this->state;
|
|
this->state = state;
|
|
if (oldState == ModuleState::STATE_BINDING) {
|
|
setModuleMode(module_index, ::ModuleSettingsMode::MODULE_MODE_NORMAL);
|
|
}
|
|
if (state == ModuleState::STATE_NOT_READY) {
|
|
trsp.clear();
|
|
}
|
|
else if (state == ModuleState::STATE_SYNC_RUNNING || state == ModuleState::STATE_SYNC_DONE)
|
|
{
|
|
// Get config when switched to STATE_SYNC_XXX
|
|
trsp.enqueue(COMMAND::MODULE_GET_CONFIG, FRAME_TYPE::REQUEST_GET_DATA);
|
|
|
|
// Change connected state and refresh UI
|
|
cfg.others.isConnected = isConnected();
|
|
cfg.others.lastUpdated = get_tmr10ms();
|
|
cfg.others.dirtyFlag = 0U;
|
|
|
|
if (state == ModuleState::STATE_SYNC_DONE)
|
|
{
|
|
// Update power config
|
|
TRACE("Added PWM CMD");
|
|
DIRTY_CMD((&cfg), afhds3::DirtyConfig::DC_RX_CMD_TX_PWR);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
void ProtoState::requestInfoAndRun(bool send)
|
|
{
|
|
// set model ID
|
|
// trsp.enqueue(COMMAND::MODEL_ID, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, true,
|
|
// g_model.header.modelId[module_index]);
|
|
|
|
// RUN
|
|
trsp.enqueue(COMMAND::MODULE_MODE, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, true,
|
|
(uint8_t)MODULE_MODE_E::RUN);
|
|
|
|
if (send) { trsp.processQueue(); }
|
|
}*/
|
|
|
|
void ProtoState::parseData(uint8_t* rxBuffer, uint8_t rxBufferCount)
|
|
{
|
|
AfhdsFrame* responseFrame = reinterpret_cast<AfhdsFrame*>(rxBuffer);
|
|
if (containsData((enum FRAME_TYPE) responseFrame->frameType)) {
|
|
switch (responseFrame->command) {
|
|
case COMMAND::MODULE_READY:
|
|
TRACE("AFHDS3 [MODULE_READY] %02X", responseFrame->value);
|
|
if (responseFrame->value == MODULE_STATUS_READY) {
|
|
setState(ModuleState::STATE_READY);
|
|
// requestInfoAndRun();
|
|
}
|
|
else {
|
|
setState(ModuleState::STATE_NOT_READY);
|
|
}
|
|
break;
|
|
case COMMAND::MODULE_GET_CONFIG: {
|
|
// modelcfgGet = false;
|
|
TRACE("AFHDS3 [MODULE_GET_CONFIG]");
|
|
size_t len = min<size_t>(sizeof(cfg.buffer), rxBufferCount);
|
|
std::memcpy((void*) cfg.buffer, &responseFrame->value, len);
|
|
moduleData->afhds3.emi = cfg.v0.EMIStandard;
|
|
moduleData->afhds3.telemetry = cfg.v0.IsTwoWay;
|
|
moduleData->afhds3.phyMode = cfg.v0.PhyMode;
|
|
cfg.others.ExternalBusType = cfg.v0.ExternalBusType;
|
|
TRACE("PhyMode %d, emi %d", moduleData->afhds3.phyMode, moduleData->afhds3.emi);
|
|
SET_DIRTY();
|
|
cfg.others.lastUpdated = get_tmr10ms();
|
|
} break;
|
|
case COMMAND::MODULE_VERSION:
|
|
std::memcpy((void*) &version, &responseFrame->value, sizeof(version));
|
|
TRACE("AFHDS3 [MODULE_VERSION] Product %d, HW %d, BOOT %d, FW %d",
|
|
version.productNumber, version.hardwareVersion,
|
|
version.bootloaderVersion, version.firmwareVersion);
|
|
break;
|
|
case COMMAND::MODULE_STATE:
|
|
// TRACE("AFHDS3 [MODULE_STATE] %02X", responseFrame->value);
|
|
setState((ModuleState)responseFrame->value);
|
|
if(STATE_SYNC_DONE == (ModuleState)responseFrame->value){
|
|
if(this->cmd_flg & 0x04)
|
|
{
|
|
this->cmd_flg &= ~0x04;
|
|
this->cmd_flg |= 0x02;
|
|
trsp.enqueue(COMMAND::MODULE_VERSION, FRAME_TYPE::REQUEST_GET_DATA);
|
|
// modelcfgGet = true;
|
|
// cfg.others.isConnected = true;
|
|
// cfg.others.lastUpdated = get_tmr10ms();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this->cmd_flg |= 0x04;
|
|
// cfg.others.isConnected = false;
|
|
// cfg.others.lastUpdated = get_tmr10ms();
|
|
}
|
|
break;
|
|
case COMMAND::MODULE_MODE:
|
|
TRACE("AFHDS3 [MODULE_MODE] %02X", responseFrame->value);
|
|
if (responseFrame->value != CMD_RESULT::SUCCESS) {
|
|
setState(ModuleState::STATE_NOT_READY);
|
|
}
|
|
break;
|
|
case COMMAND::MODULE_SET_CONFIG:
|
|
if (responseFrame->value != CMD_RESULT::SUCCESS) {
|
|
setState(ModuleState::STATE_NOT_READY);
|
|
}
|
|
TRACE("AFHDS3 [MODULE_SET_CONFIG], %02X", responseFrame->value);
|
|
break;
|
|
case COMMAND::MODEL_ID:
|
|
TRACE("AFHDS3 [MODEL_ID]");
|
|
if (responseFrame->value == CMD_RESULT::SUCCESS) {
|
|
TRACE("Enqueue get config");
|
|
// trsp.enqueue(COMMAND::MODULE_GET_CONFIG, FRAME_TYPE::REQUEST_GET_DATA);
|
|
// trsp.enqueue(COMMAND::MODULE_GET_CONFIG, FRAME_TYPE::REQUEST_GET_DATA);
|
|
// modelcfgGet = true;
|
|
}
|
|
break;
|
|
case COMMAND::TELEMETRY_DATA:
|
|
{
|
|
uint8_t* telemetry = &responseFrame->value;
|
|
|
|
if (telemetry[0] == 0x22) {
|
|
telemetry++;
|
|
while (telemetry < rxBuffer + rxBufferCount) {
|
|
|
|
uint8_t len = telemetry[0];
|
|
if (len < 4 || telemetry + len > rxBuffer + rxBufferCount)
|
|
{
|
|
break;
|
|
}
|
|
telemetry[0] = 0;
|
|
::processFlySkyAFHDS3Sensor( telemetry, len-3 );
|
|
telemetry += len;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case COMMAND::COMMAND_RESULT: {
|
|
uint8_t *data = &responseFrame->value;
|
|
uint16_t cmd_code = *data++;
|
|
cmd_code |= (*data++)<<8;
|
|
uint8_t result = *data++;
|
|
auto *cfg = this->getConfig();
|
|
switch (cmd_code)
|
|
{
|
|
case RX_CMD_RSSI_CHANNEL_SETUP:
|
|
if(RX_CMDRESULT::RXSUCCESS==result) {
|
|
clearDirtyFlag(DC_RX_CMD_RSSI_CHANNEL_SETUP);
|
|
} break;
|
|
case RX_CMD_OUT_PWM_PPM_MODE:
|
|
if(RX_CMDRESULT::RXSUCCESS==result) {
|
|
clearDirtyFlag(DC_RX_CMD_OUT_PWM_PPM_MODE);
|
|
} break;
|
|
case RX_CMD_FREQUENCY_V0:
|
|
if(RX_CMDRESULT::RXSUCCESS==result) {
|
|
clearDirtyFlag(DC_RX_CMD_FREQUENCY_V0);
|
|
} break;
|
|
case RX_CMD_PORT_TYPE_V1:
|
|
if(RX_CMDRESULT::RXSUCCESS==result) {
|
|
clearDirtyFlag(DC_RX_CMD_PORT_TYPE_V1);
|
|
} break;
|
|
case RX_CMD_FREQUENCY_V1:
|
|
if(RX_CMDRESULT::RXSUCCESS==result) {
|
|
if (checkDirtyFlag(DC_RX_CMD_FREQUENCY_V1)) {
|
|
clearDirtyFlag(DC_RX_CMD_FREQUENCY_V1);
|
|
} else {
|
|
clearDirtyFlag(DC_RX_CMD_FREQUENCY_V1_2);
|
|
}
|
|
} break;
|
|
case RX_CMD_BUS_TYPE_V0:
|
|
if(RX_CMDRESULT::RXSUCCESS==result) {
|
|
this->cmd_flg &= ~0x28;
|
|
clearDirtyFlag(DC_RX_CMD_BUS_TYPE_V0);
|
|
} break;
|
|
case RX_CMD_IBUS_DIRECTION:
|
|
if(RX_CMDRESULT::RXSUCCESS==*data++) {
|
|
clearDirtyFlag(DC_RX_CMD_IBUS_DIRECTION);
|
|
this->cmd_flg &= ~0x10;
|
|
if( 0==cfg->version && 2==cfg->v0.ExternalBusType)
|
|
this->cmd_flg |= 0x20;
|
|
}break;
|
|
case RX_CMD_GET_VERSION :
|
|
if(RX_CMDRESULT::RXSUCCESS==result) {
|
|
if(14==*data++)
|
|
{
|
|
this->cmd_flg &= ~0x02;
|
|
std::memcpy((void*) &rx_version, data, sizeof(rx_version));
|
|
this->cmd_flg |= 0x08;
|
|
}
|
|
}break;
|
|
default:
|
|
break;
|
|
}
|
|
TRACE("AFHDS3 [CMD Result] Cmd: %X, Result: %d, DirtyFlag: %X", cmd_code, result, cfg->others.dirtyFlag);
|
|
} break;
|
|
}
|
|
}
|
|
|
|
if (responseFrame->frameType == FRAME_TYPE::REQUEST_GET_DATA ||
|
|
responseFrame->frameType == FRAME_TYPE::REQUEST_SET_EXPECT_DATA) {
|
|
TRACE("Command %02X NOT IMPLEMENTED!", responseFrame->command);
|
|
}
|
|
}
|
|
|
|
inline bool isSbus(uint8_t mode)
|
|
{
|
|
return (mode & 1);
|
|
}
|
|
|
|
inline bool isPWM(uint8_t mode)
|
|
{
|
|
return !(mode & 2);
|
|
}
|
|
|
|
bool ProtoState::syncSettings()
|
|
{
|
|
// Handles old receivers bug
|
|
if(this->cmd_flg&0x02)
|
|
{
|
|
uint8_t data[] = { (uint8_t)(RX_CMD_GET_VERSION&0xFF), (uint8_t)((RX_CMD_GET_VERSION>>8)&0xFF), 0x00 };
|
|
trsp.putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, data, sizeof(data));
|
|
return true;
|
|
}
|
|
auto *cfg = this->getConfig();
|
|
if((this->cmd_flg&0x38) && (!cfg->version) )
|
|
{
|
|
uint8_t bustype = cfg->others.ExternalBusType<=1?0:2;
|
|
uint8_t busdir = cfg->others.ExternalBusType;
|
|
if( 1==receiver_type(rx_version.ProductNumber) )
|
|
{
|
|
if(this->cmd_flg&0x08)
|
|
{
|
|
uint8_t data[] = { (uint8_t)(RX_CMD_BUS_TYPE_V0&0xFF), (uint8_t)((RX_CMD_BUS_TYPE_V0>>8)&0xFF), 0x1, bustype };
|
|
trsp.putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, data, sizeof(data));
|
|
if(busdir<2)
|
|
this->cmd_flg |= 0x10;
|
|
return true;
|
|
}
|
|
if((this->cmd_flg&0x10) && busdir<2)
|
|
{
|
|
cfg->others.ExternalBusType = IBUS1_OUT;
|
|
busdir = IBUS1_OUT;
|
|
uint8_t data1[] = { (uint8_t)(RX_CMD_IBUS_DIRECTION&0xFF), (uint8_t)((RX_CMD_IBUS_DIRECTION>>8)&0xFF), 0x1, busdir };
|
|
trsp.putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, data1, sizeof(data1));
|
|
return true;
|
|
}
|
|
}
|
|
else if( 2==receiver_type(rx_version.ProductNumber) )
|
|
{
|
|
if(this->cmd_flg&0x08)//IBUS
|
|
{
|
|
uint8_t data[] = { (uint8_t)(RX_CMD_BUS_TYPE_V0&0xFF), (uint8_t)((RX_CMD_BUS_TYPE_V0>>8)&0xFF), 0x1, 0 };
|
|
trsp.putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, data, sizeof(data));
|
|
this->cmd_flg |= 0x10;
|
|
return true;
|
|
}
|
|
else if((this->cmd_flg&0x10))//IBUS-IN IBUS-OUT
|
|
{
|
|
if(busdir>=2) busdir = 0; //IBUSOUT
|
|
uint8_t data1[] = { (uint8_t)(RX_CMD_IBUS_DIRECTION&0xFF), (uint8_t)((RX_CMD_IBUS_DIRECTION>>8)&0xFF), 0x1, busdir };
|
|
trsp.putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, data1, sizeof(data1));
|
|
return true;
|
|
}
|
|
else if((this->cmd_flg&0x20))
|
|
{
|
|
uint8_t data[] = { (uint8_t)(RX_CMD_BUS_TYPE_V0&0xFF), (uint8_t)((RX_CMD_BUS_TYPE_V0>>8)&0xFF), 0x1, 2 };
|
|
trsp.putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, data, sizeof(data));
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sync settings when dirty flag is set
|
|
if (checkDirtyFlag(DC_RX_CMD_TX_PWR))
|
|
{
|
|
TRACE("AFHDS3 [RX_CMD_TX_PWR] %d", AFHDS3_POWER[moduleData->afhds3.rfPower] / 4);
|
|
uint8_t data[] = { (uint8_t)(RX_CMD_TX_PWR&0xFF), (uint8_t)((RX_CMD_TX_PWR>>8)&0xFF), 2,
|
|
(uint8_t)(AFHDS3_POWER[moduleData->afhds3.rfPower]&0xFF), (uint8_t)((AFHDS3_POWER[moduleData->afhds3.rfPower]>>8)&0xFF)};
|
|
trsp.putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, data, sizeof(data));
|
|
clearDirtyFlag(DC_RX_CMD_TX_PWR);
|
|
return true;
|
|
}
|
|
if (checkDirtyFlag(DC_RX_CMD_RSSI_CHANNEL_SETUP))
|
|
{
|
|
TRACE("AFHDS3 [RX_CMD_RSSI_CHANNEL_SETUP]");
|
|
uint8_t data[] = { (uint8_t)(RX_CMD_RSSI_CHANNEL_SETUP&0xFF), (uint8_t)((RX_CMD_RSSI_CHANNEL_SETUP>>8)&0xFF), 1, cfg->v1.SignalStrengthRCChannelNb };
|
|
trsp.putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, data, sizeof(data));
|
|
return true;
|
|
}
|
|
if (checkDirtyFlag(DC_RX_CMD_OUT_PWM_PPM_MODE))
|
|
{
|
|
TRACE("AFHDS3 [RX_CMD_OUT_PWM_PPM_MODE]");
|
|
uint8_t data[] = { (uint8_t)(RX_CMD_OUT_PWM_PPM_MODE&0xFF), (uint8_t)((RX_CMD_OUT_PWM_PPM_MODE>>8)&0xFF), 1, cfg->v0.AnalogOutput };
|
|
trsp.putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, data, sizeof(data));
|
|
return true;
|
|
}
|
|
if (checkDirtyFlag(DC_RX_CMD_FREQUENCY_V0))
|
|
{
|
|
TRACE("AFHDS3 [RX_CMD_FREQUENCY_V0]");
|
|
uint16_t Frequency = ((cfg->v0.PWMFrequency.Synchronized<<15)| cfg->v0.PWMFrequency.Frequency);
|
|
uint8_t data[] = { (uint8_t)(RX_CMD_FREQUENCY_V0&0xFF), (uint8_t)((RX_CMD_FREQUENCY_V0>>8)&0xFF), 2,
|
|
(uint8_t)(Frequency&0xFF), (uint8_t)((Frequency>>8)&0xFF) };
|
|
trsp.putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, data, sizeof(data));
|
|
return true;
|
|
}
|
|
if (checkDirtyFlag(DC_RX_CMD_PORT_TYPE_V1))
|
|
{
|
|
TRACE("AFHDS3 [RX_CMD_PORT_TYPE_V1]");
|
|
uint8_t data[] = { (uint8_t)(RX_CMD_PORT_TYPE_V1&0xFF), (uint8_t)((RX_CMD_PORT_TYPE_V1>>8)&0xFF), 4, 0, 0, 0, 0 };
|
|
std::memcpy(&data[3], &cfg->v1.NewPortTypes, SES_NPT_NB_MAX_PORTS);
|
|
trsp.putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, data, sizeof(data));
|
|
return true;
|
|
}
|
|
if (checkDirtyFlag(DC_RX_CMD_FREQUENCY_V1))
|
|
{
|
|
TRACE("AFHDS3 [RX_CMD_FREQUENCY_V1]");
|
|
uint8_t data[32 + 3 + 3] = { (uint8_t)(RX_CMD_FREQUENCY_V1&0xFF), (uint8_t)((RX_CMD_FREQUENCY_V1>>8)&0xFF), 32+3};
|
|
data[3] = 0;
|
|
std::memcpy(&data[4], &cfg->v1.PWMFrequenciesV1.PWMFrequencies[0], 32);
|
|
data[36] = cfg->v1.PWMFrequenciesV1.Synchronized & 0xff;
|
|
data[37] = (cfg->v1.PWMFrequenciesV1.Synchronized>>8) & 0xff;
|
|
trsp.putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, data, sizeof(data));
|
|
DIRTY_CMD(cfg, DC_RX_CMD_FREQUENCY_V1_2);
|
|
return true;
|
|
}
|
|
if (checkDirtyFlag(DC_RX_CMD_FREQUENCY_V1_2))
|
|
{
|
|
TRACE("AFHDS3 [RX_CMD_FREQUENCY_V1_2]");
|
|
uint8_t data[32 + 3 + 3] = { (uint8_t)(RX_CMD_FREQUENCY_V1_2&0xFF), (uint8_t)((RX_CMD_FREQUENCY_V1_2>>8)&0xFF), 32+3};
|
|
data[3] = 1;
|
|
std::memcpy(&data[4], &cfg->v1.PWMFrequenciesV1.PWMFrequencies[16], 32);
|
|
data[36] = (cfg->v1.PWMFrequenciesV1.Synchronized>>16) & 0xff;
|
|
data[37] = (cfg->v1.PWMFrequenciesV1.Synchronized>>24) & 0xff;
|
|
trsp.putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, data, sizeof(data));
|
|
return true;
|
|
}
|
|
if (checkDirtyFlag(DC_RX_CMD_BUS_TYPE_V0))
|
|
{
|
|
TRACE("AFHDS3 [RX_CMD_BUS_TYPE_V0]");
|
|
bool onlySupportIBUSOut = (1==receiver_type(rx_version.ProductNumber));
|
|
|
|
if (onlySupportIBUSOut && cfg->others.ExternalBusType == EB_BT_IBUS1_IN)
|
|
{
|
|
cfg->others.ExternalBusType = EB_BT_IBUS1_OUT;
|
|
}
|
|
uint8_t data[] = { (uint8_t)(RX_CMD_BUS_TYPE_V0&0xFF), (uint8_t)((RX_CMD_BUS_TYPE_V0>>8)&0xFF), 1,
|
|
cfg->others.ExternalBusType == EB_BT_SBUS1 ? EB_BT_SBUS1 : EB_BT_IBUS1};
|
|
trsp.putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, data, sizeof(data));
|
|
|
|
if (!onlySupportIBUSOut)
|
|
{
|
|
DIRTY_CMD(cfg, DC_RX_CMD_IBUS_DIRECTION);
|
|
}
|
|
return true;
|
|
|
|
/*
|
|
if(1>=cfg->others.ExternalBusType) //IBUS1
|
|
{
|
|
uint8_t data[] = { (uint8_t)(RX_CMD_BUS_TYPE_V0&0xFF), (uint8_t)((RX_CMD_BUS_TYPE_V0>>8)&0xFF), 1, 0 };
|
|
trsp.putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, data, sizeof(data));
|
|
if( 1==receiver_type(rx_version.ProductNumber))
|
|
{
|
|
cfg->others.ExternalBusType = 0;//These RXs only support iBUS-OUT
|
|
}
|
|
cfg->others.iBusType = cfg->others.ExternalBusType;
|
|
DIRTY_CMD(cfg, DC_RX_CMD_IBUS_DIRECTION);
|
|
}
|
|
else if(2==cfg->others.ExternalBusType)//SBUS
|
|
{
|
|
uint8_t data[] = { (uint8_t)(RX_CMD_BUS_TYPE_V0&0xFF), (uint8_t)((RX_CMD_BUS_TYPE_V0>>8)&0xFF), 1, cfg->others.ExternalBusType };
|
|
trsp.putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, data, sizeof(data));
|
|
}
|
|
return true;
|
|
*/
|
|
}
|
|
if (checkDirtyFlag(DC_RX_CMD_IBUS_DIRECTION))
|
|
{
|
|
TRACE("AFHDS3 [RX_CMD_IBUS_DIRECTION]");
|
|
uint8_t data[] = { (uint8_t)(RX_CMD_IBUS_DIRECTION&0xFF), (uint8_t)((RX_CMD_IBUS_DIRECTION>>8)&0xFF), 1,
|
|
cfg->others.ExternalBusType == EB_BT_IBUS1_OUT ? EB_BT_IBUS1_OUT : EB_BT_IBUS1_IN};
|
|
trsp.putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, data, sizeof(data));
|
|
return true;
|
|
}
|
|
|
|
// No need to sync
|
|
return false;
|
|
}
|
|
|
|
void ProtoState::sendChannelsData()
|
|
{
|
|
uint8_t channels_start = moduleData->channelsStart;
|
|
uint8_t channelsCount = 8 + moduleData->channelsCount;
|
|
uint8_t channels_last = channels_start + channelsCount;
|
|
|
|
int16_t buffer[AFHDS3_MAX_CHANNELS + 1] = {0};
|
|
|
|
uint8_t* header = (uint8_t*)buffer;
|
|
header[0] = CHANNELS_DATA_MODE::CHANNELS;
|
|
|
|
uint8_t channels = _phyMode_channels[cfg.v0.PhyMode];
|
|
header[1] = channels;
|
|
|
|
for (uint8_t channel = channels_start, index = 1; channel < channels_last;
|
|
channel++, index++) {
|
|
int16_t channelValue = convert(::getChannelValue(channel));
|
|
buffer[index] = channelValue;
|
|
}
|
|
|
|
trsp.putFrame(COMMAND::CHANNELS_FAILSAFE_DATA, FRAME_TYPE::REQUEST_SET_NO_RESP,
|
|
(uint8_t*)buffer, (channels + 1) * 2);
|
|
}
|
|
|
|
void ProtoState::stop()
|
|
{
|
|
TRACE("AFHDS3 STOP");
|
|
auto mode = (uint8_t)MODULE_MODE_E::STANDBY;
|
|
trsp.putFrame(COMMAND::MODULE_MODE, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, &mode, 1);
|
|
}
|
|
|
|
void ProtoState::resetConfig(uint8_t version)
|
|
{
|
|
memclear(&cfg, sizeof(cfg));
|
|
cfg.version = version;
|
|
|
|
if (cfg.version == 1) {
|
|
cfg.v1.SignalStrengthRCChannelNb = 0xFF;
|
|
cfg.v1.FailsafeTimeout = 500;
|
|
for (int i = 0; i < SES_NB_MAX_CHANNELS; i++)
|
|
cfg.v1.PWMFrequenciesV1.PWMFrequencies[i] = 50;
|
|
} else {
|
|
cfg.v0.SignalStrengthRCChannelNb = 0xFF;
|
|
cfg.v0.FailsafeTimeout = 500;
|
|
cfg.v0.PWMFrequency.Frequency = 50;
|
|
}
|
|
}
|
|
|
|
void ProtoState::applyConfigFromModel()
|
|
{
|
|
uint8_t version = 0;
|
|
#if defined(SIMU)
|
|
// TODO: work out why this is not initialised in some cases
|
|
if (moduleData == nullptr) return;
|
|
#endif
|
|
if (moduleData->afhds3.phyMode >= ROUTINE_FLCR1_18CH) {
|
|
version = 1;
|
|
}
|
|
|
|
if (version != cfg.version) {
|
|
resetConfig(version);
|
|
}
|
|
|
|
if (cfg.version == 1) {
|
|
cfg.v1.EMIStandard = moduleData->afhds3.emi;
|
|
cfg.v1.IsTwoWay = moduleData->afhds3.telemetry;
|
|
cfg.v1.PhyMode = moduleData->afhds3.phyMode;
|
|
|
|
// Failsafe
|
|
setFailSafe(cfg.v1.FailSafe);
|
|
if (moduleData->failsafeMode != FAILSAFE_NOPULSES) {
|
|
cfg.v1.FailsafeOutputMode = true;
|
|
} else {
|
|
cfg.v1.FailsafeOutputMode = false;
|
|
}
|
|
} else {
|
|
cfg.v0.EMIStandard = moduleData->afhds3.emi;
|
|
cfg.v0.IsTwoWay = moduleData->afhds3.telemetry;
|
|
cfg.v0.PhyMode = moduleData->afhds3.phyMode;
|
|
cfg.v0.ExternalBusType = cfg.others.ExternalBusType==EB_BT_SBUS1 ? EB_BT_SBUS1 : EB_BT_IBUS1;
|
|
// Failsafe
|
|
setFailSafe(cfg.v0.FailSafe);
|
|
if (moduleData->failsafeMode != FAILSAFE_NOPULSES) {
|
|
cfg.v0.FailsafeOutputMode = true;
|
|
} else {
|
|
cfg.v0.FailsafeOutputMode = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint8_t receiver_type( unsigned long productnumber )
|
|
{
|
|
if(PN_FTR10 == productnumber || PN_FGR4 == productnumber || PN_FTR16S == productnumber)
|
|
{
|
|
return 1; // This type of RX can be set to IBUS-OUT/SBUS
|
|
}
|
|
else if(PN_FTR4 == productnumber)
|
|
{
|
|
return 2; // This type of RX can be set to IBUS-IN/IBUS-OUT/SBUS
|
|
}
|
|
else if( (PN_FTR8B&0xFFFF) <= productnumber)
|
|
{
|
|
return 3; //V1 RX
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
inline int16_t ProtoState::convert(int channelValue)
|
|
{
|
|
//pulseValue = limit<uint16_t>(0, 988 + ((channelValue + 1024) / 2), 0xfff);
|
|
//988 - 750 = 238
|
|
//238 * 20 = 4760
|
|
//2250 - 2012 = 238
|
|
//238 * 20 = 4760
|
|
// 988 ---- 2012
|
|
//-10240 ---- 10240
|
|
//-1024 ---- 1024
|
|
return ::limit<int16_t>(AFHDS3_FAILSAFE_MIN, channelValue * 10, AFHDS3_FAILSAFE_MAX);
|
|
}
|
|
|
|
uint8_t ProtoState::setFailSafe(int16_t* target, uint8_t rfchannelsCount )
|
|
{
|
|
int16_t pulseValue = 0;
|
|
uint8_t channels_start = moduleData->channelsStart;
|
|
uint8_t channelsCount = 8 + moduleData->channelsCount;
|
|
uint8_t channels_last = channels_start + channelsCount;
|
|
std::memset(target, 0, 2*rfchannelsCount );
|
|
for (uint8_t channel = channels_start, i=0; i<rfchannelsCount && channel < channels_last; channel++, i++) {
|
|
if (moduleData->failsafeMode == FAILSAFE_CUSTOM) {
|
|
if(FAILSAFE_CHANNEL_HOLD==g_model.failsafeChannels[channel]){
|
|
pulseValue = FAILSAFE_HOLD_VALUE;
|
|
}else if(FAILSAFE_CHANNEL_NOPULSE==g_model.failsafeChannels[channel]){
|
|
pulseValue = FAILSAFE_NOPULSES_VALUE;
|
|
}
|
|
else{
|
|
pulseValue = convert(g_model.failsafeChannels[channel]);
|
|
}
|
|
}
|
|
else if (moduleData->failsafeMode == FAILSAFE_HOLD) {
|
|
pulseValue = FAILSAFE_HOLD_VALUE;
|
|
}
|
|
else if (moduleData->failsafeMode == FAILSAFE_NOPULSES) {
|
|
pulseValue = FAILSAFE_NOPULSES_VALUE;
|
|
}
|
|
else {
|
|
pulseValue = FAILSAFE_NOPULSES_VALUE;
|
|
}
|
|
target[i] = pulseValue;
|
|
}
|
|
//return max channels because channel count can not be change after bind
|
|
return (uint8_t) (AFHDS3_MAX_CHANNELS);
|
|
}
|
|
|
|
Config_u* getConfig(uint8_t module)
|
|
{
|
|
auto p_state = &protoState[module];
|
|
return p_state->getConfig();
|
|
}
|
|
|
|
void applyModelConfig(uint8_t module)
|
|
{
|
|
auto p_state = &protoState[module];
|
|
p_state->applyConfigFromModel();
|
|
}
|
|
|
|
static const etx_serial_init _uartParams = {
|
|
.baudrate = 0, //AFHDS3_UART_BAUDRATE,
|
|
.encoding = ETX_Encoding_8N1,
|
|
.direction = ETX_Dir_TX_RX,
|
|
.polarity = ETX_Pol_Normal,
|
|
};
|
|
|
|
static void* initModule(uint8_t module)
|
|
{
|
|
etx_module_state_t* mod_st = nullptr;
|
|
etx_serial_init params(_uartParams);
|
|
uint16_t period = AFHDS3_UART_COMMAND_TIMEOUT * 1000;
|
|
uint8_t fAddr = (module == INTERNAL_MODULE ? DeviceAddress::IRM301
|
|
: DeviceAddress::FRM303)
|
|
<< 4 |
|
|
DeviceAddress::TRANSMITTER;
|
|
|
|
params.baudrate = AFHDS3_UART_BAUDRATE;
|
|
params.polarity =
|
|
module == INTERNAL_MODULE ? ETX_Pol_Normal : ETX_Pol_Inverted;
|
|
mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, ¶ms);
|
|
|
|
#if defined(CONFIGURABLE_MODULE_PORT)
|
|
if (!mod_st && module == EXTERNAL_MODULE) {
|
|
// Try Connect using aux serial mod
|
|
params.polarity = ETX_Pol_Normal;
|
|
mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, ¶ms);
|
|
}
|
|
#endif
|
|
|
|
if (!mod_st && module == EXTERNAL_MODULE) {
|
|
// soft-serial fallback
|
|
params.baudrate = AFHDS3_SOFTSERIAL_BAUDRATE;
|
|
params.direction = ETX_Dir_TX;
|
|
period = AFHDS3_SOFTSERIAL_COMMAND_TIMEOUT * 1000 /* us */;
|
|
mod_st = modulePortInitSerial(module, ETX_MOD_PORT_SOFT_INV, ¶ms);
|
|
// TODO: telemetry RX ???
|
|
}
|
|
|
|
if (!mod_st) return nullptr;
|
|
|
|
auto p_state = &protoState[module];
|
|
p_state->init(module, pulsesGetModuleBuffer(module), mod_st, fAddr);
|
|
mod_st->user_data = (void*)p_state;
|
|
|
|
mixerSchedulerSetPeriod(module, period);
|
|
|
|
return mod_st;
|
|
}
|
|
|
|
static void deinitModule(void* ctx)
|
|
{
|
|
auto mod_st = (etx_module_state_t*)ctx;
|
|
modulePortDeInit(mod_st);
|
|
}
|
|
|
|
static void sendPulses(void* ctx, uint8_t* buffer, int16_t* channels,
|
|
uint8_t nChannels)
|
|
{
|
|
(void)buffer;
|
|
(void)channels;
|
|
(void)nChannels;
|
|
|
|
auto mod_st = (etx_module_state_t*)ctx;
|
|
auto p_state = (ProtoState*)mod_st->user_data;
|
|
p_state->setupFrame();
|
|
p_state->sendFrame();
|
|
}
|
|
|
|
etx_proto_driver_t ProtoDriver = {
|
|
.protocol = PROTOCOL_CHANNELS_AFHDS3,
|
|
.init = initModule,
|
|
.deinit = deinitModule,
|
|
.sendPulses = sendPulses,
|
|
.processData = processTelemetryData,
|
|
};
|
|
|
|
} // namespace afhds3
|