Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/elements/assembly-device.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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.
121 changes: 121 additions & 0 deletions docs/elements/assembly-motor.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
---
title: <assembly.motor />
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:

<CircuitPreview
wrapCode
circuitJson={preview1}
defaultView="3d"
hideSchematicTab
browser3dView={false}
code={`import { assembly } from "@tscircuit/core"
import type { BoardProps } from "@tscircuit/props"

const MotorController = (props: BoardProps) => (
<board
width="42mm" height="42mm"
thickness="1.6mm"
routingDisabled
{...props}
>
<hole diameter="3.2mm" pcbX={-15.5} pcbY={-15.5} />
<hole diameter="3.2mm" pcbX={-15.5} pcbY={15.5} />
<hole diameter="3.2mm" pcbX={15.5} pcbY={-15.5} />
<hole diameter="3.2mm" pcbX={15.5} pcbY={15.5} />
{/* Add your controller components and traces here. */}
</board>
)

export default () => (
<assembly.device>
<assembly.motor
name="NEMA17"
standard="nema17"
shaftFacingDirection="z-"
/>
<MotorController
mountedTo="NEMA17.backface"
mountGap="6mm"
/>
</assembly.device>
)`}
/>

`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 () => (
<assembly.device>
<assembly.motor
name="NEMA17"
model={model}
shaftFacingDirection="z-"
/>
</assembly.device>
)
```

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.
2 changes: 2 additions & 0 deletions docs/elements/board.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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. |
Expand Down
136 changes: 136 additions & 0 deletions src/data/assembly-previews/assembly-motor-1.json
Original file line number Diff line number Diff line change
@@ -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
}
]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading