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.
- Dealer requests bet from Player.
- Player waits for real life player to input bet.
- Dealer keeps querying Player state until bet has been entered (so we don’t lock up the entire system waiting purely for the bet).
- Bet is sent to Dealer.
Packet
Logic
TRANSMITPacket()
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) */ }; }