目录
概述
1 系统架构
1.1 系统结构
1.2 系统硬件框架结构
1.3 蓝牙模块介绍
2 微信小程序实现
2.1 UI介绍
2.2 代码实现
3 上位机功能实现
3.1 通信协议
3.2 系统测试
4 下位机功能实现
4.1 功能介绍
4.2 代码实现
4.3 源代码文件
5 测试
5.1 编译和下载代码
5.2 App上数据显示的UI
概述
本文主要介绍设计一个系统实现如下功能:使用Renesas R7FA8D1BH (Cortex®-M85)和微信小程序App数据传输。笔者介绍了系统设计的架构,包括硬件架构,软件架构,以及上位机的实现代码。并在下位机实现数据发送功能,App正确的解析数据,并通过UI显示数据的功能。
1 系统架构
1.1 系统结构
功能介绍
使用Renesas R7FA8D1BH (Cortex®-M85)的UART端口,与蓝牙模块通信,将数据包发送出去,上位机使用基于微信小程序编写的App,上位机接收到数据之后,解析数据包,并显示在UI上。
1.2 系统硬件框架结构
1)数据采集部分
SHT20: 温湿度传感器,采集环境温度和湿度
ISL29035: 光照传感器,采集环境的光照强度
DS18B20: 高精度温度传感器,设备工作环境温度监测
ADC(Temperatre): MCU内部温度监测
ADC(可调电阻): 模拟数据输入
2)显示部分
OLED: 实时显示传感器的数据
3)模式切换
KEY-UP: 物理按键,正方向切换UI
KEY-DOWM: 物理按键,反方向切换UI
4)数据传输部分
使用蓝牙模块发送数据包,该数据包会被微信小程序接收
1.3 蓝牙模块介绍
HC-08蓝牙串口通信模块是新一代的基于Bluetooth Specification V4.0 BLE 蓝牙协议的数传模块。无线工作频段为 2.4GHz ISM,调制方式是 GFSK。模块最大发射功率为4dBm,接收灵敏度-93dBm,空旷环境下和 手机可以实现 80 米超远距离通信。
其和MCU之间通过串口通信,软件设计也简单便捷,且不需要考虑蓝牙协议栈问题,非常适合做速成产品。
蓝牙模块与MCU之间连接图:
2 微信小程序实现
2.1 UI介绍
接收数据:
1) 显示温度和湿度数据
2) 显示光照数据
3 ) 显示MCU内部温度数据
2.2 代码实现
1)创建detail.wxml文件,实现UI框架
详细代码如下:
<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: -90rpx; "><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: -90rpx; "><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_up" 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_up" 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_up" 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_up" plain="true" style="position: relative; left: 5rpx; top: -173rpx; width: 179rpx; height: 361rpx; display: block; box-sizing: border-box">停止</button></view>
</view>
2)样式文件
详细代码如下:
.connect_box {width: 100%;height: 30px;line-height: 30px;font-size: 32rpx;color: #666;background-color: antiquewhite;font-family: "Microsoft YaHei";
}
.connect_device_name{float: left;padding-left: 10px;color: #39beff;
}.connect_state {float: right;padding-right: 10px;text-decoration: underline;color: #39beff;
}page {min-height: 100%;}.container {height: 100%;display: flex;flex-direction: column;align-items: center;justify-content: space-between;box-sizing: border-box;background-color: cadetblue;}.block {padding: 15px;width: 100%;box-sizing: border-box;}.block-title {font-size: 20px;margin-bottom: 30px;}.block-subtitle {margin: 15px 0;font-size: 14px;color: #666;}.block-item {position: relative;display: flex;justify-content: space-between;align-items: center;margin: 15px 0;padding: 15px;font-size: 18px;border-radius: 8px;background:azure;box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.1);}.block-item:last-child {margin-bottom: 0;}.block-item.column {flex-direction: column;}.item-row {display: flex;justify-content: space-between;align-items: center;width: 100%;}.item-row + .item-row {margin-top: 10px;}.item-column {display: flex;flex-direction: column;justify-content: space-between;align-self: stretch;flex-grow: 1;}.item-title {font-size: 16px;}.item-title-icon {height: 1.2em;width: 1.2em;margin-right: 10px;vertical-align: middle;}.slider-box {flex-grow: 1;}.slider {margin-left: 10px;margin-right: 20px;flex-grow: 1;}.slider-value {display: flex;justify-content: space-around;align-items: center;margin: 15px auto;width: 70%;}.input-unit {text-align: right;font-size: 14px;color: #999;}.input-value {display: inline-block;padding: 0 16px;border: 1px solid #CCCCCC;border-radius: 4px;max-width: 3.5em;font-size: 16px;text-align: center;}
3) JS文件
详细代码如下:
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; //取出服务里面的UUIDvar 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 againble.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 = deviceIdthis._serviceId = serviceIdthis._characteristicId = item.uuidthis.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 notifydeviceId,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 = 0x18 ; that.writeValue( val ); }, run_down:function(){var that = this;var val = 0x52; that.writeValue( val ); },run_left:function(){var that = this;var val = 0x08; that.writeValue( val ); },run_right:function(){var that = this;var val = 0x5a; 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();}
})
3 上位机功能实现
3.1 通信协议
发送数据协议如下:
log: %d,%d,...,%d:end
Demo log如下:
log:25.07,60.69,1312,172.76:end
微信小程序log 调试信息
微信小程序端解析数据方法:
代码第313行: 判断数据包的头(log:)和尾部(:end)
代码第320行:解析字符串
3.2 系统测试
调试log信息,解析到下位机上传的数据
4 下位机功能实现
4.1 功能介绍
1)系统上电之后,首先初始化MCU的资源
2)初始化外围资源,包括各类sensor
3) 实时读取传感器的数据
4)实时通过蓝牙模块上传数据
4.2 代码实现
1) 发送数据接口
代码48行: 判断字符串是否发送完毕
代码51行:使用串口发送数据
2)发送数据函数
代码67行:格式化数组
代码75行:格式化发送数据
代码77行:发送数据
3)实时获取数据函数
代码36行: 读取DS18B20数据
代码47~48行:读取SHT20数据
代码50行:读取ISL29035的数据
代码52~53行: Mcu内部数据
代码55~61行:打包传感器数据
代码65行:上传数据
4.3 源代码文件
获取和传输数据函数
void app_getSensorDataPacket( void )
{static int sec;rtc_time_t get_time;float humidity;float temperature;unsigned int luxValue;uint16_t adc_ch1_value;float adc_temp_value;bool result;result = ds18b20NoBlockingProcess();//DS18B20 if( result ){stru_SensorData.ds18b20_value = st_ds1b20val.temperatureVal;}user_get_currentRtc(&get_time);if( get_time.tm_sec != sec ){sec = get_time.tm_sec;sht2x_get_humidy(&humidity);sht2x_get_temperature(&temperature);IsL29035_read_lux( &luxValue );adc_basic_get_value( &adc_ch1_value );adc_basic_get_temperature( &adc_temp_value );stru_SensorData.humidity = humidity;stru_SensorData.temperature = temperature;stru_SensorData.luxValue = luxValue;// mcu internel adc and temperature stru_SensorData.mcuTemp_value = adc_temp_value*100;stru_SensorData.adc_RawCnt = adc_ch1_value;// send log to appbluetooth_sengLog();}
}
蓝牙模块核心应用程序
/*FILE NAME : app_bluetooth.cDescription: app UIAuthor : tangmingfei2013@126.comDate : 2024/06/03*/
#include "app_bluetooth.h"
#include "app_main.h"
#include "bluetooth.h" #define PROT_FRAME_LEN 16Stru_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 commandstru_BlueCmd.recStatus +=1;if( stru_BlueCmd.recStatus > PROT_FRAME_LEN )stru_BlueCmd.recStatus = 0;if( rev_cnt >= 2 ){rev_cnt = 0;stru_BlueCmd.mcmd = recv_buf[0];stru_BlueCmd.mode = recv_buf[1];}}static void SendInterface(uint8_t* pu8_buf, uint16_t u16_len )
{r_sci_b_uart1_sendArry(pu8_buf, u16_len);
}static void bluetooth_sendString( uint8_t *ss )
{uint8_t buff[2];uint8_t index = 0;while ( ss[index]!='\0' ){buff[0]= ss[index];SendInterface(buff, 1);index++;}
}void bluetooth_sengLog( void )
{static uint8_t step = 0;Struc_SensorPack *pSensorData;uint8_t dataBuff[128];pSensorData = &stru_SensorData;switch( step ){default:case 0:memset((char*)dataBuff, '\0', sizeof(dataBuff));//sprintf((char*)dataBuff, "log:%d,%.2f,", pSensorData->luxValue,pSensorData->sr_value);// bluetooth_sendString( dataBuff );step = 1;break;case 1://memset((char*)dataBuff, '\0', sizeof(dataBuff));sprintf((char*)dataBuff, "log:%.2f,%.2f,%d,%.2f:end", pSensorData->temperature*0.01, pSensorData->humidity*0.01, pSensorData->luxValue,pSensorData->mcuTemp_value*0.01);bluetooth_sendString( dataBuff );step = 0;break;}
}/* End of this file */
5 测试
5.1 编译和下载代码
编译和下载代码