Skip to content

Usage guide

Two layers

Layer Header Use it when
Low-level core slmp_minimal.h You want fixed caller-owned buffers, explicit slmp::DeviceAddress values, and direct sync or async SLMP calls.
High-level facade slmp_high_level.h You want string addresses, typed values, named snapshots, chunked reads, and reusable polling plans.

The high-level layer is optional. Include slmp_high_level.h explicitly when you use helpers such as slmp::highlevel::readTyped.

Setup

This complete TCP setup creates the transport, buffers, slmp::SlmpClient, and profile configuration. For UDP transport, keep the same host 192.168.250.100 and use UDP port 1035.

#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 kTcpPort = 1025;
constexpr auto kProfile = slmp::highlevel::PlcProfile::IqR;

WiFiClient tcp;
slmp::ArduinoClientTransport transport(tcp);

uint8_t txBuffer[160] = {};
uint8_t rxBuffer[160] = {};
slmp::SlmpClient plc(transport, txBuffer, sizeof(txBuffer), rxBuffer, sizeof(rxBuffer));

void setup() {
    Serial.begin(115200);
    WiFi.begin(kWifiSsid, kWifiPassword);
    while (WiFi.status() != WL_CONNECTED) {
        delay(250);
    }

    slmp::highlevel::configureClientForPlcProfile(plc, kProfile);
    if (plc.connect(kPlcHost, kTcpPort)) {
        Serial.println("PLC connected");
    }
}

void loop() {
    delay(1000);
}

Read a single value

slmp::highlevel::readTyped reads one logical value from one address.

Address form Value type Field to read
D100 or D100:U slmp::highlevel::ValueType::U16 value.u16
D100:S slmp::highlevel::ValueType::S16 value.s16
D200:D slmp::highlevel::ValueType::U32 value.u32
D200:L slmp::highlevel::ValueType::S32 value.s32
D300:F slmp::highlevel::ValueType::Float32 value.f32
M1000 or D50.3 slmp::highlevel::ValueType::Bit value.bit
#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 kTcpPort = 1025;
constexpr auto kProfile = slmp::highlevel::PlcProfile::IqR;

WiFiClient tcp;
slmp::ArduinoClientTransport transport(tcp);
uint8_t txBuffer[160] = {};
uint8_t rxBuffer[160] = {};
slmp::SlmpClient plc(transport, txBuffer, sizeof(txBuffer), rxBuffer, sizeof(rxBuffer));

void setup() {
    Serial.begin(115200);
    WiFi.begin(kWifiSsid, kWifiPassword);
    while (WiFi.status() != WL_CONNECTED) {
        delay(250);
    }
    slmp::highlevel::configureClientForPlcProfile(plc, kProfile);
    plc.connect(kPlcHost, kTcpPort);
}

void loop() {
    slmp::highlevel::Value value;
    const slmp::Error err = slmp::highlevel::readTyped(plc, kProfile, "D100", value);
    if (err == slmp::Error::Ok) {
        Serial.printf("D100=%u\n", static_cast<unsigned>(value.u16));
    } else {
        Serial.printf("readTyped failed: %s\n", slmp::errorString(err));
    }
    delay(1000);
}

Write a single value

slmp::highlevel::writeTyped writes one logical value. Use only test addresses while you are bringing up your PLC connection.

#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 kTcpPort = 1025;
constexpr auto kProfile = slmp::highlevel::PlcProfile::IqR;

WiFiClient tcp;
slmp::ArduinoClientTransport transport(tcp);
uint8_t txBuffer[160] = {};
uint8_t rxBuffer[160] = {};
slmp::SlmpClient plc(transport, 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);
    }
    slmp::highlevel::configureClientForPlcProfile(plc, kProfile);
    plc.connect(kPlcHost, kTcpPort);
}

void loop() {
    if (!wroteOnce) {
        const slmp::Error err = slmp::highlevel::writeTyped(
            plc,
            kProfile,
            "D9000",
            slmp::highlevel::Value::u16Value(321U));
        Serial.printf("write D9000: %s\n", slmp::errorString(err));
        wroteOnce = (err == slmp::Error::Ok);
    }
    delay(1000);
}

Named snapshot

slmp::highlevel::readNamed reads mixed addresses in caller order. slmp::highlevel::writeNamed writes an ordered list of address/value pairs.

#include <Arduino.h>
#include <WiFi.h>

#include <string>
#include <vector>

#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 kTcpPort = 1025;
constexpr auto kProfile = slmp::highlevel::PlcProfile::IqR;

WiFiClient tcp;
slmp::ArduinoClientTransport transport(tcp);
uint8_t txBuffer[192] = {};
uint8_t rxBuffer[192] = {};
slmp::SlmpClient plc(transport, 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);
    }
    slmp::highlevel::configureClientForPlcProfile(plc, kProfile);
    plc.connect(kPlcHost, kTcpPort);
}

void loop() {
    if (!wroteOnce) {
        slmp::highlevel::Snapshot updates = {
            {"D9000", slmp::highlevel::Value::u16Value(100U)},
            {"D9002:S", slmp::highlevel::Value::s16Value(-10)},
            {"D9004:L", slmp::highlevel::Value::s32Value(-123456)},
            {"D9008:F", slmp::highlevel::Value::float32Value(12.5f)}
        };
        const slmp::Error writeErr = slmp::highlevel::writeNamed(plc, kProfile, updates);
        Serial.printf("writeNamed: %s\n", slmp::errorString(writeErr));
        wroteOnce = (writeErr == slmp::Error::Ok);
    }

    const std::vector<std::string> addresses = {
        "SM400",
        "D100",
        "D101:S",
        "D200:F",
        "D50.3"
    };

    slmp::highlevel::Snapshot snapshot;
    const slmp::Error readErr = slmp::highlevel::readNamed(plc, kProfile, addresses, snapshot);
    if (readErr == slmp::Error::Ok && snapshot.size() == addresses.size()) {
        Serial.printf(
            "SM400=%u D100=%u D101=%d D200:F=%.3f D50.3=%u\n",
            snapshot[0].value.bit ? 1U : 0U,
            static_cast<unsigned>(snapshot[1].value.u16),
            static_cast<int>(snapshot[2].value.s16),
            static_cast<double>(snapshot[3].value.f32),
            snapshot[4].value.bit ? 1U : 0U);
    }

    delay(1000);
}

Block reads

Use slmp::highlevel::readWordsChunked when you intentionally allow a large contiguous word read to split into multiple low-level requests.

#include <Arduino.h>
#include <WiFi.h>

#include <vector>

#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 kTcpPort = 1025;
constexpr auto kProfile = slmp::highlevel::PlcProfile::IqR;

WiFiClient tcp;
slmp::ArduinoClientTransport transport(tcp);
uint8_t txBuffer[192] = {};
uint8_t rxBuffer[2048] = {};
slmp::SlmpClient plc(transport, txBuffer, sizeof(txBuffer), rxBuffer, sizeof(rxBuffer));

void setup() {
    Serial.begin(115200);
    WiFi.begin(kWifiSsid, kWifiPassword);
    while (WiFi.status() != WL_CONNECTED) {
        delay(250);
    }
    slmp::highlevel::configureClientForPlcProfile(plc, kProfile);
    plc.connect(kPlcHost, kTcpPort);
}

void loop() {
    std::vector<uint16_t> words;
    const slmp::Error err = slmp::highlevel::readWordsChunked(
        plc,
        "D1000",
        1200,
        words,
        960,
        true);
    Serial.printf("readWordsChunked: %s count=%u\n", slmp::errorString(err), static_cast<unsigned>(words.size()));
    delay(5000);
}

Polling

slmp::highlevel::Poller stores one compiled slmp::highlevel::ReadPlan so repeated reads do not re-parse the address strings.

#include <Arduino.h>
#include <WiFi.h>

#include <string>
#include <vector>

#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 kTcpPort = 1025;
constexpr auto kProfile = slmp::highlevel::PlcProfile::IqR;

WiFiClient tcp;
slmp::ArduinoClientTransport transport(tcp);
uint8_t txBuffer[192] = {};
uint8_t rxBuffer[192] = {};
slmp::SlmpClient plc(transport, txBuffer, sizeof(txBuffer), rxBuffer, sizeof(rxBuffer));
slmp::highlevel::Poller poller;

void setup() {
    Serial.begin(115200);
    WiFi.begin(kWifiSsid, kWifiPassword);
    while (WiFi.status() != WL_CONNECTED) {
        delay(250);
    }
    slmp::highlevel::configureClientForPlcProfile(plc, kProfile);
    plc.connect(kPlcHost, kTcpPort);
    poller.compile({"D100", "D101:S", "D200:F", "M1000"}, kProfile);
}

void loop() {
    slmp::highlevel::Snapshot snapshot;
    const slmp::Error err = poller.readOnce(plc, snapshot);
    Serial.printf("poller: %s values=%u\n", slmp::errorString(err), static_cast<unsigned>(snapshot.size()));
    delay(1000);
}

Device range catalog

slmp::highlevel::readDeviceRangeCatalogForPlcProfile reads live device range bounds for one explicit profile. It requires your selected PLC profile and does not auto-discover the PLC profile.

#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 kTcpPort = 1025;
constexpr auto kProfile = slmp::highlevel::PlcProfile::IqR;

WiFiClient tcp;
slmp::ArduinoClientTransport transport(tcp);
uint8_t txBuffer[192] = {};
uint8_t rxBuffer[192] = {};
slmp::SlmpClient plc(transport, txBuffer, sizeof(txBuffer), rxBuffer, sizeof(rxBuffer));
bool printedCatalog = false;

void setup() {
    Serial.begin(115200);
    WiFi.begin(kWifiSsid, kWifiPassword);
    while (WiFi.status() != WL_CONNECTED) {
        delay(250);
    }
    slmp::highlevel::configureClientForPlcProfile(plc, kProfile);
    plc.connect(kPlcHost, kTcpPort);
}

void loop() {
    if (!printedCatalog) {
        slmp::highlevel::DeviceRangeCatalog catalog;
        const slmp::Error err = slmp::highlevel::readDeviceRangeCatalogForPlcProfile(plc, kProfile, catalog);
        if (err == slmp::Error::Ok && !catalog.entries.empty()) {
            const slmp::highlevel::DeviceRangeEntry& entry = catalog.entries.front();
            Serial.printf(
                "%s supported=%u range=%s\n",
                entry.device.c_str(),
                entry.supported ? 1U : 0U,
                entry.address_range.c_str());
        } else {
            Serial.printf("catalog failed: %s\n", slmp::errorString(err));
        }
        printedCatalog = true;
    }
    delay(1000);
}

Long device families

LTN, LSTN, LCN, and LZ are 32-bit families in the high-level API. Use slmp::highlevel::ValueType::U32 or slmp::highlevel::ValueType::S32.

Family Unsigned form Signed form Caution
LTN LTN0:D LTN0:L Plain 16-bit access yields wrong data.
LSTN LSTN0:D LSTN0:L Plain 16-bit access yields wrong data.
LCN LCN0:D LCN0:L Plain 16-bit access yields wrong data.
LZ LZ0:D LZ0:L Plain 16-bit access yields wrong data.

Address reference table

Form Meaning Example
Plain word Default value type for the device group. D100
:U Unsigned 16-bit word. D100:U
:S Signed 16-bit word. D100:S
:D Unsigned 32-bit value from two words. D200:D
:L Signed 32-bit value from two words. D200:L
:F IEEE-754 float32 from two words. D300:F
.n One bit inside a word device, where n is hexadecimal 0 through F. D50.3