Movers and Shakers

Enter Your Electronics & Design Project for Your Chance to Win a $100 Shopping Cart!

Back to The Project14 homepage

Project14 Home
Monthly Themes
Monthly Theme Poll

 

I was building a big project and wanted to move the nozzle of a squirting flower, and the only servos I had were micro servos which did not have enough power to move the mechanism, so I decided to build something, a Jumbo Servo.

As mentioned in element14 Essentials: Motor Control 1  a servo consists of a motor with some kind of position feedback and a control circuit. I decided that a potentiometer and an arduino should be capable of this. I found a 12v gearbox motor in the spares drawer and after a little cleaning and lubrication got that running. There was a gear on the output shaft that could be used for driving a the potentiometer. I added a mounting plate and determined where I could add a potentiometer. A 3D printed gear was added to slip onto the pot and mesh with the output gear. Also something to drive the motor would be needed and I had a suitable L298 H-Bridge module which fitted the spec.

 

 

So that my jumbo servo would look the part, I made a wooden case and wooden servo horn. A 3D printed output shaft was also added.

Case

Next up was the electronics. I decided to go with I2C for communication rather than the analogue PWM which is typically used by a servo. This allows me to stop the motor when the servo has reached it's destination rather than constantly seeking position, it also means that I can read back the position to the controlling software and avoid that twitch you get when you first power on a servo.

 

To test this I used two arduinos and used the examples from Arduino to work out what code was needed. Because I was using a long cable (for I2C at least) I added some low value pullups on the servo end.

Here's my first draft of the code.

 

#include <Wire.h>

const int slaveAddress = 8;

const int sensePin = A0;
const int drivePin1 = 3;
const int drivePin2 = 4;
const int speedPin = 5; //Needs to support PWM

short registers[5];
int readCmd;
int blink = 0;

enum readRegisters { Target = 0,
  Running = 1,
  Position = 2,
  Speed = 3,
  Direction = 4
  };

enum commands { CmdStop = 1,
  CmdAngle = 2,
  CmdSpeed = 3,
  };

void setup() {
  stop();

  pinMode(drivePin1, OUTPUT);
  pinMode(drivePin2, OUTPUT);
  pinMode(speedPin, OUTPUT);
  pinMode(13, OUTPUT); //Onboard LED
  pinMode(sensePin, INPUT);

  Wire.begin(slaveAddress); // join i2c bus with address #8
  Wire.onReceive(receiveEvent); // write data
  Wire.onRequest(requestEvent); // requests to read data

}

void loop() {
  digitalWrite(13, blink);
  blink = !blink;  
  
  registers[Position] = analogRead(sensePin);

  if (registers[Running]) {
       if (registers[Direction] == 1) {
            if (registers[Position] >= registers[Target]) {
                 stop();
            }
       }
       if (registers[Direction] == -1) {
            if (registers[Position] <= registers[Target]) {
                 stop();
            }
       }
  }
}

void stop() {
  digitalWrite(drivePin1, LOW);
  digitalWrite(drivePin2, LOW);
  analogWrite(speedPin, 0);
  registers[Running] = false;
}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int bytesReceived) {
  char command;
  int value;
  int lowB;
  int highB;

  switch (bytesReceived) {
  case 1:
  //Read request register
  readCmd = Wire.read();
  break;
  case 3:
       command = Wire.read(); // receive byte as a character
       //Pi is low endian
       lowB = Wire.read();
       highB = Wire.read();
       value = (highB << 8) | lowB;

       switch (command) {
       case CmdStop:
            stop();
            break;
       case CmdAngle:
            registers[Target] = value;
            registers[Running] = true;
            if (registers[Target] > registers[Position]) {
                 digitalWrite(drivePin1, LOW);
                 digitalWrite(drivePin2, HIGH);
                 registers[Direction] = 1;
            }
            else {
                 digitalWrite(drivePin2, LOW);
                 digitalWrite(drivePin1, HIGH);  
                 registers[Direction] = -1;
            }
       break;
       case CmdSpeed:
            registers[Speed] = value;
            analogWrite(speedPin, registers[Speed]);
       break;
       }
  break;
  default:
       for (int a = 0; a < bytesReceived; a++) {
            Wire.read(); // throw buffer away so we can read again
       }
  }
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
// Don't add serial print to this routine, it will error!!

void requestEvent() {

  short value = registers[readCmd];
  Wire.write((uint8_t *)&value, sizeof(value));
}

 

I've got myself an AdaFruit "ItsyBitsy" which is basically a small form factor Arduino Leonado. So the next step is to wire this in, instead of the Arduino.

 

Circuit

I'll also need to do some simple calibration to turn the 0-1023 value into a degrees value. Because of the gearing on the pot the servo can do a bit more than 180 degress so I should be able to set it up with a safe limit so I don't over drive the pot.

Have added a breakout board for the ItsyBity and completed some further testing with some LEDs in place of the H-Bridge,

It H-Bridge Test

I then did some testing with the H-Bridge. I used back to back LEDs to check that the circuit was wired correctly and the software was working. After a few loose connections I got that working. However, swapping the motor in caused my 2A PSU to shutdown.

 

So I plugged these into the bench power supply (thanks again Secret Santa) and set the voltage and current limit. Luckily I happened to have a slightly smaller motor that had the same size shaft and screw holes that took a lot less current. Next up I'll swap that in.