本帖最后由 wlxn 于 2020-7-2 22:46 编辑
龙芯板子+rt-thread实现web远程控制四轮小车 硬件方案硬件清单
小车套件(含电池组、电机控制器)
龙芯机器人控制器(ls1c)
串口wifi模块(es8266)
部署架构
软件方案整个软件方案的架构分为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>
复制代码
手机端界面效果图 开发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测试工具是否接收到正确的信息。
三、搭建rt-thread开发环境
下载、安装env编译环境下载、安装交叉编译环境下载rt-thread 源码
方法请参考rt-thread官网。
(略)
四、配置内核
1、配置添加AT组件支持
首先在 env 中运行 menuconfig,在界面中找到 AT 组件,并勾选,如图所示。
RT-Thread 配置 AT 主界面 进入 AT 配置界面后,具体配置项如图 所示。
RT-Thread 配置 AT 组件界面 添加 SAL组件,启用 SAL(Socket抽象层),路径如图 所示。
启用 SAL(Socket 抽象层)配置图 将 SAL(Socket抽象层)配置为支持 AT指令,如图所示。
2、配置添加ESP8266模夫支持
ESP8266模块是一个标准的 AT 设备,能够使用 RT-Thread 组件进行操作。 首先添加 IOT中的 AT DEVICE包支持,如图 所示。
然后配置 ESP8266,添加该模块,如图 所示
然后选择ES8266这个设备,对使用的
2、配置添加mqtt客户端支持
五、开发控制器程序
- /*
- 代码 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);
复制代码
六、联调测试
略
成果展示
照片:
所有部件的“全家福”
组装起来的样子,绿板是龙芯板,前面是WIFI模块。上面一大块大家伙是充电宝。
这是前视图,可以看到电机驱动模块在中间夹层,还有电池组(看不到)
将写做好的内核下载、运行,并启动mycontrol程序
视频 最后的效果演示视频请看下面链接: https://www.bilibili.com/video/BV1rZ4y1H7iR 总结
1、本次开发最大的难度在于龙芯板子的驱动不全,在研究内核 与驱动开发时,最大的不足就是对硬件组成原理的不熟悉。这一方面需要进一步深入学习
2、这一次幸亏我有龙芯1c的板子,其rt-thread移值的较好,为了保证完成方案,在龙芯2k驱动开发遇到极大困难时,我及时用1c板子先行把整个方案的其它部分完成并测试成功。
3、特别感谢群里bigmagic flygoat等几个网友的帮助,没有他们的指点,我是完不成这个作业的。最后感谢主办方,龙芯公司给提供的这次学习机会
|