From 46a0a7e1ce3c7a9e705e6a51ba723f3bb7dbda52 Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Thu, 8 May 2025 10:48:26 -0600 Subject: [PATCH 01/81] Add GNSS_CONTROL characteristic --- Core/Src/main.c | 4 +- STM32_WPAN/App/custom_app.c | 60 +++++++++++++++++++ STM32_WPAN/App/custom_stm.c | 111 ++++++++++++++++++++++++++++++++++-- STM32_WPAN/App/custom_stm.h | 6 ++ flysight-2-firmware.ioc | 18 +++++- 5 files changed, 190 insertions(+), 9 deletions(-) diff --git a/Core/Src/main.c b/Core/Src/main.c index 6d606a4..dc8a2d5 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -102,6 +102,7 @@ static uint8_t watchdog_timer_id; /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); void PeriphCommonClock_Config(void); +static void MX_RF_Init(void); static void MX_GPIO_Init(void); static void MX_DMA_Init(void); static void MX_IPCC_Init(void); @@ -109,7 +110,6 @@ static void MX_RTC_Init(void); static void MX_I2C3_Init(void); static void MX_SPI1_Init(void); static void MX_TIM1_Init(void); -static void MX_RF_Init(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ @@ -163,13 +163,13 @@ int main(void) /* USER CODE END SysInit */ /* Initialize all configured peripherals */ + MX_RF_Init(); MX_GPIO_Init(); MX_DMA_Init(); MX_RTC_Init(); MX_I2C3_Init(); MX_SPI1_Init(); MX_TIM1_Init(); - MX_RF_Init(); /* USER CODE BEGIN 2 */ FS_State_Init(); FS_IMU_Init(); diff --git a/STM32_WPAN/App/custom_app.c b/STM32_WPAN/App/custom_app.c index db63f3a..9e4182b 100644 --- a/STM32_WPAN/App/custom_app.c +++ b/STM32_WPAN/App/custom_app.c @@ -40,6 +40,7 @@ typedef struct uint8_t Crs_tx_Notification_Status; /* GNSS */ uint8_t Gnss_pv_Notification_Status; + uint8_t Gnss_control_Notification_Status; /* Start */ uint8_t Start_control_Indication_Status; uint8_t Start_result_Indication_Status; @@ -108,6 +109,8 @@ static void Custom_Crs_tx_Send_Notification(void); /* GNSS */ static void Custom_Gnss_pv_Update_Char(void); static void Custom_Gnss_pv_Send_Notification(void); +static void Custom_Gnss_control_Update_Char(void); +static void Custom_Gnss_control_Send_Notification(void); /* Start */ static void Custom_Start_control_Update_Char(void); static void Custom_Start_control_Send_Indication(void); @@ -185,6 +188,24 @@ void Custom_STM_App_Notification(Custom_STM_App_Notification_evt_t *pNotificatio /* USER CODE END CUSTOM_STM_GNSS_PV_NOTIFY_DISABLED_EVT */ break; + case CUSTOM_STM_GNSS_CONTROL_WRITE_EVT: + /* USER CODE BEGIN CUSTOM_STM_GNSS_CONTROL_WRITE_EVT */ + + /* USER CODE END CUSTOM_STM_GNSS_CONTROL_WRITE_EVT */ + break; + + case CUSTOM_STM_GNSS_CONTROL_NOTIFY_ENABLED_EVT: + /* USER CODE BEGIN CUSTOM_STM_GNSS_CONTROL_NOTIFY_ENABLED_EVT */ + + /* USER CODE END CUSTOM_STM_GNSS_CONTROL_NOTIFY_ENABLED_EVT */ + break; + + case CUSTOM_STM_GNSS_CONTROL_NOTIFY_DISABLED_EVT: + /* USER CODE BEGIN CUSTOM_STM_GNSS_CONTROL_NOTIFY_DISABLED_EVT */ + + /* USER CODE END CUSTOM_STM_GNSS_CONTROL_NOTIFY_DISABLED_EVT */ + break; + /* Start */ case CUSTOM_STM_START_CONTROL_WRITE_EVT: /* USER CODE BEGIN CUSTOM_STM_START_CONTROL_WRITE_EVT */ @@ -397,6 +418,45 @@ void Custom_Gnss_pv_Send_Notification(void) /* Property Notification */ return; } +void Custom_Gnss_control_Update_Char(void) /* Property Read */ +{ + uint8_t updateflag = 0; + + /* USER CODE BEGIN Gnss_control_UC_1*/ + + /* USER CODE END Gnss_control_UC_1*/ + + if (updateflag != 0) + { + Custom_STM_App_Update_Char(CUSTOM_STM_GNSS_CONTROL, (uint8_t *)UpdateCharData); + } + + /* USER CODE BEGIN Gnss_control_UC_Last*/ + + /* USER CODE END Gnss_control_UC_Last*/ + return; +} + +void Custom_Gnss_control_Send_Notification(void) /* Property Notification */ +{ + uint8_t updateflag = 0; + + /* USER CODE BEGIN Gnss_control_NS_1*/ + + /* USER CODE END Gnss_control_NS_1*/ + + if (updateflag != 0) + { + Custom_STM_App_Update_Char(CUSTOM_STM_GNSS_CONTROL, (uint8_t *)NotifyCharData); + } + + /* USER CODE BEGIN Gnss_control_NS_Last*/ + + /* USER CODE END Gnss_control_NS_Last*/ + + return; +} + /* Start */ void Custom_Start_control_Update_Char(void) /* Property Read */ { diff --git a/STM32_WPAN/App/custom_stm.c b/STM32_WPAN/App/custom_stm.c index 4e7335f..8b10f6b 100644 --- a/STM32_WPAN/App/custom_stm.c +++ b/STM32_WPAN/App/custom_stm.c @@ -33,6 +33,7 @@ typedef struct{ uint16_t CustomCrs_RxHdle; /**< CRS_RX handle */ uint16_t CustomGnssHdle; /**< GNSS handle */ uint16_t CustomGnss_PvHdle; /**< GNSS_PV handle */ + uint16_t CustomGnss_ControlHdle; /**< GNSS_Control handle */ uint16_t CustomStartHdle; /**< Start handle */ uint16_t CustomStart_ControlHdle; /**< Start_Control handle */ uint16_t CustomStart_ResultHdle; /**< Start_Result handle */ @@ -71,7 +72,8 @@ typedef struct{ /* Private variables ---------------------------------------------------------*/ uint8_t SizeCrs_Tx = 244; uint8_t SizeCrs_Rx = 244; -uint8_t SizeGnss_Pv = 29; +uint8_t SizeGnss_Pv = 44; +uint8_t SizeGnss_Control = 2; uint8_t SizeStart_Control = 1; uint8_t SizeStart_Result = 9; @@ -129,6 +131,7 @@ do {\ #define COPY_CRS_RX_UUID(uuid_struct) COPY_UUID_128(uuid_struct,0x00,0x00,0x00,0x02,0x8e,0x22,0x45,0x41,0x9d,0x4c,0x21,0xed,0xae,0x82,0xed,0x19) #define COPY_GNSS_UUID(uuid_struct) COPY_UUID_128(uuid_struct,0x00,0x00,0x00,0x01,0xcc,0x7a,0x48,0x2a,0x98,0x4a,0x7f,0x2e,0xd5,0xb3,0xe5,0x8f) #define COPY_GNSS_PV_UUID(uuid_struct) COPY_UUID_128(uuid_struct,0x00,0x00,0x00,0x00,0x8e,0x22,0x45,0x41,0x9d,0x4c,0x21,0xed,0xae,0x82,0xed,0x19) +#define COPY_GNSS_CONTROL_UUID(uuid_struct) COPY_UUID_128(uuid_struct,0x00,0x00,0x00,0x06,0x8e,0x22,0x45,0x41,0x9d,0x4c,0x21,0xed,0xae,0x82,0xed,0x19) #define COPY_START_UUID(uuid_struct) COPY_UUID_128(uuid_struct,0x00,0x00,0x00,0x02,0xcc,0x7a,0x48,0x2a,0x98,0x4a,0x7f,0x2e,0xd5,0xb3,0xe5,0x8f) #define COPY_START_CONTROL_UUID(uuid_struct) COPY_UUID_128(uuid_struct,0x00,0x00,0x00,0x03,0x8e,0x22,0x45,0x41,0x9d,0x4c,0x21,0xed,0xae,0x82,0xed,0x19) #define COPY_START_RESULT_UUID(uuid_struct) COPY_UUID_128(uuid_struct,0x00,0x00,0x00,0x04,0x8e,0x22,0x45,0x41,0x9d,0x4c,0x21,0xed,0xae,0x82,0xed,0x19) @@ -257,6 +260,50 @@ static SVCCTL_EvtAckStatus_t Custom_STM_Event_Handler(void *Event) } } /* if (attribute_modified->Attr_Handle == (CustomContext.CustomGnss_PvHdle + CHARACTERISTIC_DESCRIPTOR_ATTRIBUTE_OFFSET))*/ + else if (attribute_modified->Attr_Handle == (CustomContext.CustomGnss_ControlHdle + CHARACTERISTIC_DESCRIPTOR_ATTRIBUTE_OFFSET)) + { + return_value = SVCCTL_EvtAckFlowEnable; + /* USER CODE BEGIN CUSTOM_STM_Service_2_Char_2 */ + + /* USER CODE END CUSTOM_STM_Service_2_Char_2 */ + switch (attribute_modified->Attr_Data[0]) + { + /* USER CODE BEGIN CUSTOM_STM_Service_2_Char_2_attribute_modified */ + + /* USER CODE END CUSTOM_STM_Service_2_Char_2_attribute_modified */ + + /* Disabled Notification management */ + case (!(COMSVC_Notification)): + /* USER CODE BEGIN CUSTOM_STM_Service_2_Char_2_Disabled_BEGIN */ + + /* USER CODE END CUSTOM_STM_Service_2_Char_2_Disabled_BEGIN */ + Notification.Custom_Evt_Opcode = CUSTOM_STM_GNSS_CONTROL_NOTIFY_DISABLED_EVT; + Custom_STM_App_Notification(&Notification); + /* USER CODE BEGIN CUSTOM_STM_Service_2_Char_2_Disabled_END */ + + /* USER CODE END CUSTOM_STM_Service_2_Char_2_Disabled_END */ + break; + + /* Enabled Notification management */ + case COMSVC_Notification: + /* USER CODE BEGIN CUSTOM_STM_Service_2_Char_2_COMSVC_Notification_BEGIN */ + + /* USER CODE END CUSTOM_STM_Service_2_Char_2_COMSVC_Notification_BEGIN */ + Notification.Custom_Evt_Opcode = CUSTOM_STM_GNSS_CONTROL_NOTIFY_ENABLED_EVT; + Custom_STM_App_Notification(&Notification); + /* USER CODE BEGIN CUSTOM_STM_Service_2_Char_2_COMSVC_Notification_END */ + + /* USER CODE END CUSTOM_STM_Service_2_Char_2_COMSVC_Notification_END */ + break; + + default: + /* USER CODE BEGIN CUSTOM_STM_Service_2_Char_2_default */ + + /* USER CODE END CUSTOM_STM_Service_2_Char_2_default */ + break; + } + } /* if (attribute_modified->Attr_Handle == (CustomContext.CustomGnss_ControlHdle + CHARACTERISTIC_DESCRIPTOR_ATTRIBUTE_OFFSET))*/ + else if (attribute_modified->Attr_Handle == (CustomContext.CustomStart_ControlHdle + CHARACTERISTIC_DESCRIPTOR_ATTRIBUTE_OFFSET)) { return_value = SVCCTL_EvtAckFlowEnable; @@ -357,6 +404,13 @@ static SVCCTL_EvtAckStatus_t Custom_STM_Event_Handler(void *Event) Custom_STM_App_Notification(&Notification); /* USER CODE END CUSTOM_STM_Service_1_Char_2_ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE */ } /* if (attribute_modified->Attr_Handle == (CustomContext.CustomCrs_RxHdle + CHARACTERISTIC_VALUE_ATTRIBUTE_OFFSET))*/ + else if (attribute_modified->Attr_Handle == (CustomContext.CustomGnss_ControlHdle + CHARACTERISTIC_VALUE_ATTRIBUTE_OFFSET)) + { + return_value = SVCCTL_EvtAckFlowEnable; + /* USER CODE BEGIN CUSTOM_STM_Service_2_Char_2_ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE */ + + /* USER CODE END CUSTOM_STM_Service_2_Char_2_ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE */ + } /* if (attribute_modified->Attr_Handle == (CustomContext.CustomGnss_ControlHdle + CHARACTERISTIC_VALUE_ATTRIBUTE_OFFSET))*/ else if (attribute_modified->Attr_Handle == (CustomContext.CustomStart_ControlHdle + CHARACTERISTIC_VALUE_ATTRIBUTE_OFFSET)) { return_value = SVCCTL_EvtAckFlowEnable; @@ -576,16 +630,18 @@ void SVCCTL_InitCustomSvc(void) /** * GNSS * - * Max_Attribute_Records = 1 + 2*1 + 1*no_of_char_with_notify_or_indicate_property + 1*no_of_char_with_broadcast_property + * Max_Attribute_Records = 1 + 2*2 + 1*no_of_char_with_notify_or_indicate_property + 1*no_of_char_with_broadcast_property * service_max_attribute_record = 1 for GNSS + * 2 for GNSS_PV + + * 2 for GNSS_Control + * 1 for GNSS_PV configuration descriptor + - * = 4 + * 1 for GNSS_Control configuration descriptor + + * = 7 * * This value doesn't take into account number of descriptors manually added * In case of descriptors added, please update the max_attr_record value accordingly in the next SVCCTL_InitService User Section */ - max_attr_record = 4; + max_attr_record = 7; /* USER CODE BEGIN SVCCTL_InitService */ /* max_attr_record to be updated if descriptors have been added */ @@ -618,7 +674,7 @@ void SVCCTL_InitCustomSvc(void) ATTR_PERMISSION_ENCRY_READ | ATTR_PERMISSION_ENCRY_WRITE, GATT_NOTIFY_READ_REQ_AND_WAIT_FOR_APPL_RESP, 0x10, - CHAR_VALUE_LEN_CONSTANT, + CHAR_VALUE_LEN_VARIABLE, &(CustomContext.CustomGnss_PvHdle)); if (ret != BLE_STATUS_SUCCESS) { @@ -633,6 +689,32 @@ void SVCCTL_InitCustomSvc(void) /* Place holder for Characteristic Descriptors */ /* USER CODE END SVCCTL_Init_Service2_Char1 */ + /** + * GNSS_Control + */ + COPY_GNSS_CONTROL_UUID(uuid.Char_UUID_128); + ret = aci_gatt_add_char(CustomContext.CustomGnssHdle, + UUID_TYPE_128, &uuid, + SizeGnss_Control, + CHAR_PROP_WRITE | CHAR_PROP_NOTIFY, + ATTR_PERMISSION_ENCRY_READ | ATTR_PERMISSION_ENCRY_WRITE, + GATT_NOTIFY_ATTRIBUTE_WRITE, + 0x10, + CHAR_VALUE_LEN_VARIABLE, + &(CustomContext.CustomGnss_ControlHdle)); + if (ret != BLE_STATUS_SUCCESS) + { + APP_DBG_MSG(" Fail : aci_gatt_add_char command : GNSS_CONTROL, error code: 0x%x \n\r", ret); + } + else + { + APP_DBG_MSG(" Success: aci_gatt_add_char command : GNSS_CONTROL \n\r"); + } + + /* USER CODE BEGIN SVCCTL_Init_Service2_Char2/ */ + /* Place holder for Characteristic Descriptors */ + + /* USER CODE END SVCCTL_Init_Service2_Char2 */ /** * Start @@ -803,6 +885,25 @@ tBleStatus Custom_STM_App_Update_Char(Custom_STM_Char_Opcode_t CharOpcode, uint8 /* USER CODE END CUSTOM_STM_App_Update_Service_2_Char_1*/ break; + case CUSTOM_STM_GNSS_CONTROL: + ret = aci_gatt_update_char_value(CustomContext.CustomGnssHdle, + CustomContext.CustomGnss_ControlHdle, + 0, /* charValOffset */ + SizeGnss_Control, /* charValueLen */ + (uint8_t *) pPayload); + if (ret != BLE_STATUS_SUCCESS) + { + APP_DBG_MSG(" Fail : aci_gatt_update_char_value GNSS_CONTROL command, result : 0x%x \n\r", ret); + } + else + { + APP_DBG_MSG(" Success: aci_gatt_update_char_value GNSS_CONTROL command\n\r"); + } + /* USER CODE BEGIN CUSTOM_STM_App_Update_Service_2_Char_2*/ + + /* USER CODE END CUSTOM_STM_App_Update_Service_2_Char_2*/ + break; + case CUSTOM_STM_START_CONTROL: ret = aci_gatt_update_char_value(CustomContext.CustomStartHdle, CustomContext.CustomStart_ControlHdle, diff --git a/STM32_WPAN/App/custom_stm.h b/STM32_WPAN/App/custom_stm.h index bc0db0f..28a3353 100644 --- a/STM32_WPAN/App/custom_stm.h +++ b/STM32_WPAN/App/custom_stm.h @@ -39,6 +39,7 @@ typedef enum CUSTOM_STM_CRS_RX, /* GNSS */ CUSTOM_STM_GNSS_PV, + CUSTOM_STM_GNSS_CONTROL, /* Start */ CUSTOM_STM_START_CONTROL, CUSTOM_STM_START_RESULT, @@ -56,6 +57,10 @@ typedef enum CUSTOM_STM_GNSS_PV_READ_EVT, CUSTOM_STM_GNSS_PV_NOTIFY_ENABLED_EVT, CUSTOM_STM_GNSS_PV_NOTIFY_DISABLED_EVT, + /* GNSS_Control */ + CUSTOM_STM_GNSS_CONTROL_WRITE_EVT, + CUSTOM_STM_GNSS_CONTROL_NOTIFY_ENABLED_EVT, + CUSTOM_STM_GNSS_CONTROL_NOTIFY_DISABLED_EVT, /* Start_Control */ CUSTOM_STM_START_CONTROL_WRITE_EVT, CUSTOM_STM_START_CONTROL_INDICATE_ENABLED_EVT, @@ -92,6 +97,7 @@ typedef struct extern uint8_t SizeCrs_Tx; extern uint8_t SizeCrs_Rx; extern uint8_t SizeGnss_Pv; +extern uint8_t SizeGnss_Control; extern uint8_t SizeStart_Control; extern uint8_t SizeStart_Result; diff --git a/flysight-2-firmware.ioc b/flysight-2-firmware.ioc index 00c7038..edb1802 100644 --- a/flysight-2-firmware.ioc +++ b/flysight-2-firmware.ioc @@ -688,7 +688,7 @@ STM32_WPAN.CUSTOM_TEMPLATE=Enabled STM32_WPAN.GAP_PERIPHERAL_ROLE=1 STM32_WPAN.INCLUDE_AD_TYPE_COMPLETE_LOCAL_NAME=1 STM32_WPAN.INCLUDE_AD_TYPE_MANUFACTURER_SPECIFIC_DATA=1 -STM32_WPAN.IPParameters=CUSTOM_P2P_SERVER,CUSTOM_TEMPLATE,CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER,CFG_HW_LPUART1_ENABLED,CFG_USE_SMPS,CFG_LPM_SUPPORTED,CFG_DEBUG_TRACE_UART,CFG_IO_CAPABILITY,CFG_MITM_PROTECTION,CFG_BLE_NUM_LINK,CFG_BLE_MAX_ATT_MTU,CFG_IDENTITY_ADDRESS,CFG_STATIC_RANDOM_ADDRESS,CFG_PRIVACY,GAP_PERIPHERAL_ROLE,CFG_GAP_DEVICE_NAME,CFG_GAP_DEVICE_NAME_LENGTH,INCLUDE_AD_TYPE_COMPLETE_LOCAL_NAME,AD_TYPE_COMPLETE_LOCAL_NAME,INCLUDE_AD_TYPE_MANUFACTURER_SPECIFIC_DATA,AD_TYPE_MANUFACTURER_SPECIFIC_DATA_COMPANY_IDENTIFIER,AD_TYPE_MANUFACTURER_DATA_COMMENT_1,PAIRING_PARAMETERS,CFG_BONDING_MODE,NUMBER_OF_SERVICES,SERVICE1_LONG_NAME,SERVICE1_SHORT_NAME,SERVICE2_LONG_NAME,SERVICE2_SHORT_NAME,SERVICE1_NUMBER_OF_CHARACTERISTICS,SERVICE1_CHAR1_LONG_NAME,SERVICE1_CHAR1_SHORT_NAME,SERVICE1_CHAR2_LONG_NAME,SERVICE1_CHAR2_SHORT_NAME,SERVICE1_CHAR1_VALUE_LENGTH,SERVICE1_CHAR1_UUID,SERVICE1_CHAR1_LENGTH_CHARACTERISTIC,SERVICE1_CHAR1_PROP_NOTIFY,SERVICE1_CHAR1_ATTR_PERMISSION_ENCRY_READ,SERVICE1_CHAR1_GATT_NOTIFY_ATTRIBUTE_WRITE,SERVICE1_CHAR1_GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP,SERVICE1_CHAR1_GATT_NOTIFY_READ_REQ_AND_WAIT_FOR_APPL_RESP,SERVICE1_CHAR2_UUID,SERVICE1_CHAR2_VALUE_LENGTH,SERVICE1_CHAR2_LENGTH_CHARACTERISTIC,SERVICE1_CHAR2_PROP_READ,SERVICE1_CHAR2_PROP_WRITE_WITHOUT_RESP,SERVICE1_CHAR2_ATTR_PERMISSION_ENCRY_READ,SERVICE1_CHAR2_ATTR_PERMISSION_ENCRY_WRITE,SERVICE1_CHAR2_GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP,SERVICE1_CHAR2_GATT_NOTIFY_READ_REQ_AND_WAIT_FOR_APPL_RESP,SERVICE2_CHAR1_LONG_NAME,SERVICE2_CHAR1_SHORT_NAME,SERVICE2_CHAR1_VALUE_LENGTH,SERVICE2_CHAR1_PROP_READ,SERVICE2_CHAR1_PROP_NOTIFY,SERVICE2_CHAR1_ATTR_PERMISSION_ENCRY_READ,SERVICE2_CHAR1_GATT_NOTIFY_ATTRIBUTE_WRITE,SERVICE2_CHAR1_GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP,SERVICE2_CHAR1_GATT_NOTIFY_READ_REQ_AND_WAIT_FOR_APPL_RESP,CFG_BLE_OPTIONS_DEVICE_NAME,CFG_DEBUGGER_SUPPORTED,SERVICE3_LONG_NAME,SERVICE3_SHORT_NAME,SERVICE3_CHAR1_LONG_NAME,SERVICE3_CHAR1_SHORT_NAME,SERVICE3_CHAR1_PROP_WRITE,SERVICE3_CHAR1_PROP_INDICATE,SERVICE3_CHAR1_ATTR_PERMISSION_ENCRY_READ,SERVICE3_CHAR1_ATTR_PERMISSION_ENCRY_WRITE,SERVICE3_CHAR1_GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP,SERVICE3_CHAR1_GATT_NOTIFY_READ_REQ_AND_WAIT_FOR_APPL_RESP,SERVICE2_UUID,SERVICE3_UUID,SERVICE3_CHAR1_UUID,SERVICE3_NUMBER_OF_CHARACTERISTICS,SERVICE3_CHAR2_LONG_NAME,SERVICE3_CHAR2_SHORT_NAME,SERVICE3_CHAR2_UUID,SERVICE3_CHAR2_VALUE_LENGTH,SERVICE3_CHAR2_PROP_READ,SERVICE3_CHAR2_PROP_INDICATE,SERVICE3_CHAR2_ATTR_PERMISSION_ENCRY_READ,SERVICE3_CHAR2_GATT_NOTIFY_ATTRIBUTE_WRITE,SERVICE3_CHAR2_GATT_NOTIFY_READ_REQ_AND_WAIT_FOR_APPL_RESP,SERVICE3_CHAR2_GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP,CFG_RTCCLK_DIVIDER_CONF,SERVICE1_CHAR1_ATTR_PERMISSION_ENCRY_WRITE,SERVICE2_CHAR1_ATTR_PERMISSION_ENCRY_WRITE,SERVICE3_CHAR2_ATTR_PERMISSION_ENCRY_WRITE +STM32_WPAN.IPParameters=CUSTOM_P2P_SERVER,CUSTOM_TEMPLATE,CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER,CFG_HW_LPUART1_ENABLED,CFG_USE_SMPS,CFG_LPM_SUPPORTED,CFG_DEBUG_TRACE_UART,CFG_IO_CAPABILITY,CFG_MITM_PROTECTION,CFG_BLE_NUM_LINK,CFG_BLE_MAX_ATT_MTU,CFG_IDENTITY_ADDRESS,CFG_STATIC_RANDOM_ADDRESS,CFG_PRIVACY,GAP_PERIPHERAL_ROLE,CFG_GAP_DEVICE_NAME,CFG_GAP_DEVICE_NAME_LENGTH,INCLUDE_AD_TYPE_COMPLETE_LOCAL_NAME,AD_TYPE_COMPLETE_LOCAL_NAME,INCLUDE_AD_TYPE_MANUFACTURER_SPECIFIC_DATA,AD_TYPE_MANUFACTURER_SPECIFIC_DATA_COMPANY_IDENTIFIER,AD_TYPE_MANUFACTURER_DATA_COMMENT_1,PAIRING_PARAMETERS,CFG_BONDING_MODE,NUMBER_OF_SERVICES,SERVICE1_LONG_NAME,SERVICE1_SHORT_NAME,SERVICE2_LONG_NAME,SERVICE2_SHORT_NAME,SERVICE1_NUMBER_OF_CHARACTERISTICS,SERVICE1_CHAR1_LONG_NAME,SERVICE1_CHAR1_SHORT_NAME,SERVICE1_CHAR2_LONG_NAME,SERVICE1_CHAR2_SHORT_NAME,SERVICE1_CHAR1_VALUE_LENGTH,SERVICE1_CHAR1_UUID,SERVICE1_CHAR1_LENGTH_CHARACTERISTIC,SERVICE1_CHAR1_PROP_NOTIFY,SERVICE1_CHAR1_ATTR_PERMISSION_ENCRY_READ,SERVICE1_CHAR1_GATT_NOTIFY_ATTRIBUTE_WRITE,SERVICE1_CHAR1_GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP,SERVICE1_CHAR1_GATT_NOTIFY_READ_REQ_AND_WAIT_FOR_APPL_RESP,SERVICE1_CHAR2_UUID,SERVICE1_CHAR2_VALUE_LENGTH,SERVICE1_CHAR2_LENGTH_CHARACTERISTIC,SERVICE1_CHAR2_PROP_READ,SERVICE1_CHAR2_PROP_WRITE_WITHOUT_RESP,SERVICE1_CHAR2_ATTR_PERMISSION_ENCRY_READ,SERVICE1_CHAR2_ATTR_PERMISSION_ENCRY_WRITE,SERVICE1_CHAR2_GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP,SERVICE1_CHAR2_GATT_NOTIFY_READ_REQ_AND_WAIT_FOR_APPL_RESP,SERVICE2_CHAR1_LONG_NAME,SERVICE2_CHAR1_SHORT_NAME,SERVICE2_CHAR1_VALUE_LENGTH,SERVICE2_CHAR1_PROP_READ,SERVICE2_CHAR1_PROP_NOTIFY,SERVICE2_CHAR1_ATTR_PERMISSION_ENCRY_READ,SERVICE2_CHAR1_GATT_NOTIFY_ATTRIBUTE_WRITE,SERVICE2_CHAR1_GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP,SERVICE2_CHAR1_GATT_NOTIFY_READ_REQ_AND_WAIT_FOR_APPL_RESP,CFG_BLE_OPTIONS_DEVICE_NAME,CFG_DEBUGGER_SUPPORTED,SERVICE3_LONG_NAME,SERVICE3_SHORT_NAME,SERVICE3_CHAR1_LONG_NAME,SERVICE3_CHAR1_SHORT_NAME,SERVICE3_CHAR1_PROP_WRITE,SERVICE3_CHAR1_PROP_INDICATE,SERVICE3_CHAR1_ATTR_PERMISSION_ENCRY_READ,SERVICE3_CHAR1_ATTR_PERMISSION_ENCRY_WRITE,SERVICE3_CHAR1_GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP,SERVICE3_CHAR1_GATT_NOTIFY_READ_REQ_AND_WAIT_FOR_APPL_RESP,SERVICE2_UUID,SERVICE3_UUID,SERVICE3_CHAR1_UUID,SERVICE3_NUMBER_OF_CHARACTERISTICS,SERVICE3_CHAR2_LONG_NAME,SERVICE3_CHAR2_SHORT_NAME,SERVICE3_CHAR2_UUID,SERVICE3_CHAR2_VALUE_LENGTH,SERVICE3_CHAR2_PROP_READ,SERVICE3_CHAR2_PROP_INDICATE,SERVICE3_CHAR2_ATTR_PERMISSION_ENCRY_READ,SERVICE3_CHAR2_GATT_NOTIFY_ATTRIBUTE_WRITE,SERVICE3_CHAR2_GATT_NOTIFY_READ_REQ_AND_WAIT_FOR_APPL_RESP,SERVICE3_CHAR2_GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP,CFG_RTCCLK_DIVIDER_CONF,SERVICE1_CHAR1_ATTR_PERMISSION_ENCRY_WRITE,SERVICE2_CHAR1_ATTR_PERMISSION_ENCRY_WRITE,SERVICE3_CHAR2_ATTR_PERMISSION_ENCRY_WRITE,SERVICE2_CHAR1_LENGTH_CHARACTERISTIC,SERVICE2_NUMBER_OF_CHARACTERISTICS,SERVICE2_CHAR2_LONG_NAME,SERVICE2_CHAR2_SHORT_NAME,SERVICE2_CHAR2_VALUE_LENGTH,SERVICE2_CHAR2_LENGTH_CHARACTERISTIC,SERVICE2_CHAR2_PROP_WRITE,SERVICE2_CHAR2_PROP_NOTIFY,SERVICE1_UUID,SERVICE2_CHAR2_UUID,SERVICE2_CHAR2_ATTR_PERMISSION_ENCRY_READ,SERVICE2_CHAR2_ATTR_PERMISSION_ENCRY_WRITE,SERVICE2_CHAR2_GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP,SERVICE2_CHAR2_GATT_NOTIFY_READ_REQ_AND_WAIT_FOR_APPL_RESP STM32_WPAN.NUMBER_OF_SERVICES=3 STM32_WPAN.PAIRING_PARAMETERS=ON STM32_WPAN.SERVICE1_CHAR1_ATTR_PERMISSION_ENCRY_READ=ATTR_PERMISSION_ENCRY_READ @@ -716,17 +716,31 @@ STM32_WPAN.SERVICE1_CHAR2_VALUE_LENGTH=244 STM32_WPAN.SERVICE1_LONG_NAME=CRS STM32_WPAN.SERVICE1_NUMBER_OF_CHARACTERISTICS=2 STM32_WPAN.SERVICE1_SHORT_NAME=CRS +STM32_WPAN.SERVICE1_UUID=00 00 STM32_WPAN.SERVICE2_CHAR1_ATTR_PERMISSION_ENCRY_READ=ATTR_PERMISSION_ENCRY_READ STM32_WPAN.SERVICE2_CHAR1_ATTR_PERMISSION_ENCRY_WRITE=ATTR_PERMISSION_ENCRY_WRITE STM32_WPAN.SERVICE2_CHAR1_GATT_NOTIFY_ATTRIBUTE_WRITE=\ STM32_WPAN.SERVICE2_CHAR1_GATT_NOTIFY_READ_REQ_AND_WAIT_FOR_APPL_RESP=GATT_NOTIFY_READ_REQ_AND_WAIT_FOR_APPL_RESP STM32_WPAN.SERVICE2_CHAR1_GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP=\ +STM32_WPAN.SERVICE2_CHAR1_LENGTH_CHARACTERISTIC=CHAR_VALUE_LEN_VARIABLE STM32_WPAN.SERVICE2_CHAR1_LONG_NAME=GNSS_PV STM32_WPAN.SERVICE2_CHAR1_PROP_NOTIFY=CHAR_PROP_NOTIFY STM32_WPAN.SERVICE2_CHAR1_PROP_READ=CHAR_PROP_READ STM32_WPAN.SERVICE2_CHAR1_SHORT_NAME=GNSS_PV -STM32_WPAN.SERVICE2_CHAR1_VALUE_LENGTH=29 +STM32_WPAN.SERVICE2_CHAR1_VALUE_LENGTH=44 +STM32_WPAN.SERVICE2_CHAR2_ATTR_PERMISSION_ENCRY_READ=ATTR_PERMISSION_ENCRY_READ +STM32_WPAN.SERVICE2_CHAR2_ATTR_PERMISSION_ENCRY_WRITE=ATTR_PERMISSION_ENCRY_WRITE +STM32_WPAN.SERVICE2_CHAR2_GATT_NOTIFY_READ_REQ_AND_WAIT_FOR_APPL_RESP=\ +STM32_WPAN.SERVICE2_CHAR2_GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP=\ +STM32_WPAN.SERVICE2_CHAR2_LENGTH_CHARACTERISTIC=CHAR_VALUE_LEN_VARIABLE +STM32_WPAN.SERVICE2_CHAR2_LONG_NAME=GNSS_Control +STM32_WPAN.SERVICE2_CHAR2_PROP_NOTIFY=CHAR_PROP_NOTIFY +STM32_WPAN.SERVICE2_CHAR2_PROP_WRITE=CHAR_PROP_WRITE +STM32_WPAN.SERVICE2_CHAR2_SHORT_NAME=GNSS_Control +STM32_WPAN.SERVICE2_CHAR2_UUID=00 06 +STM32_WPAN.SERVICE2_CHAR2_VALUE_LENGTH=2 STM32_WPAN.SERVICE2_LONG_NAME=GNSS +STM32_WPAN.SERVICE2_NUMBER_OF_CHARACTERISTICS=2 STM32_WPAN.SERVICE2_SHORT_NAME=GNSS STM32_WPAN.SERVICE2_UUID=00 01 STM32_WPAN.SERVICE3_CHAR1_ATTR_PERMISSION_ENCRY_READ=ATTR_PERMISSION_ENCRY_READ From 24e356eba6046bf5466fefcfb7acc4437085d7f5 Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Fri, 9 May 2025 09:11:06 -0600 Subject: [PATCH 02/81] Add control characteristic for live data --- FlySight/gnss_ble.c | 107 ++++++++++++++++++++++++++++++++++++ FlySight/gnss_ble.h | 74 +++++++++++++++++++++++++ STM32_WPAN/App/custom_app.c | 31 +++++++---- 3 files changed, 201 insertions(+), 11 deletions(-) create mode 100644 FlySight/gnss_ble.c create mode 100644 FlySight/gnss_ble.h diff --git a/FlySight/gnss_ble.c b/FlySight/gnss_ble.c new file mode 100644 index 0000000..aa41f04 --- /dev/null +++ b/FlySight/gnss_ble.c @@ -0,0 +1,107 @@ +/*************************************************************************** +** ** +** FlySight 2 firmware ** +** Copyright 2025 Bionic Avionics Inc. ** +** ** +** 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 . ** +** ** +**************************************************************************** +** Contact: Bionic Avionics Inc. ** +** Website: http://flysight.ca/ ** +****************************************************************************/ + +#include "gnss_ble.h" + +static uint8_t s_mask = 0xB0u; + +void GNSS_BLE_Init(void) +{ + s_mask = 0xB0u; +} + +uint8_t GNSS_BLE_GetMask(void) +{ + return s_mask; +} + +void GNSS_BLE_SetMask(uint8_t mask) +{ + s_mask = mask; +} + +uint8_t GNSS_BLE_Build(const FS_GNSS_Data_t *src, uint8_t *dst) +{ + uint8_t *p = dst; + + *p++ = s_mask; /* byte 0 : mask */ + + if (s_mask & GNSS_BLE_BIT_TOW) { /* time of week */ + memcpy(p, &src->iTOW, sizeof(src->iTOW)); p += 4; + } + + if (s_mask & GNSS_BLE_BIT_WEEK) { /* week number */ + // Not yet implemented + } + + if (s_mask & GNSS_BLE_BIT_POSITION) { /* position */ + memcpy(p, &src->lon, sizeof(src->lon)); p += 4; + memcpy(p, &src->lat, sizeof(src->lat)); p += 4; + memcpy(p, &src->hMSL, sizeof(src->hMSL)); p += 4; + } + + if (s_mask & GNSS_BLE_BIT_VELOCITY) { /* velocity */ + memcpy(p, &src->velN, sizeof(src->velN)); p += 4; + memcpy(p, &src->velE, sizeof(src->velE)); p += 4; + memcpy(p, &src->velD, sizeof(src->velD)); p += 4; + } + + if (s_mask & GNSS_BLE_BIT_ACCURACY) { /* accuracy */ + memcpy(p, &src->velN, sizeof(src->hAcc)); p += 4; + memcpy(p, &src->velE, sizeof(src->vAcc)); p += 4; + memcpy(p, &src->velD, sizeof(src->sAcc)); p += 4; + } + + if (s_mask & GNSS_BLE_BIT_NUM_SV) { /* number of satellites */ + memcpy(p, &src->velN, sizeof(src->numSV)); p += 1; + } + + return (uint8_t)(p - dst); /* total payload length */ +} + +uint8_t GNSS_BLE_HandleCtrlWrite(const uint8_t *buf, uint8_t len, uint8_t *rsp) +{ + if (len == 0u) return 0u; + + const uint8_t op = buf[0]; + + switch (op) + { + case GNSS_BLE_OP_SET_MASK: + if (len != 2u) { + rsp[0] = op; rsp[1] = GNSS_BLE_STATUS_BAD_LENGTH; + return 2u; + } + GNSS_BLE_SetMask(buf[1]); + rsp[0] = op; rsp[1] = GNSS_BLE_STATUS_OK; + return 2u; + + case GNSS_BLE_OP_GET_MASK: + rsp[0] = op; rsp[1] = GNSS_BLE_GetMask(); + return 2u; + + default: + rsp[0] = op; rsp[1] = GNSS_BLE_STATUS_BAD_OPCODE; + return 2u; + } +} diff --git a/FlySight/gnss_ble.h b/FlySight/gnss_ble.h new file mode 100644 index 0000000..57342fc --- /dev/null +++ b/FlySight/gnss_ble.h @@ -0,0 +1,74 @@ +/*************************************************************************** +** ** +** FlySight 2 firmware ** +** Copyright 2025 Bionic Avionics Inc. ** +** ** +** 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 . ** +** ** +**************************************************************************** +** Contact: Bionic Avionics Inc. ** +** Website: http://flysight.ca/ ** +****************************************************************************/ + +#ifndef GNSS_BLE_H_ +#define GNSS_BLE_H_ + +#include +#include +#include +#include "gnss.h" /* FS_GNSS_Data_t */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------------------------------------------------------ */ +/* Packet layout control */ +/* ------------------------------------------------------------------ */ +#define GNSS_BLE_MAX_LEN 44u + +/* Bit-layout of mask byte (MSB first) */ +#define GNSS_BLE_BIT_TOW 0x80u +#define GNSS_BLE_BIT_WEEK 0x40u +#define GNSS_BLE_BIT_POSITION 0x20u +#define GNSS_BLE_BIT_VELOCITY 0x10u +#define GNSS_BLE_BIT_ACCURACY 0x08u +#define GNSS_BLE_BIT_NUM_SV 0x04u + +/* ------------------------------------------------------------------ */ +/* Control-point opcodes / status */ +/* ------------------------------------------------------------------ */ +#define GNSS_BLE_OP_SET_MASK 0x01u +#define GNSS_BLE_OP_GET_MASK 0x02u + +#define GNSS_BLE_STATUS_OK 0x00u +#define GNSS_BLE_STATUS_BAD_LENGTH 0x01u +#define GNSS_BLE_STATUS_BAD_OPCODE 0x02u + +/* ------------------------------------------------------------------ */ +/* Public API */ +/* ------------------------------------------------------------------ */ +void GNSS_BLE_Init(void); +uint8_t GNSS_BLE_GetMask(void); +void GNSS_BLE_SetMask(uint8_t mask); + +uint8_t GNSS_BLE_Build(const FS_GNSS_Data_t *src, uint8_t *dst); + +uint8_t GNSS_BLE_HandleCtrlWrite(const uint8_t *buf, uint8_t len, uint8_t *rsp); + +#ifdef __cplusplus +} +#endif + +#endif /* GNSS_BLE_H_ */ diff --git a/STM32_WPAN/App/custom_app.c b/STM32_WPAN/App/custom_app.c index 9e4182b..4508672 100644 --- a/STM32_WPAN/App/custom_app.c +++ b/STM32_WPAN/App/custom_app.c @@ -31,6 +31,7 @@ /* USER CODE BEGIN Includes */ #include "crs.h" #include "start_control.h" +#include "gnss_ble.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ @@ -87,7 +88,8 @@ static uint32_t tx_read_index, tx_write_index; static Custom_CRS_Packet_t rx_buffer[FS_CRS_WINDOW_LENGTH+1]; static uint32_t rx_read_index, rx_write_index; -static uint8_t gnss_pv_packet[29]; +static uint8_t gnss_pv_packet[GNSS_BLE_MAX_LEN]; +static uint8_t gnss_control_rsp[4]; static Custom_Start_Packet_t start_buffer[FS_START_WINDOW_LENGTH+1]; static uint32_t start_read_index, start_write_index; @@ -99,6 +101,9 @@ static uint8_t connected_flag = 0; extern uint8_t SizeCrs_Tx; extern uint8_t SizeCrs_Rx; +extern uint8_t SizeGnss_Pv; +extern uint8_t SizeGnss_Control; + static uint8_t timeout_timer_id; /* USER CODE END PV */ @@ -190,9 +195,20 @@ void Custom_STM_App_Notification(Custom_STM_App_Notification_evt_t *pNotificatio case CUSTOM_STM_GNSS_CONTROL_WRITE_EVT: /* USER CODE BEGIN CUSTOM_STM_GNSS_CONTROL_WRITE_EVT */ - + { + uint8_t rsp_len = GNSS_BLE_HandleCtrlWrite( + pNotification->DataTransfered.pPayload, + pNotification->DataTransfered.Length, + gnss_control_rsp); + + if (rsp_len && Custom_App_Context.Gnss_control_Notification_Status) + { + SizeGnss_Control = rsp_len; + Custom_STM_App_Update_Char(CUSTOM_STM_GNSS_CONTROL, gnss_control_rsp); + } + } /* USER CODE END CUSTOM_STM_GNSS_CONTROL_WRITE_EVT */ - break; + break; case CUSTOM_STM_GNSS_CONTROL_NOTIFY_ENABLED_EVT: /* USER CODE BEGIN CUSTOM_STM_GNSS_CONTROL_NOTIFY_ENABLED_EVT */ @@ -671,14 +687,7 @@ static void Custom_GNSS_Transmit(void) void Custom_GNSS_Update(const FS_GNSS_Data_t *current) { - memset(&gnss_pv_packet[0], 0xb0, 1); - memcpy(&gnss_pv_packet[1], &(current->iTOW), sizeof(current->iTOW)); - memcpy(&gnss_pv_packet[5], &(current->lon), sizeof(current->lon)); - memcpy(&gnss_pv_packet[9], &(current->lat), sizeof(current->lat)); - memcpy(&gnss_pv_packet[13], &(current->hMSL), sizeof(current->hMSL)); - memcpy(&gnss_pv_packet[17], &(current->velN), sizeof(current->velN)); - memcpy(&gnss_pv_packet[21], &(current->velE), sizeof(current->velE)); - memcpy(&gnss_pv_packet[25], &(current->velD), sizeof(current->velD)); + SizeGnss_Pv = GNSS_BLE_Build(current, gnss_pv_packet); if (Custom_App_Context.Gnss_pv_Notification_Status) { From f85bcb2dc937706f6e26c60802f905f21a2e5715 Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Tue, 13 May 2025 19:05:17 -0600 Subject: [PATCH 03/81] Fix issue building live GNSS data structure --- FlySight/gnss_ble.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/FlySight/gnss_ble.c b/FlySight/gnss_ble.c index aa41f04..c75fecf 100644 --- a/FlySight/gnss_ble.c +++ b/FlySight/gnss_ble.c @@ -67,13 +67,13 @@ uint8_t GNSS_BLE_Build(const FS_GNSS_Data_t *src, uint8_t *dst) } if (s_mask & GNSS_BLE_BIT_ACCURACY) { /* accuracy */ - memcpy(p, &src->velN, sizeof(src->hAcc)); p += 4; - memcpy(p, &src->velE, sizeof(src->vAcc)); p += 4; - memcpy(p, &src->velD, sizeof(src->sAcc)); p += 4; + memcpy(p, &src->hAcc, sizeof(src->hAcc)); p += 4; + memcpy(p, &src->vAcc, sizeof(src->vAcc)); p += 4; + memcpy(p, &src->sAcc, sizeof(src->sAcc)); p += 4; } if (s_mask & GNSS_BLE_BIT_NUM_SV) { /* number of satellites */ - memcpy(p, &src->velN, sizeof(src->numSV)); p += 1; + memcpy(p, &src->numSV, sizeof(src->numSV)); p += 1; } return (uint8_t)(p - dst); /* total payload length */ From 76a0f3ce4d2c7fe8c460550696d587e9773978ea Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Thu, 15 May 2025 22:30:49 -0600 Subject: [PATCH 04/81] Fix issues with GNSS control characteristic --- STM32_WPAN/App/custom_app.c | 4 ++-- STM32_WPAN/App/custom_stm.c | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/STM32_WPAN/App/custom_app.c b/STM32_WPAN/App/custom_app.c index 4508672..0b1eb7f 100644 --- a/STM32_WPAN/App/custom_app.c +++ b/STM32_WPAN/App/custom_app.c @@ -212,13 +212,13 @@ void Custom_STM_App_Notification(Custom_STM_App_Notification_evt_t *pNotificatio case CUSTOM_STM_GNSS_CONTROL_NOTIFY_ENABLED_EVT: /* USER CODE BEGIN CUSTOM_STM_GNSS_CONTROL_NOTIFY_ENABLED_EVT */ - + Custom_App_Context.Gnss_control_Notification_Status = 1; /* USER CODE END CUSTOM_STM_GNSS_CONTROL_NOTIFY_ENABLED_EVT */ break; case CUSTOM_STM_GNSS_CONTROL_NOTIFY_DISABLED_EVT: /* USER CODE BEGIN CUSTOM_STM_GNSS_CONTROL_NOTIFY_DISABLED_EVT */ - + Custom_App_Context.Gnss_control_Notification_Status = 0; /* USER CODE END CUSTOM_STM_GNSS_CONTROL_NOTIFY_DISABLED_EVT */ break; diff --git a/STM32_WPAN/App/custom_stm.c b/STM32_WPAN/App/custom_stm.c index 8b10f6b..a933bec 100644 --- a/STM32_WPAN/App/custom_stm.c +++ b/STM32_WPAN/App/custom_stm.c @@ -408,7 +408,10 @@ static SVCCTL_EvtAckStatus_t Custom_STM_Event_Handler(void *Event) { return_value = SVCCTL_EvtAckFlowEnable; /* USER CODE BEGIN CUSTOM_STM_Service_2_Char_2_ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE */ - + Notification.Custom_Evt_Opcode = CUSTOM_STM_GNSS_CONTROL_WRITE_EVT; + Notification.DataTransfered.Length = attribute_modified->Attr_Data_Length; + Notification.DataTransfered.pPayload = attribute_modified->Attr_Data; + Custom_STM_App_Notification(&Notification); /* USER CODE END CUSTOM_STM_Service_2_Char_2_ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE */ } /* if (attribute_modified->Attr_Handle == (CustomContext.CustomGnss_ControlHdle + CHARACTERISTIC_VALUE_ATTRIBUTE_OFFSET))*/ else if (attribute_modified->Attr_Handle == (CustomContext.CustomStart_ControlHdle + CHARACTERISTIC_VALUE_ATTRIBUTE_OFFSET)) From 30e810d2f7419ba17b4c284de094eac0153b8d13 Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Thu, 15 May 2025 23:25:19 -0600 Subject: [PATCH 05/81] Factor out CRS TX queue --- Core/Inc/app_conf.h | 2 +- FlySight/crs.c | 14 +++-- STM32_WPAN/App/crs_tx_queue.c | 112 ++++++++++++++++++++++++++++++++++ STM32_WPAN/App/crs_tx_queue.h | 39 ++++++++++++ STM32_WPAN/App/custom_app.c | 78 ++--------------------- STM32_WPAN/App/custom_app.h | 2 - 6 files changed, 166 insertions(+), 81 deletions(-) create mode 100644 STM32_WPAN/App/crs_tx_queue.c create mode 100644 STM32_WPAN/App/crs_tx_queue.h diff --git a/Core/Inc/app_conf.h b/Core/Inc/app_conf.h index e6eeb4c..f042d49 100644 --- a/Core/Inc/app_conf.h +++ b/Core/Inc/app_conf.h @@ -694,7 +694,7 @@ typedef enum CFG_TASK_FS_AUDIO_CONTROL_PRODUCER_ID, CFG_TASK_FS_AUDIO_CONTROL_CONSUMER_ID, CFG_TASK_FS_CONFIG_UPDATE_ID, - CFG_TASK_CUSTOM_CRS_TRANSMIT_ID, + CFG_TASK_CRS_TX_QUEUE_TRANSMIT_ID, CFG_TASK_CUSTOM_GNSS_TRANSMIT_ID, CFG_TASK_FS_CRS_UPDATE_ID, CFG_TASK_FS_START_UPDATE_ID, diff --git a/FlySight/crs.c b/FlySight/crs.c index 5c54c64..3f9391b 100644 --- a/FlySight/crs.c +++ b/FlySight/crs.c @@ -25,6 +25,7 @@ #include "app_common.h" #include "crs.h" #include "custom_app.h" +#include "crs_tx_queue.h" #include "dbg_trace.h" #include "ff.h" #include "resource_manager.h" @@ -105,14 +106,14 @@ static volatile uint8_t timeout_flag; static void FS_CRS_SendPacket(uint8_t command, uint8_t *payload, uint8_t length) { - Custom_CRS_Packet_t *tx_packet; + CRS_TX_Queue_Packet_t *tx_packet; - if ((tx_packet = Custom_CRS_GetNextTxPacket())) + if ((tx_packet = CRS_TX_Queue_GetNextTxPacket())) { tx_packet->length = length + 1; *(tx_packet->data) = command; memcpy(tx_packet->data + 1, payload, length); - Custom_CRS_SendNextTxPacket(); + CRS_TX_Queue_SendNextTxPacket(); } } @@ -344,6 +345,7 @@ static FS_CRS_State_t FS_CRS_State_Dir(void) { FS_CRS_State_t next_state = FS_CRS_STATE_DIR; Custom_CRS_Packet_t *packet; + CRS_TX_Queue_Packet_t *tx_packet; FILINFO fno; if (!Custom_APP_IsConnected()) @@ -365,7 +367,8 @@ static FS_CRS_State_t FS_CRS_State_Dir(void) } } - while ((next_state == FS_CRS_STATE_DIR) && (packet = Custom_CRS_GetNextTxPacket())) + while ((next_state == FS_CRS_STATE_DIR) && + (tx_packet = CRS_TX_Queue_GetNextTxPacket())) { // Read a directory item if (f_readdir(&dir, &fno) == FR_OK) @@ -406,6 +409,7 @@ static FS_CRS_State_t FS_CRS_State_Read(void) { FS_CRS_State_t next_state = FS_CRS_STATE_READ; Custom_CRS_Packet_t *packet; + CRS_TX_Queue_Packet_t *tx_packet; UINT br; if (!Custom_APP_IsConnected()) @@ -452,7 +456,7 @@ static FS_CRS_State_t FS_CRS_State_Read(void) } while ((next_state == FS_CRS_STATE_READ) && - (packet = Custom_CRS_GetNextTxPacket()) && + (tx_packet = CRS_TX_Queue_GetNextTxPacket()) && (next_packet < next_ack + FS_CRS_WINDOW_LENGTH) && (next_packet < last_packet)) { diff --git a/STM32_WPAN/App/crs_tx_queue.c b/STM32_WPAN/App/crs_tx_queue.c new file mode 100644 index 0000000..b2e0c05 --- /dev/null +++ b/STM32_WPAN/App/crs_tx_queue.c @@ -0,0 +1,112 @@ +/*************************************************************************** +** ** +** FlySight 2 firmware ** +** Copyright 2025 Bionic Avionics Inc. ** +** ** +** 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 . ** +** ** +**************************************************************************** +** Contact: Bionic Avionics Inc. ** +** Website: http://flysight.ca/ ** +****************************************************************************/ + +#include "app_common.h" +#include "dbg_trace.h" +#include "ble.h" +#include "crs_tx_queue.h" +#include "crs.h" +#include "stm32_seq.h" +#include "custom_stm.h" + +static CRS_TX_Queue_Packet_t tx_buffer[FS_CRS_WINDOW_LENGTH+1]; +static uint32_t tx_read_index, tx_write_index; +static uint8_t tx_flow_status; + +extern uint8_t SizeCrs_Tx; + +static void CRS_TX_Queue_Transmit(void); + +void CRS_TX_Queue_Init(void) +{ + tx_read_index = 0; + tx_write_index = 0; + tx_flow_status = 1; + + UTIL_SEQ_RegTask(1<length; + + status = Custom_STM_App_Update_Char(CUSTOM_STM_CRS_TX, packet->data); + if (status == BLE_STATUS_INSUFFICIENT_RESOURCES) + { + tx_flow_status = 0; + } + else + { + ++tx_read_index; + + // Call update task and transmit next packet + UTIL_SEQ_SetTask(1<. ** +** ** +**************************************************************************** +** Contact: Bionic Avionics Inc. ** +** Website: http://flysight.ca/ ** +****************************************************************************/ + +#ifndef APP_CRS_TX_QUEUE_H_ +#define APP_CRS_TX_QUEUE_H_ + +typedef struct +{ + uint8_t data[244]; + uint8_t length; +} CRS_TX_Queue_Packet_t; + +void CRS_TX_Queue_Init(void); +void CRS_TX_Queue_TxPoolAvailableNotification(void); + +CRS_TX_Queue_Packet_t *CRS_TX_Queue_GetNextTxPacket(void); +void CRS_TX_Queue_SendNextTxPacket(void); + +#endif /* APP_CRS_TX_QUEUE_H_ */ diff --git a/STM32_WPAN/App/custom_app.c b/STM32_WPAN/App/custom_app.c index 0b1eb7f..2b3dd9c 100644 --- a/STM32_WPAN/App/custom_app.c +++ b/STM32_WPAN/App/custom_app.c @@ -30,6 +30,7 @@ /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "crs.h" +#include "crs_tx_queue.h" #include "start_control.h" #include "gnss_ble.h" /* USER CODE END Includes */ @@ -46,7 +47,7 @@ typedef struct uint8_t Start_control_Indication_Status; uint8_t Start_result_Indication_Status; /* USER CODE BEGIN CUSTOM_APP_Context_t */ - uint8_t Crs_tx_Flow_Status; + /* USER CODE END CUSTOM_APP_Context_t */ uint16_t ConnectionHandle; @@ -82,9 +83,6 @@ uint8_t UpdateCharData[247]; uint8_t NotifyCharData[247]; /* USER CODE BEGIN PV */ -static Custom_CRS_Packet_t tx_buffer[FS_CRS_WINDOW_LENGTH+1]; -static uint32_t tx_read_index, tx_write_index; - static Custom_CRS_Packet_t rx_buffer[FS_CRS_WINDOW_LENGTH+1]; static uint32_t rx_read_index, rx_write_index; @@ -98,7 +96,6 @@ static uint8_t start_result_packet[9]; static uint8_t connected_flag = 0; -extern uint8_t SizeCrs_Tx; extern uint8_t SizeCrs_Rx; extern uint8_t SizeGnss_Pv; @@ -126,7 +123,6 @@ static void Custom_Start_result_Send_Indication(void); static void Custom_CRS_OnConnect(Custom_App_ConnHandle_Not_evt_t *pNotification); static void Custom_CRS_OnDisconnect(void); static void Custom_CRS_OnRxWrite(Custom_STM_App_Notification_evt_t *pNotification); -static void Custom_CRS_Transmit(void); static void Custom_GNSS_Transmit(void); static void Custom_Start_OnControlWrite(Custom_STM_App_Notification_evt_t *pNotification); static void Custom_Start_Transmit(void); @@ -149,7 +145,6 @@ void Custom_STM_App_Notification(Custom_STM_App_Notification_evt_t *pNotificatio case CUSTOM_STM_CRS_TX_NOTIFY_ENABLED_EVT: /* USER CODE BEGIN CUSTOM_STM_CRS_TX_NOTIFY_ENABLED_EVT */ Custom_App_Context.Crs_tx_Notification_Status = 1; - UTIL_SEQ_SetTask(1<length; - - status = Custom_STM_App_Update_Char(CUSTOM_STM_CRS_TX, packet->data); - if (status == BLE_STATUS_INSUFFICIENT_RESOURCES) - { - Custom_App_Context.Crs_tx_Flow_Status = 0; - } - else - { - ++tx_read_index; - - // Call update task and transmit next packet - UTIL_SEQ_SetTask(1< Date: Fri, 16 May 2025 14:56:49 -0600 Subject: [PATCH 06/81] Write CRS data directly to TX buffer --- FlySight/crs.c | 44 +++++++++++++++++------------------ STM32_WPAN/App/crs_tx_queue.c | 21 +++++++++++------ STM32_WPAN/App/crs_tx_queue.h | 10 ++------ 3 files changed, 38 insertions(+), 37 deletions(-) diff --git a/FlySight/crs.c b/FlySight/crs.c index 3f9391b..2e8ad28 100644 --- a/FlySight/crs.c +++ b/FlySight/crs.c @@ -90,7 +90,6 @@ static FS_CRS_StateFunc_t *const state_table[FS_CRS_STATE_COUNT] = static FS_CRS_State_t state = FS_CRS_STATE_IDLE; static FIL file; -static uint8_t buffer[FRAME_LENGTH + 1]; static DIR dir; static uint32_t read_offset; @@ -106,14 +105,13 @@ static volatile uint8_t timeout_flag; static void FS_CRS_SendPacket(uint8_t command, uint8_t *payload, uint8_t length) { - CRS_TX_Queue_Packet_t *tx_packet; + uint8_t *tx_buffer; - if ((tx_packet = CRS_TX_Queue_GetNextTxPacket())) + if ((tx_buffer = CRS_TX_Queue_GetNextTxPacket())) { - tx_packet->length = length + 1; - *(tx_packet->data) = command; - memcpy(tx_packet->data + 1, payload, length); - CRS_TX_Queue_SendNextTxPacket(); + tx_buffer[0] = command; + memcpy(&tx_buffer[1], payload, length); + CRS_TX_Queue_SendNextTxPacket(length + 1); } } @@ -345,7 +343,7 @@ static FS_CRS_State_t FS_CRS_State_Dir(void) { FS_CRS_State_t next_state = FS_CRS_STATE_DIR; Custom_CRS_Packet_t *packet; - CRS_TX_Queue_Packet_t *tx_packet; + uint8_t *tx_buffer; FILINFO fno; if (!Custom_APP_IsConnected()) @@ -368,19 +366,20 @@ static FS_CRS_State_t FS_CRS_State_Dir(void) } while ((next_state == FS_CRS_STATE_DIR) && - (tx_packet = CRS_TX_Queue_GetNextTxPacket())) + (tx_buffer = CRS_TX_Queue_GetNextTxPacket())) { // Read a directory item if (f_readdir(&dir, &fno) == FR_OK) { - buffer[0] = ((next_packet++) & 0xff); - memcpy(buffer + 1, &fno.fsize, sizeof(fno.fsize)); - memcpy(buffer + 5, &fno.fdate, sizeof(fno.fdate)); - memcpy(buffer + 7, &fno.ftime, sizeof(fno.ftime)); - buffer[9] = fno.fattrib; - memcpy(buffer + 10, fno.fname, sizeof(fno.fname)); + tx_buffer[0] = FS_CRS_COMMAND_FILE_INFO; + tx_buffer[1] = ((next_packet++) & 0xff); + memcpy(&tx_buffer[2], &fno.fsize, sizeof(fno.fsize)); + memcpy(&tx_buffer[6], &fno.fdate, sizeof(fno.fdate)); + memcpy(&tx_buffer[8], &fno.ftime, sizeof(fno.ftime)); + tx_buffer[10] = fno.fattrib; + memcpy(&tx_buffer[11], fno.fname, sizeof(fno.fname)); - FS_CRS_SendPacket(FS_CRS_COMMAND_FILE_INFO, buffer, 23); + CRS_TX_Queue_SendNextTxPacket(24); if (fno.fname[0] == 0) { @@ -409,7 +408,7 @@ static FS_CRS_State_t FS_CRS_State_Read(void) { FS_CRS_State_t next_state = FS_CRS_STATE_READ; Custom_CRS_Packet_t *packet; - CRS_TX_Queue_Packet_t *tx_packet; + uint8_t *tx_buffer; UINT br; if (!Custom_APP_IsConnected()) @@ -456,19 +455,20 @@ static FS_CRS_State_t FS_CRS_State_Read(void) } while ((next_state == FS_CRS_STATE_READ) && - (tx_packet = CRS_TX_Queue_GetNextTxPacket()) && + (tx_buffer = CRS_TX_Queue_GetNextTxPacket()) && (next_packet < next_ack + FS_CRS_WINDOW_LENGTH) && (next_packet < last_packet)) { - buffer[0] = (next_packet & 0xff); + tx_buffer[0] = FS_CRS_COMMAND_FILE_DATA; + tx_buffer[1] = (next_packet & 0xff); if (f_eof(&file)) { // Send empty buffer to signal end of file - FS_CRS_SendPacket(FS_CRS_COMMAND_FILE_DATA, buffer, 1); + CRS_TX_Queue_SendNextTxPacket(2); last_packet = ++next_packet; } - else if (f_read(&file, &buffer[1], FRAME_LENGTH, &br) == FR_OK) + else if (f_read(&file, &tx_buffer[2], FRAME_LENGTH, &br) == FR_OK) { if (read_stride > FRAME_LENGTH) { @@ -477,7 +477,7 @@ static FS_CRS_State_t FS_CRS_State_Read(void) } // Send data - FS_CRS_SendPacket(FS_CRS_COMMAND_FILE_DATA, buffer, br + 1); + CRS_TX_Queue_SendNextTxPacket(br + 2); ++next_packet; } else diff --git a/STM32_WPAN/App/crs_tx_queue.c b/STM32_WPAN/App/crs_tx_queue.c index b2e0c05..88ade09 100644 --- a/STM32_WPAN/App/crs_tx_queue.c +++ b/STM32_WPAN/App/crs_tx_queue.c @@ -29,6 +29,12 @@ #include "stm32_seq.h" #include "custom_stm.h" +typedef struct +{ + uint8_t data[244]; + uint8_t length; +} CRS_TX_Queue_Packet_t; + static CRS_TX_Queue_Packet_t tx_buffer[FS_CRS_WINDOW_LENGTH+1]; static uint32_t tx_read_index, tx_write_index; static uint8_t tx_flow_status; @@ -53,22 +59,23 @@ void CRS_TX_Queue_TxPoolAvailableNotification(void) UTIL_SEQ_SetTask(1< Date: Fri, 16 May 2025 15:05:49 -0600 Subject: [PATCH 07/81] Move characteristic opcode into buffer --- FlySight/crs.c | 8 ++++---- STM32_WPAN/App/crs_tx_queue.c | 17 ++++++++++++----- STM32_WPAN/App/crs_tx_queue.h | 5 ++++- STM32_WPAN/App/custom_stm.h | 2 +- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/FlySight/crs.c b/FlySight/crs.c index 2e8ad28..6aabc2d 100644 --- a/FlySight/crs.c +++ b/FlySight/crs.c @@ -111,7 +111,7 @@ static void FS_CRS_SendPacket(uint8_t command, uint8_t *payload, uint8_t length) { tx_buffer[0] = command; memcpy(&tx_buffer[1], payload, length); - CRS_TX_Queue_SendNextTxPacket(length + 1); + CRS_TX_Queue_SendNextTxPacket(CUSTOM_STM_CRS_TX, length + 1); } } @@ -379,7 +379,7 @@ static FS_CRS_State_t FS_CRS_State_Dir(void) tx_buffer[10] = fno.fattrib; memcpy(&tx_buffer[11], fno.fname, sizeof(fno.fname)); - CRS_TX_Queue_SendNextTxPacket(24); + CRS_TX_Queue_SendNextTxPacket(CUSTOM_STM_CRS_TX, 24); if (fno.fname[0] == 0) { @@ -465,7 +465,7 @@ static FS_CRS_State_t FS_CRS_State_Read(void) if (f_eof(&file)) { // Send empty buffer to signal end of file - CRS_TX_Queue_SendNextTxPacket(2); + CRS_TX_Queue_SendNextTxPacket(CUSTOM_STM_CRS_TX, 2); last_packet = ++next_packet; } else if (f_read(&file, &tx_buffer[2], FRAME_LENGTH, &br) == FR_OK) @@ -477,7 +477,7 @@ static FS_CRS_State_t FS_CRS_State_Read(void) } // Send data - CRS_TX_Queue_SendNextTxPacket(br + 2); + CRS_TX_Queue_SendNextTxPacket(CUSTOM_STM_CRS_TX, br + 2); ++next_packet; } else diff --git a/STM32_WPAN/App/crs_tx_queue.c b/STM32_WPAN/App/crs_tx_queue.c index 88ade09..f6d319f 100644 --- a/STM32_WPAN/App/crs_tx_queue.c +++ b/STM32_WPAN/App/crs_tx_queue.c @@ -31,8 +31,9 @@ typedef struct { - uint8_t data[244]; - uint8_t length; + Custom_STM_Char_Opcode_t opcode; + uint8_t data[244]; + uint8_t length; } CRS_TX_Queue_Packet_t; static CRS_TX_Queue_Packet_t tx_buffer[FS_CRS_WINDOW_LENGTH+1]; @@ -71,11 +72,17 @@ uint8_t *CRS_TX_Queue_GetNextTxPacket(void) } } -void CRS_TX_Queue_SendNextTxPacket(uint8_t length) +void CRS_TX_Queue_SendNextTxPacket(Custom_STM_Char_Opcode_t opcode, + uint8_t length) { if (tx_write_index < tx_read_index + FS_CRS_WINDOW_LENGTH) { - tx_buffer[tx_write_index % FS_CRS_WINDOW_LENGTH].length = length; + CRS_TX_Queue_Packet_t *packet = + &tx_buffer[tx_write_index % FS_CRS_WINDOW_LENGTH]; + + packet->opcode = opcode; + packet->length = length; + ++tx_write_index; UTIL_SEQ_SetTask(1<length; - status = Custom_STM_App_Update_Char(CUSTOM_STM_CRS_TX, packet->data); + status = Custom_STM_App_Update_Char(packet->opcode, packet->data); if (status == BLE_STATUS_INSUFFICIENT_RESOURCES) { tx_flow_status = 0; diff --git a/STM32_WPAN/App/crs_tx_queue.h b/STM32_WPAN/App/crs_tx_queue.h index 6164053..590fe34 100644 --- a/STM32_WPAN/App/crs_tx_queue.h +++ b/STM32_WPAN/App/crs_tx_queue.h @@ -24,10 +24,13 @@ #ifndef APP_CRS_TX_QUEUE_H_ #define APP_CRS_TX_QUEUE_H_ +#include "custom_stm.h" + void CRS_TX_Queue_Init(void); void CRS_TX_Queue_TxPoolAvailableNotification(void); uint8_t *CRS_TX_Queue_GetNextTxPacket(void); -void CRS_TX_Queue_SendNextTxPacket(uint8_t length); +void CRS_TX_Queue_SendNextTxPacket( + Custom_STM_Char_Opcode_t opcode, uint8_t length); #endif /* APP_CRS_TX_QUEUE_H_ */ diff --git a/STM32_WPAN/App/custom_stm.h b/STM32_WPAN/App/custom_stm.h index 28a3353..c40b180 100644 --- a/STM32_WPAN/App/custom_stm.h +++ b/STM32_WPAN/App/custom_stm.h @@ -28,7 +28,7 @@ extern "C" { /* Includes ------------------------------------------------------------------*/ /* USER CODE BEGIN Includes */ - +#include "ble_types.h" /* USER CODE END Includes */ /* Exported types ------------------------------------------------------------*/ From 3a9ef9c0b18435ab49ff56d851ae9790a93e280a Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Fri, 16 May 2025 15:12:12 -0600 Subject: [PATCH 08/81] Rename CRS_TX_Queue to BLE_TX_Queue --- FlySight/crs.c | 16 ++++++------- .../App/{crs_tx_queue.c => ble_tx_queue.c} | 24 +++++++++---------- .../App/{crs_tx_queue.h => ble_tx_queue.h} | 14 +++++------ STM32_WPAN/App/custom_app.c | 6 ++--- 4 files changed, 30 insertions(+), 30 deletions(-) rename STM32_WPAN/App/{crs_tx_queue.c => ble_tx_queue.c} (87%) rename STM32_WPAN/App/{crs_tx_queue.h => ble_tx_queue.h} (87%) diff --git a/FlySight/crs.c b/FlySight/crs.c index 6aabc2d..fa368f8 100644 --- a/FlySight/crs.c +++ b/FlySight/crs.c @@ -21,11 +21,11 @@ ** Website: http://flysight.ca/ ** ****************************************************************************/ +#include #include "main.h" #include "app_common.h" #include "crs.h" #include "custom_app.h" -#include "crs_tx_queue.h" #include "dbg_trace.h" #include "ff.h" #include "resource_manager.h" @@ -107,11 +107,11 @@ static void FS_CRS_SendPacket(uint8_t command, uint8_t *payload, uint8_t length) { uint8_t *tx_buffer; - if ((tx_buffer = CRS_TX_Queue_GetNextTxPacket())) + if ((tx_buffer = BLE_TX_Queue_GetNextTxPacket())) { tx_buffer[0] = command; memcpy(&tx_buffer[1], payload, length); - CRS_TX_Queue_SendNextTxPacket(CUSTOM_STM_CRS_TX, length + 1); + BLE_TX_Queue_SendNextTxPacket(CUSTOM_STM_CRS_TX, length + 1); } } @@ -366,7 +366,7 @@ static FS_CRS_State_t FS_CRS_State_Dir(void) } while ((next_state == FS_CRS_STATE_DIR) && - (tx_buffer = CRS_TX_Queue_GetNextTxPacket())) + (tx_buffer = BLE_TX_Queue_GetNextTxPacket())) { // Read a directory item if (f_readdir(&dir, &fno) == FR_OK) @@ -379,7 +379,7 @@ static FS_CRS_State_t FS_CRS_State_Dir(void) tx_buffer[10] = fno.fattrib; memcpy(&tx_buffer[11], fno.fname, sizeof(fno.fname)); - CRS_TX_Queue_SendNextTxPacket(CUSTOM_STM_CRS_TX, 24); + BLE_TX_Queue_SendNextTxPacket(CUSTOM_STM_CRS_TX, 24); if (fno.fname[0] == 0) { @@ -455,7 +455,7 @@ static FS_CRS_State_t FS_CRS_State_Read(void) } while ((next_state == FS_CRS_STATE_READ) && - (tx_buffer = CRS_TX_Queue_GetNextTxPacket()) && + (tx_buffer = BLE_TX_Queue_GetNextTxPacket()) && (next_packet < next_ack + FS_CRS_WINDOW_LENGTH) && (next_packet < last_packet)) { @@ -465,7 +465,7 @@ static FS_CRS_State_t FS_CRS_State_Read(void) if (f_eof(&file)) { // Send empty buffer to signal end of file - CRS_TX_Queue_SendNextTxPacket(CUSTOM_STM_CRS_TX, 2); + BLE_TX_Queue_SendNextTxPacket(CUSTOM_STM_CRS_TX, 2); last_packet = ++next_packet; } else if (f_read(&file, &tx_buffer[2], FRAME_LENGTH, &br) == FR_OK) @@ -477,7 +477,7 @@ static FS_CRS_State_t FS_CRS_State_Read(void) } // Send data - CRS_TX_Queue_SendNextTxPacket(CUSTOM_STM_CRS_TX, br + 2); + BLE_TX_Queue_SendNextTxPacket(CUSTOM_STM_CRS_TX, br + 2); ++next_packet; } else diff --git a/STM32_WPAN/App/crs_tx_queue.c b/STM32_WPAN/App/ble_tx_queue.c similarity index 87% rename from STM32_WPAN/App/crs_tx_queue.c rename to STM32_WPAN/App/ble_tx_queue.c index f6d319f..5102866 100644 --- a/STM32_WPAN/App/crs_tx_queue.c +++ b/STM32_WPAN/App/ble_tx_queue.c @@ -21,10 +21,10 @@ ** Website: http://flysight.ca/ ** ****************************************************************************/ +#include #include "app_common.h" #include "dbg_trace.h" #include "ble.h" -#include "crs_tx_queue.h" #include "crs.h" #include "stm32_seq.h" #include "custom_stm.h" @@ -34,33 +34,33 @@ typedef struct Custom_STM_Char_Opcode_t opcode; uint8_t data[244]; uint8_t length; -} CRS_TX_Queue_Packet_t; +} BLE_TX_Queue_Packet_t; -static CRS_TX_Queue_Packet_t tx_buffer[FS_CRS_WINDOW_LENGTH+1]; +static BLE_TX_Queue_Packet_t tx_buffer[FS_CRS_WINDOW_LENGTH+1]; static uint32_t tx_read_index, tx_write_index; static uint8_t tx_flow_status; extern uint8_t SizeCrs_Tx; -static void CRS_TX_Queue_Transmit(void); +static void BLE_TX_Queue_Transmit(void); -void CRS_TX_Queue_Init(void) +void BLE_TX_Queue_Init(void) { tx_read_index = 0; tx_write_index = 0; tx_flow_status = 1; UTIL_SEQ_RegTask(1<opcode = opcode; @@ -92,10 +92,10 @@ void CRS_TX_Queue_SendNextTxPacket(Custom_STM_Char_Opcode_t opcode, } } -static void CRS_TX_Queue_Transmit(void) +static void BLE_TX_Queue_Transmit(void) { static uint8_t tx_busy = 0; - CRS_TX_Queue_Packet_t *packet; + BLE_TX_Queue_Packet_t *packet; tBleStatus status; if (!tx_busy && diff --git a/STM32_WPAN/App/crs_tx_queue.h b/STM32_WPAN/App/ble_tx_queue.h similarity index 87% rename from STM32_WPAN/App/crs_tx_queue.h rename to STM32_WPAN/App/ble_tx_queue.h index 590fe34..1f0ecb6 100644 --- a/STM32_WPAN/App/crs_tx_queue.h +++ b/STM32_WPAN/App/ble_tx_queue.h @@ -21,16 +21,16 @@ ** Website: http://flysight.ca/ ** ****************************************************************************/ -#ifndef APP_CRS_TX_QUEUE_H_ -#define APP_CRS_TX_QUEUE_H_ +#ifndef APP_BLE_TX_QUEUE_H_ +#define APP_BLE_TX_QUEUE_H_ #include "custom_stm.h" -void CRS_TX_Queue_Init(void); -void CRS_TX_Queue_TxPoolAvailableNotification(void); +void BLE_TX_Queue_Init(void); +void BLE_TX_Queue_TxPoolAvailableNotification(void); -uint8_t *CRS_TX_Queue_GetNextTxPacket(void); -void CRS_TX_Queue_SendNextTxPacket( +uint8_t *BLE_TX_Queue_GetNextTxPacket(void); +void BLE_TX_Queue_SendNextTxPacket( Custom_STM_Char_Opcode_t opcode, uint8_t length); -#endif /* APP_CRS_TX_QUEUE_H_ */ +#endif /* APP_BLE_TX_QUEUE_H_ */ diff --git a/STM32_WPAN/App/custom_app.c b/STM32_WPAN/App/custom_app.c index 2b3dd9c..fb714f2 100644 --- a/STM32_WPAN/App/custom_app.c +++ b/STM32_WPAN/App/custom_app.c @@ -19,6 +19,7 @@ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ +#include #include "main.h" #include "app_common.h" #include "dbg_trace.h" @@ -30,7 +31,6 @@ /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "crs.h" -#include "crs_tx_queue.h" #include "start_control.h" #include "gnss_ble.h" /* USER CODE END Includes */ @@ -315,7 +315,7 @@ void Custom_APP_Notification(Custom_App_ConnHandle_Not_evt_t *pNotification) void Custom_APP_Init(void) { /* USER CODE BEGIN CUSTOM_APP_Init */ - CRS_TX_Queue_Init(); + BLE_TX_Queue_Init(); UTIL_SEQ_RegTask(1< Date: Fri, 16 May 2025 17:10:42 -0600 Subject: [PATCH 09/81] Convert GNSS_PV and START_RESULT to use CRS_TX_Queue --- Core/Inc/app_conf.h | 4 +--- STM32_WPAN/App/ble_tx_queue.c | 8 ++++---- STM32_WPAN/App/custom_app.c | 29 ++++++++++++----------------- 3 files changed, 17 insertions(+), 24 deletions(-) diff --git a/Core/Inc/app_conf.h b/Core/Inc/app_conf.h index f042d49..c88045b 100644 --- a/Core/Inc/app_conf.h +++ b/Core/Inc/app_conf.h @@ -694,11 +694,9 @@ typedef enum CFG_TASK_FS_AUDIO_CONTROL_PRODUCER_ID, CFG_TASK_FS_AUDIO_CONTROL_CONSUMER_ID, CFG_TASK_FS_CONFIG_UPDATE_ID, - CFG_TASK_CRS_TX_QUEUE_TRANSMIT_ID, - CFG_TASK_CUSTOM_GNSS_TRANSMIT_ID, + CFG_TASK_BLE_TX_QUEUE_TRANSMIT_ID, CFG_TASK_FS_CRS_UPDATE_ID, CFG_TASK_FS_START_UPDATE_ID, - CFG_TASK_CUSTOM_START_TRANSMIT_ID, /* USER CODE END CFG_Task_Id_With_NO_HCI_Cmd_t */ CFG_LAST_TASK_ID_WITH_NO_HCICMD /**< Shall be LAST in the list */ } CFG_Task_Id_With_NO_HCI_Cmd_t; diff --git a/STM32_WPAN/App/ble_tx_queue.c b/STM32_WPAN/App/ble_tx_queue.c index 5102866..9fbf3c5 100644 --- a/STM32_WPAN/App/ble_tx_queue.c +++ b/STM32_WPAN/App/ble_tx_queue.c @@ -50,14 +50,14 @@ void BLE_TX_Queue_Init(void) tx_write_index = 0; tx_flow_status = 1; - UTIL_SEQ_RegTask(1<length = length; ++tx_write_index; - UTIL_SEQ_SetTask(1< Date: Fri, 16 May 2025 19:49:36 -0600 Subject: [PATCH 10/81] Create function to add to BLE_TX_Queue with a single call --- Core/Inc/app_conf.h | 10 +++++----- STM32_WPAN/App/ble_tx_queue.c | 11 +++++++++++ STM32_WPAN/App/ble_tx_queue.h | 3 +++ STM32_WPAN/App/custom_app.c | 19 ++++++------------- 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/Core/Inc/app_conf.h b/Core/Inc/app_conf.h index c88045b..fcab4f9 100644 --- a/Core/Inc/app_conf.h +++ b/Core/Inc/app_conf.h @@ -673,8 +673,11 @@ typedef enum #endif CFG_TASK_HCI_ASYNCH_EVT_ID, /* USER CODE BEGIN CFG_Task_Id_With_HCI_Cmd_t */ - CFG_TASK_LINK_CONFIG_ID, - CFG_TASK_ADV_UPDATE_ID, + CFG_TASK_LINK_CONFIG_ID, + CFG_TASK_ADV_UPDATE_ID, + CFG_TASK_BLE_TX_QUEUE_TRANSMIT_ID, + CFG_TASK_FS_CRS_UPDATE_ID, + CFG_TASK_FS_START_UPDATE_ID, /* USER CODE END CFG_Task_Id_With_HCI_Cmd_t */ CFG_LAST_TASK_ID_WITH_HCICMD, /**< Shall be LAST in the list */ } CFG_Task_Id_With_HCI_Cmd_t; @@ -694,9 +697,6 @@ typedef enum CFG_TASK_FS_AUDIO_CONTROL_PRODUCER_ID, CFG_TASK_FS_AUDIO_CONTROL_CONSUMER_ID, CFG_TASK_FS_CONFIG_UPDATE_ID, - CFG_TASK_BLE_TX_QUEUE_TRANSMIT_ID, - CFG_TASK_FS_CRS_UPDATE_ID, - CFG_TASK_FS_START_UPDATE_ID, /* USER CODE END CFG_Task_Id_With_NO_HCI_Cmd_t */ CFG_LAST_TASK_ID_WITH_NO_HCICMD /**< Shall be LAST in the list */ } CFG_Task_Id_With_NO_HCI_Cmd_t; diff --git a/STM32_WPAN/App/ble_tx_queue.c b/STM32_WPAN/App/ble_tx_queue.c index 9fbf3c5..782f039 100644 --- a/STM32_WPAN/App/ble_tx_queue.c +++ b/STM32_WPAN/App/ble_tx_queue.c @@ -92,6 +92,17 @@ void BLE_TX_Queue_SendNextTxPacket(Custom_STM_Char_Opcode_t opcode, } } +void BLE_TX_Queue_SendTxPacket(Custom_STM_Char_Opcode_t opcode, + uint8_t *data, uint8_t length) +{ + uint8_t *tx_buffer; + if ((tx_buffer = BLE_TX_Queue_GetNextTxPacket())) + { + memcpy(tx_buffer, data, length); + BLE_TX_Queue_SendNextTxPacket(opcode, length); + } +} + static void BLE_TX_Queue_Transmit(void) { static uint8_t tx_busy = 0; diff --git a/STM32_WPAN/App/ble_tx_queue.h b/STM32_WPAN/App/ble_tx_queue.h index 1f0ecb6..af6b01c 100644 --- a/STM32_WPAN/App/ble_tx_queue.h +++ b/STM32_WPAN/App/ble_tx_queue.h @@ -33,4 +33,7 @@ uint8_t *BLE_TX_Queue_GetNextTxPacket(void); void BLE_TX_Queue_SendNextTxPacket( Custom_STM_Char_Opcode_t opcode, uint8_t length); +void BLE_TX_Queue_SendTxPacket( + Custom_STM_Char_Opcode_t opcode, uint8_t *data, uint8_t length); + #endif /* APP_BLE_TX_QUEUE_H_ */ diff --git a/STM32_WPAN/App/custom_app.c b/STM32_WPAN/App/custom_app.c index 446269d..d62c1ff 100644 --- a/STM32_WPAN/App/custom_app.c +++ b/STM32_WPAN/App/custom_app.c @@ -197,7 +197,8 @@ void Custom_STM_App_Notification(Custom_STM_App_Notification_evt_t *pNotificatio if (rsp_len && Custom_App_Context.Gnss_control_Notification_Status) { SizeGnss_Control = rsp_len; - Custom_STM_App_Update_Char(CUSTOM_STM_GNSS_CONTROL, gnss_control_rsp); + BLE_TX_Queue_SendTxPacket(CUSTOM_STM_GNSS_CONTROL, + gnss_control_rsp, SizeGnss_Control); } } /* USER CODE END CUSTOM_STM_GNSS_CONTROL_WRITE_EVT */ @@ -613,12 +614,8 @@ void Custom_GNSS_Update(const FS_GNSS_Data_t *current) if (Custom_App_Context.Gnss_pv_Notification_Status) { - uint8_t *tx_buffer; - if ((tx_buffer = BLE_TX_Queue_GetNextTxPacket())) - { - memcpy(tx_buffer, gnss_pv_packet, SizeGnss_Pv); - BLE_TX_Queue_SendNextTxPacket(CUSTOM_STM_GNSS_PV, SizeGnss_Pv); - } + BLE_TX_Queue_SendTxPacket(CUSTOM_STM_GNSS_PV, + gnss_pv_packet, SizeGnss_Pv); } } @@ -667,12 +664,8 @@ void Custom_Start_Update(uint16_t year, uint8_t month, uint8_t day, if (Custom_App_Context.Start_result_Indication_Status) { - uint8_t *tx_buffer; - if ((tx_buffer = BLE_TX_Queue_GetNextTxPacket())) - { - memcpy(tx_buffer, start_result_packet, SizeStart_Result); - BLE_TX_Queue_SendNextTxPacket(CUSTOM_STM_START_RESULT, SizeStart_Result); - } + BLE_TX_Queue_SendTxPacket(CUSTOM_STM_START_RESULT, + start_result_packet, SizeStart_Result); } } From 33d298e18ce66c8e88a8b8cca3afc25b07065d5a Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Fri, 16 May 2025 20:14:05 -0600 Subject: [PATCH 11/81] Add callback after BLE_TX_Queue item is sent --- FlySight/crs.c | 13 +++++++++---- STM32_WPAN/App/ble_tx_queue.c | 15 ++++++++++----- STM32_WPAN/App/ble_tx_queue.h | 8 ++++++-- STM32_WPAN/App/custom_app.c | 6 +++--- 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/FlySight/crs.c b/FlySight/crs.c index fa368f8..13a4247 100644 --- a/FlySight/crs.c +++ b/FlySight/crs.c @@ -103,6 +103,11 @@ static uint32_t last_packet; static uint8_t ack_timer_id; static volatile uint8_t timeout_flag; +static void FS_CRS_TxCallback() +{ + UTIL_SEQ_SetTask(1<opcode = opcode; packet->length = length; + packet->callback = callback; ++tx_write_index; UTIL_SEQ_SetTask(1<callback) + { + packet->callback(); + } UTIL_SEQ_SetTask(1< Date: Wed, 12 Feb 2025 11:55:36 -0700 Subject: [PATCH 12/81] Use new structures for server operation --- STM32_WPAN/App/app_ble.c | 184 +++++++++++++++------------------------ STM32_WPAN/App/app_ble.h | 5 +- 2 files changed, 74 insertions(+), 115 deletions(-) diff --git a/STM32_WPAN/App/app_ble.c b/STM32_WPAN/App/app_ble.c index ada00ba..69bda4f 100644 --- a/STM32_WPAN/App/app_ble.c +++ b/STM32_WPAN/App/app_ble.c @@ -137,12 +137,6 @@ typedef struct _tBLEProfileGlobalContext */ uint16_t appearanceCharHandle; - /** - * connection handle of the current active connection - * When not in connection, the handle is set to 0xFFFF - */ - uint16_t connectionHandle; - /** * length of the UUID list to be used while advertising */ @@ -160,11 +154,17 @@ typedef struct _tBLEProfileGlobalContext typedef struct { BleGlobalContext_t BleApplicationContext_legacy; - APP_BLE_ConnStatus_t Device_Connection_Status; - /* USER CODE BEGIN PTD_1*/ - uint8_t Advertising_mgr_timer_Id; - /* USER CODE END PTD_1 */ -}BleApplicationContext_t; + /** + * used to identify the GAP State + */ + APP_BLE_ConnStatus_t SmartPhone_Connection_Status; + + /** + * connection handle with the Central connection (Smart Phone) + * When not in connection, the handle is set to 0xFFFF + */ + uint16_t connectionHandleCentral; +} BleApplicationContext_t; /* USER CODE BEGIN PTD */ @@ -226,6 +226,8 @@ uint8_t a_AdvData[15] = }; /* USER CODE BEGIN PV */ +uint8_t Advertising_mgr_timer_Id; + /* Advertising callback */ void (*Adv_Callback)(void) = 0; void (*Next_Adv_Callback)(void) = 0; @@ -349,8 +351,8 @@ void APP_BLE_Init(void) /** * Initialization of the BLE App Context */ - BleApplicationContext.Device_Connection_Status = APP_BLE_IDLE; - BleApplicationContext.BleApplicationContext_legacy.connectionHandle = 0xFFFF; + BleApplicationContext.SmartPhone_Connection_Status = APP_BLE_IDLE; + BleApplicationContext.connectionHandleCentral = 0xFFFF; /** * From here, all initialization are BLE application specific @@ -390,7 +392,7 @@ void APP_BLE_Init(void) /* USER CODE BEGIN APP_BLE_Init_3 */ /* Create timer to handle the connection state machine */ - HW_TS_Create(CFG_TIM_PROC_ID_ISR, &(BleApplicationContext.Advertising_mgr_timer_Id), hw_ts_SingleShot, Adv_Update_Req); + HW_TS_Create(CFG_TIM_PROC_ID_ISR, &Advertising_mgr_timer_Id, hw_ts_SingleShot, Adv_Update_Req); #if 0 /* USER CODE END APP_BLE_Init_3 */ @@ -433,6 +435,8 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void *p_Pckt) tBleStatus ret = BLE_STATUS_INVALID_PARAMS; hci_le_enhanced_connection_complete_event_rp0 *p_enhanced_connection_complete_event; hci_disconnection_complete_event_rp0 *p_disconnection_complete_event; + uint8_t role; + uint16_t connection_handle; #if (CFG_DEBUG_APP_TRACE != 0) hci_le_connection_update_complete_event_rp0 *p_connection_update_complete_event; #endif /* CFG_DEBUG_APP_TRACE != 0 */ @@ -449,54 +453,29 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void *p_Pckt) switch (p_event_pckt->evt) { - case HCI_DISCONNECTION_COMPLETE_EVT_CODE: - { - p_disconnection_complete_event = (hci_disconnection_complete_event_rp0 *) p_event_pckt->data; - - if (p_disconnection_complete_event->Connection_Handle == BleApplicationContext.BleApplicationContext_legacy.connectionHandle) - { - BleApplicationContext.BleApplicationContext_legacy.connectionHandle = 0; - BleApplicationContext.Device_Connection_Status = APP_BLE_IDLE; - APP_DBG_MSG(">>== HCI_DISCONNECTION_COMPLETE_EVT_CODE\n"); - APP_DBG_MSG(" - Connection Handle: 0x%x\n - Reason: 0x%x\n\r", - p_disconnection_complete_event->Connection_Handle, - p_disconnection_complete_event->Reason); + case HCI_DISCONNECTION_COMPLETE_EVT_CODE: + p_disconnection_complete_event = (hci_disconnection_complete_event_rp0 *) p_event_pckt->data; - /* USER CODE BEGIN EVT_DISCONN_COMPLETE_2 */ - - /* USER CODE END EVT_DISCONN_COMPLETE_2 */ - } - - /* USER CODE BEGIN EVT_DISCONN_COMPLETE_1 */ -#if 0 - /* USER CODE END EVT_DISCONN_COMPLETE_1 */ + /* USER CODE BEGIN EVT_DISCONN_COMPLETE */ - /* restart advertising */ - Adv_Request(APP_BLE_FAST_ADV); + /* USER CODE END EVT_DISCONN_COMPLETE */ + if (p_disconnection_complete_event->Connection_Handle == BleApplicationContext.connectionHandleCentral) + { + APP_DBG_MSG("\r\n\r** DISCONNECTION EVENT OF SMART PHONE \n\r"); + BleApplicationContext.SmartPhone_Connection_Status = APP_BLE_IDLE; + BleApplicationContext.connectionHandleCentral = 0xFFFF; - /** - * SPECIFIC to Custom Template APP - */ - HandleNotification.Custom_Evt_Opcode = CUSTOM_DISCON_HANDLE_EVT; - HandleNotification.ConnectionHandle = BleApplicationContext.BleApplicationContext_legacy.connectionHandle; - Custom_APP_Notification(&HandleNotification); - /* USER CODE BEGIN EVT_DISCONN_COMPLETE */ -#endif if (FS_State_Get()->enable_ble) { /* restart advertising */ FS_Adv_Request(APP_BLE_FAST_ADV); } - /** - * SPECIFIC to Custom Template APP - */ HandleNotification.Custom_Evt_Opcode = CUSTOM_DISCON_HANDLE_EVT; - HandleNotification.ConnectionHandle = BleApplicationContext.BleApplicationContext_legacy.connectionHandle; + HandleNotification.ConnectionHandle = 0xFFFF; Custom_APP_Notification(&HandleNotification); - /* USER CODE END EVT_DISCONN_COMPLETE */ - break; /* HCI_DISCONNECTION_COMPLETE_EVT_CODE */ } + break; /* HCI_DISCONNECTION_COMPLETE_EVT_CODE */ case HCI_LE_META_EVT_CODE: { @@ -522,59 +501,40 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void *p_Pckt) break; case HCI_LE_ENHANCED_CONNECTION_COMPLETE_SUBEVT_CODE: - { p_enhanced_connection_complete_event = (hci_le_enhanced_connection_complete_event_rp0 *) p_meta_evt->data; /** * The connection is done, there is no need anymore to schedule the LP ADV */ - APP_DBG_MSG(">>== HCI_LE_ENHANCED_CONNECTION_COMPLETE_SUBEVT_CODE - Connection handle: 0x%x\n", p_enhanced_connection_complete_event->Connection_Handle); - APP_DBG_MSG(" - Connection established with Central: @:%02x:%02x:%02x:%02x:%02x:%02x\n", - p_enhanced_connection_complete_event->Peer_Address[5], - p_enhanced_connection_complete_event->Peer_Address[4], - p_enhanced_connection_complete_event->Peer_Address[3], - p_enhanced_connection_complete_event->Peer_Address[2], - p_enhanced_connection_complete_event->Peer_Address[1], - p_enhanced_connection_complete_event->Peer_Address[0]); - APP_DBG_MSG(" - Connection Interval: %.2f ms\n - Connection latency: %d\n - Supervision Timeout: %d ms\n\r", - p_enhanced_connection_complete_event->Conn_Interval*1.25, - p_enhanced_connection_complete_event->Conn_Latency, - p_enhanced_connection_complete_event->Supervision_Timeout*10 - ); + connection_handle = p_enhanced_connection_complete_event->Connection_Handle; + role = p_enhanced_connection_complete_event->Role; + if (role == 0x00) + { /* ROLE CENTRAL */ - if (BleApplicationContext.Device_Connection_Status == APP_BLE_LP_CONNECTING) - { - /* Connection as client */ - BleApplicationContext.Device_Connection_Status = APP_BLE_CONNECTED_CLIENT; } else { - /* Connection as server */ - BleApplicationContext.Device_Connection_Status = APP_BLE_CONNECTED_SERVER; + APP_DBG_MSG("-- CONNECTION SUCCESS WITH SMART PHONE\n\r"); + BleApplicationContext.SmartPhone_Connection_Status = APP_BLE_CONNECTED; + BleApplicationContext.connectionHandleCentral = connection_handle; + HandleNotification.Custom_Evt_Opcode = CUSTOM_CONN_HANDLE_EVT; + HandleNotification.ConnectionHandle = connection_handle; + Custom_APP_Notification(&HandleNotification); + + /* Stop the timer */ + HW_TS_Stop(Advertising_mgr_timer_Id); + + /* Call advertising callback */ + if (Adv_Callback) Adv_Callback(); + + /* Update advertising callback */ + Adv_Callback = Next_Adv_Callback; + Next_Adv_Callback = 0; + + /* Configure the link */ + UTIL_SEQ_SetTask(1 << CFG_TASK_LINK_CONFIG_ID, CFG_SCH_PRIO_1); } - BleApplicationContext.BleApplicationContext_legacy.connectionHandle = p_enhanced_connection_complete_event->Connection_Handle; - /** - * SPECIFIC to Custom Template APP - */ - HandleNotification.Custom_Evt_Opcode = CUSTOM_CONN_HANDLE_EVT; - HandleNotification.ConnectionHandle = BleApplicationContext.BleApplicationContext_legacy.connectionHandle; - Custom_APP_Notification(&HandleNotification); - /* USER CODE BEGIN HCI_LE_ENHANCED_CONNECTION_COMPLETE_SUBEVT_CODE */ - /* Stop the timer */ - HW_TS_Stop(BleApplicationContext.Advertising_mgr_timer_Id); - - /* Call advertising callback */ - if (Adv_Callback) Adv_Callback(); - - /* Update advertising callback */ - Adv_Callback = Next_Adv_Callback; - Next_Adv_Callback = 0; - - /* Configure the link */ - UTIL_SEQ_SetTask(1 << CFG_TASK_LINK_CONFIG_ID, CFG_SCH_PRIO_1); - /* USER CODE END HCI_LE_ENHANCED_CONNECTION_COMPLETE_SUBEVT_CODE */ - break; /* HCI_LE_ENHANCED_CONNECTION_COMPLETE_SUBEVT_CODE */ - } + break; /* HCI_LE_CONNECTION_COMPLETE_SUBEVT_CODE */ default: /* USER CODE BEGIN SUBEVENT_DEFAULT */ @@ -638,7 +598,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void *p_Pckt) case ACI_GAP_PASS_KEY_REQ_VSEVT_CODE: APP_DBG_MSG(">>== ACI_GAP_PASS_KEY_REQ_VSEVT_CODE \n"); - ret = aci_gap_pass_key_resp(BleApplicationContext.BleApplicationContext_legacy.connectionHandle, CFG_FIXED_PIN); + ret = aci_gap_pass_key_resp(BleApplicationContext.connectionHandleCentral, CFG_FIXED_PIN); if (ret != BLE_STATUS_SUCCESS) { APP_DBG_MSG("==>> aci_gap_pass_key_resp : Fail, reason: 0x%x\n", ret); @@ -658,7 +618,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void *p_Pckt) ((aci_gap_numeric_comparison_value_event_rp0 *)(p_blecore_evt->data))->Numeric_Value); APP_DBG_MSG(" - Hex_value = %lx\n", ((aci_gap_numeric_comparison_value_event_rp0 *)(p_blecore_evt->data))->Numeric_Value); - ret = aci_gap_numeric_comparison_value_confirm_yesno(BleApplicationContext.BleApplicationContext_legacy.connectionHandle, YES); + ret = aci_gap_numeric_comparison_value_confirm_yesno(BleApplicationContext.connectionHandleCentral, YES); if (ret != BLE_STATUS_SUCCESS) { APP_DBG_MSG("==>> aci_gap_numeric_comparison_value_confirm_yesno-->YES : Fail, reason: 0x%x\n", ret); @@ -694,7 +654,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void *p_Pckt) case ACI_GATT_INDICATION_VSEVT_CODE: { APP_DBG_MSG(">>== ACI_GATT_INDICATION_VSEVT_CODE \r"); - aci_gatt_confirm_indication(BleApplicationContext.BleApplicationContext_legacy.connectionHandle); + aci_gatt_confirm_indication(BleApplicationContext.connectionHandleCentral); } break; @@ -722,7 +682,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void *p_Pckt) APP_BLE_ConnStatus_t APP_BLE_Get_Server_Connection_Status(void) { - return BleApplicationContext.Device_Connection_Status; + return BleApplicationContext.SmartPhone_Connection_Status; } /* USER CODE BEGIN FD*/ @@ -1006,7 +966,7 @@ static void Adv_Request(APP_BLE_ConnStatus_t NewStatus) { tBleStatus ret = BLE_STATUS_INVALID_PARAMS; - BleApplicationContext.Device_Connection_Status = NewStatus; + BleApplicationContext.SmartPhone_Connection_Status = NewStatus; /* Start Fast or Low Power Advertising */ ret = aci_gap_set_discoverable(ADV_TYPE, CFG_FAST_CONN_ADV_INTERVAL_MIN, @@ -1061,13 +1021,13 @@ static void Adv_Cancel(void) /* USER CODE END Adv_Cancel_1 */ - if (BleApplicationContext.Device_Connection_Status != APP_BLE_CONNECTED_SERVER) + if (BleApplicationContext.SmartPhone_Connection_Status != APP_BLE_CONNECTED) { tBleStatus ret = BLE_STATUS_INVALID_PARAMS; ret = aci_gap_set_non_discoverable(); - BleApplicationContext.Device_Connection_Status = APP_BLE_IDLE; + BleApplicationContext.SmartPhone_Connection_Status = APP_BLE_IDLE; if (ret != BLE_STATUS_SUCCESS) { APP_DBG_MSG("** STOP ADVERTISING ** Failed \r\n\r"); @@ -1103,7 +1063,7 @@ void BLE_SVC_L2CAP_Conn_Update(uint16_t ConnectionHandle) uint16_t timeout_multiplier = L2CAP_TIMEOUT_MULTIPLIER; tBleStatus ret; - ret = aci_l2cap_connection_parameter_update_req(BleApplicationContext.BleApplicationContext_legacy.connectionHandle, + ret = aci_l2cap_connection_parameter_update_req(BleApplicationContext.connectionHandleCentral, interval_min, interval_max, peripheral_latency, timeout_multiplier); if (ret != BLE_STATUS_SUCCESS) @@ -1127,9 +1087,9 @@ void BLE_SVC_L2CAP_Conn_Update(uint16_t ConnectionHandle) #if (L2CAP_REQUEST_NEW_CONN_PARAM != 0) static void Connection_Interval_Update_Req(void) { - if (BleApplicationContext.Device_Connection_Status != APP_BLE_FAST_ADV && BleApplicationContext.Device_Connection_Status != APP_BLE_IDLE) + if (BleApplicationContext.SmartPhone_Connection_Status != APP_BLE_FAST_ADV && BleApplicationContext.SmartPhone_Connection_Status != APP_BLE_IDLE) { - BLE_SVC_L2CAP_Conn_Update(BleApplicationContext.BleApplicationContext_legacy.connectionHandle); + BLE_SVC_L2CAP_Conn_Update(BleApplicationContext.connectionHandleCentral); } return; @@ -1143,7 +1103,7 @@ static void LinkConfiguration(void) /* See AN5289: How to maximize data throughput */ APP_DBG_MSG("set data length \n"); - status = hci_le_set_data_length(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,251,2120); + status = hci_le_set_data_length(BleApplicationContext.connectionHandleCentral,251,2120); if (status != BLE_STATUS_SUCCESS) { APP_DBG_MSG("set data length command error \n"); @@ -1170,10 +1130,10 @@ static void FS_Adv_Request(APP_BLE_ConnStatus_t NewStatus) * Stop the timer, it will be restarted for a new shot * It does not hurt if the timer was not running */ - HW_TS_Stop(BleApplicationContext.Advertising_mgr_timer_Id); + HW_TS_Stop(Advertising_mgr_timer_Id); - if ((BleApplicationContext.Device_Connection_Status == APP_BLE_FAST_ADV) - || (BleApplicationContext.Device_Connection_Status == APP_BLE_LP_ADV)) + if ((BleApplicationContext.SmartPhone_Connection_Status == APP_BLE_FAST_ADV) + || (BleApplicationContext.SmartPhone_Connection_Status == APP_BLE_LP_ADV)) { /* Connection in ADVERTISE mode have to stop the current advertising */ ret = aci_gap_set_non_discoverable(); @@ -1194,7 +1154,7 @@ static void FS_Adv_Request(APP_BLE_ConnStatus_t NewStatus) Adv_Callback = Next_Adv_Callback; Next_Adv_Callback = 0; - BleApplicationContext.Device_Connection_Status = NewStatus; + BleApplicationContext.SmartPhone_Connection_Status = NewStatus; /** * Prepare white list as described in PM0271 5.3.1 @@ -1249,7 +1209,7 @@ static void FS_Adv_Request(APP_BLE_ConnStatus_t NewStatus) if (NewStatus == APP_BLE_FAST_ADV) { - HW_TS_Start(BleApplicationContext.Advertising_mgr_timer_Id, FAST_ADV_TIMEOUT); + HW_TS_Start(Advertising_mgr_timer_Id, FAST_ADV_TIMEOUT); } return; @@ -1286,7 +1246,7 @@ void APP_BLE_CancelPairing(void) Next_Adv_Callback = 0; request_pairing = 0; - if (BleApplicationContext.Device_Connection_Status == APP_BLE_FAST_ADV) + if (BleApplicationContext.SmartPhone_Connection_Status == APP_BLE_FAST_ADV) { /* Request low power advertising */ FS_Adv_Request(APP_BLE_LP_ADV); @@ -1327,7 +1287,7 @@ void APP_BLE_UpdateDeviceName(void) } /* Update advertising data */ - APP_BLE_UpdateAdvertisingData(BleApplicationContext.Device_Connection_Status); + APP_BLE_UpdateAdvertisingData(BleApplicationContext.SmartPhone_Connection_Status); } void APP_BLE_Reset(void) @@ -1346,7 +1306,7 @@ void APP_BLE_Reset(void) } /* Re-initialize advertising */ - FS_Adv_Request(BleApplicationContext.Device_Connection_Status); + FS_Adv_Request(BleApplicationContext.SmartPhone_Connection_Status); } static void APP_BLE_UpdateAdvertisingData(APP_BLE_ConnStatus_t NewStatus) { diff --git a/STM32_WPAN/App/app_ble.h b/STM32_WPAN/App/app_ble.h index 70a6f1e..6bb6e11 100644 --- a/STM32_WPAN/App/app_ble.h +++ b/STM32_WPAN/App/app_ble.h @@ -47,9 +47,8 @@ typedef enum APP_BLE_FAST_ADV, APP_BLE_LP_ADV, APP_BLE_SCAN, - APP_BLE_LP_CONNECTING, - APP_BLE_CONNECTED_SERVER, - APP_BLE_CONNECTED_CLIENT + APP_BLE_CONNECTING, + APP_BLE_CONNECTED } APP_BLE_ConnStatus_t; /* USER CODE BEGIN ET */ From f290446a85411f2a80cbb04948d04d95f2c28848 Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Thu, 13 Feb 2025 15:40:08 -0700 Subject: [PATCH 13/81] Add skeleton for BLE central role --- Core/Inc/app_conf.h | 9 + FlySight/active_mode.c | 4 + STM32_WPAN/App/app_ble.c | 345 +++++++++++++++++++++++++++++++++--- STM32_WPAN/App/app_ble.h | 1 + STM32_WPAN/App/custom_app.c | 24 +++ STM32_WPAN/App/custom_app.h | 2 + 6 files changed, 358 insertions(+), 27 deletions(-) diff --git a/Core/Inc/app_conf.h b/Core/Inc/app_conf.h index fcab4f9..9f7ff14 100644 --- a/Core/Inc/app_conf.h +++ b/Core/Inc/app_conf.h @@ -177,6 +177,13 @@ #define CONN_L(x) ((int)((x)/0.625f)) #define CONN_P(x) ((int)((x)/1.25f)) +#define SCAN_P (0x320) +#define SCAN_L (0x320) +#define CONN_P1 (CONN_P(200)) +#define CONN_P2 (CONN_P(1000)) +#define SUPERV_TIMEOUT (400) +#define CONN_L1 (CONN_L(10)) +#define CONN_L2 (CONN_L(10)) /* L2CAP Connection Update request parameters used for test only with smart Phone */ #define L2CAP_REQUEST_NEW_CONN_PARAM 0 @@ -678,6 +685,8 @@ typedef enum CFG_TASK_BLE_TX_QUEUE_TRANSMIT_ID, CFG_TASK_FS_CRS_UPDATE_ID, CFG_TASK_FS_START_UPDATE_ID, + CFG_TASK_START_SCAN_ID, + CFG_TASK_CONN_DEV_1_ID, /* USER CODE END CFG_Task_Id_With_HCI_Cmd_t */ CFG_LAST_TASK_ID_WITH_HCICMD, /**< Shall be LAST in the list */ } CFG_Task_Id_With_HCI_Cmd_t; diff --git a/FlySight/active_mode.c b/FlySight/active_mode.c index de76460..8b983ec 100644 --- a/FlySight/active_mode.c +++ b/FlySight/active_mode.c @@ -37,6 +37,7 @@ #include "resource_manager.h" #include "sensor.h" #include "state.h" +#include "stm32_seq.h" #include "vbat.h" extern UART_HandleTypeDef huart1; @@ -47,6 +48,9 @@ void FS_ActiveMode_Init(void) uint8_t enable_flags; bool isSystemHealthy = true; + /* Start scanning for BLE peripherals */ + UTIL_SEQ_SetTask(1 << CFG_TASK_START_SCAN_ID, CFG_SCH_PRIO_0); + /* Initialize FatFS */ FS_ResourceManager_RequestResource(FS_RESOURCE_FATFS); diff --git a/STM32_WPAN/App/app_ble.c b/STM32_WPAN/App/app_ble.c index 69bda4f..90e92e0 100644 --- a/STM32_WPAN/App/app_ble.c +++ b/STM32_WPAN/App/app_ble.c @@ -159,11 +159,32 @@ typedef struct */ APP_BLE_ConnStatus_t SmartPhone_Connection_Status; + /** + * used to identify the GAP State + */ + APP_BLE_ConnStatus_t EndDevice_Connection_Status[6]; + /** * connection handle with the Central connection (Smart Phone) * When not in connection, the handle is set to 0xFFFF */ uint16_t connectionHandleCentral; + + /** + * connection handle with the Server 1 connection (End Device 1) + * When not in connection, the handle is set to 0xFFFF + */ + uint16_t connectionHandleEndDevice1; + + /** + * used when doing advertising to find end device 1 + */ + uint8_t EndDevice1Found; + + /** + * address of end device 1 + */ + tBDAddr end_device_1_bdaddr; } BleApplicationContext_t; /* USER CODE BEGIN PTD */ @@ -234,6 +255,9 @@ void (*Next_Adv_Callback)(void) = 0; /* Pairing request flag */ uint8_t request_pairing = 0; + +/* BD Address of device to be connected once discovered */ +tBDAddr P2P_SERVER1_BDADDR; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ @@ -255,6 +279,8 @@ static void Adv_Update_Req(void); static void Adv_Update(void); static void APP_BLE_UpdateAdvertisingData(APP_BLE_ConnStatus_t NewStatus); static int8_t ble_count_bonded_devices(void); +static void Scan_Request(void); +static void Connect_Request(void); /* USER CODE END PFP */ /* External variables --------------------------------------------------------*/ @@ -352,7 +378,8 @@ void APP_BLE_Init(void) * Initialization of the BLE App Context */ BleApplicationContext.SmartPhone_Connection_Status = APP_BLE_IDLE; - BleApplicationContext.connectionHandleCentral = 0xFFFF; + BleApplicationContext.EndDevice_Connection_Status[0] = APP_BLE_IDLE; + BleApplicationContext.EndDevice1Found = 0x00; /** * From here, all initialization are BLE application specific @@ -363,6 +390,8 @@ void APP_BLE_Init(void) /* USER CODE BEGIN APP_BLE_Init_4 */ UTIL_SEQ_RegTask(1<evt) { - case HCI_DISCONNECTION_COMPLETE_EVT_CODE: - p_disconnection_complete_event = (hci_disconnection_complete_event_rp0 *) p_event_pckt->data; - - /* USER CODE BEGIN EVT_DISCONN_COMPLETE */ + case HCI_DISCONNECTION_COMPLETE_EVT_CODE: + p_disconnection_complete_event = (hci_disconnection_complete_event_rp0 *) p_event_pckt->data; - /* USER CODE END EVT_DISCONN_COMPLETE */ - if (p_disconnection_complete_event->Connection_Handle == BleApplicationContext.connectionHandleCentral) - { - APP_DBG_MSG("\r\n\r** DISCONNECTION EVENT OF SMART PHONE \n\r"); - BleApplicationContext.SmartPhone_Connection_Status = APP_BLE_IDLE; - BleApplicationContext.connectionHandleCentral = 0xFFFF; + /* USER CODE BEGIN EVT_DISCONN_COMPLETE */ - if (FS_State_Get()->enable_ble) + /* USER CODE END EVT_DISCONN_COMPLETE */ + if (p_disconnection_complete_event->Connection_Handle == BleApplicationContext.connectionHandleEndDevice1) { - /* restart advertising */ - FS_Adv_Request(APP_BLE_FAST_ADV); + APP_DBG_MSG("\r\n\r** DISCONNECTION EVENT OF END DEVICE 1 \n\r"); + BleApplicationContext.EndDevice_Connection_Status[0] = APP_BLE_IDLE; + BleApplicationContext.connectionHandleEndDevice1 = 0xFFFF; + HandleNotification.Custom_Evt_Opcode = CUSTOM_DISCON_DEV_1_HANDLE_EVT; + HandleNotification.ConnectionHandle = 0xFFFF; + Custom_APP_Notification(&HandleNotification); } - HandleNotification.Custom_Evt_Opcode = CUSTOM_DISCON_HANDLE_EVT; - HandleNotification.ConnectionHandle = 0xFFFF; - Custom_APP_Notification(&HandleNotification); - } - break; /* HCI_DISCONNECTION_COMPLETE_EVT_CODE */ + if (p_disconnection_complete_event->Connection_Handle == BleApplicationContext.connectionHandleCentral) + { + APP_DBG_MSG("\r\n\r** DISCONNECTION EVENT OF SMART PHONE \n\r"); + BleApplicationContext.SmartPhone_Connection_Status = APP_BLE_IDLE; + BleApplicationContext.connectionHandleCentral = 0xFFFF; + + if (FS_State_Get()->enable_ble) + { + /* restart advertising */ + FS_Adv_Request(APP_BLE_FAST_ADV); + } + + HandleNotification.Custom_Evt_Opcode = CUSTOM_DISCON_HANDLE_EVT; + HandleNotification.ConnectionHandle = 0xFFFF; + Custom_APP_Notification(&HandleNotification); + } + break; /* HCI_DISCONNECTION_COMPLETE_EVT_CODE */ case HCI_LE_META_EVT_CODE: { @@ -510,7 +555,23 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void *p_Pckt) role = p_enhanced_connection_complete_event->Role; if (role == 0x00) { /* ROLE CENTRAL */ - + /* Inform Application it is End Device 1 */ + APP_DBG_MSG("-- CONNECTION SUCCESS WITH END DEVICE 1\n\r"); + BleApplicationContext.EndDevice_Connection_Status[0] = APP_BLE_CONNECTED; + BleApplicationContext.connectionHandleEndDevice1 = connection_handle; + HandleNotification.Custom_Evt_Opcode = CUSTOM_CONN_DEV_1_HANDLE_EVT; + HandleNotification.ConnectionHandle = connection_handle; + Custom_APP_Notification(&HandleNotification); + result = aci_gatt_disc_all_primary_services(BleApplicationContext.connectionHandleEndDevice1); + if (result == BLE_STATUS_SUCCESS) + { + APP_DBG_MSG("\r\n\r** GATT SERVICES & CHARACTERISTICS DISCOVERY \n\r"); + APP_DBG_MSG("* GATT : Start Searching Primary Services \r\n\r"); + } + else + { + APP_DBG_MSG("BLE_CTRL_App_Notification(), All services discovery Failed \r\n\r"); + } } else { @@ -536,6 +597,63 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void *p_Pckt) } break; /* HCI_LE_CONNECTION_COMPLETE_SUBEVT_CODE */ + case HCI_LE_ADVERTISING_REPORT_SUBEVT_CODE: + /* USER CODE BEGIN EVT_LE_ADVERTISING_REPORT */ + + /* USER CODE END EVT_LE_ADVERTISING_REPORT */ + le_advertising_event = (hci_le_advertising_report_event_rp0 *) p_meta_evt->data; + + event_type = le_advertising_event->Advertising_Report[0].Event_Type; + + event_data_size = le_advertising_event->Advertising_Report[0].Length_Data; + + /* WARNING: be careful when decoding advertising report as its raw format cannot be mapped on a C structure. + The data and RSSI values could not be directly decoded from the RAM using the data and RSSI field from hci_le_advertising_report_event_rp0 structure. + Instead they must be read by using offsets (please refer to BLE specification). + RSSI = (int8_t)*(uint8_t*) (adv_report_data + le_advertising_event->Advertising_Report[0].Length_Data); + */ + adv_report_data = (uint8_t*)(&le_advertising_event->Advertising_Report[0].Length_Data) + 1; + k = 0; + + /* search AD TYPE 0x09 (Complete Local Name) */ + /* search AD Type 0x02 (16 bits UUIDS) */ + if (event_type == ADV_IND) + { + while(k < event_data_size) + { + adlength = adv_report_data[k]; + adtype = adv_report_data[k + 1]; + if (adtype == AD_TYPE_MANUFACTURER_SPECIFIC_DATA) + { + /* The payload starts at adv_report_data[k + 2], and its length is (adlength - 1). */ + const uint8_t *mfg_data = &adv_report_data[k + 2]; + uint8_t mfg_len = adlength - 1; // total bytes in manufacturer data + + /* The ActiveLook doc says we should see 0x08F2 at the end. + For example: 0xDAFA08F2 or some variation. So let's do: */ + if (mfg_len >= 2) + { + /* Check the last two bytes of manufacturer data. */ + uint16_t last2 = (mfg_data[mfg_len - 2] << 8) | mfg_data[mfg_len - 1]; + if (last2 == 0x08F2) + { + APP_DBG_MSG("-- Found ActiveLook device (mfg data ends with 0x08F2)\n\r"); + BleApplicationContext.EndDevice1Found = 0x01; + P2P_SERVER1_BDADDR[0] = le_advertising_event->Advertising_Report[0].Address[0]; + P2P_SERVER1_BDADDR[1] = le_advertising_event->Advertising_Report[0].Address[1]; + P2P_SERVER1_BDADDR[2] = le_advertising_event->Advertising_Report[0].Address[2]; + P2P_SERVER1_BDADDR[3] = le_advertising_event->Advertising_Report[0].Address[3]; + P2P_SERVER1_BDADDR[4] = le_advertising_event->Advertising_Report[0].Address[4]; + P2P_SERVER1_BDADDR[5] = le_advertising_event->Advertising_Report[0].Address[5]; + } + } + } + + k += adlength + 1; + } /* end while(k < event_data_size) */ + } /* end if (event_type == ADV_IND) */ + break; + default: /* USER CODE BEGIN SUBEVENT_DEFAULT */ @@ -563,6 +681,31 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void *p_Pckt) /** * SPECIFIC to Custom Template APP */ + case ACI_L2CAP_CONNECTION_UPDATE_REQ_VSEVT_CODE: + { + /* USER CODE BEGIN EVT_BLUE_L2CAP_CONNECTION_UPDATE_REQ */ + + /* USER CODE END EVT_BLUE_L2CAP_CONNECTION_UPDATE_REQ */ + pr = (aci_l2cap_connection_update_req_event_rp0 *) p_blecore_evt->data; + result = aci_l2cap_connection_parameter_update_resp(pr->Connection_Handle, + pr->Interval_Min, + pr->Interval_Max, + pr->Latency, + pr->Timeout_Multiplier, + CONN_L1, + CONN_L2, + pr->Identifier, + 0x00); + APP_DBG_MSG("\r\n\r** NO UPDATE \n\r"); + if(result != BLE_STATUS_SUCCESS) + { + /* USER CODE BEGIN BLE_STATUS_SUCCESS */ + + /* USER CODE END BLE_STATUS_SUCCESS */ + } + } + break; + case ACI_L2CAP_CONNECTION_UPDATE_RESP_VSEVT_CODE: #if (L2CAP_REQUEST_NEW_CONN_PARAM != 0) mutex = 1; @@ -573,11 +716,35 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void *p_Pckt) break; case ACI_GAP_PROC_COMPLETE_VSEVT_CODE: - APP_DBG_MSG(">>== ACI_GAP_PROC_COMPLETE_VSEVT_CODE \r"); - /* USER CODE BEGIN EVT_BLUE_GAP_PROCEDURE_COMPLETE */ - - /* USER CODE END EVT_BLUE_GAP_PROCEDURE_COMPLETE */ - break; /* ACI_GAP_PROC_COMPLETE_VSEVT_CODE */ + { + /* USER CODE BEGIN EVT_BLUE_GAP_PROCEDURE_COMPLETE */ + + /* USER CODE END EVT_BLUE_GAP_PROCEDURE_COMPLETE */ + aci_gap_proc_complete_event_rp0 *gap_evt_proc_complete = (void*) p_blecore_evt->data; + /* CHECK GAP GENERAL DISCOVERY PROCEDURE COMPLETED & SUCCEED */ + if (gap_evt_proc_complete->Procedure_Code == GAP_GENERAL_DISCOVERY_PROC + && gap_evt_proc_complete->Status == 0x00) + { + /* USER CODE BEGIN GAP_GENERAL_DISCOVERY_PROC */ + + /* USER CODE END GAP_GENERAL_DISCOVERY_PROC */ + + APP_DBG_MSG("-- GAP GENERAL DISCOVERY PROCEDURE_COMPLETED\n\r"); + /*if a device found, connect to it, device 1 being chosen first if both found*/ + if (BleApplicationContext.EndDevice1Found == 0x01 + && BleApplicationContext.EndDevice_Connection_Status[0] != APP_BLE_CONNECTED) + { + APP_DBG_MSG("-- Setting task CFG_TASK_CONN_DEV_1_ID\n\r"); + UTIL_SEQ_SetTask(1 << CFG_TASK_CONN_DEV_1_ID, CFG_SCH_PRIO_0); + } +#if (CFG_P2P_DEMO_MULTI != 0) + /* USER CODE BEGIN EVT_BLUE_GAP_PROCEDURE_COMPLETE_Multi */ + + /* USER CODE END EVT_BLUE_GAP_PROCEDURE_COMPLETE_Multi */ +#endif + } + } + break; /* ACI_GAP_PAIRING_COMPLETE_VSEVT_CODE */ #if (RADIO_ACTIVITY_EVENT != 0) case ACI_HAL_END_OF_RADIO_ACTIVITY_VSEVT_CODE: @@ -685,6 +852,28 @@ APP_BLE_ConnStatus_t APP_BLE_Get_Server_Connection_Status(void) return BleApplicationContext.SmartPhone_Connection_Status; } +APP_BLE_ConnStatus_t APP_BLE_Get_Client_Connection_Status(uint16_t Connection_Handle) +{ + /* USER CODE BEGIN APP_BLE_Get_Client_Connection_Status_1 */ + + /* USER CODE END APP_BLE_Get_Client_Connection_Status_1 */ + APP_BLE_ConnStatus_t return_value; + + if (BleApplicationContext.connectionHandleEndDevice1 == Connection_Handle) + { + return_value = BleApplicationContext.EndDevice_Connection_Status[0]; + } + else + { + return_value = APP_BLE_IDLE; + } + /* USER CODE BEGIN APP_BLE_Get_Client_Connection_Status_2 */ + + /* USER CODE END APP_BLE_Get_Client_Connection_Status_2 */ + + return (return_value); +} + /* USER CODE BEGIN FD*/ /* USER CODE END FD*/ @@ -1416,6 +1605,108 @@ static int8_t ble_count_bonded_devices(void) } return total; } + +/** + * @brief Scan Request + * @param None + * @retval None + */ +static void Scan_Request(void) +{ + /* USER CODE BEGIN Scan_Request_1 */ + + /* USER CODE END Scan_Request_1 */ + tBleStatus result; + + if (BleApplicationContext.EndDevice_Connection_Status[0] != APP_BLE_CONNECTED) + { + /* USER CODE BEGIN APP_BLE_CONNECTED */ + + /* USER CODE END APP_BLE_CONNECTED */ + result = aci_gap_set_non_discoverable(); + if (result != BLE_STATUS_SUCCESS) + { + APP_DBG_MSG("** STOP ADVERTISING ** Failed \r\n\r"); + } + else + { + APP_DBG_MSG(" \r\n\r"); + APP_DBG_MSG("** STOP ADVERTISING ** \r\n\r"); + } + + result = aci_gap_start_general_discovery_proc(SCAN_P, + SCAN_L, + GAP_STATIC_RANDOM_ADDR, + 1); + if (result == BLE_STATUS_SUCCESS) + { + /* USER CODE BEGIN BLE_SCAN_SUCCESS */ + + /* USER CODE END BLE_SCAN_SUCCESS */ + APP_DBG_MSG(" \r\n\r** START GENERAL DISCOVERY (SCAN) ** \r\n\r"); + } + else + { + /* USER CODE BEGIN BLE_SCAN_FAILED */ + + /* USER CODE END BLE_SCAN_FAILED */ + APP_DBG_MSG("-- BLE_App_Start_Limited_Disc_Req, Failed %02x \r\n\r", result); + } + } + /* USER CODE BEGIN Scan_Request_2 */ + + /* USER CODE END Scan_Request_2 */ + + return; +} + +/** + * @brief Connection Establishement on SERVER 1 + * @param None + * @retval None + */ +static void Connect_Request(void) +{ + tBleStatus result; + APP_DBG_MSG("\r\n\r** CREATE CONNECTION TO END DEVICE 1 ** \r\n\r"); + if (BleApplicationContext.EndDevice_Connection_Status[0] != APP_BLE_CONNECTED) + { + /* USER CODE BEGIN APP_BLE_CONNECTED_SUCCESS_END_DEVICE_1 */ + + /* USER CODE END APP_BLE_CONNECTED_SUCCESS_END_DEVICE_1 */ + result = aci_gap_create_connection(SCAN_P, + SCAN_L, + GAP_PUBLIC_ADDR, + P2P_SERVER1_BDADDR, + GAP_STATIC_RANDOM_ADDR, + CONN_P1, + CONN_P2, + 0, + SUPERV_TIMEOUT, + CONN_L1, + CONN_L2); + + if (result == BLE_STATUS_SUCCESS) + { + /* USER CODE BEGIN BLE_STATUS_END_DEVICE_1_SUCCESS */ + + /* USER CODE END BLE_STATUS_END_DEVICE_1_SUCCESS */ + BleApplicationContext.EndDevice_Connection_Status[0] = APP_BLE_CONNECTING; + APP_DBG_MSG("==> Connect_Request Succeeded \n\r"); + } + else + { + /* USER CODE BEGIN BLE_STATUS_END_DEVICE_1_FAILED */ + + /* USER CODE END BLE_STATUS_END_DEVICE_1_FAILED */ + BleApplicationContext.EndDevice_Connection_Status[0] = APP_BLE_IDLE; + APP_DBG_MSG("==> Connect_Request Failed \n\r"); + } + } + + return; +} + /* USER CODE END FD_SPECIFIC_FUNCTIONS */ /************************************************************* * diff --git a/STM32_WPAN/App/app_ble.h b/STM32_WPAN/App/app_ble.h index 6bb6e11..6363f2c 100644 --- a/STM32_WPAN/App/app_ble.h +++ b/STM32_WPAN/App/app_ble.h @@ -73,6 +73,7 @@ typedef enum /* Exported functions ---------------------------------------------*/ void APP_BLE_Init(void); APP_BLE_ConnStatus_t APP_BLE_Get_Server_Connection_Status(void); +APP_BLE_ConnStatus_t APP_BLE_Get_Client_Connection_Status(uint16_t Connection_Handle); /* USER CODE BEGIN EF */ void APP_BLE_RequestPairing(void (*Callback)(void)); diff --git a/STM32_WPAN/App/custom_app.c b/STM32_WPAN/App/custom_app.c index aaf603a..2bcad59 100644 --- a/STM32_WPAN/App/custom_app.c +++ b/STM32_WPAN/App/custom_app.c @@ -121,7 +121,9 @@ static void Custom_Start_result_Send_Indication(void); /* USER CODE BEGIN PFP */ static void Custom_CRS_OnConnect(Custom_App_ConnHandle_Not_evt_t *pNotification); +static void Custom_Activelook_OnConnect(Custom_App_ConnHandle_Not_evt_t *pNotification); static void Custom_CRS_OnDisconnect(void); +static void Custom_Activelook_OnDisconnect(void); static void Custom_CRS_OnRxWrite(Custom_STM_App_Notification_evt_t *pNotification); static void Custom_Start_OnControlWrite(Custom_STM_App_Notification_evt_t *pNotification); static void Custom_App_Timeout(void); @@ -291,12 +293,24 @@ void Custom_APP_Notification(Custom_App_ConnHandle_Not_evt_t *pNotification) /* USER CODE END CUSTOM_CONN_HANDLE_EVT */ break; + case CUSTOM_CONN_DEV_1_HANDLE_EVT : + /* USER CODE BEGIN CUSTOM_CONN_DEV_1_HANDLE_EVT */ + Custom_Activelook_OnConnect(pNotification); + /* USER CODE END CUSTOM_CONN_DEV_1_HANDLE_EVT */ + break; + case CUSTOM_DISCON_HANDLE_EVT : /* USER CODE BEGIN CUSTOM_DISCON_HANDLE_EVT */ Custom_CRS_OnDisconnect(); /* USER CODE END CUSTOM_DISCON_HANDLE_EVT */ break; + case CUSTOM_DISCON_DEV_1_HANDLE_EVT : + /* USER CODE BEGIN CUSTOM_DISCON_DEV_1_HANDLE_EVT */ + Custom_Activelook_OnDisconnect(); + /* USER CODE END CUSTOM_DISCON_DEV_1_HANDLE_EVT */ + break; + default: /* USER CODE BEGIN CUSTOM_APP_Notification_default */ @@ -562,6 +576,11 @@ static void Custom_CRS_OnConnect(Custom_App_ConnHandle_Not_evt_t *pNotification) HW_TS_Start(timeout_timer_id, TIMEOUT_TICKS); } +static void Custom_Activelook_OnConnect(Custom_App_ConnHandle_Not_evt_t *pNotification) +{ + +} + static void Custom_CRS_OnDisconnect(void) { // Stop timeout timer @@ -574,6 +593,11 @@ static void Custom_CRS_OnDisconnect(void) UTIL_SEQ_SetTask(1< Date: Sat, 15 Feb 2025 10:39:34 -0700 Subject: [PATCH 14/81] Connect to Activelook glasses --- Core/Inc/app_conf.h | 15 +++++++-------- FlySight/active_mode.c | 2 +- STM32_WPAN/App/app_ble.c | 6 +++--- STM32_WPAN/App/ble_conf.h | 4 ++-- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/Core/Inc/app_conf.h b/Core/Inc/app_conf.h index 9f7ff14..5f21e52 100644 --- a/Core/Inc/app_conf.h +++ b/Core/Inc/app_conf.h @@ -177,13 +177,6 @@ #define CONN_L(x) ((int)((x)/0.625f)) #define CONN_P(x) ((int)((x)/1.25f)) -#define SCAN_P (0x320) -#define SCAN_L (0x320) -#define CONN_P1 (CONN_P(200)) -#define CONN_P2 (CONN_P(1000)) -#define SUPERV_TIMEOUT (400) -#define CONN_L1 (CONN_L(10)) -#define CONN_L2 (CONN_L(10)) /* L2CAP Connection Update request parameters used for test only with smart Phone */ #define L2CAP_REQUEST_NEW_CONN_PARAM 0 @@ -194,7 +187,13 @@ #define L2CAP_TIMEOUT_MULTIPLIER 0x1F4 /* USER CODE BEGIN Specific_Parameters */ - +#define SCAN_P (0x320) +#define SCAN_L (0x320) +#define CONN_P1 (CONN_P(200)) +#define CONN_P2 (CONN_P(1000)) +#define SUPERV_TIMEOUT (400) +#define CONN_L1 (CONN_L(10)) +#define CONN_L2 (CONN_L(10)) /* USER CODE END Specific_Parameters */ /****************************************************************************** diff --git a/FlySight/active_mode.c b/FlySight/active_mode.c index 8b983ec..069dac7 100644 --- a/FlySight/active_mode.c +++ b/FlySight/active_mode.c @@ -49,7 +49,7 @@ void FS_ActiveMode_Init(void) bool isSystemHealthy = true; /* Start scanning for BLE peripherals */ - UTIL_SEQ_SetTask(1 << CFG_TASK_START_SCAN_ID, CFG_SCH_PRIO_0); + UTIL_SEQ_SetTask(1 << CFG_TASK_START_SCAN_ID, CFG_SCH_PRIO_0); /* Initialize FatFS */ FS_ResourceManager_RequestResource(FS_RESOURCE_FATFS); diff --git a/STM32_WPAN/App/app_ble.c b/STM32_WPAN/App/app_ble.c index 90e92e0..b7ab17e 100644 --- a/STM32_WPAN/App/app_ble.c +++ b/STM32_WPAN/App/app_ble.c @@ -1636,7 +1636,7 @@ static void Scan_Request(void) result = aci_gap_start_general_discovery_proc(SCAN_P, SCAN_L, - GAP_STATIC_RANDOM_ADDR, + CFG_BLE_ADDRESS_TYPE, 1); if (result == BLE_STATUS_SUCCESS) { @@ -1677,8 +1677,8 @@ static void Connect_Request(void) result = aci_gap_create_connection(SCAN_P, SCAN_L, GAP_PUBLIC_ADDR, - P2P_SERVER1_BDADDR, - GAP_STATIC_RANDOM_ADDR, + P2P_SERVER1_BDADDR, + CFG_BLE_ADDRESS_TYPE, CONN_P1, CONN_P2, 0, diff --git a/STM32_WPAN/App/ble_conf.h b/STM32_WPAN/App/ble_conf.h index 8c78c02..a28a5b8 100644 --- a/STM32_WPAN/App/ble_conf.h +++ b/STM32_WPAN/App/ble_conf.h @@ -46,7 +46,7 @@ * This setting shall be set to '1' if the device needs to support the Central Role * In the MS configuration, both BLE_CFG_PERIPHERAL and BLE_CFG_CENTRAL shall be set to '1' */ -#define BLE_CFG_CENTRAL 0 +#define BLE_CFG_CENTRAL 1 /** * There is one handler per service enabled @@ -57,7 +57,7 @@ */ #define BLE_CFG_SVC_MAX_NBR_CB 7 -#define BLE_CFG_CLT_MAX_NBR_CB 0 +#define BLE_CFG_CLT_MAX_NBR_CB 6 /****************************************************************************** * GAP Service - Appearance From dad3cbd2cc65c416e3845176b477b37e5404999e Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Sat, 15 Feb 2025 11:18:46 -0700 Subject: [PATCH 15/81] Disconnect from Activelook when exiting active mode --- Core/Inc/app_conf.h | 1 + FlySight/active_mode.c | 3 +++ STM32_WPAN/App/app_ble.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/Core/Inc/app_conf.h b/Core/Inc/app_conf.h index 5f21e52..e237811 100644 --- a/Core/Inc/app_conf.h +++ b/Core/Inc/app_conf.h @@ -686,6 +686,7 @@ typedef enum CFG_TASK_FS_START_UPDATE_ID, CFG_TASK_START_SCAN_ID, CFG_TASK_CONN_DEV_1_ID, + CFG_TASK_DISCONN_DEV_1_ID, /* USER CODE END CFG_Task_Id_With_HCI_Cmd_t */ CFG_LAST_TASK_ID_WITH_HCICMD, /**< Shall be LAST in the list */ } CFG_Task_Id_With_HCI_Cmd_t; diff --git a/FlySight/active_mode.c b/FlySight/active_mode.c index 069dac7..08f468d 100644 --- a/FlySight/active_mode.c +++ b/FlySight/active_mode.c @@ -208,6 +208,9 @@ void FS_ActiveMode_Init(void) void FS_ActiveMode_DeInit(void) { + /* Disconnect from BLE peripherals */ + UTIL_SEQ_SetTask(1 << CFG_TASK_DISCONN_DEV_1_ID, CFG_SCH_PRIO_0); + /* Disable controller */ FS_ActiveControl_DeInit(); diff --git a/STM32_WPAN/App/app_ble.c b/STM32_WPAN/App/app_ble.c index b7ab17e..34c1993 100644 --- a/STM32_WPAN/App/app_ble.c +++ b/STM32_WPAN/App/app_ble.c @@ -281,6 +281,7 @@ static void APP_BLE_UpdateAdvertisingData(APP_BLE_ConnStatus_t NewStatus); static int8_t ble_count_bonded_devices(void); static void Scan_Request(void); static void Connect_Request(void); +static void Disconnect_Request(void); /* USER CODE END PFP */ /* External variables --------------------------------------------------------*/ @@ -392,6 +393,7 @@ void APP_BLE_Init(void) UTIL_SEQ_RegTask(1< Date: Sat, 15 Feb 2025 11:49:13 -0700 Subject: [PATCH 16/81] Continue advertising while scanning --- Core/Inc/app_conf.h | 2 +- STM32_WPAN/App/app_ble.c | 11 ----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/Core/Inc/app_conf.h b/Core/Inc/app_conf.h index e237811..fe20a86 100644 --- a/Core/Inc/app_conf.h +++ b/Core/Inc/app_conf.h @@ -203,7 +203,7 @@ * Maximum number of simultaneous connections that the device will support. * Valid values are from 1 to 8 */ -#define CFG_BLE_NUM_LINK 1 +#define CFG_BLE_NUM_LINK 2 /** * Maximum number of Services that can be stored in the GATT database. diff --git a/STM32_WPAN/App/app_ble.c b/STM32_WPAN/App/app_ble.c index 34c1993..d66d9f0 100644 --- a/STM32_WPAN/App/app_ble.c +++ b/STM32_WPAN/App/app_ble.c @@ -1625,17 +1625,6 @@ static void Scan_Request(void) /* USER CODE BEGIN APP_BLE_CONNECTED */ /* USER CODE END APP_BLE_CONNECTED */ - result = aci_gap_set_non_discoverable(); - if (result != BLE_STATUS_SUCCESS) - { - APP_DBG_MSG("** STOP ADVERTISING ** Failed \r\n\r"); - } - else - { - APP_DBG_MSG(" \r\n\r"); - APP_DBG_MSG("** STOP ADVERTISING ** \r\n\r"); - } - result = aci_gap_start_general_discovery_proc(SCAN_P, SCAN_L, CFG_BLE_ADDRESS_TYPE, From fbceaa1d31b5944256c9c7a35b86c56c181fe756 Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Sat, 15 Feb 2025 16:40:32 -0700 Subject: [PATCH 17/81] Add skeleton for writing to ActiveLook --- FlySight/active_mode.c | 10 +- FlySight/activelook.c | 106 ++++++++++ FlySight/activelook.h | 30 +++ STM32_WPAN/App/activelook_client.c | 312 +++++++++++++++++++++++++++++ STM32_WPAN/App/activelook_client.h | 44 ++++ STM32_WPAN/App/app_ble.c | 29 +-- STM32_WPAN/App/custom_app.c | 24 --- STM32_WPAN/App/custom_app.h | 2 - 8 files changed, 506 insertions(+), 51 deletions(-) create mode 100644 FlySight/activelook.c create mode 100644 FlySight/activelook.h create mode 100644 STM32_WPAN/App/activelook_client.c create mode 100644 STM32_WPAN/App/activelook_client.h diff --git a/FlySight/active_mode.c b/FlySight/active_mode.c index 08f468d..1e82b06 100644 --- a/FlySight/active_mode.c +++ b/FlySight/active_mode.c @@ -23,6 +23,7 @@ #include "main.h" #include "active_control.h" +#include "activelook.h" #include "app_common.h" #include "app_fatfs.h" #include "audio.h" @@ -37,7 +38,6 @@ #include "resource_manager.h" #include "sensor.h" #include "state.h" -#include "stm32_seq.h" #include "vbat.h" extern UART_HandleTypeDef huart1; @@ -48,8 +48,8 @@ void FS_ActiveMode_Init(void) uint8_t enable_flags; bool isSystemHealthy = true; - /* Start scanning for BLE peripherals */ - UTIL_SEQ_SetTask(1 << CFG_TASK_START_SCAN_ID, CFG_SCH_PRIO_0); + /* Initialize Activelook interface */ + FS_Activelook_Init(); /* Initialize FatFS */ FS_ResourceManager_RequestResource(FS_RESOURCE_FATFS); @@ -208,8 +208,8 @@ void FS_ActiveMode_Init(void) void FS_ActiveMode_DeInit(void) { - /* Disconnect from BLE peripherals */ - UTIL_SEQ_SetTask(1 << CFG_TASK_DISCONN_DEV_1_ID, CFG_SCH_PRIO_0); + /* De-initialize Activelook interface */ + FS_Activelook_DeInit(); /* Disable controller */ FS_ActiveControl_DeInit(); diff --git a/FlySight/activelook.c b/FlySight/activelook.c new file mode 100644 index 0000000..ced714a --- /dev/null +++ b/FlySight/activelook.c @@ -0,0 +1,106 @@ +/*************************************************************************** +** ** +** FlySight 2 firmware ** +** Copyright 2025 Bionic Avionics Inc. ** +** ** +** 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 . ** +** ** +**************************************************************************** +** Contact: Bionic Avionics Inc. ** +** Website: http://flysight.ca/ ** +****************************************************************************/ + +#include "main.h" +#include "activelook.h" +#include "activelook_client.h" +#include "app_common.h" +#include "dbg_trace.h" +#include "stm32_seq.h" +#include + +/* Forward declaration */ +static void OnActivelookDiscoveryComplete(void); +static void FS_Activelook_SendHelloWorld(void); + +/* We'll define the callback struct */ +static const FS_Activelook_ClientCb_t s_alk_cb = +{ + .OnDiscoveryComplete = OnActivelookDiscoveryComplete +}; + +void FS_Activelook_Init(void) +{ + /* Register the callback before scanning/connecting. */ + FS_Activelook_Client_RegisterCb(&s_alk_cb); + + /* Start scanning for BLE peripherals, + leading eventually to connect to the Activelook device. */ + UTIL_SEQ_SetTask(1 << CFG_TASK_START_SCAN_ID, CFG_SCH_PRIO_0); +} + +void FS_Activelook_DeInit(void) +{ + /* Disconnect from BLE device #1 */ + UTIL_SEQ_SetTask(1 << CFG_TASK_DISCONN_DEV_1_ID, CFG_SCH_PRIO_0); +} + +/* This function is called once the client discovered the Rx characteristic. */ +static void OnActivelookDiscoveryComplete(void) +{ + APP_DBG_MSG("Activelook: Discovery complete. Let's do 'Hello, world!'...\n"); + + /* Example: call your HelloWorld sending function */ + FS_Activelook_SendHelloWorld(); +} + +/* + * Example function that builds the 'txt' command to display "Hello, world!" + * as you had previously. We can call it from OnActivelookDiscoveryComplete + * or any time later, as long as FS_Activelook_Client_IsReady() is true. + */ +static void FS_Activelook_SendHelloWorld(void) +{ + if (!FS_Activelook_Client_IsReady()) + { + APP_DBG_MSG("Activelook: Not ready, cannot send Hello!\n"); + return; + } + + uint8_t packet[26]; + uint8_t index = 0; + + packet[index++] = 0xFF; + packet[index++] = 0x37; // 'txt' + packet[index++] = 0x00; + packet[index++] = 26; // total length + + // s16 x=0, y=0 + packet[index++] = 0x00; packet[index++] = 0x00; + packet[index++] = 0x00; packet[index++] = 0x00; + + // rotation=0, font=1, color=1 + packet[index++] = 0x00; + packet[index++] = 0x01; + packet[index++] = 0x01; + + const char *text = "Hello, world!"; + size_t text_len = strlen(text) + 1; + memcpy(&packet[index], text, text_len); + index += text_len; + + packet[index++] = 0xAA; + + APP_DBG_MSG("Activelook: Sending Hello, world!\n"); + FS_Activelook_Client_WriteWithoutResp(packet, sizeof(packet)); +} diff --git a/FlySight/activelook.h b/FlySight/activelook.h new file mode 100644 index 0000000..1608d9b --- /dev/null +++ b/FlySight/activelook.h @@ -0,0 +1,30 @@ +/*************************************************************************** +** ** +** FlySight 2 firmware ** +** Copyright 2025 Bionic Avionics Inc. ** +** ** +** 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 . ** +** ** +**************************************************************************** +** Contact: Bionic Avionics Inc. ** +** Website: http://flysight.ca/ ** +****************************************************************************/ + +#ifndef ACTIVELOOK_H_ +#define ACTIVELOOK_H_ + +void FS_Activelook_Init(void); +void FS_Activelook_DeInit(void); + +#endif /* ACTIVELOOK_H_ */ diff --git a/STM32_WPAN/App/activelook_client.c b/STM32_WPAN/App/activelook_client.c new file mode 100644 index 0000000..b1b91d7 --- /dev/null +++ b/STM32_WPAN/App/activelook_client.c @@ -0,0 +1,312 @@ +/*************************************************************************** +** ** +** FlySight 2 firmware ** +** Copyright 2025 Bionic Avionics Inc. ** +** ** +** 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 . ** +** ** +**************************************************************************** +** Contact: Bionic Avionics Inc. ** +** Website: http://flysight.ca/ ** +****************************************************************************/ + +#include "activelook_client.h" +#include "app_common.h" +#include "dbg_trace.h" +#include "ble.h" +#include "tl.h" +#include + +#ifndef UNPACK_2_BYTE_PARAMETER +#define UNPACK_2_BYTE_PARAMETER(ptr) \ + (uint16_t)( ((uint16_t)(*((uint8_t *)(ptr)))) \ + | ((uint16_t)(*(((uint8_t *)(ptr))+1)) << 8U) ) +#endif + +/* 16-byte UUID for the Activelook Commands service + * 0783B03E-8535-B5A0-7140-A304D2495CB7 */ +static const uint8_t ACTIVELK_SERVICE_UUID[16] = +{ + 0xB7, 0x5C, 0x49, 0xD2, + 0x04, 0xA3, 0x40, 0x71, + 0xA0, 0xB5, 0x35, 0x85, + 0x3E, 0xB0, 0x83, 0x07 +}; + +/* 16-byte UUID for the Rx characteristic + * 0783B03E-8535-B5A0-7140-A304D2495CBA */ +static const uint8_t ACTIVELK_RX_CHAR_UUID[16] = +{ + 0xBA, 0x5C, 0x49, 0xD2, + 0x04, 0xA3, 0x40, 0x71, + 0xA0, 0xB5, 0x35, 0x85, + 0x3E, 0xB0, 0x83, 0x07 +}; + +typedef enum { + DISC_STATE_IDLE = 0, + DISC_STATE_EXCH_MTU, + DISC_STATE_SVC_IN_PROGRESS, + DISC_STATE_CHAR_IN_PROGRESS +} DiscoveryState_t; + +typedef struct +{ + uint16_t connHandle; + DiscoveryState_t discState; + + uint8_t serviceFound; + uint16_t serviceStartHandle; + uint16_t serviceEndHandle; + + uint8_t rxCharFound; + uint16_t rxCharHandle; + + const FS_Activelook_ClientCb_t *cb; +} FS_Activelook_Client_Context_t; + +static FS_Activelook_Client_Context_t g_ctx; + +/****************************************************************************** + * Initialize + ******************************************************************************/ +void FS_Activelook_Client_Init(void) +{ + memset(&g_ctx, 0, sizeof(g_ctx)); + g_ctx.discState = DISC_STATE_IDLE; +} + +/****************************************************************************** + * Register optional callback interface + ******************************************************************************/ +void FS_Activelook_Client_RegisterCb(const FS_Activelook_ClientCb_t *cb) +{ + g_ctx.cb = cb; +} + +/****************************************************************************** + * Start discovery (including MTU exchange) after connecting + ******************************************************************************/ +void FS_Activelook_Client_StartDiscovery(uint16_t connectionHandle) +{ + g_ctx.connHandle = connectionHandle; + g_ctx.discState = DISC_STATE_EXCH_MTU; /* <--- Start with MTU exchange */ + g_ctx.serviceFound = 0; + g_ctx.rxCharFound = 0; + g_ctx.serviceStartHandle = 0; + g_ctx.serviceEndHandle = 0; + g_ctx.rxCharHandle = 0; + + /* Step 1: request a bigger ATT MTU from the peripheral */ + tBleStatus s = aci_gatt_exchange_config(connectionHandle); + if (s == BLE_STATUS_SUCCESS) + { + APP_DBG_MSG("ActivelookClient: Requesting MTU exchange...\n"); + } + else + { + APP_DBG_MSG("ActivelookClient: aci_gatt_exchange_config fail=0x%02X\n", s); + g_ctx.discState = DISC_STATE_IDLE; // stop + } +} + +/****************************************************************************** + * Event Handler + ******************************************************************************/ +void FS_Activelook_Client_EventHandler(void *p_blecore_evt, uint8_t hci_event_evt_code) +{ + evt_blecore_aci *blecore_evt = (evt_blecore_aci*) p_blecore_evt; + + switch (blecore_evt->ecode) + { + /********************************************************************** + * The peripheral responded to MTU exchange: + *********************************************************************/ + case ACI_ATT_EXCHANGE_MTU_RESP_VSEVT_CODE: + { + /* This event indicates the peripheral accepted some MTU. */ + aci_att_exchange_mtu_resp_event_rp0 *mtu_resp = + (aci_att_exchange_mtu_resp_event_rp0*) blecore_evt->data; + APP_DBG_MSG("ActivelookClient: ACI_ATT_EXCHANGE_MTU_RESP, final MTU=%d\r\n", + mtu_resp->Server_RX_MTU); + /* We still must wait for ACI_GATT_PROC_COMPLETE_VSEVT_CODE, which + means the procedure is fully done in the stack. */ + } + break; + + /********************************************************************** + * GATT procedure complete => check if we were in MTU exchange, or + * discovering service, or discovering char, etc. + *********************************************************************/ + case ACI_GATT_PROC_COMPLETE_VSEVT_CODE: + { + aci_gatt_proc_complete_event_rp0 *pc = + (aci_gatt_proc_complete_event_rp0*) blecore_evt->data; + + if (pc->Connection_Handle != g_ctx.connHandle) + break; /* Not for us */ + + /* See which sub-procedure we just completed by our discState */ + if (g_ctx.discState == DISC_STATE_EXCH_MTU) + { + /* The MTU exchange is done; proceed to discover the service. */ + g_ctx.discState = DISC_STATE_SVC_IN_PROGRESS; + tBleStatus s = aci_gatt_disc_all_primary_services(g_ctx.connHandle); + if (s == BLE_STATUS_SUCCESS) + { + APP_DBG_MSG("ActivelookClient: MTU ok, now discovering service...\n"); + } + else + { + APP_DBG_MSG("ActivelookClient: disc_all_primary_services fail=0x%02X\n", s); + g_ctx.discState = DISC_STATE_IDLE; + } + } + else if (g_ctx.discState == DISC_STATE_SVC_IN_PROGRESS) + { + /* Done discovering services. If we found it, discover chars. */ + if (g_ctx.serviceFound) + { + g_ctx.discState = DISC_STATE_CHAR_IN_PROGRESS; + tBleStatus s = aci_gatt_disc_all_char_of_service( + g_ctx.connHandle, + g_ctx.serviceStartHandle, + g_ctx.serviceEndHandle ); + if (s == BLE_STATUS_SUCCESS) + { + APP_DBG_MSG("ActivelookClient: Discovering chars...\n"); + } + else + { + APP_DBG_MSG("ActivelookClient: disc_all_char_of_service fail=0x%02X\n", s); + g_ctx.discState = DISC_STATE_IDLE; + } + } + else + { + APP_DBG_MSG("ActivelookClient: service not found.\n"); + g_ctx.discState = DISC_STATE_IDLE; + } + } + else if (g_ctx.discState == DISC_STATE_CHAR_IN_PROGRESS) + { + /* Done discovering characteristics. */ + g_ctx.discState = DISC_STATE_IDLE; + APP_DBG_MSG("ActivelookClient: Char discovery complete, Rx=0x%04X\n", + g_ctx.rxCharHandle); + + if (g_ctx.rxCharFound && g_ctx.cb && g_ctx.cb->OnDiscoveryComplete) + { + g_ctx.cb->OnDiscoveryComplete(); + } + } + } + break; + + /********************************************************************** + * "Read by group type" => primary service + *********************************************************************/ + case ACI_ATT_READ_BY_GROUP_TYPE_RESP_VSEVT_CODE: + { + if (g_ctx.discState == DISC_STATE_SVC_IN_PROGRESS) + { + aci_att_read_by_group_type_resp_event_rp0 *pr = + (aci_att_read_by_group_type_resp_event_rp0*) blecore_evt->data; + + uint8_t idx = 0; + while (idx < pr->Data_Length) + { + uint16_t startHdl = UNPACK_2_BYTE_PARAMETER(&pr->Attribute_Data_List[idx]); + uint16_t endHdl = UNPACK_2_BYTE_PARAMETER(&pr->Attribute_Data_List[idx+2]); + const uint8_t *uuid = &pr->Attribute_Data_List[idx+4]; + + if (memcmp(uuid, ACTIVELK_SERVICE_UUID, 16) == 0) + { + APP_DBG_MSG("ActivelookClient: Found Service 0x%04X–0x%04X\n", startHdl, endHdl); + g_ctx.serviceFound = 1; + g_ctx.serviceStartHandle = startHdl; + g_ctx.serviceEndHandle = endHdl; + } + + idx += pr->Attribute_Data_Length; + } + } + } + break; + + /********************************************************************** + * "Read by type response" => characteristics + *********************************************************************/ + case ACI_ATT_READ_BY_TYPE_RESP_VSEVT_CODE: + { + if (g_ctx.discState == DISC_STATE_CHAR_IN_PROGRESS) + { + aci_att_read_by_type_resp_event_rp0 *pr = + (aci_att_read_by_type_resp_event_rp0*) blecore_evt->data; + uint8_t idx = 0; + while (idx < pr->Data_Length) + { + /* 2B decl handle, 1B props, 2B value handle, 16B UUID */ + idx += 2; /* skip decl handle */ + idx += 1; /* skip props */ + uint16_t valH = UNPACK_2_BYTE_PARAMETER(&pr->Handle_Value_Pair_Data[idx]); + idx += 2; + const uint8_t *uuid = &pr->Handle_Value_Pair_Data[idx]; + idx += 16; + + if (memcmp(uuid, ACTIVELK_RX_CHAR_UUID, 16) == 0) + { + APP_DBG_MSG("ActivelookClient: Found RxChar=0x%04X\n", valH); + g_ctx.rxCharFound = 1; + g_ctx.rxCharHandle = valH; + } + } + } + } + break; + + default: + break; + } +} + +/****************************************************************************** + * Check if Rx handle is ready + ******************************************************************************/ +uint8_t FS_Activelook_Client_IsReady(void) +{ + return (g_ctx.rxCharFound && g_ctx.rxCharHandle != 0); +} + +/****************************************************************************** + * Write data to Rx characteristic (WWR) + ******************************************************************************/ +tBleStatus FS_Activelook_Client_WriteWithoutResp(const uint8_t *data, uint16_t length) +{ + if (!FS_Activelook_Client_IsReady()) + { + APP_DBG_MSG("ActivelookClient: Not ready, no Rx handle.\n"); + return BLE_STATUS_FAILED; + } + + tBleStatus s = aci_gatt_write_without_resp(g_ctx.connHandle, + g_ctx.rxCharHandle, + length, + (uint8_t*)data); + if (s != BLE_STATUS_SUCCESS) + { + APP_DBG_MSG("ActivelookClient: WWR error=0x%02X\n", s); + } + return s; +} diff --git a/STM32_WPAN/App/activelook_client.h b/STM32_WPAN/App/activelook_client.h new file mode 100644 index 0000000..6547fa0 --- /dev/null +++ b/STM32_WPAN/App/activelook_client.h @@ -0,0 +1,44 @@ +/*************************************************************************** +** ** +** FlySight 2 firmware ** +** Copyright 2025 Bionic Avionics Inc. ** +** ** +** 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 . ** +** ** +**************************************************************************** +** Contact: Bionic Avionics Inc. ** +** Website: http://flysight.ca/ ** +****************************************************************************/ + +#ifndef ACTIVELOOK_CLIENT_H +#define ACTIVELOOK_CLIENT_H + +#include +#include "ble_types.h" + +typedef struct +{ + void (*OnDiscoveryComplete)(void); +} FS_Activelook_ClientCb_t; + +void FS_Activelook_Client_Init(void); +void FS_Activelook_Client_RegisterCb(const FS_Activelook_ClientCb_t *cb); +void FS_Activelook_Client_StartDiscovery(uint16_t connectionHandle); + +void FS_Activelook_Client_EventHandler(void *p_blecore_evt, uint8_t hci_event_evt_code); + +uint8_t FS_Activelook_Client_IsReady(void); +tBleStatus FS_Activelook_Client_WriteWithoutResp(const uint8_t *data, uint16_t length); + +#endif /* ACTIVELOOK_CLIENT_H */ diff --git a/STM32_WPAN/App/app_ble.c b/STM32_WPAN/App/app_ble.c index d66d9f0..2519c5b 100644 --- a/STM32_WPAN/App/app_ble.c +++ b/STM32_WPAN/App/app_ble.c @@ -44,6 +44,7 @@ /* USER CODE BEGIN Includes */ #include "common.h" #include "state.h" +#include "activelook_client.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ @@ -501,9 +502,6 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void *p_Pckt) APP_DBG_MSG("\r\n\r** DISCONNECTION EVENT OF END DEVICE 1 \n\r"); BleApplicationContext.EndDevice_Connection_Status[0] = APP_BLE_IDLE; BleApplicationContext.connectionHandleEndDevice1 = 0xFFFF; - HandleNotification.Custom_Evt_Opcode = CUSTOM_DISCON_DEV_1_HANDLE_EVT; - HandleNotification.ConnectionHandle = 0xFFFF; - Custom_APP_Notification(&HandleNotification); } if (p_disconnection_complete_event->Connection_Handle == BleApplicationContext.connectionHandleCentral) @@ -557,23 +555,10 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void *p_Pckt) role = p_enhanced_connection_complete_event->Role; if (role == 0x00) { /* ROLE CENTRAL */ - /* Inform Application it is End Device 1 */ - APP_DBG_MSG("-- CONNECTION SUCCESS WITH END DEVICE 1\n\r"); - BleApplicationContext.EndDevice_Connection_Status[0] = APP_BLE_CONNECTED; - BleApplicationContext.connectionHandleEndDevice1 = connection_handle; - HandleNotification.Custom_Evt_Opcode = CUSTOM_CONN_DEV_1_HANDLE_EVT; - HandleNotification.ConnectionHandle = connection_handle; - Custom_APP_Notification(&HandleNotification); - result = aci_gatt_disc_all_primary_services(BleApplicationContext.connectionHandleEndDevice1); - if (result == BLE_STATUS_SUCCESS) - { - APP_DBG_MSG("\r\n\r** GATT SERVICES & CHARACTERISTICS DISCOVERY \n\r"); - APP_DBG_MSG("* GATT : Start Searching Primary Services \r\n\r"); - } - else - { - APP_DBG_MSG("BLE_CTRL_App_Notification(), All services discovery Failed \r\n\r"); - } + APP_DBG_MSG("-- CONNECTION SUCCESS WITH END DEVICE 1\n\r"); + BleApplicationContext.EndDevice_Connection_Status[0] = APP_BLE_CONNECTED; + BleApplicationContext.connectionHandleEndDevice1 = connection_handle; + FS_Activelook_Client_StartDiscovery(connection_handle); } else { @@ -833,6 +818,10 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void *p_Pckt) break; /* USER CODE END BLUE_EVT */ } + + /* AFTER you handle your own cases, also pass the event to the ActiveLook client. */ + FS_Activelook_Client_EventHandler((void*)p_blecore_evt, HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE); + break; /* HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE */ /* USER CODE BEGIN EVENT_PCKT */ diff --git a/STM32_WPAN/App/custom_app.c b/STM32_WPAN/App/custom_app.c index 2bcad59..aaf603a 100644 --- a/STM32_WPAN/App/custom_app.c +++ b/STM32_WPAN/App/custom_app.c @@ -121,9 +121,7 @@ static void Custom_Start_result_Send_Indication(void); /* USER CODE BEGIN PFP */ static void Custom_CRS_OnConnect(Custom_App_ConnHandle_Not_evt_t *pNotification); -static void Custom_Activelook_OnConnect(Custom_App_ConnHandle_Not_evt_t *pNotification); static void Custom_CRS_OnDisconnect(void); -static void Custom_Activelook_OnDisconnect(void); static void Custom_CRS_OnRxWrite(Custom_STM_App_Notification_evt_t *pNotification); static void Custom_Start_OnControlWrite(Custom_STM_App_Notification_evt_t *pNotification); static void Custom_App_Timeout(void); @@ -293,24 +291,12 @@ void Custom_APP_Notification(Custom_App_ConnHandle_Not_evt_t *pNotification) /* USER CODE END CUSTOM_CONN_HANDLE_EVT */ break; - case CUSTOM_CONN_DEV_1_HANDLE_EVT : - /* USER CODE BEGIN CUSTOM_CONN_DEV_1_HANDLE_EVT */ - Custom_Activelook_OnConnect(pNotification); - /* USER CODE END CUSTOM_CONN_DEV_1_HANDLE_EVT */ - break; - case CUSTOM_DISCON_HANDLE_EVT : /* USER CODE BEGIN CUSTOM_DISCON_HANDLE_EVT */ Custom_CRS_OnDisconnect(); /* USER CODE END CUSTOM_DISCON_HANDLE_EVT */ break; - case CUSTOM_DISCON_DEV_1_HANDLE_EVT : - /* USER CODE BEGIN CUSTOM_DISCON_DEV_1_HANDLE_EVT */ - Custom_Activelook_OnDisconnect(); - /* USER CODE END CUSTOM_DISCON_DEV_1_HANDLE_EVT */ - break; - default: /* USER CODE BEGIN CUSTOM_APP_Notification_default */ @@ -576,11 +562,6 @@ static void Custom_CRS_OnConnect(Custom_App_ConnHandle_Not_evt_t *pNotification) HW_TS_Start(timeout_timer_id, TIMEOUT_TICKS); } -static void Custom_Activelook_OnConnect(Custom_App_ConnHandle_Not_evt_t *pNotification) -{ - -} - static void Custom_CRS_OnDisconnect(void) { // Stop timeout timer @@ -593,11 +574,6 @@ static void Custom_CRS_OnDisconnect(void) UTIL_SEQ_SetTask(1< Date: Sat, 15 Feb 2025 19:24:48 -0700 Subject: [PATCH 18/81] Clear display and show hello world --- FlySight/activelook.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/FlySight/activelook.c b/FlySight/activelook.c index ced714a..1ff695a 100644 --- a/FlySight/activelook.c +++ b/FlySight/activelook.c @@ -78,21 +78,32 @@ static void FS_Activelook_SendHelloWorld(void) } uint8_t packet[26]; - uint8_t index = 0; + uint8_t index; + index = 0; + packet[index++] = 0xFF; + packet[index++] = 0x01; // 'clear' + packet[index++] = 0x00; + packet[index++] = 5; // total length + packet[index++] = 0xAA; + + APP_DBG_MSG("Activelook: Clearing screen\n"); + FS_Activelook_Client_WriteWithoutResp(packet, index); + + index = 0; packet[index++] = 0xFF; packet[index++] = 0x37; // 'txt' packet[index++] = 0x00; packet[index++] = 26; // total length - // s16 x=0, y=0 - packet[index++] = 0x00; packet[index++] = 0x00; - packet[index++] = 0x00; packet[index++] = 0x00; + // s16 x=255, y=128 + packet[index++] = 0; packet[index++] = 255; + packet[index++] = 0; packet[index++] = 128; - // rotation=0, font=1, color=1 - packet[index++] = 0x00; - packet[index++] = 0x01; - packet[index++] = 0x01; + // rotation=0, font=2, color=15 + packet[index++] = 4; + packet[index++] = 2; + packet[index++] = 15; const char *text = "Hello, world!"; size_t text_len = strlen(text) + 1; @@ -101,6 +112,6 @@ static void FS_Activelook_SendHelloWorld(void) packet[index++] = 0xAA; - APP_DBG_MSG("Activelook: Sending Hello, world!\n"); - FS_Activelook_Client_WriteWithoutResp(packet, sizeof(packet)); + APP_DBG_MSG("Activelook: Sending Hello, world\n"); + FS_Activelook_Client_WriteWithoutResp(packet, index); } From 6f66e51e92531d96daf914aaca349df25e9861d1 Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Sat, 15 Feb 2025 19:34:34 -0700 Subject: [PATCH 19/81] Make capitalization of ActiveLook consistent --- FlySight/active_mode.c | 8 ++--- FlySight/activelook.c | 40 ++++++++++++------------- FlySight/activelook.h | 4 +-- STM32_WPAN/App/activelook_client.c | 48 +++++++++++++++--------------- STM32_WPAN/App/activelook_client.h | 14 ++++----- STM32_WPAN/App/app_ble.c | 4 +-- 6 files changed, 59 insertions(+), 59 deletions(-) diff --git a/FlySight/active_mode.c b/FlySight/active_mode.c index 1e82b06..ae4bf39 100644 --- a/FlySight/active_mode.c +++ b/FlySight/active_mode.c @@ -48,8 +48,8 @@ void FS_ActiveMode_Init(void) uint8_t enable_flags; bool isSystemHealthy = true; - /* Initialize Activelook interface */ - FS_Activelook_Init(); + /* Initialize ActiveLook interface */ + FS_ActiveLook_Init(); /* Initialize FatFS */ FS_ResourceManager_RequestResource(FS_RESOURCE_FATFS); @@ -208,8 +208,8 @@ void FS_ActiveMode_Init(void) void FS_ActiveMode_DeInit(void) { - /* De-initialize Activelook interface */ - FS_Activelook_DeInit(); + /* De-initialize ActiveLook interface */ + FS_ActiveLook_DeInit(); /* Disable controller */ FS_ActiveControl_DeInit(); diff --git a/FlySight/activelook.c b/FlySight/activelook.c index 1ff695a..966c337 100644 --- a/FlySight/activelook.c +++ b/FlySight/activelook.c @@ -30,50 +30,50 @@ #include /* Forward declaration */ -static void OnActivelookDiscoveryComplete(void); -static void FS_Activelook_SendHelloWorld(void); +static void OnActiveLookDiscoveryComplete(void); +static void FS_ActiveLook_SendHelloWorld(void); /* We'll define the callback struct */ -static const FS_Activelook_ClientCb_t s_alk_cb = +static const FS_ActiveLook_ClientCb_t s_alk_cb = { - .OnDiscoveryComplete = OnActivelookDiscoveryComplete + .OnDiscoveryComplete = OnActiveLookDiscoveryComplete }; -void FS_Activelook_Init(void) +void FS_ActiveLook_Init(void) { /* Register the callback before scanning/connecting. */ - FS_Activelook_Client_RegisterCb(&s_alk_cb); + FS_ActiveLook_Client_RegisterCb(&s_alk_cb); /* Start scanning for BLE peripherals, - leading eventually to connect to the Activelook device. */ + leading eventually to connect to the ActiveLook device. */ UTIL_SEQ_SetTask(1 << CFG_TASK_START_SCAN_ID, CFG_SCH_PRIO_0); } -void FS_Activelook_DeInit(void) +void FS_ActiveLook_DeInit(void) { /* Disconnect from BLE device #1 */ UTIL_SEQ_SetTask(1 << CFG_TASK_DISCONN_DEV_1_ID, CFG_SCH_PRIO_0); } /* This function is called once the client discovered the Rx characteristic. */ -static void OnActivelookDiscoveryComplete(void) +static void OnActiveLookDiscoveryComplete(void) { - APP_DBG_MSG("Activelook: Discovery complete. Let's do 'Hello, world!'...\n"); + APP_DBG_MSG("ActiveLook: Discovery complete. Let's do 'Hello, world!'...\n"); /* Example: call your HelloWorld sending function */ - FS_Activelook_SendHelloWorld(); + FS_ActiveLook_SendHelloWorld(); } /* * Example function that builds the 'txt' command to display "Hello, world!" - * as you had previously. We can call it from OnActivelookDiscoveryComplete - * or any time later, as long as FS_Activelook_Client_IsReady() is true. + * as you had previously. We can call it from OnActiveLookDiscoveryComplete + * or any time later, as long as FS_ActiveLook_Client_IsReady() is true. */ -static void FS_Activelook_SendHelloWorld(void) +static void FS_ActiveLook_SendHelloWorld(void) { - if (!FS_Activelook_Client_IsReady()) + if (!FS_ActiveLook_Client_IsReady()) { - APP_DBG_MSG("Activelook: Not ready, cannot send Hello!\n"); + APP_DBG_MSG("ActiveLook: Not ready, cannot send Hello!\n"); return; } @@ -87,8 +87,8 @@ static void FS_Activelook_SendHelloWorld(void) packet[index++] = 5; // total length packet[index++] = 0xAA; - APP_DBG_MSG("Activelook: Clearing screen\n"); - FS_Activelook_Client_WriteWithoutResp(packet, index); + APP_DBG_MSG("ActiveLook: Clearing screen\n"); + FS_ActiveLook_Client_WriteWithoutResp(packet, index); index = 0; packet[index++] = 0xFF; @@ -112,6 +112,6 @@ static void FS_Activelook_SendHelloWorld(void) packet[index++] = 0xAA; - APP_DBG_MSG("Activelook: Sending Hello, world\n"); - FS_Activelook_Client_WriteWithoutResp(packet, index); + APP_DBG_MSG("ActiveLook: Sending Hello, world\n"); + FS_ActiveLook_Client_WriteWithoutResp(packet, index); } diff --git a/FlySight/activelook.h b/FlySight/activelook.h index 1608d9b..5952e07 100644 --- a/FlySight/activelook.h +++ b/FlySight/activelook.h @@ -24,7 +24,7 @@ #ifndef ACTIVELOOK_H_ #define ACTIVELOOK_H_ -void FS_Activelook_Init(void); -void FS_Activelook_DeInit(void); +void FS_ActiveLook_Init(void); +void FS_ActiveLook_DeInit(void); #endif /* ACTIVELOOK_H_ */ diff --git a/STM32_WPAN/App/activelook_client.c b/STM32_WPAN/App/activelook_client.c index b1b91d7..0ba61bd 100644 --- a/STM32_WPAN/App/activelook_client.c +++ b/STM32_WPAN/App/activelook_client.c @@ -34,7 +34,7 @@ | ((uint16_t)(*(((uint8_t *)(ptr))+1)) << 8U) ) #endif -/* 16-byte UUID for the Activelook Commands service +/* 16-byte UUID for the ActiveLook Commands service * 0783B03E-8535-B5A0-7140-A304D2495CB7 */ static const uint8_t ACTIVELK_SERVICE_UUID[16] = { @@ -73,15 +73,15 @@ typedef struct uint8_t rxCharFound; uint16_t rxCharHandle; - const FS_Activelook_ClientCb_t *cb; -} FS_Activelook_Client_Context_t; + const FS_ActiveLook_ClientCb_t *cb; +} FS_ActiveLook_Client_Context_t; -static FS_Activelook_Client_Context_t g_ctx; +static FS_ActiveLook_Client_Context_t g_ctx; /****************************************************************************** * Initialize ******************************************************************************/ -void FS_Activelook_Client_Init(void) +void FS_ActiveLook_Client_Init(void) { memset(&g_ctx, 0, sizeof(g_ctx)); g_ctx.discState = DISC_STATE_IDLE; @@ -90,7 +90,7 @@ void FS_Activelook_Client_Init(void) /****************************************************************************** * Register optional callback interface ******************************************************************************/ -void FS_Activelook_Client_RegisterCb(const FS_Activelook_ClientCb_t *cb) +void FS_ActiveLook_Client_RegisterCb(const FS_ActiveLook_ClientCb_t *cb) { g_ctx.cb = cb; } @@ -98,7 +98,7 @@ void FS_Activelook_Client_RegisterCb(const FS_Activelook_ClientCb_t *cb) /****************************************************************************** * Start discovery (including MTU exchange) after connecting ******************************************************************************/ -void FS_Activelook_Client_StartDiscovery(uint16_t connectionHandle) +void FS_ActiveLook_Client_StartDiscovery(uint16_t connectionHandle) { g_ctx.connHandle = connectionHandle; g_ctx.discState = DISC_STATE_EXCH_MTU; /* <--- Start with MTU exchange */ @@ -112,11 +112,11 @@ void FS_Activelook_Client_StartDiscovery(uint16_t connectionHandle) tBleStatus s = aci_gatt_exchange_config(connectionHandle); if (s == BLE_STATUS_SUCCESS) { - APP_DBG_MSG("ActivelookClient: Requesting MTU exchange...\n"); + APP_DBG_MSG("ActiveLook_Client: Requesting MTU exchange...\n"); } else { - APP_DBG_MSG("ActivelookClient: aci_gatt_exchange_config fail=0x%02X\n", s); + APP_DBG_MSG("ActiveLook_Client: aci_gatt_exchange_config fail=0x%02X\n", s); g_ctx.discState = DISC_STATE_IDLE; // stop } } @@ -124,7 +124,7 @@ void FS_Activelook_Client_StartDiscovery(uint16_t connectionHandle) /****************************************************************************** * Event Handler ******************************************************************************/ -void FS_Activelook_Client_EventHandler(void *p_blecore_evt, uint8_t hci_event_evt_code) +void FS_ActiveLook_Client_EventHandler(void *p_blecore_evt, uint8_t hci_event_evt_code) { evt_blecore_aci *blecore_evt = (evt_blecore_aci*) p_blecore_evt; @@ -138,7 +138,7 @@ void FS_Activelook_Client_EventHandler(void *p_blecore_evt, uint8_t hci_event_ev /* This event indicates the peripheral accepted some MTU. */ aci_att_exchange_mtu_resp_event_rp0 *mtu_resp = (aci_att_exchange_mtu_resp_event_rp0*) blecore_evt->data; - APP_DBG_MSG("ActivelookClient: ACI_ATT_EXCHANGE_MTU_RESP, final MTU=%d\r\n", + APP_DBG_MSG("ActiveLook_Client: ACI_ATT_EXCHANGE_MTU_RESP, final MTU=%d\r\n", mtu_resp->Server_RX_MTU); /* We still must wait for ACI_GATT_PROC_COMPLETE_VSEVT_CODE, which means the procedure is fully done in the stack. */ @@ -165,11 +165,11 @@ void FS_Activelook_Client_EventHandler(void *p_blecore_evt, uint8_t hci_event_ev tBleStatus s = aci_gatt_disc_all_primary_services(g_ctx.connHandle); if (s == BLE_STATUS_SUCCESS) { - APP_DBG_MSG("ActivelookClient: MTU ok, now discovering service...\n"); + APP_DBG_MSG("ActiveLook_Client: MTU ok, now discovering service...\n"); } else { - APP_DBG_MSG("ActivelookClient: disc_all_primary_services fail=0x%02X\n", s); + APP_DBG_MSG("ActiveLook_Client: disc_all_primary_services fail=0x%02X\n", s); g_ctx.discState = DISC_STATE_IDLE; } } @@ -185,17 +185,17 @@ void FS_Activelook_Client_EventHandler(void *p_blecore_evt, uint8_t hci_event_ev g_ctx.serviceEndHandle ); if (s == BLE_STATUS_SUCCESS) { - APP_DBG_MSG("ActivelookClient: Discovering chars...\n"); + APP_DBG_MSG("ActiveLook_Client: Discovering chars...\n"); } else { - APP_DBG_MSG("ActivelookClient: disc_all_char_of_service fail=0x%02X\n", s); + APP_DBG_MSG("ActiveLook_Client: disc_all_char_of_service fail=0x%02X\n", s); g_ctx.discState = DISC_STATE_IDLE; } } else { - APP_DBG_MSG("ActivelookClient: service not found.\n"); + APP_DBG_MSG("ActiveLook_Client: service not found.\n"); g_ctx.discState = DISC_STATE_IDLE; } } @@ -203,7 +203,7 @@ void FS_Activelook_Client_EventHandler(void *p_blecore_evt, uint8_t hci_event_ev { /* Done discovering characteristics. */ g_ctx.discState = DISC_STATE_IDLE; - APP_DBG_MSG("ActivelookClient: Char discovery complete, Rx=0x%04X\n", + APP_DBG_MSG("ActiveLook_Client: Char discovery complete, Rx=0x%04X\n", g_ctx.rxCharHandle); if (g_ctx.rxCharFound && g_ctx.cb && g_ctx.cb->OnDiscoveryComplete) @@ -233,7 +233,7 @@ void FS_Activelook_Client_EventHandler(void *p_blecore_evt, uint8_t hci_event_ev if (memcmp(uuid, ACTIVELK_SERVICE_UUID, 16) == 0) { - APP_DBG_MSG("ActivelookClient: Found Service 0x%04X–0x%04X\n", startHdl, endHdl); + APP_DBG_MSG("ActiveLook_Client: Found Service 0x%04X–0x%04X\n", startHdl, endHdl); g_ctx.serviceFound = 1; g_ctx.serviceStartHandle = startHdl; g_ctx.serviceEndHandle = endHdl; @@ -267,7 +267,7 @@ void FS_Activelook_Client_EventHandler(void *p_blecore_evt, uint8_t hci_event_ev if (memcmp(uuid, ACTIVELK_RX_CHAR_UUID, 16) == 0) { - APP_DBG_MSG("ActivelookClient: Found RxChar=0x%04X\n", valH); + APP_DBG_MSG("ActiveLook_Client: Found RxChar=0x%04X\n", valH); g_ctx.rxCharFound = 1; g_ctx.rxCharHandle = valH; } @@ -284,7 +284,7 @@ void FS_Activelook_Client_EventHandler(void *p_blecore_evt, uint8_t hci_event_ev /****************************************************************************** * Check if Rx handle is ready ******************************************************************************/ -uint8_t FS_Activelook_Client_IsReady(void) +uint8_t FS_ActiveLook_Client_IsReady(void) { return (g_ctx.rxCharFound && g_ctx.rxCharHandle != 0); } @@ -292,11 +292,11 @@ uint8_t FS_Activelook_Client_IsReady(void) /****************************************************************************** * Write data to Rx characteristic (WWR) ******************************************************************************/ -tBleStatus FS_Activelook_Client_WriteWithoutResp(const uint8_t *data, uint16_t length) +tBleStatus FS_ActiveLook_Client_WriteWithoutResp(const uint8_t *data, uint16_t length) { - if (!FS_Activelook_Client_IsReady()) + if (!FS_ActiveLook_Client_IsReady()) { - APP_DBG_MSG("ActivelookClient: Not ready, no Rx handle.\n"); + APP_DBG_MSG("ActiveLook_Client: Not ready, no Rx handle.\n"); return BLE_STATUS_FAILED; } @@ -306,7 +306,7 @@ tBleStatus FS_Activelook_Client_WriteWithoutResp(const uint8_t *data, uint16_t l (uint8_t*)data); if (s != BLE_STATUS_SUCCESS) { - APP_DBG_MSG("ActivelookClient: WWR error=0x%02X\n", s); + APP_DBG_MSG("ActiveLook_Client: WWR error=0x%02X\n", s); } return s; } diff --git a/STM32_WPAN/App/activelook_client.h b/STM32_WPAN/App/activelook_client.h index 6547fa0..fb302ff 100644 --- a/STM32_WPAN/App/activelook_client.h +++ b/STM32_WPAN/App/activelook_client.h @@ -30,15 +30,15 @@ typedef struct { void (*OnDiscoveryComplete)(void); -} FS_Activelook_ClientCb_t; +} FS_ActiveLook_ClientCb_t; -void FS_Activelook_Client_Init(void); -void FS_Activelook_Client_RegisterCb(const FS_Activelook_ClientCb_t *cb); -void FS_Activelook_Client_StartDiscovery(uint16_t connectionHandle); +void FS_ActiveLook_Client_Init(void); +void FS_ActiveLook_Client_RegisterCb(const FS_ActiveLook_ClientCb_t *cb); +void FS_ActiveLook_Client_StartDiscovery(uint16_t connectionHandle); -void FS_Activelook_Client_EventHandler(void *p_blecore_evt, uint8_t hci_event_evt_code); +void FS_ActiveLook_Client_EventHandler(void *p_blecore_evt, uint8_t hci_event_evt_code); -uint8_t FS_Activelook_Client_IsReady(void); -tBleStatus FS_Activelook_Client_WriteWithoutResp(const uint8_t *data, uint16_t length); +uint8_t FS_ActiveLook_Client_IsReady(void); +tBleStatus FS_ActiveLook_Client_WriteWithoutResp(const uint8_t *data, uint16_t length); #endif /* ACTIVELOOK_CLIENT_H */ diff --git a/STM32_WPAN/App/app_ble.c b/STM32_WPAN/App/app_ble.c index 2519c5b..98a8d44 100644 --- a/STM32_WPAN/App/app_ble.c +++ b/STM32_WPAN/App/app_ble.c @@ -558,7 +558,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void *p_Pckt) APP_DBG_MSG("-- CONNECTION SUCCESS WITH END DEVICE 1\n\r"); BleApplicationContext.EndDevice_Connection_Status[0] = APP_BLE_CONNECTED; BleApplicationContext.connectionHandleEndDevice1 = connection_handle; - FS_Activelook_Client_StartDiscovery(connection_handle); + FS_ActiveLook_Client_StartDiscovery(connection_handle); } else { @@ -820,7 +820,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void *p_Pckt) } /* AFTER you handle your own cases, also pass the event to the ActiveLook client. */ - FS_Activelook_Client_EventHandler((void*)p_blecore_evt, HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE); + FS_ActiveLook_Client_EventHandler((void*)p_blecore_evt, HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE); break; /* HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE */ From abb555a7f17b5b7e1221464204133519f1dffe73 Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Sat, 15 Feb 2025 21:55:45 -0700 Subject: [PATCH 20/81] Display GNSS TOW on ActiveLook --- FlySight/active_control.c | 4 ++ FlySight/activelook.c | 117 +++++++++++++++++++------------------- FlySight/activelook.h | 4 ++ 3 files changed, 67 insertions(+), 58 deletions(-) diff --git a/FlySight/active_control.c b/FlySight/active_control.c index 8364a07..9a076dd 100644 --- a/FlySight/active_control.c +++ b/FlySight/active_control.c @@ -24,6 +24,7 @@ #include #include "main.h" +#include "activelook.h" #include "app_common.h" #include "audio_control.h" #include "baro.h" @@ -227,6 +228,9 @@ void FS_ActiveControl_DataReady_Callback(void) // Update BLE characteristic Custom_GNSS_Update(data); + // Update ActiveLook glasses + FS_ActiveLook_GNSS_Update(data); + hasFix = (data->gpsFix == 3); } diff --git a/FlySight/activelook.c b/FlySight/activelook.c index 966c337..a2e82fb 100644 --- a/FlySight/activelook.c +++ b/FlySight/activelook.c @@ -29,14 +29,16 @@ #include "stm32_seq.h" #include +/* State */ +static uint8_t clear_display = 0; + /* Forward declaration */ static void OnActiveLookDiscoveryComplete(void); -static void FS_ActiveLook_SendHelloWorld(void); /* We'll define the callback struct */ static const FS_ActiveLook_ClientCb_t s_alk_cb = { - .OnDiscoveryComplete = OnActiveLookDiscoveryComplete + .OnDiscoveryComplete = OnActiveLookDiscoveryComplete }; void FS_ActiveLook_Init(void) @@ -46,72 +48,71 @@ void FS_ActiveLook_Init(void) /* Start scanning for BLE peripherals, leading eventually to connect to the ActiveLook device. */ - UTIL_SEQ_SetTask(1 << CFG_TASK_START_SCAN_ID, CFG_SCH_PRIO_0); + UTIL_SEQ_SetTask(1 << CFG_TASK_START_SCAN_ID, CFG_SCH_PRIO_0); } void FS_ActiveLook_DeInit(void) { - /* Disconnect from BLE device #1 */ - UTIL_SEQ_SetTask(1 << CFG_TASK_DISCONN_DEV_1_ID, CFG_SCH_PRIO_0); + /* Disconnect from BLE device #1 */ + UTIL_SEQ_SetTask(1 << CFG_TASK_DISCONN_DEV_1_ID, CFG_SCH_PRIO_0); } /* This function is called once the client discovered the Rx characteristic. */ static void OnActiveLookDiscoveryComplete(void) { - APP_DBG_MSG("ActiveLook: Discovery complete. Let's do 'Hello, world!'...\n"); - - /* Example: call your HelloWorld sending function */ - FS_ActiveLook_SendHelloWorld(); + APP_DBG_MSG("ActiveLook: Discovery complete\n"); + clear_display = 1; } -/* - * Example function that builds the 'txt' command to display "Hello, world!" - * as you had previously. We can call it from OnActiveLookDiscoveryComplete - * or any time later, as long as FS_ActiveLook_Client_IsReady() is true. - */ -static void FS_ActiveLook_SendHelloWorld(void) +void FS_ActiveLook_GNSS_Update(const FS_GNSS_Data_t *current) { - if (!FS_ActiveLook_Client_IsReady()) - { - APP_DBG_MSG("ActiveLook: Not ready, cannot send Hello!\n"); - return; - } - - uint8_t packet[26]; - uint8_t index; - - index = 0; - packet[index++] = 0xFF; - packet[index++] = 0x01; // 'clear' - packet[index++] = 0x00; - packet[index++] = 5; // total length - packet[index++] = 0xAA; - - APP_DBG_MSG("ActiveLook: Clearing screen\n"); - FS_ActiveLook_Client_WriteWithoutResp(packet, index); - - index = 0; - packet[index++] = 0xFF; - packet[index++] = 0x37; // 'txt' - packet[index++] = 0x00; - packet[index++] = 26; // total length - - // s16 x=255, y=128 - packet[index++] = 0; packet[index++] = 255; - packet[index++] = 0; packet[index++] = 128; - - // rotation=0, font=2, color=15 - packet[index++] = 4; - packet[index++] = 2; - packet[index++] = 15; - - const char *text = "Hello, world!"; - size_t text_len = strlen(text) + 1; - memcpy(&packet[index], text, text_len); - index += text_len; - - packet[index++] = 0xAA; - - APP_DBG_MSG("ActiveLook: Sending Hello, world\n"); - FS_ActiveLook_Client_WriteWithoutResp(packet, index); + if (!FS_ActiveLook_Client_IsReady()) + return; + + uint8_t packet[26]; + uint8_t index, length_index; + + if (clear_display) + { + clear_display = 0; + + index = 0; + packet[index++] = 0xFF; + packet[index++] = 0x01; // 'clear' + packet[index++] = 0x00; + packet[index++] = 5; // total length + packet[index++] = 0xAA; + + APP_DBG_MSG("ActiveLook: Clearing screen\n"); + FS_ActiveLook_Client_WriteWithoutResp(packet, index); + } + else + { + index = 0; + packet[index++] = 0xFF; + packet[index++] = 0x37; // 'txt' + packet[index++] = 0x00; + length_index = index++; + + // s16 x=255, y=128 + packet[index++] = 0; packet[index++] = 255; + packet[index++] = 0; packet[index++] = 128; + + // rotation=4, font=2, color=15 + packet[index++] = 4; + packet[index++] = 2; + packet[index++] = 15; + + char text[14]; + snprintf(text, 14, "t: %3lu", (current->iTOW / 1000) % 1000); + size_t text_len = strlen(text) + 1; + memcpy(&packet[index], text, text_len); + index += text_len; + + packet[index++] = 0xAA; + packet[length_index] = index; // total length + + APP_DBG_MSG("ActiveLook: Updating GNSS\n"); + FS_ActiveLook_Client_WriteWithoutResp(packet, index); + } } diff --git a/FlySight/activelook.h b/FlySight/activelook.h index 5952e07..ef2a25f 100644 --- a/FlySight/activelook.h +++ b/FlySight/activelook.h @@ -24,7 +24,11 @@ #ifndef ACTIVELOOK_H_ #define ACTIVELOOK_H_ +#include "gnss.h" + void FS_ActiveLook_Init(void); void FS_ActiveLook_DeInit(void); +void FS_ActiveLook_GNSS_Update(const FS_GNSS_Data_t *current); + #endif /* ACTIVELOOK_H_ */ From 4718e1efd84e4913f30d2d6836325596984b245e Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Sun, 16 Feb 2025 13:10:34 -0700 Subject: [PATCH 21/81] Add state to ActiveLook update --- FlySight/activelook.c | 235 ++++++++++++++++++++++++++---------------- 1 file changed, 147 insertions(+), 88 deletions(-) diff --git a/FlySight/activelook.c b/FlySight/activelook.c index a2e82fb..4e96f23 100644 --- a/FlySight/activelook.c +++ b/FlySight/activelook.c @@ -1,26 +1,3 @@ -/*************************************************************************** -** ** -** FlySight 2 firmware ** -** Copyright 2025 Bionic Avionics Inc. ** -** ** -** 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 . ** -** ** -**************************************************************************** -** Contact: Bionic Avionics Inc. ** -** Website: http://flysight.ca/ ** -****************************************************************************/ - #include "main.h" #include "activelook.h" #include "activelook_client.h" @@ -29,90 +6,172 @@ #include "stm32_seq.h" #include -/* State */ -static uint8_t clear_display = 0; - -/* Forward declaration */ +/* 1) Define the states for our simple state machine */ +typedef enum +{ + AL_STATE_INIT = 0, /* Not discovered yet */ + AL_STATE_CLEAR, /* Need to send "clear" command next */ + AL_STATE_READY, /* Ready to send periodic updates */ + AL_STATE_UPDATE /* Need to send a new GNSS update */ +} FS_ActiveLook_State_t; + +/* 2) Keep track of our current state and the last GNSS data */ +static FS_ActiveLook_State_t s_state = AL_STATE_INIT; +static FS_GNSS_Data_t s_gnssDataCache; + +/* Forward references */ +static void FS_ActiveLook_Task(void); static void OnActiveLookDiscoveryComplete(void); -/* We'll define the callback struct */ +/* Callback struct for the ActiveLook_Client */ static const FS_ActiveLook_ClientCb_t s_alk_cb = { - .OnDiscoveryComplete = OnActiveLookDiscoveryComplete + .OnDiscoveryComplete = OnActiveLookDiscoveryComplete }; +/****************************************************************************** + * Initialization + *****************************************************************************/ void FS_ActiveLook_Init(void) { - /* Register the callback before scanning/connecting. */ - FS_ActiveLook_Client_RegisterCb(&s_alk_cb); + /* Register the callback for discovery */ + FS_ActiveLook_Client_RegisterCb(&s_alk_cb); + + /* Register our local "task" with the sequencer */ + UTIL_SEQ_RegTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, UTIL_SEQ_RFU, FS_ActiveLook_Task); - /* Start scanning for BLE peripherals, - leading eventually to connect to the ActiveLook device. */ - UTIL_SEQ_SetTask(1 << CFG_TASK_START_SCAN_ID, CFG_SCH_PRIO_0); + /* Start in the INIT state */ + s_state = AL_STATE_INIT; + + /* If you want to automatically scan/connect to ActiveLook now: */ + UTIL_SEQ_SetTask(1 << CFG_TASK_START_SCAN_ID, CFG_SCH_PRIO_0); } +/****************************************************************************** + * De-initialization + *****************************************************************************/ void FS_ActiveLook_DeInit(void) { - /* Disconnect from BLE device #1 */ - UTIL_SEQ_SetTask(1 << CFG_TASK_DISCONN_DEV_1_ID, CFG_SCH_PRIO_0); + /* If we want to disconnect from the device #1: */ + UTIL_SEQ_SetTask(1 << CFG_TASK_DISCONN_DEV_1_ID, CFG_SCH_PRIO_0); } -/* This function is called once the client discovered the Rx characteristic. */ +/****************************************************************************** + * Called by the ActiveLook client once the Rx char is discovered. + * We'll move the state to CLEAR, meaning "send Clear next" and schedule the task. + *****************************************************************************/ static void OnActiveLookDiscoveryComplete(void) { - APP_DBG_MSG("ActiveLook: Discovery complete\n"); - clear_display = 1; + APP_DBG_MSG("ActiveLook: Discovery complete\n"); + s_state = AL_STATE_CLEAR; /* We want to send a 'clear' next. */ + UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); } +/****************************************************************************** + * Called from your GNSS logic whenever new GNSS data is available. + * If we're in READY state, copy the data, set state=UPDATE, schedule the task. + * If not ready, we skip it. + *****************************************************************************/ void FS_ActiveLook_GNSS_Update(const FS_GNSS_Data_t *current) { - if (!FS_ActiveLook_Client_IsReady()) - return; - - uint8_t packet[26]; - uint8_t index, length_index; - - if (clear_display) - { - clear_display = 0; - - index = 0; - packet[index++] = 0xFF; - packet[index++] = 0x01; // 'clear' - packet[index++] = 0x00; - packet[index++] = 5; // total length - packet[index++] = 0xAA; - - APP_DBG_MSG("ActiveLook: Clearing screen\n"); - FS_ActiveLook_Client_WriteWithoutResp(packet, index); - } - else - { - index = 0; - packet[index++] = 0xFF; - packet[index++] = 0x37; // 'txt' - packet[index++] = 0x00; - length_index = index++; - - // s16 x=255, y=128 - packet[index++] = 0; packet[index++] = 255; - packet[index++] = 0; packet[index++] = 128; - - // rotation=4, font=2, color=15 - packet[index++] = 4; - packet[index++] = 2; - packet[index++] = 15; - - char text[14]; - snprintf(text, 14, "t: %3lu", (current->iTOW / 1000) % 1000); - size_t text_len = strlen(text) + 1; - memcpy(&packet[index], text, text_len); - index += text_len; - - packet[index++] = 0xAA; - packet[length_index] = index; // total length - - APP_DBG_MSG("ActiveLook: Updating GNSS\n"); - FS_ActiveLook_Client_WriteWithoutResp(packet, index); - } + /* We only want to queue an update if we're in AL_STATE_READY. */ + if (!FS_ActiveLook_Client_IsReady()) + { + /* Not actually ready to send. It's discovered, but + * maybe the BLE link has disconnected. Possibly do nothing here. */ + return; + } + + if (s_state == AL_STATE_READY) + { + /* Copy the GNSS data for later use by FS_ActiveLook_Task() */ + s_gnssDataCache = *current; + + /* Now we want to send an update next time the task runs. */ + s_state = AL_STATE_UPDATE; + UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); + } + else + { + /* If we are still in CLEAR or UPDATE, or if not discovered, skip. */ + APP_DBG_MSG("ActiveLook_GNSS_Update: Not in READY, ignoring.\n"); + } +} + +/****************************************************************************** + * This function is our "Task" in the sequencer, managing the state machine. + * Each time we set s_state, we do UTIL_SEQ_SetTask(...). That eventually + * calls here, which checks the current state and does the appropriate BLE write. + *****************************************************************************/ +static void FS_ActiveLook_Task(void) +{ + /* We'll build short packets on the stack. */ + uint8_t packet[64]; + uint8_t idx = 0; + + switch (s_state) + { + case AL_STATE_INIT: + /* Haven't discovered the characteristic yet. Nothing to do. */ + break; + + case AL_STATE_CLEAR: + /* Build a 'clear' command. (Check your doc for exact ID or format.) + * Example: 0xFF 0x35 0x00 0x08 0x00 0x00 0x00 0xAA + * We'll do a simple 5-byte version if your firmware supports it. */ + idx = 0; + packet[idx++] = 0xFF; // Start + packet[idx++] = 0x35; // Clear command ID (for example) + packet[idx++] = 0x00; // Format + packet[idx++] = 5; // Packet length + packet[idx++] = 0xAA; // Footer + + APP_DBG_MSG("ActiveLook: Sending CLEAR...\n"); + FS_ActiveLook_Client_WriteWithoutResp(packet, idx); + + /* Move to READY state. Next GNSS update will cause text to be drawn. */ + s_state = AL_STATE_READY; + break; + + case AL_STATE_READY: + default: + /* No action needed. */ + break; + + case AL_STATE_UPDATE: + /* Build a small 'txt' command using s_gnssDataCache. For example: */ + + idx = 0; + packet[idx++] = 0xFF; // Start + packet[idx++] = 0x37; // "txt" command + packet[idx++] = 0x00; // Format: no query ID + const uint8_t lengthPos = idx++; // We'll fill total length at the end + + /* For example, x=255, y=128, rotation=4, font=2, color=15 */ + packet[idx++] = 0x00; packet[idx++] = 255; // x=255 (big-endian might be 0x00, 0xFF) + packet[idx++] = 0x00; packet[idx++] = 128; // y=128 + packet[idx++] = 4; // rotation + packet[idx++] = 2; // font + packet[idx++] = 15; // color + + /* Build a short text from s_gnssDataCache. For instance: */ + char text[20]; + snprintf(text, sizeof(text), "iTOW:%lu", (unsigned long)(s_gnssDataCache.iTOW/1000)); + size_t textLen = strlen(text) + 1; /* +1 for '\0' terminator */ + memcpy(&packet[idx], text, textLen); + idx += textLen; + + /* Footer */ + packet[idx++] = 0xAA; + + /* Fill in total length at 'lengthPos' */ + packet[lengthPos] = idx; + + APP_DBG_MSG("ActiveLook: Sending GNSS update...\n"); + FS_ActiveLook_Client_WriteWithoutResp(packet, idx); + + /* After sending it, return to READY. Next new GNSS data triggers another update. */ + s_state = AL_STATE_READY; + break; + } } From 1732b43248e2a994113a4af833bd1cf5cc22c8cb Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Sun, 16 Feb 2025 13:28:04 -0700 Subject: [PATCH 22/81] Write multiple lines to ActiveLook display --- FlySight/activelook.c | 234 +++++++++++++++++++++++++----------------- 1 file changed, 139 insertions(+), 95 deletions(-) diff --git a/FlySight/activelook.c b/FlySight/activelook.c index 4e96f23..3c665c6 100644 --- a/FlySight/activelook.c +++ b/FlySight/activelook.c @@ -5,17 +5,21 @@ #include "dbg_trace.h" #include "stm32_seq.h" #include +#include -/* 1) Define the states for our simple state machine */ +/*----- State machine states -----*/ typedef enum { - AL_STATE_INIT = 0, /* Not discovered yet */ - AL_STATE_CLEAR, /* Need to send "clear" command next */ - AL_STATE_READY, /* Ready to send periodic updates */ - AL_STATE_UPDATE /* Need to send a new GNSS update */ + AL_STATE_INIT = 0, /* Not discovered yet */ + AL_STATE_CLEAR, /* Need to send "clear" command */ + AL_STATE_READY, /* Idle, waiting for GNSS data */ + AL_STATE_UPDATE_LINE_1, /* GNSS arrived, show multiple lines */ + AL_STATE_UPDATE_LINE_2, + AL_STATE_UPDATE_LINE_3, + AL_STATE_UPDATE_LINE_4, } FS_ActiveLook_State_t; -/* 2) Keep track of our current state and the last GNSS data */ +/*----- Module-level static variables -----*/ static FS_ActiveLook_State_t s_state = AL_STATE_INIT; static FS_GNSS_Data_t s_gnssDataCache; @@ -23,155 +27,195 @@ static FS_GNSS_Data_t s_gnssDataCache; static void FS_ActiveLook_Task(void); static void OnActiveLookDiscoveryComplete(void); -/* Callback struct for the ActiveLook_Client */ +/* Callback struct for the ActiveLook client library */ static const FS_ActiveLook_ClientCb_t s_alk_cb = { .OnDiscoveryComplete = OnActiveLookDiscoveryComplete }; -/****************************************************************************** - * Initialization - *****************************************************************************/ -void FS_ActiveLook_Init(void) +/******************************************************************************* + * Small helper to build and send a "txt" command to the ActiveLook display + * - x,y in 16-bit big-endian + * - rotation=4, font=2, color=15 (change as desired) + * - null-terminated text + ******************************************************************************/ +static void AL_SendTxtCmd(uint16_t x, uint16_t y, const char *text) { - /* Register the callback for discovery */ - FS_ActiveLook_Client_RegisterCb(&s_alk_cb); + uint8_t packet[64]; + uint8_t idx = 0; - /* Register our local "task" with the sequencer */ - UTIL_SEQ_RegTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, UTIL_SEQ_RFU, FS_ActiveLook_Task); + /* Start byte + Command ID for "txt" = 0x37 + no Query ID = 0x00 */ + packet[idx++] = 0xFF; // start byte + packet[idx++] = 0x37; // "txt" command + packet[idx++] = 0x00; // format flags (0x00 means "no query ID") + uint8_t lengthPos = idx++; // We'll fill total packet length here at the end - /* Start in the INIT state */ - s_state = AL_STATE_INIT; + /* X coordinate (big endian) */ + packet[idx++] = (uint8_t)(x >> 8); + packet[idx++] = (uint8_t)(x & 0xFF); - /* If you want to automatically scan/connect to ActiveLook now: */ - UTIL_SEQ_SetTask(1 << CFG_TASK_START_SCAN_ID, CFG_SCH_PRIO_0); -} + /* Y coordinate (big endian) */ + packet[idx++] = (uint8_t)(y >> 8); + packet[idx++] = (uint8_t)(y & 0xFF); -/****************************************************************************** - * De-initialization - *****************************************************************************/ -void FS_ActiveLook_DeInit(void) -{ - /* If we want to disconnect from the device #1: */ - UTIL_SEQ_SetTask(1 << CFG_TASK_DISCONN_DEV_1_ID, CFG_SCH_PRIO_0); + /* rotation, font, color */ + packet[idx++] = 4; // rotation + packet[idx++] = 2; // font + packet[idx++] = 15; // color + + /* Copy in the string + its null terminator */ + size_t textLen = strlen(text) + 1; + memcpy(&packet[idx], text, textLen); + idx += textLen; + + /* Footer byte */ + packet[idx++] = 0xAA; + + /* Fill total length */ + packet[lengthPos] = idx; + + /* Now send it with Write Without Response */ + FS_ActiveLook_Client_WriteWithoutResp(packet, idx); } -/****************************************************************************** - * Called by the ActiveLook client once the Rx char is discovered. - * We'll move the state to CLEAR, meaning "send Clear next" and schedule the task. - *****************************************************************************/ +/******************************************************************************* + * Called by the ActiveLook client once the Rx characteristic is discovered. + * We'll move the state to CLEAR, meaning "clear" next, and schedule the task. + ******************************************************************************/ static void OnActiveLookDiscoveryComplete(void) { APP_DBG_MSG("ActiveLook: Discovery complete\n"); - s_state = AL_STATE_CLEAR; /* We want to send a 'clear' next. */ + s_state = AL_STATE_CLEAR; UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); } -/****************************************************************************** +/******************************************************************************* * Called from your GNSS logic whenever new GNSS data is available. - * If we're in READY state, copy the data, set state=UPDATE, schedule the task. - * If not ready, we skip it. - *****************************************************************************/ + * If we're READY, copy data, set state=UPDATE_SETUP, and schedule the task. + * Otherwise skip it. + ******************************************************************************/ void FS_ActiveLook_GNSS_Update(const FS_GNSS_Data_t *current) { - /* We only want to queue an update if we're in AL_STATE_READY. */ + /* Only queue an update if we are discovered & idle. */ if (!FS_ActiveLook_Client_IsReady()) { - /* Not actually ready to send. It's discovered, but - * maybe the BLE link has disconnected. Possibly do nothing here. */ + /* Possibly disconnected or not discovered. */ return; } if (s_state == AL_STATE_READY) { - /* Copy the GNSS data for later use by FS_ActiveLook_Task() */ + /* Cache the data for later use */ s_gnssDataCache = *current; - /* Now we want to send an update next time the task runs. */ - s_state = AL_STATE_UPDATE; + /* We'll do multiple lines */ + s_state = AL_STATE_UPDATE_LINE_1; UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); } else { - /* If we are still in CLEAR or UPDATE, or if not discovered, skip. */ - APP_DBG_MSG("ActiveLook_GNSS_Update: Not in READY, ignoring.\n"); + APP_DBG_MSG("ActiveLook_GNSS_Update: State %d, ignoring.\n", s_state); } } -/****************************************************************************** - * This function is our "Task" in the sequencer, managing the state machine. - * Each time we set s_state, we do UTIL_SEQ_SetTask(...). That eventually - * calls here, which checks the current state and does the appropriate BLE write. - *****************************************************************************/ +/******************************************************************************* + * Our main sequencer task. We handle the state machine, sending WWR commands + * without blocking. Each line is sent in a separate state so that we don't + * bombard the BLE stack with multiple WWR calls in a single pass. + ******************************************************************************/ static void FS_ActiveLook_Task(void) { - /* We'll build short packets on the stack. */ - uint8_t packet[64]; - uint8_t idx = 0; + char tmp[32]; switch (s_state) { case AL_STATE_INIT: - /* Haven't discovered the characteristic yet. Nothing to do. */ + /* Not discovered yet, do nothing */ break; case AL_STATE_CLEAR: - /* Build a 'clear' command. (Check your doc for exact ID or format.) - * Example: 0xFF 0x35 0x00 0x08 0x00 0x00 0x00 0xAA - * We'll do a simple 5-byte version if your firmware supports it. */ - idx = 0; - packet[idx++] = 0xFF; // Start - packet[idx++] = 0x35; // Clear command ID (for example) - packet[idx++] = 0x00; // Format - packet[idx++] = 5; // Packet length - packet[idx++] = 0xAA; // Footer - - APP_DBG_MSG("ActiveLook: Sending CLEAR...\n"); - FS_ActiveLook_Client_WriteWithoutResp(packet, idx); - - /* Move to READY state. Next GNSS update will cause text to be drawn. */ + { + /* Example "clear" packet: + * 0xFF 0x35 0x00 0x05 0xAA + * Adjust if your firmware uses a different ID. */ + uint8_t packet[5]; + packet[0] = 0xFF; // start + packet[1] = 0x35; // "clear" command ID + packet[2] = 0x00; // format + packet[3] = 5; // total length + packet[4] = 0xAA; // footer + APP_DBG_MSG("ActiveLook: Clearing display...\n"); + FS_ActiveLook_Client_WriteWithoutResp(packet, sizeof(packet)); + + /* Now go idle. Wait for FS_ActiveLook_GNSS_Update to set us to "update" */ s_state = AL_STATE_READY; break; + } case AL_STATE_READY: - default: - /* No action needed. */ + /* Idle: no action */ break; - case AL_STATE_UPDATE: - /* Build a small 'txt' command using s_gnssDataCache. For example: */ + case AL_STATE_UPDATE_LINE_1: + /* Heading (s_gnssDataCache.heading), at y=93 */ + /* Example: "HDG: 123" - you might scale heading if needed */ + snprintf(tmp, sizeof(tmp), "%ld", (long)s_gnssDataCache.heading); + AL_SendTxtCmd(255, 93, tmp); - idx = 0; - packet[idx++] = 0xFF; // Start - packet[idx++] = 0x37; // "txt" command - packet[idx++] = 0x00; // Format: no query ID - const uint8_t lengthPos = idx++; // We'll fill total length at the end + /* Next line */ + s_state = AL_STATE_UPDATE_LINE_2; + UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); + break; - /* For example, x=255, y=128, rotation=4, font=2, color=15 */ - packet[idx++] = 0x00; packet[idx++] = 255; // x=255 (big-endian might be 0x00, 0xFF) - packet[idx++] = 0x00; packet[idx++] = 128; // y=128 - packet[idx++] = 4; // rotation - packet[idx++] = 2; // font - packet[idx++] = 15; // color + case AL_STATE_UPDATE_LINE_2: + /* Horizontal speed (s_gnssDataCache.gSpeed/100) at y=128 */ + snprintf(tmp, sizeof(tmp), "%ld", (long)(s_gnssDataCache.gSpeed/100)); + AL_SendTxtCmd(255, 128, tmp); - /* Build a short text from s_gnssDataCache. For instance: */ - char text[20]; - snprintf(text, sizeof(text), "iTOW:%lu", (unsigned long)(s_gnssDataCache.iTOW/1000)); - size_t textLen = strlen(text) + 1; /* +1 for '\0' terminator */ - memcpy(&packet[idx], text, textLen); - idx += textLen; + s_state = AL_STATE_UPDATE_LINE_3; + UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); + break; - /* Footer */ - packet[idx++] = 0xAA; + case AL_STATE_UPDATE_LINE_3: + /* Vertical speed (s_gnssDataCache.velD / 1000) at y=163 */ + snprintf(tmp, sizeof(tmp), "%ld", (long)(s_gnssDataCache.velD/1000)); + AL_SendTxtCmd(255, 163, tmp); - /* Fill in total length at 'lengthPos' */ - packet[lengthPos] = idx; + s_state = AL_STATE_UPDATE_LINE_4; + UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); + break; - APP_DBG_MSG("ActiveLook: Sending GNSS update...\n"); - FS_ActiveLook_Client_WriteWithoutResp(packet, idx); + case AL_STATE_UPDATE_LINE_4: + /* Elevation (s_gnssDataCache.hMSL / 1000) at y=198 */ + snprintf(tmp, sizeof(tmp), "%ld", (long)(s_gnssDataCache.hMSL/1000)); + AL_SendTxtCmd(255, 198, tmp); - /* After sending it, return to READY. Next new GNSS data triggers another update. */ + /* Done with this update. Return to READY. */ s_state = AL_STATE_READY; break; } } + +/******************************************************************************* + * Standard init/deinit for ActiveLook + ******************************************************************************/ +void FS_ActiveLook_Init(void) +{ + /* Register the callback for discovery */ + FS_ActiveLook_Client_RegisterCb(&s_alk_cb); + + /* Register our local "task" with the sequencer */ + UTIL_SEQ_RegTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, UTIL_SEQ_RFU, FS_ActiveLook_Task); + + /* Start in the INIT state */ + s_state = AL_STATE_INIT; + + /* If we want to automatically scan/connect: */ + UTIL_SEQ_SetTask(1 << CFG_TASK_START_SCAN_ID, CFG_SCH_PRIO_0); +} + +void FS_ActiveLook_DeInit(void) +{ + /* Disconnect from the device if desired */ + UTIL_SEQ_SetTask(1 << CFG_TASK_DISCONN_DEV_1_ID, CFG_SCH_PRIO_0); +} From 73ce4c6c1ff5ed1978aac76e49bc003c37d0e21a Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Sun, 16 Feb 2025 21:56:08 -0700 Subject: [PATCH 23/81] Adjust text position --- FlySight/activelook.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/FlySight/activelook.c b/FlySight/activelook.c index 3c665c6..2a154cb 100644 --- a/FlySight/activelook.c +++ b/FlySight/activelook.c @@ -160,7 +160,7 @@ static void FS_ActiveLook_Task(void) /* Heading (s_gnssDataCache.heading), at y=93 */ /* Example: "HDG: 123" - you might scale heading if needed */ snprintf(tmp, sizeof(tmp), "%ld", (long)s_gnssDataCache.heading); - AL_SendTxtCmd(255, 93, tmp); + AL_SendTxtCmd(255, 208, tmp); /* Next line */ s_state = AL_STATE_UPDATE_LINE_2; @@ -170,7 +170,7 @@ static void FS_ActiveLook_Task(void) case AL_STATE_UPDATE_LINE_2: /* Horizontal speed (s_gnssDataCache.gSpeed/100) at y=128 */ snprintf(tmp, sizeof(tmp), "%ld", (long)(s_gnssDataCache.gSpeed/100)); - AL_SendTxtCmd(255, 128, tmp); + AL_SendTxtCmd(255, 168, tmp); s_state = AL_STATE_UPDATE_LINE_3; UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); @@ -179,7 +179,7 @@ static void FS_ActiveLook_Task(void) case AL_STATE_UPDATE_LINE_3: /* Vertical speed (s_gnssDataCache.velD / 1000) at y=163 */ snprintf(tmp, sizeof(tmp), "%ld", (long)(s_gnssDataCache.velD/1000)); - AL_SendTxtCmd(255, 163, tmp); + AL_SendTxtCmd(255, 128, tmp); s_state = AL_STATE_UPDATE_LINE_4; UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); @@ -188,7 +188,7 @@ static void FS_ActiveLook_Task(void) case AL_STATE_UPDATE_LINE_4: /* Elevation (s_gnssDataCache.hMSL / 1000) at y=198 */ snprintf(tmp, sizeof(tmp), "%ld", (long)(s_gnssDataCache.hMSL/1000)); - AL_SendTxtCmd(255, 198, tmp); + AL_SendTxtCmd(255, 88, tmp); /* Done with this update. Return to READY. */ s_state = AL_STATE_READY; From e81bd5d5bcb16c1e2bfae45b0719e0418845faac Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Sun, 16 Feb 2025 22:04:59 -0700 Subject: [PATCH 24/81] Fix issue with display clear --- FlySight/activelook.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FlySight/activelook.c b/FlySight/activelook.c index 2a154cb..529d655 100644 --- a/FlySight/activelook.c +++ b/FlySight/activelook.c @@ -140,7 +140,7 @@ static void FS_ActiveLook_Task(void) * Adjust if your firmware uses a different ID. */ uint8_t packet[5]; packet[0] = 0xFF; // start - packet[1] = 0x35; // "clear" command ID + packet[1] = 0x01; // "clear" command ID packet[2] = 0x00; // format packet[3] = 5; // total length packet[4] = 0xAA; // footer From 745a5f94807d97a39ce8ef8193fcc86a7a2f7159 Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Mon, 17 Feb 2025 09:22:36 -0700 Subject: [PATCH 25/81] Add task definition --- Core/Inc/app_conf.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Core/Inc/app_conf.h b/Core/Inc/app_conf.h index fe20a86..ff75300 100644 --- a/Core/Inc/app_conf.h +++ b/Core/Inc/app_conf.h @@ -706,6 +706,7 @@ typedef enum CFG_TASK_FS_AUDIO_CONTROL_PRODUCER_ID, CFG_TASK_FS_AUDIO_CONTROL_CONSUMER_ID, CFG_TASK_FS_CONFIG_UPDATE_ID, + CFG_TASK_FS_ACTIVELOOK_ID, /* USER CODE END CFG_Task_Id_With_NO_HCI_Cmd_t */ CFG_LAST_TASK_ID_WITH_NO_HCICMD /**< Shall be LAST in the list */ } CFG_Task_Id_With_NO_HCI_Cmd_t; From 925545a168e9bce63958db25eb38e8cf4647bf22 Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Mon, 17 Feb 2025 21:32:28 -0700 Subject: [PATCH 26/81] Use layout for first row --- FlySight/activelook.c | 227 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 222 insertions(+), 5 deletions(-) diff --git a/FlySight/activelook.c b/FlySight/activelook.c index 529d655..b790141 100644 --- a/FlySight/activelook.c +++ b/FlySight/activelook.c @@ -11,6 +11,9 @@ typedef enum { AL_STATE_INIT = 0, /* Not discovered yet */ + AL_STATE_CFG_WRITE, + AL_STATE_SETUP_L1, + AL_STATE_CFG_SET, AL_STATE_CLEAR, /* Need to send "clear" command */ AL_STATE_READY, /* Idle, waiting for GNSS data */ AL_STATE_UPDATE_LINE_1, /* GNSS arrived, show multiple lines */ @@ -85,7 +88,7 @@ static void AL_SendTxtCmd(uint16_t x, uint16_t y, const char *text) static void OnActiveLookDiscoveryComplete(void) { APP_DBG_MSG("ActiveLook: Discovery complete\n"); - s_state = AL_STATE_CLEAR; + s_state = AL_STATE_CFG_WRITE; UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); } @@ -118,6 +121,103 @@ void FS_ActiveLook_GNSS_Update(const FS_GNSS_Data_t *current) } } +/******************************************************************************* + * A helper to do a single WriteWithoutResp, building or storing a raw packet. + * We do only one call per pass of the state machine to avoid spamming BLE. + ******************************************************************************/ +static void AL_SendRaw(const uint8_t *data, uint16_t length) +{ + FS_ActiveLook_Client_WriteWithoutResp(data, length); +} + +/******************************************************************************* + * Build a "layoutSave" command for one row, with or without an icon, etc. + * We'll keep it simple: no icon here, just the normal text area. Adjust as needed. + * + * The snippet below demonstrates how you might build the layout data. + * If you want an icon, add sub-commands for 'img', 'txt', etc. in "extra[]". + * + * Return the final size of the packet in 'outLen', so the caller can do: + * AL_SendRaw(buffer, outLen); + ******************************************************************************/ +static uint8_t AL_BuildLayout( + uint8_t layoutId, + uint8_t yOffset, + uint8_t *outBuf +) +{ + // Start building the buffer + uint8_t idx = 0; + outBuf[idx++] = 0xFF; // Start + outBuf[idx++] = 0x60; // "layoutSave" + outBuf[idx++] = 0x00; // format => 1-byte length + uint8_t lenPos = idx++; // We'll fill the total length later + + // layout ID + outBuf[idx++] = layoutId; + + // We'll fill the additional commands size later + uint8_t addCmdSizePos = idx++; + + // X (2 bytes, MSB first) + // Suppose x=0 => 0x00, 0x00 + outBuf[idx++] = 0x00; // x hi + outBuf[idx++] = 0x00; // x lo + + // Y (1 byte!) + // Suppose y = 168 => 0xA8 + outBuf[idx++] = 0xA8; + + // Width (2 bytes, MSB first). E.g. 304 => 0x01,0x30 + outBuf[idx++] = 0x01; // width hi + outBuf[idx++] = 0x30; // width lo + + // Height (1 byte!) + // e.g. 40 => 0x28 + outBuf[idx++] = 0x28; + + // ForeColor, BackColor, Font + outBuf[idx++] = 15; // foreColor + outBuf[idx++] = 0; // backColor + outBuf[idx++] = 2; // font + + // TextValid + outBuf[idx++] = 1; // 1 => use dynamic text argument + + // Text X (2 bytes, MSB first), say 255 => 0x00,0xFF + outBuf[idx++] = 0x00; + outBuf[idx++] = 0xFF; + + // Text Y (1 byte!), e.g. 40 => 0x28 + outBuf[idx++] = 0x28; + + // Text Rotation (1 byte) + outBuf[idx++] = 4; + + // Text Opacity (1 byte) + outBuf[idx++] = 1; + + //------------------------------------------------------- + // Additional sub-commands for icon or units, if desired: + //------------------------------------------------------- + uint8_t extra[64]; + uint8_t e = 0; + + /* No subcommands yet */ + + // Done building sub-commands + outBuf[addCmdSizePos] = e; + memcpy(&outBuf[idx], extra, e); + idx += e; + + // Footer + outBuf[idx++] = 0xAA; + + // Fill total length + outBuf[lenPos] = idx; + return idx; +} + /******************************************************************************* * Our main sequencer task. We handle the state machine, sending WWR commands * without blocking. Each line is sent in a separate state so that we don't @@ -126,6 +226,8 @@ void FS_ActiveLook_GNSS_Update(const FS_GNSS_Data_t *current) static void FS_ActiveLook_Task(void) { char tmp[32]; + static uint8_t buf[128]; /* re-usable buffer for building commands */ + uint8_t length; switch (s_state) { @@ -133,6 +235,98 @@ static void FS_ActiveLook_Task(void) /* Not discovered yet, do nothing */ break; + case AL_STATE_CFG_WRITE: + { + uint8_t packet[128]; + uint8_t idx = 0; + + packet[idx++] = 0xFF; // start + packet[idx++] = 0xD0; // "cfgWrite" command ID + packet[idx++] = 0x00; // format + uint8_t lenPos = idx++; + + snprintf(tmp, sizeof(tmp), "FLYSIGHT"); + size_t textLen = strlen(tmp); + memcpy(&packet[idx], tmp, textLen); + idx += textLen; + + while (textLen < 12) + { + packet[idx++] = 0x00; // padding + textLen++; + } + + packet[idx++] = 0x00; // version + packet[idx++] = 0x00; + packet[idx++] = 0x00; + packet[idx++] = 0x01; + + packet[idx++] = 0x01; // password + packet[idx++] = 0x02; + packet[idx++] = 0x03; + packet[idx++] = 0x04; + + /* Footer byte */ + packet[idx++] = 0xAA; + + /* Fill total length */ + packet[lenPos] = idx; + + /* Now send it with Write Without Response */ + FS_ActiveLook_Client_WriteWithoutResp(packet, idx); + + /* Next config step */ + s_state = AL_STATE_SETUP_L1; + UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); + break; + } + + case AL_STATE_SETUP_L1: + length = AL_BuildLayout(10, "deg", buf); + AL_SendRaw(buf, length); + APP_DBG_MSG("Layout #1 defined.\n"); + + /* Next config step */ + s_state = AL_STATE_CFG_SET; + UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); + break; + + case AL_STATE_CFG_SET: + { + uint8_t packet[128]; + uint8_t idx = 0; + + packet[idx++] = 0xFF; // start + packet[idx++] = 0xD2; // "cfgSet" command ID + packet[idx++] = 0x00; // format + uint8_t lenPos = idx++; + + snprintf(tmp, sizeof(tmp), "FLYSIGHT"); + size_t textLen = strlen(tmp); + memcpy(&packet[idx], tmp, textLen); + idx += textLen; + + while (textLen < 12) + { + packet[idx++] = 0x00; // padding + textLen++; + } + + /* Footer byte */ + packet[idx++] = 0xAA; + + /* Fill total length */ + packet[lenPos] = idx; + + /* Now send it with Write Without Response */ + FS_ActiveLook_Client_WriteWithoutResp(packet, idx); + + /* Next config step */ + s_state = AL_STATE_CLEAR; + UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); + break; + } + case AL_STATE_CLEAR: { /* Example "clear" packet: @@ -157,15 +351,38 @@ static void FS_ActiveLook_Task(void) break; case AL_STATE_UPDATE_LINE_1: - /* Heading (s_gnssDataCache.heading), at y=93 */ - /* Example: "HDG: 123" - you might scale heading if needed */ - snprintf(tmp, sizeof(tmp), "%ld", (long)s_gnssDataCache.heading); - AL_SendTxtCmd(255, 208, tmp); + { + uint8_t packet[128]; + uint8_t idx = 0; + + packet[idx++] = 0xFF; // start + packet[idx++] = 0x62; // "layoutDisplay" command ID + packet[idx++] = 0x00; // format + uint8_t lenPos = idx++; + + packet[idx++] = 10; // layout ID + + /* Copy in the string + its null terminator */ +// snprintf(tmp, sizeof(tmp), "%ld", (long)s_gnssDataCache.heading); + snprintf(tmp, sizeof(tmp), "TESTING"); + size_t textLen = strlen(tmp); + memcpy(&packet[idx], tmp, textLen); + idx += textLen; + + /* Footer byte */ + packet[idx++] = 0xAA; + + /* Fill total length */ + packet[lenPos] = idx; + + /* Now send it with Write Without Response */ + FS_ActiveLook_Client_WriteWithoutResp(packet, idx); /* Next line */ s_state = AL_STATE_UPDATE_LINE_2; UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); break; + } case AL_STATE_UPDATE_LINE_2: /* Horizontal speed (s_gnssDataCache.gSpeed/100) at y=128 */ From bd563db05eba79439c90a70ef28dde5a3797b48c Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Mon, 17 Feb 2025 21:46:59 -0700 Subject: [PATCH 27/81] Use separate layout for each row --- FlySight/activelook.c | 181 ++++++++++++++++++++++++++++-------------- 1 file changed, 120 insertions(+), 61 deletions(-) diff --git a/FlySight/activelook.c b/FlySight/activelook.c index b790141..2b4934d 100644 --- a/FlySight/activelook.c +++ b/FlySight/activelook.c @@ -13,6 +13,10 @@ typedef enum AL_STATE_INIT = 0, /* Not discovered yet */ AL_STATE_CFG_WRITE, AL_STATE_SETUP_L1, + AL_STATE_SETUP_L2, + AL_STATE_SETUP_L3, + AL_STATE_SETUP_L4, + AL_STATE_SETUP_PAGE, AL_STATE_CFG_SET, AL_STATE_CLEAR, /* Need to send "clear" command */ AL_STATE_READY, /* Idle, waiting for GNSS data */ @@ -36,51 +40,6 @@ static const FS_ActiveLook_ClientCb_t s_alk_cb = .OnDiscoveryComplete = OnActiveLookDiscoveryComplete }; -/******************************************************************************* - * Small helper to build and send a "txt" command to the ActiveLook display - * - x,y in 16-bit big-endian - * - rotation=4, font=2, color=15 (change as desired) - * - null-terminated text - ******************************************************************************/ -static void AL_SendTxtCmd(uint16_t x, uint16_t y, const char *text) -{ - uint8_t packet[64]; - uint8_t idx = 0; - - /* Start byte + Command ID for "txt" = 0x37 + no Query ID = 0x00 */ - packet[idx++] = 0xFF; // start byte - packet[idx++] = 0x37; // "txt" command - packet[idx++] = 0x00; // format flags (0x00 means "no query ID") - uint8_t lengthPos = idx++; // We'll fill total packet length here at the end - - /* X coordinate (big endian) */ - packet[idx++] = (uint8_t)(x >> 8); - packet[idx++] = (uint8_t)(x & 0xFF); - - /* Y coordinate (big endian) */ - packet[idx++] = (uint8_t)(y >> 8); - packet[idx++] = (uint8_t)(y & 0xFF); - - /* rotation, font, color */ - packet[idx++] = 4; // rotation - packet[idx++] = 2; // font - packet[idx++] = 15; // color - - /* Copy in the string + its null terminator */ - size_t textLen = strlen(text) + 1; - memcpy(&packet[idx], text, textLen); - idx += textLen; - - /* Footer byte */ - packet[idx++] = 0xAA; - - /* Fill total length */ - packet[lengthPos] = idx; - - /* Now send it with Write Without Response */ - FS_ActiveLook_Client_WriteWithoutResp(packet, idx); -} - /******************************************************************************* * Called by the ActiveLook client once the Rx characteristic is discovered. * We'll move the state to CLEAR, meaning "clear" next, and schedule the task. @@ -165,8 +124,7 @@ static uint8_t AL_BuildLayout( outBuf[idx++] = 0x00; // x lo // Y (1 byte!) - // Suppose y = 168 => 0xA8 - outBuf[idx++] = 0xA8; + outBuf[idx++] = yOffset; // Width (2 bytes, MSB first). E.g. 304 => 0x01,0x30 outBuf[idx++] = 0x01; // width hi @@ -282,10 +240,40 @@ static void FS_ActiveLook_Task(void) } case AL_STATE_SETUP_L1: - length = AL_BuildLayout(10, "deg", buf); + length = AL_BuildLayout(10, 168, buf); AL_SendRaw(buf, length); APP_DBG_MSG("Layout #1 defined.\n"); + /* Next config step */ + s_state = AL_STATE_SETUP_L2; + UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); + break; + + case AL_STATE_SETUP_L2: + length = AL_BuildLayout(11, 128, buf); + AL_SendRaw(buf, length); + APP_DBG_MSG("Layout #2 defined.\n"); + + /* Next config step */ + s_state = AL_STATE_SETUP_L3; + UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); + break; + + case AL_STATE_SETUP_L3: + length = AL_BuildLayout(12, 88, buf); + AL_SendRaw(buf, length); + APP_DBG_MSG("Layout #3 defined.\n"); + + /* Next config step */ + s_state = AL_STATE_SETUP_L4; + UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); + break; + + case AL_STATE_SETUP_L4: + length = AL_BuildLayout(13, 48, buf); + AL_SendRaw(buf, length); + APP_DBG_MSG("Layout #4 defined.\n"); + /* Next config step */ s_state = AL_STATE_CFG_SET; UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); @@ -362,9 +350,8 @@ static void FS_ActiveLook_Task(void) packet[idx++] = 10; // layout ID - /* Copy in the string + its null terminator */ -// snprintf(tmp, sizeof(tmp), "%ld", (long)s_gnssDataCache.heading); - snprintf(tmp, sizeof(tmp), "TESTING"); + /* Copy in the string */ + snprintf(tmp, sizeof(tmp), "%ld", (long)s_gnssDataCache.heading); size_t textLen = strlen(tmp); memcpy(&packet[idx], tmp, textLen); idx += textLen; @@ -385,32 +372,104 @@ static void FS_ActiveLook_Task(void) } case AL_STATE_UPDATE_LINE_2: - /* Horizontal speed (s_gnssDataCache.gSpeed/100) at y=128 */ - snprintf(tmp, sizeof(tmp), "%ld", (long)(s_gnssDataCache.gSpeed/100)); - AL_SendTxtCmd(255, 168, tmp); + { + uint8_t packet[128]; + uint8_t idx = 0; + + packet[idx++] = 0xFF; // start + packet[idx++] = 0x62; // "layoutDisplay" command ID + packet[idx++] = 0x00; // format + uint8_t lenPos = idx++; + + packet[idx++] = 11; // layout ID + + /* Copy in the string */ + snprintf(tmp, sizeof(tmp), "%ld", (long)(s_gnssDataCache.gSpeed / 100)); + size_t textLen = strlen(tmp); + memcpy(&packet[idx], tmp, textLen); + idx += textLen; + + /* Footer byte */ + packet[idx++] = 0xAA; + + /* Fill total length */ + packet[lenPos] = idx; + + /* Now send it with Write Without Response */ + FS_ActiveLook_Client_WriteWithoutResp(packet, idx); + /* Next line */ s_state = AL_STATE_UPDATE_LINE_3; UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); break; + } case AL_STATE_UPDATE_LINE_3: - /* Vertical speed (s_gnssDataCache.velD / 1000) at y=163 */ - snprintf(tmp, sizeof(tmp), "%ld", (long)(s_gnssDataCache.velD/1000)); - AL_SendTxtCmd(255, 128, tmp); + { + uint8_t packet[128]; + uint8_t idx = 0; + + packet[idx++] = 0xFF; // start + packet[idx++] = 0x62; // "layoutDisplay" command ID + packet[idx++] = 0x00; // format + uint8_t lenPos = idx++; + + packet[idx++] = 12; // layout ID + + /* Copy in the string */ + snprintf(tmp, sizeof(tmp), "%ld", (long)(s_gnssDataCache.velD / 1000)); + size_t textLen = strlen(tmp); + memcpy(&packet[idx], tmp, textLen); + idx += textLen; + + /* Footer byte */ + packet[idx++] = 0xAA; + + /* Fill total length */ + packet[lenPos] = idx; + + /* Now send it with Write Without Response */ + FS_ActiveLook_Client_WriteWithoutResp(packet, idx); + /* Next line */ s_state = AL_STATE_UPDATE_LINE_4; UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); break; + } case AL_STATE_UPDATE_LINE_4: - /* Elevation (s_gnssDataCache.hMSL / 1000) at y=198 */ - snprintf(tmp, sizeof(tmp), "%ld", (long)(s_gnssDataCache.hMSL/1000)); - AL_SendTxtCmd(255, 88, tmp); + { + uint8_t packet[128]; + uint8_t idx = 0; + + packet[idx++] = 0xFF; // start + packet[idx++] = 0x62; // "layoutDisplay" command ID + packet[idx++] = 0x00; // format + uint8_t lenPos = idx++; + + packet[idx++] = 13; // layout ID + + /* Copy in the string */ + snprintf(tmp, sizeof(tmp), "%ld", (long)(s_gnssDataCache.hMSL / 1000)); + size_t textLen = strlen(tmp); + memcpy(&packet[idx], tmp, textLen); + idx += textLen; - /* Done with this update. Return to READY. */ + /* Footer byte */ + packet[idx++] = 0xAA; + + /* Fill total length */ + packet[lenPos] = idx; + + /* Now send it with Write Without Response */ + FS_ActiveLook_Client_WriteWithoutResp(packet, idx); + + /* Idle */ s_state = AL_STATE_READY; + UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); break; } + } } /******************************************************************************* From a7f5e4e31dfc300002c135a35c09d0cda51c3031 Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Mon, 17 Feb 2025 22:10:44 -0700 Subject: [PATCH 28/81] Display all layouts in a single page --- FlySight/activelook.c | 288 +++++++++++++++++++++--------------------- 1 file changed, 143 insertions(+), 145 deletions(-) diff --git a/FlySight/activelook.c b/FlySight/activelook.c index 2b4934d..585090b 100644 --- a/FlySight/activelook.c +++ b/FlySight/activelook.c @@ -20,10 +20,7 @@ typedef enum AL_STATE_CFG_SET, AL_STATE_CLEAR, /* Need to send "clear" command */ AL_STATE_READY, /* Idle, waiting for GNSS data */ - AL_STATE_UPDATE_LINE_1, /* GNSS arrived, show multiple lines */ - AL_STATE_UPDATE_LINE_2, - AL_STATE_UPDATE_LINE_3, - AL_STATE_UPDATE_LINE_4, + AL_STATE_UPDATE, /* GNSS arrived, update page */ } FS_ActiveLook_State_t; /*----- Module-level static variables -----*/ @@ -70,8 +67,8 @@ void FS_ActiveLook_GNSS_Update(const FS_GNSS_Data_t *current) /* Cache the data for later use */ s_gnssDataCache = *current; - /* We'll do multiple lines */ - s_state = AL_STATE_UPDATE_LINE_1; + /* Update the page */ + s_state = AL_STATE_UPDATE; UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); } else @@ -101,7 +98,6 @@ static void AL_SendRaw(const uint8_t *data, uint16_t length) ******************************************************************************/ static uint8_t AL_BuildLayout( uint8_t layoutId, - uint8_t yOffset, uint8_t *outBuf ) { @@ -124,7 +120,7 @@ static uint8_t AL_BuildLayout( outBuf[idx++] = 0x00; // x lo // Y (1 byte!) - outBuf[idx++] = yOffset; + outBuf[idx++] = 0x00; // Width (2 bytes, MSB first). E.g. 304 => 0x01,0x30 outBuf[idx++] = 0x01; // width hi @@ -176,6 +172,122 @@ static uint8_t AL_BuildLayout( return idx; } +/******************************************************************************* + * Build a "pageSave" command with 4 sublayouts. We'll call it ID=10, referencing + * layoutIds=1..4, each at offset x=0,y=0 inside the page. + * + * Return the final length so caller can AL_SendRaw() it. + ******************************************************************************/ +static uint8_t AL_BuildPage(uint8_t pageId, uint8_t *outBuf) +{ + /* pageSave => command=0x80. + Format => 0x00 => 1-byte length. + The structure is: pageId, then for each sublayout: + layoutId(1B), x(2B?), y(2B?), ... + The exact number of bytes for x,y depends on your firmware doc. + We'll assume x=2 bytes, y=2 bytes for safety. Or if doc says otherwise, adapt. + */ + uint8_t idx = 0; + + outBuf[idx++] = 0xFF; // start + outBuf[idx++] = 0x80; // "pageSave" + outBuf[idx++] = 0x00; // format => 1B length + uint8_t lenPos = idx++; + + outBuf[idx++] = pageId; + + // We have 4 sublayouts: layout #1..4 + // Each sublayout => layoutId(1B), x(2B), y(2B) + // We'll set x=0,y=0 for each. + + // sublayout #1 + outBuf[idx++] = 10; // layoutId + outBuf[idx++] = 0x00; // x hi + outBuf[idx++] = 0x00; // x lo + outBuf[idx++] = 168; // y lo + + // sublayout #2 + outBuf[idx++] = 11; + outBuf[idx++] = 0x00; + outBuf[idx++] = 0x00; + outBuf[idx++] = 128; + + // sublayout #3 + outBuf[idx++] = 12; + outBuf[idx++] = 0x00; + outBuf[idx++] = 0x00; + outBuf[idx++] = 88; + + // sublayout #4 + outBuf[idx++] = 13; + outBuf[idx++] = 0x00; + outBuf[idx++] = 0x00; + outBuf[idx++] = 48; + + // Footer + outBuf[idx++] = 0xAA; + + // Fill length + outBuf[lenPos] = idx; + + return idx; +} + +/******************************************************************************* + * Build a single "pageClearAndDisplay" command with 4 text arguments, + * separated by '\0'. That is the entire update in one WWR call. + ******************************************************************************/ +static uint8_t AL_BuildPageUpdate(uint8_t pageId, + const char *line1, + const char *line2, + const char *line3, + const char *line4, + uint8_t *outBuf) +{ + /* layoutClearAndDisplay or pageClearAndDisplay => 0x6A + structure => [ pageId, line1\0 line2\0 line3\0 line4\0 ] + */ + uint8_t idx = 0; + + outBuf[idx++] = 0xFF; + outBuf[idx++] = 0x86; // "pageClearAndDisplay" + outBuf[idx++] = 0x00; // format => 1B length + uint8_t lenPos = idx++; + + outBuf[idx++] = pageId; + + // Copy line1 plus a null + size_t l1 = strlen(line1); + memcpy(&outBuf[idx], line1, l1); + idx += l1; + outBuf[idx++] = 0; + + // line2 + size_t l2 = strlen(line2); + memcpy(&outBuf[idx], line2, l2); + idx += l2; + outBuf[idx++] = 0; + + // line3 + size_t l3 = strlen(line3); + memcpy(&outBuf[idx], line3, l3); + idx += l3; + outBuf[idx++] = 0; + + // line4 + size_t l4 = strlen(line4); + memcpy(&outBuf[idx], line4, l4); + idx += l4; + outBuf[idx++] = 0; + + // Footer + outBuf[idx++] = 0xAA; + + // fill length + outBuf[lenPos] = idx; + return idx; +} + /******************************************************************************* * Our main sequencer task. We handle the state machine, sending WWR commands * without blocking. Each line is sent in a separate state so that we don't @@ -240,41 +352,41 @@ static void FS_ActiveLook_Task(void) } case AL_STATE_SETUP_L1: - length = AL_BuildLayout(10, 168, buf); + length = AL_BuildLayout(10, buf); AL_SendRaw(buf, length); APP_DBG_MSG("Layout #1 defined.\n"); - - /* Next config step */ s_state = AL_STATE_SETUP_L2; UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); break; case AL_STATE_SETUP_L2: - length = AL_BuildLayout(11, 128, buf); + length = AL_BuildLayout(11, buf); AL_SendRaw(buf, length); APP_DBG_MSG("Layout #2 defined.\n"); - - /* Next config step */ s_state = AL_STATE_SETUP_L3; UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); break; case AL_STATE_SETUP_L3: - length = AL_BuildLayout(12, 88, buf); + length = AL_BuildLayout(12, buf); AL_SendRaw(buf, length); APP_DBG_MSG("Layout #3 defined.\n"); - - /* Next config step */ s_state = AL_STATE_SETUP_L4; UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); break; case AL_STATE_SETUP_L4: - length = AL_BuildLayout(13, 48, buf); + length = AL_BuildLayout(13, buf); AL_SendRaw(buf, length); APP_DBG_MSG("Layout #4 defined.\n"); + s_state = AL_STATE_SETUP_PAGE; + UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); + break; - /* Next config step */ + case AL_STATE_SETUP_PAGE: + length = AL_BuildPage(10, buf); + AL_SendRaw(buf, length); + APP_DBG_MSG("Page #10 referencing layouts #10..#13 defined.\n"); s_state = AL_STATE_CFG_SET; UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); break; @@ -338,137 +450,23 @@ static void FS_ActiveLook_Task(void) /* Idle: no action */ break; - case AL_STATE_UPDATE_LINE_1: - { - uint8_t packet[128]; - uint8_t idx = 0; - - packet[idx++] = 0xFF; // start - packet[idx++] = 0x62; // "layoutDisplay" command ID - packet[idx++] = 0x00; // format - uint8_t lenPos = idx++; - - packet[idx++] = 10; // layout ID - - /* Copy in the string */ - snprintf(tmp, sizeof(tmp), "%ld", (long)s_gnssDataCache.heading); - size_t textLen = strlen(tmp); - memcpy(&packet[idx], tmp, textLen); - idx += textLen; - - /* Footer byte */ - packet[idx++] = 0xAA; - - /* Fill total length */ - packet[lenPos] = idx; - - /* Now send it with Write Without Response */ - FS_ActiveLook_Client_WriteWithoutResp(packet, idx); - - /* Next line */ - s_state = AL_STATE_UPDATE_LINE_2; - UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); - break; - } - - case AL_STATE_UPDATE_LINE_2: - { - uint8_t packet[128]; - uint8_t idx = 0; - - packet[idx++] = 0xFF; // start - packet[idx++] = 0x62; // "layoutDisplay" command ID - packet[idx++] = 0x00; // format - uint8_t lenPos = idx++; - - packet[idx++] = 11; // layout ID - - /* Copy in the string */ - snprintf(tmp, sizeof(tmp), "%ld", (long)(s_gnssDataCache.gSpeed / 100)); - size_t textLen = strlen(tmp); - memcpy(&packet[idx], tmp, textLen); - idx += textLen; - - /* Footer byte */ - packet[idx++] = 0xAA; - - /* Fill total length */ - packet[lenPos] = idx; - - /* Now send it with Write Without Response */ - FS_ActiveLook_Client_WriteWithoutResp(packet, idx); - - /* Next line */ - s_state = AL_STATE_UPDATE_LINE_3; - UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); - break; - } - - case AL_STATE_UPDATE_LINE_3: - { - uint8_t packet[128]; - uint8_t idx = 0; - - packet[idx++] = 0xFF; // start - packet[idx++] = 0x62; // "layoutDisplay" command ID - packet[idx++] = 0x00; // format - uint8_t lenPos = idx++; - - packet[idx++] = 12; // layout ID - - /* Copy in the string */ - snprintf(tmp, sizeof(tmp), "%ld", (long)(s_gnssDataCache.velD / 1000)); - size_t textLen = strlen(tmp); - memcpy(&packet[idx], tmp, textLen); - idx += textLen; - - /* Footer byte */ - packet[idx++] = 0xAA; - - /* Fill total length */ - packet[lenPos] = idx; - /* Now send it with Write Without Response */ - FS_ActiveLook_Client_WriteWithoutResp(packet, idx); - - /* Next line */ - s_state = AL_STATE_UPDATE_LINE_4; - UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); - break; - } - - case AL_STATE_UPDATE_LINE_4: + case AL_STATE_UPDATE: { - uint8_t packet[128]; - uint8_t idx = 0; - - packet[idx++] = 0xFF; // start - packet[idx++] = 0x62; // "layoutDisplay" command ID - packet[idx++] = 0x00; // format - uint8_t lenPos = idx++; - - packet[idx++] = 13; // layout ID - - /* Copy in the string */ - snprintf(tmp, sizeof(tmp), "%ld", (long)(s_gnssDataCache.hMSL / 1000)); - size_t textLen = strlen(tmp); - memcpy(&packet[idx], tmp, textLen); - idx += textLen; - - /* Footer byte */ - packet[idx++] = 0xAA; - - /* Fill total length */ - packet[lenPos] = idx; - - /* Now send it with Write Without Response */ - FS_ActiveLook_Client_WriteWithoutResp(packet, idx); + /* Build 4 strings from our GNSS data. Then do a single "pageClearAndDisplay" */ + char line1[16], line2[16], line3[16], line4[16]; + snprintf(line1, sizeof(line1), "%ld",(long)(s_gnssDataCache.heading)); + snprintf(line2, sizeof(line2), "%ld",(long)(s_gnssDataCache.gSpeed/100)); + snprintf(line3, sizeof(line3), "%ld",(long)(s_gnssDataCache.velD/1000)); + snprintf(line4, sizeof(line4), "%ld",(long)(s_gnssDataCache.hMSL/1000)); + + length = AL_BuildPageUpdate(10, line1, line2, line3, line4, buf); + AL_SendRaw(buf, length); + APP_DBG_MSG("ActiveLook: Page #10 updated with heading/speed/etc.\n"); - /* Idle */ s_state = AL_STATE_READY; - UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); - break; } + break; } } From 72b603eca6752b51a58396496fe47e89f7aa0d7d Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Mon, 17 Feb 2025 22:34:32 -0700 Subject: [PATCH 29/81] Add unit text to each row --- FlySight/activelook.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/FlySight/activelook.c b/FlySight/activelook.c index 585090b..a2152b7 100644 --- a/FlySight/activelook.c +++ b/FlySight/activelook.c @@ -98,6 +98,7 @@ static void AL_SendRaw(const uint8_t *data, uint16_t length) ******************************************************************************/ static uint8_t AL_BuildLayout( uint8_t layoutId, + const char *unitsText, uint8_t *outBuf ) { @@ -138,9 +139,9 @@ static uint8_t AL_BuildLayout( // TextValid outBuf[idx++] = 1; // 1 => use dynamic text argument - // Text X (2 bytes, MSB first), say 255 => 0x00,0xFF + // Text X (2 bytes, MSB first), say 200 => 0x00,0xC8 outBuf[idx++] = 0x00; - outBuf[idx++] = 0xFF; + outBuf[idx++] = 0xC8; // Text Y (1 byte!), e.g. 40 => 0x28 outBuf[idx++] = 0x28; @@ -157,7 +158,29 @@ static uint8_t AL_BuildLayout( uint8_t extra[64]; uint8_t e = 0; - /* No subcommands yet */ + // Example: color=15 + extra[e++] = 0x03; // sub-cmd ID "color" + extra[e++] = 15; // color=15 + + // Example: font=1 + extra[e++] = 0x04; + extra[e++] = 1; // smaller font + + // Example: txt => position= (240,0) + extra[e++] = 0x09; // "text" + // x=80 => 2 bytes + extra[e++] = 0x00; + extra[e++] = 80; + // y=0 => 2 bytes + extra[e++] = 0x00; + extra[e++] = 35; + + // Then one byte with length, then the string + uint8_t eLenPos = e++; // We'll fill the extra length later + size_t unitsLen = strlen(unitsText); + memcpy(&extra[e], unitsText, unitsLen); + e += unitsLen; + extra[eLenPos] = (uint8_t)unitsLen; // Done building sub-commands outBuf[addCmdSizePos] = e; @@ -352,7 +375,7 @@ static void FS_ActiveLook_Task(void) } case AL_STATE_SETUP_L1: - length = AL_BuildLayout(10, buf); + length = AL_BuildLayout(10, "deg", buf); AL_SendRaw(buf, length); APP_DBG_MSG("Layout #1 defined.\n"); s_state = AL_STATE_SETUP_L2; @@ -360,7 +383,7 @@ static void FS_ActiveLook_Task(void) break; case AL_STATE_SETUP_L2: - length = AL_BuildLayout(11, buf); + length = AL_BuildLayout(11, "m/s", buf); AL_SendRaw(buf, length); APP_DBG_MSG("Layout #2 defined.\n"); s_state = AL_STATE_SETUP_L3; @@ -368,7 +391,7 @@ static void FS_ActiveLook_Task(void) break; case AL_STATE_SETUP_L3: - length = AL_BuildLayout(12, buf); + length = AL_BuildLayout(12, "m/s", buf); AL_SendRaw(buf, length); APP_DBG_MSG("Layout #3 defined.\n"); s_state = AL_STATE_SETUP_L4; @@ -376,7 +399,7 @@ static void FS_ActiveLook_Task(void) break; case AL_STATE_SETUP_L4: - length = AL_BuildLayout(13, buf); + length = AL_BuildLayout(13, "m", buf); AL_SendRaw(buf, length); APP_DBG_MSG("Layout #4 defined.\n"); s_state = AL_STATE_SETUP_PAGE; From e75d92450ca8d90276050da6295a83bf83c28afd Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Fri, 21 Feb 2025 13:01:03 -0800 Subject: [PATCH 30/81] Adjust display format --- FlySight/activelook.c | 47 ++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/FlySight/activelook.c b/FlySight/activelook.c index a2152b7..a7ef319 100644 --- a/FlySight/activelook.c +++ b/FlySight/activelook.c @@ -98,6 +98,7 @@ static void AL_SendRaw(const uint8_t *data, uint16_t length) ******************************************************************************/ static uint8_t AL_BuildLayout( uint8_t layoutId, + const char *headingText, const char *unitsText, uint8_t *outBuf ) @@ -139,12 +140,12 @@ static uint8_t AL_BuildLayout( // TextValid outBuf[idx++] = 1; // 1 => use dynamic text argument - // Text X (2 bytes, MSB first), say 200 => 0x00,0xC8 - outBuf[idx++] = 0x00; - outBuf[idx++] = 0xC8; + // Text X (2 bytes, MSB first), say 200 + outBuf[idx++] = 0; + outBuf[idx++] = 200; - // Text Y (1 byte!), e.g. 40 => 0x28 - outBuf[idx++] = 0x28; + // Text Y (1 byte!), e.g. 40 + outBuf[idx++] = 40; // Text Rotation (1 byte) outBuf[idx++] = 4; @@ -166,21 +167,39 @@ static uint8_t AL_BuildLayout( extra[e++] = 0x04; extra[e++] = 1; // smaller font - // Example: txt => position= (240,0) + // Example: txt => position= (260,35) + extra[e++] = 0x09; // "text" + // x=80 => 2 bytes + extra[e++] = 1; + extra[e++] = 5; + // y=0 => 2 bytes + extra[e++] = 0; + extra[e++] = 35; + + // Then one byte with length, then the string + size_t headingLen = strlen(headingText); + extra[e++] = (uint8_t)headingLen; + memcpy(&extra[e], headingText, headingLen); + e += headingLen; + + // Example: font=1 + extra[e++] = 0x04; + extra[e++] = 1; // smaller font + + // Example: txt => position= (80,35) extra[e++] = 0x09; // "text" // x=80 => 2 bytes - extra[e++] = 0x00; + extra[e++] = 0; extra[e++] = 80; // y=0 => 2 bytes - extra[e++] = 0x00; + extra[e++] = 0; extra[e++] = 35; // Then one byte with length, then the string - uint8_t eLenPos = e++; // We'll fill the extra length later size_t unitsLen = strlen(unitsText); + extra[e++] = (uint8_t)unitsLen; memcpy(&extra[e], unitsText, unitsLen); e += unitsLen; - extra[eLenPos] = (uint8_t)unitsLen; // Done building sub-commands outBuf[addCmdSizePos] = e; @@ -375,7 +394,7 @@ static void FS_ActiveLook_Task(void) } case AL_STATE_SETUP_L1: - length = AL_BuildLayout(10, "deg", buf); + length = AL_BuildLayout(10, "Hdg:", "deg", buf); AL_SendRaw(buf, length); APP_DBG_MSG("Layout #1 defined.\n"); s_state = AL_STATE_SETUP_L2; @@ -383,7 +402,7 @@ static void FS_ActiveLook_Task(void) break; case AL_STATE_SETUP_L2: - length = AL_BuildLayout(11, "m/s", buf); + length = AL_BuildLayout(11, "Vh:", "m/s", buf); AL_SendRaw(buf, length); APP_DBG_MSG("Layout #2 defined.\n"); s_state = AL_STATE_SETUP_L3; @@ -391,7 +410,7 @@ static void FS_ActiveLook_Task(void) break; case AL_STATE_SETUP_L3: - length = AL_BuildLayout(12, "m/s", buf); + length = AL_BuildLayout(12, "Vd:", "m/s", buf); AL_SendRaw(buf, length); APP_DBG_MSG("Layout #3 defined.\n"); s_state = AL_STATE_SETUP_L4; @@ -399,7 +418,7 @@ static void FS_ActiveLook_Task(void) break; case AL_STATE_SETUP_L4: - length = AL_BuildLayout(13, "m", buf); + length = AL_BuildLayout(13, "Ele:", "m", buf); AL_SendRaw(buf, length); APP_DBG_MSG("Layout #4 defined.\n"); s_state = AL_STATE_SETUP_PAGE; From 803c6f86be38585eb4a24902387c84699c5138b6 Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Fri, 21 Feb 2025 13:36:43 -0800 Subject: [PATCH 31/81] Update ActiveLook display on timer --- Core/Inc/hw_conf.h | 2 +- FlySight/activelook.c | 50 ++++++++++++++++++++++++++----------------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/Core/Inc/hw_conf.h b/Core/Inc/hw_conf.h index f52dee8..32ed8e0 100644 --- a/Core/Inc/hw_conf.h +++ b/Core/Inc/hw_conf.h @@ -109,7 +109,7 @@ * The user may define the maximum number of virtual timers supported. * It shall not exceed 255 */ -#define CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER 18 +#define CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER 19 /** * The user may define the priority in the NVIC of the RTC_WKUP interrupt handler that is used to manage the diff --git a/FlySight/activelook.c b/FlySight/activelook.c index a7ef319..695c4d3 100644 --- a/FlySight/activelook.c +++ b/FlySight/activelook.c @@ -7,6 +7,9 @@ #include #include +#define ACTIVELOOK_UPDATE_MSEC 1000 +#define ACTIVELOOK_UPDATE_RATE (ACTIVELOOK_UPDATE_MSEC*1000/CFG_TS_TICK_VAL) + /*----- State machine states -----*/ typedef enum { @@ -27,7 +30,10 @@ typedef enum static FS_ActiveLook_State_t s_state = AL_STATE_INIT; static FS_GNSS_Data_t s_gnssDataCache; +static uint8_t timer_id; + /* Forward references */ +static void FS_ActiveLook_Timer(void); static void FS_ActiveLook_Task(void); static void OnActiveLookDiscoveryComplete(void); @@ -55,26 +61,8 @@ static void OnActiveLookDiscoveryComplete(void) ******************************************************************************/ void FS_ActiveLook_GNSS_Update(const FS_GNSS_Data_t *current) { - /* Only queue an update if we are discovered & idle. */ - if (!FS_ActiveLook_Client_IsReady()) - { - /* Possibly disconnected or not discovered. */ - return; - } - - if (s_state == AL_STATE_READY) - { - /* Cache the data for later use */ - s_gnssDataCache = *current; - - /* Update the page */ - s_state = AL_STATE_UPDATE; - UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); - } - else - { - APP_DBG_MSG("ActiveLook_GNSS_Update: State %d, ignoring.\n", s_state); - } + /* Cache the data for later use */ + s_gnssDataCache = *current; } /******************************************************************************* @@ -485,6 +473,9 @@ static void FS_ActiveLook_Task(void) /* Now go idle. Wait for FS_ActiveLook_GNSS_Update to set us to "update" */ s_state = AL_STATE_READY; + + /* Start update timer */ + HW_TS_Start(timer_id, ACTIVELOOK_UPDATE_RATE); break; } @@ -512,6 +503,16 @@ static void FS_ActiveLook_Task(void) } } +static void FS_ActiveLook_Timer(void) +{ + if (s_state == AL_STATE_READY) + { + /* Update the page */ + s_state = AL_STATE_UPDATE; + UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); + } +} + /******************************************************************************* * Standard init/deinit for ActiveLook ******************************************************************************/ @@ -528,10 +529,19 @@ void FS_ActiveLook_Init(void) /* If we want to automatically scan/connect: */ UTIL_SEQ_SetTask(1 << CFG_TASK_START_SCAN_ID, CFG_SCH_PRIO_0); + + /* Initialize update timer */ + HW_TS_Create(CFG_TIM_PROC_ID_ISR, &timer_id, hw_ts_Repeated, FS_ActiveLook_Timer); } void FS_ActiveLook_DeInit(void) { + /* Reset state */ + s_state = AL_STATE_INIT; + + /* Delete update timer */ + HW_TS_Delete(timer_id); + /* Disconnect from the device if desired */ UTIL_SEQ_SetTask(1 << CFG_TASK_DISCONN_DEV_1_ID, CFG_SCH_PRIO_0); } From e966aeae0abbb39ac1e9347f7198c51ccbffbdfe Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Fri, 21 Feb 2025 20:09:43 -0800 Subject: [PATCH 32/81] Add skeleton for config options --- FlySight/active_control.c | 4 - FlySight/active_mode.c | 16 +- FlySight/activelook.c | 399 +++++++----------------------------- FlySight/activelook.h | 7 +- FlySight/activelook_mode0.c | 333 ++++++++++++++++++++++++++++++ FlySight/activelook_mode0.h | 33 +++ FlySight/config.c | 25 ++- FlySight/config.h | 7 + 8 files changed, 485 insertions(+), 339 deletions(-) create mode 100644 FlySight/activelook_mode0.c create mode 100644 FlySight/activelook_mode0.h diff --git a/FlySight/active_control.c b/FlySight/active_control.c index 9a076dd..8364a07 100644 --- a/FlySight/active_control.c +++ b/FlySight/active_control.c @@ -24,7 +24,6 @@ #include #include "main.h" -#include "activelook.h" #include "app_common.h" #include "audio_control.h" #include "baro.h" @@ -228,9 +227,6 @@ void FS_ActiveControl_DataReady_Callback(void) // Update BLE characteristic Custom_GNSS_Update(data); - // Update ActiveLook glasses - FS_ActiveLook_GNSS_Update(data); - hasFix = (data->gpsFix == 3); } diff --git a/FlySight/active_mode.c b/FlySight/active_mode.c index ae4bf39..0d6f320 100644 --- a/FlySight/active_mode.c +++ b/FlySight/active_mode.c @@ -48,9 +48,6 @@ void FS_ActiveMode_Init(void) uint8_t enable_flags; bool isSystemHealthy = true; - /* Initialize ActiveLook interface */ - FS_ActiveLook_Init(); - /* Initialize FatFS */ FS_ResourceManager_RequestResource(FS_RESOURCE_FATFS); @@ -73,6 +70,12 @@ void FS_ActiveMode_Init(void) FS_Config_Read(FS_State_Get()->config_filename); } + if (FS_Config_Get()->al_mode != 0) + { + /* Initialize ActiveLook interface */ + FS_ActiveLook_Init(); + } + if (FS_Config_Get()->enable_logging) { // Initialize enable flags @@ -208,8 +211,11 @@ void FS_ActiveMode_Init(void) void FS_ActiveMode_DeInit(void) { - /* De-initialize ActiveLook interface */ - FS_ActiveLook_DeInit(); + if (FS_Config_Get()->al_mode != 0) + { + /* De-initialize ActiveLook interface */ + FS_ActiveLook_DeInit(); + } /* Disable controller */ FS_ActiveControl_DeInit(); diff --git a/FlySight/activelook.c b/FlySight/activelook.c index 695c4d3..301f02a 100644 --- a/FlySight/activelook.c +++ b/FlySight/activelook.c @@ -1,7 +1,9 @@ #include "main.h" #include "activelook.h" #include "activelook_client.h" +#include "activelook_mode0.h" #include "app_common.h" +#include "config.h" #include "dbg_trace.h" #include "stm32_seq.h" #include @@ -15,11 +17,7 @@ typedef enum { AL_STATE_INIT = 0, /* Not discovered yet */ AL_STATE_CFG_WRITE, - AL_STATE_SETUP_L1, - AL_STATE_SETUP_L2, - AL_STATE_SETUP_L3, - AL_STATE_SETUP_L4, - AL_STATE_SETUP_PAGE, + AL_STATE_SETUP, AL_STATE_CFG_SET, AL_STATE_CLEAR, /* Need to send "clear" command */ AL_STATE_READY, /* Idle, waiting for GNSS data */ @@ -28,10 +26,41 @@ typedef enum /*----- Module-level static variables -----*/ static FS_ActiveLook_State_t s_state = AL_STATE_INIT; -static FS_GNSS_Data_t s_gnssDataCache; static uint8_t timer_id; +typedef struct { + /** + * Called exactly once when this mode is selected, before setup() is called. + * This resets any static variables so we start from scratch. + */ + void (*init)(void); + + /** + * Called once (or multiple times) per pass of the main SM, until + * it returns FS_AL_SETUP_DONE. + */ + FS_ActiveLook_SetupStatus_t (*setup)(void); + + /** + * Called whenever we want the mode to redraw or update data. + */ + void (*update)(void); +} FS_ActiveLook_ModeOps; + +static const FS_ActiveLook_ModeOps s_modeTable[] = +{ + { // mode 0 + FS_ActiveLook_Mode0_Init, + FS_ActiveLook_Mode0_Setup, + FS_ActiveLook_Mode0_Update + }, + // etc +}; + +#define FS_ACTIVELOOK_NUM_MODES (sizeof(s_modeTable)/sizeof(s_modeTable[0])) +static const FS_ActiveLook_ModeOps *s_currentMode = NULL; + /* Forward references */ static void FS_ActiveLook_Timer(void); static void FS_ActiveLook_Task(void); @@ -43,6 +72,19 @@ static const FS_ActiveLook_ClientCb_t s_alk_cb = .OnDiscoveryComplete = OnActiveLookDiscoveryComplete }; +void AL_SelectMode(uint8_t modeId) +{ + // Suppose you have s_modeTable[] with one entry per mode + if (modeId < FS_ACTIVELOOK_NUM_MODES) + s_currentMode = &s_modeTable[modeId]; + else + s_currentMode = &s_modeTable[0]; + + // Call the mode’s init if it exists + if (s_currentMode->init) + s_currentMode->init(); +} + /******************************************************************************* * Called by the ActiveLook client once the Rx characteristic is discovered. * We'll move the state to CLEAR, meaning "clear" next, and schedule the task. @@ -54,270 +96,6 @@ static void OnActiveLookDiscoveryComplete(void) UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); } -/******************************************************************************* - * Called from your GNSS logic whenever new GNSS data is available. - * If we're READY, copy data, set state=UPDATE_SETUP, and schedule the task. - * Otherwise skip it. - ******************************************************************************/ -void FS_ActiveLook_GNSS_Update(const FS_GNSS_Data_t *current) -{ - /* Cache the data for later use */ - s_gnssDataCache = *current; -} - -/******************************************************************************* - * A helper to do a single WriteWithoutResp, building or storing a raw packet. - * We do only one call per pass of the state machine to avoid spamming BLE. - ******************************************************************************/ -static void AL_SendRaw(const uint8_t *data, uint16_t length) -{ - FS_ActiveLook_Client_WriteWithoutResp(data, length); -} - -/******************************************************************************* - * Build a "layoutSave" command for one row, with or without an icon, etc. - * We'll keep it simple: no icon here, just the normal text area. Adjust as needed. - * - * The snippet below demonstrates how you might build the layout data. - * If you want an icon, add sub-commands for 'img', 'txt', etc. in "extra[]". - * - * Return the final size of the packet in 'outLen', so the caller can do: - * AL_SendRaw(buffer, outLen); - ******************************************************************************/ -static uint8_t AL_BuildLayout( - uint8_t layoutId, - const char *headingText, - const char *unitsText, - uint8_t *outBuf -) -{ - // Start building the buffer - uint8_t idx = 0; - outBuf[idx++] = 0xFF; // Start - outBuf[idx++] = 0x60; // "layoutSave" - outBuf[idx++] = 0x00; // format => 1-byte length - uint8_t lenPos = idx++; // We'll fill the total length later - - // layout ID - outBuf[idx++] = layoutId; - - // We'll fill the additional commands size later - uint8_t addCmdSizePos = idx++; - - // X (2 bytes, MSB first) - // Suppose x=0 => 0x00, 0x00 - outBuf[idx++] = 0x00; // x hi - outBuf[idx++] = 0x00; // x lo - - // Y (1 byte!) - outBuf[idx++] = 0x00; - - // Width (2 bytes, MSB first). E.g. 304 => 0x01,0x30 - outBuf[idx++] = 0x01; // width hi - outBuf[idx++] = 0x30; // width lo - - // Height (1 byte!) - // e.g. 40 => 0x28 - outBuf[idx++] = 0x28; - - // ForeColor, BackColor, Font - outBuf[idx++] = 15; // foreColor - outBuf[idx++] = 0; // backColor - outBuf[idx++] = 2; // font - - // TextValid - outBuf[idx++] = 1; // 1 => use dynamic text argument - - // Text X (2 bytes, MSB first), say 200 - outBuf[idx++] = 0; - outBuf[idx++] = 200; - - // Text Y (1 byte!), e.g. 40 - outBuf[idx++] = 40; - - // Text Rotation (1 byte) - outBuf[idx++] = 4; - - // Text Opacity (1 byte) - outBuf[idx++] = 1; - - //------------------------------------------------------- - // Additional sub-commands for icon or units, if desired: - //------------------------------------------------------- - uint8_t extra[64]; - uint8_t e = 0; - - // Example: color=15 - extra[e++] = 0x03; // sub-cmd ID "color" - extra[e++] = 15; // color=15 - - // Example: font=1 - extra[e++] = 0x04; - extra[e++] = 1; // smaller font - - // Example: txt => position= (260,35) - extra[e++] = 0x09; // "text" - // x=80 => 2 bytes - extra[e++] = 1; - extra[e++] = 5; - // y=0 => 2 bytes - extra[e++] = 0; - extra[e++] = 35; - - // Then one byte with length, then the string - size_t headingLen = strlen(headingText); - extra[e++] = (uint8_t)headingLen; - memcpy(&extra[e], headingText, headingLen); - e += headingLen; - - // Example: font=1 - extra[e++] = 0x04; - extra[e++] = 1; // smaller font - - // Example: txt => position= (80,35) - extra[e++] = 0x09; // "text" - // x=80 => 2 bytes - extra[e++] = 0; - extra[e++] = 80; - // y=0 => 2 bytes - extra[e++] = 0; - extra[e++] = 35; - - // Then one byte with length, then the string - size_t unitsLen = strlen(unitsText); - extra[e++] = (uint8_t)unitsLen; - memcpy(&extra[e], unitsText, unitsLen); - e += unitsLen; - - // Done building sub-commands - outBuf[addCmdSizePos] = e; - memcpy(&outBuf[idx], extra, e); - idx += e; - - // Footer - outBuf[idx++] = 0xAA; - - // Fill total length - outBuf[lenPos] = idx; - return idx; -} - -/******************************************************************************* - * Build a "pageSave" command with 4 sublayouts. We'll call it ID=10, referencing - * layoutIds=1..4, each at offset x=0,y=0 inside the page. - * - * Return the final length so caller can AL_SendRaw() it. - ******************************************************************************/ -static uint8_t AL_BuildPage(uint8_t pageId, uint8_t *outBuf) -{ - /* pageSave => command=0x80. - Format => 0x00 => 1-byte length. - The structure is: pageId, then for each sublayout: - layoutId(1B), x(2B?), y(2B?), ... - The exact number of bytes for x,y depends on your firmware doc. - We'll assume x=2 bytes, y=2 bytes for safety. Or if doc says otherwise, adapt. - */ - uint8_t idx = 0; - - outBuf[idx++] = 0xFF; // start - outBuf[idx++] = 0x80; // "pageSave" - outBuf[idx++] = 0x00; // format => 1B length - uint8_t lenPos = idx++; - - outBuf[idx++] = pageId; - - // We have 4 sublayouts: layout #1..4 - // Each sublayout => layoutId(1B), x(2B), y(2B) - // We'll set x=0,y=0 for each. - - // sublayout #1 - outBuf[idx++] = 10; // layoutId - outBuf[idx++] = 0x00; // x hi - outBuf[idx++] = 0x00; // x lo - outBuf[idx++] = 168; // y lo - - // sublayout #2 - outBuf[idx++] = 11; - outBuf[idx++] = 0x00; - outBuf[idx++] = 0x00; - outBuf[idx++] = 128; - - // sublayout #3 - outBuf[idx++] = 12; - outBuf[idx++] = 0x00; - outBuf[idx++] = 0x00; - outBuf[idx++] = 88; - - // sublayout #4 - outBuf[idx++] = 13; - outBuf[idx++] = 0x00; - outBuf[idx++] = 0x00; - outBuf[idx++] = 48; - - // Footer - outBuf[idx++] = 0xAA; - - // Fill length - outBuf[lenPos] = idx; - - return idx; -} - -/******************************************************************************* - * Build a single "pageClearAndDisplay" command with 4 text arguments, - * separated by '\0'. That is the entire update in one WWR call. - ******************************************************************************/ -static uint8_t AL_BuildPageUpdate(uint8_t pageId, - const char *line1, - const char *line2, - const char *line3, - const char *line4, - uint8_t *outBuf) -{ - /* layoutClearAndDisplay or pageClearAndDisplay => 0x6A - structure => [ pageId, line1\0 line2\0 line3\0 line4\0 ] - */ - uint8_t idx = 0; - - outBuf[idx++] = 0xFF; - outBuf[idx++] = 0x86; // "pageClearAndDisplay" - outBuf[idx++] = 0x00; // format => 1B length - uint8_t lenPos = idx++; - - outBuf[idx++] = pageId; - - // Copy line1 plus a null - size_t l1 = strlen(line1); - memcpy(&outBuf[idx], line1, l1); - idx += l1; - outBuf[idx++] = 0; - - // line2 - size_t l2 = strlen(line2); - memcpy(&outBuf[idx], line2, l2); - idx += l2; - outBuf[idx++] = 0; - - // line3 - size_t l3 = strlen(line3); - memcpy(&outBuf[idx], line3, l3); - idx += l3; - outBuf[idx++] = 0; - - // line4 - size_t l4 = strlen(line4); - memcpy(&outBuf[idx], line4, l4); - idx += l4; - outBuf[idx++] = 0; - - // Footer - outBuf[idx++] = 0xAA; - - // fill length - outBuf[lenPos] = idx; - return idx; -} - /******************************************************************************* * Our main sequencer task. We handle the state machine, sending WWR commands * without blocking. Each line is sent in a separate state so that we don't @@ -326,8 +104,6 @@ static uint8_t AL_BuildPageUpdate(uint8_t pageId, static void FS_ActiveLook_Task(void) { char tmp[32]; - static uint8_t buf[128]; /* re-usable buffer for building commands */ - uint8_t length; switch (s_state) { @@ -375,51 +151,34 @@ static void FS_ActiveLook_Task(void) /* Now send it with Write Without Response */ FS_ActiveLook_Client_WriteWithoutResp(packet, idx); + /* Get mode from config file */ + AL_SelectMode(FS_Config_Get()->al_mode - 1); + /* Next config step */ - s_state = AL_STATE_SETUP_L1; + s_state = AL_STATE_SETUP; UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); break; } - case AL_STATE_SETUP_L1: - length = AL_BuildLayout(10, "Hdg:", "deg", buf); - AL_SendRaw(buf, length); - APP_DBG_MSG("Layout #1 defined.\n"); - s_state = AL_STATE_SETUP_L2; - UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); - break; - - case AL_STATE_SETUP_L2: - length = AL_BuildLayout(11, "Vh:", "m/s", buf); - AL_SendRaw(buf, length); - APP_DBG_MSG("Layout #2 defined.\n"); - s_state = AL_STATE_SETUP_L3; - UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); - break; - - case AL_STATE_SETUP_L3: - length = AL_BuildLayout(12, "Vd:", "m/s", buf); - AL_SendRaw(buf, length); - APP_DBG_MSG("Layout #3 defined.\n"); - s_state = AL_STATE_SETUP_L4; - UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); - break; - - case AL_STATE_SETUP_L4: - length = AL_BuildLayout(13, "Ele:", "m", buf); - AL_SendRaw(buf, length); - APP_DBG_MSG("Layout #4 defined.\n"); - s_state = AL_STATE_SETUP_PAGE; - UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); - break; - - case AL_STATE_SETUP_PAGE: - length = AL_BuildPage(10, buf); - AL_SendRaw(buf, length); - APP_DBG_MSG("Page #10 referencing layouts #10..#13 defined.\n"); - s_state = AL_STATE_CFG_SET; - UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); + case AL_STATE_SETUP: + { + if (s_currentMode && s_currentMode->setup) + { + FS_ActiveLook_SetupStatus_t status = s_currentMode->setup(); + if (status == FS_AL_SETUP_DONE) + { + // Once the mode is fully set up, move on + s_state = AL_STATE_CFG_SET; + } + else + { + // The mode still needs more calls + // We'll come back here on next pass + } + UTIL_SEQ_SetTask(1 << CFG_TASK_FS_ACTIVELOOK_ID, CFG_SCH_PRIO_0); + } break; + } case AL_STATE_CFG_SET: { @@ -483,23 +242,11 @@ static void FS_ActiveLook_Task(void) /* Idle: no action */ break; - case AL_STATE_UPDATE: - { - /* Build 4 strings from our GNSS data. Then do a single "pageClearAndDisplay" */ - char line1[16], line2[16], line3[16], line4[16]; - snprintf(line1, sizeof(line1), "%ld",(long)(s_gnssDataCache.heading)); - snprintf(line2, sizeof(line2), "%ld",(long)(s_gnssDataCache.gSpeed/100)); - snprintf(line3, sizeof(line3), "%ld",(long)(s_gnssDataCache.velD/1000)); - snprintf(line4, sizeof(line4), "%ld",(long)(s_gnssDataCache.hMSL/1000)); - - length = AL_BuildPageUpdate(10, line1, line2, line3, line4, buf); - AL_SendRaw(buf, length); - APP_DBG_MSG("ActiveLook: Page #10 updated with heading/speed/etc.\n"); - + if (s_currentMode && s_currentMode->update) + s_currentMode->update(); s_state = AL_STATE_READY; - } - break; + break; } } diff --git a/FlySight/activelook.h b/FlySight/activelook.h index ef2a25f..f527100 100644 --- a/FlySight/activelook.h +++ b/FlySight/activelook.h @@ -24,11 +24,12 @@ #ifndef ACTIVELOOK_H_ #define ACTIVELOOK_H_ -#include "gnss.h" +typedef enum { + FS_AL_SETUP_IN_PROGRESS = 0, + FS_AL_SETUP_DONE +} FS_ActiveLook_SetupStatus_t; void FS_ActiveLook_Init(void); void FS_ActiveLook_DeInit(void); -void FS_ActiveLook_GNSS_Update(const FS_GNSS_Data_t *current); - #endif /* ACTIVELOOK_H_ */ diff --git a/FlySight/activelook_mode0.c b/FlySight/activelook_mode0.c new file mode 100644 index 0000000..e6e1919 --- /dev/null +++ b/FlySight/activelook_mode0.c @@ -0,0 +1,333 @@ +#include "main.h" +#include "activelook.h" +#include "activelook_client.h" +#include "activelook_mode0.h" +#include "gnss.h" +#include +#include + +// A static variable that persists across calls, local to Mode0 +static int s_step = 0; + +/******************************************************************************* + * A helper to do a single WriteWithoutResp, building or storing a raw packet. + * We do only one call per pass of the state machine to avoid spamming BLE. + ******************************************************************************/ +static void AL_SendRaw(const uint8_t *data, uint16_t length) +{ + FS_ActiveLook_Client_WriteWithoutResp(data, length); +} + +/******************************************************************************* + * Build a "layoutSave" command for one row, with or without an icon, etc. + * We'll keep it simple: no icon here, just the normal text area. Adjust as needed. + * + * The snippet below demonstrates how you might build the layout data. + * If you want an icon, add sub-commands for 'img', 'txt', etc. in "extra[]". + * + * Return the final size of the packet in 'outLen', so the caller can do: + * AL_SendRaw(buffer, outLen); + ******************************************************************************/ +static uint8_t AL_BuildLayout( + uint8_t layoutId, + const char *headingText, + const char *unitsText, + uint8_t *outBuf +) +{ + // Start building the buffer + uint8_t idx = 0; + outBuf[idx++] = 0xFF; // Start + outBuf[idx++] = 0x60; // "layoutSave" + outBuf[idx++] = 0x00; // format => 1-byte length + uint8_t lenPos = idx++; // We'll fill the total length later + + // layout ID + outBuf[idx++] = layoutId; + + // We'll fill the additional commands size later + uint8_t addCmdSizePos = idx++; + + // X (2 bytes, MSB first) + // Suppose x=0 => 0x00, 0x00 + outBuf[idx++] = 0x00; // x hi + outBuf[idx++] = 0x00; // x lo + + // Y (1 byte!) + outBuf[idx++] = 0x00; + + // Width (2 bytes, MSB first). E.g. 304 => 0x01,0x30 + outBuf[idx++] = 0x01; // width hi + outBuf[idx++] = 0x30; // width lo + + // Height (1 byte!) + // e.g. 40 => 0x28 + outBuf[idx++] = 0x28; + + // ForeColor, BackColor, Font + outBuf[idx++] = 15; // foreColor + outBuf[idx++] = 0; // backColor + outBuf[idx++] = 2; // font + + // TextValid + outBuf[idx++] = 1; // 1 => use dynamic text argument + + // Text X (2 bytes, MSB first), say 200 + outBuf[idx++] = 0; + outBuf[idx++] = 200; + + // Text Y (1 byte!), e.g. 40 + outBuf[idx++] = 40; + + // Text Rotation (1 byte) + outBuf[idx++] = 4; + + // Text Opacity (1 byte) + outBuf[idx++] = 1; + + //------------------------------------------------------- + // Additional sub-commands for icon or units, if desired: + //------------------------------------------------------- + uint8_t extra[64]; + uint8_t e = 0; + + // Example: color=15 + extra[e++] = 0x03; // sub-cmd ID "color" + extra[e++] = 15; // color=15 + + // Example: font=1 + extra[e++] = 0x04; + extra[e++] = 1; // smaller font + + // Example: txt => position= (260,35) + extra[e++] = 0x09; // "text" + // x=80 => 2 bytes + extra[e++] = 1; + extra[e++] = 5; + // y=0 => 2 bytes + extra[e++] = 0; + extra[e++] = 35; + + // Then one byte with length, then the string + size_t headingLen = strlen(headingText); + extra[e++] = (uint8_t)headingLen; + memcpy(&extra[e], headingText, headingLen); + e += headingLen; + + // Example: font=1 + extra[e++] = 0x04; + extra[e++] = 1; // smaller font + + // Example: txt => position= (80,35) + extra[e++] = 0x09; // "text" + // x=80 => 2 bytes + extra[e++] = 0; + extra[e++] = 80; + // y=0 => 2 bytes + extra[e++] = 0; + extra[e++] = 35; + + // Then one byte with length, then the string + size_t unitsLen = strlen(unitsText); + extra[e++] = (uint8_t)unitsLen; + memcpy(&extra[e], unitsText, unitsLen); + e += unitsLen; + + // Done building sub-commands + outBuf[addCmdSizePos] = e; + memcpy(&outBuf[idx], extra, e); + idx += e; + + // Footer + outBuf[idx++] = 0xAA; + + // Fill total length + outBuf[lenPos] = idx; + return idx; +} + +/******************************************************************************* + * Build a "pageSave" command with 4 sublayouts. We'll call it ID=10, referencing + * layoutIds=1..4, each at offset x=0,y=0 inside the page. + * + * Return the final length so caller can AL_SendRaw() it. + ******************************************************************************/ +static uint8_t AL_BuildPage(uint8_t pageId, uint8_t *outBuf) +{ + /* pageSave => command=0x80. + Format => 0x00 => 1-byte length. + The structure is: pageId, then for each sublayout: + layoutId(1B), x(2B?), y(2B?), ... + The exact number of bytes for x,y depends on your firmware doc. + We'll assume x=2 bytes, y=2 bytes for safety. Or if doc says otherwise, adapt. + */ + uint8_t idx = 0; + + outBuf[idx++] = 0xFF; // start + outBuf[idx++] = 0x80; // "pageSave" + outBuf[idx++] = 0x00; // format => 1B length + uint8_t lenPos = idx++; + + outBuf[idx++] = pageId; + + // We have 4 sublayouts: layout #1..4 + // Each sublayout => layoutId(1B), x(2B), y(2B) + // We'll set x=0,y=0 for each. + + // sublayout #1 + outBuf[idx++] = 10; // layoutId + outBuf[idx++] = 0x00; // x hi + outBuf[idx++] = 0x00; // x lo + outBuf[idx++] = 168; // y lo + + // sublayout #2 + outBuf[idx++] = 11; + outBuf[idx++] = 0x00; + outBuf[idx++] = 0x00; + outBuf[idx++] = 128; + + // sublayout #3 + outBuf[idx++] = 12; + outBuf[idx++] = 0x00; + outBuf[idx++] = 0x00; + outBuf[idx++] = 88; + + // sublayout #4 + outBuf[idx++] = 13; + outBuf[idx++] = 0x00; + outBuf[idx++] = 0x00; + outBuf[idx++] = 48; + + // Footer + outBuf[idx++] = 0xAA; + + // Fill length + outBuf[lenPos] = idx; + + return idx; +} + +/******************************************************************************* + * Build a single "pageClearAndDisplay" command with 4 text arguments, + * separated by '\0'. That is the entire update in one WWR call. + ******************************************************************************/ +static uint8_t AL_BuildPageUpdate(uint8_t pageId, + const char *line1, + const char *line2, + const char *line3, + const char *line4, + uint8_t *outBuf) +{ + /* layoutClearAndDisplay or pageClearAndDisplay => 0x6A + structure => [ pageId, line1\0 line2\0 line3\0 line4\0 ] + */ + uint8_t idx = 0; + + outBuf[idx++] = 0xFF; + outBuf[idx++] = 0x86; // "pageClearAndDisplay" + outBuf[idx++] = 0x00; // format => 1B length + uint8_t lenPos = idx++; + + outBuf[idx++] = pageId; + + // Copy line1 plus a null + size_t l1 = strlen(line1); + memcpy(&outBuf[idx], line1, l1); + idx += l1; + outBuf[idx++] = 0; + + // line2 + size_t l2 = strlen(line2); + memcpy(&outBuf[idx], line2, l2); + idx += l2; + outBuf[idx++] = 0; + + // line3 + size_t l3 = strlen(line3); + memcpy(&outBuf[idx], line3, l3); + idx += l3; + outBuf[idx++] = 0; + + // line4 + size_t l4 = strlen(line4); + memcpy(&outBuf[idx], line4, l4); + idx += l4; + outBuf[idx++] = 0; + + // Footer + outBuf[idx++] = 0xAA; + + // fill length + outBuf[lenPos] = idx; + return idx; +} + +// Mode0's init() => reset s_step +void FS_ActiveLook_Mode0_Init(void) +{ + s_step = 0; +} + +// Mode0's setup() => multi-step approach +FS_ActiveLook_SetupStatus_t FS_ActiveLook_Mode0_Setup(void) +{ + uint8_t buf[128]; + uint8_t length; + + switch (s_step) + { + case 0: + length = AL_BuildLayout(10, "Hdg:", "deg", buf); + AL_SendRaw(buf, length); + s_step++; + return FS_AL_SETUP_IN_PROGRESS; + + case 1: + length = AL_BuildLayout(11, "Vh:", "m/s", buf); + AL_SendRaw(buf, length); + s_step++; + return FS_AL_SETUP_IN_PROGRESS; + + case 2: + length = AL_BuildLayout(12, "Vd:", "m/s", buf); + AL_SendRaw(buf, length); + s_step++; + return FS_AL_SETUP_IN_PROGRESS; + + case 3: + length = AL_BuildLayout(13, "Ele:", "m", buf); + AL_SendRaw(buf, length); + s_step++; + return FS_AL_SETUP_IN_PROGRESS; + + case 4: + length = AL_BuildPage(10, buf); + AL_SendRaw(buf, length); + s_step++; + // We are done with setup + return FS_AL_SETUP_DONE; + + default: + // If we get here, we’re already done + return FS_AL_SETUP_DONE; + } +} + +// Mode0's update() => build the new page and send +void FS_ActiveLook_Mode0_Update(void) +{ + char line1[16], line2[16], line3[16], line4[16]; + uint8_t buf[128]; + uint8_t length; + + /* Build 4 strings from our GNSS data. Then do a single "pageClearAndDisplay" */ + const FS_GNSS_Data_t *data = FS_GNSS_GetData(); + snprintf(line1, sizeof(line1), "%ld",(long)(data->heading)); + snprintf(line2, sizeof(line2), "%ld",(long)(data->gSpeed/100)); + snprintf(line3, sizeof(line3), "%ld",(long)(data->velD/1000)); + snprintf(line4, sizeof(line4), "%ld",(long)(data->hMSL/1000)); + + length = AL_BuildPageUpdate(10, line1, line2, line3, line4, buf); + AL_SendRaw(buf, length); +} + diff --git a/FlySight/activelook_mode0.h b/FlySight/activelook_mode0.h new file mode 100644 index 0000000..7ae00aa --- /dev/null +++ b/FlySight/activelook_mode0.h @@ -0,0 +1,33 @@ +/*************************************************************************** +** ** +** FlySight 2 firmware ** +** Copyright 2025 Bionic Avionics Inc. ** +** ** +** 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 . ** +** ** +**************************************************************************** +** Contact: Bionic Avionics Inc. ** +** Website: http://flysight.ca/ ** +****************************************************************************/ + +#ifndef ACTIVELOOK_MODE0_H_ +#define ACTIVELOOK_MODE0_H_ + +#include "activelook.h" + +void FS_ActiveLook_Mode0_Init(void); +FS_ActiveLook_SetupStatus_t FS_ActiveLook_Mode0_Setup(void); +void FS_ActiveLook_Mode0_Update(void); + +#endif /* ACTIVELOOK_MODE0_H_ */ diff --git a/FlySight/config.c b/FlySight/config.c index 2035894..484fcd2 100644 --- a/FlySight/config.c +++ b/FlySight/config.c @@ -200,7 +200,16 @@ static const char defaultConfig[] = "; alarms will be audible.\n" "\n" "Win_Top: 0 ; Silence window top (m)\n" - "Win_Bottom: 0 ; Silence window bottom (m)\n"; + "Win_Bottom: 0 ; Silence window bottom (m)\n" + "\n" + "; ActiveLook interface\n" + "\n" + "AL_ID: 030213 ; ActiveLook device ID\n" + "AL_Mode: 1 ; ActiveLook mode\n" + "AL_Line_1: 12 ; Course\n" + "AL_Line_2: 0 ; Horizontal speed\n" + "AL_Line_3: 1 ; Vertical speed\n" + "AL_Line_4: 11 ; Altitude above DZ_Elev\n"; void FS_Config_Init(void) { @@ -272,6 +281,13 @@ void FS_Config_Init(void) config.end_nav = 0; config.max_dist = 10000; config.min_angle = 5; + + config.al_id = 0; + config.al_mode = 1; + config.al_line_1 = 13; + config.al_line_2 = 0; + config.al_line_3 = 1; + config.al_line_4 = 12; } FS_Config_Result_t FS_Config_Read(const char *filename) @@ -362,6 +378,13 @@ FS_Config_Result_t FS_Config_Read(const char *filename) HANDLE_VALUE("Max_Dist", config.max_dist, val, val >= 0 && val <= 10000); HANDLE_VALUE("Min_Angle", config.min_angle, val, val >= 0 && val <= 360); + HANDLE_VALUE("AL_ID", config.al_id, val, val >= 0 && val <= 65535); + HANDLE_VALUE("AL_Mode", config.al_mode, val, val >= 0 && val <= 1); + HANDLE_VALUE("AL_Line_1", config.al_line_1, val, val >= 0 && val <= 13); + HANDLE_VALUE("AL_Line_2", config.al_line_2, val, val >= 0 && val <= 13); + HANDLE_VALUE("AL_Line_3", config.al_line_3, val, val >= 0 && val <= 13); + HANDLE_VALUE("AL_Line_4", config.al_line_4, val, val >= 0 && val <= 13); + #undef HANDLE_VALUE if (!strcmp(name, "Init_File")) diff --git a/FlySight/config.h b/FlySight/config.h index 939c7b0..a16a2c9 100644 --- a/FlySight/config.h +++ b/FlySight/config.h @@ -157,6 +157,13 @@ typedef struct uint16_t end_nav; uint16_t max_dist; uint16_t min_angle; + + uint16_t al_id; + uint8_t al_mode; + uint8_t al_line_1; + uint8_t al_line_2; + uint8_t al_line_3; + uint8_t al_line_4; } FS_Config_Data_t; typedef enum { From b7a571e252669b239cb7583fd8a48fb96d5fa9ac Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Fri, 21 Feb 2025 20:44:36 -0800 Subject: [PATCH 33/81] Use config file to define display rows --- FlySight/activelook_mode0.c | 421 +++++++++++++++++++++--------------- FlySight/activelook_mode0.h | 19 +- 2 files changed, 264 insertions(+), 176 deletions(-) diff --git a/FlySight/activelook_mode0.c b/FlySight/activelook_mode0.c index e6e1919..843e9be 100644 --- a/FlySight/activelook_mode0.c +++ b/FlySight/activelook_mode0.c @@ -1,216 +1,219 @@ -#include "main.h" -#include "activelook.h" -#include "activelook_client.h" #include "activelook_mode0.h" -#include "gnss.h" -#include +#include "activelook_client.h" // For FS_ActiveLook_Client_WriteWithoutResp +#include "config.h" // For FS_Config_Get() +#include "gnss.h" // For FS_GNSS_GetData() #include - -// A static variable that persists across calls, local to Mode0 +#include +#include + +/* -------------------------------------------------------------------------- + 1. Data Structures for Each Line + -------------------------------------------------------------------------- */ + +/** + * A function pointer type that, given GNSS data, returns a double + * representing the value we want displayed. e.g. heading, alt, etc. + */ +typedef double (*LineValueFn_t)(const FS_GNSS_Data_t*); + +/* Simple "getter" functions for each known line type */ +static double LN_HSpeed(const FS_GNSS_Data_t *d) { return (double)d->gSpeed / 100.0; } +static double LN_VSpeed(const FS_GNSS_Data_t *d) { return (double)d->velD / 1000.0; } +static double LN_Heading(const FS_GNSS_Data_t *d) { return (double)d->heading; } +static double LN_Altitude(const FS_GNSS_Data_t *d) { return (double)d->hMSL / 1000.0; } + +/** + * A table entry describing one line type: + * - typeId: which numeric ID from the config (0,1,7,12, etc.) + * - label: text label to show on the layout, e.g. "HSpd:" + * - units: text units after the label, e.g. "m/s" + * - fn: function that returns the numeric value for the line + */ +typedef struct { + uint8_t typeId; + const char *label; + const char *units; + LineValueFn_t fn; +} AL_Mode0_LineMap_t; + +/* + A dictionary of possible line types. + Fill in as many as you want to support. +*/ +static const AL_Mode0_LineMap_t s_lineMap[] = +{ + { 0, "HSpd:", "m/s", LN_HSpeed }, + { 1, "VSpd:", "m/s", LN_VSpeed }, + { 12, "Alt:", "m", LN_Altitude }, + { 13, "Hdg:", "deg", LN_Heading }, +}; +static const unsigned s_lineMapCount = sizeof(s_lineMap) / sizeof(s_lineMap[0]); + +/** + * For the four lines in this mode, we store a "resolved" spec. + * That includes a pointer to the label, units, and function pointer + * to compute the value. We fill this in FS_ActiveLook_Mode0_Init() + * based on the user config (al_line_1..4). + */ +typedef struct { + const char *label; + const char *units; + LineValueFn_t fn; +} AL_Mode0_LineSpec_t; + +static AL_Mode0_LineSpec_t s_lineSpecs[4]; + +/** + * We'll also keep a "setup" step variable for multi-step layout creation. + */ static int s_step = 0; -/******************************************************************************* - * A helper to do a single WriteWithoutResp, building or storing a raw packet. - * We do only one call per pass of the state machine to avoid spamming BLE. - ******************************************************************************/ +/* -------------------------------------------------------------------------- + 2. Helpers for Sending Commands / Building Layout + -------------------------------------------------------------------------- */ + +/* A helper to do a single WriteWithoutResp to the BLE client. */ static void AL_SendRaw(const uint8_t *data, uint16_t length) { FS_ActiveLook_Client_WriteWithoutResp(data, length); } -/******************************************************************************* - * Build a "layoutSave" command for one row, with or without an icon, etc. - * We'll keep it simple: no icon here, just the normal text area. Adjust as needed. - * - * The snippet below demonstrates how you might build the layout data. - * If you want an icon, add sub-commands for 'img', 'txt', etc. in "extra[]". - * - * Return the final size of the packet in 'outLen', so the caller can do: - * AL_SendRaw(buffer, outLen); - ******************************************************************************/ -static uint8_t AL_BuildLayout( - uint8_t layoutId, - const char *headingText, - const char *unitsText, - uint8_t *outBuf -) +/** + * Build your "layoutSave" command. + * This is the same code you've used; simplified for brevity. + */ +static uint8_t AL_BuildLayout(uint8_t layoutId, + const char *headingText, + const char *unitsText, + uint8_t *outBuf) { - // Start building the buffer uint8_t idx = 0; - outBuf[idx++] = 0xFF; // Start + outBuf[idx++] = 0xFF; outBuf[idx++] = 0x60; // "layoutSave" - outBuf[idx++] = 0x00; // format => 1-byte length - uint8_t lenPos = idx++; // We'll fill the total length later + outBuf[idx++] = 0x00; // 1B length + uint8_t lenPos = idx++; // layout ID outBuf[idx++] = layoutId; - // We'll fill the additional commands size later + // Additional commands size placeholder uint8_t addCmdSizePos = idx++; - // X (2 bytes, MSB first) - // Suppose x=0 => 0x00, 0x00 - outBuf[idx++] = 0x00; // x hi - outBuf[idx++] = 0x00; // x lo - - // Y (1 byte!) + // X, Y, Width, Height, color, etc. Hard-coded example outBuf[idx++] = 0x00; - - // Width (2 bytes, MSB first). E.g. 304 => 0x01,0x30 - outBuf[idx++] = 0x01; // width hi - outBuf[idx++] = 0x30; // width lo - - // Height (1 byte!) - // e.g. 40 => 0x28 + outBuf[idx++] = 0x00; + outBuf[idx++] = 0x00; + outBuf[idx++] = 0x01; + outBuf[idx++] = 0x30; outBuf[idx++] = 0x28; - - // ForeColor, BackColor, Font - outBuf[idx++] = 15; // foreColor - outBuf[idx++] = 0; // backColor - outBuf[idx++] = 2; // font - - // TextValid - outBuf[idx++] = 1; // 1 => use dynamic text argument - - // Text X (2 bytes, MSB first), say 200 + outBuf[idx++] = 15; + outBuf[idx++] = 0; + outBuf[idx++] = 2; + outBuf[idx++] = 1; outBuf[idx++] = 0; outBuf[idx++] = 200; - - // Text Y (1 byte!), e.g. 40 outBuf[idx++] = 40; - - // Text Rotation (1 byte) outBuf[idx++] = 4; - - // Text Opacity (1 byte) outBuf[idx++] = 1; - //------------------------------------------------------- - // Additional sub-commands for icon or units, if desired: - //------------------------------------------------------- + // Build sub-commands for label & units uint8_t extra[64]; uint8_t e = 0; - // Example: color=15 - extra[e++] = 0x03; // sub-cmd ID "color" - extra[e++] = 15; // color=15 + // color + extra[e++] = 0x03; + extra[e++] = 15; - // Example: font=1 + // font extra[e++] = 0x04; - extra[e++] = 1; // smaller font - - // Example: txt => position= (260,35) - extra[e++] = 0x09; // "text" - // x=80 => 2 bytes extra[e++] = 1; - extra[e++] = 5; - // y=0 => 2 bytes - extra[e++] = 0; - extra[e++] = 35; - - // Then one byte with length, then the string - size_t headingLen = strlen(headingText); - extra[e++] = (uint8_t)headingLen; - memcpy(&extra[e], headingText, headingLen); - e += headingLen; - - // Example: font=1 + + // text => position + extra[e++] = 0x09; + extra[e++] = 1; // x hi + extra[e++] = 5; // x lo + extra[e++] = 0; // y hi + extra[e++] = 35; // y lo + + // label + size_t lblLen = strlen(headingText); + extra[e++] = (uint8_t)lblLen; + memcpy(&extra[e], headingText, lblLen); + e += lblLen; + + // font again extra[e++] = 0x04; - extra[e++] = 1; // smaller font - - // Example: txt => position= (80,35) - extra[e++] = 0x09; // "text" - // x=80 => 2 bytes - extra[e++] = 0; - extra[e++] = 80; - // y=0 => 2 bytes - extra[e++] = 0; - extra[e++] = 35; - - // Then one byte with length, then the string - size_t unitsLen = strlen(unitsText); - extra[e++] = (uint8_t)unitsLen; - memcpy(&extra[e], unitsText, unitsLen); - e += unitsLen; - - // Done building sub-commands + extra[e++] = 1; + + // text => position + extra[e++] = 0x09; + extra[e++] = 0; // x hi + extra[e++] = 80; // x lo + extra[e++] = 0; // y hi + extra[e++] = 35; // y lo + + // units + size_t untLen = strlen(unitsText); + extra[e++] = (uint8_t)untLen; + memcpy(&extra[e], unitsText, untLen); + e += untLen; + + // copy extras outBuf[addCmdSizePos] = e; memcpy(&outBuf[idx], extra, e); idx += e; - // Footer - outBuf[idx++] = 0xAA; + outBuf[idx++] = 0xAA; // footer + outBuf[lenPos] = idx; // fill length - // Fill total length - outBuf[lenPos] = idx; return idx; } -/******************************************************************************* - * Build a "pageSave" command with 4 sublayouts. We'll call it ID=10, referencing - * layoutIds=1..4, each at offset x=0,y=0 inside the page. - * - * Return the final length so caller can AL_SendRaw() it. - ******************************************************************************/ +/** + * Build a page referencing layout #10..#13. + */ static uint8_t AL_BuildPage(uint8_t pageId, uint8_t *outBuf) { - /* pageSave => command=0x80. - Format => 0x00 => 1-byte length. - The structure is: pageId, then for each sublayout: - layoutId(1B), x(2B?), y(2B?), ... - The exact number of bytes for x,y depends on your firmware doc. - We'll assume x=2 bytes, y=2 bytes for safety. Or if doc says otherwise, adapt. - */ uint8_t idx = 0; - - outBuf[idx++] = 0xFF; // start - outBuf[idx++] = 0x80; // "pageSave" - outBuf[idx++] = 0x00; // format => 1B length + outBuf[idx++] = 0xFF; + outBuf[idx++] = 0x80; + outBuf[idx++] = 0x00; uint8_t lenPos = idx++; outBuf[idx++] = pageId; - // We have 4 sublayouts: layout #1..4 - // Each sublayout => layoutId(1B), x(2B), y(2B) - // We'll set x=0,y=0 for each. - - // sublayout #1 - outBuf[idx++] = 10; // layoutId - outBuf[idx++] = 0x00; // x hi - outBuf[idx++] = 0x00; // x lo - outBuf[idx++] = 168; // y lo + // Sublayout #1 => layout=10 + outBuf[idx++] = 10; + outBuf[idx++] = 0x00; + outBuf[idx++] = 0x00; + outBuf[idx++] = 168; - // sublayout #2 + // Sublayout #2 => layout=11 outBuf[idx++] = 11; outBuf[idx++] = 0x00; outBuf[idx++] = 0x00; outBuf[idx++] = 128; - // sublayout #3 + // Sublayout #3 => layout=12 outBuf[idx++] = 12; outBuf[idx++] = 0x00; outBuf[idx++] = 0x00; outBuf[idx++] = 88; - // sublayout #4 + // Sublayout #4 => layout=13 outBuf[idx++] = 13; outBuf[idx++] = 0x00; outBuf[idx++] = 0x00; outBuf[idx++] = 48; - // Footer outBuf[idx++] = 0xAA; - - // Fill length outBuf[lenPos] = idx; - return idx; } -/******************************************************************************* - * Build a single "pageClearAndDisplay" command with 4 text arguments, - * separated by '\0'. That is the entire update in one WWR call. - ******************************************************************************/ +/** + * Build a single pageUpdate command with 4 lines separated by '\0'. + */ static uint8_t AL_BuildPageUpdate(uint8_t pageId, const char *line1, const char *line2, @@ -218,19 +221,15 @@ static uint8_t AL_BuildPageUpdate(uint8_t pageId, const char *line4, uint8_t *outBuf) { - /* layoutClearAndDisplay or pageClearAndDisplay => 0x6A - structure => [ pageId, line1\0 line2\0 line3\0 line4\0 ] - */ uint8_t idx = 0; - outBuf[idx++] = 0xFF; outBuf[idx++] = 0x86; // "pageClearAndDisplay" - outBuf[idx++] = 0x00; // format => 1B length + outBuf[idx++] = 0x00; uint8_t lenPos = idx++; outBuf[idx++] = pageId; - // Copy line1 plus a null + // line1 size_t l1 = strlen(line1); memcpy(&outBuf[idx], line1, l1); idx += l1; @@ -254,21 +253,67 @@ static uint8_t AL_BuildPageUpdate(uint8_t pageId, idx += l4; outBuf[idx++] = 0; - // Footer outBuf[idx++] = 0xAA; - - // fill length outBuf[lenPos] = idx; return idx; } -// Mode0's init() => reset s_step +/* -------------------------------------------------------------------------- + 3. Mode0 Implementation + -------------------------------------------------------------------------- */ + +/** + * FS_ActiveLook_Mode0_Init() + * + * - Reset our s_step to 0 + * - Read the config => al_line_1..4 + * - For each of the 4 lines, find a matching entry in s_lineMap + * (by typeId) and store it in s_lineSpecs[i]. + */ void FS_ActiveLook_Mode0_Init(void) { s_step = 0; + + // Read config + const FS_Config_Data_t *cfg = FS_Config_Get(); + + // Grab the line IDs the user wants + uint8_t lineTypes[4] = { + cfg->al_line_1, + cfg->al_line_2, + cfg->al_line_3, + cfg->al_line_4 + }; + + // For each line i, pick a default label/units/fn if not found + for (int i=0; i<4; i++) + { + s_lineSpecs[i].label = "?"; + s_lineSpecs[i].units = ""; + s_lineSpecs[i].fn = NULL; + + // Search the dictionary for a match + for (unsigned k=0; k < s_lineMapCount; k++) + { + if (s_lineMap[k].typeId == lineTypes[i]) + { + s_lineSpecs[i].label = s_lineMap[k].label; + s_lineSpecs[i].units = s_lineMap[k].units; + s_lineSpecs[i].fn = s_lineMap[k].fn; + break; + } + } + } } -// Mode0's setup() => multi-step approach +/** + * FS_ActiveLook_Mode0_Setup() + * + * Multi-step routine to define each layout #10..13 with the user-chosen + * label/units, then define the page referencing them. + * We do one BLE command per call, returning FS_AL_SETUP_IN_PROGRESS + * until we're done. + */ FS_ActiveLook_SetupStatus_t FS_ActiveLook_Mode0_Setup(void) { uint8_t buf[128]; @@ -277,57 +322,87 @@ FS_ActiveLook_SetupStatus_t FS_ActiveLook_Mode0_Setup(void) switch (s_step) { case 0: - length = AL_BuildLayout(10, "Hdg:", "deg", buf); + length = AL_BuildLayout(10, + s_lineSpecs[0].label, + s_lineSpecs[0].units, + buf); AL_SendRaw(buf, length); s_step++; return FS_AL_SETUP_IN_PROGRESS; case 1: - length = AL_BuildLayout(11, "Vh:", "m/s", buf); + length = AL_BuildLayout(11, + s_lineSpecs[1].label, + s_lineSpecs[1].units, + buf); AL_SendRaw(buf, length); s_step++; return FS_AL_SETUP_IN_PROGRESS; case 2: - length = AL_BuildLayout(12, "Vd:", "m/s", buf); + length = AL_BuildLayout(12, + s_lineSpecs[2].label, + s_lineSpecs[2].units, + buf); AL_SendRaw(buf, length); s_step++; return FS_AL_SETUP_IN_PROGRESS; case 3: - length = AL_BuildLayout(13, "Ele:", "m", buf); + length = AL_BuildLayout(13, + s_lineSpecs[3].label, + s_lineSpecs[3].units, + buf); AL_SendRaw(buf, length); s_step++; return FS_AL_SETUP_IN_PROGRESS; case 4: + // Finally define the page referencing #10..#13 length = AL_BuildPage(10, buf); AL_SendRaw(buf, length); s_step++; - // We are done with setup return FS_AL_SETUP_DONE; default: - // If we get here, we’re already done + // If we get here, we've already finished or are in error return FS_AL_SETUP_DONE; } } -// Mode0's update() => build the new page and send +/** + * FS_ActiveLook_Mode0_Update() + * + * Called periodically (e.g. from a timer or when new GNSS data arrives) + * to fill the 4 lines with fresh values. + * + * We call each line's function pointer to compute a double, then format + * it into a string, then send a single "pageClearAndDisplay" command + * containing all 4 lines. + */ void FS_ActiveLook_Mode0_Update(void) { - char line1[16], line2[16], line3[16], line4[16]; - uint8_t buf[128]; - uint8_t length; + const FS_GNSS_Data_t *gnss = FS_GNSS_GetData(); + char line[4][16]; - /* Build 4 strings from our GNSS data. Then do a single "pageClearAndDisplay" */ - const FS_GNSS_Data_t *data = FS_GNSS_GetData(); - snprintf(line1, sizeof(line1), "%ld",(long)(data->heading)); - snprintf(line2, sizeof(line2), "%ld",(long)(data->gSpeed/100)); - snprintf(line3, sizeof(line3), "%ld",(long)(data->velD/1000)); - snprintf(line4, sizeof(line4), "%ld",(long)(data->hMSL/1000)); + // For each line, call the function pointer to get a numeric value + for (int i=0; i<4; i++) + { + double val = 0.0; + if (s_lineSpecs[i].fn) + val = s_lineSpecs[i].fn(gnss); - length = AL_BuildPageUpdate(10, line1, line2, line3, line4, buf); + // Format as integer or float, up to you + snprintf(line[i], sizeof(line[i]), "%ld", (long)val); + } + + // Build the final packet with the 4 lines + uint8_t buf[128]; + uint8_t length = AL_BuildPageUpdate(10, + line[0], + line[1], + line[2], + line[3], + buf); AL_SendRaw(buf, length); } - diff --git a/FlySight/activelook_mode0.h b/FlySight/activelook_mode0.h index 7ae00aa..a499555 100644 --- a/FlySight/activelook_mode0.h +++ b/FlySight/activelook_mode0.h @@ -21,13 +21,26 @@ ** Website: http://flysight.ca/ ** ****************************************************************************/ -#ifndef ACTIVELOOK_MODE0_H_ -#define ACTIVELOOK_MODE0_H_ +#ifndef ACTIVELOOK_MODE0_H +#define ACTIVELOOK_MODE0_H #include "activelook.h" +/** + * Called once when the mode is selected, to reset and load config data. + */ void FS_ActiveLook_Mode0_Init(void); + +/** + * Called repeatedly to perform multi-step layout setup. Each call + * sends exactly one BLE command. Returns FS_AL_SETUP_DONE when complete. + */ FS_ActiveLook_SetupStatus_t FS_ActiveLook_Mode0_Setup(void); + +/** + * Called whenever we want to redraw or update the data on the display. + * Uses the config-based line definitions from FS_ActiveLook_Mode0_Init(). + */ void FS_ActiveLook_Mode0_Update(void); -#endif /* ACTIVELOOK_MODE0_H_ */ +#endif /* ACTIVELOOK_MODE0_H */ From 67e55690fa8908c6a7ac395a59777a5420465e41 Mon Sep 17 00:00:00 2001 From: Michael Cooper Date: Wed, 26 Feb 2025 13:51:56 -0700 Subject: [PATCH 34/81] Fix heading format --- .cproject | 2 ++ FlySight/activelook_mode0.c | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.cproject b/.cproject index 4d1a983..a0e9a47 100644 --- a/.cproject +++ b/.cproject @@ -25,6 +25,7 @@