
#ifndef ModbusEC_h
#define ModbusEC_h

#define __MODBUSMASTER_DEBUG__ (0)
#define __MODBUSMASTER_DEBUG_PIN_A__ 4
#define __MODBUSMASTER_DEBUG_PIN_B__ 5

#include "Arduino.h"
#include "util/crc16.h"
#include "util/word.h"

/* _____CLASS DEFINITIONS____________________________________________________ */
/**
Arduino class library for communicating with Modbus slaves over
RS232/485 (via RTU protocol).
*/
class ModbusMaster
{
public:
  ModbusMaster();

  void begin(uint8_t, Stream *serial);
  void idle(void (*)());
  void preTransmission(void (*)());
  void postTransmission(void (*)());

  static const uint8_t ku8MBIllegalFunction = 0x01;
  static const uint8_t ku8MBIllegalDataAddress = 0x02;
  static const uint8_t ku8MBIllegalDataValue = 0x03;
  static const uint8_t ku8MBSlaveDeviceFailure = 0x04;
  static const uint8_t ku8MBSuccess = 0x00;
  static const uint8_t ku8MBInvalidSlaveID = 0xE0;
  static const uint8_t ku8MBInvalidFunction = 0xE1;
  static const uint8_t ku8MBResponseTimedOut = 0xE2;
  static const uint8_t ku8MBInvalidCRC = 0xE3;

  uint16_t getResponseBuffer(uint8_t);
  void clearResponseBuffer();
  uint8_t setTransmitBuffer(uint8_t, uint16_t);
  void clearTransmitBuffer();

  void beginTransmission(uint16_t);
  uint8_t requestFrom(uint16_t, uint16_t);
  void sendBit(bool);
  void send(uint8_t);
  void send(uint16_t);
  void send(uint32_t);
  uint8_t available(void);
  uint16_t receive(void);

  uint8_t readHoldingRegisters(uint16_t, uint16_t);
  uint8_t writeMultipleRegisters(uint16_t, uint16_t);
  uint8_t writeMultipleRegisters();
  uint8_t  writeSingleRegister(uint16_t, uint16_t);
  uint8_t readAddresEctoControl();
  uint8_t writeAddresEctoControl(uint8_t);

private:
  Stream *_serial;                               ///< reference to serial port object
  uint8_t _u8MBSlave;                            ///< Modbus slave (1..255) initialized in begin()
  static const uint8_t ku8MaxBufferSize = 20;    ///< size of response/transmit buffers
  uint16_t _u16ReadAddress;                      ///< slave register from which to read
  uint16_t _u16ReadQty;                          ///< quantity of words to read
  uint16_t _u16ResponseBuffer[ku8MaxBufferSize]; ///< buffer to store Modbus slave response; read via GetResponseBuffer()
  uint16_t _u16WriteAddress;                     ///< slave register to which to write
  uint16_t _u16WriteQty;                         ///< quantity of words to write
  uint16_t _u16TransmitBuffer[ku8MaxBufferSize]; ///< buffer containing data to transmit to Modbus slave; set via SetTransmitBuffer()
  uint16_t *txBuffer;                            // from Wire.h -- need to clean this up Rx
  uint8_t _u8TransmitBufferIndex;
  uint16_t u16TransmitBufferLength;
  uint16_t *rxBuffer; // from Wire.h -- need to clean this up Rx
  uint8_t _u8ResponseBufferIndex;
  uint8_t _u8ResponseBufferLength;

  // Modbus function codes for 16 bit access
  static const uint8_t ku8MBReadHoldingRegisters = 0x03;   ///< Modbus function 0x03 Read Holding Registers
  static const uint8_t ku8MBWriteMultipleRegisters = 0x10; ///< Modbus function 0x10 Write Multiple Registers
  static const uint8_t ku8MBWriteSingleRegister        = 0x06; ///< Modbus function 0x06 Write Single Register
  static const uint8_t ku8MBProgRead46 = 0x46;  ///< EctoControl function 0x46 Устройство возвращает в ответе свой текущий адрес ADDR
  static const uint8_t ku8MBProgWrite47 = 0x47; ///< EctoControl function 0x47 высылается ведущим устройством ведомому с указанием сменить свой имеющийся адрес на заданный
                                                // высылается ведущим устройством единственному устройству на шине с неизвестным адресом

  // Modbus timeout [milliseconds]
  static const uint16_t ku16MBResponseTimeout = 500; ///< Modbus timeout [milliseconds]

  // master function that conducts Modbus transactions
  uint8_t ModbusMasterTransaction(uint8_t u8MBFunction);

  // idle callback function; gets called during idle time between TX and RX
  void (*_idle)();
  // preTransmission callback function; gets called before writing a Modbus message
  void (*_preTransmission)();
  // postTransmission callback function; gets called after a Modbus message has been sent
  void (*_postTransmission)();
};
#endif
