FQ777/XBM37: new subprotocol

This commit is contained in:
pascallanger
2026-07-16 18:25:58 +02:00
parent 095acb0975
commit 865f6c1597
8 changed files with 793 additions and 43 deletions

View File

@@ -31,6 +31,19 @@ enum {
FQ777_FLAG_FLIP = 0x80,
};
enum {
XBM37_B5_OK = 0x80,
XBM37_B6_RATE_LOW = 0x00,
XBM37_B6_RATE_MID = 0x01,
XBM37_B6_RATE_HIGH = 0x02,
XBM37_B6_LED_OFF = 0x04,
XBM37_B6_RTH = 0x08,
XBM37_B6_HEADLESS = 0x10,
XBM37_B6_VIDEO = 0x20,
XBM37_B6_PICTURE = 0x40,
XBM37_B6_FLIP = 0x80,
};
const uint8_t ssv_xor[] = {0x80,0x44,0x64,0x75,0x6C,0x71,0x2A,0x36,0x7C,0xF1,0x6E,0x52,0x9,0x9D,0x1F,0x78,0x3F,0xE1,0xEE,0x16,0x6D,0xE8,0x73,0x9,0x15,0xD7,0x92,0xE7,0x3,0xBA};
uint8_t FQ777_bind_addr [] = {0xe7,0xe7,0xe7,0xe7,0x67};
@@ -87,12 +100,21 @@ static void __attribute__((unused)) FQ777_send_packet()
uint8_t packet_ori[8];
if (IS_BIND_IN_PROGRESS)
{
// 4,5,6 = address fields
// last field is checksum of address fields
// Byte 0 is always 0x20; bytes 1-3 are sub-protocol specific
packet_ori[0] = 0x20;
packet_ori[1] = 0x15;
packet_ori[2] = 0x05;
packet_ori[3] = 0x06;
if (sub_protocol == XBM37)
{
packet_ori[1] = 0x14;
packet_ori[2] = 0x07;
packet_ori[3] = 0x03;
}
else // FQ777
{
packet_ori[1] = 0x15;
packet_ori[2] = 0x05;
packet_ori[3] = 0x06;
}
// Bytes 4-6: address fields, byte 7: checksum of address fields (shared by both protocols)
packet_ori[4] = rx_tx_addr[0];
packet_ori[5] = rx_tx_addr[1];
packet_ori[6] = rx_tx_addr[2];
@@ -100,36 +122,64 @@ static void __attribute__((unused)) FQ777_send_packet()
}
else
{
// throt, yaw, pitch, roll, trims, flags/left button,00,right button
//0-3 0x00-0x64
//4 roll/pitch/yaw trims. cycles through one trim at a time - 0-40 trim1, 40-80 trim2, 80-C0 trim3 (center: A0 20 60)
//5 flags for throttle button, two buttons above throttle - def: 0x40
//6 00 ??
//7 checksum - add values in other fields
if (sub_protocol == XBM37)
{
packet_ori[0] = convert_channel_16b_limit(THROTTLE,0xE1,0); // reverse channel
packet_ori[1] = convert_channel_16b_limit(RUDDER,0,0xE1);
packet_ori[2] = convert_channel_16b_limit(AILERON,0xE1,0); // reverse channel
packet_ori[3] = convert_channel_16b_limit(ELEVATOR,0xE1,0); // reverse channel
packet_ori[4] = (convert_channel_8b(CH13) << 2) + 1; // ele trim (01..20..40) front ^ plus 0x01, back - minus 0x01 per click
packet_ori[5] = 0x3F - (convert_channel_8b(CH14) << 2) // ail trim (40..20..01) left <- plus 0x01, right -> minus 0x01 per click
| GET_FLAG(CH12_SW, XBM37_B5_OK); // bit7 = OK button ...unknown use for this model?
// Trims are usually done through the radio configuration but leaving the code here just in case...
uint8_t trim_mod = packet_count % 144;
uint8_t trim_val = 0;
if (36 <= trim_mod && trim_mod < 72) // yaw
trim_val = 0x20; // don't modify yaw trim
else
if (108 < trim_mod && trim_mod) // pitch
trim_val = 0xA0;
else // roll
trim_val = 0x60;
uint8_t rate_bits;
if (CH5_SW)
rate_bits = XBM37_B6_RATE_HIGH; // high rate
else if (Channel_data[CH5] < CHANNEL_MIN_COMMAND)
rate_bits = XBM37_B6_RATE_LOW; // low rate
else
rate_bits = XBM37_B6_RATE_MID; // medium rate
packet_ori[0] = convert_channel_16b_limit(THROTTLE,0,0x64);
packet_ori[1] = convert_channel_16b_limit(RUDDER,0,0x64);
packet_ori[2] = convert_channel_16b_limit(ELEVATOR,0,0x64);
packet_ori[3] = convert_channel_16b_limit(AILERON,0,0x64);
packet_ori[4] = trim_val; // calculated above
packet_ori[5] = GET_FLAG(CH5_SW, FQ777_FLAG_FLIP)
| GET_FLAG(CH7_SW, FQ777_FLAG_HEADLESS)
| GET_FLAG(!CH6_SW, FQ777_FLAG_RETURN)
| GET_FLAG(CH8_SW,FQ777_FLAG_EXPERT);
packet_ori[6] = 0x00;
// calculate checksum
packet_ori[6] = rate_bits // bits[1:0] = rate
| GET_FLAG(CH8_SW, XBM37_B6_LED_OFF) // bit2 = LED off
| GET_FLAG(CH11_SW, XBM37_B6_RTH) // bit3 = RTH
| GET_FLAG(CH7_SW, XBM37_B6_HEADLESS) // bit4 = headless
| GET_FLAG(CH10_SW, XBM37_B6_VIDEO) // bit5 = video
| GET_FLAG(CH9_SW, XBM37_B6_PICTURE) // bit6 = picture
| GET_FLAG(CH6_SW, XBM37_B6_FLIP); // bit7 = flip
}
else // FQ777
{
// throt, yaw, pitch, roll, trims, flags/left button,00,right button
//0-3 0x00-0x64
//4 roll/pitch/yaw trims. cycles through one trim at a time - 0-40 trim1, 40-80 trim2, 80-C0 trim3 (center: A0 20 60)
//5 flags for throttle button, two buttons above throttle - def: 0x40
//6 00 ??
//7 checksum - add values in other fields
// Trims are usually done through the radio configuration but leaving the code here just in case...
uint8_t trim_mod = packet_count % 144;
uint8_t trim_val = 0;
if (36 <= trim_mod && trim_mod < 72) // yaw
trim_val = 0x20; // don't modify yaw trim
else
if (108 < trim_mod && trim_mod) // pitch
trim_val = 0xA0;
else // roll
trim_val = 0x60;
packet_ori[0] = convert_channel_16b_limit(THROTTLE,0,0x64);
packet_ori[1] = convert_channel_16b_limit(RUDDER,0,0x64);
packet_ori[2] = convert_channel_16b_limit(ELEVATOR,0,0x64);
packet_ori[3] = convert_channel_16b_limit(AILERON,0,0x64);
packet_ori[4] = trim_val; // calculated above
packet_ori[5] = GET_FLAG(CH5_SW, FQ777_FLAG_FLIP)
| GET_FLAG(CH7_SW, FQ777_FLAG_HEADLESS)
| GET_FLAG(!CH6_SW, FQ777_FLAG_RETURN)
| GET_FLAG(CH8_SW,FQ777_FLAG_EXPERT);
packet_ori[6] = 0x00;
}
// Data packet checksum: sum of bytes 0-6 (not applied to bind packets)
uint8_t checksum = 0;
for (int i = 0; i < 7; ++i)
checksum += packet_ori[i];
@@ -181,12 +231,17 @@ void FQ777_init(void)
BIND_IN_PROGRESS; // autobind protocol
bind_counter = FQ777_BIND_COUNT;
packet_count=0;
hopping_frequency[0] = 0x4D;
hopping_frequency[1] = 0x43;
hopping_frequency[2] = 0x27;
// Sub-protocol hop channel table: first 3 channels differ; channel[3]=0x07 is shared
static const uint8_t hop_ch[2][3] = {
{0x4D, 0x43, 0x27}, // FQ777
{0x49, 0x34, 0x26}, // XBM-37
};
hopping_frequency[0] = hop_ch[sub_protocol][0];
hopping_frequency[1] = hop_ch[sub_protocol][1];
hopping_frequency[2] = hop_ch[sub_protocol][2];
hopping_frequency[3] = 0x07;
hopping_frequency_no=0;
rx_tx_addr[2] = 0x00;
rx_tx_addr[2] = (sub_protocol == XBM37) ? (RX_num & 0x3F) : 0x00; // XBM-37 uses receiver number (0-63) for model match capability
rx_tx_addr[3] = 0xe7;
rx_tx_addr[4] = 0x67;
FQ777_RF_init();