Recommended Usage Pattern
More information on the mentioned steps can be found in Functions of the Packet Dispatcher
Typical Usage Model
Intended setup
- Define one handler function per packet type
- define one packet_handler_config_t entry per packet type (using the macros)
- provide queue storage buffers
(When using the macros, you do not need to do this manually) - call PacketDispatcherInit(...)
- whenever a frame arrives, call DispatchPacket()
Flow after setup
- Ethernet/UDP receives raw frame
- networking code builds
receive_frame DispatchPacket()decodes it- payload type is matched
- decoded payload is copied into target queue
- matching handler task wakes
- the callback processes typed payload
Examples
1) Using macros
//Imports
#include "packet_dispatcher.h"
#include "packet_dispatcher_macros.h"
/*Define handler callbacks*/
static result_t handle_drive_msg(void* buffer) {
PBDriveMsg* msg = (PBDriveMsg*)buffer;
return process_drive_msg(msg);
}
static result_t handle_sensor_diag(void* buffer) {
PBSensorDiag* msg = (PBSensorDiag*)buffer;
return process_sensor_diag(msg);
}
PACKET_HANDLER_CONFIG_STATIC(drive_handler_cfg,
PBEnvelope_drive_msg_tag,
drive_msg,
handle_drive_msg);
PACKET_HANDLER_CONFIG_STATIC_QUEUE(sensor_diag_handler_cfg,
PBEnvelope_sensor_diag_tag,
sensor_diag,
handle_sensor_diag,
10U);
static packet_handler_config_t* handlers[] = {
drive_handler_cfg,
sensor_diag_handler_cfg,
};
2) Manual configuration
//Imports
#include "packet_dispatcher.h"
static result_t handle_drive_cmd(void* buffer) {
PBDriveCommand* msg = (PBDriveCommand*)buffer;
return drive_process(msg);
}
static result_t handle_arm_cmd(void* buffer) {
PBArmCommand* msg = (PBArmCommand*)buffer;
return arm_process(msg);
}
static uint8_t drive_queue_storage[8 * sizeof(PBDriveCommand)];
static uint8_t arm_queue_storage[4 * sizeof(PBArmCommand)];
static packet_handler_config_t handlers[] = {
{
.handler = handle_drive_cmd,
.task_name = "drive_pkt",
.packet_type = PBEnvelope_drive_cmd_tag,
.task_priority = 3,
.task_stack_depth = 512,
.item_size = sizeof(PBDriveCommand),
.queue_length = 8,
.queue_buffer = drive_queue_storage,
},
{
.handler = handle_arm_cmd,
.task_name = "arm_pkt",
.packet_type = PBEnvelope_arm_cmd_tag,
.task_priority = 3,
.task_stack_depth = 512,
.item_size = sizeof(PBArmCommand),
.queue_length = 4,
.queue_buffer = arm_queue_storage,
},
};
Then during startup:
result_t res = PacketDispatcherInit(handlers, ARRAY_LEN(handlers));
And during frame reception:
DispatchPacket(&rx_frame);
Important note about array type
The current PacketDispatcherInit() API expects:
packet_handler_config_t* handlers
meaning a contiguous array of structs, not an array of pointers.
So with the current implementation, the final array should actually be:
static packet_handler_config_t handlers[] = {
drive_handler_cfg,
sensor_diag_handler_cfg,
};
not an array of pointers.
That distinction matters. The macros define actual config objects, not pointers.
c. IMPORTANT configuration rules
This module is heavily configuration-driven. Several things must match exactly.
I. packet_type must match the protobuf discriminator
Each handler’s packet_type must be the exact value used by PBEnvelope.which_payload. If this is wrong, packets will never reach that handler.
II. item_size must match the decoded payload type
The queue copies bytes from &DecodingEnvelopeCurrent.payload into a queue item of size item_size.
If item_size is:
- too small -> payload will be truncated
- too large -> copied data may include unrelated union bytes or layout assumptions
- wrong type entirely -> handler sees garbage with confidence
III. queue_buffer must be sized correctly
The backing storage must be at least: queue_length * item_size. If not, queue creation or runtime behavior is invalid.
IV. Handler must cast void * correctly
The callback receives a raw buffer pointer. It must cast to the correct generated protobuf type.