Swadge 2024 2.0.0
APIs to develop games for the Magfest Swadge
|
p2pConnection is a connection protocol with a little bit of message reliability that sits on top of ESP-NOW. Think of ESP-NOW like UDP, where you can quickly and unreliably broadcast and receive packets, and p2pConnection as TCP, which trades some speed for reliability. p2pConnection messages have sequence numbers for deduplication, are acknowledged, and are retried if not acknowledged.
Connections are made when two Swadges broadcast connection messages to each other, then send start messages to each other, and acknowledge each other's start message. The play order is determined by who acknowledges the start message first, which is suitably random. A connection sequence looks like this:
After connection, Swadges are free to send messages to each other. An example of unreliable communication with retries and duplication is as follows.
p2pSendCb() and p2pRecvCb() must be called from the ESP-NOW callbacks to pass data to and from p2p.
p2pInitialize() should be called to initialize p2p. p2pDeinit() should be called when the Swadge mode is done to clean up.
The connection won't actually start until p2pStartConnection() is called. Connection statues will be delivered to the Swadge mode through the
p2pGetPlayOrder() can be called after connection to figure out of this Swadge is player one or two.
p2pSendMsg() can be called to send a message from one Swadge to another.
p2pSetDataInAck() can be called to set up data to be delivered in the next acknowledge message. This is useful for high-bandwidth transactional messages. For example, one Swadge may send it's button state to the other, and receive the game state in the acknowledge message. This turns four messages (button state, ack, game state, ack) into two messages (button state, ack[game state]). Note that the acknowledge message with data is not acknowledged itself, but the Swadge sending the inital message will retry until it receives the acknowledge. p2pClearDataInAck() can be called to clear the data to be sent in the acknowledge.
p2pConnection can be finicky to use, so here are a few tips to ensure consistent connections and data transfer.
If ESP_NOW_IMMEDIATE is used, the receive callback (p2pMsgRxCbFn) should return as quickly as possible.The receiving callback function also runs from the Wi-Fi task. So, do not do lengthy operations in the callback function. Instead, post the necessary data to a queue and handle it from a lower priority task.
Go to the source code of this file.
Data Structures | |
struct | p2pConMsg_t |
The byte format for a connection message for a P2P session. More... | |
struct | p2pCommonHeader_t |
The byte format for a common header for all P2P packets. More... | |
struct | p2pDataMsg_t |
The byte format for a P2P data packet. More... | |
struct | _p2pInfo |
All the state variables required for a P2P session with another Swadge. More... | |
struct | p2pPacket_t |
All the information for a packet to store between the receive callback and the task it's actually processed in. More... | |
struct | _p2pInfo.ack |
Variables used for acknowledging and retrying messages. More... | |
struct | _p2pInfo.cnc |
Connection state variables. More... | |
struct | _p2pInfo.tmr |
The timers used for connection and ACKing. More... | |
Macros | |
#define | P2P_MAX_DATA_LEN 245 |
The maximum payload of a p2p packet is 245 bytes. | |
#define | P2P_START_BYTE 'p' |
A start byte for all p2p packets. | |
Typedefs | |
typedef struct _p2pInfo | p2pInfo |
All the state variables required for a P2P session with another Swadge. | |
typedef void(* | p2pConCbFn) (p2pInfo *p2p, connectionEvt_t evt) |
This typedef is for the function callback which delivers connection statuses to the Swadge mode. | |
typedef void(* | p2pMsgRxCbFn) (p2pInfo *p2p, const uint8_t *payload, uint8_t len) |
This typedef is for the function callback which delivers received p2p packets to the Swadge mode. | |
typedef void(* | p2pMsgTxCbFn) (p2pInfo *p2p, messageStatus_t status, const uint8_t *data, uint8_t len) |
This typedef is for the function callback which delivers acknowledge status for transmitted messages to the Swadge mode. | |
typedef void(* | p2pAckSuccessFn) (p2pInfo *p2p, const uint8_t *data, uint8_t len) |
This typedef is for a function callback called when a message is acknowledged. It make also contain a data packet which was appended to the ACK. | |
typedef void(* | p2pAckFailureFn) (p2pInfo *p2p) |
This typedef is for a function callback called when a message is not acknowledged. | |
Enumerations | |
enum | playOrder_t { NOT_SET , GOING_SECOND , GOING_FIRST } |
After connecting, one Swadge will be GOING_FIRST and one will be GOING_SECOND. More... | |
enum | connectionEvt_t { CON_STARTED , RX_GAME_START_ACK , RX_GAME_START_MSG , CON_ESTABLISHED , CON_LOST } |
These are the states a Swadge will go through when connecting to another. More... | |
enum | messageStatus_t { MSG_ACKED , MSG_FAILED } |
Message statuses after transmission. More... | |
enum | p2pMsgType_t { P2P_MSG_CONNECT , P2P_MSG_START , P2P_MSG_ACK , P2P_MSG_DATA_ACK , P2P_MSG_DATA } |
The five different types of p2p messages. More... | |
Functions | |
void | p2pInitialize (p2pInfo *p2p, uint8_t modeId, p2pConCbFn conCbFn, p2pMsgRxCbFn msgRxCbFn, int8_t connectionRssi) |
Initialize the p2p connection protocol. | |
void | p2pSetAsymmetric (p2pInfo *p2p, uint8_t incomingModeId) |
Set the p2p connection protocol to listen for a different mode ID from its own. | |
void | p2pDeinit (p2pInfo *p2p) |
Stop up all timers. | |
void | p2pStartConnection (p2pInfo *p2p) |
Start the connection process by sending broadcasts and notify the mode. | |
void | p2pSendMsg (p2pInfo *p2p, const uint8_t *payload, uint16_t len, p2pMsgTxCbFn msgTxCbFn) |
Send a message from one Swadge to another. This must not be called before the CON_ESTABLISHED event occurs. Message addressing, ACKing, and retries all happen automatically. | |
void | p2pSendCb (p2pInfo *p2p, const uint8_t *mac_addr, esp_now_send_status_t status) |
This must be called by whatever function is registered to the Swadge mode's fnEspNowSendCb. | |
void | p2pRecvCb (p2pInfo *p2p, const uint8_t *mac_addr, const uint8_t *data, uint8_t len, int8_t rssi) |
This function must be called whenever an ESP NOW packet is received. | |
void | p2pSetDataInAck (p2pInfo *p2p, const uint8_t *ackData, uint8_t ackDataLen) |
Set data to be automatically used as the payload all future ACKs, until the data is cleared. | |
void | p2pClearDataInAck (p2pInfo *p2p) |
Clear any data which was set to be used as the payload in the next ACK. | |
playOrder_t | p2pGetPlayOrder (p2pInfo *p2p) |
After the swadge is connected to another, return whether this Swadge is player 1 or player 2. This can be used to determine client/server roles. | |
void | p2pSetPlayOrder (p2pInfo *p2p, playOrder_t order) |
Override whether the Swadge is player 1 or player 2. You probably shouldn't do this, but you might want to for single player modes. | |
struct p2pConMsg_t |
Data Fields | ||
---|---|---|
uint8_t | startByte | Start byte, must be P2P_START_BYTE. |
uint8_t | modeId | Mode byte, must be unique per-mode. |
p2pMsgType_t | messageType | Message type byte. |
struct p2pCommonHeader_t |
Data Fields | ||
---|---|---|
uint8_t | startByte | Start byte, must be P2P_START_BYTE. |
uint8_t | modeId | Mode byte, must be unique per-mode. |
p2pMsgType_t | messageType | Message type byte. |
uint8_t | seqNum | A sequence number for this packet. |
uint8_t | macAddr[6] | The MAC address destination for this packet. |
struct p2pDataMsg_t |
Data Fields | ||
---|---|---|
p2pCommonHeader_t | hdr | The common header bytes for a P2P packet. |
uint8_t | data[P2P_MAX_DATA_LEN] | The data bytes sent or received. |
struct _p2pInfo |
Data Fields | ||
---|---|---|
uint8_t | modeId | The mode ID set by the mode using P2P. |
uint8_t | incomingModeId | A mode ID to listen for which is different than initialized mode ID. See p2pSetAsymmetric() |
p2pConMsg_t | conMsg | The connection message to transmit. |
p2pCommonHeader_t | startMsg | The start message to transmit. |
p2pDataMsg_t | ackMsg | The acknowledge message to transmit. |
uint8_t | dataInAckLen | The length of any extra data which was appended to the ACK, see p2pSetDataInAck() |
p2pConCbFn | conCbFn | A callback function called during the connection process. |
p2pMsgRxCbFn | msgRxCbFn | A callback function called when receiving a message. |
p2pMsgTxCbFn | msgTxCbFn | A callback function called when transmitting a message. |
int8_t | connectionRssi | The minimum RSSI required to begin a connection. |
struct _p2pInfo.ack | ack | Variables used for acknowledging and retrying messages. |
struct _p2pInfo.cnc | cnc | Connection state variables. |
struct _p2pInfo.tmr | tmr | The timers used for connection and ACKing. |
struct p2pPacket_t |
Data Fields | ||
---|---|---|
int8_t | rssi | The received signal strength indicator for the packet. |
uint8_t | mac[6] | The MAC address of the sender. |
uint8_t | len | The length of the received bytes. |
p2pDataMsg_t | data | The received bytes. |
struct _p2pInfo.ack |
Data Fields | ||
---|---|---|
bool | isWaitingForAck | true if waiting for an ACK after transmitting a message |
p2pDataMsg_t | msgToAck | A transmitted message which is waiting for an ACK. |
uint16_t | msgToAckLen | The length of the message which is waiting for an ACK. |
uint32_t | timeSentUs | The time the message is waiting for an ACK was transmitted. |
p2pAckSuccessFn | SuccessFn | A callback function to be called if the message is ACKed. |
p2pAckFailureFn | FailureFn | A callback function to be called if the message is not ACKed. |
struct _p2pInfo.cnc |
Data Fields | ||
---|---|---|
playOrder_t | playOrder | Either GOING_FIRST or GOING_SECOND depending on how the handshake went. |
uint8_t | myMac[6] | This Swadge's MAC address. |
uint8_t | otherMac[6] | The other Swadge's MAC address. |
bool | isActive | true if the connection process has started |
bool | isConnected | true if connected to another Swadge |
bool | broadcastReceived | true if a broadcast was received to start the connection handshake |
bool | rxGameStartMsg | true if the other Swadge's game start message was received |
bool | rxGameStartAck | True if this Swadge's game start message was acknowledged. |
bool | otherMacReceived | true if the other Swadge's MAC address has been received |
uint8_t | mySeqNum | The current sequence number used for transmissions. |
uint8_t | lastSeqNum | The last sequence number used for transmissions. |
struct _p2pInfo.tmr |
#define P2P_MAX_DATA_LEN 245 |
The maximum payload of a p2p packet is 245 bytes.
#define P2P_START_BYTE 'p' |
A start byte for all p2p packets.
All the state variables required for a P2P session with another Swadge.
typedef void(* p2pConCbFn) (p2pInfo *p2p, connectionEvt_t evt) |
This typedef is for the function callback which delivers connection statuses to the Swadge mode.
p2p | The p2pInfo |
evt | The connection event |
typedef void(* p2pMsgRxCbFn) (p2pInfo *p2p, const uint8_t *payload, uint8_t len) |
This typedef is for the function callback which delivers received p2p packets to the Swadge mode.
p2p | The p2pInfo |
payload | The data that was received |
len | The length of the data that was received |
typedef void(* p2pMsgTxCbFn) (p2pInfo *p2p, messageStatus_t status, const uint8_t *data, uint8_t len) |
This typedef is for the function callback which delivers acknowledge status for transmitted messages to the Swadge mode.
p2p | The p2pInfo |
status | The status of the transmission |
data | The data that was transmitted |
len | The length of the data that was transmitted |
typedef void(* p2pAckSuccessFn) (p2pInfo *p2p, const uint8_t *data, uint8_t len) |
This typedef is for a function callback called when a message is acknowledged. It make also contain a data packet which was appended to the ACK.
p2p | The p2pInfo |
data | A data payload returned along with the ACK |
len | The length of the data payload appended to the ACK |
typedef void(* p2pAckFailureFn) (p2pInfo *p2p) |
This typedef is for a function callback called when a message is not acknowledged.
p2p | The p2pInfo |
enum playOrder_t |
After connecting, one Swadge will be GOING_FIRST and one will be GOING_SECOND.
Enumerator | |
---|---|
NOT_SET | Swadges haven't connected yet. |
GOING_SECOND | This Swadge goes second (player two) |
GOING_FIRST | This swadge goes first (player one) |
enum connectionEvt_t |
These are the states a Swadge will go through when connecting to another.
enum messageStatus_t |
enum p2pMsgType_t |
void p2pInitialize | ( | p2pInfo * | p2p, |
uint8_t | modeId, | ||
p2pConCbFn | conCbFn, | ||
p2pMsgRxCbFn | msgRxCbFn, | ||
int8_t | connectionRssi ) |
Initialize the p2p connection protocol.
p2p | The p2pInfo struct with all the state information |
modeId | 8 bit mode ID. Must be unique per-swadge mode. |
conCbFn | A function pointer which will be called when connection events occur |
msgRxCbFn | A function pointer which will be called when a packet is received for the swadge mode |
connectionRssi | The strength needed to start a connection with another swadge. A positive value means swadges are quite close. I've seen RSSI go as low as -70. 0 is practically touching |
void p2pSetAsymmetric | ( | p2pInfo * | p2p, |
uint8_t | incomingModeId ) |
Set the p2p connection protocol to listen for a different mode ID from its own.
p2p | The p2pInfo struct with all the state information |
incomingModeId | The unique mode ID used by the other end of this connection |
void p2pDeinit | ( | p2pInfo * | p2p | ) |
Stop up all timers.
p2p | The p2pInfo struct with all the state information |
void p2pStartConnection | ( | p2pInfo * | p2p | ) |
Start the connection process by sending broadcasts and notify the mode.
p2p | The p2pInfo struct with all the state information |
void p2pSendMsg | ( | p2pInfo * | p2p, |
const uint8_t * | payload, | ||
uint16_t | len, | ||
p2pMsgTxCbFn | msgTxCbFn ) |
Send a message from one Swadge to another. This must not be called before the CON_ESTABLISHED event occurs. Message addressing, ACKing, and retries all happen automatically.
p2p | The p2pInfo struct with all the state information |
payload | A byte array to be copied to the payload for this message |
len | The length of the byte array |
msgTxCbFn | A callback function when this message is ACKed or dropped |
void p2pSendCb | ( | p2pInfo * | p2p, |
const uint8_t * | mac_addr, | ||
esp_now_send_status_t | status ) |
This must be called by whatever function is registered to the Swadge mode's fnEspNowSendCb.
This is called after an attempted transmission. If it was successful, and the message should be acked, start a retry timer. If it wasn't successful, just try again
p2p | The p2pInfo struct with all the state information |
mac_addr | unused |
status | Whether the transmission succeeded or failed |
void p2pRecvCb | ( | p2pInfo * | p2p, |
const uint8_t * | mac_addr, | ||
const uint8_t * | data, | ||
uint8_t | len, | ||
int8_t | rssi ) |
This function must be called whenever an ESP NOW packet is received.
p2p | The p2pInfo struct with all the state information |
mac_addr | The MAC of the swadge that sent the data |
data | The data |
len | The length of the data |
rssi | The rssi of the received data |
void p2pSetDataInAck | ( | p2pInfo * | p2p, |
const uint8_t * | ackData, | ||
uint8_t | ackDataLen ) |
Set data to be automatically used as the payload all future ACKs, until the data is cleared.
This data will not be transmitted until the next ACK is sent, whenever that is. Normally an ACK does not contain any payload data, but an application can have a more efficient continuous request and response flow by embedding the response in the ACK.
p2p | The p2pInfo struct with all the state information |
ackData | The data to be included in the next ACK, will be copied here |
ackDataLen | The length of the data to be included in the next ACK |
void p2pClearDataInAck | ( | p2pInfo * | p2p | ) |
Clear any data which was set to be used as the payload in the next ACK.
p2p | The p2pInfo struct with all the state information |
playOrder_t p2pGetPlayOrder | ( | p2pInfo * | p2p | ) |
After the swadge is connected to another, return whether this Swadge is player 1 or player 2. This can be used to determine client/server roles.
p2p | The p2pInfo struct with all the state information |
void p2pSetPlayOrder | ( | p2pInfo * | p2p, |
playOrder_t | order ) |
Override whether the Swadge is player 1 or player 2. You probably shouldn't do this, but you might want to for single player modes.
p2p | The p2pInfo struct with all the state information |
order | The order to set |