3. DATA STRUCTS

The struct serves as the data payload for sending control signals from the transmitting device to the receiver using ESP-NOW. In addition, it may contain additional data such as telemetry, battery status, etc. The sensors_data_t struct encapsulates all control commands and sensor states relevant to the vehicle’s operation. It’s intended to be sent from a transmitting device (like a remote control) to a receiver (such as a microcontroller on board of the vehicle).

typedef struct {
    int         x_axis;             // Joystick x-position
    int         y_axis;             // Joystick y-position
    bool        nav_bttn;           // Joystick push button
    bool        led;                // LED ON/OFF state
    uint8_t     motor1_rpm_pwm;     // PWMs for 4 DC motors
    uint8_t     motor2_rpm_pwm;
    uint8_t     motor3_rpm_pwm;
    uint8_t     motor4_rpm_pwm;
} __attribute__((packed)) sensors_data_t;
struct motors_rpm {
    int motor1_rpm_pwm;
    int motor2_rpm_pwm;
    int motor3_rpm_pwm;
    int motor4_rpm_pwm;
};

When used with communication protocols like ESP-NOW, this struct is encoded into a byte stream, then transmitted at regular intervals or in response to user input, and finally decoded on the receiving end to control hardware.

What is struct?

In C programming, a struct (short for structure) is a user-defined data type that lets you group multiple variables of different types together under a single name. It’s like a container that holds related information — perfect for organizing data that logically belongs together. Structs are especially powerful in systems programming, embedded projects, and when dealing with raw binary data — like parsing sensor input or transmitting control packets over ESP-NOW.

3.1. Data Payload

x_axis and y_axis fields capture analog input from a joystick, determining direction and speed. nav_bttn represents a joystick push-button.

led allows the transmitter to toggle an onboard LED and is used for status indication (e.g. pairing, battery warning, etc).

motor1_rpm_pwm to motor4_rpm_pwm provide individual PWM signals to four DC motors. This enables fine-grained speed control, supports differential drive configurations, and even allows for maneuvering in multi-directional platforms like omni-wheel robots.

3.1.1. Why use __attribute((packed))?

ESP-NOW uses fixed-size data packets (up to 250 bytes). The __attribute__((packed)) removes compiler-added padding for precise byte alignment.

As packed attribute tells the compiler not to add any padding between fields in memory, this makes the struct:

  • Compact

  • Predictable for serialization over protocols like UART or ESP-NOW

  • Ideal for low-latency transmission in embedded systems

This ensures the receiver interprets the exact byte layout you expect, minimizing bandwidth and maximizing compatibility across platforms.