arduino robot: ps2 mouse + pololu motor controller

hello all,

we trying to build an arduino robot ...
by using the following components:

1x Pololu Dual MC33887 motor controller
2x GHM-16 gearhead motors
2x QME-01 encoders (attached to the motors)
1x ps2 mouse
1x Arduino Duemilanove

specs
http://www.looksgood.de/_tmp/ghm16.pdf
http://www.looksgood.de/_tmp/encodeur-quadrature-qme-01-lynxmotion-assembly-guide.pdf
http://www.looksgood.de/_tmp/schaltplan2009.10.14.pdf

actually we are quite happy that we reached already the step, that we have everything up and running:
we get precise encoder values, we are able to control the motors with PWM via the pololu controller and with the help of the ps2 library the mouse is also working as an input sensor.

but so far we couldn't solve one strange problem:
if we drive the motors above certain PWM values the whole arduino freezes :frowning:
for instances:

// causes freeze
out_motorPWM[0] = 255;
out_motorPWM[1] = 255;
// working
out_motorPWM[0] = 127;
out_motorPWM[1] = 127;

please note also, without reading the mouse values in the main loop, we have no problems to drive the motors with any PWM value without any freeze.
so it seems to us that we have some strange interaction with the motors and the ps2 mouse lib.

any hint is very welcome. see below code and wiring.

thanks
benedikt

#include <ps2.h>
// Ps2mouse lib -> http://www.arduino.cc/playground/ComponentLib/Ps2mouse

// ---- pins ----
#define pinM1_DirectionA 4
#define pinM1_DirectionB 5
#define pinM1_PWM 10

#define pinM2_DirectionA 13
#define pinM2_DirectionB 7
#define pinM2_PWM 11 

#define pinE1_A 2
#define pinE1_B 8

#define pinE2_A 3
#define pinE2_B 6

#define pinP_data 9 
#define pinP_clock 12

PS2 mouse(pinP_clock, pinP_data);

//  ---- vars ----
int out_motorPWM[2] = {
  0,0};

// encoder 1
volatile int in_steps1 = 0;
int in_E1_prev = LOW;
int in_E1_curr = LOW;

// encoder 2
volatile int in_steps2 = 0;
int in_E2_prev = LOW;
int in_E2_curr = LOW;

// mouse
char mstat = '0';
char mx = '0';
char my = '0';


void setup() {
  // motor controller
  pinMode(pinM1_DirectionA, OUTPUT); 
  pinMode(pinM1_DirectionB, OUTPUT);
  pinMode(pinM1_PWM, OUTPUT);

  pinMode(pinM2_DirectionA, OUTPUT); 
  pinMode(pinM2_DirectionB, OUTPUT);
  pinMode(pinM2_PWM, OUTPUT);

  // encoders
  pinMode (pinE1_A,INPUT);
  pinMode (pinE1_B,INPUT);
  pinMode (pinE2_A,INPUT);
  pinMode (pinE2_B,INPUT);
  attachInterrupt(0, doEncoder1, CHANGE);
  attachInterrupt(1, doEncoder2, CHANGE);

  Serial.begin(19200); 
  Serial.println("hello");

  mouse_init();
  // wait for the mouse to be ready
  delay(2000);
}

void loop() {
  // ---- read data from computer host ----
  if (Serial.available() > 0) {
    int incomingByte = Serial.read();
    // turn on both motors
    if (incomingByte == 'h') {
      // cause arduino to freeze
      out_motorPWM[0] = 255;
      out_motorPWM[1] = 255;
      // works
      //out_motorPWM[0] = 127;
      //out_motorPWM[1] = 127;
    } 
    // turn off both motors
    if (incomingByte == 'l') {
      out_motorPWM[0] = 0;
      out_motorPWM[1] = 0;
    }
  } 
  
  // ---- set PWM + direction motor controller ----
  drive("motor1");
  drive("motor2");

  analogWrite(pinM1_PWM, abs(out_motorPWM[0]));
  analogWrite(pinM2_PWM, abs(out_motorPWM[1]));  
  

  // ---- read mouse data ----
  mouse.write(0xeb);  // give me data!
  mouse.read();      // ignore ack
  mstat = mouse.read();
  mx = mouse.read();
  my = mouse.read();


  // ---- send data to host ----
  Serial.print(in_steps1);
  Serial.print(';');
  Serial.print(in_steps2);
  Serial.print(';');
  Serial.print(mx, DEC);
  Serial.print(';');
  Serial.print(my, DEC);
  Serial.println();

  // reset variables after sending data, we just wanna have the delta
  in_steps1 = 0;
  in_steps2 = 0;

}


// ---- turn motros on + set direction ----
void drive(char m[]) {
  if (m=="motor1" && out_motorPWM[0]>=0) { // forward
    digitalWrite(pinM1_DirectionA, HIGH);
    digitalWrite(pinM1_DirectionB, LOW);
  }
  else if (m=="motor1" && out_motorPWM[0]<0) { // backward
    digitalWrite(pinM1_DirectionA, LOW);
    digitalWrite(pinM1_DirectionB, HIGH);
  }
  else if (m=="motor2" && out_motorPWM[1]>=0) { // forward
    digitalWrite(pinM2_DirectionA, HIGH);
    digitalWrite(pinM2_DirectionB, LOW);
  }
  else if (m=="motor2" && out_motorPWM[1]<0) { // backward
    digitalWrite(pinM2_DirectionA, LOW);
    digitalWrite(pinM2_DirectionB, HIGH);
  }
}


// ---- encoder interrupts event ----
void doEncoder1() {
  // left motor, cw = forward
  if ((digitalRead(pinE1_A) == HIGH)) {
    if (digitalRead(pinE1_B) == LOW) {
      in_steps1++;
    } 
    else {
      in_steps1--;
    }
  }
  else {
    if (digitalRead(pinE1_B) == LOW) {
      in_steps1--;
    } 
    else {
      in_steps1++;
    }
  }
}

void doEncoder2() {
  // right motor, cw = backward
  if (digitalRead(pinE2_A) == HIGH) {
    if (digitalRead(pinE2_B) == LOW) {
      in_steps2--;
    } 
    else {
      in_steps2++;
    }
  }
  else {
    if (digitalRead(pinE2_B) == LOW) {
      in_steps2++;
    } 
    else {
      in_steps2--;
    }
  }
}

// ---- mouse ----
void mouse_init()
{
  mouse.write(0xff);  // reset
  mouse.read();  // ack byte
  mouse.read();  // blank */
  mouse.read();  // blank */
  mouse.write(0xf0);  // remote mode
  mouse.read();  // ack
  delayMicroseconds(100);
}

What is the rating of your power supply, and what do the motors draw?
It's possible that the motor load at full power is exceeding the supply rating.

Another thing I would check is brush noise (assuming these are the typical less expensive DC motors). Brush noise can cause all sorts of headaches.

A NPO cap across the motor terminals helps squelch the noise (and also extends the life of the brushes). Keep the leads as short as possible.

Choke inductors in series with the motor leads also help a lot.

Its possible there is some 'unintended behavior' (bug) in the PS2 library, but given that it 'almost' works, I would check these things before delving into the library code.

hello mike,

our motor controller can handle 3A but both motors have together only something like 2A. we have also a capacitor attached, see picture. plus we drove yesterday evening the robot without problems in "remote car" style for hours (of course without the ps2 mouse).

what do you think? a bug in the ps2 lib?

thanks benedikt

PS2 mouse lib has no build-in timeouts. It waits for high / low values on clock / data pins in infinite while loops. If (maybe due to some noise from motors) clock or data pins stay in unexpected level, the code waits in loop.

I added a method to my version of the ps2 library that tests to see if a mouse is connected. Perhaps somehthing like this could help here

boolean PS2::IsResponding()
{
  /* put pins in output mode */
  gohi(_ps2data);
  gohi(_ps2clk);
  delayMicroseconds(300);
  golo(_ps2clk);
  delayMicroseconds(300);
  golo(_ps2data);
  delayMicroseconds(10);
  gohi(_ps2clk);
  for(uint8_t i = 0; i < 200; i++) {
    if(digitalRead(_ps2clk) == LOW){    /* wait for mouse to take control of clock); */
      gohi(_ps2clk);
      delay(50);  // wait for mouse to timeout
      return true;    
    }
    delay(1);
  }
  return false;  // mouse did not grab clock
}

The isResponding method returns true of a mouse responds to the command. It returns false if no response after a short timeout

PS2 lib is full of cycles like :

    // wait for clock cycle 
    while (!digitalRead(_clock_pin)) {;}
    while (digitalRead(_clock_pin)) {;}

My question was "What is the rating of your power supply, and what do the motors draw? "

You've answered half the question... the motors draw 2 amps full load.

The other part of the question is, how much current is the power supply able to supply?

I propose a simple experiment. Power your Arduino and the motor drive from separate supplies, and see whether your problem remains.

If the problem goes away, there is either insuffucient supply, or excessive noise from the motors.

If the problem remains, there is indeed a likely bug in the software.

hello mike, pepe34 and mem,

sorry for the ultra long reaction time but we had to finish another more important project in the last weeks. here we go:
yes we powered the motors with an external power suppy and the problem is still there. plus we tried to get rid of all the while cycles in the ps2 lib. but then the "mouse readings intervals" became really too slow, to make sense for calculating the position of the robot ...

so in the end we simply gave up, probably the motors produce too much noise for our electronic skills :frowning:

but at least we have now a nice remote controlled car. not bad. if you interested here is a video:
http://blog.intuitymedialab.eu/2009/10/22/ux-toolbox-update-hardware-hacking/

thanks for all the help
benedikt