/* * The MySensors Arduino library handles the wireless radio link and protocol * between your home built sensors/actuators and HA controller of choice. * The sensors forms a self healing radio network with optional repeaters. Each * repeater and gateway builds a routing tables in EEPROM which keeps track of the * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad * Copyright (C) 2013-2018 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org * Support Forum: http://forum.mysensors.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. */ /** * @file MyMessage.h * * @brief API and type declarations for MySensors messages * @defgroup MyMessagegrp MyMessage * @ingroup publics * @{ * * @brief Here you can find all message types used by the MySensors protocol as well as macros for * parsing and manipulating messages. */ #ifndef MyMessage_h #define MyMessage_h #ifdef __cplusplus #include #include #endif #define PROTOCOL_VERSION (2u) //!< The version of the protocol #define MAX_MESSAGE_LENGTH (32u) //!< The maximum size of a message (including header) #define HEADER_SIZE (7u) //!< The size of the header #define MAX_PAYLOAD (MAX_MESSAGE_LENGTH - HEADER_SIZE) //!< The maximum size of a payload depends on #MAX_MESSAGE_LENGTH and #HEADER_SIZE /// @brief The command field (message-type) defines the overall properties of a message typedef enum { C_PRESENTATION = 0, //!< Sent by a node when they present attached sensors. This is usually done in presentation() at startup. C_SET = 1, //!< This message is sent from or to a sensor when a sensor value should be updated. C_REQ = 2, //!< Requests a variable value (usually from an actuator destined for controller). C_INTERNAL = 3, //!< Internal MySensors messages (also include common messages provided/generated by the library). C_STREAM = 4 //!< For firmware and other larger chunks of data that need to be divided into pieces. } mysensors_command_t; #if !DOXYGEN // Hide until we migrate /// @brief Type of sensor (used when presenting sensors) typedef enum { S_DOOR = 0, //!< Door sensor, V_TRIPPED, V_ARMED S_MOTION = 1, //!< Motion sensor, V_TRIPPED, V_ARMED S_SMOKE = 2, //!< Smoke sensor, V_TRIPPED, V_ARMED S_BINARY = 3, //!< Binary light or relay, V_STATUS, V_WATT S_LIGHT = 3, //!< \deprecated Same as S_BINARY S_DIMMER = 4, //!< Dimmable light or fan device, V_STATUS (on/off), V_PERCENTAGE (dimmer level 0-100), V_WATT S_COVER = 5, //!< Blinds or window cover, V_UP, V_DOWN, V_STOP, V_PERCENTAGE (open/close to a percentage) S_TEMP = 6, //!< Temperature sensor, V_TEMP S_HUM = 7, //!< Humidity sensor, V_HUM S_BARO = 8, //!< Barometer sensor, V_PRESSURE, V_FORECAST S_WIND = 9, //!< Wind sensor, V_WIND, V_GUST S_RAIN = 10, //!< Rain sensor, V_RAIN, V_RAINRATE S_UV = 11, //!< Uv sensor, V_UV S_WEIGHT = 12, //!< Personal scale sensor, V_WEIGHT, V_IMPEDANCE S_POWER = 13, //!< Power meter, V_WATT, V_KWH, V_VAR, V_VA, V_POWER_FACTOR S_HEATER = 14, //!< Header device, V_HVAC_SETPOINT_HEAT, V_HVAC_FLOW_STATE, V_TEMP S_DISTANCE = 15, //!< Distance sensor, V_DISTANCE S_LIGHT_LEVEL = 16, //!< Light level sensor, V_LIGHT_LEVEL (uncalibrated in percentage), V_LEVEL (light level in lux) S_ARDUINO_NODE = 17, //!< Used (internally) for presenting a non-repeating Arduino node S_ARDUINO_REPEATER_NODE = 18, //!< Used (internally) for presenting a repeating Arduino node S_LOCK = 19, //!< Lock device, V_LOCK_STATUS S_IR = 20, //!< IR device, V_IR_SEND, V_IR_RECEIVE S_WATER = 21, //!< Water meter, V_FLOW, V_VOLUME S_AIR_QUALITY = 22, //!< Air quality sensor, V_LEVEL S_CUSTOM = 23, //!< Custom sensor S_DUST = 24, //!< Dust sensor, V_LEVEL S_SCENE_CONTROLLER = 25, //!< Scene controller device, V_SCENE_ON, V_SCENE_OFF. S_RGB_LIGHT = 26, //!< RGB light. Send color component data using V_RGB. Also supports V_WATT S_RGBW_LIGHT = 27, //!< RGB light with an additional White component. Send data using V_RGBW. Also supports V_WATT S_COLOR_SENSOR = 28, //!< Color sensor, send color information using V_RGB S_HVAC = 29, //!< Thermostat/HVAC device. V_HVAC_SETPOINT_HEAT, V_HVAC_SETPOINT_COLD, V_HVAC_FLOW_STATE, V_HVAC_FLOW_MODE, V_TEMP S_MULTIMETER = 30, //!< Multimeter device, V_VOLTAGE, V_CURRENT, V_IMPEDANCE S_SPRINKLER = 31, //!< Sprinkler, V_STATUS (turn on/off), V_TRIPPED (if fire detecting device) S_WATER_LEAK = 32, //!< Water leak sensor, V_TRIPPED, V_ARMED S_SOUND = 33, //!< Sound sensor, V_TRIPPED, V_ARMED, V_LEVEL (sound level in dB) S_VIBRATION = 34, //!< Vibration sensor, V_TRIPPED, V_ARMED, V_LEVEL (vibration in Hz) S_MOISTURE = 35, //!< Moisture sensor, V_TRIPPED, V_ARMED, V_LEVEL (water content or moisture in percentage?) S_INFO = 36, //!< LCD text device / Simple information device on controller, V_TEXT S_GAS = 37, //!< Gas meter, V_FLOW, V_VOLUME S_GPS = 38, //!< GPS Sensor, V_POSITION S_WATER_QUALITY = 39 //!< V_TEMP, V_PH, V_ORP, V_EC, V_STATUS } mysensors_sensor_t; /// @brief Type of sensor data (for set/req/ack messages) typedef enum { V_TEMP = 0, //!< S_TEMP. Temperature S_TEMP, S_HEATER, S_HVAC V_HUM = 1, //!< S_HUM. Humidity V_STATUS = 2, //!< S_BINARY, S_DIMMER, S_SPRINKLER, S_HVAC, S_HEATER. Used for setting/reporting binary (on/off) status. 1=on, 0=off V_LIGHT = 2, //!< \deprecated Same as V_STATUS V_PERCENTAGE = 3, //!< S_DIMMER. Used for sending a percentage value 0-100 (%). V_DIMMER = 3, //!< \deprecated Same as V_PERCENTAGE V_PRESSURE = 4, //!< S_BARO. Atmospheric Pressure V_FORECAST = 5, //!< S_BARO. Whether forecast. string of "stable", "sunny", "cloudy", "unstable", "thunderstorm" or "unknown" V_RAIN = 6, //!< S_RAIN. Amount of rain V_RAINRATE = 7, //!< S_RAIN. Rate of rain V_WIND = 8, //!< S_WIND. Wind speed V_GUST = 9, //!< S_WIND. Gust V_DIRECTION = 10, //!< S_WIND. Wind direction 0-360 (degrees) V_UV = 11, //!< S_UV. UV light level V_WEIGHT = 12, //!< S_WEIGHT. Weight(for scales etc) V_DISTANCE = 13, //!< S_DISTANCE. Distance V_IMPEDANCE = 14, //!< S_MULTIMETER, S_WEIGHT. Impedance value V_ARMED = 15, //!< S_DOOR, S_MOTION, S_SMOKE, S_SPRINKLER. Armed status of a security sensor. 1 = Armed, 0 = Bypassed V_TRIPPED = 16, //!< S_DOOR, S_MOTION, S_SMOKE, S_SPRINKLER, S_WATER_LEAK, S_SOUND, S_VIBRATION, S_MOISTURE. Tripped status of a security sensor. 1 = Tripped, 0 V_WATT = 17, //!< S_POWER, S_BINARY, S_DIMMER, S_RGB_LIGHT, S_RGBW_LIGHT. Watt value for power meters V_KWH = 18, //!< S_POWER. Accumulated number of KWH for a power meter V_SCENE_ON = 19, //!< S_SCENE_CONTROLLER. Turn on a scene V_SCENE_OFF = 20, //!< S_SCENE_CONTROLLER. Turn of a scene V_HVAC_FLOW_STATE = 21, //!< S_HEATER, S_HVAC. HVAC flow state ("Off", "HeatOn", "CoolOn", or "AutoChangeOver") V_HEATER = 21, //!< \deprecated Same as V_HVAC_FLOW_STATE V_HVAC_SPEED = 22, //!< S_HVAC, S_HEATER. HVAC/Heater fan speed ("Min", "Normal", "Max", "Auto") V_LIGHT_LEVEL = 23, //!< S_LIGHT_LEVEL. Uncalibrated light level. 0-100%. Use V_LEVEL for light level in lux V_VAR1 = 24, //!< VAR1 V_VAR2 = 25, //!< VAR2 V_VAR3 = 26, //!< VAR3 V_VAR4 = 27, //!< VAR4 V_VAR5 = 28, //!< VAR5 V_UP = 29, //!< S_COVER. Window covering. Up V_DOWN = 30, //!< S_COVER. Window covering. Down V_STOP = 31, //!< S_COVER. Window covering. Stop V_IR_SEND = 32, //!< S_IR. Send out an IR-command V_IR_RECEIVE = 33, //!< S_IR. This message contains a received IR-command V_FLOW = 34, //!< S_WATER. Flow of water (in meter) V_VOLUME = 35, //!< S_WATER. Water volume V_LOCK_STATUS = 36, //!< S_LOCK. Set or get lock status. 1=Locked, 0=Unlocked V_LEVEL = 37, //!< S_DUST, S_AIR_QUALITY, S_SOUND (dB), S_VIBRATION (hz), S_LIGHT_LEVEL (lux) V_VOLTAGE = 38, //!< S_MULTIMETER V_CURRENT = 39, //!< S_MULTIMETER V_RGB = 40, //!< S_RGB_LIGHT, S_COLOR_SENSOR. Sent as ASCII hex: RRGGBB (RR=red, GG=green, BB=blue component) V_RGBW = 41, //!< S_RGBW_LIGHT. Sent as ASCII hex: RRGGBBWW (WW=white component) V_ID = 42, //!< Used for sending in sensors hardware ids (i.e. OneWire DS1820b). V_UNIT_PREFIX = 43, //!< Allows sensors to send in a string representing the unit prefix to be displayed in GUI, not parsed by controller! E.g. cm, m, km, inch. V_HVAC_SETPOINT_COOL = 44, //!< S_HVAC. HVAC cool setpoint (Integer between 0-100) V_HVAC_SETPOINT_HEAT = 45, //!< S_HEATER, S_HVAC. HVAC/Heater setpoint (Integer between 0-100) V_HVAC_FLOW_MODE = 46, //!< S_HVAC. Flow mode for HVAC ("Auto", "ContinuousOn", "PeriodicOn") V_TEXT = 47, //!< S_INFO. Text message to display on LCD or controller device V_CUSTOM = 48, //!< Custom messages used for controller/inter node specific commands, preferably using S_CUSTOM device type. V_POSITION = 49, //!< GPS position and altitude. Payload: latitude;longitude;altitude(m). E.g. "55.722526;13.017972;18" V_IR_RECORD = 50, //!< Record IR codes S_IR for playback V_PH = 51, //!< S_WATER_QUALITY, water PH V_ORP = 52, //!< S_WATER_QUALITY, water ORP : redox potential in mV V_EC = 53, //!< S_WATER_QUALITY, water electric conductivity μS/cm (microSiemens/cm) V_VAR = 54, //!< S_POWER, Reactive power: volt-ampere reactive (var) V_VA = 55, //!< S_POWER, Apparent power: volt-ampere (VA) V_POWER_FACTOR = 56, //!< S_POWER, Ratio of real power to apparent power: floating point value in the range [-1,..,1] } mysensors_data_t; #endif /// @brief Type of internal messages (for internal messages) typedef enum { I_BATTERY_LEVEL = 0, //!< Battery level I_TIME = 1, //!< Time (request/response) I_VERSION = 2, //!< Version I_ID_REQUEST = 3, //!< ID request I_ID_RESPONSE = 4, //!< ID response I_INCLUSION_MODE = 5, //!< Inclusion mode I_CONFIG = 6, //!< Config (request/response) I_FIND_PARENT_REQUEST = 7, //!< Find parent I_FIND_PARENT_RESPONSE = 8, //!< Find parent response I_LOG_MESSAGE = 9, //!< Log message I_CHILDREN = 10, //!< Children I_SKETCH_NAME = 11, //!< Sketch name I_SKETCH_VERSION = 12, //!< Sketch version I_REBOOT = 13, //!< Reboot request I_GATEWAY_READY = 14, //!< Gateway ready I_SIGNING_PRESENTATION = 15, //!< Provides signing related preferences (first byte is preference version) I_NONCE_REQUEST = 16, //!< Request for a nonce I_NONCE_RESPONSE = 17, //!< Payload is nonce data I_HEARTBEAT_REQUEST = 18, //!< Heartbeat request I_PRESENTATION = 19, //!< Presentation message I_DISCOVER_REQUEST = 20, //!< Discover request I_DISCOVER_RESPONSE = 21, //!< Discover response I_HEARTBEAT_RESPONSE = 22, //!< Heartbeat response I_LOCKED = 23, //!< Node is locked (reason in string-payload) I_PING = 24, //!< Ping sent to node, payload incremental hop counter I_PONG = 25, //!< In return to ping, sent back to sender, payload incremental hop counter I_REGISTRATION_REQUEST = 26, //!< Register request to GW I_REGISTRATION_RESPONSE = 27, //!< Register response from GW I_DEBUG = 28, //!< Debug message I_SIGNAL_REPORT_REQUEST = 29, //!< Device signal strength request I_SIGNAL_REPORT_REVERSE = 30, //!< Internal I_SIGNAL_REPORT_RESPONSE = 31, //!< Device signal strength response (RSSI) I_PRE_SLEEP_NOTIFICATION = 32, //!< Message sent before node is going to sleep I_POST_SLEEP_NOTIFICATION = 33 //!< Message sent after node woke up (if enabled) } mysensors_internal_t; /// @brief Type of data stream (for streamed message) typedef enum { ST_FIRMWARE_CONFIG_REQUEST = 0, //!< Request new FW, payload contains current FW details ST_FIRMWARE_CONFIG_RESPONSE = 1, //!< New FW details to initiate OTA FW update ST_FIRMWARE_REQUEST = 2, //!< Request FW block ST_FIRMWARE_RESPONSE = 3, //!< Response FW block ST_SOUND = 4, //!< Sound ST_IMAGE = 5, //!< Image ST_FIRMWARE_CONFIRM = 6, //!< Mark running firmware as valid (MyOTAFirmwareUpdateNVM + mcuboot) ST_FIRMWARE_RESPONSE_RLE = 7, //!< Response FW block with run length encoded data } mysensors_stream_t; /// @brief Type of payload typedef enum { P_STRING = 0, //!< Payload type is string P_BYTE = 1, //!< Payload type is byte P_INT16 = 2, //!< Payload type is INT16 P_UINT16 = 3, //!< Payload type is UINT16 P_LONG32 = 4, //!< Payload type is INT32 P_ULONG32 = 5, //!< Payload type is UINT32 P_CUSTOM = 6, //!< Payload type is binary P_FLOAT32 = 7 //!< Payload type is float32 } mysensors_payload_t; #ifndef BIT #define BIT(n) ( 1<<(n) ) //!< Bit indexing macro #endif #define BIT_MASK(len) ( BIT(len)-1 ) //!< Create a bitmask of length 'len' #define BF_MASK(start, len) ( BIT_MASK(len)<<(start) ) //!< Create a bitfield mask of length starting at bit 'start' #define BF_PREP(x, start, len) ( ((x)&BIT_MASK(len)) << (start) ) //!< Prepare a bitmask for insertion or combining #define BF_GET(y, start, len) ( ((y)>>(start)) & BIT_MASK(len) ) //!< Extract a bitfield of length 'len' starting at bit 'start' from 'y' #define BF_SET(y, x, start, len) ( y= ((y) &~ BF_MASK(start, len)) | BF_PREP(x, start, len) ) //!< Insert a new bitfield value 'x' into 'y' // Getters/setters for special bit fields in header #define mSetVersion(_message,_version) BF_SET(_message.version_length, _version, 0, 2) //!< Set version field #define mGetVersion(_message) ((uint8_t)BF_GET(_message.version_length, 0, 2)) //!< Get version field #define mSetSigned(_message,_signed) BF_SET(_message.version_length, _signed, 2, 1) //!< Set signed field #define mGetSigned(_message) ((bool)BF_GET(_message.version_length, 2, 1)) //!< Get signed field #define mSetLength(_message,_length) BF_SET(_message.version_length, _length, 3, 5) //!< Set length field #define mGetLength(_message) ((uint8_t)BF_GET(_message.version_length, 3, 5)) //!< Get length field #define mSetCommand(_message,_command) BF_SET(_message.command_ack_payload, _command, 0, 3) //!< Set command field #define mGetCommand(_message) ((uint8_t)BF_GET(_message.command_ack_payload, 0, 3)) //!< Get command field #define mSetRequestAck(_message,_rack) BF_SET(_message.command_ack_payload, _rack, 3, 1) //!< Set ack-request field #define mGetRequestAck(_message) ((bool)BF_GET(_message.command_ack_payload, 3, 1)) //!< Get ack-request field #define mSetAck(_message,_ackMsg) BF_SET(_message.command_ack_payload, _ackMsg, 4, 1) //!< Set ack field #define mGetAck(_message) ((bool)BF_GET(_message.command_ack_payload, 4, 1)) //!< Get ack field #define mSetPayloadType(_message, _pt) BF_SET(_message.command_ack_payload, _pt, 5, 3) //!< Set payload type field #define mGetPayloadType(_message) ((uint8_t)BF_GET(_message.command_ack_payload, 5, 3)) //!< Get payload type field // internal access for special fields #define miGetCommand() ((uint8_t)BF_GET(command_ack_payload, 0, 3)) //!< Internal getter for command field #define miSetLength(_length) BF_SET(version_length, _length, 3, 5) //!< Internal setter for length field #define miGetLength() ((uint8_t)BF_GET(version_length, 3, 5)) //!< Internal getter for length field #define miSetVersion(_version) BF_SET(version_length, _version, 0, 2) //!< Internal setter for version field #define miGetVersion() ((uint8_t)BF_GET(version_length, 0, 2)) //!< Internal getter for version field #define miSetRequestAck(_rack) BF_SET(command_ack_payload, _rack, 3, 1) //!< Internal setter for ack-request field #define miGetRequestAck() ((bool)BF_GET(command_ack_payload, 3, 1)) //!< Internal getter for ack-request field #define miSetAck(_ack) BF_SET(command_ack_payload, _ack, 4, 1) //!< Internal setter for ack field #define miGetAck() ((bool)BF_GET(command_ack_payload, 4, 1)) //!< Internal getter for ack field #define miSetPayloadType(_pt) BF_SET(command_ack_payload, _pt, 5, 3) //!< Internal setter for payload type field #define miGetPayloadType() (uint8_t)BF_GET(command_ack_payload, 5, 3) //!< Internal getter for payload type field #if defined(__cplusplus) || defined(DOXYGEN) /** * @brief MyMessage is used to create, manipulate, send and read MySensors messages */ class MyMessage { private: char* getCustomString(char *buffer) const; public: /** * Default constructor */ MyMessage(void); /** * Constructor * @param sensor id of the child sensor for this message * @param type see http://korturl.nu/stupidurl */ MyMessage(const uint8_t sensor, const uint8_t type); /** * Single character hex (0 - 15) conversion * @param i byte (only lower 4 bits will be considered) * @return single char with the hex representation (0 to F) of the parameter */ char i2h(const uint8_t i) const; /** * @brief Clear message contents. */ void clear(void); /** * If payload is something else than P_STRING you can have the payload value converted * into string representation by supplying a buffer with the minimum size of * 2*MAX_PAYLOAD+1. This is to be able to fit hex-conversion of a full binary payload. * @param buffer pointer to a buffer that's at least 2*MAX_PAYLOAD+1 bytes large */ char* getStream(char *buffer) const; /** * @brief Copy the payload into the supplied buffer */ char* getString(char *buffer) const; /** * @brief Get payload as string * @return pointer to a char array storing the string */ const char* getString(void) const; /** * @brief Get custom payload * @return pointer to the raw payload */ void* getCustom(void) const; /** * @brief Get bool payload * @return a bool with the value of the payload (true/false) */ bool getBool(void) const; /** * @brief Get unsigned 8-bit integer payload * @return the value of the payload, 0 to 255 */ uint8_t getByte(void) const; /** * @brief Get float payload * @return the floating-point value of the payload */ float getFloat(void) const; /** * @brief Get signed 16-bit integer payload * @return the value of the payload, –32768 to 32767 */ int16_t getInt(void) const; /** * @brief Get unsigned 16-bit integer payload * @return the value of the payload, 0 to 65535 */ uint16_t getUInt(void) const; /** * @brief Get signed 32-bit integer payload * @return the value of the payload, –2147483648 to 2147483647 */ int32_t getLong(void) const; /** * @brief Get unsigned 32-bit integer payload * @return the value of the payload, 0 to 4294967295 */ uint32_t getULong(void) const; /** * @brief Getter for command type * @return #mysensors_command_t */ uint8_t getCommand(void) const; /** * @brief Getter for ack-flag. * @return true if this is an ack message */ bool isAck(void) const; /** * @brief Set message type * @param type see http://korturl.nu/stupidurl */ MyMessage& setType(const uint8_t type); /** * @brief Set which child sensor this message belongs to * @param sensor */ MyMessage& setSensor(const uint8_t sensor); /** * @brief Set final destination node id for this message * @param destination */ MyMessage& setDestination(const uint8_t destination); /** * @brief Set entire payload * @param payload pointer to the buffer where the payload is stored * @param length of the payload */ MyMessage& set(const void* payload, const uint8_t length); /** * @brief Set payload to character array * @param value pointer to the character array. The array must be null-terminated. */ MyMessage& set(const char* value); #if !defined(__linux__) /** * @brief Set payload to character array from flash * @param value pointer to the character array. The array must be null-terminated. */ MyMessage& set(const __FlashStringHelper* value); #endif /** * @brief Set payload to decimal number * @param value float * @param decimals number of decimals to include */ MyMessage& set(const float value, const uint8_t decimals); /** * @brief Set payload to bool value * @param value true or false */ MyMessage& set(const bool value); /** * @brief Set payload to unsigned 8-bit integer value * @param value (0 to 255) */ MyMessage& set(const uint8_t value); /** * @brief Set payload to unsigned 32-bit integer value * @param value (0 to 4294967295) */ MyMessage& set(const uint32_t value); /** * @brief Set payload to signed 32-bit integer value * @param value (–2147483648 to 2147483647) */ MyMessage& set(const int32_t value); /** * @brief Set payload to unsigned 16-bit integer value * @param value (0 to 65535) */ MyMessage& set(const uint16_t value); /** * @brief Set payload to signed 16-bit integer value * @param value (–32768 to 32767) */ MyMessage& set(const int16_t value); #else typedef union { struct { #endif uint8_t last; //!< 8 bit - Id of last node this message passed uint8_t sender; //!< 8 bit - Id of sender node (origin) uint8_t destination; //!< 8 bit - Id of destination node /** * 2 bit - Protocol version
* 1 bit - Signed flag
* 5 bit - Length of payload */ uint8_t version_length; /** * 3 bit - Command type
* 1 bit - Request an ack - Indicator that receiver should send an ack back
* 1 bit - Is ack message - Indicator that this is the actual ack message
* 3 bit - Payload data type */ uint8_t command_ack_payload; uint8_t type; //!< 8 bit - Type varies depending on command uint8_t sensor; //!< 8 bit - Id of sensor that this message concerns. /* * Each message can transfer a payload. We add one extra byte for string * terminator \0 to be "printable" this is not transferred OTA * This union is used to simplify the construction of the binary data types transferred. */ union { uint8_t bValue; //!< unsigned byte value (8-bit) uint16_t uiValue; //!< unsigned integer value (16-bit) int16_t iValue; //!< signed integer value (16-bit) uint32_t ulValue; //!< unsigned long value (32-bit) int32_t lValue; //!< signed long value (32-bit) struct { //!< Float messages float fValue; uint8_t fPrecision; //!< Number of decimals when serializing }; struct { //!< Presentation messages uint8_t version; //!< Library version uint8_t sensorType; //!< Sensor type hint for controller, see table above }; char data[MAX_PAYLOAD + 1]; //!< Buffer for raw payload data } __attribute__((packed)); //!< Doxygen will complain without this comment #if defined(__cplusplus) || defined(DOXYGEN) } __attribute__((packed)); #else }; uint8_t array[HEADER_SIZE + MAX_PAYLOAD + 1]; //!< buffer for entire message } __attribute__((packed)) MyMessage; #endif #endif /** @}*/