Hi @akilou23 ,
this was a fun exercise for me to write the code for this application.
My code makes use of the NewEncoder-library from user @gfvalvo
All the details to detect encoder-pin-changes are done inside the library
to install this library follow this tutorial
This is void loop() of the code
void loop() {
DetectAndQueClicks();
CreateForwardPulses();
CreateBackwardPulses();
}
Pretty short. Because the code is well organised in functions.
To qoute the comment about loop()
void loop() iterates at high speed, because the timing is NON-blocking
this enables to increment a counter-variable for each encoder-click
this can be seen as some kind of queing up the incoming encoder-clicks
creating pulses is done independent from this queing up
you could say the clicks are dammed up
each time an encoder-click is detected the counter increments by 1 "++"
each time an output-pulse has finished the counter is decremented by 1 "--"
If you would turn the encoder into one direction for a long time the code would create pulses for a even longer time as the pulses must have a minimum-length
To avoid creating forward/backward-pulses in a mess inbetween each other
if you change encoder-rotationdirection qued up clicks of the opposite direction where decremented first until they are zero.
for demonstration purposes the constant pulseLength is set to 1000 milliseconds
reduce this pulseLength to the value you would like to have.
#include "Arduino.h"
#include "NewEncoder.h"
const byte EncChA_Pin = 2;
const byte EncChB_Pin = 3;
const int minVal = -20;
const int maxVal = 20;
const int startVal = 0;
const byte forwardPulsePin = 8;
const byte backwardPulsePin = 9;
const unsigned long pulseLength = 1000;
// Pins 2 and 3 should work for many processors, including Uno. See README for meaning of constructor arguments.
// Use FULL_PULSE for encoders that produce one complete quadrature pulse per detnet, such as: https://www.adafruit.com/product/377
// Use HALF_PULSE for endoders that produce one complete quadrature pulse for every two detents, such as: https://www.mouser.com/ProductDetail/alps/ec11e15244g1/?qs=YMSFtX0bdJDiV4LBO61anw==&countrycode=US¤cycode=USD
NewEncoder myEncoderObject(EncChA_Pin, EncChB_Pin, minVal, maxVal, startVal, FULL_PULSE);
NewEncoder::EncoderState myCurrentEncoderState;
int16_t currentValue;
int16_t prevEncoderValue;
int forwardCount = 0;
unsigned long forwartPulseStarted = 0;
int backwardCount = 0;
unsigned long backwardPulseStarted = 0;
// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
unsigned long currentMillis = millis();
if ( currentMillis - startOfPeriod >= TimePeriod ) {
// more time than TimePeriod has elapsed since last time if-condition was true
startOfPeriod = currentMillis; // a new period starts right here so set new starttime
return true;
}
else return false; // actual TimePeriod is NOT yet over
}
void setup() {
digitalWrite(forwardPulsePin, HIGH);
pinMode(forwardPulsePin, OUTPUT);
digitalWrite(backwardPulsePin, HIGH);
pinMode(backwardPulsePin, OUTPUT);
// myEncState is a variable of type EncoderState
// EncoderState is a structured variable that has two "simple" variables
// .currentValue which is type int16_t
// (16 bit signed integer valuerange -36767 to 36767)
// currentValue counts up / down with each pulse created through rotating the encoder
// and
// .currentClick which is of type "EncoderClick"
// the variable type "EncoderClick" can have just 3 values
// NoClick, DownClick, UpClick where "click" means a "pulse" created through rotating the encoder
NewEncoder::EncoderState myEncState;
Serial.begin(115200);
Serial.println( F("Setup-Start") );
if (!myEncoderObject.begin()) {
Serial.println("Encoder Failed to Start. Check pin assignments and available interrupts. Aborting.");
while (1) {
yield();
}
} else {
// store values of currentValue and EncoderClick into variable myEncState
myEncoderObject.getState(myEncState);
Serial.print("Encoder Successfully Started at value = ");
prevEncoderValue = myEncState.currentValue;
Serial.println(prevEncoderValue);
}
}
void DetectAndQueClicks() {
myEncoderObject.getState(myCurrentEncoderState);
switch (myCurrentEncoderState.currentClick) {
case NewEncoder::UpClick: // UpClick is reset to NoClick on reading it
Serial.println("UpClick");
if (backwardCount == 0) { // if no backward-clicks are queued up
forwardCount++; // whenever an UpClick occures increment
}
else { // if backward-clicks are queued up decrement backwardCount
if (backwardCount > 0) {
backwardCount--;
Serial.println("backwardCount--");
}
}
break;
case NewEncoder::DownClick: // DownClick is reset to NoClick on reading it
Serial.println("DownClick");
if (forwardCount == 0) {
backwardCount++; // whenever an DownClick occures increment
}
else {
if (forwardCount > 0) {
forwardCount--;
Serial.println("forwardCount--");
}
}
break;
}
}
void CreateForwardPulses() {
static boolean CreatePulse;
if (!CreatePulse && forwardCount > 0) { // as long as forward clicks have been occurred
forwartPulseStarted = millis(); // store actual timestamp
CreatePulse = true; // set flag-variable
digitalWrite(forwardPulsePin, LOW); // start low-pulse
}
if (CreatePulse) {
if ( TimePeriodIsOver(forwartPulseStarted, pulseLength) ) { // check if the number of milliseconds stored in pulseLength have passed by
// if the number of milliseconds stored in pulseLength have passed by
forwardCount--;
CreatePulse = false; // reset flagvariable to false
digitalWrite(forwardPulsePin, HIGH); // end low-pulse
Serial.println( F("forward pulse finished") );
}
}
}
void CreateBackwardPulses() {
static boolean CreatePulse;
if (!CreatePulse && backwardCount > 0) { // as long as forward clicks have been occurred
backwardPulseStarted = millis(); // store actual timestamp
CreatePulse = true; // set flag-variable
digitalWrite(backwardPulsePin, LOW); // start low-pulse
}
if (CreatePulse) {
if ( TimePeriodIsOver(backwardPulseStarted, pulseLength) ) { // check if the number of milliseconds stored in pulseLength have passed by
// if the number of milliseconds stored in pulseLength have passed by
backwardCount--;
CreatePulse = false; // reset flagvariable to false
digitalWrite(backwardPulsePin, HIGH); // end low-pulse
Serial.println( F("backward pulse finished") );
}
}
}
// void loop() iterates at high speed
//because the timing is NON-blocking
// this enables to increment a counter-variable for each encoder-click
// this can be seen as some kind of queing up the incoming encoder-clicks
// creating pulses is done independent from this queing up
// you could say the clicks are dammed up
// each time an encoder-click is detected the counter increments by 1
// each time an output-pulse has finished the counter is decremented by 1
void loop() {
DetectAndQueClicks();
CreateForwardPulses();
CreateBackwardPulses();
}
best regards Stefan