微信小程序实现树莓派(raspberry pi)小车控制


Posted in Javascript onFebruary 12, 2020

本文是基于上一篇“网页版树莓派小车控制程序”改造而成。主要也练习了一下微信小程序的开发。这里简单记录一下主要代码片段。也是趟过了许多的坑,例如:微信小程序不支持完全全屏,微信小程序不能横屏展示。所以开发过程中也用了一些非常手段。可以说这只是一个很基本的demo,所以里面很多东西,比如摄像头监控ip、页面元素定位我都使用了写死的值。特别是界面,我只是在iPhone 6上面做的实验,所以换到其他手机上时,界面就会变型了。

1. 基本思路

  • 进入小程序时展示index页,可以让用户输入服务端url(模拟上一篇中在浏览器获取get请求)
  • 然后跳转到实际的小车控制界面,并可以通过点击按钮实现小车控制
  • 控制小车的移动,主要是在control.js中定义了界面按钮事件的响应,在响应事件的过程中实现http请求的发送

index页面如下: 

微信小程序实现树莓派(raspberry pi)小车控制

进去之后的页面如下(其中中间空白处会展示摄像头监控,不过我并没有启动,所以看不见):

微信小程序实现树莓派(raspberry pi)小车控制

2. 代码结构如下:

其中,index下面是首页,control是控制页面,res目录下存放的是图片资源

微信小程序实现树莓派(raspberry pi)小车控制

3. index目录

index.js

//index.js
//获取应用实例
const app = getApp()
 
Page({
 data: {
 logo: "/res/rasp-logo.png",
 welcome: "欢迎使用树莓小车",
 enterBtn: "进入",
 PromoteMsg: "Please enter the server address (eg: http://x.x.x.x:8080)",
 reqURL: ""
 },
 // 从输入框中获取用户输入的服务器地址信息
 getURL: function (e) {
 this.setData({
 reqURL: e.detail.value
 })
 },
 enterClicked: function (e) {
 /*
 * 当按下进入按钮,需要做以下事情:
 * 1. 首先判断用户是否已经在输入框中输入完整的服务器地址
 * 2. 发起一个到服务器的GET请求,并分析服务器的响应结果
 * 3. 跳转到小车控制界面
 */
 console.log(this.data.reqURL)
 
 if (this.data.reqURL == '') {
 wx.showModal({
 title: '提示',
 content: '请先输入正确的服务器地址!',
 })
 return
 }
 
 // 发起到服务器的GET请求
 wx.request({
 url: this.data.reqURL,
 success: function (res) {
 // 在这里获取POST请求地址,以及视频流地址,然后赋值给全局变量,供control页面调用
 console.log(res.data.match(/url = \"(\S*)\"/)[1])
 console.log(res.data.match(/src=\"(\S*)\"/)[1])
 app.globalData.postURL = res.data.match(/url = \"(\S*)\"/)[1]
 app.globalData.cameraURL = res.data.match(/src=\"(\S*)\"/)[1]
 
 // 跳转到control页面
 wx.navigateTo({
  url: '/pages/control/control',
 })
 },
 fail: function(res) {
 wx.showModal({
  title: '提示',
  content: '请检查输入的服务器地址!',
 })
 }
 })
 }
})

index.json:无数据,只有一对打括号

index.wxml

<!--index.wxml-->
<view>
 <view class="welcome">
 <view class="logo">
 <image style="width: 250rpx; height: 250rpx" src="{{logo}}"></image>
 </view>
 <view>
 <text class="words">{{welcome}}</text>
 </view>
 </view>
 
 <input class="requestURL" type="text" placeholder="{{PromoteMsg}}" focus='1' cursor='10' confirm-type="done" bindinput='getURL'></input>
 <button class='enter' bindtap='enterClicked'>{{enterBtn}}</button>
</view>

index.wxss

/**index.wxss**/
.welcome{
 display: flex;
 margin-top: 50rpx;
 flex-direction: column;
 align-items: center;
 justify-content: space-between;
}
 
.requestURL{
 margin: 50rpx 10rpx 30rpx 10rpx;
 border: 1px solid gray;
 font-style: italic;
 font-size: small
}
 
.enter{
 margin-right: 10rpx;
 width: 150rpx;
 height: 60rpx;
 font-size: small
}

4. control目录

control.js

// pages/control/control.js
const app = getApp()
 
Page({
 
 /**
 * 页面的初始数据
 */
 data: {
 // Car control images
 "forwardBtn": "/res/forward.png",
 "leftBtn": "/res/left.png",
 "rightBtn": "/res/right.png",
 "backLeftBtn": "/res/back-left.png",
 "backRightBtn": "/res/back-right.png",
 "backBtn": "/res/backward.png",
 
 // Camera control images
 "upBtn": "/res/forward.png",
 "camLeftBtn": "/res/camLeft.png",
 "camRightBtn": "/res/camRight.png",
 "downBtn": "/res/backward.png",
 "resetBtn": "/res/reset.png"
 },
 
 carMove: function(event) {
 wx.request({
 url: this.data.postURL,
 data: event.currentTarget.dataset.direction,
 method: "POST",
 success: function(res){
 
 },
 fail: function(res){
 
 }
 })
 },
 
 carStop: function(event) {
 wx.request({
 url: this.data.postURL,
 data: "S",
 method: "POST",
 success: function (res) {
 
 },
 fail: function (res) {
 
 }
 })
 },
 
 camMove: function(event) {
 wx.request({
 url: this.data.postURL,
 data: event.currentTarget.dataset.direction,
 method: "POST",
 success: function (res) {
 
 },
 fail: function (res) {
 
 }
 })
 },
 
 /**
 * 生命周期函数--监听页面加载
 */
 onLoad: function (options) {
 //this.data.cameraURL = app.globalData.cameraURL
 this.setData({
 cameraURL: app.globalData.cameraURL,
 postURL: app.globalData.postURL
 })
 console.log(this.data.cameraURL)
 console.log("post url in control page: " + app.globalData.postURL)
 },
 
 /**
 * 生命周期函数--监听页面初次渲染完成
 */
 onReady: function () {
 
 },
 
 /**
 * 生命周期函数--监听页面显示
 */
 onShow: function () {
 //console.log(wx.getSystemInfoSync().windowWidth)
 //console.log(wx.getSystemInfoSync().windowHeight)
 },
 
 /**
 * 生命周期函数--监听页面隐藏
 */
 onHide: function () {
 
 },
 
 /**
 * 生命周期函数--监听页面卸载
 */
 onUnload: function () {
 
 },
 
 /**
 * 页面相关事件处理函数--监听用户下拉动作
 */
 onPullDownRefresh: function () {
 
 },
 
 /**
 * 页面上拉触底事件的处理函数
 */
 onReachBottom: function () {
 
 },
 
 /**
 * 用户点击右上角分享
 */
 onShareAppMessage: function () {
 
 }
})

control.json

{
 "navigationBarBackgroundColor": "#ffffff",
 "navigationBarTextStyle": "black",
 "navigationBarTitleText": "树莓小车",
 "backgroundColor": "#eeeeee",
 "backgroundTextStyle": "light",
 "enablePullDownRefresh": false,
 "navigationStyle": "custom",
 "disableScroll": true
}

control.wxml

<!--pages/control/control.wxml-->
<view class='control'>
 <!-- This image shows the camera view -->
 <image class='cameraView' src='http://192.168.1.104:8080/?action=stream' style="z-index:1"></image>
 
 <!-- The following six images control the car move -->
 <image class='button' id='forward' src='{{forwardBtn}}' style="position:absolute;z-index:2" bindtouchstart='carMove' data-direction='F' bindtouchend='carStop'></image>
 <image class='button' id='left' src='{{leftBtn}}' style="position:absolute;z-index:2" bindtouchstart='carMove' data-direction='L' bindtouchend='carStop'></image>
 <image class='button' id='right' src='{{rightBtn}}' style="position:absolute;z-index:2" bindtouchstart='carMove' data-direction='R' bindtouchend='carStop'></image>
 <image class='button' id='backLeft' src='{{backLeftBtn}}' style="position:absolute;z-index:2" bindtouchstart='carMove' data-direction='BL' bindtouchend='carStop'></image>
 <image class='button' id='backRight' src='{{backRightBtn}}' style="position:absolute;z-index:2" bindtouchstart='carMove' data-direction='BR' bindtouchend='carStop'></image>
 <image class='button' id='back' src='{{backBtn}}' style="position:absolute;z-index:2" bindtouchstart='carMove' data-direction='B' bindtouchend='carStop'></image>
 
 <!-- The following images control the camera move -->
 <image class='button' id='up' src='{{upBtn}}' style="position:absolute;z-index:2" bindtouchstart='camMove' data-direction='VU'></image>
 <image class='button' id='camLeft' src='{{camLeftBtn}}' style="position:absolute;z-index:2" bindtouchstart='camMove' data-direction='HL'></image>
 <image class='button' id='camRight' src='{{camRightBtn}}' style="position:absolute;z-index:2" bindtouchstart='camMove' data-direction='HR'></image>
 <image class='button' id='down' src='{{downBtn}}' style="position:absolute;z-index:2" bindtouchstart='camMove' data-direction='VD'></image>
 <image class='button' id='reset' src='{{resetBtn}}' style="position:absolute;z-index:2" bindtouchstart='camMove' data-direction='RESET'></image>
</view>

control.wxss

/* pages/control/control.wxss */
 
.control {
 width: 100%;
 height: 100%;
 transform: rotate(90deg);
 background-color: #eee;
 justify-content: center;
}
 
.cameraView {
 margin-left: 0px;
 width: 603px;
 height: 375px;
 background-color: #eee;
 justify-content: center;
}
 
.button {
 height: 60px;
 width: 60px;
 opacity: 0.3;
}
 
#forward {
 left: 60px;
 top: 135px;
}
 
#left {
 left: 0px;
 top: 195px;
}
 
#right {
 left: 120px;
 top: 195px;
}
 
#backLeft {
 left: 0px;
 top: 255px;
}
 
#backRight {
 left: 120px;
 top: 255px;
}
 
#back {
 left: 60px;
 top: 315px;
}
 
#up {
 left: 480px;
 top: 195px;
}
 
#camLeft {
 left: 420px;
 top: 255px;
}
 
#camRight {
 left: 540px;
 top: 255px;
}
 
#down {
 left: 480px;
 top: 315px;
}
 
#reset{
 left: 480px;
 top: 135px
}

5. 工程全局控制

app.js:实际似乎并没有用到,里面都是工程创建时的默认代码

//app.js
App({
 onLaunch: function () {
 // 展示本地存储能力
 var logs = wx.getStorageSync('logs') || []
 logs.unshift(Date.now())
 wx.setStorageSync('logs', logs)
 
 // 登录
 wx.login({
 success: res => {
 // 发送 res.code 到后台换取 openId, sessionKey, unionId
 }
 })
 // 获取用户信息
 wx.getSetting({
 success: res => {
 if (res.authSetting['scope.userInfo']) {
  // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
  wx.getUserInfo({
  success: res => {
  // 可以将 res 发送给后台解码出 unionId
  this.globalData.userInfo = res.userInfo
 
  // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
  // 所以此处加入 callback 以防止这种情况
  if (this.userInfoReadyCallback) {
  this.userInfoReadyCallback(res)
  }
  }
  })
 }
 }
 })
 },
 globalData: {
 userInfo: null,
 postURL: null,
 cameraURL: null
 }
})

app.json:

{
 "pages": [
 "pages/index/index",
 "pages/control/control"
 ],
 "window": {
 "backgroundTextStyle": "light",
 "navigationBarBackgroundColor": "#fff",
 "navigationBarTitleText": "树莓小车",
 "navigationBarTextStyle": "black",
 "showStatusBar": false
 }
}

app.wxss:

/**app.wxss**/
.container {
 height: 100%;
 display: flex;
 flex-direction: column;
 align-items: center;
 justify-content: space-between;
 padding: 200rpx 0;
 box-sizing: border-box;
} 
project.control.json:
{
 "description": "项目配置文件。",
 "packOptions": {
 "ignore": []
 },
 "setting": {
 "urlCheck": false,
 "es6": true,
 "postcss": true,
 "minified": true,
 "newFeature": true
 },
 "compileType": "miniprogram",
 "libVersion": "2.0.4",
 "appid": "wx18414b9f85bfc895",
 "projectname": "wechat-control",
 "isGameTourist": false,
 "condition": {
 "search": {
 "current": -1,
 "list": []
 },
 "conversation": {
 "current": -1,
 "list": []
 },
 "game": {
 "currentL": -1,
 "list": []
 },
 "miniprogram": {
 "current": -1,
 "list": []
 }
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jquery 3D球状导航的文章分类
Jul 06 Javascript
jQuery表格排序组件-tablesorter使用示例
May 26 Javascript
基于jQuery插件实现环形图标菜单旋转切换特效
May 15 Javascript
JSON遍历方式实例总结
Dec 07 Javascript
javascript事件处理模型实例说明
May 31 Javascript
jQuery页面加载初始化的3种方法(推荐)
Jun 02 Javascript
javascript checkbox/radio onchange不能兼容ie8处理办法
Jun 13 Javascript
react 国际化的实现代码示例
Sep 14 Javascript
深入理解js A*寻路算法原理与具体实现过程
Dec 13 Javascript
JS实现可视化音频效果的实例代码
Jan 16 Javascript
简单了解Vue + ElementUI后台管理模板
Apr 07 Javascript
解决Vue+SpringBoot+Shiro跨域问题
Jun 09 Vue.js
JavaScript 替换所有匹配内容及正则替换方法
Feb 12 #Javascript
使用webpack搭建pixi.js开发环境
Feb 12 #Javascript
十分钟教你上手ES2020新特性
Feb 12 #Javascript
node.js基于dgram数据报模块创建UDP服务器和客户端操作示例
Feb 12 #Javascript
解决三元运算符 报错“SyntaxError: can''t assign to conditional expression”
Feb 12 #Javascript
node.js使用zlib模块进行数据压缩和解压操作示例
Feb 12 #Javascript
浅析vue-router实现原理及两种模式
Feb 11 #Javascript
You might like
IIS6.0+PHP5.x+MySQL5.x+Zend3.0x+GD+phpMyAdmin2.8x通用安装实例(已经完成)
2006/12/06 PHP
PHP zlib扩展实现页面GZIP压缩输出
2010/06/17 PHP
PHP sprintf()函数用例解析
2011/05/18 PHP
学习php中的正则表达式
2014/08/17 PHP
php获取错误信息的方法
2015/07/17 PHP
Laravel下生成验证码的类
2017/11/15 PHP
js禁止小键盘输入数字功能代码
2011/08/01 Javascript
跨域传值即主页面与iframe之间互相传值
2013/12/09 Javascript
使用jquery写个更改表格行顺序的小功能
2014/04/29 Javascript
Json实现异步请求提交评论无需跳转其他页面
2014/10/11 Javascript
node.js中的console.time方法使用说明
2014/12/09 Javascript
jQuery实现响应鼠标滚动的动感菜单效果
2015/09/21 Javascript
jQuery基于事件控制实现点击显示内容下拉效果
2017/03/07 Javascript
微信小程序HTTP接口请求封装的实现
2019/02/21 Javascript
layui2.0使用table+laypage实现真分页
2019/07/27 Javascript
Node.js HTTP服务器中的文件、图片上传的方法
2019/09/23 Javascript
[44:37]完美世界DOTA2联赛PWL S3 Forest vs access 第一场 12.11
2020/12/13 DOTA
[01:11]steam端dota2实名认证操作流程视频
2021/03/11 DOTA
python的几种开发工具介绍
2007/03/07 Python
Python使用正则表达式获取网页中所需要的信息
2018/01/29 Python
Django实战之用户认证(初始配置)
2018/07/16 Python
python  ceiling divide 除法向上取整(或小数向上取整)的实例
2019/12/27 Python
浅谈python3打包与拆包在函数的应用详解
2020/05/02 Python
解决HTML5手机端页面缩放的问题
2017/10/27 HTML / CSS
德国机车企业:FC-Moto
2017/10/27 全球购物
Lookfantastic香港官网:英国知名美妆购物网站
2018/06/19 全球购物
乌克兰在线药房:Аптека24
2019/10/30 全球购物
七年级政治教学反思
2014/02/03 职场文书
工程造价专业求职信
2014/07/17 职场文书
“四风”问题的主要表现和危害思想汇报
2014/09/19 职场文书
合伙开公司协议书范本
2014/10/28 职场文书
写给医生的感谢信
2015/01/22 职场文书
2015年少先队活动总结
2015/03/25 职场文书
家庭经济困难证明
2015/06/23 职场文书
SQL Server使用导出向导功能
2022/04/08 SQL Server
Python采集壁纸并实现炫轮播
2022/04/30 Python