Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Info Regarding Byte Alignment or Packing Mismatches #847

Closed
JeffRocchio opened this issue Jun 28, 2022 · 8 comments
Closed

Add Info Regarding Byte Alignment or Packing Mismatches #847

JeffRocchio opened this issue Jun 28, 2022 · 8 comments

Comments

@JeffRocchio
Copy link

I have discovered, the hard way, that the ATTiny84/Arduino (tiny84) platform and the Raspberry Pi (RPi) don't pack and unpack their data structures quite the same. What this means is that data sent out from one side may not be correct after receipt and loading into a matching C++ struct on the other side. To many folks this is probably a thing that is so obvious it doesn't bear mentioning. For some of us tho it is a surprise.

My Recommendation:

  1. Add a topic to the 'Common Issues' doc that points this out so that less experienced users will be alerted to this.
  2. Also, in the demo code add a comment that points folks to the topic in Common Issues. For example, in manualAcknowledgements.cpp add a comment where struct PayloadStruct is defined that points this out.

Rationale for Adding Comments in the Demo Code:

After I had gettingStarted working just fine between the tiny84 and the RPi I moved on to manualAcknowledgements. I got that working just fine. And I noted that on both ends both programs used a matching struct to send and receive the dummy data of that demo - a string and one uint8_t. My my use case I needed to send a few long int and two floats. So I added those to both the send and receive side .cpp files. But after doing this, on the RPi, receive, side, I was getting widly incorrect values for my added variables. But they were consistently wrong, so I figured it wasn't bad wiring, radio interference, or the like. I did figure it had to be how the RPi was treating the incoming bytes. But since the examples used matching 'structs', and on the receive side just passed the struct directly to the radio object, as in: PayloadStruct received; radio.read(&received, sizeof(received)); I assumed that this was the proper way to receive in the data. So that then sent me off on all sorts of theories that chewed up a lot of time (like, e.g, possible endian differences). If there had been a warning comment next to the 'struct' definition in the .cpp code then I would have known immediately what was going on.

FYI, My Solution:

In my search I did see any number of ideas concerning getting data cleanly across a network layer between different platforms, beit endian variance or, as I encountered here, byte boundary alignment and/or packing/unpacking. In order to debug the problem I leveraged the richness of the RPi platform to be able to display a lot of stuff on my SSH terminal window. What solved it for me was working out how to get and display the raw byte values in HEX so that I could see, step-by-step, what was going on. That was a whole C++ learning lab just there. But having done that it then became relatively trivial to just receive the incoming data on the RPi side into a uint8_t[], and then write a function that "manually" loads the matching transmit struct from the raw bytes. As in: pStruct->testFloat1 = *(float *)&pBytes[offset]; Of course, if you had an endian mismatch you'd have to do some additional work. And being a novice here, somebody may tell me this is a bad idea for reasons I don't yet understand.

Attached Files For Reference:

SSH terminal output that first showed the problem - CapDataReceive_02b_Output.txt
RPi Side Code For Above - CapDataReceive_02b.cpp
SSH terminal output with issue fixed - CapDataReceive_02c_Output.txt
RPi Side Code for Above - CapDataReceive_02c.cpp

CapDataReceive_02b_Output.txt
CapDataReceive_02c_Output.txt
CapDataReceive_02c_cpp.pdf
CapDataReceive_02b_cpp.pdf

@TMRh20
Copy link
Member

TMRh20 commented Jun 28, 2022 via email

@2bndy5
Copy link
Member

2bndy5 commented Jun 28, 2022

What you're fighting is called memory misalignment. It can be solved by making sure data fits into 32-bit wide boundaries. For example, your test data sends an 11-byte string in the first segment of the payload, but it would be better if this was the last segment. If you look at the raw hex data, you may have noticed that everything after the first c-string is offset by 1 byte.

I love the idea of adding this to the docs and examples' comments. But, it must be done in a way that beginners can understand. Otherwise, they're likely to end up "wasting" precious development time. I'll ask for your review in the PR that aims to solve this request.

For what it's worth, it sounds like your time debugging this wasn't a complete loss: You learned a lot. Python is great for debugging because it is such a high level language that you don't have to pay with pointers (arrays), and you can play with memory contents in realtime (using an interactive REPL - aka "python prompt").

@Avamander
Copy link
Member

Using same-size data types with __attribute__((__packed__)) on your struct would avoid this issue as long as endianness matches. Offset-based decoding is just an ugly way of doing that.

A better choice, if you have the resources, would be to use CBOR or some similar format that has a well-defined way of encoding and decoding.

@JeffRocchio
Copy link
Author

use CBOR

I have run across a reference to this a few times over the past few days. Just did a quick look at it and can see that it is a good answer to this general problem - tho not sure as yet of the added complexity and resource usage. I also see there are several Arduino libraries, some of which are forks of Intel's TinyCBOR. Is there an obvious one to use for the ATTiny84/Arduino do you think?

@JeffRocchio
Copy link
Author

For what it's worth, it sounds like your time debugging this wasn't a complete loss: You learned a lot.

True. And this is the pleasure of hobby projects - no deadlines to get in the way of going down rabbit holes for the fun of it. lol

Python is great for debugging

Python is everywhere it seems. It appears inevitable I'll take it up at some point. I have worked in probably a dozen different languages over my career (work and hobbies) so I know I am pretty good at picking up new languages. For the moment tho I am enjoying this revisit back to C, one of my first languages. And the 'modern' C++ is interesting as I have done OO in other languages; tho to be sure I haven't really exercised the OO capabilities of C++ in this project.

@Avamander
Copy link
Member

Is there an obvious one to use for the ATTiny84/Arduino do you think?

The ATTiny's are probably too memory constrained for that, a "packed" struct would be cheaper.

@JeffRocchio
Copy link
Author

a "packed" struct would be cheaper

Gotcha; and thanks for the tip.

TMRh20 added a commit that referenced this issue Feb 19, 2024
TMRh20 added a commit that referenced this issue Feb 20, 2024
* Docs: Add info on byte alignment/packing mismatches

#847
---------

Co-authored-by: Brendan <2bndy5@gmail.com>
@TMRh20
Copy link
Member

TMRh20 commented Feb 24, 2024

Resolved in #945

@TMRh20 TMRh20 closed this as completed Feb 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants