F103 modbus TCP
Dependencies: BufferedSerial BufferedSoftSerial CRC16 Nucleo_F103RB_W5500HelloWorld MessageQueue WIZnet_Library Watchdog eeprom mbed-rtos mbed
Fork of Nucleo_F103RB_W5500HelloWorld by
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接受超时停止计时 }