Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 9 of 9

Thread: Modbus RTU

  1. #1
    Junior Member
    Join Date
    Aug 2016
    Posts
    12

    Modbus RTU

    Hi, i am trying to setup Teensy as ModbusRTU Slave. Using >this< library i can set up registers up to 250?.. But if i want to set up a higher range of registers, for example register 300 teensy 3.1 freezes. I have to push the reset button to flash it again.. Somone knows a fix or has an other (working) Library?

  2. #2
    Senior Member Epyon's Avatar
    Join Date
    Apr 2013
    Location
    Belgium
    Posts
    425
    Looks like a buffer overflow of some sorts?

    I have tested a lot of Modbus libraries and found that this one performs the most satisfactory. To get it to work on Teensy you just need to comment out some AVR registers in the ModbusRTU.h file.

  3. #3
    Junior Member
    Join Date
    Aug 2016
    Posts
    12
    Hi thanks, i have tried the example but it doesent compile...

    Code:
    #include <ModbusRtu.h>
    uint16_t au16data[16] = {
      3, 1415, 9265, 4, 2, 7182, 28182, 8, 0, 0, 0, 0, 0, 0, 1, 4 };
    Modbus slave(1,1,0); 
    void setup() {
      slave.begin( 115200, SERIAL_8N1 ); 
    }
    void loop() {
      slave.poll( au16data, 16 );
    }
    It says:
    Code:
    It says
    
    In file included from C:\Users\o5i\Desktop\sketch_sep13a\sketch_sep13a.ino:10:0:
    
    C:\Users\o5i\Documents\Arduino\libraries\Modbus-Master-Slave-for-Arduino-master/ModbusRtu.h: In member function 'void Modbus::begin(long int)':
    
    C:\Users\o5i\Documents\Arduino\libraries\Modbus-Master-Slave-for-Arduino-master/ModbusRtu.h:316:14: error: cannot convert 'usb_serial_class*' to 'HardwareSerial*' in assignment
    
             port = &Serial;
    
                  ^
    
    C:\Users\o5i\Documents\Arduino\libraries\Modbus-Master-Slave-for-Arduino-master/ModbusRtu.h: In member function 'void Modbus::begin(long int, uint8_t)':
    
    C:\Users\o5i\Documents\Arduino\libraries\Modbus-Master-Slave-for-Arduino-master/ModbusRtu.h:399:14: error: cannot convert 'usb_serial_class*' to 'HardwareSerial*' in assignment
    
             port = &Serial;
    
                  ^
    
    C:\Users\o5i\Documents\Arduino\libraries\Modbus-Master-Slave-for-Arduino-master/ModbusRtu.h: In member function 'void Modbus::sendTxBuffer()':
    
    C:\Users\o5i\Documents\Arduino\libraries\Modbus-Master-Slave-for-Arduino-master/ModbusRtu.h:924:13: error: 'UCSR0A' was not declared in this scope
    
                 UCSR0A=UCSR0A |(1 << TXC0);
    
                 ^
    
    C:\Users\o5i\Documents\Arduino\libraries\Modbus-Master-Slave-for-Arduino-master/ModbusRtu.h:924:34: error: 'TXC0' was not declared in this scope
    
                 UCSR0A=UCSR0A |(1 << TXC0);
    
                                      ^
    
    C:\Users\o5i\Documents\Arduino\libraries\Modbus-Master-Slave-for-Arduino-master/ModbusRtu.h:960:22: error: 'UCSR0A' was not declared in this scope
    
                 while (!(UCSR0A & (1 << TXC0)));
    
                          ^
    
    C:\Users\o5i\Documents\Arduino\libraries\Modbus-Master-Slave-for-Arduino-master/ModbusRtu.h:960:37: error: 'TXC0' was not declared in this scope
    
                 while (!(UCSR0A & (1 << TXC0)));
    
                                         ^
    
    C:\Users\o5i\Documents\Arduino\libraries\Modbus-Master-Slave-for-Arduino-master/ModbusRtu.h:891:13: warning: unused variable 'i' [-Wunused-variable]
    
         uint8_t i = 0;
    
                 ^
    
    C:\Users\o5i\Documents\Arduino\libraries\Modbus-Master-Slave-for-Arduino-master/ModbusRtu.h: In member function 'void Modbus::get_FC1()':
    
    C:\Users\o5i\Documents\Arduino\libraries\Modbus-Master-Slave-for-Arduino-master/ModbusRtu.h:1152:21: warning: unused variable 'i' [-Wunused-variable]
    
         uint8_t u8byte, i;
    
                         ^
    
    C:\Users\o5i\Documents\Arduino\libraries\Modbus-Master-Slave-for-Arduino-master/ModbusRtu.h: In member function 'int8_t Modbus::process_FC16(uint16_t*, uint8_t)':
    
    C:\Users\o5i\Documents\Arduino\libraries\Modbus-Master-Slave-for-Arduino-master/ModbusRtu.h:1393:13: warning: unused variable 'u8func' [-Wunused-variable]
    
         uint8_t u8func = au8Buffer[ FUNC ];  // get the original FUNC code
    
                 ^
    
    Bibliothek Modbus-Master-Slave-for-Arduino-master im Ordner: C:\Users\o5i\Documents\Arduino\libraries\Modbus-Master-Slave-for-Arduino-master (legacy) wird verwendet
    Bibliothek SoftwareSerial in Version 1.0 im Ordner: C:\Users\o5i\Desktop\Micros\arduino-1.6.9\hardware\teensy\avr\libraries\SoftwareSerial  wird verwendet
    Fehler beim Kompilieren für das Board Teensy 3.2 / 3.1.

    I use teensy 3.1, it looks like the lib doesent like the serial?

    Can you send me the modified file?
    Last edited by o5i; 09-13-2016 at 09:10 PM.

  4. #4
    Senior Member Epyon's Avatar
    Join Date
    Apr 2013
    Location
    Belgium
    Posts
    425
    Like I said, you need to change some AVR registers to equivalent Teensy counterparts. Look for void Modbus::begin at line #254 and replace it by this:

    Code:
    void Modbus::begin(long u32speed) {
    
      switch( u8serno ) {
      case 1:
        port = &Serial1;
        port->begin(u32speed);
        break;
      case 2:
        port = &Serial2;
        port->begin(u32speed);
        break;
      case 3:
        port = &Serial3;
        port->begin(u32speed);
        break;
      case 0:
      default:
        port = &Serial1;
        break;
      }
      //port->begin(u32speed);
    
      if (u8txenpin > 1) { // pin 0 & pin 1 are reserved for RX/TX
        // return RS485 transceiver to transmit mode
        pinMode(u8txenpin, OUTPUT);
        digitalWrite(u8txenpin, LOW);
      }
    
      port->flush();
      u8lastRec = u8BufferSize = 0;
      u16InCnt = u16OutCnt = u16errCnt = 0;
    }
    And the same a bit lower for
    Code:
    void Modbus::begin(long u32speed,uint8_t u8config) {
    
      switch( u8serno ) {
      case 1:
        port = &Serial1;
        port->begin(u32speed, u8config);
        break;
      case 2:
        port = &Serial2;
        port->begin(u32speed, u8config);
        break;
      case 3:
        port = &Serial2;
        port->begin(u32speed, u8config);
        break;
      case 0:
      default:
        port = &Serial1;
        break;
      }
    
      //port->begin(u32speed, u8config);
      if (u8txenpin > 1) { // pin 0 & pin 1 are reserved for RX/TX
        // return RS485 transceiver to transmit mode
        pinMode(u8txenpin, OUTPUT);
        digitalWrite(u8txenpin, LOW);
      }
    
      port->flush();
      u8lastRec = u8BufferSize = 0;
      u16InCnt = u16OutCnt = u16errCnt = 0;
    }
    I was planning to write some examples on my blog, but haven't got around to it.

  5. #5
    Junior Member
    Join Date
    Aug 2016
    Posts
    12
    Hi, the file looks litlebit different, probably there was an update?

    Code:
    /**
     * @file 	ModbusRtu.h
     * @version     1.21
     * @date        2016.02.21
     * @author 	Samuel Marco i Armengol
     * @contact     sammarcoarmengol@gmail.com
     * @contribution Helium6072
     *
     * @description
     *  Arduino library for communicating with Modbus devices
     *  over RS232/USB/485 via RTU protocol.
     *
     *  Further information:
     *  http://modbus.org/
     *  http://modbus.org/docs/Modbus_over_serial_line_V1_02.pdf
     *
     * @license
     *  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; version
     *  2.1 of the License.
     *
     *  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 St, Fifth Floor, Boston, MA  02110-1301  USA
     *
     * @defgroup setup Modbus Object Instantiation/Initialization
     * @defgroup loop Modbus Object Management
     * @defgroup buffer Modbus Buffer Management
     * @defgroup discrete Modbus Function Codes for Discrete Coils/Inputs
     * @defgroup register Modbus Function Codes for Holding/Input Registers
     *
     */
    
    #include <inttypes.h>
    #include "Arduino.h"
    #include "Print.h"
    #include <SoftwareSerial.h>
    
    /**
     * @struct modbus_t
     * @brief
     * Master query structure:
     * This includes all the necessary fields to make the Master generate a Modbus query.
     * A Master may keep several of these structures and send them cyclically or
     * use them according to program needs.
     */
    typedef struct
    {
        uint8_t u8id;          /*!< Slave address between 1 and 247. 0 means broadcast */
        uint8_t u8fct;         /*!< Function code: 1, 2, 3, 4, 5, 6, 15 or 16 */
        uint16_t u16RegAdd;    /*!< Address of the first register to access at slave/s */
        uint16_t u16CoilsNo;   /*!< Number of coils or registers to access */
        uint16_t *au16reg;     /*!< Pointer to memory image in master */
    }
    modbus_t;
    
    enum
    {
        RESPONSE_SIZE = 6,
        EXCEPTION_SIZE = 3,
        CHECKSUM_SIZE = 2
    };
    
    /**
     * @enum MESSAGE
     * @brief
     * Indexes to telegram frame positions
     */
    enum MESSAGE
    {
        ID                             = 0, //!< ID field
        FUNC, //!< Function code position
        ADD_HI, //!< Address high byte
        ADD_LO, //!< Address low byte
        NB_HI, //!< Number of coils or registers high byte
        NB_LO, //!< Number of coils or registers low byte
        BYTE_CNT  //!< byte counter
    };
    
    /**
     * @enum MB_FC
     * @brief
     * Modbus function codes summary.
     * These are the implement function codes either for Master or for Slave.
     *
     * @see also fctsupported
     * @see also modbus_t
     */
    enum MB_FC
    {
        MB_FC_NONE                     = 0,   /*!< null operator */
        MB_FC_READ_COILS               = 1,	/*!< FCT=1 -> read coils or digital outputs */
        MB_FC_READ_DISCRETE_INPUT      = 2,	/*!< FCT=2 -> read digital inputs */
        MB_FC_READ_REGISTERS           = 3,	/*!< FCT=3 -> read registers or analog outputs */
        MB_FC_READ_INPUT_REGISTER      = 4,	/*!< FCT=4 -> read analog inputs */
        MB_FC_WRITE_COIL               = 5,	/*!< FCT=5 -> write single coil or output */
        MB_FC_WRITE_REGISTER           = 6,	/*!< FCT=6 -> write single register */
        MB_FC_WRITE_MULTIPLE_COILS     = 15,	/*!< FCT=15 -> write multiple coils or outputs */
        MB_FC_WRITE_MULTIPLE_REGISTERS = 16	/*!< FCT=16 -> write multiple registers */
    };
    
    enum COM_STATES
    {
        COM_IDLE                     = 0,
        COM_WAITING                  = 1
    
    };
    
    enum ERR_LIST
    {
        ERR_NOT_MASTER                = -1,
        ERR_POLLING                   = -2,
        ERR_BUFF_OVERFLOW             = -3,
        ERR_BAD_CRC                   = -4,
        ERR_EXCEPTION                 = -5
    };
    
    enum
    {
        NO_REPLY = 255,
        EXC_FUNC_CODE = 1,
        EXC_ADDR_RANGE = 2,
        EXC_REGS_QUANT = 3,
        EXC_EXECUTE = 4
    };
    
    const unsigned char fctsupported[] =
    {
        MB_FC_READ_COILS,
        MB_FC_READ_DISCRETE_INPUT,
        MB_FC_READ_REGISTERS,
        MB_FC_READ_INPUT_REGISTER,
        MB_FC_WRITE_COIL,
        MB_FC_WRITE_REGISTER,
        MB_FC_WRITE_MULTIPLE_COILS,
        MB_FC_WRITE_MULTIPLE_REGISTERS
    };
    
    #define T35  5
    #define  MAX_BUFFER  64	//!< maximum size for the communication buffer in bytes
    
    /**
     * @class Modbus
     * @brief
     * Arduino class library for communicating with Modbus devices over
     * USB/RS232/485 (via RTU protocol).
     */
    class Modbus
    {
    private:
        HardwareSerial *port; //!< Pointer to Serial class object
        SoftwareSerial *softPort; //!< Pointer to SoftwareSerial class object
        uint8_t u8id; //!< 0=master, 1..247=slave number
        uint8_t u8serno; //!< serial port: 0-Serial, 1..3-Serial1..Serial3; 4: use software serial
        uint8_t u8txenpin; //!< flow control pin: 0=USB or RS-232 mode, >0=RS-485 mode
        uint8_t u8state;
        uint8_t u8lastError;
        uint8_t au8Buffer[MAX_BUFFER];
        uint8_t u8BufferSize;
        uint8_t u8lastRec;
        uint16_t *au16regs;
        uint16_t u16InCnt, u16OutCnt, u16errCnt;
        uint16_t u16timeOut;
        uint32_t u32time, u32timeOut;
        uint8_t u8regsize;
    
        void init(uint8_t u8id, uint8_t u8serno, uint8_t u8txenpin);
    	void init(uint8_t u8id);
        void sendTxBuffer();
        int8_t getRxBuffer();
        uint16_t calcCRC(uint8_t u8length);
        uint8_t validateAnswer();
        uint8_t validateRequest();
        void get_FC1();
        void get_FC3();
        int8_t process_FC1( uint16_t *regs, uint8_t u8size );
        int8_t process_FC3( uint16_t *regs, uint8_t u8size );
        int8_t process_FC5( uint16_t *regs, uint8_t u8size );
        int8_t process_FC6( uint16_t *regs, uint8_t u8size );
        int8_t process_FC15( uint16_t *regs, uint8_t u8size );
        int8_t process_FC16( uint16_t *regs, uint8_t u8size );
        void buildException( uint8_t u8exception ); // build exception message
    
    public:
        Modbus();
        Modbus(uint8_t u8id, uint8_t u8serno);
        Modbus(uint8_t u8id, uint8_t u8serno, uint8_t u8txenpin);
        Modbus(uint8_t u8id);
        void begin(long u32speed);
        void begin(SoftwareSerial *sPort, long u32speed);
        void begin(long u32speed, uint8_t u8config);
        void begin();
        void setTimeOut( uint16_t u16timeout); //!<write communication watch-dog timer
        uint16_t getTimeOut(); //!<get communication watch-dog timer value
        boolean getTimeOutState(); //!<get communication watch-dog timer state
        int8_t query( modbus_t telegram ); //!<only for master
        int8_t poll(); //!<cyclic poll for master
        int8_t poll( uint16_t *regs, uint8_t u8size ); //!<cyclic poll for slave
        uint16_t getInCnt(); //!<number of incoming messages
        uint16_t getOutCnt(); //!<number of outcoming messages
        uint16_t getErrCnt(); //!<error counter
        uint8_t getID(); //!<get slave ID between 1 and 247
        uint8_t getState();
        uint8_t getLastError(); //!<get last error message
        void setID( uint8_t u8id ); //!<write new ID for the slave
        void end(); //!<finish any communication and release serial communication port
    };
    
    /* _____PUBLIC FUNCTIONS_____________________________________________________ */
    
    /**
     * @brief
     * Default Constructor for Master through Serial
     *
     * @ingroup setup
     */
    Modbus::Modbus()
    {
        init(0, 0, 0);
    }
    
    /**
     * @brief
     * Full constructor for a Master/Slave through USB/RS232C
     *
     * @param u8id   node address 0=master, 1..247=slave
     * @param u8serno  serial port used 0..3
     * @ingroup setup
     * @overload Modbus::Modbus(uint8_t u8id, uint8_t u8serno)
     * @overload Modbus::Modbus(uint8_t u8id)
     * @overload Modbus::Modbus()
     */
    Modbus::Modbus(uint8_t u8id, uint8_t u8serno)
    {
        init(u8id, u8serno, 0);
    }
    
    /**
     * @brief
     * Full constructor for a Master/Slave through USB/RS232C/RS485
     * It needs a pin for flow control only for RS485 mode
     *
     * @param u8id   node address 0=master, 1..247=slave
     * @param u8serno  serial port used 0..3
     * @param u8txenpin pin for txen RS-485 (=0 means USB/RS232C mode)
     * @ingroup setup
     * @overload Modbus::Modbus(uint8_t u8id, uint8_t u8serno, uint8_t u8txenpin)
     * @overload Modbus::Modbus(uint8_t u8id)
     * @overload Modbus::Modbus()
     */
    Modbus::Modbus(uint8_t u8id, uint8_t u8serno, uint8_t u8txenpin)
    {
        init(u8id, u8serno, u8txenpin);
    }
    
    /**
     * @brief
     * Constructor for a Master/Slave through USB/RS232C via software serial
     * This constructor only specifies u8id (node address) and should be only
     * used if you want to use software serial instead of hardware serial.
     * If you use this constructor you have to begin ModBus object by
     * using "void Modbus::begin(SoftwareSerial *softPort, long u32speed)".
     *
     * @param u8id   node address 0=master, 1..247=slave
     * @ingroup setup
     * @overload Modbus::Modbus(uint8_t u8id, uint8_t u8serno, uint8_t u8txenpin)
     * @overload Modbus::Modbus(uint8_t u8id, uint8_t u8serno)
     * @overload Modbus::Modbus()
     */
    Modbus::Modbus(uint8_t u8id)
    {
        init(u8id);
    }
    
    /**
     * @brief
     * Initialize class object.
     *
     * Sets up the serial port using specified baud rate.
     * Call once class has been instantiated, typically within setup().
     *
     * @see http://arduino.cc/en/Serial/Begin#.Uy4CJ6aKlHY
     * @param speed   baud rate, in standard increments (300..115200)
     * @ingroup setup
     */
    void Modbus::begin(long u32speed)
    {
    
        switch( u8serno )
        {
    #if defined(UBRR1H)
        case 1:
            port = &Serial1;
            break;
    #endif
    
    #if defined(UBRR2H)
        case 2:
            port = &Serial2;
            break;
    #endif
    
    #if defined(UBRR3H)
        case 3:
            port = &Serial3;
            break;
    #endif
        case 0:
        default:
            port = &Serial;
            break;
        }
    
        port->begin(u32speed);
        if (u8txenpin > 1)   // pin 0 & pin 1 are reserved for RX/TX
        {
            // return RS485 transceiver to transmit mode
            pinMode(u8txenpin, OUTPUT);
            digitalWrite(u8txenpin, LOW);
        }
    
        while(port->read() >= 0);
        u8lastRec = u8BufferSize = 0;
        u16InCnt = u16OutCnt = u16errCnt = 0;
    }
    
    /**
     * @brief
     * Initialize class object.
     *
     * Sets up the software serial port using specified baud rate and SoftwareSerial object.
     * Call once class has been instantiated, typically within setup().
     *
     * @param speed   *softPort, pointer to SoftwareSerial class object
     * @param speed   baud rate, in standard increments (300..115200)
     * @ingroup setup
     */
    void Modbus::begin(SoftwareSerial *sPort, long u32speed)
    {
    
        softPort=sPort;
    
        softPort->begin(u32speed);
    
        if (u8txenpin > 1)   // pin 0 & pin 1 are reserved for RX/TX
        {
            // return RS485 transceiver to transmit mode
            pinMode(u8txenpin, OUTPUT);
            digitalWrite(u8txenpin, LOW);
        }
    
        while(softPort->read() >= 0);
        u8lastRec = u8BufferSize = 0;
        u16InCnt = u16OutCnt = u16errCnt = 0;
    }
    
    /**
     * @brief
     * Initialize class object.
     *
     * Sets up the serial port using specified baud rate.
     * Call once class has been instantiated, typically within setup().
     *
     * @see http://arduino.cc/en/Serial/Begin#.Uy4CJ6aKlHY
     * @param speed   baud rate, in standard increments (300..115200)
     * @param config  data frame settings (data length, parity and stop bits)
     * @ingroup setup
     */
    void Modbus::begin(long u32speed,uint8_t u8config)
    {
    
        switch( u8serno )
        {
    #if defined(UBRR1H)
        case 1:
            port = &Serial1;
            break;
    #endif
    
    #if defined(UBRR2H)
        case 2:
            port = &Serial2;
            break;
    #endif
    
    #if defined(UBRR3H)
        case 3:
            port = &Serial3;
            break;
    #endif
        case 0:
        default:
            port = &Serial;
            break;
        }
    
        port->begin(u32speed, u8config);
        if (u8txenpin > 1)   // pin 0 & pin 1 are reserved for RX/TX
        {
            // return RS485 transceiver to transmit mode
            pinMode(u8txenpin, OUTPUT);
            digitalWrite(u8txenpin, LOW);
        }
    
        while(port->read() >= 0);
        u8lastRec = u8BufferSize = 0;
        u16InCnt = u16OutCnt = u16errCnt = 0;
    }
    
    /**
     * @brief
     * Initialize default class object.
     *
     * Sets up the serial port using 19200 baud.
     * Call once class has been instantiated, typically within setup().
     *
     * @overload Modbus::begin(uint16_t u16BaudRate)
     * @ingroup setup
     */
    void Modbus::begin()
    {
        begin(19200);
    }
    
    /**
     * @brief
     * Method to write a new slave ID address
     *
     * @param 	u8id	new slave address between 1 and 247
     * @ingroup setup
     */
    void Modbus::setID( uint8_t u8id)
    {
        if (( u8id != 0) && (u8id <= 247))
        {
            this->u8id = u8id;
        }
    }
    
    /**
     * @brief
     * Method to read current slave ID address
     *
     * @return u8id	current slave address between 1 and 247
     * @ingroup setup
     */
    uint8_t Modbus::getID()
    {
        return this->u8id;
    }
    
    /**
     * @brief
     * Initialize time-out parameter
     *
     * Call once class has been instantiated, typically within setup().
     * The time-out timer is reset each time that there is a successful communication
     * between Master and Slave. It works for both.
     *
     * @param time-out value (ms)
     * @ingroup setup
     */
    void Modbus::setTimeOut( uint16_t u16timeOut)
    {
        this->u16timeOut = u16timeOut;
    }
    
    /**
     * @brief
     * Return communication Watchdog state.
     * It could be usefull to reset outputs if the watchdog is fired.
     *
     * @return TRUE if millis() > u32timeOut
     * @ingroup loop
     */
    boolean Modbus::getTimeOutState()
    {
        return (millis() > u32timeOut);
    }
    
    /**
     * @brief
     * Get input messages counter value
     * This can be useful to diagnose communication
     *
     * @return input messages counter
     * @ingroup buffer
     */
    uint16_t Modbus::getInCnt()
    {
        return u16InCnt;
    }
    
    /**
     * @brief
     * Get transmitted messages counter value
     * This can be useful to diagnose communication
     *
     * @return transmitted messages counter
     * @ingroup buffer
     */
    uint16_t Modbus::getOutCnt()
    {
        return u16OutCnt;
    }
    
    /**
     * @brief
     * Get errors counter value
     * This can be useful to diagnose communication
     *
     * @return errors counter
     * @ingroup buffer
     */
    uint16_t Modbus::getErrCnt()
    {
        return u16errCnt;
    }
    
    /**
     * Get modbus master state
     *
     * @return = 0 IDLE, = 1 WAITING FOR ANSWER
     * @ingroup buffer
     */
    uint8_t Modbus::getState()
    {
        return u8state;
    }
    
    /**
     * Get the last error in the protocol processor
     *
     * @returnreturn   NO_REPLY = 255      Time-out
     * @return   EXC_FUNC_CODE = 1   Function code not available
     * @return   EXC_ADDR_RANGE = 2  Address beyond available space for Modbus registers
     * @return   EXC_REGS_QUANT = 3  Coils or registers number beyond the available space
     * @ingroup buffer
     */
    uint8_t Modbus::getLastError()
    {
        return u8lastError;
    }
    
    /**
     * @brief
     * *** Only Modbus Master ***
     * Generate a query to an slave with a modbus_t telegram structure
     * The Master must be in COM_IDLE mode. After it, its state would be COM_WAITING.
     * This method has to be called only in loop() section.
     *
     * @see modbus_t
     * @param modbus_t  modbus telegram structure (id, fct, ...)
     * @ingroup loop
     * @todo finish function 15
     */
    int8_t Modbus::query( modbus_t telegram )
    {
        uint8_t u8regsno, u8bytesno;
        if (u8id!=0) return -2;
        if (u8state != COM_IDLE) return -1;
    
        if ((telegram.u8id==0) || (telegram.u8id>247)) return -3;
    
        au16regs = telegram.au16reg;
    
        // telegram header
        au8Buffer[ ID ]         = telegram.u8id;
        au8Buffer[ FUNC ]       = telegram.u8fct;
        au8Buffer[ ADD_HI ]     = highByte(telegram.u16RegAdd );
        au8Buffer[ ADD_LO ]     = lowByte( telegram.u16RegAdd );
    
        switch( telegram.u8fct )
        {
        case MB_FC_READ_COILS:
        case MB_FC_READ_DISCRETE_INPUT:
        case MB_FC_READ_REGISTERS:
        case MB_FC_READ_INPUT_REGISTER:
            au8Buffer[ NB_HI ]      = highByte(telegram.u16CoilsNo );
            au8Buffer[ NB_LO ]      = lowByte( telegram.u16CoilsNo );
            u8BufferSize = 6;
            break;
        case MB_FC_WRITE_COIL:
            au8Buffer[ NB_HI ]      = ((au16regs[0] > 0) ? 0xff : 0);
            au8Buffer[ NB_LO ]      = 0;
            u8BufferSize = 6;
            break;
        case MB_FC_WRITE_REGISTER:
            au8Buffer[ NB_HI ]      = highByte(au16regs[0]);
            au8Buffer[ NB_LO ]      = lowByte(au16regs[0]);
            u8BufferSize = 6;
            break;
        case MB_FC_WRITE_MULTIPLE_COILS: // TODO: implement "sending coils"
            u8regsno = telegram.u16CoilsNo / 16;
            u8bytesno = u8regsno * 2;
            if ((telegram.u16CoilsNo % 16) != 0)
            {
                u8bytesno++;
                u8regsno++;
            }
    
            au8Buffer[ NB_HI ]      = highByte(telegram.u16CoilsNo );
            au8Buffer[ NB_LO ]      = lowByte( telegram.u16CoilsNo );
            au8Buffer[ NB_LO+1 ]    = u8bytesno;
            u8BufferSize = 7;
    
            u8regsno = u8bytesno = 0; // now auxiliary registers
            for (uint16_t i = 0; i < telegram.u16CoilsNo; i++)
            {
    
    
            }
            break;
    
        case MB_FC_WRITE_MULTIPLE_REGISTERS:
            au8Buffer[ NB_HI ]      = highByte(telegram.u16CoilsNo );
            au8Buffer[ NB_LO ]      = lowByte( telegram.u16CoilsNo );
            au8Buffer[ NB_LO+1 ]    = (uint8_t) ( telegram.u16CoilsNo * 2 );
            u8BufferSize = 7;
    
            for (uint16_t i=0; i< telegram.u16CoilsNo; i++)
            {
                au8Buffer[ u8BufferSize ] = highByte( au16regs[ i ] );
                u8BufferSize++;
                au8Buffer[ u8BufferSize ] = lowByte( au16regs[ i ] );
                u8BufferSize++;
            }
            break;
        }
    
        sendTxBuffer();
        u8state = COM_WAITING;
        return 0;
    }
    
    /**
     * @brief *** Only for Modbus Master ***
     * This method checks if there is any incoming answer if pending.
     * If there is no answer, it would change Master state to COM_IDLE.
     * This method must be called only at loop section.
     * Avoid any delay() function.
     *
     * Any incoming data would be redirected to au16regs pointer,
     * as defined in its modbus_t query telegram.
     *
     * @params	nothing
     * @return errors counter
     * @ingroup loop
     */
    int8_t Modbus::poll()
    {
        // check if there is any incoming frame
    	uint8_t u8current;
        if(u8serno<4)
            u8current = port->available();
        else
            u8current = softPort->available();
    
        if (millis() > u32timeOut)
        {
            u8state = COM_IDLE;
            u8lastError = NO_REPLY;
            u16errCnt++;
            return 0;
        }
    
        if (u8current == 0) return 0;
    
        // check T35 after frame end or still no frame end
        if (u8current != u8lastRec)
        {
            u8lastRec = u8current;
            u32time = millis() + T35;
            return 0;
        }
        if (millis() < u32time) return 0;
    
        // transfer Serial buffer frame to auBuffer
        u8lastRec = 0;
        int8_t i8state = getRxBuffer();
        if (i8state < 7)
        {
            u8state = COM_IDLE;
            u16errCnt++;
            return i8state;
        }
    
        // validate message: id, CRC, FCT, exception
        uint8_t u8exception = validateAnswer();
        if (u8exception != 0)
        {
            u8state = COM_IDLE;
            return u8exception;
        }
    
        // process answer
        switch( au8Buffer[ FUNC ] )
        {
        case MB_FC_READ_COILS:
        case MB_FC_READ_DISCRETE_INPUT:
            // call get_FC1 to transfer the incoming message to au16regs buffer
            get_FC1( );
            break;
        case MB_FC_READ_INPUT_REGISTER:
        case MB_FC_READ_REGISTERS :
            // call get_FC3 to transfer the incoming message to au16regs buffer
            get_FC3( );
            break;
        case MB_FC_WRITE_COIL:
        case MB_FC_WRITE_REGISTER :
        case MB_FC_WRITE_MULTIPLE_COILS:
        case MB_FC_WRITE_MULTIPLE_REGISTERS :
            // nothing to do
            break;
        default:
            break;
        }
        u8state = COM_IDLE;
        return u8BufferSize;
    }
    
    /**
     * @brief
     * *** Only for Modbus Slave ***
     * This method checks if there is any incoming query
     * Afterwards, it would shoot a validation routine plus a register query
     * Avoid any delay() function !!!!
     * After a successful frame between the Master and the Slave, the time-out timer is reset.
     *
     * @param *regs  register table for communication exchange
     * @param u8size  size of the register table
     * @return 0 if no query, 1..4 if communication error, >4 if correct query processed
     * @ingroup loop
     */
    int8_t Modbus::poll( uint16_t *regs, uint8_t u8size )
    {
    
        au16regs = regs;
        u8regsize = u8size;
    	uint8_t u8current;
    
    
        // check if there is any incoming frame
        if(u8serno<4)
            u8current = port->available();
        else
            u8current = softPort->available();
    
        if (u8current == 0) return 0;
    
        // check T35 after frame end or still no frame end
        if (u8current != u8lastRec)
        {
            u8lastRec = u8current;
            u32time = millis() + T35;
            return 0;
        }
        if (millis() < u32time) return 0;
    
        u8lastRec = 0;
        int8_t i8state = getRxBuffer();
        u8lastError = i8state;
        if (i8state < 7) return i8state;
    
        // check slave id
        if (au8Buffer[ ID ] != u8id) return 0;
    
        // validate message: CRC, FCT, address and size
        uint8_t u8exception = validateRequest();
        if (u8exception > 0)
        {
            if (u8exception != NO_REPLY)
            {
                buildException( u8exception );
                sendTxBuffer();
            }
            u8lastError = u8exception;
            return u8exception;
        }
    
        u32timeOut = millis() + long(u16timeOut);
        u8lastError = 0;
    
        // process message
        switch( au8Buffer[ FUNC ] )
        {
        case MB_FC_READ_COILS:
        case MB_FC_READ_DISCRETE_INPUT:
            return process_FC1( regs, u8size );
            break;
        case MB_FC_READ_INPUT_REGISTER:
        case MB_FC_READ_REGISTERS :
            return process_FC3( regs, u8size );
            break;
        case MB_FC_WRITE_COIL:
            return process_FC5( regs, u8size );
            break;
        case MB_FC_WRITE_REGISTER :
            return process_FC6( regs, u8size );
            break;
        case MB_FC_WRITE_MULTIPLE_COILS:
            return process_FC15( regs, u8size );
            break;
        case MB_FC_WRITE_MULTIPLE_REGISTERS :
            return process_FC16( regs, u8size );
            break;
        default:
            break;
        }
        return i8state;
    }
    
    /* _____PRIVATE FUNCTIONS_____________________________________________________ */
    
    void Modbus::init(uint8_t u8id, uint8_t u8serno, uint8_t u8txenpin)
    {
        this->u8id = u8id;
        this->u8serno = (u8serno > 3) ? 0 : u8serno;
        this->u8txenpin = u8txenpin;
        this->u16timeOut = 1000;
    }
    
    void Modbus::init(uint8_t u8id)
    {
        this->u8id = u8id;
        this->u8serno = 4;
        this->u8txenpin = 0;
        this->u16timeOut = 1000;
    }
    
    /**
     * @brief
     * This method moves Serial buffer data to the Modbus au8Buffer.
     *
     * @return buffer size if OK, ERR_BUFF_OVERFLOW if u8BufferSize >= MAX_BUFFER
     * @ingroup buffer
     */
    int8_t Modbus::getRxBuffer()
    {
        boolean bBuffOverflow = false;
    
        if (u8txenpin > 1) digitalWrite( u8txenpin, LOW );
    
        u8BufferSize = 0;
        if(u8serno<4)
            while ( port->available() )
            {
                au8Buffer[ u8BufferSize ] = port->read();
                u8BufferSize ++;
    
                if (u8BufferSize >= MAX_BUFFER) bBuffOverflow = true;
            }
        else
            while ( softPort->available() )
            {
                au8Buffer[ u8BufferSize ] = softPort->read();
                u8BufferSize ++;
    
                if (u8BufferSize >= MAX_BUFFER) bBuffOverflow = true;
            }
        u16InCnt++;
    
        if (bBuffOverflow)
        {
            u16errCnt++;
            return ERR_BUFF_OVERFLOW;
        }
        return u8BufferSize;
    }
    
    /**
     * @brief
     * This method transmits au8Buffer to Serial line.
     * Only if u8txenpin != 0, there is a flow handling in order to keep
     * the RS485 transceiver in output state as long as the message is being sent.
     * This is done with UCSRxA register.
     * The CRC is appended to the buffer before starting to send it.
     *
     * @param nothing
     * @return nothing
     * @ingroup buffer
     */
    void Modbus::sendTxBuffer()
    {
        uint8_t i = 0;
    
        // append CRC to message
        uint16_t u16crc = calcCRC( u8BufferSize );
        au8Buffer[ u8BufferSize ] = u16crc >> 8;
        u8BufferSize++;
        au8Buffer[ u8BufferSize ] = u16crc & 0x00ff;
        u8BufferSize++;
    
        // set RS485 transceiver to transmit mode
        if (u8txenpin > 1)
        {
            switch( u8serno )
            {
    #if defined(UBRR1H)
            case 1:
                UCSR1A=UCSR1A |(1 << TXC1);
                break;
    #endif
    
    #if defined(UBRR2H)
            case 2:
                UCSR2A=UCSR2A |(1 << TXC2);
                break;
    #endif
    
    #if defined(UBRR3H)
            case 3:
                UCSR3A=UCSR3A |(1 << TXC3);
                break;
    #endif
            case 0:
            default:
                UCSR0A=UCSR0A |(1 << TXC0);
                break;
            }
            digitalWrite( u8txenpin, HIGH );
        }
    
        // transfer buffer to serial line
        if(u8serno<4)
            port->write( au8Buffer, u8BufferSize );
        else
            softPort->write( au8Buffer, u8BufferSize );
    
        // keep RS485 transceiver in transmit mode as long as sending
        if (u8txenpin > 1)
        {
            switch( u8serno )
            {
    #if defined(UBRR1H)
            case 1:
                while (!(UCSR1A & (1 << TXC1)));
                break;
    #endif
    
    #if defined(UBRR2H)
            case 2:
                while (!(UCSR2A & (1 << TXC2)));
                break;
    #endif
    
    #if defined(UBRR3H)
            case 3:
                while (!(UCSR3A & (1 << TXC3)));
                break;
    #endif
            case 0:
            default:
                while (!(UCSR0A & (1 << TXC0)));
                break;
            }
    
            // return RS485 transceiver to receive mode
            digitalWrite( u8txenpin, LOW );
        }
        if(u8serno<4)
            while(port->read() >= 0);
        else
            while(softPort->read() >= 0);
    
        u8BufferSize = 0;
    
        // set time-out for master
        u32timeOut = millis() + (unsigned long) u16timeOut;
    
        // increase message counter
        u16OutCnt++;
    }
    
    /**
     * @brief
     * This method calculates CRC
     *
     * @return uint16_t calculated CRC value for the message
     * @ingroup buffer
     */
    uint16_t Modbus::calcCRC(uint8_t u8length)
    {
        unsigned int temp, temp2, flag;
        temp = 0xFFFF;
        for (unsigned char i = 0; i < u8length; i++)
        {
            temp = temp ^ au8Buffer[i];
            for (unsigned char j = 1; j <= 8; j++)
            {
                flag = temp & 0x0001;
                temp >>=1;
                if (flag)
                    temp ^= 0xA001;
            }
        }
        // Reverse byte order.
        temp2 = temp >> 8;
        temp = (temp << 8) | temp2;
        temp &= 0xFFFF;
        // the returned value is already swapped
        // crcLo byte is first & crcHi byte is last
        return temp;
    }
    
    /**
     * @brief
     * This method validates slave incoming messages
     *
     * @return 0 if OK, EXCEPTION if anything fails
     * @ingroup buffer
     */
    uint8_t Modbus::validateRequest()
    {
        // check message crc vs calculated crc
        uint16_t u16MsgCRC =
            ((au8Buffer[u8BufferSize - 2] << 8)
             | au8Buffer[u8BufferSize - 1]); // combine the crc Low & High bytes
        if ( calcCRC( u8BufferSize-2 ) != u16MsgCRC )
        {
            u16errCnt ++;
            return NO_REPLY;
        }
    
        // check fct code
        boolean isSupported = false;
        for (uint8_t i = 0; i< sizeof( fctsupported ); i++)
        {
            if (fctsupported[i] == au8Buffer[FUNC])
            {
                isSupported = 1;
                break;
            }
        }
        if (!isSupported)
        {
            u16errCnt ++;
            return EXC_FUNC_CODE;
        }
    
        // check start address & nb range
        uint16_t u16regs = 0;
        uint8_t u8regs;
        switch ( au8Buffer[ FUNC ] )
        {
        case MB_FC_READ_COILS:
        case MB_FC_READ_DISCRETE_INPUT:
        case MB_FC_WRITE_MULTIPLE_COILS:
            u16regs = word( au8Buffer[ ADD_HI ], au8Buffer[ ADD_LO ]) / 16;
            u16regs += word( au8Buffer[ NB_HI ], au8Buffer[ NB_LO ]) /16;
            u8regs = (uint8_t) u16regs;
            if (u8regs > u8regsize) return EXC_ADDR_RANGE;
            break;
        case MB_FC_WRITE_COIL:
            u16regs = word( au8Buffer[ ADD_HI ], au8Buffer[ ADD_LO ]) / 16;
            u8regs = (uint8_t) u16regs;
            if (u8regs > u8regsize) return EXC_ADDR_RANGE;
            break;
        case MB_FC_WRITE_REGISTER :
            u16regs = word( au8Buffer[ ADD_HI ], au8Buffer[ ADD_LO ]);
            u8regs = (uint8_t) u16regs;
            if (u8regs > u8regsize) return EXC_ADDR_RANGE;
            break;
        case MB_FC_READ_REGISTERS :
        case MB_FC_READ_INPUT_REGISTER :
        case MB_FC_WRITE_MULTIPLE_REGISTERS :
            u16regs = word( au8Buffer[ ADD_HI ], au8Buffer[ ADD_LO ]);
            u16regs += word( au8Buffer[ NB_HI ], au8Buffer[ NB_LO ]);
            u8regs = (uint8_t) u16regs;
            if (u8regs > u8regsize) return EXC_ADDR_RANGE;
            break;
        }
        return 0; // OK, no exception code thrown
    }
    
    /**
     * @brief
     * This method validates master incoming messages
     *
     * @return 0 if OK, EXCEPTION if anything fails
     * @ingroup buffer
     */
    uint8_t Modbus::validateAnswer()
    {
        // check message crc vs calculated crc
        uint16_t u16MsgCRC =
            ((au8Buffer[u8BufferSize - 2] << 8)
             | au8Buffer[u8BufferSize - 1]); // combine the crc Low & High bytes
        if ( calcCRC( u8BufferSize-2 ) != u16MsgCRC )
        {
            u16errCnt ++;
            return NO_REPLY;
        }
    
        // check exception
        if ((au8Buffer[ FUNC ] & 0x80) != 0)
        {
            u16errCnt ++;
            return ERR_EXCEPTION;
        }
    
        // check fct code
        boolean isSupported = false;
        for (uint8_t i = 0; i< sizeof( fctsupported ); i++)
        {
            if (fctsupported[i] == au8Buffer[FUNC])
            {
                isSupported = 1;
                break;
            }
        }
        if (!isSupported)
        {
            u16errCnt ++;
            return EXC_FUNC_CODE;
        }
    
        return 0; // OK, no exception code thrown
    }
    
    /**
     * @brief
     * This method builds an exception message
     *
     * @ingroup buffer
     */
    void Modbus::buildException( uint8_t u8exception )
    {
        uint8_t u8func = au8Buffer[ FUNC ];  // get the original FUNC code
    
        au8Buffer[ ID ]      = u8id;
        au8Buffer[ FUNC ]    = u8func + 0x80;
        au8Buffer[ 2 ]       = u8exception;
        u8BufferSize         = EXCEPTION_SIZE;
    }
    
    /**
     * This method processes functions 1 & 2 (for master)
     * This method puts the slave answer into master data buffer
     *
     * @ingroup register
     * TODO: finish its implementation
     */
    void Modbus::get_FC1()
    {
        uint8_t u8byte, i;
        u8byte = 0;
    
        //  for (i=0; i< au8Buffer[ 2 ] /2; i++) {
        //    au16regs[ i ] = word(
        //    au8Buffer[ u8byte ],
        //    au8Buffer[ u8byte +1 ]);
        //    u8byte += 2;
        //  }
    }
    
    /**
     * This method processes functions 3 & 4 (for master)
     * This method puts the slave answer into master data buffer
     *
     * @ingroup register
     */
    void Modbus::get_FC3()
    {
        uint8_t u8byte, i;
        u8byte = 3;
    
        for (i=0; i< au8Buffer[ 2 ] /2; i++)
        {
            au16regs[ i ] = word(
                                au8Buffer[ u8byte ],
                                au8Buffer[ u8byte +1 ]);
            u8byte += 2;
        }
    }
    
    /**
     * @brief
     * This method processes functions 1 & 2
     * This method reads a bit array and transfers it to the master
     *
     * @return u8BufferSize Response to master length
     * @ingroup discrete
     */
    int8_t Modbus::process_FC1( uint16_t *regs, uint8_t u8size )
    {
        uint8_t u8currentRegister, u8currentBit, u8bytesno, u8bitsno;
        uint8_t u8CopyBufferSize;
        uint16_t u16currentCoil, u16coil;
    
        // get the first and last coil from the message
        uint16_t u16StartCoil = word( au8Buffer[ ADD_HI ], au8Buffer[ ADD_LO ] );
        uint16_t u16Coilno = word( au8Buffer[ NB_HI ], au8Buffer[ NB_LO ] );
    
        // put the number of bytes in the outcoming message
        u8bytesno = (uint8_t) (u16Coilno / 8);
        if (u16Coilno % 8 != 0) u8bytesno ++;
        au8Buffer[ ADD_HI ]  = u8bytesno;
        u8BufferSize         = ADD_LO;
    
        // read each coil from the register map and put its value inside the outcoming message
        u8bitsno = 0;
    
        for (u16currentCoil = 0; u16currentCoil < u16Coilno; u16currentCoil++)
        {
            u16coil = u16StartCoil + u16currentCoil;
            u8currentRegister = (uint8_t) (u16coil / 16);
            u8currentBit = (uint8_t) (u16coil % 16);
    
            bitWrite(
                au8Buffer[ u8BufferSize ],
                u8bitsno,
                bitRead( regs[ u8currentRegister ], u8currentBit ) );
            u8bitsno ++;
    
            if (u8bitsno > 7)
            {
                u8bitsno = 0;
                u8BufferSize++;
            }
        }
    
        // send outcoming message
        if (u16Coilno % 8 != 0) u8BufferSize ++;
        u8CopyBufferSize = u8BufferSize +2;
        sendTxBuffer();
        return u8CopyBufferSize;
    }
    
    /**
     * @brief
     * This method processes functions 3 & 4
     * This method reads a word array and transfers it to the master
     *
     * @return u8BufferSize Response to master length
     * @ingroup register
     */
    int8_t Modbus::process_FC3( uint16_t *regs, uint8_t u8size )
    {
    
        uint8_t u8StartAdd = word( au8Buffer[ ADD_HI ], au8Buffer[ ADD_LO ] );
        uint8_t u8regsno = word( au8Buffer[ NB_HI ], au8Buffer[ NB_LO ] );
        uint8_t u8CopyBufferSize;
        uint8_t i;
    
        au8Buffer[ 2 ]       = u8regsno * 2;
        u8BufferSize         = 3;
    
        for (i = u8StartAdd; i < u8StartAdd + u8regsno; i++)
        {
            au8Buffer[ u8BufferSize ] = highByte(regs[i]);
            u8BufferSize++;
            au8Buffer[ u8BufferSize ] = lowByte(regs[i]);
            u8BufferSize++;
        }
        u8CopyBufferSize = u8BufferSize +2;
        sendTxBuffer();
    
        return u8CopyBufferSize;
    }
    
    /**
     * @brief
     * This method processes function 5
     * This method writes a value assigned by the master to a single bit
     *
     * @return u8BufferSize Response to master length
     * @ingroup discrete
     */
    int8_t Modbus::process_FC5( uint16_t *regs, uint8_t u8size )
    {
        uint8_t u8currentRegister, u8currentBit;
        uint8_t u8CopyBufferSize;
        uint16_t u16coil = word( au8Buffer[ ADD_HI ], au8Buffer[ ADD_LO ] );
    
        // point to the register and its bit
        u8currentRegister = (uint8_t) (u16coil / 16);
        u8currentBit = (uint8_t) (u16coil % 16);
    
        // write to coil
        bitWrite(
            regs[ u8currentRegister ],
            u8currentBit,
            au8Buffer[ NB_HI ] == 0xff );
    
    
        // send answer to master
        u8BufferSize = 6;
        u8CopyBufferSize = u8BufferSize +2;
        sendTxBuffer();
    
        return u8CopyBufferSize;
    }
    
    /**
     * @brief
     * This method processes function 6
     * This method writes a value assigned by the master to a single word
     *
     * @return u8BufferSize Response to master length
     * @ingroup register
     */
    int8_t Modbus::process_FC6( uint16_t *regs, uint8_t u8size )
    {
    
        uint8_t u8add = word( au8Buffer[ ADD_HI ], au8Buffer[ ADD_LO ] );
        uint8_t u8CopyBufferSize;
        uint16_t u16val = word( au8Buffer[ NB_HI ], au8Buffer[ NB_LO ] );
    
        regs[ u8add ] = u16val;
    
        // keep the same header
        u8BufferSize         = RESPONSE_SIZE;
    
        u8CopyBufferSize = u8BufferSize +2;
        sendTxBuffer();
    
        return u8CopyBufferSize;
    }
    
    /**
     * @brief
     * This method processes function 15
     * This method writes a bit array assigned by the master
     *
     * @return u8BufferSize Response to master length
     * @ingroup discrete
     */
    int8_t Modbus::process_FC15( uint16_t *regs, uint8_t u8size )
    {
        uint8_t u8currentRegister, u8currentBit, u8frameByte, u8bitsno;
        uint8_t u8CopyBufferSize;
        uint16_t u16currentCoil, u16coil;
        boolean bTemp;
    
        // get the first and last coil from the message
        uint16_t u16StartCoil = word( au8Buffer[ ADD_HI ], au8Buffer[ ADD_LO ] );
        uint16_t u16Coilno = word( au8Buffer[ NB_HI ], au8Buffer[ NB_LO ] );
    
    
        // read each coil from the register map and put its value inside the outcoming message
        u8bitsno = 0;
        u8frameByte = 7;
        for (u16currentCoil = 0; u16currentCoil < u16Coilno; u16currentCoil++)
        {
    
            u16coil = u16StartCoil + u16currentCoil;
            u8currentRegister = (uint8_t) (u16coil / 16);
            u8currentBit = (uint8_t) (u16coil % 16);
    
            bTemp = bitRead(
                        au8Buffer[ u8frameByte ],
                        u8bitsno );
    
            bitWrite(
                regs[ u8currentRegister ],
                u8currentBit,
                bTemp );
    
            u8bitsno ++;
    
            if (u8bitsno > 7)
            {
                u8bitsno = 0;
                u8frameByte++;
            }
        }
    
        // send outcoming message
        // it's just a copy of the incomping frame until 6th byte
        u8BufferSize         = 6;
        u8CopyBufferSize = u8BufferSize +2;
        sendTxBuffer();
        return u8CopyBufferSize;
    }
    
    /**
     * @brief
     * This method processes function 16
     * This method writes a word array assigned by the master
     *
     * @return u8BufferSize Response to master length
     * @ingroup register
     */
    int8_t Modbus::process_FC16( uint16_t *regs, uint8_t u8size )
    {
        uint8_t u8func = au8Buffer[ FUNC ];  // get the original FUNC code
        uint8_t u8StartAdd = au8Buffer[ ADD_HI ] << 8 | au8Buffer[ ADD_LO ];
        uint8_t u8regsno = au8Buffer[ NB_HI ] << 8 | au8Buffer[ NB_LO ];
        uint8_t u8CopyBufferSize;
        uint8_t i;
        uint16_t temp;
    
        // build header
        au8Buffer[ NB_HI ]   = 0;
        au8Buffer[ NB_LO ]   = u8regsno;
        u8BufferSize         = RESPONSE_SIZE;
    
        // write registers
        for (i = 0; i < u8regsno; i++)
        {
            temp = word(
                       au8Buffer[ (BYTE_CNT + 1) + i * 2 ],
                       au8Buffer[ (BYTE_CNT + 2) + i * 2 ]);
    
            regs[ u8StartAdd + i ] = temp;
        }
        u8CopyBufferSize = u8BufferSize +2;
        sendTxBuffer();
    
        return u8CopyBufferSize;
    }

  6. #6
    Senior Member Epyon's Avatar
    Join Date
    Apr 2013
    Location
    Belgium
    Posts
    425
    It's the same file, I just made some edits for my specific application that removed some lines. The code to be changed that I posted above can be found in your file at lines #292 and #344. Just look for the function names and edit that piece of code.

  7. #7
    Junior Member
    Join Date
    Aug 2016
    Posts
    12
    Hi i did the modifications but it doesent work:
    Code:
    n file included from C:\Users\Perger\AppData\Local\Temp\arduino_modified_sketch_175523\sketch_sep14a.ino:3:0:
    
    C:\Users\Perger\Documents\Arduino\libraries\Modbus-Master-Slave-for-Arduino-master/ModbusRtumod.h: In member function 'void Modbus::sendTxBuffer()':
    
    C:\Users\Perger\Documents\Arduino\libraries\Modbus-Master-Slave-for-Arduino-master/ModbusRtumod.h:912:13: error: 'UCSR0A' was not declared in this scope
    
                 UCSR0A=UCSR0A |(1 << TXC0);
    
                 ^
    
    C:\Users\Perger\Documents\Arduino\libraries\Modbus-Master-Slave-for-Arduino-master/ModbusRtumod.h:912:34: error: 'TXC0' was not declared in this scope
    
                 UCSR0A=UCSR0A |(1 << TXC0);
    
                                      ^
    
    C:\Users\Perger\Documents\Arduino\libraries\Modbus-Master-Slave-for-Arduino-master/ModbusRtumod.h:948:22: error: 'UCSR0A' was not declared in this scope
    
                 while (!(UCSR0A & (1 << TXC0)));
    
                          ^
    
    C:\Users\Perger\Documents\Arduino\libraries\Modbus-Master-Slave-for-Arduino-master/ModbusRtumod.h:948:37: error: 'TXC0' was not declared in this scope
    
                 while (!(UCSR0A & (1 << TXC0)));
    
                                         ^
    You want to share your file whit me?

  8. #8
    Junior Member
    Join Date
    Sep 2017
    Posts
    1

    Question ModbusRtu.h on a Teensy++ 2.0 (AT90USB1286)

    Quote Originally Posted by Epyon View Post
    Looks like a buffer overflow of some sorts?

    I have tested a lot of Modbus libraries and found that this one performs the most satisfactory. To get it to work on Teensy you just need to comment out some AVR registers in the ModbusRTU.h file.
    Hi Epyon,

    and did you try to run it on a Teensy++ 2.0 (AT90USB1286)?

    Regards,
    Luís

  9. #9
    Senior Member Epyon's Avatar
    Join Date
    Apr 2013
    Location
    Belgium
    Posts
    425
    Quote Originally Posted by LuisMorais View Post
    Hi Epyon,

    and did you try to run it on a Teensy++ 2.0 (AT90USB1286)?

    Regards,
    Luís
    No, I also don't have any 2.0 hardware. But because it is based on AVR, you can probably use the standard version of the library with minimal editting.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •