The group assignment of this week is to actuate and automate our machine and to document the group project and your individual contribution. You can find the documentation of our marble machine on our group page.

What I did – step-by-step

For this week I worked on the wiring of the machine, I helped out were needed and I wrote the documenation of this week and the mechanical week. The full documentation can be found on our group page. I also posted the documenation on my own page because I wrote most of it.

Wiring the machine

For this week I teamed up with Joey to make sure we have working servo’s to kick out the marbles. One of the biggeste challenge was how to wire the system.

What we did – Group assignment step-by-step

Our machine

Hero shot Our Marble Calender Machine

What does it do?

The machine is a physical representation of the online booking system of the machines at the Waag Fab Lab. It shows the reserved slots for the selected machine: CNC, Modela, Laser or 3D-printer. The machine will start moving marbles around, when it’s finished the marbles show which slots are open for you to use the machine and which slots are reserved. This way you’re always sure if and when you can use the selected machine. And you don’t have to go to the online to see when the machine is booked today.

booking system Waag The Marble Calender Machine: a physical representation of the Waag fab lab booking system

How does it work?

  1. Press the button for the machine you want to use and see if it’s reserved
  2. After you selected the machine the stairs start moving and marbles start rolling, up the stairs and into the calendar slots.
  3. Each slot first fills with one marble, this represents half a hour, from 8.00 to 18.00.
  4. The machine is connected to the booking system of the Waag Fab lab. Based on these bookings it will kick out the marbles from the slots that aren’t reserved.
  5. These marbles go down with the marble runs back to start of the stairs.
  6. The marbles that stay in their slot show the reserved spots of the machine for today.
  7. When you want to see the reservation of another machine you press the button of the CNC for example.
  8. The machine starts over again with filling the slots and losing its marbles.

how does the machine work The Marble Calender Machine: how does it work

Who made it?

We had a lot fun and of course some stress for the deadlines working together as team on our marble calender machine. Details on each of our processes can found on our individual Fabacademy pages.

booking system Waag Waag Fab lab team: Anne, Micky, Rutger and Joey

A marblelous team

  • Joey likes to make stuff. he is a researcher at the Amsterdam University of Applied Sciences, where he works with Internet of Things technology and research assistive technology IoT for people with a visual impairment.
  • Anne was the lab manager of Makerversity in Amsterdamis, now she is a freelance designer and maker on a mission to create, learn and and share.
  • Rutger has a background in Anthropology but since a few years he dedicate myself to designing and making. This is where he found his true passion. He works with silicone to make jewelry and his trade mark is that all of my pieces don’t use closings.
  • Micky is lecturer at the department of Communication and Multimedia Design at the Amsterdam University of Applied Sciences. She teaches courses about Design Ethics and Design Argumentation. She loves to combining theory and philosophy with applied design work.

The Machine design

For Machine design week, we continued with fine tuning the mechanics of our machine. But mainly we worked on the automation and actuation of the machine. The planning and dividing of tasks was more organic this week. In the beginning of the week Anne and Rutger teamed up to finish the mechanics of the stairs and to automate it. Joey and Micky teamed up to automate and actuate the servos that kick the marbles from their slots. Of course during the week we all helped each other to be able to finish our machine on time.

Step 1: Making the stairs work

One of our main priority for the machine design was to get the stairs to work otherwise the marbles wouldn’t go up.

Fixing the mechanics

In the mechanical week Rutger made different prototypes, but we still had to tweak some parts. Mainly the slope of each step, the mechanics of the movement and the sliders. Rutger explains more about this process in his documentation.

Stairs mechanics Fine tuning and tweaking the mechanics of the stairs

Testing the mechanics by hand of the new stair design

Automating the stairs

We used a stepper motor to move the stairs up and down. Henk showed us the code he used last year and Anne tweaked his code to make it work for the stairs. The code tweaked by Anne, more details on the code can be found in her individual documentation.

#define EN        8      //Negative Enable pin
#define X_DIR     5      //Direction pin
#define X_STP     2     //Step pin

int delayTime=20; //Delay between each pause (uS)
int stps=29000;// Steps to move microsteps 1/32 (200*32 = 6400)
void step(boolean dir, byte dirPin, byte stepperPin, int steps) {

 digitalWrite(dirPin, dir); //

 for (int i = 0; i < steps; i++) {
   digitalWrite(stepperPin, HIGH);
   delayMicroseconds(delayTime);
   digitalWrite(stepperPin, LOW);
   delayMicroseconds(delayTime);
 }
}
void setup(){
 pinMode(X_DIR, OUTPUT); //direction pin = output
 pinMode(X_STP, OUTPUT); //step pin = output
 pinMode(EN, OUTPUT); //negative enable pin  =output
 digitalWrite(EN, LOW); //start negative enable pin at low
}
void loop(){
 step(false, X_DIR, X_STP, stps); //true equals back towards motor
   step(false, X_DIR, X_STP, stps); //true equals back towards motor
       step(false, X_DIR, X_STP, stps); //true equals back towards motor
           step(false, X_DIR, X_STP, stps); //true equals back towards motor
 delay(1000);
 step(true, X_DIR, X_STP, stps); //true equals back towards motor
   step(true, X_DIR, X_STP, stps); //true equals back towards motor
       step(true, X_DIR, X_STP, stps); //true equals back towards motor
           step(true, X_DIR, X_STP, stps); //true equals back towards motor
 delay(1000);

} 

To fine-tune the movement of the stairs we had to change amount of steps

Fine tuning the movement of the stairs with the use of a stepper

Step 2: Making the machine booking work

The other big concern for our machine was the representation of our booking system. For this idea we needed to connect 20 servo motors to the main marble run.

servos Connecting the servo motors

Automating the servo motors

Joey worked on connecting all the servo motors, he wrote in detail about this process in his individual documentation. We have 20 slots, each slot should be controlled individually by one servo, meaning we need to control 20 servos. To do this we need to connect the servo motors to a PWM pin of the micro controller. Therefore we need to use at least 4 Atmega328 chips, we have these available in the form of Arduino pro minis.

Automating the servo motors

The code to control the servo motors is written by Joey, it controls the servos via serial and it allows the Arduino to address each servo individually.

/* 
 *  The Multiple servo controller with Serial controll
 *  This code allows you to controll multiple servos at the same time adressing them by an integer number( 1 between 6),
 *  and tell them to switch state (1 or 0) for open or closed,
 *  using the Serial interface. 
 *  The serailEvent handling is borrowed and modified from Tom Igoe serial event example http://www.arduino.cc/en/Tutorial/SerialEvent
 *  
 *  http://fab.academany.org/2019/labs/waag/students/josephus-vanderbie//week17.html
 *  made by Joey van der Bie
 *  2019-05-13
 *  
 *  This code is based on the Sweep example
 by BARRAGAN <http://barraganstudio.com>
 This example code is in the public domain.

 modified 8 Nov 2013
 by Scott Fitzgerald
 http://www.arduino.cc/en/Tutorial/Sweep
*/
//CHECK OR UPDATE THESE NUMBERS BEFORE UPLOADING!!!!
int ARDUINO_NUMBER = 1; //range 1,2,3 or 4 (Arduino position in servo)
int NUMBER_OF_SERVOS = 5; //number of servo's per Arduino;
//CHECK OR UPDATE THESE NUMBERS BEFORE UPLOADING!!!!

byte servoNumber = B0000000;
byte servoState = B0000000;
byte emptyValue = B1000000; //this is the empty value for our servoNumber and servoState veriables that allows us to check if it is not set.
byte serialProcessBitMask = B00011111;
byte endByte = B11111111;

bool stringComplete = false;  // whether the string is complete


#include <Servo.h>

Servo servo1, servo2, servo3, servo4, servo5, servo6;  // create servo object to control a servo
// twelve servo objects can be created on most boards

int pos = 0;    // variable to store the servo position
int delayTime = 5; // delay between servo steps
int degreeSteps = 1; // steps to take between degrees
int delayBetweenServos = 200; // delay between the movement of servos

int SERVOSTATE_OPEN = 1; //indicator for communication via serial
int SERVOSTATE_CLOSED = 0; //indicator for communication via serial

//default positions (not normally used)
int SERVOSTATE_OPEN_POSITION = 2;//degrees position for open
int SERVOSTATE_CLOSED_POSITION = 95;// degrees position for closed

//The servo structure 
struct ServoStruct {
  unsigned int  servoNumber:5; // the number we addres from our serial communication, the :5 is the number of bits used
  unsigned int  pinNumber:4; // the pin it is connected to, the :4 is the number of bits
  int   currentPosition; // current position in degrees
   Servo  servoObject; // the actual object 
   int OPEN_POSITION; // open position (anything between 0 and 180)
   int CLOSED_POSITION;// closed position (anything between 0 and 180)
};

struct ServoStruct ss1, ss2, ss3, ss4, ss5, ss6;


void setup() {
  Serial.begin(9600);
  
  ss1.servoNumber = (ARDUINO_NUMBER-1)*NUMBER_OF_SERVOS + 1;
  ss1.pinNumber = 3;
  ss1.currentPosition = 0;
  ss1.OPEN_POSITION = SERVOSTATE_OPEN_POSITION;
  ss1.CLOSED_POSITION = SERVOSTATE_CLOSED_POSITION;
  ss1.servoObject = servo1;
  ss1.servoObject.attach(ss1.pinNumber);
  ss1.servoObject.write(ss1.OPEN_POSITION); // move the servo to its start position

  ss2.servoNumber = (ARDUINO_NUMBER-1)*NUMBER_OF_SERVOS + 2;
  ss2.pinNumber = 5;
  ss2.currentPosition = 0;
  ss2.OPEN_POSITION = SERVOSTATE_OPEN_POSITION;
  ss2.CLOSED_POSITION = SERVOSTATE_CLOSED_POSITION;
  ss2.servoObject = servo2;
  ss2.servoObject.attach(ss2.pinNumber);
  ss2.servoObject.write(ss2.OPEN_POSITION);
  
  ss3.servoNumber = (ARDUINO_NUMBER-1)*NUMBER_OF_SERVOS + 3;
  ss3.pinNumber = 6;
  ss3.currentPosition = 0;
  ss3.OPEN_POSITION = SERVOSTATE_OPEN_POSITION;
  ss3.CLOSED_POSITION = SERVOSTATE_CLOSED_POSITION;
  ss3.servoObject = servo3;
  ss3.servoObject.attach(ss3.pinNumber);
  ss3.servoObject.write(ss3.OPEN_POSITION);
  
  ss4.servoNumber = (ARDUINO_NUMBER-1)*NUMBER_OF_SERVOS + 4;
  ss4.pinNumber = 10;
  ss4.currentPosition = 0;
  ss4.OPEN_POSITION = SERVOSTATE_OPEN_POSITION;
  ss4.CLOSED_POSITION = SERVOSTATE_CLOSED_POSITION;
  ss4.servoObject = servo4;
  ss4.servoObject.attach(ss4.pinNumber);
  ss4.servoObject.write(ss4.OPEN_POSITION);
  
  ss5.servoNumber = (ARDUINO_NUMBER-1)*NUMBER_OF_SERVOS + 5;
  ss5.pinNumber = 9;
  ss5.currentPosition = 0;
  ss6.OPEN_POSITION = SERVOSTATE_OPEN_POSITION;
  ss6.CLOSED_POSITION = SERVOSTATE_CLOSED_POSITION;
  ss5.servoObject = servo5;
  ss5.servoObject.attach(ss5.pinNumber);
  ss5.servoObject.write(ss5.OPEN_POSITION);
  
  ss6.servoNumber = (ARDUINO_NUMBER-1)*NUMBER_OF_SERVOS + 6;
  ss6.pinNumber = 11;
  ss6.currentPosition = 0;
  ss6.OPEN_POSITION = SERVOSTATE_OPEN_POSITION;
  ss6.CLOSED_POSITION = SERVOSTATE_CLOSED_POSITION;
  ss6.servoObject = servo6;
  ss6.servoObject.attach(ss6.pinNumber);
  ss6.servoObject.write(ss6.OPEN_POSITION);
}

void loop() {
//  servoToState(1,SERVOSTATE_CLOSED); 
  // print the string when a newline arrives:
  if (stringComplete) {
    //Serial.print("number: ");
    //Serial.write((int)servoNumber);
    //Serial.println("");
    //Serial.print("state: ");
    //Serial.write((int)servoState);
    
    servoToState((int)servoNumber, (int)servoState);
    resetSerialStorageValues();
  }

}

void servoToState(int servoNr, int state){
  if(state == SERVOSTATE_OPEN){
   // state = SERVOSTATE_OPEN_POSITION;
  }else if(state == SERVOSTATE_CLOSED){
   // state = SERVOSTATE_CLOSED_POSITION;
  }else {
    //invalid state, stop the function
        //Serial.print("invalid state:");
        //Serial.println(state, BIN);
        //Serial.println(SERVOSTATE_OPEN, BIN);
        //Serial.println(SERVOSTATE_CLOSED, BIN);
    return;
  }
  
  if(ss1.servoNumber == servoNr){
    moveServo(&ss1, state?ss1.OPEN_POSITION:ss1.CLOSED_POSITION);
  }else   if(ss2.servoNumber == servoNr){
    moveServo(&ss2,  state?ss2.OPEN_POSITION:ss2.CLOSED_POSITION);
  }else   if(ss3.servoNumber == servoNr){
    moveServo(&ss3,  state?ss3.OPEN_POSITION:ss3.CLOSED_POSITION);
  }else   if(ss4.servoNumber == servoNr){
    moveServo(&ss4,  state?ss4.OPEN_POSITION:ss4.CLOSED_POSITION);
  }else   if(ss5.servoNumber == servoNr){
    moveServo(&ss5,  state?ss5.OPEN_POSITION:ss5.CLOSED_POSITION);
  }else   if(ss6.servoNumber == servoNr){
    moveServo(&ss6,  state?ss6.OPEN_POSITION:ss6.CLOSED_POSITION);
  }else{
      //Serial.print("No servos found for number:");
      //Serial.println(servoNr, BIN);
      //Serial.println(ss1.servoNumber, BIN);
      //Serial.println(ss2.servoNumber, BIN);
      //Serial.println(ss3.servoNumber, BIN);
      //Serial.println(ss4.servoNumber, BIN);
      //Serial.println(ss5.servoNumber, BIN);
      //Serial.println(ss6.servoNumber, BIN);
  }
}

void moveServo(struct ServoStruct *servo, int newPosition){
  //Serial.print("Moving servo");
  //Serial.println(servo->servoNumber);
  if(servo->currentPosition < newPosition){
    for (pos = servo->currentPosition; pos <= newPosition; pos += degreeSteps) { // goes from 0 degrees to 180 degrees
      servo->servoObject.write(pos); 
      delay(delayTime);                       // waits 15ms for the servo to reach the position
    }
  }else{
        for (pos = servo->currentPosition; pos >= newPosition; pos -= degreeSteps) { // goes from 0 degrees to 180 degrees
      servo->servoObject.write(pos); 
      delay(delayTime);                       // waits 15ms for the servo to reach the position
    }
  }
  servo->currentPosition = newPosition;
}

/*
  SerialEvent occurs whenever a new data comes in the hardware serial RX. This
  routine is run between each time loop() runs, so using delay inside loop can
  delay response. Multiple bytes of data may be available.
*/
void serialEvent() {
  while (Serial.available()) {
    byte incommmingByte = Serial.read();
    if (incommmingByte == endByte) {
      if(servoNumber == emptyValue || servoState == emptyValue){
        //we have missed a crucial value, reset our ledger.
        resetSerialStorageValues();
      }else{
        //everything checks out, lets process the command
        stringComplete = true;
      }
    }else if(servoNumber == emptyValue){
      servoNumber = serialProcessBitMask & incommmingByte;
    }else if(servoState == emptyValue){
      //maks the incomming byte and limit it to one bit (0 or 1);
      servoState =  serialProcessBitMask & incommmingByte;
    }else{
      //something went wrong
      //continue by storing the values, but move them up a place
      servoNumber = servoState;
      servoState = incommmingByte;
    }
  }
}

void resetSerialStorageValues(){
    stringComplete = false;
    servoNumber = emptyValue;
    servoState = emptyValue;
}

Joey explains the parts of his code and the other codes used the automate the machine more in detail in his documentation.

Wiring the servos

connections The wiring of our machine

To make sure we could control 20 servos we needed a wire that would connect all the servos to ground, vcc and data. Micky made the cable tree, as can be seen in the drawing on the top right and the result on the top left. However when we connected the wire the servo motors it turned out to be difficult to debug as we had 20 connections in one. Because we have four board with each a connection to five wires.

Fine tuning the servo motors and wiring

Therefore we decided to change the wiring from one cable connected to 20 servo motors to four cables each connected to five servos. Micky redid the cable, more details on the wiring and the cable tree can be found on her documentation.

Step 3: Connecting the parts together

When we had the stairs and calender working separately the next step was to connect the parts together.

  1. Stairs need to drop off the marble in the calender run.
  2. After the marbles are kicked out by the servos they need to roll back to the stairs.
  3. The marble run needs to drop the marble at the stairs.

connections Connecting the parts together

Designing the marble run

To be able to move the marbles back to the stairs and from the stairs to the slot. We had to design the runs for the marbles to go down. Rutger had a lot fun making the runs which connect the marble calender with the stairs, with some loops.

marble run Marble run

Step 5: Assembling our machine

When we had all the part finished and together the big moment was there to assemble everything and to make the machine. First we had to make sure everything fitted within the frame and the wiring was as need as possible, for the time we have.

marble run Machine assembly

The next step was to get all the electronics to work. Before we assembles we had it working separately but now it was time to see how each part would work.

Step 6: Powering our machine

The double stairs that bring the marbles from the bottom to the top are attached to the frame. The back stairs are fixed and the front stairs are fixed to the bottom. The bottom stairs is mounted to a plate with two guide rails that allow the stairs to go smoothly up and down. A third rod is threaded that is directly driven using a nema 17 motor. The motor is controlled with a Arduino Uno with Motor shield. All power for the stairs are from a bench power supply (this tells us how much the stairs are using). A better more permanent solution would be a 9V/1A DC power adapter. The Arduino Uno doesn’t get power from the Motor shield so it can be powered with a central power line connecting all Arduino boards (5v).

marble run Marble run

Losing our marbles

We managed to pull off a lot in just two weeks and we’re very proud of the end result. However for the next time we do have so things we would like to change and improve for version 2.0.

  • Material: We are currently using a combination of a lot of different materials, this makes it look a bit messy. Limiting the kinds of materials and colors would be better. For the marble track acrylic would look a lot better!
  • Electronics: We spend a lot of time on all the wiring, cables and we designed some boards. The wires are sometimes a bit too short. They could be hidden slightly better. And the PCBs and connections are really fragile. And the boards still have a few mistakes in them. For version two of this machine it would be great to design custom PCB’s for this machine and have them ordered from China. This way we can also improve our power management.
  • Design: It’s not at all clear yet what the machine communicates: availability of the machines in the lab. This can be improved with more emphasis on the ‘calendar’.
  • Construction: Due to material availability in the lab we have long parts that tend to bend in the middle (servo beam & first track). This could be improved with a better choice of material and a improved design
⊲  Previous post 16. Interface and application programming Next post   ⊳ 18. Wildcard week