10#ifndef MCPROTOCOL_EXAMPLE_PLC_BAUD
11#define MCPROTOCOL_EXAMPLE_PLC_BAUD 19200
14#ifndef MCPROTOCOL_EXAMPLE_DEBUG_BAUD
15#define MCPROTOCOL_EXAMPLE_DEBUG_BAUD 115200
18#ifndef MCPROTOCOL_EXAMPLE_POLL_INTERVAL_MS
19#define MCPROTOCOL_EXAMPLE_POLL_INTERVAL_MS 1000U
39constexpr DeviceAddress kHeadDevice {.
code = DeviceCode::D, .number = 100};
42 MelsecSerialClient client;
43 std::array<std::uint16_t, 4> out_words {};
44 bool request_started =
false;
46 bool request_done =
false;
47 bool request_reported =
false;
48 std::uint32_t next_request_ms = 0;
49 Status completion_status {};
53HardwareSerial& g_plc_serial = Serial1;
55ProtocolConfig make_protocol() {
56 ProtocolConfig config;
57 config.frame_kind = FrameKind::C4;
58 config.code_mode = CodeMode::Ascii;
59 config.ascii_format = AsciiFormat::Format4;
60 config.target_series = PlcSeries::Q_L;
61 config.sum_check_enabled =
false;
62 config.route = RouteConfig {
63 .kind = RouteKind::HostStation,
67 .request_destination_module_io_no = 0x03FF,
68 .request_destination_module_station_no = 0x00,
69 .self_station_enabled =
false,
70 .self_station_no = 0x00,
75void on_request_complete(
void* user, Status status) {
76 auto* app =
static_cast<AppState*
>(user);
77 app->request_done =
true;
78 app->completion_status = status;
79 app->request_started =
false;
81 app->next_request_ms = millis() + kPollIntervalMs;
84void configure_plc_uart() {
85 g_plc_serial.begin(kPlcBaud, SERIAL_8E1);
88void pump_uart_tx(std::uint32_t now_ms) {
89 if (!g_app.request_started || g_app.tx_sent) {
98 g_plc_serial.write(
reinterpret_cast<const std::uint8_t*
>(frame.
data()), frame.
size());
100 const Status status = g_app.client.notify_tx_complete(now_ms);
102 on_request_complete(&g_app, status);
106 g_app.tx_sent =
true;
109void pump_uart_rx(std::uint32_t now_ms) {
110 std::array<char, 64> rx_chunk {};
111 while (g_plc_serial.available() > 0) {
112 const int available = g_plc_serial.available();
113 const std::size_t request_size =
114 static_cast<std::size_t
>(available > 0 ? available : 0);
115 const std::size_t read_size =
116 request_size < rx_chunk.size() ? request_size : rx_chunk.size();
117 const std::size_t bytes_read =
static_cast<std::size_t
>(
118 g_plc_serial.readBytes(rx_chunk.data(), read_size));
119 if (bytes_read == 0) {
123 g_app.client.on_rx_bytes(
126 reinterpret_cast<const std::byte*
>(rx_chunk.data()),
131void start_read_if_due(std::uint32_t now_ms) {
132 if (g_app.request_started || g_app.client.busy() || now_ms < g_app.next_request_ms) {
136 g_app.request_done =
false;
137 g_app.request_reported =
false;
138 g_app.tx_sent =
false;
139 const Status status = g_app.client.async_batch_read_words(
141 BatchReadWordsRequest {
142 .head_device = kHeadDevice,
143 .points =
static_cast<std::uint16_t
>(g_app.out_words.size()),
149 on_request_complete(&g_app, status);
153 g_app.request_started =
true;
157 if (!g_app.request_done || g_app.request_reported) {
161 g_app.request_reported =
true;
162 if (!g_app.completion_status.ok()) {
163 Serial.print(
"mega2560 uart example failed: ");
164 Serial.println(g_app.completion_status.message);
168 Serial.print(
"mega2560 uart read ok: D100=");
169 Serial.print(g_app.out_words[0], HEX);
170 Serial.print(
" D101=");
171 Serial.print(g_app.out_words[1], HEX);
172 Serial.print(
" D102=");
173 Serial.print(g_app.out_words[2], HEX);
174 Serial.print(
" D103=");
175 Serial.println(g_app.out_words[3], HEX);
182 configure_plc_uart();
184 const Status status = g_app.client.configure(make_protocol());
186 on_request_complete(&g_app, status);
190 g_app.next_request_ms = 0;
191 Serial.println(
"mega2560 uart example: read-only D100-D103 via Serial1");
195 const std::uint32_t now_ms = millis();
196 start_read_if_due(now_ms);
197 pump_uart_tx(now_ms);
198 pump_uart_rx(now_ms);
199 g_app.client.poll(now_ms);
Asynchronous MC protocol client for UART / serial integrations.
constexpr size_type size() const noexcept
constexpr pointer data() const noexcept
constexpr bool empty() const noexcept
Single-include entry point for the public serial client API.
DeviceCode
Device-family identifier used by the request codecs.
RouteKind
Route layout inside the request header.
FrameKind
MC protocol frame family used on the serial link.
CodeMode
Request/response payload encoding.
AsciiFormat
ASCII formatting variant for C4 / C3 / C2 serial frames.
PlcSeries
PLC family selection used for subcommand and device-layout differences.
Device code plus numeric address.
Top-level protocol configuration shared by codecs and client requests.
Route header fields for serial MC requests.
Result object returned by most public APIs.