Maker.io main logo

Stepper Motor Turntable

2,177

2025-05-02 | By Adafruit Industries

License: See Original Project 3D Printing Solder / Desoldering

Courtesy of Adafruit

Guide by Ruiz Brothers and Liz Clark

Overview

3d_printing_guide-thumb

 

Put a spin on your videos with this motorized turntable. Turn the ‎IKEA Snudda Lazy Susan into a motorized tabletop using ‎CircuitPython and electronics from Adafruit.‎

Use a stepper motor and the silent TMC2209 driver to give the ‎tabletop a twirl. This project allows you to achieving smooth motion ‎for filming your projects. With an OLED screen and rotary encoder, ‎you can change the direction, adjust speed and have start and stop ‎control.‎

OLED Menu

The on-screen menu on the OLED uses custom fonts and icons to ‎display if the motor is on, the direction, and the speed. Using the ‎rotary encoder, you can push to select, and turn to change and ‎adjust the direction, speed and on/off.‎

3d_printing_demo-menu

Speed Control

Using the TMC2209 stepper driver, you can choose up to three ‎different speeds. Low, medium, and fast are shown on the OLED as ‎icons with text.

3d_printing_demo-speed

Parts

Circuit Diagram

The diagram below provides a general visual reference for wiring of ‎the components once you get to the Assembly page. This diagram ‎was created using the software package Fritzing.‎

Adafruit Library for Fritzing

Adafruit uses the Adafruit's Fritzing parts library to create circuit ‎diagrams for projects. You can download the library or just grab ‎individual parts. Get the library and parts from GitHub - Adafruit ‎Fritzing Parts.‎

diagram_1

Wired Connections

  • VDD pin from TMC2209 to 3V pin on KB2040‎

  • G pin from TMC2209 to GND pin on KB2040‎

  • DIR pin from TMC2209 to D5 pin on KB2040‎

  • STEP pin from TMC2209 to D6 pin on KB2040‎

  • MS1 pin from TMC2209 to D2 pin on KB2040‎

  • MS2 pin from TMC2209 D3 pin on KB2040‎

  • EN pin from TMC2209 to D4 pin on KB2040‎

  • Rotary STEMMA QT to KB2040‎

  • ‎1.3in OLED to Rotary STEMMA QT‎

  • B- (blue wire) from stepper motor to 2B pin on TMC2209‎

  • B+ (red wire) from stepper motor to 2A pin on TMC2209‎

  • A+ (black wire) from stepper motor to 1A pin on TMC2209‎

  • A- (green wire) from stepper motor to 1B pin on TMC2209‎

  • GND pin from MPM3601 to GND pin on KB2040‎

  • 5V pin from MPM3601 to RAW pin on KB2040‎

  • +DC Jack to VIN pin on MPM3601‎

  • +DC Jack to + on TMC2209‎

  • ‎-DC Jack to - on TMC2209 ‎

  • ‎-DC Jack to GND pin on KB2040 ‎

The KB2040 and Stepper Motor are powered by a 12V 2A power ‎supply.‎

CAD Files

3D Printed Parts

Individual 3MF files for 3D printing will need to be oriented in slicing ‎software to print on FDM machines without any support material ‎using PLA filament. Original design source may be downloaded ‎using the links below.‎

printed_2

All Parts

Use the All Parts.3MF file to import all of the models into your slicer. ‎This file includes multiple plates with the models pre-oriented for ‎ready-to-print parts.‎

parts_3

‎3MF.zip‎

Download CAD Source

Build Volume

The parts require a 3D printer with a minimum build volume of:‎

  • ‎220mm (X) x 220mm (Y) x 40mm (Z)‎

volume_4

Design Source Files

The project assembly was designed in Fusion 360. This can be ‎downloaded in different formats like STEP, STL and more.‎

Electronic components like Adafruit's boards, displays, connectors ‎and more can be downloaded from the Adafruit CAD parts GitHub ‎Repo.‎

assembly_5

Install CircuitPython

CircuitPython is a derivative of MicroPython designed to simplify ‎experimentation and education on low-cost microcontrollers. It ‎makes it easier than ever to get prototyping by requiring no upfront ‎desktop software downloads. Simply copy and edit files on ‎the CIRCUITPY drive to iterate.‎

CircuitPython Quickstart

Follow this step-by-step to quickly get CircuitPython running on your ‎board.‎

Download the latest version of CircuitPython for this board via ‎circuitpython.org

Click the link above to download the latest CircuitPython UF2 file.‎

Save it wherever is convenient for you.‎

download_6

board_7

To enter the bootloader, hold down the BOOT/BOOTSEL ‎button (highlighted in red above), and while continuing to hold it ‎‎(don't let go!), press and release the reset button (highlighted in red ‎or blue above). Continue to hold the BOOT/BOOTSEL button until ‎the RPI-RP2 drive appears!‎

If the drive does not appear, release all the buttons, and then repeat ‎the process above.‎

You can also start with your board unplugged from USB, press and ‎hold the BOOTSEL button (highlighted in red above), continue to ‎hold it while plugging it into USB, and wait for the drive to appear ‎before releasing the button.‎

A lot of people end up using charge-only USB cables and it is very ‎frustrating! Make sure you have a USB cable you know is good for ‎data sync.‎

You will see a new disk drive appear called RPI-RP2.‎‎

Drag the adafruit_circuitpython_etc.uf2 file to RPI-RP2.‎

drag_8

drag_9

The RPI-RP2 drive will disappear, and a new disk drive ‎called CIRCUITPY will appear.‎

That's it, you're done! :)‎

drive_10

Safe Mode

You want to edit your code.py or modify the files on ‎your CIRCUITPY drive but find that you can't. Perhaps your board ‎has gotten into a state where CIRCUITPY is read-only. You may have ‎turned off the CIRCUITPY drive altogether. Whatever the reason, safe ‎mode can help.‎

Safe mode in CircuitPython does not run any user code on startup ‎and disables auto-reload. This means a few things. First, safe ‎mode bypasses any code in boot.py (where you can ‎set CIRCUITPY read-only or turn it off completely). Second, it does ‎not run the code in code.py. And finally, it does not automatically ‎soft-reload when data is written to the CIRCUITPY drive.‎

Therefore, whatever you may have done to put your board in a non-‎interactive state, safe mode gives you the opportunity to correct it ‎without losing all of the data on the CIRCUITPY drive.‎

Entering Safe Mode

To enter safe mode when using CircuitPython, plug in your board or ‎hit reset (highlighted in red above). Immediately after the board ‎starts up or resets, it waits 1000ms. On some boards, the onboard ‎status LED (highlighted in green above) will blink yellow during that ‎time. If you press reset during that 1000ms, the board will start up in ‎safe mode. It can be difficult to react to the yellow LED, so you may ‎want to think of it simply as a slow double click of the reset button. ‎‎(Remember, a fast double click of reset enters the bootloader.)‎

In Safe Mode

If you successfully enter safe mode on CircuitPython, the LED will ‎intermittently blink yellow three times.‎

If you connect to the serial console, you'll find the following message.‎

Copy Code
Auto-reload is off.
Running in safe mode! Not running saved code.

CircuitPython is in safe mode because you pressed the reset button during boot. Press again to exit safe mode.

Press any key to enter the REPL. Use CTRL-D to reload.

You can now edit the contents of the CIRCUITPY drive. ‎Remember, your code will not run until you press the reset button, or ‎unplug and plug in your board, to get out of safe mode.‎

Flash Resetting UF2‎

If your board ever gets into a really weird state and CIRCUITPY ‎doesn't show up as a disk drive after installing CircuitPython, try ‎loading this 'nuke' UF2 to RPI-RP2. which will do a 'deep clean' on ‎your Flash Memory. You will lose all the files on the board, but at ‎least you'll be able to revive it! After loading this UF2, follow the steps ‎above to re-install CircuitPython.‎

Download flash erasing "nuke" UF2‎

Code the Turntable

Once you've finished setting up your KB2040 with CircuitPython, you ‎can access the code and necessary libraries by downloading the ‎Project Bundle.‎

To do this, click on the Download Project Bundle button in the ‎window below. It will download to your computer as a zipped folder.

Download Project Bundle

Copy Code
# SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT

import asyncio
import board
import displayio
import i2cdisplaybus
import adafruit_imageload
from digitalio import DigitalInOut, Direction
from adafruit_seesaw import seesaw, rotaryio, digitalio
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text import label
import adafruit_displayio_ssd1306

# rotary encoder
i2c = board.STEMMA_I2C()
seesaw = seesaw.Seesaw(i2c, addr=0x36)
encoder = rotaryio.IncrementalEncoder(seesaw)
pos = -encoder.position
last_pos = pos
seesaw.pin_mode(24, seesaw.INPUT_PULLUP)
button = digitalio.DigitalIO(seesaw, 24)
button_state = False

#display setup
displayio.release_displays()

# oled
oled_reset = board.D9
display_bus = i2cdisplaybus.I2CDisplayBus(i2c, device_address=0x3D, reset=oled_reset)
WIDTH = 128
HEIGHT = 64
display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=WIDTH, height=HEIGHT)
# icon sprite sheet
bitmap, palette = adafruit_imageload.load("/icons.bmp",
                                          bitmap=displayio.Bitmap,
                                          palette=displayio.Palette)

icons_grid = displayio.TileGrid(bitmap, pixel_shader=palette,
                                 tile_height=38, tile_width=38,
                                 x=int((display.width / 2)-(38/2)), y=display.height-38)
# text at top of screen
font = bitmap_font.load_font('/Arial-14.bdf')
main_area = label.Label(
    font, text=" ", color=0xFFFFFF)
main_area.anchor_point = (0.5, 0.0)
main_area.anchored_position = (display.width / 2, 0)

splash = displayio.Group()
splash.append(icons_grid)
splash.append(main_area)
display.root_group = splash

# direction and step pins
DIR = DigitalInOut(board.D5)
DIR.direction = Direction.OUTPUT
STEP = DigitalInOut(board.D6)
STEP.direction = Direction.OUTPUT

# enable pin, default off
EN = DigitalInOut(board.D4)
EN.direction = Direction.OUTPUT
EN.value = True

# stepper pins, default 32
MS2 = DigitalInOut(board.D3)
MS2.direction = Direction.OUTPUT
MS2.value = True
MS1 = DigitalInOut(board.D2)
MS1.direction = Direction.OUTPUT
MS1.value = False

# speed dictionaries
speed1 = {
    "micro" : 16,
    "ms1" : True,
    "ms2" : True,
    "icon" : 2,
    "label" : "FAST"
  }
speed2 = {
    "micro" : 32,
    "ms1" : True,
    "ms2" : False,
    "icon" : 1,
    "label" : "MEDIUM"
  }
speed3 = {
    "micro" : 64,
    "ms1" : False,
    "ms2" : True,
    "icon" : 0,
    "label" : "SLOW"
  }
speeds = [speed3, speed2, speed1]

def change_speed(speed):
    MS1.value = speeds[speed]["ms1"]
    MS2.value = speeds[speed]["ms2"]
    icons_grid[0] = speeds[speed]["icon"]
    main_area.text = speeds[speed]["label"]

# enable dictionaries
on = {
    "en" : False,
    "icon" : 6,
    "label" : "ON"
  }

off = {
    "en" : True,
    "icon" : 5,
    "label" : "OFF"
  }

onDict = [on, off]

def onOff(go):
    EN.value = onDict[go]["en"]
    icons_grid[0] = onDict[go]["icon"]
    main_area.text = onDict[go]["label"]

# direction dictionaries
clock = {
    "dir" : True,
    "icon" : 3,
    "label" : "CLOCK"
  }
counter = {
    "dir" : False,
    "icon" : 4,
    "label" : "COUNTER"
  }

directions = [clock, counter]

def changeDirection(direct):
    DIR.value = directions[direct]["dir"]
    icons_grid[0] = directions[direct]["icon"]
    main_area.text = directions[direct]["label"]

# menu states
states = ["SPEED", "ON/OFF", "DIRECTION", "scroll"]
state_icons = [2, 6, 3]

class GUI_Attributes:
    def __init__(self):
        self.menu = 0
        self.index = 0
        self.state = states[3]
        self.current_speed = 1

async def step():
    while True:
        if EN.value is False:
            STEP.value = not STEP.value
        await asyncio.sleep(0)

async def read_encoder(p, last_p, choice):
    while True:
        p = encoder.position
        if p != last_p:
            if p > last_p:
                if choice.state is states[0]:
                    choice.index = (choice.index + 1) % 3
                    change_speed(choice.index)
                    choice.current_speed = choice.index
                if choice.state is states[1]:
                    choice.index = (choice.index + 1) % 2
                    onOff(choice.index)
                if choice.state is states[2]:
                    choice.index = (choice.index + 1) % 2
                    changeDirection(choice.index)
                if choice.state is states[3]:
                    choice.menu = (choice.menu + 1) % 3
                    main_area.text = states[choice.menu]
                    icons_grid[0] = state_icons[choice.menu]
            else:
                if choice.state is states[0]:
                    choice.index = (choice.index - 1) % 3
                    change_speed(choice.index)
                    choice.current_speed = choice.index
                if choice.state is states[1]:
                    choice.index = (choice.index - 1) % 2
                    onOff(choice.index)
                if choice.state is states[2]:
                    choice.index = (choice.index - 1) % 2
                    changeDirection(choice.index)
                if choice.state is states[3]:
                    choice.menu = (choice.menu - 1) % 3
                    main_area.text = states[choice.menu]
                    icons_grid[0] = state_icons[choice.menu]
            print(choice.menu)
            last_p = p
        await asyncio.sleep(0.1)

async def read_button(choice, b_state):
    while True:
        if not button.value and not b_state:
            if choice.state == states[3]:
                choice.state = states[choice.menu]
                if choice.state == states[0]:
                    choice.index = choice.current_speed
                    change_speed(choice.index)
                if choice.state == states[1]:
                    choice.index = EN.value
                    onOff(choice.index)
                if choice.state == states[2]:
                    choice.index = DIR.value
                    changeDirection(choice.index)
            else:
                choice.state = states[3]
                main_area.text = states[choice.menu]
            b_state = True
        if button.value and b_state:
            b_state = False
        await asyncio.sleep(0.1)

async def main():
    choice = GUI_Attributes()
    step_task = asyncio.create_task(step())
    enc_task = asyncio.create_task(read_encoder(pos, last_pos, choice))
    button_task = asyncio.create_task(read_button(choice, button_state))
    main_area.text = states[choice.menu]
    icons_grid[0] = state_icons[choice.menu]
    await asyncio.gather(step_task, enc_task, button_task)

asyncio.run(main())

View on GitHub

Upload the Code, Assets and Libraries to the KB2040‎

After downloading the Project Bundle, plug your KB2040 into the ‎computer's USB port with a known good USB data+power cable. ‎You should see a new flash drive appear in the computer's File ‎Explorer or Finder (depending on your operating system) ‎called CIRCUITPY. Unzip the folder and copy the following items to ‎the KB2040's CIRCUITPY drive.‎

  • lib folder

  • code.py

  • Arial-14.bdf‎

  • icons.bmp

Your KB2040 CIRCUITPY drive should look like this after copying ‎the lib folder, font file, bitmap file and the code.py file.‎

folder_11

How the CircuitPython Code Works

The code begins by instantiating the rotary encoder and button over ‎I2C with the adafruit_seesaw library.‎

Download File

Copy Code
# rotary encoder
i2c = board.STEMMA_I2C()
seesaw = seesaw.Seesaw(i2c, addr=0x36)
encoder = rotaryio.IncrementalEncoder(seesaw)
pos = -encoder.position
last_pos = pos
seesaw.pin_mode(24, seesaw.INPUT_PULLUP)
button = digitalio.DigitalIO(seesaw, 24)
button_state = False

Graphics

The OLED is instantiated over I2C. A bitmap sprite sheet is used for ‎the icons, and one bitmap label is used for the text at the top of the ‎display.‎

Download File

Copy Code
#display setup
displayio.release_displays()

# oled
oled_reset = board.D9
display_bus = i2cdisplaybus.I2CDisplayBus(i2c, device_address=0x3D, reset=oled_reset)
WIDTH = 128
HEIGHT = 64
display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=WIDTH, height=HEIGHT)
# icon sprite sheet
bitmap, palette = adafruit_imageload.load("/icons.bmp",
                                          bitmap=displayio.Bitmap,
                                          palette=displayio.Palette)

icons_grid = displayio.TileGrid(bitmap, pixel_shader=palette,
                                 tile_height=38, tile_width=38,
                                 x=int((display.width / 2)-(38/2)), y=display.height-38)
# text at top of screen
font = bitmap_font.load_font('/Arial-14.bdf')
main_area = label.Label(
    font, text=" ", color=0xFFFFFF)
main_area.anchor_point = (0.5, 0.0)
main_area.anchored_position = (display.width / 2, 0)

splash = displayio.Group()
splash.append(icons_grid)
splash.append(main_area)
display.root_group = splash

TMC2209 Pins

The direction, step, enable, MS1 and MS2 pins are connected to ‎output pins on the KB2040. All of them affect how the stepper motor ‎is controlled:‎

  • Direction - direction that the motor rotates

  • Step - toggled to move the motor

  • Enable - enables/disables the motor

  • MS1 and MS2 - address pins that set the step division

Download File

Copy Code
# direction and step pins
DIR = DigitalInOut(board.D5)
DIR.direction = Direction.OUTPUT
STEP = DigitalInOut(board.D6)
STEP.direction = Direction.OUTPUT

# enable pin, default off
EN = DigitalInOut(board.D4)
EN.direction = Direction.OUTPUT
EN.value = True

# stepper pins, default 32
MS2 = DigitalInOut(board.D3)
MS2.direction = Direction.OUTPUT
MS2.value = True
MS1 = DigitalInOut(board.D2)
MS1.direction = Direction.OUTPUT
MS1.value = False

Dictionaries for Menus

Each setting option for the turntable has a few different parameters; ‎usually graphics and pin values. To handle this, dictionaries are used ‎to store all of the settings together. The first set of dictionaries control ‎the speed of the turntable by changing the MS1 and MS2 pin values. ‎A function called change_speed() sets the pin values and updates ‎the graphics on the OLED.‎

Download File

Copy Code
# speed dictionaries
speed1 = {
    "micro" : 16,
    "ms1" : True,
    "ms2" : True,
    "icon" : 2,
    "label" : "FAST"
  }
speed2 = {
    "micro" : 32,
    "ms1" : True,
    "ms2" : False,
    "icon" : 1,
    "label" : "MEDIUM"
  }
speed3 = {
    "micro" : 64,
    "ms1" : False,
    "ms2" : True,
    "icon" : 0,
    "label" : "SLOW"
  }
speeds = [speed3, speed2, speed1]

def change_speed(speed):
    MS1.value = speeds[speed]["ms1"]
    MS2.value = speeds[speed]["ms2"]
    icons_grid[0] = speeds[speed]["icon"]
    main_area.text = speeds[speed]["label"]

‎The same dictionary and function pairing is used for turning the ‎motor on and off and setting the direction of the motor.‎

Download File

Copy Code
# enable dictionaries
on = {
    "en" : False,
    "icon" : 6,
    "label" : "ON"
  }

off = {
    "en" : True,
    "icon" : 5,
    "label" : "OFF"
  }

onDict = [on, off]

def onOff(go):
    EN.value = onDict[go]["en"]
    icons_grid[0] = onDict[go]["icon"]
    main_area.text = onDict[go]["label"]

# direction dictionaries
clock = {
    "dir" : True,
    "icon" : 3,
    "label" : "CLOCK"
  }
counter = {
    "dir" : False,
    "icon" : 4,
    "label" : "COUNTER"
  }

directions = [clock, counter]

def changeDirection(direct):
    DIR.value = directions[direct]["dir"]
    icons_grid[0] = directions[direct]["icon"]
    main_area.text = directions[direct]["label"]

‎There is an array of menu states that tracks whether you are ‎scrolling through the menu options ("scroll") or actively changing a ‎setting.‎

Download File

Copy Code
# menu states
states = ["SPEED", "ON/OFF", "DIRECTION", "scroll"]
state_icons = [2, 6, 3]

asyncio Class

Since this code utilizes a lot of multitasking, with menu scrolling and ‎toggling the step pin to move the motor, asyncio is used. To track ‎states of things across the different tasks in an asyncio program, you ‎need to create a class. The class for this program is ‎called GUI_Attributes and has items for tracking the state of the ‎menu and the current_speed of the turntable.‎

Download File

Copy Code
class GUI_Attributes:
    def __init__(self):
        self.menu = 0
        self.index = 0
        self.state = states[3]
        self.current_speed = 1

asyncio Tasks

Three tasks are used in the code. The first is called step() and it ‎toggles the step pin on the motor driver to move the turntable.‎

Download File

Copy Code
async def step():
    while True:
        if EN.value is False:
            STEP.value = not STEP.value
        await asyncio.sleep(0)

The next function is called read_encoder() and it handles the rotary ‎encoder and scrolling through the various menus. When self.states is ‎set to scroll, the encoder scrolls through the three menu options. ‎When self.states is set to one of the three menus ‎‎(SPEED, ON/OFF or DIRECTION), then the rotary encoder scrolls ‎through the options associated with those menus and runs the ‎function to change the associated pin value and icon on the OLED.‎

Download File

Copy Code
async def read_encoder(p, last_p, choice):
    while True:
        p = encoder.position
        if p != last_p:
            if p > last_p:
                if choice.state is states[0]:
                    choice.index = (choice.index + 1) % 3
                    change_speed(choice.index)
                    choice.current_speed = choice.index
                if choice.state is states[1]:
                    choice.index = (choice.index + 1) % 2
                    onOff(choice.index)
                if choice.state is states[2]:
                    choice.index = (choice.index + 1) % 2
                    changeDirection(choice.index)
                if choice.state is states[3]:
                    choice.menu = (choice.menu + 1) % 3
                    main_area.text = states[choice.menu]
                    icons_grid[0] = state_icons[choice.menu]
            else:
                if choice.state is states[0]:
                    choice.index = (choice.index - 1) % 3
                    change_speed(choice.index)
                    choice.current_speed = choice.index
                if choice.state is states[1]:
                    choice.index = (choice.index - 1) % 2
                    onOff(choice.index)
                if choice.state is states[2]:
                    choice.index = (choice.index - 1) % 2
                    changeDirection(choice.index)
                if choice.state is states[3]:
                    choice.menu = (choice.menu - 1) % 3
                    main_area.text = states[choice.menu]
                    icons_grid[0] = state_icons[choice.menu]
            print(choice.menu)
            last_p = p
        await asyncio.sleep(0.1)

The final task is read_button(). This task handles the button input on ‎the rotary encoder and changes the value of self.state from ‎the GUI_Attributes class. If the value of self.state is scroll and the ‎button is pressed, then you will enter the menu for the selected ‎option (SPEED, ON/OFF or DIRECTION). If self.state is one of these ‎selected menus and the button is pressed, then self.state is set back ‎to scroll, and you are able to scroll through the menu options without ‎changing any settings.‎

Download File

Copy Code
async def read_button(choice, b_state):
    while True:
        if not button.value and not b_state:
            if choice.state == states[3]:
                choice.state = states[choice.menu]
                if choice.state == states[0]:
                    choice.index = choice.current_speed
                    change_speed(choice.index)
                if choice.state == states[1]:
                    choice.index = EN.value
                    onOff(choice.index)
                if choice.state == states[2]:
                    choice.index = DIR.value
                    changeDirection(choice.index)
            else:
                choice.state = states[3]
                main_area.text = states[choice.menu]
            b_state = True
        if button.value and b_state:
            b_state = False
        await asyncio.sleep(0.1)

‎asyncio main()

The class and tasks are packed into an asyncio program ‎called main(). Each task is instantiated with ‎the create_task() function and then put into the program with ‎the gather() function. main() runs as a loop with the run() function.‎

Download File

Copy Code
async def main():
    choice = GUI_Attributes()
    step_task = asyncio.create_task(step())
    enc_task = asyncio.create_task(read_encoder(pos, last_pos, choice))
    button_task = asyncio.create_task(read_button(choice, button_state))
    main_area.text = states[choice.menu]
    icons_grid[0] = state_icons[choice.menu]
    await asyncio.gather(step_task, enc_task, button_task)

asyncio.run(main())

If you want to learn more about asyncio in CircuitPython check out ‎the Learn Guide.‎

Cooperative Multitasking in CircuitPython with asyncio

By Dan Halbert

Overview

View Guide

guide_12

Lazy Susan

SNUDDA Disassembly

Place the turntable with the bottom base facing up and locate the ‎hex nut in the center.‎

susan_13

Remove Base

Use a 13mm metric socket to unfasten the hex nut. Remove the ‎wooden base to revel the swivel plate underneath. ‎

remove_14

remove_15

Remove Swivel Plate

Use a Phillips head screwdriver to unfasten the three-wood screw ‎from the swivel plate.‎

Move the swivel plate and center bolt off the tabletop and save them.‎

The swivel plate, hex nut, bolt, and three wood screws will be ‎repurposed later in the build.‎

swivel_16

swivel_17

Wiring

Shorten Stepper Wires

Use wire cutters to reduce the length of the four wires from the ‎stepper motor to be 7in (18cm) in length.‎

Use wire strippers to remove a bit of insulation from each of the ‎wires. Then, apply a bit of solder to tin the tips of the exposed strands ‎of wire.‎

stepper_19

Current Limit on TMC2209‎

Use a small flat head screwdriver to adjust the potentiometer on the ‎TMC2209 stepper driver.‎

Rotate the potentiometer counterclockwise so the position matches ‎the photo.‎

This will limit current to the stepper motor which will reduce the ‎overall heat of the motor while it's operating.‎

current_20

Wires for TMC2209‎

Use the 26AWG wires to create seven different connections for the ‎TMC2209 stepper driver. Use different colored wires and make them ‎approximently 4.5in (11.5cm) in length.‎

Use wire strippers to remove a bit of insulation from both ends of ‎each wire. Then, apply a bit of solder to tin the tips of the exposed ‎strands of wire.‎

wires_21

Connect Wires to TMC2209‎

Solder the wires to the following pins on the stepper motor driver.‎

  • VDD

  • GND

  • DIR

  • STEP

  • MS1‎

  • MS2‎

  • EN

connect_22

Connect Wires to KB2040‎

Solder the wires from the TMC2209 driver to the following pins on ‎the KB2040 dev board.‎

  • VDD to 3V

  • GND to G

  • DIR to 5‎

  • STEP to 6‎

  • MS1 to 2‎

  • MS2 to 3‎

  • EN to 4‎

connect_23

Wires for MPM3601 and DC Jack

Prep six more wires for the DC barrel jack and MPM3601 buck ‎converter. These can be 4.5in (11.5cm) in length.‎

Use wire strippers to remove a bit of insulation from both ends of ‎each wire. Then, apply a bit of solder to tin the tips of the exposed ‎strands of wire.‎

jack_24

Solder Wires to MPM3601‎

Solder the wires to the following pins on the MPM3601 buck ‎converter.‎

  • GND

  • ‎5V‎

  • VIN

solder_25

Solder MPM3601 to KB2040‎

Solder the following wire connections to the KB2040 dev board.‎

  • GND to GND

  • ‎5V to RAW‎

solder_26

Mount DC Jack

Secure the DC jack to the 3D printed mount before making any ‎wired connections.‎

Use the included hex nut to secure the DC jack to the 3D printed ‎mount.‎

jack_27

jack_28

Solder Wires to DC Jack

Solder two black wires to the ground terminal (pin on the far side) on ‎the DC jack.‎

Solder two of the red wires to the voltage terminal (the large pin in ‎the center) on the DC Jack.‎

wirestodc_29

wirestodc_30

Solder DC Jack to KB2040‎

Solder the following wires on the DC jack to the KB2040 dev board.‎

  • GND to GND

The remaining wires on the DC jack will be connected to the ‎TMC2209 stepper motor driver.‎

driver_31

Wire Checks

Double check all of the wires have been connected to the ‎appropriate pins. ‎

checks_32

Assembly

Secure TMC2209‎

Use M2.5 x 6mm long machine screws to secure the TMC2209 to the ‎‎3D printed PCB mount.‎

Place the TMC2209 over the four standoffs on the 3D printed PCB ‎mount with the mounting holes lined up.‎

Insert and fasten two M2.5 x 6mm long to secure the TMC2209 to the ‎‎3D printed PCB mount.‎

secure_33

secure_34

Secure MPM3601‎

Place the MPM3601 over the remaining standoff on the 3D printed ‎PCB mount with the mounting hole lined up.‎

Insert and fasten a single M2.5 x 6mm long machine screw to secure ‎the MPM3601 buck converter to the 3D printed PCB mount.‎

standoff_35

standoff_36

Secure KB2040‎

Orient the KB2040 dev board with the 3D printed KB2040 mount.‎

Insert the KB2040 dev board into the 3D printed KB2040 mount so ‎the edges of the PCB are fitted underneath the corner clips.‎

insert_37

insert_38

Mounted Components

Double check the KB2040, TMC2209, MPM3601 and DC jack have ‎been properly secured to their 3D printed mounts.‎

mounted_39

Stepper Mount

Orient the stepper motor mount with the 3D printed motor mount.‎

Use 4x M3 x 6mm long machine screws to secure the stepper motor ‎to the 3D printed motor mount.‎

stepper_40

stepper_41

stepper_42

Connect Stepper Motor and DC Jack

Use the screw-block terminals on the TMC2209 driver to secure the ‎four wires from the stepper motor and the two wires from the DC ‎jack.‎

  • Blue Wire (B-) - 2B‎

  • Red Wire (B+) - 2A‎

  • Black Wire (A+) - 1A‎

  • Green Wire (A-) - 1B‎

connect_43

Connect STEMMA QT Cables

Use the short STEMMA QT cable to connect the 1.3in OLED to the ‎rotary encoder.‎

Connect the long STEMMA QT cable to the other end of the 1.3in ‎OLED.‎

cables_44

Connect STEMMA QT to KB2040

Connect the long STEMMA QT cable to the STEMMA QT port on ‎KB2040 dev board.‎

long_45

Circuit Power Test

Connect the adjustable power supply to a wall outlet with the ‎output NOT plugged into the circuit. Turn the voltage knob to set it ‎to 12V. ‎

Connect the DC plug from the power supply to the DC jack ‎connected to the circuit.‎

The OLED should display text and icons. Use the rotary encoder to ‎turn on the stepper motor and test the various settings.‎

Unplug the DC plug when testing is complete.‎

circuit_46

Install Gear to Stepper Motor

Align the 3D printed gear with the shaft of the stepper motor. The ‎flat surface of the gear should be facing up.‎

Press the gear into the shaft of the stepper motor so it matches the ‎assembly photo.‎

gear_47

gear_48

Controller Case

Use 4x M2.5 x 10mm long machine screws and hex nuts to secure ‎the OLED to the 3D printed controller case.‎

Line up the OLED with the mounting holes on the 3D printed ‎controller case.‎

controller_49

controller_50

Secure the OLED Display to the Controller Case

Place the OLED into the controller case with the mount holes lined ‎up.‎

Insert and fasten the four M2.5 x 10mm long screws through the ‎controller case and OLED mounting tabs.‎

Use the M2.5 hex nuts to secure the OLED to the 3D printed ‎controller case.‎

case_51

case_52

case_53

Secure Rotary Encoder to Controller Case

Insert the knob of the rotary encoder through the hole from the ‎inside of the 3D printed controller case.‎

Use the included hex nut to secure the rotary encoder to the 3D ‎printed controller case.‎

rotary_54

rotary_55

Secure Frame to Bottom Plate

Orient the 3D printed case frame with the bottom plate.‎

Place the case frame over the bottom plate with the four mounting ‎tabs lined up.‎

Use four M3 x 6mm long machine screws to secure the two parts ‎together.‎

Add the rubber feet to corners of the 3D printed bottom plate.‎

frame_56

frame_57

frame_58

Attach Controller Case to Bottom Plate

Use two M3 x 8mm long machine screws and hex nuts to secure the ‎‎3D printed controller case to the bottom plate.‎

plate_59

Secure Controller Case to Bottom Plate

Slide the 3D printed controller case into the designated spot on the ‎bottom plate with the two mounting holes lined up.‎

Insert and fasten the M3 x 8mm long machine screws through the ‎two parts. Use two M3 hex nuts to secure the two parts together.‎

screws_60

screws_61

screws_62

Secure Motor Mount to Bottom Plate

Place the 3D printed motor mount into the bottom plate with the ‎designated mounting holes lined up.‎

Insert and fasten four M3 x 6mm long machine screws through the ‎motor mount and bottom plate.‎

fasten_63

fasten_64

fasten_65

Secure KB2040 Mount to Bottom Plate

Place the 3D printed KB2040 mount onto the designated mounting ‎holes on the bottom plate.‎

Insert two M3 x 10mm long machine screw through the KB2040 ‎mounting tabs and bottom plate. Use two hex nuts to secure the ‎parts together.‎

tabs_66

tabs_67

tabs_68

Secure DC Mount to Bottom Plate

Place the 3D printed DC mount onto the designated mounting holes ‎on the bottom plate.‎

Insert two M3 x 8mm long machine secures through the DC ‎mounting holes and bottom plate.‎

bottom_69

bottom_70

bottom_71

Secure PCB Mount to Bottom Plate

Place the 3D printed PCB mount onto the designated mounting ‎holes on the bottom plate.‎

Insert two M3 x 6mm long machine secures through the PCB mount ‎and the bottom plate.‎

pcb_72

pcb_73

Connect STEMMA QT to KB2040‎

Double check the STEMMA QT cable is connecting the OLED to the ‎KB2040 dev board.‎

stemma_74

Power Test

Insert the DC plug from the power supply to the DC jack. Test the ‎circuit using the rotary encoder knob to adjust the various settings to ‎turn on the stepper motor.‎

The green LEDs should light up on the 1.3in OLED and rotary ‎encoder.‎

power_75

power_76

Lazy Susan Swivel Plate

You have the option to use the wooden tabletop, or the 3D printed ‎tabletop in your build.‎

Get the Lazy Susan swivel plate ready to install onto your preferred ‎tabletop.‎

lazy_77

lazy_78

IKEA Top

Use the instructions on this page if you would like to use the Ikea ‎wooden lazy susan top. If you would like to use the 3D printed top, ‎skip to the next page.‎

Install Gear & Susan

Place the 3D printed internal gear over the center of the wooden ‎tabletop with the three mounting holes lined up.‎

Place the Lazy Susan swivel plate with the bolt over the 3D printed ‎internal gear with the mounting holes lined up.‎

top_79

top_80

Secure Susan

Insert and fasten the three wood screws through the mounting holes ‎to secure the three parts together.‎

secure_81

Install Susan Mount

Place the Lazy Susan Mount over the swivel plate with the bolt going ‎through the center hole.‎

Insert the hex nut onto the thread of the bolt.‎

install_82

install_83

Secure Susan Mount

Use a 13mm metric socket to fasten the nut to the bolt. The Lazy ‎Susan mount should spin freely.‎

socket_84

socket_85

Align Mount and Bottom Plate

Begin joining the Susan Mount to the bottom plate.‎

Orient the Susan mount with the stepper motor so the side with the ‎large opening allows the parts to fit together. ‎

The various wires should pass through the wall openings in the ‎Susan mount. ‎

align_86

Secure Susan Mount to Bottom Plate

Turn over the tabletop so the bottom plate is facing up.‎

Position the bottom plate so the four corresponding mounting holes ‎are lined up with the mounting holes on the Susan mount.‎

Insert and fasten four M3 x 6mm long machine screws to secure the ‎Susan mount to the bottom plate.‎

susan_87

susan_88

Install Knob

Line up the 3D printed knob with the shaft of the rotary encoder.‎

Press the 3D printed knob into the shaft of the rotary encoder.‎

Plug the power supply to the DC jack on the back of the turntable.‎

knob_89

knob_90

Final Build

Congratulations on your build! Use the rotary encoder to adjust the ‎various settings on the OLED.‎

3DP Top‎

3D Printed Top Assembly‎

Orient the 3D printed tabletop with the three standoffs facing up.‎

Place the internal gear over the tabletop with the three mounting ‎holes lined up.‎

assembly_91

assembly_92

Secure Lazy Susan

Place the Lazy Susan swivel plate over the internal gear with the ‎mounting holes lined up.‎

Use three M5 x 6mm long machine screws to secure the swivel plate ‎to the internal gear and tabletop.‎

secure_93

secure_94

secure_95

Secure Lazy Susan Mount

Place the Susan mount over the swivel plate with the bolt going ‎through the center hole.‎

Use a 13mm metric socket to secure the nut to the bolt.‎

The Susan mount is spin freely.‎

spin_96

spin_97

spin_98

Align Susan Mount and Bottom Plate

Begin joining the Susan Mount to the bottom plate.‎

Orient the Susan mount with the stepper motor so the side with the ‎large opening allows the parts to fit together. ‎

The various wires should pass through the wall openings in the ‎Susan mount. ‎

join_99

Secure Susan Mount to Bottom Plate

Turn over the tabletop so the bottom plate is facing up.‎

Position the bottom plate so the four corresponding mounting holes ‎are lined up with the mounting holes on the Susan mount.‎

Insert and fasten four M3 x 6mm long machine screws to secure the ‎Susan mount to the bottom plate.‎

mount_100

mount_101

Install Knob

Line up the 3D printed knob with the shaft of the rotary encoder.‎

Press the 3D printed knob into the shaft of the rotary encoder.‎

Plug the power supply to the DC jack on the back of the turntable.‎

plug_102

plug_103

Final Build

Congratulations on your build! Use the rotary encoder to adjust the ‎various settings on the OLED.‎

build_104

Usage

Using the Display Menu

Turn the knob to switch menu screens between On/Off, Direction, ‎and Speed.‎

Press knob to select a menu item.‎

Turn the knob to select a setting.‎

Press the knob to enable the on-screen setting and return to the ‎main menu.‎

3d_printing_demo-menu

Adjust Motor Speed

Press the knob to select the speed setting. Turn the knob to change ‎speed, choose from three options, slow, medium, and fast.‎

Press know to select the on-screen speed setting.‎

3d_printing_demo-speed

Mfr Part # 6121
ADAFRUIT TMC2209 STEPPER MOTOR D
Adafruit Industries LLC
Mfr Part # 5302
KB2040 RP2040 KEE BOAR DRIVER
Adafruit Industries LLC
Mfr Part # 4739
MPM3610 5V BUCK CONVERTER BREAKO
Adafruit Industries LLC
Mfr Part # 938
GRAPHIC DISPLAY OLED WHITE 1.3"
Adafruit Industries LLC
$163.81
View More Details
Mfr Part # 5880
ADAFRUIT I2C STEMMA QT ROTARY EN
Adafruit Industries LLC
Mfr Part # 5130
AC/DC DESKTOP ADAPTER 3-24V
Adafruit Industries LLC
$143.69
View More Details
Mfr Part # 4399
STEMMA QWIIC JST SH CABLE 50MM
Adafruit Industries LLC
Mfr Part # 2513
HOOK-UP 26AWG 600V RED 25'
Adafruit Industries LLC
Mfr Part # 2514
HOOK-UP 26AWG 600V BLUE 25'
Adafruit Industries LLC
Mfr Part # 2516
HOOK-UP 26AWG 600V GREEN 25'
Adafruit Industries LLC
Mfr Part # 2517
HOOK-UP 26AWG 600V BLACK 25'
Adafruit Industries LLC
Mfr Part # 2518
HOOK-UP 26AWG 600V WHITE
Adafruit Industries LLC
Mfr Part # 2520
HOOK-UP 26AWG 600V ORANGE 25'
Adafruit Industries LLC
Mfr Part # 3299
BLACK NYLON SCREW AND STAND-OFF
Adafruit Industries LLC
$139.18
View More Details
Add all DigiKey Parts to Cart
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.