以前也用单片机做过WIFI小车,但是单片机没有自带WIFI,仍然需要用到小路由器作为图传和控制信号传输。既然肯定要用到路由器,那何不直接用路由器作为主控呢,这样就省掉了单片机。这次作为主控的GL-inet迷你路由,64M内存,16M Flash,支持OPENWRT系统,自带刷不死Uboot,Uart调试接口已焊好,另预留5个GPIO接口,可以充分满足DIY爱好者的需要。5个GPIO制作WIFI小车勉强够用。首先来一张完成图。

一、需要用到的材料

  1. Gl-inet路由器(有TPLink WR703n应该也可以);
  2. L298N电机驱动模块;
  3. 2节18650电池;
  4. 小车底盘(带电机);
  5. LM2596S降压模块(输出5V电压)。

二、硬件组装

一个最基本的WIFI小车至少需要2路电机控制前进、后退、左转、右转。GL-inet的5个gpio需要用其中4个来控制两路电机。以下是电路连接说明。

路由器GPIO接口,从左到右依次是21、22、18、19、20、GND,其中22、18、19、20分别连接电机驱动板IN1、IN2、IN3、IN4。

三、程序代码

GL-inet路由需要刷openwrt-gl-ser2net.bin,因为原版的固件,打开摄像头图像连接要进行登录管理后台,刷完这个固件就可以直接打开。

  1. 主控(/usr/lib/lua/xiaoche.lua)采用LUA脚本编写,代码如下。
require("gpio")

--分割字符串函数
function Split(szFullString, szSeparator)
local nFindStartIndex = 1
local nSplitIndex = 1
local nSplitArray = {}
while true do
local nFindLastIndex = string.find(szFullString, szSeparator, nFindStartIndex)
if not nFindLastIndex then
nSplitArray[nSplitIndex] = string.sub(szFullString, nFindStartIndex, string.len(szFullString))
break
end
nSplitArray[nSplitIndex] = string.sub(szFullString, nFindStartIndex, nFindLastIndex - 1)
nFindStartIndex = nFindLastIndex + string.len(szSeparator)
nSplitIndex = nSplitIndex + 1
end
return nSplitArray
end
--小车控制相关GPIO:左电机19,20;右电机18,22
--小车前进
function Forward()
writeGPIO(20,0)
writeGPIO(19,1)
writeGPIO(18,1)
writeGPIO(22,0)
end
--小车后退
function Backward()
writeGPIO(20,1)
writeGPIO(19,0)
writeGPIO(18,0)
writeGPIO(22,1) end
--小车左转弯
function Left()
writeGPIO(20,0)
writeGPIO(19,1)
writeGPIO(18,0)
writeGPIO(22,1)
end
--小车右转弯
function Right()
writeGPIO(20,1)
writeGPIO(19,0)
writeGPIO(18,1)
writeGPIO(22,0)
end
--小车停车
function Stop()
writeGPIO(20,0)
writeGPIO(19,0)
writeGPIO(18,0)
writeGPIO(22,0)
end local WebService = {} function WebService.Run()
local GET = os.getenv("QUERY_STRING")
--解析GET数据
local list = Split(GET,'=')
local c = list[2]
--操作GPIO
configureOutGPIO(20)
configureOutGPIO(19)
configureOutGPIO(18)
configureOutGPIO(22)
local status="Stop"
if(c=="forward")
then
Forward()
status="Forward"
elseif(c=="backward")
then
Backward()
status="Backward"
elseif(c=="left")
then
Left()
status="Left"
elseif(c=="right")
then
Right()
status="Right"
else
Stop()
status="Stop"
end
--返回数据
io.write("Content-type: text/html\nPragma: no-cache\n\n")
io.write(status)
end return WebService
  1. 需要用到一个GPIO操作函数库(/usr/lib/lua/gpio.lua),代码如下。
--@author: Ewelina, Rafa, Rafa
--GPIO utilities --Writes 'what' to 'where'
function writeToFile (where,what)
local fileToWrite=io.open(where, 'w')
fileToWrite:write(what)
fileToWrite:close()
end
--Reads a character from file 'where' and returns the string
function readFromFile (where)
local fileToRead=io.open(where, 'r')
fileStr = fileToRead:read(1)
fileToRead:close()
return fileStr
end --Returns true if file exists
function file_exists(name)
local f=io.open(name,"r")
if f~=nil then io.close(f) return true else return false end
end --Exports gpio ID to use as an output pin
function configureOutGPIO (id)
if not file_exists('/sys/class/gpio/gpio'..id..'/direction') then
writeToFile('/sys/class/gpio/export',id)
end
writeToFile('/sys/class/gpio/gpio'..id..'/direction','out')
end --Exports gpio ID to use as an input pin
function configureInGPIO (id)
if not file_exists('/sys/class/gpio/gpio'..id..'/direction') then
writeToFile('/sys/class/gpio/export',id)
end
writeToFile('/sys/class/gpio/gpio'..id..'/direction','in')
end --Reads GPIO 'id' and returns it's value
--@Pre: GPIO 'id' must be exported with configureInGPIO
function readGPIO(id)
gpioVal = readFromFile('/sys/class/gpio/gpio'..id..'/value')
return gpioVal
end --Writes a value to GPIO 'id'
--@Pre: GPIO 'id' must be exported with configureOutGPIO
function writeGPIO(id, val)
gpioVal = writeToFile('/sys/class/gpio/gpio'..id..'/value',val)
return true
end
  1. /www/cgi-bin/xc文件代码如下。
#!/usr/bin/lua
local Webservice = require 'xiaoche'
Webservice.Run()
  1. 控制面板/www/xiaoche.html代码。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=500;target-densitydpi=device-dpi; minimum-scale=0.5; maximum-scale=2,user-scalable=no">
<title>视频小车控制端</title>
<script type="text/javascript" src="jquery-1.9.1.min.js"></script>
<script>
function Down(){
$.get("cgi-bin/xc?c="+$(this).attr('id'), function(data){
$("#status").html(data);
});
} function Up(){
$.get("cgi-bin/xc?c=stop", function(data){
$("#status").html(data);
});
} function load (){
document.getElementById("forward").addEventListener("touchstart", Down);
document.getElementById("backward").addEventListener("touchstart", Down);
document.getElementById("left").addEventListener("touchstart", Down);
document.getElementById("right").addEventListener("touchstart", Down);
document.getElementById("forward").addEventListener("touchend", Up);
document.getElementById("backward").addEventListener("touchend", Up);
document.getElementById("left").addEventListener("touchend", Up);
document.getElementById("right").addEventListener("touchend", Up);
//加载摄像头图像
$("#video").html("<img src='http://"+location.hostname+":8083/?action=stream' style='width:100%;'>");
}
window.addEventListener('load',load,false);
</script>
<style>
html,body{
margin:0 auto;
background:#CCC;
}
.container{
margin:0 auto;
width:100%;
max-width:500px;
background-color:#CCC;
}
.video{
width:100%;
margin:0 auto;
text-align:center;
}
.control{
width:100%;
height:220px;
}
.button_fx{
width:100%;
height:100%;
}
</style>
</head> <body>
<div class="container">
<div id="video" class="video"></div>
<table class="control" border="0" cellpadding="5">
<tr>
<td width="15%" height="30%">&nbsp;</td>
<td width="15%"><input type="button" class="button_fx" value="前进" id="forward"></td>
<td width="15%">&nbsp;</td>
<td width="20%" align="center"><div id="status"> </div></td>
<td width="35%" align="center">摄像头控制</td>
</tr>
<tr>
<td width="15%" height="30%"><input type="button" class="button_fx" value="左转" id="left"></td>
<td width="15%">&nbsp;</td>
<td width="15%"><input type="button" class="button_fx" value="右转" id="right"></td>
<td width="20%">&nbsp;</td>
<td width="35%"><input type="button" class="button_fx" value="向上" id="gpio21s"></td>
</tr>
<tr>
<td width="15%" height="30%">&nbsp;</td>
<td width="15%"><input type="button" class="button_fx" value="后退" id="backward"></td>
<td width="15%">&nbsp;</td>
<td width="20%">&nbsp;</td>
<td width="35%"><input type="button" class="button_fx" value="向下" id="gpio21x"></td>
</tr>
</table>
</div>
</body>
</html>

此外需要用到的/www/jquery-1.9.1.min.js不要忘了下载到相应目录,别的jquery版本也可。

控制小车只要用手机连接路由器,浏览器打开http://路由器IP/xiaoche.html,建议用via浏览器,长按不会跳出菜单影响操作。把路由器设置位中继模式配合内网穿透,还可以在外网远程控制。最后来一段小车演示视频:

GL-inet路由器当主控制作WIFI视频小车的更多相关文章

  1. 树莓派小车By 树莓派爱好者ITJoker(通过python socket通信实现树莓派视频小车)(一)

    本文由树莓派爱好者ITJoker 编辑,转载请注明出处.本人也有新浪博客同样是树莓派爱好者ITJoker 所需材料:树莓派2B或者2B以上,L2985n驱动板,若干排线,电池及电池盒,usb无线网卡( ...

  2. 制作Wi-Fi Ducky远程HID攻击设备

    1.介绍WIFI DUCKY 它是一个Wi-Fi控制的BadUSB设备来远程执行Ducky Scripts. 使用充当键盘的USB设备来注入攻击,Hak5 的 USB Rubber Ducky 是这种 ...

  3. 手把手教你制作AppPreview视频并上传到appStore进行审核

    手把手教你制作AppPreview视频并上传到appStore进行审核 注意,你需要使用iMovie才能够制作AppPreview视频文件,用QuickTime录制的无效! 最终效果 1. 新建一个事 ...

  4. 基于Qt的wifi智能小车的制作(一)

     基于Qt的wifi智能小车的制作(一) 好久不写博客了,真的是有点惭愧了.翻开上一次的博客,到现在已经2个多月了,只能说是自己太懒惰了!忙是另一回事!趁今天晚上有点时间回顾下这一段时间的收获以及做的 ...

  5. 追星女孩必备!使用Camtasia制作爱豆视频

    制作爱豆视频,我用得比较多的是Camtasia(Windows)教程录制.因为这款软件操作简单,功能强大,用起来相当顺手呢.而且更重要的是,Camtasia有录屏功能,电脑存量不足的情况下,真的很好用 ...

  6. 使用Camtasia制作冰雪奇缘视频

    冰雪奇缘的精良制作,以及场景的华丽,让很多女孩子都很喜欢.对于其中美丽的冰雪场景,我们还可以使用Camtasia(Windows系统)教程录制软件来做冰雪奇缘视频. Camtasia教程录制软件是一款 ...

  7. 通过Camtasia来制作画中画视频效果的方法

    随着全民娱乐化的发展,视频的形式也更加多种多样了.视频形式的多样化能让观众从不同形式的视频中观赏到更有趣味的内容.比如像画中画的视频形式,让视频中的人物看起来像与观众一同观看视频,或者形成两个视频的对 ...

  8. 通过本地IIS服务器+路由器==实现本地局域网WIFI覆盖

    这是一张手机连接wifi局域网下载视频的图片,速度可以达到10M/S左右,下面让我们来看一下,本地服务器是如何建立的. 1.启动本地IIS服务 步骤如下 一.电脑右键-属性-控制面板-程序和功能-打开 ...

  9. 使用 ESP8266 制作 WiFi 干扰器 - 无需密码即可使用任何 WiFi

    嘿,朋友,我是 Kedar,你有没有想阻止所有的 WiFi信号?或者只是想从 WiFi 踢某人或邻居 WiFi .那么,本玩法是你等待结束的时刻了.这是为你提供的.仅需 $8 的 DIY Wifi 干 ...

随机推荐

  1. 从壹开始前后端分离[.netCore 不定期 ] 36 ║解决JWT自定义中间件授权过期问题

    缘起 哈喽,老张的不定期更新的日常又开始了,在咱们的前后端分离的.net core 框架中,虽然已经实现了权限验证<框架之五 || Swagger的使用 3.3 JWT权限验证[修改]>, ...

  2. AntZipUtils【基于Ant的Zip压缩解压缩工具类】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 Android 压缩解压zip文件一般分为两种方式: 基于JDK的Zip压缩工具类 该版本存在问题:压缩时如果目录或文件名含有中文, ...

  3. springboot~ EventListener事件监听的使用

    EventListener事件触发和监听器可以对代码解耦,在一些与业务无关的,通用的操作方法,我们可以把它设计成事件监听器,像通知,消息这些模块都可以这样设计. 事件源 @Getter @Builde ...

  4. 阿里 Java 手册系列教程:为啥强制子类、父类变量名不同?

    摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 目录 父子类变量名相同会咋样? 为啥强制子类.父类变量名不同? ...

  5. 改行了,学C#

    C#数组: 定义方法 ]; ,]; //这个是二维数组 只有这一种定义方法,不像java有多种定义方法.等号前面在栈中初始化类型为一维数组类型(int[])或二维数组类型(int[,])的存储堆中地址 ...

  6. ASP.NET Core 基于JWT的认证(二)

    ASP.NET Core 基于JWT的认证(二) 上一节我们对 Jwt 的一些基础知识进行了一个简单的介绍,这一节我们将详细的讲解,本次我们将详细的介绍一下 Jwt在 .Net Core 上的实际运用 ...

  7. Centos 7.6搭建Tomcat 环境,发布Java项目

    安装 JDK Tomcat 的安装依赖 JDK,在安装 Tomcat 之前需要先安装 Java JDK.输入命令 java -version,如果显示 JDK 版本,证明已经安装了 JDK java ...

  8. 用jQuery做一个选项卡

    1.首先我们点击选项卡的标题栏来改变内容

  9. Python从零搭建Conf_Web配置管理平台

    环境 CentOS 6/7 x64 Python:2 .7.6 Etcd: 3.2.18 Confd:0 .16.0 Nginx: 1.12.1 效果演示 一,拓扑图: 二.涉及软件 ETD: .分布 ...

  10. Java学习点滴——初识Java

    基于<Java编程思想>第四版 前言 "程序就是算法加数据结构",而算法就是控制语句加操作符,编写一个程序就是使用控制语句加操作符去操作数据结构,因此我从Java的控制 ...