本帖最后由 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等几个网友的帮助,没有他们的指点,我是完不成这个作业的。最后感谢主办方,龙芯公司给提供的这次学习机会
|