diff --git a/docs/elements/assembly-device.mdx b/docs/elements/assembly-device.mdx index abaedc69..caaf2575 100644 --- a/docs/elements/assembly-device.mdx +++ b/docs/elements/assembly-device.mdx @@ -35,7 +35,7 @@ export default () => ( | `name` | Optional device name. | | `model` | Modelprinter string or HTTP(S) model URL. | | `modelUrl` | Imported CAD model URL. Use either `model` or `modelUrl`. | -| `children` | Boards, subassemblies, and screens. | +| `children` | Boards, motors, subassemblies, and screens. | ## Modelprinter strings @@ -59,4 +59,4 @@ export default () => ( Assembly models add 3D geometry without creating PCB footprints or electrical connections. For imported models, see [`cadmodel`](./cadmodel.mdx) for supported formats. -Use [`assembly.subassembly`](./assembly-subassembly.mdx) to group mechanical parts, or [`assembly.screen`](./assembly-screen.mdx) to attach a display to a connector. See [Mounting 3D models](../guides/tscircuit-essentials/mounting-3d-models.mdx) for screws aligned with board holes. +Use [`assembly.motor`](./assembly-motor.mdx) to add a motor and mount a controller to its backface, [`assembly.subassembly`](./assembly-subassembly.mdx) to group mechanical parts, or [`assembly.screen`](./assembly-screen.mdx) to attach a display to a connector. See [Mounting 3D models](../guides/tscircuit-essentials/mounting-3d-models.mdx) for screws aligned with board holes. diff --git a/docs/elements/assembly-motor.mdx b/docs/elements/assembly-motor.mdx new file mode 100644 index 00000000..c014e50a --- /dev/null +++ b/docs/elements/assembly-motor.mdx @@ -0,0 +1,121 @@ +--- +title: +description: Represent a motor in an assembly and mount a PCB to its backface. +--- + +import CircuitPreview from "@site/src/components/CircuitPreview" +import preview1 from "@site/src/data/assembly-previews/assembly-motor-1.json" + +`assembly.motor` represents a motor in an [`assembly.device`](./assembly-device.mdx). `standard` selects a built-in NEMA model; `model` supplies your own model specification. The examples below use a NEMA17 motor. The motor has no PCB footprint or schematic symbol. + +![TSX examples on the left and 3D renders on the right comparing a NEMA17 above and below a PCB, with 6 mm clearance from the motor backface to the nearest PCB surface.](/img/elements/assembly-motor-board-mounting-annotated.snap.png) + +## Mount a controller to the motor + +Give the motor a `name`, then set the board's `mountedTo` to that name followed by `.backface`. A controller component must forward `mountedTo` and `mountGap` to its underlying [`board`](./board.mdx), as `MotorController` does here: + + ( + + + + + + {/* Add your controller components and traces here. */} + +) + +export default () => ( + + + + +)`} +/> + +`backface` is the face opposite the shaft. `mountGap` is the clearance between that face and the nearest PCB surface, rather than the PCB center plane. The example places a 1.6 mm board 6 mm from the motor's rear face, with the shaft pointing below the board. + +Mounting keeps the board's PCB layout in the XY plane at Z = 0. The motor follows the board's center and rotation in that plane. Add mounting holes and standoffs yourself; mounting props do not create them. The example's four holes use the default NEMA17 rear-hole spacing of 31 mm. + +## Motor properties + +| Property | Type | Description | +|----------|------|-------------| +| `name` | `string` | Required motor name, used by board mount references. | +| `displayName` | `string` | Optional display label. | +| `standard` | `"nema8" \| "nema17" \| "nema23"` | Optional shortcut to a built-in motor model. Use either `standard` or `model`. | +| `model` | `string` | Custom modelprinter specification. The current motor renderer accepts NEMA specifications. | +| `shaftFacingDirection` | `"x+" \| "x-" \| "y+" \| "y-" \| "z+" \| "z-"` | Optional shaft direction; defaults to `"z+"`. | + +Supply exactly one of `standard` or `model`. A built-in standard selects representative dimensions; choose a custom `model` when your motor has a different body or shaft length. + +:::note Current custom CAD support + +The current `assembly.motor` implementation accepts NEMA modelprinter specifications and does not yet accept imported model URLs or `cadModel`. For an arbitrary motor CAD file, use [`assembly.subassembly`](./assembly-subassembly.mdx) with `model`, `modelUrl`, or `cadModel`. Board-to-motor backface mounting requires `assembly.motor`. + +::: + +## Customize a NEMA model + +Use `model` to specify dimensions without generating or supplying a model URL: + +```tsx +import { assembly } from "@tscircuit/core" + +const model = + "nema17_bodylength38mm_shaftlength24mm" + + "_flatdepth0.5mm_flatlength15mm" + +export default () => ( + + + +) +``` + +This model has a 38 mm body, a 24 mm shaft, and a D-flat 0.5 mm deep over the last 15 mm of the shaft. The `nema8`, `nema17`, and `nema23` standards can all be customized with modelprinter parameters. + +## Shaft directions and board mounting + +Directions use circuit coordinates: +X right, +Y top, and +Z above. + +| Direction | Shaft points | +|-----------|--------------| +| `x+`, `x-` | Right, left | +| `y+`, `y-` | Toward +Y, toward −Y | +| `z+`, `z-` | Above, below | + +All six directions render motor models. Board backface mounting currently supports `z+` and `z-`: the motor is above the PCB for `z+`, and below it for `z-`. Mounting a board to a sideways motor reports an error until rotated PCB boards are supported. + +| Board property | Type | Description | +|----------------|------|-------------| +| `mountedTo` | `string` | Motor face reference, such as `"NEMA17.backface"`. | +| `mountGap` | `string \| number` | Nonnegative distance in millimetres, such as `"6mm"` or `6`. Defaults to zero when mounted; only meaningful with `mountedTo`. | + +Mount references resolve by exact motor name within the nearest `assembly.device`, regardless of declaration order. Names in sibling devices remain independent. Missing or duplicate targets, unsupported faces, and multiple boards trying to position one motor report errors. + +An unmounted motor's origin is at the world origin on its shaft-side mounting face. Its backface lies one body length behind that face, so changing `bodylength` also changes the placement needed for a mounted board. diff --git a/docs/elements/board.mdx b/docs/elements/board.mdx index 415ffb47..716e5fce 100644 --- a/docs/elements/board.mdx +++ b/docs/elements/board.mdx @@ -48,6 +48,8 @@ import CircuitPreview from '@site/src/components/CircuitPreview' | `outlineOffsetX`, `outlineOffsetY` | `string \| number` | Offset a custom outline relative to the bounding box origin. | | `material` | `'fr4' \| 'fr1' \| 'flex'` | PCB substrate material. Defaults to `'fr4'`. | | `thickness` | `string \| number` | Board thickness in millimeters. | +| `mountedTo` | `string` | Mount to a named motor face, such as `"NEMA17.backface"`. See [`assembly.motor`](./assembly-motor.mdx). | +| `mountGap` | `string \| number` | Clearance from the motor backface to the nearest PCB surface, in millimeters. Defaults to zero when `mountedTo` is set. | | `title` | `string` | Title for the board, displayed in documentation and exports. | | `solderMaskColor` | `string` | Color applied to both top and bottom solder masks. | | `topSolderMaskColor` | `string` | Color of the top solder mask. | diff --git a/src/data/assembly-previews/assembly-motor-1.json b/src/data/assembly-previews/assembly-motor-1.json new file mode 100644 index 00000000..1df70148 --- /dev/null +++ b/src/data/assembly-previews/assembly-motor-1.json @@ -0,0 +1,136 @@ +[ + { + "type": "source_project_metadata", + "source_project_metadata_id": "source_project_metadata_0", + "software_used_string": "@tscircuit/core@0.0.2058" + }, + { + "type": "source_group", + "source_group_id": "source_group_0", + "is_subcircuit": true, + "was_automatically_named": true, + "subcircuit_id": "subcircuit_source_group_0" + }, + { + "type": "source_component", + "source_component_id": "source_component_0", + "ftype": "simple_chip", + "name": "NEMA17" + }, + { + "type": "source_board", + "source_board_id": "source_board_0", + "source_group_id": "source_group_0" + }, + { + "type": "schematic_group", + "schematic_group_id": "schematic_group_0", + "is_subcircuit": true, + "subcircuit_id": "subcircuit_source_group_0", + "name": "unnamed_board1", + "center": { + "x": 0, + "y": 0 + }, + "width": 0, + "height": 0, + "schematic_component_ids": [], + "source_group_id": "source_group_0", + "show_as_schematic_box": false + }, + { + "type": "pcb_board", + "pcb_board_id": "pcb_board_0", + "source_board_id": "source_board_0", + "subcircuit_id": "subcircuit_source_group_0", + "center": { + "x": 0, + "y": 0 + }, + "thickness": 1.6, + "num_layers": 2, + "width": 42, + "height": 42, + "material": "fr4", + "min_trace_width": 0.1, + "min_via_hole_diameter": 0.2, + "min_via_pad_diameter": 0.3, + "min_via_hole_edge_to_via_hole_edge_clearance": 0.1, + "min_trace_to_hole_edge_clearance": 0.2, + "min_trace_to_pad_edge_clearance": 0.1, + "min_pad_edge_to_pad_edge_clearance": 0.1, + "min_plated_hole_drill_edge_to_drill_edge_clearance": 0.15, + "min_board_edge_clearance": 0.2 + }, + { + "type": "pcb_hole", + "pcb_hole_id": "pcb_hole_0", + "pcb_component_id": null, + "hole_shape": "circle", + "hole_diameter": 3.2, + "x": -15.5, + "y": -15.5, + "is_covered_with_solder_mask": false, + "subcircuit_id": "subcircuit_source_group_0" + }, + { + "type": "pcb_hole", + "pcb_hole_id": "pcb_hole_1", + "pcb_component_id": null, + "hole_shape": "circle", + "hole_diameter": 3.2, + "x": -15.5, + "y": 15.5, + "is_covered_with_solder_mask": false, + "subcircuit_id": "subcircuit_source_group_0" + }, + { + "type": "pcb_hole", + "pcb_hole_id": "pcb_hole_2", + "pcb_component_id": null, + "hole_shape": "circle", + "hole_diameter": 3.2, + "x": 15.5, + "y": -15.5, + "is_covered_with_solder_mask": false, + "subcircuit_id": "subcircuit_source_group_0" + }, + { + "type": "pcb_hole", + "pcb_hole_id": "pcb_hole_3", + "pcb_component_id": null, + "hole_shape": "circle", + "hole_diameter": 3.2, + "x": 15.5, + "y": 15.5, + "is_covered_with_solder_mask": false, + "subcircuit_id": "subcircuit_source_group_0" + }, + { + "type": "cad_component", + "cad_component_id": "cad_component_0", + "model_glb_url": "https://modelcdn.tscircuit.com/jscad_models/nema17.glb", + "position": { + "x": 0, + "y": 0, + "z": -44.8 + }, + "rotation": { + "x": 0, + "y": 180, + "z": 0 + }, + "layer": "bottom", + "source_component_id": "source_component_0", + "subcircuit_id": "subcircuit_source_group_0", + "model_origin_position": { + "x": 0, + "y": 0, + "z": 0 + }, + "model_unit_to_mm_scale_factor": 1, + "model_object_fit": "contain_within_bounds", + "anchor_alignment": "center", + "show_as_translucent_model": false + } +] diff --git a/static/img/elements/assembly-motor-board-mounting-annotated.snap.png b/static/img/elements/assembly-motor-board-mounting-annotated.snap.png new file mode 100644 index 00000000..9a97ea34 Binary files /dev/null and b/static/img/elements/assembly-motor-board-mounting-annotated.snap.png differ