protocol for WL Heli V911-s
- planger
- Offline
This is therefore my second attempt in a short time window.
I'm using a Pluto SDR box running on pothosware and MATLAB. I haven't been able to get gnuradio to recognize directly the Pluto on my windows... I'm using MATLAB to quickly see the signal and demodulate it using some math. Basically, I'm looking at the unwrapped phase variations to determine the 0 and 1. Therefore I don't need to know the sync word at all, it's just coming out from the data.
Pascal
Please Log in or Create an account to join the conversation.
- goebish
- Offline
- I Void Warranties
- Posts: 2631
Please Log in or Create an account to join the conversation.
- SeByDocKy
- Offline
- Posts: 1016
planger wrote: II'm using MATLAB to quickly see the signal and demodulate it using some math. Basically, I'm looking at the unwrapped phase variations to determine the 0 and 1. Therefore I don't need to know the sync word at all, it's just coming out from the data.
Pascal
Haaa Matlab my old and faithfull compagnon since 20+ years .. (OMG .... I am old now )
Please Log in or Create an account to join the conversation.
- goebish
- Offline
- I Void Warranties
- Posts: 2631
Most of the time I use the hackrf, I like it for its large spectrum capability (1MHz-6GHz) but the ADC/DAC is 8 bit only (not good at all for weak signals) and it's not full duplex but I don't need that yet, also I feel like I would be able to repair it if I ever fry the rf frontend or something else.
Please Log in or Create an account to join the conversation.
- planger
- Offline
SeByDocKy wrote: Haaa Matlab my old and faithfull compagnon since 20+ years .. (OMG .... I am old now )
The attached picture is the unwrapped phase of the RF signal plotted in MATLAB.
You can read from there: 0x71, 0x0F, 0x55, 0x95, 0x3C,... (down is 0 up is 1)
I see it coming, obviously I'm not reading the paylod by hand It's the output of my script.
Please Log in or Create an account to join the conversation.
- goebish
- Offline
- I Void Warranties
- Posts: 2631
Please Log in or Create an account to join the conversation.
- planger
- Offline
0x19, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xE8, 0x1F, 0xFF, 0x03, 0x20, 0x00, 0x00, 0x00
With the following parameters: XN297 @250Kbps, payload 16 bytes, scrambled and not enhanced.
TX Address: 0XA5, 0xFF, 0x70, 0x8D, 0x76
byte 0: value changes at each power up. Seen 0x04, 0x0E, 0x13, 0x19, 0x1A...
byte 1: flag 0x04 when pressing the button on the remote (0x00=default), not sure what it does. I'll look at the manual later...
byte 4: 0xFE unknown
bytes 8..12: packed TAER 11 bits.
I've added a CRC check to the XN297_ReadPayload function since I was getting way too much noise in the incoming packets preventing me to understand the content...
I'll look at the bind packets later. The pieces are coming together slowly but...
Pascal
Please Log in or Create an account to join the conversation.
- planger
- Offline
You can obtain kind of the same result in pothosware or gnuradio by sending directly the block RF ouput (complex numbers) in an "angle" block which will show you the phase. The problem is that the ouput (real) is wrapped around -pi and +pi. I haven't found how to unwrap it within these tools (might be possible but...). This is why I've used MATLAB to do the math work.goebish wrote: Interesting, I've never tried this very low level approach ...
Pascal
Please Log in or Create an account to join the conversation.
- goebish
- Offline
- I Void Warranties
- Posts: 2631
planger wrote: I've added a CRC check to the XN297_ReadPayload function since I was getting way too much noise in the incoming packets
Nice, this has been on my todo list for a long time
Please Log in or Create an account to join the conversation.
- planger
- Offline
I've just decoded using my MATLAB script one of the captured bind packets:
- RF channel: 35
- TX address: 0x4B 0x4E 0x42 0x4E 0x44
- Payload 16 bytes: 0x42 0x4E 0x44 0xA5 0xFF 0x70 0x8D 0x76 0x11 0x15 0x1A 0x1F 0x24 0x29 0x2E 0x33
byte 0-2: padding from the TX adddress? I expect it to be constant to be checked by some live tests.
byte 3-7: TX address for normal mode
byte 8-15: RF channels for normal mode
- sent every 3ms for 100 packets then every 10ms for 100 packets then switch to normal mode
The only unknown and needed info at this stage is the 1st byte of the normal packet.
Pascal
Please Log in or Create an account to join the conversation.
- goebish
- Offline
- I Void Warranties
- Posts: 2631
Please Log in or Create an account to join the conversation.
- goebish
- Offline
- I Void Warranties
- Posts: 2631
Excerpt from the XN297L datasheet (use google translator):
While the nrf24l01 datasheet states:1M / 2Mbps模式,需要晶振精度 ±40ppm
250kbps模式,需要晶振精度 ±20ppm
Though in theory in the XN297L(Tx) --> NRF24L01(Rx) direction that should be less of an issue; but you may have noticed with SDR that xn297(L) channels usually have a frequency drift of ~+200kHz compared to the nrf, that probably doesn't help either... That's +80ppm @ 16MHz, we're already way out of spec, even @ 1Mbps, that's a wonder it works that well already.Accepts low cost ±60ppm 16MHz crystal
Anyone ever tried to fiddle with the "do not touch" registers on the nrf24l01 (0x18 to 0x1B) ?
I should try to do some fuzz testing on those registers while watching the resulting FFT on SDR, there might be an undocumented one that allows to set a base frequency offset like on the a7105/cyrf6936/cc2500...
Please Log in or Create an account to join the conversation.
- goebish
- Offline
- I Void Warranties
- Posts: 2631
Please Log in or Create an account to join the conversation.
- planger
- Offline
Here is the XN297_ReadPayload with CRC check. It's fully compatible with the previous function (so no need to change any protocols unless you want to improve them by checking CRC). The return indicates CRC OK=true, CRC NOK=false.goebish wrote:
Nice, this has been on my todo list for a long timeplanger wrote: I've added a CRC check to the XN297_ReadPayload function since I was getting way too much noise in the incoming packets
The payload is always decrypted so you still can see what's incoming even if the CRC is considered bad.
boolean XN297_ReadPayload(uint8_t* msg, uint8_t len)
{ //!!! Don't forget if using CRC to do a +2 on any of the used NRF24L01_11_RX_PW_Px !!!
uint8_t buf[32];
if (xn297_crc)
NRF24L01_ReadPayload(buf, len+2); // Read payload + CRC
else
NRF24L01_ReadPayload(buf, len);
// Decode payload
for(uint8_t i=0; i<len; i++)
{
uint8_t b_in=buf[i];
if(xn297_scramble_enabled)
b_in ^= xn297_scramble[i+xn297_addr_len];
msg[i] = bit_reverse(b_in);
}
if (!xn297_crc)
return true; // No CRC so OK by default...
// Calculate CRC
uint16_t crc = 0xb5d2;
//process address
for (uint8_t i = 0; i < xn297_addr_len; ++i)
{
uint8_t b_in=xn297_tx_addr[xn297_addr_len-i-1];
if(xn297_scramble_enabled)
b_in ^= xn297_scramble[i];
crc = crc16_update(crc, b_in, 8);
}
//process payload
for (uint8_t i = 0; i < len; ++i)
crc = crc16_update(crc, buf[i], 8);
//xorout
if(xn297_scramble_enabled)
crc ^= pgm_read_word(&xn297_crc_xorout_scrambled[xn297_addr_len - 3 + len]);
else
crc ^= pgm_read_word(&xn297_crc_xorout[xn297_addr_len - 3 + len]);
//test
if( (crc >> 8) == buf[len] && (crc & 0xff) == buf[len+1])
return true; // CRC OK
return false; // CRC NOK
}
Please Log in or Create an account to join the conversation.
- goebish
- Offline
- I Void Warranties
- Posts: 2631
Please Log in or Create an account to join the conversation.
- planger
- Offline
A little more work to fully reverse the protocol:planger wrote: The only unknown and needed info at this stage is the 1st byte of the normal packet.
- The 8 RF frequncies in the bind packet are fixed increasing values: 0x11 0x15 0x1A 0x1F 0x24 0x29 0x2E 0x33. (Will test later if they can be anything and in any order...)
- The way to play the frequencies is encoded in the 1st byte of the normal packet (increasing/decreasing/every odd_even/...). It also tells which frequency (or table index) needs to be used next.
Anyway at this stage, I have enough information to code a working protocol with hopefully a random ID and random channels (even if they are played in order for now).
More to come soon.
Please Log in or Create an account to join the conversation.
- planger
- Offline
Ok using the original remote control details but still .
Please Log in or Create an account to join the conversation.
- planger
- Offline
The flag is for high/low rate. Forcing it to high...
Please Log in or Create an account to join the conversation.
- planger
- Offline
a. bit3=0 & bit4=0 => 0 1 2 3 4 5 6 7
b. bit3=0 & bit4=1 => 7 6 5 4 3 2 1 0
c. bit3=1 & bit4=0 => 4 0 5 1 6 2 7 3
d. bit3=1 & bit4=1 => 3 7 2 6 1 5 0 4
Calculation:
a. val=index
b. val=7-index
c. if(index&1) val=index>>1; else val=4+(index>>1)
d. val=7-c
With that the protocol is fully reversed.
What I don't know is the rf channel range to create the initial freq table...
The current table is created like this:
for(uint8_t i=0;i<V911S_NUM_RF_CHANNELS;i++)
hopping_frequency=0x10+i*5;
hopping_frequency[0]++;
In this example 0x10 seems not allowed since they add 1 to it.
0x23 is the bind address so they might avoid it but it's not required since it uses a different ID.
To stay within the original spirit, I'm thinking to use something like this (pseudo code):
rand=random(0xfefefefe)%5; //0-4
for(uint8_t i=0;i<V911S_NUM_RF_CHANNELS;i++)
hopping_frequency=0x10+i*5+rand;
if(!rand) hopping_frequency[0]++;
order=random(0xfefefefe)&0x03; // table read order
Pascal
Please Log in or Create an account to join the conversation.
- planger
- Offline
From the manual there is a yaw calibration mode. When you long press the left button for more than 3 sec (and not 3 press like the doc states...) the heli starts to blink and the original remote blink/beep. This sets packet[2] to 0x01 which I've currently assigned to CH5. It's so well explained that I'm not sure how the calib works...
Also I've noticed that the heli does remember to which ID/freqs it was last bound to. So no need to bind each time.
Please Log in or Create an account to join the conversation.
- Home
- Forum
- Development
- Protocol Development
- protocol for WL Heli V911-s