【LilyGO-T-Wristband手环评测之心率手环】
【故事】 LilyGO-T-Wristband手环是芯元电子最新推出的一款极客玩具。我有幸参与评测活动。这款手环基于esp32主控,配置屏幕、触摸按键以及姿态传感器,RTC当然必不可少。首先让我们看看这款产品的外观和配置。 - 亮点一:外观很迷你,完全和市面的消费级运动手环感观一致。
- 亮点二:具备极客再创作的条件。Esp32成熟的core,加上usb-ttl转接板以及连接排线,以及很不错的上手文档。
在本次评测中,我用一个心率功能+触摸唤醒和休眠,来展示一下这款极客玩具的性能。 【视频】 首先看看完成的视频,因为这款手环有一个很漂亮的面板,和底壳浑然一体,但是在视频拍摄中,为了更清晰展示细节,我把面板暂时打开。
【软件】 这个评测作品,使用了esp32-ble官方库,以及arduino-IDE 1.8.10 官方出厂代码中用到了以下两个库,我只用到了TFT_eSPI,注意在使用时一定要在.h文件中把相应的配置做一个修改。如图: 【硬件】 使用了一块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和屏幕,以免造成不必要的损失。 【需要注意以及完善的地方】 1、关于手环结构。在极客应用中,玩家需要拆开面板(尤其是在解决OTA之前),但这块手环的面板和底壳贴合很好,所以不适合经常拆卸。一旦发生在拆装过程中,面板和底壳对不齐的情况,一旦用力扣压,有可能导致伤及屏幕。建议LilyGO关于这部分在说明书里面,详细介绍一下。 2、关于wifi OTA。出厂测试代码很赞,尤其是无线升级这块应当重点介绍一下,这部分做好了,就可以避免用户经常拆卸外壳,和m5stack的uiflow(wifi方式)的思路也是一致的,希望这部分重点介绍。 3、关于电源待机,手环配置了一块80mah的锂电池,在使用了休眠功能后,基本上可以待机一天。这个是很不容易的。ESP32一旦开了wifi和ble以及屏幕,能耗的确是一个大问题,大家也都在尝试解决。从这一点看,LilyGO手环做得算是很不错了。我这次评测把触摸休眠和唤醒做了一个介绍,也是希望抛砖引玉,大家更多关注智能穿戴的能耗以及降耗问题。 感谢芯元电子提供评测的机会,感谢arduino.cn提供交流平台。 祝芯元电子(LilyGO)越做越好。春节快乐。 这个不同寻常的春节正因为有了创客制作,才略多了些许趣味,显得不是那么艰难。
【代码分享】
|