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

沧海笑1122 发表于 2020-1-28 17:06

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

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

【故事】LilyGO-T-Wristband手环是芯元电子最新推出的一款极客玩具。我有幸参与评测活动。这款手环基于esp32主控,配置屏幕、触摸按键以及姿态传感器,RTC当然必不可少。首先让我们看看这款产品的外观和配置。
[*]亮点一:外观很迷你,完全和市面的消费级运动手环感观一致。
[*]亮点二:具备极客再创作的条件。Esp32成熟的core,加上usb-ttl转接板以及连接排线,以及很不错的上手文档。
在本次评测中,我用一个心率功能+触摸唤醒和休眠,来展示一下这款极客玩具的性能。 【视频】 首先看看完成的视频,因为这款手环有一个很漂亮的面板,和底壳浑然一体,但是在视频拍摄中,为了更清晰展示细节,我把面板暂时打开。    点击这里观看视频☞☞☞
【软件】这个评测作品,使用了esp32-ble官方库,以及arduino-IDE 1.8.10官方出厂代码中用到了以下两个库,我只用到了TFT_eSPI,注意在使用时一定要在.h文件中把相应的配置做一个修改。如图:· TFT_eSPI· PCF8563_Library 【硬件】使用了一块LilyGO-T-Wristband手环以及一条BLE心率带。
【代码及注释】代码部分不是很复杂,我尽可能做一些注释
/**
*@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 towhom 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.
staticBLEUUID serviceUUID(BLEUUID((uint16_t)0x180D));
// The HRM characteristic of the remote service we are interested in.
staticBLEUUID    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;
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;
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;
    Serial.print("Heart Rate ");
    Serial.print(hrm.HRM, DEC);
    Serial.println("bpm");
          hrm_str =String(pData, 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;
    }
}


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


Justinxu 发表于 2020-9-9 16:59

你好, 我想用这个手环帮孩子做个倒计时功能, 不知道可以请你帮忙吗?

沧海笑1122 发表于 2020-9-10 10:39

Justinxu 发表于 2020-9-9 16:59
你好, 我想用这个手环帮孩子做个倒计时功能, 不知道可以请你帮忙吗?

说说看你的具体想法。

Justinxu 发表于 2020-9-15 20:04

不好意思, 这两天比较忙, 没上了看。 谢谢你的回复啊。

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

启动, 进入5分钟倒计时
到最后一分钟前每过1分钟短响提醒。
最后30秒每10秒提醒,
最后10秒每秒提醒,
归零后停止,或者正计时, 但再启动,马上回到上次的循环。

沧海笑1122 发表于 2020-9-17 10:41

Justinxu 发表于 2020-9-15 20:04
不好意思, 这两天比较忙, 没上了看。 谢谢你的回复啊。

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


这个手环的核心是esp32,可以用arduino(c++)和mpy开发,你刚才描述的功能完全可以实现,就是一个计时+提醒,这款手环有RTC,计时相关都没问题,提醒的话,我记得是不带喇叭(发声装置),如果你需要声音提醒,这个可能不合适,因为体积非常袖珍,所以不适合扩展,你可以考虑其其他产品。

Justinxu 发表于 2020-9-17 22:58

谢谢你的回复。 有什么好的推荐吗?
页: [1]
查看完整版本: LilyGO-T-Wristband手环评测之心率手环