ELECTRONICS PRODUCTION
Assignment Overview
Group Assignment
- Characterize the design rules for your in-house PCB production process
- Submit a PCB design to a board house
Individual Assignment
- Make and test an embedded microcontroller system that you designed
- Extra credit: make it with another process
The full group documentation is available here.
Group Assignment
This week's group assignment focused on characterizing the design rules for our in-house PCB milling process. By systematically testing different trace widths, clearances, and via sizes on the Roland Modela MDX-20, we established reliable minimum values that guide all future PCB designs produced at our lab. Understanding these limits before committing to a design prevents frustrating rework and wasted material.
PCB Milling Machine — Roland Modela MDX-20
The Roland Modela MDX-20 is a compact desktop CNC milling machine widely used across Fab Labs for PCB fabrication. Rather than etching copper with chemicals, it mechanically removes unwanted copper from FR1 copper-clad boards using precision carbide end mills. This dry, subtractive process produces single- or double-sided boards entirely in-house, with no chemical handling required.
Key specifications of the MDX-20 relevant to PCB work:
- Working area: 203 × 152 × 60.5 mm (X × Y × Z)
- Positional accuracy: ±0.01 mm — adequate for SMD pads down to 0402
- Spindle speed: 6,500 RPM (fixed)
- Typical end mills: 1/64" (0.4 mm) for trace isolation; 1/32" (0.8 mm) for board outline
- Control interface: VPanel (Windows) or Mods CE browser-based CAM
Design Rule Test Board
To establish reliable design rules, we milled a purpose-built test board containing traces of varying widths (ranging from 0.004" to 0.020"), different inter-trace clearances, and a series of drill holes at increasing diameters. After milling, each feature was visually inspected under a digital microscope and continuity-tested with a multimeter.
Results from the characterization test:
- Minimum reliable trace width: 0.006" (≈ 0.15 mm) with the 1/64" end mill
- Minimum clearance between traces: 0.006" (≈ 0.15 mm)
- Minimum drill/via diameter: 0.8 mm (1/32" end mill)
- Board material: FR1 copper-clad, 1.6 mm thickness, 35 µm copper layer
Submitting a Design to a Board House
As part of the group assignment, we also submitted a PCB design to a professional board house — JLCPCB — to understand the commercial manufacturing workflow and appreciate the differences in precision, layer count, and finish quality compared to in-house milling.
The workflow involved:
- Designing the PCB in KiCad with the board house's DRC rules loaded
- Exporting Gerber files (one per layer: copper, mask, silkscreen, edge cuts) and Excellon drill files
- Packaging all files into a single ZIP archive
- Uploading to JLCPCB, selecting 2-layer FR4, HASL finish, and standard stackup
- Reviewing the board house's automated DRC report, which confirmed our design was within their standard tolerances
The comparison was illuminating — professional boards offer tighter tolerances (down to 0.1 mm trace/space), plated through-holes, solder mask, and silkscreen, none of which are available with our in-house Roland milling process.
Individual Assignment — Making the PCB
For the individual assignment, I fabricated the PCB I had designed during Electronics Design week (Week 06) — a custom board built around the Seeed Studio XIAO RP2040 microcontroller. The board was milled, stuffed with SMD components, soldered, and tested entirely within the Fab Lab.
The design features the XIAO RP2040 as the central processing unit, a tactile push button for user input, an SMD LED with a current-limiting resistor as a visual indicator, and pin headers for I/O breakout. The design intentionally keeps the footprint small while exposing all key GPIO pins for prototyping.
Step 1
Exporting Files — Gerbers & PNG Conversion
Before loading anything into the CAM tool (Mods CE), the PCB layout must be exported from KiCad and converted into a format the milling machine understands. This is a two-stage process: first exporting Gerber files from KiCad, then converting the relevant copper layer to a high-resolution black-and-white PNG that Mods CE reads as its input image.
Exporting Gerber Files from KiCad
In KiCad's PCB editor, I navigated to File → Fabrication Outputs → Gerbers to open the Plot dialog. The following layers were selected for export:
- F.Cu — front copper layer (traces and pads)
- F.Mask — solder mask openings
- F.Silkscreen — component labels and reference designators
- Edge.Cuts — board outline, used for the cutout toolpath
What Are Gerber Files?
Gerber files are the universal standard for communicating PCB design data to fabrication equipment.
They describe each physical layer of a PCB as a separate file containing vector graphics instructions: where copper should exist, where solder mask openings should be, where silkscreen text should print, and where the board edge should be cut.
Each layer of your board maps to a dedicated Gerber file:
- F.Cu / B.Cu — front and back copper layers (traces, pads, fills)
- F.Mask / B.Mask — solder mask openings over exposed pads
- F.Silkscreen / B.Silkscreen — component reference designators and markings
- Edge.Cuts — the board outline used for routing or milling the perimeter
- Excellon drill file (.drl) — hole positions and diameters (technically a separate format, always included alongside Gerbers)
Together, this set of files forms a complete description of the board. Any board house or in-house milling setup can read the same Gerber package and reproduce the design faithfully, regardless of which EDA tool was used to create it.
In the context of this week's workflow, Gerber files served two distinct roles. For the board house submission (JLCPCB), the full Gerber package was zipped and uploaded directly — the board house's automated system reads each layer and produces the finished PCB. For in-house Roland MDX milling, Gerbers cannot be used directly because Mods CE requires a raster image input; the Gerber files were therefore converted to high-resolution PNGs using the Gerber2PNG tool, which renders each layer as a precise black-and-white bitmap that Mods CE can process into machine toolpaths.
Converting Gerbers to PNG with Gerber2PNG
Mods CE does not read Gerber files directly, it needs a raster image where copper regions are black and gaps are white. To produce this, our lab uses the Gerber2PNG web tool hosted at gerber2png.fablabkerala.in, developed by Fab Lab Kerala.
I uploaded all exported Gerber files to the tool, selected Generate All to produce PNG layers at 1000 DPI, and downloaded the resulting images, one for the copper traces, one for the edge cuts, and one for the drill positions.
Gerber2PNG KiCad Plugin
Fab Lab Kerala also maintains a dedicated KiCad plugin version of the Gerber2PNG tool that integrates directly into the KiCad toolbar. Rather than switching to a browser-based workflow, this plugin lets you convert Gerbers to PNG with a single click from within KiCad, streamlining the process considerably.
It can be installed through KiCad's built-in Plugin and Content Manager or manually from the GitHub repository. Once installed, a toolbar button appears in the PCB editor. Clicking it generates the trace, edge-cuts, and drill PNGs at the correct resolution for milling, without leaving the application.
The plugin is open source: Gerber2PNG
Installing the Gerber2PNG KiCad Plugin
The plugin is distributed as a ZIP archive from its GitHub repository. Installation is handled entirely through KiCad's built-in Plugin and Content Manager, requiring no manual file copying or command-line steps. The four-step process below covers downloading the archive, opening the manager, installing from file, and confirming the installation.
Step 1 — Navigate to the GitHub repositoryOpen the plugin's GitHub repository at the link provided above. On the repository's main page, click the green Code button and select Download ZIP to download the full plugin archive to your machine. Save it to a memorable location — you will need to browse to this file in the next step.
Confirm the ZIP file has been saved to your local machine. The archive contains the plugin's Python source files, metadata, and any required assets.
Step 2 — Open the Plugin and Content ManagerIn KiCad's main project window (or from within the PCB editor), navigate to Tools → Plugin and Content Manager. This opens the manager, which lists all installed plugins alongside available packages from the official KiCad repository.
For a local installation from file, the standard repository listing is not used — instead the Install from File… button at the bottom of the window is used to bypass the online catalogue entirely.
Step 3 — Install from file and select the ZIPClick Install from File… to open a file browser dialog. Navigate to the location where the plugin ZIP was saved and select it, then click Open. KiCad will extract the archive, register the plugin, and prompt you to apply pending changes. Click Apply Pending Changes to complete the installation. A new toolbar button for Gerber2PNG will appear in the PCB editor on the next launch.
Once the PCB design is complete, clicking the Gerber2PNG toolbar button launches the tool directly in the browser with the current board's Gerber files pre-loaded — no manual export or file browsing required. A single click on Generate All produces the trace, edge-cuts, and drill PNGs at the correct resolution, ready to be fed straight into Mods CE for toolpath generation.
Step 2
Generating Toolpaths with Mods CE
Mods CE (Community Edition) is a browser-based, modular CAM tool developed and maintained by the Fab Academy community. It runs entirely in the browser with no installation required and converts the PCB PNG images into machine-ready .rml toolpath files for the Roland MDX/SRM-series mills. You can access it at modsproject.org.
You can take the PNG imgaes generated form the Gerber2PNG and load it into the Mods CE and generate the toolpath for milling.
Unlike traditional desktop CAM software, Mods CE uses a visual, node-based pipeline, you connect processing modules together to chain operations from image input through to machine output. The community provides pre-built programs for common machines, so most users only need to load a program and adjust a few parameters.
Opening Mods CE and Loading the Roland MDX Program
I opened Mods CE in the browser and right-clicked the canvas to bring up the program menu, then navigated to:
- Programs → Open Server Program → Roland MDX mill → PCB PNG
This loads a pre-built node network configured specifically for the Roland MDX-20, including modules for reading a PNG, computing isolation offsets, generating the mill path, and outputting Roland RML commands.
Loading the PCB Trace Image
I clicked the select png image module and loaded the copper trace PNG exported earlier. The image must be strictly black and white: copper areas appear black, and the areas to be milled away are white. Mods CE reads the image dimensions and embedded DPI metadata to correctly scale all toolpath coordinates into real-world millimetres.
Configuring Mill Settings
The mill raster 2D module is where all the key cutting parameters are set. These values directly affect trace quality, milling time, and the risk of shorts or broken traces.
- Tool diameter: 0.4 mm (1/64" flat end mill for trace isolation)
- Cut depth: −0.1 mm (just enough to cut through the 35 µm copper layer)
- Max depth: −0.1 mm (single-pass — copper is thin, no need for multiple depth passes)
- Offset number: 4 (number of concentric clearance passes around each trace)
- Tool overlap: 50% (ensures complete copper removal between adjacent offset passes)
- Feed rate: 4 mm/s (conservative speed for fine 0402 SMD traces)
The offset number controls how much copper is cleared around each trace. A higher value removes more surrounding copper — reducing short-circuit risk but proportionally increasing milling time. For a design with 0402 SMD components, an offset of 4 provides a good balance.
Previewing the Toolpath
Before sending any commands to the machine, Mods CE renders a live preview of the computed toolpath in the view module. I carefully inspected the preview to confirm:
- All traces were correctly isolated with adequate clearance on both sides
- No toolpath line crossed over an existing trace (which would cause an electrical short)
- The board outline was absent — that runs as a completely separate operation with a different bit
- Pad areas for SMD components were preserved and not accidentally milled
Changing the Bit
Before anything else, the bit needs to go in. I jogged the spindle to a safe raised position through Mods, then used an Allen key to loosen the collet and seat the V-bit (0.2 mm tip). This is the tool for etching the traces. The 1/32" (0.8 mm) end mill comes later, for drilling and the board outline. The order matters: traces first, then drills, then outline — working from inside the board out.
Setting Parameters and Zeroing Z
With the V-bit in, I used the V-bit calculator to get the right cut parameters. Depth came out to 0.09 mm, offset set to 4. Then I zeroed Z manually — lowering the bit slowly until it just touched the copper surface, then tightening it in place. Getting this right matters a lot. Too shallow and you leave thin copper bridges between traces. Too deep and you're cutting into the FR1 substrate and the bit wears out fast.
Sending the File
Back in Mods, I opened the socket and hit Send File to push the trace toolpath to the machine. It started cutting right away. If something looks off mid-run — depth too shallow, strange sound — the right move is to pause at the machine, close the socket in Mods, and reset before trying again. I stayed close and watched the first few passes to make sure the depth and clearance looked right.
Step 3
Milling the Board
The FR1 board was taped down flat to the sacrificial bed with double-sided tape across the full surface. Flatness really does affect trace quality, so I pressed it down evenly before starting.
Once the traces were done, I swapped to the 1/32" (0.8 mm) end mill and ran the drills, then the board outline. Same process: re-zero Z, open socket, send file.
Step 4
Soldering Components
With the board milled and sanded, I proceeded to solder the components into it.
Sourcing Components from FabStash
All components were sourced from FabStash — Fab Lab Kerala's in-house component inventory and management system. FabStash provides a searchable catalogue of lab-stocked parts complete with datasheets, package types, and live stock levels, making it straightforward to locate and verify exactly what is needed for a design without waiting for external procurement.
The FabStash inventory is accessible at inventory.fablabkerala.in.
In Fabstash, you can search for the components you need, and add them to the cart. Once you have added all the components you need, you can click 'Request' and the components will be reserved for you.
After you click 'Request', you can click the clipboard icon and see the pending request you send to the lab.
You can click on the request and then you can see the components you requested. At the bottom right corner, there is a button to print the file. Click it and print the file.
After you print out the request, you can take the components from the Academy inventory and attach them to the corresponding component name.
Soldering Process
Before soldering, each component footprint on the board was verified against its physical package to confirm correct orientation and pin assignment. The 0402 passive components (resistors and LED) were placed under a soldering microscope for precision work.
For each SMD component, my process was:
- Apply a small amount of liquid flux to both pads
- Tin one pad lightly with solder
- Place the component with reverse-action tweezers and tack it to the tinned pad
- Solder the second pad with fresh solder and a clean iron tip
- Return to the first pad to reflow and form a proper fillet joint
- Clean the area with IPA flux remover
The XIAO RP2040 module was soldered last — its castellated pads were first pre-tinned, then aligned to the board footprint and reflowed carefully to avoid cold joints or bridging between adjacent pads.
Step 5
Testing the Board
Before applying power, I performed a thorough pre-power inspection:
- Visual inspection — checked every solder joint under the microscope for cold joints, bridges, or lifted pads
- Continuity check — probed VCC and GND rails to confirm no short circuit existed between them
- Net connectivity — verified key signal nets (LED pin, button pin, XIAO I/O) were connected as designed
The board passed all pre-power checks. I connected it to a PC via USB-C. The XIAO RP2040 was recognised immediately as a USB device, confirming the board's power rails were clean and the microcontroller was functional.
Step 6
Programming Environment Setup
With the board tested and confirmed functional over USB, the next step was to configure a programming environment and write firmware.
Configuring the Arduino IDE for the XIAO RP2040
The XIAO RP2040 is not included in the Arduino IDE's default board list and requires a third-party board package to be added manually. Follow the steps below to prepare the IDE:
- Open the Arduino IDE and navigate to File → Preferences.
-
In the Additional Board Manager URLs field, paste the following URL
and click OK:
https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json - Navigate to Tools → Board → Board Manager, search for Raspberry Pi Pico/RP2040, and click Install. This installs the full RP2040 board family including the XIAO variant.
- Once installed, select the target board via Tools → Board → Raspberry Pi RP2040 Boards → Seeed XIAO RP2040.
- Connect the board via USB-C, select the correct port under Tools → Port, and click Upload to flash a sketch.
Step 7
Source Code — Christmas Lights
To demonstrate the board's capabilities, I wrote a Christmas lights animation program that drives four LEDs through a sequence of patterns. The program also accepts input from the tactile push button to cycle through animation speeds at runtime, making it an interactive demonstration of both digital output and digital input on the XIAO RP2040.
Program Behaviour
Each iteration of loop() runs through four distinct LED animation patterns
in sequence:
- Chase forward — LEDs light up one at a time from D0 to D3, creating a left-to-right sweep
- Chase backward — the same sweep runs in reverse, from D3 back to D0
- Fill and empty — LEDs accumulate one by one until all four are lit, then extinguish in reverse order
- Blink all — all four LEDs flash on and off together three times
The timing of every animation step is governed by a single delayTime variable
(initialised to 150 ms). Pressing the push button increments this value by 50 ms,
slowing the animation, and wraps back to 50 ms once it exceeds 400 ms, cycling
through five distinct speed levels.
Complete Code
// Christmas Lights — XIAO RP2040
// Cycles through four LED animation patterns.
// Tactile button press increments animation speed.
const int ledPins[] = {D0, D1, D2, D3};
const int NUM_LEDS = 4;
const int BUTTON_PIN = D8;
int delayTime = 150; // ms between each animation step
void setup() {
for (int i = 0; i < NUM_LEDS; i++) {
pinMode(ledPins[i], OUTPUT);
digitalWrite(ledPins[i], LOW);
}
pinMode(BUTTON_PIN, INPUT_PULLUP); // active-low with internal pull-up
}
void loop() {
// 1 — Chase forward (D0 → D3)
for (int i = 0; i < NUM_LEDS; i++) {
digitalWrite(ledPins[i], HIGH);
delay(delayTime);
digitalWrite(ledPins[i], LOW);
}
// 2 — Chase backward (D3 → D0)
for (int i = NUM_LEDS - 1; i >= 0; i--) {
digitalWrite(ledPins[i], HIGH);
delay(delayTime);
digitalWrite(ledPins[i], LOW);
}
// 3 — Fill up then empty
for (int i = 0; i < NUM_LEDS; i++) {
digitalWrite(ledPins[i], HIGH);
delay(delayTime);
}
for (int i = NUM_LEDS - 1; i >= 0; i--) {
digitalWrite(ledPins[i], LOW);
delay(delayTime);
}
// 4 — Blink all together x3
for (int j = 0; j < 3; j++) {
for (int i = 0; i < NUM_LEDS; i++) digitalWrite(ledPins[i], HIGH);
delay(delayTime);
for (int i = 0; i < NUM_LEDS; i++) digitalWrite(ledPins[i], LOW);
delay(delayTime);
}
// Button press cycles speed: 50 → 100 → ... → 400 → 50 ms
if (digitalRead(BUTTON_PIN) == LOW) {
delayTime += 50;
if (delayTime > 400) delayTime = 50;
delay(300); // software debounce
}
}
Step 8
Improved Program — Interrupt-Driven Button Control
The first version of the Christmas lights program polled the button state at the end of
each loop() iteration, meaning a button press could be missed entirely if it
happened while the animation was mid-pattern. For the improved version I rewrote the button
handling to use a hardware interrupt, so every press is captured
immediately regardless of where the animation is in its cycle.
The program also expands to five animation patterns (up from four), a four-speed mode system (Slow → Medium → Fast → Crazy), and an LED doubling effect in fast modes where two adjacent LEDs light simultaneously to give the illusion of faster movement.
Key Improvements Over v1
-
Hardware interrupt (ISR) —
attachInterrupt()registersbuttonISR()on the falling edge of the button pin. The ISR sets avolatileflag rather than acting directly, keeping the handler as short as possible. -
Interrupt debounce — a 250 ms guard window inside the ISR using
millis()rejects spurious re-triggers caused by contact bounce, with no hardware RC filter needed. -
smartDelay()— replaces every rawdelay()call inside the animation patterns. It breaks the wait into 10 ms slices and callscheckButton()between each slice, so a mode change takes effect within 10 ms rather than waiting for the full animation step to finish. - Mode feedback flash — on each button press, all LEDs extinguish briefly, then (mode + 1) LEDs light up as a visual indicator of the new speed level before the animation resumes.
- LED doubling in fast modes — in modes 2 and 3, the chase patterns simultaneously illuminate two adjacent LEDs, widening the apparent moving block and keeping the animation readable at high speed.
- Pattern 5 — alternating pairs — a new pattern that toggles LEDs 0 & 2 against LEDs 1 & 3, producing a strobe-like effect that intensifies at higher speeds.
Speed Mode Reference
Mode 1 — Medium: 150 ms per step — standard pace.
Mode 2 — Fast: 80 ms per step, LED doubling active, double repetition count on blink and pair patterns.
Mode 3 — Crazy: 40 ms per step, LED doubling active — maximum intensity.
Complete Code
// Christmas Lights v2 — XIAO RP2040
// 4 LEDs, 1 Button | 4 speed modes with interrupt-driven button control
const int ledPins[] = {D0, D1, D2, D3};
const int NUM_LEDS = 4;
const int BUTTON_PIN = D8;
volatile bool buttonPressed = false;
int mode = 0; // 0=slow 1=medium 2=fast 3=crazy
int delayTime = 300; // ms per animation step
unsigned long lastDebounce = 0;
// ── Interrupt Service Routine ──────────────────────────────────────────────
// Called instantly on every falling edge of BUTTON_PIN.
// Sets a flag only; never acts directly from within the ISR.
// 250 ms guard window rejects contact bounce without hardware filtering.
void buttonISR() {
if (millis() - lastDebounce > 250) {
buttonPressed = true;
lastDebounce = millis();
}
}
// ── Mode change handler ────────────────────────────────────────────────────
// Reads and clears the ISR flag, advances the mode, updates delayTime,
// then flashes (mode+1) LEDs as visual confirmation of the new speed.
void checkButton() {
if (buttonPressed) {
buttonPressed = false;
mode = (mode + 1) % 4;
switch (mode) {
case 0: delayTime = 300; break;
case 1: delayTime = 150; break;
case 2: delayTime = 80; break;
case 3: delayTime = 40; break;
}
// Flash feedback: lit LED count equals current mode index + 1
for (int i = 0; i < NUM_LEDS; i++) digitalWrite(ledPins[i], LOW);
delay(100);
for (int i = 0; i <= mode; i++) digitalWrite(ledPins[i], HIGH);
delay(300);
for (int i = 0; i < NUM_LEDS; i++) digitalWrite(ledPins[i], LOW);
delay(100);
}
}
// ── Responsive delay ───────────────────────────────────────────────────────
// Breaks the wait into 10 ms slices so a button press is acted on
// within 10 ms rather than at the end of the full animation step.
void smartDelay(int ms) {
for (int i = 0; i < ms; i += 10) {
delay(10);
checkButton();
}
}
// ── Setup ──────────────────────────────────────────────────────────────────
void setup() {
for (int i = 0; i < NUM_LEDS; i++) {
pinMode(ledPins[i], OUTPUT);
digitalWrite(ledPins[i], LOW);
}
pinMode(BUTTON_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonISR, FALLING);
}
// ── Main loop ──────────────────────────────────────────────────────────────
void loop() {
// PATTERN 1: Chase forward (modes 2+ light the next LED simultaneously)
for (int i = 0; i < NUM_LEDS; i++) {
digitalWrite(ledPins[i], HIGH);
if (mode >= 2 && i + 1 < NUM_LEDS) digitalWrite(ledPins[i + 1], HIGH);
smartDelay(delayTime);
digitalWrite(ledPins[i], LOW);
if (mode >= 2 && i + 1 < NUM_LEDS) digitalWrite(ledPins[i + 1], LOW);
}
// PATTERN 2: Chase backward
for (int i = NUM_LEDS - 1; i >= 0; i--) {
digitalWrite(ledPins[i], HIGH);
if (mode >= 2 && i - 1 >= 0) digitalWrite(ledPins[i - 1], HIGH);
smartDelay(delayTime);
digitalWrite(ledPins[i], LOW);
if (mode >= 2 && i - 1 >= 0) digitalWrite(ledPins[i - 1], LOW);
}
// PATTERN 3: Fill up then empty
for (int i = 0; i < NUM_LEDS; i++) {
digitalWrite(ledPins[i], HIGH);
smartDelay(delayTime);
}
for (int i = NUM_LEDS - 1; i >= 0; i--) {
digitalWrite(ledPins[i], LOW);
smartDelay(delayTime);
}
// PATTERN 4: Blink all (6 times in fast modes, 3 in slow/medium)
int blinkCount = (mode >= 2) ? 6 : 3;
for (int j = 0; j < blinkCount; j++) {
for (int i = 0; i < NUM_LEDS; i++) digitalWrite(ledPins[i], HIGH);
smartDelay(delayTime);
for (int i = 0; i < NUM_LEDS; i++) digitalWrite(ledPins[i], LOW);
smartDelay(delayTime);
}
// PATTERN 5: Alternating pairs (D0+D2 vs D1+D3)
int pairCount = (mode >= 2) ? 6 : 3;
for (int j = 0; j < pairCount; j++) {
digitalWrite(ledPins[0], HIGH); digitalWrite(ledPins[2], HIGH);
digitalWrite(ledPins[1], LOW); digitalWrite(ledPins[3], LOW);
smartDelay(delayTime);
digitalWrite(ledPins[0], LOW); digitalWrite(ledPins[2], LOW);
digitalWrite(ledPins[1], HIGH); digitalWrite(ledPins[3], HIGH);
smartDelay(delayTime);
}
for (int i = 0; i < NUM_LEDS; i++) digitalWrite(ledPins[i], LOW);
}
Key Learnings
- Proper board fixturing — flat double-sided tape and a levelled sacrificial layer — is the single biggest factor in trace milling consistency
- Zeroing the Z-axis precisely on the copper surface is critical; even 0.05 mm error can cause incomplete cuts or substrate damage
- Mods CE's node-based workflow is powerful once understood; the pre-built MDX program makes the learning curve manageable
- Converting to PNG at exactly 1000 DPI ensures the toolpath scaling is accurate in the real world
- Flux application before soldering 0402 SMD components dramatically improves joint quality and reduces bridging
- Always perform a continuity check between power and ground before first power-on — a short can destroy the microcontroller instantly
- FabStash makes component sourcing within the lab efficient and eliminates procurement delays for common parts