【环境专家之智能手表】Part14:APP数据可视化!
[复制链接]
1.介绍
上一篇已经把数据存储到EEPROM中了,那么看不到的数据是没有任何用滴,所以需要APP拿到数据,将数据可视化出来,这样看着才有意义。数据需要可视化需要有两个步骤,第一个步骤是APP如何从手表获取数据,第二部分是如何可视化数据,接下来我们按照步骤一步一步来~
2.数据通信
首先需要了解我们需要哪些数据,APP设计显示一天的内容,那么根据这个需求需要定制一个通信协议,通过查看【ICS_CHARACTERISTIC_VALUE_LENGTH】宏定义,知道蓝牙一次传输的数据长度为20个字节,我也不去增加蓝牙的吞吐量,所以要从协议下手,我们一天的数据有【8*48=384】个字节,所以需要拆包。根据上面的需求定义了如下图1所示的通信协议。
图1
通信协议定义好,可以看到一包发送两组数据,那么一包的字节数为【2*8+3=19】,没有超过20个字节。
先让下位机去实现一下这个通信协议,首先读取到获取的日期,转换为零点时间戳,去Flash中查找,看看是否能够查找到该日期的数据,如果查找到了则将数据打包发送。这里涉及到拆包,其实很简单,每两组数据添加上一个包头,然后循环24次就全部发送出去了,具体实现如下所示:
case PHONE_GET_DATA:
{
uint8_t send_buff[19] = {PHONE_GET_DATA_ACK, 0, 0};
struct my_tm t;
uint32_t time_temp;
one_day_data_t day_data;
uint8_t i = 0;
t.tm_year = data[1] + 2000;
t.tm_mon = data[2];
t.tm_mday = data[3];
t.tm_hour = 0;
t.tm_min = 0;
t.tm_sec = 0;
time_temp = mktime(t);
if(watch_data_rec_day(time_temp, &day_data) != 0)//查看是否有数据
{
phone_send_data(send_buff, sizeof(send_buff)); //没数据回复
return;
}
else
send_buff[1] = 1;
for(i = 0;i < 24;i++)
{
send_buff[2] = i + 1;
memcpy(&send_buff[3], &day_data.data[i * 2], sizeof(single_data_t) * 2);
phone_send_data(send_buff, sizeof(send_buff)); //回复数据
}
break;
}
由于时间问题,没有等上一天的时间,而是直接使用【rand】生成随机数,存储到EEPROM中,具体实现如下:
single_data_t data_temp;
uint8_t rand_value = rand() % 100;
data_temp.act = rand_value / 10;//activity_get_status();
srand(HAL_Time());
rand_value = rand() % 100;
data_temp.temp = (rand_value * 2 + ((float)bme680_output.temperature)) / 130.0f;
srand(HAL_Time());
rand_value = rand() % 100;
data_temp.hum = (rand_value * 10 + ((float)bme680_output.humidity)) / 1020.0f;
srand(HAL_Time());
rand_value = rand() % 100;
data_temp.lum = 0;
if(bme680_output.pressure > 80000 && bme680_output.pressure < 200000)
data_temp.pre = bme680_output.pressure + rand_value;
else
data_temp.pre = 0;
watch_data_today_cnt_save(get_time_utc()%86400%48, data_temp);
那么下位机通信功能基本就完成了。
3.数据可视化
上位机先完成对数据的读取,只需要正常解析就可以了,解析如下:
case 0x0C:
int act_value, temp_value, hum_value, lum_value, pre_value;
for(int i = 0;i < 2;i++) {
act_value = ReceiveBuff[3 + i * 8];
temp_value = ReceiveBuff[4 + i * 8];
hum_value = ReceiveBuff[5 + i * 8];
lum_value = ReceiveBuff[6 + i * 8];
pre_value = ReceiveBuff[7 + i * 8] + (ReceiveBuff[8 + i * 8] >> 8) + (ReceiveBuff[9 + i * 8] >> 16) + (ReceiveBuff[10 + i * 8] >> 24);
}
break;
还是比较简单的,气压值是小端模式存储的,至于可视化我采用了最简单的控件【Canvas】和【Paint】,直接在APP上画线段,虽然很不美观,但是能够比较直观的看到数据的波形。
由于有多种数据,我采用了一个画布中画多种数据,每种数据通过不同的颜色显示,而且可以通过多选框对画布进行显示控制,具体的实现方式有些复杂,给大家简要的说一下,首先监听多选框中的状态改变事件,当状态改变了,这时将画布中的判断标志位也更新,画布中通过这个标志位来判断是否需要继续显示该波形,从而实现单独显示一个波形图,波形的数据存储在【List<Integer>】列表类型中,这个列表属于FIFO类型,先进来的数据先出去,所以每次有数据将旧数据顶掉就可以了,下图2是界面的随机数的显示:
图2
获取日期和设置时间一样,也是通过滚轮的方式设置的。
对于如何将数据显示到屏幕上,只需要将数据写入列表中,当列表长度达到最长的时候,从列表中移除数据即可:
case 0x0C:
int act_value, temp_value, hum_value, lum_value, pre_value;
for(int i = 0;i < 2;i++) {
act_value = ReceiveBuff[3 + i * 8];
temp_value = ReceiveBuff[4 + i * 8];
hum_value = ReceiveBuff[5 + i * 8];
lum_value = ReceiveBuff[6 + i * 8];
pre_value = ReceiveBuff[7 + i * 8] + (ReceiveBuff[8 + i * 8] >> 8) + (ReceiveBuff[9 + i * 8] >> 16) + (ReceiveBuff[10 + i * 8] >> 24);
if (ReceiveBuff[1] == 0x01) {
if (cView.dataAct.size() > 48) {
cView.dataAct.remove(0);
cView.dataTemp.remove(0);
cView.dataHum.remove(0);
cView.dataLum.remove(0);
cView.dataPre.remove(0);
}
cView.dataAct.add(act_value * 50);
cView.dataTemp.add(temp_value * 5);
cView.dataHum.add(hum_value * 5);
cView.dataLum.add(lum_value * 5);
cView.dataPre.add(pre_value);
}
}
break;
显示是通过线程每100ms调用一次画图,所以是异步的,当有数据进来不会马上画图,而是到到线程的时间才会进行画图,附上线程的代码:
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
mHandler.postDelayed(new Runnable() {
public void run() {
mHandler.sendMessage(mHandler.obtainMessage());
SimpleChartView.this.invalidate();//重绘
}
}, 100);//100ms刷新一次
};
};
由于气压值在【100000】左右,如果直接显示,那么波动的效果就不会很好,所以这边做了一个处理,求出平均值,减去之后再显示,这样就是一个动态的方式显示,相当于只显示气压值的变化,具体显示代码如下,同时也有气压显示的处理:
long sum = 0;
int average = 0;
if(dataPre.size()>31) {
for (int i = dataPre.size()-31; i < dataPre.size()-1; i++) {
sum += dataPre.get(i);
}
average = (int)(sum/30);
}
if(dataAct.size()>1){
for (int i = 0; i < dataAct.size()-1; i++) {
if(Acy_CheckBox)
canvas.drawLine(Xstart+i*20, Ystart+Ylength-dataAct.get(i), Xstart+(i+1)*20, Ystart+Ylength-dataAct.get(i+1), paintAct);
if(Temp_CheckBox)
canvas.drawLine(Xstart+i*20, Ystart+Ylength-dataY.get(i), Xstart+(i+1)*20, Ystart+Ylength-dataY.get(i+1), paintTemp);
if(Hum_CheckBox)
canvas.drawLine(Xstart+i*20, Ystart+Ylength-dataZ.get(i), Xstart+(i+1)*20, Ystart+Ylength-dataZ.get(i+1), paintHum);
if(Lum_CheckBox)
canvas.drawLine(Xstart + i * 20, Ystart + Ylength - dataTotal.get(i), Xstart + (i + 1) * 20, Ystart + Ylength - dataTotal.get(i + 1), paintLum);
if(Pre_CheckBox)
canvas.drawLine(Xstart + i * 20, Ystart + Ylength + average - dataPre.get(i) - 200, Xstart + (i + 1) * 20, Ystart + Ylength + average - dataPre.get(i + 1) - 200, paintPre);
}
}
这样可视化就基本完成了,来看一看效果吧:
图3
4.总结
可视化做的比较潦草,不过最终效果还是有的,这一篇的难点在于数据的通信,也就是拆包,和可视化部分,如何将多个数据显示出来,不过好在最后都完成了,下一篇可能是低功耗或者下井模式的设计。
|