• Models
  • Contests
  • Slicer
  • Login
  • Start Here
    thingiverse-iconprintables-iconcults3d-iconmakerworld-iconmyminifactory-icon

    3D GO

    3D ModelsContestsCollectionsSaved ModelsOn a mobile device?

3D GO

Privacy Policy
µMill – Manual Micro CNC Mill with Digital Readout 3D Printer File Image 1
µMill – Manual Micro CNC Mill with Digital Readout 3D Printer File Image 2
µMill – Manual Micro CNC Mill with Digital Readout 3D Printer File Image 3
µMill – Manual Micro CNC Mill with Digital Readout 3D Printer File Image 4
µMill – Manual Micro CNC Mill with Digital Readout 3D Printer File Image 5
µMill – Manual Micro CNC Mill with Digital Readout 3D Printer File Image 6
µMill – Manual Micro CNC Mill with Digital Readout 3D Printer File Image 7
µMill – Manual Micro CNC Mill with Digital Readout 3D Printer File Image 8
µMill – Manual Micro CNC Mill with Digital Readout 3D Printer File Image 9
µMill – Manual Micro CNC Mill with Digital Readout 3D Printer File Image 10
µMill – Manual Micro CNC Mill with Digital Readout 3D Printer File Thumbnail 1
µMill – Manual Micro CNC Mill with Digital Readout 3D Printer File Thumbnail 2
µMill – Manual Micro CNC Mill with Digital Readout 3D Printer File Thumbnail 3
µMill – Manual Micro CNC Mill with Digital Readout 3D Printer File Thumbnail 4
µMill – Manual Micro CNC Mill with Digital Readout 3D Printer File Thumbnail 5
µMill – Manual Micro CNC Mill with Digital Readout 3D Printer File Thumbnail 6
µMill – Manual Micro CNC Mill with Digital Readout 3D Printer File Thumbnail 7
µMill – Manual Micro CNC Mill with Digital Readout 3D Printer File Thumbnail 8
µMill – Manual Micro CNC Mill with Digital Readout 3D Printer File Thumbnail 9
µMill – Manual Micro CNC Mill with Digital Readout 3D Printer File Thumbnail 10

µMill – Manual Micro CNC Mill with Digital Readout

ZK avatarZK

May 31, 2025

makerworld-icon
DescriptionCommentsTags

Description

About a year ago, I published the Proxxon KT70 PCB CNC Micro Mill—a compact and precise CNC machine for milling PCBs and small parts. It quickly became a valuable tool in my workshop.

However, I often found myself wishing for a more hands-on solution—something that allowed for quick adjustments or simple manual operations without needing to go through the full CNC workflow. That’s what led me to create the µMill: a hybrid manual micro mill with digital position readout, compact electronics, and user-friendly control.

🔧 Key Features

Manual XY Control with Digital Readout

Smooth and accurate manual movement along the X and Y axes, measured using high-resolution rotary encoders and displayed on a 128x64 LCD screen. Both axes can be zeroed independently using dedicated buttons—perfect for relative measurements and repositioning.

Spindle Speed Control and Display

The top-mounted potentiometer adjusts the spindle speed, with the expected RPM shown on screen. The spindle is activated via a green button.

Emergency Stop System

A red safety button immediately disables the motor and shorts the spindle wires to quickly brake the rotation—offering both convenience and peace of mind.

🛠 Applications

  • Manual PCB edits and corrections
  • Milling small plastic or aluminum parts
  • Deburring, engraving, and surface cleanup
  • Learning platform for working with digital readouts (DRO)

💡 Electronics & Firmware Overview

The µMill is powered by a custom Arduino-based controller, which handles user input, spindle control, and position tracking. The schematic can be found in documentation folder. To have GRBL control, to operate as a CNC you just need an additional Arduino UNO with the CNC shield, 3 stepper motors and stepper drivers.

 

AS5600 encoders require magnets to read rotation. I have added print files to make it easier to place the magnets exactly at the center of the stepper motor shafts. Use epoxy and leave it overnight.

Components:

  • Arduino Nano (or compatible) – main microcontroller
  • 2× AS5600 magnetic encoders – for X and Y position sensing
  • 128×64 SPI LCD display – shows RPM and live XY positions
  • 10k potentiometer – sets spindle speed and displays RPM
  • TIP120 transistor – drives the spindle with PWM
  • Red Emergency Stop – disables and brakes the spindle
  • Green Spindle Enable – turns on spindle at set speed
  • Black Reset Buttons – zero the X or Y axis individually
  • DC Spindle Motor – controlled via PWM

Firmware Features:

  • Real-time Measurement:
    High-resolution rotary encoders provide smooth, accurate displacement readings. Encoder data is filtered in software and updated every 20ms.
  • Graphical Interface:
    The LCD shows:
    • Estimated spindle RPM
    • X and Y displacement (in mm)
    • µMill logo
  • Speed Control:
    The analog value from the potentiometer is mapped to PWM output and to a live RPM estimate on screen.
  • Emergency Safety:
    The red stop button both cuts power and actively brakes the motor for safe and fast stopping.

🧪 Projects Made with µMill

Here are a few PCB projects designed and milled with this setup:

  • Optocoupler Endstop Filter

  • First Version of Manual Control PCB (failed)

💬 Final Thoughts

I hope this project inspires others to build their own µMill. It was a fun and rewarding challenge, and it turned out to be more useful than I expected—especially for quick manual tasks with precise positioning.

If there’s enough interest, I’d be happy to improve and expand the project further with the community. Possible future upgrades could include:

  • Z-axis support
  • Custom PCB with all components
  • Detailed assembly and wiring guide
  • Public GitHub repository


If you run into any issues or have questions about the build, feel free to ask—I'll do my best to help and explain everything clearly.

 

 

For now, I'm sharing the firmware code directly below.

 

#include <Wire.h>
#include <AS5600.h>
#include <U8g2lib.h>

AS5600 as5600; // Object for AS5600 sensor

// Initialize 128x64 LCD (SPI), rotated 270°
U8G2_ST7920_128X64_1_HW_SPI u8g2(U8G2_R3, /* CS=*/ 10, /* RST=*/ 9);

#define TCA9548A_ADDRESS 0x70 // I2C address of the multiplexer
#define RESET_X_PIN 3         // Button to reset X axis
#define RESET_Y_PIN 4         // Button to reset Y axis
#define PWM_PIN 5             // PWM output (motor control)
#define POT_PIN A0            // Potentiometer input

// Position tracking variables
float total_mm_x = 0.0;
float total_mm_y = 0.0;
float last_angle_x = 0.0;
float last_angle_y = 0.0;
float reference_angle_x = 0.0;
float reference_angle_y = 0.0;
int revolutions_x = 0;
int revolutions_y = 0;

// Timing control
unsigned long last_sensor_read = 0;
unsigned long last_display_update = 0;
const unsigned long SENSOR_INTERVAL = 20;    // Sensor read interval (ms)
const unsigned long DISPLAY_INTERVAL = 100;  // LCD update interval (ms)

// Select a channel on the TCA9548A multiplexer
void tcaSelect(uint8_t channel) {
 if (channel > 7) return;
 Wire.beginTransmission(TCA9548A_ADDRESS);
 Wire.write(1 << channel);
 Wire.endTransmission();
}

void setup() {
 Serial.begin(9600);
 Wire.begin();
 Wire.setClock(400000); // Set I2C speed to 400kHz

 // Initialize LCD
 u8g2.begin();
 u8g2.setFont(u8g2_font_profont12_tf);
 u8g2.clearBuffer();
 u8g2.drawStr(0, 10, "Initializing...");
 u8g2.sendBuffer();

 // Configure pins
 pinMode(RESET_X_PIN, INPUT_PULLUP);
 pinMode(RESET_Y_PIN, INPUT_PULLUP);
 pinMode(PWM_PIN, OUTPUT);
 pinMode(POT_PIN, INPUT);

 // Initialize AS5600 sensor for X axis
 tcaSelect(0);
 if (as5600.begin()) {
   reference_angle_x = as5600.rawAngle() * 0.087890625;
   last_angle_x = 0.0;
 } else {
   Serial.println(F("Error: X axis sensor not detected"));
 }

 // Initialize AS5600 sensor for Y axis
 tcaSelect(1);
 if (as5600.begin()) {
   reference_angle_y = as5600.rawAngle() * 0.087890625;
   last_angle_y = 0.0;
 } else {
   Serial.println(F("Error: Y axis sensor not detected"));
 }
}

void loop() {
 unsigned long current_time = millis();

 // Reset X axis if button pressed
 if (digitalRead(RESET_X_PIN) == LOW) {
   tcaSelect(0);
   total_mm_x = 0.0;
   revolutions_x = 0;
   reference_angle_x = as5600.rawAngle() * 0.087890625;
   last_angle_x = 0.0;
   delay(50);
   while (digitalRead(RESET_X_PIN) == LOW);
 }

 // Reset Y axis if button pressed
 if (digitalRead(RESET_Y_PIN) == LOW) {
   tcaSelect(1);
   total_mm_y = 0.0;
   revolutions_y = 0;
   reference_angle_y = as5600.rawAngle() * 0.087890625;
   last_angle_y = 0.0;
   delay(50);
   while (digitalRead(RESET_Y_PIN) == LOW);
 }

 // Read AS5600 sensors every 20 ms
 if (current_time - last_sensor_read >= SENSOR_INTERVAL) {
   tcaSelect(0);
   float current_angle_x = as5600.rawAngle() * 0.087890625;
   float relative_angle_x = current_angle_x - reference_angle_x;
   float delta_angle_x = relative_angle_x - last_angle_x;
   while (delta_angle_x > 180.0) {
     revolutions_x--;
     delta_angle_x -= 360.0;
   }
   while (delta_angle_x < -180.0) {
     revolutions_x++;
     delta_angle_x += 360.0;
   }
   total_mm_x = revolutions_x + (relative_angle_x / 360.0);
   last_angle_x = relative_angle_x;

   tcaSelect(1);
   float current_angle_y = as5600.rawAngle() * 0.087890625;
   float relative_angle_y = current_angle_y - reference_angle_y;
   float delta_angle_y = relative_angle_y - last_angle_y;
   while (delta_angle_y > 180.0) {
     revolutions_y--;
     delta_angle_y -= 360.0;
   }
   while (delta_angle_y < -180.0) {
     revolutions_y++;
     delta_angle_y += 360.0;
   }
   total_mm_y = revolutions_y + (relative_angle_y / 360.0);
   last_angle_y = relative_angle_y;

   last_sensor_read = current_time;
 }

 // Read potentiometer and compute PWM and expected RPM
 int pot_value = analogRead(POT_PIN);          // Value from 0 to 1023
 int pwm_value = map(pot_value, 0, 1023, 0, 255);
 analogWrite(PWM_PIN, pwm_value);

 // Map potentiometer value to expected RPM (0 to 10,000)
 unsigned long expected_rpm = map(pot_value, 0, 1023, 0, 10000);

 // Update display every 100 ms
 if (current_time - last_display_update >= DISPLAY_INTERVAL) {
   u8g2.firstPage();
   do {
     char buffer[20];
     char value_str[8];

     // Display expected RPM
     snprintf(buffer, sizeof(buffer), "RPM: %lu", expected_rpm);
     u8g2.drawStr(0, 10, buffer);

     // Display X axis
     dtostrf(total_mm_x, 6, 2, value_str);
     snprintf(buffer, sizeof(buffer), "X:%s mm", value_str);
     u8g2.drawStr(0, 48, buffer);

     // Display Y axis
     dtostrf(total_mm_y, 6, 2, value_str);
     snprintf(buffer, sizeof(buffer), "Y:%s mm", value_str);
     u8g2.drawStr(0, 85, buffer);

     // µMill title
     u8g2.drawStr(36, 125, "\xB5Mill");

   } while (u8g2.nextPage());

   last_display_update = current_time;
 }
}
 

📦BOM: 

https://docs.google.com/spreadsheets/d/16nbs9V1qnrR16r7QubPuwHfgM05_ZPlatjmNZF1QJMQ/edit?usp=sharing

 

🛠 Assembly Instructions

The assembly process is largely self-explanatory through the provided images and animations. However, important notes and additional explanations are included to clarify the more critical steps.

During the build, I practiced exporting GIF animations from SolidWorks and putting together simple work instructions — I hope this will make everything as clear as possible.

💡 Tip: If anything seems unclear or you're missing a specific step, feel free to check the labeled images or reach out!

🧩 Start of Assembly

Start by inserting the bearings into their seats, then add the nuts and tighten everything with M3 screws; finally, prepare the motor mount and fasten it using two M5 screws and nuts, at the end fasten the T8 nut (see GIF bellow):

Assemble the Z-axis by attaching the motor mount to the carriage, adding T-nuts and bolts, then installing the leadscrew and linear rods:

make sure to use a shaft collar to relieve stress on the motor bearings*

 

Assemble all individual components together (see the GIF below), and don’t forget to add the two extra T-nuts as shown in the warning image.

Assemble the Z-axis endstop holder and install it right away, but don’t fully tighten it so you can adjust it later; tighten it firmly only after setting the trigger point in relation to the spindle mount (there’s limited space, but it works). Also add the Z top holder.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Again don’t forget to add the shaft collar at the top end of the leadscrew.

 

Assemble the back panel, which is easiest to 3D print with 100% infill, and then drill holes for each power supply – this part is still a work in progress.

 

Mount the AS5600 sensor and its holder onto the stepper motor, and insert all the required T-nuts at the same time (YOU NEED 8 not 6 im sorry :) )

Install the electronics enclosure and attach the side panels.

Assemble the X-axis mount:

You can use M3 nuts, and M3x30 mm bolts for better strenght (
instead of M3x20).

Use heat inserts for this part:

Simply insert the X-axis mount into the slots of the Proxxon table, tighten the motor coupler onto the table’s leadscrew – this completes the mechanical assembly for CNC operation:

For manual readout functionality, a small modification is needed – replace the two M5 screws (7 mm long) with M5 screws of 15 mm length:

Assemble the enclosure for the Arduino and all associated electronics, and mount it in the designated position:

 

 

 

 

 

This completes the assembly!

 

 

 

License:

BY-NC-SA

Related Models

bakercube preview image

bakercube

iomaa profile image

iomaa

44,380

Rock Solid Milling Machine v.09 preview image

Rock Solid Milling Machine v.09

Chris Borge profile image

Chris Borge

4,349

Customizable Christmas Hat Ornaments - Keychain preview image

Customizable Christmas Hat Ornaments - Keychain

Bloomly profile image

Bloomly

303

DIY Dremel CNC preview image

DIY Dremel CNC

NikodemBartnik profile image

NikodemBartnik

29,115

Case for CNC Kitchen Threaded Inserts Soldering Tips preview image

Case for CNC Kitchen Threaded Inserts Soldering Tips

CNC Kitchen profile image

CNC Kitchen

1,130

BETTER CAT40 CNC TOOL TAG preview image

BETTER CAT40 CNC TOOL TAG

Engineerable profile image

Engineerable

89

Mostly Printed CNC "MPCNC"  BURLY C-23.5mm OD preview image

Mostly Printed CNC "MPCNC" BURLY C-23.5mm OD

Allted profile image

Allted

17,597

Mostly Printed CNC Primo F-25mm MPCNC preview image

Mostly Printed CNC Primo F-25mm MPCNC

V1 Engineering profile image

V1 Engineering

606