cole_bruza
Published © GPL3+

Robotic Arm Scale

This 3-joint robotic arm with attached load cell was created for my final embedded systems project at John Brown University.

IntermediateShowcase (no instructions)15 hours133
Robotic Arm Scale

Things used in this project

Story

Read more

Schematics

Robotic Arm Scale Full Schematic

Servo Control Flow Chart

Weight Measurement/Display Flow chart

Video Demonstration

Code

Servo Control Code

C/C++
Code for simultaneous servo control of three servos with end point detection, position reset, and save/return to custom position.
#include <msp430.h>
#define Serv1 0x02 //Servo to P1.1
#define Serv2 0x80 //Servo to P1.7
#define Serv3 0x02 //Servo to P2.1
#define RLED 0x01 // Define LEDs
#define GLED 0x40
#define DELAYLOOPS 10000
#define Button1 0x04 //Button1 to P1.2
#define Button2 0x08 //Button2 to P1.3
#define Button3 0x10 //Button1 to P1.4
#define Button4 0x20 //Button2 to P1.5
#define Button5 0x08 //Button1 to P2.3
#define Button6 0x10 //Button2 to P2.4
#define DBNC 500
#define T 2000;
char debounceGoingLow1();    // add de-bounce for high-low transition
char debounceGoingLow2();    // add de-bounce for high-low transition
char debounceGoingLow3();    // add de-bounce for high-low transition
char debounceGoingLow4();    // add de-bounce for high-low transition
char debounceGoingLow5();    // add de-bounce for high-low transition
char debounceGoingLow6();    // add de-bounce for high-low transition

void runMotor(volatile unsigned int Ton, volatile unsigned int Toff);
int TurnMotorLeft(volatile unsigned int Ton, volatile unsigned int Toff,
                  volatile unsigned int pos1, volatile unsigned int pos2);
int TurnMotorRight(volatile unsigned int Ton, volatile unsigned int Toff,
                   volatile unsigned int pos1, volatile unsigned int pos2);
void runMotor2(volatile unsigned int Ton, volatile unsigned int Toff);
int TurnMotorLeft2(volatile unsigned int Ton, volatile unsigned int Toff,
                   volatile unsigned int pos1, volatile unsigned int pos2);
int TurnMotorRight2(volatile unsigned int Ton, volatile unsigned int Toff,
                    volatile unsigned int pos1, volatile unsigned int pos2);
void runMotor3(volatile unsigned int Ton, volatile unsigned int Toff);
int TurnMotorLeft3(volatile unsigned int Ton, volatile unsigned int Toff,
                   volatile unsigned int pos1, volatile unsigned int pos2);
int TurnMotorRight3(volatile unsigned int Ton, volatile unsigned int Toff,
                    volatile unsigned int pos1, volatile unsigned int pos2);
volatile unsigned int Ton1;
volatile unsigned int Ton2;
volatile unsigned int Ton3;

const unsigned int posmid1 = 150; // middle position for servo1
const unsigned int posmid2 = 213; // middle position for servo2
const unsigned int posmid3 = 155; // middle position for servo3
const unsigned int pos1 = 50; 	 // far right limit of servo
const unsigned int pos2 = 250;	 // far left limit of servo
volatile unsigned int Ton1Save;
volatile unsigned int Ton2Save;
volatile unsigned int Ton3Save;
void main(void)
{
    volatile unsigned int LoopCounter; // "volatile" ensures will not be optimized away
    WDTCTL = WDTPW + WDTHOLD;       // Stop watchdog timer
    P1DIR |= Serv1 | Serv2 | RLED | GLED; // Set P1.0
    P2DIR |= Serv3;

    P1REN |= Button1; // initialize buttons and Pull-Up Resistors
    P1OUT |= Button1;

    P1REN |= Button2;
    P1OUT |= Button2;

    P1REN |= Button3;
    P1OUT |= Button3;

    P1REN |= Button4;
    P1OUT |= Button4;

    P2REN |= Button5;
    P2OUT |= Button5;

    P2REN |= Button6;
    P2OUT |= Button6;

    volatile unsigned int Toff;

    Ton1 = posmid1; //Start at mid postion
    Ton2 = posmid2; //Start at mid postion
    Ton3 = posmid3; //Start at mid postion
    P1OUT &= ~GLED; //start with GLED off
    while (1)                       // loop forever

    {
        P1OUT &= ~RLED;
        
        
        if (Button1 & ~P1IN) // turn motor1 left if button1 is pressed
        {
            if (debounceGoingLow1)
            {	
 	 	   Toff = T-Ton1;
                Ton1 = TurnMotorLeft(Ton1, Toff, pos1, pos2);
            }
        }

       
        if (Button2 & ~P1IN) // turn motor1 right if button2 is pressed
        {
            if (debounceGoingLow2)
            {
		   Toff = T-Ton1;
                Ton1 = TurnMotorRight(Ton1, Toff, pos1, pos2);
            }
        }
	  Toff = T-Ton1;
  runMotor(Ton1, Toff); // run motor1 at current position

        

        if (Button3 & ~P1IN) // turn motor2 left if button3 is pressed
        {
            if (debounceGoingLow3)
            {	
		   Toff = T-Ton2;
                Ton2 = TurnMotorLeft2(Ton2, Toff, pos1, pos2);
            }
        }

        if (Button4 & ~P1IN) // turn motor2 right if button4 is pressed
        {
            if (debounceGoingLow4)
            {
		   Toff = T-Ton2;
                Ton2 = TurnMotorRight2(Ton2, Toff, pos1, pos2);
            }
        }
	  Toff = T-Ton2;
         runMotor2(Ton2, Toff); // run motor2 at current position

        

        if (Button5 & ~P2IN) // turn motor3 left if button5 is pressed
        {
            if (debounceGoingLow5)
            {
		   Toff = T -Ton3;
                Ton3 = TurnMotorLeft3(Ton3, Toff, pos1, pos2);
            }
        }

        if (Button6 & ~P2IN) // turn motor3 right if button6 is pressed
        {
            if (debounceGoingLow6)
            {
		   Toff = T -Ton3; 
                Ton3 = TurnMotorRight3(Ton3, Toff, pos1, pos2);
            }
        }
 Toff = T -Ton3;
        runMotor3(Ton3, Toff); // run motor3 at current position

    }
}
// Function to add delay’s (from previous project)
void Delay(unsigned int delay)
{
    volatile unsigned int j;
    for (j = 0; j <= delay; j++)
        ;
}
// Debounce Functions (from previous project solution)
char debounceGoingLow1()
{
    Delay(DBNC);
    if (Button1 & ~P1IN)
        return 0x01;          //Still low
    else
        return 0x00;
}

char debounceGoingLow2()
{
    Delay(DBNC);
    if (Button2 & ~P1IN)
        return 0x01;          //Still low
    else
        return 0x00;
}

char debounceGoingLow3()
{
    Delay(DBNC);
    if (Button3 & ~P1IN)
        return 0x01;          //Still low
    else
        return 0x00;
}
char debounceGoingLow4()
{
    Delay(DBNC);
    if (Button4 & ~P1IN)
        return 0x01;          //Still low
    else
        return 0x00;
}
char debounceGoingLow5()
{
    Delay(DBNC);
    if (Button5 & ~P2IN)
        return 0x01;          //Still low
    else
        return 0x00;
}
char debounceGoingLow6()
{
    Delay(DBNC);
    if (Button6 & ~P2IN)
        return 0x01;          //Still low
    else
        return 0x00;
}
// main servo control function. Runs servo at a certain position based off
// of the difference between Ton and Toff (duty cycle).
void runMotor(volatile unsigned int Ton, volatile unsigned int Toff)
{
    volatile unsigned int LoopCounter;
    for (LoopCounter = 0; LoopCounter < Toff; ++LoopCounter)
        ;

    P1OUT |= Serv1;   // turn on

    for (LoopCounter = 0; LoopCounter < Ton; ++LoopCounter)
        ;
    P1OUT &= ~Serv1;   // turn off

}
// repeat for motor2
void runMotor2(volatile unsigned int Ton, volatile unsigned int Toff)
{
    volatile unsigned int LoopCounter;
    for (LoopCounter = 0; LoopCounter < Toff; ++LoopCounter)
        ;

    P1OUT |= Serv2;   // turn on

    for (LoopCounter = 0; LoopCounter < Ton; ++LoopCounter)
        ;
    P1OUT &= ~Serv2;   // turn off

}
// repeat for motor3
void runMotor3(volatile unsigned int Ton, volatile unsigned int Toff)
{
    volatile unsigned int LoopCounter;
    for (LoopCounter = 0; LoopCounter < Toff; ++LoopCounter)
        ;

    P2OUT |= Serv3;   // turn on

    for (LoopCounter = 0; LoopCounter < Ton; ++LoopCounter)
        ;
    P2OUT &= ~Serv3;   // turn off

}
// increments duty cycle to turn servo left (with some extra functions)
int TurnMotorLeft(volatile unsigned int Ton, volatile unsigned int Toff,
                  volatile unsigned int pos1, volatile unsigned int pos2)
{

    // if the other button is pressed at the same time, turn on GLED and Reset Pos'
    if (Button2 & ~P1IN)
    {
        if (debounceGoingLow2)
        {
            while (Button2 & ~P1IN)
            {
                P1OUT |= GLED;
            }
            P1OUT &= ~GLED;
            Ton = posmid1;  // Resets all servos to original position if
            Ton2 = posmid2; // both buttons are pressed at once
            Ton3 = posmid3;

        }
    }
    // as long as the servo isnt at max position, increment duty cycle
    if (Ton <= pos2)
    {
        Ton = Ton + 5;
        Toff = T
        -Ton;
        runMotor(Ton, Toff);
    }
    else // if at max position, stop and turn on RLED
    {
        P1OUT |= RLED;

    }

    return Ton; // return current position
}
// decrements duty cycle to turn servo right (with some extra functions)
int TurnMotorRight(volatile unsigned int Ton, volatile unsigned int Toff,
                   volatile unsigned int pos1, volatile unsigned int pos2)
{
    // Same as TurnMotorLeft but reversed.

    if (Button1 & ~P1IN)
    {
        if (debounceGoingLow2)
        {
            while (Button1 & ~P1IN)
            {
                P1OUT |= GLED;
            }
            P1OUT &= ~GLED;
            Ton = posmid1;  // Resets all servos to original position if
            Ton2 = posmid2; // both buttons are pressed at once
            Ton3 = posmid3;
        }
    }
    if (Ton >= pos1)
    {
        Ton = Ton - 5;
        Toff = T
        -Ton;
        runMotor(Ton, Toff);
    }
    else
    {
        P1OUT |= RLED;
    }

    return Ton;
}
// repeated for motor2
int TurnMotorLeft2(volatile unsigned int Ton, volatile unsigned int Toff,
                   volatile unsigned int pos1, volatile unsigned int pos2)
{

    // if the other button is pressed at the same time, turn on GLED and save position
    if (Button4 & ~P1IN)
    {
        if (debounceGoingLow4)
        {
            while (Button4 & ~P1IN)
            {
                P1OUT |= GLED;
            }
            P1OUT &= ~GLED;
            Ton1Save = Ton1; // save current position of servos
            Ton2Save = Ton;
            Ton3Save = Ton3;

        }
    }
    // as long as the servo isnt at max position, increment duty cycle
    if (Ton <= pos2)
    {
        Ton = Ton + 3;
        Toff = T
        -Ton;
        runMotor2(Ton, Toff);
    }
    else // if at max position, stop and turn on RLED
    {
        P1OUT |= RLED;

    }

    return Ton; // return current position
}
// repeated for motor2
int TurnMotorRight2(volatile unsigned int Ton, volatile unsigned int Toff,
                    volatile unsigned int pos1, volatile unsigned int pos2)
{
    // Same as TurnMotorLeft but reversed.

    if (Button3 & ~P1IN)
    {
        if (debounceGoingLow3)
        {
            while (Button3 & ~P1IN)
            {
                P1OUT |= GLED;
            }
            P1OUT &= ~GLED;
            Ton1Save = Ton1; // save current position of servos
            Ton2Save = Ton;
            Ton3Save = Ton3;
        }
    }
    if (Ton >= pos1)
    {
        Ton = Ton - 3;
        Toff = T
        -Ton;
        runMotor2(Ton, Toff);
    }
    else
    {
        P1OUT |= RLED;
    }

    return Ton;
}
// repeated for motor3
int TurnMotorLeft3(volatile unsigned int Ton, volatile unsigned int Toff,
                   volatile unsigned int pos1, volatile unsigned int pos2)
{

    // if the other button is pressed at the same time, turn on GLED and do nothing
    if (Button6 & ~P2IN)
    {
        if (debounceGoingLow6)
        {
            while (Button6 & ~P2IN)
            {
                P1OUT |= GLED;
            }
            P1OUT &= ~GLED;
            Ton1 = Ton1Save; // change pos’ of servos to saved pos’
            Ton2 = Ton2Save;
            Ton = Ton3Save;

        }
    }
    // as long as the servo isnt at max position, increment duty cycle
    if (Ton <= pos2)
    {
        Ton = Ton + 5;
        Toff = T
        -Ton;
        runMotor3(Ton, Toff);
    }
    else // if at max position, stop and turn on RLED
    {
        P1OUT |= RLED;
    }

    return Ton; // return current position
}
// repeated for motor3
int TurnMotorRight3(volatile unsigned int Ton, volatile unsigned int Toff,
                    volatile unsigned int pos1, volatile unsigned int pos2)
{
    // Same as TurnMotorLeft but reversed.

    if (Button5 & ~P2IN)
    {
        if (debounceGoingLow5)
        {
            while (Button5 & ~P2IN)
            {
                P1OUT |= GLED;
            }
            P1OUT &= ~GLED;
            Ton1 = Ton1Save; // change pos’ of servos to saved pos’
            Ton2 = Ton2Save;
            Ton = Ton3Save;
        }
    }
    if (Ton >= pos1)
    {
        Ton = Ton - 5;
        Toff = T
        -Ton;
        runMotor3(Ton, Toff);
    }
    else
    {
        P1OUT |= RLED;
    }

    return Ton;
}

Load Cell Arduino Code

Arduino
Used to interface the load cell and HX711 amplifier with an arduino and SSD1306 OLED display. Requires 'Wire.h', 'Adafruit_GFX.h', Adafruit_SSD1306.h', and 'HX711_ADC' libraries
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <HX711_ADC.h>
#if defined(ESP8266)|| defined(ESP32) || defined(AVR)
#include <EEPROM.h>
#endif

//pins:
#define BUTTON_PIN 8 //Tare Button
const int HX711_dout = 4; //mcu > HX711 dout pin
const int HX711_sck = 5; //mcu > HX711 sck pin

//HX711 constructor:
HX711_ADC LoadCell(HX711_dout, HX711_sck);

const int calVal_eepromAdress = 0;
unsigned long t = 0;
//OLED Setup
Adafruit_SSD1306  display(128, 64, &Wire, -1);
void setup() {
  pinMode(BUTTON_PIN, INPUT_PULLUP); // Setup Tare button
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Setup OLED
  display.clearDisplay(); // Clear Display
  display.display(); //Send Cleared Display
  display.setTextSize(2); //Set Text Size and Color
  display.setTextColor(WHITE);

  Serial.begin(57600); 
  delay(10);
  Serial.println();
  Serial.println("Starting...");

  LoadCell.begin();
  float calibrationValue; // calibration value 

#if defined(ESP8266)|| defined(ESP32)
  EEPROM.begin(512); 
#endif
  EEPROM.get(calVal_eepromAdress, calibrationValue); //Read Cal’ value from EEPROM

  unsigned long stabilizingtime = 2000; 
  boolean _tare = true; //Enables Taring

  LoadCell.start(stabilizingtime, _tare);
  if (LoadCell.getTareTimeoutFlag()) {
    Serial.println("Timeout, check MCU>HX711 wiring and pin designations");
    while (1);
  }
  else {
    LoadCell.setCalFactor(calibrationValue); // set calibration value 
    Serial.println("Startup is complete");
  }
  
}

void loop() {

  byte buttonState = digitalRead(BUTTON_PIN); //Variable for state of Button
  static boolean newDataReady = 0;
 
  // check for new data/start next conversion:
  if (LoadCell.update()) newDataReady = true;

  // get smoothed value from the dataset:
  if (newDataReady) {
    if (millis() > t) 
{ // Prints weight to serial monitor and OLED
      float i = LoadCell.getData();

      Serial.print("Load_cell output val: "); // Print to Serial
      Serial.println(i); 
      delay(100);

      display.setCursor(32, 16); // Print to OLED
      display.print(i);
      display.display();
      display.clearDisplay();

      newDataReady = 0;
      t = millis();
    }
  }

  // Tare if button is pressed
  if (buttonState == LOW) {

    LoadCell.tareNoDelay();
  }

  // Alert Serial monitor and OLED if tare is complete
  if (LoadCell.getTareStatus() == true) {
    Serial.println("Tare complete");
    display.clearDisplay();
    display.setCursor(32, 16);
    display.println("Tare");
    display.println("Complete");
    display.display();
    display.clearDisplay();
    delay(1000);
  }

}

Credits

cole_bruza

cole_bruza

1 project • 0 followers

Comments