【RTOS】+ 基于云的远程小车控制

[复制链接]
4811|3
手机看帖
扫描二维码
随时随地手机跟帖
wlxn|  楼主 | 2020-6-15 11:33 | 显示全部楼层
本帖最后由 wlxn 于 2020-7-2 22:46 编辑

龙芯板子+rt-thread实现web远程控制四轮小车
硬件方案硬件清单
  • 板子:龙芯机器人控制器(ls1c) / 龙芯派(ls2k1000)
  • 四轮小车套件:包括车体、四驱电机、电机驱动板()、电池组、充电宝
  • 正点电子 ESP8266  WIFI模块 一个

IMG_20200614_165446-1.jpg
IMG_20200614_165426-1.jpg
154375ee8360b53cfb.png

小车套件(含电池组、电机控制器)


IMG_20200616_070902-1.jpg
龙芯机器人控制器(ls1c)
IMG_20200616_070743-1.jpg
串口wifi模块(es8266)


部署架构
327145ee83d3052dcd.png


软件方案
整个软件方案的架构分为3部分:
  • WEB服务端(运行在云端),主要提供手机上可访问的控制界面,并将手机端发出的控制动作转换成mqtt的消息发布出去,这个程序需要开发,本次采用python WEB框架flask开发,前端采用html5 技术开发。
  • mqtt服务器、(运行在云端),负责接收WEB服务端发送的消息,转发给板子上的控制程序,这个程序不用开发,直接安装开源的
  • 控制器程序 ,运行在板子上,负责监听mqtt服务发来的消息,根据不同的消息来控制电机的转动,实现前进、后退、左转、右转等控制动作,这个程序需要开发,将采用c语言开发,操作系统采用rt-thread4.0.3

此处将有示意图
实现步骤一、搭建云端环境
本次云服务器,我用的是我自已在腾讯云申请的云主机,云主机运行的操作系统如下:
[root@VM_0_4_centos ~]# uname -a
Linux VM_0_4_centos 3.10.0-862.el7.x86_64 #1 SMP Fri Apr 20 16:44:24 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
在上面安装了docker环境,并用docker 下载并运行了eclipse-mosquitto容器。服务端口为1883。命令如下
$[root@VM_0_4_centos ~]# docker pull eclipse-mosquitto
$[root@VM_0_4_centos ~]# docker run -d  -p 1883:1883  eclipse-mosquittop
--查看是否正常启动
$[root@VM_0_4_centos ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                               NAMES
b78bb919034d        eclipse-mosquitto   "/docker-entrypoin..."   3 seconds ago       Up 3 seconds        0.0.0.0:1883->1883/tcp              keen_goldstine
--可以看到mqtt服务器已经正常运行了
下载python镜象并启动容器,但需要安装flask框架及mqtt客户端插件,命令如下:
[root@VM_0_4_centos ~]# docker pull python
Using default tag: latest
Trying to pull repository docker.io/library/python ...
... ...

--因默认该环境没有flask及mqtt插件,所以需要进行一步安装,且为了保证下次重启可用,需要将镜象保存更新
[root@VM_0_4_centos ~]# docker run -i -t python /bin/bash
... ...
--进入容器后,安装flask
root@b45cbfa11dfb:/# pip install flask
... ...
-- 然后再安装flask-mqtt
root@b45cbfa11dfb:/# pip install flask-mqtt

-- 最后将当前容器保存为新的镜象,这样以后用这个镜象启动容器,即实现一个可用的开发环境。
[root@VM_0_4_centos python]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                       PORTS                               NAMES
b45cbfa11dfb        python              "/bin/bash"              8 minutes ago       Exited (127) 7 seconds ago                                       jovial_mirzakhani
... ...
[root@VM_0_4_centos python]# docker commit b45cbfa11dfb  flask_mqtt:v1
sha256:00993b203a7db9becc53ff7a642664ee3bfed3ac36432878357ce245ada7dd0b
二、开发云端程序开发web界面
为达到较好的移动端显示效果,并且由于我们这个项目非常的简单,也不必要用重量级的框架,只要容易实现方便的自适应终端屏幕就可以 了,因此我采用bootstrap+jquery 框架实现,源码mygui.html 如下:
<!DOCTYPE html>
<html>
        <head>
                <meta charset="utf-8">
                <title>远程小车控制</title>
                <meta name="viewport" content="width=device-width, initial-scale=1.0">
                <meta name="viewport" content="width=device-width, initial-scale=1">
                <link rel="stylesheet" >  
                <script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
                <script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
        
          <style >
                  body {
                          background-color: darkkhaki;
                  }
                  table {
                          text-align: center;
                          width:100%;
                          height:100%;
                          
                  }
                  
                  td {
                          text-align: center;
                          margin: 5px 5px 5px 5px;
                          padding:5px 5px 5px 5px;
                  }
          </style>
        </head>
        <body >
               
                <div align="center">
                        <h2 align="center">远程小车控制</h2>
                        <hr>
               
                        <table  >
                                <tr><td></td><td ><button id="forward" type="button" class="btn btn-primary">前进</button></td><td></td></tr>
                                <tr><td><button id="left" type="button" class="btn btn-primary">左转</button></td><td><button id="stop" type="button" class="btn btn-primary">停车</button></td><td><button type="button" id="right" class="btn btn-primary">右转</button></td></tr>
                                <tr><td></td><td><button id="back" type="button" class="btn btn-primary">后退</button></td><td></td></tr>
                        </table>
               
                </div>
                <br><hr>
           <div align="center">作者:11680838@qq.com</div>
           
           <script>
                  
                   function mysubmit(ops){
                           $.get("http://www.iloveai.club:5000/drive?ops="+ops,function(data,status){
                              if(console){
                                    console.log("数据: " + data + "\n状态: " + status);
                              }else{
                                          alert("数据: " + data + "\n状态: " + status);
                                          
                                  }
                                });
                   }
                   $(document).ready(function(){
                     $("#forward").click(function(){
                      //alert("前进");
                          mysubmit("forward") ;
                     });
                         $("#back").click(function(){
                          //alert("后退");
                          mysubmit("back") ;
                         });
                         $("#left").click(function(){
                           // alert("左转");
                           mysubmit("left") ;
                         });
                         $("#right").click(function(){
                           // alert("右转");
                           mysubmit("right") ;
                         });
                         $("#stop").click(function(){
                            //alert("停车");
                                mysubmit("stop") ;
                         });
                   });
           </script>
        </body>
</html>
Screenshot_20200615_105523_com.tencent.mtt.jpg
手机端界面效果图
开发flask服务程序
myserver.py
#!/bin/python3
# fileName: myserver.py


from flask import Flask,request
import paho.mqtt.client as mqtt
import time

app = Flask(__name__)


@app.route("/")

def hello_world():
        return 'hello world'

@app.route("/drive")
def drive():
        ops = request.args['ops']
        #print(ops)
        sendMsg(ops)
        return 'success'

def sendMsg(ops):
        print(ops)
        HOST = 'www.iloveai.club'
        PORT = 1883
        client = mqtt.Client()
        client.connect(HOST, PORT, 60)
        client.loop_start()
        client.publish("driver",ops,2)
        client.loop_stop()
        client.disconnect()
        #client.loop_forever()



if __name__ == '__main__':

        app.run(host='0.0.0.0')
云端子系统验证
用一个mqtt测试工具mqttfx,订阅我们发布的主题消息,然后用手机打开我们开发的web网页,分别点击前进、后退、左转、右转等按钮,然后查看mqtt测试工具是否接收到正确的信息。
IMG_20200613_224105-1.jpg

三、搭建rt-thread开发环境



下载、安装env编译环境下载、安装交叉编译环境下载rt-thread 源码
方法请参考rt-thread官网。
(略)



四、配置内核

1、配置添加AT组件支持
     首先在 env 中运行 menuconfig,在界面中找到 AT 组件,并勾选,如图所示。

   779955ee83f29a207f.png

       RT-Thread 配置 AT 主界面 进入 AT 配置界面后,具体配置项如图  所示。

    42085ee83f47d1acb.png
   RT-Thread 配置 AT 组件界面 添加 SAL组件,启用 SAL(Socket抽象层),路径如图 所示。
      82155ee83f61bcb40.png

启用 SAL(Socket 抽象层)配置图 将 SAL(Socket抽象层)配置为支持 AT指令,如图所示。
123925ee83f91b6ff8.png

2、配置添加ESP8266模夫支持
ESP8266模块是一个标准的 AT 设备,能够使用 RT-Thread 组件进行操作。 首先添加 IOT中的 AT DEVICE包支持,如图 所示。

572655ee8401419d64.png
然后配置 ESP8266,添加该模块,如图 所示

409435ee840435dd8b.png
然后选择ES8266这个设备,对使用的
989395ee8406209f15.png
394295ee8407f28d65.png



2、配置添加mqtt客户端支持
398845ee8896103b20.png



五、开发控制器程序
/*
代码 mycontrol.c
负责从云端mqtt服务器订阅控制消息,根据不同消息来驱动gpio接口来驱动
小车电机不同的转动,实现前进、后退、左转和右转

*/

#include <rtthread.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <drivers/pin.h>
#include "../drivers/drv_gpio.h"
#include "paho_mqtt.h"

#define DBG_ENABLE
#define DBG_SECTION_NAME    "mqtt.sample"
#define DBG_LEVEL           DBG_LOG
#define DBG_COLOR
#include <rtdbg.h>

/* 定义gpio接口编号,对应电机驱动器的4个逻辑接线口*/
#define IN4 (rt_uint8_t)49  //控制电机B
#define IN3 (rt_uint8_t)51
#define IN2 (rt_uint8_t)54 //控制电机A
#define IN1 (rt_uint8_t)56

/*定义mqtt服务器地址,端口等信息*/
#define MQTT_URI                "tcp://www.iloveai.club:1883"
#define MQTT_USERNAME           "admin"
#define MQTT_PASSWORD           "admin"
#define MQTT_SUBTOPIC           "driver"
#define MQTT_PUBTOPIC           "/mqtt/test"
#define MQTT_WILLMSG            "Goodbye!"

/* define MQTT client context */
static MQTTClient client;
static int is_started = 0;



/* 根据str的值来驱动电机进行不同的转动组合,实现前进、后退、左转、右转*/
void drive(char *str)
{
    // pin 初始化

    hw_pin_init();
   
    // 把相应 gpio 设为输出模式
    rt_pin_mode(IN4, PIN_MODE_OUTPUT);
    rt_pin_mode(IN3, PIN_MODE_OUTPUT);
    rt_pin_mode(IN2, PIN_MODE_OUTPUT);
    rt_pin_mode(IN1, PIN_MODE_OUTPUT);  

    if(strcmp(str,"forward") == 0){  //AB电机都正转

        rt_pin_write(IN1,PIN_HIGH);
        rt_pin_write(IN2,PIN_LOW);
        rt_pin_write(IN3,PIN_HIGH);
        rt_pin_write(IN4,PIN_LOW);

    }else if(strcmp(str,"back") == 0){ //AB电机都倒转

        rt_pin_write(IN1,PIN_LOW);
        rt_pin_write(IN2,PIN_HIGH);
        rt_pin_write(IN3,PIN_LOW);
        rt_pin_write(IN4,PIN_HIGH);

    }else if(strcmp(str,"left") == 0){ //A电机倒转,B电机正转
       rt_pin_write(IN3,PIN_HIGH);
        rt_pin_write(IN4,PIN_LOW);
        rt_pin_write(IN1,PIN_LOW);
        rt_pin_write(IN2,PIN_HIGH);

    }else if(strcmp(str,"right") == 0){ //A电机正转,B电机倒转
        rt_pin_write(IN1,PIN_HIGH);
        rt_pin_write(IN2,PIN_LOW);
        rt_pin_write(IN3,PIN_LOW);
        rt_pin_write(IN4,PIN_HIGH);


    }else if(strcmp(str,"stop") == 0){  //AB都不转

        rt_pin_write(IN1,PIN_HIGH);
        rt_pin_write(IN2,PIN_HIGH);
        rt_pin_write(IN3,PIN_LOW);
        rt_pin_write(IN4,PIN_LOW);

    }



}


static void mqtt_sub_callback(MQTTClient *c, MessageData *msg_data)
{
    *((char *)msg_data->message->payload + msg_data->message->payloadlen) = '\0';
    LOG_D("mqtt sub callback: %.*s %.*s",
               msg_data->topicName->lenstring.len,
               msg_data->topicName->lenstring.data,
               msg_data->message->payloadlen,
               (char *)msg_data->message->payload);
   
    drive((char *)msg_data->message->payload);
   
}

//启动mqtt客户端
static int mqtt_start(void)
{
    /* init condata param by using MQTTPacket_connectData_initializer */
    MQTTPacket_connectData condata = MQTTPacket_connectData_initializer;
    static char cid[20] = { 0 };

   

    if (is_started)
    {
        LOG_D("mqtt client is already connected.");
        return -1;
    }
    /* config MQTT context param */
    {
        client.isconnected = 0;
        client.uri = MQTT_URI;

        /* generate the random client ID */
        rt_snprintf(cid, sizeof(cid), "rtthread%d", rt_tick_get());
        /* config connect param */
        memcpy(&client.condata, &condata, sizeof(condata));
        client.condata.clientID.cstring = cid;
        client.condata.keepAliveInterval = 30;
        client.condata.cleansession = 1;
        client.condata.username.cstring = MQTT_USERNAME;
        client.condata.password.cstring = MQTT_PASSWORD;

        /* config MQTT will param. */
        client.condata.willFlag = 1;
        client.condata.will.qos = 1;
        client.condata.will.retained = 0;
        client.condata.will.topicName.cstring = MQTT_PUBTOPIC;
        client.condata.will.message.cstring = MQTT_WILLMSG;

        /* malloc buffer. */
        client.buf_size = client.readbuf_size = 1024;
        client.buf = rt_calloc(1, client.buf_size);
        client.readbuf = rt_calloc(1, client.readbuf_size);
        if (!(client.buf && client.readbuf))
        {
            LOG_D("no memory for MQTT client buffer!");
            return -1;
        }

      
        /* set subscribe table and event callback */
        client.messageHandlers[0].topicFilter = rt_strdup(MQTT_SUBTOPIC);
        client.messageHandlers[0].callback = mqtt_sub_callback;
        client.messageHandlers[0].qos = QOS1;

      
    }

    /* run mqtt client */
    paho_mqtt_start(&client);
    is_started = 1;

    return 0;
}




void mycontrol(void)
{
   
    mqtt_start();
}

/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(mycontrol, mycontrol);



六、联调测试


成果展示

照片:
微信图片_20200618093627.jpg
所有部件的“全家福”
微信图片_20200618093614.jpg
组装起来的样子,绿板是龙芯板,前面是WIFI模块。上面一大块大家伙是充电宝。
微信图片_20200618093553.jpg
这是前视图,可以看到电机驱动模块在中间夹层,还有电池组(看不到)

微信图片_20200618093619.jpg

将写做好的内核下载、运行,并启动mycontrol程序


视频
最后的效果演示视频请看下面链接:
https://www.bilibili.com/video/BV1rZ4y1H7iR
总结
1、本次开发最大的难度在于龙芯板子的驱动不全,在研究内核 与驱动开发时,最大的不足就是对硬件组成原理的不熟悉。这一方面需要进一步深入学习
2、这一次幸亏我有龙芯1c的板子,其rt-thread移值的较好,为了保证完成方案,在龙芯2k驱动开发遇到极大困难时,我及时用1c板子先行把整个方案的其它部分完成并测试成功。
3、特别感谢群里bigmagic flygoat等几个网友的帮助,没有他们的指点,我是完不成这个作业的。最后感谢主办方,龙芯公司给提供的这次学习机会

389815ee83c778c907.png
微信图片_20200618093609.jpg
微信图片_20200618093614.jpg

源码.zip

3.41 KB

龙芯板子+rt-thread实现web远程控制四轮小车.zip

1.01 MB

使用特权

评论回复
ly87802541| | 2020-6-17 23:13 | 显示全部楼层
nice job!

使用特权

评论回复
zeshoufx| | 2020-6-30 08:31 | 显示全部楼层
wlxn 发表于 2020-6-15 11:33
龙芯板子+rt-thread实现web远程控制四轮小车硬件方案硬件清单
  • 板子:龙芯机器人控制器(ls1c) / 龙芯派 ...

  • 谢谢分享,,,,,,,,,

    使用特权

    评论回复
    发新帖 我要提问
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    1

    主题

    16

    帖子

    0

    粉丝