2

I am communicating with a board that requires I send it 2 signed byte.

explaination of data type
enter image description here what I need to send enter image description here

Would I need to bitwise manipulation or can I just send 16bit integer as the following?

int16_t rc_min_angle = -90; 
int16_t rc_max_angle = 120;

write(fd, &rc_min_angle, 2); 
write(fd, &rc_max_angle, 2);
Lightsout
  • 3,454
  • 2
  • 36
  • 65
  • @CostantinoGrana The manual says "Units: 0,02197265625 degree." I have no idea what those 2 numbers mean but I think its in degrees? – Lightsout Jan 02 '19 at 22:30
  • 0.02197265625 is 45/2048. The units are 45/2048ths of a degree? Be careful! – John Kugelman Jan 02 '19 at 22:32
  • Some compilers have extensions to define variables as either big or little-endian. This is very useful because some architectures, like powerpc, are able to read/write integers in both modes. – fdk1342 Jan 02 '19 at 22:33
  • @JohnKugelman is that comma a typo on the manual? Period would make more sense. – Lightsout Jan 02 '19 at 22:34
  • It's a European-style decimal point, I presume. – John Kugelman Jan 02 '19 at 22:34
  • @JohnKugelman I can't see my original comment anymore. Anyway, I was not too far: it's not radians, but it's fixed point even if I don't get why 45/2048ths of degree instead of a simpler 1/2048ths. – Costantino Grana Jan 02 '19 at 22:41
  • 1
    @bakalolo As John Kugelman said, you have to take your degrees, multiply them by 2048, then divide by 45. So -90 becomes -4096, 120 becomes 5461 and so on. – Costantino Grana Jan 02 '19 at 22:46

4 Answers4

5

int16_t has the correct size but may or may not be the correct endianness. To ensure little endian order use macros such as the ones from endian.h:

#define _BSD_SOURCE
#include <endian.h>

...

uint16_t ec_min_angle_le = htole16(ec_min_angle);
uint16_t ec_max_angle_le = htole16(ec_max_angle);

write(fd, &ec_min_angle_le, 2);
write(fd, &ec_max_angle_le, 2);

Here htole16 stands for "host to little endian 16-bit". It converts from the host machine's native endianness to little endian: if the machine is big endian it swaps the bytes; if it's little endian it's a no-op.

Also note that you have you pass the address of the values to write(), not the values themselves. Sadly, we cannot inline the calls and write write(fd, htole16(ec_min_angle_le), 2).

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
  • Disagree with "Sadly, we cannot inline the calls". Counter example `write(fd, &(uint16_t){htole16(ec_min_angle)}, 2);` – chux - Reinstate Monica Jan 02 '19 at 22:29
  • Similarly, other platforms have hton() and ntoh() functions in their C libraries, to put the byte swapping into the context of network buffer filling/draining https://stackoverflow.com/q/15863775 . "Host to network", and vice versa. – Greg Jan 03 '19 at 02:53
  • 1
    @vvaltchev By _inline_ the answerer likely meant the sense of "writing the call inline" (in one line), not "expanding the called function inline". – user4815162342 Jan 03 '19 at 14:27
  • @user4815162342 I totally agree with you, I just read the text by far too quickly, missing that. I was focused on `htole16()`, while the whole "inline comment" actually was about the interface of `write()`, not `htole16()`. Anyway, it's fun to observe how quickly the discussion went off-topic, with several comments about the "inlining" of the 2nd argument of `write()`, which is totally irrelevant, given the question `:-)` – vvaltchev Jan 04 '19 at 15:28
  • @vvaltchev Good point about the off-topicness - usage of `write()` generated a similar discussion under answer as well. Here it's slightly better because inline expression is not _completely_ off-topic, because it affects the ergonomy. – user4815162342 Jan 04 '19 at 15:59
1

If endian functions are not available, simply write the bytes in little endian order.

Perhaps with a compound literal.

//         v------------- compound literal ---------------v
write(fd, &(uint8_t[2]){rc_min_angle%256, ec_min_angle/256}, 2);
write(fd, &(uint8_t[2]){rc_max_angle%256, ec_max_angle/256}, 2);
//                      ^-- LS byte ---^  ^-- MS byte ---^
//        &

I added the & assuming the write() is a like write(2) - Linux.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

If you don't need to have it type-generic, you can simply do:

#include <stdint.h>
#include <unistd.h>

/*most optimizers will turn this into `return 1;`*/
_Bool little_endian_eh() { uint16_t x = 1; return *(char *)&x; }

void swap2bytes(void *X) { char *x=X,t; t=x[0]; x[0]=x[1]; x[1]=t; }

int main()
{
    int16_t rc_min_angle = -90; 
    int16_t rc_max_angle = 120;

    //this'll very likely be a noop since most machines
    //are little-endian
    if(!little_endian_eh()){
        swap2bytes(&rc_min_angle);
        swap2bytes(&rc_max_angle);
    }

    //TODO error checking on write calls
    int fd =1;
    write(fd, &rc_min_angle, 2); 
    write(fd, &rc_max_angle, 2);
}
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
0

To send little-endian data, you can just generate the bytes manually:

int write_le(int fd, int16_t val) {
    unsigned char val_le[2] = {
      val & 0xff, (uint16_t) val >> 8
    };

    int nwritten = 0, total = 2;
    while (nwritten < total) {
      int n = write(fd, val_le + nwritten, total - nwritten);
      if (n == -1)
        return nwritten > 0 ? nwritten : -1;
      nwritten += n;
    }
    return nwritten;
}

A good compiler will recognize that the code does nothing and compile the bit manipulation to no-op on a little-endian platform. (See e.g. gcc generating the same code for the variant with and without the bit-twiddling.)

Note also that you shouldn't ignore the return value of write() - not only can it encounter an error, it can also write less than you gave it to, in which case you must repeat the write.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
user4815162342
  • 141,790
  • 18
  • 296
  • 355
  • 1
    OK fair enough. Yet if code examines the return value of `write(fd, val_le, 2)`, then why not also the return value of `write(fd, val_le + 1, 1);`? Seems clear enough to just code `return write(fd, val_le, 2);` and let the caller decide on the error handling. – chux - Reinstate Monica Jan 02 '19 at 22:37
  • @chux You're right, examining the second `write()` cannot be avoided. `return write(fd, val_le, 2)` is insufficent because a _valid_ `write()` can return 1, it's simply not an error case. – user4815162342 Jan 02 '19 at 22:44