Mercurial > public > ostc4
comparison Discovery/Src/hud.c @ 1082:1aa45000f92c Icon_Integration tip
Added configuration menu for HUD functions:
The V1 HUD has 4 functions (holes) which may be realized by one or two LEDs. The functions (like ppo2 monitoring or ascent speed) may be configurated by the diver using the HUD menu. The functions which may be selected depend on the HW configuration (e.g. the connected sensors) and the number of LEDs which are needed to realize the function. The previous HUD test implementation may still be activate usind the compile switch ENABLE_HUD_TESTING
| author | Ideenmodellierer |
|---|---|
| date | Sun, 15 Mar 2026 21:40:35 +0100 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1081:1b38d7b8da35 | 1082:1aa45000f92c |
|---|---|
| 1 /** | |
| 2 ****************************************************************************** | |
| 3 * @file hud.c | |
| 4 * @author heinrichs weikamp gmbh | |
| 5 * @version V0.0.1 | |
| 6 * @date 09-Mar-2026 | |
| 7 * @brief Support function for HUD configuration | |
| 8 * | |
| 9 @verbatim | |
| 10 ============================================================================== | |
| 11 ##### How to use ##### | |
| 12 ============================================================================== | |
| 13 @endverbatim | |
| 14 ****************************************************************************** | |
| 15 * @attention | |
| 16 * | |
| 17 * COPYRIGHT(c) 2026 heinrichs weikamp | |
| 18 * | |
| 19 ****************************************************************************** | |
| 20 */ | |
| 21 | |
| 22 #include "hud.h" | |
| 23 #include "settings.h" | |
| 24 #include "data_central.h" | |
| 25 #include "data_exchange.h" | |
| 26 #include "data_exchange_main.h" | |
| 27 #include "gfx_colors.h" | |
| 28 #include "math.h" | |
| 29 #include "tHome.h" | |
| 30 | |
| 31 uint8_t hudActive; | |
| 32 static uint8_t hudAddress; | |
| 33 static uint8_t hudVersion; | |
| 34 | |
| 35 static uint8_t hudLEDPerFct[NUM_OF_HUD_FCT]; /* array providing the version depending LED per function mapping */ | |
| 36 static uint8_t hudLEDNeedPerFct[HUD_FCT_END+1] = {0, 1, 2, 2, 2, 2, 2, 2, 0}; /* array providing information how many LEDs are needed to provide a function */ | |
| 37 | |
| 38 void hud_Init() | |
| 39 { | |
| 40 uint8_t index = 0; | |
| 41 SDiveState * pStateReal = stateRealGetPointerWrite(); | |
| 42 SSettings *pSettings = settingsGetPointer(); | |
| 43 | |
| 44 memset(pStateReal->lifeData.HUD_led_sequence,0,EXT_INTERFACE_HUD_LED_MAX); | |
| 45 hudActive = 0; | |
| 46 hudAddress = 0xFF; | |
| 47 hudVersion = 0; | |
| 48 | |
| 49 for(index = EXT_INTERFACE_MUX_OFFSET; index < EXT_INTERFACE_SENSOR_CNT; index++) | |
| 50 { | |
| 51 if(pSettings->ext_sensor_map[index] == SENSOR_HUD) | |
| 52 { | |
| 53 hudActive = 1; | |
| 54 hudAddress = index; | |
| 55 break; | |
| 56 } | |
| 57 } | |
| 58 } | |
| 59 | |
| 60 uint8_t hud_IsActive() | |
| 61 { | |
| 62 return hudActive; | |
| 63 } | |
| 64 | |
| 65 uint8_t hud_GetAddress(void) | |
| 66 { | |
| 67 return hudAddress; | |
| 68 } | |
| 69 | |
| 70 void hud_GetString(uint8_t id, uint8_t* pText) | |
| 71 { | |
| 72 switch(id) | |
| 73 { | |
| 74 case HUD_FCT_NONE: sprintf((char*)pText,"%c", TXT_Off); | |
| 75 break; | |
| 76 case HUD_FCT_WARNING: sprintf((char*)pText,"%c", TXT_Warning); | |
| 77 break; | |
| 78 case HUD_FCT_PPO2SUM: sprintf((char*)pText,"%c%c (1 - 3)", TXT_2BYTE, TXT2BYTE_O2monitor); | |
| 79 break; | |
| 80 case HUD_FCT_PPO2_0: | |
| 81 case HUD_FCT_PPO2_1: | |
| 82 case HUD_FCT_PPO2_2: sprintf((char*)pText,"%c%c (%d)", TXT_2BYTE, TXT2BYTE_O2monitor, (id - HUD_FCT_PPO2_0)); | |
| 83 break; | |
| 84 case HUD_FCT_ASCENT_SPEED: sprintf((char*)pText,"%c%c", TXT_2BYTE, TXT2BYTE_AscentSpeed); | |
| 85 break; | |
| 86 case HUD_FCT_DECO: sprintf((char*)pText,"%c%c", TXT_2BYTE, TXT2BYTE_WarnDecoMissed); | |
| 87 break; | |
| 88 default: | |
| 89 break; | |
| 90 } | |
| 91 } | |
| 92 | |
| 93 uint8_t hud_NextFct(uint8_t curFct, uint8_t fctId) | |
| 94 { | |
| 95 uint8_t done = 0; | |
| 96 uint8_t nextFct = curFct; | |
| 97 SSettings *pSettings = settingsGetPointer(); | |
| 98 | |
| 99 | |
| 100 while (done == 0) | |
| 101 { | |
| 102 nextFct++; | |
| 103 if(nextFct < HUD_FCT_END) | |
| 104 { | |
| 105 if(hudLEDNeedPerFct[nextFct] <= hudLEDPerFct[fctId]) | |
| 106 { | |
| 107 switch(nextFct) /* this switch handles conditional function. e.g. monitor2 should be skipped if no sensor2 is connected */ | |
| 108 { | |
| 109 case HUD_FCT_PPO2_0: | |
| 110 case HUD_FCT_PPO2_1: | |
| 111 case HUD_FCT_PPO2_2: if((pSettings->ext_sensor_map[nextFct - HUD_FCT_PPO2_0] >= SENSOR_ANALOG) && (pSettings->ext_sensor_map[nextFct - HUD_FCT_PPO2_0] < SENSOR_TYPE_O2_END)) | |
| 112 { | |
| 113 done = 1; | |
| 114 } | |
| 115 break; | |
| 116 default: done = 1; | |
| 117 break; | |
| 118 } | |
| 119 } | |
| 120 } | |
| 121 else | |
| 122 { | |
| 123 nextFct = HUD_FCT_NONE; | |
| 124 break; | |
| 125 } | |
| 126 } | |
| 127 return nextFct; | |
| 128 } | |
| 129 | |
| 130 static void hud_UpdateWarning(uint8_t fctId) | |
| 131 { | |
| 132 // SDiveState * pStateReal = stateRealGetPointerWrite(); | |
| 133 | |
| 134 if(stateUsed->warnings.numWarnings) | |
| 135 { | |
| 136 stateUsedWrite->lifeData.HUD_led_sequence[fctId * 2] = 0x32; | |
| 137 } | |
| 138 else | |
| 139 { | |
| 140 stateUsedWrite->lifeData.HUD_led_sequence[fctId * 2] = 0; | |
| 141 } | |
| 142 } | |
| 143 | |
| 144 static void hud_UpdatePPO2Monitor(uint8_t fctId, float ppO2) | |
| 145 { | |
| 146 // SDiveState * pStateReal = stateRealGetPointerWrite(); | |
| 147 | |
| 148 stateUsedWrite->lifeData.HUD_led_sequence[(fctId * 2)] = 0; | |
| 149 stateUsedWrite->lifeData.HUD_led_sequence[(fctId * 2) + 1] = 0; | |
| 150 | |
| 151 if(fabs(stateUsed->lifeData.actualGas.setPoint_cbar - ppO2) < 0.06) /* green constant */ | |
| 152 { | |
| 153 stateUsedWrite->lifeData.HUD_led_sequence[(fctId * 2) + 1] = 0x01; | |
| 154 } | |
| 155 else if((stateUsed->lifeData.actualGas.setPoint_cbar - ppO2) < 0.2) /* to low => blink green */ | |
| 156 { | |
| 157 stateUsedWrite->lifeData.HUD_led_sequence[(fctId * 2) + 1] = 0x13; | |
| 158 } | |
| 159 else if((stateUsed->lifeData.actualGas.setPoint_cbar - ppO2) < 0.2) /* to high => blink red */ | |
| 160 { | |
| 161 stateUsedWrite->lifeData.HUD_led_sequence[(fctId * 2)] = 0x13; | |
| 162 } | |
| 163 else /* out of range => red */ | |
| 164 { | |
| 165 stateUsedWrite->lifeData.HUD_led_sequence[(fctId * 2)] = 0x01; | |
| 166 } | |
| 167 } | |
| 168 | |
| 169 static void hud_UpdateAscentSpeed(uint8_t fctId) | |
| 170 { | |
| 171 // SDiveState * pStateReal = stateRealGetPointerWrite(); | |
| 172 uint8_t indicatorColor = 0; | |
| 173 | |
| 174 indicatorColor = drawingColor_from_ascentspeed(stateUsed->lifeData.ascent_rate_meter_per_min); | |
| 175 stateUsedWrite->lifeData.HUD_led_sequence[(fctId * 2)] = 0; | |
| 176 stateUsedWrite->lifeData.HUD_led_sequence[(fctId * 2) + 1] = 0; | |
| 177 | |
| 178 switch(indicatorColor) /* map color to LED operation */ | |
| 179 { | |
| 180 case CLUT_NiceGreen: stateUsedWrite->lifeData.HUD_led_sequence[(fctId * 2) + 1] = 0x01; /* green constant */ | |
| 181 break; | |
| 182 case CLUT_WarningYellow: stateUsedWrite->lifeData.HUD_led_sequence[(fctId * 2)] = 0x55; /* fast blink red */ | |
| 183 break; | |
| 184 case CLUT_WarningRed: stateUsedWrite->lifeData.HUD_led_sequence[(fctId * 2)] = 0x01; /* red constant */ | |
| 185 break; | |
| 186 } | |
| 187 } | |
| 188 | |
| 189 static void hud_UpdateDecoIndicator(uint8_t fctId) | |
| 190 { | |
| 191 uint16_t nextstopLengthSeconds = 0; | |
| 192 uint8_t nextstopDepthMeter = 0; | |
| 193 const SDecoinfo * pDecoinfo = getDecoInfo(); | |
| 194 | |
| 195 stateUsedWrite->lifeData.HUD_led_sequence[(fctId * 2)] = 0; | |
| 196 stateUsedWrite->lifeData.HUD_led_sequence[(fctId * 2) + 1] = 0; | |
| 197 | |
| 198 if(pDecoinfo->output_time_to_surface_seconds) | |
| 199 { | |
| 200 tHome_findNextStop(pDecoinfo->output_stop_length_seconds, &nextstopDepthMeter, &nextstopLengthSeconds); | |
| 201 if(nextstopDepthMeter > 0) | |
| 202 { | |
| 203 if(fabs(stateUsed->lifeData.depth_meter - nextstopDepthMeter) < 1.5) /* close to stop */ | |
| 204 { | |
| 205 if((stateUsed->lifeData.depth_meter + 0.1) >= nextstopDepthMeter) | |
| 206 { | |
| 207 stateUsedWrite->lifeData.HUD_led_sequence[(fctId * 2) + 1] = 0x01; /* close below deco stop => green constant */ | |
| 208 } | |
| 209 else | |
| 210 { | |
| 211 stateUsedWrite->lifeData.HUD_led_sequence[(fctId * 2)] = 0x55; /* close above deco stop => red fast blink */ | |
| 212 } | |
| 213 } | |
| 214 else if(((stateUsed->lifeData.depth_meter +0.1) > nextstopDepthMeter)) | |
| 215 { | |
| 216 stateUsedWrite->lifeData.HUD_led_sequence[(fctId * 2) + 1] = 0x13; /* Ascent to deco stop => green slow blink */ | |
| 217 } | |
| 218 else | |
| 219 { | |
| 220 stateUsedWrite->lifeData.HUD_led_sequence[(fctId * 2)] = 0x01; /* Missed deco stop => red constant */ | |
| 221 } | |
| 222 } | |
| 223 } | |
| 224 } | |
| 225 | |
| 226 void hud_UpdateStatus() | |
| 227 { | |
| 228 static uint32_t updateTick = 0; | |
| 229 | |
| 230 SDiveState * pStateReal = stateRealGetPointerWrite(); | |
| 231 SSettings *pSettings = settingsGetPointer(); | |
| 232 | |
| 233 uint8_t index = 0; | |
| 234 | |
| 235 if(hudVersion == 0) | |
| 236 { | |
| 237 if((pStateReal->lifeData.extIf_sensor_data[hudAddress][HUD_INFO_VERSION_OFFSET] > 0) && (pStateReal->lifeData.extIf_sensor_data[hudAddress][HUD_INFO_VERSION_OFFSET] <= 1)) | |
| 238 { | |
| 239 hudVersion = pStateReal->lifeData.extIf_sensor_data[hudAddress][HUD_INFO_VERSION_OFFSET]; | |
| 240 switch(hudVersion) | |
| 241 { | |
| 242 case 1: | |
| 243 default: hudLEDPerFct[0] = 2; | |
| 244 hudLEDPerFct[1] = 2; | |
| 245 hudLEDPerFct[2] = 2; | |
| 246 hudLEDPerFct[3] = 1; | |
| 247 break; | |
| 248 } | |
| 249 } | |
| 250 } | |
| 251 else | |
| 252 { | |
| 253 if(time_elapsed_ms(updateTick, HAL_GetTick()) > 2000) | |
| 254 { | |
| 255 if(stateUsed->mode == MODE_DIVE) | |
| 256 { | |
| 257 for( index = 0; index < NUM_OF_HUD_FCT; index++) | |
| 258 { | |
| 259 switch(pSettings->hudFunction[index]) | |
| 260 { | |
| 261 case HUD_FCT_WARNING: hud_UpdateWarning(index); | |
| 262 break; | |
| 263 case HUD_FCT_PPO2SUM: hud_UpdatePPO2Monitor(index, stateUsed->lifeData.ppO2); | |
| 264 break; | |
| 265 case HUD_FCT_PPO2_0: | |
| 266 case HUD_FCT_PPO2_1: | |
| 267 case HUD_FCT_PPO2_2: hud_UpdatePPO2Monitor(index, stateUsed->lifeData.ppO2Sensor_bar[pSettings->hudFunction[index] - HUD_FCT_PPO2_0]); | |
| 268 break; | |
| 269 case HUD_FCT_ASCENT_SPEED: hud_UpdateAscentSpeed(index); | |
| 270 break; | |
| 271 case HUD_FCT_DECO: hud_UpdateDecoIndicator(index); | |
| 272 break; | |
| 273 case HUD_FCT_NONE: | |
| 274 default: | |
| 275 break; | |
| 276 } | |
| 277 } | |
| 278 } | |
| 279 else | |
| 280 { | |
| 281 #ifndef ENABLE_HUD_TESTING | |
| 282 memset(pStateReal->lifeData.HUD_led_sequence,0,EXT_INTERFACE_HUD_LED_MAX); | |
| 283 pStateReal->lifeData.HUD_led_sequence[6] = 0x01; /* switch only blue LED on */ | |
| 284 #endif | |
| 285 } | |
| 286 DataEX_setExtInterface_Cmd(EXT_INTERFACE_HUD_UPDATE, hudAddress); | |
| 287 updateTick = HAL_GetTick(); | |
| 288 } | |
| 289 } | |
| 290 } |
