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 int kRxPin = 6;
40constexpr int kTxPin = 7;
41constexpr DeviceAddress kHeadDevice {.
code = DeviceCode::D, .number = 100};
44 MelsecSerialClient client;
45 std::array<std::uint16_t, 4> out_words {};
46 bool request_started =
false;
48 bool request_done =
false;
49 bool request_reported =
false;
50 std::uint32_t next_request_ms = 0;
51 Status completion_status {};
55HardwareSerial& g_plc_serial = Serial1;
57ProtocolConfig make_protocol() {
58 ProtocolConfig config;
59 config.frame_kind = FrameKind::C4;
60 config.code_mode = CodeMode::Ascii;
61 config.ascii_format = AsciiFormat::Format4;
62 config.target_series = PlcSeries::Q_L;
63 config.sum_check_enabled =
false;
64 config.route = RouteConfig {
65 .kind = RouteKind::HostStation,
69 .request_destination_module_io_no = 0x03FF,
70 .request_destination_module_station_no = 0x00,
71 .self_station_enabled =
false,
72 .self_station_no = 0x00,
77void on_request_complete(
void* user, Status status) {
78 auto* app =
static_cast<AppState*
>(user);
79 app->request_done =
true;
80 app->completion_status = status;
81 app->request_started =
false;
83 app->next_request_ms = millis() + kPollIntervalMs;
86void configure_plc_uart() {
87 g_plc_serial.begin(kPlcBaud, SERIAL_8E1, kRxPin, kTxPin);
90void pump_uart_tx(std::uint32_t now_ms) {
91 if (!g_app.request_started || g_app.tx_sent) {
100 g_plc_serial.write(
reinterpret_cast<const std::uint8_t*
>(frame.
data()), frame.
size());
101 g_plc_serial.flush();
102 const Status status = g_app.client.notify_tx_complete(now_ms);
104 on_request_complete(&g_app, status);
108 g_app.tx_sent =
true;
111void pump_uart_rx(std::uint32_t now_ms) {
112 std::array<char, 64> rx_chunk {};
113 while (g_plc_serial.available() > 0) {
114 const int available = g_plc_serial.available();
115 const std::size_t request_size =
116 static_cast<std::size_t
>(available > 0 ? available : 0);
117 const std::size_t read_size =
118 request_size < rx_chunk.size() ? request_size : rx_chunk.size();
119 const std::size_t bytes_read =
static_cast<std::size_t
>(
120 g_plc_serial.readBytes(rx_chunk.data(), read_size));
121 if (bytes_read == 0) {
125 g_app.client.on_rx_bytes(
128 reinterpret_cast<const std::byte*
>(rx_chunk.data()),
133void start_read_if_due(std::uint32_t now_ms) {
134 if (g_app.request_started || g_app.client.busy() || now_ms < g_app.next_request_ms) {
138 g_app.request_done =
false;
139 g_app.request_reported =
false;
140 g_app.tx_sent =
false;
141 const Status status = g_app.client.async_batch_read_words(
143 BatchReadWordsRequest {
144 .head_device = kHeadDevice,
145 .points =
static_cast<std::uint16_t
>(g_app.out_words.size()),
151 on_request_complete(&g_app, status);
155 g_app.request_started =
true;
159 if (!g_app.request_done || g_app.request_reported) {
163 g_app.request_reported =
true;
164 if (!g_app.completion_status.ok()) {
165 Serial.print(
"esp32c3 uart example failed: ");
166 Serial.println(g_app.completion_status.message);
170 Serial.print(
"esp32c3 uart read ok: D100=");
171 Serial.print(g_app.out_words[0], HEX);
172 Serial.print(
" D101=");
173 Serial.print(g_app.out_words[1], HEX);
174 Serial.print(
" D102=");
175 Serial.print(g_app.out_words[2], HEX);
176 Serial.print(
" D103=");
177 Serial.println(g_app.out_words[3], HEX);
184 configure_plc_uart();
186 const Status status = g_app.client.configure(make_protocol());
188 on_request_complete(&g_app, status);
192 g_app.next_request_ms = 0;
193 Serial.println(
"esp32c3 uart example: read-only D100-D103 via Serial1");
197 const std::uint32_t now_ms = millis();
198 start_read_if_due(now_ms);
199 pump_uart_tx(now_ms);
200 pump_uart_rx(now_ms);
201 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.