微信小程序实现树莓派(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 (十一) 实战表单验证与自动完成提示插件
Feb 23 Javascript
JavaScript 一道字符串分解的题目
Aug 03 Javascript
js中判断Object、Array、Function等引用类型对象是否相等
Aug 29 Javascript
javascript新建标签,判断键盘输入,以及判断焦点(示例代码)
Nov 25 Javascript
JavaScript数组和循环详解
Apr 27 Javascript
介绍一个简单的JavaScript类框架
Jun 24 Javascript
jquery中表单 多选框的一种巧妙写法
Sep 06 Javascript
JS实现的模仿QQ头像资料卡显示与隐藏效果
Apr 07 Javascript
VUE 全局变量的几种实现方式
Aug 22 Javascript
Vuejs2 + Webpack框架里,模拟下载的实例讲解
Sep 05 Javascript
JavaScript对象拷贝与赋值操作实例分析
Dec 10 Javascript
Weex开发之地图篇的具体使用
Oct 16 Javascript
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
php获取当前时间的毫秒数的方法
2014/01/26 PHP
PHP实现的简单网络硬盘
2015/07/29 PHP
php监测数据是否成功插入到Mysql数据库的方法
2016/11/25 PHP
php生成0~1随机小数的方法(必看)
2017/04/05 PHP
PHP _construct()函数讲解
2019/02/03 PHP
JavaScript 页面编码与浏览器类型判断代码
2010/06/03 Javascript
jQuery学习笔记(3)--用jquery(插件)实现多选项卡功能
2013/04/08 Javascript
详解JavaScript函数绑定
2013/08/18 Javascript
jquery的clone方法应用于textarea和select的bug修复
2014/06/26 Javascript
jQuery+css实现百度百科的页面导航效果
2014/12/16 Javascript
jQuery制作简单柱状图实例
2015/01/28 Javascript
jQuery简单实现两级下拉菜单效果代码
2015/09/15 Javascript
javascript图片滑动效果实现
2021/01/28 Javascript
使用jsonp实现跨域获取数据实例讲解
2016/12/25 Javascript
浅谈jQuery中的$.extend方法来扩展JSON对象
2017/02/12 Javascript
ES6中参数的默认值语法介绍
2017/05/03 Javascript
浅析Vue.js中v-bind v-model的使用和区别
2018/12/04 Javascript
JavaScript基于SVG的图片切换效果实例代码
2020/12/15 Javascript
[01:05]主宰至宝剑心之遗
2017/03/16 DOTA
[04:51]TI10典藏宝瓶Ⅱ外观视频展示
2020/08/15 DOTA
初学python数组的处理代码
2011/01/04 Python
Python三级目录展示的实现方法
2016/09/28 Python
Python matplotlib 画图窗口显示到gui或者控制台的实例
2018/05/24 Python
Python socket实现多对多全双工通信的方法
2019/02/13 Python
Python 调用 Windows API COM 新法
2019/08/22 Python
Python socket实现的文件下载器功能示例
2019/11/15 Python
Python PyInstaller库基本使用方法分析
2019/12/12 Python
解决pyinstaller打包运行程序时出现缺少plotly库问题
2020/06/02 Python
基于Tensorflow的MNIST手写数字识别分类
2020/06/17 Python
Python如何将将模块分割成多个文件
2020/08/04 Python
python 解决函数返回return的问题
2020/12/05 Python
利用CSS3实现折角效果实例源码
2016/09/28 HTML / CSS
泰国国际航空公司官网:Thai Airways International
2019/12/04 全球购物
会计人员岗位职责
2014/03/19 职场文书
2015年社区流动人口工作总结
2015/05/12 职场文书
Python基础之元组与文件知识总结
2021/05/19 Python