4.PC上位机遥控

您当前位置: 首页 > 树莓派-4WD小车 > 遥控操作

上位机软件.zip 点击下载 


一.实验基本介绍

  本次实验主要是通过搭建TCP并发服务器,支持多用户连接控制智能小车,客户端上位机通过连接上小车搭建好的TCP_control并发服务器。通过发送TCP协议数据控制小车的前进,后退,左转,右转,停止,左旋,右旋,以及前舵机的左中右控制,后面摄像头舵机任意角度的连续控制,还有七彩灯的控制,灭火,鸣笛,小车的加速,减速。以及树莓派智能小车通过丰富的传感器采集的数据实时的显示在我们的上位机上。也是通过TCP协议通信。


 

二.TCP通信模型的设计

 

服务器端:(被动接受请求)

 

socket            //电话机

  |             

bind(ip+port)      //绑定电话号码 绑定服务器自己的ipport等待客户端连接。

  |

listen            //监听有人打电话进来

  |

accept           //接听电话

  |

recv/send        //通话过程

  |

close            //挂机

 

 

客户端:(主动发起连接)

socket            //电话机

  |             

bind(ip+port)      //绑定电话号码

  |

connect          //拨打电话

  |

recv/send        //通话过程

  |

close            //挂机

 

三.TCP常用函数讲解

创建流式套接字

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

    功能: 创建socket ,返回对应的文件描述符

    参数:

          @domain    域(通信的范围)

          @type     SOCK_STREAM  流式套接字有序可靠,面向连接字节流

                    SOCK_DGRAM   报文套接字:无连接的,不可靠的

                    SOCK_RAW  原始套接字可以访问一些低层的网络协议

 

          @protocol    0表示默认的方式

                        SOCK_STREAM    TCP

                        SOCK_DGRAM     UDP

    返回值:

             成功  文件描述符

             失败  -1 ,并设置errno

把服务器的ipportsockfd绑定

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

       功能:

          绑定一个地址(ip+port)到一个socket 文件描述符上

 

       参数:

           @sockfd   socket 函数获得的文件描述符

           @addr     地址信息结构体

                    

                   //通用结构体

                   struct sockaddr {

                       sa_family_t sa_family; //地址族

                       char      sa_data[14];   //地址信息

                   }

                   //TCP/IP协议的地址结构

                   struct sockaddr_in {

                        sa_family_t    sin_family;  //协议簇

                        in_port_t      sin_port;    //端口

                        struct in_addr sin_addr;   //ip地址

                    };

     

            @addrlen  表示 addr 参数对应类型的地址信息结构体的大小

 

       返回值:

             成功 0

             失败 -1&errno

 

操作:

    1.定义地址结构体变量,清零

          struct sockaddr_in  ser_addr;

          memset(&ser_addr,0,sizeof(ser_addr));

 

    2.填充地址信息

         ser_addr.sin_family  = AF_INET;//地址协议族

         ser_addr.sin_port    = htons(8888);//端口号

         ser_addr.sin_addr    = inet_addr("192.168.1.7");

 

     (3). 绑定

         if(bind(sockfd,(struct sockaddr*)&ser_addr,sizeof(ser_addr)) < 0)

          {

              perror("bind fail");

              exit(EXIT_FAILURE);

          }

获得客户连接请求,创建连接连接套接字,负责数据通信。

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

 

        功能: 获取连接请求,建立连接套接字

 

        参数:

            @sockfd   监听套接字

            @addr     用来获取对端的地址信息

            @addrlen  值结果参数(注意:必须自己初始化一个值sizeof(addr))

        返回值:

            成功  新连接的套接字文件描述符

            失败  -1 ,并设置错误码。

 

----------------------------------------------------------------------------------------------------------------

特点:

1.请求队列中没有请求,阻塞调用者

2.每次提取一路请求,就会创建一个新的套接字,我们称为连接套接字,用来和客户端收发数据

-----------------------------------------------------------------------------------------------------------------

 

int listen(int sockfd ,int backlog);

功能:把同一个时刻来的套接字存放如监听队列中。<同一个时刻>

参数:

@sockfd         监听的套接字

@backlog        连接请求队列的最大长度。一般设置为30 以下。

 

注意:有的时候backlog很小,但是我们也可以了解很多台机器。服务器机器处理速度很快队列来不及填满就处理完了,而且在同一个时刻到来的连接还是很少的

 

客户端向服务器请求连接的函数

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

功能:向服务端发起连接。

参数:

            @sockfd  客户端的socket文件描述符

            @addr    代表服务器端的地址信息

            @addrlen   addr 对应的地址结构体类型的大小

返回值:

成功返回0,失败返回-1

 

发送数据

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

功能:给指定的sockfd发送数据

参数:

        @scokfd     套接字

        @ buf         发送数据的缓冲区

        @len           要发送数据的长度

        @flags       设置网络标志   ,常常设置为0

返回值:

成功,返回值 > 0,发送成功的字节数

      返回值==0,发送超时

      返回值<0, 发送失败,并设置错误码

 

//在阻塞模式下,下面的公式等价

注:send(sockfd, buf, len, 0);

 

接收数据

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

功能:接收指定的sockfd的数据即连接套接字返回的文件描述符

参数:

      @scokfd     套接字

        @ buf         接受数据的缓冲区

        @len           要接受数据的长度

        @flags      设置网络标志   ,常常设置为0

返回值:

成功,返回值 > 0,成功接收的字节数

      返回值==0,对方关闭了连接

      返回值<0, 发送失败,并设置错误码            

            

//在阻塞模式下,下面的公式等价

注:recv(sockfd, buf, len, 0);

四.TCP多线程并发服务器设计

多线程TCP并发服务器 

//线程处理函数

void *do_client(void *age)

{

        int connect_fd = *(int *)arg;

        while(1)

        {

                //接收客户端数据

                recv();..

        }

}

 

  socket(...);

  bind(...);

  listen(...);

  while(1)

  {

    pconnect_fd = malloc(sizeof(int)); //在堆区创建空间保持文件描述符

 

     *pconnect = accept(...);

       //创建线程接收数据 

       ret = pthread_creat(&tind,NULL,do_client,pconnect_fd);

         close(...);

         exit(...);

     }

       pthread_detach(tid);

  }      

 

对于本次实验中我们也是采用了多线程的思维去处理,

一路线程负责解析上位机传过来的TCP数据,并执行相应的动作。

一路线程负责将小车传感器采集的数据打包传给上位机显示。

一路线程负责检测舵机的控制状态,并执行相应的舵机动作。


 

五.操作步骤

     

  小车启动嘀嘀嘀三声之后会发出热点,名称Yahboom_Car密码12345678我们用电脑连接小车的热点。再通过putty登录到小车,IP地址填写192.168.50.1

image.png

   这时输入账号 pi 密码yahboom进入树莓派

image.png


随后输入top


(J`HP~I1O$VAB[~A88(3[$E.png

可以看到541进程号是蓝牙遥控的进程,554是摄像头进程,注意这里的进程号不同的树莓派是不同的,然后我们要先关闭蓝牙进程这样我们打开TCP进程才不会与蓝牙进程冲突。

 

输入命令杀掉进程:注意上个界面如何退出呢,输入ctrl+z退出。

命令:sudo kill  -9  541

如下图所示:

 

T(IT1LP7}5UU`@$4UI}K8Z4.png

然后进到SmartCar目录下:


R}4_I9KSGBDVRLOV))D}[6D.png

找到TCP_control.c 里面有个ip地址和端口号可以根据树莓派自己的ip进行修改,一般出厂是自带热点所以ip192.168.50.1(注意:树莓派4B最新版本采用50网段,老的客户可能是0网段),端口号默认是【8888

修改完毕一定要编译程序:

gcc TCP_control.c -o TCP_control -lwiringPi -lpthread

编译完成后,

我们直接运行TCP进程,如下命令:

 

./TCP_control

 

这样我们的TCP进程就运行成功了。

如下提示Listen

533V87)[MJ]C)}U)W4@SSEA.png


下面我们打开上位机选择  4WD-树莓派。


TZ8F3XGMTC~{[W{_AXU{JW6.png

 

然后点击【连接视频】则可以看到视频,点击【连接控制】则其他所有按键都可以控制。

(注意:树莓派4B最新版本采用50网段192.168.50.1,老的客户可能是0网段192.168.0.1)


%VEX1XU2VA4Z28MR}6PIHLY.png


成功后如下:


TTII%AVXXLKC@GIL6@E3QOM.png


服务器会有如下提示:


J0A5AT5XTEG%FO`PM656SCU.png


至此TCP控制就算完成。