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.

 

 

The gearbox I used is available on CPC.

 

Is it running?

Did a message go in/out at @date?

Something has gone wrong how to diganose

 

Volumes

            Typically low

                GSIS (vessel itineraries) are a few thousand per day but many of these are not for our vessels.

 

Filtering input data

 

Activation by

  Date

  User

        Authentication

  Data change

 

Reference data alignment

 

Validation

 

Mapping

    Simple

    Function

    Lookup

    Default

    One to many and many to one

 

Handling problems

   Invalid data provided

   Mismatched reference data

   Ignore

   Error

   Stage for user

   Target is not available

   Alerting

 

Where will it run?

   .Net core?

   On premise

   On cloud

   DMZ

 

Network issues

    Proxy

    Authentication

 

Destinations

    File

    Ftp / SFTP

    OneView

    API

 

Sources

   Files

   Queue

   API pull and push

   For exports can we leverage the custom data sources?

 

Permissions and credentials

 

How to handle changes that have been made to the connected systems and interface itself?

   API changes (one view)

   API changes third party

   Functional changes

   Audit / traceability of changes

   Versioning

 

XML Vs JSON

 

Leverage existing skills

 

Debugging

Testing

Verification / validation

 

Build and provisioning

 

Discovery of data, some API and message formats are complex

 

How do we activate an extract?

                User initiated

                Schedule initiated

                Third party system initiated e.g. some kind of API