main
John Lauer Fan component engineering assets and verified simulated rotor motion prototype 422c1bc 12d ago
"""Independently authored nominal mechanical model from Sunon drawing p6.
Rotor blades, struts and wire routing are illustrative; not exact vendor geometry.
Uses existing CadQuery installation, no new local CAD software installed.
"""
from pathlib import Path
import cadquery as cq,json,math
r=Path(__file__).resolve().parents[1]
a=cq.Assembly(name='MF40200V1_1000U_G99_nominal_illustration')
frame=cq.Workplane('XY').rect(40,40).extrude(20).edges('|Z').fillet(2)
frame=frame.cut(cq.Workplane('XY').circle(18.5).extrude(20))
for x in [-16,16]:
 for y in [-16,16]:frame=frame.cut(cq.Workplane('XY').center(x,y).circle(2.15).extrude(20))
a.add(frame,name='frame_nominal_40x40x20',color=cq.Color(.24,.26,.28))
a.add(cq.Workplane('XY').circle(9).extrude(12).translate((0,0,4)),name='motor_hub_illustrative',color=cq.Color(.32,.34,.36))
for i in range(7):
 blade=cq.Workplane('XY').polyline([(8,-2),(17,-4),(18,0),(10,4)]).close().extrude(1.2).translate((0,0,10)).rotate((0,0,0),(0,0,1),i*360/7)
 a.add(blade,name=f'illustrative_blade_{i+1}',color=cq.Color(.38,.40,.42))
for i in range(3):
 spoke=cq.Workplane('XY').box(18,2,2).translate((9,0,1)).rotate((0,0,0),(0,0,1),i*120)
 a.add(spoke,name=f'illustrative_support_{i+1}',color=cq.Color(.24,.26,.28))
# Deliberately no 305mm cable geometry: routing is installation-dependent.
a.export(str(r/'cad/MF40200V1-1000U-G99-ai-nominal.step'))
fp=['(footprint "Sunon_MF4020_Mechanical" (version 20241229) (generator "adom") (layer "F.Cu")',
'(descr "Fan mounting template only; no PCB electrical terminals; dimensions from Sunon D04114960G-01 p6")',
'(attr board_only exclude_from_pos_files exclude_from_bom)',
'(fp_text reference "FAN**" (at 0 -23) (layer "F.SilkS") (effects (font (size 1 1) (thickness 0.15))))',
'(fp_text value "MF40200V1-1000U-G99" (at 0 23) (layer "F.Fab") (effects (font (size 1 1) (thickness 0.15))))',
'(fp_rect (start -20 -20) (end 20 20) (stroke (width 0.15) (type default)) (fill none) (layer "F.Fab"))',
'(fp_rect (start -20.75 -20.75) (end 20.75 20.75) (stroke (width 0.05) (type default)) (fill none) (layer "F.CrtYd"))']
for x in [-16,16]:
 for y in [-16,16]:fp.append(f'(pad "" np_thru_hole circle (at {x} {y}) (size 4.3 4.3) (drill 4.3) (layers "*.Cu" "*.Mask"))')
fp+=['(model "${KIPRJMOD}/cad/MF40200V1-1000U-G99-ai-nominal.step" (offset (xyz 0 0 0)) (scale (xyz 1 1 1)) (rotate (xyz 0 0 0)))',')']
(r/'cad/Sunon_MF4020_Mechanical.kicad_mod').write_text('\n'.join(fp)+'\n')
obj={'symbolName':'MF40200V1-1000U-G99','mpn':'MF40200V1-1000U-G99','manufacturer':'Sunon','referencePrefix':'M','description':'5V 40x40x20mm fan with open-collector tach; lead numbers are library convention','datasheetUrl':'https://www.mouser.com/datasheet/3/1161/1/MF40200V1_1000U_G99_D04114960G_01_1.pdf','wikiUrl':'https://wiki.adom.inc/adom/mf40200v1-1000u-g99','distributors':{'mouser':'369-MF40200V1100UG99'},'pins':[{'number':'1','name':'RED_5V','type':'power_in','side':'left','group':'Power'},{'number':'2','name':'BLACK_GND','type':'power_in','side':'left','group':'Power'},{'number':'3','name':'YELLOW_FG','type':'open_collector','side':'right','group':'Tach'}],'preferences':{'pinLayout':'left-right','chipOutline':False}}
(r/'cad/symbol-input.json').write_text(json.dumps(obj,indent=2))
print('Created nominal model, mechanical footprint and symbol input; rotor details illustrative')