Getting started
Start here
Use this page to make your first SLMP read and write from an ESP32/RP2040-class board. The examples use TCP at 192.168.250.100:1025; use UDP port 1035 when you build a UDP transport variant.
Prerequisites
| Need | Detail |
|---|---|
| Build system | PlatformIO or Arduino IDE with ESP32/RP2040 board support. |
| C++ language | Strict ISO C++17; GNU extensions and C++20-only syntax are unsupported. |
| Network stack | A board or shield that provides a WiFiClient or EthernetClient compatible TCP client. |
| PLC endpoint | Your PLC is reachable at 192.168.250.100; TCP uses port 1025, and UDP uses port 1035. |
| Register for testing | A safe word register such as D100:U for reads and a test-only word such as D9000:U for writes. |
Add the library
Add the PlatformIO Registry package to your platformio.ini:
lib_deps =
fa-yoshinobu/slmp-connect-cpp-minimal
Include the right headers
| Header | Use |
|---|---|
#include <slmp_minimal.h> |
Low-level slmp::SlmpClient, device helpers, fixed buffers, and direct protocol calls. |
#include <slmp_high_level.h> |
Optional high-level helpers such as slmp::highlevel::readTyped, slmp::highlevel::writeTyped, and slmp::highlevel::Poller. |
#include <slmp_arduino_transport.h> |
TCP and UDP transport adapters for Arduino-compatible ESP32/RP2040 cores. |
The high-level layer is optional and is not included by slmp_minimal.h automatically.
Choose your PLC profile
Pass the concrete PLC profile and complete target route to the SlmpClient constructor. The client derives frame and compatibility settings from that profile; it does not auto-detect your PLC.
| Target | Profile to start with |
|---|---|
| MELSEC iQ-R | slmp::highlevel::PlcProfile::IqR |
| MELSEC iQ-F | slmp::highlevel::PlcProfile::IqF |
| MELSEC iQ-L | slmp::highlevel::PlcProfile::IqL |
| QCPU via QJ71E71-100 | slmp::highlevel::PlcProfile::QCpuQj71E71100 |
| LCPU built-in or via LJ71E71-100 | slmp::highlevel::PlcProfile::LCpu or slmp::highlevel::PlcProfile::LCpuLj71E71100 |
| QnU/QnUDV built-in or via QJ71E71-100 | slmp::highlevel::PlcProfile::QnU, slmp::highlevel::PlcProfile::QnUQj71E71100, slmp::highlevel::PlcProfile::QnUDV, or slmp::highlevel::PlcProfile::QnUDVQj71E71100 |
The complete sketches below show where the profile configuration belongs.
Strict profile checks are always enabled in the normal public API. Profile evidence collection uses separate maintainer tooling.
First read
This complete sketch connects to 192.168.250.100:1025 and reads D100:U once per second.
#include <Arduino.h>
#include <WiFi.h>
#include <slmp_arduino_transport.h>
#include <slmp_high_level.h>
#include <slmp_minimal.h>
constexpr char kWifiSsid[] = "YOUR_WIFI_SSID";
constexpr char kWifiPassword[] = "YOUR_WIFI_PASSWORD";
constexpr char kPlcHost[] = "192.168.250.100";
constexpr uint16_t kPlcPort = 1025;
constexpr auto kProfile = slmp::highlevel::PlcProfile::IqR;
WiFiClient tcp;
slmp::ArduinoClientTransport transport(
tcp,
slmp::configureEsp32WifiClientKeepAlive,
slmp::writeEsp32WifiClientNonBlocking);
uint8_t txBuffer[160] = {};
uint8_t rxBuffer[160] = {};
slmp::SlmpClient plc(transport, kProfile, slmp::TargetAddress{0x00, 0xFF, slmp::module_io::OwnStation, 0x00}, txBuffer, sizeof(txBuffer), rxBuffer, sizeof(rxBuffer));
void setup() {
Serial.begin(115200);
WiFi.begin(kWifiSsid, kWifiPassword);
while (WiFi.status() != WL_CONNECTED) {
delay(250);
}
if (plc.connect(kPlcHost, kPlcPort)) {
Serial.println("PLC connected");
} else {
Serial.println("PLC connection failed");
}
}
void loop() {
slmp::highlevel::Value value;
const slmp::Error err = slmp::highlevel::readTyped(plc, "D100:U", value);
if (err == slmp::Error::Ok) {
Serial.printf("D100=%u\n", static_cast<unsigned>(value.u16));
} else {
Serial.printf("read failed: %s\n", slmp::errorString(err));
}
delay(1000);
}
ArduinoClientTransport requires both a keepalive configurator and an explicit
non-blocking transmit policy. The ESP32 helpers above configure the 30-second
TCP keepalive idle and perform exactly one MSG_DONTWAIT socket send per write
attempt. For another Arduino network stack, supply the keepalive configurator
and use writeArduinoClientWhenReady only when its availableForWrite() result
is a reliable immediate-capacity signal. A missing policy or a missing or failed
keepalive configurator makes the connection fail.
Expected serial output:
PLC connected
D100=1234
First write
Use a test-only register that your PLC program does not use for control decisions. This example writes 1234 to D9000:U and then reads it back.
#include <Arduino.h>
#include <WiFi.h>
#include <slmp_arduino_transport.h>
#include <slmp_high_level.h>
#include <slmp_minimal.h>
constexpr char kWifiSsid[] = "YOUR_WIFI_SSID";
constexpr char kWifiPassword[] = "YOUR_WIFI_PASSWORD";
constexpr char kPlcHost[] = "192.168.250.100";
constexpr uint16_t kPlcPort = 1025;
constexpr auto kProfile = slmp::highlevel::PlcProfile::IqR;
WiFiClient tcp;
slmp::ArduinoClientTransport transport(
tcp,
slmp::configureEsp32WifiClientKeepAlive,
slmp::writeEsp32WifiClientNonBlocking);
uint8_t txBuffer[160] = {};
uint8_t rxBuffer[160] = {};
slmp::SlmpClient plc(transport, kProfile, slmp::TargetAddress{0x00, 0xFF, slmp::module_io::OwnStation, 0x00}, txBuffer, sizeof(txBuffer), rxBuffer, sizeof(rxBuffer));
bool wroteOnce = false;
void setup() {
Serial.begin(115200);
WiFi.begin(kWifiSsid, kWifiPassword);
while (WiFi.status() != WL_CONNECTED) {
delay(250);
}
if (!plc.connect(kPlcHost, kPlcPort)) {
Serial.println("PLC connection failed");
}
}
void loop() {
if (!wroteOnce) {
const slmp::Error writeErr = slmp::highlevel::writeTyped(
plc,
"D9000:U",
slmp::highlevel::Value::u16Value(1234U));
Serial.printf("write D9000: %s\n", slmp::errorString(writeErr));
wroteOnce = (writeErr == slmp::Error::Ok);
}
slmp::highlevel::Value value;
const slmp::Error readErr = slmp::highlevel::readTyped(plc, "D9000:U", value);
if (readErr == slmp::Error::Ok) {
Serial.printf("D9000=%u\n", static_cast<unsigned>(value.u16));
}
delay(1000);
}
Confirm success
- The board joins the same network as your PLC.
- The serial monitor prints
PLC connected. - The PLC-side communication data code is Binary and the port/open setting matches your transport; see the MELSEC PLC Setup Guide.
- PLC-side RUN-time write permission is enabled before you run a write example where the PLC exposes that setting.
D100:Uprints a stable value or a value you expect from the PLC.- The write test uses a safe address reserved for bring-up.
- A write followed by a read returns the value you wrote.
If it does not work
| Symptom | Check |
|---|---|
connect() fails |
Your board must provide a WiFiClient or EthernetClient compatible transport, and the PLC must listen on TCP port 1025. |
| Connection opens but all requests fail | Confirm Binary communication data code in the PLC setup guide. |
| Reads work but writes fail | Confirm RUN-time write permission in the PLC setup guide and the selected profile write policy. |
| High-level helpers are undefined | Add #include <slmp_high_level.h>; it is not included automatically. |
| Address parsing fails | Check that your slmp::highlevel::PlcProfile matches your actual hardware. |
profile_feature_blocked is returned |
The selected profile does not support that operation. Use a supported operation. |
X or Y looks wrong |
Use the profile-aware overloads of slmp::highlevel::readTyped and slmp::highlevel::writeTyped. |