pascallanger / DIY-Multiprotocol-TX-Module

Multiprotocol TX Module (or MULTI-Module) is a 2.4GHz transmitter module which controls many different receivers and models.
https://www.rcgroups.com/forums/showthread.php?t=2165676&goto=newpost
GNU General Public License v3.0
1.62k stars 435 forks source link

Radiolink protocol support #386

Closed zendes closed 4 years ago

zendes commented 4 years ago

Hi. After completing the Kyosho protocol just now, I'm hoping you're in the mood to add another one.

This time it's a Radiolink RC4GS/RC6GS. These are generic 4/6 channel surface radios with several receivers available. They feature basic telemetry (signal strength & battery voltage), and offer a built-in gyro for stabilization.

I have the RC6GS radio and a few receivers that I can probe: R4FGM(on its way), R6F & R7FG. It's a cc2500 based system, and I was able to find the transmitter module separately.

Is the procedure to start this protocol the same? Attach logic analyzer to all pins, and capture startup, bind, and each of the 6 channels separately?

I have a variable lab bench power supply, so I can also capture the receiver with varying input voltages for the return telemetry if that's any help.

Let me know if you have time and what other information I need to supply to start.

RC6GS transmitter RC6GS main board RC6FG module RC6FG module pins

R7FG receiver R7FG top R7FG bottom

R6F receiver R6F top R6F bottom

pascallanger commented 4 years ago

Go ahead and do the exact same dumps. The Kyosho protocol was easy since the id and hopping table was part of the bind packet. Having access to the RX will be needed if that's not the case. But first do the TX dumps.

zendes commented 4 years ago

Captures attached (at 10MHz). There is no special bind mode on the transmitter, you have it powered up and push&hold a button on the receiver, and that'll bind it. Let me know if you need receiver captures and from which scenarios.

Radiolink RC6GS SPI captures.zip

pascallanger commented 4 years ago

Wow, this one is going to be really hard! I haven't been able to find anything yet... The check at the end looks like a sum but not as simple as that. It hops on 16 frequencies but "randomely", there are 2 bytes in the payload moving all the time which might be linked but no idea how. The channels are on 12 bits but they seem to be scrambled as well...

zendes commented 4 years ago

I'll go make some captures of the simplest receiver I have, both with and without transmitter connected. Maybe that'll help.

Can you share a bit of the process you're using and how you're getting the capture data to be more manageable? I tried looking at it but scrolling through pulseview isn't exactly user friendly.

pascallanger commented 4 years ago

I'll share a readable version of your dump when I'll be in front of my computer. I'm not sure having a RX dump will help but it can't harm to have it. What I think would be useful to get is:

pascallanger commented 4 years ago

Here is the startup file: startup.zip You can load it in excel, do a text to columns and apply filters to extract what you need. At least this is what I'm doing. I would need the above to continue looking at it.

pascallanger commented 4 years ago
0x01 0x3A 0x99 0x22 0x3A 0xFF 0xF3 0x5F 0x2C 0xAA 0x52 0x95 0xAA 0xA0 0x0F 0x00 0xFF 0xFB 0xDF 0xFF 0xFE 0xF7 0xBF 0xFF 0xA1 0x0F 0x7D 0x03 0x00 0x00 0x00 0x01 0x53
REQ ID? ID? ID? ID? ST_7..0 7..4=THR_3..0,3=unknown,2..0=STR_10..8 THR_11..4 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- HOP1 HOP2 -- -- -- Check=SUM(0..31)

REQ=request telemetry from RX when equals to 0x03 otherwise 0x01. RX was not responding so I don't know what the sequence is when it replies. Steering=strange the value looks to be on 11 bits, center 1023, low 170, high 1876. The 12th high bit value changes like if it was in fact the most lower bit... Throttle=12bits ppm format but centered on 1535 (instead of 1500, trim?), low 1109, high 1962. HOP1 and HOP2 are surely the key of the hopping sequence... Check=ok, this is the sum of all payload bytes (messed up my calc first time)

pascallanger commented 4 years ago

I really need a longer dump. HOP1 3..0 is the index of the current RF channel in use from this table {0x12,0xC6,0x66,0x72,0x1E,0x42,0x06,0x4E,0x36,0xAE,0x8A,0xA2,0x2A,0x7E,0x96,0x5A} HOP2 7..4 values are between 0 and 7, all others are between 0 and F

So right now we could send valid frames to the RX but we don't know the hopping freq that needs to be followed to keep the connection going. What I'm afraid is that this is a pseudo random generator and what's being sent is the seed making it nearly impossible to reverse...

pascallanger commented 4 years ago

After spending 16 hours on it, if you want to know how the pseudo random generator is done you would need to offer me a couple of more beers!!! That was a hard one. I can't beleive that I've been able to reverse it in the end. Now that I know the solution, I can see how I could have been way quicker to find it out. So basically, I can now generate a valid frame with the pseudo random generated value and the associated RF frequency. I also know how to inject throttle and somewhat steering. Missing pieces:

zendes commented 4 years ago

Here is a r6f receiver startup dump and a 10 sec dump of the TX & RX on the same logic analyzer, while I slowly move the steering. I've added both SPI decoders, so they should load automatically. If not, the pin mapping is:

TX: 4 mosi, 5 miso, 6 clk, 7 cs RX: 0 mosi, 1 miso, 2 clk, 3 cs

My last donation ID was 3BH75810W9931454U. If we call that "1 beer", let me know how many more you want. Happy to help fund the effort you're putting in! :-)

I have a second transmitter on the way, so once that comes in I should be able to get you another transmitter ID, if that's helpful.

Hope this is enough info. If not, lmk exactly what you need, how long you want the dump to be and what channels you want me to move and I'll get them. I've been out of town for a few days bc girlfriend birthday, but I'm back now.

pascallanger commented 4 years ago

I was kidding about the extra beers ;-) Since you are back, I've looked at the others channel dumps and I'm confused by some of them.. In some the throttle and steering are moving as well as a channel, in others I have more than 1 channel moving and I'm not sure if the channles are centered either... Can you make sure that all your channels are centered (no trim) and there are no mixers when you do dumps?

pascallanger commented 4 years ago
0x01 0x3A 0x99 0x22 0x3A 0xFF 0xF3 0x5F 0x2C 0xAA 0x52 0x95 0xAA 0xA0 0x0F 0x00 0xFF 0xFB 0xDF 0xFF 0xFE 0xF7 0xBF 0xFF 0xA1 0x0F 0x7D 0x03 0x00 0x00 0x00 0x01 0x53
TYPE ID ID ID ID CH CH CH CH CH CH CH CH CH CH CH CH CH CH CH CH CH CH CH CH CH CH HOP1 HOP2 UNK1 UNK2 TABLE CHECK
pascallanger commented 4 years ago
Telemetry packets received from RX 0x00 0x3A 0x99 0x22 0x3A 0x03 0xD3 0x4C 0x64 0x00 0x00 0x00 0x00 0x00 0x31
TYPE-RX ID1 ID2 ID3 ID4 TYPE-TX RX_RSSI RX_BAT VBATT UNK UNK UNK UNK UNK CHECK
pascallanger commented 4 years ago

@zendes Can you do the different dumps:

zendes commented 4 years ago

As requested, here are the dumps. All dumps were created with all channels on 0, where possible: Ch1: steering, analog, 0 Ch2: throttle, analog, 0 Ch3: knob, analog, 0 Ch4: 2pos switch, -80 Ch5: 3pos switch, 0 Ch6: 3pos switch, 0

battery telemetry.zip: I've saved the files under what the TX showed as received battery voltage, in 0.5v steps from 7.0v to 10.0v. This is the main battery pack voltage.

rx battery telemetry.zip: same as above, except this is the BEC output voltage that also goes to the servo's.

rssi telemetry.zip: I've saved the files under an approximation of what the TX showed as received RSSI values. They change rapidly, but never more than a few steps.

rx bind.zip: I did a few "switch receiver to bind mode" (all within the first 1-2 sec of the capture) and one "startup + enter bind mode after about 4 seconds".

tx table.zip: not sure what you meant by table, but I've turned the TX off and back on again in between each of these captures. If that's not what you need, lmk.

zendes commented 4 years ago

Also maybe of note: the telemetry captures were all done using a different RX (R7FG) than the bind ones (R6F). Not sure if that matters, just thought I'd let you know.

zendes commented 4 years ago

Just read something in the manual about a subsidiary ID. It's midnight here, but I'll go dive into that tomorrow. It sounds similar to a receiver ID in FrSky. It can be turned on and off, and when on can have 10 different values. It's used to distinguish between several bound receivers. So far I've had it off, but I'll make some captures tomorrow with all possible values.

pascallanger commented 4 years ago

@zendes The telemetry is now reversed. Check my previous posts for updated details on both TX frames and RX telemetry frames. I'm waiting for:

I've started to write the protocol.

pascallanger commented 4 years ago

I've just looked at the documentation you linked on the 2 TXs. This is pure marketing, under the hood it's completly different...

I've also noticed that the surface TXs can be used with the air receivers with at least the R8EF and R8FM are listed. So my guess is that the T8FB and T8S radios are using the exact same protocol (Note that the opposite is also true on the T8FB page they are also pointing to these surface receivers the R4F and R4FG). To be confirmed...

zendes commented 4 years ago

Fudging numbers that a normal consumer can't validate. Somehow that doesn't surprise me... :-(

About the radio protocol: that looks to be true. Both the surface radios (RC4GS & RC6GS) and the T-series radios (T8FB & T8S) are listed as compatible with all the surface receivers (R4F, R4FGM, R6F, R6FG, R7FG, R8F) and the T-series receivers (R8EF, R8FN and R8SM). There's probably nothing "surface" or "air" about the protocol, they've just built a flavor that works for them and re-used it in as many products as they could.

Anyhow, I've figured out what they mean with the whole subsidiary ID. It's indeed a receiver ID, but when turned off it works similar to a wildcard so all bound receivers (irrespective of their bound ID) work. I've tested this by turning it on with ID 1, binding a receiver, then switching it to ID 2 and binding another one. When the feature is on, only the receiver bound to that ID works. When it's disabled, both receivers operate at the same time (wildcard). This might be the 4th byte in the ID field you found. I've attached a set of captures with the receiver ID enabled, one each for several of the IDs. RX IDs.zip

Failsafe: this feature works 100% from the TX, no need to rebind/push a button/do anything on the RX. You set the desired position in the menu, press enter and if you 1 sec after that turn of the TX the RX will move the channel to the set position. I've attached captures of me setting one channel and then exiting the menu, and me setting multiple channels before exiting. This will hopefully show you if each channel gets sent out individually or if the TX sends a single "failsafe value for every channel" packet. failsafe settings.zip

Lastly: I've hooked up the receiver to an SBUS decoder, and I can confirm that ch8 indeed follows ch3. This isn't actually what's going on though: ch8 is the gyro sensitivity, and the default TX config is for the ch3 knob to also control that. If I set the gyro in the TX menu to fixed 50%, I see ch8 go to 0 and it no longer moves with the ch3 knob. If I set it to variable, turning the ch3 knob also moves ch8. I think what you should get from all of this is that ch8 works the same as the other channels, and any coupling is TX config. It might be something worth mentioning on the wiki though, that ch8 is gyro sensitivity. Once we have the protocol done I'll send you a PR for updating the docs if that's ok.

zendes commented 4 years ago

About the last 3 UNK bytes in the telemetry packet: I think those are for future expansions. There are RATE: 000rpm and T:NULL fields on the TX telemetry page, but the manual has the following note: The 'RATE: RPM' and 'T: NULL' telemetry functions are still under development. If there are new developments, we will announce them to the Radiolink official website as soon as possible, thanks for your attention.. There also isn't an RPM sensor available for these receivers.

TX telemetry page

pascallanger commented 4 years ago

For the RX_ID, they use the 1st byte TYPE to send it. if(RX_ID) TYPE |= ((RX_ID+2)<<4)+4; But I'm not sure if I should use it... On multi I'm modifying directly the TX global ID with the RX number so it appears like a different TXs to the RX. You have up to 64 RX numbers available. Here that would limit the number of RX numbers to 10 which might be enough but... Let me know what you think. I would need one dump with the ID activated and a RX on to see the telemetry back to the radio. I'm expecting TYPE-RX to be different from 0 may be 0x34 for ID=1.

pascallanger commented 4 years ago

The failsafe channels are sent all the time. They are in fact the extra 8 channels being transmitted. CH1-8 are normal channels and CH9-16 are associated failsafe values.

pascallanger commented 4 years ago

I've pushed the RadioLink protocol on Master v1.3.1.44 and the doc https://github.com/pascallanger/DIY-Multiprotocol-TX-Module/blob/master/Protocols_Details.md#radiolink---74 By default I've left #define RLINK_FORCE_ID enabled meaning that it will use the ID from your current radio. Test like this and tell me how it goes. If it works then comment that line (add //) and test again. Telemetry should also work. I'm not sure of the timing but let's see.

zendes commented 4 years ago

Will do. I recently got a Radiomaster TX16S, which has an internal module. I previously installed firmware by plugging my module in via USB and just flashing it via Arduino IDE, but OpenTX doesn't like the compiled file to flash via the TX. Is there a different procedure to compile/prep so OpenTX can flash the internal module?

zendes commented 4 years ago

This protocol only supports 10 IDs and one wildcard. Are you saying that your way of setting the RX ID would only support the 10 IDs but not the wildcard? I was hoping to be able to use ID 0 as the wildcard, and then 1-10 for the specific RX IDs, but I'm not sure that's a big loss tbh. You can always just bind everything with the same RX ID and still get the "wildcard" behaviour.

pascallanger commented 4 years ago

To flash through OpenTX you must use in Arduino the "Export compiled Binary" under Sketch. Make sure to compile with #define INVERT_TELEMETRY commented.

pascallanger commented 4 years ago

This protocol only supports 10 IDs and one wildcard. Are you saying that your way of setting the RX ID would only support the 10 IDs but not the wildcard? I was hoping to be able to use ID 0 as the wildcard, and then 1-10 for the specific RX IDs, but I'm not sure that's a big loss tbh. You can always just bind everything with the same RX ID and still get the "wildcard" behaviour.

What I'm saying is that there are 2 ways to implement it:

pascallanger commented 4 years ago

Still no test?

zendes commented 4 years ago

It's 2pm here, so I'm at work. I'll be able to test once I get off, so in about 3-4 hours.

pascallanger commented 4 years ago

Nearly midnight here so I will know only tomorrow. Please give as much as details as you can if it doen't work properly.

zendes commented 4 years ago

Step 1: TX & RX with ID set.zip

zendes commented 4 years ago

First test, with RLINK_FORCE_ID defined: bind succeeds, and channels move as expected. I get RSSI telemetry and both voltages in OpenTX.

Issues/observations:

  1. Both voltages are 50% of what they should be. Bench PSU at 10.0v, radio shows 5.1v. Bench PSU at 4.0v, radio shows 2.0v.
  2. Rx Battery voltage shows up as sensor A1 instead of RxBt.
  3. RSSI shows a value of 225 with no unit, while the RadioLink TX shows -30dBm. An X8R connected to OpenTX shows 80dB.
  4. I first through the servo moved more "jittery", but I'm not sure this is really a thing. Thought I would mention it, but maybe ignore this one for now until I have more data/find out I was wrong.
zendes commented 4 years ago

Second test, with RLINK_FORCE_ID commented out: bind also succeeds, and channels again move as expected. Same issues as above.

I now believe issue 4 is because of dropped frames between the TX & RX, as I sometimes get "telemetry lost" from the TX. Tried with and without low power mode, and I've also done the cc2500 fine tune which did not improve the situation.

I've done a 10sec RX capture of me slowly sweeping ch1, and where the servo clearly moved in "chunks". I've also done a capture of the RX startup while having OpenTX powered up, in case that's useful. v1.3.1.44 captures.zip

zendes commented 4 years ago

To answer your other question about RX ID: no strong opinion, and I don't think it functionally matters to a user. This isn't really a "killer feature" on this system anyway. They sell it as "if your first boat gets stuck, you enable this and send out a second boat to rescue it while the first one is still powered up". You can accomplish the same thing by first binding everything to ID 1 and when necessary re-binding to ID 2. I'd say pick whatever feels more natural in the code.

zendes commented 4 years ago

I didn't mention this, but both times (with and without #define RLINK_FORCE_ID) I was able to bind to both my R6F and R7FG receivers.

pascallanger commented 4 years ago

Ok that's a good start. Telemetry values are as expected. I'll fix the half voltage values. The naming and units are what they are since I'm using frskyd telemetry which I think is ok. Check the doc that I've written on this proto.

pascallanger commented 4 years ago

I've just pushed v1.3.1.45 which is fixing the voltage values.

pascallanger commented 4 years ago

I'm a little confused...

Next we need to find out what makes the freq table change. I'll need a number of dumps from the RX to figure this out.

pascallanger commented 4 years ago

Hummm this is what I was thinking: With the ID 0x3A,0x99,0x22,0x3A the table is 0x12,0xBA,0x66,0x72,0x1E,0x42,0x06,0x4E,0x36,0xAE,0x8A,0xA2,0x2A,0x7E,0x96,0x5A With the ID 0x62,0x4D,0xF9,0x27 the table is 0x06,0x36,0x5A,0x1E,0x72,0x2A,0x66,0x8A,0x42,0x4E,0xBA,0xA2,0x96,0x7E,0x12,0xAE

When the 1st table is used with the second ID (what is done right now in the code), then only 3 frequencies are in common explaining the large quantity of missing frames. Now we need to understand how the ID influence the table.

pascallanger commented 4 years ago

Table in another format:

0x3A 0x99 0x22 0x3A 1 15 8 9 2 5 0 6 4 14 11 13 3 10 12 7
0x62 0x4D 0xF9 0x27 0 4 7 2 9 3 8 11 5 6 15 13 12 10 1 14
pascallanger commented 4 years ago

Staying with FORCE_ID enabled, Do different dumps by changing the ID on this line: https://github.com/pascallanger/DIY-Multiprotocol-TX-Module/blob/75dc616130bfe854c2d684c3f9ae825092c0d85f/Multiprotocol/RadioLink_cc2500.ino#L44 Since we do not have overlap due to the timezone, I'm giving you a long list of dumps to do on the RX with:

The goal is to see if all bytes of the ID are used to build this table and also get some values. There is nothing anymore I can do right now... There are 2.092279e+13 possibilities to arrange that table of 16 frequencies... I'm not sure we can reverse it if it's truly pseudo random...

zendes commented 4 years ago

First test using v1.3.1.45, no modifications (so RLINK_FORCE_ID defined), multi module in low power mode

I'll see how many ID dumps I can get you today...

pascallanger commented 4 years ago

TQly of 20 means that all the packets from the RX are received. I forgot that the telemetry is every 5 packets... I'll correct that to be 100..0%.

zendes commented 4 years ago

It's about 1am here, so I'm calling it a night. I went through your list and only got a bind on \x0F\x00\x00\x00, so then I tried some other IDs and from my first tests it looks like the first byte needs to be either 0x0F or 0x0E. Right now I have captures of 5 different IDs that bind. Let me know which other IDs you want me to try.

\x00\x00\x00\x00: doesn't bind
\x00\x00\x00\x01: doesn't bind
\x00\x00\x01\x00: doesn't bind
\x00\x01\x00\x00: doesn't bind
\x01\x00\x00\x00: doesn't bind
\x00\x00\x00\x02: doesn't bind
\x00\x00\x02\x00: doesn't bind
\x00\x00\x00\x0F: doesn't bind
\x00\x00\x0F\x00: doesn't bind
\x00\x0F\x00\x00: doesn't bind
\x0F\x00\x00\x00: binds, but about every sec loses telemetry
\x00\x00\x00\x10: doesn't bind
\x00\x00\x10\x00: doesn't bind
\x00\x10\x00\x00: doesn't bind
\x10\x00\x00\x00: doesn't bind
\x1F\x00\x00\x00: doesn't bind
\x2F\x00\x00\x00: doesn't bind
\x0E\x00\x00\x00: binds, but about every other sec loses telemetry
\x0D\x00\x00\x00: doesn't bind
\x0C\x00\x00\x00: doesn't bind
\x0E\x01\x00\x00: binds, but about every other sec loses telemetry
\x0E\x02\x00\x00: binds, but about every 2-4 sec loses telemetry
\x0E\x03\x00\x00: binds, and doesn't lose telemetry (no opentx warning)

TX Addresses.zip

pascallanger commented 4 years ago

If you still have a little time, can you try 0e 00 03 00 and 0e 00 00 03?

pascallanger commented 4 years ago
From the dumps I've been able to extract only this: ID 3A 99 22 3A Table 1 15 8 9 2 5 0 6 4 14 11 13 3 10 12 7
ID 62 4D F9 27 Table 0 4 7 2 9 3 8 11 5 6 15 13 12 10 1 14
ID E 0 0 0 Table 5 9 14 4 13 1 8 10 3 2 6 12 7 11 15 0
ID E 1 0 0 Table 14 4 1 3 0 9 7 10 15 8 5 2 11 12 6 13
ID E 2 0 0 Table 5 9 15 1 13 10 6 0 8 14 3 12 7 4 2 11
ID E 3 0 0 Table 1 9 13 4 8 3 7 10 11 14 6 5 0 15 12 2

All the others do not have any usefull information...

May be we should have done it differently: ID 3A 99 22 3A
ID 3B 99 22 3A
ID 3A 9A 22 3A
ID 3A 99 23 3A
ID 3A 99 22 3B

The first line is your original radio id so don't try it but see what happens with the 4 other lines.

pascallanger commented 4 years ago

Not sure if you are used to powershell but I'm attaching the script I'm using: CC2500_pv_chan.zip To use it:

zendes commented 4 years ago

TX IDs similar to original.zip

I'm using an OSX system, so no powershell unfortunately. I was able to find a faster way of getting the captures, so I took some time during lunch today & did the ones you asked for.

If you think there's value in getting a bunch more, I'll take a crack at automating some of this. Can you let me know what ID range you'd like the table of?

pascallanger commented 4 years ago

I can't check the files until tomorrow unfortunately... What I'm wondering is if there is a loop happening at some stage or if it's a pure pseudo random output.

If you can automate, then may be we could get 256 consecutive samples. To go faster without reflashing the module, you can use the rx number. Just replace the last byte of your ID by RX_num and then in the GUI change the value. The protocol will immediately switch to the new value.

All I need from my point of view is the annotation text export from pulseview.