LilyGO-T-Wristband手环评测之心率手环-Arduino中文社区 - Powered by Discuz!

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 10335|回复: 5

LilyGO-T-Wristband手环评测之心率手环

[复制链接]
发表于 2020-1-28 17:06 | 显示全部楼层 |阅读模式
【LilyGO-T-Wristband手环评测之心率手环】

故事】
  LilyGO-T-Wristband手环是芯元电子最新推出的一款极客玩具。我有幸参与评测活动。这款手环基于esp32主控,配置屏幕、触摸按键以及姿态传感器,RTC当然必不可少。首先让我们看看这款产品的外观和配置。
band5.png
band4.png
band6.png
  • 亮点一:外观很迷你,完全和市面的消费级运动手环感观一致。
  • 亮点二:具备极客再创作的条件。Esp32成熟的core,加上usb-ttl转接板以及连接排线,以及很不错的上手文档。
  在本次评测中,我用一个心率功能+触摸唤醒和休眠,来展示一下这款极客玩具的性能。
【视频】
首先看看完成的视频,因为这款手环有一个很漂亮的面板,和底壳浑然一体,但是在视频拍摄中,为了更清晰展示细节,我把面板暂时打开。

【软件】
  这个评测作品,使用了esp32-ble官方库,以及arduino-IDE 1.8.10
官方出厂代码中用到了以下两个库,我只用到了TFT_eSPI,注意在使用时一定要在.h文件中把相应的配置做一个修改。如图:
tft.jpg
【硬件】
  使用了一块LilyGO-T-Wristband手环以及一条BLE心率带。

【代码及注释】
  代码部分不是很复杂,我尽可能做一些注释

[mw_shl_code=arduino,true]/**
*  @filename   :   epd1in54-demo.ino
*  @brief      :   1.54inch e-paper display demo
*  @author     :   Yehui from Waveshare
*
*  Copyright (C) Waveshare     September 5 2017
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documnetation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to  whom the Software is
* furished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
Pinout
Name        Pin
TFT Driver        ST7735
TFT_MISO        N/A
TFT_MOSI        19
TFT_SCLK        18
TFT_CS        5
TFT_DC        23
TFT_RST        26
TFT_BL        27
Touchpad        33
Touchpad Power        25
IMU Interrupt        38
RTC Interrupt        34
Battery ADC        35
VBUS ADC        36
I2C_SDA        21
I2C_SCL        22
LED        4
CHARGE Indication        38

ver:2020.01.26
1、ttgo手环,显示心率数据
2、触摸休眠

*/


//=============== TFT
#include <TFT_eSPI.h> // Graphics and font library for ST7735 driver chip
#include "ttgo.h"
#include <SPI.h>
#include <Wire.h>
TFT_eSPI tft = TFT_eSPI();  // Invoke library, pins defined in User_Setup.h
//====================

#include "BLEDevice.h"
const String sketchName = "ESP32 HRM BLE Client";

// BLE
// The remote HRM service we wish to connect to.
static  BLEUUID serviceUUID(BLEUUID((uint16_t)0x180D));
// The HRM characteristic of the remote service we are interested in.
static  BLEUUID    charUUID(BLEUUID((uint16_t)0x2A37));

static BLEAddress *pServerAddress;
static boolean doConnect = false;
static boolean connected = false;
static boolean notification = false;
static BLERemoteCharacteristic* pRemoteCharacteristic;
unsigned long screen_update, stats_update;
// TypeDef
typedef struct {
  char ID[20];
  uint16_t HRM;
}HRM;
HRM hrm;

/*===== SETTINGS =====*/
/* Display settings */这部分是为了下一步显示心率曲线而进行的变量设置,暂不删除
#define minRow       0              /* default =   0 */
#define maxRow     319              /* default = 319 */
#define minLine      0              /* default =   0 */
#define maxLine    200              /* default =  200 */
#define LineText     0
#define Line        60
#define LineVal     100
unsigned long maxVal     = 0;
double multiplicator     = 0.0;
unsigned int val[320];
unsigned long hrms       = 0;//全局变量-心率值
String hrm_str ;//全局变量-心率值
bool pressed = false; //触摸键按下

//计算显示用的比例因子
void getMultiplicator() {
  maxVal = 1;
  //选出最大值
  for (int i = 0; i < maxRow; i++) {
    if (val > maxVal) maxVal = val;
  }
  //如果maxVal大于LineVal,则比例因子=LineVal/maxVal,否则比例因子=1
  if (maxVal > LineVal) multiplicator = (double)LineVal / (double)maxVal;
  else multiplicator = 1;
}
//=============设置手环PIN 定义
#define TP_PIN_PIN          33
#define I2C_SDA_PIN         21
#define I2C_SCL_PIN         22
#define IMU_INT_PIN         38
#define RTC_INT_PIN         34
#define BATT_ADC_PIN        35
#define VBUS_PIN            36
#define TP_PWR_PIN          25
#define LED_PIN             4
#define CHARGE_PIN          32
int vref = 1100;

//--------------------------------------------------------------------------------------------
// BLE notifyCallback
//--------------------------------------------------------------------------------------------
static void notifyCallback( BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) {
    hrm.HRM = pData[1];
    Serial.print("Heart Rate ");
    Serial.print(hrm.HRM, DEC);
    Serial.println("bpm");
          hrm_str =  String(pData[1], DEC);   
          hrms=hrm_str.toInt();
    tft.fillScreen(TFT_BLACK);
    tft.setTextColor(TFT_YELLOW, TFT_BLACK);
    tft.drawString("My Heart Rate", 0, 0);
    //tft.drawString("   ", 10, 10,7); //先清空数据部分,防止从三位数变回二位数时,出现多余数据
    tft.drawString(String(hrms), 10, 10,7);
}


//--------------------------------------------------------------------------------------------
//  Connect to BLE HRM
//--------------------------------------------------------------------------------------------
bool connectToServer(BLEAddress pAddress) {
    Serial.print(F("Forming a connection to "));
    Serial.println(pAddress.toString().c_str());

    BLEClient*  pClient  = BLEDevice::createClient();
    Serial.println(F(" - Created client"));
   
    // Connect to the HRM BLE Server.
    pClient->connect(pAddress);
    Serial.println(F(" - Connected to server"));

    // Obtain a reference to the service we are after in the remote BLE server.
    BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
    if (pRemoteService == nullptr) {
      Serial.print(F("Failed to find our service UUID: "));
      Serial.println(serviceUUID.toString().c_str());
      return false;
    }
    Serial.println(F(" - Found our service"));


    // Obtain a reference to the characteristic in the service of the remote BLE server.
    pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
    if (pRemoteCharacteristic == nullptr) {
      Serial.print(F("Failed to find our characteristic UUID: "));
      Serial.println(charUUID.toString().c_str());
      return false;
    }
    Serial.println(F(" - Found our characteristic"));

    // Register for Notify
    pRemoteCharacteristic->registerForNotify(notifyCallback);
}

//--------------------------------------------------------------------------------------------
// Scan for BLE servers and find the first one that advertises the service we are looking for.
//--------------------------------------------------------------------------------------------
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
/**
   * Called for each advertising BLE server.
   */
  void onResult(BLEAdvertisedDevice advertisedDevice) {
    Serial.print(F("BLE Advertised Device found: "));
    Serial.println(advertisedDevice.toString().c_str());

    // We have found a device, let us now see if it contains the service we are looking for.
    if (advertisedDevice.haveServiceUUID() && advertisedDevice.getServiceUUID().equals(serviceUUID)) {

      //
      Serial.print(F("Found our device!  address: "));
      advertisedDevice.getScan()->stop();

      pServerAddress = new BLEAddress(advertisedDevice.getAddress());
      doConnect = true;

    } // Found our server
  } // onResult
}; // MyAdvertisedDeviceCallbacks


unsigned long time_start_ms;
unsigned long time_now_s;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  
//=====TFT INIT
    tft.init();
    tft.setRotation(1);
    tft.setSwapBytes(true);
    tft.pushImage(0, 0,  160, 80, ttgo);
    delay(2000);
    tft.fillScreen(TFT_BLACK);
    tft.setTextColor(TFT_YELLOW, TFT_BLACK); // Note: the new fonts do not draw the background colour
    tft.drawString("My Heart Rate", 0, 0);
    Serial.print("Hello World");
    delay(2000);
//============== TP_PWR INIT

    pinMode(TP_PIN_PIN, INPUT);
    //! Must be set to pull-up output mode in order to wake up in deep sleep mode
    pinMode(TP_PWR_PIN, PULLUP);
    digitalWrite(TP_PWR_PIN, HIGH);
//=====BLE INIT

  time_start_ms = millis();

   BLEDevice::init("");

  // Retrieve a Scanner and set the callback we want to use to be informed when we
  // have detected a new device.  Specify that we want active scanning and start the
  // scan to run for 30 seconds.
  BLEScan* pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->setActiveScan(true);
  pBLEScan->start(30);
  
}


String getVoltage() //这个函数本测试未用到,但是仍保留,在下一个版本可能尝试
{
    uint16_t v = analogRead(BATT_ADC_PIN);
    float battery_voltage = ((float)v / 4095.0) * 2.0 * 3.3 * (vref / 1000.0);
    return String(battery_voltage) + "V";
}


void loop() {
// If the flag "doConnect" is true then we have scanned for and found the desired
  // BLE Server with which we wish to connect.  Now we connect to it.  Once we are
  // connected we set the connected flag to be true.
  if (doConnect == true) {
    if (connectToServer(*pServerAddress)) {
      Serial.println(F("We are now connected to the BLE HRM"));
      connected = true;
    } else {
      Serial.println(F("We have failed to connect to the HRM; there is nothin more we will do."));
    }
    doConnect = false;
  }

  // Turn notification on
  if (connected) {
    if (notification == false) {
      Serial.println(F("Turning Notifocation On"));
      const uint8_t onPacket[] = {0x1, 0x0};
      pRemoteCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)onPacket, 2, true);
      notification = true;
    }
   
  }
  //============显示电池电压
//       tft.setTextColor(TFT_BLUE, TFT_BLACK);
//       tft.drawCentreString(getVoltage(), 120, 60, 1); // Next size up font 2
      
if (digitalRead(TP_PIN_PIN) == HIGH) {
        if (!pressed) {
        tft.setTextColor(TFT_GREEN, TFT_BLACK);
        tft.setTextDatum(MC_DATUM);
        tft.drawString("Sleep Now,Press again wake", 10, 65 );
        Serial.println("Go to Sleep");
        
        delay(3000);
        //==========关闭屏幕、esp32进入深度睡眠
        tft.writecommand(ST7735_SLPIN);
        tft.writecommand(ST7735_DISPOFF);
        
        esp_sleep_enable_ext1_wakeup(GPIO_SEL_33, ESP_EXT1_WAKEUP_ANY_HIGH);
        esp_deep_sleep_start();
        pressed = true;
   
        }
    } else {
        pressed = false;
    }
}
[/mw_shl_code]


【关于调试的过程】
这款手环提供了一个带有软排线的TTL-USB转接板,两侧金手指向下,轻轻扣好,即可通过IDE进行调试。我也尝试了arduino方式以及烧写micropython固件。usb的驱动支持很完美。提供两个接口,type-C以及microusb。即便如此,拆装过程还是需要耐心,建议玩家在拆卸和装配外壳的过程中,做好笔记,事先拍摄照片,确保装配过程完好,注意不要用力压盖没有完全贴合的PCB和屏幕,以免造成不必要的损失。
IMG_20200126_220306aq.jpg
IMG_20200125_213256_a1.jpg
IMG_20200125a1.jpg
【需要注意以及完善的地方】
1、关于手环结构。在极客应用中,玩家需要拆开面板(尤其是在解决OTA之前),但这块手环的面板和底壳贴合很好,所以不适合经常拆卸。一旦发生在拆装过程中,面板和底壳对不齐的情况,一旦用力扣压,有可能导致伤及屏幕。建议LilyGO关于这部分在说明书里面,详细介绍一下。
2、关于wifi OTA。出厂测试代码很赞,尤其是无线升级这块应当重点介绍一下,这部分做好了,就可以避免用户经常拆卸外壳,和m5stackuiflow(wifi方式)的思路也是一致的,希望这部分重点介绍。
3、关于电源待机,手环配置了一块80mah的锂电池,在使用了休眠功能后,基本上可以待机一天。这个是很不容易的。ESP32一旦开了wifible以及屏幕,能耗的确是一个大问题,大家也都在尝试解决。从这一点看,LilyGO手环做得算是很不错了。我这次评测把触摸休眠和唤醒做了一个介绍,也是希望抛砖引玉,大家更多关注智能穿戴的能耗以及降耗问题。
   感谢芯元电子提供评测的机会,感谢arduino.cn提供交流平台。
祝芯元电子(LilyGO)越做越好。春节快乐。
这个不同寻常的春节正因为有了创客制作,才略多了些许趣味,显得不是那么艰难。

【代码分享

upload0128.rar (20.16 KB, 下载次数: 51)


发表于 2020-9-9 16:59 | 显示全部楼层
你好, 我想用这个手环帮孩子做个倒计时功能, 不知道可以请你帮忙吗?
 楼主| 发表于 2020-9-10 10:39 | 显示全部楼层
Justinxu 发表于 2020-9-9 16:59
你好, 我想用这个手环帮孩子做个倒计时功能, 不知道可以请你帮忙吗?

说说看你的具体想法。
发表于 2020-9-15 20:04 | 显示全部楼层
不好意思, 这两天比较忙, 没上了看。 谢谢你的回复啊。

我想用这个表实现如下过程。

启动, 进入5分钟倒计时
到最后一分钟前每过1分钟短响提醒。
最后30秒每10秒提醒,
最后10秒每秒提醒,
归零后停止,或者正计时, 但再启动,马上回到上次的循环。
 楼主| 发表于 2020-9-17 10:41 | 显示全部楼层
Justinxu 发表于 2020-9-15 20:04
不好意思, 这两天比较忙, 没上了看。 谢谢你的回复啊。

我想用这个表实现如下过程。

这个手环的核心是esp32,可以用arduino(c++)和mpy开发,你刚才描述的功能完全可以实现,就是一个计时+提醒,这款手环有RTC,计时相关都没问题,提醒的话,我记得是不带喇叭(发声装置),如果你需要声音提醒,这个可能不合适,因为体积非常袖珍,所以不适合扩展,你可以考虑其其他产品。
发表于 2020-9-17 22:58 | 显示全部楼层
谢谢你的回复。 有什么好的推荐吗?
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-11-28 02:49 , Processed in 0.107187 second(s), 18 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表