w494143467 发表于 2021-6-27 09:27

【环境专家之智能手表】Part12:手表名称和时间的配置

<p><strong><span style="font-size:20px;">1.介绍</span></strong></p>

<p><span style="font-size:16px;">手表出厂肯定会用同一个固件,那么名字肯定都是一样的,所以每个用户拿到手里会去修改手表的名字,同时手表需要存储数据的话需要自己跑时间戳,这样才能知道自己这条数据时采集至何时,那么说了就开始动手干吧!</span></p>

<p><strong><span style="font-size:20px;">2.下位机设计</span></strong></p>

<p><span style="font-size:16px;">首先还是需要定义协议,在之前的基础之上添加相关的协议,协议内容如下图1:</span></p>

<p class="imagemiddle" style="text-align: center;"></p>

<p style="text-align: center;"><span style="font-size:16px;">图1</span></p>

<p><span style="font-size:16px;">首先是修改名字,这里目前最多10字节,一个汉字占用两个字节,十个字节五个汉字基本够大家使用,如果是英文的话,十个字母也是完全足够的。</span></p>

<p><span style="font-size:16px;">设置名字的话,需要先修改一下设置名字的API,在名字的开头添加上【#R】这两个字符,代表着该广播设备为【环境专家之智能手表】,具体修改为如下:</span></p>

<pre>
<code class="language-cpp">void BDK_BLE_SetLocalName(const char* name)
{
    uint32_t name_len;

    if (ble_env.state == BLE_STATE_OFF)
    {
      BDK_BLE_Initialize();
    }

    if (name != NULL)
    {
            uint8_t name_temp = {'#', 'R'};
            memcpy(&amp;name_temp, name, 10);
      name_len = 12;
      if (name_len &lt;= BDK_BLE_LOCAL_NAME_MAX_LENGTH)
      {
            memcpy(ble_env.local_name, name_temp, name_len);
            ble_env.local_name_len = name_len;
      }
    }
}</code></pre>

<p><span style="font-size:16px;">当然这个广播名字需要存储在Flash中,这个还是比较简单的,如果Flash中没有名字,则使用默认名字【Downhole】,最终显示在广播中的就是【#RDownhole】,所以名字的初始化如下所示:</span></p>

<pre>
<code class="language-cpp">void phone_communication_init(void)
{
        int32_t retval = 0;
        //.....
        retval = I2CEeprom_Read(BIND_NAME_ADDR, (uint8_t*)watch_name, sizeof(watch_name), &amp;eeprom);
        if(watch_name == 0x00)
        {
                memcpy(watch_name, (uint8_t *)"Downhole", 10);        //默认名字
        }
    //.....
}
</code></pre>

<p>最后就是接收到设置名称的数据时的处理代码了,具体如下:</p>

<pre>
<code class="language-cpp">    case PHONE_SET_NAME:
        {
                uint8_t send_buff = {PHONE_SET_NAME_ACK, 0};
                memcpy(watch_name, &amp;data, 10);        //设置广播名字
                BDK_BLE_SetLocalName(watch_name);
                I2CEeprom_Write(BIND_NAME_ADDR, (uint8_t*)watch_name, sizeof(watch_name), &amp;eeprom);
                phone_send_data(send_buff, sizeof(send_buff));        //回复名字设置
                break;
        }</code></pre>

<p><span style="font-size:16px;">为什么只判断第一位,因为只要设置名字,那么第一位数据肯定不是【0x00】,初始化完成之后,调用设置广播名字的函数即可【BDK_BLE_SetLocalName】。</span></p>

<p><span style="font-size:16px;">接下来是设置时间,设置时间之前肯定需要有一个时间戳能够在手表上跑,所以先定义一个全局变量,存储时间戳,然后定义一个接口用于获取时间戳和让时间戳加一,【add_time_utc】该函数放在一个1S的定时中即可,代码如下:</span></p>

<pre>
<code>my_time_t time_utc = 0;

uint32_t get_time_utc(void)
{
        return time_utc;
}

void add_time_utc(void)
{
        time_utc++;
}
</code></pre>

<p><span style="font-size:16px;">时间戳不需要掉电存储,为什么呢,因为掉电之后,就算再快上电,时间戳的时间也会比现实时间滞后,而时间错了之后存储的数据是无效的,那么只需要设定开机未设置时间,则不对数据进行存储就可以了。</span></p>

<p><span style="font-size:16px;">所以设置时间和查询时间的解析处理代码如下:</span></p>

<pre>
<code class="language-cpp">        case PHONE_SET_TIME:
        {
                uint8_t send_buff = {PHONE_SET_TIME_ACK, 0};
                struct my_tm t;
                t.tm_year = data + 2000;
                t.tm_mon= data;
                t.tm_mday = data;
                t.tm_hour = data;
                t.tm_min= data;
                t.tm_sec= data;
                time_utc = mktime(t);
                printf("time_utc:%d\r\n", time_utc);
                phone_send_data(send_buff, sizeof(send_buff));        //回复设置状态
                break;
        }
        case PHONE_INQ_TIME:
        {
                uint8_t send_buff = {PHONE_INQ_TIME_ACK, 0};
                struct my_tm t;
                localtime(time_utc, &amp;t);
                send_buff = t.tm_year - 2000;
                send_buff = t.tm_mon;
                send_buff = t.tm_mday;
                send_buff = t.tm_hour;
                send_buff = t.tm_min;
                send_buff = t.tm_sec;
                phone_send_data(send_buff, sizeof(send_buff));        //发送手表当前时间
                break;
        }</code></pre>

<p><span style="font-size:16px;">为什么需要查询时间接口呢,主要其实是为了APP连接手表时,自动设置时间,当时间未初始化时,则对时间进行设置,以及手表时间出现误差时对时间的偏移进行校准,这时数据肯定也出现了偏差,知道偏差的时间也可以对数据存储的时间进行校准。</span></p>

<p><strong><span style="font-size:20px;">3.APP设计</span></strong></p>

<p><span style="font-size:16px;">APP主要是界面的设计,因为通信协议还是比较简单的,对于界面的设计流程我就不过多的说明了,直接来看一下设计的结果吧,如下图2所示:</span></p>

<p class="imagemiddle" style="text-align: center;"></p>

<p style="text-align: center;"><span style="font-size:16px;">图2</span></p>

<p><span style="font-size:16px;">设置时间时使用了数字滑动控件【NumberPicker】,我个人觉得这样设置时间比较方便,按下【获取手表时间】按钮可以获取手表的当前时间,【设置手表未上方时间】按钮主要是为了调试使用,可以设置手表为任意时间,【设置为手机当前时间】主要是为了方便直接设置手表时间,设置手机当前时间的代码如下:</span></p>

<pre>
<code class="language-java">    //设置当前时间
    private void SetTime(){

      int year1,year2,month,day,hour,minute,second;
      SimpleDateFormat formatter   =   new   SimpleDateFormat   ("yyyy-MM-dd HH:mm:ss");
      Date curDate =new Date(System.currentTimeMillis());
      String   thistime   =   formatter.format(curDate);
      //year1 = Integer.valueOf(thistime.substring(0,2)).intValue();
      year2 = Integer.valueOf(thistime.substring(2,4)).intValue();
      month = Integer.valueOf(thistime.substring(5,7)).intValue();
      day = Integer.valueOf(thistime.substring(8,10)).intValue();
      hour = Integer.valueOf(thistime.substring(11,13)).intValue();
      minute = Integer.valueOf(thistime.substring(14,16)).intValue();
      second = Integer.valueOf(thistime.substring(17)).intValue();

      byte[] bbb = new byte;
      bbb=(byte) 0x05;
      bbb=(byte) year2;
      bbb=(byte) (month+1);
      bbb=(byte) day;
      bbb=(byte) hour;
      bbb=(byte) minute;
      bbb=(byte) second;
      Send_Data_Ble(bbb, bbb.length);
      //Toast.makeText(mContext, "设置当前时间", Toast.LENGTH_SHORT).show();
    }</code></pre>

<p><span style="font-size:16px;">然后就是协议发送和接收部分,这里没有太难的,我就贴出一部分代码:</span></p>

<pre>
<code class="language-java">case 0x06:
        if(ReceiveBuff == 0x00) {
                ConfigurationStateTextView.setTextColor(0xff0000ff);
                ConfigurationStateTextView.setText("设置时间成功!");
        }
        else {
                ConfigurationStateTextView.setTextColor(0xffff0000);
                ConfigurationStateTextView.setText("设置时间失败!");
        }
        break;
case 0x08:
        String TimeString = "20" + ReceiveBuff + "-" + ReceiveBuff + "-" + ReceiveBuff + " " + ReceiveBuff + ":" + ReceiveBuff + ":" + ReceiveBuff;
        WatchPowerPercentEditText.setText(TimeString);
        break;
case 0x0A:
        if(ReceiveBuff == 0x00) {
                Toast.makeText(mContext, "设置名字成功!", Toast.LENGTH_SHORT).show();
        }
        else {
                Toast.makeText(mContext, "设置名字失败!", Toast.LENGTH_SHORT).show();
        }
        break;</code></pre>

<p><span style="font-size:16px;">设置名字的效果如下GIF所示:</span></p>

<p class="imagemiddle" style="text-align: center;"></p>

<p style="text-align: center;"><span style="font-size:16px;">图3</span></p>

<p><span style="font-size: 16px;">关于时间的操作图下GIF所示:</span></p>

<p class="imagemiddle" style="text-align: center;"></p>

<p style="text-align: center;"><span style="font-size:16px;">图4</span></p>

<p><strong><span style="font-size:20px;">4.总结</span></strong></p>

<p><span style="font-size:16px;">时间其实是手表比较重要的一部分,但是难度不大,只要维护好时间戳就可以了,对于设置名字,其实只要熟悉广播包的数据协议其实也没有什么难度,下一篇就是数据采集和存储同时APP获取并显示。</span></p>

Jacktang 发表于 2021-6-27 20:15

<p>等着下一篇数据采集和存储同时APP获取并显示</p>

w494143467 发表于 2021-6-27 20:24

Jacktang 发表于 2021-6-27 20:15
等着下一篇数据采集和存储同时APP获取并显示

<p>已经写出来了~</p>
页: [1]
查看完整版本: 【环境专家之智能手表】Part12:手表名称和时间的配置