[mw_shl_code=arduino,true]/*
===========================================
Copyright (c) 2018 Stefan Kremser
github.com/spacehuhn
===========================================
(1)图形部分根据ESP8266 + OLED = WiFi Packet Monitor 修改
(2)BLE蓝牙带部分,根据# ESP32 BLE HRM/MQTT Gateway Code for an ESP32 with an integrated OLED Connecting to a BLE HRM Monitor
Links:- Video
https://youtu.be/iCKIIMrphtg
Date:
2019-08-27
1、利用watchIO平台,读取ble心率带数据并且显示
2、后续将心率数据以彩色图形方式显示(160*80)
2019-10-04
1、优化显示页面以及图形部分
2019-10-05
1、增加了11段渐变色显示心率图形
*/
char c_hrm[]=""; //char 类型的hrm值,用于在手表显示器中显示字符数据
//WatchIO手表配置
#include "config.h"
#include "power.h"
#include "lcd.h"
const uint16_t COLORS_LIGHT[10] = {
0xFA55, 0x0C3E, 0xC01E, 0xF255, 0xF820,
0xF321, 0xFFA0, 0x17A0, 0x04BF, 0xC01F
};
const uint32_t COLORS_DARK[10] = {
0x40E6, 0x0128, 0x3809, 0x38C5, 0x4001,
0x40E0, 0x4A20, 0x0220, 0x0108, 0x3008
};
//BLE部分设置
#include "BLEDevice.h"
const String sketchName = "ESP32 HRM BLE Client";
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;
/*===== GRAPH SETTINGS =====*/
/* Display settings */
#define minRow 0 /* default = 0 */
#define maxRow 87 /* 局部刷新区88列 */
#define minLine 0 /* default = 0 */
#define maxLine 63 /* 局部刷新区64行 */
//* render settings */此部分备用
#define Row1 0
#define Row2 30
//#define LineText 0
//#define Line 60
#define LineVal 60 //按60做比例系数,<最高行数63
//===== Run-Time variables =====//
unsigned long prevTime = 0;
unsigned long curTime = 0;
unsigned long hrms = 0;//全局变量-心率值
unsigned long maxVal = 0;
double multiplicator = 0.0;
unsigned int val[90]; //数组size
//计算显示用的比例因子
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;
}
//--------------------------------------------------------------------------------------------
// BLE notifyCallback 在此Callback函数中,读取心率带数据,然后给全局变量unsigned long hrms 赋值
//--------------------------------------------------------------------------------------------
static void notifyCallback( BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) {
hrm.HRM = pData[1];
String hrm_str = String(pData[1], DEC);
hrms=hrm_str.toInt();
}
//--------------------------------------------------------------------------------------------
// 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
//===== SETUP =====
void setup() {
/* start Serial */
Serial.begin(115200);
Serial.println("starting!");
//Start BLE--BLE部分初始化
// 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.
BLEDevice::init("");
BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true);
pBLEScan->start(30);
//-----watchIO 部分初始化
init_power();
lcd_init();
}
//===== LOOP =====//
void loop() {
curTime = millis(); //当前时间计时
// 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;
}
//every 3 second
if (curTime - prevTime >= 3000) {
// 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;
}
}
prevTime = curTime;
char c_hrm[]="";
dtostrf(hrms,3,0,c_hrm); //转换为char类型,用于显示
//整体左移所有的心率值
for (int i = 0; i < maxRow; i++) {
val = val[i + 1];
}
val[87] = hrms;//最新数据放入val[87]
//recalculate scaling factor 重新计算比例因子
getMultiplicator();
//调试信息,运行时可以去除
Serial.print("hrm:");
Serial.println(hrms); //串口打印一个心率值
//canvas.fillScreen(ST77XX_BLUE);
canvas.fillScreen(0x435c); //屏幕背景色
canvas.setCursor(10, 10);
canvas.setTextSize(2);
canvas.setTextColor(ST77XX_WHITE);
canvas.print("HRM:"); //显示HRM标签
canvas.setTextSize(3);
canvas.setTextColor(ST77XX_YELLOW);
canvas.setCursor(10, 50);
canvas.print(hrms); //显示心率数据
//画一个矩形填充框,图像在其中显示
canvas.fillRect(60,4,94,71,0xFF79);
//============在矩形填充框内绘制心率图形
for (int i = 0; i <=maxRow; i++) {
// 用垂直线表达心率值,并且按照比例尺进行调整
//调试信息,运行时请去除
//Serial.println("i-------- val --------- multiplicator");
//Serial.println(i);
// Serial.println(val);
// Serial.println(multiplicator);
int line3;//最初是三段式渐变,因此命名为Line3,后面延用
line3= val*multiplicator;
//以下我们准备了三个方案显示心率图形
/*方案一:三段显示
// 第一段:0~33% 0xEFDE浅
canvas.drawFastVLine(i+63, int(maxLine - val*multiplicator)+9+line3*0.66, line3*0.34, 0xEFDE);
// 第二段:34~66% 0x76B8中
canvas.drawFastVLine(i+63, int(maxLine - val*multiplicator)+9+line3*0.33, line3*0.33, 0x76B8);
//第三段:67~100% 0x1531深
canvas.drawFastVLine(i+63, int(maxLine - val*multiplicator)+9, line3*0.33, 0x1531 );
*/
//------------方案二,四段显示
/*
//第一段:0~33% 0xff79浅
canvas.drawFastVLine(i+63, int(maxLine - val*multiplicator)+9+line3*0.55, line3*0.45, 0xff79);
// 第二段:34~66% 0xfef9中
canvas.drawFastVLine(i+63, int(maxLine - val*multiplicator)+9+line3*0.25, line3*0.3, 0xfef9);
//第三段:67~100% 0xfe79较深
canvas.drawFastVLine(i+63, int(maxLine - val*multiplicator)+9+line3*0.05, line3*0.2, 0xfe79 );
//第四段:61~100% 0xfdd9深
canvas.drawFastVLine(i+63, int(maxLine - val*multiplicator)+9, line3*0.05, 0x7840 );
*/
//----------------方案三,11段,第一段和最后一段各5%,剩余10%每段
//第一段:0~5% 0xef60 浅
canvas.drawFastVLine(i+63, int(maxLine - val*multiplicator)+9+line3*0.94, line3*0.05+1, 0xef60);
// 第二段:6~15% 0xf6e0
canvas.drawFastVLine(i+63, int(maxLine - val*multiplicator)+9+line3*0.84, line3*0.1+1, 0xf6e0);
//第三段:16~25% 0xf660
canvas.drawFastVLine(i+63, int(maxLine - val*multiplicator)+9+line3*0.74, line3*0.1+1, 0xf660);
//第四段:26~35% 0xf5c0
canvas.drawFastVLine(i+63, int(maxLine - val*multiplicator)+9+line3*0.64, line3*0.1+1, 0xf5c0);
//第五段:36~45% 0xf540
canvas.drawFastVLine(i+63, int(maxLine - val*multiplicator)+9+line3*0.54, line3*0.1+1, 0xf540);
// 第六段:46~55% 0xf4c0
canvas.drawFastVLine(i+63, int(maxLine - val*multiplicator)+9+line3*0.44, line3*0.1+1,0xf4c0);
//第七段:56~65% 0xfc40
canvas.drawFastVLine(i+63, int(maxLine - val*multiplicator)+9+line3*0.34, line3*0.1+1,0xfc40);
//第八段:66~75% 0xfbc0
canvas.drawFastVLine(i+63, int(maxLine - val*multiplicator)+9+line3*0.24, line3*0.1+1,0xfbc0);
//第九段:76~85% 0xfb20
canvas.drawFastVLine(i+63, int(maxLine - val*multiplicator)+9+line3*0.14, line3*0.1+1,0xfb20);
// 第十段:86~95% 0xfaa0
canvas.drawFastVLine(i+63, int(maxLine - val*multiplicator)+9+line3*0.05, line3*0.1+1,0xfaa0);
// 第十一段:96~100% 0xfa20 深
canvas.drawFastVLine(i+63, int(maxLine - val*multiplicator)+9, line3*0.05,0xfa20); //红色
}
hrms = 0;
delay(500);
}
}[/mw_shl_code]