Reading index "Z" pulse

I am a noob. Just figuring out arduino code. ( I have some very rusty c++ experience). I need to drive a motor, and count/display the total revs. Ultimately I need a android app (IOT I think?) That uses a single stop/start button, and direction button, but that is Phase II.

Ultimately I would like to exit the loop once the motor has moved a fixed number of revs, but right now I do not know what that count is. The Z pulse input is for troubleshooting, till I know the rev #. Ultimately I will need help reading the z pulse count, (With an if / then?) and then stopping motion. I have the motor code running OK now. Could more optimization.

The (brushless) motor outputs a once per rev "Z" index pulse. So this is NOT a quadrature encoder. I have found several "encoder" examples on the forum. I hesitate to upload my code yet, as it is very cluttered with commented out attempts. I also found the Jmotor library that supports single pulse encoders, but cannot get it to compile for my R4 UNO Wifi Arduino.

Attached is a real simple example for reading a quad encoder. Can someone PLEASE explain where the ISR is, (I "think" it is the attachInterupt) and where to insert the rotor value code? I do not care how slow it runs, so not sure I need to only output the value every 10 seconds. The motor only turns for about 2 seconds.

I have made a number of attempts, and I clearly do not know what I am doing. Included is the last post on the thread. I also do not understand how to "format the post" where it makes it more readable & highlights the code in gray...
__
// encoder pulse read

byte A = 2; // One quadrature pin
byte B = 3; // the other quadrature pin
volatile int Rotor = 0;

void setup() {

  // set DIO pins
  pinMode(A, INPUT);
  pinMode(B, INPUT);

  // Attach interrupt to pin A
  attachInterrupt(0, UpdateRotation, FALLING);

  // Use serial port to keep user informed of rotation
  Serial.begin(57600);
  }


  void loop() {
  }

  void UpdateRotation() {
        // This is the subroutine that runs every time pin 2
        // switches from high to low.

        if (digitalRead(B)) {
              Rotor++;
              } else {
              Rotor--;
        }
        Serial.println(Rotor, DEC);

}

No. Exactly the opposite. The code to read the encoder state and update Rotor goes in the InteruptServiceRoutine. The code to print out the value of Rotor goes in loop(). The value should not be printed on every pass through loop, obviously, but every 10 seconds or so would be OK.
__

attachInterrupt() hides the interrupt enabling and handling code for the particular microcontroller you are using - hides complexity and helps if you are porting code between micros with different processors
this meansures pulse width using a ESP32

// ESP32 determine pulse width in microseconds

#define RXPIN 16

volatile int pin = 0;
volatile long timer = 0;
long average = 0.0;

// interrupt handler - noet pin value and time in microseconds
void IRAM_ATTR handleInterrupt2() {
  pin = digitalRead(RXPIN);
  timer = micros();
}

void setup() {
  Serial.begin(115200);
  pinMode(RXPIN, INPUT_PULLDOWN);
  attachInterrupt(digitalPinToInterrupt(RXPIN), handleInterrupt2, CHANGE);
}

// calculat pulse widtch in microseconds
void loop() {
  static int lastPin = -1;
  static long lastTimer = 0, counter = 0;
  static long printer = millis();
  // if had inperrupt on pin 16 get pulse widtch time
  if (pin != lastPin) {
    lastPin = pin;
    // add pulse width time in microseconds to average and increment counter
    if (lastTimer) average += (timer - lastTimer);
    counter++;
    lastTimer = timer;
  }
  // after 10 seconds print average pulse widtch time
  if (millis() - printer > 10000) {
    Serial.println((float)average / counter);  // print average pulse width
    lastTimer =  counter = average= 0;  // reset initialse values
    printer = millis();
  }
}

some results from serial monitor

serial monitor output 100microsecond period square wave

50.00
50.00
50.00
50.00
50.00
50.00
50.00

serial monitor output 25microsecond period square wave

12.74
12.74
12.74
12.74
12.74
12.74
12.74

10microsecond period square wave and output is garbage - clearly missing changes

if you are using a QEI encoder on the ESP32 have a look at esp32encoder library

EDIT: also avoid printing insides a interrupt service routine, e.g. UpdateRotation() in post 1, it can crash the program causing resets
use volatile variables (pin and timer in above program) to pass data from the ISR to loop() where it can be printed

Please edit your post, select all code and click the <CODE/> button; next save your post. This will apply so called code tags which make your code easier to read and copy and the forum software will display it correctly.


How many revs per second does your encoder generate?
Which board are you using?

I just found the button. I am using Arduino1.8.19.
The motor runs at about 6,000 rpm, but the gearhead is a 100:1 reduction so the actual shaft rpm is ~60rpm. The index pulse is the 6,000 rpm.
I have a R4 UNO Wifi.

I "think" I got past the ISR issues, but am now getting a VERY different error from the Arduino.h library. I only get this error with one set of code. I am going to re-post this error under a different title. Use code tags to format code for the forum

See below:


Arduino: 1.8.19 (Windows 7), Board: "Arduino UNO R4 WiFi"

In file included from 
...\CODE\brushless_pwmISR\brushless_pwmISR.ino:3:0:

...\brushless_pwmISR\brushless_pwmISR.ino: In function 'void setup()':

....\AppData\Local\Arduino15\packages\arduino\hardware\renesas_uno\1.3.2\cores\arduino/Arduino.h:113:16: error: expected ';' before '_UART1_'

 #define Serial _UART1_

                ^

...\CODE\brushless_pwmISR\brushless_pwmISR.ino:29:5: note: in expansion of macro 'Serial'

     Serial.begin(9600);

     ^~~~~~

exit status 1

Error compiling for board Arduino UNO R4 WiFi.

Hi, @1claya
Welcome to the forum.

Please post your current code.

Thanks.. Tom.... :smiley: :+1: :coffee: :australia:

//brushless motor control

#include <Arduino.h>;

int motorPin5 =  5;    // PWM speed
int motorPin6 =  6;    // direction change
const int buttonPin2 = 2;     // the number of the pushbutton pin
int buttonState = 0;         // variable for reading the direction switch
// int index = 0;
int ledPin13 = 13;
int ledPin12 = 12;
const int indexPin3 = 3; //revcount pulse
volatile long rotationCount = 0; // Variable to store the number of rotations
// volatile long Rotor = 0;
volatile bool ledState = false;  // Variable to store the LED state
volatile bool newZPulse = false; // Variable to store the Z pulse

// The setup() method runs once, when the sketch starts

void setup(){                

    pinMode(indexPin3, INPUT_PULLUP);  // initialize the digital pins as an output:      
    pinMode(motorPin5, OUTPUT); 
    pinMode(motorPin6, OUTPUT); 
    pinMode(ledPin13, OUTPUT);
    pinMode(ledPin12, OUTPUT);
//    pinMode(index, OUTPUT);       // initialize the index pin as an input:
    digitalWrite(ledPin13, LOW)
    Serial.begin(9600); 
    attachInterrupt(digitalPinToInterrupt(indexPin3), handleIndexPulse, RISING); // ISR, Attach interrupt
}

void loop() {
  noInterrupts();
  long localRotationCount = rotationCount;
  bool localLedState = ledState;
  interrupts();
     
  digitalWrite(motorPin6, LOW); //motor stopped
  digitalWrite(ledPin13, HIGH);
    rotateRight(200, 2000);
    buttonState = digitalRead(buttonPin2);
    
        digitalWrite(motorPin6, LOW); //stop motor, PWM low

    digitalWrite(ledPin13, LOW); //led off 
         digitalWrite(motorPin6, LOW); //direction change

         if(newZPulse){
      newZPulse = false; //if there is a new pulse
      
     Serial.print(", Rotations: ");
  Serial.print(localRotationCount);
        digitalWrite(ledPin12, HIGH );
    delay(50);    // Delay a little bit to avoid bouncing
    digitalWrite(ledPin12, LOW);
  }   
    rotateLeft(200, 2000);
        digitalWrite(motorPin5, LOW); //stop motor, PWM low
}
  void  handleIndexPulse(){  
    rotationCount++;   // Increment rotation count
    newZPulse = true;
       ledState = !ledState; // Update the LED state   
}

void rotateLeft(int speedOfRotate, int length){
  analogWrite(motorPin5, speedOfRotate); //rotates motor
  digitalWrite(motorPin6, HIGH);    // set the Pin motorPin6 LOW
  delay(length); //waits
  digitalWrite(motorPin5, HIGH);    // set the Pin motorPin5 LOW
}

void rotateRight(int speedOfRotate, int length){
  analogWrite(motorPin5, speedOfRotate); //rotates motor
  digitalWrite(motorPin5, LOW);    // set the Pin motorPin5 LOW
  delay(length); //waits
  digitalWrite(motorPin6, LOW);    // set the Pin motorPin6 HIGH
}

Tom,
I was able to fix the code and get it to compile. No idea what I fixed. You have a chance to look at the code and help me understand what I might have done wrong to cause the compiler error. I am so new at this. It may have been a misplaced curly bracket??

I have a revised version, that I am having a problem with a while statement now. Is there a way to exit a loop once a while becomes false? I want the loop to exit when the encoder count gets to a value. It just keeps looping.

Below is segments of revised code. The motor spins forward & stops using the rotateRight function, but relies on a delay. I would prefer the rotationCount to stop the motor when it reaches maxrev. Why is the analogWrite, (MotorPwmPin, LOW) line ignored after rotationCount >maxrev?
Also, what is the purpose of the newZPulse boolean? Someone posted this in an example of reading pulses during ISR.

I edited out a lot of switch/button code, extraneous debugging code.

#include <Arduino.h>

int motorPwmPin =  5;    // PWM speed
int motorDirectionPin =  6;    // direction change pin

const int indexPin3 = 3; //revcount pulse from motor
const int maxrev = 411; //revcount max
volatile bool ledState = false;  // Variable to store the LED state
volatile long rotationCount = 0; // Variable to store the number of rotations
volatile bool newZPulse = false;

void setup(){                
  // initialize the digital pins as an output:
    pinMode(indexPin3, INPUT_PULLUP);            
    pinMode(motorPwmPin, OUTPUT); 
    pinMode(motorDirectionPin, OUTPUT);

    Serial.begin(115200);     
    attachInterrupt(digitalPinToInterrupt(indexPin3), handleIndexPulse, RISING); 

// ISR, Attach interrupt
}
void loop() {
  noInterrupts();
  long localRotationCount = rotationCount;
  bool localLedState = ledState;
  interrupts();

    rotateRight(255, 400); //extend rotation (speed, time (ms))
        if(newZPulse){
      newZPulse = false; //if there is a new pulse << Why this line
   
    if (rotationCount > maxrev){   //<<how do I get the motor to stop when this 

count is reached
      rotationCount = 0; //Resets rev counter, future version decrements back to 

0 from rotationCount
     
//  analogWrite(motorPwmPin, LOW);    // STOP <<Prefer to read rotationCount and 

stop when > maxrev <<This line is ignored

  void handleIndexPulse(){ // ISR, Attach interrupt 
    rotationCount++;   // increment rotation count from previous count
    newZPulse = true;  <<what does this line do?

void rotateRight(int speedOfRotate, int length){
  analogWrite(motorPwmPin, speedOfRotate); //PWM
  digitalWrite(motorDirectionPin, LOW); // extend direction    
  delay(length); //motor runs xx milliseconds << same as above 
  analogWrite(motorPwmPin, LOW);    //STOP <<Changed from digitalWrite<< This 
stops motor, but only when using the delay.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.