Skip to content

Proper unsigned value support for CRSF Telemetry#7066

Open
CapnBry wants to merge 1 commit intoEdgeTX:mainfrom
CapnBry:crsf-unsigned
Open

Proper unsigned value support for CRSF Telemetry#7066
CapnBry wants to merge 1 commit intoEdgeTX:mainfrom
CapnBry:crsf-unsigned

Conversation

@CapnBry
Copy link
Copy Markdown
Contributor

@CapnBry CapnBry commented Feb 2, 2026

Alters the deserialization of CRSF telemetry to properly support unsigned values, and removes the check for values being -1.

Details

The CRSF telemetry parser currently unpacks every value as signed, regardless of what the spec says for that parameter. There are unsigned values in the spec though, and the current code (going all the way back) does not present them properly. For example the GPS packet's heading value is unsigned and that means that any value greater than 327.67 degrees shows up as negative. Betaflight telemetry example:

screen-2000-01-01-000131

In addition, the current code checks for values where every byte sent is 0xff and discards the value if that condition is met. There is nothing in the CRSF spec that says "send 0xff... to not update the value", the only mention is on the 0x09 Barometric Altitude description where the example contains a comment stating "OpenTX counts any 0xFFFF [altitude] value as incorrect". Due to this code being used always, any telemetry parameter anywhere will ignore 0xff, 0xffff, 0xffffff, or 0xffffffff. This has been removed for also not matching the CRSF spec.

I also removed the template function used to do the parsing. I am not sure who was like ooh this is gonna really optimize performance when the compiler unrolls that loop for me! It's stupid, just pass the size.

Now-Unsigned Telemetry Items

  • GPS - Ground Speed, Heading, Altitude (uses -1000 offset), satellites
  • Baro Alt - Altitude (encoded)
  • Airspeed
  • RPM - Sensor ID
  • Temp - Sensor ID
  • Cells/Voltages - Sensor ID, Voltage (in both types)
  • Link RX - RSSI percentage, RF Power dBm
  • Link TX - RSSI percentage, RF Power dBm, FPS
  • Battery - "Capacity" (mAh consumed), Percent remaining
  • CRSFShot / OpenTX Sync - Update Interval

See? It works! Code size is also reduced by this change by 192 bytes.

screen-2000-01-01-000131-after

@3djc
Copy link
Copy Markdown
Collaborator

3djc commented Feb 2, 2026

To be fair, initial CRSF support was done with minimal documentation.

@pfeerick
Copy link
Copy Markdown
Member

pfeerick commented Feb 2, 2026 via email

@CapnBry
Copy link
Copy Markdown
Contributor Author

CapnBry commented Feb 2, 2026

Wasn’t the initial support done by or in collaboration with TBS though?

Yeah it was done by a third party working for TBS I believe. I also think of this implementation as the reference, but also nobody still around knows why they were coded the way they were. I do believe that the -1 thing was unintentional baggage from an early prototype implementation, because no FC software I am aware of (Betaflight, iNav or Ardupilot) sends -1 to prevent a field from updating. I don't believe TBS sends internal telemetry (LINK_RX/TX) with -1 in the values, else it would be mentioned in the spec.

But! This is all just researched speculation on my part. As far as the non-TX-to-Handset telemetry items, it seems that that's all but guaranteed by the FC implementations listed. There could be some obscure legacy hardware I'm not aware of though.

@bkleiner
Copy link
Copy Markdown

bkleiner commented Feb 27, 2026

As far as i can tell no crossfire version available today uses this mechanic, to be frank, i don't think it makes a lot of sense to begin with, if one doesn't want update, one might as well just re-send the last value.

As for its origin, appears it was somewhat haphazardly introduced here.

Copy link
Copy Markdown
Member

@raphaelcoeffic raphaelcoeffic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For RSSI1/RSSI2/SNR fields, a stale/no-data byte of 0xFF was previously suppressed; it will now show as -1 in telemetry widgets during link-down transitions. Cosmetic rather than functional.

This position is defensible (it's not in spec), but worth being aware of for release notes.

Comment on lines +175 to 184
value = getCrossfireTelemetryValue(3, rxBuffer, 2, false);
if (value & 0x8000) {
// Altitude in meters
value &= ~(0x8000);
value *= 100; // cm
} else {
// Altitude in decimeters + 10000dm
value -= 10000;
value *= 10;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about just adding this to avoid regressions?

  value = getCrossfireTelemetryValue(3, rxBuffer, 2, false);
  if (value == 0xFFFF) break;   // spec: sentinel for invalid

Since the spec says something like: "OpenTX counts any 0xFFFF [altitude] value as incorrect".

Comment on lines +360 to +364
// values are in 10th of micro-seconds
uint32_t update_interval = getCrossfireTelemetryValue(6, rxBuffer, 4, false) / 10;
int32_t offset = getCrossfireTelemetryValue(10, rxBuffer, 4, true) / 10;
//TRACE("[XF] Rate: %d, Lag: %d", update_interval, offset);
getModuleSyncStatus(module).update(update_interval, offset);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Old code required both update_interval AND offset to have at least one non-0xFF byte before calling getModuleSyncStatus(module).update(...). New code always calls it.

If a malformed/initial frame with all-0xFF ever shows up, update_interval = 0xFFFFFFFF / 10 ≈ 429M µs ≈ 7 min would be fed to the mixer scheduler. Depending on how update() clamps, this could skew the refresh period badly.

Probably not observed in practice (ELRS/TBS likely don't emit that), but the old defensive check cost nothing. Consider keeping a bounds check (e.g. if (update_interval && update_interval < REASONABLE_MAX)).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants