Documentation | Usage Guide

Machine Week Group Project - 2-Axis CNC Plotter

This week our team focused on building a complete small CNC plotting machine from scratch: mechanics, electronics, firmware, and testing.

Our goal was not only to make it move, but to make it reproducible and clearly documented so another team can rebuild it.

We used a Raspberry Pi Pico (RP2040) as the controller, two stepper motors with TMC2208 drivers for motion, and one servo motor for pen up/down.

Firmware is written in MicroPython and controlled over USB serial commands.

> Attribution: This firmware is a remix of the PicoNC idea and workflow. We adapted the base concept for our machine with CoreXY support, pen-servo control, and a practical command set for drawing.


1) Project Intent and Scope

For Machine Week, we wanted a compact plotting machine that can:

Section 1 Screenshot


2) Machine Building and Assembly

Build Source and Preparation

We followed the Blot assembly workflow and adapted it to our available lab hardware:

Used Parts

Figure 1. Used parts prepared before assembly.

Carriage Assembly

We started by assembling the carriage using:

Detailed assembly sequence:

  1. Place the printed carriage body on a flat table and pre-sort long screws, spacers, eccentric spacers, and wheels.
  2. Press the flanged bearings into the wheel positions and verify each wheel rotates freely before mounting.
  3. Mount the first two wheels diagonally on the carriage plate, then add the opposite pair to keep the plate balanced during tightening.
  4. Install the fixed spacers on one side and eccentric spacers on the adjustable side so wheel preload can be tuned later.
  5. Insert M5 screws through the wheel stack (screw -> wheel/bearing -> spacer/eccentric -> printed part -> nut) and keep nuts slightly loose.
  6. Tighten in a cross pattern, then rotate eccentric spacers until all wheels touch the rail profile without binding.
  7. Final check: carriage should slide smoothly with no rattle and no hard points.

Carriage Assembly

Figure 2. Carriage plate assembly with wheel hardware layout.

Carriage Downside

Figure 3. Bottom side of the carriage after wheel installation.

Carriage Upside

Figure 4. Top side of the carriage showing final wheel alignment.

Printed Rail and Tool Holder

For the printed rail assembly we used:

Detailed assembly sequence:

  1. Insert the two flanged bearings into the printed rail pocket, matching the orientation visible in the photo.
  2. Pass the M5 screw through the rail and bearing stack with the 1 mm spacer where needed to avoid side rubbing.
  3. Secure with the M5 nut but keep a small clearance so the bearing can rotate freely.
  4. Dry-fit the servo in its slot and verify the horn side aligns with the pen-link side before final fastening.
  5. Route the servo cable toward the rear side of the machine to keep it away from moving belts.

Printed Rail Assembly

Figure 5. Printed rail with bearing installed inside the housing.

Then we assembled the tool holder using:

Detailed assembly sequence:

  1. Build the 3-wheel triangular arrangement first (two supporting wheels and one adjustable wheel).
  2. Use the eccentric spacer on the adjustable wheel so the grip on the rail can be tuned.
  3. Install the knurled M5x20 pen clamp screw through the tool-head body and test with a real pen during assembly.
  4. Tighten the wheel hardware until the holder rolls smoothly on the printed rail with minimal side play.
  5. Verify pen axis is vertical and the pen can move up/down without touching wheel hardware.

Tool Holder

Figure 6. Front view of tool holder with three-wheel arrangement and pen test fit.

Tool Holder

Figure 7. Side view of tool holder showing pen clamp and wheel stack.

Frame, Feet, and Belts

We cut two aluminum extrusions to 25 cm:

Cutting Extrusions

Figure 8. Aluminum extrusion cutting process (view 1).

Cutting Extrusions

Figure 9. Aluminum extrusion cutting process (view 2).

After checking carriage travel on the extrusions, we assembled the feet with:

We used 3 T-nuts per leg to fix the motor feet.

Detailed assembly sequence:

  1. Slide T-nuts into the side channels of each 25 cm extrusion before closing both ends.
  2. Align each printed leg square to the extrusion and lightly tighten screws first.
  3. Mount each stepper on the leg and install pulley on motor shaft; keep pulley height aligned with belt path.
  4. Re-check frame squareness by measuring diagonals, then perform final tightening.
  5. Mount the cross rail/carriage assembly and confirm full travel by hand.

Feet Assembly

Figure 10. Inserting and positioning T-nuts in extrusion channels.

Feet Assembly

Figure 11. Frame with feet and carriage mounted for travel checks.

For belt routing, we created a loop through the belt fastener, routed through carriage and pulleys, then returned to the fastener and tensioned.

Detailed belt routing and tensioning:

  1. Make the first belt loop and lock it into the carriage-side belt fastener.
  2. Route belt around the first pulley, through the carriage path, and around the opposite side pulley as shown.
  3. Return belt end back to the fastener and create the second loop.
  4. Keep both belt segments parallel and untwisted in the central crossing area.
  5. Apply tension gradually while moving carriage by hand to detect tight spots.
  6. Final tension target: no visible slack, smooth travel end-to-end, and no audible skipping when moved quickly by hand.

Belt Threading

Figure 12. Belt routing through pulley and carriage path.

Belt Threading

Figure 13. Top view of belt crossing and alignment.

Assembly

Figure 14. Overall machine assembly after belt installation.

Servo as Z-Like Pen Axis

The servo motor on the tool holder provides pen up / pen down behavior (Z-like action for drawing).

Pen Down

Figure 15. Pen down position for drawing.

Pen Up

Figure 16. Pen up position for travel moves.

Mechanical Lessons Learned


3) Electronics and Wiring

Controller and drivers:

Pin Mapping

SignalPico PinNote
Motor A/B Direction (X logical DIR)GP8TMC2208 DIR
Motor A/B Step (X logical STEP)GP9TMC2208 STEP
Motor second channel DIR (Y logical DIR)GP12TMC2208 DIR
Motor second channel STEP (Y logical STEP)GP13TMC2208 STEP
Pen servo PWMGP2750 Hz PWM

Tested working in our setup:

Electrical Notes


4) Firmware and Software Documentation

Firmware runs on MicroPython and exposes a line-based serial protocol over USB CDC.

Core Features

Main Configuration Parameters


# Servo
SERVO_MODE = "CR"        # "ANGLE" or "CR"
SWAP_PEN = True
PEN_UP_ANGLE = 30
PEN_DOWN_ANGLE = 120
SERVO_STOP_US = 1500
PEN_UP_US = 1400
PEN_DOWN_US = 1600
PEN_UP_MS = 220
PEN_DOWN_MS = 220

# Kinematics
COREXY = True
SWAP_XY = True
MOTOR_A_SIGN = 1
MOTOR_B_SIGN = 1

# Motion speed
DEFAULT_INTERVAL = 1200  # microseconds, lower is faster

Kinematics Behavior

If axis behavior looks wrong:

Motion Model

Motion is intentionally blocking.

Each command executes to completion before next command is processed.

Why we kept it this way:


5) Serial Command Protocol

Commands are case-insensitive, one line each, ending with newline.

Motion Commands

CommandFunction
X<n>Relative X move in steps (signed)
Y<n>Relative Y move in steps (signed)
MOVE X<n> Y<n>Synchronous move; either axis optional
SPEED <us>Set step interval in microseconds (>=200)
STOPStop motion and release servo
WAITProcess pending servo release, returns OK IDLE
STATUSReturn machine busy/idle state

Pen / Servo Commands

CommandFunction
PENUPLift pen
PENDOWNLower pen
SERVO <val>Direct servo value (angle or pulse width)
SERVO_MODE ANGLE / CRSwitch servo mode
SERVO_STOP_US <us>Set neutral pulse in CR mode
PENUP_US <us> / PENDOWN_US <us>Tune CR movement pulse
PENUP_MS <ms> / PENDOWN_MS <ms>Tune CR movement duration
PENUP_ANGLE <deg> / PENDOWN_ANGLE <deg>Tune angle mode positions

Example Session


PLOTTER READY
PENUP
OK PEN_UP
MOVE X500 Y500
OK MOVE X=500 Y=500
PENDOWN
OK PEN_DOWN
X1000
OK X 1000
Y-500
OK Y -500
PENUP
OK PEN_UP

6) Software Workflow (Host Side)

Our workflow for testing and drawing:

  1. Flash latest MicroPython to Pico.
  2. Upload firmware as main.py.
  3. Open serial console (Thonny or Web Serial terminal).
  4. Run manual movement tests:

motor tests

Figure 20. Carriage movement test driven from the software command interface.

  1. Tune:
  1. Send drawing path commands.

For reliable start-up, we always:


7) Calibration, Validation, and Results

What We Calibrated

Validation Method

Observed Results

Calibartion test

Figure 19. Assembly-stage movement test after mounting frame, feet, and carriage.


8) Troubleshooting Guide

Pen motion reversed (up/down swapped): toggle SWAP_PEN.

X produces diagonal movement: enable COREXY.

X and Y appear exchanged: set SWAP_XY = True.

One motor direction wrong: invert MOTOR_A_SIGN or MOTOR_B_SIGN.

Motors stall at high speed: increase DEFAULT_INTERVAL (slower), then retune.

Servo jitters: verify power stability and adjust pulse values.

CR servo drifts at stop: tune SERVO_STOP_US around neutral (for example 1490-1520).


9) What We Would Improve Next

This would move the project from a robust weekly prototype to a more complete production-style machine controller.


10) Usage

This section links to the interactive usage/control pages for the HAAC plotter workflow:

Sample usage

Figure 17. Usage site walkthrough video.


11) Video

Machine Week Video

Figure 17. Machine Week demo video file preview.

12) Group Reflection

Machine Week forced us to connect mechanical reality and software logic directly.

We observed that many apparent "software bugs" in machine projects are actually system-level interactions between mechanics, wiring, and timing assumptions.

The strongest outcome was getting clean and repeatable drawing only after tuning all subsystems together.

Our main takeaway is that documentation is part of the machine itself: if calibration and decisions are not written clearly, the machine is not truly reproducible.


Usage Guide

Week 11 – Group Project

This group assignment explored local network communication by sending messages between a computer and a mobile phone over shared Wi-Fi, then extending the setup into a simple two-way chat system.

Goal of the Assignment

The goal of this group assignment was to send a message between a computer and a mobile device using network communication.

Devices Used

Project A

Device: Windows 11 laptop

Role: Python server

Software: PyCharm

Project B

Device: iPhone 13

Role: Client

Software: Safari browser

We used a Python-based HTTP server to receive messages sent from a mobile phone over a shared Wi-Fi network. The computer hosted the server, and the phone sent a message using an HTTP request. The message was included in the URL and received by the server.

Project A Setup

Step 1: Open PyCharm

  1. Launch PyCharm.
  2. Click New Project.
  3. If needed, download PyCharm from JetBrains.

Step 2: Create Project

  1. Name it something like NetworkingProject.
  2. Click Create.
  3. Wait for the project to load.
PyCharm new project screen
PyCharm new project window.

Step 3: Create Python File

  1. Click the + sign at the top of the left panel.
  2. Select New Python File.
  3. Name it server.

This creates server.py.

Step 4: The Code

Paste the following code into server.py:

from http.server import BaseHTTPRequestHandler, HTTPServer

class MyHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        message = self.path[1:]  # remove "/"
        print("Received message from iPhone:", message)
        self.send_response(200)
        self.end_headers()
        self.wfile.write(b"Message received!")

server = HTTPServer(("0.0.0.0", 8000), MyHandler)
print("Server running...")
server.serve_forever()
PyCharm server.py code and run console
The server code opened in PyCharm and running successfully.

Step 5: Run the Code

  1. Right-click inside the file.
  2. Click Run 'server'.
  3. Or click the green arrow button.

You should see Server running... at the bottom.

Step 6: Get Your IP Address

  1. Open the PyCharm terminal or normal Command Prompt.
  2. Type ipconfig.
  3. Find the IPv4 address, for example 192.168.X.X.
PyCharm terminal showing ipconfig output
Finding the IPv4 address using ipconfig.

Project B Setup

  1. On the iPhone, open Safari.
  2. Type the local server address in this format:
http://192.168.x.x:8000/HelloFromiPhone

Then enter the website.

iPhone Safari entering the local network URL
The iPhone sending a message by entering the server URL in Safari.
iPhone showing message received screen
The browser confirms that the message was received.

Final Result of One-Way Communication

In the PyCharm console, we could see:

Received message from iPhone: HelloFromiPhone

This confirmed that the system worked successfully.

PyCharm console showing received message from iPhone
Console output confirming successful one-way communication.

2-Way Communication System

Instead of just sending a message, we stored messages on the server and allowed both devices to send and receive messages. In this way, the project became a mini chat system.

Step 1: Replace the Code

In PyCharm, replace the previous code with the following:

from http.server import BaseHTTPRequestHandler, HTTPServer
import urllib.parse

messages = []  # shared message list

class MyHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        global messages

        if self.path.startswith("/send"):
            query = urllib.parse.urlparse(self.path).query
            params = urllib.parse.parse_qs(query)

            msg = params.get("msg", [""])[0]
            sender = params.get("sender", ["Unknown"])[0]

            full_msg = f"{sender}: {msg}"
            messages.append(full_msg)
            print(full_msg)

            self.send_response(200)
            self.end_headers()
            self.wfile.write(b"Message sent!")

        elif self.path.startswith("/messages"):
            self.send_response(200)
            self.end_headers()
            response = "\n".join(messages)
            self.wfile.write(response.encode())

        else:
            self.send_response(200)
            self.send_header("Content-type", "text/html")
            self.end_headers()

            html = f"""
            <html>
            <body>
            <h2>Mini Chat</h2>
            <form action=\"/send\">
            Name: <input type=\"text\" name=\"sender\"><br><br>
            Message: <input type=\"text\" name=\"msg\"><br><br>
            <input type=\"submit\" value=\"Send\">
            </form>
            <h3>Messages:</h3>
            <pre id=\"chat\"></pre>
            <script>
            async function loadMessages() {
                let res = await fetch('/messages');
                let text = await res.text();
                document.getElementById('chat').innerText = text;
            }
            setInterval(loadMessages, 1000);
            </script>
            </body>
            </html>
            """
            self.wfile.write(html.encode())

server = HTTPServer(("0.0.0.0", 8000), MyHandler)
print("Chat server running...")
server.serve_forever()
Two-way communication server code in PyCharm
The updated code for the two-way communication system.

Step 2: Run It

Click Run in PyCharm. You should see:

Chat server running...

Step 3: Open on Both Devices

On the computer browser:

http://localhost:8000

On the iPhone in Safari:

http://YOUR_IP:8000

Then send messages between both devices.

Mini Chat Interface

After clicking Send on the phone, the chat interface and message confirmation appeared.

Mini chat interface on iPhone
The chat interface opened on the iPhone.
iPhone confirmation after sending a message
The confirmation page shown after sending a message.
Mini chat messages on computer browser
Messages displayed in the browser on the computer.
Server console showing sent and received messages
Server logs showing requests and sent messages.

Common Issues

Make sure that:

If the iPhone shows “This site can’t be reached”, check:

Reflection

In this assignment, we learned how communication between devices works using networking concepts. We understood how a computer can act as a server while a mobile phone acts as a client.

We also learned how HTTP requests function and how data can be transmitted through a URL. Writing and running the Python code helped us understand how servers handle incoming requests.

One difficulty we faced was finding the correct IP address and ensuring both devices were connected to the same Wi-Fi network. After solving these issues, the system worked successfully.

Later, we extended the system from one-way communication into a two-way communication system. A shared message list was implemented on the server, allowing both the computer and the mobile device to send and receive messages dynamically.

Overall, this project improved our understanding of networking, especially client-server communication and data transmission over a local network.