mirror of
https://github.com/pascallanger/DIY-Multiprotocol-TX-Module.git
synced 2026-08-01 10:39:01 +00:00
Compare commits
1 Commits
master
...
pascallang
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec7b4cd189 |
@@ -1,305 +0,0 @@
|
||||
local toolName = "TNS|DumboRC P series|TNE"
|
||||
|
||||
-- EdgeTX/Multi bridge:
|
||||
-- Multi_Buffer[4] is RF payload length, [5..12] is RF payload.
|
||||
-- Multi rewrites the 00 00 token and checksum from its hop state.
|
||||
-- Special1 payload: 18 00 00 arg_l arg_h 00. Args: 1 poll, 2 Set GY EPA, 3 Set FS.
|
||||
-- Special2 payload: 28 00 00 gyro0 gyro1 gyro2 gyro3 00.
|
||||
-- Gyro word: bit0 enable, bit1 phase, bits2..5 SEN channel selector
|
||||
-- (0=NULL, 1..7=CH2..CH8), bits6..13 SEN, bits14..21 PCA,
|
||||
-- bits22..29 AGS, bits30..31 servo frequency (67HZ,250HZ,50HZ,300HZ).
|
||||
|
||||
local PROTO_RLINK = 74
|
||||
local SUB_DUMBORC_P = 4
|
||||
|
||||
local TX_READY = 4
|
||||
local TX_PAYLOAD = 5
|
||||
local TX_MAX_PAYLOAD = 8
|
||||
local RX_READY = 14
|
||||
local RX_PAYLOAD = 15
|
||||
local RX_MAX_PAYLOAD = 32
|
||||
local colorLcd = type(lcd.RGB) == "function"
|
||||
|
||||
local servoHz = { "67HZ", "250HZ", "50HZ", "300HZ" }
|
||||
|
||||
local gyro = { enable = 0, phase = 0, senCh = 0, sen = 0, pca = 0, ags = 0, flags = 0 }
|
||||
local lines = {}
|
||||
local sel = 1
|
||||
local top = 1
|
||||
local edit = false
|
||||
local blink = 0
|
||||
local moduleOk = false
|
||||
local haveValues = false
|
||||
|
||||
local function limit(v, mn, mx)
|
||||
if v < mn then return mn end
|
||||
if v > mx then return mx end
|
||||
return v
|
||||
end
|
||||
|
||||
local function byte(v)
|
||||
return limit(math.floor(tonumber(v) or 0), 0, 255)
|
||||
end
|
||||
|
||||
local function gyroRaw(v)
|
||||
return limit(math.floor(tonumber(v) or 0), 0, 200)
|
||||
end
|
||||
|
||||
local function gyroText(v)
|
||||
local raw = gyroRaw(v)
|
||||
return tostring(math.floor(raw / 2)) .. (raw % 2 == 0 and ".0" or ".5")
|
||||
end
|
||||
|
||||
local function screenLines()
|
||||
if colorLcd then return limit(math.floor(((LCD_H or 272) - 46) / 20), 8, 12) end
|
||||
return 6
|
||||
end
|
||||
|
||||
local function drawTitle(title)
|
||||
if colorLcd and lcd.drawFilledRectangle then
|
||||
lcd.drawFilledRectangle(0, 0, LCD_W, 30, COLOR_THEME_SECONDARY1)
|
||||
lcd.drawText(3, 5, title, COLOR_THEME_PRIMARY2)
|
||||
elseif lcd.drawScreenTitle then
|
||||
lcd.drawScreenTitle(title, 0, 0)
|
||||
else
|
||||
lcd.drawText(0, 0, title, INVERS)
|
||||
end
|
||||
end
|
||||
|
||||
local function findModule()
|
||||
for i = 0, 1 do
|
||||
local m = model.getModule(i)
|
||||
if m and m["Type"] == 6 and m["protocol"] == PROTO_RLINK and m["subProtocol"] == SUB_DUMBORC_P then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function initBuffer()
|
||||
multiBuffer(0, string.byte("R"))
|
||||
if multiBuffer(0) ~= string.byte("R") then
|
||||
error("Not enough memory!")
|
||||
return
|
||||
end
|
||||
multiBuffer(1, string.byte("L"))
|
||||
multiBuffer(2, string.byte("n"))
|
||||
multiBuffer(3, string.byte("k"))
|
||||
multiBuffer(TX_READY, 0)
|
||||
multiBuffer(RX_READY, 0)
|
||||
end
|
||||
|
||||
local function release()
|
||||
for i = 0, RX_PAYLOAD + RX_MAX_PAYLOAD - 1 do
|
||||
multiBuffer(i, 0)
|
||||
end
|
||||
end
|
||||
|
||||
local function sendPayload(payload)
|
||||
local len = #payload
|
||||
if len < 1 or len > TX_MAX_PAYLOAD or multiBuffer(TX_READY) ~= 0 then
|
||||
return false
|
||||
end
|
||||
for i = 1, TX_MAX_PAYLOAD do
|
||||
multiBuffer(TX_PAYLOAD + i - 1, 0)
|
||||
end
|
||||
for i = 1, #payload do
|
||||
multiBuffer(TX_PAYLOAD + i - 1, byte(payload[i]))
|
||||
end
|
||||
multiBuffer(TX_READY, len)
|
||||
return true
|
||||
end
|
||||
|
||||
local function sendSpecial1(arg)
|
||||
return sendPayload({ 0x18, 0, 0, arg % 256, math.floor(arg / 256), 0 })
|
||||
end
|
||||
|
||||
local function gyroSelector()
|
||||
if gyro.senCh < 2 or gyro.senCh > 8 then return 0 end
|
||||
return gyro.senCh - 1
|
||||
end
|
||||
|
||||
local function sendGyro()
|
||||
local sen = gyroRaw(gyro.sen)
|
||||
local pca = gyroRaw(gyro.pca)
|
||||
local ags = gyroRaw(gyro.ags)
|
||||
local b0 = gyro.enable + gyro.phase * 2 + gyroSelector() * 4 + (sen % 4) * 64
|
||||
local b1 = math.floor(sen / 4) + (pca % 4) * 64
|
||||
local b2 = math.floor(pca / 4) + (ags % 4) * 64
|
||||
local b3 = math.floor(ags / 4) + limit(gyro.flags, 0, 3) * 64
|
||||
sendPayload({ 0x28, 0, 0, b0, b1, b2, b3, 0 })
|
||||
end
|
||||
|
||||
local function decodeGyroBytes(b0, b1, b2, b3)
|
||||
b0 = byte(b0); b1 = byte(b1); b2 = byte(b2); b3 = byte(b3)
|
||||
gyro.enable = b0 % 2
|
||||
gyro.phase = math.floor(b0 / 2) % 2
|
||||
local selector = math.floor(b0 / 4) % 16
|
||||
gyro.senCh = selector == 0 and 0 or limit(selector + 1, 2, 8)
|
||||
gyro.sen = gyroRaw(math.floor(b0 / 64) + (b1 % 64) * 4)
|
||||
gyro.pca = gyroRaw(math.floor(b1 / 64) + (b2 % 64) * 4)
|
||||
gyro.ags = gyroRaw(math.floor(b2 / 64) + (b3 % 64) * 4)
|
||||
gyro.flags = math.floor(b3 / 64) % 4
|
||||
haveValues = true
|
||||
end
|
||||
|
||||
local function pollRx()
|
||||
local len = multiBuffer(RX_READY)
|
||||
if not len or len == 0 then return end
|
||||
if len > RX_MAX_PAYLOAD then
|
||||
multiBuffer(RX_READY, 0)
|
||||
return
|
||||
end
|
||||
|
||||
local p = {}
|
||||
for i = 1, len do
|
||||
p[i] = multiBuffer(RX_PAYLOAD + i - 1)
|
||||
end
|
||||
multiBuffer(RX_READY, 0)
|
||||
|
||||
if len == 7 and p[1] == 0x01 then
|
||||
decodeGyroBytes(p[3], p[4], p[5], p[6])
|
||||
end
|
||||
end
|
||||
|
||||
local function chText()
|
||||
if gyro.senCh == 0 then return "NULL" end
|
||||
return "CH" .. gyro.senCh
|
||||
end
|
||||
|
||||
local function rebuildLines()
|
||||
lines = {
|
||||
{ "Poll RX", "action", function() sendSpecial1(1) end },
|
||||
{ "Gyro", "toggle", "enable" },
|
||||
{ "Phase", "toggle", "phase" },
|
||||
{ "SEN Ch", "channel", "senCh" },
|
||||
{ "SEN", "percent", "sen" },
|
||||
{ "PCA", "percent", "pca" },
|
||||
{ "AGS", "percent", "ags" },
|
||||
{ "Servo Hz", "flags", "flags" },
|
||||
{ "Send Gyro", "action", sendGyro },
|
||||
{ "Set GY EPA", "action", function() sendSpecial1(2) end },
|
||||
{ "Set FS", "action", function() sendSpecial1(3) end },
|
||||
}
|
||||
end
|
||||
|
||||
local function lineValue(line)
|
||||
if line[2] == "action" then return ">" end
|
||||
if not haveValues then return "--" end
|
||||
if line[2] == "toggle" then return gyro[line[3]] == 0 and "Off" or "On" end
|
||||
if line[2] == "channel" then return chText() end
|
||||
if line[2] == "percent" then return gyroText(gyro[line[3]]) end
|
||||
if line[2] == "flags" then return servoHz[gyro.flags + 1] or tostring(gyro.flags) end
|
||||
return ""
|
||||
end
|
||||
|
||||
local function changeValue(dir, fast)
|
||||
local line = lines[sel]
|
||||
if not line then return end
|
||||
local step = fast and 20 or 1
|
||||
haveValues = true
|
||||
|
||||
if line[2] == "toggle" then
|
||||
gyro[line[3]] = dir > 0 and 1 or 0
|
||||
elseif line[2] == "channel" then
|
||||
local vals = { 0, 2, 3, 4, 5, 6, 7, 8 }
|
||||
local pos = 1
|
||||
for i = 1, #vals do
|
||||
if vals[i] == gyro.senCh then pos = i end
|
||||
end
|
||||
gyro.senCh = vals[limit(pos + dir, 1, #vals)]
|
||||
elseif line[2] == "percent" then
|
||||
gyro[line[3]] = gyroRaw(gyro[line[3]] + dir * step)
|
||||
elseif line[2] == "flags" then
|
||||
gyro.flags = limit(gyro.flags + dir, 0, 3)
|
||||
end
|
||||
end
|
||||
|
||||
local function fastRotary()
|
||||
return getRotEncSpeed() > 1
|
||||
end
|
||||
|
||||
local function handleEvent(event)
|
||||
local nextEvent = edit and EVT_VIRTUAL_INC or EVT_VIRTUAL_NEXT
|
||||
local prevEvent = edit and EVT_VIRTUAL_DEC or EVT_VIRTUAL_PREV
|
||||
if event == nextEvent then
|
||||
if edit then changeValue(1, fastRotary()) else sel = limit(sel + 1, 1, #lines) end
|
||||
elseif event == prevEvent then
|
||||
if edit then changeValue(-1, fastRotary()) else sel = limit(sel - 1, 1, #lines) end
|
||||
elseif event == EVT_VIRTUAL_NEXT_PAGE then
|
||||
if edit then changeValue(1, true) else sel = limit(sel + screenLines(), 1, #lines) end
|
||||
elseif event == EVT_VIRTUAL_PREV_PAGE then
|
||||
if edit then changeValue(-1, true) else sel = limit(sel - screenLines(), 1, #lines) end
|
||||
killEvents(event)
|
||||
elseif event == EVT_VIRTUAL_ENTER then
|
||||
local line = lines[sel]
|
||||
if line and line[2] == "action" then
|
||||
line[3]()
|
||||
elseif line then
|
||||
edit = not edit
|
||||
end
|
||||
end
|
||||
|
||||
local count = screenLines()
|
||||
if sel < top then top = sel end
|
||||
if sel >= top + count then top = sel - count + 1 end
|
||||
end
|
||||
|
||||
local function draw()
|
||||
lcd.clear()
|
||||
drawTitle("DumboRC P series")
|
||||
|
||||
local font = colorLcd and 0 or SMLSIZE
|
||||
local x = 2
|
||||
local y = colorLcd and 34 or 9
|
||||
local dy = colorLcd and 20 or 8
|
||||
local valueX = colorLcd and 150 or 82
|
||||
|
||||
if not moduleOk then
|
||||
if colorLcd then
|
||||
lcd.drawText(x, y + dy, "Select Multi RadLink/Dumbo_P", font + BLINK)
|
||||
else
|
||||
lcd.drawText(x, y + dy, "Select RadLink", font + BLINK)
|
||||
lcd.drawText(x, y + dy * 2, "Dumbo_P", font + BLINK)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
for i = top, math.min(#lines, top + screenLines() - 1) do
|
||||
local attr = font
|
||||
if i == sel then
|
||||
attr = attr + INVERS
|
||||
if edit then
|
||||
blink = (blink + 1) % 30
|
||||
if blink > 15 then attr = font end
|
||||
end
|
||||
end
|
||||
local yy = y + (i - top) * dy
|
||||
lcd.drawText(x, yy, lines[i][1], attr)
|
||||
lcd.drawText(valueX, yy, lineValue(lines[i]), attr)
|
||||
end
|
||||
end
|
||||
|
||||
local function init()
|
||||
moduleOk = type(multiBuffer) == "function" and findModule()
|
||||
rebuildLines()
|
||||
if moduleOk then initBuffer() end
|
||||
end
|
||||
|
||||
local function run(event)
|
||||
if event == nil then
|
||||
error("Cannot be run as a model script!")
|
||||
return 2
|
||||
elseif event == EVT_VIRTUAL_EXIT then
|
||||
if moduleOk then release() end
|
||||
return 2
|
||||
end
|
||||
if moduleOk then
|
||||
pollRx()
|
||||
handleEvent(event)
|
||||
end
|
||||
draw()
|
||||
return 0
|
||||
end
|
||||
|
||||
return { init = init, run = run }
|
||||
@@ -86,17 +86,14 @@
|
||||
55,1,FrSkyRX,CloneTX,0
|
||||
55,2,FrSkyRX,EraseTX,0
|
||||
55,3,FrSkyRX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
|
||||
58,0,FX,FX816,1
|
||||
58,1,FX,FX620,1
|
||||
58,0,FX,816,1
|
||||
58,1,FX,620,1
|
||||
58,2,FX,9630,1,Rate,Gyro,TrimR,TrimA,TrimE
|
||||
58,3,FX,Q560,1,FLIP,Gyro,LEDs
|
||||
58,4,FX,QF012,1,FLIP,Gyro,Invert,Reset
|
||||
58,5,FX,BM26,1,FLIP,Gyro,LEDs,LED,CALIB
|
||||
58,6,FX,A570,1,STOP,Rate,Color,LED,TrimR,TrimA,TrimE
|
||||
20,0,FY326,FY326,1,Flip,RTH,HLess,Expert,Calib
|
||||
20,1,FY326,FY319,1,Flip,RTH,HLess,Expert,Calib
|
||||
23,0,FQ777,124,1,Flip,RTH,HLess,Expert
|
||||
23,1,FQ777,XBM37,1,Rate,Flip,HLess,LED,Pict,Video,RTH,OK,ETrim,Atrim
|
||||
23,0,FY326,FY326,1,Flip,RTH,HLess,Expert
|
||||
47,0,GD00x,V1,1,Trim,LED,Rate
|
||||
47,1,GD00x,V2,1,Trim,LED,Rate
|
||||
32,0,GW008,FY326,1,Flip
|
||||
@@ -113,7 +110,7 @@
|
||||
26,1,Hontai,JJRCX1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib
|
||||
26,2,Hontai,X5C1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib
|
||||
26,3,Hontai,FQ777_951,1,Flip,Arm,Pict,Video,HLess,RTH,Calib
|
||||
26,4,Hontai,XKK170,1,Rate,Emerg,TakLan,Calib,TrimA,TrimE,Optic
|
||||
26,4,Hontai,XKK170,1,Rate,Emerg,TakLan,Calib,TrimA,TrimE
|
||||
57,0,HoTT,Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
|
||||
57,1,HoTT,No_Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
|
||||
2,0,Hubsan,H107,1,Flip,Light,Pict,Video,HLess
|
||||
@@ -122,7 +119,7 @@
|
||||
22,0,J6Pro,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12
|
||||
71,0,JJRC345,JJRC345,1,Flip,HLess,RTH,LED,UNK1,UNK2,UNK3
|
||||
71,1,JJRC345,SkyTmblr,1,Flip,HLess,RTH,LED,UNK1,UNK2,UNK3
|
||||
49,0,KF606,KF606,1,Trim,LED
|
||||
49,0,KF606,KF606,1,Trim
|
||||
49,1,KF606,MIG320,1,Trim,LED
|
||||
49,2,KF606,ZCZ50,1,Trim,UNK
|
||||
9,0,KN,WLToys,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim,Rtrim,Hover
|
||||
@@ -163,9 +160,8 @@
|
||||
72,0,Q90C,Std,0,FMode,VTX+
|
||||
74,0,RadioLink,Surface,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8
|
||||
74,1,RadioLink,Air,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8
|
||||
74,2,RadioLink,DumboRC,0,CH5,CH6,CH7,CH8GY,CH9,CH10,n-a,n-a,n-a,n-a,n-a,n-a
|
||||
74,2,RadioLink,DumboRC,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8
|
||||
74,3,RadioLink,RC4G,0,CH5,FS_CH1,FS_CH2,FS_CH3,FS_CH4
|
||||
74,4,RadioLink,Dumbo_P,0,CH5,CH6,CH7,CH8GY,CH9,CH10,n-a,n-a,n-a,n-a,n-a,n-a
|
||||
76,0,Realacc,Std,1,Flip,Light,Calib,HLess,RTH,ThCut,Rotat
|
||||
50,0,Redpine,Fast,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16
|
||||
50,1,Redpine,Slow,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16
|
||||
@@ -200,8 +196,8 @@
|
||||
62,0,XK,X450,1,FMode,TakeOf,Emerg,3D_6G,Pict,Video
|
||||
62,1,XK,X420,1,FMode,TakeOf,Emerg,3D_6G,Pict,Video,Flip,Light
|
||||
62,2,XK,Cars,0,FMode,TakeOf,Emerg,3D_6G,Pict,Video,Flip,Light
|
||||
99,0,XK2,X4,0,Rate,Mode,Hover,Light,Stunt
|
||||
99,1,XK2,P10,0,Rate,Mode,Hover,Light,Stunt
|
||||
99,0,XK2,X4,0,Rate,Mode,Hover,Light
|
||||
99,1,XK2,P10,0,Rate,Mode,Hover,Light
|
||||
8,0,YD717,Std,1,Flip,Light,Pict,Video,HLess
|
||||
8,1,YD717,SkyWlkr,1,Flip,Light,Pict,Video,HLess
|
||||
8,2,YD717,Simax4,1,Flip,Light,Pict,Video,HLess
|
||||
@@ -239,4 +235,3 @@
|
||||
104,0,KAMTOM,Std,0,ST_Tr,TH_Tr,TH_DR
|
||||
105,0,Shenqi2,Std,1
|
||||
106,0,WL91x,Std,0
|
||||
107,0,WPL,Std,0,Light,TH_DR,ST_DR
|
||||
|
||||
@@ -280,7 +280,7 @@ local function Multi_Init()
|
||||
end
|
||||
|
||||
--Exceptions on first 4 channels...
|
||||
if ( protocol == 73 or (protocol == 74 and (sub_protocol == 0 or sub_protocol == 2 or sub_protocol == 4)) or (protocol == 60 and sub_protocol == 2) or protocol == 89) then -- Kyosho or RadioLink (Surface or DumboRC/P) or Pelikan/SCX24 or Losi
|
||||
if ( protocol == 73 or (protocol == 74 and sub_protocol == 0) or (protocol == 60 and sub_protocol == 2) or protocol == 89) then -- Kyosho or RadioLink Surface or Pelikan/SCX24 or Losi
|
||||
channel_names[1] = "ST"
|
||||
channel_names[2] = "THR"
|
||||
channel_names[3] = "CH3"
|
||||
|
||||
@@ -93,9 +93,6 @@ static void AFHDS2A_update_telemetry()
|
||||
if (option & 0x80)
|
||||
{// forward 0xAA and 0xAC telemetry to TX, skip rx and tx id to save space
|
||||
packet_in[0]= TX_RSSI;
|
||||
//Fix for CROSSOVER-RX,
|
||||
if(packet[17+8]==0xFF && packet[21+8]==0xFC && packet[25+8]==0xFF)
|
||||
packet[17+8]=0xFE; // RX_ERR_RATE needed for TX to validate the telem
|
||||
#if 0
|
||||
debug("T(%02X)=",packet[0]);
|
||||
for(uint8_t i=9;i < AFHDS2A_RXPACKET_SIZE; i++)
|
||||
|
||||
@@ -599,7 +599,6 @@ void DSM_init()
|
||||
uint16_t temp = DSM_CLONE_EEPROM_OFFSET;
|
||||
for(uint8_t i=0;i<4;i++)
|
||||
cyrfmfg_id[i] = eeprom_read_byte((EE_ADDR)temp++);
|
||||
cyrfmfg_id[3]^=RX_num; //Model match
|
||||
#if DEBUG_BIND
|
||||
debugln("Using cloned ID");
|
||||
debug("Clone ID=")
|
||||
@@ -620,7 +619,8 @@ void DSM_init()
|
||||
{
|
||||
//SUB_PROTO_VALID;
|
||||
CYRF_GetMfgData(cyrfmfg_id);
|
||||
cyrfmfg_id[3]^=RX_num; //Model match
|
||||
//Model match
|
||||
cyrfmfg_id[3]^=RX_num;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,8 +44,9 @@ static void __attribute__((unused)) E129_build_data_packet()
|
||||
packet[ 1] = 0xA6; // Set to A5 every few packets??
|
||||
|
||||
//Flags
|
||||
packet[ 2] = 0xF7; // E129 High rate 0xF7, low 0xF4
|
||||
if(sub_protocol == E129_C186)
|
||||
if(sub_protocol == E129_E129)
|
||||
packet[ 2] = 0xF7; // High rate 0xF7, low 0xF4
|
||||
else //C186
|
||||
{
|
||||
packet[ 2] = 0xFA; // High rate 0xFA, medium 0xF7, low 0xF4
|
||||
packet[13] = bit_reverse(rx_tx_addr[2]);
|
||||
|
||||
@@ -31,19 +31,6 @@ 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};
|
||||
|
||||
@@ -100,21 +87,12 @@ static void __attribute__((unused)) FQ777_send_packet()
|
||||
uint8_t packet_ori[8];
|
||||
if (IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
// Byte 0 is always 0x20; bytes 1-3 are sub-protocol specific
|
||||
// 4,5,6 = address fields
|
||||
// last field is checksum of address fields
|
||||
packet_ori[0] = 0x20;
|
||||
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[1] = 0x15;
|
||||
packet_ori[2] = 0x05;
|
||||
packet_ori[3] = 0x06;
|
||||
packet_ori[4] = rx_tx_addr[0];
|
||||
packet_ori[5] = rx_tx_addr[1];
|
||||
packet_ori[6] = rx_tx_addr[2];
|
||||
@@ -122,64 +100,36 @@ static void __attribute__((unused)) FQ777_send_packet()
|
||||
}
|
||||
else
|
||||
{
|
||||
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?
|
||||
// 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
|
||||
|
||||
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
|
||||
|
||||
// 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[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)
|
||||
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
|
||||
uint8_t checksum = 0;
|
||||
for (int i = 0; i < 7; ++i)
|
||||
checksum += packet_ori[i];
|
||||
@@ -231,17 +181,12 @@ void FQ777_init(void)
|
||||
BIND_IN_PROGRESS; // autobind protocol
|
||||
bind_counter = FQ777_BIND_COUNT;
|
||||
packet_count=0;
|
||||
// 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[0] = 0x4D;
|
||||
hopping_frequency[1] = 0x43;
|
||||
hopping_frequency[2] = 0x27;
|
||||
hopping_frequency[3] = 0x07;
|
||||
hopping_frequency_no=0;
|
||||
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[2] = 0x00;
|
||||
rx_tx_addr[3] = 0xe7;
|
||||
rx_tx_addr[4] = 0x67;
|
||||
FQ777_RF_init();
|
||||
|
||||
@@ -42,15 +42,9 @@ Multiprotocol is distributed in the hope that it will be useful,
|
||||
#define FX_QF012_PACKET_PERIOD 12194
|
||||
#define FX_QF012_RX_PAYLOAD_SIZE 3
|
||||
|
||||
#define FX_BM26_BIND_CHANNEL 40
|
||||
#define FX_BM26_PACKET_PERIOD 14066
|
||||
|
||||
#define FX_A570_PACKET_PERIOD 10270
|
||||
|
||||
//#define FORCE_FX620_ID
|
||||
//#define FORCE_FX9630_ID
|
||||
//#define FORCE_QIDI_ID
|
||||
//#define FORCE_FX_A570_ID
|
||||
|
||||
enum
|
||||
{
|
||||
@@ -67,7 +61,7 @@ static void __attribute__((unused)) FX_send_packet()
|
||||
{
|
||||
XN297_Hopping(hopping_frequency_no++);
|
||||
if(sub_protocol >= FX9630)
|
||||
{ // FX9630 & FX_Q560 & FX_QF012 & FX_A570
|
||||
{ // FX9630 & FX_Q560 & FX_QF012
|
||||
if (hopping_frequency_no >= FX9630_NUM_CHANNELS)
|
||||
{
|
||||
hopping_frequency_no = 0;
|
||||
@@ -76,7 +70,7 @@ static void __attribute__((unused)) FX_send_packet()
|
||||
trim_ch++;
|
||||
trim_ch &= 3;
|
||||
}
|
||||
else // FX_Q560 & FX_QF012 & FX_A570
|
||||
else // FX_Q560 & FX_QF012
|
||||
trim_ch = 0;
|
||||
}
|
||||
}
|
||||
@@ -90,7 +84,27 @@ static void __attribute__((unused)) FX_send_packet()
|
||||
|
||||
//Channels
|
||||
uint8_t val;
|
||||
if (sub_protocol == FX816 || sub_protocol == FX620)
|
||||
if (sub_protocol >= FX9630)
|
||||
{ // FX9630 & FX_Q560 & FX_QF012
|
||||
packet[0] = convert_channel_8b(THROTTLE);
|
||||
packet[1] = convert_channel_8b(AILERON);
|
||||
packet[2] = 0xFF - convert_channel_8b(ELEVATOR);
|
||||
packet[3] = convert_channel_8b(RUDDER);
|
||||
val = trim_ch==0 ? 0x20 : (convert_channel_8b(trim_ch + CH6) >> 2); // no trim on Throttle
|
||||
packet[4] = val; // Trim for channel x 0C..20..34
|
||||
packet[5] = (trim_ch << 4) // channel x << 4
|
||||
| GET_FLAG(CH5_SW, (sub_protocol == FX_QF012 ? 0x08 : 0x01)) // DR toggle swich: 0 small throw, 1 large throw / Q560 acrobatic / QF012 Special effects
|
||||
// FX9630 =>0:6G small throw, 1:6G large throw, 2:3D
|
||||
// QIDI-550=>0:3D, 1:6G, 2:Torque
|
||||
// QF012=>0:beginner(6G), 1:mid(3D), 2:expert(Gyro off)
|
||||
| (Channel_data[CH6] < CHANNEL_MIN_COMMAND ? 0x00 : (Channel_data[CH6] > CHANNEL_MAX_COMMAND ? 0x04 : 0x02));
|
||||
if(sub_protocol == FX_Q560)
|
||||
packet[5] |= GET_FLAG(CH7_SW, 0x18); // Q560 LED flag 0x10 conflicting with trim_ch... Corrected on new boards using 0x08 instead
|
||||
else if (sub_protocol == FX_QF012)
|
||||
packet[5] |= GET_FLAG(CH7_SW, 0x40) // QF012 invert flight
|
||||
| GET_FLAG(CH8_SW, 0x80); // QF012 Restore fine tunning midpoint
|
||||
}
|
||||
else // FX816 & FX620
|
||||
{
|
||||
uint8_t offset=sub_protocol == FX816 ? FX816_CH_OFFSET:FX620_CH_OFFSET;
|
||||
val=convert_channel_8b(AILERON);
|
||||
@@ -102,60 +116,6 @@ static void __attribute__((unused)) FX_send_packet()
|
||||
packet[offset] = sub_protocol == FX816 ? 0:0x7F;
|
||||
packet[offset+1] = convert_channel_16b_limit(THROTTLE,0,100); //FX816:0x00..0x63, FX620:0x00..0x5E but that should work
|
||||
}
|
||||
else if (sub_protocol == FX_A570)
|
||||
{
|
||||
packet[0] = convert_channel_16b_limit(THROTTLE,0x1C,0xE4);
|
||||
packet[1] = convert_channel_16b_limit(AILERON,0xE4,0x1C);
|
||||
packet[2] = convert_channel_16b_limit(ELEVATOR,0xE4,0x1C);
|
||||
packet[3] = convert_channel_16b_limit(RUDDER,0x1C,0xE4);
|
||||
// Trims - Ruddder L:01 R:02, Aileron L:03 R:04, Elevator Fwd:05 Back:06
|
||||
packet[4] = (Channel_data[CH10] < CHANNEL_MIN_COMMAND ? 0x01 : GET_FLAG(CH10_SW, 0x02)) // Rudder trim
|
||||
| (Channel_data[CH11] < CHANNEL_MIN_COMMAND ? 0x03 : GET_FLAG(CH11_SW, 0x04)) // Aileron trim
|
||||
| (Channel_data[CH12] < CHANNEL_MIN_COMMAND ? 0x06 : GET_FLAG(CH12_SW, 0x05)); // Elevator trim
|
||||
packet[5] = GET_FLAG(CH5_SW, 0x40) // motors stop
|
||||
// A570 flight modes = 0>vertical flight(3 axis mode):00, 1>flat flight(6G):02, 2>vertical flight(2 axis mode):04
|
||||
| (Channel_data[CH6] < CHANNEL_MIN_COMMAND ? 0x00 : (Channel_data[CH6] > CHANNEL_MAX_COMMAND ? 0x04 : 0x02))
|
||||
| GET_FLAG(CH7_SW, 0x01) // 0:low rate, 1:high rate
|
||||
| GET_FLAG(CH8_SW, 0x08) // LED color change
|
||||
| GET_FLAG(CH9_SW, 0x10); // LED off
|
||||
}
|
||||
else
|
||||
{ // FX9630 & FX_Q560 & FX_QF012 & FX_BM26
|
||||
packet[0] = convert_channel_8b(THROTTLE);
|
||||
packet[1] = convert_channel_8b(AILERON);
|
||||
packet[2] = 0xFF - convert_channel_8b(ELEVATOR);
|
||||
packet[3] = convert_channel_8b(RUDDER);
|
||||
if ( sub_protocol == FX_BM26 ) // BM26 Ail, Elev, Rud range between 0x00-0x3F-0x7F
|
||||
{
|
||||
packet[1] >>= 1;
|
||||
packet[2] >>= 1;
|
||||
packet[3] >>= 1;
|
||||
}
|
||||
val = trim_ch==0 ? 0x20 : (convert_channel_8b(trim_ch + CH6) >> 2); // no trim on Throttle
|
||||
packet[4] = val; // Trim for channel x 0C..20..34
|
||||
packet[5] = (trim_ch << 4) // channel x << 4
|
||||
| GET_FLAG(CH5_SW, (sub_protocol == FX_QF012 ? 0x08 : 0x01)) // DR toggle swich: 0 small throw, 1 large throw / Q560 acrobatic / QF012 Special effects
|
||||
// FX9630 =>0:6G small throw, 1:6G large throw, 2:3D
|
||||
// QIDI-550=>0:3D, 1:6G, 2:Torque
|
||||
// QF012=>0:beginner(6G), 1:mid(3D), 2:expert(Gyro off)
|
||||
| (Channel_data[CH6] < CHANNEL_MIN_COMMAND ? 0x00 : (Channel_data[CH6] > CHANNEL_MAX_COMMAND ? 0x04 : 0x02));
|
||||
if(sub_protocol == FX_Q560)
|
||||
packet[5] |= GET_FLAG(CH7_SW, 0x18); // Q560 LED flag 0x10 conflicting with trim_ch... Corrected on new boards using 0x08 instead
|
||||
else if (sub_protocol == FX_QF012)
|
||||
packet[5] |= GET_FLAG(CH7_SW, 0x40) // QF012 invert flight
|
||||
| GET_FLAG(CH8_SW, 0x80); // QF012 Restore fine tunning midpoint
|
||||
else if ( sub_protocol == FX_BM26 )
|
||||
{
|
||||
packet[5] &= 0xC0;
|
||||
packet[5] += 0x40;
|
||||
// BM26 P51=>0:beginner(6G), 1:mid(3D), 2:expert(Gyro off)
|
||||
packet[5] |= (Channel_data[CH6] < CHANNEL_MIN_COMMAND ? 0x08 : (Channel_data[CH6] > CHANNEL_MAX_COMMAND ? 0x0C : 0x0A));
|
||||
packet[6] = GET_FLAG( CH5_SW, 0x20) // 6G Roll
|
||||
| GET_FLAG(!CH7_SW, 0x08) // Light control switch, default on
|
||||
| GET_FLAG(!CH8_SW, 0x10) // Turn signal switch, default on
|
||||
| GET_FLAG( CH9_SW, 0x01); // Gyro Calibration
|
||||
}
|
||||
}
|
||||
|
||||
//Bind and specifics
|
||||
if(sub_protocol == FX816)
|
||||
@@ -182,7 +142,7 @@ static void __attribute__((unused)) FX_send_packet()
|
||||
packet[5] = 0xAB; // Is it based on ID??
|
||||
}
|
||||
}
|
||||
else // FX9630 & FX_Q560 & FX_QF012 & FX_BM26 & FX_A570
|
||||
else // FX9630 & FX_Q560 & FX_QF012
|
||||
{
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
@@ -204,8 +164,6 @@ static void __attribute__((unused)) FX_send_packet()
|
||||
val = val ^ 0xFF;
|
||||
packet[last_packet_idx]=val;
|
||||
|
||||
if ( IS_BIND_IN_PROGRESS && sub_protocol == FX_BM26 ) packet[7] = packet[6];
|
||||
|
||||
//Debug
|
||||
#if 0
|
||||
for(uint8_t i=0;i<packet_length;i++)
|
||||
@@ -222,7 +180,6 @@ static void __attribute__((unused)) FX_send_packet()
|
||||
static void __attribute__((unused)) FX_RF_init()
|
||||
{
|
||||
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
|
||||
packet_length = FX9630_PAYLOAD_SIZE; // default
|
||||
if(sub_protocol == FX816)
|
||||
{
|
||||
XN297_SetTXAddr((uint8_t *)"\xcc\xcc\xcc\xcc\xcc", 5);
|
||||
@@ -237,23 +194,12 @@ static void __attribute__((unused)) FX_RF_init()
|
||||
packet_period = FX620_BIND_PACKET_PERIOD;
|
||||
packet_length = FX620_PAYLOAD_SIZE;
|
||||
}
|
||||
else if (sub_protocol == FX_BM26)
|
||||
{
|
||||
XN297_SetTXAddr((uint8_t *)"\x12\x34\x10\x10", 4);
|
||||
XN297_RFChannel(FX_BM26_BIND_CHANNEL);
|
||||
packet_period = FX_BM26_PACKET_PERIOD;
|
||||
}
|
||||
else if(sub_protocol == FX_A570)
|
||||
{
|
||||
XN297_SetTXAddr((uint8_t *)"\x56\x78\x92\x13", 4);
|
||||
XN297_RFChannel(FX9630_BIND_CHANNEL);
|
||||
packet_period = FX_A570_PACKET_PERIOD;
|
||||
}
|
||||
else // FX9630 & FX_Q560 & FX_QF012
|
||||
{
|
||||
XN297_SetTXAddr((uint8_t *)"\x56\x78\x90\x12", 4);
|
||||
XN297_RFChannel(FX9630_BIND_CHANNEL);
|
||||
packet_period = sub_protocol == FX_QF012 ? FX_QF012_PACKET_PERIOD : FX9630_PACKET_PERIOD;
|
||||
packet_length = FX9630_PAYLOAD_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,11 +226,10 @@ static void __attribute__((unused)) FX_initialize_txid()
|
||||
for(uint8_t i=1;i<FX_NUM_CHANNELS;i++)
|
||||
hopping_frequency[i] = i*10 + hopping_frequency[0];
|
||||
}
|
||||
else // FX9630 & FX_Q560 & FX_QF012 & FX_BM26 & FX_A570
|
||||
else // FX9630 & FX_Q560 & FX_QF012
|
||||
{
|
||||
//??? Need to find out how the first RF channel is calculated ???
|
||||
hopping_frequency[0] = sub_protocol == FX_BM26? 0x0C : 0x13;
|
||||
if (sub_protocol == FX_A570) hopping_frequency[0] = 0x0E;
|
||||
hopping_frequency[0] = 0x13;
|
||||
//Other 2 RF channels are sent during the bind phase so they can be whatever
|
||||
hopping_frequency[1] = RX_num & 0x0F + 0x1A;
|
||||
hopping_frequency[2] = rx_tx_addr[3] & 0x0F + 0x38;
|
||||
@@ -300,19 +245,14 @@ static void __attribute__((unused)) FX_initialize_txid()
|
||||
//memcpy(rx_tx_addr,(uint8_t*)"\x38\xC7\x6D\x8D", 4);
|
||||
//memcpy(hopping_frequency,"\x0D\x20\x3A", FX9630_NUM_CHANNELS);
|
||||
#endif
|
||||
// BM26 rx_tx_addr: "\x17\x03\x00\x00", hopping_frequency: 0x0C,0x30,0x43(12,48,67)
|
||||
#ifdef FORCE_FX_A570_ID
|
||||
memcpy(rx_tx_addr,(uint8_t*)"\xA9\x56\xFC\x1C", 4);
|
||||
memcpy(hopping_frequency,"\x0E\x1F\x39", FX9630_NUM_CHANNELS); //Original dump=>14=0x0E,31=0x1F,57=0x39
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t FX_callback()
|
||||
{
|
||||
#ifdef FX_HUB_TELEMETRY
|
||||
bool rx=false;
|
||||
static uint8_t telem_count = 0;
|
||||
|
||||
switch(phase)
|
||||
{
|
||||
case FX_DATA:
|
||||
@@ -341,20 +281,16 @@ uint16_t FX_callback()
|
||||
}
|
||||
FX_send_packet();
|
||||
#ifdef FX_HUB_TELEMETRY
|
||||
if (sub_protocol == FX816 || sub_protocol == FX620 || sub_protocol == FX_A570)
|
||||
if(sub_protocol < FX9630)
|
||||
break;
|
||||
if(rx)
|
||||
{
|
||||
debug("RX");
|
||||
if(XN297_ReadPayload(packet_in, FX_QF012_RX_PAYLOAD_SIZE))
|
||||
{//Good CRC
|
||||
//packets: A5 00 11 -> A5 01 11
|
||||
telemetry_link = 1;
|
||||
telemetry_lost = 0;
|
||||
telem_count = 0;
|
||||
if ( sub_protocol == FX_BM26 )
|
||||
v_lipo1 = packet_in[0] < packet_in[2] ? 60:81; // packets: AA 00 55 -> 55 00 AA = low voltage 3.7V
|
||||
else
|
||||
v_lipo1 = packet_in[1] ? 60:81; // packets: A5 00 11 -> A5 01 11 = low voltage 3.7V
|
||||
v_lipo1 = packet_in[1] ? 60:81; // low voltage 3.7V
|
||||
#if 0
|
||||
for(uint8_t i=0; i < FX_QF012_RX_PAYLOAD_SIZE; i++)
|
||||
debug(" %02X", packet_in[i]);
|
||||
@@ -362,19 +298,6 @@ uint16_t FX_callback()
|
||||
}
|
||||
debugln();
|
||||
}
|
||||
if(telem_count > 4*63) // Around 3.5 sec with no telemetry
|
||||
telemetry_lost = 1;
|
||||
else
|
||||
{
|
||||
telem_count++;
|
||||
if(!telemetry_lost && (telem_count & 0x3F) == 0)
|
||||
{// Should have received a telem packet but... Send telem to the radio to keep it alive
|
||||
telemetry_link = 1;
|
||||
#if 0
|
||||
debugln("Miss");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
phase++;
|
||||
return FX9630_WRITE_TIME;
|
||||
default: //FX_RX
|
||||
@@ -407,7 +330,6 @@ void FX_init()
|
||||
bind_counter=FX_BIND_COUNT;
|
||||
#ifdef FX_HUB_TELEMETRY
|
||||
RX_RSSI = 100; // Dummy value
|
||||
telemetry_lost = 1;
|
||||
phase = FX_DATA;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -78,8 +78,7 @@ static void __attribute__((unused)) HONTAI_send_packet()
|
||||
packet[i+3] = convert_channel_16b_limit(CH_TAER[i],0x28,0xD8);
|
||||
packet[6] ^= 0xFF; //Reverse rudder
|
||||
//flags
|
||||
packet[1] = GET_FLAG(CH8_SW, 0x04) //Gyro calibration (momentary)
|
||||
|GET_FLAG(CH11_SW, 0x10); //XK K270 optical flow switch
|
||||
packet[1] = GET_FLAG(CH8_SW, 0x04); //Gyro calibration (momentary)
|
||||
// |GET_FLAG(CH_SW, 0x08) //Unk long press second top right button (momentary)
|
||||
// |GET_FLAG(CH_SW, 0x10) //Unk short press second top right button (toggle)
|
||||
// |GET_FLAG(CH_SW, 0x40) //Unk short press second top left button (momentary)
|
||||
|
||||
@@ -48,19 +48,32 @@ static void __attribute__((unused)) KF606_send_packet()
|
||||
hopping_frequency_no ^= 1; // 2 RF channels
|
||||
|
||||
packet[0] = 0x55;
|
||||
if(sub_protocol == KF606_ZCZ50) len--;
|
||||
packet[len-3] = convert_channel_8b(THROTTLE); // 0..255
|
||||
packet[1] = convert_channel_8b(THROTTLE); // 0..255
|
||||
// Deadband is needed on aileron, 40 gives +-6%
|
||||
packet[len-2] = convert_channel_8b_limit_deadband(AILERON,0x00,0x80,0xFF,40); // Aileron: High rate:2B..80..DA
|
||||
uint8_t p3;
|
||||
p3 = convert_channel_8b(CH5)>>3; // Aileron trim must be on a separated channel 01..10..1F
|
||||
p3 += (packet[2]-0x80)>>3; // Drive trims for more aileron authority
|
||||
if(p3 > 0x80)
|
||||
p3 = 0x01;
|
||||
else if(p3 > 0x1F)
|
||||
p3 = 0x1F;
|
||||
p3 |= GET_FLAG(CH6_SW, 0xC0); // 0xC0 and 0xE0 are both turning the LED off, not sure if there is another hidden feature
|
||||
packet[len-1] = p3;
|
||||
switch(sub_protocol)
|
||||
{
|
||||
case KF606_KF606:
|
||||
packet[2] = convert_channel_8b_limit_deadband(AILERON,0x20,0x80,0xE0,40); // Aileron: Max values:20..80..E0, Low rates:50..80..AF, High rates:3E..80..C1
|
||||
packet[3] = convert_channel_16b_limit(CH5,0xC1,0xDF); // Aileron trim must be on a separated channel C1..D0..DF
|
||||
break;
|
||||
case KF606_MIG320:
|
||||
packet[2] = convert_channel_8b_limit_deadband(AILERON,0x00,0x80,0xFF,40); // Aileron: High rate:2B..80..DA
|
||||
packet[3] = convert_channel_16b_limit(CH5,0x01,0x1F); // Aileron trim must be on a separated channel 01..10..1F
|
||||
packet[3] += (packet[2]-0x80)>>3; // Drive trims for more aileron authority
|
||||
if(packet[3] > 0x80)
|
||||
packet[3] = 0x01;
|
||||
else if(packet[3] > 0x1F)
|
||||
packet[3] = 0x1F;
|
||||
packet[3] |= GET_FLAG(CH6_SW, 0xC0); // 0xC0 and 0xE0 are both turning the LED off, not sure if there is another hidden feature
|
||||
break;
|
||||
case KF606_ZCZ50:
|
||||
len--; // uses only 3 bytes of payload
|
||||
packet[0] = packet[1]; // Throttle: 0x00..0xFF
|
||||
packet[1] = convert_channel_8b_limit_deadband(AILERON,0x20,0x80,0xE0,40); // Aileron: Max values:20..80..E0, low rate 0x52..0x80..0xB1, high rate: 0x41..0x80..0xC3.
|
||||
packet[2] = convert_channel_16b_limit(CH5,0x01,0x1F); // Trim: 0x01..0x10..0x1F
|
||||
packet[2] |= GET_FLAG(CH6_SW, 0xC0); // Unknown: 0x00 or 0xC0. Left top switch on original TX changes nothing on my plane. Maybe ON/OFF for main motor?
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(sub_protocol == KF606_MIG320)
|
||||
|
||||
@@ -530,7 +530,7 @@ void MT99XX_init(void)
|
||||
bind_counter = MT99XX_BIND_COUNT;
|
||||
if(IS_BIND_DONE)
|
||||
{
|
||||
if(sub_protocol != A180 && sub_protocol != DRAGON && sub_protocol != F949G && sub_protocol != PA18+8 && sub_protocol != SU35+8)
|
||||
if(sub_protocol != A180 && sub_protocol != DRAGON && sub_protocol != F949G && sub_protocol != PA18+8)
|
||||
BIND_IN_PROGRESS; // autobind protocol
|
||||
else
|
||||
bind_counter = 1;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
20,FY326,FY326,FY319
|
||||
21,Futaba,SFHSS
|
||||
22,J6PRO
|
||||
23,FQ777,124,XBM37
|
||||
23,FQ777
|
||||
24,ASSAN
|
||||
25,FrskyV
|
||||
26,HONTAI,HONTAI,JJRCX1,X5C1,FQ777_951,XKK170
|
||||
@@ -55,7 +55,7 @@
|
||||
55,Frsky_RX,Multi,CloneTX,EraseTX,CPPM
|
||||
56,AFHDS2A_RX,Multi,CPPM
|
||||
57,HoTT,Sync,No_Sync
|
||||
58,FX,FX816,FX620,9630,Q560,QF012,BM26,A570
|
||||
58,FX,816,620,9630,Q560,QF012
|
||||
59,Bayang_RX,Multi,CPPM
|
||||
60,Pelikan,Pro,Lite,SCX24
|
||||
61,EazyRC
|
||||
@@ -71,7 +71,7 @@
|
||||
71,JJRC345,JJRC345,SkyTmblr
|
||||
72,Q90C
|
||||
73,Kyosho,FHSS,Hype
|
||||
74,RadioLink,Surface,Air,DumboRC,RC4G,Dumbo_P
|
||||
74,RadioLink,Surface,Air,DumboRC,RC4G
|
||||
75,---
|
||||
76,Realacc
|
||||
77,OMP
|
||||
@@ -102,4 +102,3 @@
|
||||
104,KAMTOM
|
||||
105,Shenqi2
|
||||
106,WL91x
|
||||
107,WPL
|
||||
|
||||
@@ -118,7 +118,6 @@ const char STR_UDIRC[] ="UDIRC";
|
||||
const char STR_JIABAILE[] ="JIABAILE";
|
||||
const char STR_KAMTOM[] ="KAMTOM";
|
||||
const char STR_WL91X[] ="WL91x";
|
||||
const char STR_WPL[] ="WPL";
|
||||
|
||||
const char STR_SUBTYPE_FLYSKY[] = "\x04""Std\0""V9x9""V6x6""V912""CX20";
|
||||
const char STR_SUBTYPE_HUBSAN[] = "\x04""H107""H301""H501";
|
||||
@@ -147,7 +146,6 @@ const char STR_SUBTYPE_MT99[] = "\x06""MT99\0 ""H7\0 ""YZ\0 ""LS\0 "
|
||||
const char STR_SUBTYPE_MT992[] = "\x04""PA18""SU35";
|
||||
const char STR_SUBTYPE_MJXQ[] = "\x07""WLH08\0 ""X600\0 ""X800\0 ""H26D\0 ""E010\0 ""H26WH\0 ""Phoenix";
|
||||
const char STR_SUBTYPE_FY326[] = "\x05""Std\0 ""FY319";
|
||||
const char STR_SUBTYPE_FQ777[] = "\x05""124\0 ""XBM37";
|
||||
const char STR_SUBTYPE_HONTAI[] = "\x06""Std\0 ""JJRCX1""X5C1\0 ""FQ_951""XKK170";
|
||||
const char STR_SUBTYPE_AFHDS2A[] = "\x08""PWM,IBUS""PPM,IBUS""PWM,SBUS""PPM,SBUS""Gyro_Off""Gyro_On\0""G_On_Rev";
|
||||
const char STR_SUBTYPE_Q2X2[] = "\x04""Q222""Q242""Q282";
|
||||
@@ -180,7 +178,7 @@ const char STR_SUBTYPE_WFLY2[] = "\x05""RF20x";
|
||||
const char STR_SUBTYPE_HOTT[] = "\x07""Sync\0 ""No_Sync";
|
||||
const char STR_SUBTYPE_PELIKAN[] = "\x05""Pro\0 ""Lite\0""SCX24";
|
||||
const char STR_SUBTYPE_V761[] = "\x05""3ch\0 ""4ch\0 ""TOPRC";
|
||||
const char STR_SUBTYPE_RLINK[] = "\x07""Surface""Air\0 ""DumboRC""RC4G\0 ""Dumbo_P";
|
||||
const char STR_SUBTYPE_RLINK[] = "\x07""Surface""Air\0 ""DumboRC""RC4G\0 ";
|
||||
const char STR_SUBTYPE_KYOSHO[] = "\x04""FHSS""Hype";
|
||||
const char STR_SUBTYPE_KYOSHO2[] = "\x05""KT-17";
|
||||
const char STR_SUBTYPE_KYOSHO3[] = "\x03""ASF";
|
||||
@@ -189,8 +187,8 @@ const char STR_SUBTYPE_JJRC345[] = "\x08""JJRC345\0""SkyTmblr";
|
||||
const char STR_SUBTYPE_MOULDKG[] = "\x05""A4444""D4444""A664\0";
|
||||
const char STR_SUBTYPE_KF606[] = "\x06""KF606\0""MIG320""ZCZ50\0";
|
||||
const char STR_SUBTYPE_E129[] = "\x04""E129""C186";
|
||||
const char STR_SUBTYPE_FX[] = "\x05""FX816""FX620""9630\0""Q560\0""QF012""BM26\0""A570\0";
|
||||
const char STR_SUBTYPE_SGF22[] = "\x04""F22\0""F22S""J20\0""CX10";
|
||||
const char STR_SUBTYPE_FX[] = "\x05""816\0 ""620\0 ""9630\0""Q560\0""QF012";
|
||||
const char STR_SUBTYPE_SGF22[] = "\x04""F22\0""F22S""J20\0";
|
||||
const char STR_SUBTYPE_JIABAILE[] = "\x04""Std\0""Gyro";
|
||||
#define NO_SUBTYPE nullptr
|
||||
|
||||
@@ -315,7 +313,7 @@ const mm_protocol_definition multi_protocols[] = {
|
||||
{PROTO_AFHDS2A_RX, STR_AFHDS2A_RX,STR_CPPM, NBR_CPPM, OPTION_NONE, 0, 0, SW_A7105, AFHDS2A_RX_init, AFHDS2A_RX_callback },
|
||||
#endif
|
||||
#if defined(FQ777_NRF24L01_INO)
|
||||
{PROTO_FQ777, STR_FQ777, STR_SUBTYPE_FQ777, 2, OPTION_NONE, 0, 0, SW_NRF, FQ777_init, FQ777_callback },
|
||||
{PROTO_FQ777, STR_FQ777, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, FQ777_init, FQ777_callback },
|
||||
#endif
|
||||
//OpenTX 2.3.x issue: DO NOT CHANGE ORDER below
|
||||
#if defined(FRSKY_RX_CC2500_INO)
|
||||
@@ -346,7 +344,7 @@ const mm_protocol_definition multi_protocols[] = {
|
||||
{PROTO_FUTABA, STR_FUTABA, STR_SUBTYPE_FUTABA, 1, OPTION_RFTUNE, 1, 1, SW_CC2500, SFHSS_init, SFHSS_callback },
|
||||
#endif
|
||||
#if defined(FX_NRF24L01_INO)
|
||||
{PROTO_FX, STR_FX, STR_SUBTYPE_FX, 7, OPTION_NONE, 0, 0, SW_NRF, FX_init, FX_callback },
|
||||
{PROTO_FX, STR_FX, STR_SUBTYPE_FX, 5, OPTION_NONE, 0, 0, SW_NRF, FX_init, FX_callback },
|
||||
#endif
|
||||
#if defined(FY326_NRF24L01_INO)
|
||||
{PROTO_FY326, STR_FY326, STR_SUBTYPE_FY326, 2, OPTION_NONE, 0, 0, SW_NRF, FY326_init, FY326_callback },
|
||||
@@ -460,7 +458,7 @@ const mm_protocol_definition multi_protocols[] = {
|
||||
{PROTO_Q90C, STR_Q90C, NO_SUBTYPE, 0, OPTION_RFTUNE, 0, 0, SW_NRF, Q90C_init, Q90C_callback },
|
||||
#endif
|
||||
#if defined(RLINK_CC2500_INO)
|
||||
{PROTO_RLINK, STR_RLINK, STR_SUBTYPE_RLINK, 5, OPTION_RFTUNE, 0, 0, SW_CC2500, RLINK_init, RLINK_callback },
|
||||
{PROTO_RLINK, STR_RLINK, STR_SUBTYPE_RLINK, 4, OPTION_RFTUNE, 0, 0, SW_CC2500, RLINK_init, RLINK_callback },
|
||||
#endif
|
||||
#if defined(REALACC_NRF24L01_INO)
|
||||
{PROTO_REALACC, STR_REALACC, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, REALACC_init, REALACC_callback },
|
||||
@@ -475,7 +473,7 @@ const mm_protocol_definition multi_protocols[] = {
|
||||
{PROTO_SCORPIO, STR_SCORPIO, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_CYRF, SCORPIO_init, SCORPIO_callback },
|
||||
#endif
|
||||
#if defined(SGF22_NRF24L01_INO)
|
||||
{PROTO_SGF22, STR_SGF22, STR_SUBTYPE_SGF22, 4, OPTION_NONE, 0, 0, SW_NRF, SGF22_init, SGF22_callback },
|
||||
{PROTO_SGF22, STR_SGF22, STR_SUBTYPE_SGF22, 3, OPTION_NONE, 0, 0, SW_NRF, SGF22_init, SGF22_callback },
|
||||
#endif
|
||||
#if defined(SHENQI_NRF24L01_INO)
|
||||
{PROTO_SHENQI, STR_SHENQI, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, SHENQI_init, SHENQI_callback },
|
||||
@@ -520,9 +518,7 @@ const mm_protocol_definition multi_protocols[] = {
|
||||
#if defined(WL91X_CCNRF_INO)
|
||||
{PROTO_WL91X, STR_WL91X, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, WL91X_init, WL91X_callback },
|
||||
#endif
|
||||
#if defined(WPL_NRF24L01_INO)
|
||||
{PROTO_WPL, STR_WPL, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, WPL_init, WPL_callback },
|
||||
#endif
|
||||
|
||||
#if defined(XERALL_NRF24L01_INO)
|
||||
{PROTO_XERALL, STR_XERALL, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, XERALL_init, XERALL_callback },
|
||||
#endif
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#define VERSION_MAJOR 1
|
||||
#define VERSION_MINOR 3
|
||||
#define VERSION_REVISION 4
|
||||
#define VERSION_PATCH_LEVEL 58
|
||||
#define VERSION_PATCH_LEVEL 50
|
||||
|
||||
#define MODE_SERIAL 0
|
||||
|
||||
@@ -134,7 +134,6 @@ enum PROTOCOLS
|
||||
PROTO_KAMTOM = 104, // =>NRF24L01
|
||||
PROTO_SHENQI2 = 105, // =>NRF24L01
|
||||
PROTO_WL91X = 106, // =>CC2500 & NRF24L01
|
||||
PROTO_WPL = 107, // =>NRF24L01
|
||||
|
||||
PROTO_NANORF = 126, // =>NRF24L01
|
||||
PROTO_TEST = 127, // =>CC2500
|
||||
@@ -418,11 +417,6 @@ enum ESKY
|
||||
ESKY_STD = 0,
|
||||
ESKY_ET4 = 1,
|
||||
};
|
||||
enum FQ777
|
||||
{
|
||||
FQ777 = 0,
|
||||
XBM37 = 1,
|
||||
};
|
||||
enum FRSKY_RX
|
||||
{
|
||||
FRSKY_RX = 0,
|
||||
@@ -473,7 +467,6 @@ enum RLINK
|
||||
RLINK_AIR = 1,
|
||||
RLINK_DUMBORC = 2,
|
||||
RLINK_RC4G = 3,
|
||||
RLINK_DUMBORC_P = 4,
|
||||
};
|
||||
enum MOULDKG
|
||||
{
|
||||
@@ -499,15 +492,12 @@ enum FX
|
||||
FX9630 = 2,
|
||||
FX_Q560 = 3,
|
||||
FX_QF012 = 4,
|
||||
FX_BM26 = 5,
|
||||
FX_A570 = 6,
|
||||
};
|
||||
enum SGF22
|
||||
{
|
||||
SGF22_F22 = 0,
|
||||
SGF22_F22S = 1,
|
||||
SGF22_J20 = 2,
|
||||
SGF22_CX10 = 3,
|
||||
};
|
||||
enum JIABAILE
|
||||
{
|
||||
@@ -585,7 +575,6 @@ enum MultiPacketTypes
|
||||
MULTI_TELEMETRY_MLINK = 15,
|
||||
MULTI_TELEMETRY_CONFIG = 16,
|
||||
MULTI_TELEMETRY_PROTO = 17,
|
||||
MULTI_TELEMETRY_RLINK = 18,
|
||||
};
|
||||
|
||||
// Macros
|
||||
@@ -1202,8 +1191,6 @@ Serial: 100000 Baud 8e2 _ xxxx xxxx p --
|
||||
RLINK_SURFACE 0
|
||||
RLINK_AIR 1
|
||||
RLINK_DUMBORC 2
|
||||
RLINK_RC4G 3
|
||||
RLINK_DUMBORC_P 4
|
||||
|
||||
Power value => 0x80 0=High/1=Low
|
||||
Stream[3] = option_protocol;
|
||||
@@ -1231,7 +1218,6 @@ Serial: 100000 Baud 8e2 _ xxxx xxxx p --
|
||||
FrSkyX and FrSkyX2: Stream[27..34] during normal operation unstuffed SPort data to be sent
|
||||
HoTT: Stream[27] 1 byte for telemetry type
|
||||
DSM: Stream[27..33] Forward Programming
|
||||
RadioLink/DumboRC P: Stream[27..35] raw command payload, used to send failsafe and gyro settings
|
||||
*/
|
||||
/*
|
||||
Multiprotocol telemetry/command definition for OpenTX and erskyTX
|
||||
@@ -1382,9 +1368,5 @@ Serial: 100000 Baud 8e2 _ xxxx xxxx p --
|
||||
data[n+2] = number of sub protocols
|
||||
data[n+3] = sub protocols text length, only sent if nbr_sub != 0
|
||||
data[n+4..] = sub protocol names, only sent if nbr_sub != 0
|
||||
|
||||
Type 0x12 RadioLink/DumboRC P raw command payload
|
||||
length: variable
|
||||
data[0..] = raw command payload, used to send failsafe and gyro settings
|
||||
|
||||
|
||||
*/
|
||||
|
||||
@@ -265,11 +265,6 @@ uint8_t packet_in[TELEMETRY_BUFFER_SIZE];//telemetry receiving packets
|
||||
uint8_t CONFIG_SerialRX_val[7];
|
||||
bool CONFIG_SerialRX=false;
|
||||
#endif
|
||||
#ifdef RLINK_HUB_TELEMETRY
|
||||
uint8_t RLINK_SerialRX_val[8];
|
||||
uint8_t RLINK_SerialRX_len=0;
|
||||
bool RLINK_SerialRX=false;
|
||||
#endif
|
||||
#endif // TELEMETRY
|
||||
|
||||
uint8_t multi_protocols_index=0xFF;
|
||||
@@ -1582,15 +1577,6 @@ void update_serial_data()
|
||||
CONFIG_SerialRX=true;
|
||||
}
|
||||
#endif
|
||||
#ifdef RLINK_HUB_TELEMETRY
|
||||
if(protocol==PROTO_RLINK && sub_protocol==RLINK_DUMBORC_P
|
||||
&& rx_len>27 && rx_len<=27+sizeof(RLINK_SerialRX_val))
|
||||
{//DumboRC P raw command payload from Lua/multiBuffer bridge
|
||||
RLINK_SerialRX_len=rx_len-27;
|
||||
memcpy(RLINK_SerialRX_val, (const void *)&rx_ok_buff[27], RLINK_SerialRX_len);
|
||||
RLINK_SerialRX=true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
RX_DONOTUPDATE_off;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// Radiolink surface protocol. TXs: RC4GS,RC6GS. Compatible RXs:R7FG(Std),R6FG,R6F,R8EF,R8FM,R8F,R4FGM
|
||||
// Radiolink surface protocol. TXs: RC4GS,RC6GS. Compatible RXs:R7FG(Std),R6FG,R6F,R8EF,R8FM,R8F,R4FGM
|
||||
|
||||
#if defined(RLINK_CC2500_INO)
|
||||
|
||||
@@ -37,7 +37,6 @@ enum {
|
||||
|
||||
uint32_t RLINK_rand1;
|
||||
uint32_t RLINK_rand2;
|
||||
uint32_t RLINK_pseudo;
|
||||
|
||||
static uint32_t __attribute__((unused)) RLINK_prng_next(uint32_t r)
|
||||
{
|
||||
@@ -85,7 +84,7 @@ static void __attribute__((unused)) RLINK_shuffle_freqs(uint32_t seed)
|
||||
static void __attribute__((unused)) RLINK_hop()
|
||||
{
|
||||
uint8_t inc=3*(rx_tx_addr[0]&3);
|
||||
|
||||
|
||||
// init hop table
|
||||
for(uint8_t i=0; i<RLINK_HOP; i++)
|
||||
hopping_frequency[i] = (12*i) + inc;
|
||||
@@ -94,79 +93,12 @@ static void __attribute__((unused)) RLINK_hop()
|
||||
RLINK_shuffle_freqs(RLINK_compute_start_id(rx_tx_addr[0] + (rx_tx_addr[1] << 8)));
|
||||
RLINK_shuffle_freqs(RLINK_compute_start_id(rx_tx_addr[2] + (rx_tx_addr[3] << 8)));
|
||||
|
||||
// replace one of the channel randomly
|
||||
// replace one of the channel randomely
|
||||
rf_ch_num=random(0xfefefefe)%0x11; // 0x00..0x10
|
||||
if(inc==9) inc=6; // frequency exception
|
||||
hopping_frequency[rf_ch_num]=12*16+inc;
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) RLINK_hop_RC4G()
|
||||
{
|
||||
// Find 2 unused channels
|
||||
// first channel is a multiple of 3 between 00 and 5D
|
||||
// second channel is a multiple of 3 between 63 and BD
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_WriteReg(CC2500_17_MCSM1,0x3C);
|
||||
CC2500_Strobe(CC2500_SFRX);
|
||||
CC2500_SetTxRxMode(RX_EN);
|
||||
CC2500_Strobe(CC2500_SRX);
|
||||
delayMilliseconds(1); //wait for RX mode
|
||||
uint16_t val;
|
||||
uint8_t val_low = 0xFF;
|
||||
hopping_frequency[0] = 0x00;
|
||||
hopping_frequency[1] = 0x63;
|
||||
for(uint8_t ch=0; ch<=0xBD; ch+=3)
|
||||
{
|
||||
if(ch==0x63)
|
||||
val_low = 0xFF; //init for second block
|
||||
if(ch==0x60)
|
||||
continue; //skip channel
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, ch); //switch channel
|
||||
delayMicroseconds(370); //wait to read
|
||||
val = 0;
|
||||
for(uint8_t i=0;i<16;i++)
|
||||
val += CC2500_ReadReg(CC2500_34_RSSI | CC2500_READ_BURST);
|
||||
val >>= 4;
|
||||
debug("C:%02X RSSI:%02X",ch,val);
|
||||
if(val_low > val)
|
||||
{
|
||||
debug(" OK");
|
||||
val_low = val;
|
||||
hopping_frequency[ch<0x63?0:1]=ch; //save best channel
|
||||
}
|
||||
debugln("");
|
||||
}
|
||||
CC2500_WriteReg(CC2500_17_MCSM1,0x30);
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_SetTxRxMode(TX_EN);
|
||||
#ifdef RLINK_RC4G_FORCE_ID
|
||||
hopping_frequency[0] = 0x03;
|
||||
hopping_frequency[1] = 0x6F;
|
||||
#endif
|
||||
}
|
||||
|
||||
// calc next pseudo random value
|
||||
static void __attribute__((unused)) RLINK_next_pseudo()
|
||||
{
|
||||
RLINK_pseudo = ((RLINK_pseudo * 0xAA) + 0x03) % 0x7673;
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) RLINK_set_next_channel()
|
||||
{
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency[RLINK_pseudo & 0x0F]);
|
||||
}
|
||||
|
||||
static uint8_t __attribute__((unused)) RLINK_checksum(const uint8_t *data, uint8_t payload_len, bool include_id=false)
|
||||
{
|
||||
uint8_t sum=0;
|
||||
for(uint8_t i=0;i<payload_len;i++)
|
||||
sum+=data[i];
|
||||
if(include_id)
|
||||
for(uint8_t i=0;i<RLINK_TX_ID_LEN;i++)
|
||||
sum+=rx_tx_addr[i];
|
||||
return sum;
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) RLINK_TXID_init()
|
||||
{
|
||||
#ifdef RLINK_RC4G_FORCE_ID
|
||||
@@ -185,10 +117,52 @@ static void __attribute__((unused)) RLINK_TXID_init()
|
||||
memcpy(rx_tx_addr,"\xFC\x11\x0D\x20",RLINK_TX_ID_LEN); //air T8FB
|
||||
#endif
|
||||
// channels order depend on ID
|
||||
if(sub_protocol==RLINK_RC4G)
|
||||
RLINK_hop_RC4G();
|
||||
if(sub_protocol!=RLINK_RC4G)
|
||||
RLINK_hop();
|
||||
else
|
||||
RLINK_hop();
|
||||
{//RLINK_RC4G
|
||||
// Find 2 unused channels
|
||||
// first channel is a multiple of 3 between 00 and 5D
|
||||
// second channel is a multiple of 3 between 63 and BD
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_WriteReg(CC2500_17_MCSM1,0x3C);
|
||||
CC2500_Strobe(CC2500_SFRX);
|
||||
CC2500_SetTxRxMode(RX_EN);
|
||||
CC2500_Strobe(CC2500_SRX);
|
||||
delayMilliseconds(1); //wait for RX mode
|
||||
uint16_t val;
|
||||
uint8_t val_low = 0xFF;
|
||||
hopping_frequency[0] = 0x00;
|
||||
hopping_frequency[1] = 0x63;
|
||||
for(uint8_t ch=0; ch<=0xBD; ch+=3)
|
||||
{
|
||||
if(ch==0x63)
|
||||
val_low = 0xFF; //init for second block
|
||||
if(ch==0x60)
|
||||
continue; //skip channel
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, ch); //switch channel
|
||||
delayMicroseconds(370); //wait to read
|
||||
val = 0;
|
||||
for(uint8_t i=0;i<16;i++)
|
||||
val += CC2500_ReadReg(CC2500_34_RSSI | CC2500_READ_BURST);
|
||||
val >>= 4;
|
||||
debug("C:%02X RSSI:%02X",ch,val);
|
||||
if(val_low > val)
|
||||
{
|
||||
debug(" OK");
|
||||
val_low = val;
|
||||
hopping_frequency[ch<0x63?0:1]=ch; //save best channel
|
||||
}
|
||||
debugln("");
|
||||
}
|
||||
CC2500_WriteReg(CC2500_17_MCSM1,0x30);
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_SetTxRxMode(TX_EN);
|
||||
#ifdef RLINK_RC4G_FORCE_ID
|
||||
hopping_frequency[0] = 0x03;
|
||||
hopping_frequency[1] = 0x6F;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef RLINK_DEBUG
|
||||
debug("ID:");
|
||||
@@ -217,21 +191,22 @@ static void __attribute__((unused)) RLINK_rf_init()
|
||||
for (uint8_t i = 0; i < 39; ++i)
|
||||
CC2500_WriteReg(i, pgm_read_byte_near(&RLINK_init_values[i]));
|
||||
|
||||
if(sub_protocol == RLINK_DUMBORC || sub_protocol == RLINK_DUMBORC_P)
|
||||
if(sub_protocol==RLINK_DUMBORC)
|
||||
{
|
||||
CC2500_WriteReg(4, 0xBA);
|
||||
CC2500_WriteReg(5, 0xDC);
|
||||
}
|
||||
else if(sub_protocol==RLINK_RC4G)
|
||||
CC2500_WriteReg(5, 0xA5);
|
||||
|
||||
|
||||
CC2500_WriteReg(CC2500_0C_FSCTRL0, option);
|
||||
|
||||
|
||||
CC2500_SetTxRxMode(TX_EN);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) RLINK_send_packet()
|
||||
{
|
||||
static uint32_t pseudo=0;
|
||||
uint32_t bits = 0;
|
||||
uint8_t bitsavailable = 0;
|
||||
uint8_t idx = 6;
|
||||
@@ -250,18 +225,17 @@ static void __attribute__((unused)) RLINK_send_packet()
|
||||
{
|
||||
case RLINK_SURFACE:
|
||||
packet[1] |= 0x01;
|
||||
//radiolink additional ID which is working only on a small set of RXs
|
||||
//radiolink additionnal ID which is working only on a small set of RXs
|
||||
//if(RX_num) packet[1] |= ((RX_num+2)<<4)+4; // RX number limited to 10 values, 0 is a wildcard
|
||||
break;
|
||||
case RLINK_AIR:
|
||||
packet[1] |= 0x21; //air 0x21 on dump but it looks to support telemetry at least RSSI
|
||||
break;
|
||||
case RLINK_DUMBORC:
|
||||
case RLINK_DUMBORC_P:
|
||||
packet[1] |= 0x01; //always 0x00 on dump but does appear to support telemtry on newer transmitters
|
||||
packet[1] = 0x00; //always 0x00 on dump
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// ID
|
||||
memcpy(&packet[2],rx_tx_addr,RLINK_TX_ID_LEN);
|
||||
|
||||
@@ -282,28 +256,31 @@ static void __attribute__((unused)) RLINK_send_packet()
|
||||
bitsavailable -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// hop
|
||||
RLINK_next_pseudo();
|
||||
RLINK_set_next_channel();
|
||||
packet[28]= RLINK_pseudo;
|
||||
packet[29]= RLINK_pseudo >> 8;
|
||||
pseudo=((pseudo * 0xAA) + 0x03) % 0x7673; // calc next pseudo random value
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency[pseudo & 0x0F]);
|
||||
packet[28]= pseudo;
|
||||
packet[29]= pseudo >> 8;
|
||||
packet[30]= 0x00; // unknown
|
||||
packet[31]= 0x00; // unknown
|
||||
packet[32]= rf_ch_num; // index of value changed in the RF table
|
||||
|
||||
|
||||
// check
|
||||
packet[33]=RLINK_checksum(&packet[1], RLINK_TX_PACKET_LEN-1);
|
||||
uint8_t sum=0;
|
||||
for(uint8_t i=1;i<33;i++)
|
||||
sum+=packet[i];
|
||||
packet[33]=sum;
|
||||
|
||||
// send packet
|
||||
CC2500_WriteData(packet, RLINK_TX_PACKET_LEN+1);
|
||||
|
||||
|
||||
// packets type
|
||||
packet_count++;
|
||||
if(packet_count>5) packet_count=0;
|
||||
|
||||
#ifdef RLINK_DEBUG
|
||||
debugln("C= 0x%02X",hopping_frequency[RLINK_pseudo & 0x0F]);
|
||||
debugln("C= 0x%02X",hopping_frequency[pseudo & 0x0F]);
|
||||
debug("P=");
|
||||
for(uint8_t i=1;i<RLINK_TX_PACKET_LEN+1;i++)
|
||||
debug(" 0x%02X",packet[i]);
|
||||
@@ -311,78 +288,6 @@ static void __attribute__((unused)) RLINK_send_packet()
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef RLINK_HUB_TELEMETRY
|
||||
|
||||
static bool __attribute__((unused)) RLINK_DUMBORC_send_command()
|
||||
{
|
||||
if(!RLINK_SerialRX || sub_protocol != RLINK_DUMBORC_P)
|
||||
return false;
|
||||
|
||||
RLINK_SerialRX=false;
|
||||
|
||||
if(RLINK_SerialRX_len > sizeof(RLINK_SerialRX_val))
|
||||
return false;
|
||||
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
|
||||
RLINK_next_pseudo();
|
||||
RLINK_set_next_channel();
|
||||
|
||||
packet[0] = RLINK_SerialRX_len;
|
||||
memcpy(&packet[1], RLINK_SerialRX_val, RLINK_SerialRX_len);
|
||||
packet[2] = RLINK_pseudo;
|
||||
packet[3] = RLINK_pseudo >> 8;
|
||||
// special packages have id check embedded in checksum
|
||||
packet[RLINK_SerialRX_len] = RLINK_checksum(&packet[1], RLINK_SerialRX_len - 1, true);
|
||||
|
||||
CC2500_WriteData(packet, RLINK_SerialRX_len + 1);
|
||||
|
||||
packet_count++;
|
||||
if(packet_count>5) packet_count=0;
|
||||
|
||||
#ifdef RLINK_DEBUG
|
||||
debugln("C= 0x%02X",hopping_frequency[RLINK_pseudo & 0x0F]);
|
||||
debug("DumboRC P command=");
|
||||
for(uint8_t i=1;i<packet[0]+1;i++)
|
||||
debug(" 0x%02X",packet[i]);
|
||||
debugln("");
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
// logic is rougly copied from ddf-350, which in itself seems to be based on cc2500 docs
|
||||
static uint8_t __attribute__((unused)) RLINK_DUMBORC_tele_rssi_as_percent(uint8_t rssi)
|
||||
{
|
||||
if(rssi < 15)
|
||||
rssi=15;
|
||||
else if(rssi > 90)
|
||||
rssi=90;
|
||||
return ((90 - rssi) * 100) / 75;
|
||||
}
|
||||
|
||||
static bool __attribute__((unused)) RLINK_DUMBORC_validate_telemetry_packet(const uint8_t *data)
|
||||
{
|
||||
const uint8_t declaredLen = data[0];
|
||||
|
||||
if(data[1] == 0x00)
|
||||
{
|
||||
// telemetry package follows base radiolink procotol with slightly less rules
|
||||
if(declaredLen != RLINK_RX_PACKET_LEN || memcmp(&data[2], rx_tx_addr, RLINK_TX_ID_LEN) != 0)
|
||||
return false;
|
||||
|
||||
// telemetry packages do not have id check embeeded in checksum, just like base RadioLink
|
||||
return data[RLINK_RX_PACKET_LEN] == RLINK_checksum(&data[1], RLINK_RX_PACKET_LEN - 1);
|
||||
}
|
||||
|
||||
if(sub_protocol != RLINK_DUMBORC_P)
|
||||
return false;
|
||||
|
||||
// special packages have id check embedded in checksum
|
||||
return data[declaredLen] == RLINK_checksum(&data[1], declaredLen - 1, true);
|
||||
}
|
||||
|
||||
#ifndef MULTI_AIR
|
||||
static void __attribute__((unused)) RLINK_RC4G_send_packet()
|
||||
{
|
||||
@@ -406,7 +311,7 @@ static void __attribute__((unused)) RLINK_RC4G_send_packet()
|
||||
packet[5+i*2] = val;
|
||||
packet[8+i ] |= (val>>4) & 0xF0;
|
||||
}
|
||||
//special channel which is linked to gyro on the original TX but allocating it on CH5 here
|
||||
//special channel which is linked to gyro on the orginal TX but allocating it on CH5 here
|
||||
packet[10] = convert_channel_16b_limit(CH5,0,100);
|
||||
//failsafe
|
||||
for(uint8_t i=0;i<4;i++)
|
||||
@@ -427,15 +332,10 @@ static void __attribute__((unused)) RLINK_RC4G_send_packet()
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined RLINK_HUB_TELEMETRY
|
||||
uint16_t RLINK_timing_last_rfsend = 0;
|
||||
#endif
|
||||
|
||||
#define RLINK_TIMING_PROTO 20000-100 // -100 for compatibility with R8EF
|
||||
#define RLINK_TIMING_RFSEND 10500
|
||||
#define RLINK_TIMING_CHECK 2000
|
||||
#define RLINK_RC4G_TIMING_PROTO 14460
|
||||
#define RLINK_DUMBORC_COMMAND_RFSEND 5000
|
||||
uint16_t RLINK_callback()
|
||||
{
|
||||
if(sub_protocol == RLINK_RC4G)
|
||||
@@ -460,92 +360,47 @@ uint16_t RLINK_callback()
|
||||
#endif
|
||||
CC2500_SetPower();
|
||||
CC2500_SetFreqOffset();
|
||||
RLINK_send_packet();
|
||||
#if not defined RLINK_HUB_TELEMETRY
|
||||
RLINK_send_packet();
|
||||
return RLINK_TIMING_PROTO; // RLINK_DATA
|
||||
return RLINK_TIMING_PROTO;
|
||||
#else
|
||||
if(RLINK_DUMBORC_send_command())
|
||||
{
|
||||
phase++; // RX1
|
||||
RLINK_timing_last_rfsend = RLINK_DUMBORC_COMMAND_RFSEND;
|
||||
return RLINK_timing_last_rfsend;
|
||||
}
|
||||
|
||||
RLINK_send_packet();
|
||||
|
||||
if(!(packet[1]&0x02))
|
||||
return RLINK_TIMING_PROTO; // Normal packet -> RLINK_DATA
|
||||
// Telemetry packet
|
||||
return RLINK_TIMING_PROTO; //Normal packet
|
||||
//Telemetry packet
|
||||
phase++; // RX1
|
||||
RLINK_timing_last_rfsend = RLINK_TIMING_RFSEND;
|
||||
return RLINK_timing_last_rfsend;
|
||||
return RLINK_TIMING_RFSEND;
|
||||
case RLINK_RX1:
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_Strobe(CC2500_SFRX);
|
||||
CC2500_SetTxRxMode(RX_EN);
|
||||
CC2500_Strobe(CC2500_SRX);
|
||||
phase++; // RX2
|
||||
return RLINK_TIMING_PROTO-RLINK_timing_last_rfsend-RLINK_TIMING_CHECK;
|
||||
return RLINK_TIMING_PROTO-RLINK_TIMING_RFSEND-RLINK_TIMING_CHECK;
|
||||
case RLINK_RX2:
|
||||
len = CC2500_ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F;
|
||||
const bool dumborc_family = sub_protocol == RLINK_DUMBORC || sub_protocol == RLINK_DUMBORC_P;
|
||||
//Telemetry frame is 15 bytes + 1 byte for length + 2 bytes for RSSI&LQI&CRC
|
||||
const bool rlink_telem_len = !dumborc_family && len == RLINK_RX_PACKET_LEN + 1 + 2;
|
||||
// length byte + type byte + checksum byte + RSSI/LQI/CRC
|
||||
const bool dumborc_len = dumborc_family && len >= 5 && len <= sizeof(packet_in);
|
||||
if (rlink_telem_len || dumborc_len)
|
||||
len = CC2500_ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F;
|
||||
if (len == RLINK_RX_PACKET_LEN + 1 + 2) //Telemetry frame is 15 bytes + 1 byte for length + 2 bytes for RSSI&LQI&CRC
|
||||
{
|
||||
#ifdef RLINK_DEBUG_TELEM
|
||||
debug("Telem:");
|
||||
#endif
|
||||
CC2500_ReadData(packet_in, len);
|
||||
if(len >= 3 && packet_in[0] == len - 3 && (packet_in[len-1] & 0x80))
|
||||
{//Telemetry received with correct length and CC2500 CRC
|
||||
if(packet_in[0]==RLINK_RX_PACKET_LEN && (packet_in[len-1] & 0x80) && memcmp(&packet[2],rx_tx_addr,RLINK_TX_ID_LEN)==0 && packet_in[6]==packet[1])
|
||||
{//Correct telemetry received: length, CRC, ID and type
|
||||
#ifdef RLINK_DEBUG_TELEM
|
||||
for(uint8_t i=0;i<len;i++)
|
||||
debug(" %02X",packet_in[i]);
|
||||
#endif
|
||||
bool valid_telem=false;
|
||||
if(dumborc_family)
|
||||
{
|
||||
if(RLINK_DUMBORC_validate_telemetry_packet(packet_in))
|
||||
{
|
||||
if(packet_in[1] == 0x00)
|
||||
{
|
||||
uint8_t tele_rssi = RLINK_DUMBORC_tele_rssi_as_percent(packet_in[7]);
|
||||
uint16_t ext_v = packet_in[9] | (((uint16_t)packet_in[10]) << 8);
|
||||
uint16_t direct_rssi = packet_in[11] | (((uint16_t)packet_in[12]) << 8);
|
||||
direct_rssi = direct_rssi > 100 ? 100 : direct_rssi;
|
||||
RX_RSSI = direct_rssi ? direct_rssi : tele_rssi;
|
||||
v_lipo1 = 0; //Has no RX batt
|
||||
v_lipo2 = ext_v > 255 ? 255 : ext_v; //Batt in same position as base radiolink
|
||||
valid_telem=true;
|
||||
}
|
||||
else if(sub_protocol == RLINK_DUMBORC_P)
|
||||
{
|
||||
telemetry_link=2; // Raw DumboRC P packet to Lua/multiBuffer handling.
|
||||
pps_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(packet_in[0] == RLINK_RX_PACKET_LEN && memcmp(&packet[2],rx_tx_addr,RLINK_TX_ID_LEN)==0 && packet_in[6]==packet[1])
|
||||
{
|
||||
RX_RSSI=packet_in[7]&0x7F; //Should be packet_in[7]-256 but since it's an uint8_t...
|
||||
v_lipo1=packet_in[8]<<1; //RX Batt
|
||||
v_lipo2=packet_in[9]; //Batt
|
||||
valid_telem=true;
|
||||
}
|
||||
if(valid_telem)
|
||||
{
|
||||
TX_RSSI = packet_in[len-2];
|
||||
if(TX_RSSI >=128)
|
||||
TX_RSSI -= 128;
|
||||
else
|
||||
TX_RSSI += 128;
|
||||
telemetry_link=1; //Send telemetry out
|
||||
pps_counter++;
|
||||
packet_count=0;
|
||||
}
|
||||
TX_RSSI = packet_in[len-2];
|
||||
if(TX_RSSI >=128)
|
||||
TX_RSSI -= 128;
|
||||
else
|
||||
TX_RSSI += 128;
|
||||
RX_RSSI=packet_in[7]&0x7F; //Should be packet_in[7]-256 but since it's an uint8_t...
|
||||
v_lipo1=packet_in[8]<<1; //RX Batt
|
||||
v_lipo2=packet_in[9]; //Batt
|
||||
telemetry_link=1; //Send telemetry out
|
||||
pps_counter++;
|
||||
packet_count=0;
|
||||
}
|
||||
#ifdef RLINK_DEBUG_TELEM
|
||||
debugln("");
|
||||
@@ -579,4 +434,4 @@ void RLINK_init()
|
||||
phase = RLINK_DATA;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -19,16 +19,14 @@ Multiprotocol is distributed in the hope that it will be useful,
|
||||
#include "iface_xn297.h"
|
||||
|
||||
//#define FORCE_SGF22_ORIGINAL_ID
|
||||
#define FORCE_SGF22_CX10_ORIGINAL_ID
|
||||
|
||||
#define SGF22_PACKET_PERIOD 11950 //10240
|
||||
#define SGF22_BIND_RF_CHANNEL 78
|
||||
#define SGF22_PAYLOAD_SIZE 12
|
||||
#define SGF22_BIND_COUNT 50
|
||||
#define SGF22_RF_NUM_CHANNELS 4
|
||||
#define SGF22_BIND_RF_CHANNEL 78
|
||||
#define SGF22_F22S_BIND_RF_CHANNEL 10
|
||||
#define SGF22_J20_BIND_RF_CHANNEL 28
|
||||
#define SGF22_CX10_BIND_RF_CHANNEL 48
|
||||
|
||||
//packet[8]
|
||||
#define SGF22_FLAG_3D 0x00
|
||||
@@ -81,43 +79,26 @@ static void __attribute__((unused)) SGF22_send_packet()
|
||||
packet_sent = 0;
|
||||
//packet
|
||||
packet[0] = 0x1B;
|
||||
if (sub_protocol != SGF22_CX10)
|
||||
{//SGF22_F22,SGF22_F22S,SGF22_J20
|
||||
packet[8] = SGF22_FLAG_3D // CH5 -100%, F22 & F22S - 3D mode, J20 - Gyro off
|
||||
| GET_FLAG(CH6_SW, SGF22_FLAG_ROLL) // roll
|
||||
| GET_FLAG(CH7_SW, SGF22_FLAG_LIGHT) // push up throttle trim for light in the stock TX
|
||||
| GET_FLAG(CH9_SW, SGF22_FLAG_VIDEO) // push down throttle trim for video in the stock TX
|
||||
| GET_FLAG(CH11_SW, SGF22_FX922_FLAG_BALANCE)
|
||||
| GET_FLAG(CH12_SW, SGF22_FX922_FLAG_BALANCEHIGH);
|
||||
if(Channel_data[CH5] > CHANNEL_MAX_COMMAND)
|
||||
packet[8] |= SGF22_FLAG_VERTICAL; // CH5 100%, vertical mode (torque)
|
||||
else if(Channel_data[CH5] > CHANNEL_MIN_COMMAND )
|
||||
packet[8] = SGF22_FLAG_3D // CH5 -100%, F22 & F22S - 3D mode, J20 - Gyro off
|
||||
| GET_FLAG(CH6_SW, SGF22_FLAG_ROLL) // roll
|
||||
| GET_FLAG(CH7_SW, SGF22_FLAG_LIGHT) // push up throttle trim for light in the stock TX
|
||||
| GET_FLAG(CH9_SW, SGF22_FLAG_VIDEO) // push down throttle trim for video in the stock TX
|
||||
| GET_FLAG(CH11_SW, SGF22_FX922_FLAG_BALANCE)
|
||||
| GET_FLAG(CH12_SW, SGF22_FX922_FLAG_BALANCEHIGH);
|
||||
if(Channel_data[CH5] > CHANNEL_MAX_COMMAND)
|
||||
packet[8] |= SGF22_FLAG_VERTICAL; // CH5 100%, vertical mode (torque)
|
||||
else if(Channel_data[CH5] > CHANNEL_MIN_COMMAND )
|
||||
packet[8] |= ( sub_protocol == SGF22_J20 ? SGF22_J20_FLAG_HORIZONTAL : SGF22_FLAG_6G ); // CH5 0%, F22 & F22S - 6G mode, J20 - Horizontal mode
|
||||
}
|
||||
else //SGF22_CX10 114548
|
||||
{
|
||||
if(CH6_SW)
|
||||
flags = 0x06; // high rate
|
||||
else
|
||||
if(Channel_data[CH6] < CHANNEL_MIN_COMMAND)
|
||||
flags = 0x04; // low rate
|
||||
else
|
||||
flags = 0x05; // mid rate
|
||||
packet[8] = flags
|
||||
| GET_FLAG(CH5_SW, 0x08); // flip
|
||||
}
|
||||
packet[9] = GET_FLAG(CH8_SW, SGF22_FLAG_PHOTO) // F22: photo, press in throttle trim in the stock TX, J20: invert flight
|
||||
packet[9] = GET_FLAG(CH8_SW, SGF22_FLAG_PHOTO) // F22: photo, press in throttle trim in the stock TX, J20: invert flight
|
||||
| GET_FLAG(CH10_SW, ( sub_protocol == SGF22_J20 ? SGF22_J20_FLAG_FIXHEIGHT : SGF22_FLAG_TRIMRESET )) ; // F22: Both sticks down inwards in the stock TX, J20: Altitude hold
|
||||
packet[10] = 0x42; // no fine tune
|
||||
packet[11] = 0x10; // no fine tune
|
||||
packet[10] = 0x42; // no fine tune
|
||||
packet[11] = 0x10; // no fine tune
|
||||
}
|
||||
if(sub_protocol == SGF22_F22S)
|
||||
packet[0] += 6;
|
||||
else if (sub_protocol == SGF22_J20)
|
||||
packet[0] += 3;
|
||||
else if (sub_protocol == SGF22_CX10)
|
||||
packet[0] += 0x6A;
|
||||
packet[1] = packet_count; // sequence
|
||||
packet[1] = packet_count; // sequence
|
||||
packet[2] = rx_tx_addr[2];
|
||||
packet[3] = rx_tx_addr[3];
|
||||
packet[4] = convert_channel_8b(THROTTLE);
|
||||
@@ -127,10 +108,7 @@ static void __attribute__((unused)) SGF22_send_packet()
|
||||
|
||||
XN297_SetPower();
|
||||
XN297_SetTxRxMode(TX_EN);
|
||||
if (sub_protocol != SGF22_CX10)
|
||||
XN297_WriteEnhancedPayload(packet, SGF22_PAYLOAD_SIZE,0);
|
||||
else
|
||||
XN297_WritePayload(packet, SGF22_PAYLOAD_SIZE);
|
||||
XN297_WriteEnhancedPayload(packet, SGF22_PAYLOAD_SIZE,0);
|
||||
#if 0
|
||||
debug_time("");
|
||||
for(uint8_t i=0; i<SGF22_PAYLOAD_SIZE; i++)
|
||||
@@ -154,7 +132,7 @@ static void __attribute__((unused)) SGF22_initialize_txid()
|
||||
{ 0x18, 0x37, 0x27, 0x47 } };
|
||||
memcpy(hopping_frequency, &hop[val], SGF22_RF_NUM_CHANNELS);
|
||||
|
||||
/*//Same code size...
|
||||
/*//Same code sze...
|
||||
hopping_frequency[0] = 0x0C + 3 * val;
|
||||
hopping_frequency[1] = hopping_frequency[0] + 0x1E;
|
||||
if(val > 1) hopping_frequency[1]++;
|
||||
@@ -167,23 +145,6 @@ static void __attribute__((unused)) SGF22_initialize_txid()
|
||||
rx_tx_addr[3] = 0x61; // TX2:51 TX3:0C
|
||||
memcpy(hopping_frequency,"\x15\x34\x24\x44", SGF22_RF_NUM_CHANNELS); //Original dump=>21=0x15,52=0x34,36=0x24,68=0x44
|
||||
#endif
|
||||
#ifdef FORCE_SGF22_CX10_ORIGINAL_ID
|
||||
if(sub_protocol == SGF22_CX10)
|
||||
{
|
||||
if(rx_tx_addr[3] & 1)
|
||||
{
|
||||
rx_tx_addr[2] = 0x4C;
|
||||
rx_tx_addr[3] = 0xD7;
|
||||
memcpy(hopping_frequency, "\x37\x42\x47\x3c", SGF22_RF_NUM_CHANNELS);
|
||||
}
|
||||
else
|
||||
{
|
||||
rx_tx_addr[2] = 0x50;
|
||||
rx_tx_addr[3] = 0xE1;
|
||||
memcpy(hopping_frequency, "\x3b\x4b\x46\x41", SGF22_RF_NUM_CHANNELS);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if 0
|
||||
debug("ID: %02X %02X, C: ",rx_tx_addr[2],rx_tx_addr[3]);
|
||||
for(uint8_t i=0; i<SGF22_RF_NUM_CHANNELS; i++)
|
||||
@@ -200,7 +161,7 @@ static void __attribute__((unused)) SGF22_RF_init()
|
||||
XN297_SetRXAddr((uint8_t*)"\xC7\x95\x3C\xBB\xA5", SGF22_PAYLOAD_SIZE);
|
||||
#endif
|
||||
|
||||
const uint8_t bind_chan[] = {SGF22_BIND_RF_CHANNEL, SGF22_F22S_BIND_RF_CHANNEL, SGF22_J20_BIND_RF_CHANNEL, SGF22_CX10_BIND_RF_CHANNEL};
|
||||
const uint8_t bind_chan[] = {SGF22_BIND_RF_CHANNEL, SGF22_F22S_BIND_RF_CHANNEL, SGF22_J20_BIND_RF_CHANNEL};
|
||||
XN297_RFChannel(bind_chan[sub_protocol]); // Set bind channel
|
||||
}
|
||||
|
||||
@@ -208,7 +169,6 @@ uint16_t SGF22_callback()
|
||||
{
|
||||
#ifdef SGF22_HUB_TELEMETRY
|
||||
bool rx = false;
|
||||
static uint8_t telem_count = 0;
|
||||
#endif
|
||||
|
||||
switch(phase)
|
||||
@@ -235,8 +195,6 @@ uint16_t SGF22_callback()
|
||||
{//packets: 00 0B 00 -> 00 0B 01
|
||||
telemetry_link = 1;
|
||||
v_lipo1 = packet_in[2] ? 0 : 255; //2.9V for 1S, 7.0V for 2S
|
||||
telemetry_lost = 0;
|
||||
telem_count = 0;
|
||||
}
|
||||
#if 0
|
||||
debug("L %d ",p_len);
|
||||
@@ -246,20 +204,6 @@ uint16_t SGF22_callback()
|
||||
debugln("");
|
||||
#endif
|
||||
}
|
||||
if(telem_count > 4*63) // Around 3.5sec with no telemetry
|
||||
telemetry_lost = 1;
|
||||
else
|
||||
{
|
||||
telem_count++;
|
||||
if(!telemetry_lost && (telem_count & 0x3F) == 0)
|
||||
{// Should have received a telem packet but... Send telem to the radio to keep it alive
|
||||
telemetry_link = 1;
|
||||
#if 0
|
||||
debugln("Miss");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
phase++;
|
||||
break;
|
||||
@@ -301,7 +245,6 @@ void SGF22_init()
|
||||
phase = SGF22_DATA1;
|
||||
#ifdef SGF22_HUB_TELEMETRY
|
||||
RX_RSSI = 100; // Dummy value
|
||||
telemetry_lost = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -404,7 +404,7 @@ RX: 0x02 0x4A 0xA3 0x2D 0x1A 0x49 0xFE 0x06 0x00 0x00 0x02 0x01 0x06 0x06 0x00 0
|
||||
TX: 0x02 0x65 0xE2 0x5E 0x55 0x4D 0xFE 0xEE 0x00 0x00 0x01 0x01 0x06 0x05 0x00 0x00
|
||||
Notes:
|
||||
- RX cyrfmfg_id is 0x4A,0xA3,0x2D,0x1A,0x49,0xFE and TX cyrfmfg_id is 0x65,0xE2,0x5E,0x55,0x4D,0xFE
|
||||
- P[7] = RX SOP code index, changes from 0x06 to 0xEE but not needed to complete the bind -> doesn't care??
|
||||
- P[7] changes from 0x06 to 0xEE but not needed to complete the bind -> doesn't care??
|
||||
- P[8] RF channel - 1 (on packets type 0x03)
|
||||
- P[9] 0x00 unchanged??
|
||||
- P[10] needs to be set to 0x01 to complete the bind -> normal packet P[0]??
|
||||
@@ -474,123 +474,4 @@ Traxxas TQ 1st generation
|
||||
-------------------------
|
||||
https://github.com/pascallanger/DIY-Multiprotocol-TX-Module/issues/967#issuecomment-2079038576
|
||||
*/
|
||||
/*
|
||||
Traxxas TQi
|
||||
-------------------------
|
||||
Unknown 3 first packets:
|
||||
CHANNEL: 0x2B
|
||||
SOP_CODE: 0x3C 0x37 0xCC 0x91 0xE2 0xF8 0xCC 0x91
|
||||
CRC_SEED_LSB: 0x5A
|
||||
CRC_SEED_MSB: 0x5A
|
||||
RX: 0xA0 0x1F 0x60 0x58 0x34 0x44 0x1C 0x11 0xA5 0x72 0x8D 0x9D 0xEA 0x49 0x2B 0xE2
|
||||
RX: 0xD3 0x53 0xD4 0x45 0x1B 0x6F 0x75 0x42 0xEF 0x8A 0xD2 0x24 0x8E 0xF0 0x05 0x6C
|
||||
RX: 0xAA 0xD7 0xD7 0x43 0x0F 0x2F 0x02 0x8F 0x06 0x54 0x3A 0x35 0xF5 0xCF 0x0F 0x82
|
||||
|
||||
Packets 0x02: Bind learn TX/RX addresses same as TQ 2nd:
|
||||
CHANNEL: 0x2B
|
||||
SOP_CODE: 0x3C 0x37 0xCC 0x91 0xE2 0xF8 0xCC 0x91
|
||||
CRC_SEED_LSB: 0x5A
|
||||
CRC_SEED_MSB: 0x5A
|
||||
RX: 0x02 0x5B 0xE9 0x7E 0x14 0xF1 0xFE 0x0D 0x00 0x00 0x02 0x02 0x00 0x26 0x00 0x00
|
||||
TX: 0x02 0xE0 0xAC 0x7F 0x14 0xF1 0xFE 0x00 0x00 0x00 0x01 0x02 0x00 0x51 0x00 0x00
|
||||
|
||||
Notes:
|
||||
- P[1..6] = RX cyrfmfg_id is 0x5B,0xE9,0x7E,0x14,0xF1,0xFE and TX cyrfmfg_id is 0xE0,0xAC,0x7F,0x14,0xF1,0xFE
|
||||
- P[7] RX SOP code index, changes from 0x0D to 0x00 (but not needed to complete the bind -> doesn't care??)
|
||||
- P[8] RF channel - 1 (on packets type 0x03)
|
||||
- P[9] 0x00 unchanged??
|
||||
- P[10] needs to be set to 0x01 to complete the bind -> normal packet P[0]??
|
||||
- P[11] unchanged ?? (-> no bind if set to 0x00 or 0x81)
|
||||
- P[12] unchanged ?? (-> no bind if set to 0x05 or 0x86)
|
||||
- P[13] changes from 0x26 to 0x51 (but not needed to complete the bind -> doesn't care??)
|
||||
- P[14..15]=0x00 unchanged??
|
||||
|
||||
Next steps:
|
||||
Packets 0x08: ???
|
||||
TX: 0x08 0x00 0x03 0x14 0xFD 0x3E 0x28 0xB9 0xCA 0x48 0x76 0x4E 0x00 0x51 0x00 0x00
|
||||
TX: 0x08 0x00 0x02 0x14 0xFD 0x3E 0x28 0xB9 0xCA 0x48 0x76 0x4E 0x00 0x51 0x00 0x00
|
||||
TX: 0x08 0x00 0x01 0x14 0xFD 0x3E 0x28 0xB9 0xCA 0x48 0x76 0x4E 0x00 0x51 0x00 0x00
|
||||
RX: 0x08 0x00 0x03 0x14 0xFD 0x24 0x20 0xB8 0xCA 0x48 0x76 0x4E 0x00 0x26 0x00 0x00
|
||||
RX: 0x08 0x00 0x01 0x14 0xFD 0x24 0x20 0xB8 0xCA 0x48 0x76 0x4E 0x00 0x26 0x00 0x00
|
||||
P[1] index
|
||||
P[2] sequence number
|
||||
|
||||
Packets 0x09: ???
|
||||
TX: 0x09 0x00 0x03 0xEE 0x12 0x49 0x57 0x1F 0x4B 0x60 0x6B 0x4D 0x14 0xEF 0x69 0xC4
|
||||
TX: 0x09 0x00 0x02 0xEE 0x12 0x49 0x57 0x1F 0x4B 0x60 0x6B 0x4D 0x14 0xEF 0x69 0xC4
|
||||
TX: 0x09 0x00 0x01 0xEE 0x12 0x49 0x57 0x1F 0x4B 0x60 0x6B 0x4D 0x14 0xEF 0x69 0xC4
|
||||
TX: 0x09 0x01 0x03 0xB2 0x63 0x43 0x25 0x22 0x4D 0xA1 0x15 0xFE 0xBA 0x7D 0x67 0x3E
|
||||
TX: 0x09 0x01 0x02 0xB2 0x63 0x43 0x25 0x22 0x4D 0xA1 0x15 0xFE 0xBA 0x7D 0x67 0x3E
|
||||
TX: 0x09 0x01 0x01 0xB2 0x63 0x43 0x25 0x22 0x4D 0xA1 0x15 0xFE 0xBA 0x7D 0x67 0x3E
|
||||
TX: 0x09 0x02 0x03 0xCB 0xBE 0xA6 0xD1 0xCD 0x23 0xA1 0x15 0xFE 0xBA 0x7D 0x67 0x3E
|
||||
TX: 0x09 0x02 0x02 0xCB 0xBE 0xA6 0xD1 0xCD 0x23 0xA1 0x15 0xFE 0xBA 0x7D 0x67 0x3E
|
||||
TX: 0x09 0x02 0x01 0xCB 0xBE 0xA6 0xD1 0xCD 0x23 0xA1 0x15 0xFE 0xBA 0x7D 0x67 0x3E
|
||||
RX: 0x09 0x00 0x03 0x90 0x24 0x93 0x08 0x41 0xC7 0x7F 0x59 0xB4 0xE3 0x25 0x77 0xB5
|
||||
RX: 0x09 0x00 0x01 0x90 0x24 0x93 0x08 0x41 0xC7 0x7F 0x59 0xB4 0xE3 0x25 0x77 0xB5
|
||||
RX: 0x09 0x01 0x02 0x88 0xFA 0x4D 0xFB 0x33 0xD4 0xFE 0x5F 0x1C 0xA9 0x0B 0x7A 0xE6
|
||||
RX: 0x09 0x02 0x03 0x86 0xAF 0xD0 0x91 0xF6 0xDD 0xFE 0x5F 0x1C 0xA9 0x0B 0x7A 0xE6
|
||||
RX: 0x09 0x02 0x01 0x86 0xAF 0xD0 0x91 0xF6 0xDD 0xFE 0x5F 0x1C 0xA9 0x0B 0x7A 0xE6
|
||||
P[1] index
|
||||
P[2] sequence number
|
||||
|
||||
Switch to normal mode:
|
||||
SOP_CODE: 0xD2 0x8F 0xB1 0x2A 0xEF 0x64 0xB0 0x2A
|
||||
CRC_SEED_LSB: 0x85
|
||||
CRC_SEED_MSB: 0xC3
|
||||
CHANNEL: 0x05
|
||||
TX: 0x01 0x00 0x02 0xF5 0x02 0x9E 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x99 0x67 0x3E
|
||||
Normal content except P[14]&P[15] = 0x67&0x3E from previous packet sent
|
||||
|
||||
SOP_CODE: 0x97 0xE5 0x14 0x72 0x7F 0x1A 0x14 0x72
|
||||
CRC_SEED_LSB: 0xA5
|
||||
CRC_SEED_MSB: 0xA5
|
||||
CHANNEL: 0x22
|
||||
RX: 0x04 0x5B 0xE9 0x7E 0x14 0xF1 0xFE 0x0D 0xDD 0xFE 0x02 0x02 0x00 0x26 0x7A 0xE6
|
||||
RX packets 0x04: RX identity ?
|
||||
P[1..6] = RX cyrfmfg_id 0x5B,0xE9,0x7E,0x14,0xF1,0xFE
|
||||
P[7] = SOP index
|
||||
P[8] = unknown
|
||||
P[9] = unknown
|
||||
P[10] = same value as bind 0x02
|
||||
P[11] = same value as bind 0x02
|
||||
P[12] = same value as bind 0x00
|
||||
P[13] = same value as bind 0x26
|
||||
P[14] = unknown
|
||||
P[15] = unknown
|
||||
|
||||
SOP_CODE: 0xD2 0x8F 0xB1 0x2A 0xEF 0x64 0xB0 0x2A
|
||||
CRC_SEED_LSB: 0x85
|
||||
CRC_SEED_MSB: 0xC3
|
||||
CHANNEL: 0x05
|
||||
TX: 0x14 0x00 0x03 0xF5 0x02 0x9E 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x99 0x67 0x3E
|
||||
TX: 0x14 0x00 0x02 0xF5 0x02 0x9E 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x99 0x67 0x3E
|
||||
TX: 0x14 0x00 0x01 0xF5 0x02 0x9E 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x99 0x67 0x3E
|
||||
RX: 0x14 0x00 0x03 0x22 0xEC 0x42 0x9B 0xD7 0x99 0x3B 0xBB 0x85 0x17 0x41 0x67 0xA0
|
||||
RX: 0x14 0x00 0x01 0x22 0xEC 0x42 0x9B 0xD7 0x99 0x3B 0xBB 0x85 0x17 0x41 0x67 0xA0
|
||||
RX: 0x14 0x01 0x02 0x22 0xEF 0x42 0x9C 0xCA 0xFB 0x39 0xBB 0x85 0x17 0x41 0x67 0xA0
|
||||
TX packets 0x14 same as packets 0x01 with a sequence number on P[2]
|
||||
RX packets 0x14 unknown content
|
||||
|
||||
Packets 0x01 & 0x07: normal and telemetry request/answer
|
||||
TX: 0x01 0x00 0x02 0xF5 0x02 0x9E 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x99 0x00 0x00
|
||||
TX: 0x07 0x00 0x02 0xF5 0x02 0x9E 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x99 0x00 0x00
|
||||
RX: 0x07 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0xFB 0x00 0x00 0x85 0x17 0x41 0x67 0xA0
|
||||
TX: 0x01 0x00 0x02 0xF5 0x02 0x9E 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x99 0x00 0x00
|
||||
TX: 0x07 0x00 0x02 0xF5 0x02 0x9E 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x99 0x00 0x00
|
||||
RX: 0x07 0x01 0x00 0x00 0x00 0x11 0x49 0xC8 0xFB 0x00 0x00 0x85 0x17 0x41 0x67 0xA0
|
||||
TX: 0x01 0x00 0x02 0xF5 0x02 0x9E 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x99 0x00 0x00
|
||||
TX: 0x07 0x00 0x02 0xF5 0x02 0x9E 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x99 0x00 0x00
|
||||
RX: 0x07 0x01 0x00 0x00 0x00 0x12 0xDC 0x20 0xFB 0x00 0x00 0x85 0x17 0x41 0x67 0xA0
|
||||
TX packets 0x07: request for telemetry
|
||||
RX packets 0x07: unknown
|
||||
|
||||
CRC calc same as TQ 2nd:
|
||||
TX ID: 0xE0,0xAC,0x7F,0x14,0xF1,0xFE
|
||||
RX ID: 0x5B,0xE9,0x7E,0x14,0xF1,0xFE CRC 0x85 0xC3 => CRC: E0-5B=85 AC-E9=C3
|
||||
SOP index same as TQ 2nd:
|
||||
SOP index=0x0D, SOP code= 0xD2, 0x8F, 0xB1, 0x2A, 0xEF, 0x64, 0xB0, 0x2A
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -315,15 +315,6 @@ static void multi_send_status()
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef RLINK_HUB_TELEMETRY
|
||||
void RLINK_raw_frame()
|
||||
{
|
||||
multi_send_header(MULTI_TELEMETRY_RLINK, packet_in[0]);
|
||||
for (uint8_t i = 1; i <= packet_in[0]; i++) // raw DumboRC reply payload
|
||||
Serial_write(packet_in[i]);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void multi_send_frskyhub()
|
||||
{
|
||||
multi_send_header(MULTI_TELEMETRY_HUB, 9);
|
||||
@@ -896,7 +887,7 @@ void TelemetryUpdate()
|
||||
t += TXBUFFER_SIZE - h ;
|
||||
else
|
||||
t -= h ;
|
||||
if ( t < 48 ) //32 )
|
||||
if ( t < 32 )
|
||||
{
|
||||
debugln("TEL_BUF_FULL %d",t);
|
||||
return ;
|
||||
@@ -1001,14 +992,6 @@ void TelemetryUpdate()
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if defined RLINK_HUB_TELEMETRY
|
||||
if(telemetry_link == 2 && protocol == PROTO_RLINK)
|
||||
{
|
||||
RLINK_raw_frame();
|
||||
telemetry_link=0;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if defined SCANNER_TELEMETRY
|
||||
if (telemetry_link && protocol == PROTO_SCANNER)
|
||||
{
|
||||
|
||||
@@ -244,7 +244,7 @@ uint16_t V761_callback()
|
||||
else
|
||||
{
|
||||
packet_count++;
|
||||
if(!telemetry_lost && !rx && (packet_count & 0x3F) == 0)
|
||||
if(!telemetry_lost && !rx && (packet_count%64) == 0)
|
||||
{// Should have received a telem packet but... Send telem to the radio to keep it alive
|
||||
telemetry_link = 1;
|
||||
#ifdef V761_TELEM_DEBUG
|
||||
|
||||
@@ -341,7 +341,6 @@
|
||||
#undef SYMAX_NRF24L01_INO
|
||||
#undef V2X2_NRF24L01_INO
|
||||
#undef V761_NRF24L01_INO
|
||||
#undef WPL_NRF24L01_INO
|
||||
#undef XERALL_NRF24L01_INO
|
||||
#undef YD717_NRF24L01_INO
|
||||
#undef YUXIANG_NRF24L01_INO
|
||||
@@ -400,7 +399,6 @@
|
||||
#undef UDIRC_CCNRF_INO
|
||||
#undef KAMTOM_NRF24L01_INO
|
||||
#undef WL91X_CCNRF_INO
|
||||
#undef WPL_NRF24L01_INO
|
||||
//Save flash space...
|
||||
#undef CABELL_NRF24L01_INO
|
||||
#undef REDPINE_CC2500_INO
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
/*
|
||||
This project 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.
|
||||
|
||||
Multiprotocol 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 Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// Compatible with WPL "Basic" TX models D12, D12KM, D22, D32, D42, D14
|
||||
|
||||
#if defined(WPL_NRF24L01_INO)
|
||||
|
||||
#include "iface_xn297.h"
|
||||
|
||||
#define FORCE_WPL_ORIGINAL_ID
|
||||
|
||||
#define WPL_PACKET_PERIOD 9875
|
||||
#define WPL_RF_NUM_CHANNELS 4
|
||||
#define WPL_PAYLOAD_SIZE 16
|
||||
#define WPL_BIND_COUNT 303 //3sec
|
||||
|
||||
static void __attribute__((unused)) WPL_send_packet()
|
||||
{
|
||||
#if 0
|
||||
debug("no:%d, rf:%d, ",hopping_frequency_no + (IS_BIND_IN_PROGRESS?0:4),hopping_frequency[hopping_frequency_no + (IS_BIND_IN_PROGRESS?0:4)]);
|
||||
#endif
|
||||
XN297_Hopping(hopping_frequency_no + (IS_BIND_IN_PROGRESS?0:4) );
|
||||
hopping_frequency_no++;
|
||||
hopping_frequency_no &= WPL_RF_NUM_CHANNELS-1; // 4 RF channels
|
||||
|
||||
memset(&packet[8],0,7);
|
||||
packet[0] = 0x94; //??
|
||||
packet[1] = 0x16; //??
|
||||
packet[2] = 0xCC; //??
|
||||
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
memcpy(&packet[3],rx_tx_addr,5);
|
||||
packet[9] = 0x08; // ?? Not bound + Headlights on
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[3 ] = convert_channel_s8b(CH1); // Throttle
|
||||
packet[4 ] = convert_channel_s8b(CH2); // Steering
|
||||
packet[5 ] = convert_channel_16b_limit(CH3,0x22,0x5E); // Steering trim
|
||||
packet[6 ] = rx_tx_addr[3]; // 0x32??
|
||||
packet[7 ] = convert_channel_s8b(CH4); // Aux
|
||||
packet[9 ] = 0x80 // ?? Bound
|
||||
| GET_FLAG(CH5_SW, 0x08) // Headlights 100%=on
|
||||
| GET_FLAG(CH6_SW, 0x04) // Throttle rate 100%=high
|
||||
| GET_FLAG(CH7_SW, 0x02); // Steering rate 100%=high
|
||||
}
|
||||
uint8_t sum = 0x66;
|
||||
for(uint8_t i=0;i<WPL_PAYLOAD_SIZE-1;i++)
|
||||
sum += packet[i];
|
||||
packet[WPL_PAYLOAD_SIZE-1] = sum;
|
||||
// Send
|
||||
XN297_SetPower();
|
||||
XN297_SetTxRxMode(TX_EN);
|
||||
XN297_WritePayload(packet, WPL_PAYLOAD_SIZE);
|
||||
#if 0
|
||||
for(uint8_t i=0; i<WPL_PAYLOAD_SIZE; i++)
|
||||
debug(" %02X",packet[i]);
|
||||
debugln("");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) WPL_RF_init()
|
||||
{
|
||||
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
|
||||
XN297_SetTXAddr((uint8_t*)"\x69\xA5\x37\x4D\x8B", 5);
|
||||
XN297_HoppingCalib(WPL_RF_NUM_CHANNELS*2); // Calibrate bind and normal channels
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) WPL_initialize_txid()
|
||||
{
|
||||
//Bind frequencies
|
||||
memcpy(hopping_frequency ,"\x17\x25\x46\x36", WPL_RF_NUM_CHANNELS); //23=17, 37=25, 70=46, 54=36
|
||||
#ifdef FORCE_WPL_ORIGINAL_ID
|
||||
//Original ID
|
||||
memcpy(rx_tx_addr,"\x96\x2A\xA9\x32\xB4",5);
|
||||
//Normal frequencies
|
||||
memcpy(hopping_frequency+4,"\x0C\x2A\x3D\x1D", WPL_RF_NUM_CHANNELS); //12=0C, 42=2A, 61=3D, 29=1D
|
||||
#endif
|
||||
}
|
||||
|
||||
uint16_t WPL_callback()
|
||||
{
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(WPL_PACKET_PERIOD);
|
||||
#endif
|
||||
if(bind_counter)
|
||||
if(--bind_counter==0)
|
||||
{
|
||||
BIND_DONE;
|
||||
XN297_SetTXAddr(rx_tx_addr, 5);
|
||||
}
|
||||
WPL_send_packet();
|
||||
return WPL_PACKET_PERIOD;
|
||||
}
|
||||
|
||||
void WPL_init()
|
||||
{
|
||||
BIND_IN_PROGRESS; // autobind protocol
|
||||
WPL_initialize_txid();
|
||||
WPL_RF_init();
|
||||
hopping_frequency_no = 0;
|
||||
bind_counter=WPL_BIND_COUNT;
|
||||
}
|
||||
|
||||
#endif
|
||||
/* https://github.com/pascallanger/DIY-Multiprotocol-TX-Module/issues/1120
|
||||
Bind packet
|
||||
-----------
|
||||
XN297 1Mb Scrambled
|
||||
Bind address: 69 A5 37 4D 8B
|
||||
RF channels: 23, 37, 70, 54
|
||||
Timing: 9875µs
|
||||
Payload 16 bytes: 94 16 CC 96 2A A9 32 B4 00 08 00 00 00 00 00 33
|
||||
|
||||
P[0] = 94 ??
|
||||
P[1] = 16 ??
|
||||
P[2] = CC ??
|
||||
P[3..7] = Normal address
|
||||
P[8] = 00 ??
|
||||
P[9] = 08 ?? not bound?, Throttle and Steering rate low, Headlights on
|
||||
P[10..14] = 00 ??
|
||||
P[15] = sum(P[0..14])+66 why 66? 66=(94+16)^CC...
|
||||
|
||||
Normal packet
|
||||
-----------
|
||||
XN297 1Mb Scrambled
|
||||
Normal address: 96 2A A9 32 B4
|
||||
RF channels: 12=0C, 42=2A, 61=3D, 29=1D -> no idea where they come from...
|
||||
Timing: 9875µs
|
||||
Payload 16 bytes: 94 16 CC 80 80 38 32 80 00 88 00 00 00 00 00 4E
|
||||
P[0] = 94 ??
|
||||
P[1] = 16 ??
|
||||
P[2] = CC ??
|
||||
P[3] = Throttle, not enough data on dumps... Same coding as Steering?
|
||||
P[4] = Steering, not enough data on dumps, looks like one side goes from 7F to 00 and the other 80 to FF which would be s8b
|
||||
P[5] = Steering trim 22..5E, mid gives 40 not 38... Was the trim centered on the other dumps with value 38?
|
||||
P[6] = 32 ?? Left over from the bind packet TX_ADDR[3]?
|
||||
P[7] = 80 ?? Additional channel? It moves at the same time as the trim but my guess is that it is an unconnected channel.
|
||||
P[8] = 00 ??
|
||||
P[9] = 80 ?? bound?, Throttle and Steering rate low, Headlights off
|
||||
|02 -> Steering rate high
|
||||
|04 -> Throttle rate high
|
||||
|08 -> Headlights on
|
||||
P[10..14] = 00 ??
|
||||
P[15] = sum(P[0..14])+66 why 66? 66=(94+16)^CC...
|
||||
*/
|
||||
@@ -86,8 +86,7 @@ static void __attribute__((unused)) XK2_send_packet()
|
||||
packet[5] |= 0x10; //Gyro off (senior mode)
|
||||
else if(Channel_data[CH6] > CHANNEL_MIN_COMMAND)
|
||||
packet[5] |= 0x08; //3D
|
||||
packet[5] |= GET_FLAG(CH9_SW, 0x04); //Sky Viper Vector stunt flag
|
||||
//Request telemetry flag
|
||||
//Requiest telemetry flag
|
||||
packet[6] = 0x01;
|
||||
//RXID checksum
|
||||
packet[7] = crc8; //Sum RX_ID[0..2]
|
||||
|
||||
@@ -22,9 +22,7 @@
|
||||
#include "iface_xn297.h"
|
||||
|
||||
// Parameters which can be modified
|
||||
#define XN297DUMP_PERIOD_FAST_SCAN 50000 // 50000
|
||||
#define XN297DUMP_PERIOD_SLOW_SCAN 100000 // 75000
|
||||
#define XN297DUMP_MAX_PACKET 50 // 20
|
||||
#define XN297DUMP_PERIOD_SCAN 50000 // 25000
|
||||
#define XN297DUMP_MAX_RF_CHANNEL 84 // Default 84
|
||||
|
||||
// Do not touch from there
|
||||
@@ -40,7 +38,6 @@ boolean ack;
|
||||
uint8_t pid;
|
||||
uint8_t bitrate;
|
||||
uint8_t old_option;
|
||||
uint32_t scan_counter;
|
||||
|
||||
static void __attribute__((unused)) XN297Dump_RF_init()
|
||||
{
|
||||
@@ -198,10 +195,10 @@ static uint16_t XN297Dump_callback()
|
||||
{
|
||||
if(sub_protocol<XN297DUMP_AUTO)
|
||||
{
|
||||
if(option==0xFF && scan_counter>XN297DUMP_PERIOD_SLOW_SCAN)
|
||||
if(option==0xFF && bind_counter>XN297DUMP_PERIOD_SCAN)
|
||||
{ // Scan frequencies
|
||||
hopping_frequency_no++;
|
||||
scan_counter=0;
|
||||
bind_counter=0;
|
||||
}
|
||||
if(hopping_frequency_no!=rf_ch_num)
|
||||
{ // Channel has changed
|
||||
@@ -297,10 +294,10 @@ static uint16_t XN297Dump_callback()
|
||||
phase++;
|
||||
break;
|
||||
case 1:
|
||||
if(scan_counter>XN297DUMP_PERIOD_FAST_SCAN)
|
||||
if(bind_counter>XN297DUMP_PERIOD_SCAN)
|
||||
{ // Scan frequencies
|
||||
hopping_frequency_no++;
|
||||
scan_counter=0;
|
||||
bind_counter=0;
|
||||
if(hopping_frequency_no>XN297DUMP_MAX_RF_CHANNEL)
|
||||
{
|
||||
hopping_frequency_no=0;
|
||||
@@ -370,7 +367,7 @@ static uint16_t XN297Dump_callback()
|
||||
debugln("\r\n--------------------------------");
|
||||
phase=2;
|
||||
debugln("Identifying all RF channels in use.");
|
||||
scan_counter=0;
|
||||
bind_counter=0;
|
||||
hopping_frequency_no=0;
|
||||
rf_ch_num=0;
|
||||
packet_count=0;
|
||||
@@ -392,11 +389,11 @@ static uint16_t XN297Dump_callback()
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if(scan_counter>XN297DUMP_PERIOD_SLOW_SCAN)
|
||||
if(bind_counter>XN297DUMP_PERIOD_SCAN)
|
||||
{ // Scan frequencies
|
||||
hopping_frequency_no++;
|
||||
scan_counter=0;
|
||||
if(packet_count && packet_count<=XN297DUMP_MAX_PACKET)
|
||||
bind_counter=0;
|
||||
if(packet_count && packet_count<=20)
|
||||
debug("\r\nTrying RF channel: ");
|
||||
packet_count=0;
|
||||
if(hopping_frequency_no>XN297DUMP_MAX_RF_CHANNEL)
|
||||
@@ -441,7 +438,7 @@ static uint16_t XN297Dump_callback()
|
||||
hopping_frequency_no=0;
|
||||
phase=3;
|
||||
packet_count=0;
|
||||
scan_counter=0;
|
||||
bind_counter=0;
|
||||
debugln("Time between CH:%d and CH:%d",hopping_frequency[compare_channel],hopping_frequency[hopping_frequency_no]);
|
||||
time_rf[hopping_frequency_no]=0xFFFFFFFF;
|
||||
XN297_RFChannel(hopping_frequency[compare_channel]);
|
||||
@@ -498,9 +495,9 @@ static uint16_t XN297Dump_callback()
|
||||
debug(" %02X",packet[i]);
|
||||
packet_count++;
|
||||
nbr_rf[rf_ch_num-1]=packet_count;
|
||||
if(packet_count>XN297DUMP_MAX_PACKET)
|
||||
if(packet_count>20)
|
||||
{//change channel
|
||||
scan_counter=XN297DUMP_PERIOD_SLOW_SCAN+1;
|
||||
bind_counter=XN297DUMP_PERIOD_SCAN+1;
|
||||
debug("\r\nTrying RF channel: ");
|
||||
}
|
||||
}
|
||||
@@ -512,10 +509,10 @@ static uint16_t XN297Dump_callback()
|
||||
XN297Dump_overflow();
|
||||
break;
|
||||
case 3:
|
||||
if(scan_counter>XN297DUMP_PERIOD_SLOW_SCAN)
|
||||
if(bind_counter>XN297DUMP_PERIOD_SCAN)
|
||||
{ // Scan frequencies
|
||||
hopping_frequency_no++;
|
||||
scan_counter=0;
|
||||
bind_counter=0;
|
||||
if(hopping_frequency_no>=rf_ch_num)
|
||||
{
|
||||
uint8_t next=0;
|
||||
@@ -599,7 +596,7 @@ static uint16_t XN297Dump_callback()
|
||||
packet_count++;
|
||||
if(packet_count>24)
|
||||
{
|
||||
scan_counter=XN297DUMP_PERIOD_SLOW_SCAN+1;
|
||||
bind_counter=XN297DUMP_PERIOD_SCAN+1;
|
||||
packet_count=0;
|
||||
}
|
||||
}
|
||||
@@ -944,7 +941,7 @@ static uint16_t XN297Dump_callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
scan_counter++;
|
||||
bind_counter++;
|
||||
if(IS_RX_FLAG_on) // Let the radio update the protocol
|
||||
{
|
||||
if(Update_All()) return 10000; // New protocol selected
|
||||
@@ -970,7 +967,7 @@ void XN297Dump_init(void)
|
||||
if(address_length<3||address_length>5)
|
||||
address_length=5; //default
|
||||
XN297Dump_RF_init();
|
||||
scan_counter=0;
|
||||
bind_counter=0;
|
||||
rf_ch_num=0xFF;
|
||||
prev_option=option^0x55;
|
||||
phase=0; // init
|
||||
|
||||
@@ -261,7 +261,6 @@
|
||||
#define SYMAX_NRF24L01_INO
|
||||
#define V2X2_NRF24L01_INO
|
||||
#define V761_NRF24L01_INO
|
||||
#define WPL_NRF24L01_INO
|
||||
#define XERALL_NRF24L01_INO
|
||||
#define YD717_NRF24L01_INO
|
||||
#define YUXIANG_NRF24L01_INO
|
||||
@@ -654,8 +653,7 @@ const PPM_Parameters PPM_prot[14*NBR_BANKS]= {
|
||||
V912
|
||||
CX20
|
||||
PROTO_FQ777
|
||||
FQ777
|
||||
XBM37
|
||||
NONE
|
||||
PROTO_FRSKY_RX
|
||||
FRSKY_RX
|
||||
FRSKY_CLONE
|
||||
@@ -698,10 +696,8 @@ const PPM_Parameters PPM_prot[14*NBR_BANKS]= {
|
||||
FX816
|
||||
FX620
|
||||
FX9630
|
||||
FX_Q560
|
||||
FX_QF012
|
||||
FX_BM26
|
||||
FX_A570
|
||||
Q560
|
||||
QF012
|
||||
PROTO_FY326
|
||||
FY326
|
||||
FY319
|
||||
@@ -830,7 +826,6 @@ const PPM_Parameters PPM_prot[14*NBR_BANKS]= {
|
||||
RLINK_AIR
|
||||
RLINK_DUMBORC
|
||||
RLINK_RC4G
|
||||
RLINK_DUMBORC_P
|
||||
PROTO_SCANNER
|
||||
NONE
|
||||
PROTO_SCORPIO
|
||||
@@ -839,7 +834,6 @@ const PPM_Parameters PPM_prot[14*NBR_BANKS]= {
|
||||
SGF22
|
||||
F22S
|
||||
J20
|
||||
CX10
|
||||
PROTO_SHENQI
|
||||
NONE
|
||||
PROTO_SHENQI2
|
||||
@@ -882,8 +876,6 @@ const PPM_Parameters PPM_prot[14*NBR_BANKS]= {
|
||||
W6_6_1
|
||||
W6_HEL
|
||||
W6_HEL_I
|
||||
PROTO_WPL
|
||||
NONE
|
||||
PROTO_XERALL
|
||||
NONE
|
||||
PROTO_XK
|
||||
|
||||
@@ -90,7 +90,7 @@ CFlie|AIR|38|CFlie||||||||NRF24L01|
|
||||
[Flysky](Protocols_Details.md#FLYSKY---1)||1|Flysky|V9x9|V6x6|V912|CX20||||A7105|
|
||||
[Flysky AFHDS2A](Protocols_Details.md#FLYSKY-AFHDS2A---28)||28|PWM_IBUS|PPM_IBUS|PWM_SBUS|PPM_SBUS|Gyro_Off|Gyro_On|Gyro_On_Rev||A7105|
|
||||
[Flysky AFHDS2A RX](Protocols_Details.md#FLYSKY-AFHDS2A-RX---56)||56|Multi|CPPM|||||||A7105|
|
||||
[FQ777](Protocols_Details.md#FQ777---23)||23|124|XBM37|||||||NRF24L01|SSV7241
|
||||
[FQ777](Protocols_Details.md#FQ777---23)||23|||||||||NRF24L01|SSV7241
|
||||
[FrskyD](Protocols_Details.md#FRSKYD---3)||3|D8|Cloned|||||||CC2500|
|
||||
[FrskyL](Protocols_Details.md#FRSKYL---67)||67|LR12|LR12 6CH|||||||CC2500|
|
||||
[FrskyR9](Protocols_Details.md#FRSKYR9---65)||65|FrskyR9|R9_915|R9_868||||||SX1276|
|
||||
@@ -99,7 +99,7 @@ CFlie|AIR|38|CFlie||||||||NRF24L01|
|
||||
[FrskyX2](Protocols_Details.md#FRSKYX2---64)||64|CH_16|CH_8|EU_16|EU_8|Cloned|Cloned_8|||CC2500|
|
||||
[Frsky_RX](Protocols_Details.md#FRSKY_RX---55)||55|Multi|CloneTX|EraseTX|CPPM|||||CC2500|
|
||||
[Futaba/SFHSS](Protocols_Details.md#Futaba---21)||21|SFHSS||||||||CC2500|
|
||||
[FX](Protocols_Details.md#FX---58)||58|FX816|FX620|9630|Q560|QF012|BM26|A570||NRF24L01|
|
||||
[FX](Protocols_Details.md#FX---58)||28|816|620|9630|Q560|QF012||||NRF24L01|
|
||||
[FY326](Protocols_Details.md#FY326---20)||20|FY326|FY319|||||||NRF24L01|
|
||||
[GD00X](Protocols_Details.md#GD00X---47)||47|GD_V1*|GD_V2*|||||||NRF24L01|XN297L
|
||||
[GW008](Protocols_Details.md#GW008---32)||32|||||||||NRF24L01|XN297
|
||||
@@ -137,12 +137,12 @@ CFlie|AIR|38|CFlie||||||||NRF24L01|
|
||||
[Q2X2](Protocols_Details.md#Q2X2---29)||29|Q222|Q242|Q282||||||NRF24L01|
|
||||
[Q303](Protocols_Details.md#Q303---31)||31|Q303|CX35|CX10D|CX10WD|||||NRF24L01|XN297
|
||||
[Q90C](Protocols_Details.md#Q90C---72)||72|Q90C*||||||||NRF24L01|XN297
|
||||
[RadioLink](Protocols_Details.md#RadioLink---74)||74|Surface|Air|DumboRC|RC4G|Dumbo_P||||CC2500|
|
||||
[RadioLink](Protocols_Details.md#RadioLink---74)||74|Surface|Air|DumboRC|RC4G|||||CC2500|
|
||||
[Realacc](Protocols_Details.md#Realacc---76)||76|R11||||||||NRF24L01|
|
||||
[Redpine](Protocols_Details.md#Redpine---50)||50|FAST|SLOW|||||||NRF24L01|XN297
|
||||
[Scanner](Protocols_Details.md#Scanner---54)||54|||||||||CC2500|
|
||||
[Scorpio](Protocols_Details.md#Scorpio---94)||94|||||||||CYRF6936|
|
||||
[SGF22](Protocols_Details.md#SGF22---97)||97|F22|F22S|J20|CX10|||||NRF24L01|XN297
|
||||
[SGF22](Protocols_Details.md#SGF22---97)||97|F22|F22S|J20||||||NRF24L01|XN297
|
||||
[Shenqi](Protocols_Details.md#Shenqi---19)||19|Shenqi||||||||NRF24L01|LT8900
|
||||
[Shenqi2](Protocols_Details.md#Shenqi2---105)||105|Shenqi2||||||||NRF24L01|XN297
|
||||
[Skyartec](Protocols_Details.md#Skyartec---68)||68|||||||||CC2500|CC2500
|
||||
@@ -155,10 +155,9 @@ CFlie|AIR|38|CFlie||||||||NRF24L01|
|
||||
[WFLY](Protocols_Details.md#WFLY---40)||40|WFR0x||||||||CYRF6936|
|
||||
[WFLY2](Protocols_Details.md#WFLY2---79)||79|RF20x||||||||A7105|
|
||||
[WK2x01](Protocols_Details.md#WK2X01---30)||30|WK2801|WK2401|W6_5_1|W6_6_1|W6_HEL|W6_HEL_I|||CYRF6936|
|
||||
[WL91X](Protocols_Details.md#WL91X---106)||106|||||||||NRF24L01&CC2500|XN297
|
||||
[WPL](Protocols_Details.md#WPL---107)||107|||||||||NRF24L01|XN297
|
||||
[WL91X](Protocols_Details.md#WL91X---106)||106|||||||||NRF24L011&CC2500|XN297
|
||||
[XERALL](Protocols_Details.md#XERALL---91)||91|Tank||||||||NRF24L01|XN297
|
||||
[XK](Protocols_Details.md#XK---62)||62|X450|X420|Cars||||||NRF24L01&CC2500|XN297
|
||||
[XK](Protocols_Details.md#XK---62)||62|X450|X420|Cars||||||NRF24L011&CC2500|XN297
|
||||
[XK2](Protocols_Details.md#XK2---99)||99|X4|P10|||||||NRF24L01&CC2500|XN297
|
||||
[YD717](Protocols_Details.md#YD717---8)||8|YD717|SKYWLKR|SYMAX4|XINXUN|NIHUI||||NRF24L01|
|
||||
[YuXiang](Protocols_Details.md#YuXiang---100)||100|||||||||NRF24L01|XN297
|
||||
@@ -1063,21 +1062,7 @@ Air protocol. TXs: T8FB,T8S. Compatible RXs: R8EF,R8FM,R8SM,R4FG,R4F
|
||||
Telemetry: RX_RSSI (for the original value add -256), TX_RSSI, TX_QLY (0..100%)
|
||||
|
||||
### Sub_protocol DumboRC - *2*
|
||||
Compatible RXs:
|
||||
* X6/X6F/X6FG/X6DC/X6DCG/X10F/X10FG (Other X Series should work as well)
|
||||
* P6F/P6FG/P6DC/P6DCG/P6FP/P10F/P10FG (Other P Series should work as well)
|
||||
|
||||
For P series specific features, see subprotocol 4 below.
|
||||
|
||||
CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8|CH9|CH10
|
||||
---|---|---|---|---|---|---|---|---|----
|
||||
CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8/Gyro gain|CH9|CH10
|
||||
|
||||
Telemetry:
|
||||
* RX_RSSI uses the receiver's direct percentage when available
|
||||
* TX_RSSI is the module-side received RSSI
|
||||
* TX_QLY is 0..100%
|
||||
* A2=external battery voltage in 0.1V units (set the ratio to 25.5 and adjust with offset)
|
||||
Compatible RXs: X6/X6F/X6FG
|
||||
|
||||
### Sub_protocol RC4G - *3*
|
||||
Compatible RXs: R4EH-G(/R4EH-H)
|
||||
@@ -1090,21 +1075,6 @@ FS=FailSafe
|
||||
|
||||
CH5 is driven by CH3 on the original TX, gyro sensitivity?
|
||||
|
||||
### Sub_protocol Dumbo_P - *4*
|
||||
Compatible RXs: P6F/P6FG/P6DC/P6DCG/P6FP/P10F/P10FG (Other P Series should work as well)
|
||||
|
||||
P series supports configuring receiver from transmitter. Originally, this logic appeared on DumboRC DDF-350 transmitter in 1.1.5 firmware version.
|
||||
|
||||
You can adjust these settings by using "DumboRC P Series.lua" script.
|
||||
|
||||
Settings include:
|
||||
* gyro on/off
|
||||
* gyro phase
|
||||
* gyro tuning
|
||||
* gyro gain channel
|
||||
* setting gyro endpoints
|
||||
* setting failsafe values
|
||||
|
||||
## Futaba - *21*
|
||||
Also called SFHSS depending on radio version.
|
||||
|
||||
@@ -1173,9 +1143,9 @@ New generation of GD models
|
||||
### Sub_protocol KF606 - *0*
|
||||
Model: KF606
|
||||
|
||||
CH1|CH2|CH3|CH4|CH5|CH6
|
||||
---|---|---|---|---|---
|
||||
A||T||TRIM|LED
|
||||
CH1|CH2|CH3|CH4|CH5
|
||||
---|---|---|---|---
|
||||
A||T||TRIM
|
||||
|
||||
### Sub_protocol MIG320 - *1*
|
||||
Model: Zhiyang MIG-320
|
||||
@@ -1603,25 +1573,21 @@ If a CC2500 is installed it will be used for this sub protocol. Option in this c
|
||||
|
||||
If only a NRF24L01 is installed then this sub protocol might be problematic because it is using the xn297L emulation with a transmission speed of 250kbps which doesn't work very well with every NRF24L01, this is an hardware issue with the authenticity and accuracy of the components.
|
||||
|
||||
CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8|CH9
|
||||
---|---|---|---|---|---|---|---|---
|
||||
A|E|T|R|Rate|Mode|Hover|Light|Stunt
|
||||
CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8
|
||||
---|---|---|---|---|---|---|---
|
||||
A|E|T|R|Rate|Mode|Hover|Light
|
||||
|
||||
The plane does not need to be bound each time if it is powered on **after** the radio/protocol is on.
|
||||
|
||||
Telemetry is supported. The plane sends a battery status of good->empty which is visible in A1 (good=8.4V->empty=7.1V) and RSSI gets a dummy value of 100. For the Sky Viper Vector, the battery telemetry will still appear in A1 with good=8.4V->empty=7.1V despite being a 1S plane with a switch good/bad in reality at 3.3V.
|
||||
Telemetry is supported. The plane sends a battery status of good->empty which is visible in A1 (good=8.4V->empty=7.1V) and RSSI gets a dummy value of 100.
|
||||
|
||||
The rudder trim is driven from the rudder channel to increase the range (Original TX rudder has no range once the motor has been turned on...).
|
||||
|
||||
Mode: -100%=6G, 0%=3D, +100%=Gyro off (Senior mode)
|
||||
|
||||
Stunt: Sky Viper Vector
|
||||
|
||||
### Sub_protocol X4 - *0*
|
||||
Transmitter: XK X4-A160, X5S, model: XK A160S, XK A280, XK A300
|
||||
|
||||
Model: Sky Viper Vector
|
||||
|
||||
### Sub_protocol P10 - *1*
|
||||
Model: Park10 J3-CUB
|
||||
|
||||
@@ -1872,7 +1838,7 @@ FMODE and AUX7 have 4 positions: -100%..-50%=>0, -50%..5%=>1, 5%..50%=>2, 50%..1
|
||||
## FX - *58*
|
||||
FEI XIONG
|
||||
|
||||
### Sub_protocol FX816 - *0*
|
||||
### Sub_protocol 816 - *0*
|
||||
Model: FX816 P38, B17
|
||||
|
||||
Only 8 TX IDs available
|
||||
@@ -1881,7 +1847,7 @@ CH1|CH2|CH3|CH4
|
||||
---|---|---|---
|
||||
A|-|T|-
|
||||
|
||||
### Sub_protocol FX620 - *1*
|
||||
### Sub_protocol 620 - *1*
|
||||
Model: FX620 SU35
|
||||
|
||||
CH1|CH2|CH3|CH4
|
||||
@@ -1900,7 +1866,7 @@ FX9630 and FX9603 Gyro: -100%=6G small throw, 0%=6G large throw, +100%=3D
|
||||
QIDI-550 Gyro: -100%=3D, 0%=6G, +100%=Torque
|
||||
|
||||
### Sub_protocol Q560 - *3*
|
||||
Model: QIDI-560, QIDI-580 (Cirrus SR22)
|
||||
Model: QIDI-560
|
||||
|
||||
CH1|CH2|CH3|CH4|CH5|CH6|CH7
|
||||
---|---|---|---|---|---|---
|
||||
@@ -1908,8 +1874,7 @@ A|E|T|R|FLIP|GYRO|LEDs
|
||||
|
||||
FLIP is a toggle channel meaning that -100% to +100% is a command and +100% to -100% is also a command
|
||||
|
||||
- GYRO QIDI-560: -100%=6G, 0%=3D+Gyro, +100%=3D
|
||||
- GYRO QIDI-580: -100%=6G, 0%=6G+Inverted, +100%=3D+Gyro
|
||||
Gyro: -100%=6G, 0%=3D+Gyro, +100%=3D
|
||||
|
||||
### Sub_protocol QF012 - *4*
|
||||
Model: QF012 SBD Dauntless
|
||||
@@ -1924,36 +1889,6 @@ Gyro: -100%=6G, 0%=3D+Gyro, +100%=3D
|
||||
|
||||
Reset: Restore fine tunning midpoint
|
||||
|
||||
### Sub_protocol BM26 - *5*
|
||||
Model: BM26 P51
|
||||
|
||||
Telemetry supported. The plane sends a battery status of good->empty which is visible in A1 (good=4.2V->empty=3.1V) and RSSI gets a dummy value of 100.
|
||||
|
||||
CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8|CH9
|
||||
---|---|---|---|---|---|---|---|---
|
||||
A|E|T|R|FLIP|GYRO|LEDs|Turning Light switch|Calib
|
||||
|
||||
Gyro: -100%=6G, 0%=3D+Gyro, +100%=3D
|
||||
|
||||
### Sub_protocol A570 - *6*
|
||||
VTOL Model: Kootai A570, QIDI-570
|
||||
|
||||
This model has no telemetry. Low battery indicated by model LED's turning flashing red.
|
||||
|
||||
CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8|CH9|CH10|CH11|CH12
|
||||
---|---|---|---|---|---|---|---|---|---|---|---
|
||||
A|E|T|R|STOP|MODE|Rate|Color|LED-Off|TrimR|TrimA|TrimE
|
||||
|
||||
- STOP: -100% allows standard start / sticks moved down and center (Mode 2), +100% all motors stop
|
||||
- MODE: -100%=Vertical hover, 0%=Flat flight, +100%=Vertical flight (Original TX will not allow motor start in Flat flight)
|
||||
- Rate: -100%=Low Rate, +100%=High Rate
|
||||
- Color: -100% / +100% toggles LED lights through color choices
|
||||
- LED-Off: +100% switches lights OFF, -100% allows lights ON after a toggle of the color channel
|
||||
- Trims: -100%=Rud-L,Ail-L,Ele-Back, +100%=Rud-R,Ail-R,Ele-Fwd, if used must be tied to momentary 3-pos switches
|
||||
- (trim switches disabled in FM tab, added to trim channels 10,11,12 in mixes tab)
|
||||
|
||||
Calibration: Same as original TX, model on level surface, TX bound, sticks lower left corners, LED's flashing
|
||||
|
||||
## FY326 - *20*
|
||||
|
||||
### Sub_protocol FY326 - *0*
|
||||
@@ -1967,29 +1902,12 @@ A|E|T|R|FLIP|RTH|HEADLESS|EXPERT|CALIBRATE
|
||||
Model: X6 FY319 Quadcopter (Needs Testing)
|
||||
|
||||
## FQ777 - *23*
|
||||
Autobind protocol
|
||||
|
||||
### Sub_protocol 124 - *0*
|
||||
Model: FQ777-124 (with SV7241A)
|
||||
|
||||
CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8
|
||||
---|---|---|---|---|---|---|---
|
||||
A|E|T|R|FLIP|RTH|HEADLESS|EXPERT
|
||||
|
||||
### Sub_protocol XBM37 - *1*
|
||||
Model: T-Smart XBM-37 (with SV7241A)
|
||||
|
||||
CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8|CH9|CH10|CH11|CH12|CH13|CH14
|
||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---
|
||||
A|E|T|R|RATE|FLIP|HEADLESS|LED|PICTURE|VIDEO|RTH|OK|ETrim|Atrim
|
||||
|
||||
- RATE: -100% Low, 0% Mid, +100% High
|
||||
- CH6 to CH12 are OFF/-100 and ON/+100 exception LED is OFF/+100
|
||||
- Receiver numbers (0-63) available for model match, MPM global ID used for unique module identifier.
|
||||
- Changing the TX module or RX number will require re-binding the receiver.
|
||||
|
||||
Calibration: Set quad on level surface, pull both sticks down in outer corners until LED's flash (Mode-2 TX)
|
||||
|
||||
## GW008 - *32*
|
||||
Model: Global Drone GW008 from Banggood
|
||||
|
||||
@@ -2084,16 +2002,14 @@ ARM|
|
||||
|
||||
### Sub_protocol XKK170 - *3*
|
||||
|
||||
Model: XK K170 UH-60L Black hawk, XK K270 UH-60L
|
||||
Model: XK K170 UH-60L Black hawk
|
||||
|
||||
CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8|CH9|CH10|CH11
|
||||
---|---|---|---|---|---|---|---|---|---|---
|
||||
A|E|T|R|RATE|EMERGENCY|TAKE_OFF/LANDING|CALIB|TrimA|TrimE|Optical
|
||||
CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8|CH9|CH10
|
||||
---|---|---|---|---|---|---|---|---|---
|
||||
A|E|T|R|RATE|EMERGENCY|TAKE_OFF/LANDING|CALIB|TrimA|TrimE
|
||||
|
||||
RATE: -100% Low, 0% Mid, +100% High
|
||||
|
||||
Optic: enable/disable the optical flow sensor for K270
|
||||
|
||||
## JIABAILE - *102*
|
||||
|
||||
### Sub_protocol Std - *0*
|
||||
@@ -2336,19 +2252,12 @@ Manual CH11=-100% & CH12=-100%, Balance CH11=+100% & CH12=-100%, Large Angle Bal
|
||||
### Sub_protocol F22S
|
||||
Model: ParkTen F22S
|
||||
|
||||
Mode -100% = 3D, 0% = 6G
|
||||
F22S: Mode -100% = 3D, 0% = 6G
|
||||
|
||||
### Sub_protocol J20
|
||||
Model: KF700 J20
|
||||
|
||||
Mode -100% = Gyro off, 0% = Horizontal, 100% = Vertical. CH8 - Invert, CH10 - Fix Height (Altitude hold)
|
||||
|
||||
### Sub_protocol CX10
|
||||
Model: Cheerson CX-10 with red PCB
|
||||
|
||||
**Only 2 IDs available**, use RX num to cycle through them.
|
||||
|
||||
Mode -100% = Low, 0% = Medium, 100% = High
|
||||
J20: Mode -100% = Gyro off, 0% = Horizontal, 100% = Vertical. CH8 - Invert, CH10 - Fix Height (Altitude hold)
|
||||
|
||||
## Shenqi - *19*
|
||||
Autobind protocol
|
||||
@@ -2454,19 +2363,6 @@ CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8|CH9
|
||||
---|---|---|---|---|---|---|---|---
|
||||
A|E|T|R|GYRO|CALIB|FLIP|RTN_ACT|RTN
|
||||
|
||||
## WPL - *107*
|
||||
TX: "Basic", Models: D12 / D12KM / D22 / D32 / D42 / D14
|
||||
|
||||
**Only 1 ID** available. If you have a TX contact me on GitHub or RCGroups.
|
||||
|
||||
Autobind protocol
|
||||
|
||||
CH1|CH2|CH3|CH4|CH5|CH6|CH7
|
||||
---|---|---|---|---|---|---
|
||||
TH|ST|ST_Trim|Aux|Light|TH_Rate|ST_Rate
|
||||
|
||||
Light: -100%=Off, +100%=On, Rate: -100%=Low, +100%=High
|
||||
|
||||
## XERALL - *91*
|
||||
Model: Xerall TankCopter
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ mv build/Multiprotocol.ino.bin ./binaries/mm-avr-txflash-aetr-A7105-inv-v$MULTI_
|
||||
printf "\e[33;1mBuilding mm-avr-txflash-aetr-CC2500-inv-v$MULTI_VERSION.bin\e[0m\n";
|
||||
opt_disable $ALL_PROTOCOLS;
|
||||
opt_enable $CC2500_PROTOCOLS;
|
||||
opt_disable HITEC_CC2500_INO REDPINE_CC2500_INO OMP_CC2500_INO SKYARTEC_CC2500_INO SCANNER_CC2500_INO FRSKYL_CC2500_INO RLINK_CC2500_INO;
|
||||
opt_disable HITEC_CC2500_INO REDPINE_CC2500_INO OMP_CC2500_INO SKYARTEC_CC2500_INO SCANNER_CC2500_INO FRSKYL_CC2500_INO;
|
||||
buildMulti;
|
||||
exitcode=$((exitcode+$?));
|
||||
mv build/Multiprotocol.ino.bin ./binaries/mm-avr-txflash-aetr-CC2500-inv-v$MULTI_VERSION.bin;
|
||||
|
||||
@@ -1,670 +0,0 @@
|
||||
# XBM-37 Quad SPI Capture Analysis
|
||||
|
||||
**Manufacturer:** T-Smart
|
||||
**Model:** XBM-37 (toy quadcopter)
|
||||
**TX/RX RF Chip:** SV7241A (QFN-20, NRF24L01+ clone / BK2425 derivative)
|
||||
**Protocol Basis:** Closely related to FQ777 (same RF chip family, same bind address, same ssv ESB air encoding)
|
||||
**Capture Tool:** Logic analyzer – digital (02a) and SPI-decoded (all "b" files)
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Capture File Inventory](#1-capture-file-inventory)
|
||||
2. [Key Protocol Parameters](#2-key-protocol-parameters)
|
||||
3. [SPI Initialization Sequence](#3-spi-initialization-sequence)
|
||||
4. [Bind Sequence – Deep Analysis (02a + 02b)](#4-bind-sequence--deep-analysis-02a--02b)
|
||||
5. [Bind vs No-RX Comparison (01b vs 02b)](#5-bind-vs-no-rx-comparison-01b-vs-02b)
|
||||
6. [Normal Data Packet Format](#6-normal-data-packet-format)
|
||||
7. [Per-Channel Control Analysis (03b – 24b)](#7-per-channel-control-analysis-03b--24b)
|
||||
8. [RF Timing Summary](#8-rf-timing-summary)
|
||||
9. [MPM Implementation Notes](#9-mpm-implementation-notes)
|
||||
|
||||
---
|
||||
|
||||
## 1. Capture File Inventory
|
||||
|
||||
All captures are located in `Captures_XBM-37/`. The **"b" files** are SPI-decoded exports
|
||||
(columns: `Time [s], Packet ID, MOSI, MISO`). The **"a" file** is the raw digital export
|
||||
(columns: `Time[s], MOSI, MISO, SCK, CSN, CE, IRQ`).
|
||||
|
||||
| File | Type | Description | SPI Transactions | TX Payloads | Duration |
|
||||
|------|------|-------------|-----------------|-------------|----------|
|
||||
| `01b-XBM-37_Quad_TX-PowerOn-NoRX.csv` | SPI | TX power-on, **no RX present** | 5,437 | 1,352 | 2.963 s |
|
||||
| `02a-XBM-37_Quad_TX-PowerOn-withRX-Bind.csv` | **Digital** | TX power-on + bind **with RX** (all 6 lines) | 405,271 samples | 1,345 payloads | 4.380 s |
|
||||
| `02b-XBM-37_Quad_TX-PowerOn-withRX-Bind.csv` | SPI | TX power-on + bind **with RX** | 5,409 | 1,345 | 2.949 s |
|
||||
| `03b-XBM-37_Quad_Aileron-Center-Left-Center-Right-Center.csv` | SPI | Aileron stick: center → left → center → right → center | 6,039 | 1,509 | 3.127 s |
|
||||
| `04b-XBM-37_Quad_Elevator-Center-Back-Center-Forward-Center.csv` | SPI | Elevator stick: center → back → center → fwd → center | 6,035 | 1,509 | 3.123 s |
|
||||
| `05b-XBM-37_Quad_Throttle-Low-High-Low.csv` | SPI | Throttle: low → high → low | 6,034 | 1,509 | 3.122 s |
|
||||
| `06b-XBM-37_Quad_Rudder-Center-Left-Center-Right-Center.csv` | SPI | Rudder (yaw): center → left → center → right → center | 6,034 | 1,509 | 3.122 s |
|
||||
| `07b-XBM-37_Quad_RateModeSwitch-1-2-3.csv` | SPI | Rate/speed mode switch: 1 → 2 → 3 | 6,034 | 1,509 | 3.122 s |
|
||||
| `08b-XBM-37_Quad_FlipSwitch-PushButton_Off-On.csv` | SPI | Flip trick button: off → on | 6,034 | 1,509 | 3.122 s |
|
||||
| `09b-XBM-37_Quad_VideoSwitch-Off-On-Off-On.csv` | SPI | Video record toggle: off → on → off → on | 6,023 | 1,506 | 3.116 s |
|
||||
| `10b-XBM-37_Quad_PictureSwitch-PushButton-3X.csv` | SPI | Photo/picture button: pressed 3 times | 6,019 | 1,505 | 3.113 s |
|
||||
| `11b-XBM-37_Quad_HeadlessSwitch-PushButton-Off-On.csv` | SPI | Headless mode button: off → on | 6,022 | 1,506 | 3.116 s |
|
||||
| `12b-XBM-37_Quad_ReturnToHomeSwitch-PushButton-Off-On.csv` | SPI | RTH button: off → on | 6,034 | 1,508 | 3.127 s |
|
||||
| `13b-XBM-37_Quad_LED-LightsSwitch-PushButton-On-Off.csv` | SPI | LED lights toggle: on → off | 6,019 | 1,505 | 3.113 s |
|
||||
| `14b-XBM-37_Quad_OK-Switch-PushButton-Off-On.csv` | SPI | OK button: off → on | 6,027 | 1,507 | 3.120 s |
|
||||
| `20b-XBM-37_Quad_Elevator-Trim-Center-Forward-Max_32-Clicks.csv` | SPI | Elevator trim: center → forward max (32 clicks) | 38,683 | 9,671 | 20.004 s |
|
||||
| `21b-XBM-37_Quad_Elevator-Trim-Center-Back-Max_31-Clicks.csv` | SPI | Elevator trim: center → back max (31 clicks) | 33,215 | 8,304 | 17.191 s |
|
||||
| `22b-XBM-37_Quad_Aileron-Trim-Center-Left-Max_32-Clicks.csv` | SPI | Aileron trim: center → left max (32 clicks) | 32,315 | 8,079 | 16.730 s |
|
||||
| `23b-XBM-37_Quad_Aileron-Trim-Center-Right-Max_31-Clicks.csv` | SPI | Aileron trim: center → right max (31 clicks) | 28,495 | 7,124 | 14.756 s |
|
||||
| `24b-XBM-37_Quad_Ail-Trim-Forward-Max_Ele-Trim-Right-Max_Push-OK-Button-2X.csv` | SPI | Elevator trim at forward max, aileron trim at right max, OK button pressed 2× | 29,275 | 7,319 | 15.176 s |
|
||||
|
||||
### Notes
|
||||
- The "b" SPI files each contain one SPI byte per row. Multiple rows sharing the same `Packet ID` belong to one CSN-low SPI transaction.
|
||||
- Files 03b–24b were all captured in **normal (post-bind) flight mode**. The TX had previously
|
||||
completed a successful bind.
|
||||
- File 01b and 02b are functionally **identical** captures (confirmed by direct payload comparison)
|
||||
except for a tiny time offset (~7 µs between corresponding packets). This confirms the TX
|
||||
transmits identically whether or not an RX is present; see Section 5.
|
||||
|
||||
---
|
||||
|
||||
## 2. Key Protocol Parameters
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| RF transceiver chip | SV7241A (QFN-20, NRF24L01+ / BK2425 derivative) |
|
||||
| RF frequency band | 2.4 GHz ISM |
|
||||
| Air data rate (SV7241A) | 2 Mbps (RF_SETUP = 0x26, bit5 = RF_DR = 1) |
|
||||
| Air data rate (nRF24L01+ equivalent) | **250 kbps** via `ssv_pack_dpl()` method |
|
||||
| Packet size (raw, pre-pack) | 8 bytes |
|
||||
| Packet size (after ssv_pack_dpl) | 12 bytes |
|
||||
| Enhanced ShockBurst | Yes (dynamic payload length, no auto-ACK) |
|
||||
| SPI clock frequency | ~143 kHz (7 µs period) |
|
||||
| Bind packet count | **400** |
|
||||
| Bind address (broadcast) | `E7 E7 E7 E7 67` |
|
||||
| Data address format | `[TX_ID₀ TX_ID₁ TX_ID₂ E7 67]` |
|
||||
| Bind channel (first packet) | 0x00 (universal) |
|
||||
| Hop channels (bind + data) | **0x49, 0x34, 0x26, 0x07** (4 channels) |
|
||||
| Packet period (steady state) | **~2,070 µs** (~2.07 ms) |
|
||||
| CE high pulse width | ~1,172 µs |
|
||||
| CE low gap (inter-packet) | ~896 µs |
|
||||
| TX-only mode | Yes — EN_RXADDR = 0x00; TX never listens |
|
||||
|
||||
---
|
||||
|
||||
## 3. SPI Initialization Sequence
|
||||
|
||||
The sequence below is from `02b` (PIDs 0–24), occurring over the first ~5 ms after the SPI
|
||||
bus becomes active (~150–335 ms after TX power-on due to startup delays).
|
||||
|
||||
### 3.1 SV7241A Private Bank Registers
|
||||
|
||||
The SV7241A (like BK2425) has bank-switched extended registers at addresses 0x18–0x1B.
|
||||
Register 0x1F selects the bank. These registers hold RF/analog calibration constants specific
|
||||
to the chip and **do not exist on nRF24L01+** — they are ignored when implementing with MPM.
|
||||
|
||||
| PID | Register | Data Written | Purpose |
|
||||
|-----|----------|-------------|---------|
|
||||
| 0 | 0x1F | `00` | Select bank 0 |
|
||||
| 1 | 0x1B | `10 E1 D3 3D` | Bank 0 – RF calibration reg 1 |
|
||||
| 2 | 0x19 | `06 AA A2 DB` | Bank 0 – RF calibration reg 2 |
|
||||
| 3 | 0x1F | `01` | Select bank 1 |
|
||||
| 4 | 0x19 | `77 48 9A E8` | Bank 1 – internal reg |
|
||||
| 5 | 0x1B | `76 87 CA 01` | Bank 1 – internal reg |
|
||||
| 6 | 0x1F | `02` | Select bank 2 |
|
||||
| 7 | 0x1B | `A0 00 18 A0` | Bank 2 – internal reg |
|
||||
| 8 | 0x1F | `04` | Select bank 4 |
|
||||
| 9 | 0x18 | `01 00 F0 00` | Bank 4 – internal reg |
|
||||
| 10 | 0x1F | `05` | Select bank 5 |
|
||||
| 11 | 0x18 | `84 03 2A 03` | Bank 5 – internal reg |
|
||||
| 12 | 0x19 | `90 BF 00 00` | Bank 5 – internal reg |
|
||||
| 13 | 0x1A | `A0 0F 00 00` | Bank 5 – internal reg |
|
||||
|
||||
### 3.2 Standard NRF24L01-Compatible Registers
|
||||
|
||||
Written immediately after the private registers (PIDs 14–24):
|
||||
|
||||
| PID | Register | Written Value | Decoded Meaning |
|
||||
|-----|----------|--------------|-----------------|
|
||||
| 14 | CONFIG (0x00) | `0x0C` | EN_CRC=1, CRCO=1 (2-byte CRC), PWR_UP=0, PRIM_RX=0 (TX) |
|
||||
| 15 | CONFIG (0x00) | *read back* `0x0C` | Verify CONFIG |
|
||||
| 16 | TX_ADDR (0x10) | `E7 E7 E7 E7 67` | Bind broadcast address |
|
||||
| 17 | RX_ADDR_P0 (0x0A) | `E7 E7 E7 E7 67` | Matches TX_ADDR for bind |
|
||||
| 18 | EN_AA (0x01) | `0x00` | Auto-ACK **disabled** on all pipes |
|
||||
| 19 | EN_RXADDR (0x02) | `0x00` | All RX pipes **disabled** – TX only |
|
||||
| 20 | RF_CH (0x05) | `0x49` | Initial RF channel (73 MHz offset) |
|
||||
| 21 | FEATURE (0x1D) | `0x04` | EN_DPL = 1 (dynamic payload length) |
|
||||
| 22 | DYNPD (0x1C) | `0x01` | DPL enabled on pipe 0 |
|
||||
| 23 | RF_SETUP (0x06) | `0x26` | SV7241A: 2 Mbps, max power |
|
||||
| 24 | CONFIG (0x00) | `0x0E` | PWR_UP=1 → TX powered up |
|
||||
|
||||
**RF_SETUP = 0x26 mapping:**
|
||||
|
||||
| Chip | Bit 5 meaning | Value 0x26 decodes as |
|
||||
|------|--------------|----------------------|
|
||||
| SV7241A | RF_DR (0=1Mbps, 1=2Mbps) | **2 Mbps**, max power |
|
||||
| nRF24L01+ | RF_DR_LOW (0=normal, 1=250kbps) | **250 kbps**, max power |
|
||||
|
||||
→ On nRF24L01+, writing 0x26 would set **250 kbps** (not 2 Mbps). For MPM emulation the
|
||||
correct approach (same as FQ777) is `NRF24L01_SetBitrate(NRF24L01_BR_250K)` combined with
|
||||
`ssv_pack_dpl()` to produce a compatible air packet.
|
||||
|
||||
**Startup timing:**
|
||||
|
||||
The TX power-on to first SPI transaction takes ~150 ms (CE is held high from
|
||||
t = –72 ms until t = 0, then the ~150 ms wait elapses before SPI activity at t ≈ +181 ms in the
|
||||
02b file). The entire init register sequence completes in ~6 ms.
|
||||
|
||||
---
|
||||
|
||||
## 4. Bind Sequence – Deep Analysis (02a + 02b)
|
||||
|
||||
### 4.1 Overview
|
||||
|
||||
After the ~150 ms startup wait, the TX sends exactly **400 bind packets** before switching to
|
||||
normal data mode.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Total bind packets | **400** |
|
||||
| Bind address | `E7 E7 E7 E7 67` (fixed broadcast) |
|
||||
| First bind packet channel | `0x00` (universal channel) |
|
||||
| Remaining 399 bind packet channels | Cycling: `0x49 → 0x34 → 0x26 → 0x07 → 0x49 → …` |
|
||||
| Bind packet payload | `20 14 07 03 TX_ID₀ TX_ID₁ TX_ID₂ CKSUM` |
|
||||
| Bind packet content | Identical across all 400 transmissions |
|
||||
|
||||
The first bind packet is sent on RF channel **0x00**, ensuring a newly powered-on RX (sitting
|
||||
on its default channel) can hear the bind announcement regardless of any prior state. Subsequent
|
||||
bind packets cycle through the four data-hopping channels.
|
||||
|
||||
### 4.2 Bind Packet Structure
|
||||
|
||||
```
|
||||
Byte 0 1 2 3 4 5 6 7
|
||||
[20 14 07 03 TX_ID₀ TX_ID₁ TX_ID₂ CKSUM]
|
||||
```
|
||||
|
||||
| Byte | Value (this TX) | Description |
|
||||
|------|----------------|-------------|
|
||||
| B0 | `0x20` | Bind identifier byte (constant — marks this as a bind packet) |
|
||||
| B1 | `0x14` | Protocol variant constant (XBM-37 specific; FQ777 uses `0x15`) |
|
||||
| B2 | `0x07` | Protocol variant constant (XBM-37 specific; FQ777 uses `0x05`) |
|
||||
| B3 | `0x03` | Protocol variant constant (XBM-37 specific; FQ777 uses `0x06`) |
|
||||
| B4 | `0x91` | TX_ID byte 0 — unique per TX unit |
|
||||
| B5 | `0x05` | TX_ID byte 1 — unique per TX unit |
|
||||
| B6 | `0x05` | TX_ID byte 2 — unique per TX unit |
|
||||
| B7 | `0x9B` | Checksum = (TX_ID₀ + TX_ID₁ + TX_ID₂) & 0xFF = (0x91+0x05+0x05) & 0xFF |
|
||||
|
||||
**Checksum formula for bind packet:** `B7 = (B4 + B5 + B6) & 0xFF`
|
||||
(This differs from the data packet checksum which sums B0–B6.)
|
||||
|
||||
### 4.3 TX ID
|
||||
|
||||
TX ID is **3 bytes** embedded in B4–B6 of the bind packet. This TX unit has:
|
||||
|
||||
```
|
||||
TX_ID = [0x91, 0x05, 0x05]
|
||||
```
|
||||
|
||||
After bind, the TX_ADDR is set to: `[TX_ID₀ TX_ID₁ TX_ID₂ 0xE7 0x67]`
|
||||
= `[91 05 05 E7 67]`
|
||||
|
||||
The RX extracts TX_ID from the bind packet and uses it to construct the matching address for
|
||||
normal data reception.
|
||||
|
||||
### 4.4 Bind Channel Sequence (from 02b SPI decoded)
|
||||
|
||||
```
|
||||
Packet #1: ch=0x00 [20 14 07 03 91 05 05 9B] ← universal announce
|
||||
Packet #2: ch=0x49 [20 14 07 03 91 05 05 9B]
|
||||
Packet #3: ch=0x34 [20 14 07 03 91 05 05 9B]
|
||||
Packet #4: ch=0x26 [20 14 07 03 91 05 05 9B]
|
||||
Packet #5: ch=0x07 [20 14 07 03 91 05 05 9B]
|
||||
Packet #6: ch=0x49 [20 14 07 03 91 05 05 9B]
|
||||
...continues cycling 0x49→0x34→0x26→0x07...
|
||||
Packet #400: ch=0x26 [20 14 07 03 91 05 05 9B] ← last bind
|
||||
```
|
||||
|
||||
Channel usage across all 400 bind packets:
|
||||
|
||||
| Channel | Count | Notes |
|
||||
|---------|-------|-------|
|
||||
| 0x00 | 1 | First packet only |
|
||||
| 0x49 (73 MHz) | 100 | Regular cycle |
|
||||
| 0x34 (52 MHz) | 100 | Regular cycle |
|
||||
| 0x26 (38 MHz) | 100 | Regular cycle (includes last bind packet) |
|
||||
| 0x07 (7 MHz) | 99 | Regular cycle |
|
||||
|
||||
### 4.5 Bind-to-Normal Transition
|
||||
|
||||
After the 400th bind packet the TX:
|
||||
|
||||
1. Completes the 400th transmission (CE pulse ~1,172 µs, then CE low).
|
||||
2. Waits **~16.6 ms** (no SPI activity; firmware processing time).
|
||||
3. Writes the new TX_ADDR: `W_TX_ADDR [91 05 05 E7 67]` (SPI pid=1625 at t=1.175882 s).
|
||||
4. Immediately clears STATUS and starts normal data mode.
|
||||
5. First normal packet: ch=**0x07** (continuation of the hop sequence from where bind ended).
|
||||
|
||||
```
|
||||
Last bind (400th): t=1.159258 s, ch=0x26
|
||||
TX_ADDR change: t=1.175882 s (+16.6 ms gap)
|
||||
First data packet: t=1.176619 s, ch=0x07 [E1 70 70 70 20 20 00 71]
|
||||
```
|
||||
|
||||
### 4.6 IRQ / STATUS Analysis (from 02a Digital Capture)
|
||||
|
||||
The IRQ line (active-low) is asserted by the SV7241A for every successfully transmitted packet
|
||||
(TX_DS interrupt):
|
||||
|
||||
| Observation | Value |
|
||||
|-------------|-------|
|
||||
| Total IRQ assertions | 1,340 (matching 1,340 normal CE pulses) |
|
||||
| IRQ pulse duration (typical) | ~600 µs |
|
||||
| IRQ source | TX_DS only (TX complete) |
|
||||
| RX_DR interrupts | **Zero** — no data ever received |
|
||||
|
||||
The SPI confirms there are **no `R_RX_PAYLOAD` (0x61) commands** in either 01b or 02b.
|
||||
Combined with `EN_RXADDR = 0x00` (all RX pipes disabled), it is impossible for the TX to
|
||||
receive data from the RX. This is confirmed by the STATUS register never showing bit6
|
||||
(RX_DR) = 1.
|
||||
|
||||
One extended IRQ assertion of **15.9 ms** occurs at t=1.160476 s (immediately after the 400th
|
||||
bind packet). This is not an RX event — it is simply the TX_DS interrupt remaining uncleared
|
||||
while the firmware processes the bind-completion event and updates TX_ADDR. The IRQ is
|
||||
cleared when `W_STATUS [70]` is written at t=1.176254 s (pid=1626).
|
||||
|
||||
### 4.7 CE Pulse Timing (from 02a)
|
||||
|
||||
| Measurement | Value |
|
||||
|-------------|-------|
|
||||
| Normal CE pulse duration | min 1,165 µs, max 1,178 µs, **avg 1,172 µs** |
|
||||
| CE inter-pulse gap (end to start) | **~896 µs** |
|
||||
| Total cycle (CE high + gap) | **~2,068 µs** |
|
||||
| Pulse-to-pulse interval (high to high) | min 2.063 ms, max 20.686 ms, **avg 2.084 ms** |
|
||||
|
||||
The 20.686 ms outlier corresponds to the bind-to-normal transition pause (~16.6 ms).
|
||||
|
||||
---
|
||||
|
||||
## 5. Bind vs No-RX Comparison (01b vs 02b)
|
||||
|
||||
**Finding: The TX transmits identically whether or not an RX is present.**
|
||||
|
||||
| Metric | 01b (No RX) | 02b (With RX) |
|
||||
|--------|------------|----------------|
|
||||
| Total TX payloads | 1,352 | 1,345 |
|
||||
| Bind packets | 400 | 400 |
|
||||
| Bind packet content | `20 14 07 03 91 05 05 9B` | `20 14 07 03 91 05 05 9B` (identical) |
|
||||
| TX_ADDR change time | t=1.175899 s | t=1.175882 s |
|
||||
| TX_ADDR after bind | `[91 05 05 E7 67]` | `[91 05 05 E7 67]` (identical) |
|
||||
| Normal data payload (idle) | `E1 70 70 70 20 20 00 71` | `E1 70 70 70 20 20 00 71` (identical) |
|
||||
| Time offset between captures | — | ~7 µs per packet |
|
||||
|
||||
The TX performs an automatic timed bind sequence (400 packets) and then switches to data mode
|
||||
unconditionally, regardless of RX acknowledgment. The TX **never knows** whether the RX
|
||||
accepted the bind. Bind is one-sided: the RX passively listens for the bind packet on ch=0x00
|
||||
(or the cycling channels), extracts the TX_ID and hop channels, and then follows the TX's
|
||||
normal data transmissions.
|
||||
|
||||
---
|
||||
|
||||
## 6. Normal Data Packet Format
|
||||
|
||||
### 6.1 Packet Structure (8 bytes)
|
||||
|
||||
```
|
||||
Byte 0 1 2 3 4 5 6 7
|
||||
[Throttle Rudder Aileron Elevator Flags1 Flags2 Flags3 CKSUM]
|
||||
```
|
||||
|
||||
**Checksum (B7):** `B7 = (B0 + B1 + B2 + B3 + B4 + B5 + B6) & 0xFF`
|
||||
|
||||
All 8-byte checksum values have been verified against this formula across all capture files.
|
||||
|
||||
### 6.2 Stick Channels (B0–B3)
|
||||
|
||||
| Byte | Channel | Min | Center | Max | Notes |
|
||||
|------|---------|-----|--------|-----|-------|
|
||||
| B0 | Throttle | `0xE1` | `0x70` | `0x00` | Non-return throttle |
|
||||
| B1 | Rudder (Yaw) | `0x00` | `0x70` | `0xE1` | |
|
||||
| B2 | Aileron (Roll) | `0xE1` | `0x70` | `0x00` | |
|
||||
| B3 | Elevator (Pitch) | `0xE1` | `0x70` | `0x00` | |
|
||||
|
||||
All analog channels use the same range: **0x00 – 0xE1** (0 – 225 decimal) with center at
|
||||
**0x70** (112 decimal).
|
||||
- Throttle, Aileron, and Elevator use reversed state (0xE1...0x70...0x00).
|
||||
|
||||
### 6.3 Flags Byte 1 (B4)
|
||||
|
||||
| Value | Meaning | Description |
|
||||
|-------|---------|-------------|
|
||||
| `0x20` | **Trim Elevator Center** | Resets at TX start |
|
||||
| `0x21 up to 0x40` | Trim elevator **forward** | 32 Clicks to max |
|
||||
| `0x1F downto 0x01` | Trim elevator **back** | 31 Clicks to max |
|
||||
|
||||
### 6.4 Flags Byte 2 (B5)
|
||||
|
||||
| Value | Meaning | Description |
|
||||
|-----|------|-------------|
|
||||
| `0x20` | **Trim Aileron Center** | Resets at TX start |
|
||||
| `0x21 up to 0x40` | Trim aileron **left** | 32 Clicks to max |
|
||||
| `0x1F down to 0x01` | Trim aileron **right** | 31 Clicks to max |
|
||||
| `0x80` | OK button | OK pressed (`0x20` → `0xA0`) |
|
||||
|
||||
`B5 bit7` is an OR mask on top of trim value (`B5_with_OK = B5_trim | 0x80`), e.g.
|
||||
center `0x20 -> 0xA0`, right-max `0x01 -> 0x81`.
|
||||
|
||||
### 6.5 Flags Byte 3 (B6)
|
||||
|
||||
| Bits | Mask | Name | Description |
|
||||
|------|------|------|-------------|
|
||||
| [1:0] | `0x03` | Rate mode | `0x00`=rate 1 (slow), `0x01`=rate 2, `0x02`=rate 3 (fast) |
|
||||
| 2 | `0x04` | LED off | `0`=LED lights ON, `1`=LED lights OFF |
|
||||
| 3 | `0x08` | RTH | `1`=return-to-home active |
|
||||
| 4 | `0x10` | Headless | `1`=headless mode active |
|
||||
| 5 | `0x20` | Video | `1`=video recording active |
|
||||
| 6 | `0x40` | Picture | `1`=photo capture triggered |
|
||||
| 7 | `0x80` | Flip | `1`=3D flip command |
|
||||
|
||||
Normal state: `B6 = 0x00` (rate 1, LED on, no special modes)
|
||||
|
||||
### 6.6 Return-to-Home (RTH)
|
||||
|
||||
Updated analysis of **file 12b** (RTH OFF in first half, ON in second half):
|
||||
|
||||
- RTH-OFF payload is steady at `7E 70 70 70 20 20 00 0E`.
|
||||
- After the OFF→ON transition, the payload changes to `7E 70 70 70 20 20 08 16`.
|
||||
- The persistent functional change in this capture is `B6: 0x00 -> 0x08`
|
||||
(bit3 asserted when RTH is ON).
|
||||
|
||||
In this export, RTH is represented in the steady 8-byte payload by `B6 bit3`.
|
||||
|
||||
### 6.7 Example Payloads
|
||||
|
||||
| Payload | Meaning |
|
||||
|---------|---------|
|
||||
| `E1 70 70 70 20 20 00 71` | Throttle low `0xE1`, all sticks center `0x70`, LED on, no special modes `0x00` |
|
||||
| `82 70 70 70 20 20 00 12` | Throttle ~mid `0x82`, all sticks center, LED on, no special modes |
|
||||
| `00 70 70 70 20 20 00 90` | Throttle max `0x00`, all sticks center, LED on, no special modes |
|
||||
| `E1 70 00 70 20 20 00 01` | Throttle low, aileron full right `0x00`, LED on, no special modes |
|
||||
| `E1 70 E1 70 20 20 00 E2` | Throttle low, aileron full left `0xE1`, LED on, no special modes |
|
||||
| `E1 70 70 00 20 20 00 01` | Throttle low, elevator full back `0x00`,LED on, no special modes |
|
||||
| `82 70 70 70 20 20 80 92` | Throttle ~mid, all sticks center, flip command active `0x80` |
|
||||
| `82 70 70 70 20 20 10 22` | Throttle ~mid, all sticks center, headless mode active `0x10` |
|
||||
| `82 70 70 70 20 20 01 13` | Throttle ~mid, all sticks center, rate mode 2 `0x01` |
|
||||
| `82 70 70 70 20 20 02 14` | Throttle ~mid, all sticks center, rate mode 3 `0x02` |
|
||||
| `00 70 70 70 20 A0 00 F1` | Throttle max, all sticks center, OK button pressed `0xA0` |
|
||||
| `00 70 70 70 20 20 04 75` | Throttle max, all sticks center, LED lights OFF `0x04` |
|
||||
|
||||
---
|
||||
|
||||
## 7. Per-Channel Control Analysis (03b – 24b)
|
||||
|
||||
All control captures were taken in post-bind normal mode. Hopping channels confirmed active:
|
||||
`0x07, 0x49, 0x34, 0x26` (cyclic). Average packet interval: **~2.07 ms** across all files.
|
||||
|
||||
### 03b – Aileron (Roll)
|
||||
|
||||
- **Affected byte:** B2
|
||||
- Range observed: `0xE1` (full left) → `0x70` (center) → `0x00` (full right)
|
||||
|
||||
### 04b – Elevator (Pitch)
|
||||
|
||||
- **Affected byte:** B3
|
||||
- Range observed: `0x00` (full back) → `0x70` (center) → `0xE1` (full forward)
|
||||
|
||||
### 05b – Throttle
|
||||
|
||||
- **Affected byte:** B0
|
||||
- Range observed: `0xE1` (low minimum) ↔ `0x70` (center) ↔ `0x00` (full maximum)
|
||||
- Non-return throttle (stick does not spring back to center)
|
||||
|
||||
### 06b – Rudder (Yaw)
|
||||
|
||||
- **Affected byte:** B1
|
||||
- Range observed: `0x00` (full left) → `0x70` (center) → `0xE1` (full right)
|
||||
- Note: B0 (throttle) also varies in this capture because left stick controls both
|
||||
throttle (up/down) and rudder (left/right) Mode 2 transmitter
|
||||
|
||||
### 07b – Rate Mode Switch
|
||||
|
||||
- **Affected byte:** B6 bits[1:0]
|
||||
- `B6 = 0x00`: Rate 1 (slowest/beginner)
|
||||
- `B6 = 0x01`: Rate 2 (intermediate)
|
||||
- `B6 = 0x02`: Rate 3 (fastest/expert)
|
||||
|
||||
### 08b – Flip Switch
|
||||
|
||||
- **Affected byte:** B6 bit7
|
||||
- `B6 = 0x00`: No flip; `B6 = 0x80`: 3D flip command active
|
||||
- While flip is held: throttle (B0) increments slightly (`0x82` → `0x83`)
|
||||
|
||||
### 09b – Video Switch
|
||||
|
||||
- **Affected byte:** B6 bit5
|
||||
- `B6 = 0x00`: Video off; `B6 = 0x20`: Video recording active
|
||||
- Toggle on/off: transitions between these two states
|
||||
|
||||
### 10b – Picture Switch (3×)
|
||||
|
||||
- **Affected byte:** B6 bit6
|
||||
- `B6 = 0x00`: Idle; `B6 = 0x40`: Photo capture triggered (momentary)
|
||||
|
||||
### 11b – Headless Switch
|
||||
|
||||
- **Affected byte:** B6 bit4
|
||||
- `B6 = 0x00`: Headless off; `B6 = 0x10`: Headless mode active
|
||||
|
||||
### 12b – Return to Home Switch
|
||||
|
||||
- **Capture split tested:** first half RTH OFF, second half RTH ON.
|
||||
- **RTH OFF payload:** `7E 70 70 70 20 20 00 0E`
|
||||
- **RTH ON payload:** `7E 70 70 70 20 20 08 16`
|
||||
- **Primary byte change:** `B6: 0x00 -> 0x08`
|
||||
- `B6 bit3` is asserted when RTH is active in this capture
|
||||
- **Minor secondary variation while ON:** `B0` occasionally increments `0x7E -> 0x7F`,
|
||||
producing checksum `0x16 -> 0x17`
|
||||
- All other payload bytes remain unchanged across the OFF→ON transition in this file.
|
||||
|
||||
### 13b – LED Lights Switch
|
||||
|
||||
- **Affected byte:** B6 bit2
|
||||
- `B6 = 0x00`: LED lights **ON** (default)
|
||||
- `B6 = 0x04`: LED lights **OFF**
|
||||
- Note: Inverted logic — bit2=1 means OFF
|
||||
|
||||
### 14b – OK Switch
|
||||
|
||||
- **Affected byte:** B5 bit7
|
||||
- `B5 = 0x20`: OK button not pressed
|
||||
- `B5 = 0xA0`: OK button pressed (0x20 | 0x80)
|
||||
|
||||
### 20b – Elevator Trim Center → Forward Max (32 clicks)
|
||||
|
||||
- **Affected byte:** B4 (elevator trim)
|
||||
- Observed progression: `0x20 -> ... -> 0x40`
|
||||
- Endpoint in this capture: `B4 = 0x40` (forward max)
|
||||
- `B5` remains `0x20`; `B6` remains `0x00`
|
||||
|
||||
### 21b – Elevator Trim Center → Back Max (31 clicks)
|
||||
|
||||
- **Affected byte:** B4 (elevator trim)
|
||||
- Observed progression: `0x20 -> ... -> 0x01`
|
||||
- Endpoint in this capture: `B4 = 0x01` (back max)
|
||||
- `B5` remains `0x20`; `B6` remains `0x00`
|
||||
|
||||
### 22b – Aileron Trim Center → Left Max (32 clicks)
|
||||
|
||||
- **Affected byte:** B5 (aileron trim)
|
||||
- Observed progression: `0x20 -> ... -> 0x40`
|
||||
- Endpoint in this capture: `B5 = 0x40` (left max)
|
||||
- `B4` remains `0x20`; `B6` remains `0x00`
|
||||
|
||||
### 23b – Aileron Trim Center → Right Max (31 clicks)
|
||||
|
||||
- **Affected byte:** B5 (aileron trim)
|
||||
- Observed progression: `0x20 -> ... -> 0x01`
|
||||
- Endpoint in this capture: `B5 = 0x01` (right max)
|
||||
- `B4` remains `0x20`; `B6` remains `0x00`
|
||||
|
||||
### 24b – Trim Endpoints + OK Button (2 presses)
|
||||
|
||||
- Fixed trim baseline in this capture:
|
||||
- `B4 = 0x40` (elevator forward max)
|
||||
- `B5 trim = 0x01` (aileron right max)
|
||||
- **OK effect:** `B5 bit7` toggles over trim baseline:
|
||||
- not pressed: `B5 = 0x01`
|
||||
- pressed: `B5 = 0x81` (`0x01 | 0x80`)
|
||||
- `B6` remains `0x00` throughout.
|
||||
|
||||
---
|
||||
|
||||
## 8. RF Timing Summary
|
||||
|
||||
### 8.1 Per-Packet SPI Cycle (02b decoded)
|
||||
|
||||
Each packet transmission consists of 4 SPI transactions:
|
||||
|
||||
```
|
||||
1. W_STATUS [27] = 0x70 → Clear TX_DS / MAX_RT / RX_DR interrupt flags
|
||||
2. FLUSH_TX [E1] → Empty TX FIFO
|
||||
3. W_RF_CH [25] = <ch> → Set hop channel
|
||||
4. W_TX_PAYLOAD [A0] = 8B → Write 8-byte payload to TX FIFO
|
||||
[CE pulse ~1,172 µs to transmit]
|
||||
[~896 µs gap before next cycle]
|
||||
```
|
||||
|
||||
### 8.2 Bind Phase Timing
|
||||
|
||||
| Event | Timestamp (02b) |
|
||||
|-------|----------------|
|
||||
| Init register writes complete | t = 0.186 s |
|
||||
| Startup wait completes | t = 0.335 s (~150 ms) |
|
||||
| First bind packet (ch=0x00) | t = 0.335838 s |
|
||||
| 400th (last) bind packet (ch=0x26) | t = 1.159258 s |
|
||||
| TX_ADDR change to data address | t = 1.175882 s (+16.6 ms) |
|
||||
| First normal data packet | t = 1.176619 s |
|
||||
|
||||
Total bind phase duration: **~0.824 s** (150 ms wait + ~674 ms for 400 packets at 2.07 ms each)
|
||||
|
||||
### 8.3 Normal Data Phase Timing
|
||||
|
||||
| Event | Interval |
|
||||
|-------|---------|
|
||||
| Packet 1 → 2 (startup) | 0.918 ms |
|
||||
| Packet 2 → 3 (startup) | 0.916 ms |
|
||||
| Packet 3 → 4 (startup) | 1.491 ms |
|
||||
| Steady state (4+) | **~2.070 ms** per packet |
|
||||
|
||||
The first 3 packets after bind-to-data transition have shorter intervals, then settle into the
|
||||
steady 2.07 ms period.
|
||||
|
||||
### 8.4 STATUS Byte Values Observed
|
||||
|
||||
| STATUS | Meaning | When seen |
|
||||
|--------|---------|-----------|
|
||||
| `0x0E` | TX FIFO not full, RX FIFO empty, all interrupts clear | Normal/idle |
|
||||
| `0x2E` | TX_DS = 1 (TX complete interrupt), TX FIFO not full | After each TX packet |
|
||||
|
||||
RX_DR (bit6) is **never set** in any STATUS byte — confirming no data is ever received.
|
||||
|
||||
---
|
||||
|
||||
## 9. MPM Implementation Notes
|
||||
|
||||
### 9.1 Comparison with FQ777
|
||||
|
||||
The XBM-37 protocol is closely related to FQ777 but has the following differences:
|
||||
|
||||
| Property | FQ777 | XBM-37 | Comment|
|
||||
|----------|-------|---------|---------|
|
||||
| Bind count | 1,000 | **400** | works with either |
|
||||
| Bind packet B1 | `0x15` | `0x14` | different |
|
||||
| Bind packet B2 | `0x05` | `0x07` | different |
|
||||
| Bind packet B3 | `0x06` | `0x03` | different |
|
||||
| Hop channels | `4D 43 27 07` | **`49 34 26 07`** | different |
|
||||
| Data range | 0x00–0x64 | **0x00–0xE1** | different |
|
||||
| Bind address | `E7 E7 E7 E7 67` | `E7 E7 E7 E7 67` | (same) |
|
||||
| Checksum method | Sum B0–B6 | Sum B0–B6 | (same) |
|
||||
| Bind checksum | Sum B4–B6 | Sum B4–B6 | (same) |
|
||||
| ssv_pack_dpl encoding | Yes | Yes | (same) |
|
||||
| Air bitrate (nRF24L01+) | 250 kbps | 250 kbps | (same) |
|
||||
|
||||
### 9.2 Required Implementation Constants
|
||||
|
||||
```c
|
||||
static const uint8_t XBM37_bind_addr[] = {0xE7, 0xE7, 0xE7, 0xE7, 0x67};
|
||||
static const uint8_t XBM37_hop_channels[] = {0x49, 0x34, 0x26, 0x07};
|
||||
```
|
||||
|
||||
### 9.3 Bind Packet Builder
|
||||
|
||||
```c
|
||||
// Bind packet (bytes 4–6 = TX ID; B7 = checksum of B4+B5+B6)
|
||||
packet[0] = 0x20;
|
||||
packet[1] = 0x14;
|
||||
packet[2] = 0x07;
|
||||
packet[3] = 0x03;
|
||||
packet[4] = rx_tx_addr[0]; // TX_ID byte 0
|
||||
packet[5] = rx_tx_addr[1]; // TX_ID byte 1
|
||||
packet[6] = rx_tx_addr[2]; // TX_ID byte 2
|
||||
packet[7] = packet[4] + packet[5] + packet[6];
|
||||
```
|
||||
|
||||
### 9.4 Data Packet Builder
|
||||
|
||||
```c
|
||||
packet[0] = convert_channel_16b_limit(THROTTLE, 0xE1, 0x00);
|
||||
packet[1] = convert_channel_16b_limit(RUDDER, 0x00, 0xE1);
|
||||
packet[2] = convert_channel_16b_limit(AILERON, 0xE1, 0x00);
|
||||
packet[3] = convert_channel_16b_limit(ELEVATOR, 0xE1, 0x00);
|
||||
packet[4] = ((convert_channel_8b(CH13) * 63) / 255) + 1; // ele trim (01..20..40)
|
||||
packet[5] = 64 - (((uint32_t)convert_channel_8b(CH14) * 63 + 127) / 255); // ail trim (40..20..01)
|
||||
packet[5] |= GET_FLAG(OK_SW, 0x80); // OK button
|
||||
packet[6] = (rate_mode & 0x03) // bits[1:0] = rate (0/1/2)
|
||||
| GET_FLAG(LED_SW, 0x04) // bit2=1 = LED off
|
||||
| GET_FLAG(RTH_SW, 0x08) // bit3 = RTH
|
||||
| GET_FLAG(HEADLESS_SW, 0x10) // bit4 = headless
|
||||
| GET_FLAG(VIDEO_SW, 0x20) // bit5 = video
|
||||
| GET_FLAG(PHOTO_SW, 0x40) // bit6 = picture
|
||||
| GET_FLAG(FLIP_SW, 0x80); // bit7 = flip
|
||||
packet[7] = 0;
|
||||
for (uint8_t i = 0; i < 7; i++) packet[7] += packet[i]; // checksum
|
||||
```
|
||||
|
||||
### 9.5 Address Management
|
||||
|
||||
```c
|
||||
// TX init:
|
||||
rx_tx_addr[2] = 0x00; // original hardcoded value now changes for model match capability (see below)
|
||||
rx_tx_addr[3] = 0xE7;
|
||||
rx_tx_addr[4] = 0x67;
|
||||
// rx_tx_addr[0] and [1] are random/unique to the TX
|
||||
// rx_tx_addr[2] now varies by changing receiver number (0-63) for model match
|
||||
|
||||
// Bind address (used during bind):
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, XBM37_bind_addr, 5);
|
||||
|
||||
// After 400th bind packet, switch to data address:
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, 5);
|
||||
```
|
||||
|
||||
### 9.6 RF Init
|
||||
|
||||
```c
|
||||
void XBM37_RF_init() {
|
||||
NRF24L01_Initialize();
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, XBM37_bind_addr, 5);
|
||||
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // no auto-ACK
|
||||
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x00); // no RX pipes
|
||||
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x04); // EN_DPL
|
||||
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x01); // DPL pipe 0
|
||||
NRF24L01_SetBitrate(NRF24L01_BR_250K); // 250 kbps on nRF24L01+
|
||||
}
|
||||
```
|
||||
|
||||
### 9.7 Air Encoding
|
||||
|
||||
Because both the XBM-37 TX and RX use SV7241A (a BK2425 derivative), the air packet format is
|
||||
the SV7241A Enhanced ShockBurst encoding. When implementing with nRF24L01+, the same
|
||||
`ssv_pack_dpl()` function used in the FQ777 protocol must be applied to convert the raw 8-byte
|
||||
payload into the 12-byte packed representation that nRF24L01+ transmits at 250 kbps to produce
|
||||
a compatible on-air signal.
|
||||
|
||||
---
|
||||
|
||||
*Analysis completed using Python scripts against the raw CSV captures. All packet checksums,
|
||||
channel sequences, bind counts, and register values verified programmatically.*
|
||||
Reference in New Issue
Block a user