F103 modbus TCP

Dependencies:   BufferedSerial BufferedSoftSerial CRC16 Nucleo_F103RB_W5500HelloWorld MessageQueue WIZnet_Library Watchdog eeprom mbed-rtos mbed

Fork of Nucleo_F103RB_W5500HelloWorld by WIZnet

main.cpp

Committer:
dragyu
Date:
2016-11-01
Revision:
3:ec57904a3e81
Parent:
2:2541ce0cc63e

File content as of revision 3:ec57904a3e81:

#include "mbed.h"
#include "WIZnetInterface.h"
#include <string>
#include "Serial.h"
#include "eeprom.h"
#include "CRC16.h"
#include "Watchdog.h"
#include "rtos.h"
//Serial mdb232(PA_9,PA_10);
//Serial mdb485(PA_2,PA_3);
Serial *pc;//
DigitalOut pcSendRev(PB_0);//0 rev  1 send
Watchdog wd;
bool reset_soft = false;// 控制停止喂狗使设备复位
struct CONFIG{
    char ipAddr[16];
    char ipSubnet[16];
    char ipGateway[16];
    int16_t port;
    unsigned char MAC_Addr[6] ;
    
    int8_t RTUChoice;       //1:RTU模式 0:ascii
    int8_t bits;            //5-8bit
    SerialBase::Parity parity;        //  SerialBase::Odd , SerialBase::Even  SerialBase::None    分别用int 1,2,3代表
    int8_t stop_bits;       //1-2bit
    int16_t baud;
    int16_t frameTimeOut;
    int16_t timeOut;
    int16_t RS  ;              //485 232
    }Config_t = {             //默认值
        "192.168.31.3",
        "255.255.255.0",
        "192.168.31.1",
        (int16_t)502,       //port
        {0x00,0x08,0xDC,0x12,0x34,0x56},//mac地址
        1,                  //choice RTU mode
        8,                  //数据位8位
        SerialBase::None,
        1,                  //stop
        (int16_t)19200,      //baud
        7 ,                  //帧超时7ms
        1500  ,                 //等待超时1500ms
        485
        }; 
CONFIG Config = Config_t;//备份一个配置信息。         
static  char buffer[256] ;           //接收或者发送用的临时buffer
uint16_t buffer_num = 6;
struct TCP_BUFFER{          //
    char xid[2];
    char pid[2];
    int16_t length;
    char serialAddr;
    char data[255];
    }tcp_buffer;
    
struct SERIAL_BUFFER{
    char serialAddr;        //地址
    char funCode;           //功能码
    char data[252];
    char crc[2];            //低位前高位后
    }serial_buffer;
    
enum flagThread1{
    waitThead1,
    connectedThead1,
    tcpRecvThead1,
    serialRecvThead1,
    tcpSendThead1
    }flagThread1;            //状态机标识  thread1
    
enum FlagThead2{
     waitThead2,
     connectedThead2,
     handleDataThead2,
     getReguestThead2,
     postReguestThead2
     }flagThread2;           //状态机标识 thread2
char readline[200];    //解析提交的表单 读取一行数据 再存在这 
SPI spi(PA_7, PA_6, PA_5); // mosi, miso, sclk
WIZnetInterface ethernet(&spi, PB_6, PB_7);//scs(PB_6), nRESET(PB_7); // reset pin is dummy, don't affect any pin of WIZ550io
EEPROM memory(I2C_SDA,I2C_SCL,0,EEPROM::T24C02);     
   
TCPSocketServer webserver;
TCPSocketConnection webclient;
TCPSocketServer server;
TCPSocketConnection client;  
int rev_num;//tcp收到的直接数     
  
/*****************************计算超时用到的函数及变量*************************************************/
bool flagT35 = false; //是否接受了串口数据标志  ture 接收到串口数据
Timer timeout; //1s 超时
Timer timeoutT35;//t3.5超时
void timeoutTest();    //串口中断服务程序

/*********************状态机处理函数*************************************/

void InitDefault(void);
void InitEEprom(void);


void TcpRecvThead1(void);
void SerialRecvThead1(void);
void TcpSendThead1(void);

int webclientReadline();
int readSubmit( char *d,char a);
void webServerHandleData (void const *args)
{
    string sbuffer;
    char *cbuffer = new char[100];
    int numRev;
    string sget = "GET";
    string spost = "POST";
    while(true) {
        Thread::signal_wait(0x2);
        if ( flagThread2 ==  handleDataThead2 ) {
            sbuffer.clear();
            numRev= webclient.receive(cbuffer,3);
            if (numRev == 3) {
                sbuffer.append(cbuffer,3);
                if (sbuffer == sget) {
                    printf("get\r\n");
                    flagThread2 = getReguestThead2;
                    do {
                        numRev = webclient.receive(cbuffer,100);
                    } while(numRev == 100) ;
                }//清空rev buffer
                else {
                    webclient.receive(cbuffer,1);
                    sbuffer.append(cbuffer,1);
                    if(sbuffer == spost) {
                        printf("post\r\n");
                        flagThread2 = postReguestThead2;
                    } else {
                        printf("no\r\n");
                        flagThread2 = waitThead2;
                        webclient.close();
                    }
                }
            } else
                flagThread2 = waitThead2;
        }
    }
}

void webServerGetReguest(void const *args)
{
    string sbuffer;
    char str[10];
    sbuffer.assign("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n");
    sbuffer.append("Connection: close\r\n\r\n<!DOCTYPE HTML>\r\n");
    sbuffer.append("<html>\r\n<form id = \"muForm\"action=\"/config\"method=\"post\">\r\n");
    sbuffer.append("<table id=\"attfile\">\r\n<tr><td>NETWORK</td></tr>");

    sbuffer.append("<tr><td>IP</td>\r\n<td><input type=\"text\"name=\"IP\"size=\"20\"value=\"");
    sbuffer.append(ethernet.getIPAddress());
    sbuffer.append("\"></td><td> e.g. 192.168.1.55</td></tr>");

    sbuffer.append("<tr><td>MASK</td><td><input type=\"text\" name=\"MASK\" size=\"20\"value=\"");
    sbuffer.append(Config.ipSubnet);
    sbuffer.append("\"></td><td> e.g. 255.255.255.0</td></tr>");

    sbuffer.append("<tr><td>gateway</td> <td><input type=\"text\" name=\"gateway\" size=\"20\"value=\"");
    sbuffer.append(Config.ipGateway);
    sbuffer.append("\"></td><td> e.g. 192.168.1.1</td></tr>");

    sbuffer.append("<tr><td>PORT</td> <td><input type=\"text\" name=\"PORT\" size=\"20\"value=\"");
    sprintf(str,"%d",Config.port);
    sbuffer.append(str);
    sbuffer.append("\"></td><td> e.g. 502</td></tr>");

    sprintf(str,"%d",Config.baud);
    sbuffer.append("<tr><td>Serial</td> </tr><tr><td>baud</td> <td><input type=\"text\" name=\"baud\"size=\"20\"value=\"");
    sbuffer.append(str);
    sbuffer.append("\"></td><td> e.g. 115200 19200 9600</td> </tr>");


    sbuffer.append("<tr><td>parity</td> <td><input type=\"text\"name=\"parity\"size=\"20\"value=\"");
    switch(Config.parity) {
        case SerialBase::Odd:
            sbuffer.append("odd");
            break;
        case SerialBase::Even:
            sbuffer.append("even");
            break;
        case SerialBase::None:
            sbuffer.append("none");
            break;
    }

    sbuffer.append("\"></td><td> e.g. none even odd</td> </tr>");

    sbuffer.append("<tr><td>bits</td> <td><input type=\"text\"name=\"bits\"size=\"20\"value=\"");
    int t;
    if(Config.parity != SerialBase::None)
       t = Config.bits -1;
    else 
        t=Config.bits;
    sprintf(str,"%d",t);
    sbuffer.append(str);
    sbuffer.append("\"></td><td> e.g. 7-8</td> </tr>");
    
    
    sbuffer.append("<tr><td>stop_bits</td> <td><input type=\"text\"name=\"stop_bits\"size=\"20\"value=\"");
    sprintf(str,"%d",Config.stop_bits);
    sbuffer.append(str);
    sbuffer.append("\"></td><td> e.g. 1-2</td> </tr>");

    sbuffer.append("<tr><td>The frame timeout</td> <td><input type=\"text\"name=\"t35\"size=\"20\"value=\"");
    sprintf(str,"%d",Config.frameTimeOut);
    sbuffer.append(str);
    sbuffer.append("\"></td><td> ms</td> </tr>");

    sbuffer.append("<tr><td>timeout</td> <td><input type=\"text\"name=\"timeout\"size=\"20\"value=\"");
    sprintf(str,"%d",Config.timeOut);
    sbuffer.append(str);
    sbuffer.append("\"></td><td> ms</td></tr>");

    sbuffer.append("<tr><td>232 or 485</td> <td><input type=\"text\" name=\"232or485\" size=\"20\"value=\"");
    sprintf(str,"%d",Config.RS);
    sbuffer.append(str);
    sbuffer.append("\"></td><td> e.g. 232 485</td> </tr>");

    sbuffer.append("</table><input type = \"button\"onclick= \"formSubmit()\" value=\"submit\">");
    sbuffer.append("</form><script type=\"text/javascript\">");

    sbuffer.append("function formSubmit()");
    sbuffer.append("{document.getElementById(\"muForm\").submit()}");
    sbuffer.append("</script></html>");

    while(true) {
        Thread::signal_wait(0x3);
        if(flagThread2 == getReguestThead2 ) {
            webclient.send_all((char *)sbuffer.c_str(),sbuffer.length());
            webclient.close();
            flagThread2 = waitThead2;
        }
    }
}


void modbus_tcp_rs(void const *args)
{
    while(true) {

       if(flagThread1 == connectedThead1) {
            

          
            pcSendRev = 1;//send
            rev_num = client.receive(buffer,6);
          
            if(rev_num == 6) {
                //   client.send_all("connectedThead1",15);
                tcp_buffer.xid[0] =  buffer[0];
                tcp_buffer.xid[1] =  buffer[1];
                tcp_buffer.pid[0] =  buffer[2];
                tcp_buffer.pid[1] =  buffer[3];
                tcp_buffer.length =  buffer[4];
                tcp_buffer.length =  tcp_buffer.length <<8;
                tcp_buffer.length =  tcp_buffer.length | buffer[5];
                rev_num = client.receive(buffer,tcp_buffer.length);
                tcp_buffer.serialAddr =  buffer[0];
                if (rev_num == tcp_buffer.length){
                    TcpRecvThead1(); //处理tcp接收到的数据包 串口发送出去
                    SerialRecvThead1();//等待接收串口数据
                    if(flagThread1 == tcpSendThead1){
                        tcp_buffer.length = buffer_num-8;//前面空了6个 后面2个crc去掉
                        buffer[0] = tcp_buffer.xid[0];
                        buffer[1] = tcp_buffer.xid[1];
                        buffer[2] = tcp_buffer.pid[0];
                        buffer[3] = tcp_buffer.pid[1];
                        buffer[4] = tcp_buffer.length>>8 & 0xff;
                        buffer[5] = tcp_buffer.length & 0xff;
                        client.send_all(buffer,buffer_num-2);//打包发送 mdb tcp
                        flagThread1 = connectedThead1;
                    }  
                }
            }
       }
     Thread::wait(200);
    }
}


void webServerPost(void const *args)
{   string spost;
    char str[10];
    spost.assign("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n");
    spost.append("Connection: close\r\n\r\n<!DOCTYPE HTML>\r\n");
    spost.append("<html>reset now!! Remember !!! IP:");
    
    while(1) {
        Thread::signal_wait(0x05);
        if(flagThread2 == postReguestThead2) {
         //-----------读取表单参数--------------------------   
           
            while(webclientReadline());//返回 0 结束  后面的就是提交的表单内容 ip=192.168.1.3....
            
            readSubmit(readline,'=');//"IP"之类的  
                  
            readSubmit(Config.ipAddr,'&');                                                                                    
            readSubmit(readline,'=');//"mask"之类的            
            readSubmit(Config.ipSubnet,'&');
            readSubmit(readline,'=');//"gateway"之类的          
            readSubmit(Config.ipGateway,'&');
            readSubmit(readline,'=');//"PORT"
            readSubmit(readline,'&');           
            Config.port= (int16_t)atoi(readline);            
            readSubmit(readline,'=');//"baud"
            readSubmit(readline,'&');
            Config.baud= (int16_t)atoi(readline);    
            readSubmit(readline,'=');//"parity"           
            readSubmit(readline,'&');
            string s = readline;            
            if(s == "none" )
            Config.parity= SerialBase::None;
            if(s == "even" )
            Config.parity= SerialBase::Even;
            if(s == "odd" )
            Config.parity= SerialBase::Odd;
            readSubmit(readline,'=');//"bits"
            readSubmit(readline,'&');    
            Config.bits= (int8_t)atoi(readline);
            readSubmit(readline,'=');//"stop_bits"
            readSubmit(readline,'&');    
            Config.stop_bits= (int8_t)atoi(readline);   
            readSubmit(readline,'=');//"T35"
            readSubmit(readline,'&');     
            Config.frameTimeOut= (int16_t)atoi(readline);  
            readSubmit(readline,'=');//"timeout"
            readSubmit(readline,'&');     
            Config.timeOut= (int16_t)atoi(readline); 
            readSubmit(readline,'=');//"232or485"
            readSubmit(readline,'&');     
            Config.RS= (int16_t)atoi(readline); 
    

//--------------------发送更新后的 页面 ip port-----------------            
            spost.append(Config.ipAddr);            
            spost.append("    PORT:");
            sprintf(str,"%d",Config.port);
            spost.append(str);
            spost.append("</html>");
            webclient.send_all((char *)spost.c_str(),spost.length());
            webclient.close();
            
       //---------- 存进eeprom 参数-----------------------
          
          memory.write(1,0xaa);
          wait_ms(2);
          memory.write(32,Config.ipAddr,8);
          
          wait_ms(2);
          memory.write(40,Config.ipAddr+8,8);
          wait_ms(2);
          memory.write(48,Config.ipSubnet,8);
          wait_ms(2);
          memory.write(56,Config.ipSubnet+8,8);
          wait_ms(2);
          memory.write(64,Config.ipGateway,8);
          wait_ms(2);
          memory.write(72,Config.ipGateway+8,8);
          wait_ms(2);          
          memory.write(80,Config.MAC_Addr,6);
          wait_ms(2);
          memory.write(2,Config.port);
          wait_ms(2);
         
          memory.write(7,Config.bits);
          wait_ms(2);
          if(Config.parity == SerialBase::Odd)           
          memory.write(4,int8_t(1));
          if(Config.parity == SerialBase::Even)
          memory.write(4,int8_t(2));
          if(Config.parity == SerialBase::None)
          memory.write(4,int8_t(3)); 
          wait_ms(2);        
          memory.write(8,Config.stop_bits);
          wait_ms(2);
          memory.write(5,Config.baud); 
          wait_ms(2);
          memory.write(10,Config.RS); 
          wait_ms(2);
          //memory.write(12,Config.frameTimeOut); 
         // wait_ms(2);
          memory.write(14,Config.timeOut);          
          flagThread2 = waitThead2;         
          reset_soft = true; //复位
               

       } 
    }
}

//----------------- 以特殊字符结尾的字符串-----------
int readSubmit(char *d,char a)
{
    int num = 0;
    while(1) {
        //截取 等号 & 前的字符串

        if(-1 == webclient.receive(&d[num],1)) {
            d[num] = '\0';
            return num;
        }
        if(d[num] != a)
            num++;
        else {
            d[num] = '\0';
            return num;
        }

    }
}








//----------返回一行的字节数--------------
int webclientReadline()
{
    int num=0;
    while(1) {
        if( -1 == webclient.receive(&readline[num],1))
            return -1;
        if (readline[num] != '\r') 
        num++;
        else {
            if(-1 == webclient.receive(&readline[num],1))
                return -1;
            if (readline[num] == '\n') ;
            return num;
        }
    }
}




//--------------喂狗-----------------------

void wd_server(void const *args) {   
    while(true){
    if (!reset_soft)
            wd.Service();
     Thread::wait(2000);       
            }
}


int main() {
    int16_t t232or485 =0;
    memory.read(10,t232or485);
    wait_ms(1);
    
    if ((uint16_t)t232or485 == (uint16_t)232)
        pc = new Serial(PA_9,PA_10);//232
    else 
        pc = new Serial(PA_2,PA_3); //485
        
    pcSendRev = 1;//send
    if(wd.WatchdogCausedReset())
        printf("Watchdog caused reset.\r\n");
    int8_t initChoice;
    memory.read(1,initChoice); //查看一下eeprom是否初始化
    wait_ms(1);
    printf("%d",t232or485);
    printf("int:%c\r\n",initChoice);
    if( 0xAA == (uint8_t)initChoice )//已经初始化
        {printf("aa\r\n");
        InitEEprom();}
    else
        InitDefault();  
    
 //------------listen 502  80  -----------
    
    server.bind(Config.port);   //502
    server.listen();
    server.set_blocking(false, 0);
    webserver.bind(80);
    webserver.listen();
    webserver.set_blocking(false, 0);
    client.set_blocking(false, 0); // Timeout=0.
    webclient.set_blocking(false, 0);
    flagThread1 = waitThead1;   //状态机 初状态
    flagThread2 = waitThead2;
    wd.Configure(4);       // sets the timeout interval 大约6.1s    
    //Thread thread1(wd_server) ;
    
    //---------------thread 开启 -----------------
    Thread thread2(webServerHandleData,(void *)"2",osPriorityHigh );
    Thread thread3(webServerGetReguest,(void *)"3",osPriorityHigh );
    Thread thread4(modbus_tcp_rs,(void *)"4",osPriorityAboveNormal);
    Thread thread5(webServerPost,(void *)"5",osPriorityRealtime );
    
     //-----------对线程管理----------socket accept-----
    while(1) {
        if (client.is_fin_received()) {
            client.close();
            flagThread1 = waitThead1;
        }
        if(flagThread1 == waitThead1) {
            server.accept(client);
            if((client.is_connected() == true)  ) {
                flagThread1 = connectedThead1;
            }
        }
        if (!reset_soft)
            wd.Service();
        Thread::wait(50);
        if(webclient.is_fin_received()) {
            webclient.close();
            flagThread2 = waitThead2;
        }

        if(flagThread2 == waitThead2) {
            webserver.accept(webclient);
            if((webclient.is_connected() == true)  ) {
                flagThread2 = handleDataThead2;
                thread2.signal_set(0x2);      //启动线程2 给他一个signal
            }
        }

        if (flagThread2 == getReguestThead2)
            thread3.signal_set(0x3);          //启动线程3 给他一个signal
        if(flagThread2 == postReguestThead2)
            thread5.signal_set(0x5);
        if (!reset_soft)
            wd.Service();   
        Thread::wait(50);


   
    }
}

void InitDefault(void)
{   pc->format(Config.bits,Config.parity,Config.stop_bits);
    pc->baud(Config.baud);
    int ret = ethernet.init(Config.MAC_Addr,Config.ipAddr,Config.ipSubnet,Config.ipGateway);
    if (!ret) {
        printf("Initialized, MAC: %s\r\n", ethernet.getMACAddress());
        ret = ethernet.connect();
        if (!ret) {
            printf("IP: %s, MASK: %s, GW: %s\r\n",
            ethernet.getIPAddress(), ethernet.getNetworkMask(), ethernet.getGateway());
            } else {
                printf("Error ethernet.connect() - ret = %d\r\n", ret);
                exit(0);
            }
        } else {
            printf("Error ethernet.init() - ret = %d\r\n", ret);
            exit(0);
        }

    pc->attach(&timeoutTest);
    }


void InitEEprom(void)
{
  
  int8_t t;
  memory.read(32,Config.ipAddr,16);
  wait_ms(2);
  memory.read(48,Config.ipSubnet,16);
  wait_ms(2);
  memory.read(64,Config.ipGateway,16);
  wait_ms(2);
  memory.read(80,Config.MAC_Addr,6);
  wait_ms(2);
  memory.read(2,Config.port);
  wait_ms(2);
  memory.read(7,Config.bits);
  wait_ms(2);
  memory.read(4,t);
    if( t==1 )
        Config.parity = SerialBase::Odd;
    if( t==2 )
        Config.parity = SerialBase::Even;
    if(t == 3)
        Config.parity = SerialBase::None;
    wait_ms(2);
  memory.read(8,Config.stop_bits);
  wait_ms(2);
  memory.read(5,Config.baud);
  wait_ms(2);
  memory.read(10,Config.RS);
  wait_ms(2);
 // memory.read(12,Config.frameTimeOut);
 // wait_ms(2);
  memory.read(14,Config.timeOut);
  wait_ms(2);
  InitDefault();
    }

    

void TcpRecvThead1(void)
{
    static CRC16 crc16;
    int16_t crc;
    crc = crc16.calculateCRC16(buffer,tcp_buffer.length);
    buffer[tcp_buffer.length+1] = crc & 0xff;
    buffer[tcp_buffer.length] = (crc >> 8) & 0xff;

   for (crc =0 ; crc<tcp_buffer.length+2;crc++)
        pc->putc(buffer[crc]);
 
   
    
    flagThread1 = serialRecvThead1;
    buffer_num = 6;
    timeout.start();        //开启1s 超时
    
    timeoutT35.start();
    wait_ms(3);
    }

void SerialRecvThead1(void)
{
    static int j = 0;
    //char t;
    static CRC16 crc16;
    int16_t crc;
    pcSendRev = 0;//Rev
    while(flagThread1 == serialRecvThead1) {
        if( Config.timeOut < timeout.read_ms() ) {
            pcSendRev = 1;//Send
            timeout.reset();
            timeout.stop();
            timeoutT35.reset();
            timeoutT35.stop();
            flagThread1 =  connectedThead1;
          /*  buffer[7] = buffer[1]|0x80;   //存的功能码 上一步
            buffer[0] = tcp_buffer.xid[0];
            buffer[1] = tcp_buffer.xid[1];
            buffer[2] = tcp_buffer.pid[0];
            buffer[3] = tcp_buffer.pid[1];
            buffer[4] = 0x0;
            buffer[5] = 0x3;
            buffer[6] = tcp_buffer.serialAddr;
            buffer[8] = 0x4;
            client.send_all(buffer,9);//报错
            */
        }
        if( Config.frameTimeOut < timeoutT35.read_ms() && flagT35 ) { // 一帧数据结束 7ms超时
            timeoutT35.reset();
            timeoutT35.stop();
            flagT35 = false;
            //      t = buffer[buffer_num-1];buffer[buffer_num-1]=buffer[buffer_num-2];buffer[buffer_num-2]=t;
            crc = crc16.calculateCRC16(buffer+6,buffer_num-6) ;  //为0没有crc错误
            if (!crc ) {    //无crc 提取mdb帧数据
                serial_buffer.serialAddr = buffer[6];
                serial_buffer.funCode = buffer[7];
                for(j = 0; j < buffer_num-4 ; j++)
                    serial_buffer.data[j] = buffer[j+8];
                serial_buffer.crc[0] = buffer[buffer_num-2];
                serial_buffer.crc[1] = buffer[buffer_num-1];
                //   buffer_num = 0;//提取完一帧数据 计数清零
                flagThread1 =  tcpSendThead1;   //进入下一个状态
            } else          //有crc 错误
                flagThread1 =  connectedThead1;
        }
    }
}

void timeoutTest()  //串口中断
{
    if(flagThread1 !=serialRecvThead1)
        buffer[245]=pc->getc();//丢弃串口数据包
    else {
        buffer[buffer_num++] = pc->getc();
        flagT35  = true ;      //标志已经接收到串口数据
        timeoutT35.reset();  //重新及时只看最后一个接受的字符之后的时间 总线空闲检测
        timeout.stop();
    }     //1s接受超时停止计时

}