基于CC2640蓝牙4.2的智能健身自行车(附带简易APP)(结项)
经过一个多星期的开发测试,智能自行车的开发终于进入了尾声。昇润科技的SDK大大简化了开发时间,使得开发者能快速、高效的完成开发,同时也能保证设备的低功耗与强鲁棒性。
下面为目前已完成进度:
实现了对自行车的测速功能。
实现了对心率粗略的测量,在传感器佩戴良好的情况下,可以实现较精确的对心率的测量。
实现了数据定时回传功能,配合对应开发的APP可实现实时速度显示。
APP开发完成,可实现连接或断开设备、读取所需数据并显示。
首先说下测速原理。
室内自行车的转盘上我放置了两个强磁铁,其经过霍尔元件时会触发中断。CC2640每两秒统计一次中断触发次数,同时根据外径计算出当前对应的速度。设备安置如下:
计算速度的代码如下:
很简单的计算,半径单位为厘米,周长 C = 2*Pi*R,如果放了一个磁铁那么计数器Counter与周长的乘积就应该除以二。得到了当前速度(m/ms),将其换算成km/h,即为CurrentSpeed。
心率测量的计算算法稍微麻烦一点,我分成心率检出跟心率计数两个部分来说明。
心率检出:
首先说下采样速率,100hz,速率不太高,勉强够用,速度越快精度越高。
采样得到的图像近似如下图:
首先交代下状态:
依次为:低于平均,高于平均且上升,高于平均且下降。
- typedef enum {BelowAverage,AboveAverageRising,AboveAverageFalling}HeartBeatSituation;
我们可以看到心跳的最高电平很高,其他的都不算太高。首先进行500次采样,对采集得到的电压取平均值作为阈值。
ADC采样得到的电压低于阈值,则置状态:BelowAverage;
若其高于阈值,则与采样得到的最高电平(HeartBeatsPeak,默认为0)比较,若高于,则置状态AboveAverageRiseing,并修改最高电平数值;若低于,则置AboveAverageFalling。
当其再次低于阈值时,判断其状态,若其为AboveAverageFalling,或者最高电平(HeartBeatsPeak)不为0,则判定一次心跳(可看成下降沿触发中断)。若不是,则置状态:BelowAverage。
这就完成了心率检出。
对应实现的代码如下:
- void BeatsCaculate(uint16_t Level)
- {
- if(SampleCounter != -1)
- {
- return ;
- }
- if(Level <= AverageLevel)
- {
- if(HeartBeatStatues == AboveAverageFalling || HeartBeatsPeak)
- {
- HeartBeatsCounter ++;
- }
- HeartBeatStatues = BelowAverage;
- HeartBeatsPeak = 0;
- }
- else
- {
- if(HeartBeatsPeak < Level)
- {
- HeartBeatStatues = AboveAverageRising;
- HeartBeatsPeak = Level;
- }
- else
- {
- HeartBeatStatues = AboveAverageFalling;
- }
- }
- }
心率计数:
这个跟上面相比,我现在的处理方法就很简单很粗糙了。
我使用的方案主要有两套,最开始的一种方法是维护一个四元素队列,每秒动态的替换心率数据,使用最小二乘法拟合线形回归方程,对其进行积分来得到每分钟的心率。但这个因为前一级心率检出的效果严重依赖佩戴方式,所得到的效果并不好。
现在采用的方法很简单,对其采样5s,把得到的心率总数乘以12,即计算出当前的心率速度。虽然跟随性不太好,但准确性还是可以的。
数据回传就不多说了,很简单,Set一下就OK的。
App方面需要讲的就太多了。
我刚进入App开发不久,所以这次没有使用昇润科技提供的安卓代码,而是选择自己造了一遍轮子进而了解下App的开发过程。
首先是获取权限并开启蓝牙。
获取权限:
- if (!App.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
- throw new RuntimeException("Don't support BLE.");
- }
开启蓝牙:
- bluetoothManager = (BluetoothManager) ParentActivity.getSystemService(Context.BLUETOOTH_SERVICE);
- mBluetoothAdapter = bluetoothManager.getAdapter();
- if (!mBluetoothAdapter.isEnabled()) {
- if(mBluetoothAdapter.enable())
- {
- Toast.makeText(ParentActivity,"蓝牙打开成功!",Toast.LENGTH_SHORT).show();
- while(!mBluetoothAdapter.isEnabled())
- {
- ThreadSleep(10);
- }
- }
- else
- {
- Toast.makeText(ParentActivity,"蓝牙打开失败!",Toast.LENGTH_SHORT).show();
- return;
- }
- }
之后是连接BLE设备。
- public void ConnectDevices(BluetoothDevice[] Device, String TargetMAC) {
- if (!isConnect) {
- for (BluetoothDevice Dev : Device) {
- if (Dev.getAddress().equals(TargetMAC)) {
- mBluetoothGatt = Dev.connectGatt(ParentActivity, false, new BluetoothGattCallback() {
- @Override
- public void onCharacteristicRead(BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic, int status)
- {
- DataRead = characteristic.getValue();
- ReadyToRead.release(1);
- }
- @Override
- public void onServicesDiscovered(BluetoothGatt gatt, int status)
- {
- BLEService = gatt.getServices();
- }
- @Override
- public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
- switch (newState) {
- case BluetoothProfile.STATE_CONNECTED:
- mBluetoothGatt.discoverServices();
- isConnect = true;
- break;
- case BluetoothProfile.STATE_DISCONNECTED:isConnect = false;
- break;
- default:
- break;
- }
- }
- });
- }
- }
- }
- }
最后就是从设备读取数据了。
- public byte[] GetData(BluetoothGattCharacteristic characteristic)
- {
- if(mBluetoothGatt.readCharacteristic(characteristic))
- {
- try {
- ReadyToRead.acquire();
- }
- catch (InterruptedException e){}
- return DataRead;
- }
- return null;
- }
下面放出演示图:
前三张为心率数据发送。因为实在是无法一边拿着板子一边握着传感器一边截图一边蹬车,所以只能分开来进行演示。
UUID为00001002那个即为对应接收的。
下面四张为float型的当前速度,直接在App转成float即可。只要他是按照IEE754就OK(基本都按照吧……)。
最后一张为停止踩单车后的数据。
下面为APP获取数据的演示。
全部代码传于百度盘:
链接:http://pan.baidu.com/s/1kUXsXr5 密码:kzkv
PS:App需要先连接设备后再读取数据,否则会闪退……(没做异常处理……原谅我……)
PS:连接的设备的MAC在程序里写死了……(原谅我)
PS:安卓版本需要5.1以上。
以上。
不错的文档,值得收藏
好详细,收了。