1 / 8
µMill – Manual Micro CNC Mill with Digital Readout — zdjęcie 1
1 / 8

µMill – Manual Micro CNC Mill with Digital Readout

Zamów wydruk tego modelu

Obliczymy ostateczną cenę na podstawie wybranego pliku. Sprawdzisz ją i zaakceptujesz przed płatnością.

Zapisane na tym urządzeniu.

Upgraded Proxxon KT70 PCB CNC Micro Mill, with manual control and better reliability

Ocena
0 / 5
Polubienia
32
Pobrania
86
Wydruki
0
Wyświetlenia
1006
Oceny
0

Aktualizacja u źródła:

Informacje o modelu

Źródło
Printables
Kategoria
Tools
Licencja
Creative Commons — Attribution — Noncommercial

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, while still being capable of CNC operations.

🔧 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.

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 is a PCB project designed and milled with the CNC setup:

Optocoupler Endstop Filter:

💬 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

Tagi