NXT Motor Encoder

Hi all,

I haven't had my Arduino terribly long, but one of my first goals was to get it talking to my Lego motors as to broaden my horizons. However, I have become frustrated quickly. Here is the code I'm using:

/*

Motor Test

*/

volatile int tack0 = 0;
volatile int tack1 = 0;

volatile int degree = 0;
volatile int count = 0;
int speedPin = 9;

int tacho0 = 2;
int tacho1 = 13;

int motor1APin = 10;
int motor2APin = 8;

// include the library code:
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 6, 5, 4, 3);

void setup() {
  // set up the LCD's number of rows and columns: 
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.print("hello, world!");

  pinMode(speedPin, OUTPUT);
  
  pinMode(motor1APin, OUTPUT);
  pinMode(tacho0, INPUT);
  digitalWrite(tacho0, HIGH);
  pinMode(motor2APin, OUTPUT);
  pinMode(tacho1, INPUT);
  digitalWrite(tacho1, HIGH);
  
  attachInterrupt(0, encode, RISING);
}

void loop() {
}

void encode() {
  tack1 = digitalRead(tacho1);
  tack0 = digitalRead(tacho0);
  count++;
  if (tack0!=1) {
    tack0 = 1;
    lcd.setCursor(14,1);
    lcd.print("!");
  }
  if (tack0 == tack1) {
    degree++;
  } else {
    degree--;
  }
  lcd.print("                ");
  lcd.setCursor(0,1);
  lcd.print(degree);
  lcd.setCursor(6,1);
  lcd.print(count);*/
  lcd.setCursor(0,1);
  lcd.print("            ");
  lcd.setCursor(0,1);
  lcd.print(degree);
  lcd.setCursor(6,1);
  lcd.print(count);
  lcd.setCursor(13,1);
  lcd.print(tack0);
  lcd.setCursor(15,1);
  lcd.print(tack1);
}

Basically, what I've noticed is that if I spin the motor too quickly, it doesn't keep track of the rotations very well (for instance, tack0 doesn't always equal 1...). Furthermore, the interrupts seem to all register, but if I turn it too quickly (on the NXT, the encoder records every degree of rotation), it looks like the other digital signal has already switched to something else, so that it reads that the motor is turning in the wrong direction.

I've only got the one motor attached, and I've tried to streamline the code so that it reads in the signals as soon as an interrupt is called, giving it minimal time to change. Is this something the arduino just can't handle? (I'm not turning it more than a couple RPS)

Additionally, I never figured it out, but during my debugging, it seemed that the interrupt would often only be called in twos... that is, the "count" variable would only be an even number... can't figure out why.

Any advice/suggestions would be greatly appreciated.

Thanks!

Number one, you have waaaay too much in your interrupt routine - you need to pare that down to as little as possible (especially removing the LCD print routines). Interrupt handler routines need to be very lightweight; put your print routines in loop(), for example.

You also didn't post a schematic - while I think the above is what is causing most of your problems (ie, by printing stuff, etc in your interrupt handler, you miss other interrupts) - your schematic might reveal other issues.

I am not really the one to discuss this with, though (don't have any experience in the area) - I just noticed the above because others have had similar issues with sensors and encoders missing interrupts, or acting weird, and invariably the answer (given by others with more experience than me) is "simplify the interrupt handler".

:slight_smile:

Thanks for the advice. Streamlining the interrupt seems a sensible thing to do. My new code looks like this:

volatile int degree = 0;
volatile int count = 0;

int speedPin = 9;

int tacho0 = 2;
int tacho1 = 13;

int motor1APin = 10;
int motor2APin = 8;

// include the library code:
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 6, 5, 4, 3);

void setup() {
  // set up the LCD's number of rows and columns: 
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.print("hello, world!");

  pinMode(speedPin, OUTPUT);
  
  pinMode(motor1APin, OUTPUT);
  pinMode(tacho0, INPUT);
  digitalWrite(tacho0, HIGH);
  pinMode(motor2APin, OUTPUT);
  pinMode(tacho1, INPUT);
  digitalWrite(tacho1, HIGH);
  
  attachInterrupt(0, encode, RISING);
}

void loop() {
  lcd.setCursor(0,1);
  lcd.print("            ");
  lcd.setCursor(0,1);
  lcd.print(degree);
  lcd.setCursor(6,1);
  lcd.print(count);
  delay(1);
}

void encode() {
  if (digitalRead(tacho1)==HIGH) {
    degree++;
  } else {
    degree--;
  }
  count++;
}

I can't imagine the interrupt procedure getting much leaner, except to take out the count increment, but that's how I check that things are being counted correctly.

And in fact, this does a decent job of counting "forward revolutions" (incrementing the degree variable), missing up to 15% of them at times, but when I turn the motor the other way, it is completely useless. It appears that the degree variable is being added and subtracted from as the interrupt is called during consistent reverse motion. I can't imagine why this would be since I am turning it at a very slow pace. It's hard to imagine I'm doing anything that the Arduino can't keep up with, interrupt-wise...

Also, what's the best way to draw up a schematic? Just Paint or something, or is there a good software available on Linux?

Thanks!

Speaking of schematics, I've been playing with some of the other encoder scripts from the playground, and still not having any luck. I'm getting suspicious that my encoder is not wired properly.

I am writing the pins they're connected to on the Arduino HIGH, but do I also need to "pull up" the pins otherwise? Because it's a Lego motor, I can't say exactly how the encoder is wired (I know it is optical) except that I feed it +5 V, and get back two TACH signals. It may use the same ground that the motor gets. Do I need to wire these other than straight to the Arduino interrupt pins?

Thanks again.

Thanks for the advice. Streamlining the interrupt seems a sensible thing to do. My new code looks like this:

That looks much better.

I can't imagine the interrupt procedure getting much leaner, except to take out the count increment, but that's how I check that things are being counted correctly.

That's what I was meaning with what I wrote - you picked it up quickly, so far so good.

:slight_smile:

And in fact, this does a decent job of counting "forward revolutions" (incrementing the degree variable), missing up to 15% of them at times, but when I turn the motor the other way, it is completely useless. It appears that the degree variable is being added and subtracted from as the interrupt is called during consistent reverse motion. I can't imagine why this would be since I am turning it at a very slow pace. It's hard to imagine I'm doing anything that the Arduino can't keep up with, interrupt-wise...

This should work; I am not an expert on this stuff, like I wrote before. I have heard that rather than trying to detect on "RISING" or "FALLING", that "CHANGE" works better - so you might try that.

Other than that, it might be your hookup - do you need pullup/pulldown resistors on the pin? Something else? What is the output voltage of the encoder, anyhow?

Also, what's the best way to draw up a schematic? Just Paint or something, or is there a good software available on Linux?

In a pinch, I have used everything from simple paint software in Linux (not Gimp - it isn't meant for such drawing - GNU Paint works OK) to vector editors (Open Office Drawing, for instance). The problem with these is the lack of "parts libraries", so you have to come up with your own scheme. Plus there is no standards or anything, no way to output anything other than the image or vector format, etc.

You can also try Fritzing - it is a pretty cool program for beginners, but it too has its own limitations, so keep that in mind.

There's also EagleCAD, which a lot of people use; the free version (they have one for Linux - it is free though, not Open Source) is fairly useful, but the board size is limited, and more "unlimited" versions (they sell different ones for different needs) get expensive quickly. But it has fairly good support, and a lot of people using it so advice and help is easy to find (not too mention part layouts).

Then theres gEDA:

http://www.gpleda.org/

From what I have been able to gather, if you are at all serious about schematic capture and PCB design under *nix - gEDA is what you should learn. Its learning curve is -very steep- (I have yet to do anything more than play with gschem, the schematic capture piece, but I found it easy to lay a simple h-bridge schematic out); but I have heard that if you can master the complete toolset, you can do just about anything (multi-layer PCBs with tons of SMT parts and schematics that hurt your eyes to look at).

Best of all - its completely open source; very complex designs have been done with, and a lot of part layouts are available (new parts aren't that hard to create, from what I have read).

Speaking of schematics, I've been playing with some of the other encoder scripts from the playground, and still not having any luck. I'm getting suspicious that my encoder is not wired properly.

Possibly, but we won't know that without a schematic - in the meantime, try the change to the attachInterrupt() I wrote above, then post a schematic if that doesn't help.

I am writing the pins they're connected to on the Arduino HIGH, but do I also need to "pull up" the pins otherwise? Because it's a Lego motor, I can't say exactly how the encoder is wired (I know it is optical) except that I feed it +5 V, and get back two TACH signals.

I honestly don't know; maybe? Have you measured the signal TACH output levels - are they 5V signals? Have you tried any of the Lego NXT forums for help (particularly the ones detailing homemade sensors and interfacing)? There might be some more info out there of that nature...

It may use the same ground that the motor gets. Do I need to wire these other than straight to the Arduino interrupt pins?

Maybe the two TACH outputs are actually a quadrature encoder with outputs 90 degrees out-of-phase? In that case, both would be connected to the interrupt/input pins, and the ground tied to the Arduino's ground. As far as pullups or pulldowns, I don't know. There is a lot of missing information here, mainly because of the closed nature of the LEGO part. You may need to do some more research on how the LEGO NXT motor and encoder is constructed/wired; perhaps someone has interfaced it to a PIC, BASIC Stamp, or even a PC via a parallel port - that might give some more info on how to hook it up to the Arduino.

Be careful in your experiments - don't fry your board!

Good luck, and I hope this is helping out (and I hope someone else here will post - because as I noted, I am out of my depths on this, and only going by what I have learned elsewhere - not necessarily what I have tried!)...

:wink:

Helping a lot, thanks. Not sure if it's the right thing to do but I'm trying pulling the pins to ground with 10k resistors -- not seeming to make any difference as of yet. I see the value of pulling them down, but what I'm not crystal clear about is the purpose of writing HIGH to the interrupt pins, so as to "pull them up". This is what I get for being a mechanical engineer....

The only thing I know about the NXT hardware is that they have Schmidt triggers on the outputs of the encoder (it is quadrature encoded) before the chip processes the two signals. Maybe this is really important for me... I have no way of telling how clean these signals are (I'm going to try to get to an oscilloscope that may tell me). Multimeter tells me they are low at 0.4 V and high at 5 V (what I give it, even though the NXT gives it 4.3 V). No appreciable difference in using CHANGE vs. RISING.

Currently, I'm really struggling because the behavior is extremely erratic right now. Sometimes the readings are nice and tight within 10% of the actual rotations, and sometimes they are orders of magnitude off. I really have no idea what is causing it to behave differently at different times (nothing is harder than debugging something that only sporadically doesn't work). First order of business is doing some soldering to header pins to make sure connections in the breadboard are more robust. We'll see if that improves consistency.

Next order of business is churning out that schematic. I'll save the complicated stuff for later... OpenOffice, here I come!

Thanks for the continued help. I'm really quite surprised there isn't a common/cheap decoder chip that is universally used to handle this kind of common task separate from the Arduino.

Am I risking burning up my Arduino? I think/hope what I'm doing is mostly benign....! I guess we'll know when I post the schematic!

Here's a geda schematic that hopefully makes some sense. I've haven't shown any pull-up/down from the tach pins of the lego motor; still not sure if I need them.

When everything is working at its best, the software still only correctly counts 360 out of ~400 degrees of rotation when driven at a decent clip (say, 1000 encoder ticks per second). Is that something the Arduino can't handle that I'll just have to live with?

Now I'm completely at a loss. I borrowed some code from one of the posts about encoders (Arduino Playground - RotaryEncoders) and adapted it slightly for my purposes:

//PIN's definition
#define encoder0PinA  2
#define encoder0PinB  3


volatile int encoder0Pos = 0;
volatile boolean PastA = 0;
volatile boolean PastB = 0;
volatile int count = 0;
volatile int count2 = 0;

// include the library code:
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 6, 5, 4, 7);

void setup() 
{
  // set up the LCD's number of rows and columns: 
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.print("hello, world!");

  pinMode(encoder0PinA, INPUT);
  //turn on pullup resistor
  digitalWrite(encoder0PinA, HIGH); //ONLY FOR SOME ENCODER(MAGNETIC)!!!! 
  pinMode(encoder0PinB, INPUT); 
  //turn on pullup resistor
  digitalWrite(encoder0PinB, HIGH); //ONLY FOR SOME ENCODER(MAGNETIC)!!!! 
  PastA = (boolean)digitalRead(encoder0PinA); //initial value of channel A;
  PastB = (boolean)digitalRead(encoder0PinB); //and channel B

//To speed up even more, you may define manually the ISRs
// encoder A channel on interrupt 0 (arduino's pin 2)
  attachInterrupt(0, doEncoderA, RISING);
// encoder B channel pin on interrupt 1 (arduino's pin 3)
  attachInterrupt(1, doEncoderB, CHANGE); 

}


void loop()
{  
 //your staff....ENJOY! :D
  lcd.setCursor(0,1);
  lcd.print("                ");
  lcd.setCursor(0,1);
  lcd.print(encoder0Pos);
  lcd.setCursor(6,1);
  lcd.print(count);
  lcd.setCursor(11,1);
  lcd.print(count2);
}

//you may easily modify the code  get quadrature..
//..but be sure this whouldn't let Arduino back! 
void doEncoderA()
{
     PastB ? encoder0Pos--:  encoder0Pos++;
     count++;
}

void doEncoderB()
{
     PastB = !PastB;
     count2++;
}

The nutty thing is that at times, it works fine (I turn the motor myself and monitor the outputs), and I can reset the Arduino board and it will be buggy for no reason I can figure out.

For instance, typically you'd expect count2 to be twice as big as count since the B interrupt is happening on a change and the A only on rising. And yet, sometimes after a reset, count2 will actually lag count as I turn the motor.

At this point I've got to believe it's completely a hardware problem. Maybe my schematic will shed some light. Does the fact that it works some of the time shed any light on the matter?

Thanks!

So I've failed to have any luck consistently reading rotations with interrupts. The fact that it inconsistently works makes me think that I'm not doing anything egregiously wrong. My suspicion is that I'm on the hairy edge of the Arduino's interrupt capabilities.

Consider that for each motor rotation, that each phase of the optical encoder undergoes 720 changes of state. At full speed of say, 5 rps, that's 3600 changes of state. That's a delay between changes of 0.3 ms. I know the Arduino is running in the MHz range, but I'm suspicious that this is faster than the interrupt was intended to behave (i.e. while my interrupt code is running, the next interrupt wants to be called, and any comparison operators I use are compromised). This is the only explanation I can come up with for the inconsistent counting.

I've had the best luck without interrupts, similar to the first example on the rotary encoder playground. I'm going to stick to that and just have dedicated motor movement in my application.

Comments?

That's a delay between changes of 0.3 ms. I know the Arduino is running in the MHz range, but I'm suspicious that this is faster than the interrupt was intended to behave (i.e. while my interrupt code is running, the next interrupt wants to be called

It seems unlikely that a well-written interrupt service routine should take 300us, or about 4800 machine cycles.

I've haven't shown any pull-up/down from the tach pins of the lego motor; still not sure if I need them.

And the result of your investigation was...?

I've decided that the motor package already contains Schmitt triggers downstream of the encoders and the power and ground supplied preclude the need for pull-up resistors.

When I measured the voltage with the multimeter, it only reads 0 or +5 V, no in-between, implying that Schmitt triggers are being used. Furthermore, there doesn't appear to be "floating" values of voltage which isn't surprising since I'm supplying ground and +5V.

So basically that leaves me in a place where the behavior is fairly predictable without interrupts and very unpredictable with interrupts (with the main bug that even when I rotate in a single direction, rising signals on one encoder don't always lead to the same signal of either a low or high on the other encoder).

If anyone wants a project to work on, it would be neat to know what a certain interrupt approach could handle data rate-wise...