tags:

views:

401

answers:

6

This is related to my question asked here today on SO. Is there a better way to build a packet to send over serial rather than doing this:

unsigned char buff[255];

buff[0] = 0x02
buff[1] = 0x01
buff[2] = 0x03

WriteFile(.., buff,3, &dwBytesWrite,..);

Note: I have about twenty commands to send, so if there was a better way to send these bytes to the serial device in a more concise manner rather than having to specify each byte, it would be great. Each byte is hexadecimal, with the last byte being the checksum. I should clarify that I know I will have to specify each byte to build the commands, but is there a better way than having to specify each array position?

?

A: 

const char *c = "\x02\x02\x03";

jspcal
You're losing const-correctness there.
Tom
+8  A: 

You can initialize static buffers like so:

const unsigned char command[] = {0x13, 0x37, 0xf0, 0x0d};

You could even use these to initialize non-const buffers and then replace only changing bytes by index.

joshperry
Pretend he said const unsigned char ...
Richard Pennington
haha, yeah took this from a previous answer I had done where he had a typedef... It should be char.
joshperry
+8  A: 

Not sure what you're asking. If you ask about the problem of setting the byte one by one and messing up the data, usually this is doen with a packed struct with members having meaningful names. Like:

#pragma push(pack)
#pragma pack(1)

struct FooHeader {
  uint someField;
  byte someFlag;
  dword someStatus;
};

#pragma pack(pop)

FooHeader hdr;
hdr.someField = 2;
hdr.someFlag = 3;
hdr.someStatus = 4;

WriteFile(..., sizeof(hdr), &hdr);
Remus Rusanu
reading more of the machine's documentation, it only accepts unpacked messages. However, this educated me more on packing bytes so thank you for your answer
0A0D
+1  A: 

Yes, there is a better method. Have your classes read from and write to a packed buffer. You could even implement this as an interface. Templates would help to.

An example of writing:

template <typename Member_Type>
void Store_Value_In_Buffer(const Member_Type&, member,
                           unsigned char *& p_buffer)
{
  *((Member_Type *)(p_buffer)) = member;
  p_buffer += sizeof(Member_Type);
  return;
}

struct My_Class
{
    unsigned int datum;

    void store_to_buffer(unsigned char *& p_buffer)
    {
      Store_Value_In_Buffer(datum, buffer);
      return;
    }
};

//...
unsigned char    buffer[256];
unsigned char *  p_buffer(buffer);
MyClass object;
object.datum = 5;
object.store_to_buffer(p_buffer);
std::cout.write(p_buffer, 256);

Part of the interface is also to query the objects for the size that they would occupy in the buffer, say a method size_in_buffer. This is left as an exercise for the reader. :-)

Thomas Matthews
+2  A: 

Is there a better way to build a packet than assembling it byte by byte?

Yes, but it will require some thought and some careful engineering. Many of the other answers tell you other mechanisms by which you can put together a sequence of bytes in C++. But I suggest you design an abstraction that represents a part of a packet:

class PacketField {
    void add_to_packet(Packet p);
};

Then you can define various subclasses:

  • Add a single byte to the packet
  • Add a 16-bit integer in big-endian order. Another for little-endian. Other widths besides 16.
  • Add a string to the packet; code the string by inserting the length and then the bytes.

You also can define a higher-order version:

PacketField sequence(PacketField first, PacketField second);

Returns a field that consists of the two arguments in sequence. If you like operator overloading you could overload this as + or <<.

Your underlying Packet abstraction will just be an extensible sequence of bytes (dynamic array) with some kind of write method.

If you wind up programming a lot of network protocols, you'll find this sort of design pays off big time.

Edit: The point of the PacketField class is composability and reuse:

  • By composing packet fields you can create more complex packet fields. For example, you could define "add a TCP header" as a function from PacketFields to PacketFields.

  • With luck you build up a library of PacketFields that are specific to your application or protocol family or whatever. Then you reuse the fields in the library.

  • You can create subclasses of PacketField that take extra parameters.

It's quite possibly that you can do something equally nice without having to have this extra level of indirection; I'm recommending it because I've seen it used effectively in other applications. You are decoupling the knowledge of how to build a packet (which can be applied to any packet, any time) from the act of actually building a particular packet. Separating concerns like this can help reuse.

Norman Ramsey
so if the Packet class contains the actual packet and the write method, which writes it to the serial port, what is the point of the PacketField class? Why not just do `p.addBytestoPacket()` then `p.write()` ? Just trying to understand so I can try to implement thanks!
0A0D
@Roboto: I've tried to extend my answer to cover your extra questions. Without an example protocol to implement, I'm not sure I can do better. For an example of a related problem you might try Googling for papers by John Hughes and by Phil Wadler on the design of a prettyprinting library. They are trying to produce nicely formatted ASCII, not binary code, and they are using functions, not objects, but the underlying ideas and principles are the same.
Norman Ramsey
This is a really good solution but I don't think I will implement it on this project. This would of been my second choice for acceptance so if you could keep it around in case someone else comes along, that would be great
0A0D
+1  A: 

There is a much better way, which is using structs to set the structures. This is usually how network packets are built on a low level.

For example, say you have packets which have an id, length, flag byte, and data, you'd do something like this:

struct packet_header {
   int id;
   byte length;
   byte flags;
}; 

byte my_packet[] = new byte[100];
packet_header *header = &my_packet;
header->id = 20;
header->length = 10; // This can be set automatically by a function, maybe?
// etc.
header++; // Header now points to the data section.

Do note that you're going to have to make sure that the structures are "packed", i.e. when you write byte length, it really takes up a byte. Usually, you'd achieve this using something like #pragma pack or similar (you'll have to read about your compiler's pragma settings).

Also, note that you should probably use functions to do common operations. For example, create a function which gets as input the size, data to send, and other information, and fills out the packet header and data for you. This way, you can perform calculations about the actual size you want to write in the length field, you can calculate the CRC inside the function, etc.

Edit: This is a C-centric way of doing things, which is the style of a lot of networking code. A more C++-centric (object oriented) approach could also work, but I'm less familiar with them.

Edan Maor
You could do the same with an anonymous union in C++ if you'd like type safety.
Billy ONeal