// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
// SPDX-License-Identifier: AGPL-3.0-or-later

/**
 * @file 30.ModbusTcp.ino
 * @brief Modbus TCP slave/server (Modbus Application Protocol) on TCP/502.
 *
 * Serves a small data model (coils, discrete inputs, holding + input registers)
 * over Modbus TCP. A SCADA/PLC client or a tool like `mbpoll` can read and write
 * it:
 *     mbpoll -m tcp -t 4:hex -r 1 -c 2 <ip>      # read holding regs 0..1
 *     mbpoll -m tcp -t 0 -r 1 1 <ip>             # write coil 0 = 1
 *
 * The application owns the data model: it writes input registers / discrete
 * inputs (read-only to the client) to publish sensor state, reads holding
 * registers / coils the client has written, and is notified of client writes via
 * modbus_on_write(). Modbus has no authentication or encryption - run it only on
 * a trusted control network (front it with the per-IP accept throttle).
 *
 * NOTE: optional services are gated by a compile flag the *library* sources must
 * also see; for PlatformIO enable it for the whole build, e.g.:
 *     build_flags = -DDETWS_ENABLE_MODBUS=1
 * (Arduino IDE: it is already set for you in the build_opt.h beside this sketch, so it builds as-is.)
 */

#define DETWS_ENABLE_MODBUS 1

#include "dwserver.h"
#include "network_drivers/physical/physical.h"
#include "services/modbus/modbus.h"
#include <WiFi.h>

static const char *SSID = "YOUR_SSID";
static const char *PASSWORD = "YOUR_PASSWORD";

DetWebServer server;

// Notified whenever a client writes a coil or holding register.
static void on_write(uint8_t fc, uint16_t start, uint16_t count)
{
    Serial.printf("client write: fc=0x%02X start=%u count=%u\n", fc, start, count);
}

void setup()
{
    Serial.begin(115200);

    init_wifi_physical(SSID, PASSWORD);
    Serial.print("Connecting to WiFi");
    while (!wifi_ready())
    {
        delay(250);
        Serial.print('.');
    }
    Serial.print("\nIP: ");
    Serial.println(WiFi.localIP());
    WiFi.setSleep(false);

    modbus_server_init();
    modbus_set_holding_reg(0, 0x1234); // client-writable registers
    modbus_set_input_reg(0, 0);        // application-published (read-only to client)
    modbus_on_write(on_write);

    server.listen(502, PROTO_MODBUS);
    server.begin();
    Serial.println("Modbus TCP slave on :502");
}

void loop()
{
    server.handle();

    // Publish a live value into an input register the client can poll.
    static uint32_t last = 0;
    if (millis() - last >= 1000)
    {
        last = millis();
        modbus_set_input_reg(0, (uint16_t)(millis() / 1000)); // uptime seconds
    }
}
