Hi - I have started a project to build a model train controller for my father. I am building 3 x speed controllers and various points controllers, level crossing sensors etc.
The speed controllers will be using a PWM motor control module form Pololu that I need to drive. I have decided to use rotary encoders for the user input to the speed controllers and have prototyped the hardware debounce using a RC circuit and inverting Schmitt trigger (74HC14) for each encoder output which is working perfectly with the Buxtronix state table rotary encoder library -
arduino/libraries/Rotary at master · buxtronix/arduino · GitHub.
The code I have written so far is just test code and absolutely zero refactoring has taken place. I know it needs a lot of work !!!
I have been going at this for a few days now and I am stuck on creating a non-linear step for each corresponding rotary encoder step based on rotation speed of the encoder.
I basically want to increase at a very fine step for slow turns but as the user increases the turn rate I want to ramp up the motor speed in a non-linear fashion.
I haven't got the Pololu speed controller as yet so I am just mocking this up with numbers (0-254) which will eventually used with analogWrite() to generate a PWM signal.
The code runs Timer1 on a Teensy++ 2.0 (will be moving to a more powerful Teensy 3.6 later) at 1kHz and attaches a timer interrupt to poll the rotary encoder. If a state change is detected the main loop will then look at the value and every 50ms looks at how many steps the encoder has incremented / decremented and divides by time to get an encoder speed.
This is where I am stuck ! I need to turn this speed into a step value other than "1". If the speed of user rotation increases I want to step up faster.
The calculations I am using may be totally wrong, I am just going around in circles here which is why I am asking for some help.
#include <Rotary.h>
#include <TimerOne.h>
Rotary rotary1 = Rotary(0, 1);
void setup() {
Timer1.initialize(1000); //1000us = 1ms = 0.001s = 1kHz
Timer1.attachInterrupt(process_encoders);
Serial.begin(57600);
}
typedef struct {
int state = 0;
volatile int value = 0;
long time_old;
int value_old;
float encoder_speed;
long steps;
int speed_value = 0; //speed value to be used for motor controller PWM
byte direction = 1; //0 = CW / 1 = CCW
} encoder_struct;
encoder_struct encoder1;
int speed_check_period = 50; //millis
int pb_counter = 0;
void loop() {
if (encoder1.state == 1) { //only run if the timer interrupt has flagged a state change
//Serial.print("Value: ");
//Serial.println(encoder1.value);
//check speed every 50ms rather than on each loop
cli(); //stop interrupts happening before calculation
if((millis() - encoder1.time_old) >= speed_check_period) { //millis rollover ??
encoder1.steps = abs(encoder1.value - encoder1.value_old); //number of encoder steps triggered since last speed check
//Serial.print("Steps since last check: ");
//Serial.println(encoder1.steps);
encoder1.encoder_speed = (((float)encoder1.steps / (float)(millis() - encoder1.time_old)) * 1000); //using a 1000 multiplier
Serial.print("Encoder Speed: ");
Serial.println(encoder1.encoder_speed);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// create a non-linear step value based on speed to increment encoder1.speed_value
if(encoder1.direction == 1) { //turning clockwise
encoder1.speed_value = constrain((encoder1.speed_value + 1),0,254);
} else {
encoder1.speed_value = constrain((encoder1.speed_value - 1),0,254);
}
Serial.print("Speed1: ");
Serial.println(encoder1.speed_value);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
encoder1.time_old = millis();
encoder1.value_old = encoder1.value;
}
sei(); //restart interrupts
encoder1.state = 0;
}
}
static void process_encoders() { //read encoder values using timer interrupt
unsigned char result = rotary1.process();
if (result == DIR_CW) {
encoder1.value++;
encoder1.direction = 1;
encoder1.state = 1; //flag the struct as being changed
} else if (result == DIR_CCW) {
encoder1.value--;
encoder1.direction = 0;
encoder1.state = 1; //flag the struct as being changed
}
}
The serial output from some testing is below, the smaller "Encoder Speed" values are when I turn the encoder slowly and then I twist it as fast as I can to get the upper value. Each time I am only increasing "Speed1" by one as I don't yet have code to smoothly increase to 254 based on "Encoder Speed":
Encoder Speed: 0.39
Speed1: 1
Encoder Speed: 1.86
Speed1: 2
Encoder Speed: 1.62
Speed1: 3
Encoder Speed: 1.41
Speed1: 4
Encoder Speed: 0.95
Speed1: 5
Encoder Speed: 22.22
Speed1: 6
Encoder Speed: 1.16
Speed1: 7
Encoder Speed: 34.48
Speed1: 8
Encoder Speed: 53.57
Speed1: 9
Encoder Speed: 75.47
Speed1: 10
Here is my prototype !
