Aplikacijski kod

Dependencies:   mbed

U prikazanom primjeru izvedena je remote ModBus TCP/IP komunikacija. Generalni opis konfiguracije: U ovom radu je korištena oprema prizvođača Mikortik te mikrokontroler mbed NXP LPC1768. Libery za ethernet odnosno Modbus TCP/IP sam skino sam stranice mbeda te sam ga modificirao kako bi odgovarao željenoj aplikaciji. (https://os.mbed.com/users/paleskyjp/code/ModbusTCP_Modified/) Preko mrežne opreme izeveo sam SSTP VPN koji spaja lokalne subnete iz uređaja "mbed LOCAL" i uređaja "mbed LAPTOP" u jedan jedinstveni te omogućava nesmetanu komunikaciju između uređaja povezanih u lokalnim subnetima. Moguće se također spojiti direktno preko ethernet kabela u uređaj, ali u ovom primjeru laptop je spojen na WIFI mrežu nazvanzu "MBED TVZ" te preko nje komunicira sa mikrokontrolerom koji je kabelom spojem u mrežni uređaj naziva "mbed LOCAL". Riječ remote u ovom primjeru pokazuje da su dva uređaja "mbed LOCAL" i "mbed LAPTOP" preko WIFI-ja spojeni na 2 različita hosta (mobiteli pored njih) te je otvoren VPN kanal između njih. Na uređajima je također podešen DHCP koji dodijeljuje adrese uređajima koji se pokušavaju spojiti na njih, tako i mikrokontroleru. Lokalni subnet nije pušten na internet te mbed nije direktno izložen internetu, što pridonosi sigurnosnom aspektu komunikacije. Za tesitranje komunikacije dovoljno je koristit osnovne alate koji dolaze sa svakim windows-ima, naredba ping (na videu doljnji desni dio ekrana, dok je mbed bio ugašen vidljivo je da ne prolazi komunikacija, nakon što se upalio i spojio na mrežu komunikacija se uspostavila te je vidljivo kako konstantno vraća odgovor) Alat korišten za upis podataka u registre je Modbus Master koji podržava TCP/IP. Svakako je moguće koristiti i druge aplikacije ili druge uređaje koji podržavaju ovaj tip komunikacije. Primjerice PLC, drugi mbed, neko drugo računalo,... Aplikacija sama po sebi nije nešto pretjerano komplicirana ali put samog paketa od Modbus master-a do samog Modbus slave-a nije tako jednostavan. Aplikacija se odnosi na dio ukoliko na nulti registar bilo koje adresnog polja Modbus master pošalje vrijednost veću od 5 konstantno svijetli LED2 izvedena na samoj ploćici mbeda, te ukoliko dođe vrijednos manja od 5 da ugasi LED2. To je samo demostracija rada komunikacije. U ovoj konfiguraciji moguće je bilo gdje odnjeti mikortik i mbed te ga spojiti na WIFI koji ima propust prema internetu te se sa drugim mikrotikom povezati sa mbed-om i nesmetano komunicirati.

ModBus/porttcp.cpp

Committer:
lincina
Date:
2019-11-14
Revision:
4:97b3bd92b315
Parent:
2:53de07fd9705

File content as of revision 4:97b3bd92b315:

/*
 * FreeModbus Libary: mbed Port
 * Copyright (C) 2006 Christian Walter <wolti@sil.at>
 *
 * 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 St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * File: $Id: porttcp.c,v 1.1 2006/08/30 23:18:07 wolti Exp $
 */
/* ----------------------- System includes ----------------------------------*/
#include <stdio.h>
#include <string.h>

#include "port.h"

/* ----------------------- mbed includes ------------------------------------*/
#include "mbed.h"
#include "EthernetNetIf.h"
#include "TCPSocket.h"

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"

/* ----------------------- MBAP Header --------------------------------------*/
#define MB_TCP_UID          6
#define MB_TCP_LEN          4
#define MB_TCP_FUNC         7

/* ----------------------- Defines  -----------------------------------------*/
#define MB_TCP_DEFAULT_PORT 502 /* TCP listening port. */
#define MB_TCP_BUF_SIZE     ( 256 + 7 ) /* Must hold a complete Modbus TCP frame. */

/* ----------------------- Prototypes ---------------------------------------*/
void prvvMBPortReleaseClient(  );
BOOL prvbMBPortAcceptClient(  );
BOOL prvMBTCPGetFrame(  );
void onListeningTCPSocketEvent(TCPSocketEvent e);
void onConnectedTCPSocketEvent(TCPSocketEvent e);

/* ----------------------- Static variables ---------------------------------*/
static UCHAR    aucTCPBuf[MB_TCP_BUF_SIZE];
static USHORT   usTCPBufPos;
static USHORT   usTCPFrameBytesLeft;

static TCPSocket ListeningSock;
static TCPSocket* pConnectedSock; // for ConnectedSock
static Host client;

/* ----------------------- Begin implementation -----------------------------*/
BOOL
xMBTCPPortInit( USHORT usTCPPort )
{
    BOOL            bOkay = FALSE;
    USHORT          usPort;

    if( usTCPPort == 0 )
    {
        usPort = MB_TCP_DEFAULT_PORT;
    }
    else
    {
        usPort = ( USHORT ) usTCPPort;
    }
    pConnectedSock=NULL;

    // Set the callbacks for Listening
    ListeningSock.setOnEvent(&onListeningTCPSocketEvent); 

    // bind and listen on TCP
    if( ListeningSock.bind(Host(IpAddr(), usPort)) )
    {
        // Failed to bind
        bOkay = FALSE;
    }
    else if( ListeningSock.listen() )
    {
        // Failed to listen
        bOkay = FALSE;
    }
    else
    {
#ifdef MB_TCP_DEBUG
        vMBPortLog( MB_LOG_DEBUG, "MBTCP-ACCEPT", "Protocol stack ready.\r\n" );
#endif
        bOkay = TRUE;
    }    

    return bOkay;
}

void
vMBTCPPortClose(  )
{
    /* Shutdown any open client sockets. */
    prvvMBPortReleaseClient();

    /* Shutdown or listening socket. */
    ListeningSock.close();

    /* Release resources for the event queue. */
//    vMBPortEventClose(  );
}

void
vMBTCPPortDisable( void )
{
    prvvMBPortReleaseClient( );
}

BOOL
xMBTCPPortGetRequest( UCHAR ** ppucMBTCPFrame, USHORT * usTCPLength )
{
    *ppucMBTCPFrame = &aucTCPBuf[0];
    *usTCPLength = usTCPBufPos;

    /* Reset the buffer. */
    usTCPBufPos = 0;
    usTCPFrameBytesLeft = MB_TCP_FUNC;
    return TRUE;
}

BOOL
xMBTCPPortSendResponse( const UCHAR * pucMBTCPFrame, USHORT usTCPLength )
{
    BOOL            bFrameSent = FALSE;

    if( pConnectedSock )
    {
        if(pConnectedSock->send((char *)pucMBTCPFrame, usTCPLength)>=0)
        {
            bFrameSent = TRUE;
            printf("sent %d bytes\n",usTCPLength);
        }
        else
        {
            /* Drop the connection in case of an write error. */
            printf("sent error!\n");
            prvvMBPortReleaseClient( );
        }
    }
    return bFrameSent;
}

void
prvvMBPortReleaseClient(  )
{
    if(pConnectedSock){
        IpAddr clientIp = client.getIp();
#ifdef MB_TCP_DEBUG
        vMBPortLog( MB_LOG_DEBUG, "MBTCP-CLOSE", "Closed connection to %d.%d.%d.%d.\r\n",
             clientIp[0], clientIp[1], clientIp[2], clientIp[3]);
#endif
        pConnectedSock->close();
        pConnectedSock=NULL;
    }
}


BOOL prvbMBPortAcceptClient(  )
{
    // Accepts connection from client and gets connected socket.   

    if (ListeningSock.accept(&client, &pConnectedSock)) {
        return FALSE; //Error in accept, discard connection
    }
    // Setup the new socket events
    pConnectedSock->setOnEvent(&onConnectedTCPSocketEvent);
    // We can find out from where the connection is coming by looking at the
    // Host parameter of the accept() method
    IpAddr clientIp = client.getIp();

#ifdef MB_TCP_DEBUG
    vMBPortLog( MB_LOG_DEBUG, "MBTCP-ACCEPT", "Accepted new client %d.%d.%d.%d\r\n",
        clientIp[0], clientIp[1], clientIp[2], clientIp[3]);
#endif

    usTCPBufPos = 0;
    usTCPFrameBytesLeft = MB_TCP_FUNC;

    return TRUE;
}


BOOL
prvMBTCPGetFrame(  )
{
    BOOL            bOkay = TRUE;
    USHORT          usLength;
    int             iRes;
    /* Make sure that we can safely process the next read request. If there
     * is an overflow drop the client.
     */
    
    if( ( usTCPBufPos + usTCPFrameBytesLeft ) >= MB_TCP_BUF_SIZE )
    {
        // buffer overrun
        return FALSE;
    }

    while (iRes = pConnectedSock->recv((char *)&aucTCPBuf[usTCPBufPos], MB_TCP_BUF_SIZE-usTCPBufPos) ) {
        usTCPBufPos+=iRes;
        usTCPFrameBytesLeft-=iRes;
    }

    /* If we have received the MBAP header we can analyze it and calculate
     * the number of bytes left to complete the current request. If complete
     * notify the protocol stack.
     */
    if( usTCPBufPos >= MB_TCP_FUNC )
    {
        /* Length is a byte count of Modbus PDU (function code + data) and the
         * unit identifier. */
        usLength = aucTCPBuf[MB_TCP_LEN] << 8U;
        usLength |= aucTCPBuf[MB_TCP_LEN + 1];

        /* Is the frame already complete. */
        if( usTCPBufPos < ( MB_TCP_UID + usLength ) )
        {
            usTCPFrameBytesLeft = usLength + MB_TCP_UID - usTCPBufPos;
        }
        /* The frame is complete. */
        else if( usTCPBufPos == ( MB_TCP_UID + usLength ) )
        {
#ifdef MB_TCP_DEBUG
            prvvMBTCPLogFrame( "MBTCP-RECV", &aucTCPBuf[0], usTCPBufPos );
#endif
            ( void )xMBPortEventPost( EV_FRAME_RECEIVED );
        }
        else
        {
#ifdef MB_TCP_DEBUG
            vMBPortLog( MB_LOG_DEBUG, "MBTCP-ERROR",
                            "Received too many bytes! Droping client.[%d>%d]\r\n" ,usTCPBufPos, MB_TCP_UID + usLength);
#endif
            /* This should not happen. We can't deal with such a client and
             * drop the connection for security reasons.
             */
            prvvMBPortReleaseClient(  );
        }
    }
    return bOkay;
}


void onListeningTCPSocketEvent(TCPSocketEvent e)
{
    switch(e)
    {
    case TCPSOCKET_ACCEPT:
        if(!prvbMBPortAcceptClient())
        {
#ifdef MB_TCP_DEBUG
            vMBPortLog( MB_LOG_DEBUG, "MBTCP-ERROR", "Error with client connection! Droping it.\r\n" );
#endif
            ListeningSock.close();
        }
        break;

    default:
#ifdef MB_TCP_DEBUG
        vMBPortLog( MB_LOG_DEBUG, "MBTCP-ERROR", "Unexpeted condition!.\r\n" );
#endif
        ListeningSock.close();
        break;
     };
}

void onConnectedTCPSocketEvent(TCPSocketEvent e)
{
    switch(e)
    {
    case TCPSOCKET_READABLE:
        if(!prvMBTCPGetFrame())prvvMBPortReleaseClient();
        break;
    case TCPSOCKET_CONNECTED:
    case TCPSOCKET_WRITEABLE:
        break;

    case TCPSOCKET_CONTIMEOUT:
    case TCPSOCKET_CONRST:
    case TCPSOCKET_CONABRT:
    case TCPSOCKET_ERROR:
    case TCPSOCKET_DISCONNECTED:
        prvvMBPortReleaseClient();
        break;
    default:
        break;
    }
}