diff Discovery/Src/cv_heartbeat.c @ 1032:33b91584d827 Puls_Integration

New CV Pulse: The basic infrastructure for external puls measurement via Bluetooth has been added. Precondition is an OSTC with an activated central role. The OSTC will then search for a BLE device with puls measurement service. Reading data and visualization is not implemented yet.
author Ideenmodellierer
date Mon, 28 Jul 2025 18:34:45 +0200
parents
children 5f66e44d69f0
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Discovery/Src/cv_heartbeat.c	Mon Jul 28 18:34:45 2025 +0200
@@ -0,0 +1,437 @@
+///////////////////////////////////////////////////////////////////////////////
+/// -*- coding: UTF-8 -*-
+///
+/// \file   Discovery/Src/cv_heartbeat.c
+/// \brief  providing functionality to connect OSTC to a Polar HC10 heartbeat sensor
+/// \author heinrichs weikamp gmbh
+/// \date   03-July-2025
+///
+/// $Id$
+///////////////////////////////////////////////////////////////////////////////
+/// \par Copyright (c) 2014-2025 Heinrichs Weikamp gmbh
+///
+///     This program is free software: you can redistribute it and/or modify
+///     it under the terms of the GNU General Public License as published by
+///     the Free Software Foundation, either version 3 of the License, or
+///     (at your option) any later version.
+///
+///     This program is distributed in the hope that it will be useful,
+///     but WITHOUT ANY WARRANTY; without even the implied warranty of
+///     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+///     GNU General Public License for more details.
+///
+///     You should have received a copy of the GNU General Public License
+///     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//////////////////////////////////////////////////////////////////////////////
+
+#include "cv_heartbeat.h"
+#include "tMenuEdit.h"
+
+#include "gfx_fonts.h"
+#include "tHome.h"
+#include "ostc.h"
+#include "tComm.h"
+#include "tInfoLogger.h"
+
+static sensorHeartbeat_State_t heartbeatState = SENSOR_HB_OFFLINE;
+
+static uint8_t OnAction_Heartbeat(uint32_t editId, uint8_t blockNumber, uint8_t digitNumber, uint8_t digitContent, uint8_t action);
+static uint32_t startDetection_ms;
+
+#define MAX_BT_DEVICE 10		/* max number of device which may be handled */
+static btDdeviceData_t btDeviceList[MAX_BT_DEVICE];
+static btDeviceService_t curDeviceService[10];
+static uint8_t curServiceIndex = 0;
+static uint8_t curBtIndex = 0;
+static uint8_t connHandle = ' ';
+
+static indicatior_t checkIndicators(uint8_t* pdata)
+{
+#if 0
+	static uint8_t foundRSSI = 0;
+	static uint8_t foundNAME = 0;
+	static uint8_t foundMNF = 0;
+	static uint8_t foundUUID = 0;
+	static uint8_t foundOK = 0;
+#endif
+	indicatior_t ret = NO_INDICATOR;
+
+	if(strcmp((char*)pdata,"+UBTD:") == 0)
+	{
+		ret = DEVICE_INDICATOR;
+	}
+	else if(strcmp((char*)pdata,"+UUBTACLC:") == 0)
+	{
+		ret = CONNECTION_INDICATOR;
+	}
+	else if(strcmp((char*)pdata,"+UBTGDP:") == 0)
+	{
+		ret = SERVICE_INDICATOR;
+	}
+
+	return ret;
+}
+
+static void handleOK()
+{
+	switch(heartbeatState)
+	{
+		case SENSOR_HB_ENABLE_BLE:	heartbeatState = SENSOR_HB_CHECK_CONFIG;
+							break;
+		case SENSOR_HB_CHECK_CONFIG: heartbeatState = SENSOR_HB_DISCOVER;
+							break;
+		case SENSOR_HB_RESTART: 	heartbeatState = SENSOR_HB_OFFLINE;
+							break;
+		case SENSOR_HB_DISCOVER: heartbeatState = SENSOR_HB_CONNECT;
+							break;
+		case SENSOR_HB_SERVICES: heartbeatState = SENSOR_HB_OFFLINE;
+							break;
+		default:
+							break;
+	}
+}
+
+static void handleERROR()
+{
+
+}
+
+static uint8_t getDeviceList()
+{
+	static uint8_t firstDevice = 1;
+	static uint8_t curLine[MAX_CHAR_PER_LINE];			/* holds complete line and is used for logging */
+	static uint8_t curLineIndex = 0;
+	static uint8_t parameter[40];		/* content of the parameter in read state */
+	static uint8_t writeIndex = 0;
+	static uint8_t complete = 0;
+
+	static readDataType_t readType = BT_READ_NOTHING;
+
+	char text[40];
+	uint8_t data = 0;
+	data = UART_getChar();
+
+	while((data != 0) && (complete == 0))
+	{
+		if(curLineIndex == MAX_CHAR_PER_LINE)		/* avoid overflow */
+		{
+			InfoLogger_writeLine(curLine,curLineIndex,0);
+			memset(curLine,0,sizeof(curLine));
+			curLineIndex = 0;
+		}
+		if((data == '\r') || (data == '\n'))
+		{
+			if(curLineIndex > 0)
+			{
+				InfoLogger_writeLine(curLine,curLineIndex,0);
+				if(strcmp((char*)curLine,"OK") == 0)
+				{
+					handleOK();
+				}
+				else
+				{
+					if(strcmp((char*)curLine,"ERROR") == 0)
+					{
+						handleERROR();
+					}
+				}
+			}
+			switch(readType)
+			{
+				case BT_READ_DEVICE_NAME: 	if(writeIndex < BLUEMOD_NAME_SIZE)
+											{
+												memcpy (btDeviceList[curBtIndex].name, parameter, writeIndex);
+											}
+								break;
+				case BT_READ_SERV_UUID: 	if(writeIndex < 50)
+											{
+												memcpy(curDeviceService[curServiceIndex].uuid, parameter, writeIndex);
+											}
+											curServiceIndex++;
+								break;
+				default:
+					break;
+			}
+			curLineIndex = 0;
+			writeIndex = 0;
+			memset(curLine,0,sizeof(curLine));
+			readType = BT_READ_NOTHING;
+		}
+		else
+		{
+			if(curLineIndex < MAX_CHAR_PER_LINE) curLine[curLineIndex++] = data;
+
+			if(data == ':')
+			{
+				switch(checkIndicators(curLine))
+				{
+					case DEVICE_INDICATOR: 		readType = BT_READ_DEVICE_ADDR;
+						break;
+					case CONNECTION_INDICATOR:  readType = BT_READ_CON_DETAILS;
+						break;
+					case SERVICE_INDICATOR: 	readType = BT_READ_SERV_HANDLE;
+						break;
+					default:
+						break;
+				}
+				writeIndex = 0;
+				memset(parameter,0,sizeof(parameter));
+			}
+			else
+			{
+				if(data == ',')					/* parameter end */
+				{
+					switch(readType)
+					{
+						case BT_READ_DEVICE_ADDR:  if(writeIndex < BLUEMOD_ADDR_SIZE-1)
+													{
+														if(firstDevice)
+														{
+															firstDevice = 0;
+														}
+														else
+														{
+															curBtIndex++;
+														}
+														parameter[writeIndex-1] = 0;	/*remove 'p' from address */
+														strcpy((char*)btDeviceList[curBtIndex].address, (char*)parameter);
+													}
+													readType = BT_READ_DEVICE_RSSI;
+								break;
+						case BT_READ_DEVICE_RSSI:	if(writeIndex < BLUEMOD_RSSI_SIZE-1)
+													{
+														strcpy((char*)btDeviceList[curBtIndex].rssi, (char*)parameter);
+													}
+													readType = BT_READ_DEVICE_NAME;
+								break;
+						case BT_READ_DEVICE_NAME: 	if(writeIndex < BLUEMOD_NAME_SIZE-1)
+													{
+														memcpy(btDeviceList[curBtIndex].name, parameter, writeIndex);
+													}
+													readType = BT_READ_NOTHING;
+										break;
+						case BT_READ_CON_DETAILS:	connHandle = parameter[0];
+													heartbeatState = SENSOR_HB_SERVICES;
+													readType = BT_READ_NOTHING;
+										break;
+						case BT_READ_SERV_HANDLE:	curDeviceService[curServiceIndex].handle = parameter[0];
+													readType = BT_READ_SERV_START;
+										break;
+						case BT_READ_SERV_START: 	if(writeIndex < 6)
+													{
+														memcpy(curDeviceService[curServiceIndex].start, parameter, writeIndex);
+													}
+													readType = BT_READ_SERV_END;
+										break;
+						case BT_READ_SERV_END: 		if(writeIndex < 6)
+													{
+														memcpy(curDeviceService[curServiceIndex].end, parameter, writeIndex);
+													}
+													readType = BT_READ_SERV_UUID;
+										break;
+
+						default:					readType = BT_READ_NOTHING;
+							break;
+					}
+					writeIndex = 0;
+					memset(parameter,0 , sizeof(parameter));
+				}
+				else
+				{
+				//	if(readType != BT_READ_NOTHING)
+					{
+						parameter[writeIndex++] = data;
+					}
+				}
+			}
+		}
+		data = UART_getChar();
+	}
+	return complete;
+}
+
+sensorHeartbeat_State_t cv_heartbeat_getState()
+{
+	return heartbeatState;
+}
+
+void openEdit_Heartbeat(void)
+{
+    SSettings *settings = settingsGetPointer();
+
+    char text[32];
+    snprintf(text, 32, "\001%c%c", TXT_2BYTE, TXT2BYTE_Pulse);
+    write_topline(text);
+
+    set_globalState(StMOption_Heartbeat);
+    resetMenuEdit(CLUT_MenuPageCvOption);
+
+    snprintf(text, 32, "%c%c", TXT_2BYTE, TXT2BYTE_SensorDetect);
+    write_field_button(StMOption_Heartbeat, 30, 299, ME_Y_LINE1, &FontT48, text);
+
+    write_buttonTextline(TXT2BYTE_ButtonMinus, TXT2BYTE_ButtonEnter, TXT2BYTE_ButtonPlus);
+
+    setEvent(StMOption_Heartbeat, (uint32_t)OnAction_Heartbeat);
+}
+
+static uint8_t OnAction_Heartbeat(uint32_t editId, uint8_t blockNumber, uint8_t digitNumber, uint8_t digitContent, uint8_t action)
+{
+	switch(heartbeatState)
+	{
+		case SENSOR_HB_OFFLINE:
+				HAL_UART_AbortReceive_IT(&UartHandle);
+				MX_UART_BT_Init_DMA();
+				UART_StartDMARx();
+				heartbeatState = SENSOR_HB_ENABLE_BLE;
+				startDetection_ms = HAL_GetTick();
+				curBtIndex = 0;
+				memset(btDeviceList, 0, sizeof(btDeviceList));
+			break;
+
+		default:
+			break;
+	}
+    return UNSPECIFIC_RETURN;
+}
+
+void cv_heartbeat_Control(void)
+{
+	static uint8_t action = 0;
+	static uint8_t retry = 0;
+	static uint8_t lastState = 0;
+	static uint8_t devicesIndex = 0;
+
+
+	char cmd[50];
+
+	cmd[0] = 0;
+
+	getDeviceList();
+
+	if(action == 3)
+	{
+		action = 0;
+		switch(heartbeatState)
+		{
+			case SENSOR_HB_ENABLE_BLE:	HAL_Delay(1000);
+										snprintf(cmd, sizeof(cmd), "+++" ); //"AT+UBTD=2,1,5000\r\n"
+										InfoLogger_writeLine((uint8_t*)cmd,3,1);
+										HAL_UART_Transmit(&UartHandle, (uint8_t*)cmd, 3, 5000);
+										HAL_Delay(1000);
+										cmd[0] = 0;
+				break;
+			case SENSOR_HB_CHECK_CONFIG:  snprintf(cmd, sizeof(cmd), "AT+UBTCFG=2\r\n" );	// AT+UBTLE?
+
+#if 0
+				if(settingsGetPointer()->dive_mode == DIVEMODE_OC)
+				{
+					snprintf(cmd, sizeof(cmd), "AT+UBTLE=2\r\n" ); //+UBTLE=1 //"AT+UBTD=2,1,5000\r\n"
+				}
+				else
+				{
+					snprintf(cmd, sizeof(cmd), "AT+UBTLE=3\r\n" ); //+UBTLE=1 //"AT+UBTD=2,1,5000\r\n"
+				}
+#endif
+				break;
+			case SENSOR_HB_DISCOVER:	if(lastState != SENSOR_HB_DISCOVER)
+										{
+											snprintf(cmd, sizeof(cmd), "AT+UBTD=2,1\r\n" ); //+UBTLE=1 //"AT+UBTD=2,1,5000\r\n"
+											devicesIndex = 0;
+										}
+
+										//snprintf(cmd, sizeof(cmd), "AT&W\r\n" ); //  AT+UBTD=2,1\r\n "AT+UBTD=2,1,5000\r\n"
+				break;
+#if 0
+			case SENSOR_HB_RESTART:	snprintf(cmd, sizeof(cmd), "AT+UBTD=2,1\r\n" ); //+UBTLE=1 //"AT+UBTD=2,1,5000\r\n"
+
+									//	snprintf(cmd, sizeof(cmd), "AT+CPWROFF\r\n" ); //  AT+UBTD=2,1\r\n "AT+UBTD=2,1,5000\r\n"
+				break;
+#endif
+			case SENSOR_HB_CONNECT:		if(curBtIndex != devicesIndex)
+										{
+											snprintf(cmd, sizeof(cmd), "AT+UBTACLC=%s\r\n",btDeviceList[devicesIndex].address);
+											devicesIndex++;
+										}
+				break;
+			case SENSOR_HB_SERVICES:	if((connHandle >= '0') && (connHandle <= '9') && (lastState != SENSOR_HB_SERVICES))
+										{
+											snprintf(cmd, sizeof(cmd), "AT+UBTGDP=0%c\r\n",connHandle);
+										}
+										else
+										{
+											heartbeatState = SENSOR_HB_OFFLINE;
+										}
+				break;
+
+			default:
+				break;
+		}
+		if(cmd[0] != 0)
+		{
+			{
+				InfoLogger_writeLine((uint8_t*)cmd,strlen(cmd),1);
+				HAL_UART_Transmit(&UartHandle, (uint8_t*)cmd, strlen(cmd), 5000);
+				retry++;
+				if(retry == 3)
+				{
+					heartbeatState = SENSOR_HB_OFFLINE;
+				}
+			}
+		}
+		if(lastState != heartbeatState)
+		{
+			lastState = heartbeatState;
+			retry = 0;
+		}
+	}
+	else
+	{
+		action++;
+	}
+}
+void refresh_Heartbeat(void)
+{
+	char text[32];
+	uint8_t index = 0;
+
+	snprintf(text, 32, "\001%c%c", TXT_2BYTE, TXT2BYTE_Pulse);
+	write_topline(text);
+
+	switch(heartbeatState)
+	{
+		case SENSOR_HB_OFFLINE:
+		default:							snprintf(text, 32, "%c%c", TXT_2BYTE, TXT2BYTE_SensorDetect);
+											write_label_var(30, 299, ME_Y_LINE1, &FontT48, text);
+
+	    									if(curBtIndex > 0)
+	    									{
+	    										while((index < curBtIndex) && (index < 3))
+	    										{
+													snprintf(text, 40, "%s", btDeviceList[index].address);
+													write_label_var(  30, 300,  ME_Y_LINE3 + (index * ME_Y_LINE_STEP), &FontT48, text);
+													index++;
+	    										}
+	    									}
+			break;
+		case SENSOR_HB_ENABLE_BLE:			snprintf(text, 32, "Activate BLE");
+											write_label_var(  30, 300, ME_Y_LINE1, &FontT48, text);
+			break;
+		case SENSOR_HB_DISCOVER:
+	    									snprintf(text, 32, "Searching");
+	    									write_label_var(  30, 300, ME_Y_LINE1, &FontT48, text);
+
+	    									if(curBtIndex > 0)
+	    									{
+	    										while((index < curBtIndex) && (index < 4))
+	    										{
+													snprintf(text, 40, "%s", btDeviceList[index].address);
+													write_label_var(  30, 300,  ME_Y_LINE2 + (index * ME_Y_LINE_STEP), &FontT48, text);
+													index++;
+	    										}
+	    									}
+			break;
+	}
+
+    write_buttonTextline(TXT2BYTE_ButtonMinus, TXT2BYTE_ButtonEnter, TXT2BYTE_ButtonPlus);
+}
+