MustafaMulla29/rp2040-bldc-motor-controller-new

73×72 mm 4-layer USB‑C PD (5/9/15/20 V) RP2040 controller PCB with protected power switching, MCT8329A sensorless BLDC three-phase MOSFET bridge, USB data/programming, onboard controls, and three motor outputs feeding a 2212 motor through rigid bullet adapters.

Version
0.3.100
License
unset
Stars
0

scripts/check-current-budget.py

"""Check compiled R142 and budget arithmetic. Does not certify current or heating."""
import hashlib,json,sys
from pathlib import Path
ROOT=Path(__file__).resolve().parent.parent
sys.path.insert(0,str(ROOT/'firmware'))
from power_policy import INPUT_LIMIT_MA,CONTRACT_MA

def calculate(spec,r142):
 nominal=spec['switchKcl']*spec['switchVcl']/r142
 accuracy=.14 if nominal>=1.6 else .20
 rt=spec['resistorInitialTolerance']+spec['resistorTemperatureAllowance']
 upper=nominal*(1+accuracy)/(1-rt);lower=nominal*(1-accuracy)/(1+rt)
 rows=[]
 for key,ma in spec['logicOutputAllocationMa'].items():
  voltage=int(key);input_v=spec['minimumBoardVbusMv'][key]/1000-spec['assumedLogicDiodeDropV']
  logic_a=spec['logicOutputV']*ma/1000/(spec['minimumAssumedConverterEfficiency']*input_v)+spec['directInputAllowanceMa']/1000
  rows.append({'profileMv':voltage,'logicInputAllowanceA':logic_a,'normalTotalInputA':logic_a+spec['provisionalMotorFeedCeilingMa'][key]/1000,
               'illustrativeOverloadTotalInputA':upper+logic_a if voltage>5000 else None,
               'overloadHeadroomA':spec['contractMa']/1000-upper-logic_a if voltage>5000 else None})
 fallback_logic_a=next(r['logicInputAllowanceA'] for r in rows if r['profileMv']==5000)
 return {'r142Ohms':r142,'nominalMotorFeedLimitA':nominal,'illustrativeLimitMinimumA':lower,'illustrativeLimitMaximumA':upper,'resistorTotalToleranceUsed':rt,
         'resistorNominalDissipationW':spec['switchVcl']**2/r142,'profiles':rows,'fallbackLogicAllowanceA':fallback_logic_a}

def review(c,spec):
 parts={x['name']:x for x in c if x['type']=='source_component'};r=calculate(spec,parts['R142']['resistance']);fail=[]
 if r['r142Ohms']!=spec['r142Ohms']:fail.append('Compiled R142 disagrees with budget')
 if CONTRACT_MA!=spec['contractMa']:fail.append('PD contract disagrees with budget')
 if INPUT_LIMIT_MA!={int(k):v for k,v in spec['provisionalMotorFeedCeilingMa'].items()}:fail.append('Firmware helper ceilings disagree with budget')
 if any(row['normalTotalInputA']>=spec['contractMa']/1000 or (row['overloadHeadroomA'] is not None and row['overloadHeadroomA']<=0) for row in r['profiles']):fail.append('Input allocation exceeds PD contract')
 if r['fallbackLogicAllowanceA']>=spec['fallbackMa']/1000:fail.append('Logic allocation exceeds fallback current')
 if max(INPUT_LIMIT_MA.values())/1000>=r['illustrativeLimitMinimumA']:fail.append('Normal ceiling reaches lower hardware-limit estimate')
 r['failures']=fail;r['status']=spec['status'];r['limitations']=spec['limitations'];return r

if __name__=='__main__':
 p=ROOT/'dist/index/circuit.json';raw=p.read_bytes();spec=json.loads((ROOT/'manufacturing/current-budget.json').read_text());r=review(json.loads(raw),spec)
 r['sha256']=hashlib.sha256(raw).hexdigest();r['old1500OhmComparison']=calculate(spec,1500)
 (ROOT/'dist/current-budget-review.json').write_text(json.dumps(r,indent=2)+'\n');print(json.dumps(r,indent=2));sys.exit(bool(r['failures']))