ModbusTCP Light This version utilize lwIP library with the minimal implementation of Modbus TCP. Error processing hasn\\\'t been implemented yet.

Dependencies:   mbed lwip

mbtcp.cpp

Committer:
paleskyjp
Date:
2012-03-16
Revision:
1:59cee932f289
Parent:
0:8ee5ec4bcec2

File content as of revision 1:59cee932f289:

/*
 * 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 $
 *
 * modified by Yuji Hosogaya 3-16-2012
 *
 * Originally this file was porttcp.c, then combined with other files
 * in order to make a light version of Modbus TCP.
 */
#include "mbtcp.h"

Ethernet ethernet;
struct netif netif_data;

/* ----------------------- 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. */

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

/* ----------------------- Begin implementation -----------------------------*/
void EventPost(struct tcp_pcb *pcb)
{
    USHORT usAddress,usNRegs;
    UCHAR txbuf[MB_TCP_BUF_SIZE];
    int len=0;
    UCHAR ucTemp;
    MB_STRUCT *rxmbs=(MB_STRUCT *)aucTCPBuf;
    MB_STRUCT *txmbs=(MB_STRUCT *)txbuf;
    MB_WORD_REQ *wd_req=(MB_WORD_REQ *)GET_DATA_PTR(rxmbs);
    MB_WORD_RES *wd_res=(MB_WORD_RES *)GET_DATA_PTR(txmbs);

    usAddress=TO_USHORT(wd_req->ADDR_H,wd_req->ADDR_L)+1;
    usNRegs=TO_USHORT(wd_req->LEN_H,wd_req->LEN_L);

    printf("TID=%04X | PID=%04X | LEN=%04X | UID=%02X | FUNC=%02X\nDATA=",
        GET_TID(rxmbs), GET_PID(rxmbs), GET_LEN(rxmbs), GET_UID(rxmbs), GET_FUNC(rxmbs) );
    for(int i=0;i<usTCPBufPos-8;i++){
        printf("%02X",aucTCPBuf[i+8]);
    }
    printf("\n");
    
    switch(GET_FUNC(rxmbs))
    {
    case FC_RD_COILS:
        memcpy((char*)txmbs,(char*)rxmbs,8);
        wd_res->BYTES=usNRegs*2;
        len=usNRegs/8;
        if(usNRegs%8!=0)len++;
        SET_LEN(txmbs,len+3);
        wd_res->BYTES=(UCHAR)len;
        
        eMBRegCoilsCB( (UCHAR *) &wd_res->DATA, usAddress, usNRegs, MB_REG_READ);
        len+=9;
        break;

    case FC_RD_DISC_INPUTS:
        memcpy((char*)txmbs,(char*)rxmbs,8);
        wd_res->BYTES=usNRegs*2;
        len=usNRegs/8;
        if(usNRegs%8!=0)len++;
        SET_LEN(txmbs,len+3);
        wd_res->BYTES=(UCHAR)len;

        eMBRegDiscreteCB( (UCHAR *) &wd_res->DATA, usAddress, usNRegs );
        len+=9;
        break;

    case FC_RD_HOLDING_REGS:
        memcpy((char*)txmbs,(char*)rxmbs,8);
        wd_res->BYTES=usNRegs*2;
        len=usNRegs*2;
        SET_LEN(txmbs,len+3);

        eMBRegHoldingCB( (UCHAR *)&wd_res->DATA, usAddress, usNRegs, MB_REG_READ);
        len+=9;
        break;

    case FC_RD_INPUT_REGS:
        memcpy((char*)txmbs,(char*)rxmbs,8);
        wd_res->BYTES=usNRegs*2;
        len=usNRegs*2;
        SET_LEN(txmbs,len+3);

        eMBRegInputCB( (UCHAR *)&wd_res->DATA, usAddress, usNRegs );
        len+=9;
        break;

    case FC_WR_SINGLE_COIL:
        ucTemp=(wd_req->LEN_H==0x00)?0:1;
        len=12;
        memcpy((char*)txmbs,(char*)rxmbs,len);
        eMBRegCoilsCB( &ucTemp, usAddress, 1, MB_REG_WRITE);
        break;

    case FC_WR_SINGLE_REG:
        len=12;
        memcpy((char*)txmbs,(char*)rxmbs,12);
        eMBRegHoldingCB( &wd_req->LEN_H, usAddress, 1, MB_REG_WRITE );
        break;

    case FC_WR_MULTI_COILS:
        len=12;
        memcpy((char*)txmbs,(char*)rxmbs,len);
        SET_LEN(txmbs,6);
        
        eMBRegCoilsCB( &wd_req->DATA[1], usAddress, usNRegs, MB_REG_WRITE);
        break;

    case FC_WR_MULTI_REGS:
        len=12;
        memcpy((char*)txmbs,(char*)rxmbs,len);
        SET_LEN(txmbs,6);

        eMBRegHoldingCB( &wd_req->DATA[1], usAddress, usNRegs, MB_REG_WRITE);
        break;
    case FC_RD_WR_MULTI_REGS:
        memcpy((char*)txmbs,(char*)rxmbs,8);

        // Write operation
        eMBRegHoldingCB( &wd_req->DATA[5], usAddress, usNRegs, MB_REG_WRITE);

        // Read operation
        usAddress=TO_USHORT(wd_req->DATA[0],wd_req->DATA[1])+1;
        usNRegs=TO_USHORT(wd_req->DATA[2],wd_req->DATA[3]);
        eMBRegHoldingCB( &wd_res->DATA, usAddress, usNRegs, MB_REG_READ);

        len=usNRegs*2;
        wd_res->BYTES=len;
        SET_LEN(txmbs,len+3);

        len+=9;
        break;

    default:
        printf("UNSUPPORTED FUNCTION CODE\n");
        break;
    }
    if(len>0){
        for(int i=0;i<len;i++){
            printf("%02X ",txbuf[i]);
        }
        printf("\n\n");
        if (tcp_write(pcb, (void *)txbuf, len, 1) == ERR_OK) {
            tcp_output(pcb);
        }else{
            printf("Failed to send a response!!\n");
        }
    }
}


err_t recv_callback(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
    struct netif   *netif = &netif_data;
    unsigned short usLength;
    int i;

    /* Check if status is ok and data is arrived. */
    if (err == ERR_OK && p != NULL) {
        /* Inform TCP that we have taken the data. */
        tcp_recved(pcb, p->tot_len);

        memcpy( &aucTCPBuf[usTCPBufPos], p->payload, p->tot_len );
        usTCPBufPos += p->tot_len;

        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 ) )
            {
            }
            else if( usTCPBufPos == ( MB_TCP_UID + usLength ) )
            {
                for(i=0;i<usTCPBufPos;i++){
                    printf("%02X ",aucTCPBuf[i]);
                }
                printf("\n");
                EventPost(pcb);
                usTCPBufPos=0;
            }
            else
            {
                printf("Received to many bytes! Droping client.\n" );
                /* This should not happen. We can't deal with such a client and
                 * drop the connection for security reasons.
                 */
                //vvMBPortReleaseClient( pxPCB );
            }
        }
        pbuf_free(p);
    }else{
            /* No data arrived */
            /* That means the client closes the connection and sent us a packet with FIN flag set to 1. */
            /* We have to cleanup and destroy out TCPConnection. */
            printf("Connection closed by client.\r\n");
            pbuf_free(p);
        }

    return ERR_OK;
}
/* Accept an incomming call on the registered port */
err_t accept_callback(void *arg, struct tcp_pcb *npcb, err_t err) {
    LWIP_UNUSED_ARG(arg);
    /* Subscribe a receive callback function */
    tcp_recv(npcb, &recv_callback);
    /* Don't panic! Everything is fine. */
    printf("accepted connection\n");
    usTCPBufPos=0;
    return ERR_OK;
}

void mb_init()
{
    struct netif   *netif = &netif_data;
    struct ip_addr  ipaddr;
    struct ip_addr  netmask;
    struct ip_addr  gateway;
    Ticker tickFast, tickSlow, tickARP, eth_tick, dns_tick, dhcp_coarse, dhcp_fine;
    
    char *hostname = "my-mbed";
    printf("Setting up...\n");

    /* Start Network with DHCP */
    IP4_ADDR(&netmask, 255,255,255,255);
    IP4_ADDR(&gateway, 0,0,0,0);
    IP4_ADDR(&ipaddr, 0,0,0,0);
    /* Initialise after configuration */
    lwip_init();
    netif->hwaddr_len = ETHARP_HWADDR_LEN;
    device_address((char *)netif->hwaddr);
    netif = netif_add(netif, &ipaddr, &netmask, &gateway, NULL, device_init, ip_input);
    netif->hostname = hostname;
    netif_set_default(netif);
    dhcp_start(netif); // <-- Use DHCP
    
        /* Initialise all needed timers */
    tickARP.attach_us( &etharp_tmr,  ARP_TMR_INTERVAL  * 1000);
    tickFast.attach_us(&tcp_fasttmr, TCP_FAST_INTERVAL * 1000);
    tickSlow.attach_us(&tcp_slowtmr, TCP_SLOW_INTERVAL * 1000);
    dns_tick.attach_us(&dns_tmr, DNS_TMR_INTERVAL * 1000);
    dhcp_coarse.attach_us(&dhcp_coarse_tmr, DHCP_COARSE_TIMER_MSECS * 1000);
    dhcp_fine.attach_us(&dhcp_fine_tmr, DHCP_FINE_TIMER_MSECS * 1000);

    while (!netif_is_up(netif)) { 
         device_poll(); 
    } 
    printf("Setup OK\n");
    printf("mbed IP Address is: %d.%d.%d.%d\n", (netif->ip_addr.addr)&0xFF, (netif->ip_addr.addr>>8)&0xFF, (netif->ip_addr.addr>>16)&0xFF, (netif->ip_addr.addr>>24)&0xFF);

    /* Bind a function to a tcp port */
    struct tcp_pcb *pcb = tcp_new();
    if (tcp_bind(pcb, IP_ADDR_ANY, 502) == ERR_OK) {
        pcb = tcp_listen(pcb);
        tcp_accept(pcb, &accept_callback);
    }
}