From 7b7b826643371cd16c98d08f1cc1885b24c10534 Mon Sep 17 00:00:00 2001 From: Rocketct Date: Thu, 24 Oct 2024 15:05:05 +0200 Subject: [PATCH 01/17] added client T1S --- src/ModbusT1SClient.cpp | 571 ++++++++++++++++++++++++++++++++++++++++ src/ModbusT1SClient.h | 261 ++++++++++++++++++ 2 files changed, 832 insertions(+) create mode 100644 src/ModbusT1SClient.cpp create mode 100644 src/ModbusT1SClient.h diff --git a/src/ModbusT1SClient.cpp b/src/ModbusT1SClient.cpp new file mode 100644 index 0000000..7b893ea --- /dev/null +++ b/src/ModbusT1SClient.cpp @@ -0,0 +1,571 @@ +/* + This file is part of the ArduinoModbus library. + Copyright (c) 2024 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#if (defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_UNOR4_MINIMA)) +#include + +extern "C" { +#include "libmodbus/modbus.h" +#include "libmodbus/modbus-rtu.h" +} + +#include "ModbusT1SClient.h" + +size_t const UDP_RX_MSG_BUF_SIZE = 16 + 1; + +/** + * @class ModbusT1SClientClass + * Class for Modbus T1S Client communication. + * + * This class provides functionalities to communicate with a Modbus T1S server. + */ + +/** + * Default constructor for ModbusT1SClientClass. + * + * Initializes the Modbus client with a default timeout of 1000 milliseconds. + */ +ModbusT1SClientClass::ModbusT1SClientClass() : + ModbusClient(1000) +{ +} + +/** + * Constructor for ModbusT1SClientClass with RS485 support. + * + * Initializes the Modbus client with a default timeout of 1000 milliseconds and sets up RS485 communication. + * + * @param rs485 Reference to an RS485Class object for RS485 communication. + */ +ModbusT1SClientClass::ModbusT1SClientClass(RS485Class& rs485) : + ModbusClient(1000), + _rs485(&rs485) +{ +} + +/** + * Destructor for ModbusT1SClientClass. + * + * Cleans up any resources used by the ModbusT1SClientClass. + */ +ModbusT1SClientClass::~ModbusT1SClientClass() +{ +} + +/** + * Initializes the Modbus client with the specified RS485 instance, baud rate, and configuration. + * + * This function sets up the Modbus client for communication using the specified RS485 instance, baud rate, and configuration. + * + * @param rs485 Reference to an RS485Class object for RS485 communication. + * @param baudrate The baud rate for the Modbus communication. + * @param config The configuration for the Modbus communication (e.g., parity, stop bits). + * @return int Returns 1 if initialization is successful, 0 otherwise. + */ +int ModbusT1SClientClass::begin(RS485Class& rs485, unsigned long baudrate, uint16_t config) +{ + _rs485 = &rs485; + return begin(baudrate, config); +} + +/** + * Sets the IP address of the Modbus server. + * + * This function sets the IP address of the Modbus server that the client will communicate with. + * + * @param server_ip The IP address of the Modbus server. + */ +void ModbusT1SClientClass::setServerIp(IPAddress server_ip) +{ + _server_ip = server_ip; +} + +/** + * Sets the port number of the Modbus server. + * + * This function sets the port number of the Modbus server that the client will communicate with. + * + * @param server_port The port number of the Modbus server. + */ +void ModbusT1SClientClass::setServerPort(uint16_t server_port) +{ + _server_port = server_port; +} + +/** + * Sets the Modbus ID for the client. + * + * This function sets the Modbus ID for the client to use when communicating with the Modbus server. + * + * @param id The Modbus ID to use. + */ +void ModbusT1SClientClass::setModbusId(uint16_t id) +{ + _modbus_id = id; +} + +/** + * Sets the timeout for receiving a response from the Modbus server. + * + * This function sets the timeout for receiving a response from the Modbus server. + * + * @param timeout The timeout value in milliseconds. + */ +void ModbusT1SClientClass::setRxTimeout(unsigned long timeout) +{ + _rx_timeout = timeout; +} +/** + * Reads the status of a coil from the Modbus server. + * + * This function sends a request to read the status of a coil at the specified address + * from the Modbus server using the provided UDP client and port. + * + * @param address The address of the coil to read. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. + * @param port The port number to use for the communication. + * @return int The status of the coil (1 for ON, 0 for OFF) or -1 if an error occurs. + */ +int ModbusT1SClientClass::coilRead(int address, Arduino_10BASE_T1S_UDP * client, uint16_t port) +{ + int res = -1; + if(client == nullptr) { + return res; + } + uint8_t tx_buf[6] = {(uint8_t)(port & 0xFF00) >> 8, (uint8_t)(port & 0x00FF), + (uint8_t)((_modbus_id & 0xFF00) >> 8), (uint8_t)_modbus_id, + (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; + int tx_packet_size = sizeof(tx_buf); + write(tx_buf, tx_packet_size, client); + unsigned long start = millis(); + while(millis() - start < _rx_timeout) { + if(read(client) > 0) { + if(checkPacket(port, _modbus_id, address)) { + res = int(udp_rx_buf.at(6)); + udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); + break; + } + udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); + } + } + return res; +} + +/** + * Reads the status of a coil from the Modbus server with a specified ID. + * + * This function sends a request to read the status of a coil at the specified address + * from the Modbus server with the given ID using the provided UDP client and port. + * + * @param id The ID of the Modbus server. + * @param address The address of the coil to read. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. + * @param port The port number to use for the communication. + * @return int The status of the coil (1 for ON, 0 for OFF) or -1 if an error occurs. + */ +int ModbusT1SClientClass::coilRead(int id, int address, Arduino_10BASE_T1S_UDP * client, uint16_t port) +{ + int res = -1; + if(client == nullptr) { + return res; + } + uint8_t tx_buf[6] = {(uint8_t)(port & 0xFF00) >> 8, (uint8_t)(port & 0x00FF), + (uint8_t)((id & 0xFF00) >> 8), (uint8_t)id, + (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; + int tx_packet_size = sizeof(tx_buf); + write(tx_buf, tx_packet_size, client); + unsigned long start = millis(); + while(millis() - start < _rx_timeout) { + if(read(client)) { + if(checkPacket(port, id, address)) { + res = int(udp_rx_buf.at(6)); + udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); + break; + } + udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); + } + } + return res; +} + +/** + * Writes a value to a coil on the Modbus server. + * + * This function sends a request to write a value to a coil at the specified address + * on the Modbus server using the provided UDP client and port. + * + * @param address The address of the coil to write to. + * @param value The value to write to the coil (1 for ON, 0 for OFF). + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. + * @param port The port number to use for the communication. + * @return int 1 if the write operation is successful, -1 if an error occurs. + */ +int ModbusT1SClientClass::coilWrite(int address, uint8_t value, Arduino_10BASE_T1S_UDP * client, uint16_t port) +{ + if(client == nullptr) { + return -1; + } + uint8_t tx_buf[7] = {(uint8_t)(port & 0xFF00) >> 8, (uint8_t)(port & 0x00FF), + (uint8_t)((_modbus_id & 0xFF00) >> 8), (uint8_t)_modbus_id, + (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address, value}; + int tx_packet_size = sizeof(tx_buf); + write(tx_buf, tx_packet_size, client); + return 1; +} + + +/** + * Writes a value to a coil on the Modbus server with a specified ID. + * + * This function sends a request to write a value to a coil at the specified address + * on the Modbus server with the given ID using the provided UDP client and port. + * + * @param id The ID of the Modbus server. + * @param address The address of the coil to write to. + * @param value The value to write to the coil (1 for ON, 0 for OFF). + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. + * @param port The port number to use for the communication. + * @return int 1 if the write operation is successful, -1 if an error occurs. + */ +int ModbusT1SClientClass::coilWrite(int id, int address, uint8_t value, Arduino_10BASE_T1S_UDP * client, uint16_t port) +{ + if(client == nullptr) { + return -1; + } + uint8_t tx_buf[7] = {(uint8_t)(port & 0xFF00) >> 8, (uint8_t)(port & 0x00FF), (uint8_t)((id & 0xFF00) >> 8), (uint8_t)id, + (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address, value}; + int tx_packet_size = sizeof(tx_buf); + write(tx_buf, tx_packet_size, client); + return 1; +} + +/** + * Reads the status of a discrete input from the Modbus server. + * + * This function sends a request to read the status of a discrete input at the specified address + * from the Modbus server using the provided UDP client and port. + * + * @param address The address of the discrete input to read. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. + * @param port The port number to use for the communication. + * @return int The status of the discrete input (1 for ON, 0 for OFF) or -1 if an error occurs. + */ +int ModbusT1SClientClass::discreteInputRead(int address, Arduino_10BASE_T1S_UDP * client, uint16_t port) +{ + int res = -1; + if(client == nullptr) { + return res; + } + + uint8_t tx_buf[6] = {(uint8_t)(port & 0xFF00) >> 8, (uint8_t)(port & 0x00FF), + (uint8_t)((_modbus_id & 0xFF00) >> 8), (uint8_t)_modbus_id, + (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; + int tx_packet_size = sizeof(tx_buf); + write(tx_buf, tx_packet_size, client); + unsigned long start = millis(); + while(millis() - start < _rx_timeout) { + if(read(client)) { + if(checkPacket(port, _modbus_id, address)) { + res = int(udp_rx_buf.at(6)); + udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); + break; + } + udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); + } + } + return res; +} + +/** + * Reads the status of a discrete input from the Modbus server with a specified ID. + * + * This function sends a request to read the status of a discrete input at the specified address + * from the Modbus server with the given ID using the provided UDP client and port. + * + * @param id The ID of the Modbus server. + * @param address The address of the discrete input to read. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. + * @param port The port number to use for the communication. + * @return int The status of the discrete input (1 for ON, 0 for OFF) or -1 if an error occurs. + */ +int ModbusT1SClientClass::discreteInputRead(int id, int address, Arduino_10BASE_T1S_UDP * client, uint16_t port) +{ + int res = -1; + if(client == nullptr) { + return res; + } + uint8_t tx_buf[6] = {(uint8_t)(port & 0xFF00) >> 8, (uint8_t)(port & 0x00FF), + (uint8_t)((id & 0xFF00) >> 8), (uint8_t)id, + (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; + int tx_packet_size = sizeof(tx_buf); + write(tx_buf, tx_packet_size, client); + unsigned long start = millis(); + while(millis() - start < _rx_timeout) { + if(read(client)) { + if(checkPacket(port, id, address)) { + res = int(udp_rx_buf.at(6)); + udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); + break; + } + udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); + } + } + return res; +} + +/** + * Reads the value of an input register from the Modbus server. + * + * This function sends a request to read the value of an input register at the specified address + * from the Modbus server using the provided UDP client and port. + * + * @param address The address of the input register to read. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. + * @param port The port number to use for the communication. + * @return long The value of the input register or -1 if an error occurs. + */ +long ModbusT1SClientClass::inputRegisterRead(int address, Arduino_10BASE_T1S_UDP * client, uint16_t port) +{ + long res = -1; + if(client == nullptr) { + return res; + } + uint8_t tx_buf[6] = {(uint8_t)(port & 0xFF00) >> 8, (uint8_t)(port & 0x00FF), + (uint8_t)((_modbus_id & 0xFF00) >> 8), (uint8_t)_modbus_id, + (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; + int tx_packet_size = sizeof(tx_buf); + write(tx_buf, tx_packet_size, client); + unsigned long start = millis(); + while(millis() - start < _rx_timeout) { + if(read(client)) { + if(checkPacket(port, _modbus_id, address)) { + res = int(udp_rx_buf.at(6) << 8 | udp_rx_buf.at(7)); + udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); + break; + } + udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); + } + } + return res; +} + +/** + * Reads the value of an input register from the Modbus server with a specified ID. + * + * This function sends a request to read the value of an input register at the specified address + * from the Modbus server with the given ID using the provided UDP client and port. + * + * @param id The ID of the Modbus server. + * @param address The address of the input register to read. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. + * @param port The port number to use for the communication. + * @return long The value of the input register or -1 if an error occurs. + */ +long ModbusT1SClientClass::inputRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client, uint16_t port) +{ + long res = -1; + if(client == nullptr) { + return res; + } + + uint8_t tx_buf[6] = {(uint8_t)(port & 0xFF00) >> 8, (uint8_t)(port & 0x00FF), + (uint8_t)((id & 0xFF00) >> 8), (uint8_t)id, + (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; + int tx_packet_size = sizeof(tx_buf); + write(tx_buf, tx_packet_size, client); + unsigned long start = millis(); + while(millis() - start < _rx_timeout) { + if(read(client) > 0) { + if(checkPacket(port, id, address)) { + res = int(udp_rx_buf.at(6) << 8 | udp_rx_buf.at(7)); + udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); + break; + } + udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); + } + } + return res; +} + +/** + * Reads a value from a holding register on a Modbus server. + * + * This function sends a request to read a single holding register on a Modbus server + * using the specified client and port. + * + * @param address The address of the holding register to read from. + * @param client A pointer to an Arduino_10BASE_T1S_UDP client used for communication. + * @param port The port number to use for the communication. + * @return Returns the value of the holding register on success, or -1 if the client is null. + */ +long ModbusT1SClientClass::holdingRegisterRead(int address, Arduino_10BASE_T1S_UDP * client, uint16_t port) +{ + long res = -1; + if(client == nullptr) { + return res; + } + uint8_t tx_buf[6] = {(uint8_t)(port & 0xFF00) >> 8, (uint8_t)(port & 0x00FF), + (uint8_t)((_modbus_id & 0xFF00) >> 8), (uint8_t)_modbus_id, + (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; + int tx_packet_size = sizeof(tx_buf); + write(tx_buf, tx_packet_size, client); + unsigned long start = millis(); + while(millis() - start < _rx_timeout) { + int const rx_packet_size = client->parsePacket(); + if(read(client)) { + if(checkPacket(port, _modbus_id, address)) { + res = int(udp_rx_buf.at(6) << 8 | udp_rx_buf.at(7)); + udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); + break; + } + udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); + } + } + return res; +} + +/** + * Writes a value to a holding register on a Modbus server. + * + * This function sends a request to write a single holding register on a Modbus server + * using the specified client and port. + * + * @param id The ID of the Modbus server. + * @param address The address of the holding register to write to. + * @param value The value to write to the holding register. + * @param client A pointer to an Arduino_10BASE_T1S_UDP client used for communication. + * @param port The port number to use for the communication. + * @return Returns 1 on success, or -1 if the client is null. + */ +long ModbusT1SClientClass::holdingRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client, uint16_t port) +{ + long res = -1; + if(client == nullptr) { + return res; + } + uint8_t tx_buf[6] = {(uint8_t)(port & 0xFF00) >> 8, (uint8_t)(port & 0x00FF), + (uint8_t)((id & 0xFF00) >> 8), (uint8_t)(id & 0x00FF), + (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; + + int tx_packet_size = sizeof(tx_buf); + write(tx_buf, tx_packet_size, client); + unsigned long start = millis(); + while(millis() - start < _rx_timeout) { + if(read(client)) { + if(checkPacket(port, id, address)) { + res = int(udp_rx_buf.at(6) << 8 | udp_rx_buf.at(7)); + udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); + break; + } + udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); + } + } + return res; +} + +/** + * Writes a value to a holding register on the Modbus server. + * + * This function sends a request to write a value to a holding register at the specified address + * on the Modbus server using the provided UDP client. + * + * @param address The address of the holding register to write to. + * @param value The value to write to the holding register. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. + * @return int 1 if the write operation is successful, -1 if an error occurs. + */ +int ModbusT1SClientClass::holdingRegisterWrite(int address, uint16_t value, Arduino_10BASE_T1S_UDP * client, uint16_t port) +{ + if(client == nullptr) { + return -1; + } + uint8_t tx_buf[8] = {(uint8_t)(port & 0xFF00) >> 8, (uint8_t)(port & 0x00FF), + (uint8_t)((_modbus_id & 0xFF00) >> 8), (uint8_t)_modbus_id, + (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address, (uint8_t)((value & 0xFF00) >> 8), (uint8_t)(value & 0x00FF)}; + int tx_packet_size = sizeof(tx_buf); + write(tx_buf, tx_packet_size, client); + return 1; +} + + +/** + * Writes a value to a holding register on a Modbus server. + * + * This function sends a request to write a single holding register on a Modbus server + * using the specified client and port. + * + * @param id The ID of the Modbus server. + * @param address The address of the holding register to write to. + * @param value The value to write to the holding register. + * @param client A pointer to an Arduino_10BASE_T1S_UDP client used for communication. + * @param port The port number to use for the communication. + * @return Returns 1 on success, or -1 if the client is null. + */ +int ModbusT1SClientClass::holdingRegisterWrite(int id, int address, uint16_t value, Arduino_10BASE_T1S_UDP * client, uint16_t port) +{ + if(client == nullptr) { + return -1; + } + uint8_t tx_buf[8] = {(uint8_t)(port & 0xFF00) >> 8, (uint8_t)(port & 0x00FF), + (uint8_t)((id & 0xFF00) >> 8), (uint8_t)id, + (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address, + (uint8_t)((value & 0xFF00) >> 8), (uint8_t)(value & 0x00FF)}; + + int tx_packet_size = sizeof(tx_buf); + write(tx_buf, tx_packet_size, client); + return 1; +} + +void ModbusT1SClientClass::write(uint8_t * buf, int len, Arduino_10BASE_T1S_UDP * client) +{ + client->beginPacket(_server_ip, _server_port); + client->write((const uint8_t *)buf, len); + client->endPacket(); +} + +int ModbusT1SClientClass::read(Arduino_10BASE_T1S_UDP * client) +{ + int const rx_packet_size = client->parsePacket(); + if (rx_packet_size) + { + uint8_t rx_msg_buf[rx_packet_size] = {0}; + int bytes_read = client->read(rx_msg_buf, rx_packet_size - 1); + while (bytes_read != 0) { + std::copy(rx_msg_buf, rx_msg_buf + bytes_read, std::back_inserter(udp_rx_buf)); + bytes_read = client->read(rx_msg_buf, UDP_RX_MSG_BUF_SIZE - 1); + } + client->flush(); + return udp_rx_buf.size(); + } + return 0; +} + +bool ModbusT1SClientClass::checkPacket(uint16_t port, uint16_t id, uint16_t address) +{ + uint16_t port_rec = udp_rx_buf.at(0) << 8 | udp_rx_buf.at(1); + uint16_t id_rcv = udp_rx_buf.at(2) << 8 | udp_rx_buf.at(3); + uint16_t add_rcv = udp_rx_buf.at(4) << 8 | udp_rx_buf.at(5); + if(port_rec == port && add_rcv == address && id_rcv == id) { + return true; + } + return false; +} + +ModbusT1SClientClass ModbusT1SClient; +#endif diff --git a/src/ModbusT1SClient.h b/src/ModbusT1SClient.h new file mode 100644 index 0000000..9ecb293 --- /dev/null +++ b/src/ModbusT1SClient.h @@ -0,0 +1,261 @@ +/* + This file is part of the ArduinoModbus library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _MODBUS_T1S_CLIENT_H_INCLUDED +#define _MODBUS_T1S_CLIENT_H_INCLUDED +#if (defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_UNOR4_MINIMA)) + + +#include "ModbusClient.h" +#include +#include +#include + + + +#define RX_TIMEOUT 1000 + +class ModbusT1SClientClass : public ModbusClient { +public: + ModbusT1SClientClass(); + ModbusT1SClientClass(RS485Class& rs485); + virtual ~ModbusT1SClientClass(); + + /** + * Start the Modbus T1S client with the specified parameters + * + * @param baudrate Baud rate to use + * @param config serial config. to use defaults to SERIAL_8N1 + * + * Return 1 on success, 0 on failure + */ + int begin(unsigned long baudrate, uint16_t config = SERIAL_8N1); + int begin(RS485Class& rs485, unsigned long baudrate, uint16_t config = SERIAL_8N1); + +/** + * Sets the IP address of the Modbus server. + * + * This function sets the IP address of the Modbus server that the client will communicate with. + * + * @param server_ip The IP address of the Modbus server. Default is IPAddress(0, 0, 0, 0). + */ +void setServerIp(IPAddress server_ip = IPAddress(0, 0, 0, 0)); + +/** + * Sets the port number of the Modbus server. + * + * This function sets the port number of the Modbus server that the client will communicate with. + * + * @param server_port The port number of the Modbus server. Default is 8889. + */ +void setServerPort(uint16_t server_port = 8889); + +/** + * Sets the Modbus ID of the client. + * + * This function sets the Modbus ID that the client will use for communication. + * + * @param id The Modbus ID. + */ +void setModbusId(uint16_t id); + +/** + * Reads the status of a coil from the Modbus server. + * + * This function sends a request to read the status of a coil at the specified address + * from the Modbus server using the provided UDP client and port. + * + * @param address The address of the coil to read. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. + * @param port The port number of the Modbus server. Default is 0. + * @return int The status of the coil (1 for ON, 0 for OFF) or -1 if an error occurs. + */ +int coilRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t port = 0); + +/** + * Reads the status of a coil from the Modbus server with a specified ID. + * + * This function sends a request to read the status of a coil at the specified address + * from the Modbus server with the given ID using the provided UDP client and port. + * + * @param id The ID of the Modbus server. + * @param address The address of the coil to read. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. + * @param port The port number of the Modbus server. Default is 0. + * @return int The status of the coil (1 for ON, 0 for OFF) or -1 if an error occurs. + */ +int coilRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t port = 0); + +/** + * Writes a value to a coil on the Modbus server. + * + * This function sends a request to write a value to a coil at the specified address + * on the Modbus server using the provided UDP client and port. + * + * @param address The address of the coil to write to. + * @param value The value to write to the coil (1 for ON, 0 for OFF). + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. + * @param port The port number of the Modbus server. Default is 0. + * @return int Returns 1 if the write operation is successful, 0 otherwise. + */ +int coilWrite(int address, uint8_t value, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t port = 0); + +/** + * Writes a value to a coil on the Modbus server with a specified ID. + * + * This function sends a request to write a value to a coil at the specified address + * on the Modbus server with the given ID using the provided UDP client and port. + * + * @param id The ID of the Modbus server. + * @param address The address of the coil to write to. + * @param value The value to write to the coil (1 for ON, 0 for OFF). + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. + * @param port The port number of the Modbus server. Default is 0. + * @return int Returns 1 if the write operation is successful, 0 otherwise. + */ +int coilWrite(int id, int address, uint8_t value, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t port = 0); + +/** + * Reads the status of a discrete input from the Modbus server. + * + * This function sends a request to read the status of a discrete input at the specified address + * from the Modbus server using the provided UDP client and port. + * + * @param address The address of the discrete input to read. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. + * @param port The port number of the Modbus server. Default is 0. + * @return int The status of the discrete input (1 for ON, 0 for OFF) or -1 if an error occurs. + */ +int discreteInputRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t port = 0); + +/** + * Reads the status of a discrete input from the Modbus server with a specified ID. + * + * This function sends a request to read the status of a discrete input at the specified address + * from the Modbus server with the given ID using the provided UDP client and port. + * + * @param id The ID of the Modbus server. + * @param address The address of the discrete input to read. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. + * @param port The port number of the Modbus server. Default is 0. + * @return int The status of the discrete input (1 for ON, 0 for OFF) or -1 if an error occurs. + */ +int discreteInputRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t port = 0); + +/** + * Reads the value of an input register from the Modbus server. + * + * This function sends a request to read the value of an input register at the specified address + * from the Modbus server using the provided UDP client and port. + * + * @param address The address of the input register to read. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. + * @param port The port number of the Modbus server. Default is 0. + * @return long The value of the input register or -1 if an error occurs. + */ +long inputRegisterRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t port = 0); + +/** + * Reads the value of an input register from the Modbus server with a specified ID. + * + * This function sends a request to read the value of an input register at the specified address + * from the Modbus server with the given ID using the provided UDP client and port. + * + * @param id The ID of the Modbus server. + * @param address The address of the input register to read. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. + * @param port The port number of the Modbus server. Default is 0. + * @return long The value of the input register or -1 if an error occurs. + */ +long inputRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t port = 0); + +/** + * Writes a value to a holding register on the Modbus server. + * + * This function sends a request to write a value to a holding register at the specified address + * on the Modbus server using the provided UDP client and port. + * + * @param address The address of the holding register to write to. + * @param value The value to write to the holding register. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. + * @param port The port number of the Modbus server. Default is 0. + * @return int Returns 1 if the write operation is successful, 0 otherwise. + */ +int holdingRegisterWrite(int address, uint16_t value, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t port = 0); + +/** + * Writes a value to a holding register on the Modbus server with a specified ID. + * + * This function sends a request to write a value to a holding register at the specified address + * on the Modbus server with the given ID using the provided UDP client and port. + * + * @param id The ID of the Modbus server. + * @param address The address of the holding register to write to. + * @param value The value to write to the holding register. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. + * @param port The port number of the Modbus server. Default is 0. + * @return int Returns 1 if the write operation is successful, 0 otherwise. + */ +int holdingRegisterWrite(int id, int address, uint16_t value, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t port = 0); + +/** + * Reads the value of a holding register from the Modbus server. + * + * This function sends a request to read the value of a holding register at the specified address + * from the Modbus server using the provided UDP client and port. + * + * @param address The address of the holding register to read. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. + * @param port The port number of the Modbus server. Default is 0. + * @return long The value of the holding register or -1 if an error occurs. + */ +long holdingRegisterRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t port = 0); + +/** + * Reads the value of a holding register from the Modbus server with a specified ID. + * + * This function sends a request to read the value of a holding register at the specified address + * from the Modbus server with the given ID using the provided UDP client and port. + * + * @param id The ID of the Modbus server. + * @param address The address of the holding register to read. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. + * @param port The port number of the Modbus server. Default is 0. + * @return long The value of the holding register or -1 if an error occurs. + */ +long holdingRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t port = 0); + +void setRxTimeout(unsigned long timeout = RX_TIMEOUT); +private: + void write(uint8_t * buf, int len, Arduino_10BASE_T1S_UDP * client); + int read(Arduino_10BASE_T1S_UDP * client); + bool checkPacket(uint16_t port, uint16_t id, uint16_t address); + +private: + unsigned long _rx_timeout = RX_TIMEOUT; + IPAddress _server_ip = IPAddress(0, 0, 0, 0); + std::vector udp_rx_buf; + uint16_t _server_port = 8889; + uint16_t _modbus_id = 0; + RS485Class* _rs485 = &RS485; +}; + +extern ModbusT1SClientClass ModbusT1SClient; +#endif +#endif From fa2bd80b573d9d2e21d5c871e8d6ffa8dd4e48c8 Mon Sep 17 00:00:00 2001 From: Rocketct Date: Thu, 24 Oct 2024 15:05:47 +0200 Subject: [PATCH 02/17] added examples T1S --- .../T1S/ModbusT1SClient/ModbusT1SClient.ino | 165 ++++++++++++++ .../T1S/ModbusT1SClient/arduino_secrets.h | 9 + ...dbusT1SClientTemperatureHumiditySensor.ino | 165 ++++++++++++++ .../arduino_secrets.h | 8 + .../T1S/ModbusT1SServer/ModbusT1SServer.ino | 199 +++++++++++++++++ .../T1S/ModbusT1SServer/arduino_secrets.h | 7 + ...dbusT1SServerTemperatureHumiditySensor.ino | 208 ++++++++++++++++++ .../arduino_secrets.h | 7 + 8 files changed, 768 insertions(+) create mode 100644 examples/T1S/ModbusT1SClient/ModbusT1SClient.ino create mode 100644 examples/T1S/ModbusT1SClient/arduino_secrets.h create mode 100644 examples/T1S/ModbusT1SClientTemperatureHumiditySensor/ModbusT1SClientTemperatureHumiditySensor.ino create mode 100644 examples/T1S/ModbusT1SClientTemperatureHumiditySensor/arduino_secrets.h create mode 100644 examples/T1S/ModbusT1SServer/ModbusT1SServer.ino create mode 100644 examples/T1S/ModbusT1SServer/arduino_secrets.h create mode 100644 examples/T1S/ModbusT1SServerTemperatureHumiditySensor/ModbusT1SServerTemperatureHumiditySensor.ino create mode 100644 examples/T1S/ModbusT1SServerTemperatureHumiditySensor/arduino_secrets.h diff --git a/examples/T1S/ModbusT1SClient/ModbusT1SClient.ino b/examples/T1S/ModbusT1SClient/ModbusT1SClient.ino new file mode 100644 index 0000000..94693e5 --- /dev/null +++ b/examples/T1S/ModbusT1SClient/ModbusT1SClient.ino @@ -0,0 +1,165 @@ +/* + Modbus T1S Client Toggle + + This sketch demonstrates how to send commands to a Modbus T1S server connected + via T1S Single Pair Ethernet. + + Circuit: + - T1S shield + - Uno WiFi R4 +*/ + +#include // ArduinoModbus depends on the ArduinoRS485 library +#include +#include "arduino_secrets.h" +/************************************************************************************** + CONSTANTS + **************************************************************************************/ +static uint8_t const T1S_PLCA_NODE_ID = 2; + +static IPAddress const ip_addr { + 192, 168, 42, 100 + T1S_PLCA_NODE_ID +}; +static IPAddress const network_mask { + 255, 255, 255, 0 +}; +static IPAddress const gateway { + 192, 168, 42, 100 +}; + +static T1SPlcaSettings const t1s_plca_settings { + T1S_PLCA_NODE_ID +}; +static T1SMacSettings const t1s_default_mac_settings; + +static IPAddress const UDP_SERVER_IP_ADDR = {192, 168, 42, 100 + 0}; + +/************************************************************************************** + GLOBAL VARIABLES + **************************************************************************************/ +auto const tc6_io = new TC6::TC6_Io +( SPI + , CS_PIN + , RESET_PIN + , IRQ_PIN); +auto const tc6_inst = new TC6::TC6_Arduino_10BASE_T1S(tc6_io); +Arduino_10BASE_T1S_UDP udp_client; + + +void setup() { + Serial.begin(115200); + while (!Serial); + + Serial.println("Modbus T1S Client Toggle"); + + /* Initialize digital IO interface for interfacing + with the LAN8651. + */ + pinMode(IRQ_PIN, INPUT_PULLUP); + attachInterrupt(digitalPinToInterrupt(IRQ_PIN), + []() { + tc6_io->onInterrupt(); + }, + FALLING); + + /* Initialize IO module. */ + if (!tc6_io->begin()) + { + Serial.println("'tc6_io::begin(...)' failed."); + for (;;) { } + } + + MacAddress const mac_addr = MacAddress::create_from_uid(); + + if (!tc6_inst->begin(ip_addr + , network_mask + , gateway + , mac_addr + , t1s_plca_settings + , t1s_default_mac_settings)) + { + Serial.println("'TC6::begin(...)' failed."); + for (;;) { } + } + + Serial.print("IP\t"); + Serial.println(ip_addr); + Serial.println(mac_addr); + Serial.println(t1s_plca_settings); + Serial.println(t1s_default_mac_settings); + + if (!udp_client.begin(UDP_CLIENT_PORT)) + { + Serial.println("begin(...) failed for UDP client"); + for (;;) { } + } + + /* A0 -> LOCAL_ENABLE -> DO NOT feed power from board to network. */ + tc6_inst->digitalWrite(TC6::DIO::A0, false); + /* A1 -> T1S_DISABLE -> Open the switch connecting network to board by pulling EN LOW. */ + tc6_inst->digitalWrite(TC6::DIO::A1, false); + + ModbusT1SClient.setServerIp(UDP_SERVER_IP_ADDR); + ModbusT1SClient.setServerPort(UDP_SERVER_PORT); + ModbusT1SClient.setModbusId(MODBUS_ID); + + Serial.println("UDP_Client"); +} + +void loop() { + tc6_inst->service(); + + static unsigned long prev_beacon_check = 0; + static unsigned long prev_udp_packet_sent = 0; + + auto const now = millis(); + + if ((now - prev_beacon_check) > 1000) + { + prev_beacon_check = now; + if (!tc6_inst->getPlcaStatus(OnPlcaStatus)) { + Serial.println("getPlcaStatus(...) failed"); + } + } + // for (slave) id 1: write the value of 0x01, to the coil at address 0x00 + int res = ModbusT1SClient.coilRead(0x00, &udp_client, UDP_READ_COIL_PORT); + + if (res == -1) { + Serial.println("Failed to read coil! "); + } else { + Serial.print("Coil value: "); + Serial.println(res); + } + + res = ModbusT1SClient.coilWrite(0x00, 1, &udp_client, UDP_WRITE_COIL_PORT); + if (res == -1) { + Serial.println("Failed to write coil! "); + } else { + Serial.println("write done"); + } + + res = ModbusT1SClient.inputRegisterRead(0x00, &udp_client, UDP_READ_IR_PORT); + if (res == -1) { + Serial.println("Failed to read Input Register! "); + } else { + Serial.print("Input Register value: "); + Serial.println(res); + } + +} + +static void OnPlcaStatus(bool success, bool plcaStatus) +{ + if (!success) + { + Serial.println("PLCA status register read failed"); + return; + } + + if (plcaStatus) { + Serial.println("PLCA Mode active"); + } else { + Serial.println("CSMA/CD fallback"); + tc6_inst->enablePlca(); + } +} \ No newline at end of file diff --git a/examples/T1S/ModbusT1SClient/arduino_secrets.h b/examples/T1S/ModbusT1SClient/arduino_secrets.h new file mode 100644 index 0000000..c134b4e --- /dev/null +++ b/examples/T1S/ModbusT1SClient/arduino_secrets.h @@ -0,0 +1,9 @@ +static uint16_t const UDP_CLIENT_PORT = 8888; +static uint16_t const UDP_SERVER_PORT = 8889; +#define UDP_READ_COIL_PORT 1 +#define UDP_WRITE_COIL_PORT 2 +#define UDP_READ_DI_PORT 3 +#define UDP_READ_IR_PORT 4 +#define UDP_READ_HR_PORT 5 +#define UDP_WRITE_HR_PORT 6 +#define MODBUS_ID 42 \ No newline at end of file diff --git a/examples/T1S/ModbusT1SClientTemperatureHumiditySensor/ModbusT1SClientTemperatureHumiditySensor.ino b/examples/T1S/ModbusT1SClientTemperatureHumiditySensor/ModbusT1SClientTemperatureHumiditySensor.ino new file mode 100644 index 0000000..793a319 --- /dev/null +++ b/examples/T1S/ModbusT1SClientTemperatureHumiditySensor/ModbusT1SClientTemperatureHumiditySensor.ino @@ -0,0 +1,165 @@ +/* + Modbus T1S Client Temperature Humidity sensor + + This sketch creates a Modbus T1S Client and demonstrates + how to use the ModbusT1S API to communicate. + - Arduino Uno Wifi R4 + - T1S shield + - SPE ethernet connected to the client + - all the terminations placed on the hardware +*/ + +#include // ArduinoModbus depends on the ArduinoRS485 library +#include +#include "arduino_secrets.h" +/************************************************************************************** + CONSTANTS + **************************************************************************************/ +static uint8_t const T1S_PLCA_NODE_ID = 2; + +static IPAddress const ip_addr { + 192, 168, 42, 100 + T1S_PLCA_NODE_ID +}; +static IPAddress const network_mask { + 255, 255, 255, 0 +}; +static IPAddress const gateway { + 192, 168, 42, 100 +}; + +static T1SPlcaSettings const t1s_plca_settings { + T1S_PLCA_NODE_ID +}; +static T1SMacSettings const t1s_default_mac_settings; + +static IPAddress const UDP_SERVER_IP_ADDR = {192, 168, 42, 100 + 0}; + +/************************************************************************************** + GLOBAL VARIABLES + **************************************************************************************/ +auto const tc6_io = new TC6::TC6_Io +( SPI + , CS_PIN + , RESET_PIN + , IRQ_PIN); +auto const tc6_inst = new TC6::TC6_Arduino_10BASE_T1S(tc6_io); +Arduino_10BASE_T1S_UDP udp_client; + + +void setup() { + Serial.begin(115200); + while (!Serial); + + Serial.println("Modbus T1S Client Toggle"); + + /* Initialize digital IO interface for interfacing + with the LAN8651. + */ + pinMode(IRQ_PIN, INPUT_PULLUP); + attachInterrupt(digitalPinToInterrupt(IRQ_PIN), + []() { + tc6_io->onInterrupt(); + }, + FALLING); + + /* Initialize IO module. */ + if (!tc6_io->begin()) + { + Serial.println("'tc6_io::begin(...)' failed."); + for (;;) { } + } + + MacAddress const mac_addr = MacAddress::create_from_uid(); + + if (!tc6_inst->begin(ip_addr + , network_mask + , gateway + , mac_addr + , t1s_plca_settings + , t1s_default_mac_settings)) + { + Serial.println("'TC6::begin(...)' failed."); + for (;;) { } + } + + Serial.print("IP\t"); + Serial.println(ip_addr); + Serial.println(mac_addr); + Serial.println(t1s_plca_settings); + Serial.println(t1s_default_mac_settings); + + if (!udp_client.begin(UDP_CLIENT_PORT)) + { + Serial.println("begin(...) failed for UDP client"); + for (;;) { } + } + + /* A0 -> LOCAL_ENABLE -> DO NOT feed power from board to network. */ + tc6_inst->digitalWrite(TC6::DIO::A0, false); + /* A1 -> T1S_DISABLE -> Open the switch connecting network to board by pulling EN LOW. */ + tc6_inst->digitalWrite(TC6::DIO::A1, true); + + ModbusT1SClient.setServerIp(UDP_SERVER_IP_ADDR); + ModbusT1SClient.setServerPort(UDP_SERVER_PORT); + + + Serial.println("UDP_Client"); +} + +unsigned long start = 0; +void loop() { + tc6_inst->service(); + + static unsigned long prev_beacon_check = 0; + static unsigned long prev_udp_packet_sent = 0; + + auto const now = millis(); + + if ((now - prev_beacon_check) > 1000) + { + prev_beacon_check = now; + if (!tc6_inst->getPlcaStatus(OnPlcaStatus)) { + Serial.println("getPlcaStatus(...) failed"); + } + } + + if ((millis() - start) > 1000) + { + int res = ModbusT1SClient.inputRegisterRead(1, 0x01, &udp_client, UDP_READ_IR_PORT); + if (res == -1) { + Serial.println("Failed to read temperature! "); + } else { + int16_t const temperature_raw =res; + float const temperature_deg = temperature_raw / 10.f; + Serial.print("Temperature: "); + Serial.println(temperature_deg); + } + + res = ModbusT1SClient.inputRegisterRead(1, 0x02, &udp_client, UDP_READ_IR_PORT); + if (res == -1) { + Serial.println("Failed to read humidity! "); + } else { + int16_t const humidity_raw = res; + float const humidity_per_cent = humidity_raw / 10.f; + Serial.print("Humidity: "); + Serial.println(humidity_per_cent); + } + start = millis(); + } +} + +static void OnPlcaStatus(bool success, bool plcaStatus) +{ + if (!success) + { + Serial.println("PLCA status register read failed"); + return; + } + + if (plcaStatus) { + Serial.println("PLCA Mode active"); + } else { + Serial.println("CSMA/CD fallback"); + tc6_inst->enablePlca(); + } +} \ No newline at end of file diff --git a/examples/T1S/ModbusT1SClientTemperatureHumiditySensor/arduino_secrets.h b/examples/T1S/ModbusT1SClientTemperatureHumiditySensor/arduino_secrets.h new file mode 100644 index 0000000..b6af131 --- /dev/null +++ b/examples/T1S/ModbusT1SClientTemperatureHumiditySensor/arduino_secrets.h @@ -0,0 +1,8 @@ +static uint16_t const UDP_CLIENT_PORT = 8888; +static uint16_t const UDP_SERVER_PORT = 8889; +#define UDP_READ_COIL_PORT 1 +#define UDP_WRITE_COIL_PORT 2 +#define UDP_READ_DI_PORT 3 +#define UDP_READ_IR_PORT 4 +#define UDP_READ_HR_PORT 5 +#define UDP_WRITE_HR_PORT 6 \ No newline at end of file diff --git a/examples/T1S/ModbusT1SServer/ModbusT1SServer.ino b/examples/T1S/ModbusT1SServer/ModbusT1SServer.ino new file mode 100644 index 0000000..3c001b6 --- /dev/null +++ b/examples/T1S/ModbusT1SServer/ModbusT1SServer.ino @@ -0,0 +1,199 @@ +/* + Modbus T1S Server LED + + + This sketch demonstrates how to receive commands from a Modbus T1S Client connected + via T1S Single Pair Ethernet. + + Circuit: + - T1S shield + - Uno WiFi R4 +*/ + +#include // ArduinoModbus depends on the ArduinoRS485 library +#include +#include "arduino_secrets.h" + +/************************************************************************************** + CONSTANTS + **************************************************************************************/ +#define RS485_SERIAL Serial1 +#define RS485_TX_PIN 1 +#define RS485_RX_PIN 0 +#define RS485_DE_PIN 8 +#define RS485_RE_PIN 7 +#define PRE_DELAY 100 +#define POST_DELAY 100 +#define PWR_CARRIER_RATIO 0.18f +RS485Class serial485(RS485_SERIAL, RS485_TX_PIN, RS485_DE_PIN, RS485_RE_PIN); + +static unsigned int const MODBUS_BAUDRATE = 9600; +static float const MODBUS_BIT_DURATION = 1.f / MODBUS_BAUDRATE; +static float const MODBUS_PRE_DELAY_BR = MODBUS_BIT_DURATION * 9.6f * 3.5f * 1e6; +static float const MODBUS_POST_DELAY_BR = MODBUS_BIT_DURATION * 9.6f * 3.5f * 1e6; + +static int const MODBUS_DEVICE_ID = 1; +static int const MODBUS_DEVICE_TEMPERATURE_REGISTER = 0x0001; +static int const MODBUS_DEVICE_HUMIDITY_REGISTER = 0x0002; + +static uint8_t const T1S_PLCA_NODE_ID = 0; /* The UDP server doubles as PLCA coordinator. */ + +static IPAddress const ip_addr { + 192, 168, 42, 100 + T1S_PLCA_NODE_ID +}; +static IPAddress const network_mask { + 255, 255, 255, 0 +}; +static IPAddress const gateway { + 192, 168, 42, 100 +}; + +static T1SPlcaSettings const t1s_plca_settings { + T1S_PLCA_NODE_ID +}; +static T1SMacSettings const t1s_default_mac_settings; + +/************************************************************************************** + GLOBAL VARIABLES + **************************************************************************************/ +auto const tc6_io = new TC6::TC6_Io +( SPI + , CS_PIN + , RESET_PIN + , IRQ_PIN); +auto const tc6_inst = new TC6::TC6_Arduino_10BASE_T1S(tc6_io); +Arduino_10BASE_T1S_UDP udp_server; + +/************************************************************************************** + SETUP/LOOP + **************************************************************************************/ +void setup() { + Serial.begin(115200); + + Serial.println("Modbus RTU Server LED"); + + /* Initialize digital IO interface for interfacing + with the LAN8651. + */ + pinMode(IRQ_PIN, INPUT_PULLUP); + attachInterrupt(digitalPinToInterrupt(IRQ_PIN), + []() { + tc6_io->onInterrupt(); + }, + FALLING); + + /* Initialize IO module. */ + if (!tc6_io->begin()) + { + Serial.println("'TC6_Io::begin(...)' failed."); + for (;;) { } + } + + MacAddress const mac_addr = MacAddress::create_from_uid(); + + if (!tc6_inst->begin(ip_addr + , network_mask + , gateway + , mac_addr + , t1s_plca_settings + , t1s_default_mac_settings)) + { + Serial.println("'TC6::begin(...)' failed."); + for (;;) { } + } + + Serial.print("IP\t"); + Serial.println(ip_addr); + Serial.println(mac_addr); + Serial.println(t1s_plca_settings); + Serial.println(t1s_default_mac_settings); + + if (!udp_server.begin(UDP_SERVER_PORT)) + { + Serial.println("begin(...) failed for UDP coil read server "); + for (;;) { } + } + + tc6_inst->digitalWrite(TC6::DIO::A0, true); + /* A1 -> T1S_DISABLE -> close the switch connecting network to board. */ + tc6_inst->digitalWrite(TC6::DIO::A1, true); + + serial485.setDelays(MODBUS_PRE_DELAY_BR, MODBUS_POST_DELAY_BR); + if (!ModbusT1SServer.begin(serial485, MODBUS_BAUDRATE, SERIAL_8N1)) { + Serial.println("Failed to start Modbus RTU Client!"); + while (1); + } + + ModbusT1SServer.setT1SServer(&udp_server); + Serial.println("UDP_Server"); +} + +void loop() { + /* Services the hardware and the protocol stack. + Must be called cyclic. The faster the better. + */ + tc6_inst->service(); + + static unsigned long prev_beacon_check = 0; + + auto const now = millis(); + if ((now - prev_beacon_check) > 1000) + { + prev_beacon_check = now; + if (!tc6_inst->getPlcaStatus(OnPlcaStatus)) { + Serial.println("getPlcaStatus(...) failed"); + } + } + + switch (ModbusT1SServer.parsePacket()) + { + case UDP_READ_COIL_PORT: + Serial.println("Read coil"); + ModbusT1SServer.coilRead(); + break; + + case UDP_WRITE_COIL_PORT: + Serial.println("Write coil"); + ModbusT1SServer.coilWrite(); + break; + + case UDP_READ_DI_PORT: + Serial.println("Read discrete input"); + ModbusT1SServer.discreteInputRead(); + break; + + case UDP_READ_IR_PORT: + Serial.println("Read input register"); + ModbusT1SServer.inputRegisterRead(); + break; + + case UDP_READ_HR_PORT: + Serial.println("Read holding register"); + ModbusT1SServer.holdingRegisterRead(); + break; + + case UDP_WRITE_HR_PORT: + Serial.println("Write holding register"); + ModbusT1SServer.holdingRegisterWrite(); + break; + + default: + break; + } +} + +static void OnPlcaStatus(bool success, bool plcaStatus) +{ + if (!success) + { + Serial.println("PLCA status register read failed"); + return; + } + + if (plcaStatus) { + Serial.println("PLCA Mode active"); + } else { + Serial.println("CSMA/CD fallback"); + tc6_inst->enablePlca(); + } +} \ No newline at end of file diff --git a/examples/T1S/ModbusT1SServer/arduino_secrets.h b/examples/T1S/ModbusT1SServer/arduino_secrets.h new file mode 100644 index 0000000..20e6fec --- /dev/null +++ b/examples/T1S/ModbusT1SServer/arduino_secrets.h @@ -0,0 +1,7 @@ +static uint16_t const UDP_SERVER_PORT = 8889; +#define UDP_READ_COIL_PORT 1 +#define UDP_WRITE_COIL_PORT 2 +#define UDP_READ_DI_PORT 3 +#define UDP_READ_IR_PORT 4 +#define UDP_READ_HR_PORT 5 +#define UDP_WRITE_HR_PORT 6 diff --git a/examples/T1S/ModbusT1SServerTemperatureHumiditySensor/ModbusT1SServerTemperatureHumiditySensor.ino b/examples/T1S/ModbusT1SServerTemperatureHumiditySensor/ModbusT1SServerTemperatureHumiditySensor.ino new file mode 100644 index 0000000..eb4d711 --- /dev/null +++ b/examples/T1S/ModbusT1SServerTemperatureHumiditySensor/ModbusT1SServerTemperatureHumiditySensor.ino @@ -0,0 +1,208 @@ +/* + Modbus T1S Server Temperature Humidity sensor + + This sketch creates a Modbus T1S Server and demonstrates + how to use read temperature and humidity values as sensed + by the RTU Modbus capable MD02 sensor. + + Circuit: + - Arduino Uno Wifi R4 + - T1S shield + - SPE ethernet connected to the client + - ISO GND connected to GND of the Modbus RTU server + - Y connected to A/Y of the Modbus RTU client + - Z connected to B/Z of the Modbus RTU client + - all the terminations placed on the hardware +*/ + +#include // ArduinoModbus depends on the ArduinoRS485 library +#include +#include "arduino_secrets.h" + +/************************************************************************************** + CONSTANTS + **************************************************************************************/ +#define RS485_SERIAL Serial1 +#define RS485_TX_PIN 1 +#define RS485_RX_PIN 0 +#define RS485_DE_PIN 8 +#define RS485_RE_PIN 7 +#define PRE_DELAY 100 +#define POST_DELAY 100 +#define PWR_CARRIER_RATIO 0.18f +RS485Class serial485(RS485_SERIAL, RS485_TX_PIN, RS485_DE_PIN, RS485_RE_PIN); + +static unsigned int const MODBUS_BAUDRATE = 9600; +static float const MODBUS_BIT_DURATION = 1.f / MODBUS_BAUDRATE; +static float const MODBUS_PRE_DELAY_BR = MODBUS_BIT_DURATION * 9.6f * 3.5f * 1e6; +static float const MODBUS_POST_DELAY_BR = MODBUS_BIT_DURATION * 9.6f * 3.5f * 1e6; + +static int const MODBUS_DEVICE_ID = 1; +static int const MODBUS_DEVICE_TEMPERATURE_REGISTER = 0x0001; +static int const MODBUS_DEVICE_HUMIDITY_REGISTER = 0x0002; + +static uint8_t const T1S_PLCA_NODE_ID = 0; /* The UDP server doubles as PLCA coordinator. */ + +static IPAddress const ip_addr { + 192, 168, 42, 100 + T1S_PLCA_NODE_ID +}; +static IPAddress const network_mask { + 255, 255, 255, 0 +}; +static IPAddress const gateway { + 192, 168, 42, 100 +}; + +static T1SPlcaSettings const t1s_plca_settings { + T1S_PLCA_NODE_ID +}; +static T1SMacSettings const t1s_default_mac_settings; + +/************************************************************************************** + GLOBAL VARIABLES + **************************************************************************************/ +auto const tc6_io = new TC6::TC6_Io +( SPI + , CS_PIN + , RESET_PIN + , IRQ_PIN); +auto const tc6_inst = new TC6::TC6_Arduino_10BASE_T1S(tc6_io); +Arduino_10BASE_T1S_UDP udp_server; + +/************************************************************************************** + SETUP/LOOP + **************************************************************************************/ +void setup() { + Serial.begin(115200); + + Serial.println("Modbus RTU Server LED"); + + /* Initialize digital IO interface for interfacing + with the LAN8651. + */ + pinMode(IRQ_PIN, INPUT_PULLUP); + attachInterrupt(digitalPinToInterrupt(IRQ_PIN), + []() { + tc6_io->onInterrupt(); + }, + FALLING); + + /* Initialize IO module. */ + if (!tc6_io->begin()) + { + Serial.println("'TC6_Io::begin(...)' failed."); + for (;;) { } + } + + MacAddress const mac_addr = MacAddress::create_from_uid(); + + if (!tc6_inst->begin(ip_addr + , network_mask + , gateway + , mac_addr + , t1s_plca_settings + , t1s_default_mac_settings)) + { + Serial.println("'TC6::begin(...)' failed."); + for (;;) { } + } + + Serial.print("IP\t"); + Serial.println(ip_addr); + Serial.println(mac_addr); + Serial.println(t1s_plca_settings); + Serial.println(t1s_default_mac_settings); + + if (!udp_server.begin(UDP_SERVER_PORT)) + { + Serial.println("begin(...) failed for UDP coil read server "); + for (;;) { } + } + + tc6_inst->digitalWrite(TC6::DIO::A0, false); + /* A1 -> T1S_DISABLE -> close the switch connecting network to board. */ + tc6_inst->digitalWrite(TC6::DIO::A1, true); + + serial485.setDelays(MODBUS_PRE_DELAY_BR, MODBUS_POST_DELAY_BR); + unsigned long br = MODBUS_BAUDRATE; + uint16_t config = SERIAL_8N1; + if (!ModbusT1SServer.begin(serial485, br, config)) { + Serial.println("Failed to start Modbus RTU Client!"); + while (1); + } + + ModbusT1SServer.setT1SServer(&udp_server); + Serial.println("UDP_Server"); + Serial.println(MODBUS_PRE_DELAY_BR); + Serial.println(MODBUS_POST_DELAY_BR); +} + +void loop() { + /* Services the hardware and the protocol stack. + Must be called cyclic. The faster the better. + */ + tc6_inst->service(); + + static unsigned long prev_beacon_check = 0; + + auto const now = millis(); + if ((now - prev_beacon_check) > 1000) + { + prev_beacon_check = now; + if (!tc6_inst->getPlcaStatus(OnPlcaStatus)) { + Serial.println("getPlcaStatus(...) failed"); + } + } + + switch (ModbusT1SServer.parsePacket()) + { + case UDP_READ_COIL_PORT: + Serial.println("Read coil"); + ModbusT1SServer.coilRead(); + break; + + case UDP_WRITE_COIL_PORT: + Serial.println("Write coil"); + ModbusT1SServer.coilWrite(); + break; + + case UDP_READ_DI_PORT: + Serial.println("Read discrete input"); + ModbusT1SServer.discreteInputRead(); + break; + + case UDP_READ_IR_PORT: + Serial.println("Read input register"); + ModbusT1SServer.inputRegisterRead(); + break; + + case UDP_READ_HR_PORT: + Serial.println("Read holding register"); + ModbusT1SServer.holdingRegisterRead(); + break; + + case UDP_WRITE_HR_PORT: + Serial.println("Write holding register"); + ModbusT1SServer.holdingRegisterWrite(); + break; + + default: + break; + } +} + +static void OnPlcaStatus(bool success, bool plcaStatus) +{ + if (!success) + { + Serial.println("PLCA status register read failed"); + return; + } + + if (plcaStatus) { + Serial.println("PLCA Mode active"); + } else { + Serial.println("CSMA/CD fallback"); + tc6_inst->enablePlca(); + } +} \ No newline at end of file diff --git a/examples/T1S/ModbusT1SServerTemperatureHumiditySensor/arduino_secrets.h b/examples/T1S/ModbusT1SServerTemperatureHumiditySensor/arduino_secrets.h new file mode 100644 index 0000000..47405ff --- /dev/null +++ b/examples/T1S/ModbusT1SServerTemperatureHumiditySensor/arduino_secrets.h @@ -0,0 +1,7 @@ +static uint16_t const UDP_SERVER_PORT = 8889; +#define UDP_READ_COIL_PORT 1 +#define UDP_WRITE_COIL_PORT 2 +#define UDP_READ_DI_PORT 3 +#define UDP_READ_IR_PORT 4 +#define UDP_READ_HR_PORT 5 +#define UDP_WRITE_HR_PORT 6 \ No newline at end of file From ae53b66edc229e5e9572787a0c41429ff32742e0 Mon Sep 17 00:00:00 2001 From: Rocketct Date: Thu, 24 Oct 2024 15:06:19 +0200 Subject: [PATCH 03/17] added Server T1S --- src/ModbusT1SServer.cpp | 386 ++++++++++++++++++++++++++++++++++++++++ src/ModbusT1SServer.h | 161 +++++++++++++++++ 2 files changed, 547 insertions(+) create mode 100644 src/ModbusT1SServer.cpp create mode 100644 src/ModbusT1SServer.h diff --git a/src/ModbusT1SServer.cpp b/src/ModbusT1SServer.cpp new file mode 100644 index 0000000..e8181a5 --- /dev/null +++ b/src/ModbusT1SServer.cpp @@ -0,0 +1,386 @@ +/* + This file is part of the ArduinoModbus library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#if (defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_UNOR4_MINIMA)) +#include + +extern "C" { +#include "libmodbus/modbus.h" +#include "libmodbus/modbus-rtu.h" +} + +#include "ModbusT1SServer.h" + +size_t const UDP_RX_MSG_BUF_SIZE = 16 + 1; + +/** + * @class ModbusT1SServerClass + * Class for Modbus T1S Server communication. + * + * This class provides functionalities to communicate with a Modbus T1S server. + */ +ModbusT1SServerClass::ModbusT1SServerClass() +{ +} + +/** + * Constructor for ModbusT1SServerClass with RS485 support. + * + * Initializes the Modbus server with RS485 communication. + * + * @param rs485 Reference to an RS485Class object for RS485 communication. + */ +ModbusT1SServerClass::ModbusT1SServerClass(RS485Class& rs485) : _rs485(&rs485) +{ + +} + +/** + * Destructor for ModbusT1SServerClass. + */ +ModbusT1SServerClass::~ModbusT1SServerClass() +{ +} + +/** + * Initializes the Modbus server with the specified RS485 instance, baud rate, and configuration. + * + * This function sets up the Modbus server for communication using the specified RS485 instance, baud rate, and configuration. + * + * @param id (slave) id of the server + * @param baudrate The baud rate for the Modbus communication. + * @param config The configuration for the Modbus communication (e.g., parity, stop bits). + * @return int Returns 1 if initialization is successful, 0 otherwise. + */ +int ModbusT1SServerClass::begin(int id, unsigned long baudrate, uint16_t config) +{ + if(!ModbusRTUClient.begin(*_rs485, baudrate, config)) { + return -1; + } + + ModbusRTUClient.setTimeout(2*1000UL); + return 1; +} + +/** + * Initializes the Modbus server with the specified RS485 instance, baud rate, and configuration. + * + * This function sets up the Modbus server for communication using the specified RS485 instance, baud rate, and configuration. + * + * @param rs485 Reference to an RS485Class object for RS485 communication. + * @param id (slave) id of the server + * @param baudrate The baud rate for the Modbus communication. + * @param config The configuration for the Modbus communication (e.g., parity, stop bits). + * @return int Returns 1 if initialization is successful, 0 otherwise. + */ +int ModbusT1SServerClass::begin(RS485Class& rs485, int id, unsigned long baudrate, uint16_t config) +{ + _rs485 = &rs485; + return begin(id, baudrate, config); +} + +/** + * Initializes the Modbus server with the specified RS485 instance, baud rate, and configuration. + * + * This function sets up the Modbus server for communication using the specified RS485 instance, baud rate, and configuration. + * + * @param rs485 Reference to an RS485Class object for RS485 communication. + * @param baudrate The baud rate for the Modbus communication. + * @param config The configuration for the Modbus communication (e.g., parity, stop bits). + * @return int Returns 1 if initialization is successful, 0 otherwise. + */ +int ModbusT1SServerClass::begin(RS485Class& rs485, unsigned long baudrate, uint16_t config) +{ + _rs485 = &rs485; + if(!ModbusRTUClient.begin(rs485, baudrate, config)) { + return -1; + } + + ModbusRTUClient.setTimeout(2*1000UL); + return 1; +} + +/** + * Sets the Arduino_10BASE_T1S_UDP server for communication. + * + * This function sets the Arduino_10BASE_T1S_UDP server that the Modbus server will communicate with. + * + * @param server A pointer to the Arduino_10BASE_T1S_UDP server. + */ +int ModbusT1SServerClass::poll() +{ + uint8_t request[MODBUS_RTU_MAX_ADU_LENGTH]; + + int requestLength = modbus_receive(_mb, request); + + if (requestLength > 0) { + modbus_reply(_mb, request, requestLength, &_mbMapping); + return 1; + } + return 0; +} + +/** + * Sets the Arduino_10BASE_T1S_UDP server for communication. + * + * This function sets the Arduino_10BASE_T1S_UDP server that the Modbus server will communicate with. + * + * @param server A pointer to the Arduino_10BASE_T1S_UDP server. + */ +int ModbusT1SServerClass::coilRead(int address) +{ + int res = -1; + if(_server == nullptr) { + return res; + } + + int modbus_id = udp_rx_buf.at(2) << 8 | udp_rx_buf.at(3); + int address_mod = udp_rx_buf.at(4) << 8 | udp_rx_buf.at(5); + if(address != -1) { + address_mod = address; + } + + if (ModbusRTUClient.requestFrom(modbus_id, COILS, address_mod, 1)) { + if (ModbusRTUClient.available()) { + res = ModbusRTUClient.read(); + udp_rx_buf.push_back((uint8_t)res); + _server->beginPacket(_last_ip, _last_port); + _server->write((uint8_t *)udp_rx_buf.data(), udp_rx_buf.size()); + _server->endPacket(); + } + } + udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); + return res; +} + +/** + * Writes a value to a coil on the Modbus server. + * + * This function sends a request to write a value to a coil at the specified address + * on the Modbus server using the provided UDP client. + * + * @param address The address of the coil to write to. + * @param value The value to write to the coil (1 for ON, 0 for OFF). + * @return int 1 if the write operation is successful, -1 if an error occurs. + */ +int ModbusT1SServerClass::coilWrite(int address, uint8_t value) +{ + int res = -1; + if(_server == nullptr) { + return res; + } + + int modbus_id = udp_rx_buf.at(2) << 8 | udp_rx_buf.at(3); + int address_mod = udp_rx_buf.at(4) << 8 | udp_rx_buf.at(5); + if(address != -1) { + address_mod = address; + } + uint8_t coilValue = int( udp_rx_buf.at(6)); + + ModbusRTUClient.beginTransmission(modbus_id, COILS, address_mod, 1); + res = ModbusRTUClient.write(coilValue); + if (!ModbusRTUClient.endTransmission()) { + res = -1; + } + udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); + return res; +} + +/** + * Reads the status of a discrete input from the Modbus server. + * + * This function sends a request to read the status of a discrete input at the specified address + * from the Modbus server using the provided UDP client. + * + * @param address The address of the discrete input to read. + * @return int The status of the discrete input (1 for ON, 0 for OFF) or -1 if an error occurs. + */ +int ModbusT1SServerClass::discreteInputRead(int address) { + int res = -1; + if(_server == nullptr) { + return res; + } + + int modbus_id = udp_rx_buf.at(2) << 8 | udp_rx_buf.at(3); + int address_mod = udp_rx_buf.at(4) << 8 | udp_rx_buf.at(5); + if(address != -1) { + address_mod = address; + } + + if (ModbusRTUClient.requestFrom(modbus_id, DISCRETE_INPUTS, address_mod, 1)) { + if (ModbusRTUClient.available()) { + res = ModbusRTUClient.read(); + udp_rx_buf.push_back((uint8_t)res); + _server->beginPacket(_last_ip, _last_port); + _server->write((uint8_t *)udp_rx_buf.data(), udp_rx_buf.size()); + _server->endPacket(); + } + } + udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); + return res; +} + +/** + * Reads the value of an input register from the Modbus server. + * + * This function sends a request to read the value of an input register at the specified address + * from the Modbus server using the provided UDP client. + * + * @param address The address of the input register to read. + * @return long The value of the input register or -1 if an error occurs. + */ +long ModbusT1SServerClass::inputRegisterRead(int address) +{ + long res = -1; + if(_server == nullptr) { + return res; + } + + int modbus_id = udp_rx_buf.at(2) << 8 | udp_rx_buf.at(3); + int address_mod = udp_rx_buf.at(4) << 8 | udp_rx_buf.at(5); + if(address != -1) { + address_mod = address; + } + + if (ModbusRTUClient.requestFrom(modbus_id, INPUT_REGISTERS, address_mod, 1)) { + if (ModbusRTUClient.available()) { + int16_t data = ModbusRTUClient.read(); + uint8_t tx_buf[2] = {(uint8_t)((data & 0xFF00) >> 8), (uint8_t)(data & 0x00FF)}; + std::copy(tx_buf, tx_buf + 2, std::back_inserter(udp_rx_buf)); + _server->beginPacket(_last_ip, _last_port); + _server->write((uint8_t *)udp_rx_buf.data(), udp_rx_buf.size()); + _server->endPacket(); + } + } + udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); + return res; +} + +/** + * Writes a value to a holding register on the Modbus server. + * + * This function sends a request to write a value to a holding register at the specified address + * on the Modbus server using the provided UDP client. + * + * @param address The address of the holding register to write to. + * @param value The value to write to the holding register. + * @return int 1 if the write operation is successful, -1 if an error occurs. + */ +long ModbusT1SServerClass::holdingRegisterRead(int address) { + long res = -1; + if(_server == nullptr) { + return res; + } + + int modbus_id = udp_rx_buf.at(2) << 8 | udp_rx_buf.at(3); + int address_mod = udp_rx_buf.at(4) << 8 | udp_rx_buf.at(5); + if(address != -1) { + address_mod = address; + } + + if (ModbusRTUClient.requestFrom(modbus_id, HOLDING_REGISTERS, address_mod, 1)) { + if (ModbusRTUClient.available()) { + int16_t data = ModbusRTUClient.read(); + uint8_t tx_buf[2] = {(uint8_t)((data & 0xFF00) >> 8), (uint8_t)(data & 0x00FF)}; + std::copy(tx_buf, tx_buf + 2, std::back_inserter(udp_rx_buf)); + _server->beginPacket(_last_ip, _last_port); + _server->write((uint8_t *)udp_rx_buf.data(), udp_rx_buf.size()); + _server->endPacket(); + } + } + udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); + return res; +} + +/** + * Writes a value to a holding register on the Modbus server. + * + * This function sends a request to write a value to a holding register at the specified address + * on the Modbus server using the provided UDP client. + * + * @param address The address of the holding register to write to. + * @param value The value to write to the holding register. + * @return int 1 if the write operation is successful, -1 if an error occurs. + */ +int ModbusT1SServerClass::holdingRegisterWrite(int address) { + int res = -1; + if(_server == nullptr) { + return res; + } + + int modbus_id = udp_rx_buf.at(2) << 8 | udp_rx_buf.at(3); + int address_mod = udp_rx_buf.at(4) << 8 | udp_rx_buf.at(5); + if(address != -1) { + address_mod = address; + } + uint16_t value = udp_rx_buf.at(6) << 8 | udp_rx_buf.at(7); + + ModbusRTUClient.beginTransmission(modbus_id, HOLDING_REGISTERS, address_mod, 1); + res = ModbusRTUClient.write(value); + if (!ModbusRTUClient.endTransmission()) { + res = -1; + } + udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); + return res; +} + +/** + * Parses the Modbus packet received from the server. + * + * This function parses the Modbus packet received from the server. + * + * @return int The parsed packet or -1 if an error occurs. + */ +int ModbusT1SServerClass::parsePacket() { + int res = -1; + if(_server == nullptr) { + return res; + } + + int const rx_packet_size = _server->parsePacket(); + if (rx_packet_size) + { + uint8_t rx_msg_buf[UDP_RX_MSG_BUF_SIZE] = {0}; + int bytes_read = _server->read(rx_msg_buf, UDP_RX_MSG_BUF_SIZE - 1); + while (bytes_read != 0) { + std::copy(rx_msg_buf, rx_msg_buf + bytes_read, std::back_inserter(udp_rx_buf)); + bytes_read = _server->read(rx_msg_buf, UDP_RX_MSG_BUF_SIZE - 1); + } + res = udp_rx_buf.at(0) << 8 | udp_rx_buf.at(1); + } + + _last_ip = _server->remoteIP(); + _last_port = _server->remotePort(); + _server->flush(); + + return res; +} + +/** + * Sets the Arduino_10BASE_T1S_UDP server for communication. + * + * This function sets the Arduino_10BASE_T1S_UDP server that the Modbus server will communicate with. + * + * @param server A pointer to the Arduino_10BASE_T1S_UDP server. + */ +void ModbusT1SServerClass::setT1SServer(Arduino_10BASE_T1S_UDP * server) { + _server = server; +} + +ModbusT1SServerClass ModbusT1SServer; +#endif diff --git a/src/ModbusT1SServer.h b/src/ModbusT1SServer.h new file mode 100644 index 0000000..67b2fd2 --- /dev/null +++ b/src/ModbusT1SServer.h @@ -0,0 +1,161 @@ +/* + This file is part of the ArduinoModbus library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _MODBUS_T1S_SERVER_H_INCLUDED +#define _MODBUS_T1S_SERVER_H_INCLUDED +#if (defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_UNOR4_MINIMA)) + +#include "ModbusServer.h" +#include +#include +#include "ModbusRTUClient.h" +#include + + + +class ModbusT1SServerClass : public ModbusServer { +public: + /** + * Default constructor for ModbusT1SServerClass. + * + * Initializes the Modbus server with RS485 communication. + */ + ModbusT1SServerClass(); + ModbusT1SServerClass(RS485Class& rs485); + + /** + * Destructor for ModbusT1SServerClass. + */ + virtual ~ModbusT1SServerClass(); + + /** + * Start the Modbus T1S server with the specified parameters + * + * @param id (slave) id of the server + * @param baudrate Baud rate to use + * @param config serial config. to use defaults to SERIAL_8N1 + * + * Return 1 on success, 0 on failure + */ + int begin(int id, unsigned long baudrate, uint16_t config = SERIAL_8N1); + int begin(RS485Class& rs485, int id, unsigned long baudrate, uint16_t config = SERIAL_8N1); + int begin(RS485Class& rs485, unsigned long baudrate, uint16_t config = SERIAL_8N1); + + /** + * Reads a coil from the Modbus server. + * + * This function sends a request to read a coil at the specified address + * from the Modbus server using the provided UDP client. + * + * @param address The address of the coil to read. + * @return int The value of the coil or -1 if an error occurs. + */ + int coilRead(int address = -1); + + /** + * Writes a value to a coil on the Modbus server. + * + * This function sends a request to write a value to a coil at the specified address + * on the Modbus server using the provided UDP client. + * + * @param address The address of the coil to write to. + * @param value The value to write to the coil (1 for ON, 0 for OFF). + * @return int Returns 1 if the write operation is successful, 0 otherwise. + */ + int coilWrite(int address = -1, uint8_t value = 255); + + /** + * Reads the status of a discrete input from the Modbus server. + * + * This function sends a request to read the status of a discrete input at the specified address + * from the Modbus server using the provided UDP client. + * + * @param address The address of the discrete input to read. + * @return int The status of the discrete input (1 for ON, 0 for OFF) or -1 if an error occurs. + */ + int discreteInputRead(int address = -1); + + /** + * Reads the value of an input register from the Modbus server. + * + * This function sends a request to read the value of an input register at the specified address + * from the Modbus server using the provided UDP client. + * + * @param address The address of the input register to read. + * @return long The value of the input register or -1 if an error occurs. + */ + long inputRegisterRead(int address = -1); + + /** + * Writes a value to a holding register on the Modbus server. + * + * This function sends a request to write a value to a holding register at the specified address + * on the Modbus server using the provided UDP client. + * + * @param address The address of the holding register to write to. + * @param value The value to write to the holding register. + * @return int 1 if the write operation is successful, -1 if an error occurs. + */ + long holdingRegisterRead(int address = -1); + + /** + * Writes a value to a holding register on the Modbus server. + * + * This function sends a request to write a value to a holding register at the specified address + * on the Modbus server using the provided UDP client. + * + * @param address The address of the holding register to write to. + * @param value The value to write to the holding register. + * @return int 1 if the write operation is successful, -1 if an error occurs. + */ + int holdingRegisterWrite(int address = -1); + + /** + * Parses the Modbus packet received from the server. + * + * This function parses the Modbus packet received from the server. + * + * @return int The parsed packet or -1 if an error occurs. + */ + int parsePacket(); + + /** + * Set the T1S server to use for communication + * + * @param server The T1S server to use + */ + void setT1SServer(Arduino_10BASE_T1S_UDP * server); + + /** + * Poll interface for requests + */ + virtual int poll(); + +private: + RS485Class* _rs485 = &RS485; + std::vector udp_rx_buf; + Arduino_10BASE_T1S_UDP * _server = nullptr; + IPAddress _last_ip; + uint16_t _last_port; +}; +//Arduino_10BASE_T1S_UDP udp_server; +extern ModbusT1SServerClass ModbusT1SServer; + +#endif +#endif From 150095881b783bbdd2b9ef9b636cf1bbc027cc75 Mon Sep 17 00:00:00 2001 From: Rocketct Date: Thu, 24 Oct 2024 15:06:58 +0200 Subject: [PATCH 04/17] added case for t1s in modbus-private --- src/ArduinoModbus.h | 3 +++ src/libmodbus/modbus-private.h | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ArduinoModbus.h b/src/ArduinoModbus.h index 41f1615..5a89502 100644 --- a/src/ArduinoModbus.h +++ b/src/ArduinoModbus.h @@ -26,4 +26,7 @@ #include "ModbusTCPClient.h" #include "ModbusTCPServer.h" +#include "ModbusT1SClient.h" +#include "ModbusT1SServer.h" + #endif diff --git a/src/libmodbus/modbus-private.h b/src/libmodbus/modbus-private.h index 79e7b93..7c4f541 100644 --- a/src/libmodbus/modbus-private.h +++ b/src/libmodbus/modbus-private.h @@ -10,7 +10,7 @@ #ifndef _MSC_VER # include -#if defined(ARDUINO) && defined(__AVR__) +#if defined(ARDUINO) && (defined(__AVR__) || (ARDUINO_UNOR4_WIFI) || defined(ARDUINO_UNOR4_MINIMA)) #define ssize_t unsigned long #define fd_set void* From 7adc945179faad21982fa8bdfae8884d5bead41d Mon Sep 17 00:00:00 2001 From: Rocketct Date: Thu, 24 Oct 2024 16:34:11 +0200 Subject: [PATCH 05/17] fixed properties and keywords --- keywords.txt | 2 ++ library.properties | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/keywords.txt b/keywords.txt index 845bdb3..108120e 100644 --- a/keywords.txt +++ b/keywords.txt @@ -11,6 +11,8 @@ ModbusRTUClient KEYWORD1 ModbusRTUServer KEYWORD1 ModbusRTUClient KEYWORD1 ModbusTCPServer KEYWORD1 +ModbusT1SClient KEYWORD1 +ModbusT1SServer KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) diff --git a/library.properties b/library.properties index 84aace6..8da0553 100644 --- a/library.properties +++ b/library.properties @@ -3,9 +3,9 @@ version=1.0.9 author=Arduino maintainer=Arduino sentence=Use Modbus equipment with your Arduino. -paragraph=Using TCP or RS485 shields, like the MKR 485 Shield. This library depends on the ArduinoRS485 library. +paragraph=Using TCP, RS485 or T1S shields, like the MKR 485 Shield. This library depends on the ArduinoRS485 library. category=Communication url=https://www.arduino.cc/en/ArduinoModbus/ArduinoModbus -architectures=megaavr,samd,mbed_nano,mbed_portenta,mbed_opta +architectures=megaavr,samd,mbed_nano,mbed_portenta,mbed_opta,renesas_uno includes=ArduinoModbus.h depends=ArduinoRS485 From ab278798a6efe77ca75fd34de5c975ed52caa9e3 Mon Sep 17 00:00:00 2001 From: Rocketct Date: Thu, 31 Oct 2024 10:42:02 +0100 Subject: [PATCH 06/17] added common file to include structure that that are not required to be set by the user --- src/ModbusT1SCommon.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/ModbusT1SCommon.h diff --git a/src/ModbusT1SCommon.h b/src/ModbusT1SCommon.h new file mode 100644 index 0000000..f2029ff --- /dev/null +++ b/src/ModbusT1SCommon.h @@ -0,0 +1,22 @@ +#ifndef _MODBUS_T1S_COMMON_H_INCLUDED +#define _MODBUS_T1S_COMMON_H_INCLUDED +#if (defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_UNOR4_MINIMA)) +#include + +enum ModbusT1SFunctionCode { + UDP_READ_COIL_PORT = 1, + UDP_WRITE_COIL_PORT, + UDP_READ_DI_PORT, + UDP_READ_IR_PORT, + UDP_READ_HR_PORT, + UDP_WRITE_HR_PORT +}; + +auto const tc6_io = new TC6::TC6_Io +( SPI + , CS_PIN + , RESET_PIN + , IRQ_PIN); +auto const tc6_inst = new TC6::TC6_Arduino_10BASE_T1S(tc6_io); +#endif +#endif \ No newline at end of file From 288da45f185128a8cbce924fd46af6eb977d156f Mon Sep 17 00:00:00 2001 From: Rocketct Date: Thu, 31 Oct 2024 10:41:28 +0100 Subject: [PATCH 07/17] added library changes to the client requested from the review --- src/ModbusT1SClient.cpp | 350 ++++++++++++++++++++++++++++++---------- src/ModbusT1SClient.h | 44 +++-- 2 files changed, 291 insertions(+), 103 deletions(-) diff --git a/src/ModbusT1SClient.cpp b/src/ModbusT1SClient.cpp index 7b893ea..a8b2932 100644 --- a/src/ModbusT1SClient.cpp +++ b/src/ModbusT1SClient.cpp @@ -84,6 +84,59 @@ int ModbusT1SClientClass::begin(RS485Class& rs485, unsigned long baudrate, uint1 return begin(baudrate, config); } +int ModbusT1SClientClass::begin(int node_id) +{ + _node_id = node_id; + pinMode(IRQ_PIN, INPUT_PULLUP); + attachInterrupt(digitalPinToInterrupt(IRQ_PIN), + + []() { + tc6_io->onInterrupt(); + }, + FALLING); + + /* Initialize IO module. */ + if (!tc6_io->begin()) + { + return 0; + } + + IPAddress ip_addr = IPAddress(192, 168, 42, 100 + _node_id); + IPAddress network_mask = IPAddress(255, 255, 255, 0); + IPAddress gateway = IPAddress(192, 168, 42, 100); + IPAddress server_addr = IPAddress(192, 168, 42, 100); + _server_ip = server_addr; + + T1SPlcaSettings t1s_plca_settings { + _node_id + }; + + T1SMacSettings const t1s_default_mac_settings; + MacAddress const mac_addr = MacAddress::create_from_uid(); + + if (!tc6_inst->begin(ip_addr + , network_mask + , gateway + , mac_addr + , t1s_plca_settings + , t1s_default_mac_settings)) + { + return 0; + } + + Serial.print("IP\t"); + Serial.println(ip_addr); + Serial.println(mac_addr); + Serial.println(t1s_plca_settings); + Serial.println(t1s_default_mac_settings); + + if (!_client->begin(udp_port)) + { + return 0; + } + return 1; +} + /** * Sets the IP address of the Modbus server. * @@ -142,21 +195,27 @@ void ModbusT1SClientClass::setRxTimeout(unsigned long timeout) * @param port The port number to use for the communication. * @return int The status of the coil (1 for ON, 0 for OFF) or -1 if an error occurs. */ -int ModbusT1SClientClass::coilRead(int address, Arduino_10BASE_T1S_UDP * client, uint16_t port) +int ModbusT1SClientClass::coilRead(int address, Arduino_10BASE_T1S_UDP * client, int port) { int res = -1; - if(client == nullptr) { - return res; + if(client != nullptr) { + _client = client; + } + + int _port = port; + if(port == -1) { + _port = UDP_READ_COIL_PORT; } - uint8_t tx_buf[6] = {(uint8_t)(port & 0xFF00) >> 8, (uint8_t)(port & 0x00FF), + + uint8_t tx_buf[6] = {(uint8_t)(_port & 0xFF00) >> 8, (uint8_t)(_port & 0x00FF), (uint8_t)((_modbus_id & 0xFF00) >> 8), (uint8_t)_modbus_id, (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; int tx_packet_size = sizeof(tx_buf); - write(tx_buf, tx_packet_size, client); + write(tx_buf, tx_packet_size, _client); unsigned long start = millis(); while(millis() - start < _rx_timeout) { - if(read(client) > 0) { - if(checkPacket(port, _modbus_id, address)) { + if(read(_client) > 0) { + if(checkPacket(_port, _modbus_id, address)) { res = int(udp_rx_buf.at(6)); udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); break; @@ -179,21 +238,27 @@ int ModbusT1SClientClass::coilRead(int address, Arduino_10BASE_T1S_UDP * client, * @param port The port number to use for the communication. * @return int The status of the coil (1 for ON, 0 for OFF) or -1 if an error occurs. */ -int ModbusT1SClientClass::coilRead(int id, int address, Arduino_10BASE_T1S_UDP * client, uint16_t port) +int ModbusT1SClientClass::coilRead(int id, int address, Arduino_10BASE_T1S_UDP * client, int port) { int res = -1; - if(client == nullptr) { - return res; + if(client != nullptr) { + _client = client; } - uint8_t tx_buf[6] = {(uint8_t)(port & 0xFF00) >> 8, (uint8_t)(port & 0x00FF), + + int _port = port; + if(port == -1) { + _port = UDP_READ_COIL_PORT; + } + + uint8_t tx_buf[6] = {(uint8_t)(_port & 0xFF00) >> 8, (uint8_t)(_port & 0x00FF), (uint8_t)((id & 0xFF00) >> 8), (uint8_t)id, (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; int tx_packet_size = sizeof(tx_buf); - write(tx_buf, tx_packet_size, client); + write(tx_buf, tx_packet_size, _client); unsigned long start = millis(); while(millis() - start < _rx_timeout) { - if(read(client)) { - if(checkPacket(port, id, address)) { + if(read(_client)) { + if(checkPacket(_port, id, address)) { res = int(udp_rx_buf.at(6)); udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); break; @@ -216,20 +281,25 @@ int ModbusT1SClientClass::coilRead(int id, int address, Arduino_10BASE_T1S_UDP * * @param port The port number to use for the communication. * @return int 1 if the write operation is successful, -1 if an error occurs. */ -int ModbusT1SClientClass::coilWrite(int address, uint8_t value, Arduino_10BASE_T1S_UDP * client, uint16_t port) +int ModbusT1SClientClass::coilWrite(int address, uint8_t value, Arduino_10BASE_T1S_UDP * client, int port) { - if(client == nullptr) { - return -1; + if(client != nullptr) { + _client = client; + } + + int _port = port; + if(port == -1) { + _port = UDP_WRITE_COIL_PORT; } - uint8_t tx_buf[7] = {(uint8_t)(port & 0xFF00) >> 8, (uint8_t)(port & 0x00FF), + + uint8_t tx_buf[7] = {(uint8_t)(_port & 0xFF00) >> 8, (uint8_t)(_port & 0x00FF), (uint8_t)((_modbus_id & 0xFF00) >> 8), (uint8_t)_modbus_id, (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address, value}; int tx_packet_size = sizeof(tx_buf); - write(tx_buf, tx_packet_size, client); + write(tx_buf, tx_packet_size, _client); return 1; } - /** * Writes a value to a coil on the Modbus server with a specified ID. * @@ -243,15 +313,22 @@ int ModbusT1SClientClass::coilWrite(int address, uint8_t value, Arduino_10BASE_T * @param port The port number to use for the communication. * @return int 1 if the write operation is successful, -1 if an error occurs. */ -int ModbusT1SClientClass::coilWrite(int id, int address, uint8_t value, Arduino_10BASE_T1S_UDP * client, uint16_t port) +int ModbusT1SClientClass::coilWrite(int id, int address, uint8_t value, Arduino_10BASE_T1S_UDP * client, int port) { - if(client == nullptr) { - return -1; + if(client != nullptr) { + _client = client; } - uint8_t tx_buf[7] = {(uint8_t)(port & 0xFF00) >> 8, (uint8_t)(port & 0x00FF), (uint8_t)((id & 0xFF00) >> 8), (uint8_t)id, + + + int _port = port; + if(port == -1) { + _port = UDP_WRITE_COIL_PORT; + } + + uint8_t tx_buf[7] = {(uint8_t)(_port & 0xFF00) >> 8, (uint8_t)(_port & 0x00FF), (uint8_t)((id & 0xFF00) >> 8), (uint8_t)id, (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address, value}; int tx_packet_size = sizeof(tx_buf); - write(tx_buf, tx_packet_size, client); + write(tx_buf, tx_packet_size, _client); return 1; } @@ -266,22 +343,27 @@ int ModbusT1SClientClass::coilWrite(int id, int address, uint8_t value, Arduino_ * @param port The port number to use for the communication. * @return int The status of the discrete input (1 for ON, 0 for OFF) or -1 if an error occurs. */ -int ModbusT1SClientClass::discreteInputRead(int address, Arduino_10BASE_T1S_UDP * client, uint16_t port) +int ModbusT1SClientClass::discreteInputRead(int address, Arduino_10BASE_T1S_UDP * client, int port) { int res = -1; - if(client == nullptr) { - return res; + if(client != nullptr) { + _client = client; } - uint8_t tx_buf[6] = {(uint8_t)(port & 0xFF00) >> 8, (uint8_t)(port & 0x00FF), + int _port = port; + if(port == -1) { + _port = UDP_READ_DI_PORT; + } + + uint8_t tx_buf[6] = {(uint8_t)(_port & 0xFF00) >> 8, (uint8_t)(_port & 0x00FF), (uint8_t)((_modbus_id & 0xFF00) >> 8), (uint8_t)_modbus_id, (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; int tx_packet_size = sizeof(tx_buf); - write(tx_buf, tx_packet_size, client); + write(tx_buf, tx_packet_size, _client); unsigned long start = millis(); while(millis() - start < _rx_timeout) { - if(read(client)) { - if(checkPacket(port, _modbus_id, address)) { + if(read(_client)) { + if(checkPacket(_port, _modbus_id, address)) { res = int(udp_rx_buf.at(6)); udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); break; @@ -304,21 +386,27 @@ int ModbusT1SClientClass::discreteInputRead(int address, Arduino_10BASE_T1S_UDP * @param port The port number to use for the communication. * @return int The status of the discrete input (1 for ON, 0 for OFF) or -1 if an error occurs. */ -int ModbusT1SClientClass::discreteInputRead(int id, int address, Arduino_10BASE_T1S_UDP * client, uint16_t port) +int ModbusT1SClientClass::discreteInputRead(int id, int address, Arduino_10BASE_T1S_UDP * client, int port) { int res = -1; - if(client == nullptr) { - return res; + if(client != nullptr) { + _client = client; + } + + int _port = port; + if(port == -1) { + _port = UDP_READ_DI_PORT; } - uint8_t tx_buf[6] = {(uint8_t)(port & 0xFF00) >> 8, (uint8_t)(port & 0x00FF), + + uint8_t tx_buf[6] = {(uint8_t)(_port & 0xFF00) >> 8, (uint8_t)(_port & 0x00FF), (uint8_t)((id & 0xFF00) >> 8), (uint8_t)id, (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; int tx_packet_size = sizeof(tx_buf); - write(tx_buf, tx_packet_size, client); + write(tx_buf, tx_packet_size, _client); unsigned long start = millis(); while(millis() - start < _rx_timeout) { - if(read(client)) { - if(checkPacket(port, id, address)) { + if(read(_client)) { + if(checkPacket(_port, id, address)) { res = int(udp_rx_buf.at(6)); udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); break; @@ -340,21 +428,27 @@ int ModbusT1SClientClass::discreteInputRead(int id, int address, Arduino_10BASE_ * @param port The port number to use for the communication. * @return long The value of the input register or -1 if an error occurs. */ -long ModbusT1SClientClass::inputRegisterRead(int address, Arduino_10BASE_T1S_UDP * client, uint16_t port) +long ModbusT1SClientClass::inputRegisterRead(int address, Arduino_10BASE_T1S_UDP * client, int port) { long res = -1; - if(client == nullptr) { - return res; + if(client != nullptr) { + _client = client; } - uint8_t tx_buf[6] = {(uint8_t)(port & 0xFF00) >> 8, (uint8_t)(port & 0x00FF), + + int _port = port; + if(port == -1) { + _port = UDP_READ_IR_PORT; + } + + uint8_t tx_buf[6] = {(uint8_t)(_port & 0xFF00) >> 8, (uint8_t)(_port & 0x00FF), (uint8_t)((_modbus_id & 0xFF00) >> 8), (uint8_t)_modbus_id, (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; int tx_packet_size = sizeof(tx_buf); - write(tx_buf, tx_packet_size, client); + write(tx_buf, tx_packet_size, _client); unsigned long start = millis(); while(millis() - start < _rx_timeout) { - if(read(client)) { - if(checkPacket(port, _modbus_id, address)) { + if(read(_client)) { + if(checkPacket(_port, _modbus_id, address)) { res = int(udp_rx_buf.at(6) << 8 | udp_rx_buf.at(7)); udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); break; @@ -377,22 +471,27 @@ long ModbusT1SClientClass::inputRegisterRead(int address, Arduino_10BASE_T1S_UDP * @param port The port number to use for the communication. * @return long The value of the input register or -1 if an error occurs. */ -long ModbusT1SClientClass::inputRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client, uint16_t port) +long ModbusT1SClientClass::inputRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client, int port) { long res = -1; - if(client == nullptr) { - return res; + if(client != nullptr) { + _client = client; + } + + int _port = port; + if(port == -1) { + _port = UDP_READ_IR_PORT; } - uint8_t tx_buf[6] = {(uint8_t)(port & 0xFF00) >> 8, (uint8_t)(port & 0x00FF), + uint8_t tx_buf[6] = {(uint8_t)(_port & 0xFF00) >> 8, (uint8_t)(_port & 0x00FF), (uint8_t)((id & 0xFF00) >> 8), (uint8_t)id, (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; int tx_packet_size = sizeof(tx_buf); - write(tx_buf, tx_packet_size, client); + write(tx_buf, tx_packet_size, _client); unsigned long start = millis(); while(millis() - start < _rx_timeout) { - if(read(client) > 0) { - if(checkPacket(port, id, address)) { + if(read(_client) > 0) { + if(checkPacket(_port, id, address)) { res = int(udp_rx_buf.at(6) << 8 | udp_rx_buf.at(7)); udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); break; @@ -414,22 +513,27 @@ long ModbusT1SClientClass::inputRegisterRead(int id, int address, Arduino_10BASE * @param port The port number to use for the communication. * @return Returns the value of the holding register on success, or -1 if the client is null. */ -long ModbusT1SClientClass::holdingRegisterRead(int address, Arduino_10BASE_T1S_UDP * client, uint16_t port) +long ModbusT1SClientClass::holdingRegisterRead(int address, Arduino_10BASE_T1S_UDP * client, int port) { long res = -1; - if(client == nullptr) { - return res; + if(client != nullptr) { + _client = client; + } + + int _port = port; + if(port == -1) { + _port = UDP_READ_HR_PORT; } - uint8_t tx_buf[6] = {(uint8_t)(port & 0xFF00) >> 8, (uint8_t)(port & 0x00FF), + uint8_t tx_buf[6] = {(uint8_t)(_port & 0xFF00) >> 8, (uint8_t)(_port & 0x00FF), (uint8_t)((_modbus_id & 0xFF00) >> 8), (uint8_t)_modbus_id, (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; int tx_packet_size = sizeof(tx_buf); write(tx_buf, tx_packet_size, client); unsigned long start = millis(); while(millis() - start < _rx_timeout) { - int const rx_packet_size = client->parsePacket(); - if(read(client)) { - if(checkPacket(port, _modbus_id, address)) { + int const rx_packet_size = _client->parsePacket(); + if(read(_client)) { + if(checkPacket(_port, _modbus_id, address)) { res = int(udp_rx_buf.at(6) << 8 | udp_rx_buf.at(7)); udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); break; @@ -453,22 +557,28 @@ long ModbusT1SClientClass::holdingRegisterRead(int address, Arduino_10BASE_T1S_U * @param port The port number to use for the communication. * @return Returns 1 on success, or -1 if the client is null. */ -long ModbusT1SClientClass::holdingRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client, uint16_t port) +long ModbusT1SClientClass::holdingRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client, int port) { long res = -1; - if(client == nullptr) { - return res; + if(client != nullptr) { + _client = client; } - uint8_t tx_buf[6] = {(uint8_t)(port & 0xFF00) >> 8, (uint8_t)(port & 0x00FF), + + int _port = port; + if(port == -1) { + _port = UDP_READ_HR_PORT; + } + + uint8_t tx_buf[6] = {(uint8_t)(_port & 0xFF00) >> 8, (uint8_t)(_port & 0x00FF), (uint8_t)((id & 0xFF00) >> 8), (uint8_t)(id & 0x00FF), (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; int tx_packet_size = sizeof(tx_buf); - write(tx_buf, tx_packet_size, client); + write(tx_buf, tx_packet_size, _client); unsigned long start = millis(); while(millis() - start < _rx_timeout) { - if(read(client)) { - if(checkPacket(port, id, address)) { + if(read(_client)) { + if(checkPacket(_port, id, address)) { res = int(udp_rx_buf.at(6) << 8 | udp_rx_buf.at(7)); udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); break; @@ -490,20 +600,25 @@ long ModbusT1SClientClass::holdingRegisterRead(int id, int address, Arduino_10BA * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. * @return int 1 if the write operation is successful, -1 if an error occurs. */ -int ModbusT1SClientClass::holdingRegisterWrite(int address, uint16_t value, Arduino_10BASE_T1S_UDP * client, uint16_t port) +int ModbusT1SClientClass::holdingRegisterWrite(int address, uint16_t value, Arduino_10BASE_T1S_UDP * client, int port) { - if(client == nullptr) { - return -1; + if(client != nullptr) { + _client = client; + } + + int _port = port; + if(port == -1) { + _port = UDP_WRITE_HR_PORT; } - uint8_t tx_buf[8] = {(uint8_t)(port & 0xFF00) >> 8, (uint8_t)(port & 0x00FF), + + uint8_t tx_buf[8] = {(uint8_t)(_port & 0xFF00) >> 8, (uint8_t)(_port & 0x00FF), (uint8_t)((_modbus_id & 0xFF00) >> 8), (uint8_t)_modbus_id, (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address, (uint8_t)((value & 0xFF00) >> 8), (uint8_t)(value & 0x00FF)}; int tx_packet_size = sizeof(tx_buf); - write(tx_buf, tx_packet_size, client); + write(tx_buf, tx_packet_size, _client); return 1; } - /** * Writes a value to a holding register on a Modbus server. * @@ -517,48 +632,54 @@ int ModbusT1SClientClass::holdingRegisterWrite(int address, uint16_t value, Ardu * @param port The port number to use for the communication. * @return Returns 1 on success, or -1 if the client is null. */ -int ModbusT1SClientClass::holdingRegisterWrite(int id, int address, uint16_t value, Arduino_10BASE_T1S_UDP * client, uint16_t port) +int ModbusT1SClientClass::holdingRegisterWrite(int id, int address, uint16_t value, Arduino_10BASE_T1S_UDP * client, int port) { - if(client == nullptr) { - return -1; + if(client != nullptr) { + _client = client; } - uint8_t tx_buf[8] = {(uint8_t)(port & 0xFF00) >> 8, (uint8_t)(port & 0x00FF), + + int _port = port; + if(port == -1) { + _port = UDP_WRITE_HR_PORT; + } + + uint8_t tx_buf[8] = {(uint8_t)(_port & 0xFF00) >> 8, (uint8_t)(_port & 0x00FF), (uint8_t)((id & 0xFF00) >> 8), (uint8_t)id, (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address, (uint8_t)((value & 0xFF00) >> 8), (uint8_t)(value & 0x00FF)}; int tx_packet_size = sizeof(tx_buf); - write(tx_buf, tx_packet_size, client); + write(tx_buf, tx_packet_size, _client); return 1; } void ModbusT1SClientClass::write(uint8_t * buf, int len, Arduino_10BASE_T1S_UDP * client) { - client->beginPacket(_server_ip, _server_port); - client->write((const uint8_t *)buf, len); - client->endPacket(); + _client->beginPacket(_server_ip, _server_port); + _client->write((const uint8_t *)buf, len); + _client->endPacket(); } int ModbusT1SClientClass::read(Arduino_10BASE_T1S_UDP * client) { - int const rx_packet_size = client->parsePacket(); + int const rx_packet_size = _client->parsePacket(); if (rx_packet_size) { uint8_t rx_msg_buf[rx_packet_size] = {0}; - int bytes_read = client->read(rx_msg_buf, rx_packet_size - 1); + int bytes_read = _client->read(rx_msg_buf, rx_packet_size - 1); while (bytes_read != 0) { std::copy(rx_msg_buf, rx_msg_buf + bytes_read, std::back_inserter(udp_rx_buf)); - bytes_read = client->read(rx_msg_buf, UDP_RX_MSG_BUF_SIZE - 1); + bytes_read = _client->read(rx_msg_buf, UDP_RX_MSG_BUF_SIZE - 1); } - client->flush(); + _client->flush(); return udp_rx_buf.size(); } return 0; } -bool ModbusT1SClientClass::checkPacket(uint16_t port, uint16_t id, uint16_t address) +bool ModbusT1SClientClass::checkPacket(int port, uint16_t id, uint16_t address) { - uint16_t port_rec = udp_rx_buf.at(0) << 8 | udp_rx_buf.at(1); + int port_rec = udp_rx_buf.at(0) << 8 | udp_rx_buf.at(1); uint16_t id_rcv = udp_rx_buf.at(2) << 8 | udp_rx_buf.at(3); uint16_t add_rcv = udp_rx_buf.at(4) << 8 | udp_rx_buf.at(5); if(port_rec == port && add_rcv == address && id_rcv == id) { @@ -566,6 +687,61 @@ bool ModbusT1SClientClass::checkPacket(uint16_t port, uint16_t id, uint16_t addr } return false; } +void ModbusT1SClientClass::setT1SClient(Arduino_10BASE_T1S_UDP * client) +{ + _client = client; +} +void ModbusT1SClientClass::setT1SPort(int port) +{ + udp_port = port; +} + +void ModbusT1SClientClass::checkPLCAStatus() +{ + tc6_inst->service(); + + static unsigned long prev_beacon_check = 0; + static unsigned long prev_udp_packet_sent = 0; + + auto const now = millis(); + + if ((now - prev_beacon_check) > 1000) + { + prev_beacon_check = now; + if(callback == nullptr) + { + if (!tc6_inst->getPlcaStatus(OnPlcaStatus_client)) { + Serial.println("getPlcaStatus(...) failed"); + } + } else { + if (!tc6_inst->getPlcaStatus(callback)) { + Serial.println("getPlcaStatus(...) failed"); + } + } + } +} + +void ModbusT1SClientClass::setCallback(callback_f cb) { + if(cb != nullptr) { + callback = cb; + } +} + +static void OnPlcaStatus_client(bool success, bool plcaStatus) +{ + if (!success) + { + Serial.println("PLCA status register read failed"); + return; + } + + if (plcaStatus) { + Serial.println("PLCA Mode active"); + } else { + Serial.println("CSMA/CD fallback"); + tc6_inst->enablePlca(); + } +} ModbusT1SClientClass ModbusT1SClient; #endif diff --git a/src/ModbusT1SClient.h b/src/ModbusT1SClient.h index 9ecb293..e6715e9 100644 --- a/src/ModbusT1SClient.h +++ b/src/ModbusT1SClient.h @@ -25,12 +25,14 @@ #include "ModbusClient.h" #include #include +#include "ModbusT1SCommon.h" #include #define RX_TIMEOUT 1000 - +static void OnPlcaStatus_client(bool success, bool plcaStatus); +using callback_f = void (*)(bool, bool); class ModbusT1SClientClass : public ModbusClient { public: ModbusT1SClientClass(); @@ -47,7 +49,7 @@ class ModbusT1SClientClass : public ModbusClient { */ int begin(unsigned long baudrate, uint16_t config = SERIAL_8N1); int begin(RS485Class& rs485, unsigned long baudrate, uint16_t config = SERIAL_8N1); - + int begin(int node_id); /** * Sets the IP address of the Modbus server. * @@ -86,7 +88,7 @@ void setModbusId(uint16_t id); * @param port The port number of the Modbus server. Default is 0. * @return int The status of the coil (1 for ON, 0 for OFF) or -1 if an error occurs. */ -int coilRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t port = 0); +int coilRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr, int port = -1); /** * Reads the status of a coil from the Modbus server with a specified ID. @@ -100,7 +102,7 @@ int coilRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t po * @param port The port number of the Modbus server. Default is 0. * @return int The status of the coil (1 for ON, 0 for OFF) or -1 if an error occurs. */ -int coilRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t port = 0); +int coilRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr, int port = -1); /** * Writes a value to a coil on the Modbus server. @@ -114,7 +116,7 @@ int coilRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr, uin * @param port The port number of the Modbus server. Default is 0. * @return int Returns 1 if the write operation is successful, 0 otherwise. */ -int coilWrite(int address, uint8_t value, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t port = 0); +int coilWrite(int address, uint8_t value, Arduino_10BASE_T1S_UDP * client = nullptr, int port = -1); /** * Writes a value to a coil on the Modbus server with a specified ID. @@ -129,7 +131,7 @@ int coilWrite(int address, uint8_t value, Arduino_10BASE_T1S_UDP * client = null * @param port The port number of the Modbus server. Default is 0. * @return int Returns 1 if the write operation is successful, 0 otherwise. */ -int coilWrite(int id, int address, uint8_t value, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t port = 0); +int coilWrite(int id, int address, uint8_t value, Arduino_10BASE_T1S_UDP * client = nullptr, int port = -1); /** * Reads the status of a discrete input from the Modbus server. @@ -142,7 +144,7 @@ int coilWrite(int id, int address, uint8_t value, Arduino_10BASE_T1S_UDP * clien * @param port The port number of the Modbus server. Default is 0. * @return int The status of the discrete input (1 for ON, 0 for OFF) or -1 if an error occurs. */ -int discreteInputRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t port = 0); +int discreteInputRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr, int port = -1); /** * Reads the status of a discrete input from the Modbus server with a specified ID. @@ -156,7 +158,7 @@ int discreteInputRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr, ui * @param port The port number of the Modbus server. Default is 0. * @return int The status of the discrete input (1 for ON, 0 for OFF) or -1 if an error occurs. */ -int discreteInputRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t port = 0); +int discreteInputRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr, int port = -1); /** * Reads the value of an input register from the Modbus server. @@ -169,7 +171,7 @@ int discreteInputRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nul * @param port The port number of the Modbus server. Default is 0. * @return long The value of the input register or -1 if an error occurs. */ -long inputRegisterRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t port = 0); +long inputRegisterRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr, int port = -1); /** * Reads the value of an input register from the Modbus server with a specified ID. @@ -183,7 +185,7 @@ long inputRegisterRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr, u * @param port The port number of the Modbus server. Default is 0. * @return long The value of the input register or -1 if an error occurs. */ -long inputRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t port = 0); +long inputRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr, int port = -1); /** * Writes a value to a holding register on the Modbus server. @@ -197,7 +199,7 @@ long inputRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nu * @param port The port number of the Modbus server. Default is 0. * @return int Returns 1 if the write operation is successful, 0 otherwise. */ -int holdingRegisterWrite(int address, uint16_t value, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t port = 0); +int holdingRegisterWrite(int address, uint16_t value, Arduino_10BASE_T1S_UDP * client = nullptr, int port = -1); /** * Writes a value to a holding register on the Modbus server with a specified ID. @@ -212,7 +214,7 @@ int holdingRegisterWrite(int address, uint16_t value, Arduino_10BASE_T1S_UDP * c * @param port The port number of the Modbus server. Default is 0. * @return int Returns 1 if the write operation is successful, 0 otherwise. */ -int holdingRegisterWrite(int id, int address, uint16_t value, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t port = 0); +int holdingRegisterWrite(int id, int address, uint16_t value, Arduino_10BASE_T1S_UDP * client = nullptr, int port = -1); /** * Reads the value of a holding register from the Modbus server. @@ -225,7 +227,7 @@ int holdingRegisterWrite(int id, int address, uint16_t value, Arduino_10BASE_T1S * @param port The port number of the Modbus server. Default is 0. * @return long The value of the holding register or -1 if an error occurs. */ -long holdingRegisterRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t port = 0); +long holdingRegisterRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr, int port = -1); /** * Reads the value of a holding register from the Modbus server with a specified ID. @@ -239,21 +241,31 @@ long holdingRegisterRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr, * @param port The port number of the Modbus server. Default is 0. * @return long The value of the holding register or -1 if an error occurs. */ -long holdingRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr, uint16_t port = 0); - +long holdingRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr, int port = -1); +void setT1SClient(Arduino_10BASE_T1S_UDP * client = nullptr); void setRxTimeout(unsigned long timeout = RX_TIMEOUT); +void setT1SPort(int port = 8889); +void checkPLCAStatus(); +void setCallback(callback_f cb = nullptr); + + private: + callback_f callback = nullptr; void write(uint8_t * buf, int len, Arduino_10BASE_T1S_UDP * client); int read(Arduino_10BASE_T1S_UDP * client); - bool checkPacket(uint16_t port, uint16_t id, uint16_t address); + bool checkPacket(int port, uint16_t id, uint16_t address); private: + unsigned long _rx_timeout = RX_TIMEOUT; IPAddress _server_ip = IPAddress(0, 0, 0, 0); std::vector udp_rx_buf; uint16_t _server_port = 8889; uint16_t _modbus_id = 0; RS485Class* _rs485 = &RS485; + Arduino_10BASE_T1S_UDP * _client = nullptr; + int _node_id = 1; + int udp_port = 0; }; extern ModbusT1SClientClass ModbusT1SClient; From c5e9eefb9efeba57ea4bb0a1c6f11f1c6c7bb6fc Mon Sep 17 00:00:00 2001 From: Rocketct Date: Thu, 31 Oct 2024 10:41:49 +0100 Subject: [PATCH 08/17] added library changes to the Server requested from the review --- src/ModbusT1SServer.cpp | 157 +++++++++++++++++++++++++++++++++++++++- src/ModbusT1SServer.h | 31 +++++++- 2 files changed, 183 insertions(+), 5 deletions(-) diff --git a/src/ModbusT1SServer.cpp b/src/ModbusT1SServer.cpp index e8181a5..cad0359 100644 --- a/src/ModbusT1SServer.cpp +++ b/src/ModbusT1SServer.cpp @@ -27,6 +27,7 @@ extern "C" { #include "ModbusT1SServer.h" size_t const UDP_RX_MSG_BUF_SIZE = 16 + 1; +RS485Class serial485(RS485_SERIAL, RS485_TX_PIN, RS485_DE_PIN, RS485_RE_PIN); /** * @class ModbusT1SServerClass @@ -108,10 +109,72 @@ int ModbusT1SServerClass::begin(RS485Class& rs485, unsigned long baudrate, uint1 { _rs485 = &rs485; if(!ModbusRTUClient.begin(rs485, baudrate, config)) { - return -1; + return 0; + } + return 1; +} + + +int ModbusT1SServerClass::begin(int node_id) +{ + _node_id = node_id; + pinMode(IRQ_PIN, INPUT_PULLUP); + attachInterrupt(digitalPinToInterrupt(IRQ_PIN), + + []() { + tc6_io->onInterrupt(); + }, + FALLING); + + /* Initialize IO module. */ + if (!tc6_io->begin()) + { + return 0; + } + + IPAddress ip_addr = IPAddress(192, 168, 42, 100 + _node_id); + IPAddress network_mask = IPAddress(255, 255, 255, 0); + IPAddress gateway = IPAddress(192, 168, 42, 100); + + T1SPlcaSettings t1s_plca_settings { + _node_id + }; + T1SMacSettings const t1s_default_mac_settings; + MacAddress const mac_addr = MacAddress::create_from_uid(); + + if (!tc6_inst->begin(ip_addr + , network_mask + , gateway + , mac_addr + , t1s_plca_settings + , t1s_default_mac_settings)) + { + return 0; + } + + Serial.print("IP\t"); + Serial.println(ip_addr); + Serial.println(mac_addr); + Serial.println(t1s_plca_settings); + Serial.println(t1s_default_mac_settings); + + if (!_server->begin(udp_port)) + { + return 0; + } + + float MODBUS_BIT_DURATION = 1.f / _baudrate; + float MODBUS_PRE_DELAY_BR = MODBUS_BIT_DURATION * 9.6f * 3.5f * 1e6; + float MODBUS_POST_DELAY_BR = MODBUS_BIT_DURATION * 9.6f * 3.5f * 1e6; + + serial485.setDelays(MODBUS_PRE_DELAY_BR, MODBUS_POST_DELAY_BR); + + if(!ModbusRTUClient.begin(serial485, (unsigned long) _baudrate, (uint16_t) SERIAL_8N1)) { + return 0; } ModbusRTUClient.setTimeout(2*1000UL); + return 1; } @@ -250,7 +313,6 @@ long ModbusT1SServerClass::inputRegisterRead(int address) if(_server == nullptr) { return res; } - int modbus_id = udp_rx_buf.at(2) << 8 | udp_rx_buf.at(3); int address_mod = udp_rx_buf.at(4) << 8 | udp_rx_buf.at(5); if(address != -1) { @@ -371,6 +433,67 @@ int ModbusT1SServerClass::parsePacket() { return res; } +void ModbusT1SServerClass::checkPLCAStatus() +{ + tc6_inst->service(); + + static unsigned long prev_beacon_check = 0; + static unsigned long prev_udp_packet_sent = 0; + + auto const now = millis(); + + if ((now - prev_beacon_check) > 1000) + { + prev_beacon_check = now; + if(callback == nullptr) + { + if (!tc6_inst->getPlcaStatus(OnPlcaStatus_server)) { + Serial.println("getPlcaStatus(...) failed"); + } + } else { + if (!tc6_inst->getPlcaStatus(callback)) { + Serial.println("getPlcaStatus(...) failed"); + } + } + } +} + +void ModbusT1SServerClass::update() { + /* Services the hardware and the protocol stack. + Must be called cyclic. The faster the better. + */ + checkPLCAStatus(); + switch (ModbusT1SServer.parsePacket()) + { + case UDP_READ_COIL_PORT: + ModbusT1SServer.coilRead(); + break; + + case UDP_WRITE_COIL_PORT: + ModbusT1SServer.coilWrite(); + break; + + case UDP_READ_DI_PORT: + discreteInputRead(); + break; + + case UDP_READ_IR_PORT: + inputRegisterRead(); + break; + + case UDP_READ_HR_PORT: + holdingRegisterRead(); + break; + + case UDP_WRITE_HR_PORT: + holdingRegisterWrite(); + break; + + default: + break; + } +} + /** * Sets the Arduino_10BASE_T1S_UDP server for communication. * @@ -382,5 +505,35 @@ void ModbusT1SServerClass::setT1SServer(Arduino_10BASE_T1S_UDP * server) { _server = server; } + +void ModbusT1SServerClass::setT1SPort(int port) { + udp_port = port; +} +void ModbusT1SServerClass::setBadrate(int baudrate) { + _baudrate = baudrate; +} + +void ModbusT1SServerClass::setCallback(callback_f cb) { + if(cb != nullptr) { + callback = cb; + } +} + +static void OnPlcaStatus_server(bool success, bool plcaStatus) +{ + if (!success) + { + Serial.println("PLCA status register read failed"); + return; + } + + if (plcaStatus) { + Serial.println("PLCA Mode active"); + } else { + Serial.println("CSMA/CD fallback"); + tc6_inst->enablePlca(); + } +} + ModbusT1SServerClass ModbusT1SServer; #endif diff --git a/src/ModbusT1SServer.h b/src/ModbusT1SServer.h index 67b2fd2..883703a 100644 --- a/src/ModbusT1SServer.h +++ b/src/ModbusT1SServer.h @@ -25,10 +25,21 @@ #include #include #include "ModbusRTUClient.h" +#include "ModbusT1SCommon.h" #include - - +// valutare meglio dove metterla +#define RS485_SERIAL Serial1 +#define RS485_TX_PIN 1 +#define RS485_RX_PIN 0 +#define RS485_DE_PIN 8 +#define RS485_RE_PIN 7 +#define PRE_DELAY 100 +#define POST_DELAY 100 +#define PWR_CARRIER_RATIO 0.18f + +static void OnPlcaStatus_server(bool success, bool plcaStatus); +using callback_f = void (*)(bool, bool); class ModbusT1SServerClass : public ModbusServer { public: /** @@ -56,7 +67,7 @@ class ModbusT1SServerClass : public ModbusServer { int begin(int id, unsigned long baudrate, uint16_t config = SERIAL_8N1); int begin(RS485Class& rs485, int id, unsigned long baudrate, uint16_t config = SERIAL_8N1); int begin(RS485Class& rs485, unsigned long baudrate, uint16_t config = SERIAL_8N1); - + int begin(int node_id); /** * Reads a coil from the Modbus server. * @@ -142,17 +153,31 @@ class ModbusT1SServerClass : public ModbusServer { */ void setT1SServer(Arduino_10BASE_T1S_UDP * server); + void setT1SPort(int port = 8889); + + void update(); + + void setBadrate(int baudrate); + + /** * Poll interface for requests */ virtual int poll(); + void checkPLCAStatus(); + void setCallback(callback_f cb = nullptr); + private: + callback_f callback = nullptr; RS485Class* _rs485 = &RS485; std::vector udp_rx_buf; Arduino_10BASE_T1S_UDP * _server = nullptr; IPAddress _last_ip; uint16_t _last_port; + int _baudrate = 9600; + int _node_id = 1; + int udp_port = 0; }; //Arduino_10BASE_T1S_UDP udp_server; extern ModbusT1SServerClass ModbusT1SServer; From 0b698cedc42b677cb13f020fa88cd5261c5b957b Mon Sep 17 00:00:00 2001 From: Rocketct Date: Thu, 31 Oct 2024 10:42:30 +0100 Subject: [PATCH 09/17] fixed examples in accord with the new library structures --- .../T1S/ModbusT1SClient/ModbusT1SClient.ino | 124 ++----------- .../T1S/ModbusT1SClient/arduino_secrets.h | 9 - ...dbusT1SClientTemperatureHumiditySensor.ino | 121 ++---------- .../arduino_secrets.h | 8 - .../T1S/ModbusT1SServer/ModbusT1SServer.ino | 170 ++--------------- .../T1S/ModbusT1SServer/arduino_secrets.h | 7 - ...dbusT1SServerTemperatureHumiditySensor.ino | 173 ++---------------- .../arduino_secrets.h | 7 - 8 files changed, 58 insertions(+), 561 deletions(-) delete mode 100644 examples/T1S/ModbusT1SClient/arduino_secrets.h delete mode 100644 examples/T1S/ModbusT1SClientTemperatureHumiditySensor/arduino_secrets.h delete mode 100644 examples/T1S/ModbusT1SServer/arduino_secrets.h delete mode 100644 examples/T1S/ModbusT1SServerTemperatureHumiditySensor/arduino_secrets.h diff --git a/examples/T1S/ModbusT1SClient/ModbusT1SClient.ino b/examples/T1S/ModbusT1SClient/ModbusT1SClient.ino index 94693e5..7fe6c3f 100644 --- a/examples/T1S/ModbusT1SClient/ModbusT1SClient.ino +++ b/examples/T1S/ModbusT1SClient/ModbusT1SClient.ino @@ -1,5 +1,5 @@ /* - Modbus T1S Client Toggle + Modbus T1S Client This sketch demonstrates how to send commands to a Modbus T1S server connected via T1S Single Pair Ethernet. @@ -9,121 +9,34 @@ - Uno WiFi R4 */ -#include // ArduinoModbus depends on the ArduinoRS485 library +#include #include -#include "arduino_secrets.h" -/************************************************************************************** - CONSTANTS - **************************************************************************************/ -static uint8_t const T1S_PLCA_NODE_ID = 2; - -static IPAddress const ip_addr { - 192, 168, 42, 100 + T1S_PLCA_NODE_ID -}; -static IPAddress const network_mask { - 255, 255, 255, 0 -}; -static IPAddress const gateway { - 192, 168, 42, 100 -}; - -static T1SPlcaSettings const t1s_plca_settings { - T1S_PLCA_NODE_ID -}; -static T1SMacSettings const t1s_default_mac_settings; - -static IPAddress const UDP_SERVER_IP_ADDR = {192, 168, 42, 100 + 0}; -/************************************************************************************** - GLOBAL VARIABLES - **************************************************************************************/ -auto const tc6_io = new TC6::TC6_Io -( SPI - , CS_PIN - , RESET_PIN - , IRQ_PIN); -auto const tc6_inst = new TC6::TC6_Arduino_10BASE_T1S(tc6_io); Arduino_10BASE_T1S_UDP udp_client; - +static uint8_t const T1S_PLCA_NODE_ID = 2; +static uint16_t const UDP_SERVER_PORT = 8889; +static uint16_t const UDP_CLIENT_PORT = 8888; +#define MODBUS_ID 42 void setup() { - Serial.begin(115200); - while (!Serial); - - Serial.println("Modbus T1S Client Toggle"); + Serial.begin(115200); - /* Initialize digital IO interface for interfacing - with the LAN8651. - */ - pinMode(IRQ_PIN, INPUT_PULLUP); - attachInterrupt(digitalPinToInterrupt(IRQ_PIN), - []() { - tc6_io->onInterrupt(); - }, - FALLING); - - /* Initialize IO module. */ - if (!tc6_io->begin()) - { - Serial.println("'tc6_io::begin(...)' failed."); - for (;;) { } - } - - MacAddress const mac_addr = MacAddress::create_from_uid(); - - if (!tc6_inst->begin(ip_addr - , network_mask - , gateway - , mac_addr - , t1s_plca_settings - , t1s_default_mac_settings)) - { - Serial.println("'TC6::begin(...)' failed."); - for (;;) { } - } - - Serial.print("IP\t"); - Serial.println(ip_addr); - Serial.println(mac_addr); - Serial.println(t1s_plca_settings); - Serial.println(t1s_default_mac_settings); - - if (!udp_client.begin(UDP_CLIENT_PORT)) - { - Serial.println("begin(...) failed for UDP client"); - for (;;) { } - } - - /* A0 -> LOCAL_ENABLE -> DO NOT feed power from board to network. */ - tc6_inst->digitalWrite(TC6::DIO::A0, false); - /* A1 -> T1S_DISABLE -> Open the switch connecting network to board by pulling EN LOW. */ - tc6_inst->digitalWrite(TC6::DIO::A1, false); - - ModbusT1SClient.setServerIp(UDP_SERVER_IP_ADDR); + ModbusT1SClient.setT1SClient(&udp_client); + ModbusT1SClient.setT1SPort(UDP_CLIENT_PORT); ModbusT1SClient.setServerPort(UDP_SERVER_PORT); ModbusT1SClient.setModbusId(MODBUS_ID); + ModbusT1SClient.setCallback(OnPlcaStatus); - Serial.println("UDP_Client"); + if (!ModbusT1SClient.begin(T1S_PLCA_NODE_ID)) { + Serial.println("Failed to start Modbus T1S Client!"); + while (1); + } } void loop() { - tc6_inst->service(); - - static unsigned long prev_beacon_check = 0; - static unsigned long prev_udp_packet_sent = 0; - - auto const now = millis(); - - if ((now - prev_beacon_check) > 1000) - { - prev_beacon_check = now; - if (!tc6_inst->getPlcaStatus(OnPlcaStatus)) { - Serial.println("getPlcaStatus(...) failed"); - } - } - // for (slave) id 1: write the value of 0x01, to the coil at address 0x00 - int res = ModbusT1SClient.coilRead(0x00, &udp_client, UDP_READ_COIL_PORT); + ModbusT1SClient.checkPLCAStatus(); + int res = ModbusT1SClient.coilRead(0x00); if (res == -1) { Serial.println("Failed to read coil! "); } else { @@ -131,21 +44,20 @@ void loop() { Serial.println(res); } - res = ModbusT1SClient.coilWrite(0x00, 1, &udp_client, UDP_WRITE_COIL_PORT); + res = ModbusT1SClient.coilWrite(0, 0x01); if (res == -1) { Serial.println("Failed to write coil! "); } else { Serial.println("write done"); } - res = ModbusT1SClient.inputRegisterRead(0x00, &udp_client, UDP_READ_IR_PORT); + res = ModbusT1SClient.inputRegisterRead(0x00); if (res == -1) { Serial.println("Failed to read Input Register! "); } else { Serial.print("Input Register value: "); Serial.println(res); } - } static void OnPlcaStatus(bool success, bool plcaStatus) diff --git a/examples/T1S/ModbusT1SClient/arduino_secrets.h b/examples/T1S/ModbusT1SClient/arduino_secrets.h deleted file mode 100644 index c134b4e..0000000 --- a/examples/T1S/ModbusT1SClient/arduino_secrets.h +++ /dev/null @@ -1,9 +0,0 @@ -static uint16_t const UDP_CLIENT_PORT = 8888; -static uint16_t const UDP_SERVER_PORT = 8889; -#define UDP_READ_COIL_PORT 1 -#define UDP_WRITE_COIL_PORT 2 -#define UDP_READ_DI_PORT 3 -#define UDP_READ_IR_PORT 4 -#define UDP_READ_HR_PORT 5 -#define UDP_WRITE_HR_PORT 6 -#define MODBUS_ID 42 \ No newline at end of file diff --git a/examples/T1S/ModbusT1SClientTemperatureHumiditySensor/ModbusT1SClientTemperatureHumiditySensor.ino b/examples/T1S/ModbusT1SClientTemperatureHumiditySensor/ModbusT1SClientTemperatureHumiditySensor.ino index 793a319..8d14f3a 100644 --- a/examples/T1S/ModbusT1SClientTemperatureHumiditySensor/ModbusT1SClientTemperatureHumiditySensor.ino +++ b/examples/T1S/ModbusT1SClientTemperatureHumiditySensor/ModbusT1SClientTemperatureHumiditySensor.ino @@ -9,133 +9,46 @@ - all the terminations placed on the hardware */ -#include // ArduinoModbus depends on the ArduinoRS485 library +#include #include -#include "arduino_secrets.h" -/************************************************************************************** - CONSTANTS - **************************************************************************************/ -static uint8_t const T1S_PLCA_NODE_ID = 2; - -static IPAddress const ip_addr { - 192, 168, 42, 100 + T1S_PLCA_NODE_ID -}; -static IPAddress const network_mask { - 255, 255, 255, 0 -}; -static IPAddress const gateway { - 192, 168, 42, 100 -}; -static T1SPlcaSettings const t1s_plca_settings { - T1S_PLCA_NODE_ID -}; -static T1SMacSettings const t1s_default_mac_settings; - -static IPAddress const UDP_SERVER_IP_ADDR = {192, 168, 42, 100 + 0}; +static uint8_t const T1S_PLCA_NODE_ID = 2; +static uint16_t const UDP_SERVER_PORT = 8889; +static uint16_t const UDP_CLIENT_PORT = 8888; -/************************************************************************************** - GLOBAL VARIABLES - **************************************************************************************/ -auto const tc6_io = new TC6::TC6_Io -( SPI - , CS_PIN - , RESET_PIN - , IRQ_PIN); -auto const tc6_inst = new TC6::TC6_Arduino_10BASE_T1S(tc6_io); Arduino_10BASE_T1S_UDP udp_client; - void setup() { Serial.begin(115200); - while (!Serial); - - Serial.println("Modbus T1S Client Toggle"); - - /* Initialize digital IO interface for interfacing - with the LAN8651. - */ - pinMode(IRQ_PIN, INPUT_PULLUP); - attachInterrupt(digitalPinToInterrupt(IRQ_PIN), - []() { - tc6_io->onInterrupt(); - }, - FALLING); - - /* Initialize IO module. */ - if (!tc6_io->begin()) - { - Serial.println("'tc6_io::begin(...)' failed."); - for (;;) { } - } - - MacAddress const mac_addr = MacAddress::create_from_uid(); - - if (!tc6_inst->begin(ip_addr - , network_mask - , gateway - , mac_addr - , t1s_plca_settings - , t1s_default_mac_settings)) - { - Serial.println("'TC6::begin(...)' failed."); - for (;;) { } - } - - Serial.print("IP\t"); - Serial.println(ip_addr); - Serial.println(mac_addr); - Serial.println(t1s_plca_settings); - Serial.println(t1s_default_mac_settings); - if (!udp_client.begin(UDP_CLIENT_PORT)) - { - Serial.println("begin(...) failed for UDP client"); - for (;;) { } - } - - /* A0 -> LOCAL_ENABLE -> DO NOT feed power from board to network. */ - tc6_inst->digitalWrite(TC6::DIO::A0, false); - /* A1 -> T1S_DISABLE -> Open the switch connecting network to board by pulling EN LOW. */ - tc6_inst->digitalWrite(TC6::DIO::A1, true); - - ModbusT1SClient.setServerIp(UDP_SERVER_IP_ADDR); + ModbusT1SClient.setT1SClient(&udp_client); + ModbusT1SClient.setT1SPort(UDP_CLIENT_PORT); ModbusT1SClient.setServerPort(UDP_SERVER_PORT); + ModbusT1SClient.setCallback(OnPlcaStatus); - - Serial.println("UDP_Client"); + if (!ModbusT1SClient.begin(T1S_PLCA_NODE_ID)) { + Serial.println("Failed to start Modbus T1S Client!"); + while (1); + } } unsigned long start = 0; void loop() { - tc6_inst->service(); - - static unsigned long prev_beacon_check = 0; - static unsigned long prev_udp_packet_sent = 0; - - auto const now = millis(); - - if ((now - prev_beacon_check) > 1000) - { - prev_beacon_check = now; - if (!tc6_inst->getPlcaStatus(OnPlcaStatus)) { - Serial.println("getPlcaStatus(...) failed"); - } - } + ModbusT1SClient.checkPLCAStatus(); if ((millis() - start) > 1000) { - int res = ModbusT1SClient.inputRegisterRead(1, 0x01, &udp_client, UDP_READ_IR_PORT); + int res = ModbusT1SClient.inputRegisterRead(1, 0x01); if (res == -1) { Serial.println("Failed to read temperature! "); } else { - int16_t const temperature_raw =res; + int16_t const temperature_raw = res; float const temperature_deg = temperature_raw / 10.f; Serial.print("Temperature: "); Serial.println(temperature_deg); } - - res = ModbusT1SClient.inputRegisterRead(1, 0x02, &udp_client, UDP_READ_IR_PORT); + + res = ModbusT1SClient.inputRegisterRead(1, 0x02); if (res == -1) { Serial.println("Failed to read humidity! "); } else { @@ -162,4 +75,4 @@ static void OnPlcaStatus(bool success, bool plcaStatus) Serial.println("CSMA/CD fallback"); tc6_inst->enablePlca(); } -} \ No newline at end of file +} diff --git a/examples/T1S/ModbusT1SClientTemperatureHumiditySensor/arduino_secrets.h b/examples/T1S/ModbusT1SClientTemperatureHumiditySensor/arduino_secrets.h deleted file mode 100644 index b6af131..0000000 --- a/examples/T1S/ModbusT1SClientTemperatureHumiditySensor/arduino_secrets.h +++ /dev/null @@ -1,8 +0,0 @@ -static uint16_t const UDP_CLIENT_PORT = 8888; -static uint16_t const UDP_SERVER_PORT = 8889; -#define UDP_READ_COIL_PORT 1 -#define UDP_WRITE_COIL_PORT 2 -#define UDP_READ_DI_PORT 3 -#define UDP_READ_IR_PORT 4 -#define UDP_READ_HR_PORT 5 -#define UDP_WRITE_HR_PORT 6 \ No newline at end of file diff --git a/examples/T1S/ModbusT1SServer/ModbusT1SServer.ino b/examples/T1S/ModbusT1SServer/ModbusT1SServer.ino index 3c001b6..1d156fd 100644 --- a/examples/T1S/ModbusT1SServer/ModbusT1SServer.ino +++ b/examples/T1S/ModbusT1SServer/ModbusT1SServer.ino @@ -1,5 +1,5 @@ /* - Modbus T1S Server LED + Modbus T1S Server This sketch demonstrates how to receive commands from a Modbus T1S Client connected @@ -10,176 +10,30 @@ - Uno WiFi R4 */ -#include // ArduinoModbus depends on the ArduinoRS485 library +#include #include -#include "arduino_secrets.h" -/************************************************************************************** - CONSTANTS - **************************************************************************************/ -#define RS485_SERIAL Serial1 -#define RS485_TX_PIN 1 -#define RS485_RX_PIN 0 -#define RS485_DE_PIN 8 -#define RS485_RE_PIN 7 -#define PRE_DELAY 100 -#define POST_DELAY 100 -#define PWR_CARRIER_RATIO 0.18f -RS485Class serial485(RS485_SERIAL, RS485_TX_PIN, RS485_DE_PIN, RS485_RE_PIN); +static uint8_t const T1S_PLCA_NODE_ID = 0; +static uint16_t const UDP_SERVER_PORT = 8889; -static unsigned int const MODBUS_BAUDRATE = 9600; -static float const MODBUS_BIT_DURATION = 1.f / MODBUS_BAUDRATE; -static float const MODBUS_PRE_DELAY_BR = MODBUS_BIT_DURATION * 9.6f * 3.5f * 1e6; -static float const MODBUS_POST_DELAY_BR = MODBUS_BIT_DURATION * 9.6f * 3.5f * 1e6; - -static int const MODBUS_DEVICE_ID = 1; -static int const MODBUS_DEVICE_TEMPERATURE_REGISTER = 0x0001; -static int const MODBUS_DEVICE_HUMIDITY_REGISTER = 0x0002; - -static uint8_t const T1S_PLCA_NODE_ID = 0; /* The UDP server doubles as PLCA coordinator. */ - -static IPAddress const ip_addr { - 192, 168, 42, 100 + T1S_PLCA_NODE_ID -}; -static IPAddress const network_mask { - 255, 255, 255, 0 -}; -static IPAddress const gateway { - 192, 168, 42, 100 -}; - -static T1SPlcaSettings const t1s_plca_settings { - T1S_PLCA_NODE_ID -}; -static T1SMacSettings const t1s_default_mac_settings; - -/************************************************************************************** - GLOBAL VARIABLES - **************************************************************************************/ -auto const tc6_io = new TC6::TC6_Io -( SPI - , CS_PIN - , RESET_PIN - , IRQ_PIN); -auto const tc6_inst = new TC6::TC6_Arduino_10BASE_T1S(tc6_io); Arduino_10BASE_T1S_UDP udp_server; -/************************************************************************************** - SETUP/LOOP - **************************************************************************************/ void setup() { Serial.begin(115200); - Serial.println("Modbus RTU Server LED"); - - /* Initialize digital IO interface for interfacing - with the LAN8651. - */ - pinMode(IRQ_PIN, INPUT_PULLUP); - attachInterrupt(digitalPinToInterrupt(IRQ_PIN), - []() { - tc6_io->onInterrupt(); - }, - FALLING); - - /* Initialize IO module. */ - if (!tc6_io->begin()) - { - Serial.println("'TC6_Io::begin(...)' failed."); - for (;;) { } - } - - MacAddress const mac_addr = MacAddress::create_from_uid(); - - if (!tc6_inst->begin(ip_addr - , network_mask - , gateway - , mac_addr - , t1s_plca_settings - , t1s_default_mac_settings)) - { - Serial.println("'TC6::begin(...)' failed."); - for (;;) { } - } - - Serial.print("IP\t"); - Serial.println(ip_addr); - Serial.println(mac_addr); - Serial.println(t1s_plca_settings); - Serial.println(t1s_default_mac_settings); - - if (!udp_server.begin(UDP_SERVER_PORT)) - { - Serial.println("begin(...) failed for UDP coil read server "); - for (;;) { } - } - - tc6_inst->digitalWrite(TC6::DIO::A0, true); - /* A1 -> T1S_DISABLE -> close the switch connecting network to board. */ - tc6_inst->digitalWrite(TC6::DIO::A1, true); + ModbusT1SServer.setT1SServer(&udp_server); + ModbusT1SServer.setT1SPort(UDP_SERVER_PORT); + ModbusT1SServer.setBadrate(9600); + ModbusT1SServer.setCallback(OnPlcaStatus); - serial485.setDelays(MODBUS_PRE_DELAY_BR, MODBUS_POST_DELAY_BR); - if (!ModbusT1SServer.begin(serial485, MODBUS_BAUDRATE, SERIAL_8N1)) { - Serial.println("Failed to start Modbus RTU Client!"); + if (!ModbusT1SServer.begin(T1S_PLCA_NODE_ID)) { + Serial.println("Failed to start Modbus T1S Server!"); while (1); } - - ModbusT1SServer.setT1SServer(&udp_server); - Serial.println("UDP_Server"); } void loop() { - /* Services the hardware and the protocol stack. - Must be called cyclic. The faster the better. - */ - tc6_inst->service(); - - static unsigned long prev_beacon_check = 0; - - auto const now = millis(); - if ((now - prev_beacon_check) > 1000) - { - prev_beacon_check = now; - if (!tc6_inst->getPlcaStatus(OnPlcaStatus)) { - Serial.println("getPlcaStatus(...) failed"); - } - } - - switch (ModbusT1SServer.parsePacket()) - { - case UDP_READ_COIL_PORT: - Serial.println("Read coil"); - ModbusT1SServer.coilRead(); - break; - - case UDP_WRITE_COIL_PORT: - Serial.println("Write coil"); - ModbusT1SServer.coilWrite(); - break; - - case UDP_READ_DI_PORT: - Serial.println("Read discrete input"); - ModbusT1SServer.discreteInputRead(); - break; - - case UDP_READ_IR_PORT: - Serial.println("Read input register"); - ModbusT1SServer.inputRegisterRead(); - break; - - case UDP_READ_HR_PORT: - Serial.println("Read holding register"); - ModbusT1SServer.holdingRegisterRead(); - break; - - case UDP_WRITE_HR_PORT: - Serial.println("Write holding register"); - ModbusT1SServer.holdingRegisterWrite(); - break; - - default: - break; - } + ModbusT1SServer.update(); } static void OnPlcaStatus(bool success, bool plcaStatus) @@ -196,4 +50,4 @@ static void OnPlcaStatus(bool success, bool plcaStatus) Serial.println("CSMA/CD fallback"); tc6_inst->enablePlca(); } -} \ No newline at end of file +} diff --git a/examples/T1S/ModbusT1SServer/arduino_secrets.h b/examples/T1S/ModbusT1SServer/arduino_secrets.h deleted file mode 100644 index 20e6fec..0000000 --- a/examples/T1S/ModbusT1SServer/arduino_secrets.h +++ /dev/null @@ -1,7 +0,0 @@ -static uint16_t const UDP_SERVER_PORT = 8889; -#define UDP_READ_COIL_PORT 1 -#define UDP_WRITE_COIL_PORT 2 -#define UDP_READ_DI_PORT 3 -#define UDP_READ_IR_PORT 4 -#define UDP_READ_HR_PORT 5 -#define UDP_WRITE_HR_PORT 6 diff --git a/examples/T1S/ModbusT1SServerTemperatureHumiditySensor/ModbusT1SServerTemperatureHumiditySensor.ino b/examples/T1S/ModbusT1SServerTemperatureHumiditySensor/ModbusT1SServerTemperatureHumiditySensor.ino index eb4d711..226f59e 100644 --- a/examples/T1S/ModbusT1SServerTemperatureHumiditySensor/ModbusT1SServerTemperatureHumiditySensor.ino +++ b/examples/T1S/ModbusT1SServerTemperatureHumiditySensor/ModbusT1SServerTemperatureHumiditySensor.ino @@ -15,180 +15,29 @@ - all the terminations placed on the hardware */ -#include // ArduinoModbus depends on the ArduinoRS485 library +#include #include -#include "arduino_secrets.h" -/************************************************************************************** - CONSTANTS - **************************************************************************************/ -#define RS485_SERIAL Serial1 -#define RS485_TX_PIN 1 -#define RS485_RX_PIN 0 -#define RS485_DE_PIN 8 -#define RS485_RE_PIN 7 -#define PRE_DELAY 100 -#define POST_DELAY 100 -#define PWR_CARRIER_RATIO 0.18f -RS485Class serial485(RS485_SERIAL, RS485_TX_PIN, RS485_DE_PIN, RS485_RE_PIN); +static uint8_t const T1S_PLCA_NODE_ID = 0; +static uint16_t const UDP_SERVER_PORT = 8889; -static unsigned int const MODBUS_BAUDRATE = 9600; -static float const MODBUS_BIT_DURATION = 1.f / MODBUS_BAUDRATE; -static float const MODBUS_PRE_DELAY_BR = MODBUS_BIT_DURATION * 9.6f * 3.5f * 1e6; -static float const MODBUS_POST_DELAY_BR = MODBUS_BIT_DURATION * 9.6f * 3.5f * 1e6; - -static int const MODBUS_DEVICE_ID = 1; -static int const MODBUS_DEVICE_TEMPERATURE_REGISTER = 0x0001; -static int const MODBUS_DEVICE_HUMIDITY_REGISTER = 0x0002; - -static uint8_t const T1S_PLCA_NODE_ID = 0; /* The UDP server doubles as PLCA coordinator. */ - -static IPAddress const ip_addr { - 192, 168, 42, 100 + T1S_PLCA_NODE_ID -}; -static IPAddress const network_mask { - 255, 255, 255, 0 -}; -static IPAddress const gateway { - 192, 168, 42, 100 -}; - -static T1SPlcaSettings const t1s_plca_settings { - T1S_PLCA_NODE_ID -}; -static T1SMacSettings const t1s_default_mac_settings; - -/************************************************************************************** - GLOBAL VARIABLES - **************************************************************************************/ -auto const tc6_io = new TC6::TC6_Io -( SPI - , CS_PIN - , RESET_PIN - , IRQ_PIN); -auto const tc6_inst = new TC6::TC6_Arduino_10BASE_T1S(tc6_io); Arduino_10BASE_T1S_UDP udp_server; -/************************************************************************************** - SETUP/LOOP - **************************************************************************************/ void setup() { Serial.begin(115200); - Serial.println("Modbus RTU Server LED"); - - /* Initialize digital IO interface for interfacing - with the LAN8651. - */ - pinMode(IRQ_PIN, INPUT_PULLUP); - attachInterrupt(digitalPinToInterrupt(IRQ_PIN), - []() { - tc6_io->onInterrupt(); - }, - FALLING); - - /* Initialize IO module. */ - if (!tc6_io->begin()) - { - Serial.println("'TC6_Io::begin(...)' failed."); - for (;;) { } - } - - MacAddress const mac_addr = MacAddress::create_from_uid(); - - if (!tc6_inst->begin(ip_addr - , network_mask - , gateway - , mac_addr - , t1s_plca_settings - , t1s_default_mac_settings)) - { - Serial.println("'TC6::begin(...)' failed."); - for (;;) { } - } - - Serial.print("IP\t"); - Serial.println(ip_addr); - Serial.println(mac_addr); - Serial.println(t1s_plca_settings); - Serial.println(t1s_default_mac_settings); - - if (!udp_server.begin(UDP_SERVER_PORT)) - { - Serial.println("begin(...) failed for UDP coil read server "); - for (;;) { } - } - - tc6_inst->digitalWrite(TC6::DIO::A0, false); - /* A1 -> T1S_DISABLE -> close the switch connecting network to board. */ - tc6_inst->digitalWrite(TC6::DIO::A1, true); - - serial485.setDelays(MODBUS_PRE_DELAY_BR, MODBUS_POST_DELAY_BR); - unsigned long br = MODBUS_BAUDRATE; - uint16_t config = SERIAL_8N1; - if (!ModbusT1SServer.begin(serial485, br, config)) { - Serial.println("Failed to start Modbus RTU Client!"); + ModbusT1SServer.setT1SServer(&udp_server); + ModbusT1SServer.setT1SPort(UDP_SERVER_PORT); + ModbusT1SServer.setBadrate(9600); + ModbusT1SServer.setCallback(OnPlcaStatus); + if (!ModbusT1SServer.begin(T1S_PLCA_NODE_ID)) { + Serial.println("Failed to start Modbus T1S Server!"); while (1); } - - ModbusT1SServer.setT1SServer(&udp_server); - Serial.println("UDP_Server"); - Serial.println(MODBUS_PRE_DELAY_BR); - Serial.println(MODBUS_POST_DELAY_BR); } void loop() { - /* Services the hardware and the protocol stack. - Must be called cyclic. The faster the better. - */ - tc6_inst->service(); - - static unsigned long prev_beacon_check = 0; - - auto const now = millis(); - if ((now - prev_beacon_check) > 1000) - { - prev_beacon_check = now; - if (!tc6_inst->getPlcaStatus(OnPlcaStatus)) { - Serial.println("getPlcaStatus(...) failed"); - } - } - - switch (ModbusT1SServer.parsePacket()) - { - case UDP_READ_COIL_PORT: - Serial.println("Read coil"); - ModbusT1SServer.coilRead(); - break; - - case UDP_WRITE_COIL_PORT: - Serial.println("Write coil"); - ModbusT1SServer.coilWrite(); - break; - - case UDP_READ_DI_PORT: - Serial.println("Read discrete input"); - ModbusT1SServer.discreteInputRead(); - break; - - case UDP_READ_IR_PORT: - Serial.println("Read input register"); - ModbusT1SServer.inputRegisterRead(); - break; - - case UDP_READ_HR_PORT: - Serial.println("Read holding register"); - ModbusT1SServer.holdingRegisterRead(); - break; - - case UDP_WRITE_HR_PORT: - Serial.println("Write holding register"); - ModbusT1SServer.holdingRegisterWrite(); - break; - - default: - break; - } + ModbusT1SServer.update(); } static void OnPlcaStatus(bool success, bool plcaStatus) @@ -205,4 +54,4 @@ static void OnPlcaStatus(bool success, bool plcaStatus) Serial.println("CSMA/CD fallback"); tc6_inst->enablePlca(); } -} \ No newline at end of file +} diff --git a/examples/T1S/ModbusT1SServerTemperatureHumiditySensor/arduino_secrets.h b/examples/T1S/ModbusT1SServerTemperatureHumiditySensor/arduino_secrets.h deleted file mode 100644 index 47405ff..0000000 --- a/examples/T1S/ModbusT1SServerTemperatureHumiditySensor/arduino_secrets.h +++ /dev/null @@ -1,7 +0,0 @@ -static uint16_t const UDP_SERVER_PORT = 8889; -#define UDP_READ_COIL_PORT 1 -#define UDP_WRITE_COIL_PORT 2 -#define UDP_READ_DI_PORT 3 -#define UDP_READ_IR_PORT 4 -#define UDP_READ_HR_PORT 5 -#define UDP_WRITE_HR_PORT 6 \ No newline at end of file From 9dcb66c67e1fca298b78424f45b4c8147e318db0 Mon Sep 17 00:00:00 2001 From: Rocketct Date: Thu, 14 Nov 2024 09:45:35 +0100 Subject: [PATCH 10/17] Modified the examples to show the new API --- ...RTUClientMD02TemperatureHumiditySensor.ino | 4 +- .../T1S/ModbusT1SClient/ModbusT1SClient.ino | 4 +- ...dbusT1SClientTemperatureHumiditySensor.ino | 5 +- .../T1S/ModbusT1SServer/ModbusT1SServer.ino | 8 +- ...dbusT1SServerTemperatureHumiditySensor.ino | 57 --- src/ModbusT1SClient.cpp | 462 +++++------------- src/ModbusT1SClient.h | 47 +- 7 files changed, 146 insertions(+), 441 deletions(-) delete mode 100644 examples/T1S/ModbusT1SServerTemperatureHumiditySensor/ModbusT1SServerTemperatureHumiditySensor.ino diff --git a/examples/RTU/ModbusRTUClientMD02TemperatureHumiditySensor/ModbusRTUClientMD02TemperatureHumiditySensor.ino b/examples/RTU/ModbusRTUClientMD02TemperatureHumiditySensor/ModbusRTUClientMD02TemperatureHumiditySensor.ino index 2f113be..8a66eb3 100644 --- a/examples/RTU/ModbusRTUClientMD02TemperatureHumiditySensor/ModbusRTUClientMD02TemperatureHumiditySensor.ino +++ b/examples/RTU/ModbusRTUClientMD02TemperatureHumiditySensor/ModbusRTUClientMD02TemperatureHumiditySensor.ino @@ -56,7 +56,7 @@ void loop() if (ModbusRTUClient.available()) { int16_t const temperature_raw = ModbusRTUClient.read(); - float const temperature_deg = temperature_raw / 100.f; + float const temperature_deg = temperature_raw / 10.f; Serial.println(temperature_deg); } @@ -68,7 +68,7 @@ void loop() if (ModbusRTUClient.available()) { int16_t const humidity_raw = ModbusRTUClient.read(); - float const humidity_per_cent = humidity_raw / 100.f; + float const humidity_per_cent = humidity_raw / 10.f; Serial.println(humidity_per_cent); } diff --git a/examples/T1S/ModbusT1SClient/ModbusT1SClient.ino b/examples/T1S/ModbusT1SClient/ModbusT1SClient.ino index 7fe6c3f..8f91f0a 100644 --- a/examples/T1S/ModbusT1SClient/ModbusT1SClient.ino +++ b/examples/T1S/ModbusT1SClient/ModbusT1SClient.ino @@ -21,7 +21,7 @@ static uint16_t const UDP_CLIENT_PORT = 8888; void setup() { Serial.begin(115200); - ModbusT1SClient.setT1SClient(&udp_client); + ModbusT1SClient.setT1SClient(udp_client); ModbusT1SClient.setT1SPort(UDP_CLIENT_PORT); ModbusT1SClient.setServerPort(UDP_SERVER_PORT); ModbusT1SClient.setModbusId(MODBUS_ID); @@ -34,7 +34,7 @@ void setup() { } void loop() { - ModbusT1SClient.checkPLCAStatus(); + ModbusT1SClient.update(); int res = ModbusT1SClient.coilRead(0x00); if (res == -1) { diff --git a/examples/T1S/ModbusT1SClientTemperatureHumiditySensor/ModbusT1SClientTemperatureHumiditySensor.ino b/examples/T1S/ModbusT1SClientTemperatureHumiditySensor/ModbusT1SClientTemperatureHumiditySensor.ino index 8d14f3a..bfafef8 100644 --- a/examples/T1S/ModbusT1SClientTemperatureHumiditySensor/ModbusT1SClientTemperatureHumiditySensor.ino +++ b/examples/T1S/ModbusT1SClientTemperatureHumiditySensor/ModbusT1SClientTemperatureHumiditySensor.ino @@ -21,7 +21,7 @@ Arduino_10BASE_T1S_UDP udp_client; void setup() { Serial.begin(115200); - ModbusT1SClient.setT1SClient(&udp_client); + ModbusT1SClient.setT1SClient(udp_client); ModbusT1SClient.setT1SPort(UDP_CLIENT_PORT); ModbusT1SClient.setServerPort(UDP_SERVER_PORT); ModbusT1SClient.setCallback(OnPlcaStatus); @@ -30,11 +30,12 @@ void setup() { Serial.println("Failed to start Modbus T1S Client!"); while (1); } + ModbusT1SClient.disablePOE(); } unsigned long start = 0; void loop() { - ModbusT1SClient.checkPLCAStatus(); + ModbusT1SClient.update(); if ((millis() - start) > 1000) { diff --git a/examples/T1S/ModbusT1SServer/ModbusT1SServer.ino b/examples/T1S/ModbusT1SServer/ModbusT1SServer.ino index 1d156fd..4bd5445 100644 --- a/examples/T1S/ModbusT1SServer/ModbusT1SServer.ino +++ b/examples/T1S/ModbusT1SServer/ModbusT1SServer.ino @@ -13,6 +13,8 @@ #include #include +RS485Class serial485(RS485_SERIAL, RS485_TX_PIN, RS485_DE_PIN, RS485_RE_PIN); + static uint8_t const T1S_PLCA_NODE_ID = 0; static uint16_t const UDP_SERVER_PORT = 8889; @@ -21,15 +23,15 @@ Arduino_10BASE_T1S_UDP udp_server; void setup() { Serial.begin(115200); - ModbusT1SServer.setT1SServer(&udp_server); + ModbusT1SServer.setT1SServer(udp_server); ModbusT1SServer.setT1SPort(UDP_SERVER_PORT); - ModbusT1SServer.setBadrate(9600); ModbusT1SServer.setCallback(OnPlcaStatus); - if (!ModbusT1SServer.begin(T1S_PLCA_NODE_ID)) { + if (!ModbusT1SServer.begin(T1S_PLCA_NODE_ID, 9600, SERIAL_8N1, serial485)) { Serial.println("Failed to start Modbus T1S Server!"); while (1); } + ModbusT1SServer.disablePOE(); } void loop() { diff --git a/examples/T1S/ModbusT1SServerTemperatureHumiditySensor/ModbusT1SServerTemperatureHumiditySensor.ino b/examples/T1S/ModbusT1SServerTemperatureHumiditySensor/ModbusT1SServerTemperatureHumiditySensor.ino deleted file mode 100644 index 226f59e..0000000 --- a/examples/T1S/ModbusT1SServerTemperatureHumiditySensor/ModbusT1SServerTemperatureHumiditySensor.ino +++ /dev/null @@ -1,57 +0,0 @@ -/* - Modbus T1S Server Temperature Humidity sensor - - This sketch creates a Modbus T1S Server and demonstrates - how to use read temperature and humidity values as sensed - by the RTU Modbus capable MD02 sensor. - - Circuit: - - Arduino Uno Wifi R4 - - T1S shield - - SPE ethernet connected to the client - - ISO GND connected to GND of the Modbus RTU server - - Y connected to A/Y of the Modbus RTU client - - Z connected to B/Z of the Modbus RTU client - - all the terminations placed on the hardware -*/ - -#include -#include - -static uint8_t const T1S_PLCA_NODE_ID = 0; -static uint16_t const UDP_SERVER_PORT = 8889; - -Arduino_10BASE_T1S_UDP udp_server; - -void setup() { - Serial.begin(115200); - - ModbusT1SServer.setT1SServer(&udp_server); - ModbusT1SServer.setT1SPort(UDP_SERVER_PORT); - ModbusT1SServer.setBadrate(9600); - ModbusT1SServer.setCallback(OnPlcaStatus); - if (!ModbusT1SServer.begin(T1S_PLCA_NODE_ID)) { - Serial.println("Failed to start Modbus T1S Server!"); - while (1); - } -} - -void loop() { - ModbusT1SServer.update(); -} - -static void OnPlcaStatus(bool success, bool plcaStatus) -{ - if (!success) - { - Serial.println("PLCA status register read failed"); - return; - } - - if (plcaStatus) { - Serial.println("PLCA Mode active"); - } else { - Serial.println("CSMA/CD fallback"); - tc6_inst->enablePlca(); - } -} diff --git a/src/ModbusT1SClient.cpp b/src/ModbusT1SClient.cpp index a8b2932..ae190a0 100644 --- a/src/ModbusT1SClient.cpp +++ b/src/ModbusT1SClient.cpp @@ -16,7 +16,7 @@ License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - + #if (defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_UNOR4_MINIMA)) #include @@ -27,8 +27,6 @@ extern "C" { #include "ModbusT1SClient.h" -size_t const UDP_RX_MSG_BUF_SIZE = 16 + 1; - /** * @class ModbusT1SClientClass * Class for Modbus T1S Client communication. @@ -42,7 +40,8 @@ size_t const UDP_RX_MSG_BUF_SIZE = 16 + 1; * Initializes the Modbus client with a default timeout of 1000 milliseconds. */ ModbusT1SClientClass::ModbusT1SClientClass() : - ModbusClient(1000) + ModbusClient(1000), + callback(default_OnPlcaStatus) { } @@ -68,22 +67,6 @@ ModbusT1SClientClass::~ModbusT1SClientClass() { } -/** - * Initializes the Modbus client with the specified RS485 instance, baud rate, and configuration. - * - * This function sets up the Modbus client for communication using the specified RS485 instance, baud rate, and configuration. - * - * @param rs485 Reference to an RS485Class object for RS485 communication. - * @param baudrate The baud rate for the Modbus communication. - * @param config The configuration for the Modbus communication (e.g., parity, stop bits). - * @return int Returns 1 if initialization is successful, 0 otherwise. - */ -int ModbusT1SClientClass::begin(RS485Class& rs485, unsigned long baudrate, uint16_t config) -{ - _rs485 = &rs485; - return begin(baudrate, config); -} - int ModbusT1SClientClass::begin(int node_id) { _node_id = node_id; @@ -100,16 +83,22 @@ int ModbusT1SClientClass::begin(int node_id) { return 0; } + //RRR ADD set IPs + if(_gateway == IPAddress(0, 0, 0, 0)) { + _gateway = IPAddress(192, 168, 42, 100); + } - IPAddress ip_addr = IPAddress(192, 168, 42, 100 + _node_id); + IPAddress ip_addr = IPAddress(_gateway[0], _gateway[1], _gateway[2], _gateway[3] + _node_id); IPAddress network_mask = IPAddress(255, 255, 255, 0); - IPAddress gateway = IPAddress(192, 168, 42, 100); - IPAddress server_addr = IPAddress(192, 168, 42, 100); - _server_ip = server_addr; + IPAddress gateway = IPAddress(_gateway[0], _gateway[1], _gateway[2], _gateway[3]); + IPAddress server_addr = IPAddress(_gateway[0], _gateway[1], _gateway[2], _gateway[3]); + + if(_server_ip == IPAddress(0, 0, 0, 0)) { + _server_ip = server_addr; + } - T1SPlcaSettings t1s_plca_settings { - _node_id - }; + + T1SPlcaSettings t1s_plca_settings(_node_id); T1SMacSettings const t1s_default_mac_settings; MacAddress const mac_addr = MacAddress::create_from_uid(); @@ -124,12 +113,6 @@ int ModbusT1SClientClass::begin(int node_id) return 0; } - Serial.print("IP\t"); - Serial.println(ip_addr); - Serial.println(mac_addr); - Serial.println(t1s_plca_settings); - Serial.println(t1s_default_mac_settings); - if (!_client->begin(udp_port)) { return 0; @@ -195,35 +178,9 @@ void ModbusT1SClientClass::setRxTimeout(unsigned long timeout) * @param port The port number to use for the communication. * @return int The status of the coil (1 for ON, 0 for OFF) or -1 if an error occurs. */ -int ModbusT1SClientClass::coilRead(int address, Arduino_10BASE_T1S_UDP * client, int port) +int ModbusT1SClientClass::coilRead(int address, Arduino_10BASE_T1S_UDP * client) { - int res = -1; - if(client != nullptr) { - _client = client; - } - - int _port = port; - if(port == -1) { - _port = UDP_READ_COIL_PORT; - } - - uint8_t tx_buf[6] = {(uint8_t)(_port & 0xFF00) >> 8, (uint8_t)(_port & 0x00FF), - (uint8_t)((_modbus_id & 0xFF00) >> 8), (uint8_t)_modbus_id, - (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; - int tx_packet_size = sizeof(tx_buf); - write(tx_buf, tx_packet_size, _client); - unsigned long start = millis(); - while(millis() - start < _rx_timeout) { - if(read(_client) > 0) { - if(checkPacket(_port, _modbus_id, address)) { - res = int(udp_rx_buf.at(6)); - udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); - break; - } - udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); - } - } - return res; + return coilRead(_modbus_id, address, client); } /** @@ -238,35 +195,9 @@ int ModbusT1SClientClass::coilRead(int address, Arduino_10BASE_T1S_UDP * client, * @param port The port number to use for the communication. * @return int The status of the coil (1 for ON, 0 for OFF) or -1 if an error occurs. */ -int ModbusT1SClientClass::coilRead(int id, int address, Arduino_10BASE_T1S_UDP * client, int port) +int ModbusT1SClientClass::coilRead(int id, int address, Arduino_10BASE_T1S_UDP * client) { - int res = -1; - if(client != nullptr) { - _client = client; - } - - int _port = port; - if(port == -1) { - _port = UDP_READ_COIL_PORT; - } - - uint8_t tx_buf[6] = {(uint8_t)(_port & 0xFF00) >> 8, (uint8_t)(_port & 0x00FF), - (uint8_t)((id & 0xFF00) >> 8), (uint8_t)id, - (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; - int tx_packet_size = sizeof(tx_buf); - write(tx_buf, tx_packet_size, _client); - unsigned long start = millis(); - while(millis() - start < _rx_timeout) { - if(read(_client)) { - if(checkPacket(_port, id, address)) { - res = int(udp_rx_buf.at(6)); - udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); - break; - } - udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); - } - } - return res; + return receive(id, address, client, UDP_READ_COIL_PORT); } /** @@ -281,23 +212,9 @@ int ModbusT1SClientClass::coilRead(int id, int address, Arduino_10BASE_T1S_UDP * * @param port The port number to use for the communication. * @return int 1 if the write operation is successful, -1 if an error occurs. */ -int ModbusT1SClientClass::coilWrite(int address, uint8_t value, Arduino_10BASE_T1S_UDP * client, int port) +int ModbusT1SClientClass::coilWrite(int address, uint16_t value, Arduino_10BASE_T1S_UDP * client) { - if(client != nullptr) { - _client = client; - } - - int _port = port; - if(port == -1) { - _port = UDP_WRITE_COIL_PORT; - } - - uint8_t tx_buf[7] = {(uint8_t)(_port & 0xFF00) >> 8, (uint8_t)(_port & 0x00FF), - (uint8_t)((_modbus_id & 0xFF00) >> 8), (uint8_t)_modbus_id, - (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address, value}; - int tx_packet_size = sizeof(tx_buf); - write(tx_buf, tx_packet_size, _client); - return 1; + return coilWrite(_modbus_id, address, value, client); } /** @@ -313,23 +230,9 @@ int ModbusT1SClientClass::coilWrite(int address, uint8_t value, Arduino_10BASE_T * @param port The port number to use for the communication. * @return int 1 if the write operation is successful, -1 if an error occurs. */ -int ModbusT1SClientClass::coilWrite(int id, int address, uint8_t value, Arduino_10BASE_T1S_UDP * client, int port) +int ModbusT1SClientClass::coilWrite(int id, int address, uint16_t value, Arduino_10BASE_T1S_UDP * client) { - if(client != nullptr) { - _client = client; - } - - - int _port = port; - if(port == -1) { - _port = UDP_WRITE_COIL_PORT; - } - - uint8_t tx_buf[7] = {(uint8_t)(_port & 0xFF00) >> 8, (uint8_t)(_port & 0x00FF), (uint8_t)((id & 0xFF00) >> 8), (uint8_t)id, - (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address, value}; - int tx_packet_size = sizeof(tx_buf); - write(tx_buf, tx_packet_size, _client); - return 1; + return send(id, address, value, client, UDP_WRITE_COIL_PORT); } /** @@ -343,35 +246,9 @@ int ModbusT1SClientClass::coilWrite(int id, int address, uint8_t value, Arduino_ * @param port The port number to use for the communication. * @return int The status of the discrete input (1 for ON, 0 for OFF) or -1 if an error occurs. */ -int ModbusT1SClientClass::discreteInputRead(int address, Arduino_10BASE_T1S_UDP * client, int port) +int ModbusT1SClientClass::discreteInputRead(int address, Arduino_10BASE_T1S_UDP * client) { - int res = -1; - if(client != nullptr) { - _client = client; - } - - int _port = port; - if(port == -1) { - _port = UDP_READ_DI_PORT; - } - - uint8_t tx_buf[6] = {(uint8_t)(_port & 0xFF00) >> 8, (uint8_t)(_port & 0x00FF), - (uint8_t)((_modbus_id & 0xFF00) >> 8), (uint8_t)_modbus_id, - (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; - int tx_packet_size = sizeof(tx_buf); - write(tx_buf, tx_packet_size, _client); - unsigned long start = millis(); - while(millis() - start < _rx_timeout) { - if(read(_client)) { - if(checkPacket(_port, _modbus_id, address)) { - res = int(udp_rx_buf.at(6)); - udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); - break; - } - udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); - } - } - return res; + return discreteInputRead(_modbus_id, address, client); } /** @@ -386,35 +263,9 @@ int ModbusT1SClientClass::discreteInputRead(int address, Arduino_10BASE_T1S_UDP * @param port The port number to use for the communication. * @return int The status of the discrete input (1 for ON, 0 for OFF) or -1 if an error occurs. */ -int ModbusT1SClientClass::discreteInputRead(int id, int address, Arduino_10BASE_T1S_UDP * client, int port) +int ModbusT1SClientClass::discreteInputRead(int id, int address, Arduino_10BASE_T1S_UDP * client) { - int res = -1; - if(client != nullptr) { - _client = client; - } - - int _port = port; - if(port == -1) { - _port = UDP_READ_DI_PORT; - } - - uint8_t tx_buf[6] = {(uint8_t)(_port & 0xFF00) >> 8, (uint8_t)(_port & 0x00FF), - (uint8_t)((id & 0xFF00) >> 8), (uint8_t)id, - (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; - int tx_packet_size = sizeof(tx_buf); - write(tx_buf, tx_packet_size, _client); - unsigned long start = millis(); - while(millis() - start < _rx_timeout) { - if(read(_client)) { - if(checkPacket(_port, id, address)) { - res = int(udp_rx_buf.at(6)); - udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); - break; - } - udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); - } - } - return res; + return receive(id, address, client, UDP_READ_DI_PORT); } /** @@ -428,35 +279,9 @@ int ModbusT1SClientClass::discreteInputRead(int id, int address, Arduino_10BASE_ * @param port The port number to use for the communication. * @return long The value of the input register or -1 if an error occurs. */ -long ModbusT1SClientClass::inputRegisterRead(int address, Arduino_10BASE_T1S_UDP * client, int port) +long ModbusT1SClientClass::inputRegisterRead(int address, Arduino_10BASE_T1S_UDP * client) { - long res = -1; - if(client != nullptr) { - _client = client; - } - - int _port = port; - if(port == -1) { - _port = UDP_READ_IR_PORT; - } - - uint8_t tx_buf[6] = {(uint8_t)(_port & 0xFF00) >> 8, (uint8_t)(_port & 0x00FF), - (uint8_t)((_modbus_id & 0xFF00) >> 8), (uint8_t)_modbus_id, - (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; - int tx_packet_size = sizeof(tx_buf); - write(tx_buf, tx_packet_size, _client); - unsigned long start = millis(); - while(millis() - start < _rx_timeout) { - if(read(_client)) { - if(checkPacket(_port, _modbus_id, address)) { - res = int(udp_rx_buf.at(6) << 8 | udp_rx_buf.at(7)); - udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); - break; - } - udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); - } - } - return res; + return inputRegisterRead(_modbus_id, address, client); } /** @@ -471,35 +296,9 @@ long ModbusT1SClientClass::inputRegisterRead(int address, Arduino_10BASE_T1S_UDP * @param port The port number to use for the communication. * @return long The value of the input register or -1 if an error occurs. */ -long ModbusT1SClientClass::inputRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client, int port) +long ModbusT1SClientClass::inputRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client) { - long res = -1; - if(client != nullptr) { - _client = client; - } - - int _port = port; - if(port == -1) { - _port = UDP_READ_IR_PORT; - } - - uint8_t tx_buf[6] = {(uint8_t)(_port & 0xFF00) >> 8, (uint8_t)(_port & 0x00FF), - (uint8_t)((id & 0xFF00) >> 8), (uint8_t)id, - (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; - int tx_packet_size = sizeof(tx_buf); - write(tx_buf, tx_packet_size, _client); - unsigned long start = millis(); - while(millis() - start < _rx_timeout) { - if(read(_client) > 0) { - if(checkPacket(_port, id, address)) { - res = int(udp_rx_buf.at(6) << 8 | udp_rx_buf.at(7)); - udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); - break; - } - udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); - } - } - return res; + return receive(id, address, client, UDP_READ_IR_PORT); } /** @@ -513,35 +312,9 @@ long ModbusT1SClientClass::inputRegisterRead(int id, int address, Arduino_10BASE * @param port The port number to use for the communication. * @return Returns the value of the holding register on success, or -1 if the client is null. */ -long ModbusT1SClientClass::holdingRegisterRead(int address, Arduino_10BASE_T1S_UDP * client, int port) +long ModbusT1SClientClass::holdingRegisterRead(int address, Arduino_10BASE_T1S_UDP * client) { - long res = -1; - if(client != nullptr) { - _client = client; - } - - int _port = port; - if(port == -1) { - _port = UDP_READ_HR_PORT; - } - uint8_t tx_buf[6] = {(uint8_t)(_port & 0xFF00) >> 8, (uint8_t)(_port & 0x00FF), - (uint8_t)((_modbus_id & 0xFF00) >> 8), (uint8_t)_modbus_id, - (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; - int tx_packet_size = sizeof(tx_buf); - write(tx_buf, tx_packet_size, client); - unsigned long start = millis(); - while(millis() - start < _rx_timeout) { - int const rx_packet_size = _client->parsePacket(); - if(read(_client)) { - if(checkPacket(_port, _modbus_id, address)) { - res = int(udp_rx_buf.at(6) << 8 | udp_rx_buf.at(7)); - udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); - break; - } - udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); - } - } - return res; + return holdingRegisterRead(_modbus_id, address, client); } /** @@ -557,38 +330,12 @@ long ModbusT1SClientClass::holdingRegisterRead(int address, Arduino_10BASE_T1S_U * @param port The port number to use for the communication. * @return Returns 1 on success, or -1 if the client is null. */ -long ModbusT1SClientClass::holdingRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client, int port) +long ModbusT1SClientClass::holdingRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client) { - long res = -1; - if(client != nullptr) { - _client = client; - } - - int _port = port; - if(port == -1) { - _port = UDP_READ_HR_PORT; - } - - uint8_t tx_buf[6] = {(uint8_t)(_port & 0xFF00) >> 8, (uint8_t)(_port & 0x00FF), - (uint8_t)((id & 0xFF00) >> 8), (uint8_t)(id & 0x00FF), - (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; - - int tx_packet_size = sizeof(tx_buf); - write(tx_buf, tx_packet_size, _client); - unsigned long start = millis(); - while(millis() - start < _rx_timeout) { - if(read(_client)) { - if(checkPacket(_port, id, address)) { - res = int(udp_rx_buf.at(6) << 8 | udp_rx_buf.at(7)); - udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); - break; - } - udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); - } - } - return res; + return receive(id, address, client, UDP_READ_HR_PORT); } + /** * Writes a value to a holding register on the Modbus server. * @@ -600,23 +347,9 @@ long ModbusT1SClientClass::holdingRegisterRead(int id, int address, Arduino_10BA * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. * @return int 1 if the write operation is successful, -1 if an error occurs. */ -int ModbusT1SClientClass::holdingRegisterWrite(int address, uint16_t value, Arduino_10BASE_T1S_UDP * client, int port) +int ModbusT1SClientClass::holdingRegisterWrite(int address, uint16_t value, Arduino_10BASE_T1S_UDP * client) { - if(client != nullptr) { - _client = client; - } - - int _port = port; - if(port == -1) { - _port = UDP_WRITE_HR_PORT; - } - - uint8_t tx_buf[8] = {(uint8_t)(_port & 0xFF00) >> 8, (uint8_t)(_port & 0x00FF), - (uint8_t)((_modbus_id & 0xFF00) >> 8), (uint8_t)_modbus_id, - (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address, (uint8_t)((value & 0xFF00) >> 8), (uint8_t)(value & 0x00FF)}; - int tx_packet_size = sizeof(tx_buf); - write(tx_buf, tx_packet_size, _client); - return 1; + return holdingRegisterWrite(_modbus_id, address, value, client); } /** @@ -632,64 +365,91 @@ int ModbusT1SClientClass::holdingRegisterWrite(int address, uint16_t value, Ardu * @param port The port number to use for the communication. * @return Returns 1 on success, or -1 if the client is null. */ -int ModbusT1SClientClass::holdingRegisterWrite(int id, int address, uint16_t value, Arduino_10BASE_T1S_UDP * client, int port) +int ModbusT1SClientClass::holdingRegisterWrite(int id, int address, uint16_t value, Arduino_10BASE_T1S_UDP * client) { - if(client != nullptr) { - _client = client; + return send(id, address, value, client, UDP_WRITE_HR_PORT); +} + +long ModbusT1SClientClass::receive(int id, int address, Arduino_10BASE_T1S_UDP * client, int functionCode) { + + long res = -1; + if(client == nullptr) { + client = _client; } - int _port = port; - if(port == -1) { - _port = UDP_WRITE_HR_PORT; + uint8_t tx_buf[8] = {(uint8_t)(functionCode & 0xFF00) >> 8, (uint8_t)(functionCode & 0x00FF), + (uint8_t)((id & 0xFF00) >> 8), (uint8_t)id, + (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address}; + int tx_packet_size = sizeof(tx_buf); + write(tx_buf, tx_packet_size, client); + unsigned long start = millis(); + while(millis() - start < _rx_timeout) { + if(read(client)) { + if(checkPacket(functionCode, id, address)) { + switch (functionCode) { + case UDP_READ_IR_PORT: + case UDP_READ_HR_PORT: + res = int(udp_rx_buf[6] << 8 | udp_rx_buf[7]); + return res; + case UDP_READ_COIL_PORT: + case UDP_READ_DI_PORT: + res = int(udp_rx_buf[7]); + return res; + default: + break; + } + } + } + } + return res; +} + +int ModbusT1SClientClass::send(int id, int address, uint16_t value, Arduino_10BASE_T1S_UDP * client, int functionCode) +{ + if(client == nullptr) { + client = _client; } - uint8_t tx_buf[8] = {(uint8_t)(_port & 0xFF00) >> 8, (uint8_t)(_port & 0x00FF), - (uint8_t)((id & 0xFF00) >> 8), (uint8_t)id, - (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address, - (uint8_t)((value & 0xFF00) >> 8), (uint8_t)(value & 0x00FF)}; + uint8_t tx_buf[8] = {(uint8_t)(functionCode & 0xFF00) >> 8, (uint8_t)(functionCode & 0x00FF), (uint8_t)((id & 0xFF00) >> 8), (uint8_t)id, + (uint8_t)((address & 0xFF00) >> 8), (uint8_t)address, (value & 0xFF00) >> 8, (uint8_t)(value & 0x00FF)}; int tx_packet_size = sizeof(tx_buf); - write(tx_buf, tx_packet_size, _client); + write(tx_buf, tx_packet_size, client); return 1; } void ModbusT1SClientClass::write(uint8_t * buf, int len, Arduino_10BASE_T1S_UDP * client) { - _client->beginPacket(_server_ip, _server_port); - _client->write((const uint8_t *)buf, len); - _client->endPacket(); + client->beginPacket(_server_ip, _server_port); + client->write((const uint8_t *)buf, len); + client->endPacket(); } int ModbusT1SClientClass::read(Arduino_10BASE_T1S_UDP * client) { - int const rx_packet_size = _client->parsePacket(); - if (rx_packet_size) + int const rx_packet_size = client->parsePacket(); + if (rx_packet_size == 8) { - uint8_t rx_msg_buf[rx_packet_size] = {0}; - int bytes_read = _client->read(rx_msg_buf, rx_packet_size - 1); - while (bytes_read != 0) { - std::copy(rx_msg_buf, rx_msg_buf + bytes_read, std::back_inserter(udp_rx_buf)); - bytes_read = _client->read(rx_msg_buf, UDP_RX_MSG_BUF_SIZE - 1); - } - _client->flush(); - return udp_rx_buf.size(); + int bytes_read = client->read(udp_rx_buf, 8); + return rx_packet_size == bytes_read; } return 0; } bool ModbusT1SClientClass::checkPacket(int port, uint16_t id, uint16_t address) { - int port_rec = udp_rx_buf.at(0) << 8 | udp_rx_buf.at(1); - uint16_t id_rcv = udp_rx_buf.at(2) << 8 | udp_rx_buf.at(3); - uint16_t add_rcv = udp_rx_buf.at(4) << 8 | udp_rx_buf.at(5); + int port_rec = udp_rx_buf[0] << 8 | udp_rx_buf[1]; + uint16_t id_rcv = udp_rx_buf[2] << 8 | udp_rx_buf[3]; + uint16_t add_rcv = udp_rx_buf[4] << 8 | udp_rx_buf[5]; if(port_rec == port && add_rcv == address && id_rcv == id) { return true; } return false; } -void ModbusT1SClientClass::setT1SClient(Arduino_10BASE_T1S_UDP * client) + +void ModbusT1SClientClass::setT1SClient(Arduino_10BASE_T1S_UDP & client) { - _client = client; + _client = &client; } void ModbusT1SClientClass::setT1SPort(int port) @@ -697,7 +457,7 @@ void ModbusT1SClientClass::setT1SPort(int port) udp_port = port; } -void ModbusT1SClientClass::checkPLCAStatus() +void ModbusT1SClientClass::update() { tc6_inst->service(); @@ -709,16 +469,7 @@ void ModbusT1SClientClass::checkPLCAStatus() if ((now - prev_beacon_check) > 1000) { prev_beacon_check = now; - if(callback == nullptr) - { - if (!tc6_inst->getPlcaStatus(OnPlcaStatus_client)) { - Serial.println("getPlcaStatus(...) failed"); - } - } else { - if (!tc6_inst->getPlcaStatus(callback)) { - Serial.println("getPlcaStatus(...) failed"); - } - } + //tc6_inst->getPlcaStatus(callback); } } @@ -728,20 +479,27 @@ void ModbusT1SClientClass::setCallback(callback_f cb) { } } -static void OnPlcaStatus_client(bool success, bool plcaStatus) +static void default_OnPlcaStatus(bool success, bool plcaStatus) { if (!success) { - Serial.println("PLCA status register read failed"); return; } - if (plcaStatus) { - Serial.println("PLCA Mode active"); - } else { - Serial.println("CSMA/CD fallback"); + if (!plcaStatus) { tc6_inst->enablePlca(); } } + +void ModbusT1SClientClass::enablePOE() { + tc6_inst->digitalWrite(TC6::DIO::A0, true); + tc6_inst->digitalWrite(TC6::DIO::A1, true); +} + +void ModbusT1SClientClass::disablePOE() { + tc6_inst->digitalWrite(TC6::DIO::A0, false); + tc6_inst->digitalWrite(TC6::DIO::A1, true); +} + ModbusT1SClientClass ModbusT1SClient; #endif diff --git a/src/ModbusT1SClient.h b/src/ModbusT1SClient.h index e6715e9..ecb7e46 100644 --- a/src/ModbusT1SClient.h +++ b/src/ModbusT1SClient.h @@ -1,6 +1,6 @@ /* This file is part of the ArduinoModbus library. - Copyright (c) 2018 Arduino SA. All rights reserved. + Copyright (c) 2024 Arduino SA. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -28,10 +28,8 @@ #include "ModbusT1SCommon.h" #include - - #define RX_TIMEOUT 1000 -static void OnPlcaStatus_client(bool success, bool plcaStatus); + using callback_f = void (*)(bool, bool); class ModbusT1SClientClass : public ModbusClient { public: @@ -47,8 +45,6 @@ class ModbusT1SClientClass : public ModbusClient { * * Return 1 on success, 0 on failure */ - int begin(unsigned long baudrate, uint16_t config = SERIAL_8N1); - int begin(RS485Class& rs485, unsigned long baudrate, uint16_t config = SERIAL_8N1); int begin(int node_id); /** * Sets the IP address of the Modbus server. @@ -88,7 +84,7 @@ void setModbusId(uint16_t id); * @param port The port number of the Modbus server. Default is 0. * @return int The status of the coil (1 for ON, 0 for OFF) or -1 if an error occurs. */ -int coilRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr, int port = -1); +int coilRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr); /** * Reads the status of a coil from the Modbus server with a specified ID. @@ -102,7 +98,7 @@ int coilRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr, int port = * @param port The port number of the Modbus server. Default is 0. * @return int The status of the coil (1 for ON, 0 for OFF) or -1 if an error occurs. */ -int coilRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr, int port = -1); +int coilRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr); /** * Writes a value to a coil on the Modbus server. @@ -116,7 +112,7 @@ int coilRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr, int * @param port The port number of the Modbus server. Default is 0. * @return int Returns 1 if the write operation is successful, 0 otherwise. */ -int coilWrite(int address, uint8_t value, Arduino_10BASE_T1S_UDP * client = nullptr, int port = -1); +int coilWrite(int address, uint16_t value, Arduino_10BASE_T1S_UDP * client = nullptr); /** * Writes a value to a coil on the Modbus server with a specified ID. @@ -131,7 +127,7 @@ int coilWrite(int address, uint8_t value, Arduino_10BASE_T1S_UDP * client = null * @param port The port number of the Modbus server. Default is 0. * @return int Returns 1 if the write operation is successful, 0 otherwise. */ -int coilWrite(int id, int address, uint8_t value, Arduino_10BASE_T1S_UDP * client = nullptr, int port = -1); +int coilWrite(int id, int address, uint16_t value, Arduino_10BASE_T1S_UDP * client = nullptr); /** * Reads the status of a discrete input from the Modbus server. @@ -144,7 +140,7 @@ int coilWrite(int id, int address, uint8_t value, Arduino_10BASE_T1S_UDP * clien * @param port The port number of the Modbus server. Default is 0. * @return int The status of the discrete input (1 for ON, 0 for OFF) or -1 if an error occurs. */ -int discreteInputRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr, int port = -1); +int discreteInputRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr); /** * Reads the status of a discrete input from the Modbus server with a specified ID. @@ -158,7 +154,7 @@ int discreteInputRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr, in * @param port The port number of the Modbus server. Default is 0. * @return int The status of the discrete input (1 for ON, 0 for OFF) or -1 if an error occurs. */ -int discreteInputRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr, int port = -1); +int discreteInputRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr); /** * Reads the value of an input register from the Modbus server. @@ -171,7 +167,7 @@ int discreteInputRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nul * @param port The port number of the Modbus server. Default is 0. * @return long The value of the input register or -1 if an error occurs. */ -long inputRegisterRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr, int port = -1); +long inputRegisterRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr); /** * Reads the value of an input register from the Modbus server with a specified ID. @@ -185,7 +181,7 @@ long inputRegisterRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr, i * @param port The port number of the Modbus server. Default is 0. * @return long The value of the input register or -1 if an error occurs. */ -long inputRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr, int port = -1); +long inputRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr); /** * Writes a value to a holding register on the Modbus server. @@ -199,7 +195,7 @@ long inputRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nu * @param port The port number of the Modbus server. Default is 0. * @return int Returns 1 if the write operation is successful, 0 otherwise. */ -int holdingRegisterWrite(int address, uint16_t value, Arduino_10BASE_T1S_UDP * client = nullptr, int port = -1); +int holdingRegisterWrite(int address, uint16_t value, Arduino_10BASE_T1S_UDP * client = nullptr); /** * Writes a value to a holding register on the Modbus server with a specified ID. @@ -214,7 +210,7 @@ int holdingRegisterWrite(int address, uint16_t value, Arduino_10BASE_T1S_UDP * c * @param port The port number of the Modbus server. Default is 0. * @return int Returns 1 if the write operation is successful, 0 otherwise. */ -int holdingRegisterWrite(int id, int address, uint16_t value, Arduino_10BASE_T1S_UDP * client = nullptr, int port = -1); +int holdingRegisterWrite(int id, int address, uint16_t value, Arduino_10BASE_T1S_UDP * client = nullptr); /** * Reads the value of a holding register from the Modbus server. @@ -227,7 +223,7 @@ int holdingRegisterWrite(int id, int address, uint16_t value, Arduino_10BASE_T1S * @param port The port number of the Modbus server. Default is 0. * @return long The value of the holding register or -1 if an error occurs. */ -long holdingRegisterRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr, int port = -1); +long holdingRegisterRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr); /** * Reads the value of a holding register from the Modbus server with a specified ID. @@ -241,27 +237,32 @@ long holdingRegisterRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr, * @param port The port number of the Modbus server. Default is 0. * @return long The value of the holding register or -1 if an error occurs. */ -long holdingRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr, int port = -1); -void setT1SClient(Arduino_10BASE_T1S_UDP * client = nullptr); +long holdingRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr); +void setT1SClient(Arduino_10BASE_T1S_UDP & client); void setRxTimeout(unsigned long timeout = RX_TIMEOUT); void setT1SPort(int port = 8889); -void checkPLCAStatus(); +void update(); void setCallback(callback_f cb = nullptr); +void enablePOE(); +void disablePOE(); private: - callback_f callback = nullptr; + long receive(int id, int address, Arduino_10BASE_T1S_UDP * client, int functionCode); + int send(int id, int address, uint16_t value, Arduino_10BASE_T1S_UDP * client, int functionCode); void write(uint8_t * buf, int len, Arduino_10BASE_T1S_UDP * client); int read(Arduino_10BASE_T1S_UDP * client); bool checkPacket(int port, uint16_t id, uint16_t address); private: - + IPAddress _gateway = IPAddress(0, 0, 0, 0); unsigned long _rx_timeout = RX_TIMEOUT; + callback_f callback = nullptr; IPAddress _server_ip = IPAddress(0, 0, 0, 0); - std::vector udp_rx_buf; uint16_t _server_port = 8889; uint16_t _modbus_id = 0; + uint8_t udp_rx_buf[8] = {0}; + RS485Class* _rs485 = &RS485; Arduino_10BASE_T1S_UDP * _client = nullptr; int _node_id = 1; From 5f7d62394abed498db57732f11ccb29f2083546e Mon Sep 17 00:00:00 2001 From: Rocketct Date: Thu, 14 Nov 2024 09:48:59 +0100 Subject: [PATCH 11/17] added sets APIs for server management and generalized read and write --- src/ModbusT1SServer.cpp | 373 ++++++++++++++-------------------------- src/ModbusT1SServer.h | 33 ++-- 2 files changed, 140 insertions(+), 266 deletions(-) diff --git a/src/ModbusT1SServer.cpp b/src/ModbusT1SServer.cpp index cad0359..456b6ae 100644 --- a/src/ModbusT1SServer.cpp +++ b/src/ModbusT1SServer.cpp @@ -26,17 +26,16 @@ extern "C" { #include "ModbusT1SServer.h" -size_t const UDP_RX_MSG_BUF_SIZE = 16 + 1; -RS485Class serial485(RS485_SERIAL, RS485_TX_PIN, RS485_DE_PIN, RS485_RE_PIN); - /** * @class ModbusT1SServerClass * Class for Modbus T1S Server communication. * * This class provides functionalities to communicate with a Modbus T1S server. */ -ModbusT1SServerClass::ModbusT1SServerClass() +ModbusT1SServerClass::ModbusT1SServerClass(): + callback(default_OnPlcaStatus) { + } /** @@ -58,66 +57,14 @@ ModbusT1SServerClass::~ModbusT1SServerClass() { } -/** - * Initializes the Modbus server with the specified RS485 instance, baud rate, and configuration. - * - * This function sets up the Modbus server for communication using the specified RS485 instance, baud rate, and configuration. - * - * @param id (slave) id of the server - * @param baudrate The baud rate for the Modbus communication. - * @param config The configuration for the Modbus communication (e.g., parity, stop bits). - * @return int Returns 1 if initialization is successful, 0 otherwise. - */ -int ModbusT1SServerClass::begin(int id, unsigned long baudrate, uint16_t config) -{ - if(!ModbusRTUClient.begin(*_rs485, baudrate, config)) { - return -1; - } - - ModbusRTUClient.setTimeout(2*1000UL); - return 1; -} - -/** - * Initializes the Modbus server with the specified RS485 instance, baud rate, and configuration. - * - * This function sets up the Modbus server for communication using the specified RS485 instance, baud rate, and configuration. - * - * @param rs485 Reference to an RS485Class object for RS485 communication. - * @param id (slave) id of the server - * @param baudrate The baud rate for the Modbus communication. - * @param config The configuration for the Modbus communication (e.g., parity, stop bits). - * @return int Returns 1 if initialization is successful, 0 otherwise. - */ -int ModbusT1SServerClass::begin(RS485Class& rs485, int id, unsigned long baudrate, uint16_t config) -{ - _rs485 = &rs485; - return begin(id, baudrate, config); -} - -/** - * Initializes the Modbus server with the specified RS485 instance, baud rate, and configuration. - * - * This function sets up the Modbus server for communication using the specified RS485 instance, baud rate, and configuration. - * - * @param rs485 Reference to an RS485Class object for RS485 communication. - * @param baudrate The baud rate for the Modbus communication. - * @param config The configuration for the Modbus communication (e.g., parity, stop bits). - * @return int Returns 1 if initialization is successful, 0 otherwise. - */ -int ModbusT1SServerClass::begin(RS485Class& rs485, unsigned long baudrate, uint16_t config) +int ModbusT1SServerClass::begin(int node_id, unsigned long baudrate, uint16_t config, RS485Class& rs485) { - _rs485 = &rs485; - if(!ModbusRTUClient.begin(rs485, baudrate, config)) { - return 0; + _node_id = node_id; + if (&rs485 != nullptr) + { + _rs485 = &rs485; } - return 1; -} - -int ModbusT1SServerClass::begin(int node_id) -{ - _node_id = node_id; pinMode(IRQ_PIN, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(IRQ_PIN), @@ -132,13 +79,15 @@ int ModbusT1SServerClass::begin(int node_id) return 0; } - IPAddress ip_addr = IPAddress(192, 168, 42, 100 + _node_id); + if(_gateway == IPAddress(0, 0, 0, 0)) { + _gateway = IPAddress(192, 168, 42, 100); + } + + IPAddress ip_addr = IPAddress(_gateway[0], _gateway[1], _gateway[2], _gateway[3] + _node_id); IPAddress network_mask = IPAddress(255, 255, 255, 0); - IPAddress gateway = IPAddress(192, 168, 42, 100); + IPAddress gateway = IPAddress(_gateway[0], _gateway[1], _gateway[2], _gateway[3]); - T1SPlcaSettings t1s_plca_settings { - _node_id - }; + T1SPlcaSettings t1s_plca_settings(_node_id); T1SMacSettings const t1s_default_mac_settings; MacAddress const mac_addr = MacAddress::create_from_uid(); @@ -151,14 +100,7 @@ int ModbusT1SServerClass::begin(int node_id) { return 0; } - - Serial.print("IP\t"); - Serial.println(ip_addr); - Serial.println(mac_addr); - Serial.println(t1s_plca_settings); - Serial.println(t1s_default_mac_settings); - - if (!_server->begin(udp_port)) + if (!_server->begin(udp_port)) { return 0; } @@ -167,17 +109,21 @@ int ModbusT1SServerClass::begin(int node_id) float MODBUS_PRE_DELAY_BR = MODBUS_BIT_DURATION * 9.6f * 3.5f * 1e6; float MODBUS_POST_DELAY_BR = MODBUS_BIT_DURATION * 9.6f * 3.5f * 1e6; - serial485.setDelays(MODBUS_PRE_DELAY_BR, MODBUS_POST_DELAY_BR); + _rs485->setDelays(MODBUS_PRE_DELAY_BR, MODBUS_POST_DELAY_BR); - if(!ModbusRTUClient.begin(serial485, (unsigned long) _baudrate, (uint16_t) SERIAL_8N1)) { + if(!ModbusRTUClient.begin(*_rs485, (unsigned long) _baudrate, (uint16_t) SERIAL_8N1)) { return 0; } - ModbusRTUClient.setTimeout(2*1000UL); + ModbusRTUClient.setTimeout(2*1000); //RRR set timeout return 1; } +void ModbusT1SServerClass::setTimeout(unsigned long timeout) { + ModbusRTUClient.setTimeout(timeout); +} + /** * Sets the Arduino_10BASE_T1S_UDP server for communication. * @@ -187,14 +133,6 @@ int ModbusT1SServerClass::begin(int node_id) */ int ModbusT1SServerClass::poll() { - uint8_t request[MODBUS_RTU_MAX_ADU_LENGTH]; - - int requestLength = modbus_receive(_mb, request); - - if (requestLength > 0) { - modbus_reply(_mb, request, requestLength, &_mbMapping); - return 1; - } return 0; } @@ -207,28 +145,7 @@ int ModbusT1SServerClass::poll() */ int ModbusT1SServerClass::coilRead(int address) { - int res = -1; - if(_server == nullptr) { - return res; - } - - int modbus_id = udp_rx_buf.at(2) << 8 | udp_rx_buf.at(3); - int address_mod = udp_rx_buf.at(4) << 8 | udp_rx_buf.at(5); - if(address != -1) { - address_mod = address; - } - - if (ModbusRTUClient.requestFrom(modbus_id, COILS, address_mod, 1)) { - if (ModbusRTUClient.available()) { - res = ModbusRTUClient.read(); - udp_rx_buf.push_back((uint8_t)res); - _server->beginPacket(_last_ip, _last_port); - _server->write((uint8_t *)udp_rx_buf.data(), udp_rx_buf.size()); - _server->endPacket(); - } - } - udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); - return res; + return read(address, COILS); } /** @@ -243,27 +160,8 @@ int ModbusT1SServerClass::coilRead(int address) */ int ModbusT1SServerClass::coilWrite(int address, uint8_t value) { - int res = -1; - if(_server == nullptr) { - return res; - } - - int modbus_id = udp_rx_buf.at(2) << 8 | udp_rx_buf.at(3); - int address_mod = udp_rx_buf.at(4) << 8 | udp_rx_buf.at(5); - if(address != -1) { - address_mod = address; - } - uint8_t coilValue = int( udp_rx_buf.at(6)); - - ModbusRTUClient.beginTransmission(modbus_id, COILS, address_mod, 1); - res = ModbusRTUClient.write(coilValue); - if (!ModbusRTUClient.endTransmission()) { - res = -1; - } - udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); - return res; + return write(address, COILS); } - /** * Reads the status of a discrete input from the Modbus server. * @@ -274,28 +172,7 @@ int ModbusT1SServerClass::coilWrite(int address, uint8_t value) * @return int The status of the discrete input (1 for ON, 0 for OFF) or -1 if an error occurs. */ int ModbusT1SServerClass::discreteInputRead(int address) { - int res = -1; - if(_server == nullptr) { - return res; - } - - int modbus_id = udp_rx_buf.at(2) << 8 | udp_rx_buf.at(3); - int address_mod = udp_rx_buf.at(4) << 8 | udp_rx_buf.at(5); - if(address != -1) { - address_mod = address; - } - - if (ModbusRTUClient.requestFrom(modbus_id, DISCRETE_INPUTS, address_mod, 1)) { - if (ModbusRTUClient.available()) { - res = ModbusRTUClient.read(); - udp_rx_buf.push_back((uint8_t)res); - _server->beginPacket(_last_ip, _last_port); - _server->write((uint8_t *)udp_rx_buf.data(), udp_rx_buf.size()); - _server->endPacket(); - } - } - udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); - return res; + return read(address, DISCRETE_INPUTS); } /** @@ -309,28 +186,7 @@ int ModbusT1SServerClass::discreteInputRead(int address) { */ long ModbusT1SServerClass::inputRegisterRead(int address) { - long res = -1; - if(_server == nullptr) { - return res; - } - int modbus_id = udp_rx_buf.at(2) << 8 | udp_rx_buf.at(3); - int address_mod = udp_rx_buf.at(4) << 8 | udp_rx_buf.at(5); - if(address != -1) { - address_mod = address; - } - - if (ModbusRTUClient.requestFrom(modbus_id, INPUT_REGISTERS, address_mod, 1)) { - if (ModbusRTUClient.available()) { - int16_t data = ModbusRTUClient.read(); - uint8_t tx_buf[2] = {(uint8_t)((data & 0xFF00) >> 8), (uint8_t)(data & 0x00FF)}; - std::copy(tx_buf, tx_buf + 2, std::back_inserter(udp_rx_buf)); - _server->beginPacket(_last_ip, _last_port); - _server->write((uint8_t *)udp_rx_buf.data(), udp_rx_buf.size()); - _server->endPacket(); - } - } - udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); - return res; + return read(address, INPUT_REGISTERS); } /** @@ -344,29 +200,7 @@ long ModbusT1SServerClass::inputRegisterRead(int address) * @return int 1 if the write operation is successful, -1 if an error occurs. */ long ModbusT1SServerClass::holdingRegisterRead(int address) { - long res = -1; - if(_server == nullptr) { - return res; - } - - int modbus_id = udp_rx_buf.at(2) << 8 | udp_rx_buf.at(3); - int address_mod = udp_rx_buf.at(4) << 8 | udp_rx_buf.at(5); - if(address != -1) { - address_mod = address; - } - - if (ModbusRTUClient.requestFrom(modbus_id, HOLDING_REGISTERS, address_mod, 1)) { - if (ModbusRTUClient.available()) { - int16_t data = ModbusRTUClient.read(); - uint8_t tx_buf[2] = {(uint8_t)((data & 0xFF00) >> 8), (uint8_t)(data & 0x00FF)}; - std::copy(tx_buf, tx_buf + 2, std::back_inserter(udp_rx_buf)); - _server->beginPacket(_last_ip, _last_port); - _server->write((uint8_t *)udp_rx_buf.data(), udp_rx_buf.size()); - _server->endPacket(); - } - } - udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); - return res; + return read(address, HOLDING_REGISTERS); } /** @@ -380,25 +214,7 @@ long ModbusT1SServerClass::holdingRegisterRead(int address) { * @return int 1 if the write operation is successful, -1 if an error occurs. */ int ModbusT1SServerClass::holdingRegisterWrite(int address) { - int res = -1; - if(_server == nullptr) { - return res; - } - - int modbus_id = udp_rx_buf.at(2) << 8 | udp_rx_buf.at(3); - int address_mod = udp_rx_buf.at(4) << 8 | udp_rx_buf.at(5); - if(address != -1) { - address_mod = address; - } - uint16_t value = udp_rx_buf.at(6) << 8 | udp_rx_buf.at(7); - - ModbusRTUClient.beginTransmission(modbus_id, HOLDING_REGISTERS, address_mod, 1); - res = ModbusRTUClient.write(value); - if (!ModbusRTUClient.endTransmission()) { - res = -1; - } - udp_rx_buf.erase(udp_rx_buf.begin(), udp_rx_buf.end()); - return res; + return write(address, HOLDING_REGISTERS); } /** @@ -414,26 +230,19 @@ int ModbusT1SServerClass::parsePacket() { return res; } - int const rx_packet_size = _server->parsePacket(); - if (rx_packet_size) + int rx_packet_size = _server->parsePacket(); + if (rx_packet_size == 8) { - uint8_t rx_msg_buf[UDP_RX_MSG_BUF_SIZE] = {0}; - int bytes_read = _server->read(rx_msg_buf, UDP_RX_MSG_BUF_SIZE - 1); - while (bytes_read != 0) { - std::copy(rx_msg_buf, rx_msg_buf + bytes_read, std::back_inserter(udp_rx_buf)); - bytes_read = _server->read(rx_msg_buf, UDP_RX_MSG_BUF_SIZE - 1); - } - res = udp_rx_buf.at(0) << 8 | udp_rx_buf.at(1); + int bytes_read = _server->read(udp_rx_buf, 8); + res = udp_rx_buf[0] << 8 | udp_rx_buf[1]; + _last_ip = _server->remoteIP(); + _last_port = _server->remotePort(); + return rx_packet_size == bytes_read ? res : -1; } - - _last_ip = _server->remoteIP(); - _last_port = _server->remotePort(); - _server->flush(); - return res; } -void ModbusT1SServerClass::checkPLCAStatus() +void ModbusT1SServerClass::checkStatus() { tc6_inst->service(); @@ -445,16 +254,7 @@ void ModbusT1SServerClass::checkPLCAStatus() if ((now - prev_beacon_check) > 1000) { prev_beacon_check = now; - if(callback == nullptr) - { - if (!tc6_inst->getPlcaStatus(OnPlcaStatus_server)) { - Serial.println("getPlcaStatus(...) failed"); - } - } else { - if (!tc6_inst->getPlcaStatus(callback)) { - Serial.println("getPlcaStatus(...) failed"); - } - } + tc6_inst->getPlcaStatus(callback); } } @@ -462,15 +262,15 @@ void ModbusT1SServerClass::update() { /* Services the hardware and the protocol stack. Must be called cyclic. The faster the better. */ - checkPLCAStatus(); - switch (ModbusT1SServer.parsePacket()) - { + checkStatus(); + switch (parsePacket()) + { case UDP_READ_COIL_PORT: - ModbusT1SServer.coilRead(); + coilRead(); break; case UDP_WRITE_COIL_PORT: - ModbusT1SServer.coilWrite(); + coilWrite(); break; case UDP_READ_DI_PORT: @@ -491,9 +291,74 @@ void ModbusT1SServerClass::update() { default: break; + } +} + +long ModbusT1SServerClass::read(int address, int functionCode) { + long res = -1; + if(_server == nullptr) { + return res; } + + int modbus_id = udp_rx_buf[2] << 8 | udp_rx_buf[3]; + int address_mod = udp_rx_buf[4] << 8 | udp_rx_buf[5]; + if (ModbusRTUClient.requestFrom(modbus_id, functionCode, address_mod, 1)) { + if (ModbusRTUClient.available()) { + switch (functionCode) { + case INPUT_REGISTERS: + case HOLDING_REGISTERS: + res = ModbusRTUClient.read(); + break; + case COILS: + case DISCRETE_INPUTS: + res = (uint8_t) ModbusRTUClient.read(); + break; + default: + return res; + } + + if(res != -1) { + udp_rx_buf[6] = (uint8_t)((res & 0xFF00) >> 8); + udp_rx_buf[7] = (uint8_t)(res & 0x00FF); + _server->beginPacket(_last_ip, _last_port); + _server->write((uint8_t *)udp_rx_buf, sizeof(udp_rx_buf)); + _server->endPacket(); + } + } + } + return res; } +long ModbusT1SServerClass::write(int address, int functionCode) { + long res = -1; + if(_server == nullptr) { + return res; + } + + int modbus_id = udp_rx_buf[2] << 8 | udp_rx_buf[3]; + int address_mod = udp_rx_buf[4] << 8 | udp_rx_buf[5]; + int value = -1; + switch (functionCode) { + case HOLDING_REGISTERS: + value = udp_rx_buf[6] << 8 | udp_rx_buf[7]; + break; + case COILS: + value = udp_rx_buf[7]; + break; + default: + return res; + break; + } + + ModbusRTUClient.beginTransmission(modbus_id, functionCode, address_mod, 1); + res = ModbusRTUClient.write(value); + if (!ModbusRTUClient.endTransmission()) { + res = -1; + } + return res; +} + + /** * Sets the Arduino_10BASE_T1S_UDP server for communication. * @@ -501,15 +366,15 @@ void ModbusT1SServerClass::update() { * * @param server A pointer to the Arduino_10BASE_T1S_UDP server. */ -void ModbusT1SServerClass::setT1SServer(Arduino_10BASE_T1S_UDP * server) { - _server = server; +void ModbusT1SServerClass::setT1SServer(Arduino_10BASE_T1S_UDP & server) { + _server = &server; } void ModbusT1SServerClass::setT1SPort(int port) { udp_port = port; } -void ModbusT1SServerClass::setBadrate(int baudrate) { +void ModbusT1SServerClass::setBaudrate(int baudrate) { _baudrate = baudrate; } @@ -519,7 +384,7 @@ void ModbusT1SServerClass::setCallback(callback_f cb) { } } -static void OnPlcaStatus_server(bool success, bool plcaStatus) +static void default_OnPlcaStatus(bool success, bool plcaStatus) { if (!success) { @@ -535,5 +400,19 @@ static void OnPlcaStatus_server(bool success, bool plcaStatus) } } +void ModbusT1SServerClass::enablePOE() { + tc6_inst->digitalWrite(TC6::DIO::A0, true); + tc6_inst->digitalWrite(TC6::DIO::A1, true); +} + +void ModbusT1SServerClass::disablePOE() { + tc6_inst->digitalWrite(TC6::DIO::A0, false); + tc6_inst->digitalWrite(TC6::DIO::A1, true); +} + +void ModbusT1SServerClass::setGatwayIP(IPAddress ip) { + _gateway = ip; +} + ModbusT1SServerClass ModbusT1SServer; #endif diff --git a/src/ModbusT1SServer.h b/src/ModbusT1SServer.h index 883703a..7f2d8be 100644 --- a/src/ModbusT1SServer.h +++ b/src/ModbusT1SServer.h @@ -28,15 +28,6 @@ #include "ModbusT1SCommon.h" #include -// valutare meglio dove metterla -#define RS485_SERIAL Serial1 -#define RS485_TX_PIN 1 -#define RS485_RX_PIN 0 -#define RS485_DE_PIN 8 -#define RS485_RE_PIN 7 -#define PRE_DELAY 100 -#define POST_DELAY 100 -#define PWR_CARRIER_RATIO 0.18f static void OnPlcaStatus_server(bool success, bool plcaStatus); using callback_f = void (*)(bool, bool); @@ -64,10 +55,8 @@ class ModbusT1SServerClass : public ModbusServer { * * Return 1 on success, 0 on failure */ - int begin(int id, unsigned long baudrate, uint16_t config = SERIAL_8N1); - int begin(RS485Class& rs485, int id, unsigned long baudrate, uint16_t config = SERIAL_8N1); - int begin(RS485Class& rs485, unsigned long baudrate, uint16_t config = SERIAL_8N1); - int begin(int node_id); + + int begin(int node_id, unsigned long baudrate, uint16_t config = SERIAL_8N1, RS485Class& rs485 = RS485); /** * Reads a coil from the Modbus server. * @@ -151,27 +140,33 @@ class ModbusT1SServerClass : public ModbusServer { * * @param server The T1S server to use */ - void setT1SServer(Arduino_10BASE_T1S_UDP * server); + void setT1SServer(Arduino_10BASE_T1S_UDP & server); void setT1SPort(int port = 8889); void update(); - void setBadrate(int baudrate); + void setBaudrate(int baudrate); /** * Poll interface for requests */ + void setGatwayIP(IPAddress ip); virtual int poll(); - void checkPLCAStatus(); + void checkStatus(); void setCallback(callback_f cb = nullptr); - + long read(int address, int functionCode); + long write(int address, int functionCode); + void enablePOE(); + void disablePOE(); + void setTimeout(unsigned long timeout); private: + IPAddress _gateway = IPAddress(0, 0, 0, 0); + uint8_t udp_rx_buf[8] = {0}; callback_f callback = nullptr; RS485Class* _rs485 = &RS485; - std::vector udp_rx_buf; Arduino_10BASE_T1S_UDP * _server = nullptr; IPAddress _last_ip; uint16_t _last_port; @@ -179,7 +174,7 @@ class ModbusT1SServerClass : public ModbusServer { int _node_id = 1; int udp_port = 0; }; -//Arduino_10BASE_T1S_UDP udp_server; + extern ModbusT1SServerClass ModbusT1SServer; #endif From 0542763286c9c0ae6f62662f7fe0cbc26b12ed62 Mon Sep 17 00:00:00 2001 From: Rocketct Date: Thu, 14 Nov 2024 09:49:51 +0100 Subject: [PATCH 12/17] Moved common structure in common file --- src/ModbusT1SCommon.cpp | 4 ++++ src/ModbusT1SCommon.h | 29 +++++++++++++++++++++-------- 2 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 src/ModbusT1SCommon.cpp diff --git a/src/ModbusT1SCommon.cpp b/src/ModbusT1SCommon.cpp new file mode 100644 index 0000000..9a08cd1 --- /dev/null +++ b/src/ModbusT1SCommon.cpp @@ -0,0 +1,4 @@ +#include "ModbusT1SCommon.h" +#if (defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_UNOR4_MINIMA)) +INIT_TC6(SPI, CS_PIN, RESET_PIN, IRQ_PIN); +#endif \ No newline at end of file diff --git a/src/ModbusT1SCommon.h b/src/ModbusT1SCommon.h index f2029ff..812244f 100644 --- a/src/ModbusT1SCommon.h +++ b/src/ModbusT1SCommon.h @@ -1,8 +1,28 @@ #ifndef _MODBUS_T1S_COMMON_H_INCLUDED #define _MODBUS_T1S_COMMON_H_INCLUDED -#if (defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_UNOR4_MINIMA)) + #include +#define INIT_TC6(_SPI, _CS_PIN, _RESET_PIN, _IRQ_PIN) \ + TC6::TC6_Io* tc6_io = new TC6::TC6_Io \ + ( _SPI \ + , _CS_PIN \ + , _RESET_PIN \ + , _IRQ_PIN); \ + TC6::TC6_Arduino_10BASE_T1S* tc6_inst = new TC6::TC6_Arduino_10BASE_T1S(tc6_io); + +extern TC6::TC6_Arduino_10BASE_T1S* tc6_inst; +extern TC6::TC6_Io* tc6_io; +static void default_OnPlcaStatus(bool success, bool plcaStatus); + +#if (defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_UNOR4_MINIMA)) +#define RS485_SERIAL Serial1 +#define RS485_TX_PIN 1 +#define RS485_RX_PIN 0 +#define RS485_DE_PIN 8 +#define RS485_RE_PIN 7 +#endif + enum ModbusT1SFunctionCode { UDP_READ_COIL_PORT = 1, UDP_WRITE_COIL_PORT, @@ -12,11 +32,4 @@ enum ModbusT1SFunctionCode { UDP_WRITE_HR_PORT }; -auto const tc6_io = new TC6::TC6_Io -( SPI - , CS_PIN - , RESET_PIN - , IRQ_PIN); -auto const tc6_inst = new TC6::TC6_Arduino_10BASE_T1S(tc6_io); -#endif #endif \ No newline at end of file From 1d7144943f089a2eb89eed8eef2879703bf1e586 Mon Sep 17 00:00:00 2001 From: Rocketct Date: Thu, 14 Nov 2024 10:41:16 +0100 Subject: [PATCH 13/17] Fixed headers inclusion to compile with all architectures --- src/ModbusT1SClient.cpp | 4 +--- src/ModbusT1SClient.h | 3 --- src/ModbusT1SCommon.cpp | 3 +-- src/ModbusT1SServer.cpp | 9 ++------- src/ModbusT1SServer.h | 5 +---- src/libmodbus/modbus-private.h | 2 +- 6 files changed, 6 insertions(+), 20 deletions(-) diff --git a/src/ModbusT1SClient.cpp b/src/ModbusT1SClient.cpp index ae190a0..9904f44 100644 --- a/src/ModbusT1SClient.cpp +++ b/src/ModbusT1SClient.cpp @@ -16,8 +16,7 @@ License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#if (defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_UNOR4_MINIMA)) + #include extern "C" { @@ -502,4 +501,3 @@ void ModbusT1SClientClass::disablePOE() { } ModbusT1SClientClass ModbusT1SClient; -#endif diff --git a/src/ModbusT1SClient.h b/src/ModbusT1SClient.h index ecb7e46..e9177bb 100644 --- a/src/ModbusT1SClient.h +++ b/src/ModbusT1SClient.h @@ -19,8 +19,6 @@ #ifndef _MODBUS_T1S_CLIENT_H_INCLUDED #define _MODBUS_T1S_CLIENT_H_INCLUDED -#if (defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_UNOR4_MINIMA)) - #include "ModbusClient.h" #include @@ -271,4 +269,3 @@ void disablePOE(); extern ModbusT1SClientClass ModbusT1SClient; #endif -#endif diff --git a/src/ModbusT1SCommon.cpp b/src/ModbusT1SCommon.cpp index 9a08cd1..d5a3d43 100644 --- a/src/ModbusT1SCommon.cpp +++ b/src/ModbusT1SCommon.cpp @@ -1,4 +1,3 @@ #include "ModbusT1SCommon.h" -#if (defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_UNOR4_MINIMA)) + INIT_TC6(SPI, CS_PIN, RESET_PIN, IRQ_PIN); -#endif \ No newline at end of file diff --git a/src/ModbusT1SServer.cpp b/src/ModbusT1SServer.cpp index 456b6ae..8a66894 100644 --- a/src/ModbusT1SServer.cpp +++ b/src/ModbusT1SServer.cpp @@ -16,7 +16,7 @@ License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#if (defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_UNOR4_MINIMA)) + #include extern "C" { @@ -388,14 +388,10 @@ static void default_OnPlcaStatus(bool success, bool plcaStatus) { if (!success) { - Serial.println("PLCA status register read failed"); return; } - if (plcaStatus) { - Serial.println("PLCA Mode active"); - } else { - Serial.println("CSMA/CD fallback"); + if (!plcaStatus) { tc6_inst->enablePlca(); } } @@ -415,4 +411,3 @@ void ModbusT1SServerClass::setGatwayIP(IPAddress ip) { } ModbusT1SServerClass ModbusT1SServer; -#endif diff --git a/src/ModbusT1SServer.h b/src/ModbusT1SServer.h index 7f2d8be..b2ab70d 100644 --- a/src/ModbusT1SServer.h +++ b/src/ModbusT1SServer.h @@ -19,7 +19,6 @@ #ifndef _MODBUS_T1S_SERVER_H_INCLUDED #define _MODBUS_T1S_SERVER_H_INCLUDED -#if (defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_UNOR4_MINIMA)) #include "ModbusServer.h" #include @@ -28,8 +27,7 @@ #include "ModbusT1SCommon.h" #include - -static void OnPlcaStatus_server(bool success, bool plcaStatus); + using callback_f = void (*)(bool, bool); class ModbusT1SServerClass : public ModbusServer { public: @@ -178,4 +176,3 @@ class ModbusT1SServerClass : public ModbusServer { extern ModbusT1SServerClass ModbusT1SServer; #endif -#endif diff --git a/src/libmodbus/modbus-private.h b/src/libmodbus/modbus-private.h index 7c4f541..ecd2f94 100644 --- a/src/libmodbus/modbus-private.h +++ b/src/libmodbus/modbus-private.h @@ -10,7 +10,7 @@ #ifndef _MSC_VER # include -#if defined(ARDUINO) && (defined(__AVR__) || (ARDUINO_UNOR4_WIFI) || defined(ARDUINO_UNOR4_MINIMA)) +#if defined(ARDUINO) && (defined(__AVR__) || defined(ARDUINO_ARCH_RENESAS)) #define ssize_t unsigned long #define fd_set void* From f7fda2bf35afbc1729b9c2f2760c493a4ae2f260 Mon Sep 17 00:00:00 2001 From: Rocketct Date: Thu, 14 Nov 2024 11:24:50 +0100 Subject: [PATCH 14/17] added github actions inclusion --- .github/workflows/compile-examples.yml | 32 +++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml index aae4de7..493bffd 100644 --- a/.github/workflows/compile-examples.yml +++ b/.github/workflows/compile-examples.yml @@ -31,6 +31,7 @@ jobs: # Install the ArduinoModbus library from the local path - source-path: ./ - name: ArduinoRS485 + - name: Arduino_10BASE_T1S UNIVERSAL_SKETCH_PATHS: | - examples/RTU @@ -42,24 +43,38 @@ jobs: - fqbn: arduino:megaavr:uno2018:mode=off ethernet: true nina: true + spe: false artifact-name-suffix: arduino-megaavr-uno2018 - fqbn: arduino:samd:mkrwifi1010 ethernet: true nina: true + spe: false artifact-name-suffix: arduino-samd-mkrwifi1010 - fqbn: arduino:mbed_nano:nano33ble ethernet: false nina: false + spe: false artifact-name-suffix: arduino-mbed_nano-nano33ble - fqbn: arduino:mbed_portenta:envie_m7 ethernet: false nina: false + spe: false artifact-name-suffix: arduino-mbed_portenta-envie_m7 - fqbn: arduino:mbed_opta:opta ethernet: true nina: false + spe: false artifact-name-suffix: arduino-mbed_opta-opta - + - fqbn: arduino:renesas_uno:unor4wifi + ethernet: false + nina: false + spe: true + artifact-name-suffix: arduino-renesas_uno-unor4wifi + - fqbn: arduino:renesas_uno:minima + ethernet: false + nina: false + spe: true + artifact-name-suffix: arduino-renesas_uno-minima # Make board type-specific customizations to the matrix jobs include: - board: @@ -90,6 +105,21 @@ jobs: nina: false nina-libraries: "" nina-sketch-paths: "" + - board: + # Boards with T1S shield + spe: true + # Install these libraries in addition to the ones defined by env.UNIVERSAL_LIBRARIES + spe-libraries: "" + # Compile these sketches in addition to the ones defined by env.UNIVERSAL_SKETCH_PATHS + nina-sketch-paths: | + - examples/T1S + - board: + # Boards with T1S shield + spe: false + # Install these libraries in addition to the ones defined by env.UNIVERSAL_LIBRARIES + spe-libraries: "" + # Compile these sketches in addition to the ones defined by env.UNIVERSAL_SKETCH_PATHS + nina-sketch-paths: "" steps: - name: Checkout From bfcc265645920656b070c74908f72d1ba367c876 Mon Sep 17 00:00:00 2001 From: Rocketct Date: Thu, 14 Nov 2024 13:09:12 +0100 Subject: [PATCH 15/17] Added check to avoid T1S library inclusion when megaavr core is used --- src/ArduinoModbus.h | 3 ++- src/ModbusT1SClient.cpp | 3 ++- src/ModbusT1SClient.h | 3 ++- src/ModbusT1SCommon.cpp | 3 ++- src/ModbusT1SCommon.h | 4 +++- src/ModbusT1SServer.cpp | 4 +++- src/ModbusT1SServer.h | 3 ++- 7 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/ArduinoModbus.h b/src/ArduinoModbus.h index 5a89502..7690ef4 100644 --- a/src/ArduinoModbus.h +++ b/src/ArduinoModbus.h @@ -26,7 +26,8 @@ #include "ModbusTCPClient.h" #include "ModbusTCPServer.h" +#ifndef __AVR__ #include "ModbusT1SClient.h" #include "ModbusT1SServer.h" - +#endif #endif diff --git a/src/ModbusT1SClient.cpp b/src/ModbusT1SClient.cpp index 9904f44..5c01e0f 100644 --- a/src/ModbusT1SClient.cpp +++ b/src/ModbusT1SClient.cpp @@ -25,7 +25,7 @@ extern "C" { } #include "ModbusT1SClient.h" - +#ifndef __AVR__ /** * @class ModbusT1SClientClass * Class for Modbus T1S Client communication. @@ -501,3 +501,4 @@ void ModbusT1SClientClass::disablePOE() { } ModbusT1SClientClass ModbusT1SClient; +#endif diff --git a/src/ModbusT1SClient.h b/src/ModbusT1SClient.h index e9177bb..829ac24 100644 --- a/src/ModbusT1SClient.h +++ b/src/ModbusT1SClient.h @@ -19,7 +19,7 @@ #ifndef _MODBUS_T1S_CLIENT_H_INCLUDED #define _MODBUS_T1S_CLIENT_H_INCLUDED - +#ifndef __AVR__ #include "ModbusClient.h" #include #include @@ -269,3 +269,4 @@ void disablePOE(); extern ModbusT1SClientClass ModbusT1SClient; #endif +#endif diff --git a/src/ModbusT1SCommon.cpp b/src/ModbusT1SCommon.cpp index d5a3d43..4aab607 100644 --- a/src/ModbusT1SCommon.cpp +++ b/src/ModbusT1SCommon.cpp @@ -1,3 +1,4 @@ #include "ModbusT1SCommon.h" - +#ifndef __AVR__ INIT_TC6(SPI, CS_PIN, RESET_PIN, IRQ_PIN); +#endif \ No newline at end of file diff --git a/src/ModbusT1SCommon.h b/src/ModbusT1SCommon.h index 812244f..1a75111 100644 --- a/src/ModbusT1SCommon.h +++ b/src/ModbusT1SCommon.h @@ -1,6 +1,7 @@ #ifndef _MODBUS_T1S_COMMON_H_INCLUDED #define _MODBUS_T1S_COMMON_H_INCLUDED +#ifndef __AVR__ #include #define INIT_TC6(_SPI, _CS_PIN, _RESET_PIN, _IRQ_PIN) \ @@ -32,4 +33,5 @@ enum ModbusT1SFunctionCode { UDP_WRITE_HR_PORT }; -#endif \ No newline at end of file +#endif +#endif diff --git a/src/ModbusT1SServer.cpp b/src/ModbusT1SServer.cpp index 8a66894..288bdce 100644 --- a/src/ModbusT1SServer.cpp +++ b/src/ModbusT1SServer.cpp @@ -17,6 +17,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + #include extern "C" { @@ -25,7 +26,7 @@ extern "C" { } #include "ModbusT1SServer.h" - +#ifndef __AVR__ /** * @class ModbusT1SServerClass * Class for Modbus T1S Server communication. @@ -411,3 +412,4 @@ void ModbusT1SServerClass::setGatwayIP(IPAddress ip) { } ModbusT1SServerClass ModbusT1SServer; +#endif diff --git a/src/ModbusT1SServer.h b/src/ModbusT1SServer.h index b2ab70d..628df61 100644 --- a/src/ModbusT1SServer.h +++ b/src/ModbusT1SServer.h @@ -20,6 +20,7 @@ #ifndef _MODBUS_T1S_SERVER_H_INCLUDED #define _MODBUS_T1S_SERVER_H_INCLUDED +#ifndef __AVR__ #include "ModbusServer.h" #include #include @@ -27,7 +28,6 @@ #include "ModbusT1SCommon.h" #include - using callback_f = void (*)(bool, bool); class ModbusT1SServerClass : public ModbusServer { public: @@ -176,3 +176,4 @@ class ModbusT1SServerClass : public ModbusServer { extern ModbusT1SServerClass ModbusT1SServer; #endif +#endif From b98bbea01abec3221de183deeadedf31afdf0c5e Mon Sep 17 00:00:00 2001 From: Rocketct Date: Fri, 15 Nov 2024 10:50:20 +0100 Subject: [PATCH 16/17] Added Docs --- src/ModbusT1SClient.cpp | 211 +++++++++++------- src/ModbusT1SClient.h | 469 +++++++++++++++++++++++----------------- src/ModbusT1SServer.cpp | 115 ++++++++-- src/ModbusT1SServer.h | 81 ++++++- 4 files changed, 577 insertions(+), 299 deletions(-) diff --git a/src/ModbusT1SClient.cpp b/src/ModbusT1SClient.cpp index 5c01e0f..5ba7bd3 100644 --- a/src/ModbusT1SClient.cpp +++ b/src/ModbusT1SClient.cpp @@ -82,7 +82,7 @@ int ModbusT1SClientClass::begin(int node_id) { return 0; } - //RRR ADD set IPs + if(_gateway == IPAddress(0, 0, 0, 0)) { _gateway = IPAddress(192, 168, 42, 100); } @@ -174,7 +174,6 @@ void ModbusT1SClientClass::setRxTimeout(unsigned long timeout) * * @param address The address of the coil to read. * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. - * @param port The port number to use for the communication. * @return int The status of the coil (1 for ON, 0 for OFF) or -1 if an error occurs. */ int ModbusT1SClientClass::coilRead(int address, Arduino_10BASE_T1S_UDP * client) @@ -191,7 +190,6 @@ int ModbusT1SClientClass::coilRead(int address, Arduino_10BASE_T1S_UDP * client) * @param id The ID of the Modbus server. * @param address The address of the coil to read. * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. - * @param port The port number to use for the communication. * @return int The status of the coil (1 for ON, 0 for OFF) or -1 if an error occurs. */ int ModbusT1SClientClass::coilRead(int id, int address, Arduino_10BASE_T1S_UDP * client) @@ -208,7 +206,6 @@ int ModbusT1SClientClass::coilRead(int id, int address, Arduino_10BASE_T1S_UDP * * @param address The address of the coil to write to. * @param value The value to write to the coil (1 for ON, 0 for OFF). * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. - * @param port The port number to use for the communication. * @return int 1 if the write operation is successful, -1 if an error occurs. */ int ModbusT1SClientClass::coilWrite(int address, uint16_t value, Arduino_10BASE_T1S_UDP * client) @@ -226,7 +223,6 @@ int ModbusT1SClientClass::coilWrite(int address, uint16_t value, Arduino_10BASE_ * @param address The address of the coil to write to. * @param value The value to write to the coil (1 for ON, 0 for OFF). * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. - * @param port The port number to use for the communication. * @return int 1 if the write operation is successful, -1 if an error occurs. */ int ModbusT1SClientClass::coilWrite(int id, int address, uint16_t value, Arduino_10BASE_T1S_UDP * client) @@ -242,7 +238,6 @@ int ModbusT1SClientClass::coilWrite(int id, int address, uint16_t value, Arduino * * @param address The address of the discrete input to read. * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. - * @param port The port number to use for the communication. * @return int The status of the discrete input (1 for ON, 0 for OFF) or -1 if an error occurs. */ int ModbusT1SClientClass::discreteInputRead(int address, Arduino_10BASE_T1S_UDP * client) @@ -259,7 +254,6 @@ int ModbusT1SClientClass::discreteInputRead(int address, Arduino_10BASE_T1S_UDP * @param id The ID of the Modbus server. * @param address The address of the discrete input to read. * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. - * @param port The port number to use for the communication. * @return int The status of the discrete input (1 for ON, 0 for OFF) or -1 if an error occurs. */ int ModbusT1SClientClass::discreteInputRead(int id, int address, Arduino_10BASE_T1S_UDP * client) @@ -275,7 +269,6 @@ int ModbusT1SClientClass::discreteInputRead(int id, int address, Arduino_10BASE_ * * @param address The address of the input register to read. * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. - * @param port The port number to use for the communication. * @return long The value of the input register or -1 if an error occurs. */ long ModbusT1SClientClass::inputRegisterRead(int address, Arduino_10BASE_T1S_UDP * client) @@ -292,7 +285,6 @@ long ModbusT1SClientClass::inputRegisterRead(int address, Arduino_10BASE_T1S_UDP * @param id The ID of the Modbus server. * @param address The address of the input register to read. * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. - * @param port The port number to use for the communication. * @return long The value of the input register or -1 if an error occurs. */ long ModbusT1SClientClass::inputRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client) @@ -308,7 +300,6 @@ long ModbusT1SClientClass::inputRegisterRead(int id, int address, Arduino_10BASE * * @param address The address of the holding register to read from. * @param client A pointer to an Arduino_10BASE_T1S_UDP client used for communication. - * @param port The port number to use for the communication. * @return Returns the value of the holding register on success, or -1 if the client is null. */ long ModbusT1SClientClass::holdingRegisterRead(int address, Arduino_10BASE_T1S_UDP * client) @@ -326,7 +317,6 @@ long ModbusT1SClientClass::holdingRegisterRead(int address, Arduino_10BASE_T1S_U * @param address The address of the holding register to write to. * @param value The value to write to the holding register. * @param client A pointer to an Arduino_10BASE_T1S_UDP client used for communication. - * @param port The port number to use for the communication. * @return Returns 1 on success, or -1 if the client is null. */ long ModbusT1SClientClass::holdingRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client) @@ -361,7 +351,6 @@ int ModbusT1SClientClass::holdingRegisterWrite(int address, uint16_t value, Ardu * @param address The address of the holding register to write to. * @param value The value to write to the holding register. * @param client A pointer to an Arduino_10BASE_T1S_UDP client used for communication. - * @param port The port number to use for the communication. * @return Returns 1 on success, or -1 if the client is null. */ int ModbusT1SClientClass::holdingRegisterWrite(int id, int address, uint16_t value, Arduino_10BASE_T1S_UDP * client) @@ -369,6 +358,137 @@ int ModbusT1SClientClass::holdingRegisterWrite(int id, int address, uint16_t val return send(id, address, value, client, UDP_WRITE_HR_PORT); } +/** + * Checks if the received packet matches the specified port, id, and address. + * + * This function compares the received packet's port, id, and address with the provided + * values to determine if they match. + * + * @param port The port number to check against the received packet. + * @param id The id to check against the received packet. + * @param address The address to check against the received packet. + * @return true if the received packet matches the specified port, id, and address; false otherwise. + */ +bool ModbusT1SClientClass::checkPacket(int port, uint16_t id, uint16_t address) +{ + int port_rec = udp_rx_buf[0] << 8 | udp_rx_buf[1]; + uint16_t id_rcv = udp_rx_buf[2] << 8 | udp_rx_buf[3]; + uint16_t add_rcv = udp_rx_buf[4] << 8 | udp_rx_buf[5]; + if(port_rec == port && add_rcv == address && id_rcv == id) { + return true; + } + return false; +} + +/** + * Sets the T1S client for the Modbus communication. + * + * This function assigns a 10BASE-T1S UDP client to be used for Modbus communication. + * + * @param client A reference to an Arduino_10BASE_T1S_UDP object that represents the T1S client. + */ +void ModbusT1SClientClass::setT1SClient(Arduino_10BASE_T1S_UDP & client) +{ + _client = &client; +} + +/** + * Sets the T1S port for the Modbus communication. + * + * This function sets the port number to be used for the T1S communication. + * + * @param port The port number to use for T1S communication. + */ +void ModbusT1SClientClass::setT1SPort(int port) +{ + udp_port = port; +} + +/** + * Polls the Modbus client for incoming data. + * + * This function polls the Modbus client for incoming data. + */ +void ModbusT1SClientClass::update() +{ + tc6_inst->service(); + + static unsigned long prev_beacon_check = 0; + static unsigned long prev_udp_packet_sent = 0; + + auto const now = millis(); + + if ((now - prev_beacon_check) > 1000) + { + prev_beacon_check = now; + tc6_inst->getPlcaStatus(callback); + } +} + +/** + * Sets the callback function to be used by the ModbusT1SClient. + * + * This function sets the callback function to be used by the ModbusT1SClient. + * + * @param cb The callback function to use. + */ +void ModbusT1SClientClass::setCallback(callback_f cb) { + if(cb != nullptr) { + callback = cb; + } +} + +/** + * Default callback function for PLCA status check. + * + * This function is the default callback function for PLCA status check. + * + * @param success The success status of the PLCA status check. + * @param plcaStatus The PLCA status. + */ +static void default_OnPlcaStatus(bool success, bool plcaStatus) +{ + if (!success) + { + return; + } + + if (!plcaStatus) { + tc6_inst->enablePlca(); + } +} + +/** + * Sets the gateway IP address for the Modbus client. + * + * This function sets the gateway IP address for the Modbus client. + * + * @param gateway The gateway IP address. + */ +void ModbusT1SClientClass::setGateway(IPAddress gateway) { + _gateway = gateway; +} + +/** + * Enables Power Over Ethernet (POE). + * + * This function enables Power Over Ethernet (POE) on the T1S client. + */ +void ModbusT1SClientClass::enablePOE() { + tc6_inst->digitalWrite(TC6::DIO::A0, true); + tc6_inst->digitalWrite(TC6::DIO::A1, true); +} + +/** + * Disables Power Over Ethernet (POE). + * + * This function disables Power Over Ethernet (POE) on the T1S client. + */ +void ModbusT1SClientClass::disablePOE() { + tc6_inst->digitalWrite(TC6::DIO::A0, false); + tc6_inst->digitalWrite(TC6::DIO::A1, true); +} + long ModbusT1SClientClass::receive(int id, int address, Arduino_10BASE_T1S_UDP * client, int functionCode) { long res = -1; @@ -417,6 +537,7 @@ int ModbusT1SClientClass::send(int id, int address, uint16_t value, Arduino_10BA return 1; } + void ModbusT1SClientClass::write(uint8_t * buf, int len, Arduino_10BASE_T1S_UDP * client) { client->beginPacket(_server_ip, _server_port); @@ -434,71 +555,5 @@ int ModbusT1SClientClass::read(Arduino_10BASE_T1S_UDP * client) } return 0; } - -bool ModbusT1SClientClass::checkPacket(int port, uint16_t id, uint16_t address) -{ - int port_rec = udp_rx_buf[0] << 8 | udp_rx_buf[1]; - uint16_t id_rcv = udp_rx_buf[2] << 8 | udp_rx_buf[3]; - uint16_t add_rcv = udp_rx_buf[4] << 8 | udp_rx_buf[5]; - if(port_rec == port && add_rcv == address && id_rcv == id) { - return true; - } - return false; -} - -void ModbusT1SClientClass::setT1SClient(Arduino_10BASE_T1S_UDP & client) -{ - _client = &client; -} - -void ModbusT1SClientClass::setT1SPort(int port) -{ - udp_port = port; -} - -void ModbusT1SClientClass::update() -{ - tc6_inst->service(); - - static unsigned long prev_beacon_check = 0; - static unsigned long prev_udp_packet_sent = 0; - - auto const now = millis(); - - if ((now - prev_beacon_check) > 1000) - { - prev_beacon_check = now; - //tc6_inst->getPlcaStatus(callback); - } -} - -void ModbusT1SClientClass::setCallback(callback_f cb) { - if(cb != nullptr) { - callback = cb; - } -} - -static void default_OnPlcaStatus(bool success, bool plcaStatus) -{ - if (!success) - { - return; - } - - if (!plcaStatus) { - tc6_inst->enablePlca(); - } -} - -void ModbusT1SClientClass::enablePOE() { - tc6_inst->digitalWrite(TC6::DIO::A0, true); - tc6_inst->digitalWrite(TC6::DIO::A1, true); -} - -void ModbusT1SClientClass::disablePOE() { - tc6_inst->digitalWrite(TC6::DIO::A0, false); - tc6_inst->digitalWrite(TC6::DIO::A1, true); -} - ModbusT1SClientClass ModbusT1SClient; #endif diff --git a/src/ModbusT1SClient.h b/src/ModbusT1SClient.h index 829ac24..04d96c1 100644 --- a/src/ModbusT1SClient.h +++ b/src/ModbusT1SClient.h @@ -31,8 +31,25 @@ using callback_f = void (*)(bool, bool); class ModbusT1SClientClass : public ModbusClient { public: + /** + * Default constructor for ModbusT1SClientClass. + * + * Initializes the Modbus client with a default timeout of 1000 milliseconds. + */ ModbusT1SClientClass(); + + /** + * Constructor for ModbusT1SClientClass with RS485 support. + * + * Initializes the Modbus client with a default timeout of 1000 milliseconds and sets up RS485 communication. + * + * @param rs485 Reference to an RS485Class object for RS485 communication. + */ ModbusT1SClientClass(RS485Class& rs485); + + /** + * Destructor for ModbusT1SClientClass. + */ virtual ~ModbusT1SClientClass(); /** @@ -44,205 +61,259 @@ class ModbusT1SClientClass : public ModbusClient { * Return 1 on success, 0 on failure */ int begin(int node_id); -/** - * Sets the IP address of the Modbus server. - * - * This function sets the IP address of the Modbus server that the client will communicate with. - * - * @param server_ip The IP address of the Modbus server. Default is IPAddress(0, 0, 0, 0). - */ -void setServerIp(IPAddress server_ip = IPAddress(0, 0, 0, 0)); - -/** - * Sets the port number of the Modbus server. - * - * This function sets the port number of the Modbus server that the client will communicate with. - * - * @param server_port The port number of the Modbus server. Default is 8889. - */ -void setServerPort(uint16_t server_port = 8889); - -/** - * Sets the Modbus ID of the client. - * - * This function sets the Modbus ID that the client will use for communication. - * - * @param id The Modbus ID. - */ -void setModbusId(uint16_t id); - -/** - * Reads the status of a coil from the Modbus server. - * - * This function sends a request to read the status of a coil at the specified address - * from the Modbus server using the provided UDP client and port. - * - * @param address The address of the coil to read. - * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. - * @param port The port number of the Modbus server. Default is 0. - * @return int The status of the coil (1 for ON, 0 for OFF) or -1 if an error occurs. - */ -int coilRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr); - -/** - * Reads the status of a coil from the Modbus server with a specified ID. - * - * This function sends a request to read the status of a coil at the specified address - * from the Modbus server with the given ID using the provided UDP client and port. - * - * @param id The ID of the Modbus server. - * @param address The address of the coil to read. - * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. - * @param port The port number of the Modbus server. Default is 0. - * @return int The status of the coil (1 for ON, 0 for OFF) or -1 if an error occurs. - */ -int coilRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr); - -/** - * Writes a value to a coil on the Modbus server. - * - * This function sends a request to write a value to a coil at the specified address - * on the Modbus server using the provided UDP client and port. - * - * @param address The address of the coil to write to. - * @param value The value to write to the coil (1 for ON, 0 for OFF). - * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. - * @param port The port number of the Modbus server. Default is 0. - * @return int Returns 1 if the write operation is successful, 0 otherwise. - */ -int coilWrite(int address, uint16_t value, Arduino_10BASE_T1S_UDP * client = nullptr); - -/** - * Writes a value to a coil on the Modbus server with a specified ID. - * - * This function sends a request to write a value to a coil at the specified address - * on the Modbus server with the given ID using the provided UDP client and port. - * - * @param id The ID of the Modbus server. - * @param address The address of the coil to write to. - * @param value The value to write to the coil (1 for ON, 0 for OFF). - * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. - * @param port The port number of the Modbus server. Default is 0. - * @return int Returns 1 if the write operation is successful, 0 otherwise. - */ -int coilWrite(int id, int address, uint16_t value, Arduino_10BASE_T1S_UDP * client = nullptr); - -/** - * Reads the status of a discrete input from the Modbus server. - * - * This function sends a request to read the status of a discrete input at the specified address - * from the Modbus server using the provided UDP client and port. - * - * @param address The address of the discrete input to read. - * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. - * @param port The port number of the Modbus server. Default is 0. - * @return int The status of the discrete input (1 for ON, 0 for OFF) or -1 if an error occurs. - */ -int discreteInputRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr); - -/** - * Reads the status of a discrete input from the Modbus server with a specified ID. - * - * This function sends a request to read the status of a discrete input at the specified address - * from the Modbus server with the given ID using the provided UDP client and port. - * - * @param id The ID of the Modbus server. - * @param address The address of the discrete input to read. - * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. - * @param port The port number of the Modbus server. Default is 0. - * @return int The status of the discrete input (1 for ON, 0 for OFF) or -1 if an error occurs. - */ -int discreteInputRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr); - -/** - * Reads the value of an input register from the Modbus server. - * - * This function sends a request to read the value of an input register at the specified address - * from the Modbus server using the provided UDP client and port. - * - * @param address The address of the input register to read. - * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. - * @param port The port number of the Modbus server. Default is 0. - * @return long The value of the input register or -1 if an error occurs. - */ -long inputRegisterRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr); - -/** - * Reads the value of an input register from the Modbus server with a specified ID. - * - * This function sends a request to read the value of an input register at the specified address - * from the Modbus server with the given ID using the provided UDP client and port. - * - * @param id The ID of the Modbus server. - * @param address The address of the input register to read. - * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. - * @param port The port number of the Modbus server. Default is 0. - * @return long The value of the input register or -1 if an error occurs. - */ -long inputRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr); - -/** - * Writes a value to a holding register on the Modbus server. - * - * This function sends a request to write a value to a holding register at the specified address - * on the Modbus server using the provided UDP client and port. - * - * @param address The address of the holding register to write to. - * @param value The value to write to the holding register. - * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. - * @param port The port number of the Modbus server. Default is 0. - * @return int Returns 1 if the write operation is successful, 0 otherwise. - */ -int holdingRegisterWrite(int address, uint16_t value, Arduino_10BASE_T1S_UDP * client = nullptr); - -/** - * Writes a value to a holding register on the Modbus server with a specified ID. - * - * This function sends a request to write a value to a holding register at the specified address - * on the Modbus server with the given ID using the provided UDP client and port. - * - * @param id The ID of the Modbus server. - * @param address The address of the holding register to write to. - * @param value The value to write to the holding register. - * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. - * @param port The port number of the Modbus server. Default is 0. - * @return int Returns 1 if the write operation is successful, 0 otherwise. - */ -int holdingRegisterWrite(int id, int address, uint16_t value, Arduino_10BASE_T1S_UDP * client = nullptr); - -/** - * Reads the value of a holding register from the Modbus server. - * - * This function sends a request to read the value of a holding register at the specified address - * from the Modbus server using the provided UDP client and port. - * - * @param address The address of the holding register to read. - * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. - * @param port The port number of the Modbus server. Default is 0. - * @return long The value of the holding register or -1 if an error occurs. - */ -long holdingRegisterRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr); - -/** - * Reads the value of a holding register from the Modbus server with a specified ID. - * - * This function sends a request to read the value of a holding register at the specified address - * from the Modbus server with the given ID using the provided UDP client and port. - * - * @param id The ID of the Modbus server. - * @param address The address of the holding register to read. - * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. - * @param port The port number of the Modbus server. Default is 0. - * @return long The value of the holding register or -1 if an error occurs. - */ -long holdingRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr); -void setT1SClient(Arduino_10BASE_T1S_UDP & client); -void setRxTimeout(unsigned long timeout = RX_TIMEOUT); -void setT1SPort(int port = 8889); -void update(); -void setCallback(callback_f cb = nullptr); -void enablePOE(); -void disablePOE(); + + /** + * Sets the IP address of the Modbus server. + * + * This function sets the IP address of the Modbus server that the client will communicate with. + * + * @param server_ip The IP address of the Modbus server. Default is IPAddress(0, 0, 0, 0). + */ + void setServerIp(IPAddress server_ip = IPAddress(0, 0, 0, 0)); + + /** + * Sets the port number of the Modbus server. + * + * This function sets the port number of the Modbus server that the client will communicate with. + * + * @param server_port The port number of the Modbus server. Default is 8889. + */ + void setServerPort(uint16_t server_port = 8889); + + /** + * Sets the Modbus ID of the client. + * + * This function sets the Modbus ID that the client will use for communication. + * + * @param id The Modbus ID. + */ + void setModbusId(uint16_t id); + + /** + * Reads the status of a coil from the Modbus server. + * + * This function sends a request to read the status of a coil at the specified address + * from the Modbus server using the provided UDP client and port. + * + * @param address The address of the coil to read. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. + * @return int The status of the coil (1 for ON, 0 for OFF) or -1 if an error occurs. + */ + int coilRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr); + + /** + * Reads the status of a coil from the Modbus server with a specified ID. + * + * This function sends a request to read the status of a coil at the specified address + * from the Modbus server with the given ID using the provided UDP client and port. + * + * @param id The ID of the Modbus server. + * @param address The address of the coil to read. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. + * @return int The status of the coil (1 for ON, 0 for OFF) or -1 if an error occurs. + */ + int coilRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr); + + /** + * Writes a value to a coil on the Modbus server. + * + * This function sends a request to write a value to a coil at the specified address + * on the Modbus server using the provided UDP client and port. + * + * @param address The address of the coil to write to. + * @param value The value to write to the coil (1 for ON, 0 for OFF). + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. + * @return int Returns 1 if the write operation is successful, 0 otherwise. + */ + int coilWrite(int address, uint16_t value, Arduino_10BASE_T1S_UDP * client = nullptr); + + /** + * Writes a value to a coil on the Modbus server with a specified ID. + * + * This function sends a request to write a value to a coil at the specified address + * on the Modbus server with the given ID using the provided UDP client and port. + * + * @param id The ID of the Modbus server. + * @param address The address of the coil to write to. + * @param value The value to write to the coil (1 for ON, 0 for OFF). + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. + * @return int Returns 1 if the write operation is successful, 0 otherwise. + */ + int coilWrite(int id, int address, uint16_t value, Arduino_10BASE_T1S_UDP * client = nullptr); + + /** + * Reads the status of a discrete input from the Modbus server. + * + * This function sends a request to read the status of a discrete input at the specified address + * from the Modbus server using the provided UDP client and port. + * + * @param address The address of the discrete input to read. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. + * @return int The status of the discrete input (1 for ON, 0 for OFF) or -1 if an error occurs. + */ + int discreteInputRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr); + + /** + * Reads the status of a discrete input from the Modbus server with a specified ID. + * + * This function sends a request to read the status of a discrete input at the specified address + * from the Modbus server with the given ID using the provided UDP client and port. + * + * @param id The ID of the Modbus server. + * @param address The address of the discrete input to read. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. + * @return int The status of the discrete input (1 for ON, 0 for OFF) or -1 if an error occurs. + */ + int discreteInputRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr); + + /** + * Reads the value of an input register from the Modbus server. + * + * This function sends a request to read the value of an input register at the specified address + * from the Modbus server using the provided UDP client and port. + * + * @param address The address of the input register to read. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. + * @return long The value of the input register or -1 if an error occurs. + */ + long inputRegisterRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr); + + /** + * Reads the value of an input register from the Modbus server with a specified ID. + * + * This function sends a request to read the value of an input register at the specified address + * from the Modbus server with the given ID using the provided UDP client and port. + * + * @param id The ID of the Modbus server. + * @param address The address of the input register to read. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. + * @return long The value of the input register or -1 if an error occurs. + */ + long inputRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr); + + /** + * Writes a value to a holding register on the Modbus server. + * + * This function sends a request to write a value to a holding register at the specified address + * on the Modbus server using the provided UDP client and port. + * + * @param address The address of the holding register to write to. + * @param value The value to write to the holding register. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. + * @return int Returns 1 if the write operation is successful, 0 otherwise. + */ + int holdingRegisterWrite(int address, uint16_t value, Arduino_10BASE_T1S_UDP * client = nullptr); + + /** + * Writes a value to a holding register on the Modbus server with a specified ID. + * + * This function sends a request to write a value to a holding register at the specified address + * on the Modbus server with the given ID using the provided UDP client and port. + * + * @param id The ID of the Modbus server. + * @param address The address of the holding register to write to. + * @param value The value to write to the holding register. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. + * @return int Returns 1 if the write operation is successful, 0 otherwise. + */ + int holdingRegisterWrite(int id, int address, uint16_t value, Arduino_10BASE_T1S_UDP * client = nullptr); + + /** + * Reads the value of a holding register from the Modbus server. + * + * This function sends a request to read the value of a holding register at the specified address + * from the Modbus server using the provided UDP client and port. + * + * @param address The address of the holding register to read. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. + * @return long The value of the holding register or -1 if an error occurs. + */ + long holdingRegisterRead(int address, Arduino_10BASE_T1S_UDP * client = nullptr); + + /** + * Reads the value of a holding register from the Modbus server with a specified ID. + * + * This function sends a request to read the value of a holding register at the specified address + * from the Modbus server with the given ID using the provided UDP client and port. + * + * @param id The ID of the Modbus server. + * @param address The address of the holding register to read. + * @param client A pointer to the Arduino_10BASE_T1S_UDP client used for communication. Default is nullptr. + * @return long The value of the holding register or -1 if an error occurs. + */ + long holdingRegisterRead(int id, int address, Arduino_10BASE_T1S_UDP * client = nullptr); + + /** + * Sets the T1S client for the Modbus communication. + * + * This function assigns a 10BASE-T1S UDP client to be used for Modbus communication. + * + * @param client A reference to an Arduino_10BASE_T1S_UDP object that represents the T1S client. + */ + void setT1SClient(Arduino_10BASE_T1S_UDP & client); + + /** + * Sets the gateway IP address for the Modbus client. + * + * This function sets the gateway IP address for the Modbus client. + * + * @param gateway The gateway IP address. + */ + + void setGateway(IPAddress gateway = IPAddress(0, 0, 0, 0)); + + /** + * Sets the timeout for receiving a response from the Modbus server. + * + * This function sets the timeout for receiving a response from the Modbus server. + * + * @param timeout The timeout value in milliseconds. + */ + void setRxTimeout(unsigned long timeout = RX_TIMEOUT); + + /** + * Sets the port to use for communication. + * + * This function sets the port to use for communication. + * + * @param port The port to use. + */ + void setT1SPort(int port = 8889); + + /** + * Polls the Modbus client for incoming data. + * + * This function polls the Modbus client for incoming data. + * + * @return int The number of bytes read from the client. + */ + void update(); + + /** + * Sets the callback function to be used by the ModbusT1SClient. + * + * This function allows you to specify a callback function that will be called + * when certain events occur in the ModbusT1SClient. If no callback function is + * provided, the default value of nullptr will be used, meaning no callback + * will be executed. + * + * @param cb The callback function to be set. The default value is nullptr. + */ + void setCallback(callback_f cb = nullptr); + + /** + * Enables Power Over Ethernet (POE) on the Modbus client. + * + * This function enables Power Over Ethernet (POE) on the Modbus client and sets the device as the power source. + */ + void enablePOE(); + + /** + * Disables Power Over Ethernet (POE) on the Modbus client. + * + * This function disables Power Over Ethernet (POE) on the Modbus client and set the USB as power source. + */ + void disablePOE(); private: diff --git a/src/ModbusT1SServer.cpp b/src/ModbusT1SServer.cpp index 288bdce..ebd2ffa 100644 --- a/src/ModbusT1SServer.cpp +++ b/src/ModbusT1SServer.cpp @@ -27,11 +27,11 @@ extern "C" { #include "ModbusT1SServer.h" #ifndef __AVR__ + /** - * @class ModbusT1SServerClass - * Class for Modbus T1S Server communication. + * Default constructor for ModbusT1SServerClass. * - * This class provides functionalities to communicate with a Modbus T1S server. + * Initializes the Modbus server with RS485 communication. */ ModbusT1SServerClass::ModbusT1SServerClass(): callback(default_OnPlcaStatus) @@ -58,6 +58,16 @@ ModbusT1SServerClass::~ModbusT1SServerClass() { } +/** + * Start the Modbus T1S server with the specified parameters and RS485 communication. + * + * @param node_id id of the server + * @param baudrate Baud rate to use + * @param config serial config. to use defaults to SERIAL_8N1 + * @param rs485 RS485 object to use + * + * Return 1 on success, 0 on failure + */ int ModbusT1SServerClass::begin(int node_id, unsigned long baudrate, uint16_t config, RS485Class& rs485) { _node_id = node_id; @@ -116,11 +126,18 @@ int ModbusT1SServerClass::begin(int node_id, unsigned long baudrate, uint16_t co return 0; } - ModbusRTUClient.setTimeout(2*1000); //RRR set timeout + ModbusRTUClient.setTimeout(2*1000); return 1; } +/** + * Set the Modbus RTU client timeout. + * + * This function sets the Modbus RTU client timeout. + * + * @param timeout The timeout value in milliseconds. + */ void ModbusT1SServerClass::setTimeout(unsigned long timeout) { ModbusRTUClient.setTimeout(timeout); } @@ -156,10 +173,9 @@ int ModbusT1SServerClass::coilRead(int address) * on the Modbus server using the provided UDP client. * * @param address The address of the coil to write to. - * @param value The value to write to the coil (1 for ON, 0 for OFF). * @return int 1 if the write operation is successful, -1 if an error occurs. */ -int ModbusT1SServerClass::coilWrite(int address, uint8_t value) +int ModbusT1SServerClass::coilWrite(int address) { return write(address, COILS); } @@ -197,7 +213,6 @@ long ModbusT1SServerClass::inputRegisterRead(int address) * on the Modbus server using the provided UDP client. * * @param address The address of the holding register to write to. - * @param value The value to write to the holding register. * @return int 1 if the write operation is successful, -1 if an error occurs. */ long ModbusT1SServerClass::holdingRegisterRead(int address) { @@ -211,7 +226,6 @@ long ModbusT1SServerClass::holdingRegisterRead(int address) { * on the Modbus server using the provided UDP client. * * @param address The address of the holding register to write to. - * @param value The value to write to the holding register. * @return int 1 if the write operation is successful, -1 if an error occurs. */ int ModbusT1SServerClass::holdingRegisterWrite(int address) { @@ -243,6 +257,13 @@ int ModbusT1SServerClass::parsePacket() { return res; } +/** + * Checks the PLCA status and Services the hardware and the protocol stack. + * + * This function checks the PLCA status and services the hardware and the + * protocol stack. + * + */ void ModbusT1SServerClass::checkStatus() { tc6_inst->service(); @@ -259,10 +280,13 @@ void ModbusT1SServerClass::checkStatus() } } +/** + * Manage the incoming T1S packets and make the appropriate Modbus request. + * + * This function manages the incoming T1S packets and makes the appropriate Modbus request. + */ void ModbusT1SServerClass::update() { - /* Services the hardware and the protocol stack. - Must be called cyclic. The faster the better. - */ + checkStatus(); switch (parsePacket()) { @@ -295,6 +319,16 @@ void ModbusT1SServerClass::update() { } } +/** + * Reads a value from the T1S client, sends it to the Modbus server and sends the response back to the client. + * + * This function reads a value from the T1S client, sends it to the Modbus server and sends the response back to the client. + * + * @param address The address of the value to read. + * @param functionCode The function code to use for the Modbus request. + * + * @return long The value read from the Modbus server. + */ long ModbusT1SServerClass::read(int address, int functionCode) { long res = -1; if(_server == nullptr) { @@ -330,6 +364,16 @@ long ModbusT1SServerClass::read(int address, int functionCode) { return res; } +/** + * reads a value from the T1S client and sends it to the Modbus server. + * + * This function reads a value from the T1S client and sends it to the Modbus server. + * + * @param address The address of the value to read. + * @param functionCode The function code to use for the Modbus request. + * + * @return long The value read from the Modbus server. + */ long ModbusT1SServerClass::write(int address, int functionCode) { long res = -1; if(_server == nullptr) { @@ -359,7 +403,6 @@ long ModbusT1SServerClass::write(int address, int functionCode) { return res; } - /** * Sets the Arduino_10BASE_T1S_UDP server for communication. * @@ -371,20 +414,49 @@ void ModbusT1SServerClass::setT1SServer(Arduino_10BASE_T1S_UDP & server) { _server = &server; } - +/** + * Sets the port to use for communication. + * + * This function sets the port to use for communication. + * + * @param port The port to use. + */ void ModbusT1SServerClass::setT1SPort(int port) { udp_port = port; } + +/** + * Sets the baud rate to use for communication. + * + * This function sets the baud rate to use for communication. + * + * @param baudrate The baud rate to use. + */ void ModbusT1SServerClass::setBaudrate(int baudrate) { _baudrate = baudrate; } +/** + * Sets the callback function to use for PLCA status check. + * + * This function sets the callback function to use for PLCA status check. + * + * @param cb The callback function to use. + */ void ModbusT1SServerClass::setCallback(callback_f cb) { if(cb != nullptr) { callback = cb; } } +/** + * Default callback function for PLCA status check. + * + * This function is the default callback function for PLCA status check. + * + * @param success The success status of the PLCA status check. + * @param plcaStatus The PLCA status. + */ static void default_OnPlcaStatus(bool success, bool plcaStatus) { if (!success) @@ -397,16 +469,33 @@ static void default_OnPlcaStatus(bool success, bool plcaStatus) } } +/** + * Enables Power Over Ethernet (POE). + * + * This function enables Power Over Ethernet (POE) setting the device as power source. + */ void ModbusT1SServerClass::enablePOE() { tc6_inst->digitalWrite(TC6::DIO::A0, true); tc6_inst->digitalWrite(TC6::DIO::A1, true); } +/** + * Disables Power Over Ethernet (POE). + * + * This function disables Power Over Ethernet (POE) and set the USB as power source. + */ void ModbusT1SServerClass::disablePOE() { tc6_inst->digitalWrite(TC6::DIO::A0, false); tc6_inst->digitalWrite(TC6::DIO::A1, true); } +/** + * Sets the gateway IP address. + * + * This function sets the gateway IP address. + * + * @param ip The gateway IP address. + */ void ModbusT1SServerClass::setGatwayIP(IPAddress ip) { _gateway = ip; } diff --git a/src/ModbusT1SServer.h b/src/ModbusT1SServer.h index 628df61..8876d1e 100644 --- a/src/ModbusT1SServer.h +++ b/src/ModbusT1SServer.h @@ -47,14 +47,16 @@ class ModbusT1SServerClass : public ModbusServer { /** * Start the Modbus T1S server with the specified parameters * - * @param id (slave) id of the server + * @param node_id id of the server * @param baudrate Baud rate to use * @param config serial config. to use defaults to SERIAL_8N1 + * @param rs485 RS485 object to use * * Return 1 on success, 0 on failure */ int begin(int node_id, unsigned long baudrate, uint16_t config = SERIAL_8N1, RS485Class& rs485 = RS485); + /** * Reads a coil from the Modbus server. * @@ -73,10 +75,9 @@ class ModbusT1SServerClass : public ModbusServer { * on the Modbus server using the provided UDP client. * * @param address The address of the coil to write to. - * @param value The value to write to the coil (1 for ON, 0 for OFF). * @return int Returns 1 if the write operation is successful, 0 otherwise. */ - int coilWrite(int address = -1, uint8_t value = 255); + int coilWrite(int address = -1); /** * Reads the status of a discrete input from the Modbus server. @@ -107,7 +108,6 @@ class ModbusT1SServerClass : public ModbusServer { * on the Modbus server using the provided UDP client. * * @param address The address of the holding register to write to. - * @param value The value to write to the holding register. * @return int 1 if the write operation is successful, -1 if an error occurs. */ long holdingRegisterRead(int address = -1); @@ -119,7 +119,6 @@ class ModbusT1SServerClass : public ModbusServer { * on the Modbus server using the provided UDP client. * * @param address The address of the holding register to write to. - * @param value The value to write to the holding register. * @return int 1 if the write operation is successful, -1 if an error occurs. */ int holdingRegisterWrite(int address = -1); @@ -140,26 +139,90 @@ class ModbusT1SServerClass : public ModbusServer { */ void setT1SServer(Arduino_10BASE_T1S_UDP & server); + /** + * Set the port to use for communication + * + * @param port The port to use + */ void setT1SPort(int port = 8889); + /** + * Update the Modbus server. + * + * This function updates the Modbus server. + */ void update(); + /** + * Set the baudrate to use for communication + * + * @param baudrate The baudrate to use + */ void setBaudrate(int baudrate); +/** + * Sets the gateway IP address. + * + * This function sets the gateway IP address. + * + * @param ip The gateway IP address. + */ + void setGatwayIP(IPAddress ip); /** - * Poll interface for requests + * Poll the Modbus server. + * + * This function polls the Modbus server. + * + * @return int The status of the poll. */ - void setGatwayIP(IPAddress ip); virtual int poll(); + + /** + * Checks the PLCA status and Services the hardware and the protocol stack. + * + * This function checks the PLCA status and services the hardware and the + * protocol stack. + * + */ void checkStatus(); + + /** + * Set the callback function to use for PLCA status check. + * + * This function sets the callback function to use for PLCA status check. + * + * @param cb The callback function to use. + */ void setCallback(callback_f cb = nullptr); - long read(int address, int functionCode); - long write(int address, int functionCode); + + /** + * Enables Power Over Ethernet (POE). + * + * This function enables Power Over Ethernet (POE) setting the device as power source. + */ void enablePOE(); + + /** + * Disables Power Over Ethernet (POE). + * + * This function disables Power Over Ethernet (POE) and set the USB as power source. + */ void disablePOE(); + + /** + * Set the Modbus RTU client timeout. + * + * This function sets the Modbus RTU client timeout. + * + * @param timeout The timeout value in milliseconds. + */ void setTimeout(unsigned long timeout); +private: + long read(int address, int functionCode); + long write(int address, int functionCode); + private: IPAddress _gateway = IPAddress(0, 0, 0, 0); uint8_t udp_rx_buf[8] = {0}; From e0b10e1b3b34902d268865a553f31fb8af690009 Mon Sep 17 00:00:00 2001 From: Rocketct Date: Mon, 18 Nov 2024 10:12:47 +0100 Subject: [PATCH 17/17] Added keywords to highligths t1s apis --- keywords.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/keywords.txt b/keywords.txt index 108120e..0fecedd 100644 --- a/keywords.txt +++ b/keywords.txt @@ -46,6 +46,21 @@ configureInputRegisters KEYWORD2 discreteInputWrite KEYWORD2 inputRegisterWrite KEYWORD2 +setServerIp KEYWORD2 +setServerPort KEYWORD2 +setModbusId KEYWORD2 +setT1SServer KEYWORD2 +setT1SClient KEYWORD2 +setGateway KEYWORD2 +setRxTimeout KEYWORD2 +setT1SPort KEYWORD2 +update KEYWORD2 +setCallback KEYWORD2 +enablePOE KEYWORD2 +disablePOE KEYWORD2 +checkStatus KEYWORD2 +parsePacket KEYWORD2 + ####################################### # Constants (LITERAL1) #######################################