ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
melsec.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file melsec.cpp
6 * @brief Mitsubishi MELSEC MC binary 3E builder + parser (pure, host-tested).
7 */
8
10
11#if PC_ENABLE_MELSEC
12
14#include <string.h>
15
16size_t pc_melsec_build_read(uint8_t *buf, size_t cap, uint8_t device_code, uint32_t head_device, uint16_t points,
17 uint16_t monitoring_timer)
18{
19 if (!buf || cap < MELSEC_3E_READ_REQ_LEN)
20 {
21 return 0;
22 }
23 size_t p = 0;
24 buf[p++] = MELSEC_3E_REQ_SUBHEADER0;
25 buf[p++] = MELSEC_3E_REQ_SUBHEADER1;
26 buf[p++] = MELSEC_NETWORK_DEFAULT;
27 buf[p++] = MELSEC_PC_DEFAULT;
28 p += pc_wr16le(buf + p, MELSEC_DEST_IO_DEFAULT);
29 buf[p++] = MELSEC_DEST_MULTIDROP_DEFAULT;
30 // request data length = the octets from the monitoring timer onward:
31 // timer(2) + command(2) + subcommand(2) + head device(3) + device code(1) + points(2) = 12
32 p += pc_wr16le(buf + p, MELSEC_3E_READ_REQ_DATA_LEN);
33 p += pc_wr16le(buf + p, monitoring_timer);
34 p += pc_wr16le(buf + p, MELSEC_CMD_BATCH_READ);
35 p += pc_wr16le(buf + p, MELSEC_SUBCMD_WORD);
36 buf[p++] = (uint8_t)(head_device & 0xFF); // head device number, 3 octets little-endian
37 buf[p++] = (uint8_t)((head_device >> 8) & 0xFF);
38 buf[p++] = (uint8_t)((head_device >> 16) & 0xFF);
39 buf[p++] = device_code;
40 p += pc_wr16le(buf + p, points);
41 return p; // == MELSEC_3E_READ_REQ_LEN
42}
43
44size_t pc_melsec_build_write(uint8_t *buf, size_t cap, uint8_t device_code, uint32_t head_device, uint16_t points,
45 uint16_t monitoring_timer, const uint8_t *data, size_t data_len)
46{
47 if (!buf || (data_len && !data))
48 {
49 return 0;
50 }
51 if (data_len > (size_t)(0xFFFFu - MELSEC_3E_READ_REQ_DATA_LEN)) // the request-length field is 16-bit
52 {
53 return 0;
54 }
55 if (cap < MELSEC_3E_READ_REQ_LEN + data_len)
56 {
57 return 0;
58 }
59 size_t p = 0;
60 buf[p++] = MELSEC_3E_REQ_SUBHEADER0;
61 buf[p++] = MELSEC_3E_REQ_SUBHEADER1;
62 buf[p++] = MELSEC_NETWORK_DEFAULT;
63 buf[p++] = MELSEC_PC_DEFAULT;
64 p += pc_wr16le(buf + p, MELSEC_DEST_IO_DEFAULT);
65 buf[p++] = MELSEC_DEST_MULTIDROP_DEFAULT;
66 // request data length = the fixed 12 (timer..points) plus the write data octets.
67 p += pc_wr16le(buf + p, (uint16_t)(MELSEC_3E_READ_REQ_DATA_LEN + data_len));
68 p += pc_wr16le(buf + p, monitoring_timer);
69 p += pc_wr16le(buf + p, MELSEC_CMD_BATCH_WRITE);
70 p += pc_wr16le(buf + p, MELSEC_SUBCMD_WORD);
71 buf[p++] = (uint8_t)(head_device & 0xFF); // head device number, 3 octets little-endian
72 buf[p++] = (uint8_t)((head_device >> 8) & 0xFF);
73 buf[p++] = (uint8_t)((head_device >> 16) & 0xFF);
74 buf[p++] = device_code;
75 p += pc_wr16le(buf + p, points);
76 if (data_len)
77 {
78 memcpy(buf + p, data, data_len);
79 p += data_len;
80 }
81 return p; // == MELSEC_3E_READ_REQ_LEN + data_len
82}
83
84bool pc_melsec_parse_response(const uint8_t *buf, size_t len, MelsecResponse *out)
85{
86 // subheader(2)+net(1)+pc(1)+io(2)+multidrop(1)+length(2)+endcode(2) = MELSEC_3E_RES_MIN_LEN
87 if (!buf || !out || len < MELSEC_3E_RES_MIN_LEN)
88 {
89 return false;
90 }
91 if (buf[0] != MELSEC_3E_RES_SUBHEADER0 || buf[1] != MELSEC_3E_RES_SUBHEADER1)
92 {
93 return false;
94 }
95 uint16_t data_length = pc_rd16le(buf + MELSEC_3E_RES_LEN_OFFSET); // covers the end code + the response data
96 if (data_length < MELSEC_ENDCODE_LEN)
97 {
98 return false;
99 }
100 if (MELSEC_3E_RES_DATALEN_BASE + (size_t)data_length > len)
101 {
102 return false;
103 }
104 out->end_code = pc_rd16le(buf + MELSEC_3E_RES_DATALEN_BASE);
105 out->data = buf + MELSEC_3E_RES_DATA_OFFSET;
106 out->data_len = (size_t)data_length - MELSEC_ENDCODE_LEN; // minus the 2-octet end code
107 return true;
108}
109
110#endif // PC_ENABLE_MELSEC
Fixed-width integer serializers into a raw uint8_t* buffer - one source of truth.
uint16_t pc_rd16le(const uint8_t *p)
Read a little-endian u16 at p.
Definition endian.h:60
size_t pc_wr16le(uint8_t *p, uint16_t v)
Write v little-endian at p.
Definition endian.h:32
Mitsubishi MELSEC MC protocol (binary 3E frame) codec (PC_ENABLE_MELSEC) - zero-heap batch-read reque...