微信小程序App实现小车方向控制

目录

概述

[1 系统框架结构](#1 系统框架结构)

[1.1 结构介绍](#1.1 结构介绍)

[1.2 硬件模块介绍](#1.2 硬件模块介绍)

[1.2.1 蓝牙模块介绍](#1.2.1 蓝牙模块介绍)

[1.2 .2 模块功能介绍](#1.2 .2 模块功能介绍)

[2 功能实现](#2 功能实现)

[2.1 微信小程序APP](#2.1 微信小程序APP)

[2.2 下位机功能](#2.2 下位机功能)

[3 功能测试](#3 功能测试)

[3.1 小程序UI测试](#3.1 小程序UI测试)

[3.2 小车方向控制](#3.2 小车方向控制)


微信小程序和蓝牙模块控制小车运行状态

概述

本文主要介绍使用微信小程序和蓝牙模块设计一个智能小程控制系统,笔者介绍了系统的实现框架结构,还介绍了微信小程序的代码结构和源代码。下位机部分树妖包括:小车方向控制代码,微信小程序与下位机的通信方法。

1 系统框架结构

1.1 结构介绍

1) 使用STM32F103 Timer8 产生4路PWM信号,用于控制电机的转向和调速

2) TIMER7和IO EXIT interrupt 控制光电编码器,用于计算当前电机的转速

3) STM32F103 UART-3接口和蓝牙模块通信

1.2 硬件模块介绍

1.2.1 蓝牙模块介绍

HC-08蓝牙串口通信模块是新一代的基于Bluetooth Specification V4.0 BLE 蓝牙协议的数传模块。无线工作频段为 2.4GHz ISM,调制方式是 GFSK。模块最大发射功率为4dBm,接收灵敏度-93dBm,空旷环境下和 手机可以实现 80 米超远距离通信。

其和MCU之间通过串口通信,软件设计也简单便捷,且不需要考虑蓝牙协议栈问题,非常适合做速成产品。

蓝牙模块与MCU之间连接图:

1.2 .2 模块功能介绍

1)测试传感器

工作原理:

当模块中的槽无遮挡时,接收管导通,DQ输出为低电平

当槽被遮挡时,DQ输出为高电平。

2)码盘

该码盘一周总共有20个孔,则其将一个圆分为20个等分,在测速的时候。只需记录其在1s时间内走过孔的个数,然后通过轮胎的周长与孔等分的关系,就能计算出速度。

3)轮胎参数

根据参数可得,轮胎的直径为:6.8cm

2 功能实现

2.1 微信小程序APP

1) UI设计

通过4个按钮控制小车的运动方向

2)代码实现:

在 detail.wxml 文件中实现如下UI代码

html 复制代码
<view class="connect_box">
    <text class='connect_device_name'>{{deviceName}}</text>
    <text wx:if="{{connected}}" class="connect_state" catchtap="DisConnectTap">已连接</text>
    <text wx:else class="connect_state" catchtap="DisConnectTap" >未连接</text>
</view>

<view class="block-content">

    <view class="block-item column">
            <view class="item-row slider-box">
                <view class="item-title" >
                  <image class="item-title-icon" src="/images/light_s.png" />温度(℃)
                </view>
                <view class="input-unit"></view>

                <view class="item-title" style="position: relative;  left: 30rpx; ">
                  <image class="item-title-icon" src="/images/light_s.png" />湿度(%)
                </view>
                <view class="input-unit"></view>
            </view>
          
            <view class="item-row">
                <view class="input-value">
                    <input type="digit" value="{{temphtValue}}" disabled="false"/>
                </view>

                <view class="input-value" style="position: relative;  left: -40rpx; ">
                    <input type="digit" value="{{humidityValue}}" disabled="false"/>
                </view>

            </view>
    </view> 

    <view class="block-item column">
            <view class="item-row slider-box">
                <view class="item-title">
                  <image class="item-title-icon" src="/images/light_s.png" />光照(lux)
                </view>
                <view class="input-unit"></view>

                <view class="item-title" style="position: relative;  left: 60rpx; ">
                  <image class="item-title-icon" src="/images/light_s.png" />SR测距(cm)
                </view>
                <view class="input-unit"></view>

            </view>
          
            <view class="item-row">
                <view class="input-value" >
                    <input type="digit" value="{{luxValue}}" disabled="false"/>
                </view>

                <view class="input-value" style="position: relative;  left: -40rpx; ">
                    <input type="digit" value="{{srValue}}" disabled="false"/>
                </view>

            </view>
    </view> 


    <view class="block-item column" style="width: 750rpx; height: 507rpx; display: flex; box-sizing: border-box; left: 0rpx; top: 0rpx; position: relative">
      <button type="primary" bindtap="run_up" plain="true" style="position: relative; left: 3rpx; top: 5rpx; width: 183rpx; height: 357rpx; display: block; box-sizing: border-box">前进</button>
      <button type="primary" bindtap="run_down" plain="true" style="position: relative; left: 0rpx; top: 263rpx; width: 188rpx; height: 326rpx; display: block; box-sizing: border-box">后退</button>
      <button type="primary" bindtap="run_left" plain="true" style="position: relative; left: -247rpx; top: 3rpx; width: 183rpx; height: 361rpx; display: block; box-sizing: border-box">左转</button>
      <button type="primary" bindtap="run_right" plain="true" style="position: relative; left: 256rpx; top: -82rpx; width: 183rpx; height: 343rpx; display: block; box-sizing: border-box">右转</button>
      <button type="primary" bindtap="run_stop" plain="true" style="position: relative; left: 5rpx; top: -173rpx; width: 179rpx; height: 361rpx; display: block; box-sizing: border-box">停止</button>
    </view> 
</view>

逻辑功能实现:

javascript 复制代码
const utils = require('utils.js')
const ble = require('bluetooth.js')
Page({
    /**
     * 页面的初始数据
     */
    data: {
        pageload: false,
        connected: false,
        send_hex: false,
        send_string: true,
        send_string_val: 'Hex',
        recv_string: true,
        recv_string_val: 'Hex',
        recv_value: '',
        send_number: 0,
        recv_number: 0,
        recv_hex: true,
        try_cnt:0,
        rece_string:'',
        deviceArray: []
    },


    /**
     * 生命周期函数--监听页面加载
     */
    onLoad: function (options) {
        var that = this; 

        console.log(options);
        this.setData({
            pageload:true,
            connected: false,
            deviceId: options.id,
            try_cnt:0,
            deviceName: options.name
        });

        console.log("detail:  onLoad");
        wx.stopBluetoothDevicesDiscovery({
            success: function (res) {
                console.log('停止搜索设备', res)
            }
        })

        that.closeBLEConnection(that.data.deviceId);
        that.createBLEConnection(that.data.deviceId, that.data.deviceName); 
    }, 

    onHide () {
        var that = this;
        // Do something when hide.
        // 断开连接
        console.log("detail:  onHide");
    },

    onShow:function()
    {      
        // var that = this; 
        // connect bluetooth
        // that.closeBLEConnection(that.data.deviceId);
        // that.createBLEConnection(that.data.deviceId, that.data.deviceName); 
    },

    onUnload() {
        var that = this;

        this.setData({
            pageload:true,
            connected: false,
            try_cnt:0,
        });

        console.log("page:  onUnload  ");
        that.offBLEMonitor();
        that.closeBLEConnection(that.data.deviceId);
        that.closeBluetoothAdapter();

        wx.showToast({
            title: '断开蓝牙',
            icon: 'success',
            duration: 2000
          })  
    },


    DisConnectTap:function()
    {
        var that = this;

        that.setData({
            pageload:true,
            connected: false,
            try_cnt:0,
        });

        ble.openBluetoothAdapter(that.data.deviceId, that.data.deviceName);
        that.createBLEConnection(that.data.deviceId, that.data.deviceName); 
    },



    RecvCleanTap: function () {
        this.setData({
            recv_value: '',
            recv_number: 0
        });
    },  

    /**
     * 创建连接
     * @param {*} deviceId 
     * @param {*} name 
     */
    createBLEConnection(deviceId, name) 
    {
        wx.createBLEConnection({
            deviceId,  
            success: (res) => {
                console.log('createBLEConnection - success: ', res)
                this.getBLEDeviceServices(deviceId)              
            },
            fail: (res) => {
                console.error('createBLEConnection - fail: ', res)
                if(res.errCode == 10006 ){
                    this.createBLEConnection(deviceId, name)
                }
                else{
                    ble.openBluetoothAdapter(deviceId, name)
                    this.createBLEConnection(deviceId, name)
                }

                this.setData({
                    connected: false,
                })               
            } 
        })
    },
        
    getBLEDeviceServices(deviceId) 
    {
        var that = this;
        wx.getBLEDeviceServices({
            deviceId,
            success: (res) => 
            {
                console.log('getBLEDeviceServices - success: ', res)
                for (let i = 0; i < res.services.length; i++) 
                {
                    var ergodic_UUID =res.services[i].uuid;      //取出服务里面的UUID
                    var UUID_slice = ergodic_UUID.slice(4, 8);   //截取4到8位

                    console.log('getBLEDeviceServices, service ID =  ', res.services[i].uuid);
                    if ( res.services[i].isPrimary && (UUID_slice == "FFE0") ) 
                    {
                        that.setData({
                            serviceId: res.services[i].uuid,
                        });
                        break;
                    }
                }

                wx.getConnectedBluetoothDevices({
                    services: res.services,
                    success: (res) => 
                    {                         
                      console.log("getConnectedBluetoothDevices - success:  " +  res)
                    },
                    fail: (res) => {
                        console.error('getConnectedBluetoothDevices - fail: ', res)
                        ble.openBluetoothAdapter(deviceId, that.data.deviceName)
                    }                    
                })

                that.getBLEDeviceCharacteristics(deviceId, that.data.serviceId);
            },
            fail: (res) => {
                console.error('getBLEDeviceServices - fail: ', res)
                // try it again
                ble.openBluetoothAdapter(deviceId, that.data.deviceName)
                that.monitor_connected();
            } 
        });
    },

   getBLEDeviceCharacteristics(deviceId, serviceId) 
   {
        var that = this;
        let falg = false;

        wx.getBLEDeviceCharacteristics({
            deviceId,
            serviceId,
            success: (res) => 
            {
                that.setData({
                    connected: true,
                })                
                console.log('getBLEDeviceCharacteristics success', res.characteristics)
                for (let i = 0; i < res.characteristics.length; i++) 
                {
                    let item = res.characteristics[i]
                     
                    console.log('getBLEDeviceCharacteristics, Characteristics ID =  ', item.uuid)
                    // 该特征值:可读
                    if (item.properties.read) 
                    {
                        wx.readBLECharacteristicValue({
                            deviceId,
                            serviceId,
                            characteristicId: item.uuid,
                        })
                    }

                    // 该特征值:可写
                    if (item.properties.write) 
                    {
                        this.setData({
                            canWrite: true
                        })
                        this._deviceId = deviceId
                        this._serviceId = serviceId
                        this._characteristicId = item.uuid
                        this.writeValue()
                    }

                    if (item.properties.notify || item.properties.indicate) 
                    {
                        that.setData({
                            characteristicId: item.uuid
                        });
                        falg = true;
                        break;
                    }
                }

                
                if( falg )
                {
                    console.debug('getBLEDeviceCharacteristics - deviceId : ', deviceId)
                    console.debug('getBLEDeviceCharacteristics - serviceId : ', serviceId)
                    console.debug('getBLEDeviceCharacteristics - characteristicId: ', that.data.characteristicId)

                    // read device character value 
                    that.readBLECharacteristicValue(deviceId, serviceId, that.data.characteristicId)   
                    that.notifyBLECharacteristicValueChange(deviceId, serviceId, that.data.characteristicId) 
                }
            },
            fail: (res) => {
                console.error('getBLEDeviceCharacteristics -- fail: ', res)
                this.setData({
                    connected: false,
                })

                if (res.errCode === 10006)
                {
                    that.offBLEMonitor();
                    that.createBLEConnection(deviceId, that.data.deviceName); 
                }
            }
        })

   },

   readBLECharacteristicValue(deviceId,serviceId, characteristicId )
   {
        wx.readBLECharacteristicValue({
                // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
                deviceId,
                // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
                serviceId,
                // 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
                characteristicId,
                success (res) {
                    console.log('readBLECharacteristicValue:', res.errCode)
                }
         })
   },

   notifyBLECharacteristicValueChange(deviceId,serviceId, characteristicId )
   {
        var that = this;

        wx.notifyBLECharacteristicValueChange({
            state: true,    // enable notify
            deviceId,
            serviceId,
            characteristicId,

            success: (res) => {
                console.info('notifyBLECharacteristicValueChange success: ', res.errMsg)

                // read data here 
                // 操作之前先监听,保证第一时间获取数据
                wx.onBLECharacteristicValueChange(function(res) 
                {
                    that.data.connected = true; 
                    console.info('onBLECharacteristicValueChange', res);
                    console.info(`characteristic ${res.characteristicId} has changed, now is ${res.value}`);

                    var result = res.value;
                    var hex = utils.buf2hex(result);
                    var _hex_ss =  utils.hex2string(hex);

                    console.info("hex: " + hex);
                    console.info("string: " + _hex_ss);
                    that.data.rece_string += _hex_ss;

                    var recv_number_1 = that.data.recv_number + _hex_ss.length / 2;
                    var recv_number = Math.round(recv_number_1);

                    if( that.data.rece_string.includes("log:") && that.data.rece_string.includes(":end")){
                        that.setData({
                            recv_number: recv_number,
                            recv_value: that.data.recv_value + that.data.rece_string,
                        });

                        console.info("string: " + that.data.rece_string);
                        let buff =  that.data.rece_string.split(':')
                        console.log(buff)
                        that.data.rece_string = ''

                        let valueList = buff[1].split(',')
                        that.data.recv_value = "";
                        that.data.recv_number = 0;
                        console.log(valueList)
                        if(valueList.length == 4 ){
                        that.setData({
                            temphtValue: valueList[0],
                            humidityValue: valueList[1],
                            luxValue: valueList[2],
                            srValue: valueList[3],
                        });
                        } 
                    }     
                    that.monitor_connected();
                })                                 
            },

            fail: (res) => {
                console.error('notifyBLECharacteristicValueChange fail: ', res)
                that.monitor_connected();
            }
        })    
   },

   monitor_connected_action()
   {
        var that = this;


        let deviceId = that.data.deviceId;

        wx.onBLEConnectionStateChange(function(res) {
            // 该方法回调中可以用于处理连接意外断开等异常情况
            console.log( "onBLEConnectionStateChange ----- " +  `device ${res.deviceId} state has changed, connected: ${res.connected}`)
            if( res.deviceId == deviceId && res.connected == false )
            {    
                wx.closeBLEConnection({
                    deviceId,
                    success: (res) => {
                        console.debug('detail: closeBLEConnection success', res);  
                        that.offBLEMonitor();
                        that.createBLEConnection(deviceId, that.data.deviceName); 
                    },
                    fail: (res) => {                     
                        console.error('detail: closeBLEConnection fail', res);  
                        if (res.errCode === 10006) {
                            that.offBLEMonitor();
                            that.createBLEConnection(deviceId, that.data.deviceName); 
                        }
                    }
                })

                that.setData({
                    try_cnt: that.data.try_cnt + 1,
                })
            }
            else{
                that.data.try_cnt  = 0;
            }
        })   
   },

   monitor_connected()
   {
        var that = this;

        setTimeout(that.monitor_connected_action, 200);
   },

    writeValue( val ) 
    {
        // 向蓝牙设备发送一个0x00的16进制数据
        let buffer = new ArrayBuffer(1);
        let dataView = new DataView(buffer);

        dataView.setUint8(0, val);

        console.debug('getBLEDeviceCharacteristics - deviceId : ', this._deviceId)
        console.debug('getBLEDeviceCharacteristics - serviceId : ', this._serviceId)
        console.debug('getBLEDeviceCharacteristics - characteristicId: ', this._characteristicId)

        wx.writeBLECharacteristicValue({
            deviceId: this._deviceId,
            serviceId: this._serviceId,
            characteristicId: this._characteristicId,
            value: buffer,
            success: (res) => {
                console.debug('writeBLECharacteristicValue success', res);  
            },
            fail: (res) => {
                console.error(' writeBLECharacteristicValue fail', res);  
                this.setData({
                    connected: false,
                }) 
            }
        })
    },

    closeBluetoothAdapter() 
    {
        wx.closeBluetoothAdapter({           
            success (res) {
              console.log(res)
            }        
        })

        this.setData({
            connected: false,
        }),         
        this._discoveryStarted = false
    },

    closeBLEConnection( deviceId ) 
    {
        wx.closeBLEConnection({
            deviceId,
            success: (res) => {
                console.debug('detail: closeBLEConnection success', res);  
            },
            fail: (res) => {
                console.error('detail: closeBLEConnection fail', res);  
            }
        })

        this.setData({
            connected: false,
            canWrite: false,
        })
    },

    run_up:function()
    {
        var that = this;

        var val = 0x01 ;
        that.writeValue( val );
    }, 
    
    run_down:function()
    {
        var that = this;

        var val = 0x02;      
        that.writeValue( val );   
    },

    run_left:function()
    {
        var that = this;

        var val = 0x03;      
        that.writeValue( val );   

    },

    run_right:function()
    {
        var that = this;

        var val = 0x04;      
        that.writeValue( val );
    },

    run_stop:function()
    {
        var that = this;

        var val = 0x05;
        that.writeValue( val );
    },

    whiteLightValueSliderChange:function(e)
    {
        var that = this;
        // 向蓝牙设备发送一个0x00的16进制数据
        var val = Math.random() * 255 | 0; 
        that.writeValue( val );   
    },

    offBLEMonitor(){
        this.setData({
            connected: false,
        }), 
        wx.offBLEPeripheralConnectionStateChanged();
        wx.offBLEConnectionStateChange();
        wx.offBLECharacteristicValueChange();
        wx.offBLEMTUChange();
    }
})

2.2 下位机功能

代码第29行: 从UART读取一个byte

代码第30行:判断buff接收到的字符串是否越界

代码第38行:设置蓝牙控制命令

cpp 复制代码
#include "bluetooth.h"

#define PROT_FRAME_LEN       16

Stru_BlueCmd stru_BlueCmd;

static uint8_t recv_buf[PROT_FRAME_LEN];
static uint8_t rev_cnt = 0;


void bluetoothCmd_DataRecvByte(uint8_t data )
{
    recv_buf[rev_cnt++] =  data;
    if( rev_cnt >= PROT_FRAME_LEN)
         rev_cnt = 0;
    
    // bluetooth command
    rev_cnt = 0;
    stru_BlueCmd.recStatus +=1;
    if( stru_BlueCmd.recStatus > PROT_FRAME_LEN )
        stru_BlueCmd.recStatus = 0;
    stru_BlueCmd.mcmd = recv_buf[0];
    
}

串口回调函数:

在void UART_RxCpltCallback(UART_HandleTypeDef *huart)函数调用该函数

小车运动方向控制函数:

方向控制枚举定义

电机转动控制函数:

cpp 复制代码
void motor_ctrlAct( uint8_t type, uint16_t speed  )
{
         switch( type )
         {
             case IDLE:
                 break;
             
             case STOP_RUN:
                HAL_TIM_SetPWM_Pulse( 0, TIM_CHANNEL_1);
                HAL_TIM_SetPWM_Pulse( 0, TIM_CHANNEL_2);
                
                HAL_TIM_SetPWM_Pulse( 0, TIM_CHANNEL_3);
                HAL_TIM_SetPWM_Pulse( 0, TIM_CHANNEL_4);
                break;
             
             case RIGHT_RUN:
                HAL_TIM_SetPWM_Pulse( speed*FACTOR, TIM_CHANNEL_2);
                HAL_TIM_SetPWM_Pulse( 0, TIM_CHANNEL_1);
                
                HAL_TIM_SetPWM_Pulse( speed, TIM_CHANNEL_3);
                HAL_TIM_SetPWM_Pulse( 0, TIM_CHANNEL_4);
                break;
             
             case LEFT_RUN:
                HAL_TIM_SetPWM_Pulse( speed*FACTOR, TIM_CHANNEL_1);
                HAL_TIM_SetPWM_Pulse( 0, TIM_CHANNEL_2);
                
                HAL_TIM_SetPWM_Pulse( speed, TIM_CHANNEL_4);
                HAL_TIM_SetPWM_Pulse( 0, TIM_CHANNEL_3);
                break;
             
             case UP_RUN:
                HAL_TIM_SetPWM_Pulse( speed, TIM_CHANNEL_1);
                HAL_TIM_SetPWM_Pulse( 0, TIM_CHANNEL_2);
                
                HAL_TIM_SetPWM_Pulse( speed, TIM_CHANNEL_3);
                HAL_TIM_SetPWM_Pulse( 0, TIM_CHANNEL_4);
                break;
             
             case DOWN_RUN:
                HAL_TIM_SetPWM_Pulse( 0, TIM_CHANNEL_1);
                HAL_TIM_SetPWM_Pulse( speed, TIM_CHANNEL_2);
                
                HAL_TIM_SetPWM_Pulse( 0, TIM_CHANNEL_3);
                HAL_TIM_SetPWM_Pulse( speed, TIM_CHANNEL_4);
                break;
         }
}

蓝牙模块控制电机转动功能:

3 功能测试

3.1 小程序UI测试

1) 手机扫描蓝牙模块信息

手机App上打开小程序,其会自动扫描蓝牙模块的信息

蓝牙模块连接成功,小程序会跳转到主控UI页面

同时,在调试接口上打印出连接成功的信息:

3.2 小车方向控制

通过小程序上的如下四个按钮控制小车的运行方向

系统硬件

调试状态:

相关推荐
计算机源码社7 小时前
分享一个基于微信小程序的居家养老服务小程序 养老服务预约安卓app uniapp(源码、调试、LW、开题、PPT)
android·微信小程序·uni-app·毕业设计项目·毕业设计源码·计算机课程设计·计算机毕业设计开题
DreamByte9 小时前
Python Tkinter小程序
开发语言·python·小程序
说私域9 小时前
开源 AI 智能名片小程序:开启内容营销新境界
人工智能·小程序
汇匠源9 小时前
零工市场小程序:保障灵活就业
java·小程序·零工市场
哈尔滨财富通科技9 小时前
家居小程序有什么用?
小程序
双普拉斯10 小时前
微信小程序点赞动画特效实现
nginx·微信小程序·notepad++
程序员阿龙10 小时前
【2025】基于微信小程序的网上点餐系统设计与实现、基于微信小程序的智能网上点餐系统、微信小程序点餐系统设计、智能点餐系统开发、微信小程序网上点餐平台设计
微信小程序·小程序·毕业设计·订单管理·在线点餐·订单跟踪·在线支付
Angus-zoe10 小时前
uniapp+vue+微信小程序实现侧边导航
vue.js·微信小程序·uni-app
开利网络1 天前
综合探索数字化转型的奥秘与前景
运维·微信小程序·自动化·产品运营·数字化营销
张人玉1 天前
微信小程序开发——比较两个数字大小
微信小程序·小程序