Motor Shield: Controlling DC Motors and PWM

Adafruit Ethernet, Motor, Proto, Wave, Datalogger, GPS Shields - etc!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
paulmc_rules
 
Posts: 7
Joined: Tue Nov 13, 2012 7:45 pm

Motor Shield: Controlling DC Motors and PWM

Post by paulmc_rules »

Hi, just purchased a motor shield for my arduino uno, and got to say although designed for different tasks, it all ready blows the dagu 48 red back spider away on servo movement (it actually stalls the servos in the correct position when even a considerable force is acting against it).

Anyway, as you can guess I can move servos fine, it is just a few things I need to know about dc motors. I will be wiring these up tomorrow. I have standard firmata loaded on the uno with serial messages sent from Max/MSP 5 via a USB cable (works great!).

By hacking Maxuino (a patch for Max/MSP 5) I can see what serial codes I need to send to the board to control input, output, pwm and servo with their repsective variables. The image shows Maxuino and the message window showing what messages are being sent to the board:

Image

By hacking away I know that motor 1 is controlled by digital pin 11 and needs to be in pwm mode so I send the board 244 11 3 to activate the pin to the correct mode and 244 11 0 to de-activate it. There is a slider for changing the pwm pulse. This slider has 101 variables (0.00-1.00) and when cycling through from 0.00 to 1.00 the board receives these messages: 235 0 0 to 235 127 0 and 235 1 1 to i]235 127 1[/i] (mostly in increments of 2 eg. 235 0 0, 235 2 0, 235 4 0...).

What I would like to know to is what are the sizes of the pulses I am sending? I do not want to experiment too much if I do not have to, as I do not want to damage my motor. And what pulses do I need to send to make the motor move clock-wise, anti-clockwise and stall? This is the motor I am using: http://www.pololu.com/catalog/product/2218 (very similar to Solarbot I have read).

Apologies for the long post and if I have posted in the wrong section (I'm a newbie), but I want to run 4 motors simultaneously. These motors are running in pairs, would it work if I wired two motors into motor 1 terminal and the other two motors into motor terminal 2, or is this a really bad idea, and should I use all four terminals? If this is possible, I guess I won't need to change the specs of my AC/DC adapter? I have purchased an 8v 2000mA adapter (providing 6.8v after the 1.2v drop in the L293D H-bridge (I couldn't find a 7.2 on ebay with enough current) and 2000mA is probably a little bit more than I need but I have read going a little bit over should not hurt too much).

Any help is greatly appreciated, thanks :)

User avatar
adafruit_support_bill
 
Posts: 88089
Joined: Sat Feb 07, 2009 10:11 am

Re: Motor Shield: Controlling DC Motors and PWM

Post by adafruit_support_bill »

What I would like to know to is what are the sizes of the pulses I am sending? I do not want to experiment too much if I do not have to, as I do not want to damage my motor.
I don't know much about the software you are using, but the AF_Motor library allows you to select the PWM frequency. Lower frequencies generally give more torque, but can make an annoying buzz or whine. Some 'coreless' motors can be damaged by low PWM frequencies, but you should have no problem with the motors you are using. Frequencies above 20KHz or so are inaudible, and most small DC motors perform well at those frequencies.
would it work if I wired two motors into motor 1 terminal and the other two motors into motor terminal 2, or is this a really bad idea, and should I use all four terminals?
Not a good idea. You should use the 4 terminals for 4 motors.
I have purchased an 8v 2000mA adapter (providing 6.8v after the 1.2v drop in the L293D H-bridge (I couldn't find a 7.2 on ebay with enough current) and 2000mA is probably a little bit more than I need but I have read going a little bit over should not hurt too much).
The higher voltage will give you a higher current draw. But you are probably OK. The stall current (1.6A) for those motors is over the limit for the standard H-bridge on the shield (0.6A), but with a 298:1 gear ratio, you are unlikely to stall them. You might want to check the actual current draw in operation. If it is getting close to 0.6A, you should consider some of the options here (search for "I need more power!").

paulmc_rules
 
Posts: 7
Joined: Tue Nov 13, 2012 7:45 pm

Re: Motor Shield: Controlling DC Motors and PWM

Post by paulmc_rules »

Thanks for your reply, it has been quite helpful. I have made some progress with controlling the motors by modifying a sketch that I found in another post. Using this Arduino sketch I can control my four motors fine:

Code: Select all

#include <AFMotor.h>

AF_DCMotor motor1(1, MOTOR12_64KHZ);
AF_DCMotor motor2(2, MOTOR12_64KHZ);
AF_DCMotor motor3(3, MOTOR34_64KHZ);
AF_DCMotor motor4(4, MOTOR34_64KHZ);

char val;

void setup()
{

  Serial.begin(57600);

  motor1.setSpeed(200);
  motor2.setSpeed(200);
  motor3.setSpeed(200);
  motor4.setSpeed(200);
}

void loop()
{
  if(Serial.available())
  {
    val = Serial.read();
  }
 
  if(val == 'U'){
    motor1.run(FORWARD);
    motor2.run(FORWARD);
  } else if(val == 'D'){
    motor1.run(BACKWARD);
    motor2.run(BACKWARD);
  } else if(val == 'N'){
    motor3.run(FORWARD);
    motor4.run(FORWARD);
  } else if(val == 'F'){
    motor3.run(BACKWARD);
    motor4.run(BACKWARD);
  }else if(val == 'S'){
    motor1.run(RELEASE);
    motor2.run(RELEASE);
    motor3.run(RELEASE);
    motor4.run(RELEASE);
  }
  delay(100);
}
Through Max/MSP 5 I send 85 (ASCII for U) to drive motors 1&2 forward, 68 to drive them backwards (D), 78 to drive motors 3&4 forward (C) and 70 to drive them backwards (F). The motors seem to work flawlessly, however when servos are plugged in they seem to adjust slightly or freak out when either of those messages are sent. I am pretty certain it isn't the messages that I am sending to the board that are playing havoc, as using the serial monitor to type in the letter commands instead still causes the servos to move. I think it maybe more of a problem with the sketch.

I also tried implementing this sketch into the standard firmata sketch like this (but not to my surprise, it did not work at all, and I am starting to think that I may be better off without firmata, as I only want to control 2 servos in addition to the two motors):

Code: Select all

#include <Servo.h>
#include <Wire.h>
#include <AFMotor.h>
#include <Firmata.h>

#include <AFMotor.h>

AF_DCMotor motor1(1, MOTOR12_64KHZ);
AF_DCMotor motor2(2, MOTOR12_64KHZ);
AF_DCMotor motor3(3, MOTOR34_64KHZ);
AF_DCMotor motor4(4, MOTOR34_64KHZ);

char val;

// move the following defines to Firmata.h?
#define I2C_WRITE B00000000
#define I2C_READ B00001000
#define I2C_READ_CONTINUOUSLY B00010000
#define I2C_STOP_READING B00011000
#define I2C_READ_WRITE_MODE_MASK B00011000
#define I2C_10BIT_ADDRESS_MODE_MASK B00100000

#define MAX_QUERIES 8
#define MINIMUM_SAMPLING_INTERVAL 10

#define REGISTER_NOT_SPECIFIED -1

/*==============================================================================
 * GLOBAL VARIABLES
 *============================================================================*/

/* analog inputs */
int analogInputsToReport = 0; // bitwise array to store pin reporting

/* digital input ports */
byte reportPINs[TOTAL_PORTS];       // 1 = report this port, 0 = silence
byte previousPINs[TOTAL_PORTS];     // previous 8 bits sent

/* pins configuration */
byte pinConfig[TOTAL_PINS];         // configuration of every pin
byte portConfigInputs[TOTAL_PORTS]; // each bit: 1 = pin in INPUT, 0 = anything else
int pinState[TOTAL_PINS];           // any value that has been written

/* timer variables */
unsigned long currentMillis;        // store the current value from millis()
unsigned long previousMillis;       // for comparison with currentMillis
int samplingInterval = 19;          // how often to run the main loop (in ms)

/* i2c data */
struct i2c_device_info {
  byte addr;
  byte reg;
  byte bytes;
};

/* for i2c read continuous more */
i2c_device_info query[MAX_QUERIES];

byte i2cRxData[32];
boolean isI2CEnabled = false;
signed char queryIndex = -1;
unsigned int i2cReadDelayTime = 0;  // default delay time between i2c read request and Wire.requestFrom()

Servo servos[MAX_SERVOS];
/*==============================================================================
 * FUNCTIONS
 *============================================================================*/

void readAndReportData(byte address, int theRegister, byte numBytes) {
  // allow I2C requests that don't require a register read
  // for example, some devices using an interrupt pin to signify new data available
  // do not always require the register read so upon interrupt you call Wire.requestFrom()  
  if (theRegister != REGISTER_NOT_SPECIFIED) {
    Wire.beginTransmission(address);
    #if ARDUINO >= 100
    Wire.write((byte)theRegister);
    #else
    Wire.send((byte)theRegister);
    #endif
    Wire.endTransmission();
    delayMicroseconds(i2cReadDelayTime);  // delay is necessary for some devices such as WiiNunchuck
  } else {
    theRegister = 0;  // fill the register with a dummy value
  }

  Wire.requestFrom(address, numBytes);  // all bytes are returned in requestFrom

  // check to be sure correct number of bytes were returned by slave
  if(numBytes == Wire.available()) {
    i2cRxData[0] = address;
    i2cRxData[1] = theRegister;
    for (int i = 0; i < numBytes; i++) {
      #if ARDUINO >= 100
      i2cRxData[2 + i] = Wire.read();
      #else
      i2cRxData[2 + i] = Wire.receive();
      #endif
    }
  }
  else {
    if(numBytes > Wire.available()) {
      Firmata.sendString("I2C Read Error: Too many bytes received");
    } else {
      Firmata.sendString("I2C Read Error: Too few bytes received"); 
    }
  }

  // send slave address, register and received bytes
  Firmata.sendSysex(SYSEX_I2C_REPLY, numBytes + 2, i2cRxData);
}

void outputPort(byte portNumber, byte portValue, byte forceSend)
{
  // pins not configured as INPUT are cleared to zeros
  portValue = portValue & portConfigInputs[portNumber];
  // only send if the value is different than previously sent
  if(forceSend || previousPINs[portNumber] != portValue) {
    Firmata.sendDigitalPort(portNumber, portValue);
    previousPINs[portNumber] = portValue;
  }
}

/* -----------------------------------------------------------------------------
 * check all the active digital inputs for change of state, then add any events
 * to the Serial output queue using Serial.print() */
void checkDigitalInputs(void)
{
  /* Using non-looping code allows constants to be given to readPort().
   * The compiler will apply substantial optimizations if the inputs
   * to readPort() are compile-time constants. */
  if (TOTAL_PORTS > 0 && reportPINs[0]) outputPort(0, readPort(0, portConfigInputs[0]), false);
  if (TOTAL_PORTS > 1 && reportPINs[1]) outputPort(1, readPort(1, portConfigInputs[1]), false);
  if (TOTAL_PORTS > 2 && reportPINs[2]) outputPort(2, readPort(2, portConfigInputs[2]), false);
  if (TOTAL_PORTS > 3 && reportPINs[3]) outputPort(3, readPort(3, portConfigInputs[3]), false);
  if (TOTAL_PORTS > 4 && reportPINs[4]) outputPort(4, readPort(4, portConfigInputs[4]), false);
  if (TOTAL_PORTS > 5 && reportPINs[5]) outputPort(5, readPort(5, portConfigInputs[5]), false);
  if (TOTAL_PORTS > 6 && reportPINs[6]) outputPort(6, readPort(6, portConfigInputs[6]), false);
  if (TOTAL_PORTS > 7 && reportPINs[7]) outputPort(7, readPort(7, portConfigInputs[7]), false);
  if (TOTAL_PORTS > 8 && reportPINs[8]) outputPort(8, readPort(8, portConfigInputs[8]), false);
  if (TOTAL_PORTS > 9 && reportPINs[9]) outputPort(9, readPort(9, portConfigInputs[9]), false);
  if (TOTAL_PORTS > 10 && reportPINs[10]) outputPort(10, readPort(10, portConfigInputs[10]), false);
  if (TOTAL_PORTS > 11 && reportPINs[11]) outputPort(11, readPort(11, portConfigInputs[11]), false);
  if (TOTAL_PORTS > 12 && reportPINs[12]) outputPort(12, readPort(12, portConfigInputs[12]), false);
  if (TOTAL_PORTS > 13 && reportPINs[13]) outputPort(13, readPort(13, portConfigInputs[13]), false);
  if (TOTAL_PORTS > 14 && reportPINs[14]) outputPort(14, readPort(14, portConfigInputs[14]), false);
  if (TOTAL_PORTS > 15 && reportPINs[15]) outputPort(15, readPort(15, portConfigInputs[15]), false);
}

// -----------------------------------------------------------------------------
/* sets the pin mode to the correct state and sets the relevant bits in the
 * two bit-arrays that track Digital I/O and PWM status
 */
void setPinModeCallback(byte pin, int mode)
{
  if (pinConfig[pin] == I2C && isI2CEnabled && mode != I2C) {
    // disable i2c so pins can be used for other functions
    // the following if statements should reconfigure the pins properly
    disableI2CPins();
  }
  if (IS_PIN_SERVO(pin) && mode != SERVO && servos[PIN_TO_SERVO(pin)].attached()) {
    servos[PIN_TO_SERVO(pin)].detach();
  }
  if (IS_PIN_ANALOG(pin)) {
    reportAnalogCallback(PIN_TO_ANALOG(pin), mode == ANALOG ? 1 : 0); // turn on/off reporting
  }
  if (IS_PIN_DIGITAL(pin)) {
    if (mode == INPUT) {
      portConfigInputs[pin/8] |= (1 << (pin & 7));
    } else {
      portConfigInputs[pin/8] &= ~(1 << (pin & 7));
    }
  }
  pinState[pin] = 0;
  switch(mode) {
  case ANALOG:
    if (IS_PIN_ANALOG(pin)) {
      if (IS_PIN_DIGITAL(pin)) {
        pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver
        digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups
      }
      pinConfig[pin] = ANALOG;
    }
    break;
  case INPUT:
    if (IS_PIN_DIGITAL(pin)) {
      pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver
      digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups
      pinConfig[pin] = INPUT;
    }
    break;
  case OUTPUT:
    if (IS_PIN_DIGITAL(pin)) {
      digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable PWM
      pinMode(PIN_TO_DIGITAL(pin), OUTPUT);
      pinConfig[pin] = OUTPUT;
    }
    break;
  case PWM:
    if (IS_PIN_PWM(pin)) {
      pinMode(PIN_TO_PWM(pin), OUTPUT);
      analogWrite(PIN_TO_PWM(pin), 0);
      pinConfig[pin] = PWM;
    }
    break;
  case SERVO:
    if (IS_PIN_SERVO(pin)) {
      pinConfig[pin] = SERVO;
      if (!servos[PIN_TO_SERVO(pin)].attached()) {
          servos[PIN_TO_SERVO(pin)].attach(PIN_TO_DIGITAL(pin));
      }
    }
    break;
  case I2C:
    if (IS_PIN_I2C(pin)) {
      // mark the pin as i2c
      // the user must call I2C_CONFIG to enable I2C for a device
      pinConfig[pin] = I2C;
    }
    break;
  default:
    Firmata.sendString("Unknown pin mode"); // TODO: put error msgs in EEPROM
  }
  // TODO: save status to EEPROM here, if changed
}

void analogWriteCallback(byte pin, int value)
{
  if (pin < TOTAL_PINS) {
    switch(pinConfig[pin]) {
    case SERVO:
      if (IS_PIN_SERVO(pin))
        servos[PIN_TO_SERVO(pin)].write(value);
        pinState[pin] = value;
      break;
    case PWM:
      if (IS_PIN_PWM(pin))
        analogWrite(PIN_TO_PWM(pin), value);
        pinState[pin] = value;
      break;
    }
  }
}

void digitalWriteCallback(byte port, int value)
{
  byte pin, lastPin, mask=1, pinWriteMask=0;

  if (port < TOTAL_PORTS) {
    // create a mask of the pins on this port that are writable.
    lastPin = port*8+8;
    if (lastPin > TOTAL_PINS) lastPin = TOTAL_PINS;
    for (pin=port*8; pin < lastPin; pin++) {
      // do not disturb non-digital pins (eg, Rx & Tx)
      if (IS_PIN_DIGITAL(pin)) {
        // only write to OUTPUT and INPUT (enables pullup)
        // do not touch pins in PWM, ANALOG, SERVO or other modes
        if (pinConfig[pin] == OUTPUT || pinConfig[pin] == INPUT) {
          pinWriteMask |= mask;
          pinState[pin] = ((byte)value & mask) ? 1 : 0;
        }
      }
      mask = mask << 1;
    }
    writePort(port, (byte)value, pinWriteMask);
  }
}


// -----------------------------------------------------------------------------
/* sets bits in a bit array (int) to toggle the reporting of the analogIns
 */
//void FirmataClass::setAnalogPinReporting(byte pin, byte state) {
//}
void reportAnalogCallback(byte analogPin, int value)
{
  if (analogPin < TOTAL_ANALOG_PINS) {
    if(value == 0) {
      analogInputsToReport = analogInputsToReport &~ (1 << analogPin);
    } else {
      analogInputsToReport = analogInputsToReport | (1 << analogPin);
    }
  }
  // TODO: save status to EEPROM here, if changed
}

void reportDigitalCallback(byte port, int value)
{
  if (port < TOTAL_PORTS) {
    reportPINs[port] = (byte)value;
  }
  // do not disable analog reporting on these 8 pins, to allow some
  // pins used for digital, others analog.  Instead, allow both types
  // of reporting to be enabled, but check if the pin is configured
  // as analog when sampling the analog inputs.  Likewise, while
  // scanning digital pins, portConfigInputs will mask off values from any
  // pins configured as analog
}

/*==============================================================================
 * SYSEX-BASED commands
 *============================================================================*/

void sysexCallback(byte command, byte argc, byte *argv)
{
  byte mode;
  byte slaveAddress;
  byte slaveRegister;
  byte data;
  unsigned int delayTime; 
  
  switch(command) {
  case I2C_REQUEST:
    mode = argv[1] & I2C_READ_WRITE_MODE_MASK;
    if (argv[1] & I2C_10BIT_ADDRESS_MODE_MASK) {
      Firmata.sendString("10-bit addressing mode is not yet supported");
      return;
    }
    else {
      slaveAddress = argv[0];
    }

    switch(mode) {
    case I2C_WRITE:
      Wire.beginTransmission(slaveAddress);
      for (byte i = 2; i < argc; i += 2) {
        data = argv[i] + (argv[i + 1] << 7);
        #if ARDUINO >= 100
        Wire.write(data);
        #else
        Wire.send(data);
        #endif
      }
      Wire.endTransmission();
      delayMicroseconds(70);
      break;
    case I2C_READ:
      if (argc == 6) {
        // a slave register is specified
        slaveRegister = argv[2] + (argv[3] << 7);
        data = argv[4] + (argv[5] << 7);  // bytes to read
        readAndReportData(slaveAddress, (int)slaveRegister, data);
      }
      else {
        // a slave register is NOT specified
        data = argv[2] + (argv[3] << 7);  // bytes to read
        readAndReportData(slaveAddress, (int)REGISTER_NOT_SPECIFIED, data);
      }
      break;
    case I2C_READ_CONTINUOUSLY:
      if ((queryIndex + 1) >= MAX_QUERIES) {
        // too many queries, just ignore
        Firmata.sendString("too many queries");
        break;
      }
      queryIndex++;
      query[queryIndex].addr = slaveAddress;
      query[queryIndex].reg = argv[2] + (argv[3] << 7);
      query[queryIndex].bytes = argv[4] + (argv[5] << 7);
      break;
    case I2C_STOP_READING:
	  byte queryIndexToSkip;      
      // if read continuous mode is enabled for only 1 i2c device, disable
      // read continuous reporting for that device
      if (queryIndex <= 0) {
        queryIndex = -1;        
      } else {
        // if read continuous mode is enabled for multiple devices,
        // determine which device to stop reading and remove it's data from
        // the array, shifiting other array data to fill the space
        for (byte i = 0; i < queryIndex + 1; i++) {
          if (query[i].addr = slaveAddress) {
            queryIndexToSkip = i;
            break;
          }
        }
        
        for (byte i = queryIndexToSkip; i<queryIndex + 1; i++) {
          if (i < MAX_QUERIES) {
            query[i].addr = query[i+1].addr;
            query[i].reg = query[i+1].addr;
            query[i].bytes = query[i+1].bytes; 
          }
        }
        queryIndex--;
      }
      break;
    default:
      break;
    }
    break;
  case I2C_CONFIG:
    delayTime = (argv[0] + (argv[1] << 7));

    if(delayTime > 0) {
      i2cReadDelayTime = delayTime;
    }

    if (!isI2CEnabled) {
      enableI2CPins();
    }
    
    break;
  case SERVO_CONFIG:
    if(argc > 4) {
      // these vars are here for clarity, they'll optimized away by the compiler
      byte pin = argv[0];
      int minPulse = argv[1] + (argv[2] << 7);
      int maxPulse = argv[3] + (argv[4] << 7);

      if (IS_PIN_SERVO(pin)) {
        if (servos[PIN_TO_SERVO(pin)].attached())
          servos[PIN_TO_SERVO(pin)].detach();
        servos[PIN_TO_SERVO(pin)].attach(PIN_TO_DIGITAL(pin), minPulse, maxPulse);
        setPinModeCallback(pin, SERVO);
      }
    }
    break;
  case SAMPLING_INTERVAL:
    if (argc > 1) {
      samplingInterval = argv[0] + (argv[1] << 7);
      if (samplingInterval < MINIMUM_SAMPLING_INTERVAL) {
        samplingInterval = MINIMUM_SAMPLING_INTERVAL;
      }      
    } else {
      //Firmata.sendString("Not enough data");
    }
    break;
  case EXTENDED_ANALOG:
    if (argc > 1) {
      int val = argv[1];
      if (argc > 2) val |= (argv[2] << 7);
      if (argc > 3) val |= (argv[3] << 14);
      analogWriteCallback(argv[0], val);
    }
    break;
  case CAPABILITY_QUERY:
    Serial.write(START_SYSEX);
    Serial.write(CAPABILITY_RESPONSE);
    for (byte pin=0; pin < TOTAL_PINS; pin++) {
      if (IS_PIN_DIGITAL(pin)) {
        Serial.write((byte)INPUT);
        Serial.write(1);
        Serial.write((byte)OUTPUT);
        Serial.write(1);
      }
      if (IS_PIN_ANALOG(pin)) {
        Serial.write(ANALOG);
        Serial.write(10);
      }
      if (IS_PIN_PWM(pin)) {
        Serial.write(PWM);
        Serial.write(8);
      }
      if (IS_PIN_SERVO(pin)) {
        Serial.write(SERVO);
        Serial.write(14);
      }
      if (IS_PIN_I2C(pin)) {
        Serial.write(I2C);
        Serial.write(1);  // to do: determine appropriate value 
      }
      Serial.write(127);
    }
    Serial.write(END_SYSEX);
    break;
  case PIN_STATE_QUERY:
    if (argc > 0) {
      byte pin=argv[0];
      Serial.write(START_SYSEX);
      Serial.write(PIN_STATE_RESPONSE);
      Serial.write(pin);
      if (pin < TOTAL_PINS) {
        Serial.write((byte)pinConfig[pin]);
	Serial.write((byte)pinState[pin] & 0x7F);
	if (pinState[pin] & 0xFF80) Serial.write((byte)(pinState[pin] >> 7) & 0x7F);
	if (pinState[pin] & 0xC000) Serial.write((byte)(pinState[pin] >> 14) & 0x7F);
      }
      Serial.write(END_SYSEX);
    }
    break;
  case ANALOG_MAPPING_QUERY:
    Serial.write(START_SYSEX);
    Serial.write(ANALOG_MAPPING_RESPONSE);
    for (byte pin=0; pin < TOTAL_PINS; pin++) {
      Serial.write(IS_PIN_ANALOG(pin) ? PIN_TO_ANALOG(pin) : 127);
    }
    Serial.write(END_SYSEX);
    break;
  }
}

void enableI2CPins()
{
  byte i;
  // is there a faster way to do this? would probaby require importing 
  // Arduino.h to get SCL and SDA pins
  for (i=0; i < TOTAL_PINS; i++) {
    if(IS_PIN_I2C(i)) {
      // mark pins as i2c so they are ignore in non i2c data requests
      setPinModeCallback(i, I2C);
    } 
  }
   
  isI2CEnabled = true; 
  
  // is there enough time before the first I2C request to call this here?
  Wire.begin();
}

/* disable the i2c pins so they can be used for other functions */
void disableI2CPins() {
    isI2CEnabled = false;
    // disable read continuous mode for all devices
    queryIndex = -1;
    // uncomment the following if or when the end() method is added to Wire library
    // Wire.end();
}

/*==============================================================================
 * SETUP()
 *============================================================================*/

void systemResetCallback()
{
  // initialize a defalt state
  // TODO: option to load config from EEPROM instead of default
  if (isI2CEnabled) {
  	disableI2CPins();
  }
  for (byte i=0; i < TOTAL_PORTS; i++) {
    reportPINs[i] = false;      // by default, reporting off
    portConfigInputs[i] = 0;	// until activated
    previousPINs[i] = 0;
  }
  // pins with analog capability default to analog input
  // otherwise, pins default to digital output
  for (byte i=0; i < TOTAL_PINS; i++) {
    if (IS_PIN_ANALOG(i)) {
      // turns off pullup, configures everything
      setPinModeCallback(i, ANALOG);
    } else {
      // sets the output to 0, configures portConfigInputs
      setPinModeCallback(i, OUTPUT);
    }
  }
  // by default, do not report any analog inputs
  analogInputsToReport = 0;

  /* send digital inputs to set the initial state on the host computer,
   * since once in the loop(), this firmware will only send on change */
  /*
  TODO: this can never execute, since no pins default to digital input
        but it will be needed when/if we support EEPROM stored config
  for (byte i=0; i < TOTAL_PORTS; i++) {
    outputPort(i, readPort(i, portConfigInputs[i]), true);
  }
  */
}

void setup() 
{
  Firmata.setFirmwareVersion(FIRMATA_MAJOR_VERSION, FIRMATA_MINOR_VERSION);

  Firmata.attach(ANALOG_MESSAGE, analogWriteCallback);
  Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback);
  Firmata.attach(REPORT_ANALOG, reportAnalogCallback);
  Firmata.attach(REPORT_DIGITAL, reportDigitalCallback);
  Firmata.attach(SET_PIN_MODE, setPinModeCallback);
  Firmata.attach(START_SYSEX, sysexCallback);
  Firmata.attach(SYSTEM_RESET, systemResetCallback);

  Firmata.begin(57600);
  systemResetCallback();  // reset to default config
  
  motor1.setSpeed(200);
  motor2.setSpeed(200);
  motor3.setSpeed(200);
  motor4.setSpeed(200);
}

/*==============================================================================
 * LOOP()
 *============================================================================*/
void loop() 
{
  byte pin, analogPin;

  /* DIGITALREAD - as fast as possible, check for changes and output them to the
   * FTDI buffer using Serial.print()  */
  checkDigitalInputs();  

  /* SERIALREAD - processing incoming messagse as soon as possible, while still
   * checking digital inputs.  */
  while(Firmata.available())
    Firmata.processInput();

  /* SEND FTDI WRITE BUFFER - make sure that the FTDI buffer doesn't go over
   * 60 bytes. use a timer to sending an event character every 4 ms to
   * trigger the buffer to dump. */

  currentMillis = millis();
  if (currentMillis - previousMillis > samplingInterval) {
    previousMillis += samplingInterval;
    /* ANALOGREAD - do all analogReads() at the configured sampling interval */
    for(pin=0; pin<TOTAL_PINS; pin++) {
      if (IS_PIN_ANALOG(pin) && pinConfig[pin] == ANALOG) {
        analogPin = PIN_TO_ANALOG(pin);
        if (analogInputsToReport & (1 << analogPin)) {
          Firmata.sendAnalog(analogPin, analogRead(analogPin));
        }
      }
    }
    // report i2c data for all device with read continuous mode enabled
    if (queryIndex > -1) {
      for (byte i = 0; i < queryIndex + 1; i++) {
        readAndReportData(query[i].addr, query[i].reg, query[i].bytes);
      }
    }
  }
    {
  if(Serial.available())
  {
    val = Serial.read();
  }
 
  if(val == 'U'){
    motor1.run(FORWARD);
    motor2.run(FORWARD);
  } else if(val == 'D'){
    motor1.run(BACKWARD);
    motor2.run(BACKWARD);
  } else if(val == 'N'){
    motor3.run(FORWARD);
    motor4.run(FORWARD);
  } else if(val == 'F'){
    motor3.run(BACKWARD);
    motor4.run(BACKWARD);
  }else if(val == 'S'){
    motor1.run(RELEASE);
    motor2.run(RELEASE);
    motor3.run(RELEASE);
    motor4.run(RELEASE);
  }
  delay(100);
}
}
Once again any help would be appreciated.

User avatar
adafruit_support_bill
 
Posts: 88089
Joined: Sat Feb 07, 2009 10:11 am

Re: Motor Shield: Controlling DC Motors and PWM

Post by adafruit_support_bill »

however when servos are plugged in they seem to adjust slightly or freak out when either of those messages are sent.
What pins are you using for the servos? I don't see them in the code you posted.

paulmc_rules
 
Posts: 7
Joined: Tue Nov 13, 2012 7:45 pm

Re: Motor Shield: Controlling DC Motors and PWM

Post by paulmc_rules »

They will be on pins 9 and 10, but I haven't started to try programming them yet, as I thought it would be best to try to stop their movement from the motor messages first.

User avatar
adafruit_support_bill
 
Posts: 88089
Joined: Sat Feb 07, 2009 10:11 am

Re: Motor Shield: Controlling DC Motors and PWM

Post by adafruit_support_bill »

If the servo control pins are not actively generating a control signal, they output will be mostly brush noise fed back from the motors. This will be strongest on motor starts of changes in speed/direction. The servos will (try to) interpret any pulse as a control signal, hence the random twitching.

paulmc_rules
 
Posts: 7
Joined: Tue Nov 13, 2012 7:45 pm

Re: Motor Shield: Controlling DC Motors and PWM

Post by paulmc_rules »

Ah I see. Well that would make sense according to how they're behaving. After I've got some sleep I will add some servo code and hopefully this will sort it, will post how I get on.

paulmc_rules
 
Posts: 7
Joined: Tue Nov 13, 2012 7:45 pm

Re: Motor Shield: Controlling DC Motors and PWM

Post by paulmc_rules »

Yes, by just simply attaching servos onto pins 9 and 10 solved the issue of the noise coming from the motors and interfering with the servos.

I am now trying adapt the sketch to allow me to change the speed of the motors. In Max/MSP (the software used to send serial information to the board) I first select which motors are moving and their direction like so:
paulmc_rules wrote:Through Max/MSP 5 I send 85 (ASCII for U) to drive motors 1&2 forward, 68 to drive them backwards (D), 78 to drive motors 3&4 forward (C) and 70 to drive them backwards (F).
Then I use a sliders to send floats from 0-250 to control the speeds once the motors position is fixed.

For instance, if I want motors 1 and 2 to go forwards I send 85 (ASCII for U) and then adjust the slider to accelerate and decelerate the motors' speed, each time I move the slider another serial message is sent. If I want to move the motors to a speed of 200 the slider sends this serial code: 65200. In my sketch below I have tried breaking this code into two parts. First 65 (ASCII for A) to tell the board I want to adjust motors 1&2's speed, and then 200 for the speed values. Could someone point out where I have gone wrong?

Code: Select all

#include <AFMotor.h>
#include <Servo.h>

Servo x;
Servo y;

int positionx = 0;
int positiony = 0;

AF_DCMotor motor1(1, MOTOR12_64KHZ);
AF_DCMotor motor2(2, MOTOR12_64KHZ);
AF_DCMotor motor3(3, MOTOR34_64KHZ);
AF_DCMotor motor4(4, MOTOR34_64KHZ);

char val;
String readString, valstring, valuestring;
float value;

void setup()
{

  Serial.begin(57600);

  motor1.setSpeed(0);
  motor2.setSpeed(0);
  motor3.setSpeed(0);
  motor4.setSpeed(0);
  
  x.attach(9);
  y.attach(10);
  
}

void loop()
{
  while (Serial.available()){
  delay(50);
  if(Serial.available() < 100){
    val = Serial.read();
  }
  if(Serial.available() > 99){
  char c = Serial.read();
  readString += c;
  valstring = readString.substring(0, 2);
  valuestring = readString.substring(2, 5);
  char val[2];
  valstring.toCharArray(val, 2);
  value = valuestring.toInt(); 
  }
  if(val == 'U'){
    motor1.run(FORWARD);
    motor2.run(FORWARD);
  }
  if(val == 'D'){
    motor1.run(BACKWARD);
    motor2.run(BACKWARD);
  }
  if(val == 'N'){
    motor3.run(FORWARD);
    motor4.run(FORWARD);
  }
  if(val == 'F'){
    motor3.run(BACKWARD);
    motor4.run(BACKWARD);
  }
  if (val == 'A');{
    motor1.setSpeed( value );
    motor2.setSpeed( value );
  }
  if (val == 'B');{
    motor3.setSpeed( value );
    motor4.setSpeed( value );
  }
  if(val == 'S'){
    motor1.run(RELEASE);
    motor2.run(RELEASE);
    motor3.run(RELEASE);
    motor4.run(RELEASE);
  }
  }
}
This image is of my Max/MSP controller and may make it easier to understand how I want to control the motors:

Image

...where $1 is the value of the slider (0-250), and 'p prepend' takes the gap out of the message.

Thanks for your help so far.

paulmc_rules
 
Posts: 7
Joined: Tue Nov 13, 2012 7:45 pm

Re: Motor Shield: Controlling DC Motors and PWM

Post by paulmc_rules »

By doing some serial prints I have realised that when I for example send 13 to the board with ASCII (49 51) the board is reading it as two separate values: 1 and 3 instead of 13. I would like to send values with three characters so how would I be able to group 3 ASCII messages together to form one value?

User avatar
adafruit_support_bill
 
Posts: 88089
Joined: Sat Feb 07, 2009 10:11 am

Re: Motor Shield: Controlling DC Motors and PWM

Post by adafruit_support_bill »

In my sketch below I have tried breaking this code into two parts. First 65 (ASCII for A) to tell the board I want to adjust motors 1&2's speed, and then 200 for the speed values.
Is that 200 in binary, or 200 in ASCII? In the code, your 'val' array is only 2 characters long. It can't store an ASCII "200" without overflowing.

Also, in 'C', strings are null terminated, so you need an extra character for the null.

paulmc_rules
 
Posts: 7
Joined: Tue Nov 13, 2012 7:45 pm

Re: Motor Shield: Controlling DC Motors and PWM

Post by paulmc_rules »

I'm guessing it would be binary so instead I'd need to send it in ASCII (050 048 048).

So lets presume that using my controller I adjust motors 1&2 speeds to 200, the board would receive ASCII values: 65 50 48 48. The board then interprets the ASCII values and converts them into independent characters A 2 0 0 instead of A200. So somehow, I need the sketch to expect 4 different values in one go and put them together as one whole string. How would I go about doing this?

With the string stored as A200 I could then I could create two different strings storing A and 200 using substrings. The string storing A could be converted to a char called 'val' and the string storing 200 could be converted to an integer called 'value' to correspond with this part of my sketch:

Code: Select all

if (val == 'A');{
motor1.setSpeed( value );
motor2.setSpeed( value );

User avatar
adafruit_support_bill
 
Posts: 88089
Joined: Sat Feb 07, 2009 10:11 am

Re: Motor Shield: Controlling DC Motors and PWM

Post by adafruit_support_bill »


Locked
Please be positive and constructive with your questions and comments.

Return to “Arduino Shields from Adafruit”