Using IR Receiver with a Stepper Motor

I’m hoping someone can help me understand if what I want to do is feasible.

I’m new to Arduino, and have been working my way through the elegoo.com tutorials for my Uno board.

Ultimately, my stepper motor is going to control a telescope mount - stars are constantly moving, so I need the scope to keep track, but if my scope tracks at slightly the wrong speed, I may need to nudge the speed up a little. When the speed changes, the motor must continue uninterrupted and this is where I think I might be limited. My plan was to use the IR controller to change the speed using the Vol+/- buttons.

From the IR code that I’ve seen so far, it looks like the code (and therefore the motor) needs to pause for half a second so that that the program doesn’t receive multiple commands in the same button press. Or if I set the speeds to a pre-set number on the keypad, I think the motor will pause briefly whilst the board receives the signal. I can’t see there is a way for the motor to continue turning when an IR button press is received.

I’m attaching the two pieces of code that I’ve found so far - the stepper code, and IR receiver code, but I’m a bit stuck as to where to begin to so that I solve in the way I describe above.

Is what I’m trying to achieve possible? Can I achieve what I want by modifying the attached code, or do I need to rethink this completely? If it’s possible, can you help me find some example code that might work, or help to get me started?

Thank you
Kev

stepper_Example.ino (825 Bytes)

IR_Receiver_Module.ino (2.25 KB)

How is the motor connected to the scope’s RA axis (mechanically)?

The Arduino should be well able to receive commands between pulses to the stepper motor so that the motion is not disturbed.

Have a look at how the code is organized in Several Things at a Time

Note how each function runs very briefly and returns to loop() so the next one can be called. None of the functions tries to complete a task in one call. And there may be dozens of calls to a function before it is actually time for it to do anything.

...R

Robin2:
The Arduino should be well able to receive commands between pulses to the stepper motor so that the motion is not disturbed.

Have a look at how the code is organized in Several Things at a Time

Note how each function runs very briefly and returns to loop() so the next one can be called. None of the functions tries to complete a task in one call. And there may be dozens of calls to a function before it is actually time for it to do anything.

…R

Sounds good - Looks like I have some reading and experimenting to do. If working as described, this sounds like it’s what I’m looking for. Thank you!

JCA34F: How is the motor connected to the scope's RA axis (mechanically)?

Thanks for the reply.

Yes - I'm making a device that will attach to an old EQ mount that has manual motion controls.

Robin2:
The Arduino should be well able to receive commands between pulses to the stepper motor so that the motion is not disturbed.

Have a look at how the code is organized in Several Things at a Time

…R

I spent some time trying to reorganise my code, but I’ve quickly got out of my depth, and will need a little hand holding. I’m going to post what I’ve done so far, and try to explain my though process. I’m not sure if it’s worth continuing or starting again with a different approach.

I’ve attached 3 files:
stepper_motor_with_IR_receiver is my attempt at a sketch which is nowhere near ready to compile, but does have my thoughts as comments where I think I need to solve issues.

stepper1.cpp is the library I intend to use
stepper.cpp is the library that I’m rebuilding - but I wont use in the final version
and I’m pinching some code ideas from this link - Several Things at a Time though I’ve not really attempted that part properly yet - I’m just pulling together the code that I think will be helpful for now and loosely organising it so that it’s roughly where I think it ought to be. I’m also pinching some lines of code from the IR receiver sketch that I attached in a previous message.

So here’s my thought process:

The library stepper.cpp should be rebuilt into a sketch. The first half of the library seems to be initialising variables, so I’m thinking to keep the first half in a library, and write everything after this comment

/*
 * Sets the speed in revs per minute
 */

into my sketch as this is where I will eventually integrate the IR code

I’ve hit a few bumps that I’m hoping I can solve later. I’m expecting that I’m mixing variable names, and possibly attempting things in the sketch that can only be done in the library, so I’m getting errors like this:

Stepper_Motor_with_IR_Receiver:142: error: qualified-id in declaration before ‘(’ token

I presume at some point I’ll need to tie the variables from the library back to the sketch. I’ll need to integrate the timings from Several Things at a Time into the sketch, and of course I’ll need to solve the variable names, as the library references “This->” a lot, which I don’t really understand yet. I presume they are local rather global, but resolving that is hurting my head.

So my question is - am I on the right path? I’ve spent a few hours on this already and am at the limit of what I think I can do by myself - should I continue with the approach, or would I be better off starting again? Rebuilding the library is proving more challenging than I first thought. I’m now wondering if splitting the library in half was a good idea, and if I should just either just scrap my stepper1.cpp library altogether and put all of it into the sketch, or scrap the sketch I’ve started and attempt to integrate the IR code, and the “several things at a Time” code into the stepper.cpp library?

Do I soldier on with the current plan, or have I created a whole bundle of knitting to unravel?!

Thanks to anyone who has managed to read this far and still has hope for me!

Stepper1.CPP (5.92 KB)

Stepper.cpp (11.2 KB)

Stepper_Motor_with_IR_Receiver.ino (14.9 KB)

When I am trying to learn or develop a new technique I always start with very short programs. I don't understand how you have a 14k program at this early stage.

Also you say "nowhere near ready to compile". You should be compiling and testing after every few lines of code is added.

Maybe have a look at Planning and Implementing a Program

...R

Robin2: Maybe have a look at Planning and Implementing a Program

...R

Start from scratch was where I think I was heading - that thread is helpful. Thanks again.

I’ve re-written my code and hopefully makes a bit more sense now. I’m attaching a compressed folder that contains an example stepper sketch along with the library. The example works perfectly for me. I’ve also attached EQ Driver.ino that should run independently without the stepper library, along with the schematic that I’m using.

What I have done is to take the library and re-write it as a sketch. In doing that, it makes more sense (to me) so that that I can eventually add in some IR receiver code that runs between steps. In this way I’m hoping I will be able to change the speed of the stepper with the IR controller in such a way that the stepper motor doesn’t pause/interrupt the stepper whilst it receives the signal. The IR code hasn’t been added yet, so at the moment I’m just trying to get the stepper work.

The program compiles and uploads to my Uno r3. But the motor doesn’t turn. The LED’s on the driver module light up as if it is sequencing through the steps. The motor “buzzes” but doesn’t rotate. Where have I gone wrong?

I wonder if this line may be a problem:

step_delay = 60L * 1000L * 1000L / number_of_steps / roles_Per_Minute;

I don’t really understand how it works - in the library, I can’t see where step_delay is declared as a variable, and I don’t really see what 60L and 1000L are. I can see it controls the delay between steps and therefore the speed of the motor, so I’ll eventually need to modify it so that the IR controller functions work with it. Have I set this up OK my code? What else might be causing the motor to not turn?

Thanks in advance for any help or ideas to help me solve!

EQ_Driver.ino.ino (4.68 KB)

stepper_Example.zip (4.87 KB)

For short programs please include in your Reply like this - many people read the Forum on tablets and can’t download files.

 /* The sequence of control signals for 4 control wires is as follows:
 *
 * Step C0 C1 C2 C3
 *    1  1  0  1  0
 *    2  0  1  1  0
 *    3  0  1  0  1
 *    4  1  0  0  1
 */

//=======Libraries======================================================
#include <IRremote.h>
#include <IRremoteInt.h>

#include "Arduino.h"
#include "IRremote.h"
//=======End Libraries==================================================


//===Initialise=========================================================
//Constants
//---stepper motor--------------------------------------------
const int number_of_steps = 2048; //number of steps for the stepper motor
const int motor_pin_1 = 8; //Signal Pin of Stepper Motor
const int motor_pin_2 = 10; //Signal Pin of Stepper Motor
const int motor_pin_3 = 9; //Signal Pin of Stepper Motor
const int motor_pin_4 = 11; //Signal Pin of Stepper Motor
const int direction = 0;      // motor direction
//---IR receiver constants----------------------------------------------
//const int receiver = 11; // Signal Pin of IR receiver when added

//variables
long roles_Per_Minute = 6; //6 RPM to start - variable type to take decimals?? needs to increment by 0.1
int step_number = 0;    // which step the motor is on
int last_step_time = 0; // time stamp in us of the last step taken
int steps_to_move;
long step_delay; //step delay - not sure why this isn't declared in the stepper library example


//Functions
//--------stepMotor Function---------------------
void stepMotor(int thisStep)

{
    switch (thisStep) {
      case 0:  // 1010
        digitalWrite(motor_pin_1, HIGH);
        digitalWrite(motor_pin_2, LOW);
        digitalWrite(motor_pin_3, HIGH);
        digitalWrite(motor_pin_4, LOW);
      break;
      case 1:  // 0110
        digitalWrite(motor_pin_1, LOW);
        digitalWrite(motor_pin_2, HIGH);
        digitalWrite(motor_pin_3, HIGH);
        digitalWrite(motor_pin_4, LOW);
      break;
      case 2:  //0101
        digitalWrite(motor_pin_1, LOW);
        digitalWrite(motor_pin_2, HIGH);
        digitalWrite(motor_pin_3, LOW);
        digitalWrite(motor_pin_4, HIGH);
      break;
      case 3:  //1001
        digitalWrite(motor_pin_1, HIGH);
        digitalWrite(motor_pin_2, LOW);
        digitalWrite(motor_pin_3, LOW);
        digitalWrite(motor_pin_4, HIGH);
      break;
}
}

//----Step Function-----------------------------------------
void stepper(long RPM)
{
  roles_Per_Minute = RPM;
  steps_to_move = number_of_steps; //number of steps declared as a constant
  int steps_left = abs(steps_to_move);  // how many steps to take
  step_delay = 60L * 1000L * 1000L / number_of_steps / roles_Per_Minute; //eventually write this line into IR code to update step delay with IR Controller
  
  // decrement the number of steps, moving one step each time:
  while (steps_left > 0)
  {
    unsigned long now = micros();
    // move only if the appropriate delay has passed:
    if (now - last_step_time >= step_delay)
    {
      // get the timeStamp of when you stepped:
      last_step_time = now;
      // increment or decrement the step number,
      // depending on direction:
      if (direction == 1)
      {
        step_number++;
        if (step_number == number_of_steps) {
          step_number = 0;
        }
      }
      else
      {
        if (step_number == 0) {
          step_number = number_of_steps;
        }
        step_number--;
      }
      // decrement the steps left:
      steps_left--;
        stepMotor(step_number % 4);
    }
  }
}
//--------translateIR Function---------------------
//Add function to translate IR codes here
//void translateIR(TIMESTAMP) // takes action based on IR code received
//IF timestamp + 500ms < NOW then
//    SKIP CODE
//ELSE
//Execute IR button press
//--------End Functions----------------------

//create objects
//Add IR objects here
//IRrecv irrecv(receiver);     // create instance of 'irrecv'
//decode_results results;      // create instance of 'decode_results'

//======Setup============================================================
//runs once

void setup() {
//Pin order: 8, 10, 9, 11);
//steps Per Revolution = 2048;  // change this to fit the number of steps per revolution
//Revs Per Minute = 6;         // Adjustable range of 28BYJ-48 stepper is 0~17 rpm


  Serial.begin(9600);
  //Serial.println("IR Receiver Button Decode"); 
  //irrecv.enableIRIn(); // Start the receiver
  // setup the pins on the microcontroller:
  pinMode(motor_pin_1, OUTPUT);
  pinMode(motor_pin_2, OUTPUT);
  pinMode(motor_pin_3, OUTPUT);
  pinMode(motor_pin_4, OUTPUT);

  
}
//======End Setup=======================================================
//=====Main Loop==================
void loop() {
Serial.println("loop");
stepper(roles_Per_Minute);

}

//=====End Main Loop===============

…R

I don't know if you have the step sequence correct. For my 28BYJ-48 stepper motor I use this sequence

1100
0110
0011
1001

I suggest you don't pass the step position to the stepMotor() function - it is something that is entirely local to the motor movement and is irrelevant to rest of the program. Instead just calculate it within the function. Increment the position for forward motion and decrement it for reverse.

...R

Robin2:
I don’t know if you have the step sequence correct. For my 28BYJ-48 stepper motor I use this sequence

1100

0110
0011
1001




I suggest you don't pass the step position to the stepMotor() function - it is something that is entirely local to the motor movement and is irrelevant to rest of the program. Instead just calculate it within the function. Increment the position for forward motion and decrement it for reverse.

...R

Thanks again Robin - I re-wrote the code as you suggested, copied below. It still didn’t work, so I added some print lines to see what was going.

In the loops, I’ve added some print lines, like this:

Serial.print("|1001|");

With the print added, the motor turns perfectly. Without the print lines, the motor does nothing!
What’s that about then?! I presume my original code would have worked with these lines in it too, but what gives? Why would a serial.print solve it?

/* The sequence of control signals for 4 control wires is as follows:
*
* Step C0 C1 C2 C3
*    1  1  0  1  0
*    2  0  1  1  0
*    3  0  1  0  1
*    4  1  0  0  1
*/

//=======Libraries======================================================
#include <IRremote.h>
#include <IRremoteInt.h>

#include "Arduino.h"
#include "IRremote.h"
//=======End Libraries==================================================


//===Initialise=========================================================
//Constants
//---stepper motor--------------------------------------------
const int number_of_steps = 2048; //number of steps for the stepper motor
const int motor_pin_1 = 8; //Signal Pin of Stepper Motor
const int motor_pin_2 = 10; //Signal Pin of Stepper Motor
const int motor_pin_3 = 9; //Signal Pin of Stepper Motor
const int motor_pin_4 = 11; //Signal Pin of Stepper Motor
const int direction = 0;      // motor direction
//---IR receiver constants----------------------------------------------
//const int receiver = 11; // Signal Pin of IR receiver when added

//variables
long roles_Per_Minute = 6; //6 RPM to start - variable type to take decimals?? needs to increment by 0.1
int step_number = 0;    // which step the motor is on
int last_step_time = 0; // time stamp in us of the last step taken
int steps_to_move;
long step_delay; //step delay - not sure why this isn't declared in the stepper library example


//Functions

//----Step Function-----------------------------------------
void stepper(long RPM)
{
 roles_Per_Minute = RPM;
 steps_to_move = number_of_steps; //number of steps declared as a constant
 int steps_left = abs(steps_to_move);  // how many steps to take
 step_delay = 60L * 1000L * 1000L / number_of_steps / roles_Per_Minute; //eventually write this line into IR code to update step delay with IR Controller
 
 // decrement the number of steps, moving one step each time:
 while (steps_left > 0)
 {
   unsigned long now = micros();
   // move only if the appropriate delay has passed:
   if (now - last_step_time >= step_delay)
   {
     // get the timeStamp of when you stepped:
     last_step_time = now;
     // increment or decrement the step number,
     // depending on direction:
     if (direction == 1)//Anti-Clockwise
     {
       steps_left--;
         for(int step_number = 0; step_number < 4; step_number += 1)
           {
           switch (step_number) {
                 case 0:  // 1010
                 Serial.print("|1010|");
                   digitalWrite(motor_pin_1, HIGH);
                   digitalWrite(motor_pin_2, LOW);
                   digitalWrite(motor_pin_3, HIGH);
                   digitalWrite(motor_pin_4, LOW);
                 break;
                 case 1:  // 0110
                 Serial.print("|0110|");
                   digitalWrite(motor_pin_1, LOW);
                   digitalWrite(motor_pin_2, HIGH);
                   digitalWrite(motor_pin_3, HIGH);
                   digitalWrite(motor_pin_4, LOW);
                 break;
                 case 2:  //0101
                 Serial.print("|0101|");
                   digitalWrite(motor_pin_1, LOW);
                   digitalWrite(motor_pin_2, HIGH);
                   digitalWrite(motor_pin_3, LOW);
                   digitalWrite(motor_pin_4, HIGH);
                 break;
                 case 3:  //1001
                 Serial.print("|1001|");
                   digitalWrite(motor_pin_1, HIGH);
                   digitalWrite(motor_pin_2, LOW);
                   digitalWrite(motor_pin_3, LOW);
                   digitalWrite(motor_pin_4, HIGH);
                 break;
           }//end switch
       }//end for loop
     }//end if
     else
       {
       steps_left--;
         for(int step_number = 3; step_number >= 0; step_number -= 1)
           {
           switch (step_number) {
                 case 0:  // 1010
                 Serial.print("|1010|");
                   digitalWrite(motor_pin_1, HIGH);
                   digitalWrite(motor_pin_2, LOW);
                   digitalWrite(motor_pin_3, HIGH);
                   digitalWrite(motor_pin_4, LOW);
                 break;
                 case 1:  // 0110
                 Serial.print("|0110|");
                   digitalWrite(motor_pin_1, LOW);
                   digitalWrite(motor_pin_2, HIGH);
                   digitalWrite(motor_pin_3, HIGH);
                   digitalWrite(motor_pin_4, LOW);
                 break;
                 case 2:  //0101
                 Serial.print("0101");
                   digitalWrite(motor_pin_1, LOW);
                   digitalWrite(motor_pin_2, HIGH);
                   digitalWrite(motor_pin_3, LOW);
                   digitalWrite(motor_pin_4, HIGH);
                 break;
                 case 3:  //1001
                 Serial.print("|1001|");
                   digitalWrite(motor_pin_1, HIGH);
                   digitalWrite(motor_pin_2, LOW);
                   digitalWrite(motor_pin_3, LOW);
                   digitalWrite(motor_pin_4, HIGH);
                 break;  
             }
         }
       }
   }
  }
 }


//--------translateIR Function---------------------
//Add function to translate IR codes here
//void translateIR(TIMESTAMP) // takes action based on IR code received
//IF timestamp + 500ms < NOW then
//    SKIP CODE
//ELSE
//Execute IR button press
//--------End Functions----------------------

//create objects
//Add IR objects here
//IRrecv irrecv(receiver);     // create instance of 'irrecv'
//decode_results results;      // create instance of 'decode_results'

//======Setup============================================================
//runs once

void setup() {
//Pin order: 8, 10, 9, 11);
//steps Per Revolution = 2048;  // change this to fit the number of steps per revolution
//Revs Per Minute = 6;         // Adjustable range of 28BYJ-48 stepper is 0~17 rpm


 Serial.begin(9600);
 //Serial.println("IR Receiver Button Decode"); 
 //irrecv.enableIRIn(); // Start the receiver
 // setup the pins on the microcontroller:
 pinMode(motor_pin_1, OUTPUT);
 pinMode(motor_pin_2, OUTPUT);
 pinMode(motor_pin_3, OUTPUT);
 pinMode(motor_pin_4, OUTPUT);

 
}
//======End Setup=======================================================
//=====Main Loop==================
void loop() {
Serial.println("loop");
stepper(roles_Per_Minute);

}

//=====End Main Loop===============

KevMeister: With the print added, the motor turns perfectly. Without the print lines, the motor does nothing! What's that about then?!

Most likely the print statement is acting as a short delay and slowing the rate of pulses. Your code must be trying to move the motor too quickly.

Always start stepper motor testing with a very slow step rate.

...R

Robin2:
Most likely the print statement is acting as a short delay and slowing the rate of pulses. Your code must be trying to move the motor too quickly.

Always start stepper motor testing with a very slow step rate.

…R

After a bit of head scratching, I’ve finally got a program that controls the stepper at the correct speed and allows the IR sensor to control the motor between steps.

The issue in the previous code was two-fold - my loop was designed such that the delay was only applied to the first step, and not the remaining 3 steps. The one of the variables that captured the micros() was an integer, and should have been long and was returning bad results when measuring time between steps.

Thanks for your help Robin - you got me started and thinking through the problems. I’m attaching my final code in case that’s helpful.

EQ_Driver.ino (9.11 KB)