BACnet协议栈移植分析之三:mstpsnap.c

mstpscap.c这个文件主要实现从串口上抓取MS/TP协议上的packet功能。测试时的格式为:mstsnap [serial]  [baud]  [network]

默认参数是串口/dev/ttyUSB0、波特率是38400,eth0。

#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
/* OS specific include*/
#include "net.h"
#include "timer.h"
/* local includes */
#include "bytes.h"
#include "rs485.h"
#include "crc.h"
#include "mstp.h"
#include "dlmstp.h"
#include "mstptext.h"
#include "bacint.h"

/** @file linux/mstpsnap.c  Example application testing BACnet MS/TP on Linux. */

#ifndef max
#define max(a,b) (((a) >(b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif

/* local port data - shared with RS-485 */
static volatile struct mstp_port_struct_t MSTP_Port;
/* buffers needed by mstp port struct */
static uint8_t RxBuffer[MAX_MPDU];
static uint8_t TxBuffer[MAX_MPDU];
static uint16_t Timer_Silence(
    void)
{
    uint32_t delta_time = 0;

    delta_time = timer_milliseconds(TIMER_SILENCE);
    if (delta_time > 0xFFFF) {
        delta_time = 0xFFFF;
    }

    return (uint16_t) delta_time;
}

static void Timer_Silence_Reset(
    void)
{
    timer_reset(TIMER_SILENCE);
}

/* functions used by the MS/TP state machine to put or get data */
uint16_t MSTP_Put_Receive(
    volatile struct mstp_port_struct_t *mstp_port)
{
    (void) mstp_port;

    return 0;
}

/* for the MS/TP state machine to use for getting data to send */
/* Return: amount of PDU data */
uint16_t MSTP_Get_Send(
    volatile struct mstp_port_struct_t * mstp_port,
    unsigned timeout)
{       /* milliseconds to wait for a packet */
    (void) mstp_port;
    (void) timeout;
    return 0;
}

uint16_t MSTP_Get_Reply(
    volatile struct mstp_port_struct_t * mstp_port,
    unsigned timeout)
{       /* milliseconds to wait for a packet */
    (void) mstp_port;
    (void) timeout;
    return 0;
}

static int network_init(
    const char *name,
    int protocol)
{
    /* check to see if we are being run as root */
    if (getuid() != 0) {                   
//函数返回一个调用程序的真实用户ID,一般来说,这个函数都是会调用成功的。getuid=0表示root用户
        fprintf(stderr, "Requires root priveleges.\n");
        return -1;
    }

    int sockfd = socket(PF_PACKET, SOCK_RAW, htons(protocol));             //得到原始的IP包,htons得到网络字节序(大端模式)网络上统一使用大端模式传输
       if (sockfd == -1) {
        perror("Unable to create socket");
        return sockfd;
    }

    struct ifreq ifr;  //获取IP地址和MAC地址

    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, name, strlen(name));

    if (ioctl(sockfd, SIOCGIFINDEX, &ifr) == -1) {      //获取并打印网卡地址
        perror("Unable to get interface index");
        return -1;
    }

    struct sockaddr_ll sll;    //本地地址

    memset(&sll, 0, sizeof(sll));
    sll.sll_family = AF_PACKET;
    sll.sll_ifindex = ifr.ifr_ifindex;
    sll.sll_protocol = htons(protocol);

    if (bind(sockfd, (struct sockaddr *) &sll, sizeof(sll)) == -1) {       //绑定套接字
        perror("Unable to bind socket");
        return -1;
    }

    return sockfd;
}

static void snap_received_packet(
    volatile struct mstp_port_struct_t *mstp_port,
    int sockfd)
{
    uint16_t mtu_len = 0;       /* number of octets of packet saved in file */
    unsigned i = 0;     /* counter */
    static uint8_t mtu[1500] = { 0 };          
//考虑到物理层实现等原因,以太网一帧的长度一般要求小于1500字节
    uint16_t max_data = 0;

    mtu[0] = 0;
    mtu[1] = 0;
    mtu[2] = 0;
    mtu[3] = 0;
    mtu[4] = 0;
    mtu[5] = mstp_port->DestinationAddress;
    mtu[6] = 0;
    mtu[7] = 0;
    mtu[8] = 0;
    mtu[9] = 0;
    mtu[10] = 0;
    mtu[11] = mstp_port->SourceAddress;
    /* length - 12, 13 */
    mtu[14] = 0xaa;     /* DSAP for SNAP */
    mtu[15] = 0xaa;     /* SSAP for SNAP */
    mtu[16] = 0x03;     /* Control Field for SNAP */
    mtu[17] = 0x00;     /* Organization Code: Cimetrics */
    mtu[18] = 0x10;     /* Organization Code: Cimetrics */
    mtu[19] = 0x90;     /* Organization Code: Cimetrics */
    mtu[20] = 0x00;     /* Protocol ID */
    mtu[21] = 0x01;     /* Protocol ID */
    mtu[22] = 0x00;     /* delta time */
    mtu[23] = 0x00;     /* delta time */
    mtu[24] = 0x80;     /* unknown byte */
    mtu[25] = mstp_port->FrameType;
    mtu[26] = mstp_port->DestinationAddress;
    mtu[27] = mstp_port->SourceAddress;
    mtu[28] = HI_BYTE(mstp_port->DataLength);
    mtu[29] = LO_BYTE(mstp_port->DataLength);
    mtu[30] = mstp_port->HeaderCRCActual;
    mtu_len = 31;
    if (mstp_port->DataLength) {                       
//分别往里面存放从输入缓存器中的数据、DataCRCActual(先高位后低位顺序)
        max_data = min(mstp_port->InputBufferSize, mstp_port->DataLength);
        for (i = 0; i < max_data; i++) {
            mtu[31 + i] = mstp_port->InputBuffer[i];
        }
        mtu[31 + max_data] = mstp_port->DataCRCActualMSB;
        mtu[31 + max_data + 1] = mstp_port->DataCRCActualLSB;
        mtu_len += (max_data + 2);
    }
    /* Ethernet length is data only - not address or length bytes */
    encode_unsigned16(&mtu[12], mtu_len - 14);  
//给整数类型编码
    (void) write(sockfd, &mtu[0], mtu_len);
}


static void cleanup(
    void)
{
}

#if (!defined(_WIN32))
static void sig_int(
    int signo)
{
    (void) signo;

    cleanup();
    exit(0);
}

void signal_init(
    void)
{
    signal(SIGINT, sig_int);
    signal(SIGHUP, sig_int);
    signal(SIGTERM, sig_int);
}
#endif

/* simple test to packetize the data and print it */
int main(int argc,char *argv[])                  //main函数实现在Linux 终端运行root#mstpsnap /dev/tty/USB0 38400 eth0
{
    volatile struct mstp_port_struct_t *mstp_port;
    long my_baud = 38400;
    uint32_t packet_count = 0;
    int sockfd = -1;
    char *my_interface = "eth0";

    /* mimic our pointer in the state machine */
    mstp_port = &MSTP_Port;
    if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) {
        printf("mstsnap [serial] [baud] [network]\r\n"
            "Captures MS/TP packets from a serial interface\r\n"
            "and sends them to a network interface using SNAP \r\n"
            "protocol packets (mimics Cimetrics U+4 packet).\r\n" "\r\n"
            "Command line options:\r\n" "[serial] - serial interface.\r\n"
            "    defaults to /dev/ttyUSB0.\r\n"
            "[baud] - baud rate.  9600, 19200, 38400, 57600, 115200\r\n"
            "    defaults to 38400.\r\n" "[network] - network interface.\r\n"
            "    defaults to eth0.\r\n"
"");
        return 0;
    }
    /* initialize our interface */
    if (argc > 1) {
        RS485_Set_Interface(argv[1]);
    }
    if (argc > 2) {
        my_baud = strtol(argv[2], NULL, 0); 
//把argv[2]按照十进制转换
    }
    if (argc > 3) {
        my_interface = argv[3];
    }
    sockfd = network_init(my_interface, ETH_P_ALL);
    if (sockfd == -1) {
        return 1;
    }                    //设置一些MS/TP协议里面的内容参数,这部分要求熟悉MS/TP协议内容,具体可参考教材<智能楼宇BACnet原理与控制>
    RS485_Set_Baud_Rate(my_baud);
    RS485_Initialize();
    MSTP_Port.InputBuffer = &RxBuffer[0];
    MSTP_Port.InputBufferSize = sizeof(RxBuffer);
    MSTP_Port.OutputBuffer = &TxBuffer[0];
    MSTP_Port.OutputBufferSize = sizeof(TxBuffer);
    MSTP_Port.This_Station = 127;
    MSTP_Port.Nmax_info_frames = 1;
    MSTP_Port.Nmax_master = 127;
    MSTP_Port.SilenceTimer = Timer_Silence;
    MSTP_Port.SilenceTimerReset = Timer_Silence_Reset;
    MSTP_Init(mstp_port);
    fprintf(stdout, "mstpcap: Using %s for capture at %ld bps.\n",
        RS485_Interface(), (long) RS485_Get_Baud_Rate());
    atexit(cleanup);
#if defined(_WIN32)
    SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_PROCESSED_INPUT);
    SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlCHandler, TRUE);
#else
    signal_init();
#endif
    /* run forever */
    for (;;) {
        RS485_Check_UART_Data(mstp_port);
        MSTP_Receive_Frame_FSM(mstp_port);
        /* process the data portion of the frame */
        if (mstp_port->ReceivedValidFrame) {
            mstp_port->ReceivedValidFrame = false;
            snap_received_packet(mstp_port, sockfd);
            packet_count++;
        } else if (mstp_port->ReceivedInvalidFrame) {
            mstp_port->ReceivedInvalidFrame = false;
            fprintf(stderr, "ReceivedInvalidFrame\n");
            snap_received_packet(mstp_port, sockfd);
            packet_count++;
        }
        if (!(packet_count % 100)) {
            fprintf(stdout, "\r%hu packets", packet_count);
        }
    }

    return 0;
}

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
STM32 BACnet协议是指在STM32微控制器上实现的一种与建筑自动化控制网络相关的通讯协议。BACnet是由美国自动化技术和控制系统协会(ASHRAE)制定的一套通信协议标准,用于建筑设备之间的数据传输。现在,很多建筑自动化系统都采用BACnet协议,因为它具有良好的可扩展性和互操作性。 STM32是一系列由ST公司设计和生产的32位ARM Cortex-M微控制器,广泛应用于嵌入式系统开发中。STM32微控制器具有丰富的外设和高性能的处理能力,可以满足各种应用需求。 STM32微控制器通过各种接口和协议与外部设备进行通信,包括串口、SPI、I2C等。在实现STM32 BACnet协议时,通常会使用其中的串口或以太网接口与BACnet网络进行连接。 实现STM32 BACnet协议需要开发者掌握BACnet协议的相关知识,并在STM32上进行软件开发。通过STM32的开发环境和相应的软件库,开发者可以编写适用于STM32的BACnet协议栈,并将其应用在具体的建筑自动化系统中。 实现STM32 BACnet协议的好处是能够快速、可靠地与现有的BACnet设备进行通信,实现数据的读取和控制。通过该协议,建筑自动化系统可以实现智能化、自动化的管理和控制,提高能源利用效率,降低能源消耗和运营成本。 总之,STM32 BACnet协议是一种在STM32微控制器上实现的与建筑自动化控制网络相关的通讯协议,通过该协议可以实现与BACnet设备的快速、可靠通信,从而实现建筑自动化管理的智能化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值