liblightmodbus 3.0
A lightweight, header-only, hardware-agnostic Modbus RTU/TCP library
Loading...
Searching...
No Matches
base.h
Go to the documentation of this file.
1#ifndef LIGHTMODBUS_BASE_H
2#define LIGHTMODBUS_BASE_H
3
4#include <stdint.h>
5
18#ifndef LIGHTMODBUS_WARN_UNUSED
19#define LIGHTMODBUS_WARN_UNUSED __attribute__((warn_unused_result))
20#endif
21
28#ifndef LIGHTMODBUS_ALWAYS_INLINE
29#define LIGHTMODBUS_ALWAYS_INLINE __attribute__((always_inline))
30#endif
31
32#define MODBUS_PDU_MIN 1
33#define MODBUS_PDU_MAX 253
34
35#define MODBUS_RTU_ADU_MIN 4
36#define MODBUS_RTU_ADU_MAX 256
37#define MODBUS_RTU_ADU_PADDING 3
38#define MODBUS_RTU_PDU_OFFSET 1
39
40#define MODBUS_TCP_ADU_MIN 8
41#define MODBUS_TCP_ADU_MAX 260
42#define MODBUS_TCP_ADU_PADDING 7
43#define MODBUS_TCP_PDU_OFFSET 7
44
49#define LIGHTMODBUS_RET_ERROR LIGHTMODBUS_WARN_UNUSED ModbusErrorInfo
50
56#define MODBUS_ERROR_SOURCE_GENERAL 0u
57
62#define MODBUS_ERROR_SOURCE_REQUEST 1u
63
68#define MODBUS_ERROR_SOURCE_RESPONSE 2u
69
74#define MODBUS_ERROR_SOURCE_RESERVED 3u
75
80#define MODBUS_MAKE_ERROR(s, e) ((ModbusErrorInfo){.source = (s), .error = (e)})
81
86#define MODBUS_NO_ERROR() MODBUS_MAKE_ERROR(MODBUS_ERROR_SOURCE_GENERAL, MODBUS_OK)
87
93#define MODBUS_GENERAL_ERROR(e) MODBUS_MAKE_ERROR(MODBUS_ERROR_SOURCE_GENERAL, (MODBUS_ERROR_##e))
94
100#define MODBUS_REQUEST_ERROR(e) MODBUS_MAKE_ERROR(MODBUS_ERROR_SOURCE_REQUEST, (MODBUS_ERROR_##e))
101
107#define MODBUS_RESPONSE_ERROR(e) MODBUS_MAKE_ERROR(MODBUS_ERROR_SOURCE_RESPONSE, (MODBUS_ERROR_##e))
108
125typedef struct ModbusErrorInfo
126{
127 unsigned int source : 2;
128 unsigned int error : 6;
130
225
239
250
251// Forward declaration for ModbusBuffer
252struct ModbusBuffer;
253
260 struct ModbusBuffer *buffer,
261 uint16_t size,
262 void *context);
263
275typedef struct ModbusBuffer
276{
279
280 uint8_t *data;
281 uint8_t *pdu;
282 uint16_t length;
283
284 uint8_t padding;
285 uint8_t pduOffset;
287
289 ModbusBuffer *buffer,
290 uint16_t size,
291 void *context);
292
294void modbusBufferDestroy(ModbusBuffer *buffer, void *context);
295LIGHTMODBUS_WARN_UNUSED ModbusError modbusBufferAllocateADU(ModbusBuffer *buffer, uint16_t pduSize, void *context);
296void modbusBufferFree(ModbusBuffer *buffer, void *context);
297
298uint16_t modbusCRC(const uint8_t *data, uint16_t length);
299
303static inline void modbusBufferModePDU(ModbusBuffer *buffer)
304{
305 buffer->padding = 0;
306 buffer->pduOffset = 0;
307}
308
312static inline void modbusBufferModeRTU(ModbusBuffer *buffer)
313{
316}
317
321static inline void modbusBufferModeTCP(ModbusBuffer *buffer)
322{
325}
326
333LIGHTMODBUS_WARN_UNUSED static inline uint8_t modbusMaskRead(const uint8_t *mask, uint16_t n)
334{
335 return (mask[n >> 3] & (1 << (n & 7))) != 0;
336}
337
344static inline void modbusMaskWrite(uint8_t *mask, uint16_t n, uint8_t value)
345{
346 if (value)
347 mask[n >> 3] |= (1 << (n & 7));
348 else
349 mask[n >> 3] &= ~(1 << (n & 7));
350}
351
357LIGHTMODBUS_WARN_UNUSED static inline uint16_t modbusBitsToBytes(uint16_t n)
358{
359 return (n + 7) >> 3;
360}
361
365LIGHTMODBUS_WARN_UNUSED static inline uint16_t modbusRLE(const uint8_t *p)
366{
367 uint8_t lo = *p;
368 uint8_t hi = *(p + 1);
369 return (uint16_t) lo | ((uint16_t) hi << 8);
370}
371
375static inline uint16_t modbusWLE(uint8_t *p, uint16_t val)
376{
377 *p = val & 0xff;
378 *(p + 1) = val >> 8;
379 return val;
380}
381
385LIGHTMODBUS_WARN_UNUSED static inline uint16_t modbusRBE(const uint8_t *p)
386{
387 uint8_t lo = *(p + 1);
388 uint8_t hi = *p;
389 return (uint16_t) lo | ((uint16_t) hi << 8);
390}
391
395static inline uint16_t modbusWBE(uint8_t *p, uint16_t val)
396{
397 *p = val >> 8;
398 *(p + 1) = val & 0xff;
399 return val;
400}
401
408static inline uint8_t modbusCheckRangeU16(uint16_t index, uint16_t count)
409{
410 // return (uint16_t)(index + count - 1) < index;
411 return index > UINT16_MAX - count + 1;
412}
413
423{
424 return err.source;
425}
426
435
446
456
466
476
490 const uint8_t *frame,
491 uint16_t length,
492 uint8_t checkCRC,
493 const uint8_t **pdu,
494 uint16_t *pduLength,
495 uint8_t *address)
496{
497 // Check length
498 if (length < MODBUS_RTU_ADU_MIN || length > MODBUS_RTU_ADU_MAX)
499 return MODBUS_ERROR_LENGTH;
500
501 // Extract address
502 *address = frame[0];
503
504 // Check CRC
505 if (checkCRC && modbusCRC(frame, length - 2) != modbusRLE(frame + length - 2))
506 return MODBUS_ERROR_CRC;
507
508 *pdu = frame + MODBUS_RTU_PDU_OFFSET;
509 *pduLength = length - MODBUS_RTU_ADU_PADDING;
510
511 return MODBUS_OK;
512}
513
523 uint8_t *frame,
524 uint16_t length,
525 uint8_t address)
526{
527 // Check length
528 if (length < MODBUS_RTU_ADU_MIN || length > MODBUS_RTU_ADU_MAX)
529 return MODBUS_ERROR_LENGTH;
530
531 // Write address
532 frame[0] = address;
533
534 // Compute and write CRC
535 modbusWLE(&frame[length - 2], modbusCRC(frame, length - 2));
536
537 return MODBUS_OK;
538}
539
554 const uint8_t *frame,
555 uint16_t length,
556 const uint8_t **pdu,
557 uint16_t *pduLength,
558 uint16_t *transactionID,
559 uint8_t *unitID)
560{
561 // Check length
562 if (length < MODBUS_TCP_ADU_MIN || length > MODBUS_TCP_ADU_MAX)
563 return MODBUS_ERROR_LENGTH;
564
565 // Discard non-Modbus messages
566 uint16_t protocolID = modbusRBE(&frame[2]);
567 if (protocolID != 0)
569
570 // Length mismatch
571 uint16_t messageLength = modbusRBE(&frame[4]);
572 if (messageLength != length - 6)
573 return MODBUS_ERROR_LENGTH;
574
575 *transactionID = modbusRBE(&frame[0]);
576 *unitID = frame[6];
577 *pdu = frame + MODBUS_TCP_PDU_OFFSET;
578 *pduLength = length - MODBUS_TCP_ADU_PADDING;
579
580 return MODBUS_OK;
581}
582
593 uint8_t *frame,
594 uint16_t length,
595 uint16_t transactionID,
596 uint8_t unitID)
597{
598 // Check length
599 if (length < MODBUS_TCP_ADU_MIN || length > MODBUS_TCP_ADU_MAX)
600 return MODBUS_ERROR_LENGTH;
601
602 modbusWBE(&frame[0], transactionID);
603 modbusWBE(&frame[2], 0);
604 modbusWBE(&frame[4], length - 6);
605 frame[6] = unitID;
606 return MODBUS_OK;
607}
608
609#endif
void modbusBufferFree(ModbusBuffer *buffer, void *context)
Frees memory allocated inside the buffer.
Definition base.impl.h:68
#define LIGHTMODBUS_WARN_UNUSED
Definition base.h:19
ModbusExceptionCode
Represents a Modbus exception code.
Definition base.h:230
@ MODBUS_EXCEP_ILLEGAL_ADDRESS
Illegal data address.
Definition base.h:233
@ MODBUS_EXCEP_ACK
Acknowledge.
Definition base.h:236
@ MODBUS_EXCEP_NONE
Definition base.h:231
@ MODBUS_EXCEP_NACK
Negative acknowledge.
Definition base.h:237
@ MODBUS_EXCEP_SLAVE_FAILURE
Slave could not process the request.
Definition base.h:235
@ MODBUS_EXCEP_ILLEGAL_FUNCTION
Illegal function code.
Definition base.h:232
@ MODBUS_EXCEP_ILLEGAL_VALUE
Illegal data value.
Definition base.h:234
static uint16_t modbusRLE(const uint8_t *p)
Safely reads a little-endian 16-bit word from provided pointer.
Definition base.h:365
uint16_t modbusCRC(const uint8_t *data, uint16_t length)
Calculates 16-bit Modbus CRC of provided data.
Definition base.impl.h:126
static ModbusError modbusGetErrorCode(ModbusErrorInfo err)
Returns ModbusError contained in ModbusErrorInfo.
Definition base.h:431
ModbusDataType
Represents different Modbus data types.
Definition base.h:244
@ MODBUS_DISCRETE_INPUT
Discrete input.
Definition base.h:248
@ MODBUS_HOLDING_REGISTER
Holding register.
Definition base.h:245
@ MODBUS_INPUT_REGISTER
Input register.
Definition base.h:246
@ MODBUS_COIL
Coil.
Definition base.h:247
#define LIGHTMODBUS_ALWAYS_INLINE
Definition base.h:29
#define MODBUS_RTU_ADU_PADDING
Number of extra bytes added to the PDU in Modbus RTU.
Definition base.h:37
static ModbusError modbusPackTCP(uint8_t *frame, uint16_t length, uint16_t transactionID, uint8_t unitID)
Sets up the MBAP header in a Modbus TCP frame.
Definition base.h:592
ModbusError
Represtents different kinds of errors.
Definition base.h:137
@ MODBUS_ERROR_FUNCTION
Invalid function.
Definition base.h:168
@ MODBUS_ERROR_RANGE
Invalid register range.
Definition base.h:193
@ MODBUS_ERROR_VALUE
Invalid register value.
Definition base.h:185
@ MODBUS_ERROR_OK
Same as MODBUS_OK.
Definition base.h:148
@ MODBUS_ERROR_LENGTH
Invalid frame length.
Definition base.h:156
@ MODBUS_ERROR_CRC
CRC invalid.
Definition base.h:199
@ MODBUS_ERROR_BAD_TRANSACTION
Mismatched transaction ID.
Definition base.h:213
@ MODBUS_ERROR_ALLOC
Memory allocation error.
Definition base.h:163
@ MODBUS_ERROR_BAD_PROTOCOL
Invalid protocol ID (nonzero)
Definition base.h:205
@ MODBUS_ERROR_OTHER
Other error.
Definition base.h:223
@ MODBUS_ERROR_COUNT
Invalid register count.
Definition base.h:175
@ MODBUS_ERROR_ADDRESS
Invalid slave address.
Definition base.h:218
@ MODBUS_ERROR_INDEX
Invalid index value.
Definition base.h:180
@ MODBUS_OK
No error.
Definition base.h:143
#define MODBUS_TCP_PDU_OFFSET
Offset of PDU relative to the frame beginning in Modbus TCP.
Definition base.h:43
#define MODBUS_ERROR_SOURCE_REQUEST
The request frame contains errors.
Definition base.h:62
#define MODBUS_TCP_ADU_PADDING
Number of extra bytes added to the PDU in Modbus TCP.
Definition base.h:42
static ModbusError modbusUnpackRTU(const uint8_t *frame, uint16_t length, uint8_t checkCRC, const uint8_t **pdu, uint16_t *pduLength, uint8_t *address)
Unpacks data from a Modbus RTU frame and optionally checks CRC.
Definition base.h:489
ModbusErrorInfo modbusBufferInit(ModbusBuffer *buffer, ModbusAllocator allocator)
Initializes a buffer for use.
Definition base.impl.h:51
ModbusError modbusBufferAllocateADU(ModbusBuffer *buffer, uint16_t pduSize, void *context)
Allocates memory to hold Modbus ADU.
Definition base.impl.h:98
static ModbusError modbusGetRequestError(ModbusErrorInfo err)
Returns request error from ModbusErrorInfo.
Definition base.h:462
static void modbusMaskWrite(uint8_t *mask, uint16_t n, uint8_t value)
Writes n-th bit in an array.
Definition base.h:344
#define MODBUS_ERROR_SOURCE_RESPONSE
The response frame contains errors.
Definition base.h:68
static uint8_t modbusCheckRangeU16(uint16_t index, uint16_t count)
Checks whether provided address range causes an uint16_t overflow.
Definition base.h:408
static ModbusError modbusUnpackTCP(const uint8_t *frame, uint16_t length, const uint8_t **pdu, uint16_t *pduLength, uint16_t *transactionID, uint8_t *unitID)
Unpacks data from a Modbus TCP frame.
Definition base.h:553
#define MODBUS_RTU_ADU_MAX
Maximum length of ADU in Modbus RTU.
Definition base.h:36
static uint8_t modbusIsOk(ModbusErrorInfo err)
Checks if ModbusErrorInfo contains an error.
Definition base.h:442
static void modbusBufferModePDU(ModbusBuffer *buffer)
Prepares buffer to only store a Modbus PDU.
Definition base.h:303
#define MODBUS_ERROR_SOURCE_GENERAL
General library error - can be caused by providing an incorrect argument or a internal library error.
Definition base.h:56
static uint16_t modbusWBE(uint8_t *p, uint16_t val)
Safely writes a big-endian 16-bit word to provided pointer.
Definition base.h:395
#define LIGHTMODBUS_RET_ERROR
Return type for library functions returning ModbusErrorInfo that should be handled properly.
Definition base.h:49
static ModbusError modbusGetGeneralError(ModbusErrorInfo err)
Returns general error from ModbusErrorInfo.
Definition base.h:452
ModbusError modbusDefaultAllocator(ModbusBuffer *buffer, uint16_t size, void *context)
The default memory allocator based on realloc()
Definition base.impl.h:21
ModbusError(* ModbusAllocator)(struct ModbusBuffer *buffer, uint16_t size, void *context)
Pointer to a memory allocator function.
Definition base.h:259
static uint8_t modbusGetErrorSource(ModbusErrorInfo err)
Returns uint8_t describing error source of ModbusErrorInfo.
Definition base.h:422
void modbusBufferDestroy(ModbusBuffer *buffer, void *context)
Equivalent of modbusBufferFree() \copydetail modbusBufferFree()
Definition base.impl.h:78
static ModbusError modbusGetResponseError(ModbusErrorInfo err)
Returns response error from ModbusErrorInfo.
Definition base.h:472
static uint16_t modbusRBE(const uint8_t *p)
Safely reads a big-endian 16-bit word from provided pointer.
Definition base.h:385
static void modbusBufferModeRTU(ModbusBuffer *buffer)
Prepares buffer to store a Modbus RTU message.
Definition base.h:312
static void modbusBufferModeTCP(ModbusBuffer *buffer)
Prepares buffer to store a Modbus TCP message.
Definition base.h:321
#define MODBUS_RTU_PDU_OFFSET
Offset of PDU relative to the frame beginning in Modbus RTU.
Definition base.h:38
static uint16_t modbusBitsToBytes(uint16_t n)
Returns number of bytes necessary to hold given number of bits.
Definition base.h:357
static ModbusError modbusPackRTU(uint8_t *frame, uint16_t length, uint8_t address)
Sets up address and CRC in a Modbus RTU frame.
Definition base.h:522
static uint16_t modbusWLE(uint8_t *p, uint16_t val)
Safely writes a little-endian 16-bit word to provided pointer.
Definition base.h:375
#define MODBUS_TCP_ADU_MAX
Maximum length of ADU in Modbus TCP.
Definition base.h:41
static uint8_t modbusMaskRead(const uint8_t *mask, uint16_t n)
Reads n-th bit from an array.
Definition base.h:333
Stores a Modbus frame.
Definition base.h:276
uint16_t length
Length of the entire frame (PDU size + padding)
Definition base.h:282
ModbusAllocator allocator
Pointer to the allocator function.
Definition base.h:278
uint8_t pduOffset
PDU offset relative to the beginning of the frame.
Definition base.h:285
uint8_t * data
Pointer to the frame buffer.
Definition base.h:280
uint8_t * pdu
A pointer to the PDU section of the frame.
Definition base.h:281
uint8_t padding
Number of extra bytes surrounding the PDU.
Definition base.h:284
Richer error represenation - source and type of error.
Definition base.h:126
unsigned int source
Source of the error (e.g. MODBUS_ERROR_SOURCE_REQUEST)
Definition base.h:127
unsigned int error
Contains ModbusError.
Definition base.h:128