@@ -50,6 +50,9 @@ class StrobeCalibrationManager:
5050 # Safe fallback DAC value if calibration fails
5151 SAFE_DAC_VALUE = 0x96
5252
53+ # If ADC CH0 reads above this with strobe off, something is wrong (blown MOSFET, shorted gate driver)
54+ PREFLIGHT_CURRENT_THRESHOLD = 6
55+
5356 # LDO voltage bounds
5457 LDO_MIN_V = 4.5
5558 LDO_MAX_V = 11.0
@@ -205,67 +208,69 @@ def _calibrate(self, target_current: float):
205208 Returns:
206209 (success, final_dac, led_current)
207210 """
211+ # Pre-flight: check for current with strobe off — indicates blown MOSFET or gate driver
212+ idle_adc = self ._read_adc (self .ADC_CH0_CMD )
213+ if idle_adc > self .PREFLIGHT_CURRENT_THRESHOLD :
214+ self .status ["message" ] = f"Current detected with strobe off (ADC CH0={ idle_adc } ). Likely blown MOSFET or gate driver — check V3 Connector Board."
215+ return False , - 1 , - 1
216+
208217 # Phase 1: find safe starting DAC
209218 dac_start , ldo = self ._find_dac_start ()
210219
211220 if dac_start < 0 :
212- logger . debug ( f"DAC 0 already below LDO minimum ({ ldo :.2f} V)" )
221+ self . status [ "message" ] = f"DAC value of 0 is below minimum LDO voltage ({ self . LDO_MIN_V :.2f} V): { ldo :.2f } V. This indicates a problem with the controller board."
213222 return False , - 1 , - 1
214223
215224 logger .debug (f"Calibrating: target={ target_current } A, dac_start={ dac_start :#04x} " )
216225
217226 # Phase 2: sweep from dac_start downward, looking for target crossing
218- current_dac = dac_start
219227 final_dac = self .DAC_MIN
220- total_steps = dac_start - self . DAC_MIN + 1
228+ crossed = False
221229
222- while current_dac >= self .DAC_MIN :
230+ for dac in range ( dac_start , self .DAC_MIN - 1 , - 1 ) :
223231 if self ._cancel_requested :
224232 logger .info ("Calibration cancelled by user" )
225233 return False , - 1 , - 1
226234
227- self ._set_dac (current_dac )
235+ self ._set_dac (dac )
228236 time .sleep (0.1 )
229237
230- # Update progress for UI polling
231- steps_done = dac_start - current_dac
238+ steps_done = dac_start - dac
239+ total_steps = dac_start - self . DAC_MIN + 1
232240 if total_steps > 0 :
233241 self .status ["progress" ] = int (20 + (steps_done / total_steps ) * 60 )
234242
235243 ldo = self .get_ldo_voltage ()
236244
237245 if ldo < self .LDO_MIN_V :
238- logger .debug (f"LDO { ldo :.2f} V below min at DAC={ current_dac :#04x} , skipping" )
239- final_dac = current_dac
240- current_dac -= 1
246+ logger .debug (f"LDO { ldo :.2f} V below min at DAC={ dac :#04x} , skipping" )
247+ final_dac = dac
241248 continue
242249
243250 if ldo > self .LDO_MAX_V :
244- logger . debug ( f"LDO { ldo :.2f} V above max — something is wrong" )
251+ self . status [ "message" ] = f"LDO voltage ( { ldo :.2f} V) above maximum ( { self . LDO_MAX_V } V). Stopping calibration, as something is wrong."
245252 return False , - 1 , - 1
246253
247254 led_current = self .get_led_current ()
248- logger .debug (f"DAC={ current_dac :#04x} , current={ led_current :.2f} A" )
255+ logger .debug (f"DAC={ dac :#04x} , current={ led_current :.2f} A" )
249256
250- # Hard safety cap (bug fix over original script)
251257 if led_current > self .HARD_CAP_CURRENT :
252- logger . error ( f"LED current { led_current :.2f} A exceeds hard cap { self .HARD_CAP_CURRENT } A" )
258+ self . status [ "message" ] = f"LED current ( { led_current :.2f} A) exceeds hard cap ( { self .HARD_CAP_CURRENT } A). This strongly indicates the LED is shorted."
253259 return False , - 1 , - 1
254260
255261 if led_current > target_current :
256- logger .debug (f"Crossed target at DAC={ current_dac :#04x} ({ led_current :.2f} A)" )
257- final_dac = current_dac + 1
262+ logger .debug (f"Crossed target at DAC={ dac :#04x} ({ led_current :.2f} A)" )
263+ final_dac = dac + 1
264+ crossed = True
258265 break
259266
260- final_dac = current_dac
261- current_dac -= 1
267+ final_dac = dac
262268
263- # Edge cases — sweep ran off either end
264- if current_dac <= self .DAC_MIN :
265- logger .debug (f"Reached DAC_MIN without crossing target" )
269+ if not crossed :
270+ self .status ["message" ] = f"Reached MIN_DAC without reaching target ({ target_current } A). This generally indicates a problem."
266271 return False , - 1 , - 1
267- if current_dac >= self .DAC_MAX :
268- logger . debug ( f"DAC_MAX still above target — hardware problem" )
272+ if final_dac >= self .DAC_MAX :
273+ self . status [ "message" ] = "MAX_DAC resulted in current above target. This generally indicates a problem."
269274 return False , - 1 , - 1
270275
271276 # Phase 3: average readings at the final setting to refine
@@ -370,8 +375,9 @@ def _run_calibration_sync(self, target: float) -> Dict[str, Any]:
370375 return self .status
371376 else :
372377 self ._set_dac (self .SAFE_DAC_VALUE )
378+ reason = self .status .get ("message" , "Calibration failed" )
373379 self .status = {"state" : "failed" , "progress" : 0 ,
374- "message" : "Calibration failed — DAC set to safe fallback" }
380+ "message" : f" { reason } DAC set to safe fallback. " }
375381 return self .status
376382
377383 except Exception as e :
0 commit comments