I am building a wet compass for a flight simulator. I currently have a stepper motor functioning well with the simulator but at the point of crossing 360° to 0° the stepper does a full reverse 360° to get into position.

My question... can anyone please assist me in creating code that makes the linear cross from 360° to 0° work in both directions?

The simulator feeds a float val that is a degree out of 360° e.g. 172.13. My stepper has 4076 steps per full revolution. You will see the math in the code below. I am using the AccelStepper library.

void ProgOut(byte id, float val) {
switch (1) {
case 1:  

if (val)
        { stepper1.moveTo(val * 11.3222222);         } 

break; }}

I have tried something like this:

void ProgOut(byte id, float val) {
switch (1) { 
case 1:  
if ( stepper1.distanceToGo () >= 4064){
    else if (val)
                stepper1.moveTo(val * 11.3222222);         

break; }}
  • 1
    Your library doesn't understand that your stepper goes in a full circle. See if it has some more primitive moves, like "home" and "move by n steps". If it does you're in business, but you'll have to write your own "go to" function. Alternately, dig through the documentation and see if it can be TOLD that it's dealing with a stepper that goes in a circle. Again, if it does, you're in business.
    – TimWescott
    Nov 10, 2018 at 0:52
  • have you tried to go to 361?
    – jsotola
    Nov 10, 2018 at 1:02
  • @TimWescott thanks Tim for the suggestion. I’ll go through it again to see what I have available
    – Andy
    Nov 10, 2018 at 1:25
  • @jsotola the Val is feeding from the simulator software. So the maximum is actually 359.99. The Val after that would be 0
    – Andy
    Nov 10, 2018 at 1:26
  • why are you involving data from the simulator during debugging? .... figure out how to step from 359 to 0 without moving a full circle ..... once that works, then use the simulator data again
    – jsotola
    Nov 10, 2018 at 2:00

2 Answers 2


Two options (for starters):

  1. Use the move() class rather than the moveTo() class. This will allow you to do relative moves rather than absolute.
  2. Use setCurrentPosition() (which sets the current position to zero) followed by moveTo() with a destination which is newPos - prevPos.

For either to work you will need to keep track of the current position of the needle.

To work out the relative move you will need to do some kink of modulo arithmetic but this will require some trickery to generate negative values for anti-clockwise moves.

// delta is the angular adjustment required.
delta = (currHeading + 180 - prevHeading) % 360 - 180;
setCurrentPosition();        // Zero the motor.
moveTo(delta);               // Move absolute from the new zero.
prevHeading = currHeading;   // Keep track.
  • The code assumes that you will never have to make a heading adjustment of more that 180° in one step.
  • Adding in the 180° before the modulo calculation and subtracting it again afterwards is a trick to allow negative results from the modulo arithmetic.

Also working on making a compass for flight simulator. Hopefully the following code will help answer the question. This is a working code that I have tested using serial monitor... Note: For Flight Sim use, I did find that to get enough speed and smooth movement for the X27-168 gauge motor you need to use a stepper driver with microstepping rather than driving the X27-168 directly from the Nano I/O pins. You will have to adjust Max speed & acceleration and also edit the firmware for micro-stepping with using a stepper driver. Note: When using a stepper driver chip, you need to use the correct type for 6 step coil energising that the X27-168 stepper uses eg. AX1201728SG. Polulu (A4988) type stepper drivers use 8 steps coil energisation which will not move the gauge motor correctly - while it will move the gauge motor it creates a lot of noise and is not ideal.

I did have to add the following to get both left and right movements through North. I used Movement rather than delta -
if(Movement( < -180){Movement = Movement + 360;}

A single movement of 180 degrees defaults to CCW direction. however with Flight simulator data extraction, you shouldn't get a single movement of that amount.

/*Analog Compass Gauge.**
This uses an Arduino Nano processor that has four of its I/O's directly connected to an X27-168 geared stepper motor(Gauge indicator motor).
While it works, there should really be four sets of protection diodes across each of the I/O pins - diode to +5V & diode to 0V to eliminate spikes over 5v and below 0v on each I/O pin.
The X27-168 geared stepper motor has had the internal stop cut off on the main output shaft gear to allow full 360 degree movement.
A 55mm dia plastic round plate has been fitted to the X27-168 geared stepper motor 1mm diameter drive shaft and a printed label attached to the plate to show compass direction.
The plastic plate has a small Neodymium magnet (1mm dia x 2mm long) which can pass near a Hall Effect sensor (A3144)to provide a homing reference signal.
+5v,0v and I/O signal pin is connected directly to the Hall Effect sensor. The Nano internal I/O resistor is used to provide a pullup voltage for the Hall effect sensor signal.  The firmware uses the AccelStepper library. 
PC Serial Monitor is used to enter new compass headings.
The X27-168 geared stepper motor is referred to as "stepper1" in the firmware.
    #include "AccelStepper.h" 
        AccelStepper Stepper1(AccelStepper::FULL4WIRE, 3, 4, 5, 6);   // Define stepper I/O pins (Using I/O pins connected directly to X27-168 Geared Stepper Motor)
       //AccelStepper Stepper1(AccelStepper::DRIVER, 2, 5); // Defines I/O pins when using a stepper driver e.g. AX1201728SG Quad driver chip -  Dir pin - Stepper pin 
       #define Home_Sensor      2   // Pin 2 - Connected to Home Sensor - Hall Effect A3144- (0v when sensed)
       int Offset = -67;   // ****** Adjust this setting to ensure North facing when at Home Position(0)  ******  
       int MaxSpeed = 80;   // ****** Adjust this setting for Max Speed of Stepper as to not miss steps    ******
       int Acceleration = 25   // ****** Adjust this setting for Acceleration of Stepper as to not miss steps ******
       int    Move_finished =   1;   // Used to check if move is completed
       long   Initial_Homing = -1;   // Used to Home Stepper at startup
       String InputString =    "";   // String to hold incoming data from serial monitor
       bool   StringComplete = false;   // Whether the string is complete
       int    LastHeading = 0;   // Register for Last heading
       int    NewHeading  = 0;   // Register for New heading
       int    Movement;   // Register for difference between Last and New heading
       void setup() {
       Serial.begin(9600);   // Start the Serial monitor at 9600 Baud
       pinMode(Home_Sensor, INPUT_PULLUP);   // Using internal pullup for Hall Effect sensor 
       delay(5);   // Wait for stepper Driver wake up (if used)
       Stepper1.setMaxSpeed(MaxSpeed);   // Intial Homing Max Speed of Stepper (Slower to get better accuracy)
       Stepper1.setAcceleration(Acceleration );   // Intial Homing Acceleration of Stepper
       Serial.println("........ Homing Stepper ........");

       while (digitalRead(Home_Sensor)) {   // Make the Stepper1 move CW until the sensor is activated   
       Stepper1.moveTo(Initial_Homing);   // Set the position to move to
       Initial_Homing--;   // Decrease by 1 for next move if needed
       Stepper1.run();   // Start moving stepper1
       Stepper1.setCurrentPosition(0);   // Set the current position as zero for now
       while (!digitalRead(Home_Sensor)) {   // Make the Stepper1 move CCW until the sensor is deactivated
       //**** Should now be Homed to Magnet position which may not be North as per label. Below allows tuning to correct position of label ****
       Stepper1.setMaxSpeed(MaxSpeed);   // Intial Homing Correction Max Speed of Stepper1
       Stepper1.setAcceleration(Acceleration );   // Intial Homing Correction Acceleration of Stepper1

       Stepper1.moveTo(Offset);   // To correct to have North as Home position
       while(Stepper1.distanceToGo() != 0){Stepper1.run();}   // Step to adjusted home position
       Stepper1.setCurrentPosition(0);   // Set Home position that will be used for all further movements..
       Serial.println("........ Homing Completed ........");
       Serial.println(" *** Enter Compass Heading 0 - 259 Degrees ***");
       // ---------   End of intial Homing Setup  ------------
       void loop() {
       while (Serial.available() > 0) {   // Read serial input:
       int InChar = Serial.read();
       if (isDigit(InChar)) {InputString += (char)InChar;}   // Convert the incoming byte to a char and add it to the string
       if (InChar == '\n') {   // If you get a newline, 
       NewHeading = (InputString.toInt());   // Convert input heading string to int value heading
       NewHeading = (NewHeading/2)*2;   // Eliminates 1 degree movements. Reduces movement errors with small movements

       if(NewHeading > 359 ||NewHeading < 0) {   // Displays error message if input is out of range
       NewHeading = LastHeading;   // No change due to incorrect data entered
       Serial.println("Input Error. Enter value between 0 - 259 Degrees ");
       else { Serial.print("Heading "); Serial.print(NewHeading); Serial.println("...  Enter New Heading");

       InputString = "";   // Clear the string for new input:      
       Movement = ((NewHeading + 180 - LastHeading) % 360) - 180;   // Allows movement from North East to North West
       if(Movement < -180){Movement = Movement + 360;}   // Allows movement from North West to North East
       Stepper1.setCurrentPosition(0);   // Zero the motor.
       Stepper1.setMaxSpeed(MaxSpeed);   // Set Max Speed of Stepper1 
       Stepper1.setAcceleration(Acceleration );   // Set Acceleration of Stepper1

       Stepper1.move(Movement*2);   // Set new move to position of Stepper1
       LastHeading = NewHeading;   // New heading now becomes last heading 
       while(Stepper1.distanceToGo() != 0){Stepper1.run();   // Keep updating AccelStepper for current movement
       int XInChar = Serial.read();XInChar = Serial.read();}   // Keep clearing serial buffer- Only allow new heading input once movement finished.   

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.