<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Eric Park</title>
    <description>The latest articles on DEV Community by Eric Park (@gustabe).</description>
    <link>https://dev.to/gustabe</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3784943%2F9bf701b3-8c68-43a8-b914-67c2860ac739.png</url>
      <title>DEV Community: Eric Park</title>
      <link>https://dev.to/gustabe</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gustabe"/>
    <language>en</language>
    <item>
      <title>Step 6: Moving to an Absolute Position — Let the Code Figure Out the Direction</title>
      <dc:creator>Eric Park</dc:creator>
      <pubDate>Fri, 10 Apr 2026 01:12:54 +0000</pubDate>
      <link>https://dev.to/gustabe/step-6-moving-to-an-absolute-position-let-the-code-figure-out-the-direction-1hd3</link>
      <guid>https://dev.to/gustabe/step-6-moving-to-an-absolute-position-let-the-code-figure-out-the-direction-1hd3</guid>
      <description>&lt;h1&gt;
  
  
  Step 6: Moving to an Absolute Position — Let the Code Figure Out the Direction
&lt;/h1&gt;

&lt;p&gt;Hello everyone. This is Eric Park, 3D Printer Engineer from South Korea.&lt;/p&gt;

&lt;p&gt;Quick recap before we dive in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Step 1&lt;/strong&gt;: Made the motor spin for the first time. 200 pulses, one full revolution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 2&lt;/strong&gt;: Controlled speed by adjusting the delay between pulses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 3&lt;/strong&gt;: Built &lt;code&gt;rotateAngle()&lt;/code&gt; — pass in degrees, the function calculates steps automatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 4&lt;/strong&gt;: Added direction control and started tracking the current angle as a global variable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 5&lt;/strong&gt;: Switched from angles to millimeters. Introduced &lt;code&gt;moveX(distance, direction)&lt;/code&gt; with &lt;code&gt;currentX&lt;/code&gt; tracking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 6 (today)&lt;/strong&gt;: Switch to absolute coordinates. You give a target position, the code decides direction automatically.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Problem with Step 5
&lt;/h2&gt;

&lt;p&gt;In Step 5, I wrote a function called &lt;code&gt;moveX()&lt;/code&gt;. It looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;moveX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="cm"&gt;/* move 5mm in the positive direction */&lt;/span&gt;
&lt;span class="n"&gt;moveX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="cm"&gt;/* move 3mm in the negative direction */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every call required me to specify:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How far to move&lt;/li&gt;
&lt;li&gt;Which direction&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is fine for manual jogging — "go 5mm left," "go 3mm right." But it is not how real motion control systems work.&lt;/p&gt;

&lt;p&gt;A G-code command looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;G1 X50.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It does not say "go 50mm to the right." It says "go to position X=50.0." If the current position is X=30, the controller figures out it needs to move 20mm in the positive direction. If the current position is X=70, it moves 20mm in the negative direction.&lt;/p&gt;

&lt;p&gt;That is what Step 6 is about — absolute coordinates.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Changes
&lt;/h2&gt;

&lt;p&gt;The function signature changes from this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;moveX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;positive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;speedDelay&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;moveToX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;targetX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;speedDelay&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No direction parameter. You give it a destination, it calculates everything.&lt;/p&gt;

&lt;p&gt;Inside the function, two things happen automatically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Calculate the distance: &lt;code&gt;targetX - currentX&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Determine direction from the sign of that result&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the result is positive, move in the positive direction. If negative, move in the negative direction.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/*
 * step6_absolute.c
 *
 * Step 6: Absolute coordinate movement
 *
 * Machine assumptions:
 *   - One full revolution = 10mm of travel
 *   - 200 steps per revolution
 *   - Therefore: 20 steps per mm
 *
 * Compile: gcc -o step6 step6_absolute.c -lwiringPi -lm
 * Run:     sudo ./step6
 */&lt;/span&gt;

&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;wiringPi.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdbool.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;math.h&amp;gt;&lt;/span&gt;&lt;span class="c1"&gt;     /* fabs() */&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cm"&gt;/* ── Pin definitions (BCM GPIO numbering) ── */&lt;/span&gt;
&lt;span class="cp"&gt;#define STEP_PIN    4
#define DIR_PIN     3
#define ENABLE_PIN  2
&lt;/span&gt;
&lt;span class="cm"&gt;/* ── Machine configuration ── */&lt;/span&gt;
&lt;span class="cp"&gt;#define STEPS_PER_REV  200      &lt;/span&gt;&lt;span class="cm"&gt;/* 360 / 1.8 degrees = 200 steps */&lt;/span&gt;&lt;span class="cp"&gt;
#define MM_PER_REV     10.0f   &lt;/span&gt;&lt;span class="cm"&gt;/* depends on your belt/screw setup */&lt;/span&gt;&lt;span class="cp"&gt;
#define STEPS_PER_MM   (STEPS_PER_REV / MM_PER_REV)  &lt;/span&gt;&lt;span class="cm"&gt;/* 20.0 */&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cm"&gt;/* ── Current position (global) ── */&lt;/span&gt;
&lt;span class="cm"&gt;/*
 * This variable tracks where the motor is right now.
 * It starts at 0.0 (we assume power-on = origin).
 * Every moveToX() call updates it when the move completes.
 */&lt;/span&gt;
&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;g_current_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/* ─────────────────────────────────────────────
 * rotateMotor: send step pulses to the driver
 *
 * steps    : how many pulses to send
 * delay_ms : delay between each pulse (controls speed)
 * ───────────────────────────────────────────── */&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;rotateMotor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;delay_ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delay_ms&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delay_ms&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* ─────────────────────────────────────────────
 * moveToX: move to an absolute X position
 *
 * targetX   : destination in mm (absolute)
 * delay_ms  : speed control (smaller = faster)
 *
 * Direction is calculated automatically.
 * ───────────────────────────────────────────── */&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;moveToX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;targetX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;delay_ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="cm"&gt;/* Step 1: calculate how far we need to travel */&lt;/span&gt;
    &lt;span class="cm"&gt;/*
     * distance = targetX - g_current_x
     *
     * Examples:
     *   current=0.0,  target=30.0  → distance = +30.0 (move positive)
     *   current=30.0, target=10.0  → distance = -20.0 (move negative)
     *   current=10.0, target=10.0  → distance =   0.0 (already there)
     */&lt;/span&gt;
    &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;targetX&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;g_current_x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cm"&gt;/* Step 2: if distance is near zero, skip the move */&lt;/span&gt;
    &lt;span class="cm"&gt;/*
     * fabs() returns the absolute value of a float.
     * We use 0.01mm as the threshold — anything smaller
     * is less than 1 step at 20 steps/mm, so there is
     * nothing meaningful to do.
     */&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fabs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Already at target position: X = %.2fmm&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g_current_x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/* Step 3: determine direction from the sign of distance */&lt;/span&gt;
    &lt;span class="cm"&gt;/*
     * distance &amp;gt; 0  →  positive direction (DIR pin HIGH)
     * distance &amp;lt; 0  →  negative direction (DIR pin LOW)
     *
     * We also need the absolute value for the step count.
     * fabs() gives us that without an if-else.
     */&lt;/span&gt;
    &lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;positive&lt;/span&gt;       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;abs_distance&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fabs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"X%.2f -&amp;gt; X%.2f: moving %.2fmm in %s direction&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;g_current_x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;targetX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;abs_distance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;positive&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"+"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/* Set the direction pin before sending pulses */&lt;/span&gt;
    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DIR_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;positive&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/* Step 4: convert mm to steps */&lt;/span&gt;
    &lt;span class="cm"&gt;/*
     * steps = abs_distance × STEPS_PER_MM
     *
     * Example: 15.0mm × 20.0 steps/mm = 300 steps
     *
     * (int) cast truncates the decimal.
     * At 20 steps/mm, the minimum resolution is 0.05mm.
     * Any fractional mm below that is lost here.
     * This is the same rounding limitation we saw in Step 3.
     */&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;abs_distance&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;STEPS_PER_MM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  -&amp;gt; %d steps&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/* Step 5: run the motor */&lt;/span&gt;
    &lt;span class="n"&gt;rotateMotor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delay_ms&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/* Step 6: update the position tracker */&lt;/span&gt;
    &lt;span class="cm"&gt;/*
     * We set g_current_x to targetX (not += distance).
     * Using targetX directly avoids floating point
     * accumulation errors from repeated additions.
     */&lt;/span&gt;
    &lt;span class="n"&gt;g_current_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;targetX&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  -&amp;gt; current position: X = %.2fmm&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g_current_x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* ─────────────────────────────────────────────
 * main: test the absolute coordinate system
 * ───────────────────────────────────────────── */&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wiringPiSetupGpio&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"wiringPi init failed&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DIR_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENABLE_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/* Enable the motor driver (active LOW on most drivers) */&lt;/span&gt;
    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENABLE_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"=== Absolute Coordinate Movement Test ===&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Config: %.1fmm/rev, %.1f steps/mm&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;MM_PER_REV&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;STEPS_PER_MM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Start: X = %.2fmm&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g_current_x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/* Test sequence */&lt;/span&gt;
    &lt;span class="n"&gt;moveToX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="cm"&gt;/* X0   -&amp;gt; X10  (positive direction) */&lt;/span&gt;
    &lt;span class="n"&gt;moveToX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="cm"&gt;/* X10  -&amp;gt; X25  (positive direction) */&lt;/span&gt;
    &lt;span class="n"&gt;moveToX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="cm"&gt;/* X25  -&amp;gt; X5   (negative direction, auto!) */&lt;/span&gt;
    &lt;span class="n"&gt;moveToX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="cm"&gt;/* X5   -&amp;gt; X15.5 */&lt;/span&gt;
    &lt;span class="n"&gt;moveToX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="cm"&gt;/* X15.5 -&amp;gt; X15.5 (already there) */&lt;/span&gt;
    &lt;span class="n"&gt;moveToX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="cm"&gt;/* X15.5 -&amp;gt; X0  (back to origin) */&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"=== Final result ===&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Final position: X = %.2fmm&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g_current_x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/* Disable the motor driver */&lt;/span&gt;
    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENABLE_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Walking Through the Key Part
&lt;/h2&gt;

&lt;p&gt;The function &lt;code&gt;moveToX()&lt;/code&gt; does its most important work in three lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;targetX&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;g_current_x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;positive&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;abs_distance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fabs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let me trace through a concrete example. Suppose &lt;code&gt;g_current_x = 25.0&lt;/code&gt; and we call &lt;code&gt;moveToX(5.0, 2)&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;distance    = 5.0 - 25.0 = -20.0
positive    = (-20.0 &amp;gt; 0.0) = false   → DIR pin goes LOW
abs_distance = fabs(-20.0) = 20.0
steps       = (int)(20.0 * 20.0) = 400
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The direction is set automatically. I never had to think about it — I just said "go to X=5.0."&lt;/p&gt;




&lt;h2&gt;
  
  
  One Detail Worth Explaining: Why &lt;code&gt;g_current_x = targetX&lt;/code&gt; Instead of &lt;code&gt;+= distance&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;At the end of the function, I write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;g_current_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;targetX&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An alternative would be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;g_current_x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both give the same answer mathematically. But with floating point numbers, repeated additions accumulate small rounding errors. If you add and subtract many times, the result drifts slightly from the true value.&lt;/p&gt;

&lt;p&gt;Assigning &lt;code&gt;targetX&lt;/code&gt; directly avoids that drift. Each move lands exactly on the intended coordinate.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the Output Looks Like
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=== Absolute Coordinate Movement Test ===
Config: 10.0mm/rev, 20.0 steps/mm

Start: X = 0.00mm

X0.00 -&amp;gt; X10.00: moving 10.00mm in + direction
  -&amp;gt; 200 steps
  -&amp;gt; current position: X = 10.00mm

X10.00 -&amp;gt; X25.00: moving 15.00mm in + direction
  -&amp;gt; 300 steps
  -&amp;gt; current position: X = 25.00mm

X25.00 -&amp;gt; X5.00: moving 20.00mm in - direction
  -&amp;gt; 400 steps
  -&amp;gt; current position: X = 5.00mm

X5.00 -&amp;gt; X15.50: moving 10.50mm in + direction
  -&amp;gt; 210 steps
  -&amp;gt; current position: X = 15.50mm

Already at target position: X = 15.50mm

X15.50 -&amp;gt; X0.00: moving 15.50mm in - direction
  -&amp;gt; 310 steps
  -&amp;gt; current position: X = 0.00mm

=== Final result ===
Final position: X = 0.00mm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The third move (X25 → X5) goes in the negative direction automatically. No direction argument, no manual calculation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Honest Limitation: The Position Tracker Has No Memory
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;g_current_x&lt;/code&gt; only exists in RAM. If the motor skips a step under load — which stepper motors do occasionally — the tracker has no way to know. It still believes the motor is exactly where the math says it should be.&lt;/p&gt;

&lt;p&gt;Real systems solve this with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An encoder on the motor shaft for feedback&lt;/li&gt;
&lt;li&gt;A homing sequence at startup to establish a known reference point&lt;/li&gt;
&lt;li&gt;Limit switches to detect the physical endpoints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For now, we are assuming the motor never misses a step. That is fine for learning, but it is worth understanding the assumption.&lt;/p&gt;




&lt;h2&gt;
  
  
  Practice Ideas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Call &lt;code&gt;moveToX(0.0f, 2)&lt;/code&gt; twice in a row. Does the second call print "already at target position"?&lt;/li&gt;
&lt;li&gt;Try &lt;code&gt;moveToX(-10.0f, 2)&lt;/code&gt;. Can the motor go to a negative coordinate? Does &lt;code&gt;g_current_x&lt;/code&gt; correctly show &lt;code&gt;-10.0&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Change &lt;code&gt;MM_PER_REV&lt;/code&gt; to match your actual hardware (GT2 belt + 20-tooth pulley = 40.0mm). Does the motion scale correctly?&lt;/li&gt;
&lt;li&gt;Add a &lt;code&gt;homeX()&lt;/code&gt; function that calls &lt;code&gt;moveToX(0.0f, 2)&lt;/code&gt;. This is the beginning of a real homing function.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What Is Next
&lt;/h2&gt;

&lt;p&gt;Step 7 will add acceleration and deceleration to &lt;code&gt;moveToX()&lt;/code&gt;. Right now the motor starts and stops at full speed, which causes vibration and can lose steps at high speeds.&lt;/p&gt;

&lt;p&gt;With a proper velocity profile — slow start, ramp up, ramp down before stopping — the motion becomes smoother and more reliable. That is also the foundation for understanding how Klipper and Marlin handle motion planning.&lt;/p&gt;

&lt;p&gt;See you next week.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Eric Park, from Daegu, South Korea&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tags: &lt;code&gt;raspberrypi&lt;/code&gt; &lt;code&gt;c&lt;/code&gt; &lt;code&gt;steppermotor&lt;/code&gt; &lt;code&gt;3dprinting&lt;/code&gt; &lt;code&gt;beginners&lt;/code&gt; &lt;code&gt;embeddedsystems&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>3dprinting</category>
      <category>c</category>
    </item>
    <item>
      <title>Step 5: Moving in Millimeters — Position Tracking with Real Units</title>
      <dc:creator>Eric Park</dc:creator>
      <pubDate>Thu, 02 Apr 2026 12:28:58 +0000</pubDate>
      <link>https://dev.to/gustabe/step-5-moving-in-millimeters-position-tracking-with-real-units-5c8e</link>
      <guid>https://dev.to/gustabe/step-5-moving-in-millimeters-position-tracking-with-real-units-5c8e</guid>
      <description>&lt;h1&gt;
  
  
  Step 5: Moving in Millimeters — Position Tracking with Real Units
&lt;/h1&gt;

&lt;p&gt;Hello everyone. This is Eric Park, 3D Printer Engineer from South Korea.&lt;/p&gt;

&lt;p&gt;We are now at Step 5. Here is a quick recap of where we have been:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Step 1: Sent 200 pulses, made the motor complete one full revolution&lt;/li&gt;
&lt;li&gt;Step 2: Controlled speed by adjusting the delay between pulses&lt;/li&gt;
&lt;li&gt;Step 3: Rotated the motor by a specific angle using a formula&lt;/li&gt;
&lt;li&gt;Step 4: Added direction control and started tracking angle as a global variable&lt;/li&gt;
&lt;li&gt;Step 5 (today): Forget angles. We move in millimeters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This step felt like a real turning point for me. For the first time, the motor started speaking the same language as a real machine.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Millimeters?
&lt;/h2&gt;

&lt;p&gt;In Steps 3 and 4, I was controlling the motor by angle — 90 degrees, 180 degrees, and so on. That works fine for a rotating shaft.&lt;/p&gt;

&lt;p&gt;But a 3D printer or CNC machine does not think in degrees. It thinks in millimeters. When Klipper receives &lt;code&gt;G1 X50&lt;/code&gt;, it moves the X axis to 50mm. Not 1000 degrees, not 4000 steps — just 50mm.&lt;/p&gt;

&lt;p&gt;So the question is: how do we convert mm to motor steps?&lt;/p&gt;




&lt;h2&gt;
  
  
  The Key Formula
&lt;/h2&gt;

&lt;p&gt;A stepper motor has a mechanical chain between it and the actual moving part. In my test setup, I am assuming a GT2 timing belt with a 20-tooth pulley. Here is how the math works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Pulley teeth × belt pitch = distance per revolution
20 teeth × 2mm = 40mm per revolution
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait — actually in my setup I am using a simpler approximation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 revolution = 10mm movement
200 steps = 10mm
1mm = 200 / 10 = 20 steps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the conversion is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;distance_mm&lt;/span&gt; &lt;span class="err"&gt;×&lt;/span&gt; &lt;span class="n"&gt;steps_per_mm&lt;/span&gt;
&lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;distance_mm&lt;/span&gt; &lt;span class="err"&gt;×&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Move 5mm → 5 × 20 = &lt;strong&gt;100 steps&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Move 3mm → 3 × 20 = &lt;strong&gt;60 steps&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Move 0.1mm → 0.1 × 20 = &lt;strong&gt;2 steps&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is defined once at the top of the code, and everything else uses it automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Changed From Step 4
&lt;/h2&gt;

&lt;p&gt;In Step 4, the function looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;rotateAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clockwise&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// rotate 90 degrees, clockwise, delay=2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Step 5, it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;moveX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// move 5.0mm in positive direction, delay=2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interface is now talking about physical distance, not motor rotation. That feels much more natural when you are thinking about building a machine.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/*
 * step5_position_tracking.c
 * Step 5: Coordinate-based position tracking (mm units)
 *
 * Compile: gcc -o step5 step5_position_tracking.c -lwiringPi -lm
 * Run    : sudo ./step5
 */&lt;/span&gt;

&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;wiringPi.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdbool.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;math.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cm"&gt;/* ── Pin definitions (BCM GPIO numbers) ── */&lt;/span&gt;
&lt;span class="cp"&gt;#define STEP_PIN    4
#define DIR_PIN     3
#define ENABLE_PIN  2
&lt;/span&gt;
&lt;span class="cm"&gt;/* ── Motor and mechanical settings ── */&lt;/span&gt;
&lt;span class="cp"&gt;#define STEPS_PER_REV  200      &lt;/span&gt;&lt;span class="cm"&gt;/* 360 / 1.8 = 200 steps per revolution */&lt;/span&gt;&lt;span class="cp"&gt;
#define MM_PER_REV     10.0f   &lt;/span&gt;&lt;span class="cm"&gt;/* 1 revolution moves 10mm (adjust for your machine) */&lt;/span&gt;&lt;span class="cp"&gt;
#define STEPS_PER_MM   (STEPS_PER_REV / MM_PER_REV)   &lt;/span&gt;&lt;span class="cm"&gt;/* 20 steps/mm */&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cm"&gt;/* ── Current position tracking ── */&lt;/span&gt;
&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;currentX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="cm"&gt;/* unit: mm */&lt;/span&gt;

&lt;span class="cm"&gt;/* Low-level pulse generation */&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;rotateMotor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;delayMs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delayMs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delayMs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/*
 * moveX: move by a relative distance in mm
 *
 * distance_mm : how far to move (always positive)
 * positive    : true = forward (+X), false = backward (-X)
 * speedDelay  : delay between pulses (ms)
 */&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;moveX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;distance_mm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;positive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;speedDelay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="cm"&gt;/* Set direction */&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;positive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DIR_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"X+ direction: +%.2fmm&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;distance_mm&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DIR_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"X- direction: -%.2fmm&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;distance_mm&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/* Convert mm to steps */&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;distance_mm&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;STEPS_PER_MM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  -&amp;gt; %d steps&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/* Execute */&lt;/span&gt;
    &lt;span class="n"&gt;rotateMotor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;speedDelay&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/* Update position */&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;positive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;currentX&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;distance_mm&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;currentX&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;distance_mm&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  -&amp;gt; current position: X = %.2fmm&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;currentX&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wiringPiSetupGpio&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"wiringPi init failed&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DIR_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENABLE_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENABLE_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Motor enabled&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"=== Position tracking test (mm units) ===&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Settings: 1 rev = %.1fmm, 1mm = %.0f steps&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;MM_PER_REV&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;STEPS_PER_MM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Start: X = %.2fmm&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;currentX&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;moveX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="cm"&gt;/* +5mm */&lt;/span&gt;
    &lt;span class="n"&gt;moveX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="cm"&gt;/* +3mm */&lt;/span&gt;
    &lt;span class="n"&gt;moveX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="cm"&gt;/* -2mm */&lt;/span&gt;
    &lt;span class="n"&gt;moveX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="cm"&gt;/* +1.5mm */&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"=== Result ===&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Final position: X = %.2fmm&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;currentX&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Expected      : 5 + 3 - 2 + 1.5 = 7.5mm&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENABLE_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Walking Through the Code
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;STEPS_PER_MM&lt;/code&gt; macro:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#define STEPS_PER_MM   (STEPS_PER_REV / MM_PER_REV)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This calculates itself from the two values above it. If I change &lt;code&gt;MM_PER_REV&lt;/code&gt; to match my actual machine, everything else updates automatically. That is the benefit of defining constants at the top — I only need to change one number.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;currentX&lt;/code&gt; global variable:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;currentX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps track of where the motor is right now. Every time &lt;code&gt;moveX()&lt;/code&gt; runs, it adds or subtracts from this value. This is the same idea as Step 4's &lt;code&gt;currentAngle&lt;/code&gt;, but now in mm.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The conversion inside &lt;code&gt;moveX()&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;distance_mm&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;STEPS_PER_MM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the only place the formula appears. The rest of the code just calls &lt;code&gt;moveX()&lt;/code&gt; with millimeters. Clean separation between "what I want" and "how to achieve it."&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Discovered During Testing
&lt;/h2&gt;

&lt;p&gt;When I tested &lt;code&gt;moveX(0.1f, true, 2)&lt;/code&gt;, the motor moved only 2 steps. That is the smallest possible movement. Anything smaller than &lt;code&gt;1 / STEPS_PER_MM = 0.05mm&lt;/code&gt; rounds down to zero and the motor does not move at all.&lt;/p&gt;

&lt;p&gt;This is not a bug — it is a real physical limitation. One step equals one minimum increment. With 20 steps/mm, the resolution is 0.05mm per step.&lt;/p&gt;

&lt;p&gt;If I need higher resolution, I would switch the driver to microstepping (for example, 1/16 stepping gives 320 steps/mm, or 0.003mm resolution). But for now, 0.05mm is more than enough for learning.&lt;/p&gt;




&lt;h2&gt;
  
  
  Adjusting for Your Machine
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;MM_PER_REV&lt;/code&gt; value depends entirely on your mechanical setup. Here are common examples:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setup&lt;/th&gt;
&lt;th&gt;MM_PER_REV&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GT2 belt + 20-tooth pulley&lt;/td&gt;
&lt;td&gt;40.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GT2 belt + 16-tooth pulley&lt;/td&gt;
&lt;td&gt;32.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lead screw, 2mm pitch&lt;/td&gt;
&lt;td&gt;2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lead screw, 8mm pitch&lt;/td&gt;
&lt;td&gt;8.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The easiest way to find your actual value: move the motor exactly 200 steps (one revolution) and measure how far the carriage moved with a ruler. That measurement is your &lt;code&gt;MM_PER_REV&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Practice Ideas
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Change &lt;code&gt;MM_PER_REV&lt;/code&gt; to match your actual machine. Measure 1 revolution and set the correct value.&lt;/li&gt;
&lt;li&gt;Try &lt;code&gt;moveX(0.1f, true, 2)&lt;/code&gt; — what is the smallest movement you can actually see?&lt;/li&gt;
&lt;li&gt;Add a &lt;code&gt;moveY()&lt;/code&gt; function for a second axis with different &lt;code&gt;steps_per_mm&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Try calling &lt;code&gt;moveX()&lt;/code&gt; with &lt;code&gt;distance_mm = 0&lt;/code&gt;. What happens?&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;In Step 5, direction is still manual — I have to type &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt; to choose which way to go.&lt;/p&gt;

&lt;p&gt;In Step 6, I will change the interface to &lt;code&gt;moveToX(target_mm)&lt;/code&gt;. You give it a destination, and the code figures out direction automatically. That brings us one step closer to how G-code actually works.&lt;/p&gt;

&lt;p&gt;See you next time.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Eric Park, from Daegu, South Korea&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tags: &lt;code&gt;raspberrypi&lt;/code&gt; &lt;code&gt;c&lt;/code&gt; &lt;code&gt;steppermotor&lt;/code&gt; &lt;code&gt;3dprinting&lt;/code&gt; &lt;code&gt;beginners&lt;/code&gt; &lt;code&gt;embeddedsystems&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>3dprinting</category>
      <category>c</category>
    </item>
    <item>
      <title>Step 2: Controlling the Speed of a Stepper Motor</title>
      <dc:creator>Eric Park</dc:creator>
      <pubDate>Thu, 26 Mar 2026 12:01:47 +0000</pubDate>
      <link>https://dev.to/gustabe/step-2-controlling-the-speed-of-a-stepper-motor-1pe</link>
      <guid>https://dev.to/gustabe/step-2-controlling-the-speed-of-a-stepper-motor-1pe</guid>
      <description>&lt;h1&gt;
  
  
  Step 2: Controlling the Speed of a Stepper Motor
&lt;/h1&gt;

&lt;p&gt;Hello everyone. This is Eric Park, 3D Printer Engineer from South Korea.&lt;/p&gt;

&lt;p&gt;Welcome back to the series. Quick recap from Step 1:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We set up the GPIO pins on the Raspberry Pi&lt;/li&gt;
&lt;li&gt;We connected the A4988 driver to a NEMA17 motor&lt;/li&gt;
&lt;li&gt;We sent 200 pulses to the motor and watched it complete one full revolution for the first time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That first spin felt surprisingly satisfying. But the motor was just running at one fixed speed. Step 2 is about changing that.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Controls Speed?
&lt;/h2&gt;

&lt;p&gt;In Step 1, the code looked something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;delay(2)&lt;/code&gt; is a 2 millisecond pause after each pulse. The motor waits for that pause before receiving the next pulse.&lt;/p&gt;

&lt;p&gt;So the relationship is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;shorter delay  =  less waiting  =  faster motor
longer delay   =  more waiting  =  slower motor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the entire idea behind Step 2. We are not changing anything complicated — just the number we pass to &lt;code&gt;delay()&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Calculating Speed
&lt;/h2&gt;

&lt;p&gt;Let me put a number to this.&lt;/p&gt;

&lt;p&gt;Each full revolution requires 200 steps. If each step takes 2ms (HIGH) + 2ms (LOW) = 4ms total, then one revolution takes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;200 steps × 4ms = 800ms = 0.8 seconds per revolution
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I change the delay to 1ms:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;200 steps × 2ms = 400ms = 0.4 seconds per revolution
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Twice as fast. If I change the delay to 4ms:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;200 steps × 8ms = 1600ms = 1.6 seconds per revolution
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Half as fast. The delay value directly controls how long each revolution takes.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Practical Limit: Too Fast = Missed Steps
&lt;/h2&gt;

&lt;p&gt;I was curious how fast I could push the motor, so I tried very small delays — 0.5ms, then 0.1ms.&lt;/p&gt;

&lt;p&gt;The motor started making a grinding noise and vibrating in place instead of rotating. This is called &lt;strong&gt;step loss&lt;/strong&gt; (or missing steps). The motor's internal magnets need a minimum amount of time to physically move between positions. If the pulses arrive faster than the motor can mechanically respond, it misses them.&lt;/p&gt;

&lt;p&gt;For a typical NEMA17 at 12V with an A4988 driver, around 1ms delay is usually the practical minimum before step loss starts. The exact limit depends on the motor's current setting and the load it is carrying.&lt;/p&gt;

&lt;p&gt;This was an interesting thing to discover just by experimenting. No special equipment needed — the sound alone tells you when the motor is being pushed too hard.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;wiringPi.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cp"&gt;#define STEP_PIN    4
#define DIR_PIN     3
#define ENABLE_PIN  2
&lt;/span&gt;
&lt;span class="cp"&gt;#define STEPS_PER_REV  200
&lt;/span&gt;
&lt;span class="cm"&gt;/*
 * rotateMotor: spin the motor a given number of steps at a given speed
 *
 * steps    : how many steps to execute
 * delay_ms : delay between pulses in milliseconds
 *            smaller = faster, larger = slower
 */&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;rotateMotor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;delay_ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delay_ms&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delay_ms&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wiringPiSetupGpio&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"wiringPi init failed&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DIR_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENABLE_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/* Motor ON (ENABLE is active LOW on A4988) */&lt;/span&gt;
    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENABLE_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Step 2: Speed Control ---&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/* Slow */&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Slow (delay = 5ms)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;rotateMotor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEPS_PER_REV&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/* Medium */&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Medium (delay = 2ms)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;rotateMotor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEPS_PER_REV&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/* Fast */&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Fast (delay = 1ms)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;rotateMotor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEPS_PER_REV&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/* Motor OFF */&lt;/span&gt;
    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENABLE_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Done.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Walking Through the Code
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The &lt;code&gt;delay_ms&lt;/code&gt; parameter:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In Step 1, the delay was a hardcoded number inside the loop. Here I moved it into a function parameter. Now I can call &lt;code&gt;rotateMotor(200, 5)&lt;/code&gt; for slow or &lt;code&gt;rotateMotor(200, 1)&lt;/code&gt; for fast, without changing anything inside the function.&lt;/p&gt;

&lt;p&gt;This is a small change, but it is an important habit. When a value is likely to change, give it a name or make it a parameter. Hardcoded numbers buried inside loops are difficult to find and adjust later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;#define STEPS_PER_REV 200&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Same idea. The number 200 comes from the motor spec (1.8 degree step angle → 360 / 1.8 = 200). If I ever switch to a different motor, I only need to update that one line.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The timing per step:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Each step takes &lt;code&gt;delay_ms × 2&lt;/code&gt; milliseconds total — one delay after HIGH, one after LOW. So &lt;code&gt;delay_ms = 2&lt;/code&gt; means 4ms per step, not 2ms. I made this mistake when I first calculated the revolution time and got a number that did not match what I measured with a stopwatch. Worth keeping in mind.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Noticed During Testing
&lt;/h2&gt;

&lt;p&gt;Running the three speeds back to back — slow, medium, fast — makes the difference very obvious. The motor sound changes noticeably. At 5ms it sounds deliberate and measured. At 1ms it has a higher pitch and feels more tense.&lt;/p&gt;

&lt;p&gt;I also tried going below 1ms by calling &lt;code&gt;delay(0)&lt;/code&gt;. The motor vibrated and did not complete the revolution at all. So there is a real physical floor here, not just a software one.&lt;/p&gt;

&lt;p&gt;One more thing: between the three test runs, there is a &lt;code&gt;delay(1000)&lt;/code&gt; pause. Without this, the transitions happen so fast that it is hard to tell where one speed ends and the next begins. Small details like this matter when you are trying to actually observe what the code is doing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Practice Ideas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Change the three delay values and find the minimum before your motor starts losing steps.&lt;/li&gt;
&lt;li&gt;Try calling &lt;code&gt;rotateMotor(STEPS_PER_REV, 3)&lt;/code&gt; and time one revolution with a stopwatch. Does the calculated time (200 × 3ms × 2 = 1200ms) match what you measure?&lt;/li&gt;
&lt;li&gt;Add a fourth test run between slow and medium — something like &lt;code&gt;delay_ms = 3&lt;/code&gt;. Can you hear the difference?&lt;/li&gt;
&lt;li&gt;Try &lt;code&gt;STEPS_PER_REV / 2&lt;/code&gt; steps (half a revolution) at different speeds. Does the motor stop at exactly 180 degrees?&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What Is Next
&lt;/h2&gt;

&lt;p&gt;Step 3 adds angle control. Right now I have to manually decide how many steps to use. In Step 3, I will build a function where I enter the angle I want — 90 degrees, 180 degrees, whatever — and the code calculates the steps automatically.&lt;/p&gt;

&lt;p&gt;See you next time.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Eric Park, from Daegu, South Korea&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tags: &lt;code&gt;raspberrypi&lt;/code&gt; &lt;code&gt;c&lt;/code&gt; &lt;code&gt;steppermotor&lt;/code&gt; &lt;code&gt;3dprinting&lt;/code&gt; &lt;code&gt;beginners&lt;/code&gt; &lt;code&gt;embeddedsystems&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>c</category>
      <category>iot</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Step 4: Controlling Direction — and Starting to Track Position</title>
      <dc:creator>Eric Park</dc:creator>
      <pubDate>Thu, 26 Mar 2026 11:59:10 +0000</pubDate>
      <link>https://dev.to/gustabe/step-4-controlling-direction-and-starting-to-track-position-1j88</link>
      <guid>https://dev.to/gustabe/step-4-controlling-direction-and-starting-to-track-position-1j88</guid>
      <description>&lt;h1&gt;
  
  
  Step 4: Controlling Direction — and Starting to Track Position
&lt;/h1&gt;

&lt;p&gt;Hello everyone. This is Eric Park, 3D Printer Engineer from South Korea.&lt;/p&gt;

&lt;p&gt;Quick recap before we start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Step 1&lt;/strong&gt;: Made the motor spin for the first time — 200 pulses, one full revolution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 2&lt;/strong&gt;: Controlled speed by changing the delay between pulses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 3&lt;/strong&gt;: Built a &lt;code&gt;rotateAngle()&lt;/code&gt; function — enter an angle, the code calculates the steps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 4 (today)&lt;/strong&gt;: The motor can now rotate both clockwise and counterclockwise. And we start tracking the current angle.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What Was Still Missing After Step 3
&lt;/h2&gt;

&lt;p&gt;After Step 3, I had a working &lt;code&gt;rotateAngle()&lt;/code&gt; function. But it always rotated in the same direction. My motor was stuck going one way.&lt;/p&gt;

&lt;p&gt;For anything useful — a printer head moving left and right, a robotic arm returning to start — you need both directions. Step 4 adds that.&lt;/p&gt;

&lt;p&gt;And once you have bidirectional movement, a natural question comes up: where is the motor right now? Step 4 introduces a simple way to track that.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Direction Control Works
&lt;/h2&gt;

&lt;p&gt;The A4988 driver has a DIR pin. The logic is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DIR pin HIGH  =  clockwise (+ direction)
DIR pin LOW   =  counterclockwise (- direction)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the pin before sending step pulses, and the motor follows.&lt;/p&gt;

&lt;p&gt;In code, I represent direction with a &lt;code&gt;bool&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;clockwise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="cm"&gt;/* + direction */&lt;/span&gt;
&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;clockwise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="cm"&gt;/* - direction */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the direction pin gets set once before the loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DIR_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clockwise&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That one line replaces a whole if-else block. I found the ternary operator a little strange at first, but it reads well once you get used to it: "if clockwise, set HIGH, otherwise set LOW."&lt;/p&gt;




&lt;h2&gt;
  
  
  The Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;wiringPi.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdbool.h&amp;gt;&lt;/span&gt;&lt;span class="c1"&gt;   /* bool, true, false */&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cp"&gt;#define STEP_PIN    4
#define DIR_PIN     3
#define ENABLE_PIN  2
&lt;/span&gt;
&lt;span class="cp"&gt;#define STEPS_PER_REV  200
&lt;/span&gt;
&lt;span class="cm"&gt;/*
 * g_current_angle: tracks the motor's current angle in degrees
 *
 * This is a global variable. It starts at 0 and gets updated
 * after every call to rotateAngle().
 *
 * It is not perfectly accurate (rounding errors accumulate over time),
 * but it is good enough for learning purposes at this stage.
 */&lt;/span&gt;
&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;g_current_angle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/*
 * rotateAngle: rotate by a specific angle in a specific direction
 *
 * angle     : how many degrees to rotate (positive number)
 * delay_ms  : delay between pulses in milliseconds
 * clockwise : true = clockwise (+), false = counterclockwise (-)
 */&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;rotateAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;delay_ms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;clockwise&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="cm"&gt;/* Convert angle to step count */&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;STEPS_PER_REV&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/* Set direction pin BEFORE sending pulses */&lt;/span&gt;
    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DIR_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clockwise&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Rotating %.1f deg, %s -&amp;gt; %d steps&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;clockwise&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"CW"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"CCW"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delay_ms&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delay_ms&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/* Update position tracking */&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clockwise&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;g_current_angle&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;g_current_angle&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Current angle: %.1f deg&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g_current_angle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wiringPiSetupGpio&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"wiringPi init failed&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DIR_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENABLE_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENABLE_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Step 4: Direction Control ---&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/* Rotate 90 degrees clockwise */&lt;/span&gt;
    &lt;span class="n"&gt;rotateAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/* Rotate 90 degrees counterclockwise — back to start */&lt;/span&gt;
    &lt;span class="n"&gt;rotateAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/* Go forward 180 degrees, then come back */&lt;/span&gt;
    &lt;span class="n"&gt;rotateAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;rotateAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Final position: %.1f degrees&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g_current_angle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENABLE_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Walking Through the Code
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;#include &amp;lt;stdbool.h&amp;gt;&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;C does not have a built-in &lt;code&gt;bool&lt;/code&gt; type by default. This header adds &lt;code&gt;bool&lt;/code&gt;, &lt;code&gt;true&lt;/code&gt;, and &lt;code&gt;false&lt;/code&gt;. Without it, the compiler does not recognize those keywords.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The direction pin:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DIR_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clockwise&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This must be set before the step pulses start. If you change direction in the middle of a pulse sequence, the motor gets confused. Set direction first, then pulse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The global variable &lt;code&gt;g_current_angle&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;g_current_angle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After each rotation, the function adds or subtracts the angle from this variable. It gives us a running record of where the motor is.&lt;/p&gt;

&lt;p&gt;I named it with a &lt;code&gt;g_&lt;/code&gt; prefix. This is a naming convention to make global variables easy to spot in the code. When you see &lt;code&gt;g_current_angle&lt;/code&gt; anywhere in the file, you know immediately it is a global — not a local variable inside a function.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Updating position after movement:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clockwise&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;g_current_angle&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;g_current_angle&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clockwise adds to the angle, counterclockwise subtracts. After the &lt;code&gt;main()&lt;/code&gt; test sequence — 90 CW, 90 CCW, 180 CW, 180 CCW — &lt;code&gt;g_current_angle&lt;/code&gt; should be exactly &lt;code&gt;0.0&lt;/code&gt;. The motor ends up where it started.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Limitation I Noticed
&lt;/h2&gt;

&lt;p&gt;The rounding issue from Step 3 still applies here, and it now accumulates.&lt;/p&gt;

&lt;p&gt;If I call &lt;code&gt;rotateAngle(30.0, ...)&lt;/code&gt; ten times in the same direction, the motor actually moves 28.8 degrees each time (16 steps × 1.8°). After ten calls, &lt;code&gt;g_current_angle&lt;/code&gt; says 300 degrees, but the motor has really moved 288 degrees. A 12-degree drift.&lt;/p&gt;

&lt;p&gt;For short sequences this is not a problem. But for longer runs, it matters. The real solution is a homing sequence — a physical reference point the motor returns to so position can be reset from reality, not from software tracking alone. We will get to that later in the series.&lt;/p&gt;




&lt;h2&gt;
  
  
  Practice Ideas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Add a third call in &lt;code&gt;main()&lt;/code&gt; to rotate 45 degrees CW, then 45 degrees CCW. Does it return to exactly 0.0?&lt;/li&gt;
&lt;li&gt;Try calling &lt;code&gt;rotateAngle(360.0, 2, true)&lt;/code&gt; then &lt;code&gt;rotateAngle(360.0, 2, false)&lt;/code&gt;. Watch the motor go one full revolution and come back.&lt;/li&gt;
&lt;li&gt;Change &lt;code&gt;delay_ms&lt;/code&gt; to 1 for CW and 3 for CCW. Does the speed actually change by direction?&lt;/li&gt;
&lt;li&gt;Print &lt;code&gt;g_current_angle&lt;/code&gt; after every call. Can you predict what it will say before running?&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What Is Next
&lt;/h2&gt;

&lt;p&gt;Step 5 introduces a coordinate system — tracking not just angle, but position in millimeters. We will define a &lt;code&gt;steps_per_mm&lt;/code&gt; value based on the motor and lead screw specs, and the code will start using real physical units.&lt;/p&gt;

&lt;p&gt;This is where things start to look like actual machine control.&lt;/p&gt;

&lt;p&gt;See you next time.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Eric Park, from Daegu, South Korea&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tags: &lt;code&gt;raspberrypi&lt;/code&gt; &lt;code&gt;c&lt;/code&gt; &lt;code&gt;steppermotor&lt;/code&gt; &lt;code&gt;3dprinting&lt;/code&gt; &lt;code&gt;beginners&lt;/code&gt; &lt;code&gt;embeddedsystems&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>iot</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Step 3: Rotating a Stepper Motor by Exact Angle</title>
      <dc:creator>Eric Park</dc:creator>
      <pubDate>Wed, 11 Mar 2026 22:37:55 +0000</pubDate>
      <link>https://dev.to/gustabe/step-3-rotating-a-stepper-motor-by-exact-angle-543h</link>
      <guid>https://dev.to/gustabe/step-3-rotating-a-stepper-motor-by-exact-angle-543h</guid>
      <description>&lt;h1&gt;
  
  
  Step 3: Rotating a Stepper Motor by Exact Angle
&lt;/h1&gt;

&lt;p&gt;Hello everyone! This is Eric Park, 3D Printer Engineer from South Korea.&lt;/p&gt;

&lt;p&gt;We are now at Step 3 of our controller series. If you missed the previous posts, here is a quick recap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Step 1: We made the motor spin for the first time — 200 pulses, one full revolution&lt;/li&gt;
&lt;li&gt;Step 2: We controlled the speed by changing the delay between pulses&lt;/li&gt;
&lt;li&gt;Step 3 (today): We tell the motor to rotate by a specific angle — 90 degrees, 45 degrees, whatever we want&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where things start to feel much more like a real machine.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Goal
&lt;/h2&gt;

&lt;p&gt;By the end of Step 2, I could control speed. But I still could not tell the motor "rotate exactly 90 degrees." I had to manually calculate how many steps to use, which felt clumsy.&lt;/p&gt;

&lt;p&gt;Step 3 is about building a &lt;code&gt;rotateAngle()&lt;/code&gt; function. I type in the angle I want, and the code figures out the steps automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Key Formula
&lt;/h2&gt;

&lt;p&gt;A 1.8-degree stepper motor needs 200 steps to complete one full revolution.&lt;/p&gt;

&lt;p&gt;Why 200? Because 360 degrees / 1.8 degrees per step = 200 steps.&lt;/p&gt;

&lt;p&gt;So if I want to rotate a specific angle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;360.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;90 degrees → (90 / 360.0) × 200 = &lt;strong&gt;50 steps&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;45 degrees → (45 / 360.0) × 200 = &lt;strong&gt;25 steps&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;180 degrees → (180 / 360.0) × 200 = &lt;strong&gt;100 steps&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This formula is the entire idea behind Step 3. Everything else is just implementing it in C.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/*
 * Step 3: Rotate by exact angle
 *
 * Learning goals:
 * - Understand how angle converts to step count
 * - Implement math formulas in C code
 * - Use ENABLE pin to activate and deactivate the motor
 */&lt;/span&gt;

&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;wiringPi.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cp"&gt;#define STEP_PIN      4    // wiringPi pin 4 = GPIO 23
#define DIR_PIN       3    // wiringPi pin 3 = GPIO 22
#define ENABLE_PIN    2    // wiringPi pin 2 = GPIO 27
#define STEPS_PER_REV 200  // 200 steps = 360 degrees (1.8 degree motor)
&lt;/span&gt;
&lt;span class="c1"&gt;// Low-level: send N pulses to the motor&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;rotateMotor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;delayMs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delayMs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delayMs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// High-level: rotate by angle in degrees&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;rotateAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;speedDelay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Convert angle to step count&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)((&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;STEPS_PER_REV&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%.1f degrees = %d steps&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;rotateMotor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;speedDelay&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Initialize wiringPi using BCM GPIO numbers&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wiringPiSetupGpio&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"wiringPi init failed&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DIR_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENABLE_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Activate motor (LOW = enabled for most drivers)&lt;/span&gt;
    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENABLE_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Motor enabled&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Set direction (HIGH = clockwise in our setup)&lt;/span&gt;
    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DIR_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Rotating 90 degrees&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;rotateAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Rotating 45 degrees&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;rotateAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Rotating 180 degrees&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;rotateAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Done!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Deactivate motor&lt;/span&gt;
    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENABLE_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/*
 * Compile: gcc -o step3 step3_angle_control.c -lwiringPi
 * Run:     sudo ./step3
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Walking Through the Code
&lt;/h2&gt;

&lt;h3&gt;
  
  
  STEPS_PER_REV
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#define STEPS_PER_REV 200
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I defined this as a constant at the top. This makes the code easier to read and easier to change. If I ever use a 0.9-degree motor (400 steps/revolution), I only need to change this one line.&lt;/p&gt;

&lt;p&gt;If your motor moves the wrong distance, this is the first value to check. Common values are 200 (1.8 degree) and 400 (0.9 degree).&lt;/p&gt;

&lt;h3&gt;
  
  
  Two separate functions
&lt;/h3&gt;

&lt;p&gt;I split the work into two functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;rotateMotor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;delayMs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// sends pulses&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;rotateAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;speedDelay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// converts angle, then calls rotateMotor&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;rotateMotor&lt;/code&gt; only knows about pulses. It does not know anything about angles.&lt;br&gt;
&lt;code&gt;rotateAngle&lt;/code&gt; only knows about angles. It does the math and passes the result down.&lt;/p&gt;

&lt;p&gt;This separation is a good habit. Each function does one thing. If something breaks, I know which layer to look at.&lt;/p&gt;
&lt;h3&gt;
  
  
  The conversion line
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)((&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;STEPS_PER_REV&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;(int)&lt;/code&gt; cast truncates the decimal. For example, 33.33 steps becomes 33. This is acceptable for most use cases. The small error is less than one step, which is less than 1.8 degrees.&lt;/p&gt;

&lt;p&gt;If you need more precision, a microstepping driver can help — but that is a topic for a later step.&lt;/p&gt;
&lt;h3&gt;
  
  
  ENABLE pin
&lt;/h3&gt;

&lt;p&gt;In Step 1, I added the ENABLE pin but did not explain it much. Here is the fuller picture.&lt;/p&gt;

&lt;p&gt;Most stepper motor drivers have an enable (ENA) input:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LOW: motor is energized and holds its position&lt;/li&gt;
&lt;li&gt;HIGH: motor is de-energized, shaft can spin freely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I set it to LOW at startup and HIGH at the end. During operation, the motor holds position. After the program ends, you can spin the shaft by hand without fighting the motor current.&lt;/p&gt;
&lt;h3&gt;
  
  
  wiringPiSetupGpio() vs wiringPiSetup()
&lt;/h3&gt;

&lt;p&gt;In Step 1, I used &lt;code&gt;wiringPiSetup()&lt;/code&gt;. This uses wiringPi's own pin numbering system. In Step 3, I switched to &lt;code&gt;wiringPiSetupGpio()&lt;/code&gt;, which uses BCM GPIO numbers directly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// wiringPiSetup()   → pin 4 = wiringPi pin 4&lt;/span&gt;
&lt;span class="c1"&gt;// wiringPiSetupGpio() → pin 4 = GPIO 4 (BCM)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I made this change because BCM numbers are what most wiring diagrams and documentation use. It reduces confusion when checking your wiring.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Tested
&lt;/h2&gt;

&lt;p&gt;After writing the code, I ran a few tests.&lt;/p&gt;

&lt;p&gt;First I confirmed 90 degrees → 50 steps → motor turns a quarter of a revolution. That matched visually.&lt;/p&gt;

&lt;p&gt;Then I tried the math edge case: &lt;code&gt;rotateAngle(45, 2)&lt;/code&gt; gives 25 steps exactly. No rounding issue.&lt;/p&gt;

&lt;p&gt;Then I tried &lt;code&gt;rotateAngle(30, 2)&lt;/code&gt;: (30 / 360.0) × 200 = 16.666... → &lt;code&gt;(int)&lt;/code&gt; truncates to 16 steps → 16 × 1.8 = 28.8 degrees instead of 30.&lt;/p&gt;

&lt;p&gt;This is a real limitation. The minimum addressable unit is 1.8 degrees (1 step), so any angle that does not divide evenly into the step size will have a small error. For my project at this stage, that is acceptable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Practice Ideas
&lt;/h2&gt;

&lt;p&gt;If you are following along, try these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Change &lt;code&gt;STEPS_PER_REV&lt;/code&gt; to 400 and see what happens&lt;/li&gt;
&lt;li&gt;Try &lt;code&gt;rotateAngle(360, 2)&lt;/code&gt; and confirm it is exactly one full revolution&lt;/li&gt;
&lt;li&gt;Try &lt;code&gt;rotateAngle(0, 2)&lt;/code&gt; — what does &lt;code&gt;(int)(0 / 360.0 * 200)&lt;/code&gt; equal? Does it crash or just do nothing?&lt;/li&gt;
&lt;li&gt;Change the &lt;code&gt;(int)&lt;/code&gt; cast to &lt;code&gt;(int)round(...)&lt;/code&gt; — does it improve accuracy for 30 degrees?&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Step 4 adds direction control. Right now the motor always turns clockwise. In Step 4, I will add the ability to specify a direction — and start tracking where the motor actually is.&lt;/p&gt;

&lt;p&gt;That position tracking idea ends up being important later. When we get to Step 7 (acceleration profiles) and eventually G-code parsing, knowing the current position is essential.&lt;/p&gt;

&lt;p&gt;See you next Thursday!&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Eric Park, from Daegu, South Korea&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tags: &lt;code&gt;raspberrypi&lt;/code&gt; &lt;code&gt;c&lt;/code&gt; &lt;code&gt;steppermotor&lt;/code&gt; &lt;code&gt;3dprinting&lt;/code&gt; &lt;code&gt;beginners&lt;/code&gt; &lt;code&gt;embeddedsystems&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>3d</category>
      <category>3dprinting</category>
      <category>steppermotor</category>
      <category>c</category>
    </item>
    <item>
      <title>step.1 start rotation of stepper motor with raspberry pi</title>
      <dc:creator>Eric Park</dc:creator>
      <pubDate>Thu, 26 Feb 2026 11:09:43 +0000</pubDate>
      <link>https://dev.to/gustabe/step1-start-rotation-of-stepper-motor-with-raspberry-pi-58h8</link>
      <guid>https://dev.to/gustabe/step1-start-rotation-of-stepper-motor-with-raspberry-pi-58h8</guid>
      <description>&lt;h1&gt;
  
  
  Step 1: Let's Spin a Stepper Motor with Raspberry Pi!
&lt;/h1&gt;

&lt;p&gt;Hello everyone! This is Eric Park, 3D Printer Engineer from South Korea.&lt;/p&gt;

&lt;p&gt;Welcome to the first real step of our journey — &lt;strong&gt;"Making a Controller with Raspberry Pi"&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;If you haven't read my introduction post yet, here's the short version: I'm a 3D printer engineer who couldn't understand Marlin or Klipper code, so I decided to study from the very basics — with the help of AI (Claude). And now I want to share everything I learn, step by step, with anyone who feels the same way I did.&lt;/p&gt;

&lt;p&gt;Today is &lt;strong&gt;Step 1: Make the motor spin.&lt;/strong&gt; That's it. Simple. Fun. Let's go!&lt;/p&gt;




&lt;h2&gt;
  
  
  What You Need (BOM - Bill of Materials)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4513lfpcag3xw5pyvg3p.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4513lfpcag3xw5pyvg3p.jpg" alt=" " width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Messy Desk ;)
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Wiring Diagram
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I'll add a proper image diagram soon! For now, here's the connection table and an ASCII diagram to get you started.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Raspberry Pi GPIO        A4988 Driver Board
─────────────────────────────────────────────
GPIO 4  (Pin 7)  ──────► STEP
GPIO 3  (Pin 5)  ──────► DIR
GPIO 2  (Pin 3)  ──────► ENABLE (active LOW)
GND              ──────► GND (logic side)

12V Power Supply
─────────────────────────────────────────────
12V (+)          ──────► VMOT
GND (-)          ──────► GND (motor side)

Stepper Motor    ──────► A, A̅, B, B̅  (4 wires)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important notes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The A4988 has two GND pins — one for logic (connect to RPi GND), one for motor power (connect to 12V GND)&lt;/li&gt;
&lt;li&gt;Set the current limit on your A4988 before connecting the motor! (Turn the potentiometer carefully)&lt;/li&gt;
&lt;li&gt;ENABLE pin is &lt;strong&gt;active LOW&lt;/strong&gt; — meaning &lt;code&gt;LOW&lt;/code&gt; = motor ON, &lt;code&gt;HIGH&lt;/code&gt; = motor OFF&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Code — Step 1
&lt;/h2&gt;

&lt;p&gt;Let's look at the code together. I'll explain each part so we can understand what's happening!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/*
 * step1.c
 * Basic Stepper Motor Control
 *
 * Goal:
 *   - Understand GPIO control
 *   - Generate step pulses to spin the motor
 *   - Control direction
 */&lt;/span&gt;

&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;wiringPi.h&amp;gt;&lt;/span&gt;&lt;span class="c1"&gt;   // Raspberry Pi GPIO library&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="c1"&gt;// === Pin Definitions ===&lt;/span&gt;
&lt;span class="c1"&gt;// We use BCM GPIO numbers (wiringPiSetupGpio mode)&lt;/span&gt;
&lt;span class="cp"&gt;#define STEP_PIN    4   // GPIO 4 → sends pulses to move motor
#define DIR_PIN     3   // GPIO 3 → sets rotation direction
#define ENABLE_PIN  2   // GPIO 2 → enables/disables motor
&lt;/span&gt;
&lt;span class="c1"&gt;// === Settings ===&lt;/span&gt;
&lt;span class="cp"&gt;#define STEPS_TO_MOVE   200    // 200 steps = 1 full revolution (for 1.8° motor)
#define STEP_DELAY_US   1000   // delay between pulses in microseconds (= speed)
&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// Step 1: Initialize wiringPi with BCM GPIO numbering&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wiringPiSetupGpio&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"wiringPi init failed!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Step 2: Set pin modes&lt;/span&gt;
    &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DIR_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENABLE_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Step 3: Enable the motor driver (ENABLE is active LOW)&lt;/span&gt;
    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENABLE_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Motor enabled!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// small wait after enable&lt;/span&gt;

    &lt;span class="c1"&gt;// Step 4: Set direction (HIGH = one way, LOW = other way)&lt;/span&gt;
    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DIR_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Direction: Forward&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Step 5: Send step pulses!&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Moving %d steps...&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;STEPS_TO_MOVE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;STEPS_TO_MOVE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="c1"&gt;// One pulse = one step&lt;/span&gt;
        &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;delayMicroseconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_DELAY_US&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// pulse ON time&lt;/span&gt;

        &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;delayMicroseconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_DELAY_US&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// pulse OFF time&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Done! Motor moved %d steps.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;STEPS_TO_MOVE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Step 6: Disable motor (saves power, reduces heat)&lt;/span&gt;
    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENABLE_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Motor disabled.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Code Explanation — Let's Read It Together
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;#include &amp;lt;wiringPi.h&amp;gt;&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This is the library that lets us control the Raspberry Pi's GPIO pins from C code. Think of it as the "translator" between our code and the hardware pins.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Pin Definitions (&lt;code&gt;#define&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#define STEP_PIN    4
#define DIR_PIN     3
#define ENABLE_PIN  2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of writing the number &lt;code&gt;4&lt;/code&gt; everywhere in the code, we give it a name &lt;code&gt;STEP_PIN&lt;/code&gt;. This makes the code much easier to read and change later. This is a very common practice in C!&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;code&gt;wiringPiSetupGpio()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This tells wiringPi: &lt;em&gt;"I want to use standard BCM GPIO numbers."&lt;/em&gt; It must be called once at the very start, before using any GPIO functions.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;code&gt;pinMode(pin, OUTPUT)&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Each GPIO pin can work as either &lt;strong&gt;INPUT&lt;/strong&gt; (read a signal) or &lt;strong&gt;OUTPUT&lt;/strong&gt; (send a signal). Since we're sending pulses to the motor driver, we set all three pins as &lt;code&gt;OUTPUT&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. The ENABLE Pin — Active LOW Logic
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENABLE_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// LOW = ON (motor enabled)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a little confusing at first! The A4988 driver uses "active LOW" logic for ENABLE, which means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;LOW (0V)&lt;/code&gt; → Motor is &lt;strong&gt;ENABLED&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HIGH (3.3V)&lt;/code&gt; → Motor is &lt;strong&gt;DISABLED&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is common in electronics — don't worry, you'll get used to it!&lt;/p&gt;

&lt;h3&gt;
  
  
  6. The Step Pulse Loop
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;STEPS_TO_MOVE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;delayMicroseconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_DELAY_US&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;delayMicroseconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_DELAY_US&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each loop iteration = &lt;strong&gt;one step&lt;/strong&gt; of the motor.&lt;/p&gt;

&lt;p&gt;One step is created by:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pulling STEP pin &lt;code&gt;HIGH&lt;/code&gt; → motor driver detects rising edge → motor moves one step&lt;/li&gt;
&lt;li&gt;Waiting (&lt;code&gt;delayMicroseconds&lt;/code&gt;) → this controls speed!&lt;/li&gt;
&lt;li&gt;Pulling STEP pin &lt;code&gt;LOW&lt;/code&gt; → ready for the next pulse&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Key insight:&lt;/strong&gt; The smaller the delay, the faster the motor spins!&lt;br&gt;
Try changing &lt;code&gt;STEP_DELAY_US&lt;/code&gt; from &lt;code&gt;1000&lt;/code&gt; to &lt;code&gt;500&lt;/code&gt; and see what happens.&lt;/p&gt;


&lt;h2&gt;
  
  
  How to Compile and Run
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Compile&lt;/span&gt;
gcc step1.c &lt;span class="nt"&gt;-o&lt;/span&gt; step1 &lt;span class="nt"&gt;-lwiringPi&lt;/span&gt;

&lt;span class="c"&gt;# Run (needs sudo for GPIO access)&lt;/span&gt;
&lt;span class="nb"&gt;sudo&lt;/span&gt; ./step1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Motor enabled!
Direction: Forward
Moving 200 steps...
Done! Motor moved 200 steps.
Motor disabled.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What Did We Learn?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;How to set up GPIO pins with wiringPi&lt;/li&gt;
&lt;li&gt;What STEP, DIR, and ENABLE pins do&lt;/li&gt;
&lt;li&gt;How a step pulse works (HIGH → delay → LOW → delay)&lt;/li&gt;
&lt;li&gt;How delay time controls motor speed&lt;/li&gt;
&lt;li&gt;Active LOW logic (ENABLE pin)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What's Next? (Step 2 Preview)
&lt;/h2&gt;

&lt;p&gt;Right now, our motor just spins 200 steps and stops. But what if we want to control &lt;strong&gt;how many mm it moves&lt;/strong&gt;, or make it go &lt;strong&gt;back and forth&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;Next week (Step 2), we'll add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Direction control&lt;/strong&gt; — forward and backward&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Loop for continuous movement&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Start thinking about converting steps to mm&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See you next Thursday!&lt;/p&gt;




&lt;h2&gt;
  
  
  Let's Talk!
&lt;/h2&gt;

&lt;p&gt;Have any questions? Did your motor spin? Did it NOT spin? Let me know in the comments!&lt;/p&gt;

&lt;p&gt;If you're in a similar situation — an engineer who wants to understand the code behind their machines — I hope this series helps you feel less alone in that journey. We're learning together.&lt;/p&gt;

&lt;p&gt;And if you have questions about 3D printing, Korea, or anything else — feel free to reach out anytime!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Eric Park, from Daegu, South Korea&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tags: &lt;code&gt;raspberrypi&lt;/code&gt; &lt;code&gt;C&lt;/code&gt; &lt;code&gt;steppermotor&lt;/code&gt; &lt;code&gt;3dprinting&lt;/code&gt; &lt;code&gt;beginners&lt;/code&gt; &lt;code&gt;embedded&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>stepper</category>
      <category>c</category>
      <category>ai</category>
    </item>
    <item>
      <title>Greeting from south korea</title>
      <dc:creator>Eric Park</dc:creator>
      <pubDate>Tue, 24 Feb 2026 13:11:27 +0000</pubDate>
      <link>https://dev.to/gustabe/greeting-from-south-korea-124n</link>
      <guid>https://dev.to/gustabe/greeting-from-south-korea-124n</guid>
      <description>&lt;p&gt;Greeting, Everyone.&lt;/p&gt;

&lt;p&gt;Thank you for reading this article ! :)&lt;br&gt;
This is Eric Park, 3D Printer Engineer from South Korea, Dae-gu.&lt;/p&gt;

&lt;p&gt;I was worked as a 3D Printer Engineer for 8 years.&lt;br&gt;
My first 3D Printer is DIY 3D Printer with Marlin 8bit, and now I am using Klipper and with the automation 3D Printing.&lt;/p&gt;

&lt;p&gt;in the future, I will prepare and introduce my diy 3d printer.&lt;/p&gt;

&lt;p&gt;I was interested in control and code. but, as I am not a code engineer, I can not understand when I see the marlin &amp;amp; klipper code ;(&lt;/p&gt;

&lt;p&gt;so, I decided make 3d printer controller with AI.&lt;br&gt;
I was very surprised when I use Claude, and I realise I can make a Code for control 3d printer with Claude.&lt;/p&gt;

&lt;p&gt;and now, I am studying code with claude, control stepper motor. and I want to share the my study material who want to study the code, like me.&lt;/p&gt;

&lt;p&gt;This travel called "make a controller with raspberrypi".&lt;br&gt;
This travel included 21 steps, and also share everything, code, also BOM.&lt;/p&gt;

&lt;p&gt;and in the future, I will make my own firmware and 3d printer, and test with gcode.&lt;/p&gt;

&lt;p&gt;I will publish the blog every thursday, following the korea time.&lt;/p&gt;

&lt;p&gt;again, thank you for read this article, and anything you want to me, please feel free to contact me. about korea, also.&lt;/p&gt;

&lt;p&gt;thanks!&lt;/p&gt;

&lt;p&gt;Eric Park, from korea.&lt;/p&gt;

</description>
      <category>3dprinting</category>
      <category>stepper</category>
      <category>c</category>
      <category>raspberrypi</category>
    </item>
  </channel>
</rss>
