Programing motor with encoder

I tried implamenting your code hacked together from the other code but u cant get the motor to rotate and the serial monitor just keeps repeating "0" after "start"

#include <util/atomic.h> // For the ATOMIC_BLOCK macro

#define ENCA 18 // YELLOW
#define ENCB 19 // WHITE
#define PWM  3
#define IN2 2
#define IN1 4

int pwm;
int in1;
int in2;
volatile unsigned long  count = 0;
unsigned long copyCount = 0;

unsigned long lastRead = 0;
unsigned long interval = 1000;//one second

void setup()
{
  Serial.begin(115200);
  Serial.println("start...");

   pinMode(PWM,OUTPUT);
  pinMode(IN1,OUTPUT);
  pinMode(IN2,OUTPUT);
  
  //interrupt on pin3
  pinMode(3,INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(3),isrCount,RISING);
}

void loop()
{
      digitalWrite(in1,HIGH);
    digitalWrite(in2,LOW);
    analogWrite(pwm,255);
  if (millis() - lastRead >= interval) //read interrupt count every second
  {
    lastRead  += interval;
    // disable interrupts,make copy of count,reenable interrupts
    noInterrupts();
    copyCount = count;
    count = 0;
    interrupts();
    //use copyCount for all calulations and actions in loop
    Serial.println(copyCount);
  }
}

void isrCount()
{
  count++;
}

1 Like

You can't have the motor pwm and the interrupt input on the same pin3.
I didn't adjust the simple code for the mega and your wiring, so just use

//attachInterrupt(digitalPinToInterrupt(3),isrCount,RISING);
attachInterrupt(digitalPinToInterrupt(ENCA),isrCount,RISING);
1 Like

Seems like it really is 5736 pulses per rev.

Yes, with gearing of 478:1 that's 12 pulses per revolution of the motor with the quadrature encoder reading algorithm which only triggers on one edge of one pulse.

I think 12 is a standard count for many of the common gear motors.

1 Like

I'm still having trouble, I adjusted

 pinMode(18,INPUT_PULLUP);

and still no motion

yeah confirmed by marking the fixture and setting the target to 5736 it stops back on the mark after a full rotation

1 Like

Seems like you have control. How is the speed? How long does it take to make the revolution?

Did you get rid of this line?

Some of the earlier code had pwm output on pin5. What is the pin you are using for the motor controller?

this is what i have currently

#include <util/atomic.h> // For the ATOMIC_BLOCK macro

#define ENCA 18 // YELLOW
#define ENCB 19 // WHITE
#define PWM  3
#define IN2 2
#define IN1 4

int pwm;
int in1;
int in2;
volatile unsigned long  count = 0;
unsigned long copyCount = 0;

unsigned long lastRead = 0;
unsigned long interval = 1000;//one second

void setup()
{
  Serial.begin(115200);
  Serial.println("start...");

   pinMode(PWM,OUTPUT);
  pinMode(IN1,OUTPUT);
  pinMode(IN2,OUTPUT);
  pinMode(ENCA,INPUT);  
  //interrupt on pin3
  pinMode(18,INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(ENCA),isrCount,RISING);
}

void loop()
{
      digitalWrite(in1,HIGH);
    digitalWrite(in2,LOW);
    analogWrite(pwm,100);
  if (millis() - lastRead >= interval) //read interrupt count every second
  {
    lastRead  += interval;
    // disable interrupts,make copy of count,reenable interrupts
    noInterrupts();
    copyCount = count;
    count = 0;
    interrupts();
    //use copyCount for all calulations and actions in loop
    Serial.println(copyCount);
  }
}

void isrCount()
{
  count++;
}

it takes approx. 1min 9 sec per rotation

Are you saying that you get the motor to turn in your earlier code with this,
analogWrite(pwm,100);
but not in the version you posted?

The motor turning or not should not be dependent upon the encoder reading. If the test code you just posted does not turn the motor, can you post the code which does.

this is the current code I'm running to do the tests, its based off the tutorial with changes to the pins for the mega as well as some of the changes that i have been talking to DaveX with. It has a lot of unnecessary stuff in it, I'm trying to widdle a code down to bare bones, one direction of rotation with a fixed repeatable distance.

the motor also seams to be rotating really slow cause in the description of the motor when i bought it said i should be able to get10rpm out of it.

#include <util/atomic.h> // For the ATOMIC_BLOCK macro

#define ENCA 18 // YELLOW
#define ENCB 19 // WHITE
#define PWM  3
#define IN2 2
#define IN1 4

volatile int posi = 0; // specify posi as volatile: https://www.arduino.cc/reference/en/language/variables/variable-scope-qualifiers/volatile/
long prevT = 0;
float eprev = 0;
float eintegral = 0;

void setup() {
  Serial.begin(9600);
  pinMode(ENCA,INPUT);
  pinMode(ENCB,INPUT);
  attachInterrupt(digitalPinToInterrupt(ENCA),readEncoder,RISING);
  
  pinMode(PWM,OUTPUT);
  pinMode(IN1,OUTPUT);
  pinMode(IN2,OUTPUT);
  
  Serial.println("target pos");
}

void loop() {

  // set target position
  int target = 5736;
  //int target = 6000*sin(1.0* prevT/1e6);

  // PID constants
  float kp = 1;
  float kd = 0.025;
  float ki = 0.0;

  // time difference
  long currT = micros();
  float deltaT = ((float) (currT - prevT))/( 1.0e6 );
  prevT = currT;

  // Read the position in an atomic block to avoid a potential
  // misread if the interrupt coincides with this code running
  // see: https://www.arduino.cc/reference/en/language/variables/variable-scope-qualifiers/volatile/
  int pos = 0; 
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    pos = posi;
  }
  
  // error
  int e = pos - target;

  // derivative
  float dedt = (e-eprev)/(deltaT);

  // integral
  eintegral = eintegral + e*deltaT;

  // control signal
  float u = kp*e + kd*dedt + ki*eintegral;

  // motor power
   int pwr = 255 ;

  // motor direction
    int dir = 1;
  if(u<0){
    dir = -1;
  }

  // signal the motor
  setMotor(dir,pwr,PWM,IN1,IN2);


  // store previous error
  eprev = e;

  Serial.print(target);
  Serial.print(" ");
  Serial.print(pos);
  Serial.println();
}

void setMotor(int dir, int pwmVal, int pwm, int in1, int in2){
  analogWrite(pwm,pwmVal);
  if(dir == 1){
    digitalWrite(in1,HIGH);
    digitalWrite(in2,LOW);
  }
  else if(dir == -1){
    digitalWrite(in1,LOW);
    digitalWrite(in2,HIGH);
  }
  else{
    digitalWrite(in1,LOW);
    digitalWrite(in2,LOW);
  }  
}

void readEncoder(){
  int b = digitalRead(ENCB);
  if(b > 0){
    posi++;
  }
  else{
    posi--;
  }
}

Then something like:

int target = 5736*sin(2.0*3.14154/90/2 * prevT/1e6); 

Should scan back and forth 1 revolution every 180 seconds.

Still, setting a moving target with the sin is a bit of a distraction.

To move the target an angle, pause for a time, and repeat, you could add something like this completely untested code:

// timing and configuration variables:
static unsigned long lastPosMs = 0;
static bool pausingInPosition = false;
const unsigned long pauseInterval = 1000UL *60;
const int advanceAngle = 37.5; // degrees
...
// oneshot for reaching position:
if (!pausingInPosition && target == pos){ 
   pausingInPosition = true;
   lastPauseMs = millis(); 
   //... any other things to do once, you reach position
}

// 
if (pausingInPosition && 
    millis() - lastPosMs >= pauseInterval){ 
        // finished pausing
        target = target + 5736* advanceAngle/360; // advance
        pausingInPosition = false;
    } 
}

if(pausingInPosition){
  ; // other actions to do repeatedly while paused 
}

Non-blocking state-machine wise, target,pos pausingInPosition and (millis() - lastPosMs) would be your state variables, and loop should run pretty quick.

Oh, with

it doesn't look like you are using the PID. Maybe that's why it felt like it was doing something when it stopped. You might consider:

   int pwr = abs(constrain(u,-255,255)) ;

which would work with the PID code to taper power down when you get close to target.

I really appreciate the help!

I tried the

but the motor wont even start up I think the ramping of power isn't enough to rotate the gears.

How would i just make the motor rotate one complete rotation and stop? Then maybe we can figure out why the speed of the motor is so low.

target = 5376;

...should run a complete turn and stop.

If you periodically increase target by 5736, you should get periodic turns.

One issue is that you are using nice non-blocking code, as in BlinkWithoutDelay, which runs through loop() very quickly to update the PID, direction, and other things. Adding a delay() or a for loop would break that, so you need to have some event like a button press or timer that says when to start another turn.

My untested code in Programing motor with encoder - #33 by DaveX was trying to run a 1/4 turn, then pause, then cycle again. You could increase change the advanceAngle to 360 and the pauseInterval to 3600UL*1000 to make it full turns an hour apart.

Hmm. It looked like the working code was pwr=255 and dir=-1/1. I wonder what the u from your PID is giving you? With kp = 1 and target = 5376, it should be u=5376, and then with the pwr = abs(constrain(...)) it should be 255 until you are within 17 degrees of a full rotation.

I'm not clear what the need is for most of the code you're using. The stuff from post #29 seems the most promising. Did it work?

1 Like

it takes approx. 1min 9 sec per rotation

the motor also seams to be rotating really slow cause in the description of the motor when i bought it said i should be able to get10rpm out of it.

Can you please provide a schematic as to how everything is powered?

What are you using for the 12v motor supply? The l298n can have a significant voltage drop.

I have the L298N hooked directly to a 12v power supply, I don't have the software to do schematics and I can't really buy or download random software because its a work computer

The intentions is to get the code down to something that as clean as possible, but I couldn't get the code from #29 to work. That's why I have ben using the other one because its from the tutorial

I believe the tutorial hade pwr=100 but I found somewhere that you can put pwr = (anywhere between 0-255) so i switched it to 255 thinking it would make the motor faster