飞道的博客

简单WiFi控制小车系统(树莓派+python+web控制界面)

452人阅读  评论(0)

~~ 如果有什么问题可以在我的Five-great的博客留言,我会及时回复。欢迎来访交流 ~~


下面是小车 

 

 

好丑 对不对 ,不过反正可以蛇皮走位就行。

   蛇皮走位演示视频: https://pan.baidu.com/s/1RHHr8bRHWzSEAkrpwu99aw

 只需要 一个 index.html  和Index.py 就可以实现 简单WiFi 控制小车。

需要准备

  python 

   bottle 库

 bottle 安装

命令: pip install bottle

 

 

树莓派控制界面(web客户端)

  index.html 


  
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>遥控树莓派 </title>
  7. <link href="http://cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" media="screen">
  8. <script src="http://cdn.staticfile.org/jquery/2.2.4/jquery.min.js"> </script>
  9. <style type="text/css">
  10. #front {
  11. margin-left: 55px;
  12. margin-bottom: 3px;
  13. }
  14. #rear{
  15. margin-top: 3px;
  16. margin-left: 55px;
  17. }
  18. .btn{
  19. background: #62559f;
  20. }
  21. </style>
  22. <script>
  23. $( function(){
  24. $( "button").click( function(){
  25. $.post( "/cmd", this.id, function(data,status){});
  26. });
  27. });
  28. </script>
  29. </head>
  30. <body>
  31. <div id="container" class="container">
  32. <div>
  33. <button id="front" class="btn btn-lg btn-primary glyphicon glyphicon-circle-arrow-up"> </button>
  34. </div>
  35. <div>
  36. <button id='leftFront' class="btn btn-lg btn-primary glyphicon glyphicon-circle-arrow-left"> </button>
  37. <button id='stop' class="btn btn-lg btn-primary glyphicon glyphicon-stop"> </button>
  38. <button id='rightFront' class="btn btn-lg btn-primary glyphicon glyphicon-circle-arrow-right"> </button>
  39. </div>
  40. <div>
  41. <button id='rear' class="btn btn-lg btn-primary glyphicon glyphicon-circle-arrow-down"> </button>
  42. </div>
  43. <div>
  44. <button id='leftRear' class="btn btn-lg btn-primary glyphicon">左后转 </button>
  45. <button id='rightRear' class="btn btn-lg btn-primary glyphicon">右后转 </button>
  46. </div>
  47. </div>
  48. <script src="//cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"> </script>
  49. </body>
  50. </html>

 

  js脚本解释:


  
  1. <script>
  2. $( function(){
  3. $( "button").click( function(){
  4. $.post( "/cmd", this.id, function(data,status){});
  5. //表示 按钮对应的id值 会被传入树莓派服务器中,就如同 你在树莓派的命令行(cmd)中输入 id 的值
  6. });
  7. });
  8. </script>

 

树莓派小车控制程序+we服务端

 Index.py


  
  1. #!/usr/bin/env python3
  2. # -*- coding:utf-8 -*-
  3. from bottle import get,post,run,request,template
  4. import RPi.GPIO as GPIO
  5. import time
  6. import sys
  7. #### 定义Car类
  8. class Car(object):
  9. def __init__(self):
  10. self.enab_pin = [ 5, 6, 13, 19]
  11. #### self.enab_pin是使能端的pin
  12. self.inx_pin = [ 21, 22, 23, 24]
  13. #### self.inx_pin是控制端in的pin
  14. self.RightAhead_pin = self.inx_pin[ 0]
  15. self.RightBack_pin = self.inx_pin[ 1]
  16. self.LeftAhead_pin = self.inx_pin[ 2]
  17. self.LeftBack_pin = self.inx_pin[ 3]
  18. #### 分别是右轮前进,右轮退后,左轮前进,左轮退后的pin
  19. self.setup()
  20. #### setup函数初始化端口
  21. def setup(self):
  22. print ( "begin setup ena enb pin")
  23. GPIO.setmode(GPIO.BCM)
  24. GPIO.setwarnings( False)
  25. for pin in self.enab_pin:
  26. GPIO.setup(pin,GPIO.OUT)
  27. GPIO.output(pin,GPIO.HIGH)
  28. #### 初始化使能端pin,设置成高电平
  29. pin = None
  30. for pin in self.inx_pin:
  31. GPIO.setup(pin,GPIO.OUT)
  32. GPIO.output(pin,GPIO.LOW)
  33. #### 初始化控制端pin,设置成低电平
  34. print ( "setup ena enb pin over")
  35. #### fornt函数,小车前进
  36. def front(self):
  37. self.setup()
  38. GPIO.output(self.RightAhead_pin,GPIO.HIGH)
  39. GPIO.output(self.LeftAhead_pin,GPIO.HIGH)
  40. #### leftFront函数,小车左拐弯
  41. def leftFront(self):
  42. self.setup()
  43. GPIO.output(self.RightAhead_pin,GPIO.HIGH)
  44. #### rightFront函数,小车右拐弯
  45. def rightFront(self):
  46. self.setup()
  47. GPIO.output(self.LeftAhead_pin,GPIO.HIGH)
  48. #### rear函数,小车后退
  49. def rear(self):
  50. self.setup()
  51. GPIO.output(self.RightBack_pin,GPIO.HIGH)
  52. GPIO.output(self.LeftBack_pin,GPIO.HIGH)
  53. #### leftRear函数,小车左退
  54. def leftRear(self):
  55. self.setup()
  56. GPIO.output(self.RightBack_pin,GPIO.HIGH)
  57. #### rightRear函数,小车右退
  58. def rightRear(self):
  59. self.setup()
  60. GPIO.output(self.LeftBack_pin,GPIO.HIGH)
  61. #### 定义main主函数
  62. def main(status):
  63. car = Car()
  64. if status == "front":
  65. car.front()
  66. elif status == "leftFront":
  67. car.leftFront()
  68. elif status == "rightFront":
  69. car.rightFront()
  70. elif status == "rear":
  71. car.rear()
  72. elif status == "leftRear":
  73. car.leftRear()
  74. elif status == "rightRear":
  75. car.rightRear()
  76. elif status == "stop":
  77. car.setup()
  78. @get("/")
  79. def index():
  80. return template( "index")
  81. @post("/cmd")
  82. def cmd():
  83. adss=request.body.read().decode()
  84. print( "按下了按钮:"+adss)
  85. main(adss)
  86. return "OK"
  87. run(host= "0.0.0.0")

 

web服务端 实际就这点代码, 主要是 bottle 库的强大,(实际控制的小车的代码 根据自己的需求改就行了)


  
  1. from bottle import get,post,run,request,template
  2. @get("/")
  3. def index():
  4. return template( "index")
  5. #### 这个是 客户端请求 服务端就发给一个 index.html 控制界面给客户端
  6. @post("/cmd")
  7. def cmd():
  8. adss=request.body.read().decode() #### 接收到 客户端 发过来的数据
  9. print( "按下了按钮:"+adss)
  10. main(adss) #### 传值到主函数 实现对应功能
  11. return "OK"
  12. run(host= "0.0.0.0") #### 开启服务端

 

运行 index.py 开启服务器:

然后打开浏览器(手机浏览器也可以但必须在同一个局域网内) 输入 树莓派的ip 我的是 192.168.191.4:8080

有可能 打开比较慢  10分钟内吧 哈哈哈(我第一次打开 就用了好久 都以为没有成功)

手机端输入ip

登录成功!!!

 

 

输入之后  服务器会给你抛出一个 index.html 控制文件。

然后就可以点击按键 控制小车了  下面是 服务端中反馈

 

框架搭好后,根据自己需求更改 。

 

补充说明一下啊 因为我改过系统的语言和编码设置 (支持utf-8)

  详情 :  树莓派 设置系统中文 并安装中文输入法

当很多人遇到

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 的错误,原因是python的str默认是ascii编码,和unicode编码冲突,就会报这个标题错误,解决办法是

1. 开头添加

                          import sys

                           reload(sys)

                           sys.setdefaultencoding('utf8') 

 

2.暴力一点,把所有中文字符 汉字什么的 包括注释了的 都统统删掉 也可以解决

 

 还有遇到 bottle 下载安装后 ,运行说 没有 安装 bottle  可能是 你把 bottle 安装到 python 2.7 环境下,而在python3 环境下找不到。

解决办法:

1 在命令行中 用对应pythonX  环境下运行

2.在执行脚本代码前 手动引包(得找到bottle 安装路径


如果你想了解更多树莓派相关知识或则其他控制小车的手段

(如 自写网页,数据库,语音控制等)

可以此处留言或前往Five-great的博客留言板进行交流讨论 欢迎您的来访

能帮助您 ,留点个赞 ,关注一波 …^_^…

 

最后 奉上 采用 websocket 的实现的代码

先运行服务端代码 car.py,然后再 运行 car.html

  car.py 代码


  
  1. #coding=utf8
  2. import struct, socket, sys
  3. import hashlib
  4. import threading, random
  5. import time
  6. from base64 import b64encode, b64decode
  7. import RPi.GPIO as GPIO
  8. import sys
  9. GPIO.setmode(GPIO.BCM)
  10. GPIO.setwarnings( False)
  11. GPIO.setup( 17,GPIO.OUT)
  12. p=GPIO.PWM( 17, 600)
  13. p_pin = 35
  14. p.start(p_pin)
  15. #### 定义Car类
  16. class Car(object):
  17. def __init__(self):
  18. self.inx_pin = [ 19, 26, 5, 6]
  19. #### self.inx_pin是控制端in的pin
  20. self.RightAhead_pin = self.inx_pin[ 0]
  21. self.LeftAhead_pin = self.inx_pin[ 1]
  22. self.RightBack_pin = self.inx_pin[ 2]
  23. self.LeftBack_pin = self.inx_pin[ 3]
  24. #### 分别是右轮前进,左轮前进,右轮退后,左轮退后的pin
  25. self.RightP_pin= 17
  26. self.LeftP_pin = 27
  27. self.setup()
  28. #### setup函数初始化端口
  29. def setup(self):
  30. GPIO.setmode(GPIO.BCM)
  31. GPIO.setwarnings( False)
  32. #### 初始化使能端pin,设置成高电平
  33. pin = None
  34. for pin in self.inx_pin:
  35. GPIO.setup(pin,GPIO.OUT)
  36. GPIO.output(pin,GPIO.LOW)
  37. #### 初始化控制端pin,设置成低电平
  38. print ( "setup ena enb pin over")
  39. #### fornt函数,小车前进
  40. def front(self):
  41. self.setup()
  42. GPIO.output(self.RightAhead_pin,GPIO.HIGH)
  43. GPIO.output(self.LeftAhead_pin,GPIO.HIGH)
  44. #### leftFront函数,小车左拐弯
  45. def leftFront(self):
  46. self.setup()
  47. GPIO.output(self.RightAhead_pin,GPIO.HIGH)
  48. #### rightFront函数,小车右拐弯
  49. def rightFront(self):
  50. self.setup()
  51. GPIO.output(self.LeftAhead_pin,GPIO.HIGH)
  52. #### rear函数,小车后退
  53. def rear(self):
  54. self.setup()
  55. GPIO.output(self.RightBack_pin,GPIO.HIGH)
  56. GPIO.output(self.LeftBack_pin,GPIO.HIGH)
  57. #### leftRear函数,小车左退
  58. def leftRear(self):
  59. self.setup()
  60. GPIO.output(self.RightBack_pin,GPIO.HIGH)
  61. #### rightRear函数,小车右退
  62. def rightRear(self):
  63. self.setup()
  64. GPIO.output(self.LeftBack_pin,GPIO.HIGH)
  65. #### 定义main主函数
  66. def main(status):
  67. car = Car()
  68. if status == "front":
  69. car.front()
  70. elif status == "leftFront":
  71. car.leftFront()
  72. elif status == "rightFront":
  73. car.rightFront()
  74. elif status == "rear":
  75. car.rear()
  76. elif status == "leftRear":
  77. car.leftRear()
  78. elif status == "rightRear":
  79. car.rightRear()
  80. elif status == "stop":
  81. car.setup()
  82. #p.stop()
  83. elif status == "q1":
  84. p.ChangeDutyCycle( 35)
  85. elif status == "q2":
  86. p.ChangeDutyCycle( 50)
  87. elif status == "q3":
  88. p.ChangeDutyCycle( 75)
  89. elif status == "q4":
  90. p.ChangeDutyCycle( 90)
  91. elif status == "q5":
  92. p.ChangeDutyCycle( 100)
  93. ##socket
  94. connectionlist = {}
  95. def decode(data):
  96. if not len(data):
  97. return False
  98. # 用数据包的第二个字节,与127作与位运算,拿到前七位。
  99. length = data[ 1] & 127
  100. # 这七位在数据头部分成为payload,如果payload等于126,就要再扩展2个字节。
  101. # 如果等于127,就要再扩展8个字节。
  102. # 如果小于等于125,那它就占这一个字节。
  103. if length == 126:
  104. extend_payload_len = data[ 2: 4]
  105. mask = data[ 4: 8]
  106. decoded = data[ 8:]
  107. elif length == 127:
  108. extend_payload_len = data[ 2: 10]
  109. mask = data[ 10: 14]
  110. decoded = data[ 14:]
  111. else:
  112. extend_payload_len = None
  113. mask = data[ 2: 6]
  114. decoded = data[ 6:]
  115. byte_list = bytearray()
  116. print(mask)
  117. print(decoded)
  118. # 当payload确定之后,再往后数4个字节,这4个字节成为masking key,再之后的内容就是接收到的数据部分。
  119. # 数据部分的每一字节都要和masking key作异或位运算,得出来的结果就是真实的数据内容。
  120. for i in range(len(decoded)):
  121. chunk = decoded[i] ^ mask[i % 4]
  122. byte_list.append(chunk)
  123. new_str = str(byte_list, encoding= "utf-8")
  124. print(new_str)
  125. return new_str
  126. def encode(data):
  127. data=str.encode(data)
  128. head = b'\x81'
  129. if len(data) < 126:
  130. head += struct.pack( 'B', len(data))
  131. elif len(data) <= 0xFFFF:
  132. head += struct.pack( '!BH', 126, len(data))
  133. else:
  134. head += struct.pack( '!BQ', 127, len(data))
  135. return head+data
  136. def sendMessage(message):
  137. global connectionlist
  138. for connection in connectionlist.values():
  139. connection.send(encode(message))
  140. def deleteconnection(item):
  141. global connectionlist
  142. del connectionlist[ 'connection'+item]
  143. class WebSocket(threading.Thread):
  144. GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
  145. def __init__(self,conn,index,name,remote, path="/"):
  146. threading.Thread.__init__(self)
  147. self.conn = conn
  148. self.index = index
  149. self.name = name
  150. self.remote = remote
  151. self.path = path
  152. self.buffer = ""
  153. def run(self):
  154. print( 'Socket%s Start!' % self.index)
  155. headers = {}
  156. self.handshaken = False
  157. while True:
  158. try:
  159. if self.handshaken == False:
  160. print ( 'Socket%s Start Handshaken with %s!' % (self.index,self.remote))
  161. self.buffer += bytes.decode(self.conn.recv( 1024))
  162. if self.buffer.find( '\r\n\r\n') != -1:
  163. header, data = self.buffer.split( '\r\n\r\n', 1)
  164. for line in header.split( "\r\n")[ 1:]:
  165. key, value = line.split( ": ", 1)
  166. headers[key] = value
  167. headers[ "Location"] = ( "ws://%s%s" %(headers[ "Host"], self.path))
  168. key = headers[ 'Sec-WebSocket-Key']
  169. token = b64encode(hashlib.sha1(str.encode(str(key + self.GUID))).digest())
  170. handshake= "HTTP/1.1 101 Switching Protocols\r\n"\
  171. "Upgrade: websocket\r\n"\
  172. "Connection: Upgrade\r\n"\
  173. "Sec-WebSocket-Accept: "+bytes.decode(token)+ "\r\n"\
  174. "WebSocket-Origin: "+str(headers[ "Origin"])+ "\r\n"\
  175. "WebSocket-Location: "+str(headers[ "Location"])+ "\r\n\r\n"
  176. self.conn.send(str.encode(str(handshake)))
  177. self.handshaken = True
  178. print( 'Socket%s Handshaken with %s success!' %(self.index, self.remote))
  179. sendMessage( 'Welcome, ' + self.name + ' !')
  180. else:
  181. msg = decode(self.conn.recv( 1024))
  182. main(msg)
  183. if msg == 'quit':
  184. print ( 'Socket%s Logout!' % (self.index))
  185. nowTime = time.strftime( '%H:%M:%S',time.localtime(time.time()))
  186. sendMessage( '%s %s say: %s' % (nowTime, self.remote, self.name+ ' Logout'))
  187. deleteconnection(str(self.index))
  188. self.conn.close()
  189. break
  190. else:
  191. #print('Socket%s Got msg:%s from %s!' % (self.index, msg, self.remote))
  192. nowTime = time.strftime( '%H:%M:%S',time.localtime(time.time()))
  193. sendMessage( '%s %s say: %s' % (nowTime, self.remote, msg))
  194. self.buffer = ""
  195. except Exception as e:
  196. self.conn.close()
  197. class WebSocketServer(object):
  198. def __init__(self):
  199. self.socket = None
  200. def begin(self):
  201. print( 'WebSocketServer Start!')
  202. self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  203. self.socket.bind(( "172.19.8.102", 8081))
  204. self.socket.listen( 50)
  205. global connectionlist
  206. i = 0
  207. while True:
  208. connection, address = self.socket.accept()
  209. username=address[ 0]
  210. newSocket = WebSocket(connection,i,username,address)
  211. newSocket.start()
  212. connectionlist[ 'connection'+str(i)]=connection
  213. i = i + 1
  214. if __name__ == "__main__":
  215. server = WebSocketServer()
  216. server.begin()

 

 car.html  代码:


  
  1. <!DOCTYPE html>
  2. <html lang="zh-cn">
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6.     <title>遥控树莓派 </title>
  7.     <link href="http://cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" media="screen">
  8.     <script src="http://cdn.staticfile.org/jquery/2.2.4/jquery.min.js"> </script>
  9.     <style type="text/css">
  10.         #front {
  11.             margin-left: 55px;
  12.             margin-bottom: 3px;
  13.         }
  14.         #rear{
  15.             margin-top: 3px;
  16.             margin-left: 55px;
  17.         }
  18.         .btn{
  19.               background: #62559f;
  20.             }
  21.     </style>
  22.     <script>
  23.   var socket;
  24.         function init() {
  25.             var host = "ws://192.168.1.111:8081/";
  26.             try {
  27.                 socket = new WebSocket(host);
  28.                 socket.onopen = function () {
  29.                  
  30.                 };
  31.                 socket.onmessage = function () {
  32.                     
  33.                 };
  34.                 socket.onclose = function () {
  35.                     
  36.                 };
  37.             }
  38.             catch (ex) {
  39.                 
  40.             }
  41.            
  42.         }
  43.         function send(msg) {
  44.             try {
  45.                 socket.send(msg);
  46.             } catch (ex) {
  47.                 
  48.             }
  49.         }
  50.         window.onbeforeunload = function () {
  51.             try {
  52.                 socket.send( 'quit');
  53.                 socket.close();
  54.                 socket = null;
  55.             }
  56.             catch (ex) {
  57.                 
  58.             }
  59.         };
  60.      
  61.        
  62.  
  63.  
  64.     </script>
  65. </head>
  66. <body onload="init()">
  67. <div id="container" class="container">
  68.     
  69.     <div>
  70.         <button id="front" class="btn btn-lg btn-primary glyphicon glyphicon-circle-arrow-up" onclick="send('front')"> </button>
  71.     </div>
  72.     <div>
  73.  
  74.         <button id='leftFront' class="btn btn-lg btn-primary glyphicon glyphicon-circle-arrow-left" onclick="send('leftFront')"> </button>
  75.         <button id='stop' class="btn btn-lg btn-primary glyphicon glyphicon-stop" onclick="send('stop')"> </button>
  76.         <button id='rightFront' class="btn btn-lg btn-primary glyphicon glyphicon-circle-arrow-right" onclick="send('rightFront')"> </button>
  77.     </div>
  78.     <div  style="height:50px;">
  79.         <button id='rear' class="btn btn-lg btn-primary glyphicon glyphicon-circle-arrow-down" onclick="send('rear')"> </button>
  80.     </div>
  81.     <div style="height:20px;"> </div>
  82.       <div style="height:50px;">
  83.         <button id='leftRear' class="btn btn-lg btn-primary glyphicon" onclick="send('leftRear')">左后转 </button>
  84.         <button id='rightRear' class="btn btn-lg btn-primary glyphicon" onclick="send('rightRear')">右后转 </button>
  85.     </div>
  86.       <div style="height:20px;"> </div>
  87.     <div  style="height:50px;">
  88.         <button id='q1' class="btn btn-lg btn-primary glyphicon" onclick="send('q1')">P1 </button>
  89.         <button id='q2' class="btn btn-lg btn-primary glyphicon" onclick="send('q2')">P2 </button>
  90.         <button id='q3' class="btn btn-lg btn-primary glyphicon" onclick="send('q3')">P3 </button>
  91.         <div style="height:20px;"> </div>
  92.         <button id='q4' class="btn btn-lg btn-primary glyphicon" onclick="send('q4')">P4 </button>
  93.         <button id='q5' class="btn btn-lg btn-primary glyphicon" onclick="send('q5')">P5 </button>
  94.     </div>
  95. </div>
  96.  
  97. <script src="//cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"> </script>
  98. </body>
  99. </html>

注意: host 端口号要匹配哦 


转载:https://blog.csdn.net/qq_41923622/article/details/85850780
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场