/* 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 */ #include #include "ModbusClient.h" ModbusClient::ModbusClient(unsigned long defaultTimeout) : _mb(NULL), _timeout(defaultTimeout), _defaultId(0x00), _transmissionBegun(false), _values(NULL), _available(0), _read(0), _availableForWrite(0), _written(0) { } ModbusClient::~ModbusClient() { if (_values != NULL) { free(_values); } if (_mb != NULL) { modbus_free(_mb); } } int ModbusClient::begin(modbus_t* mb, int defaultId) { end(); _mb = mb; _defaultId = defaultId; if (_mb == NULL) { return 0; } if (modbus_connect(_mb) != 0) { modbus_free(_mb); _mb = NULL; return 0; } _transmissionBegun = false; _available = 0; _read = 0; _availableForWrite = 0; _written = 0; modbus_set_error_recovery(_mb, MODBUS_ERROR_RECOVERY_PROTOCOL); setTimeout(_timeout); return 1; } void ModbusClient::end() { if (_values != NULL) { free(_values); _values = NULL; } if (_mb != NULL) { modbus_close(_mb); modbus_free(_mb); _mb = NULL; } } int ModbusClient::coilRead(int address) { return coilRead(_defaultId, address); } int ModbusClient::coilRead(int id, int address) { uint8_t value; modbus_set_slave(_mb, id); if (modbus_read_bits(_mb, address, 1, &value) < 0) { return -1; } return value; } int ModbusClient::discreteInputRead(int address) { return discreteInputRead(_defaultId, address); } int ModbusClient::discreteInputRead(int id, int address) { uint8_t value; modbus_set_slave(_mb, id); if (modbus_read_input_bits(_mb, address, 1, &value) < 0) { return -1; } return value; } long ModbusClient::holdingRegisterRead(int address) { return holdingRegisterRead(_defaultId, address); } long ModbusClient::holdingRegisterRead(int id, int address) { uint16_t value; modbus_set_slave(_mb, id); if (modbus_read_registers(_mb, address, 1, &value) < 0) { return -1; } return value; } long ModbusClient::inputRegisterRead(int address) { return inputRegisterRead(_defaultId, address); } long ModbusClient::inputRegisterRead(int id, int address) { uint16_t value; modbus_set_slave(_mb, id); if (modbus_read_input_registers(_mb, address, 1, &value) < 0) { return -1; } return value; } int ModbusClient::coilWrite(int address, uint8_t value) { return coilWrite(_defaultId, address, value); } int ModbusClient::coilWrite(int id, int address, uint8_t value) { modbus_set_slave(_mb, id); if (modbus_write_bit(_mb, address, value) < 0) { return 0; } return 1; } int ModbusClient::holdingRegisterWrite(int address, uint16_t value) { return holdingRegisterWrite(_defaultId, address, value); } int ModbusClient::holdingRegisterWrite(int id, int address, uint16_t value) { modbus_set_slave(_mb, id); if (modbus_write_register(_mb, address, value) < 0) { return 0; } return 1; } int ModbusClient::registerMaskWrite(int address, uint16_t andMask, uint16_t orMask) { return registerMaskWrite(_defaultId, address, andMask, orMask); } int ModbusClient::registerMaskWrite(int id, int address, uint16_t andMask, uint16_t orMask) { modbus_set_slave(_mb, id); if (modbus_mask_write_register(_mb, address, andMask, orMask) < 0) { return 0; } return 1; } int ModbusClient::beginTransmission(int type, int address, int nb) { return beginTransmission(_defaultId, type, address, nb); } int ModbusClient::beginTransmission(int id, int type, int address, int nb) { if ((type != COILS && type != HOLDING_REGISTERS) || nb < 1) { errno = EINVAL; return 0; } int valueSize = (type == COILS) ? sizeof(uint8_t) : sizeof(uint16_t); _values = realloc(_values, nb * valueSize); if (_values == NULL) { errno = ENOMEM; return 0; } memset(_values, 0x00, nb * valueSize); _transmissionBegun = true; _id = id; _type = type; _address = address; _nb = nb; _available = 0; _read = 0; _availableForWrite = nb; _written = 0; return 1; } int ModbusClient::write(unsigned int value) { if (!_transmissionBegun || _availableForWrite <= 0) { return 0; } switch (_type) { case COILS: ((uint8_t*)_values)[_written++] = value; _availableForWrite--; return 1; case HOLDING_REGISTERS: ((uint16_t*)_values)[_written++] = value; _availableForWrite--; return 1; default: return 0; } return 1; } int ModbusClient::endTransmission() { if (!_transmissionBegun) { return 0; } int result = -1; modbus_set_slave(_mb, _id); switch (_type) { case COILS: result = modbus_write_bits(_mb, _address, _nb, (const uint8_t*)_values); break; case HOLDING_REGISTERS: result = modbus_write_registers(_mb, _address, _nb, (const uint16_t*)_values); break; default: return 0; } _transmissionBegun = false; _available = 0; _read = 0; _availableForWrite = 0; _written = 0; return (result < 0) ? 0 : 1; } int ModbusClient::requestFrom(int type, int address, int nb) { return requestFrom(_defaultId, type, address, nb); } int ModbusClient::requestFrom(int id, int type, int address, int nb) { if ((type != COILS && type != DISCRETE_INPUTS && type != HOLDING_REGISTERS && type != INPUT_REGISTERS) || (nb < 1)) { errno = EINVAL; return 0; } int valueSize = (type == COILS || type == DISCRETE_INPUTS) ? sizeof(uint8_t) : sizeof(uint16_t); _values = realloc(_values, nb * valueSize); if (_values == NULL) { errno = ENOMEM; return 0; } int result = -1; modbus_set_slave(_mb, id); switch (type) { case COILS: result = modbus_read_bits(_mb, address, nb, (uint8_t*)_values); break; case DISCRETE_INPUTS: result = modbus_read_input_bits(_mb, address, nb, (uint8_t*)_values); break; case HOLDING_REGISTERS: result = modbus_read_registers(_mb, address, nb, (uint16_t*)_values); break; case INPUT_REGISTERS: result = modbus_read_input_registers(_mb, address, nb, (uint16_t*)_values); break; default: break; } if (result == -1) { return 0; } _transmissionBegun = false; _type = type; _available = nb; _read = 0; _availableForWrite = 0; _written = 0; return nb; } int ModbusClient::available() { return _available; } long ModbusClient::read() { if (_available <= 0) { return -1; } long result = -1; switch (_type) { case COILS: case DISCRETE_INPUTS: result = ((uint8_t*)_values)[_read]; break; case HOLDING_REGISTERS: case INPUT_REGISTERS: result = ((uint16_t*)_values)[_read]; break; default: break; } if (result != -1) { _available--; _read++; } return result; } const char* ModbusClient::lastError() { if (errno == 0) { return NULL; } return modbus_strerror(errno); } void ModbusClient::setTimeout(unsigned long ms) { _timeout = ms; if (_mb) { modbus_set_response_timeout(_mb, _timeout / 1000, (_timeout % 1000) * 1000); } }