I am using the Encoder Library from PJRC Encoder Library, for Measuring Quadarature Encoded Position or Rotation Signals, which works wonderful. The library is the first one mentioned on the Arduino Reading Rotary Encoders page. I use the HONEYWELL HLC2701 IR optic encoders with A connected to an interrupt and B connected to a non interrupt input. There is no bouncing or any other interference.
I have a class PID, which takes as one of its arguments a pointer to an encoder instance. The PID is very similar to the Arduino PID_v1 library. Instead of taking its input from a variable, it reads the encoder. The following C++ code works great:
PID.h
-----------------------------------------------------------
class PID
{
public:
PID(Encoder* encoder, Motor* motor, double* setpoint, double kp, double ki, double kd, int direction);
bool Compute();
...
private:
Encoder *_input;
Motor *_output;
double *_setpoint;
...
PID.cpp
-----------------------------------------------------------
PID::PID(Encoder* encoder, Motor* motor, double* setpoint,
double Kp, double Ki, double Kd, int direction)
{
_output = motor;
_input = encoder;
...
bool PID::Compute()
{
...
double input = 0;
double error = 0;
...
if (dt >= _sampletime) {
// Read encoder and immediately reset it to 0.
input = float(_input->read());
_input->write(0);
setpoint = *_setpoint;
error = setpoint - input;
...
Every time the PID loop is executed, the encoder is read as input and the error is computed. Immediatly after reading the encoder, it is reset to 0 for counting the pulses for the next loop. This gives good results.
Problem is, the encoder is reset every time and I can not use the encoder readings directly to calculate the distance the motor travelled so far. So I inserted the following code:
bool PID::Compute()
{
...
double input = 0;
double error = 0;
double output = 0;
long encoder = 0;
if (dt >= _sampletime) {
// Read encoder and subtract previous encoder reading to calculate input.
encoder = _input->read();
input = float(encoder - _lastencoder);
_lastencoder = encoder;
...
The _lastencoder variable is a long and initialized to zero on startup.
Now the encoder returns weird numbers when I turn the motor just a little bit, maybe one encoder pulse or so:
encoder, _lastencoder, input :3 3 0.00
encoder, _lastencoder, input :1073807398 3 1073807360.00
encoder, _lastencoder, input :3 1073807398 -1073807360.00
encoder, _lastencoder, input :1073807398 3 1073807360.00
encoder, _lastencoder, input :3 1073807398 -1073807360.00
encoder, _lastencoder, input :3 3 0.00
So, could anybody please tell me what goes wrong?
TIA
PS
The encoder library is a little weird because all code is in the header file and not in the cpp file. The reader function looks like this:
#ifdef ENCODER_USE_INTERRUPTS
inline int32_t read() {
if (interrupts_in_use < 2) {
noInterrupts();
update(&encoder);
} else {
noInterrupts();
}
int32_t ret = encoder.position;
interrupts();
return ret;
}
inline void write(int32_t p) {
noInterrupts();
encoder.position = p;
interrupts();
}
#else
inline int32_t read() {
update(&encoder);
return encoder.position;
}
inline void write(int32_t p) {
encoder.position = p;
}
#endif