精华内容
下载资源
问答
  • 欧姆龙CJ1M系列以太网项目及PLC间串口通信编程应用实例 介绍了关于欧姆龙CJ1M系列以太网项目及PLC间串口通信编程应用实例的详细说明,提供PLC的技术资料的下载。
  • 所以今天就给大家带来CC-LINK通讯编程实例。如果看完还是不太明白怎么办?海蓝现开设技术交流3群,目前入群不设门槛,欢迎各位技术小哥哥加入交流,进群关注私信我就可以了。下面一起学习吧。1.CC-LINK连接CC-LINK...

    前言:小蓝上次分享了三菱PLC编程指令文章,有一位粉丝说还有标签,FB模块,以太网通讯,MODBUS,TCP通讯这些有的学的。所以今天就给大家带来CC-LINK通讯编程实例。如果看完还是不太明白怎么办?海蓝现开设技术交流3群,目前入群不设门槛,欢迎各位技术小哥哥加入交流,进群关注私信我就可以了。下面一起学习吧。

    1.CC-LINK连接

    CC-LINK连接图如下:

    d1986b8e978a1c8482a8356c9caa2efd.png

    三菱PLC CC-LINK通讯编程实例分享,看完你就会了!

    对应的PLC可为FX1N、FX1NC、FX2N、FX2NC、FX3U、FX3UC,因为在使用CC-LINK通讯时要扩展CC-LINK模块,而FX1S没有扩展模块功能,故FX1S不能用于此通讯方式。

    2)FX1N/FX2N/FX3U即可以作为主站,也可以作为远程设备站使用。

    此种通讯因为要加CC-LINK通讯模块,所以成本较高。

    3)在CC-LINK网络中还可以加入变频器伺服等符合CC-LINK规格的设备。

    2.N:N网络连接

    N:N网络连接连接图如下:

    b34778d0a009206d50fe71bda860a32b.png

    三菱PLC CC-LINK通讯编程实例分享,看完你就会了!

    3.并联连接

    并联连接图如下:

    5cddd3dc9d51fe87e156ffdf389d8a1a.png

    三菱PLC CC-LINK通讯编程实例分享,看完你就会了!

    1) 通讯对象是FX1S、FX1N、FX1NC、FX2N、FX2NC、FX3U、FX3UC同一系列的PLC之间。

    2) 该通讯在PLC间进行1:1通讯,进行数据交换,只能满足2台PLC的通讯。

    一、N:N网络通讯

    1、通讯解决方案

    用FX2N,FX2NC,FX1N,FXON可编程控制器进行的数据传输可建立在N:N的基础上。使用此网络通讯,它们能链接—个小规模系统中的数据。

    2、相关标志和数据寄存器

    对于FXlN/FX2N/FX2NC类可编程控制器,使用N:N网络通讯辅助继电器,其中M8038用来设置网络参数,

    M8183在主站点的通讯错误时为ON,

    M8184到M8190在从站点产生错误时为ON,(第1个从站点M8184,第7个从站点M8190)

    M8191在与其它站点通讯时为ON

    数据寄存器D8176设置站点号,0为主站点,1到7为从站点号D8177设定从站点的总数,设定值1为1个从站点,2为两个从站点;D8178设定刷新范围,0为模式0(默认值),1为模式1,2为模式2;D8179 主站设定通讯重试次数,设定值为0到10;D8180 设定主站点和从站点间的通讯驻留时间,设定值为5到255,对应时间为50到2550ms

    案例

    该系统有三个站点,其中一个主站,两个从站,每个站点的可编程控制器都连接一个FX2N-485-BD通讯板,通讯板之间用单根双绞线连接。刷新范围选择模式1,重试次数选择3,通信超时选50ms,系统要求:

    ①主站点的输入点X0到X3输出到从站点1和2的输出点Y10到Y13。

    ②从站点1的输入点X0到X3输出到主站和从站点2的输出点Y14到Y17。

    ③从站点2的输入点X0到X3输出到主站和从站点1的输出点Y20到Y23。

    主站点的梯形图编制

    48e6d53cdded8a397a7af11f18ca5b30.png

    三菱PLC CC-LINK通讯编程实例分享,看完你就会了!

    从站点1的梯形图编制

    b755136bac9ad5a0bcc4f331af467db9.png

    三菱PLC CC-LINK通讯编程实例分享,看完你就会了!

    从站点2的梯形图编制

    73d8975aed6edee209037452109538aa.png

    三菱PLC CC-LINK通讯编程实例分享,看完你就会了!

    二、并行链接通讯

    1、并行通讯解决方案

    用FX2N,FX2NC,FX1N,FX和FX2C可编程控制器进行数据传输时,是采用100个辅助继电器和10个数据寄存在 1:1的基础上来完成。FXlS和FXON的数据传输是采用50个辅助继电器和10个数据寄存器进行的。

    2、使用方法

    当两个FX系列的可编程控制器的主单元分别安装一块通讯模块后,用单根双绞线连接即可,编程时设定主站和从站,应用特殊继电器在两台可编程控制间进行自动的数据传送,很容易实现数据通讯连接。主站和从站的设定由M8070和M8071设定,另外并行连接有一般和高速两种模式,由M8162的通断识别。

    3、通讯实例

    在并行通讯系统中,控制要求如下:

    ①主站点输入X0到X7的ON/OFF状态输出到从站点的Y0到Y7。

    ②当主站点的计算结果(D0+D2)大于100,从站的Y10通。

    从站点的M0到M7的ON/OF状态输出到主站点的Y0到Y7。

    从站点中D10的值被用来设置主站点中定时器。

    主站点梯形图

    115610cffe52c63375d77ce6041724b3.png

    三菱PLC CC-LINK通讯编程实例分享,看完你就会了!

    三、计算机链接

    小型控制系统中的可编程控制器除了使用编程软件外,一般不需要与别的设备通讯。可编程控制器的编程器接口一般都是RS-422或RS-485,而计算机的串行通讯接口是RS-232C,编程软件与可编程控制器交换信息时需要配接专用的带转接电路的编程电缆或通讯适配器,例如为了实现编程软件与FX系列plc之间的程序传送,需要使用SC-09编程电缆。

    四、无协议通讯

    大多数可编程控制器都有一种串行口无协议通讯指令,如FX系列的RS指令,它们用于可编程控制器与上位计算机或其它RS-232C设备的通讯。这种通讯方式最为灵活,可编程控制器与RS-232C设备之间可以使用用户自定义的通讯规定,但是可编程控制器的编程工作量较大,对编程人员的要求较高。如果不同厂家的设备使用的通讯规定不同,即使物理接口都是RS-485,也不能将它们接在同一网络内,在这种情况下一台设备要占用可编程控制器的一个通讯接口。

    用各种RS232C单元,包括个人计算机,条形码阅读器和打印机,来进行数据通讯,可通过无协议通讯完成,此通讯使用RS指令或一个FX2N-232IF特殊功能模块完成。

    五、可选编程端口通讯

    现在的可编程终端产品(如三菱的GOT-900系列图形操作终端)一般都能用于多个厂家的可编程控制器。与组态软件一样,可编程终端与可编程控制器的通讯程序也不需要由用户来编写,在为编程终端的画面组态时,只需要指定画面中的元素(如按钮、指示灯)对应的可编程控制器编程元件的编号就可以了,二者之间的数据交换是自动完成的。

    对于FX2N,FX2NC,FX1N,FXlS系列的可编程控制器,当该端口连接在FX2N-232-BD,FXON—32ADP,FX1N—232—BD,FX2N-422—BD上时,可支持—个编程协议。

    今天的分享就到这里了,有什么想知道的知识欢迎大家后台私信给我,小编下次就知道该分享哪些知识点啦!或者你可以加入我们海蓝技术3群,里面有来自全国各地的技术大牛,一起交流学习技术。目前3群不设门槛进入,有需要的技术小哥哥,赶快私信“进群”给我吧。

    d11f2349fbc224dc6cbd50afe8d4a5e2.png

    三菱好产品,海蓝好服务!加入海蓝技术交流群,与行业大神一同成长。

    展开全文
  • /*name:ether.cfunc:print ether protocol mac address flowcompile: gcc ether.c -o ether -lpcapplatform:linux/unix */#include #include #include #include #include #include #de

     

    1. /*
    2. name:ether.c
    3. func:print ether protocol mac address flow
    4. compile: gcc ether.c -o ether -lpcap
    5. platform:linux/unix 
    6. */
    7. #include <pcap.h>
    8. #include <stdio.h>
    9. #include <netinet/if_ether.h>
    10. #include <stdlib.h>
    11. #include <unistd.h>
    12. #include <signal.h>
    13. #define MAXSTRINGSIZE    1500
    14. #define DEFAULT_SNAPLEN  68
    15. //STP protocol 
    16. u_int8_t DMAC[6]={0x01,0x80,0xC2,0x00,0x00,0x00};
    17. static int ether_counter=0;
    18. int tFlag=0;
    19.  
    20. //count the  size of the packet
    21. unsigned int arp_array[1024],ip_array[1024],rarp_array[1024],/
    22.         stp_array[1024],other_array[1024];
    23. unsigned int arp_c=0,ip_c=0,rarp_c=0,stp_c=0,other_c=0;
    24. double  arp_flow,ip_flow,rarp_flow,stp_flow,other_flow;
    25. #define HWADDR(addr) /
    26.     ((unsigned char *)&addr)[0], /
    27.     ((unsigned char *)&addr)[1], /
    28.     ((unsigned char *)&addr)[2], /
    29.     ((unsigned char *)&addr)[3], /
    30.     ((unsigned char *)&addr)[4], /
    31.     ((unsigned char *)&addr)[5]
    32. void usage();
    33. char *program_name;
    34. int cap_time=1;
    35. double cap_sum(unsigned int array[], unsigned int c)
    36. {
    37.         int i=0;
    38.         double sum=0;
    39.         for(;i<c;i++)
    40.         sum+=array[i];
    41.         return sum;
    42.         
    43. }
    44. void sig_alarm(int sig)
    45. {
    46.         arp_flow=cap_sum(arp_array,arp_c)/cap_time;
    47.         ip_flow=cap_sum(ip_array,ip_c)/cap_time;
    48.         rarp_flow=cap_sum(rarp_array,rarp_c)/cap_time;
    49.         stp_flow=cap_sum(stp_array,stp_c)/cap_time;
    50.         other_flow=cap_sum(other_array,other_c)/cap_time;
    51.         printf("/n--------------------network flux-----------------------/n");
    52.         printf("cap time: %d s/n", cap_time);
    53.         printf("packet count: %d/n", ether_counter);
    54.         printf("arp protocol: %lf bytes/s/n",arp_flow);
    55.         printf("ip protocol: %lf bytes/s/n", ip_flow);
    56.         printf("rarp protocol: %lf bytes/s/n", rarp_flow);
    57.         printf("stp protocol: %lf bytes/s/n", stp_flow);
    58.         printf("other protocol: %lf bytes/s/n", other_flow);
    59.         fflush(stdout);
    60.         exit(0);
    61. }
    62. void print_etherType(struct ether_header *eth,const struct pcap_pkthdr *h)
    63. {
    64.         int i=0;
    65.         u_char *p;
    66.         register char  *cp;
    67.         
    68.         switch(ntohs(eth->ether_type)){
    69.                 case ETHERTYPE_IP:
    70.                         ip_c++; 
    71.                         ip_array[ip_c-1]=h->len;             
    72.                         printf("IP");break;
    73.                 case ETHERTYPE_ARP:
    74.                         arp_c++;
    75.                         arp_array[arp_c-1]=h->len;
    76.                         printf("ARP");break;
    77.                 case ETHERTYPE_REVARP:
    78.                         rarp_c++;
    79.                         rarp_array[rarp_c-1]=h->len;
    80.                         printf("RARP");break;
    81.                 default:
    82.                         //printf("%x ", ntohs(eth->ether_type));     
    83.                         p = eth->ether_dhost;
    84.                         while( *(p+i) == *(DMAC+i) )
    85.                         {
    86.                         i++;
    87.                         if(i == 6)
    88.                         break;
    89.                         }                               
    90.                         if(i == 6){
    91.                                 stp_c++;
    92.                                 stp_array[stp_c-1]=h->len;
    93.                                 printf("STP");
    94.                                 }
    95.                         else{
    96.                                 other_c++;
    97.                                 other_array[other_c-1]=h->len;
    98.                                 printf("***");
    99.                                 }
    100.                         //break;
    101.                 }
    102.                 fflush(stdout);
    103.         
    104. }
    105. void eth_printer(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
    106. {
    107.         
    108.         struct ether_header *eth;
    109.         eth = (struct ether_header *)p;
    110.         ++ether_counter;
    111.         printf("%02X:%02X:%02X:%02X:%02X:%02X",
    112.           HWADDR(eth->ether_shost));
    113.         printf("->");
    114.         printf("%02X:%02X:%02X:%02X:%02X:%02X",
    115.           HWADDR(eth->ether_dhost));
    116.         printf("/t");
    117.         print_etherType(eth,h);
    118.         printf("/t%d", h->len);
    119.         printf("/n");
    120.         fflush(stdout); 
    121. }
    122. int main(int argc, char **argv)
    123. {
    124.         char ebuf[PCAP_ERRBUF_SIZE];
    125.         
    126.         register int op;//options      
    127.         
    128.         register char *device, *cp; //network interface list
    129.         
    130.         pcap_t *pd;
    131.         pcap_if_t *devpointer; //device list
    132.         pcap_if_t *d; //device list
    133.         int devnum; 
    134.         
    135.         int i=0;
    136.         if ((cp = strrchr(argv[0], '/')) != NULL)
    137.                 program_name = cp + 1;
    138.         else
    139.                 program_name = argv[0];
    140.         if(argc < 2)
    141.                 usage();
    142.                 
    143.         while ((op = getopt(argc, argv,"Di:t:")) != -1)
    144.         switch (op) {
    145.                 case 'D':
    146.                         if (pcap_findalldevs(&devpointer, ebuf) < 0)
    147.                                 fprintf(stderr,"Error in pcap_findalldevs_ex: %s/n", ebuf);
    148.                         else {
    149.                                 for (i = 0; devpointer != 0; i++) {
    150.                                         printf("%d.%s", i+1, devpointer->name);
    151.                                         if (devpointer->description != NULL)
    152.                                                 printf(" (%s)", devpointer->description);
    153.                                         printf("/n");
    154.                                         devpointer = devpointer->next;
    155.                                 }
    156.                         }
    157.                         return 0;
    158.                 case 'i':
    159.                         if (optarg[0] == '0' && optarg[1] == 0){
    160.                                 printf("Invalid adapter index");
    161.                                 usage();
    162.                                 }
    163.                                 
    164.                         
    165.                         if ((devnum = atoi(optarg)) != 0) {
    166.                                 if (devnum < 0)
    167.                                         fprintf(stderr, "Invalid adapter index");
    168.                                 if (pcap_findalldevs(&devpointer, ebuf) < 0)
    169.                                         fprintf(stderr,"Error in pcap_findalldevs: %s", ebuf);
    170.                                 else {
    171.                                         for (i = 0; i < devnum-1; i++){
    172.                                                 devpointer = devpointer->next;
    173.                                                 if (devpointer == NULL)
    174.                                                         printf("Invalid adapter index");
    175.                                         }
    176.                                 }
    177.                                 device = devpointer->name;
    178.                                 break;
    179.                         }
    180.                         device = optarg;
    181.                         break;
    182.                                                 
    183.              case 't':
    184.                 cap_time=atoi(optarg);
    185.                 tFlag=1;
    186.                 break;
    187.              default:
    188.                  usage();       
    189.                  break;
    190.                 
    191.         }
    192.                 
    193.         if((pd = pcap_open_live(device, DEFAULT_SNAPLEN, 1, 1000, ebuf)) == NULL)
    194.         {
    195.                 (void)fprintf(stderr, "pcap_loop: %s/n", pcap_geterr(pd));
    196.                 exit(1);
    197.         }
    198.         signal(SIGALRM, sig_alarm);
    199.         if(tFlag==1)
    200.         alarm(cap_time);
    201.         struct bpf_program fcode;
    202.         pcap_compile(pd, &fcode, NULL, 1, 0);
    203.         pcap_setfilter(pd, &fcode);
    204.                 if(pcap_loop(pd, -1, eth_printer, NULL) < 0){
    205.                 (void)fprintf(stderr, "pcap_loop: %s/n", pcap_geterr(pd));
    206.                 exit(1);
    207.         }
    208.         pcap_close(pd);
    209.         return 0;
    210. }
    211. void usage()
    212. {
    213.                 printf("==============================================================/n");
    214.                 printf("+/tether tool/t/t/n");
    215.                 printf("+/n");
    216.                 printf("+/t/t ~~~print the simple ether mac flow~~~/n");
    217.                 printf("+/tCopyright  shile/n");
    218.                 printf("==============================================================/n/n");
    219.                 printf("usage: %s [-iDt] /n/t-D list interfaces/n/t-i <interface> [-t <cap time>] /n/n", program_name);
    220.                 exit(0);
    221. }
    展开全文
  • PLC间串口通信在一套PLC系统中多于一个CPU总线单元时,需对模块单元号进行设置。CPU总线单元在水机自动化装置中常使用的主要包括以下几种:
  • 以太网报文格式:IP 报文格式:UDP 报文格式:校验和函数:/******************************************************* 功能: 校验和函数 参数: buf: 需要校验数据的首地址 nword: 需要校验数据长度的一半 ...

    以太网报文格式:

    76013acee8086d9dd4270ea0464e2c8b.png

    IP 报文格式:

    fe3473b831d72745dcd1a43e819006dd.png

    UDP 报文格式:

    976501cf753b1ea9939f108003e842d6.png

    校验和函数:

    /*******************************************************
    功能:
    	校验和函数
    参数:
    	buf: 需要校验数据的首地址
    	nword: 需要校验数据长度的一半
    返回值:
    	校验和
    *******************************************************/
    unsigned short checksum(unsigned short *buf, int nword)
    {
    	unsigned long sum;
    	for(sum = 0; nword > 0; nword--)
    	{
    		sum += htons(*buf);
    		buf++;
    	}
    	sum = (sum>>16) + (sum&0xffff);
    	sum += (sum>>16);
    	return ~sum;
    }

    需要C/C++ Linux服务器架构师学习资料加qun获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

    26ec9de86ce9a6cb53f74a88925cb0ed.png

    这里是在linux下通过原始套接字组一个 udp 数据包,给 PC 机的网络调试助手发送信息:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <net/if.h>				//struct ifreq
    #include <sys/ioctl.h>			//ioctl、SIOCGIFADDR
    #include <sys/socket.h>
    #include <netinet/ether.h>		//ETH_P_ALL
    #include <netpacket/packet.h>	//struct sockaddr_ll
     
     
    unsigned short checksum(unsigned short *buf, int nword);//校验和函数
    int main(int argc, char *argv[])
    {
    	//1.创建通信用的原始套接字
    	int sock_raw_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    	
    	//2.根据各种协议首部格式构建发送数据报
    	unsigned char send_msg[1024] = {
    		//--------------组MAC--------14------
    		0x74, 0x27, 0xea, 0xb5, 0xef, 0xd8, //dst_mac: 74-27-EA-B5-FF-D8
    		0xc8, 0x9c, 0xdc, 0xb7, 0x0f, 0x19, //src_mac: c8:9c:dc:b7:0f:19
    		0x08, 0x00,                         //类型:0x0800 IP协议
    		//--------------组IP---------20------
    		0x45, 0x00, 0x00, 0x00,             //版本号:4, 首部长度:20字节, TOS:0, --总长度--:
    		0x00, 0x00, 0x00, 0x00,				//16位标识、3位标志、13位片偏移都设置0
    		0x80, 17,   0x00, 0x00,				//TTL:128、协议:UDP(17)、16位首部校验和
    		10,  221,   20,  11,				//src_ip: 10.221.20.11
    		10,  221,   20,  10,				//dst_ip: 10.221.20.10
    		//--------------组UDP--------8+78=86------
    		0x1f, 0x90, 0x1f, 0x90,             //src_port:0x1f90(8080), dst_port:0x1f90(8080)
    		0x00, 0x00, 0x00, 0x00,               //#--16位UDP长度--30个字节、#16位校验和
    	};
    	
    	int len = sprintf(send_msg+42, "%s", "this is for the udp test");
    	if(len % 2 == 1)//判断len是否为奇数
    	{
    		len++;//如果是奇数,len就应该加1(因为UDP的数据部分如果不为偶数需要用0填补)
    	}
    	
    	*((unsigned short *)&send_msg[16]) = htons(20+8+len);//IP总长度 = 20 + 8 + len
    	*((unsigned short *)&send_msg[14+20+4]) = htons(8+len);//udp总长度 = 8 + len
    	//3.UDP伪头部
    	unsigned char pseudo_head[1024] = {
    		//------------UDP伪头部--------12--
    		10,  221,   20,  11,				//src_ip: 10.221.20.11
    		10,  221,   20,  10,				//dst_ip: 10.221.20.10
    		0x00, 17,   0x00, 0x00,             	//0,17,#--16位UDP长度--20个字节
    	};
    	
    	*((unsigned short *)&pseudo_head[10]) = htons(8 + len);//为头部中的udp长度(和真实udp长度是同一个值)
    	//4.构建udp校验和需要的数据报 = udp伪头部 + udp数据报
    	memcpy(pseudo_head+12, send_msg+34, 8+len);//--计算udp校验和时需要加上伪头部--
    	//5.对IP首部进行校验
    	*((unsigned short *)&send_msg[24]) = htons(checksum((unsigned short *)(send_msg+14),20/2));
    	//6.--对UDP数据进行校验--
    	*((unsigned short *)&send_msg[40]) = htons(checksum((unsigned short *)pseudo_head,(12+8+len)/2));
    	
    	
    	//6.发送数据
    	struct sockaddr_ll sll;					//原始套接字地址结构
    	struct ifreq req;					//网络接口地址
    	
    	strncpy(req.ifr_name, "eth0", IFNAMSIZ);			//指定网卡名称
    	if(-1 == ioctl(sock_raw_fd, SIOCGIFINDEX, &req))	//获取网络接口
    	{
    		perror("ioctl");
    		close(sock_raw_fd);
    		exit(-1);
    	}
    	
    	/*将网络接口赋值给原始套接字地址结构*/
    	bzero(&sll, sizeof(sll));
    	sll.sll_ifindex = req.ifr_ifindex;
    	len = sendto(sock_raw_fd, send_msg, 14+20+8+len, 0 , (struct sockaddr *)&sll, sizeof(sll));
    	if(len == -1)
    	{
    		perror("sendto");
    	}
    	return 0;
    }
     
    unsigned short checksum(unsigned short *buf, int nword)
    {
    	unsigned long sum;
    	for(sum = 0; nword > 0; nword--)
    	{
    		sum += htons(*buf);
    		buf++;
    	}
    	sum = (sum>>16) + (sum&0xffff);
    	sum += (sum>>16);
    	return ~sum;
    }

    运行结果如下:

    e284d5dfe710a31f13ebef956fc03453.png
    展开全文
  • SAL全称Socket Abstract Layer,即套接字抽象层,主要作用是对上层应用提供一层统一的 socket 编程接口,屏蔽底层网络硬件的差异。 LiteOS的SAL架构如下: SAL的优势从图中一看即知: 无论底层使用以太网 LwIP协议...

    1. SAL套接字抽象层

    SAL全称Socket Abstract Layer,即套接字抽象层,主要作用是对上层应用提供一层统一的 socket 编程接口,屏蔽底层网络硬件的差异。

    LiteOS的SAL架构如下:

    SAL网络架构

    SAL的优势从图中一看即知:

    无论底层使用以太网 LwIP协议栈组合,还是使用ESP8266/M26 AT框架组合,经过SAL套接字抽象层之后,对用户提供的接口都是统一的,极大的提高了程序的可移植性。

    SAL框架的源码及其实现在SDK中的IoT_LINK_1.0.0\iot_link\network\tcpip目录:

    SAL实现源码

    除了sal文件夹之外,其余的文件夹分别对应着不同的sal实现,比如esp8266_socket对应的是基于AT框架和ESP8266的SAL实现。

    SAL相关的头文件存放在IoT_LINK_1.0.0\iot_link\inc文件夹中,如图:

    SAL头文件

    • sal.h:SAL头文件,使用时需包含;
    • sal_imp.h:抽象接口定义头文件;
    • sal_types.h:socket编程中涉及到的类型定义;
    • sal_define.h:socket编程中涉及到的宏定义;
    • link_endian.h:socket编程中的大小端字节序转换函数定义;

    2. Socket编程基础

    2.1. Socket概述

    Socket称为套接字,本质上是一种文件描述符,所以socket通信的过程和操作文件的方法基本类似。

    TCP/IP协议族的传输层中,分为有连接的,可靠的TCP传输方式,和无连接的,不可靠的UDP传输方式,所以Socket分为两种:

    • 流式Socket(SOCK_STREAM):提供可靠的、面向连接的通信流,使用TCP协议;
    • 数据报Socket(SOCK_DGRAM):提供一种无连接的服务,使用UDP协议;

    2.2. Socket结构体

    一个标准的Socket应该包括以下五部分:

    • 协议类型
    • 目的IP
    • 目的端口
    • 源ip
    • 源端口

    SAL提供了两种socket的结构体用于存放数据,sockaddr结构体和sockaddr_in结构体,定义均在sal_types.h文件中。

    sockaddr结构体的定义如下:

    struct sockaddr
    {
        sa_family_t     sa_family;      /* address family, AF_xxx   */
        char            sa_data[14];    /* 14 bytes of protocol address */
    };
    

    参数说明如下:

    • sa_family:地址族,一般为AF_INET,表示IPv4协议;
    • sa_data:包含了源ip、源端口、目的ip、目的端口;

    sockaddr_in结构体的定义如下:

    struct sockaddr_in
    {
        sa_family_t sin_family;             /* AF_INET */
        in_port_t sin_port;                 /* Port number.  */
        struct in_addr sin_addr;            /* Internet address.  */
        unsigned char sin_zero[8];          /* Pad to size of `struct sockaddr'.  */
    };
    

    sockaddr结构体将所有的ip和端口信息都放在了sa_data中,不利用编程,而sockaddr_in结构体本质上和sockaddr结构体一样,但是将目的ip和目的端口分离出来,容易编程,所以一般在使用的时候有如下技巧:

    使用sockaddr_in结构体赋值,作为参数传递时强制转换为sockaddr类型传递

    2.3. 字节序转换函数

    在sockaddr_in结构体中填写sin_port和sin_addr这两个值时,需要注意:

    • in_port_tuint16_t类型;
    • sin_addruint32_t类型;

    这样就涉及到了两个转换问题:

    • ip地址的转换

    ip地址通常是一个字符串,比如"192.168.1.100",但是此处需要转换为一个uint32_t类型的数据,SAL提供了一个转换函数,在之前提到的link_endian.h文件中,函数如下:

    
    
    • 字节序的转换

    字节序分为大端存储和小端存储,为了保证统一性,屏蔽硬件差异,需要将ip地址和端口的值转换为网络字节序,SAL提供了本地字节序和网络字节序的互相转换函数,在link_endian.h文件中,其中h表示host主机,n表示network网络字节序

    htonl(unsigned long int hostlong);
    htons(unisgned short int hostshort);
    ntohl(unsigned long int netlong);
    ntohs(unsigned short int netshort);
    

    3. AT框架和SAL配置及开启

    本实验中我们使用ESP8266 AT框架 SAL进行实验,所以需要开启使能AT框架和SAL。

    3.1. AT框架开启

    关于AT框架具体的剖析,可以阅读上一篇教程

    在工程目录下的.sdkconfig中手动配置开启驱动框架(串口使用)和AT框架:

    AT框架开启

    实验中使用的是ESP8266,所以还需要配置路由器的SSID和PASSWD,在SDK目录中IoT_LINK_1.0.0\iot_link\network\tcpip\esp8266_socket目录下, 打开esp8266_socket_imp.h文件:

    在其中设置ESP8266连接的热点名称和密码,这里我的设置如下:

    热点设置

    最后,需要修改同文件夹下的esp8266_socket_imp.mk文件,将图中标出的两处TOP_DIR改为SDK_DIR:

    makefile文件

    makefile设置

    3.2. SAL开启

    SAL默认是未开启的,需要在工程目录下的.sdkconfig中手动配置开启:

    使能esp8266实现的sal

    其中CONFIG_TCPIP_ENABLE = y需要自己添加,CONFIG_TCPIP_TYPE宏定义的值目前支持,可以根据自己的需求选择:

    • “lwip_socket”
    • “linux_socket”
    • “macos_socket”
    • “esp8266_socket”
    • “none”

    注意:两个宏定义必须同时存在且使能,SAL才会生效。

    3.3. SAL自动初始化

    使能了SAL之后,系统会自动进行初始化,在SDK目录中的IoT_LINK_1.0.0\iot_link下的link_main.c文件中即可看到:

    SAL自动初始化

    4. TCP Socket客户端编程实例

    4.1. TCP服务端的建立

    在本实验中,TCP Server使用网络调试助手模拟,在本机8000端口开启一个TCP服务器,如图:

    TCP服务端

    4.2. SAL提供的Socket客户端编程API

    建立socket

    API原型如下:

    int sal_socket(int domain, int type, int protocol);
    

    参数说明如下:

    参数 说明 常用值
    domain 协议或地址族 AF_INET,表示IPv4
    type socket类型 SOCK_STREAM,表示TCP
    SOCK_DGRAM,表示UDP
    protocol 使用的协议号 0,表示使用默认协议号
    返回值 socket描述符 int类型值,-1则表示失败

    连接服务器socket

    API原型如下:

    int sal_connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
    

    参数说明如下:

    参数 说明
    sockfd 创建成功的sockfd描述符
    addr sockaddr结构体指针
    addrlen sockaddr结构体长度

    socket发送数据

    API原型如下:

    int sal_send(int sockfd,const void *buf,size_t len,int flags);
    

    参数说明如下:

    参数 说明
    sockfd 创建成功的sockfd描述符
    buf 发送数据
    len 发送数据长度
    flags 发送或接收标记,一般都设为0

    socket接收数据(非堵塞)

    API原型如下:

    int sal_recv(int sockfd,void *buf,size_t len,int flags);
    

    参数说明如下:

    参数 说明
    sockfd 创建成功的sockfd描述符
    buf 接收数据缓冲区
    len 接收数据缓冲区长度
    flags 发送或接收标记,一般都设为0

    关闭socket

    API原型如下:

    int sal_closesocket(int sockfd);
    

    参数说明如下:

    参数 说明
    sockfd 创建成功的sockfd描述符

    4.3. 基于SAL的TCP客户端编程

    打开之前创建的HelloWorld工程(如果没有,可以参考第一篇教程新建),创建下面的文件夹sal_test_demo,并在该文件夹中新建一个测试文件sal_tcp_demo.c

    新建实验文件

    编辑以下内容:

    注意,其中的server_ip和server_port应该是服务器的实际情况相对应!

    #include <osal.h>
    #include <sal.h>
    
    #define server_port 8000
    #define server_ip   "192.168.0.101"
    
    static int sal_tcp_demo_entry()
    {
        int sockfd;
    
        /* 创建TCP socket */
        sockfd = sal_socket(AF_INET, SOCK_STREAM, 0);
        if(sockfd < 0)
        {
            printf("TCP Socket create fail.\r\n");
            return -1;
        }
        else
        {
            printf("TCP Socket create ok.\r\n");
        }
    
        /* 连接服务器 */
        struct sockaddr_in server_addr;
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(server_port);
        server_addr.sin_addr.s_addr = inet_addr(server_ip);
        while(-1 == sal_connect(sockfd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr)))
        {
            //连接失败,则1s后自动重连
            printf("connect server fail, repeat...\r\n");
            osal_task_sleep(1000);
        }
        printf("connect server ok.\r\n");
        
        int nbytes;
        char buf[] = "hello server!";
        //发送数据到服务器
        nbytes = sal_send(sockfd, buf, sizeof(buf), 0);
        if(nbytes < 0)
        {
            printf("send dat %s fail.\r\n", buf);
            return -1;
        }
        else
        {
            printf("send [%d] bytes: %s.\r\n", nbytes , buf);
        }
    
        //等待接收服务器数据
        char recv_buf[50]={0};
        while( -1 == (nbytes = sal_recv(sockfd, recv_buf, 50, 0)));
        printf("recv [%d] bytes: %s.\r\n", nbytes, recv_buf);
    
        //关闭socket
        sal_closesocket(sockfd);
        printf("TCP socket closed.\r\n");
    
        return 0;
    }
    
    int standard_app_demo_main()
    {
        osal_task_create("sal_tcp_demo",sal_tcp_demo_entry,NULL,0x800,NULL,12);
        return 0;
    }
    

    然后在user_demo.mk中添加文件路径:

    	#example for sal_tcp_demo
    	ifeq ($(CONFIG_USER_DEMO), "sal_tcp_demo")	
    		user_demo_src  = ${wildcard $(TOP_DIR)/targets/STM32L431_BearPi/Demos/sal_test_demo/sal_tcp_demo.c}
    	endif
    

    位置如下:

    添加位置

    最后在.sdkconfig中配置选中该demo文件:

    配置选中dmeo

    然后编译,下载,即可看到串口输出(前提是确保TCP服务器已开启):

    串口输出

    在TCP服务端软件也可以看到:

    服务端信息

    在服务端发送数据,在串口可以看到客户端已接收:

    串口输出

    5. UDP Socket客户端编程实例

    5.1. UDP服务端的建立

    在本实验中,UDP Server使用网络调试助手模拟,在本机8000端口开启一个UDP服务器,如图:

    UDP服务端

    5.2. SAL提供的Socket客户端编程API

    连接服务器socket

    API原型如下:

    int sal_connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
    

    参数说明如下:

    参数 说明
    sockfd 创建成功的sockfd描述符
    addr sockaddr结构体指针
    addrlen sockaddr结构体长度

    发送数据

    API原型如下:

    int sal_sendto(int sockfd, const void *dataptr, size_t size, int flags,
        const struct sockaddr *to, socklen_t tolen);
    

    参数说明如下:

    参数 说明
    sockfd 创建成功的sockfd描述符
    dataptr 待发送的数据指针
    size 发送包数据大小
    flags 发送或接收标记,一般都设为0
    addr sockaddr结构体指针
    addrlen sockaddr结构体长度

    接收数据

    API原型如下:

    int sal_recvfrom(int sockfd, void *mem, size_t len, int flags,
          struct sockaddr *from, socklen_t *fromlen);
    

    参数说明如下:

    参数 说明
    sockfd 创建成功的sockfd描述符
    mem 接收缓冲区数据指针
    size 接收数据大小
    flags 发送或接收标记,一般都设为0
    addr sockaddr结构体指针
    addrlen sockaddr结构体长度

    关闭socket

    API原型如下:

    int sal_closesocket(int sockfd);
    

    参数说明如下:

    参数 说明
    sockfd 创建成功的sockfd描述符

    5.3. 基于SAL的UDP客户端编程

    打开之前创建的HelloWorld工程(如果没有,可以参考第一篇教程新建),创建下面的文件夹sal_test_demo,并在该文件夹中新建一个测试文件sal_udp_demo.c

    新建实验文件

    编辑以下内容:

    注意,其中的server_ip和server_port应该是服务器的实际情况相对应!

    #include <osal.h>
    #include <sal.h>
    
    #define server_port 8000
    #define server_ip   "192.168.0.101"
    
    static int sal_udp_demo_entry()
    {
        int sockfd;
    
        /* 创建udp socket */
        sockfd = sal_socket(AF_INET, SOCK_DGRAM, 0);
        if(sockfd < 0)
        {
            printf("udp Socket create fail.\r\n");
            return -1;
        }
        else
        {
            printf("udp Socket create ok.\r\n");
        }
    
        /* 服务端信息 */
        struct sockaddr_in server_addr;
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(server_port);
        server_addr.sin_addr.s_addr = inet_addr(server_ip);
    
        /* 发送数据到服务器 */
        int nbytes;
        char buf[] = "hello server!";
        nbytes = sal_sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&server_addr, sizeof(struct sockaddr));
        if(nbytes < 0)
        {
            printf("send dat %s fail.\r\n", buf);
            return -1;
        }
        else
        {
            printf("send [%d] bytes: %s.\r\n", nbytes , buf);
        }
    
        /* 等待接收服务器数据 */
        char recv_buf[50]={0};
        while( -1 == (nbytes = sal_recvfrom(sockfd, recv_buf, 50, 0,  (struct sockaddr*)&server_addr, sizeof(struct sockaddr))));
        printf("recv [%d] bytes: %s.\r\n", nbytes, recv_buf);
    
        /* 关闭socket */
        sal_closesocket(sockfd);
        printf("udp socket closed.\r\n");
    
        return 0;
    }
    
    int standard_app_demo_main()
    {
        osal_task_create("sal_udp_demo",sal_udp_demo_entry,NULL,0x800,NULL,12);
        return 0;
    }
    

    然后在user_demo.mk中添加文件路径:

    	#example for sal_udp_demo
    	ifeq ($(CONFIG_USER_DEMO), "sal_udp_demo")	
    		user_demo_src  = ${wildcard $(TOP_DIR)/targets/STM32L431_BearPi/Demos/sal_test_demo/sal_udp_demo.c}
    	endif
    

    位置如下:

    添加位置

    最后在.sdkconfig中配置选中该demo文件:

    配置选中dmeo

    然后编译,下载,即可看到串口输出(前提是确保UDP服务器已开启):

    串口输出

    在UDP服务端软件也可以看到:

    服务端信息

    在服务端发送数据,在串口可以看到客户端已接收:

    串口输出

    展开全文
  • 串口编程实例

    2014-11-24 10:21:07
    虽然以太网接口和USB接口也是以一个串行流进行数据传送的,但是串口连接通常特指那些与RS-232标准兼容的硬件或者调制解调器的接口。虽然现在在很多个人计算机上,原来用以连接外部设备的串口已经广泛的被USB和...
  • 以下书籍都是现在业内主流的Linux shell语言好书,同步收录在顶书,通过下面...如何凭借短短几个命令行从Web挖掘数据的shell脚本,如何通过srlell脚本设置以太网和无线LAN,以及如何利用少量命令的组合完成诸如文本...
  • 网络编程类40个实例

    2014-05-11 15:56:33
    VC++编程实现网络嗅探器 OpenGL源代码oglwrapperclass_src sniffer 的小例子 p2p source code pop3邮件接收器 pop3高级程序开发 P2P之UDP穿透NAT的原理与实现工程 P2P之UDP穿透NAT的原理与实现源代码 截获以太网数据...
  • CP1H上面的选件板插槽可以插入1-2块CP1W-CIF41模块,系统即具有了以太网功能,也可以使用CP1W-EXT01和CJ1W-ETN21以太网扩展模块来组态,用于在CX-Programmer软件下的编程或监控,也可以和其它网络设备(可以是上位机...
  • 以太网报文格式:详细的说明,请看《MAC 头部报文分析》。IP 报文格式:详细的说明,请看《IP 数据报格式详解》。UDP 报文格式:详细的说明,请看《UDP 数据报格式详解》。校验和函数:/***************************...
  • 如果 A (192.168.1.1 )向 B (192.168.1.2 )发送一个数据包,那么需要的条件有 ip、port、使用的协议(TCP/UDP)之外还需要 MAC 地址,因为在以太网数据包中 MAC 地址是必须要有的。那么怎样才能知道对方的 MAC ...
  • 以太网报文格式: 详细的说明,请看《MAC 头部报文分析》。 IP 报文格式: 详细的说明,请看《IP 数据报格式详解》。 UDP 报文格式: 详细的说明,请...
  • 如果 A (192.168.1.1 )向 B (192.168.1.2 )发送一个数据包,那么需要的条件有 ip、port、使用的协议(TCP/UDP)之外还需要 MAC 地址,因为在以太网数据包中 MAC 地址是必须要有的。那么怎样才能知道对方的 MAC ...
  • 该文介绍了ZedBoard平台下可编程逻辑端SPI接口和ARM处理器...结合实例阐述了可编程逻辑端SPI接口设计和Linux下IP驱动生成,以及采用UDP/IP协议实现以太网传输的技术。实验结果证明了该系统能够精确地完成实时数据传输。
  • 本示例通过一个简单的计数器程序说明相关硬件和软件的配置和简单使用。...硬件配置方法:我们选用的通信方式是基于以太网卡的TCP/IP协议通信所以,我们直接使用网线将S7-1200和配置有网卡的计算机的以太网接...
  • Labview视觉检测编程实例,案例中利用NI VISON 控件 对库卡机器人图像采集,判定识别,结果通过以太网传输给机器人。
  • VC程序实例,主要完成PC机与以太网转串口设备之间的通讯。包括:网路编程、串口编程方面的应用.
  • 网络编程实践笔记

    2021-01-23 23:05:08
    非并发网络编程实例: TTCP:classic TCP performance testing tool(TCP性能测试工具) Round-trip:measure clock error between two hosts(测试两台机器的时间差) Netcat:a Swiss hnife Slow sink/sourc
  • Windows网络编程

    2012-11-21 14:37:58
    5.4.3 以太网技术 192 5.4.4 令牌环网 196 5.5 综合实例 198 5.5.1 获取网卡信息1 198 5.5.2 获取网卡信息2 201 5.5.3 获取网卡信息3 203 5.6 小结 211 第6章 Winsock基础 212 6.1 套接字 212 6.2 Socket...
  • 1.5.1 以太网ARP请求应答报文详解 1.5.2 ARP高速缓存的查看和修改 1.5.3 使用tcpdump观察ARP通信过程 1.6 DNS工作原理 1.6.1 DNS查询和应答报文详解 1.6.2 Linux下访问DNS服务 1.6.3 使用tcpdump观察...
  • 3.5.2SHELL编程实例 3.6Perl编程语言 3.6.1Ped基本程序 3.6.2Ped变量 3.6.3文件句柄和文件操作 3.6.4循环结构 3.6.5条件结构 3.7本章总结 第4章 常用的Linux开发工具 4.1GCC编译器 4.1.1GCC版本信息 4.1.2GCC目录...
  • 2.3 Winsock编程实例 41 2.3.1 主机地址查询程序 41 2.3.2 一个简单的FTP程序 43 2.3.3 小结 48 第三章 Visual C++6.0编程基础 49 3.1 Visual C++6.0编程工具简介 49 3.2 面向对象语言C++ 51 3.3 ViualC++6.0的使用 ...
  • Linux C 编程一站式学习.pdf

    千次下载 热门讨论 2010-11-24 01:27:27
    2. 以太网(RFC 894)帧格式 3. ARP数据报格式 4. IP数据报格式 5. IP地址与路由 6. UDP段格式 7. TCP协议 7.1. 段格式 7.2. 通讯时序 7.3. 流量控制 37. socket编程 1. 预备知识 1.1. 网络字节序 1.2. socket地址的...
  • JAVA上百实例源码以及开源项目

    千次下载 热门讨论 2016-01-03 17:37:40
     Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、关闭输入流、关闭套接字关闭输出流、输出错误信息等Java编程小技巧。 Java数组倒置...

空空如也

空空如也

1 2 3 4 5 ... 10
收藏数 196
精华内容 78
关键字:

以太网编程实例