How does the async nature of RVR notifications work?

There are several places where there are “notify” events: e.g., motor fault notifications or color detection notifications. I assume these events are asynchronous…but how do they actually work? Do they just pop data in the output stream when they happen?

I’ve been building a C library for the RVR interface on a Raspberry Pi. For sensors, I spawn a separate thread to handle the sensor stream. But that works because the RVR will stream data until it’s told to stop. If the notify events push out data between other data outputs, it will require a different way to do it.

Thanks.

1 Like

Yes, the RVR sends notifications and streaming data intermixed with responses from requests.

I’ve diagrammed my best guess at how this works. I"m doing a C++ reverse engineering of the protocol and documenting it in a Wiki.

3 Likes

I’m going at it a little different. I have created a C# program with a form that has most of the functions broken out on it.
You can use the program to single step through the code and see how things are assembled and sent out to the RVR. I have also broken down the incoming stream data into the many parts.
Streaming seems a little confusing to me as there is not enough data coming back to identify what stream it is. So if you enable all the sensor streams it would be impossible to determine what sensor data you are getting back.
Any way I have a working C# program up on GitHub C# Form Project

Mike

1 Like

Sensor streaming works by enabling a sensor and then telling it how often you want it to report back the data when you start it. The sensor data then comes back as a binary value that has to be scaled back to the actual sensor values.

Mike

1 Like

Reported sensor data has a token specified in the configuration. See my Wiki Streaming page. Just finished it last night. May sill have some rough spots.

2 Likes

The documentation only show 3 different values for token so that is not very useful if you want to stream Accelerometer and IMU with Gyro unless the token can be any value from 1 to 255 which is not clear from the documentation.

Mike

1 Like

My testing shows the token values can be more than what is shown. Code base is under reorg so can’t check this in detail. But I used a token of 2 and a token of 7 for ambient light and the raw values were the same. Part of the reorg is how I calculate final value.

1 Like

I was basing the token on the code in the control program. Just tried assigning tokens to each sensor and it works just fine. So multiple tokens can be used to identify different streams coming back from RVR.

Mike

1 Like

What I’ve done is make the token the same as the last byte in the Id field. Only hassle is the Quaternion sensor since its has an Id of zero.

2 Likes

My recent testing is having a problem with this. What I’m seeing is if I assign 4 tokens to the BluetoothSOC any additional tokens are rejected with an Error 6 - failed for command specific reason.

My interpretation now is there are 4 slots for tokens on each processor. The token itself can be any value within some limit but only 4 tokens can be active. The sensor table list tokens makes more sense now. I’ll probably organize differently, though.

Also pretty sure that only the lower nybble can be used. Had a token coming back with 0x1n because there was an error with the sensor. May have been the color detector because detection wasn’t enabled.

@Sphero_JimK, can you help us out here?

1 Like

Hi @rmerriam,

Sorry for the long delay in responding, I’ve been on vacation for a while. I haven’t worked on sensor streaming myself, but I’ll do my best to fill things in a bit for now, until we can publish more complete documentation.

You are correct that there are 4 slots. Each slot, termed a “configuration slot”, is basically a grouping of streaming services for a particular client (the client is outside of the robot). The tokens in the table you found in the docs are really only applicable to the SDK controls concept and the way the SDK groups sensors.

For doing it yourself, you can configure whatever sensors you want in a particular configuration slot, and each slot will send out a packet on the interval you specified. Streaming packets for individual configuration slots are evenly spaced during the streaming interval to reduce stackup of large packets, i.e. if you configure streaming at 500 ms with 2 slots, you’ll get a packet from one of the slots every 250 ms.

Correct, the token ID you provide is only the lower nibble of that byte in a streaming packet. The upper nibble contains status bits, and as you found, 0x1 means that at least one service could have a problem with its data. For example, you would get this if you configured color sensor streaming but didn’t enable color detection. 0x1 is currently the only implemented error bit.

I’m pushing to publish more thorough documentation of streaming, but don’t know when that will be ready.

-Jim

1 Like

Jim,

Thanks for the information. I am testing with a 30 msec streaming period and found some anomalies. I’ll summarize:

  1. Streaming one item generates regular 30 msec notifications
  2. Streaming two items generates regular 40 msec notifications 20 msec apart.
  3. Streaming three items generates regular 30 msec notifications 20 msec apart.

The three streams are:

  1. Speed Velocity Locator - observation 1, 2 & 3
  2. Imu Accel Gyro - observation 2, 3
  3. Quaternion - observation 2, 3

It didn’t matter which 2nd stream was added.

The observation with 2 streams is bothersome since specifying a 30 msec update rate for streaming isn’t assured.

Additional quick tests at 50 and 100 msec get a 60 msec and 50 msec update rate, respectively, for 2 streaming sets.

What is the maximum rate for notifications?

Raw data below.

Rud

Main is my processing loop, operator is where a response is processed.
XX/YY - interval from previous notification, XX is any and YY is for same token

Speed Velocity Locator

200103.164518.748: main locator: 0.0000 0.0479
200103.164518.756: operator() pkt: 28 2 18 3D FF D 9 96 56 50 7F FF FF 0 84 CB 2B 0 80 0 0 0 80 0 1C 0 

200103.164518.778: main locator: 0.0000 0.0537
30 msec
200103.164518.786: operator() pkt: 28 2 18 3D FF D 9 79 DB 60 7F FF E2 80 84 BC EE 0 80 0 0 0 80 0 1F 0 
30 msec

200103.164518.809: main locator: 0.0000 0.0596
31 msec
200103.164518.817: operator() pkt: 28 2 18 3D FF D 9 E5 9F 50 7F FF ED 0 84 F2 D0 0 80 0 0 0 80 0 22 0 
31 msec

200103.164518.839: main locator: 0.0000 0.0645
30 msec
200103.164518.846: operator() pkt: 28 2 18 3D FF D 9 42 5 E0 7F FF F6 80 84 A1 3 0 80 0 0 0 80 0 24 0 
29 msec

------------
Speed Velocity Locator
Imu Accel Gyro
200103.165312.984: main locator: 0.0000 0.0000
200103.165313.000: operator() pkt: 28 2 18 3D FF D 5 1 77 C0 7F FF F7 0 82 80 BC 0 80 0 0 0 80 0 3 0 

200103.165313.014: main locator: 0.0000 0.0059
30 msec
200103.165313.022: operator() pkt: 28 2 18 3D FF C 80 7E A3 0 7F A9 17 80 80 0 D2 0 7F 9D DB 0 7F 5F 7C 0 87 E6 B7 0 80 2D 83 0 7F EA B3 0 80 9 A5 0 
22/ msec
200103.165313.040: operator() pkt: 28 2 18 3D FF D B B 85 50 80 0 13 0 85 85 C2 0 80 0 0 0 80 0 8 0 
18/40 msec

200103.165313.044: main locator: 0.0000 0.0156
30 msec
200103.165313.062: operator() pkt: 28 2 18 3D FF C 80 CD B9 0 7F A5 11 0 80 0 B0 0 80 2C 94 0 80 63 D4 0 87 FD 14 0 80 2E 64 0 80 27 97 0 80 6 1 0 
40 msec

200103.165313.074: main locator: 0.0000 0.0156
30 msec
200103.165313.080: operator() pkt: 28 2 18 3D FF D A 11 4E 70 7F FF E8 0 85 8 A7 0 80 0 0 0 80 0 C 0 
18/40 msec

200103.165313.102: operator() pkt: 28 2 18 3D FF C 81 1D 25 0 7F 83 8F 80 7F FF AB 80 7F 69 67 80 7E 88 51 0 87 91 3C 0 80 25 86 0 80 7 DC 0 80 0 0 0 
22/40 msec

200103.165313.104: main locator: 0.0000 0.0225
30 msec
200103.165313.120: operator() pkt: 28 2 18 3D FF D 9 D5 A7 20 7F FF FA 80 84 EA D3 0 80 0 0 0 80 0 10 0 
18/40 msec

200103.165313.134: main locator: 0.0000 0.0303
30 msec
200103.165313.142: operator() pkt: 28 2 18 3D FF C 81 51 26 0 7F 7D CD 80 80 0 2D 0 7F BF 2D 80 7F 8E 89 80 87 E7 DC 0 80 18 82 0 80 8 5E 0 80 0 0 0 
22/40 msec

200103.165313.160: operator() pkt: 28 2 18 3D FF D A 37 B4 70 80 0 0 0 85 1B DA 0 80 0 0 0 80 0 14 0 
18/40 msec

-------------------
Speed Velocity Locator
Imu Accel Gyro
Quaternion
200103.170017.191: main locator: 0.0000 0.0000
200103.170017.192: operator() pkt: 28 2 18 3D FF D 13 B1 11 40 80 0 2 0 89 D8 89 0 80 0 0 0 80 0 2 0 
200103.170017.203: operator() pkt: 28 2 18 3D FF B FF FF 75 0 80 BB AB 0 80 17 9C 0 80 0 A3 0 
11/ msec
200103.170017.214: operator() pkt: 28 2 18 3D FF C 80 85 D8 0 80 18 C3 0 80 1 3F 0 7E 95 EB 80 7D CC 4D 0 84 D1 DD 0 80 A0 95 0 7F F6 B3 0 80 1B 33 0 
11/ msec

200103.170017.221: main locator: 0.0000 0.0039
30 msec
200103.170017.222: operator() pkt: 28 2 18 3D FF D F DE 37 D0 7F FF 97 0 87 EF 1C 0 80 0 0 0 80 0 6 0 
/30 msec
200103.170017.232: operator() pkt: 28 2 18 3D FF B FF FF 80 0 80 B4 7E 0 80 0 95 0 80 0 5F 0 
10/29 msec
200103.170017.244: operator() pkt: 28 2 18 3D FF C 80 83 6E 0 7F FA F6 0 80 1 4A 0 7E E1 70 0 7E A8 36 0 86 70 C5 0 7F 86 FC 80 7F 76 F9 0 80 14 96 0 
12/30 msec

200103.170017.251: main locator: 0.0000 0.0117
30 msec
200103.170017.252: operator() pkt: 28 2 18 3D FF D C 71 7B 0 7F FF D5 80 86 38 BE 0 80 0 0 0 80 0 9 0 
/30 msec
200103.170017.262: operator() pkt: 28 2 18 3D FF B FF FE 67 0 81 43 30 0 80 14 6A 0 80 3 D4 0 
10/30 msec
200103.170017.274: operator() pkt: 28 2 18 3D FF C 80 CB D2 0 80 5 7F 0 7F FE 80 0 7F 9F D 0 7F 80 B4 80 87 5E F9 0 80 69 75 0 80 14 BF 0 80 1F D4 0 
12/30 msec

-------------------
Speed Velocity Locator
Quaternion
200103.171223.843: main locator: 0.0000 0.0195
30 msec
200103.171223.845: operator() pkt: 28 2 18 3D FF D A FA B2 E0 7F FF E0 0 85 7D 5A 0 80 0 0 0 80 0 F 0 
200103.171223.864: operator() pkt: 28 2 18 3D FF B FF FB 9A 0 82 11 5 0 7F A3 13 80 7F FF 7B 0 
19/msec

200103.171223.873: main locator: 0.0000 0.0283
30 msec
200103.171223.885: operator() pkt: 28 2 18 3D FF D A F FF B0 7F FF D4 0 85 8 0 0 80 0 0 0 80 0 13 0 
21/40 msec

200103.171223.904: main locator: 0.0000 0.0361
31 msec
200103.171223.904: operator() pkt: 28 2 18 3D FF B FF FA 42 0 82 5D 17 0 7F 9B 45 0 80 0 8E 0 
19/40 msec
200103.171223.925: operator() pkt: 28 2 18 3D FF D A 6 3 A0 7F FF E9 80 85 3 2 0 80 0 0 0 80 0 17 0 
21/40 msec

200103.171223.934: main locator: 0.0000 0.0439
30 msec
200103.171223.945: operator() pkt: 28 2 18 3D FF B FF F8 13 0 82 C8 83 0 7F 93 5A 80 7F FF 27 0 
20/40 msec
200103.171223.965: operator() pkt: 28 2 18 3D FF D A D1 BA C0 7F FF EB 0 85 68 DE 0 80 0 0 0 80 0 1B 0 
20/40 msec
200103.171223.985: operator() pkt: 28 2 18 3D FF B FF F6 82 0 83 C C0 0 7F 8F A5 80 7F FF 3C 80 
20/40 msec

1 Like

Thanks for the detailed report, @rmerriam.

I’ll file your findings in a ticket for the right person to look into it in more depth. The streaming module itself updates on a nominal 10 ms interval, which certainly makes even time division of 30ms/2 impossible, and perhaps rounding produces the 20 ms spacing from observation 2. I’m not sure about observation 3.

I’d suggest trying to just use one token for now if your system can handle receiving large packets. See if that makes things more predictable.

-Jim

1 Like