MC Protocol Serial C++ 0.2.3
MC protocol serial library for MCU-oriented environments
Loading...
Searching...
No Matches
platformio_esp32c3_arduino_uart.cpp
Go to the documentation of this file.
1#include <Arduino.h>
2
3#include <array>
4#include <cstddef>
5#include <cstdint>
6
9
10#ifndef MCPROTOCOL_EXAMPLE_PLC_BAUD
11#define MCPROTOCOL_EXAMPLE_PLC_BAUD 19200
12#endif
13
14#ifndef MCPROTOCOL_EXAMPLE_DEBUG_BAUD
15#define MCPROTOCOL_EXAMPLE_DEBUG_BAUD 115200
16#endif
17
18#ifndef MCPROTOCOL_EXAMPLE_POLL_INTERVAL_MS
19#define MCPROTOCOL_EXAMPLE_POLL_INTERVAL_MS 1000U
20#endif
21
22namespace {
23
36
37constexpr std::uint32_t kPollIntervalMs = MCPROTOCOL_EXAMPLE_POLL_INTERVAL_MS;
38constexpr std::uint32_t kPlcBaud = MCPROTOCOL_EXAMPLE_PLC_BAUD;
39constexpr int kRxPin = 6;
40constexpr int kTxPin = 7;
41constexpr DeviceAddress kHeadDevice {.code = DeviceCode::D, .number = 100};
42
43struct AppState {
44 MelsecSerialClient client;
45 std::array<std::uint16_t, 4> out_words {};
46 bool request_started = false;
47 bool tx_sent = false;
48 bool request_done = false;
49 bool request_reported = false;
50 std::uint32_t next_request_ms = 0;
51 Status completion_status {};
52};
53
54AppState g_app;
55HardwareSerial& g_plc_serial = Serial1;
56
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,
66 .station_no = 0x00,
67 .network_no = 0x00,
68 .pc_no = 0xFF,
69 .request_destination_module_io_no = 0x03FF,
70 .request_destination_module_station_no = 0x00,
71 .self_station_enabled = false,
72 .self_station_no = 0x00,
73 };
74 return config;
75}
76
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;
82 app->tx_sent = false;
83 app->next_request_ms = millis() + kPollIntervalMs;
84}
85
86void configure_plc_uart() {
87 g_plc_serial.begin(kPlcBaud, SERIAL_8E1, kRxPin, kTxPin);
88}
89
90void pump_uart_tx(std::uint32_t now_ms) {
91 if (!g_app.request_started || g_app.tx_sent) {
92 return;
93 }
94
95 const std::span<const std::byte> frame = g_app.client.pending_tx_frame();
96 if (frame.empty()) {
97 return;
98 }
99
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);
103 if (!status.ok()) {
104 on_request_complete(&g_app, status);
105 return;
106 }
107
108 g_app.tx_sent = true;
109}
110
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) {
122 break;
123 }
124
125 g_app.client.on_rx_bytes(
126 now_ms,
128 reinterpret_cast<const std::byte*>(rx_chunk.data()),
129 bytes_read));
130 }
131}
132
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) {
135 return;
136 }
137
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(
142 now_ms,
143 BatchReadWordsRequest {
144 .head_device = kHeadDevice,
145 .points = static_cast<std::uint16_t>(g_app.out_words.size()),
146 },
147 std::span<std::uint16_t>(g_app.out_words.data(), g_app.out_words.size()),
148 on_request_complete,
149 &g_app);
150 if (!status.ok()) {
151 on_request_complete(&g_app, status);
152 return;
153 }
154
155 g_app.request_started = true;
156}
157
158void report_once() {
159 if (!g_app.request_done || g_app.request_reported) {
160 return;
161 }
162
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);
167 return;
168 }
169
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);
178}
179
180} // namespace
181
182void setup() {
183 Serial.begin(MCPROTOCOL_EXAMPLE_DEBUG_BAUD);
184 configure_plc_uart();
185
186 const Status status = g_app.client.configure(make_protocol());
187 if (!status.ok()) {
188 on_request_complete(&g_app, status);
189 return;
190 }
191
192 g_app.next_request_ms = 0;
193 Serial.println("esp32c3 uart example: read-only D100-D103 via Serial1");
194}
195
196void loop() {
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);
202 report_once();
203}
Asynchronous MC protocol client for UART / serial integrations.
Definition client.hpp:37
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.
Definition types.hpp:198
RouteKind
Route layout inside the request header.
Definition types.hpp:190
FrameKind
MC protocol frame family used on the serial link.
Definition types.hpp:147
CodeMode
Request/response payload encoding.
Definition types.hpp:161
AsciiFormat
ASCII formatting variant for C4 / C3 / C2 serial frames.
Definition types.hpp:169
PlcSeries
PLC family selection used for subcommand and device-layout differences.
Definition types.hpp:181
#define MCPROTOCOL_EXAMPLE_POLL_INTERVAL_MS
#define MCPROTOCOL_EXAMPLE_PLC_BAUD
#define MCPROTOCOL_EXAMPLE_DEBUG_BAUD
Device code plus numeric address.
Definition types.hpp:348
Top-level protocol configuration shared by codecs and client requests.
Definition types.hpp:323
Route header fields for serial MC requests.
Definition types.hpp:296
Result object returned by most public APIs.
Definition status.hpp:26