imrishabh18/rp2040-motor-controller

The circuit implements a powered USB interface with a W25Q16 flash memory chip, a RP2040 microcontroller, and AO3400A MOSFETs for switch control, enabling USB communication, memory storage, and device power management.

Version
1.0.19
License
unset
Stars
0

README.md

# RP2040 NEMA 17 motor cap with temperature and position feedback

A 42.30 × 42.32 mm, four-layer, 1.6 mm PCB based on the [PD-Stepper V1.1 mechanical outline](https://github.com/joshr120/PD-Stepper). Four 3.3 mm mounting holes sit on a 31 × 31 mm square. The four mounting rings, MCU, driver and temperature circuit share one GND net.

The smaller board replaces the original screw terminal with a top-entry **JST PH 4-pin, 2 mm-pitch connector**, B4B-PH-K-S(LF)(SN). Pin order is A+, A−, B+, B−; use a matching PHR-4 motor cable. USB and component placement have been rearranged. Top and bottom have ground pours; the two inner layers carry routed connections.

Use [the 1:1 mounting template](mechanical/mounting-template.svg) to check your motor. The reference targets NEMA 17 and uses replacement motor tie screws. Only the AS5600 encoder is mounted on the motor-facing underside, matching PD-Stepper’s arrangement. All other components—including the buzzer, its support circuit and the low-profile motor capacitor—face outward on top. PD-Stepper supplies a 2.5 mm heat-spreader model; our required insulated spacer thickness depends on the actual motor, magnet gap and connector lead clearance. The encoder requires a diametrically magnetized magnet centred on the rear shaft at the board origin. Magnet spacing must be adjusted for a valid sensor field and mechanical clearance. Confirm the actual rear screw pattern, shaft/bearing clearance and screw length before fabrication. The reference's cover and heatspreader do not fit this component layout directly.

This extends [imrishabh18/rp2040-motor-controller](https://tscircuit.com/imrishabh18/rp2040-motor-controller) with a TMP102 sensor beside the DRV8847 and a hardware temperature interlock. The sensor measures nearby PCB temperature. See [verification.md](verification.md) for the current automated results. The circuit has not been tested on physical hardware, and the reference firmware has not been flashed.

## Behavior

| Condition | Reference firmware response |
|---|---|
| Boot, reset or watchdog reset | Motor disabled; user must send `arm` |
| Valid temperature below 60°C and valid encoder magnet, followed by `arm` | Enable permitted, subject to USB-PD power and driver fault checks |
| Temperature at least 65°C | Buzzer chirps for 200 ms each second; USB warning and slow LED blink |
| Temperature at least 75°C, or active temperature ALERT | Motor disabled and fault latched; continuous buzzer tone and fast LED blink |
| Cooling below 60°C | Motor remains stopped until another explicit `arm` |
| Missing temperature sensor, I2C failure, changed configuration or invalid reading | Motor disabled and fault latched; continuous alarm if the loop is running |
| Missing encoder/magnet, magnet too weak/strong, or encoder bus failure | Motor disabled and fault latched; angle marked unavailable |
| Firmware stops servicing the loop | 1-second watchdog resets the RP2040; startup remains disabled |

**These are provisional board-temperature limits.** Validate and adjust them with the actual motor, current, enclosure and cooling. The nearby sensor has thermal lag; its reading can be lower than the driver junction temperature. The DRV8847's built-in protection remains the last line of defense for rapid die heating. Temperature protection cannot prevent every electrical failure.

## Position feedback, alarm and capacitor

The AS5600-ASOM (JLCPCB C79815) sits at (0, 0) on the bottom face. GPIO0/1 form a separate I2C0 bus at address 0x36, with external 4.7 kΩ pull-ups. VDD5V and VDD3V3 are tied to the unfiltered 3.3 V rail for 3.3 V operation. DIR is grounded. The firmware reports a 12-bit single-turn angle and accepts `zero` while stopped; zero is held in RAM, with no permanent sensor programming. This provides measured shaft position; application-specific closed-loop motion and missed-step comparison are still the motor firmware’s responsibility.

BZ1 is an HYG-8503A SMD magnetic buzzer (C7544813), driven at 2731 Hz by GPIO16 through an AO3400A MOSFET (C20917). A 100 kΩ gate pull-down keeps it off at reset; a Schottky flyback diode and local 10 µF bypass protect the switching circuit. The audible warning is controlled by firmware; the existing hardware temperature shutdown remains independent. The 3.3 V regulator must supply the added buzzer load (allow approximately 120 mA while sounding) plus the encoder and existing logic.

The motor bulk capacitor is now a **220 µF, 16 V TCJD227M016R0050 polymer tantalum capacitor** (C282231). Its D case is 7.3 × 4.3 mm and 2.9 mm nominal / 3.1 mm maximum high. It retains the bulk capacitance on the protected 9 V motor rail and sits on the top face at (−18, −1.5) mm. It has an imported JLCPCB footprint and CAD model, as do the encoder and buzzer. The top-entry JST PH motor header remains C131334, labelled “JST PH”; use PHR-4 with A+, A−, B+, B− wire order.

## Temperature interlock

`GPIO22 → existing USB-PD enable transistor → AND gate A`

`TMP102 ALERT → AND gate B and GPIO24`

`AND gate Y → DRV8847 nSLEEP`

The 3.3 V gate drives nSLEEP; the sensor's open-drain output does not fight an MCU output. Pull-down resistors keep the MCU enable and gate input inactive during reset or loss of PD approval. The original nSLEEP pull-down stays at the driver. SDA/SCL and ALERT have external pull-ups. Each new IC has local supply bypassing.

| Component | Role | Part / value | JLCPCB |
|---|---|---|---|
| U_TEMP | Board temperature and autonomous threshold output | TI TMP102AIDRLR, SOT563 | C99269 |
| U_THERM_GATE | Temperature interlock | TI SN74LVC1G08DBVR, SOT-23-5 | C7666 |
| C_TEMP, C_THERM_GATE | 3.3 V bypass | 100 nF, 0402 | C1525 |
| R_TEMP_SDA, R_TEMP_SCL | I2C pull-ups | 4.7 kΩ, 0402 | C25900 |
| R_TEMP_ALERT | ALERT pull-up | 10 kΩ, 0402 | C25744 |
| R_ENABLE_PD, R_MCU_ENABLE_PD | Default disabled state | 100 kΩ, 0402 | C25741 |

The sensor is at (8.000, −10.750) mm, 4.125 mm from the driver center on the same layer. U_TEMP's grounded pins face the driver, with a direct 0.3 mm ground trace to the driver's exposed ground pad. Keep the intervening ground copper thermally coupled when editing the layout. Verify this coupling on the fabricated board.

| Signal | RP2040 GPIO | Physical RP2040 pin |
|---|---:|---:|
| Encoder I2C0 SDA | 0 | 2 |
| Encoder I2C0 SCL | 1 | 3 |
| Buzzer PWM | 16 | 27 |
| Temperature I2C1 SDA | 26 | 38 |
| I2C1 SCL | 27 | 39 |
| Active-low temperature alert | 24 | 36 |
| Existing motor enable | 22 | 34 |
| Existing driver fault | 23 | 35 |
| Existing status LED | 25 | 37 |

## Firmware integration

No existing motor-control firmware was included in the source project. `firmware/` supplies a tested MicroPython protection module and a USB monitoring example; it has **not been flashed or tested on physical hardware**. The example deliberately leaves motor motion generation to the application's existing motor code.

Copy `tmp102.py`, `thermal_policy.py`, `thermal_guard.py`, `as5600.py`, `audible_alarm.py` and `main.py` to a compatible RP2040 MicroPython filesystem. The example accepts `status`, `arm`, `stop` and `zero` over USB serial, and prints temperature and shaft angle once per second. Encoder health is sampled every 10 ms, with invalid magnet status preventing arming. `arm` enables the driver with its coil inputs initially low; it does not start stepping by itself.

For an existing application, initialize `ThermalGuard` before configuring motor outputs, call `poll()` at least every 100 ms, and allow motion only while `guard.policy.enabled` is true. Stop PWM/PIO motion on faults and route all enable requests through `guard.arm()`. Do not write GPIO22 elsewhere. Keep the watchdog feed in the serviced application loop, after protection processing. Reset and errors must never restore a saved motion command automatically.

The sensor operates continuously at 8 Hz with an active-low comparator, a single-sample fault threshold, THIGH = 75°C and TLOW = 60°C. Firmware verifies these registers before accepting a reading, including when arming. A falling ALERT interrupt captures a stop even if the pin later recovers. The hardware gate responds to ALERT independently of I2C polling. Firmware provides the persistent stop until user rearm; the gate itself is not a hardware latch.

Before firmware configuration, the TMP102 defaults to an 80°C high limit and a 75°C low limit at 4 Hz. Running old firmware therefore does **not** provide the documented warning, shutdown or restart policy. A missing/unpowered sensor cannot assert ALERT: detection of that fault relies on the new firmware and watchdog.

## Build and verify

```sh
bun install --frozen-lockfile
bun run typecheck
bun run build
bun run validate:build
bun run test:built
bun run check:built
python3 -m unittest discover -s firmware -p 'test_*.py' -v
bun run render:schematic
bun scripts/render-pcb.ts
```

The completed layout is stored in `routing/route-plan.json` and loaded through tscircuit's documented local `algorithmFn` interface. The routes were produced with Pipeline 7 and then corrected for clearance and trace width. A fresh build reconstructs the copper, vias and pours and runs the normal checks. The saved plan rejects changes to its input geometry, connections or routing rules; it must be regenerated and validated after an electrical or placement change. No checker is disabled or error record removed by the build.

The toolchain versions, common-module patch and lockfile are pinned. The entire four-layer board routes as one group. The common-module patch contains RP2040 footprint, placement and schematic fixes; preserve it alongside `bun.lock` and `routing/route-plan.json`. The BOOT/RUN schematic symbols are separated to avoid the schematic router's oversized ground-wire detour.

Warnings remain for catalog lookup, component metadata and trace-length guidance. Motor-trace width and clearance regressions are checked separately; the current measurements are in `verification.md`. No DRC check is disabled. `check:built` checks both the CLI's printed error count and Gerber-derived copper shorts across all four layers.

The electrical regression follows source traces and physical package pins. It verifies the sensor bus, grounded address pin, supply/return paths and distinct interlock nets; deliberate disconnection and bypass mutations must fail. The existing ground, output separation and routing checks also remain active. Firmware tests exercise signed temperature decoding, sensor resets and errors, exact thresholds, latched stops and the MicroPython adapter with mocked GPIO/I2C. These are software checks, not thermal or hardware measurements.

## Hardware acceptance before use

1. With the motor disconnected, confirm 3.3 V at both new ICs and detect I2C address 0x48. Confirm nSLEEP is low on boot and remains low until a cool, valid `arm` request.
2. Heat the sensor/driver area while recording an external temperature reference and USB readings. Verify the warning, nSLEEP cutoff and the requirement to rearm after cooling. Test by forcing ALERT low as well.
3. Check that absent PD power, a disconnected sensor, a bus fault, changed thresholds and a stalled firmware loop all prevent or stop operation. Measure watchdog response and nSLEEP on a scope.
4. Under worst-case motor load and stall, measure driver case/board temperatures, winding current and cooldown. Set limits with allowance for sensor error, thermal gradients and thermal lag. Confirm stopping the motor is appropriate for the application; sleep removes holding torque.

5. Align the shaft magnet over the encoder, verify valid magnet status through a full revolution, then compare measured angles with known positions. Verify the result with motor coils energized and with the buzzer sounding. Check the encoder and through-hole connector leads clear the motor and that the outward-facing buzzer vent remains open.
6. Verify the 65°C chirp, 75°C continuous tone and supply voltage while sounding. Check bulk-capacitor polarity and motor-rail transients under load.

## Sources

Pinouts, electrical behavior and component limits were checked against the [TI TMP102 datasheet](https://www.ti.com/lit/ds/symlink/tmp102.pdf), [TI SN74LVC1G08 datasheet](https://www.ti.com/lit/ds/symlink/sn74lvc1g08.pdf), and [TI DRV8847 datasheet](https://www.ti.com/lit/ds/symlink/drv8847.pdf). GPIO function assignments follow the [RP2040 datasheet](https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf). Firmware APIs follow the [MicroPython RP2 reference](https://docs.micropython.org/en/latest/rp2/quickref.html) and [watchdog documentation](https://docs.micropython.org/en/latest/library/machine.WDT.html). Encoder wiring follows the [AS5600 datasheet](https://datasheet.lcsc.com/datasheet/pdf/fab02cf30a2c48b5aeeebf03db9fe675.pdf?productCode=C79815); the buzzer uses the [HYDZ external-drive specification](https://www.whayelec.com/buzzer/SMD-BUZZER/6.html). Capacitor dimensions follow the [KYOCERA AVX TCJ datasheet](https://datasheets.kyocera-avx.com/TCJ.pdf). Imported footprints were checked for package pin-number consistency; supplier inventory is not reserved.

The customized RP2040 module is bundled in `vendor/rp2040.js` (MIT license alongside it), so registry builds preserve the local footprint and layout fixes without requiring dependency patch installation.

### Encoder DNP and assembly export

The encoder U_ENCODER is `doNotPlace`: its footprint remains, but it is not fitted. Run `bun run build` followed by `bun run export:assembly` to generate the BOM, pick-and-place files and Gerber archive with DNP parts excluded. Use `dist/index/assembly-bom.csv` and `assembly-pick-and-place.csv`; the plain pinned CLI export includes DNP parts.