Requirements

Although serial communication is fast, it makes sense to try and fit as much data into a single byte as possible.

Communication between the Dealer and the Players is state based.

  1. Dealer requests bet from Player.
  2. Player waits for real life player to input bet.
  3. Dealer keeps querying Player state until bet has been entered (so we don’t lock up the entire system waiting purely for the bet).
  4. Bet is sent to Dealer.

Packet

Logic

TRANSMITPacket()

TransmitPacket

RECIEVEPACKET()

RecievePacket

Protocol

<header><packet info><payload><footer>

Data Structure

<header> = Unique identifier
<packet info> = [PacketID][PayloadLength]
<payload> = [Byte0][Byte1]…[ByteN]
<footer> = Unique identifier

Verification (Checksum)

fletcher16 checksum

Arduino side

if( Serial.available() >= 4 ) {  //Protocol >= 4 bytes (header + info + footer + Npayload)
   Header = Serial.read();
   PacketInfo = Serial.read();
   
   unsigned int payloadLength = (PakInfo & 0x0F);
   Byte payload[payloadLength];

   Byte result = -1;
   for(int i = 0; i < payloadLength; i++)
   {
       Byte result = Serial.read();

       /* Premature packet footer */
       if(result == FOOTER_ID)
           break;
       
       payload[i] = result; 
   }

   /* Less data than expected */
   if(result == FOOTER_ID)
   {
      return PACKET_ERR_INVALID;
   }

   Footer = Serial.read();

   Byte chksum = CalculateChecksum(...); // Generate checksum somehow
 
   Serial.write(chksum); // Send back to dealer for verification
 }

Dealer side

Playing card

Data structure

Credit to my lecturer Ian Stephenson for the main idea behind this, his example implementation used macros to define the constants. I decided to use enums placed inside namespaces so they don’t pollute the global namespace while giving a readability benefit. I find Suit::Spade clearer than just SPADE.

A possible alternative would be to use bitfields, but in the higher level parts of the library these simple constants/macros are abstracted away with C++ classes and functions anyway.

Suit
namespace Suit
{
  /* Suit is stored in the left nibble of a byte */
  enum Value
  {
    Diamond = (1<<4), // 0001 0000
    Heart   = (1<<5), // 0010 0000
    Club    = (1<<6), // 0100 0000
    Spade   = (1<<7)  // 1000 0000
  };
}
Rank
namespace Rank
{
  /* Rank is stored in the right nibble of a byte */
  enum Value
  {
    Joker = (0x00), // This isn't used in poker, but is added for completeness
    Two   = (0x01),
    Three = (0x02),
    Four  = (0x03),
    Five  = (0x04),
    Six   = (0x05),
    Seven = (0x06),
    Eight = (0x07),
    Nine  = (0x08),
    Ten   = (0x09),
    Jack  = (0x0A),
    Queen = (0x0B),
    King  = (0x0C),
    Ace   = (0x0D),
 /* Unused= (0x0E), */
 /* Unused= (0x0F)  */
  };
}