Skip to content

michaelshiloh/SRLbigArm

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 

Repository files navigation

SRLbigArm

Designing a PLC to automate the Big Arm walking

We have the scale model built by Arik

12 September 2025

Done

  • Labeled joints to correspond with SRL's Big Arm
  • Identified which Arduino pin controls which linear actuator
  • Tested that the linear actuator is strong enough to lift up the base (it is!)

Arduino pins to joint mapping (with the white tape on the 4 position header socket that connects to the 4 pins on the H-bridge facing away from the H-bridge)

  • Wrist: Pins 4 and 5
  • Elbow: Pins 2 and 3
  • Shoulder: Pins 8 and 9
  • Pins 6 and 7 are wired to a channel of the H-Bridge but are unused at this time; could be used for rotating or the stabilizing side legs

Sample code to make the shoulder move (either retract or extend)

void setup() {
  pinMode (8, OUTPUT);
  pinMode (9, OUTPUT);

// To reverse the direction, change the HIGH to LOW and LOW to HIGH
  digitalWrite (8, LOW);
  digitalWrite (9, HIGH);
}
  1. Note that this will not stop since there is no command to stop the motors
  2. Note that if the actuator is already fully extended or retracted, an internal switch prevents it from further movement and damage

Next steps:

  • Schedule with Tony to get trained on the MIG welder, the horizontal band saw, and the metal drill press
  • Unscrew the wooden base with all the electronics
  • Undo the large bolt under the arm
  • Coordinate with me to remove the arm. This is a two person job to be done safely.
  • Take the base down to the Scene Shop and cut off the unused portion (horizontal band saw, I think)
  • Drill holes for the casters
  • Drill holes on the sides in case we decided to add the stabilizing side legs
  • Should we add the rotating part at this time?

9 October 2025

Created code that activates each limb of the arm indivudually

Uses three helper functions:

  • extendActuator(): extends an actuator by making IN1 LOW and IN2 HIGH
  • retractActuator(): retracts by making IN1 HIGH and IN2 LOW
  • stopActuator(): stops motion by setting both lines LOW

In the setup() section:

  • All actuator pins are configured as OUTPUT
  • All joints that are moving now stops
  • Initialise serial communication at 9600 baud and print usage instructions for the user

Controls:

  • "sc" contracts the shoulder for 1 second
  • "ec" contracts the elbow for 1 second
  • "wc" contracts the wrist for 1 second
  • "se" extends the shoulder for 1 second
  • "ee" extends the elbow for 1 second
  • "we" extends the wrist for 1 second

Final Points:

  • Each command prints feedback, activates the joint for exactly one second, then stops it
  • Invalid or unknown commands prompt a help message with valid options
  • This arrangement lets me test each joint’s extend/contract movement and provides immediate feedback, ensuring manual control over each actuator from the serial monitor

Code

const int SHOULDER_IN1 = 8;
const int SHOULDER_IN2 = 9;

const int ELBOW_IN1 = 2;
const int ELBOW_IN2 = 3;

const int WRIST_IN1 = 4;
const int WRIST_IN2 = 5;

void stopActuator(int in1, int in2) {
  digitalWrite(in1, LOW);
  digitalWrite(in2, LOW);
}

// Using your polarity swap: extend = IN1 LOW, IN2 HIGH
void extendActuator(int in1, int in2) {
  digitalWrite(in1, LOW);
  digitalWrite(in2, HIGH);
}

// retract = IN1 HIGH, IN2 LOW
void retractActuator(int in1, int in2) {
  digitalWrite(in1, HIGH);
  digitalWrite(in2, LOW);
}

void setup() {
  pinMode(SHOULDER_IN1, OUTPUT);
  pinMode(SHOULDER_IN2, OUTPUT);
  pinMode(ELBOW_IN1, OUTPUT);
  pinMode(ELBOW_IN2, OUTPUT);
  pinMode(WRIST_IN1, OUTPUT);
  pinMode(WRIST_IN2, OUTPUT);
  
  stopActuator(SHOULDER_IN1, SHOULDER_IN2);
  stopActuator(ELBOW_IN1, ELBOW_IN2);
  stopActuator(WRIST_IN1, WRIST_IN2);
  
  Serial.begin(9600);
  Serial.println("Type commands to move actuators:");
  Serial.println("'sc', 'ec', 'wc' to contract shoulder, elbow, wrist");
  Serial.println("'se', 'ee', 'we' to extend shoulder, elbow, wrist");
}

void loop() {
  if (Serial.available() > 0) {
    String command = Serial.readStringUntil('\n');
    command.trim();

    if (command == "sc") {
      Serial.println("Contracting shoulder for 1 second...");
      retractActuator(SHOULDER_IN1, SHOULDER_IN2);
      delay(1000);
      stopActuator(SHOULDER_IN1, SHOULDER_IN2);
    }
    else if (command == "ec") {
      Serial.println("Contracting elbow for 1 second...");
      retractActuator(ELBOW_IN1, ELBOW_IN2);
      delay(1000);
      stopActuator(ELBOW_IN1, ELBOW_IN2);
    }
    else if (command == "wc") {
      Serial.println("Contracting wrist for 1 second...");
      retractActuator(WRIST_IN1, WRIST_IN2);
      delay(1000);
      stopActuator(WRIST_IN1, WRIST_IN2);
    }
    else if (command == "se") {
      Serial.println("Extending shoulder for 1 second...");
      extendActuator(SHOULDER_IN1, SHOULDER_IN2);
      delay(1000);
      stopActuator(SHOULDER_IN1, SHOULDER_IN2);
    }
    else if (command == "ee") {
      Serial.println("Extending elbow for 1 second...");
      extendActuator(ELBOW_IN1, ELBOW_IN2);
      delay(1000);
      stopActuator(ELBOW_IN1, ELBOW_IN2);
    }
    else if (command == "we") {
      Serial.println("Extending wrist for 1 second...");
      extendActuator(WRIST_IN1, WRIST_IN2);
      delay(1000);
      stopActuator(WRIST_IN1, WRIST_IN2);
    }
    else {
      Serial.println("Unknown command. Valid commands: sc, ec, wc, se, ee, we");
    }
  }
}

Identified Possible Locations For Complete R.O.M. Sensors

  • Used cardboard pieces to mark possible locations of sensors and the manners in which they should be marked
image

Scheduled with Tony to get trained on the MIG welder, the horizontal band saw, and the metal drill press

  • Should be completed by Monday 12th

To Do's

  • Unscrew the wooden base with all the electronics

  • Drill holes for the casters

  • Drill holes on the sides in case we decided to add the stabilizing side legs

  • Undo the large bolt under the arm

  • Coordinate with Prof to remove the arm. This is a two person job to be done safely.

  • Should we add the rotating part at this time?


15 October 2025

Improved Previous Code iteration

Input Flexibility:

  • Original: Accepts one command at a time with fixed 1-second duration.
  • Improved: Accepts multiple commands with optional custom durations in a single input line.

Simultaneous Actuator Control:

  • Original: Controls only one actuator per command.
  • Improved: Allows several actuators to move simultaneously with independently timed durations.

Command Parsing:

  • Original: Simple if-else matching on single command strings.
  • Improved: Tokenizes input, supports dynamic command-duration pairs, and uses default duration when individual durations aren’t provided.

Timing Management:

  • Original: Uses blocking delay() calls for fixed durations.
  • Improved: Uses non-blocking timing logic with millis() allowing concurrent actuator timing and more fluid control.

Data Structure Usage:

  • Original: No structured storage for actuator states or durations.
  • Improved: Uses ActuatorControl structs to maintain clear, scalable actuator states.

User Feedback:

  • Original: Prints simple, fixed messages per command.
  • Improved: Prints detailed messages including actuator, direction, and duration.

Scalability and Readability:

  • Original: Longer, repetitive code paths per command.
  • Improved: Cleaner, modular code easier to extend to new commands or actuators.

Sequence Capability:

  • Original: No built-in support for executing command sequences.
  • Improved: Supports special commands like "cycle 1" to perform complex multi-step sequences with precise timing.

Code

const int SHOULDER_IN1 = 8;
const int SHOULDER_IN2 = 9;
const int ELBOW_IN1 = 2;
const int ELBOW_IN2 = 3;
const int WRIST_IN1 = 4;
const int WRIST_IN2 = 5;

struct ActuatorControl {
  bool extend = false;
  bool contract = false;
  unsigned long duration = 0;  // duration in milliseconds
  unsigned long stopTime = 0;
};

void stopActuator(int in1, int in2) {
  digitalWrite(in1, LOW);
  digitalWrite(in2, LOW);
}

void extendActuator(int in1, int in2) {
  digitalWrite(in1, LOW);
  digitalWrite(in2, HIGH);
}

void retractActuator(int in1, int in2) {
  digitalWrite(in1, HIGH);
  digitalWrite(in2, LOW);
}

ActuatorControl shoulder, elbow, wrist;

void setup() {
  pinMode(SHOULDER_IN1, OUTPUT);
  pinMode(SHOULDER_IN2, OUTPUT);
  pinMode(ELBOW_IN1, OUTPUT);
  pinMode(ELBOW_IN2, OUTPUT);
  pinMode(WRIST_IN1, OUTPUT);
  pinMode(WRIST_IN2, OUTPUT);

  stopActuator(SHOULDER_IN1, SHOULDER_IN2);
  stopActuator(ELBOW_IN1, ELBOW_IN2);
  stopActuator(WRIST_IN1, WRIST_IN2);

  Serial.begin(9600);
  Serial.println("Enter commands with optional individual durations:");
  Serial.println("Examples:");
  Serial.println("  sc ec wc 10     (all contract for 10s)");
  Serial.println("  sc 10 ec 5 wc 2 (each different duration)");
}

void loop() {
  unsigned long currentTime = 0;

  if (Serial.available() > 0) {
    String input = Serial.readStringUntil('\n');
    input.trim();

    // Reset actuator states before parsing
    shoulder = ActuatorControl();
    elbow = ActuatorControl();
    wrist = ActuatorControl();

    // Tokenize input based on spaces
    const int maxTokens = 30;
    String tokens[maxTokens];
    int tokenCount = 0;
    int fromIdx = 0;
    while (fromIdx < input.length() && tokenCount < maxTokens) {
      int sp = input.indexOf(' ', fromIdx);
      if (sp == -1) sp = input.length();
      tokens[tokenCount++] = input.substring(fromIdx, sp);
      fromIdx = sp + 1;
    }

    // Determine default duration if last token is a positive number
    unsigned long defaultDuration = 0;
    if (tokenCount > 0) {
      int lastVal = tokens[tokenCount - 1].toInt();
      if (lastVal > 0) {
        defaultDuration = (unsigned long) lastVal * 1000UL;
        tokenCount--; // Exclude default duration token from command parsing
      }
    }

    // Parse commands and optional durations
    for (int i = 0; i < tokenCount; i++) {
      String cmd = tokens[i];
      cmd.trim();

      unsigned long dur = 0;
      // Peek next token for optional individual duration
      if (i + 1 < tokenCount) {
        int val = tokens[i + 1].toInt();
        if (val > 0) {
          dur = (unsigned long)val * 1000UL;
          i++; // Skip duration token
        } else {
          dur = defaultDuration;
        }
      } else {
        dur = defaultDuration;
      }

      if (dur == 0) {
        Serial.print("Duration missing or zero for command ");
        Serial.println(cmd);
        continue;
      }

      if (cmd == "se") {
        shoulder.extend = true;
        shoulder.duration = dur;
      } else if (cmd == "sc") {
        shoulder.contract = true;
        shoulder.duration = dur;
      } else if (cmd == "ee") {
        elbow.extend = true;
        elbow.duration = dur;
      } else if (cmd == "ec") {
        elbow.contract = true;
        elbow.duration = dur;
      } else if (cmd == "we") {
        wrist.extend = true;
        wrist.duration = dur;
      } else if (cmd == "wc") {
        wrist.contract = true;
        wrist.duration = dur;
      } else if (cmd.length() > 0) {
        Serial.print("Unknown command: ");
        Serial.println(cmd);
      }
    }

    // Start all actuators simultaneously
    unsigned long startTime = millis();

    if (shoulder.extend || shoulder.contract) {
      Serial.print("Shoulder ");
      Serial.print(shoulder.extend ? "extend" : "contract");
      Serial.print(" for ");
      Serial.print(shoulder.duration / 1000);
      Serial.println(" seconds");
      shoulder.stopTime = startTime + shoulder.duration;
      if (shoulder.extend) extendActuator(SHOULDER_IN1, SHOULDER_IN2);
      else retractActuator(SHOULDER_IN1, SHOULDER_IN2);
    }

    if (elbow.extend || elbow.contract) {
      Serial.print("Elbow ");
      Serial.print(elbow.extend ? "extend" : "contract");
      Serial.print(" for ");
      Serial.print(elbow.duration / 1000);
      Serial.println(" seconds");
      elbow.stopTime = startTime + elbow.duration;
      if (elbow.extend) extendActuator(ELBOW_IN1, ELBOW_IN2);
      else retractActuator(ELBOW_IN1, ELBOW_IN2);
    }

    if (wrist.extend || wrist.contract) {
      Serial.print("Wrist ");
      Serial.print(wrist.extend ? "extend" : "contract");
      Serial.print(" for ");
      Serial.print(wrist.duration / 1000);
      Serial.println(" seconds");
      wrist.stopTime = startTime + wrist.duration;
      if (wrist.extend) extendActuator(WRIST_IN1, WRIST_IN2);
      else retractActuator(WRIST_IN1, WRIST_IN2);
    }

    // Actuator control loop: stop them individually when their time elapses
    while (true) {
      currentTime = millis();
      bool allDone = true;

      if ((shoulder.extend || shoulder.contract) && currentTime >= shoulder.stopTime) {
        stopActuator(SHOULDER_IN1, SHOULDER_IN2);
        shoulder = ActuatorControl();
      } else if (shoulder.extend || shoulder.contract) {
        allDone = false;
      }

      if ((elbow.extend || elbow.contract) && currentTime >= elbow.stopTime) {
        stopActuator(ELBOW_IN1, ELBOW_IN2);
        elbow = ActuatorControl();
      } else if (elbow.extend || elbow.contract) {
        allDone = false;
      }

      if ((wrist.extend || wrist.contract) && currentTime >= wrist.stopTime) {
        stopActuator(WRIST_IN1, WRIST_IN2);
        wrist = ActuatorControl();
      } else if (wrist.extend || wrist.contract) {
        allDone = false;
      }

      if (allDone) break;

      delay(10);  // 10 ms polling delay
    }

    Serial.println("All done.");
  }
}

Bolted On Wheels

  • Attached wheels to the back through drilling, nuts, and bolts
  • The back plate is slightly inclined so the wheels are slightly of angled obtusely but they still roll well and the angle prevents the robot from free-rolling when inclined

Completed 1 Cycle Of Movements That Moves Robot Forward

  • The robot is capable of moving forward 60cm
  • This was discovered through iterating possible movemnts between the Shoulder, Elbow, and Wrist
  • The moves to input to achieve this are: SE 15 WE 5 SE 25 EE 10 WE 10 SC 40

Unscrewed the wooden base with all the electronics

  • Unscrewed but still keeping on top of base for ease of electronics access

Welding Training

  • Completed all training and only need to practice more to ensure skills are maintained

Immediate To Do's

  • Optimise arm movement
  • Start designing Max/Min sensors based on Arm movement

Delayed To Do's

  • Will drill holes on the sides in case we decided to add the stabilizing side legs but this can be done once the rotation mechanism is more established
  • Undo the large bolt under the arm
  • Coordinate with Prof to remove the arm. (This is a two person job to be done safely.)
  • Should we add the rotating part at this time?

How to use Github markdown

This is how you make a header

This is how you make a bigger header

this is how you make a paragraph this is part of the same paragraph this is part of the same paragraph this is part of the same paragraph this is part of the same paragraph this is part of the same paragraph

To start a new paragraph, insert a the blank line after the previous paragraph

  • this is how you make a list
  • second item
  • etc.

More info here and in many other sources online

About

Designing a PLC to automate the Big Arm walking

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •