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 Motor Drive Control for Makers 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.
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.
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,
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.
The gearbox I used is available on CPC.









Top Comments