Modbus RTU class for slave devices. It shares data with other software components via a Message queue object, and can exist more than one Modbus Slave RTU instances in a application program.

Dependents:   Nucleo_modbus_protocol_test

ModbusSlaveRTU.h

Committer:
gabrielrivas
Date:
2015-01-21
Revision:
6:0143a57e0c8e
Parent:
5:7cec6597adee

File content as of revision 6:0143a57e0c8e:

/** Modbus Slave RTU
 * Copyright (c) 2015 Gabriel Rivas
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef _MODBUS_SLAVE_RTU_H_
#define _MODBUS_SLAVE_RTU_H_

#include "mbed.h"
#include "rtos.h"
#include "MessageQueue.h"

#define MODBUS_MAX_LEN (256)

struct ThreadSafeArray_t {
    Mutex mutex;
    uint8_t *data;
    uint32_t length;
};

/**
 * Modbus RTU class for slave devices. It shares data with other software components via a Message queue object, 
 * and can exist more than one Modbus Slave RTU instances in a application program.
 */
class ModbusSlaveRTU
{
public:
    /*! Status of the internal modbus FSM. */
    enum MbStatusTypes_t {
        LISTENING  = 0x20, /**< Listening for incomming requests.*/
        PROCESSING = 0x21, /**< Processing received request.*/
        SENDING    = 0x23  /**< Sending generated response.*/
    };
    
private:    
    /*! Modbus response message bytes indexes. */
    enum MbByteIndex_t {
        ADDRESS         = 0x00, /**< Modbus address position.*/
        FUNCTION        = 0x01, /**< Modbus function position.*/
        BYTE_COUNT      = 0x02, /**< Byte count high byte position.*/
        DATA_START      = 0x03, /**< Returned data start position.*/
        BYTE_OFFSET     = 0x02, /**< Starting register in bytes, high byte position.*/
        REGISTER_COUNT  = 0x04  /**< Number of registers to be read, high byte position.*/
    };

    /*! Modbus errors reported during message processing. */
    enum MbReceivedMessageErrors_t {
        NO_ERROR         = 0x10, /**< No error.*/
        ADDRESS_ERROR    = 0x11, /**< Wrong slave address.*/
        CRC_ERROR        = 0x12, /**< CRC from received message doesn't match with locally calculated CRC.*/
        OFFSET_ERROR     = 0x13, /**< Offset is out of range.*/
        BYTE_COUNT_ERROR = 0x14  /**< Byte count requested is out of range.*/
    };
    
    /*! Internal modbus FSM states. */
    enum MbSlaveRTUStates_t {
        WAIT_REQUEST_MESSAGE      = 0x00, /**< Idle while bytes are being received.*/
        GET_RECEIVED_BYTES        = 0x01, /**< Copy received bytes into the internal buffer.*/
        BUILD_RESPONSE_MESSAGE    = 0x02, /**< Building response message.*/
        TRANSMIT_RESPONSE_MESSAGE = 0x03  /**< Transmitting response message.*/
    };

    /*! Modbus function codes. */
    enum MbFunctionCodes_t {
        READ_COIL_STATUS        = 0x01, /**< Function to read a single coil status.*/
        READ_INPUT_STATUS       = 0x02, /**< Function to read input register.*/
        READ_HOLDING_REGISTERS  = 0x03, /**< Function to read one or more internal holding resgisters.*/
        READ_INPUT_REGISTERS    = 0x04, /**< Function to read one or more input resgisters.*/
        FORCE_SINGLE_COIL       = 0x05, /**< Function to write a value into a single discrete output.*/
        PRESET_SINGLE_REGISTER  = 0x06, /**< Function to write a value into a single holding register.*/
        READ_EXCEPTION_STATUS   = 0x07, /**< Function to request the value of the status byte.*/
        LOOPBACK_TEST           = 0x08, /**< Function to test the communications interface. Returns the same message.*/
        FORCE_MULTIPLE_COILS    = 0x0F  /**< Function to set more than one coil to a given value*/
    };

    /*! Modbus frame structure elements. */
    struct MbSlaveRTUFrame_t {
        uint8_t mAddress;             /**< Slave device address.*/
        MbFunctionCodes_t mFunction;  /**< Requested function of type MbFunctionCodes_t.*/
        uint16_t mOffset;             /**< Offset in bytes from the starting address.*/
        uint16_t mByteCount;          /**< Number of bytes to respond.*/
        uint8_t mCRCH;                /**< CRC high byte.*/
        uint8_t  mCRCL;               /**< CRC low byte.*/
        uint16_t mSize;               /**< Size of the frame in bytes.*/
    };

    /*! Memory map structure. */
    struct MemoryMap_t {
        ThreadSafeArray_t *coilRegisters;    /**< Pointer to the coil registers array.*/
        ThreadSafeArray_t *holdingRegisters; /**< Pointer to the holding registers array.*/
        ThreadSafeArray_t *inputRegisters;   /**< Pointer to the input registers array.*/
    };

public:
    /**
     * Creates a Modbus Slave RTU object.
     * @param id Mosbus address.
     * @param txQueue Message queue to write modbus responses into.
     * @param rxQueue Message queue to read modbus requests from.     
     * @param *coilRegisters Pointer to the memory space reserved for the coil registers.
     * @param *inputRegisters Pointer to the memory space reserved for the input registers.
     * @param *inputRegisters Pointer to the memory space reserved for the holding registers.
     */
    ModbusSlaveRTU( uint8_t id,
                    MessageQueue<uint8_t>* txQueue,
                    MessageQueue<uint8_t>* rxQueue,
                    ThreadSafeArray_t *coilRegisters,
                    ThreadSafeArray_t *inputRegisters,
                    ThreadSafeArray_t *holdingRegisters);

public:
    /**
     * Function to start the Modbus RTU Slave engine.
     */
    void trigger(void);

    /**
     * Gets the status of the internal FSM.
     * @return Status value as defined in MbStatusTypes_t.
     */
    MbStatusTypes_t getStatus(void);
    
    /**
     * Internal FSM process.
     */    
    void FSM(void);

private:
    /**
     * Handler for read holding registers function request.
     * @return Error or success code as defined in MbReceivedMessageErrors_t.
     */
    MbReceivedMessageErrors_t readHoldingRegistersHandler(void);
    
    /**
     * Handler for read coil registers function request.
     * @return Error or success code as defined in MbReceivedMessageErrors_t.
     */    
    MbReceivedMessageErrors_t readCoilRegistersHandler(void);
    
    /**
     * Builds response message.
     * @return Error or success code as defined in MbReceivedMessageErrors_t.
     */    
    MbReceivedMessageErrors_t buildResponse(void);

    /**
     * Function that computes the CRC of a Modbus message.
     * @param *data Pointer to the message data.
     * @param uint8_t Length of the message.
     * @return The CRC Code.
     */
    uint16_t getCRC(uint8_t *  data,uint8_t len);

    /**
     * Verifies the CRC value of a message to determine it has been received correctly.
     * @param *data Pointer to the message data.
     * @return Returns 1 if the message has been well received, 0 otherwise.
     */
    uint8_t checkMessageCRC(uint8_t *  data);

    /**
     * Function that appends the header and tail bytes for a response message.
     * @return void.
     */
    void appendHeaderAndTailToMessage(void);

    /**
     * Function that checks for error in the received message.
     * @param *array Safe array being processed depending on the modbus function.
     * @return The error or success code as defined in MbReceivedMessageErrors_t.
     */
    MbReceivedMessageErrors_t checkReceivedMessageErrors(ThreadSafeArray_t *array);

private:
    /** Internal state of the modbus FSM.
     */
    MbSlaveRTUStates_t m_state;
    
    /** Response frame.
     */    
    MbSlaveRTUFrame_t m_frame;
    
    /** Modbus address.
     */
    uint8_t m_address;
    
    /** Modbus process status.
     */
    MbStatusTypes_t m_status;
    
    /** Memory map for registers.
     */
    MemoryMap_t m_memoryMap;
    
    /** Queue to write response data.
     */
    MessageQueue<uint8_t>* m_bTxQueue;
    
    /** Queue to read incomming requests from.
     */
    MessageQueue<uint8_t>* m_bRxQueue;
    
    /** Internal byte array to copy incomming requests.
     */
    uint8_t m_receivedMessage[MODBUS_MAX_LEN];
    
    /** Internal byte array to generate responses.
     */   
    uint8_t m_responseMessage[MODBUS_MAX_LEN];
};
#endif